diff --git a/app/modules/DialogueInference/DialogueInference.tsx b/app/modules/DialogueInference/DialogueInference.tsx index 1439dd4d37..32676f3d7f 100644 --- a/app/modules/DialogueInference/DialogueInference.tsx +++ b/app/modules/DialogueInference/DialogueInference.tsx @@ -10,15 +10,18 @@ import { } from "@fortawesome/pro-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { deleteInferenceJob, inferenceJobs } from "~/pages/PageEnigma/store/inferenceJobs"; +import { + deleteInferenceJob, + inferenceJobs, +} from "~/pages/PageEnigma/store/inferenceJobs"; import { InferenceJob } from "~/pages/PageEnigma/models"; import { ButtonDialogue } from "../ButtonDialogue"; -import { ButtonIcon, H4,H6,P } from "~/components"; +import { ButtonIcon, H4, H6, P } from "~/components"; import { JobState } from "~/hooks/useInferenceJobManager/useInferenceJobManager"; -const printJobType = (jobType : string)=>{ - switch (jobType){ +const printJobType = (jobType: string) => { + switch (jobType) { case "generic": return "Text to Speech"; default: @@ -26,23 +29,23 @@ const printJobType = (jobType : string)=>{ } }; -const printJobStatus = (jobStatus : string)=>{ - switch (jobStatus){ +const printJobStatus = (jobStatus: string) => { + switch (jobStatus) { case JobState.PENDING: return "Adding to the queue."; case JobState.STARTED: return "Processing, please wait..."; case JobState.COMPLETE_SUCCESS: - return "Completed! See Audio Panel for the result. file." + return "Completed! See Audio Panel for the result. file."; default: return "Unknown Status"; } }; -const JobStatusIcon = ({jobStatus} : {jobStatus:string})=>{ - switch (jobStatus){ - case JobState.PENDING:{ - return( +const JobStatusIcon = ({ jobStatus }: { jobStatus: string }) => { + switch (jobStatus) { + case JobState.PENDING: { + return ( { /> ); } - case JobState.STARTED:{ - return( - ); } - case JobState.COMPLETE_SUCCESS:{ - return( + case JobState.COMPLETE_SUCCESS: { + return ( { /> ); } - default:{ - return( + default: { + return ( { } }; -const InferenceItem = ({job}:{job:InferenceJob}) => { - // console.log(job); - return( -
  • +const InferenceItem = ({ job }: { job: InferenceJob }) => { + return ( +
  • -

    {printJobType(job.job_type)}

    -
    {printJobStatus(job.job_status)}
    +

    {printJobType(job.job_type)}

    +
    {printJobStatus(job.job_status)}
    - {job.job_status === JobState.COMPLETE_SUCCESS && + {job.job_status === JobState.COMPLETE_SUCCESS && (
    { + onClick={() => { deleteInferenceJob(job); }} />
    - } + )}
  • ); }; -export const DialogueInference = () =>{ +export const DialogueInference = () => { useSignals(); - const buttonIcon = inferenceJobs.value.length > 0 - ? faClipboardList : faClipboard; - return( + const buttonIcon = + inferenceJobs.value.length > 0 ? faClipboardList : faClipboard; + return ( - {inferenceJobs.value.length > 0 && + className: inferenceJobs.value.length === 0 ? "invisible" : "visible", + }}> + {inferenceJobs.value.length > 0 && (
      - {inferenceJobs.value.map(job=> + {inferenceJobs.value.map((job) => ( - )} + ))}
    - } - { - inferenceJobs.value.length === 0 && + )} + {inferenceJobs.value.length === 0 && (

    You currently have no running proccesses.

    - } + )}
    ); -} - +}; diff --git a/app/modules/LowerPanel/LowerPanel.tsx b/app/modules/LowerPanel/LowerPanel.tsx index 33b641b823..e2d37203de 100644 --- a/app/modules/LowerPanel/LowerPanel.tsx +++ b/app/modules/LowerPanel/LowerPanel.tsx @@ -1,7 +1,7 @@ -import React, { useCallback, UIEvent } from "react"; +import React, { useCallback } from "react"; import { useMouseEventsTimeline } from "~/pages/PageEnigma/comps/Timeline/utils/useMouseEventsTimeline"; import { - currentScroll, + timelineScrollX, currentTime, dndTimelineHeight, overTimeline, @@ -29,7 +29,7 @@ export const LowerPanel = ({ children, onStyle }: LowerPanelPropsI) => { (event: React.PointerEvent) => { if (event.button === 0) { const newTime = Math.round( - (event.clientX + currentScroll.value - 92) / 4 / scale.value, + (event.clientX + timelineScrollX.value - 92) / 4 / scale.value, ); if (newTime < 0) { return; @@ -45,10 +45,6 @@ export const LowerPanel = ({ children, onStyle }: LowerPanelPropsI) => { [], ); - const onScroll = useCallback((event: UIEvent) => { - currentScroll.value = event.currentTarget.scrollLeft; - }, []); - return ( <> {!onStyle && ( @@ -59,19 +55,13 @@ export const LowerPanel = ({ children, onStyle }: LowerPanelPropsI) => { /> )}
    { overTimeline.value = true; }} onPointerLeave={() => (overTimeline.value = false)} onPointerDown={onTimelineClick} - onScroll={onScroll} > {children}
    diff --git a/app/pages/PageEnigma/PageEditor.tsx b/app/pages/PageEnigma/PageEditor.tsx index 0bd7c6ab5f..94614eaa4e 100644 --- a/app/pages/PageEnigma/PageEditor.tsx +++ b/app/pages/PageEnigma/PageEditor.tsx @@ -1,5 +1,4 @@ import React, { useContext } from "react"; -import { useParams } from "@remix-run/react"; import { LoadingBar, LoadingDots } from "~/components"; import { SidePanel } from "~/modules/SidePanel"; @@ -57,8 +56,7 @@ export const PageEditor = ({ setPage }: Props) => {
    + style={{ height: "calc(100vh - 68px)" }}> {/* Engine section/side panel */}
    { style={{ height, width, - }} - > + }}>
    + }> {/* Top controls */}
    + onClick={handleOverlayClick}>
    @@ -98,8 +93,7 @@ export const PageEditor = ({ setPage }: Props) => { (sidePanelVisible.value ? sidePanelWidth.value : 0) - 84, }} - onClick={handleOverlayClick} - > + onClick={handleOverlayClick}> diff --git a/app/pages/PageEnigma/PageEnigmaComponent.tsx b/app/pages/PageEnigma/PageEnigmaComponent.tsx index 867a227781..276347387b 100644 --- a/app/pages/PageEnigma/PageEnigmaComponent.tsx +++ b/app/pages/PageEnigma/PageEnigmaComponent.tsx @@ -7,7 +7,6 @@ import { PageEditor } from "~/pages/PageEnigma/PageEditor"; import { PageStyling } from "~/pages/PageEnigma/PageStyling"; import { useParams } from "@remix-run/react"; - export const PageEnigmaComponent = () => { useSignals(); @@ -15,16 +14,19 @@ export const PageEnigmaComponent = () => { const editor = useContext(EngineContext); const [page, setPage] = useState("edit"); const params = useParams(); - + useEffect(() => { if (editor && editor.can_initialize && dispatchAppUiState !== null) { console.log("initializing Editor"); - - const sceneToken = params["sceneToken"] - - editor.initialize({ - dispatchAppUiState - },sceneToken); + + const sceneToken = params["sceneToken"]; + + editor.initialize( + { + dispatchAppUiState, + }, + sceneToken, + ); } }, [editor, dispatchAppUiState]); diff --git a/app/pages/PageEnigma/PageStyling.tsx b/app/pages/PageEnigma/PageStyling.tsx index 5008a64c06..7ce76f2f12 100644 --- a/app/pages/PageEnigma/PageStyling.tsx +++ b/app/pages/PageEnigma/PageStyling.tsx @@ -5,15 +5,23 @@ import { faArrowsRotate, faFilm, } from "@fortawesome/pro-solid-svg-icons"; -import { Button,LoadingBar } from "~/components"; +import { Button, LoadingBar } from "~/components"; import { StyleSelection } from "~/pages/PageEnigma/comps/StyleSelection"; -import { LowerPanel } from "~/modules/LowerPanel"; -import { TimerGrid } from "~/pages/PageEnigma/comps/TimerGrid/TimerGrid"; +import { TimerGrid } from "~/pages/PageEnigma/comps/Timeline/TimerGrid"; import { Scrubber } from "~/pages/PageEnigma/comps/Timeline/Scrubber"; import { PreviewImages } from "~/pages/PageEnigma/comps/PreviewImages"; import { TopBar } from "~/modules/TopBar"; -import { useContext } from "react"; +import React, { UIEvent, useCallback, useContext } from "react"; import { EngineContext } from "~/contexts/EngineContext"; +import { + currentTime, + filmLength, + scale, + timelineScrollX, +} from "~/pages/PageEnigma/store"; +import Queue from "~/pages/PageEnigma/Queue/Queue"; +import { QueueNames } from "~/pages/PageEnigma/Queue/QueueNames"; +import { toEngineActions } from "~/pages/PageEnigma/Queue/toEngineActions"; interface Props { setPage: (page: string) => void; @@ -37,6 +45,30 @@ export const PageStyling = ({ setPage }: Props) => { setPage("edit"); }; + const onScroll = useCallback((event: UIEvent) => { + timelineScrollX.value = event.currentTarget.scrollLeft; + }, []); + + const onTimelineClick = useCallback( + (event: React.PointerEvent) => { + if (event.button === 0) { + const newTime = Math.round( + (event.clientX + timelineScrollX.value - 92) / 4 / scale.value, + ); + if (newTime < 0) { + return; + } + currentTime.value = newTime; + Queue.publish({ + queueName: QueueNames.TO_ENGINE, + action: toEngineActions.UPDATE_TIME, + data: { currentTime: Math.round(newTime) }, + }); + } + }, + [], + ); + return (
    @@ -56,8 +88,7 @@ export const PageStyling = ({ setPage }: Props) => {
    - - - - +
    +
    + + +
    +
    + id="editor-loading-bar" + wrapperClassName="absolute top-0 left-0" + innerWrapperClassName="max-w-screen-sm" + isShowing={appUiState.showEditorLoadingBar.isShowing} + message={appUiState.showEditorLoadingBar.message} + label={appUiState.showEditorLoadingBar.label} + progress={appUiState.showEditorLoadingBar.progress} + />
    ); }; diff --git a/app/pages/PageEnigma/comps/ControlPanelSceneObject/ControlPanelSceneObject.tsx b/app/pages/PageEnigma/comps/ControlPanelSceneObject/ControlPanelSceneObject.tsx index 8d0f0f8642..47d7a976d0 100644 --- a/app/pages/PageEnigma/comps/ControlPanelSceneObject/ControlPanelSceneObject.tsx +++ b/app/pages/PageEnigma/comps/ControlPanelSceneObject/ControlPanelSceneObject.tsx @@ -7,8 +7,8 @@ import { } from "@fortawesome/pro-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { AppUiContext } from "../../../../contexts/AppUiContext"; -import { EngineContext } from "../../../../contexts/EngineContext"; +import { AppUiContext } from "~/contexts/AppUiContext"; +import { EngineContext } from "~/contexts/EngineContext"; import { Button, H5, InputVector } from "~/components"; import { XYZ } from "../../datastructures/common"; @@ -33,6 +33,9 @@ export const ControlPanelSceneObject = () => { useEffect(() => { // TODO this causes a subtle bug because it renders way too many times. + if (!appUiState.controlPanel.currentSceneObject) { + return; + } const vectors = appUiState.controlPanel.currentSceneObject.objectVectors; editorEngine?.setSelectedObject( @@ -176,8 +179,7 @@ export const ControlPanelSceneObject = () => { enterTo="opacity-100" leave="transition-opacity duration-100" leaveFrom="opacity-100" - leaveTo="opacity-0" - > + leaveTo="opacity-0">
    @@ -227,8 +229,7 @@ export const ControlPanelSceneObject = () => { - -
    - - - + + +
    - ) -} \ No newline at end of file + ); +}; diff --git a/app/pages/PageEnigma/comps/Timeline/Audio.tsx b/app/pages/PageEnigma/comps/Timeline/Audio.tsx index b6d716a765..094aa4fbb5 100644 --- a/app/pages/PageEnigma/comps/Timeline/Audio.tsx +++ b/app/pages/PageEnigma/comps/Timeline/Audio.tsx @@ -1,10 +1,5 @@ import { TrackClips } from "~/pages/PageEnigma/comps/Timeline/TrackClips"; -import { - audioGroup, - fullWidth, - toggleAudioMute, - updateAudio, -} from "~/pages/PageEnigma/store"; +import { audioGroup, fullWidth, updateAudio } from "~/pages/PageEnigma/store"; import { ClipGroup } from "~/pages/PageEnigma/models"; export const Audio = () => { @@ -12,21 +7,16 @@ export const Audio = () => { return (
    -
    - Global Audio -
    -
    +
    diff --git a/app/pages/PageEnigma/comps/Timeline/Camera.tsx b/app/pages/PageEnigma/comps/Timeline/Camera.tsx index 55d97e85d2..06a55ad090 100644 --- a/app/pages/PageEnigma/comps/Timeline/Camera.tsx +++ b/app/pages/PageEnigma/comps/Timeline/Camera.tsx @@ -9,11 +9,10 @@ export const Camera = () => { return (
    -
    Camera
    -
    +
    { - const character = characterGroups.value.find((row) => (row.id = characterId)); - +export const Character = ({ character }: Props) => { const { updateClipLipSync, updateClipPosition, updateClipAnimations } = useMemo(() => buildUpdaters(updateCharacters), []); - const toggleCharacterLipSyncMute = useCallback(() => { - toggleLipSyncMute(character?.id ?? ""); - }, [character?.id]); - - if (!character) { - return false; - } const { animationClips, positionKeyframes, lipSyncClips } = character; return (
    -
    - Character -
    -
    +
    { type={ClipType.ANIMATION} /> diff --git a/app/pages/PageEnigma/comps/Timeline/Characters.tsx b/app/pages/PageEnigma/comps/Timeline/Characters.tsx index 81db814d3a..35b9236b50 100644 --- a/app/pages/PageEnigma/comps/Timeline/Characters.tsx +++ b/app/pages/PageEnigma/comps/Timeline/Characters.tsx @@ -1,4 +1,4 @@ -import { characterGroups } from "~/pages/PageEnigma/store"; +import { characterGroup } from "~/pages/PageEnigma/store"; import { Character } from "~/pages/PageEnigma/comps/Timeline/Character"; import { useSignals } from "@preact/signals-react/runtime"; @@ -6,8 +6,10 @@ export const Characters = () => { useSignals(); return ( <> - {characterGroups.value.map((character) => ( - + {characterGroup.value.characters.map((character) => ( +
    + +
    ))} ); diff --git a/app/pages/PageEnigma/comps/Timeline/ObjectGroups.tsx b/app/pages/PageEnigma/comps/Timeline/ObjectGroups.tsx index 3e9e634c5d..4dab178184 100644 --- a/app/pages/PageEnigma/comps/Timeline/ObjectGroups.tsx +++ b/app/pages/PageEnigma/comps/Timeline/ObjectGroups.tsx @@ -1,17 +1,17 @@ import { objectGroup } from "~/pages/PageEnigma/store"; import { useSignals } from "@preact/signals-react/runtime"; -import { ObjectTrack } from "~/pages/PageEnigma/comps/Timeline/ObjectTrack"; +import { ObjectTrackComponent } from "~/pages/PageEnigma/comps/Timeline/ObjectTrack"; export const ObjectGroups = () => { useSignals(); return ( <> - {objectGroup.value.objects.length > 0 && ( -
    - + {objectGroup.value.objects.map((object) => ( +
    +
    - )} + ))} ); }; diff --git a/app/pages/PageEnigma/comps/Timeline/ObjectTrack.tsx b/app/pages/PageEnigma/comps/Timeline/ObjectTrack.tsx index 6ee2460961..da2806c9a9 100644 --- a/app/pages/PageEnigma/comps/Timeline/ObjectTrack.tsx +++ b/app/pages/PageEnigma/comps/Timeline/ObjectTrack.tsx @@ -1,27 +1,26 @@ -import { fullWidth, objectGroup, updateObject } from "~/pages/PageEnigma/store"; +import { fullWidth, updateObject } from "~/pages/PageEnigma/store"; import { TrackKeyFrames } from "~/pages/PageEnigma/comps/Timeline/TrackKeyFrames"; -import { ClipGroup } from "~/pages/PageEnigma/models"; +import { ClipGroup, ObjectTrack } from "~/pages/PageEnigma/models"; -export const ObjectTrack = () => { +interface Props { + object: ObjectTrack; +} + +export const ObjectTrackComponent = ({ object }: Props) => { return (
    -
    - Objects +
    +
    - {objectGroup.value.objects.map((object) => ( -
    - -
    - ))}
    ); }; diff --git a/app/pages/PageEnigma/comps/Timeline/RowHeaders.tsx b/app/pages/PageEnigma/comps/Timeline/RowHeaders.tsx new file mode 100644 index 0000000000..0d519e7c50 --- /dev/null +++ b/app/pages/PageEnigma/comps/Timeline/RowHeaders.tsx @@ -0,0 +1,89 @@ +import { + audioGroup, + characterGroup, + objectGroup, + timelineScrollY, + toggleAudioMute, + toggleLipSyncMute, +} from "~/pages/PageEnigma/store"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { faVolume, faVolumeSlash } from "@fortawesome/pro-solid-svg-icons"; +import { useSignals } from "@preact/signals-react/runtime"; + +export const RowHeaders = () => { + useSignals(); + + return ( +
    +
    + {characterGroup.value.characters.map((character) => ( +
    +
    +
    + Character +
    +
    + +
    +
    +
    + ))} +
    +
    +
    + Camera +
    +
    +
    +
    +
    +
    + Global Audio +
    +
    + +
    +
    +
    + {objectGroup.value.objects.map((obj) => ( +
    +
    +
    + Object +
    +
    +
    + ))} +
    +
    + ); +}; diff --git a/app/pages/PageEnigma/comps/Timeline/Scrubber.tsx b/app/pages/PageEnigma/comps/Timeline/Scrubber.tsx index 7f0f24cad2..7cea4086b7 100644 --- a/app/pages/PageEnigma/comps/Timeline/Scrubber.tsx +++ b/app/pages/PageEnigma/comps/Timeline/Scrubber.tsx @@ -1,4 +1,9 @@ -import { currentTime, fullHeight, scale } from "~/pages/PageEnigma/store"; +import { + timelineScrollX, + currentTime, + fullHeight, + scale, +} from "~/pages/PageEnigma/store"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faSortDown } from "@fortawesome/pro-solid-svg-icons"; import { useMouseEventsScrubber } from "~/pages/PageEnigma/comps/Timeline/utils/useMouseEventsScrubber"; @@ -9,10 +14,16 @@ export const Scrubber = () => { const { onPointerDown, time } = useMouseEventsScrubber(); const displayTime = time === -1 ? currentTime.value : time; + if (displayTime * 4 * scale.value + 84 - timelineScrollX.value < 84) { + return null; + } return (
    diff --git a/app/pages/PageEnigma/comps/Timeline/Timeline.tsx b/app/pages/PageEnigma/comps/Timeline/Timeline.tsx index dcbc98147b..7887b5fe71 100644 --- a/app/pages/PageEnigma/comps/Timeline/Timeline.tsx +++ b/app/pages/PageEnigma/comps/Timeline/Timeline.tsx @@ -1,4 +1,4 @@ -import { useCallback, useEffect, useState } from "react"; +import { UIEvent, useCallback, useEffect, useState } from "react"; import { LowerPanel } from "~/modules/LowerPanel"; import { Camera } from "./Camera"; @@ -10,15 +10,21 @@ import { deleteCharacterClip, selectedItem, ignoreDelete, + timelineScrollX, + timelineScrollY, + filmLength, + scale, } from "~/pages/PageEnigma/store"; import { useQueueHandler } from "~/pages/PageEnigma/comps/Timeline/utils/useQueueHandler"; import { useSignals } from "@preact/signals-react/runtime"; -import { TimerGrid } from "~/pages/PageEnigma/comps/TimerGrid/TimerGrid"; +import { TimerGrid } from "~/pages/PageEnigma/comps/Timeline/TimerGrid"; import { Scrubber } from "~/pages/PageEnigma/comps/Timeline/Scrubber"; import { Characters } from "~/pages/PageEnigma/comps/Timeline/Characters"; import { ObjectGroups } from "~/pages/PageEnigma/comps/Timeline/ObjectGroups"; import useUpdateKeyframe from "~/pages/PageEnigma/contexts/TrackContext/utils/useUpdateKeyframe"; import { Clip, Keyframe } from "~/pages/PageEnigma/models"; +import { RowHeaders } from "~/pages/PageEnigma/comps/Timeline/RowHeaders"; +import { pageWidth } from "~/store"; function getItemType(item: Clip | Keyframe | null) { if (!item) { @@ -32,6 +38,11 @@ export const Timeline = () => { const [dialogOpen, setDialogOpen] = useState(false); const { deleteKeyframe } = useUpdateKeyframe(); + const onScroll = useCallback((event: UIEvent) => { + timelineScrollX.value = event.currentTarget.scrollLeft; + timelineScrollY.value = event.currentTarget.scrollTop; + }, []); + // implement the code to handle incoming messages from the Engine useQueueHandler(); @@ -75,17 +86,39 @@ export const Timeline = () => { <> -
    - +
    +
    + +
    +
    +
    + +
    + +
    +
    +
    + +
    +
    +
    -
    - -
    -
    -
    - - { + useSignals(); + const sectionWidth = 60 * 4 * scale.value; + + return ( +
    +
    + {Array(filmLength.value) + .fill(0) + .map((_, index) => ( + +
    + 00:{index < 10 ? "0" + index.toString() : index.toString()} +
    +
    + {Array(9) + .fill(0) + .map((_, ind) => ( +
    + ))} + + ))} +
    + 00: + {filmLength.value < 10 + ? "0" + filmLength.value.toString() + : filmLength.value.toString()} +
    +
    +
    +
    + ); +}; diff --git a/app/pages/PageEnigma/comps/Timeline/TrackClips.tsx b/app/pages/PageEnigma/comps/Timeline/TrackClips.tsx index 23952825c8..771b152b17 100644 --- a/app/pages/PageEnigma/comps/Timeline/TrackClips.tsx +++ b/app/pages/PageEnigma/comps/Timeline/TrackClips.tsx @@ -23,8 +23,6 @@ interface Props { title: string; group: ClipGroup; type?: ClipType; - toggleMute?: () => void; - muted?: boolean; updateClip: (options: { id: string; length: number; offset: number }) => void; } @@ -56,9 +54,7 @@ function getCanBuild({ export const TrackClips = ({ id, clips, - toggleMute, updateClip, - muted, title, group, type, @@ -81,7 +77,7 @@ export const TrackClips = ({ return; } - // Now check the the clip fits + // Now check if the clip fits const position = track.getBoundingClientRect(); const clipOffset = (event.clientX - position.x) / 4 / scale.value; @@ -114,49 +110,31 @@ export const TrackClips = ({ } return ( -
    -
    - {clips.map((clip, index) => ( - 0 ? clips[index - 1].offset + clips[index - 1].length : 0 - } - max={ - index < clips.length - 1 - ? clips[index + 1].offset - : filmLength.value * 60 - } - group={group} - updateClip={updateClip} - clip={clip} - /> - ))} -
    - {title} -
    - {!!toggleMute && ( - - )} +
    + {clips.map((clip, index) => ( + 0 ? clips[index - 1].offset + clips[index - 1].length : 0 + } + max={ + index < clips.length - 1 + ? clips[index + 1].offset + : filmLength.value * 60 + } + group={group} + updateClip={updateClip} + clip={clip} + /> + ))} +
    + {title}
    ); diff --git a/app/pages/PageEnigma/comps/Timeline/TrackKeyFrames.tsx b/app/pages/PageEnigma/comps/Timeline/TrackKeyFrames.tsx index 5035fec17f..7cec6fffdb 100644 --- a/app/pages/PageEnigma/comps/Timeline/TrackKeyFrames.tsx +++ b/app/pages/PageEnigma/comps/Timeline/TrackKeyFrames.tsx @@ -24,7 +24,7 @@ export const TrackKeyFrames = ({ group, }: Props) => { return ( -
    +
    { Queue.publish({ queueName: QueueNames.TO_ENGINE, action: toEngineActions.UPDATE_TIME, - data: { currentTime: Math.round(time) + currentScroll.value }, + data: { currentTime: Math.round(time) + timelineScrollX.value }, }); } }; @@ -46,7 +46,7 @@ export const useMouseEventsScrubber = () => { Queue.publish({ queueName: QueueNames.TO_ENGINE, action: toEngineActions.UPDATE_TIME, - data: { currentTime: delta + currentScroll.value }, + data: { currentTime: delta + timelineScrollX.value }, }); } return delta; diff --git a/app/pages/PageEnigma/comps/Timeline/utils/useQueueHandler.ts b/app/pages/PageEnigma/comps/Timeline/utils/useQueueHandler.ts index 8d021a2958..8546aab3ec 100644 --- a/app/pages/PageEnigma/comps/Timeline/utils/useQueueHandler.ts +++ b/app/pages/PageEnigma/comps/Timeline/utils/useQueueHandler.ts @@ -40,31 +40,34 @@ export function useQueueHandler() { const { addKeyframe, clearExistingData, deleteObjectOrCharacter } = useContext(TrackContext); - const handleFromEngineActions = useCallback(({ action, data }: Arguments) => { - console.log("FROM ENGINE", action, data); - switch (action) { - case fromEngineActions.UPDATE_TIME: - currentTime.value = (data as UpdateTime).currentTime; - break; - case fromEngineActions.UPDATE_TIME_LINE: - clearExistingData(); - (data as ClipUI[]).forEach((item) => { - LOADING_FUNCTIONS[item.group](item); - }); - break; - case fromEngineActions.UPDATE_CHARACTER_ID: { - addNewCharacter(data as MediaItem); - break; + const handleFromEngineActions = useCallback( + ({ action, data }: Arguments) => { + console.log("FROM ENGINE", action, data); + switch (action) { + case fromEngineActions.UPDATE_TIME: + currentTime.value = (data as UpdateTime).currentTime; + break; + case fromEngineActions.UPDATE_TIME_LINE: + clearExistingData(); + (data as ClipUI[]).forEach((item) => { + LOADING_FUNCTIONS[item.group](item); + }); + break; + case fromEngineActions.UPDATE_CHARACTER_ID: { + addNewCharacter(data as MediaItem); + break; + } + case fromEngineActions.DELETE_OBJECT: { + // this could be an object or character + deleteObjectOrCharacter(data as MediaItem); + break; + } + default: + throw new Error(`Unknown action ${action}`); } - case fromEngineActions.DELETE_OBJECT: { - // this could be an object or character - deleteObjectOrCharacter(data as MediaItem); - break; - } - default: - throw new Error(`Unknown action ${action}`); - } - }, []); + }, + [clearExistingData, deleteObjectOrCharacter], + ); const handleToTimelineActions = useCallback( ({ action, data }: Arguments) => { diff --git a/app/pages/PageEnigma/comps/TimerGrid/TimerGrid.tsx b/app/pages/PageEnigma/comps/TimerGrid/TimerGrid.tsx deleted file mode 100644 index 6a160f6896..0000000000 --- a/app/pages/PageEnigma/comps/TimerGrid/TimerGrid.tsx +++ /dev/null @@ -1,55 +0,0 @@ -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/TrackProvider.tsx b/app/pages/PageEnigma/contexts/TrackContext/TrackProvider.tsx index 5132e53acc..471724640d 100644 --- a/app/pages/PageEnigma/contexts/TrackContext/TrackProvider.tsx +++ b/app/pages/PageEnigma/contexts/TrackContext/TrackProvider.tsx @@ -12,7 +12,7 @@ import { dropId, dropOffset, addObject, - characterGroups, + characterGroup, cameraGroup, audioGroup, objectGroup, @@ -75,7 +75,10 @@ export const TrackProvider = ({ children }: Props) => { }, [endDrag]); const clearExistingData = useCallback(() => { - characterGroups.value = []; + characterGroup.value = { + id: "ChG1", + characters: [], + }; cameraGroup.value = { id: "CG1", keyframes: [], diff --git a/app/pages/PageEnigma/js/editor.ts b/app/pages/PageEnigma/js/editor.ts index 775c82b33d..e1c508afb1 100644 --- a/app/pages/PageEnigma/js/editor.ts +++ b/app/pages/PageEnigma/js/editor.ts @@ -219,11 +219,12 @@ class Editor { } isEmpty(value: string) { - return (value == null || (typeof value === "string" && value.trim().length === 0)); + return ( + value == null || (typeof value === "string" && value.trim().length === 0) + ); } initialize(config: any, sceneToken) { - //setup reactland Callbacks this.dispatchAppUiState = config.dispatchAppUiState; @@ -358,7 +359,7 @@ class Editor { this.updateLoop(); if (this.isEmpty(sceneToken) == false) { - this.loadScene(sceneToken) + this.loadScene(sceneToken); } this.dispatchAppUiState({ @@ -384,7 +385,7 @@ class Editor { console.log(result); } - public async testTestTimelineEvents() { } + public async testTestTimelineEvents() {} public async loadScene(scene_media_token: string) { this.dispatchAppUiState({ @@ -427,7 +428,7 @@ class Editor { // TO UPDATE selected objects in the scene might want to add to the scene ... async setSelectedObject(position: XYZ, rotation: XYZ, scale: XYZ) { if (this.selected != undefined || this.selected != null) { - //console.log(`triggering setSelectedObject`) + console.log(`triggering setSelectedObject`); this.selected.position.x = position.x; this.selected.position.y = position.y; this.selected.position.z = position.z; @@ -528,6 +529,7 @@ class Editor { this.removeTransformControls(); this.selected = this.cam_obj; + this.dispatchAppUiState({ type: APPUI_ACTION_TYPES.SHOW_CONTROLPANELS_SCENEOBJECT, }); @@ -574,7 +576,7 @@ class Editor { payload: { showEditorLoadingBar: { progress: progress, - message: message + message: message, }, }, }); @@ -719,7 +721,9 @@ class Editor { this.rawRenderer.setSize(1024, 576); this.render_camera.aspect = 1024 / 576; this.record_stream = this.rawRenderer.domElement.captureStream(60); // Capture at 60 FPS - this.recorder = new MediaRecorder(this.record_stream, { mimeType: 'video/webm' }); + this.recorder = new MediaRecorder(this.record_stream, { + mimeType: "video/webm", + }); this.recorder.ondataavailable = (event) => { if (event.data.size > 0) { this.frame_buffer.push(event.data); @@ -729,7 +733,7 @@ class Editor { }; this.recorder.onstop = () => { this.stopPlayback(); - } + }; this.recorder.start(); } @@ -797,7 +801,7 @@ class Editor { if (changeView) { this.switchCameraView(); } - } else if (this.last_scrub == this.timeline.scrubber_frame_position) { + } else if (this.last_scrub !== this.timeline.scrubber_frame_position) { this.updateSelectedUI(); } @@ -864,10 +868,10 @@ class Editor { audioSegment, "-filter_complex", "[1:a]adelay=" + - startTime * 1000 + - "|" + - startTime * 1000 + - "[a1];[0:a][a1]amix=inputs=2[a]", + startTime * 1000 + + "|" + + startTime * 1000 + + "[a1];[0:a][a1]amix=inputs=2[a]", "-map", "[a]", `${itteration}final_tmp.wav`, @@ -895,9 +899,9 @@ class Editor { async _debugDownloadVideo(videoURL: string) { // DEBUG ONLY to download the video - let a = document.createElement('a'); + const a = document.createElement("a"); a.href = videoURL; - a.download = 'video.mp4'; // Name of the downloaded file + a.download = "video.mp4"; // Name of the downloaded file document.body.appendChild(a); a.click(); // Trigger the download } @@ -906,18 +910,17 @@ class Editor { //let video_fps = Math.floor(this.frames * (this.cap_fps / this.timeline.timeline_limit)); //console.log("Video FPS:", video_fps) this.rendering = false; - const videoBlob = new Blob(this.frame_buffer, { type: 'video/webm' }); + const videoBlob = new Blob(this.frame_buffer, { type: "video/webm" }); const videoURL = URL.createObjectURL(videoBlob); this.generating_preview = true; const ffmpeg = createFFmpeg({ log: true }); await ffmpeg.load(); - this.updateLoad(50, "Processing ...") + this.updateLoad(50, "Processing ..."); // Write the Uint8Array to the FFmpeg file system - ffmpeg.FS('writeFile', 'input.webm', await fetchFile(videoURL)); - + ffmpeg.FS("writeFile", "input.webm", await fetchFile(videoURL)); await ffmpeg.run( "-i", @@ -1106,13 +1109,12 @@ class Editor { // This initializes the generation of a video render scene is where the core work happens generateVideo() { - console.log("Generating video...", this.frame_buffer); if (this.rendering) { return; } - this.showLoading() + this.showLoading(); this.rendering = true; // has to go first to debounce this.startPlayback(); @@ -1127,8 +1129,7 @@ class Editor { } startPlayback() { - - this.updateLoad(25, "Starting Processing") + this.updateLoad(25, "Starting Processing"); this.timeline.is_playing = true; this.timeline.scrubber_frame_position = 0; @@ -1143,7 +1144,7 @@ class Editor { } updateSelectedUI() { - if (this.selected == undefined) { + if (this.selected === undefined || this.timeline.is_playing) { return; } const pos = this.selected.position; diff --git a/app/pages/PageEnigma/models/track.ts b/app/pages/PageEnigma/models/track.ts index 1ba2096b85..09c4340895 100644 --- a/app/pages/PageEnigma/models/track.ts +++ b/app/pages/PageEnigma/models/track.ts @@ -41,6 +41,12 @@ export interface Keyframe { export interface CharacterGroup { id: string; + characters: CharacterTrack[]; +} + +export interface CharacterTrack { + object_uuid: string; + media_id: string; name: string; muted: boolean; animationClips: Clip[]; diff --git a/app/pages/PageEnigma/store/characterGroups/addCharacter.ts b/app/pages/PageEnigma/store/characterGroups/addCharacter.ts index c68a300002..470847c3a6 100644 --- a/app/pages/PageEnigma/store/characterGroups/addCharacter.ts +++ b/app/pages/PageEnigma/store/characterGroups/addCharacter.ts @@ -1,8 +1,8 @@ -import { CharacterGroup, MediaItem } from "~/pages/PageEnigma/models"; +import { CharacterTrack, MediaItem } from "~/pages/PageEnigma/models"; import Queue from "~/pages/PageEnigma/Queue/Queue"; import { QueueNames } from "~/pages/PageEnigma/Queue/QueueNames"; import { toEngineActions } from "~/pages/PageEnigma/Queue/toEngineActions"; -import { characterGroups } from "~/pages/PageEnigma/store"; +import { characterGroup } from "~/pages/PageEnigma/store"; export function addCharacter(character: MediaItem) { Queue.publish({ @@ -13,16 +13,25 @@ export function addCharacter(character: MediaItem) { } export function addNewCharacter(data: MediaItem) { + const newCharacterGroups = { + ...characterGroup.value, + characters: [...characterGroup.value.characters], + }; + const newCharacter = { - id: data.object_uuid, + object_uuid: data.object_uuid, name: data.name, + media_id: data.media_id, muted: false, animationClips: [], positionKeyframes: [], lipSyncClips: [], - } as CharacterGroup; + } as CharacterTrack; - characterGroups.value = [...characterGroups.value, newCharacter].sort( - (charA, charB) => (charA.id < charB.id ? -1 : 1), + newCharacterGroups.characters.push(newCharacter); + newCharacterGroups.characters.sort((charA, charB) => + charA.object_uuid < charB.object_uuid ? -1 : 1, ); + + characterGroup.value = newCharacterGroups; } diff --git a/app/pages/PageEnigma/store/characterGroups/characterGroup.ts b/app/pages/PageEnigma/store/characterGroups/characterGroup.ts new file mode 100644 index 0000000000..254a7abfea --- /dev/null +++ b/app/pages/PageEnigma/store/characterGroups/characterGroup.ts @@ -0,0 +1,276 @@ +import { + CharacterGroup, + Clip, + ClipGroup, + ClipType, + Keyframe, + MediaItem, + QueueKeyframe, +} from "~/pages/PageEnigma/models"; +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 characterGroup = signal({ + id: "CG1", + characters: [], +}); + +export function addCharacterAnimation({ + dragItem, + characterId, + offset, +}: { + dragItem: MediaItem; + characterId: string; + offset: number; +}) { + const clip_uuid = uuid.v4(); + const newClip = { + version: 1, + media_id: dragItem.media_id, + group: ClipGroup.CHARACTER, + type: ClipType.ANIMATION, + offset, + length: dragItem.length, + clip_uuid, + name: dragItem.name, + object_uuid: characterId, + } as Clip; + + const oldCharacterGroup = characterGroup.value; + characterGroup.value = { + ...oldCharacterGroup, + characters: oldCharacterGroup.characters.map((character) => { + if (character.object_uuid !== characterId) { + return { ...character }; + } + + Queue.publish({ + queueName: QueueNames.TO_ENGINE, + action: toEngineActions.ADD_CLIP, + data: newClip, + }); + + return { + ...character, + animationClips: [...character.animationClips, newClip].sort( + (clipA, clipB) => clipA.offset - clipB.offset, + ), + }; + }), + }; +} + +export function addCharacterAudio({ + dragItem, + characterId, + offset, +}: { + dragItem: MediaItem; + characterId: string; + offset: number; +}) { + const clip_uuid = uuid.v4(); + const newClip = { + version: 1, + media_id: dragItem.media_id, + group: ClipGroup.CHARACTER, + type: ClipType.AUDIO, + offset, + length: dragItem.length, + clip_uuid, + object_uuid: characterId, + name: dragItem.name, + } as Clip; + + const oldCharacterGroup = characterGroup.value; + characterGroup.value = { + ...oldCharacterGroup, + characters: oldCharacterGroup.characters.map((character) => { + if (character.object_uuid !== characterId) { + return { ...character }; + } + + Queue.publish({ + queueName: QueueNames.TO_ENGINE, + action: toEngineActions.ADD_CLIP, + data: newClip, + }); + + return { + ...character, + lipSyncClips: [...character.lipSyncClips, newClip].sort( + (clipA, clipB) => clipA.offset - clipB.offset, + ), + }; + }), + }; +} + +export function addCharacterKeyframe( + keyframe: QueueKeyframe, + offset: number, + addToast: (type: "error" | "warning" | "success", message: string) => void, +) { + const oldCharacterGroup = characterGroup.value; + + if ( + oldCharacterGroup.characters.some((characterTrack) => { + if (characterTrack.object_uuid !== keyframe.object_uuid) { + return; + } + return characterTrack.positionKeyframes.some( + (row) => row.offset === offset, + ); + }) + ) { + addToast("warning", "There can only be one keyframe at this offset."); + return; + } + + 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; + + characterGroup.value = { + ...oldCharacterGroup, + characters: oldCharacterGroup.characters.map((character) => { + if (character.object_uuid !== keyframe.object_uuid) { + return { ...character }; + } + + Queue.publish({ + queueName: QueueNames.TO_ENGINE, + action: toEngineActions.ADD_CLIP, + data: newKeyframe, + }); + + return { + ...character, + positionKeyframes: [...character.positionKeyframes, newKeyframe].sort( + (clipA, clipB) => clipA.offset - clipB.offset, + ), + }; + }), + }; +} + +export function toggleLipSyncMute(characterId: string) { + const oldCharacterGroup = characterGroup.value; + characterGroup.value = { + ...oldCharacterGroup, + characters: oldCharacterGroup.characters.map((character) => { + if (character.object_uuid === 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.object_uuid === characterId + ? !character.muted + : character.muted, + }; + }), + }; +} + +export function selectCharacterClip(clipId: string) { + const oldCharacterGroup = characterGroup.value; + characterGroup.value = { + ...oldCharacterGroup, + characters: oldCharacterGroup.characters.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(clip: Clip) { + const oldCharacterGroup = characterGroup.value; + characterGroup.value = { + ...oldCharacterGroup, + characters: oldCharacterGroup.characters.map((character) => ({ + ...character, + animationClips: character.animationClips.filter((row) => { + if (row.clip_uuid === clip.clip_uuid) { + Queue.publish({ + queueName: QueueNames.TO_ENGINE, + action: toEngineActions.DELETE_CLIP, + data: row, + }); + return false; + } + return true; + }), + lipSyncClips: character.lipSyncClips.filter((row) => { + if (row.clip_uuid === clip.clip_uuid) { + Queue.publish({ + queueName: QueueNames.TO_ENGINE, + action: toEngineActions.DELETE_CLIP, + data: row, + }); + return false; + } + return true; + }), + })), + }; +} + +export function deleteCharacterKeyframe(keyframe: Keyframe) { + const oldCharacterGroup = characterGroup.value; + characterGroup.value = { + ...oldCharacterGroup, + characters: oldCharacterGroup.characters.map((character) => ({ + ...character, + positionKeyframes: 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/characterGroups/characterGroups.ts b/app/pages/PageEnigma/store/characterGroups/characterGroups.ts deleted file mode 100644 index cfe8e61149..0000000000 --- a/app/pages/PageEnigma/store/characterGroups/characterGroups.ts +++ /dev/null @@ -1,245 +0,0 @@ -import { - CharacterGroup, - Clip, - ClipGroup, - ClipType, - Keyframe, - MediaItem, - QueueKeyframe, -} from "~/pages/PageEnigma/models"; -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([]); - -export function addCharacterAnimation({ - dragItem, - characterId, - offset, -}: { - dragItem: MediaItem; - characterId: string; - offset: number; -}) { - const clip_uuid = uuid.v4(); - const newClip = { - version: 1, - media_id: dragItem.media_id, - group: ClipGroup.CHARACTER, - type: ClipType.ANIMATION, - offset, - length: dragItem.length, - clip_uuid, - name: dragItem.name, - 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({ - dragItem, - characterId, - offset, -}: { - dragItem: MediaItem; - characterId: string; - offset: number; -}) { - const clip_uuid = uuid.v4(); - const newClip = { - version: 1, - media_id: dragItem.media_id, - group: ClipGroup.CHARACTER, - type: ClipType.AUDIO, - offset, - length: dragItem.length, - clip_uuid, - object_uuid: characterId, - name: dragItem.name, - } as Clip; - - const oldCharacterGroups = characterGroups.value; - characterGroups.value = oldCharacterGroups.map((character) => { - if (character.id !== characterId) { - return { ...character }; - } - - Queue.publish({ - queueName: QueueNames.TO_ENGINE, - action: toEngineActions.ADD_CLIP, - data: newClip, - }); - - return { - ...character, - lipSyncClips: [...character.lipSyncClips, newClip].sort( - (clipA, clipB) => clipA.offset - clipB.offset, - ), - }; - }); -} - -export function addCharacterKeyframe( - keyframe: QueueKeyframe, - offset: number, - addToast: (type: "error" | "warning" | "success", message: string) => void, -) { - const oldCharacterGroups = characterGroups.value; - - if ( - oldCharacterGroups.some((characterGroup) => { - return characterGroup.positionKeyframes.some( - (row) => row.offset === offset, - ); - }) - ) { - addToast("warning", "There can only be one keyframe at this offset."); - return; - } - - 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; - - 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(clip: Clip) { - const oldCharacterGroups = characterGroups.value; - characterGroups.value = oldCharacterGroups.map((character) => ({ - ...character, - animationClips: character.animationClips.filter((row) => { - if (row.clip_uuid === clip.clip_uuid) { - Queue.publish({ - queueName: QueueNames.TO_ENGINE, - action: toEngineActions.DELETE_CLIP, - data: row, - }); - return false; - } - return true; - }), - lipSyncClips: character.lipSyncClips.filter((row) => { - if (row.clip_uuid === clip.clip_uuid) { - Queue.publish({ - queueName: QueueNames.TO_ENGINE, - action: toEngineActions.DELETE_CLIP, - data: row, - }); - return false; - } - return true; - }), - })); -} - -export function deleteCharacterKeyframe(keyframe: Keyframe) { - const oldCharacterGroups = characterGroups.value; - characterGroups.value = oldCharacterGroups.map((character) => ({ - ...character, - positionKeyframes: 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/characterGroups/deleteCharacter.ts b/app/pages/PageEnigma/store/characterGroups/deleteCharacter.ts index 84c00ad0b8..7a26906c37 100644 --- a/app/pages/PageEnigma/store/characterGroups/deleteCharacter.ts +++ b/app/pages/PageEnigma/store/characterGroups/deleteCharacter.ts @@ -1,8 +1,11 @@ import { MediaItem } from "~/pages/PageEnigma/models"; -import { characterGroups } from "~/pages/PageEnigma/store"; +import { characterGroup } from "~/pages/PageEnigma/store"; export function deleteCharacter(item: MediaItem) { - characterGroups.value = characterGroups.value.filter( - (character) => character.id !== item.object_uuid, - ); + characterGroup.value = { + ...characterGroup.value, + characters: characterGroup.value.characters.filter( + (character) => character.object_uuid !== item.object_uuid, + ), + }; } diff --git a/app/pages/PageEnigma/store/characterGroups/index.ts b/app/pages/PageEnigma/store/characterGroups/index.ts index 1357ba2e4d..01bcfd0ae0 100644 --- a/app/pages/PageEnigma/store/characterGroups/index.ts +++ b/app/pages/PageEnigma/store/characterGroups/index.ts @@ -1,5 +1,5 @@ export * from "./addCharacter"; -export * from "./characterGroups"; +export * from "./characterGroup"; export * from "./deleteCharacter"; export * from "./loadCharacterData"; export * from "./updateCharacters"; diff --git a/app/pages/PageEnigma/store/characterGroups/loadCharacterData.ts b/app/pages/PageEnigma/store/characterGroups/loadCharacterData.ts index db7722abe1..ba4b900b2d 100644 --- a/app/pages/PageEnigma/store/characterGroups/loadCharacterData.ts +++ b/app/pages/PageEnigma/store/characterGroups/loadCharacterData.ts @@ -1,15 +1,15 @@ import { ClipUI } from "~/pages/PageEnigma/datastructures/clips/clip_ui"; import { - CharacterGroup, + CharacterTrack, Clip, ClipType, Keyframe, } from "~/pages/PageEnigma/models"; -import { characterGroups } from "~/pages/PageEnigma/store"; +import { characterGroup } from "~/pages/PageEnigma/store"; function getAddCharacter(item: ClipUI) { - const existingCharacter = characterGroups.value.find( - (character) => character.id === item.object_uuid, + const existingCharacter = characterGroup.value.characters.find( + (character) => character.object_uuid === item.object_uuid, ); if (existingCharacter) { @@ -17,24 +17,28 @@ function getAddCharacter(item: ClipUI) { } const newCharacter = { - id: item.object_uuid, + object_uuid: item.object_uuid, name: item.object_name, + media_id: item.media_id, muted: false, animationClips: [], positionKeyframes: [], lipSyncClips: [], - } as CharacterGroup; + } as CharacterTrack; - characterGroups.value = [ - ...characterGroups.value.filter( - (character) => character.id !== item.object_uuid, - ), - newCharacter, - ].sort((charA, charB) => (charA.id < charB.id ? -1 : 1)); + characterGroup.value = { + ...characterGroup.value, + characters: [ + ...characterGroup.value.characters.filter( + (character) => character.object_uuid !== item.object_uuid, + ), + newCharacter, + ].sort((charA, charB) => (charA.object_uuid < charB.object_uuid ? -1 : 1)), + }; - return characterGroups.value.find( - (character) => character.id === item.object_uuid, - ) as CharacterGroup; + return characterGroup.value.characters.find( + (character) => character.object_uuid === item.object_uuid, + ) as CharacterTrack; } export function loadCharacterData(item: ClipUI) { diff --git a/app/pages/PageEnigma/store/characterGroups/updateCharacters.ts b/app/pages/PageEnigma/store/characterGroups/updateCharacters.ts index 27895ea9f1..650582773c 100644 --- a/app/pages/PageEnigma/store/characterGroups/updateCharacters.ts +++ b/app/pages/PageEnigma/store/characterGroups/updateCharacters.ts @@ -2,7 +2,7 @@ import { ClipType } from "~/pages/PageEnigma/models"; import Queue from "~/pages/PageEnigma/Queue/Queue"; import { QueueNames } from "~/pages/PageEnigma/Queue/QueueNames"; import { toEngineActions } from "~/pages/PageEnigma/Queue/toEngineActions"; -import { characterGroups } from "~/pages/PageEnigma/store"; +import { characterGroup } from "~/pages/PageEnigma/store"; export function updateCharacters({ type, @@ -15,80 +15,89 @@ export function updateCharacters({ length?: number; offset: number; }) { - const oldCharacterGroups = characterGroups.value; + const oldCharacterGroup = characterGroup.value; if (type === ClipType.ANIMATION) { - 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!; + characterGroup.value = { + ...oldCharacterGroup, + characters: oldCharacterGroup.characters.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, - }); + Queue.publish({ + queueName: QueueNames.TO_ENGINE, + action: toEngineActions.UPDATE_CLIP, + data: clip, + }); - return { - ...character, - animationClips: newAnimationClips, - }; - }); + return { + ...character, + animationClips: newAnimationClips, + }; + }), + }; } if (type === ClipType.TRANSFORM) { - 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; + characterGroup.value = { + ...oldCharacterGroup, + characters: oldCharacterGroup.characters.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_KEYFRAME, - data: keyframe, - }); + Queue.publish({ + queueName: QueueNames.TO_ENGINE, + action: toEngineActions.UPDATE_KEYFRAME, + data: keyframe, + }); - return { - ...character, - positionKeyframes: newPositionKeyframes, - }; - }); + return { + ...character, + positionKeyframes: newPositionKeyframes, + }; + }), + }; } if (type === ClipType.AUDIO) { - 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!; + characterGroup.value = { + ...oldCharacterGroup, + characters: oldCharacterGroup.characters.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, - }); + Queue.publish({ + queueName: QueueNames.TO_ENGINE, + action: toEngineActions.UPDATE_CLIP, + data: clip, + }); - return { - ...character, - lipSyncClips: newLipSyncClips, - }; - }); + return { + ...character, + lipSyncClips: newLipSyncClips, + }; + }), + }; } } diff --git a/app/pages/PageEnigma/store/objectGroup/objectGroup.ts b/app/pages/PageEnigma/store/objectGroup/objectGroup.ts index 5de11c02a8..4fcc9ab30a 100644 --- a/app/pages/PageEnigma/store/objectGroup/objectGroup.ts +++ b/app/pages/PageEnigma/store/objectGroup/objectGroup.ts @@ -73,7 +73,7 @@ export function deleteObjectKeyframe(keyframe: Keyframe) { const obj = oldObjectGroup.objects.find( (row) => row.object_uuid === keyframe.object_uuid, ); - console.log("delete", obj, keyframe.keyframe_uuid); + if (!obj) { return oldObjectGroup; } diff --git a/app/pages/PageEnigma/store/sizing.ts b/app/pages/PageEnigma/store/sizing.ts index 052685850f..a491741299 100644 --- a/app/pages/PageEnigma/store/sizing.ts +++ b/app/pages/PageEnigma/store/sizing.ts @@ -1,5 +1,5 @@ import { signal, computed } from "@preact/signals-core"; -import { characterGroups } from "~/pages/PageEnigma/store/characterGroups"; +import { characterGroup } from "~/pages/PageEnigma/store/characterGroups"; import { objectGroup } from "~/pages/PageEnigma/store/objectGroup"; // timeline @@ -9,7 +9,7 @@ export const timelineHeight = signal(0); export const fullHeight = computed(() => { return ( - characterGroups.value.length * 268 + + characterGroup.value.characters.length * 268 + objectGroup.value.objects.length * 60 + 300 + 96 diff --git a/app/pages/PageEnigma/store/timing.ts b/app/pages/PageEnigma/store/timing.ts index 482fa09c8b..b36790e0c1 100644 --- a/app/pages/PageEnigma/store/timing.ts +++ b/app/pages/PageEnigma/store/timing.ts @@ -2,4 +2,5 @@ import { signal } from "@preact/signals-core"; export const currentTime = signal(0); -export const currentScroll = signal(0); +export const timelineScrollX = signal(0); +export const timelineScrollY = signal(0); diff --git a/app/reducers/appUiReducer/reducer.tsx b/app/reducers/appUiReducer/reducer.tsx index 74cd098279..fe7c22edb3 100644 --- a/app/reducers/appUiReducer/reducer.tsx +++ b/app/reducers/appUiReducer/reducer.tsx @@ -2,21 +2,21 @@ import { State, Action, ACTION_TYPES } from "./types"; export function reducer(state: State, action: Action): State { switch (action.type) { - case ACTION_TYPES.OPEN_DIALOGUE_TTS:{ - return{ + case ACTION_TYPES.OPEN_DIALOGUE_TTS: { + return { ...state, - diagloueTts:{ + diagloueTts: { isOpen: true, - } - } + }, + }; } - case ACTION_TYPES.CLOSE_DIALOGUE_TTS:{ - return{ + case ACTION_TYPES.CLOSE_DIALOGUE_TTS: { + return { ...state, - diagloueTts:{ + diagloueTts: { isOpen: false, - } - } + }, + }; } case ACTION_TYPES.SHOW_CONTROLPANELS_SCENEOBJECT: { // console.log("pay", action.payload);