/* 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 Penumbra Overture. */ #include "hpl1/penumbra-overture/PlayerHands.h" #include "hpl1/penumbra-overture/Init.h" #include "hpl1/penumbra-overture/Player.h" #include "hpl1/penumbra-overture/PlayerHelper.h" #include "hpl1/penumbra-overture/HudModel_Throw.h" #include "hpl1/penumbra-overture/HudModel_Weapon.h" ////////////////////////////////////////////////////////////////////////// // HELP FUNCTIONS ////////////////////////////////////////////////////////////////////////// //----------------------------------------------------------------------- cHudModelPose GetPoseFromElem(const tString &asName, TiXmlElement *apElem) { cHudModelPose hudPose; tString sAttrPos = asName + "_Pos"; tString sAttrRot = asName + "_Rot"; hudPose.mvPos = cString::ToVector3f(apElem->Attribute(sAttrPos.c_str()), 0); hudPose.mvRot = cMath::Vector3ToRad( cString::ToVector3f(apElem->Attribute(sAttrRot.c_str()), 0)); return hudPose; } //----------------------------------------------------------------------- cMatrixf InterpolatePosesToMatrix(float afT, const cHudModelPose &aPoseA, const cHudModelPose &aPoseB) { cVector3f vPos = aPoseA.mvPos * (1 - afT) + aPoseB.mvPos * afT; cMatrixf mtxRotA = cMath::MatrixRotate(aPoseA.mvRot, eEulerRotationOrder_XYZ); cMatrixf mtxRotB = cMath::MatrixRotate(aPoseB.mvRot, eEulerRotationOrder_XYZ); cQuaternion qA; qA.FromRotationMatrix(mtxRotA); cQuaternion qB; qB.FromRotationMatrix(mtxRotB); cQuaternion qFinal = cMath::QuaternionSlerp(afT, qA, qB, true); cMatrixf mtxFinal = cMath::MatrixQuaternion(qFinal); mtxFinal.SetTranslation(vPos); return mtxFinal; } //----------------------------------------------------------------------- ////////////////////////////////////////////////////////////////////////// // HUD MODEL ////////////////////////////////////////////////////////////////////////// //----------------------------------------------------------------------- iHudModel::iHudModel(ePlayerHandType aType) { mType = aType; mState = eHudModelState_Idle; } //----------------------------------------------------------------------- void iHudModel::LoadEntities() { cWorld3D *pWorld = mpInit->mpGame->GetScene()->GetWorld3D(); // Mesh entity mpMesh->IncUserCount(); // entity is newer user. mpEntity = pWorld->CreateMeshEntity(msName, mpMesh); // Create lights for (int i = 0; i < mpMesh->GetLightNum(); i++) { iLight3D *pLight = mpMesh->CreateLightInWorld(msName, mpMesh->GetLight(i), mpEntity, pWorld); if (pLight) { mvLightColors.push_back(pLight->GetDiffuseColor()); mvLightRadii.push_back(pLight->GetFarAttenuation()); mvLights.push_back(pLight); } } // Create billboards for (int i = 0; i < mpMesh->GetBillboardNum(); i++) { cBillboard *pBill = mpMesh->CreateBillboardInWorld(msName, mpMesh->GetBillboard(i), mpEntity, pWorld); if (pBill) mvBillboards.push_back(pBill); } // Create particle systems for (int i = 0; i < mpMesh->GetParticleSystemNum(); i++) { cParticleSystem3D *pPS = mpMesh->CreateParticleSystemInWorld(msName, mpMesh->GetParticleSystem(i), mpEntity, pWorld); if (pPS) mvParticleSystems.push_back(pPS); } // Create sounds entities for (int i = 0; i < mpMesh->GetSoundEntityNum(); i++) { cSoundEntity *pSound = mpMesh->CreateSoundEntityInWorld(msName, mpMesh->GetSoundEntity(i), mpEntity, pWorld); if (pSound) mvSoundEntities.push_back(pSound); } LoadExtraEntites(); } //----------------------------------------------------------------------- void iHudModel::DestroyEntities() { cWorld3D *pWorld = mpInit->mpGame->GetScene()->GetWorld3D(); // Mesh entity pWorld->DestroyMeshEntity(mpEntity); mpEntity = NULL; // Particle systems for (size_t i = 0; i < mvParticleSystems.size(); ++i) { pWorld->DestroyParticleSystem(mvParticleSystems[i]); } mvParticleSystems.clear(); // Billboards for (size_t i = 0; i < mvBillboards.size(); ++i) { pWorld->DestroyBillboard(mvBillboards[i]); } mvBillboards.clear(); // Lights for (size_t i = 0; i < mvLights.size(); ++i) { pWorld->DestroyLight(mvLights[i]); } mvLights.clear(); // Sound entities for (size_t i = 0; i < mvSoundEntities.size(); ++i) { if (pWorld->SoundEntityExists(mvSoundEntities[i])) pWorld->DestroySoundEntity(mvSoundEntities[i]); } mvSoundEntities.clear(); DestroyExtraEntities(); } //----------------------------------------------------------------------- void iHudModel::Reset() { mfTime = 0; ResetExtraData(); } //----------------------------------------------------------------------- void iHudModel::EquipEffect(bool abJustCreated) { if (msEquipSound != "") { cSoundHandler *pSoundHanlder = mpInit->mpGame->GetSound()->GetSoundHandler(); pSoundHanlder->PlayGui(msEquipSound, false, 1); } for (size_t i = 0; i < mvLights.size(); ++i) { if (abJustCreated) mvLights[i]->SetDiffuseColor(cColor(0, 0)); mvLights[i]->FadeTo(mvLightColors[i], mvLightRadii[i], 0.3f); } } void iHudModel::UnequipEffect() { if (msUnequipSound != "") { cSoundHandler *pSoundHanlder = mpInit->mpGame->GetSound()->GetSoundHandler(); pSoundHanlder->PlayGui(msUnequipSound, false, 1); } for (size_t i = 0; i < mvLights.size(); ++i) { mvLights[i]->FadeTo(cColor(0, 0), mvLightRadii[i], 0.3f); } } ////////////////////////////////////////////////////////////////////////// // CONSTRUCTORS ////////////////////////////////////////////////////////////////////////// //----------------------------------------------------------------------- cPlayerHands::cPlayerHands(cInit *apInit) : iUpdateable("FadeHandler") { mpInit = apInit; mpMeshManager = mpInit->mpGame->GetResources()->GetMeshManager(); mlMaxPositions = 3; mlMaxRotations = 16; mlCurrentModelNum = 2; for (int i = 0; i < mlCurrentModelNum; ++i) { mvCurrentHudModels[i] = NULL; } } //----------------------------------------------------------------------- cPlayerHands::~cPlayerHands(void) { /////////////////////////////////////////// // Replace this and use some cache instead tHudModelMapIt it = m_mapHudModels.begin(); for (; it != m_mapHudModels.end(); ++it) { iHudModel *pHandModel = it->second; mpMeshManager->Destroy(pHandModel->mpMesh); } STLMapDeleteAll(m_mapHudModels); } //----------------------------------------------------------------------- ////////////////////////////////////////////////////////////////////////// // PUBLIC METHODS ////////////////////////////////////////////////////////////////////////// //----------------------------------------------------------------------- void cPlayerHands::OnStart() { } //----------------------------------------------------------------------- void cPlayerHands::Update(float afTimeStep) { UpdatePrevPositions(); /////////////////////////////////// // Get the camera properties cCamera3D *pCam = mpInit->mpPlayer->GetCamera(); cVector3f vRot = cVector3f(pCam->GetPitch(), pCam->GetYaw(), pCam->GetRoll()); cMatrixf mtxSmoothCam = cMath::MatrixRotate(vRot * -1.0f, eEulerRotationOrder_YXZ); cVector3f vUp = mtxSmoothCam.GetUp(); // pCam->GetUp(); cVector3f vRight = mtxSmoothCam.GetRight(); // pCam->GetRight(); cVector3f vForward = mtxSmoothCam.GetForward() * -1.0f; // pCam->GetForward(); ///////////////////////////////////// // Update the current model for (int i = 0; i < mlCurrentModelNum; ++i) { iHudModel *pHudModel = mvCurrentHudModels[i]; if (pHudModel == NULL) continue; cMatrixf mtxPose; //////////////////// // Update state switch (pHudModel->mState) { // Idle case eHudModelState_Idle: { if (pHudModel->UpdatePoseMatrix(mtxPose, afTimeStep) == false) { mtxPose = cMath::MatrixRotate(pHudModel->mEquipPose.mvRot, eEulerRotationOrder_XYZ); mtxPose.SetTranslation(pHudModel->mEquipPose.mvPos); } break; } // Equip case eHudModelState_Equip: { float fT = cMath::Clamp(pHudModel->mfTime, 0, 1); mtxPose = InterpolatePosesToMatrix(fT, pHudModel->mUnequipPose, pHudModel->mEquipPose); pHudModel->mfTime += afTimeStep / pHudModel->mfEquipTime; if (pHudModel->mfTime >= 1) { pHudModel->mState = eHudModelState_Idle; pHudModel->mfTime = 1; } break; } // Unequip case eHudModelState_Unequip: { float fT = cMath::Clamp(pHudModel->mfTime, 0, 1); mtxPose = InterpolatePosesToMatrix(fT, pHudModel->mEquipPose, pHudModel->mUnequipPose); pHudModel->mfTime += afTimeStep / pHudModel->mfUnequipTime; if (pHudModel->mfTime >= 1) { // Log("Creating next model and destroying current!\n"); pHudModel->mState = eHudModelState_Idle; pHudModel->mfTime = 0; pHudModel->DestroyEntities(); mvCurrentHudModels[i] = NULL; if (pHudModel->msNextModel != "") { SetCurrentModel(i, pHudModel->msNextModel); } pHudModel->Reset(); continue; } break; } } //////////////////// // Set rotation cMatrixf mtxTransform = cMath::MatrixMul( cMath::MatrixRotate(mvSmoothCameraRot, eEulerRotationOrder_XYZ), mtxPose.GetRotation()); // pHudModel->mpEntity->SetMatrix(mtxRot); ///////////////////////// // Set position const cVector3f &vLocalPos = mtxPose.GetTranslation(); cVector3f vRealLocalPos = vUp * vLocalPos.y + vRight * vLocalPos.x + vForward * vLocalPos.z + cVector3f(0, -mpInit->mpPlayer->GetHeadMove()->GetPos() * 0.1f, 0); ; mtxTransform.SetTranslation(pCam->GetPosition() + vRealLocalPos); pHudModel->mpEntity->SetMatrix(mtxTransform); } } //----------------------------------------------------------------------- void cPlayerHands::Reset() { for (int i = 0; i < mlCurrentModelNum; ++i) { iHudModel *pHudModel = mvCurrentHudModels[i]; if (pHudModel) { pHudModel->DestroyEntities(); } mvCurrentHudModels[i] = NULL; } tHudModelMapIt it = m_mapHudModels.begin(); for (; it != m_mapHudModels.end(); ++it) { iHudModel *pModel = it->second; pModel->Reset(); } } //----------------------------------------------------------------------- void cPlayerHands::OnDraw() { } //----------------------------------------------------------------------- void cPlayerHands::AddHudModel(iHudModel *apHudModel) { apHudModel->mpMesh = mpMeshManager->CreateMesh(apHudModel->msModelFile); apHudModel->mpInit = mpInit; m_mapHudModels.insert(tHudModelMap::value_type(cString::ToLowerCase(apHudModel->msName), apHudModel)); } //----------------------------------------------------------------------- static ePlayerHandType ToHandType(const char *apString) { if (apString == NULL) return ePlayerHandType_Normal; tString sType = cString::ToLowerCase(apString); if (sType == "normal") return ePlayerHandType_Normal; else if (sType == "weaponmelee") return ePlayerHandType_WeaponMelee; else if (sType == "throw") return ePlayerHandType_Throw; return ePlayerHandType_Normal; } //////////////////////////////////// bool cPlayerHands::AddModelFromFile(const tString &asFile) { tString sFileName = cString::SetFileExt(asFile, "hud"); tString sPath = mpInit->mpGame->GetResources()->GetFileSearcher()->GetFilePath(sFileName); if (sPath == "") { Error("Couldn't find '%s' in resource directories!\n", sFileName.c_str()); return false; } //////////////////////////////////////////////// // Load the document TiXmlDocument *pXmlDoc = hplNew(TiXmlDocument, (sPath.c_str())); if (pXmlDoc->LoadFile() == false) { Error("Couldn't load XML document '%s'\n", sPath.c_str()); hplDelete(pXmlDoc); return false; } //////////////////////////////////////////////// // Load the root TiXmlElement *pRootElem = pXmlDoc->FirstChildElement(); if (pRootElem == NULL) { Error("Couldn't load root from XML document '%s'\n", sPath.c_str()); hplDelete(pXmlDoc); return false; } //////////////////////////////////////////////// // Load the MAIN element. TiXmlElement *pMainElem = pRootElem->FirstChildElement("MAIN"); if (pMainElem == NULL) { Error("Couldn't load MAIN element from XML document '%s'\n", sPath.c_str()); hplDelete(pXmlDoc); return false; } ePlayerHandType handType = ToHandType(pMainElem->Attribute("Type")); iHudModel *pHudModel = NULL; switch (handType) { case ePlayerHandType_Normal: pHudModel = hplNew(cHudModel_Normal, ()); break; case ePlayerHandType_WeaponMelee: pHudModel = hplNew(cHudModel_WeaponMelee, ()); break; case ePlayerHandType_Throw: pHudModel = hplNew(cHudModel_Throw, ()); break; default: break; } pHudModel->msName = cString::ToString(pMainElem->Attribute("Name"), ""); pHudModel->msModelFile = cString::ToString(pMainElem->Attribute("ModelFile"), ""); pHudModel->mfEquipTime = cString::ToFloat(pMainElem->Attribute("EquipTime"), 0.3f); pHudModel->mfUnequipTime = cString::ToFloat(pMainElem->Attribute("UnequipTime"), 0.3f); pHudModel->mEquipPose = GetPoseFromElem("EquipPose", pMainElem); pHudModel->mUnequipPose = GetPoseFromElem("UnequipPose", pMainElem); pHudModel->msEquipSound = cString::ToString(pMainElem->Attribute("EquipSound"), ""); pHudModel->msUnequipSound = cString::ToString(pMainElem->Attribute("UnequipSound"), ""); pHudModel->LoadData(pRootElem); AddHudModel(pHudModel); hplDelete(pXmlDoc); return true; } //----------------------------------------------------------------------- void cPlayerHands::SetCurrentModel(int alNum, const tString &asName) { // Log("Setting current %d to '%s'\n",alNum,asName.c_str()); ////////////////////////////////////////////// // Check so that it is not already equipped if (mvCurrentHudModels[alNum] && cString::ToLowerCase(asName) == cString::ToLowerCase(mvCurrentHudModels[alNum]->msName) && mvCurrentHudModels[alNum]->mState == eHudModelState_Idle) { // Log(" model already active!\n"); return; } /////////////////////////// // Add a newer hud model if (asName != "") { tHudModelMapIt it = m_mapHudModels.find(cString::ToLowerCase(asName)); if (it == m_mapHudModels.end()) { Log(" Couldn't find hud model '%s'!\n", asName.c_str()); return; } iHudModel *pHandModel = it->second; /*cWorld3D *pWorld = */ mpInit->mpGame->GetScene()->GetWorld3D(); if (mvCurrentHudModels[alNum]) { if (mvCurrentHudModels[alNum] != pHandModel) { if (mvCurrentHudModels[alNum]->mState != eHudModelState_Unequip) { mvCurrentHudModels[alNum]->UnequipEffect(); mvCurrentHudModels[alNum]->mState = eHudModelState_Unequip; mvCurrentHudModels[alNum]->mfTime = 1.0f - mvCurrentHudModels[alNum]->mfTime; } mvCurrentHudModels[alNum]->msNextModel = asName; // Log(" Unequipping %s, time: %f\n",mvCurrentHudModels[alNum]->msName.c_str(), // mvCurrentHudModels[alNum]->mfTime); } else { pHandModel->EquipEffect(false); mvCurrentHudModels[alNum]->mState = eHudModelState_Equip; mvCurrentHudModels[alNum]->mfTime = 1.0f - mvCurrentHudModels[alNum]->mfTime; mvCurrentHudModels[alNum]->msNextModel = asName; } } else { pHandModel->LoadEntities(); pHandModel->EquipEffect(true); if (mvCurrentHudModels[alNum] == pHandModel) { pHandModel->mfTime = 1.0f - pHandModel->mfTime; } else { pHandModel->mfTime = 0; } pHandModel->mState = eHudModelState_Equip; mvCurrentHudModels[alNum] = pHandModel; } } /////////////////////////// // Only remove old else { if (mvCurrentHudModels[alNum]) { mvCurrentHudModels[alNum]->UnequipEffect(); mvCurrentHudModels[alNum]->mState = eHudModelState_Unequip; mvCurrentHudModels[alNum]->mfTime = 1.0f - mvCurrentHudModels[alNum]->mfTime; mvCurrentHudModels[alNum]->msNextModel = asName; // Log(" Destroying old\n"); } else { // Log("No old to destroy!!!\n"); } } } //----------------------------------------------------------------------- iHudModel *cPlayerHands::GetModel(const tString &asName) { tHudModelMapIt it = m_mapHudModels.find(cString::ToLowerCase(asName)); if (it == m_mapHudModels.end()) { return NULL; } return it->second; } //----------------------------------------------------------------------- void cPlayerHands::OnWorldExit() { for (int i = 0; i < mlCurrentModelNum; ++i) { iHudModel *pHudModel = mvCurrentHudModels[i]; if (pHudModel) { pHudModel->DestroyEntities(); } } } //----------------------------------------------------------------------- void cPlayerHands::OnWorldLoad() { mlstRotations.clear(); mlstPositions.clear(); for (int i = 0; i < mlCurrentModelNum; ++i) { iHudModel *pHudModel = mvCurrentHudModels[i]; if (pHudModel) { pHudModel->LoadEntities(); } } } //----------------------------------------------------------------------- ////////////////////////////////////////////////////////////////////////// // PUBLIC METHODS ////////////////////////////////////////////////////////////////////////// //----------------------------------------------------------------------- void cPlayerHands::UpdatePrevPositions() { /////////////////////////////////// // Get current position cCamera3D *pCam = mpInit->mpPlayer->GetCamera(); cVector3f vCamRotation(pCam->GetPitch(), pCam->GetYaw(), 0); cVector3f vCamPosition = pCam->GetPosition(); mlstRotations.push_back(vCamRotation); mlstPositions.push_back(vCamPosition); // Delete if there are too many values stored. if ((int)mlstPositions.size() > mlMaxPositions) mlstPositions.pop_front(); if ((int)mlstRotations.size() > mlMaxRotations) mlstRotations.pop_front(); /////////////////////////////////////// // Get the current camera position and rotation cVector3f vRotation(0, 0, 0); cVector3f vPosition(0, 0, 0); float fRotNum = 0; //float fPosNum = 0; // float fRotMulStart = 1.0f; // float fRotMulEnd = 0.1f; // float fSize = (float)mlstRotations.size(); // float fD = (fRotMulStart - fRotMulEnd) / fSize; float fMul = 1.0f; // fRotMulEnd; for (tVector3fListIt it = mlstRotations.begin(); it != mlstRotations.end(); ++it) { vRotation += *it * fMul; fRotNum += fMul; // fMul += fD; } for (tVector3fListIt it = mlstPositions.begin(); it != mlstPositions.end(); ++it) { vPosition += *it; //fPosNum++; } mvSmoothCameraPos = vCamPosition; // vPosition / fPosNum; mvSmoothCameraRot = vRotation / fRotNum; } //-----------------------------------------------------------------------