Web Engine Docs
Preparing documentation
Use the search bar to quickly find any topic
Preparing documentation
Use the search bar to quickly find any topic
Generate navigation meshes from scene geometry for accurate AI pathfinding and navigation.
Web Engine uses Recast/Detour for navigation mesh generation. NavMesh provides:
Tag component that marks geometry as walkable for NavMesh generation:
import { NavMeshSurface } from '@web-engine-dev/core/engine/ecs/components';import { addComponent } from 'bitecs';// Mark a mesh as walkable for NavMeshaddComponent(world, NavMeshSurface, groundEid);
NavMesh generation is controlled by these parameters:
| Parameter | Description | Default |
|---|---|---|
| cellSize | Voxel grid cell size (smaller = more detail) | 0.3 |
| cellHeight | Voxel grid cell height | 0.2 |
| agentHeight | Height of the agent | 2.0 |
| agentRadius | Radius of the agent | 0.5 |
| agentMaxClimb | Maximum step height | 0.5 |
| agentMaxSlope | Maximum slope angle (degrees) | 45 |
import { NavMeshGenerator, NavMeshConfig } from '@web-engine-dev/core/engine/ai';// Initialize the generator (only once)await NavMeshGenerator.init();// Configure NavMesh generationconst config: NavMeshConfig = {cellSize: 0.3,cellHeight: 0.2,agentHeight: 2.0,agentRadius: 0.5,agentMaxClimb: 0.5,agentMaxSlope: 45,};// Generate NavMesh from sceneconst navMesh = NavMeshGenerator.generate(scene, world, config);if (!navMesh) {console.error('NavMesh generation failed');}
For large scenes, use the NavMesh worker bridge for non-blocking generation:
import { navMeshBridge } from '@web-engine-dev/core/engine/ai';// Generate NavMesh in a workerconst result = await navMeshBridge.generate(positions, indices, config);if (result.success && result.navMesh) {console.log('NavMesh generated successfully');// Optionally get debug mesh for visualizationif (result.debugMesh) {const { positions, indices } = result.debugMesh;// Create THREE.Mesh for debug rendering}} else {console.error('NavMesh generation failed:', result.error);}
import { navMeshBridge } from '@web-engine-dev/core/engine/ai';import * as THREE from 'three';// Find a path between two pointsconst start = new THREE.Vector3(0, 0, 0);const end = new THREE.Vector3(10, 0, 10);const pathResult = await navMeshBridge.findPath(start, end);if (pathResult.success && pathResult.path) {// Path is array of {x, y, z} pointsconst waypoints = pathResult.path.map(p =>new THREE.Vector3(p.x, p.y, p.z));console.log(`Found path with ${waypoints.length} waypoints`);} else {console.error('Pathfinding failed:', pathResult.error);}
// Find nearest point on NavMeshconst nearestPoint = navMesh.getClosestPoint(position);// Check if position is on NavMeshconst isOnNavMesh = navMesh.isPointOnNavMesh(position, maxDistance);
For runtime NavMesh updates, you can manually trigger baking:
// Extract geometry from sceneconst geometry = NavMeshGenerator.extractGeometry(scene, world);if (geometry) {const { positions, indices } = geometry;// Generate NavMeshconst navMesh = NavMeshGenerator.generate(scene, world, config);if (navMesh) {console.log('NavMesh baked successfully');}}
For dynamic environments, consider re-baking affected regions:
// Mark geometry as dirty when it changesfunction onGeometryChanged(eid: number) {// Queue for NavMesh rebuildrebuildQueue.add(eid);}// Rebuild NavMesh periodically or on demandfunction rebuildNavMesh() {if (rebuildQueue.size > 0) {// Re-extract and regenerateconst navMesh = NavMeshGenerator.generate(scene, world, config);rebuildQueue.clear();}}
// Add dynamic obstacle (future API)// Note: Dynamic obstacles require NavMesh tilecache// Currently in developmentimport { NavMeshObstacle } from '@web-engine-dev/core/engine/ecs/components';addComponent(world, NavMeshObstacle, obstacleEid);NavMeshObstacle.radius[obstacleEid] = 1.0;NavMeshObstacle.height[obstacleEid] = 2.0;
import * as THREE from 'three';// Get debug mesh from NavMeshconst debugMeshData = navMesh.getDebugNavMesh?.();if (debugMeshData) {const { positions, indices } = debugMeshData;// Create geometryconst geometry = new THREE.BufferGeometry();geometry.setAttribute('position',new THREE.BufferAttribute(positions, 3));geometry.setIndex(new THREE.BufferAttribute(indices, 1));// Create meshconst material = new THREE.MeshBasicMaterial({color: 0x00ff00,wireframe: true,transparent: true,opacity: 0.5,});const debugMesh = new THREE.Mesh(geometry, material);scene.add(debugMesh);}
function visualizePath(waypoints: THREE.Vector3[]) {const points = waypoints.map(p => new THREE.Vector3(p.x, p.y, p.z));const geometry = new THREE.BufferGeometry().setFromPoints(points);const material = new THREE.LineBasicMaterial({color: 0xff0000,linewidth: 2,});const line = new THREE.Line(geometry, material);scene.add(line);return line;}
Smaller cell sizes produce more accurate NavMesh but increase generation time and memory:
Agent radius erodes the NavMesh to ensure agents stay away from walls. Set it to match your agent's collision radius for accurate pathfinding.
Always use worker-based generation for large scenes to avoid blocking the main thread:
// Use worker for non-blocking generationconst result = await navMeshBridge.generate(positions, indices, config);// Show loading indicator during generationsetIsGenerating(true);const result = await navMeshBridge.generate(positions, indices, config);setIsGenerating(false);
import { disposeNavMeshBridge, resetNavMeshBridge } from '@web-engine-dev/core/engine/ai';// Clean up when donedisposeNavMeshBridge();// Reset for scene changeresetNavMeshBridge();
import {NavMeshGenerator,navMeshBridge,NavMeshConfig,} from '@web-engine-dev/core/engine/ai';import { NavMeshSurface } from '@web-engine-dev/core/engine/ecs/components';// 1. Initializeawait NavMeshGenerator.init();// 2. Mark walkable geometryaddComponent(world, NavMeshSurface, groundEid);addComponent(world, NavMeshSurface, rampEid);// 3. Configure and generateconst config: NavMeshConfig = {cellSize: 0.3,cellHeight: 0.2,agentHeight: 2.0,agentRadius: 0.5,agentMaxClimb: 0.5,agentMaxSlope: 45,};const geometry = NavMeshGenerator.extractGeometry(scene, world);if (!geometry) {throw new Error('No walkable geometry found');}const result = await navMeshBridge.generate(geometry.positions,geometry.indices,config);if (!result.success) {throw new Error(`NavMesh generation failed: ${result.error}`);}// 4. Find pathsconst pathResult = await navMeshBridge.findPath(start, end);if (pathResult.success && pathResult.path) {const waypoints = pathResult.path.map(p =>new THREE.Vector3(p.x, p.y, p.z));// Use waypoints for AI navigationsetBlackboardValue(blackboardId, 'path', waypoints);}// 5. Cleanup on scene unloaddisposeNavMeshBridge();