BGE: Adding support for Bullet's collision masks. Each object now has a collision...
authorMitchell Stokes <mogurijin@gmail.com>
Tue, 30 Oct 2012 15:44:16 +0000 (15:44 +0000)
committerMitchell Stokes <mogurijin@gmail.com>
Tue, 30 Oct 2012 15:44:16 +0000 (15:44 +0000)
The majority of this patch was provided by Kupoman with some edits from me and heavy testing by z0r.

release/scripts/startup/bl_ui/properties_game.py
source/blender/blenkernel/intern/object.c
source/blender/blenloader/intern/readfile.c
source/blender/makesdna/DNA_object_types.h
source/blender/makesrna/intern/rna_object.c
source/gameengine/Converter/BL_BlenderDataConversion.cpp
source/gameengine/Ketsji/KX_GameObject.cpp
source/gameengine/Ketsji/KX_GameObject.h
source/gameengine/Physics/Bullet/CcdPhysicsEnvironment.cpp

index 5ff49a7d369b3d4b74a68dfe709b18f90aefdc3b..bf0c9eb762a4a61be7e6c16ed250049fea65dd7e 100644 (file)
@@ -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"
index 431f2c04f7ddd150af47c8e7d021badc5b0af548..a7b53370d1d51d6375fc8636705f5d26ce9e8f7f 100644 (file)
@@ -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;
 
index de9e5f5e08ea291928cf55c98b1675100131603f..d694de611311062d09b2251d8b7983acf032a29c 100644 (file)
@@ -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! */
 
index 2fc04c4a5db72fc4700f9f5b698264e885523b17..932d209f2c722355841660753526d845bc69f0ae 100644 (file)
@@ -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
index 0c1ec0b02f9a07c6d6f6e82782f7ddd0b0220664..7b5feff614c15e16f3e90d602a74bf43a8883c10 100644 (file)
@@ -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<<i));
+}
+
+static void rna_GameObjectSettings_col_group_set(PointerRNA *ptr, const int *values)
+{
+       Object *ob = (Object*)ptr->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<<i);
+               else ob->col_group &= ~(1<<i);
+       }
+}
+
+static void rna_GameObjectSettings_col_mask_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_mask & (1<<i));
+}
+
+static void rna_GameObjectSettings_col_mask_set(PointerRNA *ptr, const int *values)
+{
+       Object *ob = (Object*)ptr->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<<i);
+               else ob->col_mask &= ~(1<<i);
+       }
+}
+
+
 static void rna_Object_active_shape_key_index_range(PointerRNA *ptr, int *min, int *max, int *softmin, int *softmax)
 {
        Object *ob = (Object *)ptr->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);
index 982988cc088d58b473a28a79d32534958d63d296..58ae415e9d3e2c412921df20198be0d092058aa1 100644 (file)
@@ -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;
index 48a45c2d1c2731e80e03f31993f63f1d80398e2a..a669f4346ea5803259a26288d13b89e7982f4fef 100644 (file)
@@ -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()
 {
index 5ceef2c60c97e0c247bf0b80c57f6a69a9c99b73..d5ae13eb702fadf09a1aa8845f10e9f0fd280523 100644 (file)
@@ -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
         */
index e43ddb303e972345a93822e97964a1dd7a5248d4..687fc116234f4841d243715eada917994e220064 100644 (file)
@@ -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;