- obstacle culling for correct simulation in 3d
- flag for steering actuator termination on reaching target
- path recalculation period
- advance by waypoints (for path following)
This commit is contained in:
Nick Samarin 2010-06-18 23:48:52 +00:00
parent 700c32e738
commit c92d0dfdf6
14 changed files with 215 additions and 65 deletions

@ -544,6 +544,9 @@ class WORLD_PT_game_physics_obstacles(WorldButtonsPanel):
wide_ui = context.region.width > narrowui wide_ui = context.region.width > narrowui
layout.prop(gs, "obstacle_simulation", text = "Type") layout.prop(gs, "obstacle_simulation", text = "Type")
if gs.obstacle_simulation != 'None':
layout.prop(gs, "level_height", text="Level height")
classes = [ classes = [
PHYSICS_PT_game_physics, PHYSICS_PT_game_physics,

@ -473,6 +473,7 @@ Scene *add_scene(char *name)
sce->gm.matmode = GAME_MAT_MULTITEX; sce->gm.matmode = GAME_MAT_MULTITEX;
sce->gm.obstacleSimulation= OBSTSIMULATION_NONE; sce->gm.obstacleSimulation= OBSTSIMULATION_NONE;
sce->gm.levelHeight = 2.f;
sound_create_scene(sce); sound_create_scene(sce);

@ -4276,6 +4276,10 @@ static void draw_actuator_steering(uiLayout *layout, PointerRNA *ptr)
row = uiLayoutRow(layout, 0); row = uiLayoutRow(layout, 0);
uiItemR(row, ptr, "acceleration", 0, NULL, 0); uiItemR(row, ptr, "acceleration", 0, NULL, 0);
uiItemR(row, ptr, "turnspeed", 0, NULL, 0); uiItemR(row, ptr, "turnspeed", 0, NULL, 0);
row = uiLayoutRow(layout, 0);
uiItemR(row, ptr, "selfterminated", 0, NULL, 0);
if (RNA_enum_get(ptr, "mode")==ACT_STEERING_PATHFOLLOWING)
uiItemR(row, ptr, "updateperiod", 0, NULL, 0);
} }

@ -215,12 +215,14 @@ typedef struct bArmatureActuator {
} bArmatureActuator; } bArmatureActuator;
typedef struct bSteeringActuator { typedef struct bSteeringActuator {
char pad[4]; char pad[7];
char flag;
int type; /* 0=seek, 1=flee, 2=path following */ int type; /* 0=seek, 1=flee, 2=path following */
float dist; float dist;
float velocity; float velocity;
float acceleration; float acceleration;
float turnspeed; float turnspeed;
int updateTime;
struct Object *target; struct Object *target;
struct Object *navmesh; struct Object *navmesh;
} bSteeringActuator; } bSteeringActuator;
@ -518,6 +520,8 @@ typedef struct FreeCamera {
#define ACT_STEERING_SEEK 0 #define ACT_STEERING_SEEK 0
#define ACT_STEERING_FLEE 1 #define ACT_STEERING_FLEE 1
#define ACT_STEERING_PATHFOLLOWING 2 #define ACT_STEERING_PATHFOLLOWING 2
/* steeringactuator->flag */
#define ACT_STEERING_SELFTERMINATED 1
#endif #endif

@ -451,11 +451,12 @@ typedef struct GameData {
* bit 3: (gameengine): Activity culling is enabled. * bit 3: (gameengine): Activity culling is enabled.
* bit 5: (gameengine) : enable Bullet DBVT tree for view frustrum culling * bit 5: (gameengine) : enable Bullet DBVT tree for view frustrum culling
*/ */
short mode, flag, matmode, pad[6]; short mode, flag, matmode/*, pad[2]*/;
short occlusionRes; /* resolution of occlusion Z buffer in pixel */ short occlusionRes; /* resolution of occlusion Z buffer in pixel */
short physicsEngine; short physicsEngine;
short ticrate, maxlogicstep, physubstep, maxphystep; short ticrate, maxlogicstep, physubstep, maxphystep;
short obstacleSimulation; short obstacleSimulation;
float levelHeight;
/* standalone player */ /* standalone player */
struct GameFraming framing; struct GameFraming framing;

@ -1897,6 +1897,17 @@ static void rna_def_steering_actuator(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Target Object", "Set target object"); RNA_def_property_ui_text(prop, "Target Object", "Set target object");
RNA_def_property_update(prop, NC_LOGIC, NULL); RNA_def_property_update(prop, NC_LOGIC, NULL);
prop= RNA_def_property(srna, "selfterminated", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", ACT_STEERING_SELFTERMINATED);
RNA_def_property_ui_text(prop, "Self terminated", "Terminate when target is reached");
RNA_def_property_update(prop, NC_LOGIC, NULL);
prop= RNA_def_property(srna, "updateperiod", PROP_INT, PROP_NONE);
RNA_def_property_int_sdna(prop, NULL, "updateTime");
RNA_def_property_ui_range(prop, -1, 100000, 1, 1);
RNA_def_property_ui_text(prop, "Update period", "Path update period");
RNA_def_property_update(prop, NC_LOGIC, NULL);
prop= RNA_def_property(srna, "navmesh", PROP_POINTER, PROP_NONE); prop= RNA_def_property(srna, "navmesh", PROP_POINTER, PROP_NONE);
RNA_def_property_struct_type(prop, "Object"); RNA_def_property_struct_type(prop, "Object");
RNA_def_property_pointer_sdna(prop, NULL, "navmesh"); RNA_def_property_pointer_sdna(prop, NULL, "navmesh");

@ -1760,6 +1760,13 @@ static void rna_def_scene_game_data(BlenderRNA *brna)
RNA_def_property_enum_items(prop, obstacle_simulation_items); RNA_def_property_enum_items(prop, obstacle_simulation_items);
RNA_def_property_ui_text(prop, "Obstacle simulation", "Simulation used for obstacle avoidance in the game engine"); RNA_def_property_ui_text(prop, "Obstacle simulation", "Simulation used for obstacle avoidance in the game engine");
RNA_def_property_update(prop, NC_SCENE, NULL); RNA_def_property_update(prop, NC_SCENE, NULL);
prop= RNA_def_property(srna, "level_height", PROP_FLOAT, PROP_ACCELERATION);
RNA_def_property_float_sdna(prop, NULL, "levelHeight");
RNA_def_property_range(prop, 0.0f, 200.0f);
RNA_def_property_ui_text(prop, "Level height", "Max difference in heights of obstacles to enable their interaction");
RNA_def_property_update(prop, NC_SCENE, NULL);
} }
static void rna_def_scene_render_layer(BlenderRNA *brna) static void rna_def_scene_render_layer(BlenderRNA *brna)

@ -1057,10 +1057,12 @@ void BL_ConvertActuators(char* maggiename,
break; break;
} }
bool selfTerminated = (stAct->flag & ACT_STEERING_SELFTERMINATED) !=0;
KX_SteeringActuator *tmpstact KX_SteeringActuator *tmpstact
= new KX_SteeringActuator(gameobj, mode, targetob, navmeshob,stAct->dist, = new KX_SteeringActuator(gameobj, mode, targetob, navmeshob,stAct->dist,
stAct->velocity, stAct->acceleration, stAct->turnspeed, stAct->velocity, stAct->acceleration, stAct->turnspeed,
scene->GetObstacleSimulation()); selfTerminated, stAct->updateTime,
scene->GetObstacleSimulation());
baseact = tmpstact; baseact = tmpstact;
break; break;
} }

@ -334,31 +334,52 @@ void KX_NavMeshObject::DrawNavMesh()
return; return;
MT_Vector3 color(0.f, 0.f, 0.f); MT_Vector3 color(0.f, 0.f, 0.f);
for (int i = 0; i < m_navMesh->getPolyDetailCount(); ++i) enum RenderMode {DETAILED_TRIS, WALLS};
static const RenderMode renderMode = DETAILED_TRIS;
switch (renderMode)
{ {
const dtStatPoly* p = m_navMesh->getPoly(i); case WALLS :
const dtStatPolyDetail* pd = m_navMesh->getPolyDetail(i); for (int pi=0; pi<m_navMesh->getPolyCount(); pi++)
for (int j = 0; j < pd->ntris; ++j)
{ {
const unsigned char* t = m_navMesh->getDetailTri(pd->tbase+j); const dtStatPoly* poly = m_navMesh->getPoly(pi);
MT_Vector3 tri[3];
for (int k = 0; k < 3; ++k)
{
const float* v;
if (t[k] < p->nv)
v = m_navMesh->getVertex(p->v[t[k]]);
else
v = m_navMesh->getDetailVertex(pd->vbase+(t[k]-p->nv));
float pos[3];
vcopy(pos, v);
flipAxes(pos);
tri[k].setValue(pos);
}
for (int k=0; k<3; k++) for (int i = 0, j = (int)poly->nv-1; i < (int)poly->nv; j = i++)
KX_RasterizerDrawDebugLine(tri[k], tri[(k+1)%3], color); {
if (poly->n[j]) continue;
const float* vj = m_navMesh->getVertex(poly->v[j]);
const float* vi = m_navMesh->getVertex(poly->v[i]);
KX_RasterizerDrawDebugLine(MT_Vector3(vj[0], vj[2], vj[1]), MT_Vector3(vi[0], vi[2], vi[1]), color);
}
} }
break;
case DETAILED_TRIS :
for (int i = 0; i < m_navMesh->getPolyDetailCount(); ++i)
{
const dtStatPoly* p = m_navMesh->getPoly(i);
const dtStatPolyDetail* pd = m_navMesh->getPolyDetail(i);
for (int j = 0; j < pd->ntris; ++j)
{
const unsigned char* t = m_navMesh->getDetailTri(pd->tbase+j);
MT_Vector3 tri[3];
for (int k = 0; k < 3; ++k)
{
const float* v;
if (t[k] < p->nv)
v = m_navMesh->getVertex(p->v[t[k]]);
else
v = m_navMesh->getDetailVertex(pd->vbase+(t[k]-p->nv));
float pos[3];
vcopy(pos, v);
flipAxes(pos);
tri[k].setValue(pos);
}
for (int k=0; k<3; k++)
KX_RasterizerDrawDebugLine(tri[k], tri[(k+1)%3], color);
}
}
break;
} }
} }

@ -166,7 +166,8 @@ static float interpolateToi(float a, const float* dir, const float* toi, const i
return 0; return 0;
} }
KX_ObstacleSimulation::KX_ObstacleSimulation() KX_ObstacleSimulation::KX_ObstacleSimulation(MT_Scalar levelHeight)
: m_levelHeight(levelHeight)
{ {
} }
@ -285,12 +286,51 @@ void KX_ObstacleSimulation::DrawObstacles()
else if (m_obstacles[i]->m_shape==KX_OBSTACLE_CIRCLE) else if (m_obstacles[i]->m_shape==KX_OBSTACLE_CIRCLE)
{ {
KX_RasterizerDrawDebugCircle(m_obstacles[i]->m_pos, m_obstacles[i]->m_rad, bluecolor, KX_RasterizerDrawDebugCircle(m_obstacles[i]->m_pos, m_obstacles[i]->m_rad, bluecolor,
normal.normalized(), SECTORS_NUM); normal, SECTORS_NUM);
} }
} }
} }
KX_ObstacleSimulationTOI::KX_ObstacleSimulationTOI(): static MT_Point3 nearestPointToObstacle(MT_Point3& pos ,KX_Obstacle* obstacle)
{
switch (obstacle->m_shape)
{
case KX_OBSTACLE_SEGMENT :
{
MT_Vector3 ab = obstacle->m_pos2 - obstacle->m_pos;
if (!ab.fuzzyZero())
{
MT_Vector3 abdir = ab.normalized();
MT_Vector3 v = pos - obstacle->m_pos;
MT_Scalar proj = abdir.dot(v);
CLAMP(proj, 0, ab.length());
MT_Point3 res = obstacle->m_pos + abdir*proj;
return res;
}
}
case KX_OBSTACLE_CIRCLE :
default:
return obstacle->m_pos;
}
}
bool KX_ObstacleSimulation::FilterObstacle(KX_Obstacle* activeObst, KX_NavMeshObject* activeNavMeshObj, KX_Obstacle* otherObst)
{
//filter obstacles by type
if ( (otherObst == activeObst) ||
(otherObst->m_type==KX_OBSTACLE_NAV_MESH && otherObst->m_gameObj!=activeNavMeshObj) )
return false;
//filter obstacles by position
MT_Point3 p = nearestPointToObstacle(activeObst->m_pos, otherObst);
if ( fabs(activeObst->m_pos.z() - p.z()) > m_levelHeight)
return false;
return true;
}
KX_ObstacleSimulationTOI::KX_ObstacleSimulationTOI(MT_Scalar levelHeight):
KX_ObstacleSimulation(levelHeight),
m_avoidSteps(32), m_avoidSteps(32),
m_minToi(0.5f), m_minToi(0.5f),
m_maxToi(1.2f), m_maxToi(1.2f),
@ -360,8 +400,8 @@ void KX_ObstacleSimulationTOI::AdjustObstacleVelocity(KX_Obstacle* activeObst, K
for (int i = 0; i < nobs; ++i) for (int i = 0; i < nobs; ++i)
{ {
KX_Obstacle* ob = m_obstacles[i]; KX_Obstacle* ob = m_obstacles[i];
if ( (ob==activeObst) || bool res = FilterObstacle(activeObst, activeNavMeshObj, ob);
(ob->m_type==KX_OBSTACLE_NAV_MESH && ob->m_gameObj!=activeNavMeshObj) ) if (!res)
continue; continue;
float htmin,htmax; float htmin,htmax;

@ -73,9 +73,12 @@ class KX_ObstacleSimulation
protected: protected:
std::vector<KX_Obstacle*> m_obstacles; std::vector<KX_Obstacle*> m_obstacles;
MT_Scalar m_levelHeight;
virtual KX_Obstacle* CreateObstacle(); virtual KX_Obstacle* CreateObstacle();
bool FilterObstacle(KX_Obstacle* activeObstacle, KX_NavMeshObject* activeNavMeshObj, KX_Obstacle* otherObstacle);
public: public:
KX_ObstacleSimulation(); KX_ObstacleSimulation(MT_Scalar levelHeight);
virtual ~KX_ObstacleSimulation(); virtual ~KX_ObstacleSimulation();
void DrawObstacles(); void DrawObstacles();
@ -114,7 +117,7 @@ protected:
std::vector<TOICircle*> m_toiCircles; // TOI circles (one per active agent) std::vector<TOICircle*> m_toiCircles; // TOI circles (one per active agent)
virtual KX_Obstacle* CreateObstacle(); virtual KX_Obstacle* CreateObstacle();
public: public:
KX_ObstacleSimulationTOI(); KX_ObstacleSimulationTOI(MT_Scalar levelHeight);
~KX_ObstacleSimulationTOI(); ~KX_ObstacleSimulationTOI();
virtual void AdjustObstacleVelocity(KX_Obstacle* activeObst, KX_NavMeshObject* activeNavMeshObj, virtual void AdjustObstacleVelocity(KX_Obstacle* activeObst, KX_NavMeshObject* activeNavMeshObj,
MT_Vector3& velocity, MT_Scalar maxDeltaSpeed,MT_Scalar maxDeltaAngle); MT_Vector3& velocity, MT_Scalar maxDeltaSpeed,MT_Scalar maxDeltaAngle);

@ -214,7 +214,7 @@ KX_Scene::KX_Scene(class SCA_IInputDevice* keyboarddevice,
switch (scene->gm.obstacleSimulation) switch (scene->gm.obstacleSimulation)
{ {
case OBSTSIMULATION_TOI: case OBSTSIMULATION_TOI:
m_obstacleSimulation = new KX_ObstacleSimulationTOI; m_obstacleSimulation = new KX_ObstacleSimulationTOI((MT_Scalar)scene->gm.levelHeight);
break; break;
default: default:
m_obstacleSimulation = NULL; m_obstacleSimulation = NULL;

@ -52,6 +52,8 @@ KX_SteeringActuator::KX_SteeringActuator(SCA_IObject *gameobj,
MT_Scalar velocity, MT_Scalar velocity,
MT_Scalar acceleration, MT_Scalar acceleration,
MT_Scalar turnspeed, MT_Scalar turnspeed,
bool isSelfTerminated,
int pathUpdatePeriod,
KX_ObstacleSimulation* simulation) : KX_ObstacleSimulation* simulation) :
SCA_IActuator(gameobj, KX_ACT_STEERING), SCA_IActuator(gameobj, KX_ACT_STEERING),
m_mode(mode), m_mode(mode),
@ -60,10 +62,14 @@ KX_SteeringActuator::KX_SteeringActuator(SCA_IObject *gameobj,
m_velocity(velocity), m_velocity(velocity),
m_acceleration(acceleration), m_acceleration(acceleration),
m_turnspeed(turnspeed), m_turnspeed(turnspeed),
m_isSelfTerminated(isSelfTerminated),
m_pathUpdatePeriod(pathUpdatePeriod),
m_updateTime(0), m_updateTime(0),
m_isActive(false), m_isActive(false),
m_simulation(simulation), m_simulation(simulation),
m_obstacle(NULL) m_obstacle(NULL),
m_pathLen(0),
m_wayPointIdx(-1)
{ {
m_navmesh = static_cast<KX_NavMeshObject*>(navmesh); m_navmesh = static_cast<KX_NavMeshObject*>(navmesh);
if (m_navmesh) if (m_navmesh)
@ -147,6 +153,7 @@ bool KX_SteeringActuator::Update(double curtime, bool frame)
if (m_posevent && !m_isActive) if (m_posevent && !m_isActive)
{ {
delta = 0; delta = 0;
m_pathUpdateTime = -1;
m_updateTime = curtime; m_updateTime = curtime;
m_isActive = true; m_isActive = true;
} }
@ -156,61 +163,89 @@ bool KX_SteeringActuator::Update(double curtime, bool frame)
RemoveAllEvents(); RemoveAllEvents();
if (bNegativeEvent || !delta) if (!delta)
return false; // do nothing on negative events return true;
if (!m_target) if (bNegativeEvent || !m_target)
return false; return false; // do nothing on negative events
KX_GameObject *obj = (KX_GameObject*) GetParent(); KX_GameObject *obj = (KX_GameObject*) GetParent();
const MT_Point3& mypos = obj->NodeGetWorldPosition(); const MT_Point3& mypos = obj->NodeGetWorldPosition();
const MT_Point3& targpos = m_target->NodeGetWorldPosition(); const MT_Point3& targpos = m_target->NodeGetWorldPosition();
MT_Vector3 vectotarg = targpos - mypos; MT_Vector3 vectotarg = targpos - mypos;
MT_Vector3 steervec = MT_Vector3(0, 0, 0); MT_Vector3 steervec = MT_Vector3(0, 0, 0);
bool apply_steerforce = true; bool apply_steerforce = false;
bool terminate = true;
switch (m_mode) { switch (m_mode) {
case KX_STEERING_SEEK: case KX_STEERING_SEEK:
if (vectotarg.length2()>m_distance*m_distance) if (vectotarg.length2()>m_distance*m_distance)
{ {
apply_steerforce = true; terminate = false;
steervec = vectotarg; steervec = vectotarg;
steervec.normalize(); steervec.normalize();
apply_steerforce = true;
} }
break; break;
case KX_STEERING_FLEE: case KX_STEERING_FLEE:
if (vectotarg.length2()<m_distance*m_distance) if (vectotarg.length2()<m_distance*m_distance)
{ {
apply_steerforce = true; terminate = false;
steervec = -vectotarg; steervec = -vectotarg;
steervec.normalize(); steervec.normalize();
apply_steerforce = true;
} }
break; break;
case KX_STEERING_PATHFOLLOWING: case KX_STEERING_PATHFOLLOWING:
if (m_navmesh && vectotarg.length2()>m_distance*m_distance) if (m_navmesh && vectotarg.length2()>m_distance*m_distance)
{ {
static const int MAX_PATH_LENGTH = 128; terminate = false;
static const MT_Vector3 PATH_COLOR(1,0,0);
float path[MAX_PATH_LENGTH*3]; static const MT_Scalar WAYPOINT_RADIUS(1.);
int pathlen = m_navmesh->FindPath(mypos, targpos, path, MAX_PATH_LENGTH);
if (pathlen > 1) if (m_pathUpdateTime<0 || (m_pathUpdatePeriod>=0 &&
curtime - m_pathUpdateTime>((double)m_pathUpdatePeriod/1000)))
{ {
//debug draw m_pathUpdateTime = curtime;
m_navmesh->DrawPath(path, pathlen, PATH_COLOR); m_pathLen = m_navmesh->FindPath(mypos, targpos, m_path, MAX_PATH_LENGTH);
m_wayPointIdx = m_pathLen > 1 ? 1 : -1;
apply_steerforce = true;
MT_Vector3 waypoint(&path[3]);
steervec = waypoint - mypos;
steervec.z() = 0;
steervec.normalize();
} }
if (m_wayPointIdx>0)
{
MT_Vector3 waypoint(&m_path[3*m_wayPointIdx]);
if ((waypoint-mypos).length2()<WAYPOINT_RADIUS*WAYPOINT_RADIUS)
{
m_wayPointIdx++;
if (m_wayPointIdx>=m_pathLen)
{
m_wayPointIdx = -1;
terminate = true;
}
else
waypoint.setValue(&m_path[3*m_wayPointIdx]);
}
steervec = waypoint - mypos;
apply_steerforce = true;
//debug draw
static const MT_Vector3 PATH_COLOR(1,0,0);
m_navmesh->DrawPath(m_path, m_pathLen, PATH_COLOR);
}
} }
break; break;
} }
if (apply_steerforce) if (apply_steerforce)
{ {
bool isdyna = obj->IsDynamic();
if (isdyna)
steervec.z() = 0;
if (!steervec.fuzzyZero())
steervec.normalize();
MT_Vector3 newvel = m_velocity*steervec; MT_Vector3 newvel = m_velocity*steervec;
//adjust velocity to avoid obstacles //adjust velocity to avoid obstacles
@ -222,14 +257,23 @@ bool KX_SteeringActuator::Update(double curtime, bool frame)
KX_RasterizerDrawDebugLine(mypos, mypos + newvel, MT_Vector3(0.,1.,0.)); KX_RasterizerDrawDebugLine(mypos, mypos + newvel, MT_Vector3(0.,1.,0.));
} }
//temporary solution: set 2D steering velocity directly to obj if (isdyna)
//correct way is to apply physical force {
//MT_Vector3 movement = delta*m_velocity*steervec; //temporary solution: set 2D steering velocity directly to obj
//obj->ApplyMovement(movement, false); //correct way is to apply physical force
MT_Vector3 curvel = obj->GetLinearVelocity(); MT_Vector3 curvel = obj->GetLinearVelocity();
newvel.z() = curvel.z(); newvel.z() = curvel.z();
obj->setLinearVelocity(newvel, false); obj->setLinearVelocity(newvel, false);
}
else
{
MT_Vector3 movement = delta*newvel;
obj->ApplyMovement(movement, false);
}
} }
if (terminate && m_isSelfTerminated)
return false;
} }
return true; return true;

@ -43,6 +43,7 @@ class KX_GameObject;
class KX_NavMeshObject; class KX_NavMeshObject;
struct KX_Obstacle; struct KX_Obstacle;
class KX_ObstacleSimulation; class KX_ObstacleSimulation;
const int MAX_PATH_LENGTH = 128;
class KX_SteeringActuator : public SCA_IActuator class KX_SteeringActuator : public SCA_IActuator
{ {
@ -61,6 +62,12 @@ class KX_SteeringActuator : public SCA_IActuator
KX_Obstacle* m_obstacle; KX_Obstacle* m_obstacle;
double m_updateTime; double m_updateTime;
bool m_isActive; bool m_isActive;
bool m_isSelfTerminated;
float m_path[MAX_PATH_LENGTH*3];
int m_pathLen;
int m_pathUpdatePeriod;
double m_pathUpdateTime;
int m_wayPointIdx;
public: public:
enum KX_STEERINGACT_MODE enum KX_STEERINGACT_MODE
{ {
@ -79,6 +86,8 @@ public:
MT_Scalar velocity, MT_Scalar velocity,
MT_Scalar acceleration, MT_Scalar acceleration,
MT_Scalar turnspeed, MT_Scalar turnspeed,
bool isSelfTerminated,
int pathUpdatePeriod,
KX_ObstacleSimulation* simulation); KX_ObstacleSimulation* simulation);
virtual ~KX_SteeringActuator(); virtual ~KX_SteeringActuator();
virtual bool Update(double curtime, bool frame); virtual bool Update(double curtime, bool frame);