lecodes-cli 0.18.2 → 0.19.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 (57) hide show
  1. package/dist/index.js +1240 -170
  2. package/package.json +3 -2
  3. package/runtime/scene-harness.json +1 -1
  4. package/runtime/sdk/compile/aspectMacro.ts +86 -0
  5. package/runtime/sdk/compile/assetIconMacro.ts +384 -0
  6. package/runtime/sdk/compile/assetMacro.ts +146 -0
  7. package/runtime/sdk/compile/assetName.ts +50 -0
  8. package/runtime/sdk/compile/bundler.ts +287 -0
  9. package/runtime/sdk/compile/compileProject.ts +129 -0
  10. package/runtime/sdk/compile/detectEntry.ts +128 -0
  11. package/runtime/sdk/compile/fontMacro.ts +459 -0
  12. package/runtime/sdk/compile/fontRegistry.ts +78 -0
  13. package/runtime/sdk/compile/header.ts +67 -0
  14. package/runtime/sdk/compile/index.ts +100 -0
  15. package/runtime/sdk/compile/libraryImports.ts +52 -0
  16. package/runtime/sdk/compile/liteMaterial.ts +247 -0
  17. package/runtime/sdk/compile/sceneEditor.ts +88 -0
  18. package/runtime/sdk/compile/serverSplit.ts +233 -0
  19. package/runtime/sdk/compile/serverTypes.ts +227 -0
  20. package/runtime/sdk/compile/sfnt.ts +98 -0
  21. package/runtime/sdk/compile/shaderSchema.ts +202 -0
  22. package/runtime/sdk/compile/shaderTargets.ts +81 -0
  23. package/runtime/sdk/compile/sourcemap.ts +25 -0
  24. package/runtime/sdk/core/Aspect.ts +65 -9
  25. package/runtime/sdk/core/StateMachine.ts +308 -0
  26. package/runtime/sdk/core/time.ts +3 -0
  27. package/runtime/sdk/gl/CameraPlace.ts +52 -52
  28. package/runtime/sdk/gl/Light.ts +12 -4
  29. package/runtime/sdk/gl/Lightmap.ts +87 -10
  30. package/runtime/sdk/gl/Material.ts +15 -0
  31. package/runtime/sdk/gl/Model.ts +68 -8
  32. package/runtime/sdk/gl/NavAgent.ts +337 -0
  33. package/runtime/sdk/gl/NavMesh.ts +397 -0
  34. package/runtime/sdk/gl/Ragdoll.ts +270 -0
  35. package/runtime/sdk/gl/Scene.ts +7 -0
  36. package/runtime/sdk/gl/Shape.ts +37 -5
  37. package/runtime/sdk/gl/Terrain.ts +1092 -0
  38. package/runtime/sdk/gl/Texture.ts +17 -0
  39. package/runtime/sdk/gl/Trigger.ts +45 -45
  40. package/runtime/sdk/gl/animation/Animator.ts +35 -2
  41. package/runtime/sdk/gl/animation/Layer.ts +3 -1
  42. package/runtime/sdk/gl/animation/Loop.ts +5 -0
  43. package/runtime/sdk/gl/animation/Playback.ts +4 -3
  44. package/runtime/sdk/gl/animation/core.ts +52 -10
  45. package/runtime/sdk/gl/scenarios.ts +291 -291
  46. package/runtime/sdk/gl/state.ts +6 -6
  47. package/runtime/sdk/gl/terrainMesh.ts +219 -0
  48. package/runtime/sdk/inject.ts +12 -1
  49. package/runtime/sdk/net/codec.ts +119 -0
  50. package/runtime/sdk/net/core.ts +384 -0
  51. package/runtime/sdk/net/index.ts +181 -0
  52. package/runtime/sdk/net/replication.ts +622 -0
  53. package/runtime/sdk/scene/defineScene.ts +140 -14
  54. package/runtime/sdk/scene/editorPlugins.ts +18 -0
  55. package/runtime/sdk/scene/gizmos.ts +148 -148
  56. package/runtime/sdk/ui/UINode.ts +4 -0
  57. package/runtime/sdk-types.json +1 -1
@@ -141,6 +141,13 @@ const installRenderSyncedFrames = (): void => {
141
141
  _creator.setEarlyUpdate((dt: number) => { glState.frame++; early(dt) })
142
142
  _creator.setLateUpdate(late)
143
143
  return true
144
+ }, (fixed) => {
145
+ // The fixed phase runs INSIDE the engine's substep loop (physics/world.cpp) when the host has the
146
+ // hook; each substep moves bodies natively, so the frame stamp bumps per step too. Feature-detected:
147
+ // an older host returns false and the dispatcher steps its own 1/60 accumulator.
148
+ if (typeof _creator.setFixedUpdate !== "function") return false
149
+ _creator.setFixedUpdate((dt: number) => { glState.frame++; fixed(dt) })
150
+ return true
144
151
  })
145
152
  // Time.scale / Time.paused reach the engine's own clocks (physics, animators, particles) through
146
153
  // setTimeScale; a host without it keeps running its sims at wall-clock speed (feature-detected).
@@ -22,7 +22,11 @@ import { cx, cy, cz, type Vec3Like } from "../math/vec"
22
22
  import type { Node } from "./Node"
23
23
 
24
24
  /** Which geometry a Shape uses — the one-of the fields below encode (see `_build`'s order). */
25
- export type ShapeKind = "auto" | "box" | "sphere" | "cylinder" | "capsule" | "mesh" | "convex"
25
+ export type ShapeKind = "auto" | "box" | "sphere" | "cylinder" | "capsule" | "mesh" | "convex" | "heightfield"
26
+
27
+ /** What a terrain node carries for `Shape { heightfield: true }` (gl/Terrain.ts `_data`). */
28
+ type TerrainData = { sizeX: number, sizeZ: number, cellSize: number, heights: Float32Array, holes: Uint8Array | null }
29
+ const terrainOf = (node: Node): TerrainData | null => (node as unknown as { _terrain?: { _data: TerrainData } })._terrain?._data ?? null
26
30
 
27
31
  /** A node-local AABB, in the node's OWN frame (its own transform excluded), before world scale. */
28
32
  export type ShapeBounds = { min: [number, number, number], max: [number, number, number] }
@@ -60,6 +64,10 @@ export class Shape extends Aspect<"shape", Node> {
60
64
  * only (a dynamic body can't be a triangle mesh; Physics throws). `'convex'` = convex hull of the
61
65
  * vertices, works for dynamic bodies too. A Model uses its bind pose (skinned parts skipped). */
62
66
  mesh?: boolean | "convex"
67
+ /** The node's terrain (`Terrain.create(...).node`) as a Jolt height field — the exact drawn grid,
68
+ * holes included (docs/terrain-plan.md). Static / kinematic / picking / character ground only,
69
+ * like `mesh: true`. `terrain.commit()` pushes edits into the live shape. */
70
+ heightfield?: boolean
63
71
  /**
64
72
  * The shape's centre, relative to the node's origin — world units in the node's ROTATED frame
65
73
  * (the node's scale is ignored, exactly like the explicit dimensions above). Default [0, 0, 0].
@@ -140,6 +148,7 @@ export class Shape extends Aspect<"shape", Node> {
140
148
 
141
149
  /** Which geometry this shape currently uses (the `_build` dispatch, as a name). */
142
150
  get kind(): ShapeKind {
151
+ if (this.heightfield) return "heightfield"
143
152
  if (this.mesh === "convex") return "convex"
144
153
  if (this.mesh) return "mesh"
145
154
  if (this.sphere !== undefined) return "sphere"
@@ -238,8 +247,14 @@ export class Shape extends Aspect<"shape", Node> {
238
247
  if (!this.raycast && this._bodyId) _creator.physicsSetPickable(this._bodyId, false)
239
248
  }
240
249
 
241
- /** @internal true for an exact triangle mesh — Physics refuses a dynamic body on it. */
242
- get _isTriangleMesh(): boolean { return this.mesh === true }
250
+ /** @internal true for an exact triangle mesh or a height field — Physics refuses a dynamic body on it. */
251
+ get _isTriangleMesh(): boolean { return this.mesh === true || this.heightfield === true }
252
+
253
+ /** @internal the height range a heightfield shape can encode (`Terrain.commit` rebuilds past it). */
254
+ _heightfieldRange: [number, number] | null = null
255
+
256
+ /** @internal rebuild the geometry in place (a terrain edit that left the encodable range). */
257
+ _rebuildShape(): void { this._rebuild() }
243
258
 
244
259
  // Rebuild the geometry in place: the body keeps its id, velocity and world transform (Physics
245
260
  // holds that id, so re-creating the body instead would strand it). A shape a CharacterController
@@ -290,7 +305,8 @@ export class Shape extends Aspect<"shape", Node> {
290
305
  this._autoOrigin = null
291
306
  this._sig = this._signature()
292
307
  let id = 0
293
- if (this.mesh) id = this._buildMesh(this.mesh === "convex")
308
+ if (this.heightfield) id = this._buildHeightField()
309
+ else if (this.mesh) id = this._buildMesh(this.mesh === "convex")
294
310
  else if (this.sphere !== undefined) id = _creator.physicsBuildSphere(this.sphere)
295
311
  else if (this.cylinder) id = _creator.physicsBuildCylinder(this.cylinder.halfHeight, this.cylinder.radius)
296
312
  else if (this.capsule) id = _creator.physicsBuildCapsule(this.capsule.halfHeight, this.capsule.radius)
@@ -321,7 +337,23 @@ export class Shape extends Aspect<"shape", Node> {
321
337
  private _signature(): string {
322
338
  const v = (a: Vec3Like | undefined) => (a ? `${cx(a)},${cy(a)},${cz(a)}` : "")
323
339
  const t = (o: { halfHeight: number, radius: number } | undefined) => (o ? `${o.halfHeight},${o.radius}` : "")
324
- return `${v(this.box)}|${this.sphere ?? ""}|${t(this.cylinder)}|${t(this.capsule)}|${this.mesh ?? ""}|${v(this.origin)}`
340
+ return `${v(this.box)}|${this.sphere ?? ""}|${t(this.cylinder)}|${t(this.capsule)}|${this.mesh ?? ""}|${this.heightfield ? "hf" : ""}|${v(this.origin)}`
341
+ }
342
+
343
+ // heightfield: the terrain on this node hands its grid over; the engine keeps its own copy and
344
+ // pads it to Jolt's block grid. No terrain / no host support = no collider (0), with a warning.
345
+ private _buildHeightField(): number {
346
+ const t = terrainOf(this.node)
347
+ if (!t) { console.warn(`Shape { heightfield }: "${this.node.name}" carries no Terrain — no collider`); return 0 }
348
+ if (!_creator.physicsBuildHeightField) { console.warn("Shape { heightfield } needs a newer host — the terrain has no collider"); return 0 }
349
+ const s = this.node.worldScale
350
+ let lo = Infinity, hi = -Infinity
351
+ for (let i = 0; i < t.heights.length; i++) { const h = t.heights[i]; if (h < lo) lo = h; if (h > hi) hi = h }
352
+ const range = Math.max(1, hi - lo), margin = Math.max(2, range * 0.25)
353
+ this._heightfieldRange = [ lo - margin, hi + margin ] // mirrors creator-gl physicsBuildHeightField's headroom
354
+ const id = _creator.physicsBuildHeightField(t.heights, t.sizeX, t.sizeZ, t.cellSize, t.holes, s.x, s.y, s.z)
355
+ if (!id) console.warn(`Shape { heightfield }: the engine refused "${this.node.name}"'s grid (all holes?)`)
356
+ return id
325
357
  }
326
358
 
327
359
  // mesh: a Mesh node hands its CPU geometry over; anything else is tried as a GLB root (the host