/* ScummVM - Graphic Adventure Engine * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT * file distributed with this source distribution. * * 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 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ /* * Copyright (C) 2006-2010 - Frictional Games * * This file is part of HPL1 Engine. */ #include "hpl1/engine/impl/MeshLoaderCollada.h" #include "hpl1/engine/graphics/LowLevelGraphics.h" #include "hpl1/engine/graphics/VertexBuffer.h" #include "hpl1/engine/system/String.h" #include "hpl1/engine/system/System.h" #include "hpl1/engine/system/low_level_system.h" #include "hpl1/engine/scene/ColliderEntity.h" #include "hpl1/engine/scene/Light3DPoint.h" #include "hpl1/engine/scene/Light3DSpot.h" #include "hpl1/engine/scene/MeshEntity.h" #include "hpl1/engine/scene/Node3D.h" #include "hpl1/engine/scene/PortalContainer.h" #include "hpl1/engine/scene/Scene.h" #include "hpl1/engine/scene/SoundEntity.h" #include "hpl1/engine/scene/World3D.h" #include "hpl1/engine/graphics/Material.h" #include "hpl1/engine/graphics/Mesh.h" #include "hpl1/engine/graphics/SubMesh.h" #include "hpl1/engine/resources/FileSearcher.h" #include "hpl1/engine/resources/MaterialManager.h" #include "hpl1/engine/resources/MeshManager.h" #include "hpl1/engine/resources/Resources.h" #include "hpl1/engine/graphics/Animation.h" #include "hpl1/engine/graphics/AnimationTrack.h" #include "hpl1/engine/graphics/Bone.h" #include "hpl1/engine/graphics/Skeleton.h" #include "hpl1/engine/graphics/Beam.h" #include "hpl1/engine/graphics/BillBoard.h" #include "hpl1/engine/graphics/ParticleSystem3D.h" #include "hpl1/engine/physics/Physics.h" #include "hpl1/engine/physics/PhysicsBody.h" #include "hpl1/engine/physics/PhysicsMaterial.h" #include "hpl1/engine/physics/PhysicsWorld.h" #include "hpl1/engine/physics/SurfaceData.h" #include "hpl1/engine/impl/tinyXML/tinyxml.h" #include "hpl1/engine/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 referred 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; else error("Unknown JointType: %s", sJointType.c_str()); 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 position add /*cMatrixf mtxTrans = cMath::MatrixTranslate(vOffset); pCollider->m_mtxOffset = cMath::MatrixMul( pNode->m_mtxWorldTransform, mtxTrans);*/ // World position 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 = 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 = 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); } // WORKAROUND: Bug #14571: "HPL1: sliding door does not move" // The original version of the Newton Dynamics library created incorrect colliders for the wall that allowed the door to move correctly. // The newer version fixes that, tilting the door (which should slide upwards) forward, making it unable to move. // Modifying the door collider did not solve the issue as the player would get partially blocked if they entered from the wrong direction. // Position 22 is the y coordinate of the wall vertex that intersects the door, and the value is taken from another vertex position in the list. if (apNode->msName == "room4_wall2") { pVtxBuffer->GetArray(eVertexFlag_Position)[22] = -64.470757f; } // WORKAROUND: Bug #14572: "HPL1: crash after breaking the ice in level "Lake Utuqaq"" // The object below has an empty mesh that generates an invalid collider, which causes a crash when the Newton library // tries to resolve a collision between the object and another physics body called "ice4_broken_pieceShape3". if (apNode->msName == "Shape01") { return nullptr; } 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"); return nullptr; } // 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); } 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); 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) { sName = vParams[1]; 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) { sName = vParams[1]; 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 { 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]; 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); 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 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 < (int)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 ((int)apNode->msName.size() > lStartChar + 4 + lDigits && apNode->msName[lStartChar + 4 + lDigits] != '_' && (int)apNode->msName.size() >= 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); } } //----------------------------------------------------------------------- } // namespace hpl