lecodes-gl-lite 2.0.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/README.md +27 -0
- package/package.json +33 -0
- package/src/assets/decoders/README.md +13 -0
- package/src/assets/decoders/basis_transcoder.js +19 -0
- package/src/assets/decoders/basis_transcoder.wasm +0 -0
- package/src/assets/decoders/draco_decoder.wasm +0 -0
- package/src/assets/decoders/draco_wasm_wrapper.js +117 -0
- package/src/assets/neutral_ibl128.ktx +0 -0
- package/src/core/animBridge.ts +51 -0
- package/src/core/boneNames.ts +22 -0
- package/src/core/frame.ts +230 -0
- package/src/core/glbStructure.ts +100 -0
- package/src/core/headlessBridge.ts +92 -0
- package/src/core/mapView.ts +195 -0
- package/src/core/math3d.ts +275 -0
- package/src/core/particleBridge.ts +154 -0
- package/src/core/physicsBridge.ts +305 -0
- package/src/core/recording.ts +59 -0
- package/src/core/sceneCore.ts +833 -0
- package/src/core/serialize.ts +144 -0
- package/src/gen/unsupported.gen.ts +212 -0
- package/src/index.ts +5 -0
- package/src/vendor/creator-anim/dist/creatorAnim.d.ts +123 -0
- package/src/vendor/creator-anim/dist/creatorAnim.js +14 -0
- package/src/vendor/creator-anim/js/index.ts +656 -0
- package/src/vendor/creator-nav/dist/creatorNav.d.ts +56 -0
- package/src/vendor/creator-nav/dist/creatorNav.js +14 -0
- package/src/vendor/creator-nav/js/index.ts +400 -0
- package/src/vendor/creator-particles/dist/creatorParticles.d.ts +43 -0
- package/src/vendor/creator-particles/dist/creatorParticles.js +14 -0
- package/src/vendor/creator-particles/js/index.ts +137 -0
- package/src/vendor/creator-physics/dist/creatorPhysics.d.ts +107 -0
- package/src/vendor/creator-physics/dist/creatorPhysics.js +14 -0
- package/src/vendor/creator-physics/js/index.ts +515 -0
- package/src/vite-env.d.ts +1 -0
- package/src/webgl/batching.ts +453 -0
- package/src/webgl/createViewerLite.ts +1627 -0
- package/src/webgl/customMaterial.ts +228 -0
- package/src/webgl/decoders/basis.ts +123 -0
- package/src/webgl/decoders/draco.ts +110 -0
- package/src/webgl/decoders/index.ts +68 -0
- package/src/webgl/decoders/worker.ts +45 -0
- package/src/webgl/editor.ts +1297 -0
- package/src/webgl/glb.ts +593 -0
- package/src/webgl/ibl.ts +137 -0
- package/src/webgl/shaders.ts +779 -0
|
@@ -0,0 +1,400 @@
|
|
|
1
|
+
// creator-nav's TypeScript side over the wasm build (dist/creatorNav.js):
|
|
2
|
+
//
|
|
3
|
+
// • `createNavBridge(getBuffer)` — the `_creatorNav` object the WEB hosts (viewer, viewer-lite,
|
|
4
|
+
// lecodes-headless) hand to the app: the same method shapes as creator-pkg's
|
|
5
|
+
// creatorNavHandler.cpp, so the SDK never knows which host it runs on. The wasm loads lazily on
|
|
6
|
+
// `ready()`; a project without navigation never pays for it.
|
|
7
|
+
// • `NavBuilder` + `readNgeo` — the CLI's `lecodes navmesh bake`: a geometry dump (the desktop
|
|
8
|
+
// host's `physicsStaticGeometry`) → Recast → LNAV bytes.
|
|
9
|
+
//
|
|
10
|
+
// Parameter layouts mirror include/creator-nav/creator-nav.h (CNAV_BUILD_* / CNAV_AGENT_* /
|
|
11
|
+
// CNAV_READ_*) — keep the three in step.
|
|
12
|
+
|
|
13
|
+
import createCreatorNav, { type CreatorNavModule } from "../dist/creatorNav.js"
|
|
14
|
+
|
|
15
|
+
export const BUILD = {
|
|
16
|
+
CELL_SIZE: 0, CELL_HEIGHT: 1, AGENT_RADIUS: 2, AGENT_HEIGHT: 3, AGENT_MAX_CLIMB: 4, AGENT_MAX_SLOPE: 5,
|
|
17
|
+
TILE_SIZE: 6, REGION_MIN_SIZE: 7, REGION_MERGE_SIZE: 8, EDGE_MAX_LEN: 9, EDGE_MAX_ERROR: 10,
|
|
18
|
+
VERTS_PER_POLY: 11, DETAIL_SAMPLE_DIST: 12, DETAIL_MAX_ERROR: 13, FILTER_LOW_HANGING: 14,
|
|
19
|
+
FILTER_LEDGES: 15, FILTER_LOW_HEIGHT: 16, COUNT: 17,
|
|
20
|
+
} as const
|
|
21
|
+
|
|
22
|
+
export const AGENT = {
|
|
23
|
+
RADIUS: 0, HEIGHT: 1, MAX_SPEED: 2, MAX_ACCELERATION: 3, COLLISION_QUERY_RANGE: 4, PATH_OPTIMIZATION_RANGE: 5,
|
|
24
|
+
SEPARATION_WEIGHT: 6, AVOIDANCE: 7, INCLUDE_FLAGS: 8, EXCLUDE_FLAGS: 9, ANTICIPATE_TURNS: 10,
|
|
25
|
+
OPTIMIZE_VIS: 11, OPTIMIZE_TOPO: 12, COUNT: 13,
|
|
26
|
+
} as const
|
|
27
|
+
|
|
28
|
+
export const READ = {
|
|
29
|
+
POS_X: 0, POS_Y: 1, POS_Z: 2, VEL_X: 3, VEL_Y: 4, VEL_Z: 5, DVEL_X: 6, DVEL_Y: 7, DVEL_Z: 8,
|
|
30
|
+
STATE: 9, TARGET_STATE: 10, REMAINING: 11, PARTIAL: 12, STRIDE: 13,
|
|
31
|
+
} as const
|
|
32
|
+
|
|
33
|
+
export const AREA_UNWALKABLE = 255
|
|
34
|
+
export const FLAGS_ALL = 0xffff
|
|
35
|
+
|
|
36
|
+
export type BuildParams = Partial<{
|
|
37
|
+
cellSize: number, cellHeight: number, agentRadius: number, agentHeight: number, agentMaxClimb: number,
|
|
38
|
+
agentMaxSlope: number, tileSize: number, regionMinSize: number, regionMergeSize: number, edgeMaxLen: number,
|
|
39
|
+
edgeMaxError: number, vertsPerPoly: number, detailSampleDist: number, detailMaxError: number,
|
|
40
|
+
}>
|
|
41
|
+
|
|
42
|
+
const BUILD_KEYS: Record<keyof Required<BuildParams>, number> = {
|
|
43
|
+
cellSize: BUILD.CELL_SIZE, cellHeight: BUILD.CELL_HEIGHT, agentRadius: BUILD.AGENT_RADIUS, agentHeight: BUILD.AGENT_HEIGHT,
|
|
44
|
+
agentMaxClimb: BUILD.AGENT_MAX_CLIMB, agentMaxSlope: BUILD.AGENT_MAX_SLOPE, tileSize: BUILD.TILE_SIZE,
|
|
45
|
+
regionMinSize: BUILD.REGION_MIN_SIZE, regionMergeSize: BUILD.REGION_MERGE_SIZE, edgeMaxLen: BUILD.EDGE_MAX_LEN,
|
|
46
|
+
edgeMaxError: BUILD.EDGE_MAX_ERROR, vertsPerPoly: BUILD.VERTS_PER_POLY, detailSampleDist: BUILD.DETAIL_SAMPLE_DIST,
|
|
47
|
+
detailMaxError: BUILD.DETAIL_MAX_ERROR,
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
let modulePromise: Promise<CreatorNavModule> | null = null
|
|
51
|
+
/** The wasm module, loaded once per process (lazy). */
|
|
52
|
+
export const loadCreatorNav = (): Promise<CreatorNavModule> => (modulePromise ??= createCreatorNav())
|
|
53
|
+
|
|
54
|
+
// ---- heap helpers --------------------------------------------------------------------------------
|
|
55
|
+
|
|
56
|
+
const withF32 = <T>(mod: CreatorNavModule, values: ArrayLike<number>, fn: (ptr: number) => T): T => {
|
|
57
|
+
const ptr = mod._malloc(Math.max(4, values.length * 4))
|
|
58
|
+
mod.HEAPF32.set(values, ptr >> 2)
|
|
59
|
+
try { return fn(ptr) } finally { mod._free(ptr) }
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const withString = <T>(mod: CreatorNavModule, s: string | null, fn: (ptr: number) => T): T => {
|
|
63
|
+
if (s === null) return fn(0)
|
|
64
|
+
const n = mod.lengthBytesUTF8(s) + 1
|
|
65
|
+
const ptr = mod._malloc(n)
|
|
66
|
+
mod.stringToUTF8(s, ptr, n)
|
|
67
|
+
try { return fn(ptr) } finally { mod._free(ptr) }
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// ---- NGEO — the host's geometry dump -------------------------------------------------------------
|
|
71
|
+
|
|
72
|
+
export type NgeoData = {
|
|
73
|
+
/** 9 floats per triangle (three xyz), world space */
|
|
74
|
+
triangles: Float32Array
|
|
75
|
+
/** one area byte per triangle */
|
|
76
|
+
areas: Uint8Array
|
|
77
|
+
triangleCount: number
|
|
78
|
+
bodyCount: number
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** Parse a `physicsStaticGeometry` dump ('NGEO' u32 · version · triangleCount · bodyCount · records). */
|
|
82
|
+
export const readNgeo = (bytes: Uint8Array): NgeoData => {
|
|
83
|
+
const dv = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength)
|
|
84
|
+
if (bytes.byteLength < 16 || dv.getUint32(0, true) !== 0x4f45474e) throw new Error("not an NGEO geometry dump")
|
|
85
|
+
const version = dv.getUint32(4, true)
|
|
86
|
+
if (version !== 1) throw new Error(`NGEO version ${version}, expected 1`)
|
|
87
|
+
const triangleCount = dv.getUint32(8, true)
|
|
88
|
+
const bodyCount = dv.getUint32(12, true)
|
|
89
|
+
const triangles = new Float32Array(triangleCount * 9)
|
|
90
|
+
const areas = new Uint8Array(triangleCount)
|
|
91
|
+
let at = 16
|
|
92
|
+
for (let i = 0; i < triangleCount; i++) {
|
|
93
|
+
for (let k = 0; k < 9; k++) { triangles[i * 9 + k] = dv.getFloat32(at, true); at += 4 }
|
|
94
|
+
areas[i] = dv.getUint32(at, true) & 0xff
|
|
95
|
+
at += 4
|
|
96
|
+
}
|
|
97
|
+
return { triangles, areas, triangleCount, bodyCount }
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// ---- NavBuilder — Recast over the wasm ------------------------------------------------------------
|
|
101
|
+
|
|
102
|
+
export type BuildResult = {
|
|
103
|
+
data: Uint8Array
|
|
104
|
+
tiles: number
|
|
105
|
+
polys: number
|
|
106
|
+
bounds: { min: [number, number, number], max: [number, number, number] }
|
|
107
|
+
/** seconds */
|
|
108
|
+
time: number
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export class NavBuilder {
|
|
112
|
+
private handle: number
|
|
113
|
+
|
|
114
|
+
constructor(private readonly mod: CreatorNavModule, params: BuildParams = {}) {
|
|
115
|
+
const p = new Float32Array(BUILD.COUNT)
|
|
116
|
+
withF32(mod, p, (ptr) => { mod._cnavBuildDefaults(ptr); p.set(mod.HEAPF32.subarray(ptr >> 2, (ptr >> 2) + BUILD.COUNT)) })
|
|
117
|
+
for (const [key, value] of Object.entries(params)) {
|
|
118
|
+
if (value === undefined) continue
|
|
119
|
+
const idx = BUILD_KEYS[key as keyof BuildParams]
|
|
120
|
+
if (idx === undefined) throw new Error(`NavBuilder: unknown build parameter "${key}"`)
|
|
121
|
+
p[idx] = value
|
|
122
|
+
}
|
|
123
|
+
this.handle = withF32(mod, p, (ptr) => mod._cnavBuildCreate(ptr))
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/** Indexed triangles; `area` for every triangle unless `areas` (one byte per triangle) is given. */
|
|
127
|
+
addTriangles(xyz: Float32Array, indices: Int32Array | Uint32Array, area = 0, areas?: Uint8Array): void {
|
|
128
|
+
const mod = this.mod
|
|
129
|
+
const vp = mod._malloc(xyz.byteLength)
|
|
130
|
+
mod.HEAPF32.set(xyz, vp >> 2)
|
|
131
|
+
const ip = mod._malloc(indices.byteLength)
|
|
132
|
+
mod.HEAP32.set(indices as unknown as Int32Array, ip >> 2)
|
|
133
|
+
let ap = 0
|
|
134
|
+
if (areas) { ap = mod._malloc(areas.byteLength); mod.HEAPU8.set(areas, ap) }
|
|
135
|
+
const ok = mod._cnavBuildAddTriangles(this.handle, vp, xyz.length / 3, ip, indices.length / 3, area, ap)
|
|
136
|
+
mod._free(vp); mod._free(ip); if (ap) mod._free(ap)
|
|
137
|
+
if (!ok) throw new Error(`NavBuilder.addTriangles: ${this.lastError()}`)
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/** An unindexed soup (9 floats per triangle) — the NGEO dump's shape. */
|
|
141
|
+
addSoup(triangles: Float32Array, areas?: Uint8Array): void {
|
|
142
|
+
const n = triangles.length / 3
|
|
143
|
+
const indices = new Int32Array(n)
|
|
144
|
+
for (let i = 0; i < n; i++) indices[i] = i
|
|
145
|
+
this.addTriangles(triangles, indices, 0, areas)
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
addVolume(hullXZ: ArrayLike<number>, minY: number, maxY: number, area: number): void {
|
|
149
|
+
const ok = withF32(this.mod, hullXZ, (ptr) => this.mod._cnavBuildAddVolume(this.handle, ptr, hullXZ.length / 2, minY, maxY, area))
|
|
150
|
+
if (!ok) throw new Error(`NavBuilder.addVolume: ${this.lastError()}`)
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
addLink(a: ArrayLike<number>, b: ArrayLike<number>, radius: number, bidirectional = true, area = 0, userId = 0): void {
|
|
154
|
+
const ok = withF32(this.mod, [ a[0]!, a[1]!, a[2]!, b[0]!, b[1]!, b[2]! ], (ptr) =>
|
|
155
|
+
this.mod._cnavBuildAddLink(this.handle, ptr, ptr + 12, radius, bidirectional ? 1 : 0, area, userId))
|
|
156
|
+
if (!ok) throw new Error(`NavBuilder.addLink: ${this.lastError()}`)
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
run(meta: object | null = null, onProgress?: (tile: number, tileCount: number, message: string | null) => void): BuildResult {
|
|
160
|
+
const mod = this.mod
|
|
161
|
+
const t0 = performance.now()
|
|
162
|
+
let cb = 0
|
|
163
|
+
if (onProgress) {
|
|
164
|
+
cb = mod.addFunction((tile: number, count: number, msg: number) => {
|
|
165
|
+
onProgress(tile, count, msg ? mod.UTF8ToString(msg) : null)
|
|
166
|
+
}, "viiii")
|
|
167
|
+
}
|
|
168
|
+
const ok = withString(mod, meta ? JSON.stringify(meta) : null, (mp) => mod._cnavBuildRun(this.handle, mp, cb, 0))
|
|
169
|
+
if (cb) mod.removeFunction(cb)
|
|
170
|
+
if (!ok) throw new Error(`navmesh build failed: ${this.lastError()}`)
|
|
171
|
+
const sizePtr = mod._malloc(4)
|
|
172
|
+
const dataPtr = mod._cnavBuildData(this.handle, sizePtr)
|
|
173
|
+
const size = mod.HEAPU32[sizePtr >> 2]!
|
|
174
|
+
mod._free(sizePtr)
|
|
175
|
+
const data = new Uint8Array(mod.HEAPU8.subarray(dataPtr, dataPtr + size)) // a copy
|
|
176
|
+
const bp = mod._malloc(24)
|
|
177
|
+
mod._cnavBuildBounds(this.handle, bp, bp + 12)
|
|
178
|
+
const b = mod.HEAPF32.subarray(bp >> 2, (bp >> 2) + 6)
|
|
179
|
+
const bounds = { min: [ b[0]!, b[1]!, b[2]! ] as [number, number, number], max: [ b[3]!, b[4]!, b[5]! ] as [number, number, number] }
|
|
180
|
+
mod._free(bp)
|
|
181
|
+
return { data, tiles: mod._cnavBuildTileCount(this.handle), polys: mod._cnavBuildPolyCount(this.handle), bounds, time: (performance.now() - t0) / 1000 }
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
lastError(): string { return this.mod.UTF8ToString(this.mod._cnavLastError()) }
|
|
185
|
+
|
|
186
|
+
destroy(): void {
|
|
187
|
+
if (this.handle) { this.mod._cnavBuildDestroy(this.handle); this.handle = 0 }
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/** The polygon mesh of an LNAV file as a triangle soup (xyz per vertex) — the CLI's `--obj` debug dump. */
|
|
192
|
+
export const navMeshTriangles = (mod: CreatorNavModule, data: Uint8Array): Float32Array => {
|
|
193
|
+
const ptr = mod._malloc(data.byteLength)
|
|
194
|
+
mod.HEAPU8.set(data, ptr)
|
|
195
|
+
const mesh = mod._cnavMeshLoad(ptr, data.byteLength)
|
|
196
|
+
mod._free(ptr)
|
|
197
|
+
if (!mesh) throw new Error(`navmesh load failed: ${mod.UTF8ToString(mod._cnavLastError())}`)
|
|
198
|
+
const n = mod._cnavDebugTriangles(mesh, 0, 0)
|
|
199
|
+
const out = new Float32Array(n * 3)
|
|
200
|
+
if (n > 0) {
|
|
201
|
+
const op = mod._malloc(n * 12)
|
|
202
|
+
mod._cnavDebugTriangles(mesh, op, n)
|
|
203
|
+
out.set(mod.HEAPF32.subarray(op >> 2, (op >> 2) + n * 3))
|
|
204
|
+
mod._free(op)
|
|
205
|
+
}
|
|
206
|
+
mod._cnavMeshDestroy(mesh)
|
|
207
|
+
return out
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// ---- the `_creatorNav` bridge for the web hosts ---------------------------------------------------
|
|
211
|
+
|
|
212
|
+
export type NavBridge = {
|
|
213
|
+
/** Web hosts only: load the wasm. Native hosts have no such method (the SDK awaits it when present). */
|
|
214
|
+
ready(): Promise<void>
|
|
215
|
+
lastError(): string
|
|
216
|
+
load(systemId: number): number
|
|
217
|
+
unload(meshId: number): void
|
|
218
|
+
header(meshId: number): string
|
|
219
|
+
polyCount(meshId: number): number
|
|
220
|
+
setAreaCost(meshId: number, area: number, cost: number): void
|
|
221
|
+
findPath(meshId: number, fx: number, fy: number, fz: number, tx: number, ty: number, tz: number, ex: number, ey: number, ez: number, include: number, exclude: number, out: Float32Array): number
|
|
222
|
+
nearest(meshId: number, x: number, y: number, z: number, ex: number, ey: number, ez: number, include: number, exclude: number, out: Float32Array): boolean
|
|
223
|
+
raycast(meshId: number, fx: number, fy: number, fz: number, tx: number, ty: number, tz: number, include: number, exclude: number, out: Float32Array): boolean
|
|
224
|
+
randomPoint(meshId: number, cx: number, cy: number, cz: number, radius: number, seed: number, include: number, exclude: number, out: Float32Array): boolean
|
|
225
|
+
debugTriangles(meshId: number): Float32Array
|
|
226
|
+
crowdCreate(meshId: number, maxAgents: number, maxRadius: number): number
|
|
227
|
+
crowdDestroy(crowdId: number): void
|
|
228
|
+
crowdAdd(crowdId: number, x: number, y: number, z: number, params: Float32Array): number
|
|
229
|
+
crowdRemove(crowdId: number, agent: number): void
|
|
230
|
+
crowdSetParams(crowdId: number, agent: number, params: Float32Array): void
|
|
231
|
+
crowdSetTarget(crowdId: number, agent: number, x: number, y: number, z: number): boolean
|
|
232
|
+
crowdSetVelocity(crowdId: number, agent: number, vx: number, vy: number, vz: number): void
|
|
233
|
+
crowdResetTarget(crowdId: number, agent: number): void
|
|
234
|
+
crowdWarp(crowdId: number, agent: number, x: number, y: number, z: number): boolean
|
|
235
|
+
crowdUpdate(crowdId: number, dt: number): void
|
|
236
|
+
crowdRead(crowdId: number, out: Float32Array): number
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/** The web hosts' `_creatorNav`. `getBuffer` resolves a fetch system id to its bytes (the host's
|
|
240
|
+
* fetch store — viewer-lite's `getBuffer`, the renderer's `services.getBuffer`). */
|
|
241
|
+
export const createNavBridge = (getBuffer: (systemId: number) => ArrayBuffer | Uint8Array | null | undefined): NavBridge => {
|
|
242
|
+
let mod: CreatorNavModule | null = null
|
|
243
|
+
const meshes = new Map<number, number>() // meshId → cnavMesh*
|
|
244
|
+
const crowds = new Map<number, { ptr: number, max: number, buf: number }>()
|
|
245
|
+
let nextId = 1
|
|
246
|
+
// scratch heap for small in/out arrays (48 floats: enough for every fixed-size argument set)
|
|
247
|
+
let scratch = 0
|
|
248
|
+
const s = (): number => scratch || (scratch = mod!._malloc(48 * 4))
|
|
249
|
+
const sf = (offset: number, values: ArrayLike<number>): number => {
|
|
250
|
+
const ptr = s() + offset * 4
|
|
251
|
+
mod!.HEAPF32.set(values, ptr >> 2)
|
|
252
|
+
return ptr
|
|
253
|
+
}
|
|
254
|
+
const readF = (ptr: number, n: number, out: Float32Array): void => { out.set(mod!.HEAPF32.subarray(ptr >> 2, (ptr >> 2) + n)) }
|
|
255
|
+
const need = (): CreatorNavModule => {
|
|
256
|
+
if (!mod) throw new Error("_creatorNav: call ready() before loading a navmesh")
|
|
257
|
+
return mod
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
return {
|
|
261
|
+
async ready() { mod = await loadCreatorNav() },
|
|
262
|
+
lastError() { return mod ? mod.UTF8ToString(mod._cnavLastError()) : "wasm not loaded" },
|
|
263
|
+
|
|
264
|
+
load(systemId) {
|
|
265
|
+
const m = need()
|
|
266
|
+
const buf = getBuffer(systemId)
|
|
267
|
+
if (!buf) return 0
|
|
268
|
+
const bytes = buf instanceof Uint8Array ? buf : new Uint8Array(buf)
|
|
269
|
+
const ptr = m._malloc(bytes.byteLength)
|
|
270
|
+
m.HEAPU8.set(bytes, ptr)
|
|
271
|
+
const mesh = m._cnavMeshLoad(ptr, bytes.byteLength)
|
|
272
|
+
m._free(ptr)
|
|
273
|
+
if (!mesh) return 0
|
|
274
|
+
const id = nextId++
|
|
275
|
+
meshes.set(id, mesh)
|
|
276
|
+
return id
|
|
277
|
+
},
|
|
278
|
+
unload(meshId) {
|
|
279
|
+
const mesh = meshes.get(meshId)
|
|
280
|
+
if (!mesh) return
|
|
281
|
+
for (const [ cid, c ] of crowds) { need()._cnavCrowdDestroy(c.ptr); need()._free(c.buf); crowds.delete(cid) }
|
|
282
|
+
need()._cnavMeshDestroy(mesh)
|
|
283
|
+
meshes.delete(meshId)
|
|
284
|
+
},
|
|
285
|
+
header(meshId) {
|
|
286
|
+
const mesh = meshes.get(meshId)
|
|
287
|
+
return mesh ? need().UTF8ToString(need()._cnavMeshHeader(mesh)) : ""
|
|
288
|
+
},
|
|
289
|
+
polyCount(meshId) {
|
|
290
|
+
const mesh = meshes.get(meshId)
|
|
291
|
+
return mesh ? need()._cnavMeshPolyCount(mesh) : 0
|
|
292
|
+
},
|
|
293
|
+
setAreaCost(meshId, area, cost) {
|
|
294
|
+
const mesh = meshes.get(meshId)
|
|
295
|
+
if (mesh) need()._cnavMeshSetAreaCost(mesh, area, cost)
|
|
296
|
+
},
|
|
297
|
+
|
|
298
|
+
findPath(meshId, fx, fy, fz, tx, ty, tz, ex, ey, ez, include, exclude, out) {
|
|
299
|
+
const mesh = meshes.get(meshId)
|
|
300
|
+
if (!mesh) return 0
|
|
301
|
+
const m = need()
|
|
302
|
+
const n = Math.floor(out.length / 3)
|
|
303
|
+
const from = sf(0, [ fx, fy, fz ]), to = sf(3, [ tx, ty, tz ]), ext = ex > 0 ? sf(6, [ ex, ey, ez ]) : 0
|
|
304
|
+
const partial = s() + 9 * 4
|
|
305
|
+
m.HEAP32[partial >> 2] = 0
|
|
306
|
+
const outPtr = m._malloc(Math.max(1, n) * 12)
|
|
307
|
+
const count = m._cnavFindPath(mesh, from, to, ext, include, exclude, outPtr, n, partial)
|
|
308
|
+
if (count > 0) readF(outPtr, count * 3, out)
|
|
309
|
+
const isPartial = m.HEAPU8[partial] !== 0
|
|
310
|
+
m._free(outPtr)
|
|
311
|
+
return isPartial ? -count : count
|
|
312
|
+
},
|
|
313
|
+
nearest(meshId, x, y, z, ex, ey, ez, include, exclude, out) {
|
|
314
|
+
const mesh = meshes.get(meshId)
|
|
315
|
+
if (!mesh) return false
|
|
316
|
+
const m = need()
|
|
317
|
+
const p = sf(0, [ x, y, z ]), ext = ex > 0 ? sf(3, [ ex, ey, ez ]) : 0, o = s() + 6 * 4
|
|
318
|
+
const ok = m._cnavNearest(mesh, p, ext, include, exclude, o) !== 0
|
|
319
|
+
if (ok) readF(o, 3, out)
|
|
320
|
+
return ok
|
|
321
|
+
},
|
|
322
|
+
raycast(meshId, fx, fy, fz, tx, ty, tz, include, exclude, out) {
|
|
323
|
+
const mesh = meshes.get(meshId)
|
|
324
|
+
if (!mesh) return true
|
|
325
|
+
const m = need()
|
|
326
|
+
const from = sf(0, [ fx, fy, fz ]), to = sf(3, [ tx, ty, tz ]), hit = s() + 6 * 4, nrm = s() + 9 * 4
|
|
327
|
+
const blocked = m._cnavRaycast(mesh, from, to, include, exclude, hit, nrm) !== 0
|
|
328
|
+
readF(hit, 6, out)
|
|
329
|
+
return blocked
|
|
330
|
+
},
|
|
331
|
+
randomPoint(meshId, cx, cy, cz, radius, seed, include, exclude, out) {
|
|
332
|
+
const mesh = meshes.get(meshId)
|
|
333
|
+
if (!mesh) return false
|
|
334
|
+
const m = need()
|
|
335
|
+
const c = radius > 0 ? sf(0, [ cx, cy, cz ]) : 0, o = s() + 3 * 4
|
|
336
|
+
const ok = m._cnavRandomPoint(mesh, c, radius, seed >>> 0, include, exclude, o) !== 0
|
|
337
|
+
if (ok) readF(o, 3, out)
|
|
338
|
+
return ok
|
|
339
|
+
},
|
|
340
|
+
debugTriangles(meshId) {
|
|
341
|
+
const mesh = meshes.get(meshId)
|
|
342
|
+
if (!mesh) return new Float32Array(0)
|
|
343
|
+
const m = need()
|
|
344
|
+
const n = m._cnavDebugTriangles(mesh, 0, 0)
|
|
345
|
+
const ptr = m._malloc(Math.max(1, n) * 12)
|
|
346
|
+
m._cnavDebugTriangles(mesh, ptr, n)
|
|
347
|
+
const out = new Float32Array(n * 3)
|
|
348
|
+
readF(ptr, n * 3, out)
|
|
349
|
+
m._free(ptr)
|
|
350
|
+
return out
|
|
351
|
+
},
|
|
352
|
+
|
|
353
|
+
crowdCreate(meshId, maxAgents, maxRadius) {
|
|
354
|
+
const mesh = meshes.get(meshId)
|
|
355
|
+
if (!mesh) return 0
|
|
356
|
+
const m = need()
|
|
357
|
+
const ptr = m._cnavCrowdCreate(mesh, maxAgents, maxRadius)
|
|
358
|
+
if (!ptr) return 0
|
|
359
|
+
const id = nextId++
|
|
360
|
+
crowds.set(id, { ptr, max: maxAgents, buf: m._malloc(maxAgents * READ.STRIDE * 4) })
|
|
361
|
+
return id
|
|
362
|
+
},
|
|
363
|
+
crowdDestroy(crowdId) {
|
|
364
|
+
const c = crowds.get(crowdId)
|
|
365
|
+
if (!c) return
|
|
366
|
+
need()._cnavCrowdDestroy(c.ptr)
|
|
367
|
+
need()._free(c.buf)
|
|
368
|
+
crowds.delete(crowdId)
|
|
369
|
+
},
|
|
370
|
+
crowdAdd(crowdId, x, y, z, params) {
|
|
371
|
+
const c = crowds.get(crowdId)
|
|
372
|
+
if (!c) return -1
|
|
373
|
+
const pos = sf(0, [ x, y, z ]), p = sf(3, params)
|
|
374
|
+
return need()._cnavCrowdAdd(c.ptr, pos, params.length >= AGENT.COUNT ? p : 0)
|
|
375
|
+
},
|
|
376
|
+
crowdRemove(crowdId, agent) { const c = crowds.get(crowdId); if (c) need()._cnavCrowdRemove(c.ptr, agent) },
|
|
377
|
+
crowdSetParams(crowdId, agent, params) {
|
|
378
|
+
const c = crowds.get(crowdId)
|
|
379
|
+
if (c && params.length >= AGENT.COUNT) need()._cnavCrowdSetParams(c.ptr, agent, sf(0, params))
|
|
380
|
+
},
|
|
381
|
+
crowdSetTarget(crowdId, agent, x, y, z) {
|
|
382
|
+
const c = crowds.get(crowdId)
|
|
383
|
+
return c ? need()._cnavCrowdSetTarget(c.ptr, agent, sf(0, [ x, y, z ])) !== 0 : false
|
|
384
|
+
},
|
|
385
|
+
crowdSetVelocity(crowdId, agent, vx, vy, vz) { const c = crowds.get(crowdId); if (c) need()._cnavCrowdSetVelocity(c.ptr, agent, sf(0, [ vx, vy, vz ])) },
|
|
386
|
+
crowdResetTarget(crowdId, agent) { const c = crowds.get(crowdId); if (c) need()._cnavCrowdResetTarget(c.ptr, agent) },
|
|
387
|
+
crowdWarp(crowdId, agent, x, y, z) {
|
|
388
|
+
const c = crowds.get(crowdId)
|
|
389
|
+
return c ? need()._cnavCrowdWarp(c.ptr, agent, sf(0, [ x, y, z ])) !== 0 : false
|
|
390
|
+
},
|
|
391
|
+
crowdUpdate(crowdId, dt) { const c = crowds.get(crowdId); if (c) need()._cnavCrowdUpdate(c.ptr, dt) },
|
|
392
|
+
crowdRead(crowdId, out) {
|
|
393
|
+
const c = crowds.get(crowdId)
|
|
394
|
+
if (!c || out.length < c.max * READ.STRIDE) return 0
|
|
395
|
+
const n = need()._cnavCrowdRead(c.ptr, c.buf)
|
|
396
|
+
readF(c.buf, c.max * READ.STRIDE, out)
|
|
397
|
+
return n
|
|
398
|
+
},
|
|
399
|
+
}
|
|
400
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// Type surface of the single-file Emscripten module build-wasm.sh emits next to this file.
|
|
2
|
+
// The exported C functions take heap pointers; use js/index.ts rather than this directly.
|
|
3
|
+
// Keep in step with the CPART_EXPORTS list in CMakeLists.txt and include/creator-particles/creator-particles.h.
|
|
4
|
+
|
|
5
|
+
export interface CreatorParticlesModule {
|
|
6
|
+
HEAPU8: Uint8Array
|
|
7
|
+
HEAP32: Int32Array
|
|
8
|
+
HEAPU32: Uint32Array
|
|
9
|
+
HEAPF32: Float32Array
|
|
10
|
+
_malloc(bytes: number): number
|
|
11
|
+
_free(ptr: number): void
|
|
12
|
+
|
|
13
|
+
_cpartCreate(key: number, maxParticles: number): number
|
|
14
|
+
_cpartDestroy(key: number): void
|
|
15
|
+
_cpartExists(key: number): number
|
|
16
|
+
_cpartReset(): void
|
|
17
|
+
_cpartSetConfig(key: number, data: number, length: number): void
|
|
18
|
+
_cpartSpawn(key: number, count: number): void
|
|
19
|
+
_cpartSetVisible(key: number, visible: number): void
|
|
20
|
+
_cpartMode(key: number): number
|
|
21
|
+
_cpartOrder(key: number): number
|
|
22
|
+
_cpartCapacity(key: number): number
|
|
23
|
+
_cpartGeometryVersion(key: number): number
|
|
24
|
+
_cpartMeshVertexCount(key: number): number
|
|
25
|
+
_cpartMeshIndexCount(key: number): number
|
|
26
|
+
_cpartIndexPatternCount(key: number): number
|
|
27
|
+
_cpartWriteIndexPattern(key: number, out: number): void
|
|
28
|
+
_cpartUpdate(key: number, dt: number, world16: number, cameraPos3: number): void
|
|
29
|
+
_cpartAlive(key: number): number
|
|
30
|
+
_cpartVertexCount(key: number): number
|
|
31
|
+
_cpartVertexData(key: number): number
|
|
32
|
+
_cpartVertexStride(key: number): number
|
|
33
|
+
_cpartIndexCount(key: number): number
|
|
34
|
+
_cpartNoiseCreate(): number
|
|
35
|
+
_cpartNoiseSetFrequency(id: number, frequency: number): void
|
|
36
|
+
_cpartNoiseSetOctaves(id: number, octaves: number): void
|
|
37
|
+
_cpartNoiseSetLacunarity(id: number, lacunarity: number): void
|
|
38
|
+
_cpartNoiseSetGain(id: number, gain: number): void
|
|
39
|
+
_cpartNoise2D(id: number, x: number, y: number): number
|
|
40
|
+
_cpartNoise3D(id: number, x: number, y: number, z: number): number
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export default function createCreatorParticles(): Promise<CreatorParticlesModule>
|