@vgai/engine 0.5.36 → 0.5.37
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/dist/ai/navigation.d.ts +8 -1
- package/dist/ai/navigation.d.ts.map +1 -1
- package/dist/ai/navigation.js +93 -3
- package/package.json +1 -1
- package/src/ai/navigation.ts +103 -4
package/dist/ai/navigation.d.ts
CHANGED
|
@@ -12,7 +12,8 @@
|
|
|
12
12
|
import { NavMeshHelper } from '@recast-navigation/three';
|
|
13
13
|
import type { CrowdAgent } from 'recast-navigation';
|
|
14
14
|
import { Crowd } from 'recast-navigation';
|
|
15
|
-
import type {
|
|
15
|
+
import type { Scene } from 'three';
|
|
16
|
+
import { Mesh } from 'three';
|
|
16
17
|
export type { CrowdAgent };
|
|
17
18
|
export interface Vector3Like {
|
|
18
19
|
x: number;
|
|
@@ -39,6 +40,12 @@ export interface CrowdAgentSnapshot {
|
|
|
39
40
|
corners: Vector3Like[];
|
|
40
41
|
}
|
|
41
42
|
export interface NavMeshBuildParams {
|
|
43
|
+
/** Force bounded tiled generation for imported large-world scenes whose instanced render
|
|
44
|
+
* census cannot be inferred from the source BufferGeometry counts alone. */
|
|
45
|
+
tiled?: boolean | undefined;
|
|
46
|
+
/** Recast tile width/height in voxels. Unity's NavMeshSurface default is 256; keeping tiles at
|
|
47
|
+
* that scale also avoids thousands of empty-tile allocations across sparse imported worlds. */
|
|
48
|
+
tileSize?: number | undefined;
|
|
42
49
|
cellSize?: number | undefined;
|
|
43
50
|
cellHeight?: number | undefined;
|
|
44
51
|
walkableSlopeAngle?: number | undefined;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"navigation.d.ts","sourceRoot":"","sources":["../../src/ai/navigation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,aAAa,
|
|
1
|
+
{"version":3,"file":"navigation.d.ts","sourceRoot":"","sources":["../../src/ai/navigation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,aAAa,EAA2C,MAAM,0BAA0B,CAAC;AAClG,OAAO,KAAK,EAAE,UAAU,EAAW,MAAM,mBAAmB,CAAC;AAC7D,OAAO,EAAE,KAAK,EAAoD,MAAM,mBAAmB,CAAC;AAC5F,OAAO,KAAK,EAAkB,KAAK,EAAE,MAAM,OAAO,CAAC;AACnD,OAAO,EAA0B,IAAI,EAAE,MAAM,OAAO,CAAC;AAGrD,YAAY,EAAE,UAAU,EAAE,CAAC;AAE3B,MAAM,WAAW,WAAW;IAC1B,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;CACX;AAED;iEACiE;AACjE,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,WAAW,CAAC;IACtB,QAAQ,EAAE,WAAW,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf;;8EAE0E;IAC1E,KAAK,EAAE,SAAS,GAAG,SAAS,GAAG,SAAS,CAAC;IACzC;+EAC2E;IAC3E,MAAM,EAAE,WAAW,CAAC;IACpB;;mEAE+D;IAC/D,OAAO,EAAE,WAAW,EAAE,CAAC;CACxB;AAKD,MAAM,WAAW,kBAAkB;IACjC;gFAC4E;IAC5E,KAAK,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC5B;mGAC+F;IAC/F,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,kBAAkB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACxC,cAAc,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACpC,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,cAAc,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACpC,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,sBAAsB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5C,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,eAAe,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACtC;AAID,wBAAsB,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC,CAIpD;AA2ED,qBAAa,cAAc;IACzB,OAAO,CAAC,OAAO,CAAwB;IACvC,OAAO,CAAC,KAAK,CAA6B;IAC1C,OAAO,CAAC,KAAK,CAAsB;IACnC,OAAO,CAAC,MAAM,CAA8B;IAE5C,2EAA2E;IAC3E,eAAe,CAAC,MAAM,EAAE,IAAI,EAAE,EAAE,MAAM,CAAC,EAAE,kBAAkB,GAAG,OAAO;IA+CrE;;;;OAIG;IACH,OAAO,CAAC,aAAa;IAoBrB,iFAAiF;IACjF,UAAU,IAAI,OAAO;IAIrB;;;;;;;;;;;;;;;;OAgBG;IACH,QAAQ,CAAC,KAAK,EAAE,WAAW,EAAE,GAAG,EAAE,WAAW,GAAG,WAAW,EAAE;IAkB7D,kDAAkD;IAClD,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,KAAK;IAMrC,sDAAsD;IACtD,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAI7B;;;;OAIG;IACH,cAAc,IAAI,kBAAkB,EAAE;IAwBtC,iGAAiG;IACjG,YAAY,CAAC,KAAK,EAAE,KAAK,GAAG,aAAa,GAAG,IAAI;IAShD,sDAAsD;IACtD,UAAU,IAAI,UAAU;IAKxB,6DAA6D;IAC7D,YAAY,CAAC,IAAI,EAAE,UAAU,GAAG,IAAI;IAQpC,OAAO,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI;CAM5B"}
|
package/dist/ai/navigation.js
CHANGED
|
@@ -9,8 +9,9 @@
|
|
|
9
9
|
* const crowd = nav.createCrowd(10);
|
|
10
10
|
* // In game loop: nav.updateCrowd(dt);
|
|
11
11
|
*/
|
|
12
|
-
import { NavMeshHelper, threeToSoloNavMesh } from '@recast-navigation/three';
|
|
12
|
+
import { NavMeshHelper, threeToSoloNavMesh, threeToTiledNavMesh } from '@recast-navigation/three';
|
|
13
13
|
import { Crowd, exportNavMesh, importNavMesh, init, NavMeshQuery } from 'recast-navigation';
|
|
14
|
+
import { InstancedMesh, Matrix4, Mesh } from 'three';
|
|
14
15
|
import { DEFAULTS } from '../defaults';
|
|
15
16
|
/** Detour `CrowdAgentState` values, named. */
|
|
16
17
|
const AGENT_STATES = ['invalid', 'walking', 'offmesh'];
|
|
@@ -21,6 +22,74 @@ export async function initNavigation() {
|
|
|
21
22
|
await init();
|
|
22
23
|
wasmInitialized = true;
|
|
23
24
|
}
|
|
25
|
+
/**
|
|
26
|
+
* `@recast-navigation/three` applies each mesh's world matrix to its positions but copies the
|
|
27
|
+
* source indices unchanged. A mirrored world matrix therefore reverses every triangle and can
|
|
28
|
+
* make a visibly upward floor entirely unwalkable. Seat a winding-corrected geometry on an
|
|
29
|
+
* equivalent root mesh for the bake; the game keeps its own native mesh untouched.
|
|
30
|
+
*/
|
|
31
|
+
function reverseTriangleWinding(geometry) {
|
|
32
|
+
const index = geometry.getIndex();
|
|
33
|
+
if (index === null) {
|
|
34
|
+
const position = geometry.getAttribute('position').clone();
|
|
35
|
+
for (let vertex = 0; vertex + 2 < position.count; vertex += 3) {
|
|
36
|
+
for (let component = 0; component < position.itemSize; component += 1) {
|
|
37
|
+
const middle = position.getComponent(vertex + 1, component);
|
|
38
|
+
position.setComponent(vertex + 1, component, position.getComponent(vertex + 2, component));
|
|
39
|
+
position.setComponent(vertex + 2, component, middle);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
geometry.setAttribute('position', position);
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
for (let triangle = 0; triangle + 2 < index.count; triangle += 3) {
|
|
46
|
+
const middle = index.getX(triangle + 1);
|
|
47
|
+
index.setX(triangle + 1, index.getX(triangle + 2));
|
|
48
|
+
index.setX(triangle + 2, middle);
|
|
49
|
+
}
|
|
50
|
+
index.needsUpdate = true;
|
|
51
|
+
}
|
|
52
|
+
function recastInputMeshes(meshes) {
|
|
53
|
+
const temporaryGeometries = [];
|
|
54
|
+
const mirroredGeometries = new WeakMap();
|
|
55
|
+
const geometryForMatrix = (source, matrix) => {
|
|
56
|
+
if (matrix.determinant() >= 0)
|
|
57
|
+
return source;
|
|
58
|
+
const cached = mirroredGeometries.get(source);
|
|
59
|
+
if (cached !== undefined)
|
|
60
|
+
return cached;
|
|
61
|
+
const geometry = source.clone();
|
|
62
|
+
reverseTriangleWinding(geometry);
|
|
63
|
+
mirroredGeometries.set(source, geometry);
|
|
64
|
+
temporaryGeometries.push(geometry);
|
|
65
|
+
return geometry;
|
|
66
|
+
};
|
|
67
|
+
const proxyAt = (geometry, matrix) => {
|
|
68
|
+
const proxy = new Mesh(geometryForMatrix(geometry, matrix));
|
|
69
|
+
// The proxy has no parent, so its local matrix is the source mesh's complete world matrix.
|
|
70
|
+
// The recast bridge calls updateMatrixWorld() itself; disabling auto-update preserves it.
|
|
71
|
+
proxy.matrixAutoUpdate = false;
|
|
72
|
+
proxy.matrix.copy(matrix);
|
|
73
|
+
proxy.matrixWorldNeedsUpdate = true;
|
|
74
|
+
return proxy;
|
|
75
|
+
};
|
|
76
|
+
const corrected = [];
|
|
77
|
+
const instanceMatrix = new Matrix4();
|
|
78
|
+
const worldMatrix = new Matrix4();
|
|
79
|
+
for (const mesh of meshes) {
|
|
80
|
+
mesh.updateWorldMatrix(true, false);
|
|
81
|
+
if (mesh instanceof InstancedMesh) {
|
|
82
|
+
for (let instance = 0; instance < mesh.count; instance += 1) {
|
|
83
|
+
mesh.getMatrixAt(instance, instanceMatrix);
|
|
84
|
+
worldMatrix.multiplyMatrices(mesh.matrixWorld, instanceMatrix);
|
|
85
|
+
corrected.push(proxyAt(mesh.geometry, worldMatrix));
|
|
86
|
+
}
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
corrected.push(mesh.matrixWorld.determinant() >= 0 ? mesh : proxyAt(mesh.geometry, mesh.matrixWorld));
|
|
90
|
+
}
|
|
91
|
+
return { meshes: corrected, temporaryGeometries };
|
|
92
|
+
}
|
|
24
93
|
export class NavMeshManager {
|
|
25
94
|
navMesh = null;
|
|
26
95
|
query = null;
|
|
@@ -29,7 +98,13 @@ export class NavMeshManager {
|
|
|
29
98
|
/** Build navmesh from Three.js Mesh objects (grounds, obstacles, etc.). */
|
|
30
99
|
buildFromMeshes(meshes, params) {
|
|
31
100
|
const d = DEFAULTS.navigation;
|
|
32
|
-
const
|
|
101
|
+
const input = recastInputMeshes(meshes);
|
|
102
|
+
const triangleCount = input.meshes.reduce((total, mesh) => {
|
|
103
|
+
const index = mesh.geometry.getIndex();
|
|
104
|
+
const vertices = mesh.geometry.getAttribute('position').count;
|
|
105
|
+
return total + Math.floor((index?.count ?? vertices) / 3);
|
|
106
|
+
}, 0);
|
|
107
|
+
const config = {
|
|
33
108
|
cs: params?.cellSize ?? d.cellSize,
|
|
34
109
|
ch: params?.cellHeight ?? d.cellHeight,
|
|
35
110
|
walkableSlopeAngle: params?.walkableSlopeAngle ?? d.walkableSlopeAngle,
|
|
@@ -44,7 +119,22 @@ export class NavMeshManager {
|
|
|
44
119
|
maxVertsPerPoly: 6,
|
|
45
120
|
detailSampleDist: 6,
|
|
46
121
|
detailSampleMaxError: 1,
|
|
47
|
-
}
|
|
122
|
+
};
|
|
123
|
+
// A solo heightfield allocates for the whole scene at once. Large imported Unity scenes can
|
|
124
|
+
// exceed Recast's WASM address space during rasterization even though every individual mesh
|
|
125
|
+
// is ordinary-sized. Tiled generation is the library's native large-world path and keeps the
|
|
126
|
+
// same NavMesh result/API while bounding each rasterization allocation.
|
|
127
|
+
let result;
|
|
128
|
+
try {
|
|
129
|
+
result =
|
|
130
|
+
params?.tiled === true || input.meshes.length > 256 || triangleCount > 10_000
|
|
131
|
+
? threeToTiledNavMesh(input.meshes, { ...config, tileSize: params?.tileSize ?? 256 })
|
|
132
|
+
: threeToSoloNavMesh(input.meshes, config);
|
|
133
|
+
}
|
|
134
|
+
finally {
|
|
135
|
+
for (const geometry of input.temporaryGeometries)
|
|
136
|
+
geometry.dispose();
|
|
137
|
+
}
|
|
48
138
|
if (!result.success || !result.navMesh)
|
|
49
139
|
return false;
|
|
50
140
|
// Free the previous recast objects before replacing them (WASM heap leak).
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@vgai/engine",
|
|
3
3
|
"author": "Volter AI, Inc.",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
|
-
"version": "0.5.
|
|
5
|
+
"version": "0.5.37",
|
|
6
6
|
"description": "Readable TypeScript game engine and universal host for Three.js, PixiJS, and React games.",
|
|
7
7
|
"keywords": [
|
|
8
8
|
"game-engine",
|
package/src/ai/navigation.ts
CHANGED
|
@@ -10,10 +10,11 @@
|
|
|
10
10
|
* // In game loop: nav.updateCrowd(dt);
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
-
import { NavMeshHelper, threeToSoloNavMesh } from '@recast-navigation/three';
|
|
13
|
+
import { NavMeshHelper, threeToSoloNavMesh, threeToTiledNavMesh } from '@recast-navigation/three';
|
|
14
14
|
import type { CrowdAgent, NavMesh } from 'recast-navigation';
|
|
15
15
|
import { Crowd, exportNavMesh, importNavMesh, init, NavMeshQuery } from 'recast-navigation';
|
|
16
|
-
import type {
|
|
16
|
+
import type { BufferGeometry, Scene } from 'three';
|
|
17
|
+
import { InstancedMesh, Matrix4, Mesh } from 'three';
|
|
17
18
|
import { DEFAULTS } from '../defaults';
|
|
18
19
|
|
|
19
20
|
export type { CrowdAgent };
|
|
@@ -48,6 +49,12 @@ export interface CrowdAgentSnapshot {
|
|
|
48
49
|
const AGENT_STATES = ['invalid', 'walking', 'offmesh'] as const;
|
|
49
50
|
|
|
50
51
|
export interface NavMeshBuildParams {
|
|
52
|
+
/** Force bounded tiled generation for imported large-world scenes whose instanced render
|
|
53
|
+
* census cannot be inferred from the source BufferGeometry counts alone. */
|
|
54
|
+
tiled?: boolean | undefined;
|
|
55
|
+
/** Recast tile width/height in voxels. Unity's NavMeshSurface default is 256; keeping tiles at
|
|
56
|
+
* that scale also avoids thousands of empty-tile allocations across sparse imported worlds. */
|
|
57
|
+
tileSize?: number | undefined;
|
|
51
58
|
cellSize?: number | undefined;
|
|
52
59
|
cellHeight?: number | undefined;
|
|
53
60
|
walkableSlopeAngle?: number | undefined;
|
|
@@ -68,6 +75,79 @@ export async function initNavigation(): Promise<void> {
|
|
|
68
75
|
wasmInitialized = true;
|
|
69
76
|
}
|
|
70
77
|
|
|
78
|
+
/**
|
|
79
|
+
* `@recast-navigation/three` applies each mesh's world matrix to its positions but copies the
|
|
80
|
+
* source indices unchanged. A mirrored world matrix therefore reverses every triangle and can
|
|
81
|
+
* make a visibly upward floor entirely unwalkable. Seat a winding-corrected geometry on an
|
|
82
|
+
* equivalent root mesh for the bake; the game keeps its own native mesh untouched.
|
|
83
|
+
*/
|
|
84
|
+
function reverseTriangleWinding(geometry: BufferGeometry): void {
|
|
85
|
+
const index = geometry.getIndex();
|
|
86
|
+
if (index === null) {
|
|
87
|
+
const position = geometry.getAttribute('position').clone();
|
|
88
|
+
for (let vertex = 0; vertex + 2 < position.count; vertex += 3) {
|
|
89
|
+
for (let component = 0; component < position.itemSize; component += 1) {
|
|
90
|
+
const middle = position.getComponent(vertex + 1, component);
|
|
91
|
+
position.setComponent(vertex + 1, component, position.getComponent(vertex + 2, component));
|
|
92
|
+
position.setComponent(vertex + 2, component, middle);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
geometry.setAttribute('position', position);
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
for (let triangle = 0; triangle + 2 < index.count; triangle += 3) {
|
|
99
|
+
const middle = index.getX(triangle + 1);
|
|
100
|
+
index.setX(triangle + 1, index.getX(triangle + 2));
|
|
101
|
+
index.setX(triangle + 2, middle);
|
|
102
|
+
}
|
|
103
|
+
index.needsUpdate = true;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function recastInputMeshes(meshes: Mesh[]): {
|
|
107
|
+
readonly meshes: Mesh[];
|
|
108
|
+
readonly temporaryGeometries: BufferGeometry[];
|
|
109
|
+
} {
|
|
110
|
+
const temporaryGeometries: BufferGeometry[] = [];
|
|
111
|
+
const mirroredGeometries = new WeakMap<BufferGeometry, BufferGeometry>();
|
|
112
|
+
const geometryForMatrix = (source: BufferGeometry, matrix: Matrix4): BufferGeometry => {
|
|
113
|
+
if (matrix.determinant() >= 0) return source;
|
|
114
|
+
const cached = mirroredGeometries.get(source);
|
|
115
|
+
if (cached !== undefined) return cached;
|
|
116
|
+
const geometry = source.clone();
|
|
117
|
+
reverseTriangleWinding(geometry);
|
|
118
|
+
mirroredGeometries.set(source, geometry);
|
|
119
|
+
temporaryGeometries.push(geometry);
|
|
120
|
+
return geometry;
|
|
121
|
+
};
|
|
122
|
+
const proxyAt = (geometry: BufferGeometry, matrix: Matrix4): Mesh => {
|
|
123
|
+
const proxy = new Mesh(geometryForMatrix(geometry, matrix));
|
|
124
|
+
// The proxy has no parent, so its local matrix is the source mesh's complete world matrix.
|
|
125
|
+
// The recast bridge calls updateMatrixWorld() itself; disabling auto-update preserves it.
|
|
126
|
+
proxy.matrixAutoUpdate = false;
|
|
127
|
+
proxy.matrix.copy(matrix);
|
|
128
|
+
proxy.matrixWorldNeedsUpdate = true;
|
|
129
|
+
return proxy;
|
|
130
|
+
};
|
|
131
|
+
const corrected: Mesh[] = [];
|
|
132
|
+
const instanceMatrix = new Matrix4();
|
|
133
|
+
const worldMatrix = new Matrix4();
|
|
134
|
+
for (const mesh of meshes) {
|
|
135
|
+
mesh.updateWorldMatrix(true, false);
|
|
136
|
+
if (mesh instanceof InstancedMesh) {
|
|
137
|
+
for (let instance = 0; instance < mesh.count; instance += 1) {
|
|
138
|
+
mesh.getMatrixAt(instance, instanceMatrix);
|
|
139
|
+
worldMatrix.multiplyMatrices(mesh.matrixWorld, instanceMatrix);
|
|
140
|
+
corrected.push(proxyAt(mesh.geometry, worldMatrix));
|
|
141
|
+
}
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
corrected.push(
|
|
145
|
+
mesh.matrixWorld.determinant() >= 0 ? mesh : proxyAt(mesh.geometry, mesh.matrixWorld),
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
return { meshes: corrected, temporaryGeometries };
|
|
149
|
+
}
|
|
150
|
+
|
|
71
151
|
export class NavMeshManager {
|
|
72
152
|
private navMesh: NavMesh | null = null;
|
|
73
153
|
private query: NavMeshQuery | null = null;
|
|
@@ -77,7 +157,13 @@ export class NavMeshManager {
|
|
|
77
157
|
/** Build navmesh from Three.js Mesh objects (grounds, obstacles, etc.). */
|
|
78
158
|
buildFromMeshes(meshes: Mesh[], params?: NavMeshBuildParams): boolean {
|
|
79
159
|
const d = DEFAULTS.navigation;
|
|
80
|
-
const
|
|
160
|
+
const input = recastInputMeshes(meshes);
|
|
161
|
+
const triangleCount = input.meshes.reduce((total, mesh) => {
|
|
162
|
+
const index = mesh.geometry.getIndex();
|
|
163
|
+
const vertices = mesh.geometry.getAttribute('position').count;
|
|
164
|
+
return total + Math.floor((index?.count ?? vertices) / 3);
|
|
165
|
+
}, 0);
|
|
166
|
+
const config = {
|
|
81
167
|
cs: params?.cellSize ?? d.cellSize,
|
|
82
168
|
ch: params?.cellHeight ?? d.cellHeight,
|
|
83
169
|
walkableSlopeAngle: params?.walkableSlopeAngle ?? d.walkableSlopeAngle,
|
|
@@ -92,7 +178,20 @@ export class NavMeshManager {
|
|
|
92
178
|
maxVertsPerPoly: 6,
|
|
93
179
|
detailSampleDist: 6,
|
|
94
180
|
detailSampleMaxError: 1,
|
|
95
|
-
}
|
|
181
|
+
};
|
|
182
|
+
// A solo heightfield allocates for the whole scene at once. Large imported Unity scenes can
|
|
183
|
+
// exceed Recast's WASM address space during rasterization even though every individual mesh
|
|
184
|
+
// is ordinary-sized. Tiled generation is the library's native large-world path and keeps the
|
|
185
|
+
// same NavMesh result/API while bounding each rasterization allocation.
|
|
186
|
+
let result: ReturnType<typeof threeToSoloNavMesh> | ReturnType<typeof threeToTiledNavMesh>;
|
|
187
|
+
try {
|
|
188
|
+
result =
|
|
189
|
+
params?.tiled === true || input.meshes.length > 256 || triangleCount > 10_000
|
|
190
|
+
? threeToTiledNavMesh(input.meshes, { ...config, tileSize: params?.tileSize ?? 256 })
|
|
191
|
+
: threeToSoloNavMesh(input.meshes, config);
|
|
192
|
+
} finally {
|
|
193
|
+
for (const geometry of input.temporaryGeometries) geometry.dispose();
|
|
194
|
+
}
|
|
96
195
|
|
|
97
196
|
if (!result.success || !result.navMesh) return false;
|
|
98
197
|
|