initial
This commit is contained in:
1693
utils/studiomdl/UnifyLODs.cpp
Normal file
1693
utils/studiomdl/UnifyLODs.cpp
Normal file
File diff suppressed because it is too large
Load Diff
98
utils/studiomdl/bmpread.cpp
Normal file
98
utils/studiomdl/bmpread.cpp
Normal 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;
|
||||
}
|
||||
|
||||
3173
utils/studiomdl/collisionmodel.cpp
Normal file
3173
utils/studiomdl/collisionmodel.cpp
Normal file
File diff suppressed because it is too large
Load Diff
40
utils/studiomdl/collisionmodel.h
Normal file
40
utils/studiomdl/collisionmodel.h
Normal file
@@ -0,0 +1,40 @@
|
||||
//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $Workfile: $
|
||||
// $Date: $
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
// $Log: $
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//===========================================================================//
|
||||
|
||||
#ifndef COLLISIONMODEL_H
|
||||
#define COLLISIONMODEL_H
|
||||
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
class CDmElement;
|
||||
struct s_source_t;
|
||||
|
||||
void Cmd_CollisionText( void );
|
||||
int DoCollisionModel( bool separateJoints );
|
||||
|
||||
#ifdef MDLCOMPILE
|
||||
int DoCollisionModel( s_source_t *pSource, CDmElement *pInfo, bool bStaticProp );
|
||||
void LoadCollisionText( const char *pszCollisionText );
|
||||
#endif MDLCOMPILE
|
||||
|
||||
// execute after simplification, before writing
|
||||
void CollisionModel_Build( void );
|
||||
// execute during writing
|
||||
extern void CollisionModel_Write( long checkSum );
|
||||
extern void CollisionModel_SetName( const char *pName );
|
||||
|
||||
void CollisionModel_ExpandBBox( Vector &mins, Vector &maxs );
|
||||
|
||||
#endif // COLLISIONMODEL_H
|
||||
350
utils/studiomdl/collisionmodelsource.cpp
Normal file
350
utils/studiomdl/collisionmodelsource.cpp
Normal file
@@ -0,0 +1,350 @@
|
||||
//========= Copyright c 1996-2008, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: Builds physics2 collision models from studio model source
|
||||
//
|
||||
// $Workfile: $
|
||||
// $Date: $
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
#include "studiomdl.h"
|
||||
#include "collisionmodelsource.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Transforms the source's verts into "world" space
|
||||
// Input : *psource -
|
||||
// *worldVerts -
|
||||
//-----------------------------------------------------------------------------
|
||||
void CCollisionModelSource::ConvertToWorldSpace( CUtlVector<Vector> &worldVerts, s_source_t *pmodel )
|
||||
{
|
||||
int i, n;
|
||||
|
||||
if (!m_bAssumeWorldspace)
|
||||
{
|
||||
matrix3x4_t boneToWorld[MAXSTUDIOSRCBONES]; // bone transformation matrix
|
||||
CalcBoneTransforms( g_panimation[0], 0, boneToWorld );
|
||||
|
||||
for (i = 0; i < pmodel->numvertices; i++)
|
||||
{
|
||||
Vector tmp,tmp2;
|
||||
worldVerts[i].Init(0,0,0 );
|
||||
|
||||
int nBoneCount = pmodel->vertex[i].boneweight.numbones;
|
||||
for (n = 0; n < nBoneCount; n++)
|
||||
{
|
||||
// convert to Half-Life world space
|
||||
// convert vertex into original models' bone local space
|
||||
int localBone = pmodel->vertex[i].boneweight.bone[n];
|
||||
int globalBone = pmodel->boneLocalToGlobal[localBone];
|
||||
Assert( localBone >= 0 );
|
||||
Assert( globalBone >= 0 );
|
||||
|
||||
matrix3x4_t boneToPose;
|
||||
ConcatTransforms( pmodel->boneToPose[localBone], g_bonetable[globalBone].srcRealign, boneToPose );
|
||||
VectorITransform( pmodel->vertex[i].position, boneToPose, tmp2 );
|
||||
|
||||
// now transform to that bone's world-space position in this animation
|
||||
VectorTransform(tmp2, boneToWorld[globalBone], tmp );
|
||||
VectorMA( worldVerts[i], pmodel->vertex[i].boneweight.weight[n], tmp, worldVerts[i] );
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
matrix3x4_t srcBoneToWorld[MAXSTUDIOSRCBONES]; // bone transformation matrix
|
||||
BuildRawTransforms( pmodel, "BindPose", 0, pmodel->scale, pmodel->adjust, pmodel->rotation, 0, srcBoneToWorld );
|
||||
|
||||
for (i = 0; i < pmodel->numvertices; i++)
|
||||
{
|
||||
Vector tmp;
|
||||
worldVerts[i].Init( 0, 0, 0 );
|
||||
|
||||
int nBoneCount = pmodel->vertex[i].boneweight.numbones;
|
||||
for (n = 0; n < nBoneCount; n++)
|
||||
{
|
||||
int localBone = pmodel->vertex[i].boneweight.bone[n];
|
||||
Assert( localBone >= 0 );
|
||||
|
||||
// convert vertex into world space
|
||||
VectorTransform( pmodel->vertex[i].position, srcBoneToWorld[localBone], tmp );
|
||||
// just assume the model is in identity space
|
||||
// FIXME: shouldn't this do an inverse xform of the default boneToWorld?
|
||||
|
||||
VectorMA( worldVerts[i], pmodel->vertex[i].boneweight.weight[n], tmp, worldVerts[i] );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( g_flCollisionPrecision > 0 )
|
||||
{
|
||||
#ifdef DEBUG
|
||||
printf("Applying collision precision truncation: %f\n", g_flCollisionPrecision );
|
||||
#endif
|
||||
for ( int i = 0; i < worldVerts.Count(); i++ )
|
||||
{
|
||||
worldVerts[i].x -= fmod( worldVerts[i].x, g_flCollisionPrecision );
|
||||
worldVerts[i].y -= fmod( worldVerts[i].y, g_flCollisionPrecision );
|
||||
worldVerts[i].z -= fmod( worldVerts[i].z, g_flCollisionPrecision );
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Transforms the set of verts into the space of a particular bone
|
||||
// Input : *psource -
|
||||
// boneIndex -
|
||||
// *boneVerts -
|
||||
//-----------------------------------------------------------------------------
|
||||
void CCollisionModelSource::ConvertToBoneSpace( int boneIndex, CUtlVector<Vector> &boneVerts )
|
||||
{
|
||||
int i;
|
||||
|
||||
int remapIndex = m_pModel->boneLocalToGlobal[boneIndex];
|
||||
matrix3x4_t boneToPose;
|
||||
if ( remapIndex < 0 )
|
||||
{
|
||||
MdlWarning("Error! physics for unused bone %s\n", m_pModel->localBone[boneIndex].name );
|
||||
MatrixCopy( m_pModel->boneToPose[boneIndex], boneToPose );
|
||||
}
|
||||
else
|
||||
{
|
||||
ConcatTransforms( m_pModel->boneToPose[boneIndex], g_bonetable[remapIndex].srcRealign, boneToPose );
|
||||
}
|
||||
|
||||
for (i = 0; i < m_pModel->numvertices; i++)
|
||||
{
|
||||
VectorITransform(m_pModel->vertex[i].position, boneToPose, boneVerts[i] );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool CCollisionModelSource::ShouldProcessBone( int boneIndex )
|
||||
{
|
||||
if ( boneIndex >= 0 )
|
||||
{
|
||||
if ( m_bonemap[boneIndex] == boneIndex )
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// called before processing, after the model has been simplified.
|
||||
// Update internal state due to simplification
|
||||
void CCollisionModelSource::Simplify()
|
||||
{
|
||||
if ( m_pModel )
|
||||
{
|
||||
for ( int i = 0; i < m_pModel->numbones; i++ )
|
||||
{
|
||||
if ( m_pModel->boneLocalToGlobal[i] < 0 )
|
||||
{
|
||||
SkipBone(i);
|
||||
}
|
||||
|
||||
// Walk the parents of this bone, if they map to the same global bone then go ahead and
|
||||
// merge them now so we can aggregate the collision models
|
||||
int nMatchingParent = i;
|
||||
int nParentCheck = m_pModel->localBone[nMatchingParent].parent;
|
||||
int nGlobalMatch = m_pModel->boneLocalToGlobal[i];
|
||||
while ( nParentCheck >= 0 && m_pModel->boneLocalToGlobal[nParentCheck] == nGlobalMatch )
|
||||
{
|
||||
nMatchingParent = nParentCheck;
|
||||
nParentCheck = m_pModel->localBone[nParentCheck].parent;
|
||||
}
|
||||
if ( nMatchingParent != i )
|
||||
{
|
||||
MergeBones( nMatchingParent, i );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern int g_rootIndex;
|
||||
const char *pAnimationRootBone = g_bonetable[g_rootIndex].name;
|
||||
|
||||
// merge this root bone with the root of animation
|
||||
MergeBones( pAnimationRootBone, m_rootName );
|
||||
|
||||
}
|
||||
|
||||
|
||||
void CCollisionModelSource::SkipBone( int boneIndex )
|
||||
{
|
||||
if ( boneIndex >= 0 )
|
||||
m_bonemap[boneIndex] = -1;
|
||||
}
|
||||
|
||||
|
||||
void CCollisionModelSource::InitBoneMap( void )
|
||||
{
|
||||
m_bonemap.SetSize(m_pModel->numbones);
|
||||
for ( int i = 0; i < m_pModel->numbones; i++ )
|
||||
{
|
||||
m_bonemap[i] = i;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CCollisionModelSource::MergeBones( int parent, int child )
|
||||
{
|
||||
if ( parent < 0 || child < 0 )
|
||||
return;
|
||||
|
||||
int map = parent;
|
||||
int safety = 0;
|
||||
while ( m_bonemap[map] != map )
|
||||
{
|
||||
map = m_bonemap[map];
|
||||
safety++;
|
||||
// infinite loop?
|
||||
if ( safety > m_pModel->numbones )
|
||||
break;
|
||||
|
||||
if ( map < 0 )
|
||||
break;
|
||||
}
|
||||
|
||||
m_bonemap[child] = map;
|
||||
}
|
||||
|
||||
void CCollisionModelSource::MergeBones(const char *parent, const char *child)
|
||||
{
|
||||
MergeBones(FindLocalBoneNamed( parent ), FindLocalBoneNamed( child ));
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Search a source for a bone with a specified name
|
||||
// Input : *pSource -
|
||||
// *pName -
|
||||
// Output : int boneIndex, -1 if none
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
int FindLocalBoneNamed( const s_source_t *pSource, const char *pName )
|
||||
{
|
||||
if ( pName && pSource )
|
||||
{
|
||||
int i;
|
||||
for ( i = 0; i < pSource->numbones; i++ )
|
||||
{
|
||||
if ( !stricmp( pName, pSource->localBone[i].name ) )
|
||||
return i;
|
||||
}
|
||||
|
||||
pName = RenameBone( pName );
|
||||
|
||||
for ( i = 0; i < pSource->numbones; i++ )
|
||||
{
|
||||
if ( !stricmp( pName, pSource->localBone[i].name ) )
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int CCollisionModelSource::FindLocalBoneNamed( const char *pName )
|
||||
{
|
||||
return ::FindLocalBoneNamed(m_pModel, pName);
|
||||
}
|
||||
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Test this face to see if any of its verts are assigned to a particular bone
|
||||
// *pmodel -
|
||||
// *face -
|
||||
// boneIndex -
|
||||
// Output : Returns true if this face has a vert assigned to boneIndex
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CCollisionModelSource::FaceHasVertOnBone( const s_face_t &face, int boneIndex )
|
||||
{
|
||||
if ( boneIndex < 0 )
|
||||
return true;
|
||||
|
||||
int j;
|
||||
s_boneweight_t *pweight;
|
||||
pweight = &m_pModel->vertex[ face.a ].boneweight;
|
||||
for ( j = 0; j < pweight->numbones; j++ )
|
||||
{
|
||||
// assigned to boneIndex?
|
||||
if ( RemapBone( pweight->bone[j] ) == boneIndex )
|
||||
return true;
|
||||
}
|
||||
|
||||
pweight = &m_pModel->vertex[ face.b ].boneweight;
|
||||
for ( j = 0; j < pweight->numbones; j++ )
|
||||
{
|
||||
// assigned to boneIndex?
|
||||
if ( RemapBone( pweight->bone[j] ) == boneIndex )
|
||||
return true;
|
||||
}
|
||||
|
||||
pweight = &m_pModel->vertex[ face.c ].boneweight;
|
||||
for ( j = 0; j < pweight->numbones; j++ )
|
||||
{
|
||||
// assigned to boneIndex?
|
||||
if ( RemapBone( pweight->bone[j] ) == boneIndex )
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
int CCollisionModelSource::RemapBone( int boneIndex ) const
|
||||
{
|
||||
if ( boneIndex >= 0 )
|
||||
return m_bonemap[boneIndex];
|
||||
return boneIndex;
|
||||
}
|
||||
|
||||
|
||||
|
||||
s_face_t CCollisionModelSource::GetGlobalFace( s_mesh_t *pMesh, int nFace )
|
||||
{
|
||||
s_face_t output;
|
||||
GlobalFace(&output, pMesh, m_pModel->face + pMesh->faceoffset + nFace);
|
||||
return output;
|
||||
}
|
||||
|
||||
|
||||
void CCollisionModelSource::FindBoundBones(s_mesh_t *pMesh, CUtlVector<int>&setBones)
|
||||
{
|
||||
s_face_t *pFaces = m_pModel->face + pMesh->faceoffset;
|
||||
s_vertexinfo_t *pVertices = m_pModel->vertex + pMesh->vertexoffset;
|
||||
|
||||
for ( int nFace = 0; nFace < pMesh->numfaces; nFace++ )
|
||||
{
|
||||
FindBoundBones(pVertices[pFaces[nFace].a].boneweight, setBones);
|
||||
FindBoundBones(pVertices[pFaces[nFace].b].boneweight, setBones);
|
||||
FindBoundBones(pVertices[pFaces[nFace].c].boneweight, setBones);
|
||||
}
|
||||
}
|
||||
|
||||
void CCollisionModelSource::FindBoundBones(s_boneweight_t &weights, CUtlVector<int>&setBones)
|
||||
{
|
||||
for(int nBoundBone = 0; nBoundBone < weights.numbones; ++nBoundBone)
|
||||
{ int boneIndex = RemapBone(weights.bone[nBoundBone]);
|
||||
if(!setBones.HasElement(boneIndex))
|
||||
setBones.AddToTail(boneIndex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Fixup the pointers in this face to reference the mesh globally (source relative)
|
||||
// (faces are mesh relative, each source has several meshes)
|
||||
// Input : *pout -
|
||||
// *pmesh -
|
||||
// *pin -
|
||||
//-----------------------------------------------------------------------------
|
||||
void GlobalFace( s_face_t *pout, s_mesh_t *pmesh, s_face_t *pin )
|
||||
{
|
||||
pout->a = pmesh->vertexoffset + pin->a;
|
||||
pout->b = pmesh->vertexoffset + pin->b;
|
||||
pout->c = pmesh->vertexoffset + pin->c;
|
||||
}
|
||||
72
utils/studiomdl/collisionmodelsource.h
Normal file
72
utils/studiomdl/collisionmodelsource.h
Normal 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
|
||||
1219
utils/studiomdl/compileclothproxy.cpp
Normal file
1219
utils/studiomdl/compileclothproxy.cpp
Normal file
File diff suppressed because it is too large
Load Diff
437
utils/studiomdl/compileclothproxy.h
Normal file
437
utils/studiomdl/compileclothproxy.h
Normal 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
|
||||
5013
utils/studiomdl/dmxsupport.cpp
Normal file
5013
utils/studiomdl/dmxsupport.cpp
Normal file
File diff suppressed because it is too large
Load Diff
131
utils/studiomdl/filebuffer.h
Normal file
131
utils/studiomdl/filebuffer.h
Normal file
@@ -0,0 +1,131 @@
|
||||
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef FILEBUFFER_H
|
||||
#define FILEBUFFER_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "tier1/smartptr.h"
|
||||
#include "tier2/p4helpers.h"
|
||||
|
||||
class CFileBuffer
|
||||
{
|
||||
public:
|
||||
CFileBuffer( int size )
|
||||
{
|
||||
m_pData = new unsigned char[size];
|
||||
#ifdef _DEBUG
|
||||
m_pUsed = new const char *[size];
|
||||
memset( m_pUsed, 0, size * sizeof( const char * ) );
|
||||
#endif
|
||||
m_Size = size;
|
||||
m_pCurPos = m_pData;
|
||||
#ifdef _DEBUG
|
||||
memset( m_pData, 0xbaadf00d, size );
|
||||
#endif
|
||||
}
|
||||
~CFileBuffer()
|
||||
{
|
||||
delete [] m_pData;
|
||||
#ifdef _DEBUG
|
||||
delete [] m_pUsed;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef _DEBUG
|
||||
void TestWritten( int EndOfFileOffset )
|
||||
{
|
||||
if ( !g_quiet )
|
||||
{
|
||||
printf( "testing to make sure that the whole file has been written\n" );
|
||||
}
|
||||
int i;
|
||||
for( i = 0; i < EndOfFileOffset; i++ )
|
||||
{
|
||||
if( !m_pUsed[i] )
|
||||
{
|
||||
printf( "offset %d not written, end of file invalid!\n", i );
|
||||
Assert( 0 );
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void WriteToFile( const char *fileName, int size )
|
||||
{
|
||||
CPlainAutoPtr< CP4File > spFile( g_p4factory->AccessFile( fileName ) );
|
||||
spFile->Edit();
|
||||
FILE *fp = fopen( fileName, "wb" );
|
||||
if( !fp )
|
||||
{
|
||||
MdlWarning( "Can't open \"%s\" for writing!\n", fileName );
|
||||
return;
|
||||
}
|
||||
|
||||
fwrite( m_pData, 1, size, fp );
|
||||
|
||||
fclose( fp );
|
||||
spFile->Add();
|
||||
}
|
||||
|
||||
void WriteAt( int offset, void *data, int size, const char *name )
|
||||
{
|
||||
// printf( "WriteAt: \"%s\" offset: %d end: %d size: %d\n", name, offset, offset + size - 1, size );
|
||||
m_pCurPos = m_pData + offset;
|
||||
|
||||
#ifdef _DEBUG
|
||||
int i;
|
||||
const char **used = m_pUsed + offset;
|
||||
bool bitched = false;
|
||||
for( i = 0; i < size; i++ )
|
||||
{
|
||||
if( used[i] )
|
||||
{
|
||||
if( !bitched )
|
||||
{
|
||||
printf( "overwrite at %d! (overwriting \"%s\" with \"%s\")\n", i + offset, used[i], name );
|
||||
Assert( 0 );
|
||||
bitched = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
used[i] = name;
|
||||
}
|
||||
}
|
||||
#endif // _DEBUG
|
||||
|
||||
Append( data, size );
|
||||
}
|
||||
int GetOffset( void )
|
||||
{
|
||||
return m_pCurPos - m_pData;
|
||||
}
|
||||
void *GetPointer( int offset )
|
||||
{
|
||||
return m_pData + offset;
|
||||
}
|
||||
private:
|
||||
void Append( void *data, int size )
|
||||
{
|
||||
Assert( m_pCurPos + size - m_pData < m_Size );
|
||||
memcpy( m_pCurPos, data, size );
|
||||
m_pCurPos += size;
|
||||
}
|
||||
CFileBuffer(); // undefined
|
||||
int m_Size;
|
||||
unsigned char *m_pData;
|
||||
unsigned char *m_pCurPos;
|
||||
#ifdef _DEBUG
|
||||
const char **m_pUsed;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
#endif // FILEBUFFER_H
|
||||
232
utils/studiomdl/hardwarematrixstate.cpp
Normal file
232
utils/studiomdl/hardwarematrixstate.cpp
Normal file
@@ -0,0 +1,232 @@
|
||||
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#include <windows.h>
|
||||
#include "HardwareMatrixState.h"
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "studio.h"
|
||||
#include "studiomdl.h"
|
||||
|
||||
CHardwareMatrixState::CHardwareMatrixState()
|
||||
{
|
||||
m_LRUCounter = 0;
|
||||
m_NumMatrices = 0;
|
||||
m_matrixState = NULL;
|
||||
m_savedMatrixState = NULL;
|
||||
}
|
||||
|
||||
void CHardwareMatrixState::Init( int numHardwareMatrices )
|
||||
{
|
||||
m_NumMatrices = numHardwareMatrices;
|
||||
delete [] m_matrixState;
|
||||
m_matrixState = new MatrixState_t[m_NumMatrices];
|
||||
Assert( m_matrixState );
|
||||
delete [] m_savedMatrixState;
|
||||
m_savedMatrixState = new MatrixState_t[m_NumMatrices];
|
||||
Assert( m_savedMatrixState );
|
||||
m_LRUCounter = 0;
|
||||
m_AllocatedMatrices = 0;
|
||||
|
||||
int i;
|
||||
for( i = 0; i < m_NumMatrices; i++ )
|
||||
{
|
||||
m_matrixState[i].allocated = false;
|
||||
}
|
||||
}
|
||||
|
||||
bool CHardwareMatrixState::AllocateMatrix( int globalMatrixID )
|
||||
{
|
||||
int i;
|
||||
|
||||
if( IsMatrixAllocated( globalMatrixID ) )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
for( i = 0; i < m_NumMatrices; i++ )
|
||||
{
|
||||
if( !m_matrixState[i].allocated )
|
||||
{
|
||||
m_matrixState[i].globalMatrixID = globalMatrixID;
|
||||
m_matrixState[i].allocated = true;
|
||||
m_matrixState[i].lastUsageID = m_LRUCounter++;
|
||||
++m_AllocatedMatrices;
|
||||
DumpState();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
DumpState();
|
||||
return false;
|
||||
}
|
||||
|
||||
int CHardwareMatrixState::FindLocalLRUIndex( void )
|
||||
{
|
||||
int oldestLRUCounter = INT_MAX;
|
||||
int i;
|
||||
int oldestID = 0;
|
||||
|
||||
for( i = 0; i < m_NumMatrices; i++ )
|
||||
{
|
||||
if( !m_matrixState[i].allocated )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if( m_matrixState[i].lastUsageID < oldestLRUCounter )
|
||||
{
|
||||
oldestLRUCounter = m_matrixState[i].lastUsageID;
|
||||
oldestID = i;
|
||||
}
|
||||
}
|
||||
|
||||
Assert( oldestLRUCounter != INT_MAX );
|
||||
return oldestID;
|
||||
}
|
||||
|
||||
void CHardwareMatrixState::DeallocateLRU( void )
|
||||
{
|
||||
int id;
|
||||
|
||||
id = FindLocalLRUIndex();
|
||||
m_matrixState[id].allocated = false;
|
||||
--m_AllocatedMatrices;
|
||||
}
|
||||
|
||||
void CHardwareMatrixState::DeallocateLRU( int n )
|
||||
{
|
||||
int i;
|
||||
|
||||
for( i = 0; i < n; i++ )
|
||||
{
|
||||
DeallocateLRU();
|
||||
}
|
||||
}
|
||||
|
||||
bool CHardwareMatrixState::IsMatrixAllocated( int globalMatrixID ) const
|
||||
{
|
||||
int i;
|
||||
|
||||
for( i = 0; i < m_NumMatrices; i++ )
|
||||
{
|
||||
if( m_matrixState[i].globalMatrixID == globalMatrixID &&
|
||||
m_matrixState[i].allocated )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void CHardwareMatrixState::DeallocateAll()
|
||||
{
|
||||
int i;
|
||||
|
||||
DumpState();
|
||||
for( i = 0; i < m_NumMatrices; i++ )
|
||||
{
|
||||
m_matrixState[i].allocated = false;
|
||||
m_matrixState[i].globalMatrixID = INT_MAX;
|
||||
m_matrixState[i].lastUsageID = INT_MAX;
|
||||
}
|
||||
m_AllocatedMatrices = 0;
|
||||
DumpState();
|
||||
}
|
||||
|
||||
void CHardwareMatrixState::SaveState( void )
|
||||
{
|
||||
int i;
|
||||
|
||||
for( i = 0; i < m_NumMatrices; i++ )
|
||||
{
|
||||
m_savedMatrixState[i] = m_matrixState[i];
|
||||
}
|
||||
}
|
||||
|
||||
void CHardwareMatrixState::RestoreState( void )
|
||||
{
|
||||
int i;
|
||||
|
||||
for( i = 0; i < m_NumMatrices; i++ )
|
||||
{
|
||||
m_matrixState[i] = m_savedMatrixState[i];
|
||||
}
|
||||
}
|
||||
|
||||
int CHardwareMatrixState::AllocatedMatrixCount() const
|
||||
{
|
||||
return m_AllocatedMatrices;
|
||||
}
|
||||
|
||||
int CHardwareMatrixState::FreeMatrixCount() const
|
||||
{
|
||||
return m_NumMatrices - m_AllocatedMatrices;
|
||||
}
|
||||
|
||||
int CHardwareMatrixState::GetNthBoneGlobalID( int n ) const
|
||||
{
|
||||
int i;
|
||||
int m = 0;
|
||||
|
||||
for( i = 0; i < m_NumMatrices; i++ )
|
||||
{
|
||||
if( m_matrixState[i].allocated )
|
||||
{
|
||||
if( n == m )
|
||||
{
|
||||
return m_matrixState[i].globalMatrixID;
|
||||
}
|
||||
m++;
|
||||
}
|
||||
}
|
||||
Assert( 0 );
|
||||
MdlError( "GetNthBoneGlobalID() Failure\n" );
|
||||
return 0;
|
||||
}
|
||||
|
||||
void CHardwareMatrixState::DumpState( void )
|
||||
{
|
||||
int i;
|
||||
static char buf[256];
|
||||
|
||||
//#ifndef _DEBUG
|
||||
return;
|
||||
//#endif
|
||||
|
||||
OutputDebugString( "DumpState\n:" );
|
||||
for( i = 0; i < m_NumMatrices; i++ )
|
||||
{
|
||||
if( m_matrixState[i].allocated )
|
||||
{
|
||||
sprintf( buf, "%d: allocated: %s lastUsageID: %d globalMatrixID: %d\n",
|
||||
i,
|
||||
m_matrixState[i].allocated ? "true " : "false",
|
||||
m_matrixState[i].lastUsageID,
|
||||
m_matrixState[i].globalMatrixID );
|
||||
OutputDebugString( buf );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int CHardwareMatrixState::FindHardwareMatrix( int globalMatrixID )
|
||||
{
|
||||
int i;
|
||||
|
||||
for( i = 0; i < m_NumMatrices; i++ )
|
||||
{
|
||||
if( m_matrixState[i].globalMatrixID == globalMatrixID )
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
Assert( 0 );
|
||||
MdlError( "barfing in FindHardwareMatrix\n" );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
71
utils/studiomdl/hardwarematrixstate.h
Normal file
71
utils/studiomdl/hardwarematrixstate.h
Normal file
@@ -0,0 +1,71 @@
|
||||
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef HARDWAREMATRIXSTATE_H
|
||||
#define HARDWAREMATRIXSTATE_H
|
||||
#pragma once
|
||||
|
||||
// This emulates the hardware matrix palette and keeps up with
|
||||
// matrix usage, LRU's matrices, etc.
|
||||
class CHardwareMatrixState
|
||||
{
|
||||
public:
|
||||
CHardwareMatrixState();
|
||||
|
||||
void Init( int numHardwareMatrices );
|
||||
|
||||
// return false if there is no slot for this matrix.
|
||||
bool AllocateMatrix( int globalMatrixID );
|
||||
|
||||
// deallocate the least recently used matrix
|
||||
void DeallocateLRU( void );
|
||||
void DeallocateLRU( int n );
|
||||
|
||||
// return true if a matrix is allocate.
|
||||
bool IsMatrixAllocated( int globalMatrixID ) const;
|
||||
|
||||
// flush usage flags - signifies that none of the matrices are being used in the current strip
|
||||
// do this when starting a new strip.
|
||||
void SetAllUnused();
|
||||
|
||||
void DeallocateAll();
|
||||
|
||||
// save the complete state of the hardware matrices
|
||||
void SaveState();
|
||||
|
||||
// restore the complete state of the hardware matrices
|
||||
void RestoreState();
|
||||
|
||||
// Returns the number of free + unsed matrices
|
||||
int AllocatedMatrixCount() const;
|
||||
int FreeMatrixCount() const;
|
||||
|
||||
int GetNthBoneGlobalID( int n ) const;
|
||||
|
||||
void DumpState( void );
|
||||
|
||||
private:
|
||||
int FindHardwareMatrix( int globalMatrixID );
|
||||
int FindLocalLRUIndex( void );
|
||||
|
||||
// Increment and return LRU counter.
|
||||
struct MatrixState_t
|
||||
{
|
||||
bool allocated;
|
||||
int lastUsageID;
|
||||
int globalMatrixID;
|
||||
};
|
||||
|
||||
int m_LRUCounter;
|
||||
int m_NumMatrices;
|
||||
int m_AllocatedMatrices;
|
||||
|
||||
MatrixState_t *m_matrixState;
|
||||
MatrixState_t *m_savedMatrixState;
|
||||
};
|
||||
|
||||
#endif // HARDWAREMATRIXSTATE_H
|
||||
71
utils/studiomdl/hardwarevertexcache.cpp
Normal file
71
utils/studiomdl/hardwarevertexcache.cpp
Normal file
@@ -0,0 +1,71 @@
|
||||
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include "HardwareVertexCache.h"
|
||||
|
||||
CHardwareVertexCache::CHardwareVertexCache()
|
||||
{
|
||||
m_Fifo = NULL;
|
||||
m_Size = 0;
|
||||
Flush();
|
||||
}
|
||||
|
||||
void CHardwareVertexCache::Init( int size )
|
||||
{
|
||||
m_Size = size;
|
||||
m_Fifo = new int[size];
|
||||
Flush();
|
||||
}
|
||||
|
||||
void CHardwareVertexCache::Flush( void )
|
||||
{
|
||||
m_HeadIndex = 0;
|
||||
m_NumEntries = 0;
|
||||
}
|
||||
|
||||
bool CHardwareVertexCache::IsPresent( int index )
|
||||
{
|
||||
int i;
|
||||
// printf( "testing if %d is present\n", index );
|
||||
for( i = 0; i < m_NumEntries; i++ )
|
||||
{
|
||||
if( m_Fifo[( m_HeadIndex + i ) % m_Size] == index )
|
||||
{
|
||||
// printf( "yes!\n" );
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// printf( "no!\n" );
|
||||
// Print();
|
||||
return false;
|
||||
}
|
||||
|
||||
void CHardwareVertexCache::Insert( int index )
|
||||
{
|
||||
// printf( "Inserting: %d\n", index );
|
||||
m_Fifo[( m_HeadIndex + m_NumEntries ) % m_Size] = index;
|
||||
if( m_NumEntries == m_Size )
|
||||
{
|
||||
m_HeadIndex = ( m_HeadIndex + 1 ) % m_Size;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_NumEntries++;
|
||||
}
|
||||
// Print();
|
||||
}
|
||||
|
||||
void CHardwareVertexCache::Print( void )
|
||||
{
|
||||
int i;
|
||||
for( i = 0; i < m_NumEntries; i++ )
|
||||
{
|
||||
printf( "fifo entry %d: %d\n", i, ( int )m_Fifo[( m_HeadIndex + i ) % m_Size] );
|
||||
}
|
||||
}
|
||||
32
utils/studiomdl/hardwarevertexcache.h
Normal file
32
utils/studiomdl/hardwarevertexcache.h
Normal file
@@ -0,0 +1,32 @@
|
||||
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef HARDWAREVERTEXCACHE_H
|
||||
#define HARDWAREVERTEXCACHE_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
// emulate a hardware post T&L vertex fifo
|
||||
|
||||
class CHardwareVertexCache
|
||||
{
|
||||
public:
|
||||
CHardwareVertexCache();
|
||||
void Init( int size );
|
||||
void Insert( int index );
|
||||
bool IsPresent( int index );
|
||||
void Flush( void );
|
||||
void Print( void );
|
||||
private:
|
||||
int m_Size;
|
||||
int *m_Fifo;
|
||||
int m_HeadIndex;
|
||||
int m_NumEntries;
|
||||
};
|
||||
|
||||
#endif // HARDWAREVERTEXCACHE_H
|
||||
183
utils/studiomdl/mdlcompile.vpc
Normal file
183
utils/studiomdl/mdlcompile.vpc
Normal file
@@ -0,0 +1,183 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// MDLCOMPILE.VPC
|
||||
//
|
||||
// Project Script
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
$Macro SRCDIR "..\.."
|
||||
$Macro OUTBINDIR "$SRCDIR\..\game\bin"
|
||||
|
||||
$Include "$SRCDIR\vpc_scripts\source_exe_con_win32_base.vpc"
|
||||
|
||||
$Include "$SRCDIR\vpc_scripts\fbx.vpc"
|
||||
|
||||
$Configuration
|
||||
{
|
||||
$Compiler
|
||||
{
|
||||
$AdditionalIncludeDirectories "$BASE,..\common,..\nvtristriplib,$SRCDIR\Game_Shared,$SRCDIR\ps3sdk\cell\host-common\include,$SRCDIR\ps3sdk\cell\target\common\include"
|
||||
|
||||
$PreprocessorDefinitions "$BASE;PROTECTED_THINGS_DISABLE;MDLCOMPILE"
|
||||
}
|
||||
|
||||
$Linker
|
||||
{
|
||||
$AdditionalDependencies "$BASE winmm.lib libedgegeomtool.Release.Win32.vs8.lib"
|
||||
$AdditionalLibraryDirectories "$BASE;$SRCDIR\ps3sdk\cell\host-win32\lib"
|
||||
}
|
||||
}
|
||||
|
||||
$Configuration "Debug"
|
||||
{
|
||||
$General
|
||||
{
|
||||
$OutputDirectory ".\Debug_mdlcompile" [$WIN32]
|
||||
$IntermediateDirectory ".\Debug_mdlcompile" [$WIN32]
|
||||
}
|
||||
}
|
||||
|
||||
$Configuration "Release"
|
||||
{
|
||||
$General
|
||||
{
|
||||
$OutputDirectory ".\Release_mdlcompile" [$WIN32]
|
||||
$IntermediateDirectory ".\Release_mdlcompile" [$WIN32]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$Project "mdlcompile"
|
||||
{
|
||||
$Folder "Source Files"
|
||||
{
|
||||
$File "..\common\cmdlib.cpp"
|
||||
$File "collisionmodel.cpp"
|
||||
$File "$SRCDIR\public\collisionutils.cpp"
|
||||
$File "collisionmodelsource.cpp"
|
||||
$File "..\common\datalinker.cpp"
|
||||
$File "dmxsupport.cpp"
|
||||
$File "$SRCDIR\public\filesystem_helpers.cpp"
|
||||
$File "$SRCDIR\public\filesystem_init.cpp"
|
||||
$File "..\common\filesystem_tools.cpp"
|
||||
$File "hardwarematrixstate.cpp"
|
||||
$File "hardwarevertexcache.cpp"
|
||||
$File "$SRCDIR\public\interpolatortypes.cpp"
|
||||
$File "$SRCDIR\public\movieobjects\movieobjects_compiletools.cpp"
|
||||
$File "$SRCDIR\public\mdlobjects\mdlobjects.cpp"
|
||||
$File "mrmsupport.cpp"
|
||||
$File "objsupport.cpp"
|
||||
$File "optimize.cpp"
|
||||
$File "optimize_subd.cpp"
|
||||
$File "perfstats.cpp"
|
||||
$File "..\common\physdll.cpp"
|
||||
$File "..\common\scriplib.cpp"
|
||||
$File "simplify.cpp"
|
||||
$File "$SRCDIR\public\studio.cpp"
|
||||
$File "$SRCDIR\common\studiobyteswap.cpp"
|
||||
$File "studiomdl.cpp"
|
||||
$File "UnifyLODs.cpp"
|
||||
$File "v1support.cpp"
|
||||
$File "write.cpp"
|
||||
$File "compileclothproxy.h"
|
||||
$File "compileclothproxy.cpp"
|
||||
}
|
||||
|
||||
$Folder "Header Files"
|
||||
{
|
||||
$File "..\common\cmdlib.h"
|
||||
$File "collisionmodel.h"
|
||||
$File "collisionmodelsource.h"
|
||||
$File "filebuffer.h"
|
||||
$File "..\common\datalinker.h"
|
||||
$File "..\common\filesystem_tools.h"
|
||||
$File "hardwarematrixstate.h"
|
||||
$File "hardwarevertexcache.h"
|
||||
$File "..\nvtristriplib\nvtristrip.h"
|
||||
$File "perfstats.h"
|
||||
$File "..\common\physdll.h"
|
||||
$File "..\common\scriplib.h"
|
||||
$File "studiomdl.h"
|
||||
$File "optimize_subd.h"
|
||||
}
|
||||
|
||||
$Folder "Public Header Files"
|
||||
{
|
||||
$File "$SRCDIR\public\gametrace.h"
|
||||
$File "$SRCDIR\public\filesystem.h"
|
||||
$File "$SRCDIR\public\filesystem_helpers.h"
|
||||
$File "$SRCDIR\public\cmodel.h"
|
||||
$File "$SRCDIR\public\basehandle.h"
|
||||
$File "$SRCDIR\public\tier0\basetypes.h"
|
||||
$File "$SRCDIR\public\bitvec.h"
|
||||
$File "$SRCDIR\public\bone_accessor.h"
|
||||
$File "$SRCDIR\public\bone_setup.h"
|
||||
$File "$SRCDIR\public\bspflags.h"
|
||||
$File "$SRCDIR\public\tier1\byteswap.h"
|
||||
$File "$SRCDIR\public\tier1\characterset.h"
|
||||
$File "$SRCDIR\public\collisionutils.h"
|
||||
$File "$SRCDIR\public\mathlib\compressed_vector.h"
|
||||
$File "$SRCDIR\public\const.h"
|
||||
$File "$SRCDIR\public\vphysics\constraints.h"
|
||||
$File "$SRCDIR\public\tier0\dbg.h"
|
||||
$File "$SRCDIR\public\tier0\fasttimer.h"
|
||||
$File "$SRCDIR\public\appframework\iappsystem.h"
|
||||
$File "$SRCDIR\public\tier0\icommandline.h"
|
||||
$File "$SRCDIR\public\ihandleentity.h"
|
||||
$File "$SRCDIR\public\materialsystem\imaterial.h"
|
||||
$File "$SRCDIR\public\materialsystem\imaterialsystem.h"
|
||||
$File "$SRCDIR\public\materialsystem\imaterialvar.h"
|
||||
$File "$SRCDIR\public\tier1\interface.h"
|
||||
$File "$SRCDIR\public\istudiorender.h"
|
||||
$File "$SRCDIR\public\tier1\keyvalues.h"
|
||||
$File "$SRCDIR\public\materialsystem\materialsystem_config.h"
|
||||
$File "$SRCDIR\public\mathlib\mathlib.h"
|
||||
$File "$SRCDIR\public\tier0\memdbgoff.h"
|
||||
$File "$SRCDIR\public\tier0\memdbgon.h"
|
||||
$File "$SRCDIR\public\phyfile.h"
|
||||
$File "$SRCDIR\public\optimize.h"
|
||||
$File "$SRCDIR\public\tier0\platform.h"
|
||||
$File "$SRCDIR\public\vstdlib\random.h"
|
||||
$File "$SRCDIR\common\studiobyteswap.h"
|
||||
$File "$SRCDIR\public\string_t.h"
|
||||
$File "$SRCDIR\public\tier1\strtools.h"
|
||||
$File "$SRCDIR\public\studio.h"
|
||||
$File "$SRCDIR\public\tier3\tier3.h"
|
||||
$File "$SRCDIR\public\tier1\utlbuffer.h"
|
||||
$File "$SRCDIR\public\tier1\utldict.h"
|
||||
$File "$SRCDIR\public\tier1\utllinkedlist.h"
|
||||
$File "$SRCDIR\public\tier1\utlmemory.h"
|
||||
$File "$SRCDIR\public\tier1\utlrbtree.h"
|
||||
$File "$SRCDIR\public\tier1\utlsymbol.h"
|
||||
$File "$SRCDIR\public\tier1\utlvector.h"
|
||||
$File "$SRCDIR\public\vcollide.h"
|
||||
$File "$SRCDIR\public\vcollide_parse.h"
|
||||
$File "$SRCDIR\public\mathlib\vector.h"
|
||||
$File "$SRCDIR\public\mathlib\vector2d.h"
|
||||
$File "$SRCDIR\public\mathlib\vector4d.h"
|
||||
$File "$SRCDIR\public\mathlib\vmatrix.h"
|
||||
$File "$SRCDIR\public\vphysics_interface.h"
|
||||
$File "$SRCDIR\public\mathlib\vplane.h"
|
||||
$File "$SRCDIR\public\tier0\vprof.h"
|
||||
$File "$SRCDIR\public\vstdlib\vstdlib.h"
|
||||
}
|
||||
|
||||
$Folder "Link Libraries"
|
||||
{
|
||||
$Lib appframework
|
||||
$Lib bonesetup
|
||||
$Lib datamodel
|
||||
$Lib dmeutils
|
||||
$Lib dmserializers
|
||||
$Lib mathlib
|
||||
$Lib mathlib_extended
|
||||
$Lib resourcefile
|
||||
$Lib mdlobjects
|
||||
$Lib meshutils
|
||||
$Lib movieobjects
|
||||
$Lib nvtristrip
|
||||
$Lib tier1
|
||||
$Lib tier2
|
||||
$Lib tier3
|
||||
$Lib fbxutils
|
||||
}
|
||||
}
|
||||
934
utils/studiomdl/mrmsupport.cpp
Normal file
934
utils/studiomdl/mrmsupport.cpp
Normal file
@@ -0,0 +1,934 @@
|
||||
//========= Copyright © 1996-2008, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
|
||||
//
|
||||
// studiomdl.c: generates a studio .mdl file from a .qc script
|
||||
// models/<scriptname>.mdl.
|
||||
//
|
||||
|
||||
|
||||
#pragma warning( disable : 4244 )
|
||||
#pragma warning( disable : 4237 )
|
||||
#pragma warning( disable : 4305 )
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/stat.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "cmdlib.h"
|
||||
#include "scriplib.h"
|
||||
#include "mathlib/mathlib.h"
|
||||
#include "studio.h"
|
||||
#include "studiomdl.h"
|
||||
//#include "..\..\dlls\activity.h"
|
||||
|
||||
bool IsEnd( char const* pLine )
|
||||
{
|
||||
if (strncmp( "end", pLine, 3 ) != 0)
|
||||
return false;
|
||||
return (pLine[3] == '\0') || (pLine[3] == '\n');
|
||||
}
|
||||
|
||||
|
||||
int SortAndBalanceBones( int iCount, int iMaxCount, int bones[], float weights[] )
|
||||
{
|
||||
int i;
|
||||
|
||||
// collapse duplicate bone weights
|
||||
for (i = 0; i < iCount-1; i++)
|
||||
{
|
||||
int j;
|
||||
for (j = i + 1; j < iCount; j++)
|
||||
{
|
||||
if (bones[i] == bones[j])
|
||||
{
|
||||
weights[i] += weights[j];
|
||||
weights[j] = 0.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// do sleazy bubble sort
|
||||
int bShouldSort;
|
||||
do {
|
||||
bShouldSort = false;
|
||||
for (i = 0; i < iCount-1; i++)
|
||||
{
|
||||
if (weights[i+1] > weights[i])
|
||||
{
|
||||
int j = bones[i+1]; bones[i+1] = bones[i]; bones[i] = j;
|
||||
float w = weights[i+1]; weights[i+1] = weights[i]; weights[i] = w;
|
||||
bShouldSort = true;
|
||||
}
|
||||
}
|
||||
} while (bShouldSort);
|
||||
|
||||
#ifdef MDLCOMPILE
|
||||
// throw away all weights less than 1/10,000th
|
||||
while (iCount > 1 && weights[iCount-1] < 0.0001)
|
||||
{
|
||||
iCount--;
|
||||
}
|
||||
#else // #ifdef MDLCOMPILE
|
||||
// throw away all weights less than 1/20th
|
||||
while (iCount > 1 && weights[iCount-1] < 0.05)
|
||||
{
|
||||
iCount--;
|
||||
}
|
||||
#endif // #ifdef MDLCOMPILE
|
||||
|
||||
// clip to the top iMaxCount bones
|
||||
if (iCount > iMaxCount)
|
||||
{
|
||||
iCount = iMaxCount;
|
||||
}
|
||||
|
||||
float t = 0;
|
||||
for (i = 0; i < iCount; i++)
|
||||
{
|
||||
t += weights[i];
|
||||
}
|
||||
|
||||
if (t <= 0.0)
|
||||
{
|
||||
// missing weights?, go ahead and evenly share?
|
||||
// FIXME: shouldn't this error out?
|
||||
t = 1.0 / iCount;
|
||||
|
||||
for (i = 0; i < iCount; i++)
|
||||
{
|
||||
weights[i] = t;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// scale to sum to 1.0
|
||||
t = 1.0 / t;
|
||||
|
||||
for (i = 0; i < iCount; i++)
|
||||
{
|
||||
weights[i] = weights[i] * t;
|
||||
}
|
||||
}
|
||||
|
||||
return iCount;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void Grab_Vertexlist( s_source_t *psource )
|
||||
{
|
||||
while (1)
|
||||
{
|
||||
if (fgets( g_szLine, sizeof( g_szLine ), g_fpInput ) != NULL)
|
||||
{
|
||||
int j;
|
||||
int bone;
|
||||
Vector p;
|
||||
int iCount, bones[4];
|
||||
float weights[4];
|
||||
|
||||
g_iLinecount++;
|
||||
|
||||
// check for end
|
||||
if (IsEnd(g_szLine))
|
||||
return;
|
||||
|
||||
|
||||
int i = sscanf( g_szLine, "%d %d %f %f %f %d %d %f %d %f %d %f %d %f",
|
||||
&j,
|
||||
&bone,
|
||||
&p[0], &p[1], &p[2],
|
||||
&iCount,
|
||||
&bones[0], &weights[0], &bones[1], &weights[1], &bones[2], &weights[2], &bones[3], &weights[3] );
|
||||
|
||||
if (i == 5)
|
||||
{
|
||||
if (bone < 0 || bone >= psource->numbones)
|
||||
{
|
||||
MdlWarning( "bogus bone index\n" );
|
||||
MdlWarning( "%d %s :\n%s", g_iLinecount, g_szFilename, g_szLine );
|
||||
MdlError( "Exiting due to errors\n" );
|
||||
}
|
||||
|
||||
VectorCopy( p, g_vertex[j] );
|
||||
g_bone[j].numbones = 1;
|
||||
g_bone[j].bone[0] = bone;
|
||||
g_bone[j].weight[0] = 1.0;
|
||||
}
|
||||
else if (i > 5)
|
||||
{
|
||||
iCount = SortAndBalanceBones( iCount, MAXSTUDIOBONEWEIGHTS, bones, weights );
|
||||
|
||||
VectorCopy( p, g_vertex[j] );
|
||||
g_bone[j].numbones = iCount;
|
||||
for (i = 0; i < iCount; i++)
|
||||
{
|
||||
g_bone[j].bone[i] = bones[i];
|
||||
g_bone[j].weight[i] = weights[i];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
MdlError("%s: error on line %d: %s", g_szFilename, g_iLinecount, g_szLine );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void Grab_Facelist( s_source_t *psource )
|
||||
{
|
||||
while (1)
|
||||
{
|
||||
if (fgets( g_szLine, sizeof( g_szLine ), g_fpInput ) != NULL)
|
||||
{
|
||||
int j;
|
||||
s_tmpface_t f;
|
||||
|
||||
g_iLinecount++;
|
||||
|
||||
// check for end
|
||||
if (IsEnd(g_szLine))
|
||||
return;
|
||||
|
||||
if (sscanf( g_szLine, "%d %d %d %d",
|
||||
&j,
|
||||
&f.a, &f.b, &f.c) == 4)
|
||||
{
|
||||
g_face[j] = f;
|
||||
}
|
||||
else
|
||||
{
|
||||
MdlError("%s: error on line %d: %s", g_szFilename, g_iLinecount, g_szLine );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void Grab_Materiallist( s_source_t *psource )
|
||||
{
|
||||
while (1)
|
||||
{
|
||||
if (fgets( g_szLine, sizeof( g_szLine ), g_fpInput ) != NULL)
|
||||
{
|
||||
// char name[256];
|
||||
char path[MAX_PATH];
|
||||
rgb2_t a, d, s;
|
||||
float g;
|
||||
int j;
|
||||
|
||||
g_iLinecount++;
|
||||
|
||||
// check for end
|
||||
if (IsEnd(g_szLine))
|
||||
return;
|
||||
|
||||
if (sscanf( g_szLine, "%d %f %f %f %f %f %f %f %f %f %f %f %f %f \"%[^\"]s",
|
||||
&j,
|
||||
&a.r, &a.g, &a.b, &a.a,
|
||||
&d.r, &d.g, &d.b, &d.a,
|
||||
&s.r, &s.g, &s.b, &s.a,
|
||||
&g,
|
||||
path ) == 15)
|
||||
{
|
||||
if (path[0] == '\0')
|
||||
{
|
||||
psource->texmap[j] = -1;
|
||||
}
|
||||
else if (j < sizeof(psource->texmap))
|
||||
{
|
||||
psource->texmap[j] = LookupTexture( path );
|
||||
}
|
||||
else
|
||||
{
|
||||
MdlError( "Too many materials, max %d\n", sizeof(psource->texmap) );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Grab_Texcoordlist( s_source_t *psource )
|
||||
{
|
||||
while (1)
|
||||
{
|
||||
if (fgets( g_szLine, sizeof( g_szLine ), g_fpInput ) != NULL)
|
||||
{
|
||||
int j;
|
||||
Vector2D t;
|
||||
|
||||
g_iLinecount++;
|
||||
|
||||
// check for end
|
||||
if (IsEnd(g_szLine))
|
||||
return;
|
||||
|
||||
if (sscanf( g_szLine, "%d %f %f",
|
||||
&j,
|
||||
&t[0], &t[1]) == 3)
|
||||
{
|
||||
t[1] = 1.0 - t[1];
|
||||
g_texcoord[0][j][0] = t[0];
|
||||
g_texcoord[0][j][1] = t[1];
|
||||
}
|
||||
else
|
||||
{
|
||||
MdlError("%s: error on line %d: %s", g_szFilename, g_iLinecount, g_szLine );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Grab_Normallist( s_source_t *psource )
|
||||
{
|
||||
while (1)
|
||||
{
|
||||
if (fgets( g_szLine, sizeof( g_szLine ), g_fpInput ) != NULL)
|
||||
{
|
||||
int j;
|
||||
int bone;
|
||||
Vector n;
|
||||
|
||||
g_iLinecount++;
|
||||
|
||||
// check for end
|
||||
if (IsEnd(g_szLine))
|
||||
return;
|
||||
|
||||
|
||||
if (sscanf( g_szLine, "%d %d %f %f %f",
|
||||
&j,
|
||||
&bone,
|
||||
&n[0], &n[1], &n[2]) == 5)
|
||||
{
|
||||
if (bone < 0 || bone >= psource->numbones)
|
||||
{
|
||||
MdlWarning( "bogus bone index\n" );
|
||||
MdlWarning( "%d %s :\n%s", g_iLinecount, g_szFilename, g_szLine );
|
||||
MdlError( "Exiting due to errors\n" );
|
||||
}
|
||||
|
||||
VectorCopy( n, g_normal[j] );
|
||||
}
|
||||
else
|
||||
{
|
||||
MdlError("%s: error on line %d: %s", g_szFilename, g_iLinecount, g_szLine );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void Grab_Faceattriblist( s_source_t *psource )
|
||||
{
|
||||
while (1)
|
||||
{
|
||||
if (fgets( g_szLine, sizeof( g_szLine ), g_fpInput ) != NULL)
|
||||
{
|
||||
int j;
|
||||
int smooth;
|
||||
int material;
|
||||
s_tmpface_t f;
|
||||
unsigned short s;
|
||||
|
||||
g_iLinecount++;
|
||||
|
||||
// check for end
|
||||
if (IsEnd(g_szLine))
|
||||
return;
|
||||
|
||||
if (sscanf( g_szLine, "%d %d %d %d %d %d %d %d %d",
|
||||
&j,
|
||||
&material,
|
||||
&smooth,
|
||||
&f.ta[0], &f.tb[0], &f.tc[0],
|
||||
&f.na, &f.nb, &f.nc) == 9)
|
||||
{
|
||||
f.a = g_face[j].a;
|
||||
f.b = g_face[j].b;
|
||||
f.c = g_face[j].c;
|
||||
|
||||
f.material = UseTextureAsMaterial( psource->texmap[material] );
|
||||
if (f.material < 0)
|
||||
{
|
||||
MdlError( "face %d references NULL texture %d\n", j, material );
|
||||
}
|
||||
|
||||
if (1)
|
||||
{
|
||||
s = f.b; f.b = f.c; f.c = s;
|
||||
s = f.tb[0]; f.tb[0] = f.tc[0]; f.tc[0] = s;
|
||||
s = f.nb; f.nb = f.nc; f.nc = s;
|
||||
}
|
||||
|
||||
g_face[j] = f;
|
||||
}
|
||||
else
|
||||
{
|
||||
MdlError("%s: error on line %d: %s", g_szFilename, g_iLinecount, g_szLine );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int closestNormal( int v, int n )
|
||||
{
|
||||
float maxdot = -1.0;
|
||||
float dot;
|
||||
int r = n;
|
||||
|
||||
v_unify_t *cur = v_list[v];
|
||||
|
||||
while (cur)
|
||||
{
|
||||
dot = DotProduct( g_normal[cur->n], g_normal[n] );
|
||||
if (dot > maxdot)
|
||||
{
|
||||
r = cur->n;
|
||||
maxdot = dot;
|
||||
}
|
||||
cur = cur->next;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
int AddToVlist(int v, int m, int n, int* t, int firstref)
|
||||
{
|
||||
v_unify_t *prev = NULL;
|
||||
v_unify_t *cur = v_list[v];
|
||||
|
||||
while (cur)
|
||||
{
|
||||
if (cur->m == m && cur->n == n)
|
||||
{
|
||||
bool bMatch = true;
|
||||
for (int i = 0; (i < MAXSTUDIOTEXCOORDS) && bMatch; ++i)
|
||||
{
|
||||
if (cur->t[i] != t[i])
|
||||
{
|
||||
bMatch = false;
|
||||
}
|
||||
}
|
||||
if (bMatch)
|
||||
{
|
||||
cur->refcount++;
|
||||
return cur - v_listdata;
|
||||
}
|
||||
}
|
||||
prev = cur;
|
||||
cur = cur->next;
|
||||
}
|
||||
|
||||
if (g_numvlist >= MAXSTUDIOSRCVERTS)
|
||||
{
|
||||
MdlError( "Too many unified vertices\n");
|
||||
}
|
||||
|
||||
cur = &v_listdata[g_numvlist++];
|
||||
cur->lastref = -1;
|
||||
cur->refcount = 1;
|
||||
cur->v = v;
|
||||
cur->m = m;
|
||||
cur->n = n;
|
||||
for (int i = 0; i < MAXSTUDIOTEXCOORDS; ++i)
|
||||
{
|
||||
cur->t[i] = t[i];
|
||||
}
|
||||
|
||||
if (prev)
|
||||
{
|
||||
prev->next = cur;
|
||||
}
|
||||
else
|
||||
{
|
||||
v_list[v] = cur;
|
||||
}
|
||||
|
||||
return g_numvlist - 1;
|
||||
}
|
||||
|
||||
void DecrementReferenceVlist( int uv, int numverts )
|
||||
{
|
||||
if (uv < 0 || uv > MAXSTUDIOSRCVERTS)
|
||||
MdlError( "decrement outside of range\n");
|
||||
|
||||
v_listdata[uv].refcount--;
|
||||
|
||||
if (v_listdata[uv].refcount == 0)
|
||||
{
|
||||
v_listdata[uv].lastref = numverts;
|
||||
}
|
||||
else if (v_listdata[uv].refcount < 0)
|
||||
{
|
||||
MdlError("<0 ref\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void UnifyIndices( s_source_t *psource )
|
||||
{
|
||||
int i;
|
||||
|
||||
s_face_t uface;
|
||||
|
||||
// clear v_list
|
||||
g_numvlist = 0;
|
||||
memset( v_list, 0, sizeof( v_list ) );
|
||||
memset( v_listdata, 0, sizeof( v_listdata ) );
|
||||
|
||||
// create an list of all the
|
||||
for (i = 0; i < g_numfaces; i++)
|
||||
{
|
||||
uface.a = AddToVlist(g_face[i].a, g_face[i].material, g_face[i].na, (int*)g_face[i].ta, g_numverts);
|
||||
uface.b = AddToVlist(g_face[i].b, g_face[i].material, g_face[i].nb, (int*)g_face[i].tb, g_numverts);
|
||||
uface.c = AddToVlist(g_face[i].c, g_face[i].material, g_face[i].nc, (int*)g_face[i].tc, g_numverts);
|
||||
uface.d = 0xFFFFFFFF;
|
||||
|
||||
if ( g_face[i].d != 0xFFFFFFFF )
|
||||
{
|
||||
uface.d = AddToVlist(g_face[i].d, g_face[i].material, g_face[i].nd, (int*)g_face[i].td, g_numverts);
|
||||
}
|
||||
|
||||
// keep an original copy
|
||||
g_src_uface[i] = uface;
|
||||
}
|
||||
|
||||
// printf("%d : %d %d %d\n", numvlist, g_numverts, g_numnormals, g_numtexcoords );
|
||||
}
|
||||
|
||||
void CalcModelTangentSpaces( s_source_t *pSrc );
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Builds a list of unique vertices in a source
|
||||
//-----------------------------------------------------------------------------
|
||||
static void BuildUniqueVertexList( s_source_t *pSource, const int *pDesiredToVList )
|
||||
{
|
||||
// allocate memory
|
||||
pSource->vertex = (s_vertexinfo_t *)calloc( pSource->numvertices, sizeof( s_vertexinfo_t ) );
|
||||
|
||||
int numValidTexcoords = 1;
|
||||
|
||||
for (int i = 1; i < MAXSTUDIOTEXCOORDS; ++i)
|
||||
{
|
||||
if (g_numtexcoords[i])
|
||||
{
|
||||
numValidTexcoords++;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// create arrays of unique vertexes, normals, texcoords.
|
||||
for (int i = 0; i < pSource->numvertices; i++)
|
||||
{
|
||||
int j = pDesiredToVList[i];
|
||||
|
||||
s_vertexinfo_t &vertex = pSource->vertex[i];
|
||||
VectorCopy( g_vertex[ v_listdata[j].v ], vertex.position );
|
||||
VectorCopy( g_normal[ v_listdata[j].n ], vertex.normal );
|
||||
|
||||
vertex.boneweight.numbones = g_bone[ v_listdata[j].v ].numbones;
|
||||
int k;
|
||||
for( k = 0; k < MAXSTUDIOBONEWEIGHTS; k++ )
|
||||
{
|
||||
vertex.boneweight.bone[k] = g_bone[ v_listdata[j].v ].bone[k];
|
||||
vertex.boneweight.weight[k] = g_bone[ v_listdata[j].v ].weight[k];
|
||||
}
|
||||
|
||||
for (k = 0; k < numValidTexcoords; ++k)
|
||||
{
|
||||
Vector2Copy(g_texcoord[k][v_listdata[j].t[k]], vertex.texcoord[k]);
|
||||
}
|
||||
vertex.numTexcoord = numValidTexcoords;
|
||||
|
||||
// store a bunch of other info
|
||||
vertex.material = v_listdata[j].m;
|
||||
|
||||
#if 0
|
||||
pSource->vertexInfo[i].firstref = v_listdata[j].firstref;
|
||||
pSource->vertexInfo[i].lastref = v_listdata[j].lastref;
|
||||
#endif
|
||||
// printf("%4d : %2d : %6.2f %6.2f %6.2f\n", i, psource->boneweight[i].bone[0], psource->vertex[i][0], psource->vertex[i][1], psource->vertex[i][2] );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// sort new vertices by materials, last used
|
||||
//-----------------------------------------------------------------------------
|
||||
static int vlistCompare( const void *elem1, const void *elem2 )
|
||||
{
|
||||
v_unify_t *u1 = &v_listdata[*(int *)elem1];
|
||||
v_unify_t *u2 = &v_listdata[*(int *)elem2];
|
||||
|
||||
// sort by material
|
||||
if (u1->m < u2->m)
|
||||
return -1;
|
||||
if (u1->m > u2->m)
|
||||
return 1;
|
||||
|
||||
// sort by last used
|
||||
if (u1->lastref < u2->lastref)
|
||||
return -1;
|
||||
if (u1->lastref > u2->lastref)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void SortVerticesByMaterial( int *pDesiredToVList, int *pVListToDesired )
|
||||
{
|
||||
for ( int i = 0; i < g_numvlist; i++ )
|
||||
{
|
||||
pDesiredToVList[i] = i;
|
||||
}
|
||||
qsort( pDesiredToVList, g_numvlist, sizeof( int ), vlistCompare );
|
||||
for ( int i = 0; i < g_numvlist; i++ )
|
||||
{
|
||||
pVListToDesired[ pDesiredToVList[i] ] = i;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// sort new faces by materials, last used
|
||||
//-----------------------------------------------------------------------------
|
||||
static int faceCompare( const void *elem1, const void *elem2 )
|
||||
{
|
||||
int i1 = *(int *)elem1;
|
||||
int i2 = *(int *)elem2;
|
||||
|
||||
// sort by material
|
||||
if (g_face[i1].material < g_face[i2].material)
|
||||
return -1;
|
||||
if (g_face[i1].material > g_face[i2].material)
|
||||
return 1;
|
||||
|
||||
// sort by original usage
|
||||
if (i1 < i2)
|
||||
return -1;
|
||||
if (i1 > i2)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void SortFacesByMaterial( int *pDesiredToSrcFace )
|
||||
{
|
||||
// NOTE: Unlike SortVerticesByMaterial, srcFaceToDesired isn't needed, so we're not computing it
|
||||
for ( int i = 0; i < g_numfaces; i++ )
|
||||
{
|
||||
pDesiredToSrcFace[i] = i;
|
||||
}
|
||||
qsort( pDesiredToSrcFace, g_numfaces, sizeof( int ), faceCompare );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Builds mesh structures in the source
|
||||
//-----------------------------------------------------------------------------
|
||||
static void PointMeshesToVertexAndFaceData( s_source_t *pSource, int *pDesiredToSrcFace )
|
||||
{
|
||||
// First, assign all meshes to be empty
|
||||
// A mesh is a set of faces + vertices that all use 1 material
|
||||
for ( int m = 0; m < MAXSTUDIOSKINS; m++ )
|
||||
{
|
||||
pSource->mesh[m].numvertices = 0;
|
||||
pSource->mesh[m].vertexoffset = pSource->numvertices;
|
||||
|
||||
pSource->mesh[m].numfaces = 0;
|
||||
pSource->mesh[m].faceoffset = pSource->numfaces;
|
||||
}
|
||||
|
||||
// find first and count of vertices per material
|
||||
for ( int i = 0; i < pSource->numvertices; i++ )
|
||||
{
|
||||
int m = pSource->vertex[i].material;
|
||||
pSource->mesh[m].numvertices++;
|
||||
if (pSource->mesh[m].vertexoffset > i)
|
||||
{
|
||||
pSource->mesh[m].vertexoffset = i;
|
||||
}
|
||||
}
|
||||
|
||||
// find first and count of faces per material
|
||||
for ( int i = 0; i < pSource->numfaces; i++ )
|
||||
{
|
||||
int m = g_face[ pDesiredToSrcFace[i] ].material;
|
||||
|
||||
pSource->mesh[m].numfaces++;
|
||||
if (pSource->mesh[m].faceoffset > i)
|
||||
{
|
||||
pSource->mesh[m].faceoffset = i;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
for (k = 0; k < MAXSTUDIOSKINS; k++)
|
||||
{
|
||||
printf("%d : %d:%d %d:%d\n", k, psource->mesh[k].numvertices, psource->mesh[k].vertexoffset, psource->mesh[k].numfaces, psource->mesh[k].faceoffset );
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Builds the face list in the mesh
|
||||
//-----------------------------------------------------------------------------
|
||||
static void BuildFaceList( s_source_t *pSource, int *pVListToDesired, int *pDesiredToSrcFace )
|
||||
{
|
||||
pSource->face = (s_face_t *)calloc( pSource->numfaces, sizeof( s_face_t ));
|
||||
for ( int m = 0; m < MAXSTUDIOSKINS; m++)
|
||||
{
|
||||
if ( !pSource->mesh[m].numfaces )
|
||||
continue;
|
||||
|
||||
pSource->meshindex[ pSource->nummeshes++ ] = m;
|
||||
|
||||
for ( int i = pSource->mesh[m].faceoffset; i < pSource->mesh[m].numfaces + pSource->mesh[m].faceoffset; i++)
|
||||
{
|
||||
int j = pDesiredToSrcFace[i];
|
||||
|
||||
// NOTE: per-face vertex indices a,b,c,d are mesh relative (hence the subtraction), while g_src_uface are model relative
|
||||
pSource->face[i].a = pVListToDesired[ g_src_uface[j].a ] - pSource->mesh[m].vertexoffset;
|
||||
pSource->face[i].b = pVListToDesired[ g_src_uface[j].b ] - pSource->mesh[m].vertexoffset;
|
||||
pSource->face[i].c = pVListToDesired[ g_src_uface[j].c ] - pSource->mesh[m].vertexoffset;
|
||||
|
||||
if ( g_src_uface[j].d != 0xFFFFFFFF )
|
||||
{
|
||||
pSource->face[i].d = pVListToDesired[ g_src_uface[j].d ] - pSource->mesh[m].vertexoffset;
|
||||
}
|
||||
|
||||
Assert( ((pSource->face[i].a & 0xF0000000) == 0) && ((pSource->face[i].b & 0xF0000000) == 0) &&
|
||||
((pSource->face[i].c & 0xF0000000) == 0) && (((pSource->face[i].d & 0xF0000000) == 0) || (pSource->face[i].d == 0xFFFFFFFF)) );
|
||||
// printf("%3d : %4d %4d %4d %4d\n", i, pSource->face[i].a, pSource->face[i].b, pSource->face[i].c, pSource->face[i].d );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Remaps the vertex animations based on the new vertex ordering
|
||||
//-----------------------------------------------------------------------------
|
||||
static void RemapVertexAnimations( s_source_t *pSource, int *pVListToDesired )
|
||||
{
|
||||
CUtlVectorAuto< int > temp;
|
||||
int nAnimationCount = pSource->m_Animations.Count();
|
||||
for ( int i = 0; i < nAnimationCount; ++i )
|
||||
{
|
||||
s_sourceanim_t &anim = pSource->m_Animations[i];
|
||||
if ( !anim.newStyleVertexAnimations )
|
||||
continue;
|
||||
|
||||
for ( int j = 0; j < MAXSTUDIOANIMFRAMES; ++j )
|
||||
{
|
||||
int nVAnimCount = anim.numvanims[j];
|
||||
if ( nVAnimCount == 0 )
|
||||
continue;
|
||||
|
||||
// Copy off the initial vertex data
|
||||
// Have to do it in 2 loops because it'll overwrite itself if we do it in 1
|
||||
for ( int k = 0; k < nVAnimCount; ++k )
|
||||
{
|
||||
temp[k] = anim.vanim[j][k].vertex;
|
||||
}
|
||||
|
||||
for ( int k = 0; k < nVAnimCount; ++k )
|
||||
{
|
||||
// NOTE: vertex animations are model relative, not mesh relative
|
||||
anim.vanim[j][k].vertex = pVListToDesired[ temp[k] ];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Sorts vertices by material type, re-maps data structures that refer to those vertices
|
||||
// to use the new indices
|
||||
//-----------------------------------------------------------------------------
|
||||
void BuildIndividualMeshes( s_source_t *pSource )
|
||||
{
|
||||
int *v_listsort = (int *)malloc( g_numvlist * sizeof( int ) ); // map desired order to vlist entry
|
||||
int *v_ilistsort = (int *)malloc( g_numvlist * sizeof( int ) ); // map vlist entry to desired order
|
||||
int *facesort = (int *)malloc( g_numfaces * sizeof( int ) ); // map desired order to src_face entry
|
||||
|
||||
SortVerticesByMaterial( v_listsort, v_ilistsort );
|
||||
SortFacesByMaterial( facesort );
|
||||
|
||||
pSource->numvertices = g_numvlist;
|
||||
pSource->numfaces = g_numfaces;
|
||||
|
||||
BuildUniqueVertexList( pSource, v_listsort );
|
||||
PointMeshesToVertexAndFaceData( pSource, facesort );
|
||||
BuildFaceList( pSource, v_ilistsort, facesort );
|
||||
RemapVertexAnimations( pSource, v_ilistsort );
|
||||
CalcModelTangentSpaces( pSource );
|
||||
|
||||
free( facesort );
|
||||
free( v_ilistsort );
|
||||
free( v_listsort );
|
||||
}
|
||||
|
||||
|
||||
void Grab_MRMFaceupdates( s_source_t *psource )
|
||||
{
|
||||
while (1)
|
||||
{
|
||||
if (fgets( g_szLine, sizeof( g_szLine ), g_fpInput ) != NULL)
|
||||
{
|
||||
g_iLinecount++;
|
||||
|
||||
// check for end
|
||||
if (IsEnd(g_szLine))
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int Load_VRM ( s_source_t *psource )
|
||||
{
|
||||
char cmd[1024];
|
||||
int option;
|
||||
|
||||
if (!OpenGlobalFile( psource->filename ))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if( !g_quiet )
|
||||
{
|
||||
printf ("grabbing %s\n", psource->filename);
|
||||
}
|
||||
|
||||
g_iLinecount = 0;
|
||||
|
||||
while (fgets( g_szLine, sizeof( g_szLine ), g_fpInput ) != NULL)
|
||||
{
|
||||
g_iLinecount++;
|
||||
sscanf( g_szLine, "%1023s %d", cmd, &option );
|
||||
if (stricmp( cmd, "version" ) == 0)
|
||||
{
|
||||
if (option != 2)
|
||||
{
|
||||
MdlError("bad version\n");
|
||||
}
|
||||
}
|
||||
else if (stricmp( cmd, "name" ) == 0)
|
||||
{
|
||||
}
|
||||
else if (stricmp( cmd, "vertices" ) == 0)
|
||||
{
|
||||
g_numverts = option;
|
||||
}
|
||||
else if (stricmp( cmd, "faces" ) == 0)
|
||||
{
|
||||
g_numfaces = option;
|
||||
}
|
||||
else if (stricmp( cmd, "materials" ) == 0)
|
||||
{
|
||||
// doesn't matter;
|
||||
}
|
||||
else if (stricmp( cmd, "texcoords" ) == 0)
|
||||
{
|
||||
g_numtexcoords[0] = option;
|
||||
if (option == 0)
|
||||
MdlError( "model has no texture coordinates\n");
|
||||
}
|
||||
else if (stricmp( cmd, "normals" ) == 0)
|
||||
{
|
||||
g_numnormals = option;
|
||||
}
|
||||
else if (stricmp( cmd, "tristrips" ) == 0)
|
||||
{
|
||||
// should be 0;
|
||||
}
|
||||
|
||||
else if (stricmp( cmd, "vertexlist" ) == 0)
|
||||
{
|
||||
Grab_Vertexlist( psource );
|
||||
}
|
||||
else if (stricmp( cmd, "facelist" ) == 0)
|
||||
{
|
||||
Grab_Facelist( psource );
|
||||
}
|
||||
else if (stricmp( cmd, "materiallist" ) == 0)
|
||||
{
|
||||
Grab_Materiallist( psource );
|
||||
}
|
||||
else if (stricmp( cmd, "texcoordlist" ) == 0)
|
||||
{
|
||||
Grab_Texcoordlist( psource );
|
||||
}
|
||||
else if (stricmp( cmd, "normallist" ) == 0)
|
||||
{
|
||||
Grab_Normallist( psource );
|
||||
}
|
||||
else if (stricmp( cmd, "faceattriblist" ) == 0)
|
||||
{
|
||||
Grab_Faceattriblist( psource );
|
||||
}
|
||||
|
||||
else if (stricmp( cmd, "MRM" ) == 0)
|
||||
{
|
||||
}
|
||||
else if (stricmp( cmd, "MRMvertices" ) == 0)
|
||||
{
|
||||
}
|
||||
else if (stricmp( cmd, "MRMfaces" ) == 0)
|
||||
{
|
||||
}
|
||||
else if (stricmp( cmd, "MRMfaceupdates" ) == 0)
|
||||
{
|
||||
Grab_MRMFaceupdates( psource );
|
||||
}
|
||||
|
||||
else if (stricmp( cmd, "nodes" ) == 0)
|
||||
{
|
||||
psource->numbones = Grab_Nodes( psource->localBone );
|
||||
}
|
||||
else if (stricmp( cmd, "skeleton" ) == 0)
|
||||
{
|
||||
Grab_Animation( psource, "BindPose" );
|
||||
}
|
||||
/*
|
||||
else if (stricmp( cmd, "triangles" ) == 0) {
|
||||
Grab_Triangles( psource );
|
||||
}
|
||||
*/
|
||||
else
|
||||
{
|
||||
MdlError("unknown VRM command : %s \n", cmd );
|
||||
}
|
||||
}
|
||||
|
||||
UnifyIndices( psource );
|
||||
BuildIndividualMeshes( psource );
|
||||
|
||||
fclose( g_fpInput );
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
432
utils/studiomdl/objsupport.cpp
Normal file
432
utils/studiomdl/objsupport.cpp
Normal 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
4471
utils/studiomdl/optimize.cpp
Normal file
File diff suppressed because it is too large
Load Diff
936
utils/studiomdl/optimize_subd.cpp
Normal file
936
utils/studiomdl/optimize_subd.cpp
Normal 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 ¢erOffset = baseQuad->vtx1RingCenterQuadOffset[baseLocalID] = 1;
|
||||
unsigned short &valence = baseQuad->valences[baseLocalID] = 0;
|
||||
unsigned short &oneRingSize = baseQuad->vtx1RingSize[baseLocalID] = 0;
|
||||
|
||||
HalfEdge *heBase = &baseQuad->halfEdges[ MOD4[baseLocalID] ];
|
||||
HalfEdge *he = heBase->sectorStart;
|
||||
|
||||
oneRing[oneRingSize++] = he->patch->vtxIDs[ MOD4[he->localID+0] ];
|
||||
valence++;
|
||||
oneRing[oneRingSize++] = he->patch->vtxIDs[ MOD4[he->localID+1] ];
|
||||
|
||||
HalfEdge *heEnd = he->twin;
|
||||
he = he->PrevInFace();
|
||||
|
||||
do
|
||||
{
|
||||
oneRing[oneRingSize++] = he->patch->vtxIDs[ MOD4[he->localID+3] ];
|
||||
valence++;
|
||||
oneRing[oneRingSize++] = he->patch->vtxIDs[ MOD4[he->localID+0] ];
|
||||
|
||||
if ( he->twin == heBase )
|
||||
{
|
||||
centerOffset = oneRingSize - 1;
|
||||
}
|
||||
|
||||
he = (he->BndEdge() && baseQuad->bndVtx[baseLocalID]) ? NULL : he->NextByHead(); // make sure we only step over BndEdge if it is dangling.
|
||||
|
||||
} while ( ( he != NULL ) && ( he != heEnd ) );
|
||||
|
||||
if ( ( he != NULL) && ( he == heEnd ) ) // if we closed the loop, add off-edge vertex from last quad.
|
||||
{
|
||||
oneRing[oneRingSize++] = he->patch->vtxIDs[ MOD4[ he->localID+3 ]];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Depends on bndVtx, cornerVtx, valence
|
||||
void COptimizeSubDBuilder::ComputeSectorAngle( SubD_Face_t *baseQuad, unsigned short baseLocalID )
|
||||
{
|
||||
if ( !baseQuad->bndVtx[baseLocalID] ) // If no boundary vertex, nothing needs to be done (includes dangling crease)
|
||||
return;
|
||||
|
||||
if ( !baseQuad->cornerVtx[baseLocalID] ) // If no corner, set loopGapAngle = PI (or PI/2 for valence==2)
|
||||
{
|
||||
baseQuad->loopGapAngle[baseLocalID] = 65535 / ( baseQuad->valences[baseLocalID] == 2 ? 4 : 2 );
|
||||
return;
|
||||
}
|
||||
|
||||
HalfEdge *he = baseQuad->halfEdges[ MOD4[baseLocalID] ].sectorStart;
|
||||
|
||||
Vector center_pos = VTXPOS( he->patch->vtxIDs[ he->localID ] );
|
||||
Vector center_nor = VTXNOR( he->patch->vtxIDs[ he->localID ] );
|
||||
VectorNormalize(center_nor);
|
||||
|
||||
int debugVtxID = he->patch->vtxIDs[ MOD4[ he->localID+1 ] ];
|
||||
|
||||
Vector eVec1 = VTXPOS( he->patch->vtxIDs[ MOD4[ he->localID+1 ] ] ) - center_pos, eVec2;
|
||||
Vector npVec1 = project_and_normalize( eVec1, center_nor ), npVec2;
|
||||
|
||||
float sector_angle = 0;
|
||||
|
||||
he = he->PrevInFace();
|
||||
|
||||
do
|
||||
{
|
||||
debugVtxID = he->patch->vtxIDs[ MOD4[ he->localID ] ];
|
||||
|
||||
eVec2 = VTXPOS( he->patch->vtxIDs[ MOD4[ he->localID ] ] ) - center_pos;
|
||||
npVec2 = project_and_normalize( eVec2, center_nor );
|
||||
sector_angle += acosf( DotProduct( npVec1, npVec2 ) );
|
||||
|
||||
he = he->BndEdge() ? NULL : he->NextByHead(); // make sure we only step over BndEdge if it is dangling.
|
||||
npVec1 = npVec2;
|
||||
} while ( he != NULL ); // only way to terminate is to hit BndEdge
|
||||
|
||||
VectorNormalize( eVec1 );
|
||||
VectorNormalize( eVec2 );
|
||||
float loopGapAngleF = acosf( DotProduct(eVec1, eVec2) ); // measure overall gap
|
||||
baseQuad->loopGapAngle[baseLocalID] = (unsigned int) ( ( 65535.0 * loopGapAngleF ) / ( 2 * PI ) );
|
||||
}
|
||||
|
||||
void COptimizeSubDBuilder::MendVertices(SubD_Face_t *baseQuad, unsigned short baseLocalID)
|
||||
{
|
||||
HalfEdge *he = baseQuad->halfEdges[ baseLocalID ].sectorStart;
|
||||
unsigned short vtxID = baseQuad->vtxIDs[ baseLocalID ];
|
||||
|
||||
Vector p = VTXPOS( vtxID );
|
||||
Vector n = VTXNOR( vtxID );
|
||||
|
||||
HalfEdge *heEnd = he->twin;
|
||||
he = he->PrevInFace();
|
||||
|
||||
do
|
||||
{
|
||||
if (( VTXPOS( he->patch->vtxIDs[ MOD4[he->localID+1] ]) == p ) &&
|
||||
( VTXNOR( he->patch->vtxIDs[ MOD4[he->localID+1] ]) == n ))
|
||||
{
|
||||
he->patch->vtxIDs[ MOD4[he->localID+1] ] = vtxID;
|
||||
|
||||
}
|
||||
|
||||
if ( (he->twin) &&
|
||||
( VTXPOS( he->twin->patch->vtxIDs[ MOD4[he->twin->localID] ] ) == p) &&
|
||||
( VTXNOR( he->twin->patch->vtxIDs[ MOD4[he->twin->localID] ] ) == n) )
|
||||
{
|
||||
he->twin->patch->vtxIDs[ MOD4[he->twin->localID] ] = vtxID;
|
||||
}
|
||||
|
||||
he = he->NextByHead();
|
||||
} while (( he != NULL ) && (he != heEnd));
|
||||
|
||||
}
|
||||
|
||||
// Computes a bitfield with bits set if the corresponding neighbor-vertex is a concave corner
|
||||
// this has to go in a second pass as all per-face-per-vertex flags from the first pass to be computed beforehand
|
||||
void COptimizeSubDBuilder::ComputeNbCorners( SubD_Face_t *baseQuad, unsigned short baseLocalID )
|
||||
{
|
||||
NeighborCornerBitfield nbCorners( &baseQuad->nbCornerVtx[baseLocalID] );
|
||||
|
||||
HalfEdge *he = baseQuad->halfEdges[ MOD4[baseLocalID] ].sectorStart;
|
||||
nbCorners.pushBit( he->patch->cornerVtx[ MOD4[he->localID+1] ] == 2 );
|
||||
|
||||
HalfEdge *heEnd = he->twin;
|
||||
he = he->PrevInFace();
|
||||
|
||||
do
|
||||
{
|
||||
nbCorners.pushBit( he->patch->cornerVtx[ he->localID ] == 2 );
|
||||
|
||||
he = ( he->BndEdge() && baseQuad->bndVtx[baseLocalID] ) ? NULL : he->NextByHead(); // make sure we only step over BndEdge if it is dangling.
|
||||
} while (( he != NULL ) && (he != heEnd));
|
||||
}
|
||||
|
||||
unsigned short COptimizeSubDBuilder::FindNeighborVertex( HalfEdge** ppOutMirrorEdge, const HalfEdge *pHalfEdge, int indexAlongEdge )
|
||||
// Finds neighboring vertex along the mirror edge of pHalfEdge.
|
||||
// Returns the index of the vertex.
|
||||
// pOutMirrorEdge is the mirror edge we took this vertex from.
|
||||
// pHalfEdge is the shared edge who's mirror we want to find.
|
||||
// indexAlongEdge is the index of the vertex along the edge. ( 0 or 1 only )
|
||||
{
|
||||
HalfEdge* pMirrorEdge = pHalfEdge->twin;
|
||||
|
||||
unsigned short vertexID = (unsigned short)-1;
|
||||
if ( pMirrorEdge )
|
||||
{
|
||||
vertexID = pMirrorEdge->patch->vtxIDs[ ( pMirrorEdge->localID + indexAlongEdge ) % 4 ] ;
|
||||
}
|
||||
|
||||
if ( ppOutMirrorEdge )
|
||||
{
|
||||
*ppOutMirrorEdge = pMirrorEdge;
|
||||
}
|
||||
|
||||
return vertexID;
|
||||
}
|
||||
|
||||
// Computes the neighboring texcoords ( Interior, EdgeV, EdgeU, Corner ) for each vertex
|
||||
// texcoords are computed in such a way that every shared edge or corner computes the same values
|
||||
// this is used as a tie-breaking scheme for creating consistent texture sampling for displacement maps
|
||||
void COptimizeSubDBuilder::ComputeNeighborTexcoords( SubD_Face_t *baseQuad )
|
||||
{
|
||||
unsigned short p = baseQuad->patchID;
|
||||
unsigned short invalidNeighborValue = (unsigned short)-1;
|
||||
|
||||
// Loop over all 4 verts of the quad
|
||||
for ( int i=0; i<4; ++i )
|
||||
{
|
||||
unsigned short index = baseQuad->vtxIDs[i];
|
||||
|
||||
// Interior point is alway the current corner
|
||||
baseQuad->vUV0[i] = VTXIDX( index );
|
||||
// Assert( index == baseQuad->vUV0[i] );
|
||||
|
||||
// Default to original texcoord values for 1 and 2
|
||||
baseQuad->vUV1[i] = baseQuad->vUV0[i];
|
||||
baseQuad->vUV2[i] = baseQuad->vUV0[i];
|
||||
|
||||
// Find the texture coordinates of our neighbors
|
||||
// Only keep the texture coordinates of the neighbor with the greatest quad index
|
||||
HalfEdge* pMirrorEdgeV = NULL;
|
||||
|
||||
// V edge ( store the UVs of the patch with the greatest ID )
|
||||
unsigned short iNeighborPatchV = invalidNeighborValue;
|
||||
unsigned short iNeighborV = FindNeighborVertex( &pMirrorEdgeV, &baseQuad->halfEdges[ i ], 1 );
|
||||
if ( iNeighborV != invalidNeighborValue ) // hard edge test
|
||||
{
|
||||
iNeighborPatchV = pMirrorEdgeV->patch->patchID;
|
||||
if ( iNeighborPatchV > p )
|
||||
{
|
||||
baseQuad->vUV1[i] = VTXIDX( iNeighborV );
|
||||
}
|
||||
}
|
||||
|
||||
HalfEdge* pMirrorEdgeU = NULL;
|
||||
|
||||
// U edge ( store the UVs of the patch with the greatest ID )
|
||||
unsigned short iNeighborPatchU = invalidNeighborValue;
|
||||
unsigned short iNeighborU = FindNeighborVertex( &pMirrorEdgeU, &baseQuad->halfEdges[ (i+3)%4 ], 0 );
|
||||
if ( iNeighborU != invalidNeighborValue ) // hard edge test
|
||||
{
|
||||
iNeighborPatchU = pMirrorEdgeU->patch->patchID;
|
||||
if ( iNeighborPatchU > p )
|
||||
{
|
||||
baseQuad->vUV2[i] = VTXIDX( iNeighborU );
|
||||
}
|
||||
}
|
||||
|
||||
// Corner ( store the UVs of the patch with the greatest ID ).
|
||||
// Walk from NeighborV to NeighborU and store data for the largest patch ID.
|
||||
// We may redundantly check NeighborPatchU here if this is a valence 3 vertex.
|
||||
HalfEdge* pMirrorEdgeCorner = pMirrorEdgeV;
|
||||
unsigned short iNeighborPatch = invalidNeighborValue;
|
||||
unsigned short iMaxNeighborCorner = index;
|
||||
unsigned short iMaxPatch = baseQuad->patchID;
|
||||
if ( pMirrorEdgeCorner )
|
||||
{
|
||||
do
|
||||
{
|
||||
HalfEdge* pNextEdge = pMirrorEdgeCorner->NextInFace();
|
||||
unsigned short iNeighborCorner = FindNeighborVertex( &pMirrorEdgeCorner, pNextEdge, 1 );
|
||||
if ( iNeighborCorner != invalidNeighborValue ) // hard edge test
|
||||
{
|
||||
iNeighborPatch = pMirrorEdgeCorner->patch->patchID;
|
||||
if ( pMirrorEdgeCorner->patch->patchID > iMaxPatch )
|
||||
{
|
||||
iMaxPatch = pMirrorEdgeCorner->patch->patchID;
|
||||
iMaxNeighborCorner = iNeighborCorner;
|
||||
}
|
||||
}
|
||||
} while( iNeighborPatch != iNeighborPatchU && pMirrorEdgeCorner );
|
||||
}
|
||||
|
||||
// Determine whether We still need to check against U and V adjacent patches
|
||||
if ( pMirrorEdgeU && ( pMirrorEdgeU->patch->patchID > iMaxPatch ) )
|
||||
{
|
||||
iMaxPatch = pMirrorEdgeU->patch->patchID;
|
||||
iMaxNeighborCorner = iNeighborU;
|
||||
}
|
||||
|
||||
if ( pMirrorEdgeV && ( pMirrorEdgeV->patch->patchID > iMaxPatch ) )
|
||||
{
|
||||
iMaxPatch = pMirrorEdgeV->patch->patchID;
|
||||
iMaxNeighborCorner = iNeighborV;
|
||||
}
|
||||
|
||||
baseQuad->vUV3[i] = VTXIDX( iMaxNeighborCorner );
|
||||
}
|
||||
}
|
||||
|
||||
void DumpPatchLite( SubD_Face_t *patch )
|
||||
{
|
||||
Msg( "Patch: %d\n", patch->patchID );
|
||||
Msg( " vtxIDs: %d %d %d %d\n", patch->vtxIDs[0], patch->vtxIDs[1], patch->vtxIDs[2], patch->vtxIDs[3] );
|
||||
Msg( " halfEdges.twin: %d/%d %d/%d %d/%d %d/%d\n",
|
||||
patch->halfEdges[0].twin ? patch->halfEdges[0].twin->patch->patchID : -1, patch->halfEdges[0].twin ? patch->halfEdges[0].twin->localID: -1,
|
||||
patch->halfEdges[1].twin ? patch->halfEdges[1].twin->patch->patchID : -1, patch->halfEdges[1].twin ? patch->halfEdges[1].twin->localID: -1,
|
||||
patch->halfEdges[2].twin ? patch->halfEdges[2].twin->patch->patchID : -1, patch->halfEdges[2].twin ? patch->halfEdges[2].twin->localID: -1,
|
||||
patch->halfEdges[3].twin ? patch->halfEdges[3].twin->patch->patchID : -1, patch->halfEdges[3].twin ? patch->halfEdges[3].twin->localID: -1 );
|
||||
Msg( " halfEdges.sectorStart: %d/%d %d/%d %d/%d %d/%d\n",
|
||||
patch->halfEdges[0].sectorStart ? patch->halfEdges[0].sectorStart->patch->patchID : -1, patch->halfEdges[0].sectorStart ? patch->halfEdges[0].sectorStart->localID: -1,
|
||||
patch->halfEdges[1].sectorStart ? patch->halfEdges[1].sectorStart->patch->patchID : -1, patch->halfEdges[1].sectorStart ? patch->halfEdges[1].sectorStart->localID: -1,
|
||||
patch->halfEdges[2].sectorStart ? patch->halfEdges[2].sectorStart->patch->patchID : -1, patch->halfEdges[2].sectorStart ? patch->halfEdges[2].sectorStart->localID: -1,
|
||||
patch->halfEdges[3].sectorStart ? patch->halfEdges[3].sectorStart->patch->patchID : -1, patch->halfEdges[3].sectorStart ? patch->halfEdges[3].sectorStart->localID: -1 );
|
||||
}
|
||||
|
||||
// Rotate a particular face one step (element N grabs from element N-1)
|
||||
void COptimizeSubDBuilder::RotateOnce( SubD_Face_t *pPatch )
|
||||
{
|
||||
// Msg( "- Before ------------------------------------------------------------------------------------------\n" );
|
||||
// DumpPatchLite( pPatch );
|
||||
|
||||
SubD_Face_t tmpFace;
|
||||
memcpy( &tmpFace, pPatch, sizeof( SubD_Face_t ) );
|
||||
|
||||
HalfEdge *pTwins[4] = { NULL, NULL, NULL, NULL };
|
||||
for ( int i=0; i<4; i++ )
|
||||
{
|
||||
pTwins[i] = pPatch->halfEdges[i].twin; // Point to each HalfEdge's twin
|
||||
if ( pTwins[i] )
|
||||
{
|
||||
Assert( pTwins[i]->twin == &(pPatch->halfEdges[i]) ); // ith twin should be pointing back to ith HalfEdge
|
||||
}
|
||||
}
|
||||
|
||||
for ( int i=0; i<4; i++ )
|
||||
{
|
||||
pPatch->vtxIDs[i] = tmpFace.vtxIDs[(i+3)%4]; // Grab from n-1
|
||||
pPatch->bndEdge[i] = tmpFace.bndEdge[(i+3)%4];
|
||||
pPatch->bndVtx[i] = tmpFace.bndVtx[(i+3)%4];
|
||||
|
||||
memcpy( &(pPatch->halfEdges[i]), &(tmpFace.halfEdges[(i+3)%4]), sizeof(HalfEdge) );
|
||||
pPatch->halfEdges[i].localID = i;
|
||||
pPatch->halfEdges[i].sectorStart = &pPatch->halfEdges[i];
|
||||
}
|
||||
|
||||
for ( int i=0; i<4; i++ )
|
||||
{
|
||||
if ( pTwins[i] )
|
||||
{
|
||||
pTwins[i]->twin = &(pPatch->halfEdges[(i+1)%4]); // Record twin's twin after we've rotated the local patch data
|
||||
}
|
||||
}
|
||||
|
||||
// Msg( "- After ------------------------------------------------------------------------------------------\n" );
|
||||
// DumpPatchLite( pPatch );
|
||||
// Msg( "---------------------------------------------------------------------------------------------------\n" );
|
||||
// Msg( "---------------------------------------------------------------------------------------------------\n\n" );
|
||||
}
|
||||
|
||||
void COptimizeSubDBuilder::RotateFace( SubD_Face_t *pPatch, int nTimesToRotate )
|
||||
{
|
||||
for ( int i=0; i<nTimesToRotate; i++ )
|
||||
{
|
||||
RotateOnce( pPatch );
|
||||
}
|
||||
}
|
||||
|
||||
int COptimizeSubDBuilder::FaceEdgeIndex( SubD_Face_t *pFace, HalfEdge *pEdge )
|
||||
{
|
||||
int i = 0;
|
||||
while ( &(pFace->halfEdges[i]) != pEdge )
|
||||
{
|
||||
i++;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
void COptimizeSubDBuilder::Propagate( CUtlVector<Orientation> & orientationArray, HalfEdge *pEdge, bool dir )
|
||||
{
|
||||
Assert( pEdge );
|
||||
|
||||
while( true )
|
||||
{
|
||||
HalfEdge *pNeighborEdge = pEdge->twin;
|
||||
if ( !pNeighborEdge )
|
||||
break; // Stop at mesh boundaries.
|
||||
|
||||
SubD_Face_t *pFace = pNeighborEdge->patch;
|
||||
if ( !pFace )
|
||||
break; // Stop at mesh boundaries.
|
||||
|
||||
int nEdgeIndex = FaceEdgeIndex( pFace, pNeighborEdge );
|
||||
|
||||
Orientation & faceOrientation = orientationArray[pFace->patchID];
|
||||
|
||||
if ( nEdgeIndex == 1 || nEdgeIndex == 3 )
|
||||
{
|
||||
if ( faceOrientation.uSet )
|
||||
{
|
||||
Assert( faceOrientation.u == ( ( nEdgeIndex == 1 ) ^ dir ) );
|
||||
break;
|
||||
}
|
||||
faceOrientation.SetU( ( nEdgeIndex == 1 ) ^ dir );
|
||||
}
|
||||
else // if ( nEdgeIndex == 0 || nEdgeIndex == 2 )
|
||||
{
|
||||
if ( faceOrientation.vSet )
|
||||
{
|
||||
Assert( faceOrientation.v == ( ( nEdgeIndex == 0 ) ^ dir ) );
|
||||
break;
|
||||
}
|
||||
faceOrientation.SetV( ( nEdgeIndex == 0 ) ^ dir );
|
||||
}
|
||||
|
||||
pEdge = pNeighborEdge->NextInFace()->NextInFace();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static HalfEdge *FaceEdge( SubD_Face_t *pPatch, int idx )
|
||||
{
|
||||
int i = 0;
|
||||
HalfEdge *pEdge = &pPatch->halfEdges[0];
|
||||
|
||||
while ( i != idx )
|
||||
{
|
||||
i++;
|
||||
pEdge = pEdge->NextInFace();
|
||||
}
|
||||
|
||||
return pEdge;
|
||||
}
|
||||
|
||||
// Reorient faces in order to avoid parametric discontinuities.
|
||||
void COptimizeSubDBuilder::ConsistentPatchOrientation()
|
||||
{
|
||||
CUtlVector<Orientation> orientationArray;
|
||||
orientationArray.AddMultipleToTail( m_numPatches );
|
||||
|
||||
for( int f = 0; f < m_numPatches; f++ )
|
||||
{
|
||||
SubD_Face_t *pPatch = &m_faceList[f];
|
||||
HalfEdge *pEdges = &pPatch->halfEdges[0];
|
||||
|
||||
if ( !orientationArray[f].uSet )
|
||||
{
|
||||
orientationArray[f].SetU( false );
|
||||
Propagate( orientationArray, pEdges+1, false );
|
||||
Propagate( orientationArray, pEdges+3, true );
|
||||
}
|
||||
|
||||
if ( !orientationArray[f].vSet )
|
||||
{
|
||||
orientationArray[f].SetV( false );
|
||||
Propagate( orientationArray, pEdges+0, false );
|
||||
Propagate( orientationArray, pEdges+2, true );
|
||||
}
|
||||
}
|
||||
|
||||
for( int f = 0; f < m_numPatches; f++ )
|
||||
{
|
||||
SubD_Face_t *pPatch = &m_faceList[f];
|
||||
|
||||
const Orientation &o = orientationArray[f]; // Determine edge from orientation flags.
|
||||
static const int nTimesToRotate[4] = {0, 1, 3, 2};
|
||||
const int idx = nTimesToRotate[(o.v << 1) + o.u];
|
||||
|
||||
RotateFace( pPatch, idx );
|
||||
}
|
||||
}
|
||||
|
||||
void COptimizeSubDBuilder::TagCreases()
|
||||
{
|
||||
static int MOD4[] = {0,1,2,3,0,1,2,3};
|
||||
|
||||
for (unsigned short i=0; i<m_numPatches; i++)
|
||||
{
|
||||
SubD_Face_t *pPatch = &m_faceList[i];
|
||||
|
||||
for ( int k=0; k<4; k++ ) // for all vertices
|
||||
{
|
||||
if ( pPatch->halfEdges[k].twin != NULL )
|
||||
{
|
||||
HalfEdge *twin = pPatch->halfEdges[k].twin;
|
||||
SubD_Face_t *nbQuad = twin->patch;
|
||||
int quad0vtx0ID = pPatch->vtxIDs[ MOD4[k+0] ];
|
||||
int quad1vtx0ID = nbQuad->vtxIDs[ MOD4[twin->localID+1] ];
|
||||
|
||||
int quad0vtx1ID = pPatch->vtxIDs[ MOD4[k+1] ];
|
||||
int quad1vtx1ID = nbQuad->vtxIDs[ MOD4[twin->localID+0] ];
|
||||
|
||||
if ( ( VTXNOR( quad0vtx0ID ) != VTXNOR( quad1vtx0ID ) ) ||
|
||||
( VTXNOR( quad0vtx1ID ) != VTXNOR( quad1vtx1ID ) ) )
|
||||
{
|
||||
pPatch->bndEdge[k] = true;
|
||||
pPatch->bndVtx[MOD4[k+0]] = true;
|
||||
pPatch->bndVtx[MOD4[k+1]] = true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}; // namespace
|
||||
171
utils/studiomdl/optimize_subd.h
Normal file
171
utils/studiomdl/optimize_subd.h
Normal file
@@ -0,0 +1,171 @@
|
||||
#ifndef OPTIMIZE_SUBD_H
|
||||
#define OPTIMIZE_SUBD_H
|
||||
#pragma once
|
||||
|
||||
#include "optimize.h"
|
||||
#include "studio.h"
|
||||
|
||||
// Maximum number of points that can be part of a subd quad.
|
||||
// This includes the 4 interior points of the quad, plus the 1-ring neighborhood
|
||||
#define MAX_SUBD_POINTS 32
|
||||
|
||||
#define MAX_SUBD_ONERING_POINTS (MAX_SUBD_POINTS + 4*5)
|
||||
|
||||
#define CORNER_WITH_SMOOTHBNDTANGENTS 2
|
||||
|
||||
namespace OptimizedModel
|
||||
{
|
||||
|
||||
struct SubD_Face_t;
|
||||
|
||||
// minimal HalfEdge structure, embedded in a face (#halfedges = #vertexperface)
|
||||
struct HalfEdge
|
||||
{
|
||||
HalfEdge *twin;
|
||||
HalfEdge *sectorStart;
|
||||
unsigned char localID; // local halfedge/vertex ID
|
||||
SubD_Face_t *patch;
|
||||
|
||||
inline HalfEdge *NextInFace();
|
||||
inline HalfEdge *PrevInFace();
|
||||
inline HalfEdge *NextByHead();
|
||||
inline HalfEdge *PrevByHead();
|
||||
inline HalfEdge *NextByTail();
|
||||
inline HalfEdge *PrevByTail();
|
||||
|
||||
inline unsigned short &BndEdge();
|
||||
};
|
||||
|
||||
struct Orientation
|
||||
{
|
||||
uint8 u : 1;
|
||||
uint8 v : 1;
|
||||
uint8 uSet : 1;
|
||||
uint8 vSet : 1;
|
||||
|
||||
void SetU( bool b )
|
||||
{
|
||||
Assert( !uSet );
|
||||
u = b;
|
||||
uSet = true;
|
||||
}
|
||||
|
||||
void SetV( bool b )
|
||||
{
|
||||
Assert( !vSet );
|
||||
v = b;
|
||||
vSet = true;
|
||||
}
|
||||
|
||||
Orientation() { uSet = vSet = false; }
|
||||
};
|
||||
|
||||
struct SubD_Face_t
|
||||
{
|
||||
unsigned short patchID; // for building our 4 sets of watertight UVs
|
||||
unsigned short vtxIDs[4];
|
||||
|
||||
unsigned short oneRing[MAX_SUBD_ONERING_POINTS];
|
||||
unsigned short vtx1RingSize[4]; // Pre-calculated prefixes for the first 4 points
|
||||
unsigned short vtx1RingCenterQuadOffset[4]; // start of inner quad vertices in vertex 1-ring
|
||||
|
||||
unsigned short valences[4]; // Valences for the first 4 points in current sector
|
||||
unsigned short minOneRingIndex[4]; // Location in oneRing array to start applying stencil (determined by lowest position index)
|
||||
|
||||
unsigned short bndVtx[4]; // is vertex on the boundary?
|
||||
unsigned short bndEdge[4]; // is associated edge on the boundary?
|
||||
unsigned short cornerVtx[4]; // should a boundary-vertex be treated as a corner?
|
||||
|
||||
unsigned short nbCornerVtx[4]; // bitfield, for all on-edge neighbors record if corner vertices
|
||||
|
||||
unsigned short loopGapAngle[4];
|
||||
|
||||
unsigned short edgeBias[8];
|
||||
|
||||
unsigned short vUV0[4]; // Vert index for Interior TexCoord (for vtxIDs[0-3])
|
||||
unsigned short vUV1[4]; // Vert index for Parametric V TexCoord (for vtxIDs[0-3])
|
||||
unsigned short vUV2[4]; // Vert index for Parametric U TexCoord (for vtxIDs[0-3])
|
||||
unsigned short vUV3[4]; // Vert index for Corner TexCoord (for vtxIDs[0-3])
|
||||
|
||||
HalfEdge halfEdges[4];
|
||||
|
||||
void SetEdgeBias(int localID, float f0, float f1)
|
||||
{
|
||||
if (halfEdges[localID].twin==NULL) return;
|
||||
edgeBias[2*localID] = f0 * 32768.0f;
|
||||
edgeBias[2*localID+1] = f1 * 32768.0f;
|
||||
halfEdges[localID].twin->patch->edgeBias[ 2*halfEdges[localID].twin->localID+1 ] = (1.0f - f0) * 32768.0f;
|
||||
halfEdges[localID].twin->patch->edgeBias[ 2*halfEdges[localID].twin->localID ] = (1.0f - f1) * 32768.0f;
|
||||
}
|
||||
};
|
||||
|
||||
inline HalfEdge *HalfEdge::NextInFace()
|
||||
{
|
||||
static int MOD4[8] = {0,1,2,3,0,1,2,3};
|
||||
return &patch->halfEdges[MOD4[localID+1]];
|
||||
}
|
||||
inline HalfEdge *HalfEdge::PrevInFace()
|
||||
{
|
||||
static int MOD4[8] = {0,1,2,3,0,1,2,3};
|
||||
return &patch->halfEdges[MOD4[localID+3]];
|
||||
}
|
||||
inline HalfEdge *HalfEdge::NextByHead() { return (twin==NULL)? NULL : twin->PrevInFace(); }
|
||||
inline HalfEdge *HalfEdge::PrevByHead() { return NextInFace()->twin; }
|
||||
inline HalfEdge *HalfEdge::NextByTail() { return PrevInFace()->twin; }
|
||||
inline HalfEdge *HalfEdge::PrevByTail() { return (twin==NULL)? NULL : twin->NextInFace(); }
|
||||
|
||||
inline bool FaceIsRegular( SubD_Face_t *patch )
|
||||
{
|
||||
return ( patch->valences[0] == 4 && patch->valences[1] == 4 && patch->valences[2] == 4 && patch->valences[3] == 4 ) &&
|
||||
( patch->bndVtx[0] == false && patch->bndVtx[1] == false && patch->bndVtx[2] == false && patch->bndVtx[3] == false ) &&
|
||||
( patch->bndEdge[0] == false && patch->bndEdge[1] == false && patch->bndEdge[2] == false && patch->bndEdge[3] == false );
|
||||
}
|
||||
|
||||
inline unsigned short &HalfEdge::BndEdge() { return patch->bndEdge[localID]; }
|
||||
|
||||
typedef CUtlVector<SubD_Face_t> SubD_FaceList_t;
|
||||
typedef CUtlVector<Vertex_t> SubD_VertexList_t;
|
||||
typedef const mstudio_meshvertexdata_t *SubD_VertexData_t;
|
||||
|
||||
class COptimizeSubDBuilder
|
||||
{
|
||||
public:
|
||||
COptimizeSubDBuilder(SubD_FaceList_t& subDFaceList, const SubD_VertexList_t& vertexList, const SubD_VertexData_t &vertexData, bool bIsTagged, bool bMendVertices=true );
|
||||
void ProcessPatches( bool bIsTagged, bool bMendVertices );
|
||||
|
||||
HalfEdge *FindTwin(HalfEdge &he);
|
||||
void CheckForManifoldMesh( );
|
||||
void BuildNeighborhoodInfo( );
|
||||
|
||||
void ComputeSectorStart( SubD_Face_t *quad, unsigned short k );
|
||||
void ComputePerVertexInfo( SubD_Face_t *baseQuad, unsigned short baseLocalID );
|
||||
void ComputeSectorAngle( SubD_Face_t *baseQuad, unsigned short baseLocalID );
|
||||
void ComputeNbCorners( SubD_Face_t *baseQuad, unsigned short baseLocalID );
|
||||
void ComputeSectorOneRing( SubD_Face_t *baseQuad, unsigned short baseLocalID );
|
||||
unsigned short FindNeighborVertex( HalfEdge** ppOutMirrorEdge, const HalfEdge *pHalfEdge, int indexAlongEdge );
|
||||
void ComputeNeighborTexcoords( SubD_Face_t *baseQuad );
|
||||
void MendVertices( SubD_Face_t *quad, unsigned short baseLocalID );
|
||||
void TagCreases();
|
||||
|
||||
private:
|
||||
|
||||
// Routines used for orienting faces for edge consistency
|
||||
void RotateOnce( SubD_Face_t *pFace );
|
||||
void RotateFace( SubD_Face_t *pFace, int nTimesToRotate );
|
||||
int FaceEdgeIndex( SubD_Face_t *pFace, HalfEdge *pEdge );
|
||||
void Propagate( CUtlVector<Orientation> & orientationArray, HalfEdge *pEdge, bool dir );
|
||||
void ConsistentPatchOrientation();
|
||||
void RemapIndices();
|
||||
void SetMinOneRingIndices();
|
||||
|
||||
SubD_FaceList_t &m_faceList;
|
||||
const SubD_VertexList_t &m_vtxList;
|
||||
const SubD_VertexData_t &m_vtxData;
|
||||
int m_numPatches;
|
||||
CUtlVector<int> m_IndexRemapTable;
|
||||
};
|
||||
|
||||
|
||||
}; // namespace OptimizedModel
|
||||
|
||||
#endif // OPTIMIZE_SUBD_H
|
||||
260
utils/studiomdl/perfstats.cpp
Normal file
260
utils/studiomdl/perfstats.cpp
Normal file
@@ -0,0 +1,260 @@
|
||||
//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//===========================================================================//
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <tier0/dbg.h>
|
||||
#include "interface.h"
|
||||
#include "istudiorender.h"
|
||||
#include "studio.h"
|
||||
#include "optimize.h"
|
||||
#include "cmdlib.h"
|
||||
#include "studiomdl.h"
|
||||
#include "perfstats.h"
|
||||
#include "tier1/tier1_logging.h"
|
||||
|
||||
extern void MdlError( char const *pMsg, ... );
|
||||
|
||||
static StudioRenderConfig_t s_StudioRenderConfig;
|
||||
|
||||
class CStudioDataCache : public CBaseAppSystem<IStudioDataCache>
|
||||
{
|
||||
public:
|
||||
bool VerifyHeaders( studiohdr_t *pStudioHdr );
|
||||
vertexFileHeader_t *CacheVertexData( studiohdr_t *pStudioHdr );
|
||||
};
|
||||
|
||||
static CStudioDataCache g_StudioDataCache;
|
||||
EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CStudioDataCache, IStudioDataCache, STUDIO_DATA_CACHE_INTERFACE_VERSION, g_StudioDataCache );
|
||||
|
||||
|
||||
/*
|
||||
=================
|
||||
VerifyHeaders
|
||||
|
||||
Minimal presence and header validation, no data loads
|
||||
Return true if successful, false otherwise.
|
||||
=================
|
||||
*/
|
||||
bool CStudioDataCache::VerifyHeaders( studiohdr_t *pStudioHdr )
|
||||
{
|
||||
// default valid
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
CacheVertexData
|
||||
|
||||
Cache model's specified dynamic data
|
||||
=================
|
||||
*/
|
||||
vertexFileHeader_t *CStudioDataCache::CacheVertexData( studiohdr_t *pStudioHdr )
|
||||
{
|
||||
// minimal implementation - return persisted data
|
||||
return (vertexFileHeader_t*)pStudioHdr->VertexBase();
|
||||
}
|
||||
|
||||
static void UpdateStudioRenderConfig( void )
|
||||
{
|
||||
memset( &s_StudioRenderConfig, 0, sizeof(s_StudioRenderConfig) );
|
||||
|
||||
s_StudioRenderConfig.bEyeMove = true;
|
||||
s_StudioRenderConfig.fEyeShiftX = 0.0f;
|
||||
s_StudioRenderConfig.fEyeShiftY = 0.0f;
|
||||
s_StudioRenderConfig.fEyeShiftZ = 0.0f;
|
||||
s_StudioRenderConfig.fEyeSize = 10.0f;
|
||||
s_StudioRenderConfig.bSoftwareSkin = false;
|
||||
s_StudioRenderConfig.bNoHardware = false;
|
||||
s_StudioRenderConfig.bNoSoftware = false;
|
||||
s_StudioRenderConfig.bTeeth = true;
|
||||
s_StudioRenderConfig.drawEntities = true;
|
||||
s_StudioRenderConfig.bFlex = true;
|
||||
s_StudioRenderConfig.bEyes = true;
|
||||
s_StudioRenderConfig.bWireframe = false;
|
||||
s_StudioRenderConfig.bDrawZBufferedWireframe = false;
|
||||
s_StudioRenderConfig.bDrawNormals = false;
|
||||
s_StudioRenderConfig.skin = 0;
|
||||
s_StudioRenderConfig.maxDecalsPerModel = 0;
|
||||
s_StudioRenderConfig.bWireframeDecals = false;
|
||||
s_StudioRenderConfig.fullbright = false;
|
||||
s_StudioRenderConfig.bSoftwareLighting = false;
|
||||
s_StudioRenderConfig.bShowEnvCubemapOnly = false;
|
||||
g_pStudioRender->UpdateConfig( s_StudioRenderConfig );
|
||||
}
|
||||
|
||||
static CBufferedLoggingListener s_BufferedLoggingListener;
|
||||
|
||||
void SpewPerfStats( studiohdr_t *pStudioHdr, const char *pFilename, unsigned int flags )
|
||||
{
|
||||
char fileName[260];
|
||||
vertexFileHeader_t *pNewVvdHdr;
|
||||
vertexFileHeader_t *pVvdHdr = 0;
|
||||
OptimizedModel::FileHeader_t *pVtxHdr = 0;
|
||||
studiohwdata_t studioHWData;
|
||||
int vvdSize = 0;
|
||||
|
||||
const char *prefix[] = { ".dx90.vtx", ".dx80.vtx", ".sw.vtx" };
|
||||
const int numVtxFiles = ( g_gameinfo.bSupportsDX8 && !g_bFastBuild ) ? ARRAYSIZE( prefix ) : 1;
|
||||
bool bExtraData = (pStudioHdr->flags & STUDIOHDR_FLAGS_EXTRA_VERTEX_DATA) != 0;
|
||||
|
||||
|
||||
if( !( flags & SPEWPERFSTATS_SHOWSTUDIORENDERWARNINGS ) )
|
||||
{
|
||||
LoggingSystem_PushLoggingState();
|
||||
LoggingSystem_RegisterLoggingListener( &s_BufferedLoggingListener );
|
||||
}
|
||||
|
||||
// no stats on these
|
||||
if (!pStudioHdr->numbodyparts)
|
||||
return;
|
||||
|
||||
// Need to update the render config to spew perf stats.
|
||||
UpdateStudioRenderConfig();
|
||||
|
||||
// persist the vvd data
|
||||
Q_StripExtension( pFilename, fileName, sizeof( fileName ) );
|
||||
strcat( fileName, ".vvd" );
|
||||
|
||||
if (FileExists( fileName ))
|
||||
{
|
||||
vvdSize = LoadFile( fileName, (void**)&pVvdHdr );
|
||||
}
|
||||
else
|
||||
{
|
||||
MdlError( "Could not open '%s'\n", fileName );
|
||||
}
|
||||
|
||||
// validate header
|
||||
if (pVvdHdr->id != MODEL_VERTEX_FILE_ID)
|
||||
{
|
||||
MdlError( "Bad id for '%s' (got %d expected %d)\n", fileName, pVvdHdr->id, MODEL_VERTEX_FILE_ID);
|
||||
}
|
||||
if (pVvdHdr->version != MODEL_VERTEX_FILE_VERSION)
|
||||
{
|
||||
MdlError( "Bad version for '%s' (got %d expected %d)\n", fileName, pVvdHdr->version, MODEL_VERTEX_FILE_VERSION);
|
||||
}
|
||||
if (pVvdHdr->checksum != pStudioHdr->checksum)
|
||||
{
|
||||
MdlError( "Bad checksum for '%s' (got %d expected %d)\n", fileName, pVvdHdr->checksum, pStudioHdr->checksum);
|
||||
}
|
||||
|
||||
if (pVvdHdr->numFixups)
|
||||
{
|
||||
// need to perform mesh relocation fixups
|
||||
// allocate a new copy
|
||||
pNewVvdHdr = (vertexFileHeader_t *)malloc( vvdSize );
|
||||
if (!pNewVvdHdr)
|
||||
{
|
||||
MdlError( "Error allocating %d bytes for Vertex File '%s'\n", vvdSize, fileName );
|
||||
}
|
||||
|
||||
Studio_LoadVertexes( pVvdHdr, pNewVvdHdr, 0, true, bExtraData );
|
||||
|
||||
// discard original
|
||||
free( pVvdHdr );
|
||||
|
||||
pVvdHdr = pNewVvdHdr;
|
||||
}
|
||||
|
||||
// iterate all ???.vtx files
|
||||
for (int j = 0; j< numVtxFiles; j++)
|
||||
{
|
||||
// make vtx filename
|
||||
Q_StripExtension( pFilename, fileName, sizeof( fileName ) );
|
||||
strcat( fileName, prefix[j] );
|
||||
|
||||
// persist the vtx data
|
||||
if (FileExists(fileName))
|
||||
{
|
||||
LoadFile( fileName, (void**)&pVtxHdr );
|
||||
}
|
||||
else
|
||||
{
|
||||
MdlError( "Could not open '%s'\n", fileName );
|
||||
}
|
||||
|
||||
// validate header
|
||||
if (pVtxHdr->version != OPTIMIZED_MODEL_FILE_VERSION)
|
||||
{
|
||||
MdlError( "Bad version for '%s' (got %d expected %d)\n", fileName, pVtxHdr->version, OPTIMIZED_MODEL_FILE_VERSION );
|
||||
}
|
||||
if (pVtxHdr->checkSum != pStudioHdr->checksum)
|
||||
{
|
||||
MdlError( "Bad checksum for '%s' (got %d expected %d)\n", fileName, pVtxHdr->checkSum, pStudioHdr->checksum );
|
||||
}
|
||||
|
||||
// studio render will request these through cache interface
|
||||
pStudioHdr->SetVertexBase( (void *)pVvdHdr );
|
||||
pStudioHdr->SetIndexBase( (void *)pVtxHdr );
|
||||
|
||||
g_pStudioRender->LoadModel( pStudioHdr, pVtxHdr, &studioHWData );
|
||||
|
||||
if( flags & SPEWPERFSTATS_SHOWPERF )
|
||||
{
|
||||
if( flags & SPEWPERFSTATS_SPREADSHEET )
|
||||
{
|
||||
printf( "%s,%s,%d,", fileName, prefix[j], studioHWData.m_NumLODs - studioHWData.m_RootLOD );
|
||||
}
|
||||
else
|
||||
{
|
||||
printf( "\n" );
|
||||
printf( "Performance Stats: %s\n", fileName );
|
||||
printf( "------------------\n" );
|
||||
}
|
||||
}
|
||||
|
||||
int i;
|
||||
if( flags & SPEWPERFSTATS_SHOWPERF )
|
||||
{
|
||||
for( i = studioHWData.m_RootLOD; i < studioHWData.m_NumLODs; i++ )
|
||||
{
|
||||
DrawModelInfo_t drawModelInfo;
|
||||
drawModelInfo.m_Skin = 0;
|
||||
drawModelInfo.m_Body = 0;
|
||||
drawModelInfo.m_HitboxSet = 0;
|
||||
drawModelInfo.m_pClientEntity = 0;
|
||||
drawModelInfo.m_pColorMeshes = 0;
|
||||
drawModelInfo.m_pStudioHdr = pStudioHdr;
|
||||
drawModelInfo.m_pHardwareData = &studioHWData;
|
||||
CUtlBuffer statsOutput( 0, 0, CUtlBuffer::TEXT_BUFFER );
|
||||
if( !( flags & SPEWPERFSTATS_SPREADSHEET ) )
|
||||
{
|
||||
printf( "LOD:%d\n", i );
|
||||
}
|
||||
drawModelInfo.m_Lod = i;
|
||||
|
||||
DrawModelResults_t results;
|
||||
g_pStudioRender->GetPerfStats( &results, drawModelInfo, &statsOutput );
|
||||
if( flags & SPEWPERFSTATS_SPREADSHEET )
|
||||
{
|
||||
printf( "%d,%d,%d,", results.m_ActualTriCount, results.m_NumBatches, results.m_NumMaterials );
|
||||
}
|
||||
else
|
||||
{
|
||||
printf( " actual tris:%d\n", ( int )results.m_ActualTriCount );
|
||||
printf( " texture memory bytes: %d (only valid in a rendering app)\n", ( int )results.m_TextureMemoryBytes );
|
||||
printf( ( char * )statsOutput.Base() );
|
||||
}
|
||||
}
|
||||
if( flags & SPEWPERFSTATS_SPREADSHEET )
|
||||
{
|
||||
printf( "\n" );
|
||||
}
|
||||
}
|
||||
g_pStudioRender->UnloadModel( &studioHWData );
|
||||
free(pVtxHdr);
|
||||
}
|
||||
|
||||
if (pVvdHdr)
|
||||
free(pVvdHdr);
|
||||
|
||||
if( !( flags & SPEWPERFSTATS_SHOWSTUDIORENDERWARNINGS ) )
|
||||
{
|
||||
LoggingSystem_PopLoggingState();
|
||||
s_BufferedLoggingListener.EmitBufferedSpew();
|
||||
}
|
||||
}
|
||||
|
||||
25
utils/studiomdl/perfstats.h
Normal file
25
utils/studiomdl/perfstats.h
Normal file
@@ -0,0 +1,25 @@
|
||||
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef PERFSTATS_H
|
||||
#define PERFSTATS_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "studio.h"
|
||||
#include "optimize.h"
|
||||
|
||||
enum
|
||||
{
|
||||
SPEWPERFSTATS_SHOWSTUDIORENDERWARNINGS = 1,
|
||||
SPEWPERFSTATS_SHOWPERF = 2,
|
||||
SPEWPERFSTATS_SPREADSHEET = 4,
|
||||
};
|
||||
|
||||
void SpewPerfStats( studiohdr_t *pStudioHdr, const char *pFilename, unsigned int flags );
|
||||
|
||||
#endif // PERFSTATS_H
|
||||
402
utils/studiomdl/physics2collision.cpp
Normal file
402
utils/studiomdl/physics2collision.cpp
Normal file
@@ -0,0 +1,402 @@
|
||||
//========= Copyright c 1996-2008, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: Builds physics2 collision models from studio model source
|
||||
//
|
||||
// $Workfile: $
|
||||
// $Date: $
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#include <string.h>
|
||||
#include "tier1/tier1.h"
|
||||
#include "tier1/smartptr.h"
|
||||
#include "tier2/p4helpers.h"
|
||||
#include "datalinker.h"
|
||||
#include "vphysics2_interface.h"
|
||||
#include "vphysics2_interface_flags.h"
|
||||
#include "alignedarray.h"
|
||||
#include "studiomdl.h"
|
||||
#include "filesystem_tools.h"
|
||||
#include "collisionmodelsource.h"
|
||||
#include "physics2collision.h"
|
||||
#include "physdll.h"
|
||||
#include "phzfile.h"
|
||||
|
||||
static IPhysics2Cook *g_pCook;
|
||||
|
||||
|
||||
struct bodypart_t
|
||||
{
|
||||
IPhysics2CookedMeshBase* mesh;
|
||||
int bone;
|
||||
bodypart_t(){}
|
||||
bodypart_t(IPhysics2CookedMeshBase* _mesh, int _bone):mesh(_mesh),bone(_bone){}
|
||||
};
|
||||
|
||||
class CPhysics2CollisionBuilder: public CCollisionModelSource
|
||||
{
|
||||
public:
|
||||
void Init(CCollisionModelSource *pSource)
|
||||
{
|
||||
*static_cast<CCollisionModelSource*>(this) = *pSource;
|
||||
}
|
||||
void Shutdown()
|
||||
{
|
||||
Destroy(m_bodyparts);
|
||||
}
|
||||
void Destroy(CUtlVector<bodypart_t>&bodyparts)
|
||||
{
|
||||
for(int i = 0; i < bodyparts.Size(); ++i)
|
||||
g_pCook->Destroy(bodyparts[i].mesh);
|
||||
}
|
||||
|
||||
void Build()
|
||||
{
|
||||
if(m_isJointed)
|
||||
BuildJointed();
|
||||
else
|
||||
BuildRigid();
|
||||
}
|
||||
void BuildRigid();
|
||||
void BuildJointed();
|
||||
void Write();
|
||||
|
||||
void Destroy(CUtlVector<IPhysics2CookedMeshBase*> &arrPolytopes);
|
||||
|
||||
CUtlVector<bodypart_t> m_bodyparts;
|
||||
};
|
||||
|
||||
class CMeshAdaptor:public CPhysics2CustomMeshBase
|
||||
{
|
||||
public:
|
||||
virtual uint GetType()const {return PHYSICS2_SHAPE_TYPE_CUSTOM;}
|
||||
virtual uint NumVertices() const {return m_pMesh->numvertices;}
|
||||
virtual uint NumTriangles() const {return m_pMesh->numfaces;}
|
||||
virtual void GetVertices(float *pVertsOut, uint nByteStride, const fltx4 &factor = Four_Ones)
|
||||
{
|
||||
uint numVerts = m_pMesh->numvertices;
|
||||
byte *pOut = (byte*)pVertsOut;
|
||||
for(uint i =0; i< numVerts; ++i)
|
||||
{
|
||||
fltx4 vert = MulSIMD(LoadUnaligned3SIMD(&m_pVerts[i+m_pMesh->vertexoffset].x), factor);
|
||||
StoreUnaligned3SIMD((float*)pOut, vert);
|
||||
pOut += nByteStride;
|
||||
}
|
||||
}
|
||||
virtual void GetTriangles(int *pTrisOut, uint nByteStride)
|
||||
{
|
||||
uint numTris = m_pMesh->numfaces;
|
||||
byte *pOut = (byte*)pTrisOut;
|
||||
|
||||
for(uint i = 0;i < numTris; ++i)
|
||||
{
|
||||
const s_face_t *pFace = m_pFaces + i + m_pMesh->faceoffset;
|
||||
Assert(pFace->a+m_pMesh->vertexoffset < (uint)m_pMesh->numvertices && pFace->b+m_pMesh->vertexoffset < (uint)m_pMesh->numvertices && pFace->c+m_pMesh->vertexoffset < (uint)m_pMesh->numvertices
|
||||
);
|
||||
((int*)pOut)[0] = pFace->a;
|
||||
((int*)pOut)[1] = pFace->b;
|
||||
((int*)pOut)[2] = pFace->c;
|
||||
pOut += nByteStride;
|
||||
}
|
||||
}
|
||||
virtual uint GetSizeOf()const {return sizeof(*this);}
|
||||
|
||||
const s_face_t *m_pFaces;// non-offset faces
|
||||
const Vector *m_pVerts; // non-offset verts
|
||||
const s_mesh_t *m_pMesh;
|
||||
};
|
||||
|
||||
|
||||
static CPhysics2CollisionBuilder g_builder;
|
||||
|
||||
void Physics2Collision_Build(CCollisionModelSource *pSource)
|
||||
{
|
||||
g_pCook = g_pPhysics2->GetCook();
|
||||
|
||||
g_builder.Init(pSource);
|
||||
g_builder.Build();
|
||||
}
|
||||
|
||||
void Physics2Collision_Write()
|
||||
{
|
||||
g_builder.Write();
|
||||
}
|
||||
|
||||
|
||||
void CPhysics2CollisionBuilder::BuildJointed()
|
||||
{
|
||||
// first, go through all meshes and determine what bones they belong to
|
||||
//CUtlVector<CUtlVector<int> > arrMeshBones(0,m_pModel->nummeshes);
|
||||
// remap it : bone -> faces
|
||||
CUtlVector<CUtlVector<s_face_t> > arrBoneFaces;
|
||||
arrBoneFaces.SetSize(m_pModel->numbones);
|
||||
|
||||
// Constructing elements of the array. This is irritating, there should be a method to do that..
|
||||
for(int i =0; i < m_pModel->numbones; ++i)
|
||||
new(&arrBoneFaces[i])CUtlVector<s_mesh_t *>(32);
|
||||
//for(int i = 0; i < m_pModel->nummeshes; ++i)
|
||||
// new(&arrMeshBones[i])CUtlVector<int>();
|
||||
|
||||
// for each mesh, find bone(s) it belongs to and push it to that bone (those bones)
|
||||
for(int nMesh = 0; nMesh < m_pModel->nummeshes; ++nMesh)
|
||||
{
|
||||
s_mesh_t *pMesh = m_pModel->mesh + m_pModel->meshindex[nMesh];
|
||||
|
||||
for(int nFace = 0; nFace < pMesh->numfaces; ++nFace)
|
||||
{
|
||||
s_face_t face = GetGlobalFace(pMesh, nFace);
|
||||
s_boneweight_t &boneweight = m_pModel->vertex[face.a].boneweight;
|
||||
if(boneweight.numbones)
|
||||
{
|
||||
int boneIndex = RemapBone(boneweight.bone[0]);
|
||||
if(boneIndex >= 0 && boneIndex < m_pModel->numbones)
|
||||
arrBoneFaces[boneIndex].AddToTail(face);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// for each bone, we have 0..many meshes now; compile the meshes; we don't try to share the meshes between different bones here,
|
||||
// the idea is that we'll have rigid binding to skeleton, possibly sometimes multiple meshes to the same bone, but not the same mesh
|
||||
// to multiple bones
|
||||
CUtlVector<Vector> bonespaceVerts;
|
||||
bonespaceVerts.SetCount(m_pModel->numvertices);
|
||||
for(int nBone = 0; nBone < m_pModel->numbones; ++nBone)
|
||||
{
|
||||
CUtlVector<IPhysics2CookedMeshBase*> arrPolytopes;
|
||||
bodypart_t bodypart;
|
||||
bodypart.bone = nBone;
|
||||
bodypart.mesh = NULL;
|
||||
CUtlVector<s_face_t> &arrFaces = arrBoneFaces[nBone];
|
||||
if(ShouldProcessBone(nBone) && arrFaces.Size())
|
||||
{
|
||||
// convert ALL vertices into this bone's frame (it's easier)
|
||||
ConvertToBoneSpace(nBone, bonespaceVerts);
|
||||
// cook one polytope for each s_mesh_t (out of the Mesh interface)
|
||||
s_mesh_t mesh;
|
||||
mesh.faceoffset = 0;
|
||||
mesh.numfaces = arrFaces.Size();
|
||||
mesh.vertexoffset = 0;
|
||||
mesh.numvertices = bonespaceVerts.Size();
|
||||
|
||||
CMeshAdaptor adaptor;
|
||||
adaptor.m_pMesh = &mesh;
|
||||
adaptor.m_pFaces = arrFaces.Base();
|
||||
adaptor.m_pVerts = bonespaceVerts.Base();
|
||||
if(IPhysics2CookedPolytope *pCookedPolytope = g_pCook->CookPolytope(&adaptor))
|
||||
arrPolytopes.AddToTail(pCookedPolytope);
|
||||
}
|
||||
if(arrPolytopes.Size() > 1)
|
||||
{
|
||||
if(m_allowConcaveJoints)
|
||||
{
|
||||
bodypart.mesh = g_pCook->CookMopp(arrPolytopes.Base(), arrPolytopes.Size());
|
||||
}
|
||||
else
|
||||
{
|
||||
bodypart.mesh = g_pCook->CookPolytopeFromMeshes(arrPolytopes.Base(), arrPolytopes.Size());
|
||||
}
|
||||
|
||||
Destroy(arrPolytopes);
|
||||
}
|
||||
else
|
||||
if(arrPolytopes.Size() == 1)
|
||||
{
|
||||
bodypart.mesh = arrPolytopes[0];
|
||||
}
|
||||
if(bodypart.mesh)
|
||||
m_bodyparts.AddToTail(bodypart);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void CPhysics2CollisionBuilder::BuildRigid()
|
||||
{
|
||||
CUtlVector<Vector> worldspaceVerts;
|
||||
worldspaceVerts.SetCount(m_pModel->numvertices);
|
||||
ConvertToWorldSpace( worldspaceVerts );
|
||||
|
||||
m_bodyparts.SetSize(0);
|
||||
bool bValid = true;
|
||||
if ( m_allowConcave )
|
||||
{
|
||||
CUtlVector<CMeshAdaptor> arrMeshes;
|
||||
int numMeshes = m_pModel->nummeshes;
|
||||
arrMeshes.SetCount(numMeshes);
|
||||
for ( int i = 0; i < numMeshes; i++ )
|
||||
{
|
||||
s_mesh_t *pMesh = m_pModel->mesh + m_pModel->meshindex[i];
|
||||
arrMeshes[i].m_pFaces = m_pModel->face;
|
||||
arrMeshes[i].m_pVerts = worldspaceVerts.Base();//m_pModel->vertex;
|
||||
arrMeshes[i].m_pMesh = pMesh;
|
||||
}
|
||||
// this is one way to do it: make one polysoup
|
||||
//g_pCook->CookPolysoupFromMeshes(arrMeshes.Base(), numMeshes);
|
||||
|
||||
// another way is to create a bunch of convex polytopes
|
||||
for ( int i = 0; i < numMeshes; i++ )
|
||||
{
|
||||
IPhysics2CookedPolytope *polytope = g_pCook->CookPolytope(&arrMeshes[i]);
|
||||
if(polytope)
|
||||
{
|
||||
m_bodyparts.AddToTail(bodypart_t(polytope, -1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( m_bodyparts.Count() > m_maxConvex )
|
||||
{
|
||||
MdlWarning("COSTLY COLLISION MODEL!!!! (%d parts - %d allowed)\n", m_bodyparts.Count(), m_maxConvex );
|
||||
bValid = false;
|
||||
}
|
||||
|
||||
if ( !bValid && m_bodyparts.Count() )
|
||||
{
|
||||
MdlWarning("Error with convex elements of %s, building single convex!!!!\n", m_pModel->filename );
|
||||
Destroy(m_bodyparts);
|
||||
}
|
||||
|
||||
// either we don't want concave, or there was an error building it
|
||||
if ( !m_bodyparts.Count() )
|
||||
{
|
||||
CUtlVector_Vector4DAligned arrVerts;
|
||||
arrVerts.SetSize(worldspaceVerts.Count());
|
||||
for(int i = 0;i < worldspaceVerts.Count(); ++i)
|
||||
{
|
||||
const Vector &v = worldspaceVerts[i];
|
||||
arrVerts[i].Init(v.x,v.y,v.z);
|
||||
}
|
||||
IPhysics2CookedPolytope *polytope = g_pCook->CookPolytopeFromVertices((Vector4DAligned*)arrVerts.Base(), worldspaceVerts.Count());
|
||||
m_bodyparts.AddToTail(bodypart_t(polytope,-1));
|
||||
}
|
||||
|
||||
if(m_bodyparts.Size() > 1)
|
||||
{
|
||||
// fold it into one single neat mesh
|
||||
CUtlVector<IPhysics2CookedMeshBase*>arrMeshes(m_bodyparts.Size(),m_bodyparts.Size());
|
||||
for(int i = 0;i < m_bodyparts.Size(); ++i)
|
||||
arrMeshes[i] = m_bodyparts[i].mesh;
|
||||
IPhysics2CookedMopp *mopp = g_pCook->CookMopp(arrMeshes.Base(), m_bodyparts.Size());
|
||||
Destroy(m_bodyparts);
|
||||
if(mopp)
|
||||
m_bodyparts.AddToTail(bodypart_t(mopp, -1));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void CPhysics2CollisionBuilder::Destroy(CUtlVector<IPhysics2CookedMeshBase*> &arrPolytopes)
|
||||
{
|
||||
for ( int i = 0; i < arrPolytopes.Count(); i++ )
|
||||
g_pCook->Destroy( arrPolytopes[i] );
|
||||
arrPolytopes.Purge();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void CPhysics2CollisionBuilder::Write()
|
||||
{
|
||||
char filename[512];
|
||||
|
||||
strcpy( filename, gamedir );
|
||||
strcat( filename, "models/" );
|
||||
strcat( filename, m_pOverrideName ? m_pOverrideName : outname );
|
||||
Q_SetExtension( filename, ".phz", sizeof( filename ) );
|
||||
if(!m_bodyparts.Size())
|
||||
{
|
||||
CPlainAutoPtr< CP4File > spFile( g_p4factory->AccessFile( filename ) );
|
||||
unlink(filename);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
DataLinker::Stream stream;
|
||||
Physics2CollisionHeader_t *pHeader = stream.Write<Physics2CollisionHeader_t>();
|
||||
pHeader->m_dataVersion = g_pPhysics2->GetSerializeVersion();
|
||||
pHeader->m_numBones = m_bodyparts.Size();
|
||||
//if(!m_pModel->numbones)
|
||||
// pHeader->m_numBones = 1; // there's still 1 pseudo-bone there
|
||||
Physics2RigidPolyShape_t *pRigids = stream.IStream::WriteAndLinkStrided(&pHeader->m_shapes, sizeof(Physics2RigidPolyShape_t), m_bodyparts.Count());
|
||||
|
||||
///
|
||||
// Note: I want all inertia descriptors to reside together for cache coherency in dynamics phase, so I'm writing inertia first, then the shapes
|
||||
///
|
||||
|
||||
for(int nBodyPart = 0; nBodyPart < m_bodyparts.Size(); ++nBodyPart)
|
||||
{
|
||||
int boneIndex = m_bodyparts[nBodyPart].bone;
|
||||
IPhysics2CookedMeshBase *pMesh = m_bodyparts[nBodyPart].mesh;
|
||||
const char *boneName = boneIndex < 0 ? "" : m_pModel->localBone[boneIndex].name;
|
||||
// we'll leave all offsets to NULL if there's no mesh for that bone
|
||||
if(pMesh)
|
||||
{
|
||||
IPhysics2CookedInertia *pInertia = g_pCook->CookInertia(pMesh->GetShape()); // the inertia of the model as a rigid whole
|
||||
if(pInertia)
|
||||
{
|
||||
stream.IStream::Link(&pRigids[nBodyPart].m_inertia, pInertia->Serialize(&stream));
|
||||
g_pCook->Destroy(pInertia);
|
||||
}
|
||||
else
|
||||
Warning("Could not cook inertia for '%s' #d\n", boneName, boneIndex);
|
||||
}
|
||||
pRigids[nBodyPart].m_localBoneIndex = boneIndex;
|
||||
|
||||
if(boneIndex >= 0)
|
||||
{
|
||||
int globalBoneIndex = m_pModel->boneLocalToGlobal[boneIndex];
|
||||
pRigids[nBodyPart].m_globalBoneIndex = globalBoneIndex;
|
||||
}
|
||||
}
|
||||
|
||||
for(int nBodyPart = 0; nBodyPart < m_bodyparts.Size(); ++nBodyPart)
|
||||
{
|
||||
bodypart_t &bp = m_bodyparts[nBodyPart];
|
||||
// we'll leave all offsets to NULL if there's no mesh for that bone
|
||||
pRigids[nBodyPart].m_shapeType = bp.mesh->GetType();
|
||||
stream.IStream::Link(&pRigids[nBodyPart].m_shape, bp.mesh->Serialize(&stream));
|
||||
}
|
||||
|
||||
*(char*)stream.WriteBytes(1) = '\n'; // for debugging
|
||||
for(int nBodyPart = 0; nBodyPart < m_bodyparts.Size(); ++nBodyPart)
|
||||
{
|
||||
bodypart_t &bp = m_bodyparts[nBodyPart];
|
||||
if(bp.bone >= 0)
|
||||
{
|
||||
const char *name = m_pModel->localBone[bp.bone].name;
|
||||
if(name)
|
||||
{
|
||||
int nameLen = strlen(name);
|
||||
char *pNameOut = (char*)stream.WriteBytes(nameLen + 2);
|
||||
stream.IStream::Link(&pRigids[nBodyPart].m_name, pNameOut);
|
||||
memcpy(pNameOut, name, nameLen+1);
|
||||
pNameOut[nameLen+1] = '\n'; // for debugging
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint nDataSize = stream.GetTotalSize();
|
||||
void *pData = MemAlloc_AllocAligned(nDataSize, 16, __FILE__, __LINE__);
|
||||
if(stream.Compile(pData))
|
||||
{
|
||||
CPlainAutoPtr< CP4File > spFile( g_p4factory->AccessFile( filename ) );
|
||||
spFile->Edit();
|
||||
FILE *fp = fopen( filename, "wb" );
|
||||
if(fp)
|
||||
{
|
||||
int numWritten = fwrite(pData, nDataSize, 1, fp);
|
||||
fclose(fp);
|
||||
}
|
||||
else
|
||||
{
|
||||
MdlWarning("Error writing %s!!!\n", filename );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
MdlWarning("Cannot compile the phz data\n");
|
||||
}
|
||||
|
||||
MemAlloc_FreeAligned(pData, __FILE__, __LINE__);
|
||||
}
|
||||
|
||||
21
utils/studiomdl/physics2collision.h
Normal file
21
utils/studiomdl/physics2collision.h
Normal 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
9311
utils/studiomdl/simplify.cpp
Normal file
File diff suppressed because it is too large
Load Diff
12759
utils/studiomdl/studiomdl.cpp
Normal file
12759
utils/studiomdl/studiomdl.cpp
Normal file
File diff suppressed because it is too large
Load Diff
1933
utils/studiomdl/studiomdl.h
Normal file
1933
utils/studiomdl/studiomdl.h
Normal file
File diff suppressed because it is too large
Load Diff
169
utils/studiomdl/studiomdl.vpc
Normal file
169
utils/studiomdl/studiomdl.vpc
Normal file
@@ -0,0 +1,169 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// STUDIOMDL.VPC
|
||||
//
|
||||
// Project Script
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
$Macro SRCDIR "..\.."
|
||||
$Macro OUTBINDIR "$SRCDIR\..\game\bin"
|
||||
|
||||
$Include "$SRCDIR\vpc_scripts\source_exe_con_win32_base.vpc"
|
||||
|
||||
$Include "$SRCDIR\vpc_scripts\fbx.vpc"
|
||||
|
||||
$Configuration
|
||||
{
|
||||
$Compiler
|
||||
{
|
||||
$AdditionalIncludeDirectories "$BASE,..\common,..\nvtristriplib,$SRCDIR\Game_Shared, $SRCDIR\ps3sdk\cell\host-common\include,$SRCDIR\ps3sdk\cell\target\common\include"
|
||||
|
||||
$PreprocessorDefinitions "$BASE;PROTECTED_THINGS_DISABLE"
|
||||
}
|
||||
|
||||
$Linker
|
||||
{
|
||||
$AdditionalDependencies "$BASE winmm.lib libedgegeomtool.Release.Win32.vs8.lib"
|
||||
$AdditionalLibraryDirectories "$BASE;$SRCDIR\ps3sdk\cell\host-win32\lib"
|
||||
}
|
||||
}
|
||||
|
||||
$Project "Studiomdl"
|
||||
{
|
||||
$Folder "Source Files"
|
||||
{
|
||||
$File "..\common\cmdlib.cpp"
|
||||
$File "..\common\datalinker.cpp"
|
||||
$File "collisionmodel.cpp"
|
||||
$File "collisionmodelsource.cpp"
|
||||
// $File "physics2collision.cpp"
|
||||
$File "$SRCDIR\public\collisionutils.cpp"
|
||||
$File "dmxsupport.cpp"
|
||||
$File "$SRCDIR\public\filesystem_helpers.cpp"
|
||||
$File "$SRCDIR\public\filesystem_init.cpp"
|
||||
$File "..\common\filesystem_tools.cpp"
|
||||
$File "hardwarematrixstate.cpp"
|
||||
$File "hardwarevertexcache.cpp"
|
||||
$File "$SRCDIR\public\interpolatortypes.cpp"
|
||||
$File "$SRCDIR\public\mdlobjects\mdlobjects.cpp"
|
||||
$File "$SRCDIR\public\movieobjects\movieobjects_compiletools.cpp"
|
||||
$File "mrmsupport.cpp"
|
||||
$File "objsupport.cpp"
|
||||
$File "optimize_subd.cpp"
|
||||
$File "optimize.cpp"
|
||||
$File "perfstats.cpp"
|
||||
$File "..\common\physdll.cpp"
|
||||
$File "..\common\scriplib.cpp"
|
||||
$File "simplify.cpp"
|
||||
$File "$SRCDIR\public\studio.cpp"
|
||||
$File "$SRCDIR\common\studiobyteswap.cpp"
|
||||
$File "studiomdl.cpp"
|
||||
$File "compileclothproxy.cpp"
|
||||
$File "UnifyLODs.cpp"
|
||||
$File "v1support.cpp"
|
||||
$File "write.cpp"
|
||||
}
|
||||
|
||||
$Folder "Header Files"
|
||||
{
|
||||
$File "..\common\cmdlib.h"
|
||||
$File "..\common\datalinker.h"
|
||||
$File "collisionmodel.h"
|
||||
$File "collisionmodelsource.h"
|
||||
$File "physics2collision.h"
|
||||
$File "physics2collision.h"
|
||||
$File "filebuffer.h"
|
||||
$File "..\common\filesystem_tools.h"
|
||||
$File "hardwarematrixstate.h"
|
||||
$File "hardwarevertexcache.h"
|
||||
$File "..\nvtristriplib\nvtristrip.h"
|
||||
$File "perfstats.h"
|
||||
$File "..\common\physdll.h"
|
||||
$File "..\common\scriplib.h"
|
||||
$File "studiomdl.h"
|
||||
$File "optimize_subd.h"
|
||||
$File "compileclothproxy.h"
|
||||
}
|
||||
|
||||
$Folder "Public Header Files"
|
||||
{
|
||||
$File "$SRCDIR\public\alignedarray.h"
|
||||
$File "$SRCDIR\public\gametrace.h"
|
||||
$File "$SRCDIR\public\filesystem.h"
|
||||
$File "$SRCDIR\public\filesystem_helpers.h"
|
||||
$File "$SRCDIR\public\cmodel.h"
|
||||
$File "$SRCDIR\public\basehandle.h"
|
||||
$File "$SRCDIR\public\tier0\basetypes.h"
|
||||
$File "$SRCDIR\public\bitvec.h"
|
||||
$File "$SRCDIR\public\bone_accessor.h"
|
||||
$File "$SRCDIR\public\bone_setup.h"
|
||||
$File "$SRCDIR\public\bspflags.h"
|
||||
$File "$SRCDIR\public\tier1\byteswap.h"
|
||||
$File "$SRCDIR\public\tier1\characterset.h"
|
||||
$File "$SRCDIR\public\collisionutils.h"
|
||||
$File "$SRCDIR\public\mathlib\compressed_vector.h"
|
||||
$File "$SRCDIR\public\const.h"
|
||||
$File "$SRCDIR\public\vphysics\constraints.h"
|
||||
$File "$SRCDIR\public\tier0\dbg.h"
|
||||
$File "$SRCDIR\public\tier0\fasttimer.h"
|
||||
$File "$SRCDIR\public\appframework\iappsystem.h"
|
||||
$File "$SRCDIR\public\tier0\icommandline.h"
|
||||
$File "$SRCDIR\public\ihandleentity.h"
|
||||
$File "$SRCDIR\public\materialsystem\imaterial.h"
|
||||
$File "$SRCDIR\public\materialsystem\imaterialsystem.h"
|
||||
$File "$SRCDIR\public\materialsystem\imaterialvar.h"
|
||||
$File "$SRCDIR\public\tier1\interface.h"
|
||||
$File "$SRCDIR\public\istudiorender.h"
|
||||
$File "$SRCDIR\public\tier1\keyvalues.h"
|
||||
$File "$SRCDIR\public\materialsystem\materialsystem_config.h"
|
||||
$File "$SRCDIR\public\mathlib\mathlib.h"
|
||||
$File "$SRCDIR\public\tier0\memdbgoff.h"
|
||||
$File "$SRCDIR\public\tier0\memdbgon.h"
|
||||
$File "$SRCDIR\public\phyfile.h"
|
||||
$File "$SRCDIR\public\phzfile.h"
|
||||
$File "$SRCDIR\public\optimize.h"
|
||||
$File "$SRCDIR\public\tier0\platform.h"
|
||||
$File "$SRCDIR\public\vstdlib\random.h"
|
||||
$File "$SRCDIR\common\studiobyteswap.h"
|
||||
$File "$SRCDIR\public\string_t.h"
|
||||
$File "$SRCDIR\public\tier1\strtools.h"
|
||||
$File "$SRCDIR\public\studio.h"
|
||||
$File "$SRCDIR\public\tier3\tier3.h"
|
||||
$File "$SRCDIR\public\tier1\utlbuffer.h"
|
||||
$File "$SRCDIR\public\tier1\utldict.h"
|
||||
$File "$SRCDIR\public\tier1\utllinkedlist.h"
|
||||
$File "$SRCDIR\public\tier1\utlmemory.h"
|
||||
$File "$SRCDIR\public\tier1\utlrbtree.h"
|
||||
$File "$SRCDIR\public\tier1\utlsymbol.h"
|
||||
$File "$SRCDIR\public\tier1\utlvector.h"
|
||||
$File "$SRCDIR\public\vcollide.h"
|
||||
$File "$SRCDIR\public\vcollide_parse.h"
|
||||
$File "$SRCDIR\public\mathlib\vector.h"
|
||||
$File "$SRCDIR\public\mathlib\vector2d.h"
|
||||
$File "$SRCDIR\public\mathlib\vector4d.h"
|
||||
$File "$SRCDIR\public\mathlib\vmatrix.h"
|
||||
$File "$SRCDIR\public\vphysics_interface.h"
|
||||
$File "$SRCDIR\public\mathlib\vplane.h"
|
||||
$File "$SRCDIR\public\tier0\vprof.h"
|
||||
$File "$SRCDIR\public\vstdlib\vstdlib.h"
|
||||
}
|
||||
|
||||
$Folder "Link Libraries"
|
||||
{
|
||||
$Lib resourcefile
|
||||
$Lib dmeutils
|
||||
$Lib meshutils
|
||||
$Lib appframework
|
||||
$Lib bonesetup
|
||||
$Lib datamodel
|
||||
$Lib dmserializers
|
||||
$Lib mathlib
|
||||
$Lib mathlib_extended
|
||||
$Lib mdlobjects
|
||||
$Lib movieobjects
|
||||
$Lib nvtristrip
|
||||
$Lib tier1
|
||||
$Lib tier2
|
||||
$Lib tier3
|
||||
$Lib fbxutils
|
||||
}
|
||||
}
|
||||
350
utils/studiomdl/tristrip.cpp
Normal file
350
utils/studiomdl/tristrip.cpp
Normal file
@@ -0,0 +1,350 @@
|
||||
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
// tristrip - convert triangle list into tristrips and fans
|
||||
|
||||
#pragma warning( disable : 4244 )
|
||||
#pragma warning( disable : 4237 )
|
||||
#pragma warning( disable : 4305 )
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "cmdlib.h"
|
||||
#include "lbmlib.h"
|
||||
#include "scriplib.h"
|
||||
#include "mathlib/mathlib.h"
|
||||
#include "..\..\engine\studio.h"
|
||||
#include "studiomdl.h"
|
||||
|
||||
int used[MAXSTUDIOTRIANGLES];
|
||||
|
||||
// the command list holds counts and s/t values that are valid for
|
||||
// every frame
|
||||
short commands[MAXSTUDIOTRIANGLES * 13];
|
||||
int numcommands;
|
||||
|
||||
// all frames will have their vertexes rearranged and expanded
|
||||
// so they are in the order expected by the command list
|
||||
|
||||
int allverts, alltris;
|
||||
|
||||
int stripverts[MAXSTUDIOTRIANGLES+2];
|
||||
int striptris[MAXSTUDIOTRIANGLES+2];
|
||||
int stripcount;
|
||||
|
||||
int neighbortri[MAXSTUDIOTRIANGLES][3];
|
||||
int neighboredge[MAXSTUDIOTRIANGLES][3];
|
||||
|
||||
|
||||
s_trianglevert_t (*triangles)[3];
|
||||
s_mesh_t *pmesh;
|
||||
|
||||
|
||||
void FindNeighbor (int starttri, int startv)
|
||||
{
|
||||
s_trianglevert_t m1, m2;
|
||||
int j;
|
||||
s_trianglevert_t *last, *check;
|
||||
int k;
|
||||
|
||||
// used[starttri] |= (1 << startv);
|
||||
|
||||
last = &triangles[starttri][0];
|
||||
|
||||
m1 = last[(startv+1)%3];
|
||||
m2 = last[(startv+0)%3];
|
||||
|
||||
for (j=starttri+1, check=&triangles[starttri+1][0] ; j<pmesh->numtris ; j++, check += 3)
|
||||
{
|
||||
if (used[j] == 7)
|
||||
continue;
|
||||
for (k=0 ; k<3 ; k++)
|
||||
{
|
||||
if (memcmp(&check[k],&m1,sizeof(m1)))
|
||||
continue;
|
||||
if (memcmp(&check[ (k+1)%3 ],&m2,sizeof(m2)))
|
||||
continue;
|
||||
|
||||
neighbortri[starttri][startv] = j;
|
||||
neighboredge[starttri][startv] = k;
|
||||
|
||||
neighbortri[j][k] = starttri;
|
||||
neighboredge[j][k] = startv;
|
||||
|
||||
used[starttri] |= (1 << startv);
|
||||
used[j] |= (1 << k);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
================
|
||||
StripLength
|
||||
================
|
||||
*/
|
||||
int StripLength (int starttri, int startv)
|
||||
{
|
||||
int j;
|
||||
int k;
|
||||
|
||||
used[starttri] = 2;
|
||||
|
||||
stripverts[0] = (startv)%3;
|
||||
stripverts[1] = (startv+1)%3;
|
||||
stripverts[2] = (startv+2)%3;
|
||||
|
||||
striptris[0] = starttri;
|
||||
striptris[1] = starttri;
|
||||
striptris[2] = starttri;
|
||||
stripcount = 3;
|
||||
|
||||
while( 1 )
|
||||
{
|
||||
if (stripcount & 1)
|
||||
{
|
||||
j = neighbortri[starttri][(startv+1)%3];
|
||||
k = neighboredge[starttri][(startv+1)%3];
|
||||
}
|
||||
else
|
||||
{
|
||||
j = neighbortri[starttri][(startv+2)%3];
|
||||
k = neighboredge[starttri][(startv+2)%3];
|
||||
}
|
||||
if (j == -1 || used[j])
|
||||
goto done;
|
||||
|
||||
stripverts[stripcount] = (k+2)%3;
|
||||
striptris[stripcount] = j;
|
||||
stripcount++;
|
||||
|
||||
used[j] = 2;
|
||||
|
||||
starttri = j;
|
||||
startv = k;
|
||||
}
|
||||
|
||||
done:
|
||||
|
||||
// clear the temp used flags
|
||||
for (j=0 ; j<pmesh->numtris ; j++)
|
||||
if (used[j] == 2)
|
||||
used[j] = 0;
|
||||
|
||||
return stripcount;
|
||||
}
|
||||
|
||||
/*
|
||||
===========
|
||||
FanLength
|
||||
===========
|
||||
*/
|
||||
int FanLength (int starttri, int startv)
|
||||
{
|
||||
int j;
|
||||
int k;
|
||||
|
||||
used[starttri] = 2;
|
||||
|
||||
stripverts[0] = (startv)%3;
|
||||
stripverts[1] = (startv+1)%3;
|
||||
stripverts[2] = (startv+2)%3;
|
||||
|
||||
striptris[0] = starttri;
|
||||
striptris[1] = starttri;
|
||||
striptris[2] = starttri;
|
||||
stripcount = 3;
|
||||
|
||||
while( 1 )
|
||||
{
|
||||
j = neighbortri[starttri][(startv+2)%3];
|
||||
k = neighboredge[starttri][(startv+2)%3];
|
||||
|
||||
if (j == -1 || used[j])
|
||||
goto done;
|
||||
|
||||
stripverts[stripcount] = (k+2)%3;
|
||||
striptris[stripcount] = j;
|
||||
stripcount++;
|
||||
|
||||
used[j] = 2;
|
||||
|
||||
starttri = j;
|
||||
startv = k;
|
||||
}
|
||||
|
||||
done:
|
||||
|
||||
// clear the temp used flags
|
||||
for (j=0 ; j<pmesh->numtris ; j++)
|
||||
if (used[j] == 2)
|
||||
used[j] = 0;
|
||||
|
||||
return stripcount;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
================
|
||||
BuildTris
|
||||
|
||||
Generate a list of trifans or strips
|
||||
for the model, which holds for all frames
|
||||
================
|
||||
*/
|
||||
int numcommandnodes;
|
||||
|
||||
int BuildTris (s_trianglevert_t (*x)[3], s_mesh_t *y, byte **ppdata )
|
||||
{
|
||||
int i, j, k, m;
|
||||
int startv;
|
||||
int len, bestlen, besttype;
|
||||
int bestverts[MAXSTUDIOTRIANGLES];
|
||||
int besttris[MAXSTUDIOTRIANGLES];
|
||||
int peak[MAXSTUDIOTRIANGLES];
|
||||
int type;
|
||||
int total = 0;
|
||||
long t;
|
||||
int maxlen;
|
||||
|
||||
triangles = x;
|
||||
pmesh = y;
|
||||
|
||||
|
||||
t = time( NULL );
|
||||
|
||||
for (i=0 ; i<pmesh->numtris ; i++)
|
||||
{
|
||||
neighbortri[i][0] = neighbortri[i][1] = neighbortri[i][2] = -1;
|
||||
used[i] = 0;
|
||||
peak[i] = pmesh->numtris;
|
||||
}
|
||||
|
||||
// printf("finding neighbors\n");
|
||||
for (i=0 ; i<pmesh->numtris; i++)
|
||||
{
|
||||
for (k = 0; k < 3; k++)
|
||||
{
|
||||
if (used[i] & (1 << k))
|
||||
continue;
|
||||
|
||||
FindNeighbor( i, k );
|
||||
}
|
||||
// printf("%d", used[i] );
|
||||
}
|
||||
// printf("\n");
|
||||
|
||||
//
|
||||
// build tristrips
|
||||
//
|
||||
numcommandnodes = 0;
|
||||
numcommands = 0;
|
||||
memset (used, 0, sizeof(used));
|
||||
|
||||
for (i=0 ; i<pmesh->numtris ;)
|
||||
{
|
||||
// pick an unused triangle and start the trifan
|
||||
if (used[i])
|
||||
{
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
maxlen = 9999;
|
||||
bestlen = 0;
|
||||
m = 0;
|
||||
for (k = i; k < pmesh->numtris && bestlen < 127; k++)
|
||||
{
|
||||
int localpeak = 0;
|
||||
|
||||
if (used[k])
|
||||
continue;
|
||||
|
||||
if (peak[k] <= bestlen)
|
||||
continue;
|
||||
|
||||
m++;
|
||||
for (type = 0 ; type < 2 ; type++)
|
||||
{
|
||||
for (startv =0 ; startv < 3 ; startv++)
|
||||
{
|
||||
if (type == 1)
|
||||
len = FanLength (k, startv);
|
||||
else
|
||||
len = StripLength (k, startv);
|
||||
if (len > 127)
|
||||
{
|
||||
// skip these, they are too long to encode
|
||||
}
|
||||
else if (len > bestlen)
|
||||
{
|
||||
besttype = type;
|
||||
bestlen = len;
|
||||
for (j=0 ; j<bestlen ; j++)
|
||||
{
|
||||
besttris[j] = striptris[j];
|
||||
bestverts[j] = stripverts[j];
|
||||
}
|
||||
// printf("%d %d\n", k, bestlen );
|
||||
}
|
||||
if (len > localpeak)
|
||||
localpeak = len;
|
||||
}
|
||||
}
|
||||
peak[k] = localpeak;
|
||||
if (localpeak == maxlen)
|
||||
break;
|
||||
}
|
||||
total += (bestlen - 2);
|
||||
|
||||
// printf("%d (%d) %d\n", bestlen, pmesh->numtris - total, i );
|
||||
|
||||
maxlen = bestlen;
|
||||
|
||||
// mark the tris on the best strip as used
|
||||
for (j=0 ; j<bestlen ; j++)
|
||||
used[besttris[j]] = 1;
|
||||
|
||||
if (besttype == 1)
|
||||
commands[numcommands++] = -bestlen;
|
||||
else
|
||||
commands[numcommands++] = bestlen;
|
||||
|
||||
for (j=0 ; j<bestlen ; j++)
|
||||
{
|
||||
s_trianglevert_t *tri;
|
||||
|
||||
tri = &triangles[besttris[j]][bestverts[j]];
|
||||
|
||||
commands[numcommands++] = tri->vertindex;
|
||||
commands[numcommands++] = tri->normindex;
|
||||
commands[numcommands++] = tri->s;
|
||||
commands[numcommands++] = tri->t;
|
||||
}
|
||||
// printf("%d ", bestlen - 2 );
|
||||
numcommandnodes++;
|
||||
|
||||
if (t != time(NULL))
|
||||
{
|
||||
printf("%2d%%\r", (total * 100) / pmesh->numtris );
|
||||
t = time(NULL);
|
||||
}
|
||||
}
|
||||
|
||||
commands[numcommands++] = 0; // end of list marker
|
||||
|
||||
*ppdata = (byte *)commands;
|
||||
|
||||
// printf("%d %d %d\n", numcommandnodes, numcommands, pmesh->numtris );
|
||||
return numcommands * sizeof( short );
|
||||
}
|
||||
|
||||
477
utils/studiomdl/v1support.cpp
Normal file
477
utils/studiomdl/v1support.cpp
Normal 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
5390
utils/studiomdl/write.cpp
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user