mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-04 17:38:10 +00:00
Outliner V1 (#517)

This commit is contained in:
1 parent
865f009ee2
commit
903038df5e
14 files changed
+646
-256
No files matched your search
@@ -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,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
};
|
||||
Reference in new issue
Block a user