Skip to content

Commit

Permalink
feat: add optional 'bounds' option to navmesh generators
Browse files Browse the repository at this point in the history
  • Loading branch information
isaac-mason committed Dec 17, 2024
1 parent 6314be8 commit 93d383e
Show file tree
Hide file tree
Showing 6 changed files with 141 additions and 7 deletions.
8 changes: 8 additions & 0 deletions .changeset/moody-years-end.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
'recast-navigation': minor
'@recast-navigation/generators': minor
---

feat: add optional 'bounds' config to navmesh generators

If provided, it will be used as the bounds for the navmesh heightfield during generation. If not provided, the bounds will be calculated from the input geometry. If the bounds are known ahead of time, providing them can save some time during generation.
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
TriangleAreasArray,
TrianglesArray,
UnsignedCharArray,
Vector3Tuple,
VerticesArray,
allocCompactHeightfield,
allocContourSet,
Expand Down Expand Up @@ -52,6 +53,12 @@ export type SoloNavMeshGeneratorConfig = Pretty<
* @default true
*/
buildBvTree?: boolean;

/**
* The minimum and maximum bounds of the heightfield's AABB in world units.
* If not provided, the bounding box will be calculated from the input positions and indices
*/
bounds?: [bbMin: Vector3Tuple, bbMax: Vector3Tuple];
}
>;

Expand Down Expand Up @@ -158,7 +165,19 @@ export const generateSoloNavMeshData = (
const trianglesArray = new TrianglesArray();
trianglesArray.copy(triangles);

const { bbMin, bbMax } = getBoundingBox(positions, indices);
let bbMin: Vector3Tuple;
let bbMax: Vector3Tuple;

if (navMeshGeneratorConfig.bounds) {
bbMin = navMeshGeneratorConfig.bounds[0];
bbMax = navMeshGeneratorConfig.bounds[1];
} else {
const boundingBox = getBoundingBox(positions, indices);
bbMin = boundingBox.bbMin;
bbMax = boundingBox.bbMax;
console.log('bbMin', bbMin);
console.log('bbMax', bbMax);
}

//
// Step 1. Initialize build config.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,12 @@ type TileCacheRecastConfig = Omit<RecastConfig, 'minRegionArea' | 'maxEdgeLen'>;

export type TileCacheGeneratorConfig = Pretty<
TileCacheRecastConfig & {
/**
* The minimum and maximum bounds of the heightfield's AABB in world units.
* If not provided, the bounding box will be calculated from the input positions and indices
*/
bounds?: [bbMin: Vector3Tuple, bbMax: Vector3Tuple];

/**
* How many layers (or "floors") each navmesh tile is expected to have.
*/
Expand Down Expand Up @@ -165,7 +171,17 @@ export const generateTileCache = (
const trianglesArray = new TrianglesArray();
trianglesArray.copy(triangles);

const { bbMin, bbMax } = getBoundingBox(positions, indices);
let bbMin: Vector3Tuple;
let bbMax: Vector3Tuple;

if (navMeshGeneratorConfig.bounds) {
bbMin = navMeshGeneratorConfig.bounds[0];
bbMax = navMeshGeneratorConfig.bounds[1];
} else {
const boundingBox = getBoundingBox(positions, indices);
bbMin = boundingBox.bbMin;
bbMax = boundingBox.bbMax;
}

const { expectedLayersPerTile, maxObstacles, ...recastConfig } = {
...tileCacheGeneratorConfigDefaults,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,12 @@ export const buildTiledNavMeshRcConfig = ({
export type TiledNavMeshGeneratorConfig = Pretty<
RecastConfig &
OffMeshConnectionGeneratorParams & {
/**
* The minimum and maximum bounds of the heightfield's AABB in world units.
* If not provided, the bounding box will be calculated from the input positions and indices
*/
bounds?: [bbMin: Vector3Tuple, bbMax: Vector3Tuple];

/**
* @default 128
*/
Expand Down Expand Up @@ -669,8 +675,17 @@ export const generateTiledNavMesh = (
...navMeshGeneratorConfig,
};

/* get input bounding box */
const { bbMin, bbMax } = getBoundingBox(positions, indices);
let bbMin: Vector3Tuple;
let bbMax: Vector3Tuple;

if (navMeshGeneratorConfig.bounds) {
bbMin = navMeshGeneratorConfig.bounds[0];
bbMax = navMeshGeneratorConfig.bounds[1];
} else {
const boundingBox = getBoundingBox(positions, indices);
bbMin = boundingBox.bbMin;
bbMax = boundingBox.bbMax;
}

const {
config: rcConfig,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -386,9 +386,9 @@ export const generateNavMesh = (
navMeshCreateParams.setPolyMeshCreateParams(polyMesh);
navMeshCreateParams.setPolyMeshDetailCreateParams(polyMeshDetail);

navMeshCreateParams.setWalkableHeight(config.walkableHeight);
navMeshCreateParams.setWalkableRadius(config.walkableRadius);
navMeshCreateParams.setWalkableClimb(config.walkableClimb);
navMeshCreateParams.setWalkableHeight(config.walkableHeight * config.ch);
navMeshCreateParams.setWalkableRadius(config.walkableRadius * config.cs);
navMeshCreateParams.setWalkableClimb(config.walkableClimb * config.ch);

navMeshCreateParams.setCellSize(config.cs);
navMeshCreateParams.setCellHeight(config.ch);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import { OrbitControls } from '@react-three/drei';
import { NavMesh, NavMeshQuery, Vector3Tuple } from '@recast-navigation/core';
import { threeToSoloNavMesh } from '@recast-navigation/three';
import React, { useEffect, useState } from 'react';
import { Box3, Group, Mesh } from 'three';
import { Debug } from '../../common/debug';
import { NavTestEnvironment } from '../../common/nav-test-environment';
import { decorators } from '../../decorators';
import { parameters } from '../../parameters';

export default {
title: 'NavMesh / Custom Bounds',
decorators,
parameters,
};

const BOUNDS = new Box3();
BOUNDS.min.set(-3, -1, -5);
BOUNDS.max.set(9, 5, 3);

export const CustomBounds = () => {
const [group, setGroup] = useState<Group | null>(null);

const [navMesh, setNavMesh] = useState<NavMesh | undefined>();
const [navMeshQuery, setNavMeshQuery] = useState<NavMeshQuery | undefined>();

useEffect(() => {
if (!group) return;

const meshes: Mesh[] = [];

group.traverse((child) => {
if (child instanceof Mesh) {
meshes.push(child);
}
});

const walkableRadiusWorld = 0.1;
const cellSize = 0.05;

const { success, navMesh } = threeToSoloNavMesh(meshes, {
cs: cellSize,
ch: 0.2,
walkableRadius: Math.ceil(walkableRadiusWorld / cellSize),
bounds: [BOUNDS.min.toArray(), BOUNDS.max.toArray()],
});

if (!success) {
return;
}

setNavMesh(navMesh);
setNavMeshQuery(navMeshQuery);

return () => {
navMesh.destroy();

setNavMesh(undefined);
setNavMeshQuery(undefined);
};
}, [group]);

return (
<>
<group ref={setGroup}>
<NavTestEnvironment />
</group>

<box3Helper args={[BOUNDS]} />

<Debug navMesh={navMesh} />

<OrbitControls makeDefault />
</>
);
};

0 comments on commit 93d383e

Please sign in to comment.