1
0

feat(actors): build articulated dog v2

This commit is contained in:
2026-08-19 03:07:40 -07:00
parent 264daaab61
commit 20deae2a0f
9 changed files with 615 additions and 104 deletions
+17 -6
View File
@@ -11,12 +11,12 @@ import {
buildCrow,
buildDog,
buildHumanoid,
dogPoseStateForSpeed,
disposeCrow,
disposeDog,
disposeHumanoid,
poseCrow,
poseDogAttention,
poseDogWalk,
poseDog,
poseHumanoid,
type CrowRig,
type DogRig,
@@ -200,7 +200,7 @@ export function createSceneActor(options: SceneActorOptions): SceneActor {
let faceTexture: THREE.Texture | null = null;
let disposed = false;
function sync(): void {
function sync(elapsedSeconds = 0, previousSpeedMps?: number): void {
const state = controller.state();
root.position.set(
origin.x + state.x * scale,
@@ -217,8 +217,18 @@ export function createSceneActor(options: SceneActorOptions): SceneActor {
if (actor.kind === "humanoid") {
poseHumanoid(actor.rig, { walkPhase: state.posePhase, stride: state.poseAmount * 0.68 });
} else if (actor.kind === "dog") {
poseDogWalk(actor.rig, state.posePhase, state.poseAmount * 0.68);
poseDogAttention(actor.rig, 0, state.posePhase * 0.7);
const dogState = dogPoseStateForSpeed(state.speedMps);
const acceleration = elapsedSeconds > 0 && previousSpeedMps !== undefined
? THREE.MathUtils.clamp((state.speedMps - previousSpeedMps) / elapsedSeconds / 5, -1, 1)
: 0;
poseDog(actor.rig, {
state: dogState,
phase: state.posePhase,
amount: dogState === "idle" ? 1 : THREE.MathUtils.clamp(0.38 + state.poseAmount * 0.62, 0, 1),
turn: desired.turn,
acceleration,
attentionYaw: -desired.turn * 0.08,
});
} else if (state.mode === "flight") {
// The controller names the aerodynamic state; the rig owns how that
// state bends shoulder, elbow, wrist, tail and feet.
@@ -267,9 +277,10 @@ export function createSceneActor(options: SceneActorOptions): SceneActor {
tick(elapsedSeconds) {
if (!disposed && enabled) {
const beforeKind = controller.state().kind;
const previousSpeedMps = controller.state().speedMps;
controller.tick(elapsedSeconds, desired);
if (controller.state().kind !== beforeKind) replaceActor(controller.state().kind);
else sync();
else sync(Number.isFinite(elapsedSeconds) ? Math.max(0, elapsedSeconds) : 0, previousSpeedMps);
clearEdges();
}
return controller.snapshot();