Files
scummvm-cursorfix/engines/hpl1/engine/scene/SubMeshEntity.cpp
2026-02-02 04:50:13 +01:00

524 lines
17 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/scene/SubMeshEntity.h"
#include "hpl1/engine/scene/MeshEntity.h"
#include "hpl1/engine/graphics/Material.h"
#include "hpl1/engine/graphics/Mesh.h"
#include "hpl1/engine/graphics/SubMesh.h"
#include "hpl1/engine/graphics/VertexBuffer.h"
#include "hpl1/engine/resources/MaterialManager.h"
#include "hpl1/engine/resources/MeshManager.h"
#include "hpl1/engine/graphics/Animation.h"
#include "hpl1/engine/graphics/AnimationTrack.h"
#include "hpl1/engine/graphics/Bone.h"
#include "hpl1/engine/graphics/Skeleton.h"
#include "hpl1/engine/scene/AnimationState.h"
#include "hpl1/engine/scene/NodeState.h"
#include "hpl1/engine/physics/PhysicsBody.h"
#include "hpl1/engine/math/Math.h"
namespace hpl {
//////////////////////////////////////////////////////////////////////////
// CONSTRUCTORS
//////////////////////////////////////////////////////////////////////////
cSubMeshEntity::cSubMeshEntity(const tString &asName, cMeshEntity *apMeshEntity, cSubMesh *apSubMesh,
cMaterialManager *apMaterialManager) : iRenderable(asName) {
mpMeshEntity = apMeshEntity;
mpSubMesh = apSubMesh;
mbIsOneSided = mpSubMesh->GetIsOneSided();
mvOneSidedNormal = mpSubMesh->GetOneSidedNormal();
mpMaterialManager = apMaterialManager;
mbCastShadows = false;
mbGraphicsUpdated = false;
mpBody = NULL;
if (mpMeshEntity->GetMesh()->GetSkeleton()) {
mpDynVtxBuffer = mpSubMesh->GetVertexBuffer()->CreateCopy(eVertexBufferUsageType_Dynamic);
mvDynTriangles = *mpSubMesh->GetTriangleVecPtr();
} else {
mpDynVtxBuffer = NULL;
}
mpLocalNode = NULL;
mpEntityCallback = hplNew(cSubMeshEntityBodyUpdate, ());
mbUpdateBody = false;
mpMaterial = NULL;
}
cSubMeshEntity::~cSubMeshEntity() {
hplDelete(mpEntityCallback);
if (mpDynVtxBuffer)
hplDelete(mpDynVtxBuffer);
/* Clear any custom textures here*/
if (mpMaterial)
mpMaterialManager->Destroy(mpMaterial);
}
//-----------------------------------------------------------------------
//////////////////////////////////////////////////////////////////////////
// BODY CALLBACK
//////////////////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------
void cSubMeshEntityBodyUpdate::OnTransformUpdate(iEntity3D *apEntity) {
/*cSubMeshEntity *pSubEntity = static_cast<cSubMeshEntity*>(apEntity);
if(pSubEntity->GetBody())
{
if(apEntity->GetWorldMatrix() != pSubEntity->GetBody()->GetLocalMatrix())
{
Log("Setting matrix on %s from\n",pSubEntity->GetBody()->GetName().c_str());
Log(" %s\n",apEntity->GetWorldMatrix().ToString().c_str());
Log(" %s\n",pSubEntity->GetBody()->GetLocalMatrix().ToString().c_str());
pSubEntity->GetBody()->SetMatrix(apEntity->GetWorldMatrix());
}
}*/
}
//-----------------------------------------------------------------------
//////////////////////////////////////////////////////////////////////////
// PUBLIC METHODS
//////////////////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------
void cSubMeshEntity::UpdateLogic(float afTimeStep) {
if (mbUpdateBody && mpBody) {
mpBody->SetMatrix(GetWorldMatrix());
}
}
//-----------------------------------------------------------------------
iMaterial *cSubMeshEntity::GetMaterial() {
if (mpMaterial == NULL && mpSubMesh->GetMaterial() == NULL) {
Error("Materials for sub entity %s are NULL!\n", GetName().c_str());
}
if (mpMaterial)
return mpMaterial;
else
return mpSubMesh->GetMaterial();
}
//-----------------------------------------------------------------------
// Set Src as private variable to give this a little boost! Or?
static inline void MatrixFloatTransformSet(float *pDest, const cMatrixf &a_mtxA, const float *pSrc, const float fWeight) {
pDest[0] = (a_mtxA.m[0][0] * pSrc[0] + a_mtxA.m[0][1] * pSrc[1] + a_mtxA.m[0][2] * pSrc[2] + a_mtxA.m[0][3]) * fWeight;
pDest[1] = (a_mtxA.m[1][0] * pSrc[0] + a_mtxA.m[1][1] * pSrc[1] + a_mtxA.m[1][2] * pSrc[2] + a_mtxA.m[1][3]) * fWeight;
pDest[2] = (a_mtxA.m[2][0] * pSrc[0] + a_mtxA.m[2][1] * pSrc[1] + a_mtxA.m[2][2] * pSrc[2] + a_mtxA.m[2][3]) * fWeight;
}
static inline void MatrixFloatRotateSet(float *pDest, const cMatrixf &a_mtxA, const float *pSrc, const float fWeight) {
pDest[0] = (a_mtxA.m[0][0] * pSrc[0] + a_mtxA.m[0][1] * pSrc[1] + a_mtxA.m[0][2] * pSrc[2]) * fWeight;
pDest[1] = (a_mtxA.m[1][0] * pSrc[0] + a_mtxA.m[1][1] * pSrc[1] + a_mtxA.m[1][2] * pSrc[2]) * fWeight;
pDest[2] = (a_mtxA.m[2][0] * pSrc[0] + a_mtxA.m[2][1] * pSrc[1] + a_mtxA.m[2][2] * pSrc[2]) * fWeight;
}
////////////////////////////////////////////////////////////
// Set Src as private variable to give this a little boost!Or?
static inline void MatrixFloatTransformAdd(float *pDest, const cMatrixf &a_mtxA, const float *pSrc, const float fWeight) {
pDest[0] += (a_mtxA.m[0][0] * pSrc[0] + a_mtxA.m[0][1] * pSrc[1] + a_mtxA.m[0][2] * pSrc[2] + a_mtxA.m[0][3]) * fWeight;
pDest[1] += (a_mtxA.m[1][0] * pSrc[0] + a_mtxA.m[1][1] * pSrc[1] + a_mtxA.m[1][2] * pSrc[2] + a_mtxA.m[1][3]) * fWeight;
pDest[2] += (a_mtxA.m[2][0] * pSrc[0] + a_mtxA.m[2][1] * pSrc[1] + a_mtxA.m[2][2] * pSrc[2] + a_mtxA.m[2][3]) * fWeight;
}
static inline void MatrixFloatRotateAdd(float *pDest, const cMatrixf &a_mtxA, const float *pSrc, const float fWeight) {
pDest[0] += (a_mtxA.m[0][0] * pSrc[0] + a_mtxA.m[0][1] * pSrc[1] + a_mtxA.m[0][2] * pSrc[2]) * fWeight;
pDest[1] += (a_mtxA.m[1][0] * pSrc[0] + a_mtxA.m[1][1] * pSrc[1] + a_mtxA.m[1][2] * pSrc[2]) * fWeight;
pDest[2] += (a_mtxA.m[2][0] * pSrc[0] + a_mtxA.m[2][1] * pSrc[1] + a_mtxA.m[2][2] * pSrc[2]) * fWeight;
}
////////////////////////////////////////////////////////////
void cSubMeshEntity::UpdateGraphics(cCamera3D *apCamera, float afFrameTime, cRenderList *apRenderList) {
if (mpDynVtxBuffer) {
if (mpMeshEntity->mbSkeletonPhysicsSleeping && mbGraphicsUpdated) {
return;
}
mbGraphicsUpdated = true;
const float *pBindPos = mpSubMesh->GetVertexBuffer()->GetArray(eVertexFlag_Position);
const float *pBindNormal = mpSubMesh->GetVertexBuffer()->GetArray(eVertexFlag_Normal);
const float *pBindTangent = mpSubMesh->GetVertexBuffer()->GetArray(eVertexFlag_Texture1);
float *pSkinPos = mpDynVtxBuffer->GetArray(eVertexFlag_Position);
float *pSkinNormal = mpDynVtxBuffer->GetArray(eVertexFlag_Normal);
float *pSkinTangent = mpDynVtxBuffer->GetArray(eVertexFlag_Texture1);
const int lVtxStride = kvVertexElements[cMath::Log2ToInt(eVertexFlag_Position)];
const int lVtxNum = mpDynVtxBuffer->GetVertexNum();
for (int vtx = 0; vtx < lVtxNum; vtx++) {
// To count the bone bindings
int lCount = 0;
// Get pointer to weights and bone index.
const float *pWeight = &mpSubMesh->mpVertexWeights[vtx * 4];
if (*pWeight == 0)
continue;
const unsigned char *pBoneIdx = &mpSubMesh->mpVertexBones[vtx * 4];
{
const cMatrixf &mtxTransform = mpMeshEntity->mvBoneMatrices[*pBoneIdx];
// Log("Vtx: %d\n",vtx);
// Log("Boneidx: %d Count %d Weight: %f vtx: %d\n",(int)*pBoneIdx,lCount, *pWeight,vtx);
// ATTENTION: Some optimizing might be done by accumulating the matrix instead.
// THIS is really unsure since it seems like it will result in more math, matrix mul = 8*4 calc
// Vertex mul with matrix is 3 * 3 calculations
// this means: vertex= 9*3 = 27, matrix = 32
MatrixFloatTransformSet(pSkinPos, mtxTransform, pBindPos, *pWeight);
MatrixFloatRotateSet(pSkinNormal, mtxTransform, pBindNormal, *pWeight);
MatrixFloatRotateSet(pSkinTangent, mtxTransform, pBindTangent, *pWeight);
++pWeight;
++pBoneIdx;
++lCount;
}
// Iterate weights until 0 is found or count < 4
while (*pWeight != 0 && lCount < 4) {
// Log("Boneidx: %d Count %d Weight: %f\n",(int)*pBoneIdx,lCount, *pWeight);
const cMatrixf &mtxTransform = mpMeshEntity->mvBoneMatrices[*pBoneIdx];
// Transform with the local movement of the bone.
MatrixFloatTransformAdd(pSkinPos, mtxTransform, pBindPos, *pWeight);
MatrixFloatRotateAdd(pSkinNormal, mtxTransform, pBindNormal, *pWeight);
MatrixFloatRotateAdd(pSkinTangent, mtxTransform, pBindTangent, *pWeight);
++pWeight;
++pBoneIdx;
++lCount;
}
pBindPos += lVtxStride;
pSkinPos += lVtxStride;
pBindNormal += 3;
pSkinNormal += 3;
pBindTangent += 4;
pSkinTangent += 4;
}
float *pSkinPosArray = mpDynVtxBuffer->GetArray(eVertexFlag_Position);
if (mpMeshEntity->IsShadowCaster()) {
// Update the shadow double
memcpy(&pSkinPosArray[lVtxStride * lVtxNum], pSkinPosArray, sizeof(float) * lVtxStride * lVtxNum);
for (int vtx = lVtxStride * lVtxNum + lVtxStride - 1; vtx < lVtxStride * lVtxNum * 2; vtx += lVtxStride) {
pSkinPosArray[vtx] = 0;
}
}
// Update buffer
mpDynVtxBuffer->UpdateData(eVertexFlag_Position | eVertexFlag_Normal | eVertexFlag_Texture1, false);
if (mpMeshEntity->IsShadowCaster()) {
// Update triangles
cMath::CreateTriangleData(mvDynTriangles,
mpDynVtxBuffer->GetIndices(), mpDynVtxBuffer->GetIndexNum(),
pSkinPosArray, lVtxStride, lVtxNum);
}
}
/*if(mpDynVtxBuffer)
{
const float *pBindPosArray = mpSubMesh->GetVertexBuffer()->GetArray(eVertexFlag_Position);
const float *pBindNormalArray = mpSubMesh->GetVertexBuffer()->GetArray(eVertexFlag_Normal);
const float *pBindTangentArray = mpSubMesh->GetVertexBuffer()->GetArray(eVertexFlag_Texture1);
float *pSkinPosArray = mpDynVtxBuffer->GetArray(eVertexFlag_Position);
float *pSkinNormalArray = mpDynVtxBuffer->GetArray(eVertexFlag_Normal);
float *pSkinTangentArray = mpDynVtxBuffer->GetArray(eVertexFlag_Texture1);
int lVtxStride = kvVertexElements[cMath::Log2ToInt(eVertexFlag_Position)];
int lVtxNum = mpDynVtxBuffer->GetVertexNum();
memset(pSkinPosArray,0,sizeof(float)*lVtxStride*lVtxNum);
memset(pSkinNormalArray,0,sizeof(float)*3*lVtxNum);
memset(pSkinTangentArray,0,sizeof(float)*4*lVtxNum);
int lSize = mpSubMesh->GetVertexBonePairNum();
for(int i=0; i<lSize; i++)
{
const cVertexBonePair& VBPair = mpSubMesh->GetVertexBonePair(i);
//Log("%d: Vtx: %d Bone: %d\n",i,VBPair.vtxIdx, VBPair.boneIdx);
const float* pBindPos = &pBindPosArray[VBPair.vtxIdx*lVtxStride];
float* pSkinPos = &pSkinPosArray[VBPair.vtxIdx*lVtxStride];
const float* pBindNorm = &pBindNormalArray[VBPair.vtxIdx*3];
float* pSkinNorm = &pSkinNormalArray[VBPair.vtxIdx*3];
const float* pBindTan = &pBindTangentArray[VBPair.vtxIdx*4];
float* pSkinTan = &pSkinTangentArray[VBPair.vtxIdx*4];
const cMatrixf &mtxTransform = mpMeshEntity->mvBoneMatrices[VBPair.boneIdx];
//Transform with the local movement of the bone.
MatrixFloatTransform(pSkinPos,mtxTransform, pBindPos, VBPair.weight);
pSkinPos[3] = 1;
MatrixFloatRotate(pSkinNorm,mtxTransform, pBindNorm, VBPair.weight);
MatrixFloatRotate(pSkinTan,mtxTransform, pBindTan, VBPair.weight);
pSkinTan[3] = pBindTan[3];
//cVector3f vSkin = cMath::MatrixMul(mpMeshEntity->mvBoneMatrices[VBPair.boneIdx],
// cVector3f(pBindPos[0],pBindPos[1],pBindPos[2]));
//pSkinPos[0] += vSkin.x * VBPair.weight;
//pSkinPos[1] += vSkin.y * VBPair.weight;
//pSkinPos[2] += vSkin.z * VBPair.weight;
}
//Update the shadow double
memcpy(&pSkinPosArray[lVtxStride*lVtxNum],pSkinPosArray,sizeof(float)*lVtxStride*lVtxNum);
for(int vtx=lVtxStride*lVtxNum + lVtxStride-1; vtx < lVtxStride*lVtxNum*2; vtx+=lVtxStride)
{
pSkinPosArray[vtx] = 0;
}
//Update buffer
mpDynVtxBuffer->UpdateData(eVertexFlag_Position | eVertexFlag_Normal | eVertexFlag_Texture1,false);
//Update triangles
cMath::CreateTriangleData(mvDynTriangles,
mpDynVtxBuffer->GetIndices(), mpDynVtxBuffer->GetIndexNum(),
pSkinPosArray, lVtxStride, lVtxNum);
}*/
}
iVertexBuffer *cSubMeshEntity::GetVertexBuffer() {
if (mpDynVtxBuffer) {
return mpDynVtxBuffer;
} else {
return mpSubMesh->GetVertexBuffer();
}
}
cBoundingVolume *cSubMeshEntity::GetBoundingVolume() {
return mpMeshEntity->GetBoundingVolume();
}
int cSubMeshEntity::GetMatrixUpdateCount() {
if (mpMeshEntity->HasNodes()) {
return GetTransformUpdateCount();
} else {
return mpMeshEntity->GetMatrixUpdateCount();
}
}
cMatrixf *cSubMeshEntity::GetModelMatrix(cCamera3D *apCamera) {
if (mpMeshEntity->HasNodes()) {
// Log("%s Matrix from local node!\n",msName.c_str());
return &GetWorldMatrix();
} else {
// Log("%s Matrix from mesh!\n",msName.c_str());
if (mpMeshEntity->IsStatic())
return NULL;
return mpMeshEntity->GetModelMatrix(NULL);
}
}
//-----------------------------------------------------------------------
void cSubMeshEntity::SetLocalNode(cNode3D *apNode) {
mpLocalNode = apNode;
mpLocalNode->AddEntity(this);
}
//-----------------------------------------------------------------------
cNode3D *cSubMeshEntity::GetLocalNode() {
return mpLocalNode;
}
//-----------------------------------------------------------------------
tRenderContainerDataList *cSubMeshEntity::GetRenderContainerDataList() {
// Log("Get from parent %s\n",mpMeshEntity->GetName().c_str());
return mpMeshEntity->GetRenderContainerDataList();
}
//-----------------------------------------------------------------------
void cSubMeshEntity::SetUpdateBody(bool abX) {
mbUpdateBody = abX;
/*if(mbUpdateBody)
{
AddCallback(mpEntityCallback);
}
else
{
RemoveCallback(mpEntityCallback);
}*/
}
bool cSubMeshEntity::GetUpdateBody() {
return mbUpdateBody;
}
//-----------------------------------------------------------------------
cTriangleData &cSubMeshEntity::GetTriangle(int alIndex) {
if (mpDynVtxBuffer)
return mvDynTriangles[alIndex];
else
return (*mpSubMesh->GetTriangleVecPtr())[alIndex];
}
int cSubMeshEntity::GetTriangleNum() {
if (mpDynVtxBuffer)
return (int)mvDynTriangles.size();
else
return (int)mpSubMesh->GetTriangleVecPtr()->size();
}
tTriangleDataVec *cSubMeshEntity::GetTriangleVecPtr() {
if (mpDynVtxBuffer)
return &mvDynTriangles;
else
return mpSubMesh->GetTriangleVecPtr();
}
//-----------------------------------------------------------------------
void cSubMeshEntity::SetCustomMaterial(iMaterial *apMaterial, bool abDestroyOldCustom) {
if (abDestroyOldCustom) {
if (mpMaterial)
mpMaterialManager->Destroy(mpMaterial);
}
mpMaterial = apMaterial;
}
//-----------------------------------------------------------------------
cSector *cSubMeshEntity::GetCurrentSector() const {
return mpMeshEntity->GetCurrentSector();
}
//-----------------------------------------------------------------------
//////////////////////////////////////////////////////////////////////////
// SAVE OBJECT STUFF
//////////////////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------
kBeginSerialize(cSaveData_cSubMeshEntity, cSaveData_iRenderable)
kSerializeVar(msMaterial, eSerializeType_String)
kSerializeVar(mbCastShadows, eSerializeType_Bool)
kSerializeVar(mlBodyId, eSerializeType_Int32)
kSerializeVar(mbUpdateBody, eSerializeType_Bool)
kEndSerialize()
//-----------------------------------------------------------------------
iSaveData *cSubMeshEntity::CreateSaveData() {
return hplNew(cSaveData_cSubMeshEntity, ());
}
//-----------------------------------------------------------------------
void cSubMeshEntity::SaveToSaveData(iSaveData *apSaveData) {
kSaveData_SaveToBegin(cSubMeshEntity);
kSaveData_SaveTo(mbCastShadows);
kSaveData_SaveTo(mbUpdateBody);
pData->msMaterial = mpMaterial == NULL ? "" : mpMaterial->GetName();
kSaveData_SaveObject(mpBody, mlBodyId);
}
//-----------------------------------------------------------------------
void cSubMeshEntity::LoadFromSaveData(iSaveData *apSaveData) {
kSaveData_LoadFromBegin(cSubMeshEntity);
kSaveData_LoadFrom(mbCastShadows);
kSaveData_LoadFrom(mbUpdateBody);
if (pData->msMaterial != "") {
iMaterial *pMat = mpMaterialManager->CreateMaterial(pData->msMaterial);
if (pMat)
SetCustomMaterial(pMat);
}
}
//-----------------------------------------------------------------------
void cSubMeshEntity::SaveDataSetup(cSaveObjectHandler *apSaveObjectHandler, cGame *apGame) {
kSaveData_SetupBegin(cSubMeshEntity);
kSaveData_LoadObject(mpBody, mlBodyId, iPhysicsBody *);
if (mpBody && mbUpdateBody == false) {
mpBody->CreateNode()->AddEntity(this);
}
}
//-----------------------------------------------------------------------
} // namespace hpl