lecodes-cli 0.19.0 → 0.20.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 +1 -0
- package/dist/index.js +1069 -396
- package/package.json +14 -13
- package/runtime/scene-harness.json +1 -1
- package/runtime/sdk-types.json +1 -1
- package/runtime/web/assets/Roboto-Variable-DHm9jpd-.woff2 +0 -0
- package/runtime/web/assets/__vite-browser-external-BIHI7g3E.js +1 -0
- package/runtime/web/assets/basis-C64VHDVD.js +1 -0
- package/runtime/web/assets/basis_transcoder-VXdx5NbI.wasm +0 -0
- package/runtime/web/assets/createViewerLite-BvHobVhp.js +852 -0
- package/runtime/web/assets/draco-BiISTFcR.js +118 -0
- package/runtime/web/assets/draco_decoder-DsQ12WqX.wasm +0 -0
- package/runtime/web/assets/index-CfNCCBjN.js +2 -0
- package/runtime/web/assets/mapViewImpl-B2JcES8l.js +810 -0
- package/runtime/web/assets/maplibre-gl-worker-CJfwIrte.js +8 -0
- package/runtime/web/assets/neutral_ibl128-D-lzdkLN.ktx +0 -0
- package/runtime/web/assets/worker-Caf-yYEI.js +2 -0
- package/runtime/web/embed.html +32 -0
- package/runtime/web/embed.js +171 -0
- package/src/api.ts +302 -0
- package/src/browserAuth.ts +87 -0
- package/src/cmgenTool.ts +104 -0
- package/src/commands/app.ts +896 -0
- package/src/commands/appAndroid.ts +603 -0
- package/src/commands/appDesktop.ts +339 -0
- package/src/commands/appDesktopMac.ts +470 -0
- package/src/commands/appIcon.ts +187 -0
- package/src/commands/appShared.ts +542 -0
- package/src/commands/appTemplates.ts +907 -0
- package/src/commands/appTemplatesAndroid.ts +599 -0
- package/src/commands/appTemplatesGradlew.ts +9 -0
- package/src/commands/assets.ts +28 -0
- package/src/commands/clone.ts +60 -0
- package/src/commands/compile.ts +253 -0
- package/src/commands/create.ts +59 -0
- package/src/commands/design.ts +863 -0
- package/src/commands/designTemplates.ts +11 -0
- package/src/commands/desktop.ts +127 -0
- package/src/commands/dev.ts +236 -0
- package/src/commands/diff.ts +59 -0
- package/src/commands/init.ts +191 -0
- package/src/commands/install.ts +198 -0
- package/src/commands/lightmap.ts +290 -0
- package/src/commands/link.ts +147 -0
- package/src/commands/login.ts +24 -0
- package/src/commands/navmesh.ts +225 -0
- package/src/commands/pn.ts +305 -0
- package/src/commands/projectTemplates.ts +130 -0
- package/src/commands/pull.ts +109 -0
- package/src/commands/push.ts +173 -0
- package/src/commands/render.ts +416 -0
- package/src/commands/scene.ts +148 -0
- package/src/commands/shaders.ts +157 -0
- package/src/commands/status.ts +29 -0
- package/src/commands/test.ts +371 -0
- package/src/commands/thumbs.ts +178 -0
- package/src/commands/types.ts +87 -0
- package/src/commands/update.ts +190 -0
- package/src/compile/assetIcons.ts +214 -0
- package/src/compile/collect.ts +46 -0
- package/src/compile/collectLocal.ts +37 -0
- package/src/compile/designCompile.ts +104 -0
- package/src/compile/fonts.ts +156 -0
- package/src/compile/headlessBundle.ts +129 -0
- package/src/compile/nativeStack.ts +21 -0
- package/src/compile/projectCompile.ts +109 -0
- package/src/compile/sceneCompile.ts +113 -0
- package/src/compile/screenEntry.ts +127 -0
- package/src/compile/shaders.ts +245 -0
- package/src/config.ts +42 -0
- package/src/designMeta.ts +35 -0
- package/src/desktopRenderer.ts +545 -0
- package/src/desktopScript.ts +276 -0
- package/src/dev/clientTemplates.ts +160 -0
- package/src/dev/devServer.ts +333 -0
- package/src/dev/webRunner.ts +22 -0
- package/src/dev/wsServer.ts +144 -0
- package/src/distRoot.ts +20 -0
- package/src/ignore.ts +163 -0
- package/src/index.ts +494 -0
- package/src/lecodes-3d-editor.d.ts +41 -0
- package/src/lecodes-assets.d.ts +7 -0
- package/src/lecodes-design.d.ts +191 -0
- package/src/lecodes-renderer.d.ts +131 -0
- package/src/localFiles.ts +144 -0
- package/src/manifest.ts +46 -0
- package/src/matcTool.ts +137 -0
- package/src/peers.ts +40 -0
- package/src/project.ts +61 -0
- package/src/projectEnv.ts +94 -0
- package/src/qrcode-terminal.d.ts +9 -0
- package/src/releases.ts +125 -0
- package/src/serverDiff.ts +78 -0
- package/src/textDiff.ts +103 -0
- package/src/types.ts +0 -0
- package/src/util.ts +146 -0
- package/runtime/sdk/animate/animate.ts +0 -238
- package/runtime/sdk/animate/bezier.ts +0 -138
- package/runtime/sdk/animate/easings.ts +0 -126
- package/runtime/sdk/canvas/Canvas.ts +0 -305
- package/runtime/sdk/compile/aspectMacro.ts +0 -86
- package/runtime/sdk/compile/assetIconMacro.ts +0 -384
- package/runtime/sdk/compile/assetMacro.ts +0 -146
- package/runtime/sdk/compile/assetName.ts +0 -50
- package/runtime/sdk/compile/bundler.ts +0 -287
- package/runtime/sdk/compile/compileProject.ts +0 -129
- package/runtime/sdk/compile/detectEntry.ts +0 -128
- package/runtime/sdk/compile/fontMacro.ts +0 -459
- package/runtime/sdk/compile/fontRegistry.ts +0 -78
- package/runtime/sdk/compile/header.ts +0 -67
- package/runtime/sdk/compile/index.ts +0 -100
- package/runtime/sdk/compile/libraryImports.ts +0 -52
- package/runtime/sdk/compile/liteMaterial.ts +0 -247
- package/runtime/sdk/compile/sceneEditor.ts +0 -88
- package/runtime/sdk/compile/serverSplit.ts +0 -233
- package/runtime/sdk/compile/serverTypes.ts +0 -227
- package/runtime/sdk/compile/sfnt.ts +0 -98
- package/runtime/sdk/compile/shaderSchema.ts +0 -202
- package/runtime/sdk/compile/shaderTargets.ts +0 -81
- package/runtime/sdk/compile/sourcemap.ts +0 -25
- package/runtime/sdk/core/Aspect.ts +0 -568
- package/runtime/sdk/core/InspectorUI.ts +0 -212
- package/runtime/sdk/core/StateMachine.ts +0 -308
- package/runtime/sdk/core/color.ts +0 -66
- package/runtime/sdk/core/compWrite.ts +0 -42
- package/runtime/sdk/core/events.ts +0 -38
- package/runtime/sdk/core/fields.ts +0 -120
- package/runtime/sdk/core/registry.ts +0 -23
- package/runtime/sdk/core/signals.ts +0 -277
- package/runtime/sdk/core/time.ts +0 -84
- package/runtime/sdk/g2/Camera2D.ts +0 -40
- package/runtime/sdk/g2/CharacterController2D.ts +0 -276
- package/runtime/sdk/g2/Node2D.ts +0 -267
- package/runtime/sdk/g2/OneWay2D.ts +0 -66
- package/runtime/sdk/g2/Physics2D.ts +0 -346
- package/runtime/sdk/g2/Scene2D.ts +0 -209
- package/runtime/sdk/g2/Shape2D.ts +0 -259
- package/runtime/sdk/g2/Sprite.ts +0 -89
- package/runtime/sdk/g2/SpriteAnimation.ts +0 -171
- package/runtime/sdk/g2/SpriteSheet.ts +0 -166
- package/runtime/sdk/g2/Texture2D.ts +0 -47
- package/runtime/sdk/g2/Tilemap.ts +0 -41
- package/runtime/sdk/g2/Tileset.ts +0 -71
- package/runtime/sdk/g2/Trigger2D.ts +0 -77
- package/runtime/sdk/g2/autotile.ts +0 -433
- package/runtime/sdk/g2/cells.ts +0 -91
- package/runtime/sdk/g2/defineScene2d.ts +0 -381
- package/runtime/sdk/g2/groups2d.ts +0 -106
- package/runtime/sdk/g2/loop.ts +0 -50
- package/runtime/sdk/g2/scenarios2d.ts +0 -69
- package/runtime/sdk/g2/touch.ts +0 -83
- package/runtime/sdk/gl/Camera.ts +0 -160
- package/runtime/sdk/gl/CameraPlace.ts +0 -52
- package/runtime/sdk/gl/CharacterController.ts +0 -238
- package/runtime/sdk/gl/Gearbox.ts +0 -212
- package/runtime/sdk/gl/Geometry.ts +0 -279
- package/runtime/sdk/gl/IK.ts +0 -193
- package/runtime/sdk/gl/InstancedMesh.ts +0 -132
- package/runtime/sdk/gl/Light.ts +0 -107
- package/runtime/sdk/gl/Lightmap.ts +0 -256
- package/runtime/sdk/gl/Material.ts +0 -260
- package/runtime/sdk/gl/Mesh.ts +0 -83
- package/runtime/sdk/gl/Model.ts +0 -124
- package/runtime/sdk/gl/NavAgent.ts +0 -337
- package/runtime/sdk/gl/NavMesh.ts +0 -397
- package/runtime/sdk/gl/Node.ts +0 -350
- package/runtime/sdk/gl/Noise.ts +0 -30
- package/runtime/sdk/gl/Particles.ts +0 -676
- package/runtime/sdk/gl/Physics.ts +0 -222
- package/runtime/sdk/gl/Plane.ts +0 -53
- package/runtime/sdk/gl/Ragdoll.ts +0 -270
- package/runtime/sdk/gl/Ray.ts +0 -16
- package/runtime/sdk/gl/Scene.ts +0 -486
- package/runtime/sdk/gl/Shape.ts +0 -409
- package/runtime/sdk/gl/Terrain.ts +0 -1092
- package/runtime/sdk/gl/Texture.ts +0 -63
- package/runtime/sdk/gl/Trigger.ts +0 -45
- package/runtime/sdk/gl/Vehicle.ts +0 -473
- package/runtime/sdk/gl/Wheel.ts +0 -240
- package/runtime/sdk/gl/animation/AnimationClip.ts +0 -204
- package/runtime/sdk/gl/animation/Animator.ts +0 -120
- package/runtime/sdk/gl/animation/Layer.ts +0 -31
- package/runtime/sdk/gl/animation/Loop.ts +0 -30
- package/runtime/sdk/gl/animation/Playback.ts +0 -44
- package/runtime/sdk/gl/animation/core.ts +0 -336
- package/runtime/sdk/gl/controls.ts +0 -95
- package/runtime/sdk/gl/physicsEvents.ts +0 -20
- package/runtime/sdk/gl/scenarios.ts +0 -291
- package/runtime/sdk/gl/state.ts +0 -6
- package/runtime/sdk/gl/terrainMesh.ts +0 -219
- package/runtime/sdk/gl/touch.ts +0 -68
- package/runtime/sdk/inject.ts +0 -197
- package/runtime/sdk/math/Mathf.ts +0 -118
- package/runtime/sdk/math/mat4.ts +0 -278
- package/runtime/sdk/math/quat.ts +0 -232
- package/runtime/sdk/math/vec.ts +0 -255
- package/runtime/sdk/net/codec.ts +0 -119
- package/runtime/sdk/net/core.ts +0 -384
- package/runtime/sdk/net/index.ts +0 -181
- package/runtime/sdk/net/replication.ts +0 -622
- package/runtime/sdk/plugins/camera.ts +0 -81
- package/runtime/sdk/plugins/geolocation.ts +0 -123
- package/runtime/sdk/plugins/oauth.ts +0 -61
- package/runtime/sdk/plugins/permission.ts +0 -7
- package/runtime/sdk/plugins/push.ts +0 -132
- package/runtime/sdk/plugins/qr.ts +0 -73
- package/runtime/sdk/plugins/service.ts +0 -47
- package/runtime/sdk/runtime/app.ts +0 -101
- package/runtime/sdk/runtime/appEvents.ts +0 -54
- package/runtime/sdk/runtime/channel.ts +0 -61
- package/runtime/sdk/runtime/clipboard.ts +0 -20
- package/runtime/sdk/runtime/datetime.ts +0 -329
- package/runtime/sdk/runtime/device.ts +0 -293
- package/runtime/sdk/runtime/fetch.ts +0 -77
- package/runtime/sdk/runtime/files.ts +0 -23
- package/runtime/sdk/runtime/input.ts +0 -175
- package/runtime/sdk/runtime/media.ts +0 -111
- package/runtime/sdk/runtime/misc.ts +0 -16
- package/runtime/sdk/runtime/net.ts +0 -36
- package/runtime/sdk/runtime/rpc.ts +0 -218
- package/runtime/sdk/runtime/service.ts +0 -83
- package/runtime/sdk/runtime/share.ts +0 -9
- package/runtime/sdk/runtime/storage.ts +0 -13
- package/runtime/sdk/runtime/touch.ts +0 -76
- package/runtime/sdk/scene/defineScene.ts +0 -1353
- package/runtime/sdk/scene/editorPlugins.ts +0 -110
- package/runtime/sdk/scene/gizmos.ts +0 -148
- package/runtime/sdk/scene/grammar.ts +0 -120
- package/runtime/sdk/scene/material.ts +0 -188
- package/runtime/sdk/server/auth/appConfig.ts +0 -12
- package/runtime/sdk/server/auth/global.ts +0 -80
- package/runtime/sdk/server/auth/host.ts +0 -318
- package/runtime/sdk/server/auth/models.ts +0 -83
- package/runtime/sdk/server/auth/types.ts +0 -50
- package/runtime/sdk/server/channel.ts +0 -56
- package/runtime/sdk/server/context.ts +0 -36
- package/runtime/sdk/server/db/defineDb.ts +0 -237
- package/runtime/sdk/server/db/fields.ts +0 -132
- package/runtime/sdk/server/db/httpTransport.ts +0 -93
- package/runtime/sdk/server/db/index.ts +0 -7
- package/runtime/sdk/server/db/marci/query.ts +0 -412
- package/runtime/sdk/server/db/types.ts +0 -202
- package/runtime/sdk/server/errors.ts +0 -12
- package/runtime/sdk/server/host.ts +0 -74
- package/runtime/sdk/server/inject.ts +0 -13
- package/runtime/sdk/server/runtime.ts +0 -133
- package/runtime/sdk/server/validate.ts +0 -87
- package/runtime/sdk/ui/NativeView.ts +0 -142
- package/runtime/sdk/ui/UI.ts +0 -39
- package/runtime/sdk/ui/UIBottomSheet.ts +0 -139
- package/runtime/sdk/ui/UIButton.ts +0 -101
- package/runtime/sdk/ui/UIContainer.ts +0 -60
- package/runtime/sdk/ui/UIImage.ts +0 -83
- package/runtime/sdk/ui/UIInput.ts +0 -185
- package/runtime/sdk/ui/UIModal.ts +0 -139
- package/runtime/sdk/ui/UINode.ts +0 -830
- package/runtime/sdk/ui/UIPager.ts +0 -362
- package/runtime/sdk/ui/UIPopover.ts +0 -100
- package/runtime/sdk/ui/UIScreen.ts +0 -123
- package/runtime/sdk/ui/UIScrollable.ts +0 -87
- package/runtime/sdk/ui/UISpacer.ts +0 -14
- package/runtime/sdk/ui/UITabs.ts +0 -236
- package/runtime/sdk/ui/UIText.ts +0 -51
- package/runtime/sdk/ui/UIVideo.ts +0 -88
- package/runtime/sdk/ui/UIVirtualizedList.ts +0 -241
- package/runtime/sdk/ui/UIWidget.ts +0 -127
- package/runtime/sdk/ui/fonts.ts +0 -13
- package/runtime/sdk/ui/presentable.ts +0 -117
- package/runtime/sdk/ui/router.ts +0 -132
- package/runtime/sdk/ui/theme.ts +0 -84
package/runtime/sdk/g2/touch.ts
DELETED
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
// 2D pointer dispatch + track (drag) system. The host (createViewer2d canvas / Creator2DView) delivers
|
|
2
|
-
// raw pointer phases via _creator2d.setOnPointer; this module hit-tests in the active scene and routes
|
|
3
|
-
// to the struck node and the scene's listeners. Mirrors the 3D gl/touch.ts, but the hit-test is done
|
|
4
|
-
// here (screen→world via the camera, then a physics overlap), not by the host.
|
|
5
|
-
//
|
|
6
|
-
// What can be hit: a node with a Physics2D shape (precise) → its 'touchstart'/'click' events; the
|
|
7
|
-
// active Scene2D always gets the event too (target = the hit node or null). A node listener can call
|
|
8
|
-
// ev.track({ onMove, onEnd, onCancel }) to capture the rest of the gesture (drag/pan).
|
|
9
|
-
|
|
10
|
-
import { ClickEvent, TouchStartEvent, type TrackHandler } from "../runtime/touch"
|
|
11
|
-
import { Physics2D } from "./Physics2D"
|
|
12
|
-
import { Scene2D } from "./Scene2D"
|
|
13
|
-
import type { Node2D } from "./Node2D"
|
|
14
|
-
|
|
15
|
-
type Active = { tracker?: TrackHandler, lastX: number, lastY: number }
|
|
16
|
-
const active = new Map<number, Active>()
|
|
17
|
-
|
|
18
|
-
let registered = false
|
|
19
|
-
/** Register the host pointer callback once, on the first click/touchstart listener (node or scene). */
|
|
20
|
-
export const ensurePointerEvents = (): void => {
|
|
21
|
-
if (registered) return
|
|
22
|
-
registered = true
|
|
23
|
-
_creator2d.setOnPointer((phase, id, x, y) => {
|
|
24
|
-
if (phase === 0) onDown(id, x, y)
|
|
25
|
-
else if (phase === 1) onMove(id, x, y)
|
|
26
|
-
else if (phase === 2) onUp(id, x, y)
|
|
27
|
-
else onCancel(id)
|
|
28
|
-
})
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
// Hit-test: screen (logical px) → world → topmost physics body at the point.
|
|
32
|
-
const hitTest = (scene: Scene2D, sx: number, sy: number): { node: Node2D | null, wx: number, wy: number } => {
|
|
33
|
-
const w = scene.camera.screenToWorld(sx, sy)
|
|
34
|
-
return { node: Physics2D.overlapPoint(w), wx: w.x, wy: w.y }
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
const onDown = (id: number, sx: number, sy: number): void => {
|
|
38
|
-
const scene = Scene2D.active
|
|
39
|
-
if (!scene) return
|
|
40
|
-
const { node, wx, wy } = hitTest(scene, sx, sy)
|
|
41
|
-
const ev = new TouchStartEvent<Node2D | null>(sx, sy, id)
|
|
42
|
-
ev.worldX = wx; ev.worldY = wy
|
|
43
|
-
ev.target = node
|
|
44
|
-
|
|
45
|
-
let tracker: TrackHandler | undefined
|
|
46
|
-
if (node) { node._dispatchEvent("touchstart", ev); tracker = ev._trackHandler }
|
|
47
|
-
if (!tracker) {
|
|
48
|
-
for (const l of scene._touchStartListeners.slice()) {
|
|
49
|
-
l(ev)
|
|
50
|
-
if (ev._trackHandler) { tracker = ev._trackHandler; break }
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
active.set(id, { tracker, lastX: sx, lastY: sy })
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
const onMove = (id: number, sx: number, sy: number): void => {
|
|
57
|
-
const a = active.get(id)
|
|
58
|
-
if (!a) return
|
|
59
|
-
a.tracker?.onMove?.({ clientX: sx, clientY: sy, deltaX: sx - a.lastX, deltaY: sy - a.lastY })
|
|
60
|
-
a.lastX = sx; a.lastY = sy
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
const onUp = (id: number, sx: number, sy: number): void => {
|
|
64
|
-
const a = active.get(id)
|
|
65
|
-
active.delete(id)
|
|
66
|
-
a?.tracker?.onEnd?.({ clientX: sx, clientY: sy, deltaX: a ? sx - a.lastX : 0, deltaY: a ? sy - a.lastY : 0 })
|
|
67
|
-
|
|
68
|
-
// A pointer-up is also a click, dispatched at the up location (independent of any track).
|
|
69
|
-
const scene = Scene2D.active
|
|
70
|
-
if (!scene) return
|
|
71
|
-
const { node, wx, wy } = hitTest(scene, sx, sy)
|
|
72
|
-
const ev = new ClickEvent<Node2D | null>(sx, sy, id)
|
|
73
|
-
ev.worldX = wx; ev.worldY = wy
|
|
74
|
-
ev.target = node
|
|
75
|
-
if (node) node._dispatchEvent("click", ev)
|
|
76
|
-
for (const l of scene._clickListeners.slice()) l(ev)
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
const onCancel = (id: number): void => {
|
|
80
|
-
const a = active.get(id)
|
|
81
|
-
active.delete(id)
|
|
82
|
-
a?.tracker?.onCancel?.()
|
|
83
|
-
}
|
package/runtime/sdk/gl/Camera.ts
DELETED
|
@@ -1,160 +0,0 @@
|
|
|
1
|
-
// The scene camera — a Node (so position / lookAt / transform all work) plus projection reads and
|
|
2
|
-
// screen↔world ray helpers. Created and attached by Scene. Ported from worker/src/components/Camera.ts.
|
|
3
|
-
|
|
4
|
-
import { Vec3 } from "../math/vec"
|
|
5
|
-
import { Aspect } from "../core/Aspect"
|
|
6
|
-
import { Node } from "./Node"
|
|
7
|
-
import { Ray } from "./Ray"
|
|
8
|
-
|
|
9
|
-
/** @internal `camera.follow(node)`: copies the node's world pose onto the camera every LATE frame
|
|
10
|
-
* (order 1000 — after every other aspect has moved things). One per camera; lives on the target. */
|
|
11
|
-
export class CameraFollowRig extends Aspect<"__cameraFollow"> {
|
|
12
|
-
static readonly aspect = "__cameraFollow"
|
|
13
|
-
/** @internal */ _camera!: Camera
|
|
14
|
-
constructor() { super(); this.updateOrder = 1000 }
|
|
15
|
-
update(): void {
|
|
16
|
-
const cam = this._camera
|
|
17
|
-
cam.position = this.node.worldPosition
|
|
18
|
-
cam.quaternion = this.node.worldQuaternion
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
/** Projection every host starts a scene with (creator-gl scene.cpp / the lite core agree on these). */
|
|
23
|
-
export const CAMERA_DEFAULTS = { fov: 60, near: 0.01, far: 1000 }
|
|
24
|
-
|
|
25
|
-
export class Camera extends Node {
|
|
26
|
-
private _sceneId = -1
|
|
27
|
-
/** Live projection matrix (updated by the host when the viewport/fov changes). */
|
|
28
|
-
readonly projectionMatrix = new Float32Array(16)
|
|
29
|
-
|
|
30
|
-
// Projection the app asked for (host defaults until it does). The host owns the aspect and
|
|
31
|
-
// re-applies these on every viewport change, so they are set, not maintained.
|
|
32
|
-
private _fov = CAMERA_DEFAULTS.fov
|
|
33
|
-
private _near = CAMERA_DEFAULTS.near
|
|
34
|
-
private _far = CAMERA_DEFAULTS.far
|
|
35
|
-
/** Stay quiet until the app actually sets something — hosts may be configured with their own
|
|
36
|
-
* defaults (the headless core takes a `fov` option) and a blind push would overwrite them. */
|
|
37
|
-
private _projSet = false
|
|
38
|
-
|
|
39
|
-
// Exposure the app asked for (filament's physical defaults until it does): f/16, 1/125 s, ISO 100 =
|
|
40
|
-
// EV100 15, bright sunlight — what every light intensity in the docs is tuned against.
|
|
41
|
-
private _exposure = { aperture: 16, shutterSpeed: 1 / 125, iso: 100 }
|
|
42
|
-
private _ev = 0
|
|
43
|
-
private _exposureSet = false
|
|
44
|
-
|
|
45
|
-
/** @internal — Scene wires the camera to its native scene id + projection callback. */
|
|
46
|
-
_attach(sceneId: number): void {
|
|
47
|
-
this._sceneId = sceneId
|
|
48
|
-
_creator.attachCameraToAR(sceneId, this.id, (mat) => {
|
|
49
|
-
for (let i = 0; i < 16; i++) this.projectionMatrix[i] = mat[i]
|
|
50
|
-
})
|
|
51
|
-
if (this._projSet) this._applyProjection()
|
|
52
|
-
if (this._exposureSet) this._applyExposure()
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/** Exposure compensation in stops (default 0): +1 doubles how bright the scene renders, −2
|
|
56
|
-
* quarters it. The one exposure knob most games need — a dark interior, a flash of white, or eye
|
|
57
|
-
* adaptation when the sun swings into view (drop it, and the sun stays at the display's peak
|
|
58
|
-
* while everything else darkens). Scene light follows it; particle `emissive` is post-exposure
|
|
59
|
-
* and does not, so a fireball keeps its on-screen brightness. Cheap to animate every frame. */
|
|
60
|
-
get exposureCompensation(): number { return this._ev }
|
|
61
|
-
set exposureCompensation(stops: number) {
|
|
62
|
-
this._ev = Number.isFinite(stops) ? stops : 0
|
|
63
|
-
this._exposureSet = true
|
|
64
|
-
this._applyExposure()
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/** The physical camera: `aperture` in f-stops, `shutterSpeed` in seconds, `iso`, plus the same
|
|
68
|
-
* `compensation` in stops — any subset, one call. `camera.setExposure({ iso: 400 })` is two stops
|
|
69
|
-
* brighter than the default f/16 · 1/125 · ISO 100. */
|
|
70
|
-
setExposure(exposure: { aperture?: number, shutterSpeed?: number, iso?: number, compensation?: number }): this {
|
|
71
|
-
if (exposure.aperture !== undefined) this._exposure.aperture = exposure.aperture
|
|
72
|
-
if (exposure.shutterSpeed !== undefined) this._exposure.shutterSpeed = exposure.shutterSpeed
|
|
73
|
-
if (exposure.iso !== undefined) this._exposure.iso = exposure.iso
|
|
74
|
-
if (exposure.compensation !== undefined) this._ev = exposure.compensation
|
|
75
|
-
this._exposureSet = true
|
|
76
|
-
this._applyExposure()
|
|
77
|
-
return this
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
/** Hosts that predate the call keep the fixed default (older wasm / native builds). */
|
|
81
|
-
private _applyExposure(): void {
|
|
82
|
-
if (this._sceneId < 0) return
|
|
83
|
-
const e = this._exposure
|
|
84
|
-
_creator.setCameraExposure?.(this._sceneId, e.aperture, e.shutterSpeed, e.iso * 2 ** this._ev)
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
/** Vertical field of view in degrees (default 60) — a smaller angle is a longer lens. */
|
|
88
|
-
get fov(): number { return _creator.getCameraFov(this._sceneId, 0) }
|
|
89
|
-
set fov(degrees: number) { this._fov = degrees; this._projSet = true; this._applyProjection() }
|
|
90
|
-
get horizontalFov(): number { return _creator.getCameraFov(this._sceneId, 1) }
|
|
91
|
-
|
|
92
|
-
/** Near clip distance (default 0.01) — nothing closer than this draws. */
|
|
93
|
-
get near(): number { return this._near }
|
|
94
|
-
set near(distance: number) { this._near = distance; this._projSet = true; this._applyProjection() }
|
|
95
|
-
|
|
96
|
-
/** Far clip distance / view range (default 1000) — geometry past it is culled. Shadows keep
|
|
97
|
-
* their own (shorter) range, so a long view distance doesn't cost shadow sharpness. */
|
|
98
|
-
get far(): number { return this._far }
|
|
99
|
-
set far(distance: number) { this._far = distance; this._projSet = true; this._applyProjection() }
|
|
100
|
-
|
|
101
|
-
/** Set any of fov / near / far in one call: `camera.setProjection({ fov: 45, far: 5000 })`. */
|
|
102
|
-
setProjection(projection: { fov?: number, near?: number, far?: number }): this {
|
|
103
|
-
if (projection.fov !== undefined) this._fov = projection.fov
|
|
104
|
-
if (projection.near !== undefined) this._near = projection.near
|
|
105
|
-
if (projection.far !== undefined) this._far = projection.far
|
|
106
|
-
this._projSet = true
|
|
107
|
-
this._applyProjection()
|
|
108
|
-
return this
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
/** Hosts that predate the call keep the fixed defaults (older wasm / native builds). */
|
|
112
|
-
private _applyProjection(): void {
|
|
113
|
-
if (this._sceneId < 0 || typeof _creator.setCameraProjection !== "function") return
|
|
114
|
-
_creator.setCameraProjection(this._sceneId, this._fov, this._near, this._far)
|
|
115
|
-
}
|
|
116
|
-
private _following: Node | null = null
|
|
117
|
-
/** The node the camera currently follows (`follow()`), or null. */
|
|
118
|
-
get following(): Node | null { return this._following }
|
|
119
|
-
|
|
120
|
-
/**
|
|
121
|
-
* Make the camera ride a node: every late frame the camera takes the node's WORLD pose (−Z =
|
|
122
|
-
* view direction), so movement aspects / animation on that node are camera moves — dolly shots,
|
|
123
|
-
* a cutscene path, a `CameraPlace` in a scene file. A node carrying a `CameraPlace` also hands
|
|
124
|
-
* over its projection (fov / near / far). `follow(null)` releases the camera where it is.
|
|
125
|
-
* One camera per scene: following a new node stops following the previous one.
|
|
126
|
-
*/
|
|
127
|
-
follow(target: Node | { node: Node, applyTo(camera: Camera): void } | null): this {
|
|
128
|
-
const node = target === null ? null : target instanceof Node ? target : target.node
|
|
129
|
-
if (this._following && this._following !== node) this._following.removeAspect(CameraFollowRig)
|
|
130
|
-
this._following = node
|
|
131
|
-
if (!node) return this
|
|
132
|
-
// a CameraPlace on the node (or passed directly) sets the lens too
|
|
133
|
-
const place = target instanceof Node
|
|
134
|
-
? (node as unknown as { cameraPlace?: { applyTo(camera: Camera): void } }).cameraPlace
|
|
135
|
-
: target as { applyTo(camera: Camera): void }
|
|
136
|
-
place?.applyTo(this)
|
|
137
|
-
node.aspect(CameraFollowRig, { _camera: this })
|
|
138
|
-
this.position = node.worldPosition
|
|
139
|
-
this.quaternion = node.worldQuaternion
|
|
140
|
-
return this
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
get displaySize(): [number, number] {
|
|
144
|
-
const s = _creator.getDisplaySize()
|
|
145
|
-
return [ s[0], s[1] ]
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
/** World-space ray direction through a screen pixel. */
|
|
149
|
-
getViewDirection(screenX: number, screenY: number): Vec3 {
|
|
150
|
-
if (this._sceneId < 0) throw new Error("Camera is not attached to a scene")
|
|
151
|
-
const v = new Float32Array(3)
|
|
152
|
-
_creator.getCameraViewDirection(this._sceneId, screenX, screenY, v)
|
|
153
|
-
return new Vec3(v[0], v[1], v[2])
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
/** A pick ray from the camera through a screen pixel. */
|
|
157
|
-
getRay(screenX: number, screenY: number): Ray {
|
|
158
|
-
return new Ray(this.worldPosition, this.getViewDirection(screenX, screenY))
|
|
159
|
-
}
|
|
160
|
-
}
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
// CameraPlace: "the camera as a place". A scene has ONE camera (`scene.camera`); a CameraPlace marks
|
|
2
|
-
// a node as somewhere that camera can be — with the lens it uses there. Nothing is created engine-
|
|
3
|
-
// side (no second camera, no ABI): it is a pose + a projection record on any node: an empty, an
|
|
4
|
-
// empty mounted on a bone (`mount:`), a node a path aspect moves (a dolly shot), a model node.
|
|
5
|
-
//
|
|
6
|
-
// • In a scene FILE that runs (`open()` / `load()`), the `active: true` place drives
|
|
7
|
-
// `scene.camera` (exactly `scene.camera.follow(node)`); exactly one per file.
|
|
8
|
-
// • Inside a prefab or an `instantiate()`d subtree a place is inert DATA — the hosting scene
|
|
9
|
-
// owns its camera. The host reads it (`inst.nodes.eye.get(CameraPlace)` → world pose + fov)
|
|
10
|
-
// or anchors on it (`inst.alignTo(inst.nodes.eye)`).
|
|
11
|
-
// • From code: `scene.camera.follow(dolly.get(CameraPlace))` — a cutscene shot; `follow(null)`
|
|
12
|
-
// releases.
|
|
13
|
-
// • Edit mode: draws an anchored frustum gizmo (the editor's camera preview reads the same place).
|
|
14
|
-
// See docs/scene-camera-place-plan.md.
|
|
15
|
-
|
|
16
|
-
import { Aspect } from "../core/Aspect"
|
|
17
|
-
import type { FieldMeta } from "../core/fields"
|
|
18
|
-
import { Gizmos } from "../scene/gizmos"
|
|
19
|
-
import type { Camera } from "./Camera"
|
|
20
|
-
import type { Node } from "./Node"
|
|
21
|
-
|
|
22
|
-
export class CameraPlace extends Aspect<"cameraPlace", Node> {
|
|
23
|
-
static readonly aspect = "cameraPlace"
|
|
24
|
-
/** Vertical field of view in degrees (default 60) — smaller is a longer lens. */
|
|
25
|
-
fov = 60
|
|
26
|
-
/** Near clip distance. */
|
|
27
|
-
near = 0.01
|
|
28
|
-
/** Far clip distance = view range. */
|
|
29
|
-
far = 1000
|
|
30
|
-
/** The place that drives `scene.camera` when the file declaring it RUNS. Exactly one per file;
|
|
31
|
-
* ignored inside prefabs / instantiated subtrees (the host scene owns its camera). */
|
|
32
|
-
active = false
|
|
33
|
-
/** Aspect ratio of the frustum gizmo only (the real aspect is the viewport's). */
|
|
34
|
-
static fields: FieldMeta<CameraPlace> = {
|
|
35
|
-
fov: { label: "FOV", min: 1, max: 179, step: 1 },
|
|
36
|
-
near: { min: 0.001, max: 10, step: 0.01 },
|
|
37
|
-
far: { min: 1, max: 100000, step: 1 },
|
|
38
|
-
active: { label: "Active camera" },
|
|
39
|
-
}
|
|
40
|
-
static editor = { rebuild: true }
|
|
41
|
-
|
|
42
|
-
/** Copy this place's projection onto a camera (`camera.follow(place)` does it for you). */
|
|
43
|
-
applyTo(camera: Camera): void {
|
|
44
|
-
camera.setProjection({ fov: this.fov, near: this.near, far: this.far })
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
/** Edit mode: the frustum marker (−Z = view), anchored on the node so it follows drags live and
|
|
48
|
-
* clicking it selects the node. Play mode never calls this. */
|
|
49
|
-
rebuild(): void {
|
|
50
|
-
Gizmos.frustum(this.fov, { node: this.node, color: this.active ? "#ffffff" : "#9aa3b2" })
|
|
51
|
-
}
|
|
52
|
-
}
|
|
@@ -1,238 +0,0 @@
|
|
|
1
|
-
// A character controller, as an aspect on a 3D Node, backed by Jolt's CharacterVirtual — a kinematic
|
|
2
|
-
// collide-and-slide controller (NOT a rigid body: no bouncing/tipping, crisp control, built-in stair
|
|
3
|
-
// stepping + slope handling). Requires a Shape (a capsule is recommended) and does NOT use Physics.
|
|
4
|
-
//
|
|
5
|
-
// const hero = new Mesh(capsuleGeometry(), material)
|
|
6
|
-
// .aspect(Shape, { capsule: { halfHeight: 0.6, radius: 0.3 } })
|
|
7
|
-
// .aspect(CharacterController)
|
|
8
|
-
// setLoop(() => hero.controller.move((Input.key('KeyD') ? 1 : 0) - (Input.key('KeyA') ? 1 : 0) * SPEED,
|
|
9
|
-
// (Input.key('KeyS') ? 1 : 0) - (Input.key('KeyW') ? 1 : 0) * SPEED))
|
|
10
|
-
// Input.on('keydown', e => { if (e.code === 'Space' && hero.controller.grounded) hero.controller.velocityY = 7 })
|
|
11
|
-
//
|
|
12
|
-
// The two halves of the velocity have different NATURES, so they have different channels:
|
|
13
|
-
//
|
|
14
|
-
// • HORIZONTAL — `move(x, z)`, world units/s, a PER-FRAME COMMAND. A walking character's horizontal
|
|
15
|
-
// velocity is muscle-driven and not conserved: stop pushing, stop moving. So the command expires
|
|
16
|
-
// each frame — no call this frame means standing still, and releasing the keys needs no explicit
|
|
17
|
-
// zero. It is latched for the whole frame, so every physics sub-step of that frame sees it.
|
|
18
|
-
// • VERTICAL — `velocityY`, LATCHED ballistic state. Gravity integrates into it in the engine;
|
|
19
|
-
// you seed it for a jump / dash / bounce pad / explosion (`velocityY = 7`, `velocityY += 3`).
|
|
20
|
-
// There is deliberately no ground check: coyote time, double jumps and wall jumps all need
|
|
21
|
-
// the caller's own condition.
|
|
22
|
-
//
|
|
23
|
-
// `velocity` reads back what the solver ENDED UP with after the last step (post-collision) — walk into
|
|
24
|
-
// a wall and it reads ~0 even though you commanded 5. Everything runs on the engine's fixed clock, so
|
|
25
|
-
// the aspect has no per-frame update: a character costs zero JS work per frame.
|
|
26
|
-
//
|
|
27
|
-
// FREE MODE — `gravityScale = 0` turns the character into a swimmer / flyer / drone: no gravity, and
|
|
28
|
-
// (because Jolt would otherwise snap it down to the floor it passes over) no stick-to-floor and no
|
|
29
|
-
// stair walking either. There the vertical is muscle-driven too, so `move()` takes all three
|
|
30
|
-
// components and the whole velocity becomes a per-frame command.
|
|
31
|
-
//
|
|
32
|
-
// ROTATION AND SCALE stay the node's: the engine writes only the position, so a rig can be yawed and
|
|
33
|
-
// scaled directly instead of needing a child node for the visuals (a capsule is symmetric about its up
|
|
34
|
-
// axis, so its yaw is physically irrelevant either way).
|
|
35
|
-
//
|
|
36
|
-
// CROUCHING goes through the Shape aspect (`node.aspect(Shape, { capsule: … })`) — see `resizing`.
|
|
37
|
-
//
|
|
38
|
-
// Note: it collides with solid bodies but PASSES THROUGH triggers, and (v1) is not itself detected by
|
|
39
|
-
// triggers and isn't pointer-pickable while active.
|
|
40
|
-
|
|
41
|
-
import { Aspect } from "../core/Aspect"
|
|
42
|
-
import type { FieldMeta } from "../core/fields"
|
|
43
|
-
import { Vec3, cx, cy, cz, type Vec2Like, type Vec3Like } from "../math/vec"
|
|
44
|
-
import type { CompAxis, CompWriter } from "../core/compWrite"
|
|
45
|
-
import { Shape } from "./Shape"
|
|
46
|
-
import type { Node } from "./Node"
|
|
47
|
-
|
|
48
|
-
/** Where the character's feet are, as reported by the solver after the last step. */
|
|
49
|
-
export type GroundState = "ground" | "slope" | "unsupported" | "air"
|
|
50
|
-
|
|
51
|
-
// Index = JPH::EGroundState (0 OnGround / 1 OnSteepGround / 2 NotSupported / 3 InAir).
|
|
52
|
-
const GROUND_STATES: readonly GroundState[] = ["ground", "slope", "unsupported", "air"]
|
|
53
|
-
|
|
54
|
-
const scratch = new Float32Array(3)
|
|
55
|
-
let warnedFreeMove = false
|
|
56
|
-
|
|
57
|
-
export class CharacterController extends Aspect<"controller", Node> implements CompWriter {
|
|
58
|
-
static readonly aspect = "controller"
|
|
59
|
-
|
|
60
|
-
// Both tunables are accessor-backed (they write through to the engine), and describeFields only
|
|
61
|
-
// enumerates own enumerable fields — so the inspector is told about them explicitly. The default
|
|
62
|
-
// VALUES still come from a fresh instance through the getters; this only supplies the keys.
|
|
63
|
-
static fields: FieldMeta<CharacterController> = {
|
|
64
|
-
gravityScale: { editor: "number", min: 0, step: 0.1, label: "Gravity scale" },
|
|
65
|
-
maxSlope: { editor: "number", min: 0, max: 89, step: 1, label: "Max slope°" },
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
private _charId = 0
|
|
69
|
-
private _gravityScale = 2
|
|
70
|
-
private _maxSlope = 45
|
|
71
|
-
|
|
72
|
-
onAttach(): void {
|
|
73
|
-
if (!_creator.physicsHasSupport || !_creator.physicsHasSupport()) return
|
|
74
|
-
const shape = this.node.get(Shape)
|
|
75
|
-
if (!shape) {
|
|
76
|
-
throw new Error("CharacterController requires a Shape aspect (a capsule is recommended) — add it first: node.aspect(Shape, { capsule: {…} }).aspect(CharacterController)")
|
|
77
|
-
}
|
|
78
|
-
if (shape._isTriangleMesh) {
|
|
79
|
-
throw new Error("CharacterController needs a convex Shape (capsule recommended) — { mesh: true } is a triangle mesh; use { mesh: 'convex' } or a capsule")
|
|
80
|
-
}
|
|
81
|
-
const shapeId = shape._claim() // the character owns the shape; it is not a rigid body
|
|
82
|
-
// Read the options through their PUBLIC accessors, never the `_backing` fields: chisel drops an
|
|
83
|
-
// accessor pair whose name is only ever an object-literal key, and then
|
|
84
|
-
// `aspect(CharacterController, { gravityScale: 0 })` silently leaves the default in place (the
|
|
85
|
-
// character keeps falling instead of entering free mode). One property read here keeps the pair.
|
|
86
|
-
this._charId = _creator.characterCreate(this.node.id, shapeId, this.maxSlope)
|
|
87
|
-
if (!this._charId) return
|
|
88
|
-
_creator.characterSetGravityScale(this._charId, this.gravityScale)
|
|
89
|
-
this.node._xf = this._charId // routes node.position / .x / .y / .z / .matrix writes to the character
|
|
90
|
-
this.node._xfKind = 1
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
onDetach(): void {
|
|
94
|
-
if (this._charId) { _creator.characterDestroy(this._charId); this._charId = 0 }
|
|
95
|
-
this.node._xf = 0
|
|
96
|
-
this.node._xfKind = 0
|
|
97
|
-
this.node.get(Shape)?._recreatePickBody() // hands the routing back to the pick body
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
/** Native character id (0 until attached / no physics support). */
|
|
101
|
-
get id(): number { return this._charId }
|
|
102
|
-
|
|
103
|
-
/**
|
|
104
|
-
* This frame's movement command, in world units/s — NOT normalized, NOT a per-frame displacement
|
|
105
|
-
* (that is what Unity's `Move` takes; passing `v * dt` here gives a character 60× too slow).
|
|
106
|
-
*
|
|
107
|
-
* Two components = horizontal `(x, z)`, the everyday call. Three = the whole velocity, for free mode
|
|
108
|
-
* (`gravityScale = 0`); with gravity on, the vertical component fights the ballistic one and the
|
|
109
|
-
* character barely falls, so that combination warns.
|
|
110
|
-
*
|
|
111
|
-
* Sticky only within the frame: the command expires once the engine consumes it. It also **takes the
|
|
112
|
-
* axis back from a latched `velocity`** — commanding is claiming ownership, which is what keeps the
|
|
113
|
-
* two horizontal sources from ever fighting.
|
|
114
|
-
*/
|
|
115
|
-
move(x: number, z: number): void
|
|
116
|
-
move(v: Vec2Like | Vec3Like): void
|
|
117
|
-
move(x: number | Vec2Like | Vec3Like, z?: number): void {
|
|
118
|
-
if (!this._charId) return
|
|
119
|
-
if (typeof x === "number") { _creator.characterMove(this._charId, x, z ?? 0); return }
|
|
120
|
-
// cz() is typed for 3-component inputs; a Vec2 legitimately has no `z` and reads undefined here.
|
|
121
|
-
const third = cz(x as Vec3Like) // undefined for a Vec2 / 2-tuple / 2-long typed array
|
|
122
|
-
if (third === undefined) { _creator.characterMove(this._charId, cx(x), cy(x)); return }
|
|
123
|
-
if (this._gravityScale !== 0 && !warnedFreeMove) {
|
|
124
|
-
warnedFreeMove = true
|
|
125
|
-
console.warn("CharacterController.move(): the 3-component form is the FREE-mode command (swimming / flying) and expects gravityScale = 0. With gravity on it overwrites the falling speed every frame, so the character will hang in the air. Use move(x, z) and velocityY for a walking character.")
|
|
126
|
-
}
|
|
127
|
-
_creator.characterMoveFree(this._charId, cx(x), cy(x), third)
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
/** Vertical velocity (world units/s) — LATCHED: gravity works on it, you seed it. `= 7` to jump,
|
|
131
|
-
* `+= 3` to stack an explosion on top of the current motion. No ground check: guard it yourself
|
|
132
|
-
* with `grounded` (or don't, for a double jump). Overridden every frame by a 3-component `move()`.
|
|
133
|
-
* `velocity.y = 7` is the same channel — pick whichever reads better. */
|
|
134
|
-
get velocityY(): number {
|
|
135
|
-
if (!this._charId) return 0
|
|
136
|
-
_creator.characterGetVelocity(this._charId, scratch)
|
|
137
|
-
return scratch[1]
|
|
138
|
-
}
|
|
139
|
-
set velocityY(v: number) {
|
|
140
|
-
if (this._charId) _creator.characterSetVerticalVelocity(this._charId, v)
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
/** Multiplier over the world gravity (`Physics.configure({ gravity })`); default 2, for the snappier
|
|
144
|
-
* fall games want. **0 = free mode**: no gravity, no stick-to-floor, no stair stepping — a swimmer
|
|
145
|
-
* or a drone, driven by the 3-component `move()`. */
|
|
146
|
-
get gravityScale(): number { return this._gravityScale }
|
|
147
|
-
set gravityScale(v: number) {
|
|
148
|
-
this._gravityScale = v
|
|
149
|
-
if (this._charId) _creator.characterSetGravityScale(this._charId, v)
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
/** Max ground slope (degrees) the character treats as walkable; default 45. Live. */
|
|
153
|
-
get maxSlope(): number { return this._maxSlope }
|
|
154
|
-
set maxSlope(v: number) {
|
|
155
|
-
this._maxSlope = v
|
|
156
|
-
if (this._charId) _creator.characterSetMaxSlope(this._charId, v)
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
/**
|
|
160
|
-
* True while a requested collider resize hasn't taken — you asked to stand up and there is something
|
|
161
|
-
* overhead. Resizing goes through the `Shape` aspect itself:
|
|
162
|
-
*
|
|
163
|
-
* hero.aspect(Shape, { capsule: CROUCHED }) // always fits — you are shrinking
|
|
164
|
-
* hero.aspect(Shape, { capsule: STANDING }) // may be refused under a low ceiling
|
|
165
|
-
* if (hero.controller.resizing) … // still crouched; call it again next frame
|
|
166
|
-
*
|
|
167
|
-
* A refusal changes nothing, so the retry is just the same call again — and it is an exact headroom
|
|
168
|
-
* test against the real capsule, unlike a hand-rolled raycast (a ray is a line; a capsule has girth).
|
|
169
|
-
* The FEET stay planted across a resize, so the character neither hovers nor sinks.
|
|
170
|
-
*/
|
|
171
|
-
get resizing(): boolean {
|
|
172
|
-
return this.node.get(Shape)?._resizeRefused ?? false
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
/** True while standing on walkable ground. */
|
|
176
|
-
get grounded(): boolean {
|
|
177
|
-
return this._charId ? _creator.characterGetGroundState(this._charId) === 0 : false
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
/** Where the feet are after the last step: on walkable ground, on too-steep ground, touching
|
|
181
|
-
* something that can't support it, or in the air. */
|
|
182
|
-
get groundState(): GroundState {
|
|
183
|
-
return this._charId ? (GROUND_STATES[_creator.characterGetGroundState(this._charId)] ?? "air") : "air"
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
/**
|
|
187
|
-
* READ — the velocity the solver ended up with after the most recent step (world units/s, fresh
|
|
188
|
-
* Vec3): what HAPPENED, not what you asked for. Walking into a wall reads ~0, sliding along one
|
|
189
|
-
* reads the tangent. Unlike Unity's synchronous `Move`, our step runs later in the frame, so a read
|
|
190
|
-
* is up to one frame old — irrelevant for animation speed, fall damage or "am I blocked", which is
|
|
191
|
-
* what it is for. (Exact wall contact would need a contact normal; the engine has none yet.)
|
|
192
|
-
*
|
|
193
|
-
* WRITE — LATCH the whole velocity: a knockback, a wall jump, a launch pad. Unlike `move()` it does
|
|
194
|
-
* not expire, so the character keeps flying, and gravity still pulls the vertical down into a real
|
|
195
|
-
* ballistic arc. It stays until `move()` takes the axis back — so a game simply doesn't call
|
|
196
|
-
* `move()` while the throw lasts, and ends it on its own terms:
|
|
197
|
-
*
|
|
198
|
-
* hero.controller.velocity = [dir.x * 12, 6, dir.z * 12] // hit by the blast
|
|
199
|
-
* // …in the loop:
|
|
200
|
-
* if (thrown) { if (hero.controller.grounded) thrown = false } // landing ends it
|
|
201
|
-
* else hero.controller.move(ix * SPEED, iz * SPEED) // …and this reclaims the axis
|
|
202
|
-
*
|
|
203
|
-
* Note nothing clears the latch by itself, landing included — so a thrown character that never gets
|
|
204
|
-
* a `move()` keeps sliding along the ground (a kinematic controller has no friction).
|
|
205
|
-
*
|
|
206
|
-
* Reading is not the inverse of writing: `c.velocity = c.velocity` is NOT a no-op, because the read
|
|
207
|
-
* reports the measured result (against a wall it is ~0 and would cancel the throw). In flight the
|
|
208
|
-
* two agree closely, so read-modify-write mid-air behaves as expected.
|
|
209
|
-
*
|
|
210
|
-
* `c.velocity.y = 7` (the direct spelling) is a jump — the compiler routes it to the exact
|
|
211
|
-
* `velocityY` channel via `_writeComp` below, no vector allocated — and `c.velocity.x = 3` latches
|
|
212
|
-
* the whole vector with the measured velocity filling in the other two, i.e. the read-modify-write
|
|
213
|
-
* above spelled naturally. A STORED copy is still a copy: `const v = c.velocity; v.y = 7` does nothing.
|
|
214
|
-
*/
|
|
215
|
-
get velocity(): Vec3 {
|
|
216
|
-
if (!this._charId) return new Vec3(0, 0, 0)
|
|
217
|
-
_creator.characterGetVelocity(this._charId, scratch)
|
|
218
|
-
return new Vec3(scratch[0], scratch[1], scratch[2])
|
|
219
|
-
}
|
|
220
|
-
set velocity(v: Vec3Like) {
|
|
221
|
-
if (this._charId) _creator.characterSetVelocity(this._charId, cx(v), cy(v), cz(v))
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
/** Compile-time list (chisel reads it, then strips it): the getters whose `c.<getter>.<axis> = v`
|
|
225
|
-
* spelling is routed to `_writeComp` below. See core/compWrite.ts. */
|
|
226
|
-
static _comps = [ "velocity" ]
|
|
227
|
-
|
|
228
|
-
/** @internal `c.velocity.<axis> = v` compiles to this: the vertical axis is the precise `velocityY`
|
|
229
|
-
* channel, any other latches the whole vector with the measured rest. */
|
|
230
|
-
_writeComp(_prop: string, axis: CompAxis, v: number): void {
|
|
231
|
-
if (!this._charId) return
|
|
232
|
-
if (axis === "y") { _creator.characterSetVerticalVelocity(this._charId, v); return }
|
|
233
|
-
_creator.characterGetVelocity(this._charId, scratch)
|
|
234
|
-
if (axis === "x") scratch[0] = v
|
|
235
|
-
else if (axis === "z") scratch[2] = v
|
|
236
|
-
_creator.characterSetVelocity(this._charId, scratch[0], scratch[1], scratch[2])
|
|
237
|
-
}
|
|
238
|
-
}
|