@myned-ai/gsplat-flame-avatar-renderer 1.0.1 → 1.0.4
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 +4 -29
- package/dist/gsplat-flame-avatar-renderer.cjs.js +12875 -0
- package/dist/{gsplat-flame-avatar-renderer.umd.js.map → gsplat-flame-avatar-renderer.cjs.js.map} +1 -1
- package/dist/gsplat-flame-avatar-renderer.esm.js +1 -1
- package/package.json +5 -3
- package/src/api/index.js +7 -0
- package/src/buffers/SplatBuffer.js +1394 -0
- package/src/buffers/SplatBufferGenerator.js +41 -0
- package/src/buffers/SplatPartitioner.js +110 -0
- package/src/buffers/UncompressedSplatArray.js +106 -0
- package/src/buffers/index.js +11 -0
- package/src/core/SplatGeometry.js +48 -0
- package/src/core/SplatMesh.js +2620 -0
- package/src/core/SplatScene.js +43 -0
- package/src/core/SplatTree.js +200 -0
- package/src/core/Viewer.js +2895 -0
- package/src/core/index.js +13 -0
- package/src/enums/EngineConstants.js +58 -0
- package/src/enums/LogLevel.js +13 -0
- package/src/enums/RenderMode.js +11 -0
- package/src/enums/SceneFormat.js +21 -0
- package/src/enums/SceneRevealMode.js +11 -0
- package/src/enums/SplatRenderMode.js +10 -0
- package/src/enums/index.js +13 -0
- package/src/flame/FlameAnimator.js +271 -0
- package/src/flame/FlameConstants.js +21 -0
- package/src/flame/FlameTextureManager.js +293 -0
- package/src/flame/index.js +22 -0
- package/src/flame/utils.js +50 -0
- package/src/index.js +39 -0
- package/src/loaders/DirectLoadError.js +14 -0
- package/src/loaders/INRIAV1PlyParser.js +223 -0
- package/src/loaders/PlyLoader.js +261 -0
- package/src/loaders/PlyParser.js +19 -0
- package/src/loaders/PlyParserUtils.js +311 -0
- package/src/loaders/index.js +13 -0
- package/src/materials/SplatMaterial.js +1065 -0
- package/src/materials/SplatMaterial2D.js +358 -0
- package/src/materials/SplatMaterial3D.js +278 -0
- package/src/materials/index.js +11 -0
- package/src/raycaster/Hit.js +37 -0
- package/src/raycaster/Ray.js +123 -0
- package/src/raycaster/Raycaster.js +175 -0
- package/src/raycaster/index.js +10 -0
- package/src/renderer/AnimationManager.js +574 -0
- package/src/renderer/AppConstants.js +101 -0
- package/src/renderer/GaussianSplatRenderer.js +695 -0
- package/src/renderer/index.js +24 -0
- package/src/utils/LoaderUtils.js +65 -0
- package/src/utils/Util.js +375 -0
- package/src/utils/index.js +9 -0
- package/src/worker/SortWorker.js +284 -0
- package/src/worker/index.js +8 -0
- package/dist/gsplat-flame-avatar-renderer.esm.min.js +0 -2
- package/dist/gsplat-flame-avatar-renderer.esm.min.js.map +0 -1
- package/dist/gsplat-flame-avatar-renderer.umd.js +0 -12876
- package/dist/gsplat-flame-avatar-renderer.umd.min.js +0 -2
- package/dist/gsplat-flame-avatar-renderer.umd.min.js.map +0 -1
- package/dist/gsplat-flame-avatar.esm.js +0 -12755
- package/dist/gsplat-flame-avatar.esm.js.map +0 -1
- package/dist/gsplat-flame-avatar.esm.min.js +0 -2
- package/dist/gsplat-flame-avatar.esm.min.js.map +0 -1
- package/dist/gsplat-flame-avatar.umd.js +0 -12876
- package/dist/gsplat-flame-avatar.umd.js.map +0 -1
- package/dist/gsplat-flame-avatar.umd.min.js +0 -2
- package/dist/gsplat-flame-avatar.umd.min.js.map +0 -1
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* gsplat-flame-avatar - Renderer Module
|
|
3
|
+
* Main exports for the renderer classes
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// Constants
|
|
7
|
+
export * from './AppConstants.js';
|
|
8
|
+
|
|
9
|
+
// Animation - includes state classes
|
|
10
|
+
export {
|
|
11
|
+
AnimationManager,
|
|
12
|
+
State,
|
|
13
|
+
Hello,
|
|
14
|
+
Idle,
|
|
15
|
+
Listen,
|
|
16
|
+
Think,
|
|
17
|
+
Speak
|
|
18
|
+
} from './AnimationManager.js';
|
|
19
|
+
|
|
20
|
+
// Main Renderer
|
|
21
|
+
export { GaussianSplatRenderer } from './GaussianSplatRenderer.js';
|
|
22
|
+
|
|
23
|
+
// Default export
|
|
24
|
+
export { GaussianSplatRenderer as default } from './GaussianSplatRenderer.js';
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LoaderUtils
|
|
3
|
+
*
|
|
4
|
+
* Derived from @mkkellogg/gaussian-splats-3d (MIT License)
|
|
5
|
+
* https://github.com/mkkellogg/GaussianSplats3D
|
|
6
|
+
*
|
|
7
|
+
* Loader utilities for decoding text and resolving URLs.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export class LoaderUtils {
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* @deprecated Use TextDecoder instead
|
|
14
|
+
*/
|
|
15
|
+
static decodeText(array) {
|
|
16
|
+
if (typeof TextDecoder !== 'undefined') {
|
|
17
|
+
return new TextDecoder().decode(array);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Fallback for environments without TextDecoder
|
|
21
|
+
let s = '';
|
|
22
|
+
|
|
23
|
+
for (let i = 0, il = array.length; i < il; i++) {
|
|
24
|
+
// Implicitly assumes little-endian.
|
|
25
|
+
s += String.fromCharCode(array[i]);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
try {
|
|
29
|
+
// merges multi-byte utf-8 characters.
|
|
30
|
+
return decodeURIComponent(escape(s));
|
|
31
|
+
} catch (e) { // see #16358
|
|
32
|
+
return s;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
static extractUrlBase(url) {
|
|
37
|
+
const index = url.lastIndexOf('/');
|
|
38
|
+
|
|
39
|
+
if (index === -1) return './';
|
|
40
|
+
|
|
41
|
+
return url.slice(0, index + 1);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
static resolveURL(url, path) {
|
|
45
|
+
// Invalid URL
|
|
46
|
+
if (typeof url !== 'string' || url === '') return '';
|
|
47
|
+
|
|
48
|
+
// Host Relative URL
|
|
49
|
+
if (/^https?:\/\//i.test(path) && /^\//.test(url)) {
|
|
50
|
+
path = path.replace(/(^https?:\/\/[^/]+).*/i, '$1');
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Absolute URL http://,https://,//
|
|
54
|
+
if (/^(https?:)?\/\//i.test(url)) return url;
|
|
55
|
+
|
|
56
|
+
// Data URI
|
|
57
|
+
if (/^data:.*,.*$/i.test(url)) return url;
|
|
58
|
+
|
|
59
|
+
// Blob URL
|
|
60
|
+
if (/^blob:.*$/i.test(url)) return url;
|
|
61
|
+
|
|
62
|
+
// Relative URL
|
|
63
|
+
return path + url;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
@@ -0,0 +1,375 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core utility functions for Gaussian Splat rendering
|
|
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 { DataUtils } from 'three';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Custom error for aborted operations
|
|
14
|
+
*/
|
|
15
|
+
export class AbortedPromiseError extends Error {
|
|
16
|
+
constructor(msg) {
|
|
17
|
+
super(msg);
|
|
18
|
+
this.name = 'AbortedPromiseError';
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Fetch with progress tracking using standard AbortController
|
|
24
|
+
* Returns a Promise with an attached `abort()` method and `abortController`
|
|
25
|
+
*/
|
|
26
|
+
export const fetchWithProgress = function(path, onProgress, saveChunks = true, headers) {
|
|
27
|
+
|
|
28
|
+
const abortController = new AbortController();
|
|
29
|
+
const signal = abortController.signal;
|
|
30
|
+
let aborted = false;
|
|
31
|
+
|
|
32
|
+
let onProgressCalledAtComplete = false;
|
|
33
|
+
const localOnProgress = (percent, percentLabel, chunk, fileSize) => {
|
|
34
|
+
if (onProgress && !onProgressCalledAtComplete) {
|
|
35
|
+
onProgress(percent, percentLabel, chunk, fileSize);
|
|
36
|
+
if (percent === 100) {
|
|
37
|
+
onProgressCalledAtComplete = true;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const promise = new Promise((resolve, reject) => {
|
|
43
|
+
const fetchOptions = { signal };
|
|
44
|
+
if (headers) fetchOptions.headers = headers;
|
|
45
|
+
|
|
46
|
+
fetch(path, fetchOptions)
|
|
47
|
+
.then(async (data) => {
|
|
48
|
+
// Handle error conditions where data is still returned
|
|
49
|
+
if (!data.ok) {
|
|
50
|
+
const errorText = await data.text();
|
|
51
|
+
reject(new Error(`Fetch failed: ${data.status} ${data.statusText} ${errorText}`));
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const reader = data.body.getReader();
|
|
56
|
+
let bytesDownloaded = 0;
|
|
57
|
+
let _fileSize = data.headers.get('Content-Length');
|
|
58
|
+
let fileSize = _fileSize ? parseInt(_fileSize) : undefined;
|
|
59
|
+
|
|
60
|
+
const chunks = [];
|
|
61
|
+
|
|
62
|
+
while (!aborted) {
|
|
63
|
+
try {
|
|
64
|
+
const { value: chunk, done } = await reader.read();
|
|
65
|
+
if (done) {
|
|
66
|
+
localOnProgress(100, '100%', chunk, fileSize);
|
|
67
|
+
if (saveChunks) {
|
|
68
|
+
const buffer = new Blob(chunks).arrayBuffer();
|
|
69
|
+
resolve(buffer);
|
|
70
|
+
} else {
|
|
71
|
+
resolve();
|
|
72
|
+
}
|
|
73
|
+
break;
|
|
74
|
+
}
|
|
75
|
+
bytesDownloaded += chunk.length;
|
|
76
|
+
let percent;
|
|
77
|
+
let percentLabel;
|
|
78
|
+
if (fileSize !== undefined) {
|
|
79
|
+
percent = bytesDownloaded / fileSize * 100;
|
|
80
|
+
percentLabel = `${percent.toFixed(2)}%`;
|
|
81
|
+
}
|
|
82
|
+
if (saveChunks) {
|
|
83
|
+
chunks.push(chunk);
|
|
84
|
+
}
|
|
85
|
+
localOnProgress(percent, percentLabel, chunk, fileSize);
|
|
86
|
+
} catch (error) {
|
|
87
|
+
reject(error);
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
})
|
|
92
|
+
.catch((error) => {
|
|
93
|
+
if (error.name === 'AbortError') {
|
|
94
|
+
reject(new AbortedPromiseError('Fetch aborted'));
|
|
95
|
+
} else {
|
|
96
|
+
reject(new AbortedPromiseError(error.message || error));
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
// Attach abort functionality to the promise
|
|
102
|
+
promise.abort = (reason) => {
|
|
103
|
+
aborted = true;
|
|
104
|
+
abortController.abort(reason);
|
|
105
|
+
};
|
|
106
|
+
promise.abortController = abortController;
|
|
107
|
+
|
|
108
|
+
return promise;
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
// Clamp value between min and max
|
|
112
|
+
export const clamp = function(val, min, max) {
|
|
113
|
+
return Math.max(Math.min(val, max), min);
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
// Get current time in seconds
|
|
117
|
+
export const getCurrentTime = function() {
|
|
118
|
+
return performance.now() / 1000;
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
// Dispose all meshes in a scene graph
|
|
122
|
+
export const disposeAllMeshes = (object3D) => {
|
|
123
|
+
if (object3D.geometry) {
|
|
124
|
+
object3D.geometry.dispose();
|
|
125
|
+
object3D.geometry = null;
|
|
126
|
+
}
|
|
127
|
+
if (object3D.material) {
|
|
128
|
+
object3D.material.dispose();
|
|
129
|
+
object3D.material = null;
|
|
130
|
+
}
|
|
131
|
+
if (object3D.children) {
|
|
132
|
+
for (let child of object3D.children) {
|
|
133
|
+
disposeAllMeshes(child);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
// Delayed execution helper
|
|
139
|
+
export const delayedExecute = (func, fast) => {
|
|
140
|
+
return new Promise((resolve) => {
|
|
141
|
+
window.setTimeout(() => {
|
|
142
|
+
resolve(func());
|
|
143
|
+
}, fast ? 1 : 50);
|
|
144
|
+
});
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
// Get spherical harmonics component count for degree
|
|
148
|
+
export const getSphericalHarmonicsComponentCountForDegree = (sphericalHarmonicsDegree = 0) => {
|
|
149
|
+
switch (sphericalHarmonicsDegree) {
|
|
150
|
+
case 1:
|
|
151
|
+
return 9;
|
|
152
|
+
case 2:
|
|
153
|
+
return 24;
|
|
154
|
+
}
|
|
155
|
+
return 0;
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
// Create native promise with extracted components
|
|
159
|
+
export const nativePromiseWithExtractedComponents = () => {
|
|
160
|
+
let resolver;
|
|
161
|
+
let rejecter;
|
|
162
|
+
const promise = new Promise((resolve, reject) => {
|
|
163
|
+
resolver = resolve;
|
|
164
|
+
rejecter = reject;
|
|
165
|
+
});
|
|
166
|
+
return {
|
|
167
|
+
'promise': promise,
|
|
168
|
+
'resolve': resolver,
|
|
169
|
+
'reject': rejecter
|
|
170
|
+
};
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Create a promise with extracted resolve/reject functions and optional abort capability
|
|
175
|
+
* Uses standard Promise with attached abort method
|
|
176
|
+
*/
|
|
177
|
+
export const abortablePromiseWithExtractedComponents = (abortHandler) => {
|
|
178
|
+
let resolver;
|
|
179
|
+
let rejecter;
|
|
180
|
+
const promise = new Promise((resolve, reject) => {
|
|
181
|
+
resolver = resolve;
|
|
182
|
+
rejecter = reject;
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
// Attach abort method to promise
|
|
186
|
+
promise.abort = abortHandler || (() => {});
|
|
187
|
+
|
|
188
|
+
return {
|
|
189
|
+
'promise': promise,
|
|
190
|
+
'resolve': resolver,
|
|
191
|
+
'reject': rejecter
|
|
192
|
+
};
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
// Semver class for version handling
|
|
196
|
+
export class Semver {
|
|
197
|
+
constructor(major, minor, patch) {
|
|
198
|
+
this.major = major;
|
|
199
|
+
this.minor = minor;
|
|
200
|
+
this.patch = patch;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
toString() {
|
|
204
|
+
return `${this.major}_${this.minor}_${this.patch}`;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// iOS detection
|
|
209
|
+
export function isIOS() {
|
|
210
|
+
const ua = navigator.userAgent;
|
|
211
|
+
return ua.indexOf('iPhone') > 0 || ua.indexOf('iPad') > 0;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
export function getIOSSemever() {
|
|
215
|
+
if (isIOS()) {
|
|
216
|
+
const extract = navigator.userAgent.match(/OS (\d+)_(\d+)_?(\d+)?/);
|
|
217
|
+
return new Semver(
|
|
218
|
+
parseInt(extract[1] || 0, 10),
|
|
219
|
+
parseInt(extract[2] || 0, 10),
|
|
220
|
+
parseInt(extract[3] || 0, 10)
|
|
221
|
+
);
|
|
222
|
+
}
|
|
223
|
+
return null;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// Half float conversion utilities
|
|
227
|
+
export const toHalfFloat = DataUtils.toHalfFloat.bind(DataUtils);
|
|
228
|
+
export const fromHalfFloat = DataUtils.fromHalfFloat.bind(DataUtils);
|
|
229
|
+
|
|
230
|
+
// Default spherical harmonics compression range (imported from enums)
|
|
231
|
+
import { DefaultSphericalHarmonics8BitCompressionRange } from '../enums/EngineConstants.js';
|
|
232
|
+
export { DefaultSphericalHarmonics8BitCompressionRange };
|
|
233
|
+
export const DefaultSphericalHarmonics8BitCompressionHalfRange = DefaultSphericalHarmonics8BitCompressionRange / 2.0;
|
|
234
|
+
|
|
235
|
+
// Uncompress float based on compression level
|
|
236
|
+
export const toUncompressedFloat = (f, compressionLevel, isSH = false, range8BitMin, range8BitMax) => {
|
|
237
|
+
if (compressionLevel === 0) {
|
|
238
|
+
return f;
|
|
239
|
+
} else if (compressionLevel === 1 || (compressionLevel === 2 && !isSH)) {
|
|
240
|
+
return DataUtils.fromHalfFloat(f);
|
|
241
|
+
} else if (compressionLevel === 2) {
|
|
242
|
+
return fromUint8(f, range8BitMin, range8BitMax);
|
|
243
|
+
}
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
// Convert to uint8
|
|
247
|
+
export const toUint8 = (v, rangeMin, rangeMax) => {
|
|
248
|
+
v = clamp(v, rangeMin, rangeMax);
|
|
249
|
+
const range = (rangeMax - rangeMin);
|
|
250
|
+
return clamp(Math.floor((v - rangeMin) / range * 255), 0, 255);
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
// Convert from uint8
|
|
254
|
+
export const fromUint8 = (v, rangeMin, rangeMax) => {
|
|
255
|
+
const range = (rangeMax - rangeMin);
|
|
256
|
+
return (v / 255 * range + rangeMin);
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
// Half float to uint8
|
|
260
|
+
export const fromHalfFloatToUint8 = (v, rangeMin, rangeMax) => {
|
|
261
|
+
return toUint8(fromHalfFloat(v), rangeMin, rangeMax);
|
|
262
|
+
};
|
|
263
|
+
|
|
264
|
+
// Uint8 to half float
|
|
265
|
+
export const fromUint8ToHalfFloat = (v, rangeMin, rangeMax) => {
|
|
266
|
+
return toHalfFloat(fromUint8(v, rangeMin, rangeMax));
|
|
267
|
+
};
|
|
268
|
+
|
|
269
|
+
// Read float from DataView based on compression level
|
|
270
|
+
export const dataViewFloatForCompressionLevel = (dataView, floatIndex, compressionLevel, isSH = false) => {
|
|
271
|
+
if (compressionLevel === 0) {
|
|
272
|
+
return dataView.getFloat32(floatIndex * 4, true);
|
|
273
|
+
} else if (compressionLevel === 1 || (compressionLevel === 2 && !isSH)) {
|
|
274
|
+
return dataView.getUint16(floatIndex * 2, true);
|
|
275
|
+
} else {
|
|
276
|
+
return dataView.getUint8(floatIndex, true);
|
|
277
|
+
}
|
|
278
|
+
};
|
|
279
|
+
|
|
280
|
+
// Convert between compression levels
|
|
281
|
+
export const convertBetweenCompressionLevels = function() {
|
|
282
|
+
const noop = (v) => v;
|
|
283
|
+
|
|
284
|
+
return function(val, fromLevel, toLevel, isSH = false) {
|
|
285
|
+
if (fromLevel === toLevel) return val;
|
|
286
|
+
let outputConversionFunc = noop;
|
|
287
|
+
|
|
288
|
+
if (fromLevel === 2 && isSH) {
|
|
289
|
+
if (toLevel === 1) outputConversionFunc = fromUint8ToHalfFloat;
|
|
290
|
+
else if (toLevel === 0) {
|
|
291
|
+
outputConversionFunc = fromUint8;
|
|
292
|
+
}
|
|
293
|
+
} else if (fromLevel === 2 || fromLevel === 1) {
|
|
294
|
+
if (toLevel === 0) outputConversionFunc = fromHalfFloat;
|
|
295
|
+
else if (toLevel === 2) {
|
|
296
|
+
if (!isSH) outputConversionFunc = noop;
|
|
297
|
+
else outputConversionFunc = fromHalfFloatToUint8;
|
|
298
|
+
}
|
|
299
|
+
} else if (fromLevel === 0) {
|
|
300
|
+
if (toLevel === 1) outputConversionFunc = toHalfFloat;
|
|
301
|
+
else if (toLevel === 2) {
|
|
302
|
+
if (!isSH) outputConversionFunc = toHalfFloat;
|
|
303
|
+
else outputConversionFunc = toUint8;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
return outputConversionFunc(val);
|
|
308
|
+
};
|
|
309
|
+
}();
|
|
310
|
+
|
|
311
|
+
// Copy between buffers
|
|
312
|
+
export const copyBetweenBuffers = (srcBuffer, srcOffset, destBuffer, destOffset, byteCount = 0) => {
|
|
313
|
+
const src = new Uint8Array(srcBuffer, srcOffset);
|
|
314
|
+
const dest = new Uint8Array(destBuffer, destOffset);
|
|
315
|
+
for (let i = 0; i < byteCount; i++) {
|
|
316
|
+
dest[i] = src[i];
|
|
317
|
+
}
|
|
318
|
+
};
|
|
319
|
+
|
|
320
|
+
// Float to half float conversion
|
|
321
|
+
export const floatToHalf = function() {
|
|
322
|
+
const floatView = new Float32Array(1);
|
|
323
|
+
const int32View = new Int32Array(floatView.buffer);
|
|
324
|
+
|
|
325
|
+
return function(val) {
|
|
326
|
+
floatView[0] = val;
|
|
327
|
+
const x = int32View[0];
|
|
328
|
+
|
|
329
|
+
let bits = (x >> 16) & 0x8000;
|
|
330
|
+
let m = (x >> 12) & 0x07ff;
|
|
331
|
+
const e = (x >> 23) & 0xff;
|
|
332
|
+
|
|
333
|
+
if (e < 103) return bits;
|
|
334
|
+
|
|
335
|
+
if (e > 142) {
|
|
336
|
+
bits |= 0x7c00;
|
|
337
|
+
bits |= ((e == 255) ? 0 : 1) && (x & 0x007fffff);
|
|
338
|
+
return bits;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
if (e < 113) {
|
|
342
|
+
m |= 0x0800;
|
|
343
|
+
bits |= (m >> (114 - e)) + ((m >> (113 - e)) & 1);
|
|
344
|
+
return bits;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
bits |= ((e - 112) << 10) | (m >> 1);
|
|
348
|
+
bits += m & 1;
|
|
349
|
+
return bits;
|
|
350
|
+
};
|
|
351
|
+
}();
|
|
352
|
+
|
|
353
|
+
// Encode float as uint
|
|
354
|
+
export const uintEncodedFloat = function() {
|
|
355
|
+
const floatView = new Float32Array(1);
|
|
356
|
+
const int32View = new Int32Array(floatView.buffer);
|
|
357
|
+
|
|
358
|
+
return function(f) {
|
|
359
|
+
floatView[0] = f;
|
|
360
|
+
return int32View[0];
|
|
361
|
+
};
|
|
362
|
+
}();
|
|
363
|
+
|
|
364
|
+
// RGBA to integer
|
|
365
|
+
export const rgbaToInteger = function(r, g, b, a) {
|
|
366
|
+
return r + (g << 8) + (b << 16) + (a << 24);
|
|
367
|
+
};
|
|
368
|
+
|
|
369
|
+
// RGBA array to integer
|
|
370
|
+
export const rgbaArrayToInteger = function(arr, offset) {
|
|
371
|
+
return arr[offset] + (arr[offset + 1] << 8) + (arr[offset + 2] << 16) + (arr[offset + 3] << 24);
|
|
372
|
+
};
|
|
373
|
+
|
|
374
|
+
// BASE_COMPONENT_COUNT for UncompressedSplatArray
|
|
375
|
+
export const BASE_COMPONENT_COUNT = 14;
|