Files
2026-02-02 04:50:13 +01:00

637 lines
19 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 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;
}
//-----------------------------------------------------------------------