@myned-ai/gsplat-flame-avatar-renderer 1.0.6 → 1.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +30 -0
- package/dist/gsplat-flame-avatar-renderer.cjs.js +38 -33
- package/dist/gsplat-flame-avatar-renderer.cjs.min.js +1 -1
- package/dist/gsplat-flame-avatar-renderer.cjs.min.js.map +1 -1
- package/dist/gsplat-flame-avatar-renderer.esm.js +38 -33
- package/dist/gsplat-flame-avatar-renderer.esm.min.js +1 -1
- package/dist/gsplat-flame-avatar-renderer.esm.min.js.map +1 -1
- package/package.json +5 -2
- package/src/api/index.js +0 -7
- package/src/buffers/SplatBuffer.js +0 -1394
- package/src/buffers/SplatBufferGenerator.js +0 -41
- package/src/buffers/SplatPartitioner.js +0 -110
- package/src/buffers/UncompressedSplatArray.js +0 -106
- package/src/buffers/index.js +0 -11
- package/src/core/SplatGeometry.js +0 -48
- package/src/core/SplatMesh.js +0 -2627
- package/src/core/SplatScene.js +0 -43
- package/src/core/SplatTree.js +0 -200
- package/src/core/Viewer.js +0 -2746
- package/src/core/index.js +0 -13
- package/src/enums/EngineConstants.js +0 -58
- package/src/enums/LogLevel.js +0 -13
- package/src/enums/RenderMode.js +0 -11
- package/src/enums/SceneFormat.js +0 -21
- package/src/enums/SceneRevealMode.js +0 -11
- package/src/enums/SplatRenderMode.js +0 -10
- package/src/enums/index.js +0 -13
- package/src/errors/ApplicationError.js +0 -185
- package/src/errors/index.js +0 -17
- package/src/flame/FlameAnimator.js +0 -496
- package/src/flame/FlameConstants.js +0 -21
- package/src/flame/FlameTextureManager.js +0 -293
- package/src/flame/index.js +0 -22
- package/src/flame/utils.js +0 -50
- package/src/index.js +0 -39
- package/src/loaders/DirectLoadError.js +0 -14
- package/src/loaders/INRIAV1PlyParser.js +0 -223
- package/src/loaders/PlyLoader.js +0 -519
- package/src/loaders/PlyParser.js +0 -19
- package/src/loaders/PlyParserUtils.js +0 -311
- package/src/loaders/index.js +0 -13
- package/src/materials/SplatMaterial.js +0 -1068
- package/src/materials/SplatMaterial2D.js +0 -358
- package/src/materials/SplatMaterial3D.js +0 -323
- package/src/materials/index.js +0 -11
- package/src/raycaster/Hit.js +0 -37
- package/src/raycaster/Ray.js +0 -123
- package/src/raycaster/Raycaster.js +0 -175
- package/src/raycaster/index.js +0 -10
- package/src/renderer/AnimationManager.js +0 -577
- package/src/renderer/AppConstants.js +0 -101
- package/src/renderer/GaussianSplatRenderer.js +0 -1146
- package/src/renderer/index.js +0 -24
- package/src/utils/BlobUrlManager.js +0 -294
- package/src/utils/EventEmitter.js +0 -349
- package/src/utils/LoaderUtils.js +0 -66
- package/src/utils/Logger.js +0 -171
- package/src/utils/ObjectPool.js +0 -248
- package/src/utils/RenderLoop.js +0 -306
- package/src/utils/Util.js +0 -416
- package/src/utils/ValidationUtils.js +0 -331
- package/src/utils/index.js +0 -18
- package/src/worker/SortWorker.js +0 -284
- package/src/worker/index.js +0 -8
|
@@ -1,293 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* FlameTextureManager
|
|
3
|
-
*
|
|
4
|
-
* Derived from gaussian-splat-renderer-for-lam
|
|
5
|
-
*
|
|
6
|
-
* Manages GPU textures for FLAME parametric head model:
|
|
7
|
-
* - Blendshape morph target data textures
|
|
8
|
-
* - Bone matrix textures for skeleton animation
|
|
9
|
-
* - LBS weight textures for skinning
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
import {
|
|
13
|
-
Vector2,
|
|
14
|
-
DataTexture,
|
|
15
|
-
RGBAIntegerFormat,
|
|
16
|
-
UnsignedIntType
|
|
17
|
-
} from 'three';
|
|
18
|
-
|
|
19
|
-
import { uintEncodedFloat } from './utils.js';
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Build the FLAME model texture containing blendshape positions
|
|
23
|
-
* @param {THREE.SkinnedMesh} flameModel - The FLAME mesh with morph targets
|
|
24
|
-
* @param {THREE.ShaderMaterial} material - The splat material to update uniforms
|
|
25
|
-
* @param {number} gaussianSplatCount - Number of gaussian splats
|
|
26
|
-
* @returns {object} Texture data object
|
|
27
|
-
*/
|
|
28
|
-
export function buildModelTexture(flameModel, material, gaussianSplatCount) {
|
|
29
|
-
const flameModelTexSize = new Vector2(4096, 2048);
|
|
30
|
-
|
|
31
|
-
const shapedMesh = flameModel.geometry.attributes.position.array;
|
|
32
|
-
let shapedMeshArray = [];
|
|
33
|
-
const pointNum = shapedMesh.length / 3;
|
|
34
|
-
const bsLength = flameModel.geometry.morphAttributes.position.length;
|
|
35
|
-
|
|
36
|
-
// Sort morph targets by dictionary order
|
|
37
|
-
const morphTargetNames = Object.keys(flameModel.morphTargetDictionary);
|
|
38
|
-
morphTargetNames.forEach((name) => {
|
|
39
|
-
const originalIndex = flameModel.morphTargetDictionary[name];
|
|
40
|
-
const bsMesh = flameModel.geometry.morphAttributes.position[originalIndex];
|
|
41
|
-
shapedMeshArray = shapedMeshArray.concat(Array.from(bsMesh.array));
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
// Add base mesh positions
|
|
45
|
-
shapedMeshArray = shapedMeshArray.concat(Array.from(shapedMesh));
|
|
46
|
-
|
|
47
|
-
// Create texture data
|
|
48
|
-
const flameModelData = new Float32Array(flameModelTexSize.x * flameModelTexSize.y * 4);
|
|
49
|
-
const flameModelDataInt = new Uint32Array(flameModelTexSize.x * flameModelTexSize.y * 4);
|
|
50
|
-
|
|
51
|
-
for (let c = 0; c < pointNum * (bsLength + 1); c++) {
|
|
52
|
-
flameModelData[c * 4 + 0] = shapedMeshArray[c * 3 + 0];
|
|
53
|
-
flameModelData[c * 4 + 1] = shapedMeshArray[c * 3 + 1];
|
|
54
|
-
flameModelData[c * 4 + 2] = shapedMeshArray[c * 3 + 2];
|
|
55
|
-
|
|
56
|
-
flameModelDataInt[c * 4 + 0] = uintEncodedFloat(flameModelData[c * 4 + 0]);
|
|
57
|
-
flameModelDataInt[c * 4 + 1] = uintEncodedFloat(flameModelData[c * 4 + 1]);
|
|
58
|
-
flameModelDataInt[c * 4 + 2] = uintEncodedFloat(flameModelData[c * 4 + 2]);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
const flameModelTex = new DataTexture(
|
|
62
|
-
flameModelDataInt,
|
|
63
|
-
flameModelTexSize.x,
|
|
64
|
-
flameModelTexSize.y,
|
|
65
|
-
RGBAIntegerFormat,
|
|
66
|
-
UnsignedIntType
|
|
67
|
-
);
|
|
68
|
-
flameModelTex.internalFormat = 'RGBA32UI';
|
|
69
|
-
flameModelTex.needsUpdate = true;
|
|
70
|
-
|
|
71
|
-
// Update material uniforms
|
|
72
|
-
material.uniforms.flameModelTexture.value = flameModelTex;
|
|
73
|
-
material.uniforms.flameModelTextureSize.value.copy(flameModelTexSize);
|
|
74
|
-
material.uniforms.gaussianSplatCount.value = gaussianSplatCount;
|
|
75
|
-
material.uniformsNeedUpdate = true;
|
|
76
|
-
|
|
77
|
-
return {
|
|
78
|
-
data: flameModelDataInt,
|
|
79
|
-
texture: flameModelTex,
|
|
80
|
-
size: flameModelTexSize,
|
|
81
|
-
baseData: { flameModelPos: flameModelData }
|
|
82
|
-
};
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
* Build bone matrix texture for skeleton animation
|
|
87
|
-
* @param {Float32Array} bonesMatrix - Flattened bone matrices
|
|
88
|
-
* @param {number} bonesNum - Number of bones
|
|
89
|
-
* @param {object} bsWeight - Blendshape weights object
|
|
90
|
-
* @param {object} morphTargetDictionary - Maps blendshape names to indices
|
|
91
|
-
* @param {THREE.SkinnedMesh} flameModel - The FLAME mesh
|
|
92
|
-
* @param {THREE.ShaderMaterial} material - The splat material
|
|
93
|
-
* @param {boolean} useFlameModel - Use FLAME mode
|
|
94
|
-
* @returns {object} Texture data object
|
|
95
|
-
*/
|
|
96
|
-
export function buildBoneMatrixTexture(
|
|
97
|
-
bonesMatrix,
|
|
98
|
-
bonesNum,
|
|
99
|
-
bsWeight,
|
|
100
|
-
morphTargetDictionary,
|
|
101
|
-
flameModel,
|
|
102
|
-
material,
|
|
103
|
-
useFlameModel
|
|
104
|
-
) {
|
|
105
|
-
if (!bsWeight) return null;
|
|
106
|
-
|
|
107
|
-
// bonesNum + expressionBSNum / 4 = 30, so texture height is 32
|
|
108
|
-
const boneTextureSize = new Vector2(4, 32);
|
|
109
|
-
const boneMatrixTextureData = new Float32Array(bonesMatrix);
|
|
110
|
-
const boneMatrixTextureDataInt = new Uint32Array(boneTextureSize.x * boneTextureSize.y * 4);
|
|
111
|
-
|
|
112
|
-
if (useFlameModel) {
|
|
113
|
-
// Encode bone matrices
|
|
114
|
-
for (let c = 0; c < bonesNum * 16; c++) {
|
|
115
|
-
boneMatrixTextureDataInt[c] = uintEncodedFloat(boneMatrixTextureData[c]);
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
// Set skeleton uniforms
|
|
119
|
-
if (flameModel && flameModel.skeleton) {
|
|
120
|
-
material.uniforms.boneTexture0.value = flameModel.skeleton.boneTexture;
|
|
121
|
-
material.uniforms.bindMatrix.value = flameModel.bindMatrix;
|
|
122
|
-
material.uniforms.bindMatrixInverse.value = flameModel.bindMatrixInverse;
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
// Encode blendshape weights
|
|
127
|
-
for (const key in bsWeight) {
|
|
128
|
-
if (Object.hasOwn(bsWeight, key)) {
|
|
129
|
-
const value = bsWeight[key];
|
|
130
|
-
const idx = morphTargetDictionary[key];
|
|
131
|
-
boneMatrixTextureDataInt[idx + bonesNum * 16] = uintEncodedFloat(value);
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
const boneMatrixTex = new DataTexture(
|
|
136
|
-
boneMatrixTextureDataInt,
|
|
137
|
-
boneTextureSize.x,
|
|
138
|
-
boneTextureSize.y,
|
|
139
|
-
RGBAIntegerFormat,
|
|
140
|
-
UnsignedIntType
|
|
141
|
-
);
|
|
142
|
-
boneMatrixTex.internalFormat = 'RGBA32UI';
|
|
143
|
-
boneMatrixTex.needsUpdate = true;
|
|
144
|
-
|
|
145
|
-
material.uniforms.boneTexture.value = boneMatrixTex;
|
|
146
|
-
material.uniforms.boneTextureSize.value.copy(boneTextureSize);
|
|
147
|
-
material.uniformsNeedUpdate = true;
|
|
148
|
-
|
|
149
|
-
return {
|
|
150
|
-
data: boneMatrixTextureDataInt,
|
|
151
|
-
texture: boneMatrixTex,
|
|
152
|
-
size: boneTextureSize,
|
|
153
|
-
baseData: { boneMatrix: boneMatrixTextureDataInt }
|
|
154
|
-
};
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
/**
|
|
158
|
-
* Update bone matrix texture with new animation data
|
|
159
|
-
* @param {object} splatDataTextures - Texture data storage
|
|
160
|
-
* @param {Float32Array} bonesMatrix - Updated bone matrices
|
|
161
|
-
* @param {number} bonesNum - Number of bones
|
|
162
|
-
* @param {object} bsWeight - Updated blendshape weights
|
|
163
|
-
* @param {object} morphTargetDictionary - Blendshape name to index map
|
|
164
|
-
* @param {THREE.SkinnedMesh} flameModel - The FLAME mesh
|
|
165
|
-
* @param {THREE.ShaderMaterial} material - The splat material
|
|
166
|
-
* @param {boolean} updateFlameBoneMatrix - Whether to update bone matrices
|
|
167
|
-
*/
|
|
168
|
-
export function updateBoneMatrixTexture(
|
|
169
|
-
splatDataTextures,
|
|
170
|
-
bonesMatrix,
|
|
171
|
-
bonesNum,
|
|
172
|
-
bsWeight,
|
|
173
|
-
morphTargetDictionary,
|
|
174
|
-
flameModel,
|
|
175
|
-
material,
|
|
176
|
-
updateFlameBoneMatrix = false
|
|
177
|
-
) {
|
|
178
|
-
if (!bsWeight || !morphTargetDictionary) return;
|
|
179
|
-
|
|
180
|
-
if (updateFlameBoneMatrix) {
|
|
181
|
-
const boneMatrixTextureData = new Float32Array(bonesMatrix);
|
|
182
|
-
for (let c = 0; c < bonesNum * 16; c++) {
|
|
183
|
-
splatDataTextures.baseData['boneMatrix'][c] = uintEncodedFloat(boneMatrixTextureData[c]);
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
// Update blendshape weights
|
|
188
|
-
for (const key in bsWeight) {
|
|
189
|
-
if (Object.hasOwn(bsWeight, key)) {
|
|
190
|
-
const value = bsWeight[key];
|
|
191
|
-
const idx = morphTargetDictionary[key];
|
|
192
|
-
splatDataTextures.baseData['boneMatrix'][idx + bonesNum * 16] = uintEncodedFloat(value);
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
// Update texture data
|
|
197
|
-
splatDataTextures['boneMatrix']['texture'].data = splatDataTextures.baseData['boneMatrix'];
|
|
198
|
-
splatDataTextures['boneMatrix']['texture'].needsUpdate = true;
|
|
199
|
-
material.uniforms.boneTexture.value = splatDataTextures['boneMatrix']['texture'];
|
|
200
|
-
|
|
201
|
-
// Update skeleton uniforms
|
|
202
|
-
if (flameModel.skeleton) {
|
|
203
|
-
material.uniforms.boneTexture0.value = flameModel.skeleton.boneTexture;
|
|
204
|
-
material.uniforms.bindMatrix.value = flameModel.bindMatrix;
|
|
205
|
-
material.uniforms.bindMatrixInverse.value = flameModel.bindMatrixInverse;
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
material.uniformsNeedUpdate = true;
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
/**
|
|
212
|
-
* Build LBS weight texture for skin deformation
|
|
213
|
-
* @param {THREE.SkinnedMesh} flameModel - The FLAME mesh
|
|
214
|
-
* @param {Array} bonesWeight - Per-vertex bone weights array
|
|
215
|
-
* @param {THREE.ShaderMaterial} material - The splat material
|
|
216
|
-
* @returns {object} Texture data object
|
|
217
|
-
*/
|
|
218
|
-
export function buildBoneWeightTexture(flameModel, bonesWeight, material) {
|
|
219
|
-
const shapedMesh = flameModel.geometry.attributes.position.array;
|
|
220
|
-
const pointNum = shapedMesh.length / 3;
|
|
221
|
-
|
|
222
|
-
const boneWeightTextureSize = new Vector2(512, 512);
|
|
223
|
-
const boneWeightTextureData = new Float32Array(boneWeightTextureSize.x * boneWeightTextureSize.y * 4);
|
|
224
|
-
const boneWeightTextureDataInt = new Uint32Array(boneWeightTextureSize.x * boneWeightTextureSize.y * 4);
|
|
225
|
-
|
|
226
|
-
for (let i = 0; i < pointNum; i++) {
|
|
227
|
-
// Store 5 bone weights per vertex (2 texels)
|
|
228
|
-
boneWeightTextureData[i * 8 + 0] = bonesWeight[i][0];
|
|
229
|
-
boneWeightTextureData[i * 8 + 1] = bonesWeight[i][1];
|
|
230
|
-
boneWeightTextureData[i * 8 + 2] = bonesWeight[i][2];
|
|
231
|
-
boneWeightTextureData[i * 8 + 3] = bonesWeight[i][3];
|
|
232
|
-
boneWeightTextureData[i * 8 + 4] = bonesWeight[i][4];
|
|
233
|
-
|
|
234
|
-
boneWeightTextureDataInt[i * 8 + 0] = uintEncodedFloat(bonesWeight[i][0]);
|
|
235
|
-
boneWeightTextureDataInt[i * 8 + 1] = uintEncodedFloat(bonesWeight[i][1]);
|
|
236
|
-
boneWeightTextureDataInt[i * 8 + 2] = uintEncodedFloat(bonesWeight[i][2]);
|
|
237
|
-
boneWeightTextureDataInt[i * 8 + 3] = uintEncodedFloat(bonesWeight[i][3]);
|
|
238
|
-
boneWeightTextureDataInt[i * 8 + 4] = uintEncodedFloat(bonesWeight[i][4]);
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
const boneWeightTex = new DataTexture(
|
|
242
|
-
boneWeightTextureDataInt,
|
|
243
|
-
boneWeightTextureSize.x,
|
|
244
|
-
boneWeightTextureSize.y,
|
|
245
|
-
RGBAIntegerFormat,
|
|
246
|
-
UnsignedIntType
|
|
247
|
-
);
|
|
248
|
-
boneWeightTex.internalFormat = 'RGBA32UI';
|
|
249
|
-
boneWeightTex.needsUpdate = true;
|
|
250
|
-
|
|
251
|
-
material.uniforms.boneWeightTexture.value = boneWeightTex;
|
|
252
|
-
material.uniforms.boneWeightTextureSize.value.copy(boneWeightTextureSize);
|
|
253
|
-
material.uniformsNeedUpdate = true;
|
|
254
|
-
|
|
255
|
-
return {
|
|
256
|
-
data: boneWeightTextureDataInt,
|
|
257
|
-
texture: boneWeightTex,
|
|
258
|
-
size: boneWeightTextureSize,
|
|
259
|
-
baseData: { boneWeight: boneWeightTextureDataInt }
|
|
260
|
-
};
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
/**
|
|
264
|
-
* Get updated bone matrices from skeleton
|
|
265
|
-
* @param {THREE.Skeleton} skeleton - The skeleton to read matrices from
|
|
266
|
-
* @param {number} boneNum - Number of bones
|
|
267
|
-
* @returns {Float32Array} Flattened bone matrices
|
|
268
|
-
*/
|
|
269
|
-
export function getUpdatedBoneMatrices(skeleton, boneNum) {
|
|
270
|
-
const updatedBoneMatrices = [];
|
|
271
|
-
|
|
272
|
-
for (let j = 0; j < boneNum; j++) {
|
|
273
|
-
const boneMatrix = skeleton.bones[j].matrixWorld.clone()
|
|
274
|
-
.multiply(skeleton.boneInverses[j].clone());
|
|
275
|
-
|
|
276
|
-
const elements = boneMatrix.elements;
|
|
277
|
-
for (let i = 0; i < elements.length; i++) {
|
|
278
|
-
updatedBoneMatrices.push(elements[i]);
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
return new Float32Array(updatedBoneMatrices);
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
export const FlameTextureManager = {
|
|
286
|
-
buildModelTexture,
|
|
287
|
-
buildBoneMatrixTexture,
|
|
288
|
-
updateBoneMatrixTexture,
|
|
289
|
-
buildBoneWeightTexture,
|
|
290
|
-
getUpdatedBoneMatrices
|
|
291
|
-
};
|
|
292
|
-
|
|
293
|
-
export default FlameTextureManager;
|
package/src/flame/index.js
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* gsplat-flame-avatar - FLAME Module
|
|
3
|
-
* Modified GaussianSplats3D components with FLAME parametric head model support
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
// FLAME-specific Constants (exported as FlameConstants to avoid conflict)
|
|
7
|
-
export { Constants as FlameConstants } from './FlameConstants.js';
|
|
8
|
-
|
|
9
|
-
// Texture Constants (not duplicated elsewhere)
|
|
10
|
-
export { TextureConstants } from './utils.js';
|
|
11
|
-
|
|
12
|
-
// FLAME support
|
|
13
|
-
export {
|
|
14
|
-
FlameTextureManager,
|
|
15
|
-
buildModelTexture,
|
|
16
|
-
buildBoneMatrixTexture,
|
|
17
|
-
updateBoneMatrixTexture,
|
|
18
|
-
buildBoneWeightTexture,
|
|
19
|
-
getUpdatedBoneMatrices
|
|
20
|
-
} from './FlameTextureManager.js';
|
|
21
|
-
|
|
22
|
-
export { FlameAnimator } from './FlameAnimator.js';
|
package/src/flame/utils.js
DELETED
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* FLAME Utility Functions
|
|
3
|
-
*
|
|
4
|
-
* Derived from gaussian-splat-renderer-for-lam
|
|
5
|
-
* Contains encoding functions and helpers for GPU texture data.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Encode float as uint32 for GPU texture storage
|
|
10
|
-
* Uses Float32Array/Int32Array view trick to reinterpret float bits as integer
|
|
11
|
-
*/
|
|
12
|
-
export const uintEncodedFloat = (function() {
|
|
13
|
-
const floatView = new Float32Array(1);
|
|
14
|
-
const int32View = new Int32Array(floatView.buffer);
|
|
15
|
-
|
|
16
|
-
return function(f) {
|
|
17
|
-
floatView[0] = f;
|
|
18
|
-
return int32View[0];
|
|
19
|
-
};
|
|
20
|
-
})();
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Convert RGBA values to a single integer
|
|
24
|
-
*/
|
|
25
|
-
export const rgbaToInteger = function(r, g, b, a) {
|
|
26
|
-
return r + (g << 8) + (b << 16) + (a << 24);
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Convert RGBA array at offset to a single integer
|
|
31
|
-
*/
|
|
32
|
-
export const rgbaArrayToInteger = function(arr, offset) {
|
|
33
|
-
return arr[offset] + (arr[offset + 1] << 8) + (arr[offset + 2] << 16) + (arr[offset + 3] << 24);
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Constants for texture data layout
|
|
38
|
-
*/
|
|
39
|
-
export const TextureConstants = {
|
|
40
|
-
CENTER_COLORS_ELEMENTS_PER_TEXEL: 4,
|
|
41
|
-
CENTER_COLORS_ELEMENTS_PER_SPLAT: 4,
|
|
42
|
-
COVARIANCES_ELEMENTS_PER_TEXEL_STORED: 2,
|
|
43
|
-
COVARIANCES_ELEMENTS_PER_TEXEL_ALLOCATED: 4,
|
|
44
|
-
COVARIANCES_ELEMENTS_PER_TEXEL_COMPRESSED_STORED: 2,
|
|
45
|
-
COVARIANCES_ELEMENTS_PER_TEXEL_COMPRESSED_ALLOCATED: 4,
|
|
46
|
-
COVARIANCES_ELEMENTS_PER_SPLAT: 6,
|
|
47
|
-
SCALES_ROTATIONS_ELEMENTS_PER_TEXEL: 4,
|
|
48
|
-
SCENE_INDEXES_ELEMENTS_PER_TEXEL: 1,
|
|
49
|
-
MAX_TEXTURE_TEXELS: 4096 * 4096
|
|
50
|
-
};
|
package/src/index.js
DELETED
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* gsplat-flame-avatar
|
|
3
|
-
*
|
|
4
|
-
* A specialized Gaussian Splatting library with FLAME parametric head model support.
|
|
5
|
-
* Built on Three.js for real-time facial animation of Gaussian Splat avatars.
|
|
6
|
-
*
|
|
7
|
-
* Based on @mkkellogg/gaussian-splats-3d (MIT License)
|
|
8
|
-
* Extended with FLAME integration from gaussian-splat-renderer-for-lam
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
// Enums
|
|
12
|
-
export * from './enums/index.js';
|
|
13
|
-
|
|
14
|
-
// Utils
|
|
15
|
-
export * from './utils/index.js';
|
|
16
|
-
|
|
17
|
-
// Api
|
|
18
|
-
export * from './api/index.js';
|
|
19
|
-
|
|
20
|
-
// Renderer (GaussianSplatRenderer, AnimationManager, state classes)
|
|
21
|
-
export * from './renderer/index.js';
|
|
22
|
-
|
|
23
|
-
// Buffers
|
|
24
|
-
export * from './buffers/index.js';
|
|
25
|
-
|
|
26
|
-
// Core (Viewer, SplatMesh, etc.)
|
|
27
|
-
export * from './core/index.js';
|
|
28
|
-
|
|
29
|
-
// Loaders
|
|
30
|
-
export * from './loaders/index.js';
|
|
31
|
-
|
|
32
|
-
// Materials
|
|
33
|
-
export * from './materials/index.js';
|
|
34
|
-
|
|
35
|
-
// Raycaster
|
|
36
|
-
export * from './raycaster/index.js';
|
|
37
|
-
|
|
38
|
-
// FLAME support
|
|
39
|
-
export * from './flame/index.js';
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* DirectLoadError
|
|
3
|
-
*
|
|
4
|
-
* Derived from @mkkellogg/gaussian-splats-3d (MIT License)
|
|
5
|
-
* https://github.com/mkkellogg/GaussianSplats3D
|
|
6
|
-
*
|
|
7
|
-
* Minor enhancement: Added this.name property for better error identification.
|
|
8
|
-
*/
|
|
9
|
-
export class DirectLoadError extends Error {
|
|
10
|
-
constructor(msg) {
|
|
11
|
-
super(msg);
|
|
12
|
-
this.name = 'DirectLoadError';
|
|
13
|
-
}
|
|
14
|
-
}
|
|
@@ -1,223 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* INRIAV1PlyParser
|
|
3
|
-
*
|
|
4
|
-
* Derived from @mkkellogg/gaussian-splats-3d (MIT License)
|
|
5
|
-
* https://github.com/mkkellogg/GaussianSplats3D
|
|
6
|
-
*
|
|
7
|
-
* Import paths adjusted for gsplat-flame-avatar package structure.
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
import { Quaternion } from 'three';
|
|
11
|
-
import { UncompressedSplatArray } from '../buffers/UncompressedSplatArray.js';
|
|
12
|
-
import { SplatBuffer } from '../buffers/SplatBuffer.js';
|
|
13
|
-
import { PlyParserUtils } from './PlyParserUtils.js';
|
|
14
|
-
import { clamp } from '../utils/Util.js';
|
|
15
|
-
|
|
16
|
-
const BaseFieldNamesToRead = ['scale_0', 'scale_1', 'scale_2', 'rot_0', 'rot_1', 'rot_2', 'rot_3', 'x', 'y', 'z',
|
|
17
|
-
'f_dc_0', 'f_dc_1', 'f_dc_2', 'opacity', 'red', 'green', 'blue', 'f_rest_0'];
|
|
18
|
-
|
|
19
|
-
const BaseFieldsToReadIndexes = BaseFieldNamesToRead.map((e, i) => i);
|
|
20
|
-
|
|
21
|
-
const [
|
|
22
|
-
SCALE_0, SCALE_1, SCALE_2, ROT_0, ROT_1, ROT_2, ROT_3, X, Y, Z, F_DC_0, F_DC_1, F_DC_2, OPACITY, RED, GREEN, BLUE, F_REST_0
|
|
23
|
-
] = BaseFieldsToReadIndexes;
|
|
24
|
-
|
|
25
|
-
export class INRIAV1PlyParser {
|
|
26
|
-
|
|
27
|
-
constructor() {
|
|
28
|
-
this.plyParserutils = new PlyParserUtils();
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
decodeHeaderLines(headerLines) {
|
|
32
|
-
|
|
33
|
-
let shLineCount = 0;
|
|
34
|
-
headerLines.forEach((line) => {
|
|
35
|
-
if (line.includes('f_rest_')) shLineCount++;
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
let shFieldsToReadCount = 0;
|
|
39
|
-
if (shLineCount >= 45) {
|
|
40
|
-
shFieldsToReadCount = 45;
|
|
41
|
-
} else if (shLineCount >= 24) {
|
|
42
|
-
shFieldsToReadCount = 24;
|
|
43
|
-
} else if (shLineCount >= 9) {
|
|
44
|
-
shFieldsToReadCount = 9;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
const shFieldIndexesToMap = Array.from(Array(Math.max(shFieldsToReadCount - 1, 0)));
|
|
48
|
-
let shRemainingFieldNamesToRead = shFieldIndexesToMap.map((element, index) => `f_rest_${index + 1}`);
|
|
49
|
-
|
|
50
|
-
const fieldNamesToRead = [...BaseFieldNamesToRead, ...shRemainingFieldNamesToRead];
|
|
51
|
-
const fieldsToReadIndexes = fieldNamesToRead.map((e, i) => i);
|
|
52
|
-
|
|
53
|
-
const fieldNameIdMap = fieldsToReadIndexes.reduce((acc, element) => {
|
|
54
|
-
acc[fieldNamesToRead[element]] = element;
|
|
55
|
-
return acc;
|
|
56
|
-
}, {});
|
|
57
|
-
const header = this.plyParserutils.decodeSectionHeader(headerLines, fieldNameIdMap, 0);
|
|
58
|
-
header.splatCount = header.vertexCount;
|
|
59
|
-
header.bytesPerSplat = header.bytesPerVertex;
|
|
60
|
-
header.fieldsToReadIndexes = fieldsToReadIndexes;
|
|
61
|
-
return header;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
decodeHeaderText(headerText) {
|
|
65
|
-
const headerLines = PlyParserUtils.convertHeaderTextToLines(headerText);
|
|
66
|
-
const header = this.decodeHeaderLines(headerLines);
|
|
67
|
-
header.headerText = headerText;
|
|
68
|
-
header.headerSizeBytes = headerText.indexOf(PlyParserUtils.HeaderEndToken) + PlyParserUtils.HeaderEndToken.length + 1;
|
|
69
|
-
return header;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
decodeHeaderFromBuffer(plyBuffer) {
|
|
73
|
-
const headerText = this.plyParserutils.readHeaderFromBuffer(plyBuffer);
|
|
74
|
-
return this.decodeHeaderText(headerText);
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
findSplatData(plyBuffer, header) {
|
|
78
|
-
return new DataView(plyBuffer, header.headerSizeBytes);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
parseToUncompressedSplatBufferSection(header, fromSplat, toSplat, splatData, splatDataOffset,
|
|
82
|
-
toBuffer, toOffset, outSphericalHarmonicsDegree = 0) {
|
|
83
|
-
outSphericalHarmonicsDegree = Math.min(outSphericalHarmonicsDegree, header.sphericalHarmonicsDegree);
|
|
84
|
-
const outBytesPerSplat = SplatBuffer.CompressionLevels[0].SphericalHarmonicsDegrees[outSphericalHarmonicsDegree].BytesPerSplat;
|
|
85
|
-
|
|
86
|
-
for (let i = fromSplat; i <= toSplat; i++) {
|
|
87
|
-
const parsedSplat = INRIAV1PlyParser.parseToUncompressedSplat(splatData, i, header,
|
|
88
|
-
splatDataOffset, outSphericalHarmonicsDegree);
|
|
89
|
-
const outBase = i * outBytesPerSplat + toOffset;
|
|
90
|
-
SplatBuffer.writeSplatDataToSectionBuffer(parsedSplat, toBuffer, outBase, 0, outSphericalHarmonicsDegree);
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
parseToUncompressedSplatArraySection(header, fromSplat, toSplat, splatData, splatDataOffset,
|
|
95
|
-
splatArray, outSphericalHarmonicsDegree = 0) {
|
|
96
|
-
outSphericalHarmonicsDegree = Math.min(outSphericalHarmonicsDegree, header.sphericalHarmonicsDegree);
|
|
97
|
-
for (let i = fromSplat; i <= toSplat; i++) {
|
|
98
|
-
const parsedSplat = INRIAV1PlyParser.parseToUncompressedSplat(splatData, i, header,
|
|
99
|
-
splatDataOffset, outSphericalHarmonicsDegree);
|
|
100
|
-
splatArray.addSplat(parsedSplat);
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
decodeSectionSplatData(sectionSplatData, splatCount, sectionHeader, outSphericalHarmonicsDegree) {
|
|
105
|
-
outSphericalHarmonicsDegree = Math.min(outSphericalHarmonicsDegree, sectionHeader.sphericalHarmonicsDegree);
|
|
106
|
-
const splatArray = new UncompressedSplatArray(outSphericalHarmonicsDegree);
|
|
107
|
-
for (let row = 0; row < splatCount; row++) {
|
|
108
|
-
const newSplat = INRIAV1PlyParser.parseToUncompressedSplat(sectionSplatData, row, sectionHeader,
|
|
109
|
-
0, outSphericalHarmonicsDegree);
|
|
110
|
-
splatArray.addSplat(newSplat);
|
|
111
|
-
}
|
|
112
|
-
return splatArray;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
static parseToUncompressedSplat = function() {
|
|
116
|
-
|
|
117
|
-
let rawSplat = [];
|
|
118
|
-
const tempRotation = new Quaternion();
|
|
119
|
-
|
|
120
|
-
const OFFSET_X = UncompressedSplatArray.OFFSET.X;
|
|
121
|
-
const OFFSET_Y = UncompressedSplatArray.OFFSET.Y;
|
|
122
|
-
const OFFSET_Z = UncompressedSplatArray.OFFSET.Z;
|
|
123
|
-
|
|
124
|
-
const OFFSET_SCALE0 = UncompressedSplatArray.OFFSET.SCALE0;
|
|
125
|
-
const OFFSET_SCALE1 = UncompressedSplatArray.OFFSET.SCALE1;
|
|
126
|
-
const OFFSET_SCALE2 = UncompressedSplatArray.OFFSET.SCALE2;
|
|
127
|
-
|
|
128
|
-
const OFFSET_ROTATION0 = UncompressedSplatArray.OFFSET.ROTATION0;
|
|
129
|
-
const OFFSET_ROTATION1 = UncompressedSplatArray.OFFSET.ROTATION1;
|
|
130
|
-
const OFFSET_ROTATION2 = UncompressedSplatArray.OFFSET.ROTATION2;
|
|
131
|
-
const OFFSET_ROTATION3 = UncompressedSplatArray.OFFSET.ROTATION3;
|
|
132
|
-
|
|
133
|
-
const OFFSET_FDC0 = UncompressedSplatArray.OFFSET.FDC0;
|
|
134
|
-
const OFFSET_FDC1 = UncompressedSplatArray.OFFSET.FDC1;
|
|
135
|
-
const OFFSET_FDC2 = UncompressedSplatArray.OFFSET.FDC2;
|
|
136
|
-
const OFFSET_OPACITY = UncompressedSplatArray.OFFSET.OPACITY;
|
|
137
|
-
|
|
138
|
-
const OFFSET_FRC = [];
|
|
139
|
-
|
|
140
|
-
for (let i = 0; i < 45; i++) {
|
|
141
|
-
OFFSET_FRC[i] = UncompressedSplatArray.OFFSET.FRC0 + i;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
return function(splatData, row, header, splatDataOffset = 0, outSphericalHarmonicsDegree = 0) {
|
|
145
|
-
outSphericalHarmonicsDegree = Math.min(outSphericalHarmonicsDegree, header.sphericalHarmonicsDegree);
|
|
146
|
-
INRIAV1PlyParser.readSplat(splatData, header, row, splatDataOffset, rawSplat);
|
|
147
|
-
const newSplat = UncompressedSplatArray.createSplat(outSphericalHarmonicsDegree);
|
|
148
|
-
if (rawSplat[SCALE_0] !== undefined) {
|
|
149
|
-
newSplat[OFFSET_SCALE0] = Math.exp(rawSplat[SCALE_0]);
|
|
150
|
-
newSplat[OFFSET_SCALE1] = Math.exp(rawSplat[SCALE_1]);
|
|
151
|
-
newSplat[OFFSET_SCALE2] = Math.exp(rawSplat[SCALE_2]);
|
|
152
|
-
} else {
|
|
153
|
-
newSplat[OFFSET_SCALE0] = 0.01;
|
|
154
|
-
newSplat[OFFSET_SCALE1] = 0.01;
|
|
155
|
-
newSplat[OFFSET_SCALE2] = 0.01;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
if (rawSplat[F_DC_0] !== undefined) {
|
|
159
|
-
const SH_C0 = 0.28209479177387814;
|
|
160
|
-
newSplat[OFFSET_FDC0] = rawSplat[F_DC_0] * 255;
|
|
161
|
-
newSplat[OFFSET_FDC1] = rawSplat[F_DC_1] * 255;
|
|
162
|
-
newSplat[OFFSET_FDC2] = rawSplat[F_DC_2] * 255;
|
|
163
|
-
} else if (rawSplat[RED] !== undefined) {
|
|
164
|
-
newSplat[OFFSET_FDC0] = rawSplat[RED] * 255;
|
|
165
|
-
newSplat[OFFSET_FDC1] = rawSplat[GREEN] * 255;
|
|
166
|
-
newSplat[OFFSET_FDC2] = rawSplat[BLUE] * 255;
|
|
167
|
-
} else {
|
|
168
|
-
newSplat[OFFSET_FDC0] = 0;
|
|
169
|
-
newSplat[OFFSET_FDC1] = 0;
|
|
170
|
-
newSplat[OFFSET_FDC2] = 0;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
if (rawSplat[OPACITY] !== undefined) {
|
|
174
|
-
newSplat[OFFSET_OPACITY] = (1 / (1 + Math.exp(-rawSplat[OPACITY]))) * 255;
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
newSplat[OFFSET_FDC0] = clamp(Math.floor(newSplat[OFFSET_FDC0]), 0, 255);
|
|
178
|
-
newSplat[OFFSET_FDC1] = clamp(Math.floor(newSplat[OFFSET_FDC1]), 0, 255);
|
|
179
|
-
newSplat[OFFSET_FDC2] = clamp(Math.floor(newSplat[OFFSET_FDC2]), 0, 255);
|
|
180
|
-
newSplat[OFFSET_OPACITY] = clamp(Math.floor(newSplat[OFFSET_OPACITY]), 0, 255);
|
|
181
|
-
|
|
182
|
-
if (outSphericalHarmonicsDegree >= 1) {
|
|
183
|
-
if (rawSplat[F_REST_0] !== undefined) {
|
|
184
|
-
for (let i = 0; i < 9; i++) {
|
|
185
|
-
newSplat[OFFSET_FRC[i]] = rawSplat[header.sphericalHarmonicsDegree1Fields[i]];
|
|
186
|
-
}
|
|
187
|
-
if (outSphericalHarmonicsDegree >= 2) {
|
|
188
|
-
for (let i = 0; i < 15; i++) {
|
|
189
|
-
newSplat[OFFSET_FRC[9 + i]] = rawSplat[header.sphericalHarmonicsDegree2Fields[i]];
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
tempRotation.set(rawSplat[ROT_0], rawSplat[ROT_1], rawSplat[ROT_2], rawSplat[ROT_3]);
|
|
196
|
-
tempRotation.normalize();
|
|
197
|
-
|
|
198
|
-
newSplat[OFFSET_ROTATION0] = tempRotation.x;
|
|
199
|
-
newSplat[OFFSET_ROTATION1] = tempRotation.y;
|
|
200
|
-
newSplat[OFFSET_ROTATION2] = tempRotation.z;
|
|
201
|
-
newSplat[OFFSET_ROTATION3] = tempRotation.w;
|
|
202
|
-
|
|
203
|
-
newSplat[OFFSET_X] = rawSplat[X];
|
|
204
|
-
newSplat[OFFSET_Y] = rawSplat[Y];
|
|
205
|
-
newSplat[OFFSET_Z] = rawSplat[Z];
|
|
206
|
-
|
|
207
|
-
return newSplat;
|
|
208
|
-
};
|
|
209
|
-
|
|
210
|
-
}();
|
|
211
|
-
|
|
212
|
-
static readSplat(splatData, header, row, dataOffset, rawSplat) {
|
|
213
|
-
return PlyParserUtils.readVertex(splatData, header, row, dataOffset, header.fieldsToReadIndexes, rawSplat, true);
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
parseToUncompressedSplatArray(plyBuffer, outSphericalHarmonicsDegree = 0) {
|
|
217
|
-
const header = this.decodeHeaderFromBuffer(plyBuffer);
|
|
218
|
-
const splatCount = header.splatCount;
|
|
219
|
-
const splatData = this.findSplatData(plyBuffer, header);
|
|
220
|
-
const splatArray = this.decodeSectionSplatData(splatData, splatCount, header, outSphericalHarmonicsDegree);
|
|
221
|
-
return splatArray;
|
|
222
|
-
}
|
|
223
|
-
}
|