From 38c4f63e2e43ca47813ba05e4e4598470d13428e Mon Sep 17 00:00:00 2001 From: ddCat <2695092045@qq.com> Date: Wed, 5 Aug 2026 16:34:50 +0800 Subject: [PATCH] =?UTF-8?q?fix(canvas):=20=E5=AF=BC=E6=BC=94=E5=8F=B0?= =?UTF-8?q?=E4=BA=BA=E7=89=A9=20-=20=E7=BC=A9=E5=B0=8F=20Dock=20=E5=B9=B6?= =?UTF-8?q?=E7=BB=9F=E4=B8=80=E5=9F=BA=E7=A1=80=E4=BA=BA=E5=81=B6=E5=88=9D?= =?UTF-8?q?=E5=A7=8B=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../director/canvas-director-workbench.tsx | 4 ++-- .../canvas/director/director-viewport.tsx | 13 ++++++----- .../director/director-prompt-compiler.ts | 4 ++-- web/src/lib/canvas/director/director-scene.ts | 7 ++---- web/src/styles/globals.css | 22 ++++++++++--------- 5 files changed, 25 insertions(+), 25 deletions(-) diff --git a/web/src/components/canvas/director/canvas-director-workbench.tsx b/web/src/components/canvas/director/canvas-director-workbench.tsx index 8226b0ff6..6c90d570c 100644 --- a/web/src/components/canvas/director/canvas-director-workbench.tsx +++ b/web/src/components/canvas/director/canvas-director-workbench.tsx @@ -337,10 +337,10 @@ function ObjectInspector({ object, playhead, selectedBone, onSelectBone, onUpdat const applyPose = (pose: DirectorPose) => onUpdate({ pose, activeMotionClipId: undefined, boneOverrides: {} }); return onUpdate({ name })} onDelete={onDelete}> onUpdate({ transform })} /> - {object.kind === "actor" + {object.kind === "actor" || object.primitive === "character" ?
{DIRECTOR_ACTOR_COLORS.map((color) =>
: onUpdate({ color })} />} - {object.kind === "actor" || motionClips.length ? <> + {object.kind === "actor" || object.primitive === "character" || motionClips.length ? <>
姿势预设{directorPoseLabel(object.pose || "stand")}
{poseOptions.map((option) => )}
diff --git a/web/src/components/canvas/director/director-viewport.tsx b/web/src/components/canvas/director/director-viewport.tsx index f2bb78be4..4e2f0e433 100644 --- a/web/src/components/canvas/director/director-viewport.tsx +++ b/web/src/components/canvas/director/director-viewport.tsx @@ -5,7 +5,7 @@ import { AnimationClip, AnimationMixer, Box3, Bone, Color, Group, LoopOnce, Loop import type { Material } from "three"; import { GLTFLoader, SkeletonUtils } from "three-stdlib"; -import { directorPoseBoneDeltas, interpolateDirectorBoneRotation, interpolateDirectorTransform } from "@/lib/canvas/director/director-scene"; +import { DIRECTOR_DEFAULT_ACTOR_URL, directorPoseBoneDeltas, interpolateDirectorBoneRotation, interpolateDirectorTransform } from "@/lib/canvas/director/director-scene"; import { resolveMediaUrl } from "@/services/file-storage"; import type { DirectorCamera, DirectorHumanoidBone, DirectorLight, DirectorObject, DirectorQuat, DirectorRenderMode, DirectorRig, DirectorScene, DirectorTransform, DirectorVec3 } from "@/types/director"; @@ -181,7 +181,7 @@ 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.url) return ; + if ((object.kind === "model" || object.kind === "actor" || object.primitive === "character") && (object.url || object.primitive === "character")) return ; if (object.kind === "billboard" && object.url) return ; if (object.primitive === "character") return ; const material = ; @@ -224,18 +224,19 @@ function DirectorModel({ object, selected, selectedBone, playhead, onSelectBone, const selectedBoneObject = selectedBone && rig?.boneMap[selectedBone as DirectorHumanoidBone] ? model?.getObjectByName(rig.boneMap[selectedBone as DirectorHumanoidBone]!) : null; const motion = object.motionClips?.find((item) => item.id === object.activeMotionClipId); const activeAnimation = motion ? animations.find((item) => item.name === motion.sourceAnimation) : undefined; + const modelUrl = object.kind === "actor" || object.primitive === "character" ? DIRECTOR_DEFAULT_ACTOR_URL : object.url; useEffect(() => { onActorRigReadyRef.current = onActorRigReady; }, [onActorRigReady]); useEffect(() => { let active = true; const loader = new GLTFLoader(); - void resolveMediaUrl(object.storageKey, object.url).then((url) => loader.load(url, (gltf) => { + void resolveMediaUrl(object.storageKey, modelUrl).then((url) => loader.load(url, (gltf) => { if (!active) return; const next = SkeletonUtils.clone(gltf.scene); normalizeModel(next, object.castShadow, object.receiveShadow); const nextRig = inferDirectorRig(next, gltf.animations.map((clip) => clip.name)); - if (object.kind === "actor") applyActorReferenceMaterial(next, object.color); + if (object.kind === "actor" || object.primitive === "character") applyActorReferenceMaterial(next, object.color); mixerRef.current = new AnimationMixer(next); setRig(nextRig); setRestRotations(readRigRestRotations(next, nextRig)); @@ -248,7 +249,7 @@ function DirectorModel({ object, selected, selectedBone, playhead, onSelectBone, mixerRef.current?.stopAllAction(); mixerRef.current = null; }; - }, [object.kind, object.storageKey, object.url]); + }, [modelUrl, object.kind, object.storageKey]); useEffect(() => { if (!model) return; @@ -262,7 +263,7 @@ function DirectorModel({ object, selected, selectedBone, playhead, onSelectBone, }, [invalidate, model, object.castShadow, object.receiveShadow]); useEffect(() => { - if (!model || object.kind !== "actor") return; + if (!model || (object.kind !== "actor" && object.primitive !== "character")) return; updateActorReferenceColor(model, object.color); invalidate(); }, [invalidate, model, object.color, object.kind]); diff --git a/web/src/lib/canvas/director/director-prompt-compiler.ts b/web/src/lib/canvas/director/director-prompt-compiler.ts index 42834169b..b94bc4f74 100644 --- a/web/src/lib/canvas/director/director-prompt-compiler.ts +++ b/web/src/lib/canvas/director/director-prompt-compiler.ts @@ -26,7 +26,7 @@ const cameraMoveLabels: Record = { export function compileDirectorPrompt(scene: DirectorScene, shot: DirectorShot) { const camera = scene.cameras.find((item) => item.id === shot.cameraId) || scene.cameras[0]; const visibleObjects = scene.objects.filter((item) => item.visible); - const actors = visibleObjects.filter((item) => item.kind === "actor"); + const actors = visibleObjects.filter((item) => item.kind === "actor" || item.primitive === "character"); return [ shot.prompt.trim(), `镜头设计:${shotSizeLabels[shot.shotSize]},${cameraMoveLabels[shot.cameraMove]},时长 ${formatNumber(shot.duration)} 秒。`, @@ -52,7 +52,7 @@ function cameraPrompt(camera: DirectorCamera) { function objectPrompt(object: DirectorObject) { const [x, y, z] = object.transform.position; const pose = object.pose ? `,姿势${directorPoseLabel(object.pose)}` : ""; - const color = object.kind === "actor" ? `,${directorColorLabel(object.color)}参考人偶` : ""; + const color = object.kind === "actor" || object.primitive === "character" ? `,${directorColorLabel(object.color)}参考人偶` : ""; return `${object.name}${color}位于 (${formatNumber(x)}, ${formatNumber(y)}, ${formatNumber(z)})${pose}`; } diff --git a/web/src/lib/canvas/director/director-scene.ts b/web/src/lib/canvas/director/director-scene.ts index e35fe932e..46d4edab0 100644 --- a/web/src/lib/canvas/director/director-scene.ts +++ b/web/src/lib/canvas/director/director-scene.ts @@ -3,7 +3,7 @@ import { Color, Euler, Quaternion } from "three"; import type { DirectorBoneKeyframe, DirectorBoneTrack, DirectorCamera, DirectorHumanoidBone, DirectorKeyframe, DirectorLight, DirectorObject, DirectorPose, DirectorQuat, DirectorScene, DirectorTransform, DirectorVec3 } from "@/types/director"; -export const DIRECTOR_DEFAULT_ACTOR_URL = "https://cdn.jsdelivr.net/gh/mrdoob/three.js@r185/examples/models/gltf/Soldier.glb"; +export const DIRECTOR_DEFAULT_ACTOR_URL = "https://cdn.jsdelivr.net/gh/mrdoob/three.js@r185/examples/models/gltf/Xbot.glb"; export const DIRECTOR_ACTOR_COLORS = ["#f1f3f5", "#202329", "#2f7de1", "#d84949", "#dfae3f", "#34a276"] as const; export const directorIdentityTransform = (position: DirectorVec3 = [0, 0, 0]): DirectorTransform => ({ position, rotation: [0, 0, 0], scale: [1, 1, 1] }); @@ -19,10 +19,7 @@ export function createDirectorScene(title = "未命名场景"): DirectorScene { background: "#d8dde3", environmentIntensity: 0.7, gridVisible: true, - objects: [ - createDirectorActor("演员 1", [0, 0, 0]), - createDirectorObject("box", "前景道具", [1.8, 0.45, 0.8], "#8b7355"), - ], + objects: [createDirectorActor("演员 1", [0, 0, 0])], cameras: [camera], lights: [createDirectorLight("directional", "主光", [4, 6, 4], 2.4), createDirectorLight("directional", "轮廓光", [-4, 3, -2], 1.1), createDirectorLight("ambient", "环境光", [0, 0, 0], 0.65)], shots: [{ id: shotId, name: "镜头 1", cameraId: camera.id, duration: 5, fps: 24, shotSize: "medium", cameraMove: "static", prompt: "" }], diff --git a/web/src/styles/globals.css b/web/src/styles/globals.css index c669d9b2c..12833c56c 100644 --- a/web/src/styles/globals.css +++ b/web/src/styles/globals.css @@ -349,6 +349,7 @@ --director-dock-surface: rgba(17, 19, 21, 0.92); --director-dock-fg: rgba(255, 255, 255, 0.62); --director-dock-fg-strong: rgba(255, 255, 255, 0.96); + --director-dock-active-surface: rgba(255, 255, 255, 0.14); --director-control-hover: rgba(255, 255, 255, 0.09); --director-swatch-inset: rgba(0, 0, 0, 0.18); @@ -476,6 +477,7 @@ --director-dock-surface: rgba(17, 19, 21, 0.92); --director-dock-fg: rgba(255, 255, 255, 0.62); --director-dock-fg-strong: rgba(255, 255, 255, 0.96); + --director-dock-active-surface: rgba(255, 255, 255, 0.14); --director-control-hover: rgba(255, 255, 255, 0.09); --director-swatch-inset: rgba(0, 0, 0, 0.18); @@ -555,8 +557,8 @@ --director-sequencer-row-height: 30px; --director-sequencer-base-width: 860px; --director-sequencer-shot-select-width: 144px; - --director-dock-button-size: var(--space-10); - --director-dock-bottom: var(--space-3); + --director-dock-button-size: var(--space-8); + --director-dock-bottom: var(--space-2); --director-pose-button-height: var(--space-8); --director-color-swatch-size: var(--space-6); @@ -5185,8 +5187,8 @@ html:not(.dark) .creation-model-picker-menu .canvas-model-picker-option > span:f display: flex; max-width: calc(100% - var(--space-6)); align-items: center; - gap: var(--space-1); - padding: var(--space-1-half); + gap: var(--space-half); + padding: var(--space-1); overflow-x: auto; border: var(--stroke-1) solid var(--director-sequencer-border); border-radius: var(--r-lg); @@ -5208,8 +5210,8 @@ html:not(.dark) .creation-model-picker-menu .canvas-model-picker-option > span:f } .director-viewport-dock-button > svg { - width: var(--space-5); - height: var(--space-5); + width: var(--space-4); + height: var(--space-4); } .director-viewport-dock-button:hover { @@ -5222,14 +5224,14 @@ html:not(.dark) .creation-model-picker-menu .canvas-model-picker-option > span:f } .director-viewport-dock-button.is-active { - background: var(--workspace-accent); - color: var(--color-neutral-0); - box-shadow: var(--shadow-glow-sm); + background: var(--director-dock-active-surface); + color: var(--director-dock-fg-strong); + box-shadow: inset 0 0 0 var(--stroke-1) var(--director-sequencer-border); } .director-viewport-dock-divider { width: var(--stroke-1); - height: var(--space-6); + height: var(--space-5); flex: 0 0 var(--stroke-1); margin: 0 var(--space-half); background: var(--director-sequencer-border);