1
0

feat: build articulated crow flight v2

This commit is contained in:
2026-08-19 01:12:08 -07:00
parent 747744fa6c
commit 94de2d8bee
10 changed files with 789 additions and 106 deletions
+17 -19
View File
@@ -14,7 +14,7 @@ import {
disposeCrow,
disposeDog,
disposeHumanoid,
poseCrowFlight,
poseCrow,
poseDogAttention,
poseDogWalk,
poseHumanoid,
@@ -115,7 +115,6 @@ function buildActor(kind: ActorKind, identity: Readonly<ActorIdentity>): RiggedA
kind,
rig: buildCrow({
...(color(appearance?.primaryColor) === undefined ? {} : { featherColor: color(appearance?.primaryColor) }),
...(color(appearance?.accentColor) === undefined ? {} : { sheenColor: color(appearance?.accentColor) }),
}),
};
}
@@ -209,7 +208,11 @@ export function createSceneActor(options: SceneActorOptions): SceneActor {
origin.z + state.z * scale,
);
root.rotation.order = "YXZ";
root.rotation.set(state.mode === "flight" ? state.pitch : 0, state.yaw, 0);
root.rotation.set(
state.mode === "flight" ? state.pitch : 0,
state.yaw,
state.mode === "flight" && state.kind === "crow" ? -state.roll * 0.42 : 0,
);
view.position.copy(root.position);
if (actor.kind === "humanoid") {
poseHumanoid(actor.rig, { walkPhase: state.posePhase, stride: state.poseAmount * 0.68 });
@@ -217,22 +220,17 @@ export function createSceneActor(options: SceneActorOptions): SceneActor {
poseDogWalk(actor.rig, state.posePhase, state.poseAmount * 0.68);
poseDogAttention(actor.rig, 0, state.posePhase * 0.7);
} else if (state.mode === "flight") {
// A possessed crow is airborne even before the first key arrives. Keep a
// readable soaring silhouette at neutral input; the controller still
// owns phase/intensity as soon as movement begins.
const idleSoar = enabled && state.poseAmount < 1e-4;
poseCrowFlight(
actor.rig,
idleSoar ? -Math.PI / 2 : state.posePhase,
Math.max(state.poseAmount, idleSoar ? 0.58 : 0),
);
if (idleSoar) {
// The authored wing sheet already extends along local X. A neutral
// chase view needs that broad plan silhouette, not the edge-on middle
// of a flap cycle.
actor.rig.joints.wingLeft.rotation.set(-0.08, 0, 0.08);
actor.rig.joints.wingRight.rotation.set(-0.08, 0, -0.08);
}
// The controller names the aerodynamic state; the rig owns how that
// state bends shoulder, elbow, wrist, tail and feet.
poseCrow(actor.rig, {
state: state.crowPose,
phase: state.posePhase,
amount: Math.max(state.poseAmount, enabled ? 0.68 : 0.5),
bank: THREE.MathUtils.clamp(state.roll / 0.62, -1, 1),
flight: 1,
});
} else {
poseCrow(actor.rig, { state: "perch", amount: 1, flight: 0 });
}
}