This commit is contained in:
nephacks
2025-06-04 03:22:50 +02:00
parent f234f23848
commit f12416cffd
14243 changed files with 6446499 additions and 26 deletions
File diff suppressed because it is too large Load Diff
+98
View File
@@ -0,0 +1,98 @@
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//
//=============================================================================//
#include <windows.h>
#include <STDIO.H>
int
ReadBmpFile(
char* szFile,
unsigned char** ppbPalette,
unsigned char** ppbBits,
int *pwidth,
int *pheight)
{
int rc = 0;
FILE *pfile = NULL;
BITMAPFILEHEADER bmfh;
BITMAPINFOHEADER bmih;
RGBQUAD rgrgbPalette[256];
ULONG cbPalBytes;
ULONG cbBmpBits;
BYTE* pbBmpBits;
// Bogus parameter check
if (!(ppbPalette != NULL && ppbBits != NULL))
{ rc = -1000; goto GetOut; }
// File exists?
if ((pfile = fopen(szFile, "rb")) == NULL)
{ rc = -1; goto GetOut; }
// Read file header
if (fread(&bmfh, sizeof bmfh, 1/*count*/, pfile) != 1)
{ rc = -2; goto GetOut; }
// Bogus file header check
if (!(bmfh.bfReserved1 == 0 && bmfh.bfReserved2 == 0))
{ rc = -2000; goto GetOut; }
// Read info header
if (fread(&bmih, sizeof bmih, 1/*count*/, pfile) != 1)
{ rc = -3; goto GetOut; }
// Bogus info header check
if (!(bmih.biSize == sizeof bmih && bmih.biPlanes == 1))
{ rc = -3000; goto GetOut; }
// Bogus bit depth? Only 8-bit supported.
if (bmih.biBitCount != 8)
{ rc = -4; goto GetOut; }
// Bogus compression? Only non-compressed supported.
if (bmih.biCompression != BI_RGB)
{ rc = -5; goto GetOut; }
// Figure out how many entires are actually in the table
if (bmih.biClrUsed == 0)
{
cbPalBytes = (1 << bmih.biBitCount) * sizeof( RGBQUAD );
}
else
{
cbPalBytes = bmih.biClrUsed * sizeof( RGBQUAD );
}
// Read palette (256 entries)
if (fread(rgrgbPalette, cbPalBytes, 1/*count*/, pfile) != 1)
{ rc = -6; goto GetOut; }
// Read bitmap bits (remainder of file)
cbBmpBits = bmfh.bfSize - ftell(pfile);
pbBmpBits = (BYTE *)malloc(cbBmpBits);
if (fread(pbBmpBits, cbBmpBits, 1/*count*/, pfile) != 1)
{ rc = -7; goto GetOut; }
// Set output parameters
*ppbPalette = (BYTE *)malloc(sizeof rgrgbPalette);
memcpy(*ppbPalette, rgrgbPalette, cbPalBytes);
*ppbBits = pbBmpBits;
*pwidth = bmih.biWidth;
*pheight = bmih.biHeight;
printf("w %d h %d s %d\n",bmih.biWidth, bmih.biHeight, cbBmpBits );
GetOut:
if (pfile) fclose(pfile);
return rc;
}
File diff suppressed because it is too large Load Diff
+40
View File
@@ -0,0 +1,40 @@
//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======//
//
// Purpose:
//
// $Workfile: $
// $Date: $
//
//-----------------------------------------------------------------------------
// $Log: $
//
// $NoKeywords: $
//===========================================================================//
#ifndef COLLISIONMODEL_H
#define COLLISIONMODEL_H
#ifdef _WIN32
#pragma once
#endif
class CDmElement;
struct s_source_t;
void Cmd_CollisionText( void );
int DoCollisionModel( bool separateJoints );
#ifdef MDLCOMPILE
int DoCollisionModel( s_source_t *pSource, CDmElement *pInfo, bool bStaticProp );
void LoadCollisionText( const char *pszCollisionText );
#endif MDLCOMPILE
// execute after simplification, before writing
void CollisionModel_Build( void );
// execute during writing
extern void CollisionModel_Write( long checkSum );
extern void CollisionModel_SetName( const char *pName );
void CollisionModel_ExpandBBox( Vector &mins, Vector &maxs );
#endif // COLLISIONMODEL_H
+350
View File
@@ -0,0 +1,350 @@
//========= Copyright c 1996-2008, Valve Corporation, All rights reserved. ============//
//
// Purpose: Builds physics2 collision models from studio model source
//
// $Workfile: $
// $Date: $
// $NoKeywords: $
//=============================================================================//
#include "studiomdl.h"
#include "collisionmodelsource.h"
//-----------------------------------------------------------------------------
// Purpose: Transforms the source's verts into "world" space
// Input : *psource -
// *worldVerts -
//-----------------------------------------------------------------------------
void CCollisionModelSource::ConvertToWorldSpace( CUtlVector<Vector> &worldVerts, s_source_t *pmodel )
{
int i, n;
if (!m_bAssumeWorldspace)
{
matrix3x4_t boneToWorld[MAXSTUDIOSRCBONES]; // bone transformation matrix
CalcBoneTransforms( g_panimation[0], 0, boneToWorld );
for (i = 0; i < pmodel->numvertices; i++)
{
Vector tmp,tmp2;
worldVerts[i].Init(0,0,0 );
int nBoneCount = pmodel->vertex[i].boneweight.numbones;
for (n = 0; n < nBoneCount; n++)
{
// convert to Half-Life world space
// convert vertex into original models' bone local space
int localBone = pmodel->vertex[i].boneweight.bone[n];
int globalBone = pmodel->boneLocalToGlobal[localBone];
Assert( localBone >= 0 );
Assert( globalBone >= 0 );
matrix3x4_t boneToPose;
ConcatTransforms( pmodel->boneToPose[localBone], g_bonetable[globalBone].srcRealign, boneToPose );
VectorITransform( pmodel->vertex[i].position, boneToPose, tmp2 );
// now transform to that bone's world-space position in this animation
VectorTransform(tmp2, boneToWorld[globalBone], tmp );
VectorMA( worldVerts[i], pmodel->vertex[i].boneweight.weight[n], tmp, worldVerts[i] );
}
}
}
else
{
matrix3x4_t srcBoneToWorld[MAXSTUDIOSRCBONES]; // bone transformation matrix
BuildRawTransforms( pmodel, "BindPose", 0, pmodel->scale, pmodel->adjust, pmodel->rotation, 0, srcBoneToWorld );
for (i = 0; i < pmodel->numvertices; i++)
{
Vector tmp;
worldVerts[i].Init( 0, 0, 0 );
int nBoneCount = pmodel->vertex[i].boneweight.numbones;
for (n = 0; n < nBoneCount; n++)
{
int localBone = pmodel->vertex[i].boneweight.bone[n];
Assert( localBone >= 0 );
// convert vertex into world space
VectorTransform( pmodel->vertex[i].position, srcBoneToWorld[localBone], tmp );
// just assume the model is in identity space
// FIXME: shouldn't this do an inverse xform of the default boneToWorld?
VectorMA( worldVerts[i], pmodel->vertex[i].boneweight.weight[n], tmp, worldVerts[i] );
}
}
}
if ( g_flCollisionPrecision > 0 )
{
#ifdef DEBUG
printf("Applying collision precision truncation: %f\n", g_flCollisionPrecision );
#endif
for ( int i = 0; i < worldVerts.Count(); i++ )
{
worldVerts[i].x -= fmod( worldVerts[i].x, g_flCollisionPrecision );
worldVerts[i].y -= fmod( worldVerts[i].y, g_flCollisionPrecision );
worldVerts[i].z -= fmod( worldVerts[i].z, g_flCollisionPrecision );
}
}
}
//-----------------------------------------------------------------------------
// Purpose: Transforms the set of verts into the space of a particular bone
// Input : *psource -
// boneIndex -
// *boneVerts -
//-----------------------------------------------------------------------------
void CCollisionModelSource::ConvertToBoneSpace( int boneIndex, CUtlVector<Vector> &boneVerts )
{
int i;
int remapIndex = m_pModel->boneLocalToGlobal[boneIndex];
matrix3x4_t boneToPose;
if ( remapIndex < 0 )
{
MdlWarning("Error! physics for unused bone %s\n", m_pModel->localBone[boneIndex].name );
MatrixCopy( m_pModel->boneToPose[boneIndex], boneToPose );
}
else
{
ConcatTransforms( m_pModel->boneToPose[boneIndex], g_bonetable[remapIndex].srcRealign, boneToPose );
}
for (i = 0; i < m_pModel->numvertices; i++)
{
VectorITransform(m_pModel->vertex[i].position, boneToPose, boneVerts[i] );
}
}
bool CCollisionModelSource::ShouldProcessBone( int boneIndex )
{
if ( boneIndex >= 0 )
{
if ( m_bonemap[boneIndex] == boneIndex )
return true;
}
return false;
}
// called before processing, after the model has been simplified.
// Update internal state due to simplification
void CCollisionModelSource::Simplify()
{
if ( m_pModel )
{
for ( int i = 0; i < m_pModel->numbones; i++ )
{
if ( m_pModel->boneLocalToGlobal[i] < 0 )
{
SkipBone(i);
}
// Walk the parents of this bone, if they map to the same global bone then go ahead and
// merge them now so we can aggregate the collision models
int nMatchingParent = i;
int nParentCheck = m_pModel->localBone[nMatchingParent].parent;
int nGlobalMatch = m_pModel->boneLocalToGlobal[i];
while ( nParentCheck >= 0 && m_pModel->boneLocalToGlobal[nParentCheck] == nGlobalMatch )
{
nMatchingParent = nParentCheck;
nParentCheck = m_pModel->localBone[nParentCheck].parent;
}
if ( nMatchingParent != i )
{
MergeBones( nMatchingParent, i );
}
}
}
extern int g_rootIndex;
const char *pAnimationRootBone = g_bonetable[g_rootIndex].name;
// merge this root bone with the root of animation
MergeBones( pAnimationRootBone, m_rootName );
}
void CCollisionModelSource::SkipBone( int boneIndex )
{
if ( boneIndex >= 0 )
m_bonemap[boneIndex] = -1;
}
void CCollisionModelSource::InitBoneMap( void )
{
m_bonemap.SetSize(m_pModel->numbones);
for ( int i = 0; i < m_pModel->numbones; i++ )
{
m_bonemap[i] = i;
}
}
void CCollisionModelSource::MergeBones( int parent, int child )
{
if ( parent < 0 || child < 0 )
return;
int map = parent;
int safety = 0;
while ( m_bonemap[map] != map )
{
map = m_bonemap[map];
safety++;
// infinite loop?
if ( safety > m_pModel->numbones )
break;
if ( map < 0 )
break;
}
m_bonemap[child] = map;
}
void CCollisionModelSource::MergeBones(const char *parent, const char *child)
{
MergeBones(FindLocalBoneNamed( parent ), FindLocalBoneNamed( child ));
}
//-----------------------------------------------------------------------------
// Purpose: Search a source for a bone with a specified name
// Input : *pSource -
// *pName -
// Output : int boneIndex, -1 if none
//-----------------------------------------------------------------------------
int FindLocalBoneNamed( const s_source_t *pSource, const char *pName )
{
if ( pName && pSource )
{
int i;
for ( i = 0; i < pSource->numbones; i++ )
{
if ( !stricmp( pName, pSource->localBone[i].name ) )
return i;
}
pName = RenameBone( pName );
for ( i = 0; i < pSource->numbones; i++ )
{
if ( !stricmp( pName, pSource->localBone[i].name ) )
return i;
}
}
return -1;
}
int CCollisionModelSource::FindLocalBoneNamed( const char *pName )
{
return ::FindLocalBoneNamed(m_pModel, pName);
}
//-----------------------------------------------------------------------------
// Purpose: Test this face to see if any of its verts are assigned to a particular bone
// *pmodel -
// *face -
// boneIndex -
// Output : Returns true if this face has a vert assigned to boneIndex
//-----------------------------------------------------------------------------
bool CCollisionModelSource::FaceHasVertOnBone( const s_face_t &face, int boneIndex )
{
if ( boneIndex < 0 )
return true;
int j;
s_boneweight_t *pweight;
pweight = &m_pModel->vertex[ face.a ].boneweight;
for ( j = 0; j < pweight->numbones; j++ )
{
// assigned to boneIndex?
if ( RemapBone( pweight->bone[j] ) == boneIndex )
return true;
}
pweight = &m_pModel->vertex[ face.b ].boneweight;
for ( j = 0; j < pweight->numbones; j++ )
{
// assigned to boneIndex?
if ( RemapBone( pweight->bone[j] ) == boneIndex )
return true;
}
pweight = &m_pModel->vertex[ face.c ].boneweight;
for ( j = 0; j < pweight->numbones; j++ )
{
// assigned to boneIndex?
if ( RemapBone( pweight->bone[j] ) == boneIndex )
return true;
}
return false;
}
int CCollisionModelSource::RemapBone( int boneIndex ) const
{
if ( boneIndex >= 0 )
return m_bonemap[boneIndex];
return boneIndex;
}
s_face_t CCollisionModelSource::GetGlobalFace( s_mesh_t *pMesh, int nFace )
{
s_face_t output;
GlobalFace(&output, pMesh, m_pModel->face + pMesh->faceoffset + nFace);
return output;
}
void CCollisionModelSource::FindBoundBones(s_mesh_t *pMesh, CUtlVector<int>&setBones)
{
s_face_t *pFaces = m_pModel->face + pMesh->faceoffset;
s_vertexinfo_t *pVertices = m_pModel->vertex + pMesh->vertexoffset;
for ( int nFace = 0; nFace < pMesh->numfaces; nFace++ )
{
FindBoundBones(pVertices[pFaces[nFace].a].boneweight, setBones);
FindBoundBones(pVertices[pFaces[nFace].b].boneweight, setBones);
FindBoundBones(pVertices[pFaces[nFace].c].boneweight, setBones);
}
}
void CCollisionModelSource::FindBoundBones(s_boneweight_t &weights, CUtlVector<int>&setBones)
{
for(int nBoundBone = 0; nBoundBone < weights.numbones; ++nBoundBone)
{ int boneIndex = RemapBone(weights.bone[nBoundBone]);
if(!setBones.HasElement(boneIndex))
setBones.AddToTail(boneIndex);
}
}
//-----------------------------------------------------------------------------
// Purpose: Fixup the pointers in this face to reference the mesh globally (source relative)
// (faces are mesh relative, each source has several meshes)
// Input : *pout -
// *pmesh -
// *pin -
//-----------------------------------------------------------------------------
void GlobalFace( s_face_t *pout, s_mesh_t *pmesh, s_face_t *pin )
{
pout->a = pmesh->vertexoffset + pin->a;
pout->b = pmesh->vertexoffset + pin->b;
pout->c = pmesh->vertexoffset + pin->c;
}
+72
View File
@@ -0,0 +1,72 @@
//========= Copyright c 1996-2008, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $Workfile: $
// $Date: $
//
//-----------------------------------------------------------------------------
// $Log: $
//
// $NoKeywords: $
//=============================================================================//
#ifndef COLLISION_MODEL_OPTIONS_H
#define COLLISION_MODEL_OPTIONS_H
#include "tier1/utlvector.h"
#define MAX_EXTRA_COLLISION_MODELS 24
struct extramodel_t
{
struct s_source_t *m_pSrc;
matrix3x4_t m_matOffset;
bool m_bConcave;
};
class CCollisionModelSource
{
public:
struct s_source_t *m_pModel;
extramodel_t m_ExtraModels[MAX_EXTRA_COLLISION_MODELS+1];
bool m_isJointed;
bool m_bAssumeWorldspace; // assume the model is already declared in worldspace, regardless of bone names
bool m_allowConcave;
int m_maxConvex;
char * m_pOverrideName;
CUtlVector<int> m_bonemap/*[MAXSTUDIOSRCBONES]*/;
char m_rootName[128];
bool m_allowConcaveJoints;
bool m_bRootCollisionIsEmpty;
public:
void ConvertToWorldSpace(CUtlVector<Vector> &worldVerts, s_source_t *pmodel);
void ConvertToBoneSpace( int boneIndex, CUtlVector<Vector> &boneVerts );
bool ShouldProcessBone( int boneIndex );
void Simplify();
void SkipBone( int boneIndex );
void InitBoneMap( void );
void MergeBones( int parent, int child );
void MergeBones(const char *parent, const char *child);
int FindLocalBoneNamed( const char *pName );
bool FaceHasVertOnBone( const struct s_face_t &face, int boneIndex );
s_face_t GetGlobalFace( struct s_mesh_t *pMesh, int nFace );
void FindBoundBones(struct s_mesh_t *pMesh, CUtlVector<int>&setBones);
void FindBoundBones(struct s_boneweight_t &weights, CUtlVector<int>&setBones);
int RemapBone( int boneIndex ) const;
};
// list of vertex indices that form a convex element
struct convexlist_t
{
int firstVertIndex;
int numVertIndex;
};
// Purpose: Fixup the pointers in this face to reference the mesh globally (source relative)
// (faces are mesh relative, each source has several meshes)
extern void GlobalFace( s_face_t *pout, s_mesh_t *pmesh, s_face_t *pin );
#endif
File diff suppressed because it is too large Load Diff
+437
View File
@@ -0,0 +1,437 @@
//============ Copyright (c) Valve Corporation, All rights reserved. ==========
#ifndef CLOTHPROXYCOMPILER_HDR
#define CLOTHPROXYCOMPILER_HDR
#include "movieobjects/dmevertexdata.h"
#include "bitvec.h"
#include "tier1/utlstringmap.h"
#include "mdlobjects/authphysfx.h"
#include "mdlobjects/clothproxymesh.h"
#include "movieobjects/dmefaceset.h"
#include "meshutils/mesh.h"
#include "movieobjects/dmemeshtypes.h"
class CDmeModel;
class CAuthPhysFx;
class CVClothProxyMesh;
class CDmeDag;
class CClothProxyCompiler
{
public:
CVClothProxyMeshOptions m_Options;
CClothProxyCompiler( CAuthPhysFx *pAuthFx );
~CClothProxyCompiler(){}
bool IsEmpty() const
{
return m_pAuthFx->m_Nodes.Count() == 0;
}
CAuthPhysFx *GetFx() { return m_pAuthFx; }
void Init( const CVClothProxyMeshOptions &clothProxyMeshList );
int GetOrCreateClothRootBone();
void Append( CDmeModel *pModel, float flClothEnableThreshold, const CVClothProxyMesh &proxy);
void AppendPlaneCollision( CDmeModel *pModel );
void Cook( );
CLockedResource< PhysFeModelDesc_t > Compile( CResourceStream *pStream )const;
void MarkFreeRotatingNodes( const CAuthPhysFx::CQuad &quad );
void AlignNodes();
int GetAuthFxBone( const char *pName );
template <typename T>
class CIndexedAttr
{
public:
CDmrArrayConst< T > m_Data;
CDmrArrayConst< int > m_IndexData;
public:
CIndexedAttr() {}
CIndexedAttr( CDmeVertexData *pBindState, FieldIndex_t nField )
{
Init( pBindState, nField );
}
CIndexedAttr( CDmeVertexData *pBindState, CDmeVertexDataBase::StandardFields_t nField )
{
Init( pBindState, nField );
}
void Init( CDmeVertexData *pBindState, CDmeVertexDataBase::StandardFields_t nField )
{
Init( pBindState, pBindState->FindFieldIndex( nField ) );
}
CIndexedAttr( CDmeVertexData *pBindState, const char *pField )
{
Init( pBindState, pBindState->FindFieldIndex( pField ) );
}
void Init( CDmeVertexData *pBindState, FieldIndex_t nField )
{
if ( nField >= 0 )
{
m_Data = pBindState->GetVertexData( nField );
m_IndexData = pBindState->GetIndexData( nField );
}
}
operator bool() const { return m_IndexData.IsValid() && m_Data.IsValid() && m_Data.Count() > 0 && m_IndexData.Count() > 0; }
const T& operator []( int i ) const { return m_Data[ m_IndexData[ i ] ]; }
int GetDataCount() const{ return m_Data.Count(); }
int GetElementCount()const { return m_IndexData.Count(); } // not really vertex count
int GetAttrCount()const { return 1; } // TODO: find the number of attributes per vertex
void Reset()
{
m_Data = CDmrArrayConst< T >();
m_IndexData = CDmrArrayConst< int >();
}
};
struct Binding_t
{
int nAuthFxBone;
float flWeight;
Binding_t() : nAuthFxBone( -1 ), flWeight( 0 ) {}
};
struct ProjItem_t
{
ProjItem_t( int i = -1, float f = 0 ) : nIndex( i ), flEnvelope( f ){}
ProjItem_t( const ProjItem_t &other ) : nIndex( other.nIndex ), flEnvelope( other.flEnvelope ){}
int nIndex;
float flEnvelope;
};
struct QuadProjection_t
{
Vector m_vContact; // projected point on the quad
Vector m_vNormal;
float m_flDistance; // negative if behind the plane
int m_nBindings;
Binding_t m_Binding[ 4 ];
bool IsEmpty() const { return m_nBindings == 0; }
Vector GetOriginalPoint()const { return m_vContact + m_vNormal * m_flDistance; }
void AddBinding( uint nIndex, float flWeight )
{
Binding_t &b = m_Binding[ m_nBindings++ ];
b.nAuthFxBone = nIndex;
b.flWeight = flWeight;
}
bool operator < ( const QuadProjection_t &other )const
{
return m_flDistance < other.m_flDistance;
}
};
class CModelContext
{
public:
CModelContext( CClothProxyCompiler *pCompiler, CDmeModel *pModel, float flClothEnableThreshold, const CVClothProxyMesh &proxy );
int MapJointToFxBone( int nJoint, bool bSimulated );
int GetJointCount()const { return m_JointToFxBone.Count(); }
public:
CDmeModel *m_pModel;
CUtlVector< UtlSymId_t > m_JointToBoneSubset;
CUtlVector< int > m_JointToFxBone;
CAuthPhysFx * m_pAuthFx;
float m_flClothEnableThreshold;
const CVClothProxyMesh &m_Proxy;
};
friend class CModelContext;
class CMeshContext
{
public:
CMeshContext( CClothProxyCompiler *pCompiler, CModelContext *pModelContext, CDmeMesh *pMesh, const matrix3x4_t &tm, int nDmeMesh );
public:
int m_nDmeMesh;
CDmeMesh *m_pDmeMesh;
matrix3x4_t m_MeshTransform;
CDmeVertexData *m_pBindState;
CIndexedAttr< Vector > m_AttrPos, m_AttrNormal, m_AttrTangent;
CAuthPhysFx *m_pAuthFx;
CModelContext *m_pModelContext;
CUtlVector< int > m_DmePosToFxBone; // position (index in the position array in DmeVertex) to FxBone (index of CBone in AuthFx) map
CClothProxyCompiler *m_pCompiler;
public:
int FindMostBoundJoint( int nDmePos, float flBonusForExisting );
int GetSkinningJointCount();
int GetOrCreateClothBoneIndex( int nDmePos, bool bSimulated );
int GetClothBoneIndex( int nDmePos );
CAuthPhysFx::CBone *GetOrCreateClothBone( int nDmePos, bool bSimulated );
CAuthPhysFx::CBone *GetClothBone( int nDmePos );
};
friend class CMeshContext;
int GetMaxBonesPerVertex()const { return Max( 1, Min( 4, m_Options.m_nMaxBonesPerVertex ) ); }
bool Project( const Vector &vPos, UtlSymId_t*pFindSubset, int nFindSubsetCount, CUtlVector<Binding_t> &outBindings, int nIslandFilter );
void ProjectAndAddToQueue( const ProjItem_t &q, const Vector & vPos, CUtlSortVector< QuadProjection_t > &bestProj, int nIslandFilter );
int NodeToIsland( int nNode );
int GetIslandCount() const { return m_nIslandCount; }
protected:
void AppendPlaneCollisionDag( CModelContext &modelContext, CDmeDag *pDmeDag );
void AppendPlaneCollisionMesh( CModelContext &modelContext, CDmeMesh *pMesh, const matrix3x4_t &tm );
void AppendDag( CModelContext &context, CDmeDag *pDmeDag );
void AppendMesh( CModelContext &modelContext, CDmeMesh *pMesh, const matrix3x4_t &tm );
void CreateClothBones( CMeshContext &context );
void AddFitWeights( CMeshContext &context );
void BindNodeOffsetParents( CMeshContext &context );
void OrientClothBones( CMeshContext &context );
void CreateClothQuads( CMeshContext &context );
void ApplyClothBoneAttributes( CMeshContext &context );
QuadProjection_t ProjectOnQuad( const Vector &vPos, const CAuthPhysFx::CQuad &quad );
QuadProjection_t ProjectOnTri( const Vector &vPos, const CAuthPhysFx::CQuad &quad );
public:
class CVertex
{
public:
Vector m_vPos; // world position
};
class CPolygon
{
public:
CUtlVectorFixedGrowable< CVertex*, 4 > m_Verts;
Vector m_vNormal;
};
class CAuthFxSubset
{
public:
CUtlVectorFixedGrowable< ProjItem_t, 4 > m_Quads;
CUtlVectorFixedGrowable< ProjItem_t, 4 > m_Rods;
void Append( const CAuthFxSubset &other );
};
class CAuthFxBoneSubset : public CAuthFxSubset
{
public:
CAuthFxBoneSubset( int nModelJoint ) : m_nModelJoint( nModelJoint ){}
int m_nModelJoint;
};
UtlSymId_t FindBoneSym( const char *pName )
{
return m_BoneSubsets.Find( pName );
}
CUtlVector< CUtlString > m_MeshNames; // names of meshes that comprise this proxy mesh, for debugging
protected:
CAuthPhysFx *m_pAuthFx;
CUtlStringMapAutoPurge< CAuthFxBoneSubset* > m_BoneSubsets; // for each AuthFx bone, a subset of quads and rods to project to
CAuthFxSubset m_DefaultSubset;
int m_nProxyMeshes;
int m_nRootFxNode;
private:
int m_nIslandCount;
CUtlVector< int > m_NodeToIslandMap;
};
template <typename Functor >
inline void EnumerateFaces( CDmeMesh *pDmeMesh, CDmeVertexData * pBindState, const CVarBitVec *pUsefulDmeVerts, Functor &fn )
{
//const CUtlVector< Vector > & arrDmeVert = pBindState->GetPositionData();// the most original and un-split position array
const CUtlVector< int > & arrVertIndex = pBindState->GetVertexIndexData( CDmeVertexDataBase::FIELD_POSITION );
// find connecting (static) bones, and the bones that will drive them (add those, too, taking care not to add them twice)
//pBindState->FindFieldIndex( CDmeVertexDataBase::FIELD_POSITION )
int nFaceSetCount = pDmeMesh->FaceSetCount();
int nSkippedDegenerate = 0, nSkippedManygons = 0;
for ( int nFaceSet = 0; nFaceSet < nFaceSetCount; ++nFaceSet )
{
CDmeFaceSet *pFaceSet = pDmeMesh->GetFaceSet( nFaceSet );
int nIndexCount = pFaceSet->NumIndices(); // for each face (N-gon), there are N indices and -1 in this array
int nFirstIndex = 0;
while ( nFirstIndex < nIndexCount )
{
int nVertexCount = pFaceSet->GetNextPolygonVertexCount( nFirstIndex );
if ( nVertexCount < 2 )
{
nSkippedDegenerate++;
continue;
}
int nUsefulVertexCount = nVertexCount;
if ( nVertexCount > 4 )
{
nUsefulVertexCount = 4;
nSkippedManygons++;
}
int nPosVerts[ 4 ] = { -1,-1,-1,-1};
//DmeVertexIndex_t nFaceVerts[ 4 ];
CAuthPhysFx::CQuad quad;
bool bUseful = false;
for ( int nV = 0; nV < nUsefulVertexCount; ++nV )
{
DmeVertexIndex_t nFaceVertIndex = pFaceSet->GetIndex( nFirstIndex + nV );
//nFaceVerts[ nV ] = nFaceVertIndex;
int nDmeVert = arrVertIndex[ nFaceVertIndex ];
nPosVerts[ nV ] = nDmeVert;
if ( !pUsefulDmeVerts || ( nDmeVert < pUsefulDmeVerts->GetNumBits() && pUsefulDmeVerts->IsBitSet( nDmeVert ) ) )
{// it's all useful if we don't have a bitmap; otherwise, consult the bitmap - at least one vertex of the polygon must be useful
bUseful = true;
}
}
if ( bUseful ) // skip irrelevant polygons
{
for ( int nV = nUsefulVertexCount; nV < 4; ++nV )
{
nPosVerts[ nV ] = nPosVerts[ nUsefulVertexCount - 1 ];
//nFaceVerts[ nV ] = nFaceVerts[ nUsefulVertexCount - 1 ];
}
fn( nPosVerts, nUsefulVertexCount );
}
nFirstIndex += nVertexCount + 1; // skip N-gon indices and the -1 terminator
}
}
if ( nSkippedDegenerate || nSkippedManygons )
{
Warning( "Cloth: %d degenerate and %d 5+gons\n", nSkippedDegenerate, nSkippedManygons );
}
}
/*
template <typename Functor >
inline void EnumerateFaces( CDmeMesh *pDmeMesh, CDmeVertexData * pBindState, Functor &fn )
{
//const CUtlVector< Vector > & arrDmeVert = pBindState->GetPositionData();// the most original and un-split position array
const CUtlVector< int > & arrVertIndex = pBindState->GetVertexIndexData( CDmeVertexDataBase::FIELD_POSITION );
int nFaceSetCount = pDmeMesh->FaceSetCount();
for ( int nFaceCount = 0; nFaceCount < nFaceSetCount; ++nFaceCount )
{
CDmeFaceSet *pFaceSet = pDmeMesh->GetFaceSet( nFaceCount );
int nIndexCount = pFaceSet->NumIndices(); // for each face (N-gon), there are N indices and -1 in this array
int nFirstIndex = 0;
while ( nFirstIndex < nIndexCount )
{
int nTotalVertexCount = pFaceSet->GetNextPolygonVertexCount( nFirstIndex );
int nPolyIndex0 = arrVertIndex[ pFaceSet->GetIndex( nFirstIndex ) ];
//Vector vApex = arrDmeVert[ nPolyIndex0 ]; // we're going around the first vertex and split the poly into quads and tris
for ( int nBase = 1; nBase < nTotalVertexCount; nBase += 2 )
{
int nPosVerts[ 4 ] = { nPolyIndex0 };
int nFoundVerts = 1;
//Vector vPrevVert = vApex;
for ( int m = Min( nTotalVertexCount - 1, nBase + 2 ); m >= nBase; m-- )
{
int nPolyIndexM = arrVertIndex[ pFaceSet->GetIndex( nFirstIndex + m ) ];
Vector vVertM = arrVertIndex[ nPolyIndexM ];
//if ( ( vPrevVert - vVertM ).Length() > flCollapseEdgesThreshold )
{
nPosVerts[ nFoundVerts++ ] = nPolyIndexM;
//vPrevVert = vVertM;
}
}
if ( nFoundVerts > 1 )
{
for ( int m = nFoundVerts; m < 4; ++nFoundVerts )
nPosVerts[ m ] = nPosVerts[ nFoundVerts - 1 ];
fn( nPosVerts, nFoundVerts );
}
}
nFirstIndex += nVertexCount + 1; // skip N-gon indices and the -1 terminator
}
}
}
*/
template < typename Attr >
class ClothAttributes
{
public:
Attr m_animation_attraction ;
Attr m_animation_force_attraction ;
Attr m_drag ;
Attr m_mass ;
Attr m_gravity ;
Attr m_collision_radius ;
Attr m_ground_collision ;
Attr m_ground_friction ;
Attr m_use_rods;
Attr m_anchor_free_rotate;
protected:
float Get( const CMesh::CSingleVertexFieldAccessor< float > &accessor )
{
return *accessor;
}
float Get( float x )
{
return x;
}
public:
template < typename Map >
ClothAttributes( Map map )
{
m_animation_attraction = map( "cloth_animation_attract" );
m_animation_force_attraction = map( "cloth_animation_force_attract" );
m_drag = map( "cloth_drag" );
m_mass = map( "cloth_mass" );
m_gravity = map( "cloth_gravity" );
m_collision_radius = map( "cloth_collision_radius" );
m_ground_collision = map( "cloth_ground_collision" );
m_ground_friction = map( "cloth_ground_friction" );
m_use_rods = map( "cloth_use_rods" );
m_anchor_free_rotate = map( "cloth_anchor_free_rotate" );
}
void Apply( int nVert, CAuthPhysFx::CBone &authFxBone )
{
if ( m_animation_attraction )
authFxBone.m_Integrator.flAnimationVertexAttraction = 30 * Get( m_animation_attraction[ nVert ] );
if ( m_animation_force_attraction )
authFxBone.m_Integrator.flAnimationForceAttraction = 30 * Get( m_animation_force_attraction[ nVert ] );
if ( m_drag )
authFxBone.m_Integrator.flPointDamping = 30 * Get( m_drag[ nVert ] );
if ( m_mass )
authFxBone.m_flMassBias = expf( Get( m_mass[ nVert ] ) );
if ( m_gravity )
authFxBone.m_Integrator.flGravity = Get( m_gravity[ nVert ] );
if ( m_collision_radius )
authFxBone.m_flCollisionRadius = Get( m_collision_radius[ nVert ] );
if ( m_ground_collision )
{
authFxBone.m_flWorldFriction = 1.0f - Get( m_ground_collision[ nVert ] ); // ground_collision 0 maps to "worldFriction(source1 misnomer)" 1
if ( authFxBone.m_flWorldFriction < 0.999f )
{
authFxBone.m_bNeedsWorldCollision = true;
}
}
if ( m_ground_friction )
{
authFxBone.m_flGroundFriction = Get( m_ground_friction[ nVert ] );
}
if ( m_use_rods )
{
authFxBone.m_bUseRods = Get( m_use_rods[ nVert ] )> 0.5f;
}
if ( m_anchor_free_rotate )
{
authFxBone.m_bFreeRotation = Get( m_anchor_free_rotate[ nVert ] )> 0.5f;
}
}
};
extern const char *g_pDefaultClothRootBoneName;
extern CClothProxyCompiler *g_pClothProxyCompiler ;
#endif // CLOTHPROXYCOMPILER_HDR
File diff suppressed because it is too large Load Diff
+131
View File
@@ -0,0 +1,131 @@
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#ifndef FILEBUFFER_H
#define FILEBUFFER_H
#ifdef _WIN32
#pragma once
#endif
#include "tier1/smartptr.h"
#include "tier2/p4helpers.h"
class CFileBuffer
{
public:
CFileBuffer( int size )
{
m_pData = new unsigned char[size];
#ifdef _DEBUG
m_pUsed = new const char *[size];
memset( m_pUsed, 0, size * sizeof( const char * ) );
#endif
m_Size = size;
m_pCurPos = m_pData;
#ifdef _DEBUG
memset( m_pData, 0xbaadf00d, size );
#endif
}
~CFileBuffer()
{
delete [] m_pData;
#ifdef _DEBUG
delete [] m_pUsed;
#endif
}
#ifdef _DEBUG
void TestWritten( int EndOfFileOffset )
{
if ( !g_quiet )
{
printf( "testing to make sure that the whole file has been written\n" );
}
int i;
for( i = 0; i < EndOfFileOffset; i++ )
{
if( !m_pUsed[i] )
{
printf( "offset %d not written, end of file invalid!\n", i );
Assert( 0 );
}
}
}
#endif
void WriteToFile( const char *fileName, int size )
{
CPlainAutoPtr< CP4File > spFile( g_p4factory->AccessFile( fileName ) );
spFile->Edit();
FILE *fp = fopen( fileName, "wb" );
if( !fp )
{
MdlWarning( "Can't open \"%s\" for writing!\n", fileName );
return;
}
fwrite( m_pData, 1, size, fp );
fclose( fp );
spFile->Add();
}
void WriteAt( int offset, void *data, int size, const char *name )
{
// printf( "WriteAt: \"%s\" offset: %d end: %d size: %d\n", name, offset, offset + size - 1, size );
m_pCurPos = m_pData + offset;
#ifdef _DEBUG
int i;
const char **used = m_pUsed + offset;
bool bitched = false;
for( i = 0; i < size; i++ )
{
if( used[i] )
{
if( !bitched )
{
printf( "overwrite at %d! (overwriting \"%s\" with \"%s\")\n", i + offset, used[i], name );
Assert( 0 );
bitched = true;
}
}
else
{
used[i] = name;
}
}
#endif // _DEBUG
Append( data, size );
}
int GetOffset( void )
{
return m_pCurPos - m_pData;
}
void *GetPointer( int offset )
{
return m_pData + offset;
}
private:
void Append( void *data, int size )
{
Assert( m_pCurPos + size - m_pData < m_Size );
memcpy( m_pCurPos, data, size );
m_pCurPos += size;
}
CFileBuffer(); // undefined
int m_Size;
unsigned char *m_pData;
unsigned char *m_pCurPos;
#ifdef _DEBUG
const char **m_pUsed;
#endif
};
#endif // FILEBUFFER_H
+232
View File
@@ -0,0 +1,232 @@
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include <windows.h>
#include "HardwareMatrixState.h"
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include "studio.h"
#include "studiomdl.h"
CHardwareMatrixState::CHardwareMatrixState()
{
m_LRUCounter = 0;
m_NumMatrices = 0;
m_matrixState = NULL;
m_savedMatrixState = NULL;
}
void CHardwareMatrixState::Init( int numHardwareMatrices )
{
m_NumMatrices = numHardwareMatrices;
delete [] m_matrixState;
m_matrixState = new MatrixState_t[m_NumMatrices];
Assert( m_matrixState );
delete [] m_savedMatrixState;
m_savedMatrixState = new MatrixState_t[m_NumMatrices];
Assert( m_savedMatrixState );
m_LRUCounter = 0;
m_AllocatedMatrices = 0;
int i;
for( i = 0; i < m_NumMatrices; i++ )
{
m_matrixState[i].allocated = false;
}
}
bool CHardwareMatrixState::AllocateMatrix( int globalMatrixID )
{
int i;
if( IsMatrixAllocated( globalMatrixID ) )
{
return true;
}
for( i = 0; i < m_NumMatrices; i++ )
{
if( !m_matrixState[i].allocated )
{
m_matrixState[i].globalMatrixID = globalMatrixID;
m_matrixState[i].allocated = true;
m_matrixState[i].lastUsageID = m_LRUCounter++;
++m_AllocatedMatrices;
DumpState();
return true;
}
}
DumpState();
return false;
}
int CHardwareMatrixState::FindLocalLRUIndex( void )
{
int oldestLRUCounter = INT_MAX;
int i;
int oldestID = 0;
for( i = 0; i < m_NumMatrices; i++ )
{
if( !m_matrixState[i].allocated )
{
continue;
}
if( m_matrixState[i].lastUsageID < oldestLRUCounter )
{
oldestLRUCounter = m_matrixState[i].lastUsageID;
oldestID = i;
}
}
Assert( oldestLRUCounter != INT_MAX );
return oldestID;
}
void CHardwareMatrixState::DeallocateLRU( void )
{
int id;
id = FindLocalLRUIndex();
m_matrixState[id].allocated = false;
--m_AllocatedMatrices;
}
void CHardwareMatrixState::DeallocateLRU( int n )
{
int i;
for( i = 0; i < n; i++ )
{
DeallocateLRU();
}
}
bool CHardwareMatrixState::IsMatrixAllocated( int globalMatrixID ) const
{
int i;
for( i = 0; i < m_NumMatrices; i++ )
{
if( m_matrixState[i].globalMatrixID == globalMatrixID &&
m_matrixState[i].allocated )
{
return true;
}
}
return false;
}
void CHardwareMatrixState::DeallocateAll()
{
int i;
DumpState();
for( i = 0; i < m_NumMatrices; i++ )
{
m_matrixState[i].allocated = false;
m_matrixState[i].globalMatrixID = INT_MAX;
m_matrixState[i].lastUsageID = INT_MAX;
}
m_AllocatedMatrices = 0;
DumpState();
}
void CHardwareMatrixState::SaveState( void )
{
int i;
for( i = 0; i < m_NumMatrices; i++ )
{
m_savedMatrixState[i] = m_matrixState[i];
}
}
void CHardwareMatrixState::RestoreState( void )
{
int i;
for( i = 0; i < m_NumMatrices; i++ )
{
m_matrixState[i] = m_savedMatrixState[i];
}
}
int CHardwareMatrixState::AllocatedMatrixCount() const
{
return m_AllocatedMatrices;
}
int CHardwareMatrixState::FreeMatrixCount() const
{
return m_NumMatrices - m_AllocatedMatrices;
}
int CHardwareMatrixState::GetNthBoneGlobalID( int n ) const
{
int i;
int m = 0;
for( i = 0; i < m_NumMatrices; i++ )
{
if( m_matrixState[i].allocated )
{
if( n == m )
{
return m_matrixState[i].globalMatrixID;
}
m++;
}
}
Assert( 0 );
MdlError( "GetNthBoneGlobalID() Failure\n" );
return 0;
}
void CHardwareMatrixState::DumpState( void )
{
int i;
static char buf[256];
//#ifndef _DEBUG
return;
//#endif
OutputDebugString( "DumpState\n:" );
for( i = 0; i < m_NumMatrices; i++ )
{
if( m_matrixState[i].allocated )
{
sprintf( buf, "%d: allocated: %s lastUsageID: %d globalMatrixID: %d\n",
i,
m_matrixState[i].allocated ? "true " : "false",
m_matrixState[i].lastUsageID,
m_matrixState[i].globalMatrixID );
OutputDebugString( buf );
}
}
}
int CHardwareMatrixState::FindHardwareMatrix( int globalMatrixID )
{
int i;
for( i = 0; i < m_NumMatrices; i++ )
{
if( m_matrixState[i].globalMatrixID == globalMatrixID )
{
return i;
}
}
Assert( 0 );
MdlError( "barfing in FindHardwareMatrix\n" );
return 0;
}
+71
View File
@@ -0,0 +1,71 @@
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#ifndef HARDWAREMATRIXSTATE_H
#define HARDWAREMATRIXSTATE_H
#pragma once
// This emulates the hardware matrix palette and keeps up with
// matrix usage, LRU's matrices, etc.
class CHardwareMatrixState
{
public:
CHardwareMatrixState();
void Init( int numHardwareMatrices );
// return false if there is no slot for this matrix.
bool AllocateMatrix( int globalMatrixID );
// deallocate the least recently used matrix
void DeallocateLRU( void );
void DeallocateLRU( int n );
// return true if a matrix is allocate.
bool IsMatrixAllocated( int globalMatrixID ) const;
// flush usage flags - signifies that none of the matrices are being used in the current strip
// do this when starting a new strip.
void SetAllUnused();
void DeallocateAll();
// save the complete state of the hardware matrices
void SaveState();
// restore the complete state of the hardware matrices
void RestoreState();
// Returns the number of free + unsed matrices
int AllocatedMatrixCount() const;
int FreeMatrixCount() const;
int GetNthBoneGlobalID( int n ) const;
void DumpState( void );
private:
int FindHardwareMatrix( int globalMatrixID );
int FindLocalLRUIndex( void );
// Increment and return LRU counter.
struct MatrixState_t
{
bool allocated;
int lastUsageID;
int globalMatrixID;
};
int m_LRUCounter;
int m_NumMatrices;
int m_AllocatedMatrices;
MatrixState_t *m_matrixState;
MatrixState_t *m_savedMatrixState;
};
#endif // HARDWAREMATRIXSTATE_H
+71
View File
@@ -0,0 +1,71 @@
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include <stdlib.h>
#include <stdio.h>
#include "HardwareVertexCache.h"
CHardwareVertexCache::CHardwareVertexCache()
{
m_Fifo = NULL;
m_Size = 0;
Flush();
}
void CHardwareVertexCache::Init( int size )
{
m_Size = size;
m_Fifo = new int[size];
Flush();
}
void CHardwareVertexCache::Flush( void )
{
m_HeadIndex = 0;
m_NumEntries = 0;
}
bool CHardwareVertexCache::IsPresent( int index )
{
int i;
// printf( "testing if %d is present\n", index );
for( i = 0; i < m_NumEntries; i++ )
{
if( m_Fifo[( m_HeadIndex + i ) % m_Size] == index )
{
// printf( "yes!\n" );
return true;
}
}
// printf( "no!\n" );
// Print();
return false;
}
void CHardwareVertexCache::Insert( int index )
{
// printf( "Inserting: %d\n", index );
m_Fifo[( m_HeadIndex + m_NumEntries ) % m_Size] = index;
if( m_NumEntries == m_Size )
{
m_HeadIndex = ( m_HeadIndex + 1 ) % m_Size;
}
else
{
m_NumEntries++;
}
// Print();
}
void CHardwareVertexCache::Print( void )
{
int i;
for( i = 0; i < m_NumEntries; i++ )
{
printf( "fifo entry %d: %d\n", i, ( int )m_Fifo[( m_HeadIndex + i ) % m_Size] );
}
}
+32
View File
@@ -0,0 +1,32 @@
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#ifndef HARDWAREVERTEXCACHE_H
#define HARDWAREVERTEXCACHE_H
#ifdef _WIN32
#pragma once
#endif
// emulate a hardware post T&L vertex fifo
class CHardwareVertexCache
{
public:
CHardwareVertexCache();
void Init( int size );
void Insert( int index );
bool IsPresent( int index );
void Flush( void );
void Print( void );
private:
int m_Size;
int *m_Fifo;
int m_HeadIndex;
int m_NumEntries;
};
#endif // HARDWAREVERTEXCACHE_H
+183
View File
@@ -0,0 +1,183 @@
//-----------------------------------------------------------------------------
// MDLCOMPILE.VPC
//
// Project Script
//-----------------------------------------------------------------------------
$Macro SRCDIR "..\.."
$Macro OUTBINDIR "$SRCDIR\..\game\bin"
$Include "$SRCDIR\vpc_scripts\source_exe_con_win32_base.vpc"
$Include "$SRCDIR\vpc_scripts\fbx.vpc"
$Configuration
{
$Compiler
{
$AdditionalIncludeDirectories "$BASE,..\common,..\nvtristriplib,$SRCDIR\Game_Shared,$SRCDIR\ps3sdk\cell\host-common\include,$SRCDIR\ps3sdk\cell\target\common\include"
$PreprocessorDefinitions "$BASE;PROTECTED_THINGS_DISABLE;MDLCOMPILE"
}
$Linker
{
$AdditionalDependencies "$BASE winmm.lib libedgegeomtool.Release.Win32.vs8.lib"
$AdditionalLibraryDirectories "$BASE;$SRCDIR\ps3sdk\cell\host-win32\lib"
}
}
$Configuration "Debug"
{
$General
{
$OutputDirectory ".\Debug_mdlcompile" [$WIN32]
$IntermediateDirectory ".\Debug_mdlcompile" [$WIN32]
}
}
$Configuration "Release"
{
$General
{
$OutputDirectory ".\Release_mdlcompile" [$WIN32]
$IntermediateDirectory ".\Release_mdlcompile" [$WIN32]
}
}
$Project "mdlcompile"
{
$Folder "Source Files"
{
$File "..\common\cmdlib.cpp"
$File "collisionmodel.cpp"
$File "$SRCDIR\public\collisionutils.cpp"
$File "collisionmodelsource.cpp"
$File "..\common\datalinker.cpp"
$File "dmxsupport.cpp"
$File "$SRCDIR\public\filesystem_helpers.cpp"
$File "$SRCDIR\public\filesystem_init.cpp"
$File "..\common\filesystem_tools.cpp"
$File "hardwarematrixstate.cpp"
$File "hardwarevertexcache.cpp"
$File "$SRCDIR\public\interpolatortypes.cpp"
$File "$SRCDIR\public\movieobjects\movieobjects_compiletools.cpp"
$File "$SRCDIR\public\mdlobjects\mdlobjects.cpp"
$File "mrmsupport.cpp"
$File "objsupport.cpp"
$File "optimize.cpp"
$File "optimize_subd.cpp"
$File "perfstats.cpp"
$File "..\common\physdll.cpp"
$File "..\common\scriplib.cpp"
$File "simplify.cpp"
$File "$SRCDIR\public\studio.cpp"
$File "$SRCDIR\common\studiobyteswap.cpp"
$File "studiomdl.cpp"
$File "UnifyLODs.cpp"
$File "v1support.cpp"
$File "write.cpp"
$File "compileclothproxy.h"
$File "compileclothproxy.cpp"
}
$Folder "Header Files"
{
$File "..\common\cmdlib.h"
$File "collisionmodel.h"
$File "collisionmodelsource.h"
$File "filebuffer.h"
$File "..\common\datalinker.h"
$File "..\common\filesystem_tools.h"
$File "hardwarematrixstate.h"
$File "hardwarevertexcache.h"
$File "..\nvtristriplib\nvtristrip.h"
$File "perfstats.h"
$File "..\common\physdll.h"
$File "..\common\scriplib.h"
$File "studiomdl.h"
$File "optimize_subd.h"
}
$Folder "Public Header Files"
{
$File "$SRCDIR\public\gametrace.h"
$File "$SRCDIR\public\filesystem.h"
$File "$SRCDIR\public\filesystem_helpers.h"
$File "$SRCDIR\public\cmodel.h"
$File "$SRCDIR\public\basehandle.h"
$File "$SRCDIR\public\tier0\basetypes.h"
$File "$SRCDIR\public\bitvec.h"
$File "$SRCDIR\public\bone_accessor.h"
$File "$SRCDIR\public\bone_setup.h"
$File "$SRCDIR\public\bspflags.h"
$File "$SRCDIR\public\tier1\byteswap.h"
$File "$SRCDIR\public\tier1\characterset.h"
$File "$SRCDIR\public\collisionutils.h"
$File "$SRCDIR\public\mathlib\compressed_vector.h"
$File "$SRCDIR\public\const.h"
$File "$SRCDIR\public\vphysics\constraints.h"
$File "$SRCDIR\public\tier0\dbg.h"
$File "$SRCDIR\public\tier0\fasttimer.h"
$File "$SRCDIR\public\appframework\iappsystem.h"
$File "$SRCDIR\public\tier0\icommandline.h"
$File "$SRCDIR\public\ihandleentity.h"
$File "$SRCDIR\public\materialsystem\imaterial.h"
$File "$SRCDIR\public\materialsystem\imaterialsystem.h"
$File "$SRCDIR\public\materialsystem\imaterialvar.h"
$File "$SRCDIR\public\tier1\interface.h"
$File "$SRCDIR\public\istudiorender.h"
$File "$SRCDIR\public\tier1\keyvalues.h"
$File "$SRCDIR\public\materialsystem\materialsystem_config.h"
$File "$SRCDIR\public\mathlib\mathlib.h"
$File "$SRCDIR\public\tier0\memdbgoff.h"
$File "$SRCDIR\public\tier0\memdbgon.h"
$File "$SRCDIR\public\phyfile.h"
$File "$SRCDIR\public\optimize.h"
$File "$SRCDIR\public\tier0\platform.h"
$File "$SRCDIR\public\vstdlib\random.h"
$File "$SRCDIR\common\studiobyteswap.h"
$File "$SRCDIR\public\string_t.h"
$File "$SRCDIR\public\tier1\strtools.h"
$File "$SRCDIR\public\studio.h"
$File "$SRCDIR\public\tier3\tier3.h"
$File "$SRCDIR\public\tier1\utlbuffer.h"
$File "$SRCDIR\public\tier1\utldict.h"
$File "$SRCDIR\public\tier1\utllinkedlist.h"
$File "$SRCDIR\public\tier1\utlmemory.h"
$File "$SRCDIR\public\tier1\utlrbtree.h"
$File "$SRCDIR\public\tier1\utlsymbol.h"
$File "$SRCDIR\public\tier1\utlvector.h"
$File "$SRCDIR\public\vcollide.h"
$File "$SRCDIR\public\vcollide_parse.h"
$File "$SRCDIR\public\mathlib\vector.h"
$File "$SRCDIR\public\mathlib\vector2d.h"
$File "$SRCDIR\public\mathlib\vector4d.h"
$File "$SRCDIR\public\mathlib\vmatrix.h"
$File "$SRCDIR\public\vphysics_interface.h"
$File "$SRCDIR\public\mathlib\vplane.h"
$File "$SRCDIR\public\tier0\vprof.h"
$File "$SRCDIR\public\vstdlib\vstdlib.h"
}
$Folder "Link Libraries"
{
$Lib appframework
$Lib bonesetup
$Lib datamodel
$Lib dmeutils
$Lib dmserializers
$Lib mathlib
$Lib mathlib_extended
$Lib resourcefile
$Lib mdlobjects
$Lib meshutils
$Lib movieobjects
$Lib nvtristrip
$Lib tier1
$Lib tier2
$Lib tier3
$Lib fbxutils
}
}
+934
View File
@@ -0,0 +1,934 @@
//========= Copyright © 1996-2008, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//
//=============================================================================//
//
// studiomdl.c: generates a studio .mdl file from a .qc script
// models/<scriptname>.mdl.
//
#pragma warning( disable : 4244 )
#pragma warning( disable : 4237 )
#pragma warning( disable : 4305 )
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <math.h>
#include "cmdlib.h"
#include "scriplib.h"
#include "mathlib/mathlib.h"
#include "studio.h"
#include "studiomdl.h"
//#include "..\..\dlls\activity.h"
bool IsEnd( char const* pLine )
{
if (strncmp( "end", pLine, 3 ) != 0)
return false;
return (pLine[3] == '\0') || (pLine[3] == '\n');
}
int SortAndBalanceBones( int iCount, int iMaxCount, int bones[], float weights[] )
{
int i;
// collapse duplicate bone weights
for (i = 0; i < iCount-1; i++)
{
int j;
for (j = i + 1; j < iCount; j++)
{
if (bones[i] == bones[j])
{
weights[i] += weights[j];
weights[j] = 0.0;
}
}
}
// do sleazy bubble sort
int bShouldSort;
do {
bShouldSort = false;
for (i = 0; i < iCount-1; i++)
{
if (weights[i+1] > weights[i])
{
int j = bones[i+1]; bones[i+1] = bones[i]; bones[i] = j;
float w = weights[i+1]; weights[i+1] = weights[i]; weights[i] = w;
bShouldSort = true;
}
}
} while (bShouldSort);
#ifdef MDLCOMPILE
// throw away all weights less than 1/10,000th
while (iCount > 1 && weights[iCount-1] < 0.0001)
{
iCount--;
}
#else // #ifdef MDLCOMPILE
// throw away all weights less than 1/20th
while (iCount > 1 && weights[iCount-1] < 0.05)
{
iCount--;
}
#endif // #ifdef MDLCOMPILE
// clip to the top iMaxCount bones
if (iCount > iMaxCount)
{
iCount = iMaxCount;
}
float t = 0;
for (i = 0; i < iCount; i++)
{
t += weights[i];
}
if (t <= 0.0)
{
// missing weights?, go ahead and evenly share?
// FIXME: shouldn't this error out?
t = 1.0 / iCount;
for (i = 0; i < iCount; i++)
{
weights[i] = t;
}
}
else
{
// scale to sum to 1.0
t = 1.0 / t;
for (i = 0; i < iCount; i++)
{
weights[i] = weights[i] * t;
}
}
return iCount;
}
void Grab_Vertexlist( s_source_t *psource )
{
while (1)
{
if (fgets( g_szLine, sizeof( g_szLine ), g_fpInput ) != NULL)
{
int j;
int bone;
Vector p;
int iCount, bones[4];
float weights[4];
g_iLinecount++;
// check for end
if (IsEnd(g_szLine))
return;
int i = sscanf( g_szLine, "%d %d %f %f %f %d %d %f %d %f %d %f %d %f",
&j,
&bone,
&p[0], &p[1], &p[2],
&iCount,
&bones[0], &weights[0], &bones[1], &weights[1], &bones[2], &weights[2], &bones[3], &weights[3] );
if (i == 5)
{
if (bone < 0 || bone >= psource->numbones)
{
MdlWarning( "bogus bone index\n" );
MdlWarning( "%d %s :\n%s", g_iLinecount, g_szFilename, g_szLine );
MdlError( "Exiting due to errors\n" );
}
VectorCopy( p, g_vertex[j] );
g_bone[j].numbones = 1;
g_bone[j].bone[0] = bone;
g_bone[j].weight[0] = 1.0;
}
else if (i > 5)
{
iCount = SortAndBalanceBones( iCount, MAXSTUDIOBONEWEIGHTS, bones, weights );
VectorCopy( p, g_vertex[j] );
g_bone[j].numbones = iCount;
for (i = 0; i < iCount; i++)
{
g_bone[j].bone[i] = bones[i];
g_bone[j].weight[i] = weights[i];
}
}
else
{
MdlError("%s: error on line %d: %s", g_szFilename, g_iLinecount, g_szLine );
}
}
}
}
void Grab_Facelist( s_source_t *psource )
{
while (1)
{
if (fgets( g_szLine, sizeof( g_szLine ), g_fpInput ) != NULL)
{
int j;
s_tmpface_t f;
g_iLinecount++;
// check for end
if (IsEnd(g_szLine))
return;
if (sscanf( g_szLine, "%d %d %d %d",
&j,
&f.a, &f.b, &f.c) == 4)
{
g_face[j] = f;
}
else
{
MdlError("%s: error on line %d: %s", g_szFilename, g_iLinecount, g_szLine );
}
}
}
}
void Grab_Materiallist( s_source_t *psource )
{
while (1)
{
if (fgets( g_szLine, sizeof( g_szLine ), g_fpInput ) != NULL)
{
// char name[256];
char path[MAX_PATH];
rgb2_t a, d, s;
float g;
int j;
g_iLinecount++;
// check for end
if (IsEnd(g_szLine))
return;
if (sscanf( g_szLine, "%d %f %f %f %f %f %f %f %f %f %f %f %f %f \"%[^\"]s",
&j,
&a.r, &a.g, &a.b, &a.a,
&d.r, &d.g, &d.b, &d.a,
&s.r, &s.g, &s.b, &s.a,
&g,
path ) == 15)
{
if (path[0] == '\0')
{
psource->texmap[j] = -1;
}
else if (j < sizeof(psource->texmap))
{
psource->texmap[j] = LookupTexture( path );
}
else
{
MdlError( "Too many materials, max %d\n", sizeof(psource->texmap) );
}
}
}
}
}
void Grab_Texcoordlist( s_source_t *psource )
{
while (1)
{
if (fgets( g_szLine, sizeof( g_szLine ), g_fpInput ) != NULL)
{
int j;
Vector2D t;
g_iLinecount++;
// check for end
if (IsEnd(g_szLine))
return;
if (sscanf( g_szLine, "%d %f %f",
&j,
&t[0], &t[1]) == 3)
{
t[1] = 1.0 - t[1];
g_texcoord[0][j][0] = t[0];
g_texcoord[0][j][1] = t[1];
}
else
{
MdlError("%s: error on line %d: %s", g_szFilename, g_iLinecount, g_szLine );
}
}
}
}
void Grab_Normallist( s_source_t *psource )
{
while (1)
{
if (fgets( g_szLine, sizeof( g_szLine ), g_fpInput ) != NULL)
{
int j;
int bone;
Vector n;
g_iLinecount++;
// check for end
if (IsEnd(g_szLine))
return;
if (sscanf( g_szLine, "%d %d %f %f %f",
&j,
&bone,
&n[0], &n[1], &n[2]) == 5)
{
if (bone < 0 || bone >= psource->numbones)
{
MdlWarning( "bogus bone index\n" );
MdlWarning( "%d %s :\n%s", g_iLinecount, g_szFilename, g_szLine );
MdlError( "Exiting due to errors\n" );
}
VectorCopy( n, g_normal[j] );
}
else
{
MdlError("%s: error on line %d: %s", g_szFilename, g_iLinecount, g_szLine );
}
}
}
}
void Grab_Faceattriblist( s_source_t *psource )
{
while (1)
{
if (fgets( g_szLine, sizeof( g_szLine ), g_fpInput ) != NULL)
{
int j;
int smooth;
int material;
s_tmpface_t f;
unsigned short s;
g_iLinecount++;
// check for end
if (IsEnd(g_szLine))
return;
if (sscanf( g_szLine, "%d %d %d %d %d %d %d %d %d",
&j,
&material,
&smooth,
&f.ta[0], &f.tb[0], &f.tc[0],
&f.na, &f.nb, &f.nc) == 9)
{
f.a = g_face[j].a;
f.b = g_face[j].b;
f.c = g_face[j].c;
f.material = UseTextureAsMaterial( psource->texmap[material] );
if (f.material < 0)
{
MdlError( "face %d references NULL texture %d\n", j, material );
}
if (1)
{
s = f.b; f.b = f.c; f.c = s;
s = f.tb[0]; f.tb[0] = f.tc[0]; f.tc[0] = s;
s = f.nb; f.nb = f.nc; f.nc = s;
}
g_face[j] = f;
}
else
{
MdlError("%s: error on line %d: %s", g_szFilename, g_iLinecount, g_szLine );
}
}
}
}
int closestNormal( int v, int n )
{
float maxdot = -1.0;
float dot;
int r = n;
v_unify_t *cur = v_list[v];
while (cur)
{
dot = DotProduct( g_normal[cur->n], g_normal[n] );
if (dot > maxdot)
{
r = cur->n;
maxdot = dot;
}
cur = cur->next;
}
return r;
}
int AddToVlist(int v, int m, int n, int* t, int firstref)
{
v_unify_t *prev = NULL;
v_unify_t *cur = v_list[v];
while (cur)
{
if (cur->m == m && cur->n == n)
{
bool bMatch = true;
for (int i = 0; (i < MAXSTUDIOTEXCOORDS) && bMatch; ++i)
{
if (cur->t[i] != t[i])
{
bMatch = false;
}
}
if (bMatch)
{
cur->refcount++;
return cur - v_listdata;
}
}
prev = cur;
cur = cur->next;
}
if (g_numvlist >= MAXSTUDIOSRCVERTS)
{
MdlError( "Too many unified vertices\n");
}
cur = &v_listdata[g_numvlist++];
cur->lastref = -1;
cur->refcount = 1;
cur->v = v;
cur->m = m;
cur->n = n;
for (int i = 0; i < MAXSTUDIOTEXCOORDS; ++i)
{
cur->t[i] = t[i];
}
if (prev)
{
prev->next = cur;
}
else
{
v_list[v] = cur;
}
return g_numvlist - 1;
}
void DecrementReferenceVlist( int uv, int numverts )
{
if (uv < 0 || uv > MAXSTUDIOSRCVERTS)
MdlError( "decrement outside of range\n");
v_listdata[uv].refcount--;
if (v_listdata[uv].refcount == 0)
{
v_listdata[uv].lastref = numverts;
}
else if (v_listdata[uv].refcount < 0)
{
MdlError("<0 ref\n");
}
}
void UnifyIndices( s_source_t *psource )
{
int i;
s_face_t uface;
// clear v_list
g_numvlist = 0;
memset( v_list, 0, sizeof( v_list ) );
memset( v_listdata, 0, sizeof( v_listdata ) );
// create an list of all the
for (i = 0; i < g_numfaces; i++)
{
uface.a = AddToVlist(g_face[i].a, g_face[i].material, g_face[i].na, (int*)g_face[i].ta, g_numverts);
uface.b = AddToVlist(g_face[i].b, g_face[i].material, g_face[i].nb, (int*)g_face[i].tb, g_numverts);
uface.c = AddToVlist(g_face[i].c, g_face[i].material, g_face[i].nc, (int*)g_face[i].tc, g_numverts);
uface.d = 0xFFFFFFFF;
if ( g_face[i].d != 0xFFFFFFFF )
{
uface.d = AddToVlist(g_face[i].d, g_face[i].material, g_face[i].nd, (int*)g_face[i].td, g_numverts);
}
// keep an original copy
g_src_uface[i] = uface;
}
// printf("%d : %d %d %d\n", numvlist, g_numverts, g_numnormals, g_numtexcoords );
}
void CalcModelTangentSpaces( s_source_t *pSrc );
//-----------------------------------------------------------------------------
// Builds a list of unique vertices in a source
//-----------------------------------------------------------------------------
static void BuildUniqueVertexList( s_source_t *pSource, const int *pDesiredToVList )
{
// allocate memory
pSource->vertex = (s_vertexinfo_t *)calloc( pSource->numvertices, sizeof( s_vertexinfo_t ) );
int numValidTexcoords = 1;
for (int i = 1; i < MAXSTUDIOTEXCOORDS; ++i)
{
if (g_numtexcoords[i])
{
numValidTexcoords++;
}
else
{
break;
}
}
// create arrays of unique vertexes, normals, texcoords.
for (int i = 0; i < pSource->numvertices; i++)
{
int j = pDesiredToVList[i];
s_vertexinfo_t &vertex = pSource->vertex[i];
VectorCopy( g_vertex[ v_listdata[j].v ], vertex.position );
VectorCopy( g_normal[ v_listdata[j].n ], vertex.normal );
vertex.boneweight.numbones = g_bone[ v_listdata[j].v ].numbones;
int k;
for( k = 0; k < MAXSTUDIOBONEWEIGHTS; k++ )
{
vertex.boneweight.bone[k] = g_bone[ v_listdata[j].v ].bone[k];
vertex.boneweight.weight[k] = g_bone[ v_listdata[j].v ].weight[k];
}
for (k = 0; k < numValidTexcoords; ++k)
{
Vector2Copy(g_texcoord[k][v_listdata[j].t[k]], vertex.texcoord[k]);
}
vertex.numTexcoord = numValidTexcoords;
// store a bunch of other info
vertex.material = v_listdata[j].m;
#if 0
pSource->vertexInfo[i].firstref = v_listdata[j].firstref;
pSource->vertexInfo[i].lastref = v_listdata[j].lastref;
#endif
// printf("%4d : %2d : %6.2f %6.2f %6.2f\n", i, psource->boneweight[i].bone[0], psource->vertex[i][0], psource->vertex[i][1], psource->vertex[i][2] );
}
}
//-----------------------------------------------------------------------------
// sort new vertices by materials, last used
//-----------------------------------------------------------------------------
static int vlistCompare( const void *elem1, const void *elem2 )
{
v_unify_t *u1 = &v_listdata[*(int *)elem1];
v_unify_t *u2 = &v_listdata[*(int *)elem2];
// sort by material
if (u1->m < u2->m)
return -1;
if (u1->m > u2->m)
return 1;
// sort by last used
if (u1->lastref < u2->lastref)
return -1;
if (u1->lastref > u2->lastref)
return 1;
return 0;
}
static void SortVerticesByMaterial( int *pDesiredToVList, int *pVListToDesired )
{
for ( int i = 0; i < g_numvlist; i++ )
{
pDesiredToVList[i] = i;
}
qsort( pDesiredToVList, g_numvlist, sizeof( int ), vlistCompare );
for ( int i = 0; i < g_numvlist; i++ )
{
pVListToDesired[ pDesiredToVList[i] ] = i;
}
}
//-----------------------------------------------------------------------------
// sort new faces by materials, last used
//-----------------------------------------------------------------------------
static int faceCompare( const void *elem1, const void *elem2 )
{
int i1 = *(int *)elem1;
int i2 = *(int *)elem2;
// sort by material
if (g_face[i1].material < g_face[i2].material)
return -1;
if (g_face[i1].material > g_face[i2].material)
return 1;
// sort by original usage
if (i1 < i2)
return -1;
if (i1 > i2)
return 1;
return 0;
}
static void SortFacesByMaterial( int *pDesiredToSrcFace )
{
// NOTE: Unlike SortVerticesByMaterial, srcFaceToDesired isn't needed, so we're not computing it
for ( int i = 0; i < g_numfaces; i++ )
{
pDesiredToSrcFace[i] = i;
}
qsort( pDesiredToSrcFace, g_numfaces, sizeof( int ), faceCompare );
}
//-----------------------------------------------------------------------------
// Builds mesh structures in the source
//-----------------------------------------------------------------------------
static void PointMeshesToVertexAndFaceData( s_source_t *pSource, int *pDesiredToSrcFace )
{
// First, assign all meshes to be empty
// A mesh is a set of faces + vertices that all use 1 material
for ( int m = 0; m < MAXSTUDIOSKINS; m++ )
{
pSource->mesh[m].numvertices = 0;
pSource->mesh[m].vertexoffset = pSource->numvertices;
pSource->mesh[m].numfaces = 0;
pSource->mesh[m].faceoffset = pSource->numfaces;
}
// find first and count of vertices per material
for ( int i = 0; i < pSource->numvertices; i++ )
{
int m = pSource->vertex[i].material;
pSource->mesh[m].numvertices++;
if (pSource->mesh[m].vertexoffset > i)
{
pSource->mesh[m].vertexoffset = i;
}
}
// find first and count of faces per material
for ( int i = 0; i < pSource->numfaces; i++ )
{
int m = g_face[ pDesiredToSrcFace[i] ].material;
pSource->mesh[m].numfaces++;
if (pSource->mesh[m].faceoffset > i)
{
pSource->mesh[m].faceoffset = i;
}
}
/*
for (k = 0; k < MAXSTUDIOSKINS; k++)
{
printf("%d : %d:%d %d:%d\n", k, psource->mesh[k].numvertices, psource->mesh[k].vertexoffset, psource->mesh[k].numfaces, psource->mesh[k].faceoffset );
}
*/
}
//-----------------------------------------------------------------------------
// Builds the face list in the mesh
//-----------------------------------------------------------------------------
static void BuildFaceList( s_source_t *pSource, int *pVListToDesired, int *pDesiredToSrcFace )
{
pSource->face = (s_face_t *)calloc( pSource->numfaces, sizeof( s_face_t ));
for ( int m = 0; m < MAXSTUDIOSKINS; m++)
{
if ( !pSource->mesh[m].numfaces )
continue;
pSource->meshindex[ pSource->nummeshes++ ] = m;
for ( int i = pSource->mesh[m].faceoffset; i < pSource->mesh[m].numfaces + pSource->mesh[m].faceoffset; i++)
{
int j = pDesiredToSrcFace[i];
// NOTE: per-face vertex indices a,b,c,d are mesh relative (hence the subtraction), while g_src_uface are model relative
pSource->face[i].a = pVListToDesired[ g_src_uface[j].a ] - pSource->mesh[m].vertexoffset;
pSource->face[i].b = pVListToDesired[ g_src_uface[j].b ] - pSource->mesh[m].vertexoffset;
pSource->face[i].c = pVListToDesired[ g_src_uface[j].c ] - pSource->mesh[m].vertexoffset;
if ( g_src_uface[j].d != 0xFFFFFFFF )
{
pSource->face[i].d = pVListToDesired[ g_src_uface[j].d ] - pSource->mesh[m].vertexoffset;
}
Assert( ((pSource->face[i].a & 0xF0000000) == 0) && ((pSource->face[i].b & 0xF0000000) == 0) &&
((pSource->face[i].c & 0xF0000000) == 0) && (((pSource->face[i].d & 0xF0000000) == 0) || (pSource->face[i].d == 0xFFFFFFFF)) );
// printf("%3d : %4d %4d %4d %4d\n", i, pSource->face[i].a, pSource->face[i].b, pSource->face[i].c, pSource->face[i].d );
}
}
}
//-----------------------------------------------------------------------------
// Remaps the vertex animations based on the new vertex ordering
//-----------------------------------------------------------------------------
static void RemapVertexAnimations( s_source_t *pSource, int *pVListToDesired )
{
CUtlVectorAuto< int > temp;
int nAnimationCount = pSource->m_Animations.Count();
for ( int i = 0; i < nAnimationCount; ++i )
{
s_sourceanim_t &anim = pSource->m_Animations[i];
if ( !anim.newStyleVertexAnimations )
continue;
for ( int j = 0; j < MAXSTUDIOANIMFRAMES; ++j )
{
int nVAnimCount = anim.numvanims[j];
if ( nVAnimCount == 0 )
continue;
// Copy off the initial vertex data
// Have to do it in 2 loops because it'll overwrite itself if we do it in 1
for ( int k = 0; k < nVAnimCount; ++k )
{
temp[k] = anim.vanim[j][k].vertex;
}
for ( int k = 0; k < nVAnimCount; ++k )
{
// NOTE: vertex animations are model relative, not mesh relative
anim.vanim[j][k].vertex = pVListToDesired[ temp[k] ];
}
}
}
}
//-----------------------------------------------------------------------------
// Sorts vertices by material type, re-maps data structures that refer to those vertices
// to use the new indices
//-----------------------------------------------------------------------------
void BuildIndividualMeshes( s_source_t *pSource )
{
int *v_listsort = (int *)malloc( g_numvlist * sizeof( int ) ); // map desired order to vlist entry
int *v_ilistsort = (int *)malloc( g_numvlist * sizeof( int ) ); // map vlist entry to desired order
int *facesort = (int *)malloc( g_numfaces * sizeof( int ) ); // map desired order to src_face entry
SortVerticesByMaterial( v_listsort, v_ilistsort );
SortFacesByMaterial( facesort );
pSource->numvertices = g_numvlist;
pSource->numfaces = g_numfaces;
BuildUniqueVertexList( pSource, v_listsort );
PointMeshesToVertexAndFaceData( pSource, facesort );
BuildFaceList( pSource, v_ilistsort, facesort );
RemapVertexAnimations( pSource, v_ilistsort );
CalcModelTangentSpaces( pSource );
free( facesort );
free( v_ilistsort );
free( v_listsort );
}
void Grab_MRMFaceupdates( s_source_t *psource )
{
while (1)
{
if (fgets( g_szLine, sizeof( g_szLine ), g_fpInput ) != NULL)
{
g_iLinecount++;
// check for end
if (IsEnd(g_szLine))
return;
}
}
}
int Load_VRM ( s_source_t *psource )
{
char cmd[1024];
int option;
if (!OpenGlobalFile( psource->filename ))
{
return 0;
}
if( !g_quiet )
{
printf ("grabbing %s\n", psource->filename);
}
g_iLinecount = 0;
while (fgets( g_szLine, sizeof( g_szLine ), g_fpInput ) != NULL)
{
g_iLinecount++;
sscanf( g_szLine, "%1023s %d", cmd, &option );
if (stricmp( cmd, "version" ) == 0)
{
if (option != 2)
{
MdlError("bad version\n");
}
}
else if (stricmp( cmd, "name" ) == 0)
{
}
else if (stricmp( cmd, "vertices" ) == 0)
{
g_numverts = option;
}
else if (stricmp( cmd, "faces" ) == 0)
{
g_numfaces = option;
}
else if (stricmp( cmd, "materials" ) == 0)
{
// doesn't matter;
}
else if (stricmp( cmd, "texcoords" ) == 0)
{
g_numtexcoords[0] = option;
if (option == 0)
MdlError( "model has no texture coordinates\n");
}
else if (stricmp( cmd, "normals" ) == 0)
{
g_numnormals = option;
}
else if (stricmp( cmd, "tristrips" ) == 0)
{
// should be 0;
}
else if (stricmp( cmd, "vertexlist" ) == 0)
{
Grab_Vertexlist( psource );
}
else if (stricmp( cmd, "facelist" ) == 0)
{
Grab_Facelist( psource );
}
else if (stricmp( cmd, "materiallist" ) == 0)
{
Grab_Materiallist( psource );
}
else if (stricmp( cmd, "texcoordlist" ) == 0)
{
Grab_Texcoordlist( psource );
}
else if (stricmp( cmd, "normallist" ) == 0)
{
Grab_Normallist( psource );
}
else if (stricmp( cmd, "faceattriblist" ) == 0)
{
Grab_Faceattriblist( psource );
}
else if (stricmp( cmd, "MRM" ) == 0)
{
}
else if (stricmp( cmd, "MRMvertices" ) == 0)
{
}
else if (stricmp( cmd, "MRMfaces" ) == 0)
{
}
else if (stricmp( cmd, "MRMfaceupdates" ) == 0)
{
Grab_MRMFaceupdates( psource );
}
else if (stricmp( cmd, "nodes" ) == 0)
{
psource->numbones = Grab_Nodes( psource->localBone );
}
else if (stricmp( cmd, "skeleton" ) == 0)
{
Grab_Animation( psource, "BindPose" );
}
/*
else if (stricmp( cmd, "triangles" ) == 0) {
Grab_Triangles( psource );
}
*/
else
{
MdlError("unknown VRM command : %s \n", cmd );
}
}
UnifyIndices( psource );
BuildIndividualMeshes( psource );
fclose( g_fpInput );
return 1;
}
+432
View File
@@ -0,0 +1,432 @@
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//
//=============================================================================//
//
// studiomdl.c: generates a studio .mdl file from a .qc script
// models/<scriptname>.mdl.
//
#pragma warning( disable : 4244 )
#pragma warning( disable : 4237 )
#pragma warning( disable : 4305 )
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <math.h>
#include "tier1/utlbuffer.h"
#include "cmdlib.h"
#include "scriplib.h"
#include "mathlib/mathlib.h"
#include "studio.h"
#include "tier1/characterset.h"
#include "studiomdl.h"
//#include "..\..\dlls\activity.h"
bool IsEnd( char const* pLine );
int SortAndBalanceBones( int iCount, int iMaxCount, int bones[], float weights[] );
int AddToVlist( int v, int m, int n, int t, int firstref );
void DecrementReferenceVlist( int uv, int numverts );
int faceCompare( const void *elem1, const void *elem2 );
void UnifyIndices( s_source_t *psource );
struct MtlInfo_t
{
CUtlString m_MtlName;
CUtlString m_TgaName;
};
static CUtlVector<MtlInfo_t> g_MtlLib;
void ParseMtlLib( CUtlBuffer &buf )
{
int nCurrentMtl = -1;
while ( buf.IsValid() )
{
buf.GetLine( g_szLine, sizeof(g_szLine) );
if ( !Q_strnicmp( g_szLine, "newmtl ", 7 ) )
{
char mtlName[1024];
if ( sscanf( g_szLine, "newmtl %s", mtlName ) == 1 )
{
nCurrentMtl = g_MtlLib.AddToTail( );
g_MtlLib[nCurrentMtl].m_MtlName = mtlName;
g_MtlLib[nCurrentMtl].m_TgaName = "debugempty";
}
continue;
}
if ( !Q_strnicmp( g_szLine, "map_Kd ", 7 ) )
{
if ( nCurrentMtl < 0 )
continue;
char tgaPath[MAX_PATH];
char tgaName[1024];
if ( sscanf( g_szLine, "map_Kd %s", tgaPath ) == 1 )
{
Q_FileBase( tgaPath, tgaName, sizeof(tgaName) );
g_MtlLib[nCurrentMtl].m_TgaName = tgaName;
}
continue;
}
}
}
const char *FindMtlEntry( const char *pTgaName )
{
int nCount = g_MtlLib.Count();
for ( int i = 0; i < nCount; ++i )
{
if ( !Q_stricmp( g_MtlLib[i].m_MtlName, pTgaName ) )
return g_MtlLib[i].m_TgaName;
}
return pTgaName;
}
static bool ParseVertex( CUtlBuffer& bufParse, characterset_t &breakSet, int &v, int &t, int &n )
{
char cmd[1024];
int nLen = bufParse.ParseToken( &breakSet, cmd, sizeof(cmd), false );
if ( nLen <= 0 )
return false;
v = atoi( cmd );
n = 0;
t = 0;
char c = *(char*)bufParse.PeekGet();
bool bHasTexCoord = IN_CHARACTERSET( breakSet, c ) != 0;
bool bHasNormal = false;
if ( bHasTexCoord )
{
// Snag the '/'
nLen = bufParse.ParseToken( &breakSet, cmd, sizeof(cmd), false );
Assert( nLen == 1 );
c = *(char*)bufParse.PeekGet();
if ( !IN_CHARACTERSET( breakSet, c ) )
{
nLen = bufParse.ParseToken( &breakSet, cmd, sizeof(cmd), false );
Assert( nLen > 0 );
t = atoi( cmd );
c = *(char*)bufParse.PeekGet();
bHasNormal = IN_CHARACTERSET( breakSet, c ) != 0;
}
else
{
bHasNormal = true;
bHasTexCoord = false;
}
if ( bHasNormal )
{
// Snag the '/'
nLen = bufParse.ParseToken( &breakSet, cmd, sizeof(cmd), false );
Assert( nLen == 1 );
nLen = bufParse.ParseToken( &breakSet, cmd, sizeof(cmd), false );
Assert( nLen > 0 );
n = atoi( cmd );
}
}
return true;
}
int Load_OBJ( s_source_t *psource )
{
char cmd[1024];
int i;
int material = -1;
g_MtlLib.RemoveAll();
if ( !OpenGlobalFile( psource->filename ) )
return 0;
char pFullPath[MAX_PATH];
if ( !GetGlobalFilePath( psource->filename, pFullPath, sizeof(pFullPath) ) )
return 0;
char pFullDir[MAX_PATH];
Q_ExtractFilePath( pFullPath, pFullDir, sizeof(pFullDir) );
if( !g_quiet )
{
printf( "grabbing %s\n", psource->filename );
}
g_iLinecount = 0;
psource->numbones = 1;
strcpy( psource->localBone[0].name, "default" );
psource->localBone[0].parent = -1;
Assert( psource->m_Animations.Count() == 0 );
s_sourceanim_t *pSourceAnim = FindOrAddSourceAnim( psource, "BindPose" );
pSourceAnim->numframes = 1;
pSourceAnim->startframe = 0;
pSourceAnim->endframe = 0;
pSourceAnim->rawanim[0] = (s_bone_t *)calloc( 1, sizeof( s_bone_t ) );
pSourceAnim->rawanim[0][0].pos.Init();
pSourceAnim->rawanim[0][0].rot.Init();
Build_Reference( psource, "BindPose" );
characterset_t breakSet;
CharacterSetBuild( &breakSet, "/\\" );
while ( GetLineInput() )
{
Vector tmp;
if ( strncmp( g_szLine, "v ", 2 ) == 0 )
{
i = g_numverts++;
sscanf( g_szLine, "v %f %f %f", &g_vertex[i].x, &g_vertex[i].y, &g_vertex[i].z );
g_bone[i].numbones = 1;
g_bone[i].bone[0] = 0;
g_bone[i].weight[0] = 1.0;
continue;
}
if (strncmp( g_szLine, "vn ", 3 ) == 0)
{
i = g_numnormals++;
sscanf( g_szLine, "vn %f %f %f", &g_normal[i].x, &g_normal[i].y, &g_normal[i].z );
continue;
}
if (strncmp( g_szLine, "vt ", 3 ) == 0)
{
i = g_numtexcoords[0]++;
sscanf( g_szLine, "vt %f %f", &g_texcoord[0][i].x, &g_texcoord[0][i].y );
g_texcoord[0][i].y = 1.0 - g_texcoord[0][i].y;
continue;
}
if ( !Q_strncmp( g_szLine, "mtllib ", 7 ) )
{
sscanf( g_szLine, "mtllib %s", &cmd );
CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER );
char pFullMtlLibPath[MAX_PATH];
Q_ComposeFileName( pFullDir, cmd, pFullMtlLibPath, sizeof(pFullMtlLibPath) );
if ( g_pFullFileSystem->ReadFile( pFullMtlLibPath, NULL, buf ) )
{
ParseMtlLib( buf );
}
continue;
}
if (strncmp( g_szLine, "usemtl ", 7 ) == 0)
{
sscanf( g_szLine, "usemtl %s", &cmd );
const char *pTexture = FindMtlEntry( cmd );
int texture = LookupTexture( pTexture );
psource->texmap[texture] = texture; // hack, make it 1:1
material = UseTextureAsMaterial( texture );
continue;
}
if (strncmp( g_szLine, "f ", 2 ) == 0)
{
if ( material < 0 )
{
int texture = LookupTexture( "debugempty.tga" );
psource->texmap[texture] = texture;
material = UseTextureAsMaterial( texture );
}
int v0, n0, t0;
int v1, n1, t1;
int v2, n2, t2;
s_tmpface_t f;
// Are we specifying p only, p and t only, p and n only, or p and n and t?
char *pData = g_szLine + 2;
int nLen = Q_strlen( pData );
CUtlBuffer bufParse( pData, nLen, CUtlBuffer::TEXT_BUFFER | CUtlBuffer::READ_ONLY );
ParseVertex( bufParse, breakSet, v0, t0, n0 );
ParseVertex( bufParse, breakSet, v1, t1, n1 );
Assert( v0 <= g_numverts && t0 <= g_numtexcoords[0] && n0 <= g_numnormals );
Assert( v1 <= g_numverts && t1 <= g_numtexcoords[0] && n1 <= g_numnormals );
while ( bufParse.IsValid() )
{
if ( !ParseVertex( bufParse, breakSet, v2, t2, n2 ) )
break;
Assert( v2 <= g_numverts && t2 <= g_numtexcoords[0] && n2 <= g_numnormals );
i = g_numfaces++;
f.material = material;
f.a = v0 - 1; f.na = (n0 > 0) ? n0 - 1 : 0, f.ta[0] = (t0 > 0) ? t0 - 1 : 0;
f.b = v2 - 1; f.nb = (n2 > 0) ? n2 - 1 : 0, f.tb[0] = (t2 > 0) ? t2 - 1 : 0;
f.c = v1 - 1; f.nc = (n1 > 0) ? n1 - 1 : 0, f.tc[0] = (t1 > 0) ? t1 - 1 : 0;
g_face[i] = f;
v1 = v2; t1 = t2; n1 = n2;
}
continue;
}
}
UnifyIndices( psource );
BuildIndividualMeshes( psource );
fclose( g_fpInput );
return 1;
}
int AppendVTAtoOBJ( s_source_t *psource, char *filename, int frame )
{
char cmd[1024];
int i, j;
int material = 0;
Vector tmp;
matrix3x4_t m;
AngleMatrix( RadianEuler( 1.570796, 0, 0 ), m );
if ( !OpenGlobalFile( filename ) )
return 0;
if( !g_quiet )
{
printf ("grabbing %s\n", filename );
}
g_iLinecount = 0;
g_numverts = g_numnormals = g_numtexcoords[0] = g_numfaces = 0;
while ( GetLineInput() )
{
Vector tmp;
if (strncmp( g_szLine, "v ", 2 ) == 0)
{
i = g_numverts++;
sscanf( g_szLine, "v %f %f %f", &tmp.x, &tmp.y, &tmp.z );
VectorTransform( tmp, m, g_vertex[i] );
// printf("%f %f %f\n", g_vertex[i].x, g_vertex[i].y, g_vertex[i].z );
g_bone[i].numbones = 1;
g_bone[i].bone[0] = 0;
g_bone[i].weight[0] = 1.0;
}
else if (strncmp( g_szLine, "vn ", 3 ) == 0)
{
i = g_numnormals++;
sscanf( g_szLine, "vn %f %f %f", &tmp.x, &tmp.y, &tmp.z );
VectorRotate( tmp, m, g_normal[i] );
}
else if (strncmp( g_szLine, "vt ", 3 ) == 0)
{
i = g_numtexcoords[0]++;
sscanf( g_szLine, "vt %f %f", &g_texcoord[0][i].x, &g_texcoord[0][i].y );
}
else if (strncmp( g_szLine, "usemtl ", 7 ) == 0)
{
sscanf( g_szLine, "usemtl %s", &cmd );
int texture = LookupTexture( cmd );
psource->texmap[texture] = texture; // hack, make it 1:1
material = UseTextureAsMaterial( texture );
}
else if (strncmp( g_szLine, "f ", 2 ) == 0)
{
int v0, n0, t0;
int v1, n1, t1;
int v2, n2, t2;
int v3, n3, t3;
s_tmpface_t f;
i = g_numfaces++;
j = sscanf( g_szLine, "f %d/%d/%d %d/%d/%d %d/%d/%d %d/%d/%d", &v0, &t0, &n0, &v1, &t1, &n1, &v2, &t2, &n2, &v3, &t3, &n3 );
f.material = material;
f.a = v0 - 1; f.na = n0 - 1, f.ta[0] = 0;
f.b = v2 - 1; f.nb = n2 - 1, f.tb[0] = 0;
f.c = v1 - 1; f.nc = n1 - 1, f.tc[0] = 0;
Assert( v0 <= g_numverts && v1 <= g_numverts && v2 <= g_numverts );
Assert( n0 <= g_numnormals && n1 <= g_numnormals && n2 <= g_numnormals );
g_face[i] = f;
if (j == 12)
{
i = g_numfaces++;
f.a = v0 - 1; f.na = n0 - 1, f.ta[0] = 0;
f.b = v3 - 1; f.nb = n3 - 1, f.tb[0] = 0;
f.c = v2 - 1; f.nc = n2 - 1, f.tc[0] = 0;
g_face[i] = f;
}
}
}
UnifyIndices( psource );
s_sourceanim_t *pSourceAnim = FindOrAddSourceAnim( psource, "BindPose" );
if ( frame == 0 )
{
psource->numbones = 1;
strcpy( psource->localBone[0].name, "default" );
psource->localBone[0].parent = -1;
pSourceAnim->numframes = 1;
pSourceAnim->startframe = 0;
pSourceAnim->endframe = 0;
pSourceAnim->rawanim[0] = (s_bone_t *)calloc( 1, sizeof( s_bone_t ) );
pSourceAnim->rawanim[0][0].pos.Init();
pSourceAnim->rawanim[0][0].rot = RadianEuler( 1.570796, 0.0, 0.0 );
Build_Reference( psource, "BindPose" );
BuildIndividualMeshes( psource );
}
// printf("%d %d : %d\n", g_numverts, g_numnormals, numvlist );
int t = frame;
int count = g_numvlist;
pSourceAnim->numvanims[t] = count;
pSourceAnim->vanim[t] = (s_vertanim_t *)calloc( count, sizeof( s_vertanim_t ) );
for (i = 0; i < count; i++)
{
pSourceAnim->vanim[t][i].vertex = i;
pSourceAnim->vanim[t][i].pos = g_vertex[v_listdata[i].v];
pSourceAnim->vanim[t][i].normal = g_normal[v_listdata[i].n];
}
fclose( g_fpInput );
return 1;
}
File diff suppressed because it is too large Load Diff
+936
View File
@@ -0,0 +1,936 @@
//========= Copyright 1996-2008, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include "optimize_subd.h"
#define PI 3.14159265
#define VTXIDX(vID) m_vtxList[vID].origMeshVertID
#define VTXPOS(vID) *m_vtxData->Position( m_vtxList[vID].origMeshVertID )
#define VTXNOR(vID) *m_vtxData->Normal( m_vtxList[vID].origMeshVertID )
static Vector project_and_normalize( Vector v, Vector n )
{
v = v - DotProduct(v, n)*n;
VectorNormalize(v);
return v;
}
static int MOD4[8] = {0,1,2,3,0,1,2,3};
namespace OptimizedModel
{
class NeighborCornerBitfield
{
public:
unsigned short *bitfield;
NeighborCornerBitfield(unsigned short *field): index(0), bitfield(field) { *bitfield = 0; }
void pushBit( bool bit )
{
*bitfield |= bit<<index; index++;
}
void popBit()
{
index--; *bitfield &= ~(1<<index);
}
bool getBitAt( unsigned short i )
{
return ((*bitfield & (1<<i))>>i) == 1;
}
void insertBitAt( unsigned short i, bool bit )
{
unsigned short preMask = (1<<i)-1;
*bitfield = (*bitfield & preMask) + ((*bitfield & (~preMask))<<1) + (bit<<i);
index++;
}
void removeBitAt( unsigned short i )
{
unsigned short preMask = (1<<i)-1;
unsigned short postMask = ~((1<<(i+1))-1);
*bitfield = ((*bitfield & postMask)>>1) + (*bitfield & preMask);
index--;
}
void clearBits()
{
*bitfield = 0;
}
private:
unsigned short index;
};
COptimizeSubDBuilder::COptimizeSubDBuilder(SubD_FaceList_t& subDFaceList, const SubD_VertexList_t& vertexList, const SubD_VertexData_t &vertexData, bool bIsTagged, bool bMendVertices)
: m_faceList(subDFaceList), m_vtxList(vertexList), m_vtxData(vertexData)
{
m_numPatches = (int) subDFaceList.Count();
ProcessPatches(bIsTagged,bMendVertices);
}
void dumpPatch(SubD_Face_t *patch)
{
Msg( "Patch: %d\n", patch->patchID );
Msg( " vtxIDs: %d %d %d %d\n", patch->vtxIDs[0], patch->vtxIDs[1], patch->vtxIDs[2], patch->vtxIDs[3] );
Msg( " valences: %d %d %d %d\n", patch->valences[0], patch->valences[1], patch->valences[2], patch->valences[3] );
Msg( " vtx1RingSize: %d %d %d %d\n", patch->vtx1RingSize[0], patch->vtx1RingSize[1], patch->vtx1RingSize[2], patch->vtx1RingSize[3] );
Msg( " vtx1RingCenterQuadOffset: %d %d %d %d\n", patch->vtx1RingCenterQuadOffset[0], patch->vtx1RingCenterQuadOffset[1], patch->vtx1RingCenterQuadOffset[2], patch->vtx1RingCenterQuadOffset[3] );
Msg( " bndVtx: %d %d %d %d\n", patch->bndVtx[0], patch->bndVtx[1], patch->bndVtx[2], patch->bndVtx[3] );
Msg( " cornerVtx: %d %d %d %d\n", patch->cornerVtx[0], patch->cornerVtx[1], patch->cornerVtx[2], patch->cornerVtx[3] );
Msg( " BndEdge: %d %d %d %d\n", patch->bndEdge[0], patch->bndEdge[1], patch->bndEdge[2], patch->bndEdge[3] );
Msg( " halfEdges.twin: %d/%d %d/%d %d/%d %d/%d\n",
patch->halfEdges[0].twin ? patch->halfEdges[0].twin->patch->patchID : -1, patch->halfEdges[0].twin ? patch->halfEdges[0].twin->localID: -1,
patch->halfEdges[1].twin ? patch->halfEdges[1].twin->patch->patchID : -1, patch->halfEdges[1].twin ? patch->halfEdges[1].twin->localID: -1,
patch->halfEdges[2].twin ? patch->halfEdges[2].twin->patch->patchID : -1, patch->halfEdges[2].twin ? patch->halfEdges[2].twin->localID: -1,
patch->halfEdges[3].twin ? patch->halfEdges[3].twin->patch->patchID : -1, patch->halfEdges[3].twin ? patch->halfEdges[3].twin->localID: -1 );
Msg( " halfEdges.sectorStart: %d/%d %d/%d %d/%d %d/%d\n",
patch->halfEdges[0].sectorStart ? patch->halfEdges[0].sectorStart->patch->patchID : -1, patch->halfEdges[0].sectorStart ? patch->halfEdges[0].sectorStart->localID: -1,
patch->halfEdges[1].sectorStart ? patch->halfEdges[1].sectorStart->patch->patchID : -1, patch->halfEdges[1].sectorStart ? patch->halfEdges[1].sectorStart->localID: -1,
patch->halfEdges[2].sectorStart ? patch->halfEdges[2].sectorStart->patch->patchID : -1, patch->halfEdges[2].sectorStart ? patch->halfEdges[2].sectorStart->localID: -1,
patch->halfEdges[3].sectorStart ? patch->halfEdges[3].sectorStart->patch->patchID : -1, patch->halfEdges[3].sectorStart ? patch->halfEdges[3].sectorStart->localID: -1 );
Msg( " nbCornerVtx: %x %x %x %x\n", patch->nbCornerVtx[0], patch->nbCornerVtx[1], patch->nbCornerVtx[2], patch->nbCornerVtx[3] );
Msg( " loopGapAngle: %d %d %d %d\n", patch->loopGapAngle[0], patch->loopGapAngle[1], patch->loopGapAngle[2], patch->loopGapAngle[3] );
}
void dumpPatches( SubD_FaceList_t &quads )
{
size_t nQuads = quads.Count();
for (size_t k=0; k<nQuads; k++)
{
dumpPatch(&quads[k]);
}
}
void COptimizeSubDBuilder::ProcessPatches( bool bIsTagged, bool bMendVertices )
{
// Init attributes
for ( int i=0; i<m_numPatches; ++i )
{
SubD_Face_t *pPatch = &m_faceList[i];
for (int k=0; k<4; ++k)
{
pPatch->patchID = i;
if ( !bIsTagged )
{
pPatch->bndVtx[k] = false;
pPatch->bndEdge[k] = false;
pPatch->cornerVtx[k] = false;
}
pPatch->nbCornerVtx[k] = 0;
pPatch->valences[k] = 0;
pPatch->minOneRingIndex[k] = 0;
pPatch->loopGapAngle[k] = 65535;
pPatch->edgeBias[2*k] = 16384;
pPatch->edgeBias[2*k+1] = 16384;
pPatch->halfEdges[k].twin = NULL;
pPatch->halfEdges[k].sectorStart = &pPatch->halfEdges[k]; // start one-ring with this halfedge
pPatch->halfEdges[k].localID = k;
pPatch->halfEdges[k].patch = pPatch;
}
}
RemapIndices();
BuildNeighborhoodInfo();
CheckForManifoldMesh();
ConsistentPatchOrientation();
if ( !bIsTagged )
{
TagCreases();
}
// dumpPatches(m_QuadArray);
// first pass --------------------------------------------------------------------
for ( int i=0; i<m_numPatches; i++ )
{
SubD_Face_t *pPatch = &m_faceList[i];
for ( int k=0; k<4; k++ )
{
ComputeSectorStart( pPatch, k );
ComputePerVertexInfo( pPatch, k );
ComputeSectorOneRing( pPatch, k );
ComputeSectorAngle( pPatch, k );
}
}
// dumpPatches(m_faceList);
// second pass requires all per-vertex-per-face variables to be computed ----------
for ( int i=0; i<m_numPatches; i++ )
{
SubD_Face_t *pPatch = &m_faceList[i];
for ( int k=0; k<4; k++ )
{
ComputeNbCorners( pPatch, k );
}
}
// third pass computes neighboring texcoords for watertight displacement mapping ----------
for ( int i=0; i<m_numPatches; i++ )
{
SubD_Face_t *pPatch = &m_faceList[i];
ComputeNeighborTexcoords( pPatch );
}
// Compute offsets into one-rings, necessary for subsequent evaluation consistency
SetMinOneRingIndices();
// Sort patches by regular vs extraordinary
SubD_FaceList_t regFaceList;
SubD_FaceList_t extraFaceList;
for ( int i=0; i<m_numPatches; i++ )
{
SubD_Face_t *pPatch = &m_faceList[i];
if ( FaceIsRegular( pPatch ) )
{
regFaceList.AddToTail( *pPatch );
}
else
{
extraFaceList.AddToTail( *pPatch );
}
}
// recombine
int nRegFaces = regFaceList.Count();
for ( int i=0; i<nRegFaces; i++ )
{
m_faceList[i] = regFaceList[i];
}
int nExtraFaces = extraFaceList.Count();
for ( int i=0; i<nExtraFaces; i++ )
{
m_faceList[i+nRegFaces] = extraFaceList[i];
}
// mend vertices at the end ----------
/*if ( bMendVertices )
{
for ( int i=0; i<m_numPatches; i++ )
{
SubD_Face_t *pPatch = &m_faceList[i];
for (int k=0; k<4; k++)
{
mendVertices( pPatch, k );
}
}
}*/
}
HalfEdge *COptimizeSubDBuilder::FindTwin( HalfEdge &he )
{
Vector p0 = VTXPOS( he.patch->vtxIDs[ MOD4[he.localID+0] ] );
Vector p1 = VTXPOS( he.patch->vtxIDs[ MOD4[he.localID+1] ] ); // twin face will have edge p1->p0
for (int i=0; i<m_numPatches; i++)
{
SubD_Face_t *patch = &m_faceList[i];
for ( unsigned short k=0; k<4; k++ )
{
if ( ( VTXPOS( patch->vtxIDs[ MOD4[k + 0] ] ) == p1 ) &&
( VTXPOS( patch->vtxIDs[ MOD4[k + 1] ] ) == p0 ) )
{
return &patch->halfEdges[k];
}
}
}
return NULL;
}
// Set the minimum one-ring index for each of the four vertices of a patch.
// This value is used during the mapping from vertices to Bezier control
// points in order to ensure consistent evaluation order and avoid cracks
void COptimizeSubDBuilder::SetMinOneRingIndices()
{
for ( int i=0; i<m_numPatches; i++ ) // Walk patches
{
SubD_Face_t* pPatch = &m_faceList[i];
int nFirstNeighbor = 0; // First neighbor in a given vertex's one-ring
for ( int k=0; k<4; k++ ) // For each vertex of the patch
{
int nMinNeighborIdx = m_IndexRemapTable[pPatch->oneRing[nFirstNeighbor]]; // Remapped Index
int nMinNeighborOffset = 0; // Neighbor zero is the current min
int nLastNeighbor = nFirstNeighbor + pPatch->vtx1RingSize[k] - 1; // Last neighbor
for ( int j=nFirstNeighbor; j<=nLastNeighbor; j++ ) // First neighbor to the last neighbor, inclusive
{
int nNeighborIdx = m_IndexRemapTable[pPatch->oneRing[j]]; // Use only remapped indices
if ( nNeighborIdx < nMinNeighborIdx ) // If we have a smaller remapped index
{
nMinNeighborIdx = nNeighborIdx; // Set as new min index
nMinNeighborOffset = j - nFirstNeighbor; // Offset into THIS vertex's one-ring
}
}
pPatch->minOneRingIndex[k] = nMinNeighborOffset; // Set the offset into THIS vertex's one-ring
nFirstNeighbor = nLastNeighbor + 1; // Go to next range of indices in the one-ring array
}
}
}
// Positions appear redundantly in vertex data, so we need a mapping so that SetMinOneRingIndices() can do the right thing
void COptimizeSubDBuilder::RemapIndices()
{
for ( int i=0; i<m_vtxList.Count(); i++ )
{
m_IndexRemapTable.AddToTail(i); // Set identity mapping
}
for ( int i=0; i<m_vtxList.Count(); i++ ) // Walk indices again
{
for ( int j=i+1; j<m_vtxList.Count(); j++ ) // Look at later indices
{
Vector vPosi = VTXPOS( i );
Vector vPosj = VTXPOS( j );
if ( vPosi == vPosj ) // If the positions are equivalent, set index remapping
{
m_IndexRemapTable[j] = MIN( i, j );
m_IndexRemapTable[i] = MIN( i, j );
}
}
}
/*
for ( int i=0; i<m_vtxList.Count(); i++ )
{
if ( i != m_IndexRemapTable[i] )
{
Msg( "(%d, %d) ***\n", i, m_IndexRemapTable[i] );
}
else
{
Msg( "(%d, %d)\n", i, m_IndexRemapTable[i] );
}
}
*/
}
void COptimizeSubDBuilder::BuildNeighborhoodInfo( )
{
for ( int i=0; i<m_numPatches; i++ )
{
SubD_Face_t* pPatch = &m_faceList[i];
for ( int k=0; k<4; k++ )
{
if ( !pPatch->halfEdges[k].twin )
{
HalfEdge *pTwin = FindTwin(pPatch->halfEdges[k]);
pPatch->halfEdges[k].twin = pTwin; // record twin
if ( pTwin )
{
pPatch->halfEdges[k].twin->twin = &pPatch->halfEdges[k]; // record twin's twin
}
else
{
pPatch->bndEdge[k] = true;
pPatch->bndVtx[MOD4[k+0]] = true;
pPatch->bndVtx[MOD4[k+1]] = true;
}
}
}
}
}
void COptimizeSubDBuilder::CheckForManifoldMesh( )
{
for ( int i=0; i<m_numPatches; i++ )
{
SubD_Face_t* pPatch = &m_faceList[i];
for (unsigned short k=0; k<4; ++k)
{
if (( pPatch->halfEdges[k].twin != NULL ) && (pPatch->halfEdges[k].twin->twin != &pPatch->halfEdges[k]) )
{
Msg( "Topology error at vertices %d, %d, %d\n", pPatch->vtxIDs[MOD4[k+3]], pPatch->vtxIDs[MOD4[k+0]], pPatch->vtxIDs[MOD4[k+1]] );
Vector vA = VTXPOS( pPatch->vtxIDs[MOD4[k+3]] );
Vector vB = VTXPOS( pPatch->vtxIDs[MOD4[k+0]] );
Vector vC = VTXPOS( pPatch->vtxIDs[MOD4[k+1]] );
Msg( "spaceLocator -p %.4f %.4f %.4f;\n", vA.x, vA.y, vA.z );
Msg( "spaceLocator -p %.4f %.4f %.4f;\n", vB.x, vB.y, vB.z );
Msg( "spaceLocator -p %.4f %.4f %.4f;\n", vC.x, vC.y, vC.z );
}
}
}
}
void COptimizeSubDBuilder::ComputeSectorStart(SubD_Face_t *pPatch, unsigned short k)
{
HalfEdge *sectorStart, *next = &pPatch->halfEdges[k];
do
{
sectorStart = next;
if ( next->BndEdge() )
{
next = NULL;
}
else
{
next = next->PrevByTail();
}
}
while ( ( next != NULL ) && ( next != &(pPatch->halfEdges[k]) ) );
if ( next == NULL )
{
pPatch->halfEdges[k].sectorStart = sectorStart; // only update sectorStart if we actually hit a boundary
}
}
// Propagates bndVtx to faces that do not have a BndEdge to this vertex, sets cornerVtx,
// Requires sectorStart, corrects sectorStart and bndVtx for dangling crease edges.
void COptimizeSubDBuilder::ComputePerVertexInfo(SubD_Face_t *baseQuad, unsigned short baseLocalID)
{
unsigned short nBndEdges = 0;
HalfEdge *sectorStart = baseQuad->halfEdges[ MOD4[baseLocalID] ].sectorStart, *he = sectorStart;
// Find first sector
HalfEdge *next = he->PrevByTail();
while ( ( next!=NULL ) && ( next!=sectorStart ) )
{
he = next;
next = next->PrevByTail();
}
if ( next != NULL )
{
he = sectorStart;
}
if ( he->BndEdge() )
{
nBndEdges++;
}
HalfEdge *heEnd = he->twin;
he = he->PrevInFace();
do
{
if ( he->BndEdge() )
{
nBndEdges++;
}
he = he->NextByHead();
} while (( he != NULL ) && (he != heEnd));
// Set flags
if ( nBndEdges == 1 ) // dangling BndEdge -> correct sectorStart
{
baseQuad->halfEdges[ baseLocalID ].sectorStart = &baseQuad->halfEdges[ baseLocalID ];
baseQuad->bndVtx[baseLocalID] = false;
}
else if ( nBndEdges >= 2 )
{
baseQuad->bndVtx[baseLocalID] = true;
if ( nBndEdges > 2 )
{
baseQuad->cornerVtx[baseLocalID] = true; // more than 2 BndEdges -> cornerVtx
}
}
}
//
// Writes oneRing, vtx1RingSize, vtx1RingCenterQuadOffset, valence
//
void COptimizeSubDBuilder::ComputeSectorOneRing( SubD_Face_t *baseQuad, unsigned short baseLocalID )
{
unsigned short *oneRing = baseQuad->oneRing;
for ( unsigned short k=0; k < baseLocalID; k++ )
{
oneRing += baseQuad->vtx1RingSize[k];
}
unsigned short &centerOffset = baseQuad->vtx1RingCenterQuadOffset[baseLocalID] = 1;
unsigned short &valence = baseQuad->valences[baseLocalID] = 0;
unsigned short &oneRingSize = baseQuad->vtx1RingSize[baseLocalID] = 0;
HalfEdge *heBase = &baseQuad->halfEdges[ MOD4[baseLocalID] ];
HalfEdge *he = heBase->sectorStart;
oneRing[oneRingSize++] = he->patch->vtxIDs[ MOD4[he->localID+0] ];
valence++;
oneRing[oneRingSize++] = he->patch->vtxIDs[ MOD4[he->localID+1] ];
HalfEdge *heEnd = he->twin;
he = he->PrevInFace();
do
{
oneRing[oneRingSize++] = he->patch->vtxIDs[ MOD4[he->localID+3] ];
valence++;
oneRing[oneRingSize++] = he->patch->vtxIDs[ MOD4[he->localID+0] ];
if ( he->twin == heBase )
{
centerOffset = oneRingSize - 1;
}
he = (he->BndEdge() && baseQuad->bndVtx[baseLocalID]) ? NULL : he->NextByHead(); // make sure we only step over BndEdge if it is dangling.
} while ( ( he != NULL ) && ( he != heEnd ) );
if ( ( he != NULL) && ( he == heEnd ) ) // if we closed the loop, add off-edge vertex from last quad.
{
oneRing[oneRingSize++] = he->patch->vtxIDs[ MOD4[ he->localID+3 ]];
}
}
// Depends on bndVtx, cornerVtx, valence
void COptimizeSubDBuilder::ComputeSectorAngle( SubD_Face_t *baseQuad, unsigned short baseLocalID )
{
if ( !baseQuad->bndVtx[baseLocalID] ) // If no boundary vertex, nothing needs to be done (includes dangling crease)
return;
if ( !baseQuad->cornerVtx[baseLocalID] ) // If no corner, set loopGapAngle = PI (or PI/2 for valence==2)
{
baseQuad->loopGapAngle[baseLocalID] = 65535 / ( baseQuad->valences[baseLocalID] == 2 ? 4 : 2 );
return;
}
HalfEdge *he = baseQuad->halfEdges[ MOD4[baseLocalID] ].sectorStart;
Vector center_pos = VTXPOS( he->patch->vtxIDs[ he->localID ] );
Vector center_nor = VTXNOR( he->patch->vtxIDs[ he->localID ] );
VectorNormalize(center_nor);
int debugVtxID = he->patch->vtxIDs[ MOD4[ he->localID+1 ] ];
Vector eVec1 = VTXPOS( he->patch->vtxIDs[ MOD4[ he->localID+1 ] ] ) - center_pos, eVec2;
Vector npVec1 = project_and_normalize( eVec1, center_nor ), npVec2;
float sector_angle = 0;
he = he->PrevInFace();
do
{
debugVtxID = he->patch->vtxIDs[ MOD4[ he->localID ] ];
eVec2 = VTXPOS( he->patch->vtxIDs[ MOD4[ he->localID ] ] ) - center_pos;
npVec2 = project_and_normalize( eVec2, center_nor );
sector_angle += acosf( DotProduct( npVec1, npVec2 ) );
he = he->BndEdge() ? NULL : he->NextByHead(); // make sure we only step over BndEdge if it is dangling.
npVec1 = npVec2;
} while ( he != NULL ); // only way to terminate is to hit BndEdge
VectorNormalize( eVec1 );
VectorNormalize( eVec2 );
float loopGapAngleF = acosf( DotProduct(eVec1, eVec2) ); // measure overall gap
baseQuad->loopGapAngle[baseLocalID] = (unsigned int) ( ( 65535.0 * loopGapAngleF ) / ( 2 * PI ) );
}
void COptimizeSubDBuilder::MendVertices(SubD_Face_t *baseQuad, unsigned short baseLocalID)
{
HalfEdge *he = baseQuad->halfEdges[ baseLocalID ].sectorStart;
unsigned short vtxID = baseQuad->vtxIDs[ baseLocalID ];
Vector p = VTXPOS( vtxID );
Vector n = VTXNOR( vtxID );
HalfEdge *heEnd = he->twin;
he = he->PrevInFace();
do
{
if (( VTXPOS( he->patch->vtxIDs[ MOD4[he->localID+1] ]) == p ) &&
( VTXNOR( he->patch->vtxIDs[ MOD4[he->localID+1] ]) == n ))
{
he->patch->vtxIDs[ MOD4[he->localID+1] ] = vtxID;
}
if ( (he->twin) &&
( VTXPOS( he->twin->patch->vtxIDs[ MOD4[he->twin->localID] ] ) == p) &&
( VTXNOR( he->twin->patch->vtxIDs[ MOD4[he->twin->localID] ] ) == n) )
{
he->twin->patch->vtxIDs[ MOD4[he->twin->localID] ] = vtxID;
}
he = he->NextByHead();
} while (( he != NULL ) && (he != heEnd));
}
// Computes a bitfield with bits set if the corresponding neighbor-vertex is a concave corner
// this has to go in a second pass as all per-face-per-vertex flags from the first pass to be computed beforehand
void COptimizeSubDBuilder::ComputeNbCorners( SubD_Face_t *baseQuad, unsigned short baseLocalID )
{
NeighborCornerBitfield nbCorners( &baseQuad->nbCornerVtx[baseLocalID] );
HalfEdge *he = baseQuad->halfEdges[ MOD4[baseLocalID] ].sectorStart;
nbCorners.pushBit( he->patch->cornerVtx[ MOD4[he->localID+1] ] == 2 );
HalfEdge *heEnd = he->twin;
he = he->PrevInFace();
do
{
nbCorners.pushBit( he->patch->cornerVtx[ he->localID ] == 2 );
he = ( he->BndEdge() && baseQuad->bndVtx[baseLocalID] ) ? NULL : he->NextByHead(); // make sure we only step over BndEdge if it is dangling.
} while (( he != NULL ) && (he != heEnd));
}
unsigned short COptimizeSubDBuilder::FindNeighborVertex( HalfEdge** ppOutMirrorEdge, const HalfEdge *pHalfEdge, int indexAlongEdge )
// Finds neighboring vertex along the mirror edge of pHalfEdge.
// Returns the index of the vertex.
// pOutMirrorEdge is the mirror edge we took this vertex from.
// pHalfEdge is the shared edge who's mirror we want to find.
// indexAlongEdge is the index of the vertex along the edge. ( 0 or 1 only )
{
HalfEdge* pMirrorEdge = pHalfEdge->twin;
unsigned short vertexID = (unsigned short)-1;
if ( pMirrorEdge )
{
vertexID = pMirrorEdge->patch->vtxIDs[ ( pMirrorEdge->localID + indexAlongEdge ) % 4 ] ;
}
if ( ppOutMirrorEdge )
{
*ppOutMirrorEdge = pMirrorEdge;
}
return vertexID;
}
// Computes the neighboring texcoords ( Interior, EdgeV, EdgeU, Corner ) for each vertex
// texcoords are computed in such a way that every shared edge or corner computes the same values
// this is used as a tie-breaking scheme for creating consistent texture sampling for displacement maps
void COptimizeSubDBuilder::ComputeNeighborTexcoords( SubD_Face_t *baseQuad )
{
unsigned short p = baseQuad->patchID;
unsigned short invalidNeighborValue = (unsigned short)-1;
// Loop over all 4 verts of the quad
for ( int i=0; i<4; ++i )
{
unsigned short index = baseQuad->vtxIDs[i];
// Interior point is alway the current corner
baseQuad->vUV0[i] = VTXIDX( index );
// Assert( index == baseQuad->vUV0[i] );
// Default to original texcoord values for 1 and 2
baseQuad->vUV1[i] = baseQuad->vUV0[i];
baseQuad->vUV2[i] = baseQuad->vUV0[i];
// Find the texture coordinates of our neighbors
// Only keep the texture coordinates of the neighbor with the greatest quad index
HalfEdge* pMirrorEdgeV = NULL;
// V edge ( store the UVs of the patch with the greatest ID )
unsigned short iNeighborPatchV = invalidNeighborValue;
unsigned short iNeighborV = FindNeighborVertex( &pMirrorEdgeV, &baseQuad->halfEdges[ i ], 1 );
if ( iNeighborV != invalidNeighborValue ) // hard edge test
{
iNeighborPatchV = pMirrorEdgeV->patch->patchID;
if ( iNeighborPatchV > p )
{
baseQuad->vUV1[i] = VTXIDX( iNeighborV );
}
}
HalfEdge* pMirrorEdgeU = NULL;
// U edge ( store the UVs of the patch with the greatest ID )
unsigned short iNeighborPatchU = invalidNeighborValue;
unsigned short iNeighborU = FindNeighborVertex( &pMirrorEdgeU, &baseQuad->halfEdges[ (i+3)%4 ], 0 );
if ( iNeighborU != invalidNeighborValue ) // hard edge test
{
iNeighborPatchU = pMirrorEdgeU->patch->patchID;
if ( iNeighborPatchU > p )
{
baseQuad->vUV2[i] = VTXIDX( iNeighborU );
}
}
// Corner ( store the UVs of the patch with the greatest ID ).
// Walk from NeighborV to NeighborU and store data for the largest patch ID.
// We may redundantly check NeighborPatchU here if this is a valence 3 vertex.
HalfEdge* pMirrorEdgeCorner = pMirrorEdgeV;
unsigned short iNeighborPatch = invalidNeighborValue;
unsigned short iMaxNeighborCorner = index;
unsigned short iMaxPatch = baseQuad->patchID;
if ( pMirrorEdgeCorner )
{
do
{
HalfEdge* pNextEdge = pMirrorEdgeCorner->NextInFace();
unsigned short iNeighborCorner = FindNeighborVertex( &pMirrorEdgeCorner, pNextEdge, 1 );
if ( iNeighborCorner != invalidNeighborValue ) // hard edge test
{
iNeighborPatch = pMirrorEdgeCorner->patch->patchID;
if ( pMirrorEdgeCorner->patch->patchID > iMaxPatch )
{
iMaxPatch = pMirrorEdgeCorner->patch->patchID;
iMaxNeighborCorner = iNeighborCorner;
}
}
} while( iNeighborPatch != iNeighborPatchU && pMirrorEdgeCorner );
}
// Determine whether We still need to check against U and V adjacent patches
if ( pMirrorEdgeU && ( pMirrorEdgeU->patch->patchID > iMaxPatch ) )
{
iMaxPatch = pMirrorEdgeU->patch->patchID;
iMaxNeighborCorner = iNeighborU;
}
if ( pMirrorEdgeV && ( pMirrorEdgeV->patch->patchID > iMaxPatch ) )
{
iMaxPatch = pMirrorEdgeV->patch->patchID;
iMaxNeighborCorner = iNeighborV;
}
baseQuad->vUV3[i] = VTXIDX( iMaxNeighborCorner );
}
}
void DumpPatchLite( SubD_Face_t *patch )
{
Msg( "Patch: %d\n", patch->patchID );
Msg( " vtxIDs: %d %d %d %d\n", patch->vtxIDs[0], patch->vtxIDs[1], patch->vtxIDs[2], patch->vtxIDs[3] );
Msg( " halfEdges.twin: %d/%d %d/%d %d/%d %d/%d\n",
patch->halfEdges[0].twin ? patch->halfEdges[0].twin->patch->patchID : -1, patch->halfEdges[0].twin ? patch->halfEdges[0].twin->localID: -1,
patch->halfEdges[1].twin ? patch->halfEdges[1].twin->patch->patchID : -1, patch->halfEdges[1].twin ? patch->halfEdges[1].twin->localID: -1,
patch->halfEdges[2].twin ? patch->halfEdges[2].twin->patch->patchID : -1, patch->halfEdges[2].twin ? patch->halfEdges[2].twin->localID: -1,
patch->halfEdges[3].twin ? patch->halfEdges[3].twin->patch->patchID : -1, patch->halfEdges[3].twin ? patch->halfEdges[3].twin->localID: -1 );
Msg( " halfEdges.sectorStart: %d/%d %d/%d %d/%d %d/%d\n",
patch->halfEdges[0].sectorStart ? patch->halfEdges[0].sectorStart->patch->patchID : -1, patch->halfEdges[0].sectorStart ? patch->halfEdges[0].sectorStart->localID: -1,
patch->halfEdges[1].sectorStart ? patch->halfEdges[1].sectorStart->patch->patchID : -1, patch->halfEdges[1].sectorStart ? patch->halfEdges[1].sectorStart->localID: -1,
patch->halfEdges[2].sectorStart ? patch->halfEdges[2].sectorStart->patch->patchID : -1, patch->halfEdges[2].sectorStart ? patch->halfEdges[2].sectorStart->localID: -1,
patch->halfEdges[3].sectorStart ? patch->halfEdges[3].sectorStart->patch->patchID : -1, patch->halfEdges[3].sectorStart ? patch->halfEdges[3].sectorStart->localID: -1 );
}
// Rotate a particular face one step (element N grabs from element N-1)
void COptimizeSubDBuilder::RotateOnce( SubD_Face_t *pPatch )
{
// Msg( "- Before ------------------------------------------------------------------------------------------\n" );
// DumpPatchLite( pPatch );
SubD_Face_t tmpFace;
memcpy( &tmpFace, pPatch, sizeof( SubD_Face_t ) );
HalfEdge *pTwins[4] = { NULL, NULL, NULL, NULL };
for ( int i=0; i<4; i++ )
{
pTwins[i] = pPatch->halfEdges[i].twin; // Point to each HalfEdge's twin
if ( pTwins[i] )
{
Assert( pTwins[i]->twin == &(pPatch->halfEdges[i]) ); // ith twin should be pointing back to ith HalfEdge
}
}
for ( int i=0; i<4; i++ )
{
pPatch->vtxIDs[i] = tmpFace.vtxIDs[(i+3)%4]; // Grab from n-1
pPatch->bndEdge[i] = tmpFace.bndEdge[(i+3)%4];
pPatch->bndVtx[i] = tmpFace.bndVtx[(i+3)%4];
memcpy( &(pPatch->halfEdges[i]), &(tmpFace.halfEdges[(i+3)%4]), sizeof(HalfEdge) );
pPatch->halfEdges[i].localID = i;
pPatch->halfEdges[i].sectorStart = &pPatch->halfEdges[i];
}
for ( int i=0; i<4; i++ )
{
if ( pTwins[i] )
{
pTwins[i]->twin = &(pPatch->halfEdges[(i+1)%4]); // Record twin's twin after we've rotated the local patch data
}
}
// Msg( "- After ------------------------------------------------------------------------------------------\n" );
// DumpPatchLite( pPatch );
// Msg( "---------------------------------------------------------------------------------------------------\n" );
// Msg( "---------------------------------------------------------------------------------------------------\n\n" );
}
void COptimizeSubDBuilder::RotateFace( SubD_Face_t *pPatch, int nTimesToRotate )
{
for ( int i=0; i<nTimesToRotate; i++ )
{
RotateOnce( pPatch );
}
}
int COptimizeSubDBuilder::FaceEdgeIndex( SubD_Face_t *pFace, HalfEdge *pEdge )
{
int i = 0;
while ( &(pFace->halfEdges[i]) != pEdge )
{
i++;
}
return i;
}
void COptimizeSubDBuilder::Propagate( CUtlVector<Orientation> & orientationArray, HalfEdge *pEdge, bool dir )
{
Assert( pEdge );
while( true )
{
HalfEdge *pNeighborEdge = pEdge->twin;
if ( !pNeighborEdge )
break; // Stop at mesh boundaries.
SubD_Face_t *pFace = pNeighborEdge->patch;
if ( !pFace )
break; // Stop at mesh boundaries.
int nEdgeIndex = FaceEdgeIndex( pFace, pNeighborEdge );
Orientation & faceOrientation = orientationArray[pFace->patchID];
if ( nEdgeIndex == 1 || nEdgeIndex == 3 )
{
if ( faceOrientation.uSet )
{
Assert( faceOrientation.u == ( ( nEdgeIndex == 1 ) ^ dir ) );
break;
}
faceOrientation.SetU( ( nEdgeIndex == 1 ) ^ dir );
}
else // if ( nEdgeIndex == 0 || nEdgeIndex == 2 )
{
if ( faceOrientation.vSet )
{
Assert( faceOrientation.v == ( ( nEdgeIndex == 0 ) ^ dir ) );
break;
}
faceOrientation.SetV( ( nEdgeIndex == 0 ) ^ dir );
}
pEdge = pNeighborEdge->NextInFace()->NextInFace();
}
}
static HalfEdge *FaceEdge( SubD_Face_t *pPatch, int idx )
{
int i = 0;
HalfEdge *pEdge = &pPatch->halfEdges[0];
while ( i != idx )
{
i++;
pEdge = pEdge->NextInFace();
}
return pEdge;
}
// Reorient faces in order to avoid parametric discontinuities.
void COptimizeSubDBuilder::ConsistentPatchOrientation()
{
CUtlVector<Orientation> orientationArray;
orientationArray.AddMultipleToTail( m_numPatches );
for( int f = 0; f < m_numPatches; f++ )
{
SubD_Face_t *pPatch = &m_faceList[f];
HalfEdge *pEdges = &pPatch->halfEdges[0];
if ( !orientationArray[f].uSet )
{
orientationArray[f].SetU( false );
Propagate( orientationArray, pEdges+1, false );
Propagate( orientationArray, pEdges+3, true );
}
if ( !orientationArray[f].vSet )
{
orientationArray[f].SetV( false );
Propagate( orientationArray, pEdges+0, false );
Propagate( orientationArray, pEdges+2, true );
}
}
for( int f = 0; f < m_numPatches; f++ )
{
SubD_Face_t *pPatch = &m_faceList[f];
const Orientation &o = orientationArray[f]; // Determine edge from orientation flags.
static const int nTimesToRotate[4] = {0, 1, 3, 2};
const int idx = nTimesToRotate[(o.v << 1) + o.u];
RotateFace( pPatch, idx );
}
}
void COptimizeSubDBuilder::TagCreases()
{
static int MOD4[] = {0,1,2,3,0,1,2,3};
for (unsigned short i=0; i<m_numPatches; i++)
{
SubD_Face_t *pPatch = &m_faceList[i];
for ( int k=0; k<4; k++ ) // for all vertices
{
if ( pPatch->halfEdges[k].twin != NULL )
{
HalfEdge *twin = pPatch->halfEdges[k].twin;
SubD_Face_t *nbQuad = twin->patch;
int quad0vtx0ID = pPatch->vtxIDs[ MOD4[k+0] ];
int quad1vtx0ID = nbQuad->vtxIDs[ MOD4[twin->localID+1] ];
int quad0vtx1ID = pPatch->vtxIDs[ MOD4[k+1] ];
int quad1vtx1ID = nbQuad->vtxIDs[ MOD4[twin->localID+0] ];
if ( ( VTXNOR( quad0vtx0ID ) != VTXNOR( quad1vtx0ID ) ) ||
( VTXNOR( quad0vtx1ID ) != VTXNOR( quad1vtx1ID ) ) )
{
pPatch->bndEdge[k] = true;
pPatch->bndVtx[MOD4[k+0]] = true;
pPatch->bndVtx[MOD4[k+1]] = true;
}
}
}
}
}
}; // namespace
+171
View File
@@ -0,0 +1,171 @@
#ifndef OPTIMIZE_SUBD_H
#define OPTIMIZE_SUBD_H
#pragma once
#include "optimize.h"
#include "studio.h"
// Maximum number of points that can be part of a subd quad.
// This includes the 4 interior points of the quad, plus the 1-ring neighborhood
#define MAX_SUBD_POINTS 32
#define MAX_SUBD_ONERING_POINTS (MAX_SUBD_POINTS + 4*5)
#define CORNER_WITH_SMOOTHBNDTANGENTS 2
namespace OptimizedModel
{
struct SubD_Face_t;
// minimal HalfEdge structure, embedded in a face (#halfedges = #vertexperface)
struct HalfEdge
{
HalfEdge *twin;
HalfEdge *sectorStart;
unsigned char localID; // local halfedge/vertex ID
SubD_Face_t *patch;
inline HalfEdge *NextInFace();
inline HalfEdge *PrevInFace();
inline HalfEdge *NextByHead();
inline HalfEdge *PrevByHead();
inline HalfEdge *NextByTail();
inline HalfEdge *PrevByTail();
inline unsigned short &BndEdge();
};
struct Orientation
{
uint8 u : 1;
uint8 v : 1;
uint8 uSet : 1;
uint8 vSet : 1;
void SetU( bool b )
{
Assert( !uSet );
u = b;
uSet = true;
}
void SetV( bool b )
{
Assert( !vSet );
v = b;
vSet = true;
}
Orientation() { uSet = vSet = false; }
};
struct SubD_Face_t
{
unsigned short patchID; // for building our 4 sets of watertight UVs
unsigned short vtxIDs[4];
unsigned short oneRing[MAX_SUBD_ONERING_POINTS];
unsigned short vtx1RingSize[4]; // Pre-calculated prefixes for the first 4 points
unsigned short vtx1RingCenterQuadOffset[4]; // start of inner quad vertices in vertex 1-ring
unsigned short valences[4]; // Valences for the first 4 points in current sector
unsigned short minOneRingIndex[4]; // Location in oneRing array to start applying stencil (determined by lowest position index)
unsigned short bndVtx[4]; // is vertex on the boundary?
unsigned short bndEdge[4]; // is associated edge on the boundary?
unsigned short cornerVtx[4]; // should a boundary-vertex be treated as a corner?
unsigned short nbCornerVtx[4]; // bitfield, for all on-edge neighbors record if corner vertices
unsigned short loopGapAngle[4];
unsigned short edgeBias[8];
unsigned short vUV0[4]; // Vert index for Interior TexCoord (for vtxIDs[0-3])
unsigned short vUV1[4]; // Vert index for Parametric V TexCoord (for vtxIDs[0-3])
unsigned short vUV2[4]; // Vert index for Parametric U TexCoord (for vtxIDs[0-3])
unsigned short vUV3[4]; // Vert index for Corner TexCoord (for vtxIDs[0-3])
HalfEdge halfEdges[4];
void SetEdgeBias(int localID, float f0, float f1)
{
if (halfEdges[localID].twin==NULL) return;
edgeBias[2*localID] = f0 * 32768.0f;
edgeBias[2*localID+1] = f1 * 32768.0f;
halfEdges[localID].twin->patch->edgeBias[ 2*halfEdges[localID].twin->localID+1 ] = (1.0f - f0) * 32768.0f;
halfEdges[localID].twin->patch->edgeBias[ 2*halfEdges[localID].twin->localID ] = (1.0f - f1) * 32768.0f;
}
};
inline HalfEdge *HalfEdge::NextInFace()
{
static int MOD4[8] = {0,1,2,3,0,1,2,3};
return &patch->halfEdges[MOD4[localID+1]];
}
inline HalfEdge *HalfEdge::PrevInFace()
{
static int MOD4[8] = {0,1,2,3,0,1,2,3};
return &patch->halfEdges[MOD4[localID+3]];
}
inline HalfEdge *HalfEdge::NextByHead() { return (twin==NULL)? NULL : twin->PrevInFace(); }
inline HalfEdge *HalfEdge::PrevByHead() { return NextInFace()->twin; }
inline HalfEdge *HalfEdge::NextByTail() { return PrevInFace()->twin; }
inline HalfEdge *HalfEdge::PrevByTail() { return (twin==NULL)? NULL : twin->NextInFace(); }
inline bool FaceIsRegular( SubD_Face_t *patch )
{
return ( patch->valences[0] == 4 && patch->valences[1] == 4 && patch->valences[2] == 4 && patch->valences[3] == 4 ) &&
( patch->bndVtx[0] == false && patch->bndVtx[1] == false && patch->bndVtx[2] == false && patch->bndVtx[3] == false ) &&
( patch->bndEdge[0] == false && patch->bndEdge[1] == false && patch->bndEdge[2] == false && patch->bndEdge[3] == false );
}
inline unsigned short &HalfEdge::BndEdge() { return patch->bndEdge[localID]; }
typedef CUtlVector<SubD_Face_t> SubD_FaceList_t;
typedef CUtlVector<Vertex_t> SubD_VertexList_t;
typedef const mstudio_meshvertexdata_t *SubD_VertexData_t;
class COptimizeSubDBuilder
{
public:
COptimizeSubDBuilder(SubD_FaceList_t& subDFaceList, const SubD_VertexList_t& vertexList, const SubD_VertexData_t &vertexData, bool bIsTagged, bool bMendVertices=true );
void ProcessPatches( bool bIsTagged, bool bMendVertices );
HalfEdge *FindTwin(HalfEdge &he);
void CheckForManifoldMesh( );
void BuildNeighborhoodInfo( );
void ComputeSectorStart( SubD_Face_t *quad, unsigned short k );
void ComputePerVertexInfo( SubD_Face_t *baseQuad, unsigned short baseLocalID );
void ComputeSectorAngle( SubD_Face_t *baseQuad, unsigned short baseLocalID );
void ComputeNbCorners( SubD_Face_t *baseQuad, unsigned short baseLocalID );
void ComputeSectorOneRing( SubD_Face_t *baseQuad, unsigned short baseLocalID );
unsigned short FindNeighborVertex( HalfEdge** ppOutMirrorEdge, const HalfEdge *pHalfEdge, int indexAlongEdge );
void ComputeNeighborTexcoords( SubD_Face_t *baseQuad );
void MendVertices( SubD_Face_t *quad, unsigned short baseLocalID );
void TagCreases();
private:
// Routines used for orienting faces for edge consistency
void RotateOnce( SubD_Face_t *pFace );
void RotateFace( SubD_Face_t *pFace, int nTimesToRotate );
int FaceEdgeIndex( SubD_Face_t *pFace, HalfEdge *pEdge );
void Propagate( CUtlVector<Orientation> & orientationArray, HalfEdge *pEdge, bool dir );
void ConsistentPatchOrientation();
void RemapIndices();
void SetMinOneRingIndices();
SubD_FaceList_t &m_faceList;
const SubD_VertexList_t &m_vtxList;
const SubD_VertexData_t &m_vtxData;
int m_numPatches;
CUtlVector<int> m_IndexRemapTable;
};
}; // namespace OptimizedModel
#endif // OPTIMIZE_SUBD_H
+260
View File
@@ -0,0 +1,260 @@
//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======//
//
// Purpose:
//
//===========================================================================//
#include <stdlib.h>
#include <tier0/dbg.h>
#include "interface.h"
#include "istudiorender.h"
#include "studio.h"
#include "optimize.h"
#include "cmdlib.h"
#include "studiomdl.h"
#include "perfstats.h"
#include "tier1/tier1_logging.h"
extern void MdlError( char const *pMsg, ... );
static StudioRenderConfig_t s_StudioRenderConfig;
class CStudioDataCache : public CBaseAppSystem<IStudioDataCache>
{
public:
bool VerifyHeaders( studiohdr_t *pStudioHdr );
vertexFileHeader_t *CacheVertexData( studiohdr_t *pStudioHdr );
};
static CStudioDataCache g_StudioDataCache;
EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CStudioDataCache, IStudioDataCache, STUDIO_DATA_CACHE_INTERFACE_VERSION, g_StudioDataCache );
/*
=================
VerifyHeaders
Minimal presence and header validation, no data loads
Return true if successful, false otherwise.
=================
*/
bool CStudioDataCache::VerifyHeaders( studiohdr_t *pStudioHdr )
{
// default valid
return true;
}
/*
=================
CacheVertexData
Cache model's specified dynamic data
=================
*/
vertexFileHeader_t *CStudioDataCache::CacheVertexData( studiohdr_t *pStudioHdr )
{
// minimal implementation - return persisted data
return (vertexFileHeader_t*)pStudioHdr->VertexBase();
}
static void UpdateStudioRenderConfig( void )
{
memset( &s_StudioRenderConfig, 0, sizeof(s_StudioRenderConfig) );
s_StudioRenderConfig.bEyeMove = true;
s_StudioRenderConfig.fEyeShiftX = 0.0f;
s_StudioRenderConfig.fEyeShiftY = 0.0f;
s_StudioRenderConfig.fEyeShiftZ = 0.0f;
s_StudioRenderConfig.fEyeSize = 10.0f;
s_StudioRenderConfig.bSoftwareSkin = false;
s_StudioRenderConfig.bNoHardware = false;
s_StudioRenderConfig.bNoSoftware = false;
s_StudioRenderConfig.bTeeth = true;
s_StudioRenderConfig.drawEntities = true;
s_StudioRenderConfig.bFlex = true;
s_StudioRenderConfig.bEyes = true;
s_StudioRenderConfig.bWireframe = false;
s_StudioRenderConfig.bDrawZBufferedWireframe = false;
s_StudioRenderConfig.bDrawNormals = false;
s_StudioRenderConfig.skin = 0;
s_StudioRenderConfig.maxDecalsPerModel = 0;
s_StudioRenderConfig.bWireframeDecals = false;
s_StudioRenderConfig.fullbright = false;
s_StudioRenderConfig.bSoftwareLighting = false;
s_StudioRenderConfig.bShowEnvCubemapOnly = false;
g_pStudioRender->UpdateConfig( s_StudioRenderConfig );
}
static CBufferedLoggingListener s_BufferedLoggingListener;
void SpewPerfStats( studiohdr_t *pStudioHdr, const char *pFilename, unsigned int flags )
{
char fileName[260];
vertexFileHeader_t *pNewVvdHdr;
vertexFileHeader_t *pVvdHdr = 0;
OptimizedModel::FileHeader_t *pVtxHdr = 0;
studiohwdata_t studioHWData;
int vvdSize = 0;
const char *prefix[] = { ".dx90.vtx", ".dx80.vtx", ".sw.vtx" };
const int numVtxFiles = ( g_gameinfo.bSupportsDX8 && !g_bFastBuild ) ? ARRAYSIZE( prefix ) : 1;
bool bExtraData = (pStudioHdr->flags & STUDIOHDR_FLAGS_EXTRA_VERTEX_DATA) != 0;
if( !( flags & SPEWPERFSTATS_SHOWSTUDIORENDERWARNINGS ) )
{
LoggingSystem_PushLoggingState();
LoggingSystem_RegisterLoggingListener( &s_BufferedLoggingListener );
}
// no stats on these
if (!pStudioHdr->numbodyparts)
return;
// Need to update the render config to spew perf stats.
UpdateStudioRenderConfig();
// persist the vvd data
Q_StripExtension( pFilename, fileName, sizeof( fileName ) );
strcat( fileName, ".vvd" );
if (FileExists( fileName ))
{
vvdSize = LoadFile( fileName, (void**)&pVvdHdr );
}
else
{
MdlError( "Could not open '%s'\n", fileName );
}
// validate header
if (pVvdHdr->id != MODEL_VERTEX_FILE_ID)
{
MdlError( "Bad id for '%s' (got %d expected %d)\n", fileName, pVvdHdr->id, MODEL_VERTEX_FILE_ID);
}
if (pVvdHdr->version != MODEL_VERTEX_FILE_VERSION)
{
MdlError( "Bad version for '%s' (got %d expected %d)\n", fileName, pVvdHdr->version, MODEL_VERTEX_FILE_VERSION);
}
if (pVvdHdr->checksum != pStudioHdr->checksum)
{
MdlError( "Bad checksum for '%s' (got %d expected %d)\n", fileName, pVvdHdr->checksum, pStudioHdr->checksum);
}
if (pVvdHdr->numFixups)
{
// need to perform mesh relocation fixups
// allocate a new copy
pNewVvdHdr = (vertexFileHeader_t *)malloc( vvdSize );
if (!pNewVvdHdr)
{
MdlError( "Error allocating %d bytes for Vertex File '%s'\n", vvdSize, fileName );
}
Studio_LoadVertexes( pVvdHdr, pNewVvdHdr, 0, true, bExtraData );
// discard original
free( pVvdHdr );
pVvdHdr = pNewVvdHdr;
}
// iterate all ???.vtx files
for (int j = 0; j< numVtxFiles; j++)
{
// make vtx filename
Q_StripExtension( pFilename, fileName, sizeof( fileName ) );
strcat( fileName, prefix[j] );
// persist the vtx data
if (FileExists(fileName))
{
LoadFile( fileName, (void**)&pVtxHdr );
}
else
{
MdlError( "Could not open '%s'\n", fileName );
}
// validate header
if (pVtxHdr->version != OPTIMIZED_MODEL_FILE_VERSION)
{
MdlError( "Bad version for '%s' (got %d expected %d)\n", fileName, pVtxHdr->version, OPTIMIZED_MODEL_FILE_VERSION );
}
if (pVtxHdr->checkSum != pStudioHdr->checksum)
{
MdlError( "Bad checksum for '%s' (got %d expected %d)\n", fileName, pVtxHdr->checkSum, pStudioHdr->checksum );
}
// studio render will request these through cache interface
pStudioHdr->SetVertexBase( (void *)pVvdHdr );
pStudioHdr->SetIndexBase( (void *)pVtxHdr );
g_pStudioRender->LoadModel( pStudioHdr, pVtxHdr, &studioHWData );
if( flags & SPEWPERFSTATS_SHOWPERF )
{
if( flags & SPEWPERFSTATS_SPREADSHEET )
{
printf( "%s,%s,%d,", fileName, prefix[j], studioHWData.m_NumLODs - studioHWData.m_RootLOD );
}
else
{
printf( "\n" );
printf( "Performance Stats: %s\n", fileName );
printf( "------------------\n" );
}
}
int i;
if( flags & SPEWPERFSTATS_SHOWPERF )
{
for( i = studioHWData.m_RootLOD; i < studioHWData.m_NumLODs; i++ )
{
DrawModelInfo_t drawModelInfo;
drawModelInfo.m_Skin = 0;
drawModelInfo.m_Body = 0;
drawModelInfo.m_HitboxSet = 0;
drawModelInfo.m_pClientEntity = 0;
drawModelInfo.m_pColorMeshes = 0;
drawModelInfo.m_pStudioHdr = pStudioHdr;
drawModelInfo.m_pHardwareData = &studioHWData;
CUtlBuffer statsOutput( 0, 0, CUtlBuffer::TEXT_BUFFER );
if( !( flags & SPEWPERFSTATS_SPREADSHEET ) )
{
printf( "LOD:%d\n", i );
}
drawModelInfo.m_Lod = i;
DrawModelResults_t results;
g_pStudioRender->GetPerfStats( &results, drawModelInfo, &statsOutput );
if( flags & SPEWPERFSTATS_SPREADSHEET )
{
printf( "%d,%d,%d,", results.m_ActualTriCount, results.m_NumBatches, results.m_NumMaterials );
}
else
{
printf( " actual tris:%d\n", ( int )results.m_ActualTriCount );
printf( " texture memory bytes: %d (only valid in a rendering app)\n", ( int )results.m_TextureMemoryBytes );
printf( ( char * )statsOutput.Base() );
}
}
if( flags & SPEWPERFSTATS_SPREADSHEET )
{
printf( "\n" );
}
}
g_pStudioRender->UnloadModel( &studioHWData );
free(pVtxHdr);
}
if (pVvdHdr)
free(pVvdHdr);
if( !( flags & SPEWPERFSTATS_SHOWSTUDIORENDERWARNINGS ) )
{
LoggingSystem_PopLoggingState();
s_BufferedLoggingListener.EmitBufferedSpew();
}
}
+25
View File
@@ -0,0 +1,25 @@
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================//
#ifndef PERFSTATS_H
#define PERFSTATS_H
#ifdef _WIN32
#pragma once
#endif
#include "studio.h"
#include "optimize.h"
enum
{
SPEWPERFSTATS_SHOWSTUDIORENDERWARNINGS = 1,
SPEWPERFSTATS_SHOWPERF = 2,
SPEWPERFSTATS_SPREADSHEET = 4,
};
void SpewPerfStats( studiohdr_t *pStudioHdr, const char *pFilename, unsigned int flags );
#endif // PERFSTATS_H
+402
View File
@@ -0,0 +1,402 @@
//========= Copyright c 1996-2008, Valve Corporation, All rights reserved. ============//
//
// Purpose: Builds physics2 collision models from studio model source
//
// $Workfile: $
// $Date: $
// $NoKeywords: $
//=============================================================================//
#include <string.h>
#include "tier1/tier1.h"
#include "tier1/smartptr.h"
#include "tier2/p4helpers.h"
#include "datalinker.h"
#include "vphysics2_interface.h"
#include "vphysics2_interface_flags.h"
#include "alignedarray.h"
#include "studiomdl.h"
#include "filesystem_tools.h"
#include "collisionmodelsource.h"
#include "physics2collision.h"
#include "physdll.h"
#include "phzfile.h"
static IPhysics2Cook *g_pCook;
struct bodypart_t
{
IPhysics2CookedMeshBase* mesh;
int bone;
bodypart_t(){}
bodypart_t(IPhysics2CookedMeshBase* _mesh, int _bone):mesh(_mesh),bone(_bone){}
};
class CPhysics2CollisionBuilder: public CCollisionModelSource
{
public:
void Init(CCollisionModelSource *pSource)
{
*static_cast<CCollisionModelSource*>(this) = *pSource;
}
void Shutdown()
{
Destroy(m_bodyparts);
}
void Destroy(CUtlVector<bodypart_t>&bodyparts)
{
for(int i = 0; i < bodyparts.Size(); ++i)
g_pCook->Destroy(bodyparts[i].mesh);
}
void Build()
{
if(m_isJointed)
BuildJointed();
else
BuildRigid();
}
void BuildRigid();
void BuildJointed();
void Write();
void Destroy(CUtlVector<IPhysics2CookedMeshBase*> &arrPolytopes);
CUtlVector<bodypart_t> m_bodyparts;
};
class CMeshAdaptor:public CPhysics2CustomMeshBase
{
public:
virtual uint GetType()const {return PHYSICS2_SHAPE_TYPE_CUSTOM;}
virtual uint NumVertices() const {return m_pMesh->numvertices;}
virtual uint NumTriangles() const {return m_pMesh->numfaces;}
virtual void GetVertices(float *pVertsOut, uint nByteStride, const fltx4 &factor = Four_Ones)
{
uint numVerts = m_pMesh->numvertices;
byte *pOut = (byte*)pVertsOut;
for(uint i =0; i< numVerts; ++i)
{
fltx4 vert = MulSIMD(LoadUnaligned3SIMD(&m_pVerts[i+m_pMesh->vertexoffset].x), factor);
StoreUnaligned3SIMD((float*)pOut, vert);
pOut += nByteStride;
}
}
virtual void GetTriangles(int *pTrisOut, uint nByteStride)
{
uint numTris = m_pMesh->numfaces;
byte *pOut = (byte*)pTrisOut;
for(uint i = 0;i < numTris; ++i)
{
const s_face_t *pFace = m_pFaces + i + m_pMesh->faceoffset;
Assert(pFace->a+m_pMesh->vertexoffset < (uint)m_pMesh->numvertices && pFace->b+m_pMesh->vertexoffset < (uint)m_pMesh->numvertices && pFace->c+m_pMesh->vertexoffset < (uint)m_pMesh->numvertices
);
((int*)pOut)[0] = pFace->a;
((int*)pOut)[1] = pFace->b;
((int*)pOut)[2] = pFace->c;
pOut += nByteStride;
}
}
virtual uint GetSizeOf()const {return sizeof(*this);}
const s_face_t *m_pFaces;// non-offset faces
const Vector *m_pVerts; // non-offset verts
const s_mesh_t *m_pMesh;
};
static CPhysics2CollisionBuilder g_builder;
void Physics2Collision_Build(CCollisionModelSource *pSource)
{
g_pCook = g_pPhysics2->GetCook();
g_builder.Init(pSource);
g_builder.Build();
}
void Physics2Collision_Write()
{
g_builder.Write();
}
void CPhysics2CollisionBuilder::BuildJointed()
{
// first, go through all meshes and determine what bones they belong to
//CUtlVector<CUtlVector<int> > arrMeshBones(0,m_pModel->nummeshes);
// remap it : bone -> faces
CUtlVector<CUtlVector<s_face_t> > arrBoneFaces;
arrBoneFaces.SetSize(m_pModel->numbones);
// Constructing elements of the array. This is irritating, there should be a method to do that..
for(int i =0; i < m_pModel->numbones; ++i)
new(&arrBoneFaces[i])CUtlVector<s_mesh_t *>(32);
//for(int i = 0; i < m_pModel->nummeshes; ++i)
// new(&arrMeshBones[i])CUtlVector<int>();
// for each mesh, find bone(s) it belongs to and push it to that bone (those bones)
for(int nMesh = 0; nMesh < m_pModel->nummeshes; ++nMesh)
{
s_mesh_t *pMesh = m_pModel->mesh + m_pModel->meshindex[nMesh];
for(int nFace = 0; nFace < pMesh->numfaces; ++nFace)
{
s_face_t face = GetGlobalFace(pMesh, nFace);
s_boneweight_t &boneweight = m_pModel->vertex[face.a].boneweight;
if(boneweight.numbones)
{
int boneIndex = RemapBone(boneweight.bone[0]);
if(boneIndex >= 0 && boneIndex < m_pModel->numbones)
arrBoneFaces[boneIndex].AddToTail(face);
}
}
}
// for each bone, we have 0..many meshes now; compile the meshes; we don't try to share the meshes between different bones here,
// the idea is that we'll have rigid binding to skeleton, possibly sometimes multiple meshes to the same bone, but not the same mesh
// to multiple bones
CUtlVector<Vector> bonespaceVerts;
bonespaceVerts.SetCount(m_pModel->numvertices);
for(int nBone = 0; nBone < m_pModel->numbones; ++nBone)
{
CUtlVector<IPhysics2CookedMeshBase*> arrPolytopes;
bodypart_t bodypart;
bodypart.bone = nBone;
bodypart.mesh = NULL;
CUtlVector<s_face_t> &arrFaces = arrBoneFaces[nBone];
if(ShouldProcessBone(nBone) && arrFaces.Size())
{
// convert ALL vertices into this bone's frame (it's easier)
ConvertToBoneSpace(nBone, bonespaceVerts);
// cook one polytope for each s_mesh_t (out of the Mesh interface)
s_mesh_t mesh;
mesh.faceoffset = 0;
mesh.numfaces = arrFaces.Size();
mesh.vertexoffset = 0;
mesh.numvertices = bonespaceVerts.Size();
CMeshAdaptor adaptor;
adaptor.m_pMesh = &mesh;
adaptor.m_pFaces = arrFaces.Base();
adaptor.m_pVerts = bonespaceVerts.Base();
if(IPhysics2CookedPolytope *pCookedPolytope = g_pCook->CookPolytope(&adaptor))
arrPolytopes.AddToTail(pCookedPolytope);
}
if(arrPolytopes.Size() > 1)
{
if(m_allowConcaveJoints)
{
bodypart.mesh = g_pCook->CookMopp(arrPolytopes.Base(), arrPolytopes.Size());
}
else
{
bodypart.mesh = g_pCook->CookPolytopeFromMeshes(arrPolytopes.Base(), arrPolytopes.Size());
}
Destroy(arrPolytopes);
}
else
if(arrPolytopes.Size() == 1)
{
bodypart.mesh = arrPolytopes[0];
}
if(bodypart.mesh)
m_bodyparts.AddToTail(bodypart);
}
}
void CPhysics2CollisionBuilder::BuildRigid()
{
CUtlVector<Vector> worldspaceVerts;
worldspaceVerts.SetCount(m_pModel->numvertices);
ConvertToWorldSpace( worldspaceVerts );
m_bodyparts.SetSize(0);
bool bValid = true;
if ( m_allowConcave )
{
CUtlVector<CMeshAdaptor> arrMeshes;
int numMeshes = m_pModel->nummeshes;
arrMeshes.SetCount(numMeshes);
for ( int i = 0; i < numMeshes; i++ )
{
s_mesh_t *pMesh = m_pModel->mesh + m_pModel->meshindex[i];
arrMeshes[i].m_pFaces = m_pModel->face;
arrMeshes[i].m_pVerts = worldspaceVerts.Base();//m_pModel->vertex;
arrMeshes[i].m_pMesh = pMesh;
}
// this is one way to do it: make one polysoup
//g_pCook->CookPolysoupFromMeshes(arrMeshes.Base(), numMeshes);
// another way is to create a bunch of convex polytopes
for ( int i = 0; i < numMeshes; i++ )
{
IPhysics2CookedPolytope *polytope = g_pCook->CookPolytope(&arrMeshes[i]);
if(polytope)
{
m_bodyparts.AddToTail(bodypart_t(polytope, -1));
}
}
}
if ( m_bodyparts.Count() > m_maxConvex )
{
MdlWarning("COSTLY COLLISION MODEL!!!! (%d parts - %d allowed)\n", m_bodyparts.Count(), m_maxConvex );
bValid = false;
}
if ( !bValid && m_bodyparts.Count() )
{
MdlWarning("Error with convex elements of %s, building single convex!!!!\n", m_pModel->filename );
Destroy(m_bodyparts);
}
// either we don't want concave, or there was an error building it
if ( !m_bodyparts.Count() )
{
CUtlVector_Vector4DAligned arrVerts;
arrVerts.SetSize(worldspaceVerts.Count());
for(int i = 0;i < worldspaceVerts.Count(); ++i)
{
const Vector &v = worldspaceVerts[i];
arrVerts[i].Init(v.x,v.y,v.z);
}
IPhysics2CookedPolytope *polytope = g_pCook->CookPolytopeFromVertices((Vector4DAligned*)arrVerts.Base(), worldspaceVerts.Count());
m_bodyparts.AddToTail(bodypart_t(polytope,-1));
}
if(m_bodyparts.Size() > 1)
{
// fold it into one single neat mesh
CUtlVector<IPhysics2CookedMeshBase*>arrMeshes(m_bodyparts.Size(),m_bodyparts.Size());
for(int i = 0;i < m_bodyparts.Size(); ++i)
arrMeshes[i] = m_bodyparts[i].mesh;
IPhysics2CookedMopp *mopp = g_pCook->CookMopp(arrMeshes.Base(), m_bodyparts.Size());
Destroy(m_bodyparts);
if(mopp)
m_bodyparts.AddToTail(bodypart_t(mopp, -1));
}
}
void CPhysics2CollisionBuilder::Destroy(CUtlVector<IPhysics2CookedMeshBase*> &arrPolytopes)
{
for ( int i = 0; i < arrPolytopes.Count(); i++ )
g_pCook->Destroy( arrPolytopes[i] );
arrPolytopes.Purge();
}
void CPhysics2CollisionBuilder::Write()
{
char filename[512];
strcpy( filename, gamedir );
strcat( filename, "models/" );
strcat( filename, m_pOverrideName ? m_pOverrideName : outname );
Q_SetExtension( filename, ".phz", sizeof( filename ) );
if(!m_bodyparts.Size())
{
CPlainAutoPtr< CP4File > spFile( g_p4factory->AccessFile( filename ) );
unlink(filename);
return;
}
DataLinker::Stream stream;
Physics2CollisionHeader_t *pHeader = stream.Write<Physics2CollisionHeader_t>();
pHeader->m_dataVersion = g_pPhysics2->GetSerializeVersion();
pHeader->m_numBones = m_bodyparts.Size();
//if(!m_pModel->numbones)
// pHeader->m_numBones = 1; // there's still 1 pseudo-bone there
Physics2RigidPolyShape_t *pRigids = stream.IStream::WriteAndLinkStrided(&pHeader->m_shapes, sizeof(Physics2RigidPolyShape_t), m_bodyparts.Count());
///
// Note: I want all inertia descriptors to reside together for cache coherency in dynamics phase, so I'm writing inertia first, then the shapes
///
for(int nBodyPart = 0; nBodyPart < m_bodyparts.Size(); ++nBodyPart)
{
int boneIndex = m_bodyparts[nBodyPart].bone;
IPhysics2CookedMeshBase *pMesh = m_bodyparts[nBodyPart].mesh;
const char *boneName = boneIndex < 0 ? "" : m_pModel->localBone[boneIndex].name;
// we'll leave all offsets to NULL if there's no mesh for that bone
if(pMesh)
{
IPhysics2CookedInertia *pInertia = g_pCook->CookInertia(pMesh->GetShape()); // the inertia of the model as a rigid whole
if(pInertia)
{
stream.IStream::Link(&pRigids[nBodyPart].m_inertia, pInertia->Serialize(&stream));
g_pCook->Destroy(pInertia);
}
else
Warning("Could not cook inertia for '%s' #d\n", boneName, boneIndex);
}
pRigids[nBodyPart].m_localBoneIndex = boneIndex;
if(boneIndex >= 0)
{
int globalBoneIndex = m_pModel->boneLocalToGlobal[boneIndex];
pRigids[nBodyPart].m_globalBoneIndex = globalBoneIndex;
}
}
for(int nBodyPart = 0; nBodyPart < m_bodyparts.Size(); ++nBodyPart)
{
bodypart_t &bp = m_bodyparts[nBodyPart];
// we'll leave all offsets to NULL if there's no mesh for that bone
pRigids[nBodyPart].m_shapeType = bp.mesh->GetType();
stream.IStream::Link(&pRigids[nBodyPart].m_shape, bp.mesh->Serialize(&stream));
}
*(char*)stream.WriteBytes(1) = '\n'; // for debugging
for(int nBodyPart = 0; nBodyPart < m_bodyparts.Size(); ++nBodyPart)
{
bodypart_t &bp = m_bodyparts[nBodyPart];
if(bp.bone >= 0)
{
const char *name = m_pModel->localBone[bp.bone].name;
if(name)
{
int nameLen = strlen(name);
char *pNameOut = (char*)stream.WriteBytes(nameLen + 2);
stream.IStream::Link(&pRigids[nBodyPart].m_name, pNameOut);
memcpy(pNameOut, name, nameLen+1);
pNameOut[nameLen+1] = '\n'; // for debugging
}
}
}
uint nDataSize = stream.GetTotalSize();
void *pData = MemAlloc_AllocAligned(nDataSize, 16, __FILE__, __LINE__);
if(stream.Compile(pData))
{
CPlainAutoPtr< CP4File > spFile( g_p4factory->AccessFile( filename ) );
spFile->Edit();
FILE *fp = fopen( filename, "wb" );
if(fp)
{
int numWritten = fwrite(pData, nDataSize, 1, fp);
fclose(fp);
}
else
{
MdlWarning("Error writing %s!!!\n", filename );
}
}
else
{
MdlWarning("Cannot compile the phz data\n");
}
MemAlloc_FreeAligned(pData, __FILE__, __LINE__);
}
+21
View File
@@ -0,0 +1,21 @@
//========= Copyright c 1996-2008, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $Workfile: $
// $Date: $
//
//-----------------------------------------------------------------------------
// $Log: $
//
// $NoKeywords: $
//=============================================================================//
#ifndef PHYSICS2_COLLISION_H
#define PHYSICS2_COLLISION_H
extern void Physics2Collision_Build(class CCollisionModelSource *pSource);
extern void Physics2Collision_Write();
//extern void SetPhysics2CollisionSource(struct s_source_t *pModel);
#endif
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+169
View File
@@ -0,0 +1,169 @@
//-----------------------------------------------------------------------------
// STUDIOMDL.VPC
//
// Project Script
//-----------------------------------------------------------------------------
$Macro SRCDIR "..\.."
$Macro OUTBINDIR "$SRCDIR\..\game\bin"
$Include "$SRCDIR\vpc_scripts\source_exe_con_win32_base.vpc"
$Include "$SRCDIR\vpc_scripts\fbx.vpc"
$Configuration
{
$Compiler
{
$AdditionalIncludeDirectories "$BASE,..\common,..\nvtristriplib,$SRCDIR\Game_Shared, $SRCDIR\ps3sdk\cell\host-common\include,$SRCDIR\ps3sdk\cell\target\common\include"
$PreprocessorDefinitions "$BASE;PROTECTED_THINGS_DISABLE"
}
$Linker
{
$AdditionalDependencies "$BASE winmm.lib libedgegeomtool.Release.Win32.vs8.lib"
$AdditionalLibraryDirectories "$BASE;$SRCDIR\ps3sdk\cell\host-win32\lib"
}
}
$Project "Studiomdl"
{
$Folder "Source Files"
{
$File "..\common\cmdlib.cpp"
$File "..\common\datalinker.cpp"
$File "collisionmodel.cpp"
$File "collisionmodelsource.cpp"
// $File "physics2collision.cpp"
$File "$SRCDIR\public\collisionutils.cpp"
$File "dmxsupport.cpp"
$File "$SRCDIR\public\filesystem_helpers.cpp"
$File "$SRCDIR\public\filesystem_init.cpp"
$File "..\common\filesystem_tools.cpp"
$File "hardwarematrixstate.cpp"
$File "hardwarevertexcache.cpp"
$File "$SRCDIR\public\interpolatortypes.cpp"
$File "$SRCDIR\public\mdlobjects\mdlobjects.cpp"
$File "$SRCDIR\public\movieobjects\movieobjects_compiletools.cpp"
$File "mrmsupport.cpp"
$File "objsupport.cpp"
$File "optimize_subd.cpp"
$File "optimize.cpp"
$File "perfstats.cpp"
$File "..\common\physdll.cpp"
$File "..\common\scriplib.cpp"
$File "simplify.cpp"
$File "$SRCDIR\public\studio.cpp"
$File "$SRCDIR\common\studiobyteswap.cpp"
$File "studiomdl.cpp"
$File "compileclothproxy.cpp"
$File "UnifyLODs.cpp"
$File "v1support.cpp"
$File "write.cpp"
}
$Folder "Header Files"
{
$File "..\common\cmdlib.h"
$File "..\common\datalinker.h"
$File "collisionmodel.h"
$File "collisionmodelsource.h"
$File "physics2collision.h"
$File "physics2collision.h"
$File "filebuffer.h"
$File "..\common\filesystem_tools.h"
$File "hardwarematrixstate.h"
$File "hardwarevertexcache.h"
$File "..\nvtristriplib\nvtristrip.h"
$File "perfstats.h"
$File "..\common\physdll.h"
$File "..\common\scriplib.h"
$File "studiomdl.h"
$File "optimize_subd.h"
$File "compileclothproxy.h"
}
$Folder "Public Header Files"
{
$File "$SRCDIR\public\alignedarray.h"
$File "$SRCDIR\public\gametrace.h"
$File "$SRCDIR\public\filesystem.h"
$File "$SRCDIR\public\filesystem_helpers.h"
$File "$SRCDIR\public\cmodel.h"
$File "$SRCDIR\public\basehandle.h"
$File "$SRCDIR\public\tier0\basetypes.h"
$File "$SRCDIR\public\bitvec.h"
$File "$SRCDIR\public\bone_accessor.h"
$File "$SRCDIR\public\bone_setup.h"
$File "$SRCDIR\public\bspflags.h"
$File "$SRCDIR\public\tier1\byteswap.h"
$File "$SRCDIR\public\tier1\characterset.h"
$File "$SRCDIR\public\collisionutils.h"
$File "$SRCDIR\public\mathlib\compressed_vector.h"
$File "$SRCDIR\public\const.h"
$File "$SRCDIR\public\vphysics\constraints.h"
$File "$SRCDIR\public\tier0\dbg.h"
$File "$SRCDIR\public\tier0\fasttimer.h"
$File "$SRCDIR\public\appframework\iappsystem.h"
$File "$SRCDIR\public\tier0\icommandline.h"
$File "$SRCDIR\public\ihandleentity.h"
$File "$SRCDIR\public\materialsystem\imaterial.h"
$File "$SRCDIR\public\materialsystem\imaterialsystem.h"
$File "$SRCDIR\public\materialsystem\imaterialvar.h"
$File "$SRCDIR\public\tier1\interface.h"
$File "$SRCDIR\public\istudiorender.h"
$File "$SRCDIR\public\tier1\keyvalues.h"
$File "$SRCDIR\public\materialsystem\materialsystem_config.h"
$File "$SRCDIR\public\mathlib\mathlib.h"
$File "$SRCDIR\public\tier0\memdbgoff.h"
$File "$SRCDIR\public\tier0\memdbgon.h"
$File "$SRCDIR\public\phyfile.h"
$File "$SRCDIR\public\phzfile.h"
$File "$SRCDIR\public\optimize.h"
$File "$SRCDIR\public\tier0\platform.h"
$File "$SRCDIR\public\vstdlib\random.h"
$File "$SRCDIR\common\studiobyteswap.h"
$File "$SRCDIR\public\string_t.h"
$File "$SRCDIR\public\tier1\strtools.h"
$File "$SRCDIR\public\studio.h"
$File "$SRCDIR\public\tier3\tier3.h"
$File "$SRCDIR\public\tier1\utlbuffer.h"
$File "$SRCDIR\public\tier1\utldict.h"
$File "$SRCDIR\public\tier1\utllinkedlist.h"
$File "$SRCDIR\public\tier1\utlmemory.h"
$File "$SRCDIR\public\tier1\utlrbtree.h"
$File "$SRCDIR\public\tier1\utlsymbol.h"
$File "$SRCDIR\public\tier1\utlvector.h"
$File "$SRCDIR\public\vcollide.h"
$File "$SRCDIR\public\vcollide_parse.h"
$File "$SRCDIR\public\mathlib\vector.h"
$File "$SRCDIR\public\mathlib\vector2d.h"
$File "$SRCDIR\public\mathlib\vector4d.h"
$File "$SRCDIR\public\mathlib\vmatrix.h"
$File "$SRCDIR\public\vphysics_interface.h"
$File "$SRCDIR\public\mathlib\vplane.h"
$File "$SRCDIR\public\tier0\vprof.h"
$File "$SRCDIR\public\vstdlib\vstdlib.h"
}
$Folder "Link Libraries"
{
$Lib resourcefile
$Lib dmeutils
$Lib meshutils
$Lib appframework
$Lib bonesetup
$Lib datamodel
$Lib dmserializers
$Lib mathlib
$Lib mathlib_extended
$Lib mdlobjects
$Lib movieobjects
$Lib nvtristrip
$Lib tier1
$Lib tier2
$Lib tier3
$Lib fbxutils
}
}
+350
View File
@@ -0,0 +1,350 @@
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//
//=============================================================================//
// tristrip - convert triangle list into tristrips and fans
#pragma warning( disable : 4244 )
#pragma warning( disable : 4237 )
#pragma warning( disable : 4305 )
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include "cmdlib.h"
#include "lbmlib.h"
#include "scriplib.h"
#include "mathlib/mathlib.h"
#include "..\..\engine\studio.h"
#include "studiomdl.h"
int used[MAXSTUDIOTRIANGLES];
// the command list holds counts and s/t values that are valid for
// every frame
short commands[MAXSTUDIOTRIANGLES * 13];
int numcommands;
// all frames will have their vertexes rearranged and expanded
// so they are in the order expected by the command list
int allverts, alltris;
int stripverts[MAXSTUDIOTRIANGLES+2];
int striptris[MAXSTUDIOTRIANGLES+2];
int stripcount;
int neighbortri[MAXSTUDIOTRIANGLES][3];
int neighboredge[MAXSTUDIOTRIANGLES][3];
s_trianglevert_t (*triangles)[3];
s_mesh_t *pmesh;
void FindNeighbor (int starttri, int startv)
{
s_trianglevert_t m1, m2;
int j;
s_trianglevert_t *last, *check;
int k;
// used[starttri] |= (1 << startv);
last = &triangles[starttri][0];
m1 = last[(startv+1)%3];
m2 = last[(startv+0)%3];
for (j=starttri+1, check=&triangles[starttri+1][0] ; j<pmesh->numtris ; j++, check += 3)
{
if (used[j] == 7)
continue;
for (k=0 ; k<3 ; k++)
{
if (memcmp(&check[k],&m1,sizeof(m1)))
continue;
if (memcmp(&check[ (k+1)%3 ],&m2,sizeof(m2)))
continue;
neighbortri[starttri][startv] = j;
neighboredge[starttri][startv] = k;
neighbortri[j][k] = starttri;
neighboredge[j][k] = startv;
used[starttri] |= (1 << startv);
used[j] |= (1 << k);
return;
}
}
}
/*
================
StripLength
================
*/
int StripLength (int starttri, int startv)
{
int j;
int k;
used[starttri] = 2;
stripverts[0] = (startv)%3;
stripverts[1] = (startv+1)%3;
stripverts[2] = (startv+2)%3;
striptris[0] = starttri;
striptris[1] = starttri;
striptris[2] = starttri;
stripcount = 3;
while( 1 )
{
if (stripcount & 1)
{
j = neighbortri[starttri][(startv+1)%3];
k = neighboredge[starttri][(startv+1)%3];
}
else
{
j = neighbortri[starttri][(startv+2)%3];
k = neighboredge[starttri][(startv+2)%3];
}
if (j == -1 || used[j])
goto done;
stripverts[stripcount] = (k+2)%3;
striptris[stripcount] = j;
stripcount++;
used[j] = 2;
starttri = j;
startv = k;
}
done:
// clear the temp used flags
for (j=0 ; j<pmesh->numtris ; j++)
if (used[j] == 2)
used[j] = 0;
return stripcount;
}
/*
===========
FanLength
===========
*/
int FanLength (int starttri, int startv)
{
int j;
int k;
used[starttri] = 2;
stripverts[0] = (startv)%3;
stripverts[1] = (startv+1)%3;
stripverts[2] = (startv+2)%3;
striptris[0] = starttri;
striptris[1] = starttri;
striptris[2] = starttri;
stripcount = 3;
while( 1 )
{
j = neighbortri[starttri][(startv+2)%3];
k = neighboredge[starttri][(startv+2)%3];
if (j == -1 || used[j])
goto done;
stripverts[stripcount] = (k+2)%3;
striptris[stripcount] = j;
stripcount++;
used[j] = 2;
starttri = j;
startv = k;
}
done:
// clear the temp used flags
for (j=0 ; j<pmesh->numtris ; j++)
if (used[j] == 2)
used[j] = 0;
return stripcount;
}
/*
================
BuildTris
Generate a list of trifans or strips
for the model, which holds for all frames
================
*/
int numcommandnodes;
int BuildTris (s_trianglevert_t (*x)[3], s_mesh_t *y, byte **ppdata )
{
int i, j, k, m;
int startv;
int len, bestlen, besttype;
int bestverts[MAXSTUDIOTRIANGLES];
int besttris[MAXSTUDIOTRIANGLES];
int peak[MAXSTUDIOTRIANGLES];
int type;
int total = 0;
long t;
int maxlen;
triangles = x;
pmesh = y;
t = time( NULL );
for (i=0 ; i<pmesh->numtris ; i++)
{
neighbortri[i][0] = neighbortri[i][1] = neighbortri[i][2] = -1;
used[i] = 0;
peak[i] = pmesh->numtris;
}
// printf("finding neighbors\n");
for (i=0 ; i<pmesh->numtris; i++)
{
for (k = 0; k < 3; k++)
{
if (used[i] & (1 << k))
continue;
FindNeighbor( i, k );
}
// printf("%d", used[i] );
}
// printf("\n");
//
// build tristrips
//
numcommandnodes = 0;
numcommands = 0;
memset (used, 0, sizeof(used));
for (i=0 ; i<pmesh->numtris ;)
{
// pick an unused triangle and start the trifan
if (used[i])
{
i++;
continue;
}
maxlen = 9999;
bestlen = 0;
m = 0;
for (k = i; k < pmesh->numtris && bestlen < 127; k++)
{
int localpeak = 0;
if (used[k])
continue;
if (peak[k] <= bestlen)
continue;
m++;
for (type = 0 ; type < 2 ; type++)
{
for (startv =0 ; startv < 3 ; startv++)
{
if (type == 1)
len = FanLength (k, startv);
else
len = StripLength (k, startv);
if (len > 127)
{
// skip these, they are too long to encode
}
else if (len > bestlen)
{
besttype = type;
bestlen = len;
for (j=0 ; j<bestlen ; j++)
{
besttris[j] = striptris[j];
bestverts[j] = stripverts[j];
}
// printf("%d %d\n", k, bestlen );
}
if (len > localpeak)
localpeak = len;
}
}
peak[k] = localpeak;
if (localpeak == maxlen)
break;
}
total += (bestlen - 2);
// printf("%d (%d) %d\n", bestlen, pmesh->numtris - total, i );
maxlen = bestlen;
// mark the tris on the best strip as used
for (j=0 ; j<bestlen ; j++)
used[besttris[j]] = 1;
if (besttype == 1)
commands[numcommands++] = -bestlen;
else
commands[numcommands++] = bestlen;
for (j=0 ; j<bestlen ; j++)
{
s_trianglevert_t *tri;
tri = &triangles[besttris[j]][bestverts[j]];
commands[numcommands++] = tri->vertindex;
commands[numcommands++] = tri->normindex;
commands[numcommands++] = tri->s;
commands[numcommands++] = tri->t;
}
// printf("%d ", bestlen - 2 );
numcommandnodes++;
if (t != time(NULL))
{
printf("%2d%%\r", (total * 100) / pmesh->numtris );
t = time(NULL);
}
}
commands[numcommands++] = 0; // end of list marker
*ppdata = (byte *)commands;
// printf("%d %d %d\n", numcommandnodes, numcommands, pmesh->numtris );
return numcommands * sizeof( short );
}
+477
View File
@@ -0,0 +1,477 @@
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//
//=============================================================================//
//
// studiomdl.c: generates a studio .mdl file from a .qc script
// sources/<scriptname>.mdl.
//
#pragma warning( disable : 4244 )
#pragma warning( disable : 4237 )
#pragma warning( disable : 4305 )
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <math.h>
#include "cmdlib.h"
#include "scriplib.h"
#include "mathlib/mathlib.h"
#include "studio.h"
#include "studiomdl.h"
int lookup_index( s_source_t *psource, int material, Vector& vertex, Vector& normal, Vector2D texcoord, int iCount, int bones[], float weights[], int iExtras, float extras[] )
{
int i, j, k;
for (i = 0; i < g_numvlist; i++)
{
if (v_listdata[i].m == material
&& DotProduct( g_normal[i], normal ) > normal_blend
&& VectorCompare( g_vertex[i], vertex )
&& g_texcoord[0][i][0] == texcoord[0]
&& g_texcoord[0][i][1] == texcoord[1])
{
if (g_bone[i].numbones == iCount)
{
for (j = 0; j < iCount; j++)
{
if (g_bone[i].bone[j] != bones[j] || g_bone[i].weight[j] != weights[j])
break;
}
if (j == iCount)
{
// Assume extra floats are additional texcoords
for (k = 0; k < (iExtras / 2); k++)
{
if (v_listdata[i].t[k + 1] == -1) // Texcoord not set
break;
if (g_texcoord[k + 1][i][0] != extras[k * 2])
break;
if (g_texcoord[k + 1][i][1] != extras[k * 2 + 1])
break;
}
if (k == (iExtras/2))
{
v_listdata[i].lastref = g_numvlist;
return i;
}
}
}
}
}
if (i >= MAXSTUDIOSRCVERTS) {
MdlError( "too many indices in source: \"%s\"\n", psource->filename);
}
VectorCopy( vertex, g_vertex[i] );
VectorCopy( normal, g_normal[i] );
Vector2Copy( texcoord, g_texcoord[0][i] );
g_bone[i].numbones = iCount;
for ( j = 0; j < iCount; j++)
{
g_bone[i].bone[j] = bones[j];
g_bone[i].weight[j] = weights[j];
}
v_listdata[i].v = i;
v_listdata[i].m = material;
v_listdata[i].n = i;
v_listdata[i].t[0] = i;
// Set default indices for additional texcoords to -1
for (j = 1; j < (MAXSTUDIOTEXCOORDS); ++j)
{
v_listdata[i].t[j] = -1;
}
// Populate additional texcoords with any extra floats
for (j = 0; j < (iExtras / 2); j++)
{
g_texcoord[j + 1][i][0] = extras[j * 2];
g_texcoord[j + 1][i][1] = extras[j * 2 + 1];
v_listdata[i].t[j+1] = i;
}
v_listdata[i].lastref = g_numvlist;
g_numvlist = i + 1;
return i;
}
// GetNextFaceItem
// Get next item from string of space separated data
static char* GetNextFaceItem(char* pCurrentItem)
{
if (!pCurrentItem)
{
return NULL;
}
char* pChar = pCurrentItem;
//Skip any leading spaces
while (*pChar == ' ')
{
pChar++;
}
pChar = strchr(pChar, ' ');
if (!pChar)
{
return NULL;
}
while (*pChar == ' ')
{
pChar++;
}
if ((*pChar == 0) || (*pChar == '\n'))
{
return NULL;
}
return pChar;
}
void ParseFaceData( s_source_t *psource, int material, s_face_t *pFace )
{
int index[3];
int i, j;
Vector p;
Vector normal;
Vector2D t;
int iCount, bones[MAXSTUDIOSRCBONES];
float weights[MAXSTUDIOSRCBONES];
int iExtras;
float extras[(MAXSTUDIOTEXCOORDS-1)*2];
int bone;
for (j = 0; j < 3; j++)
{
memset( g_szLine, 0, sizeof( g_szLine ) );
if (!GetLineInput())
{
MdlError("%s: error on g_szLine %d: %s", g_szFilename, g_iLinecount, g_szLine );
}
iCount = 0;
iExtras = 0;
i = sscanf(g_szLine, "%d %f %f %f %f %f %f %f %f",
&bone,
&p[0], &p[1], &p[2],
&normal[0], &normal[1], &normal[2],
&t[0], &t[1]);
if (i < 9)
continue;
if (bone < 0 || bone >= psource->numbones)
{
MdlError("bogus bone index\n%d %s :\n%s", g_iLinecount, g_szFilename, g_szLine );
}
//Scale face pos
scale_vertex( p );
// Parse bones.
int k;
char *pItem = g_szLine;
// Skip first 9 items already parsed via sscanf above
for (k = 0; k < 9; k++)
{
pItem = GetNextFaceItem(pItem);
}
// Read bone count
if (pItem)
{
iCount = atoi(pItem);
if (iCount > 0)
{
for (k = 0; k < iCount && k < MAXSTUDIOSRCBONES; k++)
{
pItem = GetNextFaceItem(pItem);
if (!pItem)
{
MdlError("Bone ID %d not found\n%d %s :\n%s", k, g_iLinecount, g_szFilename, g_szLine);
}
bones[k] = atoi(pItem);
pItem = GetNextFaceItem(pItem);
if (!pItem)
{
MdlError("Bone weight %d not found\n%d %s :\n%s", k, g_iLinecount, g_szFilename, g_szLine);
}
weights[k] = atof(pItem);
}
}
if (psource->version >= 3)
{
pItem = GetNextFaceItem(pItem);
if (pItem)
{
iExtras = atoi(pItem);
if (iExtras > 0)
{
iExtras = MIN(iExtras, (MAXSTUDIOTEXCOORDS - 1) * 2);
for (int e = 0; e < iExtras; e++)
{
pItem = GetNextFaceItem(pItem);
if (!pItem)
{
MdlError("Extra data item %d not found\n%d %s :\n%s", e, g_iLinecount, g_szFilename, g_szLine);
}
extras[e] = atof(pItem);
}
}
}
}
// printf("%d ", iCount );
//printf("\n");
//exit(1);
}
// adjust_vertex( p );
// scale_vertex( p );
// move vertex position to object space.
// VectorSubtract( p, psource->bonefixup[bone].worldorg, tmp );
// VectorTransform(tmp, psource->bonefixup[bone].im, p );
// move normal to object space.
// VectorCopy( normal, tmp );
// VectorTransform(tmp, psource->bonefixup[bone].im, normal );
// VectorNormalize( normal );
// invert v
t[1] = 1.0 - t[1];
if (iCount == 0)
{
iCount = 1;
bones[0] = bone;
weights[0] = 1.0;
}
else
{
iCount = SortAndBalanceBones( iCount, MAXSTUDIOBONEWEIGHTS, bones, weights );
}
index[j] = lookup_index( psource, material, p, normal, t, iCount, bones, weights, iExtras, extras );
}
// pFace->material = material; // BUG
pFace->a = index[0];
pFace->b = index[2];
pFace->c = index[1];
Assert( ((pFace->a & 0xF0000000) == 0) && ((pFace->b & 0xF0000000) == 0) &&
((pFace->c & 0xF0000000) == 0) );
}
void Grab_Triangles( s_source_t *psource )
{
int i;
Vector vmin, vmax;
vmin[0] = vmin[1] = vmin[2] = 99999;
vmax[0] = vmax[1] = vmax[2] = -99999;
g_numfaces = 0;
g_numvlist = 0;
//
// load the base triangles
//
int texture;
int material;
char texturename[MAX_PATH];
while (1)
{
if (!GetLineInput())
break;
// check for end
if (IsEnd( g_szLine ))
break;
// Look for extra junk that we may want to avoid...
int nLineLength = strlen( g_szLine );
if (nLineLength >= sizeof( texturename ))
{
MdlWarning("Unexpected data at line %d, (need a texture name) ignoring...\n", g_iLinecount );
continue;
}
// strip off trailing smag
strncpy( texturename, g_szLine, sizeof( texturename ) - 1 );
for (i = strlen( texturename ) - 1; i >= 0 && ! V_isgraph( texturename[i] ); i--)
{
}
texturename[i + 1] = '\0';
// funky texture overrides
for (i = 0; i < numrep; i++)
{
if (sourcetexture[i][0] == '\0')
{
strcpy( texturename, defaulttexture[i] );
break;
}
if (stricmp( texturename, sourcetexture[i]) == 0)
{
strcpy( texturename, defaulttexture[i] );
break;
}
}
if (texturename[0] == '\0')
{
// weird source problem, skip them
GetLineInput();
GetLineInput();
GetLineInput();
continue;
}
if (stricmp( texturename, "null.bmp") == 0 || stricmp( texturename, "null.tga") == 0 || stricmp( texturename, "debug/debugempty" ) == 0)
{
// skip all faces with the null texture on them.
GetLineInput();
GetLineInput();
GetLineInput();
continue;
}
texture = LookupTexture( texturename, ( psource->version == 2 ) );
psource->texmap[texture] = texture; // hack, make it 1:1
material = UseTextureAsMaterial( texture );
s_face_t f;
ParseFaceData( psource, material, &f );
// remove degenerate triangles
if (f.a == f.b || f.b == f.c || f.a == f.c)
{
// printf("Degenerate triangle %d %d %d\n", f.a, f.b, f.c );
continue;
}
g_src_uface[g_numfaces] = f;
g_face[g_numfaces].material = material;
g_numfaces++;
}
for (int i = 0; i < MAXSTUDIOTEXCOORDS; ++i)
{
if (g_texcoord[i].Count())
{
g_numtexcoords[i] = g_numvlist;
}
}
BuildIndividualMeshes( psource );
}
int Load_SMD ( s_source_t *psource )
{
char cmd[1024];
int option;
// Reset smdVersion
psource->version = 1;
if (!OpenGlobalFile( psource->filename ))
return 0;
if( !g_quiet )
{
printf ("SMD MODEL %s\n", psource->filename);
}
g_iLinecount = 0;
while (GetLineInput())
{
int numRead = sscanf( g_szLine, "%s %d", cmd, &option );
// Blank line
if ((numRead == EOF) || (numRead == 0))
continue;
if (stricmp( cmd, "version" ) == 0)
{
if (option < 1 || option > 3)
{
MdlError("bad version\n");
}
psource->version = option;
}
else if (stricmp( cmd, "nodes" ) == 0)
{
psource->numbones = Grab_Nodes( psource->localBone );
}
else if (stricmp( cmd, "skeleton" ) == 0)
{
Grab_Animation( psource, "BindPose" );
}
else if (stricmp( cmd, "triangles" ) == 0)
{
Grab_Triangles( psource );
}
else if (stricmp( cmd, "vertexanimation" ) == 0)
{
Grab_Vertexanimation( psource, "BindPose" );
}
else if ((strncmp( cmd, "//", 2 ) == 0) || (strncmp( cmd, ";", 1 ) == 0) || (strncmp( cmd, "#", 1 ) == 0))
{
ProcessSourceComment( psource, cmd );
continue;
}
else
{
MdlWarning("unknown studio command \"%s\"\n", cmd );
}
}
fclose( g_fpInput );
return 1;
}
File diff suppressed because it is too large Load Diff