lecodes-cli 0.17.2 → 0.18.1
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 +1 -1
- package/dist/index.js +2376 -755
- package/package.json +4 -4
- package/runtime/scene-harness.json +1 -1
- package/runtime/sdk/compile/aspectMacro.ts +52 -8
- package/runtime/sdk/compile/assetMacro.ts +116 -15
- package/runtime/sdk/compile/bundler.ts +39 -4
- package/runtime/sdk/compile/compileProject.ts +16 -1
- package/runtime/sdk/compile/header.ts +6 -1
- package/runtime/sdk/compile/index.ts +31 -0
- package/runtime/sdk/compile/liteMaterial.ts +247 -0
- package/runtime/sdk/compile/sceneEditor.ts +11 -1
- package/runtime/sdk/compile/shaderSchema.ts +202 -0
- package/runtime/sdk/compile/shaderTargets.ts +81 -0
- package/runtime/sdk/core/Aspect.ts +363 -95
- package/runtime/sdk/core/compWrite.ts +42 -0
- package/runtime/sdk/core/fields.ts +1 -1
- package/runtime/sdk/core/time.ts +81 -0
- package/runtime/sdk/g2/Camera2D.ts +8 -1
- package/runtime/sdk/g2/CharacterController2D.ts +253 -53
- package/runtime/sdk/g2/Node2D.ts +80 -10
- package/runtime/sdk/g2/OneWay2D.ts +66 -0
- package/runtime/sdk/g2/Physics2D.ts +240 -30
- package/runtime/sdk/g2/Scene2D.ts +33 -1
- package/runtime/sdk/g2/Shape2D.ts +218 -22
- package/runtime/sdk/g2/Trigger2D.ts +42 -12
- package/runtime/sdk/g2/groups2d.ts +106 -0
- package/runtime/sdk/g2/loop.ts +15 -4
- package/runtime/sdk/gl/Camera.ts +41 -0
- package/runtime/sdk/gl/CameraPlace.ts +52 -0
- package/runtime/sdk/gl/CharacterController.ts +184 -55
- package/runtime/sdk/gl/Gearbox.ts +212 -0
- package/runtime/sdk/gl/Geometry.ts +70 -9
- package/runtime/sdk/gl/IK.ts +193 -174
- package/runtime/sdk/gl/Light.ts +64 -2
- package/runtime/sdk/gl/Lightmap.ts +179 -0
- package/runtime/sdk/gl/Material.ts +36 -0
- package/runtime/sdk/gl/Mesh.ts +6 -23
- package/runtime/sdk/gl/Model.ts +23 -8
- package/runtime/sdk/gl/Node.ts +350 -285
- package/runtime/sdk/gl/Physics.ts +222 -126
- package/runtime/sdk/gl/Scene.ts +175 -8
- package/runtime/sdk/gl/Shape.ts +255 -12
- package/runtime/sdk/gl/Trigger.ts +1 -6
- package/runtime/sdk/gl/Vehicle.ts +473 -0
- package/runtime/sdk/gl/Wheel.ts +240 -0
- package/runtime/sdk/gl/{AnimationClip.ts → animation/AnimationClip.ts} +37 -7
- package/runtime/sdk/gl/animation/Animator.ts +87 -0
- package/runtime/sdk/gl/animation/Layer.ts +29 -0
- package/runtime/sdk/gl/animation/Loop.ts +25 -0
- package/runtime/sdk/gl/animation/Playback.ts +43 -0
- package/runtime/sdk/gl/animation/core.ts +294 -0
- package/runtime/sdk/gl/scenarios.ts +291 -349
- package/runtime/sdk/inject.ts +186 -162
- package/runtime/sdk/runtime/app.ts +13 -0
- package/runtime/sdk/runtime/input.ts +169 -6
- package/runtime/sdk/scene/defineScene.ts +1227 -1016
- package/runtime/sdk/scene/gizmos.ts +148 -0
- package/runtime/sdk/scene/material.ts +188 -0
- package/runtime/sdk-types.json +1 -1
- package/runtime/sdk/gl/Animator.ts +0 -642
- package/runtime/sdk/gl/ModelAnimation.ts +0 -95
|
@@ -58,6 +58,12 @@ export type ParticlesMaterialOptions = {
|
|
|
58
58
|
blend?: "alpha" | "add"
|
|
59
59
|
/** Soft radial falloff toward the sprite edge. Defaults to true without a map, false with one. */
|
|
60
60
|
soft?: boolean
|
|
61
|
+
/** Soft particles: fade each sprite out over this many metres as it cuts into the scene geometry
|
|
62
|
+
* behind it (Unity's "Soft Particles Factor"), so smoke and dust no longer slice through the
|
|
63
|
+
* ground and walls with a hard edge. 0 = off (default). Reads the scene's depth buffer, which the
|
|
64
|
+
* engine keeps bound while any soft system is on screen — the extra depth pass is only paid then.
|
|
65
|
+
* Filament hosts (desktop / web / Android / Apple); web-lite draws such sprites unfaded. */
|
|
66
|
+
depthFade?: number
|
|
61
67
|
/** How each particle is drawn: `'point'` (default — GPU point sprites, cheapest), `'quad'`
|
|
62
68
|
* (real camera-facing quads — no driver size cap, geometric rotation), or `'stretch'`
|
|
63
69
|
* (quads stretched along velocity — sparks, rain, speed streaks). `'ribbon'` is what `Trail`
|
|
@@ -169,6 +175,7 @@ export class Material {
|
|
|
169
175
|
m.uniforms.emissive = options.emissive ?? 0
|
|
170
176
|
m.uniforms.additive = options.blend === "add" ? 1 : 0
|
|
171
177
|
m.uniforms.softness = (options.soft ?? !options.map) ? 1 : 0
|
|
178
|
+
m.uniforms.depthFade = options.depthFade ?? 0
|
|
172
179
|
if (quad) {
|
|
173
180
|
m.uniforms.mode = options.render === "ribbon" ? 2 : options.render === "stretch" ? 1 : 0
|
|
174
181
|
m.uniforms.stretch = options.stretch ?? (options.render === "stretch" ? 0.05 : 0)
|
|
@@ -188,6 +195,24 @@ export class Material {
|
|
|
188
195
|
return m
|
|
189
196
|
}
|
|
190
197
|
|
|
198
|
+
/** The lightmap material (docs/lightmap-plan.md): PBR base colour × a baked shadow/AO atlas on UV1.
|
|
199
|
+
* Models take it through `Model.load(…, { lightmap: true })`; `Lightmap.load` builds one per static
|
|
200
|
+
* Mesh. Parameters use gltfio's names (`baseColorFactor`, `baseColorMap`, `roughnessFactor`,
|
|
201
|
+
* `metallicFactor`) plus `lightmap`, `lightmapST`, `ambientScale`, `sunStrength` (the sun / IBL terms come from
|
|
202
|
+
* filament's per-frame uniforms inside the shader). */
|
|
203
|
+
static lightmap(): Material {
|
|
204
|
+
// Inline literal: the compiler's preload header captures the shader name from it.
|
|
205
|
+
const m = new Material({ _id: _creatorUtils.fetchLocal("lightmap.filamat") } as any)
|
|
206
|
+
m.set("baseColorFactor", "#ffffffff").set("roughnessFactor", 1).set("metallicFactor", 0)
|
|
207
|
+
m.set("lightmapST", [ 1, 1, 0, 0 ]).set("ambientScale", 1).set("sunStrength", 1).set("hasNormalMap", 0)
|
|
208
|
+
return m
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/** @internal the shared template instance `Model.load({ lightmap })` hands the engine (its MATERIAL is what
|
|
212
|
+
* the GLB provider instantiates; the template itself never draws). */
|
|
213
|
+
static _lightmapTemplate(): Material { return (Material._lmTemplate ??= Material.lightmap()) }
|
|
214
|
+
private static _lmTemplate: Material | undefined
|
|
215
|
+
|
|
191
216
|
/** Shadow-catcher material (transparent except where shadows fall). */
|
|
192
217
|
static shadow(color: ColorInput = "#000000aa"): Material {
|
|
193
218
|
const m = new Material({ _id: _creatorUtils.fetchLocal("shadow.filamat") } as any)
|
|
@@ -198,6 +223,17 @@ export class Material {
|
|
|
198
223
|
|
|
199
224
|
/** Load a custom compiled shader (.mat URL) as a material. */
|
|
200
225
|
static load(url: string): Promise<Material> {
|
|
226
|
+
// "id:<n>" — the artifact is a LOCAL file the host already resolved through fetchLocal (a
|
|
227
|
+
// compile with no asset server: the desktop host reads it from next to the bundle). There is
|
|
228
|
+
// nothing to fetch, so the material is ready synchronously; -1 means the host found no such
|
|
229
|
+
// file, which would otherwise reach createMaterial as a bad handle.
|
|
230
|
+
if (url.startsWith("id:")) {
|
|
231
|
+
const id = Number(url.slice(3))
|
|
232
|
+
if (!(id >= 0)) {
|
|
233
|
+
return Promise.reject(new Error(`Shader not found next to the app (${url}). Compile it with \`lecodes shaders compile\`.`))
|
|
234
|
+
}
|
|
235
|
+
return Promise.resolve(new Material({ _id: id } as any))
|
|
236
|
+
}
|
|
201
237
|
return fetch(url, { useOnce: true }).then((resp) => {
|
|
202
238
|
if (resp.status >= 400) return Promise.reject(new Error(`Failed to fetch shader from ${url}. HTTP ${resp.status}`))
|
|
203
239
|
return new Material(resp)
|
package/runtime/sdk/gl/Mesh.ts
CHANGED
|
@@ -24,7 +24,6 @@ export type MeshOptions = {
|
|
|
24
24
|
|
|
25
25
|
export class Mesh extends Node {
|
|
26
26
|
private _geometry?: Geometry
|
|
27
|
-
private _materials: Material[] = []
|
|
28
27
|
|
|
29
28
|
constructor(geometry?: Geometry, material?: Material) {
|
|
30
29
|
super()
|
|
@@ -32,29 +31,15 @@ export class Mesh extends Node {
|
|
|
32
31
|
const mat = material ?? Material.unlit()
|
|
33
32
|
this._geometry = geometry
|
|
34
33
|
this._materials = [ mat ]
|
|
35
|
-
_creator.setMesh(this.id, Material.idOf(mat), geometry.vertices, geometry.normals, geometry.indices, geometry.uv, geometry._meshKind)
|
|
34
|
+
_creator.setMesh(this.id, Material.idOf(mat), geometry.vertices, geometry.normals, geometry.indices, geometry.uv, geometry._meshKind, geometry.uv1)
|
|
36
35
|
}
|
|
37
36
|
}
|
|
38
37
|
|
|
39
38
|
override get geometry(): Geometry | undefined { return this._geometry }
|
|
40
39
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
setMaterial(material: Material, index = 0): this {
|
|
45
|
-
this._materials[index] = material
|
|
46
|
-
_creator.setMaterial(this.id, Material.idOf(material), index)
|
|
47
|
-
return this
|
|
48
|
-
}
|
|
49
|
-
getMaterial(index = 0): Material | null {
|
|
50
|
-
const existing = this._materials[index]
|
|
51
|
-
if (existing) return existing
|
|
52
|
-
const id = _creator.getMaterial(this.id, index)
|
|
53
|
-
if (id === 0) return null
|
|
54
|
-
const mat = new Material(id)
|
|
55
|
-
this._materials[index] = mat
|
|
56
|
-
return mat
|
|
57
|
-
}
|
|
40
|
+
/** A Mesh always carries a material (slot 0) — see Node.setMaterial for the slot API. */
|
|
41
|
+
override get material(): Material { return this._materials![0]! }
|
|
42
|
+
override set material(m: Material) { this.setMaterial(m, 0) }
|
|
58
43
|
|
|
59
44
|
set culling(v: boolean) { _creator.setCulling(this.id, v) }
|
|
60
45
|
set castShadows(v: boolean) { _creator.setCastShadows(this.id, v) }
|
|
@@ -63,10 +48,8 @@ export class Mesh extends Node {
|
|
|
63
48
|
// --- factories ---
|
|
64
49
|
|
|
65
50
|
static box(options: MeshOptions & { size?: Vec3Like | number } = {}): Mesh {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
if (sx !== 1 || sy !== 1 || sz !== 1) geo.scale(sx, sy, sz)
|
|
69
|
-
return apply(new Mesh(geo, options.material), options)
|
|
51
|
+
// the size goes into the geometry (not a scale after): the lightmap chart is laid out per face area
|
|
52
|
+
return apply(new Mesh(Geometry.box(options.size ?? 1), options.material), options)
|
|
70
53
|
}
|
|
71
54
|
|
|
72
55
|
static sphere(options: MeshOptions & SphereOptions = {}): Mesh {
|
package/runtime/sdk/gl/Model.ts
CHANGED
|
@@ -1,20 +1,24 @@
|
|
|
1
1
|
// A loaded GLB model — its own node kind (a GLB is a node hierarchy with baked animation clips),
|
|
2
2
|
// distinct from Mesh (raw primitives, no animation). Animation is always present, reached as
|
|
3
|
-
// model.anim:
|
|
3
|
+
// model.anim (an Animator over the GLB's clips — crossfades, blend spaces, layers when you need them):
|
|
4
4
|
// const hero = await Model.load(asset('./hero.glb'))
|
|
5
|
-
// hero.anim.play('Run')
|
|
5
|
+
// hero.anim.play('Run', { loop: true })
|
|
6
6
|
|
|
7
7
|
import { fetch, type FetchResponse } from "../runtime/fetch"
|
|
8
8
|
import { Node } from "./Node"
|
|
9
|
-
import {
|
|
9
|
+
import { Animator } from "./animation/Animator"
|
|
10
|
+
import { Material } from "./Material"
|
|
10
11
|
|
|
11
12
|
export class Model extends Node {
|
|
12
|
-
/**
|
|
13
|
-
|
|
13
|
+
/** The model's Animator — always present, its clip table = the GLB's embedded clips. Configure
|
|
14
|
+
* more (external clips, blend spaces, layers) with `model.aspect(Animator, {...})`. */
|
|
15
|
+
declare readonly anim: Animator
|
|
16
|
+
/** @internal loaded through the lightmap material (docs/lightmap-plan.md); clones inherit it. */
|
|
17
|
+
_lightmapped = false
|
|
14
18
|
|
|
15
19
|
constructor(internalId: number) {
|
|
16
20
|
super(internalId)
|
|
17
|
-
this.aspect(
|
|
21
|
+
this.aspect(Animator)
|
|
18
22
|
}
|
|
19
23
|
|
|
20
24
|
/** Duplicate this model — a deep copy of the GLB (meshes, skeleton, animation clips), attached to
|
|
@@ -23,13 +27,21 @@ export class Model extends Node {
|
|
|
23
27
|
clone(): Model {
|
|
24
28
|
const id = _creator.cloneEntity(this.id)
|
|
25
29
|
_creator.setGlbCulling(id, false)
|
|
26
|
-
|
|
30
|
+
const m = new Model(id)
|
|
31
|
+
m._lightmapped = this._lightmapped
|
|
32
|
+
return m
|
|
27
33
|
}
|
|
28
34
|
|
|
29
35
|
/** Load a GLB model. Returns its root as a Model; play its baked clips via model.anim. */
|
|
30
36
|
static load(
|
|
31
37
|
source: string | FetchResponse,
|
|
32
|
-
options: {
|
|
38
|
+
options: {
|
|
39
|
+
culling?: boolean
|
|
40
|
+
onProgress?: (p: { loaded: number, total?: number }) => void
|
|
41
|
+
/** Load through the lightmap material so `Lightmap.load` can bind a baked atlas (the GLB needs
|
|
42
|
+
* TEXCOORD_1 — `lecodes assets doctor --lightmap-uv`). Hosts without lightmap support ignore it. */
|
|
43
|
+
lightmap?: boolean
|
|
44
|
+
} = {},
|
|
33
45
|
): Promise<Model> {
|
|
34
46
|
if (typeof source === "string") {
|
|
35
47
|
return fetch(source, { useOnce: true, onProgress: options.onProgress }).then((resp) => {
|
|
@@ -38,9 +50,12 @@ export class Model extends Node {
|
|
|
38
50
|
})
|
|
39
51
|
}
|
|
40
52
|
return new Promise<Model>((resolve, reject) => {
|
|
53
|
+
const lightmapped = !!options.lightmap && !!_creator.setNextGlbLightmapped
|
|
54
|
+
if (lightmapped) _creator.setNextGlbLightmapped!(Material.idOf(Material._lightmapTemplate()))
|
|
41
55
|
_creator.createGlb((source as unknown as { _id: number })._id, (entityId: number) => {
|
|
42
56
|
if (entityId === 0) { reject(new Error("Failed to load GLB")); return }
|
|
43
57
|
const model = new Model(entityId)
|
|
58
|
+
model._lightmapped = lightmapped
|
|
44
59
|
if (options.culling !== true) _creator.setGlbCulling(entityId, false)
|
|
45
60
|
resolve(model)
|
|
46
61
|
}, reject)
|