feat(编辑器): 支持导入 GLB/GLTF 模型并驱动角色动画
- 新增 GLB/GLTF 模型加载与渲染,支持内嵌动画播放 - 通过 IndexedDB 持久化大体积模型二进制,规避 localStorage 限制 - 新增动作片段序列,时间轴可添加、编辑与编排多段动作 - 优化机位视角相机同步,修复角度修改不生效问题 - 扩展骨骼识别,兼容 Mixamo 前缀及 UE4 骨架
This commit is contained in:
1 parent
9d3a5b9bea
commit
38dc27501a
36 files changed
+1899
-427
No files matched your search
@@ -1,8 +1,10 @@
|
||||
import { Component, type ReactNode } from "react";
|
||||
import type { CharacterRigState } from "../schema/directorProject";
|
||||
import type { CharacterRigState, MotionClip } from "../schema/directorProject";
|
||||
import { PrimitiveMannequin } from "./PrimitiveMannequin";
|
||||
import { UE4MannequinModel } from "./UE4MannequinModel";
|
||||
import { GltfCharacterModel } from "./GltfCharacterModel";
|
||||
import type { CharacterBodyType } from "./mannequin/bodyTypes";
|
||||
import { getActiveMotionClipAtTime, resolveMotionClipEnd } from "./mannequin/motion";
|
||||
|
||||
interface CharacterModelProps {
|
||||
bodyType?: CharacterBodyType;
|
||||
@@ -15,6 +17,12 @@ interface CharacterModelProps {
|
||||
motionStartTime?: number;
|
||||
/** 动作片段终点(秒):缺省视为时间轴末尾 */
|
||||
motionEndTime?: number;
|
||||
/** 时间轴上的动作片段序列(多段动作)。存在时按播放时间取当前片段覆盖上面的单一 motion。 */
|
||||
motionClips?: MotionClip[];
|
||||
/** 时间轴总时长(秒),用于解析到末尾的动作片段。 */
|
||||
duration: number;
|
||||
/** 当 rigState.modelAssetId 指向一个导入的 GLB/GLTF 资产时,其可访问的 url(用于渲染该角色几何)。 */
|
||||
modelAssetUrl?: string;
|
||||
}
|
||||
|
||||
class CharacterModelBoundary extends Component<
|
||||
@@ -37,8 +45,36 @@ class CharacterModelBoundary extends Component<
|
||||
}
|
||||
}
|
||||
|
||||
export function CharacterModel({ bodyType, color, onLabelAnchorYChange, rigState, motionTime, characterId, motionStartTime, motionEndTime }: CharacterModelProps) {
|
||||
const fallback = <PrimitiveMannequin bodyType={bodyType} color={color} rigState={rigState} motionTime={motionTime} characterId={characterId} motionStartTime={motionStartTime} motionEndTime={motionEndTime} />;
|
||||
export function CharacterModel({ bodyType, color, onLabelAnchorYChange, rigState, motionTime, characterId, motionStartTime, motionEndTime, modelAssetUrl, motionClips, duration }: CharacterModelProps) {
|
||||
// 动作序列:按当前播放时间取出应生效的片段(后段覆盖前段),用它覆盖单一 motion 字段。
|
||||
// 无片段 / 时间不在任何片段内 → 回退到旧的单一段落(rigState.motion + motionStartTime/EndTime)。
|
||||
const activeClip = getActiveMotionClipAtTime(motionClips, motionTime ?? 0, duration);
|
||||
const resolvedRigState: CharacterRigState | undefined = activeClip && rigState
|
||||
? { ...rigState, motion: activeClip }
|
||||
: rigState;
|
||||
const resolvedStart = activeClip ? activeClip.start : motionStartTime;
|
||||
const resolvedEnd = activeClip ? resolveMotionClipEnd(activeClip, duration) : motionEndTime;
|
||||
|
||||
const fallback = <PrimitiveMannequin bodyType={bodyType} color={color} rigState={resolvedRigState} motionTime={motionTime} characterId={characterId} motionStartTime={resolvedStart} motionEndTime={resolvedEnd} />;
|
||||
|
||||
// 导入的 GLB/GLTF 人形角色:几何来自资产,用通用人形骨骼映射驱动程序化动作。
|
||||
if (rigState?.modelAssetId && modelAssetUrl) {
|
||||
return (
|
||||
<CharacterModelBoundary fallback={fallback}>
|
||||
<GltfCharacterModel
|
||||
url={modelAssetUrl}
|
||||
bodyType={bodyType}
|
||||
color={color}
|
||||
onLabelAnchorYChange={onLabelAnchorYChange}
|
||||
rigState={resolvedRigState}
|
||||
motionTime={motionTime}
|
||||
characterId={characterId}
|
||||
motionStartTime={resolvedStart}
|
||||
motionEndTime={resolvedEnd}
|
||||
/>
|
||||
</CharacterModelBoundary>
|
||||
);
|
||||
}
|
||||
|
||||
if (rigState?.rigType !== "ue4-mannequin") {
|
||||
return fallback;
|
||||
@@ -50,11 +86,11 @@ export function CharacterModel({ bodyType, color, onLabelAnchorYChange, rigState
|
||||
bodyType={bodyType}
|
||||
color={color}
|
||||
onLabelAnchorYChange={onLabelAnchorYChange}
|
||||
rigState={rigState}
|
||||
rigState={resolvedRigState}
|
||||
motionTime={motionTime}
|
||||
characterId={characterId}
|
||||
motionStartTime={motionStartTime}
|
||||
motionEndTime={motionEndTime}
|
||||
motionStartTime={resolvedStart}
|
||||
motionEndTime={resolvedEnd}
|
||||
/>
|
||||
</CharacterModelBoundary>
|
||||
);
|
||||
|
||||
Reference in new issue
Block a user