Hasty before flight check in. Some adjustments to VO code (renaming, optimize segment sampling). Crowd sim performance graph.

This commit is contained in:
Mikko Mononen 2010-08-22 20:15:25 +00:00
parent 0e4aa30e85
commit 07ec6dfa08
7 changed files with 1844 additions and 217 deletions

File diff suppressed because it is too large Load Diff

View File

@ -283,14 +283,13 @@
<key>PBXSmartGroupTreeModuleOutlineStateSelectionKey</key>
<array>
<array>
<integer>17</integer>
<integer>11</integer>
<integer>59</integer>
<integer>1</integer>
<integer>0</integer>
</array>
</array>
<key>PBXSmartGroupTreeModuleOutlineStateVisibleRectKey</key>
<string>{{0, 0}, {264, 660}}</string>
<string>{{0, 646}, {264, 660}}</string>
</dict>
<key>PBXTopSmartGroupGIDs</key>
<array/>
@ -325,7 +324,7 @@
<key>PBXProjectModuleGUID</key>
<string>6B8632A30F78115100E2684A</string>
<key>PBXProjectModuleLabel</key>
<string>DetourNavMeshQuery.cpp</string>
<string>CrowdTool.cpp</string>
<key>PBXSplitModuleInNavigatorKey</key>
<dict>
<key>Split0</key>
@ -333,11 +332,11 @@
<key>PBXProjectModuleGUID</key>
<string>6B8632A40F78115100E2684A</string>
<key>PBXProjectModuleLabel</key>
<string>DetourNavMeshQuery.cpp</string>
<string>CrowdTool.cpp</string>
<key>_historyCapacity</key>
<integer>0</integer>
<key>bookmark</key>
<string>6B1C8DAF121E821F0048697F</string>
<string>6B1C8E6F1221BC910048697F</string>
<key>history</key>
<array>
<string>6BBB4AA5115B4F3400CF791D</string>
@ -356,7 +355,6 @@
<string>6BAF3CB012116AD9008CFCDF</string>
<string>6BAF3CB112116AD9008CFCDF</string>
<string>6BAF3CB412116AD9008CFCDF</string>
<string>6BAF3DFC1211AEB2008CFCDF</string>
<string>6BAF3E781212869F008CFCDF</string>
<string>6BAF404F12140B4E008CFCDF</string>
<string>6BAF405112140B4E008CFCDF</string>
@ -376,7 +374,6 @@
<string>6BAF4524121D1723008CFCDF</string>
<string>6BAF4525121D1723008CFCDF</string>
<string>6BAF4562121D1849008CFCDF</string>
<string>6BAF457C121D19CB008CFCDF</string>
<string>6BAF457D121D19CB008CFCDF</string>
<string>6BAF457E121D19CB008CFCDF</string>
<string>6BAF457F121D19CB008CFCDF</string>
@ -384,18 +381,13 @@
<string>6BAF4581121D19CB008CFCDF</string>
<string>6BAF4582121D19CB008CFCDF</string>
<string>6BAF4593121D1B18008CFCDF</string>
<string>6BAF45AC121D2C03008CFCDF</string>
<string>6BAF45B8121D2F37008CFCDF</string>
<string>6BAF45E4121D7277008CFCDF</string>
<string>6BAF4604121D7388008CFCDF</string>
<string>6BAF4605121D7388008CFCDF</string>
<string>6BAF4607121D7388008CFCDF</string>
<string>6BAF461E121D746C008CFCDF</string>
<string>6BAF4637121D74D3008CFCDF</string>
<string>6BAF46A0121D8B41008CFCDF</string>
<string>6BAF46A1121D8B41008CFCDF</string>
<string>6BAF46A2121D8B41008CFCDF</string>
<string>6BAF46A3121D8B41008CFCDF</string>
<string>6BAF46D3121D8FF1008CFCDF</string>
<string>6BAF4739121D9FBE008CFCDF</string>
<string>6BAF473A121D9FBE008CFCDF</string>
@ -403,17 +395,25 @@
<string>6BAF475A121DA31D008CFCDF</string>
<string>6BAF475B121DA31D008CFCDF</string>
<string>6BAF475C121DA31D008CFCDF</string>
<string>6BAF475D121DA31D008CFCDF</string>
<string>6BAF475E121DA31D008CFCDF</string>
<string>6BAF475F121DA31D008CFCDF</string>
<string>6BAF4760121DA31D008CFCDF</string>
<string>6BAF4778121DCAF9008CFCDF</string>
<string>6BAF478E121DD150008CFCDF</string>
<string>6B1C8D82121E80950048697F</string>
<string>6B1C8DA2121E813D0048697F</string>
<string>6B1C8DA3121E813D0048697F</string>
<string>6B1C8DAD121E821F0048697F</string>
<string>6B1C8DA8121E81C40048697F</string>
<string>6B1C8DB3121E8D970048697F</string>
<string>6B1C8DBF121E8F610048697F</string>
<string>6B1C8DC0121E8F610048697F</string>
<string>6B1C8DC5121E902F0048697F</string>
<string>6B1C8DD8121EA3470048697F</string>
<string>6B1C8E08121EB4FF0048697F</string>
<string>6B1C8E09121EB4FF0048697F</string>
<string>6B1C8E0A121EB4FF0048697F</string>
<string>6B1C8E0C121EB4FF0048697F</string>
<string>6B1C8E24121EB6D30048697F</string>
<string>6B1C8E27121EB6D30048697F</string>
<string>6B1C8E30121EB9310048697F</string>
<string>6B1C8E691221BC910048697F</string>
<string>6B1C8E6A1221BC910048697F</string>
<string>6B1C8E6B1221BC910048697F</string>
</array>
<key>prevStack</key>
<array>
@ -458,11 +458,7 @@
<string>6BAF3B1512112F65008CFCDF</string>
<string>6BAF3DAA1211882E008CFCDF</string>
<string>6BAF3E631211B713008CFCDF</string>
<string>6BAF3F5F1213E45B008CFCDF</string>
<string>6BBB4AE2115B4F3400CF791D</string>
<string>6BBB4AF7115B4F3400CF791D</string>
<string>6BAF405912140B4E008CFCDF</string>
<string>6BAF40A612142254008CFCDF</string>
<string>6BAF411712197F3D008CFCDF</string>
<string>6BAF41801219811E008CFCDF</string>
<string>6BAF41DA121A5D13008CFCDF</string>
@ -481,37 +477,43 @@
<string>6BAF410212197F3D008CFCDF</string>
<string>6BAF44FA121C5713008CFCDF</string>
<string>6BAF45E9121D7277008CFCDF</string>
<string>6BAF4455121C40AC008CFCDF</string>
<string>6BAF447E121C4895008CFCDF</string>
<string>6BAF473D121D9FBE008CFCDF</string>
<string>6BAF4749121D9FED008CFCDF</string>
<string>6BAF4762121DA31D008CFCDF</string>
<string>6BAF4767121DA31D008CFCDF</string>
<string>6BAF4768121DA31D008CFCDF</string>
<string>6BAF4797121DD34D008CFCDF</string>
<string>6B1C8D84121E80950048697F</string>
<string>6B1C8D85121E80950048697F</string>
<string>6B1C8D86121E80950048697F</string>
<string>6B1C8D87121E80950048697F</string>
<string>6B1C8D88121E80950048697F</string>
<string>6B1C8D89121E80950048697F</string>
<string>6B1C8D8A121E80950048697F</string>
<string>6B1C8D8B121E80950048697F</string>
<string>6B1C8D8C121E80950048697F</string>
<string>6B1C8D8D121E80950048697F</string>
<string>6B1C8D8E121E80950048697F</string>
<string>6B1C8D8F121E80950048697F</string>
<string>6B1C8D90121E80950048697F</string>
<string>6B1C8D91121E80950048697F</string>
<string>6B1C8D92121E80950048697F</string>
<string>6B1C8D93121E80950048697F</string>
<string>6B1C8D94121E80950048697F</string>
<string>6B1C8D95121E80950048697F</string>
<string>6B1C8D96121E80950048697F</string>
<string>6B1C8DA5121E813D0048697F</string>
<string>6B1C8DA6121E813D0048697F</string>
<string>6B1C8DAA121E81C40048697F</string>
<string>6B1C8DAE121E821F0048697F</string>
<string>6B1C8DB6121E8D970048697F</string>
<string>6B1C8DB7121E8D970048697F</string>
<string>6B1C8DDB121EA3470048697F</string>
<string>6B1C8E11121EB4FF0048697F</string>
<string>6B1C8E12121EB4FF0048697F</string>
<string>6B1C8E13121EB4FF0048697F</string>
<string>6B1C8E14121EB4FF0048697F</string>
<string>6B1C8E1B121EB4FF0048697F</string>
<string>6B1C8E2A121EB6D30048697F</string>
<string>6B1C8E2B121EB6D30048697F</string>
<string>6B1C8E2D121EB6D30048697F</string>
<string>6B1C8E2E121EB6D30048697F</string>
<string>6B1C8E58121EBEBF0048697F</string>
<string>6B1C8E59121EBEBF0048697F</string>
<string>6B1C8E661221BC080048697F</string>
<string>6B1C8E6C1221BC910048697F</string>
<string>6B1C8E6D1221BC910048697F</string>
<string>6B1C8E6E1221BC910048697F</string>
</array>
</dict>
<key>SplitCount</key>
@ -525,18 +527,18 @@
<key>GeometryConfiguration</key>
<dict>
<key>Frame</key>
<string>{{0, 0}, {994, 606}}</string>
<string>{{0, 0}, {994, 505}}</string>
<key>RubberWindowFrame</key>
<string>0 59 1280 719 0 0 1280 778 </string>
</dict>
<key>Module</key>
<string>PBXNavigatorGroup</string>
<key>Proportion</key>
<string>606pt</string>
<string>505pt</string>
</dict>
<dict>
<key>Proportion</key>
<string>67pt</string>
<string>168pt</string>
<key>Tabs</key>
<array>
<dict>
@ -604,7 +606,7 @@
<key>GeometryConfiguration</key>
<dict>
<key>Frame</key>
<string>{{10, 27}, {994, 40}}</string>
<string>{{10, 27}, {994, 141}}</string>
<key>RubberWindowFrame</key>
<string>0 59 1280 719 0 0 1280 778 </string>
</dict>

View File

@ -24,19 +24,20 @@
// Tool to create crowds.
enum BodyType
{
BODY_CIRCLE = 0,
BODY_CAPSULE = 1,
BODY_SEGMENT = 1,
};
struct Body
struct ObstacleBody
{
float p[3], q[3]; // Position of the obstacle
float vel[3]; // Velocity of the obstacle
float dvel[3]; // Velocity of the obstacle
float rad; // Radius of the obstacle
float dp[3], np[3]; // Use for side selection during sampling.
bool touch;
int type; // Type of the obstacle (see ObstacleType)
};
@ -44,9 +45,9 @@ struct Body
static const int RVO_SAMPLE_RAD = 15;
static const int MAX_RVO_SAMPLES = (RVO_SAMPLE_RAD*2+1)*(RVO_SAMPLE_RAD*2+1) + 100;
struct RVO
struct RVODebugData
{
inline RVO() : ns(0) {}
inline RVODebugData() : ns(0) {}
float spos[MAX_RVO_SAMPLES*3];
float scs[MAX_RVO_SAMPLES];
float spen[MAX_RVO_SAMPLES];
@ -90,11 +91,13 @@ struct Agent
float npos[3];
float disp[3];
float opts[3], opte[3];
float maxspeed;
float t;
float var;
RVO rvo;
RVODebugData rvo;
float colradius;
float colcenter[3];
@ -116,6 +119,59 @@ struct Agent
unsigned char active;
};
class SampleGraph
{
static const int MAX_SAMPLES = 512;
float m_samples[MAX_SAMPLES];
int m_hsamples;
public:
SampleGraph() :
m_hsamples(0)
{
for (int i = 0; i < MAX_SAMPLES; ++i)
m_samples[i] = 0;
}
~SampleGraph()
{
}
inline void addSample(const float val)
{
m_hsamples = (m_hsamples+MAX_SAMPLES-1) % MAX_SAMPLES;
m_samples[m_hsamples] = val;
}
inline int getSampleCount() const
{
return MAX_SAMPLES;
}
inline float getSample(const int i) const
{
return m_samples[(m_hsamples+i) % MAX_SAMPLES];
}
inline float getSampleMin() const
{
float val = m_samples[0];
for (int i = 1; i < MAX_SAMPLES; ++i)
if (m_samples[i] < val)
val = m_samples[i];
return val;
}
inline float getSampleMax() const
{
float val = m_samples[0];
for (int i = 1; i < MAX_SAMPLES; ++i)
if (m_samples[i] > val)
val = m_samples[i];
return val;
}
};
enum UpdateFlags
{
CROWDMAN_ANTICIPATE_TURNS = 1,
@ -128,6 +184,10 @@ class CrowdManager
static const int MAX_AGENTS = 32;
Agent m_agents[MAX_AGENTS];
int m_shortcutIter;
SampleGraph m_totalTime;
SampleGraph m_rvoTime;
public:
CrowdManager();
~CrowdManager();
@ -140,6 +200,9 @@ public:
void setMoveTarget(const int idx, const float* pos);
void update(const float dt, unsigned int flags, dtNavMeshQuery* navquery);
const SampleGraph* getTotalTimeGraph() const { return &m_totalTime; }
const SampleGraph* getRVOTimeGraph() const { return &m_rvoTime; }
};
class CrowdTool : public SampleTool
@ -155,6 +218,7 @@ class CrowdTool : public SampleTool
bool m_showCollisionSegments;
bool m_showPath;
bool m_showVO;
bool m_showOpt;
bool m_expandOptions;
bool m_anticipateTurns;
@ -164,7 +228,7 @@ class CrowdTool : public SampleTool
bool m_run;
CrowdManager m_crowd;
enum ToolMode
{
TOOLMODE_CREATE,

View File

@ -16,6 +16,9 @@
// 3. This notice may not be removed or altered from any source distribution.
//
#ifndef SAMPLEINTERFACES_H
#define SAMPLEINTERFACES_H
#include "DebugDraw.h"
#include "Recast.h"
#include "RecastDump.h"
@ -62,6 +65,12 @@ public:
virtual int getBuildTime(const rcBuilTimeLabel label);
};
// TODO: Find better solution. This is useful for timing in some cases when the full build interface would be overkill.
rcTimeVal getPerfTime();
int getPerfDeltaTimeUsec(const rcTimeVal start, const rcTimeVal end);
// OpenGL debug draw implementation.
class DebugDrawGL : public duDebugDraw
{
@ -88,3 +97,6 @@ public:
virtual bool write(const void* ptr, const size_t size);
virtual bool read(void* ptr, const size_t size);
};
#endif // SAMPLEINTERFACES_H

View File

@ -29,6 +29,7 @@
#include "Sample.h"
#include "DetourDebugDraw.h"
#include "DetourCommon.h"
#include "SampleInterfaces.h"
#ifdef WIN32
# define snprintf _snprintf
@ -197,7 +198,7 @@ static void normalizeArray(float* arr, const int n)
arr[i] = dtClamp((arr[i]-minPen)*s, 0.0f, 1.0f);
}
void normalizeSamples(RVO* rvo)
void normalizeSamples(RVODebugData* rvo)
{
normalizeArray(rvo->spen, rvo->ns);
normalizeArray(rvo->svpen, rvo->ns);
@ -207,7 +208,7 @@ void normalizeSamples(RVO* rvo)
}
void setDynCircleBody(Body* b, const float* pos, const float rad, const float* vel, const float* dvel)
void setDynCircleBody(ObstacleBody* b, const float* pos, const float rad, const float* vel, const float* dvel)
{
b->type = BODY_CIRCLE;
dtVcopy(b->p, pos);
@ -216,7 +217,7 @@ void setDynCircleBody(Body* b, const float* pos, const float rad, const float* v
b->rad = rad;
}
void setStatCircleBody(Body* b, const float* pos, const float rad)
void setStatCircleBody(ObstacleBody* b, const float* pos, const float rad)
{
b->type = BODY_CIRCLE;
dtVcopy(b->p, pos);
@ -225,14 +226,14 @@ void setStatCircleBody(Body* b, const float* pos, const float rad)
b->rad = rad;
}
void setStatCapsuleBody(Body* b, const float* p, const float* q, const float rad)
void setStatSegmentBody(ObstacleBody* b, const float* p, const float* q)
{
b->type = BODY_CAPSULE;
b->type = BODY_SEGMENT;
dtVcopy(b->p, p);
dtVcopy(b->q, q);
dtVset(b->vel, 0,0,0);
dtVset(b->dvel, 0,0,0);
b->rad = rad;
b->rad = 0;
}
static const float VEL_WEIGHT = 2.0f;
@ -261,84 +262,66 @@ static int sweepCircleCircle(const float* c0, const float r0, const float* v,
return 1;
}
static int sweepCircleSegment(const float* c0, const float r0, const float* v,
const float* sa, const float* sb, const float sr,
float& tmin, float &tmax)
static void prepareBodies(const ObstacleBody* agent, ObstacleBody* obs, const int nobs)
{
// equation parameters
float L[3], H[3];
dtVsub(L, sb, sa);
dtVsub(H, c0, sa);
const float radius = r0+sr;
const float l2 = dtVdot2D(L, L);
const float r2 = radius * radius;
const float dl = dtVperp2D(v, L);
const float hl = dtVperp2D(H, L);
const float a = dl * dl;
const float b = 2.0f * hl * dl;
const float c = hl * hl - (r2 * l2);
float d = (b*b) - (4.0f * a * c);
// infinite line missed by infinite ray.
if (d < 0.0f)
return 0;
const float i2a = 1.0f/(2*a);
d = dtSqrt(d);
tmin = (-b - d) * i2a;
tmax = (-b + d) * i2a;
// line missed by ray range.
/* if (tmax < 0.0f || tmin > 1.0f)
return 0;*/
// find what part of the ray was collided.
const float il2 = 1.0f / l2;
float Pedge[3];
dtVmad(Pedge, c0, v, tmin);
dtVsub(H, Pedge, sa);
const float e0 = dtVdot2D(H, L) * il2;
dtVmad(Pedge, c0, v, tmax);
dtVsub(H, Pedge, sa);
const float e1 = dtVdot2D(H, L) * il2;
if (e0 < 0.0f || e1 < 0.0f)
for (int i = 0; i < nobs; ++i)
{
float ctmin, ctmax;
if (sweepCircleCircle(c0, r0, v, sa, sr, ctmin, ctmax))
ObstacleBody* ob = &obs[i];
if (ob->type == BODY_CIRCLE)
{
if (e0 < 0.0f && ctmin > tmin)
tmin = ctmin;
if (e1 < 0.0f && ctmax < tmax)
tmax = ctmax;
// Side
const float* pa = agent->p;
const float* pb = ob->p;
const float orig[3] = {0,0};
float dv[3];
dtVsub(ob->dp,pb,pa);
dtVnormalize(ob->dp);
dtVsub(dv, ob->dvel, agent->dvel);
const float a = dtTriArea2D(orig, ob->dp,dv);
if (a < 0.01f)
{
ob->np[0] = -ob->dp[2];
ob->np[2] = ob->dp[0];
}
else
{
ob->np[0] = ob->dp[2];
ob->np[2] = -ob->dp[0];
}
}
else
else if (ob->type == BODY_SEGMENT)
{
return 0;
// Precalc if the agent is really close to the segment.
const float r = 0.01f;
float t;
ob->touch = dtDistancePtSegSqr2D(agent->p, ob->p, ob->q, t) < dtSqr(r);
}
}
if (e0 > 1.0f || e1 > 1.0f)
{
float ctmin, ctmax;
if (sweepCircleCircle(c0, r0, v, sb, sr, ctmin, ctmax))
{
if (e0 > 1.0f && ctmin > tmin)
tmin = ctmin;
if (e1 > 1.0f && ctmax < tmax)
tmax = ctmax;
}
else
{
return 0;
}
}
}
}
static int isectRaySeg(const float* ap, const float* u,
const float* bp, const float* bq,
float& t)
{
float /*u[3]*/ v[3], w[3];
// dtVsub(u,aq,ap);
dtVsub(v,bq,bp);
dtVsub(w,ap,bp);
float d = dtVperp2D(u,v);
if (fabsf(d) < 1e-6f) return 0;
d = 1.0f/d;
t = dtVperp2D(v,w) * d;
if (t < 0 || t > 1) return 0;
float s = dtVperp2D(u,w) * d;
if (s < 0 || s > 1) return 0;
return 1;
}
static void processSamples(Body* agent, const float vmax,
const Body* obs, const int nobs, RVO* rvo,
static void processSamples(ObstacleBody* agent, const float vmax,
const ObstacleBody* obs, const int nobs, RVODebugData* rvo,
const float* spos, const float cs, const int nspos,
float* res)
{
@ -365,7 +348,7 @@ static void processSamples(Body* agent, const float vmax,
for (int i = 0; i < nobs; ++i)
{
const Body* ob = &obs[i];
const ObstacleBody* ob = &obs[i];
float htmin = 0, htmax = 0;
if (ob->type == BODY_CIRCLE)
@ -378,30 +361,7 @@ static void processSamples(Body* agent, const float vmax,
dtVsub(vab, vab, ob->vel);
// Side
// NOTE: dp, and dv are constant over the whole calculation,
// they can be precomputed per object.
const float* pa = agent->p;
const float* pb = ob->p;
const float orig[3] = {0,0};
float dp[3],dv[3],np[3];
dtVsub(dp,pb,pa);
dtVnormalize(dp);
dtVsub(dv, ob->dvel, agent->dvel);
const float a = dtTriArea2D(orig, dp,dv);
if (a < 0.01f)
{
np[0] = -dp[2];
np[2] = dp[0];
}
else
{
np[0] = dp[2];
np[2] = -dp[0];
}
side += dtClamp(dtMin(dtVdot2D(dp,vab)*2,dtVdot2D(np,vab)*2), 0.0f, 1.0f);
side += dtClamp(dtMin(dtVdot2D(ob->dp,vab)*2, dtVdot2D(ob->np,vab)*2), 0.0f, 1.0f);
nside++;
if (!sweepCircleCircle(agent->p,agent->rad, vab, ob->p,ob->rad, htmin, htmax))
@ -414,17 +374,11 @@ static void processSamples(Body* agent, const float vmax,
htmin = -htmin * 0.5f;
}
}
else if (ob->type == BODY_CAPSULE)
else if (ob->type == BODY_SEGMENT)
{
// NOTE: the segments are assumed to come from a navmesh which is shrunken by
// the agent radius, hence the use of really small radius.
// This can be handle more efficiently by using seg-seg test instead.
// If the whole segment is to be treated as obstacle, use agent->rad instead of 0.01f!
const float r = 0.01f; // agent->rad
float t;
if (dtDistancePtSegSqr2D(agent->p, ob->p, ob->q, t) < dtSqr(r+ob->rad))
if (ob->touch)
{
// Special case when the agent is very close to the segment.
float sdir[3], snorm[3];
dtVsub(sdir, ob->q, ob->p);
snorm[0] = -sdir[2];
@ -438,8 +392,9 @@ static void processSamples(Body* agent, const float vmax,
}
else
{
if (!sweepCircleSegment(agent->p, r, vcand, ob->p, ob->q, ob->rad, htmin, htmax))
if (!isectRaySeg(agent->p, vcand, ob->p, ob->q, htmin))
continue;
htmax = maxToi;
}
// Avoid less when facing walls.
@ -483,7 +438,8 @@ static void processSamples(Body* agent, const float vmax,
}
void sampleRVO(Body* agent, const float vmax, const Body* obs, const int nobs, RVO* rvo, const float bias, float* nvel)
void sampleRVO(ObstacleBody* agent, const float vmax, const ObstacleBody* obs,
const int nobs, RVODebugData* rvo, const float bias, float* nvel)
{
dtVset(nvel, 0,0,0);
@ -620,6 +576,8 @@ void CrowdManager::update(const float dt, unsigned int flags, dtNavMeshQuery* na
if (!navquery)
return;
rcTimeVal startTime = getPerfTime();
const float ext[3] = {2,4,2};
dtQueryFilter filter;
@ -802,8 +760,9 @@ void CrowdManager::update(const float dt, unsigned int flags, dtNavMeshQuery* na
}
// Velocity planning.
rcTimeVal rvoStartTime = getPerfTime();
static const int MAX_BODIES = 32;
Body bodies[MAX_BODIES];
ObstacleBody bodies[MAX_BODIES];
for (int i = 0; i < MAX_AGENTS; ++i)
{
if (!m_agents[i].active) continue;
@ -847,14 +806,14 @@ void CrowdManager::update(const float dt, unsigned int flags, dtNavMeshQuery* na
if (nbodies < MAX_BODIES)
{
setStatCapsuleBody(&bodies[nbodies], s,s+3,0);
setStatSegmentBody(&bodies[nbodies], s,s+3);
nbodies++;
}
}
Body agent;
ObstacleBody agent;
setDynCircleBody(&agent, ag->pos, ag->radius, ag->vel, ag->dvel);
prepareBodies(&agent, bodies, nbodies);
sampleRVO(&agent, ag->maxspeed, bodies, nbodies, &ag->rvo, 0.4f, ag->nvel);
// Normalize samples for debug draw
@ -865,7 +824,9 @@ void CrowdManager::update(const float dt, unsigned int flags, dtNavMeshQuery* na
dtVcopy(ag->nvel, ag->dvel);
}
}
rcTimeVal rvoEndTime = getPerfTime();
m_rvoTime.addSample(getPerfDeltaTimeUsec(rvoStartTime, rvoEndTime) / 1000.0f);
// Integrate and update perceived velocity.
for (int i = 0; i < MAX_AGENTS; ++i)
{
@ -967,13 +928,30 @@ void CrowdManager::update(const float dt, unsigned int flags, dtNavMeshQuery* na
{
if (!m_agents[i].active) continue;
Agent* ag = &m_agents[i];
dtVset(ag->opts, 0,0,0);
dtVset(ag->opte, 0,0,0);
if (ag->npath && ag->ncorners > 1)
{
// The target is the corner after the next corner to steer to.
const float* tgt = &ag->corners[3];
float tgt[3];
dtVcopy(tgt, &ag->corners[3]);
const float distSqr = dtVdist2DSqr(ag->pos, tgt);
if (distSqr > dtSqr(0.01f)) // && distSqr < dtSqr(20.0f))
if (distSqr > dtSqr(0.01f))
{
// Clamp teh ray to max distance.
const float maxDist = ag->colradius*3;
if (distSqr > dtSqr(maxDist))
{
float delta[3];
dtVsub(delta, tgt, ag->pos);
dtVmad(tgt, ag->pos, delta, dtSqr(maxDist)/distSqr);
}
dtVcopy(ag->opts, ag->pos);
dtVcopy(ag->opte, tgt);
static const int MAX_RES = 32;
dtPolyRef res[MAX_RES];
float t, norm[3];
@ -986,6 +964,9 @@ void CrowdManager::update(const float dt, unsigned int flags, dtNavMeshQuery* na
}
}
rcTimeVal endTime = getPerfTime();
m_totalTime.addSample(getPerfDeltaTimeUsec(startTime, endTime) / 1000.0f);
}
@ -1000,6 +981,7 @@ CrowdTool::CrowdTool() :
m_showCollisionSegments(false),
m_showPath(false),
m_showVO(false),
m_showOpt(false),
m_expandOptions(true),
m_anticipateTurns(true),
m_useVO(true),
@ -1079,6 +1061,8 @@ void CrowdTool::handleMenu()
m_showPath = !m_showPath;
if (imguiCheck("Show VO", m_showVO))
m_showVO = !m_showVO;
if (imguiCheck("Show Opt", m_showOpt))
m_showOpt = !m_showOpt;
imguiUnindent();
}
}
@ -1276,10 +1260,18 @@ void CrowdTool::handleRender()
dd.end();
}
if (m_showOpt)
{
dd.begin(DU_DRAW_LINES, 2.0f);
dd.vertex(ag->opts[0],ag->opts[1]+0.3f,ag->opts[2], duRGBA(0,128,0,192));
dd.vertex(ag->opte[0],ag->opte[1]+0.3f,ag->opte[2], duRGBA(0,128,0,192));
dd.end();
}
if (m_showVO)
{
// Draw detail about agent sela
const RVO* rvo = &ag->rvo;
const RVODebugData* rvo = &ag->rvo;
const float dx = ag->pos[0];
const float dy = ag->pos[1]+ag->height;
@ -1316,6 +1308,69 @@ void CrowdTool::handleRender()
}
}
static void drawGraphBackground(const int x, const int y, const int w, const int h,
const float vmin, const float vmax, const int ndiv,
const char* units)
{
// BG
DebugDrawGL dd;
dd.begin(DU_DRAW_QUADS);
dd.vertex(x,y,0, duRGBA(0,0,0,64));
dd.vertex(x+w,y,0, duRGBA(0,0,0,96));
dd.vertex(x+w,y+h,0, duRGBA(128,128,128,64));
dd.vertex(x,y+h,0, duRGBA(128,128,128,64));
dd.end();
const int pad = 10;
const float sy = (h-pad*2) / (vmax-vmin);
const float oy = y+pad-vmin*sy;
char text[64];
// Divider Lines
for (int i = 0; i < ndiv; ++i)
{
const float u = (float)i/(float)ndiv;
const float v = vmin + (vmax-vmin)*u;
snprintf(text, 64, "%.2f %s", v, units);
const float fy = oy + v*sy;
imguiDrawText(x+w-pad, (int)fy-4, IMGUI_ALIGN_RIGHT, text, imguiRGBA(0,0,0,255));
dd.begin(DU_DRAW_LINES, 1.0f);
dd.vertex(x,fy,0,duRGBA(0,0,0,64));
dd.vertex(x+w-pad-60,fy,0,duRGBA(0,0,0,64));
dd.end();
}
}
static void drawGraph(const SampleGraph* graph,
const int x, const int y, const int w, const int h,
const float vmin, const float vmax, const unsigned int col)
{
DebugDrawGL dd;
const int pad = 10;
const float sx = (w-pad*2) / (float)graph->getSampleCount();
const float sy = (h-pad*2) / (vmax-vmin);
const float ox = x+pad;
const float oy = y+pad-vmin*sy;
// Values
dd.begin(DU_DRAW_LINES, 2.0f);
for (int i = 0; i < graph->getSampleCount()-1; ++i)
{
const float fx0 = ox + i*sx;
const float fy0 = oy + graph->getSample(i)*sy;
const float fx1 = ox + (i+1)*sx;
const float fy1 = oy + graph->getSample(i+1)*sy;
dd.vertex(fx0,fy0,0,col);
dd.vertex(fx1,fy1,0,col);
}
dd.end();
}
void CrowdTool::handleRenderOverlay(double* proj, double* model, int* view)
{
GLdouble x, y, z;
@ -1344,4 +1399,11 @@ void CrowdTool::handleRenderOverlay(double* proj, double* model, int* view)
}
}
const int gx = 300, gy = 10, gw = 500, gh = 200;
const float vmin = 0.0f, vmax = 10.0f;
drawGraphBackground(gx,gy,gw,gh, vmin,vmax, 4, "ms");
drawGraph(m_crowd.getRVOTimeGraph(), gx,gy,gw,gh, vmin,vmax, duRGBA(255,0,0,128));
drawGraph(m_crowd.getTotalTimeGraph(), gx,gy,gw,gh, vmin,vmax, duRGBA(192,255,0,192));
}

View File

@ -174,6 +174,49 @@ int BuildContext::getBuildTime(const rcBuilTimeLabel label)
////////////////////////////////////////////////////////////////////////////////////////////////////
#if defined(WIN32)
// Win32
#include <windows.h>
rcTimeVal getPerfTime()
{
__int64 count;
QueryPerformanceCounter((LARGE_INTEGER*)&count);
return count;
}
int getPerfDeltaTimeUsec(const rcTimeVal start, const rcTimeVal end)
{
static __int64 freq = 0;
if (freq == 0)
QueryPerformanceFrequency((LARGE_INTEGER*)&freq);
__int64 elapsed = end - start;
return (int)(elapsed*1000000 / freq);
}
#else
// Linux, BSD, OSX
#include <sys/time.h>
rcTimeVal getPerfTime()
{
timeval now;
gettimeofday(&now, 0);
return (rcTimeVal)now.tv_sec*1000000L + (rcTimeVal)now.tv_usec;
}
int getPerfDeltaTimeUsec(const rcTimeVal start, const rcTimeVal end)
{
return (int)(end - start);
}
#endif
////////////////////////////////////////////////////////////////////////////////////////////////////
void DebugDrawGL::depthMask(bool state)
{
glDepthMask(state ? GL_TRUE : GL_FALSE);