entangled-worlds-orbiters-shared 2.8.0 → 2.9.1
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/package.json +1 -1
- package/public/config/textures.json +33 -1
- package/src/config/index.js +4 -3
- package/src/config/textureConfig.js +40 -14
- package/src/loaders/cubeMapLoader.js +37 -15
package/package.json
CHANGED
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
{
|
|
12
12
|
"id": "water-drops",
|
|
13
13
|
"filename": "18-water-drops.ktx2",
|
|
14
|
+
"still": "thumbs/18-water-drops.jpg",
|
|
14
15
|
"label": "Water Drops",
|
|
15
16
|
"description": "Atmospheric water droplets",
|
|
16
17
|
"tags": ["atmospheric", "water", "natural"]
|
|
@@ -18,6 +19,7 @@
|
|
|
18
19
|
{
|
|
19
20
|
"id": "scream-1",
|
|
20
21
|
"filename": "17-scream.ktx2",
|
|
22
|
+
"still": "thumbs/17-scream.jpg",
|
|
21
23
|
"label": "Scream",
|
|
22
24
|
"description": "Abstract screaming patterns",
|
|
23
25
|
"tags": ["abstract", "artistic"]
|
|
@@ -25,6 +27,7 @@
|
|
|
25
27
|
{
|
|
26
28
|
"id": "scream-2",
|
|
27
29
|
"filename": "16-scream.ktx2",
|
|
30
|
+
"still": "thumbs/16-scream.jpg",
|
|
28
31
|
"label": "Scream Alt",
|
|
29
32
|
"description": "Alternative screaming patterns",
|
|
30
33
|
"tags": ["abstract", "artistic"]
|
|
@@ -32,6 +35,7 @@
|
|
|
32
35
|
{
|
|
33
36
|
"id": "neon-lights-1",
|
|
34
37
|
"filename": "15-neon-lights.ktx2",
|
|
38
|
+
"still": "thumbs/15-neon-lights.jpg",
|
|
35
39
|
"label": "Neon Lights",
|
|
36
40
|
"description": "Electric neon lighting effects",
|
|
37
41
|
"tags": ["electric", "urban", "artificial"]
|
|
@@ -39,6 +43,7 @@
|
|
|
39
43
|
{
|
|
40
44
|
"id": "neon-lights-2",
|
|
41
45
|
"filename": "14-neon-lights.ktx2",
|
|
46
|
+
"still": "thumbs/14-neon-lights.jpg",
|
|
42
47
|
"label": "Neon Lights Alt",
|
|
43
48
|
"description": "Alternative neon lighting",
|
|
44
49
|
"tags": ["electric", "urban", "artificial"]
|
|
@@ -46,6 +51,7 @@
|
|
|
46
51
|
{
|
|
47
52
|
"id": "neon-lights-3",
|
|
48
53
|
"filename": "13-neon-lights.ktx2",
|
|
54
|
+
"still": "thumbs/13-neon-lights.jpg",
|
|
49
55
|
"label": "Neon Lights Pro",
|
|
50
56
|
"description": "Professional neon effects",
|
|
51
57
|
"tags": ["electric", "urban", "artificial"]
|
|
@@ -53,6 +59,7 @@
|
|
|
53
59
|
{
|
|
54
60
|
"id": "marble-1",
|
|
55
61
|
"filename": "12-marble.ktx2",
|
|
62
|
+
"still": "thumbs/12-marble.jpg",
|
|
56
63
|
"label": "Marble",
|
|
57
64
|
"description": "Natural marble patterns",
|
|
58
65
|
"tags": ["stone", "natural", "elegant"]
|
|
@@ -60,6 +67,7 @@
|
|
|
60
67
|
{
|
|
61
68
|
"id": "marble-2",
|
|
62
69
|
"filename": "11-marble.ktx2",
|
|
70
|
+
"still": "thumbs/11-marble.jpg",
|
|
63
71
|
"label": "Marble Alt",
|
|
64
72
|
"description": "Alternative marble texture",
|
|
65
73
|
"tags": ["stone", "natural", "elegant"]
|
|
@@ -67,6 +75,7 @@
|
|
|
67
75
|
{
|
|
68
76
|
"id": "isolines-1",
|
|
69
77
|
"filename": "10-isolines.ktx2",
|
|
78
|
+
"still": "thumbs/10-isolines.jpg",
|
|
70
79
|
"label": "Isolines",
|
|
71
80
|
"description": "Topographic isolines",
|
|
72
81
|
"tags": ["geometric", "lines", "technical"]
|
|
@@ -74,6 +83,7 @@
|
|
|
74
83
|
{
|
|
75
84
|
"id": "isolines-2",
|
|
76
85
|
"filename": "09-isolines.ktx2",
|
|
86
|
+
"still": "thumbs/09-isolines.jpg",
|
|
77
87
|
"label": "Isolines Alt",
|
|
78
88
|
"description": "Alternative isolines pattern",
|
|
79
89
|
"tags": ["geometric", "lines", "technical"]
|
|
@@ -81,6 +91,7 @@
|
|
|
81
91
|
{
|
|
82
92
|
"id": "fordite-1",
|
|
83
93
|
"filename": "08-fordite.ktx2",
|
|
94
|
+
"still": "thumbs/08-fordite.jpg",
|
|
84
95
|
"label": "Fordite",
|
|
85
96
|
"description": "Industrial fordite patterns",
|
|
86
97
|
"tags": ["industrial", "layered", "colorful"]
|
|
@@ -88,6 +99,7 @@
|
|
|
88
99
|
{
|
|
89
100
|
"id": "fordite-2",
|
|
90
101
|
"filename": "07-fordite.ktx2",
|
|
102
|
+
"still": "thumbs/07-fordite.jpg",
|
|
91
103
|
"label": "Fordite Alt",
|
|
92
104
|
"description": "Alternative fordite texture",
|
|
93
105
|
"tags": ["industrial", "layered", "colorful"]
|
|
@@ -95,6 +107,7 @@
|
|
|
95
107
|
{
|
|
96
108
|
"id": "entangled-1",
|
|
97
109
|
"filename": "06-entangled.ktx2",
|
|
110
|
+
"still": "thumbs/06-entangled.jpg",
|
|
98
111
|
"label": "Entangled",
|
|
99
112
|
"description": "Quantum entangled patterns",
|
|
100
113
|
"tags": ["quantum", "abstract", "complex"]
|
|
@@ -102,6 +115,7 @@
|
|
|
102
115
|
{
|
|
103
116
|
"id": "entangled-2",
|
|
104
117
|
"filename": "05-entangled.ktx2",
|
|
118
|
+
"still": "thumbs/05-entangled.jpg",
|
|
105
119
|
"label": "Entangled Alt",
|
|
106
120
|
"description": "Alternative entangled patterns",
|
|
107
121
|
"tags": ["quantum", "abstract", "complex"]
|
|
@@ -109,6 +123,7 @@
|
|
|
109
123
|
{
|
|
110
124
|
"id": "clouds-1",
|
|
111
125
|
"filename": "04-clouds.ktx2",
|
|
126
|
+
"still": "thumbs/04-clouds.jpg",
|
|
112
127
|
"label": "Clouds",
|
|
113
128
|
"description": "Natural cloud formations",
|
|
114
129
|
"tags": ["natural", "atmospheric", "default"]
|
|
@@ -116,6 +131,7 @@
|
|
|
116
131
|
{
|
|
117
132
|
"id": "clouds-2",
|
|
118
133
|
"filename": "03-clouds.ktx2",
|
|
134
|
+
"still": "thumbs/03-clouds.jpg",
|
|
119
135
|
"label": "Clouds Alt",
|
|
120
136
|
"description": "Alternative cloud patterns",
|
|
121
137
|
"tags": ["natural", "atmospheric"]
|
|
@@ -123,6 +139,7 @@
|
|
|
123
139
|
{
|
|
124
140
|
"id": "cave-art-1",
|
|
125
141
|
"filename": "02-cave-art.ktx2",
|
|
142
|
+
"still": "thumbs/02-cave-art.jpg",
|
|
126
143
|
"label": "Cave Art",
|
|
127
144
|
"description": "Ancient cave paintings",
|
|
128
145
|
"tags": ["ancient", "artistic", "historic"]
|
|
@@ -130,6 +147,7 @@
|
|
|
130
147
|
{
|
|
131
148
|
"id": "cave-art-2",
|
|
132
149
|
"filename": "01-cave-art.ktx2",
|
|
150
|
+
"still": "thumbs/01-cave-art.jpg",
|
|
133
151
|
"label": "Cave Art Alt",
|
|
134
152
|
"description": "Alternative cave art patterns",
|
|
135
153
|
"tags": ["ancient", "artistic", "historic"]
|
|
@@ -144,14 +162,16 @@
|
|
|
144
162
|
{
|
|
145
163
|
"id": "electric-moon",
|
|
146
164
|
"filename": "moon-equirectangular.ktx2",
|
|
165
|
+
"still": "thumbs/moon-equirectangular.jpg",
|
|
147
166
|
"label": "Electric Moon",
|
|
148
167
|
"description": "Standard lunar surface texture",
|
|
149
168
|
"tags": ["lunar", "realistic", "default"],
|
|
150
|
-
"fallback": "moon-equirectangular.
|
|
169
|
+
"fallback": "moon-equirectangular.png"
|
|
151
170
|
},
|
|
152
171
|
{
|
|
153
172
|
"id": "earth-moon",
|
|
154
173
|
"filename": "2k_moon.ktx2",
|
|
174
|
+
"still": "thumbs/2k_moon.jpg",
|
|
155
175
|
"label": "Earth Moon",
|
|
156
176
|
"description": "Realistic Earth satellite texture",
|
|
157
177
|
"tags": ["lunar", "earth", "satellite"],
|
|
@@ -160,6 +180,7 @@
|
|
|
160
180
|
{
|
|
161
181
|
"id": "crater-lines",
|
|
162
182
|
"filename": "crater-lines.ktx2",
|
|
183
|
+
"still": "thumbs/crater-lines.jpg",
|
|
163
184
|
"label": "Crater Lines",
|
|
164
185
|
"description": "Linear crater formations across the lunar surface",
|
|
165
186
|
"tags": ["craters", "lunar", "pattern"],
|
|
@@ -168,6 +189,7 @@
|
|
|
168
189
|
{
|
|
169
190
|
"id": "moon-dust",
|
|
170
191
|
"filename": "moon-dust.ktx2",
|
|
192
|
+
"still": "thumbs/moon-dust.jpg",
|
|
171
193
|
"label": "Moon Dust",
|
|
172
194
|
"description": "Fine dust texture inspired by lunar regolith",
|
|
173
195
|
"tags": ["dust", "surface", "regolith"],
|
|
@@ -176,6 +198,7 @@
|
|
|
176
198
|
{
|
|
177
199
|
"id": "lunar-scepter",
|
|
178
200
|
"filename": "lunar-scepter.ktx2",
|
|
201
|
+
"still": "thumbs/lunar-scepter.jpg",
|
|
179
202
|
"label": "Lunar Scepter",
|
|
180
203
|
"description": "Sharp, vertical ridge texture reminiscent of lunar highlands",
|
|
181
204
|
"tags": ["ridges", "highlands", "lunar"],
|
|
@@ -184,6 +207,7 @@
|
|
|
184
207
|
{
|
|
185
208
|
"id": "satin-moon",
|
|
186
209
|
"filename": "satin-moon.ktx2",
|
|
210
|
+
"still": "thumbs/satin-moon.jpg",
|
|
187
211
|
"label": "Satin Moon",
|
|
188
212
|
"description": "Smooth satin-like lunar surface",
|
|
189
213
|
"tags": ["smooth", "lunar", "texture"],
|
|
@@ -192,6 +216,7 @@
|
|
|
192
216
|
{
|
|
193
217
|
"id": "impact-craters",
|
|
194
218
|
"filename": "impact-craters.ktx2",
|
|
219
|
+
"still": "thumbs/impact-craters.jpg",
|
|
195
220
|
"label": "Impact Craters",
|
|
196
221
|
"description": "Dense field of lunar craters",
|
|
197
222
|
"tags": ["craters", "impact", "texture"],
|
|
@@ -200,6 +225,7 @@
|
|
|
200
225
|
{
|
|
201
226
|
"id": "eclipse-dots",
|
|
202
227
|
"filename": "eclipse-dots.ktx2",
|
|
228
|
+
"still": "thumbs/eclipse-dots.jpg",
|
|
203
229
|
"label": "Eclipse Dots",
|
|
204
230
|
"description": "Pattern of scattered dots inspired by lunar eclipses",
|
|
205
231
|
"tags": ["dots", "eclipse", "pattern"],
|
|
@@ -208,6 +234,7 @@
|
|
|
208
234
|
{
|
|
209
235
|
"id": "shadow-dots",
|
|
210
236
|
"filename": "shadow-dots.ktx2",
|
|
237
|
+
"still": "thumbs/shadow-dots.jpg",
|
|
211
238
|
"label": "Shadow Dots",
|
|
212
239
|
"description": "Dot textures evoking shadows over the lunar surface",
|
|
213
240
|
"tags": ["dots", "shadows", "surface"],
|
|
@@ -216,6 +243,7 @@
|
|
|
216
243
|
{
|
|
217
244
|
"id": "moonrise",
|
|
218
245
|
"filename": "moonrise.ktx2",
|
|
246
|
+
"still": "thumbs/moonrise.jpg",
|
|
219
247
|
"label": "Moonrise",
|
|
220
248
|
"description": "Atmospheric gradient evoking moonrise",
|
|
221
249
|
"tags": ["moonrise", "gradient", "lunar"],
|
|
@@ -224,6 +252,7 @@
|
|
|
224
252
|
{
|
|
225
253
|
"id": "midnight-moon",
|
|
226
254
|
"filename": "midnight-moon.ktx2",
|
|
255
|
+
"still": "thumbs/midnight-moon.jpg",
|
|
227
256
|
"label": "Midnight Moon",
|
|
228
257
|
"description": "Dark-toned lunar surface texture",
|
|
229
258
|
"tags": ["midnight", "dark", "lunar"],
|
|
@@ -232,6 +261,7 @@
|
|
|
232
261
|
{
|
|
233
262
|
"id": "pale-orbit",
|
|
234
263
|
"filename": "pale-orbit.ktx2",
|
|
264
|
+
"still": "thumbs/pale-orbit.jpg",
|
|
235
265
|
"label": "Pale Orbit",
|
|
236
266
|
"description": "Light lunar surface with subtle orbital tones",
|
|
237
267
|
"tags": ["orbit", "pale", "surface"],
|
|
@@ -240,6 +270,7 @@
|
|
|
240
270
|
{
|
|
241
271
|
"id": "tidal-glow",
|
|
242
272
|
"filename": "tidal-glow.ktx2",
|
|
273
|
+
"still": "thumbs/tidal-glow.jpg",
|
|
243
274
|
"label": "Tidal Glow",
|
|
244
275
|
"description": "Soft glowing lunar texture reflecting tidal pull",
|
|
245
276
|
"tags": ["glow", "tidal", "lunar"],
|
|
@@ -248,6 +279,7 @@
|
|
|
248
279
|
{
|
|
249
280
|
"id": "silver-glow",
|
|
250
281
|
"filename": "silver-glow.ktx2",
|
|
282
|
+
"still": "thumbs/silver-glow.jpg",
|
|
251
283
|
"label": "Silver Glow",
|
|
252
284
|
"description": "Bright silver surface with reflective highlights",
|
|
253
285
|
"tags": ["silver", "glow", "lunar"],
|
package/src/config/index.js
CHANGED
|
@@ -91,33 +91,59 @@ export async function loadTextureConfig(configUrl = null, useCache = true) {
|
|
|
91
91
|
}
|
|
92
92
|
|
|
93
93
|
/**
|
|
94
|
-
*
|
|
95
|
-
* @
|
|
94
|
+
* @typedef {Object} TextureChoice
|
|
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.
|
|
100
|
+
*
|
|
101
|
+
* Every catalogue item IS a picture, so a chooser should show it rather than its name. `still` is
|
|
102
|
+
* read straight from the catalogue and never derived from `filename`: the stills do not share one
|
|
103
|
+
* extension, and a wrong guess fails silently (a broken `<img>` just shows nothing).
|
|
104
|
+
*/
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Get the full choice records for a category (clouds, moons, etc.) — label, texture URL, and the
|
|
108
|
+
* picture to show for it. The single resolution point for a category's items; `getTextureOptions`
|
|
109
|
+
* is the flat `{ label: url }` projection of this.
|
|
110
|
+
* @param {string} category - Texture category (clouds, moons)
|
|
96
111
|
* @param {Object} config - Optional pre-loaded config object
|
|
97
|
-
* @returns {Promise<
|
|
112
|
+
* @returns {Promise<TextureChoice[]>} Choice records in catalogue order
|
|
98
113
|
*/
|
|
99
|
-
export async function
|
|
114
|
+
export async function getTextureChoices(category, config = null) {
|
|
100
115
|
const textureConfig = config || await loadTextureConfig();
|
|
101
116
|
const categoryConfig = textureConfig.textures[category];
|
|
102
|
-
|
|
117
|
+
|
|
103
118
|
if (!categoryConfig) {
|
|
104
119
|
console.warn(`[TextureConfig] Category '${category}' not found`);
|
|
105
|
-
return
|
|
120
|
+
return [];
|
|
106
121
|
}
|
|
107
122
|
|
|
108
|
-
const options = {};
|
|
109
123
|
const baseUrl = textureConfig.baseUrl || '/default';
|
|
110
124
|
const categoryPath = categoryConfig.basePath || `/${category}`;
|
|
111
125
|
|
|
112
|
-
categoryConfig.items.
|
|
113
|
-
const
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
126
|
+
return (categoryConfig.items || []).flatMap(item => {
|
|
127
|
+
const url = buildTextureUrl(baseUrl, categoryPath, item.filename || item.id);
|
|
128
|
+
if (!url) return [];
|
|
129
|
+
return [{
|
|
130
|
+
id: item.id,
|
|
131
|
+
label: item.label || item.id,
|
|
132
|
+
url,
|
|
133
|
+
still: item.still ? buildTextureUrl(baseUrl, categoryPath, item.still) : null,
|
|
134
|
+
}];
|
|
118
135
|
});
|
|
136
|
+
}
|
|
119
137
|
|
|
120
|
-
|
|
138
|
+
/**
|
|
139
|
+
* Get texture options for a specific category (clouds, moons, etc.)
|
|
140
|
+
* @param {string} category - Texture category (clouds, moons, skyboxes)
|
|
141
|
+
* @param {Object} config - Optional pre-loaded config object
|
|
142
|
+
* @returns {Promise<Object>} Options object suitable for dropdown option lists
|
|
143
|
+
*/
|
|
144
|
+
export async function getTextureOptions(category, config = null) {
|
|
145
|
+
const choices = await getTextureChoices(category, config);
|
|
146
|
+
return Object.fromEntries(choices.map(c => [c.label, c.url]));
|
|
121
147
|
}
|
|
122
148
|
|
|
123
149
|
/**
|
|
@@ -56,25 +56,47 @@ function detectExtension(faceKeys) {
|
|
|
56
56
|
return 'ktx2'; // default assumption
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
+
// How many faces may be in flight at once. Not six: the KTX2 worker pool three.js gives us holds
|
|
60
|
+
// four transcoder workers, each carrying its own copy of the basis wasm, and that same pool also
|
|
61
|
+
// serves body textures and GLBs. Asking for all six together spawned every worker and queued the
|
|
62
|
+
// other loaders behind the whole sky; measured on cold fetches it was no faster than three.
|
|
63
|
+
const FACES_IN_FLIGHT = 3;
|
|
64
|
+
|
|
59
65
|
// Load KTX2 faces and create skybox mesh
|
|
60
66
|
async function loadFacesKTX2(faceKeys, renderer) {
|
|
61
67
|
const loader = getKTX2LoaderForRenderer(renderer);
|
|
68
|
+
const anisotropy = Math.min(8, renderer.capabilities.getMaxAnisotropy());
|
|
69
|
+
|
|
70
|
+
const loadFace = async face => {
|
|
71
|
+
const tex = await loader.loadAsync(faceKeys[face]);
|
|
72
|
+
tex.flipY = false;
|
|
73
|
+
tex.wrapS = THREE.ClampToEdgeWrapping;
|
|
74
|
+
tex.wrapT = THREE.ClampToEdgeWrapping;
|
|
75
|
+
tex.anisotropy = anisotropy;
|
|
76
|
+
return { face, tex };
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
// Faces overlap instead of chaining. Awaiting each one in turn made every face wait on the
|
|
80
|
+
// previous face's download *and* its transcode, so the sky arrived six round trips deep.
|
|
81
|
+
const settled = [];
|
|
82
|
+
for (let i = 0; i < FACE_ORDER.length; i += FACES_IN_FLIGHT) {
|
|
83
|
+
const wave = FACE_ORDER.slice(i, i + FACES_IN_FLIGHT).map(face => loadFace(face));
|
|
84
|
+
settled.push(...await Promise.allSettled(wave));
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// allSettled, not all: a bare Promise.all rejects on the first failure and leaks the faces that
|
|
88
|
+
// land after it. Settle every face first, then dispose everything that arrived before rethrowing.
|
|
62
89
|
const loaded = [];
|
|
63
|
-
|
|
64
|
-
for (const
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
} catch (e) {
|
|
74
|
-
// Dispose already loaded textures on failure
|
|
75
|
-
loaded.forEach(f => f.tex.dispose?.());
|
|
76
|
-
throw e;
|
|
77
|
-
}
|
|
90
|
+
let failure = null;
|
|
91
|
+
for (const result of settled) {
|
|
92
|
+
if (result.status === 'fulfilled') loaded.push(result.value);
|
|
93
|
+
else if (!failure) failure = result.reason;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (failure) {
|
|
97
|
+
// Dispose already loaded textures on failure
|
|
98
|
+
loaded.forEach(f => f.tex.dispose?.());
|
|
99
|
+
throw failure;
|
|
78
100
|
}
|
|
79
101
|
|
|
80
102
|
const size = 500;
|