event systems
This commit is contained in:
@@ -1,17 +1,87 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useScene } from "@pascal-app/core";
|
import {
|
||||||
|
emitter,
|
||||||
|
ItemNode,
|
||||||
|
sceneRegistry,
|
||||||
|
useScene,
|
||||||
|
WallNode,
|
||||||
|
} from "@pascal-app/core";
|
||||||
import { Viewer } from "@pascal-app/viewer";
|
import { Viewer } from "@pascal-app/viewer";
|
||||||
|
import { Stats } from "@react-three/drei";
|
||||||
|
import { useEffect, useRef } from "react";
|
||||||
|
import { Mesh } from "three";
|
||||||
|
import { MeshBasicNodeMaterial } from "three/webgpu";
|
||||||
|
|
||||||
export default function Editor() {
|
export default function Editor() {
|
||||||
return (
|
return (
|
||||||
<div className="w-full h-full bg-pink-50">
|
<div className="w-full h-full bg-pink-50">
|
||||||
<LevelModeSwitcher />
|
<LevelModeSwitcher />
|
||||||
<Viewer />
|
<Viewer>
|
||||||
|
<Selector />
|
||||||
|
<Stats />
|
||||||
|
</Viewer>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const Selector = () => {
|
||||||
|
const selectedItemId = useRef<ItemNode["id"]>(null);
|
||||||
|
const itemSelectedAt = useRef<number>(0);
|
||||||
|
useEffect(() => {
|
||||||
|
emitter.on("item:click", (event) => {
|
||||||
|
if (Date.now() - itemSelectedAt.current < 50) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
itemSelectedAt.current = Date.now();
|
||||||
|
if (selectedItemId.current === event.node.id) {
|
||||||
|
selectedItemId.current = null;
|
||||||
|
console.log("Deselected item:", event.node.id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
selectedItemId.current = event.node.id;
|
||||||
|
const itemMesh = sceneRegistry.nodes.get(event.node.id);
|
||||||
|
if (!itemMesh) return;
|
||||||
|
itemMesh.traverse((child) => {
|
||||||
|
if ((child as Mesh).isMesh) {
|
||||||
|
(child as Mesh).material = new MeshBasicNodeMaterial({
|
||||||
|
color: "yellow",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log("Selected item:", event.node.id);
|
||||||
|
});
|
||||||
|
|
||||||
|
emitter.on("wall:move", (event) => {
|
||||||
|
const wallNode = event.node as WallNode;
|
||||||
|
|
||||||
|
if (wallNode.children.length === 0) return;
|
||||||
|
const itemId = wallNode.children[0];
|
||||||
|
if (!itemId) return;
|
||||||
|
if (selectedItemId.current !== itemId) return;
|
||||||
|
const itemNode = useScene.getState().nodes[itemId];
|
||||||
|
const itemMesh = sceneRegistry.nodes.get(itemId);
|
||||||
|
if (!itemNode || !itemMesh) return;
|
||||||
|
|
||||||
|
itemMesh.position.set(
|
||||||
|
event.position[0],
|
||||||
|
event.position[1],
|
||||||
|
event.position[2]
|
||||||
|
);
|
||||||
|
useScene.getState().dirtyNodes.add(wallNode.id);
|
||||||
|
console.log(
|
||||||
|
"Wall move event:",
|
||||||
|
wallNode.id,
|
||||||
|
"Point position:",
|
||||||
|
event.position
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
const LevelModeSwitcher = () => {
|
const LevelModeSwitcher = () => {
|
||||||
const setLevelMode = useScene((state) => state.setLevelMode);
|
const setLevelMode = useScene((state) => state.setLevelMode);
|
||||||
const levelMode = useScene((state) => state.levelMode);
|
const levelMode = useScene((state) => state.levelMode);
|
||||||
|
|||||||
@@ -42,6 +42,7 @@
|
|||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"dedent": "^1.7.1",
|
"dedent": "^1.7.1",
|
||||||
|
"mitt": "^3.0.1",
|
||||||
"nanoid": "^5.1.6",
|
"nanoid": "^5.1.6",
|
||||||
"zod": "^4.3.5",
|
"zod": "^4.3.5",
|
||||||
"zustand": "^5.0.10",
|
"zustand": "^5.0.10",
|
||||||
@@ -752,6 +753,8 @@
|
|||||||
|
|
||||||
"minipass": ["minipass@7.1.2", "", {}, "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="],
|
"minipass": ["minipass@7.1.2", "", {}, "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="],
|
||||||
|
|
||||||
|
"mitt": ["mitt@3.0.1", "", {}, "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw=="],
|
||||||
|
|
||||||
"ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
|
"ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
|
||||||
|
|
||||||
"nanoid": ["nanoid@5.1.6", "", { "bin": { "nanoid": "bin/nanoid.js" } }, "sha512-c7+7RQ+dMB5dPwwCp4ee1/iV/q2P6aK1mTZcfr1BTuVlyW9hJYiMPybJCcnBlQtuSmTIWNeazm/zqNoZSSElBg=="],
|
"nanoid": ["nanoid@5.1.6", "", { "bin": { "nanoid": "bin/nanoid.js" } }, "sha512-c7+7RQ+dMB5dPwwCp4ee1/iV/q2P6aK1mTZcfr1BTuVlyW9hJYiMPybJCcnBlQtuSmTIWNeazm/zqNoZSSElBg=="],
|
||||||
|
|||||||
@@ -27,6 +27,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"dedent": "^1.7.1",
|
"dedent": "^1.7.1",
|
||||||
|
"mitt": "^3.0.1",
|
||||||
"nanoid": "^5.1.6",
|
"nanoid": "^5.1.6",
|
||||||
"zod": "^4.3.5",
|
"zod": "^4.3.5",
|
||||||
"zustand": "^5.0.10"
|
"zustand": "^5.0.10"
|
||||||
|
|||||||
@@ -0,0 +1,45 @@
|
|||||||
|
import mitt from "mitt";
|
||||||
|
import { ItemNode, WallNode } from "../schema";
|
||||||
|
import { AnyNode } from "../schema/types";
|
||||||
|
export interface GridEvent {
|
||||||
|
position: [number, number];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface NodeEvent {
|
||||||
|
node: AnyNode;
|
||||||
|
position: [number, number, number]; // [x, y, z] world coordinates
|
||||||
|
normal?: [number, number, number]; // [x, y, z] normal vector
|
||||||
|
stopPropagation: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface WallEvent extends NodeEvent {
|
||||||
|
node: WallNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ItemEvent extends NodeEvent {
|
||||||
|
node: ItemNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
type EditorEvents = {
|
||||||
|
"grid:click": GridEvent;
|
||||||
|
"grid:rightclick": GridEvent;
|
||||||
|
"grid:move": GridEvent;
|
||||||
|
"grid:double-click": GridEvent;
|
||||||
|
"grid:enter": GridEvent;
|
||||||
|
"grid:leave": GridEvent;
|
||||||
|
"grid:pointerdown": GridEvent;
|
||||||
|
"grid:pointerup": GridEvent;
|
||||||
|
"wall:click": WallEvent;
|
||||||
|
"wall:move": WallEvent;
|
||||||
|
"wall:enter": WallEvent;
|
||||||
|
"wall:leave": WallEvent;
|
||||||
|
"wall:pointerdown": WallEvent;
|
||||||
|
"wall:pointerup": WallEvent;
|
||||||
|
"item:click": ItemEvent;
|
||||||
|
"item:move": ItemEvent;
|
||||||
|
"item:enter": ItemEvent;
|
||||||
|
"item:leave": ItemEvent;
|
||||||
|
"item:pointerdown": ItemEvent;
|
||||||
|
"item:pointerup": ItemEvent;
|
||||||
|
};
|
||||||
|
export const emitter = mitt<EditorEvents>();
|
||||||
@@ -2,11 +2,17 @@
|
|||||||
export { default as useScene } from "./store/useScene";
|
export { default as useScene } from "./store/useScene";
|
||||||
|
|
||||||
// Hooks
|
// Hooks
|
||||||
export { useRegistry } from "./hooks/scene-registry/scene-registry";
|
export {
|
||||||
|
sceneRegistry,
|
||||||
|
useRegistry,
|
||||||
|
} from "./hooks/scene-registry/scene-registry";
|
||||||
|
|
||||||
// Systems
|
// Systems
|
||||||
export { LevelSystem } from "./systems/level/level-system";
|
export { LevelSystem } from "./systems/level/level-system";
|
||||||
export { WallSystem } from "./systems/wall/wall-system";
|
export { WallSystem } from "./systems/wall/wall-system";
|
||||||
|
|
||||||
|
// Events
|
||||||
|
export { emitter } from "./events/bus";
|
||||||
|
|
||||||
// Schema
|
// Schema
|
||||||
export * from "./schema";
|
export * from "./schema";
|
||||||
|
|||||||
@@ -43,9 +43,9 @@ function updateWallGeometry(wallId: string) {
|
|||||||
if (!mesh) return;
|
if (!mesh) return;
|
||||||
|
|
||||||
const childrenIds = node.children || [];
|
const childrenIds = node.children || [];
|
||||||
const childrenNodes = childrenIds.map(
|
const childrenNodes = childrenIds
|
||||||
(childId) => useScene.getState().nodes[childId]
|
.map((childId) => useScene.getState().nodes[childId])
|
||||||
);
|
.filter((n): n is AnyNode => n !== undefined);
|
||||||
|
|
||||||
// Perform the Extrusion with Holes logic we discussed
|
// Perform the Extrusion with Holes logic we discussed
|
||||||
const newGeo = generateExtrudedWall(node, childrenNodes);
|
const newGeo = generateExtrudedWall(node, childrenNodes);
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import { ItemNode, useRegistry } from "@pascal-app/core";
|
import { emitter, ItemNode, useRegistry } from "@pascal-app/core";
|
||||||
import { Clone } from "@react-three/drei/core/Clone";
|
import { Clone } from "@react-three/drei/core/Clone";
|
||||||
import { useGLTF } from "@react-three/drei/core/Gltf";
|
import { useGLTF } from "@react-three/drei/core/Gltf";
|
||||||
import { useRef } from "react";
|
import { ThreeEvent } from "@react-three/fiber/dist/declarations/src/core/events";
|
||||||
|
import { useCallback, useRef } from "react";
|
||||||
import { Group } from "three";
|
import { Group } from "three";
|
||||||
|
|
||||||
export const ItemRenderer = ({ node }: { node: ItemNode }) => {
|
export const ItemRenderer = ({ node }: { node: ItemNode }) => {
|
||||||
@@ -14,12 +15,127 @@ export const ItemRenderer = ({ node }: { node: ItemNode }) => {
|
|||||||
nodes.cutout.visible = false;
|
nodes.cutout.visible = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Event handlers
|
||||||
|
|
||||||
|
const onPointerDown = useCallback(
|
||||||
|
(e: ThreeEvent<PointerEvent>) => {
|
||||||
|
// Only emit events for left-click (button 0)
|
||||||
|
if (e.button !== 0) return;
|
||||||
|
|
||||||
|
const eventData = {
|
||||||
|
node,
|
||||||
|
position: [e.point.x, e.point.y, e.point.z] as [number, number, number],
|
||||||
|
normal: e.face
|
||||||
|
? ([e.face.normal.x, e.face.normal.y, e.face.normal.z] as [
|
||||||
|
number,
|
||||||
|
number,
|
||||||
|
number
|
||||||
|
])
|
||||||
|
: undefined,
|
||||||
|
stopPropagation: () => e.stopPropagation(),
|
||||||
|
};
|
||||||
|
emitter.emit("item:pointerdown", eventData);
|
||||||
|
},
|
||||||
|
[node]
|
||||||
|
);
|
||||||
|
|
||||||
|
const onClick = useCallback(
|
||||||
|
(e: ThreeEvent<PointerEvent>) => {
|
||||||
|
// Only emit events for left-click (button 0)
|
||||||
|
if (e.button !== 0) return;
|
||||||
|
|
||||||
|
const eventData = {
|
||||||
|
node,
|
||||||
|
position: [e.point.x, e.point.y, e.point.z] as [number, number, number],
|
||||||
|
normal: e.face
|
||||||
|
? ([e.face.normal.x, e.face.normal.y, e.face.normal.z] as [
|
||||||
|
number,
|
||||||
|
number,
|
||||||
|
number
|
||||||
|
])
|
||||||
|
: undefined,
|
||||||
|
stopPropagation: () => e.stopPropagation(),
|
||||||
|
};
|
||||||
|
emitter.emit("item:click", eventData);
|
||||||
|
},
|
||||||
|
[node]
|
||||||
|
);
|
||||||
|
|
||||||
|
const onPointerUp = useCallback(
|
||||||
|
(e: ThreeEvent<PointerEvent>) => {
|
||||||
|
// Only emit events for left-click (button 0)
|
||||||
|
if (e.button !== 0) return;
|
||||||
|
|
||||||
|
emitter.emit("item:pointerup", {
|
||||||
|
node,
|
||||||
|
position: [e.point.x, e.point.y, e.point.z],
|
||||||
|
normal: e.face
|
||||||
|
? ([e.face.normal.x, e.face.normal.y, e.face.normal.z] as [
|
||||||
|
number,
|
||||||
|
number,
|
||||||
|
number
|
||||||
|
])
|
||||||
|
: undefined,
|
||||||
|
stopPropagation: () => e.stopPropagation(),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[node]
|
||||||
|
);
|
||||||
|
|
||||||
|
const onPointerEnter = useCallback(
|
||||||
|
(e: ThreeEvent<PointerEvent>) => {
|
||||||
|
emitter.emit("item:enter", {
|
||||||
|
node,
|
||||||
|
position: [e.point.x, e.point.y, e.point.z],
|
||||||
|
normal: e.face
|
||||||
|
? [e.face.normal.x, e.face.normal.y, e.face.normal.z]
|
||||||
|
: undefined,
|
||||||
|
stopPropagation: () => e.stopPropagation(),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[node]
|
||||||
|
);
|
||||||
|
|
||||||
|
const onPointerLeave = useCallback(
|
||||||
|
(e: ThreeEvent<PointerEvent>) => {
|
||||||
|
emitter.emit("item:leave", {
|
||||||
|
node,
|
||||||
|
position: [e.point.x, e.point.y, e.point.z],
|
||||||
|
normal: e.face
|
||||||
|
? [e.face.normal.x, e.face.normal.y, e.face.normal.z]
|
||||||
|
: undefined,
|
||||||
|
stopPropagation: () => e.stopPropagation(),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[node]
|
||||||
|
);
|
||||||
|
|
||||||
|
const onPointerMove = useCallback(
|
||||||
|
(e: ThreeEvent<PointerEvent>) => {
|
||||||
|
emitter.emit("item:move", {
|
||||||
|
node,
|
||||||
|
position: [e.point.x, e.point.y, e.point.z],
|
||||||
|
normal: e.face
|
||||||
|
? [e.face.normal.x, e.face.normal.y, e.face.normal.z]
|
||||||
|
: undefined,
|
||||||
|
stopPropagation: () => e.stopPropagation(),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[node]
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Clone
|
<Clone
|
||||||
ref={ref}
|
ref={ref}
|
||||||
object={scene}
|
object={scene}
|
||||||
position={node.position}
|
position={node.position}
|
||||||
rotation={node.rotation}
|
rotation={node.rotation}
|
||||||
|
onPointerDown={onPointerDown}
|
||||||
|
onClick={onClick}
|
||||||
|
onPointerUp={onPointerUp}
|
||||||
|
onPointerEnter={onPointerEnter}
|
||||||
|
onPointerLeave={onPointerLeave}
|
||||||
|
onPointerMove={onPointerMove}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { useRegistry, WallNode } from "@pascal-app/core";
|
import { emitter, useRegistry, WallNode } from "@pascal-app/core";
|
||||||
import { useRef } from "react";
|
import { ThreeEvent } from "@react-three/fiber";
|
||||||
|
import { useCallback, useRef } from "react";
|
||||||
import { Mesh } from "three";
|
import { Mesh } from "three";
|
||||||
import { NodeRenderer } from "../node-renderer";
|
import { NodeRenderer } from "../node-renderer";
|
||||||
|
|
||||||
@@ -8,8 +9,127 @@ export const WallRenderer = ({ node }: { node: WallNode }) => {
|
|||||||
|
|
||||||
useRegistry(node.id, "wall", ref);
|
useRegistry(node.id, "wall", ref);
|
||||||
|
|
||||||
|
// Event handlers
|
||||||
|
|
||||||
|
const onPointerDown = useCallback(
|
||||||
|
(e: ThreeEvent<PointerEvent>) => {
|
||||||
|
// Only emit events for left-click (button 0)
|
||||||
|
if (e.button !== 0) return;
|
||||||
|
|
||||||
|
const eventData = {
|
||||||
|
node,
|
||||||
|
position: [e.point.x, e.point.y, e.point.z] as [number, number, number],
|
||||||
|
normal: e.face
|
||||||
|
? ([e.face.normal.x, e.face.normal.y, e.face.normal.z] as [
|
||||||
|
number,
|
||||||
|
number,
|
||||||
|
number
|
||||||
|
])
|
||||||
|
: undefined,
|
||||||
|
stopPropagation: () => e.stopPropagation(),
|
||||||
|
};
|
||||||
|
emitter.emit("wall:pointerdown", eventData);
|
||||||
|
},
|
||||||
|
[node]
|
||||||
|
);
|
||||||
|
|
||||||
|
const onClick = useCallback(
|
||||||
|
(e: ThreeEvent<PointerEvent>) => {
|
||||||
|
// Only emit events for left-click (button 0)
|
||||||
|
if (e.button !== 0) return;
|
||||||
|
|
||||||
|
const eventData = {
|
||||||
|
node,
|
||||||
|
position: [e.point.x, e.point.y, e.point.z] as [number, number, number],
|
||||||
|
normal: e.face
|
||||||
|
? ([e.face.normal.x, e.face.normal.y, e.face.normal.z] as [
|
||||||
|
number,
|
||||||
|
number,
|
||||||
|
number
|
||||||
|
])
|
||||||
|
: undefined,
|
||||||
|
stopPropagation: () => e.stopPropagation(),
|
||||||
|
};
|
||||||
|
emitter.emit("wall:click", eventData);
|
||||||
|
},
|
||||||
|
[node]
|
||||||
|
);
|
||||||
|
|
||||||
|
const onPointerUp = useCallback(
|
||||||
|
(e: ThreeEvent<PointerEvent>) => {
|
||||||
|
// Only emit events for left-click (button 0)
|
||||||
|
if (e.button !== 0) return;
|
||||||
|
|
||||||
|
emitter.emit("wall:pointerup", {
|
||||||
|
node,
|
||||||
|
position: [e.point.x, e.point.y, e.point.z],
|
||||||
|
normal: e.face
|
||||||
|
? ([e.face.normal.x, e.face.normal.y, e.face.normal.z] as [
|
||||||
|
number,
|
||||||
|
number,
|
||||||
|
number
|
||||||
|
])
|
||||||
|
: undefined,
|
||||||
|
stopPropagation: () => e.stopPropagation(),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[node]
|
||||||
|
);
|
||||||
|
|
||||||
|
const onPointerEnter = useCallback(
|
||||||
|
(e: ThreeEvent<PointerEvent>) => {
|
||||||
|
emitter.emit("wall:enter", {
|
||||||
|
node,
|
||||||
|
position: [e.point.x, e.point.y, e.point.z],
|
||||||
|
normal: e.face
|
||||||
|
? [e.face.normal.x, e.face.normal.y, e.face.normal.z]
|
||||||
|
: undefined,
|
||||||
|
stopPropagation: () => e.stopPropagation(),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[node]
|
||||||
|
);
|
||||||
|
|
||||||
|
const onPointerLeave = useCallback(
|
||||||
|
(e: ThreeEvent<PointerEvent>) => {
|
||||||
|
emitter.emit("wall:leave", {
|
||||||
|
node,
|
||||||
|
position: [e.point.x, e.point.y, e.point.z],
|
||||||
|
normal: e.face
|
||||||
|
? [e.face.normal.x, e.face.normal.y, e.face.normal.z]
|
||||||
|
: undefined,
|
||||||
|
stopPropagation: () => e.stopPropagation(),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[node]
|
||||||
|
);
|
||||||
|
|
||||||
|
const onPointerMove = useCallback(
|
||||||
|
(e: ThreeEvent<PointerEvent>) => {
|
||||||
|
emitter.emit("wall:move", {
|
||||||
|
node,
|
||||||
|
position: [e.point.x, e.point.y, e.point.z],
|
||||||
|
normal: e.face
|
||||||
|
? [e.face.normal.x, e.face.normal.y, e.face.normal.z]
|
||||||
|
: undefined,
|
||||||
|
stopPropagation: () => e.stopPropagation(),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[node]
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<mesh ref={ref} castShadow receiveShadow>
|
<mesh
|
||||||
|
ref={ref}
|
||||||
|
castShadow
|
||||||
|
receiveShadow
|
||||||
|
onPointerDown={onPointerDown}
|
||||||
|
onPointerUp={onPointerUp}
|
||||||
|
onPointerEnter={onPointerEnter}
|
||||||
|
onPointerLeave={onPointerLeave}
|
||||||
|
onPointerMove={onPointerMove}
|
||||||
|
onClick={onClick}
|
||||||
|
>
|
||||||
{/* WallSystem will replace this geometry in the next frame */}
|
{/* WallSystem will replace this geometry in the next frame */}
|
||||||
<boxGeometry args={[0, 0, 0]} />
|
<boxGeometry args={[0, 0, 0]} />
|
||||||
<meshStandardMaterial color="lightgray" />
|
<meshStandardMaterial color="lightgray" />
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { Environment, OrbitControls } from "@react-three/drei";
|
import { Bvh, Environment, OrbitControls } from "@react-three/drei";
|
||||||
import { Canvas, ThreeToJSXElements } from "@react-three/fiber";
|
import { Canvas, ThreeToJSXElements } from "@react-three/fiber";
|
||||||
|
|
||||||
import { LevelSystem, WallSystem } from "@pascal-app/core";
|
import { LevelSystem, WallSystem } from "@pascal-app/core";
|
||||||
@@ -14,9 +14,11 @@ declare module "@react-three/fiber" {
|
|||||||
|
|
||||||
extend(THREE as any);
|
extend(THREE as any);
|
||||||
|
|
||||||
interface ViewerProps {}
|
interface ViewerProps {
|
||||||
|
children?: React.ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
const Viewer: React.FC<ViewerProps> = () => {
|
const Viewer: React.FC<ViewerProps> = ({ children }) => {
|
||||||
return (
|
return (
|
||||||
<Canvas
|
<Canvas
|
||||||
className={"bg-[#303035]"}
|
className={"bg-[#303035]"}
|
||||||
@@ -30,11 +32,15 @@ const Viewer: React.FC<ViewerProps> = () => {
|
|||||||
>
|
>
|
||||||
<OrbitControls />
|
<OrbitControls />
|
||||||
<Environment preset="sunset" />
|
<Environment preset="sunset" />
|
||||||
<SceneRenderer />
|
<Bvh>
|
||||||
|
<SceneRenderer />
|
||||||
|
</Bvh>
|
||||||
|
|
||||||
{/* Default Systems */}
|
{/* Default Systems */}
|
||||||
<LevelSystem />
|
<LevelSystem />
|
||||||
<WallSystem />
|
<WallSystem />
|
||||||
|
|
||||||
|
{children}
|
||||||
</Canvas>
|
</Canvas>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user