bill 5 月之前
父節點
當前提交
83581eaea0

+ 12 - 2
src/api/animation.ts

@@ -20,6 +20,7 @@ type ServiceAnimationModel = {
   actions: string
   subtitles: string
   paths: string
+  mat?: string
 }
 
 export type AnimationModelAction = {
@@ -73,6 +74,13 @@ export interface AnimationModel {
   actions: AnimationModelAction[];
   subtitles: AnimationModelSubtitle[];
   paths: AnimationModelPath[];
+  mat?: {
+    position?: SceneLocalPos;
+    scale?: number;
+    rotation?: SceneLocalPos & {w: number};
+    quaternion?: SceneLocalPos & {w: number};
+    originPosition?: SceneLocalPos;
+  };
 }
 
 export type AnimationModels = AnimationModel[];
@@ -82,7 +90,8 @@ const serviceToLocal = (serviceAM: ServiceAnimationModel): AnimationModel => ({
   frames: JSON.parse(serviceAM.frames),
   actions: JSON.parse(serviceAM.actions),
   subtitles: JSON.parse(serviceAM.subtitles),
-  paths: JSON.parse(serviceAM.paths)
+  paths: JSON.parse(serviceAM.paths),
+  mat: serviceAM.mat && JSON.parse(serviceAM.mat) 
 });
 
 const localToService = (am: AnimationModel): ServiceAnimationModel => ({
@@ -90,7 +99,8 @@ const localToService = (am: AnimationModel): ServiceAnimationModel => ({
   frames: JSON.stringify(am.frames),
   actions: JSON.stringify(am.actions),
   subtitles: JSON.stringify(am.subtitles),
-  paths: JSON.stringify(am.paths)
+  paths: JSON.stringify(am.paths),
+  mat: am.mat ? JSON.stringify(am.mat) : undefined
 });
 
 export const fetchAnimationModels = async () => {

+ 82 - 30
src/sdk/association/animation.ts

@@ -16,7 +16,7 @@ import {
 } from "../sdk";
 import { computed, nextTick, reactive, ref, watch, watchEffect } from "vue";
 import { ams } from "@/store/animation";
-import { mergeFuns } from "@/components/drawing/hook";
+import { mergeFuns, uuid } from "@/components/drawing/hook";
 import { getPathNode } from "./path";
 import { diffArrayChange, mount } from "@/utils";
 import { Pos } from "@/utils/event";
@@ -31,6 +31,7 @@ export const amMap: Record<
   string,
   {
     am?: AnimationModel3D;
+    globalFrame?: AnimationModelFrame3D;
     frames: Record<string, AnimationModelFrame3D>;
     actions: Record<string, AnimationModelAction3D>;
     paths: Record<string, AnimationModelPath3D>;
@@ -106,7 +107,7 @@ export const addAM = (data: AnimationModel): Promise<AnimationModel3D> => {
 export const addFrame = (
   data: AnimationModelFrame
 ): Promise<AnimationModelFrame3D> => {
-  console.log('addFrame')
+  console.log("addFrame");
   const am = ams.value.find((item) =>
     item.frames.find(({ id }) => id === data.id)
   );
@@ -123,7 +124,6 @@ export const addFrame = (
     ([map, exists]) => {
       if (!map.am) return;
       if (exists && !map.frames[data.id]) {
-        console.log('add?')
         map.frames[data.id] = map.am.addFrame(data);
       } else if (!exists && map.frames[data.id]) {
         map.frames[data.id].destroy();
@@ -192,7 +192,7 @@ export const addAction = (
   const stopAttrib = mergeFuns(
     watchEffect(() => amMap[key].actions[data.id]?.changeTime(data.time)),
     watchEffect(() => {
-      amMap[key].actions[data.id]?.changeAmplitude(data.amplitude)
+      amMap[key].actions[data.id]?.changeAmplitude(data.amplitude);
     }),
     watchEffect(() => amMap[key].actions[data.id]?.changeSpeed(data.speed)),
     watchEffect(() =>
@@ -319,25 +319,34 @@ export const addSubtitle = (data: AnimationModelSubtitle) => {
     { immediate: true }
   );
 
-  
   const stopAttrib = mergeFuns(
-    watch([currentTime, () => amMap[am.id].am, size, play], (_a, _b, onCleanup) => {
-      if (!play.value && router.currentRoute.value.name !== RoutesName.animation) {
-        show.value = false
-      } else if (
-        currentTime.value >= data.time &&
-        currentTime.value <= (data.time + data.duration) 
-      ) {
-        const update = () => pixel.value = amMap[am.id].am?.getCurrentSubtitlePixel(size.value);
-        update()
-        show.value = true;
-
-        _sdk.sceneBus.on("cameraChange", update);
-        onCleanup(() => _sdk.sceneBus.off("cameraChange", update))
-      } else {
-        show.value = false;
-      }
-    }, {immediate: true})
+    watch(
+      [currentTime, () => amMap[am.id].am, size, play],
+      (_a, _b, onCleanup) => {
+        if (
+          !play.value &&
+          router.currentRoute.value.name !== RoutesName.animation
+        ) {
+          show.value = false;
+        } else if (
+          currentTime.value >= data.time &&
+          currentTime.value <= data.time + data.duration
+        ) {
+          const update = () =>
+            (pixel.value = amMap[am.id].am?.getCurrentSubtitlePixel(
+              size.value
+            ));
+          update();
+          show.value = true;
+
+          _sdk.sceneBus.on("cameraChange", update);
+          onCleanup(() => _sdk.sceneBus.off("cameraChange", update));
+        } else {
+          show.value = false;
+        }
+      },
+      { immediate: true }
+    )
   );
 
   const stopWatch = watch(
@@ -355,13 +364,19 @@ export const addSubtitle = (data: AnimationModelSubtitle) => {
 
 export const endTime = computed(() => {
   const amsEndTime = ams.value.map((am) => {
-    const endPoints = [...am.frames, ...am.actions, ...am.subtitles, ...am.paths].map(
-      (item) => item.time + (item.duration || 0)
-    );
+    const endPoints = [
+      ...am.frames,
+      ...am.actions,
+      ...am.subtitles,
+      ...am.paths,
+    ].map((item) => item.time + (item.duration || 0));
     return Math.max(...endPoints);
-  })
-  return Math.max(...amsEndTime) + (animationGroup.delayEndTime && animationGroup.delayEndTime() || 0)
-})
+  });
+  return (
+    Math.max(...amsEndTime) +
+    ((animationGroup.delayEndTime && animationGroup.delayEndTime()) || 0)
+  );
+});
 
 export const play = ref(false);
 watch(play, (_a, _b, onCleanup) => {
@@ -373,7 +388,7 @@ watch(play, (_a, _b, onCleanup) => {
       }
     })
   );
-})
+});
 
 export const currentTime = ref(0);
 export const associationAnimation = (sdk: SDK, el: HTMLDivElement) => {
@@ -426,5 +441,42 @@ export const associationAnimation = (sdk: SDK, el: HTMLDivElement) => {
     }
   );
 
-  
+  let cleanupMap: Record<string, () => void> = {}
+  watch(
+    () => {
+      const gAms = ams.value.filter(am => !am.frames.length && amMap[am.id]?.am)
+      return gAms
+    },
+    (am3ds, oldAm3ds = []) => {
+      const { added, deleted } = diffArrayChange(am3ds, oldAm3ds)
+      for (const am of added) {
+        const am3d = amMap[am.id]
+        if (!am3d || !am3d.am) continue;
+        console.error('global-frame', am3d.am)
+        const frame = am3d.am!.addFrame({
+          id: uuid(),
+          mat: am.mat || am3d.am.getModelPose(),
+          name: "global-frame",
+          time: 0,
+        });
+        am3d.globalFrame = frame
+        cleanupMap[am.id] = mergeFuns(
+          watchEffect(() => {
+            am.mat && frame.setMat(am.mat)
+            console.log('set-global-frame', am.mat)
+          }),
+          () => {
+            console.error('destroy-global-frame', am3d.am)
+            frame.destroy();
+            am3d.globalFrame = undefined;
+            delete cleanupMap[am.id]
+          }
+        )
+      }
+      for (const am of deleted) {
+        cleanupMap[am.id] && cleanupMap[am.id]()
+      }
+    },
+    { flush: "post", immediate: true }
+  );
 };

+ 17 - 14
src/views/animation/index.vue

@@ -20,6 +20,13 @@
       @add-action="(preset) => add('actions', preset)"
       @apply-global="k => ams.forEach((am: any) => (am[k] = focusAM![k]))"
     />
+    <GlobalFrame
+      v-if="activeAttrib?.key !== 'frames'"
+      :data="{ id: '0', name: 'global-frame', time: 0 }"
+      :frame-action="frameAction"
+      @change-frame-action="(action) => (frameAction = action.action)"
+    />
+
     <BottomPano>
       <Bottom
         :am="focusAM"
@@ -34,17 +41,13 @@
 <script lang="ts" setup>
 import Left from "./left.vue";
 import Right from "./right/index.vue";
+import GlobalFrame from "./right/frame.vue";
 import Bottom from "./bottom.vue";
 import BottomPano from "@/layout/bottom-pano.vue";
 import router from "@/router";
 import { enterEdit } from "@/store";
 import { useViewStack } from "@/hook";
-import {
-  ams,
-  AnimationModel,
-  autoSaveAnimationModel,
-  initialAnimationModels,
-} from "@/store/animation";
+import { ams, AnimationModel, initialAnimationModels } from "@/store/animation";
 import { computed, nextTick, reactive, ref, watch, watchEffect } from "vue";
 import { Active } from "./type";
 import { getAddTLItemAttr } from "@/components/drawing-time-line/check";
@@ -53,12 +56,13 @@ import { mergeFuns, uuid } from "@/components/drawing/hook";
 import { title } from "./type";
 import { amMap, getAMKey, currentTime } from "@/sdk/association/animation";
 import { AnimationModel3D } from "@/sdk";
-import { bottomBarHeightStack, showBottomBarStack } from "@/env";
+import { showBottomBarStack, showRightPanoStack } from "@/env";
 
 enterEdit(() => router.back());
 initialAnimationModels();
 // useViewStack(autoSaveAnimationModel);
 useViewStack(() => showBottomBarStack.push(ref(true)));
+useViewStack(() => showRightPanoStack.push(computed(() => !!focusAM.value)));
 
 const focusAM = ref<AnimationModel>();
 const activeAttrib = ref<Active>();
@@ -84,17 +88,16 @@ const asyncOper = (item: AnimationModel, oper: (obj: AnimationModel3D) => void)
 };
 
 watchEffect((onCleanup) => {
-  if (!amM.value || activeAttrib.value?.key !== "frames") return;
-  const frame = focusAM.value!.frames[activeAttrib.value.ndx];
+  if (!amM.value) return;
   const am3d = amM.value.am;
   if (!am3d) return;
+
   const updateMat = () => {
-    if (
-      activeAttrib.value?.key === "frames" &&
-      focusAM.value!.frames[activeAttrib.value.ndx] === frame
-    ) {
-      console.log("setMat");
+    if (activeAttrib.value?.key === "frames") {
+      const frame = focusAM.value!.frames[activeAttrib.value.ndx];
       frame.mat = JSON.parse(JSON.stringify(am3d.getModelPose()));
+    } else {
+      focusAM.value!.mat = JSON.parse(JSON.stringify(am3d.getModelPose()));
     }
   };
 

+ 1 - 1
src/views/animation/right/frame.vue

@@ -41,7 +41,7 @@ defineEmits<{
   position: absolute;
   top: calc(var(--editor-head-height) + var(--header-top));
   margin: 20px;
-  right: 0;
+  right: calc(var(--editor-menu-right) + var(--editor-toolbox-width));
   border-radius: 4px;
   height: 40px;
   background: rgba(27, 27, 28, 0.8);

+ 6 - 0
src/views/animation/right/index.vue

@@ -48,6 +48,9 @@ import Subtitle from "./subtitle.vue";
 import { checkTLItem, getAddTLItemAttr } from "@/components/drawing-time-line/check";
 import { Message } from "bill/expose-common";
 import { AnimationModel, AnimationModelFrame } from "@/store/animation";
+import { useViewStack } from "@/hook";
+import { showRightPanoStack } from "@/env";
+import { computed } from "vue";
 
 const props = defineProps<{
   am: AnimationModel;
@@ -61,6 +64,9 @@ const emit = defineEmits<{
   (e: "changeFrameAction", d: { action?: string; frame: AnimationModelFrame }): void;
 }>();
 
+useViewStack(() =>
+  showRightPanoStack.push(computed(() => props.activeAttrib?.key !== "frames"))
+);
 const comps = {
   actions: Action,
   paths: Path,