@playdrop/playdrop-cli 0.7.15 → 0.7.16
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/config/client-meta.json +1 -1
- package/node_modules/@playdrop/ai-client/dist/index.js +461 -444
- package/node_modules/@playdrop/ai-client/package.json +1 -1
- package/node_modules/@playdrop/api-client/dist/client.js +903 -882
- package/node_modules/@playdrop/api-client/dist/core/errors.js +69 -45
- package/node_modules/@playdrop/api-client/dist/core/request.js +188 -159
- package/node_modules/@playdrop/api-client/dist/domains/admin.js +516 -491
- package/node_modules/@playdrop/api-client/dist/domains/ai.js +40 -14
- package/node_modules/@playdrop/api-client/dist/domains/apps.js +480 -462
- package/node_modules/@playdrop/api-client/dist/domains/asset-packs.js +439 -419
- package/node_modules/@playdrop/api-client/dist/domains/assets.js +696 -676
- package/node_modules/@playdrop/api-client/dist/domains/auth.js +346 -320
- package/node_modules/@playdrop/api-client/dist/domains/comments.js +124 -98
- package/node_modules/@playdrop/api-client/dist/domains/free-credits.js +91 -65
- package/node_modules/@playdrop/api-client/dist/domains/me.js +71 -45
- package/node_modules/@playdrop/api-client/dist/domains/payments.js +407 -386
- package/node_modules/@playdrop/api-client/dist/domains/player-meta.js +144 -118
- package/node_modules/@playdrop/api-client/dist/domains/search.js +117 -104
- package/node_modules/@playdrop/api-client/dist/domains/tags.js +188 -162
- package/node_modules/@playdrop/api-client/dist/index.js +993 -552
- package/node_modules/@playdrop/api-client/package.json +1 -1
- package/node_modules/@playdrop/boxel-three/dist/src/animations.js +84 -62
- package/node_modules/@playdrop/boxel-three/dist/src/builders.js +341 -308
- package/node_modules/@playdrop/boxel-three/dist/src/context.js +55 -29
- package/node_modules/@playdrop/boxel-three/dist/src/exporters/glb.js +856 -858
- package/node_modules/@playdrop/boxel-three/dist/src/exporters/image.js +267 -261
- package/node_modules/@playdrop/boxel-three/dist/src/index.js +64 -15
- package/node_modules/@playdrop/boxel-three/dist/src/instantiate.js +89 -63
- package/node_modules/@playdrop/boxel-three/dist/src/nodes.js +81 -56
- package/node_modules/@playdrop/boxel-three/dist/src/overlays.js +112 -86
- package/node_modules/@playdrop/boxel-three/dist/src/primitives.js +45 -17
- package/node_modules/@playdrop/boxel-three/dist/src/scene.js +160 -136
- package/node_modules/@playdrop/boxel-three/dist/src/skinned-mesh.js +582 -584
- package/node_modules/@playdrop/boxel-three/dist/src/texture-atlas.js +123 -97
- package/node_modules/@playdrop/boxel-three/dist/src/textures.js +207 -182
- package/node_modules/@playdrop/boxel-three/dist/src/types.js +15 -1
- package/node_modules/@playdrop/boxel-three/dist/src/voxels/faces.js +451 -425
- package/node_modules/@playdrop/boxel-three/dist/src/voxels/mesher.js +109 -84
- package/node_modules/@playdrop/boxel-three/dist/test/export-image.playwright.test.js +127 -106
- package/node_modules/@playdrop/boxel-three/dist/test/fixtures/render-worker.js +73 -51
- package/node_modules/@playdrop/boxel-three/dist/test/glb-skinned.test.js +97 -79
- package/node_modules/@playdrop/boxel-three/dist/test/index.test.js +29 -7
- package/node_modules/@playdrop/boxel-three/dist/test/instantiate.test.js +80 -60
- package/node_modules/@playdrop/boxel-three/dist/test/overlays.test.js +41 -19
- package/node_modules/@playdrop/boxel-three/dist/test/scene-filter.test.js +72 -50
- package/node_modules/@playdrop/boxel-three/dist/test/scene-smoke.test.js +84 -62
- package/node_modules/@playdrop/boxel-three/dist/test/skinned-mesh.test.js +69 -47
- package/node_modules/@playdrop/boxel-three/dist/test/textured-overlay.test.js +129 -109
- package/node_modules/@playdrop/boxel-three/dist/test/voxels.test.js +40 -18
- package/node_modules/@playdrop/boxel-three/package.json +1 -1
- package/node_modules/@playdrop/config/client-meta.json +1 -1
- package/node_modules/@playdrop/types/dist/api.js +289 -203
- package/node_modules/@playdrop/types/dist/app-capability-filters.js +112 -62
- package/node_modules/@playdrop/types/dist/app.js +91 -45
- package/node_modules/@playdrop/types/dist/asset-pack.js +37 -5
- package/node_modules/@playdrop/types/dist/asset-spec.js +170 -90
- package/node_modules/@playdrop/types/dist/asset.js +186 -108
- package/node_modules/@playdrop/types/dist/content-license.js +49 -15
- package/node_modules/@playdrop/types/dist/creator-public-image.js +60 -32
- package/node_modules/@playdrop/types/dist/ecs.js +102 -82
- package/node_modules/@playdrop/types/dist/engine-builtins.js +603 -573
- package/node_modules/@playdrop/types/dist/entity.js +63 -53
- package/node_modules/@playdrop/types/dist/graph.js +116 -80
- package/node_modules/@playdrop/types/dist/index.js +47 -20
- package/node_modules/@playdrop/types/dist/owned-assets.js +55 -33
- package/node_modules/@playdrop/types/dist/player-meta.js +151 -100
- package/node_modules/@playdrop/types/dist/realtime.js +27 -7
- package/node_modules/@playdrop/types/dist/version.js +182 -124
- package/node_modules/@playdrop/types/package.json +1 -1
- package/package.json +1 -1
|
@@ -1,625 +1,623 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
+
var __export = (target, all) => {
|
|
6
|
+
for (var name in all)
|
|
7
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
8
|
+
};
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
|
+
for (let key of __getOwnPropNames(from))
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
13
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
14
|
+
}
|
|
15
|
+
return to;
|
|
16
|
+
};
|
|
17
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
18
|
+
var skinned_mesh_exports = {};
|
|
19
|
+
__export(skinned_mesh_exports, {
|
|
20
|
+
buildEntitySkinnedMesh: () => buildEntitySkinnedMesh
|
|
21
|
+
});
|
|
22
|
+
module.exports = __toCommonJS(skinned_mesh_exports);
|
|
23
|
+
var import_context = require("./context.js");
|
|
24
|
+
var import_builders = require("./builders.js");
|
|
25
|
+
var import_texture_atlas = require("./texture-atlas.js");
|
|
4
26
|
function applyNodeTransforms(THREE, object, node) {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
27
|
+
object.position.set(node.position[0], node.position[1], node.position[2]);
|
|
28
|
+
if (node.rotation) {
|
|
29
|
+
object.rotation.set(THREE.MathUtils.degToRad(node.rotation[0]), THREE.MathUtils.degToRad(node.rotation[1]), THREE.MathUtils.degToRad(node.rotation[2]));
|
|
30
|
+
}
|
|
31
|
+
if (node.scale) {
|
|
32
|
+
object.scale.set(node.scale[0], node.scale[1], node.scale[2]);
|
|
33
|
+
}
|
|
12
34
|
}
|
|
13
35
|
function assignPrimitiveOwner(object, ownerId) {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
36
|
+
const stack = [object];
|
|
37
|
+
while (stack.length > 0) {
|
|
38
|
+
const current = stack.pop();
|
|
39
|
+
current.userData = { ...current.userData ?? {}, primitiveOwnerId: ownerId };
|
|
40
|
+
current.children.forEach((child) => stack.push(child));
|
|
41
|
+
}
|
|
20
42
|
}
|
|
21
|
-
function buildNodeGraph(context, node, visitedPrimitives = new Set()) {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
if (node.center) {
|
|
37
|
-
primitiveObject.position.set(node.center[0], node.center[1], node.center[2]);
|
|
38
|
-
}
|
|
39
|
-
assignPrimitiveOwner(primitiveObject, node.id);
|
|
40
|
-
object.add(primitiveObject);
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
else if (node.size) {
|
|
45
|
-
const placeholder = createBoundingBoxMesh(context, node.size);
|
|
46
|
-
placeholder.userData = {
|
|
47
|
-
...(placeholder.userData ?? {}),
|
|
48
|
-
primitiveOwnerId: node.id,
|
|
49
|
-
size: [...node.size],
|
|
50
|
-
};
|
|
43
|
+
function buildNodeGraph(context, node, visitedPrimitives = /* @__PURE__ */ new Set()) {
|
|
44
|
+
const object = new context.THREE.Object3D();
|
|
45
|
+
object.name = node.id;
|
|
46
|
+
object.userData = {
|
|
47
|
+
...object.userData ?? {},
|
|
48
|
+
nodeId: node.id
|
|
49
|
+
};
|
|
50
|
+
applyNodeTransforms(context.THREE, object, node);
|
|
51
|
+
const nodes = /* @__PURE__ */ new Map();
|
|
52
|
+
nodes.set(node.id, object);
|
|
53
|
+
if (node.mesh) {
|
|
54
|
+
const primitive = context.primitivesById.get(node.mesh);
|
|
55
|
+
if (primitive) {
|
|
56
|
+
const primitiveObject = (0, import_builders.createPrimitiveObject)(context, primitive, "centered", visitedPrimitives);
|
|
57
|
+
if (primitiveObject) {
|
|
51
58
|
if (node.center) {
|
|
52
|
-
|
|
59
|
+
primitiveObject.position.set(node.center[0], node.center[1], node.center[2]);
|
|
53
60
|
}
|
|
54
|
-
|
|
61
|
+
assignPrimitiveOwner(primitiveObject, node.id);
|
|
62
|
+
object.add(primitiveObject);
|
|
63
|
+
}
|
|
55
64
|
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
65
|
+
} else if (node.size) {
|
|
66
|
+
const placeholder = (0, import_builders.createBoundingBoxMesh)(context, node.size);
|
|
67
|
+
placeholder.userData = {
|
|
68
|
+
...placeholder.userData ?? {},
|
|
69
|
+
primitiveOwnerId: node.id,
|
|
70
|
+
size: [...node.size]
|
|
71
|
+
};
|
|
72
|
+
if (node.center) {
|
|
73
|
+
placeholder.position.set(node.center[0], node.center[1], node.center[2]);
|
|
74
|
+
}
|
|
75
|
+
object.add(placeholder);
|
|
76
|
+
}
|
|
77
|
+
node.children.forEach((child) => {
|
|
78
|
+
const childGraph = buildNodeGraph(context, child, new Set(visitedPrimitives));
|
|
79
|
+
object.add(childGraph.object);
|
|
80
|
+
childGraph.nodes.forEach((childObject, childId) => nodes.set(childId, childObject));
|
|
81
|
+
});
|
|
82
|
+
return { object, nodes };
|
|
62
83
|
}
|
|
63
84
|
function buildBoneHierarchy(THREE, node, parentBone, bones, boneMap, boneIndexMap) {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
85
|
+
const bone = new THREE.Bone();
|
|
86
|
+
bone.name = node.id;
|
|
87
|
+
bone.position.set(node.position[0], node.position[1], node.position[2]);
|
|
88
|
+
if (node.rotation) {
|
|
89
|
+
bone.rotation.set(THREE.MathUtils.degToRad(node.rotation[0]), THREE.MathUtils.degToRad(node.rotation[1]), THREE.MathUtils.degToRad(node.rotation[2]));
|
|
90
|
+
}
|
|
91
|
+
if (node.scale) {
|
|
92
|
+
bone.scale.set(node.scale[0], node.scale[1], node.scale[2]);
|
|
93
|
+
}
|
|
94
|
+
if (parentBone) {
|
|
95
|
+
parentBone.add(bone);
|
|
96
|
+
}
|
|
97
|
+
bones.push(bone);
|
|
98
|
+
boneMap.set(node.id, bone);
|
|
99
|
+
boneIndexMap.set(node.id, bones.length - 1);
|
|
100
|
+
node.children.forEach((child) => {
|
|
101
|
+
buildBoneHierarchy(THREE, child, bone, bones, boneMap, boneIndexMap);
|
|
102
|
+
});
|
|
82
103
|
}
|
|
83
104
|
function materialToColor(THREE, material) {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
105
|
+
if ("color" in material && material.color instanceof THREE.Color) {
|
|
106
|
+
return material.color.clone();
|
|
107
|
+
}
|
|
108
|
+
return new THREE.Color(0.5, 0.5, 0.5);
|
|
88
109
|
}
|
|
89
110
|
const CONNECT_FALLOFF_RATIO = 0.35;
|
|
90
111
|
const CONNECT_MIN_RADIUS = 0.25;
|
|
91
112
|
const CONNECT_SIGMA_MULTIPLIER = 0.55;
|
|
92
113
|
const CONNECT_WEIGHT_EPSILON = 1e-4;
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
114
|
+
function buildEntitySkinnedMesh(options) {
|
|
115
|
+
const { THREE, entity } = options;
|
|
116
|
+
const warnings = [];
|
|
117
|
+
const builderContext = (0, import_context.createBuilderContext)({
|
|
118
|
+
THREE,
|
|
119
|
+
settings: { enableShadows: false }
|
|
120
|
+
});
|
|
121
|
+
(0, import_context.updateBuilderContext)(builderContext, { entity });
|
|
122
|
+
const { object: nodeRootObject, nodes: nodeObjects } = buildNodeGraph(builderContext, entity.geometry.root);
|
|
123
|
+
nodeRootObject.updateMatrixWorld(true);
|
|
124
|
+
const nodeDefs = /* @__PURE__ */ new Map();
|
|
125
|
+
const parentMap = /* @__PURE__ */ new Map();
|
|
126
|
+
(function collect(node, parentId) {
|
|
127
|
+
nodeDefs.set(node.id, node);
|
|
128
|
+
parentMap.set(node.id, parentId);
|
|
129
|
+
node.children.forEach((child) => collect(child, node.id));
|
|
130
|
+
})(entity.geometry.root, null);
|
|
131
|
+
const bones = [];
|
|
132
|
+
const boneMap = /* @__PURE__ */ new Map();
|
|
133
|
+
const boneIndexMap = /* @__PURE__ */ new Map();
|
|
134
|
+
buildBoneHierarchy(THREE, entity.geometry.root, null, bones, boneMap, boneIndexMap);
|
|
135
|
+
if (bones.length === 0) {
|
|
136
|
+
throw new Error("entity_skinned_mesh_no_bones");
|
|
137
|
+
}
|
|
138
|
+
bones[0].updateMatrixWorld(true);
|
|
139
|
+
const boneInverses = bones.map((bone) => bone.matrixWorld.clone().invert());
|
|
140
|
+
const positions = [];
|
|
141
|
+
const normals = [];
|
|
142
|
+
const colors = [];
|
|
143
|
+
const uvs = [];
|
|
144
|
+
const skinIndices = [];
|
|
145
|
+
const skinWeights = [];
|
|
146
|
+
const blendStrengths = [];
|
|
147
|
+
const tempColor = new THREE.Color(0.5, 0.5, 0.5);
|
|
148
|
+
const tempWorldPos = new THREE.Vector3();
|
|
149
|
+
const tempLocalPos = new THREE.Vector3();
|
|
150
|
+
const computeSignedDistanceToBounds = (point, halfExtents) => {
|
|
151
|
+
const dx = Math.abs(point.x) - halfExtents.x;
|
|
152
|
+
const dy = Math.abs(point.y) - halfExtents.y;
|
|
153
|
+
const dz = Math.abs(point.z) - halfExtents.z;
|
|
154
|
+
const outsideX = Math.max(dx, 0);
|
|
155
|
+
const outsideY = Math.max(dy, 0);
|
|
156
|
+
const outsideZ = Math.max(dz, 0);
|
|
157
|
+
const outsideDist = Math.sqrt(outsideX * outsideX + outsideY * outsideY + outsideZ * outsideZ);
|
|
158
|
+
if (dx <= 0 && dy <= 0 && dz <= 0) {
|
|
159
|
+
const insideX = halfExtents.x - Math.abs(point.x);
|
|
160
|
+
const insideY = halfExtents.y - Math.abs(point.y);
|
|
161
|
+
const insideZ = halfExtents.z - Math.abs(point.z);
|
|
162
|
+
const minInside = Math.min(insideX, insideY, insideZ);
|
|
163
|
+
return -minInside;
|
|
116
164
|
}
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
const
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
const
|
|
126
|
-
const
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
}
|
|
157
|
-
const sigma = radius * CONNECT_SIGMA_MULTIPLIER;
|
|
158
|
-
const ratio = distance / sigma;
|
|
159
|
-
return { inside: false, falloff: Math.exp(-(ratio * ratio)) };
|
|
165
|
+
return outsideDist;
|
|
166
|
+
};
|
|
167
|
+
const evaluateInfluence = (bounds, worldPosition, scratch) => {
|
|
168
|
+
scratch.copy(worldPosition).applyMatrix4(bounds.inverseMatrix).sub(bounds.center);
|
|
169
|
+
const distance = computeSignedDistanceToBounds(scratch, bounds.halfExtents);
|
|
170
|
+
if (distance <= 0) {
|
|
171
|
+
return { inside: true, falloff: 1 };
|
|
172
|
+
}
|
|
173
|
+
const largestAxis = Math.max(bounds.halfExtents.x, bounds.halfExtents.y, bounds.halfExtents.z);
|
|
174
|
+
const radius = Math.max(CONNECT_MIN_RADIUS, largestAxis * CONNECT_FALLOFF_RATIO);
|
|
175
|
+
if (distance >= radius) {
|
|
176
|
+
return { inside: false, falloff: 0 };
|
|
177
|
+
}
|
|
178
|
+
const sigma = radius * CONNECT_SIGMA_MULTIPLIER;
|
|
179
|
+
const ratio = distance / sigma;
|
|
180
|
+
return { inside: false, falloff: Math.exp(-(ratio * ratio)) };
|
|
181
|
+
};
|
|
182
|
+
const connectConfigs = /* @__PURE__ */ new Map();
|
|
183
|
+
const parentBlendLookup = /* @__PURE__ */ new Map();
|
|
184
|
+
const deriveBoundsFromObject = (object) => {
|
|
185
|
+
const cache = object.userData?.__connectBounds;
|
|
186
|
+
if (cache) {
|
|
187
|
+
return cache;
|
|
188
|
+
}
|
|
189
|
+
const bounds = new THREE.Box3().setFromObject(object);
|
|
190
|
+
if (!bounds || !Number.isFinite(bounds.min.x) || bounds.isEmpty()) {
|
|
191
|
+
return null;
|
|
192
|
+
}
|
|
193
|
+
const inverse = object.matrixWorld.clone().invert();
|
|
194
|
+
bounds.applyMatrix4(inverse);
|
|
195
|
+
const sizeVec = new THREE.Vector3();
|
|
196
|
+
const centerVec = new THREE.Vector3();
|
|
197
|
+
bounds.getSize(sizeVec);
|
|
198
|
+
bounds.getCenter(centerVec);
|
|
199
|
+
const size = [sizeVec.x, sizeVec.y, sizeVec.z];
|
|
200
|
+
const center = [centerVec.x, centerVec.y, centerVec.z];
|
|
201
|
+
object.userData = {
|
|
202
|
+
...object.userData ?? {},
|
|
203
|
+
__connectBounds: { size, center }
|
|
160
204
|
};
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
bounds.getCenter(centerVec);
|
|
178
|
-
const size = [sizeVec.x, sizeVec.y, sizeVec.z];
|
|
179
|
-
const center = [centerVec.x, centerVec.y, centerVec.z];
|
|
180
|
-
object.userData = {
|
|
181
|
-
...(object.userData ?? {}),
|
|
182
|
-
__connectBounds: { size, center },
|
|
183
|
-
};
|
|
184
|
-
return { size, center };
|
|
205
|
+
return { size, center };
|
|
206
|
+
};
|
|
207
|
+
const createBounds = (nodeDef, object) => {
|
|
208
|
+
if (!object) {
|
|
209
|
+
return null;
|
|
210
|
+
}
|
|
211
|
+
const sizeSource = nodeDef?.size ?? object.userData?.size ?? deriveBoundsFromObject(object)?.size;
|
|
212
|
+
if (!sizeSource) {
|
|
213
|
+
return null;
|
|
214
|
+
}
|
|
215
|
+
const derived = !nodeDef?.center ? deriveBoundsFromObject(object) : null;
|
|
216
|
+
const centerSource = nodeDef?.center ?? object.userData?.center ?? derived?.center ?? [0, 0, 0];
|
|
217
|
+
return {
|
|
218
|
+
inverseMatrix: object.matrixWorld.clone().invert(),
|
|
219
|
+
center: new THREE.Vector3(centerSource[0], centerSource[1], centerSource[2]),
|
|
220
|
+
halfExtents: new THREE.Vector3(sizeSource[0] / 2, sizeSource[1] / 2, sizeSource[2] / 2)
|
|
185
221
|
};
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
222
|
+
};
|
|
223
|
+
nodeDefs.forEach((node, nodeId) => {
|
|
224
|
+
const mode = node.connect ?? "none";
|
|
225
|
+
if (mode === "none") {
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
const parentId = parentMap.get(nodeId);
|
|
229
|
+
if (!parentId) {
|
|
230
|
+
warnings.push(`[skinned] node ${nodeId} connect ${mode} ignored (no parent)`);
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
const parentBoneIndex = boneIndexMap.get(parentId);
|
|
234
|
+
if (parentBoneIndex === void 0) {
|
|
235
|
+
warnings.push(`[skinned] node ${nodeId} connect ${mode} ignored (missing parent bone)`);
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
const childBoneIndex = boneIndexMap.get(nodeId);
|
|
239
|
+
if (childBoneIndex === void 0) {
|
|
240
|
+
warnings.push(`[skinned] node ${nodeId} connect ${mode} ignored (missing child bone)`);
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
const parentObject = nodeObjects.get(parentId);
|
|
244
|
+
if (!parentObject) {
|
|
245
|
+
warnings.push(`[skinned] node ${nodeId} connect ${mode} ignored (missing parent object)`);
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
const childObject = nodeObjects.get(nodeId);
|
|
249
|
+
if (!childObject) {
|
|
250
|
+
warnings.push(`[skinned] node ${nodeId} connect ${mode} ignored (missing child object)`);
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
const parentDef = nodeDefs.get(parentId);
|
|
254
|
+
let childBlend;
|
|
255
|
+
if (mode === "child" || mode === "both") {
|
|
256
|
+
childBlend = createBounds(parentDef, parentObject) ?? void 0;
|
|
257
|
+
if (!childBlend) {
|
|
258
|
+
warnings.push(`[skinned] node ${nodeId} connect ${mode} ignored (parent ${parentId} missing size)`);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
let parentBlend;
|
|
262
|
+
if (mode === "parent" || mode === "both") {
|
|
263
|
+
parentBlend = createBounds(node, childObject) ?? void 0;
|
|
264
|
+
if (!parentBlend) {
|
|
265
|
+
warnings.push(`[skinned] node ${nodeId} connect ${mode} ignored (child ${nodeId} missing size)`);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
if (!childBlend && !parentBlend) {
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
const config = {
|
|
272
|
+
mode,
|
|
273
|
+
childId: nodeId,
|
|
274
|
+
parentId,
|
|
275
|
+
parentBoneIndex,
|
|
276
|
+
childBoneIndex,
|
|
277
|
+
childBlend,
|
|
278
|
+
parentBlend,
|
|
279
|
+
stats: { childVertices: 0, parentVertices: 0 }
|
|
206
280
|
};
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
281
|
+
connectConfigs.set(nodeId, config);
|
|
282
|
+
if (config.parentBlend) {
|
|
283
|
+
const existing = parentBlendLookup.get(parentId);
|
|
284
|
+
if (existing) {
|
|
285
|
+
existing.push(config);
|
|
286
|
+
} else {
|
|
287
|
+
parentBlendLookup.set(parentId, [config]);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
});
|
|
291
|
+
const colorTextures = [];
|
|
292
|
+
const textureSet = /* @__PURE__ */ new Set();
|
|
293
|
+
const emissivePairs = /* @__PURE__ */ new Map();
|
|
294
|
+
let needsAlphaTest = false;
|
|
295
|
+
let needsDoubleSide = false;
|
|
296
|
+
nodeObjects.forEach((nodeObject) => {
|
|
297
|
+
nodeObject.traverse((child) => {
|
|
298
|
+
if (!child.isMesh) {
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
const mesh2 = child;
|
|
302
|
+
if (!mesh2.geometry) {
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
const material2 = mesh2.material;
|
|
306
|
+
const materials = Array.isArray(material2) ? material2 : [material2];
|
|
307
|
+
materials.forEach((entry) => {
|
|
308
|
+
const std = entry;
|
|
309
|
+
if (std.map instanceof THREE.DataTexture && std.map.image?.width && std.map.image?.height) {
|
|
310
|
+
if (!textureSet.has(std.map.uuid)) {
|
|
311
|
+
textureSet.add(std.map.uuid);
|
|
312
|
+
colorTextures.push(std.map);
|
|
313
|
+
}
|
|
226
314
|
}
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
warnings.push(`[skinned] node ${nodeId} connect ${mode} ignored (missing parent object)`);
|
|
230
|
-
return;
|
|
315
|
+
if (std.map instanceof THREE.DataTexture && std.emissiveMap instanceof THREE.DataTexture) {
|
|
316
|
+
emissivePairs.set(std.map, std.emissiveMap);
|
|
231
317
|
}
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
warnings.push(`[skinned] node ${nodeId} connect ${mode} ignored (missing child object)`);
|
|
235
|
-
return;
|
|
318
|
+
if (std.alphaTest > 0) {
|
|
319
|
+
needsAlphaTest = true;
|
|
236
320
|
}
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
if (mode === 'child' || mode === 'both') {
|
|
240
|
-
childBlend = createBounds(parentDef, parentObject) ?? undefined;
|
|
241
|
-
if (!childBlend) {
|
|
242
|
-
warnings.push(`[skinned] node ${nodeId} connect ${mode} ignored (parent ${parentId} missing size)`);
|
|
243
|
-
}
|
|
321
|
+
if (std.side === THREE.DoubleSide) {
|
|
322
|
+
needsDoubleSide = true;
|
|
244
323
|
}
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
324
|
+
});
|
|
325
|
+
});
|
|
326
|
+
});
|
|
327
|
+
const colorAtlas = (0, import_texture_atlas.buildTextureAtlas)(THREE, colorTextures);
|
|
328
|
+
const transformLookup = colorAtlas?.transforms ?? null;
|
|
329
|
+
const emissiveAtlas = colorAtlas ? (0, import_texture_atlas.buildEmissiveAtlas)(THREE, colorAtlas.width, colorAtlas.height, colorAtlas.transforms, emissivePairs) : null;
|
|
330
|
+
const processPrimitiveObject = (object, ownerId) => {
|
|
331
|
+
object.traverse((child) => {
|
|
332
|
+
if (!child.isMesh) {
|
|
333
|
+
return;
|
|
334
|
+
}
|
|
335
|
+
const mesh2 = child;
|
|
336
|
+
if (!mesh2.geometry) {
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
const material2 = mesh2.material;
|
|
340
|
+
const boneIndex = boneIndexMap.get(ownerId);
|
|
341
|
+
if (boneIndex === void 0) {
|
|
342
|
+
warnings.push(`No bone mapping found for node "${ownerId}".`);
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
345
|
+
const geometry2 = mesh2.geometry.clone();
|
|
346
|
+
geometry2.applyMatrix4(mesh2.matrixWorld);
|
|
347
|
+
const nonIndexed = geometry2.toNonIndexed();
|
|
348
|
+
if (!nonIndexed.getAttribute("normal")) {
|
|
349
|
+
nonIndexed.computeVertexNormals();
|
|
350
|
+
}
|
|
351
|
+
const positionAttr = nonIndexed.getAttribute("position");
|
|
352
|
+
const normalAttr = nonIndexed.getAttribute("normal");
|
|
353
|
+
const uvAttr = nonIndexed.getAttribute("uv");
|
|
354
|
+
if (!positionAttr) {
|
|
355
|
+
warnings.push(`Primitive "${ownerId}" produced geometry without positions.`);
|
|
356
|
+
geometry2.dispose();
|
|
357
|
+
nonIndexed.dispose();
|
|
358
|
+
return;
|
|
359
|
+
}
|
|
360
|
+
const vertexCount = positionAttr.count;
|
|
361
|
+
const materialArray = Array.isArray(material2) ? material2 : [material2];
|
|
362
|
+
const materialIndexPerVertex = new Array(vertexCount).fill(0);
|
|
363
|
+
const groups = nonIndexed.groups && nonIndexed.groups.length > 0 ? nonIndexed.groups : [{ start: 0, count: vertexCount, materialIndex: 0 }];
|
|
364
|
+
groups.forEach((group) => {
|
|
365
|
+
const start = Math.max(0, group.start);
|
|
366
|
+
const end = Math.min(vertexCount, start + group.count);
|
|
367
|
+
const materialIndex = group.materialIndex ?? 0;
|
|
368
|
+
for (let i = start; i < end; i += 1) {
|
|
369
|
+
materialIndexPerVertex[i] = Math.min(materialArray.length - 1, Math.max(0, materialIndex));
|
|
251
370
|
}
|
|
252
|
-
|
|
253
|
-
|
|
371
|
+
});
|
|
372
|
+
const worldPos = tempWorldPos;
|
|
373
|
+
const localPos = tempLocalPos;
|
|
374
|
+
const hasUv = Boolean(uvAttr);
|
|
375
|
+
const ownerConnect = connectConfigs.get(ownerId);
|
|
376
|
+
const parentBlendEntries = parentBlendLookup.get(ownerId)?.filter((entry) => entry.parentBlend) ?? [];
|
|
377
|
+
for (let i = 0; i < vertexCount; i += 1) {
|
|
378
|
+
const px = positionAttr.getX(i);
|
|
379
|
+
const py = positionAttr.getY(i);
|
|
380
|
+
const pz = positionAttr.getZ(i);
|
|
381
|
+
positions.push(px, py, pz);
|
|
382
|
+
if (normalAttr) {
|
|
383
|
+
normals.push(normalAttr.getX(i), normalAttr.getY(i), normalAttr.getZ(i));
|
|
384
|
+
} else {
|
|
385
|
+
normals.push(0, 1, 0);
|
|
254
386
|
}
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
stats: { childVertices: 0, parentVertices: 0 },
|
|
387
|
+
worldPos.set(px, py, pz);
|
|
388
|
+
const weights = /* @__PURE__ */ new Map();
|
|
389
|
+
const setWeight = (index, weight) => {
|
|
390
|
+
if (weight <= CONNECT_WEIGHT_EPSILON) {
|
|
391
|
+
weights.delete(index);
|
|
392
|
+
} else {
|
|
393
|
+
weights.set(index, weight);
|
|
394
|
+
}
|
|
264
395
|
};
|
|
265
|
-
|
|
266
|
-
if (
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
396
|
+
setWeight(boneIndex, 1);
|
|
397
|
+
if (ownerConnect?.childBlend) {
|
|
398
|
+
const evaluation = evaluateInfluence(ownerConnect.childBlend, worldPos, localPos);
|
|
399
|
+
if (ownerConnect.mode === "child") {
|
|
400
|
+
if (evaluation.inside) {
|
|
401
|
+
setWeight(boneIndex, 0);
|
|
402
|
+
setWeight(ownerConnect.parentBoneIndex, 1);
|
|
403
|
+
ownerConnect.stats.childVertices += 1;
|
|
404
|
+
} else if (evaluation.falloff > CONNECT_WEIGHT_EPSILON) {
|
|
405
|
+
const parentWeight = evaluation.falloff;
|
|
406
|
+
setWeight(ownerConnect.parentBoneIndex, parentWeight);
|
|
407
|
+
setWeight(boneIndex, Math.max(0, 1 - parentWeight));
|
|
408
|
+
ownerConnect.stats.childVertices += 1;
|
|
270
409
|
}
|
|
271
|
-
|
|
272
|
-
|
|
410
|
+
} else if (ownerConnect.mode === "both") {
|
|
411
|
+
if (evaluation.inside) {
|
|
412
|
+
setWeight(boneIndex, 0.5);
|
|
413
|
+
setWeight(ownerConnect.parentBoneIndex, 0.5);
|
|
414
|
+
ownerConnect.stats.childVertices += 1;
|
|
415
|
+
} else if (evaluation.falloff > CONNECT_WEIGHT_EPSILON) {
|
|
416
|
+
const parentWeight = Math.min(0.5, evaluation.falloff * 0.5);
|
|
417
|
+
if (parentWeight > CONNECT_WEIGHT_EPSILON) {
|
|
418
|
+
setWeight(ownerConnect.parentBoneIndex, parentWeight);
|
|
419
|
+
setWeight(boneIndex, Math.max(0, 1 - parentWeight));
|
|
420
|
+
ownerConnect.stats.childVertices += 1;
|
|
421
|
+
}
|
|
273
422
|
}
|
|
423
|
+
}
|
|
274
424
|
}
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
if (std.alphaTest > 0) {
|
|
304
|
-
needsAlphaTest = true;
|
|
425
|
+
if (parentBlendEntries.length > 0) {
|
|
426
|
+
parentBlendEntries.forEach((config) => {
|
|
427
|
+
const bounds = config.parentBlend;
|
|
428
|
+
if (!bounds)
|
|
429
|
+
return;
|
|
430
|
+
const evaluation = evaluateInfluence(bounds, worldPos, localPos);
|
|
431
|
+
if (config.mode === "parent") {
|
|
432
|
+
if (evaluation.inside) {
|
|
433
|
+
setWeight(boneIndex, 0);
|
|
434
|
+
setWeight(config.childBoneIndex, 1);
|
|
435
|
+
config.stats.parentVertices += 1;
|
|
436
|
+
} else if (evaluation.falloff > CONNECT_WEIGHT_EPSILON) {
|
|
437
|
+
const childWeight = evaluation.falloff;
|
|
438
|
+
setWeight(config.childBoneIndex, childWeight);
|
|
439
|
+
setWeight(boneIndex, Math.max(0, 1 - childWeight));
|
|
440
|
+
config.stats.parentVertices += 1;
|
|
441
|
+
}
|
|
442
|
+
} else if (config.mode === "both") {
|
|
443
|
+
if (evaluation.inside) {
|
|
444
|
+
setWeight(boneIndex, 0.5);
|
|
445
|
+
setWeight(config.childBoneIndex, 0.5);
|
|
446
|
+
config.stats.parentVertices += 1;
|
|
447
|
+
} else if (evaluation.falloff > CONNECT_WEIGHT_EPSILON) {
|
|
448
|
+
const childWeight = Math.min(0.5, evaluation.falloff * 0.5);
|
|
449
|
+
if (childWeight > CONNECT_WEIGHT_EPSILON) {
|
|
450
|
+
setWeight(config.childBoneIndex, childWeight);
|
|
451
|
+
setWeight(boneIndex, Math.max(0, 1 - childWeight));
|
|
452
|
+
config.stats.parentVertices += 1;
|
|
305
453
|
}
|
|
306
|
-
|
|
307
|
-
needsDoubleSide = true;
|
|
308
|
-
}
|
|
309
|
-
});
|
|
310
|
-
});
|
|
311
|
-
});
|
|
312
|
-
const colorAtlas = buildTextureAtlas(THREE, colorTextures);
|
|
313
|
-
const transformLookup = colorAtlas?.transforms ?? null;
|
|
314
|
-
const emissiveAtlas = colorAtlas
|
|
315
|
-
? buildEmissiveAtlas(THREE, colorAtlas.width, colorAtlas.height, colorAtlas.transforms, emissivePairs)
|
|
316
|
-
: null;
|
|
317
|
-
const processPrimitiveObject = (object, ownerId) => {
|
|
318
|
-
object.traverse(child => {
|
|
319
|
-
if (!child.isMesh) {
|
|
320
|
-
return;
|
|
321
|
-
}
|
|
322
|
-
const mesh = child;
|
|
323
|
-
if (!mesh.geometry) {
|
|
324
|
-
return;
|
|
325
|
-
}
|
|
326
|
-
const material = mesh.material;
|
|
327
|
-
const boneIndex = boneIndexMap.get(ownerId);
|
|
328
|
-
if (boneIndex === undefined) {
|
|
329
|
-
warnings.push(`No bone mapping found for node "${ownerId}".`);
|
|
330
|
-
return;
|
|
331
|
-
}
|
|
332
|
-
const geometry = mesh.geometry.clone();
|
|
333
|
-
geometry.applyMatrix4(mesh.matrixWorld);
|
|
334
|
-
const nonIndexed = geometry.toNonIndexed();
|
|
335
|
-
if (!nonIndexed.getAttribute('normal')) {
|
|
336
|
-
nonIndexed.computeVertexNormals();
|
|
337
|
-
}
|
|
338
|
-
const positionAttr = nonIndexed.getAttribute('position');
|
|
339
|
-
const normalAttr = nonIndexed.getAttribute('normal');
|
|
340
|
-
const uvAttr = nonIndexed.getAttribute('uv');
|
|
341
|
-
if (!positionAttr) {
|
|
342
|
-
warnings.push(`Primitive "${ownerId}" produced geometry without positions.`);
|
|
343
|
-
geometry.dispose();
|
|
344
|
-
nonIndexed.dispose();
|
|
345
|
-
return;
|
|
454
|
+
}
|
|
346
455
|
}
|
|
347
|
-
|
|
348
|
-
const materialArray = Array.isArray(material) ? material : [material];
|
|
349
|
-
const materialIndexPerVertex = new Array(vertexCount).fill(0);
|
|
350
|
-
const groups = nonIndexed.groups && nonIndexed.groups.length > 0 ? nonIndexed.groups : [{ start: 0, count: vertexCount, materialIndex: 0 }];
|
|
351
|
-
groups.forEach(group => {
|
|
352
|
-
const start = Math.max(0, group.start);
|
|
353
|
-
const end = Math.min(vertexCount, start + group.count);
|
|
354
|
-
const materialIndex = group.materialIndex ?? 0;
|
|
355
|
-
for (let i = start; i < end; i += 1) {
|
|
356
|
-
materialIndexPerVertex[i] = Math.min(materialArray.length - 1, Math.max(0, materialIndex));
|
|
357
|
-
}
|
|
358
|
-
});
|
|
359
|
-
const worldPos = tempWorldPos;
|
|
360
|
-
const localPos = tempLocalPos;
|
|
361
|
-
const hasUv = Boolean(uvAttr);
|
|
362
|
-
const ownerConnect = connectConfigs.get(ownerId);
|
|
363
|
-
const parentBlendEntries = parentBlendLookup.get(ownerId)?.filter(entry => entry.parentBlend) ?? [];
|
|
364
|
-
for (let i = 0; i < vertexCount; i += 1) {
|
|
365
|
-
const px = positionAttr.getX(i);
|
|
366
|
-
const py = positionAttr.getY(i);
|
|
367
|
-
const pz = positionAttr.getZ(i);
|
|
368
|
-
positions.push(px, py, pz);
|
|
369
|
-
if (normalAttr) {
|
|
370
|
-
normals.push(normalAttr.getX(i), normalAttr.getY(i), normalAttr.getZ(i));
|
|
371
|
-
}
|
|
372
|
-
else {
|
|
373
|
-
normals.push(0, 1, 0);
|
|
374
|
-
}
|
|
375
|
-
worldPos.set(px, py, pz);
|
|
376
|
-
const weights = new Map();
|
|
377
|
-
const setWeight = (index, weight) => {
|
|
378
|
-
if (weight <= CONNECT_WEIGHT_EPSILON) {
|
|
379
|
-
weights.delete(index);
|
|
380
|
-
}
|
|
381
|
-
else {
|
|
382
|
-
weights.set(index, weight);
|
|
383
|
-
}
|
|
384
|
-
};
|
|
385
|
-
setWeight(boneIndex, 1);
|
|
386
|
-
if (ownerConnect?.childBlend) {
|
|
387
|
-
const evaluation = evaluateInfluence(ownerConnect.childBlend, worldPos, localPos);
|
|
388
|
-
if (ownerConnect.mode === 'child') {
|
|
389
|
-
if (evaluation.inside) {
|
|
390
|
-
setWeight(boneIndex, 0);
|
|
391
|
-
setWeight(ownerConnect.parentBoneIndex, 1);
|
|
392
|
-
ownerConnect.stats.childVertices += 1;
|
|
393
|
-
}
|
|
394
|
-
else if (evaluation.falloff > CONNECT_WEIGHT_EPSILON) {
|
|
395
|
-
const parentWeight = evaluation.falloff;
|
|
396
|
-
setWeight(ownerConnect.parentBoneIndex, parentWeight);
|
|
397
|
-
setWeight(boneIndex, Math.max(0, 1 - parentWeight));
|
|
398
|
-
ownerConnect.stats.childVertices += 1;
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
else if (ownerConnect.mode === 'both') {
|
|
402
|
-
if (evaluation.inside) {
|
|
403
|
-
setWeight(boneIndex, 0.5);
|
|
404
|
-
setWeight(ownerConnect.parentBoneIndex, 0.5);
|
|
405
|
-
ownerConnect.stats.childVertices += 1;
|
|
406
|
-
}
|
|
407
|
-
else if (evaluation.falloff > CONNECT_WEIGHT_EPSILON) {
|
|
408
|
-
const parentWeight = Math.min(0.5, evaluation.falloff * 0.5);
|
|
409
|
-
if (parentWeight > CONNECT_WEIGHT_EPSILON) {
|
|
410
|
-
setWeight(ownerConnect.parentBoneIndex, parentWeight);
|
|
411
|
-
setWeight(boneIndex, Math.max(0, 1 - parentWeight));
|
|
412
|
-
ownerConnect.stats.childVertices += 1;
|
|
413
|
-
}
|
|
414
|
-
}
|
|
415
|
-
}
|
|
416
|
-
}
|
|
417
|
-
if (parentBlendEntries.length > 0) {
|
|
418
|
-
parentBlendEntries.forEach(config => {
|
|
419
|
-
const bounds = config.parentBlend;
|
|
420
|
-
if (!bounds)
|
|
421
|
-
return;
|
|
422
|
-
const evaluation = evaluateInfluence(bounds, worldPos, localPos);
|
|
423
|
-
if (config.mode === 'parent') {
|
|
424
|
-
if (evaluation.inside) {
|
|
425
|
-
setWeight(boneIndex, 0);
|
|
426
|
-
setWeight(config.childBoneIndex, 1);
|
|
427
|
-
config.stats.parentVertices += 1;
|
|
428
|
-
}
|
|
429
|
-
else if (evaluation.falloff > CONNECT_WEIGHT_EPSILON) {
|
|
430
|
-
const childWeight = evaluation.falloff;
|
|
431
|
-
setWeight(config.childBoneIndex, childWeight);
|
|
432
|
-
setWeight(boneIndex, Math.max(0, 1 - childWeight));
|
|
433
|
-
config.stats.parentVertices += 1;
|
|
434
|
-
}
|
|
435
|
-
}
|
|
436
|
-
else if (config.mode === 'both') {
|
|
437
|
-
if (evaluation.inside) {
|
|
438
|
-
setWeight(boneIndex, 0.5);
|
|
439
|
-
setWeight(config.childBoneIndex, 0.5);
|
|
440
|
-
config.stats.parentVertices += 1;
|
|
441
|
-
}
|
|
442
|
-
else if (evaluation.falloff > CONNECT_WEIGHT_EPSILON) {
|
|
443
|
-
const childWeight = Math.min(0.5, evaluation.falloff * 0.5);
|
|
444
|
-
if (childWeight > CONNECT_WEIGHT_EPSILON) {
|
|
445
|
-
setWeight(config.childBoneIndex, childWeight);
|
|
446
|
-
setWeight(boneIndex, Math.max(0, 1 - childWeight));
|
|
447
|
-
config.stats.parentVertices += 1;
|
|
448
|
-
}
|
|
449
|
-
}
|
|
450
|
-
}
|
|
451
|
-
});
|
|
452
|
-
}
|
|
453
|
-
let filtered = Array.from(weights.entries()).filter(([, value]) => value > CONNECT_WEIGHT_EPSILON);
|
|
454
|
-
if (filtered.length === 0) {
|
|
455
|
-
filtered = [[boneIndex, 1]];
|
|
456
|
-
}
|
|
457
|
-
const totalWeight = filtered.reduce((sum, [, value]) => sum + value, 0);
|
|
458
|
-
if (totalWeight > 0) {
|
|
459
|
-
const invTotal = 1 / totalWeight;
|
|
460
|
-
filtered = filtered.map(([index, value]) => [index, value * invTotal]);
|
|
461
|
-
}
|
|
462
|
-
else {
|
|
463
|
-
filtered = [[boneIndex, 1]];
|
|
464
|
-
}
|
|
465
|
-
filtered.sort((a, b) => b[1] - a[1]);
|
|
466
|
-
const maxBlend = filtered.reduce((acc, [index, value]) => {
|
|
467
|
-
if (index === boneIndex) {
|
|
468
|
-
return acc;
|
|
469
|
-
}
|
|
470
|
-
return Math.max(acc, value);
|
|
471
|
-
}, 0);
|
|
472
|
-
blendStrengths.push(maxBlend);
|
|
473
|
-
for (let slot = 0; slot < 4; slot += 1) {
|
|
474
|
-
const influence = filtered[slot];
|
|
475
|
-
if (influence) {
|
|
476
|
-
skinIndices.push(influence[0]);
|
|
477
|
-
skinWeights.push(influence[1]);
|
|
478
|
-
}
|
|
479
|
-
else {
|
|
480
|
-
skinIndices.push(0);
|
|
481
|
-
skinWeights.push(0);
|
|
482
|
-
}
|
|
483
|
-
}
|
|
484
|
-
const materialIndex = materialIndexPerVertex[i];
|
|
485
|
-
const mat = materialArray[materialIndex];
|
|
486
|
-
const mapTexture = mat?.map;
|
|
487
|
-
const transform = mapTexture && transformLookup ? transformLookup.get(mapTexture) : undefined;
|
|
488
|
-
if (hasUv) {
|
|
489
|
-
if (transform) {
|
|
490
|
-
const u = uvAttr.getX(i) * transform.scale[0] + transform.offset[0];
|
|
491
|
-
const v = uvAttr.getY(i) * transform.scale[1] + transform.offset[1];
|
|
492
|
-
uvs.push(u, v);
|
|
493
|
-
}
|
|
494
|
-
else {
|
|
495
|
-
uvs.push(uvAttr.getX(i), uvAttr.getY(i));
|
|
496
|
-
}
|
|
497
|
-
}
|
|
498
|
-
if (transform) {
|
|
499
|
-
colors.push(1, 1, 1);
|
|
500
|
-
}
|
|
501
|
-
else if (mat) {
|
|
502
|
-
tempColor.copy(materialToColor(THREE, mat));
|
|
503
|
-
colors.push(tempColor.r, tempColor.g, tempColor.b);
|
|
504
|
-
}
|
|
505
|
-
else {
|
|
506
|
-
colors.push(0.5, 0.5, 0.5);
|
|
507
|
-
}
|
|
508
|
-
}
|
|
509
|
-
geometry.dispose();
|
|
510
|
-
nonIndexed.dispose();
|
|
511
|
-
});
|
|
512
|
-
};
|
|
513
|
-
const traverseNode = (node) => {
|
|
514
|
-
const object = nodeObjects.get(node.id);
|
|
515
|
-
if (object) {
|
|
516
|
-
object.children.forEach(child => {
|
|
517
|
-
if (child.userData?.primitiveOwnerId === node.id) {
|
|
518
|
-
processPrimitiveObject(child, node.id);
|
|
519
|
-
}
|
|
520
|
-
});
|
|
456
|
+
});
|
|
521
457
|
}
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
458
|
+
let filtered = Array.from(weights.entries()).filter(([, value]) => value > CONNECT_WEIGHT_EPSILON);
|
|
459
|
+
if (filtered.length === 0) {
|
|
460
|
+
filtered = [[boneIndex, 1]];
|
|
461
|
+
}
|
|
462
|
+
const totalWeight = filtered.reduce((sum, [, value]) => sum + value, 0);
|
|
463
|
+
if (totalWeight > 0) {
|
|
464
|
+
const invTotal = 1 / totalWeight;
|
|
465
|
+
filtered = filtered.map(([index, value]) => [index, value * invTotal]);
|
|
466
|
+
} else {
|
|
467
|
+
filtered = [[boneIndex, 1]];
|
|
468
|
+
}
|
|
469
|
+
filtered.sort((a, b) => b[1] - a[1]);
|
|
470
|
+
const maxBlend = filtered.reduce((acc, [index, value]) => {
|
|
471
|
+
if (index === boneIndex) {
|
|
472
|
+
return acc;
|
|
473
|
+
}
|
|
474
|
+
return Math.max(acc, value);
|
|
475
|
+
}, 0);
|
|
476
|
+
blendStrengths.push(maxBlend);
|
|
477
|
+
for (let slot = 0; slot < 4; slot += 1) {
|
|
478
|
+
const influence = filtered[slot];
|
|
479
|
+
if (influence) {
|
|
480
|
+
skinIndices.push(influence[0]);
|
|
481
|
+
skinWeights.push(influence[1]);
|
|
482
|
+
} else {
|
|
483
|
+
skinIndices.push(0);
|
|
484
|
+
skinWeights.push(0);
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
const materialIndex = materialIndexPerVertex[i];
|
|
488
|
+
const mat = materialArray[materialIndex];
|
|
489
|
+
const mapTexture = mat?.map;
|
|
490
|
+
const transform = mapTexture && transformLookup ? transformLookup.get(mapTexture) : void 0;
|
|
491
|
+
if (hasUv) {
|
|
492
|
+
if (transform) {
|
|
493
|
+
const u = uvAttr.getX(i) * transform.scale[0] + transform.offset[0];
|
|
494
|
+
const v = uvAttr.getY(i) * transform.scale[1] + transform.offset[1];
|
|
495
|
+
uvs.push(u, v);
|
|
496
|
+
} else {
|
|
497
|
+
uvs.push(uvAttr.getX(i), uvAttr.getY(i));
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
if (transform) {
|
|
501
|
+
colors.push(1, 1, 1);
|
|
502
|
+
} else if (mat) {
|
|
503
|
+
tempColor.copy(materialToColor(THREE, mat));
|
|
504
|
+
colors.push(tempColor.r, tempColor.g, tempColor.b);
|
|
505
|
+
} else {
|
|
506
|
+
colors.push(0.5, 0.5, 0.5);
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
geometry2.dispose();
|
|
510
|
+
nonIndexed.dispose();
|
|
544
511
|
});
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
if (needsAlphaTest) {
|
|
555
|
-
material.alphaTest = 0.5;
|
|
556
|
-
material.transparent = false;
|
|
557
|
-
material.side = THREE.DoubleSide;
|
|
558
|
-
}
|
|
559
|
-
else if (needsDoubleSide) {
|
|
560
|
-
material.side = THREE.DoubleSide;
|
|
512
|
+
};
|
|
513
|
+
const traverseNode = (node) => {
|
|
514
|
+
const object = nodeObjects.get(node.id);
|
|
515
|
+
if (object) {
|
|
516
|
+
object.children.forEach((child) => {
|
|
517
|
+
if (child.userData?.primitiveOwnerId === node.id) {
|
|
518
|
+
processPrimitiveObject(child, node.id);
|
|
519
|
+
}
|
|
520
|
+
});
|
|
561
521
|
}
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
522
|
+
node.children.forEach(traverseNode);
|
|
523
|
+
};
|
|
524
|
+
traverseNode(entity.geometry.root);
|
|
525
|
+
if (positions.length === 0) {
|
|
526
|
+
throw new Error("entity_skinned_mesh_empty_geometry");
|
|
527
|
+
}
|
|
528
|
+
const geometry = new THREE.BufferGeometry();
|
|
529
|
+
geometry.setAttribute("position", new THREE.Float32BufferAttribute(positions, 3));
|
|
530
|
+
geometry.setAttribute("normal", new THREE.Float32BufferAttribute(normals, 3));
|
|
531
|
+
geometry.setAttribute("color", new THREE.Float32BufferAttribute(colors, 3));
|
|
532
|
+
if (uvs.length > 0) {
|
|
533
|
+
geometry.setAttribute("uv", new THREE.Float32BufferAttribute(uvs, 2));
|
|
534
|
+
}
|
|
535
|
+
geometry.setAttribute("skinIndex", new THREE.Uint16BufferAttribute(skinIndices, 4));
|
|
536
|
+
geometry.setAttribute("skinWeight", new THREE.Float32BufferAttribute(skinWeights, 4));
|
|
537
|
+
geometry.setAttribute("connectStrength", new THREE.Float32BufferAttribute(blendStrengths, 1));
|
|
538
|
+
geometry.computeBoundingBox();
|
|
539
|
+
geometry.computeBoundingSphere();
|
|
540
|
+
const material = new THREE.MeshStandardMaterial({
|
|
541
|
+
vertexColors: !colorAtlas,
|
|
542
|
+
metalness: 0,
|
|
543
|
+
roughness: 1
|
|
544
|
+
});
|
|
545
|
+
material.skinning = true;
|
|
546
|
+
if (colorAtlas) {
|
|
547
|
+
material.map = colorAtlas.texture;
|
|
548
|
+
}
|
|
549
|
+
if (emissiveAtlas) {
|
|
550
|
+
material.emissiveMap = emissiveAtlas;
|
|
551
|
+
material.emissive.setRGB(1, 1, 1);
|
|
552
|
+
material.emissiveIntensity = 1;
|
|
553
|
+
}
|
|
554
|
+
if (needsAlphaTest) {
|
|
555
|
+
material.alphaTest = 0.5;
|
|
556
|
+
material.transparent = false;
|
|
557
|
+
material.side = THREE.DoubleSide;
|
|
558
|
+
} else if (needsDoubleSide) {
|
|
559
|
+
material.side = THREE.DoubleSide;
|
|
560
|
+
}
|
|
561
|
+
const connectOverlayUniform = { value: 0 };
|
|
562
|
+
material.onBeforeCompile = (shader) => {
|
|
563
|
+
shader.uniforms.uConnectOverlay = connectOverlayUniform;
|
|
564
|
+
shader.vertexShader = shader.vertexShader.replace("#include <common>", "#include <common>\nattribute float connectStrength;\nvarying float vConnectStrength;").replace("#include <begin_vertex>", "#include <begin_vertex>\n vConnectStrength = connectStrength;");
|
|
565
|
+
shader.fragmentShader = shader.fragmentShader.replace("#include <common>", "#include <common>\nuniform float uConnectOverlay;\nvarying float vConnectStrength;").replace("#include <output_fragment>", `#include <output_fragment>
|
|
571
566
|
if (uConnectOverlay > 0.0) {
|
|
572
567
|
float overlayStrength = clamp(vConnectStrength, 0.0, 1.0);
|
|
573
568
|
vec3 overlayColor = mix(vec3(0.0, 0.2, 1.0), vec3(1.0, 0.0, 0.0), overlayStrength);
|
|
574
569
|
gl_FragColor.rgb = overlayColor;
|
|
575
570
|
}`);
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
};
|
|
571
|
+
};
|
|
572
|
+
material.userData.connectOverlayUniform = connectOverlayUniform;
|
|
573
|
+
material.needsUpdate = true;
|
|
574
|
+
const mesh = new THREE.SkinnedMesh(geometry, material);
|
|
575
|
+
mesh.name = `${entity.id ?? "entity"}__skinned`;
|
|
576
|
+
const skeleton = new THREE.Skeleton(bones, boneInverses);
|
|
577
|
+
mesh.add(bones[0]);
|
|
578
|
+
mesh.bind(skeleton);
|
|
579
|
+
mesh.normalizeSkinWeights();
|
|
580
|
+
let weldedVertices = 0;
|
|
581
|
+
const weldedByNode = {};
|
|
582
|
+
const connectBreakdown = {};
|
|
583
|
+
connectConfigs.forEach((config, nodeId) => {
|
|
584
|
+
const total = config.stats.childVertices + config.stats.parentVertices;
|
|
585
|
+
if (total > 0) {
|
|
586
|
+
weldedVertices += total;
|
|
587
|
+
weldedByNode[nodeId] = total;
|
|
588
|
+
connectBreakdown[nodeId] = {
|
|
589
|
+
childVertices: config.stats.childVertices,
|
|
590
|
+
parentVertices: config.stats.parentVertices
|
|
591
|
+
};
|
|
592
|
+
}
|
|
593
|
+
});
|
|
594
|
+
const dispose = () => {
|
|
595
|
+
mesh.geometry.dispose();
|
|
596
|
+
const mat = mesh.material;
|
|
597
|
+
if (Array.isArray(mat)) {
|
|
598
|
+
mat.forEach((entry) => entry.dispose?.());
|
|
599
|
+
} else {
|
|
600
|
+
mat.dispose?.();
|
|
601
|
+
}
|
|
602
|
+
};
|
|
603
|
+
return {
|
|
604
|
+
mesh,
|
|
605
|
+
bones,
|
|
606
|
+
skeleton,
|
|
607
|
+
boneMap,
|
|
608
|
+
stats: {
|
|
609
|
+
vertices: positions.length / 3,
|
|
610
|
+
triangles: positions.length / 9,
|
|
611
|
+
weldedVertices,
|
|
612
|
+
weldedByNode,
|
|
613
|
+
connectBreakdown,
|
|
614
|
+
atlas: colorAtlas ? { width: colorAtlas.width, height: colorAtlas.height } : null
|
|
615
|
+
},
|
|
616
|
+
warnings,
|
|
617
|
+
dispose
|
|
618
|
+
};
|
|
625
619
|
}
|
|
620
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
621
|
+
0 && (module.exports = {
|
|
622
|
+
buildEntitySkinnedMesh
|
|
623
|
+
});
|