@playcanvas/splat-transform 0.5.2 → 0.5.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/LICENSE +19 -19
- package/README.md +127 -127
- package/bin/cli.mjs +5 -5
- package/dist/index.mjs +665 -377
- package/dist/index.mjs.map +1 -1
- package/package.json +54 -57
package/dist/index.mjs
CHANGED
|
@@ -1884,7 +1884,7 @@ let Quat$1 = class Quat {
|
|
|
1884
1884
|
}
|
|
1885
1885
|
};
|
|
1886
1886
|
|
|
1887
|
-
var version$1 = "0.5.
|
|
1887
|
+
var version$1 = "0.5.4";
|
|
1888
1888
|
|
|
1889
1889
|
class Column {
|
|
1890
1890
|
name;
|
|
@@ -3368,11 +3368,11 @@ const writePly = async (fileHandle, plyData) => {
|
|
|
3368
3368
|
|
|
3369
3369
|
/**
|
|
3370
3370
|
* The engine version number. This is in semantic versioning format (MAJOR.MINOR.PATCH).
|
|
3371
|
-
*/ const version = '2.
|
|
3371
|
+
*/ const version = '2.10.3';
|
|
3372
3372
|
/**
|
|
3373
3373
|
* The engine revision number. This is the Git hash of the last commit made to the branch
|
|
3374
3374
|
* from which the engine was built.
|
|
3375
|
-
*/ const revision = '
|
|
3375
|
+
*/ const revision = '2dc84cf';
|
|
3376
3376
|
/**
|
|
3377
3377
|
* Merge the contents of two objects into a single object.
|
|
3378
3378
|
*
|
|
@@ -13136,6 +13136,60 @@ const typedArrayIndexFormatsByteSize = [
|
|
|
13136
13136
|
2,
|
|
13137
13137
|
4
|
|
13138
13138
|
];
|
|
13139
|
+
// map of primitive GLSL types to their corresponding WGSL types
|
|
13140
|
+
const primitiveGlslToWgslTypeMap = new Map([
|
|
13141
|
+
// floating-point
|
|
13142
|
+
[
|
|
13143
|
+
'float',
|
|
13144
|
+
'f32'
|
|
13145
|
+
],
|
|
13146
|
+
[
|
|
13147
|
+
'vec2',
|
|
13148
|
+
'vec2f'
|
|
13149
|
+
],
|
|
13150
|
+
[
|
|
13151
|
+
'vec3',
|
|
13152
|
+
'vec3f'
|
|
13153
|
+
],
|
|
13154
|
+
[
|
|
13155
|
+
'vec4',
|
|
13156
|
+
'vec4f'
|
|
13157
|
+
],
|
|
13158
|
+
// signed integer
|
|
13159
|
+
[
|
|
13160
|
+
'int',
|
|
13161
|
+
'i32'
|
|
13162
|
+
],
|
|
13163
|
+
[
|
|
13164
|
+
'ivec2',
|
|
13165
|
+
'vec2i'
|
|
13166
|
+
],
|
|
13167
|
+
[
|
|
13168
|
+
'ivec3',
|
|
13169
|
+
'vec3i'
|
|
13170
|
+
],
|
|
13171
|
+
[
|
|
13172
|
+
'ivec4',
|
|
13173
|
+
'vec4i'
|
|
13174
|
+
],
|
|
13175
|
+
// unsigned integer
|
|
13176
|
+
[
|
|
13177
|
+
'uint',
|
|
13178
|
+
'u32'
|
|
13179
|
+
],
|
|
13180
|
+
[
|
|
13181
|
+
'uvec2',
|
|
13182
|
+
'vec2u'
|
|
13183
|
+
],
|
|
13184
|
+
[
|
|
13185
|
+
'uvec3',
|
|
13186
|
+
'vec3u'
|
|
13187
|
+
],
|
|
13188
|
+
[
|
|
13189
|
+
'uvec4',
|
|
13190
|
+
'vec4u'
|
|
13191
|
+
]
|
|
13192
|
+
]);
|
|
13139
13193
|
/**
|
|
13140
13194
|
* Map of engine semantics into location on device in range 0..15 (note - semantics mapping to the
|
|
13141
13195
|
* same location cannot be used at the same time) organized in a way that ATTR0-ATTR7 do not
|
|
@@ -15847,12 +15901,12 @@ const deviceCache$2 = new DeviceCache();
|
|
|
15847
15901
|
return deviceCache$2.get(graphicsDevice, ()=>{
|
|
15848
15902
|
return new VertexFormat(graphicsDevice, [
|
|
15849
15903
|
{
|
|
15850
|
-
semantic:
|
|
15904
|
+
semantic: SEMANTIC_ATTR11,
|
|
15851
15905
|
components: 4,
|
|
15852
15906
|
type: TYPE_FLOAT32
|
|
15853
15907
|
},
|
|
15854
15908
|
{
|
|
15855
|
-
semantic:
|
|
15909
|
+
semantic: SEMANTIC_ATTR12,
|
|
15856
15910
|
components: 4,
|
|
15857
15911
|
type: TYPE_FLOAT32
|
|
15858
15912
|
},
|
|
@@ -17831,8 +17885,8 @@ const stringIds$1 = new StringIds();
|
|
|
17831
17885
|
Debug.trace(TRACEID_RENDER_QUEUE, `writeBuffer: ${this.buffer.label}`);
|
|
17832
17886
|
wgpu.queue.writeBuffer(this.buffer, 0, data, 0, data.length);
|
|
17833
17887
|
}
|
|
17834
|
-
read(device, offset, size, data) {
|
|
17835
|
-
return device.readStorageBuffer(this, offset, size, data);
|
|
17888
|
+
read(device, offset, size, data, immediate) {
|
|
17889
|
+
return device.readStorageBuffer(this, offset, size, data, immediate);
|
|
17836
17890
|
}
|
|
17837
17891
|
write(device, bufferOffset, data, dataOffset, size) {
|
|
17838
17892
|
device.writeStorageBuffer(this, bufferOffset, data, dataOffset, size);
|
|
@@ -20132,7 +20186,7 @@ class ResourceLine {
|
|
|
20132
20186
|
static generateFragmentOutputStruct(src, numRenderTargets) {
|
|
20133
20187
|
let structCode = 'struct FragmentOutput {\n';
|
|
20134
20188
|
for(let i = 0; i < numRenderTargets; i++){
|
|
20135
|
-
structCode += ` @location(${i}) color${i > 0 ? i : ''} :
|
|
20189
|
+
structCode += ` @location(${i}) color${i > 0 ? i : ''} : pcOutType${i},\n`;
|
|
20136
20190
|
}
|
|
20137
20191
|
// find if the src contains `.fragDepth =`, ignoring whitespace before = sign
|
|
20138
20192
|
const needsFragDepth = src.search(/\.fragDepth\s*=/) !== -1;
|
|
@@ -20896,6 +20950,10 @@ const ENDIF = /(endif|else|elif)(?:[ \t]+([^\r\n]*))?\r?\n?/g;
|
|
|
20896
20950
|
const IDENTIFIER$1 = /\{?[\w-]+\}?/;
|
|
20897
20951
|
// [!]defined(EXPRESSION)
|
|
20898
20952
|
const DEFINED = /(!|\s)?defined\(([\w-]+)\)/;
|
|
20953
|
+
// Matches all defined(...) patterns for parentheses check
|
|
20954
|
+
const DEFINED_PARENS = /!?defined\s*\([^)]*\)/g;
|
|
20955
|
+
// Matches defined or !defined at the end of a string (for parentheses detection)
|
|
20956
|
+
const DEFINED_BEFORE_PAREN = /!?defined\s*$/;
|
|
20899
20957
|
// Matches comparison operators like ==, !=, <, <=, >, >=
|
|
20900
20958
|
const COMPARISON = /([a-z_]\w*)\s*(==|!=|<|<=|>|>=)\s*([\w"']+)/i;
|
|
20901
20959
|
// currently unsupported characters in the expression: + -
|
|
@@ -21295,6 +21353,19 @@ const FRAGCOLOR = /(pcFragColor[1-8])\b/g;
|
|
|
21295
21353
|
let error = false;
|
|
21296
21354
|
expr = expr.trim();
|
|
21297
21355
|
let invert = false;
|
|
21356
|
+
// Handle boolean literals
|
|
21357
|
+
if (expr === 'true') {
|
|
21358
|
+
return {
|
|
21359
|
+
result: true,
|
|
21360
|
+
error
|
|
21361
|
+
};
|
|
21362
|
+
}
|
|
21363
|
+
if (expr === 'false') {
|
|
21364
|
+
return {
|
|
21365
|
+
result: false,
|
|
21366
|
+
error
|
|
21367
|
+
};
|
|
21368
|
+
}
|
|
21298
21369
|
// Handle defined(expr) and !defined(expr)
|
|
21299
21370
|
const definedMatch = DEFINED.exec(expr);
|
|
21300
21371
|
if (definedMatch) {
|
|
@@ -21347,9 +21418,88 @@ const FRAGCOLOR = /(pcFragColor[1-8])\b/g;
|
|
|
21347
21418
|
error
|
|
21348
21419
|
};
|
|
21349
21420
|
}
|
|
21421
|
+
/**
|
|
21422
|
+
* Processes parentheses in an expression by recursively evaluating subexpressions.
|
|
21423
|
+
* Ignores parentheses that are part of defined() calls.
|
|
21424
|
+
*
|
|
21425
|
+
* @param {string} expression - The expression to process.
|
|
21426
|
+
* @param {Map<string, string>} defines - A map containing key-value pairs of defines.
|
|
21427
|
+
* @returns {object} Returns an object containing the processed expression and an error flag.
|
|
21428
|
+
*/ static processParentheses(expression, defines) {
|
|
21429
|
+
let error = false;
|
|
21430
|
+
let processed = expression.trim();
|
|
21431
|
+
// Remove outer parentheses that wrap the entire expression
|
|
21432
|
+
while(processed.startsWith('(') && processed.endsWith(')')){
|
|
21433
|
+
let depth = 0;
|
|
21434
|
+
let wrapsEntire = true;
|
|
21435
|
+
for(let i = 0; i < processed.length - 1; i++){
|
|
21436
|
+
if (processed[i] === '(') depth++;
|
|
21437
|
+
else if (processed[i] === ')') {
|
|
21438
|
+
depth--;
|
|
21439
|
+
if (depth === 0) {
|
|
21440
|
+
wrapsEntire = false;
|
|
21441
|
+
break;
|
|
21442
|
+
}
|
|
21443
|
+
}
|
|
21444
|
+
}
|
|
21445
|
+
if (wrapsEntire) {
|
|
21446
|
+
processed = processed.slice(1, -1).trim();
|
|
21447
|
+
} else {
|
|
21448
|
+
break;
|
|
21449
|
+
}
|
|
21450
|
+
}
|
|
21451
|
+
// Keep processing until no more precedence parentheses exist
|
|
21452
|
+
while(true){
|
|
21453
|
+
let foundParen = false;
|
|
21454
|
+
let depth = 0;
|
|
21455
|
+
let maxDepth = 0;
|
|
21456
|
+
let deepestStart = -1;
|
|
21457
|
+
let deepestEnd = -1;
|
|
21458
|
+
// Find the deepest nested parentheses that aren't part of defined()
|
|
21459
|
+
let inDefinedParen = 0;
|
|
21460
|
+
for(let i = 0; i < processed.length; i++){
|
|
21461
|
+
if (processed[i] === '(') {
|
|
21462
|
+
// Check if this is part of defined() - look back for "defined" or "!defined"
|
|
21463
|
+
const beforeParen = processed.substring(0, i);
|
|
21464
|
+
if (DEFINED_BEFORE_PAREN.test(beforeParen)) {
|
|
21465
|
+
inDefinedParen++;
|
|
21466
|
+
} else if (inDefinedParen === 0) {
|
|
21467
|
+
depth++;
|
|
21468
|
+
if (depth > maxDepth) {
|
|
21469
|
+
maxDepth = depth;
|
|
21470
|
+
deepestStart = i;
|
|
21471
|
+
}
|
|
21472
|
+
foundParen = true;
|
|
21473
|
+
}
|
|
21474
|
+
} else if (processed[i] === ')') {
|
|
21475
|
+
if (inDefinedParen > 0) {
|
|
21476
|
+
inDefinedParen--;
|
|
21477
|
+
} else if (depth > 0) {
|
|
21478
|
+
if (depth === maxDepth && deepestStart !== -1) {
|
|
21479
|
+
deepestEnd = i;
|
|
21480
|
+
}
|
|
21481
|
+
depth--;
|
|
21482
|
+
}
|
|
21483
|
+
}
|
|
21484
|
+
}
|
|
21485
|
+
if (!foundParen || deepestStart === -1 || deepestEnd === -1) {
|
|
21486
|
+
break;
|
|
21487
|
+
}
|
|
21488
|
+
// Extract and evaluate the subexpression
|
|
21489
|
+
const subExpr = processed.substring(deepestStart + 1, deepestEnd);
|
|
21490
|
+
const { result, error: subError } = Preprocessor.evaluate(subExpr, defines);
|
|
21491
|
+
error = error || subError;
|
|
21492
|
+
// Replace the parentheses expression with its result
|
|
21493
|
+
processed = processed.substring(0, deepestStart) + (result ? 'true' : 'false') + processed.substring(deepestEnd + 1);
|
|
21494
|
+
}
|
|
21495
|
+
return {
|
|
21496
|
+
expression: processed,
|
|
21497
|
+
error
|
|
21498
|
+
};
|
|
21499
|
+
}
|
|
21350
21500
|
/**
|
|
21351
21501
|
* Evaluates a complex expression with support for `defined`, `!defined`, comparisons, `&&`,
|
|
21352
|
-
* and
|
|
21502
|
+
* `||`, and parentheses for precedence.
|
|
21353
21503
|
*
|
|
21354
21504
|
* @param {string} expression - The expression to evaluate.
|
|
21355
21505
|
* @param {Map<string, string>} defines - A map containing key-value pairs of defines.
|
|
@@ -21357,8 +21507,26 @@ const FRAGCOLOR = /(pcFragColor[1-8])\b/g;
|
|
|
21357
21507
|
*/ static evaluate(expression, defines) {
|
|
21358
21508
|
const correct = INVALID.exec(expression) === null;
|
|
21359
21509
|
Debug.assert(correct, `Resolving expression like this is not supported: ${expression}`);
|
|
21510
|
+
// Process parentheses first (skip if no parentheses exist or only defined() parentheses)
|
|
21511
|
+
let processedExpr = expression;
|
|
21512
|
+
let parenError = false;
|
|
21513
|
+
// Quick check: remove all defined(...) patterns and see if any parentheses remain
|
|
21514
|
+
// If they do, process them recursively to handle nested parentheses
|
|
21515
|
+
const withoutDefined = expression.replace(DEFINED_PARENS, '');
|
|
21516
|
+
if (withoutDefined.indexOf('(') !== -1) {
|
|
21517
|
+
const processed = Preprocessor.processParentheses(expression, defines);
|
|
21518
|
+
processedExpr = processed.expression;
|
|
21519
|
+
parenError = processed.error;
|
|
21520
|
+
}
|
|
21521
|
+
if (parenError) {
|
|
21522
|
+
Debug.log(`Parenthesis parsing error in expression: "${expression}"`);
|
|
21523
|
+
return {
|
|
21524
|
+
result: false,
|
|
21525
|
+
error: true
|
|
21526
|
+
};
|
|
21527
|
+
}
|
|
21360
21528
|
// Step 1: Split by "||" to handle OR conditions
|
|
21361
|
-
const orSegments =
|
|
21529
|
+
const orSegments = processedExpr.split('||');
|
|
21362
21530
|
for (const orSegment of orSegments){
|
|
21363
21531
|
// Step 2: Split each OR segment by "&&" to handle AND conditions
|
|
21364
21532
|
const andSegments = orSegment.split('&&');
|
|
@@ -21693,19 +21861,23 @@ struct WrappedVec2U { @size(16) element: vec2u }
|
|
|
21693
21861
|
Debug.assert(!options.vertexIncludes || options.vertexIncludes instanceof Map);
|
|
21694
21862
|
Debug.assert(!options.fragmentDefines || options.fragmentDefines instanceof Map);
|
|
21695
21863
|
Debug.assert(!options.fragmentIncludes || options.fragmentIncludes instanceof Map);
|
|
21864
|
+
// Normalize fragmentOutputTypes to an array
|
|
21865
|
+
const normalizedOutputTypes = (options)=>{
|
|
21866
|
+
let fragmentOutputTypes = options.fragmentOutputTypes ?? 'vec4';
|
|
21867
|
+
if (!Array.isArray(fragmentOutputTypes)) {
|
|
21868
|
+
fragmentOutputTypes = [
|
|
21869
|
+
fragmentOutputTypes
|
|
21870
|
+
];
|
|
21871
|
+
}
|
|
21872
|
+
return fragmentOutputTypes;
|
|
21873
|
+
};
|
|
21696
21874
|
const getDefines = (gpu, gl2, isVertex, options)=>{
|
|
21697
21875
|
const deviceIntro = device.isWebGPU ? gpu : gl2;
|
|
21698
21876
|
// a define per supported color attachment, which strips out unsupported output definitions in the deviceIntro
|
|
21699
21877
|
let attachmentsDefine = '';
|
|
21700
21878
|
// Define the fragment shader output type, vec4 by default
|
|
21701
21879
|
if (!isVertex) {
|
|
21702
|
-
|
|
21703
|
-
let fragmentOutputTypes = options.fragmentOutputTypes ?? 'vec4';
|
|
21704
|
-
if (!Array.isArray(fragmentOutputTypes)) {
|
|
21705
|
-
fragmentOutputTypes = [
|
|
21706
|
-
fragmentOutputTypes
|
|
21707
|
-
];
|
|
21708
|
-
}
|
|
21880
|
+
const fragmentOutputTypes = normalizedOutputTypes(options);
|
|
21709
21881
|
for(let i = 0; i < device.maxColorAttachments; i++){
|
|
21710
21882
|
attachmentsDefine += `#define COLOR_ATTACHMENT_${i}\n`;
|
|
21711
21883
|
const outType = fragmentOutputTypes[i] ?? 'vec4';
|
|
@@ -21714,6 +21886,21 @@ struct WrappedVec2U { @size(16) element: vec2u }
|
|
|
21714
21886
|
}
|
|
21715
21887
|
return attachmentsDefine + deviceIntro;
|
|
21716
21888
|
};
|
|
21889
|
+
const getDefinesWgsl = (isVertex, options)=>{
|
|
21890
|
+
let attachmentsDefine = '';
|
|
21891
|
+
// Define the fragment shader output type, vec4 by default
|
|
21892
|
+
if (!isVertex) {
|
|
21893
|
+
const fragmentOutputTypes = normalizedOutputTypes(options);
|
|
21894
|
+
// create alias for each output type
|
|
21895
|
+
for(let i = 0; i < device.maxColorAttachments; i++){
|
|
21896
|
+
const glslOutType = fragmentOutputTypes[i] ?? 'vec4';
|
|
21897
|
+
const wgslOutType = primitiveGlslToWgslTypeMap.get(glslOutType);
|
|
21898
|
+
Debug.assert(wgslOutType, `Unknown output type translation: ${glslOutType} -> ${wgslOutType}`);
|
|
21899
|
+
attachmentsDefine += `alias pcOutType${i} = ${wgslOutType};\n`;
|
|
21900
|
+
}
|
|
21901
|
+
}
|
|
21902
|
+
return attachmentsDefine;
|
|
21903
|
+
};
|
|
21717
21904
|
const name = options.name ?? 'Untitled';
|
|
21718
21905
|
let vertCode;
|
|
21719
21906
|
let fragCode;
|
|
@@ -21722,12 +21909,14 @@ struct WrappedVec2U { @size(16) element: vec2u }
|
|
|
21722
21909
|
const wgsl = options.shaderLanguage === SHADERLANGUAGE_WGSL;
|
|
21723
21910
|
if (wgsl) {
|
|
21724
21911
|
vertCode = `
|
|
21912
|
+
${getDefinesWgsl(true, options)}
|
|
21725
21913
|
${webgpuVS}
|
|
21726
21914
|
${sharedWGSL}
|
|
21727
21915
|
${vertexDefinesCode}
|
|
21728
21916
|
${options.vertexCode}
|
|
21729
21917
|
`;
|
|
21730
21918
|
fragCode = `
|
|
21919
|
+
${getDefinesWgsl(false, options)}
|
|
21731
21920
|
${webgpuPS}
|
|
21732
21921
|
${sharedWGSL}
|
|
21733
21922
|
${fragmentDefinesCode}
|
|
@@ -23339,11 +23528,14 @@ class WebgpuGpuProfiler extends GpuProfiler {
|
|
|
23339
23528
|
* storage buffer. When typed array is supplied, enough space needs to be reserved, otherwise
|
|
23340
23529
|
* only partial data is copied. If not specified, the data is returned in an Uint8Array.
|
|
23341
23530
|
* Defaults to null.
|
|
23531
|
+
* @param {boolean} [immediate] - If true, the read operation will be executed as soon as
|
|
23532
|
+
* possible. This has a performance impact, so it should be used only when necessary. Defaults
|
|
23533
|
+
* to false.
|
|
23342
23534
|
* @returns {Promise<ArrayBufferView>} A promise that resolves with the data read from the
|
|
23343
23535
|
* storage buffer.
|
|
23344
23536
|
* @ignore
|
|
23345
|
-
*/ read(offset = 0, size = this.byteSize, data = null) {
|
|
23346
|
-
return this.impl.read(this.device, offset, size, data);
|
|
23537
|
+
*/ read(offset = 0, size = this.byteSize, data = null, immediate = false) {
|
|
23538
|
+
return this.impl.read(this.device, offset, size, data, immediate);
|
|
23347
23539
|
}
|
|
23348
23540
|
/**
|
|
23349
23541
|
* Issues a write operation of the provided data into a storage buffer.
|
|
@@ -25257,6 +25449,8 @@ function arrayGet4(offset, outputArray, outputIndex) {
|
|
|
25257
25449
|
}
|
|
25258
25450
|
|
|
25259
25451
|
/**
|
|
25452
|
+
* @import { EventHandler } from '../../core/event-handler.js';
|
|
25453
|
+
*/ /**
|
|
25260
25454
|
* @callback HttpResponseCallback
|
|
25261
25455
|
* Callback used by {@link Http#get}, {@link Http#post}, {@link Http#put}, {@link Http#del}, and
|
|
25262
25456
|
* {@link Http#request}.
|
|
@@ -25334,6 +25528,7 @@ function arrayGet4(offset, outputArray, outputIndex) {
|
|
|
25334
25528
|
* @param {boolean} [options.retry] - If true then if the request fails it will be retried with an exponential backoff.
|
|
25335
25529
|
* @param {number} [options.maxRetries] - If options.retry is true this specifies the maximum number of retries. Defaults to 5.
|
|
25336
25530
|
* @param {number} [options.maxRetryDelay] - If options.retry is true this specifies the maximum amount of time to wait between retries in milliseconds. Defaults to 5000.
|
|
25531
|
+
* @param {EventHandler} [options.progress] - Object to use for firing progress events.
|
|
25337
25532
|
* @param {HttpResponseCallback} callback - The callback used when the response has returned. Passed (err, data)
|
|
25338
25533
|
* where data is the response (format depends on response type: text, Object, ArrayBuffer, XML) and
|
|
25339
25534
|
* err is the error code.
|
|
@@ -25350,7 +25545,25 @@ function arrayGet4(offset, outputArray, outputIndex) {
|
|
|
25350
25545
|
callback = options;
|
|
25351
25546
|
options = {};
|
|
25352
25547
|
}
|
|
25353
|
-
|
|
25548
|
+
const result = this.request('GET', url, options, callback);
|
|
25549
|
+
const { progress } = options;
|
|
25550
|
+
if (progress) {
|
|
25551
|
+
const handler = (event)=>{
|
|
25552
|
+
if (event.lengthComputable) {
|
|
25553
|
+
progress.fire('progress', event.loaded, event.total);
|
|
25554
|
+
}
|
|
25555
|
+
};
|
|
25556
|
+
const endHandler = (event)=>{
|
|
25557
|
+
handler(event);
|
|
25558
|
+
result.removeEventListener('loadstart', handler);
|
|
25559
|
+
result.removeEventListener('progress', handler);
|
|
25560
|
+
result.removeEventListener('loadend', endHandler);
|
|
25561
|
+
};
|
|
25562
|
+
result.addEventListener('loadstart', handler);
|
|
25563
|
+
result.addEventListener('progress', handler);
|
|
25564
|
+
result.addEventListener('loadend', endHandler);
|
|
25565
|
+
}
|
|
25566
|
+
return result;
|
|
25354
25567
|
}
|
|
25355
25568
|
/**
|
|
25356
25569
|
* Perform an HTTP POST request to the given url with additional options such as headers,
|
|
@@ -26079,6 +26292,11 @@ const lightFalloffNames = {
|
|
|
26079
26292
|
}
|
|
26080
26293
|
]
|
|
26081
26294
|
]);
|
|
26295
|
+
/**
|
|
26296
|
+
* The flag that controls shadow rendering for the all cascades
|
|
26297
|
+
*
|
|
26298
|
+
* @category Graphics
|
|
26299
|
+
*/ const SHADOW_CASCADE_ALL = 255;
|
|
26082
26300
|
/**
|
|
26083
26301
|
* Gaussian filter. May look smoother than box, but requires more samples.
|
|
26084
26302
|
*
|
|
@@ -28264,13 +28482,14 @@ class GeometryVertexStream {
|
|
|
28264
28482
|
];
|
|
28265
28483
|
const base = this.primitive[RENDERSTYLE_SOLID].base;
|
|
28266
28484
|
const count = this.primitive[RENDERSTYLE_SOLID].count;
|
|
28485
|
+
const baseVertex = this.primitive[RENDERSTYLE_SOLID].baseVertex || 0;
|
|
28267
28486
|
const indexBuffer = this.indexBuffer[RENDERSTYLE_SOLID];
|
|
28268
28487
|
const srcIndices = new typedArrayIndexFormats[indexBuffer.format](indexBuffer.storage);
|
|
28269
28488
|
const seen = new Set();
|
|
28270
28489
|
for(let j = base; j < base + count; j += 3){
|
|
28271
28490
|
for(let k = 0; k < 3; k++){
|
|
28272
|
-
const i1 = srcIndices[j + offsets[k][0]];
|
|
28273
|
-
const i2 = srcIndices[j + offsets[k][1]];
|
|
28491
|
+
const i1 = srcIndices[j + offsets[k][0]] + baseVertex;
|
|
28492
|
+
const i2 = srcIndices[j + offsets[k][1]] + baseVertex;
|
|
28274
28493
|
const hash = i1 > i2 ? i2 * numVertices + i1 : i1 * numVertices + i2;
|
|
28275
28494
|
if (!seen.has(hash)) {
|
|
28276
28495
|
seen.add(hash);
|
|
@@ -30099,6 +30318,14 @@ const lookupHashes = new Uint32Array(4);
|
|
|
30099
30318
|
* @type {boolean}
|
|
30100
30319
|
*/ this.castShadow = false;
|
|
30101
30320
|
/**
|
|
30321
|
+
* Specifies a bitmask that controls which shadow cascades a mesh instance contributes
|
|
30322
|
+
* to when rendered with a {@link LIGHTTYPE_DIRECTIONAL} light source.
|
|
30323
|
+
* This setting is only effective if the {@link castShadow} property is enabled.
|
|
30324
|
+
* Defaults to {@link SHADOW_CASCADE_ALL}, which means the mesh casts shadows into all available cascades.
|
|
30325
|
+
*
|
|
30326
|
+
* @type {number}
|
|
30327
|
+
*/ this.shadowCascadeMask = SHADOW_CASCADE_ALL;
|
|
30328
|
+
/**
|
|
30102
30329
|
* Controls whether the mesh instance can be culled by frustum culling (see
|
|
30103
30330
|
* {@link CameraComponent#frustumCulling}). Defaults to true.
|
|
30104
30331
|
*
|
|
@@ -33325,24 +33552,6 @@ const semverLess = (a, b)=>{
|
|
|
33325
33552
|
vertex_boneWeights: SEMANTIC_BLENDWEIGHT,
|
|
33326
33553
|
vertex_boneIndices: SEMANTIC_BLENDINDICES
|
|
33327
33554
|
};
|
|
33328
|
-
const varyingsWGSLTypes = new Map([
|
|
33329
|
-
[
|
|
33330
|
-
'vec4',
|
|
33331
|
-
'vec4f'
|
|
33332
|
-
],
|
|
33333
|
-
[
|
|
33334
|
-
'vec3',
|
|
33335
|
-
'vec3f'
|
|
33336
|
-
],
|
|
33337
|
-
[
|
|
33338
|
-
'vec2',
|
|
33339
|
-
'vec2f'
|
|
33340
|
-
],
|
|
33341
|
-
[
|
|
33342
|
-
'float',
|
|
33343
|
-
'f32'
|
|
33344
|
-
]
|
|
33345
|
-
]);
|
|
33346
33555
|
class LitShader {
|
|
33347
33556
|
/**
|
|
33348
33557
|
* @param {GraphicsDevice} device - The graphics device.
|
|
@@ -33459,8 +33668,8 @@ class LitShader {
|
|
|
33459
33668
|
// for the user to provide required attributes using material.setAttribute
|
|
33460
33669
|
const languageChunks = ShaderChunks.get(this.device, this.shaderLanguage);
|
|
33461
33670
|
if (this.chunks.get('transformInstancingVS') === languageChunks.get('transformInstancingVS')) {
|
|
33462
|
-
attributes.instance_line1 =
|
|
33463
|
-
attributes.instance_line2 =
|
|
33671
|
+
attributes.instance_line1 = SEMANTIC_ATTR11;
|
|
33672
|
+
attributes.instance_line2 = SEMANTIC_ATTR12;
|
|
33464
33673
|
attributes.instance_line3 = SEMANTIC_ATTR14;
|
|
33465
33674
|
attributes.instance_line4 = SEMANTIC_ATTR15;
|
|
33466
33675
|
}
|
|
@@ -33543,7 +33752,7 @@ class LitShader {
|
|
|
33543
33752
|
// generate varyings code
|
|
33544
33753
|
varyings.forEach((type, name)=>{
|
|
33545
33754
|
this.varyingsCode += `#define VARYING_${name.toUpperCase()}\n`;
|
|
33546
|
-
this.varyingsCode += this.shaderLanguage === SHADERLANGUAGE_WGSL ? `varying ${name}: ${
|
|
33755
|
+
this.varyingsCode += this.shaderLanguage === SHADERLANGUAGE_WGSL ? `varying ${name}: ${primitiveGlslToWgslTypeMap.get(type)};\n` : `varying ${type} ${name};\n`;
|
|
33547
33756
|
});
|
|
33548
33757
|
// varyings code exposed as an include
|
|
33549
33758
|
this.includes.set('varyingsVS', this.varyingsCode);
|
|
@@ -37880,6 +38089,7 @@ class ShadowMapCache {
|
|
|
37880
38089
|
* @import { Light } from '../light.js'
|
|
37881
38090
|
* @import { Renderer } from './renderer.js'
|
|
37882
38091
|
* @import { ShadowRenderer } from './shadow-renderer.js'
|
|
38092
|
+
* @import { MeshInstance } from '../mesh-instance.js';
|
|
37883
38093
|
*/ const visibleSceneAabb = new BoundingBox();
|
|
37884
38094
|
const center = new Vec3();
|
|
37885
38095
|
const shadowCamView$1 = new Mat4();
|
|
@@ -37997,18 +38207,27 @@ class ShadowRendererDirectional {
|
|
|
37997
38207
|
// cull shadow casters
|
|
37998
38208
|
this.renderer.updateCameraFrustum(shadowCam);
|
|
37999
38209
|
this.shadowRenderer.cullShadowCasters(comp, light, lightRenderData.visibleCasters, shadowCam, casters);
|
|
38000
|
-
|
|
38001
|
-
let emptyAabb = true;
|
|
38210
|
+
const cascadeFlag = 1 << cascade;
|
|
38002
38211
|
const visibleCasters = lightRenderData.visibleCasters;
|
|
38003
|
-
|
|
38212
|
+
const origNumVisibleCasters = visibleCasters.length;
|
|
38213
|
+
let numVisibleCasters = 0;
|
|
38214
|
+
// exclude all mesh instances that are hidden for this cascade.
|
|
38215
|
+
// find out AABB of visible shadow casters
|
|
38216
|
+
for(let i = 0; i < origNumVisibleCasters; i++){
|
|
38004
38217
|
const meshInstance = visibleCasters[i];
|
|
38005
|
-
if (
|
|
38006
|
-
|
|
38007
|
-
|
|
38008
|
-
|
|
38009
|
-
|
|
38218
|
+
if (meshInstance.shadowCascadeMask & cascadeFlag) {
|
|
38219
|
+
visibleCasters[numVisibleCasters++] = meshInstance;
|
|
38220
|
+
if (numVisibleCasters === 1) {
|
|
38221
|
+
visibleSceneAabb.copy(meshInstance.aabb);
|
|
38222
|
+
} else {
|
|
38223
|
+
visibleSceneAabb.add(meshInstance.aabb);
|
|
38224
|
+
}
|
|
38010
38225
|
}
|
|
38011
38226
|
}
|
|
38227
|
+
// remove empty tail
|
|
38228
|
+
if (origNumVisibleCasters !== numVisibleCasters) {
|
|
38229
|
+
visibleCasters.length = numVisibleCasters;
|
|
38230
|
+
}
|
|
38012
38231
|
// calculate depth range of the caster's AABB from the point of view of the shadow camera
|
|
38013
38232
|
shadowCamView$1.copy(shadowCamNode.getWorldTransform()).invert();
|
|
38014
38233
|
const depthRange = getDepthRange(shadowCamView$1, visibleSceneAabb.getMin(), visibleSceneAabb.getMax());
|
|
@@ -43513,7 +43732,7 @@ let id = 0;
|
|
|
43513
43732
|
if (this.morph.intRenderFormat) defines.set('MORPH_INT', '');
|
|
43514
43733
|
const outputType = this.morph.intRenderFormat ? 'uvec4' : 'vec4';
|
|
43515
43734
|
return ShaderUtils.createShader(this.device, {
|
|
43516
|
-
uniqueName: '
|
|
43735
|
+
uniqueName: `TextureMorphShader_${maxCount}-${this.morph.intRenderFormat ? 'int' : 'float'}`,
|
|
43517
43736
|
attributes: {
|
|
43518
43737
|
vertex_position: SEMANTIC_POSITION
|
|
43519
43738
|
},
|
|
@@ -48518,46 +48737,92 @@ class GSplatSorter extends EventHandler {
|
|
|
48518
48737
|
}
|
|
48519
48738
|
|
|
48520
48739
|
var glslGsplatSogsReorderPS = /* glsl */ `
|
|
48521
|
-
uniform
|
|
48522
|
-
uniform sampler2D
|
|
48740
|
+
uniform highp sampler2D means_l;
|
|
48741
|
+
uniform highp sampler2D means_u;
|
|
48742
|
+
uniform highp sampler2D quats;
|
|
48743
|
+
uniform highp sampler2D scales;
|
|
48744
|
+
uniform highp sampler2D sh_labels;
|
|
48745
|
+
|
|
48523
48746
|
uniform highp uint numSplats;
|
|
48524
48747
|
|
|
48748
|
+
uint packU32(vec4 v) {
|
|
48749
|
+
return uint(v.x * 255.0) << 24u |
|
|
48750
|
+
uint(v.y * 255.0) << 16u |
|
|
48751
|
+
uint(v.z * 255.0) << 8u |
|
|
48752
|
+
uint(v.w * 255.0);
|
|
48753
|
+
}
|
|
48754
|
+
|
|
48755
|
+
uvec4 packU32(vec4 a, vec4 b, vec4 c, vec4 d) {
|
|
48756
|
+
return uvec4(packU32(a), packU32(b), packU32(c), packU32(d));
|
|
48757
|
+
}
|
|
48758
|
+
|
|
48525
48759
|
void main(void) {
|
|
48526
|
-
|
|
48527
|
-
|
|
48528
|
-
if (
|
|
48760
|
+
int w = int(textureSize(means_l, 0).x);
|
|
48761
|
+
ivec2 uv = ivec2(gl_FragCoord.xy);
|
|
48762
|
+
if (uint(uv.x + uv.y * w) >= numSplats) {
|
|
48763
|
+
discard;
|
|
48764
|
+
}
|
|
48529
48765
|
|
|
48530
|
-
|
|
48531
|
-
|
|
48532
|
-
|
|
48766
|
+
vec3 meansLSample = texelFetch(means_l, uv, 0).xyz;
|
|
48767
|
+
vec3 meansUSample = texelFetch(means_u, uv, 0).xyz;
|
|
48768
|
+
vec4 quatsSample = texelFetch(quats, uv, 0);
|
|
48769
|
+
vec3 scalesSample = texelFetch(scales, uv, 0).xyz;
|
|
48770
|
+
vec2 shLabelsSample = texelFetch(sh_labels, uv, 0).xy;
|
|
48533
48771
|
|
|
48534
|
-
|
|
48535
|
-
|
|
48772
|
+
pcFragColor0 = packU32(
|
|
48773
|
+
vec4(meansLSample, shLabelsSample.x),
|
|
48774
|
+
vec4(meansUSample, shLabelsSample.y),
|
|
48775
|
+
vec4(quatsSample),
|
|
48776
|
+
vec4(scalesSample, 0.0)
|
|
48777
|
+
);
|
|
48536
48778
|
}
|
|
48537
48779
|
`;
|
|
48538
48780
|
|
|
48539
48781
|
var wgslGsplatSogsReorderPS = /* wgsl */ `
|
|
48540
|
-
var
|
|
48541
|
-
var
|
|
48782
|
+
var means_l: texture_2d<f32>;
|
|
48783
|
+
var means_u: texture_2d<f32>;
|
|
48784
|
+
var quats: texture_2d<f32>;
|
|
48785
|
+
var scales: texture_2d<f32>;
|
|
48786
|
+
var sh_labels: texture_2d<f32>;
|
|
48787
|
+
|
|
48542
48788
|
uniform numSplats: u32;
|
|
48543
48789
|
|
|
48790
|
+
fn packU32(v: vec4<f32>) -> u32 {
|
|
48791
|
+
return (u32(v.x * 255.0) << 24u) |
|
|
48792
|
+
(u32(v.y * 255.0) << 16u) |
|
|
48793
|
+
(u32(v.z * 255.0) << 8u) |
|
|
48794
|
+
u32(v.w * 255.0);
|
|
48795
|
+
}
|
|
48796
|
+
|
|
48797
|
+
fn packUVec32(a: vec4<f32>, b: vec4<f32>, c: vec4<f32>, d: vec4<f32>) -> vec4<u32> {
|
|
48798
|
+
return vec4<u32>(packU32(a), packU32(b), packU32(c), packU32(d));
|
|
48799
|
+
}
|
|
48800
|
+
|
|
48544
48801
|
@fragment
|
|
48545
48802
|
fn fragmentMain(input: FragmentInput) -> FragmentOutput {
|
|
48546
48803
|
var output: FragmentOutput;
|
|
48547
48804
|
|
|
48548
|
-
let w: u32 = textureDimensions(
|
|
48549
|
-
let
|
|
48550
|
-
if (
|
|
48805
|
+
let w: u32 = textureDimensions(means_l, 0).x;
|
|
48806
|
+
let uv: vec2<u32> = vec2<u32>(pcPosition.xy);
|
|
48807
|
+
if (uv.x + uv.y * w >= uniform.numSplats) {
|
|
48551
48808
|
discard;
|
|
48552
48809
|
return output;
|
|
48553
48810
|
}
|
|
48554
48811
|
|
|
48555
48812
|
// fetch the source index and calculate source uv
|
|
48556
|
-
let
|
|
48557
|
-
let
|
|
48813
|
+
let meansLSample: vec3<f32> = textureLoad(means_l, uv, 0).xyz;
|
|
48814
|
+
let meansUSample: vec3<f32> = textureLoad(means_u, uv, 0).xyz;
|
|
48815
|
+
let quatsSample: vec4<f32> = textureLoad(quats, uv, 0);
|
|
48816
|
+
let scalesSample: vec3<f32> = textureLoad(scales, uv, 0).xyz;
|
|
48817
|
+
let shLabelsSample: vec2<f32> = textureLoad(sh_labels, uv, 0).xy;
|
|
48818
|
+
|
|
48819
|
+
output.color = packUVec32(
|
|
48820
|
+
vec4(meansLSample, shLabelsSample.x),
|
|
48821
|
+
vec4(meansUSample, shLabelsSample.y),
|
|
48822
|
+
vec4(quatsSample),
|
|
48823
|
+
vec4(scalesSample, 0.0)
|
|
48824
|
+
);
|
|
48558
48825
|
|
|
48559
|
-
// sample the source texture
|
|
48560
|
-
output.color = textureLoad(sourceTexture, vec2<i32>(suv), 0);
|
|
48561
48826
|
return output;
|
|
48562
48827
|
}
|
|
48563
48828
|
`;
|
|
@@ -48654,6 +48919,7 @@ class GSplatSogsData {
|
|
|
48654
48919
|
this.sh0?.destroy();
|
|
48655
48920
|
this.sh_centroids?.destroy();
|
|
48656
48921
|
this.sh_labels?.destroy();
|
|
48922
|
+
this.packedTexture?.destroy();
|
|
48657
48923
|
}
|
|
48658
48924
|
createIter(p, r, s, c, sh) {
|
|
48659
48925
|
return new GSplatSogsIterator(this, p, r, s, c, sh);
|
|
@@ -48669,7 +48935,6 @@ class GSplatSogsData {
|
|
|
48669
48935
|
const { means } = meta;
|
|
48670
48936
|
const means_u_data = new Uint32Array(means_u._levels[0].buffer);
|
|
48671
48937
|
const means_l_data = new Uint32Array(means_l._levels[0].buffer);
|
|
48672
|
-
const order = this.orderTexture?._levels[0];
|
|
48673
48938
|
const mx = means.mins[0] / 65535;
|
|
48674
48939
|
const my = means.mins[1] / 65535;
|
|
48675
48940
|
const mz = means.mins[2] / 65535;
|
|
@@ -48677,7 +48942,7 @@ class GSplatSogsData {
|
|
|
48677
48942
|
const My = means.maxs[1] / 65535;
|
|
48678
48943
|
const Mz = means.maxs[2] / 65535;
|
|
48679
48944
|
for(let i = 0; i < numSplats; i++){
|
|
48680
|
-
const idx =
|
|
48945
|
+
const idx = i;
|
|
48681
48946
|
const means_u = means_u_data[idx];
|
|
48682
48947
|
const means_l = means_l_data[idx];
|
|
48683
48948
|
const wx = means_u << 8 & 0xff00 | means_l & 0xff;
|
|
@@ -48793,10 +49058,10 @@ class GSplatSogsData {
|
|
|
48793
49058
|
}
|
|
48794
49059
|
]);
|
|
48795
49060
|
}
|
|
48796
|
-
//
|
|
48797
|
-
|
|
48798
|
-
const {
|
|
48799
|
-
const { device
|
|
49061
|
+
// pack the means, quats, scales and sh_labels data into one RGBA32U texture
|
|
49062
|
+
packGpuMemory() {
|
|
49063
|
+
const { means_l, means_u, quats, scales, sh_labels, numSplats } = this;
|
|
49064
|
+
const { device } = means_l;
|
|
48800
49065
|
const { scope } = device;
|
|
48801
49066
|
const shader = ShaderUtils.createShader(device, {
|
|
48802
49067
|
uniqueName: 'GsplatSogsReorderShader',
|
|
@@ -48805,107 +49070,52 @@ class GSplatSogsData {
|
|
|
48805
49070
|
},
|
|
48806
49071
|
vertexChunk: 'fullscreenQuadVS',
|
|
48807
49072
|
fragmentGLSL: glslGsplatSogsReorderPS,
|
|
48808
|
-
fragmentWGSL: wgslGsplatSogsReorderPS
|
|
49073
|
+
fragmentWGSL: wgslGsplatSogsReorderPS,
|
|
49074
|
+
fragmentOutputTypes: [
|
|
49075
|
+
'uvec4'
|
|
49076
|
+
]
|
|
48809
49077
|
});
|
|
48810
|
-
const
|
|
48811
|
-
|
|
48812
|
-
|
|
48813
|
-
|
|
48814
|
-
mipmaps: false
|
|
49078
|
+
const renderTarget = new RenderTarget({
|
|
49079
|
+
colorBuffer: this.packedTexture,
|
|
49080
|
+
depth: false,
|
|
49081
|
+
mipLevel: 0
|
|
48815
49082
|
});
|
|
48816
|
-
const members = [
|
|
48817
|
-
'means_l',
|
|
48818
|
-
'means_u',
|
|
48819
|
-
'quats',
|
|
48820
|
-
'scales',
|
|
48821
|
-
'sh0',
|
|
48822
|
-
'sh_labels'
|
|
48823
|
-
];
|
|
48824
|
-
device.setBlendState(BlendState.NOBLEND);
|
|
48825
49083
|
device.setCullMode(CULLFACE_NONE);
|
|
49084
|
+
device.setBlendState(BlendState.NOBLEND);
|
|
48826
49085
|
device.setDepthState(DepthState.NODEPTH);
|
|
48827
|
-
|
|
48828
|
-
|
|
48829
|
-
|
|
48830
|
-
|
|
48831
|
-
|
|
48832
|
-
|
|
48833
|
-
|
|
48834
|
-
|
|
48835
|
-
depth: false,
|
|
48836
|
-
mipLevel: 0
|
|
48837
|
-
});
|
|
48838
|
-
// patch source texture with data from target
|
|
48839
|
-
sourceTexture._levels[0] = targetTexture._levels[0];
|
|
48840
|
-
sourceTexture.upload();
|
|
48841
|
-
resolve(scope, {
|
|
48842
|
-
orderTexture,
|
|
48843
|
-
sourceTexture,
|
|
48844
|
-
numSplats
|
|
48845
|
-
});
|
|
48846
|
-
drawQuadWithShader(device, renderTarget, shader);
|
|
48847
|
-
renderTarget.destroy();
|
|
49086
|
+
resolve(scope, {
|
|
49087
|
+
means_l,
|
|
49088
|
+
means_u,
|
|
49089
|
+
quats,
|
|
49090
|
+
scales,
|
|
49091
|
+
// use means_l as dummy texture for sh_labels if there is no spherical harmonics data
|
|
49092
|
+
sh_labels: sh_labels ?? means_l,
|
|
49093
|
+
numSplats
|
|
48848
49094
|
});
|
|
48849
|
-
|
|
49095
|
+
drawQuadWithShader(device, renderTarget, shader);
|
|
49096
|
+
renderTarget.destroy();
|
|
49097
|
+
shader.destroy();
|
|
48850
49098
|
}
|
|
48851
|
-
|
|
48852
|
-
// returns an array of 32-bit unsigned integers
|
|
48853
|
-
calcMortonOrder() {
|
|
48854
|
-
// https://fgiesen.wordpress.com/2009/12/13/decoding-morton-codes/
|
|
48855
|
-
const encodeMorton3 = (x, y, z)=>{
|
|
48856
|
-
const Part1By2 = (x)=>{
|
|
48857
|
-
x &= 0x000003ff;
|
|
48858
|
-
x = (x ^ x << 16) & 0xff0000ff;
|
|
48859
|
-
x = (x ^ x << 8) & 0x0300f00f;
|
|
48860
|
-
x = (x ^ x << 4) & 0x030c30c3;
|
|
48861
|
-
x = (x ^ x << 2) & 0x09249249;
|
|
48862
|
-
return x;
|
|
48863
|
-
};
|
|
48864
|
-
return (Part1By2(z) << 2) + (Part1By2(y) << 1) + Part1By2(x);
|
|
48865
|
-
};
|
|
48866
|
-
const { means_l, means_u } = this;
|
|
48867
|
-
const means_l_data = means_l._levels[0];
|
|
48868
|
-
const means_u_data = means_u._levels[0];
|
|
48869
|
-
const codes = new BigUint64Array(this.numSplats);
|
|
48870
|
-
// generate Morton codes for each splat based on the means directly (i.e. the log-space coordinates)
|
|
48871
|
-
for(let i = 0; i < this.numSplats; ++i){
|
|
48872
|
-
const ix = means_u_data[i * 4 + 0] << 2 | means_l_data[i * 4 + 0] >>> 6;
|
|
48873
|
-
const iy = means_u_data[i * 4 + 1] << 2 | means_l_data[i * 4 + 1] >>> 6;
|
|
48874
|
-
const iz = means_u_data[i * 4 + 2] << 2 | means_l_data[i * 4 + 2] >>> 6;
|
|
48875
|
-
codes[i] = BigInt(encodeMorton3(ix, iy, iz)) << BigInt(32) | BigInt(i);
|
|
48876
|
-
}
|
|
48877
|
-
codes.sort();
|
|
48878
|
-
// allocate data for the order buffer, but make it texture-memory sized
|
|
48879
|
-
const order = new Uint32Array(means_l.width * means_l.height);
|
|
48880
|
-
for(let i = 0; i < this.numSplats; ++i){
|
|
48881
|
-
order[i] = Number(codes[i] & BigInt(0xffffffff));
|
|
48882
|
-
}
|
|
48883
|
-
return order;
|
|
48884
|
-
}
|
|
48885
|
-
async reorderData() {
|
|
49099
|
+
async prepareGpuData() {
|
|
48886
49100
|
const { device, height, width } = this.means_l;
|
|
48887
|
-
// copy back means_l and means_u data
|
|
49101
|
+
// copy back means_l and means_u data so cpu reorder has access to it
|
|
48888
49102
|
this.means_l._levels[0] = await readImageDataAsync(this.means_l);
|
|
48889
49103
|
this.means_u._levels[0] = await readImageDataAsync(this.means_u);
|
|
48890
|
-
this.
|
|
48891
|
-
name: '
|
|
48892
|
-
width,
|
|
48893
|
-
height,
|
|
48894
|
-
format:
|
|
48895
|
-
mipmaps: false
|
|
48896
|
-
levels: [
|
|
48897
|
-
this.calcMortonOrder()
|
|
48898
|
-
]
|
|
49104
|
+
this.packedTexture = new Texture(device, {
|
|
49105
|
+
name: 'sogsPackedTexture',
|
|
49106
|
+
width: width,
|
|
49107
|
+
height: height,
|
|
49108
|
+
format: PIXELFORMAT_RGBA32U,
|
|
49109
|
+
mipmaps: false
|
|
48899
49110
|
});
|
|
48900
49111
|
device.on('devicerestored', ()=>{
|
|
48901
|
-
this.
|
|
49112
|
+
this.packGpuMemory();
|
|
48902
49113
|
});
|
|
48903
|
-
this.
|
|
49114
|
+
this.packGpuMemory();
|
|
48904
49115
|
}
|
|
48905
|
-
|
|
48906
|
-
|
|
48907
|
-
|
|
48908
|
-
means_u._levels[0] = await readImageDataAsync(means_u);
|
|
49116
|
+
// temporary, for backwards compatibility
|
|
49117
|
+
reorderData() {
|
|
49118
|
+
return this.prepareGpuData();
|
|
48909
49119
|
}
|
|
48910
49120
|
}
|
|
48911
49121
|
|
|
@@ -49095,13 +49305,9 @@ class GSplatSogsResource extends GSplatResourceBase {
|
|
|
49095
49305
|
material.setDefine('GSPLAT_SOGS_DATA', true);
|
|
49096
49306
|
material.setDefine('SH_BANDS', this.gsplatData.shBands);
|
|
49097
49307
|
[
|
|
49098
|
-
'
|
|
49099
|
-
'means_u',
|
|
49100
|
-
'quats',
|
|
49101
|
-
'scales',
|
|
49308
|
+
'packedTexture',
|
|
49102
49309
|
'sh0',
|
|
49103
|
-
'sh_centroids'
|
|
49104
|
-
'sh_labels'
|
|
49310
|
+
'sh_centroids'
|
|
49105
49311
|
].forEach((name)=>{
|
|
49106
49312
|
if (gsplatData[name]) {
|
|
49107
49313
|
material.setParameter(name, gsplatData[name]);
|
|
@@ -55805,10 +56011,7 @@ vec4 readColor(in SplatSource source) {
|
|
|
55805
56011
|
`;
|
|
55806
56012
|
|
|
55807
56013
|
var gsplatSogsDataVS$1 = /* glsl */ `
|
|
55808
|
-
uniform highp
|
|
55809
|
-
uniform highp sampler2D means_l;
|
|
55810
|
-
uniform highp sampler2D quats;
|
|
55811
|
-
uniform highp sampler2D scales;
|
|
56014
|
+
uniform highp usampler2D packedTexture;
|
|
55812
56015
|
|
|
55813
56016
|
uniform vec3 means_mins;
|
|
55814
56017
|
uniform vec3 means_maxs;
|
|
@@ -55816,13 +56019,28 @@ uniform vec3 means_maxs;
|
|
|
55816
56019
|
uniform vec3 scales_mins;
|
|
55817
56020
|
uniform vec3 scales_maxs;
|
|
55818
56021
|
|
|
56022
|
+
vec4 unpackU32(uint v) {
|
|
56023
|
+
return vec4(
|
|
56024
|
+
float((v >> 24u) & 0xFFu) / 255.0,
|
|
56025
|
+
float((v >> 16u) & 0xFFu) / 255.0,
|
|
56026
|
+
float((v >> 8u) & 0xFFu) / 255.0,
|
|
56027
|
+
float(v & 0xFFu) / 255.0
|
|
56028
|
+
);
|
|
56029
|
+
}
|
|
56030
|
+
|
|
56031
|
+
uvec4 packedSample;
|
|
56032
|
+
|
|
55819
56033
|
// read the model-space center of the gaussian
|
|
55820
56034
|
vec3 readCenter(SplatSource source) {
|
|
55821
|
-
vec3 u = texelFetch(means_u, source.uv, 0).xyz;
|
|
55822
|
-
vec3 l = texelFetch(means_l, source.uv, 0).xyz;
|
|
55823
|
-
vec3 n = (l * 255.0 + u * 255.0 * 256.0) / 65535.0;
|
|
55824
56035
|
|
|
56036
|
+
// read the packed texture sample
|
|
56037
|
+
packedSample = texelFetch(packedTexture, source.uv, 0);
|
|
56038
|
+
|
|
56039
|
+
vec3 l = unpackU32(packedSample.x).xyz;
|
|
56040
|
+
vec3 u = unpackU32(packedSample.y).xyz;
|
|
56041
|
+
vec3 n = (l * 255.0 + u * 255.0 * 256.0) / 65535.0;
|
|
55825
56042
|
vec3 v = mix(means_mins, means_maxs, n);
|
|
56043
|
+
|
|
55826
56044
|
return sign(v) * (exp(abs(v)) - 1.0);
|
|
55827
56045
|
}
|
|
55828
56046
|
|
|
@@ -55830,7 +56048,9 @@ const float norm = 2.0 / sqrt(2.0);
|
|
|
55830
56048
|
|
|
55831
56049
|
// sample covariance vectors
|
|
55832
56050
|
void readCovariance(in SplatSource source, out vec3 covA, out vec3 covB) {
|
|
55833
|
-
vec4 qdata =
|
|
56051
|
+
vec4 qdata = unpackU32(packedSample.z);
|
|
56052
|
+
vec3 sdata = unpackU32(packedSample.w).xyz;
|
|
56053
|
+
|
|
55834
56054
|
vec3 abc = (qdata.xyz - 0.5) * norm;
|
|
55835
56055
|
float d = sqrt(max(0.0, 1.0 - dot(abc, abc)));
|
|
55836
56056
|
|
|
@@ -55841,7 +56061,7 @@ void readCovariance(in SplatSource source, out vec3 covA, out vec3 covB) {
|
|
|
55841
56061
|
((mode == 2u) ? vec4(abc.xy, d, abc.z) : vec4(abc, d)));
|
|
55842
56062
|
|
|
55843
56063
|
mat3 rot = quatToMat3(quat);
|
|
55844
|
-
vec3 scale = exp(mix(scales_mins, scales_maxs,
|
|
56064
|
+
vec3 scale = exp(mix(scales_mins, scales_maxs, sdata));
|
|
55845
56065
|
|
|
55846
56066
|
// M = S * R
|
|
55847
56067
|
mat3 M = transpose(mat3(
|
|
@@ -55856,7 +56076,6 @@ void readCovariance(in SplatSource source, out vec3 covA, out vec3 covB) {
|
|
|
55856
56076
|
`;
|
|
55857
56077
|
|
|
55858
56078
|
var gsplatSogsSHVS$1 = /* glsl */ `
|
|
55859
|
-
uniform highp sampler2D sh_labels;
|
|
55860
56079
|
uniform highp sampler2D sh_centroids;
|
|
55861
56080
|
|
|
55862
56081
|
uniform float shN_mins;
|
|
@@ -55866,7 +56085,7 @@ uniform float shN_maxs;
|
|
|
55866
56085
|
|
|
55867
56086
|
void readSHData(in SplatSource source, out vec3 sh[SH_COEFFS], out float scale) {
|
|
55868
56087
|
// extract spherical harmonics palette index
|
|
55869
|
-
ivec2 t = ivec2(
|
|
56088
|
+
ivec2 t = ivec2(packedSample.x & 255u, packedSample.y & 255u);
|
|
55870
56089
|
int n = t.x + t.y * 256;
|
|
55871
56090
|
int u = (n % 64) * SH_COEFFS;
|
|
55872
56091
|
int v = n / 64;
|
|
@@ -58669,9 +58888,11 @@ var morphPS$1 = /* glsl */ `
|
|
|
58669
58888
|
void main (void) {
|
|
58670
58889
|
highp vec3 color = vec3(0, 0, 0);
|
|
58671
58890
|
|
|
58891
|
+
ivec2 pixelCoords = ivec2(uv0 * vec2(textureSize(morphTexture, 0).xy));
|
|
58892
|
+
|
|
58672
58893
|
for (int i = 0; i < count; i++) {
|
|
58673
58894
|
uint textureIndex = morphIndex[i];
|
|
58674
|
-
vec3 delta =
|
|
58895
|
+
vec3 delta = texelFetch(morphTexture, ivec3(pixelCoords, int(textureIndex)), 0).xyz;
|
|
58675
58896
|
color += morphFactor[i] * delta;
|
|
58676
58897
|
}
|
|
58677
58898
|
|
|
@@ -63810,10 +64031,7 @@ fn readColor(source: ptr<function, SplatSource>) -> vec4f {
|
|
|
63810
64031
|
`;
|
|
63811
64032
|
|
|
63812
64033
|
var gsplatSogsDataVS = /* wgsl */ `
|
|
63813
|
-
var
|
|
63814
|
-
var means_l: texture_2d<f32>;
|
|
63815
|
-
var quats: texture_2d<f32>;
|
|
63816
|
-
var scales: texture_2d<f32>;
|
|
64034
|
+
var packedTexture: texture_2d<u32>;
|
|
63817
64035
|
|
|
63818
64036
|
uniform means_mins: vec3f;
|
|
63819
64037
|
uniform means_maxs: vec3f;
|
|
@@ -63821,13 +64039,27 @@ uniform means_maxs: vec3f;
|
|
|
63821
64039
|
uniform scales_mins: vec3f;
|
|
63822
64040
|
uniform scales_maxs: vec3f;
|
|
63823
64041
|
|
|
64042
|
+
fn unpackU32(u: u32) -> vec4f {
|
|
64043
|
+
return vec4f(
|
|
64044
|
+
f32((u >> 24u) & 0xFFu) / 255.0,
|
|
64045
|
+
f32((u >> 16u) & 0xFFu) / 255.0,
|
|
64046
|
+
f32((u >> 8u) & 0xFFu) / 255.0,
|
|
64047
|
+
f32(u & 0xFFu) / 255.0
|
|
64048
|
+
);
|
|
64049
|
+
}
|
|
64050
|
+
|
|
64051
|
+
var<private> packedSample: vec4<u32>;
|
|
64052
|
+
|
|
63824
64053
|
// read the model-space center of the gaussian
|
|
63825
64054
|
fn readCenter(source: ptr<function, SplatSource>) -> vec3f {
|
|
63826
|
-
let u: vec3f = textureLoad(means_u, source.uv, 0).xyz;
|
|
63827
|
-
let l: vec3f = textureLoad(means_l, source.uv, 0).xyz;
|
|
63828
|
-
let n: vec3f = (l * 255.0 + u * 255.0 * 256.0) / 65535.0;
|
|
63829
64055
|
|
|
64056
|
+
packedSample = textureLoad(packedTexture, source.uv, 0);
|
|
64057
|
+
|
|
64058
|
+
let l: vec3f = unpackU32(packedSample.x).xyz;
|
|
64059
|
+
let u: vec3f = unpackU32(packedSample.y).xyz;
|
|
64060
|
+
let n: vec3f = (l * 255.0 + u * 255.0 * 256.0) / 65535.0;
|
|
63830
64061
|
let v: vec3f = mix(uniform.means_mins, uniform.means_maxs, n);
|
|
64062
|
+
|
|
63831
64063
|
return sign(v) * (exp(abs(v)) - 1.0);
|
|
63832
64064
|
}
|
|
63833
64065
|
|
|
@@ -63835,7 +64067,9 @@ const norm: f32 = 2.0 / sqrt(2.0);
|
|
|
63835
64067
|
|
|
63836
64068
|
// sample covariance vectors
|
|
63837
64069
|
fn readCovariance(source: ptr<function, SplatSource>, covA_ptr: ptr<function, vec3f>, covB_ptr: ptr<function, vec3f>) {
|
|
63838
|
-
let qdata: vec4f =
|
|
64070
|
+
let qdata: vec4f = unpackU32(packedSample.z);
|
|
64071
|
+
let sdata: vec3f = unpackU32(packedSample.w).xyz;
|
|
64072
|
+
|
|
63839
64073
|
let abc: vec3f = (qdata.xyz - 0.5) * norm;
|
|
63840
64074
|
let d: f32 = sqrt(max(0.0, 1.0 - dot(abc, abc)));
|
|
63841
64075
|
|
|
@@ -63854,7 +64088,7 @@ fn readCovariance(source: ptr<function, SplatSource>, covA_ptr: ptr<function, ve
|
|
|
63854
64088
|
|
|
63855
64089
|
|
|
63856
64090
|
let rot: mat3x3f = quatToMat3(quat);
|
|
63857
|
-
let scale: vec3f = exp(mix(uniform.scales_mins, uniform.scales_maxs,
|
|
64091
|
+
let scale: vec3f = exp(mix(uniform.scales_mins, uniform.scales_maxs, sdata));
|
|
63858
64092
|
|
|
63859
64093
|
// M = S * R
|
|
63860
64094
|
let M: mat3x3f = transpose(mat3x3f(
|
|
@@ -63869,7 +64103,6 @@ fn readCovariance(source: ptr<function, SplatSource>, covA_ptr: ptr<function, ve
|
|
|
63869
64103
|
`;
|
|
63870
64104
|
|
|
63871
64105
|
var gsplatSogsSHVS = /* wgsl */ `
|
|
63872
|
-
var sh_labels: texture_2d<f32>;
|
|
63873
64106
|
var sh_centroids: texture_2d<f32>;
|
|
63874
64107
|
|
|
63875
64108
|
uniform shN_mins: f32;
|
|
@@ -63877,7 +64110,7 @@ uniform shN_maxs: f32;
|
|
|
63877
64110
|
|
|
63878
64111
|
fn readSHData(source: ptr<function, SplatSource>, sh: ptr<function, array<vec3f, SH_COEFFS>>, scale: ptr<function, f32>) {
|
|
63879
64112
|
// extract spherical harmonics palette index
|
|
63880
|
-
let t: vec2<i32> = vec2<i32>(
|
|
64113
|
+
let t: vec2<i32> = vec2<i32>(i32(packedSample.x & 255u), i32(packedSample.y & 255u));
|
|
63881
64114
|
let n: i32 = t.x + t.y * 256;
|
|
63882
64115
|
let u: i32 = (n % 64) * SH_COEFFS;
|
|
63883
64116
|
let v: i32 = n / 64;
|
|
@@ -66805,7 +67038,6 @@ var morphPS = /* wgsl */ `
|
|
|
66805
67038
|
varying uv0: vec2f;
|
|
66806
67039
|
|
|
66807
67040
|
var morphTexture: texture_2d_array<f32>;
|
|
66808
|
-
var morphTextureSampler : sampler;
|
|
66809
67041
|
uniform morphFactor: array<f32, {MORPH_TEXTURE_MAX_COUNT}>;
|
|
66810
67042
|
uniform morphIndex: array<u32, {MORPH_TEXTURE_MAX_COUNT}>;
|
|
66811
67043
|
uniform count: u32;
|
|
@@ -66813,9 +67045,12 @@ var morphPS = /* wgsl */ `
|
|
|
66813
67045
|
@fragment
|
|
66814
67046
|
fn fragmentMain(input : FragmentInput) -> FragmentOutput {
|
|
66815
67047
|
var color = vec3f(0, 0, 0);
|
|
67048
|
+
let textureDims = textureDimensions(morphTexture);
|
|
67049
|
+
let pixelCoords = vec2i(input.uv0 * vec2f(textureDims));
|
|
67050
|
+
|
|
66816
67051
|
for (var i: u32 = 0; i < uniform.count; i = i + 1) {
|
|
66817
67052
|
var textureIndex: u32 = uniform.morphIndex[i].element;
|
|
66818
|
-
var delta =
|
|
67053
|
+
var delta = textureLoad(morphTexture, pixelCoords, textureIndex, 0).xyz;
|
|
66819
67054
|
color += uniform.morphFactor[i].element * delta;
|
|
66820
67055
|
}
|
|
66821
67056
|
|
|
@@ -81707,7 +81942,7 @@ let executionOrderCounter = 0;
|
|
|
81707
81942
|
*/ this._instance = null, /**
|
|
81708
81943
|
* @type {ShaderMaterial|null}
|
|
81709
81944
|
* @private
|
|
81710
|
-
*/ this._materialTmp = null, /** @private */ this._highQualitySH =
|
|
81945
|
+
*/ this._materialTmp = null, /** @private */ this._highQualitySH = true, /**
|
|
81711
81946
|
* @type {BoundingBox|null}
|
|
81712
81947
|
* @private
|
|
81713
81948
|
*/ this._customAabb = null, /**
|
|
@@ -86902,6 +87137,37 @@ class PlyParser {
|
|
|
86902
87137
|
}
|
|
86903
87138
|
}
|
|
86904
87139
|
|
|
87140
|
+
// combine the progress updates from multiple assets
|
|
87141
|
+
// and fire progress events on the target
|
|
87142
|
+
const combineProgress = (target, assets)=>{
|
|
87143
|
+
const map = new Map();
|
|
87144
|
+
const fire = ()=>{
|
|
87145
|
+
let loaded = 0;
|
|
87146
|
+
let total = 0;
|
|
87147
|
+
map.forEach((value)=>{
|
|
87148
|
+
loaded += value.loaded;
|
|
87149
|
+
total += value.total;
|
|
87150
|
+
});
|
|
87151
|
+
target.fire('progress', loaded, total);
|
|
87152
|
+
};
|
|
87153
|
+
assets.forEach((asset)=>{
|
|
87154
|
+
const progress = (loaded, total)=>{
|
|
87155
|
+
map.set(asset, {
|
|
87156
|
+
loaded,
|
|
87157
|
+
total
|
|
87158
|
+
});
|
|
87159
|
+
fire();
|
|
87160
|
+
};
|
|
87161
|
+
const done = ()=>{
|
|
87162
|
+
asset.off('progress', progress);
|
|
87163
|
+
asset.off('load', done);
|
|
87164
|
+
asset.off('error', done);
|
|
87165
|
+
};
|
|
87166
|
+
asset.on('progress', progress);
|
|
87167
|
+
asset.on('load', done);
|
|
87168
|
+
asset.on('error', done);
|
|
87169
|
+
});
|
|
87170
|
+
};
|
|
86905
87171
|
/**
|
|
86906
87172
|
* @import { AppBase } from '../app-base.js'
|
|
86907
87173
|
* @import { ResourceHandlerCallback } from '../handlers/handler.js'
|
|
@@ -86938,11 +87204,13 @@ class PlyParser {
|
|
|
86938
87204
|
texture.on('error', (err)=>reject(err));
|
|
86939
87205
|
});
|
|
86940
87206
|
assets.add(texture);
|
|
86941
|
-
assets.load(texture);
|
|
86942
87207
|
promises.push(promise);
|
|
86943
87208
|
return texture;
|
|
86944
87209
|
});
|
|
86945
87210
|
});
|
|
87211
|
+
const textureAssets = subs.map((sub)=>textures[sub]).flat();
|
|
87212
|
+
combineProgress(asset, textureAssets);
|
|
87213
|
+
textureAssets.forEach((t)=>assets.load(t));
|
|
86946
87214
|
// wait for all textures to complete loading
|
|
86947
87215
|
await Promise.allSettled(promises);
|
|
86948
87216
|
// construct the gsplat resource
|
|
@@ -86956,12 +87224,12 @@ class PlyParser {
|
|
|
86956
87224
|
data.sh0 = textures.sh0[0].resource;
|
|
86957
87225
|
data.sh_centroids = textures.shN?.[0]?.resource;
|
|
86958
87226
|
data.sh_labels = textures.shN?.[1]?.resource;
|
|
86959
|
-
|
|
86960
|
-
|
|
86961
|
-
|
|
86962
|
-
await data.
|
|
87227
|
+
const decompress = asset.data?.decompress;
|
|
87228
|
+
if (!decompress) {
|
|
87229
|
+
// no need to prepare gpu data if decompressing
|
|
87230
|
+
await data.prepareGpuData();
|
|
86963
87231
|
}
|
|
86964
|
-
const resource =
|
|
87232
|
+
const resource = decompress ? new GSplatResource(this.app.graphicsDevice, await data.decompress()) : new GSplatSogsResource(this.app.graphicsDevice, data);
|
|
86965
87233
|
callback(null, resource);
|
|
86966
87234
|
}
|
|
86967
87235
|
/**
|
|
@@ -88028,9 +88296,9 @@ class ImgAlphaTest {
|
|
|
88028
88296
|
crossOrigin = this.crossOrigin;
|
|
88029
88297
|
}
|
|
88030
88298
|
if (this.device.supportsImageBitmap) {
|
|
88031
|
-
this._loadImageBitmap(url.load, url.original, crossOrigin, handler);
|
|
88299
|
+
this._loadImageBitmap(url.load, url.original, crossOrigin, handler, asset);
|
|
88032
88300
|
} else {
|
|
88033
|
-
this._loadImage(url.load, url.original, crossOrigin, handler);
|
|
88301
|
+
this._loadImage(url.load, url.original, crossOrigin, handler, asset);
|
|
88034
88302
|
}
|
|
88035
88303
|
}
|
|
88036
88304
|
open(url, data, device, textureOptions = {}) {
|
|
@@ -88045,7 +88313,7 @@ class ImgAlphaTest {
|
|
|
88045
88313
|
texture.setSource(data);
|
|
88046
88314
|
return texture;
|
|
88047
88315
|
}
|
|
88048
|
-
_loadImage(url, originalUrl, crossOrigin, callback) {
|
|
88316
|
+
_loadImage(url, originalUrl, crossOrigin, callback, asset) {
|
|
88049
88317
|
const image = new Image();
|
|
88050
88318
|
if (crossOrigin) {
|
|
88051
88319
|
image.crossOrigin = crossOrigin;
|
|
@@ -88053,8 +88321,12 @@ class ImgAlphaTest {
|
|
|
88053
88321
|
let retries = 0;
|
|
88054
88322
|
const maxRetries = this.maxRetries;
|
|
88055
88323
|
let retryTimeout;
|
|
88324
|
+
const dummySize = 1024 * 1024;
|
|
88325
|
+
// HTMLImageElement doesn't support progress events, so we emulate it instead
|
|
88326
|
+
asset?.fire('progress', 0, dummySize);
|
|
88056
88327
|
// Call success callback after opening Texture
|
|
88057
88328
|
image.onload = function() {
|
|
88329
|
+
asset?.fire('progress', dummySize, dummySize);
|
|
88058
88330
|
callback(null, image);
|
|
88059
88331
|
};
|
|
88060
88332
|
image.onerror = function() {
|
|
@@ -88078,12 +88350,13 @@ class ImgAlphaTest {
|
|
|
88078
88350
|
};
|
|
88079
88351
|
image.src = url;
|
|
88080
88352
|
}
|
|
88081
|
-
_loadImageBitmap(url, originalUrl, crossOrigin, callback) {
|
|
88353
|
+
_loadImageBitmap(url, originalUrl, crossOrigin, callback, asset) {
|
|
88082
88354
|
const options = {
|
|
88083
88355
|
cache: true,
|
|
88084
88356
|
responseType: 'blob',
|
|
88085
88357
|
retry: this.maxRetries > 0,
|
|
88086
|
-
maxRetries: this.maxRetries
|
|
88358
|
+
maxRetries: this.maxRetries,
|
|
88359
|
+
progress: asset
|
|
88087
88360
|
};
|
|
88088
88361
|
http.get(url, options, (err, blob)=>{
|
|
88089
88362
|
if (err) {
|
|
@@ -89014,173 +89287,185 @@ class KdTree {
|
|
|
89014
89287
|
}
|
|
89015
89288
|
}
|
|
89016
89289
|
|
|
89017
|
-
const clusterWgsl = (numColumns,
|
|
89290
|
+
const clusterWgsl = (numColumns, useF16) => {
|
|
89018
89291
|
const floatType = useF16 ? 'f16' : 'f32';
|
|
89019
|
-
return `
|
|
89020
|
-
${useF16 ? 'enable f16;' : ''}
|
|
89021
|
-
|
|
89022
|
-
|
|
89023
|
-
|
|
89024
|
-
|
|
89025
|
-
|
|
89026
|
-
|
|
89027
|
-
|
|
89028
|
-
|
|
89029
|
-
|
|
89030
|
-
|
|
89031
|
-
|
|
89032
|
-
|
|
89033
|
-
|
|
89034
|
-
|
|
89035
|
-
|
|
89036
|
-
|
|
89037
|
-
|
|
89038
|
-
|
|
89039
|
-
|
|
89040
|
-
|
|
89041
|
-
|
|
89042
|
-
|
|
89043
|
-
|
|
89044
|
-
|
|
89045
|
-
}
|
|
89046
|
-
|
|
89047
|
-
|
|
89048
|
-
|
|
89049
|
-
|
|
89050
|
-
|
|
89051
|
-
|
|
89052
|
-
)
|
|
89053
|
-
|
|
89054
|
-
|
|
89055
|
-
|
|
89056
|
-
//
|
|
89057
|
-
|
|
89058
|
-
|
|
89059
|
-
|
|
89060
|
-
|
|
89061
|
-
|
|
89062
|
-
|
|
89063
|
-
|
|
89064
|
-
|
|
89065
|
-
|
|
89066
|
-
|
|
89067
|
-
|
|
89068
|
-
|
|
89069
|
-
|
|
89070
|
-
|
|
89071
|
-
|
|
89072
|
-
|
|
89073
|
-
|
|
89074
|
-
|
|
89075
|
-
|
|
89076
|
-
|
|
89077
|
-
|
|
89078
|
-
|
|
89079
|
-
|
|
89080
|
-
|
|
89081
|
-
|
|
89082
|
-
|
|
89083
|
-
|
|
89084
|
-
|
|
89085
|
-
|
|
89086
|
-
//
|
|
89087
|
-
|
|
89088
|
-
|
|
89089
|
-
|
|
89090
|
-
|
|
89091
|
-
|
|
89092
|
-
|
|
89093
|
-
|
|
89094
|
-
|
|
89095
|
-
|
|
89096
|
-
|
|
89097
|
-
|
|
89098
|
-
|
|
89099
|
-
|
|
89100
|
-
|
|
89101
|
-
|
|
89102
|
-
|
|
89103
|
-
|
|
89104
|
-
|
|
89105
|
-
|
|
89292
|
+
return /* wgsl */ `
|
|
89293
|
+
${useF16 ? 'enable f16;' : ''}
|
|
89294
|
+
|
|
89295
|
+
struct Uniforms {
|
|
89296
|
+
numPoints: u32,
|
|
89297
|
+
numCentroids: u32
|
|
89298
|
+
};
|
|
89299
|
+
|
|
89300
|
+
@group(0) @binding(0) var<uniform> uniforms: Uniforms;
|
|
89301
|
+
@group(0) @binding(1) var<storage, read> points: array<${floatType}>;
|
|
89302
|
+
@group(0) @binding(2) var<storage, read> centroids: array<${floatType}>;
|
|
89303
|
+
@group(0) @binding(3) var<storage, read_write> results: array<u32>;
|
|
89304
|
+
|
|
89305
|
+
const numColumns = ${numColumns}; // number of columns in the points and centroids tables
|
|
89306
|
+
const chunkSize = 128u; // must be a multiple of 64
|
|
89307
|
+
var<workgroup> sharedChunk: array<${floatType}, numColumns * chunkSize>;
|
|
89308
|
+
|
|
89309
|
+
// calculate the squared distance between the point and centroid
|
|
89310
|
+
fn calcDistanceSqr(point: array<${floatType}, numColumns>, centroid: u32) -> f32 {
|
|
89311
|
+
var result = 0.0;
|
|
89312
|
+
|
|
89313
|
+
var ci = centroid * numColumns;
|
|
89314
|
+
|
|
89315
|
+
for (var i = 0u; i < numColumns; i++) {
|
|
89316
|
+
let v = f32(point[i] - sharedChunk[ci+i]);
|
|
89317
|
+
result += v * v;
|
|
89318
|
+
}
|
|
89319
|
+
|
|
89320
|
+
return result;
|
|
89321
|
+
}
|
|
89322
|
+
|
|
89323
|
+
@compute @workgroup_size(64)
|
|
89324
|
+
fn main(
|
|
89325
|
+
@builtin(local_invocation_index) local_id : u32,
|
|
89326
|
+
@builtin(global_invocation_id) global_id: vec3u,
|
|
89327
|
+
@builtin(num_workgroups) num_workgroups: vec3u
|
|
89328
|
+
) {
|
|
89329
|
+
// calculate row index for this thread point
|
|
89330
|
+
let pointIndex = global_id.x + global_id.y * num_workgroups.x * 64u;
|
|
89331
|
+
|
|
89332
|
+
// copy the point data from global memory
|
|
89333
|
+
var point: array<${floatType}, numColumns>;
|
|
89334
|
+
if (pointIndex < uniforms.numPoints) {
|
|
89335
|
+
for (var i = 0u; i < numColumns; i++) {
|
|
89336
|
+
point[i] = points[pointIndex * numColumns + i];
|
|
89337
|
+
}
|
|
89338
|
+
}
|
|
89339
|
+
|
|
89340
|
+
var mind = 1000000.0;
|
|
89341
|
+
var mini = 0u;
|
|
89342
|
+
|
|
89343
|
+
// work through the list of centroids in shared memory chunks
|
|
89344
|
+
let numChunks = u32(ceil(f32(uniforms.numCentroids) / f32(chunkSize)));
|
|
89345
|
+
for (var i = 0u; i < numChunks; i++) {
|
|
89346
|
+
|
|
89347
|
+
// copy this thread's slice of the centroid shared chunk data
|
|
89348
|
+
let dstRow = local_id * (chunkSize / 64u);
|
|
89349
|
+
let srcRow = min(uniforms.numCentroids, i * chunkSize + local_id * chunkSize / 64u);
|
|
89350
|
+
let numRows = min(uniforms.numCentroids, srcRow + chunkSize / 64u) - srcRow;
|
|
89351
|
+
|
|
89352
|
+
var dst = dstRow * numColumns;
|
|
89353
|
+
var src = srcRow * numColumns;
|
|
89354
|
+
|
|
89355
|
+
for (var c = 0u; c < numRows * numColumns; c++) {
|
|
89356
|
+
sharedChunk[dst + c] = centroids[src + c];
|
|
89357
|
+
}
|
|
89358
|
+
|
|
89359
|
+
// wait for all threads to finish writing their part of centroids shared memory buffer
|
|
89360
|
+
workgroupBarrier();
|
|
89361
|
+
|
|
89362
|
+
// loop over the next chunk of centroids finding the closest
|
|
89363
|
+
if (pointIndex < uniforms.numPoints) {
|
|
89364
|
+
let thisChunkSize = min(chunkSize, uniforms.numCentroids - i * chunkSize);
|
|
89365
|
+
for (var c = 0u; c < thisChunkSize; c++) {
|
|
89366
|
+
let d = calcDistanceSqr(point, c);
|
|
89367
|
+
if (d < mind) {
|
|
89368
|
+
mind = d;
|
|
89369
|
+
mini = i * chunkSize + c;
|
|
89370
|
+
}
|
|
89371
|
+
}
|
|
89372
|
+
}
|
|
89373
|
+
|
|
89374
|
+
// next loop will overwrite the shared memory, so wait
|
|
89375
|
+
workgroupBarrier();
|
|
89376
|
+
}
|
|
89377
|
+
|
|
89378
|
+
if (pointIndex < uniforms.numPoints) {
|
|
89379
|
+
results[pointIndex] = mini;
|
|
89380
|
+
}
|
|
89381
|
+
}
|
|
89106
89382
|
`;
|
|
89107
89383
|
};
|
|
89108
89384
|
const roundUp = (value, multiple) => {
|
|
89109
89385
|
return Math.ceil(value / multiple) * multiple;
|
|
89110
89386
|
};
|
|
89111
|
-
const interleaveData = (dataTable,
|
|
89112
|
-
const {
|
|
89113
|
-
if (
|
|
89114
|
-
|
|
89387
|
+
const interleaveData = (result, dataTable, numRows, rowOffset) => {
|
|
89388
|
+
const { numColumns } = dataTable;
|
|
89389
|
+
if (result instanceof Uint16Array) {
|
|
89390
|
+
// interleave shorts
|
|
89115
89391
|
for (let c = 0; c < numColumns; ++c) {
|
|
89116
89392
|
const column = dataTable.columns[c];
|
|
89117
89393
|
for (let r = 0; r < numRows; ++r) {
|
|
89118
|
-
result[r * numColumns + c] = FloatPacking.float2Half(column.data[r]);
|
|
89394
|
+
result[r * numColumns + c] = FloatPacking.float2Half(column.data[rowOffset + r]);
|
|
89119
89395
|
}
|
|
89120
89396
|
}
|
|
89121
|
-
return result;
|
|
89122
89397
|
}
|
|
89123
89398
|
else {
|
|
89124
|
-
|
|
89399
|
+
// interleave floats
|
|
89125
89400
|
for (let c = 0; c < numColumns; ++c) {
|
|
89126
89401
|
const column = dataTable.columns[c];
|
|
89127
89402
|
for (let r = 0; r < numRows; ++r) {
|
|
89128
|
-
result[r * numColumns + c] = column.data[r];
|
|
89403
|
+
result[r * numColumns + c] = column.data[rowOffset + r];
|
|
89129
89404
|
}
|
|
89130
89405
|
}
|
|
89131
|
-
return result;
|
|
89132
89406
|
}
|
|
89133
89407
|
};
|
|
89134
|
-
class
|
|
89408
|
+
class GpuClustering {
|
|
89135
89409
|
execute;
|
|
89136
89410
|
destroy;
|
|
89137
|
-
constructor(gpuDevice,
|
|
89411
|
+
constructor(gpuDevice, numColumns, numCentroids) {
|
|
89138
89412
|
const device = gpuDevice.app.graphicsDevice;
|
|
89139
89413
|
// Check if device supports f16
|
|
89140
89414
|
const useF16 = !!('supportsShaderF16' in device && device.supportsShaderF16);
|
|
89141
|
-
const
|
|
89415
|
+
const workgroupSize = 64;
|
|
89416
|
+
const workgroupsPerBatch = 1024;
|
|
89417
|
+
const batchSize = workgroupsPerBatch * workgroupSize;
|
|
89142
89418
|
const bindGroupFormat = new BindGroupFormat(device, [
|
|
89419
|
+
new BindUniformBufferFormat('uniforms', SHADERSTAGE_COMPUTE),
|
|
89143
89420
|
new BindStorageBufferFormat('pointsBuffer', SHADERSTAGE_COMPUTE, true),
|
|
89144
89421
|
new BindStorageBufferFormat('centroidsBuffer', SHADERSTAGE_COMPUTE, true),
|
|
89145
89422
|
new BindStorageBufferFormat('resultsBuffer', SHADERSTAGE_COMPUTE)
|
|
89146
89423
|
]);
|
|
89147
|
-
const numPoints = points.numRows;
|
|
89148
|
-
const numColumns = points.numColumns;
|
|
89149
89424
|
const shader = new Shader(device, {
|
|
89150
89425
|
name: 'compute-cluster',
|
|
89151
89426
|
shaderLanguage: SHADERLANGUAGE_WGSL,
|
|
89152
|
-
cshader: clusterWgsl(numColumns,
|
|
89427
|
+
cshader: clusterWgsl(numColumns, useF16),
|
|
89428
|
+
// @ts-ignore
|
|
89429
|
+
computeUniformBufferFormats: {
|
|
89430
|
+
uniforms: new UniformBufferFormat(device, [
|
|
89431
|
+
new UniformFormat('numPoints', UNIFORMTYPE_UINT),
|
|
89432
|
+
new UniformFormat('numCentroids', UNIFORMTYPE_UINT)
|
|
89433
|
+
])
|
|
89434
|
+
},
|
|
89153
89435
|
// @ts-ignore
|
|
89154
89436
|
computeBindGroupFormat: bindGroupFormat
|
|
89155
89437
|
});
|
|
89438
|
+
const interleavedPoints = useF16 ? new Uint16Array(roundUp(numColumns * batchSize, 2)) : new Float32Array(numColumns * batchSize);
|
|
89439
|
+
const interleavedCentroids = useF16 ? new Uint16Array(roundUp(numColumns * numCentroids, 2)) : new Float32Array(numColumns * numCentroids);
|
|
89440
|
+
const resultsData = new Uint32Array(batchSize);
|
|
89441
|
+
const pointsBuffer = new StorageBuffer(device, interleavedPoints.byteLength, BUFFERUSAGE_COPY_DST);
|
|
89442
|
+
const centroidsBuffer = new StorageBuffer(device, interleavedCentroids.byteLength, BUFFERUSAGE_COPY_DST);
|
|
89443
|
+
const resultsBuffer = new StorageBuffer(device, resultsData.byteLength, BUFFERUSAGE_COPY_SRC | BUFFERUSAGE_COPY_DST);
|
|
89156
89444
|
const compute = new Compute(device, shader, 'compute-cluster');
|
|
89157
|
-
const pointsBuffer = new StorageBuffer(device, useF16 ? roundUp(numColumns * numPoints, 2) * 2 : numColumns * numPoints * 4, BUFFERUSAGE_COPY_DST);
|
|
89158
|
-
const centroidsBuffer = new StorageBuffer(device, numColumns * numCentroids * bytesPerFloat, BUFFERUSAGE_COPY_DST);
|
|
89159
|
-
const resultsBuffer = new StorageBuffer(device, numPoints * 4, BUFFERUSAGE_COPY_SRC | BUFFERUSAGE_COPY_DST);
|
|
89160
|
-
// interleave the points table data and write to gpu
|
|
89161
|
-
const interleavedPoints = interleaveData(points, useF16);
|
|
89162
|
-
pointsBuffer.write(0, interleavedPoints, 0, interleavedPoints.length);
|
|
89163
|
-
compute.setParameter('columns', numColumns);
|
|
89164
|
-
compute.setParameter('points', numPoints);
|
|
89165
|
-
compute.setParameter('centroids', numCentroids);
|
|
89166
89445
|
compute.setParameter('pointsBuffer', pointsBuffer);
|
|
89167
89446
|
compute.setParameter('centroidsBuffer', centroidsBuffer);
|
|
89168
89447
|
compute.setParameter('resultsBuffer', resultsBuffer);
|
|
89169
|
-
this.execute = async (centroids, labels) => {
|
|
89170
|
-
|
|
89171
|
-
const
|
|
89448
|
+
this.execute = async (points, centroids, labels) => {
|
|
89449
|
+
const numPoints = points.numRows;
|
|
89450
|
+
const numBatches = Math.ceil(numPoints / batchSize);
|
|
89451
|
+
// upload centroid data to gpu
|
|
89452
|
+
interleaveData(interleavedCentroids, centroids, numCentroids, 0);
|
|
89172
89453
|
centroidsBuffer.write(0, interleavedCentroids, 0, interleavedCentroids.length);
|
|
89173
|
-
|
|
89174
|
-
|
|
89175
|
-
|
|
89176
|
-
|
|
89177
|
-
|
|
89178
|
-
|
|
89179
|
-
|
|
89180
|
-
|
|
89181
|
-
|
|
89182
|
-
|
|
89183
|
-
|
|
89454
|
+
compute.setParameter('numCentroids', numCentroids);
|
|
89455
|
+
for (let batch = 0; batch < numBatches; batch++) {
|
|
89456
|
+
const currentBatchSize = Math.min(numPoints - batch * batchSize, batchSize);
|
|
89457
|
+
const groups = Math.ceil(currentBatchSize / 64);
|
|
89458
|
+
// write this batch of point data to gpu
|
|
89459
|
+
interleaveData(interleavedPoints, points, currentBatchSize, batch * batchSize);
|
|
89460
|
+
pointsBuffer.write(0, interleavedPoints, 0, useF16 ? roundUp(numColumns * currentBatchSize, 2) : numColumns * currentBatchSize);
|
|
89461
|
+
compute.setParameter('numPoints', currentBatchSize);
|
|
89462
|
+
// start compute job
|
|
89463
|
+
compute.setupDispatch(groups);
|
|
89464
|
+
device.computeDispatch([compute], `cluster-dispatch-${batch}`);
|
|
89465
|
+
// read results from gpu and store in labels
|
|
89466
|
+
await resultsBuffer.read(0, currentBatchSize * 4, resultsData, true);
|
|
89467
|
+
labels.set(resultsData.subarray(0, currentBatchSize), batch * batchSize);
|
|
89468
|
+
}
|
|
89184
89469
|
};
|
|
89185
89470
|
this.destroy = () => {
|
|
89186
89471
|
pointsBuffer.destroy();
|
|
@@ -89260,14 +89545,14 @@ const kmeans = async (points, k, iterations, device) => {
|
|
|
89260
89545
|
// construct centroids data table and assign initial values
|
|
89261
89546
|
const centroids = new DataTable(points.columns.map(c => new Column(c.name, new Float32Array(k))));
|
|
89262
89547
|
initializeCentroids(points, centroids, row);
|
|
89263
|
-
const
|
|
89548
|
+
const gpuClustering = device && new GpuClustering(device, points.numColumns, k);
|
|
89264
89549
|
const labels = new Uint32Array(points.numRows);
|
|
89265
89550
|
let converged = false;
|
|
89266
89551
|
let steps = 0;
|
|
89267
89552
|
console.log(`Running k-means clustering: dims=${points.numColumns} points=${points.numRows} clusters=${k} iterations=${iterations}...`);
|
|
89268
89553
|
while (!converged) {
|
|
89269
|
-
if (
|
|
89270
|
-
await
|
|
89554
|
+
if (gpuClustering) {
|
|
89555
|
+
await gpuClustering.execute(points, centroids, labels);
|
|
89271
89556
|
}
|
|
89272
89557
|
else {
|
|
89273
89558
|
clusterKdTreeCpu(points, centroids, labels);
|
|
@@ -89284,6 +89569,9 @@ const kmeans = async (points, k, iterations, device) => {
|
|
|
89284
89569
|
}
|
|
89285
89570
|
stdout.write('#');
|
|
89286
89571
|
}
|
|
89572
|
+
if (gpuClustering) {
|
|
89573
|
+
gpuClustering.destroy();
|
|
89574
|
+
}
|
|
89287
89575
|
console.log(' done 🎉');
|
|
89288
89576
|
return { centroids, labels };
|
|
89289
89577
|
};
|
|
@@ -89773,46 +90061,46 @@ const parseArguments = () => {
|
|
|
89773
90061
|
}
|
|
89774
90062
|
return { files, options };
|
|
89775
90063
|
};
|
|
89776
|
-
const usage = `
|
|
89777
|
-
Apply geometric transforms & filters to Gaussian-splat point clouds
|
|
89778
|
-
===================================================================
|
|
89779
|
-
|
|
89780
|
-
USAGE
|
|
89781
|
-
splat-transform [GLOBAL] <input.{ply|splat|ksplat}> [ACTIONS] ... <output.{ply|compressed.ply|meta.json|csv}> [ACTIONS]
|
|
89782
|
-
|
|
89783
|
-
• Every time an input file appears, it becomes the current working set; the following
|
|
89784
|
-
ACTIONS are applied in the order listed.
|
|
89785
|
-
• The last file on the command line is treated as the output; anything after it is
|
|
89786
|
-
interpreted as actions that modify the final result.
|
|
89787
|
-
|
|
89788
|
-
SUPPORTED INPUTS
|
|
89789
|
-
.ply .splat .ksplat
|
|
89790
|
-
|
|
89791
|
-
SUPPORTED OUTPUTS
|
|
89792
|
-
.ply .compressed.ply meta.json (SOGS) .csv
|
|
89793
|
-
|
|
89794
|
-
ACTIONS (can be repeated, in any order)
|
|
89795
|
-
-t, --translate x,y,z Translate splats by (x, y, z)
|
|
89796
|
-
-r, --rotate x,y,z Rotate splats by Euler angles (deg)
|
|
89797
|
-
-s, --scale x Uniformly scale splats by factor x
|
|
89798
|
-
-n, --filterNaN Remove any Gaussian containing NaN/Inf
|
|
89799
|
-
-c, --filterByValue name,cmp,value Keep splats where <name> <cmp> <value>
|
|
89800
|
-
cmp ∈ {lt,lte,gt,gte,eq,neq}
|
|
89801
|
-
-b, --filterBands {0|1|2|3} Strip spherical-harmonic bands > N
|
|
89802
|
-
|
|
89803
|
-
GLOBAL OPTIONS
|
|
89804
|
-
-w, --overwrite Overwrite output file if it already exists. Default is false.
|
|
89805
|
-
-h, --help Show this help and exit.
|
|
89806
|
-
-v, --version Show version and exit.
|
|
89807
|
-
-g, --no-gpu Disable gpu when compressing spherical harmonics.
|
|
89808
|
-
-i, --iterations <number> Specify the number of iterations when compressing spherical harmonics. More iterations generally lead to better results. Default is 10.
|
|
89809
|
-
|
|
89810
|
-
EXAMPLES
|
|
89811
|
-
# Simple scale-then-translate
|
|
89812
|
-
splat-transform bunny.ply -s 0.5 -t 0,0,10 bunny_scaled.ply
|
|
89813
|
-
|
|
89814
|
-
# Chain two inputs and write compressed output, overwriting if necessary
|
|
89815
|
-
splat-transform -w cloudA.ply -r 0,90,0 cloudB.ply -s 2 merged.compressed.ply
|
|
90064
|
+
const usage = `
|
|
90065
|
+
Apply geometric transforms & filters to Gaussian-splat point clouds
|
|
90066
|
+
===================================================================
|
|
90067
|
+
|
|
90068
|
+
USAGE
|
|
90069
|
+
splat-transform [GLOBAL] <input.{ply|splat|ksplat}> [ACTIONS] ... <output.{ply|compressed.ply|meta.json|csv}> [ACTIONS]
|
|
90070
|
+
|
|
90071
|
+
• Every time an input file appears, it becomes the current working set; the following
|
|
90072
|
+
ACTIONS are applied in the order listed.
|
|
90073
|
+
• The last file on the command line is treated as the output; anything after it is
|
|
90074
|
+
interpreted as actions that modify the final result.
|
|
90075
|
+
|
|
90076
|
+
SUPPORTED INPUTS
|
|
90077
|
+
.ply .splat .ksplat
|
|
90078
|
+
|
|
90079
|
+
SUPPORTED OUTPUTS
|
|
90080
|
+
.ply .compressed.ply meta.json (SOGS) .csv
|
|
90081
|
+
|
|
90082
|
+
ACTIONS (can be repeated, in any order)
|
|
90083
|
+
-t, --translate x,y,z Translate splats by (x, y, z)
|
|
90084
|
+
-r, --rotate x,y,z Rotate splats by Euler angles (deg)
|
|
90085
|
+
-s, --scale x Uniformly scale splats by factor x
|
|
90086
|
+
-n, --filterNaN Remove any Gaussian containing NaN/Inf
|
|
90087
|
+
-c, --filterByValue name,cmp,value Keep splats where <name> <cmp> <value>
|
|
90088
|
+
cmp ∈ {lt,lte,gt,gte,eq,neq}
|
|
90089
|
+
-b, --filterBands {0|1|2|3} Strip spherical-harmonic bands > N
|
|
90090
|
+
|
|
90091
|
+
GLOBAL OPTIONS
|
|
90092
|
+
-w, --overwrite Overwrite output file if it already exists. Default is false.
|
|
90093
|
+
-h, --help Show this help and exit.
|
|
90094
|
+
-v, --version Show version and exit.
|
|
90095
|
+
-g, --no-gpu Disable gpu when compressing spherical harmonics.
|
|
90096
|
+
-i, --iterations <number> Specify the number of iterations when compressing spherical harmonics. More iterations generally lead to better results. Default is 10.
|
|
90097
|
+
|
|
90098
|
+
EXAMPLES
|
|
90099
|
+
# Simple scale-then-translate
|
|
90100
|
+
splat-transform bunny.ply -s 0.5 -t 0,0,10 bunny_scaled.ply
|
|
90101
|
+
|
|
90102
|
+
# Chain two inputs and write compressed output, overwriting if necessary
|
|
90103
|
+
splat-transform -w cloudA.ply -r 0,90,0 cloudB.ply -s 2 merged.compressed.ply
|
|
89816
90104
|
`;
|
|
89817
90105
|
const main = async () => {
|
|
89818
90106
|
console.log(`splat-transform v${version$1}`);
|