/* * ***** BEGIN GPL LICENSE BLOCK ***** * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. * All rights reserved. * * The Original Code is: all of this file. * * Contributor(s): none yet. * * ***** END GPL LICENSE BLOCK ***** * Ketsji scene. Holds references to all scene data. */ /** \file gameengine/Ketsji/KX_Scene.cpp * \ingroup ketsji */ #ifdef _MSC_VER # pragma warning (disable:4786) #endif #include "KX_Scene.h" #include "KX_PythonInit.h" #include "MT_assert.h" #include "KX_KetsjiEngine.h" #include "KX_BlenderMaterial.h" #include "KX_FontObject.h" #include "RAS_IPolygonMaterial.h" #include "ListValue.h" #include "SCA_LogicManager.h" #include "SCA_TimeEventManager.h" //#include "SCA_AlwaysEventManager.h" //#include "SCA_RandomEventManager.h" //#include "KX_RayEventManager.h" #include "SCA_2DFilterActuator.h" #include "SCA_PythonController.h" #include "KX_TouchEventManager.h" #include "SCA_KeyboardManager.h" #include "SCA_MouseManager.h" //#include "SCA_PropertyEventManager.h" #include "SCA_ActuatorEventManager.h" #include "SCA_BasicEventManager.h" #include "KX_Camera.h" #include "SCA_JoystickManager.h" #include "KX_PyMath.h" #include "RAS_MeshObject.h" #include "SCA_IScene.h" #include "RAS_IRasterizer.h" #include "RAS_ICanvas.h" #include "RAS_BucketManager.h" #include "FloatValue.h" #include "SCA_IController.h" #include "SCA_IActuator.h" #include "SG_Node.h" #include "BL_System.h" #include "SG_Controller.h" #include "SG_IObject.h" #include "SG_Tree.h" #include "DNA_group_types.h" #include "DNA_scene_types.h" #include "DNA_property_types.h" #include "KX_SG_NodeRelationships.h" #include "KX_NetworkEventManager.h" #include "NG_NetworkScene.h" #include "PHY_IPhysicsEnvironment.h" #include "PHY_IGraphicController.h" #include "PHY_IPhysicsController.h" #include "KX_BlenderSceneConverter.h" #include "KX_MotionState.h" #include "BL_ModifierDeformer.h" #include "BL_ShapeDeformer.h" #include "BL_DeformableGameObject.h" #include "KX_ObstacleSimulation.h" #ifdef WITH_BULLET #include "KX_SoftBodyDeformer.h" #endif #include "KX_Light.h" #include #include "BLI_task.h" static void *KX_SceneReplicationFunc(SG_IObject* node,void* gameobj,void* scene) { KX_GameObject* replica = ((KX_Scene*)scene)->AddNodeReplicaObject(node,(KX_GameObject*)gameobj); if (replica) replica->Release(); return (void*)replica; } static void *KX_SceneDestructionFunc(SG_IObject* node,void* gameobj,void* scene) { ((KX_Scene*)scene)->RemoveNodeDestructObject(node,(KX_GameObject*)gameobj); return NULL; }; bool KX_Scene::KX_ScenegraphUpdateFunc(SG_IObject* node,void* gameobj,void* scene) { return ((SG_Node*)node)->Schedule(((KX_Scene*)scene)->m_sghead); } bool KX_Scene::KX_ScenegraphRescheduleFunc(SG_IObject* node,void* gameobj,void* scene) { return ((SG_Node*)node)->Reschedule(((KX_Scene*)scene)->m_sghead); } SG_Callbacks KX_Scene::m_callbacks = SG_Callbacks( KX_SceneReplicationFunc, KX_SceneDestructionFunc, KX_GameObject::UpdateTransformFunc, KX_Scene::KX_ScenegraphUpdateFunc, KX_Scene::KX_ScenegraphRescheduleFunc); // temporarily var until there is a button in the userinterface // (defined in KX_PythonInit.cpp) extern bool gUseVisibilityTemp; KX_Scene::KX_Scene(class SCA_IInputDevice* keyboarddevice, class SCA_IInputDevice* mousedevice, class NG_NetworkDeviceInterface *ndi, const STR_String& sceneName, Scene *scene, class RAS_ICanvas* canvas): PyObjectPlus(), m_keyboardmgr(NULL), m_mousemgr(NULL), m_sceneConverter(NULL), m_physicsEnvironment(0), m_sceneName(sceneName), m_networkDeviceInterface(ndi), m_active_camera(NULL), m_ueberExecutionPriority(0), m_blenderScene(scene) { m_suspendedtime = 0.0; m_suspendeddelta = 0.0; m_dbvt_culling = false; m_dbvt_occlusion_res = 0; m_activity_culling = false; m_suspend = false; m_isclearingZbuffer = true; m_tempObjectList = new CListValue(); m_objectlist = new CListValue(); m_parentlist = new CListValue(); m_lightlist= new CListValue(); m_inactivelist = new CListValue(); m_euthanasyobjects = new CListValue(); m_animatedlist = new CListValue(); m_logicmgr = new SCA_LogicManager(); m_timemgr = new SCA_TimeEventManager(m_logicmgr); m_keyboardmgr = new SCA_KeyboardManager(m_logicmgr,keyboarddevice); m_mousemgr = new SCA_MouseManager(m_logicmgr,mousedevice, canvas); //SCA_AlwaysEventManager* alwaysmgr = new SCA_AlwaysEventManager(m_logicmgr); //SCA_PropertyEventManager* propmgr = new SCA_PropertyEventManager(m_logicmgr); SCA_ActuatorEventManager* actmgr = new SCA_ActuatorEventManager(m_logicmgr); //SCA_RandomEventManager* rndmgr = new SCA_RandomEventManager(m_logicmgr); SCA_BasicEventManager* basicmgr = new SCA_BasicEventManager(m_logicmgr); //KX_RayEventManager* raymgr = new KX_RayEventManager(m_logicmgr); KX_NetworkEventManager* netmgr = new KX_NetworkEventManager(m_logicmgr, ndi); //m_logicmgr->RegisterEventManager(alwaysmgr); //m_logicmgr->RegisterEventManager(propmgr); m_logicmgr->RegisterEventManager(actmgr); m_logicmgr->RegisterEventManager(m_keyboardmgr); m_logicmgr->RegisterEventManager(m_mousemgr); m_logicmgr->RegisterEventManager(m_timemgr); //m_logicmgr->RegisterEventManager(rndmgr); //m_logicmgr->RegisterEventManager(raymgr); m_logicmgr->RegisterEventManager(netmgr); m_logicmgr->RegisterEventManager(basicmgr); SYS_SystemHandle hSystem = SYS_GetSystem(); bool nojoystick= SYS_GetCommandLineInt(hSystem,"nojoystick",0); if (!nojoystick) { SCA_JoystickManager *joymgr = new SCA_JoystickManager(m_logicmgr); m_logicmgr->RegisterEventManager(joymgr); } MT_assert (m_networkDeviceInterface != NULL); m_networkScene = new NG_NetworkScene(m_networkDeviceInterface); m_rootnode = NULL; m_bucketmanager=new RAS_BucketManager(); bool showObstacleSimulation = scene->gm.flag & GAME_SHOW_OBSTACLE_SIMULATION; switch (scene->gm.obstacleSimulation) { case OBSTSIMULATION_TOI_rays: m_obstacleSimulation = new KX_ObstacleSimulationTOI_rays((MT_Scalar)scene->gm.levelHeight, showObstacleSimulation); break; case OBSTSIMULATION_TOI_cells: m_obstacleSimulation = new KX_ObstacleSimulationTOI_cells((MT_Scalar)scene->gm.levelHeight, showObstacleSimulation); break; default: m_obstacleSimulation = NULL; } #ifdef WITH_PYTHON m_attr_dict = NULL; m_draw_call_pre = NULL; m_draw_call_post = NULL; #endif } KX_Scene::~KX_Scene() { // The release of debug properties used to be in SCA_IScene::~SCA_IScene // It's still there but we remove all properties here otherwise some // reference might be hanging and causing late release of objects RemoveAllDebugProperties(); while (GetRootParentList()->GetCount() > 0) { KX_GameObject* parentobj = (KX_GameObject*) GetRootParentList()->GetValue(0); this->RemoveObject(parentobj); } if (m_obstacleSimulation) delete m_obstacleSimulation; if (m_objectlist) m_objectlist->Release(); if (m_parentlist) m_parentlist->Release(); if (m_inactivelist) m_inactivelist->Release(); if (m_lightlist) m_lightlist->Release(); if (m_tempObjectList) m_tempObjectList->Release(); if (m_euthanasyobjects) m_euthanasyobjects->Release(); if (m_animatedlist) m_animatedlist->Release(); if (m_logicmgr) delete m_logicmgr; if (m_physicsEnvironment) delete m_physicsEnvironment; if (m_networkScene) delete m_networkScene; if (m_bucketmanager) { delete m_bucketmanager; } #ifdef WITH_PYTHON if (m_attr_dict) { PyDict_Clear(m_attr_dict); /* Py_CLEAR: Py_DECREF's and NULL's */ Py_CLEAR(m_attr_dict); } /* these may be NULL but the macro checks */ Py_CLEAR(m_draw_call_pre); Py_CLEAR(m_draw_call_post); #endif } RAS_BucketManager* KX_Scene::GetBucketManager() { return m_bucketmanager; } CListValue* KX_Scene::GetTempObjectList() { return m_tempObjectList; } CListValue* KX_Scene::GetObjectList() { return m_objectlist; } CListValue* KX_Scene::GetRootParentList() { return m_parentlist; } CListValue* KX_Scene::GetInactiveList() { return m_inactivelist; } CListValue* KX_Scene::GetLightList() { return m_lightlist; } SCA_LogicManager* KX_Scene::GetLogicManager() { return m_logicmgr; } SCA_TimeEventManager* KX_Scene::GetTimeEventManager() { return m_timemgr; } list* KX_Scene::GetCameras() { return &m_cameras; } void KX_Scene::SetFramingType(RAS_FrameSettings & frame_settings) { m_frame_settings = frame_settings; }; /** * Return a const reference to the framing * type set by the above call. * The contents are not guaranteed to be sensible * if you don't call the above function. */ const RAS_FrameSettings& KX_Scene::GetFramingType() const { return m_frame_settings; }; /** * Store the current scene's viewport on the * game engine canvas. */ void KX_Scene::SetSceneViewport(const RAS_Rect &viewport) { m_viewport = viewport; } const RAS_Rect& KX_Scene::GetSceneViewport() const { return m_viewport; } void KX_Scene::SetWorldInfo(class KX_WorldInfo* worldinfo) { m_worldinfo = worldinfo; } class KX_WorldInfo* KX_Scene::GetWorldInfo() { return m_worldinfo; } const STR_String& KX_Scene::GetName() { return m_sceneName; } void KX_Scene::Suspend() { m_suspend = true; } void KX_Scene::Resume() { m_suspend = false; } void KX_Scene::SetActivityCulling(bool b) { m_activity_culling = b; } bool KX_Scene::IsSuspended() { return m_suspend; } bool KX_Scene::IsClearingZBuffer() { return m_isclearingZbuffer; } void KX_Scene::EnableZBufferClearing(bool isclearingZbuffer) { m_isclearingZbuffer = isclearingZbuffer; } void KX_Scene::AddObjectDebugProperties(class KX_GameObject* gameobj) { Object* blenderobject = gameobj->GetBlenderObject(); bProperty* prop = (bProperty*)blenderobject->prop.first; while (prop) { if (prop->flag & PROP_DEBUG) AddDebugProperty(gameobj,STR_String(prop->name)); prop = prop->next; } if (blenderobject->scaflag & OB_DEBUGSTATE) AddDebugProperty(gameobj,STR_String("__state__")); } void KX_Scene::RemoveNodeDestructObject(class SG_IObject* node,class CValue* gameobj) { KX_GameObject* orgobj = (KX_GameObject*)gameobj; if (NewRemoveObject(orgobj) != 0) { // object is not yet deleted because a reference is hanging somewhere. // This should not happen anymore since we use proxy object for Python // confident enough to put an assert? //assert(false); printf("Zombie object! name=%s\n", orgobj->GetName().ReadPtr()); orgobj->SetSGNode(NULL); PHY_IGraphicController* ctrl = orgobj->GetGraphicController(); if (ctrl) { // a graphic controller is set, we must delete it as the node will be deleted delete ctrl; orgobj->SetGraphicController(NULL); } } if (node) delete node; } 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); // also register 'timers' (time properties) of the replica int numprops = newobj->GetPropertyCount(); for (int i = 0; i < numprops; i++) { CValue* prop = newobj->GetProperty(i); if (prop->GetProperty("timer")) this->m_timemgr->AddTimeProperty(prop); } if (node) { newobj->SetSGNode((SG_Node*)node); } else { m_rootnode = new SG_Node(newobj,this,KX_Scene::m_callbacks); // this fixes part of the scaling-added object bug SG_Node* orgnode = orgobj->GetSGNode(); m_rootnode->SetLocalScale(orgnode->GetLocalScale()); m_rootnode->SetLocalPosition(orgnode->GetLocalPosition()); m_rootnode->SetLocalOrientation(orgnode->GetLocalOrientation()); // define the relationship between this node and it's parent. KX_NormalParentRelation * parent_relation = KX_NormalParentRelation::New(); m_rootnode->SetParentRelation(parent_relation); newobj->SetSGNode(m_rootnode); } SG_IObject* replicanode = newobj->GetSGNode(); // SG_Node* rootnode = (replicanode == m_rootnode ? NULL : m_rootnode); replicanode->SetSGClientObject(newobj); // this is the list of object that are send to the graphics pipeline m_objectlist->Add(newobj->AddRef()); if (newobj->GetGameObjectType()==SCA_IObject::OBJ_LIGHT) m_lightlist->Add(newobj->AddRef()); newobj->AddMeshUser(); // logic cannot be replicated, until the whole hierarchy is replicated. m_logicHierarchicalGameObjects.push_back(newobj); //replicate controllers of this node SGControllerList scenegraphcontrollers = orgobj->GetSGNode()->GetSGControllerList(); replicanode->RemoveAllControllers(); SGControllerList::iterator cit; //int numcont = scenegraphcontrollers.size(); for (cit = scenegraphcontrollers.begin();!(cit==scenegraphcontrollers.end());++cit) { // controller replication is quite complicated // only replicate ipo controller for now SG_Controller* replicacontroller = (*cit)->GetReplica((SG_Node*) replicanode); if (replicacontroller) { replicacontroller->SetObject(replicanode); replicanode->AddSGController(replicacontroller); } } // replicate graphic controller if (orgobj->GetGraphicController()) { PHY_IMotionState* motionstate = new KX_MotionState(newobj->GetSGNode()); PHY_IGraphicController* newctrl = orgobj->GetGraphicController()->GetReplica(motionstate); newctrl->SetNewClientInfo(newobj->getClientInfo()); newobj->SetGraphicController(newctrl); } // replicate physics controller if (orgobj->GetPhysicsController()) { PHY_IMotionState* motionstate = new KX_MotionState(newobj->GetSGNode()); PHY_IPhysicsController* newctrl = orgobj->GetPhysicsController()->GetReplica(); KX_GameObject *parent = newobj->GetParent(); PHY_IPhysicsController* parentctrl = (parent) ? parent->GetPhysicsController() : NULL; newctrl->SetNewClientInfo(newobj->getClientInfo()); newobj->SetPhysicsController(newctrl, newobj->IsDynamic()); newctrl->PostProcessReplica(motionstate, parentctrl); } return newobj; } // before calling this method KX_Scene::ReplicateLogic(), make sure to // have called 'GameObject::ReParentLogic' for each object this // 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 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) { /* add properties to debug list, for added objects and DupliGroups */ if (KX_GetActiveEngine()->GetAutoAddDebugProperties()) { AddObjectDebugProperties(newobj); } // also relink the controller to sensors/actuators SCA_ControllerList& controllers = newobj->GetControllers(); //SCA_SensorList& sensors = newobj->GetSensors(); //SCA_ActuatorList& actuators = newobj->GetActuators(); for (SCA_ControllerList::iterator itc = controllers.begin(); !(itc==controllers.end());itc++) { SCA_IController* cont = (*itc); cont->SetUeberExecutePriority(m_ueberExecutionPriority); vector linkedsensors = cont->GetLinkedSensors(); vector linkedactuators = cont->GetLinkedActuators(); // disconnect the sensors and actuators // do it directly on the list at this controller is not connected to anything at this stage cont->GetLinkedSensors().clear(); cont->GetLinkedActuators().clear(); // now relink each sensor for (vector::iterator its = linkedsensors.begin();!(its==linkedsensors.end());its++) { SCA_ISensor* oldsensor = (*its); SCA_IObject* oldsensorobj = oldsensor->GetParent(); SCA_IObject* newsensorobj = NULL; // 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) { // no, then the sensor points outside the hierarchy, keep it the same if (m_objectlist->SearchValue(oldsensorobj)) // only replicate links that points to active objects m_logicmgr->RegisterToSensor(cont,oldsensor); } else { // yes, then the new sensor has the same position SCA_SensorList& sensorlist = oldsensorobj->GetSensors(); SCA_SensorList::iterator sit; SCA_ISensor* newsensor = NULL; int sensorpos; for (sensorpos=0, sit=sensorlist.begin(); sit!=sensorlist.end(); sit++, sensorpos++) { if ((*sit) == oldsensor) { newsensor = newsensorobj->GetSensors().at(sensorpos); break; } } assert(newsensor != NULL); m_logicmgr->RegisterToSensor(cont,newsensor); } } // now relink each actuator for (vector::iterator ita = linkedactuators.begin();!(ita==linkedactuators.end());ita++) { SCA_IActuator* oldactuator = (*ita); 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) { // no, then the sensor points outside the hierarchy, keep it the same if (m_objectlist->SearchValue(oldactuatorobj)) // only replicate links that points to active objects m_logicmgr->RegisterToActuator(cont,oldactuator); } else { // yes, then the new sensor has the same position SCA_ActuatorList& actuatorlist = oldactuatorobj->GetActuators(); SCA_ActuatorList::iterator ait; SCA_IActuator* newactuator = NULL; int actuatorpos; for (actuatorpos=0, ait=actuatorlist.begin(); ait!=actuatorlist.end(); ait++, actuatorpos++) { if ((*ait) == oldactuator) { newactuator = newactuatorobj->GetActuators().at(actuatorpos); break; } } assert(newactuator != NULL); m_logicmgr->RegisterToActuator(cont,newactuator); newactuator->SetUeberExecutePriority(m_ueberExecutionPriority); } } } // ready to set initial state 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 duplilist; if (!groupobj->GetSGNode() || !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 = (KX_GameObject*)m_logicmgr->FindGameObjByBlendObj(blenderobj); if (gameobj == NULL) { // this object has not been converted!!! // Should not happen as dupli group are created automatically continue; } gameobj->SetBlenderGroupObject(blgroupobj); if ((blenderobj->lay & group->layer)==0) { // object is not visible in the 3D view, will not be instantiated continue; } m_groupGameObjects.insert(gameobj); } set::iterator oit; for (oit=m_groupGameObjects.begin(); oit != m_groupGameObjects.end(); oit++) { gameobj = (KX_GameObject*)(*oit); KX_GameObject *parent = gameobj->GetParent(); if (parent != 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_Point3 offset(group->dupli_ofs); MT_Point3 newpos = groupobj->NodeGetWorldPosition() + newscale*(groupobj->NodeGetWorldOrientation() * (gameobj->NodeGetWorldPosition()-offset)); replica->NodeSetLocalPosition(newpos); // set the orientation after position for softbody! MT_Matrix3x3 newori = groupobj->NodeGetWorldOrientation() * gameobj->NodeGetWorldOrientation(); replica->NodeSetLocalOrientation(newori); // update scenegraph for entire tree of children replica->GetSGNode()->UpdateWorldData(0); replica->GetSGNode()->SetBBox(gameobj->GetSGNode()->BBox()); replica->GetSGNode()->SetRadius(gameobj->GetSGNode()->Radius()); // we can now add the graphic controller to the physic engine replica->ActivateGraphicController(true); // set references for dupli-group // groupobj holds a list of all objects, that belongs to this group groupobj->AddInstanceObjects(replica); // every object gets the reference to its dupli-group object replica->SetDupliGroupObject(groupobj); // done with replica replica->Release(); } // the logic must be replicated first because we need // the new logic bricks before relinking vector::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()); // If the object was a light, we need to update it's RAS_LightObject as well if ((*git)->GetGameObjectType()==SCA_IObject::OBJ_LIGHT) { KX_LightObject* lightobj = static_cast(*git); lightobj->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, class CValue* parentobject, int lifespan) { 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' KX_GameObject* originalobj = (KX_GameObject*) originalobject; KX_GameObject* parentobj = (KX_GameObject*) parentobject; m_ueberExecutionPriority++; // lets create a replica KX_GameObject* replica = (KX_GameObject*) AddNodeReplicaObject(NULL,originalobj); if (lifespan > 0) { // add a timebomb to this object // for now, convert between so called frames and realtime m_tempObjectList->Add(replica->AddRef()); // this convert the life from frames to sort-of seconds, hard coded 0.02 that assumes we have 50 frames per second // if you change this value, make sure you change it in KX_GameObject::pyattr_get_life property too CValue *fval = new CFloatValue(lifespan*0.02); replica->SetProperty("::timebomb",fval); fval->Release(); } // 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 = originalobj->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); } // At this stage all the objects in the hierarchy have been duplicated, // we can update the scenegraph, we need it for the duplication of logic MT_Point3 newpos = ((KX_GameObject*) parentobject)->NodeGetWorldPosition(); replica->NodeSetLocalPosition(newpos); MT_Matrix3x3 newori = ((KX_GameObject*) parentobject)->NodeGetWorldOrientation(); replica->NodeSetLocalOrientation(newori); // get the rootnode's scale MT_Vector3 newscale = parentobj->GetSGNode()->GetRootSGParent()->GetLocalScale(); // set the replica's relative scale with the rootnode's scale replica->NodeSetRelativeScale(newscale); replica->GetSGNode()->UpdateWorldData(0); replica->GetSGNode()->SetBBox(originalobj->GetSGNode()->BBox()); replica->GetSGNode()->SetRadius(originalobj->GetSGNode()->Radius()); // the size is correct, we can add the graphic controller to the physic engine replica->ActivateGraphicController(true); // now replicate logic vector::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()); // If the object was a light, we need to update it's RAS_LightObject as well if ((*git)->GetGameObjectType()==SCA_IObject::OBJ_LIGHT) { KX_LightObject* lightobj = static_cast(*git); lightobj->SetLayer(parentobj->GetLayer()); } } // replicate crosslinks etc. between logic bricks for (git = m_logicHierarchicalGameObjects.begin();!(git==m_logicHierarchicalGameObjects.end());++git) { ReplicateLogic((*git)); } // check if there are objects with dupligroup in the hierarchy vector duplilist; for (git = m_logicHierarchicalGameObjects.begin();!(git==m_logicHierarchicalGameObjects.end());++git) { if ((*git)->IsDupliGroup()) { // separate list as m_logicHierarchicalGameObjects is also used by DupliGroupRecurse() duplilist.push_back(*git); } } for (git = duplilist.begin();!(git==duplilist.end());++git) { DupliGroupRecurse(*git, 0); } // don't release replica here because we are returning it, not done with it... return replica; } void KX_Scene::RemoveObject(class CValue* gameobj) { KX_GameObject* newobj = (KX_GameObject*) gameobj; // disconnect child from parent SG_Node* node = newobj->GetSGNode(); if (node) { node->DisconnectFromParent(); // recursively destruct node->Destruct(); } //no need to do that: the object is destroyed and memory released //newobj->SetSGNode(0); } void KX_Scene::DelayedRemoveObject(class CValue* gameobj) { //KX_GameObject* newobj = (KX_GameObject*) gameobj; if (!m_euthanasyobjects->SearchValue(gameobj)) { m_euthanasyobjects->Add(gameobj->AddRef()); } } int KX_Scene::NewRemoveObject(class CValue* gameobj) { int ret; KX_GameObject* newobj = (KX_GameObject*) gameobj; /* remove property from debug list */ RemoveObjectDebugProperties(newobj); /* Invalidate the python reference, since the object may exist in script lists * its possible that it wont be automatically invalidated, so do it manually here, * * if for some reason the object is added back into the scene python can always get a new Proxy */ newobj->InvalidateProxy(); // keep the blender->game object association up to date // note that all the replicas of an object will have the same // blender object, that's why we need to check the game object // as only the deletion of the original object must be recorded m_logicmgr->UnregisterGameObj(newobj->GetBlenderObject(), gameobj); //todo: look at this //GetPhysicsEnvironment()->RemovePhysicsController(gameobj->getPhysicsController()); // remove all sensors/controllers/actuators from logicsystem... SCA_SensorList& sensors = newobj->GetSensors(); for (SCA_SensorList::iterator its = sensors.begin(); !(its==sensors.end());its++) { m_logicmgr->RemoveSensor(*its); } SCA_ControllerList& controllers = newobj->GetControllers(); for (SCA_ControllerList::iterator itc = controllers.begin(); !(itc==controllers.end());itc++) { m_logicmgr->RemoveController(*itc); (*itc)->ReParent(NULL); } SCA_ActuatorList& actuators = newobj->GetActuators(); for (SCA_ActuatorList::iterator ita = actuators.begin(); !(ita==actuators.end());ita++) { m_logicmgr->RemoveActuator(*ita); } // the sensors/controllers/actuators must also be released, this is done in ~SCA_IObject // now remove the timer properties from the time manager int numprops = newobj->GetPropertyCount(); for (int i = 0; i < numprops; i++) { CValue* propval = newobj->GetProperty(i); if (propval->GetProperty("timer")) { m_timemgr->RemoveTimeProperty(propval); } } // if the object is the dupligroup proxy, you have to cleanup all m_pDupliGroupObject's in all // instances refering to this group if (newobj->GetInstanceObjects()) { for (int i = 0; i < newobj->GetInstanceObjects()->GetCount(); i++) { KX_GameObject* instance = (KX_GameObject*)newobj->GetInstanceObjects()->GetValue(i); instance->RemoveDupliGroupObject(); } } // if this object was part of a group, make sure to remove it from that group's instance list KX_GameObject* group = newobj->GetDupliGroupObject(); if (group) group->RemoveInstanceObject(newobj); newobj->RemoveMeshes(); ret = 1; if (newobj->GetGameObjectType()==SCA_IObject::OBJ_LIGHT && m_lightlist->RemoveValue(newobj)) ret = newobj->Release(); if (m_objectlist->RemoveValue(newobj)) ret = newobj->Release(); if (m_tempObjectList->RemoveValue(newobj)) ret = newobj->Release(); if (m_parentlist->RemoveValue(newobj)) ret = newobj->Release(); if (m_inactivelist->RemoveValue(newobj)) ret = newobj->Release(); if (m_euthanasyobjects->RemoveValue(newobj)) ret = newobj->Release(); if (m_animatedlist->RemoveValue(newobj)) ret = newobj->Release(); if (newobj == m_active_camera) { //no AddRef done on m_active_camera so no Release //m_active_camera->Release(); m_active_camera = NULL; } // in case this is a camera m_cameras.remove((KX_Camera*)newobj); // in case this is a font m_fonts.remove((KX_FontObject*)newobj); /* currently does nothing, keep in case we need to Unregister something */ #if 0 if (m_sceneConverter) m_sceneConverter->UnregisterGameObject(newobj); #endif // return value will be 0 if the object is actually deleted (all reference gone) return ret; } void KX_Scene::ReplaceMesh(class CValue* obj,void* meshobj, bool use_gfx, bool use_phys) { KX_GameObject* gameobj = static_cast(obj); RAS_MeshObject* mesh = static_cast(meshobj); if (!gameobj) { std::cout << "KX_Scene::ReplaceMesh Warning: invalid object, doing nothing" << std::endl; return; } if (use_gfx && mesh != NULL) { gameobj->RemoveMeshes(); gameobj->AddMesh(mesh); if (gameobj->m_isDeformable) { BL_DeformableGameObject* newobj = static_cast( gameobj ); if (newobj->GetDeformer()) { delete newobj->GetDeformer(); newobj->SetDeformer(NULL); } if (mesh->GetMesh()) { // we must create a new deformer but which one? KX_GameObject* parentobj = newobj->GetParent(); // this always return the original game object (also for replicate) Object* blendobj = newobj->GetBlenderObject(); // object that owns the new mesh Object* oldblendobj = static_cast(m_logicmgr->FindBlendObjByGameMeshName(mesh->GetName())); Mesh* blendmesh = mesh->GetMesh(); bool bHasModifier = BL_ModifierDeformer::HasCompatibleDeformer(blendobj); bool bHasShapeKey = blendmesh->key != NULL && blendmesh->key->type==KEY_RELATIVE; bool bHasDvert = blendmesh->dvert != NULL; bool bHasArmature = BL_ModifierDeformer::HasArmatureDeformer(blendobj) && parentobj && // current parent is armature parentobj->GetGameObjectType() == SCA_IObject::OBJ_ARMATURE && oldblendobj && // needed for mesh deform blendobj->parent && // original object had armature (not sure this test is needed) blendobj->parent->type == OB_ARMATURE && blendmesh->dvert!=NULL; // mesh has vertex group #ifdef WITH_BULLET bool bHasSoftBody = (!parentobj && (blendobj->gameflag & OB_SOFT_BODY)); #endif if (oldblendobj==NULL) { if (bHasModifier || bHasShapeKey || bHasDvert || bHasArmature) { std::cout << "warning: ReplaceMesh() new mesh is not used in an object from the current scene, you will get incorrect behavior" << std::endl; bHasShapeKey= bHasDvert= bHasArmature=bHasModifier= false; } } if (bHasModifier) { BL_ModifierDeformer* modifierDeformer; if (bHasShapeKey || bHasArmature) { modifierDeformer = new BL_ModifierDeformer( newobj, m_blenderScene, oldblendobj, blendobj, mesh, true, static_cast( parentobj->AddRef() ) ); modifierDeformer->LoadShapeDrivers(parentobj); } else { modifierDeformer = new BL_ModifierDeformer( newobj, m_blenderScene, oldblendobj, blendobj, mesh, false, NULL ); } newobj->SetDeformer(modifierDeformer); } else if (bHasShapeKey) { BL_ShapeDeformer* shapeDeformer; if (bHasArmature) { shapeDeformer = new BL_ShapeDeformer( newobj, oldblendobj, blendobj, mesh, true, true, static_cast( parentobj->AddRef() ) ); shapeDeformer->LoadShapeDrivers(parentobj); } else { shapeDeformer = new BL_ShapeDeformer( newobj, oldblendobj, blendobj, mesh, false, true, NULL ); } newobj->SetDeformer( shapeDeformer); } else if (bHasArmature) { BL_SkinDeformer* skinDeformer = new BL_SkinDeformer( newobj, oldblendobj, blendobj, mesh, true, true, static_cast( parentobj->AddRef() ) ); newobj->SetDeformer(skinDeformer); } else if (bHasDvert) { BL_MeshDeformer* meshdeformer = new BL_MeshDeformer( newobj, oldblendobj, mesh ); newobj->SetDeformer(meshdeformer); } #ifdef WITH_BULLET else if (bHasSoftBody) { KX_SoftBodyDeformer *softdeformer = new KX_SoftBodyDeformer(mesh, newobj); newobj->SetDeformer(softdeformer); } #endif } } gameobj->AddMeshUser(); } if (use_phys) { /* update the new assigned mesh with the physics mesh */ if (gameobj->GetPhysicsController()) gameobj->GetPhysicsController()->ReinstancePhysicsShape(NULL, use_gfx?NULL:mesh); } } /* Font Object routines */ void KX_Scene::AddFont(KX_FontObject* font) { if (!FindFont(font)) m_fonts.push_back(font); } KX_FontObject* KX_Scene::FindFont(KX_FontObject* font) { list::iterator it = m_fonts.begin(); while ((it != m_fonts.end()) && ((*it) != font)) { ++it; } return ((it == m_fonts.end()) ? NULL : (*it)); } /* Camera Object routines */ KX_Camera* KX_Scene::FindCamera(KX_Camera* cam) { list::iterator it = m_cameras.begin(); while ((it != m_cameras.end()) && ((*it) != cam)) { it++; } return ((it == m_cameras.end()) ? NULL : (*it)); } KX_Camera* KX_Scene::FindCamera(STR_String& name) { list::iterator it = m_cameras.begin(); while ((it != m_cameras.end()) && ((*it)->GetName() != name)) { it++; } return ((it == m_cameras.end()) ? NULL : (*it)); } void KX_Scene::AddCamera(KX_Camera* cam) { if (!FindCamera(cam)) m_cameras.push_back(cam); } KX_Camera* KX_Scene::GetActiveCamera() { // NULL if not defined return m_active_camera; } void KX_Scene::SetActiveCamera(KX_Camera* cam) { // only set if the cam is in the active list? Or add it otherwise? if (!FindCamera(cam)) { AddCamera(cam); if (cam) std::cout << "Added cam " << cam->GetName() << std::endl; } m_active_camera = cam; } void KX_Scene::SetCameraOnTop(KX_Camera* cam) { if (!FindCamera(cam)) { // adding is always done at the back, so that's all that needs to be done AddCamera(cam); if (cam) std::cout << "Added cam " << cam->GetName() << std::endl; } else { m_cameras.remove(cam); m_cameras.push_back(cam); } } void KX_Scene::MarkVisible(SG_Tree *node, RAS_IRasterizer* rasty, KX_Camera* cam, int layer) { int intersect = KX_Camera::INTERSECT; KX_GameObject *gameobj = node->Client()?(KX_GameObject*) node->Client()->GetSGClientObject():NULL; bool visible = (gameobj && gameobj->GetVisible() && (!layer || (gameobj->GetLayer() & layer))); bool dotest = visible || node->Left() || node->Right(); /* If the camera is inside the box, assume intersect. */ if (dotest && !node->inside( cam->NodeGetWorldPosition())) { MT_Scalar radius = node->Radius(); MT_Point3 center = node->Center(); intersect = cam->SphereInsideFrustum(center, radius); if (intersect == KX_Camera::INTERSECT) { MT_Point3 box[8]; node->get(box); intersect = cam->BoxInsideFrustum(box); } } switch (intersect) { case KX_Camera::OUTSIDE: MarkSubTreeVisible(node, rasty, false, cam); break; case KX_Camera::INTERSECT: if (gameobj) MarkVisible(rasty, gameobj, cam, layer); if (node->Left()) MarkVisible(node->Left(), rasty, cam, layer); if (node->Right()) MarkVisible(node->Right(), rasty, cam, layer); break; case KX_Camera::INSIDE: MarkSubTreeVisible(node, rasty, true, cam, layer); break; } } void KX_Scene::MarkSubTreeVisible(SG_Tree *node, RAS_IRasterizer* rasty, bool visible, KX_Camera* cam, int layer) { if (node->Client()) { KX_GameObject *gameobj = (KX_GameObject*) node->Client()->GetSGClientObject(); if (gameobj->GetVisible()) { if (visible) { int nummeshes = gameobj->GetMeshCount(); // this adds the vertices to the display list for (int m=0;mGetMesh(m))->SchedulePolygons(rasty->GetDrawingMode()); } gameobj->SetCulled(!visible); gameobj->UpdateBuckets(false); } } if (node->Left()) MarkSubTreeVisible(node->Left(), rasty, visible, cam, layer); if (node->Right()) MarkSubTreeVisible(node->Right(), rasty, visible, cam, layer); } void KX_Scene::MarkVisible(RAS_IRasterizer* rasty, KX_GameObject* gameobj,KX_Camera* cam,int layer) { // User (Python/Actuator) has forced object invisible... if (!gameobj->GetSGNode() || !gameobj->GetVisible()) return; // Shadow lamp layers if (layer && !(gameobj->GetLayer() & layer)) { gameobj->SetCulled(true); gameobj->UpdateBuckets(false); return; } // If Frustum culling is off, the object is always visible. bool vis = !cam->GetFrustumCulling(); // If the camera is inside this node, then the object is visible. if (!vis) { vis = gameobj->GetSGNode()->inside( cam->GetCameraLocation() ); } // Test the object's bound sphere against the view frustum. if (!vis) { MT_Vector3 scale = gameobj->GetSGNode()->GetWorldScaling(); MT_Scalar radius = fabs(scale[scale.closestAxis()] * gameobj->GetSGNode()->Radius()); switch (cam->SphereInsideFrustum(gameobj->NodeGetWorldPosition(), radius)) { case KX_Camera::INSIDE: vis = true; break; case KX_Camera::OUTSIDE: vis = false; break; case KX_Camera::INTERSECT: // Test the object's bound box against the view frustum. MT_Point3 box[8]; gameobj->GetSGNode()->getBBox(box); vis = cam->BoxInsideFrustum(box) != KX_Camera::OUTSIDE; break; } } if (vis) { int nummeshes = gameobj->GetMeshCount(); for (int m=0;mGetMesh(m))->SchedulePolygons(rasty->GetDrawingMode()); } // Visibility/ non-visibility are marked // elsewhere now. gameobj->SetCulled(false); gameobj->UpdateBuckets(false); } else { gameobj->SetCulled(true); gameobj->UpdateBuckets(false); } } void KX_Scene::PhysicsCullingCallback(KX_ClientObjectInfo *objectInfo, void* cullingInfo) { KX_GameObject* gameobj = objectInfo->m_gameobject; if (!gameobj->GetVisible()) // ideally, invisible objects should be removed from the culling tree temporarily return; if (((CullingInfo*)cullingInfo)->m_layer && !(gameobj->GetLayer() & ((CullingInfo*)cullingInfo)->m_layer)) // used for shadow: object is not in shadow layer return; // make object visible gameobj->SetCulled(false); gameobj->UpdateBuckets(false); } void KX_Scene::CalculateVisibleMeshes(RAS_IRasterizer* rasty,KX_Camera* cam, int layer) { bool dbvt_culling = false; if (m_dbvt_culling) { // test culling through Bullet MT_Vector4 planes[6]; // get the clip planes MT_Vector4* cplanes = cam->GetNormalizedClipPlanes(); // and convert planes[0].setValue(cplanes[4].getValue()); // near planes[1].setValue(cplanes[5].getValue()); // far planes[2].setValue(cplanes[0].getValue()); // left planes[3].setValue(cplanes[1].getValue()); // right planes[4].setValue(cplanes[2].getValue()); // top planes[5].setValue(cplanes[3].getValue()); // bottom CullingInfo info(layer); double mvmat[16] = {0}; cam->GetModelviewMatrix().getValue(mvmat); double pmat[16] = {0}; cam->GetProjectionMatrix().getValue(pmat); dbvt_culling = m_physicsEnvironment->CullingTest(PhysicsCullingCallback,&info,planes,5,m_dbvt_occlusion_res, KX_GetActiveEngine()->GetCanvas()->GetViewPort(), mvmat, pmat); } if (!dbvt_culling) { // the physics engine couldn't help us, do it the hard way for (int i = 0; i < m_objectlist->GetCount(); i++) { MarkVisible(rasty, static_cast(m_objectlist->GetValue(i)), cam, layer); } } // Now that we know visible meshes, update LoDs UpdateObjectLods(); } // logic stuff void KX_Scene::LogicBeginFrame(double curtime) { // have a look at temp objects ... int lastobj = m_tempObjectList->GetCount() - 1; for (int i = lastobj; i >= 0; i--) { CValue* objval = m_tempObjectList->GetValue(i); CFloatValue* propval = (CFloatValue*) objval->GetProperty("::timebomb"); if (propval) { float timeleft = propval->GetNumber() - 1.0/KX_KetsjiEngine::GetTicRate(); if (timeleft > 0) { propval->SetFloat(timeleft); } else { DelayedRemoveObject(objval); // remove obj } } else { // all object is the tempObjectList should have a clock } } m_logicmgr->BeginFrame(curtime, 1.0/KX_KetsjiEngine::GetTicRate()); } void KX_Scene::AddAnimatedObject(CValue* gameobj) { gameobj->AddRef(); m_animatedlist->Add(gameobj); } static void update_anim_thread_func(TaskPool *pool, void *taskdata, int UNUSED(threadid)) { KX_GameObject *gameobj, *child; CListValue *children; bool needs_update; double curtime = *(double*)BLI_task_pool_userdata(pool); gameobj = (KX_GameObject*)taskdata; // Non-armature updates are fast enough, so just update them needs_update = gameobj->GetGameObjectType() != SCA_IObject::OBJ_ARMATURE; if (!needs_update) { // If we got here, we're looking to update an armature, so check its children meshes // to see if we need to bother with a more expensive pose update children = gameobj->GetChildren(); bool has_mesh = false, has_non_mesh = false; // Check for meshes that haven't been culled for (int j=0; jGetCount(); ++j) { child = (KX_GameObject*)children->GetValue(j); if (!child->GetCulled()) { needs_update = true; break; } if (child->GetMeshCount() == 0) has_non_mesh = true; else has_mesh = true; } // If we didn't find a non-culled mesh, check to see // if we even have any meshes, and update if this // armature has only non-mesh children. if (!needs_update && !has_mesh && has_non_mesh) needs_update = true; children->Release(); } if (needs_update) { gameobj->UpdateActionManager(curtime); children = gameobj->GetChildren(); if (!gameobj->GetParent() && gameobj->GetDeformer()) gameobj->GetDeformer()->Update(); for (int j=0; jGetCount(); ++j) { child = (KX_GameObject*)children->GetValue(j); if (child->GetDeformer()) { child->GetDeformer()->Update(); } } children->Release(); } } void KX_Scene::UpdateAnimations(double curtime) { KX_KetsjiEngine *engine = KX_GetActiveEngine(); if (engine->GetRestrictAnimationFPS()) { // Handle the animations independently of the logic time step double anim_timestep = 1.0 / GetAnimationFPS(); if (curtime - m_previousAnimTime < anim_timestep) return; // Sanity/debug print to make sure we're actually going at the fps we want (should be close to anim_timestep) // printf("Anim fps: %f\n", 1.0/(m_clockTime - m_previousAnimTime)); m_previousAnimTime = curtime; } TaskPool *pool = BLI_task_pool_create(KX_GetActiveEngine()->GetTaskScheduler(), &curtime); for (int i=0; iGetCount(); ++i) { BLI_task_pool_push(pool, update_anim_thread_func, m_animatedlist->GetValue(i), false, TASK_PRIORITY_LOW); } BLI_task_pool_work_and_wait(pool); BLI_task_pool_free(pool); for (int i=0; iGetCount(); ++i) { ((KX_GameObject*)m_animatedlist->GetValue(i))->UpdateActionIPOs(); } } void KX_Scene::LogicUpdateFrame(double curtime, bool frame) { m_logicmgr->UpdateFrame(curtime, frame); } void KX_Scene::LogicEndFrame() { m_logicmgr->EndFrame(); int numobj; KX_GameObject* obj; while ((numobj = m_euthanasyobjects->GetCount()) > 0) { // remove the object from this list to make sure we will not hit it again obj = (KX_GameObject*)m_euthanasyobjects->GetValue(numobj-1); m_euthanasyobjects->Remove(numobj-1); obj->Release(); RemoveObject(obj); } //prepare obstacle simulation for new frame if (m_obstacleSimulation) m_obstacleSimulation->UpdateObstacles(); } /** * UpdateParents: SceneGraph transformation update. */ void KX_Scene::UpdateParents(double curtime) { // we use the SG dynamic list SG_Node* node; while ((node = SG_Node::GetNextScheduled(m_sghead)) != NULL) { node->UpdateWorldData(curtime); } //for (int i=0; iGetCount(); i++) //{ // KX_GameObject* parentobj = (KX_GameObject*)GetRootParentList()->GetValue(i); // parentobj->NodeUpdateGS(curtime); //} // the list must be empty here assert(m_sghead.Empty()); // some nodes may be ready for reschedule, move them to schedule list for next time while ((node = SG_Node::GetNextRescheduled(m_sghead)) != NULL) { node->Schedule(m_sghead); } } RAS_MaterialBucket* KX_Scene::FindBucket(class RAS_IPolyMaterial* polymat, bool &bucketCreated) { return m_bucketmanager->FindBucket(polymat, bucketCreated); } void KX_Scene::RenderBuckets(const MT_Transform & cameratransform, class RAS_IRasterizer* rasty) { m_bucketmanager->Renderbuckets(cameratransform,rasty); KX_BlenderMaterial::EndFrame(); } void KX_Scene::RenderFonts() { list::iterator it = m_fonts.begin(); while (it != m_fonts.end()) { (*it)->DrawFontText(); ++it; } } void KX_Scene::UpdateObjectLods(void) { KX_GameObject* gameobj; MT_Vector3 cam_pos = this->m_active_camera->NodeGetWorldPosition(); for (int i = 0; i < this->GetObjectList()->GetCount(); i++) { gameobj = (KX_GameObject*) GetObjectList()->GetValue(i); if (!gameobj->GetCulled()){ gameobj->UpdateLod(cam_pos); } } } void KX_Scene::UpdateObjectActivity(void) { if (m_activity_culling) { /* determine the activity criterium and set objects accordingly */ int i=0; MT_Point3 camloc = GetActiveCamera()->NodeGetWorldPosition(); //GetCameraLocation(); for (i=0;iGetCount();i++) { KX_GameObject* ob = (KX_GameObject*) GetObjectList()->GetValue(i); if (!ob->GetIgnoreActivityCulling()) { /* Simple test: more than 10 away from the camera, count * Manhattan distance. */ MT_Point3 obpos = ob->NodeGetWorldPosition(); if ((fabs(camloc[0] - obpos[0]) > m_activity_box_radius) || (fabs(camloc[1] - obpos[1]) > m_activity_box_radius) || (fabs(camloc[2] - obpos[2]) > m_activity_box_radius) ) { ob->Suspend(); } else { ob->Resume(); } } } } } void KX_Scene::SetActivityCullingRadius(float f) { if (f < 0.5) f = 0.5; m_activity_box_radius = f; } NG_NetworkDeviceInterface* KX_Scene::GetNetworkDeviceInterface() { return m_networkDeviceInterface; } NG_NetworkScene* KX_Scene::GetNetworkScene() { return m_networkScene; } void KX_Scene::SetNetworkDeviceInterface(NG_NetworkDeviceInterface* newInterface) { m_networkDeviceInterface = newInterface; } void KX_Scene::SetNetworkScene(NG_NetworkScene *newScene) { m_networkScene = newScene; } void KX_Scene::SetGravity(const MT_Vector3& gravity) { GetPhysicsEnvironment()->SetGravity(gravity[0],gravity[1],gravity[2]); } MT_Vector3 KX_Scene::GetGravity() { MT_Vector3 gravity; GetPhysicsEnvironment()->GetGravity(gravity); return gravity; } void KX_Scene::SetSceneConverter(class KX_BlenderSceneConverter* sceneConverter) { m_sceneConverter = sceneConverter; } void KX_Scene::SetPhysicsEnvironment(class PHY_IPhysicsEnvironment* physEnv) { m_physicsEnvironment = physEnv; if (m_physicsEnvironment) { KX_TouchEventManager* touchmgr = new KX_TouchEventManager(m_logicmgr, physEnv); m_logicmgr->RegisterEventManager(touchmgr); } } void KX_Scene::setSuspendedTime(double suspendedtime) { m_suspendedtime = suspendedtime; } double KX_Scene::getSuspendedTime() { return m_suspendedtime; } void KX_Scene::setSuspendedDelta(double suspendeddelta) { m_suspendeddelta = suspendeddelta; } double KX_Scene::getSuspendedDelta() { return m_suspendeddelta; } short KX_Scene::GetAnimationFPS() { return m_blenderScene->r.frs_sec; } static void MergeScene_LogicBrick(SCA_ILogicBrick* brick, KX_Scene *from, KX_Scene *to) { SCA_LogicManager *logicmgr= to->GetLogicManager(); brick->Replace_IScene(to); brick->Replace_NetworkScene(to->GetNetworkScene()); /* near sensors have physics controllers */ KX_TouchSensor *touch_sensor = dynamic_cast(brick); if (touch_sensor) { KX_TouchEventManager *tmgr = (KX_TouchEventManager*)from->GetLogicManager()->FindEventManager(SCA_EventManager::TOUCH_EVENTMGR); touch_sensor->UnregisterSumo(tmgr); touch_sensor->GetPhysicsController()->SetPhysicsEnvironment(to->GetPhysicsEnvironment()); touch_sensor->RegisterSumo(tmgr); } // If we end up replacing a KX_TouchEventManager, we need to make sure // physics controllers are properly in place. In other words, do this // after merging physics controllers! SCA_ISensor *sensor= dynamic_cast(brick); if (sensor) { sensor->Replace_EventManager(logicmgr); } SCA_2DFilterActuator *filter_actuator = dynamic_cast(brick); if (filter_actuator) { filter_actuator->SetScene(to); } #ifdef WITH_PYTHON // Python must be called from the main thread unless we want to deal // with GIL issues. So, this is delayed until here in case of async // libload (originally in KX_ConvertControllers) SCA_PythonController *pyctrl = dynamic_cast(brick); if (pyctrl) { pyctrl->SetNamespace(KX_GetActiveEngine()->GetPyNamespace()); if (pyctrl->m_mode==SCA_PythonController::SCA_PYEXEC_SCRIPT) pyctrl->Compile(); } #endif } static void MergeScene_GameObject(KX_GameObject* gameobj, KX_Scene *to, KX_Scene *from) { { SCA_ActuatorList& actuators= gameobj->GetActuators(); SCA_ActuatorList::iterator ita; for (ita = actuators.begin(); !(ita==actuators.end()); ++ita) { MergeScene_LogicBrick(*ita, from, to); } } { SCA_SensorList& sensors= gameobj->GetSensors(); SCA_SensorList::iterator its; for (its = sensors.begin(); !(its==sensors.end()); ++its) { MergeScene_LogicBrick(*its, from, to); } } { SCA_ControllerList& controllers= gameobj->GetControllers(); SCA_ControllerList::iterator itc; for (itc = controllers.begin(); !(itc==controllers.end()); ++itc) { SCA_IController *cont= *itc; MergeScene_LogicBrick(cont, from, to); vector linkedsensors = cont->GetLinkedSensors(); vector linkedactuators = cont->GetLinkedActuators(); for (vector::iterator ita = linkedactuators.begin();!(ita==linkedactuators.end());++ita) { MergeScene_LogicBrick(*ita, from, to); } for (vector::iterator its = linkedsensors.begin();!(its==linkedsensors.end());++its) { MergeScene_LogicBrick(*its, from, to); } } } /* graphics controller */ PHY_IController *ctrl = gameobj->GetGraphicController(); if (ctrl) { /* SHOULD update the m_cullingTree */ ctrl->SetPhysicsEnvironment(to->GetPhysicsEnvironment()); } ctrl = gameobj->GetPhysicsController(); if (ctrl) { ctrl->SetPhysicsEnvironment(to->GetPhysicsEnvironment()); } /* SG_Node can hold a scene reference */ SG_Node *sg= gameobj->GetSGNode(); if (sg) { if (sg->GetSGClientInfo() == from) { sg->SetSGClientInfo(to); /* Make sure to grab the children too since they might not be tied to a game object */ NodeList children = sg->GetSGChildren(); for (int i=0; iSetSGClientInfo(to); } } /* If the object is a light, update it's scene */ if (gameobj->GetGameObjectType() == SCA_IObject::OBJ_LIGHT) ((KX_LightObject*)gameobj)->UpdateScene(to); if (gameobj->GetGameObjectType() == SCA_IObject::OBJ_CAMERA) to->AddCamera((KX_Camera*)gameobj); /* Add the object to the scene's logic manager */ to->GetLogicManager()->RegisterGameObjectName(gameobj->GetName(), gameobj); to->GetLogicManager()->RegisterGameObj(gameobj->GetBlenderObject(), gameobj); for (int i=0; iGetMeshCount(); ++i) to->GetLogicManager()->RegisterGameMeshName(gameobj->GetMesh(i)->GetName(), gameobj->GetBlenderObject()); } bool KX_Scene::MergeScene(KX_Scene *other) { PHY_IPhysicsEnvironment *env = this->GetPhysicsEnvironment(); PHY_IPhysicsEnvironment *env_other = other->GetPhysicsEnvironment(); if ((env==NULL) != (env_other==NULL)) /* TODO - even when both scenes have NONE physics, the other is loaded with bullet enabled, ??? */ { printf("KX_Scene::MergeScene: physics scenes type differ, aborting\n"); printf("\tsource %d, terget %d\n", (int)(env!=NULL), (int)(env_other!=NULL)); return false; } if (GetSceneConverter() != other->GetSceneConverter()) { printf("KX_Scene::MergeScene: converters differ, aborting\n"); return false; } GetBucketManager()->MergeBucketManager(other->GetBucketManager(), this); /* active + inactive == all ??? - lets hope so */ for (int i = 0; i < other->GetObjectList()->GetCount(); i++) { KX_GameObject* gameobj = (KX_GameObject*)other->GetObjectList()->GetValue(i); MergeScene_GameObject(gameobj, this, other); /* add properties to debug list for LibLoad objects */ if (KX_GetActiveEngine()->GetAutoAddDebugProperties()) { AddObjectDebugProperties(gameobj); } gameobj->UpdateBuckets(false); /* only for active objects */ } for (int i = 0; i < other->GetInactiveList()->GetCount(); i++) { KX_GameObject* gameobj = (KX_GameObject*)other->GetInactiveList()->GetValue(i); MergeScene_GameObject(gameobj, this, other); } GetTempObjectList()->MergeList(other->GetTempObjectList()); other->GetTempObjectList()->ReleaseAndRemoveAll(); GetObjectList()->MergeList(other->GetObjectList()); other->GetObjectList()->ReleaseAndRemoveAll(); GetInactiveList()->MergeList(other->GetInactiveList()); other->GetInactiveList()->ReleaseAndRemoveAll(); GetRootParentList()->MergeList(other->GetRootParentList()); other->GetRootParentList()->ReleaseAndRemoveAll(); GetLightList()->MergeList(other->GetLightList()); other->GetLightList()->ReleaseAndRemoveAll(); if (env) env->MergeEnvironment(env_other); /* move materials across, assume they both use the same scene-converters * Do this after lights are merged so materials can use the lights in shaders */ GetSceneConverter()->MergeScene(this, other); /* merge logic */ { SCA_LogicManager *logicmgr= GetLogicManager(); SCA_LogicManager *logicmgr_other= other->GetLogicManager(); vectorevtmgrs= logicmgr->GetEventManagers(); //vectorevtmgrs_others= logicmgr_other->GetEventManagers(); //SCA_EventManager *evtmgr; SCA_EventManager *evtmgr_other; for (unsigned int i= 0; i < evtmgrs.size(); i++) { evtmgr_other= logicmgr_other->FindEventManager(evtmgrs[i]->GetType()); if (evtmgr_other) /* unlikely but possible one scene has a joystick and not the other */ evtmgr_other->Replace_LogicManager(logicmgr); /* when merging objects sensors are moved across into the new manager, don't need to do this here */ } /* grab any timer properties from the other scene */ SCA_TimeEventManager *timemgr= GetTimeEventManager(); SCA_TimeEventManager *timemgr_other= other->GetTimeEventManager(); vector times = timemgr_other->GetTimeValues(); for (unsigned int i= 0; i < times.size(); i++) { timemgr->AddTimeProperty(times[i]); } } return true; } void KX_Scene::Update2DFilter(vector& propNames, void* gameObj, RAS_2DFilterManager::RAS_2DFILTER_MODE filtermode, int pass, STR_String& text) { m_filtermanager.EnableFilter(propNames, gameObj, filtermode, pass, text); } void KX_Scene::Render2DFilters(RAS_ICanvas* canvas) { m_filtermanager.RenderFilters(canvas); } #ifdef WITH_PYTHON void KX_Scene::RunDrawingCallbacks(PyObject *cb_list) { Py_ssize_t len; if (cb_list && (len=PyList_GET_SIZE(cb_list))) { PyObject *args = PyTuple_New(0); // save python creating each call PyObject *func; PyObject *ret; // Iterate the list and run the callbacks for (Py_ssize_t pos=0; pos < len; pos++) { func= PyList_GET_ITEM(cb_list, pos); ret= PyObject_Call(func, args, NULL); if (ret==NULL) { PyErr_Print(); PyErr_Clear(); } else { Py_DECREF(ret); } } Py_DECREF(args); } } //---------------------------------------------------------------------------- //Python PyTypeObject KX_Scene::Type = { PyVarObject_HEAD_INIT(NULL, 0) "KX_Scene", sizeof(PyObjectPlus_Proxy), 0, py_base_dealloc, 0, 0, 0, 0, py_base_repr, 0, &Sequence, &Mapping, 0,0,0,0,0,0, Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, 0,0,0,0,0,0,0, Methods, 0, 0, &CValue::Type, 0,0,0,0,0,0, py_base_new }; PyMethodDef KX_Scene::Methods[] = { KX_PYMETHODTABLE(KX_Scene, addObject), KX_PYMETHODTABLE(KX_Scene, end), KX_PYMETHODTABLE(KX_Scene, restart), KX_PYMETHODTABLE(KX_Scene, replace), KX_PYMETHODTABLE(KX_Scene, suspend), KX_PYMETHODTABLE(KX_Scene, resume), KX_PYMETHODTABLE(KX_Scene, drawObstacleSimulation), /* dict style access */ KX_PYMETHODTABLE(KX_Scene, get), {NULL,NULL} //Sentinel }; static PyObject *Map_GetItem(PyObject *self_v, PyObject *item) { KX_Scene* self = static_castBGE_PROXY_REF(self_v); const char *attr_str= _PyUnicode_AsString(item); PyObject *pyconvert; if (self == NULL) { PyErr_SetString(PyExc_SystemError, "val = scene[key]: KX_Scene, " BGE_PROXY_ERROR_MSG); return NULL; } if (!self->m_attr_dict) self->m_attr_dict = PyDict_New(); if (self->m_attr_dict && (pyconvert=PyDict_GetItem(self->m_attr_dict, item))) { if (attr_str) PyErr_Clear(); Py_INCREF(pyconvert); return pyconvert; } else { if (attr_str) PyErr_Format(PyExc_KeyError, "value = scene[key]: KX_Scene, key \"%s\" does not exist", attr_str); else PyErr_SetString(PyExc_KeyError, "value = scene[key]: KX_Scene, key does not exist"); return NULL; } } static int Map_SetItem(PyObject *self_v, PyObject *key, PyObject *val) { KX_Scene* self = static_castBGE_PROXY_REF(self_v); const char *attr_str= _PyUnicode_AsString(key); if (attr_str==NULL) PyErr_Clear(); if (self == NULL) { PyErr_SetString(PyExc_SystemError, "scene[key] = value: KX_Scene, " BGE_PROXY_ERROR_MSG); return -1; } if (!self->m_attr_dict) self->m_attr_dict = PyDict_New(); if (val==NULL) { /* del ob["key"] */ int del= 0; if (self->m_attr_dict) del |= (PyDict_DelItem(self->m_attr_dict, key)==0) ? 1:0; if (del==0) { if (attr_str) PyErr_Format(PyExc_KeyError, "scene[key] = value: KX_Scene, key \"%s\" could not be set", attr_str); else PyErr_SetString(PyExc_KeyError, "del scene[key]: KX_Scene, key could not be deleted"); return -1; } else if (self->m_attr_dict) { PyErr_Clear(); /* PyDict_DelItem sets an error when it fails */ } } else { /* ob["key"] = value */ int set = 0; if (self->m_attr_dict==NULL) /* lazy init */ self->m_attr_dict= PyDict_New(); if (PyDict_SetItem(self->m_attr_dict, key, val)==0) set= 1; else PyErr_SetString(PyExc_KeyError, "scene[key] = value: KX_Scene, key not be added to internal dictionary"); if (set==0) return -1; /* pythons error value */ } return 0; /* success */ } static int Seq_Contains(PyObject *self_v, PyObject *value) { KX_Scene* self = static_castBGE_PROXY_REF(self_v); if (self == NULL) { PyErr_SetString(PyExc_SystemError, "val in scene: KX_Scene, " BGE_PROXY_ERROR_MSG); return -1; } if (!self->m_attr_dict) self->m_attr_dict = PyDict_New(); if (self->m_attr_dict && PyDict_GetItem(self->m_attr_dict, value)) return 1; return 0; } PyMappingMethods KX_Scene::Mapping = { (lenfunc)NULL, /* inquiry mp_length */ (binaryfunc)Map_GetItem, /* binaryfunc mp_subscript */ (objobjargproc)Map_SetItem, /* objobjargproc mp_ass_subscript */ }; PySequenceMethods KX_Scene::Sequence = { NULL, /* Cant set the len otherwise it can evaluate as false */ NULL, /* sq_concat */ NULL, /* sq_repeat */ NULL, /* sq_item */ NULL, /* sq_slice */ NULL, /* sq_ass_item */ NULL, /* sq_ass_slice */ (objobjproc)Seq_Contains, /* sq_contains */ (binaryfunc) NULL, /* sq_inplace_concat */ (ssizeargfunc) NULL, /* sq_inplace_repeat */ }; PyObject *KX_Scene::pyattr_get_name(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef) { KX_Scene* self = static_cast(self_v); return PyUnicode_From_STR_String(self->GetName()); } PyObject *KX_Scene::pyattr_get_objects(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef) { KX_Scene* self = static_cast(self_v); return self->GetObjectList()->GetProxy(); } PyObject *KX_Scene::pyattr_get_objects_inactive(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef) { KX_Scene* self = static_cast(self_v); return self->GetInactiveList()->GetProxy(); } PyObject *KX_Scene::pyattr_get_lights(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef) { KX_Scene* self = static_cast(self_v); return self->GetLightList()->GetProxy(); } PyObject *KX_Scene::pyattr_get_cameras(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef) { /* With refcounts in this case... * the new CListValue is owned by python, so its possible python holds onto it longer then the BGE * however this is the same with "scene.objects + []", when you make a copy by adding lists. */ KX_Scene* self = static_cast(self_v); CListValue* clist = new CListValue(); /* return self->GetCameras()->GetProxy(); */ list::iterator it = self->GetCameras()->begin(); while (it != self->GetCameras()->end()) { clist->Add((*it)->AddRef()); it++; } return clist->NewProxy(true); } PyObject *KX_Scene::pyattr_get_active_camera(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef) { KX_Scene* self = static_cast(self_v); KX_Camera* cam= self->GetActiveCamera(); if (cam) return self->GetActiveCamera()->GetProxy(); else Py_RETURN_NONE; } int KX_Scene::pyattr_set_active_camera(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef, PyObject *value) { KX_Scene* self = static_cast(self_v); KX_Camera *camOb; if (!ConvertPythonToCamera(value, &camOb, false, "scene.active_camera = value: KX_Scene")) return PY_SET_ATTR_FAIL; self->SetActiveCamera(camOb); return PY_SET_ATTR_SUCCESS; } PyObject *KX_Scene::pyattr_get_drawing_callback_pre(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef) { KX_Scene* self = static_cast(self_v); if (self->m_draw_call_pre==NULL) self->m_draw_call_pre= PyList_New(0); Py_INCREF(self->m_draw_call_pre); return self->m_draw_call_pre; } PyObject *KX_Scene::pyattr_get_drawing_callback_post(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef) { KX_Scene* self = static_cast(self_v); if (self->m_draw_call_post==NULL) self->m_draw_call_post= PyList_New(0); Py_INCREF(self->m_draw_call_post); return self->m_draw_call_post; } int KX_Scene::pyattr_set_drawing_callback_pre(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef, PyObject *value) { KX_Scene* self = static_cast(self_v); if (!PyList_CheckExact(value)) { PyErr_SetString(PyExc_ValueError, "Expected a list"); return PY_SET_ATTR_FAIL; } Py_XDECREF(self->m_draw_call_pre); Py_INCREF(value); self->m_draw_call_pre = value; return PY_SET_ATTR_SUCCESS; } int KX_Scene::pyattr_set_drawing_callback_post(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef, PyObject *value) { KX_Scene* self = static_cast(self_v); if (!PyList_CheckExact(value)) { PyErr_SetString(PyExc_ValueError, "Expected a list"); return PY_SET_ATTR_FAIL; } Py_XDECREF(self->m_draw_call_post); Py_INCREF(value); self->m_draw_call_post = value; return PY_SET_ATTR_SUCCESS; } PyObject *KX_Scene::pyattr_get_gravity(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef) { KX_Scene* self = static_cast(self_v); return PyObjectFrom(self->GetGravity()); } int KX_Scene::pyattr_set_gravity(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef, PyObject *value) { KX_Scene* self = static_cast(self_v); MT_Vector3 vec; if (!PyVecTo(value, vec)) return PY_SET_ATTR_FAIL; self->SetGravity(vec); return PY_SET_ATTR_SUCCESS; } PyAttributeDef KX_Scene::Attributes[] = { KX_PYATTRIBUTE_RO_FUNCTION("name", KX_Scene, pyattr_get_name), KX_PYATTRIBUTE_RO_FUNCTION("objects", KX_Scene, pyattr_get_objects), KX_PYATTRIBUTE_RO_FUNCTION("objectsInactive", KX_Scene, pyattr_get_objects_inactive), KX_PYATTRIBUTE_RO_FUNCTION("lights", KX_Scene, pyattr_get_lights), KX_PYATTRIBUTE_RO_FUNCTION("cameras", KX_Scene, pyattr_get_cameras), KX_PYATTRIBUTE_RW_FUNCTION("active_camera", KX_Scene, pyattr_get_active_camera, pyattr_set_active_camera), KX_PYATTRIBUTE_RW_FUNCTION("pre_draw", KX_Scene, pyattr_get_drawing_callback_pre, pyattr_set_drawing_callback_pre), KX_PYATTRIBUTE_RW_FUNCTION("post_draw", KX_Scene, pyattr_get_drawing_callback_post, pyattr_set_drawing_callback_post), KX_PYATTRIBUTE_RW_FUNCTION("gravity", KX_Scene, pyattr_get_gravity, pyattr_set_gravity), KX_PYATTRIBUTE_BOOL_RO("suspended", KX_Scene, m_suspend), KX_PYATTRIBUTE_BOOL_RO("activity_culling", KX_Scene, m_activity_culling), KX_PYATTRIBUTE_FLOAT_RW("activity_culling_radius", 0.5f, FLT_MAX, KX_Scene, m_activity_box_radius), KX_PYATTRIBUTE_BOOL_RO("dbvt_culling", KX_Scene, m_dbvt_culling), { NULL } //Sentinel }; KX_PYMETHODDEF_DOC(KX_Scene, addObject, "addObject(object, other, time=0)\n" "Returns the added object.\n") { PyObject *pyob, *pyother; KX_GameObject *ob, *other; int time = 0; if (!PyArg_ParseTuple(args, "OO|i:addObject", &pyob, &pyother, &time)) return NULL; if ( !ConvertPythonToGameObject(pyob, &ob, false, "scene.addObject(object, other, time): KX_Scene (first argument)") || !ConvertPythonToGameObject(pyother, &other, false, "scene.addObject(object, other, time): KX_Scene (second argument)") ) return NULL; if (!m_inactivelist->SearchValue(ob)) { PyErr_Format(PyExc_ValueError, "scene.addObject(object, other, time): KX_Scene (first argument): object must be in an inactive layer"); return NULL; } SCA_IObject* replica = AddReplicaObject((SCA_IObject*)ob, other, time); // release here because AddReplicaObject AddRef's // the object is added to the scene so we don't want python to own a reference replica->Release(); return replica->GetProxy(); } KX_PYMETHODDEF_DOC(KX_Scene, end, "end()\n" "Removes this scene from the game.\n") { KX_GetActiveEngine()->RemoveScene(m_sceneName); Py_RETURN_NONE; } KX_PYMETHODDEF_DOC(KX_Scene, restart, "restart()\n" "Restarts this scene.\n") { KX_GetActiveEngine()->ReplaceScene(m_sceneName, m_sceneName); Py_RETURN_NONE; } KX_PYMETHODDEF_DOC(KX_Scene, replace, "replace(newScene)\n" "Replaces this scene with another one.\n" "Return True if the new scene exists and scheduled for replacement, False otherwise.\n") { char* name; if (!PyArg_ParseTuple(args, "s:replace", &name)) return NULL; if (KX_GetActiveEngine()->ReplaceScene(m_sceneName, name)) Py_RETURN_TRUE; Py_RETURN_FALSE; } KX_PYMETHODDEF_DOC(KX_Scene, suspend, "suspend()\n" "Suspends this scene.\n") { Suspend(); Py_RETURN_NONE; } KX_PYMETHODDEF_DOC(KX_Scene, resume, "resume()\n" "Resumes this scene.\n") { Resume(); Py_RETURN_NONE; } KX_PYMETHODDEF_DOC(KX_Scene, drawObstacleSimulation, "drawObstacleSimulation()\n" "Draw debug visualization of obstacle simulation.\n") { if (GetObstacleSimulation()) GetObstacleSimulation()->DrawObstacles(); Py_RETURN_NONE; } /* Matches python dict.get(key, [default]) */ KX_PYMETHODDEF_DOC(KX_Scene, get, "") { PyObject *key; PyObject *def = Py_None; PyObject *ret; if (!PyArg_ParseTuple(args, "O|O:get", &key, &def)) return NULL; if (m_attr_dict && (ret=PyDict_GetItem(m_attr_dict, key))) { Py_INCREF(ret); return ret; } Py_INCREF(def); return def; } #endif // WITH_PYTHON