/** * $Id$ * * ***** BEGIN GPL/BL DUAL 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. The Blender * Foundation also sells licenses for use in proprietary software under * the Blender License. See http://www.blender.org/BL/ for information * about this. * * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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/BL DUAL LICENSE BLOCK ***** */ #ifdef WIN32 #pragma warning (disable : 4786) #endif // defines USE_ODE to choose physics engine #include "KX_ConvertPhysicsObject.h" #include "KX_GameObject.h" #include "RAS_MeshObject.h" #include "KX_Scene.h" #include "SYS_System.h" #include "PHY_Pro.h" //todo cleanup #include "KX_ClientObjectInfo.h" #include "GEN_Map.h" #include "GEN_HashedPtr.h" #include "KX_PhysicsEngineEnums.h" #include "PHY_Pro.h" #include "KX_MotionState.h" // bridge between motionstate and scenegraph node #ifdef HAVE_CONFIG_H #include #endif #ifdef USE_ODE #include "KX_OdePhysicsController.h" #include "OdePhysicsEnvironment.h" #endif //USE_ODE // USE_SUMO_SOLID is defined in headerfile KX_ConvertPhysicsObject.h #ifdef USE_SUMO_SOLID #include "SumoPhysicsEnvironment.h" #include "KX_SumoPhysicsController.h" // sumo physics specific #include "SM_Object.h" #include "SM_FhObject.h" #include "SM_Scene.h" #include "SM_ClientObjectInfo.h" #include "KX_SumoPhysicsController.h" static GEN_Map map_gamemesh_to_sumoshape; static GEN_Map map_gamemesh_to_vertex_base_handle; // forward declarations static void BL_RegisterSumoObject(KX_GameObject* gameobj,class SM_Scene* sumoScene,class SM_Object* sumoObj,const STR_String& matname,bool isDynamic,bool isActor); static DT_ShapeHandle CreateShapeFromMesh(RAS_MeshObject* meshobj, bool polytope); void KX_ConvertSumoObject( KX_GameObject* gameobj, RAS_MeshObject* meshobj, KX_Scene* kxscene, PHY_ShapeProps* kxshapeprops, PHY_MaterialProps* kxmaterial, struct KX_ObjectProperties* objprop) { SM_ShapeProps* smprop = new SM_ShapeProps; smprop->m_ang_drag = kxshapeprops->m_ang_drag; smprop->m_do_anisotropic = kxshapeprops->m_do_anisotropic; smprop->m_do_fh = kxshapeprops->m_do_fh; smprop->m_do_rot_fh = kxshapeprops->m_do_rot_fh ; smprop->m_friction_scaling[0] = kxshapeprops->m_friction_scaling[0]; smprop->m_friction_scaling[1] = kxshapeprops->m_friction_scaling[1]; smprop->m_friction_scaling[2] = kxshapeprops->m_friction_scaling[2]; smprop->m_inertia = MT_Vector3(1., 1., 1.) * kxshapeprops->m_inertia; smprop->m_lin_drag = kxshapeprops->m_lin_drag; smprop->m_mass = kxshapeprops->m_mass; smprop->m_radius = objprop->m_radius; SM_MaterialProps* smmaterial = new SM_MaterialProps; smmaterial->m_fh_damping = kxmaterial->m_fh_damping; smmaterial->m_fh_distance = kxmaterial->m_fh_distance; smmaterial->m_fh_normal = kxmaterial->m_fh_normal; smmaterial->m_fh_spring = kxmaterial->m_fh_spring; smmaterial->m_friction = kxmaterial->m_friction; smmaterial->m_restitution = kxmaterial->m_restitution; SumoPhysicsEnvironment* sumoEnv = (SumoPhysicsEnvironment*)kxscene->GetPhysicsEnvironment(); SM_Scene* sceneptr = sumoEnv->GetSumoScene(); SM_Object* sumoObj=NULL; if (objprop->m_dyna && objprop->m_isactor) { DT_ShapeHandle shape = NULL; bool polytope = false; switch (objprop->m_boundclass) { case KX_BOUNDBOX: shape = DT_NewBox(objprop->m_boundobject.box.m_extends[0], objprop->m_boundobject.box.m_extends[1], objprop->m_boundobject.box.m_extends[2]); smprop->m_inertia.scale(objprop->m_boundobject.box.m_extends[0]*objprop->m_boundobject.box.m_extends[0], objprop->m_boundobject.box.m_extends[1]*objprop->m_boundobject.box.m_extends[1], objprop->m_boundobject.box.m_extends[2]*objprop->m_boundobject.box.m_extends[2]); smprop->m_inertia *= smprop->m_mass/MT_Vector3(objprop->m_boundobject.box.m_extends).length(); break; case KX_BOUNDCYLINDER: shape = DT_NewCylinder(smprop->m_radius, objprop->m_boundobject.c.m_height); smprop->m_inertia.scale(smprop->m_mass*smprop->m_radius*smprop->m_radius, smprop->m_mass*smprop->m_radius*smprop->m_radius, smprop->m_mass*objprop->m_boundobject.c.m_height*objprop->m_boundobject.c.m_height); break; case KX_BOUNDCONE: shape = DT_NewCone(objprop->m_radius, objprop->m_boundobject.c.m_height); smprop->m_inertia.scale(smprop->m_mass*smprop->m_radius*smprop->m_radius, smprop->m_mass*smprop->m_radius*smprop->m_radius, smprop->m_mass*objprop->m_boundobject.c.m_height*objprop->m_boundobject.c.m_height); break; /* Dynamic mesh objects. WARNING! slow. */ case KX_BOUNDPOLYTOPE: polytope = true; // fall through case KX_BOUNDMESH: if (meshobj && meshobj->NumPolygons() > 0) { if ((shape = CreateShapeFromMesh(meshobj, polytope))) { // TODO: calculate proper inertia smprop->m_inertia *= smprop->m_mass*smprop->m_radius*smprop->m_radius; break; } } /* If CreateShapeFromMesh fails, fall through and use sphere */ default: case KX_BOUNDSPHERE: shape = DT_NewSphere(objprop->m_radius); smprop->m_inertia *= smprop->m_mass*smprop->m_radius*smprop->m_radius; break; } sumoObj = new SM_Object(shape, !objprop->m_ghost?smmaterial:NULL,smprop,NULL); sumoObj->setRigidBody(objprop->m_angular_rigidbody?true:false); BL_RegisterSumoObject(gameobj,sceneptr,sumoObj,"",true, true); } else { // non physics object if (meshobj) { int numpolys = meshobj->NumPolygons(); { DT_ShapeHandle complexshape=0; bool polytope = false; switch (objprop->m_boundclass) { case KX_BOUNDBOX: complexshape = DT_NewBox(objprop->m_boundobject.box.m_extends[0], objprop->m_boundobject.box.m_extends[1], objprop->m_boundobject.box.m_extends[2]); break; case KX_BOUNDSPHERE: complexshape = DT_NewSphere(objprop->m_boundobject.c.m_radius); break; case KX_BOUNDCYLINDER: complexshape = DT_NewCylinder(objprop->m_boundobject.c.m_radius, objprop->m_boundobject.c.m_height); break; case KX_BOUNDCONE: complexshape = DT_NewCone(objprop->m_boundobject.c.m_radius, objprop->m_boundobject.c.m_height); break; case KX_BOUNDPOLYTOPE: polytope = true; // fall through default: case KX_BOUNDMESH: if (numpolys>0) { complexshape = CreateShapeFromMesh(meshobj, polytope); //std::cout << "Convert Physics Mesh: " << meshobj->GetName() << std::endl; /* if (!complexshape) { // Something has to be done here - if the object has no polygons, it will not be able to have // sensors attached to it. DT_Vector3 pt = {0., 0., 0.}; complexshape = DT_NewSphere(1.0); objprop->m_ghost = evilObject = true; } */ } break; } if (complexshape) { SM_Object *dynamicParent = NULL; if (objprop->m_dynamic_parent) { // problem is how to find the dynamic parent // in the scenegraph KX_SumoPhysicsController* sumoctrl = (KX_SumoPhysicsController*) objprop->m_dynamic_parent->GetPhysicsController(); if (sumoctrl) { dynamicParent = sumoctrl->GetSumoObject(); } assert(dynamicParent); } sumoObj = new SM_Object(complexshape,!objprop->m_ghost?smmaterial:NULL,NULL, dynamicParent); const STR_String& matname=meshobj->GetMaterialName(0); BL_RegisterSumoObject(gameobj,sceneptr, sumoObj, matname, objprop->m_dyna, objprop->m_isactor); } } } } // physics object get updated here ! // lazy evaluation because we might not support scaling !gameobj->UpdateTransform(); if (objprop->m_in_active_layer && sumoObj) { sceneptr->add(*sumoObj); } } static void BL_RegisterSumoObject( KX_GameObject* gameobj, class SM_Scene* sumoScene, class SM_Object* sumoObj, const STR_String& matname, bool isDynamic, bool isActor) { PHY_IMotionState* motionstate = new KX_MotionState(gameobj->GetSGNode()); // need easy access, not via 'node' etc. KX_SumoPhysicsController* physicscontroller = new KX_SumoPhysicsController(sumoScene,sumoObj,motionstate,isDynamic); gameobj->SetPhysicsController(physicscontroller); physicscontroller->setClientInfo(gameobj); if (!gameobj->getClientInfo()) std::cout << "BL_RegisterSumoObject: WARNING: Object " << gameobj->GetName() << " has no client info" << std::endl; sumoObj->setClientObject(gameobj->getClientInfo()); gameobj->GetSGNode()->AddSGController(physicscontroller); gameobj->getClientInfo()->m_type = (isActor ? KX_ClientObjectInfo::ACTOR : KX_ClientObjectInfo::STATIC); // store materialname in auxinfo, needed for touchsensors gameobj->getClientInfo()->m_auxilary_info = (matname.Length() ? (void*)(matname.ReadPtr()+2) : NULL); physicscontroller->SetObject(gameobj->GetSGNode()); } static DT_ShapeHandle InstancePhysicsComplex(RAS_MeshObject* meshobj, int vtxarray, RAS_IPolyMaterial *mat) { // instance a mesh from a single vertex array & material const RAS_TexVert *vertex_array = &((*meshobj->GetVertexCache(mat)[vtxarray])[0]); //const KX_IndexArray &index_array = *meshobj->GetIndexCache(mat)[vtxarray]; DT_VertexBaseHandle vertex_base = DT_NewVertexBase(vertex_array[0].getLocalXYZ(), sizeof(RAS_TexVert)); DT_ShapeHandle shape = DT_NewComplexShape(vertex_base); std::vector indices; for (int p = 0; p < meshobj->NumPolygons(); p++) { RAS_Polygon* poly = meshobj->GetPolygon(p); // only add polygons that have the collisionflag set if (poly->IsCollider()) { DT_VertexIndices(3, poly->GetVertexIndexBase().m_indexarray); // tesselate if (poly->VertexCount() == 4) { DT_Begin(); DT_VertexIndex(poly->GetVertexIndexBase().m_indexarray[0]); DT_VertexIndex(poly->GetVertexIndexBase().m_indexarray[2]); DT_VertexIndex(poly->GetVertexIndexBase().m_indexarray[3]); DT_End(); } } } //DT_VertexIndices(indices.size(), &indices[0]); DT_EndComplexShape(); map_gamemesh_to_vertex_base_handle.insert(GEN_HashedPtr(meshobj), vertex_base); return shape; } static DT_ShapeHandle InstancePhysicsPolytope(RAS_MeshObject* meshobj, int vtxarray, RAS_IPolyMaterial *mat) { // instance a mesh from a single vertex array & material const RAS_TexVert *vertex_array = &((*meshobj->GetVertexCache(mat)[vtxarray])[0]); //const KX_IndexArray &index_array = *meshobj->GetIndexCache(mat)[vtxarray]; DT_VertexBaseHandle vertex_base = DT_NewVertexBase(vertex_array[0].getLocalXYZ(), sizeof(RAS_TexVert)); std::vector indices; for (int p = 0; p < meshobj->NumPolygons(); p++) { RAS_Polygon* poly = meshobj->GetPolygon(p); // only add polygons that have the collisionflag set if (poly->IsCollider()) { indices.push_back(poly->GetVertexIndexBase().m_indexarray[0]); indices.push_back(poly->GetVertexIndexBase().m_indexarray[1]); indices.push_back(poly->GetVertexIndexBase().m_indexarray[2]); if (poly->VertexCount() == 4) indices.push_back(poly->GetVertexIndexBase().m_indexarray[3]); } } DT_ShapeHandle shape = DT_NewPolytope(vertex_base); DT_VertexIndices(indices.size(), &indices[0]); DT_EndPolytope(); map_gamemesh_to_vertex_base_handle.insert(GEN_HashedPtr(meshobj), vertex_base); return shape; } // This will have to be a method in a class somewhere... // Update SOLID with a changed physics mesh. // not used... yet. bool ReInstanceShapeFromMesh(RAS_MeshObject* meshobj, int vtxarray, RAS_IPolyMaterial *mat) { DT_VertexBaseHandle *vertex_base = map_gamemesh_to_vertex_base_handle[GEN_HashedPtr(meshobj)]; if (vertex_base) { const RAS_TexVert *vertex_array = &((*meshobj->GetVertexCache(mat)[vtxarray])[0]); DT_ChangeVertexBase(*vertex_base, vertex_array[0].getLocalXYZ()); return true; } return false; } static DT_ShapeHandle CreateShapeFromMesh(RAS_MeshObject* meshobj, bool polytope) { DT_ShapeHandle *shapeptr = map_gamemesh_to_sumoshape[GEN_HashedPtr(meshobj)]; // Mesh has already been converted: reuse if (shapeptr) { return *shapeptr; } // Mesh has no polygons! int numpolys = meshobj->NumPolygons(); if (!numpolys) { return NULL; } // Count the number of collision polygons and check they all come from the same // vertex array int numvalidpolys = 0; int vtxarray = -1; RAS_IPolyMaterial *poly_material = NULL; bool reinstance = true; for (int p=0; pGetPolygon(p); // only add polygons that have the collisionflag set if (poly->IsCollider()) { // check polygon is from the same vertex array if (poly->GetVertexIndexBase().m_vtxarray != vtxarray) { if (vtxarray < 0) vtxarray = poly->GetVertexIndexBase().m_vtxarray; else { reinstance = false; vtxarray = -1; } } // check poly is from the same material if (poly->GetMaterial()->GetPolyMaterial() != poly_material) { if (poly_material) { reinstance = false; poly_material = NULL; } else poly_material = poly->GetMaterial()->GetPolyMaterial(); } // count the number of collision polys numvalidpolys++; // We have one collision poly, and we can't reinstance, so we // might as well break here. if (!reinstance) break; } } // No collision polygons if (numvalidpolys < 1) return NULL; DT_ShapeHandle shape; if (reinstance) { if (polytope) shape = InstancePhysicsPolytope(meshobj, vtxarray, poly_material); else shape = InstancePhysicsComplex(meshobj, vtxarray, poly_material); } else { if (polytope) { std::cout << "CreateShapeFromMesh: " << meshobj->GetName() << " is not suitable for polytope." << std::endl; if (!poly_material) std::cout << " Check mesh materials." << std::endl; if (vtxarray < 0) std::cout << " Check number of vertices." << std::endl; } shape = DT_NewComplexShape(NULL); numvalidpolys = 0; for (int p2=0; p2GetPolygon(p2); // only add polygons that have the collisionflag set if (poly->IsCollider()) { /* We have to tesselate here because SOLID can only raycast triangles */ DT_Begin(); /* V1 */ DT_Vertex(meshobj->GetVertex(poly->GetVertexIndexBase().m_vtxarray, poly->GetVertexIndexBase().m_indexarray[2], poly->GetMaterial()->GetPolyMaterial())->getLocalXYZ()); /* V2 */ DT_Vertex(meshobj->GetVertex(poly->GetVertexIndexBase().m_vtxarray, poly->GetVertexIndexBase().m_indexarray[1], poly->GetMaterial()->GetPolyMaterial())->getLocalXYZ()); /* V3 */ DT_Vertex(meshobj->GetVertex(poly->GetVertexIndexBase().m_vtxarray, poly->GetVertexIndexBase().m_indexarray[0], poly->GetMaterial()->GetPolyMaterial())->getLocalXYZ()); numvalidpolys++; DT_End(); if (poly->VertexCount() == 4) { DT_Begin(); /* V1 */ DT_Vertex(meshobj->GetVertex(poly->GetVertexIndexBase().m_vtxarray, poly->GetVertexIndexBase().m_indexarray[3], poly->GetMaterial()->GetPolyMaterial())->getLocalXYZ()); /* V3 */ DT_Vertex(meshobj->GetVertex(poly->GetVertexIndexBase().m_vtxarray, poly->GetVertexIndexBase().m_indexarray[2], poly->GetMaterial()->GetPolyMaterial())->getLocalXYZ()); /* V4 */ DT_Vertex(meshobj->GetVertex(poly->GetVertexIndexBase().m_vtxarray, poly->GetVertexIndexBase().m_indexarray[0], poly->GetMaterial()->GetPolyMaterial())->getLocalXYZ()); numvalidpolys++; DT_End(); } } } DT_EndComplexShape(); } if (numvalidpolys > 0) { map_gamemesh_to_sumoshape.insert(GEN_HashedPtr(meshobj),shape); return shape; } delete shape; return NULL; } void KX_ClearSumoSharedShapes() { int numshapes = map_gamemesh_to_sumoshape.size(); int i; for (i=0;im_dyna; bool fullRigidBody= ( objprop->m_dyna && objprop->m_angular_rigidbody) != 0; bool phantom = objprop->m_ghost; class PHY_IMotionState* motionstate = new KX_MotionState(gameobj->GetSGNode()); class ODEPhysicsEnvironment* odeEnv = (ODEPhysicsEnvironment*)kxscene->GetPhysicsEnvironment(); dxSpace* space = odeEnv->GetOdeSpace(); dxWorld* world = odeEnv->GetOdeWorld(); if (!objprop->m_implicitsphere && MT_fuzzyZero(objprop->m_boundingbox.m_extends[0]) || MT_fuzzyZero(objprop->m_boundingbox.m_extends[1]) || MT_fuzzyZero(objprop->m_boundingbox.m_extends[2]) ) { } else { KX_OdePhysicsController* physicscontroller = new KX_OdePhysicsController( dyna, fullRigidBody, phantom, motionstate, space, world, shapeprops->m_mass, smmaterial->m_friction, smmaterial->m_restitution, objprop->m_implicitsphere, objprop->m_boundingbox.m_center, objprop->m_boundingbox.m_extends, objprop->m_radius ); gameobj->SetPhysicsController(physicscontroller); physicscontroller->setClientInfo(gameobj); gameobj->GetSGNode()->AddSGController(physicscontroller); bool isActor = objprop->m_isactor; STR_String materialname; if (meshobj) materialname = meshobj->GetMaterialName(0); const char* matname = materialname.ReadPtr(); physicscontroller->SetObject(gameobj->GetSGNode()); } } #endif // USE_ODE