Compare commits

..

No commits in common. "e520eb07c5a050029567f83bc91582809d9a962d" and "42e6d171679e185ac36d41c536b631f4da3d7589" have entirely different histories.

6 changed files with 30 additions and 114 deletions

View File

@ -1,9 +1,8 @@
import { observer } from "mobx-react-lite"; import { observer } from "mobx-react-lite";
import type { ObjectInstance } from "../types"; import type { ObjectInstance } from "../types";
import { useEffect, useRef, type RefObject } from "react"; import { useEffect, useRef, type RefObject } from "react";
import type { Group } from "three"; import type { Group, Mesh } from "three";
import { Edges, TransformControls } from "@react-three/drei"; import { Edges, TransformControls } from "@react-three/drei";
import type { ThreeEvent } from "@react-three/fiber";
import { state } from "../state"; import { state } from "../state";
import { nextSelectionEditMode } from "../state/worldEditor"; import { nextSelectionEditMode } from "../state/worldEditor";
import type { R3 } from "../types/3d"; import type { R3 } from "../types/3d";
@ -39,11 +38,9 @@ export const ObjectView = observer(function ({ object }: ObjectViewProps) {
}; };
}, [isSelected]); }, [isSelected]);
const handleClick = (e: ThreeEvent<MouseEvent>) => { const handleClick = (e: { stopPropagation: () => void }) => {
if (!state.worldEditor.isEnabled) if (!state.worldEditor.isEnabled)
return; return;
if (e.delta > 5)
return;
e.stopPropagation(); e.stopPropagation();
state.worldEditor.setSelectedObject(object.id, nextSelectionEditMode(selectionMode)); state.worldEditor.setSelectedObject(object.id, nextSelectionEditMode(selectionMode));

View File

@ -1,18 +0,0 @@
import { observer } from "mobx-react-lite";
import type { Scene } from "../types";
import { CharacterView } from "./CharacterView";
import { ObjectView } from "./ObjectView";
type SceneViewProps = {
scene: Scene,
}
export const SceneView = observer(function ({ scene }: SceneViewProps) {
return (<>
<ambientLight intensity={0.5} />
<directionalLight position={[5, 5, 5]} intensity={1} />
{Object.values(scene.objects).map((obj) =>
<ObjectView key={obj.id} object={obj} />)}
<CharacterView />
</>);
});

View File

@ -1,23 +1,11 @@
import { useLayoutEffect, useRef } from 'react'; import { useRef } from 'react';
import type React from 'react'; import type React from 'react';
import { useFrame, useThree } from '@react-three/fiber'; import { useFrame } from '@react-three/fiber';
import { OrbitControls } from '@react-three/drei'; import { OrbitControls } from '@react-three/drei';
import { observer } from 'mobx-react-lite'; import { observer } from 'mobx-react-lite';
import { state } from '../state'; import { state } from '../state';
import { SceneView } from './SceneView'; import { ObjectView } from './ObjectView';
import { CharacterView } from './CharacterView';
const CameraSync = observer(function () {
const { camera } = useThree();
const cam = state.world.currentCamera; // MobX tracks this; re-renders on change
useLayoutEffect(() => {
camera.position.set(cam.position[0], cam.position[1], cam.position[2]);
camera.rotation.set(cam.look[0], cam.look[1], cam.look[2]);
camera.updateProjectionMatrix();
});
return null;
});
export const WorldView = observer(function () { export const WorldView = observer(function () {
const world = state.world; const world = state.world;
@ -46,7 +34,9 @@ export const WorldView = observer(function () {
makeDefault makeDefault
enableDamping={false} enableDamping={false}
/> />
<CameraSync /> <ambientLight intensity={0.5} />
<SceneView scene={state.world.currentScene} /> <directionalLight position={[5, 5, 5]} intensity={1} />
{Object.values(world.currentScene.objects).map((obj) => <ObjectView key={obj.id} object={obj} />)}
<CharacterView />
</>) </>)
}); });

View File

@ -1,62 +0,0 @@
import type { Voxel } from "../../types/voxel";
const G = '#808080'; // gray fur
const L = '#d0c8b8'; // light beige-gray (snout / face)
const D = '#484040'; // dark gray (outer ear)
const E = '#e8b820'; // amber (eyes)
const N = '#181414'; // near-black (nose, pupils)
const P = '#c06858'; // pink (inner ear)
function v(x: number, y: number, z: number, color: string): Voxel {
return { typeId: 'stone', position: [x, y, z], color };
}
// Wolf faces +Z (nose at Z=6). Head spans 7×7×7.
export const wolf: Voxel[] = [
// ── Back of skull Z=0 ───────────────────────────────────────────
v(2,3,0,G), v(3,3,0,G), v(4,3,0,G),
v(2,4,0,G), v(3,4,0,G), v(4,4,0,G),
v(2,5,0,G), v(3,5,0,G), v(4,5,0,G),
// ── Skull Z=1 ───────────────────────────────────────────────────
v(1,3,1,G), v(2,3,1,G), v(3,3,1,G), v(4,3,1,G), v(5,3,1,G),
v(1,4,1,G), v(2,4,1,G), v(3,4,1,G), v(4,4,1,G), v(5,4,1,G),
v(1,5,1,G), v(2,5,1,G), v(3,5,1,G), v(4,5,1,G), v(5,5,1,G),
v(2,6,1,G), v(3,6,1,G), v(4,6,1,G),
// ── Skull + ear bases Z=2 ───────────────────────────────────────
v(1,3,2,G), v(2,3,2,G), v(3,3,2,G), v(4,3,2,G), v(5,3,2,G),
v(1,4,2,G), v(2,4,2,G), v(3,4,2,G), v(4,4,2,G), v(5,4,2,G),
v(0,5,2,G), v(1,5,2,G), v(2,5,2,G), v(3,5,2,G), v(4,5,2,G), v(5,5,2,G), v(6,5,2,G),
v(1,6,2,G), v(2,6,2,G), v(3,6,2,G), v(4,6,2,G), v(5,6,2,G),
v(0,6,2,D), v(6,6,2,D),
// ── Face + ears Z=3 ─────────────────────────────────────────────
v(1,3,3,L), v(2,3,3,L), v(3,3,3,L), v(4,3,3,L), v(5,3,3,L),
v(1,4,3,G), v(2,4,3,G), v(3,4,3,G), v(4,4,3,G), v(5,4,3,G),
// ears: dark outer, pink inner
v(0,5,3,D), v(1,5,3,P), v(2,5,3,G), v(3,5,3,G), v(4,5,3,G), v(5,5,3,P), v(6,5,3,D),
v(0,6,3,D), v(1,6,3,P), v(2,6,3,G), v(3,6,3,G), v(4,6,3,G), v(5,6,3,P), v(6,6,3,D),
// snout start
v(2,1,3,L), v(3,1,3,L), v(4,1,3,L),
v(2,2,3,L), v(3,2,3,L), v(4,2,3,L),
// ── Face + amber eyes + snout Z=4 ───────────────────────────────
v(1,3,4,L), v(2,3,4,L), v(3,3,4,L), v(4,3,4,L), v(5,3,4,L),
v(1,4,4,G), v(2,4,4,E), v(3,4,4,G), v(4,4,4,E), v(5,4,4,G),
v(1,5,4,G), v(2,5,4,G), v(3,5,4,G), v(4,5,4,G), v(5,5,4,G),
v(2,1,4,L), v(3,1,4,L), v(4,1,4,L),
v(2,2,4,L), v(3,2,4,L), v(4,2,4,L),
// ── Front face + pupils + snout Z=5 ─────────────────────────────
v(1,3,5,L), v(2,3,5,L), v(3,3,5,L), v(4,3,5,L), v(5,3,5,L),
v(1,4,5,G), v(2,4,5,N), v(3,4,5,G), v(4,4,5,N), v(5,4,5,G),
v(1,5,5,G), v(2,5,5,G), v(3,5,5,G), v(4,5,5,G), v(5,5,5,G),
v(2,1,5,L), v(3,1,5,L), v(4,1,5,L),
v(2,2,5,L), v(3,2,5,L), v(4,2,5,L),
// ── Snout tip + nose Z=6 ────────────────────────────────────────
v(2,2,6,L), v(3,2,6,L), v(4,2,6,L),
v(2,1,6,L), v(3,1,6,L), v(4,1,6,L),
v(2,3,6,N), v(3,3,6,N), v(4,3,6,N), // nose
];

View File

@ -7,7 +7,6 @@ import { clone } from "../utils";
import type { VoxelType } from "../types/voxel"; import type { VoxelType } from "../types/voxel";
import { state } from "./root"; import { state } from "./root";
import { DEFAULT_VOXEL_TYPES } from "../model/defaultVoxelTypes"; import { DEFAULT_VOXEL_TYPES } from "../model/defaultVoxelTypes";
import { wolf } from "../model/objectPrefabs/wolf";
export class WorldState { export class WorldState {
public data: World = WorldFactory.create(); public data: World = WorldFactory.create();
@ -24,14 +23,22 @@ export class WorldState {
} }
public loadMock() { public loadMock() {
const objTypeId = 'test1';
const voxelTypeId = 'glass';
const objectId1 = 'object1'; const objectId1 = 'object1';
const objectId2 = 'object2';
this.data = { this.data = {
objectTypes: { objectTypes: {
wolf: { [objTypeId]: {
id: 'wolf', id: objTypeId,
name: 'Wolf', name: 'Test Object',
voxels: wolf, voxels: [
{
typeId: voxelTypeId,
position: [0, 0, 0],
},
],
}, },
}, },
voxelTypes: DEFAULT_VOXEL_TYPES, voxelTypes: DEFAULT_VOXEL_TYPES,
@ -47,12 +54,18 @@ export class WorldState {
objects: { objects: {
[objectId1]: { [objectId1]: {
id: objectId1, id: objectId1,
typeId: 'wolf', typeId: objTypeId,
position: [0, 0, 0], position: [0, 0, 0],
rotation: [0, 0, 0], rotation: [0, 0, 0],
scale: [1, 1, 1], scale: [1, 1, 1],
}, },
[objectId2]: {
id: objectId2,
typeId: objTypeId,
position: [1, 0, 0],
rotation: [0, 0, 0],
scale: [1, 1, 1],
},
}, },
}, },
gameRules: { gameRules: {

View File

@ -62,10 +62,6 @@ export class WorldEditorState {
return this.world.data.initialScene; return this.world.data.initialScene;
} }
public get camera(): Pos3 {
return this.world.data.editorCamera;
}
public mutateWorld(mutation: Partial<World>): void { public mutateWorld(mutation: Partial<World>): void {
this.world.data = { this.world.data = {
...this.world.data, ...this.world.data,