Merging with trunk up to r38631.
[blender.git] / source / gameengine / Ketsji / KX_Scene.cpp
index 5c19911fe5837554f5f39deb0acb4938747effa4..a49c1bf4b4cbd48f918ae500277b73d55d786cff 100644 (file)
@@ -15,7 +15,7 @@
  *
  * 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ * 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.
  * Ketsji scene. Holds references to all scene data.
  */
 
-#ifdef WIN32
+/** \file gameengine/Ketsji/KX_Scene.cpp
+ *  \ingroup ketsji
+ */
+
+
+#if defined(WIN32) && !defined(FREE_WINDOWS)
 #pragma warning (disable : 4786)
 #endif //WIN32
 
 #include "KX_Scene.h"
+#include "KX_PythonInit.h"
 #include "MT_assert.h"
 #include "KX_KetsjiEngine.h"
 #include "KX_BlenderMaterial.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_AlwaysEventManager.h"
+//#include "SCA_RandomEventManager.h"
+//#include "KX_RayEventManager.h"
 #include "KX_TouchEventManager.h"
 #include "SCA_KeyboardManager.h"
 #include "SCA_MouseManager.h"
-#include "SCA_PropertyEventManager.h"
+//#include "SCA_PropertyEventManager.h"
 #include "SCA_ActuatorEventManager.h"
+#include "SCA_BasicEventManager.h"
 #include "KX_Camera.h"
 #include "SCA_JoystickManager.h"
 
 #include "RAS_MeshObject.h"
-#include "BL_SkinMeshObject.h"
 
 #include "RAS_IRasterizer.h"
 #include "RAS_BucketManager.h"
 #include "SCA_IController.h"
 #include "SCA_IActuator.h"
 #include "SG_Node.h"
-#include "SYS_System.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 "BKE_anim.h"
 
 #include "KX_SG_NodeRelationships.h"
 
 #include "BL_ShapeDeformer.h"
 #include "BL_DeformableGameObject.h"
 
-// to get USE_BULLET!
-#include "KX_ConvertPhysicsObject.h"
-
 #ifdef USE_BULLET
+#include "KX_SoftBodyDeformer.h"
+#include "KX_ConvertPhysicsObject.h"
 #include "CcdPhysicsEnvironment.h"
 #include "CcdPhysicsController.h"
 #endif
 
 #include "KX_Light.h"
 
+#include <stdio.h>
+
 void* KX_SceneReplicationFunc(SG_IObject* node,void* gameobj,void* scene)
 {
        KX_GameObject* replica = ((KX_Scene*)scene)->AddNodeReplicaObject(node,(KX_GameObject*)gameobj);
@@ -135,7 +141,8 @@ KX_Scene::KX_Scene(class SCA_IInputDevice* keyboarddevice,
                                   class SCA_IInputDevice* mousedevice,
                                   class NG_NetworkDeviceInterface *ndi,
                                   const STR_String& sceneName,
-                                  Scene *scene): 
+                                  Scene *scene,
+                                  class RAS_ICanvas* canvas): 
        PyObjectPlus(),
        m_keyboardmgr(NULL),
        m_mousemgr(NULL),
@@ -166,27 +173,29 @@ KX_Scene::KX_Scene(class SCA_IInputDevice* keyboarddevice,
        
        m_timemgr = new SCA_TimeEventManager(m_logicmgr);
        m_keyboardmgr = new SCA_KeyboardManager(m_logicmgr,keyboarddevice);
-       m_mousemgr = new SCA_MouseManager(m_logicmgr,mousedevice);
+       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_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);
-       KX_RayEventManager* raymgr = new KX_RayEventManager(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(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(rndmgr);
+       //m_logicmgr->RegisterEventManager(raymgr);
        m_logicmgr->RegisterEventManager(netmgr);
+       m_logicmgr->RegisterEventManager(basicmgr);
 
 
        SYS_SystemHandle hSystem = SYS_GetSystem();
@@ -204,7 +213,11 @@ KX_Scene::KX_Scene(class SCA_IInputDevice* keyboarddevice,
 
        m_bucketmanager=new RAS_BucketManager();
        
+#ifdef WITH_PYTHON
        m_attr_dict = PyDict_New(); /* new ref */
+       m_draw_call_pre = NULL;
+       m_draw_call_post = NULL;
+#endif
 }
 
 
@@ -253,8 +266,16 @@ KX_Scene::~KX_Scene()
        {
                delete m_bucketmanager;
        }
+
+#ifdef WITH_PYTHON
        PyDict_Clear(m_attr_dict);
-       Py_DECREF(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()
@@ -263,6 +284,10 @@ RAS_BucketManager* KX_Scene::GetBucketManager()
 }
 
 
+CListValue* KX_Scene::GetTempObjectList()
+{
+       return m_tempObjectList;
+}
 
 CListValue* KX_Scene::GetObjectList()
 {
@@ -270,7 +295,6 @@ CListValue* KX_Scene::GetObjectList()
 }
 
 
-
 CListValue* KX_Scene::GetRootParentList()
 {
        return m_parentlist;
@@ -306,7 +330,10 @@ list<class KX_Camera*>* KX_Scene::GetCameras()
        return &m_cameras;
 }
 
-
+list<class KX_FontObject*>* KX_Scene::GetFonts()
+{
+       return &m_fonts;
+}
 
 void KX_Scene::SetFramingType(RAS_FrameSettings & frame_settings)
 {
@@ -467,7 +494,7 @@ KX_GameObject* KX_Scene::AddNodeReplicaObject(class SG_IObject* node, class CVal
 
        // this is the list of object that are send to the graphics pipeline
        m_objectlist->Add(newobj->AddRef());
-       if (newobj->IsLight())
+       if (newobj->GetGameObjectType()==SCA_IObject::OBJ_LIGHT)
                m_lightlist->Add(newobj->AddRef());
        newobj->AddMeshUser();
 
@@ -510,7 +537,7 @@ KX_GameObject* KX_Scene::AddNodeReplicaObject(class SG_IObject* node, class CVal
 // 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.
+// 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.
@@ -743,7 +770,7 @@ void KX_Scene::DupliGroupRecurse(CValue* obj, int level)
                // 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)->IsLight())
+               if ((*git)->GetGameObjectType()==SCA_IObject::OBJ_LIGHT)
                {
                        KX_LightObject* lightobj = static_cast<KX_LightObject*>(*git);
                        lightobj->GetLightData()->m_layer = groupobj->GetLayer();
@@ -795,6 +822,8 @@ SCA_IObject* KX_Scene::AddReplicaObject(class CValue* originalobject,
                // 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();
@@ -851,7 +880,7 @@ SCA_IObject* KX_Scene::AddReplicaObject(class CValue* originalobject,
                // 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)->IsLight())
+               if ((*git)->GetGameObjectType()==SCA_IObject::OBJ_LIGHT)
                {
                        KX_LightObject* lightobj = static_cast<KX_LightObject*>(*git);
                        lightobj->GetLightData()->m_layer = parentobj->GetLayer();
@@ -942,8 +971,8 @@ int KX_Scene::NewRemoveObject(class CValue* gameobj)
        {
                m_logicmgr->RemoveSensor(*its);
        }
-       
-    SCA_ControllerList& controllers = newobj->GetControllers();
+
+       SCA_ControllerList& controllers = newobj->GetControllers();
        for (SCA_ControllerList::iterator itc = controllers.begin();
                 !(itc==controllers.end());itc++)
        {
@@ -972,7 +1001,7 @@ int KX_Scene::NewRemoveObject(class CValue* gameobj)
        
        newobj->RemoveMeshes();
        ret = 1;
-       if (newobj->IsLight() && m_lightlist->RemoveValue(newobj))
+       if (newobj->GetGameObjectType()==SCA_IObject::OBJ_LIGHT && m_lightlist->RemoveValue(newobj))
                ret = newobj->Release();
        if (m_objectlist->RemoveValue(newobj))
                ret = newobj->Release();
@@ -995,6 +1024,9 @@ int KX_Scene::NewRemoveObject(class CValue* gameobj)
        // 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 incase we need to Unregister something */
 #if 0
        if (m_sceneConverter)
@@ -1033,7 +1065,7 @@ void KX_Scene::ReplaceMesh(class CValue* obj,void* meshobj, bool use_gfx, bool u
                        newobj->SetDeformer(NULL);
                }
 
-               if (mesh->IsDeformed())
+               if (mesh->GetMesh()) 
                {
                        // we must create a new deformer but which one?
                        KX_GameObject* parentobj = newobj->GetParent();
@@ -1047,19 +1079,24 @@ void KX_Scene::ReplaceMesh(class CValue* obj,void* meshobj, bool use_gfx, bool u
                        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 && 
-                               blendobj->partype==PARSKEL && 
+                               blendobj->parent->type == OB_ARMATURE &&
                                blendmesh->dvert!=NULL;                                         // mesh has vertex group
+#ifdef USE_BULLET
+                       bool bHasSoftBody = (!parentobj && (blendobj->gameflag & OB_SOFT_BODY));
+#endif
                        bool releaseParent = true;
 
                        
                        if (oldblendobj==NULL) {
-                               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 || 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)
@@ -1071,7 +1108,7 @@ void KX_Scene::ReplaceMesh(class CValue* obj,void* meshobj, bool use_gfx, bool u
                                                newobj,
                                                m_blenderScene,
                                                oldblendobj, blendobj,
-                                               static_cast<BL_SkinMeshObject*>(mesh),
+                                               mesh,
                                                true,
                                                static_cast<BL_ArmatureObject*>( parentobj )
                                        );
@@ -1084,7 +1121,7 @@ void KX_Scene::ReplaceMesh(class CValue* obj,void* meshobj, bool use_gfx, bool u
                                                newobj,
                                                m_blenderScene,
                                                oldblendobj, blendobj,
-                                               static_cast<BL_SkinMeshObject*>(mesh),
+                                               mesh,
                                                false,
                                                NULL
                                        );
@@ -1099,7 +1136,7 @@ void KX_Scene::ReplaceMesh(class CValue* obj,void* meshobj, bool use_gfx, bool u
                                        shapeDeformer = new BL_ShapeDeformer(
                                                newobj,
                                                oldblendobj, blendobj,
-                                               static_cast<BL_SkinMeshObject*>(mesh),
+                                               mesh,
                                                true,
                                                true,
                                                static_cast<BL_ArmatureObject*>( parentobj )
@@ -1112,7 +1149,7 @@ void KX_Scene::ReplaceMesh(class CValue* obj,void* meshobj, bool use_gfx, bool u
                                        shapeDeformer = new BL_ShapeDeformer(
                                                newobj,
                                                oldblendobj, blendobj,
-                                               static_cast<BL_SkinMeshObject*>(mesh),
+                                               mesh,
                                                false,
                                                true,
                                                NULL
@@ -1125,7 +1162,7 @@ void KX_Scene::ReplaceMesh(class CValue* obj,void* meshobj, bool use_gfx, bool u
                                BL_SkinDeformer* skinDeformer = new BL_SkinDeformer(
                                        newobj,
                                        oldblendobj, blendobj,
-                                       static_cast<BL_SkinMeshObject*>(mesh),
+                                       mesh,
                                        true,
                                        true,
                                        static_cast<BL_ArmatureObject*>( parentobj )
@@ -1136,10 +1173,17 @@ void KX_Scene::ReplaceMesh(class CValue* obj,void* meshobj, bool use_gfx, bool u
                        else if (bHasDvert)
                        {
                                BL_MeshDeformer* meshdeformer = new BL_MeshDeformer(
-                                       newobj, oldblendobj, static_cast<BL_SkinMeshObject*>(mesh)
+                                       newobj, oldblendobj, mesh
                                );
                                newobj->SetDeformer(meshdeformer);
                        }
+#ifdef USE_BULLET
+                       else if (bHasSoftBody)
+                       {
+                               KX_SoftBodyDeformer *softdeformer = new KX_SoftBodyDeformer(mesh, newobj);
+                               newobj->SetDeformer(softdeformer);
+                       }
+#endif
 
                        // release parent reference if its not being used 
                        if( releaseParent && parentobj)
@@ -1149,12 +1193,35 @@ void KX_Scene::ReplaceMesh(class CValue* obj,void* meshobj, bool use_gfx, bool u
 
        gameobj->AddMeshUser();
        }
-       
+
+#ifdef USE_BULLET
        if(use_phys) { /* update the new assigned mesh with the physics mesh */
                KX_ReInstanceBulletShapeFromMesh(gameobj, NULL, use_gfx?NULL:mesh);
        }
+#endif
+}
+
+/* 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<KX_FontObject*>::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<KX_Camera*>::iterator it = m_cameras.begin();
@@ -1435,7 +1502,12 @@ void KX_Scene::LogicBeginFrame(double curtime)
        m_logicmgr->BeginFrame(curtime, 1.0/KX_KetsjiEngine::GetTicRate());
 }
 
-
+void KX_Scene::UpdateAnimations(double curtime)
+{
+       // Update any animations
+       for (int i=0; i<GetObjectList()->GetCount(); ++i)
+               ((KX_GameObject*)GetObjectList()->GetValue(i))->UpdateActionManager(curtime);
+}
 
 void KX_Scene::LogicUpdateFrame(double curtime, bool frame)
 {
@@ -1447,7 +1519,7 @@ void KX_Scene::LogicUpdateFrame(double curtime, bool frame)
 void KX_Scene::LogicEndFrame()
 {
        m_logicmgr->EndFrame();
-       int numobj = m_euthanasyobjects->GetCount();
+       int numobj;
 
        KX_GameObject* obj;
 
@@ -1578,10 +1650,10 @@ void KX_Scene::SetSceneConverter(class KX_BlenderSceneConverter* sceneConverter)
 void KX_Scene::SetPhysicsEnvironment(class PHY_IPhysicsEnvironment* physEnv)
 {
        m_physicsEnvironment = physEnv;
-
-       KX_TouchEventManager* touchmgr = new KX_TouchEventManager(m_logicmgr, physEnv);
-       m_logicmgr->RegisterEventManager(touchmgr);
-       return;
+       if(m_physicsEnvironment) {
+               KX_TouchEventManager* touchmgr = new KX_TouchEventManager(m_logicmgr, physEnv);
+               m_logicmgr->RegisterEventManager(touchmgr);
+       }
 }
  
 void KX_Scene::setSuspendedTime(double suspendedtime)
@@ -1601,6 +1673,261 @@ double KX_Scene::getSuspendedDelta()
        return m_suspendeddelta;
 }
 
+short KX_Scene::GetAnimationFPS()
+{
+       return m_blenderScene->r.frs_sec;
+}
+
+#ifdef USE_BULLET
+#include "KX_BulletPhysicsController.h"
+#endif
+
+static void MergeScene_LogicBrick(SCA_ILogicBrick* brick, KX_Scene *to)
+{
+       SCA_LogicManager *logicmgr= to->GetLogicManager();
+
+       brick->Replace_IScene(to);
+       brick->Replace_NetworkScene(to->GetNetworkScene());
+
+       SCA_ISensor *sensor=  dynamic_cast<class SCA_ISensor *>(brick);
+       if(sensor) {
+               sensor->Replace_EventManager(logicmgr);
+       }
+
+       /* near sensors have physics controllers */
+#ifdef USE_BULLET
+       KX_TouchSensor *touch_sensor = dynamic_cast<class KX_TouchSensor *>(brick);
+       if(touch_sensor) {
+               touch_sensor->GetPhysicsController()->SetPhysicsEnvironment(to->GetPhysicsEnvironment());
+       }
+#endif
+}
+
+#ifdef USE_BULLET
+#include "CcdGraphicController.h" // XXX  ctrl->SetPhysicsEnvironment(to->GetPhysicsEnvironment());
+#include "CcdPhysicsEnvironment.h" // XXX  ctrl->SetPhysicsEnvironment(to->GetPhysicsEnvironment());
+#include "KX_BulletPhysicsController.h"
+#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, to);
+               }
+       }
+
+
+       {
+               SCA_SensorList& sensors= gameobj->GetSensors();
+               SCA_SensorList::iterator its;
+
+               for (its = sensors.begin(); !(its==sensors.end()); ++its)
+               {
+                       MergeScene_LogicBrick(*its, 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, to);
+
+                       vector<SCA_ISensor*> linkedsensors = cont->GetLinkedSensors();
+                       vector<SCA_IActuator*> linkedactuators = cont->GetLinkedActuators();
+
+                       for (vector<SCA_IActuator*>::iterator ita = linkedactuators.begin();!(ita==linkedactuators.end());++ita) {
+                               MergeScene_LogicBrick(*ita, to);
+                       }
+
+                       for (vector<SCA_ISensor*>::iterator its = linkedsensors.begin();!(its==linkedsensors.end());++its) {
+                               MergeScene_LogicBrick(*its, to);
+                       }
+               }
+       }
+
+       /* graphics controller */
+       PHY_IGraphicController *ctrl = gameobj->GetGraphicController();
+       if(ctrl) {
+               /* SHOULD update the m_cullingTree */
+               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; i<children.size(); i++)
+                                       children[i]->SetSGClientInfo(to);
+               }
+#ifdef USE_BULLET
+               SGControllerList::iterator contit;
+               SGControllerList& controllers = sg->GetSGControllerList();
+               for (contit = controllers.begin();contit!=controllers.end();++contit)
+               {
+                       KX_BulletPhysicsController *phys_ctrl= dynamic_cast<KX_BulletPhysicsController *>(*contit);
+                       if (phys_ctrl)
+                               phys_ctrl->SetPhysicsEnvironment(to->GetPhysicsEnvironment());
+               }
+#endif // USE_BULLET
+       }
+       /* If the object is a light, update it's scene */
+       if (gameobj->GetGameObjectType() == SCA_IObject::OBJ_LIGHT)
+               ((KX_LightObject*)gameobj)->UpdateScene(to);
+
+       /* 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; i<gameobj->GetMeshCount(); ++i)
+               to->GetLogicManager()->RegisterGameMeshName(gameobj->GetMesh(i)->GetName(), gameobj->GetBlenderObject());
+}
+
+bool KX_Scene::MergeScene(KX_Scene *other)
+{
+#ifdef USE_BULLET
+       CcdPhysicsEnvironment *env=                     dynamic_cast<CcdPhysicsEnvironment *>(this->GetPhysicsEnvironment());
+       CcdPhysicsEnvironment *env_other=       dynamic_cast<CcdPhysicsEnvironment *>(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;
+       }
+#endif // USE_BULLET
+
+       if(GetSceneConverter() != other->GetSceneConverter()) {
+               printf("KX_Scene::MergeScene: converters differ, aborting\n");
+               return false;
+       }
+
+
+       GetBucketManager()->MergeBucketManager(other->GetBucketManager(), this);
+
+       /* move materials across, assume they both use the same scene-converters */
+       GetSceneConverter()->MergeScene(this, other);
+
+       /* 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);
+
+               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();
+
+#ifdef USE_BULLET
+       if(env) /* bullet scene? - dummy scenes dont need touching */
+               env->MergeEnvironment(env_other);
+#endif
+       
+       /* merge logic */
+       {
+               SCA_LogicManager *logicmgr=                     GetLogicManager();
+               SCA_LogicManager *logicmgr_other=       other->GetLogicManager();
+
+               vector<class SCA_EventManager*>evtmgrs= logicmgr->GetEventManagers();
+               //vector<class SCA_EventManager*>evtmgrs_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, dont need to do this here */
+               }
+
+               /* grab any timer properties from the other scene */
+               SCA_TimeEventManager *timemgr=          GetTimeEventManager();
+               SCA_TimeEventManager *timemgr_other=    other->GetTimeEventManager();
+               vector<CValue*> times = timemgr_other->GetTimeValues();
+
+               for(unsigned int i= 0; i < times.size(); i++) {
+                       timemgr->AddTimeProperty(times[i]);
+               }
+               
+       }
+       return true;
+}
+
+void KX_Scene::Update2DFilter(vector<STR_String>& 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)
+{
+       int 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 (int 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
 
@@ -1615,7 +1942,10 @@ PyTypeObject KX_Scene::Type = {
        0,
        0,
        py_base_repr,
-       0,0,0,0,0,0,0,0,0,
+       0,
+       &Sequence,
+       &Mapping,
+       0,0,0,0,0,0,
        Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
        0,0,0,0,0,0,0,
        Methods,
@@ -1627,13 +1957,124 @@ PyTypeObject KX_Scene::Type = {
 };
 
 PyMethodDef KX_Scene::Methods[] = {
-       KX_PYMETHODTABLE_NOARGS(KX_Scene, getLightList),
-       KX_PYMETHODTABLE_NOARGS(KX_Scene, getObjectList),
-       KX_PYMETHODTABLE_NOARGS(KX_Scene, getName),
        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),
+       
+       /* dict style access */
+       KX_PYMETHODTABLE(KX_Scene, get),
        
        {NULL,NULL} //Sentinel
 };
+static PyObject *Map_GetItem(PyObject *self_v, PyObject *item)
+{
+       KX_Scene* self= static_cast<KX_Scene*>BGE_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 && (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_cast<KX_Scene*>BGE_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 (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_cast<KX_Scene*>BGE_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 && 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)
 {
@@ -1699,14 +2140,70 @@ int KX_Scene::pyattr_set_active_camera(void *self_v, const KX_PYATTRIBUTE_DEF *a
        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<KX_Scene*>(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<KX_Scene*>(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<KX_Scene*>(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<KX_Scene*>(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;
+}
 
 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("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_RO_FUNCTION("lights",                    KX_Scene, pyattr_get_lights),
        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_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),
@@ -1714,33 +2211,6 @@ PyAttributeDef KX_Scene::Attributes[] = {
        { NULL }        //Sentinel
 };
 
-KX_PYMETHODDEF_DOC_NOARGS(KX_Scene, getLightList,
-"getLightList() -> list [KX_Light]\n"
-"Returns a list of all lights in the scene.\n"
-)
-{
-       ShowDeprecationWarning("getLightList()", "the lights property");
-       return m_lightlist->GetProxy();
-}
-
-KX_PYMETHODDEF_DOC_NOARGS(KX_Scene, getObjectList,
-"getObjectList() -> list [KX_GameObject]\n"
-"Returns a list of all game objects in the scene.\n"
-)
-{
-       ShowDeprecationWarning("getObjectList()", "the objects property");
-       return m_objectlist->GetProxy();
-}
-
-KX_PYMETHODDEF_DOC_NOARGS(KX_Scene, getName,
-"getName() -> string\n"
-"Returns the name of the scene.\n"
-)
-{
-       ShowDeprecationWarning("getName()", "the name property");
-       return PyUnicode_FromString(GetName());
-}
-
 KX_PYMETHODDEF_DOC(KX_Scene, addObject,
 "addObject(object, other, time=0)\n"
 "Returns the added object.\n")
@@ -1765,3 +2235,75 @@ KX_PYMETHODDEF_DOC(KX_Scene, addObject,
        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")
+{
+       char* name;
+       
+       if (!PyArg_ParseTuple(args, "s:replace", &name))
+               return NULL;
+       
+       KX_GetActiveEngine()->ReplaceScene(m_sceneName, name);
+       
+       Py_RETURN_NONE;
+}
+
+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;
+}
+
+/* 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