fix(canvas): 导演台构建 - 修复角色与参考视频类型错误

This commit is contained in:
ddCat
2026-08-05 23:19:16 +08:00
parent 4e5f255a4c
commit 706fd21a31
4 changed files with 8 additions and 32 deletions

View File

@@ -2,6 +2,8 @@
## Unreleased
- 修复导演台角色、动画清理、骨骼控制与白膜参考视频类型错误导致的生产构建失败。
## v1.0.39
- 创作任务支持页面切换后继续后台执行和及时停止,并保证任务结果回填与素材入库幂等。

View File

@@ -183,7 +183,6 @@ function DirectorObjectView({ object, selected, selectedBone, transformMode, pla
function DirectorObjectVisual({ object, selected, selectedBone, playhead, onSelectBone, onBoneTransform, onActorRigReady }: { object: DirectorObject; selected: boolean; selectedBone: string | null; playhead: number; onSelectBone: (bone: string | null) => void; onBoneTransform: (bone: string, rotation: DirectorQuat) => void; onActorRigReady: (rig: DirectorRig, animations: AnimationClip[]) => void }) {
if ((object.kind === "model" || object.kind === "actor" || object.primitive === "character") && (object.url || object.primitive === "character")) return <DirectorModel object={object} selected={selected} selectedBone={selectedBone} playhead={playhead} onSelectBone={onSelectBone} onBoneTransform={onBoneTransform} onActorRigReady={onActorRigReady} />;
if (object.kind === "billboard" && object.url) return <DirectorBillboard object={object} selected={selected} />;
if (object.primitive === "character") return <DirectorCharacter object={object} selected={selected} />;
const material = <meshStandardMaterial color={selected ? "#2f8cff" : object.color} roughness={0.68} metalness={0.05} />;
return (
<mesh castShadow={object.castShadow} receiveShadow={object.receiveShadow}>
@@ -193,25 +192,6 @@ function DirectorObjectVisual({ object, selected, selectedBone, playhead, onSele
);
}
function DirectorCharacter({ object, selected }: { object: DirectorObject; selected: boolean }) {
const color = selected ? "#2f8cff" : object.color;
const pose = characterPose(object.pose || "stand");
return (
<group>
<mesh castShadow position={[0, 1.55, 0]}><sphereGeometry args={[0.24, 24, 18]} /><meshStandardMaterial color={color} roughness={0.76} /></mesh>
<mesh castShadow position={[0, 1.05, 0]}><capsuleGeometry args={[0.28, 0.65, 8, 16]} /><meshStandardMaterial color={color} roughness={0.76} /></mesh>
<Limb position={[-0.34, 1.16, 0]} rotation={pose.leftArm} color={color} />
<Limb position={[0.34, 1.16, 0]} rotation={pose.rightArm} color={color} />
<Limb position={[-0.17, 0.43, 0]} rotation={pose.leftLeg} color={color} length={0.82} />
<Limb position={[0.17, 0.43, 0]} rotation={pose.rightLeg} color={color} length={0.82} />
</group>
);
}
function Limb({ position, rotation, color, length = 0.68 }: { position: [number, number, number]; rotation: [number, number, number]; color: string; length?: number }) {
return <mesh castShadow position={position} rotation={rotation}><capsuleGeometry args={[0.09, length, 6, 12]} /><meshStandardMaterial color={color} roughness={0.76} /></mesh>;
}
function DirectorModel({ object, selected, selectedBone, playhead, onSelectBone, onBoneTransform, onActorRigReady }: { object: DirectorObject; selected: boolean; selectedBone: string | null; playhead: number; onSelectBone: (bone: string | null) => void; onBoneTransform: (bone: string, rotation: DirectorQuat) => void; onActorRigReady: (rig: DirectorRig, animations: AnimationClip[]) => void }) {
const [model, setModel] = useState<Object3D | null>(null);
const [animations, setAnimations] = useState<AnimationClip[]>([]);
@@ -274,7 +254,9 @@ function DirectorModel({ object, selected, selectedBone, playhead, onSelectBone,
mixer.stopAllAction();
if (!activeAnimation) return;
mixer.clipAction(activeAnimation).setLoop(motion?.loop ? LoopRepeat : LoopOnce, motion?.loop ? Infinity : 1).play();
return () => mixer.stopAllAction();
return () => {
mixer.stopAllAction();
};
}, [activeAnimation, model, motion?.loop]);
useEffect(() => {
@@ -298,7 +280,7 @@ function DirectorModel({ object, selected, selectedBone, playhead, onSelectBone,
<primitive object={model} />
{selected && helper ? <primitive object={helper} /> : null}
{selected && rig ? Object.entries(rig.boneMap).filter(([, name]) => Boolean(name)).map(([bone, name]) => <BoneController key={bone} bone={model.getObjectByName(name!)} model={model} selected={selectedBone === bone} onSelect={() => onSelectBone(bone)} />) : null}
{selected && selectedBoneObject ? <TransformControls object={selectedBoneObject} mode="rotate" size={0.55} onMouseDown={(event) => { event.stopPropagation(); }} onMouseUp={() => onBoneTransform(selectedBone!, selectedBoneObject.quaternion.toArray() as DirectorQuat)} /> : null}
{selected && selectedBoneObject ? <TransformControls object={selectedBoneObject} mode="rotate" size={0.55} onMouseUp={() => onBoneTransform(selectedBone!, selectedBoneObject.quaternion.toArray() as DirectorQuat)} /> : null}
</group>;
}
@@ -442,14 +424,6 @@ function DirectorLightView({ light }: { light: DirectorLight }) {
return <directionalLight position={position} color={light.color} intensity={light.intensity} castShadow={light.castShadow} shadow-mapSize-width={1024} shadow-mapSize-height={1024} />;
}
function characterPose(pose: NonNullable<DirectorObject["pose"]>): Record<"leftArm" | "rightArm" | "leftLeg" | "rightLeg", [number, number, number]> {
if (pose === "walk") return { leftArm: [0.35, 0, 0.18], rightArm: [-0.35, 0, -0.18], leftLeg: [-0.24, 0, 0.08], rightLeg: [0.24, 0, -0.08] };
if (pose === "run") return { leftArm: [0.7, 0, 0.35], rightArm: [-0.7, 0, -0.35], leftLeg: [-0.55, 0, 0.12], rightLeg: [0.55, 0, -0.12] };
if (pose === "sit") return { leftArm: [0.15, 0, 0.05], rightArm: [0.15, 0, -0.05], leftLeg: [1.1, 0, 0], rightLeg: [1.1, 0, 0] };
if (pose === "fight" || pose === "throw" || pose === "push") return { leftArm: [1.1, 0.2, 0.5], rightArm: [-0.7, -0.2, -0.5], leftLeg: [-0.35, 0, 0.2], rightLeg: [0.45, 0, -0.15] };
return { leftArm: [0, 0, 0.08], rightArm: [0, 0, -0.08], leftLeg: [0, 0, 0], rightLeg: [0, 0, 0] };
}
async function captureFrame(context: CaptureContext | null, mode: DirectorRenderMode) {
if (!context) throw new Error("3D 视口尚未就绪");
const { gl, scene, camera } = context;

View File

@@ -45,7 +45,7 @@ export function createDirectorObject(primitive: DirectorObject["primitive"] = "b
};
}
export function createDirectorActor(name = "演员", position: DirectorVec3 = [0, 0, 0], color = DIRECTOR_ACTOR_COLORS[0]): DirectorObject {
export function createDirectorActor(name = "演员", position: DirectorVec3 = [0, 0, 0], color: string = DIRECTOR_ACTOR_COLORS[0]): DirectorObject {
return {
...createDirectorObject("box", name, position, color),
kind: "actor",

View File

@@ -35,7 +35,7 @@ export type CanvasGenerationBatchMode = "storyboard_image" | "storyboard_video"
export type CanvasGenerationBatchStatus = "queued" | "running" | "partial_failed" | "completed" | "cancelled";
export type CanvasGenerationBatchItemStatus = "waiting" | "submitting" | "queued" | "running" | "succeeded" | "failed" | "cancelled";
export type CanvasImageGenerationType = "generation" | "edit";
export type CanvasWorkflowKind = "free" | "script" | "story_input" | "character" | "scene" | "storyboard" | "shot" | "final" | "styleboard" | "reference_set" | "action_board";
export type CanvasWorkflowKind = "free" | "script" | "story_input" | "character" | "scene" | "storyboard" | "shot" | "final" | "styleboard" | "reference_set" | "reference_video" | "action_board";
export type CanvasVideoEditOperation = "text_to_video" | "image_to_video" | "extend" | "inpaint" | "replace_element" | "camera_motion" | "style_transfer" | "audio_to_video" | "compare_versions" | "concat";
export type CanvasSkillCategory = "writing" | "storyboard" | "image" | "video" | "utility";
export type CanvasSkillOutputMode = "text" | "json" | "image_prompt" | "workflow";