/* * ***** 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 ***** */ /** \file gameengine/Converter/KX_BlenderSceneConverter.cpp * \ingroup bgeconv */ #ifdef _MSC_VER # pragma warning (disable:4786) /* suppress stl-MSVC debug info warning */ #endif #include "KX_Scene.h" #include "KX_GameObject.h" #include "KX_IpoConvert.h" #include "RAS_MeshObject.h" #include "KX_PhysicsEngineEnums.h" #include "PHY_IPhysicsEnvironment.h" #include "KX_KetsjiEngine.h" #include "KX_PythonInit.h" // So we can handle adding new text datablocks for Python to import #include "BL_Material.h" #include "BL_ActionActuator.h" #include "KX_BlenderMaterial.h" #include "BL_System.h" #include "DummyPhysicsEnvironment.h" #include "KX_ConvertPhysicsObject.h" #ifdef WITH_BULLET #include "CcdPhysicsEnvironment.h" #endif #include "KX_LibLoadStatus.h" #include "KX_BlenderScalarInterpolator.h" #include "BL_BlenderDataConversion.h" #include "BlenderWorldInfo.h" /* This little block needed for linking to Blender... */ #ifdef WIN32 #include "BLI_winstuff.h" #endif /* This list includes only data type definitions */ #include "DNA_scene_types.h" #include "DNA_world_types.h" #include "BKE_main.h" #include "BLI_math.h" extern "C" { #include "DNA_object_types.h" #include "DNA_curve_types.h" #include "DNA_mesh_types.h" #include "DNA_material_types.h" #include "BLI_blenlib.h" #include "MEM_guardedalloc.h" #include "BKE_global.h" #include "BKE_animsys.h" #include "BKE_library.h" #include "BKE_material.h" // BKE_material_copy #include "BKE_mesh.h" // BKE_mesh_copy #include "DNA_space_types.h" #include "DNA_anim_types.h" #include "RNA_define.h" #include "../../blender/editors/include/ED_keyframing.h" } /* Only for dynamic loading and merging */ #include "RAS_BucketManager.h" // XXX cant stay #include "KX_BlenderSceneConverter.h" #include "KX_MeshProxy.h" extern "C" { #include "PIL_time.h" #include "BKE_context.h" #include "BLO_readfile.h" #include "BKE_idcode.h" #include "BKE_report.h" #include "DNA_space_types.h" #include "DNA_windowmanager_types.h" /* report api */ #include "../../blender/blenlib/BLI_linklist.h" } #include /* This is used to avoid including pthread.h in KX_BlenderSceneConverter.h */ typedef struct ThreadInfo { vector threads; pthread_mutex_t merge_lock; } ThreadInfo; KX_BlenderSceneConverter::KX_BlenderSceneConverter( struct Main* maggie, class KX_KetsjiEngine* engine ) : m_maggie(maggie), /*m_maggie_dyn(NULL),*/ m_ketsjiEngine(engine), m_alwaysUseExpandFraming(false), m_usemat(false), m_useglslmat(false), m_use_mat_cache(true) { BKE_main_id_tag_all(maggie, false); /* avoid re-tagging later on */ m_newfilename = ""; m_threadinfo = new ThreadInfo(); pthread_mutex_init(&m_threadinfo->merge_lock, NULL); } KX_BlenderSceneConverter::~KX_BlenderSceneConverter() { // clears meshes, and hashmaps from blender to gameengine data int i; // delete sumoshapes if (m_threadinfo) { vector::iterator pit = m_threadinfo->threads.begin(); while (pit != m_threadinfo->threads.end()) { pthread_join((*pit), NULL); pit++; } pthread_mutex_destroy(&m_threadinfo->merge_lock); delete m_threadinfo; } int numAdtLists = m_map_blender_to_gameAdtList.size(); for (i=0; i >::iterator itw = m_worldinfos.begin(); while (itw != m_worldinfos.end()) { delete (*itw).second; itw++; } m_worldinfos.clear(); vector >::iterator itp = m_polymaterials.begin(); while (itp != m_polymaterials.end()) { //m_polymat_cache.erase((*itp).second->GetBlenderMaterial()); delete (*itp).second; itp++; } m_polymaterials.clear(); // delete after RAS_IPolyMaterial vector >::iterator itmat = m_materials.begin(); while (itmat != m_materials.end()) { //m_mat_cache.erase((*itmat).second->material); delete (*itmat).second; itmat++; } m_materials.clear(); vector >::iterator itm = m_meshobjects.begin(); while (itm != m_meshobjects.end()) { delete (*itm).second; itm++; } m_meshobjects.clear(); #ifdef WITH_BULLET KX_ClearBulletSharedShapes(); #endif /* free any data that was dynamically loaded */ while (m_DynamicMaggie.size() != 0) { FreeBlendFile(m_DynamicMaggie[0]); } m_DynamicMaggie.clear(); } void KX_BlenderSceneConverter::SetNewFileName(const STR_String& filename) { m_newfilename = filename; } bool KX_BlenderSceneConverter::TryAndLoadNewFile() { bool result = false; // find the file /* if () { result = true; } // if not, clear the newfilename else { m_newfilename = ""; } */ return result; } Scene *KX_BlenderSceneConverter::GetBlenderSceneForName(const STR_String& name) { Scene *sce; /** * Find the specified scene by name, or NULL if nothing matches. */ if ((sce= (Scene *)BLI_findstring(&m_maggie->scene, name.ReadPtr(), offsetof(ID, name) + 2))) return sce; for (vector::iterator it=m_DynamicMaggie.begin(); !(it==m_DynamicMaggie.end()); it++) { Main *main= *it; if ((sce= (Scene *)BLI_findstring(&main->scene, name.ReadPtr(), offsetof(ID, name) + 2))) return sce; } return NULL; } #ifdef WITH_BULLET #include "LinearMath/btIDebugDraw.h" struct BlenderDebugDraw : public btIDebugDraw { BlenderDebugDraw () : m_debugMode(0) { } int m_debugMode; virtual void drawLine(const btVector3& from,const btVector3& to,const btVector3& color) { if (m_debugMode >0) { MT_Vector3 kxfrom(from[0],from[1],from[2]); MT_Vector3 kxto(to[0],to[1],to[2]); MT_Vector3 kxcolor(color[0],color[1],color[2]); KX_RasterizerDrawDebugLine(kxfrom,kxto,kxcolor); } } virtual void reportErrorWarning(const char* warningString) { } virtual void drawContactPoint(const btVector3& PointOnB,const btVector3& normalOnB,float distance,int lifeTime,const btVector3& color) { //not yet } virtual void setDebugMode(int debugMode) { m_debugMode = debugMode; } virtual int getDebugMode() const { return m_debugMode; } ///todo: find out if Blender can do this virtual void draw3dText(const btVector3& location,const char* textString) { } }; #endif void KX_BlenderSceneConverter::ConvertScene(class KX_Scene* destinationscene, class RAS_IRasterizer* rendertools, class RAS_ICanvas* canvas, bool libloading) { //find out which physics engine Scene *blenderscene = destinationscene->GetBlenderScene(); e_PhysicsEngine physics_engine = UseBullet; bool useDbvtCulling = false; // hook for registration function during conversion. m_currentScene = destinationscene; destinationscene->SetSceneConverter(this); // This doesn't really seem to do anything except cause potential issues // when doing threaded conversion, so it's disabled for now. // SG_SetActiveStage(SG_STAGE_CONVERTER); if (blenderscene) { switch (blenderscene->gm.physicsEngine) { case WOPHY_BULLET: { physics_engine = UseBullet; useDbvtCulling = (blenderscene->gm.mode & WO_DBVT_CULLING) != 0; break; } default: case WOPHY_NONE: { physics_engine = UseNone; break; } } } switch (physics_engine) { #ifdef WITH_BULLET case UseBullet: { CcdPhysicsEnvironment* ccdPhysEnv = new CcdPhysicsEnvironment(useDbvtCulling); ccdPhysEnv->SetDebugDrawer(new BlenderDebugDraw()); ccdPhysEnv->SetDeactivationLinearTreshold(blenderscene->gm.lineardeactthreshold); ccdPhysEnv->SetDeactivationAngularTreshold(blenderscene->gm.angulardeactthreshold); ccdPhysEnv->SetDeactivationTime(blenderscene->gm.deactivationtime); SYS_SystemHandle syshandle = SYS_GetSystem(); /*unused*/ int visualizePhysics = SYS_GetCommandLineInt(syshandle,"show_physics",0); if (visualizePhysics) ccdPhysEnv->SetDebugMode(btIDebugDraw::DBG_DrawWireframe|btIDebugDraw::DBG_DrawAabb|btIDebugDraw::DBG_DrawContactPoints|btIDebugDraw::DBG_DrawText|btIDebugDraw::DBG_DrawConstraintLimits|btIDebugDraw::DBG_DrawConstraints); //todo: get a button in blender ? //disable / enable debug drawing (contact points, aabb's etc) //ccdPhysEnv->setDebugMode(1); destinationscene->SetPhysicsEnvironment(ccdPhysEnv); break; } #endif default: case UseNone: physics_engine = UseNone; destinationscene ->SetPhysicsEnvironment(new DummyPhysicsEnvironment()); break; } BL_ConvertBlenderObjects(m_maggie, destinationscene, m_ketsjiEngine, physics_engine, rendertools, canvas, this, m_alwaysUseExpandFraming, libloading ); //These lookup are not needed during game m_map_blender_to_gameactuator.clear(); m_map_blender_to_gamecontroller.clear(); m_map_blender_to_gameobject.clear(); //Clearing this lookup table has the effect of disabling the cache of meshes //between scenes, even if they are shared in the blend file. //This cache mecanism is buggy so I leave it disable and the memory leak //that would result from this is fixed in RemoveScene() m_map_mesh_to_gamemesh.clear(); #ifndef WITH_BULLET /* quiet compiler warning */ (void)useDbvtCulling; #endif } // This function removes all entities stored in the converter for that scene // It should be used instead of direct delete scene // Note that there was some provision for sharing entities (meshes...) between // scenes but that is now disabled so all scene will have their own copy // and we can delete them here. If the sharing is reactivated, change this code too.. // (see KX_BlenderSceneConverter::ConvertScene) void KX_BlenderSceneConverter::RemoveScene(KX_Scene *scene) { int i, size; // delete the scene first as it will stop the use of entities delete scene; // delete the entities of this scene vector >::iterator worldit; size = m_worldinfos.size(); for (i=0, worldit=m_worldinfos.begin(); i >::iterator polymit; size = m_polymaterials.size(); for (i=0, polymit=m_polymaterials.begin(); iGetBlenderMaterial()); delete (*polymit).second; *polymit = m_polymaterials.back(); m_polymaterials.pop_back(); size--; } else { i++; polymit++; } } m_polymat_cache.erase(scene); vector >::iterator matit; size = m_materials.size(); for (i=0, matit=m_materials.begin(); imaterial); delete (*matit).second; *matit = m_materials.back(); m_materials.pop_back(); size--; } else { i++; matit++; } } m_mat_cache.erase(scene); vector >::iterator meshit; size = m_meshobjects.size(); for (i=0, meshit=m_meshobjects.begin(); i >::iterator it; for (it = m_materials.begin(); it != m_materials.end(); ++it) if (it->second == mat) return; m_materials.push_back(pair(m_currentScene,mat)); } void KX_BlenderSceneConverter::SetAlwaysUseExpandFraming( bool to_what) { m_alwaysUseExpandFraming= to_what; } void KX_BlenderSceneConverter::RegisterGameObject( KX_GameObject *gameobject, struct Object *for_blenderobject) { /* only maintained while converting, freed during game runtime */ m_map_blender_to_gameobject.insert(CHashedPtr(for_blenderobject),gameobject); } /* only need to run this during conversion since * m_map_blender_to_gameobject is freed after conversion */ void KX_BlenderSceneConverter::UnregisterGameObject( KX_GameObject *gameobject) { struct Object *bobp= gameobject->GetBlenderObject(); if (bobp) { CHashedPtr bptr(bobp); KX_GameObject **gobp= m_map_blender_to_gameobject[bptr]; if (gobp && *gobp == gameobject) { // also maintain m_map_blender_to_gameobject if the gameobject // being removed is matching the blender object m_map_blender_to_gameobject.remove(bptr); } } } KX_GameObject *KX_BlenderSceneConverter::FindGameObject( struct Object *for_blenderobject) { KX_GameObject **obp= m_map_blender_to_gameobject[CHashedPtr(for_blenderobject)]; return obp?*obp:NULL; } void KX_BlenderSceneConverter::RegisterGameMesh( RAS_MeshObject *gamemesh, struct Mesh *for_blendermesh) { if (for_blendermesh) { /* dynamically loaded meshes we don't want to keep lookups for */ m_map_mesh_to_gamemesh.insert(CHashedPtr(for_blendermesh),gamemesh); } m_meshobjects.push_back(pair(m_currentScene,gamemesh)); } RAS_MeshObject *KX_BlenderSceneConverter::FindGameMesh( struct Mesh *for_blendermesh/*, unsigned int onlayer*/) { RAS_MeshObject** meshp = m_map_mesh_to_gamemesh[CHashedPtr(for_blendermesh)]; if (meshp/* && onlayer==(*meshp)->GetLightLayer()*/) { return *meshp; } else { return NULL; } } void KX_BlenderSceneConverter::RegisterPolyMaterial(RAS_IPolyMaterial *polymat) { // First make sure we don't register the material twice vector >::iterator it; for (it = m_polymaterials.begin(); it != m_polymaterials.end(); ++it) if (it->second == polymat) return; m_polymaterials.push_back(pair(m_currentScene,polymat)); } void KX_BlenderSceneConverter::CachePolyMaterial(KX_Scene *scene, Material *mat, RAS_IPolyMaterial *polymat) { if (m_use_mat_cache && mat) m_polymat_cache[scene][mat] = polymat; } RAS_IPolyMaterial *KX_BlenderSceneConverter::FindCachedPolyMaterial(KX_Scene *scene, struct Material *mat) { return (m_use_mat_cache) ? m_polymat_cache[scene][mat] : NULL; } void KX_BlenderSceneConverter::CacheBlenderMaterial(KX_Scene *scene, struct Material *mat, BL_Material *blmat) { if (m_use_mat_cache && mat) m_mat_cache[scene][mat] = blmat; } BL_Material *KX_BlenderSceneConverter::FindCachedBlenderMaterial(KX_Scene *scene, struct Material *mat) { return (m_use_mat_cache) ? m_mat_cache[scene][mat] : NULL; } void KX_BlenderSceneConverter::RegisterInterpolatorList( BL_InterpolatorList *actList, struct bAction *for_act) { m_map_blender_to_gameAdtList.insert(CHashedPtr(for_act), actList); } BL_InterpolatorList *KX_BlenderSceneConverter::FindInterpolatorList( struct bAction *for_act) { BL_InterpolatorList **listp = m_map_blender_to_gameAdtList[CHashedPtr(for_act)]; return listp?*listp:NULL; } void KX_BlenderSceneConverter::RegisterGameActuator( SCA_IActuator *act, struct bActuator *for_actuator) { m_map_blender_to_gameactuator.insert(CHashedPtr(for_actuator), act); } SCA_IActuator *KX_BlenderSceneConverter::FindGameActuator( struct bActuator *for_actuator) { SCA_IActuator **actp = m_map_blender_to_gameactuator[CHashedPtr(for_actuator)]; return actp?*actp:NULL; } void KX_BlenderSceneConverter::RegisterGameController( SCA_IController *cont, struct bController *for_controller) { m_map_blender_to_gamecontroller.insert(CHashedPtr(for_controller), cont); } SCA_IController *KX_BlenderSceneConverter::FindGameController( struct bController *for_controller) { SCA_IController **contp = m_map_blender_to_gamecontroller[CHashedPtr(for_controller)]; return contp?*contp:NULL; } void KX_BlenderSceneConverter::RegisterWorldInfo( KX_WorldInfo *worldinfo) { m_worldinfos.push_back(pair(m_currentScene,worldinfo)); } void KX_BlenderSceneConverter::ResetPhysicsObjectsAnimationIpo(bool clearIpo) { KX_SceneList* scenes = m_ketsjiEngine->CurrentScenes(); int numScenes = scenes->size(); int i; for (i=0;iat(i); //PHY_IPhysicsEnvironment* physEnv = scene->GetPhysicsEnvironment(); CListValue* parentList = scene->GetRootParentList(); int numObjects = parentList->GetCount(); int g; for (g=0;gGetValue(g); if (gameObj->IsRecordAnimation()) { Object* blenderObject = gameObj->GetBlenderObject(); if (blenderObject) { #if 0 //erase existing ipo's Ipo* ipo = blenderObject->ipo;//findIpoForName(blenderObject->id.name+2); if (ipo) { //clear the curve data if (clearIpo) {//rcruiz IpoCurve *icu1; int numCurves = 0; for ( icu1 = (IpoCurve*)ipo->curve.first; icu1; ) { IpoCurve* tmpicu = icu1; /*int i; BezTriple *bezt; for ( bezt = tmpicu->bezt, i = 0; i < tmpicu->totvert; i++, bezt++) { printf("(%f,%f,%f),(%f,%f,%f),(%f,%f,%f)\n",bezt->vec[0][0],bezt->vec[0][1],bezt->vec[0][2],bezt->vec[1][0],bezt->vec[1][1],bezt->vec[1][2],bezt->vec[2][0],bezt->vec[2][1],bezt->vec[2][2]); }*/ icu1 = icu1->next; numCurves++; BLI_remlink( &( blenderObject->ipo->curve ), tmpicu ); if ( tmpicu->bezt ) MEM_freeN( tmpicu->bezt ); MEM_freeN( tmpicu ); localDel_ipoCurve( tmpicu ); } } } else { ipo = NULL; // XXX add_ipo(blenderObject->id.name+2, ID_OB); blenderObject->ipo = ipo; } #endif } } } } } void KX_BlenderSceneConverter::resetNoneDynamicObjectToIpo() { if (addInitFromFrame) { KX_SceneList* scenes = m_ketsjiEngine->CurrentScenes(); int numScenes = scenes->size(); if (numScenes>=0) { KX_Scene* scene = scenes->at(0); CListValue* parentList = scene->GetRootParentList(); for (int ix=0;ixGetCount();ix++) { KX_GameObject* gameobj = (KX_GameObject*)parentList->GetValue(ix); if (!gameobj->IsRecordAnimation()) { Object* blenderobject = gameobj->GetBlenderObject(); if (!blenderobject) continue; if (blenderobject->type==OB_ARMATURE) continue; float eu[3]; mat4_to_eul(eu,blenderobject->obmat); MT_Point3 pos = MT_Point3( blenderobject->obmat[3][0], blenderobject->obmat[3][1], blenderobject->obmat[3][2] ); MT_Vector3 eulxyz = MT_Vector3( eu[0], eu[1], eu[2] ); MT_Vector3 scale = MT_Vector3( blenderobject->size[0], blenderobject->size[1], blenderobject->size[2] ); gameobj->NodeSetLocalPosition(pos); gameobj->NodeSetLocalOrientation(MT_Matrix3x3(eulxyz)); gameobj->NodeSetLocalScale(scale); gameobj->NodeUpdateGS(0); } } } } } ///this generates ipo curves for position, rotation, allowing to use game physics in animation void KX_BlenderSceneConverter::WritePhysicsObjectToAnimationIpo(int frameNumber) { KX_SceneList* scenes = m_ketsjiEngine->CurrentScenes(); int numScenes = scenes->size(); int i; for (i=0;iat(i); //PHY_IPhysicsEnvironment* physEnv = scene->GetPhysicsEnvironment(); CListValue* parentList = scene->GetObjectList(); int numObjects = parentList->GetCount(); int g; for (g=0;gGetValue(g); Object* blenderObject = gameObj->GetBlenderObject(); if (blenderObject && blenderObject->parent==NULL && gameObj->IsRecordAnimation()) { if (blenderObject->adt==NULL) BKE_id_add_animdata(&blenderObject->id); if (blenderObject->adt) { const MT_Point3& position = gameObj->NodeGetWorldPosition(); //const MT_Vector3& scale = gameObj->NodeGetWorldScaling(); const MT_Matrix3x3& orn = gameObj->NodeGetWorldOrientation(); position.getValue(blenderObject->loc); float tmat[3][3]; for (int r=0;r<3;r++) for (int c=0;c<3;c++) tmat[r][c] = (float)orn[c][r]; mat3_to_compatible_eul(blenderObject->rot, blenderObject->rot, tmat); insert_keyframe(NULL, &blenderObject->id, NULL, NULL, "location", -1, (float)frameNumber, INSERTKEY_FAST); insert_keyframe(NULL, &blenderObject->id, NULL, NULL, "rotation_euler", -1, (float)frameNumber, INSERTKEY_FAST); #if 0 const MT_Point3& position = gameObj->NodeGetWorldPosition(); //const MT_Vector3& scale = gameObj->NodeGetWorldScaling(); const MT_Matrix3x3& orn = gameObj->NodeGetWorldOrientation(); float eulerAngles[3]; float eulerAnglesOld[3] = {0.0f, 0.0f, 0.0f}; float tmat[3][3]; // XXX animato Ipo* ipo = blenderObject->ipo; //create the curves, if not existing, set linear if new IpoCurve *icu_lx = findIpoCurve((IpoCurve *)ipo->curve.first,"LocX"); if (!icu_lx) { icu_lx = verify_ipocurve(&blenderObject->id, ipo->blocktype, NULL, NULL, NULL, OB_LOC_X, 1); if (icu_lx) icu_lx->ipo = IPO_LIN; } IpoCurve *icu_ly = findIpoCurve((IpoCurve *)ipo->curve.first,"LocY"); if (!icu_ly) { icu_ly = verify_ipocurve(&blenderObject->id, ipo->blocktype, NULL, NULL, NULL, OB_LOC_Y, 1); if (icu_ly) icu_ly->ipo = IPO_LIN; } IpoCurve *icu_lz = findIpoCurve((IpoCurve *)ipo->curve.first,"LocZ"); if (!icu_lz) { icu_lz = verify_ipocurve(&blenderObject->id, ipo->blocktype, NULL, NULL, NULL, OB_LOC_Z, 1); if (icu_lz) icu_lz->ipo = IPO_LIN; } IpoCurve *icu_rx = findIpoCurve((IpoCurve *)ipo->curve.first,"RotX"); if (!icu_rx) { icu_rx = verify_ipocurve(&blenderObject->id, ipo->blocktype, NULL, NULL, NULL, OB_ROT_X, 1); if (icu_rx) icu_rx->ipo = IPO_LIN; } IpoCurve *icu_ry = findIpoCurve((IpoCurve *)ipo->curve.first,"RotY"); if (!icu_ry) { icu_ry = verify_ipocurve(&blenderObject->id, ipo->blocktype, NULL, NULL, NULL, OB_ROT_Y, 1); if (icu_ry) icu_ry->ipo = IPO_LIN; } IpoCurve *icu_rz = findIpoCurve((IpoCurve *)ipo->curve.first,"RotZ"); if (!icu_rz) { icu_rz = verify_ipocurve(&blenderObject->id, ipo->blocktype, NULL, NULL, NULL, OB_ROT_Z, 1); if (icu_rz) icu_rz->ipo = IPO_LIN; } if (icu_rx) eulerAnglesOld[0] = eval_icu( icu_rx, frameNumber - 1 ) / ((180 / 3.14159265f) / 10); if (icu_ry) eulerAnglesOld[1] = eval_icu( icu_ry, frameNumber - 1 ) / ((180 / 3.14159265f) / 10); if (icu_rz) eulerAnglesOld[2] = eval_icu( icu_rz, frameNumber - 1 ) / ((180 / 3.14159265f) / 10); // orn.getValue((float *)tmat); // uses the wrong ordering, cant use this for (int r = 0; r < 3; r++) for (int c = 0; c < 3; c++) tmat[r][c] = orn[c][r]; // mat3_to_eul( eulerAngles,tmat); // better to use Mat3ToCompatibleEul mat3_to_compatible_eul( eulerAngles, eulerAnglesOld,tmat); //eval_icu for (int x = 0; x < 3; x++) eulerAngles[x] *= (float) ((180 / 3.14159265f) / 10.0); //fill the curves with data if (icu_lx) insert_vert_icu(icu_lx, frameNumber, position.x(), 1); if (icu_ly) insert_vert_icu(icu_ly, frameNumber, position.y(), 1); if (icu_lz) insert_vert_icu(icu_lz, frameNumber, position.z(), 1); if (icu_rx) insert_vert_icu(icu_rx, frameNumber, eulerAngles[0], 1); if (icu_ry) insert_vert_icu(icu_ry, frameNumber, eulerAngles[1], 1); if (icu_rz) insert_vert_icu(icu_rz, frameNumber, eulerAngles[2], 1); // Handles are corrected at the end, testhandles_ipocurve isn't needed yet #endif } } } } } void KX_BlenderSceneConverter::TestHandlesPhysicsObjectToAnimationIpo() { KX_SceneList* scenes = m_ketsjiEngine->CurrentScenes(); int numScenes = scenes->size(); int i; for (i=0;iat(i); //PHY_IPhysicsEnvironment* physEnv = scene->GetPhysicsEnvironment(); CListValue* parentList = scene->GetRootParentList(); int numObjects = parentList->GetCount(); int g; for (g=0;gGetValue(g); if (gameObj->IsRecordAnimation()) { #if 0 Object* blenderObject = gameObj->GetBlenderObject(); if (blenderObject && blenderObject->ipo) { // XXX animato Ipo* ipo = blenderObject->ipo; //create the curves, if not existing //testhandles_ipocurve checks for NULL testhandles_ipocurve(findIpoCurve((IpoCurve *)ipo->curve.first,"LocX")); testhandles_ipocurve(findIpoCurve((IpoCurve *)ipo->curve.first,"LocY")); testhandles_ipocurve(findIpoCurve((IpoCurve *)ipo->curve.first,"LocZ")); testhandles_ipocurve(findIpoCurve((IpoCurve *)ipo->curve.first,"RotX")); testhandles_ipocurve(findIpoCurve((IpoCurve *)ipo->curve.first,"RotY")); testhandles_ipocurve(findIpoCurve((IpoCurve *)ipo->curve.first,"RotZ")); } #endif } } } } #ifdef WITH_PYTHON PyObject *KX_BlenderSceneConverter::GetPyNamespace() { return m_ketsjiEngine->GetPyNamespace(); } #endif vector &KX_BlenderSceneConverter::GetMainDynamic() { return m_DynamicMaggie; } Main* KX_BlenderSceneConverter::GetMainDynamicPath(const char *path) { for (vector::iterator it=m_DynamicMaggie.begin(); !(it==m_DynamicMaggie.end()); it++) if (BLI_path_cmp((*it)->name, path) == 0) return *it; return NULL; } void KX_BlenderSceneConverter::MergeAsyncLoads() { vector *merge_scenes; vector::iterator mit; vector::iterator sit; pthread_mutex_lock(&m_threadinfo->merge_lock); for (mit=m_mergequeue.begin(); mit!=m_mergequeue.end(); ++mit) { merge_scenes = (vector*)(*mit)->GetData(); for (sit=merge_scenes->begin(); sit!=merge_scenes->end(); ++sit) { (*mit)->GetMergeScene()->MergeScene(*sit); delete (*sit); } delete merge_scenes; (*mit)->SetData(NULL); (*mit)->Finish(); } m_mergequeue.clear(); pthread_mutex_unlock(&m_threadinfo->merge_lock); } void KX_BlenderSceneConverter::AddScenesToMergeQueue(KX_LibLoadStatus *status) { pthread_mutex_lock(&m_threadinfo->merge_lock); m_mergequeue.push_back(status); pthread_mutex_unlock(&m_threadinfo->merge_lock); } static void *async_convert(void *ptr) { KX_Scene *new_scene = NULL; KX_LibLoadStatus *status = (KX_LibLoadStatus*)ptr; vector *scenes = (vector*)status->GetData(); vector *merge_scenes = new vector(); // Deleted in MergeAsyncLoads for (unsigned int i=0; isize(); ++i) { new_scene = status->GetEngine()->CreateScene((*scenes)[i], true); if (new_scene) merge_scenes->push_back(new_scene); status->AddProgress((1.f/scenes->size())*0.9f); // We'll call conversion 90% and merging 10% for now } delete scenes; status->SetData(merge_scenes); status->GetConverter()->AddScenesToMergeQueue(status); return NULL; } KX_LibLoadStatus *KX_BlenderSceneConverter::LinkBlendFileMemory(void *data, int length, const char *path, char *group, KX_Scene *scene_merge, char **err_str, short options) { BlendHandle *bpy_openlib = BLO_blendhandle_from_memory(data, length); // Error checking is done in LinkBlendFile return LinkBlendFile(bpy_openlib, path, group, scene_merge, err_str, options); } KX_LibLoadStatus *KX_BlenderSceneConverter::LinkBlendFilePath(const char *filepath, char *group, KX_Scene *scene_merge, char **err_str, short options) { BlendHandle *bpy_openlib = BLO_blendhandle_from_file(filepath, NULL); // Error checking is done in LinkBlendFile return LinkBlendFile(bpy_openlib, filepath, group, scene_merge, err_str, options); } static void load_datablocks(Main *main_newlib, BlendHandle *bpy_openlib, const char *path, int idcode) { Main *main_tmp= NULL; /* created only for linking, then freed */ LinkNode *names = NULL; short flag= 0; /* don't need any special options */ /* here appending/linking starts */ main_tmp = BLO_library_append_begin(main_newlib, &bpy_openlib, (char *)path); int totnames_dummy; names = BLO_blendhandle_get_datablock_names( bpy_openlib, idcode, &totnames_dummy); int i=0; LinkNode *n= names; while (n) { BLO_library_append_named_part(main_tmp, &bpy_openlib, (char *)n->link, idcode); n= (LinkNode *)n->next; i++; } BLI_linklist_free(names, free); /* free linklist *and* each node's data */ BLO_library_append_end(NULL, main_tmp, &bpy_openlib, idcode, flag); } KX_LibLoadStatus *KX_BlenderSceneConverter::LinkBlendFile(BlendHandle *bpy_openlib, const char *path, char *group, KX_Scene *scene_merge, char **err_str, short options) { Main *main_newlib; /* stored as a dynamic 'main' until we free it */ const int idcode = BKE_idcode_from_name(group); ReportList reports; static char err_local[255]; // TIMEIT_START(bge_link_blend_file); KX_LibLoadStatus *status; /* only scene and mesh supported right now */ if (idcode!=ID_SCE && idcode!=ID_ME &&idcode!=ID_AC) { snprintf(err_local, sizeof(err_local), "invalid ID type given \"%s\"\n", group); *err_str= err_local; BLO_blendhandle_close(bpy_openlib); return NULL; } if (GetMainDynamicPath(path)) { snprintf(err_local, sizeof(err_local), "blend file already open \"%s\"\n", path); *err_str= err_local; BLO_blendhandle_close(bpy_openlib); return NULL; } if (bpy_openlib==NULL) { snprintf(err_local, sizeof(err_local), "could not open blendfile \"%s\"\n", path); *err_str= err_local; return NULL; } main_newlib = BKE_main_new(); BKE_reports_init(&reports, RPT_STORE); load_datablocks(main_newlib, bpy_openlib, path, idcode); if (idcode==ID_SCE && options & LIB_LOAD_LOAD_SCRIPTS) { load_datablocks(main_newlib, bpy_openlib, path, ID_TXT); } /* now do another round of linking for Scenes so all actions are properly loaded */ if (idcode==ID_SCE && options & LIB_LOAD_LOAD_ACTIONS) { load_datablocks(main_newlib, bpy_openlib, path, ID_AC); } BLO_blendhandle_close(bpy_openlib); BKE_reports_clear(&reports); /* done linking */ /* needed for lookups*/ GetMainDynamic().push_back(main_newlib); BLI_strncpy(main_newlib->name, path, sizeof(main_newlib->name)); status = new KX_LibLoadStatus(this, m_ketsjiEngine, scene_merge, path); if (idcode==ID_ME) { /* Convert all new meshes into BGE meshes */ ID* mesh; for (mesh= (ID *)main_newlib->mesh.first; mesh; mesh= (ID *)mesh->next ) { if (options & LIB_LOAD_VERBOSE) printf("MeshName: %s\n", mesh->name+2); RAS_MeshObject *meshobj = BL_ConvertMesh((Mesh *)mesh, NULL, scene_merge, this, false); // For now only use the libloading option for scenes, which need to handle materials/shaders scene_merge->GetLogicManager()->RegisterMeshName(meshobj->GetName(),meshobj); } } else if (idcode==ID_AC) { /* Convert all actions */ ID *action; for (action= (ID *)main_newlib->action.first; action; action= (ID *)action->next) { if (options & LIB_LOAD_VERBOSE) printf("ActionName: %s\n", action->name+2); scene_merge->GetLogicManager()->RegisterActionName(action->name+2, action); } } else if (idcode==ID_SCE) { /* Merge all new linked in scene into the existing one */ ID *scene; // scenes gets deleted by the thread when it's done using it (look in async_convert()) vector *scenes = (options & LIB_LOAD_ASYNC) ? new vector() : NULL; for (scene= (ID *)main_newlib->scene.first; scene; scene= (ID *)scene->next ) { if (options & LIB_LOAD_VERBOSE) printf("SceneName: %s\n", scene->name+2); if (options & LIB_LOAD_ASYNC) { scenes->push_back((Scene*)scene); } else { /* merge into the base scene */ KX_Scene* other= m_ketsjiEngine->CreateScene((Scene *)scene, true); scene_merge->MergeScene(other); // RemoveScene(other); // Don't run this, it frees the entire scene converter data, just delete the scene delete other; } } if (options & LIB_LOAD_ASYNC) { pthread_t id; status->SetData(scenes); pthread_create(&id, NULL, &async_convert, (void*)status); m_threadinfo->threads.push_back(id); } #ifdef WITH_PYTHON /* Handle any text datablocks */ if (options & LIB_LOAD_LOAD_SCRIPTS) addImportMain(main_newlib); #endif /* Now handle all the actions */ if (options & LIB_LOAD_LOAD_ACTIONS) { ID *action; for (action= (ID *)main_newlib->action.first; action; action= (ID *)action->next) { if (options & LIB_LOAD_VERBOSE) printf("ActionName: %s\n", action->name+2); scene_merge->GetLogicManager()->RegisterActionName(action->name+2, action); } } } if (!(options & LIB_LOAD_ASYNC)) status->Finish(); // TIMEIT_END(bge_link_blend_file); m_status_map[main_newlib->name] = status; return status; } /* Note m_map_*** are all ok and don't need to be freed * most are temp and NewRemoveObject frees m_map_gameobject_to_blender */ bool KX_BlenderSceneConverter::FreeBlendFile(struct Main *maggie) { int maggie_index= -1; int i=0; if (maggie==NULL) return false; /* tag all false except the one we remove */ for (vector::iterator it=m_DynamicMaggie.begin(); !(it==m_DynamicMaggie.end()); it++) { Main *main= *it; if (main != maggie) { BKE_main_id_tag_all(main, false); } else { maggie_index= i; } i++; } /* should never happen but just to be safe */ if (maggie_index == -1) return false; m_DynamicMaggie.erase(m_DynamicMaggie.begin() + maggie_index); BKE_main_id_tag_all(maggie, true); /* free all tagged objects */ KX_SceneList* scenes = m_ketsjiEngine->CurrentScenes(); int numScenes = scenes->size(); for (int scene_idx=0;scene_idxat(scene_idx); if (IS_TAGGED(scene->GetBlenderScene())) { m_ketsjiEngine->RemoveScene(scene->GetName()); m_mat_cache.erase(scene); m_polymat_cache.erase(scene); scene_idx--; numScenes--; } else { /* in case the mesh might be refered to later */ { CTR_Map &mapStringToMeshes = scene->GetLogicManager()->GetMeshMap(); for (int i=0; iGetMesh())) { STR_HashedString mn = meshobj->GetName(); mapStringToMeshes.remove(mn); m_map_mesh_to_gamemesh.remove(CHashedPtr(meshobj->GetMesh())); i--; } } } /* Now unregister actions */ { CTR_Map &mapStringToActions = scene->GetLogicManager()->GetActionMap(); for (int i=0; iname+2; mapStringToActions.remove(an); i--; } } } //scene->FreeTagged(); /* removed tagged objects and meshes*/ CListValue *obj_lists[] = {scene->GetObjectList(), scene->GetInactiveList(), NULL}; for (int ob_ls_idx=0; obj_lists[ob_ls_idx]; ob_ls_idx++) { CListValue *obs= obj_lists[ob_ls_idx]; RAS_MeshObject* mesh; for (int ob_idx = 0; ob_idx < obs->GetCount(); ob_idx++) { KX_GameObject* gameobj = (KX_GameObject*)obs->GetValue(ob_idx); if (IS_TAGGED(gameobj->GetBlenderObject())) { int size_before = obs->GetCount(); /* Eventually calls RemoveNodeDestructObject * frees m_map_gameobject_to_blender from UnregisterGameObject */ scene->RemoveObject(gameobj); if (size_before != obs->GetCount()) ob_idx--; else { printf("ERROR COULD NOT REMOVE \"%s\"\n", gameobj->GetName().ReadPtr()); } } else { /* free the mesh, we could be referecing a linked one! */ int mesh_index= gameobj->GetMeshCount(); while (mesh_index--) { mesh= gameobj->GetMesh(mesh_index); if (IS_TAGGED(mesh->GetMesh())) { gameobj->RemoveMeshes(); /* XXX - slack, should only remove meshes that are library items but mostly objects only have 1 mesh */ break; } } /* make sure action actuators are not referencing tagged actions */ for (unsigned int act_idx=0; act_idxGetActuators().size(); act_idx++) { if (gameobj->GetActuators()[act_idx]->IsType(SCA_IActuator::KX_ACT_ACTION)) { BL_ActionActuator *act = (BL_ActionActuator*)gameobj->GetActuators()[act_idx]; if (IS_TAGGED(act->GetAction())) act->SetAction(NULL); } } } } } } } int size; // delete the entities of this scene /* TODO - */ #if 0 vector >::iterator worldit; size = m_worldinfos.size(); for (i=0, worldit=m_worldinfos.begin(); i KX_WorldInfoSet; KX_WorldInfoSet worldset; for (int scene_idx=0;scene_idxat(scene_idx); if (scene->GetWorldInfo()) worldset.insert( scene->GetWorldInfo() ); } vector >::iterator worldit; size = m_worldinfos.size(); for (i=0, worldit=m_worldinfos.begin(); i >::iterator polymit; size = m_polymaterials.size(); for (i=0, polymit=m_polymaterials.begin(); i(mat); bmat= bl_mat->GetBlenderMaterial(); if (IS_TAGGED(bmat)) { /* only remove from bucket */ ((*polymit).first)->GetBucketManager()->RemoveMaterial(mat); } i++; polymit++; } for (i=0, polymit=m_polymaterials.begin(); i(mat); bmat= bl_mat->GetBlenderMaterial(); if (bmat) { //printf("FOUND MAT '%s' !!! ", ((ID*)bmat)->name+2); } else { //printf("LOST MAT !!!"); } if (IS_TAGGED(bmat)) { delete (*polymit).second; *polymit = m_polymaterials.back(); m_polymaterials.pop_back(); size--; //printf("tagged !\n"); } else { i++; polymit++; //printf("(un)tagged !\n"); } } vector >::iterator matit; size = m_materials.size(); for (i=0, matit=m_materials.begin(); imaterial)) { delete (*matit).second; *matit = m_materials.back(); m_materials.pop_back(); size--; } else { i++; matit++; } } vector >::iterator meshit; size = m_meshobjects.size(); for (i=0, meshit=m_meshobjects.begin(); iGetMesh())) { delete (*meshit).second; *meshit = m_meshobjects.back(); m_meshobjects.pop_back(); size--; } else { i++; meshit++; } } #ifdef WITH_PYTHON /* make sure this maggie is removed from the import list if it's there * (this operation is safe if it isn't in the list) */ removeImportMain(maggie); #endif delete m_status_map[maggie->name]; m_status_map.erase(maggie->name); BKE_main_free(maggie); return true; } bool KX_BlenderSceneConverter::FreeBlendFile(const char *path) { return FreeBlendFile(GetMainDynamicPath(path)); } bool KX_BlenderSceneConverter::MergeScene(KX_Scene *to, KX_Scene *from) { { vector >::iterator itp = m_worldinfos.begin(); while (itp != m_worldinfos.end()) { if ((*itp).first==from) (*itp).first= to; itp++; } } { vector >::iterator itp = m_polymaterials.begin(); while (itp != m_polymaterials.end()) { if ((*itp).first==from) { (*itp).first= to; /* also switch internal data */ RAS_IPolyMaterial*mat= (*itp).second; mat->Replace_IScene(to); } itp++; } } { vector >::iterator itp = m_meshobjects.begin(); while (itp != m_meshobjects.end()) { if ((*itp).first==from) (*itp).first= to; itp++; } } { vector >::iterator itp = m_materials.begin(); while (itp != m_materials.end()) { if ((*itp).first==from) (*itp).first= to; itp++; } } return true; } /* This function merges a mesh from the current scene into another main * it does not convert */ RAS_MeshObject *KX_BlenderSceneConverter::ConvertMeshSpecial(KX_Scene* kx_scene, Main *maggie, const char *name) { /* Find a mesh in the current main */ ID *me= static_cast(BLI_findstring(&m_maggie->mesh, name, offsetof(ID, name) + 2)); if (me==NULL) { printf("Could not be found \"%s\"\n", name); return NULL; } /* Watch this!, if its used in the original scene can cause big troubles */ if (me->us > 0) { printf("Mesh has a user \"%s\"\n", name); me = (ID*)BKE_mesh_copy((Mesh*)me); me->us--; } BLI_remlink(&m_maggie->mesh, me); /* even if we made the copy it needs to be removed */ BLI_addtail(&maggie->mesh, me); /* Must copy the materials this uses else we cant free them */ { Mesh *mesh= (Mesh *)me; /* ensure all materials are tagged */ for (int i=0; itotcol; i++) if (mesh->mat[i]) mesh->mat[i]->id.flag &= ~LIB_DOIT; for (int i=0; itotcol; i++) { Material *mat_old= mesh->mat[i]; /* if its tagged its a replaced material */ if (mat_old && (mat_old->id.flag & LIB_DOIT)==0) { Material *mat_old= mesh->mat[i]; Material *mat_new= BKE_material_copy( mat_old ); mat_new->id.flag |= LIB_DOIT; mat_old->id.us--; BLI_remlink(&m_maggie->mat, mat_new); BLI_addtail(&maggie->mat, mat_new); mesh->mat[i] = mat_new; /* the same material may be used twice */ for (int j = i + 1; j < mesh->totcol; j++) { if (mesh->mat[j] == mat_old) { mesh->mat[j] = mat_new; mat_new->id.us++; mat_old->id.us--; } } } } } RAS_MeshObject *meshobj = BL_ConvertMesh((Mesh *)me, NULL, kx_scene, this, false); kx_scene->GetLogicManager()->RegisterMeshName(meshobj->GetName(),meshobj); m_map_mesh_to_gamemesh.clear(); /* This is at runtime so no need to keep this, BL_ConvertMesh adds */ return meshobj; }