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

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

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

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;
}

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

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

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

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;
}

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

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] );
}
}

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

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
}
}

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;
}

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;
}

4471
utils/studiomdl/optimize.cpp Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,936 @@
//========= Copyright <20> 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

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

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();
}
}

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

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__);
}

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

9311
utils/studiomdl/simplify.cpp Normal file

File diff suppressed because it is too large Load Diff

12759
utils/studiomdl/studiomdl.cpp Normal file

File diff suppressed because it is too large Load Diff

1933
utils/studiomdl/studiomdl.h Normal file

File diff suppressed because it is too large Load Diff

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
}
}

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 );
}

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;
}

5390
utils/studiomdl/write.cpp Normal file

File diff suppressed because it is too large Load Diff