entangled-worlds-orbiters-shared 3.1.0 → 3.3.0

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 (53) hide show
  1. package/README.md +1 -1
  2. package/dist/star/framing.d.ts +17 -6
  3. package/dist/star/framing.js +16 -52
  4. package/dist/star/index.d.ts +1 -1
  5. package/dist/star/index.js +1 -1
  6. package/dist/star/layout.js +1 -2
  7. package/dist/star/params.js +3 -2
  8. package/dist/types/config/env.d.ts +12 -0
  9. package/dist/types/config/index.d.ts +2 -0
  10. package/dist/types/config/textureConfig.d.ts +121 -0
  11. package/dist/types/core/index.d.ts +2 -0
  12. package/dist/types/core/lightFresnel.d.ts +5 -0
  13. package/dist/types/core/lights.d.ts +58 -0
  14. package/dist/types/index.d.ts +6 -0
  15. package/dist/types/loaders/bodyTextureLoader.d.ts +12 -0
  16. package/dist/types/loaders/cubeMapLoader.d.ts +70 -0
  17. package/dist/types/loaders/glbLoader.d.ts +24 -0
  18. package/dist/types/loaders/index.d.ts +5 -0
  19. package/dist/types/loaders/ktx2.d.ts +16 -0
  20. package/dist/types/moons/index.d.ts +2 -0
  21. package/dist/types/moons/moonsGeneration.d.ts +72 -0
  22. package/dist/types/moons/moonsManager.d.ts +27 -0
  23. package/dist/types/render/frameBudget.d.ts +29 -0
  24. package/dist/types/render/index.d.ts +2 -0
  25. package/dist/types/render/viewportCompositor.d.ts +58 -0
  26. package/dist/types/utils/dispose.d.ts +2 -0
  27. package/dist/types/utils/index.d.ts +2 -0
  28. package/dist/types/utils/materials.d.ts +2 -0
  29. package/dist/types/world/cloudShell.d.ts +25 -0
  30. package/dist/types/world/index.d.ts +6 -0
  31. package/dist/types/world/worldGLB.d.ts +20 -0
  32. package/dist/types/world/worldManager.d.ts +63 -0
  33. package/dist/types/world/worldNormal.d.ts +43 -0
  34. package/dist/types/world/worldTexture.d.ts +80 -0
  35. package/package.json +35 -10
  36. package/src/config/env.js +8 -0
  37. package/src/core/lights.js +12 -0
  38. package/src/loaders/bodyTextureLoader.js +7 -0
  39. package/src/loaders/cubeMapLoader.js +27 -0
  40. package/src/loaders/glbLoader.js +7 -1
  41. package/src/loaders/index.js +3 -1
  42. package/src/moons/moonsGeneration.js +32 -2
  43. package/src/moons/moonsManager.js +1 -1
  44. package/src/star/framing.ts +17 -55
  45. package/src/star/index.ts +1 -1
  46. package/src/star/layout.ts +1 -2
  47. package/src/star/params.ts +3 -2
  48. package/src/world/cloudShell.js +13 -0
  49. package/src/world/index.js +3 -1
  50. package/src/world/worldGLB.js +12 -1
  51. package/src/world/worldManager.js +32 -1
  52. package/src/world/worldNormal.js +19 -0
  53. package/src/world/worldTexture.js +41 -0
package/README.md CHANGED
@@ -76,7 +76,7 @@ import * as EntangledWorlds from 'entangled-worlds-orbiters-shared';
76
76
  - `DEFAULT_STAR_PARAMS`, `normalizeStarParams` - Author params (temperature, radius, luminosity, activity, multiplicity, separation, seed, placement) + validator for stored params
77
77
  - `starParamsFromStellarData` - Catalogue host-star data (Teff, radius, mass, luminosity, spectral type, star count or a "(AB)b" name suffix) → params
78
78
  - `blackbodyColor`, `spectralClass`, `starLayout`, `starPositions` - Pure colour / layout / orbit helpers
79
- - `getFocus` (on `createStar` / `StarManager`), `framingDistance`, `defaultStarPlacement` - Camera focus on the system barycentre and host-camera-aware placement
79
+ - `getFocus` (on `createStar` / `StarManager`), `framingDistance`, `defaultStarPlacement` - Camera focus on the system barycentre and the default placement (over the camera's shoulder, so worlds open on their day side)
80
80
  - The star lights the scene: `StarManager` key + camera-side fill lights, `setLighting`, `lightingFromLegacy`, `DEFAULT_AMBIENT` (pair with `setHemisphereEnabled(scene, false)` from `./core`; moons `emissiveLift`, GLB `emissiveIntensity` for night sides)
81
81
  - Real binaries (close pair from component masses, a distant companion for wide pairs) and an optional debris disc; `mainSequenceTemperature` / `Radius` / `Luminosity` helpers
82
82
  - Perf + look harness: `npm run bench:star` (see `bench/star/results/REPORT.md`)
@@ -19,13 +19,24 @@ export interface StarPlacementInput {
19
19
  export interface StarPlacement {
20
20
  azimuth: number;
21
21
  elevation: number;
22
- /** False when no in-frame spot clears the planet (it fills the frame); the star is then just above it. */
22
+ /** Always true (kept for older hosts): the spot is behind any camera closer than ~0.79 × the star's distance (≥ 15.8 units). */
23
23
  fits: boolean;
24
24
  }
25
25
  /**
26
- * Azimuth / elevation that put the primary's disc (plus its bright glow) just outside the planet's
27
- * angular disc and inside the frame of a camera on +Z looking at the origin — upper-right when there
28
- * is room, swinging towards straight up as the frame narrows (portrait). Hosts use it only when the
29
- * author has not set azimuth/elevation; `DEFAULT_STAR_PARAMS` stays the host-agnostic fallback.
26
+ * The opening light: the star over the camera's right shoulder (behind it, a little up and to the
27
+ * right), so the planet opens on its day side — about 90% of the face the camera sees is lit, the
28
+ * night edge on the left — and the star itself shows when the viewer orbits round. 145° / 15° is
29
+ * ~38° off the view axis. It is `DEFAULT_STAR_PARAMS`' direction, so a host with no camera gets it too.
30
30
  */
31
- export declare function defaultStarPlacement(input: StarPlacementInput): StarPlacement;
31
+ export declare const OVER_THE_SHOULDER: {
32
+ readonly azimuth: number;
33
+ readonly elevation: number;
34
+ };
35
+ /**
36
+ * Azimuth / elevation of the default star for a camera on +Z looking at the origin: over the
37
+ * camera's shoulder (`OVER_THE_SHOULDER`), so the world opens lit. The star sits ≥ 0.79 × its
38
+ * distance (≥ 15.8 units) out on +Z, behind any host camera, so the frame never changes it. Hosts
39
+ * use it only when the author has not set azimuth/elevation; `DEFAULT_STAR_PARAMS` stays the
40
+ * host-agnostic fallback.
41
+ */
42
+ export declare function defaultStarPlacement(_input?: StarPlacementInput): StarPlacement;
@@ -1,7 +1,6 @@
1
1
  // Camera framing helpers (pure, no three): how far a camera must sit to fit a focus sphere, and a
2
- // host-camera-aware default placement for the star so it reads beside the planet, not behind it.
3
- import { FOCUS_GLOW_RADII } from './layout.js';
4
- import { DEFAULT_STAR_PARAMS, STAR_PARAM_RANGES } from './params.js';
2
+ // default placement for the star that lights the planet's side facing the camera.
3
+ import { DEFAULT_STAR_PARAMS } from './params.js';
5
4
  const DEG = Math.PI / 180;
6
5
  /**
7
6
  * Camera distance (from the sphere's centre) at which a sphere of `radius` fits inside both the
@@ -15,54 +14,19 @@ export function framingDistance(radius, fovDeg, aspect, margin = 1.15) {
15
14
  return r / Math.sin(Math.min(halfV, halfH));
16
15
  }
17
16
  /**
18
- * Azimuth / elevation that put the primary's disc (plus its bright glow) just outside the planet's
19
- * angular disc and inside the frame of a camera on +Z looking at the origin — upper-right when there
20
- * is room, swinging towards straight up as the frame narrows (portrait). Hosts use it only when the
21
- * author has not set azimuth/elevation; `DEFAULT_STAR_PARAMS` stays the host-agnostic fallback.
17
+ * The opening light: the star over the camera's right shoulder (behind it, a little up and to the
18
+ * right), so the planet opens on its day side — about 90% of the face the camera sees is lit, the
19
+ * night edge on the left — and the star itself shows when the viewer orbits round. 145° / 15° is
20
+ * ~38° off the view axis. It is `DEFAULT_STAR_PARAMS`' direction, so a host with no camera gets it too.
22
21
  */
23
- export function defaultStarPlacement(input) {
24
- const fov = Number.isFinite(input.fovDeg) && input.fovDeg > 0 ? input.fovDeg : 45;
25
- const aspect = Number.isFinite(input.aspect) && input.aspect > 0 ? input.aspect : 1;
26
- const cd = Number.isFinite(input.cameraDistance) && input.cameraDistance > 0 ? input.cameraDistance : 4;
27
- const pr = Math.max(0, Number.isFinite(input.planetRadius) ? input.planetRadius : 1);
28
- const starRadius = input.starRadius ?? DEFAULT_STAR_PARAMS.radius;
29
- const [dLo, dHi] = STAR_PARAM_RANGES.distance;
30
- const D = Math.min(dHi, Math.max(dLo, input.distance ?? DEFAULT_STAR_PARAMS.distance));
31
- // Work on the image plane at unit distance (tangent units): positions and radii both scale by tan.
32
- const tanV = Math.tan((fov * DEG) / 2);
33
- const tanH = tanV * aspect;
34
- const planetTan = cd > pr ? pr / Math.sqrt(cd * cd - pr * pr) : Infinity;
35
- const starTan = (starRadius * FOCUS_GLOW_RADII) / Math.sqrt(D * D + cd * cd);
36
- const rho = planetTan + starTan * 1.35; // centre distance from the view axis: disc + glow clear the planet
37
- const maxX = tanH * 0.97 - starTan * 0.5; // centre in frame, disc mostly in frame
38
- const maxY = tanV * 0.97 - starTan * 0.5;
39
- let x = 0;
40
- let y = Number.isFinite(rho) ? rho : maxY;
41
- let fits = false;
42
- for (let phiDeg = 30; phiDeg <= 90; phiDeg += 2) {
43
- const px = rho * Math.cos(phiDeg * DEG);
44
- const py = rho * Math.sin(phiDeg * DEG);
45
- if (px <= maxX && py <= maxY) {
46
- x = px;
47
- y = py;
48
- fits = true;
49
- break;
50
- }
51
- }
52
- // Ray from the camera through (x, y) on the image plane, hit on the sphere |P| = D.
53
- const len = Math.hypot(x, y, 1);
54
- const ux = x / len;
55
- const uy = y / len;
56
- const uz = -1 / len;
57
- const cu = cd * uz; // C · u with C = (0, 0, cd)
58
- const t = -cu + Math.sqrt(Math.max(0, cu * cu - cd * cd + D * D));
59
- const px = ux * t;
60
- const py = uy * t;
61
- const pz = cd + uz * t;
62
- const round = (v) => Math.round(v * 10) / 10;
63
- return {
64
- azimuth: round(Math.atan2(px, -pz) / DEG),
65
- elevation: round(Math.asin(Math.max(-1, Math.min(1, py / D))) / DEG),
66
- fits,
67
- };
22
+ export const OVER_THE_SHOULDER = { azimuth: DEFAULT_STAR_PARAMS.azimuth, elevation: DEFAULT_STAR_PARAMS.elevation };
23
+ /**
24
+ * Azimuth / elevation of the default star for a camera on +Z looking at the origin: over the
25
+ * camera's shoulder (`OVER_THE_SHOULDER`), so the world opens lit. The star sits ≥ 0.79 × its
26
+ * distance (≥ 15.8 units) out on +Z, behind any host camera, so the frame never changes it. Hosts
27
+ * use it only when the author has not set azimuth/elevation; `DEFAULT_STAR_PARAMS` stays the
28
+ * host-agnostic fallback.
29
+ */
30
+ export function defaultStarPlacement(_input) {
31
+ return { azimuth: OVER_THE_SHOULDER.azimuth, elevation: OVER_THE_SHOULDER.elevation, fits: true };
68
32
  }
@@ -9,7 +9,7 @@ export type { StarParams, StarMultiplicity, SpectralClass } from './params.js';
9
9
  export { blackbodyColor } from './blackbody.js';
10
10
  export type { RGB } from './blackbody.js';
11
11
  export { starLayout, starPositions, starSystemPosition, starFocusRadius, flareAt } from './layout.js';
12
- export { framingDistance, defaultStarPlacement } from './framing.js';
12
+ export { framingDistance, defaultStarPlacement, OVER_THE_SHOULDER } from './framing.js';
13
13
  export type { StarPlacement, StarPlacementInput } from './framing.js';
14
14
  export type { StarLayout, LaidOutStar, StarOrbit, StarRole, FlareState } from './layout.js';
15
15
  export { starParamsFromStellarData, temperatureFromSpectralType, starCountFromPlanetName, luminosityClassFromSpectralType, mainSequenceLuminosity, mainSequenceRadius, mainSequenceTemperature, separationFromAu, youthFromAge, } from './stellarData.js';
@@ -8,7 +8,7 @@ export { STAR_QUALITY, normalizeStarQuality } from './quality.js';
8
8
  export { DEFAULT_STAR_PARAMS, STAR_PARAM_RANGES, normalizeStarParams, spectralClass, } from './params.js';
9
9
  export { blackbodyColor } from './blackbody.js';
10
10
  export { starLayout, starPositions, starSystemPosition, starFocusRadius, flareAt } from './layout.js';
11
- export { framingDistance, defaultStarPlacement } from './framing.js';
11
+ export { framingDistance, defaultStarPlacement, OVER_THE_SHOULDER } from './framing.js';
12
12
  export { starParamsFromStellarData, temperatureFromSpectralType, starCountFromPlanetName, luminosityClassFromSpectralType, mainSequenceLuminosity, mainSequenceRadius, mainSequenceTemperature, separationFromAu, youthFromAge, } from './stellarData.js';
13
13
  export { DEFAULT_AMBIENT, DEFAULT_STAR_LIGHTING, systemLightColor, keyLightIntensity, fillLightIntensity, lightingFromLegacy, LEGACY_AMBIENT_REFERENCE, LEGACY_AMBIENT_MAX, normalizeStarLighting, parseTint, } from './lighting.js';
14
14
  export { minStarDistance, maxStarDistance, SKY_MARGIN_FRACTION, sceneOuterRadius, STAR_DISTANCE_MARGIN, MAX_STAR_ANGULAR_RADIUS_DEG, } from './bounds.js';
@@ -124,8 +124,7 @@ function distantFor(p, reach) {
124
124
  if (!p.distantCompanion)
125
125
  return null;
126
126
  const r = createLcg(`${p.seed}|distant`);
127
- // Default: up and back towards the frame's centre from the host — above the planet rather than
128
- // behind it, and inside a typical frame when the host itself is placed beside the planet.
127
+ // Default: a little above the host and swung towards the scene's −Z axis — never on top of it.
129
128
  const inward = p.azimuth >= 0 ? -1 : 1;
130
129
  let azimuth = p.distantAzimuth ?? wrapDeg(p.azimuth + inward * lerp(12, 22, r()));
131
130
  let elevation = p.distantElevation ?? Math.min(60, p.elevation + lerp(6, 11, r()));
@@ -10,8 +10,9 @@ export const DEFAULT_STAR_PARAMS = Object.freeze({
10
10
  separation: 2,
11
11
  seed: 1,
12
12
  distance: 20,
13
- azimuth: 16,
14
- elevation: 10,
13
+ // Over the camera's shoulder (`OVER_THE_SHOULDER` in framing.ts): worlds open on their day side.
14
+ azimuth: 145,
15
+ elevation: 15,
15
16
  companionTemperatureK: 0,
16
17
  companionRadius: 0,
17
18
  distantCompanion: false,
@@ -0,0 +1,12 @@
1
+ /** @param {string | null | undefined} url */
2
+ export function setPublicBase(url: string | null | undefined): void;
3
+ /** @returns {string} */
4
+ export function getPublicBase(): string;
5
+ /**
6
+ * @param {string} path root-relative, relative, or an absolute http(s) URL (returned unchanged).
7
+ * An empty string comes back as is.
8
+ * @returns {string}
9
+ */
10
+ export function resolvePublicUrl(path: string): string;
11
+ /** @type {string | null} */
12
+ export let __PUBLIC_BASE: string | null;
@@ -0,0 +1,2 @@
1
+ export { loadTextureConfig, getTextureOptions, getTextureChoices, getDefaultTexture, getSkyboxOptions, getDefaultSkybox, getTextureFallbackUrl } from "./textureConfig.js";
2
+ export { setPublicBase, getPublicBase, resolvePublicUrl } from "./env.js";
@@ -0,0 +1,121 @@
1
+ /**
2
+ * Load texture configuration from remote endpoint or use passed config
3
+ * @param {string} configUrl - Override config URL
4
+ * @param {boolean} useCache - Whether to use cached config if available
5
+ * @returns {Promise<Object>} Texture configuration object
6
+ */
7
+ export function loadTextureConfig(configUrl?: string, useCache?: boolean): Promise<Object>;
8
+ /**
9
+ * @typedef {Object} TextureChoice
10
+ * @property {string} id Catalogue id.
11
+ * @property {string} label Human-readable name.
12
+ * @property {string} url Resolved texture URL (the .ktx2 the world renders).
13
+ * @property {string|null} still Resolved URL of the item's picture — a small thumbnail suitable
14
+ * for showing the texture in a picker. `null` when the catalogue names none.
15
+ *
16
+ * Every catalogue item IS a picture, so a chooser should show it rather than its name. `still` is
17
+ * read straight from the catalogue and never derived from `filename`: the stills do not share one
18
+ * extension, and a wrong guess fails silently (a broken `<img>` just shows nothing).
19
+ */
20
+ /**
21
+ * Get the full choice records for a category (clouds, moons, etc.) — label, texture URL, and the
22
+ * picture to show for it. The single resolution point for a category's items; `getTextureOptions`
23
+ * is the flat `{ label: url }` projection of this.
24
+ * @param {string} category - Texture category (clouds, moons)
25
+ * @param {Object} config - Optional pre-loaded config object
26
+ * @returns {Promise<TextureChoice[]>} Choice records in catalogue order
27
+ */
28
+ export function getTextureChoices(category: string, config?: Object): Promise<TextureChoice[]>;
29
+ /**
30
+ * Get texture options for a specific category (clouds, moons, etc.)
31
+ * @param {string} category - Texture category (clouds, moons, skyboxes)
32
+ * @param {Object} config - Optional pre-loaded config object
33
+ * @returns {Promise<Object>} Options object suitable for dropdown option lists
34
+ */
35
+ export function getTextureOptions(category: string, config?: Object): Promise<Object>;
36
+ /**
37
+ * Get default texture for a category
38
+ * @param {string} category - Texture category
39
+ * @param {Object} config - Optional pre-loaded config object
40
+ * @returns {Promise<string>} Default texture path
41
+ */
42
+ export function getDefaultTexture(category: string, config?: Object): Promise<string>;
43
+ /**
44
+ * Return the configured fallback URL for a given texture if defined.
45
+ * @param {string} category - Texture category (clouds, moons, etc.)
46
+ * @param {string} textureUrl - Resolved texture URL
47
+ * @param {Object|null} config - Optional texture config override
48
+ * @returns {string|null} Resolved fallback texture URL or null
49
+ */
50
+ export function getTextureFallbackUrl(category: string, textureUrl: string, config?: Object | null): string | null;
51
+ /**
52
+ * Get texture by ID within a category
53
+ * @param {string} category - Texture category
54
+ * @param {string} textureId - Texture ID
55
+ * @param {Object} config - Optional pre-loaded config object
56
+ * @returns {Promise<Object|null>} Texture definition object
57
+ */
58
+ export function getTextureById(category: string, textureId: string, config?: Object): Promise<Object | null>;
59
+ /**
60
+ * Get full URL for a texture
61
+ * @param {string} category - Texture category
62
+ * @param {string} textureId - Texture ID
63
+ * @param {Object} config - Optional pre-loaded config object
64
+ * @returns {Promise<string|null>} Full texture URL
65
+ */
66
+ export function getTextureUrl(category: string, textureId: string, config?: Object): Promise<string | null>;
67
+ /**
68
+ * Search textures by tags
69
+ * @param {string} category - Texture category
70
+ * @param {string[]} tags - Tags to search for
71
+ * @param {Object} config - Optional pre-loaded config object
72
+ * @returns {Promise<Object[]>} Array of matching texture definitions
73
+ */
74
+ export function searchTexturesByTags(category: string, tags: string[], config?: Object): Promise<Object[]>;
75
+ /**
76
+ * Get skybox options formatted for GUI dropdowns
77
+ * @param {Object} config - Optional pre-loaded config object
78
+ * @returns {Promise<Object>} Options object with labels and IDs
79
+ */
80
+ export function getSkyboxOptions(config?: Object): Promise<Object>;
81
+ /**
82
+ * Get default skybox ID
83
+ * @param {Object} config - Optional pre-loaded config object
84
+ * @returns {Promise<string>} Default skybox ID
85
+ */
86
+ export function getDefaultSkybox(config?: Object): Promise<string>;
87
+ /**
88
+ * Reload configuration (force refresh)
89
+ * @param {string} configUrl - Override config URL
90
+ * @returns {Promise<Object>} Fresh configuration object
91
+ */
92
+ export function reloadTextureConfig(configUrl?: string): Promise<Object>;
93
+ /**
94
+ * Get configuration metadata
95
+ * @param {Object} config - Optional pre-loaded config object
96
+ * @returns {Promise<Object>} Configuration metadata
97
+ */
98
+ export function getConfigMetadata(config?: Object): Promise<Object>;
99
+ export type TextureChoice = {
100
+ /**
101
+ * Catalogue id.
102
+ */
103
+ id: string;
104
+ /**
105
+ * Human-readable name.
106
+ */
107
+ label: string;
108
+ /**
109
+ * Resolved texture URL (the .ktx2 the world renders).
110
+ */
111
+ url: string;
112
+ /**
113
+ * Resolved URL of the item's picture — a small thumbnail suitable
114
+ * for showing the texture in a picker. `null` when the catalogue names none.
115
+ *
116
+ * Every catalogue item IS a picture, so a chooser should show it rather than its name. `still` is
117
+ * read straight from the catalogue and never derived from `filename`: the stills do not share one
118
+ * extension, and a wrong guess fails silently (a broken `<img>` just shows nothing).
119
+ */
120
+ still: string | null;
121
+ };
@@ -0,0 +1,2 @@
1
+ export { getLightFresnel } from "./lightFresnel.js";
2
+ export { ensureDefaultLights, updateDefaultLights, setHemisphereEnabled } from "./lights.js";
@@ -0,0 +1,5 @@
1
+ export function getLightFresnel({ rimHex, facingHex }?: {
2
+ rimHex?: number | undefined;
3
+ facingHex?: number | undefined;
4
+ }): THREE.ShaderMaterial;
5
+ import * as THREE from "three";
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Ensure a scene has the default hemisphere + ambient lights. Idempotent PER SCENE.
3
+ *
4
+ * A multi-orbiter realm renders N independent scenes through one renderer. A module-level
5
+ * singleton light is wrong twice over — it's only added to the FIRST scene, and a THREE light can
6
+ * live in just one scene (adding it to a second removes it from the first), so scenes 2..N rendered
7
+ * DARK ("only one entangled world"). So create + add the lights PER scene, keyed by name so a
8
+ * re-call on the same scene is a no-op. Single-orbiter (one scene) is unchanged.
9
+ *
10
+ * @param {import('three').Scene} scene
11
+ * @returns {{ hemi: import('three').HemisphereLight, ambient: import('three').AmbientLight }}
12
+ */
13
+ export function ensureDefaultLights(scene: import("three").Scene): {
14
+ hemi: import("three").HemisphereLight;
15
+ ambient: import("three").AmbientLight;
16
+ };
17
+ /**
18
+ * Apply a lighting patch to ONE scene's default lights.
19
+ *
20
+ * The scene is required. This used to address module-global light refs pointing at whichever scene
21
+ * was ensured last, so on a page rendering several scenes every lighting update landed on one of
22
+ * them — a shared-renderer page (N feed worlds, N orbiters) lit the wrong world.
23
+ *
24
+ * Every field is optional; an omitted one leaves that part of the lighting as it is.
25
+ *
26
+ * @param {import('three').Scene} scene
27
+ * @param {object} [patch]
28
+ * @param {number} [patch.ambientIntensity] ambient light intensity (clamped at 0)
29
+ * @param {number} [patch.diffuseIntensity] hemisphere light intensity (clamped at 0)
30
+ * @param {import('three').Color | number | string} [patch.lightColor] hemisphere light colour
31
+ * @param {{ x: number, y?: number, z?: number }} [patch.lightDirection] hemisphere light direction
32
+ * @param {boolean} [patch.hemisphere] switch the hemisphere light on or off
33
+ */
34
+ export function updateDefaultLights(scene: import("three").Scene, { ambientIntensity, diffuseIntensity, lightColor, lightDirection, hemisphere }?: {
35
+ ambientIntensity?: number | undefined;
36
+ diffuseIntensity?: number | undefined;
37
+ lightColor?: string | number | THREE.Color | undefined;
38
+ lightDirection?: {
39
+ x: number;
40
+ y?: number;
41
+ z?: number;
42
+ } | undefined;
43
+ hemisphere?: boolean | undefined;
44
+ }): void;
45
+ /**
46
+ * Switch a scene's default hemisphere light on or off (the ambient light is untouched). Hosts turn it
47
+ * off when a directional light — the star — lights the scene, so night sides can read.
48
+ *
49
+ * @param {import('three').Scene} scene
50
+ * @param {boolean} on
51
+ */
52
+ export function setHemisphereEnabled(scene: import("three").Scene, on: boolean): void;
53
+ /** @param {import('three').Scene} scene */
54
+ export function addDefaultLights(scene: import("three").Scene): {
55
+ hemi: import("three").HemisphereLight;
56
+ ambient: import("three").AmbientLight;
57
+ };
58
+ import * as THREE from 'three';
@@ -0,0 +1,6 @@
1
+ export * from "./world/index.js";
2
+ export * from "./moons/index.js";
3
+ export * from "./loaders/index.js";
4
+ export * from "./core/index.js";
5
+ export * from "./config/index.js";
6
+ export * from "./utils/index.js";
@@ -0,0 +1,12 @@
1
+ /**
2
+ * @param {string} url `.ktx2` goes through the renderer's KTX2 transcoder; anything else through
3
+ * THREE.TextureLoader.
4
+ * @param {object} [opts]
5
+ * @param {AbortSignal} [opts.signal] an abort during the load rejects the promise.
6
+ * @param {import('three').WebGLRenderer | null} [opts.renderer] REQUIRED for a `.ktx2` url.
7
+ */
8
+ export function loadTexture(url: string, { signal, renderer }?: {
9
+ signal?: AbortSignal | undefined;
10
+ renderer?: THREE.WebGLRenderer | null | undefined;
11
+ }): Promise<any>;
12
+ import * as THREE from 'three';
@@ -0,0 +1,70 @@
1
+ export function normalizeSkycubeName(raw: any): string;
2
+ export function buildSkycubefaceKeys(skycubeId: any, extension?: string): {
3
+ right: string;
4
+ left: string;
5
+ top: string;
6
+ bottom: string;
7
+ front: string;
8
+ back: string;
9
+ };
10
+ /**
11
+ * Main loader function - always uses explicit face URLs.
12
+ * `renderer` binds the KTX2 transcoder to the right WebGL context. REQUIRED (3.0) when the KTX2
13
+ * path is taken — the old `window.__EW_RENDERER` fallback is gone; thread the owning view's
14
+ * renderer. (The PNG/JPG path is renderer-agnostic, so its absence only bites on KTX2.)
15
+ *
16
+ * @param {object} [opts]
17
+ * @param {{ right: string, left: string, top: string, bottom: string, front: string, back: string }} [opts.faceKeys]
18
+ * explicit face URLs; when absent they are built from `skycubeName`.
19
+ * @param {string} [opts.skycubeName] skybox id (e.g. `'001'`), used when `faceKeys` is absent.
20
+ * @param {boolean} [opts.preferKTX2=true]
21
+ * @param {boolean} [opts.forceLow] force (or forbid) the low-resolution faces; omitted = detect the device.
22
+ * @param {import('three').WebGLRenderer | null} [opts.renderer]
23
+ * @returns {Promise<SkyboxResource>} pass it to `setBackground`.
24
+ */
25
+ export function loadCubeMap({ faceKeys, skycubeName, preferKTX2, forceLow, renderer }?: {
26
+ faceKeys?: {
27
+ right: string;
28
+ left: string;
29
+ top: string;
30
+ bottom: string;
31
+ front: string;
32
+ back: string;
33
+ } | undefined;
34
+ skycubeName?: string | undefined;
35
+ preferKTX2?: boolean | undefined;
36
+ forceLow?: boolean | undefined;
37
+ renderer?: THREE.WebGLRenderer | null | undefined;
38
+ }): Promise<SkyboxResource>;
39
+ /**
40
+ * What `loadCubeMap` resolves to: a skybox mesh wrapper (the KTX2 path) or a cube texture (the
41
+ * PNG/JPG path). Read `mesh` to tell them apart.
42
+ * @typedef {{ __ewType: 'skybox-mesh', mesh: import('three').Mesh } | (import('three').CubeTexture & { mesh?: undefined })} SkyboxResource
43
+ */
44
+ /**
45
+ * Show a loaded skybox as the scene's background. A previous skybox mesh or cube-texture background is
46
+ * disposed (a plain-texture background is not). Takes what `loadCubeMap` resolved to (or any texture);
47
+ * anything else, `null` included, clears it.
48
+ *
49
+ * @param {import('three').Scene} scene
50
+ * @param {unknown} resource
51
+ */
52
+ export function setBackground(scene: import("three").Scene, resource: unknown): void;
53
+ /**
54
+ * @param {import('three').Scene} scene
55
+ * @param {{ renderer?: import('three').WebGLRenderer | null }} [opts]
56
+ */
57
+ export function initSpaceScene(scene: import("three").Scene, { renderer }?: {
58
+ renderer?: import("three").WebGLRenderer | null;
59
+ }): Promise<void>;
60
+ /**
61
+ * What `loadCubeMap` resolves to: a skybox mesh wrapper (the KTX2 path) or a cube texture (the
62
+ * PNG/JPG path). Read `mesh` to tell them apart.
63
+ */
64
+ export type SkyboxResource = {
65
+ __ewType: "skybox-mesh";
66
+ mesh: import("three").Mesh;
67
+ } | (import("three").CubeTexture & {
68
+ mesh?: undefined;
69
+ });
70
+ import * as THREE from 'three';
@@ -0,0 +1,24 @@
1
+ /**
2
+ * @param {string} url
3
+ * @param {object} [opts] `renderer` is REQUIRED at runtime (throws without one).
4
+ * @param {number} [opts.retries=3] load attempts before giving up.
5
+ * @param {AbortSignal} [opts.signal] rejects the load on abort.
6
+ * @param {(event: ProgressEvent) => void} [opts.onProgress]
7
+ * @param {object} [opts.applyFlags] material flags (`forceOpaque`, `wireframe`) applied to every mesh.
8
+ * @param {boolean} [opts.withCredentials=false]
9
+ * @param {number} [opts.emissiveIntensity=1] self-glow of textured materials (their map doubles as the
10
+ * emissive map). 1 keeps the old flat-lit look; with a directional star light pass ≈0.1–0.15 so the
11
+ * night side reads.
12
+ * @param {import('three').WebGLRenderer} opts.renderer REQUIRED (3.0): the renderer this GLB's
13
+ * textures bind to. A page with several renderers has no single correct global, so the old
14
+ * `window.__EW_RENDERER` fallback is gone — thread the owning view's renderer.
15
+ */
16
+ export function loadGLB(url: string, { retries, signal, onProgress, applyFlags, withCredentials, renderer, emissiveIntensity }?: {
17
+ retries?: number | undefined;
18
+ signal?: AbortSignal | undefined;
19
+ onProgress?: ((event: ProgressEvent) => void) | undefined;
20
+ applyFlags?: object | undefined;
21
+ withCredentials?: boolean | undefined;
22
+ emissiveIntensity?: number | undefined;
23
+ renderer: import("three").WebGLRenderer;
24
+ }): Promise<any>;
@@ -0,0 +1,5 @@
1
+ export { loadGLB } from "./glbLoader.js";
2
+ export { loadTexture } from "./bodyTextureLoader.js";
3
+ export type SkyboxResource = import("./cubeMapLoader.js").SkyboxResource;
4
+ export { loadCubeMap, setBackground, normalizeSkycubeName, buildSkycubefaceKeys } from "./cubeMapLoader.js";
5
+ export { getKTX2LoaderForRenderer, disposeKTX2LoaderForRenderer } from "./ktx2.js";
@@ -0,0 +1,16 @@
1
+ /**
2
+ * The KTX2Loader bound to `renderer` — REQUIRED (3.0). The old `window.__EW_RENDERER` fallback let
3
+ * call sites compile while silently binding the transcoder to whichever renderer a page published
4
+ * last; on any multi-view page that meant untextured worlds. Every caller now threads its own
5
+ * renderer, so a missing one is a caller bug and fails loudly here rather than as a dark planet.
6
+ * @param {import('three').WebGLRenderer} renderer
7
+ * @returns {KTX2Loader}
8
+ */
9
+ export function getKTX2LoaderForRenderer(renderer: import("three").WebGLRenderer): KTX2Loader;
10
+ /**
11
+ * Dispose and forget the KTX2Loader for a renderer — call on that renderer's / scene's teardown so the
12
+ * transcoder workers are terminated (avoids leaking worker threads + GPU memory across view churn).
13
+ * @param {import('three').WebGLRenderer} renderer
14
+ */
15
+ export function disposeKTX2LoaderForRenderer(renderer: import("three").WebGLRenderer): void;
16
+ import { KTX2Loader } from 'three/examples/jsm/loaders/KTX2Loader.js';
@@ -0,0 +1,2 @@
1
+ export { MoonsManager } from "./moonsManager.js";
2
+ export { generateMoons, DEFAULT_MOON_TEX_KTX2, DEFAULT_MOON_TEX_PNG, MOON_BASE_RADIUS, MOON_SCALE_MIN, MOON_SCALE_SPAN, MOON_MAX_RADIUS, MOON_ORBIT_SCATTER, MOON_HEIGHT_SCATTER } from "./moonsGeneration.js";
@@ -0,0 +1,72 @@
1
+ /**
2
+ * @param {object} [opts]
3
+ * @param {number} [opts.count=0] 0 or less resolves to null.
4
+ * @param {number} [opts.orbitRadius=1.5]
5
+ * @param {string} [opts.textureKTX2]
6
+ * @param {string} [opts.texturePNG]
7
+ * @param {string | null} [opts.textureFallback] used instead of `texturePNG` when set.
8
+ * @param {boolean} [opts.useTexture=true]
9
+ * @param {number | string | null} [opts.seed] deterministic positions when set.
10
+ * @param {string | null} [opts.textureKey] replaces `textureKTX2` when set (the fallback stays
11
+ * `textureFallback` or `texturePNG`).
12
+ * @param {string} [opts.moonColor='#ffffff']
13
+ * @param {number} [opts.brightness=1.15]
14
+ * @param {number} [opts.emissiveColor=0x333333]
15
+ * @param {number} [opts.emissiveIntensity=0.18]
16
+ * @param {number} [opts.emissiveLift] night-side self-glow 0..1; omitted = `emissiveIntensity`.
17
+ * @param {number} [opts.roughness=0.85]
18
+ * @param {number} [opts.metalness=0]
19
+ * @param {boolean} [opts.debug=true]
20
+ * @param {boolean} [opts.smooth=true]
21
+ * @param {number} [opts.geometryWidthSegments=16]
22
+ * @param {number} [opts.geometryHeightSegments=12]
23
+ * @param {boolean} [opts.useBumpFromColor=true]
24
+ * @param {number} [opts.bumpScale=0.15]
25
+ * @param {boolean} [opts.debugShowUvGrid=false]
26
+ * @param {boolean} [opts.debugShowReferenceMoon=false]
27
+ * @param {number} [opts.referenceMoonScale=0.25]
28
+ * @param {boolean} [opts.useEmissiveMapFromTexture=true]
29
+ * @param {import('three').WebGLRenderer | null} [opts.renderer] binds the KTX2 moon texture.
30
+ */
31
+ export function generateMoons({ count, orbitRadius, textureKTX2, texturePNG, textureFallback, useTexture, seed, textureKey, moonColor, brightness, emissiveColor, emissiveIntensity, emissiveLift, roughness, metalness, debug, smooth, geometryWidthSegments, geometryHeightSegments, useBumpFromColor, bumpScale, debugShowUvGrid, debugShowReferenceMoon, referenceMoonScale, useEmissiveMapFromTexture, renderer, }?: {
32
+ count?: number | undefined;
33
+ orbitRadius?: number | undefined;
34
+ textureKTX2?: string | undefined;
35
+ texturePNG?: string | undefined;
36
+ textureFallback?: string | null | undefined;
37
+ useTexture?: boolean | undefined;
38
+ seed?: string | number | null | undefined;
39
+ textureKey?: string | null | undefined;
40
+ moonColor?: string | undefined;
41
+ brightness?: number | undefined;
42
+ emissiveColor?: number | undefined;
43
+ emissiveIntensity?: number | undefined;
44
+ emissiveLift?: number | undefined;
45
+ roughness?: number | undefined;
46
+ metalness?: number | undefined;
47
+ debug?: boolean | undefined;
48
+ smooth?: boolean | undefined;
49
+ geometryWidthSegments?: number | undefined;
50
+ geometryHeightSegments?: number | undefined;
51
+ useBumpFromColor?: boolean | undefined;
52
+ bumpScale?: number | undefined;
53
+ debugShowUvGrid?: boolean | undefined;
54
+ debugShowReferenceMoon?: boolean | undefined;
55
+ referenceMoonScale?: number | undefined;
56
+ useEmissiveMapFromTexture?: boolean | undefined;
57
+ renderer?: THREE.WebGLRenderer | null | undefined;
58
+ }): Promise<THREE.Group<THREE.Object3DEventMap> | null>;
59
+ export const DEFAULT_MOON_TEX_KTX2: string;
60
+ export const DEFAULT_MOON_TEX_PNG: string;
61
+ /** Radius of the shared moon sphere before per-moon scale. */
62
+ export const MOON_BASE_RADIUS: 0.1;
63
+ /** Per-moon scale range: MIN + random * SPAN. */
64
+ export const MOON_SCALE_MIN: 0.07;
65
+ export const MOON_SCALE_SPAN: 0.15;
66
+ /** Largest moon radius in scene units (base radius × largest scale). */
67
+ export const MOON_MAX_RADIUS: number;
68
+ /** Each moon's orbit radius varies by ± half of this around the requested orbit. */
69
+ export const MOON_ORBIT_SCATTER: 0.22;
70
+ /** Each moon sits up to ± half of this above or below the orbit plane. */
71
+ export const MOON_HEIGHT_SCATTER: 0.12;
72
+ import * as THREE from 'three';
@@ -0,0 +1,27 @@
1
+ export class MoonsManager {
2
+ /**
3
+ * @param {import('three').Scene} scene
4
+ * @param {object} [opts]
5
+ * @param {import('three').WebGLRenderer | null} [opts.renderer] renderer the moon texture binds to (KTX2).
6
+ */
7
+ constructor(scene: import("three").Scene, { renderer }?: {
8
+ renderer?: THREE.WebGLRenderer | null | undefined;
9
+ });
10
+ scene: THREE.Scene;
11
+ renderer: THREE.WebGLRenderer | null | undefined;
12
+ group: THREE.Group<THREE.Object3DEventMap> | null;
13
+ _opId: number;
14
+ _emissiveLift: number | undefined;
15
+ setAmount(count: any, opts?: {}): Promise<void>;
16
+ /**
17
+ * Night-side self-glow 0..1, applied in place to the live moons and kept for later rebuilds.
18
+ * Low values (≈0.05) let a star-lit scene show the moons' dark sides.
19
+ */
20
+ setEmissiveLift(v: any): void;
21
+ /**
22
+ * Remove the moons from the scene and free their GPU resources. `scene.remove(group)` alone leaks
23
+ * the instanced geometry, materials and the shared moon texture on every view teardown.
24
+ */
25
+ dispose(): void;
26
+ }
27
+ import * as THREE from 'three';