diff --git a/app/pages/PageEnigma/PageEnigmaComponent.tsx b/app/pages/PageEnigma/PageEnigmaComponent.tsx index bc3c858507..30b68687e8 100644 --- a/app/pages/PageEnigma/PageEnigmaComponent.tsx +++ b/app/pages/PageEnigma/PageEnigmaComponent.tsx @@ -1,4 +1,4 @@ -import { useReducer, useEffect } from "react"; +import { useReducer } from "react"; import { faSparkles } from "@fortawesome/pro-solid-svg-icons"; @@ -14,18 +14,33 @@ import { SidePanelTabs } from "./comps/SidePanelTabs"; import { EngineProvider } from "./contexts/EngineProvider"; import { AppUIProvider } from "./contexts/AppUiContext"; -import { appUiReducer, appUiInitialStateValues, APPUI_ACTION_TYPES, APPUI_VIEW_MODES } from "./reducers"; +import { + appUiReducer, + appUiInitialStateValues, + APPUI_VIEW_MODES, +} from "./reducers"; import { ViewSideBySide } from "./comps/ViewSideBySide"; -import { timelineHeight } from "~/pages/PageEnigma/store"; +import { + canDrop, + clipLength, + currPosition, + dragId, + overTimeline, + scale, + timelineHeight, +} from "~/pages/PageEnigma/store"; import { useSignals } from "@preact/signals-react/runtime"; export const PageEnigmaComponent = () => { - - const [appUiState, dispatchAppUiState] = useReducer(appUiReducer, appUiInitialStateValues); useSignals(); + const { currX, currY } = currPosition.value; + + const [appUiState, dispatchAppUiState] = useReducer( + appUiReducer, + appUiInitialStateValues, + ); const lowerHeight = timelineHeight.value; - console.log("main", timelineHeight.value); return (
@@ -42,14 +57,14 @@ export const PageEnigmaComponent = () => { style={{ height: `calc(100% - ${lowerHeight}px)` }} >
- -
- - +
+ {/* Top controls */}
@@ -65,12 +80,11 @@ export const PageEnigmaComponent = () => {
- { - appUiState.viewMode === APPUI_VIEW_MODES.SIDE_BY_SIDE && + {appUiState.viewMode === APPUI_VIEW_MODES.SIDE_BY_SIDE && ( - } + )} {
+
); -}; \ No newline at end of file +}; diff --git a/app/pages/PageEnigma/Queue/Queue.ts b/app/pages/PageEnigma/Queue/Queue.ts index 5bd8661e64..1bf90a922b 100644 --- a/app/pages/PageEnigma/Queue/Queue.ts +++ b/app/pages/PageEnigma/Queue/Queue.ts @@ -1,14 +1,21 @@ import { fromEngineActions } from "~/pages/PageEnigma/Queue/fromEngineActions"; import { toEngineActions } from "~/pages/PageEnigma/Queue/toEngineActions"; +import { QueueClip } from "~/pages/PageEnigma/models/track"; class Queue { private _queue: Record< string, - { action: fromEngineActions | toEngineActions; data: any }[] + { + action: fromEngineActions | toEngineActions; + data: QueueClip | { currentTime: number }; + }[] > = {}; private _subscribers: Record< string, - (entry: { action: fromEngineActions | toEngineActions; data: any }) => void + (entry: { + action: fromEngineActions | toEngineActions; + data: QueueClip | { currentTime: number }; + }) => void > = {}; public publish({ @@ -18,12 +25,13 @@ class Queue { }: { queueName: string; action: fromEngineActions | toEngineActions; - data: any; + data: QueueClip | { currentTime: number }; }) { if (!this._queue[queueName]) { this._queue[queueName] = []; } this._queue[queueName].push({ action, data }); + console.log("Queued", queueName, action, data); if (this._subscribers[queueName]) { this._subscribers[queueName](this._queue[queueName].splice(0, 1)[0]); @@ -34,7 +42,7 @@ class Queue { queueName: string, onMessage: (entry: { action: fromEngineActions | toEngineActions; - data: any; + data: QueueClip | { currentTime: number }; }) => void, ) { this._subscribers[queueName] = onMessage; diff --git a/app/pages/PageEnigma/Queue/fromEngineActions.ts b/app/pages/PageEnigma/Queue/fromEngineActions.ts index 58cbeb4a28..40753b789d 100644 --- a/app/pages/PageEnigma/Queue/fromEngineActions.ts +++ b/app/pages/PageEnigma/Queue/fromEngineActions.ts @@ -1,4 +1,4 @@ export enum fromEngineActions { - FRAME_TICK, // data={currentTime: number} - OBJECT_ADDED, // data={id: string, name: string, ?????} + UPDATE_TIME = "update_time", + // data={currentTime: number} } diff --git a/app/pages/PageEnigma/Queue/toEngineActions.ts b/app/pages/PageEnigma/Queue/toEngineActions.ts index a65a962483..867730e006 100644 --- a/app/pages/PageEnigma/Queue/toEngineActions.ts +++ b/app/pages/PageEnigma/Queue/toEngineActions.ts @@ -1,4 +1,18 @@ export enum toEngineActions { - ADD_ANIMATION, // data={id: string, offset: number, length: number} - SCRUBBER_TIME, // data={currentTime: number} + ADD_CLIP = "add_clip", + // data=QueueClip + DELETE_CLIP = "delete_clip", + // data=QueueClip + MUTE = "mute", + // data={version, type, group, object_uuid} if lipsync + // data={version, type, group} if global_audio + PLAY_CLIP = "play_clip", + // data=QueueClip + UNMUTE = "unmute", + // data={version, type, group, object_uuid} if lipsync + // data={version, type, group} if global_audio + UPDATE_CLIP = "update_clip", + // data=QueueClip + UPDATE_TIME = "update_time", + // data={currentTime: number} } diff --git a/app/pages/PageEnigma/comps/SidePanelTabs/ClipElement.tsx b/app/pages/PageEnigma/comps/SidePanelTabs/ClipElement.tsx index 715a956933..37c09ae69b 100644 --- a/app/pages/PageEnigma/comps/SidePanelTabs/ClipElement.tsx +++ b/app/pages/PageEnigma/comps/SidePanelTabs/ClipElement.tsx @@ -1,33 +1,23 @@ -import { useCallback, useContext, useEffect, useState } from "react"; -import { BaseClip } from "~/pages/PageEnigma/models/track"; +import { useCallback, useContext, useEffect } from "react"; +import { MediaClip } from "~/pages/PageEnigma/models/track"; import { TrackContext } from "~/pages/PageEnigma/contexts/TrackContext/TrackContext"; -import { canDrop, dragId, overTimeline, scale } from "~/pages/PageEnigma/store"; +import { + canDrop, + currPosition, + dragId, + initPosition, +} from "~/pages/PageEnigma/store"; import { useSignals } from "@preact/signals-react/runtime"; interface Props { - clip: BaseClip; + clip: MediaClip; type: "animations" | "lipSync"; } export const ClipElement = ({ clip, type }: Props) => { useSignals(); const { startDrag, endDrag } = useContext(TrackContext); - const [initPosition, setInitPosition] = useState<{ - initX: number; - initY: number; - }>({ - initX: 0, - initY: 0, - }); - const [currPosition, setCurrPosition] = useState<{ - x: number; - y: number; - }>({ - x: 0, - y: 0, - }); - const { x, y } = currPosition; - const { initX, initY } = initPosition; + const { initX, initY } = initPosition.value; useEffect(() => { const onPointerUp = () => { @@ -40,9 +30,9 @@ export const ClipElement = ({ clip, type }: Props) => { if (dragId) { event.stopPropagation(); event.preventDefault(); - const deltaX = event.clientX - initX; - const deltaY = event.clientY - initY; - setCurrPosition({ x: deltaX, y: deltaY }); + const deltaX = event.pageX - initX; + const deltaY = event.pageY - initY; + currPosition.value = { currX: initX + deltaX, currY: initY + deltaY }; return; } }; @@ -59,53 +49,31 @@ export const ClipElement = ({ clip, type }: Props) => { const onPointerDown = useCallback( (event: React.PointerEvent) => { if (event.button === 0) { - const box = document.getElementById(`ani-clip-${clip.id}`); - const position = box!.getBoundingClientRect(); - - startDrag(type, clip.id, clip.length); - setCurrPosition({ - x: position.x, - y: position.y + position.height + 1, - }); - setInitPosition({ - initX: position.x, - initY: position.y + position.height + 1, - }); + startDrag(type, clip.media_id, clip.length); + currPosition.value = { + currX: event.pageX, + currY: event.pageY, + }; + initPosition.value = { + initX: event.pageX, + initY: event.pageY, + }; canDrop.value = false; } }, - [type, clip.id, startDrag], + [type, clip.media_id, startDrag, clip.length], ); return ( -
+
{clip.name}
-
- {clip.name} -
); }; diff --git a/app/pages/PageEnigma/comps/SidePanelTabs/ClipElements.tsx b/app/pages/PageEnigma/comps/SidePanelTabs/ClipElements.tsx index defcb65ac3..1c935c9fc2 100644 --- a/app/pages/PageEnigma/comps/SidePanelTabs/ClipElements.tsx +++ b/app/pages/PageEnigma/comps/SidePanelTabs/ClipElements.tsx @@ -1,14 +1,24 @@ -import { useContext } from "react"; +import { useContext, useEffect, useState } from "react"; import { ClipElement } from "./ClipElement"; import { TrackContext } from "~/pages/PageEnigma/contexts/TrackContext/TrackContext"; +import { useSignals } from "@preact/signals-react/runtime"; +import { timelineHeight } from "~/pages/PageEnigma/store"; export const ClipElements = () => { const { animationClips } = useContext(TrackContext); + useSignals(); + const [height, setHeight] = useState(0); + + useEffect(() => { + setHeight(window.outerHeight - timelineHeight.value); + }, []); return ( -
+
{animationClips.map((clip) => { - return ; + return ( + + ); })}
); diff --git a/app/pages/PageEnigma/comps/Timeline/Timeline.tsx b/app/pages/PageEnigma/comps/Timeline/Timeline.tsx index 4c61defb6c..3fc61d7cad 100644 --- a/app/pages/PageEnigma/comps/Timeline/Timeline.tsx +++ b/app/pages/PageEnigma/comps/Timeline/Timeline.tsx @@ -21,7 +21,7 @@ import { EngineContext } from "../../contexts/EngineContext"; export const Timeline = () => { const editorEngine = useContext(EngineContext); - + const { characters, objects, @@ -148,7 +148,7 @@ export const Timeline = () => { style={{ left: 0, top: 8, - width: 2, + width: 4, height: fullHeight, }} /> diff --git a/app/pages/PageEnigma/comps/Timeline/TrackClips.tsx b/app/pages/PageEnigma/comps/Timeline/TrackClips.tsx index fc37f21df9..f07ee1f593 100644 --- a/app/pages/PageEnigma/comps/Timeline/TrackClips.tsx +++ b/app/pages/PageEnigma/comps/Timeline/TrackClips.tsx @@ -1,7 +1,7 @@ import { TrackClip } from "~/pages/PageEnigma/comps/Timeline/TrackClip"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faVolume, faVolumeSlash } from "@fortawesome/pro-solid-svg-icons"; -import { BaseClip } from "~/pages/PageEnigma/models/track"; +import { Clip } from "~/pages/PageEnigma/models/track"; import { PointerEvent } from "react"; import { canDrop, @@ -15,7 +15,7 @@ import { interface Props { id: string; - clips: BaseClip[]; + clips: Clip[]; title: string; style: "character" | "audio" | "camera" | "objects"; type?: "animations" | "positions" | "lipSync"; @@ -94,7 +94,7 @@ export const TrackClips = ({ > {clips.map((clip, index) => ( 0 ? clips[index - 1].offset + clips[index - 1].length : 0 } diff --git a/app/pages/PageEnigma/comps/Timeline/utils/useMouseEventsAnimation.ts b/app/pages/PageEnigma/comps/Timeline/utils/useMouseEventsAnimation.ts index e145579bb9..25de508f55 100644 --- a/app/pages/PageEnigma/comps/Timeline/utils/useMouseEventsAnimation.ts +++ b/app/pages/PageEnigma/comps/Timeline/utils/useMouseEventsAnimation.ts @@ -20,7 +20,7 @@ export const useMouseEventsAnimation = () => { setTime(-1); Queue.publish({ queueName: QueueNames.TO_ENGINE, - action: toEngineActions.SCRUBBER_TIME, + action: toEngineActions.UPDATE_TIME, data: { currentTime: Math.round(time) }, }); } diff --git a/app/pages/PageEnigma/comps/Timeline/utils/useMouseEventsClip.ts b/app/pages/PageEnigma/comps/Timeline/utils/useMouseEventsClip.ts index 8d666599a2..1dbe7ed124 100644 --- a/app/pages/PageEnigma/comps/Timeline/utils/useMouseEventsClip.ts +++ b/app/pages/PageEnigma/comps/Timeline/utils/useMouseEventsClip.ts @@ -1,10 +1,10 @@ import React, { Dispatch, useCallback, useEffect, useRef } from "react"; -import { BaseClip } from "~/pages/PageEnigma/models/track"; +import { Clip } from "~/pages/PageEnigma/models/track"; import { canDrop, scale } from "~/pages/PageEnigma/store"; import { useSignals } from "@preact/signals-react/runtime"; export const useMouseEventsClip = ( - clip: BaseClip, + clip: Clip, max: number, min: number, updateClip: (args: { id: string; offset: number; length: number }) => void, @@ -21,21 +21,26 @@ export const useMouseEventsClip = ( const onPointerUp = useCallback(() => { if (isActive.current) { updateClip({ - id: clip.id, + id: clip.clip_uuid, offset: Math.round(currOffset.current), length: Math.round(currLength.current), }); isActive.current = ""; canDrop.value = false; } - }, [updateClip, clip.id]); + }, [updateClip, clip.clip_uuid]); const onMouseMove = useCallback( (event: MouseEvent) => { const delta = (event.clientX - clientX.current) / 4 / scale.value; const deltaOffset = delta + initOffset.current; if (isActive.current === "drag") { - if (deltaOffset < min || deltaOffset + currLength.current > max) { + if (deltaOffset < min) { + currOffset.current = min; + return; + } + if (deltaOffset + currLength.current > max) { + currOffset.current = max - currLength.current; return; } currOffset.current = deltaOffset; diff --git a/app/pages/PageEnigma/comps/Timeline/utils/useQueueHandler.ts b/app/pages/PageEnigma/comps/Timeline/utils/useQueueHandler.ts index bc9b9176d1..3359ce8931 100644 --- a/app/pages/PageEnigma/comps/Timeline/utils/useQueueHandler.ts +++ b/app/pages/PageEnigma/comps/Timeline/utils/useQueueHandler.ts @@ -1,28 +1,27 @@ import { fromEngineActions } from "~/pages/PageEnigma/Queue/fromEngineActions"; import { useSignals } from "@preact/signals-react/runtime"; -import { useCallback, useContext, useEffect } from "react"; +import { useCallback, useEffect } from "react"; import { currentTime } from "~/pages/PageEnigma/store"; -import { TrackContext } from "~/pages/PageEnigma/contexts/TrackContext/TrackContext"; import Queue from "~/pages/PageEnigma/Queue/Queue"; import { QueueNames } from "~/pages/PageEnigma/Queue/QueueNames"; import { toEngineActions } from "~/pages/PageEnigma/Queue/toEngineActions"; +import { QueueClip } from "~/pages/PageEnigma/models/track"; + +interface UpdateTime { + currentTime: number; +} interface Arguments { action: fromEngineActions | toEngineActions; - data: any; + data: QueueClip | UpdateTime; } export function useQueueHandler() { useSignals(); - const { addObject } = useContext(TrackContext); - const handleActions = useCallback(({ action, data }: Arguments) => { switch (action) { - case fromEngineActions.FRAME_TICK: - currentTime.value += 1; - break; - case fromEngineActions.OBJECT_ADDED: - addObject(data); + case fromEngineActions.UPDATE_TIME: + currentTime.value = (data as UpdateTime).currentTime; break; default: throw new Error(`Unknown action ${action}`); diff --git a/app/pages/PageEnigma/contexts/TrackContext/TrackContext.tsx b/app/pages/PageEnigma/contexts/TrackContext/TrackContext.tsx index bd523c4041..f761df637e 100644 --- a/app/pages/PageEnigma/contexts/TrackContext/TrackContext.tsx +++ b/app/pages/PageEnigma/contexts/TrackContext/TrackContext.tsx @@ -1,13 +1,11 @@ import { createContext } from "react"; import { - AnimationClip, - AudioClip, AudioGroup, - BaseClip, CameraGroup, CharacterGroup, ObjectGroup, ObjectTrack, + MediaClip, } from "~/pages/PageEnigma/models/track"; export const TrackContext = createContext<{ @@ -23,13 +21,13 @@ export const TrackContext = createContext<{ addCharacterAnimation: (options: { clipId: string; characterId: string; - animationClips: BaseClip[]; + animationClips: MediaClip[]; offset: number; }) => void; addCharacterAudio: (options: { clipId: string; characterId: string; - audioClips: BaseClip[]; + audioClips: MediaClip[]; offset: number; }) => void; selectCharacterClip: (clipId: string) => void; @@ -55,7 +53,7 @@ export const TrackContext = createContext<{ toggleAudioMute: () => void; addGlobalAudio: ( clipId: string, - animationClips: BaseClip[], + animationClips: MediaClip[], offset: number, ) => void; selectAudioClip: (clipId: string) => void; @@ -71,8 +69,8 @@ export const TrackContext = createContext<{ selectClip: (clipId: string) => void; // sidebar clips - animationClips: AnimationClip[]; - audioClips: AudioClip[]; + animationClips: MediaClip[]; + audioClips: MediaClip[]; // drag and drop startDrag: ( diff --git a/app/pages/PageEnigma/contexts/TrackContext/TrackProvider.tsx b/app/pages/PageEnigma/contexts/TrackContext/TrackProvider.tsx index 413b947e99..bc528311bd 100644 --- a/app/pages/PageEnigma/contexts/TrackContext/TrackProvider.tsx +++ b/app/pages/PageEnigma/contexts/TrackContext/TrackProvider.tsx @@ -4,7 +4,7 @@ import useUpdateCharacters from "~/pages/PageEnigma/contexts/TrackContext/utils/ 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 { AnimationClip, AudioClip } from "~/pages/PageEnigma/models/track"; +import { ClipType, MediaClip } from "~/pages/PageEnigma/models/track"; import useUpdateDragDrop from "~/pages/PageEnigma/contexts/TrackContext/utils/useUpdateDragDrop"; import { canDrop, @@ -13,6 +13,7 @@ import { dropId, dropOffset, } from "~/pages/PageEnigma/store"; +import * as uuid from "uuid"; interface Props { children: ReactNode; @@ -31,8 +32,8 @@ export const TrackProvider = ({ children }: Props) => { setSelectedClip(clipId); }, []); - const [animationClips, setAnimationClips] = useState([]); - const [audioClips, setAudioClips] = useState([]); + const [animationClips, setAnimationClips] = useState([]); + const [audioClips, setAudioClips] = useState([]); const [scale, setScale] = useState(1); const [length, setLength] = useState(1); @@ -74,26 +75,30 @@ export const TrackProvider = ({ children }: Props) => { useEffect(() => { setAnimationClips([ { - id: "ani-id1", - offset: 0, + version: 1, + media_id: uuid.v4(), + type: ClipType.ANIMATION, length: 25, name: "Sit", }, { - id: "ani-id2", - offset: 0, + version: 1, + media_id: uuid.v4(), + type: ClipType.ANIMATION, length: 25, name: "Idle", }, { - id: "ani-id3", - offset: 0, + version: 1, + media_id: uuid.v4(), + type: ClipType.ANIMATION, length: 25, name: "Stand", }, { - id: "ani-id4", - offset: 0, + version: 1, + media_id: uuid.v4(), + type: ClipType.ANIMATION, length: 25, name: "Walk", }, diff --git a/app/pages/PageEnigma/contexts/TrackContext/utils/useUpdateAudio.ts b/app/pages/PageEnigma/contexts/TrackContext/utils/useUpdateAudio.ts index 2052926c16..5804527373 100644 --- a/app/pages/PageEnigma/contexts/TrackContext/utils/useUpdateAudio.ts +++ b/app/pages/PageEnigma/contexts/TrackContext/utils/useUpdateAudio.ts @@ -1,24 +1,21 @@ import { useCallback, useState } from "react"; -import { AudioGroup, BaseClip } from "~/pages/PageEnigma/models/track"; +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: [ - { - id: "AU1-1", - length: 200, - offset: 0, - name: "audio 1", - }, - { - id: "AU1-2", - length: 180, - offset: 300, - name: "audio 2", - }, - ], + clips: [], }); const updateAudio = useCallback( @@ -33,44 +30,56 @@ export default function useUpdateAudio() { }) => { setAudio((oldAudio) => { const newClips = [...oldAudio.clips]; - const clipIndex = newClips.findIndex((row) => row.id === id); + 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, }; }); - console.log("message", { - action: "UpdateGlobalAudio", - id, - data: { offset, length }, - }); }, [], ); const addGlobalAudio = useCallback( - (dragId: string, audioClips: BaseClip[], offset: number) => { - const clip = audioClips.find((row) => row.id === dragId); + (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, { ...clip, offset }], + clips: [...oldAudio.clips, newClip], }; }); - console.log("message", { - action: "UpdateGlobalAudio", - id: dragId, - data: { offset }, + + Queue.publish({ + queueName: QueueNames.TO_ENGINE, + action: toEngineActions.ADD_CLIP, + data: newClip, }); }, [], @@ -78,15 +87,21 @@ export default function useUpdateAudio() { 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, }; }); - console.log("message", { - action: "ToggleGlobalAudioMute", - id: "", - }); }, []); const selectAudioClip = useCallback((clipId: string) => { @@ -97,7 +112,8 @@ export default function useUpdateAudio() { ...oldAudio.clips.map((clip) => { return { ...clip, - selected: clip.id === clipId ? !clip.selected : clip.selected, + selected: + clip.clip_uuid === clipId ? !clip.selected : clip.selected, }; }), ], @@ -109,13 +125,21 @@ export default function useUpdateAudio() { setAudio((oldAudio) => { return { ...oldAudio, - clips: [...oldAudio.clips.filter((clip) => clip.id !== clipId)], + 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; + }), + ], }; }); - console.log("message", { - action: "UpdateGlobalAudio", - id: clipId, - }); }, []); return { diff --git a/app/pages/PageEnigma/contexts/TrackContext/utils/useUpdateCamera.ts b/app/pages/PageEnigma/contexts/TrackContext/utils/useUpdateCamera.ts index b5b7d21da6..d7bc1395b8 100644 --- a/app/pages/PageEnigma/contexts/TrackContext/utils/useUpdateCamera.ts +++ b/app/pages/PageEnigma/contexts/TrackContext/utils/useUpdateCamera.ts @@ -1,23 +1,13 @@ import { useCallback, useState } from "react"; import { CameraGroup } 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"; export default function useUpdateCamera() { const [camera, setCamera] = useState({ id: "CA1", - clips: [ - { - id: "CA1-1", - length: 200, - offset: 0, - name: "cam 1", - }, - { - id: "CA1-2", - length: 180, - offset: 300, - name: "cam 2", - }, - ], + clips: [], }); const updateCamera = useCallback( @@ -32,13 +22,20 @@ export default function useUpdateCamera() { }) => { setCamera((oldCamera) => { const newClips = [...oldCamera.clips]; - const clipIndex = newClips.findIndex((row) => row.id === id); + const clipIndex = newClips.findIndex((row) => row.clip_uuid === id); if (clipIndex === -1) { return { ...oldCamera }; } const clip = newClips[clipIndex]; clip.offset = offset; clip.length = length; + + Queue.publish({ + queueName: QueueNames.TO_ENGINE, + action: toEngineActions.UPDATE_CLIP, + data: clip, + }); + return { ...oldCamera, clips: newClips, @@ -56,7 +53,8 @@ export default function useUpdateCamera() { ...oldCamera.clips.map((clip) => { return { ...clip, - selected: clip.id === clipId ? !clip.selected : clip.selected, + selected: + clip.clip_uuid === clipId ? !clip.selected : clip.selected, }; }), ], @@ -68,7 +66,19 @@ export default function useUpdateCamera() { setCamera((oldCamera) => { return { ...oldCamera, - clips: [...oldCamera.clips.filter((clip) => clip.id !== clipId)], + clips: [ + ...oldCamera.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/contexts/TrackContext/utils/useUpdateCharacters.ts b/app/pages/PageEnigma/contexts/TrackContext/utils/useUpdateCharacters.ts index 4f58f67f08..cd429c0024 100644 --- a/app/pages/PageEnigma/contexts/TrackContext/utils/useUpdateCharacters.ts +++ b/app/pages/PageEnigma/contexts/TrackContext/utils/useUpdateCharacters.ts @@ -1,5 +1,11 @@ import { useCallback, useState } from "react"; -import { BaseClip, CharacterGroup } from "~/pages/PageEnigma/models/track"; +import { + Clip, + CharacterGroup, + ClipGroup, + ClipType, + MediaClip, +} from "~/pages/PageEnigma/models/track"; import * as uuid from "uuid"; import Queue from "~/pages/PageEnigma/Queue/Queue"; import { QueueNames } from "~/pages/PageEnigma/Queue/QueueNames"; @@ -12,16 +18,10 @@ export default function useUpdateCharacters() { muted: false, animationClips: [], positionClips: [], - lipSyncClips: [ - { - id: "CH1-L1", - length: 400, - offset: 150, - name: "sync 1", - }, - ], + lipSyncClips: [], }, ]); + const updateCharacters = useCallback( ({ type, @@ -39,7 +39,7 @@ export default function useUpdateCharacters() { return oldCharacters.map((character) => { const newAnimationClips = [...character.animationClips]; const clipIndex = newAnimationClips.findIndex( - (row) => row.id === id, + (row) => row.clip_uuid === id, ); if (clipIndex === -1) { return { ...character }; @@ -47,25 +47,28 @@ export default function useUpdateCharacters() { 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, }; }); }); - console.log("message", { - action: "UpdateAnimation", - id, - data: { offset, length }, - }); return; } + if (type === "positions") { setCharacters((oldCharacters) => { return oldCharacters.map((character) => { const newPositionClips = [...character.positionClips]; const clipIndex = newPositionClips.findIndex( - (row) => row.id === id, + (row) => row.clip_uuid === id, ); if (clipIndex === -1) { return { ...character }; @@ -73,6 +76,13 @@ export default function useUpdateCharacters() { const clip = newPositionClips[clipIndex]; clip.offset = offset; clip.length = length; + + Queue.publish({ + queueName: QueueNames.TO_ENGINE, + action: toEngineActions.UPDATE_CLIP, + data: clip, + }); + return { ...character, positionClips: newPositionClips, @@ -85,24 +95,28 @@ export default function useUpdateCharacters() { setCharacters((oldCharacters) => { return oldCharacters.map((character) => { const newLipSyncClips = [...character.lipSyncClips]; - const clipIndex = newLipSyncClips.findIndex((row) => row.id === id); + 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, }; }); }); - console.log("message", { - action: "UpdateLipSync", - id, - data: { offset, length }, - }); } }, [], @@ -117,14 +131,23 @@ export default function useUpdateCharacters() { }: { clipId: string; characterId: string; - animationClips: BaseClip[]; + animationClips: MediaClip[]; offset: number; }) => { - const clip = animationClips.find((row) => row.id === clipId); + 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) { @@ -132,20 +155,17 @@ export default function useUpdateCharacters() { } return { ...character, - animationClips: [ - ...character.animationClips, - { ...clip, id: uuid.v4(), offset }, - ], + animationClips: [...character.animationClips, newClip].sort( + (clipA, clipB) => clipA.offset - clipB.offset, + ), }; }); }); + Queue.publish({ queueName: QueueNames.TO_ENGINE, - action: toEngineActions.ADD_ANIMATION, - data: { - clipId, - offset, - }, + action: toEngineActions.ADD_CLIP, + data: newClip, }); }, [], @@ -160,14 +180,24 @@ export default function useUpdateCharacters() { }: { clipId: string; characterId: string; - audioClips: BaseClip[]; + audioClips: MediaClip[]; offset: number; }) => { - const clip = audioClips.find((row) => row.id === clipId); + 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) { @@ -175,14 +205,17 @@ export default function useUpdateCharacters() { } return { ...character, - lipSyncClips: [...character.lipSyncClips, { ...clip, offset }], + lipSyncClips: [...character.lipSyncClips, newClip].sort( + (clipA, clipB) => clipA.offset - clipB.offset, + ), }; }); }); - console.log("message", { - action: "AddLipSync", - id: clipId, - data: { offset }, + + Queue.publish({ + queueName: QueueNames.TO_ENGINE, + action: toEngineActions.ADD_CLIP, + data: newClip, }); }, [], @@ -190,15 +223,28 @@ export default function useUpdateCharacters() { const toggleLipSyncMute = useCallback((characterId: string) => { setCharacters((oldCharacters) => { - return oldCharacters.map((character) => ({ - ...character, - muted: - character.id === characterId ? !character.muted : character.muted, - })); - }); - console.log("message", { - action: "ToggleLipSync", - id: characterId, + 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, + }; + }); }); }, []); @@ -209,15 +255,18 @@ export default function useUpdateCharacters() { ...character, animationClips: character.animationClips.map((clip) => ({ ...clip, - selected: clip.id === clipId ? !clip.selected : clip.selected, + selected: + clip.clip_uuid === clipId ? !clip.selected : clip.selected, })), positionClips: character.positionClips.map((clip) => ({ ...clip, - selected: clip.id === clipId ? !clip.selected : clip.selected, + selected: + clip.clip_uuid === clipId ? !clip.selected : clip.selected, })), lipSyncClips: character.lipSyncClips.map((clip) => ({ ...clip, - selected: clip.id === clipId ? !clip.selected : clip.selected, + selected: + clip.clip_uuid === clipId ? !clip.selected : clip.selected, })), })), ]; @@ -229,22 +278,42 @@ export default function useUpdateCharacters() { return [ ...oldCharacters.map((character) => ({ ...character, - animationClips: character.animationClips.filter( - (clip) => clip.id !== clipId, - ), - positionClips: character.positionClips.filter( - (clip) => clip.id !== clipId, - ), - lipSyncClips: character.lipSyncClips.filter( - (clip) => clip.id !== clipId, - ), + 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; + }), + positionClips: character.positionClips.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; + }), })), ]; }); - console.log("message", { - action: "DeleteCharacterClip", - id: clipId, - }); }, []); return { diff --git a/app/pages/PageEnigma/models/track.ts b/app/pages/PageEnigma/models/track.ts index 32299f513e..dd290db8c6 100644 --- a/app/pages/PageEnigma/models/track.ts +++ b/app/pages/PageEnigma/models/track.ts @@ -1,11 +1,50 @@ -export interface BaseClip { - id: string; +export interface QueueClip { + version: number; + type: ClipType; + group: ClipGroup; + object_uuid?: string; + clip_uuid?: string; + media_id?: string; + name?: string; + offset?: number; + length?: number; + selected?: boolean; +} + +export interface MediaClip { + version: number; + type: ClipType; + media_id: string; + name: string; + length: number; +} + +export interface Clip { + version: number; + clip_uuid: string; + type: ClipType; + group: ClipGroup; + media_id: string; + object_uuid?: string; name: string; offset: number; length: number; selected?: boolean; } +export enum ClipType { + TRANSFORM = "transform", + AUDIO = "audio", + ANIMATION = "animation", +} + +export enum ClipGroup { + CHARACTER = "character", + CAMERA = "camera", + GLOBAL_AUDIO = "global_audio", + OBJECT = "object", +} + export interface BaseKeyFrame { id: string; name: string; @@ -13,35 +52,25 @@ export interface BaseKeyFrame { selected?: boolean; } -export interface AnimationClip extends BaseClip {} - -export interface PositionClip extends BaseClip {} - -export interface LipSyncClip extends BaseClip {} - export interface CharacterGroup { id: string; muted: boolean; - animationClips: AnimationClip[]; - positionClips: PositionClip[]; - lipSyncClips: LipSyncClip[]; + animationClips: Clip[]; + positionClips: Clip[]; + lipSyncClips: Clip[]; } export interface CameraGroup { id: string; - clips: CameraClip[]; + clips: Clip[]; } -export interface CameraClip extends BaseClip {} - export interface AudioGroup { id: string; muted: boolean; - clips: AudioClip[]; + clips: Clip[]; } -export interface AudioClip extends BaseClip {} - export interface ObjectGroup { id: string; objects: ObjectTrack[]; diff --git a/app/pages/PageEnigma/store/dragAndDrop.ts b/app/pages/PageEnigma/store/dragAndDrop.ts index e38765ce73..7d0c98a940 100644 --- a/app/pages/PageEnigma/store/dragAndDrop.ts +++ b/app/pages/PageEnigma/store/dragAndDrop.ts @@ -7,3 +7,11 @@ export const overTimeline = signal(false); export const dropId = signal(""); export const dropOffset = signal(0); export const clipLength = signal(0); +export const initPosition = signal<{ initX: number; initY: number }>({ + initX: 0, + initY: 0, +}); +export const currPosition = signal<{ currX: number; currY: number }>({ + currX: 0, + currY: 0, +}); diff --git a/app/root.tsx b/app/root.tsx index f22de825a3..855484c581 100644 --- a/app/root.tsx +++ b/app/root.tsx @@ -107,4 +107,4 @@ function CompleteTakeoverLoadingScreen({isShowing}:{isShowing:boolean}){ ); -}; \ No newline at end of file +};