@treasuryspatial/viewer-kit 0.2.44 → 0.2.50

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 (69) hide show
  1. package/dist/autoGenerate.d.ts +1 -0
  2. package/dist/autoGenerate.d.ts.map +1 -1
  3. package/dist/autoGenerate.js +10 -2
  4. package/dist/camera.d.ts +1 -0
  5. package/dist/camera.d.ts.map +1 -1
  6. package/dist/camera.js +8 -1
  7. package/dist/engine/ViewerEngine.d.ts +20 -0
  8. package/dist/engine/ViewerEngine.d.ts.map +1 -1
  9. package/dist/engine/ViewerEngine.js +254 -63
  10. package/dist/engine/types.d.ts +69 -3
  11. package/dist/engine/types.d.ts.map +1 -1
  12. package/dist/exports/download.d.ts +6 -0
  13. package/dist/exports/download.d.ts.map +1 -1
  14. package/dist/exports/download.js +142 -0
  15. package/dist/exports/three-export.d.ts +4 -1
  16. package/dist/exports/three-export.d.ts.map +1 -1
  17. package/dist/exports/three-export.js +138 -4
  18. package/dist/index.d.ts +13 -9
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.js +5 -4
  21. package/dist/materials/architectural.d.ts +3 -3
  22. package/dist/materials/architectural.d.ts.map +1 -1
  23. package/dist/materials/architectural.js +295 -37
  24. package/dist/materials/catalogue-data.d.ts +15 -0
  25. package/dist/materials/catalogue-data.d.ts.map +1 -1
  26. package/dist/materials/catalogue-data.js +16 -0
  27. package/dist/materials/presets.d.ts +15 -8
  28. package/dist/materials/presets.d.ts.map +1 -1
  29. package/dist/materials/presets.js +140 -39
  30. package/dist/materials/resolve.d.ts +42 -0
  31. package/dist/materials/resolve.d.ts.map +1 -1
  32. package/dist/materials/resolve.js +228 -13
  33. package/dist/materials/types.d.ts +32 -3
  34. package/dist/materials/types.d.ts.map +1 -1
  35. package/dist/presets/defaults.d.ts.map +1 -1
  36. package/dist/presets/defaults.js +17 -1
  37. package/dist/presets/sciencePresets.d.ts.map +1 -1
  38. package/dist/presets/sciencePresets.js +167 -50
  39. package/dist/scene.d.ts +28 -4
  40. package/dist/scene.d.ts.map +1 -1
  41. package/dist/scene.js +196 -31
  42. package/dist/sceneSemanticRegistry.d.ts +64 -0
  43. package/dist/sceneSemanticRegistry.d.ts.map +1 -0
  44. package/dist/sceneSemanticRegistry.js +199 -0
  45. package/dist/sky/scienceSky.d.ts.map +1 -1
  46. package/dist/sky/scienceSky.js +16 -0
  47. package/dist/systems/debugSystem.d.ts +24 -1
  48. package/dist/systems/debugSystem.d.ts.map +1 -1
  49. package/dist/systems/debugSystem.js +324 -77
  50. package/dist/systems/environmentSystem.d.ts +5 -4
  51. package/dist/systems/environmentSystem.d.ts.map +1 -1
  52. package/dist/systems/environmentSystem.js +138 -62
  53. package/dist/systems/lightingSystem.d.ts +10 -1
  54. package/dist/systems/lightingSystem.d.ts.map +1 -1
  55. package/dist/systems/lightingSystem.js +118 -17
  56. package/dist/systems/postfxSystem.d.ts +5 -1
  57. package/dist/systems/postfxSystem.d.ts.map +1 -1
  58. package/dist/systems/postfxSystem.js +84 -1
  59. package/dist/systems/rendererSystem.d.ts.map +1 -1
  60. package/dist/systems/rendererSystem.js +1 -0
  61. package/dist/tsconfig.tsbuildinfo +1 -1
  62. package/dist/uploads/geometry3dm.d.ts.map +1 -1
  63. package/dist/uploads/geometry3dm.js +1 -0
  64. package/dist/uploads/grasshopper.d.ts.map +1 -1
  65. package/dist/uploads/grasshopper.js +63 -5
  66. package/dist/uploads/mesh.js +5 -5
  67. package/dist/uploads/types.d.ts +4 -0
  68. package/dist/uploads/types.d.ts.map +1 -1
  69. package/package.json +3 -3
package/dist/scene.js CHANGED
@@ -8,15 +8,164 @@ export const DEFAULT_CAMERA_RIG = {
8
8
  direction: [1, 0.7, 1],
9
9
  mode: "iso",
10
10
  };
11
+ const UNIT_TO_METERS = {
12
+ mm: 0.001,
13
+ millimeter: 0.001,
14
+ millimeters: 0.001,
15
+ cm: 0.01,
16
+ centimeter: 0.01,
17
+ centimeters: 0.01,
18
+ m: 1,
19
+ meter: 1,
20
+ meters: 1,
21
+ ft: 0.3048,
22
+ foot: 0.3048,
23
+ feet: 0.3048,
24
+ in: 0.0254,
25
+ inch: 0.0254,
26
+ inches: 0.0254,
27
+ };
28
+ const DEFAULT_SCENE_CLIPPING = {
29
+ nearSafety: 1.6,
30
+ farSafety: 1.15,
31
+ minimumFar: 350,
32
+ };
33
+ const PLAN_PRESENTATION_ELEVATION = 0.012;
34
+ export const resolveSceneUnitScale = (units) => {
35
+ const normalized = units?.trim().toLowerCase();
36
+ if (!normalized)
37
+ return 1;
38
+ return UNIT_TO_METERS[normalized] ?? 1;
39
+ };
40
+ export const applyUnitScaleToGroup = (group, units) => {
41
+ if (!group)
42
+ return 1;
43
+ const nextScale = resolveSceneUnitScale(units);
44
+ if (!Number.isFinite(nextScale) || nextScale <= 0)
45
+ return 1;
46
+ const currentScale = typeof group.userData?.__sceneUnitScale === "number" && Number.isFinite(group.userData.__sceneUnitScale)
47
+ ? group.userData.__sceneUnitScale
48
+ : 1;
49
+ if (Math.abs(currentScale - nextScale) < 1e-9)
50
+ return nextScale;
51
+ const ratio = nextScale / currentScale;
52
+ if (!Number.isFinite(ratio) || ratio <= 0)
53
+ return currentScale;
54
+ group.scale.multiplyScalar(ratio);
55
+ group.updateMatrixWorld(true);
56
+ group.userData = {
57
+ ...(group.userData ?? {}),
58
+ __sceneUnitScale: nextScale,
59
+ __sceneUnits: units ?? null,
60
+ };
61
+ return nextScale;
62
+ };
63
+ const resolveCameraKind = (handle, requested) => requested ?? handle.getCameraState().kind ?? "perspective";
64
+ const computeClipRange = (distance, radius, clipping) => {
65
+ const resolved = { ...DEFAULT_SCENE_CLIPPING, ...(clipping ?? {}) };
66
+ let near = Math.max(0.01, distance - radius * resolved.nearSafety);
67
+ const maxNear = distance * 0.1;
68
+ if (Number.isFinite(maxNear) && maxNear > 0) {
69
+ near = Math.min(near, maxNear);
70
+ }
71
+ const dynamicMinimumFar = Math.max(1, distance * 1.5, radius * 6);
72
+ const minimumFar = Math.min(resolved.minimumFar, dynamicMinimumFar);
73
+ const far = Math.max(distance + radius * resolved.farSafety, near + Math.max(radius * 2, 1), minimumFar);
74
+ return { near, far };
75
+ };
76
+ const applyPlanPresentationStyling = (group) => {
77
+ if (!group || typeof group.traverse !== "function")
78
+ return;
79
+ const currentElevation = typeof group.userData?.__planElevation === "number" && Number.isFinite(group.userData.__planElevation)
80
+ ? group.userData.__planElevation
81
+ : 0;
82
+ const delta = PLAN_PRESENTATION_ELEVATION - currentElevation;
83
+ if (Math.abs(delta) > 1e-9) {
84
+ group.position.y += delta;
85
+ group.userData = {
86
+ ...(group.userData ?? {}),
87
+ __planElevation: PLAN_PRESENTATION_ELEVATION,
88
+ };
89
+ }
90
+ group.traverse((child) => {
91
+ if (child instanceof THREE.Mesh) {
92
+ child.renderOrder = 1;
93
+ child.frustumCulled = false;
94
+ const materials = Array.isArray(child.material) ? child.material : [child.material];
95
+ materials.forEach((material) => {
96
+ if (!material)
97
+ return;
98
+ if ("color" in material && material.color) {
99
+ material.color.setHex(0x2f855a);
100
+ }
101
+ if ("roughness" in material) {
102
+ material.roughness = 1;
103
+ }
104
+ if ("metalness" in material) {
105
+ material.metalness = 0;
106
+ }
107
+ if ("emissive" in material && material.emissive) {
108
+ material.emissive.setHex(0x1f6f49);
109
+ }
110
+ if ("emissiveIntensity" in material) {
111
+ material.emissiveIntensity = 0.18;
112
+ }
113
+ material.transparent = true;
114
+ material.opacity = Math.min(material.opacity ?? 1, 0.9);
115
+ material.depthWrite = false;
116
+ material.toneMapped = false;
117
+ material.side = THREE.DoubleSide;
118
+ material.polygonOffset = true;
119
+ material.polygonOffsetFactor = 1.5;
120
+ material.polygonOffsetUnits = 2;
121
+ material.needsUpdate = true;
122
+ });
123
+ return;
124
+ }
125
+ if (child instanceof THREE.Line) {
126
+ child.renderOrder = 200;
127
+ child.frustumCulled = false;
128
+ const material = child.material;
129
+ if (material instanceof THREE.LineBasicMaterial) {
130
+ material.color.setHex(0x1f1f1f);
131
+ material.transparent = true;
132
+ material.opacity = 1;
133
+ material.depthTest = false;
134
+ material.depthWrite = false;
135
+ material.toneMapped = false;
136
+ material.needsUpdate = true;
137
+ }
138
+ return;
139
+ }
140
+ if (child instanceof THREE.Points) {
141
+ child.renderOrder = 220;
142
+ child.frustumCulled = false;
143
+ const material = child.material;
144
+ if (material instanceof THREE.PointsMaterial) {
145
+ material.color.setHex(0x1f1f1f);
146
+ material.size = Math.max(material.size ?? 0, 10);
147
+ material.sizeAttenuation = false;
148
+ material.depthTest = false;
149
+ material.depthWrite = false;
150
+ material.toneMapped = false;
151
+ material.needsUpdate = true;
152
+ }
153
+ }
154
+ });
155
+ };
11
156
  export function computeSceneStats(root) {
12
157
  let meshCount = 0;
13
158
  let vertexCount = 0;
14
159
  let triangleCount = 0;
160
+ let shadowCastingMeshCount = 0;
161
+ let shadowReceivingMeshCount = 0;
15
162
  if (!root || typeof root.traverse !== "function") {
16
163
  return {
17
164
  meshCount,
18
165
  vertexCount,
19
166
  triangleCount,
167
+ shadowCastingMeshCount,
168
+ shadowReceivingMeshCount,
20
169
  size: { x: 0, y: 0, z: 0 },
21
170
  };
22
171
  }
@@ -24,6 +173,10 @@ export function computeSceneStats(root) {
24
173
  const mesh = child;
25
174
  if (mesh.isMesh) {
26
175
  meshCount += 1;
176
+ if (mesh.castShadow)
177
+ shadowCastingMeshCount += 1;
178
+ if (mesh.receiveShadow)
179
+ shadowReceivingMeshCount += 1;
27
180
  const geometry = mesh.geometry;
28
181
  const position = geometry.getAttribute("position");
29
182
  if (position) {
@@ -45,12 +198,15 @@ export function computeSceneStats(root) {
45
198
  meshCount,
46
199
  vertexCount,
47
200
  triangleCount: Math.round(triangleCount),
201
+ shadowCastingMeshCount,
202
+ shadowReceivingMeshCount,
48
203
  size: { x: size.x, y: size.y, z: size.z },
49
204
  };
50
205
  }
51
- export const setCameraToBounds = (handle, bounds, config) => {
206
+ export const setCameraToBounds = (handle, bounds, config, options) => {
52
207
  if (bounds.isEmpty())
53
208
  return;
209
+ const cameraKind = resolveCameraKind(handle, options?.kind);
54
210
  const fov = handle.getCameraState().fov ?? 60;
55
211
  const { position, target } = computeCameraFrame(bounds, fov, config);
56
212
  if (![position.x, position.y, position.z, target.x, target.y, target.z].every(Number.isFinite)) {
@@ -59,46 +215,41 @@ export const setCameraToBounds = (handle, bounds, config) => {
59
215
  const sphere = new THREE.Sphere();
60
216
  bounds.getBoundingSphere(sphere);
61
217
  const distance = position.distanceTo(target);
62
- const safetyFar = 1.15;
63
- const safetyNear = 1.6;
64
- let near = Math.max(0.01, distance - sphere.radius * safetyNear);
65
- const maxNear = distance * 0.1;
66
- if (Number.isFinite(maxNear) && maxNear > 0) {
67
- near = Math.min(near, maxNear);
68
- }
69
- const minSkyFar = 350;
70
- const far = Math.max(distance + sphere.radius * safetyFar, near + sphere.radius * 2, minSkyFar);
218
+ const { near, far } = computeClipRange(distance, sphere.radius, options?.clipping);
219
+ const size = bounds.getSize(new THREE.Vector3());
220
+ const orthographicHeight = Math.max(size.x, size.y, size.z, 0.05) * (config?.padding ?? 1.22);
71
221
  handle.setCamera({
222
+ kind: cameraKind,
72
223
  position: [position.x, position.y, position.z],
73
224
  target: [target.x, target.y, target.z],
225
+ orthographicHeight: cameraKind === "orthographic" ? orthographicHeight : undefined,
74
226
  near,
75
227
  far,
76
228
  });
77
229
  };
78
- export const setCameraTopDown = (handle, bounds) => {
230
+ export const setCameraTopDown = (handle, bounds, options) => {
79
231
  if (bounds.isEmpty())
80
232
  return;
233
+ const cameraKind = resolveCameraKind(handle, options?.kind);
81
234
  const center = bounds.getCenter(new THREE.Vector3());
82
235
  const size = bounds.getSize(new THREE.Vector3());
83
- const span = Math.max(size.x, size.z, 1);
236
+ const span = Math.max(size.x, size.z, 0.05);
84
237
  const height = span * 1.5;
85
238
  const position = new THREE.Vector3(center.x, center.y + height, center.z);
86
239
  const target = center.clone();
87
240
  const sphere = new THREE.Sphere();
88
241
  bounds.getBoundingSphere(sphere);
89
242
  const distance = position.distanceTo(target);
90
- const safetyFar = 1.2;
91
- const safetyNear = 1.8;
92
- let near = Math.max(0.01, distance - sphere.radius * safetyNear);
93
- const maxNear = distance * 0.1;
94
- if (Number.isFinite(maxNear) && maxNear > 0) {
95
- near = Math.min(near, maxNear);
96
- }
97
- const minSkyFar = 350;
98
- const far = Math.max(distance + sphere.radius * safetyFar, near + sphere.radius * 2, minSkyFar);
243
+ const { near, far } = computeClipRange(distance, sphere.radius, options?.clipping ?? {
244
+ nearSafety: 1.8,
245
+ farSafety: 1.2,
246
+ });
247
+ const orthographicHeight = Math.max(span * 1.1, 0.05);
99
248
  handle.setCamera({
249
+ kind: cameraKind,
100
250
  position: [position.x, position.y, position.z],
101
251
  target: [target.x, target.y, target.z],
252
+ orthographicHeight: cameraKind === "orthographic" ? orthographicHeight : undefined,
102
253
  near,
103
254
  far,
104
255
  });
@@ -209,11 +360,17 @@ export const normalizeRenderGroup = (group, options = {}) => {
209
360
  }
210
361
  };
211
362
  export function applyGroupToViewer(options) {
212
- const { viewer, group, cameraRig, viewMode = "auto", frameConfig, materialsConfig, floorLevel, ceilingHeight, } = options;
363
+ const { viewer, group, cameraRig, preserveSceneAnchor = false, viewMode = "auto", frameConfig, presentation, materialsConfig, materialsPolicy, floorLevel, ceilingHeight, preserveCamera = false, } = options;
213
364
  if (!group)
214
365
  return null;
215
- const normalization = normalizeObject3d(group, cameraRig);
216
- const bounds = normalization.bounds;
366
+ const sceneAnchor = preserveSceneAnchor && viewer ? new THREE.Vector3(...viewer.getCameraState().target) : undefined;
367
+ const normalization = normalizeObject3d(group, sceneAnchor ? { ...cameraRig, anchor: sceneAnchor } : cameraRig);
368
+ let bounds = normalization.bounds;
369
+ if (presentation?.kind === "plan2d") {
370
+ applyPlanPresentationStyling(group);
371
+ group.updateMatrixWorld(true);
372
+ bounds = new THREE.Box3().setFromObject(group);
373
+ }
217
374
  const resolvedFloor = cameraRig?.floorToZero
218
375
  ? 0
219
376
  : typeof floorLevel === "number"
@@ -225,16 +382,24 @@ export function applyGroupToViewer(options) {
225
382
  const stats = computeSceneStats(group);
226
383
  if (viewer) {
227
384
  viewer.setSceneRoot(group);
228
- const shouldTopDown = viewMode === "2d" || (viewMode === "auto" && stats.meshCount === 0);
229
- if (shouldTopDown) {
230
- setCameraTopDown(viewer, bounds);
231
- }
232
- else {
233
- setCameraToBounds(viewer, bounds, frameConfig);
385
+ if (!preserveCamera) {
386
+ const shouldTopDown = viewMode === "2d" || (viewMode === "auto" && presentation?.kind === "plan2d");
387
+ if (shouldTopDown) {
388
+ setCameraTopDown(viewer, bounds, {
389
+ kind: presentation?.cameraKind,
390
+ clipping: presentation?.clipping,
391
+ });
392
+ }
393
+ else {
394
+ setCameraToBounds(viewer, bounds, frameConfig, {
395
+ kind: presentation?.cameraKind,
396
+ clipping: presentation?.clipping,
397
+ });
398
+ }
234
399
  }
235
400
  }
236
401
  if (materialsConfig) {
237
- applyMaterialsToGroup(group, materialsConfig, resolvedFloor, resolvedHeight);
402
+ applyMaterialsToGroup(group, materialsConfig, resolvedFloor, resolvedHeight, materialsPolicy);
238
403
  viewer?.invalidate?.();
239
404
  }
240
405
  return { bounds, stats, normalization, resolvedFloor, resolvedHeight };
@@ -0,0 +1,64 @@
1
+ import * as THREE from "three";
2
+ export type SceneSemanticBounds = {
3
+ min: [number, number, number];
4
+ max: [number, number, number];
5
+ size: {
6
+ x: number;
7
+ y: number;
8
+ z: number;
9
+ };
10
+ center: [number, number, number];
11
+ };
12
+ export type SceneSemanticObject = {
13
+ registryId: string;
14
+ threeUuid: string;
15
+ object: THREE.Mesh;
16
+ objectName?: string;
17
+ objectId?: string;
18
+ meshId?: string;
19
+ sourceId?: string;
20
+ buildResultId?: string;
21
+ surfaceDomainId?: string;
22
+ surfaceType?: string;
23
+ surfaceClass?: string;
24
+ spanId?: string;
25
+ cellId?: string;
26
+ verticalBandId?: string;
27
+ floorBandId?: string;
28
+ roofBandId?: string;
29
+ frameId?: string;
30
+ faceId?: string;
31
+ role?: string;
32
+ layer?: string;
33
+ className?: string;
34
+ subclassName?: string;
35
+ surface?: string;
36
+ visible: boolean;
37
+ castShadow: boolean;
38
+ receiveShadow: boolean;
39
+ vertexCount: number;
40
+ triangleCount: number;
41
+ bounds: SceneSemanticBounds;
42
+ data: Record<string, string>;
43
+ userData: Record<string, string | number | boolean | null>;
44
+ };
45
+ export type SceneSemanticRegistry = {
46
+ objects: SceneSemanticObject[];
47
+ byRegistryId: Record<string, SceneSemanticObject>;
48
+ byThreeUuid: Record<string, SceneSemanticObject>;
49
+ bySurfaceDomainId: Record<string, string[]>;
50
+ byCellId: Record<string, string[]>;
51
+ bySurfaceClass: Record<string, string[]>;
52
+ dataKeys: string[];
53
+ counts: {
54
+ objectCount: number;
55
+ meshCount: number;
56
+ surfaceDomainCount: number;
57
+ cellCount: number;
58
+ surfaceClassCount: number;
59
+ dataKeyCount: number;
60
+ };
61
+ };
62
+ export declare function buildSceneSemanticRegistry(root?: THREE.Object3D | null): SceneSemanticRegistry;
63
+ export declare function findSceneSemanticObjectByThreeUuid(registry: SceneSemanticRegistry | null | undefined, threeUuid: string | null | undefined): SceneSemanticObject | null;
64
+ //# sourceMappingURL=sceneSemanticRegistry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sceneSemanticRegistry.d.ts","sourceRoot":"","sources":["../src/sceneSemanticRegistry.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,199 @@
1
+ import * as THREE from "three";
2
+ const pickString = (userData, ...keys) => {
3
+ for (const key of keys) {
4
+ const value = userData[key];
5
+ if (value === null || value === undefined)
6
+ continue;
7
+ const normalized = String(value).trim();
8
+ if (normalized.length > 0)
9
+ return normalized;
10
+ }
11
+ return undefined;
12
+ };
13
+ const toSerializableUserData = (userData) => {
14
+ const output = {};
15
+ for (const [key, value] of Object.entries(userData)) {
16
+ if (key.startsWith("__"))
17
+ continue;
18
+ if (value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
19
+ output[key] = value;
20
+ }
21
+ }
22
+ return output;
23
+ };
24
+ const collectDataValues = (userData) => {
25
+ const data = {};
26
+ const nestedData = userData.data;
27
+ if (nestedData && typeof nestedData === "object" && !Array.isArray(nestedData)) {
28
+ for (const [key, value] of Object.entries(nestedData)) {
29
+ if (value === null || value === undefined)
30
+ continue;
31
+ const normalized = String(value).trim();
32
+ if (normalized.length > 0) {
33
+ data[key] = normalized;
34
+ }
35
+ }
36
+ }
37
+ for (const [key, value] of Object.entries(userData)) {
38
+ if (!key.startsWith("data:") || value === null || value === undefined)
39
+ continue;
40
+ const normalized = String(value).trim();
41
+ if (normalized.length > 0) {
42
+ data[key.slice("data:".length)] = normalized;
43
+ }
44
+ }
45
+ return data;
46
+ };
47
+ const computeTriangleCount = (geometry) => {
48
+ if (!geometry)
49
+ return 0;
50
+ const index = geometry.getIndex();
51
+ if (index)
52
+ return Math.floor(index.count / 3);
53
+ const position = geometry.getAttribute("position");
54
+ return position ? Math.floor(position.count / 3) : 0;
55
+ };
56
+ const computeVertexCount = (geometry) => {
57
+ if (!geometry)
58
+ return 0;
59
+ const position = geometry.getAttribute("position");
60
+ return position?.count ?? 0;
61
+ };
62
+ const computeBounds = (object) => {
63
+ const box = new THREE.Box3().setFromObject(object);
64
+ if (box.isEmpty()) {
65
+ return {
66
+ min: [0, 0, 0],
67
+ max: [0, 0, 0],
68
+ size: { x: 0, y: 0, z: 0 },
69
+ center: [0, 0, 0],
70
+ };
71
+ }
72
+ const size = new THREE.Vector3();
73
+ const center = new THREE.Vector3();
74
+ box.getSize(size);
75
+ box.getCenter(center);
76
+ return {
77
+ min: [box.min.x, box.min.y, box.min.z],
78
+ max: [box.max.x, box.max.y, box.max.z],
79
+ size: { x: size.x, y: size.y, z: size.z },
80
+ center: [center.x, center.y, center.z],
81
+ };
82
+ };
83
+ const appendIndex = (target, key, registryId) => {
84
+ if (!key)
85
+ return;
86
+ if (!target[key]) {
87
+ target[key] = [];
88
+ }
89
+ target[key].push(registryId);
90
+ };
91
+ const makeUniqueRegistryId = (baseId, existing) => {
92
+ if (!existing[baseId])
93
+ return baseId;
94
+ let index = 2;
95
+ while (existing[`${baseId}#${index}`]) {
96
+ index += 1;
97
+ }
98
+ return `${baseId}#${index}`;
99
+ };
100
+ const buildSemanticObject = (mesh, existing) => {
101
+ const userData = (mesh.userData ?? {});
102
+ const meshId = pickString(userData, "meshId", "b2:meshId");
103
+ const objectId = pickString(userData, "objectId", "id", "b2:id", "packet");
104
+ const cellId = pickString(userData, "cellId", "b2:cellId");
105
+ const surfaceDomainId = pickString(userData, "surfaceDomainId", "b2:surfaceDomainId");
106
+ const baseId = meshId ?? objectId ?? cellId ?? surfaceDomainId ?? mesh.uuid;
107
+ const registryId = makeUniqueRegistryId(baseId, existing);
108
+ const data = collectDataValues(userData);
109
+ const surfaceClass = pickString(userData, "surfaceClass", "b2:surfaceClass", "surface");
110
+ return {
111
+ registryId,
112
+ threeUuid: mesh.uuid,
113
+ object: mesh,
114
+ objectName: mesh.name || undefined,
115
+ objectId,
116
+ meshId,
117
+ sourceId: pickString(userData, "sourceId", "b2:sourceId"),
118
+ buildResultId: pickString(userData, "buildResultId", "b2:buildResultId"),
119
+ surfaceDomainId,
120
+ surfaceType: pickString(userData, "surfaceType", "b2:surfaceType"),
121
+ surfaceClass,
122
+ spanId: pickString(userData, "spanId", "b2:spanId"),
123
+ cellId,
124
+ verticalBandId: pickString(userData, "verticalBandId", "b2:verticalBandId"),
125
+ floorBandId: pickString(userData, "floorBandId", "b2:floorBandId"),
126
+ roofBandId: pickString(userData, "roofBandId", "b2:roofBandId"),
127
+ frameId: pickString(userData, "frameId", "b2:frameId"),
128
+ faceId: pickString(userData, "faceId", "b2:faceId"),
129
+ role: pickString(userData, "role", "b2:role"),
130
+ layer: pickString(userData, "layer", "b2:layer"),
131
+ className: pickString(userData, "class", "className"),
132
+ subclassName: pickString(userData, "subclass", "subclassName"),
133
+ surface: pickString(userData, "surface"),
134
+ visible: mesh.visible,
135
+ castShadow: mesh.castShadow,
136
+ receiveShadow: mesh.receiveShadow,
137
+ vertexCount: computeVertexCount(mesh.geometry),
138
+ triangleCount: computeTriangleCount(mesh.geometry),
139
+ bounds: computeBounds(mesh),
140
+ data,
141
+ userData: toSerializableUserData(userData),
142
+ };
143
+ };
144
+ export function buildSceneSemanticRegistry(root) {
145
+ const byRegistryId = {};
146
+ const byThreeUuid = {};
147
+ const bySurfaceDomainId = {};
148
+ const byCellId = {};
149
+ const bySurfaceClass = {};
150
+ const dataKeys = new Set();
151
+ const surfaceDomainIds = new Set();
152
+ const cellIds = new Set();
153
+ const surfaceClasses = new Set();
154
+ if (root && typeof root.traverse === "function") {
155
+ root.traverse((child) => {
156
+ const mesh = child;
157
+ if (!mesh.isMesh || !mesh.geometry)
158
+ return;
159
+ if (mesh.userData?.excludeFromSemanticRegistry === true)
160
+ return;
161
+ const entry = buildSemanticObject(mesh, byRegistryId);
162
+ byRegistryId[entry.registryId] = entry;
163
+ byThreeUuid[entry.threeUuid] = entry;
164
+ appendIndex(bySurfaceDomainId, entry.surfaceDomainId, entry.registryId);
165
+ appendIndex(byCellId, entry.cellId, entry.registryId);
166
+ appendIndex(bySurfaceClass, entry.surfaceClass, entry.registryId);
167
+ if (entry.surfaceDomainId)
168
+ surfaceDomainIds.add(entry.surfaceDomainId);
169
+ if (entry.cellId)
170
+ cellIds.add(entry.cellId);
171
+ if (entry.surfaceClass)
172
+ surfaceClasses.add(entry.surfaceClass);
173
+ Object.keys(entry.data).forEach((key) => dataKeys.add(key));
174
+ });
175
+ }
176
+ const objects = Object.values(byRegistryId);
177
+ return {
178
+ objects,
179
+ byRegistryId,
180
+ byThreeUuid,
181
+ bySurfaceDomainId,
182
+ byCellId,
183
+ bySurfaceClass,
184
+ dataKeys: Array.from(dataKeys).sort((a, b) => a.localeCompare(b)),
185
+ counts: {
186
+ objectCount: objects.length,
187
+ meshCount: objects.length,
188
+ surfaceDomainCount: surfaceDomainIds.size,
189
+ cellCount: cellIds.size,
190
+ surfaceClassCount: surfaceClasses.size,
191
+ dataKeyCount: dataKeys.size,
192
+ },
193
+ };
194
+ }
195
+ export function findSceneSemanticObjectByThreeUuid(registry, threeUuid) {
196
+ if (!registry || !threeUuid)
197
+ return null;
198
+ return registry.byThreeUuid[threeUuid] ?? null;
199
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"scienceSky.d.ts","sourceRoot":"","sources":["../../src/sky/scienceSky.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AAE9D,MAAM,MAAM,gBAAgB,GAAG;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,GAAG,CAAC,EAAE,sBAAsB,CAAC,KAAK,CAAC,CAAC;CACrC,CAAC;AAmFF,wBAAgB,qBAAqB,IAAI,gBAAgB,EAAE,CAE1D;AAED,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,MAAM,GAAG,gBAAgB,GAAG,SAAS,CAE5E"}
1
+ {"version":3,"file":"scienceSky.d.ts","sourceRoot":"","sources":["../../src/sky/scienceSky.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AAE9D,MAAM,MAAM,gBAAgB,GAAG;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,GAAG,CAAC,EAAE,sBAAsB,CAAC,KAAK,CAAC,CAAC;CACrC,CAAC;AAmGF,wBAAgB,qBAAqB,IAAI,gBAAgB,EAAE,CAE1D;AAED,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,MAAM,GAAG,gBAAgB,GAAG,SAAS,CAE5E"}
@@ -1,4 +1,5 @@
1
1
  const HDR_BASE = "https://assets.treasury.space/science/hdr/shared";
2
+ const SKY_IMAGE_BASE = "https://assets.treasury.space/science/sky/shared";
2
3
  const SCIENCE_SKY_OPTIONS = [
3
4
  {
4
5
  id: "preset",
@@ -31,6 +32,21 @@ const SCIENCE_SKY_OPTIONS = [
31
32
  hdr: { src: `${HDR_BASE}/canary_wharf_4k.hdr`, exposure: 1.1 },
32
33
  },
33
34
  },
35
+ {
36
+ id: "spacex-interface",
37
+ label: "SpaceX Interface",
38
+ description: "Authored 2:1 LDR panorama wrap from the launch photo. Background only; not HDR lighting.",
39
+ sky: {
40
+ mode: "image",
41
+ image: {
42
+ src: `${SKY_IMAGE_BASE}/space_x_panorama_wrap_4096x2048_v2.jpg`,
43
+ projection: "equirectangular",
44
+ rotationY: 1.1,
45
+ showBackground: true,
46
+ showLighting: false,
47
+ },
48
+ },
49
+ },
34
50
  {
35
51
  id: "kloofendal",
36
52
  label: "Kloofendal Sky",
@@ -1,10 +1,33 @@
1
1
  import * as THREE from "three";
2
2
  import type { RenderPresetDefinition } from "../engine/types";
3
+ export declare const AO_DEPTH_PROXY_USER_DATA_KEY = "treasuryAoDepthProxy";
4
+ export type DebugSystemState = {
5
+ groundEnabled: boolean;
6
+ gridEnabled: boolean;
7
+ groundY: number | null;
8
+ gridY: number | null;
9
+ gridSize: number | null;
10
+ gridDivisions: number | null;
11
+ gridStep: number | null;
12
+ gridMajorEvery: number | null;
13
+ gridSuperMajorEvery: number | null;
14
+ groundMode: "finite" | "infinite" | null;
15
+ gridMode: "finite" | "infinite" | null;
16
+ };
3
17
  export declare class DebugSystem {
4
18
  private scene;
5
19
  private objects;
20
+ private ground;
21
+ private groundAoProxy;
22
+ private grid;
23
+ private groundInfinite;
24
+ private gridInfinite;
25
+ private gridDivisionSize;
26
+ private state;
6
27
  constructor(scene: THREE.Scene);
7
- apply(preset: RenderPresetDefinition, bounds?: THREE.Box3 | null): void;
28
+ update(anchor?: THREE.Vector3 | null): void;
29
+ getState(): DebugSystemState;
30
+ apply(preset: RenderPresetDefinition, bounds?: THREE.Box3 | null, root?: THREE.Object3D | null): void;
8
31
  dispose(): void;
9
32
  private add;
10
33
  private clear;
@@ -1 +1 @@
1
- {"version":3,"file":"debugSystem.d.ts","sourceRoot":"","sources":["../../src/systems/debugSystem.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,KAAK,EAAqB,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AAuMjF,qBAAa,WAAW;IACtB,OAAO,CAAC,KAAK,CAAc;IAC3B,OAAO,CAAC,OAAO,CAAwB;gBAE3B,KAAK,EAAE,KAAK,CAAC,KAAK;IAI9B,KAAK,CAAC,MAAM,EAAE,sBAAsB,EAAE,MAAM,CAAC,EAAE,KAAK,CAAC,IAAI,GAAG,IAAI,GAAG,IAAI;IAmGvE,OAAO,IAAI,IAAI;IAIf,OAAO,CAAC,GAAG;IAKX,OAAO,CAAC,KAAK;CAmCd"}
1
+ {"version":3,"file":"debugSystem.d.ts","sourceRoot":"","sources":["../../src/systems/debugSystem.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,KAAK,EAAqB,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AAqDjF,eAAO,MAAM,4BAA4B,yBAAyB,CAAC;AAEnE,MAAM,MAAM,gBAAgB,GAAG;IAC7B,aAAa,EAAE,OAAO,CAAC;IACvB,WAAW,EAAE,OAAO,CAAC;IACrB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,UAAU,EAAE,QAAQ,GAAG,UAAU,GAAG,IAAI,CAAC;IACzC,QAAQ,EAAE,QAAQ,GAAG,UAAU,GAAG,IAAI,CAAC;CACxC,CAAC;AAoRF,qBAAa,WAAW;IACtB,OAAO,CAAC,KAAK,CAAc;IAC3B,OAAO,CAAC,OAAO,CAAwB;IACvC,OAAO,CAAC,MAAM,CAA2B;IACzC,OAAO,CAAC,aAAa,CAA2B;IAChD,OAAO,CAAC,IAAI,CAA+B;IAC3C,OAAO,CAAC,cAAc,CAAS;IAC/B,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,gBAAgB,CAAK;IAC7B,OAAO,CAAC,KAAK,CAYX;gBAEU,KAAK,EAAE,KAAK,CAAC,KAAK;IAI9B,MAAM,CAAC,MAAM,CAAC,EAAE,KAAK,CAAC,OAAO,GAAG,IAAI,GAAG,IAAI;IAiB3C,QAAQ,IAAI,gBAAgB;IAI5B,KAAK,CAAC,MAAM,EAAE,sBAAsB,EAAE,MAAM,CAAC,EAAE,KAAK,CAAC,IAAI,GAAG,IAAI,EAAE,IAAI,CAAC,EAAE,KAAK,CAAC,QAAQ,GAAG,IAAI,GAAG,IAAI;IA8KrG,OAAO,IAAI,IAAI;IAIf,OAAO,CAAC,GAAG;IAKX,OAAO,CAAC,KAAK;CAwDd"}