@playdrop/playdrop-cli 0.7.15 → 0.7.17

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.
Files changed (70) hide show
  1. package/config/client-meta.json +1 -1
  2. package/node_modules/@playdrop/ai-client/dist/index.js +461 -444
  3. package/node_modules/@playdrop/ai-client/package.json +1 -1
  4. package/node_modules/@playdrop/api-client/dist/client.js +903 -882
  5. package/node_modules/@playdrop/api-client/dist/core/errors.js +69 -45
  6. package/node_modules/@playdrop/api-client/dist/core/request.js +188 -159
  7. package/node_modules/@playdrop/api-client/dist/domains/admin.js +516 -491
  8. package/node_modules/@playdrop/api-client/dist/domains/ai.js +40 -14
  9. package/node_modules/@playdrop/api-client/dist/domains/apps.js +480 -462
  10. package/node_modules/@playdrop/api-client/dist/domains/asset-packs.js +439 -419
  11. package/node_modules/@playdrop/api-client/dist/domains/assets.js +696 -676
  12. package/node_modules/@playdrop/api-client/dist/domains/auth.js +346 -320
  13. package/node_modules/@playdrop/api-client/dist/domains/comments.js +124 -98
  14. package/node_modules/@playdrop/api-client/dist/domains/free-credits.js +91 -65
  15. package/node_modules/@playdrop/api-client/dist/domains/me.js +71 -45
  16. package/node_modules/@playdrop/api-client/dist/domains/payments.js +407 -386
  17. package/node_modules/@playdrop/api-client/dist/domains/player-meta.js +144 -118
  18. package/node_modules/@playdrop/api-client/dist/domains/search.js +117 -104
  19. package/node_modules/@playdrop/api-client/dist/domains/tags.js +188 -162
  20. package/node_modules/@playdrop/api-client/dist/index.js +993 -552
  21. package/node_modules/@playdrop/api-client/package.json +1 -1
  22. package/node_modules/@playdrop/boxel-three/dist/src/animations.js +84 -62
  23. package/node_modules/@playdrop/boxel-three/dist/src/builders.js +341 -308
  24. package/node_modules/@playdrop/boxel-three/dist/src/context.js +55 -29
  25. package/node_modules/@playdrop/boxel-three/dist/src/exporters/glb.js +856 -858
  26. package/node_modules/@playdrop/boxel-three/dist/src/exporters/image.js +267 -261
  27. package/node_modules/@playdrop/boxel-three/dist/src/index.js +64 -15
  28. package/node_modules/@playdrop/boxel-three/dist/src/instantiate.js +89 -63
  29. package/node_modules/@playdrop/boxel-three/dist/src/nodes.js +81 -56
  30. package/node_modules/@playdrop/boxel-three/dist/src/overlays.js +112 -86
  31. package/node_modules/@playdrop/boxel-three/dist/src/primitives.js +45 -17
  32. package/node_modules/@playdrop/boxel-three/dist/src/scene.js +160 -136
  33. package/node_modules/@playdrop/boxel-three/dist/src/skinned-mesh.js +582 -584
  34. package/node_modules/@playdrop/boxel-three/dist/src/texture-atlas.js +123 -97
  35. package/node_modules/@playdrop/boxel-three/dist/src/textures.js +207 -182
  36. package/node_modules/@playdrop/boxel-three/dist/src/types.js +15 -1
  37. package/node_modules/@playdrop/boxel-three/dist/src/voxels/faces.js +451 -425
  38. package/node_modules/@playdrop/boxel-three/dist/src/voxels/mesher.js +109 -84
  39. package/node_modules/@playdrop/boxel-three/dist/test/export-image.playwright.test.js +127 -106
  40. package/node_modules/@playdrop/boxel-three/dist/test/fixtures/render-worker.js +73 -51
  41. package/node_modules/@playdrop/boxel-three/dist/test/glb-skinned.test.js +97 -79
  42. package/node_modules/@playdrop/boxel-three/dist/test/index.test.js +29 -7
  43. package/node_modules/@playdrop/boxel-three/dist/test/instantiate.test.js +80 -60
  44. package/node_modules/@playdrop/boxel-three/dist/test/overlays.test.js +41 -19
  45. package/node_modules/@playdrop/boxel-three/dist/test/scene-filter.test.js +72 -50
  46. package/node_modules/@playdrop/boxel-three/dist/test/scene-smoke.test.js +84 -62
  47. package/node_modules/@playdrop/boxel-three/dist/test/skinned-mesh.test.js +69 -47
  48. package/node_modules/@playdrop/boxel-three/dist/test/textured-overlay.test.js +129 -109
  49. package/node_modules/@playdrop/boxel-three/dist/test/voxels.test.js +40 -18
  50. package/node_modules/@playdrop/boxel-three/package.json +1 -1
  51. package/node_modules/@playdrop/config/client-meta.json +1 -1
  52. package/node_modules/@playdrop/types/dist/api.js +289 -203
  53. package/node_modules/@playdrop/types/dist/app-capability-filters.js +112 -62
  54. package/node_modules/@playdrop/types/dist/app.js +91 -45
  55. package/node_modules/@playdrop/types/dist/asset-pack.js +37 -5
  56. package/node_modules/@playdrop/types/dist/asset-spec.js +170 -90
  57. package/node_modules/@playdrop/types/dist/asset.js +186 -108
  58. package/node_modules/@playdrop/types/dist/content-license.js +49 -15
  59. package/node_modules/@playdrop/types/dist/creator-public-image.js +60 -32
  60. package/node_modules/@playdrop/types/dist/ecs.js +102 -82
  61. package/node_modules/@playdrop/types/dist/engine-builtins.js +603 -573
  62. package/node_modules/@playdrop/types/dist/entity.js +63 -53
  63. package/node_modules/@playdrop/types/dist/graph.js +116 -80
  64. package/node_modules/@playdrop/types/dist/index.js +47 -20
  65. package/node_modules/@playdrop/types/dist/owned-assets.js +55 -33
  66. package/node_modules/@playdrop/types/dist/player-meta.js +151 -100
  67. package/node_modules/@playdrop/types/dist/realtime.js +27 -7
  68. package/node_modules/@playdrop/types/dist/version.js +182 -124
  69. package/node_modules/@playdrop/types/package.json +1 -1
  70. package/package.json +1 -1
@@ -1,625 +1,623 @@
1
- import { createBuilderContext, updateBuilderContext, } from './context.js';
2
- import { createPrimitiveObject, createBoundingBoxMesh } from './builders.js';
3
- import { buildTextureAtlas, buildEmissiveAtlas } from './texture-atlas.js';
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
- object.position.set(node.position[0], node.position[1], node.position[2]);
6
- if (node.rotation) {
7
- object.rotation.set(THREE.MathUtils.degToRad(node.rotation[0]), THREE.MathUtils.degToRad(node.rotation[1]), THREE.MathUtils.degToRad(node.rotation[2]));
8
- }
9
- if (node.scale) {
10
- object.scale.set(node.scale[0], node.scale[1], node.scale[2]);
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
- const stack = [object];
15
- while (stack.length > 0) {
16
- const current = stack.pop();
17
- current.userData = { ...(current.userData ?? {}), primitiveOwnerId: ownerId };
18
- current.children.forEach(child => stack.push(child));
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
- const object = new context.THREE.Object3D();
23
- object.name = node.id;
24
- object.userData = {
25
- ...(object.userData ?? {}),
26
- nodeId: node.id,
27
- };
28
- applyNodeTransforms(context.THREE, object, node);
29
- const nodes = new Map();
30
- nodes.set(node.id, object);
31
- if (node.mesh) {
32
- const primitive = context.primitivesById.get(node.mesh);
33
- if (primitive) {
34
- const primitiveObject = createPrimitiveObject(context, primitive, 'centered', visitedPrimitives);
35
- if (primitiveObject) {
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
- placeholder.position.set(node.center[0], node.center[1], node.center[2]);
59
+ primitiveObject.position.set(node.center[0], node.center[1], node.center[2]);
53
60
  }
54
- object.add(placeholder);
61
+ assignPrimitiveOwner(primitiveObject, node.id);
62
+ object.add(primitiveObject);
63
+ }
55
64
  }
56
- node.children.forEach(child => {
57
- const childGraph = buildNodeGraph(context, child, new Set(visitedPrimitives));
58
- object.add(childGraph.object);
59
- childGraph.nodes.forEach((childObject, childId) => nodes.set(childId, childObject));
60
- });
61
- return { object, nodes };
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
- const bone = new THREE.Bone();
65
- bone.name = node.id;
66
- bone.position.set(node.position[0], node.position[1], node.position[2]);
67
- if (node.rotation) {
68
- bone.rotation.set(THREE.MathUtils.degToRad(node.rotation[0]), THREE.MathUtils.degToRad(node.rotation[1]), THREE.MathUtils.degToRad(node.rotation[2]));
69
- }
70
- if (node.scale) {
71
- bone.scale.set(node.scale[0], node.scale[1], node.scale[2]);
72
- }
73
- if (parentBone) {
74
- parentBone.add(bone);
75
- }
76
- bones.push(bone);
77
- boneMap.set(node.id, bone);
78
- boneIndexMap.set(node.id, bones.length - 1);
79
- node.children.forEach(child => {
80
- buildBoneHierarchy(THREE, child, bone, bones, boneMap, boneIndexMap);
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
- if ('color' in material && material.color instanceof THREE.Color) {
85
- return material.color.clone();
86
- }
87
- return new THREE.Color(0.5, 0.5, 0.5);
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
- export function buildEntitySkinnedMesh(options) {
94
- const { THREE, entity } = options;
95
- const warnings = [];
96
- const builderContext = createBuilderContext({
97
- THREE,
98
- settings: { enableShadows: false },
99
- });
100
- updateBuilderContext(builderContext, { entity });
101
- const { object: nodeRootObject, nodes: nodeObjects } = buildNodeGraph(builderContext, entity.geometry.root);
102
- nodeRootObject.updateMatrixWorld(true);
103
- const nodeDefs = new Map();
104
- const parentMap = new Map();
105
- (function collect(node, parentId) {
106
- nodeDefs.set(node.id, node);
107
- parentMap.set(node.id, parentId);
108
- node.children.forEach(child => collect(child, node.id));
109
- })(entity.geometry.root, null);
110
- const bones = [];
111
- const boneMap = new Map();
112
- const boneIndexMap = new Map();
113
- buildBoneHierarchy(THREE, entity.geometry.root, null, bones, boneMap, boneIndexMap);
114
- if (bones.length === 0) {
115
- throw new Error('entity_skinned_mesh_no_bones');
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
- bones[0].updateMatrixWorld(true);
118
- const boneInverses = bones.map(bone => bone.matrixWorld.clone().invert());
119
- const positions = [];
120
- const normals = [];
121
- const colors = [];
122
- const uvs = [];
123
- const skinIndices = [];
124
- const skinWeights = [];
125
- const blendStrengths = [];
126
- const tempColor = new THREE.Color(0.5, 0.5, 0.5);
127
- const tempWorldPos = new THREE.Vector3();
128
- const tempLocalPos = new THREE.Vector3();
129
- const computeSignedDistanceToBounds = (point, halfExtents) => {
130
- const dx = Math.abs(point.x) - halfExtents.x;
131
- const dy = Math.abs(point.y) - halfExtents.y;
132
- const dz = Math.abs(point.z) - halfExtents.z;
133
- const outsideX = Math.max(dx, 0);
134
- const outsideY = Math.max(dy, 0);
135
- const outsideZ = Math.max(dz, 0);
136
- const outsideDist = Math.sqrt(outsideX * outsideX + outsideY * outsideY + outsideZ * outsideZ);
137
- if (dx <= 0 && dy <= 0 && dz <= 0) {
138
- const insideX = halfExtents.x - Math.abs(point.x);
139
- const insideY = halfExtents.y - Math.abs(point.y);
140
- const insideZ = halfExtents.z - Math.abs(point.z);
141
- const minInside = Math.min(insideX, insideY, insideZ);
142
- return -minInside;
143
- }
144
- return outsideDist;
145
- };
146
- const evaluateInfluence = (bounds, worldPosition, scratch) => {
147
- scratch.copy(worldPosition).applyMatrix4(bounds.inverseMatrix).sub(bounds.center);
148
- const distance = computeSignedDistanceToBounds(scratch, bounds.halfExtents);
149
- if (distance <= 0) {
150
- return { inside: true, falloff: 1 };
151
- }
152
- const largestAxis = Math.max(bounds.halfExtents.x, bounds.halfExtents.y, bounds.halfExtents.z);
153
- const radius = Math.max(CONNECT_MIN_RADIUS, largestAxis * CONNECT_FALLOFF_RATIO);
154
- if (distance >= radius) {
155
- return { inside: false, falloff: 0 };
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
- const connectConfigs = new Map();
162
- const parentBlendLookup = new Map();
163
- const deriveBoundsFromObject = (object) => {
164
- const cache = object.userData?.__connectBounds;
165
- if (cache) {
166
- return cache;
167
- }
168
- const bounds = new THREE.Box3().setFromObject(object);
169
- if (!bounds || !Number.isFinite(bounds.min.x) || bounds.isEmpty()) {
170
- return null;
171
- }
172
- const inverse = object.matrixWorld.clone().invert();
173
- bounds.applyMatrix4(inverse);
174
- const sizeVec = new THREE.Vector3();
175
- const centerVec = new THREE.Vector3();
176
- bounds.getSize(sizeVec);
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
- const createBounds = (nodeDef, object) => {
187
- if (!object) {
188
- return null;
189
- }
190
- const sizeSource = nodeDef?.size ??
191
- object.userData?.size ??
192
- deriveBoundsFromObject(object)?.size;
193
- if (!sizeSource) {
194
- return null;
195
- }
196
- const derived = !nodeDef?.center ? deriveBoundsFromObject(object) : null;
197
- const centerSource = nodeDef?.center ??
198
- object.userData?.center ??
199
- derived?.center ??
200
- [0, 0, 0];
201
- return {
202
- inverseMatrix: object.matrixWorld.clone().invert(),
203
- center: new THREE.Vector3(centerSource[0], centerSource[1], centerSource[2]),
204
- halfExtents: new THREE.Vector3(sizeSource[0] / 2, sizeSource[1] / 2, sizeSource[2] / 2),
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
- nodeDefs.forEach((node, nodeId) => {
208
- const mode = (node.connect ?? 'none');
209
- if (mode === 'none') {
210
- return;
211
- }
212
- const parentId = parentMap.get(nodeId);
213
- if (!parentId) {
214
- warnings.push(`[skinned] node ${nodeId} connect ${mode} ignored (no parent)`);
215
- return;
216
- }
217
- const parentBoneIndex = boneIndexMap.get(parentId);
218
- if (parentBoneIndex === undefined) {
219
- warnings.push(`[skinned] node ${nodeId} connect ${mode} ignored (missing parent bone)`);
220
- return;
221
- }
222
- const childBoneIndex = boneIndexMap.get(nodeId);
223
- if (childBoneIndex === undefined) {
224
- warnings.push(`[skinned] node ${nodeId} connect ${mode} ignored (missing child bone)`);
225
- return;
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
- const parentObject = nodeObjects.get(parentId);
228
- if (!parentObject) {
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
- const childObject = nodeObjects.get(nodeId);
233
- if (!childObject) {
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
- const parentDef = nodeDefs.get(parentId);
238
- let childBlend;
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
- let parentBlend;
246
- if (mode === 'parent' || mode === 'both') {
247
- parentBlend = createBounds(node, childObject) ?? undefined;
248
- if (!parentBlend) {
249
- warnings.push(`[skinned] node ${nodeId} connect ${mode} ignored (child ${nodeId} missing size)`);
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
- if (!childBlend && !parentBlend) {
253
- return;
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
- const config = {
256
- mode,
257
- childId: nodeId,
258
- parentId,
259
- parentBoneIndex,
260
- childBoneIndex,
261
- childBlend,
262
- parentBlend,
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
- connectConfigs.set(nodeId, config);
266
- if (config.parentBlend) {
267
- const existing = parentBlendLookup.get(parentId);
268
- if (existing) {
269
- existing.push(config);
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
- else {
272
- parentBlendLookup.set(parentId, [config]);
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
- const colorTextures = [];
277
- const textureSet = new Set();
278
- const emissivePairs = new Map();
279
- let needsAlphaTest = false;
280
- let needsDoubleSide = false;
281
- nodeObjects.forEach(nodeObject => {
282
- nodeObject.traverse(child => {
283
- if (!child.isMesh) {
284
- return;
285
- }
286
- const mesh = child;
287
- if (!mesh.geometry) {
288
- return;
289
- }
290
- const material = mesh.material;
291
- const materials = Array.isArray(material) ? material : [material];
292
- materials.forEach(entry => {
293
- const std = entry;
294
- if (std.map instanceof THREE.DataTexture && std.map.image?.width && std.map.image?.height) {
295
- if (!textureSet.has(std.map.uuid)) {
296
- textureSet.add(std.map.uuid);
297
- colorTextures.push(std.map);
298
- }
299
- }
300
- if (std.map instanceof THREE.DataTexture && std.emissiveMap instanceof THREE.DataTexture) {
301
- emissivePairs.set(std.map, std.emissiveMap);
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
- if (std.side === THREE.DoubleSide) {
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
- const vertexCount = positionAttr.count;
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
- 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,
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
- 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
- }
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
- const connectOverlayUniform = { value: 0 };
563
- material.onBeforeCompile = shader => {
564
- shader.uniforms.uConnectOverlay = connectOverlayUniform;
565
- shader.vertexShader = shader.vertexShader
566
- .replace('#include <common>', '#include <common>\nattribute float connectStrength;\nvarying float vConnectStrength;')
567
- .replace('#include <begin_vertex>', '#include <begin_vertex>\n vConnectStrength = connectStrength;');
568
- shader.fragmentShader = shader.fragmentShader
569
- .replace('#include <common>', '#include <common>\nuniform float uConnectOverlay;\nvarying float vConnectStrength;')
570
- .replace('#include <output_fragment>', `#include <output_fragment>
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
- material.userData.connectOverlayUniform = connectOverlayUniform;
578
- material.needsUpdate = true;
579
- const mesh = new THREE.SkinnedMesh(geometry, material);
580
- mesh.name = `${entity.id ?? 'entity'}__skinned`;
581
- const skeleton = new THREE.Skeleton(bones, boneInverses);
582
- mesh.add(bones[0]);
583
- mesh.bind(skeleton);
584
- mesh.normalizeSkinWeights();
585
- let weldedVertices = 0;
586
- const weldedByNode = {};
587
- const connectBreakdown = {};
588
- connectConfigs.forEach((config, nodeId) => {
589
- const total = config.stats.childVertices + config.stats.parentVertices;
590
- if (total > 0) {
591
- weldedVertices += total;
592
- weldedByNode[nodeId] = total;
593
- connectBreakdown[nodeId] = {
594
- childVertices: config.stats.childVertices,
595
- parentVertices: config.stats.parentVertices,
596
- };
597
- }
598
- });
599
- const dispose = () => {
600
- mesh.geometry.dispose();
601
- const mat = mesh.material;
602
- if (Array.isArray(mat)) {
603
- mat.forEach(entry => entry.dispose?.());
604
- }
605
- else {
606
- mat.dispose?.();
607
- }
608
- };
609
- return {
610
- mesh,
611
- bones,
612
- skeleton,
613
- boneMap,
614
- stats: {
615
- vertices: positions.length / 3,
616
- triangles: positions.length / 9,
617
- weldedVertices,
618
- weldedByNode,
619
- connectBreakdown,
620
- atlas: colorAtlas ? { width: colorAtlas.width, height: colorAtlas.height } : null,
621
- },
622
- warnings,
623
- dispose,
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
+ });