/* ScummVM - Graphic Adventure Engine * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT * file distributed with this source distribution. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ /* * Copyright (C) 2006-2010 - Frictional Games * * This file is part of HPL1 Engine. */ #include "hpl1/engine/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(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(pJoint); pBallJoint->SetConeLimits(pBallJoint->GetPinDir(), cMath::ToRad(fMinValue), cMath::ToRad(fMaxValue)); break; } case ePhysicsJointType_Hinge: { iPhysicsJointHinge *pHingeJoint = static_cast(pJoint); pHingeJoint->SetMaxAngle(cMath::ToRad(fMaxValue)); pHingeJoint->SetMinAngle(cMath::ToRad(-fMinValue)); break; } case ePhysicsJointType_Screw: { iPhysicsJointScrew *pScrewJoint = static_cast(pJoint); pScrewJoint->SetMinDistance(-fMinValue / 100); pScrewJoint->SetMaxDistance(fMaxValue / 100); break; } case ePhysicsJointType_Slider: { iPhysicsJointSlider *pSliderJoint = static_cast(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