diff --git a/app/pages/PageEnigma/PageEnigma.tsx b/app/pages/PageEnigma/PageEnigma.tsx index 67f3530d30..afd78326cc 100644 --- a/app/pages/PageEnigma/PageEnigma.tsx +++ b/app/pages/PageEnigma/PageEnigma.tsx @@ -3,12 +3,6 @@ import { TrackProvider } from "~/pages/PageEnigma/contexts/TrackContext/TrackPro import { AppUIProvider } from "~/pages/PageEnigma/contexts/AppUiContext"; import { EngineProvider } from "~/pages/PageEnigma/contexts/EngineProvider"; import { DragComponent } from "~/pages/PageEnigma/comps/DragComponent/DragComponent"; -// import { toast, ToastBar, Toaster } from "react-hot-toast"; -// import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -// import { -// faTriangleExclamation, -// faXmark, -// } from "@fortawesome/pro-solid-svg-icons"; import { useEffect } from "react"; import { useSignals } from "@preact/signals-react/runtime"; import { @@ -42,52 +36,6 @@ export const PageEnigma = () => { - {/* - {(thisToast) => ( - - {({ message }) => { - return ( -
-
- {thisToast.type === "error" && ( -
- -
- Warning! -
-
- )} -
- {message} -
-
- -
- ); - }} -
- )} -
*/}
diff --git a/app/pages/PageEnigma/Queue/fromEngineActions.ts b/app/pages/PageEnigma/Queue/fromEngineActions.ts index 484eaaa6d3..977f510f85 100644 --- a/app/pages/PageEnigma/Queue/fromEngineActions.ts +++ b/app/pages/PageEnigma/Queue/fromEngineActions.ts @@ -1,6 +1,7 @@ export enum fromEngineActions { UPDATE_TIME = "update_time", UPDATE_TIME_LINE = "update_timeline", - UPDATE_CHARACTER_ID = "update_character_id", // data={currentTime: number} + UPDATE_CHARACTER_ID = "update_character_id", + DELETE_OBJECT = "delete_object", } diff --git a/app/pages/PageEnigma/comps/ControlsTopButtons/ControlsTopButtons.tsx b/app/pages/PageEnigma/comps/ControlsTopButtons/ControlsTopButtons.tsx index 51018eab7b..d6c38494b9 100644 --- a/app/pages/PageEnigma/comps/ControlsTopButtons/ControlsTopButtons.tsx +++ b/app/pages/PageEnigma/comps/ControlsTopButtons/ControlsTopButtons.tsx @@ -2,11 +2,14 @@ import { useContext, useState } from "react"; import { Button, Input } from "~/components"; import { ButtonDialogue } from "~/modules/ButtonDialogue"; import { EngineContext } from "../../contexts/EngineContext"; +import { ToasterContext } from "~/pages/PageEnigma/contexts/ToasterContext"; +import { APPUI_ACTION_TYPES } from "~/pages/PageEnigma/reducers"; export const ControlsTopButtons = () => { const editorEngine = useContext(EngineContext); const [sceneName, setSceneName] = useState(""); const [sceneToken, setSceneToken] = useState(""); + const { addToast } = useContext(ToasterContext); // for testing const [mediaToken, setMediaToken] = useState(""); @@ -22,7 +25,9 @@ export const ControlsTopButtons = () => { const handleButtonLoadScene = () => { console.log(`Scene Token is ${sceneToken}`); - editorEngine?.loadScene(sceneToken); + editorEngine?.loadScene(sceneToken).catch((err) => { + addToast("error", err.message); + }); }; const handleButtonTest = () => {}; diff --git a/app/pages/PageEnigma/comps/Timeline/utils/useQueueHandler.ts b/app/pages/PageEnigma/comps/Timeline/utils/useQueueHandler.ts index edf88049e3..8d021a2958 100644 --- a/app/pages/PageEnigma/comps/Timeline/utils/useQueueHandler.ts +++ b/app/pages/PageEnigma/comps/Timeline/utils/useQueueHandler.ts @@ -37,7 +37,8 @@ const LOADING_FUNCTIONS: Record void> = { export function useQueueHandler() { useSignals(); - const { addKeyframe, clearExistingData } = useContext(TrackContext); + const { addKeyframe, clearExistingData, deleteObjectOrCharacter } = + useContext(TrackContext); const handleFromEngineActions = useCallback(({ action, data }: Arguments) => { console.log("FROM ENGINE", action, data); @@ -55,6 +56,11 @@ export function useQueueHandler() { 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}`); } diff --git a/app/pages/PageEnigma/contexts/EngineProvider.tsx b/app/pages/PageEnigma/contexts/EngineProvider.tsx index fdaa9982ef..2428a5a529 100644 --- a/app/pages/PageEnigma/contexts/EngineProvider.tsx +++ b/app/pages/PageEnigma/contexts/EngineProvider.tsx @@ -4,7 +4,6 @@ import Editor from "~/pages/PageEnigma/js/editor"; import { EngineContext } from "./EngineContext"; import { AppUiContext } from "./AppUiContext"; -import { TrackContext } from "./TrackContext/TrackContext"; interface Props { children: ReactNode; @@ -14,7 +13,6 @@ export const EngineProvider = ({ children }: Props) => { const [editor, setEditor] = useState(null); const [appUiState, dispatchAppUiState] = useContext(AppUiContext); // To talk to react land. - const trackContext = useContext(TrackContext) useEffect(() => { //componentDidMount @@ -22,30 +20,28 @@ export const EngineProvider = ({ children }: Props) => { console.error( 'Editor Engine need a target canvas with the id "video-scene"', ); - } else { - setEditor((curr)=>{ - if(curr!==null){ + } else { + setEditor((curr) => { + if (curr !== null) { console.warn("Editor Engine already exist"); return curr; - }else{ + } else { return new Editor(); } }); } }, []); - useEffect(()=>{ - if (editor && editor.can_initialize && dispatchAppUiState!==null){ + useEffect(() => { + if (editor && editor.can_initialize && dispatchAppUiState !== null) { console.log("initializing Editor"); editor.initialize({ - dispatchAppUiState + dispatchAppUiState, }); } }, [editor, dispatchAppUiState]); return ( - - {children} - + {children} ); }; diff --git a/app/pages/PageEnigma/contexts/TrackContext/TrackContext.tsx b/app/pages/PageEnigma/contexts/TrackContext/TrackContext.tsx index 0c8d6b0b80..cfebbc1b95 100644 --- a/app/pages/PageEnigma/contexts/TrackContext/TrackContext.tsx +++ b/app/pages/PageEnigma/contexts/TrackContext/TrackContext.tsx @@ -5,6 +5,7 @@ export const TrackContext = createContext<{ // keyframes addKeyframe: (keyframe: QueueKeyframe, offset: number) => void; deleteKeyframe: (keyframe: Keyframe) => void; + deleteObjectOrCharacter: (item: MediaItem) => void; // misc clearExistingData: () => void; @@ -15,6 +16,7 @@ export const TrackContext = createContext<{ }>({ addKeyframe: () => {}, deleteKeyframe: () => {}, + deleteObjectOrCharacter: () => {}, clearExistingData: () => {}, diff --git a/app/pages/PageEnigma/contexts/TrackContext/TrackProvider.tsx b/app/pages/PageEnigma/contexts/TrackContext/TrackProvider.tsx index f8348d1c5a..8a03f6c141 100644 --- a/app/pages/PageEnigma/contexts/TrackContext/TrackProvider.tsx +++ b/app/pages/PageEnigma/contexts/TrackContext/TrackProvider.tsx @@ -1,6 +1,6 @@ import { TrackContext } from "~/pages/PageEnigma/contexts/TrackContext/TrackContext"; import { ReactNode, useCallback, useMemo } from "react"; -import { AssetType } from "~/pages/PageEnigma/models"; +import { AssetType, MediaItem } from "~/pages/PageEnigma/models"; import useUpdateDragDrop from "~/pages/PageEnigma/contexts/TrackContext/utils/useUpdateDragDrop"; import { addCharacterAnimation, @@ -16,8 +16,11 @@ import { cameraGroup, audioGroup, objectGroup, + deleteCharacter, + currPosition, } from "~/pages/PageEnigma/store"; import useUpdateKeyframe from "~/pages/PageEnigma/contexts/TrackContext/utils/useUpdateKeyframe"; +import { deleteObject } from "~/pages/PageEnigma/store/objectGroup/deleteObject"; interface Props { children: ReactNode; @@ -88,16 +91,28 @@ export const TrackProvider = ({ children }: Props) => { }; }, []); + const deleteObjectOrCharacter = useCallback((item: MediaItem) => { + deleteCharacter(item); + deleteObject(item); + }, []); + const values = useMemo(() => { return { ...keyframes, clearExistingData, + deleteObjectOrCharacter, ...dragDrop, endDrag: dropClip, }; - }, [keyframes, dragDrop, dropClip, clearExistingData]); + }, [ + keyframes, + dragDrop, + dropClip, + clearExistingData, + deleteObjectOrCharacter, + ]); return ( {children} diff --git a/app/pages/PageEnigma/contexts/TrackContext/utils/useUpdateKeyframe.ts b/app/pages/PageEnigma/contexts/TrackContext/utils/useUpdateKeyframe.ts index 57dc53d8a2..0a3b3b8d90 100644 --- a/app/pages/PageEnigma/contexts/TrackContext/utils/useUpdateKeyframe.ts +++ b/app/pages/PageEnigma/contexts/TrackContext/utils/useUpdateKeyframe.ts @@ -1,5 +1,10 @@ import { useCallback, useContext } from "react"; -import { ClipGroup, QueueKeyframe, Keyframe } from "~/pages/PageEnigma/models"; +import { + ClipGroup, + QueueKeyframe, + Keyframe, + MediaItem, +} from "~/pages/PageEnigma/models"; import { addCameraKeyframe, addCharacterKeyframe, diff --git a/app/pages/PageEnigma/js/api_manager.ts b/app/pages/PageEnigma/js/api_manager.ts index 8e5f91a902..0ed80bf05d 100644 --- a/app/pages/PageEnigma/js/api_manager.ts +++ b/app/pages/PageEnigma/js/api_manager.ts @@ -119,7 +119,7 @@ export class APIManager { // scene_media_file_token: result_scene_media_file_token, //}; - return upload_glb_response['media_file_token']; + return upload_glb_response["media_file_token"]; } public async loadSceneState( @@ -128,6 +128,10 @@ export class APIManager { const api_base_url = "https://api.fakeyou.com"; const url = `${api_base_url}/v1/media_files/file/${scene_media_file_token}`; const response = await fetch(url); + if (response.status > 200) { + throw new APIManagerResponseError("Failed to load scene"); + } + const json = await JSON.parse(await response.text()); const bucket_path = json["media_file"]["public_bucket_path"]; const media_base_url = "https://storage.googleapis.com/vocodes-public"; @@ -198,7 +202,7 @@ export class APIManager { return media_url; } - /** + /** This will save the scene to keep ids positions. It will also give the file a name which will be a uuidv4() @param scene The 3JS Scene we want to make a file to be uploaded as multipart form. diff --git a/app/pages/PageEnigma/js/editor.ts b/app/pages/PageEnigma/js/editor.ts index af0707f0b9..7f355b59c4 100644 --- a/app/pages/PageEnigma/js/editor.ts +++ b/app/pages/PageEnigma/js/editor.ts @@ -27,7 +27,10 @@ import { ClipGroup } from "~/pages/PageEnigma/models/track"; import { XYZ } from "../datastructures/common"; import { StoryTellerProxyScene } from "../proxy/storyteller_proxy_scene"; import { StoryTellerProxyTimeline } from "../proxy/storyteller_proxy_timeline"; - +import Queue from "~/pages/PageEnigma/Queue/Queue"; +import { QueueNames } from "~/pages/PageEnigma/Queue/QueueNames"; +import { fromEngineActions } from "~/pages/PageEnigma/Queue/fromEngineActions"; +import { AssetType, MediaItem } from "~/pages/PageEnigma/models"; // Main editor class that will call everything else all you need to call is " initialize() ". class Editor { @@ -202,8 +205,10 @@ class Editor { this.negative_prompt = ""; this.art_style = ArtStyle.Anime2DFlat; - - this.storyteller_proxy_scene = new StoryTellerProxyScene(this.version, this.activeScene.scene) + this.storyteller_proxy_scene = new StoryTellerProxyScene( + this.version, + this.activeScene.scene, + ); } initialize(config: any) { @@ -353,7 +358,7 @@ class Editor { console.log(result); } - public async testTestTimelineEvents() { } + public async testTestTimelineEvents() {} public async loadScene(scene_media_token: string) { this.dispatchAppUiState({ @@ -362,18 +367,31 @@ class Editor { this.current_scene_media_token = scene_media_token; - const scene_json = await this.api_manager.loadSceneState( - this.current_scene_media_token, - ); + const scene_json = await this.api_manager + .loadSceneState(this.current_scene_media_token) + .catch((err) => { + this.dispatchAppUiState({ + type: APPUI_ACTION_TYPES.HIDE_EDITOR_LOADER, + }); + throw err; + }); - let proxyScene = new StoryTellerProxyScene(this.version, this.activeScene); - await proxyScene.loadFromSceneJson(scene_json['scene']); + const proxyScene = new StoryTellerProxyScene( + this.version, + this.activeScene, + ); + await proxyScene.loadFromSceneJson(scene_json["scene"]); this.cam_obj = this.activeScene.get_object_by_name("::CAM::"); - let proxyTimeline = new StoryTellerProxyTimeline(this.version, this.timeline, this.transform_engine, this.animation_engine, this.audio_engine, this.lipsync_engine); - await proxyTimeline.loadFromJson(scene_json['timeline']); - - + const proxyTimeline = new StoryTellerProxyTimeline( + this.version, + this.timeline, + this.transform_engine, + this.animation_engine, + this.audio_engine, + this.lipsync_engine, + ); + await proxyTimeline.loadFromJson(scene_json["timeline"]); this.dispatchAppUiState({ type: APPUI_ACTION_TYPES.HIDE_EDITOR_LOADER, @@ -383,23 +401,21 @@ 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`) - this.selected.position.x = position.x - this.selected.position.y = position.y - this.selected.position.z = position.z + //console.log(`triggering setSelectedObject`) + this.selected.position.x = position.x; + this.selected.position.y = position.y; + this.selected.position.z = position.z; - this.selected.rotation.x = rotation.x - this.selected.rotation.y = rotation.y - this.selected.rotation.z = rotation.z - - this.selected.scale.x = scale.x - this.selected.scale.y = scale.y - this.selected.scale.z = scale.z + this.selected.rotation.x = rotation.x; + this.selected.rotation.y = rotation.y; + this.selected.rotation.z = rotation.z; + this.selected.scale.x = scale.x; + this.selected.scale.y = scale.y; + this.selected.scale.z = scale.z; } } - public async saveScene(name: string) { // remove controls when saving scene. this.removeTransformControls(); @@ -407,13 +423,23 @@ class Editor { type: APPUI_ACTION_TYPES.SHOW_EDITOR_LOADER, }); - let proxyScene = new StoryTellerProxyScene(this.version, this.activeScene); - let scene_json = await proxyScene.saveToScene(); + const proxyScene = new StoryTellerProxyScene( + this.version, + this.activeScene, + ); + const scene_json = await proxyScene.saveToScene(); - let proxyTimeline = new StoryTellerProxyTimeline(this.version, this.timeline, this.transform_engine, this.animation_engine, this.audio_engine, this.lipsync_engine); - let timeline_json = await proxyTimeline.saveToJson(); + const proxyTimeline = new StoryTellerProxyTimeline( + this.version, + this.timeline, + this.transform_engine, + this.animation_engine, + this.audio_engine, + this.lipsync_engine, + ); + const timeline_json = await proxyTimeline.saveToJson(); - let save_data = { scene: scene_json, timeline: timeline_json }; + const save_data = { scene: scene_json, timeline: timeline_json }; // TODO turn scene information into and object ... const result = await this.api_manager.saveSceneState( @@ -455,7 +481,6 @@ class Editor { console.log(this.camera_person_mode); if (this.cam_obj) { if (this.camera_person_mode) { - this.last_cam_pos.copy(this.camera.position); this.last_cam_rot.copy(this.camera.rotation); @@ -475,7 +500,9 @@ class Editor { this.removeTransformControls(); this.selected = this.cam_obj; - this.dispatchAppUiState({ type: APPUI_ACTION_TYPES.SHOW_CONTROLPANELS_SCENEOBJECT }); + this.dispatchAppUiState({ + type: APPUI_ACTION_TYPES.SHOW_CONTROLPANELS_SCENEOBJECT, + }); this.updateSelectedUI(); } else { this.camera.position.copy(this.last_cam_pos); @@ -517,7 +544,7 @@ class Editor { object.uuid = "CH1"; // Stick Open Pose Man: m_9f3d3z94kk6m25zywyz6an3p43fjtw - // XBot: m_r7w1tmkx2jg8nznr3hyzj4k6zhfh7d + // XBot: m_r7w1tmkx2jg8nznr3hyzj4k6zhfh7d // YBot: m_9sqg0evpr23587jnr8z3zsvav1x077 // Shrek: m_fmxy8wjnep1hdaz7qdg4n7y15d2bsp } @@ -587,14 +614,27 @@ class Editor { } deleteObject(uuid: string) { - let obj = this.activeScene.get_object_by_uuid(uuid); + const obj = this.activeScene.get_object_by_uuid(uuid); if (obj) { this.activeScene.scene.remove(obj); } this.removeTransformControls(); + Queue.publish({ + queueName: QueueNames.FROM_ENGINE, + action: fromEngineActions.DELETE_OBJECT, + data: { + version: 1, + type: AssetType.OBJECT, + media_id: "", + object_uuid: this.selected?.uuid, + name: "", + } as MediaItem, + }); this.selected = undefined; - this.dispatchAppUiState({ type: APPUI_ACTION_TYPES.HIDE_CONTROLPANELS_SCENEOBJECT }); - this.timeline.deleteObject(uuid) + this.dispatchAppUiState({ + type: APPUI_ACTION_TYPES.HIDE_CONTROLPANELS_SCENEOBJECT, + }); + this.timeline.deleteObject(uuid); } create_parim(name: string) { @@ -674,10 +714,8 @@ class Editor { const min = new THREE.Vector3(-12, -1, -12); const max = new THREE.Vector3(12, 24, 12); this.camera.position.copy(this.camera.position.clamp(min, max)); - } - } - else if (this.cam_obj) { + } else if (this.cam_obj) { this.cam_obj.visible = true; } @@ -689,8 +727,7 @@ class Editor { if (this.timeline.is_playing) { await this.timeline.update(this.rendering); - } - else if (this.last_scrub == this.timeline.scrubber_frame_position) { + } else if (this.last_scrub == this.timeline.scrubber_frame_position) { this.updateSelectedUI(); } @@ -757,10 +794,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`, @@ -790,7 +827,9 @@ class Editor { this.renderMode(); - if (this.generating_preview) { return; } + if (this.generating_preview) { + return; + } this.generating_preview = true; const ffmpeg = createFFmpeg({ log: true }); await ffmpeg.load(); @@ -845,10 +884,12 @@ class Editor { const blob = new Blob([output.buffer], { type: "video/mp4" }); const data: any = await this.api_manager.uploadMedia(blob, "render.mp4"); - console.log("data", data) + console.log("data", data); - if (data == null) { return; } - let upload_token = data['media_file_token']; + if (data == null) { + return; + } + const upload_token = data["media_file_token"]; console.log(upload_token); // Create a link to download the file stylize video using api .. //{"success":true,"upload_token":"mu_x9kr5cfafn512pjbygdszvbdpktrr"} payload @@ -868,7 +909,6 @@ class Editor { // {"success":true,"inference_job_token":"jinf_j3nbqbd15wqxb0xcks13qh3f3bz"} console.log(result); - } async generateFrame() { @@ -970,7 +1010,6 @@ class Editor { } updateSelectedUI() { - if (this.selected == undefined) { return; } @@ -1008,7 +1047,6 @@ class Editor { }, }, }); - } // Automaticly resize scene. @@ -1078,7 +1116,11 @@ class Editor { this.activeScene.scene.children.forEach((child: THREE.Object3D) => { // console.log(child); if (child.name != "") { - if (child.type == "Mesh" || child.type == "Object3D" || child.type == "Group") { + if ( + child.type == "Mesh" || + child.type == "Object3D" || + child.type == "Group" + ) { interactable.push(child); } } @@ -1092,7 +1134,7 @@ class Editor { currentObject = currentObject.parent; } this.selected = currentObject; - // Show panel here + // Show panel here if (this.selected.type == "Scene") { this.selected = intersects[0].object; @@ -1106,7 +1148,9 @@ class Editor { this.transform_interaction = true; // Contact react land - this.dispatchAppUiState({ type: APPUI_ACTION_TYPES.SHOW_CONTROLPANELS_SCENEOBJECT }); + this.dispatchAppUiState({ + type: APPUI_ACTION_TYPES.SHOW_CONTROLPANELS_SCENEOBJECT, + }); this.updateSelectedUI(); } } else if (this.transform_interaction == false) { diff --git a/app/pages/PageEnigma/store/characterGroups/deleteCharacter.ts b/app/pages/PageEnigma/store/characterGroups/deleteCharacter.ts new file mode 100644 index 0000000000..84c00ad0b8 --- /dev/null +++ b/app/pages/PageEnigma/store/characterGroups/deleteCharacter.ts @@ -0,0 +1,8 @@ +import { MediaItem } from "~/pages/PageEnigma/models"; +import { characterGroups } from "~/pages/PageEnigma/store"; + +export function deleteCharacter(item: MediaItem) { + characterGroups.value = characterGroups.value.filter( + (character) => character.id !== item.object_uuid, + ); +} diff --git a/app/pages/PageEnigma/store/characterGroups/index.ts b/app/pages/PageEnigma/store/characterGroups/index.ts index eb200150c0..1357ba2e4d 100644 --- a/app/pages/PageEnigma/store/characterGroups/index.ts +++ b/app/pages/PageEnigma/store/characterGroups/index.ts @@ -1,4 +1,5 @@ export * from "./addCharacter"; export * from "./characterGroups"; +export * from "./deleteCharacter"; export * from "./loadCharacterData"; export * from "./updateCharacters"; diff --git a/app/pages/PageEnigma/store/objectGroup/deleteObject.ts b/app/pages/PageEnigma/store/objectGroup/deleteObject.ts new file mode 100644 index 0000000000..8490a4fd88 --- /dev/null +++ b/app/pages/PageEnigma/store/objectGroup/deleteObject.ts @@ -0,0 +1,11 @@ +import { MediaItem } from "~/pages/PageEnigma/models"; +import { objectGroup } from "~/pages/PageEnigma/store"; + +export function deleteObject(item: MediaItem) { + objectGroup.value = { + ...objectGroup.value, + objects: objectGroup.value.objects.filter( + (obj) => obj.object_uuid !== item.object_uuid, + ), + }; +}