BGE patch: support for partial hierarchy in dupligroup instantiation; removal of...
authorBenoit Bolsee <benoit.bolsee@online.be>
Fri, 18 Jul 2008 19:56:56 +0000 (19:56 +0000)
committerBenoit Bolsee <benoit.bolsee@online.be>
Fri, 18 Jul 2008 19:56:56 +0000 (19:56 +0000)
This situation corresponds to a group containing only a portion
of a parent hierarchy (the Apricot team needed that to avoid
logic duplication). The BGE will instantiate only the
children that are in the group so that it follows the 3D view
more closely.
As a result, the logic links to the objects in the portion of the
hierarchy that was not replicated will point to inactive objects
(if the groups are stored in inactive layers as they should be).
To keep the logic system consistent, these links are automatically
removed.
This last part of the patch is a general fix that could go in
2.47 but as this situation does not normally occurs in pre-2.47
games, it is not needed.

source/gameengine/Ketsji/KX_Scene.cpp
source/gameengine/Ketsji/KX_Scene.h
source/gameengine/SceneGraph/SG_IObject.cpp
source/gameengine/SceneGraph/SG_IObject.h
source/gameengine/SceneGraph/SG_Node.cpp
source/gameengine/SceneGraph/SG_Node.h

index 891cd9b3d859552c39df9ca43c0e54749a6bd1ff..200883a094c82ca64a71760d7e528ed1766f358c 100644 (file)
@@ -431,6 +431,11 @@ void KX_Scene::RemoveNodeDestructObject(class SG_IObject* node,class CValue* gam
 
 KX_GameObject* KX_Scene::AddNodeReplicaObject(class SG_IObject* node, class CValue* gameobj)
 {
+       // for group duplication, limit the duplication of the hierarchy to the
+       // objects that are part of the group. 
+       if (!IsObjectInGroup(gameobj))
+               return NULL;
+       
        KX_GameObject* orgobj = (KX_GameObject*)gameobj;
        KX_GameObject* newobj = (KX_GameObject*)orgobj->GetReplica();
        m_map_gameobject_to_replica.insert(orgobj, newobj);
@@ -545,7 +550,9 @@ void KX_Scene::ReplicateLogic(KX_GameObject* newobj)
                        if (!newsensorobj)
                        {
                                // no, then the sensor points outside the hierachy, keep it the same
-                               m_logicmgr->RegisterToSensor(cont,oldsensor);
+                               if (m_objectlist->SearchValue(oldsensorobj))
+                                       // only replicate links that points to active objects
+                                       m_logicmgr->RegisterToSensor(cont,oldsensor);
                        }
                        else
                        {
@@ -583,7 +590,9 @@ void KX_Scene::ReplicateLogic(KX_GameObject* newobj)
                        if (!newactuatorobj)
                        {
                                // no, then the sensor points outside the hierachy, keep it the same
-                               m_logicmgr->RegisterToActuator(cont,oldactuator);
+                               if (m_objectlist->SearchValue(oldactuatorobj))
+                                       // only replicate links that points to active objects
+                                       m_logicmgr->RegisterToActuator(cont,oldactuator);
                        }
                        else
                        {
@@ -615,6 +624,7 @@ void KX_Scene::DupliGroupRecurse(CValue* obj, int level)
 {
        KX_GameObject* groupobj = (KX_GameObject*) obj;
        KX_GameObject* replica;
+       KX_GameObject* gameobj;
        Object* blgroupobj = groupobj->GetBlenderObject();
        Group* group;
        GroupObject *go;
@@ -628,6 +638,10 @@ void KX_Scene::DupliGroupRecurse(CValue* obj, int level)
        m_logicHierarchicalGameObjects.clear();
        m_map_gameobject_to_replica.clear();
        m_ueberExecutionPriority++;
+       // for groups will do something special: 
+       // we will force the creation of objects to those in the group only
+       // Again, this is match what Blender is doing (it doesn't care of parent relationship)
+       m_groupGameObjects.clear();
 
        group = blgroupobj->dup_group;
        for(go=(GroupObject*)group->gobject.first; go; go=(GroupObject*)go->next) 
@@ -636,13 +650,25 @@ void KX_Scene::DupliGroupRecurse(CValue* obj, int level)
                if (blgroupobj == blenderobj)
                        // this check is also in group_duplilist()
                        continue;
-               KX_GameObject* gameobj = m_sceneConverter->FindGameObject(blenderobj);
+               gameobj = m_sceneConverter->FindGameObject(blenderobj);
                if (gameobj == NULL) 
                {
                        // this object has not been converted!!!
                        // Should not happen as dupli group are created automatically 
                        continue;
                }
+               if (blenderobj->lay & group->layer==0)
+               {
+                       // object is not visible in the 3D view, will not be instantiated
+                       continue;
+               }
+               m_groupGameObjects.insert(gameobj);
+       }
+
+       set<CValue*>::iterator oit;
+       for (oit=m_groupGameObjects.begin(); oit != m_groupGameObjects.end(); oit++)
+       {
+               gameobj = (KX_GameObject*)(*oit);
                if (gameobj->GetParent() != NULL)
                {
                        // this object is not a top parent. Either it is the child of another
@@ -651,11 +677,6 @@ void KX_Scene::DupliGroupRecurse(CValue* obj, int level)
                        // is inconsistent, skip it anyway
                        continue;
                }
-               if (blenderobj->lay & group->layer==0)
-               {
-                       // object is not visible in the 3D view, will not be instantiated
-                       continue;
-               }
                replica = (KX_GameObject*) AddNodeReplicaObject(NULL,gameobj);
                // add to 'rootparent' list (this is the list of top hierarchy objects, updated each frame)
                m_parentlist->Add(replica->AddRef());
@@ -668,7 +689,8 @@ void KX_Scene::DupliGroupRecurse(CValue* obj, int level)
                {
                        SG_Node* orgnode = (*childit);
                        SG_Node* childreplicanode = orgnode->GetSGReplica();
-                       replica->GetSGNode()->AddChild(childreplicanode);
+                       if (childreplicanode)
+                               replica->GetSGNode()->AddChild(childreplicanode);
                }
                // don't replicate logic now: we assume that the objects in the group can have
                // logic relationship, even outside parent relationship
@@ -745,6 +767,7 @@ SCA_IObject* KX_Scene::AddReplicaObject(class CValue* originalobject,
 
        m_logicHierarchicalGameObjects.clear();
        m_map_gameobject_to_replica.clear();
+       m_groupGameObjects.clear();
 
        // todo: place a timebomb in the object, for temporarily objects :)
        // lifespan of zero means 'this object lives forever'
@@ -778,7 +801,8 @@ SCA_IObject* KX_Scene::AddReplicaObject(class CValue* originalobject,
        {
                SG_Node* orgnode = (*childit);
                SG_Node* childreplicanode = orgnode->GetSGReplica();
-               replica->GetSGNode()->AddChild(childreplicanode);
+               if (childreplicanode)
+                       replica->GetSGNode()->AddChild(childreplicanode);
        }
 
        //      relink any pointers as necessary, sort of a temporary solution
index 374545f9a90079112206f1270e4f39f3863a194c..80a2abe287ad2e464ecf4a4d68fab9537273aeb2 100644 (file)
@@ -216,6 +216,16 @@ protected:
         */
        std::vector<KX_GameObject*>     m_logicHierarchicalGameObjects;
        
+       /**
+        * This temporary variable will contain the list of 
+        * object that can be added during group instantiation.
+        * objects outside this list will not be added (can 
+        * happen with children that are outside the group).
+        * Used in AddReplicaObject. If the list is empty, it
+        * means don't care.
+        */
+       std::set<CValue*>       m_groupGameObjects;
+       
        /** 
         * Pointer to system variable passed in in constructor
         * only used in constructor so we do not need to keep it
@@ -292,6 +302,11 @@ public:
         */
        void UpdateParents(double curtime);
        void DupliGroupRecurse(CValue* gameobj, int level);
+       bool IsObjectInGroup(CValue* gameobj)
+       { 
+               return (m_groupGameObjects.empty() || 
+                               m_groupGameObjects.find(gameobj) != m_groupGameObjects.end());
+       }
        SCA_IObject* AddReplicaObject(CValue* gameobj,
                                                                  CValue* locationobj,
                                                                  int lifespan=0);
index c347bbc6d9a5c4b652253995fcb1f9ffe45ec4bd..d0bdac5c8f0215b1a09e28c5ef0713bc8617cad3 100644 (file)
@@ -104,7 +104,7 @@ SetSGClientObject(
 }
 
 
-       void
+       bool
 SG_IObject::
 ActivateReplicationCallback(
        SG_IObject *replica
@@ -112,8 +112,10 @@ ActivateReplicationCallback(
        if (m_callbacks.m_replicafunc)
        {
                // Call client provided replication func
-               m_callbacks.m_replicafunc(replica,m_SGclientObject,m_SGclientInfo);
+               if (m_callbacks.m_replicafunc(replica,m_SGclientObject,m_SGclientInfo) == NULL)
+                       return false;
        }
+       return true;
 };     
 
        void
index 438ab48c5565b9bcf0c24156c028bdb5241b1b41..7f6bdfbbb1cb29b22d12700b51990eeb1bfe4dac 100644 (file)
@@ -202,7 +202,7 @@ public:
 
 protected :
 
-               void
+               bool
        ActivateReplicationCallback(
                SG_IObject *replica
        );
index 4e90d7c46536c5724f5f27118a9ff2142f367505..8de7ac834779605b5b7a13fa34d10ff9233a6aeb 100644 (file)
@@ -68,7 +68,7 @@ SG_Node* SG_Node::GetSGReplica()
        SG_Node* replica = new SG_Node(*this);
        if (replica == NULL) return NULL;
 
-       ProcessSGReplica(replica);
+       ProcessSGReplica(&replica);
        
        return replica;
 }
@@ -76,25 +76,42 @@ SG_Node* SG_Node::GetSGReplica()
        void 
 SG_Node::
 ProcessSGReplica(
-       SG_Node* replica
+       SG_Node** replica
 ){
        // Apply the replication call back function.
-       ActivateReplicationCallback(replica);
+       if (!ActivateReplicationCallback(*replica)) 
+       {
+               delete (*replica);
+               *replica = NULL;
+               return;
+       }
 
        // clear the replica node of it's parent.
-       static_cast<SG_Node*>(replica)->m_SGparent = NULL;
+       static_cast<SG_Node*>(*replica)->m_SGparent = NULL;
 
        if (m_children.begin() != m_children.end())
        {
                // if this node has children, the replica has too, so clear and clone children
-               replica->ClearSGChildren();
+               (*replica)->ClearSGChildren();
        
                NodeList::iterator childit;
                for (childit = m_children.begin();childit!=m_children.end();++childit)
                {
-                       replica->AddChild((*childit)->GetSGReplica());
+                       SG_Node* childnode = (*childit)->GetSGReplica();
+                       if (childnode)
+                               (*replica)->AddChild(childnode);
                }
        }
+       // Nodes without children and without client object are
+       // not worth to keep, they will just take up CPU
+       // This can happen in partial replication of hierarchy
+       // during group duplication.
+       if ((*replica)->m_children.empty() && 
+               (*replica)->GetSGClientObject() == NULL)
+       {
+               delete (*replica);
+               *replica = NULL;
+       }
 }
 
 
index f86e3046d93ae8e33cc91a083692b1d8241cb5f3..ffaaad861e2db097ceb0cde59e178631a2f19da5 100644 (file)
@@ -205,7 +205,7 @@ private:
 
                void            
        ProcessSGReplica(
-               SG_Node* replica
+               SG_Node** replica
        );
 
        /**