/* 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/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(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; iGetVertexBonePair(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