瀏覽代碼

Comments, parameters, scene tick

Cedric Guillemet 6 年之前
父節點
當前提交
bb33b3f02d

文件差異過大導致無法顯示
+ 38 - 0
dist/preview release/recast.js


文件差異過大導致無法顯示
+ 38 - 0
dist/recast.js


+ 114 - 27
src/Navigation/INavigationEngine.ts

@@ -4,45 +4,132 @@ import { AbstractMesh } from "../Meshes/abstractMesh";
 import { Mesh } from "../Meshes/mesh";
 import { Scene } from "../scene";
 
-
-/** @hidden */
+/**
+ * Navigation plugin interface to add navigation constrained by a navigation mesh
+ */
 export interface INavigationEnginePlugin {
     name: string;
-    createMavMesh(mesh: AbstractMesh): void;
+
+    /// Create a navigation mesh that will constrain a Crowd of agents
+    createMavMesh(mesh: AbstractMesh, parameters: NavMeshParameters): void;
+
+    /// Create and a to the scene a debug navmesh. Set the material to make it visible
     createDebugNavMesh(scene: Scene): Mesh;
+
+    /// Get a navigation mesh constrained position, closest to the parameter position
     getClosestPoint(position: Vector3): Vector3;
-    dispose(): void;
+
+    /// return a random position within a position centered disk 
+    getRandomPointAround(position: Vector3, maxRadius: number): Vector3;
+
+    ///
     isSupported(): boolean;
-    check(): void;
 
-    createCrowd(maxAgents: number, maxAgentRadius: number, scene: Scene) : ICrowd;
+    /// Create a new Crowd so you can add agents
+    createCrowd(maxAgents: number, maxAgentRadius: number, scene: Scene): ICrowd;
+
+    ///
+    dispose(): void;
 }
 
 /**
- * Interface used to define a navigation engine
+ * Crowd Interface. A Crowd is a collection of moving agents constrained by a navigation mesh
  */
-export interface INavigationEngine {
-    
-    /**
-     * Release all resources
-     */
-    dispose(): void;
+export interface ICrowd {
+    /// add a new agent to the crowd with the specified parameter a corresponding transformNode.
+    /// You can attach anything to that node. The node position is updated in the scene update tick.
+    addAgent(pos: Vector3, parameters: AgentParameters, transform: TransformNode): number;
 
-    /**
-     * Builds a navmesh from a mesh
-     */
-    createMavMesh(mesh: AbstractMesh): void;
-    createDebugNavMesh(scene: Scene): Mesh;
-    getClosestPoint(position: Vector3): Vector3;
-    check(): void;
+    ///
+    getAgentPosition(index: number): Vector3;
 
-    createCrowd(maxAgents: number, maxAgentRadius: number, scene: Scene) : ICrowd;
-}
+    ///
+    getAgentVelocity(index: number): Vector3;
 
-export interface ICrowd {
-    addAgent(pos: Vector3, transform:TransformNode): number;
-    getAgentPosition(index: number): Vector3
-	removeAgent(index:number): void;
+    /// remove a particular agent previously created
+    removeAgent(index: number): void;
+
+    /// get the list of all agents attached to this crowd
+    getAgents() : number[];
+
+    /// Tick update done by the Scene. Agent position/velocity/acceleration is updated by this function
     update(deltaTime: number): void;
+
+    /// Asks a particular agent to go to a destination. That destination is constrained by the navigation mesh
     agentGoto(index: number, destination: Vector3): void;
-}
+
+    /// dispose
+    dispose() : void;
+}
+
+export class AgentParameters {
+    /// Agent radius. [Limit: >= 0]
+    radius: number;
+
+    /// Agent height. [Limit: > 0]
+    height: number;
+
+    /// Maximum allowed acceleration. [Limit: >= 0]
+    maxAcceleration: number;
+
+    /// Maximum allowed speed. [Limit: >= 0]
+    maxSpeed: number;
+
+    /// Defines how close a collision element must be before it is considered for steering behaviors. [Limits: > 0]
+    collisionQueryRange: number;
+
+    /// The path visibility optimization range. [Limit: > 0]
+    pathOptimizationRange: number;
+
+    /// How aggresive the agent manager should be at avoiding collisions with this agent. [Limit: >= 0]
+    separationWeight: number;
+}
+
+export class NavMeshParameters {
+    /// The xz-plane cell size to use for fields. [Limit: > 0] [Units: wu] 
+	cs: number;
+
+	/// The y-axis cell size to use for fields. [Limit: > 0] [Units: wu]
+    ch: number;
+    
+    //constructor(): NavMeshParameters;
+    /// The maximum slope that is considered walkable. [Limits: 0 <= value < 90] [Units: Degrees] 
+    walkableSlopeAngle: number;
+
+    /// Minimum floor to 'ceiling' height that will still allow the floor area to 
+    /// be considered walkable. [Limit: >= 3] [Units: vx] 
+    walkableHeight: number;
+
+    /// Maximum ledge height that is considered to still be traversable. [Limit: >=0] [Units: vx] 
+    walkableClimb: number;
+
+    /// The distance to erode/shrink the walkable area of the heightfield away from 
+    /// obstructions.  [Limit: >=0] [Units: vx] 
+    walkableRadius: number;
+
+    /// The maximum allowed length for contour edges along the border of the mesh. [Limit: >=0] [Units: vx] 
+    maxEdgeLen: number;
+
+    /// The maximum distance a simplfied contour's border edges should deviate 
+    /// the original raw contour. [Limit: >=0] [Units: vx]
+    maxSimplificationError: number;
+
+    /// The minimum number of cells allowed to form isolated island areas. [Limit: >=0] [Units: vx] 
+    minRegionArea: number;
+
+    /// Any regions with a span count smaller than this value will, if possible, 
+    /// be merged with larger regions. [Limit: >=0] [Units: vx] 
+    mergeRegionArea: number;
+
+    /// The maximum number of vertices allowed for polygons generated during the 
+    /// contour to polygon conversion process. [Limit: >= 3] 
+    maxVertsPerPoly: number;
+
+    /// Sets the sampling distance to use when generating the detail mesh.
+    /// (For height detail only.) [Limits: 0 or >= 0.9] [Units: wu] 
+    detailSampleDist: number;
+
+    /// The maximum distance the detail mesh surface should deviate from heightfield
+    /// data. (For height detail only.) [Limit: >=0] [Units: wu] 
+    detailSampleMaxError: number;
+}

+ 0 - 61
src/Navigation/NavigationEngine.ts

@@ -1,61 +0,0 @@
-import { Vector3 } from "../Maths/math";
-import { INavigationEngine, INavigationEnginePlugin, ICrowd } from "./INavigationEngine";
-import { _DevTools } from '../Misc/devTools';
-import { AbstractMesh } from "../Meshes/abstractMesh";
-import { Mesh } from "../Meshes/mesh";
-import { Scene } from "../scene";
-
-/**
- * Class used to control navigation engine
- */
-export class NavigationEngine implements INavigationEngine {
-
-    /**
-     * Factory used to create the default navigation plugin.
-     * @returns The default navigation plugin
-     */
-    public static DefaultPluginFactory(): INavigationEnginePlugin {
-        throw _DevTools.WarnImport("RecastJSPlugin");
-    }
-
-    /**
-     * Creates a new Navigation Engine
-     * @param _navigationPlugin defines the plugin to use (RecastJS by default)
-     */
-    constructor(private _navigationPlugin: INavigationEnginePlugin = NavigationEngine.DefaultPluginFactory()) {
-        if (!this._navigationPlugin.isSupported()) {
-            throw new Error("Navigation Engine " + this._navigationPlugin.name + " cannot be found. "
-                + "Please make sure it is included.");
-        }
-    }
-
-    createMavMesh(mesh: AbstractMesh): void {
-        this._navigationPlugin.createMavMesh(mesh);
-    }
-
-    createDebugNavMesh(scene: Scene): Mesh {
-        return this._navigationPlugin.createDebugNavMesh(scene);
-    }
-
-    getClosestPoint(position: Vector3): Vector3
-    {
-        return this._navigationPlugin.getClosestPoint(position);
-    }
-
-    createCrowd(maxAgents: number, maxAgentRadius: number, scene: Scene) : ICrowd
-    {
-        var crowd = this._navigationPlugin.createCrowd(maxAgents, maxAgentRadius, scene);
-        return crowd;
-    }
-
-    /**
-     * Release all resources
-     */
-    public dispose(): void {
-        this._navigationPlugin.dispose();
-    }
-
-    public check(): void {
-        this._navigationPlugin.check();
-    }
-}

+ 127 - 20
src/Navigation/Plugins/recastJSPlugin.ts

@@ -1,4 +1,4 @@
-import { INavigationEnginePlugin, ICrowd } from "../../Navigation/INavigationEngine";
+import { INavigationEnginePlugin, ICrowd, AgentParameters, NavMeshParameters } from "../../Navigation/INavigationEngine";
 import { Logger } from "../../Misc/logger";
 import { VertexData } from "../../Meshes/mesh.vertexData";
 import { AbstractMesh } from "../../Meshes/abstractMesh";
@@ -19,27 +19,55 @@ export class RecastJSPlugin implements INavigationEnginePlugin {
     public bjsRECAST: any = {};
     public name: string = "RecastJSPlugin";
     public navMesh: any;
+
     /**
      * Initializes the recastJS plugin
      */
     public constructor(recastInjection: any = Recast) {
-        this.bjsRECAST = recastInjection();
+        if (typeof recastInjection === "function") {
+            recastInjection(this.bjsRECAST);
+        } else {
+            this.bjsRECAST = recastInjection();
+        }
 
         if (!this.isSupported()) {
             Logger.Error("RecastJS is not available. Please make sure you included the js file.");
             return;
         }
-        this.check();
     }
 
-    createMavMesh(mesh: AbstractMesh): void {
+    /**
+     * Creates a navigation mesh
+     * @param mesh of all the geometry used to compute the navigatio mesh
+     * @param parameters bunch of parameters used to filter geometry 
+     */
+    createMavMesh(mesh: AbstractMesh, parameters: NavMeshParameters): void {
         var rc = new this.bjsRECAST.rcConfig();
+        rc.cs = parameters.cs;
+        rc.ch = parameters.ch;
+        rc.walkableSlopeAngle = parameters.walkableSlopeAngle;
+        rc.walkableHeight = parameters.walkableHeight;
+        rc.walkableClimb = parameters.walkableClimb;
+        rc.walkableRadius = parameters.walkableRadius;
+        rc.maxEdgeLen = parameters.maxEdgeLen;
+        rc.maxSimplificationError = parameters.maxSimplificationError;
+        rc.minRegionArea = parameters.minRegionArea;
+        rc.mergeRegionArea = parameters.mergeRegionArea;
+        rc.maxVertsPerPoly = parameters.maxVertsPerPoly;
+        rc.detailSampleDist = parameters.detailSampleDist;
+        rc.detailSampleMaxError = parameters.detailSampleMaxError;
+
         this.navMesh = new this.bjsRECAST.NavMesh();
         var meshIndices = mesh.getIndices();
         var positions = mesh.getVerticesData('position');	
         this.navMesh.build(positions, mesh.getTotalVertices(), meshIndices, mesh.getTotalIndices(), rc);
     }
 
+    /**
+     * Create a navigation mesh debug mesh
+     * @param scene is where the mesh will be added
+     * @returns debug display mesh
+     */
     createDebugNavMesh(scene: Scene): Mesh {
         var tri: number;
         var pt: number;
@@ -70,6 +98,11 @@ export class RecastJSPlugin implements INavigationEnginePlugin {
         return mesh;
     }
 
+    /**
+     * Get a navigation mesh constrained position, closest to the parameter position
+     * @param position world position
+     * @returns the closest point to position constrained by the navigation mesh
+     */
     getClosestPoint(position: Vector3) : Vector3
     {
         var p = new this.bjsRECAST.Vec3(position.x, position.y, position.z);
@@ -78,9 +111,26 @@ export class RecastJSPlugin implements INavigationEnginePlugin {
         return pr;
     }
 
+    /**
+     * Get a navigation mesh constrained position, closest to the parameter position
+     * @param position world position
+     * @returns the closest point to position constrained by the navigation mesh
+     */
+    getRandomPointAround(position: Vector3, maxRadius: number): Vector3 {
+        var p = new this.bjsRECAST.Vec3(position.x, position.y, position.z);
+        var ret = this.navMesh.getRandomPointAround(p, maxRadius);
+        var pr = new Vector3(ret.x, ret.y, ret.z);
+        return pr;
+    }
+
+    /**
+     * Get a navigation mesh constrained position, closest to the parameter position
+     * @param position world position
+     * @returns the closest point to position constrained by the navigation mesh
+     */
     createCrowd(maxAgents: number, maxAgentRadius: number, scene: Scene) : ICrowd
     {
-        var crowd = new RecastJSCrowd(this, maxAgents, maxAgentRadius);
+        var crowd = new RecastJSCrowd(this, maxAgents, maxAgentRadius, scene);
         scene.addCrowd(crowd);
         return crowd;
     }
@@ -89,10 +139,7 @@ export class RecastJSPlugin implements INavigationEnginePlugin {
      * Disposes
      */
     public dispose() {
-        // Dispose of world
-    }
-
-    public check() {
+        
     }
 
     public isSupported(): boolean {
@@ -103,25 +150,36 @@ export class RecastJSPlugin implements INavigationEnginePlugin {
 export class RecastJSCrowd implements ICrowd {
     public bjsRECASTPlugin: RecastJSPlugin;
     public recastCrowd: any = {};
-    public transforms:TransformNode[];
-    public agents:number[];
-    public constructor(plugin: RecastJSPlugin, maxAgents: number, maxAgentRadius: number) {
+    public transforms: TransformNode[];
+    public agents: number[];
+    private scene: Scene;
+
+    public constructor(plugin: RecastJSPlugin, maxAgents: number, maxAgentRadius: number, scene: Scene) {
         this.bjsRECASTPlugin = plugin;
         this.recastCrowd = new this.bjsRECASTPlugin.bjsRECAST.Crowd(maxAgents, maxAgentRadius, this.bjsRECASTPlugin.navMesh.getNavMesh());
         this.transforms = new Array<TransformNode>();
         this.agents = new Array<number>();
+        this.scene = scene;
     }
 
-    addAgent(pos: Vector3, transform:TransformNode): number
+    /**
+     * Add a new agent to the crowd with the specified parameter a corresponding transformNode.
+     * You can attach anything to that node. The node position is updated in the scene update tick.
+     * @param pos world position that will be constrained by the navigation mesh
+     * @param parameters agent parameters
+     * @param transform hooked to the agent that will be update by the scene
+     * @returns agent index
+     */
+    addAgent(pos: Vector3, parameters: AgentParameters, transform:TransformNode): number
     {
         var agentParams = new this.bjsRECASTPlugin.bjsRECAST.dtCrowdAgentParams();
-        agentParams.radius = 0.1;
-        agentParams.height = 0.1;
-        agentParams.maxAcceleration = 1.0;
-        agentParams.maxSpeed = 1.0;
-        agentParams.collisionQueryRange = 1.0;
-        agentParams.pathOptimizationRange = 1.0;
-        agentParams.separationWeight = 1.0;
+        agentParams.radius = parameters.radius;
+        agentParams.height = parameters.height;
+        agentParams.maxAcceleration = parameters.maxAcceleration;
+        agentParams.maxSpeed = parameters.maxSpeed;
+        agentParams.collisionQueryRange = parameters.collisionQueryRange;
+        agentParams.pathOptimizationRange = parameters.pathOptimizationRange;
+        agentParams.separationWeight = parameters.separationWeight;
         agentParams.updateFlags = 7;
         agentParams.obstacleAvoidanceType = 0;
         agentParams.queryFilterType = 0;
@@ -133,19 +191,62 @@ export class RecastJSCrowd implements ICrowd {
         return agentIndex;
     }
 
+    /**
+     * 
+     * @param index agent index returned by addAgent
+     * @returns world space position
+     */
     getAgentPosition(index: number): Vector3 {
         var agentPos = this.recastCrowd.getAgentPosition(index);
         return new Vector3(agentPos.x, agentPos.y, agentPos.z);
     }
 
+    /**
+     * 
+     * @param index agent index returned by addAgent
+     * @returns world space velocity
+     */
+    getAgentVelocity(index: number): Vector3 {
+        var agentVel = this.recastCrowd.getAgentVelocity(index);
+        return new Vector3(agentVel.x, agentVel.y, agentVel.z);
+    }
+
+    /**
+     * Asks a particular agent to go to a destination. That destination is constrained by the navigation mesh
+     * @param index agent index returned by addAgent
+     * @param destination targeted world position
+     * @returns the closest point to position constrained by the navigation mesh
+     */
     agentGoto(index: number, destination: Vector3): void {
         this.recastCrowd.agentGoto(index, new this.bjsRECASTPlugin.bjsRECAST.Vec3(destination.x, destination.y, destination.z));
     }
 
+    /**
+     * remove a particular agent previously created
+     * @param index agent index returned by addAgent
+     */
     removeAgent(index: number): void {
         this.recastCrowd.removeAgent(index);
+
+        var item = this.agents.indexOf(index);
+        if (item > -1) {
+            this.agents.splice(item, 1);
+            this.transforms.splice(item, 1);
+        }
+    }
+
+    /**
+     * get the list of all agents attached to this crowd
+     * @returns list of agent indices
+     */
+    getAgents(): number[] {
+        return this.agents;
     }
 
+    /**
+     * Tick update done by the Scene. Agent position/velocity/acceleration is updated by this function
+     * @param deltaTime in seconds
+     */
     update(deltaTime: number): void {
         // update crowd
         this.recastCrowd.update(deltaTime);
@@ -157,4 +258,10 @@ export class RecastJSCrowd implements ICrowd {
             this.transforms[index].position = this.getAgentPosition(this.agents[index]);
         }
     }
+
+    dispose() : void
+    {
+        this.recastCrowd.destroy();
+        this.scene.removeCrowd(this);
+    }
 }

+ 0 - 1
src/Navigation/index.ts

@@ -1,3 +1,2 @@
 export * from "./INavigationEngine";
-export * from "./NavigationEngine";
 export * from "./Plugins/index";

+ 22 - 4
src/scene.ts

@@ -165,11 +165,27 @@ export class Scene extends AbstractScene implements IAnimatable {
      */
     public ambientColor = new Color3(0, 0, 0);
 
-    public crowd: Nullable<ICrowd> = null;
+    public crowds: ICrowd[];
 
+    /**
+     * Adds an agent crowd to the scene
+     * Once added, every agent will have its position updated based on crowd update
+     */
     addCrowd(crowd:ICrowd): void {
-        this.crowd = crowd;
+        this.crowds.push(crowd);
     }
+
+    /**
+     * Removes a crowd from the scene
+     * Once removed, there will be no more updats to the crowd agents
+     */
+    removeCrowd(crowd:ICrowd): void {
+        var item = this.crowds.indexOf(crowd);
+        if (item > -1) {
+            this.crowds.splice(item, 1);
+        }
+    }
+
     /**
      * This is use to store the default BRDF lookup for PBR materials in your scene.
      * It should only be one of the following (if not the default embedded one):
@@ -1351,6 +1367,8 @@ export class Scene extends AbstractScene implements IAnimatable {
         if (!options || !options.virtual) {
             this._engine.onNewSceneAddedObservable.notifyObservers(this);
         }
+
+        this.crowds = new Array<ICrowd>();
     }
 
     /**
@@ -3715,8 +3733,8 @@ export class Scene extends AbstractScene implements IAnimatable {
         }
 
         // Navigation
-        if (this.crowd) {
-            this.crowd.update(0.016);
+        for (let crowd of this.crowds) {
+            crowd.update(this._engine.getDeltaTime() * 0.001);
         }
 
         // Before camera update steps