worldorbit 3.0.6 → 3.0.7

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.
@@ -37777,6 +37777,15 @@ void main() {
37777
37777
  starCore: "#ffcc67",
37778
37778
  starStroke: "rgba(255, 245, 203, 0.85)",
37779
37779
  starGlow: "#ffe8a3",
37780
+ spaceFog: "#07131d",
37781
+ starfield: "rgba(226, 239, 255, 0.9)",
37782
+ starfieldDim: "rgba(164, 194, 228, 0.45)",
37783
+ objectSpecular: "#f5f8ff",
37784
+ orbitOpacity: 0.34,
37785
+ orbitBandOpacity: 0.24,
37786
+ selectionHalo: "rgba(255, 214, 139, 0.9)",
37787
+ atmosphere: "rgba(143, 202, 255, 0.4)",
37788
+ cometTail: "rgba(193, 243, 255, 0.7)",
37780
37789
  fontFamily: '"Segoe UI Variable", "Bahnschrift", sans-serif',
37781
37790
  displayFont: '"Bahnschrift", "Segoe UI Variable", sans-serif'
37782
37791
  },
@@ -37800,6 +37809,15 @@ void main() {
37800
37809
  starCore: "#e5f98c",
37801
37810
  starStroke: "rgba(246, 255, 217, 0.9)",
37802
37811
  starGlow: "#fffab4",
37812
+ spaceFog: "#071723",
37813
+ starfield: "rgba(220, 255, 245, 0.9)",
37814
+ starfieldDim: "rgba(124, 212, 195, 0.42)",
37815
+ objectSpecular: "#ecfffb",
37816
+ orbitOpacity: 0.3,
37817
+ orbitBandOpacity: 0.22,
37818
+ selectionHalo: "rgba(120, 255, 215, 0.85)",
37819
+ atmosphere: "rgba(120, 255, 215, 0.32)",
37820
+ cometTail: "rgba(181, 255, 236, 0.68)",
37803
37821
  fontFamily: '"Segoe UI Variable", "Bahnschrift", sans-serif',
37804
37822
  displayFont: '"Bahnschrift", "Segoe UI Variable", sans-serif'
37805
37823
  },
@@ -37823,6 +37841,15 @@ void main() {
37823
37841
  starCore: "#ffb766",
37824
37842
  starStroke: "rgba(255, 236, 205, 0.88)",
37825
37843
  starGlow: "#ffe2ad",
37844
+ spaceFog: "#1c0d12",
37845
+ starfield: "rgba(255, 232, 214, 0.88)",
37846
+ starfieldDim: "rgba(255, 176, 138, 0.38)",
37847
+ objectSpecular: "#fff0e6",
37848
+ orbitOpacity: 0.3,
37849
+ orbitBandOpacity: 0.24,
37850
+ selectionHalo: "rgba(255, 178, 125, 0.85)",
37851
+ atmosphere: "rgba(255, 190, 140, 0.26)",
37852
+ cometTail: "rgba(255, 214, 173, 0.62)",
37826
37853
  fontFamily: '"Segoe UI Variable", "Bahnschrift", sans-serif',
37827
37854
  displayFont: '"Bahnschrift", "Segoe UI Variable", sans-serif'
37828
37855
  }
@@ -38872,10 +38899,12 @@ void main() {
38872
38899
  let currentVisibleObjectIds = /* @__PURE__ */ new Set();
38873
38900
  let currentSelectedObjectId = null;
38874
38901
  let currentHoveredObjectId = null;
38875
- let currentTimeSeconds = 0;
38876
38902
  let currentPositions = /* @__PURE__ */ new Map();
38877
38903
  let pendingUpdate = null;
38878
38904
  let destroyed = false;
38905
+ let smoothedCameraPosition = null;
38906
+ let smoothedCameraTarget = null;
38907
+ let currentEnvironmentKey = "";
38879
38908
  const objectVisuals = /* @__PURE__ */ new Map();
38880
38909
  const orbitVisuals = /* @__PURE__ */ new Map();
38881
38910
  const raycastTargets = [];
@@ -38884,7 +38913,7 @@ void main() {
38884
38913
  return;
38885
38914
  }
38886
38915
  const scene3d = new THREE.Scene();
38887
- const camera = new THREE.PerspectiveCamera(52, 1, 0.1, 2e4);
38916
+ const camera = new THREE.PerspectiveCamera(46, 1, 0.1, 24e3);
38888
38917
  const renderer = new THREE.WebGLRenderer({
38889
38918
  antialias: true,
38890
38919
  alpha: true,
@@ -38894,27 +38923,40 @@ void main() {
38894
38923
  renderer.domElement.dataset.worldorbit3dCanvas = "true";
38895
38924
  root.innerHTML = "";
38896
38925
  root.append(renderer.domElement);
38897
- const ambientLight = new THREE.AmbientLight(16777215, 1.2);
38898
- const keyLight = new THREE.PointLight(16777215, 1.35, 0, 2);
38899
- scene3d.add(ambientLight);
38900
- scene3d.add(keyLight);
38926
+ const ambientLight = new THREE.AmbientLight(16777215, 0.24);
38927
+ const fillLight = new THREE.DirectionalLight(13625855, 0.36);
38928
+ const rimLight = new THREE.DirectionalLight(8304895, 0.24);
38929
+ const keyLight = new THREE.PointLight(16773327, 2.6, 0, 2);
38930
+ fillLight.position.set(-360, 260, 220);
38931
+ rimLight.position.set(340, 180, -280);
38901
38932
  const orbitLayer = new THREE.Group();
38902
38933
  const objectLayer = new THREE.Group();
38934
+ const starfield = createStarfield(THREE, 320);
38935
+ const raycaster = new THREE.Raycaster();
38936
+ raycaster.params.Line = { threshold: 7 };
38937
+ scene3d.add(ambientLight);
38938
+ scene3d.add(fillLight);
38939
+ scene3d.add(rimLight);
38940
+ scene3d.add(keyLight);
38941
+ scene3d.add(starfield);
38903
38942
  scene3d.add(orbitLayer);
38904
38943
  scene3d.add(objectLayer);
38905
- const raycaster = new THREE.Raycaster();
38906
- raycaster.params.Line = { threshold: 10 };
38907
38944
  runtime = {
38908
38945
  THREE,
38909
38946
  scene3d,
38910
38947
  camera,
38911
38948
  renderer,
38949
+ ambientLight,
38950
+ fillLight,
38951
+ rimLight,
38912
38952
  keyLight,
38953
+ starfield,
38913
38954
  orbitLayer,
38914
38955
  objectLayer,
38915
38956
  raycaster,
38916
38957
  pointer: new THREE.Vector2()
38917
38958
  };
38959
+ configureRenderer(renderer, THREE, "balanced");
38918
38960
  if (pendingUpdate) {
38919
38961
  applyUpdate(pendingUpdate);
38920
38962
  }
@@ -38973,6 +39015,8 @@ void main() {
38973
39015
  destroyed = true;
38974
39016
  pendingUpdate = null;
38975
39017
  runtime?.renderer.dispose();
39018
+ runtime?.starfield?.geometry?.dispose?.();
39019
+ runtime?.starfield?.material?.dispose?.();
38976
39020
  root.remove();
38977
39021
  objectVisuals.clear();
38978
39022
  orbitVisuals.clear();
@@ -38991,7 +39035,16 @@ void main() {
38991
39035
  currentVisibleObjectIds = next.visibleObjectIds;
38992
39036
  currentSelectedObjectId = next.selectedObjectId;
38993
39037
  currentHoveredObjectId = next.hoveredObjectId;
38994
- currentTimeSeconds = next.timeSeconds;
39038
+ configureRenderer(runtime.renderer, runtime.THREE, currentRenderOptions?.quality ?? "balanced");
39039
+ const nextEnvironmentKey = JSON.stringify({
39040
+ theme: currentRenderOptions?.theme ?? null,
39041
+ quality: currentRenderOptions?.quality ?? "balanced",
39042
+ style3d: currentRenderOptions?.style3d ?? "symbolic"
39043
+ });
39044
+ if (nextEnvironmentKey !== currentEnvironmentKey) {
39045
+ updateEnvironment(runtime, currentRenderOptions);
39046
+ currentEnvironmentKey = nextEnvironmentKey;
39047
+ }
38995
39048
  if (sceneChanged) {
38996
39049
  rebuildScene(next.spatialScene);
38997
39050
  }
@@ -39001,8 +39054,9 @@ void main() {
39001
39054
  updateOrbitTransforms();
39002
39055
  updateVisibility();
39003
39056
  updateInteractionState();
39057
+ updateLighting();
39004
39058
  updateCamera();
39005
- renderNow();
39059
+ runtime.renderer.render(runtime.scene3d, runtime.camera);
39006
39060
  }
39007
39061
  function rebuildScene(spatialScene) {
39008
39062
  if (!runtime) {
@@ -39013,8 +39067,9 @@ void main() {
39013
39067
  objectVisuals.clear();
39014
39068
  orbitVisuals.clear();
39015
39069
  raycastTargets.length = 0;
39070
+ smoothedCameraPosition = null;
39071
+ smoothedCameraTarget = null;
39016
39072
  const theme = resolveTheme(currentRenderOptions?.theme);
39017
- runtime.scene3d.background = new runtime.THREE.Color(theme.backgroundStart);
39018
39073
  for (const orbit of spatialScene.orbits) {
39019
39074
  const visual = createOrbitVisual2(runtime.THREE, orbit, theme);
39020
39075
  runtime.orbitLayer.add(visual.root);
@@ -39032,10 +39087,9 @@ void main() {
39032
39087
  for (const object of currentScene?.objects ?? []) {
39033
39088
  const visual = objectVisuals.get(object.objectId);
39034
39089
  const position = currentPositions.get(object.objectId);
39035
- if (!visual || !position) {
39036
- continue;
39090
+ if (visual && position) {
39091
+ visual.root.position.set(position.x, position.y, position.z);
39037
39092
  }
39038
- visual.root.position.set(position.x, position.y, position.z);
39039
39093
  }
39040
39094
  }
39041
39095
  function updateOrbitTransforms() {
@@ -39056,8 +39110,7 @@ void main() {
39056
39110
  continue;
39057
39111
  }
39058
39112
  const hideStructure = layers.structures === false && (object.object.type === "structure" || object.object.type === "phenomenon");
39059
- const hideObjects = layers.objects === false;
39060
- visual.root.visible = !object.hidden && currentVisibleObjectIds.has(object.objectId) && !hideStructure && !hideObjects;
39113
+ visual.root.visible = !object.hidden && currentVisibleObjectIds.has(object.objectId) && layers.objects !== false && !hideStructure;
39061
39114
  }
39062
39115
  for (const orbit of currentScene?.orbits ?? []) {
39063
39116
  const visual = orbitVisuals.get(orbit.objectId);
@@ -39073,14 +39126,27 @@ void main() {
39073
39126
  return;
39074
39127
  }
39075
39128
  for (const visual of objectVisuals.values()) {
39076
- applyVisualState(runtime.THREE, visual.materials, visual.baseColor, currentSelectedObjectId === visual.objectId, currentHoveredObjectId === visual.objectId);
39077
- const scale = currentSelectedObjectId === visual.objectId ? 1.2 : currentHoveredObjectId === visual.objectId ? 1.1 : 1;
39129
+ const selected = currentSelectedObjectId === visual.objectId;
39130
+ const hovered = currentHoveredObjectId === visual.objectId;
39131
+ applyVisualState(runtime.THREE, visual.materials, selected, hovered);
39132
+ const scale = selected ? 1.16 : hovered ? 1.08 : 1;
39078
39133
  visual.root.scale.set(scale, scale, scale);
39134
+ if (visual.halo) {
39135
+ visual.halo.visible = selected || hovered;
39136
+ }
39079
39137
  }
39080
39138
  for (const visual of orbitVisuals.values()) {
39081
- applyVisualState(runtime.THREE, visual.materials, visual.baseColor, currentSelectedObjectId === visual.objectId, currentHoveredObjectId === visual.objectId);
39139
+ applyVisualState(runtime.THREE, visual.materials, currentSelectedObjectId === visual.objectId, currentHoveredObjectId === visual.objectId);
39082
39140
  }
39083
39141
  }
39142
+ function updateLighting() {
39143
+ if (!runtime || !currentScene) {
39144
+ return;
39145
+ }
39146
+ const primaryStar = currentScene.objects.find((object) => object.object.type === "star" && !object.hidden) ?? null;
39147
+ const starPosition = primaryStar ? currentPositions.get(primaryStar.objectId) ?? primaryStar.position : { x: 0, y: 40, z: 0 };
39148
+ runtime.keyLight.position.set(starPosition.x, starPosition.y + 20, starPosition.z);
39149
+ }
39084
39150
  function updateCamera() {
39085
39151
  if (!runtime || !currentScene || !currentState) {
39086
39152
  return;
@@ -39088,19 +39154,27 @@ void main() {
39088
39154
  const sceneCamera = currentRenderOptions?.camera ?? currentScene.camera;
39089
39155
  const bounds = currentScene.contentBounds;
39090
39156
  const size = Math.max(bounds.width, bounds.depth, bounds.height, 160);
39091
- const yaw = degreesToRadians3((sceneCamera?.azimuth ?? 34) + currentState.rotationDeg);
39092
- const pitch = degreesToRadians3(clampValue(sceneCamera?.elevation ?? 24, -75, 75));
39093
- const zoomDistanceFactor = clampValue(2.4 / Math.max(currentState.scale, 0.1), 0.35, 8);
39094
- const semanticDistance = clampValue(sceneCamera?.distance ?? 6, 2, 24);
39095
- const distance = clampValue(size * zoomDistanceFactor * (semanticDistance / 6), 28, 8e3);
39157
+ const yaw = degreesToRadians3((sceneCamera?.azimuth ?? 30) + currentState.rotationDeg);
39158
+ const pitch = degreesToRadians3(clampValue(sceneCamera?.elevation ?? 22, -75, 75));
39159
+ const zoomDistanceFactor = clampValue(2.2 / Math.max(currentState.scale, 0.1), 0.35, 7.2);
39160
+ const semanticDistance = clampValue(sceneCamera?.distance ?? 5.4, 2, 24);
39161
+ const distance = clampValue(size * zoomDistanceFactor * (semanticDistance / 5.4), 24, 8e3);
39096
39162
  const panFactor = Math.max(size / 900, 0.12);
39097
39163
  const target = new runtime.THREE.Vector3(bounds.center.x - currentState.translateX * panFactor, bounds.center.y, bounds.center.z - currentState.translateY * panFactor);
39098
- runtime.camera.position.set(target.x + distance * Math.cos(pitch) * Math.sin(yaw), target.y + distance * Math.sin(pitch), target.z + distance * Math.cos(pitch) * Math.cos(yaw));
39099
- runtime.camera.lookAt(target);
39164
+ const desiredPosition = new runtime.THREE.Vector3(target.x + distance * Math.cos(pitch) * Math.sin(yaw), target.y + distance * Math.sin(pitch), target.z + distance * Math.cos(pitch) * Math.cos(yaw));
39165
+ const smoothing = (currentRenderOptions?.style3d ?? "symbolic") === "cinematic" ? 0.16 : 0.32;
39166
+ if (!smoothedCameraPosition || !smoothedCameraTarget) {
39167
+ smoothedCameraPosition = desiredPosition.clone();
39168
+ smoothedCameraTarget = target.clone();
39169
+ } else {
39170
+ smoothedCameraPosition.lerp(desiredPosition, smoothing);
39171
+ smoothedCameraTarget.lerp(target, smoothing);
39172
+ }
39173
+ runtime.camera.position.copy(smoothedCameraPosition);
39174
+ runtime.camera.lookAt(smoothedCameraTarget);
39100
39175
  if (sceneCamera?.roll) {
39101
39176
  runtime.camera.rotation.z = degreesToRadians3(sceneCamera.roll);
39102
39177
  }
39103
- runtime.keyLight.position.copy(runtime.camera.position);
39104
39178
  }
39105
39179
  function resizeRenderer(spatialScene) {
39106
39180
  if (!runtime) {
@@ -39112,35 +39186,63 @@ void main() {
39112
39186
  runtime.camera.aspect = width / height;
39113
39187
  runtime.camera.updateProjectionMatrix();
39114
39188
  }
39115
- function renderNow() {
39116
- if (!runtime) {
39117
- return;
39118
- }
39119
- runtime.renderer.render(runtime.scene3d, runtime.camera);
39120
- }
39121
39189
  }
39122
39190
  function createObjectVisual(THREE, object, theme) {
39123
39191
  const root = new THREE.Group();
39124
39192
  root.userData.objectId = object.objectId;
39125
39193
  const baseColor = object.fillColor ?? colorForObject(object);
39126
- const material = new THREE.MeshPhongMaterial({
39127
- color: baseColor,
39128
- emissive: object.object.type === "star" ? new THREE.Color(theme.starGlow) : new THREE.Color(0),
39129
- emissiveIntensity: object.object.type === "star" ? 0.6 : 0.08,
39130
- transparent: true,
39131
- opacity: object.object.type === "phenomenon" ? 0.7 : 1
39132
- });
39133
- const geometry = geometryForObject(THREE, object);
39134
- const body = new THREE.Mesh(geometry, material);
39194
+ const materials = [];
39195
+ const bodyMaterial = materialForObject(THREE, object, baseColor, theme);
39196
+ const body = new THREE.Mesh(geometryForObject(THREE, object), bodyMaterial.material);
39135
39197
  body.userData.objectId = object.objectId;
39136
39198
  root.add(body);
39137
- return {
39138
- objectId: object.objectId,
39139
- root,
39140
- body,
39141
- materials: [material],
39142
- baseColor
39143
- };
39199
+ materials.push(bodyMaterial);
39200
+ if (shouldRenderAtmosphere(object)) {
39201
+ const atmosphereMaterial = {
39202
+ material: new THREE.MeshBasicMaterial({
39203
+ color: theme.atmosphere,
39204
+ transparent: true,
39205
+ opacity: 0.24,
39206
+ depthWrite: false,
39207
+ side: 2
39208
+ }),
39209
+ baseColor: theme.atmosphere,
39210
+ baseOpacity: 0.24,
39211
+ hoveredOpacity: 0.34,
39212
+ selectedOpacity: 0.42
39213
+ };
39214
+ const atmosphere = new THREE.Mesh(new THREE.SphereGeometry(Math.max(object.visualRadius, 2) * 1.16, 20, 14), atmosphereMaterial.material);
39215
+ atmosphere.userData.objectId = object.objectId;
39216
+ root.add(atmosphere);
39217
+ materials.push(atmosphereMaterial);
39218
+ }
39219
+ if (object.object.type === "comet") {
39220
+ const tailMaterial = {
39221
+ material: new THREE.MeshBasicMaterial({
39222
+ color: theme.cometTail,
39223
+ transparent: true,
39224
+ opacity: 0.36,
39225
+ depthWrite: false
39226
+ }),
39227
+ baseColor: theme.cometTail,
39228
+ baseOpacity: 0.36,
39229
+ hoveredOpacity: 0.48,
39230
+ selectedOpacity: 0.56
39231
+ };
39232
+ const tail = new THREE.Mesh(new THREE.ConeGeometry(Math.max(object.visualRadius * 0.55, 2), Math.max(object.visualRadius * 2.8, 8), 12, 1, true), tailMaterial.material);
39233
+ tail.position.set(-Math.max(object.visualRadius * 1.4, 4), 0, 0);
39234
+ tail.rotation.z = -Math.PI / 2;
39235
+ tail.userData.objectId = object.objectId;
39236
+ root.add(tail);
39237
+ materials.push(tailMaterial);
39238
+ }
39239
+ const halo = createHalo(THREE, object, theme);
39240
+ if (halo) {
39241
+ halo.visible = false;
39242
+ halo.userData.objectId = object.objectId;
39243
+ root.add(halo);
39244
+ }
39245
+ return { objectId: object.objectId, root, halo, materials };
39144
39246
  }
39145
39247
  function createOrbitVisual2(THREE, orbit, theme) {
39146
39248
  const root = new THREE.Group();
@@ -39148,60 +39250,174 @@ void main() {
39148
39250
  root.rotation.y = degreesToRadians3(orbit.rotationDeg);
39149
39251
  root.rotation.x = degreesToRadians3(orbit.inclinationDeg);
39150
39252
  const baseColor = orbit.object.properties.color ?? theme.orbit;
39151
- const materials = [];
39152
39253
  if (orbit.band) {
39153
- const material = new THREE.MeshBasicMaterial({
39154
- color: baseColor,
39155
- transparent: true,
39156
- opacity: 0.42,
39157
- side: 2
39158
- });
39159
- const geometry = bandGeometryForOrbit(THREE, orbit);
39160
- const mesh = new THREE.Mesh(geometry, material);
39254
+ const material2 = {
39255
+ material: new THREE.MeshBasicMaterial({
39256
+ color: baseColor,
39257
+ transparent: true,
39258
+ opacity: theme.orbitBandOpacity,
39259
+ side: 2,
39260
+ depthWrite: false
39261
+ }),
39262
+ baseColor,
39263
+ baseOpacity: theme.orbitBandOpacity,
39264
+ hoveredOpacity: Math.min(theme.orbitBandOpacity + 0.1, 0.58),
39265
+ selectedOpacity: Math.min(theme.orbitBandOpacity + 0.18, 0.72),
39266
+ hoveredColor: theme.accent,
39267
+ selectedColor: theme.accentStrong
39268
+ };
39269
+ const mesh = new THREE.Mesh(bandGeometryForOrbit(THREE, orbit), material2.material);
39161
39270
  mesh.userData.objectId = orbit.objectId;
39162
39271
  root.add(mesh);
39163
- materials.push(material);
39164
- } else {
39165
- const material = new THREE.LineBasicMaterial({
39272
+ return { objectId: orbit.objectId, root, materials: [material2] };
39273
+ }
39274
+ const material = {
39275
+ material: new THREE.LineBasicMaterial({
39166
39276
  color: baseColor,
39167
39277
  transparent: true,
39168
- opacity: 0.55
39169
- });
39170
- const points = sampleOrbitPoints(THREE, orbit);
39171
- const geometry = new THREE.BufferGeometry().setFromPoints(points);
39172
- const line = new THREE.LineLoop(geometry, material);
39173
- line.userData.objectId = orbit.objectId;
39174
- root.add(line);
39175
- materials.push(material);
39176
- }
39278
+ opacity: theme.orbitOpacity
39279
+ }),
39280
+ baseColor,
39281
+ baseOpacity: theme.orbitOpacity,
39282
+ hoveredOpacity: Math.min(theme.orbitOpacity + 0.18, 0.72),
39283
+ selectedOpacity: Math.min(theme.orbitOpacity + 0.3, 0.88),
39284
+ hoveredColor: theme.accent,
39285
+ selectedColor: theme.accentStrong
39286
+ };
39287
+ const geometry = new THREE.BufferGeometry().setFromPoints(sampleOrbitPoints(THREE, orbit, 120));
39288
+ const line = new THREE.LineLoop(geometry, material.material);
39289
+ line.userData.objectId = orbit.objectId;
39290
+ root.add(line);
39291
+ return { objectId: orbit.objectId, root, materials: [material] };
39292
+ }
39293
+ function materialForObject(THREE, object, baseColor, theme) {
39294
+ if (object.object.type === "star") {
39295
+ return {
39296
+ material: new THREE.MeshStandardMaterial({
39297
+ color: baseColor,
39298
+ emissive: new THREE.Color(theme.starGlow),
39299
+ emissiveIntensity: 1.2,
39300
+ roughness: 0.35,
39301
+ metalness: 0.02
39302
+ }),
39303
+ baseColor,
39304
+ baseOpacity: 1,
39305
+ hoveredOpacity: 1,
39306
+ selectedOpacity: 1,
39307
+ hoveredColor: theme.starCore,
39308
+ selectedColor: "#fff2c4",
39309
+ baseEmissive: theme.starGlow,
39310
+ hoveredEmissive: theme.starGlow,
39311
+ selectedEmissive: "#fff6cc",
39312
+ baseEmissiveIntensity: 1.2,
39313
+ hoveredEmissiveIntensity: 1.5,
39314
+ selectedEmissiveIntensity: 1.8
39315
+ };
39316
+ }
39317
+ if (object.object.type === "phenomenon") {
39318
+ return {
39319
+ material: new THREE.MeshPhongMaterial({
39320
+ color: baseColor,
39321
+ transparent: true,
39322
+ opacity: 0.7,
39323
+ emissive: new THREE.Color(baseColor),
39324
+ emissiveIntensity: 0.32,
39325
+ shininess: 90
39326
+ }),
39327
+ baseColor,
39328
+ baseOpacity: 0.7,
39329
+ hoveredOpacity: 0.82,
39330
+ selectedOpacity: 0.9,
39331
+ hoveredColor: theme.accent,
39332
+ selectedColor: theme.selectionHalo,
39333
+ baseEmissive: baseColor,
39334
+ hoveredEmissive: theme.accent,
39335
+ selectedEmissive: theme.selectionHalo,
39336
+ baseEmissiveIntensity: 0.32,
39337
+ hoveredEmissiveIntensity: 0.52,
39338
+ selectedEmissiveIntensity: 0.74
39339
+ };
39340
+ }
39341
+ const shininess = object.object.type === "structure" ? 70 : object.object.type === "ring" ? 42 : object.object.type === "belt" ? 26 : 36;
39177
39342
  return {
39178
- objectId: orbit.objectId,
39179
- root,
39180
- materials,
39181
- baseColor
39343
+ material: new THREE.MeshPhongMaterial({
39344
+ color: baseColor,
39345
+ specular: new THREE.Color(theme.objectSpecular),
39346
+ shininess,
39347
+ transparent: false,
39348
+ opacity: 1,
39349
+ emissive: new THREE.Color(0),
39350
+ emissiveIntensity: 0.02
39351
+ }),
39352
+ baseColor,
39353
+ baseOpacity: 1,
39354
+ hoveredOpacity: 1,
39355
+ selectedOpacity: 1,
39356
+ hoveredColor: shiftColorLightness(THREE, baseColor, 0.08),
39357
+ selectedColor: shiftColorLightness(THREE, baseColor, 0.16),
39358
+ hoveredEmissive: "#8fcaff",
39359
+ selectedEmissive: theme.selectionHalo,
39360
+ baseEmissiveIntensity: 0.02,
39361
+ hoveredEmissiveIntensity: 0.12,
39362
+ selectedEmissiveIntensity: 0.22
39182
39363
  };
39183
39364
  }
39184
39365
  function geometryForObject(THREE, object) {
39185
39366
  const radius = Math.max(object.visualRadius, 2);
39186
39367
  switch (object.object.type) {
39187
39368
  case "star":
39188
- return new THREE.SphereGeometry(radius * 1.12, 28, 20);
39369
+ return new THREE.SphereGeometry(radius * 1.14, 34, 24);
39189
39370
  case "structure":
39190
- return new THREE.BoxGeometry(radius * 1.5, radius * 1.5, radius * 1.5);
39371
+ return geometryForStructure(THREE, object, radius);
39191
39372
  case "phenomenon":
39192
- return new THREE.OctahedronGeometry(radius * 1.25, 0);
39373
+ return new THREE.IcosahedronGeometry(radius * 1.12, 1);
39193
39374
  case "belt":
39375
+ return new THREE.TorusGeometry(Math.max(radius * 1.15, 4), Math.max(radius * 0.28, 1), 10, 24);
39194
39376
  case "ring":
39195
- return new THREE.OctahedronGeometry(Math.max(radius * 0.85, 3), 0);
39377
+ return new THREE.TorusGeometry(Math.max(radius, 4), Math.max(radius * 0.18, 0.8), 10, 30);
39378
+ case "asteroid":
39379
+ return new THREE.DodecahedronGeometry(radius, 0);
39380
+ case "comet":
39381
+ return new THREE.SphereGeometry(radius * 0.94, 18, 14);
39196
39382
  default:
39197
- return new THREE.SphereGeometry(radius, 20, 14);
39383
+ return new THREE.SphereGeometry(radius, 24, 18);
39384
+ }
39385
+ }
39386
+ function geometryForStructure(THREE, object, radius) {
39387
+ const kind = String(object.object.properties.kind ?? "").toLowerCase();
39388
+ if (kind.includes("relay")) {
39389
+ return new THREE.OctahedronGeometry(radius * 1.15, 0);
39390
+ }
39391
+ if (kind.includes("elevator") || kind.includes("skyhook")) {
39392
+ return new THREE.CylinderGeometry(radius * 0.36, radius * 0.52, radius * 2.4, 10);
39393
+ }
39394
+ if (kind.includes("station")) {
39395
+ return new THREE.TorusKnotGeometry(radius * 0.6, Math.max(radius * 0.18, 0.6), 42, 8);
39198
39396
  }
39397
+ return new THREE.BoxGeometry(radius * 1.45, radius * 1.2, radius * 1.45);
39398
+ }
39399
+ function createHalo(THREE, object, theme) {
39400
+ const radius = Math.max(object.visualRadius, 2);
39401
+ const geometry = object.object.type === "structure" ? new THREE.BoxGeometry(radius * 2.2, radius * 2.2, radius * 2.2) : new THREE.SphereGeometry(radius * 1.38, 18, 14);
39402
+ return new THREE.Mesh(geometry, new THREE.MeshBasicMaterial({
39403
+ color: theme.selectionHalo,
39404
+ transparent: true,
39405
+ opacity: 0.18,
39406
+ depthWrite: false,
39407
+ side: 1
39408
+ }));
39409
+ }
39410
+ function shouldRenderAtmosphere(object) {
39411
+ if (object.object.type !== "planet" && object.object.type !== "moon") {
39412
+ return false;
39413
+ }
39414
+ return object.object.properties.atmosphere !== void 0;
39199
39415
  }
39200
39416
  function bandGeometryForOrbit(THREE, orbit) {
39201
39417
  const thickness = Math.max(orbit.bandThickness ?? 8, 3);
39202
39418
  const points = sampleOrbitPoints(THREE, orbit, 72);
39203
39419
  const curve = new THREE.CatmullRomCurve3(points, true);
39204
- return new THREE.TubeGeometry(curve, 128, thickness * 0.28, 10, true);
39420
+ return new THREE.TubeGeometry(curve, 144, thickness * 0.18, 10, true);
39205
39421
  }
39206
39422
  function sampleOrbitPoints(THREE, orbit, segments = 96) {
39207
39423
  const points = [];
@@ -39213,6 +39429,94 @@ void main() {
39213
39429
  }
39214
39430
  return points;
39215
39431
  }
39432
+ function createStarfield(THREE, count) {
39433
+ const geometry = new THREE.BufferGeometry();
39434
+ const positions = new Float32Array(count * 3);
39435
+ const colors = new Float32Array(count * 3);
39436
+ for (let index = 0; index < count; index += 1) {
39437
+ const offset = index * 3;
39438
+ const radius = 1800 + Math.random() * 2600;
39439
+ const theta = Math.random() * Math.PI * 2;
39440
+ const phi = Math.acos(2 * Math.random() - 1);
39441
+ positions[offset] = radius * Math.sin(phi) * Math.cos(theta);
39442
+ positions[offset + 1] = radius * Math.cos(phi) * 0.45;
39443
+ positions[offset + 2] = radius * Math.sin(phi) * Math.sin(theta);
39444
+ }
39445
+ geometry.setAttribute("position", new THREE.BufferAttribute(positions, 3));
39446
+ geometry.setAttribute("color", new THREE.BufferAttribute(colors, 3));
39447
+ return new THREE.Points(geometry, new THREE.PointsMaterial({
39448
+ size: 5,
39449
+ transparent: true,
39450
+ opacity: 0.84,
39451
+ depthWrite: false,
39452
+ vertexColors: true,
39453
+ sizeAttenuation: true
39454
+ }));
39455
+ }
39456
+ function configureRenderer(renderer, THREE, quality) {
39457
+ const pixelRatioCap = quality === "high" ? 2.4 : quality === "low" ? 1.2 : 1.8;
39458
+ renderer.setPixelRatio?.(Math.min(globalThis.window?.devicePixelRatio ?? 1, pixelRatioCap));
39459
+ if ("outputColorSpace" in renderer && "SRGBColorSpace" in THREE) {
39460
+ renderer.outputColorSpace = THREE.SRGBColorSpace;
39461
+ } else if ("outputEncoding" in renderer && "sRGBEncoding" in THREE) {
39462
+ renderer.outputEncoding = THREE.sRGBEncoding;
39463
+ }
39464
+ if ("ACESFilmicToneMapping" in THREE) {
39465
+ renderer.toneMapping = THREE.ACESFilmicToneMapping;
39466
+ }
39467
+ }
39468
+ function updateEnvironment(runtime, renderOptions) {
39469
+ const theme = resolveTheme(renderOptions?.theme);
39470
+ const quality = renderOptions?.quality ?? "balanced";
39471
+ const style3d = renderOptions?.style3d ?? "symbolic";
39472
+ const count = quality === "high" ? 520 : quality === "low" ? 180 : 320;
39473
+ const positions = new Float32Array(count * 3);
39474
+ const colors = new Float32Array(count * 3);
39475
+ const bright = new runtime.THREE.Color(theme.starfield);
39476
+ const dim = new runtime.THREE.Color(theme.starfieldDim);
39477
+ for (let index = 0; index < count; index += 1) {
39478
+ const offset = index * 3;
39479
+ const radius = 1800 + Math.random() * 2600;
39480
+ const theta = Math.random() * Math.PI * 2;
39481
+ const phi = Math.acos(2 * Math.random() - 1);
39482
+ positions[offset] = radius * Math.sin(phi) * Math.cos(theta);
39483
+ positions[offset + 1] = radius * Math.cos(phi) * 0.45;
39484
+ positions[offset + 2] = radius * Math.sin(phi) * Math.sin(theta);
39485
+ const color = Math.random() > 0.72 ? bright : dim;
39486
+ colors[offset] = color.r;
39487
+ colors[offset + 1] = color.g;
39488
+ colors[offset + 2] = color.b;
39489
+ }
39490
+ runtime.scene3d.background = new runtime.THREE.Color(theme.backgroundStart);
39491
+ runtime.scene3d.fog = new runtime.THREE.FogExp2(theme.spaceFog, 85e-5);
39492
+ runtime.ambientLight.intensity = style3d === "cinematic" ? 0.18 : 0.26;
39493
+ runtime.fillLight.intensity = style3d === "cinematic" ? 0.32 : 0.4;
39494
+ runtime.rimLight.intensity = style3d === "cinematic" ? 0.28 : 0.22;
39495
+ runtime.renderer.toneMappingExposure = style3d === "cinematic" ? 1.18 : 1.08;
39496
+ runtime.starfield.geometry.setAttribute("position", new runtime.THREE.BufferAttribute(positions, 3));
39497
+ runtime.starfield.geometry.setAttribute("color", new runtime.THREE.BufferAttribute(colors, 3));
39498
+ runtime.starfield.material.opacity = quality === "high" ? 0.96 : quality === "low" ? 0.72 : 0.84;
39499
+ runtime.starfield.material.size = quality === "high" ? 5.5 : quality === "low" ? 4.25 : 5;
39500
+ }
39501
+ function applyVisualState(THREE, materials, selected, hovered) {
39502
+ for (const entry of materials) {
39503
+ const material = entry.material;
39504
+ if (!material) {
39505
+ continue;
39506
+ }
39507
+ const color = selected ? entry.selectedColor ?? entry.baseColor : hovered ? entry.hoveredColor ?? entry.baseColor : entry.baseColor;
39508
+ material.color?.set?.(new THREE.Color(color));
39509
+ if (typeof material.opacity === "number") {
39510
+ material.opacity = selected ? entry.selectedOpacity : hovered ? entry.hoveredOpacity : entry.baseOpacity;
39511
+ material.transparent = material.opacity < 0.999;
39512
+ }
39513
+ const emissive = selected ? entry.selectedEmissive ?? entry.baseEmissive : hovered ? entry.hoveredEmissive ?? entry.baseEmissive : entry.baseEmissive;
39514
+ material.emissive?.set?.(emissive ? new THREE.Color(emissive) : new THREE.Color(0));
39515
+ if ("emissiveIntensity" in material) {
39516
+ material.emissiveIntensity = selected ? entry.selectedEmissiveIntensity ?? entry.baseEmissiveIntensity ?? 0 : hovered ? entry.hoveredEmissiveIntensity ?? entry.baseEmissiveIntensity ?? 0 : entry.baseEmissiveIntensity ?? 0;
39517
+ }
39518
+ }
39519
+ }
39216
39520
  function colorForObject(object) {
39217
39521
  switch (object.object.type) {
39218
39522
  case "star":
@@ -39235,35 +39539,23 @@ void main() {
39235
39539
  return "#b8f2ff";
39236
39540
  }
39237
39541
  }
39238
- function applyVisualState(THREE, materials, baseColor, selected, hovered) {
39239
- const color = new THREE.Color(baseColor);
39240
- if (selected) {
39241
- color.offsetHSL(0, 0, 0.16);
39242
- } else if (hovered) {
39243
- color.offsetHSL(0, 0, 0.08);
39244
- }
39245
- for (const material of materials) {
39246
- if (!material) {
39247
- continue;
39248
- }
39249
- material.color?.set?.(color);
39250
- if (typeof material.opacity === "number") {
39251
- material.opacity = selected ? 0.85 : hovered ? 0.72 : material.transparent ? 0.55 : 1;
39252
- }
39253
- material.emissive?.set?.(selected ? new THREE.Color("#ffdda9") : hovered ? new THREE.Color("#cfe9ff") : new THREE.Color(0));
39254
- material.emissiveIntensity = selected ? 0.28 : hovered ? 0.14 : material.emissiveIntensity ?? 0.08;
39255
- }
39542
+ function shiftColorLightness(THREE, colorValue, delta) {
39543
+ const color = new THREE.Color(colorValue);
39544
+ color.offsetHSL(0, 0, delta);
39545
+ return `#${color.getHexString()}`;
39256
39546
  }
39257
39547
  function clearGroup(group) {
39258
39548
  while (group.children.length > 0) {
39259
39549
  const child = group.children[0];
39260
39550
  group.remove(child);
39261
- child.geometry?.dispose?.();
39262
- if (Array.isArray(child.material)) {
39263
- child.material.forEach((entry) => entry?.dispose?.());
39264
- } else {
39265
- child.material?.dispose?.();
39266
- }
39551
+ child.traverse?.((node) => {
39552
+ node.geometry?.dispose?.();
39553
+ if (Array.isArray(node.material)) {
39554
+ node.material.forEach((entry) => entry?.dispose?.());
39555
+ } else {
39556
+ node.material?.dispose?.();
39557
+ }
39558
+ });
39267
39559
  }
39268
39560
  }
39269
39561
  function ensureWebGLSupport() {
@@ -39527,6 +39819,8 @@ void main() {
39527
39819
  preset: options.preset,
39528
39820
  projection: options.projection,
39529
39821
  viewMode: options.viewMode ?? "2d",
39822
+ quality: options.quality ?? "balanced",
39823
+ style3d: options.style3d ?? "symbolic",
39530
39824
  camera: options.camera ? { ...options.camera } : null,
39531
39825
  scaleModel: options.scaleModel ? { ...options.scaleModel } : void 0,
39532
39826
  theme: options.theme,
@@ -40710,6 +41004,9 @@ void main() {
40710
41004
  if (label.hidden || !visibleObjectIds.has(label.objectId)) {
40711
41005
  continue;
40712
41006
  }
41007
+ if (is3DView() && !shouldRender3DLabel(label.objectId, visibleObjectIds)) {
41008
+ continue;
41009
+ }
40713
41010
  const point = is3DView() ? runtime3d?.projectObjectToContainer(label.objectId) ?? null : project2DScenePointToContainer({ x: label.x, y: label.y });
40714
41011
  if (!point) {
40715
41012
  continue;
@@ -40749,6 +41046,40 @@ void main() {
40749
41046
  function isEventVisible(event, visibleObjectIds) {
40750
41047
  return event.objectIds.some((objectId) => visibleObjectIds.has(objectId));
40751
41048
  }
41049
+ function shouldRender3DLabel(objectId, visibleObjectIds) {
41050
+ if (!is3DView()) {
41051
+ return true;
41052
+ }
41053
+ if (objectId === state.selectedObjectId || objectId === hoveredObjectId) {
41054
+ return true;
41055
+ }
41056
+ const object = getObjectById(objectId);
41057
+ if (!object || object.hidden || !visibleObjectIds.has(objectId)) {
41058
+ return false;
41059
+ }
41060
+ if (object.object.type === "star") {
41061
+ return true;
41062
+ }
41063
+ const selected = state.selectedObjectId ? buildObjectDetails(state.selectedObjectId) : null;
41064
+ const hovered = hoveredObjectId ? buildObjectDetails(hoveredObjectId) : null;
41065
+ const selectedFocus = selected ? /* @__PURE__ */ new Set([
41066
+ selected.objectId,
41067
+ ...selected.renderObject.ancestorIds,
41068
+ ...selected.renderObject.childIds
41069
+ ]) : null;
41070
+ const hoveredFocus = hovered ? /* @__PURE__ */ new Set([
41071
+ hovered.objectId,
41072
+ ...hovered.renderObject.ancestorIds,
41073
+ ...hovered.renderObject.childIds
41074
+ ]) : null;
41075
+ if (selectedFocus?.has(objectId) || hoveredFocus?.has(objectId)) {
41076
+ return true;
41077
+ }
41078
+ if (object.semanticGroupIds.length > 0 && object.visualRadius >= 12) {
41079
+ return true;
41080
+ }
41081
+ return object.childIds.length > 0 && object.visualRadius >= 10;
41082
+ }
40752
41083
  function createScreenLabelElement(descriptor) {
40753
41084
  const element = document.createElement("div");
40754
41085
  element.className = `wo-viewer-label wo-viewer-label-${descriptor.kind}`;
@@ -40885,7 +41216,9 @@ void main() {
40885
41216
  layers: renderOptions.layers ? { ...renderOptions.layers } : void 0,
40886
41217
  theme: renderOptions.theme && typeof renderOptions.theme === "object" ? { ...renderOptions.theme } : renderOptions.theme,
40887
41218
  activeEventId: renderOptions.activeEventId ?? null,
40888
- viewMode: renderOptions.viewMode ?? "2d"
41219
+ viewMode: renderOptions.viewMode ?? "2d",
41220
+ quality: renderOptions.quality ?? "balanced",
41221
+ style3d: renderOptions.style3d ?? "symbolic"
40889
41222
  };
40890
41223
  }
40891
41224
  function mergeRenderOptions(current, next) {
@@ -40903,7 +41236,9 @@ void main() {
40903
41236
  ...next.layers
40904
41237
  } : current.layers ? { ...current.layers } : void 0,
40905
41238
  theme: next.theme && typeof next.theme === "object" ? { ...next.theme } : next.theme ?? current.theme,
40906
- viewMode: next.viewMode ?? current.viewMode ?? "2d"
41239
+ viewMode: next.viewMode ?? current.viewMode ?? "2d",
41240
+ quality: next.quality ?? current.quality ?? "balanced",
41241
+ style3d: next.style3d ?? current.style3d ?? "symbolic"
40907
41242
  };
40908
41243
  }
40909
41244
  function hasSceneAffectingRenderOptions(options) {
@@ -41117,6 +41452,10 @@ void main() {
41117
41452
  position: absolute;
41118
41453
  display: grid;
41119
41454
  gap: 2px;
41455
+ padding: 4px 8px;
41456
+ border-radius: 999px;
41457
+ background: linear-gradient(180deg, rgba(5, 16, 26, 0.72), rgba(5, 16, 26, 0.38));
41458
+ border: 1px solid rgba(164, 194, 228, 0.16);
41120
41459
  color: #edf6ff;
41121
41460
  font-family: "Segoe UI Variable", "Segoe UI", sans-serif;
41122
41461
  line-height: 1.15;