@jgengine/editor 0.21.0 → 0.22.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/CHANGELOG.md +18 -0
- package/dist/TerrainSculpt.js +9 -58
- package/dist/uiStore.d.ts +1 -10
- package/dist/uiStore.js +1 -13
- package/package.json +5 -5
- package/skills/jgengine/SKILL.md +1 -0
- package/skills/jgengine/api.md +2 -2
- package/skills/jgengine-editor/api.md +9 -0
package/CHANGELOG.md
CHANGED
|
@@ -26,6 +26,24 @@ At publish, rename this heading to the new version and mirror the entries into
|
|
|
26
26
|
`packages/core/src/meta/changelog.ts` (the typed `CHANGELOG` export).
|
|
27
27
|
-->
|
|
28
28
|
|
|
29
|
+
## 0.22.0
|
|
30
|
+
|
|
31
|
+
### Migrate
|
|
32
|
+
|
|
33
|
+
- **Bump lockstep SDK packages to `^0.22.0`:** `@jgengine/{core,rapier,react,ws,node,sql,convex,shell,editor,assets,navbake}`. CLI `jgengine` is `0.19.0`; `@jgengine/github` is `0.9.0`.
|
|
34
|
+
|
|
35
|
+
### Added
|
|
36
|
+
|
|
37
|
+
- Editor-authored grass exposes blade width and bend ranges, tuft radius and normal lift, using the same bounded grass renderer as environment fields. Existing scene documents keep their previous shape and lighting defaults.
|
|
38
|
+
- The full `jgengine skills` install includes `game-improvement`, with an ordered implementation workflow and checks for meaningful decisions, authored terrain and grass, and proven play quality.
|
|
39
|
+
|
|
40
|
+
### Fixed
|
|
41
|
+
|
|
42
|
+
- Grass volumes sample terrain at their authored world position and subtract their placement height once, keeping translated patches rooted on slopes instead of buried underground.
|
|
43
|
+
|
|
44
|
+
- Authored grass respects spawn, objective and road/path clearances with feathered regrowth outside the usable footing. Diagonal paths keep their own corridor clear without clearing the entire surrounding rectangle.
|
|
45
|
+
- Runtime environment ground now renders editor-authored terrain paint surfaces, layer tints, opacity and weighted blends through the same sampler as the editor. Legacy unpainted terrain keeps descriptor shading. Layer texture tiling, roughness and triplanar controls remain serialized authoring metadata; this fix does not add layered PBR texture rendering.
|
|
46
|
+
|
|
29
47
|
## 0.21.0
|
|
30
48
|
|
|
31
49
|
### Migrate
|
package/dist/TerrainSculpt.js
CHANGED
|
@@ -5,18 +5,14 @@ import * as THREE from "three";
|
|
|
5
5
|
import { createTerrainPaletteSampler, raycastHeightField, } from "@jgengine/core/world/terrain";
|
|
6
6
|
import { beginSurfaceStroke, beginTerraformStroke, editableTerrainFromSnapshot, } from "@jgengine/core/world/terraform";
|
|
7
7
|
import { useGameContext } from "@jgengine/react/provider";
|
|
8
|
-
import { displaceHeightfieldGeometry, normalizeHeightBlend, TerraformBrushCursor, } from "@jgengine/shell/terrain";
|
|
8
|
+
import { displaceHeightfieldGeometry, normalizeHeightBlend, createTerrainPaintColorSampler, TerraformBrushCursor, } from "@jgengine/shell/terrain";
|
|
9
9
|
import { editorPerfMarks } from "./perfMarks.js";
|
|
10
|
-
import {
|
|
10
|
+
import {} from "./uiStore.js";
|
|
11
11
|
import { useStoreSelector } from "./useStoreSelector.js";
|
|
12
12
|
const GROUND_PLANE = new THREE.Plane(new THREE.Vector3(0, 1, 0), 0);
|
|
13
13
|
const LOW = new THREE.Color("#3f5d34");
|
|
14
14
|
const HIGH = new THREE.Color("#8f9c66");
|
|
15
15
|
const COLOR_SPAN = 24;
|
|
16
|
-
/** Resolves a material layer to its render color (its tint, else its palette surface color). */
|
|
17
|
-
function layerColor(layer) {
|
|
18
|
-
return layer.tint ?? TERRAIN_MATERIAL_COLORS[layer.surface] ?? "#7c7f86";
|
|
19
|
-
}
|
|
20
16
|
/** The game's `environment({ terrain })` descriptor, when the world it declares carries one — the same descriptor `TerrainGround` samples at runtime via `createTerrainPaletteSampler`. */
|
|
21
17
|
function terrainDescriptorOf(world) {
|
|
22
18
|
return world?.kind === "environment" ? world.terrain : undefined;
|
|
@@ -65,28 +61,11 @@ function editFromSettings(settings, center, clickHeight) {
|
|
|
65
61
|
...(heightLimit === undefined ? {} : { heightLimit }),
|
|
66
62
|
};
|
|
67
63
|
}
|
|
68
|
-
const BLEND_A = new THREE.Color();
|
|
69
|
-
const BLEND_B = new THREE.Color();
|
|
70
|
-
/** Parsed once — `toneAt` runs per vertex, so hex→Color parsing must not. */
|
|
71
|
-
const SURFACE_COLOR_CACHE = new Map();
|
|
72
|
-
function surfaceColor(hex) {
|
|
73
|
-
let color = SURFACE_COLOR_CACHE.get(hex);
|
|
74
|
-
if (color === undefined) {
|
|
75
|
-
color = new THREE.Color(hex);
|
|
76
|
-
SURFACE_COLOR_CACHE.set(hex, color);
|
|
77
|
-
}
|
|
78
|
-
return color;
|
|
79
|
-
}
|
|
80
64
|
/** World-unit spacing between topographic relief bands drawn on the sculpt preview. */
|
|
81
65
|
const CONTOUR_INTERVAL = 1.5;
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
* darken slopes toward each band trough (a contour-map look) and higher ground brightens slightly —
|
|
86
|
-
* a `multiply`, so painted grass/dirt/rock keep their hue. Flat ground at a band height (the common
|
|
87
|
-
* unsculpted case at y=0) is left untouched: only varying height shows the bands. `strength` 0 is a
|
|
88
|
-
* no-op.
|
|
89
|
-
*/
|
|
66
|
+
const BLEND_A = new THREE.Color();
|
|
67
|
+
const BLEND_B = new THREE.Color();
|
|
68
|
+
/** Multiplicative contour bands preserve painted hue while making elevation readable. */
|
|
90
69
|
function applyRelief(out, height, strength) {
|
|
91
70
|
if (strength <= 0)
|
|
92
71
|
return;
|
|
@@ -102,37 +81,9 @@ function applyRelief(out, height, strength) {
|
|
|
102
81
|
* default — so `materialRegions`/base `colors`/`material` (worn paths, rock outcrops, biome tint)
|
|
103
82
|
* show through in the editor the same way `TerrainGround` renders them at runtime.
|
|
104
83
|
*/
|
|
105
|
-
function toneAt(
|
|
106
|
-
|
|
107
|
-
if (layers.length > 0) {
|
|
108
|
-
const weights = terrain.weightsAt(x, z);
|
|
109
|
-
if (weights.length === layers.length) {
|
|
110
|
-
let total = 0;
|
|
111
|
-
let r = 0;
|
|
112
|
-
let g = 0;
|
|
113
|
-
let b = 0;
|
|
114
|
-
for (let l = 0; l < layers.length; l += 1) {
|
|
115
|
-
const w = weights[l] * (layers[l].opacity ?? 1);
|
|
116
|
-
if (w <= 0)
|
|
117
|
-
continue;
|
|
118
|
-
const color = layerColors[l];
|
|
119
|
-
r += color.r * w;
|
|
120
|
-
g += color.g * w;
|
|
121
|
-
b += color.b * w;
|
|
122
|
-
total += w;
|
|
123
|
-
}
|
|
124
|
-
if (total > 0) {
|
|
125
|
-
out.setRGB(r / total, g / total, b / total);
|
|
126
|
-
return;
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
const surface = terrain.surfaceAt(x, z);
|
|
131
|
-
const painted = surface === null ? undefined : TERRAIN_MATERIAL_COLORS[surface];
|
|
132
|
-
if (painted !== undefined) {
|
|
133
|
-
out.copy(surfaceColor(painted));
|
|
84
|
+
function toneAt(x, z, height, out, paletteAt, heightRange, paintColorAt) {
|
|
85
|
+
if (paintColorAt(x, z, out))
|
|
134
86
|
return;
|
|
135
|
-
}
|
|
136
87
|
if (paletteAt !== null) {
|
|
137
88
|
const palette = paletteAt(x, z);
|
|
138
89
|
const blend = normalizeHeightBlend(height, heightRange[0], heightRange[1]);
|
|
@@ -151,12 +102,12 @@ function toneAt(terrain, x, z, height, out, paletteAt, heightRange, layerColors)
|
|
|
151
102
|
*/
|
|
152
103
|
function displace(geometry, terrain, bounds, region, paletteAt, heightRange, contourStrength) {
|
|
153
104
|
const started = performance.now();
|
|
154
|
-
const
|
|
105
|
+
const paintColorAt = createTerrainPaintColorSampler(terrain);
|
|
155
106
|
displaceHeightfieldGeometry(geometry, terrain.sampleHeight, {
|
|
156
107
|
bounds,
|
|
157
108
|
region,
|
|
158
109
|
color: (x, z, height, out) => {
|
|
159
|
-
toneAt(
|
|
110
|
+
toneAt(x, z, height, out, paletteAt, heightRange, paintColorAt);
|
|
160
111
|
applyRelief(out, height, contourStrength);
|
|
161
112
|
},
|
|
162
113
|
});
|
package/dist/uiStore.d.ts
CHANGED
|
@@ -7,16 +7,7 @@ export type EditorTool = "select" | "terrain";
|
|
|
7
7
|
export type TerrainBrushKind = "raise" | "lower" | "smooth" | "flatten" | "noise" | "ramp";
|
|
8
8
|
/** The terrain tool's active sub-mode: reshape the heightfield, or paint material layers onto it. */
|
|
9
9
|
export type TerrainMode = "sculpt" | "paint";
|
|
10
|
-
|
|
11
|
-
export interface TerrainMaterial {
|
|
12
|
-
id: string;
|
|
13
|
-
label: string;
|
|
14
|
-
color: string;
|
|
15
|
-
}
|
|
16
|
-
/** The default terrain paint palette (surface id → color) shared by the panel and the mesh. */
|
|
17
|
-
export declare const TERRAIN_MATERIALS: readonly TerrainMaterial[];
|
|
18
|
-
/** Maps every default material id to its render color, for the sculpt mesh's per-cell surface tint. */
|
|
19
|
-
export declare const TERRAIN_MATERIAL_COLORS: Record<string, string>;
|
|
10
|
+
export { TERRAIN_MATERIALS, TERRAIN_MATERIAL_COLORS, type TerrainMaterial } from "@jgengine/core/editor/terrainMaterials";
|
|
20
11
|
/** Live terrain material-paint controls driven by the terrain tool panel. */
|
|
21
12
|
export interface PaintSettings {
|
|
22
13
|
material: string;
|
package/dist/uiStore.js
CHANGED
|
@@ -1,16 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
export const TERRAIN_MATERIALS = [
|
|
3
|
-
{ id: "grass", label: "Grass", color: "#4b7f3f" },
|
|
4
|
-
{ id: "dirt", label: "Dirt", color: "#6b4f34" },
|
|
5
|
-
{ id: "rock", label: "Rock", color: "#7c7f86" },
|
|
6
|
-
{ id: "sand", label: "Sand", color: "#c2b283" },
|
|
7
|
-
{ id: "mud", label: "Mud", color: "#574433" },
|
|
8
|
-
{ id: "snow", label: "Snow", color: "#eef3f7" },
|
|
9
|
-
{ id: "road", label: "Road", color: "#3a3a3d" },
|
|
10
|
-
{ id: "gravel", label: "Gravel", color: "#8d8d84" },
|
|
11
|
-
];
|
|
12
|
-
/** Maps every default material id to its render color, for the sculpt mesh's per-cell surface tint. */
|
|
13
|
-
export const TERRAIN_MATERIAL_COLORS = Object.fromEntries(TERRAIN_MATERIALS.map((material) => [material.id, material.color]));
|
|
1
|
+
export { TERRAIN_MATERIALS, TERRAIN_MATERIAL_COLORS } from "@jgengine/core/editor/terrainMaterials";
|
|
14
2
|
/** The terrain tool's default paint controls. */
|
|
15
3
|
export const DEFAULT_PAINT_SETTINGS = {
|
|
16
4
|
material: "grass",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jgengine/editor",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.22.0",
|
|
4
4
|
"description": "Scene/world/asset editor for JGengine — loaded lazily by the runner; never import from game entrypoints.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -31,10 +31,10 @@
|
|
|
31
31
|
"test": "bun test src"
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@jgengine/core": "^0.
|
|
35
|
-
"@jgengine/navbake": "^0.
|
|
36
|
-
"@jgengine/react": "^0.
|
|
37
|
-
"@jgengine/shell": "^0.
|
|
34
|
+
"@jgengine/core": "^0.22.0",
|
|
35
|
+
"@jgengine/navbake": "^0.22.0",
|
|
36
|
+
"@jgengine/react": "^0.22.0",
|
|
37
|
+
"@jgengine/shell": "^0.22.0"
|
|
38
38
|
},
|
|
39
39
|
"peerDependencies": {
|
|
40
40
|
"@react-three/drei": "^10.0.0",
|
package/skills/jgengine/SKILL.md
CHANGED
|
@@ -53,6 +53,7 @@ Load only rows the target needs. Do not preload every domain.
|
|
|
53
53
|
| Need | Skill | Owner |
|
|
54
54
|
| --- | --- | --- |
|
|
55
55
|
| player promise, pillars, verbs, loops, economy, progression, balance | `game-design` | experience and rule intent |
|
|
56
|
+
| substantial existing-game improvement across depth, world and presentation | `game-improvement` | ordered improvement plan and per-game completion |
|
|
56
57
|
| metrics, topology, encounters, navigation, spatial pacing | `level-design` | playable space intent |
|
|
57
58
|
| scene placement, terrain painting, paths, asset authoring | `jgengine-editor` | editor document and authoring tools |
|
|
58
59
|
| runtime world, movement, input, interaction, AI, navigation, audio | `jgengine-world` | spatial simulation and world runtime |
|
package/skills/jgengine/api.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
- `CHANGELOG` (const): const CHANGELOG: Record<string, ChangelogEntry> — Per-version engine changelog keyed by semver string (e.g. `"0.10.0"`).
|
|
8
8
|
- `ChangelogEntry` (interface): interface ChangelogEntry — One release's migrate steps plus added/changed/removed notes (typed mirror of CHANGELOG.md).
|
|
9
|
-
- `VERSION` (const): const VERSION: "0.
|
|
9
|
+
- `VERSION` (const): const VERSION: "0.22.0" — Installed `@jgengine/core` semver — compare against {@link CHANGELOG} keys when migrating.
|
|
10
10
|
|
|
11
11
|
## @jgengine/core/authoring
|
|
12
12
|
|
|
@@ -232,7 +232,7 @@
|
|
|
232
232
|
|
|
233
233
|
- `CHANGELOG` (const): const CHANGELOG: Record<string, ChangelogEntry> — Per-version engine changelog keyed by semver string (e.g. `"0.10.0"`).
|
|
234
234
|
- `ChangelogEntry` (interface): interface ChangelogEntry — One release's migrate steps plus added/changed/removed notes (typed mirror of CHANGELOG.md).
|
|
235
|
-
- `VERSION` (const): const VERSION: "0.
|
|
235
|
+
- `VERSION` (const): const VERSION: "0.22.0" — Installed `@jgengine/core` semver — compare against {@link CHANGELOG} keys when migrating.
|
|
236
236
|
|
|
237
237
|
## @jgengine/core/runtime/adapter
|
|
238
238
|
|
|
@@ -78,6 +78,9 @@
|
|
|
78
78
|
- `SplitWorldResult` (interface): interface SplitWorldResult — The result of splitting a document: a manifest plus the shard documents keyed by their file path.
|
|
79
79
|
- `StreamUpdate` (interface): interface StreamUpdate — The residency change produced by one {@link WorldStreamer.update}.
|
|
80
80
|
- `StreamerShard` (interface): interface StreamerShard — The proximity streamer for a sharded world (#985 stage 3): a stateful scheduler that decides, as the camera moves, which shards enter and leave residency. It is pure scheduling — actually reading/merging the shard documents stays the caller's job (compose with `@jgengine/core/editor/world`'s `loadWorldDocument` on each returned delta) — so the streamer has no I/O and stays deterministic and testable. Hysteresis (`loadRadius` < `keepRadius`) stops a shard on a boundary from thrashing load/unload every frame.
|
|
81
|
+
- `TERRAIN_MATERIALS` (const): const TERRAIN_MATERIALS: readonly TerrainMaterial[] — The default terrain paint palette (surface id → color) shared by the panel and the mesh.
|
|
82
|
+
- `TERRAIN_MATERIAL_COLORS` (const): const TERRAIN_MATERIAL_COLORS: Record<string, string> — Maps every default material id to its render color, for the sculpt mesh's per-cell surface tint.
|
|
83
|
+
- `TerrainMaterial` (interface): interface TerrainMaterial — A paintable terrain material layer — a surface id plus the color it renders as.
|
|
81
84
|
- `WELL_KNOWN_MARKER_KINDS` (const): const WELL_KNOWN_MARKER_KINDS: readonly ["player_spawn", "mob", "boss", "vendor", "chest", "travel", "npc", "poi", "prop", "goal", "branch"] — Standard marker kinds recognized with default colors and behavior.
|
|
82
85
|
- `WELL_KNOWN_PATH_KINDS` (const): const WELL_KNOWN_PATH_KINDS: readonly ["road", "corridor", "branch", "route"] — Standard path kinds recognized with default colors and behavior.
|
|
83
86
|
- `WELL_KNOWN_VOLUME_KINDS` (const): const WELL_KNOWN_VOLUME_KINDS: readonly ["zone", "flatten", "cluster", "aggro", "leash", "discover", "capture", "prompt", "poi", "respawn_skip"] — Standard volume kinds recognized with default colors and behavior.
|
|
@@ -346,6 +349,12 @@
|
|
|
346
349
|
- `createWorldStreamer` (function): function createWorldStreamer(config: WorldStreamerConfig): WorldStreamer — Creates a proximity streamer over a world's shards. Always-resident shards are resident from the first update onward; each bounded shard loads when the camera comes within `loadRadius` of its footprint and unloads only once the camera passes `keepRadius` (≥ `loadRadius`, clamped up), giving a hysteresis band that prevents boundary thrash. Purely spatial and deterministic; the caller wires each delta to `loadWorldDocument`.
|
|
347
350
|
- `shardDistance` (function): function shardDistance(shard: StreamerShard, center: { x: number; z: number }): number — Distance from a point to a shard's XZ footprint (0 when inside), or 0 for an always-resident shard.
|
|
348
351
|
|
|
352
|
+
## @jgengine/core/editor/terrainMaterials
|
|
353
|
+
|
|
354
|
+
- `TERRAIN_MATERIALS` (const): const TERRAIN_MATERIALS: readonly TerrainMaterial[] — The default terrain paint palette (surface id → color) shared by the panel and the mesh.
|
|
355
|
+
- `TERRAIN_MATERIAL_COLORS` (const): const TERRAIN_MATERIAL_COLORS: Record<string, string> — Maps every default material id to its render color, for the sculpt mesh's per-cell surface tint.
|
|
356
|
+
- `TerrainMaterial` (interface): interface TerrainMaterial — A paintable terrain material layer — a surface id plus the color it renders as.
|
|
357
|
+
|
|
349
358
|
## @jgengine/core/editor/types
|
|
350
359
|
|
|
351
360
|
- `EditorBake` (interface): interface EditorBake — A named baked artifact produced by an editor or build tool.
|