@zephyr3d/scene 0.9.18 → 0.9.20
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/animation/joint_dynamics/convex_collider.js +320 -0
- package/dist/animation/joint_dynamics/convex_collider.js.map +1 -0
- package/dist/app/scriptregistry.js +124 -6
- package/dist/app/scriptregistry.js.map +1 -1
- package/dist/asset/assetmanager.js +1 -0
- package/dist/asset/assetmanager.js.map +1 -1
- package/dist/camera/camera.js +240 -201
- package/dist/camera/camera.js.map +1 -1
- package/dist/index.d.ts +458 -9
- package/dist/index.js +4 -2
- package/dist/index.js.map +1 -1
- package/dist/material/meshmaterial.js +2 -2
- package/dist/material/meshmaterial.js.map +1 -1
- package/dist/material/mixins/lightmodel/pbrblueprintmixin.js +11 -5
- package/dist/material/mixins/lightmodel/pbrblueprintmixin.js.map +1 -1
- package/dist/material/mixins/lit.js +10 -0
- package/dist/material/mixins/lit.js.map +1 -1
- package/dist/material/shader/helper.js +138 -10
- package/dist/material/shader/helper.js.map +1 -1
- package/dist/material/skin.js +4 -6
- package/dist/material/skin.js.map +1 -1
- package/dist/material/water.js +1 -1
- package/dist/material/water.js.map +1 -1
- package/dist/posteffect/bloom.js +177 -29
- package/dist/posteffect/bloom.js.map +1 -1
- package/dist/posteffect/compositor.js +111 -204
- package/dist/posteffect/compositor.js.map +1 -1
- package/dist/posteffect/motionblur.js +3 -0
- package/dist/posteffect/motionblur.js.map +1 -1
- package/dist/posteffect/posteffect.js +79 -0
- package/dist/posteffect/posteffect.js.map +1 -1
- package/dist/posteffect/skinsss.js.map +1 -1
- package/dist/posteffect/ssr.js +238 -7
- package/dist/posteffect/ssr.js.map +1 -1
- package/dist/posteffect/sss.js +16 -15
- package/dist/posteffect/sss.js.map +1 -1
- package/dist/posteffect/taa.js +121 -19
- package/dist/posteffect/taa.js.map +1 -1
- package/dist/posteffect/tonemap.js +1 -1
- package/dist/posteffect/tonemap.js.map +1 -1
- package/dist/render/abuffer_oit.js +7 -1
- package/dist/render/abuffer_oit.js.map +1 -1
- package/dist/render/cluster_light.js +52 -8
- package/dist/render/cluster_light.js.map +1 -1
- package/dist/render/dualdepthpeeling_oit.js +331 -0
- package/dist/render/dualdepthpeeling_oit.js.map +1 -0
- package/dist/render/envlight.js +6 -3
- package/dist/render/envlight.js.map +1 -1
- package/dist/render/hzb.js +11 -4
- package/dist/render/hzb.js.map +1 -1
- package/dist/render/lightpass.js +21 -12
- package/dist/render/lightpass.js.map +1 -1
- package/dist/render/renderer.js +7 -3
- package/dist/render/renderer.js.map +1 -1
- package/dist/render/rendergraph/blackboard.js +79 -0
- package/dist/render/rendergraph/blackboard.js.map +1 -0
- package/dist/render/rendergraph/device_pool_allocator.js +10 -1
- package/dist/render/rendergraph/device_pool_allocator.js.map +1 -1
- package/dist/render/rendergraph/executor.js +41 -11
- package/dist/render/rendergraph/executor.js.map +1 -1
- package/dist/render/rendergraph/forward_plus_builder.js +551 -201
- package/dist/render/rendergraph/forward_plus_builder.js.map +1 -1
- package/dist/render/rendergraph/rendergraph.js +29 -0
- package/dist/render/rendergraph/rendergraph.js.map +1 -1
- package/dist/render/rendergraph/types.js.map +1 -1
- package/dist/render/renderpass.js +12 -2
- package/dist/render/renderpass.js.map +1 -1
- package/dist/render/shadow_mask_pass.js +297 -0
- package/dist/render/shadow_mask_pass.js.map +1 -0
- package/dist/render/weightedblended_oit.js +5 -0
- package/dist/render/weightedblended_oit.js.map +1 -1
- package/dist/scene/basesprite.js +1 -0
- package/dist/scene/basesprite.js.map +1 -1
- package/dist/scene/batchgroup.js +1 -0
- package/dist/scene/batchgroup.js.map +1 -1
- package/dist/scene/environment.js +1 -0
- package/dist/scene/environment.js.map +1 -1
- package/dist/scene/msdftext.js +1 -0
- package/dist/scene/msdftext.js.map +1 -1
- package/dist/scene/msdftextsprite.js +1 -0
- package/dist/scene/msdftextsprite.js.map +1 -1
- package/dist/scene/particlesys.js +1 -0
- package/dist/scene/particlesys.js.map +1 -1
- package/dist/scene/scene.js +1 -0
- package/dist/scene/scene.js.map +1 -1
- package/dist/scene/terrain-cm/grass.js +1 -0
- package/dist/scene/terrain-cm/grass.js.map +1 -1
- package/dist/scene/terrain-cm/terrain-cm.js +1 -0
- package/dist/scene/terrain-cm/terrain-cm.js.map +1 -1
- package/dist/scene/water.js +1 -0
- package/dist/scene/water.js.map +1 -1
- package/dist/shaders/ssr.js +52 -21
- package/dist/shaders/ssr.js.map +1 -1
- package/dist/utility/misc.js +10 -1
- package/dist/utility/misc.js.map +1 -1
- package/dist/utility/serialization/manager.js +1 -0
- package/dist/utility/serialization/manager.js.map +1 -1
- package/dist/utility/serialization/scene/animation.js +1 -0
- package/dist/utility/serialization/scene/animation.js.map +1 -1
- package/dist/utility/serialization/scene/batch.js +1 -0
- package/dist/utility/serialization/scene/batch.js.map +1 -1
- package/dist/utility/serialization/scene/camera.js +35 -2
- package/dist/utility/serialization/scene/camera.js.map +1 -1
- package/dist/utility/serialization/scene/light.js +1 -0
- package/dist/utility/serialization/scene/light.js.map +1 -1
- package/dist/utility/serialization/scene/mesh.js +1 -0
- package/dist/utility/serialization/scene/mesh.js.map +1 -1
- package/dist/utility/serialization/scene/node.js +1 -0
- package/dist/utility/serialization/scene/node.js.map +1 -1
- package/dist/utility/serialization/scene/particle.js +1 -0
- package/dist/utility/serialization/scene/particle.js.map +1 -1
- package/dist/utility/serialization/scene/primitive.js +1 -0
- package/dist/utility/serialization/scene/primitive.js.map +1 -1
- package/dist/utility/serialization/scene/sprite.js +1 -0
- package/dist/utility/serialization/scene/sprite.js.map +1 -1
- package/dist/utility/serialization/scene/terrain.js +1 -0
- package/dist/utility/serialization/scene/terrain.js.map +1 -1
- package/dist/utility/serialization/scene/text.js +1 -0
- package/dist/utility/serialization/scene/text.js.map +1 -1
- package/dist/utility/serialization/scene/water.js +1 -0
- package/dist/utility/serialization/scene/water.js.map +1 -1
- package/dist/values.js +11 -1
- package/dist/values.js.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
import { Matrix4x4, Vector3 } from '@zephyr3d/base';
|
|
2
|
+
import { getVertexFormatComponentCount } from '@zephyr3d/device';
|
|
3
|
+
import '../../scene/scene.js';
|
|
4
|
+
import '../../values.js';
|
|
5
|
+
import '../../render/render_queue.js';
|
|
6
|
+
import '../../render/primitive.js';
|
|
7
|
+
import '../../material/shader/helper.js';
|
|
8
|
+
import '../../material/lambert.js';
|
|
9
|
+
import '../../material/blinn.js';
|
|
10
|
+
import '../../material/unlit.js';
|
|
11
|
+
import '../../material/particle.js';
|
|
12
|
+
import '../../material/subsurfaceprofile.js';
|
|
13
|
+
import '../../material/meshmaterial.js';
|
|
14
|
+
import '../../material/grassmaterial.js';
|
|
15
|
+
import '../../material/terrain-cm.js';
|
|
16
|
+
import '../../material/pbrmr.js';
|
|
17
|
+
import '../../material/pbrsg.js';
|
|
18
|
+
import '../../material/mtoon.js';
|
|
19
|
+
import '../../material/pbrblueprint.js';
|
|
20
|
+
import '../../material/sprite.js';
|
|
21
|
+
import '../../material/sprite_std.js';
|
|
22
|
+
import '../../utility/blueprint/material/texture.js';
|
|
23
|
+
import '../../render/rendergraph/forward_plus_builder.js';
|
|
24
|
+
import '../../render/lightpass.js';
|
|
25
|
+
import '../../render/sky.js';
|
|
26
|
+
import '../../render/clipmap.js';
|
|
27
|
+
import '../../shaders/atmosphere.js';
|
|
28
|
+
import { getSceneMeshAssetBinding } from '../../scene/scene_node.js';
|
|
29
|
+
import '../../shadow/shadowmapper.js';
|
|
30
|
+
import '../../scene/mesh.js';
|
|
31
|
+
import '../../scene/basesprite.js';
|
|
32
|
+
import '../../scene/meshdrawable.js';
|
|
33
|
+
import '../../material/msdf_text.js';
|
|
34
|
+
import '../../scene/water.js';
|
|
35
|
+
import '../../scene/particlesys.js';
|
|
36
|
+
import '../../scene/batchgroup.js';
|
|
37
|
+
import '../../scene/terrain-cm/terrain-cm.js';
|
|
38
|
+
import '../../scene/terrain-cm/grass.js';
|
|
39
|
+
import '../../scene/raycast_visitor.js';
|
|
40
|
+
import { ColliderForce } from './types.js';
|
|
41
|
+
|
|
42
|
+
const EPS = 1e-6;
|
|
43
|
+
/**
|
|
44
|
+
* Generate convex JointDynamics colliders from skinned mesh bone weights.
|
|
45
|
+
*
|
|
46
|
+
* @public
|
|
47
|
+
*/ function generateJointDynamicsConvexCollidersFromSkinWeights(meshes, skinBinding, bones, options = {}) {
|
|
48
|
+
const sources = collectSourcePrimitives(meshes);
|
|
49
|
+
const results = [];
|
|
50
|
+
for (const bone of bones){
|
|
51
|
+
const jointIndex = skinBinding.joints.indexOf(bone);
|
|
52
|
+
if (jointIndex < 0) {
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
const points = collectBonePoints(sources, skinBinding, jointIndex, options);
|
|
56
|
+
if (points.length < (options.minVertexCount ?? 8)) {
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
const hull = buildConvexHull(points, options.maxHullVertices ?? 48, options.margin ?? 0);
|
|
60
|
+
if (!hull || hull.vertices.length < 4 || hull.planes.length < 4) {
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
results.push({
|
|
64
|
+
bone,
|
|
65
|
+
vertexCount: points.length,
|
|
66
|
+
hullVertexCount: hull.vertices.length,
|
|
67
|
+
collider: {
|
|
68
|
+
radius: 0,
|
|
69
|
+
radiusTailScale: 1,
|
|
70
|
+
height: 0,
|
|
71
|
+
friction: options.friction ?? 0,
|
|
72
|
+
isInverseCollider: options.inversed ?? false,
|
|
73
|
+
forceType: ColliderForce.Off,
|
|
74
|
+
convex: hull
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
return results;
|
|
79
|
+
}
|
|
80
|
+
function collectSourcePrimitives(meshes) {
|
|
81
|
+
const result = [];
|
|
82
|
+
const seen = new Set();
|
|
83
|
+
for (const mesh of meshes){
|
|
84
|
+
const binding = getSceneMeshAssetBinding(mesh);
|
|
85
|
+
const subMesh = binding?.subMesh;
|
|
86
|
+
const primitive = subMesh?.primitive ?? null;
|
|
87
|
+
if (primitive && !seen.has(primitive)) {
|
|
88
|
+
seen.add(primitive);
|
|
89
|
+
result.push({
|
|
90
|
+
primitive,
|
|
91
|
+
subMesh
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return result;
|
|
96
|
+
}
|
|
97
|
+
function collectBonePoints(sources, skinBinding, jointIndex, options) {
|
|
98
|
+
const points = [];
|
|
99
|
+
const weightThreshold = options.weightThreshold ?? 0.45;
|
|
100
|
+
const dominantBoneOnly = options.dominantBoneOnly ?? true;
|
|
101
|
+
const inverseBind = skinBinding.inverseBindMatrices[jointIndex] ?? Matrix4x4.identity();
|
|
102
|
+
for (const source of sources){
|
|
103
|
+
const primitive = source.primitive;
|
|
104
|
+
const positionInfo = primitive.vertices.position;
|
|
105
|
+
const blendIndexInfo = primitive.vertices.blendIndices;
|
|
106
|
+
const blendWeightInfo = primitive.vertices.blendWeights;
|
|
107
|
+
if (!positionInfo || !blendIndexInfo || !blendWeightInfo) {
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
const positions = positionInfo.data;
|
|
111
|
+
const blendIndices = blendIndexInfo.data;
|
|
112
|
+
const blendWeights = blendWeightInfo.data;
|
|
113
|
+
const posStride = getVertexFormatComponentCount(positionInfo.format);
|
|
114
|
+
const idxStride = getVertexFormatComponentCount(blendIndexInfo.format);
|
|
115
|
+
const weightStride = getVertexFormatComponentCount(blendWeightInfo.format);
|
|
116
|
+
const vertexCount = Math.floor(positions.length / posStride);
|
|
117
|
+
for(let i = 0; i < vertexCount; i++){
|
|
118
|
+
let weight = 0;
|
|
119
|
+
let maxWeight = -1;
|
|
120
|
+
let maxJoint = -1;
|
|
121
|
+
const influenceCount = Math.min(4, idxStride, weightStride);
|
|
122
|
+
for(let k = 0; k < influenceCount; k++){
|
|
123
|
+
const idx = Number(blendIndices[i * idxStride + k]) | 0;
|
|
124
|
+
const w = Number(blendWeights[i * weightStride + k]) || 0;
|
|
125
|
+
if (idx === jointIndex) {
|
|
126
|
+
weight = Math.max(weight, w);
|
|
127
|
+
}
|
|
128
|
+
if (w > maxWeight) {
|
|
129
|
+
maxWeight = w;
|
|
130
|
+
maxJoint = idx;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
if (weight < weightThreshold || dominantBoneOnly && maxJoint !== jointIndex) {
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
const p = new Vector3(Number(positions[i * posStride]) || 0, Number(positions[i * posStride + 1]) || 0, Number(positions[i * posStride + 2]) || 0);
|
|
137
|
+
points.push(inverseBind.transformPointAffine(p, p));
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return uniquePoints(points);
|
|
141
|
+
}
|
|
142
|
+
function uniquePoints(points) {
|
|
143
|
+
const result = [];
|
|
144
|
+
const keys = new Set();
|
|
145
|
+
for (const point of points){
|
|
146
|
+
const key = `${Math.round(point.x * 100000)}:${Math.round(point.y * 100000)}:${Math.round(point.z * 100000)}`;
|
|
147
|
+
if (!keys.has(key)) {
|
|
148
|
+
keys.add(key);
|
|
149
|
+
result.push(point);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
return result;
|
|
153
|
+
}
|
|
154
|
+
function buildConvexHull(points, maxHullVertices, margin) {
|
|
155
|
+
const sampled = sampleSupportPoints(points, Math.max(8, maxHullVertices));
|
|
156
|
+
if (sampled.length < 4) {
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
const faces = bruteForceHullFaces(sampled);
|
|
160
|
+
if (faces.length < 4) {
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
const planes = mergePlanes(faces);
|
|
164
|
+
if (margin > 0) {
|
|
165
|
+
for (const plane of planes){
|
|
166
|
+
plane.distance -= margin;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return {
|
|
170
|
+
vertices: sampled.map((point)=>point.clone()),
|
|
171
|
+
planes,
|
|
172
|
+
edges: extractHullEdges(sampled, planes)
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
function sampleSupportPoints(points, maxCount) {
|
|
176
|
+
if (points.length <= maxCount) {
|
|
177
|
+
return [
|
|
178
|
+
...points
|
|
179
|
+
];
|
|
180
|
+
}
|
|
181
|
+
const dirs = buildSampleDirections(maxCount);
|
|
182
|
+
const selected = [];
|
|
183
|
+
const indices = new Set();
|
|
184
|
+
for (const dir of dirs){
|
|
185
|
+
let bestIndex = -1;
|
|
186
|
+
let bestDot = -Infinity;
|
|
187
|
+
for(let i = 0; i < points.length; i++){
|
|
188
|
+
const d = Vector3.dot(points[i], dir);
|
|
189
|
+
if (d > bestDot) {
|
|
190
|
+
bestDot = d;
|
|
191
|
+
bestIndex = i;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
if (bestIndex >= 0 && !indices.has(bestIndex)) {
|
|
195
|
+
indices.add(bestIndex);
|
|
196
|
+
selected.push(points[bestIndex]);
|
|
197
|
+
if (selected.length >= maxCount) {
|
|
198
|
+
break;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
return selected.length >= 4 ? selected : [
|
|
203
|
+
...points.slice(0, maxCount)
|
|
204
|
+
];
|
|
205
|
+
}
|
|
206
|
+
function buildSampleDirections(maxCount) {
|
|
207
|
+
const dirs = [
|
|
208
|
+
new Vector3(1, 0, 0),
|
|
209
|
+
new Vector3(-1, 0, 0),
|
|
210
|
+
new Vector3(0, 1, 0),
|
|
211
|
+
new Vector3(0, -1, 0),
|
|
212
|
+
new Vector3(0, 0, 1),
|
|
213
|
+
new Vector3(0, 0, -1)
|
|
214
|
+
];
|
|
215
|
+
const rings = Math.max(4, Math.ceil(Math.sqrt(maxCount)));
|
|
216
|
+
for(let y = 0; y < rings && dirs.length < maxCount; y++){
|
|
217
|
+
const v = rings === 1 ? 0 : -1 + 2 * y / (rings - 1);
|
|
218
|
+
const r = Math.sqrt(Math.max(0, 1 - v * v));
|
|
219
|
+
for(let x = 0; x < rings * 2 && dirs.length < maxCount; x++){
|
|
220
|
+
const a = x / (rings * 2) * Math.PI * 2;
|
|
221
|
+
dirs.push(new Vector3(Math.cos(a) * r, v, Math.sin(a) * r));
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
return dirs;
|
|
225
|
+
}
|
|
226
|
+
function bruteForceHullFaces(points) {
|
|
227
|
+
const center = new Vector3();
|
|
228
|
+
for (const point of points){
|
|
229
|
+
Vector3.add(center, point, center);
|
|
230
|
+
}
|
|
231
|
+
Vector3.scale(center, 1 / points.length, center);
|
|
232
|
+
const faces = [];
|
|
233
|
+
const faceKeys = new Set();
|
|
234
|
+
for(let i = 0; i < points.length - 2; i++){
|
|
235
|
+
for(let j = i + 1; j < points.length - 1; j++){
|
|
236
|
+
for(let k = j + 1; k < points.length; k++){
|
|
237
|
+
const ab = Vector3.sub(points[j], points[i]);
|
|
238
|
+
const ac = Vector3.sub(points[k], points[i]);
|
|
239
|
+
const normal = Vector3.cross(ab, ac);
|
|
240
|
+
const len = normal.magnitude;
|
|
241
|
+
if (len <= EPS) {
|
|
242
|
+
continue;
|
|
243
|
+
}
|
|
244
|
+
Vector3.scale(normal, 1 / len, normal);
|
|
245
|
+
let distance = -Vector3.dot(normal, points[i]);
|
|
246
|
+
if (Vector3.dot(normal, center) + distance > 0) {
|
|
247
|
+
Vector3.scale(normal, -1, normal);
|
|
248
|
+
distance = -Vector3.dot(normal, points[i]);
|
|
249
|
+
}
|
|
250
|
+
let valid = true;
|
|
251
|
+
for(let p = 0; p < points.length; p++){
|
|
252
|
+
if (p === i || p === j || p === k) {
|
|
253
|
+
continue;
|
|
254
|
+
}
|
|
255
|
+
if (Vector3.dot(normal, points[p]) + distance > 1e-5) {
|
|
256
|
+
valid = false;
|
|
257
|
+
break;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
if (!valid) {
|
|
261
|
+
continue;
|
|
262
|
+
}
|
|
263
|
+
const key = planeKey(normal, distance);
|
|
264
|
+
if (faceKeys.has(key)) {
|
|
265
|
+
continue;
|
|
266
|
+
}
|
|
267
|
+
faceKeys.add(key);
|
|
268
|
+
faces.push({
|
|
269
|
+
a: i,
|
|
270
|
+
b: j,
|
|
271
|
+
c: k,
|
|
272
|
+
normal: normal.clone(),
|
|
273
|
+
distance
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
return faces;
|
|
279
|
+
}
|
|
280
|
+
function mergePlanes(faces) {
|
|
281
|
+
const planes = [];
|
|
282
|
+
for (const face of faces){
|
|
283
|
+
if (planes.some((plane)=>Math.abs(Vector3.dot(plane.normal, face.normal) - 1) < 1e-4 && Math.abs(plane.distance - face.distance) < 1e-4)) {
|
|
284
|
+
continue;
|
|
285
|
+
}
|
|
286
|
+
planes.push({
|
|
287
|
+
normal: face.normal.clone(),
|
|
288
|
+
distance: face.distance
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
return planes;
|
|
292
|
+
}
|
|
293
|
+
function extractHullEdges(vertices, planes) {
|
|
294
|
+
const result = [];
|
|
295
|
+
const keys = new Set();
|
|
296
|
+
for(let i = 0; i < vertices.length - 1; i++){
|
|
297
|
+
for(let j = i + 1; j < vertices.length; j++){
|
|
298
|
+
const sharedPlaneCount = planes.reduce((count, plane)=>{
|
|
299
|
+
const di = Math.abs(Vector3.dot(plane.normal, vertices[i]) + plane.distance);
|
|
300
|
+
const dj = Math.abs(Vector3.dot(plane.normal, vertices[j]) + plane.distance);
|
|
301
|
+
return count + (di < 1e-4 && dj < 1e-4 ? 1 : 0);
|
|
302
|
+
}, 0);
|
|
303
|
+
if (sharedPlaneCount < 2) {
|
|
304
|
+
continue;
|
|
305
|
+
}
|
|
306
|
+
const key = `${i}:${j}`;
|
|
307
|
+
if (!keys.has(key)) {
|
|
308
|
+
keys.add(key);
|
|
309
|
+
result.push(i, j);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
return result;
|
|
314
|
+
}
|
|
315
|
+
function planeKey(normal, distance) {
|
|
316
|
+
return `${Math.round(normal.x * 10000)}:${Math.round(normal.y * 10000)}:${Math.round(normal.z * 10000)}:${Math.round(distance * 10000)}`;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
export { generateJointDynamicsConvexCollidersFromSkinWeights };
|
|
320
|
+
//# sourceMappingURL=convex_collider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"convex_collider.js","sources":["../../../src/animation/joint_dynamics/convex_collider.ts"],"sourcesContent":["import { Matrix4x4, Vector3 } from '@zephyr3d/base';\nimport { getVertexFormatComponentCount } from '@zephyr3d/device';\nimport type { Mesh, SceneNode } from '../../scene';\nimport { getSceneMeshAssetBinding } from '../../scene';\nimport type { AssetPrimitiveInfo, AssetSubMeshData } from '../../asset';\nimport type { SkinBinding } from '../skeleton';\nimport { ColliderForce, type ColliderR, type ConvexColliderPlane } from './types';\n\n/** @public */\nexport type JointDynamicsConvexColliderGenerationOptions = {\n weightThreshold?: number;\n dominantBoneOnly?: boolean;\n maxHullVertices?: number;\n margin?: number;\n minVertexCount?: number;\n friction?: number;\n inversed?: boolean;\n};\n\n/** @public */\nexport type JointDynamicsConvexColliderGenerationResult = {\n bone: SceneNode;\n collider: ColliderR;\n vertexCount: number;\n hullVertexCount: number;\n};\n\ntype SourcePrimitive = {\n primitive: AssetPrimitiveInfo;\n subMesh?: AssetSubMeshData;\n};\n\ntype Face = {\n a: number;\n b: number;\n c: number;\n normal: Vector3;\n distance: number;\n};\n\nconst EPS = 1e-6;\n\n/**\n * Generate convex JointDynamics colliders from skinned mesh bone weights.\n *\n * @public\n */\nexport function generateJointDynamicsConvexCollidersFromSkinWeights(\n meshes: readonly Mesh[],\n skinBinding: SkinBinding,\n bones: readonly SceneNode[],\n options: JointDynamicsConvexColliderGenerationOptions = {}\n): JointDynamicsConvexColliderGenerationResult[] {\n const sources = collectSourcePrimitives(meshes);\n const results: JointDynamicsConvexColliderGenerationResult[] = [];\n for (const bone of bones) {\n const jointIndex = skinBinding.joints.indexOf(bone);\n if (jointIndex < 0) {\n continue;\n }\n const points = collectBonePoints(sources, skinBinding, jointIndex, options);\n if (points.length < (options.minVertexCount ?? 8)) {\n continue;\n }\n const hull = buildConvexHull(points, options.maxHullVertices ?? 48, options.margin ?? 0);\n if (!hull || hull.vertices.length < 4 || hull.planes.length < 4) {\n continue;\n }\n results.push({\n bone,\n vertexCount: points.length,\n hullVertexCount: hull.vertices.length,\n collider: {\n radius: 0,\n radiusTailScale: 1,\n height: 0,\n friction: options.friction ?? 0,\n isInverseCollider: options.inversed ?? false,\n forceType: ColliderForce.Off,\n convex: hull\n }\n });\n }\n return results;\n}\n\nfunction collectSourcePrimitives(meshes: readonly Mesh[]): SourcePrimitive[] {\n const result: SourcePrimitive[] = [];\n const seen = new Set<AssetPrimitiveInfo>();\n for (const mesh of meshes) {\n const binding = getSceneMeshAssetBinding(mesh);\n const subMesh = binding?.subMesh as AssetSubMeshData | undefined;\n const primitive = subMesh?.primitive ?? null;\n if (primitive && !seen.has(primitive)) {\n seen.add(primitive);\n result.push({ primitive, subMesh });\n }\n }\n return result;\n}\n\nfunction collectBonePoints(\n sources: readonly SourcePrimitive[],\n skinBinding: SkinBinding,\n jointIndex: number,\n options: JointDynamicsConvexColliderGenerationOptions\n): Vector3[] {\n const points: Vector3[] = [];\n const weightThreshold = options.weightThreshold ?? 0.45;\n const dominantBoneOnly = options.dominantBoneOnly ?? true;\n const inverseBind = skinBinding.inverseBindMatrices[jointIndex] ?? Matrix4x4.identity();\n for (const source of sources) {\n const primitive = source.primitive;\n const positionInfo = primitive.vertices.position;\n const blendIndexInfo = primitive.vertices.blendIndices;\n const blendWeightInfo = primitive.vertices.blendWeights;\n if (!positionInfo || !blendIndexInfo || !blendWeightInfo) {\n continue;\n }\n const positions = positionInfo.data;\n const blendIndices = blendIndexInfo.data;\n const blendWeights = blendWeightInfo.data;\n const posStride = getVertexFormatComponentCount(positionInfo.format);\n const idxStride = getVertexFormatComponentCount(blendIndexInfo.format);\n const weightStride = getVertexFormatComponentCount(blendWeightInfo.format);\n const vertexCount = Math.floor(positions.length / posStride);\n for (let i = 0; i < vertexCount; i++) {\n let weight = 0;\n let maxWeight = -1;\n let maxJoint = -1;\n const influenceCount = Math.min(4, idxStride, weightStride);\n for (let k = 0; k < influenceCount; k++) {\n const idx = Number(blendIndices[i * idxStride + k]) | 0;\n const w = Number(blendWeights[i * weightStride + k]) || 0;\n if (idx === jointIndex) {\n weight = Math.max(weight, w);\n }\n if (w > maxWeight) {\n maxWeight = w;\n maxJoint = idx;\n }\n }\n if (weight < weightThreshold || (dominantBoneOnly && maxJoint !== jointIndex)) {\n continue;\n }\n const p = new Vector3(\n Number(positions[i * posStride]) || 0,\n Number(positions[i * posStride + 1]) || 0,\n Number(positions[i * posStride + 2]) || 0\n );\n points.push(inverseBind.transformPointAffine(p, p));\n }\n }\n return uniquePoints(points);\n}\n\nfunction uniquePoints(points: readonly Vector3[]): Vector3[] {\n const result: Vector3[] = [];\n const keys = new Set<string>();\n for (const point of points) {\n const key = `${Math.round(point.x * 100000)}:${Math.round(point.y * 100000)}:${Math.round(point.z * 100000)}`;\n if (!keys.has(key)) {\n keys.add(key);\n result.push(point);\n }\n }\n return result;\n}\n\nfunction buildConvexHull(points: readonly Vector3[], maxHullVertices: number, margin: number) {\n const sampled = sampleSupportPoints(points, Math.max(8, maxHullVertices));\n if (sampled.length < 4) {\n return null;\n }\n const faces = bruteForceHullFaces(sampled);\n if (faces.length < 4) {\n return null;\n }\n const planes = mergePlanes(faces);\n if (margin > 0) {\n for (const plane of planes) {\n plane.distance -= margin;\n }\n }\n return {\n vertices: sampled.map((point) => point.clone()),\n planes,\n edges: extractHullEdges(sampled, planes)\n };\n}\n\nfunction sampleSupportPoints(points: readonly Vector3[], maxCount: number): Vector3[] {\n if (points.length <= maxCount) {\n return [...points];\n }\n const dirs = buildSampleDirections(maxCount);\n const selected: Vector3[] = [];\n const indices = new Set<number>();\n for (const dir of dirs) {\n let bestIndex = -1;\n let bestDot = -Infinity;\n for (let i = 0; i < points.length; i++) {\n const d = Vector3.dot(points[i], dir);\n if (d > bestDot) {\n bestDot = d;\n bestIndex = i;\n }\n }\n if (bestIndex >= 0 && !indices.has(bestIndex)) {\n indices.add(bestIndex);\n selected.push(points[bestIndex]);\n if (selected.length >= maxCount) {\n break;\n }\n }\n }\n return selected.length >= 4 ? selected : [...points.slice(0, maxCount)];\n}\n\nfunction buildSampleDirections(maxCount: number): Vector3[] {\n const dirs = [\n new Vector3(1, 0, 0),\n new Vector3(-1, 0, 0),\n new Vector3(0, 1, 0),\n new Vector3(0, -1, 0),\n new Vector3(0, 0, 1),\n new Vector3(0, 0, -1)\n ];\n const rings = Math.max(4, Math.ceil(Math.sqrt(maxCount)));\n for (let y = 0; y < rings && dirs.length < maxCount; y++) {\n const v = rings === 1 ? 0 : -1 + (2 * y) / (rings - 1);\n const r = Math.sqrt(Math.max(0, 1 - v * v));\n for (let x = 0; x < rings * 2 && dirs.length < maxCount; x++) {\n const a = (x / (rings * 2)) * Math.PI * 2;\n dirs.push(new Vector3(Math.cos(a) * r, v, Math.sin(a) * r));\n }\n }\n return dirs;\n}\n\nfunction bruteForceHullFaces(points: readonly Vector3[]): Face[] {\n const center = new Vector3();\n for (const point of points) {\n Vector3.add(center, point, center);\n }\n Vector3.scale(center, 1 / points.length, center);\n const faces: Face[] = [];\n const faceKeys = new Set<string>();\n for (let i = 0; i < points.length - 2; i++) {\n for (let j = i + 1; j < points.length - 1; j++) {\n for (let k = j + 1; k < points.length; k++) {\n const ab = Vector3.sub(points[j], points[i]);\n const ac = Vector3.sub(points[k], points[i]);\n const normal = Vector3.cross(ab, ac);\n const len = normal.magnitude;\n if (len <= EPS) {\n continue;\n }\n Vector3.scale(normal, 1 / len, normal);\n let distance = -Vector3.dot(normal, points[i]);\n if (Vector3.dot(normal, center) + distance > 0) {\n Vector3.scale(normal, -1, normal);\n distance = -Vector3.dot(normal, points[i]);\n }\n let valid = true;\n for (let p = 0; p < points.length; p++) {\n if (p === i || p === j || p === k) {\n continue;\n }\n if (Vector3.dot(normal, points[p]) + distance > 1e-5) {\n valid = false;\n break;\n }\n }\n if (!valid) {\n continue;\n }\n const key = planeKey(normal, distance);\n if (faceKeys.has(key)) {\n continue;\n }\n faceKeys.add(key);\n faces.push({ a: i, b: j, c: k, normal: normal.clone(), distance });\n }\n }\n }\n return faces;\n}\n\nfunction mergePlanes(faces: readonly Face[]): ConvexColliderPlane[] {\n const planes: ConvexColliderPlane[] = [];\n for (const face of faces) {\n if (\n planes.some(\n (plane) =>\n Math.abs(Vector3.dot(plane.normal, face.normal) - 1) < 1e-4 &&\n Math.abs(plane.distance - face.distance) < 1e-4\n )\n ) {\n continue;\n }\n planes.push({ normal: face.normal.clone(), distance: face.distance });\n }\n return planes;\n}\n\nfunction extractHullEdges(\n vertices: readonly Vector3[],\n planes: readonly ConvexColliderPlane[]\n): number[] {\n const result: number[] = [];\n const keys = new Set<string>();\n for (let i = 0; i < vertices.length - 1; i++) {\n for (let j = i + 1; j < vertices.length; j++) {\n const sharedPlaneCount = planes.reduce((count, plane) => {\n const di = Math.abs(Vector3.dot(plane.normal, vertices[i]) + plane.distance);\n const dj = Math.abs(Vector3.dot(plane.normal, vertices[j]) + plane.distance);\n return count + (di < 1e-4 && dj < 1e-4 ? 1 : 0);\n }, 0);\n if (sharedPlaneCount < 2) {\n continue;\n }\n const key = `${i}:${j}`;\n if (!keys.has(key)) {\n keys.add(key);\n result.push(i, j);\n }\n }\n }\n return result;\n}\n\nfunction planeKey(normal: Vector3, distance: number): string {\n return `${Math.round(normal.x * 10000)}:${Math.round(normal.y * 10000)}:${Math.round(normal.z * 10000)}:${Math.round(distance * 10000)}`;\n}\n"],"names":["EPS","generateJointDynamicsConvexCollidersFromSkinWeights","meshes","skinBinding","bones","options","sources","collectSourcePrimitives","results","bone","jointIndex","joints","indexOf","points","collectBonePoints","length","minVertexCount","hull","buildConvexHull","maxHullVertices","margin","vertices","planes","push","vertexCount","hullVertexCount","collider","radius","radiusTailScale","height","friction","isInverseCollider","inversed","forceType","ColliderForce","Off","convex","result","seen","Set","mesh","binding","getSceneMeshAssetBinding","subMesh","primitive","has","add","weightThreshold","dominantBoneOnly","inverseBind","inverseBindMatrices","Matrix4x4","identity","source","positionInfo","position","blendIndexInfo","blendIndices","blendWeightInfo","blendWeights","positions","data","posStride","getVertexFormatComponentCount","format","idxStride","weightStride","Math","floor","i","weight","maxWeight","maxJoint","influenceCount","min","k","idx","Number","w","max","p","Vector3","transformPointAffine","uniquePoints","keys","point","key","round","x","y","z","sampled","sampleSupportPoints","faces","bruteForceHullFaces","mergePlanes","plane","distance","map","clone","edges","extractHullEdges","maxCount","dirs","buildSampleDirections","selected","indices","dir","bestIndex","bestDot","Infinity","d","dot","slice","rings","ceil","sqrt","v","r","a","PI","cos","sin","center","scale","faceKeys","j","ab","sub","ac","normal","cross","len","magnitude","valid","planeKey","b","c","face","some","abs","sharedPlaneCount","reduce","count","di","dj"],"mappings":"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"}
|
|
@@ -58,6 +58,37 @@ function hasRawQuery(spec) {
|
|
|
58
58
|
}
|
|
59
59
|
return query.split('&').some((part)=>part.trim() === 'raw');
|
|
60
60
|
}
|
|
61
|
+
/**
|
|
62
|
+
* Realm-global storage for shared script module registries, keyed by
|
|
63
|
+
* registry namespace. Bundles built by the same `ScriptRegistry` share one
|
|
64
|
+
* module registry so a module evaluates once and its exports (including
|
|
65
|
+
* module-level singletons) are identical across entry bundles.
|
|
66
|
+
* @internal
|
|
67
|
+
*/ const SCRIPT_MODULES_GLOBAL_KEY = '__z3dScriptModules';
|
|
68
|
+
let scriptRegistryNamespaceCounter = 0;
|
|
69
|
+
function nextScriptRegistryNamespace() {
|
|
70
|
+
return `z3d-scripts:${++scriptRegistryNamespaceCounter}`;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Monotonic serial stamped into each built bundle. The shared runtime uses it
|
|
74
|
+
* to ignore registrations coming from bundles older than the one that already
|
|
75
|
+
* registered a module, so late imports of stale bundles cannot downgrade a
|
|
76
|
+
* module record.
|
|
77
|
+
* @internal
|
|
78
|
+
*/ let scriptBuildSerialCounter = 0;
|
|
79
|
+
/**
|
|
80
|
+
* FNV-1a hash of module code, used to version shared module records.
|
|
81
|
+
* Rebuilding an unchanged module keeps its already-evaluated instance;
|
|
82
|
+
* changed content replaces the record so the next load re-evaluates.
|
|
83
|
+
* @internal
|
|
84
|
+
*/ function hashModuleCode(text) {
|
|
85
|
+
let h = 0x811c9dc5;
|
|
86
|
+
for(let i = 0; i < text.length; i++){
|
|
87
|
+
h ^= text.charCodeAt(i);
|
|
88
|
+
h = Math.imul(h, 0x01000193);
|
|
89
|
+
}
|
|
90
|
+
return (h >>> 0).toString(36);
|
|
91
|
+
}
|
|
61
92
|
/**
|
|
62
93
|
* Resolves, builds, and serves runtime modules using a VFS.
|
|
63
94
|
*
|
|
@@ -73,6 +104,13 @@ function hasRawQuery(spec) {
|
|
|
73
104
|
*
|
|
74
105
|
* Caching:
|
|
75
106
|
* - Built bundles are memoized in `_built` map keyed by canonical source path.
|
|
107
|
+
* - At runtime, all bundles built by the same registry share one realm-global
|
|
108
|
+
* module registry (keyed by a per-registry namespace): a local module is
|
|
109
|
+
* evaluated once no matter how many entry bundles inline it, so module-level
|
|
110
|
+
* singletons are identical across entries. Module records are versioned by a
|
|
111
|
+
* content hash — rebuilding an unchanged module reuses its evaluated
|
|
112
|
+
* instance, while changed content replaces the record so the next load
|
|
113
|
+
* re-evaluates the new code.
|
|
76
114
|
*
|
|
77
115
|
* @public
|
|
78
116
|
*/ class ScriptRegistry {
|
|
@@ -81,6 +119,7 @@ function hasRawQuery(spec) {
|
|
|
81
119
|
_built;
|
|
82
120
|
_building;
|
|
83
121
|
_builtDeps;
|
|
122
|
+
_namespace;
|
|
84
123
|
/**
|
|
85
124
|
* @param vfs - The virtual file system for existence checks, reads, and path ops.
|
|
86
125
|
* @param scriptsRoot - Root directory for script resolution (used with `#/` specifiers).
|
|
@@ -90,6 +129,7 @@ function hasRawQuery(spec) {
|
|
|
90
129
|
this._built = new Map();
|
|
91
130
|
this._building = new Map();
|
|
92
131
|
this._builtDeps = new Map();
|
|
132
|
+
this._namespace = nextScriptRegistryNamespace();
|
|
93
133
|
}
|
|
94
134
|
/**
|
|
95
135
|
* The active virtual file system.
|
|
@@ -104,6 +144,11 @@ function hasRawQuery(spec) {
|
|
|
104
144
|
this._built.clear();
|
|
105
145
|
this._building.clear();
|
|
106
146
|
this._builtDeps.clear();
|
|
147
|
+
// Rotate the shared module registry namespace: modules from the old VFS
|
|
148
|
+
// must not be reused as instances for same-named modules of the new VFS.
|
|
149
|
+
const root = globalThis[SCRIPT_MODULES_GLOBAL_KEY];
|
|
150
|
+
root?.delete(this._namespace);
|
|
151
|
+
this._namespace = nextScriptRegistryNamespace();
|
|
107
152
|
}
|
|
108
153
|
}
|
|
109
154
|
/**
|
|
@@ -318,11 +363,16 @@ function hasRawQuery(spec) {
|
|
|
318
363
|
if (!entry) {
|
|
319
364
|
return '';
|
|
320
365
|
}
|
|
366
|
+
// Replace content hashes with Merkle-style effective versions so that a
|
|
367
|
+
// module whose (transitive) dependencies changed is re-registered and
|
|
368
|
+
// re-evaluated even when its own source is unchanged.
|
|
369
|
+
this.applyEffectiveVersions(modules);
|
|
370
|
+
const serial = ++scriptBuildSerialCounter;
|
|
321
371
|
const chunks = [
|
|
322
372
|
this.getSystemBundleRuntime()
|
|
323
373
|
];
|
|
324
374
|
for (const module of modules.values()){
|
|
325
|
-
chunks.push(`__z3dRegister(${JSON.stringify(module.id)}, () => {\n${module.systemCode}\n});`);
|
|
375
|
+
chunks.push(`__z3dRegister(${JSON.stringify(module.id)}, ${JSON.stringify(module.version)}, ${serial}, () => {\n${module.systemCode}\n});`);
|
|
326
376
|
}
|
|
327
377
|
chunks.push(`const __z3dEntry = await __z3dLoad(${JSON.stringify(entry.id)});\n` + `const plugin = __z3dEntry.plugin;\n` + `const __z3dDefault = __z3dEntry.default ?? __z3dEntry.plugin ?? __z3dEntry;\n` + `export { plugin };\n` + `export default __z3dDefault;\n` + `//# sourceURL=${entry.id}`);
|
|
328
378
|
const url = toDataUrl(chunks.join('\n'), entry.id);
|
|
@@ -345,11 +395,47 @@ function hasRawQuery(spec) {
|
|
|
345
395
|
const rewritten = await this.rewriteImportsToLogicalIds(esmCode, module.id);
|
|
346
396
|
module.deps = rewritten.deps;
|
|
347
397
|
module.systemCode = await this.transpileToSystemModule(rewritten.code, module.id);
|
|
398
|
+
module.version = hashModuleCode(module.systemCode);
|
|
348
399
|
for (const dep of module.deps){
|
|
349
400
|
await this.collectModule(dep, modules);
|
|
350
401
|
}
|
|
351
402
|
return module;
|
|
352
403
|
}
|
|
404
|
+
/**
|
|
405
|
+
* Rewrites each collected module's version to a Merkle-style hash combining
|
|
406
|
+
* its own content hash with the effective versions of its local
|
|
407
|
+
* dependencies. Cycles fall back to the plain content hash, which is
|
|
408
|
+
* deterministic on both sides of the cycle.
|
|
409
|
+
*/ applyEffectiveVersions(modules) {
|
|
410
|
+
const effective = new Map();
|
|
411
|
+
const visiting = new Set();
|
|
412
|
+
const visit = (id)=>{
|
|
413
|
+
const cached = effective.get(id);
|
|
414
|
+
if (cached !== undefined) {
|
|
415
|
+
return cached;
|
|
416
|
+
}
|
|
417
|
+
const module = modules.get(id);
|
|
418
|
+
if (!module) {
|
|
419
|
+
return '';
|
|
420
|
+
}
|
|
421
|
+
if (visiting.has(id)) {
|
|
422
|
+
return module.version;
|
|
423
|
+
}
|
|
424
|
+
visiting.add(id);
|
|
425
|
+
let acc = module.version;
|
|
426
|
+
for (const dep of module.deps){
|
|
427
|
+
if (modules.has(dep)) {
|
|
428
|
+
acc = hashModuleCode(`${acc}:${dep}:${visit(dep)}`);
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
visiting.delete(id);
|
|
432
|
+
effective.set(id, acc);
|
|
433
|
+
return acc;
|
|
434
|
+
};
|
|
435
|
+
for (const module of modules.values()){
|
|
436
|
+
module.version = visit(module.id);
|
|
437
|
+
}
|
|
438
|
+
}
|
|
353
439
|
async resolveModuleInfo(id) {
|
|
354
440
|
const srcPath = await this.resolveSourcePath(id);
|
|
355
441
|
if (!srcPath) {
|
|
@@ -362,7 +448,8 @@ function hasRawQuery(spec) {
|
|
|
362
448
|
path,
|
|
363
449
|
type: srcPath.type,
|
|
364
450
|
deps: [],
|
|
365
|
-
systemCode: ''
|
|
451
|
+
systemCode: '',
|
|
452
|
+
version: ''
|
|
366
453
|
};
|
|
367
454
|
}
|
|
368
455
|
getTypeScriptRuntime() {
|
|
@@ -467,8 +554,19 @@ function hasRawQuery(spec) {
|
|
|
467
554
|
}
|
|
468
555
|
getSystemBundleRuntime() {
|
|
469
556
|
return `
|
|
470
|
-
const __z3dRegistry =
|
|
557
|
+
const __z3dRegistry = (() => {
|
|
558
|
+
const root = (globalThis.${SCRIPT_MODULES_GLOBAL_KEY} ??= new Map());
|
|
559
|
+
const ns = ${JSON.stringify(this._namespace)};
|
|
560
|
+
let registry = root.get(ns);
|
|
561
|
+
if (!registry) {
|
|
562
|
+
registry = new Map();
|
|
563
|
+
root.set(ns, registry);
|
|
564
|
+
}
|
|
565
|
+
return registry;
|
|
566
|
+
})();
|
|
471
567
|
let __z3dCurrentId = '';
|
|
568
|
+
let __z3dCurrentVersion = '';
|
|
569
|
+
let __z3dCurrentSerial = 0;
|
|
472
570
|
const System = {
|
|
473
571
|
register(deps, declare) {
|
|
474
572
|
if (!__z3dCurrentId) {
|
|
@@ -476,6 +574,8 @@ const System = {
|
|
|
476
574
|
}
|
|
477
575
|
__z3dRegistry.set(__z3dCurrentId, {
|
|
478
576
|
id: __z3dCurrentId,
|
|
577
|
+
version: __z3dCurrentVersion,
|
|
578
|
+
serial: __z3dCurrentSerial,
|
|
479
579
|
deps,
|
|
480
580
|
declare,
|
|
481
581
|
exports: Object.create(null),
|
|
@@ -486,13 +586,31 @@ const System = {
|
|
|
486
586
|
});
|
|
487
587
|
}
|
|
488
588
|
};
|
|
489
|
-
function __z3dRegister(id, factory) {
|
|
490
|
-
|
|
589
|
+
function __z3dRegister(id, version, serial, factory) {
|
|
590
|
+
// The module registry is shared across bundles: a module already registered
|
|
591
|
+
// by another bundle with the same effective version is reused (so
|
|
592
|
+
// module-level singletons stay unique), while changed content replaces the
|
|
593
|
+
// record so the next load re-evaluates the new code. A record from a newer
|
|
594
|
+
// build or one that is currently executing is never replaced.
|
|
595
|
+
const existing = __z3dRegistry.get(id);
|
|
596
|
+
if (existing && (existing.version === version || existing.state === 1 || existing.serial > serial)) {
|
|
597
|
+
if (serial > existing.serial) {
|
|
598
|
+
existing.serial = serial;
|
|
599
|
+
}
|
|
600
|
+
return;
|
|
601
|
+
}
|
|
602
|
+
const prevId = __z3dCurrentId;
|
|
603
|
+
const prevVersion = __z3dCurrentVersion;
|
|
604
|
+
const prevSerial = __z3dCurrentSerial;
|
|
491
605
|
__z3dCurrentId = id;
|
|
606
|
+
__z3dCurrentVersion = version;
|
|
607
|
+
__z3dCurrentSerial = serial;
|
|
492
608
|
try {
|
|
493
609
|
factory();
|
|
494
610
|
} finally {
|
|
495
|
-
__z3dCurrentId =
|
|
611
|
+
__z3dCurrentId = prevId;
|
|
612
|
+
__z3dCurrentVersion = prevVersion;
|
|
613
|
+
__z3dCurrentSerial = prevSerial;
|
|
496
614
|
}
|
|
497
615
|
}
|
|
498
616
|
function __z3dResolve(spec, parentId) {
|