|
|
//=========== Copyright � Valve Corporation, All rights reserved. ============//
//
// Purpose: Mesh class dmx loading functions
//
//===========================================================================//
#include "movieobjects/dmemodel.h"
#include "movieobjects/dmemesh.h"
#include "movieobjects/dmefaceset.h"
#include "movieobjects/dmematerial.h"
#include "movieobjects/dmeclip.h"
#include "movieobjects/dmechannel.h"
#include "movieobjects/dmeattachment.h"
#include "movieobjects/dmeanimationlist.h"
#include "movieobjects/dmecombinationoperator.h"
#include "mdlobjects/dmebbox.h"
#include "mdlobjects/dmelod.h"
#include "mdlobjects/dmelodlist.h"
#include "mdlobjects/dmebodygroup.h"
#include "mdlobjects/dmebodygrouplist.h"
#include "mdlobjects/dmehitbox.h"
#include "mdlobjects/dmehitboxset.h"
#include "mdlobjects/dmehitboxsetlist.h"
#include "mdlobjects/dmesequence.h"
#include "mdlobjects/dmesequencelist.h"
#include "mdlobjects/dmecollisionmodel.h"
#include "mdlobjects/dmecollisionjoints.h"
#include "mdlobjects/dmeincludemodellist.h"
#include "mdlobjects/dmedefinebone.h"
#include "mdlobjects/dmedefinebonelist.h"
#include "mdlobjects/dmematerialgroup.h"
#include "mdlobjects/dmematerialgrouplist.h"
#include "mdlobjects/dmeeyeball.h"
#include "mdlobjects/dmeeyeballglobals.h"
#include "mdlobjects/dmeboneweight.h"
#include "mdlobjects/dmebonemask.h"
#include "mdlobjects/dmebonemasklist.h"
#include "mdlobjects/dmeik.h"
#include "mdlobjects/dmeanimcmd.h"
#include "mdlobjects/dmemotioncontrol.h"
#include "mdlobjects/dmeposeparameter.h"
#include "mdlobjects/dmeposeparameterlist.h"
#include "mdlobjects/dmeanimblocksize.h"
#include "dmeutils/dmmeshutils.h"
#include "meshutils/mesh.h"
#define MAX_BIND_POSE_BONES 256
struct LoadMeshInfo_t { CDmeModel *m_pModel; float m_flScale; int *m_pBoneRemap; matrix3x4_t m_pBindPose[MAX_BIND_POSE_BONES]; };
class CDMXLoader { public: bool LoadVertices( CDmeDag *pDmeDag, CDmeVertexData *pBindState, const matrix3x4_t& mat, float flScale, int nBoneAssign, int *pBoneRemap, int nStartingUniqueCount ); bool LoadMesh( CDmeDag *pDmeDag, CDmeMesh *pMesh, CDmeVertexData *pBindState, const matrix3x4_t& mat, float flScale, int nBoneAssign, int *pBoneRemap ); bool LoadMesh( CDmeMesh *pMesh, CDmeDag *pDag, const matrix3x4_t &dagToBindPose = g_MatrixIdentity, float flScale = 1.0f, int nBoneAssign = -1, int *pBoneRemap = NULL, const CUtlVector< CUtlString > *pDeltaNames = NULL ); bool LoadMeshes( const LoadMeshInfo_t &info, CDmeDag *pDag, const matrix3x4_t &parentToBindPose, int nBoneAssign ); bool LoadMeshes( CDmeModel *pModel, float flScale, int *pBoneRemap = NULL ); bool LoadMaterialGroups( CDmeMaterialGroupList *pMaterialGroupList ); bool LoadDMX( const char *pDMXFile ); CUtlVector<CMesh*> &GetOutputMeshes() { return m_outputMeshes; } CUtlVector< CDmeDag* > &GetOutputDmeDags() { return m_OutputDmeDags; }
private: CUtlVector<CMesh*> m_outputMeshes; CUtlVector< CDmeDag* > m_OutputDmeDags; // DAG per mesh, when not collapsing meshes
};
void GenerateAttributesFromVertexData( CMeshVertexAttribute *pAttributes, int &nAttributes, int &nVertexStrideFloats, CDmeVertexData *pBindState ) { const CUtlVector<Vector> &positions = pBindState->GetPositionData( ); const CUtlVector<Vector> &normals = pBindState->GetNormalData( ); const CUtlVector<Vector2D> &texcoords = pBindState->GetTextureCoordData( ); const CUtlVector<Vector4D> &tangents = pBindState->GetTangentData( ); const CUtlVector<Color> &colors = pBindState->GetColorData();
int nLocalAttributes = 0; nVertexStrideFloats = 0; if ( positions.Count() ) { pAttributes[ nLocalAttributes ].m_nOffsetFloats = nVertexStrideFloats; pAttributes[ nLocalAttributes ].m_nType = VERTEX_ELEMENT_POSITION; nLocalAttributes ++; nVertexStrideFloats += 3;
if ( nLocalAttributes > nAttributes ) goto endGetAttributes; } if ( normals.Count() ) { pAttributes[ nLocalAttributes ].m_nOffsetFloats = nVertexStrideFloats; pAttributes[ nLocalAttributes ].m_nType = VERTEX_ELEMENT_NORMAL; nLocalAttributes ++; nVertexStrideFloats += 3;
if ( nLocalAttributes > nAttributes ) goto endGetAttributes; } if ( texcoords.Count() ) { pAttributes[ nLocalAttributes ].m_nOffsetFloats = nVertexStrideFloats; pAttributes[ nLocalAttributes ].m_nType = VERTEX_ELEMENT_TEXCOORD2D_0; nLocalAttributes ++; nVertexStrideFloats += 2;
if ( nLocalAttributes > nAttributes ) goto endGetAttributes; } if ( tangents.Count() ) { pAttributes[ nLocalAttributes ].m_nOffsetFloats = nVertexStrideFloats; pAttributes[ nLocalAttributes ].m_nType = VERTEX_ELEMENT_TANGENT_WITH_FLIP; nLocalAttributes ++; nVertexStrideFloats += 4;
if ( nLocalAttributes > nAttributes ) goto endGetAttributes; } if ( colors.Count() ) { pAttributes[ nLocalAttributes ].m_nOffsetFloats = nVertexStrideFloats; pAttributes[ nLocalAttributes ].m_nType = VERTEX_ELEMENT_COLOR; nLocalAttributes ++; nVertexStrideFloats += 1;
if ( nLocalAttributes > nAttributes ) goto endGetAttributes; }
endGetAttributes: nAttributes = nLocalAttributes; }
//-----------------------------------------------------------------------------
// Convert a single CDmeMesh to a CMesh
//-----------------------------------------------------------------------------
bool ConvertMeshFromDMX( CMesh *pMeshOut, CDmeMesh *pDmeMesh ) { if ( !pMeshOut || !pDmeMesh ) return false;
CDmeVertexData *pDmeBindBaseState = pDmeMesh->GetBindBaseState(); if ( !pDmeBindBaseState ) return false;
CDmeDag *pDmeDag = pDmeMesh->GetParent(); if ( !pDmeDag ) return false;
matrix3x4_t mShapeToWorld; pDmeDag->GetShapeToWorldTransform( mShapeToWorld );
CDMXLoader dmxLoader; if ( dmxLoader.LoadMesh( pDmeDag, pDmeMesh, pDmeBindBaseState, mShapeToWorld, 1.0f, 0, NULL ) ) { CUtlVector< CMesh * > &outputMeshes = dmxLoader.GetOutputMeshes(); if ( outputMeshes.Count() <= 0 ) return false;
DuplicateMesh( pMeshOut, *outputMeshes[0] ); return true; }
return false; }
//-----------------------------------------------------------------------------
// Reads the mesh data from the DMX data
//-----------------------------------------------------------------------------
bool CDMXLoader::LoadMesh( CDmeDag *pDmeDag, CDmeMesh *pMesh, CDmeVertexData *pBindState, const matrix3x4_t& mat, float flScale, int nBoneAssign, int *pBoneRemap ) { matrix3x4_t normalMat; MatrixInverseTranspose( mat, normalMat );
const CUtlVector<Vector> &positions = pBindState->GetPositionData( ); const CUtlVector<Vector> &normals = pBindState->GetNormalData( ); const CUtlVector<Vector2D> &texcoords = pBindState->GetTextureCoordData( ); const CUtlVector<Vector4D> &tangents = pBindState->GetTangentData( ); const CUtlVector<Color> &colors = pBindState->GetColorData();
bool bHasPosition = ( positions.Count() > 0 ); bool bHasNormal = ( normals.Count() > 0 ); bool bHasTexcoord = ( texcoords.Count() > 0 ); bool bHasTangent = ( tangents.Count() > 0 ); bool bHasColor = ( colors.Count() > 0 ); bool bFlipVCoordinate = pBindState->IsVCoordinateFlipped();
int nAttributes = 32; int nVertexStrideFloats = 0; CMeshVertexAttribute attributes[ 32 ]; GenerateAttributesFromVertexData( attributes, nAttributes, nVertexStrideFloats, pBindState ); // Create a mesh per face set
int nFaceSetCount = pMesh->FaceSetCount(); for ( int i = 0; i < nFaceSetCount; ++i ) { CDmeFaceSet *pFaceSet = pMesh->GetFaceSet( i ); CDmeMaterial *pMaterial = pFaceSet->GetMaterial();
int nIndexCount = pFaceSet->NumIndices(); int nTrueIndexCount = 0; int nFirstIndex = 0; while( nFirstIndex < nIndexCount ) { int nVertexCount = pFaceSet->GetNextPolygonVertexCount( nFirstIndex ); int nOutCount = ( nVertexCount - 2 ) * 3; nTrueIndexCount += nOutCount; nFirstIndex += nVertexCount + 1; }
// Each face set is an individual mesh for now
CMesh *pUtilMesh = new CMesh();
// Allocate space for VB/IB/Attrib
pUtilMesh->AllocateMesh( nTrueIndexCount, nTrueIndexCount, nVertexStrideFloats, attributes, nAttributes ); uint32 *pUtilIndices = pUtilMesh->m_pIndices; int nUtilIndices = 0;
// Set material name
pUtilMesh->m_materialName = pMaterial->GetMaterialName();
// Set vertices and indices
nFirstIndex = 0; while( nFirstIndex < nIndexCount ) { int nVertexCount = pFaceSet->GetNextPolygonVertexCount( nFirstIndex ); if ( nVertexCount >= 3 ) { int nOutCount = ( nVertexCount - 2 ) * 3; int pIndices[ 128 ];// = ( int* )_alloca( nOutCount * sizeof( uint32 ) );
Assert( nOutCount <= 128 ); pMesh->ComputeTriangulatedIndices( pBindState, pFaceSet, nFirstIndex, pIndices, nOutCount );
for ( int i=0; i<nOutCount; ++i ) { float *pUtilVertex = pUtilMesh->GetVertex( nUtilIndices ); int nVertexOffset = 0; int iVertIndex = pIndices[ i ];
if ( bHasPosition ) { int nI = pBindState->GetPositionIndex( iVertIndex );
Vector vTrans; VectorTransform( positions[ nI ], mat, vTrans ); vTrans *= flScale;
Q_memcpy( pUtilVertex + nVertexOffset, &vTrans, sizeof( Vector ) ); nVertexOffset += 3; }
if ( bHasNormal ) { int nI = pBindState->GetNormalIndex( iVertIndex );
Vector vTrans; VectorRotate( normals[ nI ], normalMat, vTrans ); VectorNormalize( vTrans );
Q_memcpy( pUtilVertex + nVertexOffset, &vTrans, sizeof( Vector ) ); nVertexOffset += 3; }
if ( bHasTexcoord ) { int nI = pBindState->GetTexCoordIndex( iVertIndex );
Vector2D vTrans; vTrans = texcoords[ nI ]; if ( bFlipVCoordinate ) { vTrans.y = 1.0f - vTrans.y; }
Q_memcpy( pUtilVertex + nVertexOffset, &vTrans, sizeof( Vector2D ) ); nVertexOffset += 2; }
if ( bHasTangent ) { int nI = pBindState->GetTangentIndex( iVertIndex );
Vector vTrans; VectorRotate( tangents[ nI ].AsVector3D(), normalMat, vTrans ); VectorNormalize( vTrans ); Vector4D vTrans4D( vTrans.x, vTrans.y, vTrans.z, tangents[ nI ].w );
Q_memcpy( pUtilVertex + nVertexOffset, &vTrans4D, sizeof( Vector4D ) ); nVertexOffset += 4; }
if ( bHasColor ) { int nI = pBindState->GetColorIndex( iVertIndex ); Q_memcpy( pUtilVertex + nVertexOffset, &colors[ nI ], sizeof( uint32 ) ); nVertexOffset += 1; }
pUtilIndices[ nUtilIndices ] = nUtilIndices; nUtilIndices ++; } }
nFirstIndex += nVertexCount + 1; // -1 between faces, to skip over it
}
// Clean and weld the mesh
float flEpsilon = 1e-6; float *pEpsilons = new float[ pUtilMesh->m_nVertexStrideFloats ]; for ( int e=0; e<pUtilMesh->m_nVertexStrideFloats; ++e ) { pEpsilons[ e ] = flEpsilon; } CMesh tempMesh; WeldVertices( &tempMesh, *pUtilMesh, pEpsilons, pUtilMesh->m_nVertexStrideFloats ); delete []pEpsilons; pUtilMesh->FreeAllMemory(); CleanMesh( pUtilMesh, tempMesh ); tempMesh.FreeAllMemory();
m_OutputDmeDags.AddToTail( pDmeDag ); m_outputMeshes.AddToTail( pUtilMesh ); }
return true; }
//-----------------------------------------------------------------------------
// Method used to add mesh data
//-----------------------------------------------------------------------------
bool CDMXLoader::LoadMeshes( const LoadMeshInfo_t &info, CDmeDag *pDag, const matrix3x4_t &parentToBindPose, int nBoneAssign ) { // We want to create an aggregate matrix transforming from this dag to its closest
// parent which actually is an animated joint. This is done so we can autoskin
// meshes to their closest parents if they have not been skinned.
matrix3x4_t dagToBindPose; int nFoundIndex = info.m_pModel->GetJointIndex( pDag ); if ( nFoundIndex >= 0 /* && ( pDag == info.m_pModel || CastElement< CDmeJoint >( pDag ) ) */ ) { nBoneAssign = nFoundIndex; }
if ( nFoundIndex >= 0 ) { ConcatTransforms( parentToBindPose, info.m_pBindPose[nFoundIndex], dagToBindPose ); } else { // NOTE: This isn't particularly kosher; we're using the current pose instead of the bind pose
// because there's no transform in the bind pose
matrix3x4_t dagToParent; pDag->GetTransform()->GetTransform( dagToParent ); ConcatTransforms( parentToBindPose, dagToParent, dagToBindPose ); }
CDmeMesh *pMesh = CastElement< CDmeMesh >( pDag->GetShape() ); if ( pMesh ) { CDmeVertexData *pBindState = pMesh->FindBaseState( "bind" ); if ( !pBindState ) return false;
if ( !LoadMesh( pDag, pMesh, pBindState, dagToBindPose, info.m_flScale, nBoneAssign, info.m_pBoneRemap ) ) return false; }
int nCount = pDag->GetChildCount(); for ( int i = 0; i < nCount; ++i ) { CDmeDag *pChild = pDag->GetChild( i ); if ( !LoadMeshes( info, pChild, dagToBindPose, nBoneAssign ) ) return false; }
return true; }
//-----------------------------------------------------------------------------
// Method used to add mesh data
//-----------------------------------------------------------------------------
bool CDMXLoader::LoadMeshes( CDmeModel *pModel, float flScale, int *pBoneRemap ) { matrix3x4_t mat; SetIdentityMatrix( mat );
LoadMeshInfo_t info; info.m_pModel = pModel; info.m_flScale = flScale; info.m_pBoneRemap = pBoneRemap;
CDmeTransformList *pBindPose = pModel->FindBaseState( "bind" ); int nCount = pBindPose ? pBindPose->GetTransformCount() : pModel->GetJointCount(); for ( int i = 0; i < nCount; ++i ) { CDmeTransform *pTransform = pBindPose ? pBindPose->GetTransform(i) : pModel->GetJointTransform(i);
matrix3x4_t jointTransform; pTransform->GetTransform( info.m_pBindPose[i] ); }
int nChildCount = pModel->GetChildCount(); for ( int i = 0; i < nChildCount; ++i ) { CDmeDag *pChild = pModel->GetChild( i ); if ( !LoadMeshes( info, pChild, mat, -1 ) ) return false; }
return true; }
bool CDMXLoader::LoadDMX( const char *pDMXFile ) { bool bRet = true; DmFileId_t fileId;
// When reading, keep the CRLF; this will make ReadFile read it in binary format
// and also append a couple 0s to the end of the buffer.
CDmElement *pRoot; if ( g_pDataModel->RestoreFromFile( pDMXFile, NULL, NULL, &pRoot ) == DMFILEID_INVALID ) return false;
CDmeModel *pModel = pRoot->GetValueElement< CDmeModel >( "model" ); if ( !LoadMeshes( pModel, 1.0f, NULL ) ) return false;
fileId = pRoot->GetFileId(); g_pDataModel->RemoveFileId( fileId ); return bRet; }
//-----------------------------------------------------------------------------
// Main entry point for loading DMX files
//-----------------------------------------------------------------------------
bool LoadMeshesFromDMX( CUtlVector<CMesh*> &outputMeshes, const char *pDMXFile ) { CDMXLoader loader; if ( !loader.LoadDMX( pDMXFile ) ) return false;
CUtlVector<CMesh*> &meshList = loader.GetOutputMeshes(); outputMeshes.AddVectorToTail( meshList );
return true; }
bool LoadMeshes( CUtlVector<CMesh*> &outputMeshes, CDmeModel *pModel, float flScale, int *pBoneRemap ) { CDMXLoader loader; if ( !loader.LoadMeshes( pModel, flScale, pBoneRemap ) ) return false;
CUtlVector<CMesh*> &meshList = loader.GetOutputMeshes(); outputMeshes.AddVectorToTail( meshList ); return true; }
// A stub that loads mesh without splitting position stream by tangent/uv/etc. in source2; here it just loads the mesh as usual
bool LoadCollisionMeshes( CUtlVector< CMesh* > &outputMeshes, CUtlVector< CDmeDag* > &outputDags, CDmeModel *pModel, float flScale ) { CDMXLoader loader;//( OUTPUT_MESH_TRIANGLE_MESH );
//loader.SetFieldFilter( nFieldMask );
//loader.SetCollapseDeltaMeshes( true );
//loader.SetKeepPositionConnectivity( true ); // this setting will make loader not merge/split vertices we don't, thus maintaining original connectivity of the mesh
//loader.SetCollapseMeshesByMaterial( false );
if ( !loader.LoadMeshes( pModel, flScale ) ) { return false; }
outputMeshes.AddVectorToTail( loader.GetOutputMeshes() ); outputDags.AddVectorToTail( loader.GetOutputDmeDags() ); return true; }
|