Added recastnavigation support

This commit is contained in:
2026-02-06 18:07:42 +03:00
parent 0405214388
commit d4bac06d94
42 changed files with 12227 additions and 158 deletions

View File

@@ -0,0 +1,108 @@
/*
OgreCrowd
---------
Copyright (c) 2012 Jonas Hauquier
Additional contributions by:
- mkultra333
- Paul Wilson
Sincere thanks and to:
- Mikko Mononen (developer of Recast navigation libraries)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#ifndef ANIMATEABLECHARACTER_H
#define ANIMATEABLECHARACTER_H
#include "Character.h"
/**
* More complex character that shows an animated walking human to represent an agent.
* This is probably closer to what you would use in a real scenario. This class can just
* be replaced with your character class if you already have one and it mainly serves
* as an example of how characters can be integrated with detour agents.
**/
class AnimateableCharacter : public Character
{
public:
/**
* Create a new human character with specified name, the entities will be placed in the specified scene manager.
* detourCrowd is the crowd manager in which an agent for this character will be created (make sure you don't create
* more characters than MAX_AGENTS).
* Set debugDraw to true to initially draw debug geometry (can be disabled afterwards too).
* Position defines initial position the character has to be placed on (should be a valid position on the navmesh).
**/
AnimateableCharacter(Ogre::String name, Ogre::SceneManager* sceneMgr, OgreDetourCrowd* detourCrowd, bool debugDraw = false, Ogre::Vector3 position = Ogre::Vector3::ZERO);
/**
* The entity that represents this character in the scene
**/
virtual Ogre::Entity* getEntity(void);
/**
* Update one tick in the render loop. Advances animation and character position.
* In order for the agents to be updated, you first need to call the detourCrowd
* update function.
**/
virtual void update(Ogre::Real timeSinceLastFrame);
/**
* @see{Character::setDebugVisibility(bool)}
**/
virtual void setDebugVisibility(bool visible);
virtual bool getDebugVisibility(void);
virtual void show(void);
void randomizeAnimationPosition(void);
protected:
bool mDebugDraw;
/**
* Main entity that represents this character.
**/
Ogre::Entity *mEnt;
/**
* Currently active animation state.
**/
Ogre::AnimationState* mAnimState;
/**
* Speed scaling factor of the animation playback.
**/
Ogre::Real mAnimSpeedScale;
/**
* Scenenode that stores all geometry related to
* recast debug drawing. Can be made visible with
* setDebugVisibility().
**/
Ogre::SceneNode *mDebugNode;
};
#endif // ANIMATEABLECHARACTER_H

View File

@@ -0,0 +1,365 @@
/*
OgreCrowd
---------
Copyright (c) 2012 Jonas Hauquier
Additional contributions by:
- mkultra333
- Paul Wilson
Sincere thanks and to:
- Mikko Mononen (developer of Recast navigation libraries)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#ifndef CHARACTER_H
#define CHARACTER_H
#include <Ogre.h>
#include "OgreDetourCrowd.h"
#include "DetourCrowd.h"
#include "OgreDetourTileCache.h"
/**
* An animeatable character that acts as crowd agent
**/
class Character
{
public:
enum QueryFlags {
DEFAULT_MASK = 1u<<0,
NAVMESH_MASK = 1u<<1,
OBSTACLE_MASK= 1u<<2
};
/**
* Create a character with specified name, for which entities will be drawn on the specified scene manager.
* detourCrowd specifies the detour crowd manager on which an agent for this character will be created
* (make sure you don't create more characters than MAX_AGENTS).
* Position defines initial position the character has to be placed on (should be a valid position on the navmesh).
**/
Character(Ogre::String name, Ogre::SceneManager* sceneMgr, OgreDetourCrowd* detourCrowd, Ogre::Vector3 position = Ogre::Vector3::ZERO);
~Character();
/**
* The delta offset an agent must be from its destination before considering the destination reached.
* Set this as the squared value of the actual delta value (squared distance is calculated for perfocmance).
**/
static const Ogre::Real DESTINATION_RADIUS;
/**
* The scenenode this character is attached to
**/
virtual Ogre::SceneNode* getNode(void) const;
/**
* The height of the agent for this character.
**/
virtual Ogre::Real getAgentHeight(void) const;
/**
* The radius of the agent for this character.
**/
virtual Ogre::Real getAgentRadius(void) const;
/**
* Update this character for drawing a new frame.
* Updates one tick in the render loop.
* In order for the agents to be updated, you first need to call the detourCrowd
* update function.
* What is updated specifically is up to a specific implementation of Character,
* but at the very least the position in the scene should be updated to reflect
* the detour agent position (possibly with additional physics engine clipping
* and collision testing).
**/
virtual void update(Ogre::Real timeSinceLastFrame) = 0;
/**
* Update the destination for this agent.
* If updatePreviousPath is set to true the previous path will be reused instead
* of calculating a completely new path, but this can only be used if the new
* destination is close to the previous (eg. when chasing a moving entity).
**/
virtual void updateDestination(Ogre::Vector3 destination, bool updatePreviousPath= false);
/**
* The destination set for this character.
**/
virtual Ogre::Vector3 getDestination(void) const;
/**
* Place character at new position.
* The character will start following the globally set destination in the detourCrowd,
* unless you give it an individual destination using updateDestination().
**/
virtual void setPosition(Ogre::Vector3 position);
/**
* The current position of the agent.
* Is only up to date once update() has been called in a frame.
**/
virtual Ogre::Vector3 getPosition(void) const;
/**
* Index ID identifying the agent of this character in the crowd
**/
virtual int getAgentID();
/**
* The agent that steers this character within the crowd
**/
virtual const dtCrowdAgent* getAgent();
/**
* Returns true when this character has reached its set destination.
**/
virtual bool destinationReached(void);
/**
* Request to set a manual velocity for this character, to control it
* manually.
* The set velocity will stay active, meaning that the character will
* keep moving in the set direction, until you stop() it.
* Manually controlling a character offers no absolute control as the
* laws of acceleration and max speed still apply to an agent, as well
* as the fact that it cannot steer off the navmesh or into other agents.
* You will notice small corrections to steering when walking into objects
* or the border of the navmesh (which is usually a wall or object).
**/
void setVelocity(Ogre::Vector3 velocity);
/**
* Manually control the character moving it forward.
**/
virtual void moveForward(void);
/**
* Stop any movement this character is currently doing. This means losing
* the requested velocity or target destination.
**/
virtual void stop(void);
/**
* The current velocity (speed and direction) this character is traveling
* at.
**/
virtual Ogre::Vector3 getVelocity(void);
/**
* The current speed this character is traveling at.
**/
virtual Ogre::Real getSpeed(void);
/**
* The maximum speed this character can attain.
* This parameter is configured for the agent controlling this character.
**/
virtual Ogre::Real getMaxSpeed(void);
/**
* The maximum acceleration this character has towards its maximum speed.
* This parameter is configured for the agent controlling this character.
**/
virtual Ogre::Real getMaxAcceleration(void);
/**
* Returns true if this character is moving.
**/
virtual bool isMoving(void);
/**
* The direction in which the character is currently looking.
**/
virtual Ogre::Vector3 getLookingDirection(void);
/**
* Set to true to show visual recast debugging geometry to represent
* the agent of this character..
* Will be initialized to OgreRecastApplication::getDebugDrawing()
* at character construction.
**/
virtual void setDebugVisibility(bool visible) = 0;
/**
* Set whether this character is controlled by an agent or whether it
* will position itself independently based on the requested velocity.
* Set to true to let the character be controlled by an agent.
* Set to false to manually control it without agent, you need to set
* detourTileCache first.
**/
void setAgentControlled(bool agentControlled);
/**
* Determines whether this character is controlled by an agent.
**/
bool isAgentControlled(void);
/**
* Set detour tile cache component.
* This is needed for controlling the agent manually without agent,
* as it will use dtTileCache to add a temporary obstacle at its
* current position to make other characters in the crowd avoid it.
**/
void setDetourTileCache(OgreDetourTileCache* dtTileCache);
/**
* Makes the character clip to the terrain height of the specified
* terrain set. Specify NULL as terrainGroup to disable.
* Height clipping only happens for the visiual character entity,
* the position of the detour crowd agent that controls it is
* not changed.
**/
void clipToTerrain(Ogre::TerrainGroup *terrainGroup);
bool isLoaded(void) const { return mAgentID >= 0; }
virtual void load(void);
virtual void load(Ogre::Vector3 position);
virtual void unLoad(void);
virtual void show(void);
virtual void hide(void);
virtual Ogre::Vector3 getRelativeLookingDirection(void);
virtual void setRelativeLookingDirection(Ogre::Vector3 direction);
protected:
/**
* Update current position of this character to the current position of its agent.
**/
virtual void updatePosition(Ogre::Real timeSinceLastFrame);
/**
* Set destination member variable directly without updating the agent state.
* Usually you should call updateDestination() externally, unless you are controlling
* the agents directly and need to update the corresponding character class to reflect
* the change in state (see OgreRecastApplication friendship).
**/
void setDestination(Ogre::Vector3 destination);
/**
* Scene manager that manages this character.
**/
Ogre::SceneManager *mSceneMgr;
/**
* Node in which this character is.
**/
Ogre::SceneNode *mNode;
/**
* Name of this character.
**/
Ogre::String mName;
/**
* Crowd in which the agent of this character is.
**/
OgreDetourCrowd *mDetourCrowd;
/**
* The agent controlling this character.
**/
const dtCrowdAgent *mAgent;
/**
* ID of mAgent within the crowd.
**/
int mAgentID;
/**
* The current destination set for this agent.
* Take care in properly setting this variable, as it is only updated properly when
* using Character::updateDestination() to set an individual destination for this character.
* After updating the destination of all agents this variable should be set externally using
* setDestination().
**/
Ogre::Vector3 mDestination;
/**
* Velocity set for this agent for manually controlling it.
* If this is not zero then a manually set velocity is currently controlling the movement
* of this character (not pathplanning towards a set destination).
**/
Ogre::Vector3 mManualVelocity;
/**
* True if this character is stopped.
**/
bool mStopped;
/**
* True if character is controlled by agent.
* False if character is manually controlled without agent.
**/
bool mAgentControlled;
/**
* Detour Tile Cache component needed when controlling this character
* manually without agent. It needs the dtTileCache to place a temporary
* obstacle at its current position to make other characters in the crowd
* avoid it.
**/
OgreDetourTileCache* mDetourTileCache;
/**
* Obstacle used when character is not being controlled by agent.
* The temp obstacle is placed on the current position of this character
* so that other agents in the crowd will not walk through it.
**/
dtTileRef mTempObstacle;
/**
* Helper to fix the height of the character to the terrain height after
* a position update. Only the visual character entity's height will be
* altered, not that of the detour crowd agent that controls it.
**/
virtual void clipToTerrainHeight(void);
/**
* The terraingroup the character height will be clipped to.
* Set to NULL to disable this feature.
**/
Ogre::TerrainGroup *mClipTo;
/**
* Ray query used to query the terrain height when terrain clipping is
* enabled. This value is stored as a member for optimization.
**/
Ogre::RaySceneQuery *mRaySceneQuery;
Ogre::Vector3 mLookingDirection;
// Friend the application class to allow setDestinationForAllAgents to update character destination values
friend class OgreRecastApplication;
};
#endif // CHARACTER_H

View File

@@ -0,0 +1,152 @@
/*
OgreCrowd
---------
Copyright (c) 2012 Jonas Hauquier
Additional contributions by:
- mkultra333
- Paul Wilson
Sincere thanks and to:
- Mikko Mononen (developer of Recast navigation libraries)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#ifndef CONVEXSHAPEOBSTACLE_H
#define CONVEXSHAPEOBSTACLE_H
#include "Obstacle.h"
#include "RecastConvexHull.h"
/**
* Convex obstacles are a more complex type of temporary obstacles that can be used together with
* the Detour Tile Cache.
* In contrast to simple cylindrical obstacles they allow any type of geometry to be defined as an obstacle.
* A convex obstacle relies on a simple convex hull of the obstacle it represents. It uses a simplified 2D
* approximation of the convex hull with a height (the hull is on the x-z ground plane, and has one height
* that is the same for each point, kind of like an upright cylinder where the sides don't have to be simply
* round but can be any shape).
* Convex obstacles can both be moved and given a different orientation (rotation). When rotating the convex
* hull approximation has to be rebuilt, as it is only a simple 2D approximation instead of a full 3D hull.
* Also note that the convex hull algorithm that is currently used is very fast but also very limited. Don't
* feed it too much geometry.
* Alternatively, when not rotating obstacles it's also possible to use the bounding box of the object instead
* of calculating a hull (in case the bounding box approximates the obstacle well, for example in case of a
* box or rectangular shape).
**/
class ConvexShapeObstacle : public Obstacle
{
public:
/**
* Construct a convex shape obstacle on the specified position.
* To make this work with detour tileCache a simplified convex hull is created
* from the obstacle entity's geometry. The hull is offset with the specified
* amount from the mesh (offset should generally be agent radius).
* DetourTileCache parameter specifies the tilecache on which the obstacle will
* be added.
**/
ConvexShapeObstacle(Ogre::Vector3 position, Ogre::Real offset, OgreDetourTileCache *detourTileCache);
virtual ~ConvexShapeObstacle(); // Important that the destructor is virtual!
/**
* @see{Obstacle::update()}
**/
virtual void update(long time);
/**
* @see{Obstacle::getEntity()}
**/
virtual Ogre::Entity* getEntity(void);
/**
* @see{Obstacle::getPosition()}
**/
virtual Ogre::Vector3 getPosition(void);
/**
* @see{Obstacle::getOrientation()}
**/
virtual Ogre::Quaternion getOrientation(void);
/**
* Update the orientation (mainly intended for rotation and position) of the obstacle
* to the specified orientation.
* Replaces the old rotation completely (this is not cumulative).
**/
virtual void updateOrientation(Ogre::Quaternion orientation);
/**
* @see{Obstacle::updatePosition(Ogre::Vector3)}
**/
virtual void updatePosition(Ogre::Vector3 position);
protected:
/**
* Current position of this obstacle.
**/
Ogre::Vector3 mPosition;
/**
* Current orientation of this obstacle.
**/
Ogre::Quaternion mOrientation;
/**
* The entity representing this obstacle in the scene.
**/
Ogre::Entity *mEnt;
/**
* The scene node in which the obstacle entity is located.
**/
Ogre::SceneNode *mNode;
/**
* Name of this obstacle.
**/
Ogre::String mName;
/**
* 2D Convex hull with height calculated for this obstacle (for the current rotation).
**/
ConvexVolume *mConvexHull;
/**
* Inputgeom object containing the original geometry of this obstacle (used to generate convex hull from).
**/
InputGeom *mInputGeom;
/**
* Amount that the convex hull is offset from the inputGeom. Normally detour requires the hulls to be offset
* with at least the radius of the agents.
**/
Ogre::Real mOffset;
/**
* Visual debug representation of the calculated convex hull, in the form of a line drawing.
**/
Ogre::ManualObject *mConvexHullDebug;
};
#endif // CONVEXSHAPEOBSTACLE_H

View File

@@ -0,0 +1,208 @@
/*
OgreCrowd
---------
Copyright (c) 2012 Jonas Hauquier
Additional contributions by:
- mkultra333
- Paul Wilson
Sincere thanks and to:
- Mikko Mononen (developer of Recast navigation libraries)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#ifndef CROWDMANAGER_H
#define CROWDMANAGER_H
#include <Ogre.h>
class Character;
class OgreRecast;
class OgreDetourCrowd;
class OgreDetourTileCache;
// TODO maybe add support for "persistent" characters, eg. those that are always visible, or at least of which the agent and AI remains when out of view distance
/**
* Manages and instances a crowd of characters in the vicinity of the camera.
*
**/
class CrowdManager
{
public:
CrowdManager(OgreDetourTileCache *tileCache, Ogre::SceneManager *sceneManager, Ogre::Camera *camera);
void setCamera(Ogre::Camera *camera) { mCamera = camera; }
Ogre::Camera* getCamera(void) { return mCamera; }
void update(Ogre::Real timeSinceLastFrame);
int getNbLoadedTiles(void);
int getNbBorderTiles(void);
void setDebugVisibility(bool visible);
/**
* The size of the crowd
**/
int getSize(void) { return mCrowdSize; }
int getNbAssignedAgents(void) { return mAssignedCharacters.size(); }
int getGridDimensions(void) { return mDimension; }
// TODO define setters for these?
static const Ogre::Real CROWD_PAGE_UPDATE_DELTA;
static const Ogre::Real MAX_CROWD_SIZE;
static const Ogre::Real RADIUS_EPSILON;
static bool HUMAN_CHARACTERS;
static bool INSTANCED_CROWD;
// TODO move this struct to OgreDetourTileCache??
// TODO functionality corresponds largely to OgreDetourTileCache::TileSelection, merge them without breaking anything
struct NavmeshTileSet
{
int xMin; // Min tile X index (inclusive)
int yMin; // Min tile Y index (inclusive)
int xMax; // Max tile X index (inclusive)
int yMax; // Min tile Y index (inclusive)
int getXWidth(void) { return 1+ xMax - xMin ; }
int getYWidth(void) { return 1+ yMax - yMin; }
int getNbTiles(void) { return getXWidth()*getYWidth(); }
};
protected:
/**
* Calculate the navmesh tile-aligned bounding area around the
* current camera position that has to be populated with crowd agents.
**/
NavmeshTileSet calculatePopulatedArea(void);
bool updatePagedCrowd(Ogre::Real timeSinceLastFrame);
void loadAgents(int tx, int ty, int nbAgents);
void unloadAgents(int tx, int ty);
// Make sure tileExists(tx,ty) !!
Ogre::Vector3 placeAgent(Character* character, int tx, int ty);
void placeAgentOnRandomBorderTile(Character *character);
Ogre::Vector3 getRandomPositionInNavmeshTile(int tx, int ty);
Ogre::Vector3 getRandomPositionInNavmeshTileSet(NavmeshTileSet tileSet);
Ogre::AxisAlignedBox getNavmeshTileSetBounds(NavmeshTileSet tileSet);
/**
* Determines whether the tile with specified grid coordinates exists
* and is loaded (in the tilecache).
**/
virtual bool tileExists(int tx, int ty);
void updatePagedAreaDebug(NavmeshTileSet pagedArea);
void debugPrint(Ogre::String message);
Ogre::String tileToStr(int tx, int ty);
Ogre::String tileToStr(Ogre::Vector2 tilePos);
Ogre::Vector3 assignAgentDestination(Character* character);
void unloadAgentsOutsideArea(NavmeshTileSet area);
NavmeshTileSet getExistingArea(NavmeshTileSet area);
void updateBorderTiles(void);
virtual bool walkedOffGrid(const Character* character);
virtual void initAgents(void);
OgreDetourTileCache *mDetourTileCache;
OgreRecast *mRecast;
OgreDetourCrowd *mDetourCrowd;
Ogre::SceneManager *mSceneMgr;
/**
* Camera around which the crowd is intantiated
**/
Ogre::Camera *mCamera;
/**
* All characters in the scene that represent an agent.
**/
std::vector<Character*> mCharacters;
// TODO do I need this list?
std::vector<Character*> mAssignedCharacters; // TODO a linked list might be better
std::vector<Character*> mUnassignedCharacters;
NavmeshTileSet mCurrentlyPagedArea;
/**
* Size (in number of navmesh tiles) in x and y direction
* that the paged area centered around camera position will
* continue and will be populated with agents.
* Defines the size of the area to be populated with agents.
**/
int mPagedAreaDistance;
// TODO allow other methods of selecting area to page? Like a circle around camera position
int mNbPagedTiles;
int mNbTilesInBorder;
int mCrowdSize;
int mDimension;
Ogre::Real mTimeSinceLastUpdate;
Ogre::Entity *mAreaDebug;
/**
* Current visibility of recast visual debug structures.
* True renders them in the scene, false hides them.
**/
bool mDebugDraw;
std::vector<Ogre::Vector2> mBorderTiles;
Ogre::InstanceManager* mInstanceManager;
};
#endif // CROWDMANAGER_H

View File

@@ -0,0 +1,120 @@
/*
OgreCrowd
---------
Copyright (c) 2012 Jonas Hauquier
Additional contributions by:
- mkultra333
- Paul Wilson
Sincere thanks and to:
- Mikko Mononen (developer of Recast navigation libraries)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#ifndef CYLINDEROBSTACLE_H
#define CYLINDEROBSTACLE_H
#include "Obstacle.h"
/**
* A cylinder obstacle is the most simple of obstacles (and probably the fastest).
* Cylinder obstacles are upright cylinders (cannot be rotated, only be moved) that have
* a height, a radius and are placed at a specified position.
* Cylindrical obstacles are ideal for symbolizing things like persons or pillars (in fact
* detourCrowd agents are also cylinders) and are very fast to update on the navmesh.
**/
class CylinderObstacle : public Obstacle
{
public:
/**
* Construct a simple cylindrical obstacle with radius TEMP_OBSTACLE_RADIUS,
* at specified position.
**/
CylinderObstacle(Ogre::Vector3 position, OgreDetourTileCache *detourTileCache);
~CylinderObstacle();
/**
* @see{Obstacle::update()}
**/
virtual void update(long time);
/**
* The reference to the temp obstacle for this obstacle in the detourTileCache.
**/
virtual dtObstacleRef getObstacleRef(void);
/**
* @see{Obstacle::getEntity()}
**/
virtual Ogre::Entity* getEntity(void);
/**
* @see{Obstacle::getPosition()}
**/
virtual Ogre::Vector3 getPosition(void);
/**
* @see{Obstacle::getOrientation()}
**/
virtual Ogre::Quaternion getOrientation(void);
/**
* Not applicable to cylindrical obstacles. Does nothing.
**/
virtual void updateOrientation(Ogre::Quaternion orientation);
/**
* @see{Obstacle::updatePosition(Ogre::Vector3)}
**/
virtual void updatePosition(Ogre::Vector3 position);
protected:
/**
* Current position of this obstacle.
**/
Ogre::Vector3 mPosition;
/**
* The reference to the obstacle in the detourTileCache.
**/
dtObstacleRef mObstacleRef;
/**
* The entity representing this obstacle in the scene.
**/
Ogre::Entity *mEnt;
/**
* The scene node in which the obstacle entity is located.
**/
Ogre::SceneNode *mNode;
/**
* Name of this obstacle.
**/
Ogre::String mName;
};
#endif // CYLINDEROBSTACLE_H

View File

@@ -0,0 +1,98 @@
/*
OgreCrowd
---------
Copyright (c) 2012 Jonas Hauquier
Additional contributions by:
- mkultra333
- Paul Wilson
Sincere thanks and to:
- Mikko Mononen (developer of Recast navigation libraries)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#ifndef INSTANCEDCHARACTER_H
#define INSTANCEDCHARACTER_H
#include <OgrePrerequisites.h>
#include "Character.h"
class InstancedCharacter : public Character
{
public:
InstancedCharacter(Ogre::String name, Ogre::SceneManager* sceneMgr, OgreDetourCrowd* detourCrowd, Ogre::InstanceManager* instanceMgr, bool debugDraw = false, Ogre::Vector3 position = Ogre::Vector3::ZERO);
/**
* Update one tick in the render loop. Advances animation and character position.
* In order for the agents to be updated, you first need to call the detourCrowd
* update function.
**/
virtual void update(Ogre::Real timeSinceLastFrame);
/**
* The instanced entity that represents this character in the scene
**/
virtual Ogre::InstancedEntity* getEntity(void);
/**
* @see{Character::setDebugVisibility(bool)}
**/
virtual void setDebugVisibility(bool visible);
virtual bool getDebugVisibility(void);
virtual void show(void);
void randomizeAnimationPosition(void);
protected:
bool mDebugDraw;
/**
* Main entity that represents this character.
**/
Ogre::InstancedEntity *mEnt;
Ogre::InstanceManager* mInstanceManager;
/**
* Currently active animation state.
**/
Ogre::AnimationState* mAnimState;
/**
* Speed scaling factor of the animation playback.
**/
Ogre::Real mAnimSpeedScale;
/**
* Scenenode that stores all geometry related to
* recast debug drawing. Can be made visible with
* setDebugVisibility().
**/
Ogre::SceneNode *mDebugNode;
};
#endif // INSTANCEDCHARACTER_H

View File

@@ -0,0 +1,125 @@
/*
OgreCrowd
---------
Copyright (c) 2012 Jonas Hauquier
Additional contributions by:
- mkultra333
- Paul Wilson
Sincere thanks and to:
- Mikko Mononen (developer of Recast navigation libraries)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#ifndef OBSTACLE_H
#define OBSTACLE_H
#include "OgreDetourTileCache.h"
/**
* Dynamic obstacle that can be put on the navmesh when using DetourTileCache.
* Obstacles only mark polygons of the navmesh with a special flag (such as unwalkable)
* and do not actually add new geometry to the navmesh input. This makes them faster,
* but no new areas can be added to the navmesh, only areas can be marked or cut out.
* Obstacles are a lot faster than rebuilding a navmesh tile with new geometry, however.
**/
class Obstacle
{
public:
enum QueryFlags {
DEFAULT_MASK = 1u<<0,
NAVMESH_MASK = 1u<<1,
OBSTACLE_MASK= 1u<<2
};
/**
* Construct an obstacle that will be added to the specified detour Tilecache.
**/
Obstacle(OgreDetourTileCache *detourTileCache);
virtual ~Obstacle();
/**
* No use at the moment.
* The future plan was to use it to do deferred updates of obstacle position/rotation.
* Not sure whether it has much use, however.
**/
virtual void update(long time) = 0;
/**
* The entity that represents this obstacle in the scene.
**/
virtual Ogre::Entity* getEntity(void)=0;
/**
* Update the orientation (mainly intended for rotation and position) of the obstacle
* to the specified orientation. Does not work for all types of obstacles (will have
* no effect in that case).
* Replaces previous rotation completely, not cumulative.
**/
virtual void updateOrientation(Ogre::Quaternion orientation) = 0;
/**
* Update position of the obstacle with this new position.
* The obstacle position will be updated on the navmesh if the difference
* in squared distance with the previous position is larger than SQUARED_DISTANCE_EPSILON.
* Replaces old position completely, not cumulative.
**/
virtual void updatePosition(Ogre::Vector3 position) = 0;
/**
* The current position of the obstacle.
**/
virtual Ogre::Vector3 getPosition(void) = 0;
/**
* The current orientation of the obstacle.
**/
virtual Ogre::Quaternion getOrientation(void) = 0;
/**
* The minimum distance an obstacle has to be moved before the obstacle is updated on the
* navmesh.
**/
static const Ogre::Real SQUARED_DISTANCE_EPSILON;
/**
* Delta tolerance in degrees with which a new orientation has to differ from the previous one
* in order for the obstacle to be updated.
**/
static const Ogre::Real ORIENTATION_TOLERANCE_DEGREES;
protected:
/**
* The detour tile cache that manages this obstacle.
**/
OgreDetourTileCache *mDetourTileCache;
/**
* Scene manager on which the visual representation of this obstacle is drawn.
**/
Ogre::SceneManager *mSceneMgr;
};
#endif // OBSTACLE_H

View File

@@ -0,0 +1,267 @@
/*
OgreCrowd
---------
Copyright (c) 2012 Jonas Hauquier
Additional contributions by:
- mkultra333
- Paul Wilson
Sincere thanks and to:
- Mikko Mononen (developer of Recast navigation libraries)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#ifndef OGREDETOURCROWD_H
#define OGREDETOURCROWD_H
#include "OgreRecastDefinitions.h"
#include "OgreRecast.h"
#include "DetourCrowd.h"
#include <vector>
/**
* Ogre wrapper around DetourCrowd.
* Controls a crowd of agents that can steer to avoid each other and follow
* individual paths.
*
* This class is largely based on the CrowdTool used in the original recastnavigation
* demo.
**/
class OgreDetourCrowd
{
public:
/**
* Initialize a detour crowd that will manage agents on the specified
* recast navmesh. It does not matter how this navmesh is constructed
* (either with OgreRecast directly or with DetourTileCache).
* Parameters such as agent dimensions will be taken from the specified
* recast component.
**/
OgreDetourCrowd(OgreRecast *recast);
~OgreDetourCrowd(void);
/**
* Add an agent to the crowd
* Returns ID of created agent (-1 if maximum agents is already created)
**/
int addAgent(const Ogre::Vector3 position);
/**
* Retrieve agent with specified ID from the crowd.
**/
const dtCrowdAgent* getAgent(int id);
/**
* Remove agent with specified ID from the crowd.
**/
void removeAgent(const int idx);
/**
* Set global destination or target for all agents in the crowd.
* Setting adjust to true will try to adjust the current calculated path
* of the agents slightly to end at the new destination, avoiding the need
* to calculate a completely new path. This only works if the destination is
* close to the previously set one, for example when chasing a moving entity.
**/
void setMoveTarget(Ogre::Vector3 position, bool adjust);
/**
* Set target or destination for an individual agent.
* Setting adjust to true will try to adjust the current calculated path
* of the agent slightly to end at the new destination, avoiding the need
* to calculate a completely new path. This only works if the destination is
* close to the previously set one, for example when chasing a moving entity.
**/
void setMoveTarget(int agentId, Ogre::Vector3 position, bool adjust);
/**
* Request a specified velocity for the agent with specified index.
* Requesting a velocity means manually controlling an agent.
* Returns true if the request was successful.
**/
bool requestVelocity(int agentId, Ogre::Vector3 velocity);
/**
* Cancels any request for the specified agent, making it stop.
* Returns true if the request was successul.
**/
bool stopAgent(int agentId);
/**
* Helper that calculates the needed velocity to steer an agent to a target destination.
* Parameters:
* position is the current position of the agent
* target is the target destination to reach
* speed is the (max) speed the agent can travel at
* Returns the calculated velocity.
*
* This function can be used together with requestMoveVelocity to achieve the functionality
* of adjustMoveTarget function.
**/
static Ogre::Vector3 calcVel(Ogre::Vector3 position, Ogre::Vector3 target, Ogre::Real speed);
static float getDistanceToGoal(const dtCrowdAgent* agent, const float maxRange);
static bool destinationReached(const dtCrowdAgent* agent, const float maxDistanceFromTarget);
/**
* Update method for the crowd manager. Will calculate new positions for moving agents.
* Call this method in your frameloop every frame to make your agents move.
*
* DetourCrowd uses sampling based local steering to calculate a velocity vector for each
* agent. The calculation time of this is limited to the number of agents in the crowd and
* the sampling amount (which is a constant).
*
* Additionally pathfinding tasks are queued and the number of computations is limited, to
* limit the maximum amount of time spent for preparing a frame. This can have as consequence
* that some agents will only start to move after a few frames, when their paths are calculated.
**/
void updateTick(const float dt);
/**
* The height of agents in this crowd. All agents in a crowd have the same height, and height is
* determined by the agent height parameter with which the navmesh is build.
**/
Ogre::Real getAgentHeight(void);
/**
* The radius of agents in this crowd. All agents in a crowd have the same radius, and radius
* determined by the agent radius parameter with which the navmesh is build.
**/
Ogre::Real getAgentRadius(void);
/**
* The number of (active) agents in this crowd.
**/
int getNbAgents(void);
/**
* The maximum number of agents that are allowed in this crowd.
**/
int getMaxNbAgents(void);
/**
* Get all (active) agents in this crowd.
**/
std::vector<dtCrowdAgent*> getActiveAgents(void);
/**
* Get the IDs of all (active) agents in this crowd.
**/
std::vector<int> getActiveAgentIds(void);
/**
* The last set destination for the crowd. This is the destination
* that will be assigned to newly added agents.
**/
Ogre::Vector3 getLastDestination(void);
/**
* Reference to the DetourCrowd object that is wrapped.
**/
dtCrowd* m_crowd;
/**
* Reference to the Recast/Detour wrapper object for Ogre.
**/
OgreRecast *m_recast;
/**
* The latest set target or destination section in the recast navmesh.
**/
dtPolyRef m_targetRef;
/**
* The latest set target or destination position.
**/
float m_targetPos[3];
/**
* Max pathlength for calculated paths.
**/
static const int AGENT_MAX_TRAIL = 64;
/**
* Max number of agents allowed in this crowd.
**/
static const int MAX_AGENTS = 128;
/**
* Stores the calculated paths for each agent in the crowd.
**/
struct AgentTrail
{
float trail[AGENT_MAX_TRAIL*3];
int htrail;
};
AgentTrail m_trails[MAX_AGENTS];
/**
* Debug info object used in the original recast/detour demo, not used in this
* application.
**/
dtCrowdAgentDebugInfo m_agentDebug;
/**
* Parameters for obstacle avoidance of DetourCrowd steering.
**/
dtObstacleAvoidanceDebugData* m_vod;
// Agent configuration parameters
bool m_anticipateTurns;
bool m_optimizeVis;
bool m_optimizeTopo;
bool m_obstacleAvoidance;
bool m_separation;
float m_obstacleAvoidanceType;
float m_separationWeight;
protected:
/**
* Helper to calculate the needed velocity to steer an agent to a target destination.
* Parameters:
* velocity is the return parameter, the calculated velocity
* position is the current position of the agent
* target is the target destination to reach
* speed is the (max) speed the agent can travel at
*
* This function can be used together with requestMoveVelocity to achieve the functionality
* of the old adjustMoveTarget function.
**/
static void calcVel(float* velocity, const float* position, const float* target, const float speed);
private:
/**
* Number of (active) agents in the crowd.
**/
int m_activeAgents;
};
#endif // OGREDETOURCROWD_H

View File

@@ -0,0 +1,822 @@
/*
OgreCrowd
---------
Copyright (c) 2012 Jonas Hauquier
Additional contributions by:
- mkultra333
- Paul Wilson
Sincere thanks and to:
- Mikko Mononen (developer of Recast navigation libraries)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#ifndef OGREDETOURTILECACHE_H
#define OGREDETOURTILECACHE_H
#include <Ogre.h>
#include "OgreRecast.h"
#include "DetourTileCacheBuilder.h"
#include "DetourTileCache.h"
#include "DetourCommon.h"
#include "fastlz.h"
#include "RecastInputGeom.h"
const float TEMP_OBSTACLE_RADIUS = 1.0f;
const float TEMP_OBSTACLE_HEIGHT = 2.0f;
struct TileSelection
{
Ogre::AxisAlignedBox bounds;
int minTx;
int maxTx;
int minTy;
int maxTy;
};
/**
* Struct to store a request to add or remove
* a convex obstacle to the tilecache as a deferred
* command (currently unused).
**/
struct ogredtTileCacheConvexObstacle
{
ConvexVolume *obstacle;
dtCompressedTileRef touched[DT_MAX_TOUCHED_TILES];
dtCompressedTileRef pending[DT_MAX_TOUCHED_TILES];
unsigned short salt;
unsigned char state;
unsigned char ntouched;
unsigned char npending;
ogredtTileCacheConvexObstacle* next; // Single linked list
};
/**
* Implementation of the meshProcess callback that detourTileCache
* does after building a navmesh. It allows you to do some extra
* processing on the navmesh, such as connecting off-mesh connections
* and assigning flags to certain poly areas.
* The reason it is initialized with an inputGeom object is that it
* is intended that the inputGeom not only stores the input geometry,
* but also information that has to be added to the navmesh in this
* post-processing phase.
**/
struct MeshProcess : public dtTileCacheMeshProcess
{
InputGeom* m_geom;
inline MeshProcess() : m_geom(0) {
}
inline void init(InputGeom* geom) {
m_geom = geom;
}
/**
* Callback that happens after navmesh has been constructed.
* Allows you to do some additional post-processing on the navmesh,
* such as adding off-mesh connections or marking poly areas with
* certain flags.
**/
virtual void process(struct dtNavMeshCreateParams* params,
unsigned char* polyAreas, unsigned short* polyFlags)
{
// Update poly flags from areas.
for (int i = 0; i < params->polyCount; ++i)
{
if (polyAreas[i] == DT_TILECACHE_WALKABLE_AREA)
polyAreas[i] = SAMPLE_POLYAREA_GROUND;
if (polyAreas[i] == SAMPLE_POLYAREA_GROUND ||
polyAreas[i] == SAMPLE_POLYAREA_GRASS ||
polyAreas[i] == SAMPLE_POLYAREA_ROAD)
{
polyFlags[i] = SAMPLE_POLYFLAGS_WALK;
}
else if (polyAreas[i] == SAMPLE_POLYAREA_WATER)
{
polyFlags[i] = SAMPLE_POLYFLAGS_SWIM;
}
else if (polyAreas[i] == SAMPLE_POLYAREA_DOOR)
{
polyFlags[i] = SAMPLE_POLYFLAGS_WALK | SAMPLE_POLYFLAGS_DOOR;
}
}
// Pass in off-mesh connections.
if (m_geom)
{
// TODO implement off-mesh connections
/*
params->offMeshConVerts = m_geom->getOffMeshConnectionVerts();
params->offMeshConRad = m_geom->getOffMeshConnectionRads();
params->offMeshConDir = m_geom->getOffMeshConnectionDirs();
params->offMeshConAreas = m_geom->getOffMeshConnectionAreas();
params->offMeshConFlags = m_geom->getOffMeshConnectionFlags();
params->offMeshConUserID = m_geom->getOffMeshConnectionId();
params->offMeshConCount = m_geom->getOffMeshConnectionCount();
*/
}
}
};
/**
* FastLZ implementation of detour tile cache tile compressor.
* You can define a custom implementation if you wish to use
* a different compression algorithm for compressing your
* detour heightfield tiles.
* The result of compression runs is the data that detourTileCache
* stores in memory (or can save out to disk).
* The compressed heightfield tiles are stored in ram as they allow
* to quickly generate a navmesh tile, possibly with obstacles added
* to them, without the need for a full rebuild.
**/
struct FastLZCompressor : public dtTileCacheCompressor
{
virtual int maxCompressedSize(const int bufferSize)
{
return (int)(bufferSize* 1.05f);
}
virtual dtStatus compress(const unsigned char* buffer, const int bufferSize,
unsigned char* compressed, const int /*maxCompressedSize*/, int* compressedSize)
{
*compressedSize = fastlz_compress((const void *const)buffer, bufferSize, compressed);
return DT_SUCCESS;
}
virtual dtStatus decompress(const unsigned char* compressed, const int compressedSize,
unsigned char* buffer, const int maxBufferSize, int* bufferSize)
{
*bufferSize = fastlz_decompress(compressed, compressedSize, buffer, maxBufferSize);
return *bufferSize < 0 ? DT_FAILURE : DT_SUCCESS;
}
};
/**
* Allows a custom memory allocation technique to be implemented
* for storing compressed tiles. This implementation does a linear
* memory allocation.
**/
struct LinearAllocator : public dtTileCacheAlloc
{
unsigned char* buffer;
int capacity;
int top;
int high;
LinearAllocator(const int cap) : buffer(0), capacity(0), top(0), high(0)
{
resize(cap);
}
~LinearAllocator()
{
dtFree(buffer);
}
void resize(const int cap)
{
if (buffer) dtFree(buffer);
buffer = (unsigned char*)dtAlloc(cap, DT_ALLOC_PERM);
capacity = cap;
}
virtual void reset()
{
high = dtMax(high, top);
top = 0;
}
virtual void* alloc(const int size)
{
if (!buffer)
return 0;
if (top+size > capacity)
return 0;
unsigned char* mem = &buffer[top];
top += size;
return mem;
}
virtual void free(void* /*ptr*/)
{
// Empty
}
};
/**
* Maximum layers (floor levels) that 2D navmeshes can have in the tilecache.
* This determines the domain size of the tilecache pages, as their dimensions
* are width*height*layers.
**/
static const int MAX_LAYERS = 32;
/**
* Struct that stores the actual tile data in binary form.
**/
struct TileCacheData
{
unsigned char* data;
int dataSize;
};
/**
* Rasterization context stores temporary data used
* when rasterizing inputGeom into a navmesh.
**/
struct RasterizationContext
{
RasterizationContext() :
solid(0),
triareas(0),
lset(0),
chf(0),
ntiles(0)
{
memset(tiles, 0, sizeof(TileCacheData)*MAX_LAYERS);
}
~RasterizationContext()
{
rcFreeHeightField(solid);
delete [] triareas;
rcFreeHeightfieldLayerSet(lset);
rcFreeCompactHeightfield(chf);
for (int i = 0; i < MAX_LAYERS; ++i)
{
dtFree(tiles[i].data);
tiles[i].data = 0;
}
}
rcHeightfield* solid;
unsigned char* triareas;
rcHeightfieldLayerSet* lset;
rcCompactHeightfield* chf;
TileCacheData tiles[MAX_LAYERS];
int ntiles;
};
/**
* Build context stores temporary data used while
* building a navmesh tile.
**/
struct BuildContext
{
inline BuildContext(struct dtTileCacheAlloc* a) : layer(0), lcset(0), lmesh(0), alloc(a) {}
inline ~BuildContext() { purge(); }
void purge()
{
dtFreeTileCacheLayer(alloc, layer);
layer = 0;
dtFreeTileCacheContourSet(alloc, lcset);
lcset = 0;
dtFreeTileCachePolyMesh(alloc, lmesh);
lmesh = 0;
}
struct dtTileCacheLayer* layer;
struct dtTileCacheContourSet* lcset;
struct dtTileCachePolyMesh* lmesh;
struct dtTileCacheAlloc* alloc;
};
// TODO put in class context
/**
* Calculate the memory space used by the tilecache.
**/
static int calcLayerBufferSize(const int gridWidth, const int gridHeight)
{
const int headerSize = dtAlign4(sizeof(dtTileCacheLayerHeader));
const int gridSize = gridWidth * gridHeight;
return headerSize + gridSize*4;
}
/**
* DetourTileCache manages a large grid of individual navmeshes stored in pages to
* allow managing a navmesh for a very large map. Navmesh pages can be requested
* when needed or swapped out when they are no longer needed.
* Using a tilecache the navigation problem is localized to one tile, but pathfinding
* can still find a path that references to other neighbour tiles on the higher hierarchy
* level of the tilecache. Localizing the pathfinding problem allows it to be more scalable,
* also for very large worlds.
* DetouTileCache stores navmeshes in an intermediary format as 2D heightfields
* that can have multiple levels. It allows to quickly generate a 3D navmesh from
* this intermediary format, with the additional option of adding or removing
* temporary obstacles to the navmesh and regenerating it.
**/
class OgreDetourTileCache
{
public:
/**
* Create a tilecache that will build a tiled recast navmesh stored at the specified
* OgreRecast component. Will use specified tilesize (a multiple of 8 between 16 and 128),
* all other configuration parameters are copied from the OgreRecast component configuration.
* Tilesize is the number of (recast) cells per tile.
**/
OgreDetourTileCache(OgreRecast *recast, int tileSize = 48);
~OgreDetourTileCache(void);
/**
* Configure the tilecache for building navmesh tiles from the specified input geometry.
* The inputGeom is mainly used for determining the bounds of the world for which a navmesh
* will be built, so at least bmin and bmax of inputGeom should be set to your world's outer
* bounds. This world bounding box is used to calculate the grid size that the tilecache has
* to initialize.
* This method has to be called once after construction, and before any tile builds happen.
**/
bool configure(InputGeom *inputGeom);
/**
* Find tiles that (partially or completely) intersect the specified bounding area.
* The selectionArea has to be in world units.
* TileCache needs to be configured before this method can work (needs to know world size
* of tilecache).
* TileSelection contains bounding box aligned to the tile bounds and tx ty index ranges
* for the affected tiles. Note that tile ranges are inclusive! (eg. if minTx=1 and maxTx=1
* then tile at x=1 has to be rebuilt)
* It is not necessary for tiles to be already built in order for
* them to be included in the selection.
* Make sure you use the included bounding box instead of an arbitrary selection bounding
* box to bound inputGeom used for rebuilding tiles. Or you might not include all geometry
* needed to rebuild all affected tiles correctly.
**/
TileSelection getTileSelection(const Ogre::AxisAlignedBox &selectionArea);
/**
* Returns a bounding box that matches the tile bounds of this cache and that is at least
* as large as the specified selectionArea bounding box. Height (y) coordinates will be set
* to the min and max height of this tilecache. (tile selection only happens in x-z plane).
* Use this function to get correct bounding boxes to cull your inputGeom or scene geometry
* with for tile rebuilding.
**/
Ogre::AxisAlignedBox getTileAlignedBox(const Ogre::AxisAlignedBox &selectionArea);
/**
* Returns the world-space bounds of the tile at specified grid position.
* Make sure that tx and ty satisfy isWithinBounds(tx, ty).
**/
Ogre::AxisAlignedBox getTileBounds(int tx, int ty);
/**
* The size of one tile in world units.
* This equals the number of cells per tile, multiplied with the cellsize.
**/
inline Ogre::Real getTileSize(void) { return m_tileSize*m_cellSize; }
OgreRecast* getRecast(void) { return m_recast; }
/**
* Build all tiles of the tilecache and construct a recast navmesh from the
* specified entities. These entities need to be already added to the scene so that
* their world position and orientation can be calculated.
*
* This is an Ogre adaptation of Sample_TempObstacles::handleBuild()
* First init the OgreRecast module like you would construct a simple single
* navmesh, then invoke this method instead of OgreRecast::NavMeshBuild() to create
* a tileCache from the specified ogre geometry.
* The specified ogre entities need to be added to a scenenode in the scene before this
* method is called.
* The resulting navmesh will be created in the OgreRecast module, at OgreRecast::m_navMesh;
*
* Will issue a configure() call so the entities specified will determine the world bounds
* of the tilecache.
**/
bool TileCacheBuild(std::vector<Ogre::Entity*> srcMeshes);
/**
* Build all navmesh tiles from specified input geom.
*
* Will issue a configure() call so the inputGeom specified will determine the world bounds
* of the tilecache. Therefore you must specify the inputGeom for the entire world.
*
* @see OgreDetourTileCache::TileCacheBuild(std::vector<Ogre::Entity*>)
**/
bool TileCacheBuild(InputGeom *inputGeom);
// TODO maybe provide isLoaded(tx, ty) method
// TODO create better distinction between loading compressed tiles in cache and building navmesh from them?
// TODO are both updateFromGeometry() and buildTiles() necessary, or can update be dropped? It might be confusing.
/**
* Build or rebuild a cache tile at the specified x and y position in the tile grid.
* Tile is built or rebuilt no matter whether there was already a tile at that position in the grid
* or not. If there previously was a tile in the specified grid position, it is first removed from the
* tilecache and replaced with the new one.
*
* At the moment this will issue an immediate update of the navmesh at the
* corresponding tiles. (the alternative is adding a request that is processed as deferred command)
*
* Note that you can speed this up by building an inputGeom from only the area that is rebuilt.
* Don't use an arbitrary bounding box for culling the inputGeom, but use getTileAlignedBox() instead!
**/
bool buildTile(const int tx, const int ty, InputGeom *inputGeom);
/**
* Build or rebuild a cache tiles or tiles that cover the specified bounding box area.
*
* The tiles are built or rebuilt no matter whether there was already a tile at that position in the grid
* or not. If there previously was a tile in the specified grid position, it is first removed from the
* tilecache and replaced with the new one.
*
* Make sure that the specified inputGeom is either the inputGeom of the complete scene (inefficient) or is
* built with a tile aligned bounding box (getTileAlignedBox())! The areaToUpdate value can be arbitrary,
* but will be converted to a tile aligned box.
*
* At the moment this will issue an immediate update of the navmesh at the
* corresponding tiles. (the alternative is adding a request that is processed as deferred command)
**/
void buildTiles(InputGeom *inputGeom, const Ogre::AxisAlignedBox *areaToUpdate = NULL);
/**
* Build or rebuild tile from list of entities.
* @see{buildTiles(InputGeom*, const Ogre::AxisAlignedBox*)}
**/
void buildTiles(std::vector<Ogre::Entity*> srcEntities, const Ogre::AxisAlignedBox *areaToUpdate = NULL);
// TODO maybe also add a unloadAllTilesExcept(boundingBox) method
/**
* Unload all tiles that cover the specified bounding box. The tiles are removed from the
* cache.
**/
void unloadTiles(const Ogre::AxisAlignedBox &areaToUpdate);
/**
* Gets grid coordinates of the tile that contains the specified world position.
**/
void getTileAtPos(const float* pos, int& tx, int& ty);
/**
* Gets grid coordinates of the tile that contains the specified world position.
**/
Ogre::Vector2 getTileAtPos(const Ogre::Vector3 pos);
/**
* Determines whether there is a tile loaded at the specified grid position.
**/
bool tileExists(int tx, int ty);
/**
* Determines whether the specified grid index is within the outer bounds of this tilecache.
**/
bool isWithinBounds(int tx, int ty);
/**
* Determines whether the specified world position is within the outer bounds of this tilecache,
* ie the coordinates are contained within a tile that is within the cache bounds.
**/
bool isWithinBounds(Ogre::Vector3 pos);
Ogre::AxisAlignedBox getWorldSpaceBounds(void);
TileSelection getBounds(void);
bool saveAll(Ogre::String filename);
bool loadAll(Ogre::String filename);
/**
* Update (tick) the tilecache.
* You must call this method in your render loop continuously to dynamically
* update the navmesh when obstacles are added or removed.
* Navmesh rebuilding happens per tile and only where needed. Tile rebuilding is
* timesliced.
**/
void handleUpdate(const float dt);
/**
* Remove all (cylindrical) temporary obstacles from the tilecache.
* The navmesh will be rebuilt after the next (one or more) update()
* call.
**/
void clearAllTempObstacles(void);
/**
* Add a temporary (cylindrical) obstacle to the tilecache (as a deferred request).
* The navmesh will be updated correspondingly after the next (one or many)
* update() call as a deferred command.
* If m_tileCache->m_params->maxObstacles obstacles are already added, this call
* will have no effect. Also, at one time only MAX_REQUESTS can be added, or nothing
* will happen.
*
* If successful returns a reference to the added obstacle.
**/
dtObstacleRef addTempObstacle(Ogre::Vector3 pos);
/**
* Remove temporary (cylindrical) obstacle with specified reference. The affected tiles
* will be rebuilt. This operation is deferred and will happen in one of the next
* update() calls. At one time only MAX_REQUESTS obstacles can be removed, or nothing will happen.
**/
bool removeTempObstacle(dtObstacleRef obstacleRef);
/**
* Remove a temporary (cylindrical) obstacle from the tilecache (as a deferred request).
* Uses a ray query to find the temp obstacle.
* The navmesh will be updated correspondingly after the next (one or many)
* update() call as a deferred command.
* At one time only MAX_REQUESTS obstacles can be removed, or nothing will happen.
**/
dtObstacleRef removeTempObstacle(Ogre::Vector3 raySource, Ogre::Vector3 rayHit);
/**
* Execute a ray intersection query to find the first temporary (cylindrical) obstacle that
* hits the ray, if any.
**/
dtObstacleRef hitTestObstacle(const dtTileCache* tc, const float* sp, const float* sq);
/**
* Returns a list of tile references to compressed tiles that cover the specified bounding
* box area.
**/
std::vector<dtCompressedTileRef> getTilesContainingBox(Ogre::Vector3 boxMin, Ogre::Vector3 boxMax);
/**
* Returns a list of tile references to compressed tiles that cover the area of a circle with
* specified radius around the specified position.
**/
std::vector<dtCompressedTileRef> getTilesAroundPoint(Ogre::Vector3 point, Ogre::Real radius);
/**
* Add a convex shaped temporary obstacle to the tilecache in pretty much the same way as cylindrical
* obstacles are added.
* Currently this is implemented a lot less efficiently than cylindrical obstacles, as it issues a complete
* rebuild of the affected tiles, instead of just cutting out the poly area of the obstacle.
* This is a big TODO that I'm holding off because it requires changes to the recast libraries themselves.
* I wait in hopes that this feature will appear in the original recast code.
* In the meanwhile, if you are looking for this, someone implemented it and shared it on the mailing list:
* http://groups.google.com/group/recastnavigation/msg/92d5f131561ddad1
* And corresponding ticket: http://code.google.com/p/recastnavigation/issues/detail?id=206
*
* The current implementation of convex obstacles is very simple and not deferred. Also obstacles
* are stored in the inputGeom, which is not really nice.
**/
//TODO by adding deferred tasking to add and removeConvexShapeObstacle one can add multiple shapes at once to the same tile without it being rebuilt multiple times
int addConvexShapeObstacle(ConvexVolume *obstacle);
/**
* Remove convex obstacle from the tileCache. The affected navmesh tiles will be rebuilt.
**/
bool removeConvexShapeObstacle(ConvexVolume* convexHull);
/**
* Remove convex obstacle with specified id from the tileCache. The affected navmesh tiles will be rebuilt.
* If removedObstacle is a valid pointer it will contain a reference to the removed obstacle.
**/
bool removeConvexShapeObstacleById(int obstacleIndex, ConvexVolume** removedObstacle = NULL);
/**
* Raycast the inputGeom and remove the hit convex obstacle. The affected navmesh tiles will be rebuilt.
* If removedObstacle is a valid pointer it will contain a reference to the removed obstacle.
**/
int removeConvexShapeObstacle(Ogre::Vector3 raySource, Ogre::Vector3 rayHit, ConvexVolume** removedObstacle = NULL);
/**
* Returns the id of the specified convex obstacle. Returns -1 if this obstacle is not currently added to the tilecache.
* Note: Ids are just array indexes and can change when obstacles are added or removed. Use with care!
**/
int getConvexShapeObstacleId(ConvexVolume *convexHull);
/**
* Returns the convex obstacle with specified id or index.
**/
ConvexVolume* getConvexShapeObstacle(int obstacleIndex);
/**
* Raycast inputGeom to find intersection with a convex obstacle. Returns the id of the hit
* obstacle, -1 if none hit.
**/
int hitTestConvexShapeObstacle(Ogre::Vector3 raySource, Ogre::Vector3 rayHit);
/**
* Remove the tile with specified reference from the tilecache. The associated navmesh tile will also
* be removed.
**/
bool removeTile(dtCompressedTileRef tileRef);
/**
* Debug draw the tile at specified grid location.
**/
void drawDetail(const int tx, const int ty);
/**
* Debug draw all tiles in the navmesh.
**/
void drawNavMesh(void);
/**
* Unused debug drawing function from the original recast demo.
* Used for drawing the obstacles in the scene.
* In this demo application we use the Obstacle class to represent obstacles in the scene.
**/
void drawObstacles(const dtTileCache* tc);
/**
* Ogre Recast component that holds the recast config and where the navmesh will be built.
**/
OgreRecast *m_recast;
/**
* Max number of layers a tile can have
**/
static const int EXPECTED_LAYERS_PER_TILE;
/**
* Max number of (temp) obstacles that can be added to the tilecache
**/
static const int MAX_OBSTACLES;
/**
*
* Extra padding added to the border size of tiles (together with agent radius)
**/
static const float BORDER_PADDING;
/**
* Set to false to disable debug drawing. Improves performance.
**/
static bool DEBUG_DRAW;
/**
* Set to true to draw the bounding box of the tile areas that were rebuilt.
**/
static bool DEBUG_DRAW_REBUILT_BB;
protected:
/**
* Build the 2D navigation grid divided in layers that is the intermediary format stored in the tilecache.
* Builds the specified tile from the given input geometry. Only the part of the geometry that intersects the
* needed tile is used.
* From this format a 3D navmesh can be quickly generated at runtime.
* This process uses a large part of the recast navmesh building pipeline (implemented in OgreRecast::NavMeshBuild()),
* up till step 4.
**/
int rasterizeTileLayers(InputGeom* geom, const int tx, const int ty, const rcConfig& cfg, TileCacheData* tiles, const int maxTiles);
/**
* Debug draw a navmesh poly
**/
void drawPolyMesh(const Ogre::String tileName, const struct dtTileCachePolyMesh &mesh, const float *orig, const float cs, const float ch, const struct dtTileCacheLayer &regionLayers, bool colorRegions=true);
/**
* Inits the tilecache. Helper used by constructors.
**/
bool initTileCache(void);
/**
* InputGeom from which the tileCache is initially inited (it's bounding box is considered the bounding box
* for the entire world that the navmesh will cover). Tile build methods without specific geometry or entity
* input will build navmesh from this geometry.
* It also stored the convex temp obstacles. (will be gone in the future)
* In the future this variable will probably disappear.
**/
InputGeom* m_geom;
// TODO maybe in the future I don't want to store inputgeom anymore, at the moment it's only used for adding convex shapes (what really should be done from compressed tiles instead of rebuilding from input geom) The whole navmesh can be stored as compressed tiles, the input geom does not need to be stored.
/**
* Set to true to keep intermediary results from navmesh build for debugging purposes.
* Set to false to free up memory after navmesh was built.
* Same as in official recast demo. (it's a checkbox in the gui)
**/
bool m_keepInterResults;
/**
* The tile cache memory allocator implementation used.
**/
struct LinearAllocator *m_talloc;
/**
* The tile compression implementation used.
**/
struct FastLZCompressor* m_tcomp;
/**
* Callback handler that processes right after processing
* a tile mesh. Adds off-mesh connections to the mesh.
**/
struct MeshProcess *m_tmproc;
/**
* The detourTileCache component this class wraps.
**/
class dtTileCache *m_tileCache;
/**
* Recast config (copied from the OgreRecast component).
**/
rcConfig m_cfg;
/**
* DetourTileCache configuration parameters.
**/
dtTileCacheParams m_tcparams;
/**
* Context that stores temporary working variables when navmesh building.
**/
rcContext *m_ctx;
/**
* Metrics for measuring and profiling build times and memory usage.
**/
float m_cacheBuildTimeMs;
int m_cacheCompressedSize;
int m_cacheRawSize;
int m_cacheLayerCount;
int m_cacheBuildMemUsage;
/**
* Configuration parameters.
**/
int m_maxTiles;
int m_maxPolysPerTile;
int m_tileSize;
float m_cellSize;
/**
* Size of the tile grid (x dimension)
**/
int m_tw;
/**
* Size of the tile grid (y dimension)
**/
int m_th;
/**
* Unused.
* Could serve for deferring convex obstacle adding/removing requests.
**/
ConvexVolume* mChangedConvexVolumes[InputGeom::MAX_VOLUMES]; // TODO is this really MAX_VOLUMES? would be more like MAX_REQUESTS
int mChangedConvexVolumesCount;
/**
* Pointer to debug drawn bounding box of rebuilt tiles.
* Used when DEBUG_DRAW_REBUILT_BB is true.
**/
Ogre::ManualObject* mDebugRebuiltBB;
static const int TILECACHESET_MAGIC = 'T'<<24 | 'S'<<16 | 'E'<<8 | 'T'; //'TSET';
static const int TILECACHESET_VERSION = 2;
struct TileCacheSetHeader
{
int magic;
int version;
int numTiles;
dtNavMeshParams meshParams;
dtTileCacheParams cacheParams;
rcConfig recastConfig;
};
struct TileCacheTileHeader
{
dtCompressedTileRef tileRef;
int dataSize;
};
};
#endif // OGREDETOURTILECACHE_H

View File

@@ -0,0 +1,950 @@
/*
OgreCrowd
---------
Copyright (c) 2012 Jonas Hauquier
Additional contributions by:
- mkultra333
- Paul Wilson
Sincere thanks and to:
- Mikko Mononen (developer of Recast navigation libraries)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#ifndef __OgreRecast_h_
#define __OgreRecast_h_
#include "OgreRecastDefinitions.h"
#include <Ogre.h>
#include "RecastInputGeom.h"
class OgreRecastNavmeshPruner;
/**
* Configuration parameters for recast navmesh building.
* A lot of the descripions of the parameters are not mine but come from the very
* useful CritterAI page (http://www.critterai.org/nmgen_config).
* For more detail and pictures, have a look there.
* Some other descriptions come from stevesp's doxygen API docs for recast itself
* (http://www.stevefsp.org/projects/rcndoc/prod/structrcConfig.html).
*
* Some settings are derived from easier to set parameters, those are denoted by
* the _ that follows their setter and getter. The easier to set parameters correspond
* to the settings available in the demo that comes with the recast library.
* You can overwrite those values by calling the setters preceeded with _.
* Otherwise it suffices to set a value for each setter without a preceding _.
**/
class OgreRecastConfigParams
{
public:
/**
* Initialize some default recast parameters
**/
OgreRecastConfigParams(void)
: cellSize(0.3),
cellHeight(0.2),
agentMaxSlope(20),
agentHeight(2.5),
agentMaxClimb(1),
agentRadius(0.5),
edgeMaxLen(12),
edgeMaxError(1.3),
regionMinSize(50),
regionMergeSize(20),
vertsPerPoly(DT_VERTS_PER_POLYGON), // (=6)
detailSampleDist(6),
detailSampleMaxError(1),
keepInterResults(false)
{ eval(); }
///////////////////////
// GUI SETTINGS:
///////////////////////
/*****************
* Rasterization
*****************/
/**
* @see{cellSize}
**/
inline void setCellSize(Ogre::Real cellSize) { this->cellSize = cellSize; eval(); }
/**
* @see{cellHeight}
**/
inline void setCellHeight(Ogre::Real cellHeight) { this->cellHeight = cellHeight; eval(); }
/*****************
* Agent
*****************/
/**
* @see{agentHeight}
**/
inline void setAgentHeight(Ogre::Real agentHeight) { this->agentHeight = agentHeight; eval(); }
/**
* @see{agentRadius}
**/
inline void setAgentRadius(Ogre::Real agentRadius) { this->agentRadius = agentRadius; eval(); }
/**
* @see{agentMaxClimb}
**/
inline void setAgentMaxClimb(Ogre::Real agentMaxClimb) { this->agentMaxClimb = agentMaxClimb; eval(); }
/**
* @see{agentMaxSlope}
**/
inline void setAgentMaxSlope(Ogre::Real agentMaxSlope) { this->agentMaxSlope = agentMaxSlope; }
/*****************
* Region
*****************/
/**
* @see{regionMinSize}
**/
inline void setRegionMinSize(Ogre::Real regionMinSize) { this->regionMinSize = regionMinSize; eval(); }
/**
* @see{regionMergeSize}
**/
inline void setRegionMergeSize(Ogre::Real regionMergeSize) { this->regionMergeSize = regionMergeSize; eval(); }
// TODO Add "monotone partitioning" option to call rcBuildRegionsMonotone in single navmesh building.
/*****************
* Polygonization
*****************/
/**
* @see{edgeMaxLen}
**/
inline void setEdgeMaxLen(Ogre::Real edgeMaxLength) { this->edgeMaxLen = edgeMaxLength; eval(); }
/**
* @see{edgeMaxError}
**/
inline void setEdgeMaxError(Ogre::Real edgeMaxError) { this->edgeMaxError = edgeMaxError;}
/**
* @see{vertsPerPoly}
**/
inline void setVertsPerPoly(int vertsPerPoly) { this->vertsPerPoly = vertsPerPoly; }
/*****************
* Detail mesh
*****************/
/**
* @see{detailSampleDist}
**/
inline void setDetailSampleDist(Ogre::Real detailSampleDist) { this->detailSampleDist = detailSampleDist; eval(); }
/**
* @see{detailSampleMaxError}
**/
inline void setDetailSampleMaxError(Ogre::Real detailSampleMaxError) { this->detailSampleMaxError = detailSampleMaxError; eval(); }
/**
* @see{keepInterResults}
**/
inline void setKeepInterResults(bool keepInterResults) { this->keepInterResults = keepInterResults; }
/**
* @see{_walkableHeight}
**/
inline void _setWalkableHeight(int walkableHeight) { this->_walkableHeight = walkableHeight; }
/**
* @see{_walkableClimb}
**/
inline void _setWalkableClimb(int walkableClimb) { this->_walkableClimb = walkableClimb; }
/**
* @see{_walkableRadius}
**/
inline void _setWalkableRadius(int walkableRadius) { this->_walkableRadius = walkableRadius; }
/**
* @see{_maxEdgeLen}
**/
inline void _setMaxEdgeLen(int maxEdgeLen) { this->_maxEdgeLen = maxEdgeLen; }
/**
* @see{_minRegionArea}
**/
inline void _setMinRegionArea(int minRegionArea) { this->_minRegionArea = minRegionArea; }
/**
* @see{_mergeRegionArea}
**/
inline void _setMergeRegionArea(int mergeRegionArea) { this->_mergeRegionArea = mergeRegionArea; }
/**
* @see{_detailSampleDist}
**/
inline void _setDetailSampleDist(Ogre::Real detailSampleDist) { this->_detailSampleDist = detailSampleDist; }
/**
* @see{_detailSampleMaxError}
**/
inline void _setDetailSampleMaxError(Ogre::Real detailSampleMaxError) { this->_detailSampleMaxError = detailSampleMaxError; }
/**
* @see{cellSize}
**/
inline Ogre::Real getCellSize(void) { return cellSize; }
/**
* @see{cellHeight}
**/
inline Ogre::Real getCellHeight(void) { return cellHeight; }
/**
* @see{agentMaxSlope}
**/
inline Ogre::Real getAgentMaxSlope(void) { return agentMaxSlope; }
/**
* @see{agentHeight}
**/
inline Ogre::Real getAgentHeight(void) { return agentHeight; }
/**
* @see{agentMaxClimb}
**/
inline Ogre::Real getAgentMaxClimb(void) { return agentMaxClimb; }
/**
* @see{agentRadius}
**/
inline Ogre::Real getAgentRadius(void) { return agentRadius; }
/**
* @see{edgeMaxLen}
**/
inline Ogre::Real getEdgeMaxLen(void) { return edgeMaxLen; }
/**
* @see{edgeMaxError}
**/
inline Ogre::Real getEdgeMaxError(void) { return edgeMaxError; }
/**
* @see{regionMinSize}
**/
inline Ogre::Real getRegionMinSize(void) { return regionMinSize; }
/**
* @see{regionMergeSize}
**/
inline Ogre::Real getRegionMergeSize(void) { return regionMergeSize; }
/**
* @see{vertsPerPoly}
**/
inline int getVertsPerPoly(void) { return vertsPerPoly; }
/**
* @see{detailSampleDist}
**/
inline Ogre::Real getDetailSampleDist(void) { return detailSampleDist; }
/**
* @see{detailSampleMaxError}
**/
inline Ogre::Real getDetailSampleMaxError(void) { return detailSampleMaxError; }
/**
* @see{keepInterResults}
**/
inline bool getKeepInterResults(void) { return keepInterResults; }
/**
* @see{_walkableHeight}
**/
inline int _getWalkableheight(void) { return _walkableHeight; }
/**
* @see{_walkableClimb}
**/
inline int _getWalkableClimb(void) { return _walkableClimb; }
/**
* @see{_walkableRadius}
**/
inline int _getWalkableRadius(void) { return _walkableRadius; }
/**
* @see{_maxEdgeLen}
**/
inline int _getMaxEdgeLen(void) { return _maxEdgeLen; }
/**
* @see{_minRegionArea}
**/
inline int _getMinRegionArea(void) { return _minRegionArea; }
/**
* @see{_mergeRegionArea}
**/
inline int _getMergeRegionArea(void) { return _mergeRegionArea; }
/**
* @see{_detailSampleDist}
**/
inline int _getDetailSampleDist(void) { return (int)_detailSampleDist; }
/**
* @see{detailSampleMaxError}
**/
inline int _getDetailSampleMaxError(void) { return (int)_detailSampleMaxError; }
private:
/**
* Derive non-directly set parameters
* This is the default behaviour and these parameters can be overridden using
* _ setters.
**/
inline void eval(void) {
_walkableHeight = (int)ceilf(agentHeight / cellHeight);
_walkableClimb = (int)floorf(agentMaxClimb / cellHeight);
_walkableRadius = (int)ceilf(agentRadius / cellSize);
_maxEdgeLen = (int)(edgeMaxLen / cellSize);
_minRegionArea = (int)rcSqr(regionMinSize); // Note: area = size*size
_mergeRegionArea = (int)rcSqr(regionMergeSize); // Note: area = size*size
_detailSampleDist = detailSampleDist < 0.9f ? 0 : cellSize * detailSampleDist;
_detailSampleMaxError = cellHeight * detailSampleMaxError;
}
/**
* Cellsize (cs) is the width and depth resolution used when sampling the source geometry.
* The width and depth of the cell columns that make up voxel fields.
* Cells are laid out on the width/depth plane of voxel fields. Width is associated with the x-axis of the source geometry. Depth is associated with the z-axis.
* A lower value allows for the generated meshes to more closely match the source geometry, but at a higher processing and memory cost.
*
* The xz-plane cell size to use for fields. [Limit: > 0] [Units: wu].
* cs and ch define voxel/grid/cell size. So their values have significant side effects on all parameters defined in voxel units.
* The minimum value for this parameter depends on the platform's floating point accuracy, with the practical minimum usually around 0.05.
**/
Ogre::Real cellSize;
/**
* Cellheight (ch) is the height resolution used when sampling the source geometry. The height of the voxels in voxel fields.
* Height is associated with the y-axis of the source geometry.
* A smaller value allows for the final meshes to more closely match the source geometry at a potentially higher processing cost.
* (Unlike cellSize, using a lower value for cellHeight does not significantly increase memory use.)
*
* The y-axis cell size to use for fields. [Limit: > 0] [Units: wu].
* cs and ch define voxel/grid/cell size. So their values have significant side effects on all parameters defined in voxel units.
* The minimum value for this parameter depends on the platform's floating point accuracy, with the practical minimum usually around 0.05.
*
* Setting ch lower will result in more accurate detection of areas the agent can still pass under, as min walkable height is discretisized
* in number of cells. Also walkableClimb's precision is affected by ch in the same way, along with some other parameters.
**/
Ogre::Real cellHeight;
/**
* The maximum slope that is considered traversable (in degrees).
* [Limits: 0 <= value < 90]
* The practical upper limit for this parameter is usually around 85 degrees.
*
* Also called maxTraversableSlope
**/
Ogre::Real agentMaxSlope;
/**
* The height of an agent. Defines the minimum height that
* agents can walk under. Parts of the navmesh with lower ceilings
* will be pruned off.
*
* This parameter serves at setting walkableHeight (minTraversableHeight) parameter, precision of this parameter is determined by cellHeight (ch).
**/
Ogre::Real agentHeight;
/**
* The Maximum ledge height that is considered to still be traversable.
* This parameter serves at setting walkableClimb (maxTraversableStep) parameter, precision of this parameter is determined by cellHeight (ch).
* [Limit: >=0]
* Allows the mesh to flow over low lying obstructions such as curbs and up/down stairways. The value is usually set to how far up/down an agent can step.
**/
Ogre::Real agentMaxClimb;
/**
* The radius on the xz (ground) plane of the circle that describes the agent (character) size.
* Serves at setting walkableRadius (traversableAreaBorderSize) parameter, the precision of walkableRadius is affected by cellSize (cs).
*
* This parameter is also used by DetourCrowd to determine the area other agents have to avoid in order not to collide with an agent.
* The distance to erode/shrink the walkable area of the heightfield away from obstructions.
* [Limit: >=0]
*
* In general, this is the closest any part of the final mesh should get to an obstruction in the source geometry. It is usually set to the maximum agent radius.
* While a value of zero is legal, it is not recommended and can result in odd edge case issues.
*
**/
Ogre::Real agentRadius;
/**
* The maximum allowed length for contour edges along the border of the mesh.
* [Limit: >=0]
* Extra vertices will be inserted as needed to keep contour edges below this length. A value of zero effectively disables this feature.
* Serves at setting maxEdgeLen, the precision of maxEdgeLen is affected by cellSize (cs).
**/
Ogre::Real edgeMaxLen;
/**
* The maximum distance a simplfied contour's border edges should deviate the original raw contour. (edge matching)
* [Limit: >=0] [Units: wu]
* The effect of this parameter only applies to the xz-plane.
*
* Also called maxSimplificationError or edgeMaxDeviation
* The maximum distance the edges of meshes may deviate from the source geometry.
* A lower value will result in mesh edges following the xz-plane geometry contour more accurately at the expense of an increased triangle count.
* A value to zero is not recommended since it can result in a large increase in the number of polygons in the final meshes at a high processing cost.
**/
Ogre::Real edgeMaxError;
/**
* The minimum number of cells allowed to form isolated island areas (size).
* [Limit: >=0]
* Any regions that are smaller than this area will be marked as unwalkable. This is useful in removing useless regions that can sometimes form on geometry such as table tops, box tops, etc.
* Serves at setting minRegionArea, which will be set to the square of this value (the regions are square, thus area=size*size)
**/
Ogre::Real regionMinSize;
/**
* Any regions with a span count smaller than this value will, if possible, be merged with larger regions.
* [Limit: >=0] [Units: vx]
* Serves at setting MergeRegionArea, which will be set to the square of this value (the regions are square, thus area=size*size)
**/
Ogre::Real regionMergeSize;
/**
* The maximum number of vertices allowed for polygons generated during the contour to polygon conversion process.
* [Limit: >= 3]
* If the mesh data is to be used to construct a Detour navigation mesh, then the upper limit is limited to <= DT_VERTS_PER_POLYGON (=6).
*
* Also called maxVertsPerPoly
* The maximum number of vertices per polygon for polygons generated during the voxel to polygon conversion process.
* Higher values increase processing cost, but can also result in better formed polygons in the final meshes. A value of around 6 is generally adequate with diminishing returns for higher values.
**/
int vertsPerPoly;
/**
* Sets the sampling distance to use when generating the detail mesh.
* (For height detail only.) [Limits: 0 or >= 0.9] [Units: wu]
*
* Also called contourSampleDistance
* Sets the sampling distance to use when matching the detail mesh to the surface of the original geometry.
* Impacts how well the final detail mesh conforms to the surface contour of the original geometry. Higher values result in a detail mesh which conforms more closely to the original geometry's surface at the cost of a higher final triangle count and higher processing cost.
* Setting this argument to less than 0.9 disables this functionality.
**/
Ogre::Real detailSampleDist;
/**
* The maximum distance the detail mesh surface should deviate from heightfield data.
* (For height detail only.) [Limit: >=0] [Units: wu]
*
* Also called contourMaxDeviation
* The maximum distance the surface of the detail mesh may deviate from the surface of the original geometry.
* The accuracy is impacted by contourSampleDistance.
* The value of this parameter has no meaning if contourSampleDistance is set to zero.
* Setting the value to zero is not recommended since it can result in a large increase in the number of triangles in the final detail mesh at a high processing cost.
* Stronly related to detailSampleDist (contourSampleDistance).
**/
Ogre::Real detailSampleMaxError;
/**
* Determines whether intermediary results are stored in OgreRecast class or whether they are removed after navmesh creation.
**/
bool keepInterResults;
/**
* Minimum height in number of (voxel) cells that the ceiling needs to be
* for an agent to be able to walk under. Related to cellHeight (ch) and
* agentHeight.
*
* Minimum floor to 'ceiling' height that will still allow the floor area to be considered walkable.
* [Limit: >= 3] [Units: vx]
* Permits detection of overhangs in the source geometry that make the geometry below un-walkable. The value is usually set to the maximum agent height.
*
* Also called minTraversableHeight
* This value should be at least two times the value of cellHeight in order to get good results.
**/
int _walkableHeight;
/**
* Maximum ledge height that is considered to still be traversable, in number of cells (height).
* [Limit: >=0] [Units: vx].
* Allows the mesh to flow over low lying obstructions such as curbs and up/down stairways. The value is usually set to how far up/down an agent can step.
*
* Also called maxTraversableStep
* Represents the maximum ledge height that is considered to still be traversable.
* Prevents minor deviations in height from improperly showing as obstructions. Permits detection of stair-like structures, curbs, etc.
**/
int _walkableClimb;
/**
* The distance to erode/shrink the walkable area of the heightfield away from obstructions, in cellsize units.
* [Limit: >=0] [Units: vx]
* In general, this is the closest any part of the final mesh should get to an obstruction in the source geometry. It is usually set to the maximum agent radius.
* While a value of zero is legal, it is not recommended and can result in odd edge case issues.
*
* Also called traversableAreaBorderSize
* Represents the closest any part of a mesh can get to an obstruction in the source geometry.
* Usually this value is set to the maximum bounding radius of agents utilizing the meshes for navigation decisions.
*
* This value must be greater than the cellSize to have an effect.
* The actual border will be larger around ledges if ledge clipping is enabled. See the clipLedges parameter for more information.
* The actual border area will be larger if smoothingTreshold is > 0. See the smoothingThreshold parameter for more information.
**/
int _walkableRadius;
/**
* The maximum allowed length for contour edges along the border of the mesh.
* [Limit: >=0] [Units: vx].
* Extra vertices will be inserted as needed to keep contour edges below this length. A value of zero effectively disables this feature.
*
* Also called maxEdgeLength
* The maximum length of polygon edges that represent the border of meshes.
* More vertices will be added to border edges if this value is exceeded for a particular edge.
* In certain cases this will reduce the number of long thin triangles.
* A value of zero will disable this feature.
**/
int _maxEdgeLen;
/**
* The minimum number of cells allowed to form isolated island areas.
* [Limit: >=0] [Units: vx].
* Any regions that are smaller than this area will be marked as unwalkable.
* This is useful in removing useless regions that can sometimes form on geometry such as table tops, box tops, etc.
*
* Also called minUnconnectedRegionSize
* The minimum region size for unconnected (island) regions.
* The value is in voxels.
* Regions that are not connected to any other region and are smaller than this size will be culled before mesh generation. I.e. They will no longer be considered traversable.
**/
int _minRegionArea;
/**
* Any regions with a span count smaller than this value will, if possible, be merged with larger regions.
* [Limit: >=0] [Units: vx]
*
* Also called mergeRegionSize or mergeRegionArea
* Any regions smaller than this size will, if possible, be merged with larger regions.
* Value is in voxels.
* Helps reduce the number of small regions. This is especially an issue in diagonal path regions where inherent faults in the region generation algorithm can result in unnecessarily small regions.
* Small regions are left unchanged if they cannot be legally merged with a neighbor region. (E.g. Merging will result in a non-simple polygon.)
**/
int _mergeRegionArea;
/**
* Sets the sampling distance to use when generating the detail mesh.
* (For height detail only.) [Limits: 0 or >= 0.9] [Units: wu]
*
* Also called contourSampleDistance
* Sets the sampling distance to use when matching the detail mesh to the surface of the original geometry.
* Impacts how well the final detail mesh conforms to the surface contour of the original geometry. Higher values result in a
* detail mesh which conforms more closely to the original geometry's surface at the cost of a higher final triangle count and higher processing cost.
* Setting this argument to less than 0.9 disables this functionality.
*
* The difference between this parameter and edge matching (edgeMaxError) is that this parameter operates on the height rather than the xz-plane.
* It also matches the entire detail mesh surface to the contour of the original geometry. Edge matching only matches edges of meshes to the contour of the original geometry.
**/
Ogre::Real _detailSampleDist;
/**
* The maximum distance the detail mesh surface should deviate from heightfield data.
* (For height detail only.) [Limit: >=0] [Units: wu]
*
* Also called contourMaxDeviation
* The maximum distance the surface of the detail mesh may deviate from the surface of the original geometry.
* The accuracy is impacted by contourSampleDistance (detailSampleDist).
* The value of this parameter has no meaning if contourSampleDistance is set to zero.
* Setting the value to zero is not recommended since it can result in a large increase in the number of triangles in the final detail mesh at a high processing cost.
* This parameter has no impact if contourSampleDistance is set to zero.
**/
Ogre::Real _detailSampleMaxError;
};
// Advance declarations, needed for expressing friendship relation
class OgreDetourTileCache;
class OgreDetourCrowd;
/**
* This class serves as a wrapper between Ogre and Recast/Detour
* It's not a full wrapper, but instead offers the main features needed
* to integrate the Ogre demo with Recast and Detour.
**/
class OgreRecast
{
public:
/**
* Use static geometry for debug drawing the navmesh.
* This is only useful when drawing tiled navmeshes (detourTileCache) and when there are
* a lot of tiles, otherwise this will result in a too high batchcount.
**/
static bool STATIC_GEOM_DEBUG;
/**
* Set to true to print verbose messages about debug drawing.
**/
static bool VERBOSE;
/**
* Initialize an OgreRecast module to generate single navmeshes or store tiled navmeshes generated by OgreDetourTileCache.
* You can specify custom parameters for navmesh building (which are also used by OgreDetourTileCache and some by DetourCrowd).
* Not specifying parameters will result in defaults being used.
**/
OgreRecast(Ogre::SceneManager* sceneMgr, OgreRecastConfigParams configParams = OgreRecastConfigParams());
/**
* Should be called every frame. Used for updating navmesh debug drawing when using static geometry
* (STATIC_GEOM_DEBUG).
**/
void update(void);
/**
* The agent radius for which this navmesh is built.
**/
float getAgentRadius(void);
/**
* The agent height for which this navmesh is built.
**/
float getAgentHeight(void);
/**
* The amount with which the drawn debug path is offset from the ground
**/
float getPathOffsetFromGround(void);
/**
* The amount with which the drawn debug navmesh polys are offset from the ground.
**/
float getNavmeshOffsetFromGround(void);
/**
* Size of each cell (the length of one dimension on the x-y plane) in world
* units.
**/
float getCellSize(void) { return m_cellSize; }
/**
* Height in world units of one navmesh cell.
**/
float getCellHeight(void) { return m_cellHeight; }
/**
* Cleanup recast parameters and variables.
* This does not clean up the objects related to debug drawing.
**/
void RecastCleanup();
/**
* Configure navbuild parameters for this module.
* Sets m_cfg and other parameters.
**/
void configure(OgreRecastConfigParams params);
/**
* Build a navigation mesh from the specified list of Ogre::Entities as source.
* It is required that all supplied entities are attached to a scenenode in the scene
* before calling this method.
*
* Recast will construct a navmesh using some configuration parameters, which are currently
* just set inside this method, but should be extracted to somewhere else in the future.
* The most important parameters to set are cellsize, agentHeight and agentRadius.
**/
bool NavMeshBuild(std::vector<Ogre::Entity*> srcMeshesA);
/**
* Build a navmesh from the specified input geometry.
* @see{OgreRecast::NavMeshBuild(std::vector<Ogre::Entity*>)}
**/
bool NavMeshBuild(InputGeom* input);
/**
* Find a path beween start point and end point and, if possible, generates a list of lines in a path.
* It might fail if the start or end points aren't near any navmesh polygons, or if the path is too long,
* or it can't make a path, or various other reasons.
*
* nPathSlot: The index number for the slot in which the found path is to be stored.
* nTarget: Number identifying the target the path leads to. Recast does nothing with this, but you can give them
* meaning in your own application.
*
* Return codes:
* 0 found path
* -1 Couldn't find polygon nearest to start point
* -2 Couldn't find polygon nearest to end point
* -3 Couldn't create a path
* -4 Couldn't find a path
* -5 Couldn't create a straight path
* -6 Couldn't find a straight path
**/
int FindPath(float* pStartPos, float* pEndPos, int nPathSlot, int nTarget);
/**
* Find a path between start and end position, works with Ogre::Vector3 points.
* @see{OgreRecast::FindPath(float*, float*, int, int)}
**/
int FindPath(Ogre::Vector3 startPos, Ogre::Vector3 endPos, int nPathSlot, int nTarget);
/**
* Retrieve the path at specified slot defined as a line along an ordered set of 3D positions.
* The path has a maximum length of MAX_PATHVERT, and is an empty list in case no path is
* defined or an invalid pathSlot index is given.
**/
std::vector<Ogre::Vector3> getPath(int pathSlot);
/**
* The ID number identifying the target for the path at specified slot. Targets have
* no meaning for OgreRecast but you can use them to give them their own meanings.
* Returns 0 when a faulty pathSlot is given.
**/
int getTarget(int pathSlot);
/**
* Draw the nav mesh for debug purposes. The navmesh is converted to an Ogre::Mesh and
* put inside the scene for rendering.
**/
void drawNavMesh(void);
/**
* Calculate visual Ogre meshes to visualize the recast navigation mesh for debugging.
*
* Convert the calculated navmesh into an Ogre::ManualObject mesh, and put it in the scene.
* A scenenode with name "RecastSN" will be created in the root of the scenegraph. This
* scenenode is referenced by the m_pRecastSN member variable.
*
* Within this scenenode a mesh with name "RecastMOWalk" will be added, which is stored in
* member variable m_pRecastMOWalk. This mesh will represents the faces of the segments in
* the navmesh. Not the lines indicating the edges of the navmesh.
*
* The manual object referenced by member variable m_pRecastMONeighbour and with name
* "RecastMONeighbour" is also added to the same scenenode. This object contains the lines
* between neighbouring segments of the navmesh. These are all except the outer edges of
* the navmesh.
*
* Finally, the manual object referenced by member variable m_pRecastMOBoundary and with name
* "RecastMOBoundary" is added to the scene node. It is a collection of lines that represent
* the outer edges of the navmesh, being the ones that do not have any neighbouring segments.
**/
void CreateRecastPolyMesh(const Ogre::String name, const unsigned short *verts, const int nverts,
const unsigned short *polys, const int npolys, const unsigned char *areas,
const int maxpolys, const unsigned short *regions, const int nvp,
const float cs, const float ch, const float *orig, bool colorRegions=true);
/**
* Remove debug drawn navmesh for navmesh tile with specified (compressed detourtile) reference.
**/
void removeDrawnNavmesh(unsigned int tileRef);
/**
* Create an Ogre::ManualObject mesh to visually debug a path on the navmesh found
* using detour. The path stored in the specified slot number is visualized, and the
* result is stored under the m_pRecastMOPath member variable and has the name "RecastMOPath".
**/
void CreateRecastPathLine(int nPathSlot);
/**
* Returns the poly filter that will be used for all (random) point and nearest poly searches,
* as well as for pathfinding.
**/
dtQueryFilter getFilter(void);
/**
* Set the poly filter that will be used for all (random) point and nearest poly searches,
* as well as for pathfinding.
**/
void setFilter(const dtQueryFilter filter);
/**
* Get the offset size (box) around points used to look for nav polygons.
* This offset is used in all search for points on the navmesh.
* The maximum offset that a specified point can be off from the navmesh.
**/
Ogre::Vector3 getPointExtents(void);
/**
* Set the offset size (box) around points used to look for nav polygons.
* This offset is used in all search for points on the navmesh.
* The maximum offset that a specified point can be off from the navmesh.
**/
void setPointExtents(Ogre::Vector3 extents);
/**
* Find a point on the navmesh closest to the specified point position, within predefined
* bounds.
* Returns true if such a point is found (returned as resultPt), returns false
* if no point is found. When false is returned, resultPt is not altered.
**/
bool findNearestPointOnNavmesh(Ogre::Vector3 position, Ogre::Vector3 &resultPt);
bool findNearestPolyOnNavmesh(Ogre::Vector3 position, Ogre::Vector3 &resultPt, dtPolyRef &resultPoly);
/**
* Returns a random point on the navmesh.
**/
Ogre::Vector3 getRandomNavMeshPoint();
/**
* Returns a random point on the navmesh that is within the circle of specified radius
* and center. Will return a random point within each navmesh polygon that touches the
* circle (so the exact point could in fact be a little outside of the specified circle).
* Returns center when no random point can be found (eg. center is too far from a navmesh
* poly).
**/
Ogre::Vector3 getRandomNavMeshPointInCircle(Ogre::Vector3 center, Ogre::Real radius);
/**
* Convenience function for converting between Ogre::Vector3
* and float* used by recast.
**/
static void OgreVect3ToFloatA(const Ogre::Vector3 vect, float* result);
/**
* Convenience function for converting between float* used by recast
* and Ogre::Vector3.
**/
static void FloatAToOgreVect3(const float* vect, Ogre::Vector3 &result);
/**
* Translate error code of detour findPath into a readable explanation.
**/
Ogre::String getPathFindErrorMsg(int errorCode);
/**
* The configuration of the recast navmesh.
**/
rcConfig getConfig(void);
OgreRecastNavmeshPruner* getNavmeshPruner(void);
// helper debug drawing stuff
Ogre::ManualObject* m_pRecastMOWalk ;
Ogre::ManualObject* m_pRecastMONeighbour ;
Ogre::ManualObject* m_pRecastMOBoundary ;
Ogre::ManualObject* m_pRecastMOPath ;
Ogre::SceneNode* m_pRecastSN ;
Ogre::SceneManager* m_pSceneMgr;
protected:
/**
* Draw the specified recast poly mesh to scene for debugging.
**/
void drawPolyMesh(const struct rcPolyMesh &mesh, bool colorRegions=true);
unsigned char* m_triareas;
rcHeightfield* m_solid;
rcCompactHeightfield* m_chf;
rcContourSet* m_cset;
rcPolyMesh* m_pmesh;
rcConfig m_cfg;
rcPolyMeshDetail* m_dmesh;
class InputGeom* m_geom;
class dtNavMesh* m_navMesh;
class dtNavMeshQuery* m_navQuery;
unsigned char m_navMeshDrawFlags;
rcContext* m_ctx;
float m_cellSize;
float m_cellHeight;
float m_agentHeight;
float m_agentRadius;
float m_agentMaxClimb;
float m_agentMaxSlope;
float m_regionMinSize;
float m_regionMergeSize;
float m_edgeMaxLen;
float m_edgeMaxError;
int m_vertsPerPoly;
float m_detailSampleDist;
float m_detailSampleMaxError;
bool m_keepInterResults ;
// Off-Mesh connections. Not used yet.
static const int MAX_OFFMESH_CONNECTIONS = 256;
float m_offMeshConVerts[MAX_OFFMESH_CONNECTIONS*3*2];
float m_offMeshConRads[MAX_OFFMESH_CONNECTIONS];
unsigned char m_offMeshConDirs[MAX_OFFMESH_CONNECTIONS];
unsigned char m_offMeshConAreas[MAX_OFFMESH_CONNECTIONS];
unsigned short m_offMeshConFlags[MAX_OFFMESH_CONNECTIONS];
unsigned int m_offMeshConId[MAX_OFFMESH_CONNECTIONS];
int m_offMeshConCount;
// helper debug drawing stuff
int m_nAreaCount ;
Ogre::StaticGeometry *m_sg;
bool m_rebuildSg;
float m_flTestStart[3] ;
float m_flTestEnd[3] ;
float *m_normals;
int m_flDataX;
int m_flDataY;
/**
* Offset that the debug path is drawn from the ground.
**/
float m_pathOffsetFromGround;
/**
* Offset that the debug navmesh is drawn from the ground.
**/
float m_navMeshOffsetFromGround;
/**
* Offset that the navmesh edges are drawn from the ground.
**/
float m_navMeshEdgesOffsetFromGround;
/**
* Colours used for the various debug drawing objects.
**/
Ogre::ColourValue m_navmeshNeighbourEdgeCol;
Ogre::ColourValue m_navmeshOuterEdgeCol;
Ogre::ColourValue m_navmeshGroundPolygonCol;
Ogre::ColourValue m_navmeshOtherPolygonCol;
Ogre::ColourValue m_pathCol;
/**
* Stores all created paths
**/
PATHDATA m_PathStore[MAX_PATHSLOT];
/**
* The poly filter that will be used for all (random) point and nearest poly searches.
**/
dtQueryFilter *mFilter;
/**
* The offset size (box) around points used to look for nav polygons.
* This offset is used in all search for points on the navmesh.
* The maximum offset that a specified point can be off from the navmesh.
**/
float mExtents[3];
Ogre::LogManager* m_pLog;
OgreRecastNavmeshPruner *mNavmeshPruner;
private:
/**
* Retrieve the vertices from a manual object, even if they are not referenced by faces.
* Does not retrieve faces, as it is intended to retrieve line drawings.
**/
std::vector<Ogre::Vector3> getManualObjectVertices(Ogre::ManualObject *man);
// Friend OgreDetourTileCache so it can access the navmesh of this component
friend class OgreDetourTileCache;
// Friend OgreDetourCrowd so it can access the navmesh of this component
friend class OgreDetourCrowd;
};
#endif // #ifndef __OgreRecast_h_

View File

@@ -0,0 +1,91 @@
/*
OgreCrowd
---------
Copyright (c) 2012 Jonas Hauquier
Additional contributions by:
- mkultra333
- Paul Wilson
Sincere thanks and to:
- Mikko Mononen (developer of Recast navigation libraries)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#ifndef __OgreRecastDefinitions_h_
#define __OgreRecastDefinitions_h_
/**
* This file sets up all definitions needed by Recast/Detour.
* Most of it is just taken from the official demo application.
**/
// recast/detour stuff
#include <recastnavigation/Recast.h>
#include <recastnavigation/DetourNavMesh.h>
#include <recastnavigation/DetourNavMeshBuilder.h>
#include <recastnavigation/DetourNavMeshQuery.h>
#define MAX_PATHSLOT 128 // how many paths we can store
#define MAX_PATHPOLY 256 // max number of polygons in a path
#define MAX_PATHVERT 512 // most verts in a path
// structure for storing output straight line paths
typedef struct
{
float PosX[MAX_PATHVERT] ;
float PosY[MAX_PATHVERT] ;
float PosZ[MAX_PATHVERT] ;
int MaxVertex ;
int Target ;
}
PATHDATA ;
// These are just sample areas to use consistent values across the samples.
// The use should specify these base on his needs.
// bzn most aren't used yet, just SAMPLE_POLYAREA_GROUND and SAMPLE_POLYFLAGS_WALK
enum SamplePolyAreas
{
SAMPLE_POLYAREA_GROUND,
SAMPLE_POLYAREA_WATER,
SAMPLE_POLYAREA_ROAD,
SAMPLE_POLYAREA_DOOR,
SAMPLE_POLYAREA_GRASS,
SAMPLE_POLYAREA_JUMP,
};
enum SamplePolyFlags
{
SAMPLE_POLYFLAGS_WALK = 0x01, // Ability to walk (ground, grass, road)
SAMPLE_POLYFLAGS_SWIM = 0x02, // Ability to swim (water).
SAMPLE_POLYFLAGS_DOOR = 0x04, // Ability to move through doors.
SAMPLE_POLYFLAGS_JUMP = 0x08, // Ability to jump.
SAMPLE_POLYFLAGS_DISABLED = 0x10, // Disabled polygon
SAMPLE_POLYFLAGS_ALL = 0xffff // All abilities.
};
#endif // #ifndef __OgreRecastDefinitions_h_

View File

@@ -0,0 +1,69 @@
/*
OgreCrowd
---------
Copyright (c) 2012 Jonas Hauquier
Additional contributions by:
- mkultra333
- Paul Wilson
Sincere thanks and to:
- Mikko Mononen (developer of Recast navigation libraries)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#ifndef OGRERECASTNAVMESHPRUNER_H
#define OGRERECASTNAVMESHPRUNER_H
#include "OgreRecast.h"
/**
* Tool to flood-fill the navmesh polygons starting at a specific polygon and marking
* all reachable neighbours. This allows to prune off unreachable parts of the navmesh.
*
* Based on NavMeshPruneTool from the original recast sample.
* The navmesh prune tool allows to disable unreachable polygons. There is currently no
* way to discard the disabled polys, because the search is done on the final navmesh
* data (when it is already rasterized) and it is really hard to modify.
**/
class OgreRecastNavmeshPruner
{
public:
OgreRecastNavmeshPruner(OgreRecast *recast, dtNavMesh *navMesh);
bool floodNavmesh(Ogre::Vector3 startPoint);
void clearSelection(void);
void pruneSelected(void);
protected:
void floodNavmesh(dtPolyRef start, unsigned char flag);
void disableUnvisitedPolys(void);
OgreRecast *mRecast;
dtNavMesh *mNavmesh;
class NavmeshFlags* mFlags;
};
#endif // OGRERECASTNAVMESHPRUNER_H

View File

@@ -0,0 +1,146 @@
/*
OgreCrowd
---------
Copyright (c) 2012 Jonas Hauquier
Additional contributions by:
- mkultra333
- Paul Wilson
Sincere thanks and to:
- Mikko Mononen (developer of Recast navigation libraries)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#ifndef RECASTCONVEXHULL_H
#define RECASTCONVEXHULL_H
#include <Ogre.h>
class InputGeom; // Advance declaration
/**
* The maximum amount of points that a convex hull
* used for dynamic obstacles on a navmesh can consist of.
* For performance reasons this cannot be too high.
* But has to have at least twice the number of vertices that
* the input has!!!
**/
static const int MAX_CONVEXVOL_PTS = 90; // TODO increase? or find better convex hull algorithm
/**
* Volume describing a convex hull around geometry. Or a convex area to mark on the navmesh.
* Can be used as a collision mesh for dynamic obstacles.
*
* Can also be used for marking areas of navmesh polygons with a specific flag.
* For example, mark a convex area with area set to RC_NULL_AREA to make the area unwalkable (obstacle).
* Area marking can also be used for marking more specific areas, such as swim (water),
* mud, door, ...
* Areas can assign weights to navmesh polygons that can be used during A* pathfinding
* for determining the optimal path.
*
* Note that convex volume marking is not the only way of assigning flags to navmesh polygons.
* Navmesh polygons can also be assigned a flag directly, the problem is that with automatic
* navmesh generation you have no control over the exact size and position of each polygon on the
* navmesh, making it sometimes impossible to mark an intended area precisely. That's why recast
* offers a rcMarkConvexArea (and in the future hopefully dtMarkConvexArea) to allow you to
* explicitly mark where a separate polygon should be in the navmesh, so that you can assign it
* a custom area flag. Convex area marking is also referred to as cookie cutter algorithm.
*
* If you define a custom constructor for convexVolume objects, note that recast requires the
* vertices of convex shapes to be wound in clockwise order (this is very important, or convex
* area marking will not work)!
*
* Also note that recast requires convex volumes to be offset with the agent radius so the agent
* does not collide with the edges.
**/
class ConvexVolume
{
public:
/**
* Create a convex hull in 2D space (on the xz plane) from
* the specified 3D points.
**/
ConvexVolume(InputGeom *geom, float offset = 0.0f);
/**
* Create a convex hull from a bounding box
**/
ConvexVolume(Ogre::AxisAlignedBox boundingBox, float offset = 0.0f);
/**
* Move this convex hull to a new world position offset with specified
* translation vector.
* Can be done pretty fast. Due to the 2D nature of this convex hull it's
* not possible to apply an arbitrary rotation, however (though in theory
* rotations around Y axis would work).
**/
void move(Ogre::Vector3 translate);
/**
* The vertices of this convex hull.
* Vertices are stored as three subsequent values, in order x, y, z
* Size of this array is always a multiple of 3, exactly 3*nverts
**/
float verts[MAX_CONVEXVOL_PTS*3];
/**
* Number of vertices in verts.
* The size of the verts array is actually nverts*3
**/
int nverts;
/**
* Minimum and maximum height of this convex hull.
**/
float hmin, hmax;
/**
* Axis aligned boundig box minimum and maximum of this convex hull.
**/
float bmin[3], bmax[3];
/**
* Area flag for the navmesh polygon that will be marked with this convex volume.
* For example, set to RC_NULL_AREA to make the area unwalkable (obstacle).
* Area marking can also be used for marking more specific areas, such as swim (water),
* mud, door, ...
* Areas can assign weights to navmesh polygons that can be used during A* pathfinding
* for determining the optimal path.
**/
int area;
private:
/**
* Compare two points. Returns true if they are equal.
**/
static inline bool cmppt(const float* a, const float* b);
/**
* isLeftOf comparison. Returns true if point c is left of line a-b.
**/
static inline bool left(const float* a, const float* b, const float* c);
};
#endif // RECASTCONVEXHULL_H

View File

@@ -0,0 +1,477 @@
/*
OgreCrowd
---------
Copyright (c) 2012 Jonas Hauquier
Additional contributions by:
- mkultra333
- Paul Wilson
Sincere thanks and to:
- Mikko Mononen (developer of Recast navigation libraries)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#ifndef RECASTINPUTGEOM_H
#define RECASTINPUTGEOM_H
#include <Ogre.h>
#include <Terrain/OgreTerrain.h>
#include <Terrain/OgreTerrainGroup.h>
#include "RecastConvexHull.h"
/**
* One node or chunk of the chunky tri mesh.
* Contains a 2D xz plane bounding box.
* n is the number of tris contained in this chunk.
* i is the starting index of the tris contained in this chunk.
* The actual tris are in the chunkyMesh object itself, in linear
* order per node, so that each node only needs the begin position
* and tri count to reference its tris.
**/
struct rcChunkyTriMeshNode
{
float bmin[2], bmax[2];
int i, n;
};
/**
* Spatial subdivision structure that structures triangles
* in axis-aligned boxes of a fixed size.
* This allows to quickly retrieve the triangles in a specific box,
* at the cost of a small pre-process step and extra memory usage.
**/
struct rcChunkyTriMesh
{
inline rcChunkyTriMesh() : nodes(0), tris(0) {};
inline ~rcChunkyTriMesh() { if(nodes) delete [] nodes; if(tris) delete [] tris; }
rcChunkyTriMeshNode* nodes;
int nnodes;
int* tris;
int ntris;
int maxTrisPerChunk;
};
/// Creates partitioned triangle mesh (AABB tree),
/// where each node contains at max trisPerChunk triangles.
bool rcCreateChunkyTriMesh(const float* verts, const int* tris, int ntris,
int trisPerChunk, rcChunkyTriMesh* cm);
/// Returns the chunk indices which overlap the input rectable.
int rcGetChunksOverlappingRect(const rcChunkyTriMesh* cm, float bmin[2], float bmax[2], int* ids, const int maxIds);
/// Returns the chunk indices which overlap the input segment.
int rcGetChunksOverlappingSegment(const rcChunkyTriMesh* cm, float p[2], float q[2], int* ids, const int maxIds);
/**
* Helper class to manage input geometry used as input for recast
* to build a navmesh.
* Adds extra features such as creating off-mesh connections between
* points in the geometry, raycasting to polygon level and bounding box
* intersection tests.
* It also allows to add or remove temporary obstacles to the geometry and
* add convex shapes as extra obstacles.
*
* This class handles the conversion of Ogre::Entities to recast compatible
* input format.
**/
class InputGeom
{
public:
/**
* Create recast compatible inputgeom from the specified entities. The entities have to be added to the
* scene before this call, as we need to calculate the world coordinates of the entity.
* Vertices and faces of the specified source entities are stored in this inputGeom, individual entity
* grouping and origin points are lost.
**/
InputGeom(std::vector<Ogre::Entity*> srcMeshes);
/**
* @see{InputGeom(std::vector<Ogre::Entity*>)}
* The same, only for a single entity.
**/
InputGeom(Ogre::Entity* srcMesh);
/**
* Construct inputGeom from the specified entities, only geometry that falls within the specified bounds
* is stored. Note: this is not done optimally, only bounding box intersections are used. But tile building
* is further optimized due to the use of the chunky tri mesh structure that is built within this inputGeom.
* Further this constructor is the same as @see{InputGeom(std::vector<Ogre::Entity*>)}
* Make sure to adapt your tileBounds fall together with the tilecache bounds so that they cover exactly the
* tiles you want to rebuild!! Don't call this with an arbitrary bounding box!
* Use OgreDetourTileCache::getTileAlignedBox() instead.
**/
InputGeom(std::vector<Ogre::Entity*> srcMeshes, const Ogre::AxisAlignedBox &tileBounds);
/**
* Construct inputGeom from terrain geometry and specified entities.
* The entity part is the same as @see{InputGeom(std::vector<Ogre::Entity*>)}
* Terrain geometry is added from highest LOD level of terrain.
**/
InputGeom(Ogre::TerrainGroup *terrainGroup, std::vector<Ogre::Entity*> srcMeshes = std::vector<Ogre::Entity*>());
/**
* Create inputGeom from terrain and entity polys that fall within specified bounding box, for rebuilding only tiles that fall within the box.
* For terrain, only the x-z bounding plane of the box is looked at (height is ignored), and only the necessary tris from the terrain are copied into inputGeom.
* For entities only simple bounding box tests happen for determining whether an entity should be added in its entirety to this inputGeom or not.
* Make sure to adapt your tileBounds fall together with the tilecache bounds so that they cover exactly the tiles you want to rebuild.
**/
InputGeom(const Ogre::AxisAlignedBox &tileBounds, Ogre::TerrainGroup *terrainGroup, std::vector<Ogre::Entity*> srcMeshes = std::vector<Ogre::Entity*>());
/**
* Output inputGeom to obj wavefront file.
* This can be used to test your inputGeom in the original recast demo (which loads .obj files).
**/
void writeObj(Ogre::String filename);
/**
* Retrieve a bounding box for the entire inputGeom, in world space
* coordinates.
**/
Ogre::AxisAlignedBox getBoundingBox(void);
/**
* Convenience funtion to calculate the bounding box of an entity in
* world coordinates.
* Entity needs to be added to the scene before calling this function.
**/
static Ogre::AxisAlignedBox getWorldSpaceBoundingBox(Ogre::MovableObject *ent);
/**
* Apply the specified rotation to all vertices of this geometry.
* Note that this does not affect the entities from which the inputGeom was
* originally built!
* Rotation will happen around specified pivot point (because inputGeom
* is in world-space coordinates and has no explicit reference to its
* origin or center point). Default value for pivot point is the world origin.
* Usually you specify the world position of the entity that this inputGeom was
* built from as pivot point (or the center of the bounding box in case of multiple
* entities).
*
* In this implementation, bounding boxes are not recalculated after a rotation (
* this is a little more difficult to do efficiently than it might seem).
* You cannot continuously rotate the same AABB because it will keep growing in size.
* You can either calculate a bounding box that fits the model in all possible rotations,
* or recalculate a bounding box from all verts (inefficient), or use a bounding box or
* convex hull from the physics engine to speed it up.
**/
void applyOrientation(Ogre::Quaternion orientation, Ogre::Vector3 pivot = Ogre::Vector3::ZERO);
/**
* Move all vertices of this inputGeom with the offset specified by the
* translation vector.
**/
void move(Ogre::Vector3 translate);
~InputGeom();
/**
* Retrieves the vertices stored within this inputGeom. The verts are an array of floats in which each
* subsequent three floats are in order the x, y and z coordinates of a vert. The size of this array is
* always a multiple of three and is exactly 3*getVertCount().
**/
float* getVerts(void);
/**
* The number of vertices stored in this inputGeom.
**/
int getVertCount(void);
/**
* Retrieves the tris stored in this inputGeom.
* A tri is defined by a sequence of three indexes which refer to an index position in the getVerts() array.
* Similar to getVerts, the size of this array is a multitude of 3 and is exactly 3*getTriCount().
**/
int* getTris(void);
/**
* The number of triangles stored in this inputGeom.
**/
int getTriCount(void);
/**
* Retrieve the normals calculated for this inputGeom. Note that the normals are not exact and are not meant for rendering,
* but they are good enough for navmesh calculation. Each normal corresponds to one vertex from getVerts() with the same index.
* The size of the normals array is 3*getVertCount().
**/
float* getNormals(void);
/**
* The axis aligned bounding box minimum of this input Geom.
**/
float* getMeshBoundsMin(void);
/**
* The axis aligned bounding box maximum of this input Geom.
**/
float* getMeshBoundsMax(void);
/**
* Determines whether this inputGeom has no geometry stored.
* Returns true if this inputGeom has no geometry.
**/
bool isEmpty(void);
/**
* Use this to verify whether the generated geometry in this inputGeom matches the geometry in your scene.
* Draws all inputGeom vertices as points in the scene.
**/
void debugMesh(Ogre::SceneManager *sceneMgr);
/**
* Maximum number of convex volume obstacles that can be added to this inputGeom.
**/
static const int MAX_VOLUMES = 256;
/**
* Retrieve vertex data from a mesh
* From http://www.ogre3d.org/tikiwiki/RetrieveVertexData
*
* This example is taken from monster's OgreODE project. The full source can be found under ogreaddons/ogreode in the Ogre SVN.
* It has been adopted, so that it can be used separately. Just copy/paste it into your own project.
*
* Note that this code assumes sizeof(long) == sizeof(uint32_t), which is not true on AMD64 Linux.
**/
static void getMeshInformation(const Ogre::MeshPtr mesh,
size_t &vertex_count,
Ogre::Vector3* &vertices,
size_t &index_count,
unsigned long* &indices,
const Ogre::Vector3 &position = Ogre::Vector3::ZERO,
const Ogre::Quaternion &orient = Ogre::Quaternion::IDENTITY,
const Ogre::Vector3 &scale = Ogre::Vector3::UNIT_SCALE);
/**
* getMeshInformation for manual meshes.
**/
static void getManualMeshInformation(const Ogre::ManualObject *manual,
size_t &vertex_count,
Ogre::Vector3* &vertices,
size_t &index_count,
unsigned long* &indices,
const Ogre::Vector3 &position = Ogre::Vector3::ZERO,
const Ogre::Quaternion &orient = Ogre::Quaternion::IDENTITY,
const Ogre::Vector3 &scale = Ogre::Vector3::UNIT_SCALE);
/**
* Debug function for drawing a convex hull as lines in the scene.
* Returns the manual object added to the scene.
* It's possible to specify a custom color for the drawn lines, default is grey.
**/
static Ogre::ManualObject* drawConvexVolume(ConvexVolume *vol, Ogre::SceneManager* sceneMgr, Ogre::ColourValue color=Ogre::ColourValue(0.5, 0.5, 0.5));
/**
* Debug function for drawing a bounding box as lines in the scene.
* Returns the manual object added to the scene.
* It's possible to specify a custom color for the drawn lines, default is grey.
**/
static Ogre::ManualObject* drawBoundingBox(Ogre::AxisAlignedBox box, Ogre::SceneManager *sceneMgr, Ogre::ColourValue color=Ogre::ColourValue(0.5, 0.5, 0.5));
/**
* The chunky tri mesh generated for this inputGeom.
* Chunky tri meshes are only used when building tiled navmeshes, and are not essential,
* just more optimized.
**/
inline const rcChunkyTriMesh* getChunkyMesh() const { return m_chunkyMesh; }
/**
* Raycast this inputGeometry.
**/
bool raycastMesh(float* src, float* dst, float& tmin);
/**
* See OgreDetourTileCache::hitTestObstacle, but here it serves for
* finding convexVolumes.
**/
int hitTestConvexVolume(const float* sp, const float* sq);
/**
* Retrieve the convex volume obstacle with specified index from this inputGeom.
**/
ConvexVolume* getConvexVolume(int volIdx);
// TODO off-mesh connections not implemented yet
/// @name Off-Mesh connections.
///@{
int getOffMeshConnectionCount() const { return m_offMeshConCount; }
const float* getOffMeshConnectionVerts() const { return m_offMeshConVerts; }
const float* getOffMeshConnectionRads() const { return m_offMeshConRads; }
const unsigned char* getOffMeshConnectionDirs() const { return m_offMeshConDirs; }
const unsigned char* getOffMeshConnectionAreas() const { return m_offMeshConAreas; }
const unsigned short* getOffMeshConnectionFlags() const { return m_offMeshConFlags; }
const unsigned int* getOffMeshConnectionId() const { return m_offMeshConId; }
void addOffMeshConnection(const float* spos, const float* epos, const float rad,
unsigned char bidir, unsigned char area, unsigned short flags);
void deleteOffMeshConnection(int i);
///@}
/// @name Box Volumes.
///@{
int getConvexVolumeCount() const { return m_volumeCount; }
const ConvexVolume* const* getConvexVolumes() const { return m_volumes; }
int addConvexVolume(ConvexVolume *vol);
bool deleteConvexVolume(int i, ConvexVolume** = NULL);
// Not implemented
void drawConvexVolumes(struct duDebugDraw* dd, bool hilight = false);
int getConvexVolumeId(ConvexVolume *convexHull);
///@}
/**
* Create a convex hull that fits around the points
* in this geometry.
* This type of convex hull is in fact a 2D shape on the
* xz plane (on the ground) and with a certain height, and
* at a certain distance above the ground (min 0, which means
* on the ground).
*
* Offset determines the offset of the hull from the
* geometry. 0 offset means the convex hull wraps
* tightly around the mesh.
*
* Convex hulls can be used as temporary or dynamic obstacles
* on a navmesh.
* You probably only want to create convex hulls from single
* entities or a few entities that are close together.
**/
ConvexVolume* getConvexHull(Ogre::Real offset = 0.0f);
private:
/**
* Calculate max and min bounds of this geometry.
**/
void calculateExtents(void);
/**
* Build chunky tri mesh.
* Only needed for building tiled navmeshes.
**/
void buildChunkyTriMesh(void);
/**
* Convert triangles and verticies of ogre entities
* to recast internal geometry format.
**/
void convertOgreEntities(void);
/**
* Convert ogre entities whose bounding box intersects the specified bounds
* to inputGeom.
**/
void convertOgreEntities(const Ogre::AxisAlignedBox &tileBounds);
/**
* Recast input vertices
**/
float* verts;
/**
* Number of verts
* The actual size of the verts array is 3*nverts
**/
int nverts;
/**
* Recast input tris
* Tris are index references to verts array
**/
int* tris;
/**
* The number of tris
* The actual size of the tris array is 3*ntris
**/
int ntris;
/**
* Normals calculated for verts
* Normals are not entirely accurate but good enough for recast use.
* Size of the normals array is 3*nverts
**/
float* normals;
/**
* Axis aligned bounding box of this inputGeom minimum.
**/
float* bmin;
/**
* Axis aligned bounding box of this inputGeom maximum.
**/
float* bmax;
/**
* Ogre entities this inputGeom was constructed from.
**/
std::vector<Ogre::Entity*> mSrcMeshes;
/**
* Reference node to which the absolute coordinates of the verts in this inputGeom was calculated.
* Is usually the scene rootnode.
**/
Ogre::SceneNode *mReferenceNode;
/**
* Terrain pages from which this inputGeom was constructed.
**/
Ogre::TerrainGroup *mTerrainGroup;
/**
* Optimized structures that stores triangles in axis aligned boxes of uniform size
* (tiles). Allows quick access to a part of the geometry, but requires more memory to store.
**/
rcChunkyTriMesh *m_chunkyMesh;
// Not implemented yet
/// @name Off-Mesh connections.
///@{
static const int MAX_OFFMESH_CONNECTIONS = 256;
float m_offMeshConVerts[MAX_OFFMESH_CONNECTIONS*3*2];
float m_offMeshConRads[MAX_OFFMESH_CONNECTIONS];
unsigned char m_offMeshConDirs[MAX_OFFMESH_CONNECTIONS];
unsigned char m_offMeshConAreas[MAX_OFFMESH_CONNECTIONS];
unsigned short m_offMeshConFlags[MAX_OFFMESH_CONNECTIONS];
unsigned int m_offMeshConId[MAX_OFFMESH_CONNECTIONS];
int m_offMeshConCount;
///@}
/// @name Convex Volumes (temporary) added to this geometry.
///@{
ConvexVolume* m_volumes[MAX_VOLUMES];
int m_volumeCount;
///@}
};
#endif // RECASTINPUTGEOM_H

View File

@@ -0,0 +1,80 @@
/*
OgreCrowd
---------
Copyright (c) 2012 Jonas Hauquier
Additional contributions by:
- mkultra333
- Paul Wilson
Sincere thanks and to:
- Mikko Mononen (developer of Recast navigation libraries)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#ifndef TESTCHARACTER_H
#define TESTCHARACTER_H
#include "Character.h"
#include <Ogre.h>
/**
* Simple representation of an agent. This is the most simple way to show and debug
* detour crowd agents.
* Agents are represented as blue cylinders.
**/
class TestCharacter : public Character
{
public:
/**
* Create a simple test character, the entities will be placed in the specified scene manager.
* detourCrowd is the crowd manager in which an agent for this character will be created (make sure you don't create
* more characters than MAX_AGENTS).
* Position defines initial position the character has to be placed on (should be a valid position on the navmesh).
**/
TestCharacter(Ogre::String name, Ogre::SceneManager *sceneMgr, OgreDetourCrowd* detourCrowd, Ogre::Vector3 position = Ogre::Vector3::ZERO);
/**
* The entity that represents this character in the scene
**/
virtual Ogre::Entity* getEntity(void);
/**
* @see{Character::update(Ogre::Real)}
**/
virtual void update(Ogre::Real timeSinceLastFrame);
/**
* @see{Character::setDebugVisibility(bool)}
**/
virtual void setDebugVisibility(bool visible);
protected:
/**
* Main entity that represents this character.
**/
Ogre::Entity *mEnt;
};
#endif // TESTCHARACTER_H