soc-2008-mxcurioni: merged changes to revision 15705
[blender.git] / source / gameengine / Ketsji / KX_Scene.cpp
index 065800379d860a743aa647d548f27a663e806d01..c374b9d6fd158ea2d5a7ac743d24f693c715c6ec 100644 (file)
@@ -66,6 +66,8 @@
 #include "SG_Controller.h"
 #include "SG_IObject.h"
 #include "SG_Tree.h"
+#include "DNA_group_types.h"
+#include "BKE_anim.h"
 
 #include "KX_SG_NodeRelationships.h"
 
@@ -429,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);
@@ -506,6 +513,11 @@ KX_GameObject* KX_Scene::AddNodeReplicaObject(class SG_IObject* node, class CVal
 // hierarchy that's because first ALL bricks must exist in the new
 // replica of the hierarchy in order to make cross-links work properly
 // !
+// It is VERY important that the order of sensors and actuators in
+// the replicated object is preserved: it is is used to reconnect the logic.
+// This method is more robust then using the bricks name in case of complex 
+// group replication. The replication of logic bricks is done in 
+// SCA_IObject::ReParentLogic(), make sure it preserves the order of the bricks.
 void KX_Scene::ReplicateLogic(KX_GameObject* newobj)
 {
        // also relink the controller to sensors/actuators
@@ -528,37 +540,38 @@ void KX_Scene::ReplicateLogic(KX_GameObject* newobj)
                for (vector<SCA_ISensor*>::iterator its = linkedsensors.begin();!(its==linkedsensors.end());its++)
                {
                        SCA_ISensor* oldsensor = (*its);
-                       STR_String name = oldsensor->GetName();
-                       //find this name in the list
-                       SCA_ISensor* newsensor = newobj->FindSensor(name);
+                       SCA_IObject* oldsensorobj = oldsensor->GetParent();
+                       SCA_IObject* newsensorobj = NULL;
                
-                       if (newsensor)
+                       // the original owner of the sensor has been replicated?
+                       void **h_obj = m_map_gameobject_to_replica[oldsensorobj];
+                       if (h_obj)
+                               newsensorobj = (SCA_IObject*)(*h_obj);
+                       if (!newsensorobj)
                        {
-                               // relink this newsensor to the controller
-                               m_logicmgr->RegisterToSensor(cont,newsensor);
+                               // no, then the sensor points outside the hierachy, keep it the same
+                               if (m_objectlist->SearchValue(oldsensorobj))
+                                       // only replicate links that points to active objects
+                                       m_logicmgr->RegisterToSensor(cont,oldsensor);
                        }
                        else
                        {
-                               // it can be linked somewhere in the hierarchy or...
-                               for (vector<KX_GameObject*>::iterator git = m_logicHierarchicalGameObjects.begin();
-                               !(git==m_logicHierarchicalGameObjects.end());++git)
-                               {
-                                       newsensor = (*git)->FindSensor(name);
-                                       if (newsensor)
-                                               break;
-                               } 
+                               // yes, then the new sensor has the same position
+                               SCA_SensorList& sensorlist = oldsensorobj->GetSensors();
+                               SCA_SensorList::iterator sit;
+                               SCA_ISensor* newsensor = NULL;
+                               int sensorpos;
 
-                               if (newsensor)
-                               {
-                                       // relink this newsensor to the controller somewhere else within this
-                                       // hierarchy
-                                       m_logicmgr->RegisterToSensor(cont,newsensor);
-                               }
-                               else
+                               for (sensorpos=0, sit=sensorlist.begin(); sit!=sensorlist.end(); sit++, sensorpos++)
                                {
-                                       // must be an external sensor, so...
-                                       m_logicmgr->RegisterToSensor(cont,oldsensor);
+                                       if ((*sit) == oldsensor) 
+                                       {
+                                               newsensor = newsensorobj->GetSensors().at(sensorpos);
+                                               break;
+                                       }
                                }
+                               assert(newsensor != NULL);
+                               m_logicmgr->RegisterToSensor(cont,newsensor);
                        }
                }
                
@@ -566,38 +579,40 @@ void KX_Scene::ReplicateLogic(KX_GameObject* newobj)
                for (vector<SCA_IActuator*>::iterator ita = linkedactuators.begin();!(ita==linkedactuators.end());ita++)
                {
                        SCA_IActuator* oldactuator = (*ita);
-                       STR_String name = oldactuator->GetName();
-                       //find this name in the list
-                       SCA_IActuator* newactuator = newobj->FindActuator(name);
-                       if (newactuator)
+                       SCA_IObject* oldactuatorobj = oldactuator->GetParent();
+                       SCA_IObject* newactuatorobj = NULL;
+
+                       // the original owner of the sensor has been replicated?
+                       void **h_obj = m_map_gameobject_to_replica[oldactuatorobj];
+                       if (h_obj)
+                               newactuatorobj = (SCA_IObject*)(*h_obj);
+
+                       if (!newactuatorobj)
                        {
-                               // relink this newsensor to the controller
-                               m_logicmgr->RegisterToActuator(cont,newactuator);
-                               newactuator->SetUeberExecutePriority(m_ueberExecutionPriority);
+                               // no, then the sensor points outside the hierachy, keep it the same
+                               if (m_objectlist->SearchValue(oldactuatorobj))
+                                       // only replicate links that points to active objects
+                                       m_logicmgr->RegisterToActuator(cont,oldactuator);
                        }
                        else
                        {
-                               // it can be linked somewhere in the hierarchy or...
-                               for (vector<KX_GameObject*>::iterator git = m_logicHierarchicalGameObjects.begin();
-                               !(git==m_logicHierarchicalGameObjects.end());++git)
-                               {
-                                       newactuator= (*git)->FindActuator(name);
-                                       if (newactuator)
-                                               break;
-                               } 
+                               // yes, then the new sensor has the same position
+                               SCA_ActuatorList& actuatorlist = oldactuatorobj->GetActuators();
+                               SCA_ActuatorList::iterator ait;
+                               SCA_IActuator* newactuator = NULL;
+                               int actuatorpos;
 
-                               if (newactuator)
+                               for (actuatorpos=0, ait=actuatorlist.begin(); ait!=actuatorlist.end(); ait++, actuatorpos++)
                                {
-                                       // relink this actuator to the controller somewhere else within this
-                                       // hierarchy
-                                       m_logicmgr->RegisterToActuator(cont,newactuator);
-                                       newactuator->SetUeberExecutePriority(m_ueberExecutionPriority);
-                               }
-                               else
-                               {
-                                       // must be an external actuator, so...
-                                       m_logicmgr->RegisterToActuator(cont,oldactuator);
+                                       if ((*ait) == oldactuator) 
+                                       {
+                                               newactuator = newactuatorobj->GetActuators().at(actuatorpos);
+                                               break;
+                                       }
                                }
+                               assert(newactuator != NULL);
+                               m_logicmgr->RegisterToActuator(cont,newactuator);
+                               newactuator->SetUeberExecutePriority(m_ueberExecutionPriority);
                        }
                }
        }
@@ -605,6 +620,150 @@ void KX_Scene::ReplicateLogic(KX_GameObject* newobj)
        newobj->ResetState();
 }
 
+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;
+       vector<KX_GameObject*> duplilist;
+
+       if (!groupobj->IsDupliGroup() ||
+               level>MAX_DUPLI_RECUR)
+               return;
+
+       // we will add one group at a time
+       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) 
+       {
+               Object* blenderobj = go->ob;
+               if (blgroupobj == blenderobj)
+                       // this check is also in group_duplilist()
+                       continue;
+               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
+                       // object in the group and it will be added automatically when the parent
+                       // is added. Or it is the child of an object outside the group and the group
+                       // is inconsistent, skip it anyway
+                       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());
+
+               // recurse replication into children nodes
+               NodeList& children = gameobj->GetSGNode()->GetSGChildren();
+
+               replica->GetSGNode()->ClearSGChildren();
+               for (NodeList::iterator childit = children.begin();!(childit==children.end());++childit)
+               {
+                       SG_Node* orgnode = (*childit);
+                       SG_Node* childreplicanode = orgnode->GetSGReplica();
+                       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
+               // In order to match 3D view, the position of groupobj is used as a 
+               // transformation matrix instead of the new position. This means that 
+               // the group reference point is 0,0,0
+
+               // get the rootnode's scale
+               MT_Vector3 newscale = groupobj->NodeGetWorldScaling();
+               // set the replica's relative scale with the rootnode's scale
+               replica->NodeSetRelativeScale(newscale);
+
+               MT_Matrix3x3 newori = groupobj->NodeGetWorldOrientation() * gameobj->NodeGetWorldOrientation();
+               replica->NodeSetLocalOrientation(newori);
+
+               MT_Point3 newpos = groupobj->NodeGetWorldPosition() + 
+                       newscale*(groupobj->NodeGetWorldOrientation() * gameobj->NodeGetWorldPosition());
+               replica->NodeSetLocalPosition(newpos);
+
+               if (replica->GetPhysicsController())
+               {
+                       // not required, already done in NodeSetLocalOrientation..
+                       //replica->GetPhysicsController()->setPosition(newpos);
+                       //replica->GetPhysicsController()->setOrientation(newori.getRotation());
+                       // Scaling has been set relatively hereabove, this does not 
+                       // set the scaling of the controller. I don't know why it's just the
+                       // relative scale and not the full scale that has to be put here...
+                       replica->GetPhysicsController()->setScaling(newscale);
+               }
+
+               replica->GetSGNode()->UpdateWorldData(0);
+               replica->GetSGNode()->SetBBox(gameobj->GetSGNode()->BBox());
+               replica->GetSGNode()->SetRadius(gameobj->GetSGNode()->Radius());
+               // done with replica
+               replica->Release();
+       }
+
+       // the logic must be replicated first because we need
+       // the new logic bricks before relinking
+       vector<KX_GameObject*>::iterator git;
+       for (git = m_logicHierarchicalGameObjects.begin();!(git==m_logicHierarchicalGameObjects.end());++git)
+       {
+               (*git)->ReParentLogic();
+       }
+       
+       //      relink any pointers as necessary, sort of a temporary solution
+       for (git = m_logicHierarchicalGameObjects.begin();!(git==m_logicHierarchicalGameObjects.end());++git)
+       {
+               // this will also relink the actuator to objects within the hierarchy
+               (*git)->Relink(&m_map_gameobject_to_replica);
+               // add the object in the layer of the parent
+               (*git)->SetLayer(groupobj->GetLayer());
+       }
+
+       // replicate crosslinks etc. between logic bricks
+       for (git = m_logicHierarchicalGameObjects.begin();!(git==m_logicHierarchicalGameObjects.end());++git)
+       {
+               ReplicateLogic((*git));
+       }
+       
+       // now look if object in the hierarchy have dupli group and recurse
+       for (git = m_logicHierarchicalGameObjects.begin();!(git==m_logicHierarchicalGameObjects.end());++git)
+       {
+               if ((*git) != groupobj && (*git)->IsDupliGroup())
+                       // can't instantiate group immediately as it destroys m_logicHierarchicalGameObjects
+                       duplilist.push_back((*git));
+       }
+
+       for (git = duplilist.begin(); !(git == duplilist.end()); ++git)
+       {
+               DupliGroupRecurse((*git), level+1);
+       }
+}
 
 
 SCA_IObject* KX_Scene::AddReplicaObject(class CValue* originalobject,
@@ -614,6 +773,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'
@@ -647,24 +807,26 @@ 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
+       // now replicate logic
        vector<KX_GameObject*>::iterator git;
        for (git = m_logicHierarchicalGameObjects.begin();!(git==m_logicHierarchicalGameObjects.end());++git)
        {
+               (*git)->ReParentLogic();
+       }
+       
+       //      relink any pointers as necessary, sort of a temporary solution
+       for (git = m_logicHierarchicalGameObjects.begin();!(git==m_logicHierarchicalGameObjects.end());++git)
+       {
+               // this will also relink the actuators in the hierarchy
                (*git)->Relink(&m_map_gameobject_to_replica);
                // add the object in the layer of the parent
                (*git)->SetLayer(parentobj->GetLayer());
        }
 
-       // now replicate logic
-       for (git = m_logicHierarchicalGameObjects.begin();!(git==m_logicHierarchicalGameObjects.end());++git)
-       {
-               (*git)->ReParentLogic();
-       }
-       
        // replicate crosslinks etc. between logic bricks
        for (git = m_logicHierarchicalGameObjects.begin();!(git==m_logicHierarchicalGameObjects.end());++git)
        {
@@ -685,8 +847,9 @@ SCA_IObject* KX_Scene::AddReplicaObject(class CValue* originalobject,
 
        if (replica->GetPhysicsController())
        {
-               replica->GetPhysicsController()->setPosition(newpos);
-               replica->GetPhysicsController()->setOrientation(newori.getRotation());
+               // not needed, already done in NodeSetLocalPosition()
+               //replica->GetPhysicsController()->setPosition(newpos);
+               //replica->GetPhysicsController()->setOrientation(newori.getRotation());
                replica->GetPhysicsController()->setScaling(newscale);
        }