lecodes-sdk 2.4.7 → 2.4.8
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/dist/global.d.ts +7 -0
- package/dist/types/gl/Camera.d.ts +100 -13
- package/dist/types/gl/Node.d.ts +13 -0
- package/dist/types/gl/ReflectionProbe.d.ts +87 -0
- package/dist/types/gl/RenderTexture.d.ts +29 -0
- package/dist/types/gl/Scene.d.ts +21 -0
- package/dist/types/inject.d.ts +4 -0
- package/dist/types/ui/tree.d.ts +1 -0
- package/dist/types/version.d.ts +1 -1
- package/dist/types.json +1 -1
- package/package.json +1 -1
- package/src/bridges/gl.d.ts +34 -0
- package/src/gl/Camera.ts +198 -25
- package/src/gl/Node.ts +61 -0
- package/src/gl/ReflectionProbe.ts +141 -0
- package/src/gl/RenderTexture.ts +93 -0
- package/src/gl/Scene.ts +54 -2
- package/src/inject.ts +4 -0
- package/src/ui/tree.ts +1 -0
- package/src/version.ts +1 -1
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
// A texture a render view draws into (creator-gl renderview.cpp; creator.h createRenderTexture / createRenderView /
|
|
2
|
+
// setRenderViewOptions / setRenderViewProjection / readRenderTexturePixels). Only the texture: the view drawing into it
|
|
3
|
+
// belongs to what made it — a secondary Camera (`camera.renderToTexture`, a 2D picture) or a ReflectionProbe (a cube) —
|
|
4
|
+
// which holds the view's owned handle, its schedule and its projection. The helpers below are those two makers' shared
|
|
5
|
+
// half: the host's texture, and the view started once their node is in a scene (Node.ts awaitScene).
|
|
6
|
+
|
|
7
|
+
import { Texture } from "./Texture"
|
|
8
|
+
|
|
9
|
+
/** @internal `_creator.createRenderTexture` flags (creator.h CREATOR_RENDER_TEXTURE_*). */
|
|
10
|
+
export const RT_FLAG_CUBEMAP = 1
|
|
11
|
+
/** @internal */
|
|
12
|
+
export const RT_FLAG_HDR = 2
|
|
13
|
+
/** @internal */
|
|
14
|
+
export const RT_FLAG_PREFILTER = 4
|
|
15
|
+
/** @internal `_creator.createRenderView` flags bit: no shadow maps in this view (creator.h CREATOR_RENDER_VIEW_SHADOWS_OFF). */
|
|
16
|
+
const VIEW_FLAG_SHADOWS_OFF = 1
|
|
17
|
+
/** @internal The longest side the host takes (creator.h createRenderTexture). */
|
|
18
|
+
export const RT_MAX_SIDE = 4096
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* A texture a render view draws into — made by `camera.renderToTexture({ width, height })` (a 2D picture: the
|
|
22
|
+
* tone-mapped image as the screen would show it) or by a `ReflectionProbe` (`probe.texture`: a cube of linear HDR light).
|
|
23
|
+
* It IS a `Texture`: a 2D one goes wherever an image does (`material.map` — a monitor, a rear-view mirror), a cube
|
|
24
|
+
* wherever a cubemap does (`scene.environment` takes the probe itself).
|
|
25
|
+
*
|
|
26
|
+
* ROW ORDER: a 2D render texture's first texel row is the BOTTOM of the camera's picture (OpenGL's order, on every
|
|
27
|
+
* backend) — an image's first row is its top. A mesh whose v runs UP its face (`Mesh.plane`) shows it upright as is; one
|
|
28
|
+
* laid out like a glTF (v down) samples it with v flipped. A cube face is laid out as every GPU's cube lookup reads it.
|
|
29
|
+
*
|
|
30
|
+
* LIFETIME: the texture lives while something holds it — the camera or probe that made it (each keeps its texture), a
|
|
31
|
+
* material showing it — and keeps its last picture when its view stops (`enabled = false`, the camera's next
|
|
32
|
+
* `renderToTexture`, the camera gone).
|
|
33
|
+
*/
|
|
34
|
+
export class RenderTexture extends Texture {
|
|
35
|
+
/** A cube (a `ReflectionProbe`'s) rather than a 2D picture (a `Camera`'s). A cube's `width` and `height` are both its
|
|
36
|
+
* edge. */
|
|
37
|
+
readonly cubemap: boolean
|
|
38
|
+
|
|
39
|
+
/** @internal Made by `Camera.renderToTexture` and `ReflectionProbe` (through `makeRenderTexture`): the size, the
|
|
40
|
+
* kind and the texture's owned handle. */
|
|
41
|
+
constructor(width: number, height: number, cubemap: boolean, texture: Handle) {
|
|
42
|
+
super(width, height, texture)
|
|
43
|
+
this.cubemap = cubemap
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* RGBA8 bytes (`width × height × 4`) of a rectangle of the texture as the NEXT frame drawn leaves it — a GPU
|
|
48
|
+
* readback, for checks and tools, not a per-frame path. Rows come in the texture's own order: the first is texel row
|
|
49
|
+
* `y`, which for a 2D texture counts from the picture's BOTTOM. `face` 0..5 = +X −X +Y −Y +Z −Z of a cube (0 for a 2D
|
|
50
|
+
* picture). A camera's (LDR) texture gives its stored sRGB bytes, a probe's (HDR) its linear light clamped to 0..1 —
|
|
51
|
+
* a prefiltered probe its sharpest level. Rejects on a host without the call, or when the host refuses the read (the
|
|
52
|
+
* rectangle outside the texture, the engine gone first).
|
|
53
|
+
*/
|
|
54
|
+
readPixels(x: number, y: number, width: number, height: number, face = 0): Promise<Uint8Array> {
|
|
55
|
+
if (!_creator.readRenderTexturePixels) return Promise.reject(new Error("RenderTexture.readPixels: this host has no readRenderTexturePixels"))
|
|
56
|
+
return new Promise<Uint8Array>((resolve, reject) => {
|
|
57
|
+
_creator.readRenderTexturePixels!(this._h.id, face, x, y, width, height, (pixels) => {
|
|
58
|
+
if (pixels) resolve(pixels)
|
|
59
|
+
else reject(new Error(`RenderTexture.readPixels: the host refused (${x}, ${y}) ${width}×${height} of face ${face}`))
|
|
60
|
+
})
|
|
61
|
+
})
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** @internal Throw for `who` on a host without render textures (createRenderTexture + createRenderView). */
|
|
66
|
+
export const requireRenderTextures = (who: string): void => {
|
|
67
|
+
if (!_creator.createRenderTexture || !_creator.createRenderView) {
|
|
68
|
+
throw new Error(`${who}: this host has no render textures (createRenderTexture) — the web hosts do not yet`)
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** @internal The host's render texture (`flags` RT_FLAG_*; a cube's `height` is its `width`), or a throw naming `who`. */
|
|
73
|
+
export const makeRenderTexture = (who: string, width: number, height: number, flags: number, samples = 0): RenderTexture => {
|
|
74
|
+
requireRenderTextures(who)
|
|
75
|
+
const cube = (flags & RT_FLAG_CUBEMAP) !== 0
|
|
76
|
+
const texture = _creator.createRenderTexture!(width, cube ? 0 : height, flags, samples)
|
|
77
|
+
if (!texture || texture.id === 0xFFFFFFFF) {
|
|
78
|
+
throw new Error(`${who}: the host refused a ${cube ? `cube of ${width}` : `${width}×${height}`} render texture (sides 1..${RT_MAX_SIDE})`)
|
|
79
|
+
}
|
|
80
|
+
return new RenderTexture(width, cube ? width : height, cube, texture)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** @internal Start a render view of scene `sceneId` into `texture` from entity `poseId` (createRenderView), or warn for
|
|
84
|
+
* `who` and give null — the host refuses an overlay scene. */
|
|
85
|
+
export const startRenderView = (
|
|
86
|
+
who: string, sceneId: number, texture: RenderTexture, poseId: number,
|
|
87
|
+
layers: number, fov: number, near: number, far: number, shadows: boolean,
|
|
88
|
+
): Handle | null => {
|
|
89
|
+
const view = _creator.createRenderView!(sceneId, texture._h.id, poseId, layers & 0xff, fov, near, far, shadows ? 0 : VIEW_FLAG_SHADOWS_OFF)
|
|
90
|
+
if (view && view.id !== 0) return view
|
|
91
|
+
console.warn(`[${who}] the host made no render view in scene ${sceneId} (an overlay scene has none) — its texture stays empty`)
|
|
92
|
+
return null
|
|
93
|
+
}
|
package/src/gl/Scene.ts
CHANGED
|
@@ -18,8 +18,9 @@ import { Camera } from "./Camera"
|
|
|
18
18
|
import { SceneAudio } from "./audio/SceneAudio"
|
|
19
19
|
import { Material } from "./Material"
|
|
20
20
|
import { PostProcessing, type PostProcessingOptions } from "./postProcessing"
|
|
21
|
+
import { ReflectionProbe } from "./ReflectionProbe"
|
|
21
22
|
import { Texture } from "./Texture"
|
|
22
|
-
import { Node, nodes } from "./Node"
|
|
23
|
+
import { Node, bindPendingViews, nodes, sceneIdOf } from "./Node"
|
|
23
24
|
import { glState } from "./state"
|
|
24
25
|
import { registerTouchEndEvent, registerTouchStartEvent } from "./touch"
|
|
25
26
|
|
|
@@ -176,6 +177,12 @@ export class Scene implements Presentable {
|
|
|
176
177
|
/** @internal what `environmentIntensity` currently asks for — the accessor's backing field, so it
|
|
177
178
|
* answers on hosts whose engine has no live setter too. */
|
|
178
179
|
_environmentIntensity = 20000
|
|
180
|
+
/** @internal The probe the constructor named for `setDefaultIbl` (-1 = none: the project-wide
|
|
181
|
+
* `ibl.ktx`) — re-sent when `environment` goes back to null. */
|
|
182
|
+
_iblId = -1
|
|
183
|
+
/** @internal What `environment` lights the scene from (null = the scene's own probe). Holding it
|
|
184
|
+
* keeps the probe — its cube and the view drawing into it — alive while it lights. */
|
|
185
|
+
_environment: ReflectionProbe | null = null
|
|
179
186
|
readonly camera: Camera
|
|
180
187
|
/** The listener + global 3D audio knobs (docs/audio-plan.md). */
|
|
181
188
|
readonly audio: SceneAudio = new SceneAudio()
|
|
@@ -226,6 +233,7 @@ export class Scene implements Presentable {
|
|
|
226
233
|
// "no probe named", which leaves the host on the project-wide `ibl.ktx` (the old behaviour).
|
|
227
234
|
const src = typeof options.ibl === "string" ? options.ibl : undefined
|
|
228
235
|
const iblId = src === undefined ? -1 : src.startsWith("id:") ? Number(src.slice(3)) : _creatorFetch.local(src)
|
|
236
|
+
this._iblId = iblId
|
|
229
237
|
_creator.setDefaultIbl(this._id, options.environmentIntensity ?? 20000, iblId)
|
|
230
238
|
}
|
|
231
239
|
this._environmentIntensity = options.ibl === false ? 0 : (options.environmentIntensity ?? 20000)
|
|
@@ -341,6 +349,44 @@ export class Scene implements Presentable {
|
|
|
341
349
|
}
|
|
342
350
|
}
|
|
343
351
|
private static _warnedEnvIntensity = false
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* Light the scene from a live REFLECTION PROBE (ReflectionProbe.ts) — a cube drawn around the probe's position, the
|
|
355
|
+
* probe riding the car / standing in the room: chrome and glass show what is actually around them, moving objects
|
|
356
|
+
* included. `null` gives the light back to the scene's own probe (`SceneOptions.ibl`, at `environmentIntensity`;
|
|
357
|
+
* re-installing it reloads the probe — a switch, not a per-frame call). A probe in no scene yet starts its view in this
|
|
358
|
+
* one; one already in another scene draws only while that scene is on show.
|
|
359
|
+
*
|
|
360
|
+
* Prefiltered (the default), the probe gives the reflections at every roughness — a car body, painted metal blur it as
|
|
361
|
+
* they should — and the diffuse ambient too (then `environmentIntensity` no longer changes it); it costs a GPU
|
|
362
|
+
* filtering per completed cube (`ReflectionProbeOptions.prefilter`). `prefilter: false` gives one sharp level:
|
|
363
|
+
* mirror-like surfaces (roughness near 0) are right, rougher ones read the sharp cube too, and only the reflections
|
|
364
|
+
* change — the diffuse ambient keeps the scene's own probe's shape at `environmentIntensity`.
|
|
365
|
+
*
|
|
366
|
+
* A probe that draws while it lights reads what it writes — fine on OpenGL; a Vulkan backend may want two probes in
|
|
367
|
+
* turn (a prefiltered probe never does: it lights from the filtered copy). Leave the probe's own carrier out of its view
|
|
368
|
+
* (`layers`), or the cube is the inside of the car. Warns once on a host without the call.
|
|
369
|
+
*/
|
|
370
|
+
get environment(): ReflectionProbe | null { return this._environment }
|
|
371
|
+
set environment(probe: ReflectionProbe | null) {
|
|
372
|
+
if (probe !== null && !(probe instanceof ReflectionProbe)) {
|
|
373
|
+
throw new Error("scene.environment: a ReflectionProbe (new ReflectionProbe({ size: 128 })) or null")
|
|
374
|
+
}
|
|
375
|
+
if (probe === this._environment) return
|
|
376
|
+
this._environment = probe
|
|
377
|
+
if (probe && sceneIdOf(probe) < 0) probe._bindView(this._id) // in no scene yet: its view starts in this one
|
|
378
|
+
if (!_creator.setEnvironmentTexture) {
|
|
379
|
+
if (!Scene._warnedEnvTexture) {
|
|
380
|
+
Scene._warnedEnvTexture = true
|
|
381
|
+
console.warn("[scene] this host has no setEnvironmentTexture — environment stays the scene's own probe")
|
|
382
|
+
}
|
|
383
|
+
return
|
|
384
|
+
}
|
|
385
|
+
if (probe) _creator.setEnvironmentTexture(this._id, probe.texture._h.id)
|
|
386
|
+
else _creator.setDefaultIbl(this._id, this._environmentIntensity, this._iblId) // installing a probe frees the cube's light
|
|
387
|
+
}
|
|
388
|
+
private static _warnedEnvTexture = false
|
|
389
|
+
|
|
344
390
|
/** The scene's stencil buffer on / off (see `SceneOptions.stencil`). */
|
|
345
391
|
setStencil(enabled: boolean): void {
|
|
346
392
|
if (_creator.setSceneStencil) _creator.setSceneStencil(this._id, enabled)
|
|
@@ -348,12 +394,18 @@ export class Scene implements Presentable {
|
|
|
348
394
|
}
|
|
349
395
|
|
|
350
396
|
add(...list: Node[]): this {
|
|
351
|
-
for (const n of list) {
|
|
397
|
+
for (const n of list) {
|
|
398
|
+
_creator.addEntityToScene(this._id, n.id)
|
|
399
|
+
nodes.pin(n) // the scene owns it now
|
|
400
|
+
n._inScene = this._id // what a render view under it starts in (Node.ts sceneIdOf)
|
|
401
|
+
}
|
|
402
|
+
bindPendingViews()
|
|
352
403
|
return this
|
|
353
404
|
}
|
|
354
405
|
remove(...list: Node[]): this {
|
|
355
406
|
for (const n of list) {
|
|
356
407
|
_creator.removeEntityFromScene(this._id, n.id)
|
|
408
|
+
if (n._inScene === this._id) n._inScene = -1
|
|
357
409
|
if (!_creator.entityIsOwned(n.id)) nodes.unpin(n.id) // loose again: its handle owns it
|
|
358
410
|
}
|
|
359
411
|
return this
|
package/src/inject.ts
CHANGED
|
@@ -251,7 +251,11 @@ export type { ScatterPlacement, ScatterOptions } from "./gl/Scatter"
|
|
|
251
251
|
export type { InstanceTransform } from "./gl/InstancedMesh"
|
|
252
252
|
export { Texture } from "./gl/Texture"
|
|
253
253
|
export type { TextureLoadOptions } from "./gl/Texture"
|
|
254
|
+
export { RenderTexture } from "./gl/RenderTexture"
|
|
254
255
|
export { Camera } from "./gl/Camera"
|
|
256
|
+
export type { CameraOptions, RenderToTextureOptions } from "./gl/Camera"
|
|
257
|
+
export { ReflectionProbe } from "./gl/ReflectionProbe"
|
|
258
|
+
export type { ReflectionProbeOptions } from "./gl/ReflectionProbe"
|
|
255
259
|
export { Ray } from "./gl/Ray"
|
|
256
260
|
export { Plane } from "./gl/Plane"
|
|
257
261
|
export { Noise } from "./gl/Noise"
|
package/src/ui/tree.ts
CHANGED
|
@@ -93,6 +93,7 @@ export const OWNED_KIND_ENTITY_2D = 5
|
|
|
93
93
|
export const OWNED_KIND_MEDIA = 6
|
|
94
94
|
export const OWNED_KIND_TWEEN = 7
|
|
95
95
|
export const OWNED_KIND_CANVAS = 8
|
|
96
|
+
export const OWNED_KIND_GL_RENDER_VIEW = 9
|
|
96
97
|
|
|
97
98
|
// PanDirection (creator-pkg.h) — the low byte of a track answer.
|
|
98
99
|
import { DEAD_HANDLE } from "../core/pins"
|
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.
|
|
7
|
+
export const SDK_VERSION = "2.4.8"
|
|
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 => {
|