@jgengine/core 0.5.0 → 0.6.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 +24 -0
- package/dist/game/defineGame.d.ts +3 -0
- package/dist/game/playableGame.d.ts +10 -1
- package/dist/meta/changelog.d.ts +1 -1
- package/dist/meta/changelog.js +19 -1
- package/dist/physics/physicsWorld.d.ts +136 -0
- package/dist/physics/physicsWorld.js +489 -0
- package/dist/runtime/gameContext.d.ts +2 -0
- package/dist/runtime/gameContext.js +3 -0
- package/dist/scene/assetCatalog.d.ts +13 -0
- package/dist/scene/entityStore.d.ts +6 -0
- package/dist/scene/entityStore.js +15 -1
- package/dist/time/simClock.d.ts +62 -0
- package/dist/time/simClock.js +132 -0
- package/dist/world/buildingIndex.d.ts +16 -0
- package/dist/world/buildingIndex.js +63 -0
- package/dist/world/buildings.d.ts +98 -0
- package/dist/world/buildings.js +320 -0
- package/dist/world/features.d.ts +105 -1
- package/dist/world/features.js +95 -0
- package/dist/world/regions.d.ts +66 -0
- package/dist/world/regions.js +158 -0
- package/dist/world/scatter.d.ts +24 -0
- package/dist/world/scatter.js +126 -0
- package/dist/world/scatterItems.d.ts +38 -0
- package/dist/world/scatterItems.js +85 -0
- package/dist/world/terrain.d.ts +54 -0
- package/dist/world/terrain.js +181 -0
- package/dist/world/water.d.ts +27 -0
- package/dist/world/water.js +125 -0
- package/dist/world/wind.d.ts +17 -0
- package/dist/world/wind.js +40 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -15,6 +15,30 @@ the latest and surface the migration steps.
|
|
|
15
15
|
|
|
16
16
|
_Nothing yet._
|
|
17
17
|
|
|
18
|
+
## 0.6.0
|
|
19
|
+
|
|
20
|
+
An additive release: **every 0.5.0 API is unchanged**, so upgrading is only a
|
|
21
|
+
version bump. The headline is a whole outdoor-world layer — composable
|
|
22
|
+
`environment(...)` descriptors, renderer-free query primitives so gameplay reads
|
|
23
|
+
the same world the shell renders, a standalone rigid-body physics sim, and the
|
|
24
|
+
`@jgengine/shell` renderers that draw it all.
|
|
25
|
+
|
|
26
|
+
### Migrate
|
|
27
|
+
|
|
28
|
+
- Bump every `@jgengine/*` dependency to `^0.6.0` (the eight packages version in lockstep).
|
|
29
|
+
- No code change is required — 0.6.0 adds surface, it doesn't move or remove any.
|
|
30
|
+
- Optional: describe an outdoor scene once with `environment({ terrain, weather, vegetation, water, structures })` from `@jgengine/core/world/features`. `@jgengine/shell`'s `EnvironmentScene` renders it (terrain, rain/snow, grass, ocean, buildings), and gameplay reads the same world through the renderer-free primitives — `resolveTerrainField(...)` for ground-snap/collision, `windField(...)` for sway/sailing, `waterSurface(...)` for buoyancy, `scatter(...)` for prop/spawn placement, `buildingIndex(...)` for placement avoidance — no three.js needed.
|
|
31
|
+
|
|
32
|
+
### Added
|
|
33
|
+
|
|
34
|
+
- `@jgengine/core/world` query primitives — pure, renderer-free world sampling so gameplay and rendering read one source of truth. `world/terrain` (`TerrainField`, `noiseField`, `resolveTerrainField`, `resolveGroundStep` for slope-limited movement), `world/wind` (`windField` — one wind source for weather sway, grass, sailing, fire spread), `world/water` (`waterSurface` / `waterSurfaceFromDescriptor` — CPU Gerstner matching the ocean shader, for buoyancy and shorelines), `world/scatter` (`scatter` — seeded, overlap-aware point distribution for vegetation/props/spawns), `world/buildings` (`generateBuilding`, `generateBuildingDistrict`) and `world/buildingIndex` (`buildingIndex` — `at`/`within`/`nearest`/`isInside`/`blockers` over a district).
|
|
35
|
+
- `@jgengine/core/world/features` composable outdoor descriptors — `environment(...)` plus `terrain()`, `rain()`, `snow()`, `grass()`, `ocean()`, `building()`.
|
|
36
|
+
- `@jgengine/core/world/regions` (`createRegionField` — blend content-agnostic biomes by nearest selector, adding `tint`/`water`/`fog`/`speedMultiplier`/opaque `data`; extends `TerrainField` so it ground-snaps too) and `@jgengine/core/world/scatterItems` (`scatterItems` — region-driven content scatter: density per region, grounded, above-water/slope-aware, with `pickWeighted` for weighted rolls).
|
|
37
|
+
- `@jgengine/core/physics/physicsWorld` — standalone fixed-capacity rigid-body sim (SoA buffers, spatial-hash broadphase, sleeping) for many colliding dynamic bodies (piles, debris, stress scenes). Distinct from the `defineGame` `physics: { gravity }` character-controller config.
|
|
38
|
+
- `ctx.time` simulation clock (`@jgengine/core/time/simClock`) — `onTick`'s `dt` is now **game time**, so `rate * dt` decay/regen/AI obeys pause and fast-forward for free without per-system wiring. Configure with `defineGame({ time: { scale?, speeds?, dayLength?, start?, startPaused? } })` (all optional; default is real-time 1:1). `ctx.time` exposes `pause`/`play`/`toggle`/`setSpeed`/`cycleSpeed` + `snapshot()`/`calendar()`; `useGameClock()` binds it in React.
|
|
39
|
+
- `@jgengine/shell` environment/terrain/water/weather/structures renderers — `EnvironmentScene` mounts an `environment()` descriptor as R3F renderers; `GrassField`, `ProceduralGround`, `Ocean`, `RainField`, `SnowField`, `LightningStrike`, `GeneratedBuilding`, and `world/InstancedBodies` (renders `PhysicsWorld` bodies).
|
|
40
|
+
- `@jgengine/react/liveBind` — `useFrameBind` drives a DOM/SVG element from a per-frame value without re-rendering React (HUDs bound to live engine state never re-render or lag).
|
|
41
|
+
|
|
18
42
|
## 0.5.0
|
|
19
43
|
|
|
20
44
|
An additive release: **every 0.4.0 API is unchanged**, so upgrading is only a
|
|
@@ -3,6 +3,7 @@ import type { ItemTraits } from "../inventory/inventoryModel.js";
|
|
|
3
3
|
import type { SaveConfig } from "../runtime/save.js";
|
|
4
4
|
import type { AssetCatalog, ModelAssetRef } from "../scene/assetCatalog.js";
|
|
5
5
|
import { type EntityStore } from "../scene/entityStore.js";
|
|
6
|
+
import type { TimeConfig } from "../time/simClock.js";
|
|
6
7
|
import type { WorldFeature } from "../world/features.js";
|
|
7
8
|
export interface PhysicsConfig {
|
|
8
9
|
gravity?: number;
|
|
@@ -30,6 +31,8 @@ export interface GameDefinition<TAssetRef extends ModelAssetRef = ModelAssetRef,
|
|
|
30
31
|
scene: EntityStore;
|
|
31
32
|
world?: WorldFeature;
|
|
32
33
|
physics?: PhysicsConfig;
|
|
34
|
+
/** Simulation clock: real→game time scale, selectable speeds, calendar. Exposed as `ctx.time`; the shell feeds its scaled dt to `loop.onTick`. */
|
|
35
|
+
time?: TimeConfig;
|
|
33
36
|
inventories?: Record<string, InventoryDeclaration>;
|
|
34
37
|
input?: ActionCodesMap;
|
|
35
38
|
server?: GameServerConfig;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { PositionedPrompt } from "../interaction/proximityPrompt.js";
|
|
2
2
|
import type { GameContext, GameContextContent } from "../runtime/gameContext.js";
|
|
3
|
+
import type { ModelDims } from "../scene/assetCatalog.js";
|
|
3
4
|
import type { GameDefinition, GameLoop } from "./defineGame.js";
|
|
4
5
|
export interface CameraFollowState {
|
|
5
6
|
entityId: string;
|
|
@@ -71,14 +72,22 @@ export interface ModelConfig {
|
|
|
71
72
|
url: string;
|
|
72
73
|
scale?: number;
|
|
73
74
|
y?: number;
|
|
75
|
+
/** How the model registers on its placement point. `"center"` (default) horizontally centers the measured footprint on the point and ground-snaps its lowest vertex to the point's Y — the correct behavior for corner-pivot modular kits. `"origin"` renders at the raw GLB origin (legacy). */
|
|
76
|
+
anchor?: "center" | "origin";
|
|
77
|
+
/** Measured footprint/center/minY; supplied automatically when the model resolves through an `@jgengine/assets` catalog. Required for `anchor: "center"` to take effect. */
|
|
78
|
+
dims?: ModelDims;
|
|
74
79
|
}
|
|
75
|
-
export interface PlayableGame<TUi = unknown, TWorldOverlay = unknown> {
|
|
80
|
+
export interface PlayableGame<TUi = unknown, TWorldOverlay = unknown, TRenderEntity = never> {
|
|
76
81
|
game: GameDefinition;
|
|
77
82
|
content: GameContextContent;
|
|
78
83
|
loop: Required<GameLoop<GameContext>>;
|
|
79
84
|
GameUI: TUi;
|
|
80
85
|
/** Optional canvas-layer VFX component (e.g. traveling projectiles). */
|
|
81
86
|
WorldOverlay?: TWorldOverlay;
|
|
87
|
+
/** Replaces the default demo backdrop (ground + grid + rocks) with the game's own scene — ground, sky, structures. Camera, input, HUD, entity rendering, and the loop stay shell-provided; supply your world without forking the shell. */
|
|
88
|
+
environment?: TWorldOverlay;
|
|
89
|
+
/** Per-entity visual override: return your own mesh for an entity and the shell still positions it and drives selection/targeting. Return null/undefined to fall back to model → sprite → primitive. */
|
|
90
|
+
renderEntity?: TRenderEntity;
|
|
82
91
|
/** Billboard sprites keyed by entity kind name; unmatched entities get primitive markers. */
|
|
83
92
|
entitySprites?: Record<string, EntitySpriteConfig>;
|
|
84
93
|
/** GLB models keyed by entity kind name; a string resolves as an asset-catalog key via game.assets, a ModelConfig renders directly. Takes priority over sprites, then primitives. */
|
package/dist/meta/changelog.d.ts
CHANGED
package/dist/meta/changelog.js
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
|
-
export const VERSION = "0.
|
|
1
|
+
export const VERSION = "0.6.0";
|
|
2
2
|
export const CHANGELOG = {
|
|
3
|
+
"0.6.0": {
|
|
4
|
+
migrate: [
|
|
5
|
+
"Bump every @jgengine/* dependency to ^0.6.0 (the eight packages version in lockstep).",
|
|
6
|
+
"0.6.0 is additive — every 0.5.0 API is unchanged, so no code change is required to upgrade.",
|
|
7
|
+
"Optional: mount an outdoor scene by handing environment({ terrain, weather, vegetation, water, structures }) from @jgengine/core/world/features to the shell — @jgengine/shell EnvironmentScene renders terrain, rain/snow, grass, ocean, and buildings from it, while gameplay reads the same world through the renderer-free query primitives (resolveTerrainField, windField, waterSurface, scatter, buildingIndex).",
|
|
8
|
+
],
|
|
9
|
+
added: [
|
|
10
|
+
"@jgengine/core/world query primitives — renderer-free world sampling so gameplay reads the same world the shell renders: world/terrain (TerrainField, noiseField, resolveTerrainField, resolveGroundStep), world/wind (windField), world/water (waterSurface, waterSurfaceFromDescriptor, CPU Gerstner), world/scatter (scatter — seeded overlap-aware point distribution), world/buildings (generateBuilding, generateBuildingDistrict) and world/buildingIndex (buildingIndex — at/within/nearest/isInside/blockers).",
|
|
11
|
+
"@jgengine/core/world/features gained composable outdoor descriptors: environment(), terrain(), rain(), snow(), grass(), ocean(), building().",
|
|
12
|
+
"@jgengine/core/world/regions (createRegionField — blend content-agnostic biomes by nearest selector, extends TerrainField so it ground-snaps) and world/scatterItems (scatterItems — region-driven, grounded, above-water/slope-aware content scatter with pickWeighted).",
|
|
13
|
+
"@jgengine/core/physics/physicsWorld — standalone fixed-capacity rigid-body sim (SoA buffers, spatial-hash broadphase, sleeping) for many colliding dynamic bodies; independent of the defineGame physics character-controller config.",
|
|
14
|
+
"ctx.time simulation clock (@jgengine/core/time/simClock) — dt in onTick is game time, so decay/regen/AI written as rate*dt obey pause and fast-forward for free. Configure via defineGame({ time: { scale?, speeds?, dayLength?, start?, startPaused? } }); pause/play/setSpeed/calendar controls, and useGameClock() in React.",
|
|
15
|
+
"@jgengine/shell environment/terrain/water/weather/structures renderers — EnvironmentScene mounts an environment() descriptor; GrassField, ProceduralGround, Ocean, RainField, SnowField, LightningStrike, GeneratedBuilding, and world/InstancedBodies (renders PhysicsWorld bodies).",
|
|
16
|
+
"@jgengine/react/liveBind — useFrameBind drives a DOM/SVG element from a per-frame value without re-rendering React.",
|
|
17
|
+
],
|
|
18
|
+
changed: [],
|
|
19
|
+
removed: [],
|
|
20
|
+
},
|
|
3
21
|
"0.5.0": {
|
|
4
22
|
migrate: [
|
|
5
23
|
"Bump every @jgengine/* dependency to ^0.5.0 (the eight packages version in lockstep).",
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
export interface PhysicsBounds {
|
|
2
|
+
min: readonly [number, number, number];
|
|
3
|
+
max: readonly [number, number, number];
|
|
4
|
+
}
|
|
5
|
+
export interface PhysicsWorldConfig {
|
|
6
|
+
/** Maximum bodies the SoA buffers can hold. Fixed at construction; no reallocation later. */
|
|
7
|
+
capacity: number;
|
|
8
|
+
/** AABB container; every body is constrained inside it (floor included). */
|
|
9
|
+
bounds: PhysicsBounds;
|
|
10
|
+
/** Downward acceleration applied to awake bodies each substep. Default -20. */
|
|
11
|
+
gravity?: number;
|
|
12
|
+
/** Broadphase cell edge; tune to ≈ small-body diameter. Default 1. */
|
|
13
|
+
cellSize?: number;
|
|
14
|
+
/** Fixed substep length in seconds. Default 1/60. */
|
|
15
|
+
fixedDt?: number;
|
|
16
|
+
/** Cap on substeps per `step()` — bounds catch-up work, drops the rest. Default 4. */
|
|
17
|
+
maxSubsteps?: number;
|
|
18
|
+
/** Bounce factor on contacts and walls, 0..1. Default 0.2. */
|
|
19
|
+
restitution?: number;
|
|
20
|
+
/** Approach speed below which restitution is ignored (contacts rest instead of jittering). Default 1. */
|
|
21
|
+
restitutionThreshold?: number;
|
|
22
|
+
/** Coulomb friction coefficient on contacts and the floor, 0..1. Default 0.4. */
|
|
23
|
+
friction?: number;
|
|
24
|
+
/** Per-second linear velocity damping (physical energy bleed so piles settle and sleep). Default 0.2. */
|
|
25
|
+
linearDamping?: number;
|
|
26
|
+
/** Sequential-impulse velocity iterations per substep (higher = more stable stacks). Default 4. */
|
|
27
|
+
solverIterations?: number;
|
|
28
|
+
/** Baumgarte positional-correction fraction per substep, 0..1. Default 0.2. */
|
|
29
|
+
correctionFactor?: number;
|
|
30
|
+
/** Penetration tolerated before correction kicks in. Default 0.005. */
|
|
31
|
+
slop?: number;
|
|
32
|
+
/** Speed at or below which a body counts as still. Default 0.08. */
|
|
33
|
+
sleepLinearVelocity?: number;
|
|
34
|
+
/** Consecutive still substeps before a body sleeps. Default 30. */
|
|
35
|
+
sleepThresholdSteps?: number;
|
|
36
|
+
/** Approach speed a contact must exceed to wake a sleeping body (resting contacts don't). Default 0.5. */
|
|
37
|
+
wakeThreshold?: number;
|
|
38
|
+
}
|
|
39
|
+
export interface PhysicsStats {
|
|
40
|
+
count: number;
|
|
41
|
+
awake: number;
|
|
42
|
+
sleeping: number;
|
|
43
|
+
contacts: number;
|
|
44
|
+
pairs: number;
|
|
45
|
+
substeps: number;
|
|
46
|
+
stepMs: number;
|
|
47
|
+
}
|
|
48
|
+
export interface AddBodyOptions {
|
|
49
|
+
position: readonly [number, number, number];
|
|
50
|
+
halfExtents: readonly [number, number, number];
|
|
51
|
+
velocity?: readonly [number, number, number];
|
|
52
|
+
/** ≤ 0 or omitted with `static` → infinite mass (immovable collider). Default 1. */
|
|
53
|
+
mass?: number;
|
|
54
|
+
/** Immovable collider: never integrates, never sleeps-out, invMass 0. */
|
|
55
|
+
static?: boolean;
|
|
56
|
+
/** Seed the body asleep (a settled bed pays no integration cost until woken). */
|
|
57
|
+
asleep?: boolean;
|
|
58
|
+
}
|
|
59
|
+
/** Grid cell containing a point, clamped into the grid. Pure — exported for tests. */
|
|
60
|
+
export declare function cellCoord(value: number, min: number, cellSize: number, cells: number): number;
|
|
61
|
+
/** Linear cell index from clamped 3D coords. Pure — exported for tests. */
|
|
62
|
+
export declare function cellIndex(cx: number, cy: number, cz: number, nx: number, ny: number): number;
|
|
63
|
+
export declare class PhysicsWorld {
|
|
64
|
+
readonly capacity: number;
|
|
65
|
+
readonly bounds: PhysicsBounds;
|
|
66
|
+
readonly gravity: number;
|
|
67
|
+
readonly cellSize: number;
|
|
68
|
+
readonly fixedDt: number;
|
|
69
|
+
readonly maxSubsteps: number;
|
|
70
|
+
readonly restitution: number;
|
|
71
|
+
readonly restitutionThreshold: number;
|
|
72
|
+
readonly friction: number;
|
|
73
|
+
readonly linearDamping: number;
|
|
74
|
+
readonly solverIterations: number;
|
|
75
|
+
readonly correctionFactor: number;
|
|
76
|
+
readonly slop: number;
|
|
77
|
+
readonly sleepMove2: number;
|
|
78
|
+
readonly sleepSteps: number;
|
|
79
|
+
readonly wakeThreshold: number;
|
|
80
|
+
readonly posX: Float32Array;
|
|
81
|
+
readonly posY: Float32Array;
|
|
82
|
+
readonly posZ: Float32Array;
|
|
83
|
+
readonly velX: Float32Array;
|
|
84
|
+
readonly velY: Float32Array;
|
|
85
|
+
readonly velZ: Float32Array;
|
|
86
|
+
readonly halfX: Float32Array;
|
|
87
|
+
readonly halfY: Float32Array;
|
|
88
|
+
readonly halfZ: Float32Array;
|
|
89
|
+
readonly invMass: Float32Array;
|
|
90
|
+
readonly flags: Uint8Array;
|
|
91
|
+
/** 1 for a body that took part in a contact this frame; reset each `step()`. Render tint reads this. */
|
|
92
|
+
readonly contact: Uint8Array;
|
|
93
|
+
private readonly sleepTimer;
|
|
94
|
+
private readonly prevX;
|
|
95
|
+
private readonly prevY;
|
|
96
|
+
private readonly prevZ;
|
|
97
|
+
private readonly nx;
|
|
98
|
+
private readonly ny;
|
|
99
|
+
private readonly nz;
|
|
100
|
+
private readonly numCells;
|
|
101
|
+
private readonly cellStart;
|
|
102
|
+
private readonly cursor;
|
|
103
|
+
private readonly sorted;
|
|
104
|
+
private readonly bodyCell;
|
|
105
|
+
private readonly awakeList;
|
|
106
|
+
private readonly awakeSlot;
|
|
107
|
+
private awakeCount;
|
|
108
|
+
private bodyCount;
|
|
109
|
+
private accumulator;
|
|
110
|
+
private readonly stats;
|
|
111
|
+
constructor(config: PhysicsWorldConfig);
|
|
112
|
+
private addAwake;
|
|
113
|
+
private removeAwake;
|
|
114
|
+
get count(): number;
|
|
115
|
+
get cells(): {
|
|
116
|
+
nx: number;
|
|
117
|
+
ny: number;
|
|
118
|
+
nz: number;
|
|
119
|
+
total: number;
|
|
120
|
+
};
|
|
121
|
+
addBody(options: AddBodyOptions): number;
|
|
122
|
+
isSleeping(i: number): boolean;
|
|
123
|
+
wake(i: number): void;
|
|
124
|
+
wakeAll(): void;
|
|
125
|
+
clear(): void;
|
|
126
|
+
/** Advance by a real frame delta; runs whole fixed substeps and carries the remainder. */
|
|
127
|
+
step(frameDt: number): PhysicsStats;
|
|
128
|
+
getStats(): PhysicsStats;
|
|
129
|
+
private substep;
|
|
130
|
+
private constrainBounds;
|
|
131
|
+
private buildGrid;
|
|
132
|
+
private cellOf;
|
|
133
|
+
private solve;
|
|
134
|
+
private resolve;
|
|
135
|
+
private updateSleep;
|
|
136
|
+
}
|