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);