905 lines
33 KiB
C++
905 lines
33 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/resources/EntityLoader_Object.h"
|
|
|
|
#include "hpl1/engine/impl/tinyXML/tinyxml.h"
|
|
#include "hpl1/engine/scene/World3D.h"
|
|
#include "hpl1/engine/system/String.h"
|
|
#include "hpl1/engine/system/low_level_system.h"
|
|
|
|
#include "hpl1/engine/physics/CollideShape.h"
|
|
#include "hpl1/engine/physics/PhysicsBody.h"
|
|
#include "hpl1/engine/physics/PhysicsController.h"
|
|
#include "hpl1/engine/physics/PhysicsJoint.h"
|
|
#include "hpl1/engine/physics/PhysicsJointBall.h"
|
|
#include "hpl1/engine/physics/PhysicsJointHinge.h"
|
|
#include "hpl1/engine/physics/PhysicsJointScrew.h"
|
|
#include "hpl1/engine/physics/PhysicsJointSlider.h"
|
|
#include "hpl1/engine/physics/PhysicsWorld.h"
|
|
|
|
#include "hpl1/engine/math/Math.h"
|
|
|
|
#include "hpl1/engine/resources/AnimationManager.h"
|
|
#include "hpl1/engine/resources/FileSearcher.h"
|
|
#include "hpl1/engine/resources/MaterialManager.h"
|
|
#include "hpl1/engine/resources/MeshLoaderHandler.h"
|
|
#include "hpl1/engine/resources/MeshManager.h"
|
|
|
|
#include "hpl1/engine/graphics/Animation.h"
|
|
#include "hpl1/engine/graphics/Mesh.h"
|
|
#include "hpl1/engine/graphics/SubMesh.h"
|
|
#include "hpl1/engine/scene/AnimationState.h"
|
|
#include "hpl1/engine/scene/MeshEntity.h"
|
|
#include "hpl1/engine/scene/Node3D.h"
|
|
|
|
#include "hpl1/engine/scene/Light3DSpot.h"
|
|
|
|
namespace hpl {
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// CONSTRUCTORS
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// PUBLIC METHODS
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
iEntity3D *cEntityLoader_Object::Load(const tString &asName, TiXmlElement *apRootElem, const cMatrixf &a_mtxTransform,
|
|
cWorld3D *apWorld, const tString &asFileName, bool abLoadReference) {
|
|
////////////////////////////////////////
|
|
// Init
|
|
mvBodies.clear();
|
|
mvJoints.clear();
|
|
|
|
mvParticleSystems.clear();
|
|
mvBillboards.clear();
|
|
mvSoundEntities.clear();
|
|
mvLights.clear();
|
|
mvBeams.clear();
|
|
|
|
mpEntity = NULL;
|
|
mpMesh = NULL;
|
|
|
|
msFileName = asFileName;
|
|
|
|
cMatrixf transformMatrix = a_mtxTransform;
|
|
if (asFileName == "boat_dynamicparaffin.ent") {
|
|
// WORKAROUND: the light is placed inside another mesh, which results in janky movement
|
|
// which wasn't present in the original game. The following just shifts the light down a bit.
|
|
// The value was chosen by trial and error.
|
|
transformMatrix.m[1][3] -= 0.07f;
|
|
}
|
|
|
|
////////////////////////////////////////
|
|
// Load MAIN
|
|
|
|
TiXmlElement *pMainElem = apRootElem->FirstChildElement("MAIN");
|
|
if (pMainElem == NULL) {
|
|
Error("Couldn't load element MAIN");
|
|
return NULL;
|
|
}
|
|
|
|
msName = cString::ToString(pMainElem->Attribute("Name"), "");
|
|
msSubType = cString::ToString(pMainElem->Attribute("Subtype"), "");
|
|
|
|
// Before load virtual call.
|
|
BeforeLoad(apRootElem, transformMatrix, apWorld);
|
|
|
|
////////////////////////////////////////
|
|
// Load GRAPHICS
|
|
TiXmlElement *pGfxElem = apRootElem->FirstChildElement("GRAPHICS");
|
|
if (pGfxElem == NULL) {
|
|
Error("Couldn't load element GRAPHICS");
|
|
return NULL;
|
|
}
|
|
|
|
// load XML properties
|
|
tString sMeshFile = cString::ToString(pGfxElem->Attribute("ModelFile"), "");
|
|
bool bShadows = cString::ToBool(pGfxElem->Attribute("CastShadows"), true);
|
|
|
|
bool bAnimationLoop = cString::ToBool(pGfxElem->Attribute("AnimationLoop"), true);
|
|
bool bAnimationRandStart = cString::ToBool(pGfxElem->Attribute("AnimationRandStart"), true);
|
|
|
|
mpMesh = apWorld->GetResources()->GetMeshManager()->CreateMesh(sMeshFile);
|
|
if (mpMesh == NULL)
|
|
return NULL;
|
|
|
|
////////////////////////////////////////
|
|
// CREATE ENTITY
|
|
|
|
mpEntity = apWorld->CreateMeshEntity(asName, mpMesh);
|
|
|
|
// Set entity properties
|
|
mpEntity->SetCastsShadows(bShadows);
|
|
|
|
////////////////////////////////////////
|
|
// Load SUBMESHES
|
|
TiXmlElement *pSubMeshElem = apRootElem->FirstChildElement("SUBMESH");
|
|
for (; pSubMeshElem != NULL; pSubMeshElem = pSubMeshElem->NextSiblingElement("SUBMESH")) {
|
|
tString sName = cString::ToString(pSubMeshElem->Attribute("Name"), "");
|
|
tString sMaterialFile = cString::ToString(pSubMeshElem->Attribute("MaterialFile"), "");
|
|
|
|
cSubMeshEntity *pSubEntity = mpEntity->GetSubMeshEntityName(sName);
|
|
if (pSubEntity == NULL) {
|
|
Warning("Sub mesh '%s' does not exist in mesh '%s'!\n", sName.c_str(),
|
|
mpMesh->GetName().c_str());
|
|
continue;
|
|
}
|
|
|
|
if (sMaterialFile != "") {
|
|
iMaterial *pMaterial = apWorld->GetResources()->GetMaterialManager()->CreateMaterial(sMaterialFile);
|
|
if (pMaterial) {
|
|
pSubEntity->SetCustomMaterial(pMaterial);
|
|
}
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////
|
|
// Load ANIMATIONS
|
|
TiXmlElement *pAnimRootElem = apRootElem->FirstChildElement("ANIMATIONS");
|
|
if (pAnimRootElem) {
|
|
mpEntity->ClearAnimations();
|
|
|
|
TiXmlElement *pAnimElem = pAnimRootElem->FirstChildElement("Animation");
|
|
for (; pAnimElem != NULL; pAnimElem = pAnimElem->NextSiblingElement("Animation")) {
|
|
tString sFile = cString::ToString(pAnimElem->Attribute("File"), "");
|
|
tString sName = cString::ToString(pAnimElem->Attribute("Name"), "Unknown");
|
|
float fSpeed = cString::ToFloat(pAnimElem->Attribute("Speed"), 1.0f);
|
|
float fSpecialEventTime = cString::ToFloat(pAnimElem->Attribute("SpecialEventTime"), 0.0f);
|
|
|
|
cAnimationManager *pAnimManager = apWorld->GetResources()->GetAnimationManager();
|
|
|
|
cAnimation *pAnimation = pAnimManager->CreateAnimation(sFile);
|
|
|
|
if (pAnimation) {
|
|
cAnimationState *pState = mpEntity->AddAnimation(pAnimation, sName, fSpeed);
|
|
pState->SetSpecialEventTime(fSpecialEventTime);
|
|
|
|
////////////////////////////////
|
|
// Get Events
|
|
TiXmlElement *pAnimEventElem = pAnimElem->FirstChildElement("Event");
|
|
for (; pAnimEventElem != NULL; pAnimEventElem = pAnimEventElem->NextSiblingElement("Event")) {
|
|
cAnimationEvent *pEvent = pState->CreateEvent();
|
|
|
|
pEvent->mfTime = cString::ToFloat(pAnimEventElem->Attribute("Time"), 1.0f);
|
|
pEvent->mType = GetAnimationEventType(pAnimEventElem->Attribute("Type"));
|
|
pEvent->msValue = cString::ToString(pAnimEventElem->Attribute("Value"), "");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////
|
|
// Load PHYSICS
|
|
bool bUsingNodeBodies = false;
|
|
|
|
//////////////////////////////////
|
|
// Properties for entities with joints
|
|
if ((mpMesh->GetPhysicsJointNum() > 0 || mpMesh->HasSeveralBodies()) &&
|
|
(mpMesh->GetAnimationNum() <= 0 || mpMesh->GetSkeleton())) {
|
|
mpMesh->CreateJointsAndBodies(&mvBodies, mpEntity, &mvJoints, transformMatrix, apWorld->GetPhysicsWorld());
|
|
|
|
////////////////////////////////////
|
|
// Properties for bodies
|
|
TiXmlElement *pPhysicsElem = apRootElem->FirstChildElement("PHYSICS");
|
|
for (; pPhysicsElem != NULL; pPhysicsElem = pPhysicsElem->NextSiblingElement("PHYSICS")) {
|
|
iPhysicsBody *pBody = NULL;
|
|
|
|
// load XML properties
|
|
tString sSubName = asName + "_" + cString::ToString(pPhysicsElem->Attribute("SubName"), "");
|
|
|
|
tString sMaterial = cString::ToString(pPhysicsElem->Attribute("Material"), "Default");
|
|
|
|
bool bStaticMeshCollider = cString::ToBool(pPhysicsElem->Attribute("StaticMeshCollider"), false);
|
|
tString sColliderMesh = cString::ToString(pPhysicsElem->Attribute("ColliderMesh"), "");
|
|
|
|
if (bStaticMeshCollider) {
|
|
cSubMesh *pSubMesh = mpMesh->GetSubMeshName(sColliderMesh);
|
|
if (pSubMesh) {
|
|
iCollideShape *pShape = apWorld->GetPhysicsWorld()->CreateMeshShape(
|
|
pSubMesh->GetVertexBuffer());
|
|
pBody = apWorld->GetPhysicsWorld()->CreateBody(sColliderMesh, pShape);
|
|
} else {
|
|
Error("Couldn't find sub mesh '%s' to create collision mesh\n", sColliderMesh.c_str());
|
|
continue;
|
|
}
|
|
|
|
cSubMeshEntity *pSubEntity = mpEntity->GetSubMeshEntityName(sColliderMesh);
|
|
|
|
pBody->CreateNode()->AddEntity(pSubEntity);
|
|
pBody->SetMass(0);
|
|
pBody->SetMatrix(transformMatrix);
|
|
|
|
pSubEntity->SetBody(pBody);
|
|
|
|
mvBodies.push_back(pBody);
|
|
} else {
|
|
pBody = (iPhysicsBody *)STLFindByName(mvBodies, sSubName);
|
|
if (pBody == NULL) {
|
|
Error("Cannot find sub entity '%s' in mesh '%s'\n", sSubName.c_str(), mpMesh->GetName().c_str());
|
|
continue;
|
|
}
|
|
}
|
|
|
|
SetBodyProperties(pBody, pPhysicsElem);
|
|
|
|
iPhysicsMaterial *pMaterial = apWorld->GetPhysicsWorld()->GetMaterialFromName(sMaterial);
|
|
if (pMaterial) {
|
|
pBody->SetMaterial(pMaterial);
|
|
} else {
|
|
Error("Physics Material '%s' for body '%s' in mesh '%s' doesn't exist!\n",
|
|
sMaterial.c_str(), pBody->GetName().c_str(), mpMesh->GetName().c_str());
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////
|
|
// Properties for joints
|
|
TiXmlElement *pJointElem = apRootElem->FirstChildElement("JOINT");
|
|
for (; pJointElem != NULL; pJointElem = pJointElem->NextSiblingElement("JOINT")) {
|
|
tString sName = asName + "_" + cString::ToString(pJointElem->Attribute("Name"), "");
|
|
|
|
iPhysicsJoint *pJoint = (iPhysicsJoint *)STLFindByName(mvJoints, sName);
|
|
if (pJoint) {
|
|
SetJointProperties(pJoint, pJointElem, apWorld);
|
|
} else {
|
|
Error("Joint '%s' not found in mesh '%s'\n", sName.c_str(), mpMesh->GetName().c_str());
|
|
}
|
|
}
|
|
|
|
// for(size_t i=0; i< vJoints.size(); i++)
|
|
//{
|
|
// Log("Created joint: %s\n",vJoints[i]->GetName().c_str());
|
|
// }
|
|
}
|
|
//////////////////////////////////
|
|
// Properties for entities nodes and animation
|
|
else if (mpMesh->GetNodeNum() > 0) {
|
|
// Log("nodes and anim\n");
|
|
iPhysicsBody *pRootBody = NULL;
|
|
|
|
bUsingNodeBodies = true;
|
|
|
|
mpMesh->CreateNodeBodies(&pRootBody, &mvBodies, mpEntity, apWorld->GetPhysicsWorld(),
|
|
transformMatrix);
|
|
|
|
TiXmlElement *pPhysicsElem = apRootElem->FirstChildElement("PHYSICS");
|
|
for (; pPhysicsElem != NULL; pPhysicsElem = pPhysicsElem->NextSiblingElement("PHYSICS")) {
|
|
iPhysicsBody *pBody = NULL;
|
|
|
|
// load XML properties
|
|
tString sSubName = cString::ToString(pPhysicsElem->Attribute("SubName"), "");
|
|
if (sSubName != "")
|
|
sSubName = asName + "_" + sSubName;
|
|
else
|
|
sSubName = asName;
|
|
|
|
tString sMaterial = cString::ToString(pPhysicsElem->Attribute("Material"), "Default");
|
|
|
|
bool bStaticMeshCollider = cString::ToBool(pPhysicsElem->Attribute("StaticMeshCollider"), false);
|
|
tString sColliderMesh = cString::ToString(pPhysicsElem->Attribute("ColliderMesh"), "");
|
|
|
|
if (bStaticMeshCollider) {
|
|
cSubMesh *pSubMesh = mpMesh->GetSubMeshName(sColliderMesh);
|
|
if (pSubMesh) {
|
|
iCollideShape *pShape = apWorld->GetPhysicsWorld()->CreateMeshShape(
|
|
pSubMesh->GetVertexBuffer());
|
|
pBody = apWorld->GetPhysicsWorld()->CreateBody(sColliderMesh, pShape);
|
|
} else {
|
|
Error("Couldn't find sub mesh '%s' to create collision mesh\n", sColliderMesh.c_str());
|
|
continue;
|
|
}
|
|
} else {
|
|
pBody = (iPhysicsBody *)STLFindByName(mvBodies, sSubName);
|
|
if (pBody == NULL) {
|
|
Error("Cannot find sub entity '%s' in mesh '%s'\n", sSubName.c_str(), mpMesh->GetName().c_str());
|
|
continue;
|
|
}
|
|
}
|
|
|
|
SetBodyProperties(pBody, pPhysicsElem);
|
|
|
|
// pBody->SetMass(0);
|
|
// pBody->SetGravity(false);
|
|
|
|
iPhysicsMaterial *pMaterial = apWorld->GetPhysicsWorld()->GetMaterialFromName(sMaterial);
|
|
if (pMaterial) {
|
|
pBody->SetMaterial(pMaterial);
|
|
} else {
|
|
Error("Physics Material '%s' for body '%s' in mesh '%s' doesn't exist!\n",
|
|
sMaterial.c_str(), pBody->GetName().c_str(), mpMesh->GetName().c_str());
|
|
}
|
|
}
|
|
|
|
// Only allow for one joint
|
|
if (mpMesh->GetPhysicsJointNum() == 1 && mvBodies.size() == 1) {
|
|
iPhysicsJoint *pJoint = mpMesh->CreateJointInWorld(asName, mpMesh->GetPhysicsJoint(0),
|
|
NULL, mvBodies[0], transformMatrix,
|
|
apWorld->GetPhysicsWorld());
|
|
if (pJoint) {
|
|
TiXmlElement *pJointElem = apRootElem->FirstChildElement("JOINT");
|
|
if (pJointElem) {
|
|
SetJointProperties(pJoint, pJointElem, apWorld);
|
|
}
|
|
}
|
|
} else if (mpMesh->GetPhysicsJointNum() > 1) {
|
|
Error("Over 1 joints in '%s'!Animated body meshes only allow for one joint!\n",
|
|
mpMesh->GetName().c_str());
|
|
}
|
|
|
|
}
|
|
//////////////////////////////////
|
|
// Properties for other entities
|
|
else {
|
|
TiXmlElement *pPhysicsElem = apRootElem->FirstChildElement("PHYSICS");
|
|
if (pPhysicsElem) {
|
|
iPhysicsBody *pBody = NULL;
|
|
|
|
// load XML properties
|
|
tString sSubName = cString::ToString(pPhysicsElem->Attribute("SubName"), "");
|
|
|
|
bool bCollides = cString::ToBool(pPhysicsElem->Attribute("Collides"), false);
|
|
bool bHasPhysics = cString::ToBool(pPhysicsElem->Attribute("HasPhysics"), false);
|
|
tString sMaterial = cString::ToString(pPhysicsElem->Attribute("Material"), "Default");
|
|
|
|
bool bStaticMeshCollider = cString::ToBool(pPhysicsElem->Attribute("StaticMeshCollider"), false);
|
|
tString sColliderMesh = cString::ToString(pPhysicsElem->Attribute("ColliderMesh"), "");
|
|
|
|
// Check if this entity should have a body.
|
|
if (bCollides) {
|
|
if (bStaticMeshCollider) {
|
|
cSubMesh *pSubMesh = mpMesh->GetSubMeshName(sColliderMesh);
|
|
if (pSubMesh) {
|
|
iCollideShape *pShape = apWorld->GetPhysicsWorld()->CreateMeshShape(
|
|
pSubMesh->GetVertexBuffer());
|
|
pBody = apWorld->GetPhysicsWorld()->CreateBody(sColliderMesh, pShape);
|
|
} else {
|
|
Error("Couldn't find sub mesh '%s' to create collision mesh\n", sColliderMesh.c_str());
|
|
}
|
|
} else {
|
|
if (mpMesh->GetColliderNum() > 0) {
|
|
pBody = apWorld->GetPhysicsWorld()->CreateBody(asName,
|
|
mpMesh->CreateCollideShape(apWorld->GetPhysicsWorld()));
|
|
} else {
|
|
Warning("No collider found for '%s'\n", asFileName.c_str());
|
|
}
|
|
}
|
|
|
|
if (pBody) {
|
|
iPhysicsMaterial *pMaterial = apWorld->GetPhysicsWorld()->GetMaterialFromName(sMaterial);
|
|
if (pMaterial) {
|
|
pBody->SetMaterial(pMaterial);
|
|
} else {
|
|
Error("Physics Material '%s' for body '%s' in mesh '%s' doesn't exist!\n",
|
|
sMaterial.c_str(), pBody->GetName().c_str(), mpMesh->GetName().c_str());
|
|
}
|
|
|
|
SetBodyProperties(pBody, pPhysicsElem);
|
|
|
|
if (bHasPhysics) {
|
|
} else {
|
|
pBody->SetMass(0);
|
|
}
|
|
|
|
pBody->CreateNode()->AddEntity(mpEntity);
|
|
// cMatrixf mtxTemp = transformMatrix;
|
|
// Log("-- Body: %s Mtx: %s\n", pBody->GetName().c_str(),
|
|
// mtxTemp.ToString().c_str());
|
|
// pBody->GetBV()->GetSize().ToString().c_str());
|
|
|
|
pBody->SetMatrix(transformMatrix);
|
|
|
|
mpEntity->SetBody(pBody);
|
|
|
|
mvBodies.push_back(pBody);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////
|
|
// Create lights
|
|
for (int i = 0; i < mpMesh->GetLightNum(); i++) {
|
|
iLight3D *pLight = mpMesh->CreateLightInWorld(asName, mpMesh->GetLight(i), mpEntity, apWorld);
|
|
if (pLight)
|
|
mvLights.push_back(pLight);
|
|
}
|
|
|
|
// Iterate light elements
|
|
TiXmlElement *pLightElem = apRootElem->FirstChildElement("LIGHT");
|
|
for (; pLightElem != NULL; pLightElem = pLightElem->NextSiblingElement("LIGHT")) {
|
|
tString sName = cString::ToString(pLightElem->Attribute("Name"), "");
|
|
|
|
iLight3D *pLight = (iLight3D *)STLFindByName(mvLights, asName + "_" + sName);
|
|
if (pLight == NULL) {
|
|
Error("Couldn't find light %s among entity %s type: %s lights\n", sName.c_str(), asName.c_str(), asFileName.c_str());
|
|
continue;
|
|
}
|
|
|
|
pLight->SetFarAttenuation(cString::ToFloat(pLightElem->Attribute("Attenuation"),
|
|
pLight->GetFarAttenuation()));
|
|
pLight->SetDiffuseColor(cString::ToColor(pLightElem->Attribute("Color"),
|
|
pLight->GetDiffuseColor()));
|
|
pLight->SetCastShadows(cString::ToBool(pLightElem->Attribute("CastShadows"),
|
|
pLight->GetCastShadows()));
|
|
|
|
if (pLight->GetLightType() == eLight3DType_Spot) {
|
|
cLight3DSpot *pSpotLight = static_cast<cLight3DSpot *>(pLight);
|
|
|
|
pSpotLight->SetFOV(cString::ToFloat(pLightElem->Attribute("FOV"),
|
|
pSpotLight->GetFOV()));
|
|
pSpotLight->SetAspect(cString::ToFloat(pLightElem->Attribute("Aspect"),
|
|
pSpotLight->GetAspect()));
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////
|
|
// Create billboards
|
|
for (int i = 0; i < mpMesh->GetBillboardNum(); i++) {
|
|
cBillboard *pBill = mpMesh->CreateBillboardInWorld(asName, mpMesh->GetBillboard(i), mpEntity, apWorld);
|
|
if (pBill)
|
|
mvBillboards.push_back(pBill);
|
|
}
|
|
|
|
////////////////////////////////////////
|
|
// Create beams
|
|
for (int i = 0; i < mpMesh->GetBeamNum(); i++) {
|
|
cBeam *pBeam = mpMesh->CreateBeamInWorld(asName, mpMesh->GetBeam(i), mpEntity, apWorld);
|
|
if (pBeam)
|
|
mvBeams.push_back(pBeam);
|
|
}
|
|
|
|
////////////////////////////////////////
|
|
// Create particle systems
|
|
for (int i = 0; i < mpMesh->GetParticleSystemNum(); i++) {
|
|
cParticleSystem3D *pPS = mpMesh->CreateParticleSystemInWorld(asName, mpMesh->GetParticleSystem(i), mpEntity, apWorld);
|
|
if (pPS)
|
|
mvParticleSystems.push_back(pPS);
|
|
}
|
|
|
|
////////////////////////////////////////
|
|
// Create sound entities
|
|
for (int i = 0; i < mpMesh->GetSoundEntityNum(); i++) {
|
|
cSoundEntity *pSound = mpMesh->CreateSoundEntityInWorld(asName, mpMesh->GetSoundEntity(i), mpEntity, apWorld);
|
|
if (pSound)
|
|
mvSoundEntities.push_back(pSound);
|
|
}
|
|
|
|
////////////////////////////////////////
|
|
// ATTACH BILLBOARDS
|
|
TiXmlElement *pAttachElem = apRootElem->FirstChildElement("ATTACH_BILLBOARDS");
|
|
if (pAttachElem != NULL) {
|
|
TiXmlElement *pPairElem = pAttachElem->FirstChildElement();
|
|
for (; pPairElem != NULL; pPairElem = pPairElem->NextSiblingElement()) {
|
|
tString sLight = asName + "_" + cString::ToString(pPairElem->Attribute("Light"), "");
|
|
tString sBillboard = asName + "_" + cString::ToString(pPairElem->Attribute("Billboard"), "");
|
|
|
|
iLight3D *pLight = apWorld->GetLight(sLight);
|
|
if (pLight == NULL) {
|
|
Warning("Couldn't find light '%s'\n", sLight.c_str());
|
|
continue;
|
|
}
|
|
|
|
cBillboard *pBillboard = apWorld->GetBillboard(sBillboard);
|
|
if (pBillboard == NULL) {
|
|
Warning("Couldn't find billboard '%s'\n", sBillboard.c_str());
|
|
continue;
|
|
}
|
|
pLight->AttachBillboard(pBillboard);
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////
|
|
// Set matrix on entity if there are no bodies.
|
|
if ((mvBodies.size() <= 0 || bUsingNodeBodies) && mpEntity->GetBody() == NULL) {
|
|
mpEntity->SetMatrix(transformMatrix);
|
|
|
|
// to make sure everything is in place.
|
|
mpEntity->UpdateLogic(0);
|
|
}
|
|
|
|
// Play animation if there is any.
|
|
if (mpEntity->GetAnimationStateNum() > 0) {
|
|
cAnimationState *pAnimState = mpEntity->GetAnimationState(0);
|
|
|
|
pAnimState->SetActive(true);
|
|
pAnimState->SetLoop(bAnimationLoop);
|
|
if (bAnimationRandStart)
|
|
pAnimState->SetTimePosition(cMath::RandRectf(0, pAnimState->GetLength()));
|
|
else
|
|
pAnimState->SetTimePosition(0);
|
|
} else if (mpMesh->GetSkeleton()) {
|
|
mpEntity->SetSkeletonPhysicsActive(true);
|
|
}
|
|
|
|
// After load virtual call.
|
|
// This is where the user adds extra stuff.
|
|
AfterLoad(apRootElem, transformMatrix, apWorld);
|
|
|
|
////////////////////////////////////////
|
|
// Create references
|
|
if (abLoadReference) {
|
|
cMeshEntity *pEntityCopy = mpEntity;
|
|
cMesh *pMeshCopy = mpMesh;
|
|
for (int i = 0; i < pMeshCopy->GetReferenceNum(); i++) {
|
|
/*iEntity3D *pRef = */
|
|
mpMesh->CreateReferenceInWorld(asName, pMeshCopy->GetReference(i), pEntityCopy,
|
|
apWorld, transformMatrix);
|
|
// if(pPS) mvParticleSystems.push_back(pPS);
|
|
}
|
|
return pEntityCopy;
|
|
} else {
|
|
return mpEntity;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void cEntityLoader_Object::SetBodyProperties(iPhysicsBody *apBody, TiXmlElement *apPhysicsElem) {
|
|
float fMass = cString::ToFloat(apPhysicsElem->Attribute("Mass"), 1);
|
|
tString sInertiaVec = cString::ToString(apPhysicsElem->Attribute("InertiaScale"), "1 1 1");
|
|
|
|
float fAngluarDamping = cString::ToFloat(apPhysicsElem->Attribute("AngularDamping"), 0.1f);
|
|
float fLinearDamping = cString::ToFloat(apPhysicsElem->Attribute("LinearDamping"), 0.1f);
|
|
|
|
bool bBlocksSound = cString::ToBool(apPhysicsElem->Attribute("BlocksSound"), false);
|
|
bool bCollideCharacter = cString::ToBool(apPhysicsElem->Attribute("CollideCharacter"), true);
|
|
bool bCollide = cString::ToBool(apPhysicsElem->Attribute("CollideNonCharacter"), true);
|
|
|
|
bool bHasGravity = cString::ToBool(apPhysicsElem->Attribute("HasGravity"), true);
|
|
|
|
float fMaxAngluarSpeed = cString::ToFloat(apPhysicsElem->Attribute("MaxAngluarSpeed"), 0);
|
|
float fMaxLinearSpeed = cString::ToFloat(apPhysicsElem->Attribute("MaxLinearSpeed"), 0);
|
|
|
|
bool bContinuousCollision = cString::ToBool(apPhysicsElem->Attribute("ContinuousCollision"), true);
|
|
|
|
bool bPushedByCharacterGravity = cString::ToBool(apPhysicsElem->Attribute("PushedByCharacterGravity"), false);
|
|
#if 0
|
|
float fAutoDisableLinearThreshold = cString::ToFloat(apPhysicsElem->Attribute("AutoDisableLinearThreshold"), 0.1f);
|
|
float fAutoDisableAngularThreshold = cString::ToFloat(apPhysicsElem->Attribute("AutoDisableAngularThreshold"), 0.1f);
|
|
int lAutoDisableNumSteps = cString::ToInt(apPhysicsElem->Attribute("AutoDisableNumSteps"), 10);
|
|
#endif
|
|
bool bVolatile = cString::ToBool(apPhysicsElem->Attribute("Volatile"), false);
|
|
|
|
bool bCanAttachCharacter = cString::ToBool(apPhysicsElem->Attribute("CanAttachCharacter"), false);
|
|
|
|
tFloatVec vInertiaScale;
|
|
cString::GetFloatVec(sInertiaVec, vInertiaScale);
|
|
|
|
apBody->SetMass(fMass);
|
|
apBody->SetAngularDamping(fAngluarDamping);
|
|
apBody->SetLinearDamping(fLinearDamping);
|
|
apBody->SetBlocksSound(bBlocksSound);
|
|
apBody->SetCollideCharacter(bCollideCharacter);
|
|
apBody->SetCollide(bCollide);
|
|
apBody->SetGravity(bHasGravity);
|
|
|
|
apBody->SetVolatile(bVolatile);
|
|
|
|
apBody->SetCanAttachCharacter(bCanAttachCharacter);
|
|
|
|
apBody->SetContinuousCollision(bContinuousCollision);
|
|
#if 0
|
|
apBody->SetAutoDisableLinearThreshold(fAutoDisableLinearThreshold);
|
|
apBody->SetAutoDisableAngularThreshold(fAutoDisableAngularThreshold);
|
|
apBody->SetAutoDisableNumSteps(lAutoDisableNumSteps);
|
|
#endif
|
|
apBody->SetPushedByCharacterGravity(bPushedByCharacterGravity);
|
|
|
|
// Log("Body %s contin: %d\n",apBody->GetName().c_str(), apBody->GetContinuousCollision()?1:0);
|
|
|
|
apBody->SetMaxAngularSpeed(fMaxAngluarSpeed);
|
|
apBody->SetMaxLinearSpeed(fMaxLinearSpeed);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void cEntityLoader_Object::SetJointProperties(iPhysicsJoint *pJoint, TiXmlElement *pJointElem,
|
|
cWorld3D *apWorld) {
|
|
float fMinValue = cString::ToFloat(pJointElem->Attribute("MinValue"), -1);
|
|
float fMaxValue = cString::ToFloat(pJointElem->Attribute("MaxValue"), -1);
|
|
|
|
tString sMoveSound = cString::ToString(pJointElem->Attribute("MoveSound"), "");
|
|
float fMinMoveSpeed = cString::ToFloat(pJointElem->Attribute("MinMoveSpeed"), 0.5f);
|
|
float fMinMoveFreq = cString::ToFloat(pJointElem->Attribute("MinMoveFreq"), 0.9f);
|
|
float fMinMoveVolume = cString::ToFloat(pJointElem->Attribute("MinMoveVolume"), 0.3f);
|
|
float fMinMoveFreqSpeed = cString::ToFloat(pJointElem->Attribute("MinMoveFreqSpeed"), 0.9f);
|
|
float fMaxMoveFreq = cString::ToFloat(pJointElem->Attribute("MaxMoveFreq"), 1.1f);
|
|
float fMaxMoveVolume = cString::ToFloat(pJointElem->Attribute("MaxMoveVolume"), 1.0f);
|
|
float fMaxMoveFreqSpeed = cString::ToFloat(pJointElem->Attribute("MaxMoveFreqSpeed"), 1.1f);
|
|
float fMiddleMoveSpeed = cString::ToFloat(pJointElem->Attribute("MiddleMoveSpeed"), 1.0f);
|
|
float fMiddleMoveVolume = cString::ToFloat(pJointElem->Attribute("MiddleMoveVolume"), 1.0f);
|
|
tString sMoveType = cString::ToString(pJointElem->Attribute("MoveType"), "Linear");
|
|
sMoveType = cString::ToLowerCase(sMoveType);
|
|
|
|
float fStickyMinDistance = cString::ToFloat(pJointElem->Attribute("StickyMinDistance"), 0.0f);
|
|
float fStickyMaxDistance = cString::ToFloat(pJointElem->Attribute("StickyMaxDistance"), 0.0f);
|
|
|
|
bool bBreakable = cString::ToBool(pJointElem->Attribute("Breakable"), false);
|
|
tString sBreakSound = cString::ToString(pJointElem->Attribute("BreakSound"), "");
|
|
float fBreakForce = cString::ToFloat(pJointElem->Attribute("BreakForce"), 1000);
|
|
|
|
bool bLimitAutoSleep = cString::ToBool(pJointElem->Attribute("LimitAutoSleep"), false);
|
|
float fLimitAutoSleepDist = cString::ToFloat(pJointElem->Attribute("LimitAutoSleepDist"), 0.02f);
|
|
int lLimitAutoSleepNumSteps = cString::ToInt(pJointElem->Attribute("LimitAutoSleepNumSteps"), 10);
|
|
|
|
pJoint->SetMoveSound(sMoveSound);
|
|
pJoint->SetMinMoveSpeed(fMinMoveSpeed);
|
|
pJoint->SetMinMoveFreq(fMinMoveFreq);
|
|
pJoint->SetMinMoveVolume(fMinMoveVolume);
|
|
pJoint->SetMinMoveFreqSpeed(fMinMoveFreqSpeed);
|
|
pJoint->SetMaxMoveFreq(fMaxMoveFreq);
|
|
pJoint->SetMaxMoveVolume(fMaxMoveVolume);
|
|
pJoint->SetMaxMoveFreqSpeed(fMaxMoveFreqSpeed);
|
|
pJoint->SetMiddleMoveSpeed(fMiddleMoveSpeed);
|
|
pJoint->SetMiddleMoveVolume(fMiddleMoveVolume);
|
|
pJoint->SetMoveSpeedType(sMoveType == "angular" ? ePhysicsJointSpeed_Angular : ePhysicsJointSpeed_Linear);
|
|
|
|
pJoint->GetMaxLimit()->msSound = cString::ToString(pJointElem->Attribute("MaxLimit_Sound"), "");
|
|
pJoint->GetMaxLimit()->mfMaxSpeed = cString::ToFloat(pJointElem->Attribute("MaxLimit_MaxSpeed"), 10.0f);
|
|
pJoint->GetMaxLimit()->mfMinSpeed = cString::ToFloat(pJointElem->Attribute("MaxLimit_MinSpeed"), 20.0f);
|
|
|
|
pJoint->GetMinLimit()->msSound = cString::ToString(pJointElem->Attribute("MinLimit_Sound"), "");
|
|
pJoint->GetMinLimit()->mfMaxSpeed = cString::ToFloat(pJointElem->Attribute("MinLimit_MaxSpeed"), 10.0f);
|
|
pJoint->GetMinLimit()->mfMinSpeed = cString::ToFloat(pJointElem->Attribute("MinLimit_MinSpeed"), 20.0f);
|
|
|
|
pJoint->SetStickyMinDistance(fStickyMinDistance);
|
|
pJoint->SetStickyMaxDistance(fStickyMaxDistance);
|
|
|
|
pJoint->SetBreakable(bBreakable);
|
|
pJoint->SetBreakForce(fBreakForce);
|
|
pJoint->SetBreakSound(sBreakSound);
|
|
|
|
pJoint->SetLimitAutoSleep(bLimitAutoSleep);
|
|
pJoint->SetLimitAutoSleepDist(fLimitAutoSleepDist);
|
|
pJoint->SetLimitAutoSleepNumSteps(lLimitAutoSleepNumSteps);
|
|
|
|
// Set min/max values
|
|
if (fMaxValue >= 0 && fMinValue >= 0) {
|
|
switch (pJoint->GetType()) {
|
|
case ePhysicsJointType_Ball: {
|
|
iPhysicsJointBall *pBallJoint = static_cast<iPhysicsJointBall *>(pJoint);
|
|
pBallJoint->SetConeLimits(pBallJoint->GetPinDir(), cMath::ToRad(fMinValue), cMath::ToRad(fMaxValue));
|
|
break;
|
|
}
|
|
case ePhysicsJointType_Hinge: {
|
|
iPhysicsJointHinge *pHingeJoint = static_cast<iPhysicsJointHinge *>(pJoint);
|
|
pHingeJoint->SetMaxAngle(cMath::ToRad(fMaxValue));
|
|
pHingeJoint->SetMinAngle(cMath::ToRad(-fMinValue));
|
|
break;
|
|
}
|
|
case ePhysicsJointType_Screw: {
|
|
iPhysicsJointScrew *pScrewJoint = static_cast<iPhysicsJointScrew *>(pJoint);
|
|
pScrewJoint->SetMinDistance(-fMinValue / 100);
|
|
pScrewJoint->SetMaxDistance(fMaxValue / 100);
|
|
break;
|
|
}
|
|
case ePhysicsJointType_Slider: {
|
|
iPhysicsJointSlider *pSliderJoint = static_cast<iPhysicsJointSlider *>(pJoint);
|
|
pSliderJoint->SetMinDistance(-fMinValue / 100);
|
|
pSliderJoint->SetMaxDistance(fMaxValue / 100);
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Load all controllers
|
|
TiXmlElement *pControllerElem = pJointElem->FirstChildElement("Controller");
|
|
for (; pControllerElem != NULL; pControllerElem = pControllerElem->NextSiblingElement("Controller")) {
|
|
LoadController(pJoint, apWorld->GetPhysicsWorld(), pControllerElem);
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
ePhysicsControllerType GetControllerType(const char *apString) {
|
|
if (apString == NULL)
|
|
return ePhysicsControllerType_LastEnum;
|
|
|
|
tString sName = apString;
|
|
|
|
if (sName == "Pid")
|
|
return ePhysicsControllerType_Pid;
|
|
else if (sName == "Spring")
|
|
return ePhysicsControllerType_Spring;
|
|
|
|
return ePhysicsControllerType_LastEnum;
|
|
}
|
|
|
|
/////////////////////////
|
|
|
|
static ePhysicsControllerInput GetControllerInput(const char *apString) {
|
|
if (apString == NULL)
|
|
return ePhysicsControllerInput_LastEnum;
|
|
|
|
tString sName = apString;
|
|
|
|
if (sName == "JointAngle")
|
|
return ePhysicsControllerInput_JointAngle;
|
|
else if (sName == "JointDist")
|
|
return ePhysicsControllerInput_JointDist;
|
|
else if (sName == "LinearSpeed")
|
|
return ePhysicsControllerInput_LinearSpeed;
|
|
else if (sName == "AngularSpeed")
|
|
return ePhysicsControllerInput_AngularSpeed;
|
|
|
|
return ePhysicsControllerInput_LastEnum;
|
|
}
|
|
|
|
/////////////////////////
|
|
|
|
static ePhysicsControllerOutput GetControllerOutput(const char *apString) {
|
|
if (apString == NULL)
|
|
return ePhysicsControllerOutput_LastEnum;
|
|
|
|
tString sName = apString;
|
|
|
|
if (sName == "Force")
|
|
return ePhysicsControllerOutput_Force;
|
|
else if (sName == "Torque")
|
|
return ePhysicsControllerOutput_Torque;
|
|
|
|
return ePhysicsControllerOutput_LastEnum;
|
|
}
|
|
|
|
/////////////////////////
|
|
|
|
static ePhysicsControllerAxis GetControllerAxis(const char *apString) {
|
|
if (apString == NULL)
|
|
return ePhysicsControllerAxis_LastEnum;
|
|
|
|
tString sName = apString;
|
|
|
|
if (sName == "X")
|
|
return ePhysicsControllerAxis_X;
|
|
else if (sName == "Y")
|
|
return ePhysicsControllerAxis_Y;
|
|
else if (sName == "Z")
|
|
return ePhysicsControllerAxis_Z;
|
|
|
|
return ePhysicsControllerAxis_LastEnum;
|
|
}
|
|
|
|
/////////////////////////
|
|
|
|
static ePhysicsControllerEnd GetControllerEnd(const char *apString) {
|
|
if (apString == NULL)
|
|
return ePhysicsControllerEnd_Null;
|
|
|
|
tString sName = apString;
|
|
|
|
if (sName == "OnMax")
|
|
return ePhysicsControllerEnd_OnMax;
|
|
else if (sName == "OnMin")
|
|
return ePhysicsControllerEnd_OnMin;
|
|
else if (sName == "OnDest")
|
|
return ePhysicsControllerEnd_OnDest;
|
|
|
|
return ePhysicsControllerEnd_Null;
|
|
}
|
|
|
|
/////////////////////////
|
|
|
|
void cEntityLoader_Object::LoadController(iPhysicsJoint *apJoint, iPhysicsWorld *apPhysicsWorld,
|
|
TiXmlElement *apElem) {
|
|
//////////////////////////////
|
|
// Get the properties
|
|
tString sName = cString::ToString(apElem->Attribute("Name"), "");
|
|
bool bActive = cString::ToBool(apElem->Attribute("Active"), false);
|
|
|
|
ePhysicsControllerType CtrlType = GetControllerType(apElem->Attribute("Type"));
|
|
float fA = cString::ToFloat(apElem->Attribute("A"), 0);
|
|
float fB = cString::ToFloat(apElem->Attribute("B"), 0);
|
|
float fC = cString::ToFloat(apElem->Attribute("C"), 0);
|
|
int lIntegralSize = cString::ToInt(apElem->Attribute("IntegralSize"), 1);
|
|
|
|
ePhysicsControllerInput CtrlInput = GetControllerInput(apElem->Attribute("Input"));
|
|
ePhysicsControllerAxis CtrlInputAxis = GetControllerAxis(apElem->Attribute("InputAxis"));
|
|
float fDestValue = cString::ToFloat(apElem->Attribute("DestValue"), 0);
|
|
float fMaxOutput = cString::ToFloat(apElem->Attribute("MaxOutput"), 0);
|
|
|
|
ePhysicsControllerOutput CtrlOutput = GetControllerOutput(apElem->Attribute("Output"));
|
|
ePhysicsControllerAxis CtrlOutputAxis = GetControllerAxis(apElem->Attribute("OutputAxis"));
|
|
bool bMulMassWithOutput = cString::ToBool(apElem->Attribute("MulMassWithOutput"), false);
|
|
|
|
ePhysicsControllerEnd CtrlEnd = GetControllerEnd(apElem->Attribute("EndType"));
|
|
tString sNextCtrl = cString::ToString(apElem->Attribute("NextController"), "");
|
|
|
|
bool bLogInfo = cString::ToBool(apElem->Attribute("LogInfo"), false);
|
|
|
|
// Convert degrees to radians.
|
|
if (CtrlInput == ePhysicsControllerInput_JointAngle) {
|
|
fDestValue = cMath::ToRad(fDestValue);
|
|
}
|
|
|
|
//////////////////////////////
|
|
// Create the controller
|
|
iPhysicsController *pController = apPhysicsWorld->CreateController(sName);
|
|
|
|
pController->SetType(CtrlType);
|
|
|
|
pController->SetA(fA);
|
|
pController->SetB(fB);
|
|
pController->SetC(fC);
|
|
|
|
pController->SetPidIntegralSize(lIntegralSize);
|
|
|
|
pController->SetActive(bActive);
|
|
pController->SetInputType(CtrlInput, CtrlInputAxis);
|
|
pController->SetDestValue(fDestValue);
|
|
|
|
pController->SetOutputType(CtrlOutput, CtrlOutputAxis);
|
|
pController->SetMaxOutput(fMaxOutput);
|
|
pController->SetMulMassWithOutput(bMulMassWithOutput);
|
|
|
|
pController->SetEndType(CtrlEnd);
|
|
pController->SetNextController(sNextCtrl);
|
|
|
|
pController->SetLogInfo(bLogInfo);
|
|
|
|
apJoint->AddController(pController);
|
|
|
|
// Log("Controller: %s active: %d val: %f %f %f input: %d %d output: %d %d\n",
|
|
// sName.c_str(),bActive, fA,fB,fC, (int)CtrlInput, (int)CtrlInputAxis, (int)CtrlOutput,(int)CtrlOutputAxis);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
eAnimationEventType cEntityLoader_Object::GetAnimationEventType(const char *apString) {
|
|
if (apString == NULL)
|
|
return eAnimationEventType_LastEnum;
|
|
|
|
tString sName = apString;
|
|
sName = cString::ToLowerCase(sName);
|
|
|
|
if (sName == "playsound") {
|
|
return eAnimationEventType_PlaySound;
|
|
}
|
|
|
|
Warning("Animation event type '%s' does not exist!\n", apString);
|
|
return eAnimationEventType_LastEnum;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// PRIVATE METHODS
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
//-----------------------------------------------------------------------
|
|
} // namespace hpl
|