lecodes-sdk 1.2.0 → 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 +5 -2
- package/dist/editor.d.ts +12 -0
- package/dist/global.d.ts +4 -8
- package/dist/host.d.ts +2 -3
- package/dist/types/animate/tween/Animation.d.ts +0 -3
- package/dist/types/animate/tween/animateValue.d.ts +4 -2
- package/dist/types/animate/tween/easing.d.ts +8 -0
- package/dist/types/animate/tween/spec.d.ts +15 -7
- package/dist/types/audio/audio.d.ts +2 -1
- package/dist/types/canvas/Canvas.d.ts +40 -108
- package/dist/types/canvas/gen/cssColor.d.ts +17 -0
- package/dist/types/canvas/gen/recorder.d.ts +118 -0
- package/dist/types/canvas/gen/spec.d.ts +144 -0
- package/dist/types/core/color.d.ts +3 -1
- package/dist/types/core/pins.d.ts +18 -0
- package/dist/types/g2/Node2D.d.ts +5 -8
- package/dist/types/g2/Scene2D.d.ts +6 -2
- package/dist/types/gl/Foliage.d.ts +30 -7
- package/dist/types/gl/Light.d.ts +8 -0
- package/dist/types/gl/Lightmap.d.ts +13 -2
- package/dist/types/gl/Material.d.ts +17 -3
- package/dist/types/gl/Model.d.ts +6 -2
- package/dist/types/gl/Node.d.ts +3 -6
- package/dist/types/gl/Scene.d.ts +41 -13
- package/dist/types/gl/Texture.d.ts +1 -1
- package/dist/types/gl/animation/Locomotion.d.ts +8 -1
- package/dist/types/inject.d.ts +11 -11
- package/dist/types/inject.editor.d.ts +1 -0
- package/dist/types/net/core.d.ts +7 -0
- package/dist/types/plugin.d.ts +76 -0
- package/dist/types/plugins/gen/camera/sdk/camera.d.ts +24 -0
- package/dist/types/plugins/gen/camera/sdk/camera.gen.d.ts +25 -0
- package/dist/types/plugins/{geolocation.d.ts → gen/geolocation/sdk/geolocation.d.ts} +2 -20
- package/dist/types/plugins/gen/geolocation/sdk/geolocation.gen.d.ts +31 -0
- package/dist/types/plugins/{map.d.ts → gen/map/sdk/map.d.ts} +9 -50
- package/dist/types/plugins/gen/map/sdk/map.gen.d.ts +53 -0
- package/dist/types/plugins/gen/push/sdk/push.d.ts +23 -0
- package/dist/types/plugins/gen/push/sdk/push.gen.d.ts +35 -0
- package/dist/types/plugins/{qr.d.ts → gen/qr-scanner/sdk/qr-scanner.d.ts} +2 -3
- package/dist/types/plugins/gen/qr-scanner/sdk/qr-scanner.gen.d.ts +15 -0
- package/dist/types/runtime/app.d.ts +9 -2
- package/dist/types/runtime/fetch.d.ts +2 -0
- package/dist/types/runtime/input.d.ts +1 -1
- package/dist/types/runtime/media.d.ts +6 -10
- package/dist/types/runtime/misc.d.ts +4 -1
- package/dist/types/runtime/net.d.ts +3 -2
- package/dist/types/runtime/touch.d.ts +32 -0
- package/dist/types/scene/defineScene.d.ts +43 -2
- package/dist/types/scene/editor.d.ts +52 -0
- package/dist/types/scene/gizmos.d.ts +7 -4
- package/dist/types/ui/NativeView.d.ts +6 -4
- package/dist/types/ui/UI.d.ts +1 -1
- package/dist/types/ui/UIBottomSheet.d.ts +6 -12
- package/dist/types/ui/UIButton.d.ts +12 -14
- package/dist/types/ui/UIContainer.d.ts +0 -6
- package/dist/types/ui/UIImage.d.ts +1 -4
- package/dist/types/ui/UIInput.d.ts +8 -24
- package/dist/types/ui/UIModal.d.ts +0 -2
- package/dist/types/ui/UINode.d.ts +70 -56
- package/dist/types/ui/UIPager.d.ts +28 -27
- package/dist/types/ui/UIPopover.d.ts +0 -2
- package/dist/types/ui/UIScreen.d.ts +12 -17
- package/dist/types/ui/UIScrollable.d.ts +1 -4
- package/dist/types/ui/UIText.d.ts +0 -2
- package/dist/types/ui/UIVideo.d.ts +3 -5
- package/dist/types/ui/UIVirtualizedList.d.ts +14 -16
- package/dist/types/ui/UIWidget.d.ts +10 -9
- package/dist/types/ui/colorKeys.gen.d.ts +9 -0
- package/dist/types/ui/presentable.d.ts +46 -32
- package/dist/types/ui/router.d.ts +18 -7
- package/dist/types/ui/styleColor.d.ts +1 -0
- package/dist/types/ui/transitions.d.ts +18 -0
- package/dist/types/ui/tree.d.ts +75 -0
- package/dist/types/version.d.ts +10 -0
- package/dist/types.json +1 -1
- package/package.json +12 -3
- package/prompts/2d.md +2 -6
- package/prompts/3d.md +1 -5
- package/prompts/README.md +1 -1
- package/prompts/canvas.md +9 -8
- package/prompts/compose.ts +1 -1
- package/prompts/core.md +3 -3
- package/prompts/design.md +1 -1
- package/prompts/dist/2d-game.md +473 -239
- package/prompts/dist/3d-app.md +553 -205
- package/prompts/dist/ar-app.md +435 -202
- package/prompts/dist/design.md +113 -95
- package/prompts/dist/ui-app.md +386 -170
- package/prompts/ui-design.md +2 -3
- package/prompts/ui.md +45 -37
- package/src/animate/tween/Animation.ts +34 -150
- package/src/animate/tween/Timeline.ts +175 -175
- package/src/animate/tween/animateValue.ts +6 -3
- package/src/animate/tween/easing.ts +10 -3
- package/src/animate/tween/spec.ts +41 -15
- package/src/audio/Sound.ts +3 -3
- package/src/audio/audio.ts +2 -1
- package/src/bridges/2d.d.ts +317 -0
- package/src/bridges/app.d.ts +91 -0
- package/src/bridges/audio.d.ts +97 -0
- package/src/bridges/canvas.d.ts +79 -0
- package/src/bridges/device.d.ts +72 -0
- package/src/bridges/fetch.d.ts +80 -0
- package/src/bridges/files.d.ts +70 -0
- package/src/bridges/gl.d.ts +1133 -0
- package/src/bridges/input.d.ts +72 -0
- package/src/bridges/media.d.ts +55 -0
- package/src/bridges/nav.d.ts +71 -0
- package/src/bridges/net.d.ts +52 -0
- package/src/bridges/service.d.ts +52 -0
- package/src/bridges/socket.d.ts +31 -0
- package/src/bridges/storage.d.ts +33 -0
- package/src/bridges/tree.d.ts +301 -0
- package/src/bridges/types.d.ts +49 -0
- package/src/canvas/Canvas.ts +114 -159
- package/src/canvas/gen/cssColor.ts +224 -0
- package/src/canvas/gen/recorder.ts +212 -0
- package/src/canvas/gen/spec.ts +201 -0
- package/src/chisel.ts +193 -0
- package/src/compile/assetMacro.ts +1 -1
- package/src/compile/bundler.ts +11 -2
- package/src/compile/compileProject.ts +43 -4
- package/src/compile/fontMacro.ts +3 -4
- package/src/compile/header.ts +26 -5
- package/src/compile/index.ts +3 -1
- package/src/compile/liteMaterial.ts +1 -1
- package/src/compile/sceneEditor.ts +11 -26
- package/src/core/color.ts +73 -30
- package/src/core/pins.ts +51 -0
- package/src/core/signals.ts +8 -1
- package/src/g2/CharacterController2D.ts +3 -3
- package/src/g2/Node2D.ts +57 -39
- package/src/g2/Physics2D.ts +2 -2
- package/src/g2/Scene2D.ts +35 -23
- package/src/g2/Texture2D.ts +1 -1
- package/src/g2/loop.ts +4 -4
- package/src/gl/CameraPlace.ts +52 -52
- package/src/gl/Foliage.ts +72 -17
- package/src/gl/Geometry.ts +1 -2
- package/src/gl/Light.ts +10 -0
- package/src/gl/Lightmap.ts +45 -29
- package/src/gl/Material.ts +95 -51
- package/src/gl/Mesh.ts +120 -120
- package/src/gl/Model.ts +21 -16
- package/src/gl/Node.ts +91 -24
- package/src/gl/Particles.ts +1 -1
- package/src/gl/Scene.ts +100 -44
- package/src/gl/Texture.ts +8 -7
- package/src/gl/animation/AnimationClip.ts +1 -1
- package/src/gl/animation/DynamicBone.ts +482 -482
- package/src/gl/animation/Locomotion.ts +8 -3
- package/src/gl/nav/NavMesh.ts +3 -4
- package/src/gl/physics/Physics.ts +2 -2
- package/src/gl/physics/physicsEvents.ts +3 -3
- package/src/gl/scenarios.ts +291 -291
- package/src/gl/terrain/Terrain.ts +4 -5
- package/src/gl/touch.ts +14 -15
- package/src/host.d.ts +2 -3
- package/src/inject.editor.ts +7 -0
- package/src/inject.ts +13 -16
- package/src/net/core.ts +6 -5
- package/src/net/index.ts +1 -1
- package/src/net/replication.ts +1 -1
- package/src/plugin.ts +191 -0
- package/src/plugins/gen/camera/contract.d.ts +27 -0
- package/src/plugins/gen/camera/sdk/camera.gen.ts +46 -0
- package/src/plugins/gen/camera/sdk/camera.ts +57 -0
- package/src/plugins/gen/geolocation/contract.d.ts +50 -0
- package/src/plugins/gen/geolocation/sdk/geolocation.gen.ts +54 -0
- package/src/plugins/{geolocation.ts → gen/geolocation/sdk/geolocation.ts} +22 -43
- package/src/plugins/gen/map/contract.d.ts +144 -0
- package/src/plugins/gen/map/sdk/map.gen.ts +88 -0
- package/src/plugins/{map.ts → gen/map/sdk/map.ts} +68 -102
- package/src/plugins/gen/push/contract.d.ts +61 -0
- package/src/plugins/gen/push/sdk/push.gen.ts +60 -0
- package/src/plugins/gen/push/sdk/push.ts +105 -0
- package/src/plugins/gen/qr-scanner/contract.d.ts +16 -0
- package/src/plugins/gen/qr-scanner/sdk/qr-scanner.gen.ts +29 -0
- package/src/plugins/gen/qr-scanner/sdk/qr-scanner.ts +52 -0
- package/src/plugins/permission.ts +5 -4
- package/src/runtime/app.ts +20 -9
- package/src/runtime/appEvents.ts +5 -4
- package/src/runtime/channel.ts +18 -15
- package/src/runtime/clipboard.ts +4 -3
- package/src/runtime/datetime.ts +2 -1
- package/src/runtime/device.ts +17 -15
- package/src/runtime/fetch.ts +30 -20
- package/src/runtime/files.ts +16 -15
- package/src/runtime/input.ts +12 -10
- package/src/runtime/media.ts +50 -46
- package/src/runtime/misc.ts +7 -3
- package/src/runtime/net.ts +8 -7
- package/src/runtime/rpc.ts +1 -3
- package/src/runtime/service.ts +19 -14
- package/src/runtime/share.ts +4 -3
- package/src/runtime/storage.ts +6 -4
- package/src/runtime/touch.ts +32 -0
- package/src/scene/defineScene.ts +61 -365
- package/src/scene/editor.ts +408 -0
- package/src/scene/editorPlugins.ts +3 -3
- package/src/scene/gizmos.ts +15 -9
- package/src/server/db/marci/query.ts +1 -1
- package/src/server/host.ts +1 -1
- package/src/server/runtime.ts +1 -1
- package/src/ui/NativeView.ts +72 -25
- package/src/ui/UI.ts +3 -3
- package/src/ui/UIBottomSheet.ts +16 -17
- package/src/ui/UIButton.ts +54 -16
- package/src/ui/UIContainer.ts +0 -6
- package/src/ui/UIImage.ts +34 -30
- package/src/ui/UIInput.ts +29 -37
- package/src/ui/UIModal.ts +1 -3
- package/src/ui/UINode.ts +347 -297
- package/src/ui/UIPager.ts +93 -78
- package/src/ui/UIPopover.ts +0 -2
- package/src/ui/UIScreen.ts +41 -36
- package/src/ui/UIScrollable.ts +19 -13
- package/src/ui/UISpacer.ts +1 -1
- package/src/ui/UITabs.ts +8 -6
- package/src/ui/UIText.ts +7 -17
- package/src/ui/UIVideo.ts +30 -27
- package/src/ui/UIVirtualizedList.ts +58 -59
- package/src/ui/UIWidget.ts +38 -18
- package/src/ui/colorKeys.gen.ts +37 -0
- package/src/ui/fonts.ts +2 -2
- package/src/ui/presentable.ts +59 -42
- package/src/ui/router.ts +52 -35
- package/src/ui/styleColor.ts +56 -0
- package/src/ui/theme.ts +7 -6
- package/src/ui/transitions.ts +249 -0
- package/src/ui/tree.ts +346 -0
- package/src/version.ts +24 -0
- package/tests/helpers/engineWorld.ts +11 -0
- package/tests/helpers/fakeTree.ts +353 -0
- package/tests/helpers/hostStubs.ts +31 -0
- package/tests/helpers/index.ts +14 -0
- package/tests/helpers/memoryMarci.ts +124 -0
- package/tests/helpers/phases.ts +23 -0
- package/tests/helpers/preload.ts +18 -0
- package/tests/helpers/stubApp.ts +2 -0
- package/tests/helpers/stubDevice.ts +2 -0
- package/tests/helpers/stubFetch.ts +2 -0
- package/tests/helpers/stubInput.ts +2 -0
- package/dist/inject.js +0 -4629
- package/dist/types/core/registry.d.ts +0 -7
- package/dist/types/plugins/camera.d.ts +0 -25
- package/dist/types/plugins/push.d.ts +0 -46
- package/src/bridges.d.ts +0 -1769
- package/src/compile/__tests__/assetIconMacro.test.ts +0 -219
- package/src/compile/__tests__/assetMacro.test.ts +0 -100
- package/src/compile/__tests__/assetName.test.ts +0 -55
- package/src/compile/__tests__/compile.test.ts +0 -310
- package/src/compile/__tests__/detectEntry.test.ts +0 -151
- package/src/compile/__tests__/fontMacro.test.ts +0 -199
- package/src/compile/__tests__/serverSplit.test.ts +0 -27
- package/src/core/__tests__/stateMachine.test.ts +0 -132
- package/src/core/registry.ts +0 -23
- package/src/plugins/camera.ts +0 -81
- package/src/plugins/push.ts +0 -132
- package/src/plugins/qr.ts +0 -73
package/src/gl/Mesh.ts
CHANGED
|
@@ -1,120 +1,120 @@
|
|
|
1
|
-
// A renderable 3D node from raw geometry — the primitive shapes (box/sphere/cylinder/capsule/plane) and
|
|
2
|
-
// Mesh.from for a custom Geometry. A GLB import is a different kind: see Model.load.
|
|
3
|
-
|
|
4
|
-
import { cx, cy, cz, type Vec3Like } from "../math/vec"
|
|
5
|
-
import {
|
|
6
|
-
Geometry,
|
|
7
|
-
type CapsuleOptions,
|
|
8
|
-
type CylinderOptions,
|
|
9
|
-
type PlaneOptions,
|
|
10
|
-
type SphereOptions,
|
|
11
|
-
} from "./Geometry"
|
|
12
|
-
import { Material } from "./Material"
|
|
13
|
-
import { Node } from "./Node"
|
|
14
|
-
|
|
15
|
-
/** Common transform/render options every primitive factory accepts. */
|
|
16
|
-
export type MeshOptions = {
|
|
17
|
-
material?: Material
|
|
18
|
-
position?: Vec3Like
|
|
19
|
-
eulerAngles?: Vec3Like
|
|
20
|
-
scale?: Vec3Like | number
|
|
21
|
-
name?: string
|
|
22
|
-
castShadows?: boolean
|
|
23
|
-
receiveShadows?: boolean
|
|
24
|
-
/** Coarse draw order, 0 (first) … 7 (last); default 4. See `Mesh.renderPriority`. */
|
|
25
|
-
renderPriority?: number
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export class Mesh extends Node {
|
|
29
|
-
private _geometry?: Geometry
|
|
30
|
-
|
|
31
|
-
constructor(geometry?: Geometry, material?: Material) {
|
|
32
|
-
super()
|
|
33
|
-
if (geometry) {
|
|
34
|
-
const mat = material ?? Material.unlit()
|
|
35
|
-
this._geometry = geometry
|
|
36
|
-
this._materials = [ mat ]
|
|
37
|
-
_creator.setMesh(this.id, Material.idOf(mat), geometry.vertices, geometry.normals, geometry.indices, geometry.uv, geometry._meshKind, geometry.uv1, geometry.colors)
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
override get geometry(): Geometry | undefined { return this._geometry }
|
|
42
|
-
/** Replace the geometry in place — the node, its transform and its material stay, the vertex and
|
|
43
|
-
* index buffers are rebuilt. What an editor overlay or a debug drawer redraws with. */
|
|
44
|
-
setGeometry(geometry: Geometry): this {
|
|
45
|
-
this._geometry = geometry
|
|
46
|
-
_creator.setMesh(this.id, Material.idOf(this._materials![0]!), geometry.vertices, geometry.normals, geometry.indices, geometry.uv, geometry._meshKind, geometry.uv1, geometry.colors)
|
|
47
|
-
// the host rebuilds the renderable for a new geometry (creator-gl createMesh destroys + builds), which
|
|
48
|
-
// resets its priority / culling / shadow flags to the defaults — an overlay redrawn every frame lost its
|
|
49
|
-
// renderPriority 7 after one frame and hid inside the model. What was set on this mesh is set again.
|
|
50
|
-
if (this._renderPriority !== undefined) this.renderPriority = this._renderPriority
|
|
51
|
-
if (this._culling !== undefined) this.culling = this._culling
|
|
52
|
-
if (this._castShadows !== undefined) this.castShadows = this._castShadows
|
|
53
|
-
if (this._receiveShadows !== undefined) this.receiveShadows = this._receiveShadows
|
|
54
|
-
return this
|
|
55
|
-
}
|
|
56
|
-
private _renderPriority?: number
|
|
57
|
-
private _culling?: boolean
|
|
58
|
-
private _castShadows?: boolean
|
|
59
|
-
private _receiveShadows?: boolean
|
|
60
|
-
|
|
61
|
-
/** A Mesh always carries a material (slot 0) — see Node.setMaterial for the slot API. */
|
|
62
|
-
override get material(): Material { return this._materials![0]! }
|
|
63
|
-
override set material(m: Material) { this.setMaterial(m, 0) }
|
|
64
|
-
|
|
65
|
-
set culling(v: boolean) { this._culling = v; _creator.setCulling(this.id, v) }
|
|
66
|
-
/** Coarse draw order within the frame: 0 draws first, 7 last, 4 is the default (Filament's
|
|
67
|
-
* renderable priority; within one priority opaque draws sort front-to-back, blended back-to-front).
|
|
68
|
-
* Something drawn over the scene with `Material.depthTest = false` goes in 7, so nothing drawn
|
|
69
|
-
* after it can cover it. Write-only; a host that predates the call ignores it. */
|
|
70
|
-
set renderPriority(v: number) {
|
|
71
|
-
this._renderPriority = v
|
|
72
|
-
if (_creator.setRenderPriority) _creator.setRenderPriority(this.id, Math.max(0, Math.min(7, Math.round(v))))
|
|
73
|
-
else if (!Mesh._warnedPriority) { Mesh._warnedPriority = true; console.warn("[mesh] this host has no setRenderPriority — renderPriority ignored") }
|
|
74
|
-
}
|
|
75
|
-
private static _warnedPriority = false
|
|
76
|
-
set castShadows(v: boolean) { this._castShadows = v; _creator.setCastShadows(this.id, v) }
|
|
77
|
-
set receiveShadows(v: boolean) { this._receiveShadows = v; _creator.setReceiveShadows(this.id, v) }
|
|
78
|
-
|
|
79
|
-
// --- factories ---
|
|
80
|
-
|
|
81
|
-
static box(options: MeshOptions & { size?: Vec3Like | number } = {}): Mesh {
|
|
82
|
-
// the size goes into the geometry (not a scale after): the lightmap chart is laid out per face area
|
|
83
|
-
return apply(new Mesh(Geometry.box(options.size ?? 1), options.material), options)
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
static sphere(options: MeshOptions & SphereOptions = {}): Mesh {
|
|
87
|
-
return apply(new Mesh(Geometry.sphere(options), options.material), options)
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
static cylinder(options: MeshOptions & CylinderOptions = {}): Mesh {
|
|
91
|
-
return apply(new Mesh(Geometry.cylinder(options), options.material), options)
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
/** A capsule along Y (`radius`, `length` between the cap centres) — a character's or a collider's shape in one mesh. */
|
|
95
|
-
static capsule(options: MeshOptions & CapsuleOptions = {}): Mesh {
|
|
96
|
-
return apply(new Mesh(Geometry.capsule(options), options.material), options)
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
static plane(options: MeshOptions & PlaneOptions = {}): Mesh {
|
|
100
|
-
return apply(new Mesh(Geometry.plane(options), options.material), options)
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
/** Wrap a custom Geometry. */
|
|
104
|
-
static from(geometry: Geometry, options: MeshOptions = {}): Mesh {
|
|
105
|
-
return apply(new Mesh(geometry, options.material), options)
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
const sizeToVec = (s: Vec3Like | number): [number, number, number] => (typeof s === "number" ? [ s, s, s ] : [ cx(s), cy(s), cz(s) ])
|
|
110
|
-
|
|
111
|
-
const apply = (mesh: Mesh, o: MeshOptions): Mesh => {
|
|
112
|
-
if (o.position) mesh.position = o.position
|
|
113
|
-
if (o.eulerAngles) mesh.eulerAngles = o.eulerAngles
|
|
114
|
-
if (o.scale !== undefined) mesh.scale = typeof o.scale === "number" ? [ o.scale, o.scale, o.scale ] : o.scale
|
|
115
|
-
if (o.name !== undefined) mesh.name = o.name
|
|
116
|
-
if (o.castShadows !== undefined) mesh.castShadows = o.castShadows
|
|
117
|
-
if (o.receiveShadows !== undefined) mesh.receiveShadows = o.receiveShadows
|
|
118
|
-
if (o.renderPriority !== undefined) mesh.renderPriority = o.renderPriority
|
|
119
|
-
return mesh
|
|
120
|
-
}
|
|
1
|
+
// A renderable 3D node from raw geometry — the primitive shapes (box/sphere/cylinder/capsule/plane) and
|
|
2
|
+
// Mesh.from for a custom Geometry. A GLB import is a different kind: see Model.load.
|
|
3
|
+
|
|
4
|
+
import { cx, cy, cz, type Vec3Like } from "../math/vec"
|
|
5
|
+
import {
|
|
6
|
+
Geometry,
|
|
7
|
+
type CapsuleOptions,
|
|
8
|
+
type CylinderOptions,
|
|
9
|
+
type PlaneOptions,
|
|
10
|
+
type SphereOptions,
|
|
11
|
+
} from "./Geometry"
|
|
12
|
+
import { Material } from "./Material"
|
|
13
|
+
import { Node } from "./Node"
|
|
14
|
+
|
|
15
|
+
/** Common transform/render options every primitive factory accepts. */
|
|
16
|
+
export type MeshOptions = {
|
|
17
|
+
material?: Material
|
|
18
|
+
position?: Vec3Like
|
|
19
|
+
eulerAngles?: Vec3Like
|
|
20
|
+
scale?: Vec3Like | number
|
|
21
|
+
name?: string
|
|
22
|
+
castShadows?: boolean
|
|
23
|
+
receiveShadows?: boolean
|
|
24
|
+
/** Coarse draw order, 0 (first) … 7 (last); default 4. See `Mesh.renderPriority`. */
|
|
25
|
+
renderPriority?: number
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export class Mesh extends Node {
|
|
29
|
+
private _geometry?: Geometry
|
|
30
|
+
|
|
31
|
+
constructor(geometry?: Geometry, material?: Material) {
|
|
32
|
+
super()
|
|
33
|
+
if (geometry) {
|
|
34
|
+
const mat = material ?? Material.unlit()
|
|
35
|
+
this._geometry = geometry
|
|
36
|
+
this._materials = [ mat ]
|
|
37
|
+
_creator.setMesh(this.id, Material.idOf(mat), geometry.vertices, geometry.normals, geometry.indices, geometry.uv, geometry._meshKind, geometry.uv1, geometry.colors)
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
override get geometry(): Geometry | undefined { return this._geometry }
|
|
42
|
+
/** Replace the geometry in place — the node, its transform and its material stay, the vertex and
|
|
43
|
+
* index buffers are rebuilt. What an editor overlay or a debug drawer redraws with. */
|
|
44
|
+
setGeometry(geometry: Geometry): this {
|
|
45
|
+
this._geometry = geometry
|
|
46
|
+
_creator.setMesh(this.id, Material.idOf(this._materials![0]!), geometry.vertices, geometry.normals, geometry.indices, geometry.uv, geometry._meshKind, geometry.uv1, geometry.colors)
|
|
47
|
+
// the host rebuilds the renderable for a new geometry (creator-gl createMesh destroys + builds), which
|
|
48
|
+
// resets its priority / culling / shadow flags to the defaults — an overlay redrawn every frame lost its
|
|
49
|
+
// renderPriority 7 after one frame and hid inside the model. What was set on this mesh is set again.
|
|
50
|
+
if (this._renderPriority !== undefined) this.renderPriority = this._renderPriority
|
|
51
|
+
if (this._culling !== undefined) this.culling = this._culling
|
|
52
|
+
if (this._castShadows !== undefined) this.castShadows = this._castShadows
|
|
53
|
+
if (this._receiveShadows !== undefined) this.receiveShadows = this._receiveShadows
|
|
54
|
+
return this
|
|
55
|
+
}
|
|
56
|
+
private _renderPriority?: number
|
|
57
|
+
private _culling?: boolean
|
|
58
|
+
private _castShadows?: boolean
|
|
59
|
+
private _receiveShadows?: boolean
|
|
60
|
+
|
|
61
|
+
/** A Mesh always carries a material (slot 0) — see Node.setMaterial for the slot API. */
|
|
62
|
+
override get material(): Material { return this._materials![0]! }
|
|
63
|
+
override set material(m: Material) { this.setMaterial(m, 0) }
|
|
64
|
+
|
|
65
|
+
set culling(v: boolean) { this._culling = v; _creator.setCulling(this.id, v) }
|
|
66
|
+
/** Coarse draw order within the frame: 0 draws first, 7 last, 4 is the default (Filament's
|
|
67
|
+
* renderable priority; within one priority opaque draws sort front-to-back, blended back-to-front).
|
|
68
|
+
* Something drawn over the scene with `Material.depthTest = false` goes in 7, so nothing drawn
|
|
69
|
+
* after it can cover it. Write-only; a host that predates the call ignores it. */
|
|
70
|
+
set renderPriority(v: number) {
|
|
71
|
+
this._renderPriority = v
|
|
72
|
+
if (_creator.setRenderPriority) _creator.setRenderPriority(this.id, Math.max(0, Math.min(7, Math.round(v))))
|
|
73
|
+
else if (!Mesh._warnedPriority) { Mesh._warnedPriority = true; console.warn("[mesh] this host has no setRenderPriority — renderPriority ignored") }
|
|
74
|
+
}
|
|
75
|
+
private static _warnedPriority = false
|
|
76
|
+
set castShadows(v: boolean) { this._castShadows = v; _creator.setCastShadows(this.id, v) }
|
|
77
|
+
set receiveShadows(v: boolean) { this._receiveShadows = v; _creator.setReceiveShadows(this.id, v) }
|
|
78
|
+
|
|
79
|
+
// --- factories ---
|
|
80
|
+
|
|
81
|
+
static box(options: MeshOptions & { size?: Vec3Like | number } = {}): Mesh {
|
|
82
|
+
// the size goes into the geometry (not a scale after): the lightmap chart is laid out per face area
|
|
83
|
+
return apply(new Mesh(Geometry.box(options.size ?? 1), options.material), options)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
static sphere(options: MeshOptions & SphereOptions = {}): Mesh {
|
|
87
|
+
return apply(new Mesh(Geometry.sphere(options), options.material), options)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
static cylinder(options: MeshOptions & CylinderOptions = {}): Mesh {
|
|
91
|
+
return apply(new Mesh(Geometry.cylinder(options), options.material), options)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** A capsule along Y (`radius`, `length` between the cap centres) — a character's or a collider's shape in one mesh. */
|
|
95
|
+
static capsule(options: MeshOptions & CapsuleOptions = {}): Mesh {
|
|
96
|
+
return apply(new Mesh(Geometry.capsule(options), options.material), options)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
static plane(options: MeshOptions & PlaneOptions = {}): Mesh {
|
|
100
|
+
return apply(new Mesh(Geometry.plane(options), options.material), options)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** Wrap a custom Geometry. */
|
|
104
|
+
static from(geometry: Geometry, options: MeshOptions = {}): Mesh {
|
|
105
|
+
return apply(new Mesh(geometry, options.material), options)
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const sizeToVec = (s: Vec3Like | number): [number, number, number] => (typeof s === "number" ? [ s, s, s ] : [ cx(s), cy(s), cz(s) ])
|
|
110
|
+
|
|
111
|
+
const apply = (mesh: Mesh, o: MeshOptions): Mesh => {
|
|
112
|
+
if (o.position) mesh.position = o.position
|
|
113
|
+
if (o.eulerAngles) mesh.eulerAngles = o.eulerAngles
|
|
114
|
+
if (o.scale !== undefined) mesh.scale = typeof o.scale === "number" ? [ o.scale, o.scale, o.scale ] : o.scale
|
|
115
|
+
if (o.name !== undefined) mesh.name = o.name
|
|
116
|
+
if (o.castShadows !== undefined) mesh.castShadows = o.castShadows
|
|
117
|
+
if (o.receiveShadows !== undefined) mesh.receiveShadows = o.receiveShadows
|
|
118
|
+
if (o.renderPriority !== undefined) mesh.renderPriority = o.renderPriority
|
|
119
|
+
return mesh
|
|
120
|
+
}
|
package/src/gl/Model.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
// hero.anim.play('Run', { loop: true })
|
|
6
6
|
|
|
7
7
|
import { fetch, type FetchResponse } from "../runtime/fetch"
|
|
8
|
-
import { Node } from "./Node"
|
|
8
|
+
import { Node, nodes } from "./Node"
|
|
9
9
|
import { Animator, _pushLod, type LodMode } from "./animation/Animator"
|
|
10
10
|
import { Material } from "./Material"
|
|
11
11
|
|
|
@@ -20,12 +20,16 @@ export interface TrianglePick {
|
|
|
20
20
|
mesh: string
|
|
21
21
|
primitive: number
|
|
22
22
|
material: string
|
|
23
|
+
/** the node's lightmap group (scene-file extras), when it has one */
|
|
24
|
+
lightmapGroup?: number
|
|
23
25
|
/** triangle index within the primitive (index-buffer order), and whether the ray came from behind */
|
|
24
26
|
triangle: number
|
|
25
27
|
backface: boolean
|
|
26
28
|
distance: number
|
|
27
29
|
point: [number, number, number]
|
|
28
30
|
bary: [number, number, number]
|
|
31
|
+
/** the lightmap uv (TEXCOORD_1) at the hit, when the primitive carries one */
|
|
32
|
+
uv1?: [number, number]
|
|
29
33
|
skin: string
|
|
30
34
|
vertices: { index: number, bind: [number, number, number], world: [number, number, number], bones: { name: string, weight: number }[] }[]
|
|
31
35
|
}
|
|
@@ -47,7 +51,7 @@ export class Model extends Node {
|
|
|
47
51
|
/** @internal -1 = auto (see `lod`) */
|
|
48
52
|
_lodMesh = -1
|
|
49
53
|
|
|
50
|
-
constructor(internalId: number) {
|
|
54
|
+
constructor(internalId: number | Handle) {
|
|
51
55
|
super(internalId)
|
|
52
56
|
this.aspect(Animator)
|
|
53
57
|
}
|
|
@@ -95,16 +99,16 @@ export class Model extends Node {
|
|
|
95
99
|
* or misbound polygon can be traced to its binding. One full CPU skin of the model per call: click-rate
|
|
96
100
|
* only. null = miss, or a host without the pick (desktop today). */
|
|
97
101
|
pickTriangle(screenX: number, screenY: number): TrianglePick | null {
|
|
98
|
-
|
|
99
|
-
return json ? JSON.parse(json) as TrianglePick : null
|
|
102
|
+
return _creator.pickTriangle?.(this.id, screenX, screenY) ?? null
|
|
100
103
|
}
|
|
101
104
|
|
|
102
105
|
/** Duplicate this model — a deep copy of the GLB (meshes, skeleton, animation clips), attached to
|
|
103
106
|
* the same parent and scene and sharing this model's current transform. The clone has its own
|
|
104
107
|
* independent animation state (reach it via clone.anim). Mirrors this model's culling flag. */
|
|
105
108
|
clone(): Model {
|
|
106
|
-
const
|
|
107
|
-
const
|
|
109
|
+
const m = new Model(_creator.cloneEntity(this.id))
|
|
110
|
+
const id = m.id
|
|
111
|
+
if (_creator.entityIsOwned(id)) nodes.pin(m) // cloned into this model's parent / scene: native owns it
|
|
108
112
|
m._lightmapped = this._lightmapped
|
|
109
113
|
m._foliage = this._foliage
|
|
110
114
|
if (!this._culling) { m._culling = false; _creator.setGlbCulling(id, false) } // the engine default is on
|
|
@@ -124,7 +128,7 @@ export class Model extends Node {
|
|
|
124
128
|
/** Frustum culling — see `culling` (default: on where the host refits skinned bounds, else off). */
|
|
125
129
|
culling?: boolean
|
|
126
130
|
onProgress?: (p: { loaded: number, total?: number }) => void
|
|
127
|
-
/** Baked lighting (
|
|
131
|
+
/** Baked lighting (engines/bake). `true` = a STATIC: loads through the lightmap material so
|
|
128
132
|
* `Lightmap.load` can bind its atlas rect (the GLB needs TEXCOORD_1 — `lecodes assets doctor
|
|
129
133
|
* --lightmap-uv`) and takes nothing from the real-time lights once the bake applies. Omitted / `false` =
|
|
130
134
|
* the standard shader, lit real-time (movers). Hosts without lightmap support ignore it. */
|
|
@@ -141,21 +145,22 @@ export class Model extends Node {
|
|
|
141
145
|
})
|
|
142
146
|
}
|
|
143
147
|
return new Promise<Model>((resolve, reject) => {
|
|
144
|
-
// the provider path: a foliage model takes the vegetation tier's pair when the host
|
|
145
|
-
//
|
|
148
|
+
// the provider path: a foliage model takes the vegetation tier's pair when the host carries it, a
|
|
149
|
+
// static the lightmap material + its masked twin; anything else the ubershader
|
|
146
150
|
const foliageWanted = !!options.foliage && !!_creator.setNextGlbLightmapped
|
|
147
151
|
let foliage = false
|
|
148
152
|
if (foliageWanted) {
|
|
149
|
-
const fol = Material.
|
|
150
|
-
const folMasked = fol
|
|
151
|
-
foliage = fol
|
|
152
|
-
if (
|
|
153
|
+
const fol = Material._foliageTemplate()
|
|
154
|
+
const folMasked = fol && Material._foliageMaskedTemplate()
|
|
155
|
+
foliage = !!fol && !!folMasked
|
|
156
|
+
if (fol && folMasked) _creator.setNextGlbLightmapped!(Material.idOf(fol), Material.idOf(folMasked))
|
|
153
157
|
}
|
|
154
158
|
const lightmapped = !foliage && !!options.lightmap && !!_creator.setNextGlbLightmapped
|
|
155
159
|
if (lightmapped) _creator.setNextGlbLightmapped!(Material.idOf(Material._lightmapTemplate()), Material.idOf(Material._lightmapMaskedTemplate()))
|
|
156
|
-
_creator.createGlb((source as unknown as {
|
|
157
|
-
if (
|
|
158
|
-
const model = new Model(
|
|
160
|
+
_creator.createGlb((source as unknown as { _h: Handle })._h.id, (root) => {
|
|
161
|
+
if (!root) { reject(new Error("Failed to load GLB")); return }
|
|
162
|
+
const model = new Model(root)
|
|
163
|
+
const entityId = model.id
|
|
159
164
|
model._lightmapped = lightmapped
|
|
160
165
|
model._foliage = foliage
|
|
161
166
|
const culling = options.culling ?? Model._cullingDefault()
|
package/src/gl/Node.ts
CHANGED
|
@@ -6,7 +6,8 @@
|
|
|
6
6
|
// A GLB import is the Model node kind (with the Animator aspect at model.anim). Hierarchy lives here.
|
|
7
7
|
|
|
8
8
|
import { AspectHost } from "../core/Aspect"
|
|
9
|
-
import {
|
|
9
|
+
import { DEAD_HANDLE, Pins, registerFreedKind } from "../core/pins"
|
|
10
|
+
import { OWNED_KIND_GL_ENTITY } from "../ui/tree"
|
|
10
11
|
import { Vec3, cx, cy, cz, type Vec3Like } from "../math/vec"
|
|
11
12
|
import { Quat, cw, type QuatLike } from "../math/quat"
|
|
12
13
|
import { Mat4, type Mat4Like } from "../math/mat4"
|
|
@@ -31,7 +32,38 @@ export type NodeTweenProps = {
|
|
|
31
32
|
}
|
|
32
33
|
|
|
33
34
|
/** id → Node, so host callbacks (touch hits, animation events) route back to the owning object. */
|
|
34
|
-
|
|
35
|
+
/** @internal The nodes native owns right now (in a scene / under a parent, or engine-created), by
|
|
36
|
+
* entity id — what a pick, a contact or an animation event resolves to. See core/pins.ts. */
|
|
37
|
+
export const nodes = new Pins<Node>()
|
|
38
|
+
|
|
39
|
+
// The loose wrappers, WEAKLY: a read-back (`parent` / `children` / `traverse` / `find`) must hand
|
|
40
|
+
// back the app's OWN handle while it exists. A loose root has no native owner, so no pin — the
|
|
41
|
+
// only other way to reach it from below was a throwaway wrapper, and anything put on that wrapper
|
|
42
|
+
// (IK's rig Animator on a bare tree's top) was lost to the app's handle. WeakRef keeps the owned
|
|
43
|
+
// model intact: the registry never holds a handle alive, a dropped root still finalizes, and a
|
|
44
|
+
// dead entry is dropped when met (or in the sweep every SWEEP registrations).
|
|
45
|
+
const loose = new Map<number, WeakRef<Node>>()
|
|
46
|
+
const SWEEP = 256
|
|
47
|
+
let sinceSweep = 0
|
|
48
|
+
const registerLoose = (n: Node): void => {
|
|
49
|
+
loose.set(n._h.id, new WeakRef(n))
|
|
50
|
+
if (++sinceSweep < SWEEP) return
|
|
51
|
+
sinceSweep = 0
|
|
52
|
+
for (const [id, ref] of loose) { const w = ref.deref(); if (!w || w._h.id !== id) loose.delete(id) }
|
|
53
|
+
}
|
|
54
|
+
/** @internal The wrapper for an entity id the runtime handed back: the pinned one, else the
|
|
55
|
+
* app's live loose handle, else a throwaway over the engine-owned id. */
|
|
56
|
+
export const nodeOf = (id: number): Node => {
|
|
57
|
+
const pinned = nodes.get(id)
|
|
58
|
+
if (pinned) return pinned
|
|
59
|
+
const ref = loose.get(id)
|
|
60
|
+
if (ref) {
|
|
61
|
+
const w = ref.deref()
|
|
62
|
+
if (w && w._h.id === id) return w
|
|
63
|
+
loose.delete(id)
|
|
64
|
+
}
|
|
65
|
+
return new Node(id)
|
|
66
|
+
}
|
|
35
67
|
|
|
36
68
|
export type NodeEvents = {
|
|
37
69
|
click: (ev: ClickEvent<Node | null>) => void
|
|
@@ -67,8 +99,12 @@ const findNodeByWalk = (rootId: number, name: string): number => {
|
|
|
67
99
|
const worldScratch = new Float32Array(3)
|
|
68
100
|
|
|
69
101
|
export class Node extends AspectHost<NodeEvents> implements CompWriter {
|
|
70
|
-
/**
|
|
71
|
-
|
|
102
|
+
/** @internal The entity's owned handle (`_h.id` = the entity id; DEAD_HANDLE once destroyed). An
|
|
103
|
+
* engine-created entity (a glTF part, an anchor) is wrapped from its number and pinned as the
|
|
104
|
+
* engine's; an entity WE created is loose until it gets a parent or a scene. */
|
|
105
|
+
_h: Handle
|
|
106
|
+
/** Native entity id. */
|
|
107
|
+
get id(): number { return this._h.id }
|
|
72
108
|
|
|
73
109
|
/** AR anchors only — whether the anchor is currently being tracked. Kept in step with the
|
|
74
110
|
* 'track' / 'untrack' events; `false` for any non-anchor node. */
|
|
@@ -80,10 +116,20 @@ export class Node extends AspectHost<NodeEvents> implements CompWriter {
|
|
|
80
116
|
/** Materials assigned through setMaterial, by primitive slot (a Mesh fills slot 0 itself). */
|
|
81
117
|
protected _materials?: Material[]
|
|
82
118
|
|
|
83
|
-
constructor(internalId?: number) {
|
|
119
|
+
constructor(internalId?: number | Handle) {
|
|
84
120
|
super()
|
|
85
|
-
|
|
86
|
-
|
|
121
|
+
registerFreedKind(OWNED_KIND_GL_ENTITY, nodes)
|
|
122
|
+
if (internalId === undefined) {
|
|
123
|
+
const h = _creator.createEntity()
|
|
124
|
+
this._h = typeof h === "number" ? { id: h } : h // a number only from a test stub
|
|
125
|
+
registerLoose(this)
|
|
126
|
+
} else if (typeof internalId === "number") {
|
|
127
|
+
this._h = { id: internalId } // engine-owned: findable while the engine keeps it
|
|
128
|
+
nodes.pin(this)
|
|
129
|
+
} else {
|
|
130
|
+
this._h = internalId // an owned root the runtime handed us (createGlb, cloneEntity)
|
|
131
|
+
registerLoose(this)
|
|
132
|
+
}
|
|
87
133
|
}
|
|
88
134
|
|
|
89
135
|
/** Geometry, if this node is a Mesh (used by physics auto-shape). Overridden by Mesh. */
|
|
@@ -263,29 +309,37 @@ export class Node extends AspectHost<NodeEvents> implements CompWriter {
|
|
|
263
309
|
// --- hierarchy ---
|
|
264
310
|
get parent(): Node | null {
|
|
265
311
|
const id = _creator.getParent(this.id)
|
|
266
|
-
return id === 0 ? null : (
|
|
312
|
+
return id === 0 ? null : nodeOf(id)
|
|
267
313
|
}
|
|
268
314
|
get children(): Node[] {
|
|
269
|
-
|
|
315
|
+
const n = _creator.getChildCount(this.id)
|
|
316
|
+
const out: Node[] = []
|
|
317
|
+
for (let i = 0; i < n; i++) { const id = _creator.getChild(this.id, i); out.push(nodeOf(id)) }
|
|
318
|
+
return out
|
|
270
319
|
}
|
|
271
320
|
get childCount(): number { return _creator.getChildCount(this.id) }
|
|
272
321
|
getChild(index: number): Node | null {
|
|
273
322
|
const id = _creator.getChild(this.id, index)
|
|
274
|
-
return id === 0 ? null : (
|
|
323
|
+
return id === 0 ? null : nodeOf(id)
|
|
275
324
|
}
|
|
276
325
|
/** Parent the given nodes under this one. */
|
|
277
326
|
add(...children: Node[]): this {
|
|
278
|
-
for (const c of children) _creator.setParent(c.id, this.id, false)
|
|
327
|
+
for (const c of children) { _creator.setParent(c.id, this.id, false); nodes.pin(c) }
|
|
279
328
|
return this
|
|
280
329
|
}
|
|
281
330
|
setParent(parent: Node | null, worldPositionStays = false): this {
|
|
282
|
-
if (parent === null)
|
|
283
|
-
|
|
331
|
+
if (parent === null) {
|
|
332
|
+
_creator.setParentNull(this.id, worldPositionStays)
|
|
333
|
+
if (!_creator.entityIsOwned(this.id)) nodes.unpin(this.id) // loose again: the handle owns it
|
|
334
|
+
} else {
|
|
335
|
+
_creator.setParent(this.id, parent.id, worldPositionStays)
|
|
336
|
+
nodes.pin(this)
|
|
337
|
+
}
|
|
284
338
|
return this
|
|
285
339
|
}
|
|
286
340
|
traverse(callback: (node: Node) => void): void {
|
|
287
341
|
callback(this)
|
|
288
|
-
_creator.traverse(this.id, (id) => callback(
|
|
342
|
+
_creator.traverse(this.id, (id) => callback(nodeOf(id)))
|
|
289
343
|
}
|
|
290
344
|
// --- materials (any renderable: a Mesh, or an internal node of a loaded Model) ---
|
|
291
345
|
/** The material assigned to slot 0 through this API (null before one is set — a GLB part's own
|
|
@@ -313,7 +367,7 @@ export class Node extends AspectHost<NodeEvents> implements CompWriter {
|
|
|
313
367
|
const hit = cache.get(name)
|
|
314
368
|
if (hit !== undefined) return hit
|
|
315
369
|
const id = _creator.findNode ? _creator.findNode(this.id, name) : findNodeByWalk(this.id, name)
|
|
316
|
-
const found = id ?
|
|
370
|
+
const found = id ? nodeOf(id) : null
|
|
317
371
|
cache.set(name, found)
|
|
318
372
|
return found
|
|
319
373
|
}
|
|
@@ -372,16 +426,29 @@ export class Node extends AspectHost<NodeEvents> implements CompWriter {
|
|
|
372
426
|
* `destroyEntity` frees the entity tree but knows nothing about JS-side aspects, and a destroyed
|
|
373
427
|
* node's `update()` must not keep ticking. Deepest nodes go first, then this one. */
|
|
374
428
|
destroy(): void {
|
|
429
|
+
if (this._h.id === 0) return
|
|
375
430
|
const subtree: Node[] = [this]
|
|
376
|
-
_creator.traverse(this.id, (id) => { const n =
|
|
377
|
-
for (let i = subtree.length - 1; i >= 0; i--)
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
431
|
+
_creator.traverse(this.id, (id) => { const n = nodes.get(id); if (n) subtree.push(n) })
|
|
432
|
+
for (let i = subtree.length - 1; i >= 0; i--) subtree[i]._detachAll()
|
|
433
|
+
const id = this.id
|
|
434
|
+
// Dead NOW — entity ids are recycled, and the runtime's freed report lands a drain later.
|
|
435
|
+
for (const n of subtree) { nodes.unpin(n.id); n._dead() }
|
|
436
|
+
_creator.destroyEntity(id)
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
/** @internal The wrapper's resource is gone: no more native calls from here. */
|
|
440
|
+
_dead(): void {
|
|
441
|
+
this._xf = 0
|
|
442
|
+
this._xfKind = 0
|
|
443
|
+
loose.delete(this._h.id)
|
|
444
|
+
this._h = DEAD_HANDLE
|
|
445
|
+
}
|
|
446
|
+
/** @internal The runtime freed the entity underneath (an ancestor destroyed or dropped): the
|
|
447
|
+
* aspects detach (their onDetach runs after the entity is gone) and the wrapper is dead. */
|
|
448
|
+
_freed(): void {
|
|
449
|
+
if (this._h.id === 0) return
|
|
450
|
+
this._detachAll()
|
|
451
|
+
this._dead()
|
|
385
452
|
}
|
|
386
453
|
}
|
|
387
454
|
|
package/src/gl/Particles.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
// All emitter/curve config crosses the bridge as ONE Float32Array of [tag, payloadLen, ...payload]
|
|
5
5
|
// records (_creator.setParticleSystemConfig) — the constructor batches every option into a single
|
|
6
6
|
// call, live setters send a one-record buffer. The tag values mirror CPART_TAG_* in
|
|
7
|
-
// creator-particles/include/creator-particles/creator-particles.h (guarded by tests/particles-tags.test.ts).
|
|
7
|
+
// creator-particles/include/creator-particles/creator-particles.h (guarded by tests/gl/particles-tags.test.ts).
|
|
8
8
|
//
|
|
9
9
|
// Curves over a particle's lifetime are built with curve()/colorCurve() chains — see the builders
|
|
10
10
|
// below. A builder's whole state is the flat `_data` array (the exact record payload), so the
|