void CVisualADSView::OnInitialUpdate() { CVisualADSDoc* pDoc = GetDocument(); SetCurrentModel(pDoc->m_pDataModel); //GetCurrentModel()->DisplayPreviewBmp() LockUpdate(TRUE); /* for (int i = 0; i < 20; i ++) { addGroup(_T("Group组"), 100 + 50 * i, 100); addUser(_T("chaoge"), 200, 200 + 50 * i); } addComputer(_T("VEOTAX"), 150, 500); addResource(_T("NRDC1001"), 400, 100); addInheritRelation(_T("PermitRWX"), m_shapes[0], m_shapes[1]); FOPRect rect; rect.left = 100; rect.top = 100; rect.right = 500; rect.bottom = 500; addDomain(_T("domaintest.net"), 500, 500, 700, 700); */ CFODrawView::OnInitialUpdate(); }
void StudioModel::scaleMeshes (float scale) { CStudioHdr *pStudioHdr = GetStudioHdr(); if (!pStudioHdr) return; int i, j, k; // manadatory to access correct verts SetCurrentModel(); // scale verts int tmp = m_bodynum; for (i = 0; i < pStudioHdr->numbodyparts(); i++) { mstudiobodyparts_t *pbodypart = pStudioHdr->pBodypart( i ); for (j = 0; j < pbodypart->nummodels; j++) { SetBodygroup (i, j); SetupModel (i); const mstudio_modelvertexdata_t *vertData = m_pmodel->GetVertexData(); for (k = 0; k < m_pmodel->numvertices; k++) { *vertData->Position(k) *= scale; } } } m_bodynum = tmp; // scale complex hitboxes int hitboxset = g_MDLViewer->GetCurrentHitboxSet(); mstudiobbox_t *pbboxes = pStudioHdr->pHitbox( 0, hitboxset ); for (i = 0; i < pStudioHdr->iHitboxCount( hitboxset ); i++) { VectorScale (pbboxes[i].bbmin, scale, pbboxes[i].bbmin); VectorScale (pbboxes[i].bbmax, scale, pbboxes[i].bbmax); } // scale bounding boxes for (i = 0; i < pStudioHdr->GetNumSeq(); i++) { mstudioseqdesc_t &seqdesc = pStudioHdr->pSeqdesc( i ); Vector tmp; tmp = seqdesc.bbmin; VectorScale( tmp, scale, tmp ); seqdesc.bbmin = tmp; tmp = seqdesc.bbmax; VectorScale( tmp, scale, tmp ); seqdesc.bbmax = tmp; } // maybe scale exeposition, pivots, attachments }
void cCustomTitleModelTable::resizeEvent(QResizeEvent *ev) { if (ModelTable) { QString Current=GetCurrentModel(); // Update view int ColumnWidth =ModelTable->ThumbnailSize.width()+20; int RowHeight =ModelTable->ThumbnailSize.height()+20; int NewColumnCount=width()/ColumnWidth; if (NewColumnCount<=0) NewColumnCount=1; int NewRowCount =ModelTable->List.count()/NewColumnCount; if (NewRowCount*NewColumnCount<ModelTable->List.count()) NewRowCount++; if ((NewColumnCount!=columnCount())||(NewRowCount!=rowCount())) { setColumnCount(NewColumnCount); setRowCount(NewRowCount); } for (int i=0;i<columnCount();i++) setColumnWidth(i,ColumnWidth); for (int i=0;i<rowCount();i++) setRowHeight(i,RowHeight); SetCurrentModel(Current); } QTableWidget::resizeEvent(ev); }
int StudioModel::FlexVerts( mstudiomesh_t *pmesh ) { // Gotta start at one here, since g_flexages defaults to 0 static int flextag = 1; mstudioflex_t *pflex = pmesh->pFlex( 0 ); // apply flex weights int i, j, n; // manadatory to access correct verts SetCurrentModel(); const mstudio_meshvertexdata_t *vertData = pmesh->GetVertexData(); Assert( vertData ); // This can only return NULL on X360 for now for (i = 0; i < pmesh->numflexes; i++) { float w = g_flexdescweight[pflex[i].flexdesc]; if (w <= pflex[i].target0 || w >= pflex[i].target3) { // value outside of range continue; } else if (w < pflex[i].target1) { // 0 to 1 ramp w = (w - pflex[i].target0) / (pflex[i].target1 - pflex[i].target0); } else if (w > pflex[i].target2) { // 1 to 0 ramp w = (pflex[i].target3 - w) / (pflex[i].target3 - pflex[i].target2); } else { // plateau w = 1.0; } if (w > -0.001 && w < 0.001) continue; // We may have wrinkle information for this flex, but if we're software skinning // we're going to ignore it. byte *pvanim = pflex[i].pBaseVertanim(); int nVAnimSizeBytes = pflex[i].VertAnimSizeBytes(); // JasonM TODO: fix this so there's a float path? // If we have a separate stream for flexes, use fixed point for (j = 0; j < pflex[i].numverts; j++, pvanim += nVAnimSizeBytes ); { mstudiovertanim_t *pAnim = (mstudiovertanim_t*)( pvanim ); n = pAnim->index; // only flex the indicies that are (still) part of this mesh if (n < (int)pmesh->numvertices) { if (g_flexages[n] < flextag) { g_flexages[n] = flextag; VectorCopy( *vertData->Position(n), g_flexedverts[n] ); VectorCopy( *vertData->Normal(n), g_flexednorms[n] ); } VectorMA( g_flexedverts[n], w, pAnim->GetDeltaFixed(), g_flexedverts[n] ); VectorMA( g_flexednorms[n], w, pAnim->GetNDeltaFixed(), g_flexednorms[n] ); } else { n = 0; } } } return flextag++; // Con_DPrintf("\n" ); }
bool StudioModel::LoadModel( const char *pModelName ) { MDLCACHE_CRITICAL_SECTION_( g_pMDLCache ); if (!pModelName) return 0; // In the case of restore, m_pModelName == modelname if (m_pModelName != pModelName) { // Copy over the model name; we'll need it later... if (m_pModelName) { delete[] m_pModelName; } m_pModelName = new char[Q_strlen(pModelName) + 1]; strcpy( m_pModelName, pModelName ); } m_MDLHandle = g_pMDLCache->FindMDL( pModelName ); // allocate a pool for a studiohdr cache if (m_pStudioHdr != NULL) { delete m_pStudioHdr; } m_pStudioHdr = new CStudioHdr( g_pMDLCache->GetStudioHdr( m_MDLHandle ), g_pMDLCache ); // manadatory to access correct verts SetCurrentModel(); m_pPhysics = LoadPhysics( m_MDLHandle ); // Copy over all of the hitboxes; we may add and remove elements m_HitboxSets.RemoveAll(); CStudioHdr *pStudioHdr = GetStudioHdr(); int i; for ( int s = 0; s < pStudioHdr->numhitboxsets(); s++ ) { mstudiohitboxset_t *set = pStudioHdr->pHitboxSet( s ); if ( !set ) continue; m_HitboxSets.AddToTail(); for ( i = 0; i < set->numhitboxes; ++i ) { mstudiobbox_t *pHit = set->pHitbox(i); int nIndex = m_HitboxSets[ s ].AddToTail( ); m_HitboxSets[s][nIndex] = *pHit; } // Set the name hbsetname_s *n = &m_HitboxSetNames[ m_HitboxSetNames.AddToTail() ]; strcpy( n->name, set->pszName() ); } // Copy over all of the surface props; we may change them... for ( i = 0; i < pStudioHdr->numbones(); ++i ) { mstudiobone_t* pBone = pStudioHdr->pBone(i); CUtlSymbol prop( pBone->pszSurfaceProp() ); m_SurfaceProps.AddToTail( prop ); } m_physPreviewBone = -1; bool forceOpaque = (pStudioHdr->flags() & STUDIOHDR_FLAGS_FORCE_OPAQUE) != 0; bool vertexLit = false; m_bIsTransparent = false; m_bHasProxy = false; studiohwdata_t *pHardwareData = g_pMDLCache->GetHardwareData( m_MDLHandle ); if ( !pHardwareData ) { Assert( 0 ); return false; } for( int lodID = pHardwareData->m_RootLOD; lodID < pHardwareData->m_NumLODs; lodID++ ) { studioloddata_t *pLODData = &pHardwareData->m_pLODs[lodID]; for ( i = 0; i < pLODData->numMaterials; ++i ) { if (pLODData->ppMaterials[i]->IsVertexLit()) { vertexLit = true; } if ((!forceOpaque) && pLODData->ppMaterials[i]->IsTranslucent()) { m_bIsTransparent = true; //Msg("Translucent material %s for model %s\n", pLODData->ppMaterials[i]->GetName(), pStudioHdr->name ); } if (pLODData->ppMaterials[i]->HasProxy()) { m_bHasProxy = true; } } } return true; }
int StudioModel::FlexVerts( mstudiomesh_t *pmesh ) { // Gotta start at one here, since g_flexages defaults to 0 static int flextag = 1; mstudioflex_t *pflex = pmesh->pFlex( 0 ); // apply flex weights int i, j, n; // manadatory to access correct verts SetCurrentModel(); const mstudio_meshvertexdata_t *vertData = pmesh->GetVertexData(); for (i = 0; i < pmesh->numflexes; i++) { float w = g_flexdescweight[pflex[i].flexdesc]; if (w <= pflex[i].target0 || w >= pflex[i].target3) { // value outside of range continue; } else if (w < pflex[i].target1) { // 0 to 1 ramp w = (w - pflex[i].target0) / (pflex[i].target1 - pflex[i].target0); } else if (w > pflex[i].target2) { // 1 to 0 ramp w = (pflex[i].target3 - w) / (pflex[i].target3 - pflex[i].target2); } else { // plateau w = 1.0; } if (w > -0.001 && w < 0.001) continue; mstudiovertanim_t *pvanim = pflex[i].pVertanim( 0 ); // JasonM TODO: fix this so there's a float path? // If we have a separate stream for flexes, use fixed point for (j = 0; j < pflex[i].numverts; j++) { n = pvanim[j].index; // only flex the indicies that are (still) part of this mesh if (n < (int)pmesh->numvertices) { if (g_flexages[n] < flextag) { g_flexages[n] = flextag; VectorCopy( *vertData->Position(n), g_flexedverts[n] ); VectorCopy( *vertData->Normal(n), g_flexednorms[n] ); } VectorMA( g_flexedverts[n], w, pvanim[j].GetDeltaFixed(), g_flexedverts[n] ); VectorMA( g_flexednorms[n], w, pvanim[j].GetNDeltaFixed(), g_flexednorms[n] ); } else { n = 0; } } } return flextag++; // Con_DPrintf("\n" ); }
//----------------------------------------------------------------------------- // Trace rays from each unique vertex, accumulating direct and indirect // sources at each ray termination. Use the winding data to distribute the unique vertexes // into the rendering layout. //----------------------------------------------------------------------------- void CVradStaticPropMgr::ComputeLighting( CStaticProp &prop, int iThread, int prop_index ) { Vector samplePosition; Vector sampleNormal; CUtlVector<colorVertex_t> colorVerts; CUtlVector<badVertex_t> badVerts; StaticPropDict_t &dict = m_StaticPropDict[prop.m_ModelIdx]; studiohdr_t *pStudioHdr = dict.m_pStudioHdr; OptimizedModel::FileHeader_t *pVtxHdr = (OptimizedModel::FileHeader_t *)dict.m_VtxBuf.Base(); if ( !pStudioHdr || !pVtxHdr ) { // must have model and its verts for lighting computation // game will fallback to fullbright return; } // for access to this model's vertexes SetCurrentModel( pStudioHdr ); for ( int bodyID = 0; bodyID < pStudioHdr->numbodyparts; ++bodyID ) { OptimizedModel::BodyPartHeader_t* pVtxBodyPart = pVtxHdr->pBodyPart( bodyID ); mstudiobodyparts_t *pBodyPart = pStudioHdr->pBodypart( bodyID ); for ( int modelID = 0; modelID < pBodyPart->nummodels; ++modelID ) { OptimizedModel::ModelHeader_t* pVtxModel = pVtxBodyPart->pModel( modelID ); mstudiomodel_t *pStudioModel = pBodyPart->pModel( modelID ); // light all unique vertexes colorVerts.EnsureCount( pStudioModel->numvertices ); memset( colorVerts.Base(), 0, colorVerts.Count() * sizeof(colorVertex_t) ); int numVertexes = 0; for ( int meshID = 0; meshID < pStudioModel->nummeshes; ++meshID ) { mstudiomesh_t *pStudioMesh = pStudioModel->pMesh( meshID ); const mstudio_meshvertexdata_t *vertData = pStudioMesh->GetVertexData(); for ( int vertexID = 0; vertexID < pStudioMesh->numvertices; ++vertexID ) { // transform position and normal into world coordinate system matrix3x4_t matrix; AngleMatrix( prop.m_Angles, prop.m_Origin, matrix ); VectorTransform( *vertData->Position( vertexID ), matrix, samplePosition ); AngleMatrix( prop.m_Angles, matrix ); VectorTransform( *vertData->Normal( vertexID ), matrix, sampleNormal ); if ( (! (prop.m_Flags & STATIC_PROP_NO_PER_VERTEX_LIGHTING ) ) && PositionInSolid( samplePosition ) ) { // vertex is in solid, add to the bad list, and recover later badVertex_t badVertex; badVertex.m_ColorVertex = numVertexes; badVertex.m_Position = samplePosition; badVertex.m_Normal = sampleNormal; badVerts.AddToTail( badVertex ); } else { Vector direct_pos=samplePosition; int skip_prop=-1; Vector directColor(0,0,0); if (prop.m_Flags & STATIC_PROP_NO_PER_VERTEX_LIGHTING ) { if (prop.m_bLightingOriginValid) VectorCopy( prop.m_LightingOrigin, direct_pos ); else VectorCopy( prop.m_Origin, direct_pos ); skip_prop = prop_index; } if ( prop.m_Flags & STATIC_PROP_NO_SELF_SHADOWING ) skip_prop = prop_index; ComputeDirectLightingAtPoint( direct_pos, sampleNormal, directColor, iThread, skip_prop ); Vector indirectColor(0,0,0); if (g_bShowStaticPropNormals) { directColor= sampleNormal; directColor += Vector(1.0,1.0,1.0); directColor *= 50.0; } else if (numbounce >= 1) ComputeIndirectLightingAtPoint( samplePosition, sampleNormal, indirectColor, iThread, true ); colorVerts[numVertexes].m_bValid = true; colorVerts[numVertexes].m_Position = samplePosition; VectorAdd( directColor, indirectColor, colorVerts[numVertexes].m_Color ); } numVertexes++; } } // color in the bad vertexes // when entire model has no lighting origin and no valid neighbors // must punt, leave black coloring if ( badVerts.Count() && ( prop.m_bLightingOriginValid || badVerts.Count() != numVertexes ) ) { for ( int nBadVertex = 0; nBadVertex < badVerts.Count(); nBadVertex++ ) { Vector bestPosition; if ( prop.m_bLightingOriginValid ) { // use the specified lighting origin VectorCopy( prop.m_LightingOrigin, bestPosition ); } else { // find the closest valid neighbor int best = 0; float closest = FLT_MAX; for ( int nColorVertex = 0; nColorVertex < numVertexes; nColorVertex++ ) { if ( !colorVerts[nColorVertex].m_bValid ) { // skip invalid neighbors continue; } Vector delta; VectorSubtract( colorVerts[nColorVertex].m_Position, badVerts[nBadVertex].m_Position, delta ); float distance = VectorLength( delta ); if ( distance < closest ) { closest = distance; best = nColorVertex; } } // use the best neighbor as the direction to crawl VectorCopy( colorVerts[best].m_Position, bestPosition ); } // crawl toward best position // sudivide to determine a closer valid point to the bad vertex, and re-light Vector midPosition; int numIterations = 20; while ( --numIterations > 0 ) { VectorAdd( bestPosition, badVerts[nBadVertex].m_Position, midPosition ); VectorScale( midPosition, 0.5f, midPosition ); if ( PositionInSolid( midPosition ) ) break; bestPosition = midPosition; } // re-light from better position Vector directColor; ComputeDirectLightingAtPoint( bestPosition, badVerts[nBadVertex].m_Normal, directColor, iThread ); Vector indirectColor; ComputeIndirectLightingAtPoint( bestPosition, badVerts[nBadVertex].m_Normal, indirectColor, iThread, true ); // save results, not changing valid status // to ensure this offset position is not considered as a viable candidate colorVerts[badVerts[nBadVertex].m_ColorVertex].m_Position = bestPosition; VectorAdd( directColor, indirectColor, colorVerts[badVerts[nBadVertex].m_ColorVertex].m_Color ); } } // discard bad verts badVerts.Purge(); // distribute the lighting results for ( int nLod = 0; nLod < pVtxHdr->numLODs; nLod++ ) { OptimizedModel::ModelLODHeader_t *pVtxLOD = pVtxModel->pLOD( nLod ); for ( int nMesh = 0; nMesh < pStudioModel->nummeshes; ++nMesh ) { mstudiomesh_t* pMesh = pStudioModel->pMesh( nMesh ); OptimizedModel::MeshHeader_t* pVtxMesh = pVtxLOD->pMesh( nMesh ); for ( int nGroup = 0; nGroup < pVtxMesh->numStripGroups; ++nGroup ) { OptimizedModel::StripGroupHeader_t* pStripGroup = pVtxMesh->pStripGroup( nGroup ); int nMeshIdx = prop.m_MeshData.AddToTail(); prop.m_MeshData[nMeshIdx].m_Verts.AddMultipleToTail( pStripGroup->numVerts ); prop.m_MeshData[nMeshIdx].m_nLod = nLod; for ( int nVertex = 0; nVertex < pStripGroup->numVerts; ++nVertex ) { int nIndex = pMesh->vertexoffset + pStripGroup->pVertex( nVertex )->origMeshVertID; Assert( nIndex < pStudioModel->numvertices ); prop.m_MeshData[nMeshIdx].m_Verts[nVertex] = colorVerts[nIndex].m_Color; } } } } } } }
//----------------------------------------------------------------------------- // Creates a collision model (based on the render geometry!) //----------------------------------------------------------------------------- void CVradStaticPropMgr::CreateCollisionModel( char const* pModelName ) { CUtlBuffer buf; CUtlBuffer bufvtx; CUtlBuffer bufphy; int i = m_StaticPropDict.AddToTail(); m_StaticPropDict[i].m_pModel = NULL; m_StaticPropDict[i].m_pStudioHdr = NULL; if ( !LoadStudioModel( pModelName, buf ) ) { VectorCopy( vec3_origin, m_StaticPropDict[i].m_Mins ); VectorCopy( vec3_origin, m_StaticPropDict[i].m_Maxs ); return; } studiohdr_t* pHdr = (studiohdr_t*)buf.Base(); // necessary for vertex access SetCurrentModel( pHdr ); VectorCopy( pHdr->hull_min, m_StaticPropDict[i].m_Mins ); VectorCopy( pHdr->hull_max, m_StaticPropDict[i].m_Maxs ); if ( LoadStudioCollisionModel( pModelName, bufphy ) ) { phyheader_t header; bufphy.Get( &header, sizeof(header) ); vcollide_t *pCollide = &m_StaticPropDict[i].m_loadedModel; s_pPhysCollision->VCollideLoad( pCollide, header.solidCount, (const char *)bufphy.PeekGet(), bufphy.TellPut() - bufphy.TellGet() ); m_StaticPropDict[i].m_pModel = m_StaticPropDict[i].m_loadedModel.solids[0]; /* static int propNum = 0; char tmp[128]; sprintf( tmp, "staticprop%03d.txt", propNum ); DumpCollideToGlView( pCollide, tmp ); ++propNum; */ } else { // mark this as unused m_StaticPropDict[i].m_loadedModel.solidCount = 0; // CPhysCollide* pPhys = CreatePhysCollide( pHdr, pVtxHdr ); m_StaticPropDict[i].m_pModel = ComputeConvexHull( pHdr ); } // clone it m_StaticPropDict[i].m_pStudioHdr = (studiohdr_t *)malloc( buf.Size() ); memcpy( m_StaticPropDict[i].m_pStudioHdr, (studiohdr_t*)buf.Base(), buf.Size() ); if ( !LoadVTXFile( pModelName, m_StaticPropDict[i].m_pStudioHdr, m_StaticPropDict[i].m_VtxBuf ) ) { // failed, leave state identified as disabled m_StaticPropDict[i].m_VtxBuf.Purge(); } }
//----------------------------------------------------------------------------- // Adds all static prop polys to the ray trace store. //----------------------------------------------------------------------------- void CVradStaticPropMgr::AddPolysForRayTrace( void ) { int count = m_StaticProps.Count(); if ( !count ) { // nothing to do return; } for ( int nProp = 0; nProp < count; ++nProp ) { CStaticProp &prop = m_StaticProps[nProp]; if ( prop.m_Flags & STATIC_PROP_NO_SHADOW ) continue; StaticPropDict_t &dict = m_StaticPropDict[prop.m_ModelIdx]; studiohdr_t *pStudioHdr = dict.m_pStudioHdr; OptimizedModel::FileHeader_t *pVtxHdr = (OptimizedModel::FileHeader_t *)dict.m_VtxBuf.Base(); if ( !pStudioHdr || !pVtxHdr ) { // must have model and its verts for decoding triangles return; } // for access to this model's vertexes SetCurrentModel( pStudioHdr ); // meshes are deeply hierarchial, divided between three stores, follow the white rabbit // body parts -> models -> lod meshes -> strip groups -> strips // the vertices and indices are pooled, the trick is knowing the offset to determine your indexed base for ( int bodyID = 0; bodyID < pStudioHdr->numbodyparts; ++bodyID ) { OptimizedModel::BodyPartHeader_t* pVtxBodyPart = pVtxHdr->pBodyPart( bodyID ); mstudiobodyparts_t *pBodyPart = pStudioHdr->pBodypart( bodyID ); for ( int modelID = 0; modelID < pBodyPart->nummodels; ++modelID ) { OptimizedModel::ModelHeader_t* pVtxModel = pVtxBodyPart->pModel( modelID ); mstudiomodel_t *pStudioModel = pBodyPart->pModel( modelID ); // assuming lod 0, could iterate if required int nLod = 0; OptimizedModel::ModelLODHeader_t *pVtxLOD = pVtxModel->pLOD( nLod ); for ( int nMesh = 0; nMesh < pStudioModel->nummeshes; ++nMesh ) { mstudiomesh_t* pMesh = pStudioModel->pMesh( nMesh ); OptimizedModel::MeshHeader_t* pVtxMesh = pVtxLOD->pMesh( nMesh ); const mstudio_meshvertexdata_t *vertData = pMesh->GetVertexData(); for ( int nGroup = 0; nGroup < pVtxMesh->numStripGroups; ++nGroup ) { OptimizedModel::StripGroupHeader_t* pStripGroup = pVtxMesh->pStripGroup( nGroup ); int nStrip; for ( nStrip = 0; nStrip < pStripGroup->numStrips; nStrip++ ) { OptimizedModel::StripHeader_t *pStrip = pStripGroup->pStrip( nStrip ); if ( pStrip->flags & OptimizedModel::STRIP_IS_TRILIST ) { for ( int i = 0; i < pStrip->numIndices; i += 3 ) { int idx = pStrip->indexOffset + i; unsigned short i1 = *pStripGroup->pIndex( idx ); unsigned short i2 = *pStripGroup->pIndex( idx + 1 ); unsigned short i3 = *pStripGroup->pIndex( idx + 2 ); int vertex1 = pStripGroup->pVertex( i1 )->origMeshVertID; int vertex2 = pStripGroup->pVertex( i2 )->origMeshVertID; int vertex3 = pStripGroup->pVertex( i3 )->origMeshVertID; // transform position into world coordinate system matrix3x4_t matrix; AngleMatrix( prop.m_Angles, prop.m_Origin, matrix ); Vector position1; Vector position2; Vector position3; VectorTransform( *vertData->Position( vertex1 ), matrix, position1 ); VectorTransform( *vertData->Position( vertex2 ), matrix, position2 ); VectorTransform( *vertData->Position( vertex3 ), matrix, position3 ); // printf( "\ngl 3\n" ); // printf( "gl %6.3f %6.3f %6.3f 1 0 0\n", XYZ(position1)); // printf( "gl %6.3f %6.3f %6.3f 0 1 0\n", XYZ(position2)); // printf( "gl %6.3f %6.3f %6.3f 0 0 1\n", XYZ(position3)); g_RtEnv.AddTriangle( nProp, position1, position2, position3, Vector(0,0,0)); } } else { // all tris expected to be discrete tri lists // must fixme if stripping ever occurs printf("unexpected strips found\n"); Assert( 0 ); return; } } } } } } } }
//----------------------------------------------------------------------------- // Add, find collision model in cache //----------------------------------------------------------------------------- static CPhysCollide* GetCollisionModel( char const* pModelName ) { // Convert to a common string char* pTemp = (char*)_alloca(strlen(pModelName) + 1); strcpy( pTemp, pModelName ); _strlwr( pTemp ); char* pSlash = strchr( pTemp, '\\' ); while( pSlash ) { *pSlash = '/'; pSlash = strchr( pTemp, '\\' ); } // Find it in the cache ModelCollisionLookup_t lookup; lookup.m_Name = pTemp; int i = s_ModelCollisionCache.Find( lookup ); if (i != s_ModelCollisionCache.InvalidIndex()) return s_ModelCollisionCache[i].m_pCollide; // Load the studio model file CUtlBuffer buf; if (!LoadStudioModel(pModelName, "prop_static", buf)) { Warning("Error loading studio model \"%s\"!\n", pModelName ); // This way we don't try to load it multiple times lookup.m_pCollide = 0; s_ModelCollisionCache.Insert( lookup ); return 0; } // Compute the convex hull of the model... studiohdr_t* pStudioHdr = (studiohdr_t*)buf.PeekGet(); // necessary for vertex access SetCurrentModel( pStudioHdr ); lookup.m_pCollide = ComputeConvexHull( pStudioHdr ); s_ModelCollisionCache.Insert( lookup ); if ( !lookup.m_pCollide ) { Warning("Bad geometry on \"%s\"!\n", pModelName ); } // Debugging if (g_DumpStaticProps) { static int propNum = 0; char tmp[128]; sprintf( tmp, "staticprop%03d.txt", propNum ); DumpCollideToGlView( lookup.m_pCollide, tmp ); ++propNum; } FreeCurrentModelVertexes(); // Insert into cache... return lookup.m_pCollide; }