mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-09 03:21:23 +00:00
Editor Refactoring (#402)
Co-authored-by: Christopher Oliphant <RALifeCoach@users.noreply.github.com> Co-authored-by: Quicksticks-oss <chongobongotoday@gmail.com>
This commit is contained in:
46 files changed
+1516
-1130
No files matched your search
@@ -23,6 +23,8 @@ module.exports = {
|
||||
// Base config
|
||||
extends: ["eslint:recommended", "plugin:storybook/recommended"],
|
||||
|
||||
ignorePatterns: ["storybook-static/*"],
|
||||
|
||||
overrides: [
|
||||
// React
|
||||
{
|
||||
|
||||
+5
-4
@@ -1,4 +1,4 @@
|
||||
export const apiHost = "https://api.fakeyou.com";
|
||||
export const apiHost = "https://api.storyteller.ai";
|
||||
|
||||
// Authentications
|
||||
// const createAccount = `${apiHost}}/create_account`;
|
||||
@@ -19,7 +19,6 @@ export const uploadAudioV2V = `${apiHost}/v1/media_uploads/upload_audio`;
|
||||
export const listMediaByUser = (username: string) =>
|
||||
`${apiHost}/v1/media_files/list/user/${username}`;
|
||||
|
||||
|
||||
// Get Any Media
|
||||
export const getMediaFileByToken = (fileToken: string) =>
|
||||
`${apiHost}/v1/media_files/file/${fileToken}`;
|
||||
@@ -30,5 +29,7 @@ export const uploadMedia = `${apiHost}/v1/media_files/upload`;
|
||||
// Scenes
|
||||
export const uploadNewScene = `${apiHost}/v1/media_files/upload/new_scene`;
|
||||
export const uploadThumbnail = `${apiHost}/v1/media_files/cover_image/`;
|
||||
export const updateExistingScene = (sceneToken:string)=>`${apiHost}/v1/media_files/upload/saved_scene/${sceneToken}`
|
||||
export const renameScene = (sceneToken:string)=>`${apiHost}/v1/media_files/rename/${sceneToken}`
|
||||
export const updateExistingScene = (sceneToken: string) =>
|
||||
`${apiHost}/v1/media_files/upload/saved_scene/${sceneToken}`;
|
||||
export const renameScene = (sceneToken: string) =>
|
||||
`${apiHost}/v1/media_files/rename/${sceneToken}`;
|
||||
@@ -0,0 +1,48 @@
|
||||
.fy-file-wrapper {
|
||||
// flex-grow: 1;
|
||||
// height: calc(100% - 4px);
|
||||
|
||||
.file-picker-button {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.fy-file-wrapper-input {
|
||||
visibility: hidden;
|
||||
height: 0;
|
||||
width: 0;
|
||||
// overflow: hidden;
|
||||
position: absolute;
|
||||
|
||||
+ label {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
.fy-uploader-layout {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
|
||||
.fy-uploader-layout-icon {
|
||||
font-size: 2.625rem;
|
||||
margin-right: 1rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.fy-file-dragging {
|
||||
background: rgba(#676781, 0.25);
|
||||
border-color: rgba(#fff, 0.33);
|
||||
}
|
||||
|
||||
.fy-uploader {
|
||||
[type="file"] {
|
||||
+ label {
|
||||
border: dashed 2px rgba(255, 255, 255, 0.12) !important;
|
||||
border-radius: 0.5rem;
|
||||
transition: 0.1s all;
|
||||
&:hover {
|
||||
// background-color: lighten($panel, 6%);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import React from "react";
|
||||
|
||||
export default function LoadingIcon() {
|
||||
return <>
|
||||
<span
|
||||
className="spinner-border spinner-border-sm ms-3"
|
||||
role="status"
|
||||
aria-hidden="true"
|
||||
></span>
|
||||
<span className="visually-hidden">Loading...</span>
|
||||
</>;
|
||||
};
|
||||
@@ -0,0 +1,12 @@
|
||||
/**
|
||||
* This is designed to surface user customer facing messages as errors.
|
||||
* Errors shouldn't be 404 or something confusing should be
|
||||
*/
|
||||
class APIManagerResponseError extends Error {
|
||||
constructor(message?: string) {
|
||||
super(message);
|
||||
this.name = "APIManagerResponseError";
|
||||
}
|
||||
}
|
||||
|
||||
export { APIManagerResponseError };
|
||||
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* This is designed to surface user customer facing messages as errors.
|
||||
*/
|
||||
type Data = { [key: string]: any };
|
||||
class APIManagerResponseSuccess {
|
||||
public user_message: string;
|
||||
public data: Data | null;
|
||||
constructor(user_message: string = "", data: Data | null = null) {
|
||||
this.data = data;
|
||||
this.user_message = user_message;
|
||||
}
|
||||
}
|
||||
|
||||
export { APIManagerResponseSuccess };
|
||||
File renamed without changes.
-2
@@ -22,5 +22,3 @@ export class AnimationEngine {
|
||||
//this.clips[object_uuid + media_id]._load_animation();
|
||||
}
|
||||
}
|
||||
|
||||
export default AnimationEngine;
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File diff suppressed because it is too large.
Load diff
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,218 @@
|
||||
import { EventDispatcher, Quaternion, Vector3, Vector2 } from "three";
|
||||
import * as THREE from "three";
|
||||
|
||||
class FreeCam extends EventDispatcher {
|
||||
object: THREE.PerspectiveCamera;
|
||||
domElement: HTMLElement | Document;
|
||||
enabled = true;
|
||||
movementSpeed = 1.0;
|
||||
rollSpeed = 0.005;
|
||||
dragToLook = false;
|
||||
autoForward = false;
|
||||
|
||||
lastMousePosition = new Vector2();
|
||||
mouseVelocity = new Vector2();
|
||||
tmpQuaternion = new Quaternion();
|
||||
|
||||
status = 0;
|
||||
|
||||
moveState = {
|
||||
up: 0,
|
||||
down: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
forward: 0,
|
||||
back: 0,
|
||||
pitchUp: 0,
|
||||
pitchDown: 0,
|
||||
yawLeft: 0,
|
||||
yawRight: 0,
|
||||
rollLeft: 0,
|
||||
rollRight: 0,
|
||||
};
|
||||
moveVector = new Vector3(0, 0, 0);
|
||||
rotationVector = new Vector3(0, 0, 0);
|
||||
|
||||
constructor(
|
||||
object: THREE.PerspectiveCamera,
|
||||
domElement: HTMLElement | Document,
|
||||
) {
|
||||
super();
|
||||
|
||||
this.object = object;
|
||||
this.domElement = domElement;
|
||||
|
||||
this.keydown = this.keydown.bind(this);
|
||||
this.keyup = this.keyup.bind(this);
|
||||
this.reset = this.reset.bind(this);
|
||||
this.update = this.update.bind(this);
|
||||
this.updateMovementVector = this.updateMovementVector.bind(this);
|
||||
this.getContainerDimensions = this.getContainerDimensions.bind(this);
|
||||
this.dispose = this.dispose.bind(this);
|
||||
|
||||
window.addEventListener("keydown", this.keydown);
|
||||
window.addEventListener("keyup", this.keyup);
|
||||
|
||||
this.updateMovementVector();
|
||||
}
|
||||
|
||||
keydown(event: KeyboardEvent) {
|
||||
if (event.altKey || !this.enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (event.code) {
|
||||
case "KeyW":
|
||||
this.moveState.forward = 1;
|
||||
break;
|
||||
case "KeyS":
|
||||
this.moveState.back = 1;
|
||||
break;
|
||||
|
||||
case "KeyA":
|
||||
this.moveState.left = 1;
|
||||
break;
|
||||
case "KeyD":
|
||||
this.moveState.right = 1;
|
||||
break;
|
||||
|
||||
case "KeyQ":
|
||||
this.moveState.down = 1;
|
||||
break;
|
||||
case "KeyE":
|
||||
this.moveState.up = 1;
|
||||
break;
|
||||
|
||||
case "ArrowUp":
|
||||
this.moveState.pitchUp = 1;
|
||||
break;
|
||||
case "ArrowDown":
|
||||
this.moveState.pitchDown = 1;
|
||||
break;
|
||||
|
||||
case "ArrowLeft":
|
||||
this.moveState.yawLeft = 1;
|
||||
break;
|
||||
case "ArrowRight":
|
||||
this.moveState.yawRight = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
this.updateMovementVector();
|
||||
}
|
||||
|
||||
keyup(event: KeyboardEvent) {
|
||||
if (!this.enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (event.code) {
|
||||
case "KeyW":
|
||||
this.moveState.forward = 0;
|
||||
break;
|
||||
case "KeyS":
|
||||
this.moveState.back = 0;
|
||||
break;
|
||||
|
||||
case "KeyA":
|
||||
this.moveState.left = 0;
|
||||
break;
|
||||
case "KeyD":
|
||||
this.moveState.right = 0;
|
||||
break;
|
||||
|
||||
case "ArrowUp":
|
||||
this.moveState.pitchUp = 0;
|
||||
break;
|
||||
case "ArrowDown":
|
||||
this.moveState.pitchDown = 0;
|
||||
break;
|
||||
|
||||
case "ArrowLeft":
|
||||
this.moveState.yawLeft = 0;
|
||||
break;
|
||||
case "ArrowRight":
|
||||
this.moveState.yawRight = 0;
|
||||
break;
|
||||
|
||||
case "KeyQ":
|
||||
this.moveState.down = 0;
|
||||
break;
|
||||
case "KeyE":
|
||||
this.moveState.up = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
this.updateMovementVector();
|
||||
}
|
||||
|
||||
reset() {
|
||||
this.moveState.forward = 0;
|
||||
this.moveState.back = 0;
|
||||
this.moveState.left = 0;
|
||||
this.moveState.right = 0;
|
||||
this.moveState.pitchUp = 0;
|
||||
this.moveState.pitchDown = 0;
|
||||
this.moveState.yawLeft = 0;
|
||||
this.moveState.yawRight = 0;
|
||||
this.moveState.down = 0;
|
||||
this.moveState.up = 0;
|
||||
this.updateMovementVector();
|
||||
}
|
||||
|
||||
update(delta: number) {
|
||||
if (!this.enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
const moveMulti = delta * this.movementSpeed;
|
||||
|
||||
this.object.translateX(this.moveVector.x * moveMulti);
|
||||
this.object.translateY(this.moveVector.y * moveMulti);
|
||||
this.object.translateZ(this.moveVector.z * moveMulti);
|
||||
}
|
||||
|
||||
updateMovementVector() {
|
||||
const forward =
|
||||
this.moveState.forward || (this.autoForward && !this.moveState.back)
|
||||
? 1
|
||||
: 0;
|
||||
|
||||
this.moveVector.x = -this.moveState.left + this.moveState.right;
|
||||
this.moveVector.y = -this.moveState.down + this.moveState.up;
|
||||
this.moveVector.z = -forward + this.moveState.back;
|
||||
}
|
||||
|
||||
getContainerDimensions() {
|
||||
if (this.domElement !== document) {
|
||||
return {
|
||||
size: [
|
||||
(this.domElement as HTMLElement).offsetWidth,
|
||||
(this.domElement as HTMLElement).offsetHeight,
|
||||
],
|
||||
offset: [
|
||||
(this.domElement as HTMLElement).offsetLeft,
|
||||
(this.domElement as HTMLElement).offsetTop,
|
||||
],
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
size: [window.innerWidth, window.innerHeight],
|
||||
offset: [0, 0],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
dispose() {
|
||||
// this.domElement.removeEventListener("contextmenu", this.contextmenu);
|
||||
// this.domElement.removeEventListener("pointerdown", this.pointerdown);
|
||||
// this.domElement.removeEventListener("pointermove", this.pointermove);
|
||||
// this.domElement.removeEventListener("pointerup", this.pointerup);
|
||||
// this.domElement.removeEventListener("pointercancel", this.pointercancel);
|
||||
//
|
||||
window.removeEventListener("keydown", this.keydown);
|
||||
window.removeEventListener("keyup", this.keyup);
|
||||
}
|
||||
}
|
||||
|
||||
export { FreeCam };
|
||||
@@ -0,0 +1,218 @@
|
||||
import Scene from "./scene";
|
||||
import Editor from "./editor";
|
||||
import * as THREE from "three";
|
||||
import { XYZ } from "../datastructures/common";
|
||||
import { editorState } from "../signals/engine";
|
||||
import { EditorStates } from "../enums";
|
||||
import Queue from "~/pages/PageEnigma/Queue/Queue";
|
||||
import { QueueNames } from "~/pages/PageEnigma/Queue/QueueNames";
|
||||
import { fromEngineActions } from "~/pages/PageEnigma/Queue/fromEngineActions";
|
||||
import { MediaItem } from "~/pages/PageEnigma/models";
|
||||
import { AssetType } from "~/enums";
|
||||
import { hideObjectPanel } from "../signals";
|
||||
|
||||
export class Utils {
|
||||
scene: Scene;
|
||||
editor: Editor;
|
||||
constructor(editor: Editor, scene: Scene) {
|
||||
this.scene = scene;
|
||||
this.editor = editor;
|
||||
}
|
||||
|
||||
// If string is empty.
|
||||
isEmpty(value: string): boolean {
|
||||
return (
|
||||
value == null || (typeof value === "string" && value.trim().length === 0)
|
||||
);
|
||||
}
|
||||
|
||||
// For react to see if an object has lipsync capability or not.
|
||||
isObjectLipsync(object_uuid: string) {
|
||||
const object = this.scene.get_object_by_uuid(object_uuid);
|
||||
let hasLipsync = false;
|
||||
if (object) {
|
||||
object.traverse((c: THREE.Object3D) => {
|
||||
if (c instanceof THREE.Mesh) {
|
||||
if (c.morphTargetInfluences && c.morphTargetDictionary) {
|
||||
const blendShapeIndexE = c.morphTargetDictionary["E"];
|
||||
// console.log(c.morphTargetDictionary, blendShapeIndexE)
|
||||
if (blendShapeIndexE !== null) {
|
||||
hasLipsync = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
return hasLipsync;
|
||||
}
|
||||
|
||||
// Returns if the object is locked or unlocked.
|
||||
isObjectLocked(object_uuid: string): boolean {
|
||||
const object = this.scene.get_object_by_uuid(object_uuid);
|
||||
if (object) {
|
||||
if (object.userData["locked"] == undefined) {
|
||||
object.userData["locked"] = false;
|
||||
}
|
||||
return object.userData["locked"];
|
||||
}
|
||||
console.log("No object found.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Locks or unlocks and object and returns its new state,
|
||||
lockUnlockObject(object_uuid: string): boolean {
|
||||
const object = this.scene.get_object_by_uuid(object_uuid);
|
||||
if (object) {
|
||||
if (object.userData["locked"] == undefined) {
|
||||
object.userData["locked"] = false;
|
||||
}
|
||||
object.userData["locked"] = !object.userData["locked"];
|
||||
|
||||
if (object.userData["locked"]) {
|
||||
this.removeTransformControls(false);
|
||||
} else if (this.editor.control) {
|
||||
this.scene.scene.add(this.editor.control);
|
||||
this.editor.control.attach(this.editor.selected);
|
||||
}
|
||||
|
||||
return object.userData["locked"];
|
||||
}
|
||||
console.log("No object found.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Removes transform controls and publishes selected.
|
||||
removeTransformControls(remove_outline: boolean = true) {
|
||||
if (this.editor.control == undefined) {
|
||||
return;
|
||||
}
|
||||
if (this.editor.outlinePass == undefined) {
|
||||
return;
|
||||
}
|
||||
if (remove_outline) {
|
||||
this.editor.last_selected = this.editor.selected;
|
||||
this.editor.selected = undefined;
|
||||
this.editor.publishSelect();
|
||||
}
|
||||
this.editor.control.detach();
|
||||
this.editor.activeScene.scene.remove(this.editor.control);
|
||||
if (remove_outline) this.editor.outlinePass.selectedObjects = [];
|
||||
}
|
||||
|
||||
// TO UPDATE selected objects in the scene might want to add to the scene ...
|
||||
async setSelectedObject(position: XYZ, rotation: XYZ, scale: XYZ) {
|
||||
if (this.editor.selected != undefined || this.editor.selected != null) {
|
||||
this.editor.selected.position.x = position.x;
|
||||
this.editor.selected.position.y = position.y;
|
||||
this.editor.selected.position.z = position.z;
|
||||
this.editor.selected.rotation.x = THREE.MathUtils.degToRad(rotation.x);
|
||||
this.editor.selected.rotation.y = THREE.MathUtils.degToRad(rotation.y);
|
||||
this.editor.selected.rotation.z = THREE.MathUtils.degToRad(rotation.z);
|
||||
this.editor.selected.scale.x = scale.x;
|
||||
this.editor.selected.scale.y = scale.y;
|
||||
this.editor.selected.scale.z = scale.z;
|
||||
}
|
||||
}
|
||||
|
||||
switchCameraView() {
|
||||
this.editor.camera_person_mode = !this.editor.camera_person_mode;
|
||||
this.editor.cameraViewControls?.reset();
|
||||
if (this.editor.cam_obj) {
|
||||
if (this.editor.camera_person_mode) {
|
||||
this.editor.last_cam_pos.copy(this.editor.camera.position);
|
||||
this.editor.last_cam_rot.copy(this.editor.camera.rotation);
|
||||
|
||||
this.editor.camera.position.copy(this.editor.cam_obj.position);
|
||||
this.editor.camera.rotation.copy(this.editor.cam_obj.rotation);
|
||||
|
||||
if (this.editor.orbitControls) {
|
||||
this.editor.orbitControls.enabled = false;
|
||||
}
|
||||
if (this.editor.lockControls) {
|
||||
this.editor.activeScene.scene.add(this.editor.lockControls.getObject());
|
||||
}
|
||||
if (this.editor.cameraViewControls) {
|
||||
this.editor.cameraViewControls.enabled = true;
|
||||
}
|
||||
this.editor.cam_obj.scale.set(0, 0, 0);
|
||||
|
||||
this.editor.removeTransformControls();
|
||||
this.editor.selected = this.editor.cam_obj;
|
||||
this.editor.publishSelect();
|
||||
this.editor.updateSelectedUI();
|
||||
editorState.value = EditorStates.CAMERA_VIEW;
|
||||
if (this.editor.activeScene.hot_items) {
|
||||
this.editor.activeScene.hot_items.forEach((element) => {
|
||||
element.visible = false;
|
||||
});
|
||||
}
|
||||
} else {
|
||||
this.editor.camera.position.copy(this.editor.last_cam_pos);
|
||||
this.editor.camera.rotation.copy(this.editor.last_cam_rot);
|
||||
if (this.editor.orbitControls) {
|
||||
this.editor.orbitControls.enabled = true;
|
||||
}
|
||||
if (this.editor.lockControls) {
|
||||
this.editor.activeScene.scene.remove(this.editor.lockControls.getObject());
|
||||
}
|
||||
if (this.editor.cameraViewControls) {
|
||||
this.editor.cameraViewControls.enabled = false;
|
||||
}
|
||||
this.editor.cam_obj.scale.set(1, 1, 1);
|
||||
if (this.editor.activeScene.hot_items) {
|
||||
this.editor.activeScene.hot_items.forEach((element) => {
|
||||
element.visible = true;
|
||||
});
|
||||
}
|
||||
|
||||
hideObjectPanel();
|
||||
editorState.value = EditorStates.EDIT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the "check sum" of the editors selected object.
|
||||
getselectedSum(): number {
|
||||
if (this.editor.selected === undefined) {
|
||||
return 0;
|
||||
}
|
||||
const posCombo =
|
||||
this.editor.selected.position.x +
|
||||
this.editor.selected.position.y +
|
||||
this.editor.selected.position.z;
|
||||
const rotCombo =
|
||||
this.editor.selected.rotation.x +
|
||||
this.editor.selected.rotation.y +
|
||||
this.editor.selected.rotation.z;
|
||||
const sclCombo =
|
||||
this.editor.selected.scale.x + this.editor.selected.scale.y + this.editor.selected.scale.z;
|
||||
return posCombo + rotCombo + sclCombo;
|
||||
}
|
||||
|
||||
deleteObject(uuid: string) {
|
||||
const obj = this.scene.get_object_by_uuid(uuid);
|
||||
this.removeTransformControls();
|
||||
if (obj?.name === this.editor.camera_name) {
|
||||
return;
|
||||
}
|
||||
if (obj) {
|
||||
this.scene.scene.remove(obj);
|
||||
}
|
||||
this.editor.timeline.deleteObject(uuid);
|
||||
Queue.publish({
|
||||
queueName: QueueNames.FROM_ENGINE,
|
||||
action: fromEngineActions.DELETE_OBJECT,
|
||||
data: {
|
||||
version: 1,
|
||||
type: AssetType.OBJECT,
|
||||
media_id: "",
|
||||
object_uuid: uuid,
|
||||
name: "",
|
||||
} as MediaItem,
|
||||
});
|
||||
this.editor.selected = undefined;
|
||||
this.editor.publishSelect();
|
||||
hideObjectPanel();
|
||||
this.editor.timeline.deleteObject(uuid);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
import Editor from "./editor";
|
||||
import * as THREE from "three";
|
||||
import { hideObjectPanel, hotkeysStatus, showObjectPanel } from "../signals";
|
||||
|
||||
export class MouseControls {
|
||||
editor: Editor;
|
||||
|
||||
constructor(editor: Editor) {
|
||||
this.editor = editor;
|
||||
}
|
||||
|
||||
onMouseDown(event: any) {
|
||||
if (event.button === 1 && this.editor.camera_person_mode) {
|
||||
this.editor.lockControls?.lock();
|
||||
}
|
||||
}
|
||||
|
||||
onMouseUp(event: any) {
|
||||
if (event.button === 1) {
|
||||
this.editor.lockControls?.unlock();
|
||||
}
|
||||
|
||||
if (event.button !== 0) {
|
||||
const camera_pos = new THREE.Vector3(
|
||||
parseFloat(this.editor.camera.position.x.toFixed(2)),
|
||||
parseFloat(this.editor.camera.position.y.toFixed(2)),
|
||||
parseFloat(this.editor.camera.position.z.toFixed(2)),
|
||||
);
|
||||
this.editor.camera_last_pos.copy(camera_pos);
|
||||
}
|
||||
}
|
||||
|
||||
onkeydown(event: KeyboardEvent) {
|
||||
if (hotkeysStatus.value.disabled) {
|
||||
return;
|
||||
}
|
||||
if (event.key === "f" && this.editor.selected && this.editor.orbitControls) {
|
||||
this.editor.orbitControls.target.copy(this.editor.selected.position);
|
||||
this.editor.orbitControls.maxDistance = 4;
|
||||
this.editor.orbitControls.update();
|
||||
this.editor.orbitControls.maxDistance = 999;
|
||||
return;
|
||||
}
|
||||
if (event.key === " ") {
|
||||
if (!this.editor.rendering && !this.editor.switchPreviewToggle && this.editor.selectedCanvas) {
|
||||
this.editor.togglePlayback();
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (event.key === "Backspace" || event.key === "Delete") {
|
||||
if (this.editor.selected) {
|
||||
this.editor.deleteObject(this.editor.selected.uuid);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Sets new mouse location usually used in raycasts.
|
||||
onMouseMove(event: any) {
|
||||
const rect = this.editor.canvReference.getBoundingClientRect();
|
||||
if (this.editor.mouse == undefined) {
|
||||
return;
|
||||
}
|
||||
this.editor.mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1;
|
||||
this.editor.mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1;
|
||||
this.editor.timeline.mouse = this.editor.mouse;
|
||||
}
|
||||
|
||||
// When the mouse clicks the screen.
|
||||
onMouseClick() {
|
||||
const camera_pos = new THREE.Vector3(
|
||||
parseFloat(this.editor.camera.position.x.toFixed(2)),
|
||||
parseFloat(this.editor.camera.position.y.toFixed(2)),
|
||||
parseFloat(this.editor.camera.position.z.toFixed(2)),
|
||||
);
|
||||
if (this.editor.camera_last_pos.equals(new THREE.Vector3(0, 0, 0))) {
|
||||
this.editor.camera_last_pos.copy(camera_pos);
|
||||
}
|
||||
|
||||
if (
|
||||
this.editor.raycaster == undefined ||
|
||||
this.editor.mouse == undefined ||
|
||||
this.editor.control == undefined ||
|
||||
this.editor.outlinePass == undefined ||
|
||||
this.editor.camera_person_mode ||
|
||||
!this.editor.camera_last_pos.equals(camera_pos)
|
||||
) {
|
||||
this.editor.camera_last_pos.copy(camera_pos);
|
||||
return;
|
||||
}
|
||||
this.editor.camera_last_pos.copy(camera_pos);
|
||||
|
||||
this.editor.raycaster.setFromCamera(this.editor.mouse, this.editor.camera);
|
||||
const interactable: any[] = [];
|
||||
this.editor.activeScene.scene.children.forEach((child: THREE.Object3D) => {
|
||||
if (child.name != "") {
|
||||
if (
|
||||
child.type == "Mesh" ||
|
||||
child.type == "Object3D" ||
|
||||
child.type == "Group"
|
||||
) {
|
||||
interactable.push(child);
|
||||
}
|
||||
}
|
||||
});
|
||||
const intersects = this.editor.raycaster.intersectObjects(interactable, true);
|
||||
|
||||
if (intersects.length > 0) {
|
||||
if (intersects[0].object.type != "GridHelper") {
|
||||
let currentObject = intersects[0].object;
|
||||
while (currentObject.parent && currentObject.parent.type !== "Scene") {
|
||||
currentObject = currentObject.parent;
|
||||
}
|
||||
this.editor.selected = currentObject;
|
||||
// Show panel here
|
||||
|
||||
if (this.editor.selected.type == "Scene") {
|
||||
this.editor.selected = intersects[0].object;
|
||||
}
|
||||
this.editor.activeScene.selected = this.editor.selected;
|
||||
this.editor.publishSelect();
|
||||
|
||||
// this.editor.update_properties()
|
||||
if (this.editor.selected.userData["locked"] !== true) {
|
||||
this.editor.activeScene.scene.add(this.editor.control);
|
||||
this.editor.control.attach(this.editor.selected);
|
||||
}
|
||||
|
||||
this.editor.outlinePass.selectedObjects = [this.editor.selected];
|
||||
this.editor.transform_interaction = true;
|
||||
// Contact react land
|
||||
showObjectPanel();
|
||||
this.editor.updateSelectedUI();
|
||||
}
|
||||
} else {
|
||||
this.editor.removeTransformControls();
|
||||
hideObjectPanel();
|
||||
}
|
||||
}
|
||||
}
|
||||
-2
@@ -20,5 +20,3 @@ export class LipSyncEngine {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default LipSyncEngine;
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,109 @@
|
||||
import { StoryTellerProxyScene } from "../proxy/storyteller_proxy_scene";
|
||||
import { StoryTellerProxyTimeline } from "../proxy/storyteller_proxy_timeline";
|
||||
import { hideEditorLoader, showEditorLoader } from "../signals";
|
||||
import Editor from "./editor";
|
||||
|
||||
export type EditorInitializeConfig = {
|
||||
sceneToken: string;
|
||||
};
|
||||
|
||||
export class SaveManager {
|
||||
editor: Editor
|
||||
constructor(editor: Editor) {
|
||||
this.editor = editor;
|
||||
}
|
||||
|
||||
public async saveScene({
|
||||
sceneTitle,
|
||||
sceneToken,
|
||||
}: {
|
||||
sceneTitle: string;
|
||||
sceneToken?: string;
|
||||
}): Promise<string> {
|
||||
this.editor.generating_preview = true; // Set this to true to stop control panel from flipping out.
|
||||
// remove controls when saving scene.
|
||||
this.editor.removeTransformControls();
|
||||
showEditorLoader();
|
||||
|
||||
const proxyScene = new StoryTellerProxyScene(
|
||||
this.editor.version,
|
||||
this.editor.activeScene,
|
||||
);
|
||||
const scene_json = await proxyScene.saveToScene();
|
||||
|
||||
const proxyTimeline = new StoryTellerProxyTimeline(
|
||||
this.editor.version,
|
||||
this.editor.timeline,
|
||||
this.editor.transform_engine,
|
||||
this.editor.animation_engine,
|
||||
this.editor.audio_engine,
|
||||
this.editor.lipsync_engine,
|
||||
this.editor.emotion_engine,
|
||||
);
|
||||
const timeline_json = await proxyTimeline.saveToJson();
|
||||
|
||||
const save_data = {
|
||||
version: this.editor.version,
|
||||
scene: scene_json,
|
||||
timeline: timeline_json,
|
||||
};
|
||||
|
||||
// TODO turn scene information into and object ...
|
||||
let sceneThumbnail = undefined;
|
||||
|
||||
if (this.editor.renderer) {
|
||||
const imgData = this.editor.renderer.domElement.toDataURL();
|
||||
const response = await fetch(imgData); // Fetch the data URL
|
||||
sceneThumbnail = await response.blob(); // Convert to Blob
|
||||
}
|
||||
|
||||
const result = await this.editor.api_manager.saveSceneState({
|
||||
saveJson: JSON.stringify(save_data),
|
||||
sceneTitle,
|
||||
sceneToken,
|
||||
sceneThumbnail,
|
||||
});
|
||||
|
||||
hideEditorLoader();
|
||||
|
||||
this.editor.generating_preview = false; // FIX THIS LATER WITH VICCCCCCCCCCCCCCCTORRRRRRRR
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public async loadScene(scene_media_token: string) {
|
||||
showEditorLoader();
|
||||
|
||||
this.editor.current_scene_media_token = scene_media_token;
|
||||
|
||||
const scene_json = await this.editor.api_manager
|
||||
.loadSceneState(this.editor.current_scene_media_token)
|
||||
.catch((err) => {
|
||||
hideEditorLoader();
|
||||
throw err;
|
||||
});
|
||||
const proxyScene = new StoryTellerProxyScene(
|
||||
this.editor.version,
|
||||
this.editor.activeScene,
|
||||
);
|
||||
await proxyScene.loadFromSceneJson(scene_json["scene"]);
|
||||
this.editor.cam_obj = this.editor.activeScene.get_object_by_name(this.editor.camera_name);
|
||||
this.editor.cam_obj?.layers.set(1);
|
||||
|
||||
const proxyTimeline = new StoryTellerProxyTimeline(
|
||||
this.editor.version,
|
||||
this.editor.timeline,
|
||||
this.editor.transform_engine,
|
||||
this.editor.animation_engine,
|
||||
this.editor.audio_engine,
|
||||
this.editor.lipsync_engine,
|
||||
this.editor.emotion_engine,
|
||||
);
|
||||
await proxyTimeline.loadFromJson(scene_json["timeline"]);
|
||||
|
||||
this.editor.timeline.checkEditorCanPlay();
|
||||
|
||||
hideEditorLoader();
|
||||
this.editor.timeline.scrub({ data: { currentTime: 0 } })
|
||||
}
|
||||
}
|
||||
File renamed without changes.
@@ -5,32 +5,23 @@ import { ClipUI } from "../datastructures/clips/clip_ui";
|
||||
import Scene from "./scene.js";
|
||||
import AudioEngine from "./audio_engine";
|
||||
import TransformEngine from "./transform_engine";
|
||||
import LipSyncEngine from "./lip_sync_engine";
|
||||
import AnimationEngine from "./animation_engine";
|
||||
import { LipSyncEngine } from "./lip_sync_engine";
|
||||
import { AnimationEngine } from "./animation_engine";
|
||||
|
||||
import Queue from "~/pages/PageEnigma/Queue/Queue";
|
||||
import Queue, {
|
||||
UnionedActionTypes,
|
||||
UnionedDataTypes,
|
||||
} from "~/pages/PageEnigma/Queue/Queue";
|
||||
import { QueueNames } from "../Queue/QueueNames";
|
||||
import { toEngineActions } from "../Queue/toEngineActions";
|
||||
import { fromEngineActions } from "../Queue/fromEngineActions";
|
||||
import { AssetType } from "~/enums";
|
||||
import { ClipGroup, ClipType } from "~/pages/PageEnigma/enums";
|
||||
import { MediaItem } from "~/pages/PageEnigma/models";
|
||||
import Editor from "~/pages/PageEnigma/js/editor";
|
||||
import { Keyframe, MediaItem, UpdateTime } from "~/pages/PageEnigma/models";
|
||||
import Editor from "~/pages/PageEnigma/Editor/editor";
|
||||
import EmotionEngine from "./emotion_engine";
|
||||
import { GenerationOptions } from '../models/generationOptions';
|
||||
|
||||
// Every object uuid / entity has a track.
|
||||
export class TimelineDataState {
|
||||
timeline_items: ClipUI[];
|
||||
scrubber_frame_position: number;
|
||||
constructor(
|
||||
timeline_items: ClipUI[] = [],
|
||||
scrubber_frame_position: number = 0,
|
||||
) {
|
||||
this.timeline_items = timeline_items;
|
||||
this.scrubber_frame_position = scrubber_frame_position;
|
||||
}
|
||||
}
|
||||
import { GenerationOptions } from "~/pages/PageEnigma/models/generationOptions";
|
||||
import { Vector3 } from "three";
|
||||
|
||||
export class TimeLine {
|
||||
editorEngine: Editor;
|
||||
@@ -56,7 +47,7 @@ export class TimeLine {
|
||||
characters: { [key: string]: ClipGroup };
|
||||
|
||||
scene: Scene;
|
||||
camera: THREE.Camera;
|
||||
camera: THREE.Camera | null;
|
||||
mouse: THREE.Vector2 | undefined;
|
||||
|
||||
current_time: number;
|
||||
@@ -71,7 +62,7 @@ export class TimeLine {
|
||||
animation_engine: AnimationEngine,
|
||||
emotion_engine: EmotionEngine,
|
||||
scene: Scene,
|
||||
camera: THREE.Camera,
|
||||
camera: THREE.Camera | null,
|
||||
mouse: THREE.Vector2 | undefined,
|
||||
camera_name: string,
|
||||
) {
|
||||
@@ -103,7 +94,6 @@ export class TimeLine {
|
||||
this.current_time = 0;
|
||||
|
||||
this.camera_name = camera_name;
|
||||
|
||||
}
|
||||
|
||||
public async updateUI() {
|
||||
@@ -114,7 +104,7 @@ export class TimeLine {
|
||||
});
|
||||
}
|
||||
|
||||
public async pushEvent(action: fromEngineActions, data: any) {
|
||||
public async pushEvent(action: fromEngineActions, data: UnionedDataTypes) {
|
||||
//this.current_time += 0.75;
|
||||
Queue.publish({
|
||||
queueName: QueueNames.FROM_ENGINE,
|
||||
@@ -123,47 +113,50 @@ export class TimeLine {
|
||||
});
|
||||
}
|
||||
|
||||
public async handleTimelineActions(data: any) {
|
||||
const action = data["action"];
|
||||
public async handleTimelineActions(data: {
|
||||
action: UnionedActionTypes;
|
||||
data: UnionedDataTypes;
|
||||
}) {
|
||||
const action = data.action;
|
||||
switch (action) {
|
||||
case toEngineActions.ADD_KEYFRAME:
|
||||
await this.addKeyFrame(data);
|
||||
await this.addKeyFrame(data.data as Keyframe);
|
||||
break;
|
||||
case toEngineActions.UPDATE_KEYFRAME:
|
||||
await this.updateKeyFrame(data);
|
||||
await this.updateKeyFrame(data.data as Keyframe);
|
||||
break;
|
||||
case toEngineActions.DELETE_KEYFRAME:
|
||||
await this.deleteKeyFrame(data);
|
||||
await this.deleteKeyFrame(data.data as Keyframe);
|
||||
break;
|
||||
case toEngineActions.ADD_CLIP:
|
||||
await this.addClip(data);
|
||||
await this.addClip(data.data as ClipUI);
|
||||
break;
|
||||
case toEngineActions.DELETE_CLIP:
|
||||
await this.deleteClip(data);
|
||||
await this.deleteClip(data.data as ClipUI);
|
||||
break;
|
||||
case toEngineActions.UPDATE_CLIP:
|
||||
await this.updateClip(data);
|
||||
await this.updateClip(data.data as ClipUI);
|
||||
break;
|
||||
case toEngineActions.UPDATE_TIME:
|
||||
await this.scrub(data);
|
||||
await this.scrub(data.data as UpdateTime);
|
||||
break;
|
||||
case toEngineActions.MUTE:
|
||||
await this.mute(data, false);
|
||||
await this.mute(data.data as ClipUI, false);
|
||||
break;
|
||||
case toEngineActions.UNMUTE:
|
||||
await this.mute(data, true);
|
||||
await this.mute(data.data as ClipUI, true);
|
||||
break;
|
||||
case toEngineActions.ADD_CHARACTER:
|
||||
this.addCharacter(data);
|
||||
this.addCharacter(data.data as MediaItem);
|
||||
break;
|
||||
case toEngineActions.ADD_OBJECT: {
|
||||
const newObject = await this.addObject(data);
|
||||
this.queueNewObjectMessage(newObject, data);
|
||||
const newObject = await this.addObject(data.data as MediaItem);
|
||||
this.queueNewObjectMessage(newObject, data.data as MediaItem);
|
||||
break;
|
||||
}
|
||||
case toEngineActions.ADD_SHAPE: {
|
||||
const newShape = await this.addShape(data);
|
||||
this.queueNewObjectMessage(newShape, data);
|
||||
const newShape = await this.addShape(data.data as MediaItem);
|
||||
this.queueNewObjectMessage(newShape, data.data as MediaItem);
|
||||
break;
|
||||
}
|
||||
case toEngineActions.ENTER_PREVIEW_STATE:
|
||||
@@ -175,21 +168,22 @@ export class TimeLine {
|
||||
case toEngineActions.TOGGLE_CAMERA_STATE:
|
||||
this.editorEngine.switchCameraView();
|
||||
break;
|
||||
case toEngineActions.GENERATE_VIDEO:
|
||||
const options = data["data"]; // super overloaded talk to the devs about this. TODO... refactor
|
||||
this.editorEngine.generation_options = options;
|
||||
case toEngineActions.GENERATE_VIDEO: {
|
||||
const options = data.data; // super overloaded talk to the devs about this. TODO... refactor
|
||||
this.editorEngine.generation_options = options as GenerationOptions;
|
||||
this.editorEngine.generateVideo();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
console.log("Action Not Wired", action);
|
||||
}
|
||||
}
|
||||
|
||||
public async addCharacter(data: { data: MediaItem }) {
|
||||
const media_id = data.data.media_id;
|
||||
const name = data.data.name;
|
||||
public async addCharacter(data: MediaItem) {
|
||||
const media_id = data.media_id;
|
||||
const name = data.name;
|
||||
const pos = this.getPos();
|
||||
const new_data = { ...data.data };
|
||||
const new_data = { ...data };
|
||||
|
||||
const obj = await this.scene.loadGlbWithPlaceholder(
|
||||
media_id,
|
||||
@@ -214,7 +208,7 @@ export class TimeLine {
|
||||
|
||||
this.addPlayableClip(
|
||||
new ClipUI(
|
||||
data.data["version"],
|
||||
data.version,
|
||||
ClipType.FAKE,
|
||||
ClipGroup.CHARACTER,
|
||||
"Default",
|
||||
@@ -297,10 +291,10 @@ export class TimeLine {
|
||||
return new THREE.Vector3(0, 0, 0);
|
||||
}
|
||||
|
||||
public async addObject(data: { data: MediaItem }) {
|
||||
public async addObject(data: MediaItem) {
|
||||
const pos = this.getPos();
|
||||
const media_id = data.data.media_id;
|
||||
const name = data.data.name;
|
||||
const media_id = data.media_id;
|
||||
const name = data.name;
|
||||
const obj = await this.scene.loadGlbWithPlaceholder(
|
||||
media_id,
|
||||
name,
|
||||
@@ -313,12 +307,12 @@ export class TimeLine {
|
||||
return obj;
|
||||
}
|
||||
|
||||
public async addShape({ data }: { data: MediaItem }) {
|
||||
public async addShape(data: MediaItem) {
|
||||
const pos = this.getPos();
|
||||
return this.editorEngine.create_parim(data.media_id, pos);
|
||||
}
|
||||
|
||||
public async addKeyFrame(data: any) {
|
||||
public async addKeyFrame(data: Keyframe) {
|
||||
// KeyFrame Object
|
||||
// version: number;
|
||||
// clip_uuid: string;
|
||||
@@ -329,9 +323,9 @@ export class TimeLine {
|
||||
// rotation: XYZ;
|
||||
// scale: XYZ;
|
||||
// selected?: boolean;
|
||||
const data_json = data["data"];
|
||||
const uuid = data_json["object_uuid"];
|
||||
const keyframe_uuid = data_json["keyframe_uuid"];
|
||||
const data_json = data;
|
||||
const uuid = data_json.object_uuid;
|
||||
const keyframe_uuid = data_json.keyframe_uuid;
|
||||
|
||||
let object_name = this.scene.get_object_by_uuid(uuid)?.name;
|
||||
if (object_name === undefined) {
|
||||
@@ -340,12 +334,12 @@ export class TimeLine {
|
||||
|
||||
this.transform_engine.addFrame(
|
||||
uuid,
|
||||
data_json["offset"],
|
||||
data_json["position"],
|
||||
data_json["rotation"],
|
||||
data_json["scale"],
|
||||
data_json["offset"],
|
||||
data_json["keyframe_uuid"],
|
||||
data_json.offset,
|
||||
data_json.position as Vector3,
|
||||
data_json.rotation as Vector3,
|
||||
data_json.scale as Vector3,
|
||||
data_json.offset,
|
||||
data_json.keyframe_uuid,
|
||||
);
|
||||
|
||||
await this.addPlayableClip(
|
||||
@@ -365,10 +359,10 @@ export class TimeLine {
|
||||
);
|
||||
|
||||
const point = this.scene.createPoint(
|
||||
data_json["position"],
|
||||
data_json["rotation"],
|
||||
data_json["scale"],
|
||||
data_json["keyframe_uuid"],
|
||||
data_json.position as Vector3,
|
||||
data_json.rotation as Vector3,
|
||||
data_json.scale as Vector3,
|
||||
data_json.keyframe_uuid,
|
||||
);
|
||||
if (this.editorEngine.camera_person_mode) {
|
||||
point.visible = false;
|
||||
@@ -377,11 +371,7 @@ export class TimeLine {
|
||||
}
|
||||
|
||||
public checkEditorCanPlay() {
|
||||
if (this.getEndPoint() <= 1) {
|
||||
this.editorEngine.can_playback = false;
|
||||
} else {
|
||||
this.editorEngine.can_playback = true;
|
||||
}
|
||||
this.editorEngine.can_playback = this.getEndPoint() > 1;
|
||||
this.editorEngine.updateSelectedUI();
|
||||
}
|
||||
|
||||
@@ -405,28 +395,25 @@ export class TimeLine {
|
||||
// Update react land here.
|
||||
}
|
||||
|
||||
public async addClip(data: any) {
|
||||
const object_uuid = data["data"]["object_uuid"];
|
||||
const media_id = data["data"]["media_id"];
|
||||
const name = data["data"]["name"];
|
||||
const group = data["data"]["group"];
|
||||
const version = data["data"]["group"];
|
||||
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"];
|
||||
|
||||
if (object_name === undefined) {
|
||||
object_name = "Undefined.";
|
||||
}
|
||||
public async addClip(data: ClipUI) {
|
||||
const object_uuid = data.object_uuid;
|
||||
const media_id = data.media_id;
|
||||
const name = data.name;
|
||||
const group = data.group;
|
||||
const version = 1;
|
||||
const type = data.type;
|
||||
const offset = data.offset;
|
||||
const end_offset = data.length + offset;
|
||||
const object_name =
|
||||
this.scene.get_object_by_uuid(object_uuid)?.name ?? "undefined";
|
||||
const clip_uuid = data.clip_uuid;
|
||||
|
||||
switch (type) {
|
||||
case "animation":
|
||||
this.animation_engine.load_object(object_uuid, media_id, name);
|
||||
break;
|
||||
case "transform":
|
||||
this.transform_engine.loadObject(object_uuid, data["data"]["length"]);
|
||||
this.transform_engine.loadObject(object_uuid, data.length);
|
||||
break;
|
||||
case "expression":
|
||||
this.emotion_engine.loadClip(object_uuid, media_id);
|
||||
@@ -475,9 +462,9 @@ export class TimeLine {
|
||||
this.checkEditorCanPlay();
|
||||
}
|
||||
|
||||
public async deleteKeyFrame(data: any) {
|
||||
const keyframe_uuid = data["data"]["keyframe_uuid"];
|
||||
const object_uuid = data["data"]["object_uuid"];
|
||||
public async deleteKeyFrame(data: Keyframe) {
|
||||
const keyframe_uuid = data.keyframe_uuid;
|
||||
const object_uuid = data.object_uuid;
|
||||
this.transform_engine.clips[object_uuid].removeKeyframe(keyframe_uuid);
|
||||
this.scene.deletePoint(keyframe_uuid);
|
||||
for (const element of this.timeline_items) {
|
||||
@@ -498,14 +485,14 @@ export class TimeLine {
|
||||
this.checkEditorCanPlay();
|
||||
}
|
||||
|
||||
public async updateKeyFrame(data: any) {
|
||||
const keyframe_uuid = data["data"]["keyframe_uuid"];
|
||||
const keyframe_offset = data["data"]["offset"];
|
||||
const object_uuid = data["data"]["object_uuid"];
|
||||
public async updateKeyFrame(data: Keyframe) {
|
||||
const keyframe_uuid = data.keyframe_uuid;
|
||||
const keyframe_offset = data.offset;
|
||||
const object_uuid = data.object_uuid;
|
||||
|
||||
const keyframe_pos = data["data"]["position"];
|
||||
const keyframe_rot = data["data"]["rotation"];
|
||||
const keyframe_scl = data["data"]["scale"];
|
||||
const keyframe_pos = data.position;
|
||||
const keyframe_rot = data.rotation;
|
||||
const keyframe_scl = data.scale;
|
||||
|
||||
this.transform_engine.clips[object_uuid].setOffset(
|
||||
keyframe_uuid,
|
||||
@@ -513,26 +500,26 @@ export class TimeLine {
|
||||
);
|
||||
this.transform_engine.clips[object_uuid].setTransform(
|
||||
keyframe_uuid,
|
||||
keyframe_pos,
|
||||
keyframe_rot,
|
||||
keyframe_scl,
|
||||
keyframe_pos as Vector3,
|
||||
keyframe_rot as Vector3,
|
||||
keyframe_scl as Vector3,
|
||||
);
|
||||
this.scene.updatePoint(
|
||||
keyframe_uuid,
|
||||
keyframe_pos,
|
||||
keyframe_rot,
|
||||
keyframe_scl,
|
||||
keyframe_pos as Vector3,
|
||||
keyframe_rot as Vector3,
|
||||
keyframe_scl as Vector3,
|
||||
);
|
||||
this.checkEditorCanPlay();
|
||||
}
|
||||
|
||||
public async updateClip(data: any) {
|
||||
public async updateClip(data: ClipUI) {
|
||||
// only length and offset changes here.
|
||||
const object_uuid = data["data"]["object_uuid"];
|
||||
const media_id = data["data"]["media_id"];
|
||||
const offset = data["data"]["offset"];
|
||||
const length = data["data"]["length"] + offset;
|
||||
const clip_uuid = data["data"]["clip_uuid"];
|
||||
const object_uuid = data.object_uuid;
|
||||
const media_id = data.media_id;
|
||||
const offset = data.offset;
|
||||
const length = data.length + offset;
|
||||
const clip_uuid = data.clip_uuid;
|
||||
|
||||
for (const element of this.timeline_items) {
|
||||
if (
|
||||
@@ -547,12 +534,10 @@ export class TimeLine {
|
||||
this.checkEditorCanPlay();
|
||||
}
|
||||
|
||||
public async deleteClip(data: any) {
|
||||
//const json_data = data["data"];
|
||||
const object_uuid = data["data"]["object_uuid"];
|
||||
const media_id = data["data"]["media_id"];
|
||||
//const type = data["type"];
|
||||
const clip_uuid = data["data"]["clip_uuid"];
|
||||
public async deleteClip(data: ClipUI) {
|
||||
const object_uuid = data.object_uuid;
|
||||
const media_id = data.media_id;
|
||||
const clip_uuid = data.clip_uuid;
|
||||
|
||||
for (let i = 0; i < this.timeline_items.length; i++) {
|
||||
const element = this.timeline_items[i];
|
||||
@@ -569,13 +554,9 @@ export class TimeLine {
|
||||
this.checkEditorCanPlay();
|
||||
}
|
||||
|
||||
public async scrubberUpdate(data: any) {
|
||||
console.log(data);
|
||||
}
|
||||
|
||||
public async mute(data: any, isMute: boolean) {
|
||||
public async mute(data: ClipUI, isMute: boolean) {
|
||||
this.timeline_items.forEach((element) => {
|
||||
if (element.group === data.data["group"]) {
|
||||
if (element.group === data.group) {
|
||||
element.should_play = isMute;
|
||||
}
|
||||
});
|
||||
@@ -585,28 +566,20 @@ export class TimeLine {
|
||||
this.timeline_items.push(clip);
|
||||
}
|
||||
|
||||
public async scrub(data: any): Promise<void> {
|
||||
public async scrub(data: UpdateTime): Promise<void> {
|
||||
if (this.is_playing) {
|
||||
return;
|
||||
}
|
||||
const value = Math.floor(data["data"]["currentTime"]);
|
||||
this.setScrubberPosition(value);
|
||||
const value = Math.floor(data.currentTime);
|
||||
await this.setScrubberPosition(value);
|
||||
this.current_time = value;
|
||||
this.update();
|
||||
await this.update();
|
||||
|
||||
if (this.editorEngine.switchPreviewToggle) {
|
||||
this.editorEngine.generateFrame();
|
||||
await this.editorEngine.generateFrame();
|
||||
}
|
||||
}
|
||||
|
||||
public async stepFrame(frames: number) {
|
||||
this.update();
|
||||
this.scrubber_frame_position += frames;
|
||||
this.pushEvent(fromEngineActions.UPDATE_TIME, {
|
||||
currentTime: this.scrubber_frame_position,
|
||||
});
|
||||
}
|
||||
|
||||
// public streaming events into the timeline from
|
||||
public async setScrubberPosition(offset: number) {
|
||||
this.scrubber_frame_position = offset; // in ms
|
||||
@@ -696,7 +669,7 @@ export class TimeLine {
|
||||
if (
|
||||
element.offset <= this.scrubber_frame_position &&
|
||||
this.scrubber_frame_position <= element.length &&
|
||||
element.should_play === true
|
||||
element.should_play
|
||||
) {
|
||||
// run async
|
||||
// element.play()
|
||||
@@ -750,8 +723,12 @@ export class TimeLine {
|
||||
object.uuid + element.media_id
|
||||
].play(object);
|
||||
const fps = 60;
|
||||
await this.animation_engine.clips[object.uuid + element.media_id].step(
|
||||
(this.scrubber_frame_position-element.offset) / fps, this.is_playing, this.scrubber_frame_position// Double FPS for best result.
|
||||
await this.animation_engine.clips[
|
||||
object.uuid + element.media_id
|
||||
].step(
|
||||
(this.scrubber_frame_position - element.offset) / fps,
|
||||
this.is_playing,
|
||||
this.scrubber_frame_position, // Double FPS for best result.
|
||||
);
|
||||
//this.animation_engine.clips[object.uuid + element.media_id].update_bones();
|
||||
}
|
||||
File renamed without changes.
@@ -0,0 +1,301 @@
|
||||
import Editor from "./editor";
|
||||
import { EditorStates } from "~/pages/PageEnigma/enums";
|
||||
import { editorState, previewSrc } from "../signals/engine";
|
||||
import Queue from "~/pages/PageEnigma/Queue/Queue";
|
||||
import { QueueNames } from "~/pages/PageEnigma/Queue/QueueNames";
|
||||
import { fromEngineActions } from "~/pages/PageEnigma/Queue/fromEngineActions";
|
||||
import { ToastTypes } from "~/enums";
|
||||
import { createFFmpeg, fetchFile, FFmpeg } from "@ffmpeg/ffmpeg";
|
||||
import { ClipUI } from "../datastructures/clips/clip_ui.js";
|
||||
import { ClipType } from "~/pages/PageEnigma/enums";
|
||||
import { Visibility } from "./api_manager.js";
|
||||
import * as THREE from "three";
|
||||
import { getSceneSignals } from "~/signals";
|
||||
|
||||
export class VideoGeneration {
|
||||
editor: Editor;
|
||||
|
||||
constructor(editor: Editor) {
|
||||
this.editor = editor;
|
||||
}
|
||||
|
||||
async generateFrame() {
|
||||
if (!this.editor.generating_preview && this.editor.rawRenderer) {
|
||||
this.editor.generating_preview = true;
|
||||
this.editor.removeTransformControls();
|
||||
this.editor.activeScene.renderMode(true);
|
||||
if (this.editor.activeScene.hot_items) {
|
||||
this.editor.activeScene.hot_items.forEach((element) => {
|
||||
element.visible = false;
|
||||
});
|
||||
}
|
||||
|
||||
// if (this.editor.render_camera && this.editor.cam_obj) {
|
||||
// this.editor.render_camera.position.copy(this.editor.cam_obj.position);
|
||||
// this.editor.render_camera.rotation.copy(this.editor.cam_obj.rotation);
|
||||
// }
|
||||
|
||||
previewSrc.value = "";
|
||||
|
||||
this.editor.rawRenderer.setSize(
|
||||
this.editor.render_width,
|
||||
this.editor.render_height,
|
||||
);
|
||||
this.editor.render_camera.aspect =
|
||||
this.editor.render_width / this.editor.render_height;
|
||||
this.editor.render_camera.updateProjectionMatrix();
|
||||
this.editor.rawRenderer.render(
|
||||
this.editor.activeScene.scene,
|
||||
this.editor.render_camera,
|
||||
);
|
||||
const imgData = this.editor.rawRenderer.domElement.toDataURL();
|
||||
const response = await fetch(imgData); // Fetch the data URL
|
||||
const blob = await response.blob(); // Convert to Blob
|
||||
|
||||
if (!this.editor.camera_person_mode) {
|
||||
this.editor.switchCameraView();
|
||||
editorState.value = EditorStates.PREVIEW;
|
||||
}
|
||||
|
||||
this.editor.generating_preview = false;
|
||||
|
||||
try {
|
||||
const url = await this.editor.api_manager.uploadMediaFrameGeneration(
|
||||
blob,
|
||||
"render.png",
|
||||
this.editor.art_style,
|
||||
this.editor.positive_prompt,
|
||||
this.editor.negative_prompt,
|
||||
);
|
||||
|
||||
previewSrc.value = url;
|
||||
return Promise.resolve(url);
|
||||
} catch (err: any) {
|
||||
Queue.publish({
|
||||
queueName: QueueNames.FROM_ENGINE,
|
||||
action: fromEngineActions.POP_A_TOAST,
|
||||
data: {
|
||||
type: ToastTypes.ERROR,
|
||||
message: err.message,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async convertAudioClip(itteration: number, ffmpeg: FFmpeg, clip: ClipUI) {
|
||||
// Base audio clip names.
|
||||
const video_og: string = itteration + "tmp.mp4";
|
||||
const wav_name: string = itteration + "tmp.wav";
|
||||
const new_video: string = itteration + 1 + "tmp.mp4";
|
||||
let startFrame: number = clip.offset;
|
||||
let endFrame: number = clip.length;
|
||||
|
||||
if (endFrame > this.editor.timeline.timeline_limit) {
|
||||
endFrame = this.editor.timeline.timeline_limit;
|
||||
}
|
||||
if (startFrame > this.editor.timeline.timeline_limit) {
|
||||
startFrame = this.editor.timeline.timeline_limit - 1;
|
||||
}
|
||||
|
||||
const startTime: number = startFrame / this.editor.cap_fps;
|
||||
const endTime: number = endFrame / this.editor.cap_fps;
|
||||
const end: number = endTime - startTime;
|
||||
|
||||
// Use ffmpeg to convert audio to video.
|
||||
const audioSegment: string = "as_" + wav_name;
|
||||
await ffmpeg.FS(
|
||||
"writeFile",
|
||||
wav_name,
|
||||
await fetchFile(
|
||||
await this.editor.api_manager.getMediaFile(clip.media_id),
|
||||
),
|
||||
);
|
||||
await ffmpeg.run(
|
||||
"-i",
|
||||
wav_name,
|
||||
"-ss",
|
||||
"0",
|
||||
"-to",
|
||||
"" + end,
|
||||
"-max_muxing_queue_size",
|
||||
"999999",
|
||||
audioSegment,
|
||||
);
|
||||
|
||||
await ffmpeg.run(
|
||||
"-i",
|
||||
video_og,
|
||||
"-max_muxing_queue_size",
|
||||
"999999",
|
||||
`${itteration}empty_tmp.wav`,
|
||||
);
|
||||
|
||||
await ffmpeg.run(
|
||||
"-i",
|
||||
`${itteration}empty_tmp.wav`,
|
||||
"-i",
|
||||
audioSegment,
|
||||
"-filter_complex",
|
||||
"[1:a]adelay=" +
|
||||
startTime * 1000 +
|
||||
"|" +
|
||||
startTime * 1000 +
|
||||
"[a1];[0:a][a1]amix=inputs=2[a]",
|
||||
"-map",
|
||||
"[a]",
|
||||
`${itteration}final_tmp.wav`,
|
||||
);
|
||||
|
||||
// Combine final audio with video.
|
||||
await ffmpeg.run(
|
||||
"-i",
|
||||
video_og,
|
||||
"-i",
|
||||
`${itteration}final_tmp.wav`,
|
||||
"-c:v",
|
||||
"copy",
|
||||
"-c:a",
|
||||
"aac",
|
||||
"-map",
|
||||
"0:v:0",
|
||||
"-map",
|
||||
"1:a:0",
|
||||
"-strict",
|
||||
"experimental",
|
||||
new_video,
|
||||
);
|
||||
}
|
||||
|
||||
async stopPlaybackAndUploadVideo(compile_audio: boolean = true) {
|
||||
this.editor.rendering = false;
|
||||
|
||||
//const videoBlob = new Blob(this.frame_buffer, { type: "video/webm" });
|
||||
//const videoURL = URL.createObjectURL(videoBlob);
|
||||
|
||||
this.editor.generating_preview = true;
|
||||
const ffmpeg = createFFmpeg({ log: false });
|
||||
await ffmpeg.load();
|
||||
|
||||
this.editor.updateLoad(50, "Processing ...");
|
||||
|
||||
// Write the Uint8Array to the FFmpeg file system
|
||||
//ffmpeg.FS("writeFile", "input.webm", await fetchFile(videoURL));
|
||||
|
||||
for (let index = 0; index < this.editor.frame_buffer.length; index++) {
|
||||
const element = this.editor.frame_buffer[index];
|
||||
await ffmpeg.FS(
|
||||
"writeFile",
|
||||
`image${index}.png`,
|
||||
await fetchFile(element),
|
||||
);
|
||||
}
|
||||
|
||||
await ffmpeg.run(
|
||||
"-framerate",
|
||||
"" + this.editor.cap_fps,
|
||||
"-i",
|
||||
"image%d.png",
|
||||
"input.mp4",
|
||||
);
|
||||
|
||||
await ffmpeg.run(
|
||||
"-i",
|
||||
"input.mp4",
|
||||
"-f",
|
||||
"lavfi",
|
||||
"-i",
|
||||
"anullsrc", // This adds a silent audio track
|
||||
"-max_muxing_queue_size",
|
||||
"999999",
|
||||
"-vf",
|
||||
"select=gte(n\\,1),scale=1024:576", // scale=1024:576
|
||||
"-c:v",
|
||||
"libx264", // Specify video codec (optional, but recommended for MP4)
|
||||
"-c:a",
|
||||
"aac", // Specify audio codec (optional, but recommended for MP4)
|
||||
"-shortest", // Ensure output duration matches the shortest stream (video or audio)
|
||||
"-pix_fmt",
|
||||
"yuv420p",
|
||||
"-f",
|
||||
"mp4",
|
||||
"0tmp.mp4",
|
||||
);
|
||||
|
||||
let itteration = 0;
|
||||
|
||||
if (compile_audio) {
|
||||
for (const clip of this.editor.timeline.timeline_items) {
|
||||
if (clip.type == ClipType.AUDIO) {
|
||||
await this.convertAudioClip(itteration, ffmpeg, clip);
|
||||
itteration += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const output = ffmpeg.FS("readFile", itteration + "tmp.mp4");
|
||||
|
||||
ffmpeg.exit();
|
||||
this.editor.generating_preview = false;
|
||||
|
||||
// Create a Blob from the output file for downloading
|
||||
const blob = new Blob([output.buffer], { type: "video/mp4" });
|
||||
|
||||
const title = getSceneSignals().title || "Untitled";
|
||||
|
||||
const style_name = this.editor.art_style.toString();
|
||||
const media_token = this.editor.current_scene_media_token || undefined;
|
||||
|
||||
const data: any = await this.editor.api_manager.uploadMedia({
|
||||
blob,
|
||||
fileName: `${title}.mp4`,
|
||||
title,
|
||||
styleName: style_name,
|
||||
maybe_scene_source_media_file_token: media_token,
|
||||
});
|
||||
|
||||
if (data == null) {
|
||||
return;
|
||||
}
|
||||
const upload_token = data["media_file_token"];
|
||||
|
||||
const result = await this.editor.api_manager
|
||||
.stylizeVideo(
|
||||
upload_token,
|
||||
this.editor.art_style,
|
||||
this.editor.positive_prompt,
|
||||
this.editor.negative_prompt,
|
||||
Visibility.Public,
|
||||
this.editor.generation_options.faceDetail,
|
||||
this.editor.generation_options.upscale,
|
||||
this.editor.generation_options.styleStrength,
|
||||
)
|
||||
.catch((error) => {
|
||||
// TODO handle stylize error.
|
||||
console.log(error);
|
||||
});
|
||||
|
||||
// {"success":true,"inference_job_token":"jinf_j3nbqbd15wqxb0xcks13qh3f3bz"}
|
||||
this.editor.updateLoad(100, "Done Check Your Media Tab On Profile.");
|
||||
this.editor.endLoading();
|
||||
|
||||
this.editor.recorder = undefined;
|
||||
if (this.editor.rawRenderer) {
|
||||
this.editor.rawRenderer.setSize(
|
||||
this.editor.startRenderWidth,
|
||||
this.editor.startRenderHeight,
|
||||
);
|
||||
}
|
||||
|
||||
this.editor.canvasRenderCamReference =
|
||||
document.getElementById("camera-view");
|
||||
this.editor.rawRenderer = new THREE.WebGLRenderer({
|
||||
antialias: false,
|
||||
canvas: this.editor.canvasRenderCamReference,
|
||||
preserveDrawingBuffer: true,
|
||||
});
|
||||
this.editor.activeScene.renderMode(false);
|
||||
|
||||
this.editor.switchEdit();
|
||||
}
|
||||
}
|
||||
@@ -18,12 +18,12 @@ export type ToastDataType = {
|
||||
message: string;
|
||||
};
|
||||
|
||||
type UnionedActionTypes =
|
||||
export type UnionedActionTypes =
|
||||
| fromEngineActions
|
||||
| toEngineActions
|
||||
| toTimelineActions;
|
||||
|
||||
type UnionedDataTypes =
|
||||
export type UnionedDataTypes =
|
||||
| QueueClip
|
||||
| UpdateTime
|
||||
| QueueKeyframe
|
||||
|
||||
@@ -65,8 +65,6 @@ export const ControlPanelSceneObject = () => {
|
||||
|
||||
const [color, setColor] = useState("#ffffff");
|
||||
|
||||
console.log("color", color);
|
||||
|
||||
const colorInputId = useId();
|
||||
|
||||
const toggleCollapse = () => {
|
||||
@@ -111,8 +109,6 @@ export const ControlPanelSceneObject = () => {
|
||||
return;
|
||||
}
|
||||
|
||||
console.log("44");
|
||||
|
||||
const vectors = currentSceneObject.objectVectors;
|
||||
|
||||
// local state relies on strings
|
||||
@@ -179,7 +175,12 @@ export const ControlPanelSceneObject = () => {
|
||||
|
||||
const handleOnAddKeyFrame = () => {
|
||||
if (currentSceneObject) {
|
||||
if (position == null || rotation == null || scale == null) {
|
||||
if (
|
||||
position == null ||
|
||||
rotation == null ||
|
||||
scale == null ||
|
||||
currentSceneObject == null
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { twMerge } from "tailwind-merge";
|
||||
import { ArtStyle } from "~/pages/PageEnigma/js/api_manager";
|
||||
import { ArtStyle } from "~/pages/PageEnigma/Editor/api_manager";
|
||||
import { H4 } from "~/components";
|
||||
import { ImgHTMLAttributes } from "react";
|
||||
|
||||
@@ -37,7 +37,8 @@ export const ItemPicker = ({
|
||||
maxWidth: (width as number) + 4,
|
||||
maxHeight: (height as number) + 4,
|
||||
}}
|
||||
onClick={handleSelected}>
|
||||
onClick={handleSelected}
|
||||
>
|
||||
<img
|
||||
className="object-fill"
|
||||
{...imgProps}
|
||||
@@ -58,7 +59,8 @@ export const ItemPicker = ({
|
||||
viewBox="0 0 512 512"
|
||||
className={`absolute right-[5px] top-[5px] h-[18px] w-[18px] shadow-xl transition-opacity duration-200 ease-in-out ${
|
||||
selected ? "opacity-100" : "opacity-0"
|
||||
}`}>
|
||||
}`}
|
||||
>
|
||||
<path
|
||||
opacity="1"
|
||||
d="M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512zM369 209L241 337c-9.4 9.4-24.6 9.4-33.9 0l-64-64c-9.4-9.4-9.4-24.6 0-33.9s24.6-9.4 33.9 0l47 47L335 175c-9.4-9.4 24.6-9.4 33.9 0s9.4 24.6 0 33.9z"
|
||||
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
RandomTextsPositive,
|
||||
RandomTextsNegative,
|
||||
} from "~/pages/PageEnigma/constants/RandomTexts";
|
||||
import { ArtStyle } from "~/pages/PageEnigma/js/api_manager";
|
||||
import { ArtStyle } from "~/pages/PageEnigma/Editor/api_manager";
|
||||
|
||||
interface Props {
|
||||
selection: ArtStyle;
|
||||
|
||||
@@ -3,7 +3,7 @@ import { useContext, useState } from "react";
|
||||
import { ButtonIcon } from "~/components";
|
||||
|
||||
import { EngineContext } from "~/pages/PageEnigma/contexts/EngineContext";
|
||||
import { ArtStyle } from "~/pages/PageEnigma/js/api_manager";
|
||||
import { ArtStyle } from "~/pages/PageEnigma/Editor/api_manager";
|
||||
import { faAngleLeft, faAngleRight } from "@fortawesome/pro-solid-svg-icons";
|
||||
import { styleList } from "~/pages/PageEnigma/styleList";
|
||||
import { sidePanelHeight, sidePanelWidth } from "~/pages/PageEnigma/signals";
|
||||
|
||||
@@ -5,7 +5,7 @@ import { StyleButtons } from "./StyleButtons";
|
||||
import { sidePanelHeight } from "~/pages/PageEnigma/signals";
|
||||
import { useSignals } from "@preact/signals-react/runtime";
|
||||
import { useState } from "react";
|
||||
import { ArtStyle } from "~/pages/PageEnigma/js/api_manager";
|
||||
import { ArtStyle } from "~/pages/PageEnigma/Editor/api_manager";
|
||||
import { styleList } from "~/pages/PageEnigma/styleList";
|
||||
|
||||
export function StylizeTab() {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { twMerge } from "tailwind-merge";
|
||||
import { H4 } from "~/components";
|
||||
import { ArtStyle } from "~/pages/PageEnigma/js/api_manager";
|
||||
import { ArtStyle } from "~/pages/PageEnigma/Editor/api_manager";
|
||||
|
||||
interface ItemPickerProps extends React.ImgHTMLAttributes<HTMLImageElement> {
|
||||
label: string;
|
||||
|
||||
@@ -4,7 +4,7 @@ import { faAngleLeft, faAngleRight } from "@fortawesome/pro-solid-svg-icons";
|
||||
import { EngineContext } from "~/pages/PageEnigma/contexts/EngineContext";
|
||||
|
||||
import { styleList } from "~/pages/PageEnigma/styleList";
|
||||
import { ArtStyle } from "~/pages/PageEnigma/js/api_manager";
|
||||
import { ArtStyle } from "~/pages/PageEnigma/Editor/api_manager";
|
||||
|
||||
import { ButtonIcon, Label, Textarea } from "~/components";
|
||||
import { ItemPicker } from "./ItemPicker";
|
||||
|
||||
@@ -20,6 +20,7 @@ import {
|
||||
QueueKeyframe,
|
||||
UpdateTime,
|
||||
} from "~/pages/PageEnigma/models";
|
||||
import { ClipGroup } from "~/pages/PageEnigma/enums";
|
||||
import { TrackContext } from "~/pages/PageEnigma/contexts/TrackContext/TrackContext";
|
||||
import { addToast } from "~/signals";
|
||||
import { ToastTypes } from "~/enums";
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ArtStyle } from "~/pages/PageEnigma/js/api_manager";
|
||||
import { ArtStyle } from "~/pages/PageEnigma/Editor/api_manager";
|
||||
|
||||
export const RandomTextsPositive: Record<ArtStyle, string[]> = {
|
||||
[ArtStyle.AnimeStandard]: [
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { createContext } from "react";
|
||||
import Editor from "~/pages/PageEnigma/js/editor";
|
||||
import Editor from "~/pages/PageEnigma/Editor/editor";
|
||||
|
||||
export const EngineContext = createContext<Editor|null>(null);
|
||||
export const EngineContext = createContext<Editor | null>(null);
|
||||
@@ -1,7 +1,7 @@
|
||||
import { ReactNode, useEffect, useState } from "react";
|
||||
import { EngineContext } from "./EngineContext";
|
||||
|
||||
import Editor from "~/pages/PageEnigma/js/editor";
|
||||
import Editor from "~/pages/PageEnigma/Editor/editor";
|
||||
|
||||
interface Props {
|
||||
sceneToken?: string;
|
||||
|
||||
@@ -2,7 +2,7 @@ import * as THREE from "three";
|
||||
import { GLTFLoader } from "three/addons/loaders/GLTFLoader.js";
|
||||
import { FBXLoader } from "three/examples/jsm/Addons.js";
|
||||
|
||||
import { MoveAIResult, Retarget } from "../../js/retargeting";
|
||||
import { MoveAIResult, Retarget } from "../../Editor/retargeting";
|
||||
import { environmentVariables } from "~/signals";
|
||||
|
||||
export class AnimationClip {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { LipSync } from "../../js/lipsync";
|
||||
import { LipSync } from "../../Editor/lipsync";
|
||||
import * as THREE from "three";
|
||||
import { environmentVariables } from "~/signals";
|
||||
interface AudioData {
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
export enum AssetFilterOption {
|
||||
ALL,
|
||||
MINE,
|
||||
BOOKMARKED,
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
export enum AssetType {
|
||||
ANIMATION = "animation",
|
||||
AUDIO = "audio",
|
||||
CAMERA = "camera",
|
||||
CHARACTER = "character",
|
||||
EXPRESSION = "expression",
|
||||
OBJECT = "object",
|
||||
SHAPE = "shape",
|
||||
STYLE = "style", // TODO Remove
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
import { UserDetailsLight } from "./voice";
|
||||
import { UserDetailsLight } from "./types";
|
||||
|
||||
import {
|
||||
MediaFileClass,
|
||||
MediaFileSubtype,
|
||||
|
||||
@@ -4,7 +4,7 @@ import {
|
||||
StoryTellerProxy3DObject,
|
||||
ObjectJSON,
|
||||
} from "./storyteller_proxy_3d_object";
|
||||
import Scene from "../js/scene";
|
||||
import Scene from "../Editor/scene";
|
||||
|
||||
interface LookUpDictionary {
|
||||
[key: string]: StoryTellerProxy3DObject;
|
||||
@@ -70,7 +70,9 @@ export class StoryTellerProxyScene {
|
||||
let obj;
|
||||
switch (token) {
|
||||
case "Parim":
|
||||
let prim_uuid = this.scene.instantiate(json_object.object_name).uuid;
|
||||
let prim_uuid = this.scene.instantiate(
|
||||
json_object.object_name,
|
||||
).uuid;
|
||||
obj = this.scene.get_object_by_uuid(prim_uuid);
|
||||
break;
|
||||
case "DirectionalLight":
|
||||
@@ -78,7 +80,10 @@ export class StoryTellerProxyScene {
|
||||
break;
|
||||
default:
|
||||
if (token.includes("m_")) {
|
||||
obj = await this.scene.loadGlbWithPlaceholder(token, json_object.object_name);
|
||||
obj = await this.scene.loadGlbWithPlaceholder(
|
||||
token,
|
||||
json_object.object_name,
|
||||
);
|
||||
} else if (token.includes("Point::")) {
|
||||
let keyframe_uuid = token.replace("Point::", "");
|
||||
obj = this.scene.createPoint(
|
||||
|
||||
@@ -4,151 +4,208 @@ import { ClipUI } from "../datastructures/clips/clip_ui";
|
||||
import { EmotionClip } from "../datastructures/clips/emotion_clip";
|
||||
import { LipSyncClip } from "../datastructures/clips/lipsync_clip";
|
||||
import { TransformClip } from "../datastructures/clips/transform_clip";
|
||||
import AnimationEngine from "../js/animation_engine";
|
||||
import AudioEngine from "../js/audio_engine";
|
||||
import EmotionEngine from "../js/emotion_engine";
|
||||
import LipSyncEngine from "../js/lip_sync_engine";
|
||||
import { TimeLine } from "../js/timeline";
|
||||
import TransformEngine from "../js/transform_engine";
|
||||
|
||||
import AnimationEngine from "../Editor/animation_engine";
|
||||
import AudioEngine from "../Editor/audio_engine";
|
||||
import EmotionEngine from "../Editor/emotion_engine";
|
||||
import LipSyncEngine from "../Editor/lip_sync_engine";
|
||||
import { TimeLine } from "../Editor/timeline";
|
||||
import TransformEngine from "../Editor/transform_engine";
|
||||
|
||||
export class StoryTellerProxyTimeline {
|
||||
timeline: TimeLine
|
||||
transform_engine: TransformEngine
|
||||
animation_engine: AnimationEngine
|
||||
audio_engine: AudioEngine
|
||||
lipsync_engine: LipSyncEngine
|
||||
emotion_engine: EmotionEngine
|
||||
timeline: TimeLine;
|
||||
transform_engine: TransformEngine;
|
||||
animation_engine: AnimationEngine;
|
||||
audio_engine: AudioEngine;
|
||||
lipsync_engine: LipSyncEngine;
|
||||
emotion_engine: EmotionEngine;
|
||||
|
||||
constructor(version: number, timeline: TimeLine, transform_engine: TransformEngine, animation_engine: AnimationEngine, audio_engine: AudioEngine, lipsync_engine: LipSyncEngine, emotion_engine: EmotionEngine) {
|
||||
this.timeline = timeline;
|
||||
this.transform_engine = transform_engine;
|
||||
this.animation_engine = animation_engine;
|
||||
this.audio_engine = audio_engine;
|
||||
this.lipsync_engine = lipsync_engine;
|
||||
this.emotion_engine = emotion_engine;
|
||||
constructor(
|
||||
version: number,
|
||||
timeline: TimeLine,
|
||||
transform_engine: TransformEngine,
|
||||
animation_engine: AnimationEngine,
|
||||
audio_engine: AudioEngine,
|
||||
lipsync_engine: LipSyncEngine,
|
||||
emotion_engine: EmotionEngine,
|
||||
) {
|
||||
this.timeline = timeline;
|
||||
this.transform_engine = transform_engine;
|
||||
this.animation_engine = animation_engine;
|
||||
this.audio_engine = audio_engine;
|
||||
this.lipsync_engine = lipsync_engine;
|
||||
this.emotion_engine = emotion_engine;
|
||||
}
|
||||
|
||||
private async getItems(items: any[]): Promise<any[]> {
|
||||
let timeline_items_data = [];
|
||||
for (let index = 0; index < items.length; index++) {
|
||||
const element = items[index];
|
||||
timeline_items_data.push(element.toJSON());
|
||||
}
|
||||
return timeline_items_data;
|
||||
}
|
||||
|
||||
private async getItems(items: any[]): Promise<any[]> {
|
||||
let timeline_items_data = [];
|
||||
for (let index = 0; index < items.length; index++) {
|
||||
const element = items[index];
|
||||
timeline_items_data.push(element.toJSON());
|
||||
};
|
||||
return timeline_items_data;
|
||||
private async getItemsDict(items: {
|
||||
[key: string]: any;
|
||||
}): Promise<{ [key: string]: any }> {
|
||||
let timeline_items_data: { [key: string]: any } = {};
|
||||
for (const key in items) {
|
||||
if (items.hasOwnProperty(key)) {
|
||||
const element = items[key];
|
||||
timeline_items_data[key] = element.toJSON();
|
||||
}
|
||||
}
|
||||
return timeline_items_data;
|
||||
}
|
||||
|
||||
private async getItemsDict(items: { [key: string]: any }): Promise<{ [key: string]: any }> {
|
||||
let timeline_items_data: { [key: string]: any } = {};
|
||||
for (const key in items) {
|
||||
if (items.hasOwnProperty(key)) {
|
||||
const element = items[key];
|
||||
timeline_items_data[key] = element.toJSON();
|
||||
}
|
||||
}
|
||||
return timeline_items_data;
|
||||
public async saveToJson(): Promise<any> {
|
||||
let timeline_json = {
|
||||
timeline: await this.getItems(this.timeline.timeline_items),
|
||||
transform: await this.getItemsDict(this.transform_engine.clips),
|
||||
animation: await this.getItemsDict(this.animation_engine.clips),
|
||||
audio: await this.getItemsDict(this.audio_engine.clips),
|
||||
lipsync: await this.getItemsDict(this.lipsync_engine.clips),
|
||||
emotion: await this.getItemsDict(this.emotion_engine.clips),
|
||||
};
|
||||
console.log(timeline_json);
|
||||
|
||||
return timeline_json;
|
||||
}
|
||||
|
||||
private async loadTimelineClips(timeline_clips: any[]): Promise<any[]> {
|
||||
let new_clips = [];
|
||||
if (timeline_clips) {
|
||||
for (let index = 0; index < timeline_clips.length; index++) {
|
||||
const element = timeline_clips[index];
|
||||
let clip = new ClipUI(
|
||||
element.version,
|
||||
element.type,
|
||||
element.group,
|
||||
element.name,
|
||||
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);
|
||||
}
|
||||
}
|
||||
return new_clips;
|
||||
}
|
||||
|
||||
public async saveToJson(): Promise<any> {
|
||||
let timeline_json = {
|
||||
timeline: await this.getItems(this.timeline.timeline_items),
|
||||
transform: await this.getItemsDict(this.transform_engine.clips),
|
||||
animation: await this.getItemsDict(this.animation_engine.clips),
|
||||
audio: await this.getItemsDict(this.audio_engine.clips),
|
||||
lipsync: await this.getItemsDict(this.lipsync_engine.clips),
|
||||
emotion: await this.getItemsDict(this.emotion_engine.clips),
|
||||
}
|
||||
console.log(timeline_json)
|
||||
|
||||
return timeline_json;
|
||||
private async loadTransformClips(items: {
|
||||
[key: string]: any;
|
||||
}): Promise<{ [key: string]: any }> {
|
||||
let timeline_items_data: { [key: string]: any } = {};
|
||||
for (const key in items) {
|
||||
if (items.hasOwnProperty(key)) {
|
||||
const element = items[key];
|
||||
timeline_items_data[key] = new TransformClip(
|
||||
element.version,
|
||||
element.object_uuid,
|
||||
element.length,
|
||||
element.media_id,
|
||||
);
|
||||
timeline_items_data[key].keyframes = element.keyframes;
|
||||
}
|
||||
}
|
||||
return timeline_items_data;
|
||||
}
|
||||
|
||||
private async loadTimelineClips(timeline_clips: any[]): Promise<any[]> {
|
||||
let new_clips = [];
|
||||
if (timeline_clips) {
|
||||
for (let index = 0; index < timeline_clips.length; index++) {
|
||||
const element = timeline_clips[index];
|
||||
let clip = new ClipUI(
|
||||
element.version, element.type,
|
||||
element.group, element.name,
|
||||
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);
|
||||
}
|
||||
}
|
||||
return new_clips;
|
||||
private async loadAudioClips(items: {
|
||||
[key: string]: any;
|
||||
}): Promise<{ [key: string]: any }> {
|
||||
let timeline_items_data: { [key: string]: any } = {};
|
||||
for (const key in items) {
|
||||
if (items.hasOwnProperty(key)) {
|
||||
const element = items[key];
|
||||
timeline_items_data[key] = new AudioClip(
|
||||
element.version,
|
||||
element.media_id,
|
||||
element.volume,
|
||||
);
|
||||
}
|
||||
}
|
||||
return timeline_items_data;
|
||||
}
|
||||
|
||||
private async loadTransformClips(items: { [key: string]: any }): Promise<{ [key: string]: any }> {
|
||||
let timeline_items_data: { [key: string]: any } = {};
|
||||
for (const key in items) {
|
||||
if (items.hasOwnProperty(key)) {
|
||||
const element = items[key];
|
||||
timeline_items_data[key] = new TransformClip(element.version, element.object_uuid, element.length, element.media_id);
|
||||
timeline_items_data[key].keyframes = element.keyframes;
|
||||
}
|
||||
}
|
||||
return timeline_items_data;
|
||||
private async loadAnimationClips(items: {
|
||||
[key: string]: any;
|
||||
}): Promise<{ [key: string]: any }> {
|
||||
let timeline_items_data: { [key: string]: any } = {};
|
||||
for (const key in items) {
|
||||
if (items.hasOwnProperty(key)) {
|
||||
const element = items[key];
|
||||
timeline_items_data[key] = new AnimationClip(
|
||||
element.version,
|
||||
element.media_id,
|
||||
"remote",
|
||||
element.object_uuid,
|
||||
element.speed,
|
||||
element.length,
|
||||
element.clip_name,
|
||||
);
|
||||
}
|
||||
}
|
||||
return timeline_items_data;
|
||||
}
|
||||
|
||||
private async loadAudioClips(items: { [key: string]: any }): Promise<{ [key: string]: any }> {
|
||||
let timeline_items_data: { [key: string]: any } = {};
|
||||
for (const key in items) {
|
||||
if (items.hasOwnProperty(key)) {
|
||||
const element = items[key];
|
||||
timeline_items_data[key] = new AudioClip(element.version, element.media_id, element.volume);
|
||||
}
|
||||
}
|
||||
return timeline_items_data;
|
||||
private async loadLipsyncClips(items: {
|
||||
[key: string]: any;
|
||||
}): Promise<{ [key: string]: any }> {
|
||||
let timeline_items_data: { [key: string]: any } = {};
|
||||
for (const key in items) {
|
||||
if (items.hasOwnProperty(key)) {
|
||||
const element = items[key];
|
||||
timeline_items_data[key] = new LipSyncClip(
|
||||
element.version,
|
||||
element.media_id,
|
||||
element.volume,
|
||||
);
|
||||
}
|
||||
}
|
||||
return timeline_items_data;
|
||||
}
|
||||
|
||||
private async loadAnimationClips(items: { [key: string]: any }): Promise<{ [key: string]: any }> {
|
||||
let timeline_items_data: { [key: string]: any } = {};
|
||||
for (const key in items) {
|
||||
if (items.hasOwnProperty(key)) {
|
||||
const element = items[key];
|
||||
timeline_items_data[key] = new AnimationClip(element.version, element.media_id, "remote", element.object_uuid, element.speed, element.length, element.clip_name);
|
||||
}
|
||||
}
|
||||
return timeline_items_data;
|
||||
private async loadEmotionClips(items: {
|
||||
[key: string]: any;
|
||||
}): Promise<{ [key: string]: any }> {
|
||||
let timeline_items_data: { [key: string]: any } = {};
|
||||
for (const key in items) {
|
||||
if (items.hasOwnProperty(key)) {
|
||||
const element = items[key];
|
||||
timeline_items_data[key] = new EmotionClip(
|
||||
element.version,
|
||||
element.media_id,
|
||||
);
|
||||
}
|
||||
}
|
||||
return timeline_items_data;
|
||||
}
|
||||
|
||||
private async loadLipsyncClips(items: { [key: string]: any }): Promise<{ [key: string]: any }> {
|
||||
let timeline_items_data: { [key: string]: any } = {};
|
||||
for (const key in items) {
|
||||
if (items.hasOwnProperty(key)) {
|
||||
const element = items[key];
|
||||
timeline_items_data[key] = new LipSyncClip(element.version, element.media_id, element.volume);
|
||||
}
|
||||
}
|
||||
return timeline_items_data;
|
||||
}
|
||||
public async loadFromJson(timeline: any) {
|
||||
this.timeline.timeline_items = await this.loadTimelineClips(
|
||||
timeline["timeline"],
|
||||
);
|
||||
this.transform_engine.clips = await this.loadTransformClips(
|
||||
timeline["transform"],
|
||||
);
|
||||
this.audio_engine.clips = await this.loadAudioClips(timeline["audio"]);
|
||||
this.lipsync_engine.clips = await this.loadLipsyncClips(
|
||||
timeline["lipsync"],
|
||||
);
|
||||
this.animation_engine.clips = await this.loadAnimationClips(
|
||||
timeline["animation"],
|
||||
);
|
||||
this.emotion_engine.clips = await this.loadEmotionClips(
|
||||
timeline["emotion"],
|
||||
);
|
||||
|
||||
private async loadEmotionClips(items: { [key: string]: any }): Promise<{ [key: string]: any }> {
|
||||
let timeline_items_data: { [key: string]: any } = {};
|
||||
for (const key in items) {
|
||||
if (items.hasOwnProperty(key)) {
|
||||
const element = items[key];
|
||||
timeline_items_data[key] = new EmotionClip(element.version, element.media_id);
|
||||
}
|
||||
}
|
||||
return timeline_items_data;
|
||||
}
|
||||
|
||||
public async loadFromJson(timeline: any) {
|
||||
this.timeline.timeline_items = await this.loadTimelineClips(timeline['timeline']);
|
||||
this.transform_engine.clips = await this.loadTransformClips(timeline['transform']);
|
||||
this.audio_engine.clips = await this.loadAudioClips(timeline['audio']);
|
||||
this.lipsync_engine.clips = await this.loadLipsyncClips(timeline['lipsync']);
|
||||
this.animation_engine.clips = await this.loadAnimationClips(timeline['animation']);
|
||||
this.emotion_engine.clips = await this.loadEmotionClips(timeline['emotion']);
|
||||
|
||||
console.log(this.timeline.timeline_items)
|
||||
|
||||
this.timeline.updateUI()
|
||||
}
|
||||
console.log(this.timeline.timeline_items);
|
||||
|
||||
this.timeline.updateUI();
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ArtStyle } from "~/pages/PageEnigma/js/api_manager";
|
||||
import { ArtStyle } from "~/pages/PageEnigma/Editor/api_manager";
|
||||
|
||||
export const styleList = [
|
||||
{
|
||||
|
||||
Reference in new issue
Block a user