lecodes-cli 0.19.0 → 0.20.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.
Files changed (270) hide show
  1. package/README.md +1 -0
  2. package/dist/index.js +1069 -396
  3. package/package.json +14 -13
  4. package/runtime/scene-harness.json +1 -1
  5. package/runtime/sdk-types.json +1 -1
  6. package/runtime/web/assets/Roboto-Variable-DHm9jpd-.woff2 +0 -0
  7. package/runtime/web/assets/__vite-browser-external-BIHI7g3E.js +1 -0
  8. package/runtime/web/assets/basis-C64VHDVD.js +1 -0
  9. package/runtime/web/assets/basis_transcoder-VXdx5NbI.wasm +0 -0
  10. package/runtime/web/assets/createViewerLite-BvHobVhp.js +852 -0
  11. package/runtime/web/assets/draco-BiISTFcR.js +118 -0
  12. package/runtime/web/assets/draco_decoder-DsQ12WqX.wasm +0 -0
  13. package/runtime/web/assets/index-CfNCCBjN.js +2 -0
  14. package/runtime/web/assets/mapViewImpl-B2JcES8l.js +810 -0
  15. package/runtime/web/assets/maplibre-gl-worker-CJfwIrte.js +8 -0
  16. package/runtime/web/assets/neutral_ibl128-D-lzdkLN.ktx +0 -0
  17. package/runtime/web/assets/worker-Caf-yYEI.js +2 -0
  18. package/runtime/web/embed.html +32 -0
  19. package/runtime/web/embed.js +171 -0
  20. package/src/api.ts +302 -0
  21. package/src/browserAuth.ts +87 -0
  22. package/src/cmgenTool.ts +104 -0
  23. package/src/commands/app.ts +896 -0
  24. package/src/commands/appAndroid.ts +603 -0
  25. package/src/commands/appDesktop.ts +339 -0
  26. package/src/commands/appDesktopMac.ts +470 -0
  27. package/src/commands/appIcon.ts +187 -0
  28. package/src/commands/appShared.ts +542 -0
  29. package/src/commands/appTemplates.ts +907 -0
  30. package/src/commands/appTemplatesAndroid.ts +599 -0
  31. package/src/commands/appTemplatesGradlew.ts +9 -0
  32. package/src/commands/assets.ts +28 -0
  33. package/src/commands/clone.ts +60 -0
  34. package/src/commands/compile.ts +253 -0
  35. package/src/commands/create.ts +59 -0
  36. package/src/commands/design.ts +863 -0
  37. package/src/commands/designTemplates.ts +11 -0
  38. package/src/commands/desktop.ts +127 -0
  39. package/src/commands/dev.ts +236 -0
  40. package/src/commands/diff.ts +59 -0
  41. package/src/commands/init.ts +191 -0
  42. package/src/commands/install.ts +198 -0
  43. package/src/commands/lightmap.ts +290 -0
  44. package/src/commands/link.ts +147 -0
  45. package/src/commands/login.ts +24 -0
  46. package/src/commands/navmesh.ts +225 -0
  47. package/src/commands/pn.ts +305 -0
  48. package/src/commands/projectTemplates.ts +130 -0
  49. package/src/commands/pull.ts +109 -0
  50. package/src/commands/push.ts +173 -0
  51. package/src/commands/render.ts +416 -0
  52. package/src/commands/scene.ts +148 -0
  53. package/src/commands/shaders.ts +157 -0
  54. package/src/commands/status.ts +29 -0
  55. package/src/commands/test.ts +371 -0
  56. package/src/commands/thumbs.ts +178 -0
  57. package/src/commands/types.ts +87 -0
  58. package/src/commands/update.ts +190 -0
  59. package/src/compile/assetIcons.ts +214 -0
  60. package/src/compile/collect.ts +46 -0
  61. package/src/compile/collectLocal.ts +37 -0
  62. package/src/compile/designCompile.ts +104 -0
  63. package/src/compile/fonts.ts +156 -0
  64. package/src/compile/headlessBundle.ts +129 -0
  65. package/src/compile/nativeStack.ts +21 -0
  66. package/src/compile/projectCompile.ts +109 -0
  67. package/src/compile/sceneCompile.ts +113 -0
  68. package/src/compile/screenEntry.ts +127 -0
  69. package/src/compile/shaders.ts +245 -0
  70. package/src/config.ts +42 -0
  71. package/src/designMeta.ts +35 -0
  72. package/src/desktopRenderer.ts +545 -0
  73. package/src/desktopScript.ts +276 -0
  74. package/src/dev/clientTemplates.ts +160 -0
  75. package/src/dev/devServer.ts +333 -0
  76. package/src/dev/webRunner.ts +22 -0
  77. package/src/dev/wsServer.ts +144 -0
  78. package/src/distRoot.ts +20 -0
  79. package/src/ignore.ts +163 -0
  80. package/src/index.ts +494 -0
  81. package/src/lecodes-3d-editor.d.ts +41 -0
  82. package/src/lecodes-assets.d.ts +7 -0
  83. package/src/lecodes-design.d.ts +191 -0
  84. package/src/lecodes-renderer.d.ts +131 -0
  85. package/src/localFiles.ts +144 -0
  86. package/src/manifest.ts +46 -0
  87. package/src/matcTool.ts +137 -0
  88. package/src/peers.ts +40 -0
  89. package/src/project.ts +61 -0
  90. package/src/projectEnv.ts +94 -0
  91. package/src/qrcode-terminal.d.ts +9 -0
  92. package/src/releases.ts +125 -0
  93. package/src/serverDiff.ts +78 -0
  94. package/src/textDiff.ts +103 -0
  95. package/src/types.ts +0 -0
  96. package/src/util.ts +146 -0
  97. package/runtime/sdk/animate/animate.ts +0 -238
  98. package/runtime/sdk/animate/bezier.ts +0 -138
  99. package/runtime/sdk/animate/easings.ts +0 -126
  100. package/runtime/sdk/canvas/Canvas.ts +0 -305
  101. package/runtime/sdk/compile/aspectMacro.ts +0 -86
  102. package/runtime/sdk/compile/assetIconMacro.ts +0 -384
  103. package/runtime/sdk/compile/assetMacro.ts +0 -146
  104. package/runtime/sdk/compile/assetName.ts +0 -50
  105. package/runtime/sdk/compile/bundler.ts +0 -287
  106. package/runtime/sdk/compile/compileProject.ts +0 -129
  107. package/runtime/sdk/compile/detectEntry.ts +0 -128
  108. package/runtime/sdk/compile/fontMacro.ts +0 -459
  109. package/runtime/sdk/compile/fontRegistry.ts +0 -78
  110. package/runtime/sdk/compile/header.ts +0 -67
  111. package/runtime/sdk/compile/index.ts +0 -100
  112. package/runtime/sdk/compile/libraryImports.ts +0 -52
  113. package/runtime/sdk/compile/liteMaterial.ts +0 -247
  114. package/runtime/sdk/compile/sceneEditor.ts +0 -88
  115. package/runtime/sdk/compile/serverSplit.ts +0 -233
  116. package/runtime/sdk/compile/serverTypes.ts +0 -227
  117. package/runtime/sdk/compile/sfnt.ts +0 -98
  118. package/runtime/sdk/compile/shaderSchema.ts +0 -202
  119. package/runtime/sdk/compile/shaderTargets.ts +0 -81
  120. package/runtime/sdk/compile/sourcemap.ts +0 -25
  121. package/runtime/sdk/core/Aspect.ts +0 -568
  122. package/runtime/sdk/core/InspectorUI.ts +0 -212
  123. package/runtime/sdk/core/StateMachine.ts +0 -308
  124. package/runtime/sdk/core/color.ts +0 -66
  125. package/runtime/sdk/core/compWrite.ts +0 -42
  126. package/runtime/sdk/core/events.ts +0 -38
  127. package/runtime/sdk/core/fields.ts +0 -120
  128. package/runtime/sdk/core/registry.ts +0 -23
  129. package/runtime/sdk/core/signals.ts +0 -277
  130. package/runtime/sdk/core/time.ts +0 -84
  131. package/runtime/sdk/g2/Camera2D.ts +0 -40
  132. package/runtime/sdk/g2/CharacterController2D.ts +0 -276
  133. package/runtime/sdk/g2/Node2D.ts +0 -267
  134. package/runtime/sdk/g2/OneWay2D.ts +0 -66
  135. package/runtime/sdk/g2/Physics2D.ts +0 -346
  136. package/runtime/sdk/g2/Scene2D.ts +0 -209
  137. package/runtime/sdk/g2/Shape2D.ts +0 -259
  138. package/runtime/sdk/g2/Sprite.ts +0 -89
  139. package/runtime/sdk/g2/SpriteAnimation.ts +0 -171
  140. package/runtime/sdk/g2/SpriteSheet.ts +0 -166
  141. package/runtime/sdk/g2/Texture2D.ts +0 -47
  142. package/runtime/sdk/g2/Tilemap.ts +0 -41
  143. package/runtime/sdk/g2/Tileset.ts +0 -71
  144. package/runtime/sdk/g2/Trigger2D.ts +0 -77
  145. package/runtime/sdk/g2/autotile.ts +0 -433
  146. package/runtime/sdk/g2/cells.ts +0 -91
  147. package/runtime/sdk/g2/defineScene2d.ts +0 -381
  148. package/runtime/sdk/g2/groups2d.ts +0 -106
  149. package/runtime/sdk/g2/loop.ts +0 -50
  150. package/runtime/sdk/g2/scenarios2d.ts +0 -69
  151. package/runtime/sdk/g2/touch.ts +0 -83
  152. package/runtime/sdk/gl/Camera.ts +0 -160
  153. package/runtime/sdk/gl/CameraPlace.ts +0 -52
  154. package/runtime/sdk/gl/CharacterController.ts +0 -238
  155. package/runtime/sdk/gl/Gearbox.ts +0 -212
  156. package/runtime/sdk/gl/Geometry.ts +0 -279
  157. package/runtime/sdk/gl/IK.ts +0 -193
  158. package/runtime/sdk/gl/InstancedMesh.ts +0 -132
  159. package/runtime/sdk/gl/Light.ts +0 -107
  160. package/runtime/sdk/gl/Lightmap.ts +0 -256
  161. package/runtime/sdk/gl/Material.ts +0 -260
  162. package/runtime/sdk/gl/Mesh.ts +0 -83
  163. package/runtime/sdk/gl/Model.ts +0 -124
  164. package/runtime/sdk/gl/NavAgent.ts +0 -337
  165. package/runtime/sdk/gl/NavMesh.ts +0 -397
  166. package/runtime/sdk/gl/Node.ts +0 -350
  167. package/runtime/sdk/gl/Noise.ts +0 -30
  168. package/runtime/sdk/gl/Particles.ts +0 -676
  169. package/runtime/sdk/gl/Physics.ts +0 -222
  170. package/runtime/sdk/gl/Plane.ts +0 -53
  171. package/runtime/sdk/gl/Ragdoll.ts +0 -270
  172. package/runtime/sdk/gl/Ray.ts +0 -16
  173. package/runtime/sdk/gl/Scene.ts +0 -486
  174. package/runtime/sdk/gl/Shape.ts +0 -409
  175. package/runtime/sdk/gl/Terrain.ts +0 -1092
  176. package/runtime/sdk/gl/Texture.ts +0 -63
  177. package/runtime/sdk/gl/Trigger.ts +0 -45
  178. package/runtime/sdk/gl/Vehicle.ts +0 -473
  179. package/runtime/sdk/gl/Wheel.ts +0 -240
  180. package/runtime/sdk/gl/animation/AnimationClip.ts +0 -204
  181. package/runtime/sdk/gl/animation/Animator.ts +0 -120
  182. package/runtime/sdk/gl/animation/Layer.ts +0 -31
  183. package/runtime/sdk/gl/animation/Loop.ts +0 -30
  184. package/runtime/sdk/gl/animation/Playback.ts +0 -44
  185. package/runtime/sdk/gl/animation/core.ts +0 -336
  186. package/runtime/sdk/gl/controls.ts +0 -95
  187. package/runtime/sdk/gl/physicsEvents.ts +0 -20
  188. package/runtime/sdk/gl/scenarios.ts +0 -291
  189. package/runtime/sdk/gl/state.ts +0 -6
  190. package/runtime/sdk/gl/terrainMesh.ts +0 -219
  191. package/runtime/sdk/gl/touch.ts +0 -68
  192. package/runtime/sdk/inject.ts +0 -197
  193. package/runtime/sdk/math/Mathf.ts +0 -118
  194. package/runtime/sdk/math/mat4.ts +0 -278
  195. package/runtime/sdk/math/quat.ts +0 -232
  196. package/runtime/sdk/math/vec.ts +0 -255
  197. package/runtime/sdk/net/codec.ts +0 -119
  198. package/runtime/sdk/net/core.ts +0 -384
  199. package/runtime/sdk/net/index.ts +0 -181
  200. package/runtime/sdk/net/replication.ts +0 -622
  201. package/runtime/sdk/plugins/camera.ts +0 -81
  202. package/runtime/sdk/plugins/geolocation.ts +0 -123
  203. package/runtime/sdk/plugins/oauth.ts +0 -61
  204. package/runtime/sdk/plugins/permission.ts +0 -7
  205. package/runtime/sdk/plugins/push.ts +0 -132
  206. package/runtime/sdk/plugins/qr.ts +0 -73
  207. package/runtime/sdk/plugins/service.ts +0 -47
  208. package/runtime/sdk/runtime/app.ts +0 -101
  209. package/runtime/sdk/runtime/appEvents.ts +0 -54
  210. package/runtime/sdk/runtime/channel.ts +0 -61
  211. package/runtime/sdk/runtime/clipboard.ts +0 -20
  212. package/runtime/sdk/runtime/datetime.ts +0 -329
  213. package/runtime/sdk/runtime/device.ts +0 -293
  214. package/runtime/sdk/runtime/fetch.ts +0 -77
  215. package/runtime/sdk/runtime/files.ts +0 -23
  216. package/runtime/sdk/runtime/input.ts +0 -175
  217. package/runtime/sdk/runtime/media.ts +0 -111
  218. package/runtime/sdk/runtime/misc.ts +0 -16
  219. package/runtime/sdk/runtime/net.ts +0 -36
  220. package/runtime/sdk/runtime/rpc.ts +0 -218
  221. package/runtime/sdk/runtime/service.ts +0 -83
  222. package/runtime/sdk/runtime/share.ts +0 -9
  223. package/runtime/sdk/runtime/storage.ts +0 -13
  224. package/runtime/sdk/runtime/touch.ts +0 -76
  225. package/runtime/sdk/scene/defineScene.ts +0 -1353
  226. package/runtime/sdk/scene/editorPlugins.ts +0 -110
  227. package/runtime/sdk/scene/gizmos.ts +0 -148
  228. package/runtime/sdk/scene/grammar.ts +0 -120
  229. package/runtime/sdk/scene/material.ts +0 -188
  230. package/runtime/sdk/server/auth/appConfig.ts +0 -12
  231. package/runtime/sdk/server/auth/global.ts +0 -80
  232. package/runtime/sdk/server/auth/host.ts +0 -318
  233. package/runtime/sdk/server/auth/models.ts +0 -83
  234. package/runtime/sdk/server/auth/types.ts +0 -50
  235. package/runtime/sdk/server/channel.ts +0 -56
  236. package/runtime/sdk/server/context.ts +0 -36
  237. package/runtime/sdk/server/db/defineDb.ts +0 -237
  238. package/runtime/sdk/server/db/fields.ts +0 -132
  239. package/runtime/sdk/server/db/httpTransport.ts +0 -93
  240. package/runtime/sdk/server/db/index.ts +0 -7
  241. package/runtime/sdk/server/db/marci/query.ts +0 -412
  242. package/runtime/sdk/server/db/types.ts +0 -202
  243. package/runtime/sdk/server/errors.ts +0 -12
  244. package/runtime/sdk/server/host.ts +0 -74
  245. package/runtime/sdk/server/inject.ts +0 -13
  246. package/runtime/sdk/server/runtime.ts +0 -133
  247. package/runtime/sdk/server/validate.ts +0 -87
  248. package/runtime/sdk/ui/NativeView.ts +0 -142
  249. package/runtime/sdk/ui/UI.ts +0 -39
  250. package/runtime/sdk/ui/UIBottomSheet.ts +0 -139
  251. package/runtime/sdk/ui/UIButton.ts +0 -101
  252. package/runtime/sdk/ui/UIContainer.ts +0 -60
  253. package/runtime/sdk/ui/UIImage.ts +0 -83
  254. package/runtime/sdk/ui/UIInput.ts +0 -185
  255. package/runtime/sdk/ui/UIModal.ts +0 -139
  256. package/runtime/sdk/ui/UINode.ts +0 -830
  257. package/runtime/sdk/ui/UIPager.ts +0 -362
  258. package/runtime/sdk/ui/UIPopover.ts +0 -100
  259. package/runtime/sdk/ui/UIScreen.ts +0 -123
  260. package/runtime/sdk/ui/UIScrollable.ts +0 -87
  261. package/runtime/sdk/ui/UISpacer.ts +0 -14
  262. package/runtime/sdk/ui/UITabs.ts +0 -236
  263. package/runtime/sdk/ui/UIText.ts +0 -51
  264. package/runtime/sdk/ui/UIVideo.ts +0 -88
  265. package/runtime/sdk/ui/UIVirtualizedList.ts +0 -241
  266. package/runtime/sdk/ui/UIWidget.ts +0 -127
  267. package/runtime/sdk/ui/fonts.ts +0 -13
  268. package/runtime/sdk/ui/presentable.ts +0 -117
  269. package/runtime/sdk/ui/router.ts +0 -132
  270. package/runtime/sdk/ui/theme.ts +0 -84
@@ -1,397 +0,0 @@
1
- // Navigation meshes (docs/navmesh-plan.md). A level bakes ONE navmesh at dev time from its static
2
- // collision geometry (`lecodes navmesh bake` — the desktop host dumps every static Jolt body's
3
- // triangles, Recast builds the mesh in the CLI) and loads it at runtime; agents (`NavAgent`) walk
4
- // it through DetourCrowd, and the game asks it for paths, nearest points, walkability rays and
5
- // random spots. A scene FILE does the loading by itself:
6
- //
7
- // env: { navmesh: { data: asset('./assets/nav/yard.navmesh'), agent: { radius: 0.4, height: 1.8 } } }
8
- //
9
- // Hand-built scenes call it once the level is complete:
10
- //
11
- // const nav = await NavMesh.load(scene, { data: asset('./assets/nav/yard.navmesh') })
12
- // nav?.findPath(a, b) // Vec3[] corners (+ .partial)
13
- // enemy.aspect(NavAgent, { speed: 3 }).agent.goTo(player.position)
14
- //
15
- // Under `lecodes navmesh bake` the same call is where the geometry dump fires ("the scene is
16
- // complete"); on a host without navigation, or with a placeholder file, `load` resolves null and
17
- // agents stay inert — the project runs everywhere. All logic lives here in JS; the native side
18
- // (creator-nav) is the algorithms and the per-step hot path: ONE crowd update + ONE flat read per
19
- // fixed step for every agent in the scene (`NavCrowd`, a system the mesh attaches to its scene).
20
-
21
- import { fetch } from "../runtime/fetch"
22
- import { System } from "../core/Aspect"
23
- import { Vec3, cx, cy, cz, type Vec3Like } from "../math/vec"
24
- import { Geometry } from "./Geometry"
25
- import { Material } from "./Material"
26
- import { Mesh } from "./Mesh"
27
- import type { Node } from "./Node"
28
- import type { Scene } from "./Scene"
29
- import type { NavAgent } from "./NavAgent"
30
-
31
- // Fixed-order layouts — keep in step with creator-nav/creator-nav.h (CNAV_AGENT_* / CNAV_READ_*).
32
- export const NAV_AGENT = {
33
- RADIUS: 0, HEIGHT: 1, MAX_SPEED: 2, MAX_ACCELERATION: 3, COLLISION_QUERY_RANGE: 4, PATH_OPTIMIZATION_RANGE: 5,
34
- SEPARATION_WEIGHT: 6, AVOIDANCE: 7, INCLUDE_FLAGS: 8, EXCLUDE_FLAGS: 9, ANTICIPATE_TURNS: 10,
35
- OPTIMIZE_VIS: 11, OPTIMIZE_TOPO: 12, COUNT: 13,
36
- } as const
37
- export const NAV_READ = {
38
- 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,
39
- STATE: 9, TARGET_STATE: 10, REMAINING: 11, PARTIAL: 12, STRIDE: 13,
40
- } as const
41
- const FLAGS_ALL = 0xffff
42
- const AREA_UNWALKABLE = 255
43
- const AREA_SKIP = 254
44
-
45
- /** The agent size a navmesh is built for (and the crowd's defaults). */
46
- export type NavAgentSize = {
47
- /** metres — polygons are eroded by it. Default 0.4 */
48
- radius?: number
49
- /** metres. Default 1.8 */
50
- height?: number
51
- /** the highest step walked over, metres. Default 0.4 */
52
- climb?: number
53
- /** the steepest walkable slope, degrees. Default 50 */
54
- slope?: number
55
- }
56
-
57
- export type NavMeshLoadOptions = {
58
- /** `asset('./assets/nav/<scene>.navmesh')` — what `lecodes navmesh bake` writes. */
59
- data: string
60
- /** The agent size the bake uses (the file records it; the crowd reads it back). */
61
- agent?: NavAgentSize
62
- /** Named areas → traversal cost (1 = plain ground). Ids are assigned in this order (1..15); the
63
- * bake stores the names, queries and agents refer to them (`include` / `exclude`, `cost`). */
64
- areas?: Record<string, number>
65
- }
66
-
67
- /** The file header `lecodes navmesh bake` wrote. */
68
- export type NavMeshInfo = {
69
- version: number
70
- agent: { radius: number, height: number, climb: number, slope: number }
71
- cell: number
72
- cellHeight: number
73
- tileSize: number
74
- bounds: [[number, number, number], [number, number, number]]
75
- stats: { tiles: number, polys: number, verts: number, triangles: number, volumes: number, links: number }
76
- meta: { scene?: string, areas?: Record<string, number>, costs?: Record<string, number>, [k: string]: unknown } | null
77
- }
78
-
79
- /** A straight path: the corners to walk, plus whether it stops short of the target. */
80
- export type NavPath = Vec3[] & { partial: boolean }
81
-
82
- export type NavQueryOptions = {
83
- /** Only these areas (names). Default: every area. */
84
- include?: string[]
85
- /** Never these areas (names). */
86
- exclude?: string[]
87
- /** Search half-extents around each endpoint (how far off the mesh a point may be). Default [2, 4, 2]. */
88
- extents?: Vec3Like
89
- }
90
-
91
- export type NavRaycastHit = { hit: boolean, point: Vec3, normal: Vec3 }
92
-
93
- type BakeConfig = { outDir: string, name?: string }
94
-
95
- const bakeConfig = (): BakeConfig | null => {
96
- const g = (globalThis as unknown as { __lecodesNavmesh?: BakeConfig }).__lecodesNavmesh
97
- return g && typeof g.outDir === "string" ? g : null
98
- }
99
-
100
- const hasNav = (): boolean => typeof _creatorNav !== "undefined" && _creatorNav !== null
101
-
102
- const scratch = new Float32Array(6)
103
- const pathScratch = new Float32Array(256 * 3)
104
-
105
- /** The crowd behind a scene's agents — a system `NavMesh.load` attaches to the scene so the whole
106
- * crowd advances once per fixed step, BEFORE every `NavAgent` applies its slot (`static after`). */
107
- export class NavCrowd extends System<"navCrowd"> {
108
- static readonly aspect = "navCrowd"
109
-
110
- /** @internal */ _mesh: NavMesh | null = null
111
- /** @internal */ _id = 0
112
- /** @internal */ _max = 0
113
- /** @internal */ _buf = new Float32Array(0)
114
- /** @internal */ _agents = new Set<NavAgent>()
115
- /** @internal */ _step = 0
116
-
117
- /** @internal Make room for one more agent (the crowd is re-created with double the slots; every
118
- * agent re-joins — slots change, targets are re-requested). */
119
- _ensureCapacity(): boolean {
120
- if (!this._mesh || !hasNav()) return false
121
- if (this._id && this._agents.size < this._max) return true
122
- const max = Math.max(16, this._max * 2)
123
- const id = _creatorNav.crowdCreate(this._mesh.id, max, Math.max(0.6, this._mesh._maxRadius))
124
- if (!id) { console.warn(`NavCrowd: ${_creatorNav.lastError()}`); return false }
125
- const old = this._id
126
- this._id = id
127
- this._max = max
128
- this._buf = new Float32Array(max * NAV_READ.STRIDE)
129
- if (old) {
130
- for (const a of this._agents) a._rejoin()
131
- _creatorNav.crowdDestroy(old)
132
- }
133
- return true
134
- }
135
-
136
- updateFixed(dt: number): void {
137
- if (!this._id) return
138
- _creatorNav.crowdUpdate(this._id, dt)
139
- _creatorNav.crowdRead(this._id, this._buf)
140
- this._step++
141
- }
142
-
143
- onDetach(): void {
144
- if (this._id && hasNav()) _creatorNav.crowdDestroy(this._id)
145
- this._id = 0
146
- this._max = 0
147
- this._agents.clear()
148
- }
149
- }
150
-
151
- export class NavMesh {
152
- /** True while `lecodes navmesh bake` runs the app — skip menus and build the level straight away. */
153
- static get baking(): boolean { return bakeConfig() !== null }
154
-
155
- /** The navmesh agents bind to (the last one loaded; one per world at a time). */
156
- static active: NavMesh | null = null
157
-
158
- private static byScene = new WeakMap<Scene, NavMesh>()
159
- private static overrides = new Map<number, number | string>() // entity → area id / name / skip
160
- private static warned = false
161
- private static baked = false
162
-
163
- /** Leave a static body out of the bake (`nav: false` in a scene file). */
164
- static exclude(node: Node): void { NavMesh.overrides.set(node.id, AREA_SKIP) }
165
- /** A static body nobody may walk on (`nav: 'unwalkable'`): its footprint is cut out. */
166
- static unwalkable(node: Node): void { NavMesh.overrides.set(node.id, AREA_UNWALKABLE) }
167
- /** A static body's surface belongs to a named area (`navArea: 'road'`; names from `load`'s `areas`). */
168
- static area(node: Node, name: string): void { NavMesh.overrides.set(node.id, name) }
169
- /** Forget every override (a scene rebuild). */
170
- static clear(): void { NavMesh.overrides.clear() }
171
-
172
- static of(scene: Scene): NavMesh | null { return NavMesh.byScene.get(scene) ?? null }
173
-
174
- /** Load the level's navmesh — or, under `lecodes navmesh bake`, dump the collision geometry for
175
- * it. Resolves null when nothing was loaded (no bake yet, a host without navigation, bake mode). */
176
- static async load(scene: Scene, opts: NavMeshLoadOptions): Promise<NavMesh | null> {
177
- const bake = bakeConfig()
178
- if (bake) { NavMesh.bake(bake, opts); return null }
179
- if (!hasNav()) {
180
- if (!NavMesh.warned) { NavMesh.warned = true; console.warn("NavMesh: this host has no navigation support — agents stay still") }
181
- return null
182
- }
183
- if (_creatorNav.ready) await _creatorNav.ready()
184
- let id = 0
185
- try {
186
- const resp = await fetch(opts.data, { useOnce: true })
187
- if (resp.status >= 400) { console.warn(`NavMesh: no navmesh at ${opts.data} (HTTP ${resp.status}) — run \`lecodes navmesh bake\``); return null }
188
- id = _creatorNav.load((resp as unknown as { _id: number })._id)
189
- resp.dispose()
190
- } catch (e) {
191
- console.warn(`NavMesh: could not read ${opts.data}: ${String(e)}`)
192
- return null
193
- }
194
- if (!id) { console.warn(`NavMesh: ${opts.data}: ${_creatorNav.lastError()}`); return null }
195
- let info: NavMeshInfo
196
- try { info = JSON.parse(_creatorNav.header(id)) as NavMeshInfo } catch { info = { version: 0 } as NavMeshInfo }
197
- if (!info.stats || info.stats.polys === 0) {
198
- console.warn(`NavMesh: ${opts.data} is a placeholder — run \`lecodes navmesh bake\``)
199
- _creatorNav.unload(id)
200
- return null
201
- }
202
- const previous = NavMesh.byScene.get(scene)
203
- if (previous) previous.dispose()
204
- const nav = new NavMesh(scene, id, info, opts)
205
- NavMesh.byScene.set(scene, nav)
206
- NavMesh.active = nav
207
- return nav
208
- }
209
-
210
- readonly scene: Scene
211
- /** @internal the host handle */
212
- readonly id: number
213
- readonly info: NavMeshInfo
214
- /** The agent size the mesh was built for. */
215
- readonly agent: { radius: number, height: number, climb: number, slope: number }
216
- /** Named areas → ids (from the bake; `load`'s `areas` when the file has none). */
217
- readonly areas: Record<string, number>
218
- /** @internal the largest agent radius seen (sizes the crowd's grid) */
219
- _maxRadius = 0
220
- private _debug: Mesh[] = []
221
- private _seed = 0x9e3779b9
222
- private _disposed = false
223
-
224
- private constructor(scene: Scene, id: number, info: NavMeshInfo, opts: NavMeshLoadOptions) {
225
- this.scene = scene
226
- this.id = id
227
- this.info = info
228
- this.agent = info.agent ?? { radius: opts.agent?.radius ?? 0.4, height: opts.agent?.height ?? 1.8, climb: opts.agent?.climb ?? 0.4, slope: opts.agent?.slope ?? 50 }
229
- this._maxRadius = this.agent.radius
230
- this.areas = info.meta?.areas ?? NavMesh.areaIds(opts.areas)
231
- const costs = { ...(info.meta?.costs ?? {}), ...(opts.areas ?? {}) }
232
- for (const [ name, cost ] of Object.entries(costs)) this.cost(name, cost)
233
- }
234
-
235
- private static areaIds(areas: Record<string, number> | undefined): Record<string, number> {
236
- const out: Record<string, number> = {}
237
- let next = 1
238
- for (const name of Object.keys(areas ?? {})) {
239
- if (next > 15) { console.warn(`NavMesh: more than 15 areas — "${name}" ignored`); break }
240
- out[name] = next++
241
- }
242
- return out
243
- }
244
-
245
- /** The include/exclude flag mask for a list of area names (undefined = every area). */
246
- flags(names?: string[]): number {
247
- if (!names) return FLAGS_ALL
248
- let mask = 0
249
- for (const n of names) {
250
- const id = this.areas[n]
251
- if (id === undefined) { console.warn(`NavMesh: unknown area "${n}"`); continue }
252
- mask |= 1 << id
253
- }
254
- return mask
255
- }
256
-
257
- private masks(opts?: NavQueryOptions): [number, number] {
258
- return [ opts?.include ? this.flags(opts.include) : FLAGS_ALL, opts?.exclude ? this.flags(opts.exclude) : 0 ]
259
- }
260
-
261
- /** Traversal cost of a named area (1 = plain ground; > 1 = avoided when a cheaper way exists). */
262
- cost(area: string, cost: number): void {
263
- const id = this.areas[area]
264
- if (id === undefined) { console.warn(`NavMesh: unknown area "${area}"`); return }
265
- if (!this._disposed) _creatorNav.setAreaCost(this.id, id, cost)
266
- }
267
-
268
- /** The straight (string-pulled) path from → to. Empty when an endpoint is off the mesh;
269
- * `.partial` when the target is unreachable (the path ends at the closest polygon). */
270
- findPath(from: Vec3Like, to: Vec3Like, opts?: NavQueryOptions & { maxCorners?: number }): NavPath {
271
- const path = [] as unknown as NavPath
272
- path.partial = false
273
- if (this._disposed) return path
274
- const [ inc, exc ] = this.masks(opts)
275
- const e = opts?.extents
276
- const max = Math.min(opts?.maxCorners ?? 256, 256)
277
- const n = _creatorNav.findPath(this.id, cx(from), cy(from), cz(from), cx(to), cy(to), cz(to),
278
- e ? cx(e) : 0, e ? cy(e) : 0, e ? cz(e) : 0, inc, exc, max, pathScratch)
279
- const count = Math.abs(n)
280
- for (let i = 0; i < count; i++) path.push(new Vec3(pathScratch[i * 3]!, pathScratch[i * 3 + 1]!, pathScratch[i * 3 + 2]!))
281
- path.partial = n < 0
282
- return path
283
- }
284
-
285
- /** The closest point on the mesh (within `extents` of `p`), or null. */
286
- nearest(p: Vec3Like, opts?: NavQueryOptions): Vec3 | null {
287
- if (this._disposed) return null
288
- const [ inc, exc ] = this.masks(opts)
289
- const e = opts?.extents
290
- const ok = _creatorNav.nearest(this.id, cx(p), cy(p), cz(p), e ? cx(e) : 0, e ? cy(e) : 0, e ? cz(e) : 0, inc, exc, scratch)
291
- return ok ? new Vec3(scratch[0]!, scratch[1]!, scratch[2]!) : null
292
- }
293
-
294
- /** Can an agent walk straight from → to? `hit` = a wall in between (at `point`, facing `normal`). */
295
- raycast(from: Vec3Like, to: Vec3Like, opts?: NavQueryOptions): NavRaycastHit {
296
- if (this._disposed) return { hit: true, point: new Vec3(cx(from), cy(from), cz(from)), normal: new Vec3(0, 0, 0) }
297
- const [ inc, exc ] = this.masks(opts)
298
- const hit = _creatorNav.raycast(this.id, cx(from), cy(from), cz(from), cx(to), cy(to), cz(to), inc, exc, scratch)
299
- return { hit, point: new Vec3(scratch[0]!, scratch[1]!, scratch[2]!), normal: new Vec3(scratch[3]!, scratch[4]!, scratch[5]!) }
300
- }
301
-
302
- /** A random reachable point — within walking distance `radius` of `center`, or anywhere on the
303
- * mesh. Deterministic: the sequence depends only on the calls made (a seeded generator). */
304
- randomPoint(center?: Vec3Like, radius = 0, opts?: NavQueryOptions): Vec3 | null {
305
- if (this._disposed) return null
306
- const [ inc, exc ] = this.masks(opts)
307
- this._seed = (Math.imul(this._seed, 1664525) + 1013904223) >>> 0
308
- const c = center && radius > 0
309
- const ok = _creatorNav.randomPoint(this.id, c ? cx(center) : 0, c ? cy(center) : 0, c ? cz(center) : 0, c ? radius : 0, this._seed || 1, inc, exc, scratch)
310
- return ok ? new Vec3(scratch[0]!, scratch[1]!, scratch[2]!) : null
311
- }
312
-
313
- /** Draw the polygon mesh (a flat unlit surface 3 cm above the walkable area). */
314
- get debug(): boolean { return this._debug.length > 0 }
315
- set debug(on: boolean) {
316
- if (on === this.debug || this._disposed) return
317
- if (!on) { for (const m of this._debug) m.destroy(); this._debug = []; return }
318
- const tris = _creatorNav.debugTriangles(this.id)
319
- const material = Material.unlit({ color: "#3b82f6" })
320
- // Geometry indexes with 16-bit indices: chunk the soup
321
- const perChunk = 65535 - (65535 % 3)
322
- for (let start = 0; start < tris.length / 3; start += perChunk) {
323
- const count = Math.min(perChunk, tris.length / 3 - start)
324
- const vertices = new Float32Array(count * 3)
325
- const normals = new Float32Array(count * 3)
326
- const uv = new Float32Array(count * 2)
327
- const indices = new Uint16Array(count)
328
- for (let i = 0; i < count; i++) {
329
- vertices[i * 3] = tris[(start + i) * 3]!
330
- vertices[i * 3 + 1] = tris[(start + i) * 3 + 1]! + 0.03
331
- vertices[i * 3 + 2] = tris[(start + i) * 3 + 2]!
332
- normals[i * 3 + 1] = 1
333
- indices[i] = i
334
- }
335
- const mesh = new Mesh(new Geometry(vertices, normals, indices, uv), material)
336
- mesh.name = "navmesh-debug"
337
- mesh.castShadows = false
338
- this.scene.add(mesh)
339
- this._debug.push(mesh)
340
- }
341
- }
342
-
343
- /** @internal The scene's crowd system (created on the first agent). */
344
- _crowd(): NavCrowd {
345
- const existing = this.scene.get(NavCrowd)
346
- if (existing && existing._mesh === this) return existing
347
- const crowd = this.scene.system(NavCrowd).navCrowd
348
- crowd._mesh = this
349
- return crowd
350
- }
351
-
352
- /** Release the host handles (a scene teardown). Agents fall back to inert. */
353
- dispose(): void {
354
- if (this._disposed) return
355
- this._disposed = true
356
- this.debug = false
357
- const crowd = this.scene.get(NavCrowd)
358
- if (crowd && crowd._mesh === this) {
359
- for (const a of crowd._agents) a._leave()
360
- this.scene.removeSystem(NavCrowd)
361
- }
362
- if (hasNav()) _creatorNav.unload(this.id)
363
- if (NavMesh.byScene.get(this.scene) === this) NavMesh.byScene.delete(this.scene)
364
- if (NavMesh.active === this) NavMesh.active = null
365
- }
366
-
367
- // ---- bake mode ----------------------------------------------------------------------------------
368
-
369
- private static bake(cfg: BakeConfig, opts: NavMeshLoadOptions): void {
370
- if (NavMesh.baked) { console.log("[navmesh] a second NavMesh.load — skipped (one bake per run; use --entry per scene)"); return }
371
- NavMesh.baked = true
372
- if (!_creator.physicsStaticGeometry) {
373
- console.error("[navmesh] error: this host cannot dump collision geometry (build the desktop host from packages/desktop, or update: lecodes desktop update)")
374
- return
375
- }
376
- const areaIds = NavMesh.areaIds(opts.areas)
377
- const ids: number[] = []
378
- const areas: number[] = []
379
- for (const [ entity, area ] of NavMesh.overrides) {
380
- let id: number
381
- if (typeof area === "string") {
382
- id = areaIds[area] ?? -1
383
- if (id < 0) { console.warn(`[navmesh] unknown area "${area}" on entity ${entity} — declare it in the navmesh 'areas'`); continue }
384
- } else id = area
385
- ids.push(entity)
386
- areas.push(id)
387
- }
388
- const path = `${cfg.outDir}/${cfg.name ?? "scene"}.navgeo`
389
- const n = _creator.physicsStaticGeometry(path, Uint32Array.from(ids), Uint8Array.from(areas))
390
- if (n < 0) { console.error(`[navmesh] error: the geometry dump failed (${path})`); return }
391
- if (n === 0) { console.error("[navmesh] error: no static collision geometry — agents walk on STATIC Physics bodies (Shape + Physics { motion: 'static' })"); return }
392
- const agent = { radius: opts.agent?.radius ?? 0.4, height: opts.agent?.height ?? 1.8, climb: opts.agent?.climb ?? 0.4, slope: opts.agent?.slope ?? 50 }
393
- console.log(`[navmesh] geometry ${n} triangles → ${path}`)
394
- console.log(`[navmesh] config ${JSON.stringify({ agent, areas: areaIds, costs: opts.areas ?? {}, data: opts.data })}`)
395
- console.log("[navmesh] done")
396
- }
397
- }