lecodes-sdk 2.4.8 → 2.4.10

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.
@@ -52,11 +52,11 @@ import { Mesh } from "../gl/Mesh"
52
52
  import { Model } from "../gl/Model"
53
53
  import { Physics } from "../gl/physics/Physics"
54
54
  import { CharacterController } from "../gl/physics/CharacterController"
55
- import { Lightmap, type LightmapTransmit } from "../gl/Lightmap"
55
+ import { Lightmap, type LightmapDef } from "../gl/Lightmap"
56
56
  import { Foliage, type FoliageOptions } from "../gl/Foliage"
57
57
  import { NavMesh } from "../gl/nav/NavMesh"
58
58
  import { Terrain, type TerrainLayer, type TerrainRegion, type TerrainOptions } from "../gl/terrain/Terrain"
59
- import { Light, type SunOptions } from "../gl/Light"
59
+ import { Light, type PointOptions, type SpotOptions, type SunOptions } from "../gl/Light"
60
60
  import { Material } from "../gl/Material"
61
61
  import { assignMaterialDef, MaterialHandle, resolveMaterialDef, type MaterialDef } from "./material"
62
62
  import type { CapsuleOptions, CylinderOptions, PlaneOptions, SphereOptions } from "../gl/Geometry"
@@ -71,7 +71,13 @@ export type MeshDef =
71
71
  | ({ kind: "capsule" } & CapsuleOptions)
72
72
  | ({ kind: "plane" } & PlaneOptions)
73
73
 
74
- export type LightDef = { kind: "sun" } & SunOptions
74
+ /** The `light: {}` source block, by `kind` — `Light.sun` / `Light.point` / `Light.spot` with the node's transform: a
75
+ * point light's `bakeArea` panel lies in the node's local XZ plane and emits along its local -Y, so `eulerAngles`
76
+ * tilts it; a spot shines along the node's -Z (`direction` aims it, as a sun's does). */
77
+ export type LightDef =
78
+ | ({ kind: "sun" } & SunOptions)
79
+ | ({ kind: "point" } & PointOptions)
80
+ | ({ kind: "spot" } & SpotOptions)
75
81
 
76
82
  /** A stamp: the terrain conforms to another node of this file (a road) at load — see Terrain.conform. */
77
83
  export type TerrainStampDef = {
@@ -192,7 +198,7 @@ export type SceneNodeDef = {
192
198
  * `'cube'` = a static whose every mesh bakes as AMBIENT CUBES instead of atlas texels — an occluder (its shadow and
193
199
  * bounce stay in the bake) lit where it stands, the way the bake treats small and thin props on its own: a pipe run, a
194
200
  * fence, a prop whose lightmap seams show. A prefab marked `'cube'` bakes its statics that way too. By name for a whole
195
- * kit: `env.lightmap.cube`. */
201
+ * kit: the `cube` option of `env.lightmap`'s `bake()`. */
196
202
  lightmap?: boolean | "cube"
197
203
  /** `model` nodes: vegetation — load through the FOLIAGE tier (wind, touch bending, per-copy tint; see
198
204
  * `Foliage`). `{ fade: [start, end] }` also thins the cards out over that distance range (m) and drops the
@@ -215,55 +221,6 @@ export type SceneCameraDef = CameraProjectionDef & {
215
221
  target?: Vec3Like
216
222
  }
217
223
 
218
- /** `env.lightmap` — the level's baked lighting (engines/bake): the files `lecodes lightmap bake`
219
- * writes. Absent = real-time only. */
220
- export type SceneLightmapDef = {
221
- /** `asset('./assets/lightmap/level.bake')` */
222
- data: string
223
- /** `asset('./assets/lightmap/level-light.ktx2')` — or, for a bake that took more than one atlas PAGE
224
- * (`lecodes lightmap bake --pages`), every page in order: `[asset('x-light.ktx2'), asset('x-light_1.ktx2')]`. */
225
- light: string | string[]
226
- /** `asset('./assets/lightmap/level-aux.ktx2')` (pages like `light`) — sun / sky visibility + light direction. */
227
- aux: string | string[]
228
- /** DEBUG: the page sets a `lecodes lightmap bake --split` wrote (`<stem>-direct[_n].ktx2`, `<stem>-indirect[_n].ktx2`),
229
- * pages like `light` — the `Lightmap.debug("direct" | "indirect")` views bind them in place of `light`. */
230
- direct?: string | string[]
231
- indirect?: string | string[]
232
- /** `asset('./assets/lightmap/level-probes.ktx2')` — the bake's reflection probes (one prefiltered cubemap of the level's
233
- * radiance per probe; every static reflects the nearest). Absent = statics reflect the sky's SH alone. */
234
- probes?: string
235
- /** FOR THE BAKE: the radiance in cd / m² of emission 1.0 on this level (emissive factor x strength x map) - every lit
236
- * region of an emissive surface bakes as a rectangle lamp. An imported pack's emission is a LOOK (the school's panels
237
- * say 10); this is where the level says what they give: a 1.3 m ceiling panel of ~8000 lm is ~1600 nits. Default: what
238
- * the camera makes of 1.0 (`1.2 x 2^EV100`). 0 = emissive surfaces light nothing. `--emissive-nits` overrides. */
239
- emissiveNits?: number
240
- /** `false` = the emissive surfaces light nothing in the bake - the level is lit by its lamps (`Light.point` with a
241
- * `bakeArea` next to every panel, the Unity packs' way) and the panels are decoration; `emissiveNits` then only says how
242
- * bright their glow is DRAWN (scene-referred: nits through the camera's exposure). Default true. */
243
- emissiveBake?: boolean
244
- /** FOR THE BAKE: materials that let light THROUGH them, by glTF material name (`"name*"` = every name with that start) →
245
- * the share of a shadow ray that passes, tinted by the material's base colour x map: `{ mat_awning: 0.3 }` puts the
246
- * awning's warm, patterned light on the sand. The surface stays opaque - baked, drawn as before, a shadow caster.
247
- * `{ through, diffuse }` splits it: `through` goes straight (the picture of the map), `diffuse` is scattered by the
248
- * fibres (the underside glows, a soft fill with no picture) - dense canvas: `{ through: 0.08, diffuse: 0.25 }`. */
249
- transmit?: Record<string, LightmapTransmit>
250
- /** FOR THE BAKE: CUBES BY NAME — regular expressions (ECMAScript, case-sensitive) searched in every baked mesh's key
251
- * (`<node path>@<ordinal>`): `['SM_pipe', 'SM_fence']` bakes every pipe and fence node as ambient cubes instead of atlas
252
- * texels, like a node's `lightmap: 'cube'`. The CLI's `--cube` adds to these. */
253
- cube?: string[]
254
- /** `asset('./assets/lightmap/level.lgrid')` — THE LIGHT GRID for movers: every node a Physics aspect or a
255
- * CharacterController moves (and what code marks with `Lightmap.track`) takes its ambient light from the bake at the
256
- * place it is at, whatever its shader. Absent = movers keep the scene's IBL. */
257
- volume?: string
258
- /** `asset('./assets/lightmap/level-uv1.bin')` — THE PACKED LIGHTMAP UV1 the bake's rects map: it packs every mesh's
259
- * lightmap charts into a square of their own instead of the unwrap's (once per mesh, whatever model holds it), and the
260
- * engine swaps this into the models' TEXCOORD_1 before the rects go on. Required when the bake wrote one (the .bake's `uv1`): without it the statics
261
- * read the wrong texels, so the bake is not applied. */
262
- uv1?: string
263
- /** A DEBUG multiplier on the atlas' lux (default 1). */
264
- lightScale?: number
265
- }
266
-
267
224
  /** `env.navmesh` — the level's navigation mesh (see navmesh.md): the file `lecodes navmesh bake`
268
225
  * writes, the agent size it is built for, and the named areas. Absent = no navigation. */
269
226
  export type SceneNavmeshDef = {
@@ -276,7 +233,9 @@ export type SceneNavmeshDef = {
276
233
  }
277
234
 
278
235
  export type SceneDef = {
279
- env?: SceneOptions & { lightmap?: SceneLightmapDef, navmesh?: SceneNavmeshDef, foliage?: FoliageOptions }
236
+ /** `lightmap` = the level's baked lighting (engines/bake): `bake('./assets/lightmap/level.bake', { …options })`, the
237
+ * macro that reads the bake's manifest at compile time (`LightmapDef`). Absent = real-time only. */
238
+ env?: SceneOptions & { lightmap?: LightmapDef, navmesh?: SceneNavmeshDef, foliage?: FoliageOptions }
280
239
  camera?: SceneCameraDef
281
240
  nodes?: Record<string, SceneNodeDef>
282
241
  }
@@ -503,12 +462,23 @@ export const createSource = (path: string, def: SceneNodeDef, lightmap: boolean)
503
462
  if (def.model !== undefined) return loadModel(def.model, lightmap, !!def.foliage)
504
463
  if (def.mesh !== undefined) return createMesh(def.mesh, def.material)
505
464
  if (def.terrain !== undefined) return createTerrain(def.terrain, lightmap)
506
- if (def.light !== undefined) { const { kind: _k, ...opts } = def.light; return Light.sun(opts) }
465
+ if (def.light !== undefined) return createLight(path, def.light)
507
466
  // make/prefab/camera/group nodes are plain wrappers — make/prefab subtrees mount under them
508
467
  // during the build (attachPrefab) or in phase 2 (attachMake); a camera node drives scene.camera
509
468
  return new Node()
510
469
  }
511
470
 
471
+ /** A `light:` block by its kind. The node IS the light: its transform (applyNode) aims a sun / spot and turns a
472
+ * point light's `bakeArea` panel (local XZ, emitting along local -Y). */
473
+ const createLight = (path: string, def: LightDef): Light => {
474
+ switch (def.kind) {
475
+ case "sun": { const { kind: _k, ...opts } = def; return Light.sun(opts) }
476
+ case "point": { const { kind: _k, ...opts } = def; return Light.point(opts) }
477
+ case "spot": { const { kind: _k, ...opts } = def; return Light.spot(opts) }
478
+ }
479
+ throw new Error(`Scene node "${path}": light kind "${String((def as { kind?: unknown }).kind)}" is not sun, point or spot`)
480
+ }
481
+
512
482
  /** Edit mode: play the preview pose on a model node (`editor: { clip, time }`) — idempotent, the
513
483
  * harness re-applies it on inspector edits. No clip = back to the rest pose. */
514
484
  export const applyEditorPose = (node: Node, pose: { clip?: string, time?: number } | undefined): void => {
@@ -1034,10 +1004,7 @@ const instantiate = async (def: SceneDef, editorRuns: EditorRun[]): Promise<{ sc
1034
1004
  }
1035
1005
 
1036
1006
  // the level is complete: apply the bake — or, under `lecodes lightmap bake`, run it
1037
- if (lm && lightmap) {
1038
- await Lightmap.load(scene, { data: lightmap.data, light: lightmap.light, aux: lightmap.aux, direct: lightmap.direct, indirect: lightmap.indirect, probes: lightmap.probes, volume: lightmap.volume, uv1: lightmap.uv1 },
1039
- { lightScale: lightmap.lightScale, emissiveNits: lightmap.emissiveNits, emissiveBake: lightmap.emissiveBake, transmit: lightmap.transmit, cube: lightmap.cube })
1040
- }
1007
+ if (lm && lightmap) await Lightmap.load(scene, lightmap)
1041
1008
  // navigation: load the baked mesh — or, under `lecodes navmesh bake`, dump the level's collision
1042
1009
  // geometry for it (the static bodies exist by now; edit mode never loads it — v1)
1043
1010
  if (navmesh && !(EDITOR && isEditMode())) {
package/src/version.ts CHANGED
@@ -4,7 +4,7 @@
4
4
  // same manifest). Its MAJOR is the bundle ↔ runtime contract: a bundle's `// sdk:` header line
5
5
  // and a host's embedded version must agree on the major for the host to run the bundle; minor
6
6
  // and patch never gate anything (features are detected, never versioned).
7
- export const SDK_VERSION = "2.4.8"
7
+ export const SDK_VERSION = "2.4.10"
8
8
 
9
9
  /** The major of a semver string, or null when it is not one. */
10
10
  export const sdkMajor = (version: string | null | undefined): number | null => {