2211 lines
73 KiB
C++
2211 lines
73 KiB
C++
/* 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 <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
/*
|
|
* 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
|