diff --git a/app/components/Button.tsx b/app/components/Button.tsx index 0ae06e6ab5..67eafce6b6 100644 --- a/app/components/Button.tsx +++ b/app/components/Button.tsx @@ -5,7 +5,7 @@ import { twMerge } from "tailwind-merge"; export interface ButtonPropsI extends React.ButtonHTMLAttributes { icon?: IconDefinition; - variant?: "primary" | "secondary"; + variant?: "primary" | "secondary" | "action"; } export const Button = ({ @@ -25,6 +25,9 @@ export const Button = ({ case "secondary": { return " bg-brand-secondary hover:bg-brand-secondary-900 text-white focus-visible:outline-brand-secondary"; } + case "action": { + return " bg-action hover:bg-action-500 text-white focus-visible:outline-action"; + } case "primary": default: { return " bg-brand-primary hover:bg-brand-primary-400 text-white focus-visible:outline-brand-primary-600"; diff --git a/app/pages/PageEnigma/PageEnigma.tsx b/app/pages/PageEnigma/PageEnigma.tsx index 17e4ca7b3a..67f3530d30 100644 --- a/app/pages/PageEnigma/PageEnigma.tsx +++ b/app/pages/PageEnigma/PageEnigma.tsx @@ -22,12 +22,9 @@ export const PageEnigma = () => { useSignals(); useEffect(() => { function setPage() { - const pxRatioWidth = - window.screen.availWidth / document.documentElement.clientWidth; - const pxRatioHeight = - window.screen.availHeight / document.documentElement.clientHeight; - pageHeight.value = window.outerHeight / pxRatioHeight; - pageWidth.value = window.outerWidth / pxRatioWidth; + // TODO address this issue with zooming + pageHeight.value = window.innerHeight; + pageWidth.value = window.outerWidth; } timelineHeight.value = window.innerHeight * 0.25; sidePanelWidth.value = 443; diff --git a/app/pages/PageEnigma/PageEnigmaComponent.tsx b/app/pages/PageEnigma/PageEnigmaComponent.tsx index e8723f4098..aefdc5f094 100644 --- a/app/pages/PageEnigma/PageEnigmaComponent.tsx +++ b/app/pages/PageEnigma/PageEnigmaComponent.tsx @@ -54,7 +54,6 @@ export const PageEnigmaComponent = () => { const width = sidePanelVisible.value ? pageWidth.value - dndWidth - 66 : pageWidth.value - 66; - const height = dndTimelineHeight.value > -1 ? pageHeight.value - dndTimelineHeight.value - 68 diff --git a/app/pages/PageEnigma/Queue/Queue.ts b/app/pages/PageEnigma/Queue/Queue.ts index 5ca88513f4..31c5b31d63 100644 --- a/app/pages/PageEnigma/Queue/Queue.ts +++ b/app/pages/PageEnigma/Queue/Queue.ts @@ -12,9 +12,10 @@ import { import { toTimelineActions } from "./toTimelineActions"; type UnionedActionTypes = fromEngineActions | toEngineActions | toTimelineActions | toInferenceActions; -type UnionedDataTypes = QueueClip | UpdateTime | QueueKeyframe | ClipUI[] | InferenceJob; +type UnionedDataTypes = QueueClip | UpdateTime | QueueKeyframe | ClipUI[] | MediaItem | InferenceJob; import { ClipUI } from "../datastructures/clips/clip_ui"; +import { MediaItem } from "~/pages/PageEnigma/models"; class Queue { private _queue: Record< string, diff --git a/app/pages/PageEnigma/Queue/fromEngineActions.ts b/app/pages/PageEnigma/Queue/fromEngineActions.ts index 2e647b32bc..484eaaa6d3 100644 --- a/app/pages/PageEnigma/Queue/fromEngineActions.ts +++ b/app/pages/PageEnigma/Queue/fromEngineActions.ts @@ -1,5 +1,6 @@ export enum fromEngineActions { UPDATE_TIME = "update_time", UPDATE_TIME_LINE = "update_timeline", + UPDATE_CHARACTER_ID = "update_character_id", // data={currentTime: number} } diff --git a/app/pages/PageEnigma/Queue/toEngineActions.ts b/app/pages/PageEnigma/Queue/toEngineActions.ts index 9a601aa64f..4be8244918 100644 --- a/app/pages/PageEnigma/Queue/toEngineActions.ts +++ b/app/pages/PageEnigma/Queue/toEngineActions.ts @@ -2,6 +2,11 @@ export enum toEngineActions { ADD_CLIP = "add_clip", // data=QueueClip ADD_KEYFRAME = "add_keyframe", + + ADD_CHARACTER = "add_character", + + ADD_OBJECT = "add_object", + // data= QueueKeyframe DELETE_CLIP = "delete_clip", // data=QueueClip diff --git a/app/pages/PageEnigma/comps/SidePanelTabs/AudioTabComponent/AudioTabComponent.tsx b/app/pages/PageEnigma/comps/SidePanelTabs/AudioTabComponent/AudioTabComponent.tsx index 9510496113..d302578f11 100644 --- a/app/pages/PageEnigma/comps/SidePanelTabs/AudioTabComponent/AudioTabComponent.tsx +++ b/app/pages/PageEnigma/comps/SidePanelTabs/AudioTabComponent/AudioTabComponent.tsx @@ -2,6 +2,9 @@ import { audioFilter, audioItems } from "~/pages/PageEnigma/store"; import { AssetFilterOption } from "~/pages/PageEnigma/models"; import { useSignals } from "@preact/signals-react/runtime"; import { ItemElements } from "~/pages/PageEnigma/comps/SidePanelTabs/itemTabs/ItemElements"; +import { Button } from "~/components"; +import { faCirclePlus } from "@fortawesome/pro-solid-svg-icons"; +import { twMerge } from "tailwind-merge"; export const AudioTabComponent = () => { useSignals(); @@ -9,39 +12,36 @@ export const AudioTabComponent = () => { return ( <>
-
+
-
- +
+
-
+
{
); -}; - -// import { useContext, useEffect, useState } from "react"; - -// import { faArrowsRotate } from "@fortawesome/pro-solid-svg-icons"; - -// import { TrackContext } from "~/pages/PageEnigma/contexts/TrackContext/TrackContext"; - -// import { APPUI_ACTION_TYPES } from "../../../reducers"; -// import { AppUiContext } from "../../../contexts/AppUiContext"; -// import { AuthenticationContext } from "~/contexts/Authentication"; -// import { useSignals } from "@preact/signals-react/runtime"; -// // import { timelineHeight } from "~/pages/PageEnigma/store"; - -// import { Button, ButtonIcon, Label, P } from "~/components"; -// import { ClipType } from "../../../models/track"; -// import { AudioElement } from "./AudioElement"; -// import { ListAudioByUser, MediaFile } from "./listAudioByUser"; - -// export const TabAudio = () => { -// const { audioClips } = useContext(TrackContext); -// const { authState } = useContext(AuthenticationContext); -// const [, dispatchAppUiState] = useContext(AppUiContext); -// const [ userAudioClips, setUserAudioClips] = useState([]); - -// useSignals(); - -// useEffect(() => { -// if(authState.userInfo && authState.userInfo.username && userAudioClips.length === 0) -// ListAudioByUser(authState.userInfo.username) -// .then(res=>{ -// setUserAudioClips([...res]); -// }); -// }, [authState]); - -// const openTTSPanel = () =>{ -// dispatchAppUiState({ -// type: APPUI_ACTION_TYPES.OPEN_DIALOGUE_TTS -// }) -// }; -// const handleRefreshUserMedia = ()=>{ -// if(authState.userInfo && authState.userInfo.username) -// ListAudioByUser(authState.userInfo.username) -// .then(res=>{ -// setUserAudioClips([...res]); -// }); -// } -// return ( -//
-//
-// -// -// -// -//
-//
-// - -//
-// {audioClips.map((clip) => { -// return ( -// -// ); -// })} -//
-//
- -//
-//
-// -// -//
-// {userAudioClips.length === 0 && -//
-//

No audio generated yet

-//
-// } -// { userAudioClips.length >= 0 && -//
-// {userAudioClips.map(item => -// -// )} -//
-// } -//
- -// -//
-// ); -// }; - +}; \ No newline at end of file diff --git a/app/pages/PageEnigma/comps/SidePanelTabs/SidePanelMenu.tsx b/app/pages/PageEnigma/comps/SidePanelTabs/SidePanelMenu.tsx index dac751a162..9d3767e1c0 100644 --- a/app/pages/PageEnigma/comps/SidePanelTabs/SidePanelMenu.tsx +++ b/app/pages/PageEnigma/comps/SidePanelTabs/SidePanelMenu.tsx @@ -1,7 +1,6 @@ import { useEffect } from "react"; import { useSignals } from "@preact/signals-react/runtime"; import { - lastSelectedTab, selectedTab, sidePanelHeight, sidePanelVisible, @@ -18,7 +17,7 @@ export const SidePanelMenu = () => { return (
{ {tabList.map((tab) => (
{tab.component}
diff --git a/app/pages/PageEnigma/comps/SidePanelTabs/TabStylization.tsx b/app/pages/PageEnigma/comps/SidePanelTabs/TabStylization.tsx index eeb5b4ee4d..59aa38d8b1 100644 --- a/app/pages/PageEnigma/comps/SidePanelTabs/TabStylization.tsx +++ b/app/pages/PageEnigma/comps/SidePanelTabs/TabStylization.tsx @@ -1,6 +1,6 @@ import { useContext, useState } from "react"; -import { Button, ItemPicker, Label, H4, Textarea } from "~/components"; +import { Button, ItemPicker, Label, Textarea } from "~/components"; import { AppUiContext } from "../../contexts/AppUiContext"; import { APPUI_ACTION_TYPES, APPUI_VIEW_MODES } from "../../reducers"; @@ -60,9 +60,9 @@ export const TabStylization = () => { }; return ( -
+
-

Select Base Style

+
{ useSignals(); @@ -9,39 +12,36 @@ export const AnimationTab = () => { return ( <>
-
+
-
- +
-
+
{ useSignals(); @@ -9,39 +12,36 @@ export const CameraTab = () => { return ( <>
-
+
-
- +
+
-
+
{ ); return ( -
+
{item.name}
{ + const displayWidth = + dndSidePanelWidth.value > -1 + ? dndSidePanelWidth.value + : sidePanelWidth.value; + const displayItems = items.filter((item) => { if (assetFilter === AssetFilterOption.ALL) { return true; @@ -16,8 +22,23 @@ export const ItemElements = ({ items, assetFilter }: Props) => { } return item.isBookmarked; }); + + function getGridColumnsClass(displayWidth: number): string { + if (displayWidth <= 280) { + return "grid-cols-2"; + } else if (displayWidth <= 360) { + return "grid-cols-3"; + } else if (displayWidth <= 440) { + return "grid-cols-4"; + } else { + return "grid-cols-4"; + } + } + + const gridColumnsClass = getGridColumnsClass(displayWidth); + return ( -
+
{displayItems.map((item) => ( ))} diff --git a/app/pages/PageEnigma/comps/SidePanelTabs/itemTabs/ObjectsTab.tsx b/app/pages/PageEnigma/comps/SidePanelTabs/itemTabs/ObjectsTab.tsx index 7eab081528..8212560290 100644 --- a/app/pages/PageEnigma/comps/SidePanelTabs/itemTabs/ObjectsTab.tsx +++ b/app/pages/PageEnigma/comps/SidePanelTabs/itemTabs/ObjectsTab.tsx @@ -7,6 +7,9 @@ import { objectItems, } from "~/pages/PageEnigma/store"; import { ItemElements } from "~/pages/PageEnigma/comps/SidePanelTabs/itemTabs/ItemElements"; +import { Button } from "~/components"; +import { faCirclePlus } from "@fortawesome/pro-solid-svg-icons"; +import { twMerge } from "tailwind-merge"; interface Props { type: AssetType; @@ -22,39 +25,36 @@ export const ObjectsTab = ({ type }: Props) => { return ( <>
-
+
-
- +
-
+
diff --git a/app/pages/PageEnigma/comps/SidePanelTabs/itemTabs/ShapeTab.tsx b/app/pages/PageEnigma/comps/SidePanelTabs/itemTabs/ShapeTab.tsx index 353663f461..ed2cabfb75 100644 --- a/app/pages/PageEnigma/comps/SidePanelTabs/itemTabs/ShapeTab.tsx +++ b/app/pages/PageEnigma/comps/SidePanelTabs/itemTabs/ShapeTab.tsx @@ -7,8 +7,8 @@ export const ShapeTab = () => { return ( <> -
Shapes
-
+
Shapes
+
, }, { - icon: "resources/icons/characters.png", + icon: "resources/icons/objects.png", title: "Style (Temporary being moved)", value: AssetType.STYLE, - component: , - } + component: , + }, ]; diff --git a/app/pages/PageEnigma/comps/Timeline/utils/useQueueHandler.ts b/app/pages/PageEnigma/comps/Timeline/utils/useQueueHandler.ts index c5bf23e06d..edf88049e3 100644 --- a/app/pages/PageEnigma/comps/Timeline/utils/useQueueHandler.ts +++ b/app/pages/PageEnigma/comps/Timeline/utils/useQueueHandler.ts @@ -1,26 +1,43 @@ import { fromEngineActions } from "~/pages/PageEnigma/Queue/fromEngineActions"; import { useSignals } from "@preact/signals-react/runtime"; import { useCallback, useContext, useEffect } from "react"; -import { currentTime } from "~/pages/PageEnigma/store"; +import { + addNewCharacter, + currentTime, + loadAudioData, + loadCameraData, + loadCharacterData, + loadObjectData, +} 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"; import { + ClipGroup, + MediaItem, QueueClip, QueueKeyframe, UpdateTime, } from "~/pages/PageEnigma/models"; import { TrackContext } from "~/pages/PageEnigma/contexts/TrackContext/TrackContext"; import { toTimelineActions } from "~/pages/PageEnigma/Queue/toTimelineActions"; +import { ClipUI } from "~/pages/PageEnigma/datastructures/clips/clip_ui"; interface Arguments { action: fromEngineActions | toEngineActions | toTimelineActions; - data: QueueClip | UpdateTime | QueueKeyframe; + data: QueueClip | UpdateTime | QueueKeyframe | ClipUI[] | MediaItem; } +const LOADING_FUNCTIONS: Record void> = { + [ClipGroup.CHARACTER]: loadCharacterData, + [ClipGroup.OBJECT]: loadObjectData, + [ClipGroup.CAMERA]: loadCameraData, + [ClipGroup.GLOBAL_AUDIO]: loadAudioData, +}; + export function useQueueHandler() { useSignals(); - const { addKeyframe } = useContext(TrackContext); + const { addKeyframe, clearExistingData } = useContext(TrackContext); const handleFromEngineActions = useCallback(({ action, data }: Arguments) => { console.log("FROM ENGINE", action, data); @@ -29,22 +46,33 @@ export function useQueueHandler() { currentTime.value = (data as UpdateTime).currentTime; break; case fromEngineActions.UPDATE_TIME_LINE: - console.log(action) + clearExistingData(); + (data as ClipUI[]).forEach((item) => { + LOADING_FUNCTIONS[item.group](item); + }); + break; + case fromEngineActions.UPDATE_CHARACTER_ID: { + addNewCharacter(data as MediaItem); + break; + } default: throw new Error(`Unknown action ${action}`); } }, []); - const handleToTimelineActions = useCallback(({ action, data }: Arguments) => { - console.log("TO TIMELINE", action, data); - switch (action) { - case toTimelineActions.ADD_KEYFRAME: - addKeyframe(data as QueueKeyframe, currentTime.value); - break; - default: - throw new Error(`Unknown action ${action}`); - } - }, []); + const handleToTimelineActions = useCallback( + ({ action, data }: Arguments) => { + console.log("TO TIMELINE", action, data); + switch (action) { + case toTimelineActions.ADD_KEYFRAME: + addKeyframe(data as QueueKeyframe, currentTime.value); + break; + default: + throw new Error(`Unknown action ${action}`); + } + }, + [addKeyframe], + ); useEffect(() => { Queue.subscribe(QueueNames.FROM_ENGINE, handleFromEngineActions); diff --git a/app/pages/PageEnigma/contexts/TrackContext/TrackContext.tsx b/app/pages/PageEnigma/contexts/TrackContext/TrackContext.tsx index f1105ec7c6..0c8d6b0b80 100644 --- a/app/pages/PageEnigma/contexts/TrackContext/TrackContext.tsx +++ b/app/pages/PageEnigma/contexts/TrackContext/TrackContext.tsx @@ -6,6 +6,9 @@ export const TrackContext = createContext<{ addKeyframe: (keyframe: QueueKeyframe, offset: number) => void; deleteKeyframe: (keyframe: Keyframe) => void; + // misc + clearExistingData: () => void; + // drag and drop startDrag: (item: MediaItem) => void; endDrag: () => void; @@ -13,6 +16,8 @@ export const TrackContext = createContext<{ addKeyframe: () => {}, deleteKeyframe: () => {}, + clearExistingData: () => {}, + startDrag: () => {}, endDrag: () => {}, }); diff --git a/app/pages/PageEnigma/contexts/TrackContext/TrackProvider.tsx b/app/pages/PageEnigma/contexts/TrackContext/TrackProvider.tsx index 2544eb5075..f8348d1c5a 100644 --- a/app/pages/PageEnigma/contexts/TrackContext/TrackProvider.tsx +++ b/app/pages/PageEnigma/contexts/TrackContext/TrackProvider.tsx @@ -6,10 +6,16 @@ import { addCharacterAnimation, addCharacterAudio, addGlobalAudio, + addCharacter, canDrop, dragItem, dropId, dropOffset, + addObject, + characterGroups, + cameraGroup, + audioGroup, + objectGroup, } from "~/pages/PageEnigma/store"; import useUpdateKeyframe from "~/pages/PageEnigma/contexts/TrackContext/utils/useUpdateKeyframe"; @@ -24,52 +30,75 @@ export const TrackProvider = ({ children }: Props) => { // cross group functions const dropClip = useCallback(() => { + if (dragItem.value) { + const mediaItem = dragItem.value; + if (mediaItem.type === AssetType.CHARACTER) { + addCharacter(dragItem.value); + } + // if (dragItem.value.type === AssetType.CAMERA) { + // console.log("Dragged In Camera Type") + // } + if (dragItem.value.type === AssetType.OBJECT) { + addObject(dragItem.value); + } + } + // if (dragItem.value.type === AssetType.SHAPE) { + // console.log("Dragged In Shape Type") + // } - console.log(`${canDrop.value} ${dragItem.value} ${dragItem.value.type}`) if (canDrop.value && dragItem.value) { if (dragItem.value.type === AssetType.ANIMATION) { addCharacterAnimation({ - dragItem: dragItem.value!, + dragItem: dragItem.value, characterId: dropId.value, offset: dropOffset.value, }); } if (dragItem.value.type === AssetType.AUDIO) { + console.log("add audio", dropId.value); addCharacterAudio({ - dragItem: dragItem.value!, + dragItem: dragItem.value, characterId: dropId.value, offset: dropOffset.value, }); addGlobalAudio({ - dragItem: dragItem.value!, + dragItem: dragItem.value, audioId: dropId.value, offset: dropOffset.value, }); } - if (dragItem.value.type === AssetType.CHARACTER) { - console.log("Dragged In Character Type") - } - if (dragItem.value.type === AssetType.CAMERA) { - console.log("Dragged In Camera Type") - } - if (dragItem.value.type === AssetType.OBJECT) { - console.log("Dragged In Object Type") - } - if (dragItem.value.type === AssetType.SHAPE) { - console.log("Dragged In Shape Type") - } } endDrag(); }, [endDrag]); + const clearExistingData = useCallback(() => { + characterGroups.value = []; + cameraGroup.value = { + id: "CG1", + keyframes: [], + }; + audioGroup.value = { + id: "AG-1", + clips: [], + muted: false, + }; + objectGroup.value = { + id: "OG1", + objects: [], + }; + }, []); + const values = useMemo(() => { return { ...keyframes, + clearExistingData, + ...dragDrop, endDrag: dropClip, }; - }, [keyframes, dragDrop, dropClip]); + }, [keyframes, dragDrop, dropClip, clearExistingData]); + return ( {children} ); diff --git a/app/pages/PageEnigma/contexts/TrackContext/utils/useUpdateKeyframe.ts b/app/pages/PageEnigma/contexts/TrackContext/utils/useUpdateKeyframe.ts index cd74478149..0aaad00d35 100644 --- a/app/pages/PageEnigma/contexts/TrackContext/utils/useUpdateKeyframe.ts +++ b/app/pages/PageEnigma/contexts/TrackContext/utils/useUpdateKeyframe.ts @@ -28,6 +28,7 @@ const DELETE_KEYFRAME: Record void> = { export default function useUpdateKeyframe() { const addKeyframe = useCallback((keyframe: QueueKeyframe, offset: number) => { + console.log("k", keyframe); ADD_KEYFRAME[keyframe.group](keyframe, offset); }, []); diff --git a/app/pages/PageEnigma/datastructures/clips/clip_ui.ts b/app/pages/PageEnigma/datastructures/clips/clip_ui.ts index f771f9910c..2d2b773f19 100644 --- a/app/pages/PageEnigma/datastructures/clips/clip_ui.ts +++ b/app/pages/PageEnigma/datastructures/clips/clip_ui.ts @@ -9,9 +9,12 @@ export class ClipUI { name: string; media_id: string; object_uuid: string; + object_name: string; offset: number; // in frames length: number; // in frames should_play: boolean; + clip_uuid: string; + keyframe_offset: number; constructor( version: number, @@ -19,21 +22,28 @@ export class ClipUI { group: ClipGroup, name: string, media_id: string, + clip_uuid: string, object_uuid: string, + object_name: string, offset: number, length: number, + keyframe_offset: number = 0, ) { this.version = version; this.group = group; // Only needed for UI + this.clip_uuid = clip_uuid; + this.name = name; // UI this.type = type; // UI and Animation / Audio / Lipsync / : Engine this.object_uuid = object_uuid; // Animation / Audio / Lipsync / : Engine + this.object_name = object_name; this.media_id = media_id; // Animation / Audio / Lipsync / : Engine this.offset = offset; this.length = length; this.should_play = true; + this.keyframe_offset = keyframe_offset; } toJSON(): any { @@ -42,10 +52,13 @@ export class ClipUI { group: this.group, name: this.name, type: this.type, + clip_uuid: this.clip_uuid, object_uuid: this.object_uuid, + object_name: this.object_name, media_id: this.media_id, start_offset: this.offset, ending_offset: this.length, + keyframe_offset: this.keyframe_offset, }; } } diff --git a/app/pages/PageEnigma/js/editor.ts b/app/pages/PageEnigma/js/editor.ts index af0707f0b9..fd3ff7b0da 100644 --- a/app/pages/PageEnigma/js/editor.ts +++ b/app/pages/PageEnigma/js/editor.ts @@ -311,7 +311,7 @@ class Editor { this.timeline.scene = this.activeScene; - this._test_demo(); + //this._test_demo(); this.renderer.domElement.addEventListener( "mousedown", @@ -1076,7 +1076,6 @@ class Editor { this.raycaster.setFromCamera(this.mouse, this.camera); const interactable: any[] = []; this.activeScene.scene.children.forEach((child: THREE.Object3D) => { - // console.log(child); if (child.name != "") { if (child.type == "Mesh" || child.type == "Object3D" || child.type == "Group") { interactable.push(child); diff --git a/app/pages/PageEnigma/js/timeline.ts b/app/pages/PageEnigma/js/timeline.ts index e1090d081e..84fa23d138 100644 --- a/app/pages/PageEnigma/js/timeline.ts +++ b/app/pages/PageEnigma/js/timeline.ts @@ -42,6 +42,7 @@ export class TimeLine { animation_engine: AnimationEngine; // lip sync engine lipSync_engine: LipSyncEngine; + characters: {[key: string]: string}; scene: Scene; @@ -55,6 +56,7 @@ export class TimeLine { scene: Scene, ) { this.timeline_items = []; + this.characters = {}; this.absolute_end = 60 * 12; this.timeline_limit = 0; // 5 seconds @@ -124,11 +126,47 @@ export class TimeLine { case toEngineActions.UNMUTE: await this.mute(data, true); break; + case toEngineActions.ADD_CHARACTER: + await this.addCharacter(data); + break; + case toEngineActions.ADD_OBJECT: + await this.addObject(data); + break; default: console.log("Action Not Wired", action); } } + public async addCharacter(data: any) { + let media_id = data.data['media_id']; + let name = data.data['name']; + let type = data.data['type']; + + let obj = await this.scene.load_glb(media_id); + obj.userData['name'] = name; + obj.name = name; + let object_uuid = obj.uuid; + + console.log("data.data", data.data) + + this.characters[object_uuid] = type; + + data.data['object_uuid'] = object_uuid; + Queue.publish({ + queueName: QueueNames.FROM_ENGINE, + action: fromEngineActions.UPDATE_CHARACTER_ID, + data: data.data, + }); + } + + public async addObject(data: any) { + let media_id = data.data['media_id']; + let name = data.data['name']; + let obj = await this.scene.load_glb(media_id); + obj.userData['name'] = name; + obj.name = name; + } + public async addKeyFrame(data: any) { // KeyFrame Object // version: number; @@ -142,12 +180,21 @@ export class TimeLine { // selected?: boolean; let data_json = data['data']; let uuid = data_json['object_uuid']; + let keyframe_uuid = data_json['keyframe_uuid']; let object_name = this.scene.get_object_by_uuid(uuid)?.name; if (object_name == undefined) { object_name = "undefined" } + for (let key in this.characters) { + let element = this.characters[key]; + if (key == uuid) { + data_json['group'] = element; + break; + } + } + let new_item = this.transform_engine.addFrame(uuid, this.absolute_end, data_json['position'], @@ -162,9 +209,12 @@ export class TimeLine { data_json['group'], object_name, "", + keyframe_uuid, uuid, + object_name, 0, - this.absolute_end)) + this.absolute_end, + data_json['offset'])) } this.scene.createPoint(data_json['position'], data_json['keyframe_uuid']); @@ -184,6 +234,13 @@ export class TimeLine { const type = data["data"]["type"]; const offset = data["data"]["offset"]; const end_offset = data["data"]["length"] + offset; + let object_name = this.scene.get_object_by_uuid(object_uuid)?.name; + const clip_uuid = data["data"]['clip_uuid']; + console.log(data); + + if(object_name == undefined) { + object_name = "Undefined." + } switch (type) { case "animation": @@ -199,11 +256,13 @@ export class TimeLine { this.addPlayableClip( new ClipUI( version, - "lipsync",// TODO potiential bug .. + "lipsync", // TODO potiential bug .. group, name, media_id, + clip_uuid, object_uuid, + object_name, offset, end_offset)); return; @@ -221,7 +280,9 @@ export class TimeLine { group, name, media_id, + clip_uuid, object_uuid, + object_name, offset, end_offset, ), @@ -286,6 +347,7 @@ export class TimeLine { } public async addPlayableClip(clip: ClipUI): Promise { + console.log("clip", clip) this.timeline_items.push(clip); } @@ -449,96 +511,3 @@ export class TimeLine { } } -// How much timeline precision we have -//const percision = 100 // using a fake update loop mock -// -//class AudioEngineMock { -// async play(media_id:string) { -// console.log("Audio Playing {media_id}") -// } -//} -// -//// Visual verification tests. -//function CheckIfBasicAudioClipWorks() { -// const timeline = new TimeLine() -// timeline.addPlayableClip(new ClipUI(1.0,'audio',1,0)) -// timeline.play() -//} -// -//function CheckIfBasicTransformClipWorks() { -// const timeline = new TimeLine() -// timeline.addPlayableClip(new ClipUI(1.0,'transform',2,0)) -// timeline.play() -//} -// -//function CheckIfBasicClipWorks() { -// const timeline = new TimeLine() -// timeline.addPlayableClip(new ClipUI(1.0,'animation',3,0)) -// timeline.play() -//} - -// function CheckIfBasicClipWorks3SecondsAfterTimelineStops() { -// const timeline = new TimeLine() -// timeline.addPlayableClip(new ClipUI("clip1", 0, 10000)) -// timeline.play() -// } - -// function CheckIfTwoClipsAtTheSameTimeWorks() { -// const timeline = new TimeLine() -// timeline.addPlayableClip(new ClipUI("clip1", 0, 1000)) -// timeline.play() -// } - -// function CheckIfTwoClipsOneAfterAnotherWorks() { -// const timeline = new TimeLine() -// timeline.addPlayableClip(new ClipUI("clip1", 0, 1000)) -// timeline.addPlayableClip(new ClipUI("clip2", 0, 2000)) -// timeline.play() -// } - -// function CheckIfTimeLineStopBeforeClipPlays() { -// const timeline = new TimeLine() -// timeline.addPlayableClip(new ClipUI("clip3",0,1000)) -// timeline.play() -// setInterval(async ()=> { -// timeline.stop() -// },1100) -// console.log("Stopped") -// } - -// function CheckIfTimeLineStartAfterClipPlays() { -// const timeline = new TimeLine() -// timeline.addPlayableClip(new ClipUI("clip3",0,1000)) -// timeline.play() -// setInterval(async ()=> { -// timeline.stop() -// },500) -// console.log("Stopped") -// } - -// function CheckIfClipsPlayAllTogetherConcurrently() { -// const timeline = new TimeLine() -// timeline.addPlayableClip(new ClipUI("clip1",0,1000)) -// timeline.addPlayableClip(new ClipUI("clip2",0,1000)) -// timeline.addPlayableClip(new ClipUI("clip3",0,1000)) -// timeline.addPlayableClip(new ClipUI("clip4",0,1000)) -// timeline.play() -// } - -// function CheckIfClipsPlayAllTogether() { -// const timeline = new TimeLine() -// timeline.addPlayableClip(new TrackClip("clip1",0,1000)) -// timeline.addPlayableClip(new TrackClip("clip2",0,1000)) -// timeline.addPlayableClip(new TrackClip("clip3",0,1000)) -// timeline.addPlayableClip(new TrackClip("clip4",0,1000)) -// timeline.play() -// } - -//CheckIfBasicClipWorks() -//CheckIfTwoClipsAtTheSameTimeWorks() -// CheckIfTwoClipsOneAfterAnotherWorks() -// CheckIfTimeLineStopBeforeClipPlays() -// CheckIfTimeLineStartAfterClipPlays() -// CheckIfClipsPlayAllTogetherConcurrently() - -// CheckIfClipsPlayAllTogether() diff --git a/app/pages/PageEnigma/models/assets.ts b/app/pages/PageEnigma/models/assets.ts index c6e4539759..c4b963c99c 100644 --- a/app/pages/PageEnigma/models/assets.ts +++ b/app/pages/PageEnigma/models/assets.ts @@ -2,9 +2,10 @@ export interface MediaItem { version: number; type: AssetType; media_id: string; + object_uuid?: string; name: string; length?: number; - thumbnail: string; + thumbnail?: string; isMine?: boolean; isBookmarked?: boolean; } @@ -16,7 +17,7 @@ export enum AssetType { ANIMATION = "animation", CAMERA = "camera", SHAPE = "shape", - STYLE = "style" // TODO Remove + STYLE = "style", // TODO Remove } export enum AssetFilterOption { diff --git a/app/pages/PageEnigma/models/track.ts b/app/pages/PageEnigma/models/track.ts index 931e2e65a4..25eecff868 100644 --- a/app/pages/PageEnigma/models/track.ts +++ b/app/pages/PageEnigma/models/track.ts @@ -17,7 +17,6 @@ export enum ClipType { TRANSFORM = "transform", AUDIO = "audio", ANIMATION = "animation", - LIPSYNC = "lipsync" } export enum ClipGroup { @@ -41,6 +40,7 @@ export interface Keyframe { export interface CharacterGroup { id: string; + name: string; muted: boolean; animationClips: Clip[]; positionKeyframes: Keyframe[]; diff --git a/app/pages/PageEnigma/proxy/storyteller_proxy_timeline.ts b/app/pages/PageEnigma/proxy/storyteller_proxy_timeline.ts index e5cb72ff2a..8c0c08051f 100644 --- a/app/pages/PageEnigma/proxy/storyteller_proxy_timeline.ts +++ b/app/pages/PageEnigma/proxy/storyteller_proxy_timeline.ts @@ -53,6 +53,8 @@ export class StoryTellerProxyTimeline { audio: await this.getItemsDict(this.audio_engine.clips), lipsync: await this.getItemsDict(this.lipsync_engine.clips), } + console.log(timeline_json) + return timeline_json; } @@ -64,8 +66,11 @@ export class StoryTellerProxyTimeline { let clip = new ClipUI( element.version, element.type, element.group, element.name, - element.media_id, element.object_uuid, - element.start_offset, element.ending_offset); + element.media_id, element.clip_uuid, + element.object_uuid, + element.object_name, + element.start_offset, element.ending_offset, + element.keyframe_offset); new_clips.push(clip); } } @@ -124,6 +129,7 @@ export class StoryTellerProxyTimeline { this.lipsync_engine.clips = await this.loadLipsyncClips(timeline['lipsync']); this.animation_engine.clips = await this.loadAnimationClips(timeline['animation']); + console.log(this.timeline.timeline_items) this.timeline.updateUI() } diff --git a/app/pages/PageEnigma/store/audioGroup.ts b/app/pages/PageEnigma/store/audioGroup.ts index 9a6d7fc2dd..630778507c 100644 --- a/app/pages/PageEnigma/store/audioGroup.ts +++ b/app/pages/PageEnigma/store/audioGroup.ts @@ -10,6 +10,7 @@ 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"; +import { ClipUI } from "~/pages/PageEnigma/datastructures/clips/clip_ui"; export const audioGroup = signal({ id: "AG-1", @@ -140,3 +141,20 @@ export function deleteAudioClip(clip: Clip) { ], }; } + +export function loadAudioData(item: ClipUI) { + const existingAudio = audioGroup.value; + const newItem = { + version: item.version, + clip_uuid: item.clip_uuid, + type: item.type, + group: item.group, + object_uuid: item.object_uuid, + media_id: item.media_id, + name: item.name, + offset: item.offset, + length: item.length, + } as Clip; + existingAudio.clips.push(newItem); + existingAudio.clips.sort((clipA, clipB) => clipA.offset - clipB.offset); +} diff --git a/app/pages/PageEnigma/store/cameraGroup.ts b/app/pages/PageEnigma/store/cameraGroup.ts index 029a8b6810..72bf56aad1 100644 --- a/app/pages/PageEnigma/store/cameraGroup.ts +++ b/app/pages/PageEnigma/store/cameraGroup.ts @@ -8,6 +8,7 @@ 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"; +import { ClipUI } from "~/pages/PageEnigma/datastructures/clips/clip_ui"; // import { toast } from "react-hot-toast"; export const cameraGroup = signal({ id: "CG1", keyframes: [] }); @@ -112,3 +113,18 @@ export function deleteCameraKeyframe(deleteKeyframe: Keyframe) { ], }; } + +export function loadCameraData(item: ClipUI) { + const existingCamera = cameraGroup.value; + const newKeyframe = { + version: item.version, + keyframe_uuid: item.clip_uuid, + group: item.group, + object_uuid: item.object_uuid, + offset: item.keyframe_offset, + } as Keyframe; + existingCamera.keyframes.push(newKeyframe); + existingCamera.keyframes.sort( + (keyframeA, keyframeB) => keyframeA.offset - keyframeB.offset, + ); +} diff --git a/app/pages/PageEnigma/store/characterGroups/addCharacter.ts b/app/pages/PageEnigma/store/characterGroups/addCharacter.ts new file mode 100644 index 0000000000..c68a300002 --- /dev/null +++ b/app/pages/PageEnigma/store/characterGroups/addCharacter.ts @@ -0,0 +1,28 @@ +import { CharacterGroup, 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"; + +export function addCharacter(character: MediaItem) { + Queue.publish({ + queueName: QueueNames.TO_ENGINE, + action: toEngineActions.ADD_CHARACTER, + data: character, + }); +} + +export function addNewCharacter(data: MediaItem) { + const newCharacter = { + id: data.object_uuid, + name: data.name, + muted: false, + animationClips: [], + positionKeyframes: [], + lipSyncClips: [], + } as CharacterGroup; + + characterGroups.value = [...characterGroups.value, newCharacter].sort( + (charA, charB) => (charA.id < charB.id ? -1 : 1), + ); +} diff --git a/app/pages/PageEnigma/store/characterGroups.ts b/app/pages/PageEnigma/store/characterGroups/characterGroups.ts similarity index 70% rename from app/pages/PageEnigma/store/characterGroups.ts rename to app/pages/PageEnigma/store/characterGroups/characterGroups.ts index 84fc54c076..b7be79cfc1 100644 --- a/app/pages/PageEnigma/store/characterGroups.ts +++ b/app/pages/PageEnigma/store/characterGroups/characterGroups.ts @@ -13,104 +13,7 @@ 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: ClipType; - id: string; - length?: number; - offset: number; -}) { - const oldCharacterGroups = characterGroups.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!; - - Queue.publish({ - queueName: QueueNames.TO_ENGINE, - action: toEngineActions.UPDATE_CLIP, - data: clip, - }); - - 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; - - Queue.publish({ - queueName: QueueNames.TO_ENGINE, - action: toEngineActions.UPDATE_CLIP, - data: keyframe, - }); - - 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!; - - Queue.publish({ - queueName: QueueNames.TO_ENGINE, - action: toEngineActions.UPDATE_CLIP, - data: clip, - }); - - return { - ...character, - lipSyncClips: newLipSyncClips, - }; - }); - } -} +export const characterGroups = signal([]); export function addCharacterAnimation({ dragItem, @@ -181,6 +84,13 @@ export function addCharacterAudio({ 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( @@ -188,12 +98,6 @@ export function addCharacterAudio({ ), }; }); - - Queue.publish({ - queueName: QueueNames.TO_ENGINE, - action: toEngineActions.ADD_CLIP, - data: newClip, - }); } export function addCharacterKeyframe(keyframe: QueueKeyframe, offset: number) { diff --git a/app/pages/PageEnigma/store/characterGroups/index.ts b/app/pages/PageEnigma/store/characterGroups/index.ts new file mode 100644 index 0000000000..eb200150c0 --- /dev/null +++ b/app/pages/PageEnigma/store/characterGroups/index.ts @@ -0,0 +1,4 @@ +export * from "./addCharacter"; +export * from "./characterGroups"; +export * from "./loadCharacterData"; +export * from "./updateCharacters"; diff --git a/app/pages/PageEnigma/store/characterGroups/loadCharacterData.ts b/app/pages/PageEnigma/store/characterGroups/loadCharacterData.ts new file mode 100644 index 0000000000..db7722abe1 --- /dev/null +++ b/app/pages/PageEnigma/store/characterGroups/loadCharacterData.ts @@ -0,0 +1,89 @@ +import { ClipUI } from "~/pages/PageEnigma/datastructures/clips/clip_ui"; +import { + CharacterGroup, + Clip, + ClipType, + Keyframe, +} from "~/pages/PageEnigma/models"; +import { characterGroups } from "~/pages/PageEnigma/store"; + +function getAddCharacter(item: ClipUI) { + const existingCharacter = characterGroups.value.find( + (character) => character.id === item.object_uuid, + ); + + if (existingCharacter) { + return existingCharacter; + } + + const newCharacter = { + id: item.object_uuid, + name: item.object_name, + muted: false, + animationClips: [], + positionKeyframes: [], + lipSyncClips: [], + } as CharacterGroup; + + characterGroups.value = [ + ...characterGroups.value.filter( + (character) => character.id !== item.object_uuid, + ), + newCharacter, + ].sort((charA, charB) => (charA.id < charB.id ? -1 : 1)); + + return characterGroups.value.find( + (character) => character.id === item.object_uuid, + ) as CharacterGroup; +} + +export function loadCharacterData(item: ClipUI) { + const existingCharacter = getAddCharacter(item); + if (item.type === ClipType.ANIMATION) { + const newItem = { + version: item.version, + clip_uuid: item.clip_uuid, + type: item.type, + group: item.group, + object_uuid: item.object_uuid, + media_id: item.media_id, + name: item.name, + offset: item.offset, + length: item.length, + } as Clip; + existingCharacter.animationClips.push(newItem); + existingCharacter.animationClips.sort( + (clipA, clipB) => clipA.offset - clipB.offset, + ); + } + if (item.type === ClipType.TRANSFORM) { + const newKeyframe = { + version: item.version, + keyframe_uuid: item.clip_uuid, + group: item.group, + object_uuid: item.object_uuid, + offset: item.keyframe_offset, + } as Keyframe; + existingCharacter.positionKeyframes.push(newKeyframe); + existingCharacter.positionKeyframes.sort( + (keyframeA, keyframeB) => keyframeA.offset - keyframeB.offset, + ); + } + if (item.type === ClipType.AUDIO) { + const newItem = { + version: item.version, + clip_uuid: item.clip_uuid, + type: item.type, + group: item.group, + object_uuid: item.object_uuid, + media_id: item.media_id, + name: item.name, + offset: item.offset, + length: item.length, + } as Clip; + existingCharacter.lipSyncClips.push(newItem); + existingCharacter.lipSyncClips.sort( + (clipA, clipB) => clipA.offset - clipB.offset, + ); + } +} diff --git a/app/pages/PageEnigma/store/characterGroups/updateCharacters.ts b/app/pages/PageEnigma/store/characterGroups/updateCharacters.ts new file mode 100644 index 0000000000..93de4af12a --- /dev/null +++ b/app/pages/PageEnigma/store/characterGroups/updateCharacters.ts @@ -0,0 +1,94 @@ +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"; + +export function updateCharacters({ + type, + id, + offset, + length, +}: { + type: ClipType; + id: string; + length?: number; + offset: number; +}) { + const oldCharacterGroups = characterGroups.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!; + + Queue.publish({ + queueName: QueueNames.TO_ENGINE, + action: toEngineActions.UPDATE_CLIP, + data: clip, + }); + + 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; + + Queue.publish({ + queueName: QueueNames.TO_ENGINE, + action: toEngineActions.UPDATE_CLIP, + data: keyframe, + }); + + 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!; + + Queue.publish({ + queueName: QueueNames.TO_ENGINE, + action: toEngineActions.UPDATE_CLIP, + data: clip, + }); + + return { + ...character, + lipSyncClips: newLipSyncClips, + }; + }); + } +} diff --git a/app/pages/PageEnigma/store/media.ts b/app/pages/PageEnigma/store/media.ts index f1579cd77b..b1eee9e62f 100644 --- a/app/pages/PageEnigma/store/media.ts +++ b/app/pages/PageEnigma/store/media.ts @@ -94,7 +94,7 @@ export const cameraItems = signal([ export const characterItems = signal([ { version: 1, - media_id: "", + media_id: "m_fmxy8wjnep1hdaz7qdg4n7y15d2bsp", type: AssetType.CHARACTER, name: "Shrek", thumbnail: "resources/placeholders/placeholder.png", @@ -122,7 +122,7 @@ export const characterItems = signal([ }, { version: 1, - media_id: "m_fmxy8wjnep1hdaz7qdg4n7y15d2bsp ", + media_id: "m_4wva09qznapzk5rcvbxy671d1qx2pr ", type: AssetType.CHARACTER, name: "Story Girl", thumbnail: "resources/characters/img13.png", @@ -132,9 +132,9 @@ export const characterItems = signal([ export const objectItems = signal([ { version: 1, - media_id: "m_w5nn3kjh1fbkmjrdac5b2qaba0pmyt", + media_id: "m_0xfrmekc56satjxn66wt6c9dkw7dxe", type: AssetType.OBJECT, - name: "Item ( Place Holder D.N.W )", + name: "Pikachu Statue", thumbnail: "resources/placeholders/placeholder.png", }, ]); diff --git a/app/pages/PageEnigma/store/objectGroup/index.ts b/app/pages/PageEnigma/store/objectGroup/index.ts new file mode 100644 index 0000000000..43c1cdb480 --- /dev/null +++ b/app/pages/PageEnigma/store/objectGroup/index.ts @@ -0,0 +1,3 @@ +export * from "./loadObjectData"; +export * from "./objectGroup"; +export * from "./updateObjectGroup"; diff --git a/app/pages/PageEnigma/store/objectGroup/loadObjectData.ts b/app/pages/PageEnigma/store/objectGroup/loadObjectData.ts new file mode 100644 index 0000000000..8831681799 --- /dev/null +++ b/app/pages/PageEnigma/store/objectGroup/loadObjectData.ts @@ -0,0 +1,48 @@ +import { ClipUI } from "~/pages/PageEnigma/datastructures/clips/clip_ui"; +import { Keyframe, ObjectTrack } from "~/pages/PageEnigma/models"; +import { objectGroup } from "~/pages/PageEnigma/store"; + +function getAddObject(item: ClipUI) { + const existingObject = objectGroup.value.objects.find( + (obj) => obj.object_uuid === item.object_uuid, + ); + + if (existingObject) { + return existingObject; + } + + const newObject = { + object_uuid: item.object_uuid, + name: item.object_name, + keyframes: [] as Keyframe[], + } as ObjectTrack; + + objectGroup.value = { + id: "OB1", + objects: [ + ...objectGroup.value.objects.filter( + (obj) => obj.object_uuid !== item.object_uuid, + ), + newObject, + ].sort((objA, objB) => (objA.object_uuid < objB.object_uuid ? -1 : 1)), + }; + + return objectGroup.value.objects.find( + (obj) => obj.object_uuid === item.object_uuid, + ) as ObjectTrack; +} + +export function loadObjectData(item: ClipUI) { + const existingObject = getAddObject(item); + const newKeyframe = { + version: item.version, + keyframe_uuid: item.clip_uuid, + group: item.group, + object_uuid: item.object_uuid, + offset: item.keyframe_offset, + } as Keyframe; + existingObject.keyframes.push(newKeyframe); + existingObject.keyframes.sort( + (keyframeA, keyframeB) => keyframeA.offset - keyframeB.offset, + ); +} diff --git a/app/pages/PageEnigma/store/objectGroup.ts b/app/pages/PageEnigma/store/objectGroup/objectGroup.ts similarity index 72% rename from app/pages/PageEnigma/store/objectGroup.ts rename to app/pages/PageEnigma/store/objectGroup/objectGroup.ts index bcb83cd51d..80b4b8118f 100644 --- a/app/pages/PageEnigma/store/objectGroup.ts +++ b/app/pages/PageEnigma/store/objectGroup/objectGroup.ts @@ -1,13 +1,16 @@ import { signal } from "@preact/signals-core"; import { Keyframe, + MediaItem, ObjectGroup, + ObjectTrack, QueueKeyframe, } from "~/pages/PageEnigma/models"; 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"; +import { ClipUI } from "~/pages/PageEnigma/datastructures/clips/clip_ui"; // import { toast } from "react-hot-toast"; export const objectGroup = signal({ @@ -15,61 +18,6 @@ export const objectGroup = signal({ objects: [], }); -export function updateObject({ - id, - offset, -}: { - id: string; - offset: number; -}): void { - const oldObjectGroup = objectGroup.value; - const obj = oldObjectGroup.objects.find((objectTrack) => - objectTrack.keyframes.some((row) => row.keyframe_uuid === id), - ); - - if (!obj) { - return; - } - - const existingKeyframe = obj.keyframes.some((row) => { - return row.offset === offset && row.keyframe_uuid !== id; - }); - - if (existingKeyframe) { - //toast.error("There can only be one keyframe at this offset."); - return; - } - - objectGroup.value = { - id: oldObjectGroup.id, - objects: oldObjectGroup.objects.map((object) => ({ - object_uuid: object.object_uuid, - name: object.name, - keyframes: object.keyframes.map((keyframe) => { - if (keyframe.keyframe_uuid !== id) { - return { - ...keyframe, - }; - } - - Queue.publish({ - queueName: QueueNames.TO_ENGINE, - action: toEngineActions.UPDATE_KEYFRAME, - data: { - ...keyframe, - offset, - }, - }); - - return { - ...keyframe, - offset, - }; - }), - })), - }; -} - export function addObjectKeyframe(keyframe: QueueKeyframe, offset: number) { const oldObjectGroup = objectGroup.value; const obj = oldObjectGroup.objects.find( @@ -166,3 +114,11 @@ export function deleteObjectKeyframe(keyframe: Keyframe) { ], }; } + +export function addObject(object: MediaItem) { + Queue.publish({ + queueName: QueueNames.TO_ENGINE, + action: toEngineActions.ADD_OBJECT, + data: object, + }); +} diff --git a/app/pages/PageEnigma/store/objectGroup/updateObjectGroup.ts b/app/pages/PageEnigma/store/objectGroup/updateObjectGroup.ts new file mode 100644 index 0000000000..0db35259af --- /dev/null +++ b/app/pages/PageEnigma/store/objectGroup/updateObjectGroup.ts @@ -0,0 +1,59 @@ +import Queue from "~/pages/PageEnigma/Queue/Queue"; +import { QueueNames } from "~/pages/PageEnigma/Queue/QueueNames"; +import { toEngineActions } from "~/pages/PageEnigma/Queue/toEngineActions"; +import { objectGroup } from "~/pages/PageEnigma/store"; + +export function updateObject({ + id, + offset, +}: { + id: string; + offset: number; +}): void { + const oldObjectGroup = objectGroup.value; + const obj = oldObjectGroup.objects.find((objectTrack) => + objectTrack.keyframes.some((row) => row.keyframe_uuid === id), + ); + + if (!obj) { + return; + } + + const existingKeyframe = obj.keyframes.some((row) => { + return row.offset === offset && row.keyframe_uuid !== id; + }); + + if (existingKeyframe) { + //toast.error("There can only be one keyframe at this offset."); + return; + } + + objectGroup.value = { + id: oldObjectGroup.id, + objects: oldObjectGroup.objects.map((object) => ({ + object_uuid: object.object_uuid, + name: object.name, + keyframes: object.keyframes.map((keyframe) => { + if (keyframe.keyframe_uuid !== id) { + return { + ...keyframe, + }; + } + + Queue.publish({ + queueName: QueueNames.TO_ENGINE, + action: toEngineActions.UPDATE_KEYFRAME, + data: { + ...keyframe, + offset, + }, + }); + + return { + ...keyframe, + offset, + }; + }), + })), + }; +} diff --git a/app/styles/tailwind.css b/app/styles/tailwind.css index b5c61c9567..d980c43d3a 100644 --- a/app/styles/tailwind.css +++ b/app/styles/tailwind.css @@ -1,3 +1,17 @@ @tailwind base; @tailwind components; @tailwind utilities; + +@layer components { + .filter-tab { + @apply text-nowrap rounded-lg border-2 border-assets-background bg-assets-background px-3 py-2 text-sm font-medium transition-all duration-150 hover:border-assets-background/0 hover:bg-assets-background/80; + } + + .filter-tab.active { + @apply border-2 border-brand-primary hover:border-brand-primary hover:bg-assets-background; + } + + .filter-tab.disabled { + @apply hover:bg-assets-background disabled:text-white/50; + } +} diff --git a/public/resources/icons/animations.png b/public/resources/icons/animations.png index fabaf7f681..430647afa9 100644 Binary files a/public/resources/icons/animations.png and b/public/resources/icons/animations.png differ diff --git a/public/resources/icons/audios.png b/public/resources/icons/audios.png index e2c3c0fa52..96979fc9c3 100644 Binary files a/public/resources/icons/audios.png and b/public/resources/icons/audios.png differ diff --git a/public/resources/icons/camera.png b/public/resources/icons/camera.png index 9e162a9e4a..3e5ff43ffe 100644 Binary files a/public/resources/icons/camera.png and b/public/resources/icons/camera.png differ diff --git a/public/resources/icons/characters.png b/public/resources/icons/characters.png index 11f2ef919d..4a5c484c47 100644 Binary files a/public/resources/icons/characters.png and b/public/resources/icons/characters.png differ diff --git a/public/resources/icons/objects.png b/public/resources/icons/objects.png index 11f2ef919d..0f79e3d075 100644 Binary files a/public/resources/icons/objects.png and b/public/resources/icons/objects.png differ diff --git a/public/resources/icons/shapes.png b/public/resources/icons/shapes.png index 0657f36870..aca5d2aec9 100644 Binary files a/public/resources/icons/shapes.png and b/public/resources/icons/shapes.png differ diff --git a/tailwind.config.ts b/tailwind.config.ts index f69f1008b2..caad8b5b76 100644 --- a/tailwind.config.ts +++ b/tailwind.config.ts @@ -94,6 +94,22 @@ export default { "950": "#0c270f", }, + action: { + //comet + DEFAULT: "#55556b", //600 + "50": "#f7f7f8", + "100": "#eeedf1", + "200": "#d8d8df", + "300": "#b5b5c4", + "400": "#8d8da3", + "500": "#6f7088", + "600": "#55556b", + "700": "#49495b", + "800": "#3f3f4d", + "900": "#373743", + "950": "#26252c", + }, + character: { unselected: "#46527C", selected: "#6384F4",