#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_FaceList_t; typedef CUtlVector 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 & 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 m_IndexRemapTable; }; }; // namespace OptimizedModel #endif // OPTIMIZE_SUBD_H