blockly3d/src/state/worldEditorState.ts

148 lines
4.1 KiB
TypeScript

import { makeAutoObservable, runInAction } from "mobx";
import type { WorldState } from "./worldState";
import { type ObjectInstance, type Pos3, type R3, type V3, type RuntimeScene } from "../types";
import { createObjectInstance } from "../utils/object";
import { randomId } from "../utils";
import { state } from "./rootState";
import { populateRuntimeObject } from "../utils/runtime";
export const ObjectEditModeEnum = [
'translate',
'rotate',
'scale',
] as const;
type ObjectEditModeTuple = typeof ObjectEditModeEnum;
export type ObjectEditMode = ObjectEditModeTuple[number];
export function nextObjectEditMode(mode: ObjectEditMode | undefined): ObjectEditMode {
if (mode === undefined)
return ObjectEditModeEnum[0];
const idx = ObjectEditModeEnum.indexOf(mode);
return ObjectEditModeEnum[(idx + 1) % ObjectEditModeEnum.length];
}
export const ObjectTypeEditModeEnum = [
'scripts',
] as const;
type ObjectTypeEditModeTuple = typeof ObjectTypeEditModeEnum;
export type ObjectTypeEditMode = ObjectTypeEditModeTuple[number];
export function nextObjectTypeEditMode(mode: ObjectTypeEditMode | undefined): ObjectTypeEditMode {
if (mode === undefined)
return ObjectTypeEditModeEnum[0];
const idx = ObjectTypeEditModeEnum.indexOf(mode);
return ObjectTypeEditModeEnum[(idx + 1) % ObjectTypeEditModeEnum.length];
}
export type SelectedObject = {
type: 'object',
id: string;
editMode: ObjectEditMode;
}
export type SelectedObjectType = {
type: 'objectType',
id: string;
editMode: ObjectTypeEditMode;
}
export type Selection = SelectedObject | SelectedObjectType;
export class WorldEditorState {
private readonly world: WorldState;
public selection: Selection | undefined;
constructor(world: WorldState) {
this.world = world;
makeAutoObservable(this);
}
public get isEnabled(): boolean {
return !state.game;
}
public setCamera(value: Pos3): void {
this.world.data.editorCamera = value;
}
public setSelection(value: Selection | undefined): void {
this.selection = value;
}
public setSelectedObject(value: Omit<SelectedObject, 'type'> | undefined): void {
this.setSelection(value
? {
type: 'object',
...value,
}
: undefined,
);
}
public setSelectedObjectType(value: Omit<SelectedObjectType, 'type'> | undefined): void {
this.setSelection(value
? {
type: 'objectType',
...value,
}
: undefined,
);
}
public resetSelection() {
this.setSelection(undefined);
}
public get scene(): RuntimeScene {
return this.world.data.initialScene;
}
public get camera(): Pos3 {
return this.world.data.editorCamera;
}
public setObjectTransform(id: string, position: V3, rotation: R3, scale: V3): void {
const obj = this.scene.objects[id];
if (!obj)
return;
runInAction(() => { // all in one go
obj.position = position;
obj.rotation = rotation;
obj.scale = scale;
});
}
public addObjectInstanceAtRandomPosition(typeId: string): ObjectInstance {
return this.addObjectInstance(
typeId,
{ x: Math.random() * 3, y: Math.random() * 3, z: Math.random() * 1 },
);
}
public addObjectInstance(
typeId: string,
pos: { x: number; y: number; z: number; },
): ObjectInstance {
const obj = populateRuntimeObject(createObjectInstance(randomId(), typeId), this.world.data);
obj.position = [pos.x, pos.y, pos.z];
obj.rotation = [0, 0, 0];
this.scene.objects[obj.id] = obj;
this.setSelectedObject({ id: obj.id, editMode: 'translate' });
return obj;
}
public deleteObject(objectId: string) {
if (this.selection?.type ==='object' && this.selection.id === objectId)
this.resetSelection();
delete (this.scene.objects[objectId]);
}
}