lecodes-sdk 2.0.12 → 2.1.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/dist/types/gl/Scene.d.ts +13 -28
- package/dist/types/gl/postProcessing.d.ts +228 -0
- package/dist/types/runtime/app.d.ts +30 -1
- package/dist/types/version.d.ts +1 -1
- package/dist/types.json +1 -1
- package/package.json +2 -2
- package/prompts/3d-scene.md +5 -2
- package/src/bridges/app.d.ts +18 -1
- package/src/bridges/gl.d.ts +86 -34
- package/src/gl/Scene.ts +15 -40
- package/src/gl/postProcessing.ts +450 -0
- package/src/runtime/app.ts +40 -1
- package/src/server/db/defineDb.ts +1 -0
- package/src/server/db/types.ts +5 -1
- package/src/server/runtime.ts +4 -1
- package/src/version.ts +1 -1
- package/tests/helpers/hostStubs.ts +16 -7
package/package.json
CHANGED
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
},
|
|
28
28
|
"scripts": {
|
|
29
29
|
"build": "bun run build-types.ts",
|
|
30
|
-
"test": "bun test",
|
|
30
|
+
"test": "bun test --randomize",
|
|
31
31
|
"typecheck": "tsc --noEmit",
|
|
32
32
|
"docs:check": "node docs/check.mjs",
|
|
33
33
|
"prompts:build": "bun prompts/compose.ts",
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"marcidb-embedded": "^0.14.1",
|
|
46
46
|
"sharp": "^0.35.2"
|
|
47
47
|
},
|
|
48
|
-
"version": "2.0
|
|
48
|
+
"version": "2.1.0",
|
|
49
49
|
"files": [
|
|
50
50
|
"src",
|
|
51
51
|
"dist",
|
package/prompts/3d-scene.md
CHANGED
|
@@ -5,8 +5,11 @@
|
|
|
5
5
|
new Scene(options?: {
|
|
6
6
|
ibl?: boolean // image-based ambient light; default TRUE — every scene has ambient
|
|
7
7
|
environmentIntensity?: number // IBL strength; default 20000
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
postProcessing?: { // the look: tone mapping, grading, a .cube LUT, bloom, vignette, AO, reflections, DoF
|
|
9
|
+
toneMapping?: 'aces' | 'neutral' | 'linear' | 'filmic', exposure?: number, contrast?: number, saturation?: number,
|
|
10
|
+
lut?: string, bloom?: boolean | { intensity?: number }, vignette?: boolean | { feather?: number },
|
|
11
|
+
ambientOcclusion?: boolean | { radius?: number }, screenSpaceReflections?: boolean, depthOfField?: boolean | { focusDistance?: number, aperture?: number }
|
|
12
|
+
} // live: scene.postProcessing.contrast = 1.1; .apply({ ... }) = the named fields only; .reset(); never assigned
|
|
10
13
|
skybox?: ColorInput // solid background / clear color
|
|
11
14
|
antialias?: boolean // 4× MSAA; default false
|
|
12
15
|
})
|
package/src/bridges/app.d.ts
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
// `?` on a method = an optional slot: a host that leaves it null gets no such JS method, so the SDK's
|
|
12
12
|
// `typeof` feature-detects stay honest. The `HostApp` interface below adds the slots the runtime
|
|
13
13
|
// calls that JS never sees (notifications to the host).
|
|
14
|
-
import type { bytes } from "./types"
|
|
14
|
+
import type { bytes, i32 } from "./types"
|
|
15
15
|
|
|
16
16
|
declare global {
|
|
17
17
|
// ---- World control (run/restart/quit — the platform's `location` analog) ----------------------
|
|
@@ -73,6 +73,23 @@ declare global {
|
|
|
73
73
|
* synchronous top level and the launcher always comes back unlocked. Optional — hosts
|
|
74
74
|
* without a rotatable screen (desktop, headless, macOS) omit it. Backs app.setOrientation. */
|
|
75
75
|
setOrientation?(mode: string): void
|
|
76
|
+
|
|
77
|
+
// ---- Versions: the installed app (the HOST) vs the bundle running in it (OTA may part them) -----
|
|
78
|
+
/** The installed app's version string — Android versionName, iOS CFBundleShortVersionString.
|
|
79
|
+
* Hosts that are not an app (desktop, headless, the web) omit it. Backs app.host.version. */
|
|
80
|
+
appVersion?(): string
|
|
81
|
+
/** The installed app's build number — Android versionCode, iOS CFBundleVersion read as an
|
|
82
|
+
* integer (0 when it is not one). Backs app.host.build. */
|
|
83
|
+
appBuild?(): i32
|
|
84
|
+
/** The SDK version embedded in this host (CREATOR_PKG_SDK_VERSION, the runtime set's version — a
|
|
85
|
+
* shell's lecodes-ios-sdk / lecodes-android-sdk release). The bundle's own is `// sdk:` of its
|
|
86
|
+
* header (app.sdkVersion); the two differ once an OTA bundle runs on an older install. Backs
|
|
87
|
+
* app.host.sdk. @custom */
|
|
88
|
+
runtimeVersion(): string
|
|
89
|
+
/** The `// last-updated:` line of the bundle the live world runs (its compile time, ISO 8601) or
|
|
90
|
+
* null for a bundle without the line. The updater's downgrade rule reads the same line. Backs
|
|
91
|
+
* app.bundle.updatedAt. @custom */
|
|
92
|
+
bundleUpdatedAt(): string | null
|
|
76
93
|
}
|
|
77
94
|
}
|
|
78
95
|
|
package/src/bridges/gl.d.ts
CHANGED
|
@@ -113,8 +113,35 @@ declare global {
|
|
|
113
113
|
* ktx every call, so it can never carry a slider. No-op until the scene has an IBL. Optional:
|
|
114
114
|
* hosts that predate it keep whatever `setDefaultIbl` set at open. */
|
|
115
115
|
setEnvironmentIntensity?(sceneId: u32, intensity: f32): void
|
|
116
|
-
|
|
117
|
-
|
|
116
|
+
/** Bloom, every knob in one call (`scene.postProcessing.bloom`): `strength` 0..1 of the blurred
|
|
117
|
+
* highlights added back, `threshold` = only what is brighter than 1.0 after exposure blooms,
|
|
118
|
+
* `highlight` clamps one pixel's contribution (1000), `levels` 1..12 = how far the glow spreads,
|
|
119
|
+
* `quality` 0..3, `lensFlare` = ghosts + halo, `dirtTextureId` (0xFFFFFFFF = none) a texture
|
|
120
|
+
* multiplied into the bloom at `dirtStrength`. */
|
|
121
|
+
setBloomOptions(sceneId: u32, enabled: boolean, strength: f32, threshold: boolean, highlight: f32, levels: u8, quality: u8, lensFlare: boolean, dirtTextureId: u32, dirtStrength: f32): void
|
|
122
|
+
/** Vignette, a uniform of the grading pass (free to animate): `midPoint` 0..1 where the darkening
|
|
123
|
+
* starts, `roundness` 0 (the aspect) .. 1 (a circle), `feather` 0..1, `color` 0xRRGGBBAA read as
|
|
124
|
+
* linear. Optional: a host that predates it draws none. */
|
|
125
|
+
setVignetteOptions?(sceneId: u32, enabled: boolean, midPoint: f32, roundness: f32, feather: f32, color: u32): void
|
|
126
|
+
/** Colour grading: the operator and every adjustment baked into the view's ONE 3D LUT. `params` is
|
|
127
|
+
* the fixed layout of `sdk/src/gl/postProcessing.ts` (`CG_*`, mirrored by creator.h's
|
|
128
|
+
* ColorGradingParam; 39 floats, the last one = apply the LUT of setColorLut). A change re-bakes a 32³ table on the CPU, so the SDK coalesces
|
|
129
|
+
* its writes per tick and quantises; an identical array is a no-op in the engine. Optional: a
|
|
130
|
+
* host that predates it renders filament's default (ACES legacy). */
|
|
131
|
+
setColorGrading?(sceneId: u32, params: Float32Array): void
|
|
132
|
+
/** A custom 3D LUT after the operator, in display-referred sRGB: the TEXT of a `.cube` file
|
|
133
|
+
* (`LUT_3D_SIZE n` + n³ `r g b` lines, red fastest), parsed by the engine once and applied while
|
|
134
|
+
* the `CG_LUT` flag of setColorGrading is 1 (a LUT is removed by the flag, never by an empty
|
|
135
|
+
* buffer: the bridge passes none). Baked into the same table. Optional. */
|
|
136
|
+
setColorLut?(sceneId: u32, cubeFetchId: bytes): void
|
|
137
|
+
/** Screen-space reflections (`scene.postProcessing.screenSpaceReflections`): what is on screen
|
|
138
|
+
* reflects, by a ray march through the depth buffer — `maxDistance` metres, `thickness` metres a
|
|
139
|
+
* surface extends behind its depth, `stride` pixels per step. Optional. */
|
|
140
|
+
setScreenSpaceReflections?(sceneId: u32, enabled: boolean, maxDistance: f32, thickness: f32, stride: f32): void
|
|
141
|
+
/** Depth of field: the camera focuses at `focusDistance` metres and the view blurs as if the lens
|
|
142
|
+
* were at f/`aperture`, without touching the exposure (0 = the camera's own f-number), times
|
|
143
|
+
* `strength` (1 = physical; a game's wide lens blurs little). Optional. */
|
|
144
|
+
setDepthOfField?(sceneId: u32, enabled: boolean, focusDistance: f32, aperture: f32, strength: f32): void
|
|
118
145
|
/** Exposure of the scene's camera — filament's physical model: `aperture` f-stops,
|
|
119
146
|
* `shutterSpeed` seconds, `sensitivity` ISO (default f/16, 1/125 s, ISO 100 = EV100 15, bright
|
|
120
147
|
* sunlight). ISO ×2 = one stop brighter. Scene-referred light follows it; particle emission is
|
|
@@ -742,26 +769,32 @@ declare global {
|
|
|
742
769
|
/** Distance fade for every instance of the entity's ASSET (present and future): the cards thin out between `start`
|
|
743
770
|
* and `end` metres from the camera and past `end` the LOD pass takes the instance out of the scene. end 0 = none. */
|
|
744
771
|
foliageSetFade?(entityId: u32, start: f32, end: f32): void
|
|
772
|
+
/** @ifdef CREATOR_GL_PHYSICS */
|
|
745
773
|
physicsConfigure(gx: f32, gy: f32, gz: f32, maxBodies: u32): void
|
|
746
|
-
/** @c setPhysicsInterpolation */
|
|
774
|
+
/** @c setPhysicsInterpolation @ifdef CREATOR_GL_PHYSICS */
|
|
747
775
|
setInterpolation(enabled: boolean): void
|
|
748
776
|
// Shape/Physics/Trigger aspects: build a shape once, create bodies from it.
|
|
777
|
+
/** @ifdef CREATOR_GL_PHYSICS */
|
|
749
778
|
physicsBuildBox(hx: f32, hy: f32, hz: f32): u32
|
|
779
|
+
/** @ifdef CREATOR_GL_PHYSICS */
|
|
750
780
|
physicsBuildSphere(radius: f32): u32
|
|
781
|
+
/** @ifdef CREATOR_GL_PHYSICS */
|
|
751
782
|
physicsBuildCylinder(halfHeight: f32, radius: f32): u32
|
|
783
|
+
/** @ifdef CREATOR_GL_PHYSICS */
|
|
752
784
|
physicsBuildCapsule(halfHeight: f32, radius: f32): u32
|
|
753
785
|
/** Mesh shape from node-local triangles. convex=false → triangle mesh (static/kinematic/pick/character
|
|
754
786
|
* only; physicsCreateBody returns 0 for a dynamic one), convex=true → convex hull (any motion).
|
|
755
|
-
* (sx,sy,sz) = world scale, applied natively. Returns 0 if the shape can't be built. @count vertexCount=vertices/3 */
|
|
787
|
+
* (sx,sy,sz) = world scale, applied natively. Returns 0 if the shape can't be built. @count vertexCount=vertices/3 @ifdef CREATOR_GL_PHYSICS */
|
|
756
788
|
physicsBuildMesh(vertices: Float32Array, indices: Uint32Array, convex: boolean, sx: f32, sy: f32, sz: f32): u32
|
|
757
|
-
/** Same from a loaded GLB root (non-skinned primitives, bind pose, baked sub-node transforms; cached per asset). */
|
|
789
|
+
/** Same from a loaded GLB root (non-skinned primitives, bind pose, baked sub-node transforms; cached per asset). @ifdef CREATOR_GL_PHYSICS */
|
|
758
790
|
physicsBuildMeshFromEntity(entityId: u32, convex: boolean, sx: f32, sy: f32, sz: f32): u32
|
|
759
791
|
/** Terrain collider (Shape { heightfield: true }, docs/terrain-plan.md §1.4): a Jolt HeightFieldShape over
|
|
760
792
|
* the grid `terrainCreate` draws (same arrays, same hole rule). Static / kinematic / pick / character
|
|
761
793
|
* ground only. (sx,sy,sz) = world scale. `physicsUpdateHeightField` rewrites a sample rectangle in
|
|
762
794
|
* place from the FULL arrays (live bodies keep the shape); heights beyond the range chosen at build
|
|
763
|
-
* time (25 % headroom) clamp — the SDK rebuilds the shape when an edit leaves that range. Optional. */
|
|
795
|
+
* time (25 % headroom) clamp — the SDK rebuilds the shape when an edit leaves that range. Optional. @ifdef CREATOR_GL_PHYSICS */
|
|
764
796
|
physicsBuildHeightField?(heights: Float32Array, sizeX: u32, sizeZ: u32, cellSize: f32, holes: Uint8Array | null, sx: f32, sy: f32, sz: f32): u32
|
|
797
|
+
/** @ifdef CREATOR_GL_PHYSICS */
|
|
765
798
|
physicsUpdateHeightField?(shapeId: u32, heights: Float32Array, sizeX: u32, sizeZ: u32, x0: u32, z0: u32, w: u32, h: u32, holes: Uint8Array | null): void
|
|
766
799
|
/** A loaded GLB root's triangle soup — the one `physicsBuildMeshFromEntity` collides — as 9 floats per
|
|
767
800
|
* triangle in the asset root's space (empty when the entity is not a GLB). `Terrain.conform` stamps a
|
|
@@ -769,56 +802,63 @@ declare global {
|
|
|
769
802
|
glbTriangles?(entityId: u32): Float32Array
|
|
770
803
|
/** Offset a built shape's centre from the node's origin (Shape `origin`) — world units in the
|
|
771
804
|
* body's rotated, UNSCALED frame, sitting outside a mesh shape's scale wrapper. Sets rather
|
|
772
|
-
* than accumulates; (0,0,0) clears it. Optional: an older host just centres on the node. */
|
|
805
|
+
* than accumulates; (0,0,0) clears it. Optional: an older host just centres on the node. @ifdef CREATOR_GL_PHYSICS */
|
|
773
806
|
physicsSetShapeOrigin?(shapeId: u32, x: f32, y: f32, z: f32): void
|
|
774
807
|
/** Swap a live body's shape, keeping its id, velocity and transform (Shape.fit / a re-attach).
|
|
775
|
-
* updateMass recomputes the inertia tensor. Refuses a triangle mesh on a dynamic body. */
|
|
808
|
+
* updateMass recomputes the inertia tensor. Refuses a triangle mesh on a dynamic body. @ifdef CREATOR_GL_PHYSICS */
|
|
776
809
|
physicsSetBodyShape?(bodyId: u32, shapeId: u32, updateMass: boolean): void
|
|
810
|
+
/** @ifdef CREATOR_GL_PHYSICS */
|
|
777
811
|
physicsDestroyShape(shapeId: u32): void
|
|
778
|
-
/** sensor = trigger (overlap events, no response); pickOnly = raycast-only, non-colliding body. */
|
|
812
|
+
/** sensor = trigger (overlap events, no response); pickOnly = raycast-only, non-colliding body. @ifdef CREATOR_GL_PHYSICS */
|
|
779
813
|
physicsCreateBody(entityId: u32, shapeId: u32, motion: i32, mass: f32, sensor: boolean, pickOnly: boolean): u32
|
|
814
|
+
/** @ifdef CREATOR_GL_PHYSICS */
|
|
780
815
|
physicsSetPickable(bodyId: u32, pickable: boolean): void
|
|
781
816
|
/** Surface friction of one body (0 = ice, ~1 = grippy asphalt). Values COMBINE as sqrt(a * b), so
|
|
782
817
|
* a low value on either side dominates. New bodies start at 0.6 — a neutral solid surface.
|
|
783
|
-
* The vehicle wheel cast reads the GROUND body's value — this is what caps a car's cornering. */
|
|
818
|
+
* The vehicle wheel cast reads the GROUND body's value — this is what caps a car's cornering. @ifdef CREATOR_GL_PHYSICS */
|
|
784
819
|
physicsSetFriction(bodyId: u32, friction: f32): void
|
|
820
|
+
/** @ifdef CREATOR_GL_PHYSICS */
|
|
785
821
|
physicsGetFriction(bodyId: u32): f64
|
|
786
|
-
/** Ray vs pickable bodies → hit entity id (0 = miss); fills `out` = [px,py,pz,nx,ny,nz,fraction]. @into out 7 */
|
|
822
|
+
/** Ray vs pickable bodies → hit entity id (0 = miss); fills `out` = [px,py,pz,nx,ny,nz,fraction]. @into out 7 @ifdef CREATOR_GL_PHYSICS */
|
|
787
823
|
physicsRaycast(ox: f32, oy: f32, oz: f32, dx: f32, dy: f32, dz: f32, maxDist: f32, out?: Float32Array): u32
|
|
788
824
|
/** Dev-time dump of the static collision geometry (docs/navmesh-plan.md §4) — the input of
|
|
789
825
|
* `lecodes navmesh bake`: every STATIC solid body's triangles in world space, as an NGEO file at
|
|
790
826
|
* `outPath`. `entityIds`/`areas` are per-entity overrides (area 0..15, 255 unwalkable, 254 skip).
|
|
791
|
-
* Returns the triangle count (-1 = failed). Desktop hosts with physics only. */
|
|
827
|
+
* Returns the triangle count (-1 = failed). Desktop hosts with physics only. @ifdef CREATOR_GL_PHYSICS */
|
|
792
828
|
physicsStaticGeometry?(outPath: string, entityIds: Uint32Array, areas: Uint8Array): i32
|
|
793
829
|
// CharacterController (Jolt CharacterVirtual). The character steps on the engine's FIXED clock like
|
|
794
830
|
// every body (frame-rate independent) and is render-interpolated; the engine owns its gravity, so
|
|
795
831
|
// the SDK never ticks it. Both velocity halves are LATCHED STATE, never one-shot events — JS runs
|
|
796
832
|
// once per frame while the sim runs 0..4 sub-steps, so a one-shot would double-apply or vanish.
|
|
797
833
|
// groundState: 0 OnGround / 1 OnSteepGround / 2 NotSupported / 3 InAir.
|
|
834
|
+
/** @ifdef CREATOR_GL_PHYSICS */
|
|
798
835
|
characterCreate(entityId: u32, shapeId: u32, maxSlopeDeg: f32): u32
|
|
836
|
+
/** @ifdef CREATOR_GL_PHYSICS */
|
|
799
837
|
characterDestroy(charId: u32): void
|
|
800
838
|
/** This frame's HORIZONTAL command (world units/s), cleared once a step consumes it — no command
|
|
801
839
|
* means standing still, not coasting. Held across the frame's sub-steps, and it takes the axis
|
|
802
|
-
* back from a latched velocity: the last writer owns X/Z. */
|
|
840
|
+
* back from a latched velocity: the last writer owns X/Z. @ifdef CREATOR_GL_PHYSICS */
|
|
803
841
|
characterMove(charId: u32, x: f32, z: f32): void
|
|
804
|
-
/** The same command including the vertical — free mode (gravityScale 0): swimming / flying. */
|
|
842
|
+
/** The same command including the vertical — free mode (gravityScale 0): swimming / flying. @ifdef CREATOR_GL_PHYSICS */
|
|
805
843
|
characterMoveFree(charId: u32, x: f32, y: f32, z: f32): void
|
|
806
|
-
/** Seed the LATCHED ballistic vertical (jump / dash). No ground check. */
|
|
844
|
+
/** Seed the LATCHED ballistic vertical (jump / dash). No ground check. @ifdef CREATOR_GL_PHYSICS */
|
|
807
845
|
characterSetVerticalVelocity(charId: u32, vy: f32): void
|
|
808
846
|
/** Latch the whole velocity — it persists until a characterMove takes the axis back (knockback,
|
|
809
|
-
* wall jump, launch pad, weightless flight). Gravity still acts on the vertical. */
|
|
847
|
+
* wall jump, launch pad, weightless flight). Gravity still acts on the vertical. @ifdef CREATOR_GL_PHYSICS */
|
|
810
848
|
characterSetVelocity(charId: u32, x: f32, y: f32, z: f32): void
|
|
811
|
-
/** Multiplier over the world gravity; 0 = free mode, which ALSO disables stick-to-floor + stairs. */
|
|
849
|
+
/** Multiplier over the world gravity; 0 = free mode, which ALSO disables stick-to-floor + stairs. @ifdef CREATOR_GL_PHYSICS */
|
|
812
850
|
characterSetGravityScale(charId: u32, scale: f32): void
|
|
851
|
+
/** @ifdef CREATOR_GL_PHYSICS */
|
|
813
852
|
characterSetMaxSlope(charId: u32, maxSlopeDeg: f32): void
|
|
814
853
|
/** Swap the collider live (crouch / stand up), keeping the FEET planted. Returns false when the new
|
|
815
854
|
* shape doesn't fit where the character stands — nothing changed, so the caller retries later and
|
|
816
|
-
* that retry is an exact headroom test. Optional: a host without it can't resize a character. */
|
|
855
|
+
* that retry is an exact headroom test. Optional: a host without it can't resize a character. @ifdef CREATOR_GL_PHYSICS */
|
|
817
856
|
characterSetShape?(charId: u32, shapeId: u32): boolean
|
|
818
|
-
/** The velocity the solver ENDED UP with after the last step (post-collision), not the command. @into out 3 */
|
|
857
|
+
/** The velocity the solver ENDED UP with after the last step (post-collision), not the command. @into out 3 @ifdef CREATOR_GL_PHYSICS */
|
|
819
858
|
characterGetVelocity(charId: u32, out: Float32Array): void
|
|
859
|
+
/** @ifdef CREATOR_GL_PHYSICS */
|
|
820
860
|
characterGetGroundState(charId: u32): i32
|
|
821
|
-
/** Discontinuous move (spawn / respawn / teleport) — also resets the interpolation pair. */
|
|
861
|
+
/** Discontinuous move (spawn / respawn / teleport) — also resets the interpolation pair. @ifdef CREATOR_GL_PHYSICS */
|
|
822
862
|
characterSetPosition(charId: u32, x: f32, y: f32, z: f32): void
|
|
823
863
|
// Vehicle (Jolt VehicleConstraint + WheeledVehicleController). One settings BLOB, so tuning knobs
|
|
824
864
|
// never grow this ABI — layout (floats):
|
|
@@ -873,7 +913,9 @@ declare global {
|
|
|
873
913
|
// wheel models itself from the state. Chassis space is forward -Z / up +Y (matching node.forward);
|
|
874
914
|
// wheel positions are suspension attachment points in unscaled chassis space; `axle` pairs wheels
|
|
875
915
|
// for the differentials + anti-roll bars.
|
|
916
|
+
/** @ifdef CREATOR_GL_PHYSICS */
|
|
876
917
|
vehicleCreate(entityId: u32, shapeId: u32, settings: Float32Array): u32
|
|
918
|
+
/** @ifdef CREATOR_GL_PHYSICS */
|
|
877
919
|
vehicleDestroy(vehicleId: u32): void
|
|
878
920
|
/** ONE input vector, latched and applied once per fixed step: [throttle 0..1, steer −1..1 (a
|
|
879
921
|
* fraction of the wheels' maxSteerDeg, applied as is; with a column, where the hands aim),
|
|
@@ -882,12 +924,12 @@ declare global {
|
|
|
882
924
|
* neutral, negative = reverse — the engine never sees a gear list), clutch 0..1 (scales the clutch
|
|
883
925
|
* in the coupled engine/wheel solve), brake_0 … brake_n (N·m of brake torque per wheel, this
|
|
884
926
|
* step)]. A shorter vector leaves the rest as it was. Sticky; a non-zero throttle, steer or brake
|
|
885
|
-
* wakes a sleeping car. */
|
|
927
|
+
* wakes a sleeping car. @ifdef CREATOR_GL_PHYSICS */
|
|
886
928
|
vehicleSetInput(vehicleId: u32, input: Float32Array): void
|
|
887
929
|
/** Re-apply the TUNABLE half of the blob (same layout) to a live car — differential, per-wheel tire
|
|
888
930
|
* curves / traction / circle, engine torque/RPM/curve, clutch strength + capacity, steer lock, the
|
|
889
931
|
* column, anti-roll stiffness, tilt limit. Structural values (mass, centre of mass, wheel geometry,
|
|
890
|
-
* the driven shares, suspension, whether an axle has a bar) are ignored: those need a re-create. */
|
|
932
|
+
* the driven shares, suspension, whether an axle has a bar) are ignored: those need a re-create. @ifdef CREATOR_GL_PHYSICS */
|
|
891
933
|
vehicleSetTuning?(vehicleId: u32, settings: Float32Array): void
|
|
892
934
|
/** out = [speed, rpm, wheelsInContact, vx, vy, vz, wx, wy, wz, steerDeg, steerTorque] (w = chassis
|
|
893
935
|
* angular velocity, rad/s; steerDeg = where the road wheels are, right-positive; steerTorque = the
|
|
@@ -898,12 +940,12 @@ declare global {
|
|
|
898
940
|
* patch's sliding speeds, m/s, + = to the right / tread spinning up — force × speed is heat; load =
|
|
899
941
|
* the suspension's force on the wheel, N). slipLong is SIGNED (+ spinning up, − locking) and so is
|
|
900
942
|
* slipAngleDeg (+ the patch sliding to the tire's right). suspensionLength, steerDeg and spin are
|
|
901
|
-
* what a wheel model's pose is built from — by the game, the SDK only exposes them. */
|
|
943
|
+
* what a wheel model's pose is built from — by the game, the SDK only exposes them. @ifdef CREATOR_GL_PHYSICS */
|
|
902
944
|
vehicleGetState(vehicleId: u32, out: Float32Array): void
|
|
903
945
|
/** Teleport upright and clear all motion (velocities, engine RPM, wheel spin); the input vector is
|
|
904
|
-
* zeroed — the SDK re-sends its own on the next early pass. */
|
|
946
|
+
* zeroed — the SDK re-sends its own on the next early pass. @ifdef CREATOR_GL_PHYSICS */
|
|
905
947
|
vehicleReset(vehicleId: u32, x: f32, y: f32, z: f32, qx: f32, qy: f32, qz: f32, qw: f32): void
|
|
906
|
-
/** The chassis rigid body, for the plain body calls (physicsApplyImpulse, …). 0 if unknown. */
|
|
948
|
+
/** The chassis rigid body, for the plain body calls (physicsApplyImpulse, …). 0 if unknown. @ifdef CREATOR_GL_PHYSICS */
|
|
907
949
|
vehicleBodyId(vehicleId: u32): u32
|
|
908
950
|
// Ragdoll (Jolt Ragdoll): a Model's skeleton handed to physics — one dynamic body per listed bone
|
|
909
951
|
// (a capsule from the bone's origin to the next joint) joined to its parent part by a swing-twist
|
|
@@ -949,24 +991,26 @@ declare global {
|
|
|
949
991
|
// half-angle), twistDeg (half-angle), hinge (0/1), hingeAxisX/Y/Z (in the MODEL
|
|
950
992
|
// node's space), hingeMinDeg, hingeMaxDeg — a hinge is a swing-twist whose cone is
|
|
951
993
|
// flat (2°) across the axis and [min, max] about it (a knee, an elbow)
|
|
994
|
+
/** @ifdef CREATOR_GL_PHYSICS */
|
|
952
995
|
ragdollCreate?(rootEntityId: u32, bones: Uint32Array, settings: Float32Array): u32
|
|
996
|
+
/** @ifdef CREATOR_GL_PHYSICS */
|
|
953
997
|
ragdollDestroy?(ragdollId: u32): void
|
|
954
998
|
/** Pose the bodies from the bones' current transforms and add them to the world, each moving as
|
|
955
999
|
* its bone was measured over the last two frames, plus the launch (vx, vy, vz) on every body.
|
|
956
|
-
* Already active = re-wake + the launch. false = unknown id / no world. */
|
|
1000
|
+
* Already active = re-wake + the launch. false = unknown id / no world. @ifdef CREATOR_GL_PHYSICS */
|
|
957
1001
|
ragdollActivate?(ragdollId: u32, vx: f32, vy: f32, vz: f32): boolean
|
|
958
1002
|
/** Take the bodies out of the world. The bones get the bodies' last pose written against the
|
|
959
1003
|
* parents' CURRENT worlds (move the model node under the hips first), and with `blend` > 0 the
|
|
960
1004
|
* model's animator is seeded with it: whatever plays next transitions from the fallen pose over
|
|
961
|
-
* `blend` seconds. */
|
|
1005
|
+
* `blend` seconds. @ifdef CREATOR_GL_PHYSICS */
|
|
962
1006
|
ragdollDeactivate?(ragdollId: u32, blend: f32): void
|
|
963
|
-
/** In the world AND at least one body still awake (a settled ragdoll reads false). */
|
|
1007
|
+
/** In the world AND at least one body still awake (a settled ragdoll reads false). @ifdef CREATOR_GL_PHYSICS */
|
|
964
1008
|
ragdollActive?(ragdollId: u32): boolean
|
|
965
|
-
/** The rigid body of part `index` (for physicsApplyImpulseAt / velocities). 0 if unknown. */
|
|
1009
|
+
/** The rigid body of part `index` (for physicsApplyImpulseAt / velocities). 0 if unknown. @ifdef CREATOR_GL_PHYSICS */
|
|
966
1010
|
ragdollBodyId?(ragdollId: u32, index: u32): u32
|
|
967
1011
|
/** The joint motors' strength (0..1 = the share of driveTorque; 0 = off = limp) and the root
|
|
968
1012
|
* anchor (the hips body kinematic on the animated hips). Kept across activations; a driven or
|
|
969
|
-
* anchored body never freezes. */
|
|
1013
|
+
* anchored body never freezes. @ifdef CREATOR_GL_PHYSICS */
|
|
970
1014
|
ragdollSetDrive?(ragdollId: u32, strength: f32, anchor: boolean): void
|
|
971
1015
|
// Dynamic bones (the SDK's DynamicBone; creator-anim canimDyn* behind creator-gl): secondary motion
|
|
972
1016
|
// for tails, ears, hair and cloaks — a bone tree under one root simulated as Verlet particle chains
|
|
@@ -1027,23 +1071,31 @@ declare global {
|
|
|
1027
1071
|
dynamicBoneColliderSet?(colliderId: u32, ax: f32, ay: f32, az: f32, bx: f32, by: f32, bz: f32, radius: f32, sided: f32): void
|
|
1028
1072
|
dynamicBoneColliderDestroy?(colliderId: u32): void
|
|
1029
1073
|
// Legacy coupled shape+body (still used by the worker RigidBody).
|
|
1074
|
+
/** @ifdef CREATOR_GL_PHYSICS */
|
|
1030
1075
|
physicsCreateBox(entityId: u32, hx: f32, hy: f32, hz: f32, motionType: i32, mass: f32): u32
|
|
1076
|
+
/** @ifdef CREATOR_GL_PHYSICS */
|
|
1031
1077
|
physicsCreateSphere(entityId: u32, radius: f32, motionType: i32, mass: f32): u32
|
|
1078
|
+
/** @ifdef CREATOR_GL_PHYSICS */
|
|
1032
1079
|
physicsCreateCylinder(entityId: u32, halfHeight: f32, radius: f32, motionType: i32, mass: f32): u32
|
|
1080
|
+
/** @ifdef CREATOR_GL_PHYSICS */
|
|
1033
1081
|
physicsSetLinearVelocity(bodyId: u32, x: f32, y: f32, z: f32): void
|
|
1034
|
-
/** @into out 3 */
|
|
1082
|
+
/** @into out 3 @ifdef CREATOR_GL_PHYSICS */
|
|
1035
1083
|
physicsGetLinearVelocity(bodyId: u32, out: Float32Array): void
|
|
1036
1084
|
/** Angular velocity about each world axis, RADIANS/second — Jolt's unit; the SDK exposes degrees.
|
|
1037
|
-
* The only way to stop a spin: a position write leaves both velocities untouched. */
|
|
1085
|
+
* The only way to stop a spin: a position write leaves both velocities untouched. @ifdef CREATOR_GL_PHYSICS */
|
|
1038
1086
|
physicsSetAngularVelocity?(bodyId: u32, x: f32, y: f32, z: f32): void
|
|
1039
|
-
/** @into out 3 */
|
|
1087
|
+
/** @into out 3 @ifdef CREATOR_GL_PHYSICS */
|
|
1040
1088
|
physicsGetAngularVelocity?(bodyId: u32, out: Float32Array): void
|
|
1089
|
+
/** @ifdef CREATOR_GL_PHYSICS */
|
|
1041
1090
|
physicsApplyImpulse(bodyId: u32, x: f32, y: f32, z: f32): void
|
|
1091
|
+
/** @ifdef CREATOR_GL_PHYSICS */
|
|
1042
1092
|
physicsApplyImpulseAt?(bodyId: u32, x: f32, y: f32, z: f32, px: f32, py: f32, pz: f32): void
|
|
1093
|
+
/** @ifdef CREATOR_GL_PHYSICS */
|
|
1043
1094
|
physicsSetBodyPosition(bodyId: u32, x: f32, y: f32, z: f32): void
|
|
1044
1095
|
/** The rotation twin (normalized host-side). Both snap the body's render-interpolation pair, so a
|
|
1045
|
-
* discontinuous move is drawn as one rather than as a one-frame slide/spin across the gap. */
|
|
1096
|
+
* discontinuous move is drawn as one rather than as a one-frame slide/spin across the gap. @ifdef CREATOR_GL_PHYSICS */
|
|
1046
1097
|
physicsSetBodyRotation?(bodyId: u32, qx: f32, qy: f32, qz: f32, qw: f32): void
|
|
1098
|
+
/** @ifdef CREATOR_GL_PHYSICS */
|
|
1047
1099
|
physicsRemoveBody(bodyId: u32): void
|
|
1048
1100
|
|
|
1049
1101
|
/** @custom */
|
package/src/gl/Scene.ts
CHANGED
|
@@ -19,22 +19,12 @@ import { Camera } from "./Camera"
|
|
|
19
19
|
import { SceneAudio } from "./audio/SceneAudio"
|
|
20
20
|
import { attachControls, type ControlsHandle, type ControlsOptions } from "./controls"
|
|
21
21
|
import { Material } from "./Material"
|
|
22
|
+
import { PostProcessing, type PostProcessingOptions } from "./postProcessing"
|
|
22
23
|
import { Texture } from "./Texture"
|
|
23
24
|
import { Node, nodes } from "./Node"
|
|
24
25
|
import { glState } from "./state"
|
|
25
26
|
import { registerTouchEndEvent, registerTouchStartEvent } from "./touch"
|
|
26
27
|
|
|
27
|
-
export type AmbientOcclusionOptions = {
|
|
28
|
-
/** Strength of the darkening (default 1). */
|
|
29
|
-
intensity?: number
|
|
30
|
-
/** How far the occlusion reaches, in metres (default 0.3). */
|
|
31
|
-
radius?: number
|
|
32
|
-
/** Falloff contrast; >1 tightens it into the crease (default 1). */
|
|
33
|
-
power?: number
|
|
34
|
-
/** Sample count + filtering (default 'medium'). Not the buffer resolution — that stays half. */
|
|
35
|
-
quality?: "low" | "medium" | "high" | "ultra"
|
|
36
|
-
}
|
|
37
|
-
|
|
38
28
|
/** `SceneOptions.taa` / `scene.setAntialias("taa", 4, taa)`: the temporal anti-aliasing knobs. */
|
|
39
29
|
export type TaaOptions = {
|
|
40
30
|
/** Render the 3D at this fraction (0.5–1) and TAA-upscale it to the viewport; 1 = none. */
|
|
@@ -92,14 +82,10 @@ export type SceneOptions = {
|
|
|
92
82
|
* rather than inflating the lights: scaling lamps past what they physically emit gives bright
|
|
93
83
|
* fixtures in a black room, because it changes the RATIO, not the level. */
|
|
94
84
|
exposureCompensation?: number
|
|
95
|
-
/**
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
* towards white — HDR fire reads pale; `'neutral'` (Khronos PBR Neutral) keeps hue and
|
|
100
|
-
* saturation until very bright; `'linear'` clips each channel (what an engine without a
|
|
101
|
-
* tonemapper shows — saturated, Unity-without-post-processing look); `'filmic'` (Uncharted). */
|
|
102
|
-
toneMapping?: "aces" | "neutral" | "linear" | "filmic"
|
|
85
|
+
/** The look: tone mapping, colour grading, a `.cube` LUT, bloom, vignette, ambient occlusion,
|
|
86
|
+
* screen-space reflections, depth of field — as one object (`scene.postProcessing` after the
|
|
87
|
+
* fact; every field is live, postProcessing.ts says which are free to animate). */
|
|
88
|
+
postProcessing?: PostProcessingOptions
|
|
103
89
|
/** The sky. Three forms:
|
|
104
90
|
* - a colour — a flat clear colour;
|
|
105
91
|
* - `{ texture }` — a KTX1 **cubemap**, the sharp `<name>_skybox.ktx` that filament's `cmgen`
|
|
@@ -112,12 +98,6 @@ export type SceneOptions = {
|
|
|
112
98
|
* space after the opaque queue. No geometry, no meridian seam, no pole distortion — do NOT build
|
|
113
99
|
* a sky dome or a fullscreen equirect material by hand, both are strictly worse. */
|
|
114
100
|
skybox?: ColorInput | "environment" | { texture: string }
|
|
115
|
-
/** Screen-space ambient occlusion — the contact darkening in creases and where props meet the
|
|
116
|
-
* ground. Without it an IBL lights a crease exactly as brightly as an open face, so everything
|
|
117
|
-
* reads as pasted onto the floor rather than standing on it. `true` takes defaults tuned for
|
|
118
|
-
* human-scale props; `radius` is world-space metres and is the one knob that must follow the
|
|
119
|
-
* scene's scale (~0.3 for objects on a table, ~0.6-1 for a yard of crates and containers). */
|
|
120
|
-
ambientOcclusion?: boolean | AmbientOcclusionOptions
|
|
121
101
|
/** Distance fog / aerial perspective: distant geometry loses contrast so the eye reads depth, and
|
|
122
102
|
* the hard edge where a finite level ends against the skybox goes away. By default the fog applies
|
|
123
103
|
* at every distance — the skybox included — so the sky itself takes the fog colour and the horizon
|
|
@@ -215,6 +195,14 @@ export class Scene implements Presentable {
|
|
|
215
195
|
readonly _touchStartListeners: Array<(ev: TouchStartEvent<Node | null>) => void> = []
|
|
216
196
|
|
|
217
197
|
private _material?: Material
|
|
198
|
+
private readonly _postProcessing: PostProcessing
|
|
199
|
+
/** The scene's post-processing (postProcessing.ts): a live object — write a field
|
|
200
|
+
* (`scene.postProcessing.contrast = 1.1`), or `apply({ ... })` several at once (named fields
|
|
201
|
+
* only), `reset()` for the defaults. Read-only: there is nothing to assign. An overlay's grading
|
|
202
|
+
* is its parent's; its bloom, vignette and the rest are its own. */
|
|
203
|
+
get postProcessing(): PostProcessing { return this._postProcessing }
|
|
204
|
+
/** An explicit throw, not a bare getter: a bundle evaluated in sloppy mode would swallow the assignment silently. */
|
|
205
|
+
set postProcessing(_: never) { throw new Error("scene.postProcessing is read-only: write a field, apply({ ... }) or reset()") }
|
|
218
206
|
|
|
219
207
|
private static _active: Scene | null = null
|
|
220
208
|
static get active(): Scene | null { return Scene._active }
|
|
@@ -245,12 +233,9 @@ export class Scene implements Presentable {
|
|
|
245
233
|
}
|
|
246
234
|
this._environmentIntensity = options.ibl === false ? 0 : (options.environmentIntensity ?? 20000)
|
|
247
235
|
if (options.exposureCompensation !== undefined) this.camera.exposureCompensation = options.exposureCompensation
|
|
248
|
-
|
|
249
|
-
if (options.
|
|
250
|
-
_creator.setToneMapping?.(this._id, { neutral: 1, linear: 2, filmic: 3 }[options.toneMapping] ?? 0)
|
|
251
|
-
}
|
|
236
|
+
this._postProcessing = new PostProcessing(this._id)
|
|
237
|
+
if (options.postProcessing) this._postProcessing.apply(options.postProcessing)
|
|
252
238
|
if (options.skybox !== undefined) this.skybox = options.skybox
|
|
253
|
-
if (options.ambientOcclusion) this.setAmbientOcclusion(options.ambientOcclusion)
|
|
254
239
|
if (options.fog) this.setFog(options.fog)
|
|
255
240
|
if (options.antialias !== undefined) {
|
|
256
241
|
const a = options.antialias
|
|
@@ -302,12 +287,6 @@ export class Scene implements Presentable {
|
|
|
302
287
|
_creator.setSkybox(this._id, Color.toPackedRgb(sky))
|
|
303
288
|
}
|
|
304
289
|
|
|
305
|
-
/** Runtime form of `ambientOcclusion` (a graphics-settings menu). `false` turns it off. */
|
|
306
|
-
setAmbientOcclusion(options: boolean | AmbientOcclusionOptions): void {
|
|
307
|
-
const o: AmbientOcclusionOptions = typeof options === "object" ? options : {}
|
|
308
|
-
const quality = { low: 0, medium: 1, high: 2, ultra: 3 }[o.quality ?? "medium"] ?? 1
|
|
309
|
-
_creator.setAmbientOcclusionOptions?.(this._id, options !== false, o.intensity ?? 1, o.radius ?? 0.3, o.power ?? 1, quality)
|
|
310
|
-
}
|
|
311
290
|
|
|
312
291
|
/** Runtime form of `fog` (weather, entering a building). `false` turns it off. */
|
|
313
292
|
setFog(options: FogOptions | false): void {
|
|
@@ -349,10 +328,6 @@ export class Scene implements Presentable {
|
|
|
349
328
|
setLodBias(bias: number): void {
|
|
350
329
|
_creator.setLodBias?.(bias)
|
|
351
330
|
}
|
|
352
|
-
/** Runtime form of `bloom` / `bloomIntensity` (a graphics-settings menu). */
|
|
353
|
-
setBloom(enabled: boolean, intensity = 0.2): void {
|
|
354
|
-
_creator.setBloomOptions(this._id, enabled, intensity, 1)
|
|
355
|
-
}
|
|
356
331
|
/**
|
|
357
332
|
* How bright the environment (IBL) lights the scene, in lux — `SceneOptions.environmentIntensity`
|
|
358
333
|
* after the fact. Live: it changes the probe's intensity, not the probe, so it costs nothing and
|