Miles Ryan authored and GitHub committed 2024-06-21 18:10:35 -04:00
1 parent 865f009ee2
commit 903038df5e
14 files changed
+646 -256

No files matched your search

+140 -90
View File
@@ -40,10 +40,11 @@ import {
loadingBarIsShowing,
signalScene,
} from "~/signals";
import { updateObjectPanel } from "../signals";
import { outlinerState, updateObjectPanel } from "../signals";
import { GenerationOptions } from "../models/generationOptions";
import { toEngineActions } from "../Queue/toEngineActions";
import { SceneGenereationMetaData } from "../models/sceneGenerationMetadata";
import { SceneManager } from "./scene_manager_api";
export type EditorInitializeConfig = {
sceneToken: string;
@@ -138,11 +139,16 @@ class Editor {
utils: Utils;
videoGeneration: VideoGeneration;
mouse_controls: MouseControls;
mouse_controls: MouseControls | undefined;
save_manager: SaveManager;
generation_options: GenerationOptions;
sceneManager: SceneManager | undefined;
outliner_feature_flag: boolean;
constructor() {
console.log(
"If you see this message twice! then it rendered twice, if you see it once it's all good.",
@@ -217,9 +223,7 @@ class Editor {
this.utils = new Utils(this, this.activeScene);
this.videoGeneration = new VideoGeneration(this);
this.mouse_controls = new MouseControls(this);
this.save_manager = new SaveManager(this);
this.current_frame = 0;
// Scene State
@@ -239,6 +243,10 @@ class Editor {
lipSync: false,
cinematic: false,
};
this.outliner_feature_flag = true;
}
getRenderDimensions() {
switch (this.render_camera_aspect_ratio) {
@@ -392,11 +400,12 @@ class Editor {
this.renderer.setSize(width, height);
this.renderer.setPixelRatio(window.devicePixelRatio);
//document.body.appendChild(this.renderer.domElement)
window.addEventListener("resize", this.onWindowResize.bind(this));
this.renderer.domElement.addEventListener(
"resize",
this.onWindowResize.bind(this),
);
// window.addEventListener("resize", this.onWindowResize.bind(this));
// this.renderer.domElement.addEventListener(
// "resize",
// this.onWindowResize.bind(this),
// );
this._configurePostProcessing();
// Controls and movement.
@@ -439,22 +448,6 @@ class Editor {
this.control.setRotationSnap(0.01);
console.log("Control Sensitivity:", this.control.sensitivity);
// OnClick and MouseMove events.
window.addEventListener(
"mousemove",
this.mouse_controls.onMouseMove.bind(this.mouse_controls),
false,
);
window.addEventListener(
"click",
this.mouse_controls.onMouseClick.bind(this.mouse_controls),
false,
);
window.addEventListener(
"keydown",
this.mouse_controls.onkeydown.bind(this.mouse_controls),
false,
);
// Base control and debug stuff remove debug in prod.
if (this.control == undefined) {
return;
@@ -486,23 +479,30 @@ class Editor {
this.timeline.scene = this.activeScene;
window.addEventListener(
"mousedown",
this.mouse_controls.onMouseDown.bind(this.mouse_controls),
false,
);
window.addEventListener(
"mouseup",
this.mouse_controls.onMouseUp.bind(this.mouse_controls),
false,
);
// saving state of the scene
this.current_scene_media_token = null;
this.current_scene_glb_media_token = null;
this.cam_obj = this.activeScene.get_object_by_name(this.camera_name);
if (this.outliner_feature_flag) {
let result = this.sceneManager?.render_outliner(this.timeline.characters);
if (result) outlinerState.items.value = result.items;
}
this.mouse_controls = new MouseControls(this.camera, this.camera_person_mode, this.lockControls,
this.camera_last_pos, this.orbitControls,
this.selectedCanvas, this.switchPreviewToggle, this.rendering, this.togglePlayback.bind(this),
this.deleteObject.bind(this), this.canvReference, this.mouse, this.timeline.mouse,
this.raycaster, this.control, this.outlinePass, this.activeScene.scene,
this.publishSelect.bind(this), this.updateSelectedUI.bind(this),
this.transform_interaction, this.last_selected, this.getAssetType.bind(this), this.setSelected.bind(this));
if (this.outliner_feature_flag) {
this.sceneManager = new SceneManager(this.version, this.mouse_controls, this.activeScene, true); // Enabled dev mode.
this.mouse_controls.sceneManager = this.sceneManager;
}
// Creates the main update loop.
//this.renderer.setAnimationLoop(this.updateLoop.bind(this));
@@ -585,11 +585,19 @@ class Editor {
action: fromEngineActions.RESET_TIMELINE,
data: null,
});
if (this.outliner_feature_flag) {
let result = this.sceneManager?.render_outliner(this.timeline.characters);
if (result) outlinerState.items.value = result.items;
}
}
public async loadScene(scene_media_token: string) {
await this.save_manager.loadScene(scene_media_token);
if (this.outliner_feature_flag) {
let result = this.sceneManager?.render_outliner(this.timeline.characters);
if (result) outlinerState.items.value = result.items;
}
// publish to the UI the values for the prompts and artistic style and settings?
Queue.publish({
queueName: QueueNames.TO_ENGINE,
@@ -598,6 +606,12 @@ class Editor {
});
}
setSelected(object: THREE.Object3D[] | undefined) {
if (this.sceneManager) {
this.sceneManager.selected_objects = object;
}
}
isObjectLipsync(object_uuid: string) {
return this.utils.isObjectLipsync(object_uuid);
}
@@ -607,7 +621,11 @@ class Editor {
}
lockUnlockObject(object_uuid: string): boolean {
return this.utils.lockUnlockObject(object_uuid);
let res = this.utils.lockUnlockObject(object_uuid);
if(this.outliner_feature_flag) {
this.updateSelectedUI();
}
return res;
}
setColor(object_uuid: string, hex_color: string) {
@@ -635,29 +653,6 @@ class Editor {
});
}
/**
* This cleans up the transform controls
* During saving it
* Doesn't retain those controls.
* @returns
*/
removeTransformControls(remove_outline: boolean = true) {
if (this.control == undefined) {
return;
}
if (this.outlinePass == undefined) {
return;
}
if (remove_outline) {
this.last_selected = this.selected;
this.selected = undefined;
this.publishSelect();
}
this.control.detach();
this.activeScene.scene.remove(this.control);
if (remove_outline) this.outlinePass.selectedObjects = [];
}
switchCameraView() {
this.utils.switchCameraView();
}
@@ -737,6 +732,10 @@ class Editor {
deleteObject(uuid: string) {
this.utils.deleteObject(uuid);
if (this.outliner_feature_flag) {
let result = this.sceneManager?.render_outliner(this.timeline.characters);
if (result) outlinerState.items.value = result.items;
}
}
create_parim(name: string, pos: THREE.Vector3) {
@@ -763,14 +762,14 @@ class Editor {
this.render_camera_aspect_ratio === CameraAspectRatio.HORIZONTAL_16_9
? 1024
: this.render_camera_aspect_ratio ===
CameraAspectRatio.VERTICAL_9_16
CameraAspectRatio.VERTICAL_9_16
? 576
: 1000;
const height =
this.render_camera_aspect_ratio === CameraAspectRatio.HORIZONTAL_16_9
? 576
: this.render_camera_aspect_ratio ===
CameraAspectRatio.VERTICAL_9_16
CameraAspectRatio.VERTICAL_9_16
? 1024
: 1000;
@@ -816,6 +815,11 @@ class Editor {
if (this.cam_obj == undefined) {
this.cam_obj = this.activeScene.get_object_by_name(this.camera_name);
if (this.outliner_feature_flag) {
let result = this.sceneManager?.render_outliner(this.timeline.characters);
if (result) outlinerState.items.value = result.items;
}
}
if (this.clock == undefined || this.renderer == undefined) {
@@ -980,21 +984,41 @@ class Editor {
}
updateSelectedUI() {
if (this.selected === undefined || this.timeline.is_playing) {
return;
let mainSelected;
if (this.outliner_feature_flag) {
if (this.sceneManager?.selected_objects === undefined || this.timeline.is_playing) {
return;
}
if (this.sceneManager?.selected_objects.length <= 0) {
return 0;
}
mainSelected = this.sceneManager?.selected_objects[0];
}
const pos = this.selected.position;
const rot = this.selected.rotation;
const scale = this.selected.scale;
else {
if (this.timeline.is_playing) {
return;
}
if (this.selected == undefined) {
return 0;
}
mainSelected = this.selected;
}
this.selected = mainSelected;
const pos = mainSelected.position;
const rot = mainSelected.rotation;
const scale = mainSelected.scale;
// TODO this is a bug we need to only show when clicked on and use UPDATE when updating.
updateObjectPanel({
group:
this.selected.name === this.camera_name
mainSelected.name === this.camera_name
? ClipGroup.CAMERA
: ClipGroup.OBJECT, // TODO: add meta data to determine what it is a camera or a object or a character into prefab clips
object_uuid: this.selected.uuid,
object_name: this.selected.name,
object_uuid: mainSelected.uuid,
object_name: mainSelected.name,
version: String(this.version),
objectVectors: {
position: {
@@ -1074,26 +1098,52 @@ class Editor {
}
publishSelect() {
if (this.selected) {
console.log("publish", this.selected);
Queue.publish({
queueName: QueueNames.FROM_ENGINE,
action: fromEngineActions.SELECT_OBJECT,
data: {
type: this.getAssetType(this.selected),
object_uuid: this.selected.uuid,
version: 1,
media_id: this.selected.id.toString(),
name: "",
} as MediaItem,
});
return;
if (this, this.outliner_feature_flag) {
if (this.sceneManager?.selected_objects && this.sceneManager?.selected_objects?.length > 0) {
Queue.publish({
queueName: QueueNames.FROM_ENGINE,
action: fromEngineActions.SELECT_OBJECT,
data: {
type: this.getAssetType(this.sceneManager?.selected_objects[0]),
object_uuid: this.sceneManager?.selected_objects[0].uuid,
version: 1,
media_id: this.sceneManager?.selected_objects[0].id.toString(),
name: "",
} as MediaItem,
});
return;
}
else {
Queue.publish({
queueName: QueueNames.FROM_ENGINE,
action: fromEngineActions.DESELECT_OBJECT,
data: null,
});
}
}
else {
if (this.selected) {
Queue.publish({
queueName: QueueNames.FROM_ENGINE,
action: fromEngineActions.SELECT_OBJECT,
data: {
type: this.getAssetType(this.selected),
object_uuid: this.selected.uuid,
version: 1,
media_id: this.selected.id.toString(),
name: "",
} as MediaItem,
});
return;
}
else {
Queue.publish({
queueName: QueueNames.FROM_ENGINE,
action: fromEngineActions.DESELECT_OBJECT,
data: null,
});
}
}
Queue.publish({
queueName: QueueNames.FROM_ENGINE,
action: fromEngineActions.DESELECT_OBJECT,
data: null,
});
}
}
+54 -23
View File
@@ -72,7 +72,8 @@ export class Utils {
this.removeTransformControls(false);
} else if (this.editor.control) {
this.scene.scene.add(this.editor.control);
this.editor.control.attach(this.editor.selected);
if(this.editor.sceneManager?.selected_objects)
this.editor.control.attach(this.editor.sceneManager?.selected_objects[0]);
}
return object.userData["locked"];
@@ -91,7 +92,7 @@ export class Utils {
}
if (remove_outline) {
this.editor.last_selected = this.editor.selected;
this.editor.selected = undefined;
this.editor.outlinePass.selectedObjects = [];
this.editor.publishSelect();
}
this.editor.control.detach();
@@ -101,16 +102,19 @@ export class Utils {
// 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;
if(this.editor.sceneManager?.selected_objects){
let object = this.editor.sceneManager?.selected_objects[0]
if (object != undefined || object != null) {
object.position.x = position.x;
object.position.y = position.y;
object.position.z = position.z;
object.rotation.x = THREE.MathUtils.degToRad(rotation.x);
object.rotation.y = THREE.MathUtils.degToRad(rotation.y);
object.rotation.z = THREE.MathUtils.degToRad(rotation.z);
object.scale.x = scale.x;
object.scale.y = scale.y;
object.scale.z = scale.z;
}
}
}
@@ -118,7 +122,7 @@ export class Utils {
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) {
if (this.editor.camera_person_mode && this.editor.camera) {
this.editor.last_cam_pos.copy(this.editor.camera.position);
this.editor.last_cam_rot.copy(this.editor.camera.rotation);
@@ -136,7 +140,7 @@ export class Utils {
}
this.editor.cam_obj.scale.set(0, 0, 0);
this.editor.removeTransformControls();
this.removeTransformControls();
this.editor.selected = this.editor.cam_obj;
this.editor.publishSelect();
this.editor.updateSelectedUI();
@@ -146,7 +150,7 @@ export class Utils {
element.visible = false;
});
}
} else {
} else if (this.editor.camera) {
this.editor.camera.position.copy(this.editor.last_cam_pos);
this.editor.camera.rotation.copy(this.editor.last_cam_rot);
if (this.editor.orbitControls) {
@@ -173,22 +177,49 @@ export class Utils {
// Returns the "check sum" of the editors selected object.
getselectedSum(): number {
if (this.editor.selected === undefined) {
if (this.editor.sceneManager?.selected_objects === undefined) {
return 0;
}
if(this.editor.sceneManager?.selected_objects.length <= 0) {
return 0;
}
const posCombo =
this.editor.selected.position.x +
this.editor.selected.position.y +
this.editor.selected.position.z;
this.editor.sceneManager?.selected_objects[0].position.x +
this.editor.sceneManager?.selected_objects[0].position.y +
this.editor.sceneManager?.selected_objects[0].position.z;
const rotCombo =
this.editor.selected.rotation.x +
this.editor.selected.rotation.y +
this.editor.selected.rotation.z;
this.editor.sceneManager?.selected_objects[0].rotation.x +
this.editor.sceneManager?.selected_objects[0].rotation.y +
this.editor.sceneManager?.selected_objects[0].rotation.z;
const sclCombo =
this.editor.selected.scale.x + this.editor.selected.scale.y + this.editor.selected.scale.z;
this.editor.sceneManager?.selected_objects[0].scale.x + this.editor.sceneManager?.selected_objects[0].scale.y + this.editor.sceneManager?.selected_objects[0].scale.z;
return posCombo + rotCombo + sclCombo;
}
/* Will add in the future
A good practice to remove 3D objects from Three.js scenes
function removeObject3D(object3D) {
if (!(object3D instanceof THREE.Object3D)) return false;
// for better memory management and performance
if (object3D.geometry) object3D.geometry.dispose();
if (object3D.material) {
if (object3D.material instanceof Array) {
// for better memory management and performance
object3D.material.forEach(material => material.dispose());
} else {
// for better memory management and performance
object3D.material.dispose();
}
}
object3D.removeFromParent(); // the parent might be the scene or another Object3D, but it is sure to be removed this way
return true;
}
*/
deleteObject(uuid: string) {
const obj = this.scene.get_object_by_uuid(uuid);
this.removeTransformControls();
+169 -66
View File
@@ -1,32 +1,137 @@
import Editor from "./editor";
import * as THREE from "three";
import { hideObjectPanel, hotkeysStatus, showObjectPanel } from "../signals";
import { hideObjectPanel, hotkeysStatus, outlinerState, showObjectPanel } from "../signals";
import { OrbitControls, OutlinePass, PointerLockControls } from "three/examples/jsm/Addons.js";
import { TransformControls } from "./TransformControls";
import { SceneManager, SceneObject } from "./scene_manager_api";
export class MouseControls {
editor: Editor;
camera: THREE.PerspectiveCamera | null;
camera_person_mode: boolean;
lockControls: PointerLockControls | undefined;
camera_last_pos: THREE.Vector3;
selected: THREE.Object3D[] | undefined;
orbitControls: OrbitControls | undefined;
selectedCanvas: boolean;
switchPreviewToggle: boolean;
rendering: boolean;
togglePlayback: Function;
deleteObject: Function;
canvReference: HTMLCanvasElement | null = null;
mouse: THREE.Vector2 | undefined;
timeline_mouse: THREE.Vector2 | undefined;
control: TransformControls | undefined;
raycaster: THREE.Raycaster | undefined;
outlinePass: OutlinePass | undefined;
scene: THREE.Scene;
publishSelect: Function;
updateSelectedUI: Function;
transform_interaction: boolean;
last_selected: THREE.Object3D[] | undefined;
getAssetType: Function;
setSelected: Function;
sceneManager: SceneManager | undefined;
constructor(editor: Editor) {
this.editor = editor;
constructor(camera: THREE.PerspectiveCamera | null, camera_person_mode: boolean, lockControls: PointerLockControls | undefined,
camera_last_pos: THREE.Vector3, orbitControls: OrbitControls | undefined,
selectedCanvas: boolean, switchPreviewToggle: boolean, rendering: boolean, togglePlayback: Function,
deleteObject: Function, canvReference: HTMLCanvasElement | null,
mouse: THREE.Vector2 | undefined, timeline_mouse: THREE.Vector2 | undefined,
raycaster: THREE.Raycaster | undefined, control: TransformControls | undefined,
outlinePass: OutlinePass | undefined, scene: THREE.Scene,
publishSelect: Function, updateSelectedUI: Function,
transform_interaction: boolean, last_selected: THREE.Object3D | undefined,
getAssetType: Function, setSelected: Function
) {
this.camera = camera;
this.camera_person_mode = camera_person_mode;
this.lockControls = lockControls;
this.camera_last_pos = camera_last_pos;
this.selected = [];
this.orbitControls = orbitControls;
this.selectedCanvas = selectedCanvas;
this.switchPreviewToggle = switchPreviewToggle;
this.rendering = rendering;
this.togglePlayback = togglePlayback;
this.deleteObject = deleteObject;
this.canvReference = canvReference;
this.mouse = mouse;
this.timeline_mouse = timeline_mouse;
this.raycaster = raycaster;
this.control = control;
this.outlinePass = outlinePass;
this.scene = scene;
this.publishSelect = publishSelect;
this.updateSelectedUI = updateSelectedUI;
this.transform_interaction = transform_interaction;
this.last_selected = [];
this.getAssetType = getAssetType;
this.setSelected = setSelected;
this.sceneManager = undefined;
}
focus() {
if(this.orbitControls){
this.orbitControls.target.copy(this.selected[0].position);
this.orbitControls.maxDistance = 4;
this.orbitControls.update();
this.orbitControls.maxDistance = 999;
}
}
removeTransformControls(remove_outline: boolean = true) {
if (this.control == undefined) {
return;
}
if (this.outlinePass == undefined) {
return;
}
if (remove_outline) {
this.last_selected = this.selected;
this.selected = [];
this.publishSelect();
}
this.control.detach();
this.scene.remove(this.control);
if (remove_outline) this.outlinePass.selectedObjects = [];
}
selectObject(currentObject: THREE.Object3D) {
this.setSelected(this.selected);
this.publishSelect();
// this.editor.update_properties()
if (currentObject.userData["locked"] !== true && this.control) {
this.scene.add(this.control);
this.control.attach(currentObject);
}
if(this.selected && this.outlinePass){
this.outlinePass.selectedObjects = this.selected;
}
this.transform_interaction = true;
// Contact react land
showObjectPanel();
this.updateSelectedUI();
}
onMouseDown(event: any) {
if (event.button === 1 && this.editor.camera_person_mode) {
this.editor.lockControls?.lock();
if (event.button === 1 && this.camera_person_mode) {
this.lockControls?.lock();
}
}
onMouseUp(event: any) {
if (event.button === 1) {
this.editor.lockControls?.unlock();
this.lockControls?.unlock();
}
if (event.button !== 0 && this.editor.camera) {
if (event.button !== 0 && this.camera) {
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)),
parseFloat(this.camera.position.x.toFixed(2)),
parseFloat(this.camera.position.y.toFixed(2)),
parseFloat(this.camera.position.z.toFixed(2)),
);
this.editor.camera_last_pos.copy(camera_pos);
this.camera_last_pos.copy(camera_pos);
}
}
@@ -34,79 +139,78 @@ export class MouseControls {
if (hotkeysStatus.value.disabled) {
return;
}
else 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;
else if (event.key === "f" && this.selected && this.orbitControls) {
this.focus();
return;
}
else if (event.key === " ") {
if (!this.editor.rendering && !this.editor.switchPreviewToggle && this.editor.selectedCanvas) {
this.editor.togglePlayback();
if (!this.rendering && !this.switchPreviewToggle && this.selectedCanvas) {
this.togglePlayback();
}
return;
}
else if (event.key === "Backspace" || event.key === "Delete") {
if (this.editor.selected) {
this.editor.deleteObject(this.editor.selected.uuid);
if (this.selected) {
this.selected.forEach(selected => {
this.deleteObject(selected.uuid);
});
}
return;
}
else if (event.key === "t") { // transform
this.editor.control?.setMode("translate");
this.control?.setMode("translate");
return;
}
else if (event.key === "r") { // rotate
this.editor.control?.setMode("rotate");
this.control?.setMode("rotate");
return;
}
else if (event.key === "g") { // scale
this.editor.control?.setMode("scale");
this.control?.setMode("scale");
return;
}
}
// Sets new mouse location usually used in raycasts.
onMouseMove(event: any) {
if(this.editor.canvReference == undefined) { return; }
const rect = this.editor.canvReference.getBoundingClientRect();
if (this.editor.mouse == undefined) {
onMouseMove(event: MouseEvent) {
if (this.canvReference == undefined) { return; }
const rect = this.canvReference.getBoundingClientRect();
if (this.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;
this.mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1;
this.mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1;
this.timeline_mouse = this.mouse;
}
// When the mouse clicks the screen.
onMouseClick() {
if(this.editor.camera == undefined) { return; }
if (this.camera == undefined) { return; }
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)),
parseFloat(this.camera.position.x.toFixed(2)),
parseFloat(this.camera.position.y.toFixed(2)),
parseFloat(this.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.camera_last_pos.equals(new THREE.Vector3(0, 0, 0))) {
this.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.raycaster == undefined ||
this.mouse == undefined ||
this.control == undefined ||
this.outlinePass == undefined ||
this.camera_person_mode ||
!this.camera_last_pos.equals(camera_pos)
) {
this.editor.camera_last_pos.copy(camera_pos);
this.camera_last_pos.copy(camera_pos);
return;
}
this.editor.camera_last_pos.copy(camera_pos);
this.camera_last_pos.copy(camera_pos);
this.editor.raycaster.setFromCamera(this.editor.mouse, this.editor.camera);
this.raycaster.setFromCamera(this.mouse, this.camera);
const interactable: any[] = [];
this.editor.activeScene.scene.children.forEach((child: THREE.Object3D) => {
this.scene.children.forEach((child: THREE.Object3D) => {
if (child.name != "") {
if (
child.type == "Mesh" ||
@@ -118,7 +222,7 @@ export class MouseControls {
}
}
});
const intersects = this.editor.raycaster.intersectObjects(interactable, true);
const intersects = this.raycaster.intersectObjects(interactable, true);
if (intersects.length > 0) {
if (intersects[0].object.type != "GridHelper") {
@@ -126,30 +230,29 @@ export class MouseControls {
while (currentObject.parent && currentObject.parent.type !== "Scene") {
currentObject = currentObject.parent;
}
this.editor.selected = currentObject;
this.selected = [];
// Show panel here
if (this.editor.selected.type == "Scene") {
this.editor.selected = intersects[0].object;
if (currentObject.type == "Scene") {
this.selected?.push(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);
else {
this.selected?.push(currentObject);
}
this.editor.outlinePass.selectedObjects = [this.editor.selected];
this.editor.transform_interaction = true;
// Contact react land
showObjectPanel();
this.editor.updateSelectedUI();
this.selectObject(currentObject);
}
} else {
this.editor.removeTransformControls();
this.selected = [];
this.setSelected(this.selected);
this.removeTransformControls();
hideObjectPanel();
}
if (this.sceneManager) {
const selected: SceneObject | null = this.sceneManager.selected();
outlinerState.selectedItem.value = selected;
}
}
}
+2 -2
View File
@@ -30,7 +30,7 @@ export class SaveManager {
}): 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();
this.editor.utils.removeTransformControls();
showEditorLoader();
const proxyScene = new StoryTellerProxyScene(
@@ -38,7 +38,7 @@ export class SaveManager {
this.editor.activeScene,
);
const scene_json = await proxyScene.saveToScene(this.editor.version);
console.log(scene_json);
const proxyTimeline = new StoryTellerProxyTimeline(
this.editor.version,
this.editor.timeline,
+7 -4
View File
@@ -12,7 +12,6 @@ class Scene {
gridHelper: THREE.GridHelper | undefined;
scene: THREE.Scene;
hot_items: THREE.Object3D[] | undefined;
selected: THREE.Object3D | undefined;
// Display message
message_mesh: THREE.Mesh | undefined;
@@ -30,7 +29,6 @@ class Scene {
this.gridHelper;
this.scene = new THREE.Scene();
this.hot_items = [];
this.selected = undefined;
this.message_mesh = undefined;
this.loading_placeholder = undefined;
@@ -136,8 +134,6 @@ class Scene {
"",
);
if (obj_keyframe_uuid === keyframe_uuid) {
console.log("FOUNDDDD", obj_keyframe_uuid);
console.log("Found!", object);
this.scene.remove(object);
return;
}
@@ -270,6 +266,13 @@ class Scene {
}
}
setVisible(object_uuid: string, visible: boolean) {
let object = this.get_object_by_uuid(object_uuid);
if (object) {
object.visible = visible;
}
}
async loadMMDWithPlaceholder(
media_id: string,
name: string,
@@ -0,0 +1,180 @@
import * as THREE from "three";
import { IconDefinition, faCamera, faCube, faPerson } from "@fortawesome/pro-solid-svg-icons";
import Scene from "./scene";
import { MouseControls } from "./keybinds_controls";
import { ClipGroup } from "~/enums";
export type SceneObject = {
id: string;
icon: IconDefinition;
name: string;
type: string;
visible: boolean;
locked: boolean;
};
export interface SceneManagerAPI {
create(media_token: string, name: string, position: THREE.Vector3): void;
retrieve(object_uuid: string): void;
delete(object_uuid: string): void;
update(): void;
selected(): void;
render_outliner(timeline_characters: { [key: string]: ClipGroup }): void;
onMouseMove(event: MouseEvent): void;
onMouseClick(): void;
onKeyDown(event: KeyboardEvent): void;
onMouseDown(event: MouseEvent): void;
onMouseUp(event: MouseEvent): void;
select_object(id: string): void;
}
export class SceneManager implements SceneManagerAPI {
scene: Scene;
mouse_controls: MouseControls;
version: number;
selected_objects: THREE.Object3D[] | undefined;
constructor(version: number, mouse_controls: MouseControls, scene: Scene, devMode: boolean = false) {
this.mouse_controls = mouse_controls;
this.scene = scene;
this.version = version
if (devMode) {
window.addEventListener(
"mousemove",
this.onMouseMove.bind(this),
false,
);
window.addEventListener(
"click",
this.onMouseClick.bind(this),
false,
);
window.addEventListener(
"keydown",
this.onKeyDown.bind(this),
false,
);
window.addEventListener(
"mousedown",
this.onMouseDown.bind(this),
false,
);
window.addEventListener(
"mouseup",
this.onMouseUp.bind(this),
false,
);
}
}
public async create(media_token: string, name: string, position: THREE.Vector3) {
await this.scene.loadObject(media_token, name, true, position, this.version);
}
/* NEVER CALL THIS INTERNALLY */
public render_outliner(timeline_characters: { [key: string]: ClipGroup }) { // needs timeline_characters to render favicons.
console.log("Render Outliner.")
// Not permanent just in place until we have multi object select ability.
let selected_item = this.selected();
let signal_items: SceneObject[] = [];
this.scene.scene.children.forEach(child => {
const converted = this.convert_object(child, timeline_characters)
if(converted.name !== ""){
signal_items.push(converted)
}
});
const outlinerState = {
selectedItem: selected_item,
items: signal_items
}
return outlinerState;
}
public async retrieve(object_uuid: string) {
return this.scene.get_object_by_uuid(object_uuid);
}
public async update() {
// We dont use parents so we will see what this will do.
}
public async delete(object_uuid: string) {
// Deletes an object.
this.mouse_controls.deleteObject(object_uuid);
}
public async double_click(object_uuid: string) {
this.mouse_controls.focus();
}
public async hideObject(object_uuid: string) {
let object = await this.retrieve(object_uuid);
if(object?.visible !== undefined){
object.visible = !object.visible;
object.userData["visible"] = object.visible;
}
}
public selected() {
let selected_item = null;
if(this.selected_objects && this.selected_objects.length > 0){
selected_item = this.selected_objects[0];
}
if(selected_item) {
return this.convert_object(selected_item, {});
}
return null;
}
public select_object(id: string) {
let object = this.scene.get_object_by_uuid(id);
if(object){
this.mouse_controls.selected = [object];
this.mouse_controls.selectObject(object);
}
}
// Converts a 3d object to signal item format.
private convert_object(object: THREE.Object3D, timeline_characters: { [key: string]: ClipGroup }) {
let faicon = faCube;
let name = object.name;
if (object.name == "::CAM::") {
faicon = faCamera;
name = "Camera";
}
else if (object.uuid in timeline_characters) {
faicon = faPerson;
}
let locked = object.userData["locked"];
if(locked == undefined) { locked = false }
return {
id: object.uuid,
icon: faicon,
name: name.charAt(0).toUpperCase() + name.slice(1),
type: object.type,
visible: object.visible,
locked: object.userData["locked"],
}
}
public onMouseMove(event: MouseEvent) {
this.mouse_controls.onMouseMove(event);
}
public onMouseClick() {
this.mouse_controls.onMouseClick();
}
public onKeyDown(event: KeyboardEvent) {
this.mouse_controls.onkeydown(event);
}
public onMouseDown(event: MouseEvent) {
this.mouse_controls.onMouseDown(event);
}
public onMouseUp(event: MouseEvent) {
this.mouse_controls.onMouseUp(event);
}
}
+12 -5
View File
@@ -27,6 +27,8 @@ import EmotionEngine from "./emotion_engine";
import { GenerationOptions } from "~/pages/PageEnigma/models/generationOptions";
import { Vector3 } from "three";
import { outlinerState } from "../signals";
export class TimeLine {
editorEngine: Editor;
timeline_items: ClipUI[];
@@ -151,22 +153,27 @@ export class TimeLine {
case toEngineActions.UNMUTE:
await this.mute(data.data as ClipUI, true);
break;
// Create operations
case toEngineActions.ADD_CHARACTER:
this.addCharacter(data.data as MediaItem);
let result = this.editorEngine.sceneManager?.render_outliner(this.characters);
if(result) outlinerState.items.value = result.items;
break;
case toEngineActions.ADD_OBJECT: {
const newObject = await this.addObject(data.data as MediaItem);
this.queueNewObjectMessage(newObject, data.data as MediaItem);
let result = this.editorEngine.sceneManager?.render_outliner(this.characters);
console.log(result)
if(result) outlinerState.items.value = result.items;
break;
}
case toEngineActions.ADD_SHAPE: {
const newShape = await this.addShape(data.data as MediaItem);
this.queueNewObjectMessage(newShape, data.data as MediaItem);
let result = this.editorEngine.sceneManager?.render_outliner(this.characters);
if(result) outlinerState.items.value = result.items;
break;
}
case toEngineActions.ENTER_PREVIEW_STATE:
await this.editorEngine.switchPreview();
break;
@@ -217,7 +224,7 @@ export class TimeLine {
obj.position.copy(pos);
const object_uuid = obj.uuid;
this.characters[object_uuid] = ClipGroup.CHARACTER;
this.characters[object_uuid] = ClipGroup.CHARACTER; // TODO: Create a class to make the idea of a character.
new_data["object_uuid"] = object_uuid;
Queue.publish({
@@ -297,8 +304,8 @@ export class TimeLine {
const raycaster = new THREE.Raycaster();
raycaster.layers.enable(0);
raycaster.layers.enable(1);
if (this.mouse && this.camera) {
raycaster.setFromCamera(this.mouse, this.camera);
if (this.editorEngine.mouse && this.camera) {
raycaster.setFromCamera(this.editorEngine.mouse, this.camera);
const intersects = raycaster.intersectObjects(
this.scene.scene.children,
false,
@@ -21,7 +21,7 @@ export class VideoGeneration {
async generateFrame() {
if (!this.editor.generating_preview && this.editor.rawRenderer) {
this.editor.generating_preview = true;
this.editor.removeTransformControls();
this.editor.utils.removeTransformControls();
this.editor.activeScene.renderMode(true);
if (this.editor.activeScene.hot_items) {
this.editor.activeScene.hot_items.forEach((element) => {
@@ -236,7 +236,7 @@ export const ControlPanelSceneObject = () => {
<div className="flex items-center gap-2">
<FontAwesomeIcon icon={faCube} />
<p className="max-w-36 truncate font-semibold">
{currentSceneObject.object_name}
{currentSceneObject.object_name.charAt(0).toUpperCase() + currentSceneObject.object_name.slice(1)}
</p>
</div>
<FontAwesomeIcon
@@ -246,17 +246,6 @@ export const ControlPanelSceneObject = () => {
/>
</div>
<Button
variant="secondary"
icon={locked ? faLock : faLockOpen}
onClick={toggleLock}
className={
locked ? "bg-brand-primary/20 hover:bg-brand-primary/40" : ""
}
>
{locked ? "Unlock" : "Lock"} object
</Button>
<Transition
show={!isCollapsed}
enter="transition-all duration-200 ease-in-out"
@@ -2,22 +2,22 @@ import { useContext, useState, useCallback } from "react";
import { useParams, useLocation, useNavigate } from "@remix-run/react";
import { useSignals, useSignalEffect } from "@preact/signals-react/runtime";
import {
// faCheckSquare,
faCheckSquare,
faFile,
// faSquare,
faSquare,
} from "@fortawesome/pro-solid-svg-icons";
import { EngineContext } from "~/pages/PageEnigma/contexts/EngineContext";
import { ToastTypes, getArtStyle } from "~/enums";
import { scene, signalScene, authentication, addToast } from "~/signals";
// import { outlinerIsShowing } from "~/pages/PageEnigma/signals/outliner/outliner";
import { outlinerIsShowing } from "~/pages/PageEnigma/signals/outliner/outliner";
import {
ButtonDialogue,
ButtonDropdown,
Input,
H4,
// Button,
Button,
} from "~/components";
import { TestFeaturesButtons } from "./TestFeaturesButtons";
@@ -37,7 +37,7 @@ import {
upscale,
} from "~/pages/PageEnigma/signals";
import { CameraAspectRatio } from "~/pages/PageEnigma/enums";
// import { twMerge } from "tailwind-merge";
import { twMerge } from "tailwind-merge";
export const ControlsTopButtons = () => {
useSignals();
@@ -173,9 +173,9 @@ export const ControlsTopButtons = () => {
}
});
// const handleShowOutliner = () => {
// outlinerIsShowing.value = !outlinerIsShowing.value;
// };
const handleShowOutliner = () => {
outlinerIsShowing.value = !outlinerIsShowing.value;
};
return (
<div className="flex flex-col gap-2 pl-2 pt-2">
@@ -309,7 +309,7 @@ export const ControlsTopButtons = () => {
]}
/>
{/* //TODO: uncomment to show outliner button
<Button
icon={outlinerIsShowing.value ? faCheckSquare : faSquare}
className="shadow-xl"
@@ -323,7 +323,7 @@ export const ControlsTopButtons = () => {
onClick={handleShowOutliner}
>
Outliner
</Button> */}
</Button>
<ButtonDialogue
buttonProps={{
@@ -1,4 +1,4 @@
import { ChangeEvent, useState } from "react";
import { ChangeEvent, useContext, useState } from "react";
import {
outlinerIsShowing,
outlinerState,
@@ -21,6 +21,7 @@ import { Button, Input } from "~/components";
import { Transition } from "@headlessui/react";
import { twMerge } from "tailwind-merge";
import { useSignals } from "@preact/signals-react/runtime";
import { EngineContext } from "../../contexts/EngineContext";
const OutlinerItem = ({ item }: { item: SceneObject }) => {
useSignals();
@@ -28,6 +29,9 @@ const OutlinerItem = ({ item }: { item: SceneObject }) => {
const isSelected = outlinerState.selectedItem.value?.id === item.id;
const editorEngine = useContext(EngineContext)
// Delete object logic here
const handleDeleteKeyPress = (event: React.KeyboardEvent<HTMLDivElement>) => {
if (event.key === "Delete") {
@@ -38,6 +42,7 @@ const OutlinerItem = ({ item }: { item: SceneObject }) => {
// Double click logic here
const handleDoubleClick = () => {
console.log("Item double clicked:", item.id);
editorEngine?.sceneManager?.double_click(item.id);
};
return (
@@ -51,7 +56,7 @@ const OutlinerItem = ({ item }: { item: SceneObject }) => {
onMouseLeave={() => setHovered(false)}
onDoubleClick={handleDoubleClick}
onKeyDown={handleDeleteKeyPress}
onClick={() => selectItem(item.id)}
onClick={() => selectItem(item.id, editorEngine?.sceneManager)}
tabIndex={0}
>
<span className="flex items-center gap-2.5">
@@ -64,7 +69,7 @@ const OutlinerItem = ({ item }: { item: SceneObject }) => {
<button
onClick={(e) => {
e.stopPropagation();
toggleLock(item.id);
toggleLock(item.id, editorEngine?.lockUnlockObject.bind(editorEngine));
}}
style={{
opacity: hovered || item.locked ? 1 : 0,
@@ -80,7 +85,7 @@ const OutlinerItem = ({ item }: { item: SceneObject }) => {
<button
onClick={(e) => {
e.stopPropagation();
toggleVisibility(item.id);
toggleVisibility(item.id, editorEngine?.sceneManager?.hideObject.bind(editorEngine.sceneManager));
}}
style={{
opacity: hovered || !item.visible ? 1 : 0,
@@ -26,6 +26,7 @@ export interface ObjectJSON {
shininess: number,
specular: number,
locked: boolean,
visible: boolean | undefined;
}
export class StoryTellerProxy3DObject {
@@ -44,6 +45,7 @@ export class StoryTellerProxy3DObject {
shininess: number;
specular: number;
locked: boolean;
visible: boolean;
constructor(
version: number,
@@ -66,6 +68,7 @@ export class StoryTellerProxy3DObject {
this.shininess = 0.0;
this.specular = 0.5;
this.locked = false;
this.visible = true;
}
getColorAsHexString(object: THREE.Object3D): string {
@@ -91,7 +94,7 @@ export class StoryTellerProxy3DObject {
}
public async toJSON(): Promise<ObjectJSON> {
const json:ObjectJSON = {
const json: ObjectJSON = {
version: this.version,
position: {
x: this.position.x,
@@ -117,6 +120,7 @@ export class StoryTellerProxy3DObject {
shininess: this.shininess,
specular: this.specular,
locked: this.locked,
visible: this.visible,
};
return json;
}
@@ -33,6 +33,7 @@ export class StoryTellerProxyScene {
child.userData["media_id"],
);
}
const proxyObject3D: StoryTellerProxy3DObject =
this.lookUpDictionary[child.uuid];
proxyObject3D.position.copy(child.position);
@@ -46,6 +47,7 @@ export class StoryTellerProxyScene {
proxyObject3D.shininess = child.userData["shininess"];
proxyObject3D.specular = child.userData["specular"];
proxyObject3D.locked = child.userData["locked"];
proxyObject3D.visible = child.visible;
const json_data = await proxyObject3D.toJSON();
return json_data
}
@@ -164,6 +166,10 @@ export class StoryTellerProxyScene {
obj.userData["metalness"] = json_object.metalness;
obj.userData["shininess"] = json_object.shininess;
obj.userData["specular"] = json_object.specular;
if(json_object.visible !== undefined){
this.scene.setVisible(obj.uuid, json_object.visible)
}
this.scene.setColor(obj.uuid, json_object.color);
}
}
@@ -1,69 +1,81 @@
import { signal } from "@preact/signals-core";
import { SceneObject } from "./types";
import { faCamera, faCube, faPerson } from "@fortawesome/pro-solid-svg-icons";
import { SceneManager } from "../../Editor/scene_manager_api";
export const outlinerState = {
selectedItem: signal<SceneObject | null>(null),
items: signal<SceneObject[]>([
// dummy outliner items
{
id: "1",
icon: faCamera,
name: "Camera",
type: "camera",
visible: true,
locked: false,
},
{
id: "2",
icon: faPerson,
name: "Story Girl",
type: "character",
visible: true,
locked: false,
},
{
id: "3",
icon: faCube,
name: "Car",
type: "object",
visible: true,
locked: false,
},
{
id: "4",
icon: faCube,
name: "Tree",
type: "object",
visible: true,
locked: false,
},
]),
items: signal<SceneObject[]>([]),
};
// {
// id: "1",
// icon: faCamera,
// name: "Camera",
// type: "camera",
// visible: true,
// locked: false,
// },
// {
// id: "2",
// icon: faPerson,
// name: "Story Girl",
// type: "character",
// visible: true,
// locked: false,
// },
// {
// id: "3",
// icon: faCube,
// name: "Car",
// type: "object",
// visible: true,
// locked: false,
// },
// {
// id: "4",
// icon: faCube,
// name: "Tree",
// type: "object",
// visible: true,
// locked: false,
// },
export const outlinerIsShowing = signal(false);
export const selectItem = (id: string) => {
export const selectItem = (id: string, sceneManager: SceneManager | undefined) => {
const item = outlinerState.items.value.find((item) => item.id === id);
if (item) {
outlinerState.selectedItem.value = item;
}
if(sceneManager) {
sceneManager.select_object(id);
}
};
export const toggleVisibility = (id: string) => {
export const toggleVisibility = (id: string, hideFn: Function | undefined) => {
const items = outlinerState.items.value;
const index = items.findIndex((item) => item.id === id);
if (index !== -1) {
items[index].visible = !items[index].visible;
outlinerState.items.value = [...items];
}
if(hideFn) {
hideFn(id);
}
};
export const toggleLock = (id: string) => {
export const toggleLock = (id: string, lockFn: Function | undefined) => {
const items = outlinerState.items.value;
const index = items.findIndex((item) => item.id === id);
if (index !== -1) {
items[index].locked = !items[index].locked;
outlinerState.items.value = [...items];
}
if(lockFn) {
lockFn(id);
}
};