lecodes-sdk 1.2.0 → 2.0.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.
Files changed (260) hide show
  1. package/README.md +5 -2
  2. package/dist/editor.d.ts +12 -0
  3. package/dist/global.d.ts +4 -8
  4. package/dist/host.d.ts +2 -3
  5. package/dist/types/animate/tween/Animation.d.ts +0 -3
  6. package/dist/types/animate/tween/animateValue.d.ts +4 -2
  7. package/dist/types/animate/tween/easing.d.ts +8 -0
  8. package/dist/types/animate/tween/spec.d.ts +15 -7
  9. package/dist/types/audio/audio.d.ts +2 -1
  10. package/dist/types/canvas/Canvas.d.ts +40 -108
  11. package/dist/types/canvas/gen/cssColor.d.ts +17 -0
  12. package/dist/types/canvas/gen/recorder.d.ts +118 -0
  13. package/dist/types/canvas/gen/spec.d.ts +144 -0
  14. package/dist/types/core/color.d.ts +3 -1
  15. package/dist/types/core/pins.d.ts +18 -0
  16. package/dist/types/g2/Node2D.d.ts +5 -8
  17. package/dist/types/g2/Scene2D.d.ts +6 -2
  18. package/dist/types/gl/Foliage.d.ts +30 -7
  19. package/dist/types/gl/Light.d.ts +8 -0
  20. package/dist/types/gl/Lightmap.d.ts +13 -2
  21. package/dist/types/gl/Material.d.ts +17 -3
  22. package/dist/types/gl/Model.d.ts +6 -2
  23. package/dist/types/gl/Node.d.ts +3 -6
  24. package/dist/types/gl/Scene.d.ts +41 -13
  25. package/dist/types/gl/Texture.d.ts +1 -1
  26. package/dist/types/gl/animation/Locomotion.d.ts +8 -1
  27. package/dist/types/inject.d.ts +11 -11
  28. package/dist/types/inject.editor.d.ts +1 -0
  29. package/dist/types/net/core.d.ts +7 -0
  30. package/dist/types/plugin.d.ts +76 -0
  31. package/dist/types/plugins/gen/camera/sdk/camera.d.ts +24 -0
  32. package/dist/types/plugins/gen/camera/sdk/camera.gen.d.ts +25 -0
  33. package/dist/types/plugins/{geolocation.d.ts → gen/geolocation/sdk/geolocation.d.ts} +2 -20
  34. package/dist/types/plugins/gen/geolocation/sdk/geolocation.gen.d.ts +31 -0
  35. package/dist/types/plugins/{map.d.ts → gen/map/sdk/map.d.ts} +9 -50
  36. package/dist/types/plugins/gen/map/sdk/map.gen.d.ts +53 -0
  37. package/dist/types/plugins/gen/push/sdk/push.d.ts +23 -0
  38. package/dist/types/plugins/gen/push/sdk/push.gen.d.ts +35 -0
  39. package/dist/types/plugins/{qr.d.ts → gen/qr-scanner/sdk/qr-scanner.d.ts} +2 -3
  40. package/dist/types/plugins/gen/qr-scanner/sdk/qr-scanner.gen.d.ts +15 -0
  41. package/dist/types/runtime/app.d.ts +9 -2
  42. package/dist/types/runtime/fetch.d.ts +2 -0
  43. package/dist/types/runtime/input.d.ts +1 -1
  44. package/dist/types/runtime/media.d.ts +6 -10
  45. package/dist/types/runtime/misc.d.ts +4 -1
  46. package/dist/types/runtime/net.d.ts +3 -2
  47. package/dist/types/runtime/touch.d.ts +32 -0
  48. package/dist/types/scene/defineScene.d.ts +43 -2
  49. package/dist/types/scene/editor.d.ts +52 -0
  50. package/dist/types/scene/gizmos.d.ts +7 -4
  51. package/dist/types/ui/NativeView.d.ts +6 -4
  52. package/dist/types/ui/UI.d.ts +1 -1
  53. package/dist/types/ui/UIBottomSheet.d.ts +6 -12
  54. package/dist/types/ui/UIButton.d.ts +12 -14
  55. package/dist/types/ui/UIContainer.d.ts +0 -6
  56. package/dist/types/ui/UIImage.d.ts +1 -4
  57. package/dist/types/ui/UIInput.d.ts +8 -24
  58. package/dist/types/ui/UIModal.d.ts +0 -2
  59. package/dist/types/ui/UINode.d.ts +70 -56
  60. package/dist/types/ui/UIPager.d.ts +28 -27
  61. package/dist/types/ui/UIPopover.d.ts +0 -2
  62. package/dist/types/ui/UIScreen.d.ts +12 -17
  63. package/dist/types/ui/UIScrollable.d.ts +1 -4
  64. package/dist/types/ui/UIText.d.ts +0 -2
  65. package/dist/types/ui/UIVideo.d.ts +3 -5
  66. package/dist/types/ui/UIVirtualizedList.d.ts +14 -16
  67. package/dist/types/ui/UIWidget.d.ts +10 -9
  68. package/dist/types/ui/colorKeys.gen.d.ts +9 -0
  69. package/dist/types/ui/presentable.d.ts +46 -32
  70. package/dist/types/ui/router.d.ts +18 -7
  71. package/dist/types/ui/styleColor.d.ts +1 -0
  72. package/dist/types/ui/transitions.d.ts +18 -0
  73. package/dist/types/ui/tree.d.ts +75 -0
  74. package/dist/types/version.d.ts +10 -0
  75. package/dist/types.json +1 -1
  76. package/package.json +12 -3
  77. package/prompts/2d.md +2 -6
  78. package/prompts/3d.md +1 -5
  79. package/prompts/README.md +1 -1
  80. package/prompts/canvas.md +9 -8
  81. package/prompts/compose.ts +1 -1
  82. package/prompts/core.md +3 -3
  83. package/prompts/design.md +1 -1
  84. package/prompts/dist/2d-game.md +473 -239
  85. package/prompts/dist/3d-app.md +553 -205
  86. package/prompts/dist/ar-app.md +435 -202
  87. package/prompts/dist/design.md +113 -95
  88. package/prompts/dist/ui-app.md +386 -170
  89. package/prompts/ui-design.md +2 -3
  90. package/prompts/ui.md +45 -37
  91. package/src/animate/tween/Animation.ts +34 -150
  92. package/src/animate/tween/Timeline.ts +175 -175
  93. package/src/animate/tween/animateValue.ts +6 -3
  94. package/src/animate/tween/easing.ts +10 -3
  95. package/src/animate/tween/spec.ts +41 -15
  96. package/src/audio/Sound.ts +3 -3
  97. package/src/audio/audio.ts +2 -1
  98. package/src/bridges/2d.d.ts +317 -0
  99. package/src/bridges/app.d.ts +91 -0
  100. package/src/bridges/audio.d.ts +97 -0
  101. package/src/bridges/canvas.d.ts +79 -0
  102. package/src/bridges/device.d.ts +72 -0
  103. package/src/bridges/fetch.d.ts +80 -0
  104. package/src/bridges/files.d.ts +70 -0
  105. package/src/bridges/gl.d.ts +1133 -0
  106. package/src/bridges/input.d.ts +72 -0
  107. package/src/bridges/media.d.ts +55 -0
  108. package/src/bridges/nav.d.ts +71 -0
  109. package/src/bridges/net.d.ts +52 -0
  110. package/src/bridges/service.d.ts +52 -0
  111. package/src/bridges/socket.d.ts +31 -0
  112. package/src/bridges/storage.d.ts +33 -0
  113. package/src/bridges/tree.d.ts +301 -0
  114. package/src/bridges/types.d.ts +49 -0
  115. package/src/canvas/Canvas.ts +114 -159
  116. package/src/canvas/gen/cssColor.ts +224 -0
  117. package/src/canvas/gen/recorder.ts +212 -0
  118. package/src/canvas/gen/spec.ts +201 -0
  119. package/src/chisel.ts +193 -0
  120. package/src/compile/assetMacro.ts +1 -1
  121. package/src/compile/bundler.ts +11 -2
  122. package/src/compile/compileProject.ts +43 -4
  123. package/src/compile/fontMacro.ts +3 -4
  124. package/src/compile/header.ts +26 -5
  125. package/src/compile/index.ts +3 -1
  126. package/src/compile/liteMaterial.ts +1 -1
  127. package/src/compile/sceneEditor.ts +11 -26
  128. package/src/core/color.ts +73 -30
  129. package/src/core/pins.ts +51 -0
  130. package/src/core/signals.ts +8 -1
  131. package/src/g2/CharacterController2D.ts +3 -3
  132. package/src/g2/Node2D.ts +57 -39
  133. package/src/g2/Physics2D.ts +2 -2
  134. package/src/g2/Scene2D.ts +35 -23
  135. package/src/g2/Texture2D.ts +1 -1
  136. package/src/g2/loop.ts +4 -4
  137. package/src/gl/CameraPlace.ts +52 -52
  138. package/src/gl/Foliage.ts +72 -17
  139. package/src/gl/Geometry.ts +1 -2
  140. package/src/gl/Light.ts +10 -0
  141. package/src/gl/Lightmap.ts +45 -29
  142. package/src/gl/Material.ts +95 -51
  143. package/src/gl/Mesh.ts +120 -120
  144. package/src/gl/Model.ts +21 -16
  145. package/src/gl/Node.ts +91 -24
  146. package/src/gl/Particles.ts +1 -1
  147. package/src/gl/Scene.ts +100 -44
  148. package/src/gl/Texture.ts +8 -7
  149. package/src/gl/animation/AnimationClip.ts +1 -1
  150. package/src/gl/animation/DynamicBone.ts +482 -482
  151. package/src/gl/animation/Locomotion.ts +8 -3
  152. package/src/gl/nav/NavMesh.ts +3 -4
  153. package/src/gl/physics/Physics.ts +2 -2
  154. package/src/gl/physics/physicsEvents.ts +3 -3
  155. package/src/gl/scenarios.ts +291 -291
  156. package/src/gl/terrain/Terrain.ts +4 -5
  157. package/src/gl/touch.ts +14 -15
  158. package/src/host.d.ts +2 -3
  159. package/src/inject.editor.ts +7 -0
  160. package/src/inject.ts +13 -16
  161. package/src/net/core.ts +6 -5
  162. package/src/net/index.ts +1 -1
  163. package/src/net/replication.ts +1 -1
  164. package/src/plugin.ts +191 -0
  165. package/src/plugins/gen/camera/contract.d.ts +27 -0
  166. package/src/plugins/gen/camera/sdk/camera.gen.ts +46 -0
  167. package/src/plugins/gen/camera/sdk/camera.ts +57 -0
  168. package/src/plugins/gen/geolocation/contract.d.ts +50 -0
  169. package/src/plugins/gen/geolocation/sdk/geolocation.gen.ts +54 -0
  170. package/src/plugins/{geolocation.ts → gen/geolocation/sdk/geolocation.ts} +22 -43
  171. package/src/plugins/gen/map/contract.d.ts +144 -0
  172. package/src/plugins/gen/map/sdk/map.gen.ts +88 -0
  173. package/src/plugins/{map.ts → gen/map/sdk/map.ts} +68 -102
  174. package/src/plugins/gen/push/contract.d.ts +61 -0
  175. package/src/plugins/gen/push/sdk/push.gen.ts +60 -0
  176. package/src/plugins/gen/push/sdk/push.ts +105 -0
  177. package/src/plugins/gen/qr-scanner/contract.d.ts +16 -0
  178. package/src/plugins/gen/qr-scanner/sdk/qr-scanner.gen.ts +29 -0
  179. package/src/plugins/gen/qr-scanner/sdk/qr-scanner.ts +52 -0
  180. package/src/plugins/permission.ts +5 -4
  181. package/src/runtime/app.ts +20 -9
  182. package/src/runtime/appEvents.ts +5 -4
  183. package/src/runtime/channel.ts +18 -15
  184. package/src/runtime/clipboard.ts +4 -3
  185. package/src/runtime/datetime.ts +2 -1
  186. package/src/runtime/device.ts +17 -15
  187. package/src/runtime/fetch.ts +30 -20
  188. package/src/runtime/files.ts +16 -15
  189. package/src/runtime/input.ts +12 -10
  190. package/src/runtime/media.ts +50 -46
  191. package/src/runtime/misc.ts +7 -3
  192. package/src/runtime/net.ts +8 -7
  193. package/src/runtime/rpc.ts +1 -3
  194. package/src/runtime/service.ts +19 -14
  195. package/src/runtime/share.ts +4 -3
  196. package/src/runtime/storage.ts +6 -4
  197. package/src/runtime/touch.ts +32 -0
  198. package/src/scene/defineScene.ts +61 -365
  199. package/src/scene/editor.ts +408 -0
  200. package/src/scene/editorPlugins.ts +3 -3
  201. package/src/scene/gizmos.ts +15 -9
  202. package/src/server/db/marci/query.ts +1 -1
  203. package/src/server/host.ts +1 -1
  204. package/src/server/runtime.ts +1 -1
  205. package/src/ui/NativeView.ts +72 -25
  206. package/src/ui/UI.ts +3 -3
  207. package/src/ui/UIBottomSheet.ts +16 -17
  208. package/src/ui/UIButton.ts +54 -16
  209. package/src/ui/UIContainer.ts +0 -6
  210. package/src/ui/UIImage.ts +34 -30
  211. package/src/ui/UIInput.ts +29 -37
  212. package/src/ui/UIModal.ts +1 -3
  213. package/src/ui/UINode.ts +347 -297
  214. package/src/ui/UIPager.ts +93 -78
  215. package/src/ui/UIPopover.ts +0 -2
  216. package/src/ui/UIScreen.ts +41 -36
  217. package/src/ui/UIScrollable.ts +19 -13
  218. package/src/ui/UISpacer.ts +1 -1
  219. package/src/ui/UITabs.ts +8 -6
  220. package/src/ui/UIText.ts +7 -17
  221. package/src/ui/UIVideo.ts +30 -27
  222. package/src/ui/UIVirtualizedList.ts +58 -59
  223. package/src/ui/UIWidget.ts +38 -18
  224. package/src/ui/colorKeys.gen.ts +37 -0
  225. package/src/ui/fonts.ts +2 -2
  226. package/src/ui/presentable.ts +59 -42
  227. package/src/ui/router.ts +52 -35
  228. package/src/ui/styleColor.ts +56 -0
  229. package/src/ui/theme.ts +7 -6
  230. package/src/ui/transitions.ts +249 -0
  231. package/src/ui/tree.ts +346 -0
  232. package/src/version.ts +24 -0
  233. package/tests/helpers/engineWorld.ts +11 -0
  234. package/tests/helpers/fakeTree.ts +353 -0
  235. package/tests/helpers/hostStubs.ts +31 -0
  236. package/tests/helpers/index.ts +14 -0
  237. package/tests/helpers/memoryMarci.ts +124 -0
  238. package/tests/helpers/phases.ts +23 -0
  239. package/tests/helpers/preload.ts +18 -0
  240. package/tests/helpers/stubApp.ts +2 -0
  241. package/tests/helpers/stubDevice.ts +2 -0
  242. package/tests/helpers/stubFetch.ts +2 -0
  243. package/tests/helpers/stubInput.ts +2 -0
  244. package/dist/inject.js +0 -4629
  245. package/dist/types/core/registry.d.ts +0 -7
  246. package/dist/types/plugins/camera.d.ts +0 -25
  247. package/dist/types/plugins/push.d.ts +0 -46
  248. package/src/bridges.d.ts +0 -1769
  249. package/src/compile/__tests__/assetIconMacro.test.ts +0 -219
  250. package/src/compile/__tests__/assetMacro.test.ts +0 -100
  251. package/src/compile/__tests__/assetName.test.ts +0 -55
  252. package/src/compile/__tests__/compile.test.ts +0 -310
  253. package/src/compile/__tests__/detectEntry.test.ts +0 -151
  254. package/src/compile/__tests__/fontMacro.test.ts +0 -199
  255. package/src/compile/__tests__/serverSplit.test.ts +0 -27
  256. package/src/core/__tests__/stateMachine.test.ts +0 -132
  257. package/src/core/registry.ts +0 -23
  258. package/src/plugins/camera.ts +0 -81
  259. package/src/plugins/push.ts +0 -132
  260. package/src/plugins/qr.ts +0 -73
package/src/gl/Scene.ts CHANGED
@@ -7,7 +7,9 @@
7
7
 
8
8
  import { Color, type ColorInput } from "../core/color"
9
9
  import { _installAspectFrames, _installTimeScale, _attachSystem, _detachSystem, _detachAllSystems, type Aspect, type AspectCtor, type FieldOf, type TargetOf } from "../core/Aspect"
10
- import { _bumpNavEpoch, _navEpoch, _navSupported, _setCurrent, Presentable, type PresentOptions } from "../ui/presentable"
10
+ import { _bumpNavEpoch, _navEpoch, _setCurrent, Presentable, type DestTuple, type DismissOptions, type PresentOptions } from "../ui/presentable"
11
+ import { _transitionId, TRANSITION_NONE } from "../ui/transitions"
12
+ import { TREE_DEST_SCENE3D, TREE_FLAG_BACK, ensureEmitter, registerDest, unregisterDest } from "../ui/tree"
11
13
  import { device } from "../runtime/device"
12
14
  import type { ClickEvent, TouchStartEvent } from "../runtime/touch"
13
15
  import type { FetchResponse } from "../runtime/fetch"
@@ -17,7 +19,7 @@ import { SceneAudio } from "./audio/SceneAudio"
17
19
  import { attachControls, type ControlsHandle, type ControlsOptions } from "./controls"
18
20
  import { Material } from "./Material"
19
21
  import { Texture } from "./Texture"
20
- import { Node, nodeRegistry } from "./Node"
22
+ import { Node, nodes } from "./Node"
21
23
  import { glState } from "./state"
22
24
  import { registerTouchEndEvent, registerTouchStartEvent } from "./touch"
23
25
 
@@ -32,6 +34,21 @@ export type AmbientOcclusionOptions = {
32
34
  quality?: "low" | "medium" | "high" | "ultra"
33
35
  }
34
36
 
37
+ /** `SceneOptions.taa` / `scene.setAntialias("taa", 4, taa)`: the temporal anti-aliasing knobs. */
38
+ export type TaaOptions = {
39
+ /** Render the 3D at this fraction (0.5–1) and TAA-upscale it to the viewport; 1 = none. */
40
+ upscale?: number
41
+ /** The history's share, 0 (max smoothing) … 1 (none); 0.12 default. */
42
+ feedback?: number
43
+ /** Post-TAA sharpen 0–1; defaults to 0.5 with `upscale` below 1, else 0. */
44
+ sharpness?: number
45
+ /** A first-person viewmodel: everything closer than this many metres to the camera is treated as
46
+ * fixed on screen — its history is read at the same pixel, not reprojected by the camera's motion,
47
+ * which would trail the weapon on every turn (TAA has no per-object motion vectors). 1 covers arms
48
+ * and weapon at eye height; 0 (the default) = off. A wall walked into within the band smears a little. */
49
+ viewmodel?: number
50
+ }
51
+
35
52
  export type FogOptions = {
36
53
  /** A TINT on the in-scattered ambient, not an absolute colour: the engine multiplies it by the
37
54
  * environment luminance, so white (the default) means "fog as bright as the ambient" and the fog
@@ -105,10 +122,18 @@ export type SceneOptions = {
105
122
  * at every distance — the skybox included — so the sky itself takes the fog colour and the horizon
106
123
  * blends on its own; `cutoff` opts geometry beyond a distance back out. */
107
124
  fog?: FogOptions
108
- /** Multisample anti-aliasing: `true` = 4×, `2`/`4` = that many samples, `false` = off (FXAA
109
- * takes over). Unset keeps the host default (desktop 4×, mobile/web off). The biggest single
110
- * fill-rate cost after resolution — turn it down on big screens before anything else. */
111
- antialias?: boolean | 2 | 4
125
+ /** Anti-aliasing: `true` = 4× MSAA, `2`/`4` = that many samples, `false` = off (FXAA takes over),
126
+ * `"taa"` = temporal anti-aliasing (Filament's TAA: one full-screen history blend instead of the
127
+ * multisampled buffers — cheaper than MSAA on an integrated GPU and it also calms specular and
128
+ * foliage shimmer; see `taa` for its knobs). Unset keeps the host default (desktop 4×, mobile/web
129
+ * off). The biggest single fill-rate cost after resolution — turn it down on big screens first. */
130
+ antialias?: boolean | 2 | 4 | "taa"
131
+ /** The TAA's knobs (`antialias: "taa"`): `upscale` = render the 3D at this fraction (0.5–1) and let
132
+ * TAA reconstruct the full resolution — the cheap way to a lower render cost with most of the
133
+ * detail kept (`renderScale` shrinks the buffer and stretches it instead); `feedback` = the
134
+ * history's share, 0.12 by default (raise it toward 0.3 if fast motion ghosts); `sharpness` =
135
+ * a post-TAA sharpen 0–1, useful with `upscale`. */
136
+ taa?: TaaOptions
112
137
  /** Keep a stencil buffer for this scene (off by default: it costs memory and a clear per frame).
113
138
  * Needed before any material's `stencil` test or write does anything. */
114
139
  stencil?: boolean
@@ -214,7 +239,7 @@ export class Scene implements Presentable {
214
239
  // `asset()` hands back "id:<n>" (already staged); a bare name is resolved on the spot; -1 is
215
240
  // "no probe named", which leaves the host on the project-wide `ibl.ktx` (the old behaviour).
216
241
  const src = typeof options.ibl === "string" ? options.ibl : undefined
217
- const iblId = src === undefined ? -1 : src.startsWith("id:") ? Number(src.slice(3)) : _creatorUtils.fetchLocal(src)
242
+ const iblId = src === undefined ? -1 : src.startsWith("id:") ? Number(src.slice(3)) : _creatorFetch.local(src)
218
243
  _creator.setDefaultIbl(this._id, options.environmentIntensity ?? 20000, iblId)
219
244
  }
220
245
  this._environmentIntensity = options.ibl === false ? 0 : (options.environmentIntensity ?? 20000)
@@ -229,7 +254,7 @@ export class Scene implements Presentable {
229
254
  if (options.antialias !== undefined) {
230
255
  const a = options.antialias
231
256
  // `false` must reach the host: desktop defaults to 4× MSAA, so an explicit off is a real change.
232
- _creator.setSceneMultiSampleAntiAliasing(this._id, a !== false, typeof a === "number" ? a : 4)
257
+ this.setAntialias(a, 4, options.taa)
233
258
  }
234
259
  if (options.stencil !== undefined) this.setStencil(options.stencil)
235
260
  // Before anything else in this constructor that could load a texture: the value has to be in
@@ -252,9 +277,15 @@ export class Scene implements Presentable {
252
277
  _creator.setSceneRenderOptions?.(this._id, renderScale, !!dynamicResolution, min)
253
278
  }
254
279
 
255
- /** Scene occlusion material. */
280
+ /** Scene occlusion material: an unlit instance in the occluder blue (the built-in `unlit`, which
281
+ * every runtime carries) — one per scene, made on first use. */
256
282
  get occlusionMaterial(): Material {
257
- if (!this._material) this._material = new Material(_creator.getSceneMaterial(this._id))
283
+ if (!this._material) {
284
+ const h = _creator.builtinMaterial("unlit")
285
+ if (!h) throw new Error("Scene.occlusionMaterial: this host carries no unlit material")
286
+ this._material = new Material(h)
287
+ this._material.uniforms.baseColor = [0.1, 0.25, 0.57, 1.0]
288
+ }
258
289
  return this._material
259
290
  }
260
291
 
@@ -263,7 +294,7 @@ export class Scene implements Presentable {
263
294
  if (typeof sky === "object" && sky !== null && "texture" in sky) {
264
295
  const src = sky.texture
265
296
  // `asset()` hands back "id:<n>" (already staged); a bare name is resolved on the spot.
266
- const id = src.startsWith("id:") ? Number(src.slice(3)) : _creatorUtils.fetchLocal(src)
297
+ const id = src.startsWith("id:") ? Number(src.slice(3)) : _creatorFetch.local(src)
267
298
  _creator.setSkyboxTexture?.(this._id, id)
268
299
  return
269
300
  }
@@ -290,8 +321,19 @@ export class Scene implements Presentable {
290
321
  setMaterialGlobalParameter(i: number, x: number, y: number, z: number, w: number): void {
291
322
  _creator.setMaterialGlobalParameter(this._id, i, x, y, z, w)
292
323
  }
293
- setAntialias(enabled: boolean, scale = 4): void {
294
- _creator.setSceneMultiSampleAntiAliasing(this._id, enabled, scale)
324
+ setAntialias(mode: boolean | 2 | 4 | "taa", scale = 4, taa?: TaaOptions): void {
325
+ if (mode === "taa") {
326
+ if (!_creator.setTemporalAntiAliasing) { // an older host: the nearest thing it has
327
+ _creator.setSceneMultiSampleAntiAliasing(this._id, true, scale)
328
+ return
329
+ }
330
+ const up = taa?.upscale ?? 1
331
+ _creator.setTemporalAntiAliasing(this._id, true, taa?.feedback ?? 0.12, taa?.sharpness ?? (up < 1 ? 0.5 : 0), up < 0.5 ? 0.5 : up > 1 ? 1 : up, taa?.viewmodel ?? 0)
332
+ return
333
+ }
334
+ // `false` must reach the host: desktop defaults to 4× MSAA, so an explicit off is a real change.
335
+ _creator.setTemporalAntiAliasing?.(this._id, false, 0.12, 0, 1, 0)
336
+ _creator.setSceneMultiSampleAntiAliasing(this._id, mode !== false, typeof mode === "number" ? mode : scale)
295
337
  }
296
338
  /** Depth-reading effects on / off (a graphics-settings menu): soft particles and projected decals
297
339
  * read the scene depth, which costs a depth pre-pass of every opaque draw (~12 % of a fill-bound
@@ -336,12 +378,15 @@ export class Scene implements Presentable {
336
378
  else if (enabled) console.warn("[scene] this host has no setSceneStencil — stencil ignored")
337
379
  }
338
380
 
339
- add(...nodes: Node[]): this {
340
- for (const n of nodes) _creator.addEntityToScene(this._id, n.id)
381
+ add(...list: Node[]): this {
382
+ for (const n of list) { _creator.addEntityToScene(this._id, n.id); nodes.pin(n) } // the scene owns it now
341
383
  return this
342
384
  }
343
- remove(...nodes: Node[]): this {
344
- for (const n of nodes) _creator.removeEntityFromScene(this._id, n.id)
385
+ remove(...list: Node[]): this {
386
+ for (const n of list) {
387
+ _creator.removeEntityFromScene(this._id, n.id)
388
+ if (!_creator.entityIsOwned(n.id)) nodes.unpin(n.id) // loose again: its handle owns it
389
+ }
345
390
  return this
346
391
  }
347
392
 
@@ -422,39 +467,47 @@ export class Scene implements Presentable {
422
467
  this.cl.push(callback)
423
468
  return this
424
469
  }
425
- onBackPressed(callback: () => void): this {
470
+ /** Hardware/system back while the scene is current. */
471
+ onBack(callback: () => void): this {
426
472
  this._backButtonCallback = callback
427
473
  return this
428
474
  }
429
-
430
- /** @internal stable wire descriptor; hosts hand it back via router onChange (`_p` = this). */
431
- _viewDesc(): object {
432
- return this._vd ??= { type: "scene3d", sceneId: this._id, _p: this }
475
+ /** @deprecated Renamed `onBack` (2026-09-26). An alias for the projects compiled before the
476
+ * rename; removed with the release that recompiles them. */
477
+ onBackPressed(callback: () => void): this { return this.onBack(callback) }
478
+
479
+ /** @internal The destination tuple (ui/presentable.ts). */
480
+ _dest(): DestTuple {
481
+ // Addressed by the router / an attached widget: make sure the (kind, id) resolves back to us.
482
+ registerDest(TREE_DEST_SCENE3D, this._id, this)
483
+ return [TREE_DEST_SCENE3D, this._id, "", ""]
484
+ }
485
+ /** @internal presentation lifecycle from the runtime */
486
+ _emitOpen(): void { for (const cb of this.ol.slice()) cb() }
487
+ /** @internal */
488
+ _emitClose(): void { for (const cb of this.cl.slice()) cb() }
489
+ /** @internal */
490
+ _emitBack(): boolean {
491
+ if (!this._backButtonCallback) return false
492
+ this._backButtonCallback()
493
+ return true
433
494
  }
434
495
 
435
496
  /** Make this the active scene — shows it as the current destination (replaces a screen /
436
- * another scene; only the active scene renders). */
497
+ * another scene; only the active scene renders). The runtime activates the engine as part of
498
+ * presenting the destination. */
437
499
  open(options?: PresentOptions): void | Promise<void> {
438
500
  _bumpNavEpoch()
439
- if (_navSupported()) {
440
- // The host activates the engine (openScene) as part of presenting the destination.
441
- _creatorUI.openView!(this._viewDesc(), options?.transition ?? "none")
442
- } else {
443
- _creator.openScene(this._id)
444
- for (const cb of this.ol) cb()
445
- }
501
+ ensureEmitter()
502
+ registerDest(TREE_DEST_SCENE3D, this._id, this)
503
+ _creatorTree.open(TREE_DEST_SCENE3D, this._id, "", "", _transitionId(options?.transition, TRANSITION_NONE))
446
504
  Scene._active = this
447
505
  glState.activeScene = this
448
506
  _setCurrent(this)
449
507
  }
450
- close(): void {
508
+ close(options?: DismissOptions): void {
451
509
  _bumpNavEpoch()
452
- if (_navSupported()) {
453
- _creatorUI.closeView!()
454
- } else {
455
- _creator.closeScene()
456
- for (const cb of this.cl) cb()
457
- }
510
+ _creatorTree.close(TREE_DEST_SCENE3D, this._id, _transitionId(options?.transition, TRANSITION_NONE))
458
511
  if (Scene._active === this) Scene._active = null
459
512
  if (glState.activeScene === this) glState.activeScene = null
460
513
  if (Presentable.current === this) _setCurrent(null)
@@ -481,7 +534,7 @@ export class Scene implements Presentable {
481
534
  export type ARMode = "default" | "markers" | "arcore" | "detached"
482
535
 
483
536
  const onTrackStateChange = (entityId: number, track: boolean): void => {
484
- const node = nodeRegistry.get(entityId)
537
+ const node = nodes.get(entityId)
485
538
  if (!node) return
486
539
  node.isTracked = track
487
540
  if (track) node._dispatchEvent("track")
@@ -508,7 +561,7 @@ export class ARScene<T extends ARMode = "default"> extends Scene {
508
561
 
509
562
  /** Create an anchor from a tracking source (e.g. an image target). */
510
563
  createAnchor(source: FetchResponse, physicalWidth = 0.2): Node {
511
- return new Node(_creator.createAnchor(this._id, physicalWidth, (source as any)._id))
564
+ return new Node(_creator.createAnchor(this._id, physicalWidth, (source as any)._h.id))
512
565
  }
513
566
 
514
567
  /**
@@ -543,9 +596,12 @@ export class ARScene<T extends ARMode = "default"> extends Scene {
543
596
  }
544
597
  }
545
598
 
546
- override close(): void {
547
- _creator.stopAR()
548
- super.close()
599
+ override close(options?: DismissOptions): void {
600
+ // The session is this scene's unless another ARScene is on screen now: a late close of a scene
601
+ // something already replaced must not stop the next one's session.
602
+ const current = Presentable.current
603
+ if (!(current instanceof ARScene) || current === this) _creator.stopAR()
604
+ super.close(options)
549
605
  }
550
606
  }
551
607
 
@@ -565,8 +621,8 @@ export class VRScene extends Scene {
565
621
  super.open(options)
566
622
  }
567
623
 
568
- override close(): void {
624
+ override close(options?: DismissOptions): void {
569
625
  _creator.stopVR()
570
- super.close()
626
+ super.close(options)
571
627
  }
572
628
  }
package/src/gl/Texture.ts CHANGED
@@ -37,8 +37,9 @@ export class Texture {
37
37
  Texture._maxSize = size > 0 ? Math.floor(size) : 0
38
38
  _creator.setTextureMaxSize?.(Texture._maxSize)
39
39
  }
40
- /** @internal native texture handle. */
41
- readonly _id: number
40
+ /** @internal The texture's owned handle (`_h.id` = the engine id). A texture the engine or another
41
+ * object owns (a canvas's, a video player's) is wrapped from its number and never freed by us. */
42
+ readonly _h: Handle
42
43
  readonly width: number
43
44
  readonly height: number
44
45
 
@@ -47,10 +48,10 @@ export class Texture {
47
48
  /** Vertical wrap mode (V). */
48
49
  wrapT = 1
49
50
 
50
- constructor(width: number, height: number, id: number) {
51
+ constructor(width: number, height: number, id: Handle | number) {
51
52
  this.width = width
52
53
  this.height = height
53
- this._id = id
54
+ this._h = typeof id === "number" ? { id } : id
54
55
  }
55
56
 
56
57
  // Wrap a Canvas's baked surface as a 3D texture (RGBA8, already rasterized → synchronous, no decode).
@@ -72,13 +73,13 @@ export class Texture {
72
73
  const channels = opts.channels ?? 4
73
74
  if (data.length < width * height * channels) throw new Error(`Texture.fromPixels: ${data.length} bytes for ${width}×${height}×${channels}`)
74
75
  const id = _creator.createTexturePixels(width, height, channels, data, opts.srgb ?? true)
75
- if (id === 0xFFFFFFFF) throw new Error("Texture.fromPixels: the host refused the texture")
76
+ if (id.id === 0xFFFFFFFF) throw new Error("Texture.fromPixels: the host refused the texture")
76
77
  return new Texture(width, height, id)
77
78
  }
78
79
 
79
80
  /** Re-upload a rectangle of a `fromPixels` texture (same channel count). */
80
81
  update(x: number, y: number, width: number, height: number, data: Uint8Array): void {
81
- _creator.updateTexturePixels?.(this._id, x, y, width, height, data)
82
+ _creator.updateTexturePixels?.(this._h.id, x, y, width, height, data)
82
83
  }
83
84
 
84
85
  /** Decode an image (PNG / JPG, or a KTX2 the core transcodes) into a texture. `srgb` (default
@@ -97,7 +98,7 @@ export class Texture {
97
98
  }
98
99
  const flags = (options.srgb === false ? TEXTURE_FLAG_LINEAR : 0) | (options.fullSize ? TEXTURE_FLAG_FULL_SIZE : 0)
99
100
  return new Promise<Texture>((resolve, reject) => {
100
- _creator.createTexture((source as any)._id, (id, w, h) => resolve(new Texture(w, h, id)), reject, flags)
101
+ _creator.createTexture((source as any)._h.id, (id, w, h) => resolve(new Texture(w, h, id)), reject, flags)
101
102
  })
102
103
  }
103
104
  }
@@ -270,7 +270,7 @@ export class AnimationClip {
270
270
  })
271
271
  }
272
272
  return new Promise<[number, ClipInfo[]]>((resolve, reject) => {
273
- _creator.loadClips((source as unknown as { _id: number })._id, (setId: number) => {
273
+ _creator.loadClips((source as unknown as { _h: Handle })._h.id, (setId: number) => {
274
274
  if (setId === 0) { reject(new Error("AnimationClip.load: the file has no animations (or is not a GLB)")); return }
275
275
  resolve([setId, readInfo(setId)])
276
276
  }, reject)