lecodes-sdk 0.19.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 +114 -0
- package/dist/global.d.ts +503 -0
- package/dist/host.d.ts +61 -0
- package/dist/inject.js +4730 -0
- package/dist/types/animate/animate.d.ts +20 -0
- package/dist/types/animate/bezier.d.ts +2 -0
- package/dist/types/animate/easings.d.ts +21 -0
- package/dist/types/canvas/Canvas.d.ts +168 -0
- package/dist/types/core/Aspect.d.ts +171 -0
- package/dist/types/core/InspectorUI.d.ts +88 -0
- package/dist/types/core/StateMachine.d.ts +81 -0
- package/dist/types/core/color.d.ts +13 -0
- package/dist/types/core/compWrite.d.ts +11 -0
- package/dist/types/core/events.d.ts +11 -0
- package/dist/types/core/fields.d.ts +58 -0
- package/dist/types/core/registry.d.ts +7 -0
- package/dist/types/core/signals.d.ts +29 -0
- package/dist/types/core/time.d.ts +31 -0
- package/dist/types/g2/Camera2D.d.ts +19 -0
- package/dist/types/g2/CharacterController2D.d.ts +134 -0
- package/dist/types/g2/Node2D.d.ts +91 -0
- package/dist/types/g2/OneWay2D.d.ts +24 -0
- package/dist/types/g2/Physics2D.d.ts +147 -0
- package/dist/types/g2/Scene2D.d.ts +72 -0
- package/dist/types/g2/Shape2D.d.ts +58 -0
- package/dist/types/g2/Sprite.d.ts +51 -0
- package/dist/types/g2/SpriteAnimation.d.ts +50 -0
- package/dist/types/g2/SpriteSheet.d.ts +69 -0
- package/dist/types/g2/Texture2D.d.ts +16 -0
- package/dist/types/g2/Tilemap.d.ts +20 -0
- package/dist/types/g2/Tileset.d.ts +32 -0
- package/dist/types/g2/Trigger2D.d.ts +20 -0
- package/dist/types/g2/autotile.d.ts +133 -0
- package/dist/types/g2/cells.d.ts +14 -0
- package/dist/types/g2/defineScene2d.d.ts +141 -0
- package/dist/types/g2/groups2d.d.ts +16 -0
- package/dist/types/g2/loop.d.ts +4 -0
- package/dist/types/g2/scenarios2d.d.ts +30 -0
- package/dist/types/g2/touch.d.ts +2 -0
- package/dist/types/gl/Camera.d.ts +79 -0
- package/dist/types/gl/CameraPlace.d.ts +26 -0
- package/dist/types/gl/CharacterController.d.ts +98 -0
- package/dist/types/gl/Gearbox.d.ts +86 -0
- package/dist/types/gl/Geometry.d.ts +39 -0
- package/dist/types/gl/IK.d.ts +53 -0
- package/dist/types/gl/InstancedMesh.d.ts +46 -0
- package/dist/types/gl/Light.d.ts +73 -0
- package/dist/types/gl/Lightmap.d.ts +85 -0
- package/dist/types/gl/Locomotion.d.ts +279 -0
- package/dist/types/gl/Material.d.ts +100 -0
- package/dist/types/gl/Mesh.d.ts +33 -0
- package/dist/types/gl/Model.d.ts +53 -0
- package/dist/types/gl/NavAgent.d.ts +99 -0
- package/dist/types/gl/NavMesh.d.ts +169 -0
- package/dist/types/gl/Node.d.ts +99 -0
- package/dist/types/gl/Noise.d.ts +20 -0
- package/dist/types/gl/Particles.d.ts +277 -0
- package/dist/types/gl/Physics.d.ts +91 -0
- package/dist/types/gl/Plane.d.ts +19 -0
- package/dist/types/gl/Ragdoll.d.ts +86 -0
- package/dist/types/gl/Ray.d.ts +8 -0
- package/dist/types/gl/Scene.d.ts +221 -0
- package/dist/types/gl/Shape.d.ts +122 -0
- package/dist/types/gl/Terrain.d.ts +280 -0
- package/dist/types/gl/Texture.d.ts +27 -0
- package/dist/types/gl/Trigger.d.ts +10 -0
- package/dist/types/gl/Vehicle.d.ts +191 -0
- package/dist/types/gl/Wheel.d.ts +95 -0
- package/dist/types/gl/animation/AnimationClip.d.ts +60 -0
- package/dist/types/gl/animation/Animator.d.ts +219 -0
- package/dist/types/gl/animation/Layer.d.ts +31 -0
- package/dist/types/gl/animation/Loop.d.ts +17 -0
- package/dist/types/gl/animation/Playback.d.ts +36 -0
- package/dist/types/gl/animation/core.d.ts +367 -0
- package/dist/types/gl/controls.d.ts +19 -0
- package/dist/types/gl/physicsEvents.d.ts +1 -0
- package/dist/types/gl/scenarios.d.ts +98 -0
- package/dist/types/gl/state.d.ts +5 -0
- package/dist/types/gl/terrainMesh.d.ts +46 -0
- package/dist/types/gl/touch.d.ts +2 -0
- package/dist/types/inject.d.ts +123 -0
- package/dist/types/math/Mathf.d.ts +39 -0
- package/dist/types/math/mat4.d.ts +66 -0
- package/dist/types/math/quat.d.ts +53 -0
- package/dist/types/math/vec.d.ts +129 -0
- package/dist/types/net/codec.d.ts +25 -0
- package/dist/types/net/core.d.ts +106 -0
- package/dist/types/net/index.d.ts +80 -0
- package/dist/types/net/replication.d.ts +101 -0
- package/dist/types/plugins/camera.d.ts +25 -0
- package/dist/types/plugins/geolocation.d.ts +39 -0
- package/dist/types/plugins/oauth.d.ts +25 -0
- package/dist/types/plugins/permission.d.ts +1 -0
- package/dist/types/plugins/push.d.ts +46 -0
- package/dist/types/plugins/qr.d.ts +21 -0
- package/dist/types/plugins/service.d.ts +33 -0
- package/dist/types/runtime/app.d.ts +67 -0
- package/dist/types/runtime/appEvents.d.ts +7 -0
- package/dist/types/runtime/channel.d.ts +1 -0
- package/dist/types/runtime/clipboard.d.ts +7 -0
- package/dist/types/runtime/datetime.d.ts +80 -0
- package/dist/types/runtime/device.d.ts +126 -0
- package/dist/types/runtime/fetch.d.ts +31 -0
- package/dist/types/runtime/files.d.ts +33 -0
- package/dist/types/runtime/input.d.ts +121 -0
- package/dist/types/runtime/media.d.ts +46 -0
- package/dist/types/runtime/misc.d.ts +11 -0
- package/dist/types/runtime/net.d.ts +15 -0
- package/dist/types/runtime/rpc.d.ts +44 -0
- package/dist/types/runtime/service.d.ts +1 -0
- package/dist/types/runtime/share.d.ts +2 -0
- package/dist/types/runtime/storage.d.ts +5 -0
- package/dist/types/runtime/touch.d.ts +56 -0
- package/dist/types/scene/defineScene.d.ts +296 -0
- package/dist/types/scene/editorPlugins.d.ts +82 -0
- package/dist/types/scene/gizmos.d.ts +56 -0
- package/dist/types/scene/grammar.d.ts +56 -0
- package/dist/types/scene/level.d.ts +98 -0
- package/dist/types/scene/material.d.ts +76 -0
- package/dist/types/server/auth/appConfig.d.ts +16 -0
- package/dist/types/server/auth/global.d.ts +56 -0
- package/dist/types/server/auth/models.d.ts +164 -0
- package/dist/types/server/auth/types.d.ts +56 -0
- package/dist/types/server/channel.d.ts +28 -0
- package/dist/types/server/context.d.ts +27 -0
- package/dist/types/server/db/defineDb.d.ts +36 -0
- package/dist/types/server/db/fields.d.ts +146 -0
- package/dist/types/server/db/index.d.ts +6 -0
- package/dist/types/server/db/marci/query.d.ts +326 -0
- package/dist/types/server/db/types.d.ts +163 -0
- package/dist/types/server/errors.d.ts +8 -0
- package/dist/types/server/inject.d.ts +5 -0
- package/dist/types/ui/NativeView.d.ts +38 -0
- package/dist/types/ui/UI.d.ts +27 -0
- package/dist/types/ui/UIBottomSheet.d.ts +71 -0
- package/dist/types/ui/UIButton.d.ts +30 -0
- package/dist/types/ui/UIContainer.d.ts +33 -0
- package/dist/types/ui/UIImage.d.ts +35 -0
- package/dist/types/ui/UIInput.d.ts +110 -0
- package/dist/types/ui/UIModal.d.ts +70 -0
- package/dist/types/ui/UINode.d.ts +363 -0
- package/dist/types/ui/UIPager.d.ts +123 -0
- package/dist/types/ui/UIPopover.d.ts +39 -0
- package/dist/types/ui/UIScreen.d.ts +57 -0
- package/dist/types/ui/UIScrollable.d.ts +40 -0
- package/dist/types/ui/UISpacer.d.ts +9 -0
- package/dist/types/ui/UITabs.d.ts +96 -0
- package/dist/types/ui/UIText.d.ts +19 -0
- package/dist/types/ui/UIVideo.d.ts +28 -0
- package/dist/types/ui/UIVirtualizedList.d.ts +97 -0
- package/dist/types/ui/UIWidget.d.ts +65 -0
- package/dist/types/ui/fonts.d.ts +9 -0
- package/dist/types/ui/presentable.d.ts +68 -0
- package/dist/types/ui/router.d.ts +40 -0
- package/dist/types/ui/theme.d.ts +26 -0
- package/dist/types.json +1 -0
- package/package.json +46 -0
- package/prompts/2d.md +395 -0
- package/prompts/3d-scene-files.md +113 -0
- package/prompts/3d-scene.md +119 -0
- package/prompts/3d.md +303 -0
- package/prompts/README.md +142 -0
- package/prompts/ar.md +109 -0
- package/prompts/canvas.md +27 -0
- package/prompts/compose.ts +46 -0
- package/prompts/concept.md +49 -0
- package/prompts/core-design.md +75 -0
- package/prompts/core.md +264 -0
- package/prompts/design.md +218 -0
- package/prompts/dist/2d-game.md +1530 -0
- package/prompts/dist/3d-app.md +1672 -0
- package/prompts/dist/ar-app.md +1548 -0
- package/prompts/dist/concept.md +49 -0
- package/prompts/dist/design.md +498 -0
- package/prompts/dist/ui-app.md +1134 -0
- package/prompts/index.md +37 -0
- package/prompts/intake-prompt.md +33 -0
- package/prompts/namer-prompt.md +12 -0
- package/prompts/router-prompt.md +50 -0
- package/prompts/select.ts +111 -0
- package/prompts/ui-design.md +203 -0
- package/prompts/ui.md +803 -0
- package/src/animate/animate.ts +238 -0
- package/src/animate/bezier.ts +138 -0
- package/src/animate/easings.ts +126 -0
- package/src/bridges.d.ts +1345 -0
- package/src/canvas/Canvas.ts +305 -0
- package/src/compile/__tests__/assetIconMacro.test.ts +219 -0
- package/src/compile/__tests__/assetMacro.test.ts +74 -0
- package/src/compile/__tests__/assetName.test.ts +55 -0
- package/src/compile/__tests__/compile.test.ts +287 -0
- package/src/compile/__tests__/detectEntry.test.ts +132 -0
- package/src/compile/__tests__/fontMacro.test.ts +199 -0
- package/src/compile/aspectMacro.ts +86 -0
- package/src/compile/assetIconMacro.ts +384 -0
- package/src/compile/assetMacro.ts +146 -0
- package/src/compile/assetName.ts +50 -0
- package/src/compile/bundler.ts +287 -0
- package/src/compile/compileProject.ts +127 -0
- package/src/compile/detectEntry.ts +128 -0
- package/src/compile/fontMacro.ts +459 -0
- package/src/compile/fontRegistry.ts +78 -0
- package/src/compile/header.ts +67 -0
- package/src/compile/index.ts +100 -0
- package/src/compile/libraryImports.ts +52 -0
- package/src/compile/liteMaterial.ts +247 -0
- package/src/compile/sceneEditor.ts +88 -0
- package/src/compile/serverSplit.ts +233 -0
- package/src/compile/serverTypes.ts +227 -0
- package/src/compile/sfnt.ts +98 -0
- package/src/compile/shaderSchema.ts +202 -0
- package/src/compile/shaderTargets.ts +77 -0
- package/src/compile/sourcemap.ts +25 -0
- package/src/core/Aspect.ts +568 -0
- package/src/core/InspectorUI.ts +212 -0
- package/src/core/StateMachine.ts +308 -0
- package/src/core/__tests__/stateMachine.test.ts +132 -0
- package/src/core/color.ts +66 -0
- package/src/core/compWrite.ts +42 -0
- package/src/core/events.ts +38 -0
- package/src/core/fields.ts +120 -0
- package/src/core/registry.ts +23 -0
- package/src/core/signals.ts +277 -0
- package/src/core/time.ts +84 -0
- package/src/g2/Camera2D.ts +40 -0
- package/src/g2/CharacterController2D.ts +276 -0
- package/src/g2/Node2D.ts +267 -0
- package/src/g2/OneWay2D.ts +66 -0
- package/src/g2/Physics2D.ts +346 -0
- package/src/g2/Scene2D.ts +209 -0
- package/src/g2/Shape2D.ts +259 -0
- package/src/g2/Sprite.ts +89 -0
- package/src/g2/SpriteAnimation.ts +171 -0
- package/src/g2/SpriteSheet.ts +166 -0
- package/src/g2/Texture2D.ts +47 -0
- package/src/g2/Tilemap.ts +41 -0
- package/src/g2/Tileset.ts +71 -0
- package/src/g2/Trigger2D.ts +77 -0
- package/src/g2/autotile.ts +433 -0
- package/src/g2/cells.ts +91 -0
- package/src/g2/defineScene2d.ts +381 -0
- package/src/g2/groups2d.ts +106 -0
- package/src/g2/loop.ts +50 -0
- package/src/g2/scenarios2d.ts +69 -0
- package/src/g2/touch.ts +83 -0
- package/src/gl/Camera.ts +160 -0
- package/src/gl/CameraPlace.ts +52 -0
- package/src/gl/CharacterController.ts +238 -0
- package/src/gl/Gearbox.ts +212 -0
- package/src/gl/Geometry.ts +279 -0
- package/src/gl/IK.ts +193 -0
- package/src/gl/InstancedMesh.ts +132 -0
- package/src/gl/Light.ts +135 -0
- package/src/gl/Lightmap.ts +402 -0
- package/src/gl/Locomotion.ts +498 -0
- package/src/gl/Material.ts +279 -0
- package/src/gl/Mesh.ts +83 -0
- package/src/gl/Model.ts +124 -0
- package/src/gl/NavAgent.ts +337 -0
- package/src/gl/NavMesh.ts +397 -0
- package/src/gl/Node.ts +350 -0
- package/src/gl/Noise.ts +30 -0
- package/src/gl/Particles.ts +676 -0
- package/src/gl/Physics.ts +222 -0
- package/src/gl/Plane.ts +53 -0
- package/src/gl/Ragdoll.ts +270 -0
- package/src/gl/Ray.ts +16 -0
- package/src/gl/Scene.ts +505 -0
- package/src/gl/Shape.ts +409 -0
- package/src/gl/Terrain.ts +1092 -0
- package/src/gl/Texture.ts +63 -0
- package/src/gl/Trigger.ts +45 -0
- package/src/gl/Vehicle.ts +473 -0
- package/src/gl/Wheel.ts +240 -0
- package/src/gl/animation/AnimationClip.ts +204 -0
- package/src/gl/animation/Animator.ts +329 -0
- package/src/gl/animation/Layer.ts +39 -0
- package/src/gl/animation/Loop.ts +31 -0
- package/src/gl/animation/Playback.ts +52 -0
- package/src/gl/animation/core.ts +670 -0
- package/src/gl/controls.ts +95 -0
- package/src/gl/physicsEvents.ts +20 -0
- package/src/gl/scenarios.ts +291 -0
- package/src/gl/state.ts +6 -0
- package/src/gl/terrainMesh.ts +219 -0
- package/src/gl/touch.ts +68 -0
- package/src/host.d.ts +61 -0
- package/src/inject.ts +203 -0
- package/src/math/Mathf.ts +118 -0
- package/src/math/mat4.ts +278 -0
- package/src/math/quat.ts +232 -0
- package/src/math/vec.ts +255 -0
- package/src/net/codec.ts +119 -0
- package/src/net/core.ts +384 -0
- package/src/net/index.ts +181 -0
- package/src/net/replication.ts +622 -0
- package/src/plugins/camera.ts +81 -0
- package/src/plugins/geolocation.ts +123 -0
- package/src/plugins/oauth.ts +61 -0
- package/src/plugins/permission.ts +7 -0
- package/src/plugins/push.ts +132 -0
- package/src/plugins/qr.ts +73 -0
- package/src/plugins/service.ts +47 -0
- package/src/runtime/app.ts +101 -0
- package/src/runtime/appEvents.ts +54 -0
- package/src/runtime/channel.ts +61 -0
- package/src/runtime/clipboard.ts +20 -0
- package/src/runtime/datetime.ts +329 -0
- package/src/runtime/device.ts +293 -0
- package/src/runtime/fetch.ts +77 -0
- package/src/runtime/files.ts +108 -0
- package/src/runtime/input.ts +175 -0
- package/src/runtime/media.ts +111 -0
- package/src/runtime/misc.ts +16 -0
- package/src/runtime/net.ts +36 -0
- package/src/runtime/rpc.ts +218 -0
- package/src/runtime/service.ts +83 -0
- package/src/runtime/share.ts +9 -0
- package/src/runtime/storage.ts +13 -0
- package/src/runtime/touch.ts +76 -0
- package/src/scene/defineScene.ts +1396 -0
- package/src/scene/editorPlugins.ts +110 -0
- package/src/scene/gizmos.ts +148 -0
- package/src/scene/grammar.ts +120 -0
- package/src/scene/level.ts +296 -0
- package/src/scene/material.ts +188 -0
- package/src/server/auth/appConfig.ts +12 -0
- package/src/server/auth/global.ts +80 -0
- package/src/server/auth/host.ts +318 -0
- package/src/server/auth/models.ts +83 -0
- package/src/server/auth/types.ts +50 -0
- package/src/server/channel.ts +56 -0
- package/src/server/context.ts +36 -0
- package/src/server/db/defineDb.ts +237 -0
- package/src/server/db/fields.ts +132 -0
- package/src/server/db/httpTransport.ts +93 -0
- package/src/server/db/index.ts +7 -0
- package/src/server/db/marci/query.ts +412 -0
- package/src/server/db/types.ts +202 -0
- package/src/server/errors.ts +12 -0
- package/src/server/host.ts +74 -0
- package/src/server/inject.ts +13 -0
- package/src/server/runtime.ts +133 -0
- package/src/server/validate.ts +87 -0
- package/src/ui/NativeView.ts +142 -0
- package/src/ui/UI.ts +39 -0
- package/src/ui/UIBottomSheet.ts +139 -0
- package/src/ui/UIButton.ts +101 -0
- package/src/ui/UIContainer.ts +60 -0
- package/src/ui/UIImage.ts +83 -0
- package/src/ui/UIInput.ts +185 -0
- package/src/ui/UIModal.ts +139 -0
- package/src/ui/UINode.ts +830 -0
- package/src/ui/UIPager.ts +362 -0
- package/src/ui/UIPopover.ts +100 -0
- package/src/ui/UIScreen.ts +123 -0
- package/src/ui/UIScrollable.ts +87 -0
- package/src/ui/UISpacer.ts +14 -0
- package/src/ui/UITabs.ts +236 -0
- package/src/ui/UIText.ts +51 -0
- package/src/ui/UIVideo.ts +88 -0
- package/src/ui/UIVirtualizedList.ts +241 -0
- package/src/ui/UIWidget.ts +127 -0
- package/src/ui/fonts.ts +13 -0
- package/src/ui/presentable.ts +117 -0
- package/src/ui/router.ts +132 -0
- package/src/ui/theme.ts +84 -0
package/src/gl/Wheel.ts
ADDED
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
// One wheel of a `Vehicle`, as an aspect on the wheel's own node. The node's position IS the wheel's
|
|
2
|
+
// position (where its centre rests on flat ground), and everything else about it lives here too:
|
|
3
|
+
//
|
|
4
|
+
// const car = new Node('car')
|
|
5
|
+
// .add(body, wheelFL.aspect(Wheel), wheelFR.aspect(Wheel),
|
|
6
|
+
// wheelRL.aspect(Wheel), wheelRR.aspect(Wheel))
|
|
7
|
+
// .aspect(Shape, { box: [0.9, 0.4, 2.1] })
|
|
8
|
+
// .aspect(Vehicle, { mass: 1200 })
|
|
9
|
+
//
|
|
10
|
+
// That is the whole minimum: `Vehicle` collects every `Wheel` under itself, groups them into axles by
|
|
11
|
+
// Z (front first), and the defaults fall out — the front axle steers, the rear one drives and carries
|
|
12
|
+
// the handbrake. Override any of it per wheel.
|
|
13
|
+
//
|
|
14
|
+
// This aspect is also what POSES the wheel: it runs in the LATE phase (after the physics step, before
|
|
15
|
+
// the frame draws) and writes the node's transform from the suspension length, steer angle and spin
|
|
16
|
+
// the engine reports. Nothing else writes that transform — the engine deliberately does not, so a
|
|
17
|
+
// wheel model's own conventions (which way its axle points, how big it looks) are settings here
|
|
18
|
+
// rather than constants in C++, and a part that should steer but NOT spin is simply a sibling node.
|
|
19
|
+
//
|
|
20
|
+
// Chassis space is forward −Z / up +Y, the SDK's own convention (`node.forward`).
|
|
21
|
+
|
|
22
|
+
import { Aspect } from "../core/Aspect"
|
|
23
|
+
import type { FieldMeta } from "../core/fields"
|
|
24
|
+
import { Mat4 } from "../math/mat4"
|
|
25
|
+
import { Quat } from "../math/quat"
|
|
26
|
+
import { Vec3, cx, cy, cz, type Vec3Like } from "../math/vec"
|
|
27
|
+
import { Gizmos } from "../scene/gizmos"
|
|
28
|
+
import { Node } from "./Node"
|
|
29
|
+
import { Shape } from "./Shape"
|
|
30
|
+
import type { Vehicle } from "./Vehicle"
|
|
31
|
+
|
|
32
|
+
/** Floats `vehicleGetState` writes before the per-wheel block: speed, rpm, contacts, vx, vy, vz. */
|
|
33
|
+
export const STATE_HEADER = 6
|
|
34
|
+
/** Floats per wheel: contact, slip, slipAngle, suspensionLength, steerDeg, spin. */
|
|
35
|
+
export const WHEEL_STATE = 6
|
|
36
|
+
|
|
37
|
+
/** How a tire behaves past its grip peak. `road` is Jolt's own curve — grip falls off after the peak,
|
|
38
|
+
* so the car can be made to let go. `arcade` holds the peak all the way out: much harder to spin. */
|
|
39
|
+
export type TireCurve = "road" | "arcade"
|
|
40
|
+
|
|
41
|
+
export interface WheelSuspension {
|
|
42
|
+
/** Total travel in metres, full droop to fully compressed (default 0.4). */
|
|
43
|
+
travel?: number
|
|
44
|
+
/** Spring frequency in Hz — bigger is stiffer (default 1.4; a race car ~2.5, a truck ~1). It also
|
|
45
|
+
* sets the ride-height sag, and the sag is how far the wheel can still extend DOWN. */
|
|
46
|
+
stiffness?: number
|
|
47
|
+
/** Damping ratio 0..1 (default 0.5). Low values pogo. */
|
|
48
|
+
damping?: number
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export interface WheelTire {
|
|
52
|
+
/** Grip multiplier on the friction curves (default 1). The grip a wheel actually gets is combined
|
|
53
|
+
* with the ground it stands on as `sqrt(tire × groundFriction)`, and a body's friction defaults to
|
|
54
|
+
* 0.6 — so a road a car should really corner on wants `Physics { friction: 1 }`. */
|
|
55
|
+
grip?: number
|
|
56
|
+
/** Which friction curve (default `road`). */
|
|
57
|
+
curve?: TireCurve
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export interface WheelVisual {
|
|
61
|
+
/** The MODEL's axle direction in its own space (default `[1, 0, 0]`). Set it instead of parenting
|
|
62
|
+
* the mesh under a corrective node when the art points another way. */
|
|
63
|
+
axis?: Vec3Like
|
|
64
|
+
/** Drawn radius, if the art is not exactly the physical `radius` — sinks a wheel into its arch or
|
|
65
|
+
* lets a fat tire overhang, with no effect on the simulation. Default: the physical radius. */
|
|
66
|
+
radius?: number
|
|
67
|
+
/** Let the model spin (default true). `false` keeps the steer angle and suspension travel but not
|
|
68
|
+
* the rotation — a brake caliper. */
|
|
69
|
+
spin?: boolean
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const DEG2RAD = Math.PI / 180
|
|
73
|
+
const UP = new Vec3(0, 1, 0)
|
|
74
|
+
|
|
75
|
+
export class Wheel extends Aspect<"wheel", Node> {
|
|
76
|
+
static readonly aspect = "wheel"
|
|
77
|
+
|
|
78
|
+
/** Wheel radius in metres. Default: measured from the node's rendered bounds, else 0.35. Too small
|
|
79
|
+
* and the car scrapes, too large and it floats. */
|
|
80
|
+
radius?: number
|
|
81
|
+
/** Wheel width in metres. Default: measured, else 0.25. */
|
|
82
|
+
width?: number
|
|
83
|
+
/** Does this wheel steer? Default: the wheels on the front axle. */
|
|
84
|
+
steer?: boolean
|
|
85
|
+
/** Does the engine drive it? Default: from the vehicle's `drive` layout. */
|
|
86
|
+
drive?: boolean
|
|
87
|
+
/** Foot brake on this wheel. */
|
|
88
|
+
brake = true
|
|
89
|
+
/** Handbrake on this wheel. Default: every non-steering wheel (locking the front wheels of a moving
|
|
90
|
+
* car is not a handbrake turn, it is a crash). */
|
|
91
|
+
handbrake?: boolean
|
|
92
|
+
/** Axle group, front to back. Default: derived from Z. Set it for a car whose wheels are not in neat
|
|
93
|
+
* left/right pairs — an axle's wheels are paired for the differential and the anti-roll bar. */
|
|
94
|
+
axle?: number
|
|
95
|
+
/** Foot-brake torque in N·m (default 1500); the handbrake pulls 3× on the wheels that have it. */
|
|
96
|
+
brakeTorque = 1500
|
|
97
|
+
|
|
98
|
+
suspension: WheelSuspension = { travel: 0.4, stiffness: 1.4, damping: 0.5 }
|
|
99
|
+
tire: WheelTire = { grip: 1, curve: "road" }
|
|
100
|
+
visual: WheelVisual = {}
|
|
101
|
+
|
|
102
|
+
/** Scene-editor inspector. `steer` / `drive` / `handbrake` are deliberately absent: they are
|
|
103
|
+
* tri-state (undefined = derive it from the axle), which a switch cannot express. */
|
|
104
|
+
static fields: FieldMeta<Wheel> = {
|
|
105
|
+
radius: { min: 0.05, step: 0.01 },
|
|
106
|
+
width: { min: 0.02, step: 0.01 },
|
|
107
|
+
axle: { min: 0, step: 1 },
|
|
108
|
+
brake: { label: "Foot brake" },
|
|
109
|
+
brakeTorque: { label: "Brake torque", min: 0, step: 50 },
|
|
110
|
+
suspension: { hidden: true },
|
|
111
|
+
tire: { hidden: true },
|
|
112
|
+
visual: { hidden: true },
|
|
113
|
+
}
|
|
114
|
+
static editor = { rebuild: true }
|
|
115
|
+
|
|
116
|
+
/** @internal Set by `Vehicle` at attach — the car this wheel belongs to, and its index in the blob. */
|
|
117
|
+
_vehicle: Vehicle | null = null
|
|
118
|
+
/** @internal */
|
|
119
|
+
_index = -1
|
|
120
|
+
/** @internal Suspension attachment point in chassis space; the wheel hangs below it. */
|
|
121
|
+
_attach = new Vec3()
|
|
122
|
+
/** @internal Resting centre in chassis space (what the author placed). */
|
|
123
|
+
_rest = new Vec3()
|
|
124
|
+
|
|
125
|
+
private _angle = 0 // integrated spin, radians — the engine reports rate, not angle
|
|
126
|
+
private _scale = new Vec3(1, 1, 1) // the node's own scale, preserved through the pose write
|
|
127
|
+
private _local = true // is the node a direct child of the chassis?
|
|
128
|
+
|
|
129
|
+
onAttach(): void {
|
|
130
|
+
this._scale = this.node.scale
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
onDetach(): void {
|
|
134
|
+
this._vehicle = null
|
|
135
|
+
this._index = -1
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/** @internal Called by Vehicle once the car exists, with the geometry it resolved. */
|
|
139
|
+
_bind(vehicle: Vehicle, index: number, rest: Vec3, attach: Vec3): void {
|
|
140
|
+
this._vehicle = vehicle
|
|
141
|
+
this._index = index
|
|
142
|
+
this._rest = rest
|
|
143
|
+
this._attach = attach
|
|
144
|
+
this._scale = this.node.scale
|
|
145
|
+
this._local = this.node.parent === vehicle.node
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// ---- live state ------------------------------------------------------------------------------
|
|
149
|
+
// Every getter reads the car's ONE shared state buffer, which is refreshed once per frame.
|
|
150
|
+
|
|
151
|
+
private _state(k: number): number {
|
|
152
|
+
const v = this._vehicle
|
|
153
|
+
if (!v || this._index < 0) return 0
|
|
154
|
+
return v._wheelState(this._index, k)
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/** Touching the ground this step. */
|
|
158
|
+
get contact(): boolean { return this._state(0) !== 0 }
|
|
159
|
+
/** Longitudinal slip RATIO: 0 = rolling in sync with the ground, ~1 = spinning up or locked.
|
|
160
|
+
* Unsigned — smoke, tire marks and wheelspin sound key off this. */
|
|
161
|
+
get slip(): number { return this._state(1) }
|
|
162
|
+
/** Slip ANGLE in degrees: how far the tire's travel direction is off from where it points.
|
|
163
|
+
* Unsigned, 0..90 — past the curve's peak (~3°) the tire is sliding sideways. Drift particles. */
|
|
164
|
+
get slipAngle(): number { return this._state(2) }
|
|
165
|
+
/** Steer angle in degrees, positive to the right. */
|
|
166
|
+
get steerAngle(): number { return this._state(4) }
|
|
167
|
+
/** Wheel spin in rad/s (× radius ≈ the speed the tire is laying down). */
|
|
168
|
+
get spin(): number { return this._state(5) }
|
|
169
|
+
/** Suspension compression, 0 = fully extended (drooping) … 1 = fully compressed. */
|
|
170
|
+
get compression(): number {
|
|
171
|
+
const travel = this.suspension.travel ?? 0.4
|
|
172
|
+
if (travel <= 0) return 0
|
|
173
|
+
const len = this._state(3)
|
|
174
|
+
const min = travel * 0.4
|
|
175
|
+
return Math.max(0, Math.min(1, (travel - len) / (travel - min)))
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// ---- the visual ------------------------------------------------------------------------------
|
|
179
|
+
|
|
180
|
+
/** LATE phase: after the step and the transform sync, right before the frame draws. */
|
|
181
|
+
update(dt: number): void {
|
|
182
|
+
const v = this._vehicle
|
|
183
|
+
if (!v || this._index < 0 || !v.id) return
|
|
184
|
+
|
|
185
|
+
if (this.visual.spin !== false) this._angle += this._state(5) * dt
|
|
186
|
+
|
|
187
|
+
// Down the suspension from the attachment point by however far it currently hangs.
|
|
188
|
+
const length = this._state(3)
|
|
189
|
+
const centre = new Vec3(this._attach.x, this._attach.y - length, this._attach.z)
|
|
190
|
+
|
|
191
|
+
// Steer about the chassis' up axis, then spin about the MODEL's own axle in the steered frame.
|
|
192
|
+
// Both reports are negated, for the same reason: the engine describes them in the DRIVER's terms
|
|
193
|
+
// and the scene is right-handed with forward on −Z. Steer is reported right-positive while a yaw
|
|
194
|
+
// about +Y turns left. Spin is reported positive-means-forward, but a positive turn about the
|
|
195
|
+
// right-hand axle carries the top of the wheel to +Z — which rolls the car backwards.
|
|
196
|
+
const steer = Quat.fromAxisAngle(UP, -this._state(4) * DEG2RAD)
|
|
197
|
+
const axis = this.visual.axis ?? [1, 0, 0]
|
|
198
|
+
const rotation = this.visual.spin === false ? steer : steer.mul(Quat.fromAxisAngle(axis, -this._angle))
|
|
199
|
+
|
|
200
|
+
// A model that draws bigger or smaller than it collides scales on top of the node's own scale.
|
|
201
|
+
const vr = this.visual.radius
|
|
202
|
+
const k = vr !== undefined && this.radius ? vr / this.radius : 1
|
|
203
|
+
const scale = k === 1 ? this._scale : new Vec3(this._scale.x * k, this._scale.y * k, this._scale.z * k)
|
|
204
|
+
|
|
205
|
+
const m = Mat4.compose(centre, rotation, scale)
|
|
206
|
+
// The common case is a direct child of the chassis, where chassis space IS the local space.
|
|
207
|
+
if (this._local) this.node.matrix = m
|
|
208
|
+
else this.node.worldMatrix = new Mat4(v.node.worldMatrix).mul(m)
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/** Edit mode: this wheel's own circle at its resting height, its travel range, and a mark when the
|
|
212
|
+
* engine drives it — enough to place a wheel without running the game. */
|
|
213
|
+
rebuild(): void {
|
|
214
|
+
const travel = this.suspension.travel ?? 0.4
|
|
215
|
+
const r = this.radius ?? this._measure().radius
|
|
216
|
+
const world = new Mat4(this.node.worldMatrix)
|
|
217
|
+
const P = (x: number, y: number, z: number): Vec3 => world.transformPoint([ x, y, z ])
|
|
218
|
+
const ring: Vec3[] = []
|
|
219
|
+
for (let i = 0; i < 24; i++) {
|
|
220
|
+
const t = (i / 24) * Math.PI * 2
|
|
221
|
+
ring.push(P(0, Math.sin(t) * r, Math.cos(t) * r))
|
|
222
|
+
}
|
|
223
|
+
Gizmos.polyline(ring, { color: this.steer === false ? "#9aa3b2" : "#5b8ef0", closed: true })
|
|
224
|
+
Gizmos.line(P(0, travel * 0.6, 0), P(0, -travel * 0.4, 0), { color: "#59606d" })
|
|
225
|
+
if (this.drive) Gizmos.cross(P(0, 0, 0), r * 0.35, { color: "#e0b25a" })
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/** @internal Radius/width from the node's rendered bounds, so a placed model needs no numbers. */
|
|
229
|
+
_measure(): { radius: number, width: number } {
|
|
230
|
+
const b = Shape.boundsOf(this.node)
|
|
231
|
+
if (!b) return { radius: 0.35, width: 0.25 }
|
|
232
|
+
const size = new Vec3(cx(b.max) - cx(b.min), cy(b.max) - cy(b.min), cz(b.max) - cz(b.min))
|
|
233
|
+
// A wheel is a disc: its diameter is the larger of the two axes across the axle, and the width is
|
|
234
|
+
// the axle direction itself.
|
|
235
|
+
const axis = this.visual.axis ?? [1, 0, 0]
|
|
236
|
+
const along = Math.abs(cx(axis)) >= Math.abs(cz(axis)) ? size.x : size.z
|
|
237
|
+
const across = Math.max(Math.abs(cx(axis)) >= Math.abs(cz(axis)) ? size.z : size.x, size.y)
|
|
238
|
+
return { radius: across > 0 ? across * 0.5 : 0.35, width: along > 0 ? along : 0.25 }
|
|
239
|
+
}
|
|
240
|
+
}
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
// AnimationClip — the unit of the animation system: immutable data (name, duration, tracks that
|
|
2
|
+
// target bone NAMES, not entities). One representation for a GLB's embedded clips (model.anim.clips),
|
|
3
|
+
// clips loaded from other files (Mixamo: one GLB per animation) and procedural clips built from
|
|
4
|
+
// curves. An Animator binds clips to a skeleton by name. Clip EVENTS (`addEvent`) live here too —
|
|
5
|
+
// a fact about the clip's timeline, shared wherever it plays. See docs/animator-plan.md.
|
|
6
|
+
//
|
|
7
|
+
// const [hero, idle, run] = await Promise.all([
|
|
8
|
+
// Model.load(asset('./hero.glb')),
|
|
9
|
+
// AnimationClip.load(asset('./idle.glb')), // the file's only/first clip
|
|
10
|
+
// AnimationClip.load(asset('./run.glb')),
|
|
11
|
+
// ])
|
|
12
|
+
// const slash = await AnimationClip.load(asset('./attacks.glb'), 'Slash') // one out of many
|
|
13
|
+
// const bob = AnimationClip.from({ tracks: { Hips: { position: [[0, [0,0,0]], [1, [0,0.05,0]]] } } })
|
|
14
|
+
|
|
15
|
+
import { fetch, type FetchResponse } from "../../runtime/fetch"
|
|
16
|
+
import type { Vec3Like } from "../../math/vec"
|
|
17
|
+
import type { QuatLike } from "../../math/quat"
|
|
18
|
+
|
|
19
|
+
/** A keyframe: `[time (s), value]`. */
|
|
20
|
+
export type ClipKey<V> = [number, V]
|
|
21
|
+
export type ClipInterpolation = "linear" | "step"
|
|
22
|
+
|
|
23
|
+
/** Curves for one target node (bone name), any subset of the three channels. */
|
|
24
|
+
export type ClipTrackDef = {
|
|
25
|
+
position?: ClipKey<Vec3Like | [number, number, number]>[]
|
|
26
|
+
rotation?: ClipKey<QuatLike | [number, number, number, number]>[]
|
|
27
|
+
scale?: ClipKey<Vec3Like | [number, number, number]>[]
|
|
28
|
+
interpolation?: ClipInterpolation
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export type ClipDef = {
|
|
32
|
+
/** Clip length in seconds — defaults to the last key time across all tracks. */
|
|
33
|
+
duration?: number
|
|
34
|
+
/** Target node name → its curves. Names bind to bones (or any child node) at Animator attach. */
|
|
35
|
+
tracks: Record<string, ClipTrackDef>
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export type ClipInfo = { name: string, duration: number, trackCount: number }
|
|
39
|
+
|
|
40
|
+
const PATH = { position: 0, rotation: 1, scale: 2 } as const
|
|
41
|
+
const INTERP = { linear: 0, step: 1 } as const
|
|
42
|
+
|
|
43
|
+
const setCache = new Map<number, AnimationClip[]>()
|
|
44
|
+
|
|
45
|
+
const readInfo = (setId: number): ClipInfo[] =>
|
|
46
|
+
((_creator.getClipSetInfo(setId) ?? []) as ClipInfo[]).map((c) => ({ name: c.name, duration: c.duration, trackCount: c.trackCount ?? 0 }))
|
|
47
|
+
|
|
48
|
+
const readValue = (v: unknown, comps: number, out: number[]): void => {
|
|
49
|
+
if (Array.isArray(v)) { for (let i = 0; i < comps; i++) out.push(Number(v[i] ?? (i === 3 ? 1 : 0))); return }
|
|
50
|
+
const o = v as Record<string, number>
|
|
51
|
+
out.push(o.x ?? 0, o.y ?? 0, o.z ?? 0)
|
|
52
|
+
if (comps === 4) out.push(o.w ?? 1)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export class AnimationClip {
|
|
56
|
+
/** Source name (the clip's name inside its file; `"clip"` for procedural clips). Informational —
|
|
57
|
+
* an Animator addresses clips by the key YOU give it. */
|
|
58
|
+
readonly name: string
|
|
59
|
+
/** Length in seconds. */
|
|
60
|
+
readonly duration: number
|
|
61
|
+
/** Number of animated (node, channel) tracks. */
|
|
62
|
+
readonly trackCount: number
|
|
63
|
+
/** @internal native clip set + index inside it. */
|
|
64
|
+
readonly _set: number
|
|
65
|
+
/** @internal */
|
|
66
|
+
readonly _index: number
|
|
67
|
+
/** @internal events: seconds + name, sorted (the engine gets them normalized via setClipEvents). */
|
|
68
|
+
_events: { t: number, name: string }[] = []
|
|
69
|
+
|
|
70
|
+
private constructor(set: number, index: number, info: ClipInfo) {
|
|
71
|
+
this._set = set
|
|
72
|
+
this._index = index
|
|
73
|
+
this.name = info.name
|
|
74
|
+
this.duration = info.duration
|
|
75
|
+
this.trackCount = info.trackCount
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** @internal — a placeholder for a Playback that never started (unknown clip). */
|
|
79
|
+
static _none(): AnimationClip { return new AnimationClip(0, 0, { name: "", duration: 0, trackCount: 0 }) }
|
|
80
|
+
|
|
81
|
+
/** @internal — wrap the clips of an existing native set (a GLB's embedded clips). Cached per set
|
|
82
|
+
* so every model / clone sharing the set gets the SAME objects (events added on one apply to all —
|
|
83
|
+
* as they do in the engine). */
|
|
84
|
+
static _ofSet(setId: number): AnimationClip[] {
|
|
85
|
+
if (!setId) return []
|
|
86
|
+
let clips = setCache.get(setId)
|
|
87
|
+
if (!clips) { clips = readInfo(setId).map((info, i) => new AnimationClip(setId, i, info)); setCache.set(setId, clips) }
|
|
88
|
+
return clips
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/** Mark a moment of the clip (SECONDS from its start) with an event name: `kick.addEvent(0.4, 'hit')`
|
|
92
|
+
* → `anim.on('hit', (clip, layer) => …)` fires when the playhead crosses it, loops included.
|
|
93
|
+
* Events are part of the clip: every model playing it gets them; `slice()` keeps the ones inside
|
|
94
|
+
* the range, re-timed. Chainable. */
|
|
95
|
+
addEvent(time: number, name: string): this {
|
|
96
|
+
this._events.push({ t: Math.min(Math.max(0, time), this.duration), name })
|
|
97
|
+
this._events.sort((a, b) => a.t - b.t)
|
|
98
|
+
this._pushEvents()
|
|
99
|
+
return this
|
|
100
|
+
}
|
|
101
|
+
private _pushEvents(): void {
|
|
102
|
+
const d = this.duration
|
|
103
|
+
_creator.setClipEvents(this._set, this._index, new Float32Array(this._events.map((e) => (d > 0 ? e.t / d : 0))))
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/** Load ONE clip from a GLB: the file's only/first clip, or the one named / at the given index. */
|
|
107
|
+
static load(source: string | FetchResponse, clip?: string | number): Promise<AnimationClip> {
|
|
108
|
+
return AnimationClip._loadSet(source).then(([setId, infos]) => {
|
|
109
|
+
let index = 0
|
|
110
|
+
if (typeof clip === "number") index = clip
|
|
111
|
+
else if (typeof clip === "string") index = infos.findIndex((c) => c.name === clip)
|
|
112
|
+
if (index < 0 || index >= infos.length) {
|
|
113
|
+
_creator.destroyClipSet(setId)
|
|
114
|
+
throw new Error(`AnimationClip.load: no clip ${JSON.stringify(clip)} in ${AnimationClip._label(source)} — clips: ${infos.map((c) => c.name).join(", ") || "(none)"}`)
|
|
115
|
+
}
|
|
116
|
+
return new AnimationClip(setId, index, infos[index])
|
|
117
|
+
})
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/** Load EVERY clip of a GLB (library files), keyed by clip name. */
|
|
121
|
+
static loadAll(source: string | FetchResponse): Promise<Record<string, AnimationClip>> {
|
|
122
|
+
return AnimationClip._loadSet(source).then(([setId, infos]) => {
|
|
123
|
+
const out: Record<string, AnimationClip> = {}
|
|
124
|
+
infos.forEach((info, i) => { out[info.name] = new AnimationClip(setId, i, info) })
|
|
125
|
+
return out
|
|
126
|
+
})
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/** Build a clip from curves in code — no DCC needed. Keys are `[time, value]`; a track binds to the
|
|
130
|
+
* node of that name when the clip is used by an Animator (bones, or any child node). */
|
|
131
|
+
static from(def: ClipDef): AnimationClip {
|
|
132
|
+
const names: string[] = []
|
|
133
|
+
const data: number[] = [0, 0]
|
|
134
|
+
let trackCount = 0
|
|
135
|
+
let duration = 0
|
|
136
|
+
for (const [target, track] of Object.entries(def.tracks)) {
|
|
137
|
+
const interp = INTERP[track.interpolation ?? "linear"] ?? 0
|
|
138
|
+
const emit = (path: 0 | 1 | 2, comps: 3 | 4, keys: ClipKey<unknown>[] | undefined) => {
|
|
139
|
+
if (!keys || keys.length === 0) return
|
|
140
|
+
const sorted = keys.slice().sort((a, b) => a[0] - b[0])
|
|
141
|
+
names.push(target)
|
|
142
|
+
data.push(path, interp, comps, sorted.length)
|
|
143
|
+
for (const k of sorted) data.push(k[0])
|
|
144
|
+
for (const k of sorted) readValue(k[1], comps, data)
|
|
145
|
+
duration = Math.max(duration, sorted[sorted.length - 1][0])
|
|
146
|
+
trackCount++
|
|
147
|
+
}
|
|
148
|
+
emit(PATH.position, 3, track.position)
|
|
149
|
+
emit(PATH.rotation, 4, track.rotation)
|
|
150
|
+
emit(PATH.scale, 3, track.scale)
|
|
151
|
+
}
|
|
152
|
+
data[0] = trackCount
|
|
153
|
+
if (def.duration !== undefined) duration = def.duration
|
|
154
|
+
data[1] = duration
|
|
155
|
+
const setId = _creator.createClipFromTracks(names.join("\n"), new Float32Array(data))
|
|
156
|
+
if (setId === 0) throw new Error("AnimationClip.from: invalid track data")
|
|
157
|
+
const info = readInfo(setId)[0] ?? { name: "clip", duration, trackCount }
|
|
158
|
+
return new AnimationClip(setId, 0, info)
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/** A sub-range of this clip as a NEW clip — `[start, end]` seconds of the source, re-timed to 0
|
|
162
|
+
* (`end` defaults to the clip's end; array-slice semantics). Cut a too-long take down to the
|
|
163
|
+
* action, or carve several sub-clips out of one packed timeline:
|
|
164
|
+
*
|
|
165
|
+
* clips: { Kick: kick.slice(0.2, 1.1) }
|
|
166
|
+
*
|
|
167
|
+
* Keys outside the range are dropped and exact boundary values are interpolated in, so the
|
|
168
|
+
* sliced clip starts and ends precisely on the source's pose at the cut points. Everything
|
|
169
|
+
* downstream (blend spaces, events' 0–1 times, root motion, phase sync) sees a normal clip of
|
|
170
|
+
* the new duration. */
|
|
171
|
+
slice(start: number, end?: number): AnimationClip {
|
|
172
|
+
const e = Math.min(end ?? this.duration, this.duration)
|
|
173
|
+
const st = Math.max(0, start)
|
|
174
|
+
const setId = _creator.sliceClip(this._set, this._index, st, e)
|
|
175
|
+
if (setId === 0) throw new Error(`AnimationClip.slice: bad range ${start}${end !== undefined ? `–${end}` : ""} of '${this.name}' (${this.duration.toFixed(2)}s)`)
|
|
176
|
+
const info = readInfo(setId)[0] ?? { name: this.name, duration: e - st, trackCount: this.trackCount }
|
|
177
|
+
const out = new AnimationClip(setId, 0, info)
|
|
178
|
+
// the engine re-timed the event TIMES; carry the names the same way
|
|
179
|
+
out._events = this._events.filter((ev) => ev.t >= st - 1e-6 && ev.t <= e + 1e-6).map((ev) => ({ t: Math.min(Math.max(0, ev.t - st), e - st), name: ev.name }))
|
|
180
|
+
return out
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
private static _label(source: string | FetchResponse): string {
|
|
184
|
+
return typeof source === "string" ? source : "response"
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
private static _loadSet(source: string | FetchResponse): Promise<[number, ClipInfo[]]> {
|
|
188
|
+
if (typeof _creator.loadClips !== "function") {
|
|
189
|
+
return Promise.reject(new Error("AnimationClip.load: this host has no animation-clip support yet"))
|
|
190
|
+
}
|
|
191
|
+
if (typeof source === "string") {
|
|
192
|
+
return fetch(source, { useOnce: true }).then((resp) => {
|
|
193
|
+
if (resp.status >= 400) throw new Error(`AnimationClip.load: failed to load ${source}. HTTP ${resp.status}`)
|
|
194
|
+
return AnimationClip._loadSet(resp)
|
|
195
|
+
})
|
|
196
|
+
}
|
|
197
|
+
return new Promise<[number, ClipInfo[]]>((resolve, reject) => {
|
|
198
|
+
_creator.loadClips((source as unknown as { _id: number })._id, (setId: number) => {
|
|
199
|
+
if (setId === 0) { reject(new Error("AnimationClip.load: the file has no animations (or is not a GLB)")); return }
|
|
200
|
+
resolve([setId, readInfo(setId)])
|
|
201
|
+
}, reject)
|
|
202
|
+
})
|
|
203
|
+
}
|
|
204
|
+
}
|