forked from mirror/DotRecast
313 lines
10 KiB
C#
313 lines
10 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using DotRecast.Core;
|
|
using DotRecast.Core.Numerics;
|
|
using DotRecast.Detour;
|
|
using DotRecast.Detour.Crowd;
|
|
using DotRecast.Recast.Toolset.Builder;
|
|
|
|
namespace DotRecast.Recast.Toolset.Tools
|
|
{
|
|
public class RcCrowdTool : IRcToolable
|
|
{
|
|
private readonly DtCrowdAgentConfig _agCfg;
|
|
private DtCrowd crowd;
|
|
private readonly DtCrowdAgentDebugInfo _agentDebug;
|
|
private long crowdUpdateTime;
|
|
private readonly Dictionary<long, RcCrowdAgentTrail> _trails;
|
|
private long _moveTargetRef;
|
|
private RcVec3f _moveTargetPos;
|
|
|
|
public RcCrowdTool()
|
|
{
|
|
_agCfg = new DtCrowdAgentConfig();
|
|
_agentDebug = new DtCrowdAgentDebugInfo();
|
|
_agentDebug.vod = new DtObstacleAvoidanceDebugData(2048);
|
|
_trails = new Dictionary<long, RcCrowdAgentTrail>();
|
|
}
|
|
|
|
|
|
public string GetName()
|
|
{
|
|
return "Crowd Control";
|
|
}
|
|
|
|
public DtCrowdAgentConfig GetCrowdConfig()
|
|
{
|
|
return _agCfg;
|
|
}
|
|
|
|
public DtCrowdAgentDebugInfo GetCrowdAgentDebugInfo()
|
|
{
|
|
return _agentDebug;
|
|
}
|
|
|
|
public Dictionary<long, RcCrowdAgentTrail> GetCrowdAgentTrails()
|
|
{
|
|
return _trails;
|
|
}
|
|
|
|
public long GetMoveTargetRef()
|
|
{
|
|
return _moveTargetRef;
|
|
}
|
|
|
|
public RcVec3f GetMoveTargetPos()
|
|
{
|
|
return _moveTargetPos;
|
|
}
|
|
|
|
public void Setup(float agentRadius, DtNavMesh navMesh)
|
|
{
|
|
DtCrowdConfig config = new DtCrowdConfig(agentRadius);
|
|
crowd = new DtCrowd(config, navMesh, __ => new DtQueryDefaultFilter(
|
|
SampleAreaModifications.SAMPLE_POLYFLAGS_ALL,
|
|
SampleAreaModifications.SAMPLE_POLYFLAGS_DISABLED,
|
|
new float[] { 1f, 10f, 1f, 1f, 2f, 1.5f })
|
|
);
|
|
|
|
// Setup local avoidance option to different qualities.
|
|
// Use mostly default settings, copy from dtCrowd.
|
|
DtObstacleAvoidanceParams option = new DtObstacleAvoidanceParams(crowd.GetObstacleAvoidanceParams(0));
|
|
|
|
// Low (11)
|
|
option.velBias = 0.5f;
|
|
option.adaptiveDivs = 5;
|
|
option.adaptiveRings = 2;
|
|
option.adaptiveDepth = 1;
|
|
crowd.SetObstacleAvoidanceParams(0, option);
|
|
|
|
// Medium (22)
|
|
option.velBias = 0.5f;
|
|
option.adaptiveDivs = 5;
|
|
option.adaptiveRings = 2;
|
|
option.adaptiveDepth = 2;
|
|
crowd.SetObstacleAvoidanceParams(1, option);
|
|
|
|
// Good (45)
|
|
option.velBias = 0.5f;
|
|
option.adaptiveDivs = 7;
|
|
option.adaptiveRings = 2;
|
|
option.adaptiveDepth = 3;
|
|
crowd.SetObstacleAvoidanceParams(2, option);
|
|
|
|
// High (66)
|
|
option.velBias = 0.5f;
|
|
option.adaptiveDivs = 7;
|
|
option.adaptiveRings = 3;
|
|
option.adaptiveDepth = 3;
|
|
|
|
crowd.SetObstacleAvoidanceParams(3, option);
|
|
}
|
|
|
|
public void UpdateAgentParams()
|
|
{
|
|
if (crowd == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
foreach (DtCrowdAgent ag in crowd.GetActiveAgents())
|
|
{
|
|
DtCrowdAgentParams agOption = new DtCrowdAgentParams();
|
|
agOption.radius = ag.option.radius;
|
|
agOption.height = ag.option.height;
|
|
agOption.maxAcceleration = ag.option.maxAcceleration;
|
|
agOption.maxSpeed = ag.option.maxSpeed;
|
|
agOption.collisionQueryRange = ag.option.collisionQueryRange;
|
|
agOption.pathOptimizationRange = ag.option.pathOptimizationRange;
|
|
agOption.obstacleAvoidanceType = ag.option.obstacleAvoidanceType;
|
|
agOption.queryFilterType = ag.option.queryFilterType;
|
|
agOption.userData = ag.option.userData;
|
|
agOption.updateFlags = _agCfg.GetUpdateFlags();
|
|
agOption.obstacleAvoidanceType = _agCfg.obstacleAvoidanceType;
|
|
agOption.separationWeight = _agCfg.separationWeight;
|
|
crowd.UpdateAgentParameters(ag, agOption);
|
|
}
|
|
}
|
|
|
|
public DtCrowd GetCrowd()
|
|
{
|
|
return crowd;
|
|
}
|
|
|
|
public void Update(float dt)
|
|
{
|
|
if (crowd == null)
|
|
return;
|
|
|
|
DtNavMesh nav = crowd.GetNavMesh();
|
|
if (nav == null)
|
|
return;
|
|
|
|
long startTime = RcFrequency.Ticks;
|
|
crowd.Update(dt, _agentDebug);
|
|
long endTime = RcFrequency.Ticks;
|
|
|
|
// Update agent trails
|
|
foreach (DtCrowdAgent ag in crowd.GetActiveAgents())
|
|
{
|
|
RcCrowdAgentTrail trail = _trails[ag.idx];
|
|
// Update agent movement trail.
|
|
trail.htrail = (trail.htrail + 1) % RcCrowdAgentTrail.AGENT_MAX_TRAIL;
|
|
trail.trail[trail.htrail * 3] = ag.npos.X;
|
|
trail.trail[trail.htrail * 3 + 1] = ag.npos.Y;
|
|
trail.trail[trail.htrail * 3 + 2] = ag.npos.Z;
|
|
}
|
|
|
|
_agentDebug.vod.NormalizeSamples();
|
|
|
|
// m_crowdSampleCount.addSample((float) crowd.GetVelocitySampleCount());
|
|
crowdUpdateTime = (endTime - startTime) / TimeSpan.TicksPerMillisecond;
|
|
}
|
|
|
|
public void RemoveAgent(DtCrowdAgent agent)
|
|
{
|
|
crowd.RemoveAgent(agent);
|
|
if (agent == _agentDebug.agent)
|
|
{
|
|
_agentDebug.agent = null;
|
|
}
|
|
}
|
|
|
|
public void AddAgent(RcVec3f p, float agentRadius, float agentHeight, float agentMaxAcceleration, float agentMaxSpeed)
|
|
{
|
|
DtCrowdAgentParams ap = CreateAgentParams(agentRadius, agentHeight, agentMaxAcceleration, agentMaxSpeed);
|
|
DtCrowdAgent ag = crowd.AddAgent(p, ap);
|
|
if (ag != null)
|
|
{
|
|
if (_moveTargetRef != 0)
|
|
crowd.RequestMoveTarget(ag, _moveTargetRef, _moveTargetPos);
|
|
|
|
// Init trail
|
|
if (!_trails.TryGetValue(ag.idx, out var trail))
|
|
{
|
|
trail = new RcCrowdAgentTrail();
|
|
_trails.Add(ag.idx, trail);
|
|
}
|
|
|
|
for (int i = 0; i < RcCrowdAgentTrail.AGENT_MAX_TRAIL; ++i)
|
|
{
|
|
trail.trail[i * 3] = p.X;
|
|
trail.trail[i * 3 + 1] = p.Y;
|
|
trail.trail[i * 3 + 2] = p.Z;
|
|
}
|
|
|
|
trail.htrail = 0;
|
|
}
|
|
}
|
|
|
|
private DtCrowdAgentParams CreateAgentParams(float agentRadius, float agentHeight, float agentMaxAcceleration, float agentMaxSpeed)
|
|
{
|
|
DtCrowdAgentParams ap = new DtCrowdAgentParams();
|
|
ap.radius = agentRadius;
|
|
ap.height = agentHeight;
|
|
ap.maxAcceleration = agentMaxAcceleration;
|
|
ap.maxSpeed = agentMaxSpeed;
|
|
ap.collisionQueryRange = ap.radius * 12.0f;
|
|
ap.pathOptimizationRange = ap.radius * 30.0f;
|
|
ap.updateFlags = _agCfg.GetUpdateFlags();
|
|
ap.obstacleAvoidanceType = _agCfg.obstacleAvoidanceType;
|
|
ap.separationWeight = _agCfg.separationWeight;
|
|
return ap;
|
|
}
|
|
|
|
public DtCrowdAgent HitTestAgents(RcVec3f s, RcVec3f p)
|
|
{
|
|
DtCrowdAgent isel = null;
|
|
float tsel = float.MaxValue;
|
|
|
|
foreach (DtCrowdAgent ag in crowd.GetActiveAgents())
|
|
{
|
|
RcVec3f bmin = new RcVec3f();
|
|
RcVec3f bmax = new RcVec3f();
|
|
GetAgentBounds(ag, ref bmin, ref bmax);
|
|
if (RcIntersections.IsectSegAABB(s, p, bmin, bmax, out var tmin, out var tmax))
|
|
{
|
|
if (tmin > 0 && tmin < tsel)
|
|
{
|
|
isel = ag;
|
|
tsel = tmin;
|
|
}
|
|
}
|
|
}
|
|
|
|
return isel;
|
|
}
|
|
|
|
private void GetAgentBounds(DtCrowdAgent ag, ref RcVec3f bmin, ref RcVec3f bmax)
|
|
{
|
|
RcVec3f p = ag.npos;
|
|
float r = ag.option.radius;
|
|
float h = ag.option.height;
|
|
bmin.X = p.X - r;
|
|
bmin.Y = p.Y;
|
|
bmin.Z = p.Z - r;
|
|
bmax.X = p.X + r;
|
|
bmax.Y = p.Y + h;
|
|
bmax.Z = p.Z + r;
|
|
}
|
|
|
|
public void SetMoveTarget(RcVec3f p, bool adjust)
|
|
{
|
|
if (crowd == null)
|
|
return;
|
|
|
|
// Find nearest point on navmesh and set move request to that location.
|
|
DtNavMeshQuery navquery = crowd.GetNavMeshQuery();
|
|
IDtQueryFilter filter = crowd.GetFilter(0);
|
|
RcVec3f halfExtents = crowd.GetQueryExtents();
|
|
|
|
if (adjust)
|
|
{
|
|
// Request velocity
|
|
if (_agentDebug.agent != null)
|
|
{
|
|
RcVec3f vel = CalcVel(_agentDebug.agent.npos, p, _agentDebug.agent.option.maxSpeed);
|
|
crowd.RequestMoveVelocity(_agentDebug.agent, vel);
|
|
}
|
|
else
|
|
{
|
|
foreach (DtCrowdAgent ag in crowd.GetActiveAgents())
|
|
{
|
|
RcVec3f vel = CalcVel(ag.npos, p, ag.option.maxSpeed);
|
|
crowd.RequestMoveVelocity(ag, vel);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
navquery.FindNearestPoly(p, halfExtents, filter, out _moveTargetRef, out _moveTargetPos, out var _);
|
|
if (_agentDebug.agent != null)
|
|
{
|
|
crowd.RequestMoveTarget(_agentDebug.agent, _moveTargetRef, _moveTargetPos);
|
|
}
|
|
else
|
|
{
|
|
foreach (DtCrowdAgent ag in crowd.GetActiveAgents())
|
|
{
|
|
crowd.RequestMoveTarget(ag, _moveTargetRef, _moveTargetPos);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private RcVec3f CalcVel(RcVec3f pos, RcVec3f tgt, float speed)
|
|
{
|
|
RcVec3f vel = tgt.Subtract(pos);
|
|
vel.Y = 0.0f;
|
|
vel.Normalize();
|
|
return vel.Scale(speed);
|
|
}
|
|
|
|
public long GetCrowdUpdateTime()
|
|
{
|
|
return crowdUpdateTime;
|
|
}
|
|
|
|
public void HighlightAgent(DtCrowdAgent agent)
|
|
{
|
|
_agentDebug.agent = agent;
|
|
}
|
|
}
|
|
} |