import { Vector3 } from "../math/Vector3"; import { Quaternion } from "../math/Quaternion"; import { Matrix3 } from "../math/Matrix3"; class SplatData { static RowLength = 3 * 4 + 3 * 4 + 4 + 4; public changed = false; public detached = false; private _vertexCount: number; private _positions: Float32Array; private _rotations: Float32Array; private _scales: Float32Array; private _colors: Uint8Array; private _selection: Uint8Array; translate: (translation: Vector3) => void; rotate: (rotation: Quaternion) => void; scale: (scale: Vector3) => void; serialize: () => Uint8Array; reattach: ( positions: ArrayBufferLike, rotations: ArrayBufferLike, scales: ArrayBufferLike, colors: ArrayBufferLike, selection: ArrayBufferLike, ) => void; constructor( vertexCount: number = 0, positions: Float32Array | null = null, rotations: Float32Array | null = null, scales: Float32Array | null = null, colors: Uint8Array | null = null, ) { this._vertexCount = vertexCount; this._positions = positions || new Float32Array(0); this._rotations = rotations || new Float32Array(0); this._scales = scales || new Float32Array(0); this._colors = colors || new Uint8Array(0); this._selection = new Uint8Array(this.vertexCount); this.translate = (translation: Vector3) => { for (let i = 0; i < this.vertexCount; i++) { this.positions[3 * i + 0] += translation.x; this.positions[3 * i + 1] += translation.y; this.positions[3 * i + 2] += translation.z; } this.changed = true; }; this.rotate = (rotation: Quaternion) => { const R = Matrix3.RotationFromQuaternion(rotation).buffer; for (let i = 0; i < this.vertexCount; i++) { const x = this.positions[3 * i + 0]; const y = this.positions[3 * i + 1]; const z = this.positions[3 * i + 2]; this.positions[3 * i + 0] = R[0] * x + R[1] * y + R[2] * z; this.positions[3 * i + 1] = R[3] * x + R[4] * y + R[5] * z; this.positions[3 * i + 2] = R[6] * x + R[7] * y + R[8] * z; const currentRotation = new Quaternion( this.rotations[4 * i + 1], this.rotations[4 * i + 2], this.rotations[4 * i + 3], this.rotations[4 * i + 0], ); const newRot = rotation.multiply(currentRotation); this.rotations[4 * i + 1] = newRot.x; this.rotations[4 * i + 2] = newRot.y; this.rotations[4 * i + 3] = newRot.z; this.rotations[4 * i + 0] = newRot.w; } this.changed = true; }; this.scale = (scale: Vector3) => { for (let i = 0; i < this.vertexCount; i++) { this.positions[3 * i + 0] *= scale.x; this.positions[3 * i + 1] *= scale.y; this.positions[3 * i + 2] *= scale.z; this.scales[3 * i + 0] *= scale.x; this.scales[3 * i + 1] *= scale.y; this.scales[3 * i + 2] *= scale.z; } this.changed = true; }; this.serialize = () => { const data = new Uint8Array(this.vertexCount * SplatData.RowLength); const f_buffer = new Float32Array(data.buffer); const u_buffer = new Uint8Array(data.buffer); for (let i = 0; i < this.vertexCount; i++) { f_buffer[8 * i + 0] = this.positions[3 * i + 0]; f_buffer[8 * i + 1] = this.positions[3 * i + 1]; f_buffer[8 * i + 2] = this.positions[3 * i + 2]; u_buffer[32 * i + 24 + 0] = this.colors[4 * i + 0]; u_buffer[32 * i + 24 + 1] = this.colors[4 * i + 1]; u_buffer[32 * i + 24 + 2] = this.colors[4 * i + 2]; u_buffer[32 * i + 24 + 3] = this.colors[4 * i + 3]; f_buffer[8 * i + 3 + 0] = this.scales[3 * i + 0]; f_buffer[8 * i + 3 + 1] = this.scales[3 * i + 1]; f_buffer[8 * i + 3 + 2] = this.scales[3 * i + 2]; u_buffer[32 * i + 28 + 0] = (this.rotations[4 * i + 0] * 128 + 128) & 0xff; u_buffer[32 * i + 28 + 1] = (this.rotations[4 * i + 1] * 128 + 128) & 0xff; u_buffer[32 * i + 28 + 2] = (this.rotations[4 * i + 2] * 128 + 128) & 0xff; u_buffer[32 * i + 28 + 3] = (this.rotations[4 * i + 3] * 128 + 128) & 0xff; } return data; }; this.reattach = ( positions: ArrayBufferLike, rotations: ArrayBufferLike, scales: ArrayBufferLike, colors: ArrayBufferLike, selection: ArrayBufferLike, ) => { console.assert( positions.byteLength === this.vertexCount * 3 * 4, `Expected ${this.vertexCount * 3 * 4} bytes, got ${positions.byteLength} bytes`, ); this._positions = new Float32Array(positions); this._rotations = new Float32Array(rotations); this._scales = new Float32Array(scales); this._colors = new Uint8Array(colors); this._selection = new Uint8Array(selection); this.detached = false; }; } static Deserialize(data: Uint8Array): SplatData { const vertexCount = data.length / SplatData.RowLength; const positions = new Float32Array(3 * vertexCount); const rotations = new Float32Array(4 * vertexCount); const scales = new Float32Array(3 * vertexCount); const colors = new Uint8Array(4 * vertexCount); const f_buffer = new Float32Array(data.buffer); const u_buffer = new Uint8Array(data.buffer); for (let i = 0; i < vertexCount; i++) { positions[3 * i + 0] = f_buffer[8 * i + 0]; positions[3 * i + 1] = f_buffer[8 * i + 1]; positions[3 * i + 2] = f_buffer[8 * i + 2]; rotations[4 * i + 0] = (u_buffer[32 * i + 28 + 0] - 128) / 128; rotations[4 * i + 1] = (u_buffer[32 * i + 28 + 1] - 128) / 128; rotations[4 * i + 2] = (u_buffer[32 * i + 28 + 2] - 128) / 128; rotations[4 * i + 3] = (u_buffer[32 * i + 28 + 3] - 128) / 128; scales[3 * i + 0] = f_buffer[8 * i + 3 + 0]; scales[3 * i + 1] = f_buffer[8 * i + 3 + 1]; scales[3 * i + 2] = f_buffer[8 * i + 3 + 2]; colors[4 * i + 0] = u_buffer[32 * i + 24 + 0]; colors[4 * i + 1] = u_buffer[32 * i + 24 + 1]; colors[4 * i + 2] = u_buffer[32 * i + 24 + 2]; colors[4 * i + 3] = u_buffer[32 * i + 24 + 3]; } return new SplatData(vertexCount, positions, rotations, scales, colors); } get vertexCount() { return this._vertexCount; } get positions() { return this._positions; } get rotations() { return this._rotations; } get scales() { return this._scales; } get colors() { return this._colors; } get selection() { return this._selection; } clone() { return new SplatData( this.vertexCount, new Float32Array(this.positions), new Float32Array(this.rotations), new Float32Array(this.scales), new Uint8Array(this.colors), ); } } export { SplatData };