entangled-worlds-orbiters-shared 3.3.0 → 4.1.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 (126) hide show
  1. package/README.md +3 -2
  2. package/dist/clock/Transport.d.ts +8 -2
  3. package/dist/clock/Transport.js +46 -7
  4. package/dist/clock/musical.js +13 -8
  5. package/dist/clock/sync.js +15 -2
  6. package/dist/config/env.d.ts +8 -0
  7. package/dist/config/env.js +36 -0
  8. package/dist/config/index.d.ts +3 -0
  9. package/dist/config/index.js +4 -0
  10. package/dist/config/textureConfig.d.ts +140 -0
  11. package/dist/config/textureConfig.js +318 -0
  12. package/dist/core/index.d.ts +2 -0
  13. package/dist/core/index.js +3 -0
  14. package/dist/core/lightFresnel.d.ts +6 -0
  15. package/dist/core/lightFresnel.js +63 -0
  16. package/dist/core/lights.d.ts +48 -0
  17. package/dist/core/lights.js +79 -0
  18. package/dist/index.d.ts +6 -0
  19. package/dist/index.js +7 -0
  20. package/dist/loaders/bodyTextureLoader.d.ts +10 -0
  21. package/dist/loaders/bodyTextureLoader.js +28 -0
  22. package/dist/loaders/cubeMapLoader.d.ts +58 -0
  23. package/dist/loaders/cubeMapLoader.js +291 -0
  24. package/dist/loaders/glbLoader.d.ts +24 -0
  25. package/dist/loaders/glbLoader.js +85 -0
  26. package/dist/loaders/index.d.ts +5 -0
  27. package/{src → dist}/loaders/index.js +0 -2
  28. package/dist/{types/loaders → loaders}/ktx2.d.ts +4 -6
  29. package/dist/loaders/ktx2.js +48 -0
  30. package/dist/moons/index.d.ts +3 -0
  31. package/dist/moons/index.js +3 -0
  32. package/dist/moons/moonsGeneration.d.ts +64 -0
  33. package/dist/moons/moonsGeneration.js +238 -0
  34. package/dist/moons/moonsManager.d.ts +43 -0
  35. package/dist/moons/moonsManager.js +93 -0
  36. package/dist/{types/render → render}/frameBudget.d.ts +18 -17
  37. package/dist/render/frameBudget.js +42 -0
  38. package/dist/render/index.d.ts +4 -0
  39. package/dist/render/viewportCompositor.d.ts +124 -0
  40. package/dist/render/viewportCompositor.js +643 -0
  41. package/dist/studio/headerBroadcast.d.ts +10 -1
  42. package/dist/studio/hostTransport.d.ts +2 -2
  43. package/dist/studio/index.d.ts +1 -1
  44. package/dist/utils/dispose.d.ts +5 -0
  45. package/dist/utils/dispose.js +50 -0
  46. package/dist/utils/index.d.ts +3 -0
  47. package/{src → dist}/utils/index.js +1 -1
  48. package/dist/utils/materials.d.ts +10 -0
  49. package/dist/utils/materials.js +36 -0
  50. package/dist/world/cloudShell.d.ts +16 -0
  51. package/dist/world/cloudShell.js +73 -0
  52. package/dist/world/index.d.ts +6 -0
  53. package/{src → dist}/world/index.js +0 -2
  54. package/dist/world/worldGLB.d.ts +17 -0
  55. package/dist/world/worldGLB.js +22 -0
  56. package/dist/world/worldManager.d.ts +57 -0
  57. package/dist/world/worldManager.js +125 -0
  58. package/dist/world/worldNormal.d.ts +30 -0
  59. package/dist/world/worldNormal.js +58 -0
  60. package/dist/world/worldTexture.d.ts +56 -0
  61. package/dist/world/worldTexture.js +299 -0
  62. package/package.json +29 -39
  63. package/src/clock/Transport.ts +37 -6
  64. package/src/clock/musical.ts +18 -8
  65. package/src/clock/sync.ts +15 -2
  66. package/src/config/env.ts +48 -0
  67. package/src/config/{index.js → index.ts} +7 -0
  68. package/src/config/{textureConfig.js → textureConfig.ts} +126 -60
  69. package/src/core/{lightFresnel.js → lightFresnel.ts} +1 -1
  70. package/src/core/{lights.js → lights.ts} +21 -21
  71. package/src/loaders/{bodyTextureLoader.js → bodyTextureLoader.ts} +10 -7
  72. package/src/loaders/{cubeMapLoader.js → cubeMapLoader.ts} +83 -49
  73. package/src/loaders/glbLoader.ts +110 -0
  74. package/src/loaders/index.ts +6 -0
  75. package/src/loaders/{ktx2.js → ktx2.ts} +5 -7
  76. package/src/moons/{index.js → index.ts} +1 -0
  77. package/src/moons/{moonsGeneration.js → moonsGeneration.ts} +45 -32
  78. package/src/moons/{moonsManager.js → moonsManager.ts} +37 -11
  79. package/src/render/{frameBudget.js → frameBudget.ts} +15 -8
  80. package/src/render/index.ts +14 -0
  81. package/src/render/{viewportCompositor.js → viewportCompositor.ts} +121 -59
  82. package/src/studio/headerBroadcast.ts +14 -1
  83. package/src/studio/hostTransport.ts +2 -1
  84. package/src/studio/index.ts +1 -1
  85. package/src/utils/dispose.ts +47 -0
  86. package/src/utils/index.ts +4 -0
  87. package/src/utils/materials.ts +44 -0
  88. package/src/world/{cloudShell.js → cloudShell.ts} +19 -12
  89. package/src/world/index.ts +7 -0
  90. package/src/world/worldGLB.ts +41 -0
  91. package/src/world/{worldManager.js → worldManager.ts} +67 -32
  92. package/src/world/{worldNormal.js → worldNormal.ts} +25 -20
  93. package/src/world/{worldTexture.js → worldTexture.ts} +70 -51
  94. package/dist/types/config/env.d.ts +0 -12
  95. package/dist/types/config/index.d.ts +0 -2
  96. package/dist/types/config/textureConfig.d.ts +0 -121
  97. package/dist/types/core/index.d.ts +0 -2
  98. package/dist/types/core/lightFresnel.d.ts +0 -5
  99. package/dist/types/core/lights.d.ts +0 -58
  100. package/dist/types/index.d.ts +0 -6
  101. package/dist/types/loaders/bodyTextureLoader.d.ts +0 -12
  102. package/dist/types/loaders/cubeMapLoader.d.ts +0 -70
  103. package/dist/types/loaders/glbLoader.d.ts +0 -24
  104. package/dist/types/loaders/index.d.ts +0 -5
  105. package/dist/types/moons/index.d.ts +0 -2
  106. package/dist/types/moons/moonsGeneration.d.ts +0 -72
  107. package/dist/types/moons/moonsManager.d.ts +0 -27
  108. package/dist/types/render/index.d.ts +0 -2
  109. package/dist/types/render/viewportCompositor.d.ts +0 -58
  110. package/dist/types/utils/dispose.d.ts +0 -2
  111. package/dist/types/utils/index.d.ts +0 -2
  112. package/dist/types/utils/materials.d.ts +0 -2
  113. package/dist/types/world/cloudShell.d.ts +0 -25
  114. package/dist/types/world/index.d.ts +0 -6
  115. package/dist/types/world/worldGLB.d.ts +0 -20
  116. package/dist/types/world/worldManager.d.ts +0 -63
  117. package/dist/types/world/worldNormal.d.ts +0 -43
  118. package/dist/types/world/worldTexture.d.ts +0 -80
  119. package/src/config/env.js +0 -38
  120. package/src/loaders/glbLoader.js +0 -78
  121. package/src/utils/dispose.js +0 -39
  122. package/src/utils/materials.js +0 -31
  123. package/src/world/worldGLB.js +0 -19
  124. /package/{src → dist}/render/index.js +0 -0
  125. /package/src/core/{index.js → index.ts} +0 -0
  126. /package/src/{index.js → index.ts} +0 -0
@@ -0,0 +1,318 @@
1
+ /**
2
+ * Texture Configuration Loader
3
+ * Loads texture definitions from remote endpoint or uses passed config
4
+ */
5
+ import { resolvePublicUrl } from './env.js';
6
+ /** The catalogue a public function was handed, or the loaded one. */
7
+ async function resolveConfig(config) {
8
+ return config || (await loadTextureConfig());
9
+ }
10
+ function buildTextureUrl(baseUrl, categoryPath, value) {
11
+ if (!value)
12
+ return null;
13
+ if (/^https?:\/\//i.test(value))
14
+ return value;
15
+ if (value.startsWith('/'))
16
+ return resolvePublicUrl(value);
17
+ const prefix = `${baseUrl || ''}${categoryPath || ''}`;
18
+ return resolvePublicUrl(`${prefix}/${value}`);
19
+ }
20
+ function findTextureItem(textureConfig, category, textureUrl) {
21
+ if (!textureUrl || !textureConfig)
22
+ return null;
23
+ const categoryConfig = textureConfig.textures?.[category];
24
+ if (!categoryConfig || !categoryConfig.items)
25
+ return null;
26
+ const baseUrl = textureConfig.baseUrl || '/default';
27
+ const categoryPath = categoryConfig.basePath || `/${category}`;
28
+ for (const item of categoryConfig.items) {
29
+ const resolved = buildTextureUrl(baseUrl, categoryPath, item.filename || item.id);
30
+ if (!resolved)
31
+ continue;
32
+ if (resolved === textureUrl) {
33
+ return { item, baseUrl, categoryPath };
34
+ }
35
+ }
36
+ return null;
37
+ }
38
+ let cachedConfig = null;
39
+ let configPromise = null;
40
+ let lastConfigUrl = null;
41
+ /**
42
+ * Load texture configuration from remote endpoint or use passed config
43
+ * @param {string} configUrl - Override config URL
44
+ * @param {boolean} useCache - Whether to use cached config if available
45
+ * @returns {Promise<TextureConfig>} Texture configuration object
46
+ */
47
+ export async function loadTextureConfig(configUrl, useCache = true) {
48
+ if (useCache && cachedConfig) {
49
+ return cachedConfig;
50
+ }
51
+ if (configPromise) {
52
+ return configPromise;
53
+ }
54
+ if (configUrl) {
55
+ lastConfigUrl = configUrl;
56
+ }
57
+ const targetUrl = configUrl || lastConfigUrl;
58
+ configPromise = (async () => {
59
+ try {
60
+ if (!targetUrl) {
61
+ throw new Error('No texture config URL provided');
62
+ }
63
+ const response = await fetch(targetUrl);
64
+ if (!response.ok) {
65
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
66
+ }
67
+ const config = (await response.json());
68
+ // Validate configuration structure
69
+ if (!config.textures || typeof config.textures !== 'object') {
70
+ throw new Error('Invalid configuration: missing textures object');
71
+ }
72
+ cachedConfig = config;
73
+ return config;
74
+ }
75
+ catch (error) {
76
+ console.error('[TextureConfig] Failed to load config:', error);
77
+ // Return minimal fallback configuration when loading fails
78
+ console.warn('[TextureConfig] Using hardcoded fallback config');
79
+ const fallback = getHardcodedFallback();
80
+ cachedConfig = fallback;
81
+ return fallback;
82
+ }
83
+ finally {
84
+ configPromise = null;
85
+ }
86
+ })();
87
+ return configPromise;
88
+ }
89
+ /**
90
+ * Get the full choice records for a category (clouds, moons, etc.) — label, texture URL, and the
91
+ * picture to show for it. The single resolution point for a category's items; `getTextureOptions`
92
+ * is the flat `{ label: url }` projection of this.
93
+ * @param {string} category - Texture category (clouds, moons)
94
+ * @param {TextureConfig} config - Optional pre-loaded config object
95
+ * @returns {Promise<TextureChoice[]>} Choice records in catalogue order
96
+ */
97
+ export async function getTextureChoices(category, config) {
98
+ const textureConfig = await resolveConfig(config);
99
+ const categoryConfig = textureConfig.textures[category];
100
+ if (!categoryConfig) {
101
+ console.warn(`[TextureConfig] Category '${category}' not found`);
102
+ return [];
103
+ }
104
+ const baseUrl = textureConfig.baseUrl || '/default';
105
+ const categoryPath = categoryConfig.basePath || `/${category}`;
106
+ return (categoryConfig.items || []).flatMap((item) => {
107
+ const url = buildTextureUrl(baseUrl, categoryPath, item.filename || item.id);
108
+ if (!url)
109
+ return [];
110
+ return [{
111
+ id: item.id,
112
+ label: item.label || item.id,
113
+ url,
114
+ still: item.still ? buildTextureUrl(baseUrl, categoryPath, item.still) : null,
115
+ }];
116
+ });
117
+ }
118
+ /**
119
+ * Get texture options for a specific category (clouds, moons, etc.)
120
+ * @param {string} category - Texture category (clouds, moons, skyboxes)
121
+ * @param {TextureConfig} config - Optional pre-loaded config object
122
+ * @returns {Promise<Record<string, string>>} `{ label: url }`, suitable for dropdown option lists
123
+ */
124
+ export async function getTextureOptions(category, config) {
125
+ const choices = await getTextureChoices(category, config);
126
+ return Object.fromEntries(choices.map(c => [c.label, c.url]));
127
+ }
128
+ /**
129
+ * Get default texture for a category
130
+ * @param {string} category - Texture category
131
+ * @param {TextureConfig} config - Optional pre-loaded config object
132
+ * @returns {Promise<string|null>} Default texture path, or null when the category has no texture
133
+ */
134
+ export async function getDefaultTexture(category, config) {
135
+ const textureConfig = await resolveConfig(config);
136
+ const categoryConfig = textureConfig.textures[category];
137
+ if (!categoryConfig) {
138
+ return null;
139
+ }
140
+ const baseUrl = textureConfig.baseUrl || '/default';
141
+ const categoryPath = categoryConfig.basePath || `/${category}`;
142
+ const defaultFilename = categoryConfig.defaultTexture;
143
+ if (defaultFilename) {
144
+ return resolvePublicUrl(`${baseUrl}${categoryPath}/${defaultFilename}`);
145
+ }
146
+ // Fallback to first item
147
+ if (categoryConfig.items && categoryConfig.items.length > 0) {
148
+ const firstItem = categoryConfig.items[0];
149
+ return resolvePublicUrl(`${baseUrl}${categoryPath}/${firstItem.filename || firstItem.id}`);
150
+ }
151
+ return null;
152
+ }
153
+ /**
154
+ * Return the configured fallback URL for a given texture if defined.
155
+ * @param {string} category - Texture category (clouds, moons, etc.)
156
+ * @param {string} textureUrl - Resolved texture URL
157
+ * @param {TextureConfig|null} config - Optional texture config override
158
+ * @returns {string|null} Resolved fallback texture URL or null
159
+ */
160
+ export function getTextureFallbackUrl(category, textureUrl, config = null) {
161
+ const textureConfig = config || cachedConfig;
162
+ if (!textureConfig)
163
+ return null;
164
+ const found = findTextureItem(textureConfig, category, textureUrl);
165
+ if (!found || !found.item?.fallback)
166
+ return null;
167
+ return buildTextureUrl(found.baseUrl, found.categoryPath, found.item.fallback);
168
+ }
169
+ /**
170
+ * Get texture by ID within a category
171
+ * @param {string} category - Texture category
172
+ * @param {string} textureId - Texture ID
173
+ * @param {TextureConfig} config - Optional pre-loaded config object
174
+ * @returns {Promise<TextureItem|null>} Texture definition object
175
+ */
176
+ export async function getTextureById(category, textureId, config) {
177
+ const textureConfig = await resolveConfig(config);
178
+ const categoryConfig = textureConfig.textures[category];
179
+ if (!categoryConfig || !categoryConfig.items) {
180
+ return null;
181
+ }
182
+ return categoryConfig.items.find(item => item.id === textureId) || null;
183
+ }
184
+ /**
185
+ * Get full URL for a texture
186
+ * @param {string} category - Texture category
187
+ * @param {string} textureId - Texture ID
188
+ * @param {TextureConfig} config - Optional pre-loaded config object
189
+ * @returns {Promise<string|null>} Full texture URL
190
+ */
191
+ export async function getTextureUrl(category, textureId, config) {
192
+ const texture = await getTextureById(category, textureId, config);
193
+ if (!texture)
194
+ return null;
195
+ const textureConfig = await resolveConfig(config);
196
+ // `getTextureById` just found the item in this category, so it is there (unless the catalogue
197
+ // was reloaded in between, which throws here, as it always did).
198
+ const categoryConfig = textureConfig.textures[category];
199
+ const baseUrl = textureConfig.baseUrl || '/default';
200
+ const categoryPath = categoryConfig.basePath || `/${category}`;
201
+ return resolvePublicUrl(`${baseUrl}${categoryPath}/${texture.filename || texture.id}`);
202
+ }
203
+ /**
204
+ * Search textures by tags
205
+ * @param {string} category - Texture category
206
+ * @param {string[]} tags - Tags to search for
207
+ * @param {TextureConfig} config - Optional pre-loaded config object
208
+ * @returns {Promise<TextureItem[]>} Array of matching texture definitions
209
+ */
210
+ export async function searchTexturesByTags(category, tags, config) {
211
+ const textureConfig = await resolveConfig(config);
212
+ const categoryConfig = textureConfig.textures[category];
213
+ if (!categoryConfig || !categoryConfig.items) {
214
+ return [];
215
+ }
216
+ return categoryConfig.items.filter(item => item.tags && tags.some(tag => item.tags.includes(tag)));
217
+ }
218
+ /**
219
+ * Get skybox options formatted for GUI dropdowns
220
+ * @param {TextureConfig} config - Optional pre-loaded config object
221
+ * @returns {Promise<Record<string, string>>} `{ label: id }`
222
+ */
223
+ export async function getSkyboxOptions(config) {
224
+ const textureConfig = await resolveConfig(config);
225
+ const skyboxConfig = textureConfig.textures.skyboxes;
226
+ if (!skyboxConfig) {
227
+ console.warn('[TextureConfig] Skyboxes category not found');
228
+ return {};
229
+ }
230
+ const options = {};
231
+ // Reads `items` unguarded, unlike the other readers: a skyboxes category without items throws here.
232
+ skyboxConfig.items.forEach(item => {
233
+ const label = item.label || item.id;
234
+ const id = item.id;
235
+ options[label] = id;
236
+ });
237
+ return options;
238
+ }
239
+ /**
240
+ * Get default skybox ID
241
+ * @param {TextureConfig} config - Optional pre-loaded config object
242
+ * @returns {Promise<string>} Default skybox ID
243
+ */
244
+ export async function getDefaultSkybox(config) {
245
+ const textureConfig = await resolveConfig(config);
246
+ const skyboxConfig = textureConfig.textures.skyboxes;
247
+ if (!skyboxConfig) {
248
+ return '001'; // fallback
249
+ }
250
+ return skyboxConfig.defaultSkybox || '001';
251
+ }
252
+ /**
253
+ * Hardcoded fallback configuration (minimal)
254
+ * Used when remote config loading fails
255
+ */
256
+ function getHardcodedFallback() {
257
+ return {
258
+ version: "1.0-fallback",
259
+ lastUpdated: new Date().toISOString(),
260
+ baseUrl: "/default",
261
+ textures: {
262
+ clouds: {
263
+ category: "atmospheric",
264
+ basePath: "/clouds",
265
+ defaultTexture: "04-clouds.ktx2",
266
+ items: [
267
+ { id: "clouds-1", filename: "04-clouds.ktx2", label: "Clouds", tags: ["natural"] },
268
+ { id: "entangled-1", filename: "06-entangled.ktx2", label: "Entangled", tags: ["quantum"] }
269
+ ]
270
+ },
271
+ moons: {
272
+ category: "celestial",
273
+ basePath: "/textures/moons",
274
+ defaultTexture: "moon-equirectangular.ktx2",
275
+ items: [
276
+ { id: "default-moon", filename: "moon-equirectangular.ktx2", label: "Default Moon", tags: ["lunar"] }
277
+ ]
278
+ },
279
+ skyboxes: {
280
+ category: "environment",
281
+ basePath: "/skybox",
282
+ defaultSkybox: "001",
283
+ items: [
284
+ { id: "001", label: "Deep Space", tags: ["space"] }
285
+ ]
286
+ }
287
+ }
288
+ };
289
+ }
290
+ /**
291
+ * Reload configuration (force refresh)
292
+ * @param {string} configUrl - Override config URL
293
+ * @returns {Promise<TextureConfig>} Fresh configuration object
294
+ */
295
+ export async function reloadTextureConfig(configUrl) {
296
+ cachedConfig = null;
297
+ configPromise = null;
298
+ return loadTextureConfig(configUrl || lastConfigUrl || undefined, false);
299
+ }
300
+ /**
301
+ * Get configuration metadata
302
+ * @param {TextureConfig} config - Optional pre-loaded config object
303
+ * @returns {Promise<TextureConfigMetadata>} Configuration metadata
304
+ */
305
+ export async function getConfigMetadata(config) {
306
+ const textureConfig = await resolveConfig(config);
307
+ return {
308
+ version: textureConfig.version,
309
+ lastUpdated: textureConfig.lastUpdated,
310
+ categories: Object.keys(textureConfig.textures),
311
+ totalTextures: Object.values(textureConfig.textures).reduce(
312
+ // Only listed categories are values; a missing one is a key that is not there.
313
+ (sum, cat) => {
314
+ const items = cat.items;
315
+ return sum + (items ? items.length : 0);
316
+ }, 0)
317
+ };
318
+ }
@@ -0,0 +1,2 @@
1
+ export { ensureDefaultLights, updateDefaultLights, setHemisphereEnabled } from './lights.js';
2
+ export { getLightFresnel } from './lightFresnel.js';
@@ -0,0 +1,3 @@
1
+ // Core rendering exports
2
+ export { ensureDefaultLights, updateDefaultLights, setHemisphereEnabled } from './lights.js';
3
+ export { getLightFresnel } from './lightFresnel.js';
@@ -0,0 +1,6 @@
1
+ import * as THREE from "three";
2
+ declare function getLightFresnel({ rimHex, facingHex }?: {
3
+ rimHex?: number;
4
+ facingHex?: number;
5
+ }): THREE.ShaderMaterial;
6
+ export { getLightFresnel };
@@ -0,0 +1,63 @@
1
+ import * as THREE from "three";
2
+ function getLightFresnel({ rimHex = 0x0088ff, facingHex = 0x000000 } = {}) {
3
+ const uniforms = {
4
+ color1: { value: new THREE.Color(rimHex) },
5
+ color2: { value: new THREE.Color(facingHex) },
6
+ fresnelBias: { value: 0.1 },
7
+ fresnelScale: { value: 1.0 },
8
+ fresnelPower: { value: 4.0 },
9
+ };
10
+ const vs = `
11
+ uniform float fresnelBias;
12
+ uniform float fresnelScale;
13
+ uniform float fresnelPower;
14
+
15
+ varying float vReflectionFactor;
16
+
17
+ void main() {
18
+ vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
19
+ vec4 worldPosition = modelMatrix * vec4( position, 1.0 );
20
+
21
+ // Transform normal to world space
22
+ vec3 worldNormal = normalize( mat3( modelMatrix[0].xyz, modelMatrix[1].xyz, modelMatrix[2].xyz ) * normal );
23
+
24
+ // Proper view direction (from surface point towards camera)
25
+ vec3 viewDir = normalize( cameraPosition - worldPosition.xyz );
26
+
27
+ // Classic rim: stronger at grazing angles
28
+ float ndotv = max( dot( worldNormal, viewDir ), 0.0 );
29
+ vReflectionFactor = fresnelBias + fresnelScale * pow( 1.0 - ndotv, fresnelPower );
30
+
31
+ gl_Position = projectionMatrix * mvPosition;
32
+ }
33
+ `;
34
+ const fs = `
35
+ uniform vec3 color1;
36
+ uniform vec3 color2;
37
+
38
+ varying float vReflectionFactor;
39
+
40
+ void main() {
41
+ float f = clamp( vReflectionFactor, 0.0, 1.0 );
42
+ gl_FragColor = vec4(mix(color2, color1, vec3(f)), f);
43
+ }
44
+ `;
45
+ const lightFresnel = new THREE.ShaderMaterial({
46
+ uniforms: uniforms,
47
+ vertexShader: vs,
48
+ fragmentShader: fs,
49
+ transparent: true,
50
+ blending: THREE.CustomBlending,
51
+ blendSrc: THREE.OneFactor,
52
+ blendDst: THREE.OneFactor,
53
+ blendEquation: THREE.AddEquation,
54
+ depthWrite: false,
55
+ depthTest: true,
56
+ depthFunc: THREE.LessDepth,
57
+ side: THREE.FrontSide,
58
+ toneMapped: false,
59
+ // wireframe: true,
60
+ });
61
+ return lightFresnel;
62
+ }
63
+ export { getLightFresnel };
@@ -0,0 +1,48 @@
1
+ import * as THREE from 'three';
2
+ /**
3
+ * Ensure a scene has the default hemisphere + ambient lights. Idempotent PER SCENE.
4
+ *
5
+ * A multi-orbiter realm renders N independent scenes through one renderer. A module-level
6
+ * singleton light is wrong twice over — it's only added to the FIRST scene, and a THREE light can
7
+ * live in just one scene (adding it to a second removes it from the first), so scenes 2..N rendered
8
+ * DARK ("only one entangled world"). So create + add the lights PER scene, keyed by name so a
9
+ * re-call on the same scene is a no-op. Single-orbiter (one scene) is unchanged.
10
+ */
11
+ export declare function ensureDefaultLights(scene: THREE.Scene): {
12
+ hemi: THREE.HemisphereLight;
13
+ ambient: THREE.AmbientLight;
14
+ };
15
+ /**
16
+ * Apply a lighting patch to ONE scene's default lights.
17
+ *
18
+ * The scene is required. This used to address module-global light refs pointing at whichever scene
19
+ * was ensured last, so on a page rendering several scenes every lighting update landed on one of
20
+ * them — a shared-renderer page (N feed worlds, N orbiters) lit the wrong world.
21
+ *
22
+ * Every field is optional; an omitted one leaves that part of the lighting as it is.
23
+ */
24
+ export declare function updateDefaultLights(scene: THREE.Scene, { ambientIntensity, diffuseIntensity, lightColor, lightDirection, hemisphere }?: {
25
+ /** ambient light intensity (clamped at 0) */
26
+ ambientIntensity?: number;
27
+ /** hemisphere light intensity (clamped at 0) */
28
+ diffuseIntensity?: number;
29
+ /** hemisphere light colour */
30
+ lightColor?: THREE.Color | number | string;
31
+ /** hemisphere light direction */
32
+ lightDirection?: {
33
+ x: number;
34
+ y?: number;
35
+ z?: number;
36
+ };
37
+ /** switch the hemisphere light on or off */
38
+ hemisphere?: boolean;
39
+ }): void;
40
+ /**
41
+ * Switch a scene's default hemisphere light on or off (the ambient light is untouched). Hosts turn it
42
+ * off when a directional light — the star — lights the scene, so night sides can read.
43
+ */
44
+ export declare function setHemisphereEnabled(scene: THREE.Scene, on: boolean): void;
45
+ export declare function addDefaultLights(scene: THREE.Scene): {
46
+ hemi: THREE.HemisphereLight;
47
+ ambient: THREE.AmbientLight;
48
+ };
@@ -0,0 +1,79 @@
1
+ import * as THREE from 'three';
2
+ /**
3
+ * Ensure a scene has the default hemisphere + ambient lights. Idempotent PER SCENE.
4
+ *
5
+ * A multi-orbiter realm renders N independent scenes through one renderer. A module-level
6
+ * singleton light is wrong twice over — it's only added to the FIRST scene, and a THREE light can
7
+ * live in just one scene (adding it to a second removes it from the first), so scenes 2..N rendered
8
+ * DARK ("only one entangled world"). So create + add the lights PER scene, keyed by name so a
9
+ * re-call on the same scene is a no-op. Single-orbiter (one scene) is unchanged.
10
+ */
11
+ export function ensureDefaultLights(scene) {
12
+ // Found by name: the objects under these names are the lights this function added.
13
+ let hemi = scene.getObjectByName('ew_HemisphereLight');
14
+ if (!hemi) {
15
+ hemi = new THREE.HemisphereLight(0xffffff, 0xffffff, 1.0);
16
+ hemi.position.set(1, 1, 1);
17
+ hemi.name = 'ew_HemisphereLight';
18
+ scene.add(hemi);
19
+ }
20
+ let ambient = scene.getObjectByName('ew_AmbientLight');
21
+ if (!ambient) {
22
+ ambient = new THREE.AmbientLight(0xffffff, 1.0);
23
+ ambient.name = 'ew_AmbientLight';
24
+ scene.add(ambient);
25
+ }
26
+ return { hemi, ambient };
27
+ }
28
+ /**
29
+ * Apply a lighting patch to ONE scene's default lights.
30
+ *
31
+ * The scene is required. This used to address module-global light refs pointing at whichever scene
32
+ * was ensured last, so on a page rendering several scenes every lighting update landed on one of
33
+ * them — a shared-renderer page (N feed worlds, N orbiters) lit the wrong world.
34
+ *
35
+ * Every field is optional; an omitted one leaves that part of the lighting as it is.
36
+ */
37
+ export function updateDefaultLights(scene, { ambientIntensity, diffuseIntensity, lightColor, lightDirection, hemisphere } = {}) {
38
+ if (!scene?.isObject3D) {
39
+ console.error('[lights] updateDefaultLights(scene, patch) requires the scene to update; ignoring patch.');
40
+ return;
41
+ }
42
+ const { hemi, ambient } = ensureDefaultLights(scene);
43
+ // `hemisphere: false` switches the white hemisphere off (e.g. when a star lights the scene);
44
+ // omitted = unchanged, so old callers keep today's look.
45
+ if (typeof hemisphere === 'boolean')
46
+ hemi.visible = hemisphere;
47
+ if (typeof ambientIntensity === 'number')
48
+ ambient.intensity = Math.max(0, ambientIntensity);
49
+ if (typeof diffuseIntensity === 'number')
50
+ hemi.intensity = Math.max(0, diffuseIntensity);
51
+ if (lightColor != null) {
52
+ if (lightColor instanceof THREE.Color) {
53
+ hemi.color.copy(lightColor);
54
+ }
55
+ else if (typeof lightColor === 'number') {
56
+ hemi.color.setHex(lightColor);
57
+ }
58
+ else if (typeof lightColor === 'string') {
59
+ hemi.color.set(lightColor);
60
+ }
61
+ }
62
+ if (lightDirection && typeof lightDirection.x === 'number') {
63
+ hemi.position.set(lightDirection.x, lightDirection.y ?? 0, lightDirection.z ?? 0).normalize();
64
+ }
65
+ }
66
+ /**
67
+ * Switch a scene's default hemisphere light on or off (the ambient light is untouched). Hosts turn it
68
+ * off when a directional light — the star — lights the scene, so night sides can read.
69
+ */
70
+ export function setHemisphereEnabled(scene, on) {
71
+ if (!scene?.isObject3D)
72
+ return;
73
+ const { hemi } = ensureDefaultLights(scene);
74
+ hemi.visible = Boolean(on);
75
+ }
76
+ // Back-compat helper
77
+ export function addDefaultLights(scene) {
78
+ return ensureDefaultLights(scene);
79
+ }
@@ -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';
package/dist/index.js ADDED
@@ -0,0 +1,7 @@
1
+ // Main exports for entangled-worlds-orbiters-shared library
2
+ export * from './world/index.js';
3
+ export * from './moons/index.js';
4
+ export * from './loaders/index.js';
5
+ export * from './core/index.js';
6
+ export * from './config/index.js';
7
+ export * from './utils/index.js';
@@ -0,0 +1,10 @@
1
+ import * as THREE from 'three';
2
+ /**
3
+ * `url`: `.ktx2` goes through the renderer's KTX2 transcoder; anything else through
4
+ * THREE.TextureLoader. `signal`: an abort during the load rejects the promise. `renderer`: REQUIRED
5
+ * for a `.ktx2` url.
6
+ */
7
+ export declare function loadTexture(url: string, { signal, renderer }?: {
8
+ signal?: AbortSignal | undefined;
9
+ renderer?: THREE.WebGLRenderer | null | undefined;
10
+ }): Promise<THREE.Texture>;
@@ -0,0 +1,28 @@
1
+ import * as THREE from 'three';
2
+ import { getKTX2LoaderForRenderer } from './ktx2.js';
3
+ const textureLoader = new THREE.TextureLoader();
4
+ /**
5
+ * `url`: `.ktx2` goes through the renderer's KTX2 transcoder; anything else through
6
+ * THREE.TextureLoader. `signal`: an abort during the load rejects the promise. `renderer`: REQUIRED
7
+ * for a `.ktx2` url.
8
+ */
9
+ export function loadTexture(url, { signal, renderer } = {}) {
10
+ return new Promise((resolve, reject) => {
11
+ if (url && url.endsWith('.ktx2')) {
12
+ // A missing renderer throws inside (as before): the type allows `null` only because the
13
+ // non-KTX2 path never needs one.
14
+ const ktx2 = getKTX2LoaderForRenderer(renderer);
15
+ const onAbort = () => reject(new Error('aborted'));
16
+ if (signal)
17
+ signal.addEventListener('abort', onAbort, { once: true });
18
+ ktx2.load(url, (tex) => { if (signal)
19
+ signal.removeEventListener('abort', onAbort); resolve(tex); }, undefined, (err) => { if (signal)
20
+ signal.removeEventListener('abort', onAbort); reject(err); });
21
+ return;
22
+ }
23
+ const tex = textureLoader.load(url, () => resolve(tex), undefined, (err) => reject(err));
24
+ if (signal) {
25
+ signal.addEventListener('abort', () => reject(new Error('aborted')));
26
+ }
27
+ });
28
+ }
@@ -0,0 +1,58 @@
1
+ import * as THREE from 'three';
2
+ /** A skycube id (`'7'`, `'07_skycube'`, `'skybox-007'`, …) as its three-digit folder name; `'001'` when none. */
3
+ export declare function normalizeSkycubeName(raw: string | null | undefined): string;
4
+ export declare function buildSkycubefaceKeys(skycubeId: string | null | undefined, extension?: string): {
5
+ right: string;
6
+ left: string;
7
+ top: string;
8
+ bottom: string;
9
+ front: string;
10
+ back: string;
11
+ };
12
+ /**
13
+ * Main loader function - always uses explicit face URLs.
14
+ * `renderer` binds the KTX2 transcoder to the right WebGL context. REQUIRED (3.0) when the KTX2
15
+ * path is taken — the old `window.__EW_RENDERER` fallback is gone; thread the owning view's
16
+ * renderer. (The PNG/JPG path is renderer-agnostic, so its absence only bites on KTX2.)
17
+ *
18
+ * - `faceKeys`: explicit face URLs; when absent they are built from `skycubeName`.
19
+ * - `skycubeName`: skybox id (e.g. `'001'`), used when `faceKeys` is absent.
20
+ * - `preferKTX2` (true).
21
+ * - `forceLow`: force (or forbid) the low-resolution faces; omitted = detect the device.
22
+ *
23
+ * Resolves to what `setBackground` takes.
24
+ */
25
+ export declare 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
+ */
43
+ export type SkyboxResource = {
44
+ __ewType: 'skybox-mesh';
45
+ mesh: THREE.Mesh;
46
+ } | (THREE.CubeTexture & {
47
+ mesh?: undefined;
48
+ });
49
+ /**
50
+ * Show a loaded skybox as the scene's background. A previous skybox mesh or cube-texture background is
51
+ * disposed (a plain-texture background is not). Takes what `loadCubeMap` resolved to (or any texture);
52
+ * anything else, `null` included, clears it.
53
+ *
54
+ */
55
+ export declare function setBackground(scene: THREE.Scene, resource: unknown): void;
56
+ export declare function initSpaceScene(scene: THREE.Scene, { renderer }?: {
57
+ renderer?: THREE.WebGLRenderer | null;
58
+ }): Promise<void>;