mirror of
https://github.com/storytold/spark.git
synced 2026-10-12 23:49:01 +00:00
121 lines
3.6 KiB
TypeScript
121 lines
3.6 KiB
TypeScript
import * as THREE from "three";
|
|
|
|
import { PackedSplats } from "./PackedSplats";
|
|
import type {
|
|
GsplatGenerator,
|
|
SplatGenerator,
|
|
SplatModifier,
|
|
} from "./SplatGenerator";
|
|
|
|
// SplatAccumulator helps manage the generation of splats from multiple
|
|
// SplatGenerators, keeping track of the splat mapping, coordinate system,
|
|
// and reference count.
|
|
|
|
// A GeneratorMapping describes a Gsplat range that was generated, including
|
|
// which generator and its version number.
|
|
export type GeneratorMapping = {
|
|
node: SplatGenerator;
|
|
generator?: GsplatGenerator;
|
|
version: number;
|
|
base: number;
|
|
count: number;
|
|
};
|
|
|
|
export class SplatAccumulator {
|
|
splats = new PackedSplats();
|
|
// The transform from Accumulator coordinate system to world coordinates.
|
|
toWorld = new THREE.Matrix4();
|
|
// An array of all Gsplat mappings that were used for generation
|
|
mapping: GeneratorMapping[] = [];
|
|
// Number of SparkViewpoints (or other) that reference this accumulator, used
|
|
// to figure out when it can be recycled for use
|
|
refCount = 0;
|
|
|
|
// Incremented every time the splats are updated/generated.
|
|
splatsVersion = -1;
|
|
// Incremented every time the splat mapping/layout is updated.
|
|
// Splat sort order can be reused between equivalent mapping versions.
|
|
mappingVersion = -1;
|
|
|
|
ensureGenerate(maxSplats: number) {
|
|
if (this.splats.ensureGenerate(maxSplats)) {
|
|
// If we had to resize our PackedSplats then clear all previous mappings
|
|
this.mapping = [];
|
|
}
|
|
}
|
|
|
|
// Generate all Gsplats from an array of generators
|
|
generateSplats({
|
|
renderer,
|
|
modifier,
|
|
generators,
|
|
forceUpdate,
|
|
originToWorld,
|
|
}: {
|
|
renderer: THREE.WebGLRenderer;
|
|
modifier: SplatModifier;
|
|
generators: GeneratorMapping[];
|
|
forceUpdate?: boolean;
|
|
originToWorld: THREE.Matrix4;
|
|
}) {
|
|
// Create a lookup from last SplatGenerator
|
|
const mapping = this.mapping.reduce((map, record) => {
|
|
map.set(record.node, record);
|
|
return map;
|
|
}, new Map<SplatGenerator, GeneratorMapping>());
|
|
|
|
// Run generators that are different from existing mapping
|
|
let updated = 0;
|
|
let numSplats = 0;
|
|
for (const { node, generator, version, base, count } of generators) {
|
|
const current = mapping.get(node);
|
|
if (
|
|
forceUpdate ||
|
|
generator !== current?.generator ||
|
|
version !== current?.version ||
|
|
base !== current?.base ||
|
|
count !== current?.count
|
|
) {
|
|
// Something is different from before so we should generate these Gsplats
|
|
if (generator && count > 0) {
|
|
const modGenerator = modifier.apply(generator);
|
|
try {
|
|
this.splats.generate({
|
|
generator: modGenerator,
|
|
base,
|
|
count,
|
|
renderer,
|
|
});
|
|
} catch (error) {
|
|
node.generator = undefined;
|
|
node.generatorError = error;
|
|
}
|
|
updated += 1;
|
|
}
|
|
}
|
|
numSplats = Math.max(numSplats, base + count);
|
|
}
|
|
|
|
this.splats.numSplats = numSplats;
|
|
this.toWorld = originToWorld;
|
|
this.mapping = generators;
|
|
return updated !== 0;
|
|
}
|
|
|
|
// Check if this accumulator has exactly the same generator mapping as
|
|
// the previous one. If so, we can reuse the Gsplat sort order.
|
|
hasCorrespondence(other: SplatAccumulator) {
|
|
if (this.mapping.length !== other.mapping.length) {
|
|
return false;
|
|
}
|
|
return this.mapping.every(({ node, base, count }, i) => {
|
|
const {
|
|
node: otherNode,
|
|
base: otherBase,
|
|
count: otherCount,
|
|
} = other.mapping[i];
|
|
return node === otherNode && base === otherBase && count === otherCount;
|
|
});
|
|
}
|
|
}
|