Permalink
Cannot retrieve contributors at this time
2492 lines (2093 sloc)
73.7 KB
| /* | |
| * Copyright (C) 2006-2010 - Frictional Games | |
| * | |
| * This file is part of HPL1 Engine. | |
| * | |
| * HPL1 Engine 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 3 of the License, or | |
| * (at your option) any later version. | |
| * | |
| * HPL1 Engine 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 HPL1 Engine. If not, see <http://www.gnu.org/licenses/>. | |
| */ | |
| #include "impl/MeshLoaderCollada.h" | |
| #include "system/LowLevelSystem.h" | |
| #include "graphics/LowLevelGraphics.h" | |
| #include "graphics/VertexBuffer.h" | |
| #include "system/String.h" | |
| #include "system/System.h" | |
| #include "scene/Scene.h" | |
| #include "scene/PortalContainer.h" | |
| #include "scene/World3D.h" | |
| #include "scene/MeshEntity.h" | |
| #include "scene/Light3DPoint.h" | |
| #include "scene/Light3DSpot.h" | |
| #include "scene/Node3D.h" | |
| #include "scene/ColliderEntity.h" | |
| #include "scene/SoundEntity.h" | |
| #include "graphics/Mesh.h" | |
| #include "graphics/SubMesh.h" | |
| #include "graphics/Material.h" | |
| #include "resources/MaterialManager.h" | |
| #include "resources/MeshManager.h" | |
| #include "resources/Resources.h" | |
| #include "resources/FileSearcher.h" | |
| #include "graphics/Bone.h" | |
| #include "graphics/Skeleton.h" | |
| #include "graphics/Animation.h" | |
| #include "graphics/AnimationTrack.h" | |
| #include "graphics/BillBoard.h" | |
| #include "graphics/Beam.h" | |
| #include "graphics/ParticleSystem3D.h" | |
| #include "physics/Physics.h" | |
| #include "physics/PhysicsWorld.h" | |
| #include "physics/PhysicsBody.h" | |
| #include "physics/PhysicsMaterial.h" | |
| #include "physics/SurfaceData.h" | |
| #include "haptic/Haptic.h" | |
| #include "haptic/HapticShape.h" | |
| #include "haptic/HapticSurface.h" | |
| #include "haptic/LowLevelHaptic.h" | |
| #include "impl/tinyXML/tinyxml.h" | |
| #include "math/Math.h" | |
| namespace hpl { | |
| #define GetAdress(sStr) if(sStr[0]=='#') sStr = cString::Sub(sStr,1); | |
| ////////////////////////////////////////////////////////////////////////// | |
| // CONSTRUCTORS | |
| ////////////////////////////////////////////////////////////////////////// | |
| //----------------------------------------------------------------------- | |
| cMeshLoaderCollada::cMeshLoaderCollada(iLowLevelGraphics *apLowLevelGraphics) | |
| : iMeshLoader(apLowLevelGraphics) | |
| { | |
| mFlags =0; | |
| } | |
| //----------------------------------------------------------------------- | |
| cMeshLoaderCollada::~cMeshLoaderCollada() | |
| { | |
| } | |
| //----------------------------------------------------------------------- | |
| ////////////////////////////////////////////////////////////////////////// | |
| // PUBLIC METHODS | |
| ////////////////////////////////////////////////////////////////////////// | |
| //----------------------------------------------------------------------- | |
| cWorld3D* cMeshLoaderCollada::LoadWorld(const tString& asFile, cScene* apScene, tWorldLoadFlag aFlags) | |
| { | |
| //Images | |
| tColladaImageVec vColladaImages; | |
| //Textures | |
| tColladaTextureVec vColladaTextures; | |
| //Materials | |
| tColladaMaterialVec vColladaMaterials; | |
| //Geometries | |
| tColladaGeometryVec vColladaGeometries; | |
| //Lights | |
| tColladaLightVec vColladaLights; | |
| //Scene | |
| cColladaScene ColladaScene; | |
| mFlags = aFlags; | |
| unsigned long lStartTime = GetApplicationTime(); | |
| //Fill the structures with collada file data | |
| bool bRet = FillStructures(asFile, &vColladaImages, &vColladaTextures, | |
| &vColladaMaterials,&vColladaLights, | |
| &vColladaGeometries, | |
| NULL,NULL, | |
| &ColladaScene,true); | |
| unsigned long lTime = GetApplicationTime() - lStartTime; | |
| Log("Loading collada for '%s' took: %d ms\n",asFile.c_str(),lTime); | |
| if(bRet==false) return NULL; | |
| cWorld3D *pWorld = apScene->CreateWorld3D(cString::SetFileExt(cString::GetFileName(asFile),"")); | |
| pWorld->SetFileName(cString::GetFileName(asFile)); | |
| cPortalContainer *pPortalContainer = pWorld->GetPortalContainer(); | |
| ///////////////////////////////////// | |
| //Create the physics world | |
| iPhysicsWorld *pPhysiscsWorld = pWorld->GetPhysics()->CreateWorld(true); | |
| pWorld->SetPhysicsWorld(pPhysiscsWorld,true); | |
| ////////////////////////////////////////////////////////////// | |
| //Find the rooms and and add them as sectors in the world. | |
| //Log("FIND ROOMS:\n"); | |
| tColladaNodeListIt it = ColladaScene.mlstNodes.begin(); | |
| for(; it != ColladaScene.mlstNodes.end(); it++) | |
| { | |
| cColladaNode* pNode = *it; | |
| //Only need to check if it is a special type | |
| if(pNode->msName[0] == '_') | |
| { | |
| //Get 5 first letters and check if these match "_room" | |
| tString sType = cString::Sub(pNode->msName,0,5); | |
| //Log("Type: %s\n",sType.c_str()); | |
| if(cString::ToLowerCase(sType) == "_room") | |
| { | |
| //Get Room number | |
| tString sNum = cString::Sub(pNode->msName,5); | |
| //int lNum = cString::ToInt(sNum.c_str(),-1); | |
| tString sRoomId = sNum; | |
| //Log("Adding room num %s\n",sRoomId.c_str()); | |
| //Add sector | |
| pPortalContainer->AddSector(sRoomId); | |
| //Add all childs of this node to the room. | |
| tColladaNodeListIt ChildIt = pNode->mlstChildren.begin(); | |
| for(; ChildIt != pNode->mlstChildren.end();ChildIt++) | |
| { | |
| AddSectorChildren(*ChildIt,sRoomId, pWorld,vColladaGeometries,vColladaLights, | |
| vColladaMaterials,vColladaTextures,vColladaImages); | |
| } | |
| } | |
| } | |
| } | |
| ////////////////////////////////////////////////////////////// | |
| //Iterate the nodes and add remaining objects to the scene. | |
| //Log("ADD OBJECTS:\n"); | |
| it = ColladaScene.mRoot.mlstChildren.begin(); | |
| for(; it != ColladaScene.mRoot.mlstChildren.end(); it++) | |
| { | |
| AddSceneObjects(*it, pWorld, vColladaGeometries,vColladaLights, | |
| vColladaMaterials,vColladaTextures,vColladaImages,&ColladaScene); | |
| } | |
| pWorld->SetUpData(); | |
| return pWorld; | |
| } | |
| //----------------------------------------------------------------------- | |
| static cColladaNode* GetNodeFromController(const tString& asGeomId, | |
| tColladaControllerVec &avColladaControllers, | |
| cColladaScene &aColladaScene) | |
| { | |
| tString sControlId=""; | |
| bool bGuess=false; | |
| for(int ctrl=0; ctrl < (int)avColladaControllers.size(); ctrl++) | |
| { | |
| cColladaController &Control = avColladaControllers[ctrl]; | |
| if(Control.msTarget == asGeomId){ | |
| sControlId = Control.msId; | |
| bGuess=false; | |
| } | |
| //Guessing, if no controller found try the one with source "". | |
| else if(sControlId=="" && Control.msTarget=="") { | |
| sControlId = Control.msId; | |
| bGuess = true; | |
| } | |
| } | |
| if(bGuess) Warning("No controller for for geometry %s, guessing on %s target = ''\n", | |
| asGeomId.c_str(), sControlId.c_str()); | |
| if(sControlId==""){ | |
| Warning("No controller refered to the geometry!\n"); | |
| return NULL; | |
| } | |
| cColladaNode* pNode = aColladaScene.GetNodeFromSource(sControlId); | |
| if(pNode==NULL){ | |
| Warning("No node for controller '%s'\n",sControlId.c_str()); | |
| } | |
| return pNode; | |
| } | |
| //------------------------------------------------- | |
| static void FixLocalTransform(cMatrixf *apMatrix, cColladaNode *apNode, | |
| tColladaAnimationVec &avColladaAnimations, cSkeleton *apSkeleton, bool abHasSeveralBodies) | |
| { | |
| cColladaNode *pParentNode = apNode->pParent; | |
| if(pParentNode && apSkeleton==NULL) | |
| { | |
| if(avColladaAnimations.empty() == false || abHasSeveralBodies) | |
| { | |
| *apMatrix = cMath::MatrixMul(cMath::MatrixScale(pParentNode->mvScale),*apMatrix); | |
| } | |
| else | |
| { | |
| *apMatrix = cMath::MatrixMul(pParentNode->m_mtxTransform,*apMatrix); | |
| } | |
| } | |
| } | |
| //------------------------------------------------- | |
| static void FixLocalPosition(cVector3f *apPos, cColladaNode *apNode, | |
| tColladaAnimationVec &avColladaAnimations, cSkeleton *apSkeleton, bool abHasSeveralBodies) | |
| { | |
| cColladaNode *pParentNode = apNode->pParent; | |
| if(pParentNode && apSkeleton==NULL) | |
| { | |
| if(avColladaAnimations.empty() == false || abHasSeveralBodies) | |
| { | |
| *apPos = cMath::MatrixMul(cMath::MatrixScale(pParentNode->mvScale),*apPos); | |
| } | |
| else | |
| { | |
| *apPos = cMath::MatrixMul(pParentNode->m_mtxTransform,*apPos); | |
| } | |
| } | |
| } | |
| //------------------------------------------------- | |
| cMesh* cMeshLoaderCollada::LoadMesh(const tString& asFile,tMeshLoadFlag aFlags) | |
| { | |
| ///////////////////////////////////////////////// | |
| // SETUP TEMP DATA STRUCTURES | |
| //Images | |
| tColladaImageVec vColladaImages; | |
| //Textures | |
| tColladaTextureVec vColladaTextures; | |
| //Materials | |
| tColladaMaterialVec vColladaMaterials; | |
| //Lights | |
| tColladaLightVec vColladaLights; | |
| //Geometries | |
| tColladaGeometryVec vColladaGeometries; | |
| //Controllers | |
| tColladaControllerVec vColladaControllers; | |
| //Animations | |
| tColladaAnimationVec vColladaAnimations; | |
| //Scene | |
| cColladaScene ColladaScene; | |
| mFlags = aFlags; | |
| //Fill the structures with collada file data | |
| tColladaGeometryVec *pGeomVec = (aFlags & eMeshLoadFlag_NoGeometry) ? NULL : &vColladaGeometries; | |
| bool bRet = FillStructures(asFile, &vColladaImages, &vColladaTextures, | |
| &vColladaMaterials,&vColladaLights, | |
| pGeomVec, &vColladaControllers, | |
| &vColladaAnimations, | |
| &ColladaScene,true); | |
| if(bRet==false) return NULL; | |
| //////////////////////// | |
| //Create Skeleton | |
| cSkeleton * pSkeleton = NULL; | |
| if(vColladaControllers.empty() == false) | |
| { | |
| pSkeleton = hplNew( cSkeleton, () ); | |
| tColladaNodeListIt it = ColladaScene.mRoot.mlstChildren.begin(); | |
| for(; it != ColladaScene.mRoot.mlstChildren.end(); it++) | |
| { | |
| cColladaNode *pNode = *it; | |
| CreateSkeletonBone(pNode, pSkeleton->GetRootBone()); | |
| } | |
| //////////////////////////////////// | |
| //Set the bind position of the bones | |
| //(This will set the local matrix as the global bind) | |
| for(size_t i=0; i< vColladaControllers.size();i++) | |
| { | |
| cColladaController &Ctrl = vColladaControllers[i]; | |
| for(size_t j=0; j<Ctrl.mvJoints.size(); j++) | |
| { | |
| cBone *pBone = pSkeleton->GetBoneByName(Ctrl.mvJoints[j]); | |
| if(pBone) | |
| { | |
| pBone->SetTransform(cMath::MatrixInverse(Ctrl.mvMatrices[j])); | |
| pBone->SetValue(1); | |
| } | |
| else | |
| { | |
| Log("Bone '%s' does not exist\n",Ctrl.mvJoints[j].c_str()); | |
| } | |
| } | |
| } | |
| //////////////////////////////////////////////// | |
| //Do another pass and calculate the local matrix | |
| cBoneIterator BoneIt = pSkeleton->GetRootBone()->GetChildIterator(); | |
| while(BoneIt.HasNext()) | |
| { | |
| cMatrixf mtxRoot = cMatrixf::Identity; | |
| CalcLocalMatrixRec(BoneIt.Next(), mtxRoot,0); | |
| } | |
| } | |
| //////////////////////////////////// | |
| //Check for joints or several bodies. | |
| bool bHasSeveralBodies = false; | |
| tString sColliderGroup=""; | |
| bool bFoundCollider=false; | |
| tColladaNodeListIt nodeIt = ColladaScene.mlstNodes.begin(); | |
| for(; nodeIt != ColladaScene.mlstNodes.end(); ++nodeIt) | |
| { | |
| cColladaNode *pNode = *nodeIt; | |
| //Check if it is a joint | |
| if(cString::ToLowerCase(cString::Sub(pNode->msName,0,6)) == "_joint") | |
| { | |
| bHasSeveralBodies = true; | |
| break; | |
| } | |
| //Check if it is a collider. In that case check if this is a new group name. | |
| if(cString::ToLowerCase(cString::Sub(pNode->msName,0,9)) == "_collider") | |
| { | |
| tString sGroup =""; | |
| if(pNode->pParent) sGroup = pNode->pParent->msName; | |
| if(bFoundCollider==false) | |
| { | |
| bFoundCollider = true; | |
| sColliderGroup = sGroup; | |
| } | |
| else if(sGroup != sColliderGroup) | |
| { | |
| bHasSeveralBodies = true; | |
| break; | |
| } | |
| } | |
| } | |
| //////////////////////////////////// | |
| //Create Mesh | |
| tString sMeshName = cString::GetFileName(asFile); | |
| cMesh *pMesh = hplNew( cMesh, (sMeshName,mpMaterialManager,mpAnimationManager) ); | |
| //Set the skeleton to the mesh | |
| if(pSkeleton) pMesh->SetSkeleton(pSkeleton); | |
| //Create Sub meshes | |
| for(int i=0;i<(int)vColladaGeometries.size(); i++) | |
| { | |
| cColladaGeometry &Geom = vColladaGeometries[i]; | |
| cColladaNode *pGeomNode = ColladaScene.GetNodeFromSource(Geom.msId); | |
| if(pGeomNode==NULL) | |
| { | |
| pGeomNode = GetNodeFromController(Geom.msId,vColladaControllers,ColladaScene); | |
| if(pGeomNode==NULL){ | |
| Error("No node with geometry id '%s'\n",Geom.msId.c_str()); | |
| continue; | |
| } | |
| } | |
| tString sNodeName = pGeomNode->msName; | |
| ///////////////////////////////////////////////////// | |
| //If the name starts with '_' it is a special object. | |
| if(sNodeName[0] == '_') | |
| { | |
| tStringVec vStrings; | |
| tString sSepp = "_"; | |
| cString::GetStringVec(sNodeName,vStrings,&sSepp); | |
| ///////////////////////////////////// | |
| //JOINT | |
| if(cString::ToLowerCase(vStrings[0]) == "joint" && vStrings.size()>1) | |
| { | |
| tFloatVec vVertexVec; | |
| tVertexVec &vArray = Geom.mvVertexVec; | |
| vVertexVec.resize(vArray.size() *3); | |
| for(size_t vtx=0; vtx < vArray.size(); ++vtx) | |
| { | |
| vVertexVec[vtx*3 + 0] = vArray[vtx].pos.x; | |
| vVertexVec[vtx*3 + 1] = vArray[vtx].pos.y; | |
| vVertexVec[vtx*3 + 2] = vArray[vtx].pos.z; | |
| } | |
| cBoundingVolume TempBV; | |
| TempBV.AddArrayPoints(&vVertexVec[0],(int)vArray.size()); | |
| TempBV.CreateFromPoints(3); | |
| cColladaNode *pNode = ColladaScene.GetNodeFromSource(Geom.msId); | |
| if(pNode==NULL){Warning("No node for geometry '%s' found when creating joint!\n",Geom.msId.c_str()); continue;} | |
| tString sJointType = cString::ToLowerCase(vStrings[1]); | |
| ePhysicsJointType JointType; | |
| if(sJointType=="hinge") JointType = ePhysicsJointType_Hinge; | |
| else if(sJointType=="ball") JointType = ePhysicsJointType_Ball; | |
| else if(sJointType=="slider") JointType = ePhysicsJointType_Slider; | |
| else if(sJointType=="screw") JointType = ePhysicsJointType_Screw; | |
| cMeshJoint *pJoint = pMesh->CreatePhysicsJoint(JointType); | |
| CreateMeshJoint(pJoint, JointType, TempBV, vStrings, pNode,ColladaScene,vColladaGeometries); | |
| } | |
| ///////////////////////////////////// | |
| //COLLIDER | |
| else if(cString::ToLowerCase(vStrings[0]) == "collider" && vStrings.size()>1) | |
| { | |
| tFloatVec vVertexVec; | |
| tVertexVec &vArray = Geom.mvVertexVec; | |
| vVertexVec.resize(vArray.size() *3); | |
| for(size_t vtx=0; vtx < vArray.size(); ++vtx) | |
| { | |
| vVertexVec[vtx*3 + 0] = vArray[vtx].pos.x; | |
| vVertexVec[vtx*3 + 1] = vArray[vtx].pos.y; | |
| vVertexVec[vtx*3 + 2] = vArray[vtx].pos.z; | |
| } | |
| cBoundingVolume TempBV; | |
| TempBV.AddArrayPoints(&vVertexVec[0],(int)vArray.size()); | |
| TempBV.CreateFromPoints(3); | |
| tString sShapeType = cString::ToLowerCase(vStrings[1]); | |
| eCollideShapeType ShapeType = eCollideShapeType_Box; | |
| cVector3f vShapeSize = TempBV.GetSize(); | |
| if(sShapeType == "box"){ | |
| ShapeType = eCollideShapeType_Box; | |
| } | |
| else if(sShapeType == "sphere"){ | |
| ShapeType = eCollideShapeType_Sphere; | |
| vShapeSize *= cVector3f(0.5f); | |
| } | |
| else if(sShapeType == "capsule"){ | |
| ShapeType = eCollideShapeType_Capsule; | |
| vShapeSize.x *= 0.5; | |
| } | |
| else if(sShapeType == "cylinder"){ | |
| ShapeType = eCollideShapeType_Cylinder; | |
| vShapeSize.x *= 0.5; | |
| } | |
| cMeshCollider *pCollider = pMesh->CreateCollider(ShapeType); | |
| pCollider->mvSize = vShapeSize; | |
| cColladaNode *pNode = ColladaScene.GetNodeFromSource(Geom.msId); | |
| if(pNode==NULL){Warning("No node for geometry '%s' when creating collider!\n",Geom.msId.c_str()); continue;} | |
| //Set the name of the group it is in. | |
| if(pNode->pParent) | |
| { | |
| pCollider->msGroup = pNode->pParent->msName; | |
| } | |
| else | |
| { | |
| pCollider->msGroup = ""; | |
| } | |
| //Set offset, some primitives are created with the centre at the bottom, | |
| //fix this. | |
| //Check if the centre is not in the middle | |
| TempBV.SetPosition(pNode->m_mtxWorldTransform.GetTranslation()); | |
| if(TempBV.GetWorldCenter() != pNode->m_mtxWorldTransform.GetTranslation()) | |
| { | |
| cVector3f vOffset = TempBV.GetWorldCenter() - | |
| pNode->m_mtxWorldTransform.GetTranslation(); | |
| vOffset = vOffset * pNode->mvScale; | |
| //Log("Centre is not a correct location! Offset: %s\n",vOffset.ToString().c_str()); | |
| //Local postion add | |
| /*cMatrixf mtxTrans = cMath::MatrixTranslate(vOffset); | |
| pCollider->m_mtxOffset = cMath::MatrixMul( pNode->m_mtxWorldTransform, | |
| mtxTrans);*/ | |
| //World postion add | |
| pCollider->m_mtxOffset = pNode->m_mtxWorldTransform; | |
| cVector3f vRotOffset = cMath::MatrixMul(pCollider->m_mtxOffset.GetRotation(), | |
| vOffset); | |
| pCollider->m_mtxOffset.SetTranslation( pCollider->m_mtxOffset.GetTranslation() + | |
| vRotOffset); | |
| } | |
| else | |
| { | |
| pCollider->m_mtxOffset = pNode->m_mtxWorldTransform; | |
| } | |
| //Add scale | |
| pCollider->mvSize = pCollider->mvSize * pNode->mvScale; | |
| //Log("Collider scale: %s\n",pNode->mvScale.ToString().c_str()); | |
| //Log("Collider size: %s\n",pCollider->mvSize.ToString().c_str()); | |
| //This is to orient the cylinder along y axis instead of x. | |
| if(ShapeType == eCollideShapeType_Cylinder || ShapeType == eCollideShapeType_Capsule) | |
| { | |
| pCollider->m_mtxOffset = cMath::MatrixMul(pCollider->m_mtxOffset, | |
| cMath::MatrixRotateZ(cMath::ToRad(90)) | |
| ); | |
| } | |
| //Log("Creating Collider %s, type: %d size: %s\n with matrix: %s\n", Geom.msName.c_str(),(int)ShapeType, | |
| // pCollider->mvSize.ToString().c_str(), | |
| // pCollider->m_mtxOffset.ToString().c_str()); | |
| } | |
| ///// BILLBOARD ///////////////////////////// | |
| else if(cString::ToLowerCase(vStrings[0])=="bb") | |
| { | |
| if(vStrings.size() < 3){ | |
| Error("Too few params in billboard entity '%s'\n",sNodeName.c_str()); | |
| } | |
| else | |
| { | |
| tString sName = vStrings[vStrings.size()-1]; | |
| tString sFile = ""; | |
| for(size_t i2=1; i2<vStrings.size()-1; ++i2) | |
| { | |
| sFile += vStrings[i2]; | |
| if(i2 != vStrings.size()-2) sFile+="_"; | |
| } | |
| cMeshBillboard *pBillboard = pMesh->CreateBillboard(); | |
| pBillboard->msName = sName; | |
| pBillboard->msParent = GetParentName(pGeomNode,&vColladaGeometries); | |
| pBillboard->msFile = cString::SetFileExt(sFile,"bnt"); | |
| pBillboard->mvSize = cVector2f(pGeomNode->mvScale.x,pGeomNode->mvScale.y); | |
| pBillboard->mfOffset = pGeomNode->mvScale.z; | |
| pBillboard->mvPosition = pGeomNode->m_mtxTransform.GetTranslation(); | |
| pBillboard->mvAxis = cMath::Vector3Normalize(cMath::MatrixInverse(pGeomNode->m_mtxWorldTransform).GetUp()); | |
| FixLocalPosition(&pBillboard->mvPosition,pGeomNode,vColladaAnimations,pSkeleton,bHasSeveralBodies); | |
| } | |
| } | |
| ///// BEAM ///////////////////////////// | |
| else if(cString::ToLowerCase(vStrings[0])=="beam") | |
| { | |
| if(vStrings.size() < 3){ | |
| Error("Too few params in billboard entity '%s'\n",sNodeName.c_str()); | |
| } | |
| else | |
| { | |
| tString sName = vStrings[vStrings.size()-1]; | |
| tString sFile = ""; | |
| for(size_t i2=1; i2<vStrings.size()-1; ++i2) | |
| { | |
| sFile += vStrings[i2]; | |
| if(i2 != vStrings.size()-2) sFile+="_"; | |
| } | |
| tString sEndName = "_beamend_"+sName; | |
| cColladaNode *pEndNode = ColladaScene.GetNode(sEndName); | |
| if(pEndNode) | |
| { | |
| cMeshBeam *pBeam = pMesh->CreateBeam(); | |
| pBeam->msName = sName; | |
| pBeam->msFile = sFile; | |
| pBeam->mvStartPosition = pGeomNode->m_mtxTransform.GetTranslation(); | |
| pBeam->mvEndPosition = pEndNode->m_mtxTransform.GetTranslation(); | |
| pBeam->msStartParent = GetParentName(pGeomNode,&vColladaGeometries); | |
| pBeam->msEndParent = GetParentName(pEndNode,&vColladaGeometries); | |
| } | |
| else | |
| { | |
| Error("Couldn't find beam end '%s'!\n",sEndName.c_str()); | |
| } | |
| } | |
| } | |
| ///// PARTICLE SYSTEM ///////////////////////////// | |
| else if(cString::ToLowerCase(vStrings[0])=="ps") | |
| { | |
| if(vStrings.size() < 3){ | |
| Error("Too few params in particle system entity '%s'\n",sNodeName.c_str()); | |
| } | |
| else | |
| { | |
| tString sName = vStrings[vStrings.size()-1]; | |
| tString sType = ""; | |
| for(size_t i2=1; i2<vStrings.size()-1; ++i2) | |
| { | |
| sType += vStrings[i2]; | |
| if(i2 != vStrings.size()-2) sType+="_"; | |
| } | |
| cMeshParticleSystem *pPS = pMesh->CreateParticleSystem(); | |
| pPS->msName = sName; | |
| pPS->msParent = GetParentName(pGeomNode,&vColladaGeometries); | |
| pPS->msType = sType; | |
| pPS->mvSize = pGeomNode->mvScale; | |
| pPS->m_mtxTransform = pGeomNode->m_mtxTransform; | |
| FixLocalTransform(&pPS->m_mtxTransform,pGeomNode,vColladaAnimations,pSkeleton,bHasSeveralBodies); | |
| } | |
| } | |
| ///// REFERENCE ///////////////////////////// | |
| else if(cString::ToLowerCase(vStrings[0])=="ref") | |
| { | |
| if(vStrings.size() < 3){ | |
| Error("Too few params in ref entity '%s'\n",sNodeName.c_str()); | |
| } | |
| else | |
| { | |
| tString sFile = ""; | |
| for(size_t i2=1; i2<vStrings.size()-1; ++i2) | |
| { | |
| sFile += vStrings[i2]; | |
| if(i2 != vStrings.size()-2) sFile+="_"; | |
| } | |
| tString sName = vStrings[vStrings.size()-1]; | |
| cMeshReference *pRef = pMesh->CreateReference(); | |
| pRef->msName = sName; | |
| pRef->msFile = sFile; | |
| pRef->msParent = GetParentName(pGeomNode,&vColladaGeometries); | |
| pRef->m_mtxTransform = pGeomNode->m_mtxTransform; | |
| FixLocalTransform(&pRef->m_mtxTransform,pGeomNode,vColladaAnimations,pSkeleton,bHasSeveralBodies); | |
| } | |
| } | |
| ///// SOUND ENTITY ///////////////////////////// | |
| else if(cString::ToLowerCase(vStrings[0])=="sound") | |
| { | |
| if(vStrings.size() < 3){ | |
| Error("Too few params in sound entity '%s'\n",sNodeName.c_str()); | |
| } | |
| else | |
| { | |
| tString sType = ""; | |
| for(size_t i2=1; i2<vStrings.size()-1; ++i2) | |
| { | |
| sType += vStrings[i2]; | |
| if(i2 != vStrings.size()-2) sType+="_"; | |
| } | |
| tString sName = vStrings[vStrings.size()-1]; | |
| cMeshSoundEntity *pSound = pMesh->CreateSoundEntity(); | |
| pSound->msName = sName; | |
| pSound->msParent = GetParentName(pGeomNode,&vColladaGeometries); | |
| pSound->msType = sType; | |
| pSound->mvPosition = pGeomNode->m_mtxTransform.GetTranslation(); | |
| FixLocalPosition(&pSound->mvPosition,pGeomNode,vColladaAnimations,pSkeleton,bHasSeveralBodies); | |
| } | |
| } | |
| continue; | |
| } | |
| tString sSubMeshName = Geom.msName=="" ? Geom.msId : Geom.msName; | |
| cSubMesh* pSubMesh = pMesh->CreateSubMesh(sSubMeshName); | |
| //Getting group the sub mesh is in | |
| cColladaNode *pNode = ColladaScene.GetNodeFromSource(Geom.msId); | |
| if(pNode==NULL) | |
| { | |
| pNode = GetNodeFromController(Geom.msId,vColladaControllers,ColladaScene); | |
| if(pNode==NULL){ | |
| Warning("No node for geometry '%s' found when trying to create sub mesh, check in controllers.\n",Geom.msId.c_str()); | |
| continue; | |
| } | |
| } | |
| pSubMesh->SetNodeName(pNode->msName); | |
| pSubMesh->SetLocalTransform(pNode->m_mtxTransform); | |
| if(pNode->pParent) | |
| { | |
| pSubMesh->SetGroup(pNode->pParent->msName); | |
| } | |
| //Set the scale | |
| pSubMesh->SetModelScale(pNode->mvScale); | |
| //Log("Creating submesh: '%s'\n",Geom.msName.c_str()); | |
| //To be filled by extra vertex positions (not used, use the one in Geometry) | |
| tColladaExtraVtxListVec &vExtraVtxVec = Geom.mvExtraVtxVec; | |
| ////////////////////////////// | |
| // Create Vertex buffer | |
| eVertexBufferUsageType UsageType = eVertexBufferUsageType_Static; | |
| //The streams will be the entity copies. | |
| //if(vColladaControllers.empty()==false) UsageType = eVertexBufferUsageType_Stream; | |
| iVertexBuffer *pVtxBuffer = CreateVertexBuffer(Geom, UsageType);//, vExtraVtxVec); | |
| pSubMesh->SetVertexBuffer(pVtxBuffer); | |
| //Create an extra set of vertices for shadow rendering | |
| pVtxBuffer->CreateShadowDouble(true); | |
| ///////////////////////////// | |
| //Add material | |
| tString sMatName = GetMaterialTextureFile(Geom.msMaterial,vColladaMaterials,vColladaTextures, | |
| vColladaImages); | |
| //Log("Material name: '%s'\n",sMatName.c_str()); | |
| iMaterial *pMaterial; | |
| if(mbUseFastMaterial && msFastMaterialFile != "") | |
| { | |
| pMaterial = mpMaterialManager->CreateMaterial(msFastMaterialFile); | |
| } | |
| else | |
| { | |
| pMaterial = mpMaterialManager->CreateMaterial(sMatName); | |
| } | |
| if(pMaterial==NULL) | |
| { | |
| Error("Couldn't create material '%s' for object '%s'\n",sMatName.c_str(), | |
| Geom.msName.c_str()); | |
| continue; | |
| } | |
| pSubMesh->SetMaterial(pMaterial); | |
| ///////////////////////////// | |
| //If there is a controller for the mesh, get vertex-bone pairs. | |
| //First find the controller. | |
| cColladaController *pCtrl =NULL; | |
| for(size_t j=0; j<vColladaControllers.size(); j++){ | |
| if(vColladaControllers[j].msTarget == Geom.msId){ | |
| pCtrl = &vColladaControllers[j]; | |
| break; | |
| } | |
| } | |
| //If no controller can be found, guess the one with target="" | |
| if(pCtrl==NULL){ | |
| for(size_t j=0; j<vColladaControllers.size(); j++){ | |
| if(vColladaControllers[j].msTarget == ""){ | |
| pCtrl = &vColladaControllers[j]; | |
| Warning("Guessing controller as target=''!\n"); | |
| break; | |
| } | |
| } | |
| } | |
| //Add the pairs | |
| if(pCtrl && pSkeleton) | |
| { | |
| //Log("Adding vertex-bone pairs!\n"); | |
| //Iterate the pairs | |
| for(size_t j=0; j<pCtrl->mvPairs.size(); j++) | |
| { | |
| //Get all vertices for this vertex pos | |
| tColladaExtraVtxListIt ExtraIt = vExtraVtxVec[j].begin(); | |
| for(; ExtraIt != vExtraVtxVec[j].end(); ++ExtraIt) | |
| { | |
| cColladaExtraVtx &Extra = *ExtraIt; | |
| //Iterate all the influences for this vertex. | |
| tColladaJointPairListIt PairIt = pCtrl->mvPairs[j].begin(); | |
| for(; PairIt != pCtrl->mvPairs[j].end(); ++PairIt) | |
| { | |
| cColladaJointPair &SrcPair = *PairIt; | |
| cVertexBonePair DestPair; | |
| int lBoneIdx = pSkeleton->GetBoneIndexByName(pCtrl->mvJoints[SrcPair.mlJoint]); | |
| DestPair.boneIdx = lBoneIdx; | |
| DestPair.weight = pCtrl->mvWeights[SrcPair.mlWeight]; | |
| DestPair.vtxIdx = Extra.mlNewVtx; | |
| //Add pair in sub mesh | |
| pSubMesh->AddVertexBonePair(DestPair); | |
| //Log("Added pair: bone %d vtx %d weight: %f\n", DestPair.boneIdx,DestPair.vtxIdx, | |
| // DestPair.weight); | |
| } | |
| } | |
| } | |
| //Compile the pairs into a more friendly format. | |
| pSubMesh->CompileBonePairs(); | |
| //Transform the mesh according to the bind transform. | |
| pVtxBuffer->Transform(pCtrl->m_mtxBindShapeMatrix); | |
| } | |
| ////////////////////////////////////////////////// | |
| //No controller, we have a non skinned mesh! | |
| else | |
| { | |
| //We have an animated non-skinned mesh! | |
| //Need to add the node hierarchy | |
| if(vColladaAnimations.empty() == false || bHasSeveralBodies) | |
| { | |
| //If the buffer has animations or joints, we only add the scale part of the | |
| //transform | |
| cMatrixf mtxScale = cMath::MatrixScale(pNode->mvScale); | |
| pVtxBuffer->Transform(mtxScale); | |
| //Log("Scaling %s: (%s) %s!\n",pNode->msName.c_str(),pNode->mvScale.ToString().c_str(),mtxScale.ToString().c_str()); | |
| } | |
| //We have a normal mesh without any geometry and NO joints | |
| else if(bHasSeveralBodies==false) | |
| { | |
| //There are no bones so transform the vertex buffer by the nodes world transform. | |
| cColladaNode *ptNode = ColladaScene.GetNodeFromSource(Geom.msId); | |
| if(ptNode) | |
| { | |
| pVtxBuffer->Transform(ptNode->m_mtxWorldTransform); | |
| //Log("Transforming buffer %s\n",pNode->msName.c_str()); | |
| } | |
| else | |
| { | |
| Warning("No node for geometry '%s' found when trying to transform geometry!\n",Geom.msId.c_str()); | |
| } | |
| } | |
| } | |
| //Create triangle data for the geometry | |
| cMath::CreateTriangleData(*pSubMesh->GetTriangleVecPtr(), | |
| pVtxBuffer->GetIndices(), pVtxBuffer->GetIndexNum(), | |
| pVtxBuffer->GetArray(eVertexFlag_Position), | |
| kvVertexElements[cMath::Log2ToInt(eVertexFlag_Position)], | |
| pVtxBuffer->GetVertexNum()); | |
| //Create edges for the geometry. | |
| bool bDoubleSided = false; | |
| cMath::CreateEdges(*pSubMesh->GetEdgeVecPtr(), | |
| pVtxBuffer->GetIndices(), pVtxBuffer->GetIndexNum(), | |
| pVtxBuffer->GetArray(eVertexFlag_Position), | |
| kvVertexElements[cMath::Log2ToInt(eVertexFlag_Position)], | |
| pVtxBuffer->GetVertexNum(), | |
| &bDoubleSided); | |
| pSubMesh->SetDoubleSided(bDoubleSided); | |
| pSubMesh->Compile(); | |
| } | |
| /////////////////////////////////////////////// | |
| // Add Node Hierarchy | |
| // only add if there are animations and no skeleton. | |
| if((vColladaAnimations.empty() == false || bHasSeveralBodies) && !pSkeleton) | |
| { | |
| cNode3D* pRootNode = pMesh->GetRootNode(); | |
| tColladaNodeListIt it = ColladaScene.mRoot.mlstChildren.begin(); | |
| for(; it != ColladaScene.mRoot.mlstChildren.end(); ++it) | |
| { | |
| CreateHierarchyNodes(pMesh, pRootNode,*it,vColladaGeometries); | |
| } | |
| //Clear scale on all joint nodes. | |
| if(bHasSeveralBodies) | |
| { | |
| for(int i=0; i<pMesh->GetNodeNum(); i++) | |
| { | |
| cNode3D* pMeshNode = pMesh->GetNode(i); | |
| cMatrixf mtxNode = pMeshNode->GetLocalMatrix(); | |
| cSubMesh *pSubMesh = pMesh->GetSubMeshName(pMeshNode->GetSource()); | |
| if(pSubMesh) | |
| { | |
| //Clear the scale | |
| cMatrixf mtxScale = cMath::MatrixScale(pSubMesh->GetModelScale()); | |
| mtxNode = cMath::MatrixMul(mtxNode, cMath::MatrixInverse(mtxScale)); | |
| } | |
| else if(tString(pMeshNode->GetSource()) != "" | |
| && pMeshNode->GetSource()[0] != '_') | |
| { | |
| Error("Cannot find submesh '%s'\n",pMeshNode->GetSource()); | |
| } | |
| pMeshNode->SetMatrix(mtxNode); | |
| } | |
| } | |
| } | |
| //Shouldn't be needed any more. | |
| //vColladaGeometries[i].Clear(); | |
| /////////////////////////////////////////////// | |
| // Create Lights | |
| for(int i=0; i< (int)vColladaLights.size(); i++) | |
| { | |
| //////////////////////////////////////// | |
| //Get the Light and Node | |
| cColladaLight &ColladaLight = vColladaLights[i]; | |
| cColladaNode *pLightNode = ColladaScene.GetNodeFromSource(ColladaLight.msId); | |
| if(pLightNode == NULL) | |
| { | |
| Error("Couldn't find node for light '%s'\n",ColladaLight.msId.c_str()); | |
| continue; | |
| } | |
| tStringVec vParams; | |
| tString sSepp = "_"; | |
| cString::GetStringVec(pLightNode->msName,vParams,&sSepp); | |
| tString sLightName = ""; | |
| tString sLightFile =""; | |
| ///////////////////////////////////// | |
| //Check if there is any light entity file and get name. | |
| //file parameter | |
| if((int)vParams.size() >= 2) | |
| { | |
| for(size_t i2=0; i2< vParams.size()-1; ++i2) | |
| { | |
| sLightFile += vParams[i2]; | |
| if(i2!= vParams.size()-2) sLightFile += "_"; | |
| } | |
| sLightFile = cString::SetFileExt(sLightFile,"lnt"); | |
| sLightName = vParams[vParams.size()-1]; | |
| } | |
| //No file parameter | |
| else | |
| { | |
| sLightName = vParams[0]; | |
| } | |
| cMeshLight *pMeshLight = pMesh->CreateLight(eLight3DType_Point); | |
| pMeshLight->m_mtxTransform = pLightNode->m_mtxTransform; | |
| pMeshLight->msParent = GetParentName(pLightNode,&vColladaGeometries); | |
| pMeshLight->mColor = ColladaLight.mDiffuseColor; | |
| pMeshLight->mfFOV = cMath::ToRad(ColladaLight.mfAngle); | |
| pMeshLight->mfRadius = pLightNode->mvScale.x; | |
| if( ColladaLight.msType == "point") | |
| { | |
| pMeshLight->mType = eLight3DType_Point; | |
| pMeshLight->mColor.a = pLightNode->mvScale.y; | |
| pMeshLight->mbCastShadows = pLightNode->mvScale.z<0.1f ? false: true; | |
| } | |
| else | |
| { | |
| pMeshLight->mType = eLight3DType_Spot; | |
| } | |
| pMeshLight->msName = sLightName; | |
| pMeshLight->msFile = sLightFile; | |
| ////////////////////////////////// | |
| //Fix the transform | |
| FixLocalTransform(&pMeshLight->m_mtxTransform,pLightNode,vColladaAnimations,pSkeleton,bHasSeveralBodies); | |
| } | |
| /////////////////////////////////////////////// | |
| // Create Animations | |
| cAnimation *pAnimation = NULL; | |
| if(vColladaAnimations.empty() == false) | |
| { | |
| pAnimation = hplNew( cAnimation, (pMesh->GetName(),cString::GetFileName(asFile)) ); | |
| pAnimation->SetAnimationName("Default"); | |
| pAnimation->SetLength(ColladaScene.mfDeltaTime); | |
| pAnimation->ResizeTracks((int)vColladaAnimations.size()); | |
| pMesh->AddAnimation(pAnimation); | |
| /////////////////////////////////////////////////// | |
| // Go through all tracks and add them to the animation | |
| for(size_t i=0; i < vColladaAnimations.size(); i++) | |
| { | |
| cAnimationTrack *pTrack = CreateAnimTrack(pAnimation, pSkeleton, | |
| vColladaAnimations[i],&ColladaScene); | |
| if(pTrack==NULL) continue; | |
| if(pSkeleton) | |
| { | |
| cBone *pBone = pSkeleton->GetBoneByName(pTrack->GetName()); | |
| int lBoneIdx = pSkeleton->GetBoneIndexByName(pTrack->GetName()); | |
| pTrack->SetNodeIndex(lBoneIdx); | |
| } | |
| else { | |
| //Find sub mesh. | |
| pTrack->SetNodeIndex(-1); | |
| } | |
| } | |
| ///////////////////////////////////////////////////// | |
| // Go through all bones and check if they have an animation | |
| // If not create single frame with the node pose. | |
| if(pSkeleton) | |
| { | |
| for(int i=0; i< pSkeleton->GetBoneNum(); ++i) | |
| { | |
| cBone *pBone = pSkeleton->GetBoneByIndex(i); | |
| if(pAnimation->GetTrackByName(pBone->GetName())==NULL) | |
| { | |
| cColladaNode *pNode = ColladaScene.GetNode(pBone->GetName()); | |
| if(pNode) | |
| { | |
| cMatrixf mtxLocal = pNode->m_mtxTransform; | |
| cMatrixf mtxInvBone = cMath::MatrixInverse(pBone->GetLocalTransform()); | |
| cMatrixf mtxChange = cMath::MatrixMul(mtxInvBone,mtxLocal); | |
| //Log("Bone %s change mtx: %s\n",pBone->GetName().c_str(), | |
| // mtxChange.ToString().c_str()); | |
| cAnimationTrack * pTrack = pAnimation->CreateTrack(pBone->GetName(),eAnimTransformFlag_Rotate | eAnimTransformFlag_Translate | eAnimTransformFlag_Scale); | |
| pTrack->SetNodeIndex(i); | |
| cKeyFrame *pFrame = pTrack->CreateKeyFrame(0); | |
| pFrame->trans = mtxChange.GetTranslation(); | |
| pFrame->scale = 1; | |
| pFrame->rotation = cQuaternion::Identity; | |
| //Get the quaternion from the rotation matrix | |
| mtxChange.SetTranslation(0); | |
| pFrame->rotation.FromRotationMatrix(mtxChange); | |
| } | |
| else | |
| { | |
| Warning("Couldn't find node for bone '%s'\n",pBone->GetName().c_str()); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| /////////////////////////////////// | |
| ///////// DEBUG /////////////////// | |
| /////////////////////////////////// | |
| // Test anim data | |
| /*Log("ANIMATIONS:\n"); | |
| for(size_t i=0; i < vColladaAnimations.size(); i++) | |
| { | |
| cColladaAnimation &Anim = vColladaAnimations[i]; | |
| Log("Anim: id: '%s' name: '%s'\n",Anim.msId.c_str(), Anim.msName.c_str()); | |
| Log("Channels:\n"); | |
| for(size_t j=0; j< Anim.mvChannels.size(); j++) | |
| Log("Id: '%s' Source: '%s' Target: '%s'\n", | |
| Anim.mvChannels[j].msId.c_str(), | |
| Anim.mvChannels[j].msSource.c_str(), | |
| Anim.mvChannels[j].msTarget.c_str()); | |
| Log("Samplers:\n"); | |
| for(size_t j=0; j< Anim.mvSamplers.size(); j++) | |
| Log("Id: '%s' Target: '%s' Time: '%s' Values: '%s'\n", Anim.mvSamplers[j].msId.c_str(), | |
| Anim.mvSamplers[j].msTarget.c_str(), | |
| Anim.mvSamplers[j].msTimeArray.c_str(), | |
| Anim.mvSamplers[j].msValueArray.c_str()); | |
| Log("Sources:\n"); | |
| for(size_t j=0; j< Anim.mvSources.size(); j++) | |
| { | |
| Log("Id: %s\n",Anim.mvSources[j].msId.c_str()); | |
| for(size_t k=0;k < Anim.mvSources[j].mvValues.size(); k++) | |
| Log("%f, ",Anim.mvSources[j].mvValues[k]); | |
| Log("\n"); | |
| } | |
| //CreateAnimTrack(NULL, Anim,&ColladaScene); | |
| }*/ | |
| return pMesh; | |
| } | |
| //----------------------------------------------------------------------- | |
| cAnimation* cMeshLoaderCollada::LoadAnimation(const tString& asFile) | |
| { | |
| //Controllers | |
| tColladaControllerVec vColladaControllers; | |
| //Animations | |
| tColladaAnimationVec vColladaAnimations; | |
| //Scene | |
| cColladaScene ColladaScene; | |
| //Fill the structures with collada file data | |
| bool bRet = FillStructures(asFile, NULL, NULL, | |
| NULL,NULL, | |
| NULL,&vColladaControllers, | |
| &vColladaAnimations, | |
| &ColladaScene,true | |
| ); | |
| if(bRet==false) return NULL; | |
| //////////////////////// | |
| //Create Skeleton | |
| cSkeleton * pSkeleton = NULL; | |
| if(vColladaControllers.empty() == false) | |
| { | |
| pSkeleton = hplNew( cSkeleton, () ); | |
| tColladaNodeListIt it = ColladaScene.mRoot.mlstChildren.begin(); | |
| for(; it != ColladaScene.mRoot.mlstChildren.end(); it++) | |
| { | |
| cColladaNode *pNode = *it; | |
| CreateSkeletonBone(pNode, pSkeleton->GetRootBone()); | |
| } | |
| //////////////////////////////////// | |
| //Set the bind position of the bones | |
| //(This will set the local matrix as the global bind) | |
| for(size_t i=0; i< vColladaControllers.size();i++) | |
| { | |
| cColladaController &Ctrl = vColladaControllers[i]; | |
| for(size_t j=0; j<Ctrl.mvJoints.size(); j++) | |
| { | |
| cBone *pBone = pSkeleton->GetBoneByName(Ctrl.mvJoints[j]); | |
| if(pBone) | |
| { | |
| pBone->SetTransform(cMath::MatrixInverse(Ctrl.mvMatrices[j])); | |
| pBone->SetValue(1); | |
| } | |
| else | |
| { | |
| Log("Bone '%s' does not exist\n",Ctrl.mvJoints[j].c_str()); | |
| } | |
| } | |
| } | |
| //////////////////////////////////////////////// | |
| //Do another pass and calculate the local matrix | |
| cBoneIterator BoneIt = pSkeleton->GetRootBone()->GetChildIterator(); | |
| while(BoneIt.HasNext()) | |
| { | |
| cMatrixf mtxRoot = cMatrixf::Identity; | |
| CalcLocalMatrixRec(BoneIt.Next(), mtxRoot,0); | |
| } | |
| } | |
| /////////////////////////////////////////////// | |
| // Create Animations | |
| cAnimation *pAnimation = NULL; | |
| if(vColladaAnimations.empty() == false) | |
| { | |
| pAnimation = hplNew( cAnimation, ("Default",cString::GetFileName(asFile)) ); | |
| pAnimation->SetLength(ColladaScene.mfDeltaTime); | |
| pAnimation->ResizeTracks((int)vColladaAnimations.size()); | |
| for(size_t i=0; i < vColladaAnimations.size(); i++) | |
| { | |
| cAnimationTrack *pTrack = CreateAnimTrack(pAnimation, pSkeleton, | |
| vColladaAnimations[i],&ColladaScene); | |
| if(pTrack==NULL) continue; | |
| if(pSkeleton) | |
| { | |
| cBone *pBone = pSkeleton->GetBoneByName(pTrack->GetName()); | |
| int lBoneIdx = pSkeleton->GetBoneIndexByName(pTrack->GetName()); | |
| pTrack->SetNodeIndex(lBoneIdx); | |
| } | |
| else { | |
| //Find sub mesh. | |
| pTrack->SetNodeIndex(-1); | |
| } | |
| } | |
| } | |
| if(pSkeleton) hplDelete(pSkeleton); | |
| return pAnimation; | |
| } | |
| //----------------------------------------------------------------------- | |
| bool cMeshLoaderCollada::IsSupported(const tString asFileType) | |
| { | |
| if(asFileType == "dae") return true; | |
| return false; | |
| } | |
| //----------------------------------------------------------------------- | |
| void cMeshLoaderCollada::AddSupportedTypes(tStringVec* avFileTypes) | |
| { | |
| avFileTypes->push_back("dae"); | |
| } | |
| //----------------------------------------------------------------------- | |
| ////////////////////////////////////////////////////////////////////////// | |
| // PRIVATE METHODS | |
| ///////////////////////////////////////////////////////////////////////// | |
| //----------------------------------------------------------------------- | |
| tString cMeshLoaderCollada::GetParentName(cColladaNode *apNode, tColladaGeometryVec *apColladaGeometries) | |
| { | |
| tString sParent = ""; | |
| if(apNode->pParent) | |
| { | |
| sParent = apNode->pParent->msName; | |
| //If the parent is a geometry the geometry name must be used. | |
| if(apNode->pParent->msSource != "") | |
| { | |
| cColladaGeometry *pGeom = GetGeometry(apNode->pParent->msSource,*apColladaGeometries); | |
| if(pGeom) | |
| { | |
| sParent = pGeom->msName; | |
| } | |
| } | |
| } | |
| return sParent; | |
| } | |
| //----------------------------------------------------------------------- | |
| static bool HasParam(const tStringVec & avVec, const tString &asParam) | |
| { | |
| for(int i=0; i< (int)avVec.size(); i++) | |
| { | |
| if(cString::ToLowerCase(avVec[i]) == asParam){ | |
| return true; | |
| } | |
| } | |
| return false; | |
| } | |
| //----------------------------------------------------------------------- | |
| void cMeshLoaderCollada::CreateMeshJoint(cMeshJoint* apJoint,ePhysicsJointType aJointType, | |
| cBoundingVolume &aBV, | |
| tStringVec &avStrings,cColladaNode* apNode, | |
| cColladaScene &aColladaScene, | |
| tColladaGeometryVec &avColladaVec) | |
| { | |
| ////////////////////////////////////////////// | |
| // GENERAL | |
| if(avStrings.size()<5){ | |
| Error("Joint node '%s' has to few args!\n",apNode->msName.c_str()); | |
| } | |
| apJoint->msName = avStrings[avStrings.size()-1]; | |
| ////////////////////////////////////// | |
| // Get if the joint bodies should collide | |
| if(HasParam(avStrings,"nocollide")){ | |
| apJoint->mbCollide = false; | |
| } | |
| else { | |
| apJoint->mbCollide = true; | |
| } | |
| ////////////////////////////////////// | |
| //Get parent and child body | |
| apJoint->mvPivot = apNode->m_mtxWorldTransform.GetTranslation(); | |
| apJoint->mvPinDir = cMath::Vector3Normalize(apNode->m_mtxWorldTransform.GetUp()); | |
| cColladaNode *pCNode = aColladaScene.GetNode(avStrings[2]); | |
| cColladaNode *pPNode = aColladaScene.GetNode(avStrings[3]); | |
| if(pCNode) { | |
| cColladaGeometry* pGeom = GetGeometry(pCNode->msSource,avColladaVec); | |
| if(pGeom){ | |
| apJoint->msChildBody =pGeom->msName; | |
| } | |
| else{ | |
| Warning("Geometry for joint child '%s' is missing! Might be connected to bone.\n",pCNode->msSource.c_str()); | |
| apJoint->msChildBody = pCNode->msName; | |
| } | |
| } | |
| else | |
| { | |
| Error("Child node '%s' is missing for joint '%s'!\n",avStrings[2].c_str(), | |
| apJoint->msName.c_str()); | |
| return; | |
| } | |
| if(pPNode) { | |
| cColladaGeometry* pGeom = GetGeometry(pPNode->msSource,avColladaVec); | |
| if(pGeom){ | |
| apJoint->msParentBody =pGeom->msName; | |
| } | |
| else{ | |
| Warning("Geometry joint parten '%s' is missing! Might be connected to bone.\n",pPNode->msSource.c_str()); | |
| apJoint->msParentBody = pPNode->msName; | |
| } | |
| } | |
| ////////////////////////////////////// | |
| //Get min and max values. | |
| if((aJointType == ePhysicsJointType_Slider || aJointType== ePhysicsJointType_Screw) | |
| && avStrings.size() <= 6) | |
| { | |
| apJoint->mfMax = aBV.GetMax().y; | |
| apJoint->mfMin = aBV.GetMin().y; | |
| if(apJoint->mfMin>0) apJoint->mfMin =0; | |
| if(apJoint->mfMax<0) apJoint->mfMax =0; | |
| //Log("Min: %f Max: %f\n",apJoint->mfMin,apJoint->mfMax); | |
| } | |
| else | |
| { | |
| if(avStrings.size()>=7) | |
| { | |
| apJoint->mfMin = cString::ToFloat(avStrings[4].c_str(),0); | |
| apJoint->mfMax = cString::ToFloat(avStrings[5].c_str(),0); | |
| if(aJointType == ePhysicsJointType_Slider || aJointType== ePhysicsJointType_Screw) | |
| { | |
| apJoint->mfMin = -apJoint->mfMin/100.0f; | |
| apJoint->mfMax = apJoint->mfMax/100.0f; | |
| } | |
| } | |
| else | |
| { | |
| apJoint->mfMin =0; | |
| apJoint->mfMax =0; | |
| } | |
| } | |
| } | |
| //----------------------------------------------------------------------- | |
| void cMeshLoaderCollada::CreateHierarchyNodes(cMesh *apMesh, cNode3D* mpParentNode, | |
| cColladaNode* apColladaNode, | |
| tColladaGeometryVec &avColladaGeom) | |
| { | |
| cNode3D* pNode = mpParentNode->CreateChild3D(apColladaNode->msName); | |
| apMesh->AddNode(pNode); | |
| pNode->SetMatrix(apColladaNode->m_mtxTransform); | |
| //Log("Node: %s\n",apColladaNode->msName.c_str()); | |
| //Log(" local node transform: %s\n",pNode->GetLocalPosition().ToString().c_str()); | |
| //Log(" local collada transform: %s\n",apColladaNode->m_mtxTransform.GetTranslation().ToString().c_str()); | |
| //Log(" world transform: %s\n",apColladaNode->m_mtxWorldTransform.GetTranslation().ToString().c_str()); | |
| pNode->SetPosition(pNode->GetLocalPosition()); | |
| //Get source name | |
| if(apColladaNode->msSource!="") | |
| { | |
| for(int i=0;i<(int)avColladaGeom.size(); i++){ | |
| if(avColladaGeom[i].msId == apColladaNode->msSource) | |
| { | |
| //Log("Setting source for %s to %s\n",pNode->GetName(), avColladaGeom[i].msName.c_str()); | |
| pNode->SetSource(avColladaGeom[i].msName); | |
| break; | |
| } | |
| } | |
| } | |
| //Iterate children | |
| tColladaNodeListIt it = apColladaNode->mlstChildren.begin(); | |
| for(; it != apColladaNode->mlstChildren.end(); ++it) | |
| { | |
| CreateHierarchyNodes(apMesh, pNode,*it,avColladaGeom); | |
| } | |
| } | |
| //----------------------------------------------------------------------- | |
| cColladaGeometry* cMeshLoaderCollada::GetGeometry(const tString& asId, tColladaGeometryVec &avGeomVec) | |
| { | |
| for(size_t i=0; i<avGeomVec.size(); i++) | |
| { | |
| if(avGeomVec[i].msId == asId) | |
| { | |
| return &avGeomVec[i]; | |
| } | |
| } | |
| return NULL; | |
| } | |
| //----------------------------------------------------------------------- | |
| cColladaLight* cMeshLoaderCollada::GetLight(const tString& asId, tColladaLightVec &avLightVec) | |
| { | |
| for(size_t i=0; i<avLightVec.size(); i++) | |
| { | |
| if(avLightVec[i].msId == asId) | |
| { | |
| return &avLightVec[i]; | |
| } | |
| } | |
| return NULL; | |
| } | |
| //----------------------------------------------------------------------- | |
| cMeshEntity* cMeshLoaderCollada::CreateStaticMeshEntity(cColladaNode* apNode, cWorld3D *apWorld, | |
| cColladaGeometry *apGeom, bool abInRoomGroup, | |
| tColladaMaterialVec &avColladaMaterials, | |
| tColladaTextureVec &avColladaTextures, | |
| tColladaImageVec &avColladaImages) | |
| { | |
| //Get the settings "in the name" | |
| tStringVec vParams; | |
| tString sSepp = "_"; | |
| cString::GetStringVec(apNode->msName,vParams,&sSepp); | |
| cMeshEntity *pEntity = NULL; | |
| iVertexBuffer *pVtxBuffer = NULL; | |
| tString sMatName = GetMaterialTextureFile(apGeom->msMaterial, | |
| avColladaMaterials,avColladaTextures, | |
| avColladaImages); | |
| cMesh *pMesh = NULL; | |
| cSubMesh* pSubMesh = NULL; | |
| bool bDrawn = false; | |
| //////////////////////////////////////////// | |
| // Check if the mesh is drawn | |
| if(HasParam(vParams, "nodraw")==false) | |
| { | |
| bDrawn = true; | |
| //Create mesh and sub mesh | |
| pMesh = hplNew( cMesh, (apNode->msName, mpMaterialManager,mpAnimationManager) ); | |
| pSubMesh = pMesh->CreateSubMesh(apNode->msName+ "_Sub"); | |
| //Create vertex buffer | |
| //tColladaExtraVtxListVec vExtraVtxVec; | |
| pVtxBuffer = CreateVertexBuffer(*apGeom, eVertexBufferUsageType_Static); ///,&apNode->m_mtxWorldTransform); | |
| //Check if If the mesh casts shadows: | |
| pVtxBuffer->CreateShadowDouble(true); | |
| //Transform vertex buffer with world transform | |
| pVtxBuffer->Transform(apNode->m_mtxWorldTransform); | |
| //Log("%s transform: (%s)\n", apNode->msName.c_str(), apNode->m_mtxWorldTransform.ToString().c_str()); | |
| pSubMesh->SetVertexBuffer(pVtxBuffer); | |
| //Create triangle data for the geometry | |
| cMath::CreateTriangleData(*pSubMesh->GetTriangleVecPtr(), | |
| pVtxBuffer->GetIndices(), pVtxBuffer->GetIndexNum(), | |
| pVtxBuffer->GetArray(eVertexFlag_Position), | |
| kvVertexElements[cMath::Log2ToInt(eVertexFlag_Position)], | |
| pVtxBuffer->GetVertexNum()); | |
| //Create edges for the geometry. | |
| bool bDoubleSided = false; | |
| cMath::CreateEdges(*pSubMesh->GetEdgeVecPtr(), | |
| pVtxBuffer->GetIndices(), pVtxBuffer->GetIndexNum(), | |
| pVtxBuffer->GetArray(eVertexFlag_Position), | |
| kvVertexElements[cMath::Log2ToInt(eVertexFlag_Position)], | |
| pVtxBuffer->GetVertexNum(), | |
| &bDoubleSided); | |
| pSubMesh->SetDoubleSided(bDoubleSided); | |
| pSubMesh->Compile(); | |
| //Add material | |
| iMaterial *pMaterial; | |
| if(mbUseFastMaterial && msFastMaterialFile != "") | |
| { | |
| pMaterial = mpMaterialManager->CreateMaterial(msFastMaterialFile); | |
| } | |
| else | |
| { | |
| pMaterial = mpMaterialManager->CreateMaterial(sMatName); | |
| } | |
| if(pMaterial==NULL) | |
| { | |
| Error("Couldn't create material '%s' for object '%s'\n",sMatName.c_str(), | |
| apNode->msName.c_str()); | |
| hplDelete(pMesh); | |
| return NULL; | |
| } | |
| pSubMesh->SetMaterial(pMaterial); | |
| //Create mesh entity | |
| pEntity = apWorld->CreateMeshEntity(apNode->msName, pMesh, false); | |
| pEntity->SetMatrix(cMatrixf::Identity); | |
| //pEntity->SetMatrix(apNode->m_mtxWorldTransform); //<- Debug! | |
| //Set the mesh as static. | |
| pEntity->SetStatic(true); | |
| pEntity->SetIsSaved(false); | |
| if(HasParam(vParams,"noshadow")) | |
| pEntity->SetCastsShadows(false); | |
| else | |
| pEntity->SetCastsShadows(true); | |
| } | |
| if(HasParam(vParams, "nocollide")==false) | |
| { | |
| if(bDrawn==false){ | |
| pVtxBuffer = CreateVertexBuffer(*apGeom, eVertexBufferUsageType_Static); | |
| pVtxBuffer->Transform(apNode->m_mtxWorldTransform); | |
| } | |
| iCollideShape *pShape = apWorld->GetPhysicsWorld()->CreateMeshShape(pVtxBuffer); | |
| iPhysicsBody *pBody = apWorld->GetPhysicsWorld()->CreateBody(apNode->msName,pShape); | |
| if(pBody) | |
| { | |
| pBody->SetMass(0); | |
| pBody->SetIsSaved(false); | |
| //Log("Created body %s!\n",pBody->GetName().c_str()); | |
| } | |
| else | |
| { | |
| Log("Body creation failed!\n"); | |
| } | |
| //Check if it blocks light | |
| pBody->SetBlocksLight(true); | |
| if(bDrawn) | |
| { | |
| if( pEntity->IsShadowCaster()==false || | |
| (pSubMesh->GetMaterial() && pSubMesh->GetMaterial()->IsTransperant())) | |
| { | |
| pBody->SetBlocksLight(false); | |
| } | |
| } | |
| else | |
| { | |
| pBody->SetBlocksLight(false); | |
| } | |
| bool bBlocksSound = false; | |
| if(abInRoomGroup) bBlocksSound = true; | |
| if(HasParam(vParams,"nosoundblock")) bBlocksSound = false; | |
| if(HasParam(vParams,"soundblock")) bBlocksSound = true; | |
| pBody->SetBlocksSound(bBlocksSound); | |
| if(HasParam(vParams, "nocharcollide")) pBody->SetCollideCharacter(false); | |
| tString sPhysicsMatName = apWorld->GetResources()->GetMaterialManager()->GetPhysicsMaterialName(sMatName); | |
| iPhysicsMaterial *pPhysicsMat = apWorld->GetPhysicsWorld()->GetMaterialFromName( | |
| sPhysicsMatName); | |
| if(pPhysicsMat) | |
| { | |
| pBody->SetMaterial(pPhysicsMat); | |
| } | |
| //Haptic creation | |
| if(cHaptic::GetIsUsed()) | |
| { | |
| cHaptic *pHaptic = apWorld->GetHaptic(); | |
| iHapticShape *pHShape = NULL; | |
| pHShape = pHaptic->GetLowLevel()->CreateMeshShape(pBody->GetName(),pVtxBuffer); | |
| if(bDrawn) pHShape->SetSubMeshEntity(pEntity->GetSubMeshEntity(0)); | |
| pBody->SetHapticShape(pHShape); | |
| if(pPhysicsMat) | |
| { | |
| iHapticSurface *pHapticSurface = pPhysicsMat->GetSurfaceData()->GetHapticSurface(); | |
| if(pHapticSurface) pHShape->SetSurface(pHapticSurface); | |
| } | |
| } | |
| if(bDrawn==false){ | |
| hplDelete(pVtxBuffer); | |
| } | |
| } | |
| return pEntity; | |
| } | |
| //----------------------------------------------------------------------- | |
| static iCollideShape* CreatePhysicsCollider(iPhysicsWorld* pPhysicsWorld,const cVector3f& avSize, | |
| eCollideShapeType aShapeType) | |
| { | |
| if(aShapeType == eCollideShapeType_Cylinder) | |
| { | |
| //This is to orient the cylinder along y axis instead of x. | |
| cMatrixf mtxOffset = cMath::MatrixRotateZ(cMath::ToRad(90)); | |
| return pPhysicsWorld->CreateCylinderShape(avSize.x, avSize.y,&mtxOffset); | |
| } | |
| else if(aShapeType == eCollideShapeType_Capsule) | |
| { | |
| //This is to orient the cylinder along y axis instead of x. | |
| cMatrixf mtxOffset = cMath::MatrixRotateZ(cMath::ToRad(90)); | |
| return pPhysicsWorld->CreateCapsuleShape(avSize.x, avSize.y,&mtxOffset); | |
| } | |
| else if(aShapeType == eCollideShapeType_Box) | |
| { | |
| return pPhysicsWorld->CreateBoxShape(avSize, NULL); | |
| } | |
| else if(aShapeType == eCollideShapeType_Sphere) | |
| { | |
| return pPhysicsWorld->CreateSphereShape(avSize.x,NULL); | |
| } | |
| return NULL; | |
| } | |
| cColliderEntity* cMeshLoaderCollada::CreateStaticCollider(cColladaNode* apNode, cWorld3D *apWorld, | |
| cColladaGeometry *apGeom, | |
| tColladaMaterialVec &avColladaMaterials, | |
| tColladaTextureVec &avColladaTextures, | |
| tColladaImageVec &avColladaImages, | |
| bool abCharacterCollider) | |
| { | |
| tStringVec vStrings; | |
| tString sSepp="_"; | |
| cString::GetStringVec(apNode->msName,vStrings,&sSepp); | |
| tFloatVec vVertexVec; | |
| tVertexVec &vArray = apGeom->mvVertexVec; | |
| vVertexVec.resize(vArray.size() *3); | |
| for(size_t vtx=0; vtx < vArray.size(); ++vtx) | |
| { | |
| vVertexVec[vtx*3 + 0] = vArray[vtx].pos.x; | |
| vVertexVec[vtx*3 + 1] = vArray[vtx].pos.y; | |
| vVertexVec[vtx*3 + 2] = vArray[vtx].pos.z; | |
| } | |
| cBoundingVolume TempBV; | |
| TempBV.AddArrayPoints(&vVertexVec[0],(int)vArray.size()); | |
| TempBV.CreateFromPoints(3); | |
| tString sShapeType = cString::ToLowerCase(vStrings[1]); | |
| eCollideShapeType ShapeType = eCollideShapeType_Box; | |
| cVector3f vShapeSize = TempBV.GetSize() * apNode->mvScale; | |
| if(sShapeType == "box"){ | |
| ShapeType = eCollideShapeType_Box; | |
| } | |
| else if(sShapeType == "sphere"){ | |
| ShapeType = eCollideShapeType_Sphere; | |
| vShapeSize *= cVector3f(0.5f); | |
| } | |
| else if(sShapeType == "capsule"){ | |
| ShapeType = eCollideShapeType_Capsule; | |
| vShapeSize.x *= 0.5; | |
| } | |
| else if(sShapeType == "cylinder"){ | |
| ShapeType = eCollideShapeType_Cylinder; | |
| vShapeSize.x *= 0.5; | |
| } | |
| iCollideShape *pCollideShape =NULL; | |
| pCollideShape = CreatePhysicsCollider(apWorld->GetPhysicsWorld(),vShapeSize,ShapeType); | |
| if(pCollideShape==NULL){ | |
| Error("Collider was not created!"); | |
| return NULL; | |
| } | |
| //Log("Creating Collider %s, type: %d size: %s\n", apGeom->msName.c_str(),(int)ShapeType, | |
| // pCollideShape->GetSize().ToString().c_str()); | |
| //Create body | |
| iPhysicsBody *pBody = apWorld->GetPhysicsWorld()->CreateBody(apNode->msName,pCollideShape); | |
| pBody->SetMatrix(apNode->m_mtxWorldTransform); | |
| //Set light block | |
| pBody->SetBlocksLight(false); | |
| //Set material | |
| tString sMatName = GetMaterialTextureFile( apGeom->msMaterial, | |
| avColladaMaterials,avColladaTextures, | |
| avColladaImages); | |
| if(sMatName!="") | |
| { | |
| tString sPhysicsMatName = apWorld->GetResources()->GetMaterialManager()->GetPhysicsMaterialName(sMatName); | |
| if(sPhysicsMatName!="") | |
| { | |
| iPhysicsMaterial *pPhysicsMat = apWorld->GetPhysicsWorld()->GetMaterialFromName( | |
| sPhysicsMatName); | |
| if(pPhysicsMat){ | |
| pBody->SetMaterial(pPhysicsMat); | |
| } | |
| } | |
| } | |
| //Check if it blocks sound | |
| bool bBlocksSound = false; | |
| if(HasParam(vStrings,"soundblock")) bBlocksSound = true; | |
| pBody->SetBlocksSound(bBlocksSound); | |
| pBody->SetIsSaved(false); | |
| pBody->SetCollideCharacter(true); | |
| if(abCharacterCollider) | |
| pBody->SetCollide(false); | |
| else | |
| pBody->SetCollide(true); | |
| //Haptic creation | |
| if(cHaptic::GetIsUsed()) | |
| { | |
| cHaptic *pHaptic = apWorld->GetHaptic(); | |
| iHapticShape *pHShape = pHaptic->GetLowLevel()->CreateShapeFromPhysicsBody(apNode->msName,pBody); | |
| } | |
| return apWorld->CreateColliderEntity(apNode->msName,pBody); | |
| } | |
| //----------------------------------------------------------------------- | |
| void cMeshLoaderCollada::AddSceneObjects(cColladaNode* apNode, cWorld3D *apWorld, | |
| tColladaGeometryVec &avColladaGeometries, | |
| tColladaLightVec &avColladaLights, | |
| tColladaMaterialVec &avColladaMaterials, | |
| tColladaTextureVec &avColladaTextures, | |
| tColladaImageVec &avColladaImages, | |
| cColladaScene *apColladaScene) | |
| { | |
| ////////////////////////////////////////////////// | |
| //Check if we are dealing with a special type. | |
| if(apNode->msName.size() > 1 && apNode->msName[0] == '_') | |
| { | |
| tString sType = cString::Sub(apNode->msName,0,5); | |
| if(cString::ToLowerCase(sType) == "_room") | |
| { | |
| //Log("Found room, leaving branch...\n"); | |
| return; | |
| } | |
| tStringVec vParams; | |
| tString sSepp = "_"; | |
| cString::GetStringVec(apNode->msName,vParams,&sSepp); | |
| //////////////////////////////////////// | |
| ///// COLLIDER ///////////////////////////// | |
| if(cString::ToLowerCase(vParams[0])=="collider") | |
| { | |
| cColladaGeometry* pGeom = GetGeometry(apNode->msSource, avColladaGeometries); | |
| if(pGeom) | |
| { | |
| cColliderEntity *pCollider = CreateStaticCollider(apNode,apWorld,pGeom,avColladaMaterials, | |
| avColladaTextures, avColladaImages, | |
| false); | |
| apWorld->GetPortalContainer()->Add(pCollider, true); | |
| } | |
| else | |
| { | |
| Error("Node '%s' does not have any geometry! Could not create collider!\n",apNode->msName.c_str()); | |
| } | |
| } | |
| //////////////////////////////////////// | |
| ///// CHARACTER COLLIDER /////////////// | |
| else if(cString::ToLowerCase(vParams[0])=="charcollider") | |
| { | |
| cColladaGeometry* pGeom = GetGeometry(apNode->msSource, avColladaGeometries); | |
| if(pGeom) | |
| { | |
| cColliderEntity *pCollider = CreateStaticCollider(apNode,apWorld,pGeom,avColladaMaterials, | |
| avColladaTextures, avColladaImages, | |
| true); | |
| apWorld->GetPortalContainer()->Add(pCollider, true); | |
| } | |
| else | |
| { | |
| Error("Node '%s' does not have any geometry!C ould not create charcollider!\n",apNode->msName.c_str()); | |
| } | |
| } | |
| //////////////////////////////////////// | |
| ///// SOUND ///////////////////////////// | |
| else if(cString::ToLowerCase(vParams[0])=="sound" && !(mFlags & eWorldLoadFlag_NoEntities)) | |
| { | |
| if(vParams.size() < 3){ | |
| Error("Too few params in sound entity '%s'\n",apNode->msName.c_str()); | |
| } | |
| else | |
| { | |
| tString sFile = ""; | |
| for(size_t i=1; i<vParams.size()-1; ++i) | |
| { | |
| sFile += vParams[i]; | |
| if(i != vParams.size()-2) sFile+="_"; | |
| } | |
| tString sName = vParams[vParams.size()-1]; | |
| cSoundEntity *pEntity = apWorld->CreateSoundEntity(sName,sFile,false); | |
| if(pEntity == NULL) | |
| { | |
| tString sName = vParams[1]; | |
| tString sFile = cString::Sub(apNode->msName,7+(int)+sName.size()+1); | |
| pEntity = apWorld->CreateSoundEntity(sName,sFile,false); | |
| } | |
| if(pEntity) pEntity->SetMatrix(apNode->m_mtxWorldTransform); | |
| } | |
| } | |
| //////////////////////////////////////// | |
| ///// START ///////////////////////////// | |
| else if(cString::ToLowerCase(vParams[0])=="start" && !(mFlags & eWorldLoadFlag_NoEntities)) | |
| { | |
| if(vParams.size() < 2){ | |
| Error("Too few params in start entity '%s'\n",apNode->msName.c_str()); | |
| } | |
| else | |
| { | |
| tString sName = cString::Sub(apNode->msName,7); | |
| cStartPosEntity *pStart = apWorld->CreateStartPos(sName); | |
| if(pStart){ | |
| pStart->SetMatrix(apNode->m_mtxWorldTransform); | |
| } | |
| } | |
| } | |
| //////////////////////////////////////// | |
| ///// REF ///////////////////////////// | |
| else if(cString::ToLowerCase(vParams[0])=="ref") | |
| { | |
| if((mFlags & eWorldLoadFlag_NoGameEntities)) return; | |
| if(vParams.size() < 3){ | |
| Error("Too few params in ref entity '%s'\n",apNode->msName.c_str()); | |
| } | |
| else | |
| { | |
| tString sFile = ""; | |
| for(size_t i=1; i<vParams.size()-1; ++i) | |
| { | |
| sFile += vParams[i]; | |
| if(i != vParams.size()-2) sFile+="_"; | |
| } | |
| tString sName = vParams[vParams.size()-1]; | |
| iEntity3D* pEntity = apWorld->CreateEntity(sName,apNode->m_mtxWorldTransform,sFile, true); | |
| //If no entity was loaded, try old style. | |
| if(pEntity==NULL) | |
| { | |
| tString sName = vParams[1]; | |
| tString sFile = cString::Sub(apNode->msName,5+(int)+sName.size()+1); | |
| sFile = cString::SetFileExt(sFile,"ent"); | |
| apWorld->CreateEntity(sName,apNode->m_mtxWorldTransform,sFile, true); | |
| } | |
| } | |
| //Skip the children of the ref! | |
| return; | |
| } | |
| //////////////////////////////////////// | |
| ///// AI NODE ////////////////////////// | |
| else if(cString::ToLowerCase(vParams[0])=="node") | |
| { | |
| if(vParams.size() < 3){ | |
| Error("Too few params in ref entity '%s'\n",apNode->msName.c_str()); | |
| } | |
| else | |
| { | |
| tString sType = vParams[1]; | |
| tString sName = cString::Sub(apNode->msName,6+(int)sType.size()+1); | |
| apWorld->AddAINode(sName,sType,apNode->m_mtxWorldTransform.GetTranslation()); | |
| } | |
| //Skip the children of the ref! | |
| return; | |
| } | |
| //////////////////////////////////////// | |
| ///// BILLBOARD ///////////////////////////// | |
| else if(cString::ToLowerCase(vParams[0])=="bb" && !(mFlags & eWorldLoadFlag_NoEntities)) | |
| { | |
| if(vParams.size() < 3){ | |
| Error("Too few params in billboard entity entity '%s'\n",apNode->msName.c_str()); | |
| } | |
| else | |
| { | |
| cVector2f vSize(apNode->mvScale.x,apNode->mvScale.y); | |
| float fOffset = apNode->mvScale.z; | |
| tString sName = vParams[vParams.size()-1]; | |
| tString sFile = ""; | |
| for(size_t i=1; i<vParams.size()-1; ++i) | |
| { | |
| sFile += vParams[i]; | |
| if(i != vParams.size()-2) sFile+="_"; | |
| } | |
| cBillboard *pBill = apWorld->CreateBillboard(sName,vSize); | |
| //To support old versions | |
| if(pBill == NULL) | |
| { | |
| sName = vParams[1]; | |
| sFile = cString::Sub(apNode->msName,4+(int)sName.size()+1); | |
| pBill = apWorld->CreateBillboard(sName,vSize); | |
| } | |
| if(pBill) | |
| { | |
| pBill->SetForwardOffset(fOffset); | |
| //Old version , trying to skip because bounding box calc for this is far from | |
| //optimal | |
| //pBill->SetPosition(apNode->m_mtxWorldTransform.GetTranslation()); | |
| //pBill->SetAxis(cMath::Vector3Normalize(cMath::MatrixInverse(apNode->m_mtxWorldTransform).GetUp())); | |
| //remove scale | |
| cMatrixf mtxScale = cMath::MatrixScale(apNode->mvScale); | |
| cMatrixf mtxWorld = cMath::MatrixMul(apNode->m_mtxWorldTransform, cMath::MatrixInverse(mtxScale)); | |
| pBill->SetMatrix(mtxWorld); | |
| pBill->SetAxis(cVector3f(0,1,0)); | |
| sFile = cString::SetFileExt(sFile,"bnt"); | |
| pBill->LoadXMLProperties(sFile); | |
| } | |
| } | |
| } | |
| //////////////////////////////////////// | |
| ///// BEAM ///////////////////////////// | |
| else if(cString::ToLowerCase(vParams[0])=="beam" && !(mFlags & eWorldLoadFlag_NoEntities)) | |
| { | |
| if(vParams.size() < 3){ | |
| Error("Too few params in billboard entity entity '%s'\n",apNode->msName.c_str()); | |
| } | |
| else | |
| { | |
| cVector2f vSize(apNode->mvScale.x,apNode->mvScale.y); | |
| float fOffset = apNode->mvScale.z; | |
| tString sName = vParams[vParams.size()-1]; | |
| tString sFile = ""; | |
| for(size_t i=1; i<vParams.size()-1; ++i) | |
| { | |
| sFile += vParams[i]; | |
| if(i != vParams.size()-2) sFile+="_"; | |
| } | |
| //Find end point node | |
| tString sEndName = "_beamend_"+sName; | |
| cColladaNode *pEndNode = apColladaScene->GetNode(sEndName); | |
| if(pEndNode) | |
| { | |
| cBeam *pBeam = apWorld->CreateBeam(sName); | |
| if(pBeam) | |
| { | |
| pBeam->SetPosition(apNode->m_mtxWorldTransform.GetTranslation()); | |
| pBeam->GetEnd()->SetPosition(pEndNode->m_mtxWorldTransform.GetTranslation()); | |
| sFile = cString::SetFileExt(sFile,"beam"); | |
| pBeam->LoadXMLProperties(sFile); | |
| } | |
| } | |
| else | |
| { | |
| Error("Couldn't find beam end '%s'!\n",sEndName.c_str()); | |
| } | |
| } | |
| } | |
| //////////////////////////////////////// | |
| ///// PARTICLE SYSTEM ////////////////// | |
| else if(cString::ToLowerCase(vParams[0])=="ps" && !(mFlags & eWorldLoadFlag_NoEntities)) | |
| { | |
| if(vParams.size() < 3){ | |
| Error("Too few params in billboard entity entity '%s'\n",apNode->msName.c_str()); | |
| } | |
| else | |
| { | |
| tString sName = vParams[vParams.size()-1]; | |
| tString sType = ""; | |
| for(size_t i=1; i<vParams.size()-1; ++i) | |
| { | |
| sType += vParams[i]; | |
| if(i != vParams.size()-2) sType+="_"; | |
| } | |
| cParticleSystem3D *pPS = apWorld->CreateParticleSystem(sName,sType,apNode->mvScale, | |
| apNode->m_mtxWorldTransform); | |
| if(pPS == NULL) | |
| { | |
| sName = vParams[1]; | |
| sType = cString::Sub(apNode->msName,4+(int)sName.size()+1); | |
| cParticleSystem3D *pPS = apWorld->CreateParticleSystem(sName,sType,apNode->mvScale, | |
| apNode->m_mtxWorldTransform); | |
| if(pPS == NULL) | |
| { | |
| Error("Couldn't load particle system '%s' with type '%s'\n", | |
| sName.c_str(), sType.c_str()); | |
| } | |
| } | |
| } | |
| } | |
| //////////////////////////////////////// | |
| ///// AREA ///////////////////////////// | |
| if(cString::ToLowerCase(vParams[0])=="area" && !(mFlags & eWorldLoadFlag_NoEntities)) | |
| { | |
| if(vParams.size() < 2){ | |
| Error("Too few params in area entity '%s'\n",apNode->msName.c_str()); | |
| } | |
| else | |
| { | |
| cColladaGeometry* pGeom = GetGeometry(apNode->msSource, avColladaGeometries); | |
| tFloatVec vVertexVec; | |
| tVertexVec &vArray = pGeom->mvVertexVec; | |
| vVertexVec.resize(vArray.size() *3); | |
| for(size_t vtx=0; vtx < vArray.size(); ++vtx) | |
| { | |
| vVertexVec[vtx*3 + 0] = vArray[vtx].pos.x; | |
| vVertexVec[vtx*3 + 1] = vArray[vtx].pos.y; | |
| vVertexVec[vtx*3 + 2] = vArray[vtx].pos.z; | |
| } | |
| cBoundingVolume TempBV; | |
| TempBV.AddArrayPoints(&vVertexVec[0],(int)vArray.size()); | |
| TempBV.CreateFromPoints(3); | |
| tString sType; | |
| tString sName; | |
| if(vParams.size() < 3 || (mFlags & eWorldLoadFlag_NoGameEntities)) | |
| { | |
| sType = ""; | |
| if(vParams.size() < 3) | |
| sName = cString::ToLowerCase(vParams[1]); | |
| else | |
| sName = cString::Sub(apNode->msName,(int)vParams[0].size()+(int)vParams[1].size()+3); | |
| } | |
| else | |
| { | |
| sType = cString::ToLowerCase(vParams[1]); | |
| sName = cString::Sub(apNode->msName,(int)vParams[0].size()+(int)vParams[1].size()+3); | |
| } | |
| cVector3f vSize = TempBV.GetSize() * apNode->mvScale; | |
| if(sType!="") | |
| { | |
| iArea3DLoader *pLoader = apWorld->GetResources()->GetArea3DLoader(sType); | |
| if(pLoader) | |
| { | |
| pLoader->Load(sName,vSize,apNode->m_mtxWorldTransform,apWorld); | |
| } | |
| } | |
| //Create engine area. | |
| cAreaEntity *pArea = apWorld->CreateAreaEntity(sName); | |
| pArea->m_mtxTransform = apNode->m_mtxWorldTransform; | |
| pArea->msType = sType; | |
| pArea->mvSize = vSize; | |
| } | |
| } | |
| } | |
| ////////////////////////////////////////////////// | |
| //Load light or geometry | |
| else | |
| { | |
| //The node has a source | |
| if(apNode->msSource != "") | |
| { | |
| //Get number of chars in prefix. | |
| int lPrefixChars =1; | |
| while( lPrefixChars < apNode->msName.size() && | |
| apNode->msName[lPrefixChars]!= '_' && | |
| apNode->msName[lPrefixChars]!='\0') { | |
| lPrefixChars++; | |
| } | |
| //Get prefix | |
| tString sPrefix = cString::ToLowerCase(cString::Sub(apNode->msName,0,lPrefixChars)); | |
| ///////////////////////////////////////////////// | |
| // Load source from external file | |
| if(apNode->mbSourceIsFile) | |
| { | |
| tString sFile = cString::SetFileExt(cString::GetFileName(apNode->msSource),""); | |
| //If static, load mesh from mesh file. | |
| if(mFlags & eWorldLoadFlag_NoGameEntities) | |
| { | |
| //Do nothing... | |
| } | |
| else if(sPrefix == "static") | |
| { | |
| cMesh *pMesh = mpMeshManager->CreateMesh(sFile); | |
| if(pMesh) | |
| { | |
| //Create the entity | |
| cMeshEntity* pEntity = apWorld->CreateMeshEntity(apNode->msName,pMesh, false); | |
| //Log("Static mesh entity mtx: %s\n",cMath::MatrixToChar(apNode->m_mtxWorldTransform)); | |
| pEntity->SetMatrix(apNode->m_mtxWorldTransform); | |
| apWorld->GetPortalContainer()->Add(pEntity, true); | |
| //Set parameters | |
| tStringVec vParams; | |
| tString sSepp = "_"; | |
| cString::GetStringVec(apNode->msName,vParams,&sSepp); | |
| if(HasParam(vParams,"noshadow")) | |
| pEntity->SetCastsShadows(false); | |
| else | |
| pEntity->SetCastsShadows(true); | |
| } | |
| } | |
| //If it is not static it is an entity. Load form entity file. | |
| else | |
| { | |
| tString sEntityFile = cString::SetFileExt(sFile,"ent"); | |
| apWorld->CreateEntity(apNode->msName,apNode->m_mtxWorldTransform,sEntityFile, true); | |
| } | |
| } | |
| /////////////////////////////////////// | |
| // Load source from collada data | |
| else | |
| { | |
| //Create an entity from the geometry. | |
| cColladaGeometry* pGeom = GetGeometry(apNode->msSource, avColladaGeometries); | |
| if(pGeom) | |
| { | |
| //Log("Creating mesh '%s'!\n",pGeom->msName.c_str()); | |
| cMeshEntity *pEntity = CreateStaticMeshEntity(apNode, apWorld, | |
| pGeom,false, | |
| avColladaMaterials, | |
| avColladaTextures, avColladaImages); | |
| if(pEntity) | |
| { | |
| apWorld->GetPortalContainer()->Add(pEntity, true); | |
| } | |
| } | |
| else if(!(mFlags & eWorldLoadFlag_NoLights)) | |
| { | |
| //If there wasn't any geometry, try and find a light. | |
| cColladaLight* pColladaLight = GetLight(apNode->msSource, avColladaLights); | |
| if(pColladaLight) | |
| { | |
| tStringVec vParams; | |
| tString sSepp = "_"; | |
| cString::GetStringVec(apNode->msName,vParams,&sSepp); | |
| //check if this is an dynamic light | |
| bool bStatic = true; | |
| int lParamAdd =0; //Too make it easier to support dynamic param | |
| if(cString::ToLowerCase(vParams[0]) == "dynamic"){ | |
| bStatic =false; | |
| lParamAdd =1; | |
| } | |
| tString sLightName =""; | |
| tString sLightFile =""; | |
| //Check if light might have file parameter | |
| if((int)vParams.size() >= 2 +lParamAdd) | |
| { | |
| for(size_t i=lParamAdd; i< vParams.size()-1; ++i) | |
| { | |
| sLightFile += vParams[i]; | |
| if(i!= vParams.size()-2) sLightFile += "_"; | |
| } | |
| sLightFile = cString::SetFileExt(sLightFile,"lnt"); | |
| sLightName = vParams[vParams.size()-1]; | |
| } | |
| //No file parameter | |
| else | |
| { | |
| sLightName = vParams[lParamAdd]; | |
| } | |
| ///////////////////////////////////// | |
| //Load the specific light type | |
| if(pColladaLight->msType =="point") | |
| { | |
| cLight3DPoint *pLight = apWorld->CreateLightPoint(sLightName, false); | |
| pLight->SetMatrix(apNode->m_mtxWorldTransform); | |
| pColladaLight->mDiffuseColor.a = apNode->mvScale.y; | |
| pLight->SetDiffuseColor(pColladaLight->mDiffuseColor); | |
| pLight->SetFarAttenuation(apNode->mvScale.x); | |
| if(apNode->mvScale.z < 0.1f) | |
| pLight->SetCastShadows(false); | |
| else | |
| pLight->SetCastShadows(true); | |
| if(bStatic) pLight->SetStatic(bStatic); | |
| if(mbRestricStaticLightToSector) pLight->SetOnlyAffectInSector(true); | |
| apWorld->GetPortalContainer()->Add(pLight, bStatic); | |
| //Log("Added light '%s' attenuation: %f a: %f\n",pLight->GetName().c_str(), | |
| // pLight->GetFarAttenuation(), pLight->GetDiffuseColor().a); | |
| if(sLightFile != "") | |
| pLight->LoadXMLProperties(sLightFile); | |
| } | |
| else if(pColladaLight->msType =="spot") | |
| { | |
| cLight3DSpot *pLight = apWorld->CreateLightSpot(sLightName,"", false); | |
| pLight->SetMatrix(apNode->m_mtxWorldTransform); | |
| pLight->SetDiffuseColor(pColladaLight->mDiffuseColor); | |
| pLight->SetFarAttenuation(apNode->mvScale.x); | |
| pLight->SetFOV(cMath::ToRad(pColladaLight->mfAngle)); | |
| if(bStatic) pLight->SetStatic(bStatic); | |
| if(mbRestricStaticLightToSector) pLight->SetOnlyAffectInSector(true); | |
| apWorld->GetPortalContainer()->Add(pLight, bStatic); | |
| if(sLightFile != "") | |
| pLight->LoadXMLProperties(sLightFile); | |
| } | |
| else | |
| { | |
| Warning("Invalid light type '%s' for light '%s'\n",pColladaLight->msType.c_str(), | |
| apNode->msName.c_str()); | |
| } | |
| } | |
| else | |
| { | |
| Warning("Source '%s' is not found!\n", apNode->msSource.c_str()); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| //Iterate children. | |
| tColladaNodeListIt ChildIt = apNode->mlstChildren.begin(); | |
| for(; ChildIt != apNode->mlstChildren.end();ChildIt++) | |
| { | |
| AddSceneObjects(*ChildIt, apWorld,avColladaGeometries,avColladaLights, | |
| avColladaMaterials,avColladaTextures,avColladaImages,apColladaScene); | |
| } | |
| } | |
| //----------------------------------------------------------------------- | |
| void cMeshLoaderCollada::AddSectorChildren(cColladaNode* apNode, tString asSector, cWorld3D *apWorld, | |
| tColladaGeometryVec &avColladaGeometries, | |
| tColladaLightVec &avColladaLights, | |
| tColladaMaterialVec &avColladaMaterials, | |
| tColladaTextureVec &avColladaTextures, | |
| tColladaImageVec &avColladaImages) | |
| { | |
| //Log("--- Node: %s\n",apNode->msName.c_str()); | |
| ////////////////////////////////////////////////// | |
| //Check if we are dealing with a special type. | |
| if(apNode->msName[0] == '_') | |
| { | |
| //Check if it is a portal | |
| tString sType = cString::Sub(apNode->msName,0,7); | |
| if(cString::ToLowerCase(sType) == "_portal") | |
| { | |
| //////////////////////////////// | |
| //Get portal number | |
| //Get digits in num: | |
| int lDigits =1; | |
| while(apNode->msName[7+lDigits] != '_' && apNode->msName[7+lDigits] != 0) lDigits++; | |
| //get string and convert to int | |
| tString sNum = cString::Sub(apNode->msName,7,lDigits); | |
| int lNum = cString::ToInt(sNum.c_str(),-1); | |
| if(lNum==-1){ | |
| Warning("Bad portal name: '%s'!\n",apNode->msName.c_str()); | |
| } | |
| /////////////////////////////////////////// | |
| // Get target room | |
| //Get the char pos for the target room | |
| int lStartChar = 7 + lDigits + 1; | |
| tString sTest = cString::ToLowerCase(cString::Sub(apNode->msName,lStartChar, 4)); | |
| if(sTest != "room"){ | |
| Error("Bad portal id 's'!\n", apNode->msName.c_str()); | |
| return; | |
| } | |
| //Get number of digits | |
| lDigits =1; | |
| while( apNode->msName.length() > lStartChar+4+lDigits && | |
| apNode->msName[lStartChar+4+lDigits] != '_' && | |
| apNode->msName.length() >= 7+lDigits && | |
| apNode->msName[7+lDigits] != 0) | |
| { | |
| lDigits++; | |
| } | |
| sNum = cString::Sub(apNode->msName,lStartChar+4,lDigits); | |
| //int lTargetSector = cString::ToInt(sNum.c_str(),-1); | |
| tString sTargetSector = sNum; | |
| //////////////////////////////////////////// | |
| // Get the portals in the target room it can see. | |
| lStartChar = lStartChar+4+lDigits; | |
| tIntVec vPortalIds; | |
| tString sValueString = cString::Sub(apNode->msName,lStartChar); | |
| tString sSepp = "_"; | |
| cString::GetIntVec(sValueString, vPortalIds,&sSepp); | |
| cColladaGeometry* pGeom = GetGeometry(apNode->msSource, avColladaGeometries); | |
| if(pGeom) | |
| { | |
| cPortal *pPortal = hplNew( cPortal, (lNum, apWorld->GetPortalContainer()) ); | |
| //Set target | |
| pPortal->SetTargetSector(sTargetSector); | |
| //Add portals that can be seen. | |
| for(size_t i=0; i< vPortalIds.size();i++) | |
| { | |
| pPortal->AddPortalId(vPortalIds[i]); | |
| //Log("Seeing: %d\n", vPortalIds[i]); | |
| } | |
| ///////////////////////////////////// | |
| //Calculate the normal of the portal | |
| cVector3f vNormal = cVector3f(0,0,0); | |
| for(size_t i=0; i < pGeom->mvVertexVec.size(); i++) | |
| { | |
| vNormal += pGeom->mvVertexVec[i].norm; | |
| } | |
| vNormal = cMath::MatrixMul(apNode->m_mtxWorldTransform.GetRotation(), vNormal); | |
| vNormal.Normalise(); | |
| pPortal->SetNormal(vNormal); | |
| ///////////////////////////////// | |
| //Add the points | |
| for(size_t i=0; i < pGeom->mvVertexVec.size(); i++) | |
| { | |
| cVector3f vPos = cMath::MatrixMul(apNode->m_mtxWorldTransform, | |
| pGeom->mvVertexVec[i].pos); | |
| pPortal->AddPoint(vPos); | |
| } | |
| ////////////////////////// | |
| //Set the world transform. | |
| pPortal->SetTransform(cMatrixf::Identity);//apNode->m_mtxWorldTransform); | |
| ///////////////////// | |
| //Compile the portal | |
| pPortal->Compile(); | |
| //////////////////////// | |
| //Add portal to sector. | |
| apWorld->GetPortalContainer()->AddPortal(pPortal, asSector); | |
| } | |
| } | |
| } | |
| //////////////////////////////////////////// | |
| // Add normal geometry or light | |
| else | |
| { | |
| if(apNode->msSource != "") | |
| { | |
| if(apNode->mbSourceIsFile) | |
| { | |
| Error("Entities are NOT allowed in the room tree!\n"); | |
| } | |
| else | |
| { | |
| //Create an entity from the geometry. | |
| cColladaGeometry* pGeom = GetGeometry(apNode->msSource, avColladaGeometries); | |
| if(pGeom) | |
| { | |
| //Log("Creating mesh '%s' from source '%s'!\n",pGeom->msName.c_str(),apNode->msSource.c_str()); | |
| cMeshEntity *pEntity = CreateStaticMeshEntity(apNode, apWorld, | |
| pGeom, true, | |
| avColladaMaterials, | |
| avColladaTextures, avColladaImages); | |
| if(pEntity) | |
| { | |
| apWorld->GetPortalContainer()->AddToSector(pEntity, asSector); | |
| } | |
| } | |
| else | |
| { | |
| cColladaLight* pLight = GetLight(apNode->msSource, avColladaLights); | |
| if(pLight) | |
| { | |
| Error("Lights are NOT allowed in the room tree!\n"); | |
| } | |
| else | |
| { | |
| Warning("Source '%s' is not found!\n", apNode->msSource.c_str()); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| //Log("---\n"); | |
| ////////////////////////////////////////////////// | |
| //Iterate children. | |
| tColladaNodeListIt ChildIt = apNode->mlstChildren.begin(); | |
| for(; ChildIt != apNode->mlstChildren.end();ChildIt++) | |
| { | |
| AddSectorChildren(*ChildIt, asSector, apWorld,avColladaGeometries,avColladaLights, | |
| avColladaMaterials,avColladaTextures,avColladaImages); | |
| } | |
| } | |
| //----------------------------------------------------------------------- | |
| } |