custom voxel mesh builder
object visibility
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import { autorun, makeAutoObservable, toJS } from "mobx";
|
||||
import type { WorldState } from "./worldState";
|
||||
import type { Game, GameObjectInstance, Pos3, RuntimeGameScene, ScriptContext, ScriptCoroutineContext } from "../types";
|
||||
import type { Game, GameObjectInstance, GameScene, Pos3, RuntimeGameScene, ScriptContext, ScriptCoroutineContext } from "../types";
|
||||
import type { CameraProps } from "@react-three/fiber";
|
||||
import type { ScriptGameEventBus } from "../scripts/eventBus";
|
||||
import { GameFactory } from "../model/gameFactory";
|
||||
@@ -18,7 +18,7 @@ export class GameState {
|
||||
private coroutines = new ScriptCoroutines();
|
||||
private eventBus: ScriptGameEventBus;
|
||||
|
||||
private _buildGameObjects() {
|
||||
private sceneObjectsAsSaveables(): GameScene['objects'] {
|
||||
return Object.fromEntries(
|
||||
Object.entries(this.scene.objects).map(([id, obj]) => [id, {
|
||||
id: obj.id,
|
||||
@@ -31,17 +31,25 @@ export class GameState {
|
||||
version: obj.version,
|
||||
linearVelocity: toJS(obj.linearVelocity),
|
||||
angularVelocity: toJS(obj.angularVelocity),
|
||||
visible: toJS(obj.visible),
|
||||
} satisfies GameObjectInstance])
|
||||
) as unknown as RuntimeGameScene['objects'];
|
||||
}
|
||||
|
||||
public get asSaveableGame(): Game<GameScene> {
|
||||
return {
|
||||
paused: this.isPaused,
|
||||
time: this.time,
|
||||
scene: {
|
||||
playerObjectId: this.scene.playerObjectId,
|
||||
objects: this.sceneObjectsAsSaveables(),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
private startAutoSave() {
|
||||
return autorun(() => {
|
||||
GameFactory.save({
|
||||
paused: this.isPaused,
|
||||
time: this.time,
|
||||
scene: { playerObjectId: this.scene.playerObjectId, objects: this._buildGameObjects() },
|
||||
});
|
||||
this.save();
|
||||
}, { delay: 5000 });
|
||||
}
|
||||
|
||||
@@ -118,11 +126,7 @@ export class GameState {
|
||||
|
||||
public save(): void {
|
||||
console.log('Saving game...');
|
||||
GameFactory.save({
|
||||
paused: this.isPaused,
|
||||
time: this.time,
|
||||
scene: { playerObjectId: this.scene.playerObjectId, objects: this._buildGameObjects() },
|
||||
});
|
||||
GameFactory.save(this.asSaveableGame);
|
||||
}
|
||||
|
||||
public resume(): void {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { makeAutoObservable } from "mobx";
|
||||
import { state } from "./rootState";
|
||||
import type { ReactNode } from "react";
|
||||
import { IconRun, IconTrash, IconPlus, IconCubePlus, IconCubeOff, IconEdit } from '@tabler/icons-react';
|
||||
import { IconRun, IconTrash, IconPlus, IconCubePlus, IconCubeOff, IconEdit, IconEye, IconEyeClosed } from '@tabler/icons-react';
|
||||
|
||||
export type MenuNodeAction = {
|
||||
id: string;
|
||||
@@ -53,6 +53,13 @@ export class MenuState {
|
||||
});
|
||||
}
|
||||
|
||||
actions.push({
|
||||
id: 'toggle-object-instance-visibility',
|
||||
content: o.visible ? <IconEye size="1em" /> : <IconEyeClosed size="1em" />,
|
||||
tooltip: 'Toggle visibility',
|
||||
onClick: () => { editor.setObjectVisible(o.id, !o.visible); },
|
||||
});
|
||||
|
||||
return {
|
||||
id: `o-${o.id}`,
|
||||
title: <>
|
||||
|
||||
@@ -147,6 +147,11 @@ export class WorldEditorState {
|
||||
delete (this.scene.objects[objectId]);
|
||||
}
|
||||
|
||||
public setObjectVisible(objectId: string, value: boolean) {
|
||||
if (this.scene.objects[objectId])
|
||||
this.scene.objects[objectId].visible = value;
|
||||
}
|
||||
|
||||
public addObjectType(): ObjectType {
|
||||
const objectType: ObjectType = {
|
||||
id: randomId(),
|
||||
@@ -165,39 +170,39 @@ export class WorldEditorState {
|
||||
|
||||
public addVoxelToObjectType(typeId: string, voxel: Voxel): void {
|
||||
const objectType = this.world.getObjectTypeById(typeId);
|
||||
if (!objectType)
|
||||
return;
|
||||
if (!objectType)
|
||||
return;
|
||||
const occupied = objectType.voxels.some(v =>
|
||||
v.position[0] === voxel.position[0] &&
|
||||
v.position[1] === voxel.position[1] &&
|
||||
v.position[2] === voxel.position[2]
|
||||
);
|
||||
if (!occupied)
|
||||
objectType.voxels.push(voxel);
|
||||
if (!occupied)
|
||||
objectType.voxels.push(voxel);
|
||||
}
|
||||
|
||||
public removeVoxelFromObjectType(typeId: string, position: V3): void {
|
||||
const objectType = this.world.getObjectTypeById(typeId);
|
||||
if (!objectType)
|
||||
return;
|
||||
if (!objectType)
|
||||
return;
|
||||
const idx = objectType.voxels.findIndex(v =>
|
||||
v.position[0] === position[0] &&
|
||||
v.position[1] === position[1] &&
|
||||
v.position[2] === position[2]
|
||||
);
|
||||
if (idx !== -1)
|
||||
objectType.voxels.splice(idx, 1);
|
||||
if (idx !== -1)
|
||||
objectType.voxels.splice(idx, 1);
|
||||
}
|
||||
|
||||
public refreshObjectTypeCaches(typeId: string): void {
|
||||
const objectType = this.world.getObjectTypeById(typeId);
|
||||
if (!objectType)
|
||||
return;
|
||||
if (!objectType)
|
||||
return;
|
||||
const world = this.world.data;
|
||||
const now = Date.now();
|
||||
for (const obj of Object.values(this.scene.objects)) {
|
||||
if (obj.typeId !== typeId)
|
||||
continue;
|
||||
if (obj.typeId !== typeId)
|
||||
continue;
|
||||
obj.cache.voxelGroups = getObjectVoxelGroups(objectType, world.voxelTypes);
|
||||
obj.cache.colliderMesh = buildObjectTrimesh(objectType, world.voxelTypes);
|
||||
obj.cache.boundingBox = computeBoundingBox(objectType);
|
||||
|
||||
Reference in New Issue
Block a user