entangled-worlds-orbiters-shared 3.3.0 → 4.0.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.
- package/README.md +3 -2
- package/dist/config/env.d.ts +8 -0
- package/dist/config/env.js +36 -0
- package/dist/config/index.d.ts +3 -0
- package/dist/config/index.js +4 -0
- package/dist/config/textureConfig.d.ts +140 -0
- package/dist/config/textureConfig.js +318 -0
- package/dist/core/index.d.ts +2 -0
- package/dist/core/index.js +3 -0
- package/dist/core/lightFresnel.d.ts +6 -0
- package/dist/core/lightFresnel.js +63 -0
- package/dist/core/lights.d.ts +48 -0
- package/dist/core/lights.js +79 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +7 -0
- package/dist/loaders/bodyTextureLoader.d.ts +10 -0
- package/dist/loaders/bodyTextureLoader.js +28 -0
- package/dist/loaders/cubeMapLoader.d.ts +58 -0
- package/dist/loaders/cubeMapLoader.js +291 -0
- package/dist/loaders/glbLoader.d.ts +24 -0
- package/dist/loaders/glbLoader.js +72 -0
- package/dist/loaders/index.d.ts +5 -0
- package/{src → dist}/loaders/index.js +0 -2
- package/dist/{types/loaders → loaders}/ktx2.d.ts +4 -6
- package/dist/loaders/ktx2.js +48 -0
- package/dist/moons/index.d.ts +3 -0
- package/dist/moons/index.js +3 -0
- package/dist/moons/moonsGeneration.d.ts +64 -0
- package/dist/moons/moonsGeneration.js +238 -0
- package/dist/moons/moonsManager.d.ts +43 -0
- package/dist/moons/moonsManager.js +93 -0
- package/dist/{types/render → render}/frameBudget.d.ts +18 -17
- package/dist/render/frameBudget.js +42 -0
- package/dist/render/index.d.ts +4 -0
- package/dist/render/viewportCompositor.d.ts +124 -0
- package/dist/render/viewportCompositor.js +643 -0
- package/dist/studio/headerBroadcast.d.ts +10 -1
- package/dist/studio/hostTransport.d.ts +2 -2
- package/dist/studio/index.d.ts +1 -1
- package/dist/utils/dispose.d.ts +5 -0
- package/dist/utils/dispose.js +50 -0
- package/dist/utils/index.d.ts +3 -0
- package/{src → dist}/utils/index.js +1 -1
- package/dist/utils/materials.d.ts +10 -0
- package/dist/utils/materials.js +36 -0
- package/dist/world/cloudShell.d.ts +16 -0
- package/dist/world/cloudShell.js +73 -0
- package/dist/world/index.d.ts +6 -0
- package/{src → dist}/world/index.js +0 -2
- package/dist/world/worldGLB.d.ts +17 -0
- package/dist/world/worldGLB.js +22 -0
- package/dist/world/worldManager.d.ts +57 -0
- package/dist/world/worldManager.js +125 -0
- package/dist/world/worldNormal.d.ts +30 -0
- package/dist/world/worldNormal.js +58 -0
- package/dist/world/worldTexture.d.ts +56 -0
- package/dist/world/worldTexture.js +299 -0
- package/package.json +29 -39
- package/src/config/env.ts +48 -0
- package/src/config/{index.js → index.ts} +7 -0
- package/src/config/{textureConfig.js → textureConfig.ts} +126 -60
- package/src/core/{lightFresnel.js → lightFresnel.ts} +1 -1
- package/src/core/{lights.js → lights.ts} +21 -21
- package/src/loaders/{bodyTextureLoader.js → bodyTextureLoader.ts} +10 -7
- package/src/loaders/{cubeMapLoader.js → cubeMapLoader.ts} +83 -49
- package/src/loaders/{glbLoader.js → glbLoader.ts} +41 -18
- package/src/loaders/index.ts +6 -0
- package/src/loaders/{ktx2.js → ktx2.ts} +5 -7
- package/src/moons/{index.js → index.ts} +1 -0
- package/src/moons/{moonsGeneration.js → moonsGeneration.ts} +45 -32
- package/src/moons/{moonsManager.js → moonsManager.ts} +37 -11
- package/src/render/{frameBudget.js → frameBudget.ts} +15 -8
- package/src/render/index.ts +14 -0
- package/src/render/{viewportCompositor.js → viewportCompositor.ts} +121 -59
- package/src/studio/headerBroadcast.ts +14 -1
- package/src/studio/hostTransport.ts +2 -1
- package/src/studio/index.ts +1 -1
- package/src/utils/dispose.ts +47 -0
- package/src/utils/index.ts +4 -0
- package/src/utils/materials.ts +44 -0
- package/src/world/{cloudShell.js → cloudShell.ts} +19 -12
- package/src/world/index.ts +7 -0
- package/src/world/worldGLB.ts +41 -0
- package/src/world/{worldManager.js → worldManager.ts} +67 -32
- package/src/world/{worldNormal.js → worldNormal.ts} +25 -20
- package/src/world/{worldTexture.js → worldTexture.ts} +70 -51
- package/dist/types/config/env.d.ts +0 -12
- package/dist/types/config/index.d.ts +0 -2
- package/dist/types/config/textureConfig.d.ts +0 -121
- package/dist/types/core/index.d.ts +0 -2
- package/dist/types/core/lightFresnel.d.ts +0 -5
- package/dist/types/core/lights.d.ts +0 -58
- package/dist/types/index.d.ts +0 -6
- package/dist/types/loaders/bodyTextureLoader.d.ts +0 -12
- package/dist/types/loaders/cubeMapLoader.d.ts +0 -70
- package/dist/types/loaders/glbLoader.d.ts +0 -24
- package/dist/types/loaders/index.d.ts +0 -5
- package/dist/types/moons/index.d.ts +0 -2
- package/dist/types/moons/moonsGeneration.d.ts +0 -72
- package/dist/types/moons/moonsManager.d.ts +0 -27
- package/dist/types/render/index.d.ts +0 -2
- package/dist/types/render/viewportCompositor.d.ts +0 -58
- package/dist/types/utils/dispose.d.ts +0 -2
- package/dist/types/utils/index.d.ts +0 -2
- package/dist/types/utils/materials.d.ts +0 -2
- package/dist/types/world/cloudShell.d.ts +0 -25
- package/dist/types/world/index.d.ts +0 -6
- package/dist/types/world/worldGLB.d.ts +0 -20
- package/dist/types/world/worldManager.d.ts +0 -63
- package/dist/types/world/worldNormal.d.ts +0 -43
- package/dist/types/world/worldTexture.d.ts +0 -80
- package/src/config/env.js +0 -38
- package/src/utils/dispose.js +0 -39
- package/src/utils/materials.js +0 -31
- package/src/world/worldGLB.js +0 -19
- /package/{src → dist}/render/index.js +0 -0
- /package/src/core/{index.js → index.ts} +0 -0
- /package/src/{index.js → index.ts} +0 -0
|
@@ -4,7 +4,57 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { resolvePublicUrl } from './env.js';
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
/** One catalogue item. */
|
|
8
|
+
export interface TextureItem {
|
|
9
|
+
id: string;
|
|
10
|
+
filename?: string;
|
|
11
|
+
label?: string;
|
|
12
|
+
/** Picture of the item (a thumbnail), relative like `filename`. */
|
|
13
|
+
still?: string;
|
|
14
|
+
/** Texture to use when this one fails to load, relative like `filename`. */
|
|
15
|
+
fallback?: string;
|
|
16
|
+
tags?: string[];
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** One catalogue category (clouds, moons, skyboxes, …). */
|
|
20
|
+
export interface TextureCategory {
|
|
21
|
+
category?: string;
|
|
22
|
+
basePath?: string;
|
|
23
|
+
defaultTexture?: string;
|
|
24
|
+
defaultSkybox?: string;
|
|
25
|
+
items?: TextureItem[];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** The texture catalogue as loaded from the config URL. */
|
|
29
|
+
export interface TextureConfig {
|
|
30
|
+
version?: string;
|
|
31
|
+
lastUpdated?: string;
|
|
32
|
+
baseUrl?: string;
|
|
33
|
+
/** By category name; a category the catalogue does not list is missing. */
|
|
34
|
+
textures: Partial<Record<string, TextureCategory>>;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** A catalogue item found by its resolved URL, with the base and path it resolved against. */
|
|
38
|
+
interface FoundTextureItem {
|
|
39
|
+
item: TextureItem;
|
|
40
|
+
baseUrl: string;
|
|
41
|
+
categoryPath: string;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** What `getConfigMetadata` reports about a catalogue. */
|
|
45
|
+
export interface TextureConfigMetadata {
|
|
46
|
+
version: string | undefined;
|
|
47
|
+
lastUpdated: string | undefined;
|
|
48
|
+
categories: string[];
|
|
49
|
+
totalTextures: number;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** The catalogue a public function was handed, or the loaded one. */
|
|
53
|
+
async function resolveConfig(config: TextureConfig | null | undefined): Promise<TextureConfig> {
|
|
54
|
+
return config || (await loadTextureConfig());
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function buildTextureUrl(baseUrl: string, categoryPath: string, value: string | undefined): string | null {
|
|
8
58
|
if (!value) return null;
|
|
9
59
|
if (/^https?:\/\//i.test(value)) return value;
|
|
10
60
|
if (value.startsWith('/')) return resolvePublicUrl(value);
|
|
@@ -12,7 +62,7 @@ function buildTextureUrl(baseUrl, categoryPath, value) {
|
|
|
12
62
|
return resolvePublicUrl(`${prefix}/${value}`);
|
|
13
63
|
}
|
|
14
64
|
|
|
15
|
-
function findTextureItem(textureConfig, category, textureUrl) {
|
|
65
|
+
function findTextureItem(textureConfig: TextureConfig, category: string, textureUrl: string): FoundTextureItem | null {
|
|
16
66
|
if (!textureUrl || !textureConfig) return null;
|
|
17
67
|
const categoryConfig = textureConfig.textures?.[category];
|
|
18
68
|
if (!categoryConfig || !categoryConfig.items) return null;
|
|
@@ -30,17 +80,17 @@ function findTextureItem(textureConfig, category, textureUrl) {
|
|
|
30
80
|
return null;
|
|
31
81
|
}
|
|
32
82
|
|
|
33
|
-
let cachedConfig = null;
|
|
34
|
-
let configPromise = null;
|
|
35
|
-
let lastConfigUrl = null;
|
|
83
|
+
let cachedConfig: TextureConfig | null = null;
|
|
84
|
+
let configPromise: Promise<TextureConfig> | null = null;
|
|
85
|
+
let lastConfigUrl: string | null = null;
|
|
36
86
|
|
|
37
87
|
/**
|
|
38
88
|
* Load texture configuration from remote endpoint or use passed config
|
|
39
89
|
* @param {string} configUrl - Override config URL
|
|
40
90
|
* @param {boolean} useCache - Whether to use cached config if available
|
|
41
|
-
* @returns {Promise<
|
|
91
|
+
* @returns {Promise<TextureConfig>} Texture configuration object
|
|
42
92
|
*/
|
|
43
|
-
export async function loadTextureConfig(configUrl
|
|
93
|
+
export async function loadTextureConfig(configUrl?: string, useCache = true): Promise<TextureConfig> {
|
|
44
94
|
if (useCache && cachedConfig) {
|
|
45
95
|
return cachedConfig;
|
|
46
96
|
}
|
|
@@ -67,7 +117,7 @@ export async function loadTextureConfig(configUrl = null, useCache = true) {
|
|
|
67
117
|
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
68
118
|
}
|
|
69
119
|
|
|
70
|
-
const config = await response.json();
|
|
120
|
+
const config = (await response.json()) as TextureConfig;
|
|
71
121
|
|
|
72
122
|
// Validate configuration structure
|
|
73
123
|
if (!config.textures || typeof config.textures !== 'object') {
|
|
@@ -80,8 +130,9 @@ export async function loadTextureConfig(configUrl = null, useCache = true) {
|
|
|
80
130
|
console.error('[TextureConfig] Failed to load config:', error);
|
|
81
131
|
// Return minimal fallback configuration when loading fails
|
|
82
132
|
console.warn('[TextureConfig] Using hardcoded fallback config');
|
|
83
|
-
|
|
84
|
-
|
|
133
|
+
const fallback = getHardcodedFallback();
|
|
134
|
+
cachedConfig = fallback;
|
|
135
|
+
return fallback;
|
|
85
136
|
} finally {
|
|
86
137
|
configPromise = null;
|
|
87
138
|
}
|
|
@@ -91,28 +142,36 @@ export async function loadTextureConfig(configUrl = null, useCache = true) {
|
|
|
91
142
|
}
|
|
92
143
|
|
|
93
144
|
/**
|
|
94
|
-
*
|
|
95
|
-
* @property {string} id Catalogue id.
|
|
96
|
-
* @property {string} label Human-readable name.
|
|
97
|
-
* @property {string} url Resolved texture URL (the .ktx2 the world renders).
|
|
98
|
-
* @property {string|null} still Resolved URL of the item's picture — a small thumbnail suitable
|
|
99
|
-
* for showing the texture in a picker. `null` when the catalogue names none.
|
|
145
|
+
* A choice record for one catalogue item.
|
|
100
146
|
*
|
|
101
147
|
* Every catalogue item IS a picture, so a chooser should show it rather than its name. `still` is
|
|
102
148
|
* read straight from the catalogue and never derived from `filename`: the stills do not share one
|
|
103
149
|
* extension, and a wrong guess fails silently (a broken `<img>` just shows nothing).
|
|
104
150
|
*/
|
|
151
|
+
export type TextureChoice = {
|
|
152
|
+
/** Catalogue id. */
|
|
153
|
+
id: string;
|
|
154
|
+
/** Human-readable name. */
|
|
155
|
+
label: string;
|
|
156
|
+
/** Resolved texture URL (the .ktx2 the world renders). */
|
|
157
|
+
url: string;
|
|
158
|
+
/**
|
|
159
|
+
* Resolved URL of the item's picture — a small thumbnail suitable for showing the texture in a
|
|
160
|
+
* picker. `null` when the catalogue names none.
|
|
161
|
+
*/
|
|
162
|
+
still: string | null;
|
|
163
|
+
};
|
|
105
164
|
|
|
106
165
|
/**
|
|
107
166
|
* Get the full choice records for a category (clouds, moons, etc.) — label, texture URL, and the
|
|
108
167
|
* picture to show for it. The single resolution point for a category's items; `getTextureOptions`
|
|
109
168
|
* is the flat `{ label: url }` projection of this.
|
|
110
169
|
* @param {string} category - Texture category (clouds, moons)
|
|
111
|
-
* @param {
|
|
170
|
+
* @param {TextureConfig} config - Optional pre-loaded config object
|
|
112
171
|
* @returns {Promise<TextureChoice[]>} Choice records in catalogue order
|
|
113
172
|
*/
|
|
114
|
-
export async function getTextureChoices(category, config
|
|
115
|
-
const textureConfig =
|
|
173
|
+
export async function getTextureChoices(category: string, config?: TextureConfig | null): Promise<TextureChoice[]> {
|
|
174
|
+
const textureConfig = await resolveConfig(config);
|
|
116
175
|
const categoryConfig = textureConfig.textures[category];
|
|
117
176
|
|
|
118
177
|
if (!categoryConfig) {
|
|
@@ -123,7 +182,7 @@ export async function getTextureChoices(category, config = null) {
|
|
|
123
182
|
const baseUrl = textureConfig.baseUrl || '/default';
|
|
124
183
|
const categoryPath = categoryConfig.basePath || `/${category}`;
|
|
125
184
|
|
|
126
|
-
return (categoryConfig.items || []).flatMap(item => {
|
|
185
|
+
return (categoryConfig.items || []).flatMap((item): TextureChoice[] => {
|
|
127
186
|
const url = buildTextureUrl(baseUrl, categoryPath, item.filename || item.id);
|
|
128
187
|
if (!url) return [];
|
|
129
188
|
return [{
|
|
@@ -138,10 +197,10 @@ export async function getTextureChoices(category, config = null) {
|
|
|
138
197
|
/**
|
|
139
198
|
* Get texture options for a specific category (clouds, moons, etc.)
|
|
140
199
|
* @param {string} category - Texture category (clouds, moons, skyboxes)
|
|
141
|
-
* @param {
|
|
142
|
-
* @returns {Promise<
|
|
200
|
+
* @param {TextureConfig} config - Optional pre-loaded config object
|
|
201
|
+
* @returns {Promise<Record<string, string>>} `{ label: url }`, suitable for dropdown option lists
|
|
143
202
|
*/
|
|
144
|
-
export async function getTextureOptions(category, config
|
|
203
|
+
export async function getTextureOptions(category: string, config?: TextureConfig | null): Promise<Record<string, string>> {
|
|
145
204
|
const choices = await getTextureChoices(category, config);
|
|
146
205
|
return Object.fromEntries(choices.map(c => [c.label, c.url]));
|
|
147
206
|
}
|
|
@@ -149,11 +208,11 @@ export async function getTextureOptions(category, config = null) {
|
|
|
149
208
|
/**
|
|
150
209
|
* Get default texture for a category
|
|
151
210
|
* @param {string} category - Texture category
|
|
152
|
-
* @param {
|
|
153
|
-
* @returns {Promise<string>} Default texture path
|
|
211
|
+
* @param {TextureConfig} config - Optional pre-loaded config object
|
|
212
|
+
* @returns {Promise<string|null>} Default texture path, or null when the category has no texture
|
|
154
213
|
*/
|
|
155
|
-
export async function getDefaultTexture(category, config
|
|
156
|
-
const textureConfig =
|
|
214
|
+
export async function getDefaultTexture(category: string, config?: TextureConfig | null): Promise<string | null> {
|
|
215
|
+
const textureConfig = await resolveConfig(config);
|
|
157
216
|
const categoryConfig = textureConfig.textures[category];
|
|
158
217
|
|
|
159
218
|
if (!categoryConfig) {
|
|
@@ -181,10 +240,10 @@ export async function getDefaultTexture(category, config = null) {
|
|
|
181
240
|
* Return the configured fallback URL for a given texture if defined.
|
|
182
241
|
* @param {string} category - Texture category (clouds, moons, etc.)
|
|
183
242
|
* @param {string} textureUrl - Resolved texture URL
|
|
184
|
-
* @param {
|
|
243
|
+
* @param {TextureConfig|null} config - Optional texture config override
|
|
185
244
|
* @returns {string|null} Resolved fallback texture URL or null
|
|
186
245
|
*/
|
|
187
|
-
export function getTextureFallbackUrl(category, textureUrl, config = null) {
|
|
246
|
+
export function getTextureFallbackUrl(category: string, textureUrl: string, config: TextureConfig | null = null): string | null {
|
|
188
247
|
const textureConfig = config || cachedConfig;
|
|
189
248
|
if (!textureConfig) return null;
|
|
190
249
|
const found = findTextureItem(textureConfig, category, textureUrl);
|
|
@@ -196,11 +255,11 @@ export function getTextureFallbackUrl(category, textureUrl, config = null) {
|
|
|
196
255
|
* Get texture by ID within a category
|
|
197
256
|
* @param {string} category - Texture category
|
|
198
257
|
* @param {string} textureId - Texture ID
|
|
199
|
-
* @param {
|
|
200
|
-
* @returns {Promise<
|
|
258
|
+
* @param {TextureConfig} config - Optional pre-loaded config object
|
|
259
|
+
* @returns {Promise<TextureItem|null>} Texture definition object
|
|
201
260
|
*/
|
|
202
|
-
export async function getTextureById(category, textureId, config
|
|
203
|
-
const textureConfig =
|
|
261
|
+
export async function getTextureById(category: string, textureId: string, config?: TextureConfig | null): Promise<TextureItem | null> {
|
|
262
|
+
const textureConfig = await resolveConfig(config);
|
|
204
263
|
const categoryConfig = textureConfig.textures[category];
|
|
205
264
|
|
|
206
265
|
if (!categoryConfig || !categoryConfig.items) {
|
|
@@ -214,15 +273,17 @@ export async function getTextureById(category, textureId, config = null) {
|
|
|
214
273
|
* Get full URL for a texture
|
|
215
274
|
* @param {string} category - Texture category
|
|
216
275
|
* @param {string} textureId - Texture ID
|
|
217
|
-
* @param {
|
|
276
|
+
* @param {TextureConfig} config - Optional pre-loaded config object
|
|
218
277
|
* @returns {Promise<string|null>} Full texture URL
|
|
219
278
|
*/
|
|
220
|
-
export async function getTextureUrl(category, textureId, config
|
|
279
|
+
export async function getTextureUrl(category: string, textureId: string, config?: TextureConfig | null): Promise<string | null> {
|
|
221
280
|
const texture = await getTextureById(category, textureId, config);
|
|
222
281
|
if (!texture) return null;
|
|
223
282
|
|
|
224
|
-
const textureConfig =
|
|
225
|
-
|
|
283
|
+
const textureConfig = await resolveConfig(config);
|
|
284
|
+
// `getTextureById` just found the item in this category, so it is there (unless the catalogue
|
|
285
|
+
// was reloaded in between, which throws here, as it always did).
|
|
286
|
+
const categoryConfig = textureConfig.textures[category]!;
|
|
226
287
|
const baseUrl = textureConfig.baseUrl || '/default';
|
|
227
288
|
const categoryPath = categoryConfig.basePath || `/${category}`;
|
|
228
289
|
return resolvePublicUrl(`${baseUrl}${categoryPath}/${texture.filename || texture.id}`);
|
|
@@ -232,11 +293,11 @@ export async function getTextureUrl(category, textureId, config = null) {
|
|
|
232
293
|
* Search textures by tags
|
|
233
294
|
* @param {string} category - Texture category
|
|
234
295
|
* @param {string[]} tags - Tags to search for
|
|
235
|
-
* @param {
|
|
236
|
-
* @returns {Promise<
|
|
296
|
+
* @param {TextureConfig} config - Optional pre-loaded config object
|
|
297
|
+
* @returns {Promise<TextureItem[]>} Array of matching texture definitions
|
|
237
298
|
*/
|
|
238
|
-
export async function searchTexturesByTags(category, tags, config
|
|
239
|
-
const textureConfig =
|
|
299
|
+
export async function searchTexturesByTags(category: string, tags: string[], config?: TextureConfig | null): Promise<TextureItem[]> {
|
|
300
|
+
const textureConfig = await resolveConfig(config);
|
|
240
301
|
const categoryConfig = textureConfig.textures[category];
|
|
241
302
|
|
|
242
303
|
if (!categoryConfig || !categoryConfig.items) {
|
|
@@ -244,17 +305,17 @@ export async function searchTexturesByTags(category, tags, config = null) {
|
|
|
244
305
|
}
|
|
245
306
|
|
|
246
307
|
return categoryConfig.items.filter(item =>
|
|
247
|
-
item.tags && tags.some(tag => item.tags
|
|
308
|
+
item.tags && tags.some(tag => item.tags!.includes(tag))
|
|
248
309
|
);
|
|
249
310
|
}
|
|
250
311
|
|
|
251
312
|
/**
|
|
252
313
|
* Get skybox options formatted for GUI dropdowns
|
|
253
|
-
* @param {
|
|
254
|
-
* @returns {Promise<
|
|
314
|
+
* @param {TextureConfig} config - Optional pre-loaded config object
|
|
315
|
+
* @returns {Promise<Record<string, string>>} `{ label: id }`
|
|
255
316
|
*/
|
|
256
|
-
export async function getSkyboxOptions(config
|
|
257
|
-
const textureConfig =
|
|
317
|
+
export async function getSkyboxOptions(config?: TextureConfig | null): Promise<Record<string, string>> {
|
|
318
|
+
const textureConfig = await resolveConfig(config);
|
|
258
319
|
const skyboxConfig = textureConfig.textures.skyboxes;
|
|
259
320
|
|
|
260
321
|
if (!skyboxConfig) {
|
|
@@ -262,9 +323,10 @@ export async function getSkyboxOptions(config = null) {
|
|
|
262
323
|
return {};
|
|
263
324
|
}
|
|
264
325
|
|
|
265
|
-
const options = {};
|
|
326
|
+
const options: Record<string, string> = {};
|
|
266
327
|
|
|
267
|
-
|
|
328
|
+
// Reads `items` unguarded, unlike the other readers: a skyboxes category without items throws here.
|
|
329
|
+
skyboxConfig.items!.forEach(item => {
|
|
268
330
|
const label = item.label || item.id;
|
|
269
331
|
const id = item.id;
|
|
270
332
|
options[label] = id;
|
|
@@ -275,11 +337,11 @@ export async function getSkyboxOptions(config = null) {
|
|
|
275
337
|
|
|
276
338
|
/**
|
|
277
339
|
* Get default skybox ID
|
|
278
|
-
* @param {
|
|
340
|
+
* @param {TextureConfig} config - Optional pre-loaded config object
|
|
279
341
|
* @returns {Promise<string>} Default skybox ID
|
|
280
342
|
*/
|
|
281
|
-
export async function getDefaultSkybox(config
|
|
282
|
-
const textureConfig =
|
|
343
|
+
export async function getDefaultSkybox(config?: TextureConfig | null): Promise<string> {
|
|
344
|
+
const textureConfig = await resolveConfig(config);
|
|
283
345
|
const skyboxConfig = textureConfig.textures.skyboxes;
|
|
284
346
|
|
|
285
347
|
if (!skyboxConfig) {
|
|
@@ -293,7 +355,7 @@ export async function getDefaultSkybox(config = null) {
|
|
|
293
355
|
* Hardcoded fallback configuration (minimal)
|
|
294
356
|
* Used when remote config loading fails
|
|
295
357
|
*/
|
|
296
|
-
function getHardcodedFallback() {
|
|
358
|
+
function getHardcodedFallback(): TextureConfig {
|
|
297
359
|
return {
|
|
298
360
|
version: "1.0-fallback",
|
|
299
361
|
lastUpdated: new Date().toISOString(),
|
|
@@ -331,27 +393,31 @@ function getHardcodedFallback() {
|
|
|
331
393
|
/**
|
|
332
394
|
* Reload configuration (force refresh)
|
|
333
395
|
* @param {string} configUrl - Override config URL
|
|
334
|
-
* @returns {Promise<
|
|
396
|
+
* @returns {Promise<TextureConfig>} Fresh configuration object
|
|
335
397
|
*/
|
|
336
|
-
export async function reloadTextureConfig(configUrl
|
|
398
|
+
export async function reloadTextureConfig(configUrl?: string): Promise<TextureConfig> {
|
|
337
399
|
cachedConfig = null;
|
|
338
400
|
configPromise = null;
|
|
339
|
-
return loadTextureConfig(configUrl || lastConfigUrl, false);
|
|
401
|
+
return loadTextureConfig(configUrl || lastConfigUrl || undefined, false);
|
|
340
402
|
}
|
|
341
403
|
|
|
342
404
|
/**
|
|
343
405
|
* Get configuration metadata
|
|
344
|
-
* @param {
|
|
345
|
-
* @returns {Promise<
|
|
406
|
+
* @param {TextureConfig} config - Optional pre-loaded config object
|
|
407
|
+
* @returns {Promise<TextureConfigMetadata>} Configuration metadata
|
|
346
408
|
*/
|
|
347
|
-
export async function getConfigMetadata(config
|
|
348
|
-
const textureConfig =
|
|
409
|
+
export async function getConfigMetadata(config?: TextureConfig | null): Promise<TextureConfigMetadata> {
|
|
410
|
+
const textureConfig = await resolveConfig(config);
|
|
349
411
|
return {
|
|
350
412
|
version: textureConfig.version,
|
|
351
413
|
lastUpdated: textureConfig.lastUpdated,
|
|
352
414
|
categories: Object.keys(textureConfig.textures),
|
|
353
415
|
totalTextures: Object.values(textureConfig.textures).reduce(
|
|
354
|
-
|
|
416
|
+
// Only listed categories are values; a missing one is a key that is not there.
|
|
417
|
+
(sum, cat) => {
|
|
418
|
+
const items = (cat as TextureCategory).items;
|
|
419
|
+
return sum + (items ? items.length : 0);
|
|
420
|
+
}, 0
|
|
355
421
|
)
|
|
356
422
|
};
|
|
357
423
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as THREE from "three";
|
|
2
2
|
|
|
3
|
-
function getLightFresnel({rimHex = 0x0088ff, facingHex = 0x000000} = {}) {
|
|
3
|
+
function getLightFresnel({ rimHex = 0x0088ff, facingHex = 0x000000 }: { rimHex?: number; facingHex?: number } = {}): THREE.ShaderMaterial {
|
|
4
4
|
const uniforms = {
|
|
5
5
|
color1: { value: new THREE.Color(rimHex) },
|
|
6
6
|
color2: { value: new THREE.Color(facingHex) },
|
|
@@ -8,19 +8,17 @@ import * as THREE from 'three';
|
|
|
8
8
|
* live in just one scene (adding it to a second removes it from the first), so scenes 2..N rendered
|
|
9
9
|
* DARK ("only one entangled world"). So create + add the lights PER scene, keyed by name so a
|
|
10
10
|
* re-call on the same scene is a no-op. Single-orbiter (one scene) is unchanged.
|
|
11
|
-
*
|
|
12
|
-
* @param {import('three').Scene} scene
|
|
13
|
-
* @returns {{ hemi: import('three').HemisphereLight, ambient: import('three').AmbientLight }}
|
|
14
11
|
*/
|
|
15
|
-
export function ensureDefaultLights(scene) {
|
|
16
|
-
|
|
12
|
+
export function ensureDefaultLights(scene: THREE.Scene): { hemi: THREE.HemisphereLight; ambient: THREE.AmbientLight } {
|
|
13
|
+
// Found by name: the objects under these names are the lights this function added.
|
|
14
|
+
let hemi = scene.getObjectByName('ew_HemisphereLight') as THREE.HemisphereLight | undefined;
|
|
17
15
|
if (!hemi) {
|
|
18
16
|
hemi = new THREE.HemisphereLight(0xffffff, 0xffffff, 1.0);
|
|
19
17
|
hemi.position.set(1, 1, 1);
|
|
20
18
|
hemi.name = 'ew_HemisphereLight';
|
|
21
19
|
scene.add(hemi);
|
|
22
20
|
}
|
|
23
|
-
let ambient = scene.getObjectByName('ew_AmbientLight');
|
|
21
|
+
let ambient = scene.getObjectByName('ew_AmbientLight') as THREE.AmbientLight | undefined;
|
|
24
22
|
if (!ambient) {
|
|
25
23
|
ambient = new THREE.AmbientLight(0xffffff, 1.0);
|
|
26
24
|
ambient.name = 'ew_AmbientLight';
|
|
@@ -37,16 +35,22 @@ export function ensureDefaultLights(scene) {
|
|
|
37
35
|
* them — a shared-renderer page (N feed worlds, N orbiters) lit the wrong world.
|
|
38
36
|
*
|
|
39
37
|
* Every field is optional; an omitted one leaves that part of the lighting as it is.
|
|
40
|
-
*
|
|
41
|
-
* @param {import('three').Scene} scene
|
|
42
|
-
* @param {object} [patch]
|
|
43
|
-
* @param {number} [patch.ambientIntensity] ambient light intensity (clamped at 0)
|
|
44
|
-
* @param {number} [patch.diffuseIntensity] hemisphere light intensity (clamped at 0)
|
|
45
|
-
* @param {import('three').Color | number | string} [patch.lightColor] hemisphere light colour
|
|
46
|
-
* @param {{ x: number, y?: number, z?: number }} [patch.lightDirection] hemisphere light direction
|
|
47
|
-
* @param {boolean} [patch.hemisphere] switch the hemisphere light on or off
|
|
48
38
|
*/
|
|
49
|
-
export function updateDefaultLights(
|
|
39
|
+
export function updateDefaultLights(
|
|
40
|
+
scene: THREE.Scene,
|
|
41
|
+
{ ambientIntensity, diffuseIntensity, lightColor, lightDirection, hemisphere }: {
|
|
42
|
+
/** ambient light intensity (clamped at 0) */
|
|
43
|
+
ambientIntensity?: number;
|
|
44
|
+
/** hemisphere light intensity (clamped at 0) */
|
|
45
|
+
diffuseIntensity?: number;
|
|
46
|
+
/** hemisphere light colour */
|
|
47
|
+
lightColor?: THREE.Color | number | string;
|
|
48
|
+
/** hemisphere light direction */
|
|
49
|
+
lightDirection?: { x: number; y?: number; z?: number };
|
|
50
|
+
/** switch the hemisphere light on or off */
|
|
51
|
+
hemisphere?: boolean;
|
|
52
|
+
} = {},
|
|
53
|
+
): void {
|
|
50
54
|
if (!scene?.isObject3D) {
|
|
51
55
|
console.error('[lights] updateDefaultLights(scene, patch) requires the scene to update; ignoring patch.');
|
|
52
56
|
return;
|
|
@@ -75,18 +79,14 @@ export function updateDefaultLights(scene, { ambientIntensity, diffuseIntensity,
|
|
|
75
79
|
/**
|
|
76
80
|
* Switch a scene's default hemisphere light on or off (the ambient light is untouched). Hosts turn it
|
|
77
81
|
* off when a directional light — the star — lights the scene, so night sides can read.
|
|
78
|
-
*
|
|
79
|
-
* @param {import('three').Scene} scene
|
|
80
|
-
* @param {boolean} on
|
|
81
82
|
*/
|
|
82
|
-
export function setHemisphereEnabled(scene, on) {
|
|
83
|
+
export function setHemisphereEnabled(scene: THREE.Scene, on: boolean): void {
|
|
83
84
|
if (!scene?.isObject3D) return;
|
|
84
85
|
const { hemi } = ensureDefaultLights(scene);
|
|
85
86
|
hemi.visible = Boolean(on);
|
|
86
87
|
}
|
|
87
88
|
|
|
88
89
|
// Back-compat helper
|
|
89
|
-
|
|
90
|
-
export function addDefaultLights(scene) {
|
|
90
|
+
export function addDefaultLights(scene: THREE.Scene): { hemi: THREE.HemisphereLight; ambient: THREE.AmbientLight } {
|
|
91
91
|
return ensureDefaultLights(scene);
|
|
92
92
|
}
|
|
@@ -4,16 +4,19 @@ import { getKTX2LoaderForRenderer } from './ktx2.js';
|
|
|
4
4
|
const textureLoader = new THREE.TextureLoader();
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* @param {AbortSignal} [opts.signal] an abort during the load rejects the promise.
|
|
11
|
-
* @param {import('three').WebGLRenderer | null} [opts.renderer] REQUIRED for a `.ktx2` url.
|
|
7
|
+
* `url`: `.ktx2` goes through the renderer's KTX2 transcoder; anything else through
|
|
8
|
+
* THREE.TextureLoader. `signal`: an abort during the load rejects the promise. `renderer`: REQUIRED
|
|
9
|
+
* for a `.ktx2` url.
|
|
12
10
|
*/
|
|
13
|
-
export function loadTexture(
|
|
11
|
+
export function loadTexture(
|
|
12
|
+
url: string,
|
|
13
|
+
{ signal, renderer }: { signal?: AbortSignal | undefined; renderer?: THREE.WebGLRenderer | null | undefined } = {},
|
|
14
|
+
): Promise<THREE.Texture> {
|
|
14
15
|
return new Promise((resolve, reject) => {
|
|
15
16
|
if (url && url.endsWith('.ktx2')) {
|
|
16
|
-
|
|
17
|
+
// A missing renderer throws inside (as before): the type allows `null` only because the
|
|
18
|
+
// non-KTX2 path never needs one.
|
|
19
|
+
const ktx2 = getKTX2LoaderForRenderer(renderer as THREE.WebGLRenderer);
|
|
17
20
|
const onAbort = () => reject(new Error('aborted'));
|
|
18
21
|
if (signal) signal.addEventListener('abort', onAbort, { once: true });
|
|
19
22
|
ktx2.load(
|