feat(All):Initial

This commit is contained in:
2025-10-04 23:36:07 +08:00
commit 2b4e5d2668
1321 changed files with 415958 additions and 0 deletions
@@ -0,0 +1,335 @@
import {reactive, toRefs, h,computed} from "vue";
import {NIcon} from 'naive-ui';
import type {TreeOption} from 'naive-ui'
import {defineStore} from 'pinia';
import {ConditionPoint} from '@vicons/carbon';
import {store} from '@/store';
import {t} from "@/language";
import type {TimelineTrack,ITimelineRow} from "@astral3d/engine";
import {App} from "@astral3d/engine";
export interface IAnimationItem {
name: string,
uuid: string,
isRunning: boolean,
isPaused: boolean,
}
interface IAnimationState {
// 选中模型的动画列表
list: Array<IAnimationItem>,
// 动画编辑器当前选中的动画
current: IAnimationItem | null,
// 当前选中动画的所有轨道
trackTree: TreeOption[],
// 全局的动画播放速率
mixerTimeScale: number,
// 当前播放的时间(毫秒)
currentTime: number,
// 当前action总时长(毫秒)
duration: number,
}
// 动画轨道类实例
let timelineInstance: TimelineTrack, waitRows: ITimelineRow[] = [];
/**
* 模型动画(动画编辑器)
*/
export const useAnimationStore = defineStore('model-animation', () => {
const state = reactive<IAnimationState>({
list: [],
current: null,
trackTree: [],
mixerTimeScale: 1.0,
currentTime: 0,
duration: 0
})
/**
* getter
**/
// 动画时间帧一步的长度,默认一步走过一个像素代表1000ms (即1px代表多少ms)
const getStepVal = () => {
if (timelineInstance) {
return (<number>timelineInstance.timeline._options.stepVal) * timelineInstance.timeline._currentZoom / (<number>timelineInstance.timeline._options.zoom);
} else {
return 1000;
}
};
// 获取格式化后的总时长 00:00:00
const getFormattedDuration = computed(() => {
const _time = state.duration / 1000;
const hours = Math.floor(_time / 3600);
const minutes = Math.floor((_time - hours * 3600) / 60);
const seconds = _time - hours * 3600 - minutes * 60;
return `${hours.toString().padStart(2, "0")}:${minutes.toString().padStart(2, "0")}:${seconds.toFixed(0).padStart(2, "0")}`;
});
// 获取格式化后的播放时间 00:00:00
const getFormattedCurrentTime = computed(() => {
const _time = state.currentTime / 1000;
const hours = Math.floor(_time / 3600);
const minutes = Math.floor((_time - hours * 3600) / 60);
const seconds = (_time- hours * 3600 - minutes * 60).toFixed(0);
return `${hours.toString().padStart(2, "0")}:${minutes.toString().padStart(2, "0")}:${seconds.padStart(2, "0")}`;
});
/**
* actions
**/
const setTimelineInstance = (instance: TimelineTrack) => {
timelineInstance = instance;
if (waitRows.length) {
timelineInstance.setRows(waitRows);
waitRows = [];
const action = App.animationManager.actionMap.get(state.current?.uuid as string);
if (!action) return;
timelineInstance.bindAction = action;
}
}
// 设置动画列表
const setList = (_list: Array<IAnimationItem>, _current: IAnimationItem | null = null) => {
state.list = _list;
if (_current) {
setCurrent(_current);
} else {
setCurrent(_list.length ? _list[0] : null);
}
}
// 设置当前选中的动画(会刷新树列表及动画轨道)
const setCurrent = (value: IAnimationItem | null) => {
state.current = value;
if (!value) {
state.trackTree = [];
state.currentTime = 0;
state.duration = 0;
if (timelineInstance) {
timelineInstance.setRows([]);
timelineInstance.bindAction = null;
waitRows = [];
}
return;
}
// 取出当前动画下的所有轨道组织rows关系
const action = App.animationManager.actionMap.get(value.uuid);
if (!action) return;
const actionClip = action.getClip();
const actionObject = action.getRoot();
state.duration = actionClip.duration * 1000;
// 取出所有轨道并按名称排序
const tracks = [...actionClip.tracks].sort((a, b) => a.name.localeCompare(b.name));
const _trackTree: TreeOption = {
key: actionObject.uuid,
label: actionObject.name,
children: [],
isLeaf: false
};
waitRows = [{
id: actionObject.uuid,
name: actionObject.name
}];
const pathMap = new Map(); // 用于记录已有路径
// 处理每个轨道
tracks.forEach(track => {
const parts = track.name.split('.');
let currentPath = '';
let parentNode: TreeOption | null = null;
parts.forEach((part, index) => {
const isLeaf = index === parts.length - 1;
const nodePath = currentPath ? `${currentPath}!!${part}` : part;
if (index === 0 && (part === actionObject.name || part === actionObject.uuid)) return;
// 如果节点已存在则直接使用
if (pathMap.has(nodePath)) {
parentNode = pathMap.get(nodePath);
currentPath = nodePath;
return;
}
// 记录到rows
const _row: ITimelineRow = {
id: nodePath,
name: part
}
// 创建新节点
const newNode: TreeOption = {
key: nodePath,
label: part,
children: [],
...(isLeaf && {
isLeaf: true,
suffix: () =>
h(
NIcon,
{
size: 16,
onClick: () => {
// 取最后一段(如 'nodeName.property[accessor]'
const lastSegment = parts[parts.length - 1];
// 再按 '[' 分割,取第一部分(如 'property'
let attr = lastSegment?.split('[')[0];
// 如果parts倒数第二个元素为material,则添加至前面
if(parts.length > 1 && parts[parts.length - 2] ==='material'){
attr = `material.${attr}`;
}
addKeyframe(attr);
}
},
{default: () => h(ConditionPoint)}
),
})
};
// 如果是叶子节点,则写入关键帧值
if (isLeaf) {
_row.keyframes = [];
// 每个关键帧的值的长度
const dataLength = Math.floor(track.values.length / track.times.length);
track.times.forEach((time, i) => {
_row.keyframes?.push({
val: time * 1000,
// @ts-ignore
data: track.values.slice(i * dataLength, (i + 1) * dataLength)
})
})
_row.track = track;
}
waitRows.push(_row);
// 挂载到父节点或根
if (parentNode) {
parentNode.children?.push(newNode);
} else {
_trackTree.children?.push(newNode);
}
// 更新路径记录
pathMap.set(nodePath, newNode);
parentNode = newNode;
currentPath = nodePath;
});
});
state.trackTree = [_trackTree];
if (timelineInstance) {
timelineInstance.setRows(waitRows);
timelineInstance.bindAction = action;
waitRows = [];
state.duration = timelineInstance._maxDuration;
}
}
const play = () => {
if (!state.current) return;
if (timelineInstance) {
timelineInstance.play();
} else {
const action = App.animationManager.actionMap.get(state.current.uuid);
if (!action) return;
action.play();
action.paused = false;
}
state.current.isPaused = false;
state.current.isRunning = true;
}
const pause = () => {
if (!state.current) return;
if (timelineInstance) {
timelineInstance.pause();
} else {
const action = App.animationManager.actionMap.get(state.current.uuid);
if (!action) return;
action.paused = !state.current.isPaused;
}
state.current.isPaused = !state.current.isPaused;
}
const stop = () => {
if (!state.current) return;
if (timelineInstance) {
timelineInstance.stop();
} else {
const action = App.animationManager.actionMap.get(state.current.uuid);
if (!action) return;
action.stop();
}
state.current.isPaused = false;
state.current.isRunning = false;
}
// 跳转至开始
const jumpToStart = () => {
if (!timelineInstance) return;
timelineInstance.timeline.setTime(0);
}
// 跳转至最后
const jumpToEnd = () => {
if (!timelineInstance) return;
timelineInstance.timeline.setTime(state.duration);
}
// 添加关键帧
const addKeyframe = (attr: string) => {
if (!state.current) {
window.$message?.error(t("prompt.Please select an animation"));
return;
}
if (state.current.isRunning) {
window.$message?.error(t("prompt['The animation is running, please stop the animation before performing operations']"));
return;
}
if (timelineInstance) {
timelineInstance.addKeyframe(attr);
state.duration = timelineInstance._maxDuration;
}
}
// 设置动画播放时间(单位:ms)
const setPlayTime = (time: number) => {
if (timelineInstance) {
timelineInstance.timeline.setTime(time);
}
};
return {
...toRefs(state),
getStepVal,
getFormattedDuration,
getFormattedCurrentTime,
setTimelineInstance,
setList,
setCurrent,
play,
pause,
stop,
jumpToStart,
jumpToEnd,
addKeyframe,
setPlayTime
}
});
// setup 之外使用
export function useAnimationStoreWithOut() {
return useAnimationStore(store);
}