feat: 旋转单位改为度并支持轨迹点拖拽
统一数据模型中的旋转为度数并在渲染时转弧度,新增 InspectorAngleField 角度控件,为场景与轨迹点提供滑块和重置按钮;选中轨迹点时角色预览到该点并支持拖拽 gizmo 调整位置;机位点击统一走 selectObject 以正确切换右侧属性卡片。
This commit is contained in:
1 parent
e74cf6d28b
commit
0794486857
21 files changed
+659
-99
No files matched your search
@@ -20,6 +20,10 @@ import type {
|
||||
DirectorObject,
|
||||
GeometryPrimitiveType,
|
||||
} from "../schema/directorProject";
|
||||
import {
|
||||
degreesRotationToRadians,
|
||||
radiansRotationToDegrees,
|
||||
} from "../schema/directorProject";
|
||||
import {
|
||||
VIEWPORT_CAMERA_ASPECT,
|
||||
VIEWPORT_CAMERA_FRUSTUM_DEPTH,
|
||||
@@ -494,6 +498,14 @@ function ObjectSceneNode({
|
||||
const updateObjectTransform = useDirectorStore((state) => state.updateObjectTransform);
|
||||
const isPlaying = usePlaybackStore((state) => state.isPlaying);
|
||||
const motionTime = usePlaybackStore((state) => state.playhead);
|
||||
// 当前选中的轨迹点若属于本角色,则把角色「预览」到该路点的位置与朝向上,
|
||||
// 让用户直接看到角色在某个轨迹点的姿态(水平角/俯仰角/横滚角)。
|
||||
// 播放时角色要沿路径动画,故预览只在非播放时生效(拖拽 gizmo 也已排除播放态)。
|
||||
const selectedTrajectoryPoint = useDirectorStore((state) => state.selectedTrajectoryPoint);
|
||||
const activeTrajectoryPoint =
|
||||
!isPlaying && selectedTrajectoryPoint?.objectId === item.id
|
||||
? item.trajectoryPoints?.find((point) => point.id === selectedTrajectoryPoint.pointId)
|
||||
: undefined;
|
||||
const isImportedModel = asset?.sourceType === "model";
|
||||
const characterLabelKey = `${item.id}:${item.bodyType ?? ""}:${item.characterRig?.rigType ?? ""}`;
|
||||
const fallbackCharacterLabelY =
|
||||
@@ -526,9 +538,10 @@ function ObjectSceneNode({
|
||||
const group = groupRef.current;
|
||||
if (!group) return;
|
||||
|
||||
// group.rotation 是弧度,存入数据模型前要转成度数(数据模型里旋转一律用度数)
|
||||
updateObjectTransform(item.id, {
|
||||
position: [group.position.x, group.position.y, group.position.z],
|
||||
rotation: [group.rotation.x, group.rotation.y, group.rotation.z],
|
||||
rotation: radiansRotationToDegrees([group.rotation.x, group.rotation.y, group.rotation.z]),
|
||||
scale: [group.scale.x, group.scale.y, group.scale.z],
|
||||
});
|
||||
}
|
||||
@@ -536,8 +549,10 @@ function ObjectSceneNode({
|
||||
const node = (
|
||||
<group
|
||||
ref={groupRef}
|
||||
position={item.transform.position}
|
||||
rotation={item.transform.rotation}
|
||||
position={activeTrajectoryPoint ? activeTrajectoryPoint.position : item.transform.position}
|
||||
// 数据模型里旋转是度数,应用到 Three.js 的 rotation 属性(弧度)前要转换,
|
||||
// 否则 360° 会被当成 360 弧度 ≈ 57 圈。
|
||||
rotation={degreesRotationToRadians(activeTrajectoryPoint ? activeTrajectoryPoint.rotation : item.transform.rotation)}
|
||||
scale={item.transform.scale}
|
||||
userData={{ directorObjectId: item.id }}
|
||||
onClick={(event) => {
|
||||
@@ -571,7 +586,9 @@ function ObjectSceneNode({
|
||||
</group>
|
||||
);
|
||||
|
||||
const showGizmo = selected && transformable && !isPlaying;
|
||||
// 选中了本角色的某个轨迹点时,角色被预览到该路点上,此时不再显示角色自身的变换 gizmo,
|
||||
// 避免和轨迹点的拖拽 gizmo 重叠;拖拽由轨迹点 gizmo 负责。
|
||||
const showGizmo = selected && transformable && !isPlaying && !activeTrajectoryPoint;
|
||||
|
||||
if (!showGizmo) return node;
|
||||
|
||||
@@ -612,12 +629,13 @@ function CrowdTransformRig({
|
||||
const group = groupRef.current;
|
||||
if (!group) return;
|
||||
|
||||
// group.rotation 是弧度,存入数据模型前要转成度数
|
||||
updateCrowdTransform(crowdId, {
|
||||
position: [group.position.x, group.position.y, group.position.z],
|
||||
rotation: [group.rotation.x, group.rotation.y, group.rotation.z],
|
||||
rotation: radiansRotationToDegrees([group.rotation.x, group.rotation.y, group.rotation.z]),
|
||||
scale: [group.scale.x, group.scale.y, group.scale.z],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (!selected || !transformable || !crowdAnchor) return null;
|
||||
|
||||
@@ -626,7 +644,7 @@ function CrowdTransformRig({
|
||||
<group
|
||||
ref={groupRef}
|
||||
position={crowdAnchor.position}
|
||||
rotation={crowdAnchor.rotation}
|
||||
rotation={degreesRotationToRadians(crowdAnchor.rotation)}
|
||||
scale={crowdAnchor.scale}
|
||||
/>
|
||||
<ViewportTransformControls
|
||||
@@ -847,8 +865,14 @@ function TrajectoryPath({
|
||||
interactive: boolean;
|
||||
}) {
|
||||
const selectTrajectoryPoint = useDirectorStore((state) => state.selectTrajectoryPoint);
|
||||
const updateTrajectoryPoint = useDirectorStore((state) => state.updateTrajectoryPoint);
|
||||
const isPlaying = usePlaybackStore((state) => state.isPlaying);
|
||||
const translationSnap = useDirectorStore((state) => (state.project.scene.snapToGrid ? 1 : null));
|
||||
const selectedGroupRef = useRef<Group>(null!);
|
||||
const points = owner.trajectoryPoints ?? [];
|
||||
const color = owner.color ?? TRAJECTORY_FALLBACK_COLOR;
|
||||
// 只有在「可交互(非打点模式)且非播放中」且确实有选中点时,才给选中点挂拖拽 gizmo
|
||||
const draggable = interactive && !isPlaying && Boolean(selectedPointId);
|
||||
|
||||
/** 平滑曲线采样点:从「角色自身位置」出发,依次穿过每个路点(Catmull-Rom 样条,
|
||||
* centripetal 型避免路径自交打环)。这样轨迹是从角色身上延伸出来的,而不是凭空
|
||||
@@ -881,6 +905,21 @@ function TrajectoryPath({
|
||||
selectTrajectoryPoint(owner.id, pointId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 拖拽过程中实时把 gizmo 移动后的坐标写回选中的轨迹点:
|
||||
* 路点组在场景组局部坐标里抬高了 GROUND_GRID_ELEVATION(小球贴地用),
|
||||
* 这里把抬升减回去,存回的是轨迹语义坐标(y 默认 0 = 贴地)。
|
||||
* 写回后「角色预览」会跟着同一份坐标实时移动,所见即所得。
|
||||
*/
|
||||
function commitSelectedPoint() {
|
||||
const group = selectedGroupRef.current;
|
||||
if (!group || !selectedPointId) return;
|
||||
|
||||
updateTrajectoryPoint(owner.id, selectedPointId, {
|
||||
position: [group.position.x, group.position.y - GROUND_GRID_ELEVATION, group.position.z],
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<group name={`trajectory-${owner.id}`} userData={{ [HIDE_FROM_VIEWPORT_CAPTURE_KEY]: true }}>
|
||||
{sampledCurve ? (
|
||||
@@ -898,6 +937,7 @@ function TrajectoryPath({
|
||||
return (
|
||||
<group
|
||||
key={point.id}
|
||||
ref={selected ? selectedGroupRef : undefined}
|
||||
position={[point.position[0], point.position[1] + GROUND_GRID_ELEVATION, point.position[2]]}
|
||||
>
|
||||
{/* 透明大圆盘只为了让小球更好点中(俯视时点地面圆盘比点小球容易) */}
|
||||
@@ -921,6 +961,16 @@ function TrajectoryPath({
|
||||
</group>
|
||||
);
|
||||
})}
|
||||
{/* 选中点挂拖拽 gizmo:拖到哪,角色预览就跟到哪。打点模式 / 播放中 / 无选中点时不挂。 */}
|
||||
{draggable ? (
|
||||
<ViewportTransformControls
|
||||
mode="translate"
|
||||
object={selectedGroupRef}
|
||||
onObjectChange={commitSelectedPoint}
|
||||
translationSnap={translationSnap}
|
||||
editingId={owner.id}
|
||||
/>
|
||||
) : null}
|
||||
</group>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user