123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804 |
- import { Logger } from "../../Misc/logger.js";
- import { VertexData } from "../../Meshes/mesh.vertexData.js";
- import { Mesh } from "../../Meshes/mesh.js";
- import { Epsilon, Vector3, Matrix } from "../../Maths/math.js";
- import { Observable } from "../../Misc/observable.js";
- import { VertexBuffer } from "../../Buffers/buffer.js";
- /**
- * RecastJS navigation plugin
- */
- export class RecastJSPlugin {
- /**
- * Initializes the recastJS plugin
- * @param recastInjection can be used to inject your own recast reference
- */
- constructor(recastInjection = Recast) {
- /**
- * Reference to the Recast library
- */
- this.bjsRECAST = {};
- /**
- * plugin name
- */
- this.name = "RecastJSPlugin";
- this._maximumSubStepCount = 10;
- this._timeStep = 1 / 60;
- this._timeFactor = 1;
- this._worker = null;
- if (typeof recastInjection === "function") {
- Logger.Error("RecastJS is not ready. Please make sure you await Recast() before using the plugin.");
- }
- else {
- this.bjsRECAST = recastInjection;
- }
- if (!this.isSupported()) {
- Logger.Error("RecastJS is not available. Please make sure you included the js file.");
- return;
- }
- this.setTimeStep();
- this._tempVec1 = new this.bjsRECAST.Vec3();
- this._tempVec2 = new this.bjsRECAST.Vec3();
- }
- /**
- * Set worker URL to be used when generating a new navmesh
- * @param workerURL url string
- * @returns boolean indicating if worker is created
- */
- setWorkerURL(workerURL) {
- if (window && window.Worker) {
- this._worker = new Worker(workerURL);
- return true;
- }
- return false;
- }
- /**
- * Set the time step of the navigation tick update.
- * Default is 1/60.
- * A value of 0 will disable fixed time update
- * @param newTimeStep the new timestep to apply to this world.
- */
- setTimeStep(newTimeStep = 1 / 60) {
- this._timeStep = newTimeStep;
- }
- /**
- * Get the time step of the navigation tick update.
- * @returns the current time step
- */
- getTimeStep() {
- return this._timeStep;
- }
- /**
- * If delta time in navigation tick update is greater than the time step
- * a number of sub iterations are done. If more iterations are need to reach deltatime
- * they will be discarded.
- * A value of 0 will set to no maximum and update will use as many substeps as needed
- * @param newStepCount the maximum number of iterations
- */
- setMaximumSubStepCount(newStepCount = 10) {
- this._maximumSubStepCount = newStepCount;
- }
- /**
- * Get the maximum number of iterations per navigation tick update
- * @returns the maximum number of iterations
- */
- getMaximumSubStepCount() {
- return this._maximumSubStepCount;
- }
- /**
- * Time factor applied when updating crowd agents (default 1). A value of 0 will pause crowd updates.
- * @param value the time factor applied at update
- */
- set timeFactor(value) {
- this._timeFactor = Math.max(value, 0);
- }
- /**
- * Get the time factor used for crowd agent update
- * @returns the time factor
- */
- get timeFactor() {
- return this._timeFactor;
- }
- /**
- * Creates a navigation mesh
- * @param meshes array of all the geometry used to compute the navigation mesh
- * @param parameters bunch of parameters used to filter geometry
- * @param completion callback when data is available from the worker. Not used without a worker
- */
- createNavMesh(meshes, parameters, completion) {
- if (this._worker && !completion) {
- Logger.Warn("A worker is avaible but no completion callback. Defaulting to blocking navmesh creation");
- }
- else if (!this._worker && completion) {
- Logger.Warn("A completion callback is avaible but no worker. Defaulting to blocking navmesh creation");
- }
- this.navMesh = new this.bjsRECAST.NavMesh();
- let index;
- let tri;
- let pt;
- const indices = [];
- const positions = [];
- let offset = 0;
- for (index = 0; index < meshes.length; index++) {
- if (meshes[index]) {
- const mesh = meshes[index];
- const meshIndices = mesh.getIndices();
- if (!meshIndices) {
- continue;
- }
- const meshPositions = mesh.getVerticesData(VertexBuffer.PositionKind, false, false);
- if (!meshPositions) {
- continue;
- }
- const worldMatrices = [];
- const worldMatrix = mesh.computeWorldMatrix(true);
- if (mesh.hasThinInstances) {
- const thinMatrices = mesh.thinInstanceGetWorldMatrices();
- for (let instanceIndex = 0; instanceIndex < thinMatrices.length; instanceIndex++) {
- const tmpMatrix = new Matrix();
- const thinMatrix = thinMatrices[instanceIndex];
- thinMatrix.multiplyToRef(worldMatrix, tmpMatrix);
- worldMatrices.push(tmpMatrix);
- }
- }
- else {
- worldMatrices.push(worldMatrix);
- }
- for (let matrixIndex = 0; matrixIndex < worldMatrices.length; matrixIndex++) {
- const wm = worldMatrices[matrixIndex];
- for (tri = 0; tri < meshIndices.length; tri++) {
- indices.push(meshIndices[tri] + offset);
- }
- const transformed = Vector3.Zero();
- const position = Vector3.Zero();
- for (pt = 0; pt < meshPositions.length; pt += 3) {
- Vector3.FromArrayToRef(meshPositions, pt, position);
- Vector3.TransformCoordinatesToRef(position, wm, transformed);
- positions.push(transformed.x, transformed.y, transformed.z);
- }
- offset += meshPositions.length / 3;
- }
- }
- }
- if (this._worker && completion) {
- // spawn worker and send message
- this._worker.postMessage([positions, offset, indices, indices.length, parameters]);
- this._worker.onmessage = function (e) {
- completion(e.data);
- };
- }
- else {
- // blocking calls
- const rc = new this.bjsRECAST.rcConfig();
- rc.cs = parameters.cs;
- rc.ch = parameters.ch;
- rc.borderSize = parameters.borderSize ? parameters.borderSize : 0;
- rc.tileSize = parameters.tileSize ? parameters.tileSize : 0;
- 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.build(positions, offset, indices, indices.length, rc);
- }
- }
- /**
- * Create a navigation mesh debug mesh
- * @param scene is where the mesh will be added
- * @returns debug display mesh
- */
- createDebugNavMesh(scene) {
- let tri;
- let pt;
- const debugNavMesh = this.navMesh.getDebugNavMesh();
- const triangleCount = debugNavMesh.getTriangleCount();
- const indices = [];
- const positions = [];
- for (tri = 0; tri < triangleCount * 3; tri++) {
- indices.push(tri);
- }
- for (tri = 0; tri < triangleCount; tri++) {
- for (pt = 0; pt < 3; pt++) {
- const point = debugNavMesh.getTriangle(tri).getPoint(pt);
- positions.push(point.x, point.y, point.z);
- }
- }
- const mesh = new Mesh("NavMeshDebug", scene);
- const vertexData = new VertexData();
- vertexData.indices = indices;
- vertexData.positions = positions;
- vertexData.applyToMesh(mesh, false);
- 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) {
- this._tempVec1.x = position.x;
- this._tempVec1.y = position.y;
- this._tempVec1.z = position.z;
- const ret = this.navMesh.getClosestPoint(this._tempVec1);
- const 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
- * @param result output the closest point to position constrained by the navigation mesh
- */
- getClosestPointToRef(position, result) {
- this._tempVec1.x = position.x;
- this._tempVec1.y = position.y;
- this._tempVec1.z = position.z;
- const ret = this.navMesh.getClosestPoint(this._tempVec1);
- result.set(ret.x, ret.y, ret.z);
- }
- /**
- * Get a navigation mesh constrained position, within a particular radius
- * @param position world position
- * @param maxRadius the maximum distance to the constrained world position
- * @returns the closest point to position constrained by the navigation mesh
- */
- getRandomPointAround(position, maxRadius) {
- this._tempVec1.x = position.x;
- this._tempVec1.y = position.y;
- this._tempVec1.z = position.z;
- const ret = this.navMesh.getRandomPointAround(this._tempVec1, maxRadius);
- const pr = new Vector3(ret.x, ret.y, ret.z);
- return pr;
- }
- /**
- * Get a navigation mesh constrained position, within a particular radius
- * @param position world position
- * @param maxRadius the maximum distance to the constrained world position
- * @param result output the closest point to position constrained by the navigation mesh
- */
- getRandomPointAroundToRef(position, maxRadius, result) {
- this._tempVec1.x = position.x;
- this._tempVec1.y = position.y;
- this._tempVec1.z = position.z;
- const ret = this.navMesh.getRandomPointAround(this._tempVec1, maxRadius);
- result.set(ret.x, ret.y, ret.z);
- }
- /**
- * Compute the final position from a segment made of destination-position
- * @param position world position
- * @param destination world position
- * @returns the resulting point along the navmesh
- */
- moveAlong(position, destination) {
- this._tempVec1.x = position.x;
- this._tempVec1.y = position.y;
- this._tempVec1.z = position.z;
- this._tempVec2.x = destination.x;
- this._tempVec2.y = destination.y;
- this._tempVec2.z = destination.z;
- const ret = this.navMesh.moveAlong(this._tempVec1, this._tempVec2);
- const pr = new Vector3(ret.x, ret.y, ret.z);
- return pr;
- }
- /**
- * Compute the final position from a segment made of destination-position
- * @param position world position
- * @param destination world position
- * @param result output the resulting point along the navmesh
- */
- moveAlongToRef(position, destination, result) {
- this._tempVec1.x = position.x;
- this._tempVec1.y = position.y;
- this._tempVec1.z = position.z;
- this._tempVec2.x = destination.x;
- this._tempVec2.y = destination.y;
- this._tempVec2.z = destination.z;
- const ret = this.navMesh.moveAlong(this._tempVec1, this._tempVec2);
- result.set(ret.x, ret.y, ret.z);
- }
- _convertNavPathPoints(navPath) {
- let pt;
- const pointCount = navPath.getPointCount();
- const positions = [];
- for (pt = 0; pt < pointCount; pt++) {
- const p = navPath.getPoint(pt);
- positions.push(new Vector3(p.x, p.y, p.z));
- }
- return positions;
- }
- /**
- * Compute a navigation path from start to end. Returns an empty array if no path can be computed
- * Path is straight.
- * @param start world position
- * @param end world position
- * @returns array containing world position composing the path
- */
- computePath(start, end) {
- this._tempVec1.x = start.x;
- this._tempVec1.y = start.y;
- this._tempVec1.z = start.z;
- this._tempVec2.x = end.x;
- this._tempVec2.y = end.y;
- this._tempVec2.z = end.z;
- const navPath = this.navMesh.computePath(this._tempVec1, this._tempVec2);
- return this._convertNavPathPoints(navPath);
- }
- /**
- * Compute a navigation path from start to end. Returns an empty array if no path can be computed.
- * Path follows navigation mesh geometry.
- * @param start world position
- * @param end world position
- * @returns array containing world position composing the path
- */
- computePathSmooth(start, end) {
- this._tempVec1.x = start.x;
- this._tempVec1.y = start.y;
- this._tempVec1.z = start.z;
- this._tempVec2.x = end.x;
- this._tempVec2.y = end.y;
- this._tempVec2.z = end.z;
- const navPath = this.navMesh.computePathSmooth(this._tempVec1, this._tempVec2);
- return this._convertNavPathPoints(navPath);
- }
- /**
- * Create a new Crowd so you can add agents
- * @param maxAgents the maximum agent count in the crowd
- * @param maxAgentRadius the maximum radius an agent can have
- * @param scene to attach the crowd to
- * @returns the crowd you can add agents to
- */
- createCrowd(maxAgents, maxAgentRadius, scene) {
- const crowd = new RecastJSCrowd(this, maxAgents, maxAgentRadius, scene);
- return crowd;
- }
- /**
- * Set the Bounding box extent for doing spatial queries (getClosestPoint, getRandomPointAround, ...)
- * The queries will try to find a solution within those bounds
- * default is (1,1,1)
- * @param extent x,y,z value that define the extent around the queries point of reference
- */
- setDefaultQueryExtent(extent) {
- this._tempVec1.x = extent.x;
- this._tempVec1.y = extent.y;
- this._tempVec1.z = extent.z;
- this.navMesh.setDefaultQueryExtent(this._tempVec1);
- }
- /**
- * Get the Bounding box extent specified by setDefaultQueryExtent
- * @returns the box extent values
- */
- getDefaultQueryExtent() {
- const p = this.navMesh.getDefaultQueryExtent();
- return new Vector3(p.x, p.y, p.z);
- }
- /**
- * build the navmesh from a previously saved state using getNavmeshData
- * @param data the Uint8Array returned by getNavmeshData
- */
- buildFromNavmeshData(data) {
- const nDataBytes = data.length * data.BYTES_PER_ELEMENT;
- const dataPtr = this.bjsRECAST._malloc(nDataBytes);
- const dataHeap = new Uint8Array(this.bjsRECAST.HEAPU8.buffer, dataPtr, nDataBytes);
- dataHeap.set(data);
- const buf = new this.bjsRECAST.NavmeshData();
- buf.dataPointer = dataHeap.byteOffset;
- buf.size = data.length;
- this.navMesh = new this.bjsRECAST.NavMesh();
- this.navMesh.buildFromNavmeshData(buf);
- // Free memory
- this.bjsRECAST._free(dataHeap.byteOffset);
- }
- /**
- * returns the navmesh data that can be used later. The navmesh must be built before retrieving the data
- * @returns data the Uint8Array that can be saved and reused
- */
- getNavmeshData() {
- const navmeshData = this.navMesh.getNavmeshData();
- const arrView = new Uint8Array(this.bjsRECAST.HEAPU8.buffer, navmeshData.dataPointer, navmeshData.size);
- const ret = new Uint8Array(navmeshData.size);
- ret.set(arrView);
- this.navMesh.freeNavmeshData(navmeshData);
- return ret;
- }
- /**
- * Get the Bounding box extent result specified by setDefaultQueryExtent
- * @param result output the box extent values
- */
- getDefaultQueryExtentToRef(result) {
- const p = this.navMesh.getDefaultQueryExtent();
- result.set(p.x, p.y, p.z);
- }
- /**
- * Disposes
- */
- dispose() { }
- /**
- * Creates a cylinder obstacle and add it to the navigation
- * @param position world position
- * @param radius cylinder radius
- * @param height cylinder height
- * @returns the obstacle freshly created
- */
- addCylinderObstacle(position, radius, height) {
- this._tempVec1.x = position.x;
- this._tempVec1.y = position.y;
- this._tempVec1.z = position.z;
- return this.navMesh.addCylinderObstacle(this._tempVec1, radius, height);
- }
- /**
- * Creates an oriented box obstacle and add it to the navigation
- * @param position world position
- * @param extent box size
- * @param angle angle in radians of the box orientation on Y axis
- * @returns the obstacle freshly created
- */
- addBoxObstacle(position, extent, angle) {
- this._tempVec1.x = position.x;
- this._tempVec1.y = position.y;
- this._tempVec1.z = position.z;
- this._tempVec2.x = extent.x;
- this._tempVec2.y = extent.y;
- this._tempVec2.z = extent.z;
- return this.navMesh.addBoxObstacle(this._tempVec1, this._tempVec2, angle);
- }
- /**
- * Removes an obstacle created by addCylinderObstacle or addBoxObstacle
- * @param obstacle obstacle to remove from the navigation
- */
- removeObstacle(obstacle) {
- this.navMesh.removeObstacle(obstacle);
- }
- /**
- * If this plugin is supported
- * @returns true if plugin is supported
- */
- isSupported() {
- return this.bjsRECAST !== undefined;
- }
- /**
- * Returns the seed used for randomized functions like `getRandomPointAround`
- * @returns seed number
- */
- getRandomSeed() {
- return this.bjsRECAST._getRandomSeed();
- }
- /**
- * Set the seed used for randomized functions like `getRandomPointAround`
- * @param seed number used as seed for random functions
- */
- setRandomSeed(seed) {
- this.bjsRECAST._setRandomSeed(seed);
- }
- }
- /**
- * Recast detour crowd implementation
- */
- export class RecastJSCrowd {
- /**
- * Constructor
- * @param plugin recastJS plugin
- * @param maxAgents the maximum agent count in the crowd
- * @param maxAgentRadius the maximum radius an agent can have
- * @param scene to attach the crowd to
- * @returns the crowd you can add agents to
- */
- constructor(plugin, maxAgents, maxAgentRadius, scene) {
- /**
- * Link to the detour crowd
- */
- this.recastCrowd = {};
- /**
- * One transform per agent
- */
- this.transforms = new Array();
- /**
- * All agents created
- */
- this.agents = new Array();
- /**
- * agents reach radius
- */
- this.reachRadii = new Array();
- /**
- * true when a destination is active for an agent and notifier hasn't been notified of reach
- */
- this._agentDestinationArmed = new Array();
- /**
- * agent current target
- */
- this._agentDestination = new Array();
- /**
- * Observer for crowd updates
- */
- this._onBeforeAnimationsObserver = null;
- /**
- * Fires each time an agent is in reach radius of its destination
- */
- this.onReachTargetObservable = new Observable();
- this.bjsRECASTPlugin = plugin;
- this.recastCrowd = new this.bjsRECASTPlugin.bjsRECAST.Crowd(maxAgents, maxAgentRadius, this.bjsRECASTPlugin.navMesh.getNavMesh());
- this._scene = scene;
- this._onBeforeAnimationsObserver = scene.onBeforeAnimationsObservable.add(() => {
- this.update(scene.getEngine().getDeltaTime() * 0.001 * plugin.timeFactor);
- });
- }
- /**
- * 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, parameters, transform) {
- const agentParams = new this.bjsRECASTPlugin.bjsRECAST.dtCrowdAgentParams();
- 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;
- agentParams.userData = 0;
- const agentIndex = this.recastCrowd.addAgent(new this.bjsRECASTPlugin.bjsRECAST.Vec3(pos.x, pos.y, pos.z), agentParams);
- this.transforms.push(transform);
- this.agents.push(agentIndex);
- this.reachRadii.push(parameters.reachRadius ? parameters.reachRadius : parameters.radius);
- this._agentDestinationArmed.push(false);
- this._agentDestination.push(new Vector3(0, 0, 0));
- return agentIndex;
- }
- /**
- * Returns the agent position in world space
- * @param index agent index returned by addAgent
- * @returns world space position
- */
- getAgentPosition(index) {
- const agentPos = this.recastCrowd.getAgentPosition(index);
- return new Vector3(agentPos.x, agentPos.y, agentPos.z);
- }
- /**
- * Returns the agent position result in world space
- * @param index agent index returned by addAgent
- * @param result output world space position
- */
- getAgentPositionToRef(index, result) {
- const agentPos = this.recastCrowd.getAgentPosition(index);
- result.set(agentPos.x, agentPos.y, agentPos.z);
- }
- /**
- * Returns the agent velocity in world space
- * @param index agent index returned by addAgent
- * @returns world space velocity
- */
- getAgentVelocity(index) {
- const agentVel = this.recastCrowd.getAgentVelocity(index);
- return new Vector3(agentVel.x, agentVel.y, agentVel.z);
- }
- /**
- * Returns the agent velocity result in world space
- * @param index agent index returned by addAgent
- * @param result output world space velocity
- */
- getAgentVelocityToRef(index, result) {
- const agentVel = this.recastCrowd.getAgentVelocity(index);
- result.set(agentVel.x, agentVel.y, agentVel.z);
- }
- /**
- * Returns the agent next target point on the path
- * @param index agent index returned by addAgent
- * @returns world space position
- */
- getAgentNextTargetPath(index) {
- const pathTargetPos = this.recastCrowd.getAgentNextTargetPath(index);
- return new Vector3(pathTargetPos.x, pathTargetPos.y, pathTargetPos.z);
- }
- /**
- * Returns the agent next target point on the path
- * @param index agent index returned by addAgent
- * @param result output world space position
- */
- getAgentNextTargetPathToRef(index, result) {
- const pathTargetPos = this.recastCrowd.getAgentNextTargetPath(index);
- result.set(pathTargetPos.x, pathTargetPos.y, pathTargetPos.z);
- }
- /**
- * Gets the agent state
- * @param index agent index returned by addAgent
- * @returns agent state
- */
- getAgentState(index) {
- return this.recastCrowd.getAgentState(index);
- }
- /**
- * returns true if the agent in over an off mesh link connection
- * @param index agent index returned by addAgent
- * @returns true if over an off mesh link connection
- */
- overOffmeshConnection(index) {
- return this.recastCrowd.overOffmeshConnection(index);
- }
- /**
- * 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
- */
- agentGoto(index, destination) {
- this.recastCrowd.agentGoto(index, new this.bjsRECASTPlugin.bjsRECAST.Vec3(destination.x, destination.y, destination.z));
- // arm observer
- const item = this.agents.indexOf(index);
- if (item > -1) {
- this._agentDestinationArmed[item] = true;
- this._agentDestination[item].set(destination.x, destination.y, destination.z);
- }
- }
- /**
- * Teleport the agent to a new position
- * @param index agent index returned by addAgent
- * @param destination targeted world position
- */
- agentTeleport(index, destination) {
- this.recastCrowd.agentTeleport(index, new this.bjsRECASTPlugin.bjsRECAST.Vec3(destination.x, destination.y, destination.z));
- }
- /**
- * Update agent parameters
- * @param index agent index returned by addAgent
- * @param parameters agent parameters
- */
- updateAgentParameters(index, parameters) {
- const agentParams = this.recastCrowd.getAgentParameters(index);
- if (parameters.radius !== undefined) {
- agentParams.radius = parameters.radius;
- }
- if (parameters.height !== undefined) {
- agentParams.height = parameters.height;
- }
- if (parameters.maxAcceleration !== undefined) {
- agentParams.maxAcceleration = parameters.maxAcceleration;
- }
- if (parameters.maxSpeed !== undefined) {
- agentParams.maxSpeed = parameters.maxSpeed;
- }
- if (parameters.collisionQueryRange !== undefined) {
- agentParams.collisionQueryRange = parameters.collisionQueryRange;
- }
- if (parameters.pathOptimizationRange !== undefined) {
- agentParams.pathOptimizationRange = parameters.pathOptimizationRange;
- }
- if (parameters.separationWeight !== undefined) {
- agentParams.separationWeight = parameters.separationWeight;
- }
- this.recastCrowd.setAgentParameters(index, agentParams);
- }
- /**
- * remove a particular agent previously created
- * @param index agent index returned by addAgent
- */
- removeAgent(index) {
- this.recastCrowd.removeAgent(index);
- const item = this.agents.indexOf(index);
- if (item > -1) {
- this.agents.splice(item, 1);
- this.transforms.splice(item, 1);
- this.reachRadii.splice(item, 1);
- this._agentDestinationArmed.splice(item, 1);
- this._agentDestination.splice(item, 1);
- }
- }
- /**
- * get the list of all agents attached to this crowd
- * @returns list of agent indices
- */
- getAgents() {
- return this.agents;
- }
- /**
- * Tick update done by the Scene. Agent position/velocity/acceleration is updated by this function
- * @param deltaTime in seconds
- */
- update(deltaTime) {
- // update obstacles
- this.bjsRECASTPlugin.navMesh.update();
- if (deltaTime <= Epsilon) {
- return;
- }
- // update crowd
- const timeStep = this.bjsRECASTPlugin.getTimeStep();
- const maxStepCount = this.bjsRECASTPlugin.getMaximumSubStepCount();
- if (timeStep <= Epsilon) {
- this.recastCrowd.update(deltaTime);
- }
- else {
- let iterationCount = Math.floor(deltaTime / timeStep);
- if (maxStepCount && iterationCount > maxStepCount) {
- iterationCount = maxStepCount;
- }
- if (iterationCount < 1) {
- iterationCount = 1;
- }
- const step = deltaTime / iterationCount;
- for (let i = 0; i < iterationCount; i++) {
- this.recastCrowd.update(step);
- }
- }
- // update transforms
- for (let index = 0; index < this.agents.length; index++) {
- // update transform position
- const agentIndex = this.agents[index];
- const agentPosition = this.getAgentPosition(agentIndex);
- this.transforms[index].position = agentPosition;
- // check agent reach destination
- if (this._agentDestinationArmed[index]) {
- const dx = agentPosition.x - this._agentDestination[index].x;
- const dz = agentPosition.z - this._agentDestination[index].z;
- const radius = this.reachRadii[index];
- const groundY = this._agentDestination[index].y - this.reachRadii[index];
- const ceilingY = this._agentDestination[index].y + this.reachRadii[index];
- const distanceXZSquared = dx * dx + dz * dz;
- if (agentPosition.y > groundY && agentPosition.y < ceilingY && distanceXZSquared < radius * radius) {
- this._agentDestinationArmed[index] = false;
- this.onReachTargetObservable.notifyObservers({ agentIndex: agentIndex, destination: this._agentDestination[index] });
- }
- }
- }
- }
- /**
- * Set the Bounding box extent for doing spatial queries (getClosestPoint, getRandomPointAround, ...)
- * The queries will try to find a solution within those bounds
- * default is (1,1,1)
- * @param extent x,y,z value that define the extent around the queries point of reference
- */
- setDefaultQueryExtent(extent) {
- const ext = new this.bjsRECASTPlugin.bjsRECAST.Vec3(extent.x, extent.y, extent.z);
- this.recastCrowd.setDefaultQueryExtent(ext);
- }
- /**
- * Get the Bounding box extent specified by setDefaultQueryExtent
- * @returns the box extent values
- */
- getDefaultQueryExtent() {
- const p = this.recastCrowd.getDefaultQueryExtent();
- return new Vector3(p.x, p.y, p.z);
- }
- /**
- * Get the Bounding box extent result specified by setDefaultQueryExtent
- * @param result output the box extent values
- */
- getDefaultQueryExtentToRef(result) {
- const p = this.recastCrowd.getDefaultQueryExtent();
- result.set(p.x, p.y, p.z);
- }
- /**
- * Get the next corner points composing the path (max 4 points)
- * @param index agent index returned by addAgent
- * @returns array containing world position composing the path
- */
- getCorners(index) {
- let pt;
- const navPath = this.recastCrowd.getCorners(index);
- const pointCount = navPath.getPointCount();
- const positions = [];
- for (pt = 0; pt < pointCount; pt++) {
- const p = navPath.getPoint(pt);
- positions.push(new Vector3(p.x, p.y, p.z));
- }
- return positions;
- }
- /**
- * Release all resources
- */
- dispose() {
- this.recastCrowd.destroy();
- this._scene.onBeforeAnimationsObservable.remove(this._onBeforeAnimationsObserver);
- this._onBeforeAnimationsObserver = null;
- this.onReachTargetObservable.clear();
- }
- }
- //# sourceMappingURL=recastJSPlugin.js.map
|