Compare commits
2 Commits
8bc56a433d
...
b3c7979f87
| Author | SHA1 | Date |
|---|---|---|
|
|
b3c7979f87 | |
|
|
6fc89cd8c9 |
|
|
@ -3,9 +3,14 @@ import type { Character } from "../types";
|
|||
|
||||
export const CharacterView = observer(function ({ character }: { character: Character }) {
|
||||
|
||||
return <mesh position={character.position} rotation={character.look}>
|
||||
const pos = character.position;
|
||||
|
||||
return <mesh
|
||||
position={[pos[0] + 0.5, pos[1] + 0.5, pos[2] + 0.5]}
|
||||
rotation={character.look}
|
||||
>
|
||||
<boxGeometry args={[0.8, 0.8, 0.8]} />
|
||||
<meshStandardMaterial color="yellow" />
|
||||
</mesh>
|
||||
</mesh >
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -1,14 +1,37 @@
|
|||
import { observer } from "mobx-react-lite";
|
||||
import type { ObjectInstance } from "../types";
|
||||
import type { ObjectInstance, ObjectType } from "../types";
|
||||
import { useRef, type RefObject } from "react";
|
||||
import type { Group } from "three";
|
||||
import { TransformControls, useHelper } from "@react-three/drei";
|
||||
import { Instance, Instances, TransformControls, useHelper } from "@react-three/drei";
|
||||
import { BoxHelper } from "three";
|
||||
import type { ThreeEvent } from "@react-three/fiber";
|
||||
import { state } from "../state";
|
||||
import { nextSelectionEditMode } from "../state/worldEditorState";
|
||||
import type { R3 } from "../types/3d";
|
||||
|
||||
type VoxelGroup = {
|
||||
id: string;
|
||||
color: string;
|
||||
opacity: number;
|
||||
positions: [number, number, number][];
|
||||
};
|
||||
|
||||
function voxelGroups(objectType: ObjectType): VoxelGroup[] {
|
||||
const map = new Map<string, VoxelGroup>();
|
||||
for (const idx in objectType.voxels) {
|
||||
const v = objectType.voxels[idx];
|
||||
const vt = state.world.getVoxelTypeById(v.typeId);
|
||||
const color = (v.color ?? vt?.color) ?? 'white';
|
||||
const opacity = (v.opacity ?? vt?.opacity) ?? 1;
|
||||
const key = `${color}-${opacity}`;
|
||||
if (!map.has(key))
|
||||
map.set(key, { id: key, color, opacity, positions: [] });
|
||||
const p = v.position;
|
||||
map.get(key)!.positions.push([p[0] + 0.5, p[1] + 0.5, p[2] + 0.5]);
|
||||
}
|
||||
return [...map.values()];
|
||||
}
|
||||
|
||||
type ObjectViewProps = {
|
||||
object: ObjectInstance;
|
||||
}
|
||||
|
|
@ -66,25 +89,16 @@ export const ObjectView = observer(function ({ object }: ObjectViewProps) {
|
|||
onClick={handleClick}
|
||||
>
|
||||
{
|
||||
objectType.voxels.map((v, idx) => {
|
||||
const vt = state.world.getVoxelTypeById(v.typeId);
|
||||
const color = (v.color ?? vt?.color) ?? 'white';
|
||||
const opacity = (v.opacity ?? vt?.opacity) ?? 1;
|
||||
return (
|
||||
<mesh
|
||||
key={idx}
|
||||
name={`voxel ${idx} [${v.position}]`}
|
||||
position={v.position}
|
||||
>
|
||||
<boxGeometry args={[1, 1, 1]} />
|
||||
<meshStandardMaterial
|
||||
color={color}
|
||||
opacity={opacity}
|
||||
transparent={opacity < 1}
|
||||
/>
|
||||
</mesh>
|
||||
);
|
||||
})
|
||||
voxelGroups(objectType).map((vg) => (
|
||||
<Instances key={vg.id}>
|
||||
<boxGeometry args={[1, 1, 1]} />
|
||||
<meshStandardMaterial color={vg.color} opacity={vg.opacity} transparent={vg.opacity < 1} />
|
||||
{
|
||||
vg.positions
|
||||
.map((pos, i) => <Instance key={i} position={pos} />)
|
||||
}
|
||||
</Instances>
|
||||
))
|
||||
}
|
||||
</group>
|
||||
</>);
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
import { useLayoutEffect, useRef } from 'react';
|
||||
import type React from 'react';
|
||||
import { useThree } from '@react-three/fiber';
|
||||
import { OrbitControls } from '@react-three/drei';
|
||||
import { Grid, OrbitControls } from '@react-three/drei';
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import { state } from '../state';
|
||||
import { SceneView } from './SceneView';
|
||||
import type { Pos3 } from '../types/3d';
|
||||
import type { OrthographicCamera, PerspectiveCamera } from 'three';
|
||||
import { type OrthographicCamera, type PerspectiveCamera } from 'three';
|
||||
|
||||
const CameraSync = observer(function ({ camera }: { camera: Pos3 }) {
|
||||
const { camera: threeCamera } = useThree();
|
||||
|
|
@ -62,6 +62,12 @@ export const SceneEditorView = observer(function () {
|
|||
enableDamping={false}
|
||||
/>
|
||||
<CameraSync camera={state.worldEditor.camera} />
|
||||
<Grid
|
||||
sectionThickness={0.5}
|
||||
cellThickness={0}
|
||||
sectionColor="white"
|
||||
infiniteGrid
|
||||
/>
|
||||
<SceneView scene={state.worldEditor.scene} />
|
||||
</>);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,5 +1,22 @@
|
|||
import { Canvas } from '@react-three/fiber';
|
||||
import { Canvas, useFrame, useThree } from '@react-three/fiber';
|
||||
import { Stats } from '@react-three/drei';
|
||||
import { useRef } from 'react';
|
||||
|
||||
function RenderInfoUpdater({ domRef }: { domRef: React.RefObject<HTMLDivElement | null> }) {
|
||||
const { gl } = useThree();
|
||||
useFrame(() => {
|
||||
if (!domRef.current)
|
||||
return;
|
||||
|
||||
const { calls, triangles } = gl.info.render;
|
||||
const shaders = gl.info.programs?.length ?? 0;
|
||||
const { geometries, textures } = gl.info.memory;
|
||||
|
||||
domRef.current.textContent = `draw: ${calls} | tri: ${triangles} | shaders: ${shaders} | geometries: ${geometries} | textures: ${textures}`;
|
||||
});
|
||||
return null;
|
||||
}
|
||||
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import { state } from '../state';
|
||||
import { GameView } from './GameView';
|
||||
|
|
@ -11,6 +28,7 @@ const IconPlay = () => <svg viewBox="0 0 14 14"><polygon points="3,1 13,7 3,13"
|
|||
|
||||
export const ThreeView = observer(function () {
|
||||
const isGame = state.isGamePlaying;
|
||||
const infoRef = useRef<HTMLDivElement>(null);
|
||||
return (
|
||||
<div style={{ position: 'relative', width: '800px', height: '600px', border: '1px solid #333', borderRadius: '8px', overflow: 'hidden' }}>
|
||||
<Canvas
|
||||
|
|
@ -18,8 +36,15 @@ export const ThreeView = observer(function () {
|
|||
onPointerMissed={() => state.worldEditor.resetSelectedObject()}
|
||||
>
|
||||
<Stats />
|
||||
<RenderInfoUpdater domRef={infoRef} />
|
||||
{isGame ? <GameView /> : <SceneEditorView />}
|
||||
</Canvas>
|
||||
<div ref={infoRef} style={{
|
||||
position: 'absolute', bottom: 8, left: 8,
|
||||
color: 'white', fontSize: 11, fontFamily: 'monospace',
|
||||
background: 'rgba(0,0,0,0.5)', padding: '2px 6px', borderRadius: 3,
|
||||
pointerEvents: 'none',
|
||||
}} />
|
||||
<div style={{ position: 'absolute', top: 8, right: 8, display: 'flex', gap: 4 }}>
|
||||
{
|
||||
state.game
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { makeAutoObservable, reaction, toJS } from "mobx";
|
||||
import { WorldFactory } from "../model/worldFactory";
|
||||
import type { ObjectType, World } from "../types";
|
||||
import type { ObjectInstance, ObjectType, World } from "../types";
|
||||
import type { VoxelType } from "../types/voxel";
|
||||
import { state } from "./rootState";
|
||||
import { DEFAULT_VOXEL_TYPES } from "../model/defaultVoxelTypes";
|
||||
|
|
@ -39,7 +39,17 @@ export class WorldState {
|
|||
public loadMock() {
|
||||
console.log('Mocking world...');
|
||||
|
||||
const objectId1 = 'object1';
|
||||
const objects = Array(3).fill(0)
|
||||
.map((_, idx) => ({
|
||||
id: `obj${idx}`,
|
||||
typeId: 'wolf',
|
||||
position: [idx * 10 - 10, 0, 0],
|
||||
rotation: [0, 0, 0],
|
||||
scale: [1, 1, 1],
|
||||
}));
|
||||
const objectMap = Object.fromEntries(
|
||||
objects.map((obj) => [obj.id, obj]),
|
||||
) as Record<string, ObjectInstance>
|
||||
|
||||
this.data = {
|
||||
objectTypes: {
|
||||
|
|
@ -59,16 +69,7 @@ export class WorldState {
|
|||
position: [0, 0, 0],
|
||||
look: [0, 0, 0],
|
||||
},
|
||||
objects: {
|
||||
[objectId1]: {
|
||||
id: objectId1,
|
||||
typeId: 'wolf',
|
||||
position: [0, 0, 0],
|
||||
rotation: [0, 0, 0],
|
||||
scale: [1, 1, 1],
|
||||
},
|
||||
|
||||
},
|
||||
objects: objectMap,
|
||||
},
|
||||
gameRules: {
|
||||
gravity: true,
|
||||
|
|
@ -77,6 +78,7 @@ export class WorldState {
|
|||
playing: false,
|
||||
},
|
||||
};
|
||||
console.log(objects);
|
||||
state.worldEditor.resetSelectedObject();
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue