@certe/atmos-terrain 0.1.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/LICENCE +674 -0
- package/README.md +155 -0
- package/dist/chunk-key.d.ts +10 -0
- package/dist/chunk-key.d.ts.map +1 -0
- package/dist/chunk-key.js +27 -0
- package/dist/chunk-key.js.map +1 -0
- package/dist/chunk.d.ts +36 -0
- package/dist/chunk.d.ts.map +1 -0
- package/dist/chunk.js +128 -0
- package/dist/chunk.js.map +1 -0
- package/dist/density-field.d.ts +34 -0
- package/dist/density-field.d.ts.map +1 -0
- package/dist/density-field.js +68 -0
- package/dist/density-field.js.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +22 -0
- package/dist/index.js.map +1 -0
- package/dist/lod-chunk.d.ts +20 -0
- package/dist/lod-chunk.d.ts.map +1 -0
- package/dist/lod-chunk.js +256 -0
- package/dist/lod-chunk.js.map +1 -0
- package/dist/lod-extract.d.ts +21 -0
- package/dist/lod-extract.d.ts.map +1 -0
- package/dist/lod-extract.js +178 -0
- package/dist/lod-extract.js.map +1 -0
- package/dist/marching-cubes-tables.d.ts +28 -0
- package/dist/marching-cubes-tables.d.ts.map +1 -0
- package/dist/marching-cubes-tables.js +344 -0
- package/dist/marching-cubes-tables.js.map +1 -0
- package/dist/marching-cubes.d.ts +17 -0
- package/dist/marching-cubes.d.ts.map +1 -0
- package/dist/marching-cubes.js +200 -0
- package/dist/marching-cubes.js.map +1 -0
- package/dist/register-builtins.d.ts +2 -0
- package/dist/register-builtins.d.ts.map +1 -0
- package/dist/register-builtins.js +34 -0
- package/dist/register-builtins.js.map +1 -0
- package/dist/terrain-editor.d.ts +15 -0
- package/dist/terrain-editor.d.ts.map +1 -0
- package/dist/terrain-editor.js +85 -0
- package/dist/terrain-editor.js.map +1 -0
- package/dist/terrain-normals.d.ts +13 -0
- package/dist/terrain-normals.d.ts.map +1 -0
- package/dist/terrain-normals.js +96 -0
- package/dist/terrain-normals.js.map +1 -0
- package/dist/terrain-volume.d.ts +42 -0
- package/dist/terrain-volume.d.ts.map +1 -0
- package/dist/terrain-volume.js +146 -0
- package/dist/terrain-volume.js.map +1 -0
- package/dist/terrain-world.d.ts +67 -0
- package/dist/terrain-world.d.ts.map +1 -0
- package/dist/terrain-world.js +344 -0
- package/dist/terrain-world.js.map +1 -0
- package/dist/types.d.ts +65 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +23 -0
- package/dist/types.js.map +1 -0
- package/package.json +29 -0
- package/src/index.ts +42 -0
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import { Component, GameObject } from '@certe/atmos-core';
|
|
2
|
+
import { MeshRenderer, createMaterial } from '@certe/atmos-renderer';
|
|
3
|
+
import { TerrainChunk } from './chunk.js';
|
|
4
|
+
import { chunkKey } from './chunk-key.js';
|
|
5
|
+
import { applyEdit } from './terrain-editor.js';
|
|
6
|
+
import { DEFAULT_TERRAIN_CONFIG } from './types.js';
|
|
7
|
+
/**
|
|
8
|
+
* Bounded terrain volume component.
|
|
9
|
+
* Manages an NxMxP grid of terrain chunks as child GameObjects with MeshRenderers.
|
|
10
|
+
*/
|
|
11
|
+
export class TerrainVolume extends Component {
|
|
12
|
+
/** Number of chunks along each axis */
|
|
13
|
+
chunksX = 4;
|
|
14
|
+
chunksY = 2;
|
|
15
|
+
chunksZ = 4;
|
|
16
|
+
config = { ...DEFAULT_TERRAIN_CONFIG };
|
|
17
|
+
_densityFn = () => 1;
|
|
18
|
+
_chunks = new Map();
|
|
19
|
+
_chunkObjects = new Map();
|
|
20
|
+
_rendererCtx = null;
|
|
21
|
+
_scene = null;
|
|
22
|
+
_material = null;
|
|
23
|
+
/** GOs kept alive for 1 overlap frame, then moved to _pendingRemoval. */
|
|
24
|
+
_pendingDisable = [];
|
|
25
|
+
/** GOs with disabled MR, ready for scene removal + buffer destruction. */
|
|
26
|
+
_pendingRemoval = [];
|
|
27
|
+
setDensityFn(fn) {
|
|
28
|
+
this._densityFn = fn;
|
|
29
|
+
}
|
|
30
|
+
init(device, pipelineResources, scene, material) {
|
|
31
|
+
this._rendererCtx = { device, pipelineResources };
|
|
32
|
+
this._scene = scene;
|
|
33
|
+
this._material = material ?? createMaterial({ roughness: 0.8, metallic: 0.0 });
|
|
34
|
+
}
|
|
35
|
+
build() {
|
|
36
|
+
if (!this._rendererCtx || !this._scene)
|
|
37
|
+
return;
|
|
38
|
+
this._destroyAllChunks();
|
|
39
|
+
for (let cz = 0; cz < this.chunksZ; cz++) {
|
|
40
|
+
for (let cy = 0; cy < this.chunksY; cy++) {
|
|
41
|
+
for (let cx = 0; cx < this.chunksX; cx++) {
|
|
42
|
+
this._buildChunk(cx, cy, cz);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
edit(op) {
|
|
48
|
+
const dirtyKeys = applyEdit(op, this._chunks, this.config.chunkSize, this.config.voxelSize);
|
|
49
|
+
for (const key of dirtyKeys) {
|
|
50
|
+
const chunk = this._chunks.get(key);
|
|
51
|
+
if (!chunk || !this._rendererCtx)
|
|
52
|
+
continue;
|
|
53
|
+
this._rebuildChunkMesh(key, chunk);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
onDestroy() {
|
|
57
|
+
this._destroyAllChunks();
|
|
58
|
+
}
|
|
59
|
+
onRender() {
|
|
60
|
+
this._flushRemovals();
|
|
61
|
+
}
|
|
62
|
+
_buildChunk(cx, cy, cz) {
|
|
63
|
+
if (!this._rendererCtx || !this._scene)
|
|
64
|
+
return;
|
|
65
|
+
const key = chunkKey(cx, cy, cz);
|
|
66
|
+
const chunk = new TerrainChunk(cx, cy, cz, this.config.chunkSize);
|
|
67
|
+
chunk.sampleDensity(this._densityFn, this.config.voxelSize);
|
|
68
|
+
const mesh = chunk.buildMesh(this._rendererCtx.device, this.config, this._densityFn);
|
|
69
|
+
this._chunks.set(key, chunk);
|
|
70
|
+
if (!mesh)
|
|
71
|
+
return;
|
|
72
|
+
const go = this._createChunkObject(cx, cy, cz, mesh);
|
|
73
|
+
this._chunkObjects.set(key, go);
|
|
74
|
+
}
|
|
75
|
+
_rebuildChunkMesh(key, chunk) {
|
|
76
|
+
if (!this._rendererCtx || !this._scene)
|
|
77
|
+
return;
|
|
78
|
+
const mesh = chunk.buildMesh(this._rendererCtx.device, this.config, this._densityFn);
|
|
79
|
+
// Keep old GO alive with MR enabled for 1 overlap frame
|
|
80
|
+
const oldGo = this._chunkObjects.get(key);
|
|
81
|
+
if (oldGo) {
|
|
82
|
+
this._pendingDisable.push(oldGo);
|
|
83
|
+
this._chunkObjects.delete(key);
|
|
84
|
+
}
|
|
85
|
+
if (!mesh)
|
|
86
|
+
return;
|
|
87
|
+
const go = this._createChunkObject(chunk.cx, chunk.cy, chunk.cz, mesh);
|
|
88
|
+
this._chunkObjects.set(key, go);
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Two-phase deferred removal:
|
|
92
|
+
* Phase 1: disable overlap GOs (were drawn alongside replacement last frame)
|
|
93
|
+
* Phase 2: destroy GOs that were disabled last frame (safe after submit)
|
|
94
|
+
*/
|
|
95
|
+
_flushRemovals() {
|
|
96
|
+
for (const go of this._pendingRemoval) {
|
|
97
|
+
const mr = go.getComponent(MeshRenderer);
|
|
98
|
+
if (mr)
|
|
99
|
+
mr.destroyMesh();
|
|
100
|
+
if (this._scene)
|
|
101
|
+
this._scene.remove(go);
|
|
102
|
+
}
|
|
103
|
+
this._pendingRemoval.length = 0;
|
|
104
|
+
for (const go of this._pendingDisable) {
|
|
105
|
+
const mr = go.getComponent(MeshRenderer);
|
|
106
|
+
if (mr)
|
|
107
|
+
mr.enabled = false;
|
|
108
|
+
this._pendingRemoval.push(go);
|
|
109
|
+
}
|
|
110
|
+
this._pendingDisable.length = 0;
|
|
111
|
+
}
|
|
112
|
+
_createChunkObject(cx, cy, cz, mesh) {
|
|
113
|
+
const chunkWorldSize = this.config.chunkSize * this.config.voxelSize;
|
|
114
|
+
const go = new GameObject(`Chunk_${cx}_${cy}_${cz}`);
|
|
115
|
+
go.transient = true;
|
|
116
|
+
go.setParent(this.gameObject);
|
|
117
|
+
go.transform.setPosition(cx * chunkWorldSize, cy * chunkWorldSize, cz * chunkWorldSize);
|
|
118
|
+
go.transform.updateWorldMatrix();
|
|
119
|
+
this._scene.add(go);
|
|
120
|
+
const mr = go.addComponent(MeshRenderer);
|
|
121
|
+
mr.init(this._rendererCtx, mesh, this._material ?? undefined);
|
|
122
|
+
return go;
|
|
123
|
+
}
|
|
124
|
+
_destroyGO(go) {
|
|
125
|
+
const mr = go.getComponent(MeshRenderer);
|
|
126
|
+
if (mr)
|
|
127
|
+
mr.destroyMesh();
|
|
128
|
+
if (this._scene)
|
|
129
|
+
this._scene.remove(go);
|
|
130
|
+
}
|
|
131
|
+
_destroyAllChunks() {
|
|
132
|
+
for (const go of this._pendingRemoval)
|
|
133
|
+
this._destroyGO(go);
|
|
134
|
+
this._pendingRemoval.length = 0;
|
|
135
|
+
for (const go of this._pendingDisable)
|
|
136
|
+
this._destroyGO(go);
|
|
137
|
+
this._pendingDisable.length = 0;
|
|
138
|
+
for (const [, go] of this._chunkObjects)
|
|
139
|
+
this._destroyGO(go);
|
|
140
|
+
this._chunkObjects.clear();
|
|
141
|
+
for (const [, chunk] of this._chunks)
|
|
142
|
+
chunk.destroyCPU();
|
|
143
|
+
this._chunks.clear();
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
//# sourceMappingURL=terrain-volume.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"terrain-volume.js","sourceRoot":"","sources":["../src/terrain-volume.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { Component, GameObject, Scene } from "@certe/atmos-core";
|
|
2
|
+
import type { Material } from "@certe/atmos-renderer";
|
|
3
|
+
import type { TerrainPipelineResources } from "@certe/atmos-renderer";
|
|
4
|
+
import type { DensityFn, TerrainConfig, TerrainEdit, LODConfig, SplatWeightFn, SplatTextures } from "./types.js";
|
|
5
|
+
/** Infinite terrain world: streams chunks around a focus point with 3-level LOD. */
|
|
6
|
+
export declare class TerrainWorld extends Component {
|
|
7
|
+
/** Chunk load radius (in chunks) */
|
|
8
|
+
loadRadius: number;
|
|
9
|
+
/** Chunk unload radius (in chunks, should be > loadRadius for hysteresis) */
|
|
10
|
+
unloadRadius: number;
|
|
11
|
+
/** Max chunk mesh builds per frame (amortization) */
|
|
12
|
+
maxBuildsPerFrame: number;
|
|
13
|
+
/** Per-frame time budget for chunk builds in ms (overrides maxBuildsPerFrame). */
|
|
14
|
+
buildBudgetMs: number;
|
|
15
|
+
/** GO whose world position is the terrain focus point (e.g. camera). */
|
|
16
|
+
cameraTarget: GameObject | null;
|
|
17
|
+
config: TerrainConfig;
|
|
18
|
+
lodConfig: LODConfig;
|
|
19
|
+
private _densityFn;
|
|
20
|
+
private _chunks;
|
|
21
|
+
private _chunkObjects;
|
|
22
|
+
private _rendererCtx;
|
|
23
|
+
private _scene;
|
|
24
|
+
private _material;
|
|
25
|
+
private _terrainPipeline;
|
|
26
|
+
private _splatTextures;
|
|
27
|
+
private _weightFn;
|
|
28
|
+
private _focusX;
|
|
29
|
+
private _focusY;
|
|
30
|
+
private _focusZ;
|
|
31
|
+
private _buildQueue;
|
|
32
|
+
private _queuedKeys;
|
|
33
|
+
private _pendingDisable;
|
|
34
|
+
private _pendingRemoval;
|
|
35
|
+
private _initialized;
|
|
36
|
+
setDensityFn(fn: DensityFn): void;
|
|
37
|
+
setFocus(x: number, y: number, z: number): void;
|
|
38
|
+
init(device: GPUDevice, pipelineResources: import('@certe/atmos-renderer').PipelineResources, scene: Scene, material?: Material): void;
|
|
39
|
+
/** Enable terrain splatting with 3 textures and a weight function. */
|
|
40
|
+
setSplatMaterials(terrainPipeline: TerrainPipelineResources, textures: SplatTextures, weightFn: SplatWeightFn): void;
|
|
41
|
+
get isSplatting(): boolean;
|
|
42
|
+
onRender(): void;
|
|
43
|
+
edit(op: TerrainEdit): void;
|
|
44
|
+
onDestroy(): void;
|
|
45
|
+
/** Compute LOD level for a chunk based on distance to focus. */
|
|
46
|
+
private _computeLOD;
|
|
47
|
+
/** 6-bit mask: bit set when neighbor has different LOD (overlap needed). */
|
|
48
|
+
private _computeSkirtFaces;
|
|
49
|
+
private _queueNewChunks;
|
|
50
|
+
/** Queue chunks whose LOD or skirt mask changed for re-meshing. */
|
|
51
|
+
private _detectLODChanges;
|
|
52
|
+
private _unloadDistantChunks;
|
|
53
|
+
private _processBuildQueue;
|
|
54
|
+
/** Re-mesh an existing chunk with updated LOD / skirt info. */
|
|
55
|
+
private _remeshExisting;
|
|
56
|
+
private _buildMesh;
|
|
57
|
+
private _rebuildChunkMesh;
|
|
58
|
+
private _createChunkObject;
|
|
59
|
+
private _removeChunk;
|
|
60
|
+
/** Immediately disable renderer and queue GO for destruction next frame. */
|
|
61
|
+
private _deferRemoval;
|
|
62
|
+
/** Two-phase deferred removal: disable → destroy (1 frame overlap for replacements). */
|
|
63
|
+
private _flushRemovals;
|
|
64
|
+
private _destroyGO;
|
|
65
|
+
private _destroyAllChunks;
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=terrain-world.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"terrain-world.d.ts","sourceRoot":"","sources":["../src/terrain-world.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAMjE,OAAO,KAAK,EACV,QAAQ,EAET,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,uBAAuB,CAAC;AAKtE,OAAO,KAAK,EACV,SAAS,EACT,aAAa,EACb,WAAW,EACX,SAAS,EACT,aAAa,EACb,aAAa,EACd,MAAM,YAAY,CAAC;AAWpB,oFAAoF;AACpF,qBAAa,YAAa,SAAQ,SAAS;IACzC,oCAAoC;IACpC,UAAU,SAAK;IACf,6EAA6E;IAC7E,YAAY,SAAK;IACjB,qDAAqD;IACrD,iBAAiB,SAAK;IACtB,kFAAkF;IAClF,aAAa,SAAK;IAElB,wEAAwE;IACxE,YAAY,EAAE,UAAU,GAAG,IAAI,CAAQ;IAEvC,MAAM,EAAE,aAAa,CAAiC;IACtD,SAAS,EAAE,SAAS,CAA6B;IAEjD,OAAO,CAAC,UAAU,CAAsB;IACxC,OAAO,CAAC,OAAO,CAAmC;IAClD,OAAO,CAAC,aAAa,CAAiC;IACtD,OAAO,CAAC,YAAY,CAAoC;IACxD,OAAO,CAAC,MAAM,CAAsB;IACpC,OAAO,CAAC,SAAS,CAAyB;IAG1C,OAAO,CAAC,gBAAgB,CAAyC;IACjE,OAAO,CAAC,cAAc,CAA8B;IACpD,OAAO,CAAC,SAAS,CAA8B;IAE/C,OAAO,CAAC,OAAO,CAAK;IACpB,OAAO,CAAC,OAAO,CAAK;IACpB,OAAO,CAAC,OAAO,CAAK;IACpB,OAAO,CAAC,WAAW,CAAoB;IACvC,OAAO,CAAC,WAAW,CAAqB;IACxC,OAAO,CAAC,eAAe,CAAoB;IAC3C,OAAO,CAAC,eAAe,CAAoB;IAC3C,OAAO,CAAC,YAAY,CAAS;IAE7B,YAAY,CAAC,EAAE,EAAE,SAAS,GAAG,IAAI;IAIjC,QAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI;IAM/C,IAAI,CACF,MAAM,EAAE,SAAS,EACjB,iBAAiB,EAAE,OAAO,uBAAuB,EAAE,iBAAiB,EACpE,KAAK,EAAE,KAAK,EACZ,QAAQ,CAAC,EAAE,QAAQ,GAClB,IAAI;IAQP,sEAAsE;IACtE,iBAAiB,CACf,eAAe,EAAE,wBAAwB,EACzC,QAAQ,EAAE,aAAa,EACvB,QAAQ,EAAE,aAAa,GACtB,IAAI;IAMP,IAAI,WAAW,IAAI,OAAO,CAMzB;IAED,QAAQ,IAAI,IAAI;IAkBhB,IAAI,CAAC,EAAE,EAAE,WAAW,GAAG,IAAI;IAe3B,SAAS,IAAI,IAAI;IAIjB,gEAAgE;IAChE,OAAO,CAAC,WAAW;IAmBnB,4EAA4E;IAC5E,OAAO,CAAC,kBAAkB;IAyB1B,OAAO,CAAC,eAAe;IAiCvB,mEAAmE;IACnE,OAAO,CAAC,iBAAiB;IAiCzB,OAAO,CAAC,oBAAoB;IA0B5B,OAAO,CAAC,kBAAkB;IAqD1B,+DAA+D;IAC/D,OAAO,CAAC,eAAe;IAavB,OAAO,CAAC,UAAU;IA2BlB,OAAO,CAAC,iBAAiB;IAyBzB,OAAO,CAAC,kBAAkB;IAkC1B,OAAO,CAAC,YAAY;IASpB,4EAA4E;IAC5E,OAAO,CAAC,aAAa;IAQrB,wFAAwF;IACxF,OAAO,CAAC,cAAc;IAatB,OAAO,CAAC,UAAU;IAQlB,OAAO,CAAC,iBAAiB;CAY1B"}
|
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
import { Component, GameObject } from "@certe/atmos-core";
|
|
2
|
+
import { MeshRenderer, TerrainMeshRenderer, createMaterial, } from "@certe/atmos-renderer";
|
|
3
|
+
import { TerrainChunk } from "./chunk.js";
|
|
4
|
+
import { buildLODMesh, buildLODSplatMesh } from "./lod-chunk.js";
|
|
5
|
+
import { chunkKey, fromChunkKey, worldToChunk } from "./chunk-key.js";
|
|
6
|
+
import { applyEdit } from "./terrain-editor.js";
|
|
7
|
+
import { DEFAULT_TERRAIN_CONFIG, DEFAULT_LOD_CONFIG } from "./types.js";
|
|
8
|
+
/** Infinite terrain world: streams chunks around a focus point with 3-level LOD. */
|
|
9
|
+
export class TerrainWorld extends Component {
|
|
10
|
+
/** Chunk load radius (in chunks) */
|
|
11
|
+
loadRadius = 4;
|
|
12
|
+
/** Chunk unload radius (in chunks, should be > loadRadius for hysteresis) */
|
|
13
|
+
unloadRadius = 6;
|
|
14
|
+
/** Max chunk mesh builds per frame (amortization) */
|
|
15
|
+
maxBuildsPerFrame = 2;
|
|
16
|
+
/** Per-frame time budget for chunk builds in ms (overrides maxBuildsPerFrame). */
|
|
17
|
+
buildBudgetMs = 8;
|
|
18
|
+
/** GO whose world position is the terrain focus point (e.g. camera). */
|
|
19
|
+
cameraTarget = null;
|
|
20
|
+
config = { ...DEFAULT_TERRAIN_CONFIG };
|
|
21
|
+
lodConfig = { ...DEFAULT_LOD_CONFIG };
|
|
22
|
+
_densityFn = () => 1;
|
|
23
|
+
_chunks = new Map();
|
|
24
|
+
_chunkObjects = new Map();
|
|
25
|
+
_rendererCtx = null;
|
|
26
|
+
_scene = null;
|
|
27
|
+
_material = null;
|
|
28
|
+
// Splat materials (optional — falls back to single-material MeshRenderer)
|
|
29
|
+
_terrainPipeline = null;
|
|
30
|
+
_splatTextures = null;
|
|
31
|
+
_weightFn = null;
|
|
32
|
+
_focusX = 0;
|
|
33
|
+
_focusY = 0;
|
|
34
|
+
_focusZ = 0;
|
|
35
|
+
_buildQueue = [];
|
|
36
|
+
_queuedKeys = new Set();
|
|
37
|
+
_pendingDisable = [];
|
|
38
|
+
_pendingRemoval = [];
|
|
39
|
+
_initialized = false;
|
|
40
|
+
setDensityFn(fn) {
|
|
41
|
+
this._densityFn = fn;
|
|
42
|
+
}
|
|
43
|
+
setFocus(x, y, z) {
|
|
44
|
+
this._focusX = x;
|
|
45
|
+
this._focusY = y;
|
|
46
|
+
this._focusZ = z;
|
|
47
|
+
}
|
|
48
|
+
init(device, pipelineResources, scene, material) {
|
|
49
|
+
this._rendererCtx = { device, pipelineResources };
|
|
50
|
+
this._scene = scene;
|
|
51
|
+
this._material =
|
|
52
|
+
material ?? createMaterial({ roughness: 0.8, metallic: 0.0 });
|
|
53
|
+
this._initialized = true;
|
|
54
|
+
}
|
|
55
|
+
/** Enable terrain splatting with 3 textures and a weight function. */
|
|
56
|
+
setSplatMaterials(terrainPipeline, textures, weightFn) {
|
|
57
|
+
this._terrainPipeline = terrainPipeline;
|
|
58
|
+
this._splatTextures = textures;
|
|
59
|
+
this._weightFn = weightFn;
|
|
60
|
+
}
|
|
61
|
+
get isSplatting() {
|
|
62
|
+
return (this._terrainPipeline !== null &&
|
|
63
|
+
this._splatTextures !== null &&
|
|
64
|
+
this._weightFn !== null);
|
|
65
|
+
}
|
|
66
|
+
onRender() {
|
|
67
|
+
if (!this._initialized)
|
|
68
|
+
return;
|
|
69
|
+
if (this.cameraTarget) {
|
|
70
|
+
const m = this.cameraTarget.transform.worldMatrix;
|
|
71
|
+
this._focusX = m[12];
|
|
72
|
+
this._focusY = m[13];
|
|
73
|
+
this._focusZ = m[14];
|
|
74
|
+
}
|
|
75
|
+
this._flushRemovals();
|
|
76
|
+
this._queueNewChunks();
|
|
77
|
+
this._detectLODChanges();
|
|
78
|
+
this._unloadDistantChunks();
|
|
79
|
+
this._processBuildQueue();
|
|
80
|
+
}
|
|
81
|
+
edit(op) {
|
|
82
|
+
const dirtyKeys = applyEdit(op, this._chunks, this.config.chunkSize, this.config.voxelSize);
|
|
83
|
+
for (const key of dirtyKeys) {
|
|
84
|
+
const chunk = this._chunks.get(key);
|
|
85
|
+
if (!chunk || !this._rendererCtx)
|
|
86
|
+
continue;
|
|
87
|
+
this._rebuildChunkMesh(key, chunk);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
onDestroy() {
|
|
91
|
+
this._destroyAllChunks();
|
|
92
|
+
}
|
|
93
|
+
/** Compute LOD level for a chunk based on distance to focus. */
|
|
94
|
+
_computeLOD(cx, cy, cz) {
|
|
95
|
+
const chunkWorldSize = this.config.chunkSize * this.config.voxelSize;
|
|
96
|
+
const [fcx, fcy, fcz] = worldToChunk(this._focusX, this._focusY, this._focusZ, chunkWorldSize);
|
|
97
|
+
const dx = cx - fcx;
|
|
98
|
+
const dy = cy - fcy;
|
|
99
|
+
const dz = cz - fcz;
|
|
100
|
+
const dist = Math.sqrt(dx * dx + dy * dy + dz * dz);
|
|
101
|
+
const [d0, d1] = this.lodConfig.lodDistances;
|
|
102
|
+
if (dist < d0)
|
|
103
|
+
return 0;
|
|
104
|
+
if (dist < d1)
|
|
105
|
+
return 1;
|
|
106
|
+
return 2;
|
|
107
|
+
}
|
|
108
|
+
/** 6-bit mask: bit set when neighbor has different LOD (overlap needed). */
|
|
109
|
+
_computeSkirtFaces(cx, cy, cz, myLod) {
|
|
110
|
+
let mask = 0;
|
|
111
|
+
const offsets = [
|
|
112
|
+
[-1, 0, 0],
|
|
113
|
+
[1, 0, 0],
|
|
114
|
+
[0, -1, 0],
|
|
115
|
+
[0, 1, 0],
|
|
116
|
+
[0, 0, -1],
|
|
117
|
+
[0, 0, 1],
|
|
118
|
+
];
|
|
119
|
+
for (let f = 0; f < 6; f++) {
|
|
120
|
+
const o = offsets[f];
|
|
121
|
+
const neighborLod = this._computeLOD(cx + o[0], cy + o[1], cz + o[2]);
|
|
122
|
+
if (neighborLod !== myLod) {
|
|
123
|
+
mask |= 1 << f;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return mask;
|
|
127
|
+
}
|
|
128
|
+
_queueNewChunks() {
|
|
129
|
+
const chunkWorldSize = this.config.chunkSize * this.config.voxelSize;
|
|
130
|
+
const [fcx, fcy, fcz] = worldToChunk(this._focusX, this._focusY, this._focusZ, chunkWorldSize);
|
|
131
|
+
const r = this.loadRadius;
|
|
132
|
+
for (let dz = -r; dz <= r; dz++) {
|
|
133
|
+
for (let dy = -r; dy <= r; dy++) {
|
|
134
|
+
for (let dx = -r; dx <= r; dx++) {
|
|
135
|
+
const distSq = dx * dx + dy * dy + dz * dz;
|
|
136
|
+
if (distSq > r * r)
|
|
137
|
+
continue;
|
|
138
|
+
const cx = fcx + dx;
|
|
139
|
+
const cy = fcy + dy;
|
|
140
|
+
const cz = fcz + dz;
|
|
141
|
+
const key = chunkKey(cx, cy, cz);
|
|
142
|
+
if (this._chunks.has(key))
|
|
143
|
+
continue;
|
|
144
|
+
if (this._queuedKeys.has(key))
|
|
145
|
+
continue;
|
|
146
|
+
this._buildQueue.push({ key, cx, cy, cz, distSq });
|
|
147
|
+
this._queuedKeys.add(key);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
this._buildQueue.sort((a, b) => a.distSq - b.distSq);
|
|
152
|
+
}
|
|
153
|
+
/** Queue chunks whose LOD or skirt mask changed for re-meshing. */
|
|
154
|
+
_detectLODChanges() {
|
|
155
|
+
const chunkWorldSize = this.config.chunkSize * this.config.voxelSize;
|
|
156
|
+
const [fcx, fcy, fcz] = worldToChunk(this._focusX, this._focusY, this._focusZ, chunkWorldSize);
|
|
157
|
+
for (const [key, chunk] of this._chunks) {
|
|
158
|
+
const newLod = this._computeLOD(chunk.cx, chunk.cy, chunk.cz);
|
|
159
|
+
const newSkirt = this._computeSkirtFaces(chunk.cx, chunk.cy, chunk.cz, newLod);
|
|
160
|
+
if (newLod !== chunk.lodLevel || newSkirt !== chunk.skirtFaces) {
|
|
161
|
+
if (this._queuedKeys.has(key))
|
|
162
|
+
continue;
|
|
163
|
+
const dx = chunk.cx - fcx, dy = chunk.cy - fcy, dz = chunk.cz - fcz;
|
|
164
|
+
this._buildQueue.push({
|
|
165
|
+
key,
|
|
166
|
+
cx: chunk.cx,
|
|
167
|
+
cy: chunk.cy,
|
|
168
|
+
cz: chunk.cz,
|
|
169
|
+
distSq: dx * dx + dy * dy + dz * dz,
|
|
170
|
+
});
|
|
171
|
+
this._queuedKeys.add(key);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
_unloadDistantChunks() {
|
|
176
|
+
const chunkWorldSize = this.config.chunkSize * this.config.voxelSize;
|
|
177
|
+
const [fcx, fcy, fcz] = worldToChunk(this._focusX, this._focusY, this._focusZ, chunkWorldSize);
|
|
178
|
+
const rSq = this.unloadRadius * this.unloadRadius;
|
|
179
|
+
const toRemove = [];
|
|
180
|
+
for (const [key] of this._chunks) {
|
|
181
|
+
const [cx, cy, cz] = fromChunkKey(key);
|
|
182
|
+
const dx = cx - fcx;
|
|
183
|
+
const dy = cy - fcy;
|
|
184
|
+
const dz = cz - fcz;
|
|
185
|
+
if (dx * dx + dy * dy + dz * dz > rSq) {
|
|
186
|
+
toRemove.push(key);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
for (const key of toRemove) {
|
|
190
|
+
this._removeChunk(key);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
_processBuildQueue() {
|
|
194
|
+
if (!this._rendererCtx || !this._scene)
|
|
195
|
+
return;
|
|
196
|
+
const deadline = performance.now() + this.buildBudgetMs;
|
|
197
|
+
let built = 0;
|
|
198
|
+
let idx = 0;
|
|
199
|
+
while (idx < this._buildQueue.length && performance.now() < deadline) {
|
|
200
|
+
const entry = this._buildQueue[idx++];
|
|
201
|
+
this._queuedKeys.delete(entry.key);
|
|
202
|
+
const existing = this._chunks.get(entry.key);
|
|
203
|
+
if (existing) {
|
|
204
|
+
this._remeshExisting(entry.key, existing);
|
|
205
|
+
built++;
|
|
206
|
+
continue;
|
|
207
|
+
}
|
|
208
|
+
const chunk = new TerrainChunk(entry.cx, entry.cy, entry.cz, this.config.chunkSize);
|
|
209
|
+
chunk.sampleDensity(this._densityFn, this.config.voxelSize);
|
|
210
|
+
const lodLevel = this._computeLOD(entry.cx, entry.cy, entry.cz);
|
|
211
|
+
const skirtFaces = this._computeSkirtFaces(entry.cx, entry.cy, entry.cz, lodLevel);
|
|
212
|
+
chunk.lodLevel = lodLevel;
|
|
213
|
+
chunk.skirtFaces = skirtFaces;
|
|
214
|
+
const mesh = this._buildMesh(chunk, lodLevel, skirtFaces);
|
|
215
|
+
this._chunks.set(entry.key, chunk);
|
|
216
|
+
if (mesh) {
|
|
217
|
+
const go = this._createChunkObject(entry.cx, entry.cy, entry.cz, mesh);
|
|
218
|
+
this._chunkObjects.set(entry.key, go);
|
|
219
|
+
}
|
|
220
|
+
built++;
|
|
221
|
+
}
|
|
222
|
+
// Remove processed entries from queue front
|
|
223
|
+
if (idx > 0) {
|
|
224
|
+
this._buildQueue.splice(0, idx);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
/** Re-mesh an existing chunk with updated LOD / skirt info. */
|
|
228
|
+
_remeshExisting(key, chunk) {
|
|
229
|
+
const newLod = this._computeLOD(chunk.cx, chunk.cy, chunk.cz);
|
|
230
|
+
const newSkirt = this._computeSkirtFaces(chunk.cx, chunk.cy, chunk.cz, newLod);
|
|
231
|
+
chunk.lodLevel = newLod;
|
|
232
|
+
chunk.skirtFaces = newSkirt;
|
|
233
|
+
this._rebuildChunkMesh(key, chunk);
|
|
234
|
+
}
|
|
235
|
+
_buildMesh(chunk, lodLevel, skirtFaces) {
|
|
236
|
+
if (!this._rendererCtx)
|
|
237
|
+
return null;
|
|
238
|
+
if (this.isSplatting) {
|
|
239
|
+
return buildLODSplatMesh(chunk, this._rendererCtx.device, this.config, lodLevel, skirtFaces, this._densityFn, this._weightFn);
|
|
240
|
+
}
|
|
241
|
+
return buildLODMesh(chunk, this._rendererCtx.device, this.config, lodLevel, skirtFaces, this._densityFn);
|
|
242
|
+
}
|
|
243
|
+
_rebuildChunkMesh(key, chunk) {
|
|
244
|
+
if (!this._rendererCtx || !this._scene)
|
|
245
|
+
return;
|
|
246
|
+
const mesh = this._buildMesh(chunk, chunk.lodLevel, chunk.skirtFaces);
|
|
247
|
+
const oldGo = this._chunkObjects.get(key);
|
|
248
|
+
if (!mesh) {
|
|
249
|
+
// No geometry — remove old GO if present
|
|
250
|
+
if (oldGo) {
|
|
251
|
+
this._deferRemoval(oldGo);
|
|
252
|
+
this._chunkObjects.delete(key);
|
|
253
|
+
}
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
const go = this._createChunkObject(chunk.cx, chunk.cy, chunk.cz, mesh);
|
|
257
|
+
this._chunkObjects.set(key, go);
|
|
258
|
+
// Keep old GO alive 1 frame for overlap (depth buffer resolves)
|
|
259
|
+
if (oldGo) {
|
|
260
|
+
this._pendingDisable.push(oldGo);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
_createChunkObject(cx, cy, cz, mesh) {
|
|
264
|
+
const chunkWorldSize = this.config.chunkSize * this.config.voxelSize;
|
|
265
|
+
const go = new GameObject(`Chunk_${cx}_${cy}_${cz}`);
|
|
266
|
+
go.transient = true;
|
|
267
|
+
go.setParent(this.gameObject);
|
|
268
|
+
go.transform.setPosition(cx * chunkWorldSize, cy * chunkWorldSize, cz * chunkWorldSize);
|
|
269
|
+
go.transform.updateWorldMatrix();
|
|
270
|
+
this._scene.add(go);
|
|
271
|
+
if (this.isSplatting && this._terrainPipeline && this._splatTextures) {
|
|
272
|
+
const tmr = go.addComponent(TerrainMeshRenderer);
|
|
273
|
+
tmr.init(this._rendererCtx.device, this._terrainPipeline, mesh, this._material ?? undefined, this._splatTextures);
|
|
274
|
+
}
|
|
275
|
+
else {
|
|
276
|
+
const mr = go.addComponent(MeshRenderer);
|
|
277
|
+
mr.init(this._rendererCtx, mesh, this._material ?? undefined);
|
|
278
|
+
}
|
|
279
|
+
return go;
|
|
280
|
+
}
|
|
281
|
+
_removeChunk(key) {
|
|
282
|
+
const go = this._chunkObjects.get(key);
|
|
283
|
+
if (go)
|
|
284
|
+
this._deferRemoval(go);
|
|
285
|
+
this._chunkObjects.delete(key);
|
|
286
|
+
const chunk = this._chunks.get(key);
|
|
287
|
+
if (chunk)
|
|
288
|
+
chunk.destroyCPU();
|
|
289
|
+
this._chunks.delete(key);
|
|
290
|
+
}
|
|
291
|
+
/** Immediately disable renderer and queue GO for destruction next frame. */
|
|
292
|
+
_deferRemoval(go) {
|
|
293
|
+
const mr = go.getComponent(MeshRenderer);
|
|
294
|
+
if (mr)
|
|
295
|
+
mr.enabled = false;
|
|
296
|
+
const tmr = go.getComponent(TerrainMeshRenderer);
|
|
297
|
+
if (tmr)
|
|
298
|
+
tmr.enabled = false;
|
|
299
|
+
this._pendingRemoval.push(go);
|
|
300
|
+
}
|
|
301
|
+
/** Two-phase deferred removal: disable → destroy (1 frame overlap for replacements). */
|
|
302
|
+
_flushRemovals() {
|
|
303
|
+
for (const go of this._pendingRemoval)
|
|
304
|
+
this._destroyGO(go);
|
|
305
|
+
this._pendingRemoval.length = 0;
|
|
306
|
+
for (const go of this._pendingDisable) {
|
|
307
|
+
const mr = go.getComponent(MeshRenderer);
|
|
308
|
+
if (mr)
|
|
309
|
+
mr.enabled = false;
|
|
310
|
+
const tmr = go.getComponent(TerrainMeshRenderer);
|
|
311
|
+
if (tmr)
|
|
312
|
+
tmr.enabled = false;
|
|
313
|
+
this._pendingRemoval.push(go);
|
|
314
|
+
}
|
|
315
|
+
this._pendingDisable.length = 0;
|
|
316
|
+
}
|
|
317
|
+
_destroyGO(go) {
|
|
318
|
+
const mr = go.getComponent(MeshRenderer);
|
|
319
|
+
if (mr)
|
|
320
|
+
mr.destroyMesh();
|
|
321
|
+
const tmr = go.getComponent(TerrainMeshRenderer);
|
|
322
|
+
if (tmr)
|
|
323
|
+
tmr.destroyMesh();
|
|
324
|
+
if (this._scene)
|
|
325
|
+
this._scene.remove(go);
|
|
326
|
+
}
|
|
327
|
+
_destroyAllChunks() {
|
|
328
|
+
for (const go of this._pendingRemoval)
|
|
329
|
+
this._destroyGO(go);
|
|
330
|
+
this._pendingRemoval.length = 0;
|
|
331
|
+
for (const go of this._pendingDisable)
|
|
332
|
+
this._destroyGO(go);
|
|
333
|
+
this._pendingDisable.length = 0;
|
|
334
|
+
for (const [, go] of this._chunkObjects)
|
|
335
|
+
this._destroyGO(go);
|
|
336
|
+
this._chunkObjects.clear();
|
|
337
|
+
for (const [, chunk] of this._chunks)
|
|
338
|
+
chunk.destroyCPU();
|
|
339
|
+
this._chunks.clear();
|
|
340
|
+
this._buildQueue = [];
|
|
341
|
+
this._queuedKeys.clear();
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
//# sourceMappingURL=terrain-world.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"terrain-world.js","sourceRoot":"","sources":["../src/terrain-world.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import type { GPUTextureHandle } from "@certe/atmos-renderer";
|
|
2
|
+
/** Density function: positive = air, negative = solid, surface at isoLevel */
|
|
3
|
+
export type DensityFn = (x: number, y: number, z: number) => number;
|
|
4
|
+
export interface DensitySample {
|
|
5
|
+
density: number;
|
|
6
|
+
weights?: Float32Array;
|
|
7
|
+
}
|
|
8
|
+
export type DensityWithWeightsFn = (x: number, y: number, z: number) => DensitySample;
|
|
9
|
+
/** Computes splat weights from surface normal + world height. Returns [w0, w1]; w2 = 1 - w0 - w1. */
|
|
10
|
+
export type SplatWeightFn = (nx: number, ny: number, nz: number, worldY: number) => [number, number];
|
|
11
|
+
/** Three splat textures (grass/rock/snow or similar) used by the terrain pipeline. */
|
|
12
|
+
export type SplatTextures = [GPUTextureHandle, GPUTextureHandle, GPUTextureHandle];
|
|
13
|
+
export interface ChunkCoord {
|
|
14
|
+
cx: number;
|
|
15
|
+
cy: number;
|
|
16
|
+
cz: number;
|
|
17
|
+
}
|
|
18
|
+
export interface TerrainConfig {
|
|
19
|
+
/** Voxels per chunk axis (default 16) */
|
|
20
|
+
chunkSize: number;
|
|
21
|
+
/** World-space size of each voxel (default 1) */
|
|
22
|
+
voxelSize: number;
|
|
23
|
+
/** Density threshold for surface extraction (default 0) */
|
|
24
|
+
isoLevel: number;
|
|
25
|
+
/** Use gradient-based smooth normals instead of triangle normals */
|
|
26
|
+
smoothNormals: boolean;
|
|
27
|
+
/** Epsilon for central-difference gradient normals */
|
|
28
|
+
normalEpsilon: number;
|
|
29
|
+
}
|
|
30
|
+
export declare const DEFAULT_TERRAIN_CONFIG: TerrainConfig;
|
|
31
|
+
export interface MeshData {
|
|
32
|
+
vertices: Float32Array;
|
|
33
|
+
indices: Uint32Array;
|
|
34
|
+
vertexCount: number;
|
|
35
|
+
indexCount: number;
|
|
36
|
+
}
|
|
37
|
+
export declare const enum BrushShape {
|
|
38
|
+
Sphere = 0,
|
|
39
|
+
Cube = 1
|
|
40
|
+
}
|
|
41
|
+
export interface TerrainEdit {
|
|
42
|
+
shape: BrushShape;
|
|
43
|
+
/** World-space center of the brush */
|
|
44
|
+
x: number;
|
|
45
|
+
y: number;
|
|
46
|
+
z: number;
|
|
47
|
+
/** Brush radius (half-extent for cube) */
|
|
48
|
+
radius: number;
|
|
49
|
+
/** Positive adds material (dig), negative removes (fill). Magnitude = strength. */
|
|
50
|
+
strength: number;
|
|
51
|
+
/** Smooth falloff within the brush (0 = hard edge, 1 = full falloff) */
|
|
52
|
+
falloff: number;
|
|
53
|
+
}
|
|
54
|
+
export declare const enum ChunkState {
|
|
55
|
+
Empty = 0,
|
|
56
|
+
Sampled = 1,
|
|
57
|
+
Meshed = 2,
|
|
58
|
+
Dirty = 3
|
|
59
|
+
}
|
|
60
|
+
export interface LODConfig {
|
|
61
|
+
/** Distance thresholds in chunks: [lod0→lod1, lod1→lod2] */
|
|
62
|
+
lodDistances: [number, number];
|
|
63
|
+
}
|
|
64
|
+
export declare const DEFAULT_LOD_CONFIG: LODConfig;
|
|
65
|
+
//# sourceMappingURL=types.d.ts.map
|