ice3 0.2.0 → 0.2.1
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/dist/_types/assets/ICEPreparedGLB.d.ts +1 -0
- package/dist/_types/assets/ICEPreviewGeometryPacket.d.ts +60 -0
- package/dist/_types/assets/index.d.ts +2 -0
- package/dist/_types/core/camera/ICEPerspectiveCamera.d.ts +3 -2
- package/dist/_types/core/geometry/ICEGeometry.d.ts +13 -0
- package/dist/_types/core/geometry/ICEIndexedGeometry.d.ts +3 -1
- package/dist/_types/core/index.d.ts +1 -1
- package/dist/_types/core/material/ICEBasicMaterial.d.ts +2 -0
- package/dist/_types/renderer/WebGL2GeometryCache.d.ts +16 -2
- package/dist/_types/renderer/WebGL2Program.d.ts +14 -0
- package/dist/_types/renderer/WebGL2RendererBackend.d.ts +4 -0
- package/dist/_types/renderer/WebGL2ShadowMap.d.ts +26 -0
- package/dist/_types/renderer/WebGPUDrawStorage.d.ts +1 -0
- package/dist/_types/renderer/WebGPUGeometryCache.d.ts +16 -2
- package/dist/_types/renderer/WebGPURenderPipeline.d.ts +1 -1
- package/dist/_types/renderer/WebGPURendererBackend.d.ts +2 -0
- package/dist/_types/renderer/types.d.ts +3 -1
- package/dist/_types/runtime/ICE3.d.ts +2 -0
- package/dist/_types/runtime/asset/ICEAssetSceneBuilder.d.ts +8 -2
- package/dist/_types/runtime/asset/ICEPreviewPacketLoader.d.ts +25 -0
- package/dist/_types/runtime/asset/ICETilesetLoader.d.ts +0 -1
- package/dist/_types/runtime/index.d.ts +2 -1
- package/dist/_types/runtime/input/ICEOrbitController.d.ts +2 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1620 -229
- package/dist/index.js.map +4 -4
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -315,6 +315,7 @@ var ICEIndexedGeometry = class {
|
|
|
315
315
|
edgeIndices;
|
|
316
316
|
colors;
|
|
317
317
|
semanticIndices;
|
|
318
|
+
identity;
|
|
318
319
|
constructor(options) {
|
|
319
320
|
if (!options.id.trim()) {
|
|
320
321
|
throw new TypeError("ICE geometry ID must not be empty");
|
|
@@ -343,6 +344,11 @@ var ICEIndexedGeometry = class {
|
|
|
343
344
|
throw new TypeError("ICE semantic index count must match the vertex count");
|
|
344
345
|
}
|
|
345
346
|
this.id = options.id;
|
|
347
|
+
this.identity = Object.freeze({
|
|
348
|
+
geometryId: options.identity?.geometryId ?? options.id,
|
|
349
|
+
topologyDigest: options.identity?.topologyDigest ?? "",
|
|
350
|
+
attributeDigest: options.identity?.attributeDigest ?? ""
|
|
351
|
+
});
|
|
346
352
|
this.positions = options.positions;
|
|
347
353
|
this.indices = options.indices;
|
|
348
354
|
if (options.edgeIndices) {
|
|
@@ -376,6 +382,7 @@ var ICEIndexedGeometry = class {
|
|
|
376
382
|
edgeIndexCount: this.edgeIndices?.length ?? 0,
|
|
377
383
|
colorCount: this.colors ? this.positions.length / 3 : 0,
|
|
378
384
|
...this.semanticIndices ? { semanticIndexCount: this.semanticIndices.length } : {},
|
|
385
|
+
...this.identity.topologyDigest || this.identity.attributeDigest ? { identity: this.identity } : {},
|
|
379
386
|
bounds: this.getBounds()
|
|
380
387
|
};
|
|
381
388
|
}
|
|
@@ -386,15 +393,18 @@ var ICEBasicMaterial = class {
|
|
|
386
393
|
typeId = "ice3:BasicMaterial";
|
|
387
394
|
color;
|
|
388
395
|
edgeColor;
|
|
396
|
+
shading;
|
|
389
397
|
constructor(options) {
|
|
390
398
|
this.color = options.color;
|
|
391
399
|
this.edgeColor = options.edgeColor;
|
|
400
|
+
this.shading = options.shading;
|
|
392
401
|
}
|
|
393
402
|
toJSON() {
|
|
394
403
|
return {
|
|
395
404
|
typeId: this.typeId,
|
|
396
405
|
color: this.color,
|
|
397
|
-
...this.edgeColor ? { edgeColor: this.edgeColor } : {}
|
|
406
|
+
...this.edgeColor ? { edgeColor: this.edgeColor } : {},
|
|
407
|
+
...this.shading ? { shading: this.shading } : {}
|
|
398
408
|
};
|
|
399
409
|
}
|
|
400
410
|
};
|
|
@@ -426,6 +436,14 @@ var ICEPerspectiveCamera = class {
|
|
|
426
436
|
this.lookTarget = new ICEVector3(...target);
|
|
427
437
|
return this;
|
|
428
438
|
}
|
|
439
|
+
setClipPlanes(near, far) {
|
|
440
|
+
if (!Number.isFinite(near) || !Number.isFinite(far) || near <= 0 || far <= near) {
|
|
441
|
+
throw new RangeError("Camera near/far values are invalid");
|
|
442
|
+
}
|
|
443
|
+
this.near = near;
|
|
444
|
+
this.far = far;
|
|
445
|
+
return this;
|
|
446
|
+
}
|
|
429
447
|
getPosition() {
|
|
430
448
|
return this.eye;
|
|
431
449
|
}
|
|
@@ -765,6 +783,7 @@ var ICEMesh = class _ICEMesh extends ICENode {
|
|
|
765
783
|
static fromJSON(value) {
|
|
766
784
|
const geometry = value.geometry;
|
|
767
785
|
const material = value.material;
|
|
786
|
+
const shading = material.shading === "directional" || material.shading === "flat" || material.shading === "water" ? material.shading : void 0;
|
|
768
787
|
return new _ICEMesh({
|
|
769
788
|
id: value.id,
|
|
770
789
|
displayName: typeof value.displayName === "string" ? value.displayName : value.id,
|
|
@@ -781,7 +800,8 @@ var ICEMesh = class _ICEMesh extends ICENode {
|
|
|
781
800
|
}),
|
|
782
801
|
material: new ICEBasicMaterial({
|
|
783
802
|
color: String(material.color),
|
|
784
|
-
...typeof material.edgeColor === "string" ? { edgeColor: material.edgeColor } : {}
|
|
803
|
+
...typeof material.edgeColor === "string" ? { edgeColor: material.edgeColor } : {},
|
|
804
|
+
...shading ? { shading } : {}
|
|
785
805
|
})
|
|
786
806
|
});
|
|
787
807
|
}
|
|
@@ -909,17 +929,26 @@ function isRenderBatch(node) {
|
|
|
909
929
|
}
|
|
910
930
|
|
|
911
931
|
// packages/renderer/dist/WebGL2Program.js
|
|
912
|
-
var
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
932
|
+
var LIGHT_DIRECTION = [-0.35, 0.85, 0.4];
|
|
933
|
+
var LIGHTING = Object.freeze({
|
|
934
|
+
ambient: 0.72,
|
|
935
|
+
direct: 0.72,
|
|
936
|
+
sky: 0.18,
|
|
937
|
+
minimum: 0.92,
|
|
938
|
+
maximum: 1.42
|
|
939
|
+
});
|
|
940
|
+
var VERTEX_SOURCE = `#version 300 es
|
|
941
|
+
in vec3 a_position;
|
|
942
|
+
in vec3 a_color;
|
|
943
|
+
in vec4 a_instanceColumn0;
|
|
944
|
+
in vec4 a_instanceColumn1;
|
|
945
|
+
in vec4 a_instanceColumn2;
|
|
946
|
+
in vec4 a_instanceColumn3;
|
|
919
947
|
uniform mat4 u_worldViewProjection;
|
|
920
948
|
uniform vec2 u_clipOffset;
|
|
921
949
|
uniform float u_useInstancing;
|
|
922
|
-
|
|
950
|
+
out vec3 v_color;
|
|
951
|
+
out vec3 v_worldPosition;
|
|
923
952
|
|
|
924
953
|
void main() {
|
|
925
954
|
mat4 instanceMatrix = mat4(
|
|
@@ -937,17 +966,122 @@ void main() {
|
|
|
937
966
|
gl_Position = u_worldViewProjection * position;
|
|
938
967
|
gl_Position.xy += u_clipOffset * gl_Position.w;
|
|
939
968
|
v_color = a_color;
|
|
969
|
+
v_worldPosition = position.xyz;
|
|
940
970
|
}
|
|
941
971
|
`;
|
|
942
|
-
var FRAGMENT_SOURCE =
|
|
972
|
+
var FRAGMENT_SOURCE = `#version 300 es
|
|
943
973
|
precision highp float;
|
|
944
974
|
uniform vec4 u_color;
|
|
975
|
+
uniform float u_pickMode;
|
|
945
976
|
uniform float u_useVertexColor;
|
|
946
|
-
|
|
977
|
+
uniform float u_useDirectionalShading;
|
|
978
|
+
uniform float u_waterShading;
|
|
979
|
+
uniform vec3 u_cameraPosition;
|
|
980
|
+
uniform sampler2D u_shadowMap;
|
|
981
|
+
uniform mat4 u_lightViewProjection;
|
|
982
|
+
uniform float u_shadowEnabled;
|
|
983
|
+
in vec3 v_color;
|
|
984
|
+
in vec3 v_worldPosition;
|
|
985
|
+
out vec4 outputColor;
|
|
947
986
|
|
|
948
987
|
void main() {
|
|
949
|
-
|
|
950
|
-
|
|
988
|
+
if (u_pickMode > 0.5) {
|
|
989
|
+
outputColor = u_color;
|
|
990
|
+
return;
|
|
991
|
+
}
|
|
992
|
+
vec3 color = u_color.rgb
|
|
993
|
+
* mix(vec3(1.0), v_color, step(0.5, u_useVertexColor));
|
|
994
|
+
vec3 normal = normalize(cross(
|
|
995
|
+
dFdx(v_worldPosition),
|
|
996
|
+
dFdy(v_worldPosition)
|
|
997
|
+
));
|
|
998
|
+
// \u4E3B\u5149 + \u534A\u7403\u5929\u5149\u3002\u6CD5\u7EBF\u7531\u5C4F\u5E55\u7A7A\u95F4\u5BFC\u6570\u6C42\u51FA\uFF0C\u671D\u5411\u4E0E\u7ED5\u5E8F\u4E00\u81F4\uFF1A
|
|
999
|
+
// \u8FCE\u7740\u5149\u7684\u4E00\u9762\u5403\u6EE1\u76F4\u5C04\uFF0C\u80CC\u5149\u9762\u53EA\u7559 35% \u7684\u53CD\u5F39\u5149\uFF0C\u6697\u90E8\u4E0D\u81F3\u4E8E\u7CCA\u6210\u4E00\u7247\u3002
|
|
1000
|
+
vec3 lightDirection = normalize(vec3(${LIGHT_DIRECTION.join(", ")}));
|
|
1001
|
+
float ndl = dot(normal, lightDirection);
|
|
1002
|
+
float key = max(ndl, 0.0) + 0.35 * max(-ndl, 0.0);
|
|
1003
|
+
float sky = 0.5 + 0.5 * normal.y;
|
|
1004
|
+
// \u76F4\u5C04\u5149\u5F3A\u5EA6\u6309\u771F\u5B9E\u65E5\u7167\u7ED9\uFF1A\u671D\u4E0A\u7684\u9762\u4F1A\u8D85\u8FC7 1\uFF0C\u8FC7\u66DD\u5230\u63A5\u8FD1\u767D\u8272\u662F\u5BF9\u7684\uFF0C
|
|
1005
|
+
// \u4E4B\u524D\u4E0A\u9650\u538B\u5230 1 \u9644\u8FD1\uFF0C\u6574\u4E2A\u5382\u533A\u5C31\u6C38\u8FDC\u6652\u4E0D\u5230\u592A\u9633\uFF0C\u770B\u7740\u53D1\u95F7\u53D1\u6697\u3002
|
|
1006
|
+
float shade = clamp(
|
|
1007
|
+
${LIGHTING.ambient} + ${LIGHTING.direct} * key + ${LIGHTING.sky} * sky,
|
|
1008
|
+
${LIGHTING.minimum},
|
|
1009
|
+
${LIGHTING.maximum}
|
|
1010
|
+
);
|
|
1011
|
+
vec3 lit = color * mix(1.0, shade, u_useDirectionalShading);
|
|
1012
|
+
|
|
1013
|
+
// \u8868\u9762\u9897\u7C92\uFF1A\u6309\u4E16\u754C\u5750\u6807\u53D6\u4E24\u5C42\u54C8\u5E0C\uFF0C\u6253\u6563\u6574\u7247\u7EAF\u8272\u5E26\u6765\u7684\u5851\u6599\u611F\u3002
|
|
1014
|
+
// \u5E45\u5EA6\u63A7\u5236\u5728 \xB16% \u5185\uFF0C\u8FDC\u4E86\u770B\u4E0D\u51FA\u3001\u8FD1\u4E86\u6709\u8D28\u611F\uFF0C\u4E0D\u4F1A\u55A7\u5BBE\u593A\u4E3B\u3002
|
|
1015
|
+
vec3 fine = floor(v_worldPosition * 4.5);
|
|
1016
|
+
vec3 coarse = floor(v_worldPosition * 0.42);
|
|
1017
|
+
float grain = fract(sin(dot(fine, vec3(12.9898, 78.233, 37.719))) * 43758.5453);
|
|
1018
|
+
float stain = fract(sin(dot(coarse, vec3(39.346, 11.135, 83.155))) * 24634.6345);
|
|
1019
|
+
// \u4F4E\u9891\u6C61\u6E0D\u8D1F\u8D23\u5927\u9762\u79EF\u4E0D\u5300\uFF0C\u9AD8\u9891\u9897\u7C92\u8D1F\u8D23\u8FD1\u770B\u7684\u624B\u611F\u3002
|
|
1020
|
+
lit *= 0.94 + 0.06 * grain + 0.05 * (stain - 0.5);
|
|
1021
|
+
|
|
1022
|
+
// Liquid surfaces use an analytic Fresnel reflection plus shallow ripple
|
|
1023
|
+
// normals. The effect is opt-in through material shading and remains
|
|
1024
|
+
// independent from any application-specific material naming.
|
|
1025
|
+
if (u_waterShading > 0.5) {
|
|
1026
|
+
float waveA = sin(v_worldPosition.x * 1.1
|
|
1027
|
+
+ sin(v_worldPosition.z * 0.43) * 2.0);
|
|
1028
|
+
float waveB = sin(v_worldPosition.z * 1.7
|
|
1029
|
+
+ v_worldPosition.x * 0.31);
|
|
1030
|
+
float waveC = sin((v_worldPosition.x + v_worldPosition.z) * 2.3);
|
|
1031
|
+
vec3 rippleNormal = normalize(vec3(
|
|
1032
|
+
waveA * 0.09 + waveC * 0.025,
|
|
1033
|
+
1.0,
|
|
1034
|
+
waveB * 0.09 - waveC * 0.02
|
|
1035
|
+
));
|
|
1036
|
+
vec3 viewDirection = normalize(u_cameraPosition - v_worldPosition);
|
|
1037
|
+
float facing = clamp(dot(rippleNormal, viewDirection), 0.0, 1.0);
|
|
1038
|
+
float fresnel = pow(1.0 - facing, 2.4);
|
|
1039
|
+
vec3 reflectedSky = vec3(0.22, 0.34, 0.36);
|
|
1040
|
+
vec3 surfaceColor = mix(
|
|
1041
|
+
color,
|
|
1042
|
+
reflectedSky,
|
|
1043
|
+
0.30 + 0.30 * fresnel
|
|
1044
|
+
);
|
|
1045
|
+
surfaceColor *= 0.92 + 0.09 * waveA + 0.05 * waveB;
|
|
1046
|
+
vec3 halfDirection = normalize(lightDirection + viewDirection);
|
|
1047
|
+
float sparkle = pow(
|
|
1048
|
+
max(dot(rippleNormal, halfDirection), 0.0),
|
|
1049
|
+
36.0
|
|
1050
|
+
);
|
|
1051
|
+
lit = mix(lit, surfaceColor * shade, 0.92)
|
|
1052
|
+
+ vec3(0.42, 0.52, 0.54) * sparkle * 0.55;
|
|
1053
|
+
}
|
|
1054
|
+
|
|
1055
|
+
// \u9634\u5F71\uFF1A\u628A\u4E16\u754C\u5750\u6807\u6295\u5230\u5149\u6E90\u7A7A\u95F4\uFF0C\u4E0E\u6DF1\u5EA6\u56FE\u6BD4\u8F83\u3002\u53D6 3\xD73 \u91C7\u6837\u7684\u5E73\u5747\u505A\u8F7B\u5FAE\u67D4\u5316\u3002
|
|
1056
|
+
if (u_shadowEnabled > 0.5) {
|
|
1057
|
+
vec4 lightSpace = u_lightViewProjection * vec4(v_worldPosition, 1.0);
|
|
1058
|
+
vec3 projected = lightSpace.xyz / max(lightSpace.w, 1e-5) * 0.5 + 0.5;
|
|
1059
|
+
if (projected.x > 0.002 && projected.x < 0.998
|
|
1060
|
+
&& projected.y > 0.002 && projected.y < 0.998
|
|
1061
|
+
&& projected.z > 0.0 && projected.z < 1.0) {
|
|
1062
|
+
// \u659C\u9762\u5BF9\u6DF1\u5EA6\u7684\u8BEF\u5DEE\u6700\u654F\u611F\uFF0C\u6309 N\xB7L \u653E\u5927\u504F\u79FB\uFF1A
|
|
1063
|
+
// \u56FA\u5B9A\u5C0F\u504F\u79FB\u5728\u8FD9\u5957 960 \u7C73\u6DF1\u7684\u5149\u9525\u91CC\u7B49\u4E8E\u51E0\u7C73\uFF0C\u5899\u9762\u4F1A\u81EA\u5DF1\u628A\u81EA\u5DF1\u906E\u4F4F\uFF08\u9634\u5F71\u75E4\u75AE\uFF09\u3002
|
|
1064
|
+
float ndlShadow = max(dot(normal, lightDirection), 0.0);
|
|
1065
|
+
// \u504F\u79FB\u6309\u5149\u9525\u6DF1\u5EA6\uFF08\u8FD1 -260 / \u8FDC 700\uFF0C\u5171 960 \u7C73\uFF09\u6362\u7B97\uFF1A
|
|
1066
|
+
// 0.006 \u5BF9\u5E94\u7EA6 5.8 \u7C73\uFF0C\u6B63\u597D\u538B\u6389\u6DF1\u5EA6\u56FE\u4E0E\u9010\u50CF\u7D20\u6295\u5F71\u4E4B\u95F4\u7684\u7CFB\u7EDF\u8BEF\u5DEE\uFF0C
|
|
1067
|
+
// \u540C\u65F6\u8FD8\u80FD\u7559\u4F4F\u5341\u51E0\u7C73\u9AD8\u6784\u7B51\u7269\u7684\u6295\u5F71\u3002
|
|
1068
|
+
float bias = 0.0015 + 0.0025 * (1.0 - ndlShadow);
|
|
1069
|
+
float lit2 = 0.0;
|
|
1070
|
+
vec2 texel = vec2(1.0 / 2048.0);
|
|
1071
|
+
for (int dx = -1; dx <= 1; dx++) {
|
|
1072
|
+
for (int dy = -1; dy <= 1; dy++) {
|
|
1073
|
+
float depth = texture(u_shadowMap, projected.xy + vec2(float(dx), float(dy)) * texel).r;
|
|
1074
|
+
lit2 += projected.z - bias > depth ? 0.0 : 1.0;
|
|
1075
|
+
}
|
|
1076
|
+
}
|
|
1077
|
+
lit *= mix(0.68, 1.0, lit2 / 9.0);
|
|
1078
|
+
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1081
|
+
// \u753B\u5E03\u6309 sRGB \u89E3\u91CA\u5199\u5165\u503C\uFF0C\u800C\u6750\u8D28\u662F\u7EBF\u6027\u7A7A\u95F4\u7684\u5206\u91CF\uFF0C\u5FC5\u987B\u505A\u4E00\u6B21\u7F16\u7801\u3002
|
|
1082
|
+
// \u4E0D\u505A\u7684\u8BDD\u7EBF\u6027 0.017 \u4F1A\u5F53\u4F5C sRGB 0.017 \u663E\u793A\uFF0C\u6DF1\u8272\u6750\u8D28\u5168\u90E8\u584C\u6210\u9ED1\u8272\u3002
|
|
1083
|
+
outputColor = vec4(
|
|
1084
|
+
pow(max(lit, vec3(0.0)), vec3(1.0 / 2.2)),
|
|
951
1085
|
u_color.a
|
|
952
1086
|
);
|
|
953
1087
|
}
|
|
@@ -963,7 +1097,14 @@ var WebGL2Program = class _WebGL2Program {
|
|
|
963
1097
|
colorLocation;
|
|
964
1098
|
clipOffsetLocation;
|
|
965
1099
|
vertexColorLocation;
|
|
1100
|
+
directionalShadingLocation;
|
|
1101
|
+
waterShadingLocation;
|
|
1102
|
+
cameraPositionLocation;
|
|
1103
|
+
shadowMapLocation;
|
|
1104
|
+
lightMatrixLocation;
|
|
1105
|
+
shadowEnabledLocation;
|
|
966
1106
|
instancingLocation;
|
|
1107
|
+
pickModeLocation;
|
|
967
1108
|
constructor(gl) {
|
|
968
1109
|
this.gl = gl;
|
|
969
1110
|
const program = this.createProgram(VERTEX_SOURCE, FRAGMENT_SOURCE);
|
|
@@ -971,8 +1112,15 @@ var WebGL2Program = class _WebGL2Program {
|
|
|
971
1112
|
const colorLocation = gl.getUniformLocation(program, "u_color");
|
|
972
1113
|
const clipOffsetLocation = gl.getUniformLocation(program, "u_clipOffset");
|
|
973
1114
|
const vertexColorLocation = gl.getUniformLocation(program, "u_useVertexColor");
|
|
1115
|
+
const directionalShadingLocation = gl.getUniformLocation(program, "u_useDirectionalShading");
|
|
1116
|
+
const waterShadingLocation = gl.getUniformLocation(program, "u_waterShading");
|
|
1117
|
+
const cameraPositionLocation = gl.getUniformLocation(program, "u_cameraPosition");
|
|
974
1118
|
const instancingLocation = gl.getUniformLocation(program, "u_useInstancing");
|
|
975
|
-
|
|
1119
|
+
const pickModeLocation = gl.getUniformLocation(program, "u_pickMode");
|
|
1120
|
+
const shadowMapLocation = gl.getUniformLocation(program, "u_shadowMap");
|
|
1121
|
+
const lightMatrixLocation = gl.getUniformLocation(program, "u_lightViewProjection");
|
|
1122
|
+
const shadowEnabledLocation = gl.getUniformLocation(program, "u_shadowEnabled");
|
|
1123
|
+
if (!matrixLocation || !colorLocation || !clipOffsetLocation || !vertexColorLocation || !directionalShadingLocation || !waterShadingLocation || !cameraPositionLocation || !instancingLocation || !pickModeLocation) {
|
|
976
1124
|
gl.deleteProgram(program);
|
|
977
1125
|
throw new ICEEngineError("ICE3_SHADER_LINK_FAILED", "ICE3_SHADER_LINK_FAILED: shader uniforms are missing");
|
|
978
1126
|
}
|
|
@@ -981,7 +1129,21 @@ var WebGL2Program = class _WebGL2Program {
|
|
|
981
1129
|
this.colorLocation = colorLocation;
|
|
982
1130
|
this.clipOffsetLocation = clipOffsetLocation;
|
|
983
1131
|
this.vertexColorLocation = vertexColorLocation;
|
|
1132
|
+
this.directionalShadingLocation = directionalShadingLocation;
|
|
1133
|
+
this.waterShadingLocation = waterShadingLocation;
|
|
1134
|
+
this.cameraPositionLocation = cameraPositionLocation;
|
|
1135
|
+
this.shadowMapLocation = shadowMapLocation;
|
|
1136
|
+
this.lightMatrixLocation = lightMatrixLocation;
|
|
1137
|
+
this.shadowEnabledLocation = shadowEnabledLocation;
|
|
1138
|
+
this.shadowMapLocation = shadowMapLocation;
|
|
1139
|
+
this.lightMatrixLocation = lightMatrixLocation;
|
|
1140
|
+
this.shadowEnabledLocation = shadowEnabledLocation;
|
|
984
1141
|
this.instancingLocation = instancingLocation;
|
|
1142
|
+
this.pickModeLocation = pickModeLocation;
|
|
1143
|
+
if (shadowMapLocation) {
|
|
1144
|
+
gl.useProgram(program);
|
|
1145
|
+
gl.uniform1i(shadowMapLocation, 3);
|
|
1146
|
+
}
|
|
985
1147
|
}
|
|
986
1148
|
use() {
|
|
987
1149
|
this.gl.useProgram(this.program);
|
|
@@ -998,9 +1160,35 @@ var WebGL2Program = class _WebGL2Program {
|
|
|
998
1160
|
setVertexColors(enabled) {
|
|
999
1161
|
this.gl.uniform1f(this.vertexColorLocation, enabled ? 1 : 0);
|
|
1000
1162
|
}
|
|
1163
|
+
/** 绑定阴影贴图与光源矩阵;关闭时着色器跳过采样。 */
|
|
1164
|
+
setShadow(enabled, texture, matrix, unit = 3) {
|
|
1165
|
+
const gl = this.gl;
|
|
1166
|
+
gl.uniform1f(this.shadowEnabledLocation, enabled && texture && matrix ? 1 : 0);
|
|
1167
|
+
if (!enabled || !texture || !matrix)
|
|
1168
|
+
return;
|
|
1169
|
+
gl.activeTexture(gl.TEXTURE0 + unit);
|
|
1170
|
+
gl.bindTexture(gl.TEXTURE_2D, texture);
|
|
1171
|
+
if (this.shadowMapLocation)
|
|
1172
|
+
gl.uniform1i(this.shadowMapLocation, unit);
|
|
1173
|
+
if (this.lightMatrixLocation)
|
|
1174
|
+
gl.uniformMatrix4fv(this.lightMatrixLocation, false, matrix);
|
|
1175
|
+
gl.activeTexture(gl.TEXTURE0);
|
|
1176
|
+
}
|
|
1177
|
+
setDirectionalShading(enabled) {
|
|
1178
|
+
this.gl.uniform1f(this.directionalShadingLocation, enabled ? 1 : 0);
|
|
1179
|
+
}
|
|
1180
|
+
setWaterShading(enabled) {
|
|
1181
|
+
this.gl.uniform1f(this.waterShadingLocation, enabled ? 1 : 0);
|
|
1182
|
+
}
|
|
1183
|
+
setCameraPosition(position) {
|
|
1184
|
+
this.gl.uniform3f(this.cameraPositionLocation, ...position);
|
|
1185
|
+
}
|
|
1001
1186
|
setInstancing(enabled) {
|
|
1002
1187
|
this.gl.uniform1f(this.instancingLocation, enabled ? 1 : 0);
|
|
1003
1188
|
}
|
|
1189
|
+
setPickMode(enabled) {
|
|
1190
|
+
this.gl.uniform1f(this.pickModeLocation, enabled ? 1 : 0);
|
|
1191
|
+
}
|
|
1004
1192
|
dispose() {
|
|
1005
1193
|
this.gl.deleteProgram(this.program);
|
|
1006
1194
|
}
|
|
@@ -1077,7 +1265,11 @@ function collectInstanceBatches(renderables, output = []) {
|
|
|
1077
1265
|
semanticIds: group.map((renderable) => renderable.mesh.selectionId),
|
|
1078
1266
|
semanticIndices,
|
|
1079
1267
|
meshIds: group.map((renderable) => renderable.id),
|
|
1080
|
-
materialKey:
|
|
1268
|
+
materialKey: [
|
|
1269
|
+
material.color,
|
|
1270
|
+
material.edgeColor ?? "",
|
|
1271
|
+
material.shading ?? ""
|
|
1272
|
+
].join(":")
|
|
1081
1273
|
});
|
|
1082
1274
|
}
|
|
1083
1275
|
}
|
|
@@ -1199,8 +1391,12 @@ var WebGL2GeometryCache = class {
|
|
|
1199
1391
|
entries = /* @__PURE__ */ new Map();
|
|
1200
1392
|
active = /* @__PURE__ */ new Set();
|
|
1201
1393
|
pinned = /* @__PURE__ */ new Set();
|
|
1394
|
+
objectKeys = /* @__PURE__ */ new WeakMap();
|
|
1395
|
+
legacyKeys = /* @__PURE__ */ new WeakMap();
|
|
1396
|
+
objectKeySequence = 0;
|
|
1202
1397
|
gl = null;
|
|
1203
1398
|
uploads = 0;
|
|
1399
|
+
updatedBytes = 0;
|
|
1204
1400
|
evictions = 0;
|
|
1205
1401
|
frameId = 0;
|
|
1206
1402
|
inactiveGraceFrames;
|
|
@@ -1214,12 +1410,24 @@ var WebGL2GeometryCache = class {
|
|
|
1214
1410
|
}
|
|
1215
1411
|
retain(gl, geometry) {
|
|
1216
1412
|
this.gl = gl;
|
|
1217
|
-
this.
|
|
1218
|
-
|
|
1413
|
+
const key = this.keyFor(geometry);
|
|
1414
|
+
this.active.add(key);
|
|
1415
|
+
const existing = this.entries.get(key);
|
|
1219
1416
|
if (existing) {
|
|
1220
|
-
existing
|
|
1221
|
-
|
|
1417
|
+
if (this.canReuse(existing, geometry)) {
|
|
1418
|
+
existing.lastUsedFrame = this.frameId;
|
|
1419
|
+
return existing;
|
|
1420
|
+
}
|
|
1421
|
+
}
|
|
1422
|
+
const next = this.createEntry(gl, geometry, key);
|
|
1423
|
+
if (existing) {
|
|
1424
|
+
this.deleteEntry(existing, false);
|
|
1222
1425
|
}
|
|
1426
|
+
this.entries.set(key, next);
|
|
1427
|
+
this.uploads += 1;
|
|
1428
|
+
return next;
|
|
1429
|
+
}
|
|
1430
|
+
createEntry(gl, geometry, key) {
|
|
1223
1431
|
const vertexArray = gl.createVertexArray();
|
|
1224
1432
|
const positionBuffer = gl.createBuffer();
|
|
1225
1433
|
const triangleIndexBuffer = gl.createBuffer();
|
|
@@ -1260,6 +1468,10 @@ var WebGL2GeometryCache = class {
|
|
|
1260
1468
|
}
|
|
1261
1469
|
gl.bindVertexArray(null);
|
|
1262
1470
|
const entry = {
|
|
1471
|
+
geometry,
|
|
1472
|
+
geometryId: geometry.identity?.geometryId ?? key,
|
|
1473
|
+
attributeDigest: geometry.identity?.attributeDigest ?? "",
|
|
1474
|
+
topologyDigest: geometry.identity?.topologyDigest ?? "",
|
|
1263
1475
|
vertexArray,
|
|
1264
1476
|
positionBuffer,
|
|
1265
1477
|
triangleIndexBuffer,
|
|
@@ -1271,28 +1483,33 @@ var WebGL2GeometryCache = class {
|
|
|
1271
1483
|
bytes: geometry.positions.byteLength + geometry.indices.byteLength + (geometry.edgeIndices?.byteLength ?? 0) + (geometry.colors?.byteLength ?? 0) + (geometry.semanticIndices?.byteLength ?? 0),
|
|
1272
1484
|
lastUsedFrame: this.frameId
|
|
1273
1485
|
};
|
|
1274
|
-
this.entries.set(geometry, entry);
|
|
1275
|
-
this.uploads += 1;
|
|
1276
1486
|
return entry;
|
|
1277
1487
|
}
|
|
1278
1488
|
beginFrame() {
|
|
1279
1489
|
this.frameId += 1;
|
|
1280
1490
|
this.active = /* @__PURE__ */ new Set();
|
|
1281
1491
|
}
|
|
1282
|
-
pin(gl, geometry) {
|
|
1283
|
-
this.
|
|
1492
|
+
pin(gl, geometry, update) {
|
|
1493
|
+
const key = this.keyFor(geometry);
|
|
1494
|
+
this.pinned.add(key);
|
|
1495
|
+
const existing = this.entries.get(key);
|
|
1496
|
+
if (existing && update && this.sameTopology(existing, geometry)) {
|
|
1497
|
+
this.gl = gl;
|
|
1498
|
+
this.applyPositionUpdate(gl, existing, geometry, update);
|
|
1499
|
+
return existing;
|
|
1500
|
+
}
|
|
1284
1501
|
const entry = this.retain(gl, geometry);
|
|
1285
1502
|
this.enforceBudget();
|
|
1286
1503
|
if (this.totalBytes > this.maxBytes) {
|
|
1287
|
-
this.pinned.delete(
|
|
1288
|
-
this.deleteEntry(entry);
|
|
1289
|
-
this.entries.delete(
|
|
1504
|
+
this.pinned.delete(key);
|
|
1505
|
+
this.deleteEntry(entry, false);
|
|
1506
|
+
this.entries.delete(key);
|
|
1290
1507
|
throw new Error("ICE3_GPU_RESOURCE_FAILED: geometry exceeds the GPU residency budget");
|
|
1291
1508
|
}
|
|
1292
1509
|
return entry;
|
|
1293
1510
|
}
|
|
1294
1511
|
unpin(geometry) {
|
|
1295
|
-
this.pinned.delete(geometry);
|
|
1512
|
+
this.pinned.delete(this.keyFor(geometry));
|
|
1296
1513
|
}
|
|
1297
1514
|
endFrame() {
|
|
1298
1515
|
for (const [geometry, entry] of this.entries) {
|
|
@@ -1327,10 +1544,13 @@ var WebGL2GeometryCache = class {
|
|
|
1327
1544
|
get uploadCount() {
|
|
1328
1545
|
return this.uploads;
|
|
1329
1546
|
}
|
|
1547
|
+
get updateBytes() {
|
|
1548
|
+
return this.updatedBytes;
|
|
1549
|
+
}
|
|
1330
1550
|
get evictionCount() {
|
|
1331
1551
|
return this.evictions;
|
|
1332
1552
|
}
|
|
1333
|
-
deleteEntry(entry) {
|
|
1553
|
+
deleteEntry(entry, eviction = true) {
|
|
1334
1554
|
if (!this.gl)
|
|
1335
1555
|
return;
|
|
1336
1556
|
this.gl.deleteBuffer(entry.positionBuffer);
|
|
@@ -1339,7 +1559,9 @@ var WebGL2GeometryCache = class {
|
|
|
1339
1559
|
this.gl.deleteBuffer(entry.colorBuffer);
|
|
1340
1560
|
this.gl.deleteBuffer(entry.semanticBuffer);
|
|
1341
1561
|
this.gl.deleteVertexArray(entry.vertexArray);
|
|
1342
|
-
|
|
1562
|
+
if (eviction) {
|
|
1563
|
+
this.evictions += 1;
|
|
1564
|
+
}
|
|
1343
1565
|
}
|
|
1344
1566
|
enforceBudget() {
|
|
1345
1567
|
let total = this.totalBytes;
|
|
@@ -1354,7 +1576,96 @@ var WebGL2GeometryCache = class {
|
|
|
1354
1576
|
total -= entry.bytes;
|
|
1355
1577
|
}
|
|
1356
1578
|
}
|
|
1579
|
+
applyPositionUpdate(gl, entry, geometry, update) {
|
|
1580
|
+
let updated = 0;
|
|
1581
|
+
try {
|
|
1582
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, entry.positionBuffer);
|
|
1583
|
+
for (const range of update.positionRanges) {
|
|
1584
|
+
if (range.byteOffset % 4 !== 0 || range.byteLength % 4 !== 0 || range.byteOffset + range.byteLength > geometry.positions.byteLength) {
|
|
1585
|
+
throw new Error("invalid position range");
|
|
1586
|
+
}
|
|
1587
|
+
gl.bufferSubData(gl.ARRAY_BUFFER, range.byteOffset, new Uint8Array(geometry.positions.buffer, geometry.positions.byteOffset + range.byteOffset, range.byteLength));
|
|
1588
|
+
updated += range.byteLength;
|
|
1589
|
+
}
|
|
1590
|
+
} catch (error) {
|
|
1591
|
+
throw new Error("ICE3_GPU_RESOURCE_FAILED: geometry update failed", {
|
|
1592
|
+
cause: error
|
|
1593
|
+
});
|
|
1594
|
+
}
|
|
1595
|
+
entry.geometry = geometry;
|
|
1596
|
+
entry.lastUsedFrame = this.frameId;
|
|
1597
|
+
this.updatedBytes += updated;
|
|
1598
|
+
}
|
|
1599
|
+
canReuse(existing, geometry) {
|
|
1600
|
+
if (existing.geometry === geometry) {
|
|
1601
|
+
return true;
|
|
1602
|
+
}
|
|
1603
|
+
if (!this.sameTopology(existing, geometry)) {
|
|
1604
|
+
return false;
|
|
1605
|
+
}
|
|
1606
|
+
const existingDigest = existing.attributeDigest;
|
|
1607
|
+
const nextDigest = geometry.identity?.attributeDigest ?? "";
|
|
1608
|
+
return (existing.geometry.positions === geometry.positions || equalNumberArrays(existing.geometry.positions, geometry.positions)) && equalOptionalNumberArrays(existing.geometry.colors, geometry.colors) && equalOptionalNumberArrays(existing.geometry.semanticIndices, geometry.semanticIndices) && equalOptionalNumberArrays(existing.geometry.edgeIndices, geometry.edgeIndices) || existingDigest !== "" && existingDigest === nextDigest;
|
|
1609
|
+
}
|
|
1610
|
+
sameTopology(existing, geometry) {
|
|
1611
|
+
const existingDigest = existing.topologyDigest;
|
|
1612
|
+
const nextDigest = geometry.identity?.topologyDigest ?? "";
|
|
1613
|
+
return existing.geometry.indices.constructor === geometry.indices.constructor && existing.geometry.indices.length === geometry.indices.length && (existing.geometry.indices === geometry.indices || existingDigest !== "" && existingDigest === nextDigest || equalTypedArrays(existing.geometry.indices, geometry.indices));
|
|
1614
|
+
}
|
|
1615
|
+
keyFor(geometry) {
|
|
1616
|
+
const identity = geometry.identity?.geometryId;
|
|
1617
|
+
if (identity) {
|
|
1618
|
+
return `identity:${identity}`;
|
|
1619
|
+
}
|
|
1620
|
+
const id = geometry.id;
|
|
1621
|
+
if (typeof id === "string" && id.trim()) {
|
|
1622
|
+
const cached = this.legacyKeys.get(geometry);
|
|
1623
|
+
if (cached)
|
|
1624
|
+
return cached;
|
|
1625
|
+
const key2 = [
|
|
1626
|
+
"content",
|
|
1627
|
+
geometry.positions.length,
|
|
1628
|
+
geometry.indices.length,
|
|
1629
|
+
contentFingerprint(geometry.positions),
|
|
1630
|
+
contentFingerprint(geometry.indices),
|
|
1631
|
+
geometry.colors ? contentFingerprint(geometry.colors) : 0,
|
|
1632
|
+
geometry.edgeIndices ? contentFingerprint(geometry.edgeIndices) : 0,
|
|
1633
|
+
geometry.semanticIndices ? contentFingerprint(geometry.semanticIndices) : 0
|
|
1634
|
+
].join(":");
|
|
1635
|
+
this.legacyKeys.set(geometry, key2);
|
|
1636
|
+
return key2;
|
|
1637
|
+
}
|
|
1638
|
+
const existing = this.objectKeys.get(geometry);
|
|
1639
|
+
if (existing) {
|
|
1640
|
+
return existing;
|
|
1641
|
+
}
|
|
1642
|
+
const key = `object:${this.objectKeySequence}`;
|
|
1643
|
+
this.objectKeySequence += 1;
|
|
1644
|
+
this.objectKeys.set(geometry, key);
|
|
1645
|
+
return key;
|
|
1646
|
+
}
|
|
1357
1647
|
};
|
|
1648
|
+
function equalTypedArrays(left, right) {
|
|
1649
|
+
return left.length === right.length && left.every((value, index) => value === right[index]);
|
|
1650
|
+
}
|
|
1651
|
+
function equalNumberArrays(left, right) {
|
|
1652
|
+
return left.length === right.length && left.every((value, index) => value === right[index]);
|
|
1653
|
+
}
|
|
1654
|
+
function equalOptionalNumberArrays(left, right) {
|
|
1655
|
+
if (left === void 0 || right === void 0) {
|
|
1656
|
+
return left === right;
|
|
1657
|
+
}
|
|
1658
|
+
return left.length === right.length && left.every((value, index) => value === right[index]);
|
|
1659
|
+
}
|
|
1660
|
+
function contentFingerprint(values) {
|
|
1661
|
+
let hash = 2166136261;
|
|
1662
|
+
const bytes = new Uint8Array(values.buffer, values.byteOffset, values.byteLength);
|
|
1663
|
+
for (const byte of bytes) {
|
|
1664
|
+
hash ^= byte;
|
|
1665
|
+
hash = Math.imul(hash, 16777619) >>> 0;
|
|
1666
|
+
}
|
|
1667
|
+
return hash;
|
|
1668
|
+
}
|
|
1358
1669
|
|
|
1359
1670
|
// packages/renderer/dist/WebGL2PickCodec.js
|
|
1360
1671
|
var MAX_SELECTION_INDEX = 16777215;
|
|
@@ -1561,6 +1872,7 @@ var WebGL2Picker = class {
|
|
|
1561
1872
|
gl.depthFunc(gl.LEQUAL);
|
|
1562
1873
|
gl.disable(gl.BLEND);
|
|
1563
1874
|
this.program.use();
|
|
1875
|
+
this.program.setPickMode(true);
|
|
1564
1876
|
this.program.setClipOffset(0, 0);
|
|
1565
1877
|
this.program.setInstancing(false);
|
|
1566
1878
|
const aspect = frame.viewport.width / Math.max(frame.viewport.height, 1);
|
|
@@ -1706,13 +2018,229 @@ var WebGL2Picker = class {
|
|
|
1706
2018
|
}
|
|
1707
2019
|
};
|
|
1708
2020
|
|
|
2021
|
+
// packages/renderer/dist/WebGL2ShadowMap.js
|
|
2022
|
+
var VERTEX_SOURCE3 = `#version 300 es
|
|
2023
|
+
layout(location = 0) in vec3 a_position;
|
|
2024
|
+
layout(location = 3) in mat4 a_instanceMatrix;
|
|
2025
|
+
uniform mat4 u_lightViewProjection;
|
|
2026
|
+
void main() {
|
|
2027
|
+
vec4 world = a_instanceMatrix * vec4(a_position, 1.0);
|
|
2028
|
+
gl_Position = u_lightViewProjection * world;
|
|
2029
|
+
}
|
|
2030
|
+
`;
|
|
2031
|
+
var FRAGMENT_SOURCE3 = `#version 300 es
|
|
2032
|
+
precision highp float;
|
|
2033
|
+
void main() {
|
|
2034
|
+
// \u53EA\u5199\u6DF1\u5EA6\uFF0C\u989C\u8272\u4E0D\u9700\u8981\u3002
|
|
2035
|
+
}
|
|
2036
|
+
`;
|
|
2037
|
+
var LIGHT_DIRECTION2 = [-0.35, 0.85, 0.4];
|
|
2038
|
+
var SHADOW_SIZE = 2048;
|
|
2039
|
+
var ORTHO_EXTENT = 430;
|
|
2040
|
+
var ORTHO_NEAR = -260;
|
|
2041
|
+
var ORTHO_FAR = 700;
|
|
2042
|
+
var WebGL2ShadowMap = class {
|
|
2043
|
+
gl;
|
|
2044
|
+
framebuffer = null;
|
|
2045
|
+
depthTexture = null;
|
|
2046
|
+
program = null;
|
|
2047
|
+
lightMatrixLocation = null;
|
|
2048
|
+
matrixScratch = new Float32Array(16);
|
|
2049
|
+
constructor(gl) {
|
|
2050
|
+
this.gl = gl;
|
|
2051
|
+
this.createResources();
|
|
2052
|
+
}
|
|
2053
|
+
/** 深度纹理,供主着色器采样。 */
|
|
2054
|
+
get texture() {
|
|
2055
|
+
return this.depthTexture;
|
|
2056
|
+
}
|
|
2057
|
+
get matrix() {
|
|
2058
|
+
return this.matrixScratch;
|
|
2059
|
+
}
|
|
2060
|
+
/** 绑定阴影帧缓冲,返回是否成功。 */
|
|
2061
|
+
begin() {
|
|
2062
|
+
const gl = this.gl;
|
|
2063
|
+
if (!this.framebuffer || !this.program)
|
|
2064
|
+
return false;
|
|
2065
|
+
gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffer);
|
|
2066
|
+
gl.viewport(0, 0, SHADOW_SIZE, SHADOW_SIZE);
|
|
2067
|
+
gl.disable(gl.POLYGON_OFFSET_FILL);
|
|
2068
|
+
gl.clear(gl.DEPTH_BUFFER_BIT);
|
|
2069
|
+
gl.enable(gl.DEPTH_TEST);
|
|
2070
|
+
gl.enable(gl.CULL_FACE);
|
|
2071
|
+
gl.cullFace(gl.FRONT);
|
|
2072
|
+
gl.useProgram(this.program);
|
|
2073
|
+
gl.uniformMatrix4fv(this.lightMatrixLocation, false, this.matrixScratch);
|
|
2074
|
+
return true;
|
|
2075
|
+
}
|
|
2076
|
+
/** 解绑,恢复默认帧缓冲。 */
|
|
2077
|
+
end(width, height) {
|
|
2078
|
+
const gl = this.gl;
|
|
2079
|
+
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
|
|
2080
|
+
gl.disable(gl.CULL_FACE);
|
|
2081
|
+
gl.enable(gl.POLYGON_OFFSET_FILL);
|
|
2082
|
+
gl.viewport(0, 0, width, height);
|
|
2083
|
+
}
|
|
2084
|
+
dispose() {
|
|
2085
|
+
const gl = this.gl;
|
|
2086
|
+
if (this.depthTexture)
|
|
2087
|
+
gl.deleteTexture(this.depthTexture);
|
|
2088
|
+
if (this.framebuffer)
|
|
2089
|
+
gl.deleteFramebuffer(this.framebuffer);
|
|
2090
|
+
if (this.program)
|
|
2091
|
+
gl.deleteProgram(this.program);
|
|
2092
|
+
this.depthTexture = null;
|
|
2093
|
+
this.framebuffer = null;
|
|
2094
|
+
this.program = null;
|
|
2095
|
+
}
|
|
2096
|
+
createResources() {
|
|
2097
|
+
const gl = this.gl;
|
|
2098
|
+
this.depthTexture = gl.createTexture();
|
|
2099
|
+
gl.bindTexture(gl.TEXTURE_2D, this.depthTexture);
|
|
2100
|
+
gl.texImage2D(gl.TEXTURE_2D, 0, gl.DEPTH_COMPONENT24, SHADOW_SIZE, SHADOW_SIZE, 0, gl.DEPTH_COMPONENT, gl.UNSIGNED_INT, null);
|
|
2101
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
|
|
2102
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
|
|
2103
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
|
|
2104
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
|
2105
|
+
this.framebuffer = gl.createFramebuffer();
|
|
2106
|
+
gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffer);
|
|
2107
|
+
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.TEXTURE_2D, this.depthTexture, 0);
|
|
2108
|
+
gl.drawBuffers([gl.NONE]);
|
|
2109
|
+
gl.readBuffer(gl.NONE);
|
|
2110
|
+
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
|
|
2111
|
+
this.program = this.createProgram(VERTEX_SOURCE3, FRAGMENT_SOURCE3);
|
|
2112
|
+
this.lightMatrixLocation = this.program ? gl.getUniformLocation(this.program, "u_lightViewProjection") : null;
|
|
2113
|
+
this.updateLightMatrix();
|
|
2114
|
+
}
|
|
2115
|
+
/** 正交光源矩阵:覆盖整片场地,与主着色器的光方向一致。 */
|
|
2116
|
+
updateLightMatrix() {
|
|
2117
|
+
const direction = normalize2(LIGHT_DIRECTION2);
|
|
2118
|
+
const target = [300, 0, 0];
|
|
2119
|
+
const eye = [
|
|
2120
|
+
target[0] + direction[0] * 400,
|
|
2121
|
+
target[1] + direction[1] * 400,
|
|
2122
|
+
target[2] + direction[2] * 400
|
|
2123
|
+
];
|
|
2124
|
+
const view = lookAt(eye, target, [0, 1, 0]);
|
|
2125
|
+
const projection = orthographic(-ORTHO_EXTENT, ORTHO_EXTENT, -ORTHO_EXTENT, ORTHO_EXTENT, ORTHO_NEAR, ORTHO_FAR);
|
|
2126
|
+
this.matrixScratch.set(multiply(projection, view));
|
|
2127
|
+
}
|
|
2128
|
+
createProgram(vertex, fragment) {
|
|
2129
|
+
const gl = this.gl;
|
|
2130
|
+
const vertexShader = this.compile(gl.VERTEX_SHADER, vertex);
|
|
2131
|
+
const fragmentShader = this.compile(gl.FRAGMENT_SHADER, fragment);
|
|
2132
|
+
if (!vertexShader || !fragmentShader)
|
|
2133
|
+
return null;
|
|
2134
|
+
const program = gl.createProgram();
|
|
2135
|
+
gl.attachShader(program, vertexShader);
|
|
2136
|
+
gl.attachShader(program, fragmentShader);
|
|
2137
|
+
gl.linkProgram(program);
|
|
2138
|
+
gl.deleteShader(vertexShader);
|
|
2139
|
+
gl.deleteShader(fragmentShader);
|
|
2140
|
+
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
|
|
2141
|
+
gl.deleteProgram(program);
|
|
2142
|
+
return null;
|
|
2143
|
+
}
|
|
2144
|
+
return program;
|
|
2145
|
+
}
|
|
2146
|
+
compile(type, source) {
|
|
2147
|
+
const gl = this.gl;
|
|
2148
|
+
const shader = gl.createShader(type);
|
|
2149
|
+
if (!shader)
|
|
2150
|
+
return null;
|
|
2151
|
+
gl.shaderSource(shader, source);
|
|
2152
|
+
gl.compileShader(shader);
|
|
2153
|
+
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
|
|
2154
|
+
gl.deleteShader(shader);
|
|
2155
|
+
return null;
|
|
2156
|
+
}
|
|
2157
|
+
return shader;
|
|
2158
|
+
}
|
|
2159
|
+
};
|
|
2160
|
+
function normalize2(value) {
|
|
2161
|
+
const length = Math.hypot(value[0], value[1], value[2]) || 1;
|
|
2162
|
+
return [value[0] / length, value[1] / length, value[2] / length];
|
|
2163
|
+
}
|
|
2164
|
+
function subtract2(a, b) {
|
|
2165
|
+
return [a[0] - b[0], a[1] - b[1], a[2] - b[2]];
|
|
2166
|
+
}
|
|
2167
|
+
function cross2(a, b) {
|
|
2168
|
+
return [
|
|
2169
|
+
a[1] * b[2] - a[2] * b[1],
|
|
2170
|
+
a[2] * b[0] - a[0] * b[2],
|
|
2171
|
+
a[0] * b[1] - a[1] * b[0]
|
|
2172
|
+
];
|
|
2173
|
+
}
|
|
2174
|
+
function dot2(a, b) {
|
|
2175
|
+
return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
|
|
2176
|
+
}
|
|
2177
|
+
function lookAt(eye, target, up) {
|
|
2178
|
+
const forward = normalize2(subtract2(eye, target));
|
|
2179
|
+
const right = normalize2(cross2(up, forward));
|
|
2180
|
+
const cameraUp = cross2(forward, right);
|
|
2181
|
+
return [
|
|
2182
|
+
right[0],
|
|
2183
|
+
right[1],
|
|
2184
|
+
right[2],
|
|
2185
|
+
0,
|
|
2186
|
+
cameraUp[0],
|
|
2187
|
+
cameraUp[1],
|
|
2188
|
+
cameraUp[2],
|
|
2189
|
+
0,
|
|
2190
|
+
forward[0],
|
|
2191
|
+
forward[1],
|
|
2192
|
+
forward[2],
|
|
2193
|
+
0,
|
|
2194
|
+
-dot2(right, eye),
|
|
2195
|
+
-dot2(cameraUp, eye),
|
|
2196
|
+
-dot2(forward, eye),
|
|
2197
|
+
1
|
|
2198
|
+
];
|
|
2199
|
+
}
|
|
2200
|
+
function orthographic(left, right, bottom, top, near, far) {
|
|
2201
|
+
const lr = 1 / (left - right);
|
|
2202
|
+
const bt = 1 / (bottom - top);
|
|
2203
|
+
const nf = 1 / (near - far);
|
|
2204
|
+
return [
|
|
2205
|
+
-2 * lr,
|
|
2206
|
+
0,
|
|
2207
|
+
0,
|
|
2208
|
+
0,
|
|
2209
|
+
0,
|
|
2210
|
+
-2 * bt,
|
|
2211
|
+
0,
|
|
2212
|
+
0,
|
|
2213
|
+
0,
|
|
2214
|
+
0,
|
|
2215
|
+
2 * nf,
|
|
2216
|
+
0,
|
|
2217
|
+
(left + right) * lr,
|
|
2218
|
+
(top + bottom) * bt,
|
|
2219
|
+
(far + near) * nf,
|
|
2220
|
+
1
|
|
2221
|
+
];
|
|
2222
|
+
}
|
|
2223
|
+
function multiply(a, b) {
|
|
2224
|
+
const out = new Array(16).fill(0);
|
|
2225
|
+
for (let column = 0; column < 4; column += 1) {
|
|
2226
|
+
for (let row = 0; row < 4; row += 1) {
|
|
2227
|
+
let sum = 0;
|
|
2228
|
+
for (let k = 0; k < 4; k += 1) {
|
|
2229
|
+
sum += (a[k * 4 + row] ?? 0) * (b[column * 4 + k] ?? 0);
|
|
2230
|
+
}
|
|
2231
|
+
out[column * 4 + row] = sum;
|
|
2232
|
+
}
|
|
2233
|
+
}
|
|
2234
|
+
return out;
|
|
2235
|
+
}
|
|
2236
|
+
|
|
1709
2237
|
// packages/renderer/dist/Color.js
|
|
1710
2238
|
var COLOR_CACHE_LIMIT = 128;
|
|
1711
2239
|
var COLOR_CACHE = /* @__PURE__ */ new Map();
|
|
1712
2240
|
function colorToRgba(color) {
|
|
1713
2241
|
const normalized = color.startsWith("#") ? color.slice(1) : color;
|
|
1714
2242
|
const expanded = normalized.length === 3 ? normalized.split("").map((part) => part + part).join("") : normalized;
|
|
1715
|
-
if (!/^[0-9a-fA-F]{6}
|
|
2243
|
+
if (!/^[0-9a-fA-F]{6}([0-9a-fA-F]{2})?$/.test(expanded)) {
|
|
1716
2244
|
throw new Error(`Unsupported ICE basic material color: ${color}`);
|
|
1717
2245
|
}
|
|
1718
2246
|
const key = expanded.toLowerCase();
|
|
@@ -1723,7 +2251,7 @@ function colorToRgba(color) {
|
|
|
1723
2251
|
Number.parseInt(expanded.slice(0, 2), 16) / 255,
|
|
1724
2252
|
Number.parseInt(expanded.slice(2, 4), 16) / 255,
|
|
1725
2253
|
Number.parseInt(expanded.slice(4, 6), 16) / 255,
|
|
1726
|
-
1
|
|
2254
|
+
expanded.length === 8 ? Number.parseInt(expanded.slice(6, 8), 16) / 255 : 1
|
|
1727
2255
|
]);
|
|
1728
2256
|
if (COLOR_CACHE.size >= COLOR_CACHE_LIMIT) {
|
|
1729
2257
|
const oldest = COLOR_CACHE.keys().next().value;
|
|
@@ -1781,6 +2309,9 @@ var EDGE_PIXEL_OFFSETS = [
|
|
|
1781
2309
|
];
|
|
1782
2310
|
var SELECTION_RGBA = [255 / 255, 208 / 255, 0];
|
|
1783
2311
|
var SELECTION_TINT_ALPHA = 0.65;
|
|
2312
|
+
function isTransparentColor(color) {
|
|
2313
|
+
return colorToRgba(color)[3] < 0.999;
|
|
2314
|
+
}
|
|
1784
2315
|
var WebGL2RendererBackend = class {
|
|
1785
2316
|
capabilities = {
|
|
1786
2317
|
backend: "webgl2",
|
|
@@ -1794,6 +2325,9 @@ var WebGL2RendererBackend = class {
|
|
|
1794
2325
|
onContextLost;
|
|
1795
2326
|
onContextRestored;
|
|
1796
2327
|
program = null;
|
|
2328
|
+
shadowMap = null;
|
|
2329
|
+
// 阴影采样开关。定位掉深度偏差之前保持关闭,避免把错误的明暗关系画出来。
|
|
2330
|
+
shadowEnabled = false;
|
|
1797
2331
|
semanticProgram = null;
|
|
1798
2332
|
geometryCache = null;
|
|
1799
2333
|
instanceCache = null;
|
|
@@ -1825,6 +2359,8 @@ var WebGL2RendererBackend = class {
|
|
|
1825
2359
|
event.preventDefault();
|
|
1826
2360
|
this.picker = null;
|
|
1827
2361
|
this.program = null;
|
|
2362
|
+
this.shadowMap?.dispose();
|
|
2363
|
+
this.shadowMap = null;
|
|
1828
2364
|
this.geometryCache?.clear();
|
|
1829
2365
|
this.geometryCache = null;
|
|
1830
2366
|
this.instanceCache?.clear();
|
|
@@ -1888,6 +2424,15 @@ var WebGL2RendererBackend = class {
|
|
|
1888
2424
|
}
|
|
1889
2425
|
this.geometryCache.pin(this.gl, geometry);
|
|
1890
2426
|
}
|
|
2427
|
+
prepareGeometry(geometry, update) {
|
|
2428
|
+
if (!this.gl || !this.geometryCache) {
|
|
2429
|
+
throw new ICEEngineError("ICE3_ENGINE_NOT_READY", "Renderer is not initialized");
|
|
2430
|
+
}
|
|
2431
|
+
this.geometryCache.pin(this.gl, geometry, update);
|
|
2432
|
+
}
|
|
2433
|
+
releaseGeometry(geometry) {
|
|
2434
|
+
this.geometryCache?.unpin(geometry);
|
|
2435
|
+
}
|
|
1891
2436
|
releaseBatchGeometry(geometry) {
|
|
1892
2437
|
this.geometryCache?.unpin(geometry);
|
|
1893
2438
|
}
|
|
@@ -1914,6 +2459,10 @@ var WebGL2RendererBackend = class {
|
|
|
1914
2459
|
};
|
|
1915
2460
|
this.program.use();
|
|
1916
2461
|
this.program.setClipOffset(0, 0);
|
|
2462
|
+
this.program.setDirectionalShading(false);
|
|
2463
|
+
this.program.setWaterShading(false);
|
|
2464
|
+
const cameraPosition = frame.camera.getPosition().toArray();
|
|
2465
|
+
this.program.setCameraPosition(cameraPosition);
|
|
1917
2466
|
this.geometryCache.beginFrame();
|
|
1918
2467
|
this.instanceCache.beginFrame();
|
|
1919
2468
|
this.instancedMeshIds.clear();
|
|
@@ -1924,93 +2473,140 @@ var WebGL2RendererBackend = class {
|
|
|
1924
2473
|
const instances = collectInstanceBatches(renderables, this.instancesScratch);
|
|
1925
2474
|
const batches = collectRenderBatches(frame.scene, this.batchesScratch);
|
|
1926
2475
|
this.profiler.mark("collect");
|
|
1927
|
-
this.
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
this.program.setColor(colorToRgba(batch.material.color));
|
|
1938
|
-
this.program.setVertexColors(Boolean(batch.geometry.colors));
|
|
1939
|
-
gl.bindVertexArray(instance.vertexArray);
|
|
1940
|
-
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, instance.triangleIndexBuffer);
|
|
1941
|
-
gl.drawElementsInstanced(gl.TRIANGLES, instance.triangleCount, batch.geometry.indices instanceof Uint16Array ? gl.UNSIGNED_SHORT : gl.UNSIGNED_INT, 0, instance.instanceCount);
|
|
1942
|
-
this.frameStats.drawCalls += 1;
|
|
1943
|
-
if (batch.material.edgeColor && instance.edgeCount > 0) {
|
|
1944
|
-
this.program.setVertexColors(false);
|
|
1945
|
-
this.program.setColor(colorToRgba(batch.material.edgeColor));
|
|
1946
|
-
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, instance.edgeIndexBuffer);
|
|
1947
|
-
for (const [offsetX, offsetY] of EDGE_PIXEL_OFFSETS) {
|
|
1948
|
-
this.program.setClipOffset(offsetX * clipPixelX, offsetY * clipPixelY);
|
|
1949
|
-
gl.drawElementsInstanced(gl.LINES, instance.edgeCount, batch.geometry.edgeIndices instanceof Uint16Array ? gl.UNSIGNED_SHORT : gl.UNSIGNED_INT, 0, instance.instanceCount);
|
|
1950
|
-
this.frameStats.drawCalls += 1;
|
|
1951
|
-
}
|
|
2476
|
+
if (this.shadowEnabled && this.shadowMap?.begin()) {
|
|
2477
|
+
for (const batch of instances) {
|
|
2478
|
+
if (isTransparentColor(batch.material.color))
|
|
2479
|
+
continue;
|
|
2480
|
+
const instance = this.instanceCache.retain(gl, this.geometryCache, batch);
|
|
2481
|
+
if (!instance)
|
|
2482
|
+
continue;
|
|
2483
|
+
gl.bindVertexArray(instance.vertexArray);
|
|
2484
|
+
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, instance.triangleIndexBuffer);
|
|
2485
|
+
gl.drawElementsInstanced(gl.TRIANGLES, instance.triangleCount, batch.geometry.indices instanceof Uint16Array ? gl.UNSIGNED_SHORT : gl.UNSIGNED_INT, 0, instance.instanceCount);
|
|
1952
2486
|
}
|
|
2487
|
+
this.shadowMap.end(frame.viewport.width * this.dpr, frame.viewport.height * this.dpr);
|
|
2488
|
+
this.program.use();
|
|
2489
|
+
this.program.setShadow(this.shadowEnabled, this.shadowMap.texture, this.shadowMap.matrix);
|
|
1953
2490
|
}
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
this.
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
2491
|
+
for (const transparentPass of [false, true]) {
|
|
2492
|
+
if (transparentPass) {
|
|
2493
|
+
gl.enable(gl.BLEND);
|
|
2494
|
+
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
|
|
2495
|
+
gl.depthMask(false);
|
|
2496
|
+
} else {
|
|
2497
|
+
gl.disable(gl.BLEND);
|
|
2498
|
+
gl.depthMask(true);
|
|
2499
|
+
}
|
|
2500
|
+
this.program.setInstancing(true);
|
|
2501
|
+
this.profiler.mark("prepareAndUpload");
|
|
2502
|
+
for (const batch of instances) {
|
|
2503
|
+
if (isTransparentColor(batch.material.color) !== transparentPass)
|
|
2504
|
+
continue;
|
|
2505
|
+
const instance = this.instanceCache.retain(gl, this.geometryCache, batch);
|
|
2506
|
+
if (!instance)
|
|
2507
|
+
continue;
|
|
2508
|
+
for (const id of batch.meshIds)
|
|
2509
|
+
this.instancedMeshIds.add(id);
|
|
2510
|
+
mvp.elements.set(viewProjection.elements);
|
|
2511
|
+
this.program.setMatrix(mvp);
|
|
2512
|
+
this.program.setColor(colorToRgba(batch.material.color));
|
|
2513
|
+
this.program.setVertexColors(Boolean(batch.geometry.colors));
|
|
2514
|
+
this.program.setDirectionalShading(batch.material.shading !== "flat");
|
|
2515
|
+
this.program.setWaterShading(batch.material.shading === "water");
|
|
2516
|
+
gl.bindVertexArray(instance.vertexArray);
|
|
2517
|
+
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, instance.triangleIndexBuffer);
|
|
2518
|
+
gl.drawElementsInstanced(gl.TRIANGLES, instance.triangleCount, batch.geometry.indices instanceof Uint16Array ? gl.UNSIGNED_SHORT : gl.UNSIGNED_INT, 0, instance.instanceCount);
|
|
2519
|
+
this.frameStats.drawCalls += 1;
|
|
2520
|
+
if (batch.material.edgeColor && instance.edgeCount > 0) {
|
|
2521
|
+
this.program.setVertexColors(false);
|
|
2522
|
+
this.program.setDirectionalShading(false);
|
|
2523
|
+
this.program.setWaterShading(false);
|
|
2524
|
+
this.program.setColor(colorToRgba(batch.material.edgeColor));
|
|
2525
|
+
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, instance.edgeIndexBuffer);
|
|
2526
|
+
for (const [offsetX, offsetY] of EDGE_PIXEL_OFFSETS) {
|
|
2527
|
+
this.program.setClipOffset(offsetX * clipPixelX, offsetY * clipPixelY);
|
|
2528
|
+
gl.drawElementsInstanced(gl.LINES, instance.edgeCount, batch.geometry.edgeIndices instanceof Uint16Array ? gl.UNSIGNED_SHORT : gl.UNSIGNED_INT, 0, instance.instanceCount);
|
|
2529
|
+
this.frameStats.drawCalls += 1;
|
|
2530
|
+
}
|
|
1978
2531
|
}
|
|
1979
2532
|
}
|
|
1980
|
-
|
|
1981
|
-
for (const renderable of renderables) {
|
|
1982
|
-
if (this.instancedMeshIds.has(renderable.id))
|
|
1983
|
-
continue;
|
|
2533
|
+
this.program.setInstancing(false);
|
|
1984
2534
|
this.program.setClipOffset(0, 0);
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
this.program.
|
|
1998
|
-
|
|
1999
|
-
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, geometry.
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2535
|
+
for (const draw of batches) {
|
|
2536
|
+
if (isTransparentColor(draw.batch.material.color) !== transparentPass)
|
|
2537
|
+
continue;
|
|
2538
|
+
this.program.setClipOffset(0, 0);
|
|
2539
|
+
mvp.elements.set(viewProjection.elements);
|
|
2540
|
+
world.elements.set(draw.worldMatrix);
|
|
2541
|
+
mvp.multiply(world);
|
|
2542
|
+
this.program.setMatrix(mvp);
|
|
2543
|
+
this.program.setColor(colorToRgba(draw.batch.material.color));
|
|
2544
|
+
const geometry = this.geometryCache.retain(gl, draw.batch.geometry);
|
|
2545
|
+
this.program.setVertexColors(Boolean(draw.batch.geometry.colors));
|
|
2546
|
+
this.program.setDirectionalShading(draw.batch.material.shading !== "flat");
|
|
2547
|
+
this.program.setWaterShading(draw.batch.material.shading === "water");
|
|
2548
|
+
gl.bindVertexArray(geometry.vertexArray);
|
|
2549
|
+
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, geometry.triangleIndexBuffer);
|
|
2550
|
+
gl.drawElements(gl.TRIANGLES, geometry.triangleCount, draw.batch.geometry.indices instanceof Uint16Array ? gl.UNSIGNED_SHORT : gl.UNSIGNED_INT, 0);
|
|
2551
|
+
this.frameStats.drawCalls += 1;
|
|
2552
|
+
this.frameStats.renderedBatches += 1;
|
|
2553
|
+
if (draw.batch.material.edgeColor) {
|
|
2554
|
+
this.program.setVertexColors(false);
|
|
2555
|
+
this.program.setDirectionalShading(false);
|
|
2556
|
+
this.program.setWaterShading(false);
|
|
2557
|
+
this.program.setColor(colorToRgba(draw.batch.material.edgeColor));
|
|
2558
|
+
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, geometry.edgeIndexBuffer);
|
|
2559
|
+
for (const [offsetX, offsetY] of EDGE_PIXEL_OFFSETS) {
|
|
2560
|
+
this.program.setClipOffset(offsetX * clipPixelX, offsetY * clipPixelY);
|
|
2561
|
+
gl.drawElements(gl.LINES, geometry.edgeCount, draw.batch.geometry.edgeIndices instanceof Uint16Array ? gl.UNSIGNED_SHORT : gl.UNSIGNED_INT, 0);
|
|
2562
|
+
this.frameStats.drawCalls += 1;
|
|
2563
|
+
}
|
|
2004
2564
|
}
|
|
2005
2565
|
}
|
|
2006
|
-
|
|
2566
|
+
for (const renderable of renderables) {
|
|
2567
|
+
if (isTransparentColor(renderable.mesh.material.color) !== transparentPass)
|
|
2568
|
+
continue;
|
|
2569
|
+
if (this.instancedMeshIds.has(renderable.id))
|
|
2570
|
+
continue;
|
|
2571
|
+
this.program.setClipOffset(0, 0);
|
|
2572
|
+
mvp.elements.set(viewProjection.elements);
|
|
2573
|
+
world.elements.set(renderable.worldMatrix);
|
|
2574
|
+
mvp.multiply(world);
|
|
2575
|
+
this.program.setMatrix(mvp);
|
|
2576
|
+
this.program.setColor(colorToRgba(renderable.mesh.material.color));
|
|
2577
|
+
const geometry = this.geometryCache.retain(gl, renderable.mesh.geometry);
|
|
2578
|
+
this.program.setVertexColors(Boolean(renderable.mesh.geometry.colors));
|
|
2579
|
+
this.program.setDirectionalShading(renderable.mesh.material.shading !== "flat");
|
|
2580
|
+
this.program.setWaterShading(renderable.mesh.material.shading === "water");
|
|
2581
|
+
gl.bindVertexArray(geometry.vertexArray);
|
|
2582
|
+
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, geometry.triangleIndexBuffer);
|
|
2583
|
+
gl.drawElements(gl.TRIANGLES, geometry.triangleCount, renderable.mesh.geometry.indices instanceof Uint16Array ? gl.UNSIGNED_SHORT : gl.UNSIGNED_INT, 0);
|
|
2584
|
+
this.frameStats.drawCalls += 1;
|
|
2585
|
+
if (renderable.mesh.material.edgeColor) {
|
|
2586
|
+
this.program.setVertexColors(false);
|
|
2587
|
+
this.program.setDirectionalShading(false);
|
|
2588
|
+
this.program.setWaterShading(false);
|
|
2589
|
+
this.program.setColor(colorToRgba(renderable.mesh.material.edgeColor));
|
|
2590
|
+
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, geometry.edgeIndexBuffer);
|
|
2591
|
+
for (const [offsetX, offsetY] of EDGE_PIXEL_OFFSETS) {
|
|
2592
|
+
this.program.setClipOffset(offsetX * clipPixelX, offsetY * clipPixelY);
|
|
2593
|
+
gl.drawElements(gl.LINES, geometry.edgeCount, renderable.mesh.geometry.edgeIndices instanceof Uint16Array ? gl.UNSIGNED_SHORT : gl.UNSIGNED_INT, 0);
|
|
2594
|
+
this.frameStats.drawCalls += 1;
|
|
2595
|
+
}
|
|
2596
|
+
}
|
|
2597
|
+
this.frameStats.renderedMeshes += 1;
|
|
2598
|
+
}
|
|
2007
2599
|
}
|
|
2600
|
+
gl.depthMask(true);
|
|
2601
|
+
gl.disable(gl.BLEND);
|
|
2008
2602
|
if (frame.selection.length > 0) {
|
|
2009
2603
|
const selected = new Set(frame.selection);
|
|
2010
2604
|
gl.enable(gl.BLEND);
|
|
2011
2605
|
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
|
|
2012
2606
|
gl.depthMask(false);
|
|
2607
|
+
gl.polygonOffset(-1, -4);
|
|
2013
2608
|
this.program.setVertexColors(false);
|
|
2609
|
+
this.program.setDirectionalShading(false);
|
|
2014
2610
|
this.program.setColor([
|
|
2015
2611
|
SELECTION_RGBA[0],
|
|
2016
2612
|
SELECTION_RGBA[1],
|
|
@@ -2056,6 +2652,7 @@ var WebGL2RendererBackend = class {
|
|
|
2056
2652
|
if (this.semanticProgram) {
|
|
2057
2653
|
this.semanticProgram.use();
|
|
2058
2654
|
this.semanticProgram.setInstancing(false);
|
|
2655
|
+
gl.polygonOffset(-1, -4);
|
|
2059
2656
|
for (const draw of batches) {
|
|
2060
2657
|
for (let semanticIndex = 0; semanticIndex < draw.batch.semanticSelectionIds.length; semanticIndex += 1) {
|
|
2061
2658
|
if (!selected.has(draw.batch.semanticSelectionIds[semanticIndex])) {
|
|
@@ -2073,6 +2670,7 @@ var WebGL2RendererBackend = class {
|
|
|
2073
2670
|
}
|
|
2074
2671
|
}
|
|
2075
2672
|
this.semanticProgram.setInstancing(true);
|
|
2673
|
+
gl.polygonOffset(-1, -4);
|
|
2076
2674
|
mvp.elements.set(viewProjection.elements);
|
|
2077
2675
|
this.semanticProgram.setMatrix(mvp);
|
|
2078
2676
|
for (const batch of instances) {
|
|
@@ -2089,6 +2687,7 @@ var WebGL2RendererBackend = class {
|
|
|
2089
2687
|
}
|
|
2090
2688
|
}
|
|
2091
2689
|
gl.depthMask(true);
|
|
2690
|
+
gl.polygonOffset(2, 4);
|
|
2092
2691
|
gl.disable(gl.BLEND);
|
|
2093
2692
|
this.program.use();
|
|
2094
2693
|
this.program.setClipOffset(0, 0);
|
|
@@ -2173,6 +2772,7 @@ var WebGL2RendererBackend = class {
|
|
|
2173
2772
|
this.geometryCache = new WebGL2GeometryCache();
|
|
2174
2773
|
this.instanceCache = new WebGL2InstanceCache();
|
|
2175
2774
|
this.picker = new WebGL2Picker(gl);
|
|
2775
|
+
this.shadowMap = new WebGL2ShadowMap(gl);
|
|
2176
2776
|
}
|
|
2177
2777
|
};
|
|
2178
2778
|
|
|
@@ -2310,8 +2910,8 @@ async function executeProbe(gpu, options) {
|
|
|
2310
2910
|
primitive: { topology: "triangle-list" }
|
|
2311
2911
|
});
|
|
2312
2912
|
const texture = context.getCurrentTexture();
|
|
2313
|
-
const
|
|
2314
|
-
const pass =
|
|
2913
|
+
const encoder2 = device.createCommandEncoder();
|
|
2914
|
+
const pass = encoder2.beginRenderPass({
|
|
2315
2915
|
colorAttachments: [{
|
|
2316
2916
|
view: texture.createView(),
|
|
2317
2917
|
clearValue: { r: 0, g: 0, b: 0, a: 1 },
|
|
@@ -2322,7 +2922,7 @@ async function executeProbe(gpu, options) {
|
|
|
2322
2922
|
pass.setPipeline(pipeline);
|
|
2323
2923
|
pass.draw(3);
|
|
2324
2924
|
pass.end();
|
|
2325
|
-
device.queue.submit([
|
|
2925
|
+
device.queue.submit([encoder2.finish()]);
|
|
2326
2926
|
const lost = await Promise.race([
|
|
2327
2927
|
device.lost.then((info) => info.message || info.reason),
|
|
2328
2928
|
new Promise((resolve) => {
|
|
@@ -2428,9 +3028,13 @@ var WebGPUGeometryCache = class {
|
|
|
2428
3028
|
entries = /* @__PURE__ */ new Map();
|
|
2429
3029
|
active = /* @__PURE__ */ new Set();
|
|
2430
3030
|
pinned = /* @__PURE__ */ new Set();
|
|
3031
|
+
objectKeys = /* @__PURE__ */ new WeakMap();
|
|
3032
|
+
legacyKeys = /* @__PURE__ */ new WeakMap();
|
|
3033
|
+
objectKeySequence = 0;
|
|
2431
3034
|
inactiveGraceFrames;
|
|
2432
3035
|
maxBytes;
|
|
2433
3036
|
uploads = 0;
|
|
3037
|
+
updatedBytes = 0;
|
|
2434
3038
|
evictions = 0;
|
|
2435
3039
|
frameId = 0;
|
|
2436
3040
|
resourceGeneration = 0;
|
|
@@ -2443,12 +3047,23 @@ var WebGPUGeometryCache = class {
|
|
|
2443
3047
|
}
|
|
2444
3048
|
}
|
|
2445
3049
|
retain(geometry) {
|
|
2446
|
-
this.
|
|
2447
|
-
|
|
3050
|
+
const key = this.keyFor(geometry);
|
|
3051
|
+
this.active.add(key);
|
|
3052
|
+
const existing = this.entries.get(key);
|
|
2448
3053
|
if (existing) {
|
|
2449
|
-
existing
|
|
2450
|
-
|
|
3054
|
+
if (this.canReuse(existing, geometry)) {
|
|
3055
|
+
existing.lastUsedFrame = this.frameId;
|
|
3056
|
+
return existing;
|
|
3057
|
+
}
|
|
3058
|
+
}
|
|
3059
|
+
const next = this.createEntry(geometry, key);
|
|
3060
|
+
if (existing) {
|
|
3061
|
+
this.deleteEntry(existing, false);
|
|
2451
3062
|
}
|
|
3063
|
+
this.entries.set(key, next);
|
|
3064
|
+
return next;
|
|
3065
|
+
}
|
|
3066
|
+
createEntry(geometry, key) {
|
|
2452
3067
|
const buffers = [];
|
|
2453
3068
|
try {
|
|
2454
3069
|
const vertexCount = geometry.positions.length / 3;
|
|
@@ -2473,6 +3088,10 @@ var WebGPUGeometryCache = class {
|
|
|
2473
3088
|
if (semanticBuffer)
|
|
2474
3089
|
buffers.push(semanticBuffer);
|
|
2475
3090
|
const entry = {
|
|
3091
|
+
geometry,
|
|
3092
|
+
geometryId: geometry.identity?.geometryId ?? key,
|
|
3093
|
+
attributeDigest: geometry.identity?.attributeDigest ?? "",
|
|
3094
|
+
topologyDigest: geometry.identity?.topologyDigest ?? "",
|
|
2476
3095
|
vertexBuffer,
|
|
2477
3096
|
triangleIndexBuffer,
|
|
2478
3097
|
edgeIndexBuffer,
|
|
@@ -2484,7 +3103,6 @@ var WebGPUGeometryCache = class {
|
|
|
2484
3103
|
bytes: buffers.reduce((sum, buffer) => sum + buffer.size, 0),
|
|
2485
3104
|
lastUsedFrame: this.frameId
|
|
2486
3105
|
};
|
|
2487
|
-
this.entries.set(geometry, entry);
|
|
2488
3106
|
this.uploads += buffers.length;
|
|
2489
3107
|
this.resourceGeneration += 1;
|
|
2490
3108
|
return entry;
|
|
@@ -2496,19 +3114,25 @@ var WebGPUGeometryCache = class {
|
|
|
2496
3114
|
throw new ICEEngineError("ICE3_GPU_RESOURCE_FAILED", "ICE3_GPU_RESOURCE_FAILED: geometry upload failed", { cause: error });
|
|
2497
3115
|
}
|
|
2498
3116
|
}
|
|
2499
|
-
pin(geometry) {
|
|
2500
|
-
this.
|
|
3117
|
+
pin(geometry, update) {
|
|
3118
|
+
const key = this.keyFor(geometry);
|
|
3119
|
+
this.pinned.add(key);
|
|
3120
|
+
const existing = this.entries.get(key);
|
|
3121
|
+
if (existing && update && this.sameTopology(existing, geometry)) {
|
|
3122
|
+
this.applyPositionUpdate(existing, geometry, update);
|
|
3123
|
+
return existing;
|
|
3124
|
+
}
|
|
2501
3125
|
const entry = this.retain(geometry);
|
|
2502
3126
|
this.enforceBudget();
|
|
2503
3127
|
if (this.totalBytes > this.maxBytes) {
|
|
2504
|
-
this.pinned.delete(
|
|
2505
|
-
this.deleteEntry(
|
|
3128
|
+
this.pinned.delete(key);
|
|
3129
|
+
this.deleteEntry(entry);
|
|
2506
3130
|
throw new ICEEngineError("ICE3_GPU_RESOURCE_FAILED", "ICE3_GPU_RESOURCE_FAILED: geometry exceeds the GPU residency budget");
|
|
2507
3131
|
}
|
|
2508
3132
|
return entry;
|
|
2509
3133
|
}
|
|
2510
3134
|
unpin(geometry) {
|
|
2511
|
-
this.pinned.delete(geometry);
|
|
3135
|
+
this.pinned.delete(this.keyFor(geometry));
|
|
2512
3136
|
}
|
|
2513
3137
|
beginFrame() {
|
|
2514
3138
|
this.frameId += 1;
|
|
@@ -2521,13 +3145,13 @@ var WebGPUGeometryCache = class {
|
|
|
2521
3145
|
if (this.frameId - entry.lastUsedFrame < this.inactiveGraceFrames) {
|
|
2522
3146
|
continue;
|
|
2523
3147
|
}
|
|
2524
|
-
this.deleteEntry(
|
|
3148
|
+
this.deleteEntry(entry);
|
|
2525
3149
|
}
|
|
2526
3150
|
this.enforceBudget();
|
|
2527
3151
|
}
|
|
2528
3152
|
clear() {
|
|
2529
3153
|
for (const [geometry, entry] of this.entries) {
|
|
2530
|
-
this.deleteEntry(
|
|
3154
|
+
this.deleteEntry(entry);
|
|
2531
3155
|
}
|
|
2532
3156
|
this.active.clear();
|
|
2533
3157
|
this.pinned.clear();
|
|
@@ -2541,6 +3165,9 @@ var WebGPUGeometryCache = class {
|
|
|
2541
3165
|
get uploadCount() {
|
|
2542
3166
|
return this.uploads;
|
|
2543
3167
|
}
|
|
3168
|
+
get updateBytes() {
|
|
3169
|
+
return this.updatedBytes;
|
|
3170
|
+
}
|
|
2544
3171
|
get evictionCount() {
|
|
2545
3172
|
return this.evictions;
|
|
2546
3173
|
}
|
|
@@ -2555,20 +3182,120 @@ var WebGPUGeometryCache = class {
|
|
|
2555
3182
|
for (const [geometry, entry] of candidates) {
|
|
2556
3183
|
if (total <= this.maxBytes)
|
|
2557
3184
|
break;
|
|
2558
|
-
this.deleteEntry(
|
|
3185
|
+
this.deleteEntry(entry);
|
|
2559
3186
|
total -= entry.bytes;
|
|
2560
3187
|
}
|
|
2561
3188
|
}
|
|
2562
|
-
deleteEntry(
|
|
3189
|
+
deleteEntry(entry, eviction = true) {
|
|
2563
3190
|
entry.vertexBuffer.destroy();
|
|
2564
3191
|
entry.triangleIndexBuffer.destroy();
|
|
2565
3192
|
entry.edgeIndexBuffer?.destroy();
|
|
2566
3193
|
entry.semanticBuffer?.destroy();
|
|
2567
|
-
this.entries
|
|
2568
|
-
|
|
3194
|
+
for (const [key, candidate] of this.entries) {
|
|
3195
|
+
if (candidate === entry) {
|
|
3196
|
+
this.entries.delete(key);
|
|
3197
|
+
break;
|
|
3198
|
+
}
|
|
3199
|
+
}
|
|
3200
|
+
if (eviction) {
|
|
3201
|
+
this.evictions += 1;
|
|
3202
|
+
}
|
|
2569
3203
|
this.resourceGeneration += 1;
|
|
2570
3204
|
}
|
|
3205
|
+
applyPositionUpdate(entry, geometry, update) {
|
|
3206
|
+
let updated = 0;
|
|
3207
|
+
try {
|
|
3208
|
+
for (const range of update.positionRanges) {
|
|
3209
|
+
if (range.byteOffset % 12 !== 0 || range.byteLength % 12 !== 0 || range.byteOffset + range.byteLength > geometry.positions.byteLength) {
|
|
3210
|
+
throw new Error("invalid position range");
|
|
3211
|
+
}
|
|
3212
|
+
const vertexOffset = range.byteOffset / 12;
|
|
3213
|
+
const vertexCount = range.byteLength / 12;
|
|
3214
|
+
const vertices = new Float32Array(vertexCount * 6);
|
|
3215
|
+
const totalVertices = geometry.positions.length / 3;
|
|
3216
|
+
const colorStride = geometry.colors ? geometry.colors.length / totalVertices : 0;
|
|
3217
|
+
for (let index = 0; index < vertexCount; index += 1) {
|
|
3218
|
+
const sourceVertex = vertexOffset + index;
|
|
3219
|
+
vertices[index * 6] = geometry.positions[sourceVertex * 3];
|
|
3220
|
+
vertices[index * 6 + 1] = geometry.positions[sourceVertex * 3 + 1];
|
|
3221
|
+
vertices[index * 6 + 2] = geometry.positions[sourceVertex * 3 + 2];
|
|
3222
|
+
vertices[index * 6 + 3] = geometry.colors?.[sourceVertex * colorStride] ?? 1;
|
|
3223
|
+
vertices[index * 6 + 4] = geometry.colors?.[sourceVertex * colorStride + 1] ?? 1;
|
|
3224
|
+
vertices[index * 6 + 5] = geometry.colors?.[sourceVertex * colorStride + 2] ?? 1;
|
|
3225
|
+
}
|
|
3226
|
+
this.device.queue.writeBuffer(entry.vertexBuffer, range.byteOffset * 2, vertices);
|
|
3227
|
+
updated += range.byteLength;
|
|
3228
|
+
}
|
|
3229
|
+
} catch (error) {
|
|
3230
|
+
throw new ICEEngineError("ICE3_GPU_RESOURCE_FAILED", "ICE3_GPU_RESOURCE_FAILED: geometry update failed", { cause: error });
|
|
3231
|
+
}
|
|
3232
|
+
entry.geometry = geometry;
|
|
3233
|
+
entry.lastUsedFrame = this.frameId;
|
|
3234
|
+
this.updatedBytes += updated;
|
|
3235
|
+
}
|
|
3236
|
+
canReuse(existing, geometry) {
|
|
3237
|
+
if (existing.geometry === geometry) {
|
|
3238
|
+
return true;
|
|
3239
|
+
}
|
|
3240
|
+
if (!this.sameTopology(existing, geometry)) {
|
|
3241
|
+
return false;
|
|
3242
|
+
}
|
|
3243
|
+
return (existing.geometry.positions === geometry.positions || equalNumberArrays2(existing.geometry.positions, geometry.positions)) && equalOptionalNumberArrays2(existing.geometry.colors, geometry.colors) && equalOptionalNumberArrays2(existing.geometry.semanticIndices, geometry.semanticIndices) && equalOptionalNumberArrays2(existing.geometry.edgeIndices, geometry.edgeIndices) || existing.attributeDigest !== "" && existing.attributeDigest === geometry.identity?.attributeDigest;
|
|
3244
|
+
}
|
|
3245
|
+
sameTopology(existing, geometry) {
|
|
3246
|
+
return existing.geometry.indices.constructor === geometry.indices.constructor && existing.geometry.indices.length === geometry.indices.length && (existing.geometry.indices === geometry.indices || existing.topologyDigest !== "" && existing.topologyDigest === geometry.identity?.topologyDigest);
|
|
3247
|
+
}
|
|
3248
|
+
keyFor(geometry) {
|
|
3249
|
+
const identity = geometry.identity?.geometryId;
|
|
3250
|
+
if (identity) {
|
|
3251
|
+
return `identity:${identity}`;
|
|
3252
|
+
}
|
|
3253
|
+
const id = geometry.id;
|
|
3254
|
+
if (typeof id === "string" && id.trim()) {
|
|
3255
|
+
const cached = this.legacyKeys.get(geometry);
|
|
3256
|
+
if (cached)
|
|
3257
|
+
return cached;
|
|
3258
|
+
const key2 = [
|
|
3259
|
+
"content",
|
|
3260
|
+
geometry.positions.length,
|
|
3261
|
+
geometry.indices.length,
|
|
3262
|
+
contentFingerprint2(geometry.positions),
|
|
3263
|
+
contentFingerprint2(geometry.indices),
|
|
3264
|
+
geometry.colors ? contentFingerprint2(geometry.colors) : 0,
|
|
3265
|
+
geometry.edgeIndices ? contentFingerprint2(geometry.edgeIndices) : 0,
|
|
3266
|
+
geometry.semanticIndices ? contentFingerprint2(geometry.semanticIndices) : 0
|
|
3267
|
+
].join(":");
|
|
3268
|
+
this.legacyKeys.set(geometry, key2);
|
|
3269
|
+
return key2;
|
|
3270
|
+
}
|
|
3271
|
+
const existing = this.objectKeys.get(geometry);
|
|
3272
|
+
if (existing) {
|
|
3273
|
+
return existing;
|
|
3274
|
+
}
|
|
3275
|
+
const key = `object:${this.objectKeySequence}`;
|
|
3276
|
+
this.objectKeySequence += 1;
|
|
3277
|
+
this.objectKeys.set(geometry, key);
|
|
3278
|
+
return key;
|
|
3279
|
+
}
|
|
2571
3280
|
};
|
|
3281
|
+
function contentFingerprint2(values) {
|
|
3282
|
+
let hash = 2166136261;
|
|
3283
|
+
const bytes = new Uint8Array(values.buffer, values.byteOffset, values.byteLength);
|
|
3284
|
+
for (const byte of bytes) {
|
|
3285
|
+
hash ^= byte;
|
|
3286
|
+
hash = Math.imul(hash, 16777619) >>> 0;
|
|
3287
|
+
}
|
|
3288
|
+
return hash;
|
|
3289
|
+
}
|
|
3290
|
+
function equalNumberArrays2(left, right) {
|
|
3291
|
+
return left.length === right.length && left.every((value, index) => value === right[index]);
|
|
3292
|
+
}
|
|
3293
|
+
function equalOptionalNumberArrays2(left, right) {
|
|
3294
|
+
if (left === void 0 || right === void 0) {
|
|
3295
|
+
return left === right;
|
|
3296
|
+
}
|
|
3297
|
+
return left.length === right.length && left.every((value, index) => value === right[index]);
|
|
3298
|
+
}
|
|
2572
3299
|
|
|
2573
3300
|
// packages/renderer/dist/WebGPUInstanceCache.js
|
|
2574
3301
|
var WebGPUInstanceCache = class {
|
|
@@ -2664,7 +3391,7 @@ function sameBytes(left, right) {
|
|
|
2664
3391
|
|
|
2665
3392
|
// packages/renderer/dist/WebGPURenderPipeline.js
|
|
2666
3393
|
var WEBGPU_UNIFORM_STRIDE = 256;
|
|
2667
|
-
var WEBGPU_UNIFORM_BYTES =
|
|
3394
|
+
var WEBGPU_UNIFORM_BYTES = 144;
|
|
2668
3395
|
var SHADER_SOURCE = (selectionMask, storageInstances, indirectInstances) => `
|
|
2669
3396
|
struct FrameUniforms {
|
|
2670
3397
|
worldViewProjection: mat4x4<f32>,
|
|
@@ -2680,6 +3407,7 @@ struct FrameUniforms {
|
|
|
2680
3407
|
useSelectionMask: f32,
|
|
2681
3408
|
pageBase: f32,
|
|
2682
3409
|
useIndirect: f32,
|
|
3410
|
+
useLighting: f32,
|
|
2683
3411
|
}
|
|
2684
3412
|
|
|
2685
3413
|
${storageInstances ? `
|
|
@@ -2696,6 +3424,7 @@ struct VertexOutput {
|
|
|
2696
3424
|
@builtin(position) position: vec4<f32>,
|
|
2697
3425
|
@location(0) color: vec4<f32>,
|
|
2698
3426
|
@location(1) @interpolate(flat) semantic: u32,
|
|
3427
|
+
@location(2) worldPosition: vec4<f32>,
|
|
2699
3428
|
}
|
|
2700
3429
|
|
|
2701
3430
|
@group(0) @binding(0) var<uniform> frame: FrameUniforms;
|
|
@@ -2748,6 +3477,7 @@ fn vertexMain(
|
|
|
2748
3477
|
mix(vec3<f32>(1.0), color, frame.useVertexColor) * frame.color.rgb,
|
|
2749
3478
|
frame.color.a
|
|
2750
3479
|
);
|
|
3480
|
+
output.worldPosition = worldPosition;
|
|
2751
3481
|
output.semantic = select(
|
|
2752
3482
|
vertexSemantic,
|
|
2753
3483
|
instanceSemantic,
|
|
@@ -2791,7 +3521,24 @@ fn fragmentMain(input: VertexOutput) -> @location(0) vec4<f32> {
|
|
|
2791
3521
|
);
|
|
2792
3522
|
}
|
|
2793
3523
|
}` : ""}
|
|
2794
|
-
|
|
3524
|
+
var outputColor = input.color;
|
|
3525
|
+
if (frame.useLighting > 0.5) {
|
|
3526
|
+
let normal = normalize(cross(
|
|
3527
|
+
dpdx(input.worldPosition.xyz),
|
|
3528
|
+
dpdy(input.worldPosition.xyz)
|
|
3529
|
+
));
|
|
3530
|
+
let lightDirection = normalize(vec3<f32>(-0.35, 0.85, 0.4));
|
|
3531
|
+
let diffuse = dot(normal, lightDirection);
|
|
3532
|
+
let key = max(diffuse, 0.0) + 0.35 * max(-diffuse, 0.0);
|
|
3533
|
+
let sky = 0.5 + 0.5 * normal.y;
|
|
3534
|
+
let shade = clamp(
|
|
3535
|
+
0.72 + 0.72 * key + 0.18 * sky,
|
|
3536
|
+
0.92,
|
|
3537
|
+
1.42
|
|
3538
|
+
);
|
|
3539
|
+
outputColor = vec4<f32>(outputColor.rgb * shade, outputColor.a);
|
|
3540
|
+
}
|
|
3541
|
+
return outputColor;
|
|
2795
3542
|
}
|
|
2796
3543
|
`;
|
|
2797
3544
|
var VERTEX_LAYOUT = [
|
|
@@ -2946,7 +3693,14 @@ var WebGPURenderPipeline = class _WebGPURenderPipeline {
|
|
|
2946
3693
|
depthStencil: {
|
|
2947
3694
|
format: "depth24plus",
|
|
2948
3695
|
depthCompare: "less-equal",
|
|
2949
|
-
depthWriteEnabled: !selection
|
|
3696
|
+
depthWriteEnabled: !selection,
|
|
3697
|
+
// Selected triangles are redrawn after opaque geometry. Pull that
|
|
3698
|
+
// overlay slightly forward instead of competing at an equal depth.
|
|
3699
|
+
...selection && topology === "triangle-list" ? {
|
|
3700
|
+
depthBias: -1,
|
|
3701
|
+
depthBiasSlopeScale: -1,
|
|
3702
|
+
depthBiasClamp: 0
|
|
3703
|
+
} : {}
|
|
2950
3704
|
},
|
|
2951
3705
|
multisample: { count: 1 }
|
|
2952
3706
|
});
|
|
@@ -2995,12 +3749,12 @@ var WebGPUTimestampProfiler = class {
|
|
|
2995
3749
|
querySetFor(frameId) {
|
|
2996
3750
|
return this.slotFor(frameId).querySet;
|
|
2997
3751
|
}
|
|
2998
|
-
resolveFrame(
|
|
3752
|
+
resolveFrame(encoder2, frameId) {
|
|
2999
3753
|
const slot = this.slotFor(frameId);
|
|
3000
3754
|
if (slot.pending) {
|
|
3001
3755
|
throw new Error("ICE3_TIMESTAMP_SLOT_BUSY");
|
|
3002
3756
|
}
|
|
3003
|
-
|
|
3757
|
+
encoder2.resolveQuerySet(slot.querySet, 0, 2, slot.buffer, 0);
|
|
3004
3758
|
this.pending.push(this.slotIndex(frameId));
|
|
3005
3759
|
slot.pending = true;
|
|
3006
3760
|
}
|
|
@@ -3168,6 +3922,7 @@ var WebGPUDrawStorage = class {
|
|
|
3168
3922
|
this.uniformBuffer[offset + 29] = draw.useSelectionMask ? 1 : 0;
|
|
3169
3923
|
this.uniformBuffer[offset + 30] = draw.pageBase ?? 0;
|
|
3170
3924
|
this.uniformBuffer[offset + 31] = draw.useIndirect ? 1 : 0;
|
|
3925
|
+
this.uniformBuffer[offset + 32] = draw.useLighting ? 1 : 0;
|
|
3171
3926
|
this.count += 1;
|
|
3172
3927
|
return index;
|
|
3173
3928
|
}
|
|
@@ -3252,13 +4007,13 @@ var WebGPUBundleCache = class {
|
|
|
3252
4007
|
get(key, record3) {
|
|
3253
4008
|
if (this.key === key && this.bundle)
|
|
3254
4009
|
return this.bundle;
|
|
3255
|
-
const
|
|
4010
|
+
const encoder2 = this.device.createRenderBundleEncoder({
|
|
3256
4011
|
label: `ice3:bundle:${key}`,
|
|
3257
4012
|
colorFormats: [this.format],
|
|
3258
4013
|
depthStencilFormat: "depth24plus"
|
|
3259
4014
|
});
|
|
3260
|
-
record3(
|
|
3261
|
-
const bundle =
|
|
4015
|
+
record3(encoder2);
|
|
4016
|
+
const bundle = encoder2.finish();
|
|
3262
4017
|
this.key = key;
|
|
3263
4018
|
this.bundle = bundle;
|
|
3264
4019
|
this.recreations += 1;
|
|
@@ -3596,8 +4351,8 @@ var WebGPUVisibilityEncoder = class _WebGPUVisibilityEncoder {
|
|
|
3596
4351
|
this.entries.set(page.buffer, entries);
|
|
3597
4352
|
return entry;
|
|
3598
4353
|
}
|
|
3599
|
-
encode(
|
|
3600
|
-
|
|
4354
|
+
encode(encoder2, entry, page, uniformBuffer, uniformOffset) {
|
|
4355
|
+
encoder2.clearBuffer(entry.indirectBuffer, 4, 4);
|
|
3601
4356
|
const bindGroup = this.device.createBindGroup({
|
|
3602
4357
|
label: "ice3:visibility-bindings",
|
|
3603
4358
|
layout: this.bindGroupLayout,
|
|
@@ -3628,7 +4383,7 @@ var WebGPUVisibilityEncoder = class _WebGPUVisibilityEncoder {
|
|
|
3628
4383
|
}
|
|
3629
4384
|
]
|
|
3630
4385
|
});
|
|
3631
|
-
const pass =
|
|
4386
|
+
const pass = encoder2.beginComputePass({ label: "ice3:visibility-pass" });
|
|
3632
4387
|
pass.setPipeline(this.pipeline);
|
|
3633
4388
|
pass.setBindGroup(0, bindGroup, [uniformOffset]);
|
|
3634
4389
|
pass.dispatchWorkgroups(Math.ceil(page.instanceCount / 64));
|
|
@@ -3640,9 +4395,9 @@ var WebGPUVisibilityEncoder = class _WebGPUVisibilityEncoder {
|
|
|
3640
4395
|
size: 4,
|
|
3641
4396
|
usage: 1 | 8
|
|
3642
4397
|
});
|
|
3643
|
-
const
|
|
3644
|
-
|
|
3645
|
-
this.device.queue.submit([
|
|
4398
|
+
const encoder2 = this.device.createCommandEncoder();
|
|
4399
|
+
encoder2.copyBufferToBuffer(entry.indirectBuffer, 4, readback, 0, 4);
|
|
4400
|
+
this.device.queue.submit([encoder2.finish()]);
|
|
3646
4401
|
try {
|
|
3647
4402
|
await readback.mapAsync(1);
|
|
3648
4403
|
const visibleInstances = new Uint32Array(readback.getMappedRange())[0];
|
|
@@ -3815,8 +4570,8 @@ var WebGPUPicker = class {
|
|
|
3815
4570
|
}
|
|
3816
4571
|
}]
|
|
3817
4572
|
});
|
|
3818
|
-
const
|
|
3819
|
-
const pass =
|
|
4573
|
+
const encoder2 = this.device.createCommandEncoder();
|
|
4574
|
+
const pass = encoder2.beginRenderPass({
|
|
3820
4575
|
colorAttachments: [{
|
|
3821
4576
|
view: this.target.createView(),
|
|
3822
4577
|
clearValue: { r: 0, g: 0, b: 0, a: 1 },
|
|
@@ -3848,7 +4603,7 @@ var WebGPUPicker = class {
|
|
|
3848
4603
|
size: 256,
|
|
3849
4604
|
usage: COPY_DST | MAP_READ
|
|
3850
4605
|
});
|
|
3851
|
-
|
|
4606
|
+
encoder2.copyTextureToBuffer({
|
|
3852
4607
|
texture: this.target,
|
|
3853
4608
|
origin: {
|
|
3854
4609
|
x: pixelX,
|
|
@@ -3860,7 +4615,7 @@ var WebGPUPicker = class {
|
|
|
3860
4615
|
bytesPerRow: 256,
|
|
3861
4616
|
rowsPerImage: 1
|
|
3862
4617
|
}, { width: 1, height: 1, depthOrArrayLayers: 1 });
|
|
3863
|
-
this.device.queue.submit([
|
|
4618
|
+
this.device.queue.submit([encoder2.finish()]);
|
|
3864
4619
|
let pixel;
|
|
3865
4620
|
try {
|
|
3866
4621
|
await readback.mapAsync(MAP_READ);
|
|
@@ -4214,6 +4969,12 @@ var WebGPURendererBackend = class _WebGPURendererBackend {
|
|
|
4214
4969
|
prepareBatchGeometry(geometry) {
|
|
4215
4970
|
this.requireGeometryCache().pin(geometry);
|
|
4216
4971
|
}
|
|
4972
|
+
prepareGeometry(geometry, update) {
|
|
4973
|
+
this.requireGeometryCache().pin(geometry, update);
|
|
4974
|
+
}
|
|
4975
|
+
releaseGeometry(geometry) {
|
|
4976
|
+
this.geometryCache?.unpin(geometry);
|
|
4977
|
+
}
|
|
4217
4978
|
releaseBatchGeometry(geometry) {
|
|
4218
4979
|
this.geometryCache?.unpin(geometry);
|
|
4219
4980
|
}
|
|
@@ -4341,6 +5102,7 @@ var WebGPURendererBackend = class _WebGPURendererBackend {
|
|
|
4341
5102
|
color: colorToRgba(batch.material.color),
|
|
4342
5103
|
useVertexColor: Boolean(batch.geometry.colors),
|
|
4343
5104
|
useInstancing: true,
|
|
5105
|
+
useLighting: batch.material.shading === "directional" || batch.material.shading === "water",
|
|
4344
5106
|
pipeline: pagePipeline.triangle,
|
|
4345
5107
|
useSelectionMask: selectionMaskEnabled,
|
|
4346
5108
|
storagePage: page,
|
|
@@ -4368,6 +5130,7 @@ var WebGPURendererBackend = class _WebGPURendererBackend {
|
|
|
4368
5130
|
color: colorToRgba(batch.material.color),
|
|
4369
5131
|
useVertexColor: Boolean(batch.geometry.colors),
|
|
4370
5132
|
useInstancing: true,
|
|
5133
|
+
useLighting: batch.material.shading === "directional" || batch.material.shading === "water",
|
|
4371
5134
|
pipeline: this.pipeline.triangle,
|
|
4372
5135
|
useSelectionMask: selectionMaskEnabled
|
|
4373
5136
|
}, viewProjection.elements, batch.semanticIds);
|
|
@@ -4412,6 +5175,7 @@ var WebGPURendererBackend = class _WebGPURendererBackend {
|
|
|
4412
5175
|
color: colorToRgba(draw.batch.material.color),
|
|
4413
5176
|
useVertexColor: Boolean(draw.batch.geometry.colors),
|
|
4414
5177
|
useInstancing: false,
|
|
5178
|
+
useLighting: draw.batch.material.shading === "directional" || draw.batch.material.shading === "water",
|
|
4415
5179
|
pipeline: this.pipeline.triangle,
|
|
4416
5180
|
useSelectionMask: selectionMaskEnabled
|
|
4417
5181
|
}, matrix, draw.batch.semanticSelectionIds);
|
|
@@ -4459,6 +5223,7 @@ var WebGPURendererBackend = class _WebGPURendererBackend {
|
|
|
4459
5223
|
color: colorToRgba(renderable.mesh.material.color),
|
|
4460
5224
|
useVertexColor: Boolean(renderable.mesh.geometry.colors),
|
|
4461
5225
|
useInstancing: false,
|
|
5226
|
+
useLighting: renderable.mesh.material.shading === "directional" || renderable.mesh.material.shading === "water",
|
|
4462
5227
|
pipeline: this.pipeline.triangle,
|
|
4463
5228
|
useSelectionMask: selectionMaskEnabled
|
|
4464
5229
|
}, matrix, [renderable.mesh.selectionId]);
|
|
@@ -4527,26 +5292,26 @@ var WebGPURendererBackend = class _WebGPURendererBackend {
|
|
|
4527
5292
|
this.frameBindGroupKey = bindGroupKey;
|
|
4528
5293
|
}
|
|
4529
5294
|
const bindGroup = this.frameBindGroup;
|
|
4530
|
-
const encodeDraws = (
|
|
5295
|
+
const encodeDraws = (encoder3) => {
|
|
4531
5296
|
for (let index = 0; index < drawList.length; index += 1) {
|
|
4532
5297
|
const draw = drawList[index];
|
|
4533
|
-
|
|
5298
|
+
encoder3.setPipeline(draw.pipeline);
|
|
4534
5299
|
const drawBindGroup = draw.storagePage ? this.storageBindGroupFor(draw.storagePage, draw.visibilityEntry?.visibleBuffer) : bindGroup;
|
|
4535
|
-
|
|
5300
|
+
encoder3.setBindGroup(0, drawBindGroup, [
|
|
4536
5301
|
index * WEBGPU_UNIFORM_STRIDE
|
|
4537
5302
|
]);
|
|
4538
|
-
|
|
5303
|
+
encoder3.setVertexBuffer(0, draw.geometry.vertexBuffer);
|
|
4539
5304
|
if (draw.storagePage) {
|
|
4540
|
-
|
|
5305
|
+
encoder3.setVertexBuffer(1, draw.semanticBuffer);
|
|
4541
5306
|
} else {
|
|
4542
|
-
|
|
4543
|
-
|
|
5307
|
+
encoder3.setVertexBuffer(1, draw.instanceBuffer);
|
|
5308
|
+
encoder3.setVertexBuffer(2, draw.semanticBuffer);
|
|
4544
5309
|
}
|
|
4545
|
-
|
|
5310
|
+
encoder3.setIndexBuffer(draw.edge ? draw.geometry.edgeIndexBuffer : draw.geometry.triangleIndexBuffer, draw.geometry.indexFormat);
|
|
4546
5311
|
if (draw.visibilityEntry) {
|
|
4547
|
-
|
|
5312
|
+
encoder3.drawIndexedIndirect(draw.visibilityEntry.indirectBuffer, 0);
|
|
4548
5313
|
} else {
|
|
4549
|
-
|
|
5314
|
+
encoder3.drawIndexed(draw.edge ? draw.geometry.edgeCount : draw.geometry.triangleCount, draw.instanceCount, 0, 0, draw.storagePage?.firstInstance ?? 0);
|
|
4550
5315
|
}
|
|
4551
5316
|
}
|
|
4552
5317
|
};
|
|
@@ -4572,7 +5337,7 @@ var WebGPURendererBackend = class _WebGPURendererBackend {
|
|
|
4572
5337
|
}
|
|
4573
5338
|
}
|
|
4574
5339
|
const targetTexture = context.getCurrentTexture();
|
|
4575
|
-
const
|
|
5340
|
+
const encoder2 = this.device.createCommandEncoder();
|
|
4576
5341
|
if (this.gpuDrivenActive) {
|
|
4577
5342
|
const encodedVisibility = /* @__PURE__ */ new Set();
|
|
4578
5343
|
for (let index = 0; index < drawList.length; index += 1) {
|
|
@@ -4581,12 +5346,12 @@ var WebGPURendererBackend = class _WebGPURendererBackend {
|
|
|
4581
5346
|
continue;
|
|
4582
5347
|
}
|
|
4583
5348
|
encodedVisibility.add(draw.visibilityEntry);
|
|
4584
|
-
this.visibilityEncoder.encode(
|
|
5349
|
+
this.visibilityEncoder.encode(encoder2, draw.visibilityEntry, draw.storagePage, this.uniformBuffer, index * WEBGPU_UNIFORM_STRIDE);
|
|
4585
5350
|
}
|
|
4586
5351
|
}
|
|
4587
5352
|
const timestampProfiler = this.captureRequest || !this.timestampProfilingEnabled ? null : this.timestampProfiler;
|
|
4588
5353
|
const timestampQuerySet = timestampProfiler?.querySetFor(frame.frameId);
|
|
4589
|
-
const pass =
|
|
5354
|
+
const pass = encoder2.beginRenderPass({
|
|
4590
5355
|
colorAttachments: [{
|
|
4591
5356
|
view: targetTexture.createView(),
|
|
4592
5357
|
clearValue: {
|
|
@@ -4627,10 +5392,10 @@ var WebGPURendererBackend = class _WebGPURendererBackend {
|
|
|
4627
5392
|
this.frameStats.drawCalls += drawList.length;
|
|
4628
5393
|
pass.end();
|
|
4629
5394
|
if (timestampProfiler) {
|
|
4630
|
-
timestampProfiler.resolveFrame(
|
|
5395
|
+
timestampProfiler.resolveFrame(encoder2, frame.frameId);
|
|
4631
5396
|
}
|
|
4632
5397
|
if (this.captureRequest) {
|
|
4633
|
-
|
|
5398
|
+
encoder2.copyTextureToBuffer({ texture: targetTexture }, {
|
|
4634
5399
|
buffer: this.captureRequest.buffer,
|
|
4635
5400
|
bytesPerRow: this.captureRequest.bytesPerRow,
|
|
4636
5401
|
rowsPerImage: this.canvas.height
|
|
@@ -4641,7 +5406,7 @@ var WebGPURendererBackend = class _WebGPURendererBackend {
|
|
|
4641
5406
|
});
|
|
4642
5407
|
this.captureRequest = null;
|
|
4643
5408
|
}
|
|
4644
|
-
const commandBuffer =
|
|
5409
|
+
const commandBuffer = encoder2.finish();
|
|
4645
5410
|
this.profiler.mark("encode");
|
|
4646
5411
|
this.device.queue.submit([commandBuffer]);
|
|
4647
5412
|
this.profiler.mark("submit");
|
|
@@ -5493,18 +6258,18 @@ var ICEInputRouter = class {
|
|
|
5493
6258
|
function pickSceneNode(scene, camera, viewport, point) {
|
|
5494
6259
|
const eye = camera.getPosition();
|
|
5495
6260
|
const target = camera.getTarget();
|
|
5496
|
-
const forward =
|
|
6261
|
+
const forward = normalize3([
|
|
5497
6262
|
target.x - eye.x,
|
|
5498
6263
|
target.y - eye.y,
|
|
5499
6264
|
target.z - eye.z
|
|
5500
6265
|
]);
|
|
5501
|
-
const right =
|
|
5502
|
-
const up =
|
|
6266
|
+
const right = normalize3(cross3(forward, [0, 1, 0]));
|
|
6267
|
+
const up = normalize3(cross3(right, forward));
|
|
5503
6268
|
const aspect = viewport.width / Math.max(viewport.height, 1);
|
|
5504
6269
|
const tangent = Math.tan(camera.fov * Math.PI / 360);
|
|
5505
6270
|
const ndcX = point.x / viewport.width * 2 - 1;
|
|
5506
6271
|
const ndcY = 1 - point.y / viewport.height * 2;
|
|
5507
|
-
const direction =
|
|
6272
|
+
const direction = normalize3([
|
|
5508
6273
|
forward[0] + right[0] * ndcX * aspect * tangent + up[0] * ndcY * tangent,
|
|
5509
6274
|
forward[1] + right[1] * ndcX * aspect * tangent + up[1] * ndcY * tangent,
|
|
5510
6275
|
forward[2] + right[2] * ndcX * aspect * tangent + up[2] * ndcY * tangent
|
|
@@ -5537,11 +6302,11 @@ function pickSceneNode(scene, camera, viewport, point) {
|
|
|
5537
6302
|
visit2(scene, true);
|
|
5538
6303
|
return nearest;
|
|
5539
6304
|
}
|
|
5540
|
-
var
|
|
6305
|
+
var normalize3 = (value) => {
|
|
5541
6306
|
const length = Math.hypot(...value);
|
|
5542
6307
|
return length > 0 ? [value[0] / length, value[1] / length, value[2] / length] : [0, 0, 0];
|
|
5543
6308
|
};
|
|
5544
|
-
var
|
|
6309
|
+
var cross3 = (left, right) => [
|
|
5545
6310
|
left[1] * right[2] - left[2] * right[1],
|
|
5546
6311
|
left[2] * right[0] - left[0] * right[2],
|
|
5547
6312
|
left[0] * right[1] - left[1] * right[0]
|
|
@@ -5997,6 +6762,17 @@ var ICE3 = class extends ICEEventDispatcher {
|
|
|
5997
6762
|
this.backend.releaseBatchGeometry?.(geometry);
|
|
5998
6763
|
return this;
|
|
5999
6764
|
}
|
|
6765
|
+
prepareGeometry(geometry, update) {
|
|
6766
|
+
if (!this.backend.prepareGeometry) {
|
|
6767
|
+
throw new ICEEngineError("ICE3_RENDER_GEOMETRY_UNAVAILABLE", "The active renderer cannot stage geometry updates");
|
|
6768
|
+
}
|
|
6769
|
+
this.backend.prepareGeometry(geometry, update);
|
|
6770
|
+
return this;
|
|
6771
|
+
}
|
|
6772
|
+
releaseGeometry(geometry) {
|
|
6773
|
+
this.backend.releaseGeometry?.(geometry);
|
|
6774
|
+
return this;
|
|
6775
|
+
}
|
|
6000
6776
|
addLayer(layer) {
|
|
6001
6777
|
this.assertActive();
|
|
6002
6778
|
if (this.layers.some((item) => item.id === layer.id)) {
|
|
@@ -6460,7 +7236,11 @@ var MAX_FRAME_DELTA_MS = 64;
|
|
|
6460
7236
|
var SETTLE_EPSILON = 1e-5;
|
|
6461
7237
|
var WHEEL_LINE_HEIGHT_PX = 16;
|
|
6462
7238
|
var MAX_WHEEL_STEP_PX = 240;
|
|
6463
|
-
var
|
|
7239
|
+
var ZOOM_DISTANCE_PER_HOME_PIXEL = 1e-3;
|
|
7240
|
+
var PAN_SPEED_PER_HOME_DISTANCE = 0.75;
|
|
7241
|
+
var MIN_KEY_TAP_MS = 16;
|
|
7242
|
+
var MIN_ZOOM_FRACTION = 5e-3;
|
|
7243
|
+
var ABSOLUTE_MIN_ZOOM_DISTANCE = 0.2;
|
|
6464
7244
|
var NAVIGATION_KEYS = /* @__PURE__ */ new Set([
|
|
6465
7245
|
"KeyW",
|
|
6466
7246
|
"KeyA",
|
|
@@ -6478,6 +7258,8 @@ var ICEOrbitController = class {
|
|
|
6478
7258
|
yaw;
|
|
6479
7259
|
pitch;
|
|
6480
7260
|
distance;
|
|
7261
|
+
minDistance;
|
|
7262
|
+
maxDistance;
|
|
6481
7263
|
targetYaw;
|
|
6482
7264
|
targetPitch;
|
|
6483
7265
|
targetDistance;
|
|
@@ -6559,7 +7341,12 @@ var ICEOrbitController = class {
|
|
|
6559
7341
|
return;
|
|
6560
7342
|
}
|
|
6561
7343
|
this.prepareMotion();
|
|
7344
|
+
const alreadyPressed = this.pressedKeys.has(input.code);
|
|
6562
7345
|
this.pressedKeys.add(input.code);
|
|
7346
|
+
if (!alreadyPressed && !input.repeat) {
|
|
7347
|
+
this.applyKeyboardMovement(MIN_KEY_TAP_MS);
|
|
7348
|
+
this.applyPosition();
|
|
7349
|
+
}
|
|
6563
7350
|
this.engine.requestRender("camera-keyboard-movement");
|
|
6564
7351
|
};
|
|
6565
7352
|
onKeyUp = (event) => {
|
|
@@ -6603,7 +7390,9 @@ var ICEOrbitController = class {
|
|
|
6603
7390
|
const offsetY = eye.y - target.y;
|
|
6604
7391
|
const offsetZ = eye.z - target.z;
|
|
6605
7392
|
this.target = target.toArray();
|
|
6606
|
-
this.distance = Math.max(
|
|
7393
|
+
this.distance = Math.max(ABSOLUTE_MIN_ZOOM_DISTANCE, Math.hypot(offsetX, offsetY, offsetZ));
|
|
7394
|
+
this.maxDistance = this.distance;
|
|
7395
|
+
this.minDistance = Math.min(this.maxDistance, Math.max(ABSOLUTE_MIN_ZOOM_DISTANCE, this.maxDistance * MIN_ZOOM_FRACTION));
|
|
6607
7396
|
this.yaw = Math.atan2(offsetX, offsetZ);
|
|
6608
7397
|
this.pitch = Math.asin(clamp(offsetY / this.distance, -1, 1));
|
|
6609
7398
|
this.targetYaw = this.yaw;
|
|
@@ -6623,7 +7412,7 @@ var ICEOrbitController = class {
|
|
|
6623
7412
|
];
|
|
6624
7413
|
const radius = Math.max(0.1, Math.hypot(target.bounds.max[0] - target.bounds.min[0], target.bounds.max[1] - target.bounds.min[1], target.bounds.max[2] - target.bounds.min[2]) / 2);
|
|
6625
7414
|
this.target = center;
|
|
6626
|
-
this.targetDistance = radius / Math.sin(this.camera.fov * Math.PI / 360) * padding;
|
|
7415
|
+
this.targetDistance = clamp(radius / Math.sin(this.camera.fov * Math.PI / 360) * padding, this.minDistance, this.maxDistance);
|
|
6627
7416
|
if (options.animate === false) {
|
|
6628
7417
|
this.distance = this.targetDistance;
|
|
6629
7418
|
this.applyPosition();
|
|
@@ -6642,7 +7431,7 @@ var ICEOrbitController = class {
|
|
|
6642
7431
|
zoomBy(delta) {
|
|
6643
7432
|
this.assertActive();
|
|
6644
7433
|
this.prepareMotion();
|
|
6645
|
-
this.targetDistance = clamp(this.targetDistance *
|
|
7434
|
+
this.targetDistance = clamp(this.targetDistance + delta * this.maxDistance * ZOOM_DISTANCE_PER_HOME_PIXEL, this.minDistance, this.maxDistance);
|
|
6646
7435
|
this.engine.requestRender("camera-zoom");
|
|
6647
7436
|
return this;
|
|
6648
7437
|
}
|
|
@@ -6688,7 +7477,7 @@ var ICEOrbitController = class {
|
|
|
6688
7477
|
if (magnitude === 0) {
|
|
6689
7478
|
return;
|
|
6690
7479
|
}
|
|
6691
|
-
const distance2 = this.
|
|
7480
|
+
const distance2 = this.maxDistance * PAN_SPEED_PER_HOME_DISTANCE * (deltaMs / 1e3) / magnitude;
|
|
6692
7481
|
const sinYaw = Math.sin(this.yaw);
|
|
6693
7482
|
const cosYaw = Math.cos(this.yaw);
|
|
6694
7483
|
const sinPitch = Math.sin(this.pitch);
|
|
@@ -6754,7 +7543,8 @@ function buildAssetScene(snapshot) {
|
|
|
6754
7543
|
}));
|
|
6755
7544
|
const materials = snapshot.materials.map((material) => new ICEBasicMaterial({
|
|
6756
7545
|
color: material.baseColor,
|
|
6757
|
-
...material.edgeColor ? { edgeColor: material.edgeColor } : {}
|
|
7546
|
+
...material.edgeColor ? { edgeColor: material.edgeColor } : {},
|
|
7547
|
+
...material.shading ? { shading: material.shading } : {}
|
|
6758
7548
|
}));
|
|
6759
7549
|
const meshesByNode = /* @__PURE__ */ new Map();
|
|
6760
7550
|
for (const mesh of snapshot.meshes) {
|
|
@@ -6829,6 +7619,82 @@ function buildAssetScene(snapshot) {
|
|
|
6829
7619
|
stats: snapshot.stats
|
|
6830
7620
|
};
|
|
6831
7621
|
}
|
|
7622
|
+
function buildPreviewPacketScene(packet, previousGeometries = /* @__PURE__ */ new Map()) {
|
|
7623
|
+
const geometries = /* @__PURE__ */ new Map();
|
|
7624
|
+
const geometryUpdates = /* @__PURE__ */ new Map();
|
|
7625
|
+
for (const source of packet.geometries) {
|
|
7626
|
+
const previous = previousGeometries.get(source.geometryId);
|
|
7627
|
+
if (previous && previous.identity.topologyDigest === source.topologyDigest && previous.identity.attributeDigest === source.attributeDigest) {
|
|
7628
|
+
geometries.set(source.geometryId, previous);
|
|
7629
|
+
continue;
|
|
7630
|
+
}
|
|
7631
|
+
const identity = Object.freeze({
|
|
7632
|
+
geometryId: source.geometryId,
|
|
7633
|
+
topologyDigest: source.topologyDigest,
|
|
7634
|
+
attributeDigest: source.attributeDigest
|
|
7635
|
+
});
|
|
7636
|
+
const geometry = new ICEIndexedGeometry({
|
|
7637
|
+
id: source.residentGeometryId ?? source.geometryId,
|
|
7638
|
+
identity,
|
|
7639
|
+
positions: source.positions,
|
|
7640
|
+
indices: source.indices
|
|
7641
|
+
});
|
|
7642
|
+
geometries.set(source.geometryId, geometry);
|
|
7643
|
+
const attributeRanges = source.ranges.filter((range) => range.usage === "vertex_attributes");
|
|
7644
|
+
const attributeStart = Math.min(...attributeRanges.map((range) => range.byteOffset));
|
|
7645
|
+
geometryUpdates.set(source.geometryId, Object.freeze({
|
|
7646
|
+
positionRanges: Object.freeze(attributeRanges.map((range) => Object.freeze({
|
|
7647
|
+
byteOffset: range.byteOffset - attributeStart,
|
|
7648
|
+
byteLength: range.byteLength
|
|
7649
|
+
})))
|
|
7650
|
+
}));
|
|
7651
|
+
}
|
|
7652
|
+
const materials = new Map(packet.materials.map((material) => [
|
|
7653
|
+
material.materialId,
|
|
7654
|
+
new ICEBasicMaterial({
|
|
7655
|
+
color: colorHex(material.baseColor)
|
|
7656
|
+
})
|
|
7657
|
+
]));
|
|
7658
|
+
const defaultMaterial = new ICEBasicMaterial({
|
|
7659
|
+
color: "#9aa6b2"
|
|
7660
|
+
});
|
|
7661
|
+
const scene = new ICEScene({ id: `packet:${packet.revision}:scene` });
|
|
7662
|
+
const nodes = /* @__PURE__ */ new Map();
|
|
7663
|
+
for (const source of packet.nodes) {
|
|
7664
|
+
const geometry = source.geometryId ? geometries.get(source.geometryId) : void 0;
|
|
7665
|
+
const material = source.materialId ? materials.get(source.materialId) ?? defaultMaterial : defaultMaterial;
|
|
7666
|
+
const options = {
|
|
7667
|
+
id: source.nodeId,
|
|
7668
|
+
displayName: source.name,
|
|
7669
|
+
selectionId: source.nodeId,
|
|
7670
|
+
matrix: source.matrix
|
|
7671
|
+
};
|
|
7672
|
+
const node = geometry ? new ICEMesh({
|
|
7673
|
+
...options,
|
|
7674
|
+
geometry,
|
|
7675
|
+
material
|
|
7676
|
+
}) : new ICENode(options);
|
|
7677
|
+
node.visible = source.visible;
|
|
7678
|
+
nodes.set(source.nodeId, node);
|
|
7679
|
+
}
|
|
7680
|
+
for (const source of packet.nodes) {
|
|
7681
|
+
const node = nodes.get(source.nodeId);
|
|
7682
|
+
const parent = source.parentId ? nodes.get(source.parentId) : void 0;
|
|
7683
|
+
if (source.parentId && !parent) {
|
|
7684
|
+
throw new Error(`ICE3_PREVIEW_PACKET_INVALID: parent node ${source.parentId} is missing`);
|
|
7685
|
+
}
|
|
7686
|
+
(parent ?? scene).addChild(node);
|
|
7687
|
+
}
|
|
7688
|
+
return {
|
|
7689
|
+
scene,
|
|
7690
|
+
geometries,
|
|
7691
|
+
geometryUpdates
|
|
7692
|
+
};
|
|
7693
|
+
}
|
|
7694
|
+
function colorHex(color) {
|
|
7695
|
+
const channel = (value) => Math.round(Math.max(0, Math.min(1, value)) * 255).toString(16).padStart(2, "0");
|
|
7696
|
+
return `#${channel(color[0])}${channel(color[1])}${channel(color[2])}`;
|
|
7697
|
+
}
|
|
6832
7698
|
|
|
6833
7699
|
// packages/assets/dist/ICEProfileValidator.js
|
|
6834
7700
|
function validatePreparedProfile(json, binary) {
|
|
@@ -6948,9 +7814,9 @@ function validatePreparedStats(json, profile) {
|
|
|
6948
7814
|
let primitiveCount = 0;
|
|
6949
7815
|
let triangleCount = 0;
|
|
6950
7816
|
let edgeSegmentCount = 0;
|
|
6951
|
-
const
|
|
7817
|
+
const selectionIds = /* @__PURE__ */ new Set();
|
|
6952
7818
|
const referencedMeshes = /* @__PURE__ */ new Set();
|
|
6953
|
-
for (const
|
|
7819
|
+
for (const node of nodes) {
|
|
6954
7820
|
if (node.mesh === void 0)
|
|
6955
7821
|
continue;
|
|
6956
7822
|
const mesh = meshes[Number(node.mesh)];
|
|
@@ -6960,7 +7826,10 @@ function validatePreparedStats(json, profile) {
|
|
|
6960
7826
|
for (const primitive of records(mesh.primitives)) {
|
|
6961
7827
|
const mode = Number(primitive.mode ?? 4);
|
|
6962
7828
|
if (mode === 4) {
|
|
6963
|
-
|
|
7829
|
+
const nodeProfile = record(record(node.extras)?.ice3);
|
|
7830
|
+
const selectionId = nodeProfile?.selectionId ?? nodeProfile?.id;
|
|
7831
|
+
if (typeof selectionId === "string")
|
|
7832
|
+
selectionIds.add(selectionId);
|
|
6964
7833
|
}
|
|
6965
7834
|
}
|
|
6966
7835
|
}
|
|
@@ -6986,7 +7855,7 @@ function validatePreparedStats(json, profile) {
|
|
|
6986
7855
|
triangleCount,
|
|
6987
7856
|
edgeSegmentCount,
|
|
6988
7857
|
materialCount: records(json.materials).length,
|
|
6989
|
-
selectionGroupCount: Array.isArray(batchingSelectionIds) ? new Set(batchingSelectionIds).size :
|
|
7858
|
+
selectionGroupCount: Array.isArray(batchingSelectionIds) ? new Set(batchingSelectionIds).size : selectionIds.size
|
|
6990
7859
|
};
|
|
6991
7860
|
for (const [key, value] of Object.entries(expected)) {
|
|
6992
7861
|
if (stats?.[key] !== value) {
|
|
@@ -7076,7 +7945,7 @@ function readPreparedGLB(bytes) {
|
|
|
7076
7945
|
const geometries = [];
|
|
7077
7946
|
const materials = [];
|
|
7078
7947
|
const meshes = [];
|
|
7079
|
-
const
|
|
7948
|
+
const selectionIds = /* @__PURE__ */ new Set();
|
|
7080
7949
|
let primitiveCount = 0;
|
|
7081
7950
|
let triangleCount = 0;
|
|
7082
7951
|
let edgeSegmentCount = 0;
|
|
@@ -7108,11 +7977,13 @@ function readPreparedGLB(bytes) {
|
|
|
7108
7977
|
edgeSegmentCount += (edgeIndices?.length ?? 0) / 2;
|
|
7109
7978
|
const baseMaterial = sourceMaterials[Number(primitive.material)];
|
|
7110
7979
|
const edgeMaterial = edgePrimitive ? sourceMaterials[Number(edgePrimitive.material)] : void 0;
|
|
7980
|
+
const shading = record(record(baseMaterial?.extras)?.ice3)?.shading;
|
|
7111
7981
|
const materialIndex = materials.length;
|
|
7112
7982
|
materials.push({
|
|
7113
7983
|
id: `${meshId}/material/${primitiveIndex}`,
|
|
7114
7984
|
baseColor: materialColor(baseMaterial),
|
|
7115
|
-
...edgeMaterial ? { edgeColor: materialColor(edgeMaterial) } : {}
|
|
7985
|
+
...edgeMaterial ? { edgeColor: materialColor(edgeMaterial) } : {},
|
|
7986
|
+
...shading === "directional" || shading === "water" ? { shading } : {}
|
|
7116
7987
|
});
|
|
7117
7988
|
const bounds = boundsFromAccessor(accessors[positionIndex]);
|
|
7118
7989
|
const geometryIndex = geometries.length;
|
|
@@ -7126,7 +7997,7 @@ function readPreparedGLB(bytes) {
|
|
|
7126
7997
|
bounds
|
|
7127
7998
|
});
|
|
7128
7999
|
for (const entry of nodesForMesh) {
|
|
7129
|
-
|
|
8000
|
+
selectionIds.add(nodes[entry.index].selectionId);
|
|
7130
8001
|
meshes.push({
|
|
7131
8002
|
id: `${meshId}/primitive/${primitiveIndex}/${entry.index}`,
|
|
7132
8003
|
node: entry.index,
|
|
@@ -7144,7 +8015,7 @@ function readPreparedGLB(bytes) {
|
|
|
7144
8015
|
triangleCount,
|
|
7145
8016
|
edgeSegmentCount,
|
|
7146
8017
|
materialCount: sourceMaterials.length,
|
|
7147
|
-
selectionGroupCount:
|
|
8018
|
+
selectionGroupCount: batching ? new Set(batching.semanticSelectionIds).size : selectionIds.size
|
|
7148
8019
|
};
|
|
7149
8020
|
const contract = preparedContract(profile);
|
|
7150
8021
|
return {
|
|
@@ -7168,7 +8039,7 @@ function readBatchingMetadata(profile, sourceNodes, sourceMeshes, sourceMaterial
|
|
|
7168
8039
|
}
|
|
7169
8040
|
const semanticIds = stringArray(raw.semanticIds, "semanticIds");
|
|
7170
8041
|
const semanticNodeIds = stringArray(raw.semanticNodeIds, "semanticNodeIds");
|
|
7171
|
-
const semanticSelectionIds = stringArray(raw.semanticSelectionIds, "semanticSelectionIds");
|
|
8042
|
+
const semanticSelectionIds = stringArray(raw.semanticSelectionIds, "semanticSelectionIds", false);
|
|
7172
8043
|
if (semanticIds.length === 0 || semanticNodeIds.length !== semanticIds.length || semanticSelectionIds.length !== semanticIds.length) {
|
|
7173
8044
|
throw assetError2("ICE3_ASSET_BATCH_INVALID", "semantic arrays are inconsistent");
|
|
7174
8045
|
}
|
|
@@ -7235,8 +8106,8 @@ function readBatchingMetadata(profile, sourceNodes, sourceMeshes, sourceMaterial
|
|
|
7235
8106
|
batches
|
|
7236
8107
|
};
|
|
7237
8108
|
}
|
|
7238
|
-
function stringArray(value, field) {
|
|
7239
|
-
if (!Array.isArray(value) || value.some((entry) => typeof entry !== "string" || !entry.trim()) || new Set(value).size !== value.length) {
|
|
8109
|
+
function stringArray(value, field, unique = true) {
|
|
8110
|
+
if (!Array.isArray(value) || value.some((entry) => typeof entry !== "string" || !entry.trim()) || unique && new Set(value).size !== value.length) {
|
|
7240
8111
|
throw assetError2("ICE3_ASSET_BATCH_INVALID", `${field} is invalid`);
|
|
7241
8112
|
}
|
|
7242
8113
|
return value;
|
|
@@ -7428,7 +8299,9 @@ function materialColor(material) {
|
|
|
7428
8299
|
const color = record(material.pbrMetallicRoughness)?.baseColorFactor;
|
|
7429
8300
|
if (!Array.isArray(color) || color.length < 3)
|
|
7430
8301
|
return "#808080";
|
|
7431
|
-
|
|
8302
|
+
const alpha = color.length >= 4 ? Number(color[3]) : 1;
|
|
8303
|
+
const channels = color.slice(0, alpha < 1 ? 4 : 3).map((value) => Math.round(Math.min(1, Math.max(0, Number(value))) * 255).toString(16).padStart(2, "0"));
|
|
8304
|
+
return `#${channels.join("")}`;
|
|
7432
8305
|
}
|
|
7433
8306
|
function integers(value) {
|
|
7434
8307
|
return Array.isArray(value) ? value.filter((entry) => Number.isInteger(entry)) : [];
|
|
@@ -7443,6 +8316,494 @@ function assetError2(code, message) {
|
|
|
7443
8316
|
return new Error(`${code}: ${message}`);
|
|
7444
8317
|
}
|
|
7445
8318
|
|
|
8319
|
+
// packages/assets/dist/ICEPreviewGeometryPacket.js
|
|
8320
|
+
var MAGIC = "ICE3PGP1";
|
|
8321
|
+
var HEADER_BYTES = 120;
|
|
8322
|
+
var MANIFEST_LENGTH_OFFSET = 40;
|
|
8323
|
+
var BINARY_LENGTH_OFFSET = 48;
|
|
8324
|
+
var MANIFEST_DIGEST_OFFSET = 56;
|
|
8325
|
+
var CONTENT_DIGEST_OFFSET = 88;
|
|
8326
|
+
var MAX_PACKET_BYTES = 256 * 1024 * 1024;
|
|
8327
|
+
var MAX_NODES = 1e6;
|
|
8328
|
+
var MAX_GEOMETRIES = 25e4;
|
|
8329
|
+
var MAX_RANGES = 64;
|
|
8330
|
+
var encoder = new TextEncoder();
|
|
8331
|
+
var decoder = new TextDecoder("utf-8", { fatal: true });
|
|
8332
|
+
function parsePreviewGeometryPacket(input) {
|
|
8333
|
+
const bytes = input instanceof Uint8Array ? input : new Uint8Array(input);
|
|
8334
|
+
if (bytes.byteLength < HEADER_BYTES || bytes.byteLength > MAX_PACKET_BYTES) {
|
|
8335
|
+
throw new RangeError("ICE3 preview packet size is invalid");
|
|
8336
|
+
}
|
|
8337
|
+
if (decoder.decode(bytes.subarray(0, 8)) !== MAGIC) {
|
|
8338
|
+
throw new TypeError("ICE3 preview packet magic is invalid");
|
|
8339
|
+
}
|
|
8340
|
+
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
8341
|
+
const schemaVersion = view.getUint16(8, true);
|
|
8342
|
+
const transportVersion = view.getUint16(10, true);
|
|
8343
|
+
const headerBytes = view.getUint32(12, true);
|
|
8344
|
+
if (schemaVersion !== 1 || transportVersion !== 1 || headerBytes !== HEADER_BYTES) {
|
|
8345
|
+
throw new TypeError("ICE3 preview packet version is unsupported");
|
|
8346
|
+
}
|
|
8347
|
+
const manifestLength = readSafeU64(view, MANIFEST_LENGTH_OFFSET);
|
|
8348
|
+
const binaryLength = readSafeU64(view, BINARY_LENGTH_OFFSET);
|
|
8349
|
+
if (manifestLength === 0 || manifestLength % 8 !== 0 || HEADER_BYTES + manifestLength + binaryLength !== bytes.byteLength) {
|
|
8350
|
+
throw new RangeError("ICE3 preview packet lengths are invalid");
|
|
8351
|
+
}
|
|
8352
|
+
const manifestStart = HEADER_BYTES;
|
|
8353
|
+
const manifestEnd = manifestStart + manifestLength;
|
|
8354
|
+
const manifestBytes = bytes.subarray(manifestStart, manifestEnd);
|
|
8355
|
+
const binary = bytes.subarray(manifestEnd);
|
|
8356
|
+
const expectedManifestDigest = bytes.subarray(MANIFEST_DIGEST_OFFSET, MANIFEST_DIGEST_OFFSET + 32);
|
|
8357
|
+
if (!equalBytes(sha256(manifestBytes), expectedManifestDigest)) {
|
|
8358
|
+
throw new TypeError("ICE3 preview packet manifest digest is invalid");
|
|
8359
|
+
}
|
|
8360
|
+
const contentHash = sha256(concatBytes(manifestBytes, binary));
|
|
8361
|
+
const expectedContentDigest = bytes.subarray(CONTENT_DIGEST_OFFSET, CONTENT_DIGEST_OFFSET + 32);
|
|
8362
|
+
if (!equalBytes(contentHash, expectedContentDigest)) {
|
|
8363
|
+
throw new TypeError("ICE3 preview packet content digest is invalid");
|
|
8364
|
+
}
|
|
8365
|
+
let candidate;
|
|
8366
|
+
try {
|
|
8367
|
+
candidate = JSON.parse(decoder.decode(manifestBytes));
|
|
8368
|
+
} catch {
|
|
8369
|
+
throw new TypeError("ICE3 preview packet manifest is invalid");
|
|
8370
|
+
}
|
|
8371
|
+
const manifest = validateManifest(candidate, binary.byteLength);
|
|
8372
|
+
return {
|
|
8373
|
+
...manifest,
|
|
8374
|
+
geometries: manifest.geometries.map((geometry) => decodeGeometry(geometry, binary)),
|
|
8375
|
+
binary
|
|
8376
|
+
};
|
|
8377
|
+
}
|
|
8378
|
+
function applyPreviewGeometryDelta(base, delta) {
|
|
8379
|
+
if (delta.parentRevision !== base.revision) {
|
|
8380
|
+
throw new RangeError(`ICE3 preview packet parent revision ${delta.parentRevision ?? "missing"} does not match ${base.revision}`);
|
|
8381
|
+
}
|
|
8382
|
+
const nodes = mergeById(base.nodes, delta.nodes, delta.removedNodeIds, (node) => node.nodeId);
|
|
8383
|
+
const geometries = mergeGeometries(base.geometries, delta, delta.removedGeometryIds);
|
|
8384
|
+
const materials = mergeById(base.materials, delta.materials, [], (material) => material.materialId);
|
|
8385
|
+
return {
|
|
8386
|
+
schemaVersion: delta.schemaVersion,
|
|
8387
|
+
transportVersion: delta.transportVersion,
|
|
8388
|
+
revision: delta.revision,
|
|
8389
|
+
parentRevision: delta.parentRevision,
|
|
8390
|
+
sourceDigest: delta.sourceDigest,
|
|
8391
|
+
backend: delta.backend,
|
|
8392
|
+
residencyMode: delta.residencyMode,
|
|
8393
|
+
nodes,
|
|
8394
|
+
geometries,
|
|
8395
|
+
materials,
|
|
8396
|
+
removedNodeIds: [],
|
|
8397
|
+
removedGeometryIds: [],
|
|
8398
|
+
binary: delta.binary
|
|
8399
|
+
};
|
|
8400
|
+
}
|
|
8401
|
+
function mergeGeometries(baseGeometries, delta, removedGeometryIds) {
|
|
8402
|
+
const removed = new Set(removedGeometryIds);
|
|
8403
|
+
const updates = new Map(delta.geometries.map((geometry) => [geometry.geometryId, geometry]));
|
|
8404
|
+
const output = [];
|
|
8405
|
+
for (const base of baseGeometries) {
|
|
8406
|
+
if (removed.has(base.geometryId)) {
|
|
8407
|
+
continue;
|
|
8408
|
+
}
|
|
8409
|
+
const update = updates.get(base.geometryId);
|
|
8410
|
+
output.push(update ? mergeGeometry(base, update) : base);
|
|
8411
|
+
updates.delete(base.geometryId);
|
|
8412
|
+
}
|
|
8413
|
+
output.push(...updates.values());
|
|
8414
|
+
return Object.freeze(output);
|
|
8415
|
+
}
|
|
8416
|
+
function mergeGeometry(base, update) {
|
|
8417
|
+
if (base === update) {
|
|
8418
|
+
return base;
|
|
8419
|
+
}
|
|
8420
|
+
return freezeGeometry(update, base.attributeDigest === update.attributeDigest ? base.positions : update.positions, base.topologyDigest === update.topologyDigest ? base.indices : update.indices);
|
|
8421
|
+
}
|
|
8422
|
+
function validateManifest(value, binaryLength) {
|
|
8423
|
+
if (!isRecord(value)) {
|
|
8424
|
+
throw invalidManifest();
|
|
8425
|
+
}
|
|
8426
|
+
const nodes = readArray(value.nodes, MAX_NODES).map(readNode);
|
|
8427
|
+
const geometries = readArray(value.geometries, MAX_GEOMETRIES).map(readGeometryRecord);
|
|
8428
|
+
const materials = readArray(value.materials, MAX_GEOMETRIES).map(readMaterial);
|
|
8429
|
+
const removedNodeIds = readStringArray(value.removedNodeIds, MAX_NODES);
|
|
8430
|
+
const removedGeometryIds = readStringArray(value.removedGeometryIds, MAX_GEOMETRIES);
|
|
8431
|
+
const manifest = {
|
|
8432
|
+
schemaVersion: readVersion(value.schemaVersion, 1),
|
|
8433
|
+
transportVersion: readVersion(value.transportVersion, 1),
|
|
8434
|
+
revision: readSafeInteger(value.revision, 0, Number.MAX_SAFE_INTEGER),
|
|
8435
|
+
parentRevision: value.parentRevision === null ? null : readSafeInteger(value.parentRevision, 0, Number.MAX_SAFE_INTEGER),
|
|
8436
|
+
sourceDigest: readDigest(value.sourceDigest),
|
|
8437
|
+
backend: readEnum(value.backend, ["cpu", "gpu"]),
|
|
8438
|
+
residencyMode: readEnum(value.residencyMode, ["cpu", "cold", "warm", "resident", "snapshot"]),
|
|
8439
|
+
nodes,
|
|
8440
|
+
geometries,
|
|
8441
|
+
materials,
|
|
8442
|
+
removedNodeIds,
|
|
8443
|
+
removedGeometryIds
|
|
8444
|
+
};
|
|
8445
|
+
if (manifest.parentRevision !== null && manifest.parentRevision >= manifest.revision) {
|
|
8446
|
+
throw invalidManifest();
|
|
8447
|
+
}
|
|
8448
|
+
ensureUnique(nodes, (node) => node.nodeId);
|
|
8449
|
+
ensureUnique(geometries, (geometry) => geometry.geometryId);
|
|
8450
|
+
ensureUnique(materials, (material) => material.materialId);
|
|
8451
|
+
for (const geometry of geometries) {
|
|
8452
|
+
for (const range of geometry.ranges) {
|
|
8453
|
+
if (range.byteOffset % 4 !== 0 || range.byteLength === 0 || range.byteOffset + range.byteLength > binaryLength) {
|
|
8454
|
+
throw invalidManifest();
|
|
8455
|
+
}
|
|
8456
|
+
}
|
|
8457
|
+
}
|
|
8458
|
+
return manifest;
|
|
8459
|
+
}
|
|
8460
|
+
function readNode(value, index) {
|
|
8461
|
+
if (!isRecord(value)) {
|
|
8462
|
+
throw new TypeError(`ICE3 preview node ${index} is invalid`);
|
|
8463
|
+
}
|
|
8464
|
+
const matrix = readArray(value.matrix, 16);
|
|
8465
|
+
if (matrix.some((entry) => typeof entry !== "number" || !Number.isFinite(entry))) {
|
|
8466
|
+
throw new TypeError(`ICE3 preview node ${index} matrix is invalid`);
|
|
8467
|
+
}
|
|
8468
|
+
return {
|
|
8469
|
+
nodeId: readNonEmptyString(value.nodeId),
|
|
8470
|
+
parentId: readNullableString(value.parentId),
|
|
8471
|
+
name: readString(value.name),
|
|
8472
|
+
matrix: Object.freeze(matrix),
|
|
8473
|
+
materialId: readNullableString(value.materialId),
|
|
8474
|
+
geometryId: readNullableString(value.geometryId),
|
|
8475
|
+
visible: readBoolean(value.visible)
|
|
8476
|
+
};
|
|
8477
|
+
}
|
|
8478
|
+
function readGeometryRecord(value, index) {
|
|
8479
|
+
if (!isRecord(value)) {
|
|
8480
|
+
throw new TypeError(`ICE3 preview geometry ${index} is invalid`);
|
|
8481
|
+
}
|
|
8482
|
+
const indexWidth = readSafeInteger(value.indexWidth, 1, 8);
|
|
8483
|
+
if (indexWidth !== 2 && indexWidth !== 4) {
|
|
8484
|
+
throw new TypeError(`ICE3 preview geometry ${index} index width is invalid`);
|
|
8485
|
+
}
|
|
8486
|
+
const ranges = readArray(value.ranges, MAX_RANGES).map(readRange);
|
|
8487
|
+
return {
|
|
8488
|
+
geometryId: readNonEmptyString(value.geometryId),
|
|
8489
|
+
residentGeometryId: readNullableString(value.residentGeometryId),
|
|
8490
|
+
topologyDigest: readDigest(value.topologyDigest),
|
|
8491
|
+
attributeDigest: readDigest(value.attributeDigest),
|
|
8492
|
+
vertexCount: readSafeInteger(value.vertexCount, 1, 1e8),
|
|
8493
|
+
indexCount: readSafeInteger(value.indexCount, 0, 3e8),
|
|
8494
|
+
indexWidth,
|
|
8495
|
+
ranges: Object.freeze(ranges)
|
|
8496
|
+
};
|
|
8497
|
+
}
|
|
8498
|
+
function readRange(value, index) {
|
|
8499
|
+
if (!isRecord(value)) {
|
|
8500
|
+
throw new TypeError(`ICE3 preview range ${index} is invalid`);
|
|
8501
|
+
}
|
|
8502
|
+
return {
|
|
8503
|
+
byteOffset: readSafeInteger(value.byteOffset, 0, MAX_PACKET_BYTES),
|
|
8504
|
+
byteLength: readSafeInteger(value.byteLength, 1, MAX_PACKET_BYTES),
|
|
8505
|
+
usage: readEnum(value.usage, ["vertex_attributes", "indices"])
|
|
8506
|
+
};
|
|
8507
|
+
}
|
|
8508
|
+
function readMaterial(value, index) {
|
|
8509
|
+
if (!isRecord(value)) {
|
|
8510
|
+
throw new TypeError(`ICE3 preview material ${index} is invalid`);
|
|
8511
|
+
}
|
|
8512
|
+
const baseColor = readFiniteArray(value.baseColor, 4);
|
|
8513
|
+
return {
|
|
8514
|
+
materialId: readNonEmptyString(value.materialId),
|
|
8515
|
+
baseColor: Object.freeze(baseColor),
|
|
8516
|
+
metallic: readFiniteNumber(value.metallic),
|
|
8517
|
+
roughness: readFiniteNumber(value.roughness),
|
|
8518
|
+
opacity: readFiniteNumber(value.opacity)
|
|
8519
|
+
};
|
|
8520
|
+
}
|
|
8521
|
+
function decodeGeometry(geometry, binary) {
|
|
8522
|
+
const attributes = geometry.ranges.find((range) => range.usage === "vertex_attributes");
|
|
8523
|
+
const topology = geometry.ranges.find((range) => range.usage === "indices");
|
|
8524
|
+
if (!attributes && !topology) {
|
|
8525
|
+
return freezeGeometry(encodeIdentity(geometry), new Float32Array(), new Uint32Array());
|
|
8526
|
+
}
|
|
8527
|
+
const positions = attributes ? decodePositions(attributes, geometry.vertexCount, binary) : new Float32Array(geometry.vertexCount * 3);
|
|
8528
|
+
const indices = topology ? decodeIndices(topology, geometry.indexCount, geometry.indexWidth, binary) : new Uint32Array();
|
|
8529
|
+
return freezeGeometry(encodeIdentity(geometry), positions, indices);
|
|
8530
|
+
}
|
|
8531
|
+
function decodePositions(range, vertexCount, binary) {
|
|
8532
|
+
const bytesPerVertex = range.byteLength / vertexCount;
|
|
8533
|
+
const components = bytesPerVertex / 4;
|
|
8534
|
+
if (!Number.isInteger(components) || components < 3) {
|
|
8535
|
+
throw new TypeError("ICE3 preview position range has an invalid stride");
|
|
8536
|
+
}
|
|
8537
|
+
const source = new Float32Array(binary.buffer, binary.byteOffset + range.byteOffset, range.byteLength / 4);
|
|
8538
|
+
if (components === 3) {
|
|
8539
|
+
return source;
|
|
8540
|
+
}
|
|
8541
|
+
const positions = new Float32Array(vertexCount * 3);
|
|
8542
|
+
for (let vertex = 0; vertex < vertexCount; vertex += 1) {
|
|
8543
|
+
const sourceOffset = vertex * components;
|
|
8544
|
+
positions[vertex * 3] = source[sourceOffset];
|
|
8545
|
+
positions[vertex * 3 + 1] = source[sourceOffset + 1];
|
|
8546
|
+
positions[vertex * 3 + 2] = source[sourceOffset + 2];
|
|
8547
|
+
}
|
|
8548
|
+
return positions;
|
|
8549
|
+
}
|
|
8550
|
+
function decodeIndices(range, indexCount, indexWidth, binary) {
|
|
8551
|
+
if (range.byteLength !== indexCount * indexWidth) {
|
|
8552
|
+
throw new TypeError("ICE3 preview index range has an invalid length");
|
|
8553
|
+
}
|
|
8554
|
+
const offset = binary.byteOffset + range.byteOffset;
|
|
8555
|
+
return indexWidth === 2 ? new Uint16Array(binary.buffer, offset, indexCount) : new Uint32Array(binary.buffer, offset, indexCount);
|
|
8556
|
+
}
|
|
8557
|
+
function freezeGeometry(identity, positions, indices) {
|
|
8558
|
+
return Object.freeze({
|
|
8559
|
+
...identity,
|
|
8560
|
+
positions,
|
|
8561
|
+
indices
|
|
8562
|
+
});
|
|
8563
|
+
}
|
|
8564
|
+
function encodeIdentity(geometry) {
|
|
8565
|
+
return geometry;
|
|
8566
|
+
}
|
|
8567
|
+
function mergeById(base, updates, removed, id) {
|
|
8568
|
+
const removedIds = new Set(removed);
|
|
8569
|
+
const updateMap = new Map(updates.map((value) => [id(value), value]));
|
|
8570
|
+
const output = base.filter((value) => !removedIds.has(id(value))).map((value) => updateMap.get(id(value)) ?? value);
|
|
8571
|
+
const baseIds = new Set(base.map(id));
|
|
8572
|
+
for (const value of updates) {
|
|
8573
|
+
if (!baseIds.has(id(value))) {
|
|
8574
|
+
output.push(value);
|
|
8575
|
+
}
|
|
8576
|
+
}
|
|
8577
|
+
return Object.freeze(output);
|
|
8578
|
+
}
|
|
8579
|
+
function readArray(value, maximum) {
|
|
8580
|
+
if (!Array.isArray(value) || value.length > maximum) {
|
|
8581
|
+
throw invalidManifest();
|
|
8582
|
+
}
|
|
8583
|
+
return value;
|
|
8584
|
+
}
|
|
8585
|
+
function readStringArray(value, maximum) {
|
|
8586
|
+
return Object.freeze(readArray(value, maximum).map(readString));
|
|
8587
|
+
}
|
|
8588
|
+
function readFiniteArray(value, length) {
|
|
8589
|
+
const values = readArray(value, length);
|
|
8590
|
+
if (values.length !== length || values.some((entry) => typeof entry !== "number" || !Number.isFinite(entry))) {
|
|
8591
|
+
throw invalidManifest();
|
|
8592
|
+
}
|
|
8593
|
+
return values;
|
|
8594
|
+
}
|
|
8595
|
+
function readVersion(value, expected) {
|
|
8596
|
+
if (value !== expected) {
|
|
8597
|
+
throw new TypeError("ICE3 preview packet version is unsupported");
|
|
8598
|
+
}
|
|
8599
|
+
return expected;
|
|
8600
|
+
}
|
|
8601
|
+
function readSafeInteger(value, minimum, maximum) {
|
|
8602
|
+
if (typeof value !== "number" || !Number.isSafeInteger(value) || value < minimum || value > maximum) {
|
|
8603
|
+
throw invalidManifest();
|
|
8604
|
+
}
|
|
8605
|
+
return value;
|
|
8606
|
+
}
|
|
8607
|
+
function readFiniteNumber(value) {
|
|
8608
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
8609
|
+
throw invalidManifest();
|
|
8610
|
+
}
|
|
8611
|
+
return value;
|
|
8612
|
+
}
|
|
8613
|
+
function readBoolean(value) {
|
|
8614
|
+
if (typeof value !== "boolean") {
|
|
8615
|
+
throw invalidManifest();
|
|
8616
|
+
}
|
|
8617
|
+
return value;
|
|
8618
|
+
}
|
|
8619
|
+
function readString(value) {
|
|
8620
|
+
if (typeof value !== "string") {
|
|
8621
|
+
throw invalidManifest();
|
|
8622
|
+
}
|
|
8623
|
+
return value;
|
|
8624
|
+
}
|
|
8625
|
+
function readNonEmptyString(value) {
|
|
8626
|
+
const result = readString(value);
|
|
8627
|
+
if (!result.trim()) {
|
|
8628
|
+
throw invalidManifest();
|
|
8629
|
+
}
|
|
8630
|
+
return result;
|
|
8631
|
+
}
|
|
8632
|
+
function readNullableString(value) {
|
|
8633
|
+
return value === null ? null : readString(value);
|
|
8634
|
+
}
|
|
8635
|
+
function readDigest(value) {
|
|
8636
|
+
const digest = readString(value);
|
|
8637
|
+
if (!/^[0-9a-fA-F]{64}$/.test(digest)) {
|
|
8638
|
+
throw invalidManifest();
|
|
8639
|
+
}
|
|
8640
|
+
return digest.toLowerCase();
|
|
8641
|
+
}
|
|
8642
|
+
function readEnum(value, allowed) {
|
|
8643
|
+
if (typeof value !== "string" || !allowed.includes(value)) {
|
|
8644
|
+
throw invalidManifest();
|
|
8645
|
+
}
|
|
8646
|
+
return value;
|
|
8647
|
+
}
|
|
8648
|
+
function ensureUnique(values, id) {
|
|
8649
|
+
if (new Set(values.map(id)).size !== values.length) {
|
|
8650
|
+
throw invalidManifest();
|
|
8651
|
+
}
|
|
8652
|
+
}
|
|
8653
|
+
function invalidManifest() {
|
|
8654
|
+
return new TypeError("ICE3 preview packet manifest is invalid");
|
|
8655
|
+
}
|
|
8656
|
+
function isRecord(value) {
|
|
8657
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
8658
|
+
}
|
|
8659
|
+
function readSafeU64(view, offset) {
|
|
8660
|
+
const value = view.getBigUint64(offset, true);
|
|
8661
|
+
if (value > BigInt(Number.MAX_SAFE_INTEGER)) {
|
|
8662
|
+
throw new RangeError("ICE3 preview packet length exceeds safe integer range");
|
|
8663
|
+
}
|
|
8664
|
+
return Number(value);
|
|
8665
|
+
}
|
|
8666
|
+
function concatBytes(left, right) {
|
|
8667
|
+
const output = new Uint8Array(left.length + right.length);
|
|
8668
|
+
output.set(left);
|
|
8669
|
+
output.set(right, left.length);
|
|
8670
|
+
return output;
|
|
8671
|
+
}
|
|
8672
|
+
function equalBytes(left, right) {
|
|
8673
|
+
return left.length === right.length && left.every((value, index) => value === right[index]);
|
|
8674
|
+
}
|
|
8675
|
+
var SHA256_CONSTANTS = new Uint32Array([
|
|
8676
|
+
1116352408,
|
|
8677
|
+
1899447441,
|
|
8678
|
+
3049323471,
|
|
8679
|
+
3921009573,
|
|
8680
|
+
961987163,
|
|
8681
|
+
1508970993,
|
|
8682
|
+
2453635748,
|
|
8683
|
+
2870763221,
|
|
8684
|
+
3624381080,
|
|
8685
|
+
310598401,
|
|
8686
|
+
607225278,
|
|
8687
|
+
1426881987,
|
|
8688
|
+
1925078388,
|
|
8689
|
+
2162078206,
|
|
8690
|
+
2614888103,
|
|
8691
|
+
3248222580,
|
|
8692
|
+
3835390401,
|
|
8693
|
+
4022224774,
|
|
8694
|
+
264347078,
|
|
8695
|
+
604807628,
|
|
8696
|
+
770255983,
|
|
8697
|
+
1249150122,
|
|
8698
|
+
1555081692,
|
|
8699
|
+
1996064986,
|
|
8700
|
+
2554220882,
|
|
8701
|
+
2821834349,
|
|
8702
|
+
2952996808,
|
|
8703
|
+
3210313671,
|
|
8704
|
+
3336571891,
|
|
8705
|
+
3584528711,
|
|
8706
|
+
113926993,
|
|
8707
|
+
338241895,
|
|
8708
|
+
666307205,
|
|
8709
|
+
773529912,
|
|
8710
|
+
1294757372,
|
|
8711
|
+
1396182291,
|
|
8712
|
+
1695183700,
|
|
8713
|
+
1986661051,
|
|
8714
|
+
2177026350,
|
|
8715
|
+
2456956037,
|
|
8716
|
+
2730485921,
|
|
8717
|
+
2820302411,
|
|
8718
|
+
3259730800,
|
|
8719
|
+
3345764771,
|
|
8720
|
+
3516065817,
|
|
8721
|
+
3600352804,
|
|
8722
|
+
4094571909,
|
|
8723
|
+
275423344,
|
|
8724
|
+
430227734,
|
|
8725
|
+
506948616,
|
|
8726
|
+
659060556,
|
|
8727
|
+
883997877,
|
|
8728
|
+
958139571,
|
|
8729
|
+
1322822218,
|
|
8730
|
+
1537002063,
|
|
8731
|
+
1747873779,
|
|
8732
|
+
1955562222,
|
|
8733
|
+
2024104815,
|
|
8734
|
+
2227730452,
|
|
8735
|
+
2361852424,
|
|
8736
|
+
2428436474,
|
|
8737
|
+
2756734187,
|
|
8738
|
+
3204031479,
|
|
8739
|
+
3329325298
|
|
8740
|
+
]);
|
|
8741
|
+
function sha256(input) {
|
|
8742
|
+
const bitLength = input.length * 8;
|
|
8743
|
+
const paddedLength = Math.ceil((input.length + 9) / 64) * 64;
|
|
8744
|
+
const data = new Uint8Array(paddedLength);
|
|
8745
|
+
data.set(input);
|
|
8746
|
+
data[input.length] = 128;
|
|
8747
|
+
const view = new DataView(data.buffer);
|
|
8748
|
+
view.setUint32(paddedLength - 8, Math.floor(bitLength / 4294967296));
|
|
8749
|
+
view.setUint32(paddedLength - 4, bitLength >>> 0);
|
|
8750
|
+
const hash = new Uint32Array([
|
|
8751
|
+
1779033703,
|
|
8752
|
+
3144134277,
|
|
8753
|
+
1013904242,
|
|
8754
|
+
2773480762,
|
|
8755
|
+
1359893119,
|
|
8756
|
+
2600822924,
|
|
8757
|
+
528734635,
|
|
8758
|
+
1541459225
|
|
8759
|
+
]);
|
|
8760
|
+
const words = new Uint32Array(64);
|
|
8761
|
+
for (let offset = 0; offset < data.length; offset += 64) {
|
|
8762
|
+
for (let index = 0; index < 16; index += 1) {
|
|
8763
|
+
words[index] = view.getUint32(offset + index * 4);
|
|
8764
|
+
}
|
|
8765
|
+
for (let index = 16; index < 64; index += 1) {
|
|
8766
|
+
const s0 = rotateRight(words[index - 15], 7) ^ rotateRight(words[index - 15], 18) ^ words[index - 15] >>> 3;
|
|
8767
|
+
const s1 = rotateRight(words[index - 2], 17) ^ rotateRight(words[index - 2], 19) ^ words[index - 2] >>> 10;
|
|
8768
|
+
words[index] = words[index - 16] + s0 + words[index - 7] + s1 >>> 0;
|
|
8769
|
+
}
|
|
8770
|
+
let [a, b, c, d, e, f, g, h] = hash;
|
|
8771
|
+
for (let index = 0; index < 64; index += 1) {
|
|
8772
|
+
const sum1 = rotateRight(e, 6) ^ rotateRight(e, 11) ^ rotateRight(e, 25);
|
|
8773
|
+
const choice = e & f ^ ~e & g;
|
|
8774
|
+
const temp1 = h + sum1 + choice + SHA256_CONSTANTS[index] + words[index] >>> 0;
|
|
8775
|
+
const sum0 = rotateRight(a, 2) ^ rotateRight(a, 13) ^ rotateRight(a, 22);
|
|
8776
|
+
const majority = a & b ^ a & c ^ b & c;
|
|
8777
|
+
const temp2 = sum0 + majority >>> 0;
|
|
8778
|
+
h = g;
|
|
8779
|
+
g = f;
|
|
8780
|
+
f = e;
|
|
8781
|
+
e = d + temp1 >>> 0;
|
|
8782
|
+
d = c;
|
|
8783
|
+
c = b;
|
|
8784
|
+
b = a;
|
|
8785
|
+
a = temp1 + temp2 >>> 0;
|
|
8786
|
+
}
|
|
8787
|
+
hash[0] = hash[0] + a >>> 0;
|
|
8788
|
+
hash[1] = hash[1] + b >>> 0;
|
|
8789
|
+
hash[2] = hash[2] + c >>> 0;
|
|
8790
|
+
hash[3] = hash[3] + d >>> 0;
|
|
8791
|
+
hash[4] = hash[4] + e >>> 0;
|
|
8792
|
+
hash[5] = hash[5] + f >>> 0;
|
|
8793
|
+
hash[6] = hash[6] + g >>> 0;
|
|
8794
|
+
hash[7] = hash[7] + h >>> 0;
|
|
8795
|
+
}
|
|
8796
|
+
const output = new Uint8Array(32);
|
|
8797
|
+
const outputView = new DataView(output.buffer);
|
|
8798
|
+
for (let index = 0; index < hash.length; index += 1) {
|
|
8799
|
+
outputView.setUint32(index * 4, hash[index], false);
|
|
8800
|
+
}
|
|
8801
|
+
return output;
|
|
8802
|
+
}
|
|
8803
|
+
function rotateRight(value, bits) {
|
|
8804
|
+
return value >>> bits | value << 32 - bits;
|
|
8805
|
+
}
|
|
8806
|
+
|
|
7446
8807
|
// packages/assets/dist/ICETilesetReader.js
|
|
7447
8808
|
var IDENTITY = [
|
|
7448
8809
|
1,
|
|
@@ -7715,7 +9076,11 @@ function parseTransform(value) {
|
|
|
7715
9076
|
return matrix;
|
|
7716
9077
|
}
|
|
7717
9078
|
function assertContains(parent, child, id) {
|
|
7718
|
-
if ([0, 1, 2].some((axis) =>
|
|
9079
|
+
if ([0, 1, 2].some((axis) => {
|
|
9080
|
+
const scale = Math.max(1, Math.abs(parent.min[axis]), Math.abs(parent.max[axis]), Math.abs(child.min[axis]), Math.abs(child.max[axis]));
|
|
9081
|
+
const tolerance = EPSILON * scale;
|
|
9082
|
+
return child.min[axis] < parent.min[axis] - tolerance || child.max[axis] > parent.max[axis] + tolerance;
|
|
9083
|
+
})) {
|
|
7719
9084
|
throw invalid(`child bounds escape parent tile ${id}`);
|
|
7720
9085
|
}
|
|
7721
9086
|
}
|
|
@@ -9212,12 +10577,6 @@ var ICETilesetHandle = class extends ICEEventDispatcher {
|
|
|
9212
10577
|
this.assetLoader.clear(uri);
|
|
9213
10578
|
this.states.set(tileId, "evicted");
|
|
9214
10579
|
this.emit("TILESET_TILE_UNLOADED", tileId);
|
|
9215
|
-
const remainingIds = /* @__PURE__ */ new Set();
|
|
9216
|
-
collectNodeAndSelectionIDs(this.scene, remainingIds);
|
|
9217
|
-
const selection = this.engine.getSelection();
|
|
9218
|
-
if (selection.some((id) => !remainingIds.has(id))) {
|
|
9219
|
-
this.engine.setSelection(selection.filter((id) => remainingIds.has(id)));
|
|
9220
|
-
}
|
|
9221
10580
|
this.engine.requestRender("tileset-tile-unloaded");
|
|
9222
10581
|
}
|
|
9223
10582
|
hasPendingVisibleTargets() {
|
|
@@ -9271,7 +10630,6 @@ var ICETilesetHandle = class extends ICEEventDispatcher {
|
|
|
9271
10630
|
this.failedLodFallbacks.delete(fallbackId);
|
|
9272
10631
|
}
|
|
9273
10632
|
this.visibleCoverageIds = targetIds;
|
|
9274
|
-
this.reconcileSelectionVisibility();
|
|
9275
10633
|
this.engine.requestRender("tileset-visible-target-committed");
|
|
9276
10634
|
if (!this.readySettled) {
|
|
9277
10635
|
this.readySettled = true;
|
|
@@ -9309,17 +10667,6 @@ var ICETilesetHandle = class extends ICEEventDispatcher {
|
|
|
9309
10667
|
}
|
|
9310
10668
|
return visible;
|
|
9311
10669
|
}
|
|
9312
|
-
reconcileSelectionVisibility() {
|
|
9313
|
-
const visibleIds = /* @__PURE__ */ new Set();
|
|
9314
|
-
for (const child of this.scene.children) {
|
|
9315
|
-
collectVisibleNodeAndSelectionIDs(child, visibleIds);
|
|
9316
|
-
}
|
|
9317
|
-
const selection = this.engine.getSelection();
|
|
9318
|
-
const visibleSelection = selection.filter((id) => visibleIds.has(id));
|
|
9319
|
-
if (visibleSelection.length !== selection.length) {
|
|
9320
|
-
this.engine.setSelection(visibleSelection);
|
|
9321
|
-
}
|
|
9322
|
-
}
|
|
9323
10670
|
rejectReadyIfVisibleSetFailed() {
|
|
9324
10671
|
if (this.readySettled || !this.workSet)
|
|
9325
10672
|
return;
|
|
@@ -9728,34 +11075,6 @@ function collectNodeIDs(node, output) {
|
|
|
9728
11075
|
for (const child of node.children)
|
|
9729
11076
|
collectNodeIDs(child, output);
|
|
9730
11077
|
}
|
|
9731
|
-
function collectNodeAndSelectionIDs(node, output) {
|
|
9732
|
-
output.add(node.id);
|
|
9733
|
-
if (node instanceof ICEMesh)
|
|
9734
|
-
output.add(node.selectionId);
|
|
9735
|
-
if (node instanceof ICERenderBatch) {
|
|
9736
|
-
for (const selectionId of node.semanticSelectionIds) {
|
|
9737
|
-
output.add(selectionId);
|
|
9738
|
-
}
|
|
9739
|
-
}
|
|
9740
|
-
for (const child of node.children) {
|
|
9741
|
-
collectNodeAndSelectionIDs(child, output);
|
|
9742
|
-
}
|
|
9743
|
-
}
|
|
9744
|
-
function collectVisibleNodeAndSelectionIDs(node, output) {
|
|
9745
|
-
if (!node.visible)
|
|
9746
|
-
return;
|
|
9747
|
-
output.add(node.id);
|
|
9748
|
-
if (node instanceof ICEMesh)
|
|
9749
|
-
output.add(node.selectionId);
|
|
9750
|
-
if (node instanceof ICERenderBatch) {
|
|
9751
|
-
for (const selectionId of node.semanticSelectionIds) {
|
|
9752
|
-
output.add(selectionId);
|
|
9753
|
-
}
|
|
9754
|
-
}
|
|
9755
|
-
for (const child of node.children) {
|
|
9756
|
-
collectVisibleNodeAndSelectionIDs(child, output);
|
|
9757
|
-
}
|
|
9758
|
-
}
|
|
9759
11078
|
function throwIfAborted(signal) {
|
|
9760
11079
|
if (signal?.aborted)
|
|
9761
11080
|
throw abortedTilesetError();
|
|
@@ -9766,6 +11085,72 @@ function isAbortError(error) {
|
|
|
9766
11085
|
function abortedTilesetError() {
|
|
9767
11086
|
return new Error("ICE3_TILESET_ABORTED: Tileset loading was aborted");
|
|
9768
11087
|
}
|
|
11088
|
+
|
|
11089
|
+
// packages/runtime/dist/asset/ICEPreviewPacketLoader.js
|
|
11090
|
+
var ICEPreviewPacketLoader = class {
|
|
11091
|
+
fetcher;
|
|
11092
|
+
pinGeometry;
|
|
11093
|
+
geometries = /* @__PURE__ */ new Map();
|
|
11094
|
+
publishedScene = null;
|
|
11095
|
+
constructor(options = {}) {
|
|
11096
|
+
const normalized = typeof options === "function" ? { fetch: options } : options;
|
|
11097
|
+
this.fetcher = normalized.fetch ?? globalThis.fetch.bind(globalThis);
|
|
11098
|
+
this.pinGeometry = normalized.pinGeometry;
|
|
11099
|
+
}
|
|
11100
|
+
get scene() {
|
|
11101
|
+
return this.publishedScene;
|
|
11102
|
+
}
|
|
11103
|
+
get currentGeometries() {
|
|
11104
|
+
return this.geometries;
|
|
11105
|
+
}
|
|
11106
|
+
async load(url, current) {
|
|
11107
|
+
let bytes;
|
|
11108
|
+
try {
|
|
11109
|
+
const response = await this.fetcher(url, {
|
|
11110
|
+
headers: {
|
|
11111
|
+
accept: "application/vnd.ice3.preview-geometry; version=1"
|
|
11112
|
+
}
|
|
11113
|
+
});
|
|
11114
|
+
if (!response.ok) {
|
|
11115
|
+
return { kind: "fallback", reason: "packet-unavailable" };
|
|
11116
|
+
}
|
|
11117
|
+
bytes = new Uint8Array(await response.arrayBuffer());
|
|
11118
|
+
} catch {
|
|
11119
|
+
return { kind: "fallback", reason: "packet-unavailable" };
|
|
11120
|
+
}
|
|
11121
|
+
let incoming;
|
|
11122
|
+
try {
|
|
11123
|
+
incoming = parsePreviewGeometryPacket(bytes);
|
|
11124
|
+
} catch {
|
|
11125
|
+
return { kind: "fallback", reason: "packet-invalid" };
|
|
11126
|
+
}
|
|
11127
|
+
let packet = incoming;
|
|
11128
|
+
if (incoming.parentRevision !== null) {
|
|
11129
|
+
if (!current || incoming.parentRevision !== current.revision) {
|
|
11130
|
+
return { kind: "fallback", reason: "packet-revision-mismatch" };
|
|
11131
|
+
}
|
|
11132
|
+
packet = applyPreviewGeometryDelta(current, incoming);
|
|
11133
|
+
}
|
|
11134
|
+
let built;
|
|
11135
|
+
try {
|
|
11136
|
+
built = buildPreviewPacketScene(packet, this.geometries);
|
|
11137
|
+
if (this.pinGeometry) {
|
|
11138
|
+
for (const [geometryId, update] of built.geometryUpdates) {
|
|
11139
|
+
const geometry = built.geometries.get(geometryId);
|
|
11140
|
+
if (!geometry) {
|
|
11141
|
+
throw new Error("ICE3_PREVIEW_PACKET_INVALID: geometry is missing");
|
|
11142
|
+
}
|
|
11143
|
+
this.pinGeometry(geometry, update);
|
|
11144
|
+
}
|
|
11145
|
+
}
|
|
11146
|
+
} catch {
|
|
11147
|
+
return { kind: "fallback", reason: "packet-invalid" };
|
|
11148
|
+
}
|
|
11149
|
+
this.geometries = built.geometries;
|
|
11150
|
+
this.publishedScene = built.scene;
|
|
11151
|
+
return { kind: "packet", packet };
|
|
11152
|
+
}
|
|
11153
|
+
};
|
|
9769
11154
|
export {
|
|
9770
11155
|
ICE3,
|
|
9771
11156
|
ICEAssetLoader,
|
|
@@ -9778,13 +11163,19 @@ export {
|
|
|
9778
11163
|
ICENode,
|
|
9779
11164
|
ICEOrbitController,
|
|
9780
11165
|
ICEPerspectiveCamera,
|
|
11166
|
+
ICEPreviewPacketLoader,
|
|
9781
11167
|
ICEScene,
|
|
9782
11168
|
ICEScreenLayer,
|
|
9783
11169
|
ICETilesetHandle,
|
|
9784
11170
|
ICETilesetLoader,
|
|
9785
11171
|
ICETypeRegistry,
|
|
9786
11172
|
ICEWorldLayer,
|
|
11173
|
+
collectSceneItems,
|
|
9787
11174
|
deserializeScene,
|
|
9788
11175
|
serializeScene
|
|
9789
11176
|
};
|
|
11177
|
+
//! 方向光阴影贴图。
|
|
11178
|
+
//!
|
|
11179
|
+
//! 一次深度 pass 把场景从光源方向渲染进深度纹理,主 pass 再按世界坐标比较深度。
|
|
11180
|
+
//! 只处理单方向光、单张贴图,够覆盖当前查看器的需要;级联与软阴影留给后续。
|
|
9790
11181
|
//# sourceMappingURL=index.js.map
|