diff --git a/release/scripts/startup/bl_ui/properties_game.py b/release/scripts/startup/bl_ui/properties_game.py index 5ff49a7d369..bf0c9eb762a 100644 --- a/release/scripts/startup/bl_ui/properties_game.py +++ b/release/scripts/startup/bl_ui/properties_game.py @@ -190,6 +190,14 @@ class PHYSICS_PT_game_physics(PhysicsButtonsPanel, Panel): layout.operator("mesh.navmesh_reset") layout.operator("mesh.navmesh_clear") + if physics_type not in {'NO_COLLISION', 'OCCLUDE'}: + layout.separator() + split = layout.split() + + col = split.column() + col.prop(game, "collision_group") + col = split.column() + col.prop(game, "collision_mask") class PHYSICS_PT_game_collision_bounds(PhysicsButtonsPanel, Panel): bl_label = "Collision Bounds" diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c index 431f2c04f7d..a7b53370d1d 100644 --- a/source/blender/blenkernel/intern/object.c +++ b/source/blender/blenkernel/intern/object.c @@ -854,7 +854,9 @@ Object *BKE_object_add_only_object(int type, const char *name) ob->step_height = 0.15f; ob->jump_speed = 10.0f; ob->fall_speed = 55.0f; - + ob->col_group = 0x01; + ob->col_mask = 0xff; + /* NT fluid sim defaults */ ob->fluidsimSettings = NULL; diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index de9e5f5e08e..d694de61131 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -8247,6 +8247,16 @@ static void do_versions(FileData *fd, Library *lib, Main *main) } + { + Object *ob; + for (ob = main->object.first; ob; ob = ob->id.next) { + if (ob->col_group == 0) { + ob->col_group = 0x01; + ob->col_mask = 0xff; + } + } + } + /* WATCH IT!!!: pointers from libdata have not been converted yet here! */ /* WATCH IT 2!: Userdef struct init has to be in editors/interface/resources.c! */ diff --git a/source/blender/makesdna/DNA_object_types.h b/source/blender/makesdna/DNA_object_types.h index 2fc04c4a5db..932d209f2c7 100644 --- a/source/blender/makesdna/DNA_object_types.h +++ b/source/blender/makesdna/DNA_object_types.h @@ -243,6 +243,9 @@ typedef struct Object { short recalc; /* dependency flag */ float anisotropicFriction[3]; + /** Collision mask settings */ + unsigned short col_group, col_mask, col_pad[2]; + ListBase constraints; /* object constraints */ ListBase nlastrips DNA_DEPRECATED; // XXX deprecated... old animation system ListBase hooks DNA_DEPRECATED; // XXX deprecated... old animation system @@ -479,6 +482,9 @@ typedef struct DupliObject { /* controller state */ #define OB_MAX_STATES 30 +/* collision masks */ +#define OB_MAX_COL_MASKS 8 + /* ob->gameflag */ #define OB_DYNAMIC 1 #define OB_CHILD 2 diff --git a/source/blender/makesrna/intern/rna_object.c b/source/blender/makesrna/intern/rna_object.c index 0c1ec0b02f9..7b5feff614c 100644 --- a/source/blender/makesrna/intern/rna_object.c +++ b/source/blender/makesrna/intern/rna_object.c @@ -1111,6 +1111,63 @@ static void rna_GameObjectSettings_used_state_get(PointerRNA *ptr, int *values) } } +static void rna_GameObjectSettings_col_group_get(PointerRNA *ptr, int *values) +{ + Object *ob = (Object*)ptr->data; + int i; + + for (i = 0; i < OB_MAX_COL_MASKS; i++) + values[i] = (ob->col_group & (1<data; + int i, tot = 0; + + /* ensure we always have some group selected */ + for (i = 0; i < OB_MAX_COL_MASKS; i++) + if(values[i]) + tot++; + + if (tot==0) + return; + + for (i = 0; i < OB_MAX_COL_MASKS; i++) { + if (values[i]) ob->col_group |= (1<col_group &= ~(1<data; + int i; + + for (i = 0; i < OB_MAX_COL_MASKS; i++) + values[i] = (ob->col_mask & (1<data; + int i, tot = 0; + + /* ensure we always have some mask selected */ + for (i = 0; i < OB_MAX_COL_MASKS; i++) + if(values[i]) + tot++; + + if (tot==0) + return; + + for (i = 0; i < OB_MAX_COL_MASKS; i++) { + if (values[i]) ob->col_mask |= (1<col_mask &= ~(1<id.data; @@ -1456,6 +1513,8 @@ static void rna_def_object_game_settings(BlenderRNA *brna) StructRNA *srna; PropertyRNA *prop; + int default_col_mask[8] = {1,0,0,0, 0,0,0,0}; + static EnumPropertyItem body_type_items[] = { {OB_BODY_TYPE_NO_COLLISION, "NO_COLLISION", 0, "No Collision", "Disable collision for this object"}, {OB_BODY_TYPE_STATIC, "STATIC", 0, "Static", "Stationary object"}, @@ -1578,6 +1637,17 @@ static void rna_def_object_game_settings(BlenderRNA *brna) RNA_def_property_range(prop, 0.0, 1000.0); RNA_def_property_ui_text(prop, "Fall Speed Max", "Maximum speed at which the character will fall"); + prop = RNA_def_property(srna, "collision_group", PROP_BOOLEAN, PROP_LAYER_MEMBER); + RNA_def_property_boolean_sdna(prop, NULL, "col_group", 1); + RNA_def_property_array(prop, OB_MAX_COL_MASKS); + RNA_def_property_ui_text(prop, "Collision Group", "The collision group of the object"); + RNA_def_property_boolean_funcs(prop, "rna_GameObjectSettings_col_group_get", "rna_GameObjectSettings_col_group_set"); + + prop = RNA_def_property(srna, "collision_mask", PROP_BOOLEAN, PROP_LAYER_MEMBER); + RNA_def_property_boolean_sdna(prop, NULL, "col_mask", 1); + RNA_def_property_array(prop, OB_MAX_COL_MASKS); + RNA_def_property_ui_text(prop, "Collision Mask", "The groups this object can collide with"); + RNA_def_property_boolean_funcs(prop, "rna_GameObjectSettings_col_mask_get", "rna_GameObjectSettings_col_mask_set"); /* lock position */ prop = RNA_def_property(srna, "lock_location_x", PROP_BOOLEAN, PROP_NONE); diff --git a/source/gameengine/Converter/BL_BlenderDataConversion.cpp b/source/gameengine/Converter/BL_BlenderDataConversion.cpp index 982988cc088..58ae415e9d3 100644 --- a/source/gameengine/Converter/BL_BlenderDataConversion.cpp +++ b/source/gameengine/Converter/BL_BlenderDataConversion.cpp @@ -1587,8 +1587,16 @@ static void BL_CreatePhysicsObjectNew(KX_GameObject* gameobj, //bool bRigidBody = (userigidbody == 0); // object has physics representation? - if (!(blenderobject->gameflag & OB_COLLISION)) + if (!(blenderobject->gameflag & OB_COLLISION)) { + // Respond to all collisions so that Near sensors work on No Collision + // objects. + gameobj->SetUserCollisionGroup(0xff); + gameobj->SetUserCollisionMask(0xff); return; + } + + gameobj->SetUserCollisionGroup(blenderobject->col_group); + gameobj->SetUserCollisionMask(blenderobject->col_mask); // get Root Parent of blenderobject struct Object* parent= blenderobject->parent; diff --git a/source/gameengine/Ketsji/KX_GameObject.cpp b/source/gameengine/Ketsji/KX_GameObject.cpp index 48a45c2d1c2..a669f4346ea 100644 --- a/source/gameengine/Ketsji/KX_GameObject.cpp +++ b/source/gameengine/Ketsji/KX_GameObject.cpp @@ -531,6 +531,19 @@ void KX_GameObject::ActivateGraphicController(bool recurse) } } +void KX_GameObject::SetUserCollisionGroup(short group) +{ + m_userCollisionGroup = group; +} +void KX_GameObject::SetUserCollisionMask(short mask) +{ + m_userCollisionMask = mask; +} + +bool KX_GameObject::CheckCollision(KX_GameObject* other) +{ + return this->m_userCollisionGroup & other->m_userCollisionMask; +} CValue* KX_GameObject::GetReplica() { diff --git a/source/gameengine/Ketsji/KX_GameObject.h b/source/gameengine/Ketsji/KX_GameObject.h index 5ceef2c60c9..d5ae13eb702 100644 --- a/source/gameengine/Ketsji/KX_GameObject.h +++ b/source/gameengine/Ketsji/KX_GameObject.h @@ -98,6 +98,10 @@ protected: bool m_bIsNegativeScaling; MT_Vector4 m_objectColor; + // Bit fields for user control over physics collisions + short m_userCollisionGroup; + short m_userCollisionMask; + // visible = user setting // culled = while rendering, depending on camera bool m_bVisible; @@ -494,6 +498,13 @@ public: */ void ActivateGraphicController(bool recurse); + void SetUserCollisionGroup(short filter); + void SetUserCollisionMask(short mask); + /** + * Extra broadphase check for user controllable collisions + */ + bool CheckCollision(KX_GameObject *other); + /** * \section Coordinate system manipulation functions */ diff --git a/source/gameengine/Physics/Bullet/CcdPhysicsEnvironment.cpp b/source/gameengine/Physics/Bullet/CcdPhysicsEnvironment.cpp index e43ddb303e9..687fc116234 100644 --- a/source/gameengine/Physics/Bullet/CcdPhysicsEnvironment.cpp +++ b/source/gameengine/Physics/Bullet/CcdPhysicsEnvironment.cpp @@ -2208,10 +2208,29 @@ bool CcdOverlapFilterCallBack::needBroadphaseCollision(btBroadphaseProxy* proxy0 { btCollisionObject *colObj0, *colObj1; CcdPhysicsController *sensorCtrl, *objCtrl; + + KX_GameObject *kxObj0 = KX_GameObject::GetClientObject( + (KX_ClientObjectInfo*) + ((CcdPhysicsController*) + (((btCollisionObject*)proxy0->m_clientObject)->getUserPointer())) + ->getNewClientInfo()); + KX_GameObject *kxObj1 = KX_GameObject::GetClientObject( + (KX_ClientObjectInfo*) + ((CcdPhysicsController*) + (((btCollisionObject*)proxy1->m_clientObject)->getUserPointer())) + ->getNewClientInfo()); + + // First check the filters. Note that this is called during scene + // conversion, so we can't assume the KX_GameObject instances exist. This + // may make some objects erroneously collide on the first frame, but the + // alternative is to have them erroneously miss. bool collides; - // first check the filters collides = (proxy0->m_collisionFilterGroup & proxy1->m_collisionFilterMask) != 0; collides = collides && (proxy1->m_collisionFilterGroup & proxy0->m_collisionFilterMask); + if (kxObj0 && kxObj1) { + collides = collides && kxObj0->CheckCollision(kxObj1); + collides = collides && kxObj1->CheckCollision(kxObj0); + } if (!collides) return false;