diff --git a/app/pages/PageEnigma/PageEnigmaComponent.tsx b/app/pages/PageEnigma/PageEnigmaComponent.tsx index 9bcf8a62d1..b1a36217e6 100644 --- a/app/pages/PageEnigma/PageEnigmaComponent.tsx +++ b/app/pages/PageEnigma/PageEnigmaComponent.tsx @@ -22,21 +22,12 @@ import { appUiInitialStateValues, APPUI_VIEW_MODES, } from "./reducers"; -import { - canDrop, - clipLength, - currPosition, - dragId, - overTimeline, - scale, - timelineHeight, -} from "~/pages/PageEnigma/store"; +import { timelineHeight } from "~/pages/PageEnigma/store"; import { useSignals } from "@preact/signals-react/runtime"; +import { DragComponent } from "~/pages/PageEnigma/comps/DragComponent/DragComponent"; export const PageEnigmaComponent = () => { useSignals(); - const { currX, currY } = currPosition.value; - const [appUiState, dispatchAppUiState] = useReducer( appUiReducer, appUiInitialStateValues, @@ -129,22 +120,7 @@ export const PageEnigmaComponent = () => { -
+
); }; diff --git a/app/pages/PageEnigma/comps/ControlPanelSceneObject/ControlPanelSceneObject.tsx b/app/pages/PageEnigma/comps/ControlPanelSceneObject/ControlPanelSceneObject.tsx index 245db0b3bb..3b8f4a764d 100644 --- a/app/pages/PageEnigma/comps/ControlPanelSceneObject/ControlPanelSceneObject.tsx +++ b/app/pages/PageEnigma/comps/ControlPanelSceneObject/ControlPanelSceneObject.tsx @@ -8,7 +8,7 @@ import { import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { AppUiContext } from "../../contexts/AppUiContext"; -import { Button, H4, H5, H6, InputVector } from "~/components"; +import { Button, H5, InputVector } from "~/components"; import { XYZ } from "../../datastructures/common"; import { ACTION_TYPES } from "../../reducers/appUiReducer/types"; @@ -16,6 +16,7 @@ import { ACTION_TYPES } from "../../reducers/appUiReducer/types"; import { QueueNames } from "../../Queue/QueueNames"; import Queue from "~/pages/PageEnigma/Queue/Queue"; import { toTimelineActions } from "../../Queue/toTimelineActions"; +import { QueueKeyframe } from "~/pages/PageEnigma/models/track"; export const ControlPanelSceneObject = () => { const [appUiState, dispatchAppUiState] = useContext(AppUiContext); @@ -24,24 +25,21 @@ export const ControlPanelSceneObject = () => { const scale = appUiState?.currentSceneObject.objectVectors.scale; const currentSceneObject = appUiState?.currentSceneObject; - const handlePositionChange = (xyz: XYZ) => { if (appUiState) { - - if (currentSceneObject == null) { - console.log("Missing Scene Object Position"); - return; + console.log("Missing Scene Object Position"); + return; } dispatchAppUiState({ type: ACTION_TYPES.UPDATE_CONTROLPANELS_SCENEOBJECT, payload: { currentSceneObject: { - group:currentSceneObject.group, - object_uuid:currentSceneObject.object_uuid, - object_name:currentSceneObject.object_name, - version:currentSceneObject.version, + group: currentSceneObject.group, + object_uuid: currentSceneObject.object_uuid, + object_name: currentSceneObject.object_name, + version: currentSceneObject.version, objectVectors: { position: { ...xyz }, rotation: appUiState.currentSceneObject.objectVectors.rotation, @@ -54,20 +52,19 @@ export const ControlPanelSceneObject = () => { }; const handleRotationChange = (xyz: XYZ) => { if (appUiState) { - - if (currentSceneObject == null) { - console.log("Missing Scene Object Rotation"); - return; - } + if (currentSceneObject == null) { + console.log("Missing Scene Object Rotation"); + return; + } dispatchAppUiState({ type: ACTION_TYPES.UPDATE_CONTROLPANELS_SCENEOBJECT, payload: { currentSceneObject: { - group:currentSceneObject.group, - object_uuid:currentSceneObject.object_uuid, - object_name:currentSceneObject.object_name, - version:currentSceneObject.version, + group: currentSceneObject.group, + object_uuid: currentSceneObject.object_uuid, + object_name: currentSceneObject.object_name, + version: currentSceneObject.version, objectVectors: { position: appUiState.currentSceneObject.objectVectors.position, rotation: { ...xyz }, @@ -80,19 +77,18 @@ export const ControlPanelSceneObject = () => { }; const handleScaleChange = (xyz: XYZ) => { if (appUiState) { - if (currentSceneObject == null) { - console.log("Missing Scene Object Scale"); - return; + console.log("Missing Scene Object Scale"); + return; } dispatchAppUiState({ type: ACTION_TYPES.UPDATE_CONTROLPANELS_SCENEOBJECT, payload: { currentSceneObject: { - group:currentSceneObject.group, - object_uuid:currentSceneObject.object_uuid, - object_name:currentSceneObject.object_name, - version:currentSceneObject.version, + group: currentSceneObject.group, + object_uuid: currentSceneObject.object_uuid, + object_name: currentSceneObject.object_name, + version: currentSceneObject.version, objectVectors: { position: appUiState.currentSceneObject.objectVectors.position, rotation: appUiState.currentSceneObject.objectVectors.rotation, @@ -101,52 +97,53 @@ export const ControlPanelSceneObject = () => { }, }, }); - - } + } }; const handleOnAddKeyFrame = () => { if (appUiState) { + if ( + position == null || + rotation == null || + scale == null || + currentSceneObject == null + ) { + return; + } - if (position == null || - rotation == null || - scale == null || - currentSceneObject == null) - { return; } + // console.log(`Position ${position.x}`) + // console.log(`Position ${position.y}`) + // console.log(`Position ${position.z}`) - // console.log(`Position ${position.x}`) - // console.log(`Position ${position.y}`) - // console.log(`Position ${position.z}`) + // console.log(`Rotation ${rotation.x}`) + // console.log(`Rotation ${rotation.y}`) + // console.log(`Rotation ${rotation.z}`) - // console.log(`Rotation ${rotation.x}`) - // console.log(`Rotation ${rotation.y}`) - // console.log(`Rotation ${rotation.z}`) - - // console.log(`Scale ${scale.x}`) - // console.log(`Scale ${scale.y}`) - // console.log(`Scale ${scale.z}`) + // console.log(`Scale ${scale.x}`) + // console.log(`Scale ${scale.y}`) + // console.log(`Scale ${scale.z}`) - console.log(`Group ${currentSceneObject.group}`) - console.log(`Object UUID ${currentSceneObject.object_uuid}`) - console.log(`Object Name ${currentSceneObject.object_name}`) - console.log(`Object Version ${currentSceneObject.version}`) + console.log(`Group ${currentSceneObject.group}`); + console.log(`Object UUID ${currentSceneObject.object_uuid}`); + console.log(`Object Name ${currentSceneObject.object_name}`); + console.log(`Object Version ${currentSceneObject.version}`); - Queue.publish({queueName:QueueNames.TO_TIMELINE, action: toTimelineActions.ADD_KEYFRAME, - data: { - group:currentSceneObject.group, - object_uuid:currentSceneObject.object_uuid, - object_name:currentSceneObject.object_name, - version:currentSceneObject.version, + Queue.publish({ + queueName: QueueNames.TO_TIMELINE, + action: toTimelineActions.ADD_KEYFRAME, + data: { + group: currentSceneObject.group, + object_uuid: currentSceneObject.object_uuid, + object_name: currentSceneObject.object_name, + version: 1, - position: currentSceneObject.objectVectors.position, - rotation: currentSceneObject.objectVectors.rotation, - scale:currentSceneObject.objectVectors.scale - } - }) - - + position: currentSceneObject.objectVectors.position, + rotation: currentSceneObject.objectVectors.rotation, + scale: currentSceneObject.objectVectors.scale, + } as QueueKeyframe, + }); } - } + }; return ( {
- - )} + {/*{selectedItem === clip.clip_uuid && (*/} + {/* */} + {/* */} + {/* */} + {/*)}*/} ); }; diff --git a/app/pages/PageEnigma/comps/Timeline/utils/useQueueHandler.ts b/app/pages/PageEnigma/comps/Timeline/utils/useQueueHandler.ts index 3c9cd3645e..f0b4d0a97a 100644 --- a/app/pages/PageEnigma/comps/Timeline/utils/useQueueHandler.ts +++ b/app/pages/PageEnigma/comps/Timeline/utils/useQueueHandler.ts @@ -23,7 +23,7 @@ export function useQueueHandler() { const { addKeyframe } = useContext(TrackContext); const handleFromEngineActions = useCallback(({ action, data }: Arguments) => { - console.log(`FROM ENGINE:${action} ${data}`); + console.log("FROM ENGINE", action, data); switch (action) { case fromEngineActions.UPDATE_TIME: currentTime.value = (data as UpdateTime).currentTime; @@ -34,10 +34,11 @@ export function useQueueHandler() { }, []); const handleToTimelineActions = useCallback(({ action, data }: Arguments) => { - console.log(`FROM ENGINE:${action} ${data}`); + console.log("TO TIMELINE", action, data); switch (action) { case toTimelineActions.ADD_KEYFRAME: - addKeyframe(data as QueueKeyframe); + console.log(1); + addKeyframe(data as QueueKeyframe, currentTime.value); break; default: throw new Error(`Unknown action ${action}`); diff --git a/app/pages/PageEnigma/comps/TimerGrid/TimerGrid.tsx b/app/pages/PageEnigma/comps/TimerGrid/TimerGrid.tsx new file mode 100644 index 0000000000..6a160f6896 --- /dev/null +++ b/app/pages/PageEnigma/comps/TimerGrid/TimerGrid.tsx @@ -0,0 +1,55 @@ +import { filmLength, fullHeight, scale } from "~/pages/PageEnigma/store"; +import { Fragment } from "react"; + +export const TimerGrid = () => { + const sectionWidth = 60 * 4 * scale.value; + + return ( +
+ {Array(filmLength.value) + .fill(0) + .map((_, index) => ( + +
+ 00:{index < 10 ? "0" + index.toString() : index.toString()} +
+
+ + ))} +
+ 00: + {filmLength.value < 10 + ? "0" + filmLength.value.toString() + : filmLength.value.toString()} +
+
+
+ ); +}; diff --git a/app/pages/PageEnigma/contexts/TrackContext/TrackContext.tsx b/app/pages/PageEnigma/contexts/TrackContext/TrackContext.tsx index 66b0899179..38a514de09 100644 --- a/app/pages/PageEnigma/contexts/TrackContext/TrackContext.tsx +++ b/app/pages/PageEnigma/contexts/TrackContext/TrackContext.tsx @@ -10,63 +10,14 @@ import { } from "~/pages/PageEnigma/models/track"; export const TrackContext = createContext<{ - // timeline characters and it's functions - characters: CharacterGroup[]; - updateCharacters: (options: { - type: "animations" | "positions" | "lipSync"; - id: string; - length: number; - offset: number; - }) => void; - toggleLipSyncMute: (characterId: string) => void; - addCharacterAnimation: (options: { - clipId: string; - characterId: string; - animationClips: MediaClip[]; - offset: number; - }) => void; - addCharacterAudio: (options: { - clipId: string; - characterId: string; - audioClips: MediaClip[]; - offset: number; - }) => void; - selectCharacterClip: (clipId: string) => void; - deleteCharacterClip: (clipId: string) => void; - - // timeline camera group - camera: CameraGroup | null; - updateCamera: (options: { id: string; offset: number }) => void; - selectCameraKeyframe: (clipId: string) => void; - - // timeline global audio group - audio: AudioGroup | null; - updateAudio: (options: { - id: string; - length: number; - offset: number; - }) => void; - toggleAudioMute: () => void; - addGlobalAudio: ( - clipId: string, - animationClips: MediaClip[], - offset: number, - ) => void; - selectAudioClip: (clipId: string) => void; - deleteAudioClip: (clipId: string) => void; - - // timeline objects group - objects: ObjectGroup; - updateObject: (options: { id: string; offset: number }) => void; + // keyframes + addKeyframe: (keyframe: QueueKeyframe, offset: number) => void; + deleteKeyframe: (keyframe: Keyframe) => void; // current - only select one item - will be replaced selectedItem: string | null; selectItem: (itemId: string) => void; - // keyframes - addKeyframe: (keyframe: QueueKeyframe, offset: number) => void; - deleteKeyframe: (keyframe: Keyframe) => void; - // sidebar clips animationClips: MediaClip[]; audioClips: MediaClip[]; @@ -79,28 +30,6 @@ export const TrackContext = createContext<{ ) => void; endDrag: () => void; }>({ - characters: [], - updateCharacters: () => {}, - toggleLipSyncMute: () => {}, - addCharacterAnimation: () => {}, - addCharacterAudio: () => {}, - selectCharacterClip: () => {}, - deleteCharacterClip: () => {}, - - camera: null, - updateCamera: () => {}, - selectCameraKeyframe: () => {}, - - audio: null, - updateAudio: () => {}, - toggleAudioMute: () => {}, - selectAudioClip: () => {}, - addGlobalAudio: () => {}, - deleteAudioClip: () => {}, - - objects: { id: "", objects: [] }, - updateObject: () => {}, - addKeyframe: () => {}, deleteKeyframe: () => {}, diff --git a/app/pages/PageEnigma/contexts/TrackContext/TrackProvider.tsx b/app/pages/PageEnigma/contexts/TrackContext/TrackProvider.tsx index 49216b1689..106d839efe 100644 --- a/app/pages/PageEnigma/contexts/TrackContext/TrackProvider.tsx +++ b/app/pages/PageEnigma/contexts/TrackContext/TrackProvider.tsx @@ -1,12 +1,9 @@ import { TrackContext } from "~/pages/PageEnigma/contexts/TrackContext/TrackContext"; import { ReactNode, useCallback, useEffect, useMemo, useState } from "react"; -import useUpdateCharacters from "~/pages/PageEnigma/contexts/TrackContext/utils/useUpdateCharacters"; -import useUpdateCamera from "~/pages/PageEnigma/contexts/TrackContext/utils/useUpdateCamera"; -import useUpdateAudio from "~/pages/PageEnigma/contexts/TrackContext/utils/useUpdateAudio"; -import useUpdateObject from "~/pages/PageEnigma/contexts/TrackContext/utils/useUpdateObject"; import { ClipType, MediaClip } from "~/pages/PageEnigma/models/track"; import useUpdateDragDrop from "~/pages/PageEnigma/contexts/TrackContext/utils/useUpdateDragDrop"; import { + addCharacterAnimation, canDrop, dragId, dragType, @@ -21,16 +18,6 @@ interface Props { } export const TrackProvider = ({ children }: Props) => { - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const { addCharacterKeyframe, deleteCharacterKeyframe, ...characters } = - useUpdateCharacters(); - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const { addCameraKeyframe, deleteCameraKeyframe, ...camera } = - useUpdateCamera(); - const audio = useUpdateAudio(); - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const { addObjectKeyframe, deleteObjectKeyframe, ...objects } = - useUpdateObject(); const keyframes = useUpdateKeyframe(); const { endDrag, ...dragDrop } = useUpdateDragDrop(); @@ -65,7 +52,7 @@ export const TrackProvider = ({ children }: Props) => { const dropClip = useCallback(() => { if (canDrop.value) { if (dragType.value === "animations") { - characters.addCharacterAnimation({ + addCharacterAnimation({ clipId: dragId.value!, characterId: dropId.value, animationClips, @@ -74,7 +61,7 @@ export const TrackProvider = ({ children }: Props) => { } } endDrag(); - }, [animationClips, characters, endDrag]); + }, [animationClips, endDrag]); const fullWidth = useMemo(() => { return length * 60 * 4 * scale; @@ -118,11 +105,6 @@ export const TrackProvider = ({ children }: Props) => { const values = useMemo(() => { return { - ...characters, - ...camera, - ...audio, - ...objects, - ...keyframes, selectItem, @@ -143,11 +125,6 @@ export const TrackProvider = ({ children }: Props) => { setTimelineHeight, }; }, [ - characters, - camera, - audio, - objects, - keyframes, selectItem, diff --git a/app/pages/PageEnigma/contexts/TrackContext/utils/useUpdateAudio.ts b/app/pages/PageEnigma/contexts/TrackContext/utils/useUpdateAudio.ts deleted file mode 100644 index 5804527373..0000000000 --- a/app/pages/PageEnigma/contexts/TrackContext/utils/useUpdateAudio.ts +++ /dev/null @@ -1,153 +0,0 @@ -import { useCallback, useState } from "react"; -import { - AudioGroup, - Clip, - ClipGroup, - ClipType, - MediaClip, -} from "~/pages/PageEnigma/models/track"; -import Queue from "~/pages/PageEnigma/Queue/Queue"; -import { QueueNames } from "~/pages/PageEnigma/Queue/QueueNames"; -import { toEngineActions } from "~/pages/PageEnigma/Queue/toEngineActions"; -import * as uuid from "uuid"; - -export default function useUpdateAudio() { - const [audio, setAudio] = useState({ - id: "AU1", - muted: false, - clips: [], - }); - - const updateAudio = useCallback( - ({ - id, - offset, - length, - }: { - id: string; - length: number; - offset: number; - }) => { - setAudio((oldAudio) => { - const newClips = [...oldAudio.clips]; - const clipIndex = newClips.findIndex((row) => row.clip_uuid === id); - if (clipIndex === -1) { - return { ...oldAudio }; - } - const clip = newClips[clipIndex]; - clip.offset = offset; - clip.length = length; - - Queue.publish({ - queueName: QueueNames.TO_ENGINE, - action: toEngineActions.UPDATE_CLIP, - data: clip, - }); - - return { - ...oldAudio, - clips: newClips, - }; - }); - }, - [], - ); - - const addGlobalAudio = useCallback( - (dragId: string, audioClips: MediaClip[], offset: number) => { - const clip = audioClips.find((row) => row.media_id === dragId); - if (!clip) { - return; - } - - const clip_uuid = uuid.v4(); - const newClip = { - ...clip, - type: ClipType.AUDIO, - group: ClipGroup.GLOBAL_AUDIO, - offset, - clip_uuid, - } as Clip; - - setAudio((oldAudio) => { - return { - ...oldAudio, - clips: [...oldAudio.clips, newClip], - }; - }); - - Queue.publish({ - queueName: QueueNames.TO_ENGINE, - action: toEngineActions.ADD_CLIP, - data: newClip, - }); - }, - [], - ); - - const toggleAudioMute = useCallback(() => { - setAudio((oldAudio) => { - Queue.publish({ - queueName: QueueNames.TO_ENGINE, - action: oldAudio?.muted ? toEngineActions.UNMUTE : toEngineActions.MUTE, - data: { - version: 1, - type: ClipType.AUDIO, - group: ClipGroup.GLOBAL_AUDIO, - }, - }); - - return { - ...oldAudio, - muted: !oldAudio.muted, - }; - }); - }, []); - - const selectAudioClip = useCallback((clipId: string) => { - setAudio((oldAudio) => { - return { - ...oldAudio, - clips: [ - ...oldAudio.clips.map((clip) => { - return { - ...clip, - selected: - clip.clip_uuid === clipId ? !clip.selected : clip.selected, - }; - }), - ], - }; - }); - }, []); - - const deleteAudioClip = useCallback((clipId: string) => { - setAudio((oldAudio) => { - return { - ...oldAudio, - clips: [ - ...oldAudio.clips.filter((clip) => { - if (clip.clip_uuid === clipId) { - Queue.publish({ - queueName: QueueNames.TO_ENGINE, - action: toEngineActions.DELETE_CLIP, - data: clip!, - }); - return false; - } - return true; - }), - ], - }; - }); - }, []); - - return { - audio, - updateAudio, - toggleAudioMute, - selectAudioClip, - addGlobalAudio, - deleteAudioClip, - }; -} diff --git a/app/pages/PageEnigma/contexts/TrackContext/utils/useUpdateCamera.ts b/app/pages/PageEnigma/contexts/TrackContext/utils/useUpdateCamera.ts deleted file mode 100644 index 741e71a394..0000000000 --- a/app/pages/PageEnigma/contexts/TrackContext/utils/useUpdateCamera.ts +++ /dev/null @@ -1,122 +0,0 @@ -import { useCallback, useState } from "react"; -import { - CameraGroup, - Keyframe, - QueueKeyframe, -} from "~/pages/PageEnigma/models/track"; -import Queue from "~/pages/PageEnigma/Queue/Queue"; -import { QueueNames } from "~/pages/PageEnigma/Queue/QueueNames"; -import { toEngineActions } from "~/pages/PageEnigma/Queue/toEngineActions"; -import * as uuid from "uuid"; - -export default function useUpdateCamera() { - const [camera, setCamera] = useState({ - id: "CA1", - keyframes: [], - }); - - const updateCamera = useCallback( - ({ id, offset }: { id: string; offset: number }) => { - setCamera((oldCamera) => { - const newKeyframes = [...oldCamera.keyframes]; - const keyframe = newKeyframes.find((row) => row.keyframe_uuid === id); - if (!keyframe) { - return { ...oldCamera }; - } - keyframe.offset = offset; - - Queue.publish({ - queueName: QueueNames.TO_ENGINE, - action: toEngineActions.UPDATE_KEYFRAME, - data: keyframe, - }); - - return { - ...oldCamera, - clips: newKeyframes, - }; - }); - }, - [], - ); - - const addCameraKeyframe = useCallback( - (keyframe: QueueKeyframe, offset: number) => { - const newKeyframe = { - version: keyframe.version, - keyframe_uuid: uuid.v4(), - group: keyframe.group, - object_uuid: keyframe.object_uuid, - offset, - position: keyframe.position, - rotation: keyframe.rotation, - scale: keyframe.scale, - selected: false, - } as Keyframe; - - setCamera((oldCamera) => { - return { - ...oldCamera, - keyframes: [...oldCamera.keyframes, newKeyframe].sort( - (keyFrameA, keyframeB) => keyFrameA.offset - keyframeB.offset, - ), - }; - }); - - Queue.publish({ - queueName: QueueNames.TO_ENGINE, - action: toEngineActions.ADD_KEYFRAME, - data: newKeyframe, - }); - }, - [], - ); - - const selectCameraKeyframe = useCallback((keyframeId: string) => { - setCamera((oldCamera) => { - return { - ...oldCamera, - clips: [ - ...oldCamera.keyframes.map((keyframe) => { - return { - ...keyframe, - selected: - keyframe.keyframe_uuid === keyframeId - ? !keyframe.selected - : keyframe.selected, - }; - }), - ], - }; - }); - }, []); - - const deleteCameraKeyframe = useCallback((keyframeId: string) => { - setCamera((oldCamera) => { - return { - ...oldCamera, - keyframes: [ - ...oldCamera.keyframes.filter((keyframe) => { - if (keyframe.keyframe_uuid === keyframeId) { - Queue.publish({ - queueName: QueueNames.TO_ENGINE, - action: toEngineActions.DELETE_KEYFRAME, - data: keyframe, - }); - return false; - } - return true; - }), - ], - }; - }); - }, []); - - return { - camera, - updateCamera, - selectCameraKeyframe, - addCameraKeyframe, - deleteCameraKeyframe, - }; -} diff --git a/app/pages/PageEnigma/contexts/TrackContext/utils/useUpdateCharacters.ts b/app/pages/PageEnigma/contexts/TrackContext/utils/useUpdateCharacters.ts deleted file mode 100644 index 27a4a14435..0000000000 --- a/app/pages/PageEnigma/contexts/TrackContext/utils/useUpdateCharacters.ts +++ /dev/null @@ -1,383 +0,0 @@ -import { useCallback, useState } from "react"; -import { - Clip, - CharacterGroup, - ClipGroup, - ClipType, - MediaClip, - QueueKeyframe, - Keyframe, -} from "~/pages/PageEnigma/models/track"; -import * as uuid from "uuid"; -import Queue from "~/pages/PageEnigma/Queue/Queue"; -import { QueueNames } from "~/pages/PageEnigma/Queue/QueueNames"; -import { toEngineActions } from "~/pages/PageEnigma/Queue/toEngineActions"; - -export default function useUpdateCharacters() { - const [characters, setCharacters] = useState([ - { - id: "CH1", - muted: false, - animationClips: [], - positionKeyframes: [], - lipSyncClips: [], - }, - ]); - - const updateCharacters = useCallback( - ({ - type, - id, - offset, - length, - }: { - type: "animations" | "positions" | "lipSync"; - id: string; - length: number; - offset: number; - }) => { - if (type === "animations") { - setCharacters((oldCharacters) => { - return oldCharacters.map((character) => { - const newAnimationClips = [...character.animationClips]; - const clipIndex = newAnimationClips.findIndex( - (row) => row.clip_uuid === id, - ); - if (clipIndex === -1) { - return { ...character }; - } - const clip = newAnimationClips[clipIndex]; - clip.offset = offset; - clip.length = length; - - Queue.publish({ - queueName: QueueNames.TO_ENGINE, - action: toEngineActions.UPDATE_CLIP, - data: clip, - }); - - return { - ...character, - animationClips: newAnimationClips, - }; - }); - }); - return; - } - - if (type === "positions") { - setCharacters((oldCharacters) => { - return oldCharacters.map((character) => { - const newPositionKeyframes = [...character.positionKeyframes]; - const keyframeIndex = newPositionKeyframes.findIndex( - (row) => row.keyframe_uuid === id, - ); - if (keyframeIndex === -1) { - return { ...character }; - } - const keyframe = newPositionKeyframes[keyframeIndex]; - keyframe.offset = offset; - - Queue.publish({ - queueName: QueueNames.TO_ENGINE, - action: toEngineActions.UPDATE_CLIP, - data: keyframe, - }); - - return { - ...character, - positionClips: newPositionKeyframes, - }; - }); - }); - return; - } - if (type === "lipSync") { - setCharacters((oldCharacters) => { - return oldCharacters.map((character) => { - const newLipSyncClips = [...character.lipSyncClips]; - const clipIndex = newLipSyncClips.findIndex( - (row) => row.clip_uuid === id, - ); - if (clipIndex === -1) { - return { ...character }; - } - const clip = newLipSyncClips[clipIndex]; - clip.offset = offset; - clip.length = length; - - Queue.publish({ - queueName: QueueNames.TO_ENGINE, - action: toEngineActions.UPDATE_CLIP, - data: clip, - }); - - return { - ...character, - lipSyncClips: newLipSyncClips, - }; - }); - }); - } - }, - [], - ); - - const addCharacterAnimation = useCallback( - ({ - clipId, - characterId, - animationClips, - offset, - }: { - clipId: string; - characterId: string; - animationClips: MediaClip[]; - offset: number; - }) => { - const clip = animationClips.find((row) => row.media_id === clipId); - if (!clip) { - return; - } - - const clip_uuid = uuid.v4(); - const newClip = { - ...clip, - group: ClipGroup.CHARACTER, - offset, - clip_uuid, - object_uuid: characterId, - } as Clip; - - setCharacters((oldCharacters) => { - return oldCharacters.map((character) => { - if (character.id !== characterId) { - return { ...character }; - } - return { - ...character, - animationClips: [...character.animationClips, newClip].sort( - (clipA, clipB) => clipA.offset - clipB.offset, - ), - }; - }); - }); - - Queue.publish({ - queueName: QueueNames.TO_ENGINE, - action: toEngineActions.ADD_CLIP, - data: newClip, - }); - }, - [], - ); - - const addCharacterAudio = useCallback( - ({ - clipId, - characterId, - audioClips, - offset, - }: { - clipId: string; - characterId: string; - audioClips: MediaClip[]; - offset: number; - }) => { - const clip = audioClips.find((row) => row.media_id === clipId); - if (!clip) { - return; - } - - const clip_uuid = uuid.v4(); - const newClip = { - ...clip, - type: ClipType.AUDIO, - group: ClipGroup.CHARACTER, - offset, - clip_uuid, - object_uuid: characterId, - } as Clip; - - setCharacters((oldCharacters) => { - return oldCharacters.map((character) => { - if (character.id !== characterId) { - return { ...character }; - } - return { - ...character, - lipSyncClips: [...character.lipSyncClips, newClip].sort( - (clipA, clipB) => clipA.offset - clipB.offset, - ), - }; - }); - }); - - Queue.publish({ - queueName: QueueNames.TO_ENGINE, - action: toEngineActions.ADD_CLIP, - data: newClip, - }); - }, - [], - ); - - const addCharacterKeyframe = useCallback( - (keyframe: QueueKeyframe, offset: number) => { - const newKeyframe = { - version: keyframe.version, - keyframe_uuid: uuid.v4(), - group: keyframe.group, - object_uuid: keyframe.object_uuid, - offset, - position: keyframe.position, - rotation: keyframe.rotation, - scale: keyframe.scale, - selected: false, - } as Keyframe; - - setCharacters((oldCharacters) => { - return oldCharacters.map((character) => { - if (character.id !== keyframe.object_uuid) { - return { ...character }; - } - return { - ...character, - positionKeyframes: [ - ...character.positionKeyframes, - newKeyframe, - ].sort( - (keyFrameA, keyframeB) => keyFrameA.offset - keyframeB.offset, - ), - }; - }); - }); - - Queue.publish({ - queueName: QueueNames.TO_ENGINE, - action: toEngineActions.ADD_KEYFRAME, - data: newKeyframe, - }); - }, - [], - ); - - const toggleLipSyncMute = useCallback((characterId: string) => { - setCharacters((oldCharacters) => { - return oldCharacters.map((character) => { - if (character.id === characterId) { - Queue.publish({ - queueName: QueueNames.TO_ENGINE, - action: character?.muted - ? toEngineActions.UNMUTE - : toEngineActions.MUTE, - data: { - version: 1, - type: ClipType.AUDIO, - group: ClipGroup.CHARACTER, - object_uuid: characterId, - }, - }); - } - - return { - ...character, - muted: - character.id === characterId ? !character.muted : character.muted, - }; - }); - }); - }, []); - - const selectCharacterClip = useCallback((clipId: string) => { - setCharacters((oldCharacters) => { - return [ - ...oldCharacters.map((character) => ({ - ...character, - animationClips: character.animationClips.map((clip) => ({ - ...clip, - selected: - clip.clip_uuid === clipId ? !clip.selected : clip.selected, - })), - positionClips: character.positionKeyframes.map((keyframe) => ({ - ...keyframe, - selected: - keyframe.keyframe_uuid === clipId - ? !keyframe.selected - : keyframe.selected, - })), - lipSyncClips: character.lipSyncClips.map((clip) => ({ - ...clip, - selected: - clip.clip_uuid === clipId ? !clip.selected : clip.selected, - })), - })), - ]; - }); - }, []); - - const deleteCharacterClip = useCallback((clipId: string) => { - setCharacters((oldCharacters) => { - return [ - ...oldCharacters.map((character) => ({ - ...character, - animationClips: character.animationClips.filter((clip) => { - if (clip.clip_uuid === clipId) { - Queue.publish({ - queueName: QueueNames.TO_ENGINE, - action: toEngineActions.DELETE_CLIP, - data: clip!, - }); - return false; - } - return true; - }), - lipSyncClips: character.lipSyncClips.filter((clip) => { - if (clip.clip_uuid === clipId) { - Queue.publish({ - queueName: QueueNames.TO_ENGINE, - action: toEngineActions.DELETE_CLIP, - data: clip!, - }); - return false; - } - return true; - }), - })), - ]; - }); - }, []); - - const deleteCharacterKeyframe = useCallback((keyframe: Keyframe) => { - setCharacters((oldCharacters) => { - return [ - ...oldCharacters.map((character) => ({ - ...character, - positionClips: character.positionKeyframes.filter((row) => { - if (row.keyframe_uuid === keyframe.keyframe_uuid) { - Queue.publish({ - queueName: QueueNames.TO_ENGINE, - action: toEngineActions.DELETE_KEYFRAME, - data: row, - }); - return false; - } - return true; - }), - })), - ]; - }); - }, []); - - return { - characters, - updateCharacters, - toggleLipSyncMute, - addCharacterAnimation, - addCharacterAudio, - selectCharacterClip, - deleteCharacterClip, - addCharacterKeyframe, - deleteCharacterKeyframe, - }; -} diff --git a/app/pages/PageEnigma/contexts/TrackContext/utils/useUpdateKeyframe.ts b/app/pages/PageEnigma/contexts/TrackContext/utils/useUpdateKeyframe.ts index ebd8ffb6a4..41153a9bcf 100644 --- a/app/pages/PageEnigma/contexts/TrackContext/utils/useUpdateKeyframe.ts +++ b/app/pages/PageEnigma/contexts/TrackContext/utils/useUpdateKeyframe.ts @@ -4,37 +4,33 @@ import { QueueKeyframe, Keyframe, } from "~/pages/PageEnigma/models/track"; -import useUpdateObject from "~/pages/PageEnigma/contexts/TrackContext/utils/useUpdateObject"; -import useUpdateCharacters from "~/pages/PageEnigma/contexts/TrackContext/utils/useUpdateCharacters"; +import { + addCharacterKeyframe, + addObjectKeyframe, + deleteCharacterKeyframe, + deleteObjectKeyframe, +} from "~/pages/PageEnigma/store"; export default function useUpdateKeyframe() { - const { addObjectKeyframe, deleteObjectKeyframe } = useUpdateObject(); - const { addCharacterKeyframe, deleteCharacterKeyframe } = - useUpdateCharacters(); + const addKeyframe = useCallback((keyframe: QueueKeyframe, offset: number) => { + console.log(keyframe, offset); + if (keyframe.group === ClipGroup.OBJECT) { + console.log(2); + addObjectKeyframe(keyframe, offset); + } + if (keyframe.group === ClipGroup.CHARACTER) { + addCharacterKeyframe(keyframe, offset); + } + }, []); - const addKeyframe = useCallback( - (keyframe: QueueKeyframe, offset: number) => { - if (keyframe.group === ClipGroup.OBJECT) { - addObjectKeyframe(keyframe, offset); - } - if (keyframe.group === ClipGroup.CHARACTER) { - addCharacterKeyframe(keyframe, offset); - } - }, - [addCharacterKeyframe, addObjectKeyframe], - ); - - const deleteKeyframe = useCallback( - (keyframe: Keyframe) => { - if (keyframe.group === ClipGroup.OBJECT) { - deleteObjectKeyframe(keyframe); - } - if (keyframe.group === ClipGroup.CHARACTER) { - deleteCharacterKeyframe(keyframe); - } - }, - [deleteCharacterKeyframe, deleteObjectKeyframe], - ); + const deleteKeyframe = useCallback((keyframe: Keyframe) => { + if (keyframe.group === ClipGroup.OBJECT) { + deleteObjectKeyframe(keyframe); + } + if (keyframe.group === ClipGroup.CHARACTER) { + deleteCharacterKeyframe(keyframe); + } + }, []); return { addKeyframe, diff --git a/app/pages/PageEnigma/contexts/TrackContext/utils/useUpdateObject.ts b/app/pages/PageEnigma/contexts/TrackContext/utils/useUpdateObject.ts deleted file mode 100644 index 4d942363d2..0000000000 --- a/app/pages/PageEnigma/contexts/TrackContext/utils/useUpdateObject.ts +++ /dev/null @@ -1,122 +0,0 @@ -import { useCallback, useState } from "react"; -import { - ObjectGroup, - QueueKeyframe, - Keyframe, -} from "~/pages/PageEnigma/models/track"; -import * as uuid from "uuid"; - -export default function useUpdateObject() { - const [objects, setObjects] = useState({ - id: "OB1", - objects: [], - }); - - const updateObject = useCallback( - ({ id, offset }: { id: string; offset: number }) => { - setObjects((oldObject) => { - return { - id: oldObject.id, - objects: oldObject.objects.map((object) => ({ - object_uuid: object.object_uuid, - name: object.name, - keyframes: object.keyframes.map((keyFrame) => ({ - ...keyFrame, - offset: keyFrame.keyframe_uuid === id ? offset : keyFrame.offset, - })), - })), - }; - }); - }, - [], - ); - - const addObjectKeyframe = useCallback( - (keyframe: QueueKeyframe, offset: number) => { - setObjects((oldObjectGroup) => { - const obj = objects.objects.find( - (row) => row.object_uuid === keyframe.object_uuid, - ); - - const newObject = obj ?? { - object_uuid: keyframe.object_uuid, - name: keyframe.object_name ?? "unknown", - keyframes: [] as Keyframe[], - }; - newObject.keyframes.push({ - version: keyframe.version, - keyframe_uuid: uuid.v4(), - group: keyframe.group, - object_uuid: keyframe.object_uuid, - offset, - position: keyframe.position, - rotation: keyframe.rotation, - scale: keyframe.scale, - selected: false, - } as Keyframe); - newObject.keyframes.sort( - (keyframeA, keyframeB) => keyframeA.offset - keyframeB.offset, - ); - - return { - ...oldObjectGroup, - objects: [ - ...oldObjectGroup.objects.filter( - (object) => object.object_uuid !== keyframe.object_uuid, - ), - newObject, - ].sort((objA, objB) => - objA.object_uuid < objB.object_uuid ? -1 : 1, - ), - }; - }); - }, - [objects], - ); - - const deleteObjectKeyframe = useCallback((keyframe: Keyframe) => { - setObjects((oldObjectGroup) => { - const obj = oldObjectGroup.objects.find( - (row) => row.object_uuid === keyframe.object_uuid, - ); - if (!obj) { - return oldObjectGroup; - } - - const newKeyframes = [ - ...obj.keyframes.filter( - (row) => row.keyframe_uuid !== keyframe.keyframe_uuid, - ), - ]; - - if (newKeyframes.length) { - return { - ...oldObjectGroup, - objects: [ - ...oldObjectGroup.objects.map((object) => ({ - ...object, - ...(object.object_uuid === keyframe.object_uuid - ? { keyframes: newKeyframes } - : {}), - })), - ], - }; - } - return { - ...oldObjectGroup, - objects: [ - ...oldObjectGroup.objects.filter( - (object) => object.object_uuid !== keyframe.object_uuid, - ), - ], - }; - }); - }, []); - - return { - objects, - updateObject, - addObjectKeyframe, - deleteObjectKeyframe, - }; -} diff --git a/app/pages/PageEnigma/store/audioGroup.ts b/app/pages/PageEnigma/store/audioGroup.ts new file mode 100644 index 0000000000..07be9b937e --- /dev/null +++ b/app/pages/PageEnigma/store/audioGroup.ts @@ -0,0 +1,136 @@ +import { + AudioGroup, + Clip, + ClipGroup, + ClipType, + MediaClip, +} from "~/pages/PageEnigma/models/track"; +import Queue from "~/pages/PageEnigma/Queue/Queue"; +import { QueueNames } from "~/pages/PageEnigma/Queue/QueueNames"; +import { toEngineActions } from "~/pages/PageEnigma/Queue/toEngineActions"; +import * as uuid from "uuid"; +import { signal } from "@preact/signals-core"; + +export const audioGroup = signal({ + id: "AG-1", + clips: [], + muted: false, +}); + +export function updateAudio({ + id, + offset, + length, +}: { + id: string; + length: number; + offset: number; +}) { + const oldAudioGroup = audioGroup.value; + const newClips = [...oldAudioGroup.clips]; + const clipIndex = newClips.findIndex((row) => row.clip_uuid === id); + if (clipIndex === -1) { + return { ...oldAudioGroup }; + } + const clip = newClips[clipIndex]; + clip.offset = offset; + clip.length = length; + + Queue.publish({ + queueName: QueueNames.TO_ENGINE, + action: toEngineActions.UPDATE_CLIP, + data: clip, + }); + + audioGroup.value = { + ...oldAudioGroup, + clips: newClips, + }; +} + +export function addGlobalAudio( + dragId: string, + audioClips: MediaClip[], + offset: number, +) { + const clip = audioClips.find((row) => row.media_id === dragId); + if (!clip) { + return; + } + + const clip_uuid = uuid.v4(); + const newClip = { + ...clip, + type: ClipType.AUDIO, + group: ClipGroup.GLOBAL_AUDIO, + offset, + clip_uuid, + } as Clip; + + const oldAudioGroup = audioGroup.value; + audioGroup.value = { + ...oldAudioGroup, + clips: [...oldAudioGroup.clips, newClip], + }; + + Queue.publish({ + queueName: QueueNames.TO_ENGINE, + action: toEngineActions.ADD_CLIP, + data: newClip, + }); +} + +export function toggleAudioMute() { + const oldAudioGroup = audioGroup.value; + Queue.publish({ + queueName: QueueNames.TO_ENGINE, + action: oldAudioGroup?.muted + ? toEngineActions.UNMUTE + : toEngineActions.MUTE, + data: { + version: 1, + type: ClipType.AUDIO, + group: ClipGroup.GLOBAL_AUDIO, + }, + }); + + audioGroup.value = { + ...oldAudioGroup, + muted: !oldAudioGroup.muted, + }; +} + +export function selectAudioClip(clipId: string) { + const oldAudioGroup = audioGroup.value; + audioGroup.value = { + ...oldAudioGroup, + clips: [ + ...oldAudioGroup.clips.map((clip) => { + return { + ...clip, + selected: clip.clip_uuid === clipId ? !clip.selected : clip.selected, + }; + }), + ], + }; +} + +export function deleteAudioClip(clipId: string) { + const oldAudioGroup = audioGroup.value; + audioGroup.value = { + ...oldAudioGroup, + clips: [ + ...oldAudioGroup.clips.filter((clip) => { + if (clip.clip_uuid === clipId) { + Queue.publish({ + queueName: QueueNames.TO_ENGINE, + action: toEngineActions.DELETE_CLIP, + data: clip!, + }); + return false; + } + return true; + }), + ], + }; +} diff --git a/app/pages/PageEnigma/store/cameraGroup.ts b/app/pages/PageEnigma/store/cameraGroup.ts new file mode 100644 index 0000000000..951cc2d9a0 --- /dev/null +++ b/app/pages/PageEnigma/store/cameraGroup.ts @@ -0,0 +1,99 @@ +import { + CameraGroup, + Keyframe, + QueueKeyframe, +} from "~/pages/PageEnigma/models/track"; +import Queue from "~/pages/PageEnigma/Queue/Queue"; +import { QueueNames } from "~/pages/PageEnigma/Queue/QueueNames"; +import { toEngineActions } from "~/pages/PageEnigma/Queue/toEngineActions"; +import * as uuid from "uuid"; +import { signal } from "@preact/signals-core"; + +export const cameraGroup = signal({ id: "CG1", keyframes: [] }); + +export function updateCamera({ id, offset }: { id: string; offset: number }) { + const oldCameraGroup = cameraGroup.value; + const newKeyframes = [...oldCameraGroup.keyframes]; + const keyframe = newKeyframes.find((row) => row.keyframe_uuid === id); + if (!keyframe) { + return; + } + keyframe.offset = offset; + + Queue.publish({ + queueName: QueueNames.TO_ENGINE, + action: toEngineActions.UPDATE_KEYFRAME, + data: keyframe, + }); + + cameraGroup.value = { + ...oldCameraGroup, + keyframes: newKeyframes, + }; +} + +export function addCameraKeyframe(keyframe: QueueKeyframe, offset: number) { + const newKeyframe = { + version: keyframe.version, + keyframe_uuid: uuid.v4(), + group: keyframe.group, + object_uuid: keyframe.object_uuid, + offset, + position: keyframe.position, + rotation: keyframe.rotation, + scale: keyframe.scale, + selected: false, + } as Keyframe; + + const oldCameraGroup = cameraGroup.value; + cameraGroup.value = { + ...oldCameraGroup, + keyframes: [...oldCameraGroup.keyframes, newKeyframe].sort( + (keyFrameA, keyframeB) => keyFrameA.offset - keyframeB.offset, + ), + }; + + Queue.publish({ + queueName: QueueNames.TO_ENGINE, + action: toEngineActions.ADD_KEYFRAME, + data: newKeyframe, + }); +} + +export function selectCameraKeyframe(keyframeId: string) { + const oldCameraGroup = cameraGroup.value; + cameraGroup.value = { + ...oldCameraGroup, + keyframes: [ + ...oldCameraGroup.keyframes.map((keyframe) => { + return { + ...keyframe, + selected: + keyframe.keyframe_uuid === keyframeId + ? !keyframe.selected + : keyframe.selected, + }; + }), + ], + }; +} + +export function deleteCameraKeyframe(keyframeId: string) { + const oldCameraGroup = cameraGroup.value; + cameraGroup.value = { + ...oldCameraGroup, + keyframes: [ + ...oldCameraGroup.keyframes.filter((keyframe) => { + if (keyframe.keyframe_uuid === keyframeId) { + Queue.publish({ + queueName: QueueNames.TO_ENGINE, + action: toEngineActions.DELETE_KEYFRAME, + data: keyframe, + }); + return false; + } + return true; + }), + ], + }; +} diff --git a/app/pages/PageEnigma/store/characterGroups.ts b/app/pages/PageEnigma/store/characterGroups.ts new file mode 100644 index 0000000000..7311bc5185 --- /dev/null +++ b/app/pages/PageEnigma/store/characterGroups.ts @@ -0,0 +1,331 @@ +import { + CharacterGroup, + Clip, + ClipGroup, + ClipType, + Keyframe, + MediaClip, + QueueKeyframe, +} from "~/pages/PageEnigma/models/track"; +import { signal } from "@preact/signals-core"; +import Queue from "~/pages/PageEnigma/Queue/Queue"; +import { QueueNames } from "~/pages/PageEnigma/Queue/QueueNames"; +import { toEngineActions } from "~/pages/PageEnigma/Queue/toEngineActions"; +import * as uuid from "uuid"; + +export const characterGroups = signal([ + { + id: "CH1", + muted: false, + animationClips: [], + positionKeyframes: [], + lipSyncClips: [], + }, +]); + +export function updateCharacters({ + type, + id, + offset, + length, +}: { + type: "animations" | "positions" | "lipSync"; + id: string; + length: number; + offset: number; +}) { + const oldCharacterGroups = characterGroups.value; + if (type === "animations") { + characterGroups.value = oldCharacterGroups.map((character) => { + const newAnimationClips = [...character.animationClips]; + const clipIndex = newAnimationClips.findIndex( + (row) => row.clip_uuid === id, + ); + if (clipIndex === -1) { + return { ...character }; + } + const clip = newAnimationClips[clipIndex]; + clip.offset = offset; + clip.length = length; + + Queue.publish({ + queueName: QueueNames.TO_ENGINE, + action: toEngineActions.UPDATE_CLIP, + data: clip, + }); + + return { + ...character, + animationClips: newAnimationClips, + }; + }); + } + + if (type === "positions") { + characterGroups.value = oldCharacterGroups.map((character) => { + const newPositionKeyframes = [...character.positionKeyframes]; + const keyframeIndex = newPositionKeyframes.findIndex( + (row) => row.keyframe_uuid === id, + ); + if (keyframeIndex === -1) { + return { ...character }; + } + const keyframe = newPositionKeyframes[keyframeIndex]; + keyframe.offset = offset; + + Queue.publish({ + queueName: QueueNames.TO_ENGINE, + action: toEngineActions.UPDATE_CLIP, + data: keyframe, + }); + + return { + ...character, + positionClips: newPositionKeyframes, + }; + }); + } + if (type === "lipSync") { + characterGroups.value = oldCharacterGroups.map((character) => { + const newLipSyncClips = [...character.lipSyncClips]; + const clipIndex = newLipSyncClips.findIndex( + (row) => row.clip_uuid === id, + ); + if (clipIndex === -1) { + return { ...character }; + } + const clip = newLipSyncClips[clipIndex]; + clip.offset = offset; + clip.length = length; + + Queue.publish({ + queueName: QueueNames.TO_ENGINE, + action: toEngineActions.UPDATE_CLIP, + data: clip, + }); + + return { + ...character, + lipSyncClips: newLipSyncClips, + }; + }); + } +} + +export function addCharacterAnimation({ + clipId, + characterId, + animationClips, + offset, +}: { + clipId: string; + characterId: string; + animationClips: MediaClip[]; + offset: number; +}) { + const clip = animationClips.find((row) => row.media_id === clipId); + if (!clip) { + return; + } + + const clip_uuid = uuid.v4(); + const newClip = { + ...clip, + group: ClipGroup.CHARACTER, + offset, + clip_uuid, + object_uuid: characterId, + } as Clip; + + const oldCharacterGroups = characterGroups.value; + characterGroups.value = oldCharacterGroups.map((character) => { + if (character.id !== characterId) { + return { ...character }; + } + return { + ...character, + animationClips: [...character.animationClips, newClip].sort( + (clipA, clipB) => clipA.offset - clipB.offset, + ), + }; + }); + + Queue.publish({ + queueName: QueueNames.TO_ENGINE, + action: toEngineActions.ADD_CLIP, + data: newClip, + }); +} + +export function addCharacterAudio({ + clipId, + characterId, + audioClips, + offset, +}: { + clipId: string; + characterId: string; + audioClips: MediaClip[]; + offset: number; +}) { + const clip = audioClips.find((row) => row.media_id === clipId); + if (!clip) { + return; + } + + const clip_uuid = uuid.v4(); + const newClip = { + ...clip, + type: ClipType.AUDIO, + group: ClipGroup.CHARACTER, + offset, + clip_uuid, + object_uuid: characterId, + } as Clip; + + const oldCharacterGroups = characterGroups.value; + characterGroups.value = oldCharacterGroups.map((character) => { + if (character.id !== characterId) { + return { ...character }; + } + return { + ...character, + lipSyncClips: [...character.lipSyncClips, newClip].sort( + (clipA, clipB) => clipA.offset - clipB.offset, + ), + }; + }); + + Queue.publish({ + queueName: QueueNames.TO_ENGINE, + action: toEngineActions.ADD_CLIP, + data: newClip, + }); +} + +export function addCharacterKeyframe(keyframe: QueueKeyframe, offset: number) { + const newKeyframe = { + version: keyframe.version, + keyframe_uuid: uuid.v4(), + group: keyframe.group, + object_uuid: keyframe.object_uuid, + offset, + position: keyframe.position, + rotation: keyframe.rotation, + scale: keyframe.scale, + selected: false, + } as Keyframe; + + const oldCharacterGroups = characterGroups.value; + characterGroups.value = oldCharacterGroups.map((character) => { + if (character.id !== keyframe.object_uuid) { + return { ...character }; + } + return { + ...character, + positionKeyframes: [...character.positionKeyframes, newKeyframe].sort( + (keyFrameA, keyframeB) => keyFrameA.offset - keyframeB.offset, + ), + }; + }); + + Queue.publish({ + queueName: QueueNames.TO_ENGINE, + action: toEngineActions.ADD_KEYFRAME, + data: newKeyframe, + }); +} + +export function toggleLipSyncMute(characterId: string) { + const oldCharacterGroups = characterGroups.value; + characterGroups.value = oldCharacterGroups.map((character) => { + if (character.id === characterId) { + Queue.publish({ + queueName: QueueNames.TO_ENGINE, + action: character?.muted + ? toEngineActions.UNMUTE + : toEngineActions.MUTE, + data: { + version: 1, + type: ClipType.AUDIO, + group: ClipGroup.CHARACTER, + object_uuid: characterId, + }, + }); + } + + return { + ...character, + muted: character.id === characterId ? !character.muted : character.muted, + }; + }); +} + +export function selectCharacterClip(clipId: string) { + const oldCharacterGroups = characterGroups.value; + characterGroups.value = oldCharacterGroups.map((character) => ({ + ...character, + animationClips: character.animationClips.map((clip) => ({ + ...clip, + selected: clip.clip_uuid === clipId ? !clip.selected : clip.selected, + })), + positionClips: character.positionKeyframes.map((keyframe) => ({ + ...keyframe, + selected: + keyframe.keyframe_uuid === clipId + ? !keyframe.selected + : keyframe.selected, + })), + lipSyncClips: character.lipSyncClips.map((clip) => ({ + ...clip, + selected: clip.clip_uuid === clipId ? !clip.selected : clip.selected, + })), + })); +} + +export function deleteCharacterClip(clipId: string) { + const oldCharacterGroups = characterGroups.value; + characterGroups.value = oldCharacterGroups.map((character) => ({ + ...character, + animationClips: character.animationClips.filter((clip) => { + if (clip.clip_uuid === clipId) { + Queue.publish({ + queueName: QueueNames.TO_ENGINE, + action: toEngineActions.DELETE_CLIP, + data: clip!, + }); + return false; + } + return true; + }), + lipSyncClips: character.lipSyncClips.filter((clip) => { + if (clip.clip_uuid === clipId) { + Queue.publish({ + queueName: QueueNames.TO_ENGINE, + action: toEngineActions.DELETE_CLIP, + data: clip!, + }); + return false; + } + return true; + }), + })); +} + +export function deleteCharacterKeyframe(keyframe: Keyframe) { + const oldCharacterGroups = characterGroups.value; + characterGroups.value = oldCharacterGroups.map((character) => ({ + ...character, + positionClips: character.positionKeyframes.filter((row) => { + if (row.keyframe_uuid === keyframe.keyframe_uuid) { + Queue.publish({ + queueName: QueueNames.TO_ENGINE, + action: toEngineActions.DELETE_KEYFRAME, + data: row, + }); + return false; + } + return true; + }), + })); +} diff --git a/app/pages/PageEnigma/store/index.ts b/app/pages/PageEnigma/store/index.ts index fb12ae01bb..2c8a560d91 100644 --- a/app/pages/PageEnigma/store/index.ts +++ b/app/pages/PageEnigma/store/index.ts @@ -1,3 +1,7 @@ +export * from "./audioGroup"; +export * from "./cameraGroup"; +export * from "./characterGroups"; export * from "./dragAndDrop"; +export * from "./objectGroup"; export * from "./sizing"; export * from "./timing"; diff --git a/app/pages/PageEnigma/store/objectGroup.ts b/app/pages/PageEnigma/store/objectGroup.ts new file mode 100644 index 0000000000..8b94c4f509 --- /dev/null +++ b/app/pages/PageEnigma/store/objectGroup.ts @@ -0,0 +1,107 @@ +import { signal } from "@preact/signals-core"; +import { + Keyframe, + ObjectGroup, + QueueKeyframe, +} from "~/pages/PageEnigma/models/track"; +import { useCallback } from "react"; +import * as uuid from "uuid"; + +export const objectGroup = signal({ + id: "OB1", + objects: [], +}); + +export function updateObject({ id, offset }: { id: string; offset: number }) { + const oldObject = objectGroup.value; + objectGroup.value = { + id: oldObject.id, + objects: oldObject.objects.map((object) => ({ + object_uuid: object.object_uuid, + name: object.name, + keyframes: object.keyframes.map((keyFrame) => ({ + ...keyFrame, + offset: keyFrame.keyframe_uuid === id ? offset : keyFrame.offset, + })), + })), + }; +} + +export function addObjectKeyframe(keyframe: QueueKeyframe, offset: number) { + console.log(keyframe, offset); + const oldObjectGroup = objectGroup.value; + const obj = oldObjectGroup.objects.find( + (row) => row.object_uuid === keyframe.object_uuid, + ); + + const newObject = obj ?? { + object_uuid: keyframe.object_uuid, + name: keyframe.object_name ?? "unknown", + keyframes: [] as Keyframe[], + }; + newObject.keyframes.push({ + version: keyframe.version, + keyframe_uuid: uuid.v4(), + group: keyframe.group, + object_uuid: keyframe.object_uuid, + offset, + position: keyframe.position, + rotation: keyframe.rotation, + scale: keyframe.scale, + selected: false, + } as Keyframe); + console.log(newObject); + newObject.keyframes.sort( + (keyframeA, keyframeB) => keyframeA.offset - keyframeB.offset, + ); + + console.log(4); + objectGroup.value = { + ...oldObjectGroup, + objects: [ + ...oldObjectGroup.objects.filter( + (object) => object.object_uuid !== keyframe.object_uuid, + ), + newObject, + ].sort((objA, objB) => (objA.object_uuid < objB.object_uuid ? -1 : 1)), + }; + console.log("T", objectGroup.value); +} + +export function deleteObjectKeyframe(keyframe: Keyframe) { + const oldObjectGroup = objectGroup.value; + const obj = oldObjectGroup.objects.find( + (row) => row.object_uuid === keyframe.object_uuid, + ); + if (!obj) { + return oldObjectGroup; + } + + const newKeyframes = [ + ...obj.keyframes.filter( + (row) => row.keyframe_uuid !== keyframe.keyframe_uuid, + ), + ]; + + if (newKeyframes.length) { + return { + ...oldObjectGroup, + objects: [ + ...oldObjectGroup.objects.map((object) => ({ + ...object, + ...(object.object_uuid === keyframe.object_uuid + ? { keyframes: newKeyframes } + : {}), + })), + ], + }; + } + objectGroup.value = { + ...oldObjectGroup, + objects: [ + ...oldObjectGroup.objects.filter( + (object) => object.object_uuid !== keyframe.object_uuid, + ), + ], + }; +} diff --git a/app/pages/PageEnigma/store/sizing.ts b/app/pages/PageEnigma/store/sizing.ts index 778621080b..9eb952e54e 100644 --- a/app/pages/PageEnigma/store/sizing.ts +++ b/app/pages/PageEnigma/store/sizing.ts @@ -1,4 +1,6 @@ import { signal, computed } from "@preact/signals-core"; +import { characterGroups } from "~/pages/PageEnigma/store/characterGroups"; +import { objectGroup } from "~/pages/PageEnigma/store/objectGroup"; export const scale = signal(1); export const filmLength = signal(12); @@ -6,3 +8,12 @@ export const timelineHeight = signal(0); export const fullWidth = computed(() => { return filmLength.value * 60 * 4 * scale.value; }); + +export const fullHeight = computed(() => { + return ( + characterGroups.value.length * 268 + + objectGroup.value.objects.length * 60 + + 300 + + 96 + ); +});