entangled-worlds-orbiters-shared 1.0.0 → 1.0.2
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 +42 -0
- package/package.json +2 -2
- package/src/config/env.js +30 -0
- package/src/config/index.js +8 -1
- package/src/config/textureConfig.js +9 -7
- package/src/loaders/cubeMapLoader.js +3 -2
- package/src/moons/moonsGeneration.js +2 -1
- package/src/world/worldNormal.js +46 -0
- package/src/world/worldTexture.js +2 -1
package/README.md
CHANGED
|
@@ -62,6 +62,48 @@ import * as EntangledWorlds from 'entangled-worlds-orbiters-shared';
|
|
|
62
62
|
### Configuration
|
|
63
63
|
- `textureConfig` - Texture configuration system
|
|
64
64
|
|
|
65
|
+
## Runtime base configuration (env)
|
|
66
|
+
|
|
67
|
+
This package is built statically. At runtime, it can use a public base URL to turn relative asset paths into absolute URLs. If you don’t provide a base, it will use same-origin root-relative paths (e.g., `/default/...`).
|
|
68
|
+
|
|
69
|
+
You have two ways to provide it:
|
|
70
|
+
|
|
71
|
+
1) Environment variables (no code needed):
|
|
72
|
+
|
|
73
|
+
If your bundler/framework defines `import.meta.env.VITE_PUBLIC_HERBARIUM_BASE` (Vite) or you set `window.VITE_PUBLIC_HERBARIUM_BASE` (before the library runs), the library will pick it up automatically.
|
|
74
|
+
|
|
75
|
+
2) Programmatic override (recommended for portability):
|
|
76
|
+
|
|
77
|
+
```js
|
|
78
|
+
import { setPublicBase, loadTextureConfig } from 'entangled-worlds-orbiters-shared/config';
|
|
79
|
+
|
|
80
|
+
// Call this early at app startup (optional if you use envs/window)
|
|
81
|
+
setPublicBase(
|
|
82
|
+
(typeof import.meta !== 'undefined' && import.meta.env && import.meta.env.VITE_PUBLIC_HERBARIUM_BASE) ||
|
|
83
|
+
(typeof process !== 'undefined' && process.env && process.env.NEXT_PUBLIC_HERBARIUM_BASE)
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
// Optionally preload a remote config file
|
|
87
|
+
// await loadTextureConfig('https://your-cdn/config/textures.json');
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Is setPublicBase required?
|
|
91
|
+
|
|
92
|
+
- Not required. If you don’t provide any base (via env/window/programmatic), the library will use same-origin root-relative URLs.
|
|
93
|
+
- Also not required if you provide `VITE_PUBLIC_HERBARIUM_BASE` via env (Vite) or set `window.VITE_PUBLIC_HERBARIUM_BASE` before using the library.
|
|
94
|
+
- Use `setPublicBase` when you want to:
|
|
95
|
+
- Override the base at runtime (e.g., switch between staging/production dynamically)
|
|
96
|
+
- Avoid build-time coupling to a specific bundler env key
|
|
97
|
+
- Ensure the base is set in environments where env injection isn’t available
|
|
98
|
+
|
|
99
|
+
Defaults:
|
|
100
|
+
- If nothing is provided, it falls back to same-origin root-relative paths.
|
|
101
|
+
|
|
102
|
+
Effects:
|
|
103
|
+
- All URLs returned by `config/textureConfig` helpers are absolute.
|
|
104
|
+
- Skybox loader and moons generation resolve relative paths against the configured base.
|
|
105
|
+
- Cloud shell textures passed to `world.makeSphere` are resolved to absolute URLs.
|
|
106
|
+
|
|
65
107
|
## Peer Dependencies
|
|
66
108
|
|
|
67
109
|
This library requires Three.js and three-stdlib as peer dependencies:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "entangled-worlds-orbiters-shared",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "Shared library for entangled worlds orbiters - contains core world rendering, moons system, and UI components",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.js",
|
|
@@ -46,4 +46,4 @@
|
|
|
46
46
|
"url": "https://github.com/plantasia-space-org/entangled-worlds-orbiters-shared/issues"
|
|
47
47
|
},
|
|
48
48
|
"homepage": "https://github.com/plantasia-space-org/entangled-worlds-orbiters-shared#readme"
|
|
49
|
-
}
|
|
49
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// Runtime public base URL helper for building absolute asset URLs at runtime
|
|
2
|
+
|
|
3
|
+
// Mutable module-scoped variable to allow apps to override at startup
|
|
4
|
+
export let __PUBLIC_BASE = null;
|
|
5
|
+
|
|
6
|
+
// Set the public base (strips trailing slash) and mirror to window for convenience
|
|
7
|
+
export function setPublicBase(url) {
|
|
8
|
+
__PUBLIC_BASE = (url || '').replace(/\/$/, '');
|
|
9
|
+
if (typeof window !== 'undefined') window.VITE_PUBLIC_HERBARIUM_BASE = __PUBLIC_BASE;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// Get the current public base from (in order): explicit set value, envs, or default
|
|
13
|
+
export function getPublicBase() {
|
|
14
|
+
const envVal =
|
|
15
|
+
(typeof import.meta !== 'undefined' && import.meta.env && import.meta.env.VITE_PUBLIC_HERBARIUM_BASE) ||
|
|
16
|
+
(typeof window !== 'undefined' && window.VITE_PUBLIC_HERBARIUM_BASE) ||
|
|
17
|
+
null;
|
|
18
|
+
const base = (__PUBLIC_BASE || envVal || '').toString();
|
|
19
|
+
return base ? base.replace(/\/$/, '') : '';
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Resolve a possibly-relative path against the current public base
|
|
23
|
+
export function resolvePublicUrl(path) {
|
|
24
|
+
if (!path) return path;
|
|
25
|
+
if (/^https?:\/\//i.test(path)) return path;
|
|
26
|
+
const base = getPublicBase();
|
|
27
|
+
const p = path.startsWith('/') ? path : `/${path}`;
|
|
28
|
+
// If base is empty, return a root-relative path (same-origin)
|
|
29
|
+
return base ? `${base}${p}` : p;
|
|
30
|
+
}
|
package/src/config/index.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* Texture Configuration Loader
|
|
3
3
|
* Loads texture definitions from local config file or remote endpoint
|
|
4
4
|
*/
|
|
5
|
+
import { resolvePublicUrl } from './env.js';
|
|
5
6
|
|
|
6
7
|
const DEFAULT_CONFIG_URL = '/config/textures.json';
|
|
7
8
|
const FALLBACK_CONFIG_URL = 'https://api.entangled-worlds.com/config/textures.json'; // Future endpoint
|
|
@@ -28,8 +29,10 @@ export async function loadTextureConfig(configUrl = DEFAULT_CONFIG_URL, useCache
|
|
|
28
29
|
|
|
29
30
|
configPromise = (async () => {
|
|
30
31
|
try {
|
|
31
|
-
|
|
32
|
-
const
|
|
32
|
+
// Resolve config URL against public base if it's a relative path
|
|
33
|
+
const resolvedConfigUrl = /^https?:\/\//i.test(configUrl) ? configUrl : resolvePublicUrl(configUrl);
|
|
34
|
+
console.log('[TextureConfig] Loading from:', resolvedConfigUrl);
|
|
35
|
+
const response = await fetch(resolvedConfigUrl);
|
|
33
36
|
|
|
34
37
|
if (!response.ok) {
|
|
35
38
|
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
@@ -96,7 +99,7 @@ export async function getTextureOptions(category, config = null) {
|
|
|
96
99
|
categoryConfig.items.forEach(item => {
|
|
97
100
|
const key = item.label || item.id;
|
|
98
101
|
const relativePath = `${baseUrl}${categoryPath}/${item.filename || item.id}`;
|
|
99
|
-
options[key] = relativePath;
|
|
102
|
+
options[key] = resolvePublicUrl(relativePath);
|
|
100
103
|
});
|
|
101
104
|
|
|
102
105
|
return options;
|
|
@@ -121,13 +124,13 @@ export async function getDefaultTexture(category, config = null) {
|
|
|
121
124
|
const defaultFilename = categoryConfig.defaultTexture;
|
|
122
125
|
|
|
123
126
|
if (defaultFilename) {
|
|
124
|
-
return `${baseUrl}${categoryPath}/${defaultFilename}
|
|
127
|
+
return resolvePublicUrl(`${baseUrl}${categoryPath}/${defaultFilename}`);
|
|
125
128
|
}
|
|
126
129
|
|
|
127
130
|
// Fallback to first item
|
|
128
131
|
if (categoryConfig.items && categoryConfig.items.length > 0) {
|
|
129
132
|
const firstItem = categoryConfig.items[0];
|
|
130
|
-
return `${baseUrl}${categoryPath}/${firstItem.filename || firstItem.id}
|
|
133
|
+
return resolvePublicUrl(`${baseUrl}${categoryPath}/${firstItem.filename || firstItem.id}`);
|
|
131
134
|
}
|
|
132
135
|
|
|
133
136
|
return null;
|
|
@@ -166,8 +169,7 @@ export async function getTextureUrl(category, textureId, config = null) {
|
|
|
166
169
|
const categoryConfig = textureConfig.textures[category];
|
|
167
170
|
const baseUrl = textureConfig.baseUrl || '/default';
|
|
168
171
|
const categoryPath = categoryConfig.basePath || `/${category}`;
|
|
169
|
-
|
|
170
|
-
return `${baseUrl}${categoryPath}/${texture.filename || texture.id}`;
|
|
172
|
+
return resolvePublicUrl(`${baseUrl}${categoryPath}/${texture.filename || texture.id}`);
|
|
171
173
|
}
|
|
172
174
|
|
|
173
175
|
/**
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import * as THREE from 'three';
|
|
2
2
|
import { KTX2Loader } from 'three/examples/jsm/loaders/KTX2Loader.js';
|
|
3
|
+
import { getPublicBase } from '../config/env.js';
|
|
3
4
|
|
|
4
|
-
// Base URL
|
|
5
|
-
const PUBLIC_BASE = (import.meta?.env?.VITE_PUBLIC_HERBARIUM_BASE) || (window?.VITE_PUBLIC_HERBARIUM_BASE) || 'https://dev-herbarium.plantasia.space';
|
|
5
|
+
// Base URL is resolved at runtime via helper
|
|
6
6
|
|
|
7
7
|
// Shared loader + caches
|
|
8
8
|
const ktx2SupportCache = new Map(); // base -> true|false
|
|
@@ -137,6 +137,7 @@ function loadFacesJPG(faceKeys) {
|
|
|
137
137
|
|
|
138
138
|
// Resolve relative paths to full URLs
|
|
139
139
|
function resolveUrls(faceKeys) {
|
|
140
|
+
const PUBLIC_BASE = getPublicBase();
|
|
140
141
|
const resolved = {};
|
|
141
142
|
FACE_ORDER.forEach(face => {
|
|
142
143
|
const url = faceKeys[face];
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import * as THREE from 'three';
|
|
2
2
|
import { loadTexture } from '../loaders/bodyTextureLoader.js';
|
|
3
|
+
import { getPublicBase } from '../config/env.js';
|
|
3
4
|
|
|
4
5
|
// Resolve public herbarium base (strip trailing slash)
|
|
5
|
-
const HERBARIUM_BASE = (
|
|
6
|
+
const HERBARIUM_BASE = getPublicBase();
|
|
6
7
|
function moonTexPath(file) { return `${HERBARIUM_BASE}/default/textures/moons/${file}`; }
|
|
7
8
|
export const DEFAULT_MOON_TEX_KTX2 = moonTexPath('moon-equirectangular.ktx2');
|
|
8
9
|
export const DEFAULT_MOON_TEX_PNG = moonTexPath('moon-equirectangular.png');
|
package/src/world/worldNormal.js
CHANGED
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
import * as THREE from 'three';
|
|
3
3
|
import { EARTH_RADIUS_UNITS } from './worldTexture.js';
|
|
4
4
|
import { applyFlagsToMaterial } from '../utils/materials.js';
|
|
5
|
+
import { getLightFresnel } from '../core/lightFresnel.js';
|
|
6
|
+
import { createCloudShell } from './cloudShell.js';
|
|
7
|
+
import { resolvePublicUrl } from '../config/env.js';
|
|
5
8
|
|
|
6
9
|
/**
|
|
7
10
|
* Create a simple sphere using THREE.MeshNormalMaterial.
|
|
@@ -12,6 +15,18 @@ export async function makeNormalSphere({
|
|
|
12
15
|
sphereWidthSegments,
|
|
13
16
|
sphereHeightSegments,
|
|
14
17
|
flags,
|
|
18
|
+
glow, // halo (glow) enabled
|
|
19
|
+
glowScale = 1.18,
|
|
20
|
+
glowRimHex = 0x66ccff,
|
|
21
|
+
glowFacingHex = 0x000000,
|
|
22
|
+
glowBias = 0.02,
|
|
23
|
+
glowFresnelScale = 1.0,
|
|
24
|
+
glowPower = 3.5,
|
|
25
|
+
cloudsEnabled,
|
|
26
|
+
cloudsTextureUrl = '/assets/texture/2k_earth_clouds.jpg',
|
|
27
|
+
cloudsColor = 0xffffff,
|
|
28
|
+
cloudsOpacity = 0.6,
|
|
29
|
+
cloudsSpeed = 0.015,
|
|
15
30
|
} = {}) {
|
|
16
31
|
const radius = typeof sphereRadius === 'number' ? Math.max(0.0001, sphereRadius) : EARTH_RADIUS_UNITS;
|
|
17
32
|
const widthSegs = Number.isFinite(sphereWidthSegments) ? Math.max(3, Math.floor(sphereWidthSegments)) : 64;
|
|
@@ -21,5 +36,36 @@ export async function makeNormalSphere({
|
|
|
21
36
|
if (flags) applyFlagsToMaterial(material, flags);
|
|
22
37
|
const mesh = new THREE.Mesh(geometry, material);
|
|
23
38
|
mesh.name = 'worldNormalSphere';
|
|
39
|
+
|
|
40
|
+
// Attach halo (glow) effect if enabled
|
|
41
|
+
if (glow) {
|
|
42
|
+
const fresnelMat = getLightFresnel({ rimHex: glowRimHex, facingHex: glowFacingHex });
|
|
43
|
+
fresnelMat.uniforms.fresnelBias.value = glowBias;
|
|
44
|
+
fresnelMat.uniforms.fresnelScale.value = glowFresnelScale;
|
|
45
|
+
fresnelMat.uniforms.fresnelPower.value = glowPower;
|
|
46
|
+
|
|
47
|
+
const glowGeom = geometry.clone();
|
|
48
|
+
const fresnelMesh = new THREE.Mesh(glowGeom, fresnelMat);
|
|
49
|
+
fresnelMesh.scale.set(glowScale, glowScale, glowScale);
|
|
50
|
+
fresnelMesh.renderOrder = 999;
|
|
51
|
+
fresnelMesh.name = 'worldNormalGlow';
|
|
52
|
+
mesh.add(fresnelMesh);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Attach clouds effect if enabled
|
|
56
|
+
if (cloudsEnabled) {
|
|
57
|
+
const cloudMesh = await createCloudShell({
|
|
58
|
+
baseGeometry: geometry,
|
|
59
|
+
baseRadius: radius,
|
|
60
|
+
widthSegments: widthSegs,
|
|
61
|
+
heightSegments: heightSegs,
|
|
62
|
+
textureUrl: resolvePublicUrl(cloudsTextureUrl),
|
|
63
|
+
color: cloudsColor,
|
|
64
|
+
opacity: cloudsOpacity,
|
|
65
|
+
speed: cloudsSpeed,
|
|
66
|
+
});
|
|
67
|
+
mesh.add(cloudMesh);
|
|
68
|
+
}
|
|
69
|
+
|
|
24
70
|
return mesh;
|
|
25
71
|
}
|
|
@@ -3,6 +3,7 @@ import { loadTexture } from '../loaders/bodyTextureLoader.js';
|
|
|
3
3
|
import { applyFlagsToMaterial } from '../utils/materials.js';
|
|
4
4
|
import { getLightFresnel } from '../core/lightFresnel.js';
|
|
5
5
|
import { createCloudShell } from './cloudShell.js';
|
|
6
|
+
import { resolvePublicUrl } from '../config/env.js';
|
|
6
7
|
|
|
7
8
|
export const EARTH_RADIUS_UNITS = 0.5; // scene units
|
|
8
9
|
|
|
@@ -220,7 +221,7 @@ export async function makeSphere({
|
|
|
220
221
|
baseRadius: radius,
|
|
221
222
|
widthSegments: widthSegs,
|
|
222
223
|
heightSegments: heightSegs,
|
|
223
|
-
textureUrl: cloudsTextureUrl,
|
|
224
|
+
textureUrl: resolvePublicUrl(cloudsTextureUrl),
|
|
224
225
|
color: cloudsColor,
|
|
225
226
|
opacity: cloudsOpacity,
|
|
226
227
|
speed: cloudsSpeed,
|