@jgengine/shell 0.10.0 → 0.11.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 (234) hide show
  1. package/CHANGELOG.md +13 -2
  2. package/LICENSE +203 -661
  3. package/README.md +1 -1
  4. package/dist/GamePhaseStamp.d.ts +1 -0
  5. package/dist/GamePhaseStamp.js +9 -0
  6. package/dist/GamePlayerShell.d.ts +0 -12
  7. package/dist/GamePlayerShell.js +88 -1043
  8. package/dist/GameUiPreview.js +0 -2
  9. package/dist/audio/audioEngine.js +30 -8
  10. package/dist/audio/audioWire.d.ts +22 -0
  11. package/dist/audio/audioWire.js +19 -0
  12. package/dist/behaviour.d.ts +2 -1
  13. package/dist/behaviour.js +2 -1
  14. package/dist/behaviourAttach.d.ts +1 -0
  15. package/dist/behaviourAttach.js +1 -0
  16. package/dist/behaviourDriver.d.ts +1 -0
  17. package/dist/behaviourDriver.js +1 -0
  18. package/dist/boundActionDispatch.d.ts +13 -0
  19. package/dist/boundActionDispatch.js +37 -0
  20. package/dist/camera/GameCameraRig.d.ts +5 -2
  21. package/dist/camera/GameCameraRig.js +2 -2
  22. package/dist/camera/GameFirstPersonCamera.d.ts +9 -2
  23. package/dist/camera/GameFirstPersonCamera.js +8 -3
  24. package/dist/camera/GameInspectionCamera.d.ts +1 -1
  25. package/dist/camera/GameInspectionCamera.js +2 -2
  26. package/dist/camera/GameOrbitCamera.js +24 -9
  27. package/dist/camera/cameraBlendMath.d.ts +3 -0
  28. package/dist/camera/cameraBlendMath.js +3 -0
  29. package/dist/camera/fovPreference.d.ts +6 -1
  30. package/dist/camera/fovPreference.js +6 -1
  31. package/dist/camera/index.d.ts +4 -7
  32. package/dist/camera/index.js +4 -7
  33. package/dist/camera/inspectionCameraMath.d.ts +7 -2
  34. package/dist/camera/inspectionCameraMath.js +7 -2
  35. package/dist/camera/orbitCameraMath.d.ts +36 -6
  36. package/dist/camera/orbitCameraMath.js +56 -6
  37. package/dist/camera/rigMath.d.ts +94 -24
  38. package/dist/camera/rigMath.js +100 -24
  39. package/dist/camera/rigResolve.d.ts +4 -2
  40. package/dist/camera/rigResolve.js +4 -2
  41. package/dist/camera/shakeChannel.d.ts +6 -2
  42. package/dist/camera/shakeChannel.js +6 -2
  43. package/dist/camera/shakeChannelMath.d.ts +1 -0
  44. package/dist/camera/shakeChannelMath.js +1 -0
  45. package/dist/cartridge/runtime.d.ts +15 -0
  46. package/dist/cartridge/runtime.js +556 -0
  47. package/dist/cartridge/spec.d.ts +188 -0
  48. package/dist/cartridge/spec.js +19 -0
  49. package/dist/cartridge/validate.d.ts +2 -0
  50. package/dist/cartridge/validate.js +78 -0
  51. package/dist/cartridge.d.ts +1 -1
  52. package/dist/cartridge.js +3 -3
  53. package/dist/commandSink.d.ts +8 -3
  54. package/dist/commandSink.js +8 -3
  55. package/dist/defineGame.js +25 -6
  56. package/dist/devtools/ColPanel.d.ts +1 -0
  57. package/dist/devtools/ColPanel.js +14 -0
  58. package/dist/devtools/DevtoolsOverlay.d.ts +7 -3
  59. package/dist/devtools/DevtoolsOverlay.js +19 -450
  60. package/dist/devtools/KeysPanel.d.ts +4 -0
  61. package/dist/devtools/KeysPanel.js +31 -0
  62. package/dist/devtools/LogsPanel.d.ts +1 -0
  63. package/dist/devtools/LogsPanel.js +12 -0
  64. package/dist/devtools/NetPanel.d.ts +4 -0
  65. package/dist/devtools/NetPanel.js +10 -0
  66. package/dist/devtools/PerfPanel.d.ts +4 -0
  67. package/dist/devtools/PerfPanel.js +53 -0
  68. package/dist/devtools/TunePanel.d.ts +3 -0
  69. package/dist/devtools/TunePanel.js +309 -0
  70. package/dist/devtools/agentBridge.d.ts +41 -0
  71. package/dist/devtools/agentBridge.js +262 -0
  72. package/dist/devtools/collisionDebug.d.ts +6 -0
  73. package/dist/devtools/collisionDebug.js +6 -0
  74. package/dist/devtools/collisionDebugMath.d.ts +8 -2
  75. package/dist/devtools/collisionDebugMath.js +24 -5
  76. package/dist/devtools/devtoolsOverrides.d.ts +6 -0
  77. package/dist/devtools/devtoolsOverrides.js +48 -0
  78. package/dist/devtools/panelAtoms.d.ts +9 -0
  79. package/dist/devtools/panelAtoms.js +10 -0
  80. package/dist/devtools/perfDiagnose.d.ts +2 -0
  81. package/dist/devtools/perfDiagnose.js +43 -0
  82. package/dist/diagnostics/RuntimeDiagnostics.d.ts +29 -0
  83. package/dist/diagnostics/RuntimeDiagnostics.js +69 -0
  84. package/dist/drivers/FrameDriver.d.ts +37 -0
  85. package/dist/drivers/FrameDriver.js +232 -0
  86. package/dist/drivers/HudOnlyDriver.d.ts +16 -0
  87. package/dist/drivers/HudOnlyDriver.js +60 -0
  88. package/dist/environment/Daylight.d.ts +23 -7
  89. package/dist/environment/Daylight.js +106 -13
  90. package/dist/environment/EnvironmentScene.js +32 -6
  91. package/dist/environment/VolumetricClouds.d.ts +17 -0
  92. package/dist/environment/VolumetricClouds.js +202 -0
  93. package/dist/environment/daylightCycle.d.ts +3 -1
  94. package/dist/environment/daylightCycle.js +3 -1
  95. package/dist/environment/groundPadMath.d.ts +3 -0
  96. package/dist/environment/groundPadMath.js +3 -0
  97. package/dist/environment/index.d.ts +1 -0
  98. package/dist/environment/index.js +1 -0
  99. package/dist/environment/skyLightingPolicy.d.ts +2 -0
  100. package/dist/environment/skyLightingPolicy.js +2 -0
  101. package/dist/hotbarActions.d.ts +26 -0
  102. package/dist/hotbarActions.js +27 -0
  103. package/dist/inputSink.d.ts +16 -4
  104. package/dist/inputSink.js +57 -7
  105. package/dist/materialOverride.d.ts +10 -0
  106. package/dist/materialOverride.js +15 -3
  107. package/dist/multiplayer.js +8 -2
  108. package/dist/postfx/PostProcessing.js +47 -9
  109. package/dist/postfx/gradeShader.js +9 -1
  110. package/dist/registry.d.ts +3 -2
  111. package/dist/render/EnvironmentLighting.d.ts +10 -0
  112. package/dist/render/EnvironmentLighting.js +34 -0
  113. package/dist/render/SceneLighting.d.ts +7 -0
  114. package/dist/render/SceneLighting.js +18 -0
  115. package/dist/render/SceneModels.d.ts +14 -0
  116. package/dist/render/SceneModels.js +337 -0
  117. package/dist/render/defaultSurface.d.ts +29 -0
  118. package/dist/render/defaultSurface.js +101 -0
  119. package/dist/render/modelLoad.d.ts +24 -0
  120. package/dist/render/modelLoad.js +73 -0
  121. package/dist/render/modelRender.d.ts +11 -0
  122. package/dist/render/modelRender.js +15 -0
  123. package/dist/render/resolveModel.d.ts +39 -3
  124. package/dist/render/resolveModel.js +79 -2
  125. package/dist/render/useEntityRenderCues.d.ts +13 -0
  126. package/dist/render/useEntityRenderCues.js +62 -0
  127. package/dist/scatter/InstancedScatter.d.ts +24 -0
  128. package/dist/scatter/InstancedScatter.js +130 -0
  129. package/dist/scatter/scatterModels.d.ts +25 -0
  130. package/dist/scatter/scatterModels.js +61 -0
  131. package/dist/scatter/scatterProxies.d.ts +6 -0
  132. package/dist/scatter/scatterProxies.js +94 -0
  133. package/dist/scatter.d.ts +1 -0
  134. package/dist/scatter.js +1 -0
  135. package/dist/scene/AuthoredScene.d.ts +82 -0
  136. package/dist/scene/AuthoredScene.js +173 -0
  137. package/dist/scene/GeneratedAssetRenderer.d.ts +14 -0
  138. package/dist/scene/GeneratedAssetRenderer.js +59 -0
  139. package/dist/scene/GrassFieldRenderer.d.ts +2 -0
  140. package/dist/scene/GrassFieldRenderer.js +25 -0
  141. package/dist/scene/SoilPatchRenderer.d.ts +2 -0
  142. package/dist/scene/SoilPatchRenderer.js +21 -0
  143. package/dist/scene/StudioStage.d.ts +37 -0
  144. package/dist/scene/StudioStage.js +42 -0
  145. package/dist/scene/WaterRenderer.d.ts +2 -0
  146. package/dist/scene/WaterRenderer.js +37 -0
  147. package/dist/scene/builtinSceneKindRenderers.d.ts +2 -0
  148. package/dist/scene/builtinSceneKindRenderers.js +22 -0
  149. package/dist/scene/sceneKindRenderers.d.ts +28 -0
  150. package/dist/scene/sceneKindRenderers.js +17 -0
  151. package/dist/scene.d.ts +1 -0
  152. package/dist/scene.js +1 -0
  153. package/dist/settings/appliedSettings.d.ts +5 -0
  154. package/dist/settings/appliedSettings.js +9 -0
  155. package/dist/settings/settingsController.d.ts +2 -0
  156. package/dist/settings/settingsController.js +22 -1
  157. package/dist/shellConstants.d.ts +4 -0
  158. package/dist/shellConstants.js +4 -0
  159. package/dist/shellMovement.d.ts +8 -0
  160. package/dist/shellMovement.js +11 -0
  161. package/dist/shellPointer.d.ts +20 -0
  162. package/dist/shellPointer.js +28 -0
  163. package/dist/structures/PlacementGhost.d.ts +4 -0
  164. package/dist/structures/PlacementGhost.js +4 -0
  165. package/dist/structures/TransformGizmo.d.ts +43 -0
  166. package/dist/structures/TransformGizmo.js +55 -0
  167. package/dist/structures/index.d.ts +1 -0
  168. package/dist/structures/index.js +1 -0
  169. package/dist/terrain/GrassField.js +1 -1
  170. package/dist/terrain/TerraformBrushCursor.js +3 -0
  171. package/dist/terrain/grassBudget.d.ts +11 -2
  172. package/dist/terrain/grassBudget.js +20 -4
  173. package/dist/terrain/grassGeometry.d.ts +3 -0
  174. package/dist/terrain/grassGeometry.js +3 -0
  175. package/dist/terrain/grassMaterial.d.ts +2 -0
  176. package/dist/terrain/grassMaterial.js +2 -0
  177. package/dist/terrain/random.d.ts +3 -0
  178. package/dist/terrain/random.js +3 -0
  179. package/dist/terrain/soilPatchMaterial.d.ts +10 -0
  180. package/dist/terrain/soilPatchMaterial.js +109 -0
  181. package/dist/terrain/terrainDetailMaterial.d.ts +19 -2
  182. package/dist/terrain/terrainDetailMaterial.js +72 -5
  183. package/dist/terrain/terrainMath.d.ts +8 -1
  184. package/dist/terrain/terrainMath.js +8 -1
  185. package/dist/touch/TouchControlsOverlay.d.ts +9 -5
  186. package/dist/touch/TouchControlsOverlay.js +201 -22
  187. package/dist/useShellMultiplayerSync.d.ts +9 -0
  188. package/dist/useShellMultiplayerSync.js +98 -0
  189. package/dist/visibility/CullingProvider.js +1 -1
  190. package/dist/water/OceanConfig.d.ts +2 -0
  191. package/dist/water/OceanConfig.js +2 -0
  192. package/dist/weather/fireSpreadPose.d.ts +1 -0
  193. package/dist/weather/fireSpreadPose.js +1 -0
  194. package/dist/weather/weatherGeometry.d.ts +1 -0
  195. package/dist/weather/weatherGeometry.js +1 -0
  196. package/dist/weather/weatherMath.d.ts +3 -0
  197. package/dist/weather/weatherMath.js +3 -0
  198. package/dist/weather/weatherUniforms.d.ts +3 -0
  199. package/dist/weather/weatherUniforms.js +3 -0
  200. package/dist/world/RetainedVfx.d.ts +10 -0
  201. package/dist/world/RetainedVfx.js +136 -0
  202. package/dist/world/WorldHud.d.ts +49 -2
  203. package/dist/world/WorldHud.js +87 -2
  204. package/dist/world/WorldScene.d.ts +21 -0
  205. package/dist/world/WorldScene.js +159 -0
  206. package/dist/world/WorldVfx.d.ts +9 -0
  207. package/dist/world/WorldVfx.js +184 -0
  208. package/dist/world/entityPose.d.ts +2 -0
  209. package/dist/world/entityPose.js +2 -0
  210. package/dist/world/floatTextStyle.d.ts +1 -0
  211. package/dist/world/floatTextStyle.js +1 -0
  212. package/dist/world/telegraphPulse.d.ts +1 -0
  213. package/dist/world/telegraphPulse.js +1 -0
  214. package/dist/world/worldBarSamples.d.ts +25 -0
  215. package/dist/world/worldBarSamples.js +38 -0
  216. package/dist/worldSky.d.ts +5 -0
  217. package/dist/worldSky.js +11 -0
  218. package/dist/worldSync.d.ts +2 -1
  219. package/dist/worldSync.js +2 -1
  220. package/package.json +6 -6
  221. package/skills/jgengine/SKILL.md +51 -0
  222. package/skills/jgengine/api.md +434 -0
  223. package/skills/jgengine/capabilities.md +29 -0
  224. package/skills/jgengine/reference-cartridge.md +72 -0
  225. package/skills/jgengine-ui/SKILL.md +36 -0
  226. package/skills/jgengine-ui/api.md +1852 -0
  227. package/skills/jgengine-ui/capabilities.md +142 -0
  228. package/skills/jgengine-ui/reference.md +500 -0
  229. package/skills/jgengine-ui/references/visual-scorecard.md +73 -0
  230. package/skills/jgengine-world/SKILL.md +38 -0
  231. package/skills/jgengine-world/api.md +2652 -0
  232. package/skills/jgengine-world/capabilities.md +370 -0
  233. package/skills/jgengine-world/reference.md +222 -0
  234. package/llms.txt +0 -2761
@@ -22,6 +22,9 @@ export interface ResolvedGrassBladeGeometryOptions {
22
22
  bend: readonly [min: number, max: number];
23
23
  heightAt: TerrainHeightSampler;
24
24
  }
25
+ /** @internal */
25
26
  export declare function resolveGrassRange(value: GrassRange | undefined, fallback: readonly [number, number]): readonly [number, number];
27
+ /** @internal */
26
28
  export declare function resolveGrassBladeGeometryOptions(options?: GrassBladeGeometryOptions): ResolvedGrassBladeGeometryOptions;
29
+ /** @internal */
27
30
  export declare function createGrassBladeGeometry(options?: GrassBladeGeometryOptions): THREE.InstancedBufferGeometry;
@@ -1,11 +1,13 @@
1
1
  import * as THREE from "three";
2
2
  import { createSeededRandom } from "./random.js";
3
3
  import { resolveTerrainSize } from "./terrainMath.js";
4
+ /** @internal */
4
5
  export function resolveGrassRange(value, fallback) {
5
6
  if (value === undefined)
6
7
  return fallback;
7
8
  return typeof value === "number" ? [value, value] : value;
8
9
  }
10
+ /** @internal */
9
11
  export function resolveGrassBladeGeometryOptions(options = {}) {
10
12
  return {
11
13
  count: Math.max(0, Math.floor(options.count ?? 1500)),
@@ -21,6 +23,7 @@ export function resolveGrassBladeGeometryOptions(options = {}) {
21
23
  function randomRange(random, range) {
22
24
  return THREE.MathUtils.lerp(range[0], range[1], random());
23
25
  }
26
+ /** @internal */
24
27
  export function createGrassBladeGeometry(options = {}) {
25
28
  const resolved = resolveGrassBladeGeometryOptions(options);
26
29
  const size = resolveTerrainSize(resolved.area);
@@ -29,5 +29,7 @@ export interface GrassMaterialHandle {
29
29
  uniforms: GrassShaderUniforms;
30
30
  }
31
31
  export declare const DEFAULT_GRASS_WIND: Required<GrassWindOptions>;
32
+ /** @internal */
32
33
  export declare function resolveGrassWind(wind: GrassWindOptions | false | undefined): Required<GrassWindOptions>;
34
+ /** @internal */
33
35
  export declare function createGrassMaterial(options?: GrassMaterialOptions): GrassMaterialHandle;
@@ -10,6 +10,7 @@ function normalizeWindDirection(direction) {
10
10
  const vector = new THREE.Vector2(direction[0], direction[1]);
11
11
  return vector.lengthSq() === 0 ? new THREE.Vector2(1, 0) : vector.normalize();
12
12
  }
13
+ /** @internal */
13
14
  export function resolveGrassWind(wind) {
14
15
  if (wind === false)
15
16
  return { ...DEFAULT_GRASS_WIND, strength: 0, flutter: 0 };
@@ -21,6 +22,7 @@ export function resolveGrassWind(wind) {
21
22
  flutter: wind?.flutter ?? DEFAULT_GRASS_WIND.flutter,
22
23
  };
23
24
  }
25
+ /** @internal */
24
26
  export function createGrassMaterial(options = {}) {
25
27
  const wind = resolveGrassWind(options.wind);
26
28
  const uniforms = {
@@ -1,4 +1,7 @@
1
1
  export type TerrainSeed = number | string;
2
+ /** @internal */
2
3
  export declare function seedToUint32(seed?: TerrainSeed): number;
4
+ /** @internal */
3
5
  export declare function createSeededRandom(seed?: TerrainSeed): () => number;
6
+ /** @internal */
4
7
  export declare function hashNoise2(x: number, z: number, seed?: TerrainSeed): number;
@@ -1,3 +1,4 @@
1
+ /** @internal */
1
2
  export function seedToUint32(seed = 1) {
2
3
  if (typeof seed === "number")
3
4
  return seed >>> 0;
@@ -8,6 +9,7 @@ export function seedToUint32(seed = 1) {
8
9
  }
9
10
  return hash >>> 0;
10
11
  }
12
+ /** @internal */
11
13
  export function createSeededRandom(seed = 1) {
12
14
  let state = seedToUint32(seed);
13
15
  return () => {
@@ -18,6 +20,7 @@ export function createSeededRandom(seed = 1) {
18
20
  return ((value ^ (value >>> 14)) >>> 0) / 4294967296;
19
21
  };
20
22
  }
23
+ /** @internal */
21
24
  export function hashNoise2(x, z, seed = 1) {
22
25
  let hash = seedToUint32(seed);
23
26
  hash ^= Math.imul(x | 0, 374761393);
@@ -0,0 +1,10 @@
1
+ import * as THREE from "three";
2
+ import type { SoilRules } from "@jgengine/core/world/soilKind";
3
+ /**
4
+ * A `MeshStandardMaterial` for one authored `soil` volume: a base dirt tint with a Worley crack-line
5
+ * network and an FBM moss-patch overlay injected via `onBeforeCompile`, seeded from `rules.seed`. Full
6
+ * PBR — lit, shadowed, fogged — like the terrain detail material it borrows its noise machinery from.
7
+ * A game never calls this directly — author a soil patch via the editor's `soil` studio kind.
8
+ * @internal
9
+ */
10
+ export declare function createSoilPatchMaterial(rules: SoilRules): THREE.MeshStandardMaterial;
@@ -0,0 +1,109 @@
1
+ import * as THREE from "three";
2
+ /**
3
+ * Worley (F1/F2 cellular) crack mask + FBM moss mask — the same noise-driven-shader technique as
4
+ * `terrainDetailMaterial.ts`'s rock/sand/snow blend, injected here via `onBeforeCompile` into a
5
+ * dedicated soil-patch material instead of the whole-ground detail material. `F2 - F1` (the gap
6
+ * between nearest and second-nearest Worley feature distances) goes to zero right at a cell boundary,
7
+ * which reads as a network of cracked-earth veins once thresholded — the standard "cracked ground"
8
+ * shader trick.
9
+ */
10
+ const SOIL_NOISE_GLSL = /* glsl */ `
11
+ uint jgSoilHashUint(uvec2 v){
12
+ uint x = v.x * 1664525u + v.y * 1013904223u;
13
+ x = (x ^ (x >> 16u)) * 2246822519u;
14
+ x = (x ^ (x >> 13u)) * 3266489917u;
15
+ x = x ^ (x >> 16u);
16
+ return x;
17
+ }
18
+ float jgSoilHash1(vec2 p){
19
+ return float(jgSoilHashUint(uvec2(floor(p)))) * (1.0 / 4294967296.0);
20
+ }
21
+ vec2 jgSoilHash2(vec2 p){
22
+ uvec2 base = uvec2(floor(p));
23
+ float a = float(jgSoilHashUint(base)) * (1.0 / 4294967296.0);
24
+ float b = float(jgSoilHashUint(base + uvec2(19u, 47u))) * (1.0 / 4294967296.0);
25
+ return vec2(a, b);
26
+ }
27
+ float jgSoilVNoise(vec2 p){
28
+ vec2 i = floor(p); vec2 f = fract(p);
29
+ vec2 u = f * f * (3.0 - 2.0 * f);
30
+ float a = jgSoilHash1(i);
31
+ float b = jgSoilHash1(i + vec2(1.0, 0.0));
32
+ float c = jgSoilHash1(i + vec2(0.0, 1.0));
33
+ float d = jgSoilHash1(i + vec2(1.0, 1.0));
34
+ return mix(mix(a, b, u.x), mix(c, d, u.x), u.y);
35
+ }
36
+ float jgSoilFbm(vec2 p){
37
+ float v = 0.0; float amp = 0.5;
38
+ for (int i = 0; i < 4; i++){ v += amp * jgSoilVNoise(p); p *= 2.0; amp *= 0.5; }
39
+ return v;
40
+ }
41
+ vec2 jgSoilWorleyF1F2(vec2 p){
42
+ vec2 ip = floor(p);
43
+ vec2 fp = fract(p);
44
+ float f1 = 8.0; float f2 = 8.0;
45
+ for (int y = -1; y <= 1; y++){
46
+ for (int x = -1; x <= 1; x++){
47
+ vec2 neighbor = vec2(float(x), float(y));
48
+ vec2 point = jgSoilHash2(ip + neighbor);
49
+ float dist = length(neighbor + point - fp);
50
+ if (dist < f1) { f2 = f1; f1 = dist; }
51
+ else if (dist < f2) { f2 = dist; }
52
+ }
53
+ }
54
+ return vec2(f1, f2);
55
+ }
56
+ `;
57
+ function hashSeed(text) {
58
+ let h = 2166136261 >>> 0;
59
+ for (let i = 0; i < text.length; i += 1) {
60
+ h ^= text.charCodeAt(i);
61
+ h = Math.imul(h, 16777619);
62
+ }
63
+ return (h >>> 0) % 100000;
64
+ }
65
+ /**
66
+ * A `MeshStandardMaterial` for one authored `soil` volume: a base dirt tint with a Worley crack-line
67
+ * network and an FBM moss-patch overlay injected via `onBeforeCompile`, seeded from `rules.seed`. Full
68
+ * PBR — lit, shadowed, fogged — like the terrain detail material it borrows its noise machinery from.
69
+ * A game never calls this directly — author a soil patch via the editor's `soil` studio kind.
70
+ * @internal
71
+ */
72
+ export function createSoilPatchMaterial(rules) {
73
+ const seedOffset = hashSeed(rules.seed);
74
+ const material = new THREE.MeshStandardMaterial({
75
+ color: rules.baseColor,
76
+ roughness: 0.92,
77
+ metalness: 0,
78
+ });
79
+ material.onBeforeCompile = (shader) => {
80
+ shader.uniforms.uSoilCrackColor = { value: new THREE.Color(rules.crackColor) };
81
+ shader.uniforms.uSoilCrackScale = { value: Math.max(0.1, rules.crackScale) };
82
+ shader.uniforms.uSoilCrackIntensity = { value: rules.crackIntensity };
83
+ shader.uniforms.uSoilMossColor = { value: new THREE.Color(rules.mossColor) };
84
+ shader.uniforms.uSoilMossCoverage = { value: rules.mossCoverage };
85
+ shader.uniforms.uSoilSeed = { value: seedOffset };
86
+ shader.vertexShader = shader.vertexShader.replace("#include <common>", `#include <common>
87
+ varying vec3 vJgSoilWorldPos;`).replace("#include <begin_vertex>", `#include <begin_vertex>
88
+ vJgSoilWorldPos = (modelMatrix * vec4(transformed, 1.0)).xyz;`);
89
+ shader.fragmentShader = shader.fragmentShader.replace("#include <common>", `#include <common>
90
+ varying vec3 vJgSoilWorldPos;
91
+ uniform vec3 uSoilCrackColor;
92
+ uniform float uSoilCrackScale;
93
+ uniform float uSoilCrackIntensity;
94
+ uniform vec3 uSoilMossColor;
95
+ uniform float uSoilMossCoverage;
96
+ uniform float uSoilSeed;
97
+ ${SOIL_NOISE_GLSL}`).replace("#include <color_fragment>", `#include <color_fragment>
98
+ vec2 jgSoilP = vJgSoilWorldPos.xz + vec2(uSoilSeed * 0.37, uSoilSeed * 0.71);
99
+ vec2 jgSoilF1F2 = jgSoilWorleyF1F2(jgSoilP / uSoilCrackScale);
100
+ float jgSoilCrack = 1.0 - smoothstep(0.0, 0.1, jgSoilF1F2.y - jgSoilF1F2.x);
101
+ vec3 jgSoilCol = mix(diffuseColor.rgb, uSoilCrackColor, jgSoilCrack * uSoilCrackIntensity);
102
+ float jgSoilMossN = jgSoilFbm(jgSoilP / 2.4);
103
+ float jgSoilMossMask = smoothstep(1.0 - uSoilMossCoverage, 1.0 - uSoilMossCoverage + 0.18, jgSoilMossN);
104
+ jgSoilCol = mix(jgSoilCol, uSoilMossColor, jgSoilMossMask);
105
+ diffuseColor.rgb = jgSoilCol;`);
106
+ };
107
+ material.customProgramCacheKey = () => `jgengine-soil-${rules.crackScale}-${rules.crackIntensity}-${rules.mossCoverage}-${seedOffset}`;
108
+ return material;
109
+ }
@@ -4,11 +4,28 @@ import type { ResolvedTerrainDetail } from "@jgengine/core/world/terrain";
4
4
  export interface TerrainDetailMaterialHandle {
5
5
  material: THREE.MeshStandardMaterial;
6
6
  }
7
+ /**
8
+ * Loaded PBR textures matching a resolved `ResolvedTerrainDetailMaterial.maps`' roles. The caller
9
+ * (a React component, via `useTexture`/`useLoader`) owns loading and disposal; `core` never touches
10
+ * `THREE.Texture` and this shell function never fetches a URL itself.
11
+ */
12
+ export interface TerrainDetailMaterialTextures {
13
+ color: THREE.Texture;
14
+ normal: THREE.Texture;
15
+ roughness: THREE.Texture;
16
+ ao: THREE.Texture;
17
+ }
7
18
  /**
8
19
  * A `MeshStandardMaterial` whose fragment shader keeps the biome-tinted vertex
9
20
  * colour as the base ground and blends procedural, noise-broken rock (by slope),
10
21
  * sand (by waterline), and snow (by height) over it — textured-reading terrain
11
22
  * with no image assets. Full PBR: lit, shadowed, and fogged like any standard
12
23
  * material, so it composes with the post-processing chain.
13
- */
14
- export declare function createTerrainDetailMaterial(detail: ResolvedTerrainDetail): TerrainDetailMaterialHandle;
24
+ *
25
+ * When `detail.material` is set, pass the matching loaded `textures` (color/normal/roughness/ao):
26
+ * they tile by world position at `detail.material.repeat` world units per tile and blend over the
27
+ * procedural result at `detail.material.strength` — the real-texture seam layers onto the existing
28
+ * look instead of replacing it.
29
+ * @internal
30
+ */
31
+ export declare function createTerrainDetailMaterial(detail: ResolvedTerrainDetail, textures?: TerrainDetailMaterialTextures): TerrainDetailMaterialHandle;
@@ -1,6 +1,16 @@
1
1
  import * as THREE from "three";
2
2
  const NOISE_GLSL = /* glsl */ `
3
- float jgHash(vec2 p){ return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453); }
3
+ uint jgHashUint(uvec2 v){
4
+ uint x = v.x * 1664525u + v.y * 1013904223u;
5
+ x = (x ^ (x >> 16u)) * 2246822519u;
6
+ x = (x ^ (x >> 13u)) * 3266489917u;
7
+ x = x ^ (x >> 16u);
8
+ return x;
9
+ }
10
+ float jgHash(vec2 p){
11
+ ivec2 i = ivec2(floor(p));
12
+ return float(jgHashUint(uvec2(i))) * (1.0 / 4294967296.0);
13
+ }
4
14
  float jgVNoise(vec2 p){
5
15
  vec2 i = floor(p); vec2 f = fract(p);
6
16
  vec2 u = f * f * (3.0 - 2.0 * f);
@@ -22,8 +32,17 @@ float jgFbm(vec2 p){
22
32
  * sand (by waterline), and snow (by height) over it — textured-reading terrain
23
33
  * with no image assets. Full PBR: lit, shadowed, and fogged like any standard
24
34
  * material, so it composes with the post-processing chain.
25
- */
26
- export function createTerrainDetailMaterial(detail) {
35
+ *
36
+ * When `detail.material` is set, pass the matching loaded `textures` (color/normal/roughness/ao):
37
+ * they tile by world position at `detail.material.repeat` world units per tile and blend over the
38
+ * procedural result at `detail.material.strength` — the real-texture seam layers onto the existing
39
+ * look instead of replacing it.
40
+ * @internal
41
+ */
42
+ export function createTerrainDetailMaterial(detail, textures) {
43
+ const hasTexture = textures !== undefined && detail.material !== undefined;
44
+ const repeat = detail.material?.repeat ?? 4;
45
+ const strength = detail.material?.strength ?? 1;
27
46
  const uniforms = {
28
47
  uRockColor: { value: new THREE.Color(detail.rockColor) },
29
48
  uSandColor: { value: new THREE.Color(detail.sandColor) },
@@ -34,6 +53,16 @@ export function createTerrainDetailMaterial(detail) {
34
53
  uDetailScale: { value: Math.max(0.5, detail.detailScale) },
35
54
  uMacroScale: { value: Math.max(0.5, detail.macroScale) },
36
55
  uStrength: { value: detail.strength },
56
+ ...(hasTexture
57
+ ? {
58
+ uMatColor: { value: textures.color },
59
+ uMatNormal: { value: textures.normal },
60
+ uMatRoughness: { value: textures.roughness },
61
+ uMatAo: { value: textures.ao },
62
+ uMatRepeat: { value: Math.max(0.01, repeat) },
63
+ uMatStrength: { value: strength },
64
+ }
65
+ : {}),
37
66
  };
38
67
  const material = new THREE.MeshStandardMaterial({
39
68
  color: "#ffffff",
@@ -51,6 +80,36 @@ varying vec3 vJgWorldNormal;`)
51
80
  vJgWorldNormal = normalize(mat3(modelMatrix) * objectNormal);`)
52
81
  .replace("#include <begin_vertex>", `#include <begin_vertex>
53
82
  vJgWorldPos = (modelMatrix * vec4(transformed, 1.0)).xyz;`);
83
+ const materialUniformsGlsl = hasTexture
84
+ ? `
85
+ uniform sampler2D uMatColor;
86
+ uniform sampler2D uMatNormal;
87
+ uniform sampler2D uMatRoughness;
88
+ uniform sampler2D uMatAo;
89
+ uniform float uMatRepeat;
90
+ uniform float uMatStrength;`
91
+ : "";
92
+ const materialColorGlsl = hasTexture
93
+ ? `
94
+ vec2 jgMatUv = jgWp / uMatRepeat;
95
+ vec3 jgMatColor = texture2D(uMatColor, jgMatUv).rgb;
96
+ jgCol = mix(jgCol, jgMatColor, uMatStrength);`
97
+ : "";
98
+ const materialRoughnessGlsl = hasTexture
99
+ ? `
100
+ float jgMatRough = texture2D(uMatRoughness, jgMatUv).g;
101
+ roughnessFactor = mix(roughnessFactor, jgMatRough, uMatStrength);`
102
+ : "";
103
+ const materialNormalGlsl = hasTexture
104
+ ? `
105
+ vec2 jgMatNormalXy = texture2D(uMatNormal, jgMatUv).rg * 2.0 - 1.0;
106
+ normal = normalize(normal + vec3(jgMatNormalXy.x, 0.0, jgMatNormalXy.y) * uMatStrength * 0.6);`
107
+ : "";
108
+ const materialAoGlsl = hasTexture
109
+ ? `
110
+ float jgMatAo = texture2D(uMatAo, jgMatUv).r;
111
+ reflectedLight.indirectDiffuse *= mix(1.0, jgMatAo, uMatStrength);`
112
+ : "";
54
113
  shader.fragmentShader = shader.fragmentShader
55
114
  .replace("#include <common>", `#include <common>
56
115
  varying vec3 vJgWorldPos;
@@ -64,6 +123,7 @@ uniform float uWaterLevel;
64
123
  uniform float uDetailScale;
65
124
  uniform float uMacroScale;
66
125
  uniform float uStrength;
126
+ ${materialUniformsGlsl}
67
127
  ${NOISE_GLSL}`)
68
128
  .replace("#include <color_fragment>", `#include <color_fragment>
69
129
  vec2 jgWp = vJgWorldPos.xz;
@@ -83,8 +143,15 @@ jgCol = mix(jgCol, jgSand, jgSandW);
83
143
  vec3 jgSnow = uSnowColor * mix(0.94, 1.04, jgFine);
84
144
  float jgSnowW = smoothstep(uSnowHeight, uSnowHeight + 8.0, jgH) * (1.0 - jgRockW * 0.6) * uStrength;
85
145
  jgCol = mix(jgCol, jgSnow, jgSnowW);
86
- diffuseColor.rgb = jgCol;`);
146
+ ${materialColorGlsl}
147
+ diffuseColor.rgb = jgCol;`)
148
+ .replace("#include <roughnessmap_fragment>", `#include <roughnessmap_fragment>
149
+ ${materialRoughnessGlsl}`)
150
+ .replace("#include <normal_fragment_maps>", `#include <normal_fragment_maps>
151
+ ${materialNormalGlsl}`)
152
+ .replace("#include <aomap_fragment>", `#include <aomap_fragment>
153
+ ${materialAoGlsl}`);
87
154
  };
88
- material.customProgramCacheKey = () => `jgengine-terrain-detail-${detail.rockSlopeStart}-${detail.snowHeight}`;
155
+ material.customProgramCacheKey = () => `jgengine-terrain-detail-${detail.rockSlopeStart}-${detail.snowHeight}-${hasTexture ? "tex" : "flat"}`;
89
156
  return { material };
90
157
  }
@@ -27,10 +27,15 @@ export interface TerrainVertexColorOptions {
27
27
  waterline?: THREE.ColorRepresentation;
28
28
  waterlineHeight?: number;
29
29
  }
30
+ /** @internal */
30
31
  export declare function normalizeHeightBlend(height: number, minHeight: number, maxHeight: number): number;
32
+ /** @internal */
31
33
  export declare function resolveTerrainSize(size?: TerrainArea): ResolvedTerrainSize;
34
+ /** @internal */
32
35
  export declare function resolveTerrainSegments(segments?: ProceduralTerrainConfig["segments"]): ResolvedTerrainSegments;
36
+ /** @internal */
33
37
  export declare function toNoiseFieldConfig(config?: ProceduralTerrainConfig): NoiseFieldConfig;
38
+ /** @internal */
34
39
  export declare function createProceduralTerrainSampler(config?: ProceduralTerrainConfig): TerrainHeightSampler;
35
40
  /** Per-position palette override for multi-biome ground coloring — `createTerrainPaletteSampler` from `@jgengine/core/world/terrain` returns exactly this shape. */
36
41
  export type TerrainPaletteSampler = (x: number, z: number) => {
@@ -51,6 +56,8 @@ export interface FieldGroundOptions {
51
56
  * Mesh any `TerrainField` — including a `CarvableField` with craters/mounds written into it — into a
52
57
  * vertex-coloured ground geometry. `sampleHeight` drives the vertices, so runtime carves show up as
53
58
  * real depressions the moment the field is re-sampled (bump the caller's rebuild key after a carve).
54
- */
59
+ * @internal
60
+ */
55
61
  export declare function createFieldGroundGeometry(field: TerrainField, options?: FieldGroundOptions): THREE.BufferGeometry;
62
+ /** @internal */
56
63
  export declare function createProceduralGroundGeometry(config?: ProceduralTerrainConfig, colors?: TerrainVertexColorOptions): THREE.BufferGeometry;
@@ -1,21 +1,25 @@
1
1
  import { noiseField } from "@jgengine/core/world/terrain";
2
2
  import * as THREE from "three";
3
3
  import {} from "./random.js";
4
+ /** @internal */
4
5
  export function normalizeHeightBlend(height, minHeight, maxHeight) {
5
6
  const range = maxHeight - minHeight;
6
7
  if (range <= 1e-6)
7
8
  return 0.5;
8
9
  return THREE.MathUtils.clamp((height - minHeight) / range, 0, 1);
9
10
  }
11
+ /** @internal */
10
12
  export function resolveTerrainSize(size = 40) {
11
13
  return typeof size === "number" ? { width: size, depth: size } : { width: size[0], depth: size[1] };
12
14
  }
15
+ /** @internal */
13
16
  export function resolveTerrainSegments(segments = 96) {
14
17
  if (typeof segments !== "number")
15
18
  return { x: Math.max(1, Math.floor(segments[0])), z: Math.max(1, Math.floor(segments[1])) };
16
19
  const resolved = Math.max(1, Math.floor(segments));
17
20
  return { x: resolved, z: resolved };
18
21
  }
22
+ /** @internal */
19
23
  export function toNoiseFieldConfig(config = {}) {
20
24
  return {
21
25
  seed: config.seed,
@@ -26,6 +30,7 @@ export function toNoiseFieldConfig(config = {}) {
26
30
  baseHeight: config.baseOffset ?? 0,
27
31
  };
28
32
  }
33
+ /** @internal */
29
34
  export function createProceduralTerrainSampler(config = {}) {
30
35
  return noiseField(toNoiseFieldConfig(config)).sampleHeight;
31
36
  }
@@ -33,7 +38,8 @@ export function createProceduralTerrainSampler(config = {}) {
33
38
  * Mesh any `TerrainField` — including a `CarvableField` with craters/mounds written into it — into a
34
39
  * vertex-coloured ground geometry. `sampleHeight` drives the vertices, so runtime carves show up as
35
40
  * real depressions the moment the field is re-sampled (bump the caller's rebuild key after a carve).
36
- */
41
+ * @internal
42
+ */
37
43
  export function createFieldGroundGeometry(field, options = {}) {
38
44
  const size = resolveTerrainSize(options.size);
39
45
  const segments = resolveTerrainSegments(options.segments);
@@ -118,6 +124,7 @@ function buildGroundGeometry(size, segments, sampler, opts) {
118
124
  geometry.computeBoundingSphere();
119
125
  return geometry;
120
126
  }
127
+ /** @internal */
121
128
  export function createProceduralGroundGeometry(config = {}, colors = {}) {
122
129
  const size = resolveTerrainSize(config.size);
123
130
  const segments = resolveTerrainSegments(config.segments);
@@ -1,5 +1,5 @@
1
1
  import { type MutableRefObject } from "react";
2
- import { type TouchScheme } from "@jgengine/core/input/touchScheme";
2
+ import { type TouchScheme, type TouchStyle } from "@jgengine/core/input/touchScheme";
3
3
  export interface TouchCodeSink {
4
4
  onCodeDown(code: string): void;
5
5
  onCodeUp(code: string): void;
@@ -14,9 +14,11 @@ export declare function primaryButtonOffsets(count: number, scale?: number): {
14
14
  bottom: number;
15
15
  }[] | null;
16
16
  /**
17
- * Vertical space (px, excluding device safe areas) the dock occupies above
18
- * the bottom edge. The shell publishes it as `--jg-hud-dock-clearance` so
19
- * `HudCanvas` regions never collide with touch controls.
17
+ * Vertical space (px, excluding device safe areas) that *bottom-docked* clusters
18
+ * occupy above the bottom edge. The shell publishes it as
19
+ * `--jg-hud-dock-clearance` so `HudCanvas` regions never collide with touch
20
+ * controls. Side rails and top clusters reserve their own rectangles through the
21
+ * layout registry instead of this scalar.
20
22
  */
21
23
  export declare function touchDockClearance(scheme: TouchScheme | null, scale?: number): number;
22
24
  export declare function TouchPlaySurface({ scheme, sink, yawRef, pitchRef, maxPitch, onPrimaryTap, }: {
@@ -27,8 +29,10 @@ export declare function TouchPlaySurface({ scheme, sink, yawRef, pitchRef, maxPi
27
29
  maxPitch: number;
28
30
  onPrimaryTap: () => void;
29
31
  }): import("react").JSX.Element;
30
- export declare function TouchControlsDock({ scheme, sink, scale }: {
32
+ export declare function TouchControlsDock({ scheme, sink, style, scale, }: {
31
33
  scheme: TouchScheme;
32
34
  sink: TouchCodeSink;
35
+ /** Player-selected skin; falls back to the scheme's game default. */
36
+ style?: TouchStyle;
33
37
  scale?: number;
34
38
  }): import("react").JSX.Element;