diff --git a/src/App.tsx b/src/App.tsx index 42240a4..8680423 100755 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,88 +1,8 @@ -import Layout from "./three/Layout"; -import { ThreeProvider } from "./three"; -import useLayoutGeometry from "./three/useLayoutGeometry"; -import NavMesh from "./three/NavMesh"; - -const wallConfig = { - wallThickness: 0.4, - width: 20, - length: 20, - wallHeight: 1, -}; - -const walls = [ - [ - [1, -9.75], - [1, 1.2], - ], - [ - [-4.6, 1.2], - [1.02, 1.2], - ], - [ - [-9.55, 1.2], - [-7.1, 1.2], - ], - [ - [4, -9.75], - [4, -4], - ], - [ - [4, -0.6], - [4, 0.75], - ], - [ - [4, 0.55], - [8.34, 0.55], - ], - [ - [-9.65, 8.5], - [-6.8, 8.5], - ], - [ - [-3.75, 8.5], - [2.5, 8.5], - ], - [ - [5.55, 8.5], - [8.34, 8.5], - ], - [ - [-9.9, -9.75], - [1.2, -9.75], - ], - [ - [3.8, -9.75], - [8.7, -9.75], - ], - [ - [-9.7, -9.75], - [-9.7, 8.7], - ], - [ - [8.5, -9.75], - [8.5, 8.7], - ], -].map( - ([start, end]) => - [ - [start[0], 0, start[1]], - [end[0], wallConfig.wallHeight, end[1]], - ] as [number[], number[]] -); +import ThreeApp from "./ThreeApp"; +import { wallConfig, walls } from "./data"; const App = () => { - const geometry = useLayoutGeometry({ - ...wallConfig, - walls, - }); - - return ( - - - - - ); + return ; }; export default App; diff --git a/src/ThreeApp.tsx b/src/ThreeApp.tsx new file mode 100644 index 0000000..e6abcac --- /dev/null +++ b/src/ThreeApp.tsx @@ -0,0 +1,20 @@ +import Layout from "./three/Layout"; +import { ThreeProvider } from "./three"; +import NavMesh from "./three/NavMesh"; +import { WallConfig, Walls } from "./data"; + +interface ThreeAppProps { + wallConfig: WallConfig; + walls: Walls; +} + +const ThreeApp = ({ wallConfig, walls }: ThreeAppProps) => { + return ( + + + + + ); +}; + +export default ThreeApp; diff --git a/src/data.ts b/src/data.ts new file mode 100644 index 0000000..ba7d254 --- /dev/null +++ b/src/data.ts @@ -0,0 +1,79 @@ +type Vector3Pair = [number[], number[]]; + +export type Walls = Array; +export interface WallConfig { + width: number; + length: number; + wallThickness: number; + wallHeight: number; +} + +const wallConfig: WallConfig = { + wallThickness: 0.4, + width: 20, + length: 20, + wallHeight: 1, +}; + +const walls: Walls = [ + [ + [1, -9.75], + [1, 1.2], + ], + [ + [-4.6, 1.2], + [1.02, 1.2], + ], + [ + [-9.55, 1.2], + [-7.1, 1.2], + ], + [ + [4, -9.75], + [4, -4], + ], + [ + [4, -0.6], + [4, 0.75], + ], + [ + [4, 0.55], + [8.34, 0.55], + ], + [ + [-9.65, 8.5], + [-6.8, 8.5], + ], + [ + [-3.75, 8.5], + [2.5, 8.5], + ], + [ + [5.55, 8.5], + [8.34, 8.5], + ], + [ + [-9.9, -9.75], + [1.2, -9.75], + ], + [ + [3.8, -9.75], + [8.7, -9.75], + ], + [ + [-9.7, -9.75], + [-9.7, 8.7], + ], + [ + [8.5, -9.75], + [8.5, 8.7], + ], +].map( + ([start, end]) => + [ + [start[0], 0, start[1]], + [end[0], wallConfig.wallHeight, end[1]], + ] as [number[], number[]] +); + +export { wallConfig, walls }; diff --git a/src/stories/App.stories.ts b/src/stories/App.stories.ts index f16bf17..8a3992e 100755 --- a/src/stories/App.stories.ts +++ b/src/stories/App.stories.ts @@ -1,15 +1,19 @@ import type { Meta, StoryObj } from "@storybook/react"; -import App from "../App"; +import ThreeApp from "../ThreeApp"; +import { wallConfig, walls } from "../data"; const meta = { - title: "App", - component: App, -} satisfies Meta; + title: "ThreeApp", + component: ThreeApp, +} satisfies Meta; export default meta; type Story = StoryObj; export const Playground: Story = { - args: {}, + args: { + wallConfig, + walls, + }, }; diff --git a/src/three/Layout.ts b/src/three/Layout.ts index 5376dd9..4b4bddb 100644 --- a/src/three/Layout.ts +++ b/src/three/Layout.ts @@ -2,12 +2,12 @@ import * as THREE from "three"; import { useThree } from "."; import { useEffect } from "react"; import useWebGLRenderTarget from "./useWebGLRenderTarget"; +import { WallConfig, Walls } from "../data"; +import useLayoutGeometry from "./useLayoutGeometry"; interface LayoutProps { - geometry: THREE.BufferGeometry; - wallHeight: number; - width: number; - length: number; + wallConfig: WallConfig; + walls: Walls; } const vertexShader = ` @@ -57,19 +57,23 @@ class OutlineMaterial extends THREE.ShaderMaterial { } } -const Layout = ({ geometry, width, length, wallHeight }: LayoutProps) => { +const Layout = ({ wallConfig, walls }: LayoutProps) => { const { scene } = useThree(); const topView = useWebGLRenderTarget(); + const geometry = useLayoutGeometry({ + ...wallConfig, + walls, + }); useEffect(() => { if (!scene) return; - const material = new OutlineMaterial(topView.texture); const mesh = new THREE.Mesh(geometry, material); mesh.frustumCulled = false; scene.add(mesh); requestAnimationFrame(() => { + const { width, length, wallHeight } = wallConfig; const topViewSene = new THREE.Scene(); const topViewMaterial = new THREE.MeshBasicMaterial({ color: "white" }); const topViewMesh = new THREE.Mesh(geometry, topViewMaterial); @@ -91,7 +95,7 @@ const Layout = ({ geometry, width, length, wallHeight }: LayoutProps) => { return () => { scene.remove(mesh); }; - }, [scene, geometry]); + }, [scene, geometry, wallConfig, walls]); return null; }; diff --git a/src/three/NavMesh.ts b/src/three/NavMesh.ts index 4d93bf3..e5835ef 100644 --- a/src/three/NavMesh.ts +++ b/src/three/NavMesh.ts @@ -4,35 +4,50 @@ import { threeToSoloNavMesh, NavMeshHelper } from "recast-navigation/three"; import { useThree } from "."; import { useEffect, useState } from "react"; +import useLayoutGeometry from "./useLayoutGeometry"; +import { WallConfig, Walls } from "../data"; interface NavMeshProps { - geometry: THREE.BufferGeometry; - wallThickness: number; + wallConfig: WallConfig; + walls: Walls; } -export const useNavMesh = ({ geometry, wallThickness }: NavMeshProps) => { +export const useNavMesh = ({ wallConfig, walls }: NavMeshProps) => { + const [libReady, setLibReady] = useState(false); const [navMesh, setNavMesh] = useState(); + const geometry = useLayoutGeometry({ + ...wallConfig, + walls, + }); + + useEffect(() => { + init().then(() => setLibReady(true)); + }, []); + useEffect(() => { + if (!libReady) return; if (!geometry) return; - init().then(() => { - const { navMesh } = threeToSoloNavMesh([new THREE.Mesh(geometry)], { - ch: 1e-2, - cs: wallThickness + 1e-2, - walkableHeight: 1, - }); - if (navMesh) { - setNavMesh(navMesh); - } + const { wallThickness } = wallConfig; + const { navMesh } = threeToSoloNavMesh([new THREE.Mesh(geometry)], { + ch: 1e-2, + cs: wallThickness + 1e-2, + walkableHeight: 1, }); - }, [geometry, wallThickness]); + if (navMesh) { + setNavMesh(navMesh); + } + return () => { + navMesh?.destroy(); + }; + }, [geometry, libReady, wallConfig, walls]); return navMesh; }; -const NavMeshRenderer = ({ geometry, wallThickness }: NavMeshProps) => { +const NavMeshRenderer = ({ wallConfig, walls }: NavMeshProps) => { const { scene } = useThree(); - const navMesh = useNavMesh({ geometry, wallThickness }); + const navMesh = useNavMesh({ wallConfig, walls }); useEffect(() => { if (!navMesh) return;