@playdrop/playdrop-cli 0.7.13 → 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.
Files changed (71) 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/config/dist/tsconfig.tsbuildinfo +1 -1
  53. package/node_modules/@playdrop/types/dist/api.js +289 -203
  54. package/node_modules/@playdrop/types/dist/app-capability-filters.js +112 -62
  55. package/node_modules/@playdrop/types/dist/app.js +91 -45
  56. package/node_modules/@playdrop/types/dist/asset-pack.js +37 -5
  57. package/node_modules/@playdrop/types/dist/asset-spec.js +170 -90
  58. package/node_modules/@playdrop/types/dist/asset.js +186 -108
  59. package/node_modules/@playdrop/types/dist/content-license.js +49 -15
  60. package/node_modules/@playdrop/types/dist/creator-public-image.js +60 -32
  61. package/node_modules/@playdrop/types/dist/ecs.js +102 -82
  62. package/node_modules/@playdrop/types/dist/engine-builtins.js +603 -573
  63. package/node_modules/@playdrop/types/dist/entity.js +63 -53
  64. package/node_modules/@playdrop/types/dist/graph.js +116 -80
  65. package/node_modules/@playdrop/types/dist/index.js +47 -20
  66. package/node_modules/@playdrop/types/dist/owned-assets.js +55 -33
  67. package/node_modules/@playdrop/types/dist/player-meta.js +151 -100
  68. package/node_modules/@playdrop/types/dist/realtime.js +27 -7
  69. package/node_modules/@playdrop/types/dist/version.js +182 -124
  70. package/node_modules/@playdrop/types/package.json +1 -1
  71. package/package.json +1 -1
@@ -1,931 +1,929 @@
1
- import { generateVoxelFaceSurfaces, FACE_ORDER } from '../voxels/faces.js';
2
- import { createBoxGeometry, collectGroupVertexIndices } from '../primitives.js';
3
- import { createBoxelScene } from '../scene.js';
4
- import { buildEntitySkinnedMesh } from '../skinned-mesh.js';
5
- import { buildAnimations } from '../animations.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 glb_exports = {};
19
+ __export(glb_exports, {
20
+ __testing: () => __testing,
21
+ createBoxelSceneGraph: () => createBoxelSceneGraph,
22
+ exportModelToGLB: () => exportModelToGLB
23
+ });
24
+ module.exports = __toCommonJS(glb_exports);
25
+ var import_faces = require("../voxels/faces.js");
26
+ var import_primitives = require("../primitives.js");
27
+ var import_scene = require("../scene.js");
28
+ var import_skinned_mesh = require("../skinned-mesh.js");
29
+ var import_animations = require("../animations.js");
6
30
  const DEFAULT_COLOR = [59, 130, 246];
7
31
  const DEFAULT_OPACITY = 0.45;
8
32
  const ATLAS_PADDING = 1;
9
- const FALLBACK_TEXTURE_ID = '__fallback__';
10
- const OVERLAY_FALLBACK_TEXTURE_ID = '__overlay_fallback__';
11
- export async function exportModelToGLB(options) {
12
- const { THREE, GLTFExporterCtor, model, mode = 'merged', binary = true, animationIds } = options;
13
- if (mode === 'skinned') {
14
- const skinned = buildEntitySkinnedMesh({ THREE, entity: model });
15
- const root = new THREE.Group();
16
- root.add(skinned.mesh);
17
- root.updateMatrixWorld(true);
18
- const nodeLookup = new Map();
19
- skinned.boneMap.forEach((bone, nodeId) => nodeLookup.set(nodeId, bone));
20
- const animations = buildAnimations(THREE, model, nodeLookup);
21
- const clips = animationIds === null
22
- ? []
23
- : animations
24
- .filter(animation => {
25
- if (!Array.isArray(animationIds) || animationIds.length === 0)
26
- return true;
27
- return animationIds.includes(animation.id);
28
- })
29
- .map(animation => animation.clip);
30
- const exporter = new GLTFExporterCtor();
31
- const dispose = () => {
32
- skinned.dispose();
33
- };
34
- return new Promise((resolve, reject) => {
35
- try {
36
- exporter.parse(root, result => {
37
- try {
38
- const blob = result instanceof ArrayBuffer
39
- ? new Blob([result], { type: 'model/gltf-binary' })
40
- : new Blob([JSON.stringify(result)], { type: 'model/gltf+json' });
41
- dispose();
42
- resolve(blob);
43
- }
44
- catch (error) {
45
- dispose();
46
- reject(error);
47
- }
48
- }, error => {
49
- dispose();
50
- reject(error instanceof Error ? error : new Error('gltf_export_failed'));
51
- }, { binary, animations: clips });
52
- }
53
- catch (error) {
54
- dispose();
55
- reject(error instanceof Error ? error : new Error(String(error)));
56
- }
57
- });
58
- }
59
- const context = createExportContext(THREE, model);
60
- const sceneGraph = buildSceneGraph(context, mode);
61
- const exporter = new GLTFExporterCtor();
62
- const exportScene = new THREE.Scene();
63
- exportScene.background = null;
64
- exportScene.add(sceneGraph.root);
65
- exportScene.updateMatrixWorld(true);
66
- const animationClips = resolveAnimationClips(sceneGraph, mode, animationIds);
33
+ const FALLBACK_TEXTURE_ID = "__fallback__";
34
+ const OVERLAY_FALLBACK_TEXTURE_ID = "__overlay_fallback__";
35
+ async function exportModelToGLB(options) {
36
+ const { THREE, GLTFExporterCtor, model, mode = "merged", binary = true, animationIds } = options;
37
+ if (mode === "skinned") {
38
+ const skinned = (0, import_skinned_mesh.buildEntitySkinnedMesh)({ THREE, entity: model });
39
+ const root = new THREE.Group();
40
+ root.add(skinned.mesh);
41
+ root.updateMatrixWorld(true);
42
+ const nodeLookup = /* @__PURE__ */ new Map();
43
+ skinned.boneMap.forEach((bone, nodeId) => nodeLookup.set(nodeId, bone));
44
+ const animations = (0, import_animations.buildAnimations)(THREE, model, nodeLookup);
45
+ const clips = animationIds === null ? [] : animations.filter((animation) => {
46
+ if (!Array.isArray(animationIds) || animationIds.length === 0)
47
+ return true;
48
+ return animationIds.includes(animation.id);
49
+ }).map((animation) => animation.clip);
50
+ const exporter2 = new GLTFExporterCtor();
51
+ const dispose = () => {
52
+ skinned.dispose();
53
+ };
67
54
  return new Promise((resolve, reject) => {
55
+ try {
56
+ exporter2.parse(root, (result) => {
57
+ try {
58
+ const blob = result instanceof ArrayBuffer ? new Blob([result], { type: "model/gltf-binary" }) : new Blob([JSON.stringify(result)], { type: "model/gltf+json" });
59
+ dispose();
60
+ resolve(blob);
61
+ } catch (error) {
62
+ dispose();
63
+ reject(error);
64
+ }
65
+ }, (error) => {
66
+ dispose();
67
+ reject(error instanceof Error ? error : new Error("gltf_export_failed"));
68
+ }, { binary, animations: clips });
69
+ } catch (error) {
70
+ dispose();
71
+ reject(error instanceof Error ? error : new Error(String(error)));
72
+ }
73
+ });
74
+ }
75
+ const context = createExportContext(THREE, model);
76
+ const sceneGraph = buildSceneGraph(context, mode);
77
+ const exporter = new GLTFExporterCtor();
78
+ const exportScene = new THREE.Scene();
79
+ exportScene.background = null;
80
+ exportScene.add(sceneGraph.root);
81
+ exportScene.updateMatrixWorld(true);
82
+ const animationClips = resolveAnimationClips(sceneGraph, mode, animationIds);
83
+ return new Promise((resolve, reject) => {
84
+ try {
85
+ exporter.parse(exportScene, (result) => {
68
86
  try {
69
- exporter.parse(exportScene, result => {
70
- try {
71
- const blob = result instanceof ArrayBuffer
72
- ? new Blob([result], { type: 'model/gltf-binary' })
73
- : new Blob([JSON.stringify(result)], { type: 'model/gltf+json' });
74
- sceneGraph.dispose();
75
- disposeExportContext(context);
76
- resolve(blob);
77
- }
78
- catch (error) {
79
- sceneGraph.dispose();
80
- disposeExportContext(context);
81
- reject(error);
82
- }
83
- }, error => {
84
- sceneGraph.dispose();
85
- disposeExportContext(context);
86
- reject(error instanceof Error ? error : new Error('gltf_export_failed'));
87
- }, { binary, animations: animationClips });
87
+ const blob = result instanceof ArrayBuffer ? new Blob([result], { type: "model/gltf-binary" }) : new Blob([JSON.stringify(result)], { type: "model/gltf+json" });
88
+ sceneGraph.dispose();
89
+ disposeExportContext(context);
90
+ resolve(blob);
91
+ } catch (error) {
92
+ sceneGraph.dispose();
93
+ disposeExportContext(context);
94
+ reject(error);
88
95
  }
89
- catch (error) {
90
- sceneGraph.dispose();
91
- disposeExportContext(context);
92
- reject(error instanceof Error ? error : new Error(String(error)));
93
- }
94
- });
95
- }
96
- export function createBoxelSceneGraph(options) {
97
- const { THREE, model } = options;
98
- const mode = options.mode ?? 'merged';
99
- const context = createExportContext(THREE, model);
100
- const sceneGraph = buildSceneGraph(context, mode);
101
- let disposed = false;
102
- const dispose = () => {
103
- if (disposed)
104
- return;
96
+ }, (error) => {
105
97
  sceneGraph.dispose();
106
98
  disposeExportContext(context);
107
- disposed = true;
108
- };
109
- return {
110
- root: sceneGraph.root,
111
- nodeLookup: sceneGraph.nodeLookup,
112
- animations: sceneGraph.animations,
113
- dispose,
114
- };
99
+ reject(error instanceof Error ? error : new Error("gltf_export_failed"));
100
+ }, { binary, animations: animationClips });
101
+ } catch (error) {
102
+ sceneGraph.dispose();
103
+ disposeExportContext(context);
104
+ reject(error instanceof Error ? error : new Error(String(error)));
105
+ }
106
+ });
107
+ }
108
+ function createBoxelSceneGraph(options) {
109
+ const { THREE, model } = options;
110
+ const mode = options.mode ?? "merged";
111
+ const context = createExportContext(THREE, model);
112
+ const sceneGraph = buildSceneGraph(context, mode);
113
+ let disposed = false;
114
+ const dispose = () => {
115
+ if (disposed)
116
+ return;
117
+ sceneGraph.dispose();
118
+ disposeExportContext(context);
119
+ disposed = true;
120
+ };
121
+ return {
122
+ root: sceneGraph.root,
123
+ nodeLookup: sceneGraph.nodeLookup,
124
+ animations: sceneGraph.animations,
125
+ dispose
126
+ };
115
127
  }
116
128
  function createExportContext(THREE, model) {
117
- const materialsById = new Map(model.materials.map(material => [material.id, material]));
118
- const primitivesById = new Map(model.primitives.map(primitive => [primitive.id, primitive]));
119
- const texturesById = new Map(model.textures.map(texture => [texture.id, texture]));
120
- const patternsById = new Map(model.patterns.map(pattern => [pattern.id, pattern]));
121
- const voxelAtlasEntries = [];
122
- const voxelSurfacesByPrimitive = new Map();
123
- let overlayFallbackNeeded = false;
124
- model.primitives.forEach(primitive => {
125
- if (primitive.kind === 'textured_box') {
126
- if (hasOverlayTextures(primitive)) {
127
- overlayFallbackNeeded = true;
128
- }
129
- }
130
- if (primitive.kind !== 'voxel_box') {
131
- return;
132
- }
133
- const surfaces = generateVoxelFaceSurfaces({
134
- primitive: primitive,
135
- materialsById,
136
- defaultColor: DEFAULT_COLOR,
137
- });
138
- if (surfaces.length === 0) {
139
- return;
140
- }
141
- const cachedSurfaces = surfaces.map((surface, index) => {
142
- const textureId = `__voxel__${primitive.id}__${surface.face}__${index}`;
143
- voxelAtlasEntries.push({
144
- id: textureId,
145
- width: surface.textureWidth,
146
- height: surface.textureHeight,
147
- color: surface.color,
148
- emissive: surface.emissive,
149
- hasEmissive: surface.hasEmissive,
150
- hasTransparency: surface.hasTransparency,
151
- });
152
- return { ...surface, textureId };
153
- });
154
- voxelSurfacesByPrimitive.set(primitive.id, cachedSurfaces);
129
+ const materialsById = new Map(model.materials.map((material) => [material.id, material]));
130
+ const primitivesById = new Map(model.primitives.map((primitive) => [primitive.id, primitive]));
131
+ const texturesById = new Map(model.textures.map((texture) => [texture.id, texture]));
132
+ const patternsById = new Map(model.patterns.map((pattern) => [pattern.id, pattern]));
133
+ const voxelAtlasEntries = [];
134
+ const voxelSurfacesByPrimitive = /* @__PURE__ */ new Map();
135
+ let overlayFallbackNeeded = false;
136
+ model.primitives.forEach((primitive) => {
137
+ if (primitive.kind === "textured_box") {
138
+ if (hasOverlayTextures(primitive)) {
139
+ overlayFallbackNeeded = true;
140
+ }
141
+ }
142
+ if (primitive.kind !== "voxel_box") {
143
+ return;
144
+ }
145
+ const surfaces = (0, import_faces.generateVoxelFaceSurfaces)({
146
+ primitive,
147
+ materialsById,
148
+ defaultColor: DEFAULT_COLOR
155
149
  });
156
- const extraEntries = [...voxelAtlasEntries];
157
- if (overlayFallbackNeeded) {
158
- extraEntries.push(buildOverlayFallbackTexturePixels());
159
- }
160
- const usedTextureIds = collectUsedTextureIds(model, primitivesById);
161
- const atlas = buildTextureAtlas({ THREE, materialsById, texturesById, patternsById, usedTextureIds }, extraEntries);
162
- return {
163
- THREE,
164
- model,
165
- materialsById,
166
- primitivesById,
167
- texturesById,
168
- patternsById,
169
- atlas,
170
- voxelSurfacesByPrimitive,
171
- plainMaterialCache: new Map(),
172
- };
150
+ if (surfaces.length === 0) {
151
+ return;
152
+ }
153
+ const cachedSurfaces = surfaces.map((surface, index) => {
154
+ const textureId = `__voxel__${primitive.id}__${surface.face}__${index}`;
155
+ voxelAtlasEntries.push({
156
+ id: textureId,
157
+ width: surface.textureWidth,
158
+ height: surface.textureHeight,
159
+ color: surface.color,
160
+ emissive: surface.emissive,
161
+ hasEmissive: surface.hasEmissive,
162
+ hasTransparency: surface.hasTransparency
163
+ });
164
+ return { ...surface, textureId };
165
+ });
166
+ voxelSurfacesByPrimitive.set(primitive.id, cachedSurfaces);
167
+ });
168
+ const extraEntries = [...voxelAtlasEntries];
169
+ if (overlayFallbackNeeded) {
170
+ extraEntries.push(buildOverlayFallbackTexturePixels());
171
+ }
172
+ const usedTextureIds = collectUsedTextureIds(model, primitivesById);
173
+ const atlas = buildTextureAtlas({ THREE, materialsById, texturesById, patternsById, usedTextureIds }, extraEntries);
174
+ return {
175
+ THREE,
176
+ model,
177
+ materialsById,
178
+ primitivesById,
179
+ texturesById,
180
+ patternsById,
181
+ atlas,
182
+ voxelSurfacesByPrimitive,
183
+ plainMaterialCache: /* @__PURE__ */ new Map()
184
+ };
173
185
  }
174
186
  function disposeExportContext(context) {
175
- if (context.atlas) {
176
- context.atlas.map.dispose();
177
- context.atlas.emissiveMap?.dispose();
178
- }
179
- context.plainMaterialCache.forEach(material => material.dispose());
180
- context.plainMaterialCache.clear();
187
+ if (context.atlas) {
188
+ context.atlas.map.dispose();
189
+ context.atlas.emissiveMap?.dispose();
190
+ }
191
+ context.plainMaterialCache.forEach((material) => material.dispose());
192
+ context.plainMaterialCache.clear();
181
193
  }
182
194
  function buildTextureAtlas(options, extraEntries = []) {
183
- const { THREE, materialsById, texturesById, patternsById, usedTextureIds } = options;
184
- if (usedTextureIds.size === 0) {
185
- if (extraEntries.length === 0) {
186
- return null;
187
- }
188
- }
189
- const entries = [];
190
- for (const textureId of usedTextureIds) {
191
- const textureDef = texturesById.get(textureId);
192
- if (!textureDef)
193
- continue;
194
- const pattern = patternsById.get(textureDef.pattern);
195
- if (!pattern)
196
- continue;
197
- entries.push(buildTexturePixels(textureDef, pattern, materialsById));
198
- }
199
- if (extraEntries.length > 0) {
200
- extraEntries.forEach(entry => {
201
- entries.push(entry);
202
- });
203
- }
204
- const fallbackEntry = buildFallbackTexturePixels();
205
- entries.push(fallbackEntry);
206
- const paddedWidths = entries.map(entry => entry.width + ATLAS_PADDING * 2);
207
- const paddedHeights = entries.map(entry => entry.height + ATLAS_PADDING * 2);
208
- const cellWidth = Math.max(...paddedWidths);
209
- const cellHeight = Math.max(...paddedHeights);
210
- const textureCount = entries.length;
211
- const columns = Math.max(1, Math.ceil(Math.sqrt(textureCount)));
212
- const rows = Math.max(1, Math.ceil(textureCount / columns));
213
- const atlasWidth = THREE.MathUtils.ceilPowerOfTwo(columns * cellWidth);
214
- const atlasHeight = THREE.MathUtils.ceilPowerOfTwo(rows * cellHeight);
215
- const colorData = new Uint8Array(atlasWidth * atlasHeight * 4);
216
- const emissiveData = new Uint8Array(atlasWidth * atlasHeight * 4);
217
- let hasEmissive = false;
218
- let hasTransparency = false;
219
- const regions = new Map();
220
- entries.forEach((entry, index) => {
221
- const column = index % columns;
222
- const row = Math.floor(index / columns);
223
- const originX = column * cellWidth;
224
- const originY = row * cellHeight;
225
- const paddedX = originX + ATLAS_PADDING;
226
- const paddedY = originY + ATLAS_PADDING;
227
- blitTexture(entry.color, entry.width, entry.height, atlasWidth, colorData, paddedX, paddedY);
228
- extendEdgePixels(colorData, entry.width, entry.height, atlasWidth, atlasHeight, paddedX, paddedY);
229
- if (entry.hasEmissive && entry.emissive) {
230
- blitTexture(entry.emissive, entry.width, entry.height, atlasWidth, emissiveData, paddedX, paddedY);
231
- extendEdgePixels(emissiveData, entry.width, entry.height, atlasWidth, atlasHeight, paddedX, paddedY);
232
- hasEmissive = true;
233
- }
234
- else {
235
- fillTransparentRegion(emissiveData, entry.width, entry.height, atlasWidth, paddedX, paddedY);
236
- }
237
- hasTransparency = hasTransparency || entry.hasTransparency;
238
- const region = {
239
- uMin: (paddedX + 0) / atlasWidth,
240
- vMin: (paddedY + 0) / atlasHeight,
241
- uMax: (paddedX + entry.width) / atlasWidth,
242
- vMax: (paddedY + entry.height) / atlasHeight,
243
- };
244
- regions.set(entry.id, region);
195
+ const { THREE, materialsById, texturesById, patternsById, usedTextureIds } = options;
196
+ if (usedTextureIds.size === 0) {
197
+ if (extraEntries.length === 0) {
198
+ return null;
199
+ }
200
+ }
201
+ const entries = [];
202
+ for (const textureId of usedTextureIds) {
203
+ const textureDef = texturesById.get(textureId);
204
+ if (!textureDef)
205
+ continue;
206
+ const pattern = patternsById.get(textureDef.pattern);
207
+ if (!pattern)
208
+ continue;
209
+ entries.push(buildTexturePixels(textureDef, pattern, materialsById));
210
+ }
211
+ if (extraEntries.length > 0) {
212
+ extraEntries.forEach((entry) => {
213
+ entries.push(entry);
245
214
  });
246
- const map = new THREE.DataTexture(colorData, atlasWidth, atlasHeight);
247
- map.needsUpdate = true;
248
- map.magFilter = THREE.NearestFilter;
249
- map.minFilter = THREE.NearestFilter;
250
- map.wrapS = THREE.ClampToEdgeWrapping;
251
- map.wrapT = THREE.ClampToEdgeWrapping;
252
- map.flipY = false;
253
- if ('colorSpace' in map) {
254
- map.colorSpace = THREE.SRGBColorSpace ?? map.colorSpace;
255
- }
256
- else if ('encoding' in map) {
257
- map.encoding = THREE.sRGBEncoding ?? map.encoding;
258
- }
259
- let emissiveMap = null;
260
- if (hasEmissive) {
261
- emissiveMap = new THREE.DataTexture(emissiveData, atlasWidth, atlasHeight);
262
- emissiveMap.needsUpdate = true;
263
- emissiveMap.magFilter = THREE.NearestFilter;
264
- emissiveMap.minFilter = THREE.NearestFilter;
265
- emissiveMap.wrapS = THREE.ClampToEdgeWrapping;
266
- emissiveMap.wrapT = THREE.ClampToEdgeWrapping;
267
- emissiveMap.flipY = false;
268
- }
269
- const fallbackRegion = regions.get(FALLBACK_TEXTURE_ID) ?? {
270
- uMin: 0,
271
- vMin: 0,
272
- uMax: 1,
273
- vMax: 1,
215
+ }
216
+ const fallbackEntry = buildFallbackTexturePixels();
217
+ entries.push(fallbackEntry);
218
+ const paddedWidths = entries.map((entry) => entry.width + ATLAS_PADDING * 2);
219
+ const paddedHeights = entries.map((entry) => entry.height + ATLAS_PADDING * 2);
220
+ const cellWidth = Math.max(...paddedWidths);
221
+ const cellHeight = Math.max(...paddedHeights);
222
+ const textureCount = entries.length;
223
+ const columns = Math.max(1, Math.ceil(Math.sqrt(textureCount)));
224
+ const rows = Math.max(1, Math.ceil(textureCount / columns));
225
+ const atlasWidth = THREE.MathUtils.ceilPowerOfTwo(columns * cellWidth);
226
+ const atlasHeight = THREE.MathUtils.ceilPowerOfTwo(rows * cellHeight);
227
+ const colorData = new Uint8Array(atlasWidth * atlasHeight * 4);
228
+ const emissiveData = new Uint8Array(atlasWidth * atlasHeight * 4);
229
+ let hasEmissive = false;
230
+ let hasTransparency = false;
231
+ const regions = /* @__PURE__ */ new Map();
232
+ entries.forEach((entry, index) => {
233
+ const column = index % columns;
234
+ const row = Math.floor(index / columns);
235
+ const originX = column * cellWidth;
236
+ const originY = row * cellHeight;
237
+ const paddedX = originX + ATLAS_PADDING;
238
+ const paddedY = originY + ATLAS_PADDING;
239
+ blitTexture(entry.color, entry.width, entry.height, atlasWidth, colorData, paddedX, paddedY);
240
+ extendEdgePixels(colorData, entry.width, entry.height, atlasWidth, atlasHeight, paddedX, paddedY);
241
+ if (entry.hasEmissive && entry.emissive) {
242
+ blitTexture(entry.emissive, entry.width, entry.height, atlasWidth, emissiveData, paddedX, paddedY);
243
+ extendEdgePixels(emissiveData, entry.width, entry.height, atlasWidth, atlasHeight, paddedX, paddedY);
244
+ hasEmissive = true;
245
+ } else {
246
+ fillTransparentRegion(emissiveData, entry.width, entry.height, atlasWidth, paddedX, paddedY);
247
+ }
248
+ hasTransparency = hasTransparency || entry.hasTransparency;
249
+ const region = {
250
+ uMin: (paddedX + 0) / atlasWidth,
251
+ vMin: (paddedY + 0) / atlasHeight,
252
+ uMax: (paddedX + entry.width) / atlasWidth,
253
+ vMax: (paddedY + entry.height) / atlasHeight
274
254
  };
275
- return { regions, fallbackRegion, map, emissiveMap, hasTransparency };
255
+ regions.set(entry.id, region);
256
+ });
257
+ const map = new THREE.DataTexture(colorData, atlasWidth, atlasHeight);
258
+ map.needsUpdate = true;
259
+ map.magFilter = THREE.NearestFilter;
260
+ map.minFilter = THREE.NearestFilter;
261
+ map.wrapS = THREE.ClampToEdgeWrapping;
262
+ map.wrapT = THREE.ClampToEdgeWrapping;
263
+ map.flipY = false;
264
+ if ("colorSpace" in map) {
265
+ map.colorSpace = THREE.SRGBColorSpace ?? map.colorSpace;
266
+ } else if ("encoding" in map) {
267
+ map.encoding = THREE.sRGBEncoding ?? map.encoding;
268
+ }
269
+ let emissiveMap = null;
270
+ if (hasEmissive) {
271
+ emissiveMap = new THREE.DataTexture(emissiveData, atlasWidth, atlasHeight);
272
+ emissiveMap.needsUpdate = true;
273
+ emissiveMap.magFilter = THREE.NearestFilter;
274
+ emissiveMap.minFilter = THREE.NearestFilter;
275
+ emissiveMap.wrapS = THREE.ClampToEdgeWrapping;
276
+ emissiveMap.wrapT = THREE.ClampToEdgeWrapping;
277
+ emissiveMap.flipY = false;
278
+ }
279
+ const fallbackRegion = regions.get(FALLBACK_TEXTURE_ID) ?? {
280
+ uMin: 0,
281
+ vMin: 0,
282
+ uMax: 1,
283
+ vMax: 1
284
+ };
285
+ return { regions, fallbackRegion, map, emissiveMap, hasTransparency };
276
286
  }
277
287
  function buildTexturePixels(textureDef, pattern, materialsById) {
278
- const [width, height] = pattern.size;
279
- const color = new Uint8Array(width * height * 4);
280
- const emissive = new Uint8Array(width * height * 4);
281
- let hasEmissive = false;
282
- let hasTransparency = false;
283
- for (let y = 0; y < height; y += 1) {
284
- for (let x = 0; x < width; x += 1) {
285
- const sourceIndex = y * width + x;
286
- const targetY = height - 1 - y;
287
- const targetIndex = (targetY * width + x) * 4;
288
- const paletteIndex = pattern.data[sourceIndex];
289
- const materialId = textureDef.materials[paletteIndex];
290
- const material = materialId ? materialsById.get(materialId) : undefined;
291
- const baseColor = material?.baseColor ?? DEFAULT_COLOR;
292
- const opacity = material?.opacity !== undefined ? clamp(material.opacity, 0, 1) : 1;
293
- color[targetIndex] = baseColor[0];
294
- color[targetIndex + 1] = baseColor[1];
295
- color[targetIndex + 2] = baseColor[2];
296
- color[targetIndex + 3] = Math.round(opacity * 255);
297
- if (opacity < 1) {
298
- hasTransparency = true;
299
- }
300
- if (material?.emissiveColor) {
301
- emissive[targetIndex] = material.emissiveColor[0];
302
- emissive[targetIndex + 1] = material.emissiveColor[1];
303
- emissive[targetIndex + 2] = material.emissiveColor[2];
304
- emissive[targetIndex + 3] = 255;
305
- hasEmissive = true;
306
- }
307
- else {
308
- emissive[targetIndex] = 0;
309
- emissive[targetIndex + 1] = 0;
310
- emissive[targetIndex + 2] = 0;
311
- emissive[targetIndex + 3] = 0;
312
- }
313
- }
314
- }
315
- return { id: textureDef.id, width, height, color, emissive: hasEmissive ? emissive : undefined, hasEmissive, hasTransparency };
288
+ const [width, height] = pattern.size;
289
+ const color = new Uint8Array(width * height * 4);
290
+ const emissive = new Uint8Array(width * height * 4);
291
+ let hasEmissive = false;
292
+ let hasTransparency = false;
293
+ for (let y = 0; y < height; y += 1) {
294
+ for (let x = 0; x < width; x += 1) {
295
+ const sourceIndex = y * width + x;
296
+ const targetY = height - 1 - y;
297
+ const targetIndex = (targetY * width + x) * 4;
298
+ const paletteIndex = pattern.data[sourceIndex];
299
+ const materialId = textureDef.materials[paletteIndex];
300
+ const material = materialId ? materialsById.get(materialId) : void 0;
301
+ const baseColor = material?.baseColor ?? DEFAULT_COLOR;
302
+ const opacity = material?.opacity !== void 0 ? clamp(material.opacity, 0, 1) : 1;
303
+ color[targetIndex] = baseColor[0];
304
+ color[targetIndex + 1] = baseColor[1];
305
+ color[targetIndex + 2] = baseColor[2];
306
+ color[targetIndex + 3] = Math.round(opacity * 255);
307
+ if (opacity < 1) {
308
+ hasTransparency = true;
309
+ }
310
+ if (material?.emissiveColor) {
311
+ emissive[targetIndex] = material.emissiveColor[0];
312
+ emissive[targetIndex + 1] = material.emissiveColor[1];
313
+ emissive[targetIndex + 2] = material.emissiveColor[2];
314
+ emissive[targetIndex + 3] = 255;
315
+ hasEmissive = true;
316
+ } else {
317
+ emissive[targetIndex] = 0;
318
+ emissive[targetIndex + 1] = 0;
319
+ emissive[targetIndex + 2] = 0;
320
+ emissive[targetIndex + 3] = 0;
321
+ }
322
+ }
323
+ }
324
+ return { id: textureDef.id, width, height, color, emissive: hasEmissive ? emissive : void 0, hasEmissive, hasTransparency };
316
325
  }
317
326
  function buildFallbackTexturePixels() {
318
- const color = new Uint8Array(4);
319
- color[0] = DEFAULT_COLOR[0];
320
- color[1] = DEFAULT_COLOR[1];
321
- color[2] = DEFAULT_COLOR[2];
322
- color[3] = 255;
323
- const emissive = new Uint8Array(4);
324
- emissive[0] = 0;
325
- emissive[1] = 0;
326
- emissive[2] = 0;
327
- emissive[3] = 0;
328
- return {
329
- id: FALLBACK_TEXTURE_ID,
330
- width: 1,
331
- height: 1,
332
- color,
333
- emissive,
334
- hasEmissive: false,
335
- hasTransparency: false,
336
- };
327
+ const color = new Uint8Array(4);
328
+ color[0] = DEFAULT_COLOR[0];
329
+ color[1] = DEFAULT_COLOR[1];
330
+ color[2] = DEFAULT_COLOR[2];
331
+ color[3] = 255;
332
+ const emissive = new Uint8Array(4);
333
+ emissive[0] = 0;
334
+ emissive[1] = 0;
335
+ emissive[2] = 0;
336
+ emissive[3] = 0;
337
+ return {
338
+ id: FALLBACK_TEXTURE_ID,
339
+ width: 1,
340
+ height: 1,
341
+ color,
342
+ emissive,
343
+ hasEmissive: false,
344
+ hasTransparency: false
345
+ };
337
346
  }
338
347
  function buildOverlayFallbackTexturePixels() {
339
- const color = new Uint8Array(4);
340
- color[0] = 0;
341
- color[1] = 0;
342
- color[2] = 0;
343
- color[3] = 0;
344
- const emissive = new Uint8Array(4);
345
- emissive[0] = 0;
346
- emissive[1] = 0;
347
- emissive[2] = 0;
348
- emissive[3] = 0;
349
- return {
350
- id: OVERLAY_FALLBACK_TEXTURE_ID,
351
- width: 1,
352
- height: 1,
353
- color,
354
- emissive,
355
- hasEmissive: false,
356
- hasTransparency: true,
357
- };
348
+ const color = new Uint8Array(4);
349
+ color[0] = 0;
350
+ color[1] = 0;
351
+ color[2] = 0;
352
+ color[3] = 0;
353
+ const emissive = new Uint8Array(4);
354
+ emissive[0] = 0;
355
+ emissive[1] = 0;
356
+ emissive[2] = 0;
357
+ emissive[3] = 0;
358
+ return {
359
+ id: OVERLAY_FALLBACK_TEXTURE_ID,
360
+ width: 1,
361
+ height: 1,
362
+ color,
363
+ emissive,
364
+ hasEmissive: false,
365
+ hasTransparency: true
366
+ };
358
367
  }
359
368
  function blitTexture(source, width, height, atlasWidth, target, offsetX, offsetY) {
360
- for (let y = 0; y < height; y += 1) {
361
- for (let x = 0; x < width; x += 1) {
362
- const sx = x;
363
- const sy = y;
364
- const sourceIndex = (sy * width + sx) * 4;
365
- const tx = offsetX + x;
366
- const ty = offsetY + y;
367
- const targetIndex = (ty * atlasWidth + tx) * 4;
368
- target[targetIndex] = source[sourceIndex];
369
- target[targetIndex + 1] = source[sourceIndex + 1];
370
- target[targetIndex + 2] = source[sourceIndex + 2];
371
- target[targetIndex + 3] = source[sourceIndex + 3];
372
- }
373
- }
369
+ for (let y = 0; y < height; y += 1) {
370
+ for (let x = 0; x < width; x += 1) {
371
+ const sx = x;
372
+ const sy = y;
373
+ const sourceIndex = (sy * width + sx) * 4;
374
+ const tx = offsetX + x;
375
+ const ty = offsetY + y;
376
+ const targetIndex = (ty * atlasWidth + tx) * 4;
377
+ target[targetIndex] = source[sourceIndex];
378
+ target[targetIndex + 1] = source[sourceIndex + 1];
379
+ target[targetIndex + 2] = source[sourceIndex + 2];
380
+ target[targetIndex + 3] = source[sourceIndex + 3];
381
+ }
382
+ }
374
383
  }
375
384
  function fillTransparentRegion(target, width, height, atlasWidth, offsetX, offsetY) {
376
- for (let y = 0; y < height; y += 1) {
377
- for (let x = 0; x < width; x += 1) {
378
- const tx = offsetX + x;
379
- const ty = offsetY + y;
380
- const targetIndex = (ty * atlasWidth + tx) * 4;
381
- target[targetIndex] = 0;
382
- target[targetIndex + 1] = 0;
383
- target[targetIndex + 2] = 0;
384
- target[targetIndex + 3] = 0;
385
- }
386
- }
385
+ for (let y = 0; y < height; y += 1) {
386
+ for (let x = 0; x < width; x += 1) {
387
+ const tx = offsetX + x;
388
+ const ty = offsetY + y;
389
+ const targetIndex = (ty * atlasWidth + tx) * 4;
390
+ target[targetIndex] = 0;
391
+ target[targetIndex + 1] = 0;
392
+ target[targetIndex + 2] = 0;
393
+ target[targetIndex + 3] = 0;
394
+ }
395
+ }
387
396
  }
388
397
  function extendEdgePixels(target, width, height, atlasWidth, atlasHeight, offsetX, offsetY) {
389
- const startX = offsetX;
390
- const endX = offsetX + width - 1;
391
- const startY = offsetY;
392
- const endY = offsetY + height - 1;
393
- if (startY > 0) {
394
- for (let x = startX; x <= endX; x += 1) {
395
- copyPixel(target, atlasWidth, x, startY, x, startY - 1);
396
- }
397
- }
398
- if (endY + 1 < atlasHeight) {
399
- for (let x = startX; x <= endX; x += 1) {
400
- copyPixel(target, atlasWidth, x, endY, x, endY + 1);
401
- }
402
- }
403
- if (startX > 0) {
404
- for (let y = startY; y <= endY; y += 1) {
405
- copyPixel(target, atlasWidth, startX, y, startX - 1, y);
406
- }
407
- }
408
- if (endX + 1 < atlasWidth) {
409
- for (let y = startY; y <= endY; y += 1) {
410
- copyPixel(target, atlasWidth, endX, y, endX + 1, y);
411
- }
412
- }
413
- const cornerTargets = [];
414
- if (startX > 0 && startY > 0)
415
- cornerTargets.push({ sx: startX, sy: startY, dx: startX - 1, dy: startY - 1 });
416
- if (startX > 0 && endY + 1 < atlasHeight)
417
- cornerTargets.push({ sx: startX, sy: endY, dx: startX - 1, dy: endY + 1 });
418
- if (endX + 1 < atlasWidth && startY > 0)
419
- cornerTargets.push({ sx: endX, sy: startY, dx: endX + 1, dy: startY - 1 });
420
- if (endX + 1 < atlasWidth && endY + 1 < atlasHeight)
421
- cornerTargets.push({ sx: endX, sy: endY, dx: endX + 1, dy: endY + 1 });
422
- cornerTargets.forEach(({ sx, sy, dx, dy }) => {
423
- copyPixel(target, atlasWidth, sx, sy, dx, dy);
424
- });
398
+ const startX = offsetX;
399
+ const endX = offsetX + width - 1;
400
+ const startY = offsetY;
401
+ const endY = offsetY + height - 1;
402
+ if (startY > 0) {
403
+ for (let x = startX; x <= endX; x += 1) {
404
+ copyPixel(target, atlasWidth, x, startY, x, startY - 1);
405
+ }
406
+ }
407
+ if (endY + 1 < atlasHeight) {
408
+ for (let x = startX; x <= endX; x += 1) {
409
+ copyPixel(target, atlasWidth, x, endY, x, endY + 1);
410
+ }
411
+ }
412
+ if (startX > 0) {
413
+ for (let y = startY; y <= endY; y += 1) {
414
+ copyPixel(target, atlasWidth, startX, y, startX - 1, y);
415
+ }
416
+ }
417
+ if (endX + 1 < atlasWidth) {
418
+ for (let y = startY; y <= endY; y += 1) {
419
+ copyPixel(target, atlasWidth, endX, y, endX + 1, y);
420
+ }
421
+ }
422
+ const cornerTargets = [];
423
+ if (startX > 0 && startY > 0)
424
+ cornerTargets.push({ sx: startX, sy: startY, dx: startX - 1, dy: startY - 1 });
425
+ if (startX > 0 && endY + 1 < atlasHeight)
426
+ cornerTargets.push({ sx: startX, sy: endY, dx: startX - 1, dy: endY + 1 });
427
+ if (endX + 1 < atlasWidth && startY > 0)
428
+ cornerTargets.push({ sx: endX, sy: startY, dx: endX + 1, dy: startY - 1 });
429
+ if (endX + 1 < atlasWidth && endY + 1 < atlasHeight)
430
+ cornerTargets.push({ sx: endX, sy: endY, dx: endX + 1, dy: endY + 1 });
431
+ cornerTargets.forEach(({ sx, sy, dx, dy }) => {
432
+ copyPixel(target, atlasWidth, sx, sy, dx, dy);
433
+ });
425
434
  }
426
435
  function copyPixel(buffer, atlasWidth, sourceX, sourceY, destX, destY) {
427
- const sourceIndex = (sourceY * atlasWidth + sourceX) * 4;
428
- const destIndex = (destY * atlasWidth + destX) * 4;
429
- buffer[destIndex] = buffer[sourceIndex];
430
- buffer[destIndex + 1] = buffer[sourceIndex + 1];
431
- buffer[destIndex + 2] = buffer[sourceIndex + 2];
432
- buffer[destIndex + 3] = buffer[sourceIndex + 3];
436
+ const sourceIndex = (sourceY * atlasWidth + sourceX) * 4;
437
+ const destIndex = (destY * atlasWidth + destX) * 4;
438
+ buffer[destIndex] = buffer[sourceIndex];
439
+ buffer[destIndex + 1] = buffer[sourceIndex + 1];
440
+ buffer[destIndex + 2] = buffer[sourceIndex + 2];
441
+ buffer[destIndex + 3] = buffer[sourceIndex + 3];
433
442
  }
434
443
  function clamp(value, min, max) {
435
- return Math.min(max, Math.max(min, value));
444
+ return Math.min(max, Math.max(min, value));
436
445
  }
437
446
  function hasOverlayTextures(box) {
438
- return Boolean(box.topOverlay ||
439
- box.bottomOverlay ||
440
- box.northOverlay ||
441
- box.southOverlay ||
442
- box.eastOverlay ||
443
- box.westOverlay ||
444
- box.sidesOverlay ||
445
- box.allOverlay);
447
+ return Boolean(box.topOverlay || box.bottomOverlay || box.northOverlay || box.southOverlay || box.eastOverlay || box.westOverlay || box.sidesOverlay || box.allOverlay);
446
448
  }
447
449
  function collectUsedTextureIds(model, primitivesById) {
448
- const textureIds = new Set();
449
- const visitPrimitive = (primitive, visited) => {
450
- if (visited.has(primitive.id))
451
- return;
452
- visited.add(primitive.id);
453
- switch (primitive.kind) {
454
- case 'plain_box':
455
- case 'voxel_box':
456
- break;
457
- case 'textured_box': {
458
- const textured = primitive;
459
- FACE_ORDER.forEach(face => {
460
- const textureId = getFaceTextureId(textured, face);
461
- if (textureId)
462
- textureIds.add(textureId);
463
- });
464
- FACE_ORDER.forEach(face => {
465
- const overlayId = getOverlayTextureId(textured, face);
466
- if (overlayId)
467
- textureIds.add(overlayId);
468
- });
469
- break;
470
- }
471
- case 'combo_box': {
472
- const combo = primitive;
473
- combo.slots.forEach(slot => {
474
- const child = primitivesById.get(slot.primitive);
475
- if (child) {
476
- visitPrimitive(child, new Set(visited));
477
- }
478
- });
479
- break;
480
- }
481
- default:
482
- break;
450
+ const textureIds = /* @__PURE__ */ new Set();
451
+ const visitPrimitive = (primitive, visited) => {
452
+ if (visited.has(primitive.id))
453
+ return;
454
+ visited.add(primitive.id);
455
+ switch (primitive.kind) {
456
+ case "plain_box":
457
+ case "voxel_box":
458
+ break;
459
+ case "textured_box": {
460
+ const textured = primitive;
461
+ import_faces.FACE_ORDER.forEach((face) => {
462
+ const textureId = getFaceTextureId(textured, face);
463
+ if (textureId)
464
+ textureIds.add(textureId);
465
+ });
466
+ import_faces.FACE_ORDER.forEach((face) => {
467
+ const overlayId = getOverlayTextureId(textured, face);
468
+ if (overlayId)
469
+ textureIds.add(overlayId);
470
+ });
471
+ break;
472
+ }
473
+ case "combo_box": {
474
+ const combo = primitive;
475
+ combo.slots.forEach((slot) => {
476
+ const child = primitivesById.get(slot.primitive);
477
+ if (child) {
478
+ visitPrimitive(child, new Set(visited));
479
+ }
480
+ });
481
+ break;
482
+ }
483
+ default:
484
+ break;
485
+ }
486
+ };
487
+ const traverseNode = (node) => {
488
+ if (node.mesh) {
489
+ const primitive = primitivesById.get(node.mesh);
490
+ if (primitive) {
491
+ visitPrimitive(primitive, /* @__PURE__ */ new Set());
492
+ }
493
+ }
494
+ node.children.forEach(traverseNode);
495
+ };
496
+ traverseNode(model.geometry.root);
497
+ return textureIds;
498
+ }
499
+ function buildSceneGraph(context, mode) {
500
+ if (mode === "merged") {
501
+ const scene2 = (0, import_scene.createBoxelScene)({ THREE: context.THREE, entity: context.model });
502
+ const root = buildMergedGroupFromScene(context, scene2);
503
+ scene2.dispose();
504
+ const dispose = () => {
505
+ const geometries = /* @__PURE__ */ new Set();
506
+ const materials = /* @__PURE__ */ new Set();
507
+ root.traverse((object) => {
508
+ if (object instanceof context.THREE.Mesh) {
509
+ const mesh = object;
510
+ if (mesh.geometry)
511
+ geometries.add(mesh.geometry);
512
+ const material = mesh.material;
513
+ if (Array.isArray(material))
514
+ material.forEach((mat) => materials.add(mat));
515
+ else if (material)
516
+ materials.add(material);
483
517
  }
518
+ });
519
+ for (const geometry of geometries) {
520
+ geometry.dispose?.();
521
+ }
522
+ for (const materialEntry of materials) {
523
+ const material = materialEntry;
524
+ material.map?.dispose?.();
525
+ material.emissiveMap?.dispose?.();
526
+ material.dispose?.();
527
+ }
484
528
  };
485
- const traverseNode = (node) => {
486
- if (node.mesh) {
487
- const primitive = primitivesById.get(node.mesh);
488
- if (primitive) {
489
- visitPrimitive(primitive, new Set());
490
- }
491
- }
492
- node.children.forEach(traverseNode);
529
+ return { root, nodeLookup: /* @__PURE__ */ new Map(), animations: [], dispose };
530
+ }
531
+ if (mode === "skinned") {
532
+ const skinned = (0, import_skinned_mesh.buildEntitySkinnedMesh)({ THREE: context.THREE, entity: context.model });
533
+ const root = new context.THREE.Group();
534
+ root.add(skinned.mesh);
535
+ const nodeLookup2 = /* @__PURE__ */ new Map();
536
+ skinned.boneMap.forEach((bone, nodeId) => nodeLookup2.set(nodeId, bone));
537
+ const animations = (0, import_animations.buildAnimations)(context.THREE, context.model, nodeLookup2);
538
+ const dispose = () => {
539
+ skinned.dispose();
493
540
  };
494
- traverseNode(model.geometry.root);
495
- return textureIds;
496
- }
497
- function buildSceneGraph(context, mode) {
498
- if (mode === 'merged') {
499
- const scene = createBoxelScene({ THREE: context.THREE, entity: context.model });
500
- const root = buildMergedGroupFromScene(context, scene);
501
- scene.dispose();
502
- const dispose = () => {
503
- const geometries = new Set();
504
- const materials = new Set();
505
- root.traverse(object => {
506
- if (object instanceof context.THREE.Mesh) {
507
- const mesh = object;
508
- if (mesh.geometry)
509
- geometries.add(mesh.geometry);
510
- const material = mesh.material;
511
- if (Array.isArray(material))
512
- material.forEach(mat => materials.add(mat));
513
- else if (material)
514
- materials.add(material);
515
- }
516
- });
517
- for (const geometry of geometries) {
518
- geometry.dispose?.();
519
- }
520
- for (const materialEntry of materials) {
521
- const material = materialEntry;
522
- material.map?.dispose?.();
523
- material.emissiveMap?.dispose?.();
524
- material.dispose?.();
525
- }
526
- };
527
- return { root, nodeLookup: new Map(), animations: [], dispose };
528
- }
529
- if (mode === 'skinned') {
530
- const skinned = buildEntitySkinnedMesh({ THREE: context.THREE, entity: context.model });
531
- const root = new context.THREE.Group();
532
- root.add(skinned.mesh);
533
- const nodeLookup = new Map();
534
- skinned.boneMap.forEach((bone, nodeId) => nodeLookup.set(nodeId, bone));
535
- const animations = buildAnimations(context.THREE, context.model, nodeLookup);
536
- const dispose = () => {
537
- skinned.dispose();
538
- };
539
- return {
540
- root,
541
- nodeLookup,
542
- animations,
543
- dispose,
544
- };
545
- }
546
- const scene = createBoxelScene({ THREE: context.THREE, entity: context.model });
547
- const nodeLookup = new Map();
548
- scene.nodes.forEach(meta => {
549
- nodeLookup.set(meta.id, meta.object);
550
- });
551
541
  return {
552
- root: scene.root,
553
- nodeLookup,
554
- animations: scene.animations,
555
- dispose: scene.dispose,
542
+ root,
543
+ nodeLookup: nodeLookup2,
544
+ animations,
545
+ dispose
556
546
  };
547
+ }
548
+ const scene = (0, import_scene.createBoxelScene)({ THREE: context.THREE, entity: context.model });
549
+ const nodeLookup = /* @__PURE__ */ new Map();
550
+ scene.nodes.forEach((meta) => {
551
+ nodeLookup.set(meta.id, meta.object);
552
+ });
553
+ return {
554
+ root: scene.root,
555
+ nodeLookup,
556
+ animations: scene.animations,
557
+ dispose: scene.dispose
558
+ };
557
559
  }
558
560
  function resolveAnimationClips(sceneGraph, mode, animationIds) {
559
- if (mode !== 'hierarchical')
560
- return [];
561
- if (animationIds === null)
562
- return [];
563
- if (Array.isArray(animationIds) && animationIds.length === 0)
564
- return [];
565
- const allowed = Array.isArray(animationIds) && animationIds.length > 0
566
- ? new Set(animationIds)
567
- : null;
568
- return sceneGraph.animations
569
- .filter(animation => (allowed ? allowed.has(animation.id) : true))
570
- .map(animation => animation.clip);
561
+ if (mode !== "hierarchical")
562
+ return [];
563
+ if (animationIds === null)
564
+ return [];
565
+ if (Array.isArray(animationIds) && animationIds.length === 0)
566
+ return [];
567
+ const allowed = Array.isArray(animationIds) && animationIds.length > 0 ? new Set(animationIds) : null;
568
+ return sceneGraph.animations.filter((animation) => allowed ? allowed.has(animation.id) : true).map((animation) => animation.clip);
571
569
  }
572
570
  function buildMergedGroupFromScene(context, scene) {
573
- const { THREE, materialsById, primitivesById, atlas, voxelSurfacesByPrimitive } = context;
574
- const group = new THREE.Group();
575
- const texturedAccumulator = atlas ? new MeshAccumulator(true) : null;
576
- const plainBuckets = new Map();
577
- scene.root.updateMatrixWorld(true);
578
- scene.root.traverse(object => {
579
- if (!(object instanceof THREE.Mesh)) {
580
- return;
581
- }
582
- const mesh = object;
583
- const primitiveId = mesh.userData?.primitiveId;
584
- const matrix = mesh.matrixWorld.clone();
585
- const sizeFromUserData = mesh.userData?.size;
586
- if (!primitiveId) {
587
- if (mesh.userData?.placeholder && sizeFromUserData) {
588
- const bucket = ensurePlainBucket({
589
- THREE,
590
- plainBuckets,
591
- options: {
592
- baseColor: DEFAULT_COLOR,
593
- emissive: null,
594
- opacity: DEFAULT_OPACITY,
595
- },
596
- });
597
- appendBoxToAccumulator({
598
- THREE,
599
- accumulator: bucket.accumulator,
600
- size: sizeFromUserData,
601
- matrix,
602
- });
603
- }
604
- return;
571
+ const { THREE, materialsById, primitivesById, atlas, voxelSurfacesByPrimitive } = context;
572
+ const group = new THREE.Group();
573
+ const texturedAccumulator = atlas ? new MeshAccumulator(true) : null;
574
+ const plainBuckets = /* @__PURE__ */ new Map();
575
+ scene.root.updateMatrixWorld(true);
576
+ scene.root.traverse((object) => {
577
+ if (!(object instanceof THREE.Mesh)) {
578
+ return;
579
+ }
580
+ const mesh = object;
581
+ const primitiveId = mesh.userData?.primitiveId;
582
+ const matrix = mesh.matrixWorld.clone();
583
+ const sizeFromUserData = mesh.userData?.size;
584
+ if (!primitiveId) {
585
+ if (mesh.userData?.placeholder && sizeFromUserData) {
586
+ const bucket = ensurePlainBucket({
587
+ THREE,
588
+ plainBuckets,
589
+ options: {
590
+ baseColor: DEFAULT_COLOR,
591
+ emissive: null,
592
+ opacity: DEFAULT_OPACITY
593
+ }
594
+ });
595
+ appendBoxToAccumulator({
596
+ THREE,
597
+ accumulator: bucket.accumulator,
598
+ size: sizeFromUserData,
599
+ matrix
600
+ });
601
+ }
602
+ return;
603
+ }
604
+ const primitive = primitivesById.get(primitiveId);
605
+ if (!primitive) {
606
+ console.warn("[entity-export] primitive not found for merged export", primitiveId);
607
+ return;
608
+ }
609
+ switch (primitive.kind) {
610
+ case "plain_box": {
611
+ const plain = primitive;
612
+ const materialDef = materialsById.get(plain.material);
613
+ const bucket = ensurePlainBucket({
614
+ THREE,
615
+ plainBuckets,
616
+ options: {
617
+ baseColor: materialDef?.baseColor ?? DEFAULT_COLOR,
618
+ emissive: materialDef?.emissiveColor ?? null,
619
+ opacity: materialDef?.opacity ?? 1
620
+ }
621
+ });
622
+ appendBoxToAccumulator({ THREE, accumulator: bucket.accumulator, size: plain.size, matrix });
623
+ break;
624
+ }
625
+ case "textured_box": {
626
+ const box = primitive;
627
+ const isOverlayMesh = mesh.userData?.overlay === true;
628
+ if (!atlas || !texturedAccumulator) {
629
+ if (isOverlayMesh) {
630
+ break;
631
+ }
632
+ const bucket = ensurePlainBucket({
633
+ THREE,
634
+ plainBuckets,
635
+ options: { baseColor: DEFAULT_COLOR, emissive: null, opacity: 1 }
636
+ });
637
+ appendBoxToAccumulator({ THREE, accumulator: bucket.accumulator, size: box.size, matrix });
638
+ break;
605
639
  }
606
- const primitive = primitivesById.get(primitiveId);
607
- if (!primitive) {
608
- console.warn('[entity-export] primitive not found for merged export', primitiveId);
609
- return;
640
+ appendTexturedBoxToAccumulator({
641
+ THREE,
642
+ accumulator: texturedAccumulator,
643
+ atlas,
644
+ box,
645
+ matrix,
646
+ size: isOverlayMesh ? sizeFromUserData ?? box.size : void 0,
647
+ getTextureId: isOverlayMesh ? (candidate, face) => getOverlayTextureId(candidate, face) ?? OVERLAY_FALLBACK_TEXTURE_ID : void 0
648
+ });
649
+ break;
650
+ }
651
+ case "voxel_box": {
652
+ const voxel = primitive;
653
+ if (!voxel.voxels || voxel.voxels.length === 0) {
654
+ break;
610
655
  }
611
- switch (primitive.kind) {
612
- case 'plain_box': {
613
- const plain = primitive;
614
- const materialDef = materialsById.get(plain.material);
615
- const bucket = ensurePlainBucket({
616
- THREE,
617
- plainBuckets,
618
- options: {
619
- baseColor: materialDef?.baseColor ?? DEFAULT_COLOR,
620
- emissive: materialDef?.emissiveColor ?? null,
621
- opacity: materialDef?.opacity ?? 1,
622
- },
623
- });
624
- appendBoxToAccumulator({ THREE, accumulator: bucket.accumulator, size: plain.size, matrix });
625
- break;
626
- }
627
- case 'textured_box': {
628
- const box = primitive;
629
- const isOverlayMesh = mesh.userData?.overlay === true;
630
- if (!atlas || !texturedAccumulator) {
631
- if (isOverlayMesh) {
632
- break;
633
- }
634
- const bucket = ensurePlainBucket({
635
- THREE,
636
- plainBuckets,
637
- options: { baseColor: DEFAULT_COLOR, emissive: null, opacity: 1 },
638
- });
639
- appendBoxToAccumulator({ THREE, accumulator: bucket.accumulator, size: box.size, matrix });
640
- break;
641
- }
642
- appendTexturedBoxToAccumulator({
643
- THREE,
644
- accumulator: texturedAccumulator,
645
- atlas,
646
- box,
647
- matrix,
648
- size: isOverlayMesh ? sizeFromUserData ?? box.size : undefined,
649
- getTextureId: isOverlayMesh
650
- ? (candidate, face) => getOverlayTextureId(candidate, face) ?? OVERLAY_FALLBACK_TEXTURE_ID
651
- : undefined,
652
- });
653
- break;
654
- }
655
- case 'voxel_box': {
656
- const voxel = primitive;
657
- if (!voxel.voxels || voxel.voxels.length === 0) {
658
- break;
659
- }
660
- const surfaces = voxelSurfacesByPrimitive.get(voxel.id);
661
- if (!atlas || !texturedAccumulator || !surfaces || surfaces.length === 0) {
662
- const fallbackSize = voxel.size ?? [1, 1, 1];
663
- const bucket = ensurePlainBucket({
664
- THREE,
665
- plainBuckets,
666
- options: { baseColor: DEFAULT_COLOR, emissive: null, opacity: DEFAULT_OPACITY },
667
- });
668
- appendBoxToAccumulator({ THREE, accumulator: bucket.accumulator, size: fallbackSize, matrix });
669
- break;
670
- }
671
- surfaces.forEach(surface => {
672
- const region = atlas.regions.get(surface.textureId) ?? atlas.fallbackRegion;
673
- const geometry = new THREE.BufferGeometry();
674
- geometry.setAttribute('position', new THREE.Float32BufferAttribute(surface.geometry.positions, 3));
675
- geometry.setAttribute('normal', new THREE.Float32BufferAttribute(surface.geometry.normals, 3));
676
- const baseUVAttr = new THREE.Float32BufferAttribute(surface.geometry.uvs, 2);
677
- geometry.setAttribute('uv', baseUVAttr);
678
- geometry.setIndex(new THREE.BufferAttribute(surface.geometry.indices, 1));
679
- const customUVs = new Float32Array(baseUVAttr.count * 2);
680
- const uSpan = region.uMax - region.uMin;
681
- const vSpan = region.vMax - region.vMin;
682
- for (let i = 0; i < baseUVAttr.count; i += 1) {
683
- const baseU = baseUVAttr.getX(i);
684
- const baseV = baseUVAttr.getY(i);
685
- customUVs[i * 2] = region.uMin + baseU * uSpan;
686
- customUVs[i * 2 + 1] = region.vMin + baseV * vSpan;
687
- }
688
- texturedAccumulator.appendGeometry({ THREE, geometry, matrix, customUVs });
689
- });
690
- break;
691
- }
692
- case 'combo_box':
693
- break;
694
- default:
695
- break;
656
+ const surfaces = voxelSurfacesByPrimitive.get(voxel.id);
657
+ if (!atlas || !texturedAccumulator || !surfaces || surfaces.length === 0) {
658
+ const fallbackSize = voxel.size ?? [1, 1, 1];
659
+ const bucket = ensurePlainBucket({
660
+ THREE,
661
+ plainBuckets,
662
+ options: { baseColor: DEFAULT_COLOR, emissive: null, opacity: DEFAULT_OPACITY }
663
+ });
664
+ appendBoxToAccumulator({ THREE, accumulator: bucket.accumulator, size: fallbackSize, matrix });
665
+ break;
696
666
  }
697
- });
698
- if (texturedAccumulator && texturedAccumulator.hasGeometry()) {
699
- const geometry = texturedAccumulator.build(THREE);
700
- const material = createTexturedMaterial(THREE, atlas);
701
- const mesh = new THREE.Mesh(geometry, material);
702
- group.add(mesh);
703
- }
704
- for (const bucket of plainBuckets.values()) {
705
- if (!bucket.accumulator.hasGeometry())
706
- continue;
707
- const geometry = bucket.accumulator.build(THREE);
708
- const mesh = new THREE.Mesh(geometry, bucket.material);
709
- group.add(mesh);
710
- }
711
- return group;
667
+ surfaces.forEach((surface) => {
668
+ const region = atlas.regions.get(surface.textureId) ?? atlas.fallbackRegion;
669
+ const geometry = new THREE.BufferGeometry();
670
+ geometry.setAttribute("position", new THREE.Float32BufferAttribute(surface.geometry.positions, 3));
671
+ geometry.setAttribute("normal", new THREE.Float32BufferAttribute(surface.geometry.normals, 3));
672
+ const baseUVAttr = new THREE.Float32BufferAttribute(surface.geometry.uvs, 2);
673
+ geometry.setAttribute("uv", baseUVAttr);
674
+ geometry.setIndex(new THREE.BufferAttribute(surface.geometry.indices, 1));
675
+ const customUVs = new Float32Array(baseUVAttr.count * 2);
676
+ const uSpan = region.uMax - region.uMin;
677
+ const vSpan = region.vMax - region.vMin;
678
+ for (let i = 0; i < baseUVAttr.count; i += 1) {
679
+ const baseU = baseUVAttr.getX(i);
680
+ const baseV = baseUVAttr.getY(i);
681
+ customUVs[i * 2] = region.uMin + baseU * uSpan;
682
+ customUVs[i * 2 + 1] = region.vMin + baseV * vSpan;
683
+ }
684
+ texturedAccumulator.appendGeometry({ THREE, geometry, matrix, customUVs });
685
+ });
686
+ break;
687
+ }
688
+ case "combo_box":
689
+ break;
690
+ default:
691
+ break;
692
+ }
693
+ });
694
+ if (texturedAccumulator && texturedAccumulator.hasGeometry()) {
695
+ const geometry = texturedAccumulator.build(THREE);
696
+ const material = createTexturedMaterial(THREE, atlas);
697
+ const mesh = new THREE.Mesh(geometry, material);
698
+ group.add(mesh);
699
+ }
700
+ for (const bucket of plainBuckets.values()) {
701
+ if (!bucket.accumulator.hasGeometry())
702
+ continue;
703
+ const geometry = bucket.accumulator.build(THREE);
704
+ const mesh = new THREE.Mesh(geometry, bucket.material);
705
+ group.add(mesh);
706
+ }
707
+ return group;
712
708
  }
713
709
  function appendTexturedBoxToAccumulator(options) {
714
- const { THREE, accumulator, atlas, box, matrix } = options;
715
- const size = options.size ?? box.size;
716
- const textureResolver = options.getTextureId ?? getFaceTextureId;
717
- const geometry = createBoxGeometry(THREE, size);
718
- const uvAttr = geometry.getAttribute('uv');
719
- const customUVs = new Float32Array(uvAttr.count * 2);
720
- customUVs.set(uvAttr.array);
721
- const faceVertexSets = collectGroupVertexIndices(geometry);
722
- if (faceVertexSets.length === 0) {
723
- geometry.dispose();
724
- return;
725
- }
726
- FACE_ORDER.forEach((face, faceIndex) => {
727
- const resolvedId = textureResolver(box, face);
728
- const region = atlas.regions.get(resolvedId ?? FALLBACK_TEXTURE_ID) ?? atlas.fallbackRegion;
729
- const vertexIndices = faceVertexSets[faceIndex];
730
- if (!vertexIndices || vertexIndices.size === 0)
731
- return;
732
- const uSpan = region.uMax - region.uMin;
733
- const vSpan = region.vMax - region.vMin;
734
- vertexIndices.forEach(vertexIndex => {
735
- const baseU = uvAttr.getX(vertexIndex);
736
- const baseV = uvAttr.getY(vertexIndex);
737
- customUVs[vertexIndex * 2] = region.uMin + baseU * uSpan;
738
- customUVs[vertexIndex * 2 + 1] = region.vMin + baseV * vSpan;
739
- });
710
+ const { THREE, accumulator, atlas, box, matrix } = options;
711
+ const size = options.size ?? box.size;
712
+ const textureResolver = options.getTextureId ?? getFaceTextureId;
713
+ const geometry = (0, import_primitives.createBoxGeometry)(THREE, size);
714
+ const uvAttr = geometry.getAttribute("uv");
715
+ const customUVs = new Float32Array(uvAttr.count * 2);
716
+ customUVs.set(uvAttr.array);
717
+ const faceVertexSets = (0, import_primitives.collectGroupVertexIndices)(geometry);
718
+ if (faceVertexSets.length === 0) {
719
+ geometry.dispose();
720
+ return;
721
+ }
722
+ import_faces.FACE_ORDER.forEach((face, faceIndex) => {
723
+ const resolvedId = textureResolver(box, face);
724
+ const region = atlas.regions.get(resolvedId ?? FALLBACK_TEXTURE_ID) ?? atlas.fallbackRegion;
725
+ const vertexIndices = faceVertexSets[faceIndex];
726
+ if (!vertexIndices || vertexIndices.size === 0)
727
+ return;
728
+ const uSpan = region.uMax - region.uMin;
729
+ const vSpan = region.vMax - region.vMin;
730
+ vertexIndices.forEach((vertexIndex) => {
731
+ const baseU = uvAttr.getX(vertexIndex);
732
+ const baseV = uvAttr.getY(vertexIndex);
733
+ customUVs[vertexIndex * 2] = region.uMin + baseU * uSpan;
734
+ customUVs[vertexIndex * 2 + 1] = region.vMin + baseV * vSpan;
740
735
  });
741
- accumulator.appendGeometry({ THREE, geometry, matrix, customUVs });
736
+ });
737
+ accumulator.appendGeometry({ THREE, geometry, matrix, customUVs });
742
738
  }
743
739
  function appendBoxToAccumulator(options) {
744
- const { THREE, accumulator, size, matrix } = options;
745
- const geometry = createBoxGeometry(THREE, size);
746
- accumulator.appendGeometry({ THREE, geometry, matrix });
740
+ const { THREE, accumulator, size, matrix } = options;
741
+ const geometry = (0, import_primitives.createBoxGeometry)(THREE, size);
742
+ accumulator.appendGeometry({ THREE, geometry, matrix });
747
743
  }
748
744
  function getFaceTextureId(box, face) {
749
- switch (face) {
750
- case 'top':
751
- return box.top ?? box.all ?? box.sides;
752
- case 'bottom':
753
- return box.bottom ?? box.all ?? box.sides;
754
- case 'north':
755
- return box.north ?? box.sides ?? box.all;
756
- case 'south':
757
- return box.south ?? box.sides ?? box.all;
758
- case 'east':
759
- return box.east ?? box.sides ?? box.all;
760
- case 'west':
761
- return box.west ?? box.sides ?? box.all;
762
- default:
763
- return box.all;
764
- }
745
+ switch (face) {
746
+ case "top":
747
+ return box.top ?? box.all ?? box.sides;
748
+ case "bottom":
749
+ return box.bottom ?? box.all ?? box.sides;
750
+ case "north":
751
+ return box.north ?? box.sides ?? box.all;
752
+ case "south":
753
+ return box.south ?? box.sides ?? box.all;
754
+ case "east":
755
+ return box.east ?? box.sides ?? box.all;
756
+ case "west":
757
+ return box.west ?? box.sides ?? box.all;
758
+ default:
759
+ return box.all;
760
+ }
765
761
  }
766
762
  function getOverlayTextureId(box, face) {
767
- switch (face) {
768
- case 'top':
769
- return box.topOverlay ?? box.allOverlay ?? box.sidesOverlay;
770
- case 'bottom':
771
- return box.bottomOverlay ?? box.allOverlay ?? box.sidesOverlay;
772
- case 'north':
773
- return box.northOverlay ?? box.sidesOverlay ?? box.allOverlay;
774
- case 'south':
775
- return box.southOverlay ?? box.sidesOverlay ?? box.allOverlay;
776
- case 'east':
777
- return box.eastOverlay ?? box.sidesOverlay ?? box.allOverlay;
778
- case 'west':
779
- return box.westOverlay ?? box.sidesOverlay ?? box.allOverlay;
780
- default:
781
- return box.allOverlay ?? box.sidesOverlay;
782
- }
763
+ switch (face) {
764
+ case "top":
765
+ return box.topOverlay ?? box.allOverlay ?? box.sidesOverlay;
766
+ case "bottom":
767
+ return box.bottomOverlay ?? box.allOverlay ?? box.sidesOverlay;
768
+ case "north":
769
+ return box.northOverlay ?? box.sidesOverlay ?? box.allOverlay;
770
+ case "south":
771
+ return box.southOverlay ?? box.sidesOverlay ?? box.allOverlay;
772
+ case "east":
773
+ return box.eastOverlay ?? box.sidesOverlay ?? box.allOverlay;
774
+ case "west":
775
+ return box.westOverlay ?? box.sidesOverlay ?? box.allOverlay;
776
+ default:
777
+ return box.allOverlay ?? box.sidesOverlay;
778
+ }
783
779
  }
784
780
  function createTexturedMaterial(THREE, atlas) {
785
- const params = {
786
- map: atlas.map,
787
- metalness: 0,
788
- roughness: 1,
789
- transparent: atlas.hasTransparency,
790
- };
791
- if (atlas.emissiveMap) {
792
- params.emissiveMap = atlas.emissiveMap;
793
- }
794
- const material = new THREE.MeshStandardMaterial(params);
795
- if (atlas.emissiveMap) {
796
- material.emissive = new THREE.Color(1, 1, 1);
797
- }
798
- return material;
781
+ const params = {
782
+ map: atlas.map,
783
+ metalness: 0,
784
+ roughness: 1,
785
+ transparent: atlas.hasTransparency
786
+ };
787
+ if (atlas.emissiveMap) {
788
+ params.emissiveMap = atlas.emissiveMap;
789
+ }
790
+ const material = new THREE.MeshStandardMaterial(params);
791
+ if (atlas.emissiveMap) {
792
+ material.emissive = new THREE.Color(1, 1, 1);
793
+ }
794
+ return material;
799
795
  }
800
796
  function createPlainMaterial(THREE, options) {
801
- const { baseColor, emissive, opacity } = options;
802
- const material = new THREE.MeshStandardMaterial({
803
- color: new THREE.Color(baseColor[0] / 255, baseColor[1] / 255, baseColor[2] / 255),
804
- metalness: 0,
805
- roughness: 1,
806
- transparent: opacity < 1,
807
- opacity,
808
- });
809
- if (emissive) {
810
- material.emissive = new THREE.Color(emissive[0] / 255, emissive[1] / 255, emissive[2] / 255);
811
- }
812
- material.userData = material.userData ?? {};
813
- material.userData.gltfExtras = {
814
- playdropBaseColor: baseColor,
815
- playdropOpacity: opacity,
816
- };
817
- return material;
797
+ const { baseColor, emissive, opacity } = options;
798
+ const material = new THREE.MeshStandardMaterial({
799
+ color: new THREE.Color(baseColor[0] / 255, baseColor[1] / 255, baseColor[2] / 255),
800
+ metalness: 0,
801
+ roughness: 1,
802
+ transparent: opacity < 1,
803
+ opacity
804
+ });
805
+ if (emissive) {
806
+ material.emissive = new THREE.Color(emissive[0] / 255, emissive[1] / 255, emissive[2] / 255);
807
+ }
808
+ material.userData = material.userData ?? {};
809
+ material.userData.gltfExtras = {
810
+ playdropBaseColor: baseColor,
811
+ playdropOpacity: opacity
812
+ };
813
+ return material;
818
814
  }
819
815
  function getPlainMaterial(context, options) {
820
- const key = plainMaterialKey(options);
821
- const cached = context.plainMaterialCache.get(key);
822
- if (cached) {
823
- return cached;
824
- }
825
- const material = createPlainMaterial(context.THREE, options);
826
- context.plainMaterialCache.set(key, material);
827
- return material;
816
+ const key = plainMaterialKey(options);
817
+ const cached = context.plainMaterialCache.get(key);
818
+ if (cached) {
819
+ return cached;
820
+ }
821
+ const material = createPlainMaterial(context.THREE, options);
822
+ context.plainMaterialCache.set(key, material);
823
+ return material;
828
824
  }
829
825
  function ensurePlainBucket({ THREE, plainBuckets, options }) {
830
- const key = plainMaterialKey(options);
831
- let bucket = plainBuckets.get(key);
832
- if (!bucket) {
833
- bucket = { accumulator: new MeshAccumulator(false), material: createPlainMaterial(THREE, options) };
834
- plainBuckets.set(key, bucket);
835
- }
836
- return bucket;
826
+ const key = plainMaterialKey(options);
827
+ let bucket = plainBuckets.get(key);
828
+ if (!bucket) {
829
+ bucket = { accumulator: new MeshAccumulator(false), material: createPlainMaterial(THREE, options) };
830
+ plainBuckets.set(key, bucket);
831
+ }
832
+ return bucket;
837
833
  }
838
834
  function plainMaterialKey(options) {
839
- const base = `${options.baseColor[0]}-${options.baseColor[1]}-${options.baseColor[2]}`;
840
- const emissive = options.emissive ? `${options.emissive[0]}-${options.emissive[1]}-${options.emissive[2]}` : 'none';
841
- const opacity = options.opacity.toFixed(4);
842
- return `plain|${base}|${emissive}|${opacity}`;
835
+ const base = `${options.baseColor[0]}-${options.baseColor[1]}-${options.baseColor[2]}`;
836
+ const emissive = options.emissive ? `${options.emissive[0]}-${options.emissive[1]}-${options.emissive[2]}` : "none";
837
+ const opacity = options.opacity.toFixed(4);
838
+ return `plain|${base}|${emissive}|${opacity}`;
843
839
  }
844
840
  class MeshAccumulator {
845
- constructor(withUVs) {
846
- this.positions = [];
847
- this.normals = [];
848
- this.indices = [];
849
- this.vertexCount = 0;
850
- this.uvs = withUVs ? [] : null;
851
- }
852
- appendGeometry(options) {
853
- const { THREE, geometry, matrix, customUVs } = options;
854
- const rawPositionAttr = geometry.getAttribute('position');
855
- const rawNormalAttr = geometry.getAttribute('normal');
856
- const rawUvAttr = geometry.getAttribute('uv');
857
- const indexAttr = geometry.index;
858
- if (!rawPositionAttr || !rawNormalAttr || !indexAttr) {
859
- geometry.dispose();
860
- return;
861
- }
862
- if (!(rawPositionAttr instanceof THREE.BufferAttribute) || !(rawNormalAttr instanceof THREE.BufferAttribute)) {
863
- geometry.dispose();
864
- return;
865
- }
866
- const positionAttr = rawPositionAttr;
867
- const normalAttr = rawNormalAttr;
868
- const uvAttr = rawUvAttr && rawUvAttr instanceof THREE.BufferAttribute
869
- ? rawUvAttr
870
- : null;
871
- if (!(indexAttr instanceof THREE.BufferAttribute)) {
872
- geometry.dispose();
873
- return;
841
+ constructor(withUVs) {
842
+ this.positions = [];
843
+ this.normals = [];
844
+ this.indices = [];
845
+ this.vertexCount = 0;
846
+ this.uvs = withUVs ? [] : null;
847
+ }
848
+ appendGeometry(options) {
849
+ const { THREE, geometry, matrix, customUVs } = options;
850
+ const rawPositionAttr = geometry.getAttribute("position");
851
+ const rawNormalAttr = geometry.getAttribute("normal");
852
+ const rawUvAttr = geometry.getAttribute("uv");
853
+ const indexAttr = geometry.index;
854
+ if (!rawPositionAttr || !rawNormalAttr || !indexAttr) {
855
+ geometry.dispose();
856
+ return;
857
+ }
858
+ if (!(rawPositionAttr instanceof THREE.BufferAttribute) || !(rawNormalAttr instanceof THREE.BufferAttribute)) {
859
+ geometry.dispose();
860
+ return;
861
+ }
862
+ const positionAttr = rawPositionAttr;
863
+ const normalAttr = rawNormalAttr;
864
+ const uvAttr = rawUvAttr && rawUvAttr instanceof THREE.BufferAttribute ? rawUvAttr : null;
865
+ if (!(indexAttr instanceof THREE.BufferAttribute)) {
866
+ geometry.dispose();
867
+ return;
868
+ }
869
+ const vertex = new THREE.Vector3();
870
+ const normal = new THREE.Vector3();
871
+ const uv = new THREE.Vector2();
872
+ const normalMatrix = new THREE.Matrix3().getNormalMatrix(matrix);
873
+ for (let i = 0; i < positionAttr.count; i += 1) {
874
+ vertex.fromBufferAttribute(positionAttr, i);
875
+ vertex.applyMatrix4(matrix);
876
+ this.positions.push(vertex.x, vertex.y, vertex.z);
877
+ normal.fromBufferAttribute(normalAttr, i);
878
+ normal.applyMatrix3(normalMatrix).normalize();
879
+ this.normals.push(normal.x, normal.y, normal.z);
880
+ if (this.uvs) {
881
+ if (customUVs) {
882
+ this.uvs.push(customUVs[i * 2], customUVs[i * 2 + 1]);
883
+ } else if (uvAttr) {
884
+ uv.fromBufferAttribute(uvAttr, i);
885
+ this.uvs.push(uv.x, uv.y);
886
+ } else {
887
+ this.uvs.push(0, 0);
874
888
  }
875
- const vertex = new THREE.Vector3();
876
- const normal = new THREE.Vector3();
877
- const uv = new THREE.Vector2();
878
- const normalMatrix = new THREE.Matrix3().getNormalMatrix(matrix);
879
- for (let i = 0; i < positionAttr.count; i += 1) {
880
- vertex.fromBufferAttribute(positionAttr, i);
881
- vertex.applyMatrix4(matrix);
882
- this.positions.push(vertex.x, vertex.y, vertex.z);
883
- normal.fromBufferAttribute(normalAttr, i);
884
- normal.applyMatrix3(normalMatrix).normalize();
885
- this.normals.push(normal.x, normal.y, normal.z);
886
- if (this.uvs) {
887
- if (customUVs) {
888
- this.uvs.push(customUVs[i * 2], customUVs[i * 2 + 1]);
889
- }
890
- else if (uvAttr) {
891
- uv.fromBufferAttribute(uvAttr, i);
892
- this.uvs.push(uv.x, uv.y);
893
- }
894
- else {
895
- this.uvs.push(0, 0);
896
- }
897
- }
898
- }
899
- const typedIndexAttr = indexAttr;
900
- const indexArray = typedIndexAttr.array;
901
- for (let i = 0; i < typedIndexAttr.count; i += 1) {
902
- this.indices.push(this.vertexCount + indexArray[i]);
903
- }
904
- this.vertexCount += positionAttr.count;
905
- geometry.dispose();
906
- }
907
- hasGeometry() {
908
- return this.positions.length > 0;
909
- }
910
- build(THREE) {
911
- const geometry = new THREE.BufferGeometry();
912
- geometry.setAttribute('position', new THREE.Float32BufferAttribute(this.positions, 3));
913
- geometry.setAttribute('normal', new THREE.Float32BufferAttribute(this.normals, 3));
914
- if (this.uvs) {
915
- geometry.setAttribute('uv', new THREE.Float32BufferAttribute(this.uvs, 2));
916
- }
917
- const indexArray = this.vertexCount > 65535 ? new Uint32Array(this.indices) : new Uint16Array(this.indices);
918
- geometry.setIndex(new THREE.BufferAttribute(indexArray, 1));
919
- geometry.computeBoundingBox();
920
- geometry.computeBoundingSphere();
921
- return geometry;
922
- }
889
+ }
890
+ }
891
+ const typedIndexAttr = indexAttr;
892
+ const indexArray = typedIndexAttr.array;
893
+ for (let i = 0; i < typedIndexAttr.count; i += 1) {
894
+ this.indices.push(this.vertexCount + indexArray[i]);
895
+ }
896
+ this.vertexCount += positionAttr.count;
897
+ geometry.dispose();
898
+ }
899
+ hasGeometry() {
900
+ return this.positions.length > 0;
901
+ }
902
+ build(THREE) {
903
+ const geometry = new THREE.BufferGeometry();
904
+ geometry.setAttribute("position", new THREE.Float32BufferAttribute(this.positions, 3));
905
+ geometry.setAttribute("normal", new THREE.Float32BufferAttribute(this.normals, 3));
906
+ if (this.uvs) {
907
+ geometry.setAttribute("uv", new THREE.Float32BufferAttribute(this.uvs, 2));
908
+ }
909
+ const indexArray = this.vertexCount > 65535 ? new Uint32Array(this.indices) : new Uint16Array(this.indices);
910
+ geometry.setIndex(new THREE.BufferAttribute(indexArray, 1));
911
+ geometry.computeBoundingBox();
912
+ geometry.computeBoundingSphere();
913
+ return geometry;
914
+ }
923
915
  }
924
- export const __testing = {
925
- buildTextureAtlas,
926
- collectUsedTextureIds,
927
- MeshAccumulator,
928
- createExportContext,
929
- buildSceneGraph,
930
- resolveAnimationClips,
916
+ const __testing = {
917
+ buildTextureAtlas,
918
+ collectUsedTextureIds,
919
+ MeshAccumulator,
920
+ createExportContext,
921
+ buildSceneGraph,
922
+ resolveAnimationClips
931
923
  };
924
+ // Annotate the CommonJS export names for ESM import in node:
925
+ 0 && (module.exports = {
926
+ __testing,
927
+ createBoxelSceneGraph,
928
+ exportModelToGLB
929
+ });