fix: 优化Gizmo

This commit is contained in:
2026-09-18 03:06:34 +08:00
parent 8645ea3007
commit 9b72f10b43
23 changed files with 787 additions and 254 deletions

View File

@@ -30,6 +30,8 @@ import { DEFAULT_CHARACTER_BODY_TYPE, normalizeBodyType } from "../runtime/manne
import {
DEFAULT_DIRECTOR_CAMERA_VIEW_SNAPSHOT,
getCameraRigPositionFromViewSnapshot,
getCameraLookRotationFromTarget,
migrateLegacyCameraShot,
} from "../schema/cameraGeometry";
import type { ViewportAspectRatio } from "../schema/viewportAspectRatio";
import {
@@ -215,7 +217,7 @@ export interface DirectorActions {
cameraId: string,
patch: Partial<DirectorCameraShot> & {
transform?: DirectorTransform;
target?: [number, number, number];
lookRotation?: [number, number, number];
}
) => void;
// 物体关键帧
@@ -517,8 +519,57 @@ function withSceneDefaults(project: DirectorProject): DirectorProject {
return { ...project, scene: nextScene };
}
/**
* 旧工程里机位的注视是一个世界坐标 target:统一换算成注视朝向角
* (水平角 / 俯仰角 / 横滚角),运镜关键帧同理。
*/
function withCameraLookRotations(project: DirectorProject): DirectorProject {
const cameras = project.cameras ?? [];
const cameraTracks = project.timeline?.cameraTracks ?? [];
const legacyCamera = cameras.some((camera) => Boolean(camera) && !Array.isArray(camera.lookRotation));
const legacyKeyframe = cameraTracks.some((track) =>
(track.keyframes ?? []).some((keyframe) => Boolean(keyframe) && !Array.isArray(keyframe.value?.lookRotation))
);
if (!legacyCamera && !legacyKeyframe) return project;
return {
...project,
cameras: cameras.map((camera) => (camera ? migrateLegacyCameraShot(camera) : camera)),
timeline: legacyKeyframe
? {
...project.timeline,
cameraTracks: cameraTracks.map((track) => ({
...track,
keyframes: (track.keyframes ?? []).map((keyframe) => ({
...keyframe,
value: migrateLegacyCameraKeyframeValue(keyframe.value),
})),
})),
}
: project.timeline,
};
}
function migrateLegacyCameraKeyframeValue(value: {
position: [number, number, number];
fov: number;
lookRotation?: [number, number, number];
target?: [number, number, number];
}): CameraKeyframeValue {
return {
position: value.position,
fov: value.fov,
lookRotation: Array.isArray(value.lookRotation)
? value.lookRotation
: Array.isArray(value.target)
? getCameraLookRotationFromTarget(value.position, value.target)
: [...DEFAULT_DIRECTOR_CAMERA_VIEW_SNAPSHOT.lookRotation],
};
}
function migrateDirectorProject(project: DirectorProject): DirectorProject {
const withTimelineProject = withSceneDefaults(withTimeline(project));
const withTimelineProject = withSceneDefaults(withCameraLookRotations(withTimeline(project)));
return {
...withTimelineProject,
objects: withTimelineProject.objects.map((object) => {
@@ -676,7 +727,7 @@ export function createDefaultDirectorProject({
fov: DEFAULT_DIRECTOR_CAMERA_VIEW_SNAPSHOT.fov,
transform: createTransform(getCameraRigPositionFromViewSnapshot(DEFAULT_DIRECTOR_CAMERA_VIEW_SNAPSHOT)),
targetMode: "manual",
target: DEFAULT_DIRECTOR_CAMERA_VIEW_SNAPSHOT.target,
lookRotation: [...DEFAULT_DIRECTOR_CAMERA_VIEW_SNAPSHOT.lookRotation],
lastCaptureUrl: null,
captures: [],
};
@@ -928,7 +979,11 @@ function refreshCamerasFocusedOnObject(cameras: DirectorCameraShot[], object: Di
camera.targetMode === "object" && camera.targetObjectId === object.id
? {
...camera,
target: getDirectorObjectFocusTarget(object),
// 锁定物体:把「机位 → 物体」这条视线换算成注视朝向角存下来
lookRotation: getCameraLookRotationFromTarget(
camera.transform.position,
getDirectorObjectFocusTarget(object)
),
}
: camera
);
@@ -960,7 +1015,10 @@ function refreshCamerasFocusedOnObjects(
return {
...camera,
target: getDirectorObjectFocusTarget(targetObject),
lookRotation: getCameraLookRotationFromTarget(
camera.transform.position,
getDirectorObjectFocusTarget(targetObject)
),
};
});
}
@@ -1298,8 +1356,8 @@ function pasteClipboardEntries(state: DirectorRuntimeState): DirectorRuntimeStat
id: nextCameraId,
name: formatSceneItemName("机位", cameraIndex),
transform: applyOffsetToTransform(entry.camera.transform, offset),
target:
entry.camera.targetMode === "manual" ? applyPositionOffset(entry.camera.target, offset) : entry.camera.target,
// 注视是朝向角,平移机位不会改变它(锁物体的机位稍后按新位置重算)
lookRotation: [...entry.camera.lookRotation],
targetObjectId: targetObjectId ?? entry.camera.targetObjectId ?? null,
captures: [],
lastCaptureUrl: null,
@@ -1354,7 +1412,10 @@ function pasteClipboardEntries(state: DirectorRuntimeState): DirectorRuntimeStat
return {
...camera,
targetObjectId: mappedTargetObjectId,
target: getDirectorObjectFocusTarget(targetObject),
lookRotation: getCameraLookRotationFromTarget(
camera.transform.position,
getDirectorObjectFocusTarget(targetObject)
),
};
});
const lastPastedObject = pastedObjectIds.length
@@ -2348,7 +2409,9 @@ export const useDirectorStore = create<DirectorStore>((set, get) => {
fov: snapshot?.fov ?? 50,
transform,
targetMode: "manual",
target: snapshot?.target ?? [0, 1.2, 0],
lookRotation: snapshot
? getCameraLookRotationFromTarget(snapshot.position, snapshot.target)
: [...DEFAULT_DIRECTOR_CAMERA_VIEW_SNAPSHOT.lookRotation],
lastCaptureUrl: null,
captures: [],
};
@@ -2643,7 +2706,7 @@ export const useDirectorStore = create<DirectorStore>((set, get) => {
...item,
...patch,
transform: patch.transform ?? item.transform,
target: patch.target ?? item.target,
lookRotation: patch.lookRotation ?? item.lookRotation,
}
: item
),
@@ -2744,12 +2807,12 @@ export const useDirectorStore = create<DirectorStore>((set, get) => {
},
})),
applyCameraKeyframeToShot: (cameraId, value) => {
// 只改 position/target/fov,rotation/scale 保留 shot 原值(相机朝向由 target 决定)
// 只改 position/注视朝向/fov,scale 保留 shot 原值
const camera = get().project.cameras.find((item) => item.id === cameraId);
if (!camera) return;
get().updateCamera(cameraId, {
fov: value.fov,
target: value.target,
lookRotation: value.lookRotation,
transform: { ...camera.transform, position: value.position },
});
},