mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-07 10:31:46 +00:00
PageScene: collapse Editor facades and relocate scene-object methods
Drops the remaining 1-line forwarders on Editor and rewires their callers to talk to the owning subsystem directly. Inlined at call sites then deleted: - updateOutliner → selection.refreshOutliner (3 actions/* callers) - publishSelect → selection.publishSelect (1 action + helper.ts) - updateSelectedUI → selection.updateSelectedUI (in-engine callers) - cacheScene → save_manager.getSceneJson (EngineProvider) - setColor → activeScene.setColor (ControlPanelSceneObject) - isObjectLipsync deleted entirely (zero callers) Relocated: - lockUnlockObject + isObjectLocked moved onto SelectionBridge. The bridge gains a SelectionBridgeDeps surface for the userData mutation (still in SceneUtils as toggleObjectLocked) and the gizmo attach/detach. Callers (Outliner, ControlPanelSceneObject) now go through editor.selection. - setSelectedObject (which despite the name wrote position/rotation/ scale onto the active selection) becomes SceneManager.updateSelectedTransform — same data ownership, no more reverse dependency from helper.ts into sceneManager.selected_objects. helper.ts shrinks to removeTransformControls, isObjectLocked, toggleObjectLocked, getSelectedSum, deleteObject. The pre-existing TS errors in deleteObject (Mesh.texture / Material[] dispose) are untouched — out of scope for this refactor.
This commit is contained in:
1 parent
5ec73e362d
commit
bccdd8df5b
11 files changed
+73
-126
No files matched your search
@@ -23,6 +23,6 @@ export async function addCharacter(
|
||||
name: obj.name || (item.name ?? "character"),
|
||||
mediaId: item.media_id,
|
||||
});
|
||||
editor.updateOutliner?.();
|
||||
editor.selection.refreshOutliner();
|
||||
return obj.uuid;
|
||||
}
|
||||
@@ -21,6 +21,6 @@ export async function addObject(
|
||||
name: obj.name || (item.name ?? "object"),
|
||||
mediaId: item.media_id,
|
||||
});
|
||||
editor.updateOutliner?.();
|
||||
editor.selection.refreshOutliner();
|
||||
return obj.uuid;
|
||||
}
|
||||
@@ -21,6 +21,6 @@ export async function addShape(
|
||||
name: obj.name || (item.name ?? "shape"),
|
||||
mediaId: item.media_id,
|
||||
});
|
||||
editor.updateOutliner?.();
|
||||
editor.selection.refreshOutliner();
|
||||
return obj.uuid;
|
||||
}
|
||||
@@ -11,5 +11,5 @@ export function deselectObject(editor: Editor): void {
|
||||
if (editor.sceneManager) {
|
||||
editor.sceneManager.selected_objects = undefined;
|
||||
}
|
||||
editor.publishSelect();
|
||||
editor.selection.publishSelect();
|
||||
}
|
||||
+3
-3
@@ -84,7 +84,7 @@ export const ControlPanelSceneObject = () => {
|
||||
}
|
||||
|
||||
setInputsUpdated(false);
|
||||
editorEngine.setSelectedObject(
|
||||
editorEngine.sceneManager?.updateSelectedTransform(
|
||||
localToEngine(localPosition),
|
||||
localToEngine(localRotation),
|
||||
localToEngine(localScale),
|
||||
@@ -103,7 +103,7 @@ export const ControlPanelSceneObject = () => {
|
||||
setLocalRotation(engineToLocal(vectors.rotation));
|
||||
setLocalScale(engineToLocal(vectors.scale));
|
||||
|
||||
setLocked(editorEngine.isObjectLocked(editorEngine?.selected?.uuid || ""));
|
||||
setLocked(editorEngine.selection.isObjectLocked(editorEngine?.selected?.uuid || ""));
|
||||
setColor(editorEngine?.selected?.userData.color);
|
||||
}, [currentSceneObject, editorEngine]);
|
||||
|
||||
@@ -220,7 +220,7 @@ export const ControlPanelSceneObject = () => {
|
||||
className="h-0 w-0 cursor-pointer opacity-0"
|
||||
id={colorInputId}
|
||||
onChange={(e: ChangeEvent<HTMLInputElement>) => {
|
||||
editorEngine?.setColor(
|
||||
editorEngine?.activeScene.setColor(
|
||||
editorEngine?.selected?.uuid || "",
|
||||
e.target.value,
|
||||
);
|
||||
|
||||
@@ -48,7 +48,7 @@ const OutlinerRow = ({ item }: { item: OutlinerItem }) => {
|
||||
const handleToggleLock = (e: React.MouseEvent) => {
|
||||
e.stopPropagation();
|
||||
usePageSceneStore.getState().toggleOutlinerLock(item.id);
|
||||
editorEngine?.lockUnlockObject(item.id);
|
||||
editorEngine?.selection.lockUnlockObject(item.id);
|
||||
};
|
||||
|
||||
const handleToggleVisibility = (e: React.MouseEvent) => {
|
||||
|
||||
+1
-3
@@ -59,9 +59,7 @@ export const EngineProvider = ({ sceneToken, children }: Props) => {
|
||||
const sceneGenerationMetadata = getSceneGenerationMetaData(
|
||||
activeEditorRef.current,
|
||||
);
|
||||
const cacheJson = activeEditorRef.current.cacheScene({
|
||||
sceneTitle: "",
|
||||
sceneToken: "",
|
||||
const cacheJson = activeEditorRef.current.save_manager.getSceneJson({
|
||||
sceneGenerationMetadata: sceneGenerationMetadata,
|
||||
});
|
||||
const cacheString = JSON.stringify(cacheJson);
|
||||
|
||||
@@ -4,7 +4,6 @@ import Scene from "./scene.js";
|
||||
import { APIManager } from "./api_manager.js";
|
||||
import { PointerLockControls } from "three/addons/controls/PointerLockControls.js";
|
||||
import { CameraAspectRatio } from "~/pages/PageScene/enums";
|
||||
import { XYZ } from "../datastructures/common";
|
||||
import { SceneUtils } from "./helper";
|
||||
import { MouseControls } from "./keybinds_controls";
|
||||
import { SaveManager } from "./save_manager";
|
||||
@@ -158,6 +157,15 @@ class Editor {
|
||||
getSceneManager: () => this.sceneManager,
|
||||
cameraName: this.cameraController.camera_name,
|
||||
version: this.version,
|
||||
toggleObjectLocked: (uuid) => this.utils.toggleObjectLocked(uuid),
|
||||
isObjectLocked: (uuid) => this.utils.isObjectLocked(uuid),
|
||||
removeTransformControls: () =>
|
||||
this.utils.removeTransformControls(false),
|
||||
attachGizmoToCurrentSelection: () => {
|
||||
this.gizmo.addToScene(this.activeScene.scene);
|
||||
const selected = this.sceneManager?.selected_objects?.[0];
|
||||
if (selected) this.gizmo.attach(selected);
|
||||
},
|
||||
});
|
||||
|
||||
this.activeScene = new Scene(
|
||||
@@ -634,29 +642,6 @@ class Editor {
|
||||
this.selection.refreshOutliner();
|
||||
}
|
||||
|
||||
isObjectLipsync(object_uuid: string) {
|
||||
return this.utils.isObjectLipsync(object_uuid);
|
||||
}
|
||||
|
||||
isObjectLocked(object_uuid: string): boolean {
|
||||
return this.utils.isObjectLocked(object_uuid);
|
||||
}
|
||||
|
||||
lockUnlockObject(object_uuid: string): boolean {
|
||||
const res = this.utils.lockUnlockObject(object_uuid);
|
||||
this.selection.updateSelectedUI();
|
||||
return res;
|
||||
}
|
||||
|
||||
setColor(object_uuid: string, hex_color: string) {
|
||||
this.activeScene.setColor(object_uuid, hex_color);
|
||||
}
|
||||
|
||||
// TO UPDATE selected objects in the scene might want to add to the scene ...
|
||||
async setSelectedObject(position: XYZ, rotation: XYZ, scale: XYZ) {
|
||||
this.utils.setSelectedObject(position, rotation, scale);
|
||||
}
|
||||
|
||||
public async saveScene({
|
||||
sceneTitle,
|
||||
sceneToken,
|
||||
@@ -673,20 +658,6 @@ class Editor {
|
||||
});
|
||||
}
|
||||
|
||||
public cacheScene({
|
||||
sceneTitle,
|
||||
sceneToken,
|
||||
sceneGenerationMetadata,
|
||||
}: {
|
||||
sceneTitle: string;
|
||||
sceneToken: string;
|
||||
sceneGenerationMetadata: SceneGenereationMetaData;
|
||||
}) {
|
||||
return this.save_manager.getSceneJson({
|
||||
sceneGenerationMetadata: sceneGenerationMetadata,
|
||||
});
|
||||
}
|
||||
|
||||
deleteObject(uuid: string) {
|
||||
this.mouse_controls?.clearFKVisuals();
|
||||
this.mouse_controls?.removeTransformControls(true);
|
||||
@@ -826,20 +797,6 @@ class Editor {
|
||||
setIs3DEditorInitialized(false);
|
||||
console.log("3D Editor Engine unmounted");
|
||||
}
|
||||
|
||||
// Facades — external callers in actions/* and helper.ts target these
|
||||
// names. Internals reach the bridge directly via this.selection.*.
|
||||
updateOutliner() {
|
||||
this.selection.updateOutliner();
|
||||
}
|
||||
|
||||
updateSelectedUI() {
|
||||
this.selection.updateSelectedUI();
|
||||
}
|
||||
|
||||
publishSelect() {
|
||||
this.selection.publishSelect();
|
||||
}
|
||||
}
|
||||
|
||||
export default Editor;
|
||||
@@ -13,7 +13,7 @@ import { AssetType, ClipGroup } from "~/enums";
|
||||
import { usePageSceneStore } from "../../PageSceneStore";
|
||||
import type { SceneManager } from "../scene_manager_api";
|
||||
|
||||
export type SelectionEngineRefs = {
|
||||
export type SelectionBridgeDeps = {
|
||||
// Lazy: SceneManager is created in Editor.initialize, after the bridge.
|
||||
getSceneManager: () => SceneManager | undefined;
|
||||
// The reserved camera-entity name ("::CAM::") used to distinguish
|
||||
@@ -21,6 +21,14 @@ export type SelectionEngineRefs = {
|
||||
cameraName: string;
|
||||
// Editor's data version, written into the object panel.
|
||||
version: number;
|
||||
|
||||
// Lock/unlock plumbing — the userData mutation lives in SceneUtils,
|
||||
// and the gizmo attach/detach lives on GizmoController. The bridge
|
||||
// calls them through these callbacks instead of importing either.
|
||||
toggleObjectLocked: (uuid: string) => boolean; // returns new locked state
|
||||
isObjectLocked: (uuid: string) => boolean;
|
||||
removeTransformControls: () => void;
|
||||
attachGizmoToCurrentSelection: () => void;
|
||||
};
|
||||
|
||||
export class SelectionBridge {
|
||||
@@ -32,7 +40,26 @@ export class SelectionBridge {
|
||||
// compared in renderSingleFrame against utils.getSelectedSum().
|
||||
last_selected_sum: number = 0;
|
||||
|
||||
constructor(private readonly engine: SelectionEngineRefs) {}
|
||||
constructor(private readonly engine: SelectionBridgeDeps) {}
|
||||
|
||||
// Toggle the locked flag on an object's userData, then either yank
|
||||
// the gizmo (lock) or re-attach it to the current selection (unlock).
|
||||
// Returns the new locked state.
|
||||
lockUnlockObject(object_uuid: string): boolean {
|
||||
const locked = this.engine.toggleObjectLocked(object_uuid);
|
||||
if (locked) {
|
||||
this.engine.removeTransformControls();
|
||||
} else {
|
||||
this.engine.attachGizmoToCurrentSelection();
|
||||
}
|
||||
this.updateSelectedUI();
|
||||
return locked;
|
||||
}
|
||||
|
||||
// True if the object is currently locked. Pure passthrough.
|
||||
isObjectLocked(object_uuid: string): boolean {
|
||||
return this.engine.isObjectLocked(object_uuid);
|
||||
}
|
||||
|
||||
setSelected(object: THREE.Object3D[] | undefined) {
|
||||
const sceneManager = this.engine.getSceneManager();
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import Scene from "./scene";
|
||||
import Editor from "./editor";
|
||||
import * as THREE from "three";
|
||||
import { XYZ } from "../datastructures/common";
|
||||
import { usePageSceneStore } from "../PageSceneStore";
|
||||
|
||||
export class SceneUtils {
|
||||
@@ -19,26 +18,6 @@ export class SceneUtils {
|
||||
);
|
||||
}
|
||||
|
||||
// For react to see if an object has lipsync capability or not.
|
||||
isObjectLipsync(object_uuid: string) {
|
||||
const object = this.scene.get_object_by_uuid(object_uuid);
|
||||
let hasLipsync = false;
|
||||
if (object) {
|
||||
object.traverse((c: THREE.Object3D) => {
|
||||
if (c instanceof THREE.Mesh) {
|
||||
if (c.morphTargetInfluences && c.morphTargetDictionary) {
|
||||
const blendShapeIndexE = c.morphTargetDictionary["E"];
|
||||
// console.log(c.morphTargetDictionary, blendShapeIndexE)
|
||||
if (blendShapeIndexE !== null) {
|
||||
hasLipsync = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
return hasLipsync;
|
||||
}
|
||||
|
||||
// Returns if the object is locked or unlocked.
|
||||
isObjectLocked(object_uuid: string): boolean {
|
||||
const object = this.scene.get_object_by_uuid(object_uuid);
|
||||
@@ -48,31 +27,20 @@ export class SceneUtils {
|
||||
}
|
||||
return object.userData["locked"];
|
||||
}
|
||||
//console.log("No object found.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Locks or unlocks and object and returns its new state,
|
||||
lockUnlockObject(object_uuid: string): boolean {
|
||||
// Pure userData mutation: flips the locked flag and returns the new
|
||||
// value. Higher-level wiring (gizmo attach/detach, selection refresh)
|
||||
// lives on SelectionBridge.lockUnlockObject.
|
||||
toggleObjectLocked(object_uuid: string): boolean {
|
||||
const object = this.scene.get_object_by_uuid(object_uuid);
|
||||
if (object) {
|
||||
if (object.userData["locked"] == undefined) {
|
||||
object.userData["locked"] = false;
|
||||
}
|
||||
object.userData["locked"] = !object.userData["locked"];
|
||||
|
||||
if (object.userData["locked"]) {
|
||||
this.removeTransformControls(false);
|
||||
} else {
|
||||
this.editor.gizmo.addToScene(this.scene.scene);
|
||||
const selected = this.editor.sceneManager?.selected_objects?.[0];
|
||||
if (selected) this.editor.gizmo.attach(selected);
|
||||
}
|
||||
|
||||
return object.userData["locked"];
|
||||
if (!object) return false;
|
||||
if (object.userData["locked"] == undefined) {
|
||||
object.userData["locked"] = false;
|
||||
}
|
||||
//console.log("No object found.");
|
||||
return false;
|
||||
object.userData["locked"] = !object.userData["locked"];
|
||||
return object.userData["locked"];
|
||||
}
|
||||
|
||||
// Removes transform controls and publishes selected.
|
||||
@@ -86,31 +54,13 @@ export class SceneUtils {
|
||||
}
|
||||
if (remove_outline) {
|
||||
outlinePass.selectedObjects = [];
|
||||
this.editor.publishSelect();
|
||||
this.editor.selection.publishSelect();
|
||||
}
|
||||
this.editor.gizmo.detach();
|
||||
this.editor.gizmo.removeFromScene(this.editor.activeScene.scene);
|
||||
if (remove_outline) outlinePass.selectedObjects = [];
|
||||
}
|
||||
|
||||
// TO UPDATE selected objects in the scene might want to add to the scene ...
|
||||
async setSelectedObject(position: XYZ, rotation: XYZ, scale: XYZ) {
|
||||
if (this.editor.sceneManager?.selected_objects) {
|
||||
const 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the "check sum" of the editors selected object.
|
||||
getSelectedSum(): number {
|
||||
if (this.editor.sceneManager?.selected_objects === undefined) {
|
||||
@@ -206,7 +156,7 @@ function removeObject3D(object3D) {
|
||||
|
||||
usePageSceneStore.getState().removeSceneObject(uuid);
|
||||
this.editor.selection.selected = undefined;
|
||||
this.editor.publishSelect();
|
||||
this.editor.selection.publishSelect();
|
||||
usePageSceneStore.getState().hideObjectPanel();
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,7 @@ import Scene from "./scene";
|
||||
import { MouseControls } from "./keybinds_controls";
|
||||
import { ClipGroup } from "~/enums";
|
||||
import { AssetType } from "~/enums";
|
||||
import { XYZ } from "../datastructures/common";
|
||||
import { usePageSceneStore } from "~/pages/PageScene/PageSceneStore";
|
||||
import {
|
||||
CommandInputTypes,
|
||||
@@ -257,6 +258,20 @@ export class SceneManager implements SceneManagerAPI {
|
||||
}
|
||||
}
|
||||
|
||||
// Writes position/rotation/scale onto the currently selected object.
|
||||
// Rotation is taken in degrees (matches the Object Panel input).
|
||||
public updateSelectedTransform(position: XYZ, rotation: XYZ, scale: XYZ) {
|
||||
const object = this.selected_objects?.[0];
|
||||
if (!object) return;
|
||||
object.position.set(position.x, position.y, position.z);
|
||||
object.rotation.set(
|
||||
THREE.MathUtils.degToRad(rotation.x),
|
||||
THREE.MathUtils.degToRad(rotation.y),
|
||||
THREE.MathUtils.degToRad(rotation.z),
|
||||
);
|
||||
object.scale.set(scale.x, scale.y, scale.z);
|
||||
}
|
||||
|
||||
// Converts a 3d object to signal item format.
|
||||
private convert_object(
|
||||
object: THREE.Object3D,
|
||||
|
||||
Reference in new issue
Block a user