diff --git a/.changeset/mighty-birds-kneel.md b/.changeset/mighty-birds-kneel.md new file mode 100644 index 00000000..567579ec --- /dev/null +++ b/.changeset/mighty-birds-kneel.md @@ -0,0 +1,6 @@ +--- +"@recast-navigation/generators": minor +"recast-navigation": minor +--- + +feat: expose and correctly cleanup polyMesh and polyMeshDetail diff --git a/.changeset/wild-plums-design.md b/.changeset/wild-plums-design.md new file mode 100644 index 00000000..4c09b218 --- /dev/null +++ b/.changeset/wild-plums-design.md @@ -0,0 +1,8 @@ +--- +"@recast-navigation/three": minor +"@recast-navigation/core": minor +"@recast-navigation/wasm": minor +"recast-navigation": minor +--- + +feat: expose duDebugDraw recast and detour debug drawing utilities diff --git a/apps/navmesh-website/src/features/controls/controls.ts b/apps/navmesh-website/src/features/controls/controls.ts index 6844db8f..c2910865 100644 --- a/apps/navmesh-website/src/features/controls/controls.ts +++ b/apps/navmesh-website/src/features/controls/controls.ts @@ -2,6 +2,7 @@ import { button, useControls } from 'leva'; import { levaText } from './leva-text'; import { NavMesh } from 'recast-navigation'; import { useState } from 'react'; +import { DebugDrawerOption } from '../recast'; export const useActionsControls = ({ navMesh, @@ -34,84 +35,87 @@ export const useActionsControls = ({ }; export const useNavMeshGenerationControls = () => { - const { keepIntermediates, ...navMeshConfig } = useControls('NavMesh Generation Config', { - cs: { - label: 'Cell Size', - value: 0.2, - }, - ch: { - label: 'Cell Height', - value: 0.2, - }, - tileSize: { - label: 'Tile Size', - value: 0, - step: 1, - }, - borderSize: { - label: 'Border Size', - value: 0, - }, - walkableSlopeAngle: { - label: 'Walkable Slope Angle', - value: 60, - }, - walkableHeight: { - label: 'Walkable Height', - value: 2, - }, - walkableClimb: { - label: 'Walkable Climb', - value: 2, - }, - walkableRadius: { - label: 'Walkable Radius', - value: 1, - }, - maxEdgeLen: { - label: 'Max Edge Length', - value: 12, - }, - maxSimplificationError: { - label: 'Max Simplification Error', - value: 1.3, - }, - minRegionArea: { - label: 'Min Region Area', - value: 8, - }, - mergeRegionArea: { - label: 'Merge Region Area', - value: 20, - }, - maxVertsPerPoly: { - label: 'Max Verts Per Poly', - value: 6, - step: 1, - }, - detailSampleDist: { - label: 'Detail Sample Dist', - value: 6, - }, - detailSampleMaxError: { - label: 'Detail Sample Max Error', - value: 1, - }, - expectedLayersPerTile: { - label: 'Expected Layers Per Tile', - value: 4, - step: 1, - }, - maxLayers: { - label: 'Max Layers', - value: 32, - step: 1, - }, - keepIntermediates: { - label: 'Keep Intermediates', - value: true, - }, - }); + const { keepIntermediates, ...navMeshConfig } = useControls( + 'NavMesh Generation Config', + { + cs: { + label: 'Cell Size', + value: 0.2, + }, + ch: { + label: 'Cell Height', + value: 0.2, + }, + tileSize: { + label: 'Tile Size', + value: 0, + step: 1, + }, + borderSize: { + label: 'Border Size', + value: 0, + }, + walkableSlopeAngle: { + label: 'Walkable Slope Angle', + value: 60, + }, + walkableHeight: { + label: 'Walkable Height', + value: 2, + }, + walkableClimb: { + label: 'Walkable Climb', + value: 2, + }, + walkableRadius: { + label: 'Walkable Radius', + value: 1, + }, + maxEdgeLen: { + label: 'Max Edge Length', + value: 12, + }, + maxSimplificationError: { + label: 'Max Simplification Error', + value: 1.3, + }, + minRegionArea: { + label: 'Min Region Area', + value: 8, + }, + mergeRegionArea: { + label: 'Merge Region Area', + value: 20, + }, + maxVertsPerPoly: { + label: 'Max Verts Per Poly', + value: 6, + step: 1, + }, + detailSampleDist: { + label: 'Detail Sample Dist', + value: 6, + }, + detailSampleMaxError: { + label: 'Detail Sample Max Error', + value: 1, + }, + expectedLayersPerTile: { + label: 'Expected Layers Per Tile', + value: 4, + step: 1, + }, + maxLayers: { + label: 'Max Layers', + value: 32, + step: 1, + }, + keepIntermediates: { + label: 'Keep Intermediates', + value: true, + }, + } + ); useControls('NavMesh Generation Config.Tips', { _: levaText( @@ -166,58 +170,30 @@ export const useDisplayOptionsControls = () => { }, }); - const [navMeshHelperDebugColor, setNavMeshHelperDebugColor] = - useState('#ffa500'); - - const { - opacity: navMeshDebugOpacity, - wireframe: navMeshDebugWireframe, - displayNavMeshHelper, - } = useControls('Display Options.NavMesh', { - _: levaText('The computed navigation mesh.'), - displayNavMeshHelper: { - label: 'Show NavMesh', - value: true, - }, - color: { - label: 'Color', - value: navMeshHelperDebugColor, - onEditEnd: setNavMeshHelperDebugColor, - }, - opacity: { - label: 'Opacity', - value: 0.65, - min: 0, - max: 1, - }, - wireframe: { - label: 'Wireframe', - value: false, - }, - }); - - const { heightfieldHelperEnabled } = useControls( - 'Display Options.Heightfield', + const { navMeshDebugDraw, navMeshDebugDrawOption } = useControls( + 'Display Options.NavMesh', { - _: levaText("Visualises Recast's voxelization process. Note that 'Keep Intermediates' must be checked."), - heightfieldHelperEnabled: { - value: false, - label: 'Show Heightfield', + _: levaText('The computed navigation mesh.'), + navMeshDebugDraw: { + label: 'Show NavMesh Debug Drawer', + value: true, + }, + navMeshDebugDrawOption: { + label: 'Display', + value: 'navmesh', + options: Object.values(DebugDrawerOption), }, } ); return { displayModel, - heightfieldHelperEnabled, navMeshGeneratorInputDebugColor, displayNavMeshGenerationInput, navMeshGeneratorInputWireframe, navMeshGeneratorInputOpacity, - navMeshHelperDebugColor, - navMeshDebugOpacity, - navMeshDebugWireframe, - displayNavMeshHelper, + navMeshDebugDraw, + navMeshDebugDrawOption, }; }; diff --git a/apps/navmesh-website/src/features/recast/helpers/heightfield-helper.tsx b/apps/navmesh-website/src/features/recast/helpers/heightfield-helper.tsx deleted file mode 100644 index 82f05fac..00000000 --- a/apps/navmesh-website/src/features/recast/helpers/heightfield-helper.tsx +++ /dev/null @@ -1,55 +0,0 @@ -import { useMemo } from 'react'; -import { NavMesh, RecastHeightfield } from 'recast-navigation'; -import { - SoloNavMeshGeneratorIntermediates, - TiledNavMeshGeneratorIntermediates, -} from 'recast-navigation/generators'; -import { HeightfieldHelper as HeightfieldHelperImpl } from 'recast-navigation/three'; -import { MeshStandardMaterial } from 'three'; - -export const HeightfieldHelper = ({ - enabled, - navMesh, - generatorIntermediates, -}: { - enabled: boolean; - generatorIntermediates?: - | SoloNavMeshGeneratorIntermediates - | TiledNavMeshGeneratorIntermediates; - navMesh?: NavMesh; -}) => { - const helper = useMemo(() => { - if (!navMesh || !enabled) { - return undefined; - } - - let heightfields: RecastHeightfield[] = []; - - if (generatorIntermediates) { - if ( - generatorIntermediates.type === 'solo' && - generatorIntermediates.heightfield - ) { - heightfields = [generatorIntermediates.heightfield].filter(Boolean); - } else if (generatorIntermediates.type === 'tiled') { - heightfields = generatorIntermediates.tileIntermediates - .map((t) => t.heightfield) - .filter(Boolean) as RecastHeightfield[]; - } - } - - if (heightfields.length <= 0) { - return undefined; - } - - const heightfieldHelper = new HeightfieldHelperImpl({ - heightfields, - material: new MeshStandardMaterial(), - highlightWalkable: true, - }); - - return heightfieldHelper; - }, [navMesh, enabled]); - - return enabled && helper && ; -}; diff --git a/apps/navmesh-website/src/features/recast/helpers/nav-mesh-debug-drawer.tsx b/apps/navmesh-website/src/features/recast/helpers/nav-mesh-debug-drawer.tsx new file mode 100644 index 00000000..c79b78d8 --- /dev/null +++ b/apps/navmesh-website/src/features/recast/helpers/nav-mesh-debug-drawer.tsx @@ -0,0 +1,97 @@ +import { useEffect, useMemo } from 'react'; +import { NavMesh, RecastHeightfield, RecastPolyMesh, RecastPolyMeshDetail } from 'recast-navigation'; +import { + SoloNavMeshGeneratorIntermediates, + TiledNavMeshGeneratorIntermediates, + TileCacheGeneratorIntermediates, +} from 'recast-navigation/generators'; +import { DebugDrawer } from 'recast-navigation/three'; + +export const DebugDrawerOption = { + NAVMESH: 'navmesh', + HEIGHTFIELD: 'heightfield', + POLY_MESH: 'poly mesh', + POLY_MESH_DETAIL: 'poly mesh detail', +}; + +type DebugDrawerOptions = + (typeof DebugDrawerOption)[keyof typeof DebugDrawerOption]; + +export const NavMeshDebugDrawer = ({ + enabled, + navMesh, + intermediates, + option, +}: { + enabled: boolean; + navMesh: NavMesh | undefined; + intermediates?: + | SoloNavMeshGeneratorIntermediates + | TiledNavMeshGeneratorIntermediates + | TileCacheGeneratorIntermediates; + option: DebugDrawerOptions; +}) => { + const debug = useMemo(() => { + return new DebugDrawer(); + }, []); + + useEffect(() => { + const onResize = () => { + debug.resize(window.innerWidth, window.innerHeight); + }; + + onResize(); + + window.addEventListener('resize', onResize); + + return () => { + window.removeEventListener('resize', onResize); + }; + }, []); + + useEffect(() => { + debug.reset(); + + if (!navMesh) return; + + const heightfieldList: RecastHeightfield[] = []; + const polyMeshList: RecastPolyMesh[] = []; + const polyMeshDetailList: RecastPolyMeshDetail[] = []; + + if (intermediates) { + const intermediatesList = Array.isArray(intermediates) ? intermediates : [intermediates]; + + for (const intermediate of intermediatesList) { + if (intermediate.heightfield) { + heightfieldList.push(intermediate.heightfield); + } + + if (intermediate.polyMesh) { + polyMeshList.push(intermediate.polyMesh); + } + + if (intermediate.polyMeshDetail) { + polyMeshDetailList.push(intermediate.polyMeshDetail); + } + } + } + + if (option === DebugDrawerOption.NAVMESH) { + debug.debugDrawNavMesh(navMesh, 0); + } else if (option === DebugDrawerOption.HEIGHTFIELD) { + for (const heightfield of heightfieldList) { + debug.debugDrawHeightfieldWalkable(heightfield); + } + } else if (option === DebugDrawerOption.POLY_MESH) { + for (const polyMesh of polyMeshList) { + debug.debugDrawPolyMesh(polyMesh); + } + } else if (option === DebugDrawerOption.POLY_MESH_DETAIL) { + for (const polyMeshDetail of polyMeshDetailList) { + debug.debugDrawPolyMeshDetail(polyMeshDetail); + } + } + }, [navMesh, option]); + + return enabled && debug && ; +}; diff --git a/apps/navmesh-website/src/features/recast/helpers/nav-mesh-helper.tsx b/apps/navmesh-website/src/features/recast/helpers/nav-mesh-helper.tsx deleted file mode 100644 index 778b558b..00000000 --- a/apps/navmesh-website/src/features/recast/helpers/nav-mesh-helper.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import { useMemo } from 'react'; -import { NavMesh } from 'recast-navigation'; -import { NavMeshHelper as NavMeshHelperImpl } from 'recast-navigation/three'; -import { Color, MeshBasicMaterial } from 'three'; - -export const NavMeshHelper = ({ - enabled, - navMesh, - navMeshHelperDebugColor, - navMeshDebugOpacity, - navMeshDebugWireframe, -}: { - enabled: boolean; - navMesh: NavMesh | undefined; - navMeshHelperDebugColor: string; - navMeshDebugOpacity: number; - navMeshDebugWireframe: boolean; -}) => { - const helper = useMemo(() => { - if (!navMesh) { - return undefined; - } - - return new NavMeshHelperImpl({ - navMesh, - navMeshMaterial: new MeshBasicMaterial({ - transparent: true, - opacity: navMeshDebugOpacity, - wireframe: navMeshDebugWireframe, - color: new Color(Number(navMeshHelperDebugColor.replace('#', '0x'))), - depthWrite: false, - }), - }); - }, [ - navMesh, - navMeshHelperDebugColor, - navMeshDebugOpacity, - navMeshDebugWireframe, - ]); - - return enabled && helper && ; -}; diff --git a/apps/navmesh-website/src/features/recast/index.ts b/apps/navmesh-website/src/features/recast/index.ts index e6c124d2..fe7c162b 100644 --- a/apps/navmesh-website/src/features/recast/index.ts +++ b/apps/navmesh-website/src/features/recast/index.ts @@ -1,5 +1,4 @@ -export * from './helpers/heightfield-helper'; export * from './helpers/nav-mesh-generator-input-helper'; -export * from './helpers/nav-mesh-helper'; +export * from './helpers/nav-mesh-debug-drawer'; export * from './export/nav-mesh-to-gltf'; export * from './crowd/recast-agent'; diff --git a/apps/navmesh-website/src/pages/editor-page.tsx b/apps/navmesh-website/src/pages/editor-page.tsx index f2f4acc1..e3d9c26e 100644 --- a/apps/navmesh-website/src/pages/editor-page.tsx +++ b/apps/navmesh-website/src/pages/editor-page.tsx @@ -22,9 +22,8 @@ import { import { download } from '../features/download'; import { ErrorBoundary, ErrorMessage } from '../features/error-handling'; import { - HeightfieldHelper, NavMeshGeneratorInputHelper, - NavMeshHelper, + NavMeshDebugDrawer, RecastAgent, RecastAgentRef, navMeshToGLTF, @@ -183,15 +182,12 @@ const Editor = () => { const { displayModel, - heightfieldHelperEnabled, navMeshGeneratorInputDebugColor, displayNavMeshGenerationInput, navMeshGeneratorInputWireframe, navMeshGeneratorInputOpacity, - navMeshHelperDebugColor, - navMeshDebugOpacity, - navMeshDebugWireframe, - displayNavMeshHelper, + navMeshDebugDraw, + navMeshDebugDrawOption, } = useDisplayOptionsControls(); const { @@ -212,12 +208,11 @@ const Editor = () => { )} - @@ -229,12 +224,6 @@ const Editor = () => { navMeshGeneratorInputOpacity={navMeshGeneratorInputOpacity} /> - - {navMesh && agentEnabled && ( { @@ -307,6 +325,7 @@ export const generateSoloNavMesh = ( // Step 6. Build polygons mesh from contours. // const polyMesh = allocPolyMesh(); + intermediates.polyMesh = polyMesh; if ( !buildPolyMesh(buildContext, contourSet, config.maxVertsPerPoly, polyMesh) ) { @@ -317,6 +336,7 @@ export const generateSoloNavMesh = ( // Step 7. Create detail mesh which allows to access approximate height on each polygon. // const polyMeshDetail = allocPolyMeshDetail(); + intermediates.polyMeshDetail = polyMeshDetail; if ( !buildPolyMeshDetail( buildContext, diff --git a/packages/recast-navigation-generators/src/generators/generate-tiled-nav-mesh.ts b/packages/recast-navigation-generators/src/generators/generate-tiled-nav-mesh.ts index 20a1f903..5f1743c9 100644 --- a/packages/recast-navigation-generators/src/generators/generate-tiled-nav-mesh.ts +++ b/packages/recast-navigation-generators/src/generators/generate-tiled-nav-mesh.ts @@ -40,6 +40,10 @@ import { rasterizeTriangles, recastConfigDefaults, vec3, + RecastPolyMesh, + RecastPolyMeshDetail, + freePolyMesh, + freePolyMeshDetail, } from '@recast-navigation/core'; import type R from '@recast-navigation/wasm'; import { Pretty } from '../types'; @@ -59,17 +63,21 @@ export const tiledNavMeshGeneratorConfigDefaults: TiledNavMeshGeneratorConfig = ...recastConfigDefaults, }; +type TileIntermediates = { + tileX: number; + tileY: number; + heightfield?: RecastHeightfield; + compactHeightfield?: RecastCompactHeightfield; + contourSet?: RecastContourSet; + polyMesh?: RecastPolyMesh; + polyMeshDetail?: RecastPolyMeshDetail; +}; + export type TiledNavMeshGeneratorIntermediates = { type: 'tiled'; buildContext: RecastBuildContext; chunkyTriMesh?: RecastChunkyTriMesh; - tileIntermediates: { - tileX: number; - tileY: number; - heightfield?: RecastHeightfield; - compactHeightfield?: RecastCompactHeightfield; - contourSet?: RecastContourSet; - }[]; + tileIntermediates: TileIntermediates[]; }; type TiledNavMeshGeneratorSuccessResult = { @@ -130,6 +138,14 @@ export const generateTiledNavMesh = ( if (tileIntermediate.contourSet) { freeContourSet(tileIntermediate.contourSet); } + + if (tileIntermediate.polyMesh) { + freePolyMesh(tileIntermediate.polyMesh); + } + + if (tileIntermediate.polyMeshDetail) { + freePolyMeshDetail(tileIntermediate.polyMeshDetail); + } } if (intermediates.chunkyTriMesh) { @@ -244,13 +260,7 @@ export const generateTiledNavMesh = ( return { success: false as const, error }; }; - const tileIntermediate: { - tileX: number; - tileY: number; - heightfield?: RecastHeightfield; - compactHeightfield?: RecastCompactHeightfield; - contourSet?: RecastContourSet; - } = { tileX, tileY }; + const tileIntermediate: TileIntermediates = { tileX, tileY }; intermediates.tileIntermediates.push(tileIntermediate); @@ -503,6 +513,7 @@ export const generateTiledNavMesh = ( // Build polygons mesh from contours. // const polyMesh = allocPolyMesh(); + tileIntermediate.polyMesh = polyMesh; if ( !buildPolyMesh( buildContext, @@ -518,6 +529,7 @@ export const generateTiledNavMesh = ( // Create detail mesh which allows to access approximate height on each polygon. // const polyMeshDetail = allocPolyMeshDetail(); + tileIntermediate.polyMeshDetail = polyMeshDetail; if ( !buildPolyMeshDetail( buildContext, diff --git a/packages/recast-navigation-three/src/debug/debug-drawer.ts b/packages/recast-navigation-three/src/debug/debug-drawer.ts new file mode 100644 index 00000000..0824570a --- /dev/null +++ b/packages/recast-navigation-three/src/debug/debug-drawer.ts @@ -0,0 +1,343 @@ +import { NavMesh, NavMeshQuery, Raw, RecastCompactHeightfield, RecastContourSet, RecastHeightfield, RecastHeightfieldLayer, RecastHeightfieldLayerSet, RecastPolyMesh, RecastPolyMeshDetail } from '@recast-navigation/core'; +import * as THREE from 'three'; +import { + LineMaterial, + LineSegments2, + LineSegmentsGeometry, +} from 'three-stdlib'; + +type VertexData = { + x: number; + y: number; + z: number; + r: number; + g: number; + b: number; + a: number; +}; + +const _color = new THREE.Color(); + +export type DebugDrawerParams = { + triMaterial?: THREE.Material; + pointMaterial?: THREE.Material; + lineMaterial?: LineMaterial; +}; + +export class DebugDrawer extends THREE.Group { + triMaterial: THREE.Material; + + pointMaterial: THREE.Material; + pointGeometry = new THREE.SphereGeometry(0.02, 32, 32); + + lineMaterial: LineMaterial; + + private debugDrawImpl: InstanceType; + + private currentVertices: VertexData[] = []; + private currentPrimitive = 0; + + constructor({ + triMaterial, + pointMaterial, + lineMaterial, + }: DebugDrawerParams = {}) { + super(); + + this.triMaterial = + triMaterial ?? + new THREE.MeshBasicMaterial({ + vertexColors: true, + transparent: true, + opacity: 0.4, + depthWrite: false, + }); + + this.pointMaterial = pointMaterial ?? new THREE.MeshBasicMaterial(); + + this.lineMaterial = + lineMaterial ?? + new LineMaterial({ + color: 0xffffff, + linewidth: 1, + vertexColors: true, + transparent: true, + opacity: 0.4, + depthWrite: false, + }); + + this.debugDrawImpl = new Raw.Module.DebugDrawImpl(); + + this.debugDrawImpl.handleBegin = (primitive: number, _size: number) => { + this.currentPrimitive = primitive; + this.currentVertices = []; + }; + + this.debugDrawImpl.handleDepthMask = (_state: number) => { + // all methods must be implemented for JSImplentation + }; + + this.debugDrawImpl.handleTexture = (_state: number) => { + // all methods must be implemented for JSImplentation + }; + + this.debugDrawImpl.handleVertexWithColor = ( + x: number, + y: number, + z: number, + color: number + ) => { + this.vertex(x, y, z, color); + }; + + this.debugDrawImpl.handleVertexWithColorAndUV = ( + x: number, + y: number, + z: number, + color: number, + _u: number, + _v: number + ) => { + this.vertex(x, y, z, color); + }; + + this.debugDrawImpl.handleEnd = () => { + if (this.currentPrimitive === Raw.Module.DU_DRAW_LINES) { + this.endLines(); + } else if (this.currentPrimitive === Raw.Module.DU_DRAW_TRIS) { + this.endTris(); + } else if (this.currentPrimitive === Raw.Module.DU_DRAW_QUADS) { + this.endQuads(); + } else if (this.currentPrimitive === Raw.Module.DU_DRAW_POINTS) { + this.endPoints(); + } + }; + } + + debugDrawHeightfieldSolid(hf: RecastHeightfield): void { + Raw.RecastDebugDraw.debugDrawHeightfieldSolid(this.debugDrawImpl, hf.raw) + } + + debugDrawHeightfieldWalkable(hf: RecastHeightfield): void { + Raw.RecastDebugDraw.debugDrawHeightfieldWalkable(this.debugDrawImpl, hf.raw) + } + + debugDrawCompactHeightfieldSolid(chf: RecastCompactHeightfield): void { + Raw.RecastDebugDraw.debugDrawCompactHeightfieldSolid(this.debugDrawImpl, chf.raw) + } + + debugDrawCompactHeightfieldRegions(chf: RecastCompactHeightfield): void { + Raw.RecastDebugDraw.debugDrawCompactHeightfieldRegions(this.debugDrawImpl, chf.raw) + } + + debugDrawCompactHeightfieldDistance(chf: RecastCompactHeightfield): void { + Raw.RecastDebugDraw.debugDrawCompactHeightfieldDistance(this.debugDrawImpl, chf.raw) + } + + debugDrawHeightfieldLayer(layer: RecastHeightfieldLayer, idx: number): void { + Raw.RecastDebugDraw.debugDrawHeightfieldLayer(this.debugDrawImpl, layer.raw, idx) + } + + debugDrawHeightfieldLayers(lset: RecastHeightfieldLayerSet): void { + Raw.RecastDebugDraw.debugDrawHeightfieldLayers(this.debugDrawImpl, lset.raw); + } + + debugDrawRegionConnections(cset: RecastContourSet, alpha: number): void { + Raw.RecastDebugDraw.debugDrawRegionConnections(this.debugDrawImpl, cset.raw, alpha); + } + + debugDrawRawContours(cset: RecastContourSet, alpha: number): void { + Raw.RecastDebugDraw.debugDrawRawContours(this.debugDrawImpl, cset.raw, alpha); + } + + debugDrawContours(cset: RecastContourSet, alpha: number): void { + Raw.RecastDebugDraw.debugDrawContours(this.debugDrawImpl, cset.raw, alpha); + } + + debugDrawPolyMesh(mesh: RecastPolyMesh): void { + Raw.RecastDebugDraw.debugDrawPolyMesh(this.debugDrawImpl, mesh.raw) + } + + debugDrawPolyMeshDetail(dmesh: RecastPolyMeshDetail): void { + Raw.RecastDebugDraw.debugDrawPolyMeshDetail(this.debugDrawImpl, dmesh.raw) + } + + debugDrawNavMesh(mesh: NavMesh, flags: number): void { + Raw.DetourDebugDraw.debugDrawNavMesh(this.debugDrawImpl, mesh.raw.getNavMesh(), flags) + } + + debugDrawNavMeshWithClosedList(mesh: NavMesh, query: NavMeshQuery, flags: number): void { + Raw.DetourDebugDraw.debugDrawNavMeshWithClosedList(this.debugDrawImpl, mesh.raw.m_navMesh, query.raw.m_navQuery, flags) + } + + debugDrawNavMeshNodes(query: NavMeshQuery): void { + Raw.DetourDebugDraw.debugDrawNavMeshNodes(this.debugDrawImpl, query.raw.m_navQuery) + } + + debugDrawNavMeshBVTree(mesh: NavMesh): void { + Raw.DetourDebugDraw.debugDrawNavMeshBVTree(this.debugDrawImpl, mesh.raw.m_navMesh) + } + + debugDrawNavMeshPortals(mesh: NavMesh): void { + Raw.DetourDebugDraw.debugDrawNavMeshPortals(this.debugDrawImpl, mesh.raw.m_navMesh) + } + + debugDrawNavMeshPolysWithFlags(mesh: NavMesh, flags: number, col: number): void { + Raw.DetourDebugDraw.debugDrawNavMeshPolysWithFlags(this.debugDrawImpl, mesh.raw.m_navMesh, flags, col) + } + + debugDrawNavMeshPoly(mesh: NavMesh, ref: number, col: number): void { + Raw.DetourDebugDraw.debugDrawNavMeshPoly(this.debugDrawImpl, mesh.raw.m_navMesh, ref, col) + } + + // todo: + // - debugDrawTileCacheLayerAreas + // - debugDrawTileCacheLayerRegions + // - debugDrawTileCacheContours + // - debugDrawTileCachePolyMesh + + resize(width: number, height: number): void { + this.lineMaterial.resolution.set(width, height); + } + + reset(): void { + for (const child of this.children) { + if (child instanceof THREE.Mesh || child instanceof LineSegments2) { + child.geometry.dispose(); + } + } + + this.clear(); + } + + dispose(): void { + this.reset(); + + Raw.Module.destroy(this.debugDrawImpl); + } + + private vertex(x: number, y: number, z: number, color: number) { + const r = ((color >> 16) & 0xff) / 255; + const g = ((color >> 8) & 0xff) / 255; + const b = (color & 0xff) / 255; + const a = ((color >> 24) & 0xff) / 255; + + this.currentVertices.push({ x, y, z, r, g, b, a }); + } + + private endPoints(): void { + const geometry = this.pointGeometry; + + const instancedMesh = new THREE.InstancedMesh( + geometry, + this.pointMaterial, + this.currentVertices.length + ); + + for (let i = 0; i < this.currentVertices.length; i++) { + const point = this.currentVertices[i]; + + instancedMesh.setMatrixAt( + i, + new THREE.Matrix4().setPosition(point.x, point.y, point.z) + ); + + instancedMesh.setColorAt(i, _color.setRGB(point.r, point.g, point.b)); + } + + instancedMesh.instanceMatrix.needsUpdate = true; + + this.add(instancedMesh); + } + + private endLines(): void { + const lineSegmentsGeometry = new LineSegmentsGeometry(); + + const positions: number[] = []; + const colors: number[] = []; + + for (let i = 0; i < this.currentVertices.length; i += 2) { + const p1 = this.currentVertices[i]; + const p2 = this.currentVertices[i + 1]; + + positions.push(p1.x, p1.y, p1.z); + positions.push(p2.x, p2.y, p2.z); + + colors.push(p1.r, p1.g, p1.b); + colors.push(p2.r, p2.g, p2.b); + } + + lineSegmentsGeometry.setPositions(positions); + lineSegmentsGeometry.setColors(colors); + + const lineSegments = new LineSegments2( + lineSegmentsGeometry, + this.lineMaterial + ); + + this.add(lineSegments); + } + + private endTris(): void { + const geometry = new THREE.BufferGeometry(); + const positions = new Float32Array(this.currentVertices.length * 3); + const colors = new Float32Array(this.currentVertices.length * 3); + + for (let i = 0; i < this.currentVertices.length; i++) { + const point = this.currentVertices[i]; + positions[i * 3 + 0] = point.x; + positions[i * 3 + 1] = point.y; + positions[i * 3 + 2] = point.z; + + colors[i * 3 + 0] = point.r; + colors[i * 3 + 1] = point.g; + colors[i * 3 + 2] = point.b; + } + + geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3)); + geometry.setAttribute('color', new THREE.BufferAttribute(colors, 3)); + + const material = this.triMaterial; + const mesh = new THREE.Mesh(geometry, material); + + this.add(mesh); + } + + private endQuads(): void { + const positions: number[] = []; + const colors: number[] = []; + + for (let i = 0; i < this.currentVertices.length; i += 4) { + const vertices = [ + this.currentVertices[i], + this.currentVertices[i + 1], + this.currentVertices[i + 2], + + this.currentVertices[i], + this.currentVertices[i + 2], + this.currentVertices[i + 3], + ]; + + for (const vertex of vertices) { + positions.push(vertex.x, vertex.y, vertex.z); + colors.push(vertex.r, vertex.g, vertex.b); + } + } + + const geometry = new THREE.BufferGeometry(); + geometry.setAttribute( + 'position', + new THREE.BufferAttribute(new Float32Array(positions), 3) + ); + geometry.setAttribute( + 'color', + new THREE.BufferAttribute(new Float32Array(colors), 3) + ); + + const material = this.triMaterial; + + const mesh = new THREE.Mesh(geometry, material); + + this.add(mesh); + } +} diff --git a/packages/recast-navigation-three/src/debug/index.ts b/packages/recast-navigation-three/src/debug/index.ts new file mode 100644 index 00000000..5d2f9f7e --- /dev/null +++ b/packages/recast-navigation-three/src/debug/index.ts @@ -0,0 +1 @@ +export * from './debug-drawer'; diff --git a/packages/recast-navigation-three/src/helpers/heightfield-helper.ts b/packages/recast-navigation-three/src/helpers/heightfield-helper.ts deleted file mode 100644 index 96e0dd43..00000000 --- a/packages/recast-navigation-three/src/helpers/heightfield-helper.ts +++ /dev/null @@ -1,144 +0,0 @@ -import { RecastHeightfield, RecastSpan } from '@recast-navigation/core'; -import { - BoxGeometry, - Color, - ColorRepresentation, - DynamicDrawUsage, - InstancedMesh, - Material, - Matrix4, - MeshBasicMaterial, - Object3D, - Vector3Tuple, -} from 'three'; - -const tmpMatrix4 = new Matrix4(); - -export type HeightfieldHelperParams = { - heightfields: RecastHeightfield[]; - - /** - * @default false - */ - highlightWalkable?: boolean; - - /** - * @default 'blue' - */ - defaultColor?: ColorRepresentation; - - /** - * @default 'green' - */ - walkableColor?: ColorRepresentation; - - /** - * @default MeshBasicMaterial - */ - material?: Material; -}; - -export class HeightfieldHelper extends Object3D { - heightfields: RecastHeightfield[]; - - highlightWalkable: boolean; - - defaultColor: Color; - - walkableColor: Color; - - material: Material; - - constructor({ - heightfields, - highlightWalkable = false, - material = new MeshBasicMaterial(), - defaultColor = 'blue', - walkableColor = 'green', - }: HeightfieldHelperParams) { - super(); - - this.heightfields = heightfields; - this.highlightWalkable = highlightWalkable; - this.material = material; - this.defaultColor = new Color(defaultColor); - this.walkableColor = new Color(walkableColor); - - this.update(); - } - - update(): void { - this.clear(); - - for (const hf of this.heightfields) { - const orig = hf.bmin(); - const cs = hf.cs(); - const ch = hf.ch(); - - const width = hf.width(); - const height = hf.height(); - - const boxes: { - position: Vector3Tuple; - walkable: boolean; - }[] = []; - - for (let y = 0; y < height; ++y) { - for (let x = 0; x < width; ++x) { - const fx = orig.x + x * cs; - const fz = orig.z + y * cs; - - let span: RecastSpan | null = hf.spans(x + y * width); - - while (span) { - const minX = fx; - const minY = orig.y + span.smin() * ch; - const minZ = fz; - - const maxX = fx + cs; - const maxY = orig.y + span.smax() * ch; - const maxZ = fz + cs; - - boxes.push({ - position: [ - (maxX + minX) / 2, - (maxY + minY) / 2, - (maxZ + minZ) / 2, - ], - walkable: this.highlightWalkable && span.area() !== 0, - }); - - span = span.next(); - } - } - } - - const geometry = new BoxGeometry(cs, ch, cs); - - const instancedMesh = new InstancedMesh( - geometry, - this.material, - boxes.length - ); - instancedMesh.instanceMatrix.setUsage(DynamicDrawUsage); - - for (let i = 0; i < boxes.length; i++) { - const { position, walkable } = boxes[i]; - - instancedMesh.setMatrixAt(i, tmpMatrix4.setPosition(...position)); - instancedMesh.setColorAt( - i, - walkable ? this.walkableColor : this.defaultColor - ); - } - - instancedMesh.instanceMatrix.needsUpdate = true; - - if (instancedMesh.instanceColor) { - instancedMesh.instanceColor.needsUpdate = true; - } - - this.add(instancedMesh); - } - } -} diff --git a/packages/recast-navigation-three/src/helpers/index.ts b/packages/recast-navigation-three/src/helpers/index.ts index 8917b53c..e70c60db 100644 --- a/packages/recast-navigation-three/src/helpers/index.ts +++ b/packages/recast-navigation-three/src/helpers/index.ts @@ -1,5 +1,4 @@ export * from './crowd-helper'; -export * from './heightfield-helper'; export * from './nav-mesh-helper'; export * from './off-mesh-connections-helper'; export * from './tile-cache-helper'; diff --git a/packages/recast-navigation-three/src/index.ts b/packages/recast-navigation-three/src/index.ts index dc75fa34..ee946491 100644 --- a/packages/recast-navigation-three/src/index.ts +++ b/packages/recast-navigation-three/src/index.ts @@ -1,2 +1,3 @@ +export * from './debug'; export * from './helpers'; export * from './utils'; diff --git a/packages/recast-navigation-wasm/CMakeLists.txt b/packages/recast-navigation-wasm/CMakeLists.txt index 3f4c36d0..ff1af784 100644 --- a/packages/recast-navigation-wasm/CMakeLists.txt +++ b/packages/recast-navigation-wasm/CMakeLists.txt @@ -23,12 +23,13 @@ file(GLOB_RECURSE SRC_FILES ) file(GLOB_RECURSE RECASTDETOUR_FILES - ${CMAKE_SOURCE_DIR}/recastnavigation/Detour/* - ${CMAKE_SOURCE_DIR}/recastnavigation/DetourCrowd/* - ${CMAKE_SOURCE_DIR}/recastnavigation/DetourTileCache/* - ${CMAKE_SOURCE_DIR}/recastnavigation/Recast/* - ${CMAKE_SOURCE_DIR}/recastnavigation/RecastDemo/Source/ChunkyTriMesh.cpp - ${CMAKE_SOURCE_DIR}/recastnavigation/RecastDemo/Contrib/fastlz/fastlz.c + ${CMAKE_SOURCE_DIR}/recastnavigation/Detour/* + ${CMAKE_SOURCE_DIR}/recastnavigation/DetourCrowd/* + ${CMAKE_SOURCE_DIR}/recastnavigation/DetourTileCache/* + ${CMAKE_SOURCE_DIR}/recastnavigation/Recast/* + ${CMAKE_SOURCE_DIR}/recastnavigation/DebugUtils/* + ${CMAKE_SOURCE_DIR}/recastnavigation/RecastDemo/Source/ChunkyTriMesh.cpp + ${CMAKE_SOURCE_DIR}/recastnavigation/RecastDemo/Contrib/fastlz/fastlz.c ) include_directories( diff --git a/packages/recast-navigation-wasm/recast-navigation.idl b/packages/recast-navigation-wasm/recast-navigation.idl index 58609d7f..a2192683 100644 --- a/packages/recast-navigation-wasm/recast-navigation.idl +++ b/packages/recast-navigation-wasm/recast-navigation.idl @@ -386,6 +386,8 @@ interface NavMesh { void NavMesh(); void NavMesh(dtNavMesh navMesh); + attribute dtNavMesh m_navMesh; + boolean initSolo(UnsignedCharArray navMeshData); boolean initTiled([Const] dtNavMeshParams params); unsigned long addTile(UnsignedCharArray navMeshData, long flags, unsigned long lastRef, UnsignedIntRef tileRef); @@ -983,6 +985,15 @@ interface DetourNavMeshBuilder { CreateNavMeshDataResult createNavMeshData([Ref] dtNavMeshCreateParams params); }; +interface dtTileCacheLayer { +}; + +interface dtTileCacheContourSet { +}; + +interface dtTileCachePolyMesh { +}; + interface dtTileCacheLayerHeader { void dtTileCacheLayerHeader(); @@ -1173,3 +1184,67 @@ interface NavMeshExporter { [Value] NavMeshExport exportNavMesh(NavMesh navMesh, TileCache tileCache); void freeNavMeshExport(NavMeshExport navMeshExport); }; + +enum duDebugDrawPrimitives { + "duDebugDrawPrimitives::DU_DRAW_POINTS", + "duDebugDrawPrimitives::DU_DRAW_LINES", + "duDebugDrawPrimitives::DU_DRAW_TRIS", + "duDebugDrawPrimitives::DU_DRAW_QUADS" +}; + +interface duDebugDraw { +}; + +interface DebugDraw { +}; +DebugDraw implements duDebugDraw; + +[JSImplementation="DebugDraw"] +interface DebugDrawImpl { + void DebugDrawImpl(); + + void handleDepthMask(boolean state); + void handleTexture(boolean state); + void handleBegin([Const] duDebugDrawPrimitives prim, float size); + void handleVertexWithColor([Const] float x, [Const] float y, [Const] float z, [Const] unsigned long color); + void handleVertexWithColorAndUV([Const] float x, [Const] float y, [Const] float z, [Const] unsigned long color, [Const] float u, [Const] float v); + void handleEnd(); +}; + +interface RecastDebugDraw { + void RecastDebugDraw(); + + void debugDrawHeightfieldSolid(duDebugDraw dd, [Const, Ref] rcHeightfield hf); + void debugDrawHeightfieldWalkable(duDebugDraw dd, [Const, Ref] rcHeightfield hf); + + void debugDrawCompactHeightfieldSolid(duDebugDraw dd, [Const, Ref] rcCompactHeightfield chf); + void debugDrawCompactHeightfieldRegions(duDebugDraw dd, [Const, Ref] rcCompactHeightfield chf); + void debugDrawCompactHeightfieldDistance(duDebugDraw dd, [Const, Ref] rcCompactHeightfield chf); + + void debugDrawHeightfieldLayer(duDebugDraw dd, [Const, Ref] rcHeightfieldLayer layer, long idx); + void debugDrawHeightfieldLayers(duDebugDraw dd, [Const, Ref] rcHeightfieldLayerSet lset); + + void debugDrawRegionConnections(duDebugDraw dd, [Const, Ref] rcContourSet cset, float alpha); + void debugDrawRawContours(duDebugDraw dd, [Const, Ref] rcContourSet cset, float alpha); + void debugDrawContours(duDebugDraw dd, [Const, Ref] rcContourSet cset, float alpha); + + void debugDrawPolyMesh(duDebugDraw dd, [Const, Ref] rcPolyMesh mesh); + void debugDrawPolyMeshDetail(duDebugDraw dd, [Const, Ref] rcPolyMeshDetail dmesh); +}; + +interface DetourDebugDraw { + void DetourDebugDraw(); + + void debugDrawNavMesh(duDebugDraw dd, [Const, Ref] dtNavMesh mesh, [Const] unsigned long flags); + void debugDrawNavMeshWithClosedList(duDebugDraw dd, [Const, Ref] dtNavMesh mesh, [Const, Ref] dtNavMeshQuery query, [Const] unsigned long flags); + void debugDrawNavMeshNodes(duDebugDraw dd, [Const, Ref] dtNavMeshQuery query); + void debugDrawNavMeshBVTree(duDebugDraw dd, [Const, Ref] dtNavMesh mesh); + void debugDrawNavMeshPortals(duDebugDraw dd, [Const, Ref] dtNavMesh mesh); + void debugDrawNavMeshPolysWithFlags(duDebugDraw dd, [Const, Ref] dtNavMesh mesh, [Const] unsigned short flags, [Const] unsigned long col); + void debugDrawNavMeshPoly(duDebugDraw dd, [Const, Ref] dtNavMesh mesh, unsigned long ref, unsigned long col); + + void debugDrawTileCacheLayerAreas(duDebugDraw dd, [Const, Ref] dtTileCacheLayer layer, [Const] float cs, [Const] float ch); + void debugDrawTileCacheLayerRegions(duDebugDraw dd, [Const, Ref] dtTileCacheLayer layer, [Const] float cs, [Const] float ch); + void debugDrawTileCacheContours(duDebugDraw dd, [Const, Ref] dtTileCacheContourSet lcset, [Const] float[] orig, [Const] float cs, [Const] float ch); + void debugDrawTileCachePolyMesh(duDebugDraw dd, [Const, Ref] dtTileCachePolyMesh pmesh, [Const] float[] orig, [Const] float cs, [Const] float ch); +}; diff --git a/packages/recast-navigation-wasm/src/DebugDraw/DebugDraw.cpp b/packages/recast-navigation-wasm/src/DebugDraw/DebugDraw.cpp new file mode 100644 index 00000000..f830348c --- /dev/null +++ b/packages/recast-navigation-wasm/src/DebugDraw/DebugDraw.cpp @@ -0,0 +1,41 @@ +#include "DebugDraw.h" + +void DebugDraw::depthMask(bool state) +{ + handleDepthMask(state); +} + +void DebugDraw::texture(bool state) +{ + handleTexture(state); +} + +void DebugDraw::begin(duDebugDrawPrimitives prim, float size) +{ + handleBegin(prim, size); +} + +void DebugDraw::vertex(const float* pos, unsigned int color) +{ + handleVertexWithColor(pos[0], pos[1], pos[2], color); +} + +void DebugDraw::vertex(const float x, const float y, const float z, unsigned int color) +{ + handleVertexWithColor(x, y, z, color); +} + +void DebugDraw::vertex(const float* pos, unsigned int color, const float* uv) +{ + handleVertexWithColorAndUV(pos[0], pos[1], pos[2], color, uv[0], uv[1]); +} + +void DebugDraw::vertex(const float x, const float y, const float z, unsigned int color, const float u, const float v) +{ + handleVertexWithColorAndUV(x, y, z, color, u, v); +} + +void DebugDraw::end() +{ + handleEnd(); +} diff --git a/packages/recast-navigation-wasm/src/DebugDraw/DebugDraw.h b/packages/recast-navigation-wasm/src/DebugDraw/DebugDraw.h new file mode 100644 index 00000000..e0d5108f --- /dev/null +++ b/packages/recast-navigation-wasm/src/DebugDraw/DebugDraw.h @@ -0,0 +1,25 @@ +#pragma once + +#include "../../recastnavigation/DebugUtils/Include/DebugDraw.h" +#include "../../recastnavigation/DebugUtils/Include/RecastDebugDraw.h" +#include "../../recastnavigation/DebugUtils/Include/DetourDebugDraw.h" + +class DebugDraw : public duDebugDraw +{ +public: + virtual void depthMask(bool state); + virtual void texture(bool state); + virtual void begin(duDebugDrawPrimitives prim, float size = 1.0f); + virtual void vertex(const float* pos, unsigned int color); + virtual void vertex(const float x, const float y, const float z, unsigned int color); + virtual void vertex(const float* pos, unsigned int color, const float* uv); + virtual void vertex(const float x, const float y, const float z, unsigned int color, const float u, const float v); + virtual void end(); + + virtual void handleDepthMask(bool state) = 0; + virtual void handleTexture(bool state) = 0; + virtual void handleBegin(duDebugDrawPrimitives prim, float size = 1.0f) = 0; + virtual void handleVertexWithColor(const float x, const float y, const float z, unsigned int color) = 0; + virtual void handleVertexWithColorAndUV(const float x, const float y, const float z, unsigned int color, const float u, const float v) = 0; + virtual void handleEnd() = 0; +}; diff --git a/packages/recast-navigation-wasm/src/DebugDraw/DetourDebugDraw.cpp b/packages/recast-navigation-wasm/src/DebugDraw/DetourDebugDraw.cpp new file mode 100644 index 00000000..e217e505 --- /dev/null +++ b/packages/recast-navigation-wasm/src/DebugDraw/DetourDebugDraw.cpp @@ -0,0 +1,47 @@ +#include "DetourDebugDraw.h" + +void DetourDebugDraw::debugDrawNavMesh(duDebugDraw* dd, const dtNavMesh& mesh, const int flags) +{ + duDebugDrawNavMesh(dd, mesh, flags); +} +void DetourDebugDraw::debugDrawNavMeshWithClosedList(struct duDebugDraw* dd, const dtNavMesh& mesh, const dtNavMeshQuery& query, unsigned char flags) +{ + duDebugDrawNavMeshWithClosedList(dd, mesh, query, flags); +} +void DetourDebugDraw::debugDrawNavMeshNodes(struct duDebugDraw* dd, const dtNavMeshQuery& query) +{ + duDebugDrawNavMeshNodes(dd, query); +} +void DetourDebugDraw::debugDrawNavMeshBVTree(struct duDebugDraw* dd, const dtNavMesh& mesh) +{ + duDebugDrawNavMeshBVTree(dd, mesh); +} +void DetourDebugDraw::debugDrawNavMeshPortals(struct duDebugDraw* dd, const dtNavMesh& mesh) +{ + duDebugDrawNavMeshPortals(dd, mesh); +} +void DetourDebugDraw::debugDrawNavMeshPolysWithFlags(struct duDebugDraw* dd, const dtNavMesh& mesh, const unsigned short polyFlags, const unsigned int col) +{ + duDebugDrawNavMeshPolysWithFlags(dd, mesh, polyFlags, col); +} +void DetourDebugDraw::debugDrawNavMeshPoly(struct duDebugDraw* dd, const dtNavMesh& mesh, dtPolyRef ref, const unsigned int col) +{ + duDebugDrawNavMeshPoly(dd, mesh, ref, col); +} + +void DetourDebugDraw::debugDrawTileCacheLayerAreas(struct duDebugDraw* dd, const dtTileCacheLayer& layer, const float cs, const float ch) +{ + duDebugDrawTileCacheLayerAreas(dd, layer, cs, ch); +} +void DetourDebugDraw::debugDrawTileCacheLayerRegions(struct duDebugDraw* dd, const dtTileCacheLayer& layer, const float cs, const float ch) +{ + duDebugDrawTileCacheLayerRegions(dd, layer, cs, ch); +} +void DetourDebugDraw::debugDrawTileCacheContours(duDebugDraw* dd, const struct dtTileCacheContourSet& lcset, const float* orig, const float cs, const float ch) +{ + duDebugDrawTileCacheContours(dd, lcset, orig, cs, ch); +} +void DetourDebugDraw::debugDrawTileCachePolyMesh(duDebugDraw* dd, const struct dtTileCachePolyMesh& lmesh, const float* orig, const float cs, const float ch) +{ + duDebugDrawTileCachePolyMesh(dd, lmesh, orig, cs, ch); +} diff --git a/packages/recast-navigation-wasm/src/DebugDraw/DetourDebugDraw.h b/packages/recast-navigation-wasm/src/DebugDraw/DetourDebugDraw.h new file mode 100644 index 00000000..424f82cb --- /dev/null +++ b/packages/recast-navigation-wasm/src/DebugDraw/DetourDebugDraw.h @@ -0,0 +1,21 @@ +#pragma once + +#include "../../recastnavigation/DebugUtils/Include/DebugDraw.h" +#include "../../recastnavigation/DebugUtils/Include/DetourDebugDraw.h" + +class DetourDebugDraw +{ +public: + void debugDrawNavMesh(duDebugDraw* dd, const dtNavMesh& mesh, const int flags); + void debugDrawNavMeshWithClosedList(struct duDebugDraw* dd, const dtNavMesh& mesh, const dtNavMeshQuery& query, unsigned char flags); + void debugDrawNavMeshNodes(struct duDebugDraw* dd, const dtNavMeshQuery& query); + void debugDrawNavMeshBVTree(struct duDebugDraw* dd, const dtNavMesh& mesh); + void debugDrawNavMeshPortals(struct duDebugDraw* dd, const dtNavMesh& mesh); + void debugDrawNavMeshPolysWithFlags(struct duDebugDraw* dd, const dtNavMesh& mesh, const unsigned short polyFlags, const unsigned int col); + void debugDrawNavMeshPoly(struct duDebugDraw* dd, const dtNavMesh& mesh, dtPolyRef ref, const unsigned int col); + + void debugDrawTileCacheLayerAreas(struct duDebugDraw* dd, const dtTileCacheLayer& layer, const float cs, const float ch); + void debugDrawTileCacheLayerRegions(struct duDebugDraw* dd, const dtTileCacheLayer& layer, const float cs, const float ch); + void debugDrawTileCacheContours(duDebugDraw* dd, const struct dtTileCacheContourSet& lcset, const float* orig, const float cs, const float ch); + void debugDrawTileCachePolyMesh(duDebugDraw* dd, const struct dtTileCachePolyMesh& lmesh, const float* orig, const float cs, const float ch); +}; diff --git a/packages/recast-navigation-wasm/src/DebugDraw/RecastDebugDraw.cpp b/packages/recast-navigation-wasm/src/DebugDraw/RecastDebugDraw.cpp new file mode 100644 index 00000000..74ea9f40 --- /dev/null +++ b/packages/recast-navigation-wasm/src/DebugDraw/RecastDebugDraw.cpp @@ -0,0 +1,61 @@ +#include "RecastDebugDraw.h" + +void RecastDebugDraw::debugDrawHeightfieldSolid(duDebugDraw* dd, const rcHeightfield& hf) +{ + duDebugDrawHeightfieldSolid(dd, hf); +} + +void RecastDebugDraw::debugDrawHeightfieldWalkable(duDebugDraw* dd, const rcHeightfield& hf) +{ + duDebugDrawHeightfieldWalkable(dd, hf); +} + +void RecastDebugDraw::debugDrawCompactHeightfieldSolid(duDebugDraw* dd, const rcCompactHeightfield& chf) +{ + duDebugDrawCompactHeightfieldSolid(dd, chf); +} + +void RecastDebugDraw::debugDrawCompactHeightfieldRegions(duDebugDraw* dd, const rcCompactHeightfield& chf) +{ + duDebugDrawCompactHeightfieldRegions(dd, chf); +} + +void RecastDebugDraw::debugDrawCompactHeightfieldDistance(duDebugDraw* dd, const rcCompactHeightfield& chf) +{ + duDebugDrawCompactHeightfieldDistance(dd, chf); +} + +void RecastDebugDraw::debugDrawHeightfieldLayer(duDebugDraw* dd, const rcHeightfieldLayer& layer, const int idx) +{ + duDebugDrawHeightfieldLayer(dd, layer, idx); +} + +void RecastDebugDraw::debugDrawHeightfieldLayers(duDebugDraw* dd, const rcHeightfieldLayerSet& lset) +{ + duDebugDrawHeightfieldLayers(dd, lset); +} + +void RecastDebugDraw::debugDrawRegionConnections(duDebugDraw* dd, const rcContourSet& cset, const float alpha = 1.0f) +{ + duDebugDrawRegionConnections(dd, cset, alpha); +} + +void RecastDebugDraw::debugDrawRawContours(duDebugDraw* dd, const rcContourSet& cset, const float alpha = 1.0f) +{ + duDebugDrawRawContours(dd, cset, alpha); +} + +void RecastDebugDraw::debugDrawContours(duDebugDraw* dd, const rcContourSet& cset, const float alpha = 1.0f) +{ + duDebugDrawContours(dd, cset, alpha); +} + +void RecastDebugDraw::debugDrawPolyMesh(duDebugDraw* dd, const rcPolyMesh& mesh) +{ + duDebugDrawPolyMesh(dd, mesh); +} + +void RecastDebugDraw::debugDrawPolyMeshDetail(duDebugDraw* dd, const rcPolyMeshDetail& dmesh) +{ + duDebugDrawPolyMeshDetail(dd, dmesh); +} \ No newline at end of file diff --git a/packages/recast-navigation-wasm/src/DebugDraw/RecastDebugDraw.h b/packages/recast-navigation-wasm/src/DebugDraw/RecastDebugDraw.h new file mode 100644 index 00000000..f5b016bf --- /dev/null +++ b/packages/recast-navigation-wasm/src/DebugDraw/RecastDebugDraw.h @@ -0,0 +1,32 @@ +#pragma once + +#include "../../recastnavigation/DebugUtils/Include/DebugDraw.h" +#include "../../recastnavigation/DebugUtils/Include/RecastDebugDraw.h" + +class RecastDebugDraw +{ +public: + void debugDrawHeightfieldSolid(duDebugDraw* dd, const rcHeightfield& hf); + + void debugDrawHeightfieldWalkable(duDebugDraw* dd, const rcHeightfield& hf); + + void debugDrawCompactHeightfieldSolid(duDebugDraw* dd, const rcCompactHeightfield& chf); + + void debugDrawCompactHeightfieldRegions(duDebugDraw* dd, const rcCompactHeightfield& chf); + + void debugDrawCompactHeightfieldDistance(duDebugDraw* dd, const rcCompactHeightfield& chf); + + void debugDrawHeightfieldLayer(duDebugDraw* dd, const rcHeightfieldLayer& layer, const int idx); + + void debugDrawHeightfieldLayers(duDebugDraw* dd, const rcHeightfieldLayerSet& lset); + + void debugDrawRegionConnections(duDebugDraw* dd, const rcContourSet& cset, const float alpha); + + void debugDrawRawContours(duDebugDraw* dd, const rcContourSet& cset, const float alpha); + + void debugDrawContours(duDebugDraw* dd, const rcContourSet& cset, const float alpha); + + void debugDrawPolyMesh(duDebugDraw* dd, const rcPolyMesh& mesh); + + void debugDrawPolyMeshDetail(duDebugDraw* dd, const rcPolyMeshDetail& dmesh); +}; \ No newline at end of file diff --git a/packages/recast-navigation-wasm/src/recast-navigation.h b/packages/recast-navigation-wasm/src/recast-navigation.h index 63b77818..cb17a782 100644 --- a/packages/recast-navigation-wasm/src/recast-navigation.h +++ b/packages/recast-navigation-wasm/src/recast-navigation.h @@ -12,3 +12,6 @@ #include "./Recast.h" #include "./Detour.h" #include "./ChunkyTriMesh.h" +#include "./DebugDraw/DebugDraw.h" +#include "./DebugDraw/RecastDebugDraw.h" +#include "./DebugDraw/DetourDebugDraw.h" diff --git a/packages/recast-navigation/.storybook/stories/debug/debug-drawer.stories.tsx b/packages/recast-navigation/.storybook/stories/debug/debug-drawer.stories.tsx new file mode 100644 index 00000000..050f8092 --- /dev/null +++ b/packages/recast-navigation/.storybook/stories/debug/debug-drawer.stories.tsx @@ -0,0 +1,170 @@ +import { OrbitControls, PerspectiveCamera } from '@react-three/drei'; +import { threeToSoloNavMesh } from '@recast-navigation/three'; +import { DebugDrawer } from '@recast-navigation/three/src/debug-drawer'; +import React, { useEffect, useState } from 'react'; +import { Group, Mesh } from 'three'; +import { NavTestEnvironment } from '../../common/nav-test-environment'; +import { decorators } from '../../decorators'; +import { parameters } from '../../parameters'; +import { NavMeshQuery } from '@recast-navigation/core'; + +export default { + title: 'Debug / Three Debug Drawer', + decorators, + parameters, +}; + +const DebugDrawExample = { + HEIGHTFIELD_SOLID: 'HEIGHTFIELD_SOLID', + HEIGHTFIELD_WALKABLE: 'HEIGHTFIELD_WALKABLE', + COMPACT_HEIGHTFIELD_SOLID: 'COMPACT_HEIGHTFIELD_SOLID', + COMPACT_HEIGHTFIELD_REGIONS: 'COMPACT_HEIGHTFIELD_REGIONS', + COMPACT_HEIGHTFIELD_DISTANCE: 'COMPACT_HEIGHTFIELD_DISTANCE', + RAW_CONTOURS: 'RAW_CONTOURS', + CONTOURS: 'CONTOURS', + POLY_MESH: 'POLY_MESH', + POLY_MESH_DETAIL: 'POLY_MESH_DETAIL', + NAVMESH: 'NAVMESH', + NAVMESH_BV_TREE: 'NAVMESH_BV_TREE', +}; + +type DebugDrawProps = { + example: (typeof DebugDrawExample)[keyof typeof DebugDrawExample]; + showModel?: boolean; +}; + +const DebugDraw = ({ example, showModel = false }: DebugDrawProps) => { + const [group, setGroup] = useState(null); + + const [debugDrawer, setDebugDrawer] = useState(); + + useEffect(() => { + if (!group) return; + + const meshes: Mesh[] = []; + + group.traverse((child) => { + if (child instanceof Mesh) { + meshes.push(child); + } + }); + + const config = { + cs: 0.05, + ch: 0.2, + walkableHeight: 1, + walkableClimb: 2.5, + walkableRadius: 1, + borderSize: 0.2, + }; + + const { success, navMesh, intermediates } = threeToSoloNavMesh( + meshes, + config, + true + ); + + if (!success) return; + + const debugDrawer = new DebugDrawer(); + debugDrawer.resize(window.innerWidth, window.innerHeight); + + if (example === DebugDrawExample.HEIGHTFIELD_SOLID) { + debugDrawer.debugDrawHeightfieldSolid(intermediates.heightfield!); + } else if (example === DebugDrawExample.HEIGHTFIELD_WALKABLE) { + debugDrawer.debugDrawHeightfieldWalkable(intermediates.heightfield!); + } else if (example === DebugDrawExample.COMPACT_HEIGHTFIELD_SOLID) { + debugDrawer.debugDrawCompactHeightfieldSolid( + intermediates.compactHeightfield! + ); + } else if (example === DebugDrawExample.COMPACT_HEIGHTFIELD_REGIONS) { + debugDrawer.debugDrawCompactHeightfieldRegions( + intermediates.compactHeightfield! + ); + } else if (example === DebugDrawExample.COMPACT_HEIGHTFIELD_DISTANCE) { + debugDrawer.debugDrawCompactHeightfieldDistance( + intermediates.compactHeightfield! + ); + } else if (example === DebugDrawExample.RAW_CONTOURS) { + debugDrawer.debugDrawRawContours(intermediates.contourSet!, 1); + } else if (example === DebugDrawExample.CONTOURS) { + debugDrawer.debugDrawContours(intermediates.contourSet!, 1); + } else if (example === DebugDrawExample.POLY_MESH) { + debugDrawer.debugDrawPolyMesh(intermediates.polyMesh!); + } else if (example === DebugDrawExample.POLY_MESH_DETAIL) { + debugDrawer.debugDrawPolyMeshDetail(intermediates.polyMeshDetail!); + } else if (example === DebugDrawExample.NAVMESH) { + debugDrawer.debugDrawNavMesh(navMesh, 0); + } else if (example === DebugDrawExample.NAVMESH_BV_TREE) { + debugDrawer.debugDrawNavMeshBVTree(navMesh); + } + + setDebugDrawer(debugDrawer); + + return () => { + setDebugDrawer(undefined); + + navMesh?.destroy(); + }; + }, [group]); + + return ( + <> + + + + + {debugDrawer && } + + + + + + ); +}; + +// Recast +export const _HeightfieldSolid = () => ( + +); + +export const _HeightfieldWalkable = () => ( + +); + +export const _CompactHeightfieldSolid = () => ( + +); + +export const _CompactHeightfieldRegions = () => ( + +); + +export const _CompactHeightfieldDistance = () => ( + +); + +export const _RawContours = () => ( + +); + +export const _Contours = () => ( + +); + +export const _PolyMesh = () => ( + +); + +export const _PolyMeshDetail = () => ( + +); + +// Detour +export const _NavMesh = () => ( + +); + +export const _NavMeshBVTree = () => ( + +); diff --git a/packages/recast-navigation/.storybook/stories/helpers/heightfield-helper.stories.tsx b/packages/recast-navigation/.storybook/stories/helpers/heightfield-helper.stories.tsx deleted file mode 100644 index 9e07535c..00000000 --- a/packages/recast-navigation/.storybook/stories/helpers/heightfield-helper.stories.tsx +++ /dev/null @@ -1,77 +0,0 @@ -import { OrbitControls } from '@react-three/drei'; -import { RecastHeightfield } from '@recast-navigation/core'; -import { - HeightfieldHelper, - threeToTiledNavMesh, -} from '@recast-navigation/three'; -import React, { useEffect, useState } from 'react'; -import { Group, Mesh, MeshStandardMaterial } from 'three'; -import { NavTestEnvironment } from '../../common/nav-test-environment'; -import { decorators } from '../../decorators'; -import { parameters } from '../../parameters'; - -export default { - title: 'Helpers / Heightfield Helper', - decorators, - parameters, -}; - -export const HeightfieldHelperExample = () => { - const [group, setGroup] = useState(null); - - const [heightfieldHelper, setHeightfieldHelper] = useState< - HeightfieldHelper | undefined - >(); - - useEffect(() => { - if (!group) return; - - const meshes: Mesh[] = []; - - group.traverse((child) => { - if (child instanceof Mesh) { - meshes.push(child); - } - }); - - const config = { - cs: 0.05, - ch: 0.2, - walkableHeight: 1, - walkableClimb: 2.5, - walkableRadius: 1, - borderSize: 0.2, - tileSize: 16, - }; - - const { intermediates } = threeToTiledNavMesh(meshes, config, true); - const heightfields = intermediates!.tileIntermediates - .map(({ heightfield }) => heightfield) - .filter(Boolean) as RecastHeightfield[]; - - const heightfieldHelper = new HeightfieldHelper({ - heightfields, - material: new MeshStandardMaterial(), - highlightWalkable: true, - }); - heightfieldHelper.update(); - - setHeightfieldHelper(heightfieldHelper); - - return () => { - setHeightfieldHelper(undefined); - }; - }, [group]); - - return ( - <> - - - - - {heightfieldHelper && } - - - - ); -}; diff --git a/packages/recast-navigation/.storybook/stories/nav-mesh/models.stories.tsx b/packages/recast-navigation/.storybook/stories/nav-mesh/models.stories.tsx index d3ae68e1..b465b84f 100644 --- a/packages/recast-navigation/.storybook/stories/nav-mesh/models.stories.tsx +++ b/packages/recast-navigation/.storybook/stories/nav-mesh/models.stories.tsx @@ -74,10 +74,10 @@ const Common = ({ level, type, tileSize }: CommonProps) => { }; if (type === 'solo') { - const { navMesh } = threeToSoloNavMesh(meshes, config, true); + const { navMesh } = threeToSoloNavMesh(meshes, config, false); setNavMesh(navMesh); } else if (type === 'tiled') { - const { navMesh } = threeToTiledNavMesh(meshes, config, true); + const { navMesh } = threeToTiledNavMesh(meshes, config, false); setNavMesh(navMesh); } else { const { navMesh } = threeToTileCache(meshes, config);