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 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.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
+ }
@@ -5,4 +5,11 @@ export {
5
5
  getDefaultTexture,
6
6
  getSkyboxOptions,
7
7
  getDefaultSkybox
8
- } from './textureConfig.js';
8
+ } from './textureConfig.js';
9
+
10
+ // runtime base helpers
11
+ export {
12
+ setPublicBase,
13
+ getPublicBase,
14
+ resolvePublicUrl
15
+ } from './env.js';
@@ -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
- console.log('[TextureConfig] Loading from:', configUrl);
32
- const response = await fetch(configUrl);
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 = (import.meta?.env?.VITE_PUBLIC_HERBARIUM_BASE || 'https://dev-herbarium.plantasia.space').replace(/\/$/, '');
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');
@@ -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,