bilca's picture
Upload 56 files
352fb85 verified
raw
history blame
15.1 kB
import { Scene } from "../../../core/Scene";
import { Splat } from "../../../splats/Splat";
import DataWorker from "web-worker:./DataWorker.ts";
import loadWasm from "../../../wasm/data";
import { Matrix4 } from "../../../math/Matrix4";
class RenderData {
public dataChanged = false;
public transformsChanged = false;
public colorTransformsChanged = false;
private _splatIndices: Map<Splat, number>;
private _offsets: Map<Splat, number>;
private _data: Uint32Array;
private _width: number;
private _height: number;
private _transforms: Float32Array;
private _transformsWidth: number;
private _transformsHeight: number;
private _transformIndices: Uint32Array;
private _transformIndicesWidth: number;
private _transformIndicesHeight: number;
private _colorTransforms: Float32Array;
private _colorTransformsWidth: number;
private _colorTransformsHeight: number;
private _colorTransformIndices: Uint32Array;
private _colorTransformIndicesWidth: number;
private _colorTransformIndicesHeight: number;
private _positions: Float32Array;
private _rotations: Float32Array;
private _scales: Float32Array;
private _vertexCount: number;
private _updating: Set<Splat> = new Set<Splat>();
private _dirty: Set<Splat> = new Set<Splat>();
private _worker: Worker;
getSplat: (index: number) => Splat | null;
getLocalIndex: (splat: Splat, index: number) => number;
markDirty: (splat: Splat) => void;
rebuild: () => void;
dispose: () => void;
constructor(scene: Scene) {
let vertexCount = 0;
let splatIndex = 0;
this._splatIndices = new Map<Splat, number>();
this._offsets = new Map<Splat, number>();
const lookup = new Map<number, Splat>();
for (const object of scene.objects) {
if (object instanceof Splat) {
this._splatIndices.set(object, splatIndex);
this._offsets.set(object, vertexCount);
lookup.set(vertexCount, object);
vertexCount += object.data.vertexCount;
splatIndex++;
}
}
this._vertexCount = vertexCount;
this._width = 2048;
this._height = Math.ceil((2 * this.vertexCount) / this.width);
this._data = new Uint32Array(this.width * this.height * 4);
this._transformsWidth = 5;
this._transformsHeight = lookup.size;
this._transforms = new Float32Array(this._transformsWidth * this._transformsHeight * 4);
this._transformIndicesWidth = 1024;
this._transformIndicesHeight = Math.ceil(this.vertexCount / this._transformIndicesWidth);
this._transformIndices = new Uint32Array(this._transformIndicesWidth * this._transformIndicesHeight);
this._colorTransformsWidth = 4;
this._colorTransformsHeight = 64;
this._colorTransforms = new Float32Array(this._colorTransformsWidth * this._colorTransformsHeight * 4);
this._colorTransforms.fill(0);
this._colorTransforms[0] = 1;
this._colorTransforms[5] = 1;
this._colorTransforms[10] = 1;
this._colorTransforms[15] = 1;
this._colorTransformIndicesWidth = 1024;
this._colorTransformIndicesHeight = Math.ceil(this.vertexCount / this._colorTransformIndicesWidth);
this._colorTransformIndices = new Uint32Array(
this._colorTransformIndicesWidth * this._colorTransformIndicesHeight,
);
this.colorTransformIndices.fill(0);
this._positions = new Float32Array(this.vertexCount * 3);
this._rotations = new Float32Array(this.vertexCount * 4);
this._scales = new Float32Array(this.vertexCount * 3);
this._worker = new DataWorker();
const updateTransform = (splat: Splat) => {
const splatIndex = this._splatIndices.get(splat) as number;
this._transforms.set(splat.transform.buffer, splatIndex * 20);
this._transforms[splatIndex * 20 + 16] = splat.selected ? 1 : 0;
splat.positionChanged = false;
splat.rotationChanged = false;
splat.scaleChanged = false;
splat.selectedChanged = false;
this.transformsChanged = true;
};
const updateColorTransforms = () => {
let colorTransformsChanged = false;
for (const splat of this._splatIndices.keys()) {
if (splat.colorTransformChanged) {
colorTransformsChanged = true;
break;
}
}
if (!colorTransformsChanged) {
return;
}
const colorTransformsMap: Matrix4[] = [new Matrix4()];
this._colorTransformIndices.fill(0);
let i = 1;
for (const splat of this._splatIndices.keys()) {
const offset = this._offsets.get(splat) as number;
for (const colorTransform of splat.colorTransforms) {
if (!colorTransformsMap.includes(colorTransform)) {
colorTransformsMap.push(colorTransform);
i++;
}
}
for (const index of splat.colorTransformsMap.keys()) {
const colorTransformIndex = splat.colorTransformsMap.get(index) as number;
this._colorTransformIndices[index + offset] = colorTransformIndex + i - 1;
}
splat.colorTransformChanged = false;
}
for (let index = 0; index < colorTransformsMap.length; index++) {
const colorTransform = colorTransformsMap[index];
this._colorTransforms.set(colorTransform.buffer, index * 16);
}
this.colorTransformsChanged = true;
};
this._worker.onmessage = (e) => {
if (e.data.response) {
const response = e.data.response;
const splat = lookup.get(response.offset) as Splat;
updateTransform(splat);
updateColorTransforms();
const splatIndex = this._splatIndices.get(splat) as number;
for (let i = 0; i < splat.data.vertexCount; i++) {
this._transformIndices[response.offset + i] = splatIndex;
}
this._data.set(response.data, response.offset * 8);
splat.data.reattach(
response.positions,
response.rotations,
response.scales,
response.colors,
response.selection,
);
this._positions.set(response.worldPositions, response.offset * 3);
this._rotations.set(response.worldRotations, response.offset * 4);
this._scales.set(response.worldScales, response.offset * 3);
this._updating.delete(splat);
splat.selectedChanged = false;
this.dataChanged = true;
}
};
// eslint-disable-next-line @typescript-eslint/no-explicit-any
let wasmModule: any;
async function initWasm() {
wasmModule = await loadWasm();
}
initWasm();
async function waitForWasm() {
while (!wasmModule) {
await new Promise((resolve) => setTimeout(resolve, 0));
}
}
const buildImmediate = (splat: Splat) => {
if (!wasmModule) {
waitForWasm().then(() => {
buildImmediate(splat);
});
return;
}
updateTransform(splat);
const positionsPtr = wasmModule._malloc(3 * splat.data.vertexCount * 4);
const rotationsPtr = wasmModule._malloc(4 * splat.data.vertexCount * 4);
const scalesPtr = wasmModule._malloc(3 * splat.data.vertexCount * 4);
const colorsPtr = wasmModule._malloc(4 * splat.data.vertexCount);
const selectionPtr = wasmModule._malloc(splat.data.vertexCount);
const dataPtr = wasmModule._malloc(8 * splat.data.vertexCount * 4);
const worldPositionsPtr = wasmModule._malloc(3 * splat.data.vertexCount * 4);
const worldRotationsPtr = wasmModule._malloc(4 * splat.data.vertexCount * 4);
const worldScalesPtr = wasmModule._malloc(3 * splat.data.vertexCount * 4);
wasmModule.HEAPF32.set(splat.data.positions, positionsPtr / 4);
wasmModule.HEAPF32.set(splat.data.rotations, rotationsPtr / 4);
wasmModule.HEAPF32.set(splat.data.scales, scalesPtr / 4);
wasmModule.HEAPU8.set(splat.data.colors, colorsPtr);
wasmModule.HEAPU8.set(splat.data.selection, selectionPtr);
wasmModule._pack(
splat.selected,
splat.data.vertexCount,
positionsPtr,
rotationsPtr,
scalesPtr,
colorsPtr,
selectionPtr,
dataPtr,
worldPositionsPtr,
worldRotationsPtr,
worldScalesPtr,
);
const outData = new Uint32Array(wasmModule.HEAPU32.buffer, dataPtr, splat.data.vertexCount * 8);
const worldPositions = new Float32Array(
wasmModule.HEAPF32.buffer,
worldPositionsPtr,
splat.data.vertexCount * 3,
);
const worldRotations = new Float32Array(
wasmModule.HEAPF32.buffer,
worldRotationsPtr,
splat.data.vertexCount * 4,
);
const worldScales = new Float32Array(wasmModule.HEAPF32.buffer, worldScalesPtr, splat.data.vertexCount * 3);
const splatIndex = this._splatIndices.get(splat) as number;
const offset = this._offsets.get(splat) as number;
for (let i = 0; i < splat.data.vertexCount; i++) {
this._transformIndices[offset + i] = splatIndex;
}
this._data.set(outData, offset * 8);
this._positions.set(worldPositions, offset * 3);
this._rotations.set(worldRotations, offset * 4);
this._scales.set(worldScales, offset * 3);
wasmModule._free(positionsPtr);
wasmModule._free(rotationsPtr);
wasmModule._free(scalesPtr);
wasmModule._free(colorsPtr);
wasmModule._free(selectionPtr);
wasmModule._free(dataPtr);
wasmModule._free(worldPositionsPtr);
wasmModule._free(worldRotationsPtr);
wasmModule._free(worldScalesPtr);
this.dataChanged = true;
this.colorTransformsChanged = true;
};
const build = (splat: Splat) => {
if (splat.positionChanged || splat.rotationChanged || splat.scaleChanged || splat.selectedChanged) {
updateTransform(splat);
}
if (splat.colorTransformChanged) {
updateColorTransforms();
}
if (!splat.data.changed || splat.data.detached) return;
const serializedSplat = {
position: new Float32Array(splat.position.flat()),
rotation: new Float32Array(splat.rotation.flat()),
scale: new Float32Array(splat.scale.flat()),
selected: splat.selected,
vertexCount: splat.data.vertexCount,
positions: splat.data.positions,
rotations: splat.data.rotations,
scales: splat.data.scales,
colors: splat.data.colors,
selection: splat.data.selection,
offset: this._offsets.get(splat) as number,
};
this._worker.postMessage(
{
splat: serializedSplat,
},
[
serializedSplat.position.buffer,
serializedSplat.rotation.buffer,
serializedSplat.scale.buffer,
serializedSplat.positions.buffer,
serializedSplat.rotations.buffer,
serializedSplat.scales.buffer,
serializedSplat.colors.buffer,
serializedSplat.selection.buffer,
],
);
this._updating.add(splat);
splat.data.detached = true;
};
this.getSplat = (index: number) => {
let splat = null;
for (const [key, value] of this._offsets) {
if (index >= value) {
splat = key;
} else {
break;
}
}
return splat;
};
this.getLocalIndex = (splat: Splat, index: number) => {
const offset = this._offsets.get(splat) as number;
return index - offset;
};
this.markDirty = (splat: Splat) => {
this._dirty.add(splat);
};
this.rebuild = () => {
for (const splat of this._dirty) {
build(splat);
}
this._dirty.clear();
};
this.dispose = () => {
this._worker.terminate();
};
for (const splat of this._splatIndices.keys()) {
buildImmediate(splat);
}
updateColorTransforms();
}
get offsets() {
return this._offsets;
}
get data() {
return this._data;
}
get width() {
return this._width;
}
get height() {
return this._height;
}
get transforms() {
return this._transforms;
}
get transformsWidth() {
return this._transformsWidth;
}
get transformsHeight() {
return this._transformsHeight;
}
get transformIndices() {
return this._transformIndices;
}
get transformIndicesWidth() {
return this._transformIndicesWidth;
}
get transformIndicesHeight() {
return this._transformIndicesHeight;
}
get colorTransforms() {
return this._colorTransforms;
}
get colorTransformsWidth() {
return this._colorTransformsWidth;
}
get colorTransformsHeight() {
return this._colorTransformsHeight;
}
get colorTransformIndices() {
return this._colorTransformIndices;
}
get colorTransformIndicesWidth() {
return this._colorTransformIndicesWidth;
}
get colorTransformIndicesHeight() {
return this._colorTransformIndicesHeight;
}
get positions() {
return this._positions;
}
get rotations() {
return this._rotations;
}
get scales() {
return this._scales;
}
get vertexCount() {
return this._vertexCount;
}
get needsRebuild() {
return this._dirty.size > 0;
}
get updating() {
return this._updating.size > 0;
}
}
export { RenderData };