From c92d0dfdf6b6a03726612f426e1f0e506a899f42 Mon Sep 17 00:00:00 2001 From: Nick Samarin Date: Fri, 18 Jun 2010 23:48:52 +0000 Subject: Added: - obstacle culling for correct simulation in 3d - flag for steering actuator termination on reaching target - path recalculation period - advance by waypoints (for path following) --- release/scripts/ui/properties_game.py | 3 + source/blender/blenkernel/intern/scene.c | 1 + source/blender/editors/space_logic/logic_window.c | 4 + source/blender/makesdna/DNA_actuator_types.h | 6 +- source/blender/makesdna/DNA_scene_types.h | 3 +- source/blender/makesrna/intern/rna_actuator.c | 11 +++ source/blender/makesrna/intern/rna_scene.c | 7 ++ .../gameengine/Converter/KX_ConvertActuators.cpp | 6 +- source/gameengine/Ketsji/KX_NavMeshObject.cpp | 63 ++++++++----- source/gameengine/Ketsji/KX_ObstacleSimulation.cpp | 52 +++++++++-- source/gameengine/Ketsji/KX_ObstacleSimulation.h | 7 +- source/gameengine/Ketsji/KX_Scene.cpp | 2 +- source/gameengine/Ketsji/KX_SteeringActuator.cpp | 102 +++++++++++++++------ source/gameengine/Ketsji/KX_SteeringActuator.h | 9 ++ 14 files changed, 213 insertions(+), 63 deletions(-) diff --git a/release/scripts/ui/properties_game.py b/release/scripts/ui/properties_game.py index dcca9f19051..8cd7e67ba90 100644 --- a/release/scripts/ui/properties_game.py +++ b/release/scripts/ui/properties_game.py @@ -544,6 +544,9 @@ class WORLD_PT_game_physics_obstacles(WorldButtonsPanel): wide_ui = context.region.width > narrowui layout.prop(gs, "obstacle_simulation", text = "Type") + if gs.obstacle_simulation != 'None': + layout.prop(gs, "level_height", text="Level height") + classes = [ PHYSICS_PT_game_physics, diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c index 4b012304e47..1fd30930933 100644 --- a/source/blender/blenkernel/intern/scene.c +++ b/source/blender/blenkernel/intern/scene.c @@ -473,6 +473,7 @@ Scene *add_scene(char *name) sce->gm.matmode = GAME_MAT_MULTITEX; sce->gm.obstacleSimulation= OBSTSIMULATION_NONE; + sce->gm.levelHeight = 2.f; sound_create_scene(sce); diff --git a/source/blender/editors/space_logic/logic_window.c b/source/blender/editors/space_logic/logic_window.c index 208b9aeabc0..c20c6b5144a 100644 --- a/source/blender/editors/space_logic/logic_window.c +++ b/source/blender/editors/space_logic/logic_window.c @@ -4276,6 +4276,10 @@ static void draw_actuator_steering(uiLayout *layout, PointerRNA *ptr) row = uiLayoutRow(layout, 0); uiItemR(row, ptr, "acceleration", 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); } diff --git a/source/blender/makesdna/DNA_actuator_types.h b/source/blender/makesdna/DNA_actuator_types.h index 89709948f50..9d26632769b 100644 --- a/source/blender/makesdna/DNA_actuator_types.h +++ b/source/blender/makesdna/DNA_actuator_types.h @@ -215,12 +215,14 @@ typedef struct bArmatureActuator { } bArmatureActuator; typedef struct bSteeringActuator { - char pad[4]; + char pad[7]; + char flag; int type; /* 0=seek, 1=flee, 2=path following */ float dist; float velocity; float acceleration; float turnspeed; + int updateTime; struct Object *target; struct Object *navmesh; } bSteeringActuator; @@ -518,6 +520,8 @@ typedef struct FreeCamera { #define ACT_STEERING_SEEK 0 #define ACT_STEERING_FLEE 1 #define ACT_STEERING_PATHFOLLOWING 2 +/* steeringactuator->flag */ +#define ACT_STEERING_SELFTERMINATED 1 #endif diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h index de86e3281fb..8f92c9f5b2f 100644 --- a/source/blender/makesdna/DNA_scene_types.h +++ b/source/blender/makesdna/DNA_scene_types.h @@ -451,11 +451,12 @@ typedef struct GameData { * bit 3: (gameengine): Activity culling is enabled. * 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 physicsEngine; short ticrate, maxlogicstep, physubstep, maxphystep; short obstacleSimulation; + float levelHeight; /* standalone player */ struct GameFraming framing; diff --git a/source/blender/makesrna/intern/rna_actuator.c b/source/blender/makesrna/intern/rna_actuator.c index df3b59b54bc..ae6e561defa 100644 --- a/source/blender/makesrna/intern/rna_actuator.c +++ b/source/blender/makesrna/intern/rna_actuator.c @@ -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_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); RNA_def_property_struct_type(prop, "Object"); RNA_def_property_pointer_sdna(prop, NULL, "navmesh"); diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c index cde0e910723..f91ac0517f9 100644 --- a/source/blender/makesrna/intern/rna_scene.c +++ b/source/blender/makesrna/intern/rna_scene.c @@ -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_ui_text(prop, "Obstacle simulation", "Simulation used for obstacle avoidance in the game engine"); 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) diff --git a/source/gameengine/Converter/KX_ConvertActuators.cpp b/source/gameengine/Converter/KX_ConvertActuators.cpp index 0bcd74c9e8b..e5c39da1545 100644 --- a/source/gameengine/Converter/KX_ConvertActuators.cpp +++ b/source/gameengine/Converter/KX_ConvertActuators.cpp @@ -1057,10 +1057,12 @@ void BL_ConvertActuators(char* maggiename, break; } + bool selfTerminated = (stAct->flag & ACT_STEERING_SELFTERMINATED) !=0; KX_SteeringActuator *tmpstact = new KX_SteeringActuator(gameobj, mode, targetob, navmeshob,stAct->dist, - stAct->velocity, stAct->acceleration, stAct->turnspeed, - scene->GetObstacleSimulation()); + stAct->velocity, stAct->acceleration, stAct->turnspeed, + selfTerminated, stAct->updateTime, + scene->GetObstacleSimulation()); baseact = tmpstact; break; } diff --git a/source/gameengine/Ketsji/KX_NavMeshObject.cpp b/source/gameengine/Ketsji/KX_NavMeshObject.cpp index 6c2301b6d8c..2c423558768 100644 --- a/source/gameengine/Ketsji/KX_NavMeshObject.cpp +++ b/source/gameengine/Ketsji/KX_NavMeshObject.cpp @@ -333,32 +333,53 @@ void KX_NavMeshObject::DrawNavMesh() if (!m_navMesh) return; 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); - const dtStatPolyDetail* pd = m_navMesh->getPolyDetail(i); - - for (int j = 0; j < pd->ntris; ++j) + case WALLS : + for (int pi=0; pigetPolyCount(); pi++) { - 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); + const dtStatPoly* poly = m_navMesh->getPoly(pi); + + for (int i = 0, j = (int)poly->nv-1; i < (int)poly->nv; j = i++) + { + 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 k=0; k<3; k++) - KX_RasterizerDrawDebugLine(tri[k], tri[(k+1)%3], color); + 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; } } diff --git a/source/gameengine/Ketsji/KX_ObstacleSimulation.cpp b/source/gameengine/Ketsji/KX_ObstacleSimulation.cpp index 5994ebdc80c..f8b064c39f8 100644 --- a/source/gameengine/Ketsji/KX_ObstacleSimulation.cpp +++ b/source/gameengine/Ketsji/KX_ObstacleSimulation.cpp @@ -166,7 +166,8 @@ static float interpolateToi(float a, const float* dir, const float* toi, const i 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) { 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_minToi(0.5f), m_maxToi(1.2f), @@ -360,10 +400,10 @@ void KX_ObstacleSimulationTOI::AdjustObstacleVelocity(KX_Obstacle* activeObst, K for (int i = 0; i < nobs; ++i) { KX_Obstacle* ob = m_obstacles[i]; - if ( (ob==activeObst) || - (ob->m_type==KX_OBSTACLE_NAV_MESH && ob->m_gameObj!=activeNavMeshObj) ) + bool res = FilterObstacle(activeObst, activeNavMeshObj, ob); + if (!res) continue; - + float htmin,htmax; if (ob->m_type == KX_OBSTACLE_CIRCLE) diff --git a/source/gameengine/Ketsji/KX_ObstacleSimulation.h b/source/gameengine/Ketsji/KX_ObstacleSimulation.h index 8463820b7c8..4b323035823 100644 --- a/source/gameengine/Ketsji/KX_ObstacleSimulation.h +++ b/source/gameengine/Ketsji/KX_ObstacleSimulation.h @@ -73,9 +73,12 @@ class KX_ObstacleSimulation protected: std::vector m_obstacles; + MT_Scalar m_levelHeight; + virtual KX_Obstacle* CreateObstacle(); + bool FilterObstacle(KX_Obstacle* activeObstacle, KX_NavMeshObject* activeNavMeshObj, KX_Obstacle* otherObstacle); public: - KX_ObstacleSimulation(); + KX_ObstacleSimulation(MT_Scalar levelHeight); virtual ~KX_ObstacleSimulation(); void DrawObstacles(); @@ -114,7 +117,7 @@ protected: std::vector m_toiCircles; // TOI circles (one per active agent) virtual KX_Obstacle* CreateObstacle(); public: - KX_ObstacleSimulationTOI(); + KX_ObstacleSimulationTOI(MT_Scalar levelHeight); ~KX_ObstacleSimulationTOI(); virtual void AdjustObstacleVelocity(KX_Obstacle* activeObst, KX_NavMeshObject* activeNavMeshObj, MT_Vector3& velocity, MT_Scalar maxDeltaSpeed,MT_Scalar maxDeltaAngle); diff --git a/source/gameengine/Ketsji/KX_Scene.cpp b/source/gameengine/Ketsji/KX_Scene.cpp index 825d9317185..c7398e8e3ee 100644 --- a/source/gameengine/Ketsji/KX_Scene.cpp +++ b/source/gameengine/Ketsji/KX_Scene.cpp @@ -214,7 +214,7 @@ KX_Scene::KX_Scene(class SCA_IInputDevice* keyboarddevice, switch (scene->gm.obstacleSimulation) { case OBSTSIMULATION_TOI: - m_obstacleSimulation = new KX_ObstacleSimulationTOI; + m_obstacleSimulation = new KX_ObstacleSimulationTOI((MT_Scalar)scene->gm.levelHeight); break; default: m_obstacleSimulation = NULL; diff --git a/source/gameengine/Ketsji/KX_SteeringActuator.cpp b/source/gameengine/Ketsji/KX_SteeringActuator.cpp index 786c52413c9..3bf8a745177 100644 --- a/source/gameengine/Ketsji/KX_SteeringActuator.cpp +++ b/source/gameengine/Ketsji/KX_SteeringActuator.cpp @@ -52,6 +52,8 @@ KX_SteeringActuator::KX_SteeringActuator(SCA_IObject *gameobj, MT_Scalar velocity, MT_Scalar acceleration, MT_Scalar turnspeed, + bool isSelfTerminated, + int pathUpdatePeriod, KX_ObstacleSimulation* simulation) : SCA_IActuator(gameobj, KX_ACT_STEERING), m_mode(mode), @@ -60,10 +62,14 @@ KX_SteeringActuator::KX_SteeringActuator(SCA_IObject *gameobj, m_velocity(velocity), m_acceleration(acceleration), m_turnspeed(turnspeed), + m_isSelfTerminated(isSelfTerminated), + m_pathUpdatePeriod(pathUpdatePeriod), m_updateTime(0), - m_isActive(false), - m_simulation(simulation), - m_obstacle(NULL) + m_isActive(false), + m_simulation(simulation), + m_obstacle(NULL), + m_pathLen(0), + m_wayPointIdx(-1) { m_navmesh = static_cast(navmesh); if (m_navmesh) @@ -147,6 +153,7 @@ bool KX_SteeringActuator::Update(double curtime, bool frame) if (m_posevent && !m_isActive) { delta = 0; + m_pathUpdateTime = -1; m_updateTime = curtime; m_isActive = true; } @@ -156,61 +163,89 @@ bool KX_SteeringActuator::Update(double curtime, bool frame) RemoveAllEvents(); - if (bNegativeEvent || !delta) - return false; // do nothing on negative events + if (!delta) + return true; - if (!m_target) - return false; + if (bNegativeEvent || !m_target) + return false; // do nothing on negative events KX_GameObject *obj = (KX_GameObject*) GetParent(); const MT_Point3& mypos = obj->NodeGetWorldPosition(); const MT_Point3& targpos = m_target->NodeGetWorldPosition(); MT_Vector3 vectotarg = targpos - mypos; MT_Vector3 steervec = MT_Vector3(0, 0, 0); - bool apply_steerforce = true; + bool apply_steerforce = false; + bool terminate = true; switch (m_mode) { case KX_STEERING_SEEK: if (vectotarg.length2()>m_distance*m_distance) { - apply_steerforce = true; + terminate = false; steervec = vectotarg; steervec.normalize(); + apply_steerforce = true; } break; case KX_STEERING_FLEE: if (vectotarg.length2()m_distance*m_distance) { - static const int MAX_PATH_LENGTH = 128; - static const MT_Vector3 PATH_COLOR(1,0,0); + terminate = false; + + static const MT_Scalar WAYPOINT_RADIUS(1.); - float path[MAX_PATH_LENGTH*3]; - 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_navmesh->DrawPath(path, pathlen, PATH_COLOR); + m_pathUpdateTime = curtime; + m_pathLen = m_navmesh->FindPath(mypos, targpos, m_path, MAX_PATH_LENGTH); + m_wayPointIdx = m_pathLen > 1 ? 1 : -1; + } + + if (m_wayPointIdx>0) + { + MT_Vector3 waypoint(&m_path[3*m_wayPointIdx]); + if ((waypoint-mypos).length2()=m_pathLen) + { + m_wayPointIdx = -1; + terminate = true; + } + else + waypoint.setValue(&m_path[3*m_wayPointIdx]); + } - apply_steerforce = true; - MT_Vector3 waypoint(&path[3]); steervec = waypoint - mypos; - steervec.z() = 0; - steervec.normalize(); - } + apply_steerforce = true; + + //debug draw + static const MT_Vector3 PATH_COLOR(1,0,0); + m_navmesh->DrawPath(m_path, m_pathLen, PATH_COLOR); + + } + } break; } if (apply_steerforce) { + bool isdyna = obj->IsDynamic(); + if (isdyna) + steervec.z() = 0; + if (!steervec.fuzzyZero()) + steervec.normalize(); MT_Vector3 newvel = m_velocity*steervec; //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.)); } - //temporary solution: set 2D steering velocity directly to obj - //correct way is to apply physical force - //MT_Vector3 movement = delta*m_velocity*steervec; - //obj->ApplyMovement(movement, false); - MT_Vector3 curvel = obj->GetLinearVelocity(); - newvel.z() = curvel.z(); - obj->setLinearVelocity(newvel, false); + if (isdyna) + { + //temporary solution: set 2D steering velocity directly to obj + //correct way is to apply physical force + MT_Vector3 curvel = obj->GetLinearVelocity(); + newvel.z() = curvel.z(); + obj->setLinearVelocity(newvel, false); + } + else + { + MT_Vector3 movement = delta*newvel; + obj->ApplyMovement(movement, false); + } } + + if (terminate && m_isSelfTerminated) + return false; } return true; diff --git a/source/gameengine/Ketsji/KX_SteeringActuator.h b/source/gameengine/Ketsji/KX_SteeringActuator.h index b303a2a8037..5ac5a9f37c4 100644 --- a/source/gameengine/Ketsji/KX_SteeringActuator.h +++ b/source/gameengine/Ketsji/KX_SteeringActuator.h @@ -43,6 +43,7 @@ class KX_GameObject; class KX_NavMeshObject; struct KX_Obstacle; class KX_ObstacleSimulation; +const int MAX_PATH_LENGTH = 128; class KX_SteeringActuator : public SCA_IActuator { @@ -61,6 +62,12 @@ class KX_SteeringActuator : public SCA_IActuator KX_Obstacle* m_obstacle; double m_updateTime; 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: enum KX_STEERINGACT_MODE { @@ -79,6 +86,8 @@ public: MT_Scalar velocity, MT_Scalar acceleration, MT_Scalar turnspeed, + bool isSelfTerminated, + int pathUpdatePeriod, KX_ObstacleSimulation* simulation); virtual ~KX_SteeringActuator(); virtual bool Update(double curtime, bool frame); -- cgit v1.2.3