Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

759 lines
22 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Places "detail" objects which are client-only renderable things
  4. //
  5. // $Revision: $
  6. // $NoKeywords: $
  7. //=============================================================================//
  8. #include "vbsp.h"
  9. #include "bsplib.h"
  10. #include "utlvector.h"
  11. #include "bspfile.h"
  12. #include "gamebspfile.h"
  13. #include "VPhysics_Interface.h"
  14. #include "Studio.h"
  15. #include "byteswap.h"
  16. #include "UtlBuffer.h"
  17. #include "CollisionUtils.h"
  18. #include <float.h>
  19. #include "CModel.h"
  20. #include "PhysDll.h"
  21. #include "utlsymbol.h"
  22. #include "tier1/strtools.h"
  23. #include "KeyValues.h"
  24. static void SetCurrentModel( studiohdr_t *pStudioHdr );
  25. static void FreeCurrentModelVertexes();
  26. IPhysicsCollision *s_pPhysCollision = NULL;
  27. //-----------------------------------------------------------------------------
  28. // These puppies are used to construct the game lumps
  29. //-----------------------------------------------------------------------------
  30. static CUtlVector<StaticPropDictLump_t> s_StaticPropDictLump;
  31. static CUtlVector<StaticPropLump_t> s_StaticPropLump;
  32. static CUtlVector<StaticPropLeafLump_t> s_StaticPropLeafLump;
  33. //-----------------------------------------------------------------------------
  34. // Used to build the static prop
  35. //-----------------------------------------------------------------------------
  36. struct StaticPropBuild_t
  37. {
  38. char const* m_pModelName;
  39. char const* m_pLightingOrigin;
  40. Vector m_Origin;
  41. QAngle m_Angles;
  42. int m_Solid;
  43. int m_Skin;
  44. int m_Flags;
  45. float m_FadeMinDist;
  46. float m_FadeMaxDist;
  47. bool m_FadesOut;
  48. float m_flForcedFadeScale;
  49. unsigned short m_nMinDXLevel;
  50. unsigned short m_nMaxDXLevel;
  51. int m_LightmapResolutionX;
  52. int m_LightmapResolutionY;
  53. };
  54. //-----------------------------------------------------------------------------
  55. // Used to cache collision model generation
  56. //-----------------------------------------------------------------------------
  57. struct ModelCollisionLookup_t
  58. {
  59. CUtlSymbol m_Name;
  60. CPhysCollide* m_pCollide;
  61. };
  62. static bool ModelLess( ModelCollisionLookup_t const& src1, ModelCollisionLookup_t const& src2 )
  63. {
  64. return src1.m_Name < src2.m_Name;
  65. }
  66. static CUtlRBTree<ModelCollisionLookup_t, unsigned short> s_ModelCollisionCache( 0, 32, ModelLess );
  67. static CUtlVector<int> s_LightingInfo;
  68. //-----------------------------------------------------------------------------
  69. // Gets the keyvalues from a studiohdr
  70. //-----------------------------------------------------------------------------
  71. bool StudioKeyValues( studiohdr_t* pStudioHdr, KeyValues *pValue )
  72. {
  73. if ( !pStudioHdr )
  74. return false;
  75. return pValue->LoadFromBuffer( pStudioHdr->pszName(), pStudioHdr->KeyValueText() );
  76. }
  77. //-----------------------------------------------------------------------------
  78. // Makes sure the studio model is a static prop
  79. //-----------------------------------------------------------------------------
  80. enum isstaticprop_ret
  81. {
  82. RET_VALID,
  83. RET_FAIL_NOT_MARKED_STATIC_PROP,
  84. RET_FAIL_DYNAMIC,
  85. };
  86. isstaticprop_ret IsStaticProp( studiohdr_t* pHdr )
  87. {
  88. if (!(pHdr->flags & STUDIOHDR_FLAGS_STATIC_PROP))
  89. return RET_FAIL_NOT_MARKED_STATIC_PROP;
  90. // If it's got a propdata section in the model's keyvalues, it's not allowed to be a prop_static
  91. KeyValues *modelKeyValues = new KeyValues(pHdr->pszName());
  92. if ( StudioKeyValues( pHdr, modelKeyValues ) )
  93. {
  94. KeyValues *sub = modelKeyValues->FindKey("prop_data");
  95. if ( sub )
  96. {
  97. if ( !(sub->GetInt( "allowstatic", 0 )) )
  98. {
  99. modelKeyValues->deleteThis();
  100. return RET_FAIL_DYNAMIC;
  101. }
  102. }
  103. }
  104. modelKeyValues->deleteThis();
  105. return RET_VALID;
  106. }
  107. //-----------------------------------------------------------------------------
  108. // Add static prop model to the list of models
  109. //-----------------------------------------------------------------------------
  110. static int AddStaticPropDictLump( char const* pModelName )
  111. {
  112. StaticPropDictLump_t dictLump;
  113. strncpy( dictLump.m_Name, pModelName, DETAIL_NAME_LENGTH );
  114. for (int i = s_StaticPropDictLump.Size(); --i >= 0; )
  115. {
  116. if (!memcmp(&s_StaticPropDictLump[i], &dictLump, sizeof(dictLump) ))
  117. return i;
  118. }
  119. return s_StaticPropDictLump.AddToTail( dictLump );
  120. }
  121. //-----------------------------------------------------------------------------
  122. // Load studio model vertex data from a file...
  123. //-----------------------------------------------------------------------------
  124. bool LoadStudioModel( char const* pModelName, char const* pEntityType, CUtlBuffer& buf )
  125. {
  126. if ( !g_pFullFileSystem->ReadFile( pModelName, NULL, buf ) )
  127. return false;
  128. // Check that it's valid
  129. if (strncmp ((const char *) buf.PeekGet(), "IDST", 4) &&
  130. strncmp ((const char *) buf.PeekGet(), "IDAG", 4))
  131. {
  132. return false;
  133. }
  134. studiohdr_t* pHdr = (studiohdr_t*)buf.PeekGet();
  135. Studio_ConvertStudioHdrToNewVersion( pHdr );
  136. if (pHdr->version != STUDIO_VERSION)
  137. {
  138. return false;
  139. }
  140. isstaticprop_ret isStaticProp = IsStaticProp(pHdr);
  141. if ( isStaticProp != RET_VALID )
  142. {
  143. if ( isStaticProp == RET_FAIL_NOT_MARKED_STATIC_PROP )
  144. {
  145. Warning("Error! To use model \"%s\"\n"
  146. " with %s, it must be compiled with $staticprop!\n", pModelName, pEntityType );
  147. }
  148. else if ( isStaticProp == RET_FAIL_DYNAMIC )
  149. {
  150. Warning("Error! %s using model \"%s\", which must be used on a dynamic entity (i.e. prop_physics). Deleted.\n", pEntityType, pModelName );
  151. }
  152. return false;
  153. }
  154. // ensure reset
  155. pHdr->pVertexBase = NULL;
  156. pHdr->pIndexBase = NULL;
  157. return true;
  158. }
  159. //-----------------------------------------------------------------------------
  160. // Computes a convex hull from a studio mesh
  161. //-----------------------------------------------------------------------------
  162. static CPhysConvex* ComputeConvexHull( mstudiomesh_t* pMesh )
  163. {
  164. // Generate a list of all verts in the mesh
  165. Vector** ppVerts = (Vector**)stackalloc(pMesh->numvertices * sizeof(Vector*) );
  166. const mstudio_meshvertexdata_t *vertData = pMesh->GetVertexData();
  167. Assert( vertData ); // This can only return NULL on X360 for now
  168. for (int i = 0; i < pMesh->numvertices; ++i)
  169. {
  170. ppVerts[i] = vertData->Position(i);
  171. }
  172. // Generate a convex hull from the verts
  173. return s_pPhysCollision->ConvexFromVerts( ppVerts, pMesh->numvertices );
  174. }
  175. //-----------------------------------------------------------------------------
  176. // Computes a convex hull from the studio model
  177. //-----------------------------------------------------------------------------
  178. CPhysCollide* ComputeConvexHull( studiohdr_t* pStudioHdr )
  179. {
  180. CUtlVector<CPhysConvex*> convexHulls;
  181. for (int body = 0; body < pStudioHdr->numbodyparts; ++body )
  182. {
  183. mstudiobodyparts_t *pBodyPart = pStudioHdr->pBodypart( body );
  184. for( int model = 0; model < pBodyPart->nummodels; ++model )
  185. {
  186. mstudiomodel_t *pStudioModel = pBodyPart->pModel( model );
  187. for( int mesh = 0; mesh < pStudioModel->nummeshes; ++mesh )
  188. {
  189. // Make a convex hull for each mesh
  190. // NOTE: This won't work unless the model has been compiled
  191. // with $staticprop
  192. mstudiomesh_t *pStudioMesh = pStudioModel->pMesh( mesh );
  193. convexHulls.AddToTail( ComputeConvexHull( pStudioMesh ) );
  194. }
  195. }
  196. }
  197. // Convert an array of convex elements to a compiled collision model
  198. // (this deletes the convex elements)
  199. return s_pPhysCollision->ConvertConvexToCollide( convexHulls.Base(), convexHulls.Size() );
  200. }
  201. //-----------------------------------------------------------------------------
  202. // Add, find collision model in cache
  203. //-----------------------------------------------------------------------------
  204. static CPhysCollide* GetCollisionModel( char const* pModelName )
  205. {
  206. // Convert to a common string
  207. char* pTemp = (char*)_alloca(strlen(pModelName) + 1);
  208. strcpy( pTemp, pModelName );
  209. _strlwr( pTemp );
  210. char* pSlash = strchr( pTemp, '\\' );
  211. while( pSlash )
  212. {
  213. *pSlash = '/';
  214. pSlash = strchr( pTemp, '\\' );
  215. }
  216. // Find it in the cache
  217. ModelCollisionLookup_t lookup;
  218. lookup.m_Name = pTemp;
  219. int i = s_ModelCollisionCache.Find( lookup );
  220. if (i != s_ModelCollisionCache.InvalidIndex())
  221. return s_ModelCollisionCache[i].m_pCollide;
  222. // Load the studio model file
  223. CUtlBuffer buf;
  224. if (!LoadStudioModel(pModelName, "prop_static", buf))
  225. {
  226. Warning("Error loading studio model \"%s\"!\n", pModelName );
  227. // This way we don't try to load it multiple times
  228. lookup.m_pCollide = 0;
  229. s_ModelCollisionCache.Insert( lookup );
  230. return 0;
  231. }
  232. // Compute the convex hull of the model...
  233. studiohdr_t* pStudioHdr = (studiohdr_t*)buf.PeekGet();
  234. // necessary for vertex access
  235. SetCurrentModel( pStudioHdr );
  236. lookup.m_pCollide = ComputeConvexHull( pStudioHdr );
  237. s_ModelCollisionCache.Insert( lookup );
  238. if ( !lookup.m_pCollide )
  239. {
  240. Warning("Bad geometry on \"%s\"!\n", pModelName );
  241. }
  242. // Debugging
  243. if (g_DumpStaticProps)
  244. {
  245. static int propNum = 0;
  246. char tmp[128];
  247. sprintf( tmp, "staticprop%03d.txt", propNum );
  248. DumpCollideToGlView( lookup.m_pCollide, tmp );
  249. ++propNum;
  250. }
  251. FreeCurrentModelVertexes();
  252. // Insert into cache...
  253. return lookup.m_pCollide;
  254. }
  255. //-----------------------------------------------------------------------------
  256. // Tests a single leaf against the static prop
  257. //-----------------------------------------------------------------------------
  258. static bool TestLeafAgainstCollide( int depth, int* pNodeList,
  259. Vector const& origin, QAngle const& angles, CPhysCollide* pCollide )
  260. {
  261. // Copy the planes in the node list into a list of planes
  262. float* pPlanes = (float*)_alloca(depth * 4 * sizeof(float) );
  263. int idx = 0;
  264. for (int i = depth; --i >= 0; ++idx )
  265. {
  266. int sign = (pNodeList[i] < 0) ? -1 : 1;
  267. int node = (sign < 0) ? - pNodeList[i] - 1 : pNodeList[i];
  268. dnode_t* pNode = &dnodes[node];
  269. dplane_t* pPlane = &dplanes[pNode->planenum];
  270. pPlanes[idx*4] = sign * pPlane->normal[0];
  271. pPlanes[idx*4+1] = sign * pPlane->normal[1];
  272. pPlanes[idx*4+2] = sign * pPlane->normal[2];
  273. pPlanes[idx*4+3] = sign * pPlane->dist;
  274. }
  275. // Make a convex solid out of the planes
  276. CPhysConvex* pPhysConvex = s_pPhysCollision->ConvexFromPlanes( pPlanes, depth, 0.0f );
  277. // This should never happen, but if it does, return no collision
  278. Assert( pPhysConvex );
  279. if (!pPhysConvex)
  280. return false;
  281. CPhysCollide* pLeafCollide = s_pPhysCollision->ConvertConvexToCollide( &pPhysConvex, 1 );
  282. // Collide the leaf solid with the static prop solid
  283. trace_t tr;
  284. s_pPhysCollision->TraceCollide( vec3_origin, vec3_origin, pLeafCollide, vec3_angle,
  285. pCollide, origin, angles, &tr );
  286. s_pPhysCollision->DestroyCollide( pLeafCollide );
  287. return (tr.startsolid != 0);
  288. }
  289. //-----------------------------------------------------------------------------
  290. // Find all leaves that intersect with this bbox + test against the static prop..
  291. //-----------------------------------------------------------------------------
  292. static void ComputeConvexHullLeaves_R( int node, int depth, int* pNodeList,
  293. Vector const& mins, Vector const& maxs,
  294. Vector const& origin, QAngle const& angles, CPhysCollide* pCollide,
  295. CUtlVector<unsigned short>& leafList )
  296. {
  297. Assert( pNodeList && pCollide );
  298. Vector cornermin, cornermax;
  299. while( node >= 0 )
  300. {
  301. dnode_t* pNode = &dnodes[node];
  302. dplane_t* pPlane = &dplanes[pNode->planenum];
  303. // Arbitrary split plane here
  304. for (int i = 0; i < 3; ++i)
  305. {
  306. if (pPlane->normal[i] >= 0)
  307. {
  308. cornermin[i] = mins[i];
  309. cornermax[i] = maxs[i];
  310. }
  311. else
  312. {
  313. cornermin[i] = maxs[i];
  314. cornermax[i] = mins[i];
  315. }
  316. }
  317. if (DotProduct( pPlane->normal, cornermax ) <= pPlane->dist)
  318. {
  319. // Add the node to the list of nodes
  320. pNodeList[depth] = node;
  321. ++depth;
  322. node = pNode->children[1];
  323. }
  324. else if (DotProduct( pPlane->normal, cornermin ) >= pPlane->dist)
  325. {
  326. // In this case, we are going in front of the plane. That means that
  327. // this plane must have an outward normal facing in the oppisite direction
  328. // We indicate this be storing a negative node index in the node list
  329. pNodeList[depth] = - node - 1;
  330. ++depth;
  331. node = pNode->children[0];
  332. }
  333. else
  334. {
  335. // Here the box is split by the node. First, we'll add the plane as if its
  336. // outward facing normal is in the direction of the node plane, then
  337. // we'll have to reverse it for the other child...
  338. pNodeList[depth] = node;
  339. ++depth;
  340. ComputeConvexHullLeaves_R( pNode->children[1],
  341. depth, pNodeList, mins, maxs, origin, angles, pCollide, leafList );
  342. pNodeList[depth - 1] = - node - 1;
  343. ComputeConvexHullLeaves_R( pNode->children[0],
  344. depth, pNodeList, mins, maxs, origin, angles, pCollide, leafList );
  345. return;
  346. }
  347. }
  348. Assert( pNodeList && pCollide );
  349. // Never add static props to solid leaves
  350. if ( (dleafs[-node-1].contents & CONTENTS_SOLID) == 0 )
  351. {
  352. if (TestLeafAgainstCollide( depth, pNodeList, origin, angles, pCollide ))
  353. {
  354. leafList.AddToTail( -node - 1 );
  355. }
  356. }
  357. }
  358. //-----------------------------------------------------------------------------
  359. // Places Static Props in the level
  360. //-----------------------------------------------------------------------------
  361. static void ComputeStaticPropLeaves( CPhysCollide* pCollide, Vector const& origin,
  362. QAngle const& angles, CUtlVector<unsigned short>& leafList )
  363. {
  364. // Compute an axis-aligned bounding box for the collide
  365. Vector mins, maxs;
  366. s_pPhysCollision->CollideGetAABB( &mins, &maxs, pCollide, origin, angles );
  367. // Find all leaves that intersect with the bounds
  368. int tempNodeList[1024];
  369. ComputeConvexHullLeaves_R( 0, 0, tempNodeList, mins, maxs,
  370. origin, angles, pCollide, leafList );
  371. }
  372. //-----------------------------------------------------------------------------
  373. // Computes the lighting origin
  374. //-----------------------------------------------------------------------------
  375. static bool ComputeLightingOrigin( StaticPropBuild_t const& build, Vector& lightingOrigin )
  376. {
  377. for (int i = s_LightingInfo.Count(); --i >= 0; )
  378. {
  379. int entIndex = s_LightingInfo[i];
  380. // Check against all lighting info entities
  381. char const* pTargetName = ValueForKey( &entities[entIndex], "targetname" );
  382. if (!Q_strcmp(pTargetName, build.m_pLightingOrigin))
  383. {
  384. GetVectorForKey( &entities[entIndex], "origin", lightingOrigin );
  385. return true;
  386. }
  387. }
  388. return false;
  389. }
  390. //-----------------------------------------------------------------------------
  391. // Places Static Props in the level
  392. //-----------------------------------------------------------------------------
  393. static void AddStaticPropToLump( StaticPropBuild_t const& build )
  394. {
  395. // Get the collision model
  396. CPhysCollide* pConvexHull = GetCollisionModel( build.m_pModelName );
  397. if (!pConvexHull)
  398. return;
  399. // Compute the leaves the static prop's convex hull hits
  400. CUtlVector< unsigned short > leafList;
  401. ComputeStaticPropLeaves( pConvexHull, build.m_Origin, build.m_Angles, leafList );
  402. if ( !leafList.Count() )
  403. {
  404. Warning( "Static prop %s outside the map (%.2f, %.2f, %.2f)\n", build.m_pModelName, build.m_Origin.x, build.m_Origin.y, build.m_Origin.z );
  405. return;
  406. }
  407. // Insert an element into the lump data...
  408. int i = s_StaticPropLump.AddToTail( );
  409. StaticPropLump_t& propLump = s_StaticPropLump[i];
  410. propLump.m_PropType = AddStaticPropDictLump( build.m_pModelName );
  411. VectorCopy( build.m_Origin, propLump.m_Origin );
  412. VectorCopy( build.m_Angles, propLump.m_Angles );
  413. propLump.m_FirstLeaf = s_StaticPropLeafLump.Count();
  414. propLump.m_LeafCount = leafList.Count();
  415. propLump.m_Solid = build.m_Solid;
  416. propLump.m_Skin = build.m_Skin;
  417. propLump.m_Flags = build.m_Flags;
  418. if (build.m_FadesOut)
  419. {
  420. propLump.m_Flags |= STATIC_PROP_FLAG_FADES;
  421. }
  422. propLump.m_FadeMinDist = build.m_FadeMinDist;
  423. propLump.m_FadeMaxDist = build.m_FadeMaxDist;
  424. propLump.m_flForcedFadeScale = build.m_flForcedFadeScale;
  425. propLump.m_nMinDXLevel = build.m_nMinDXLevel;
  426. propLump.m_nMaxDXLevel = build.m_nMaxDXLevel;
  427. if (build.m_pLightingOrigin && *build.m_pLightingOrigin)
  428. {
  429. if (ComputeLightingOrigin( build, propLump.m_LightingOrigin ))
  430. {
  431. propLump.m_Flags |= STATIC_PROP_USE_LIGHTING_ORIGIN;
  432. }
  433. }
  434. propLump.m_nLightmapResolutionX = build.m_LightmapResolutionX;
  435. propLump.m_nLightmapResolutionY = build.m_LightmapResolutionY;
  436. // Add the leaves to the leaf lump
  437. for (int j = 0; j < leafList.Size(); ++j)
  438. {
  439. StaticPropLeafLump_t insert;
  440. insert.m_Leaf = leafList[j];
  441. s_StaticPropLeafLump.AddToTail( insert );
  442. }
  443. }
  444. //-----------------------------------------------------------------------------
  445. // Places static props in the lump
  446. //-----------------------------------------------------------------------------
  447. static void SetLumpData( )
  448. {
  449. GameLumpHandle_t handle = g_GameLumps.GetGameLumpHandle(GAMELUMP_STATIC_PROPS);
  450. if (handle != g_GameLumps.InvalidGameLump())
  451. g_GameLumps.DestroyGameLump(handle);
  452. int dictsize = s_StaticPropDictLump.Size() * sizeof(StaticPropDictLump_t);
  453. int objsize = s_StaticPropLump.Size() * sizeof(StaticPropLump_t);
  454. int leafsize = s_StaticPropLeafLump.Size() * sizeof(StaticPropLeafLump_t);
  455. int size = dictsize + objsize + leafsize + 3 * sizeof(int);
  456. handle = g_GameLumps.CreateGameLump( GAMELUMP_STATIC_PROPS, size, 0, GAMELUMP_STATIC_PROPS_VERSION );
  457. // Serialize the data
  458. CUtlBuffer buf( g_GameLumps.GetGameLump(handle), size );
  459. buf.PutInt( s_StaticPropDictLump.Size() );
  460. if (dictsize)
  461. buf.Put( s_StaticPropDictLump.Base(), dictsize );
  462. buf.PutInt( s_StaticPropLeafLump.Size() );
  463. if (leafsize)
  464. buf.Put( s_StaticPropLeafLump.Base(), leafsize );
  465. buf.PutInt( s_StaticPropLump.Size() );
  466. if (objsize)
  467. buf.Put( s_StaticPropLump.Base(), objsize );
  468. }
  469. //-----------------------------------------------------------------------------
  470. // Places Static Props in the level
  471. //-----------------------------------------------------------------------------
  472. void EmitStaticProps()
  473. {
  474. CreateInterfaceFn physicsFactory = GetPhysicsFactory();
  475. if ( physicsFactory )
  476. {
  477. s_pPhysCollision = (IPhysicsCollision *)physicsFactory( VPHYSICS_COLLISION_INTERFACE_VERSION, NULL );
  478. if( !s_pPhysCollision )
  479. return;
  480. }
  481. // Generate a list of lighting origins, and strip them out
  482. int i;
  483. for ( i = 0; i < num_entities; ++i)
  484. {
  485. char* pEntity = ValueForKey(&entities[i], "classname");
  486. if (!Q_strcmp(pEntity, "info_lighting"))
  487. {
  488. s_LightingInfo.AddToTail(i);
  489. }
  490. }
  491. // Emit specifically specified static props
  492. for ( i = 0; i < num_entities; ++i)
  493. {
  494. char* pEntity = ValueForKey(&entities[i], "classname");
  495. if (!strcmp(pEntity, "static_prop") || !strcmp(pEntity, "prop_static"))
  496. {
  497. StaticPropBuild_t build;
  498. GetVectorForKey( &entities[i], "origin", build.m_Origin );
  499. GetAnglesForKey( &entities[i], "angles", build.m_Angles );
  500. build.m_pModelName = ValueForKey( &entities[i], "model" );
  501. build.m_Solid = IntForKey( &entities[i], "solid" );
  502. build.m_Skin = IntForKey( &entities[i], "skin" );
  503. build.m_FadeMaxDist = FloatForKey( &entities[i], "fademaxdist" );
  504. build.m_Flags = 0;//IntForKey( &entities[i], "spawnflags" ) & STATIC_PROP_WC_MASK;
  505. if (IntForKey( &entities[i], "ignorenormals" ) == 1)
  506. {
  507. build.m_Flags |= STATIC_PROP_IGNORE_NORMALS;
  508. }
  509. if (IntForKey( &entities[i], "disableshadows" ) == 1)
  510. {
  511. build.m_Flags |= STATIC_PROP_NO_SHADOW;
  512. }
  513. if (IntForKey( &entities[i], "disablevertexlighting" ) == 1)
  514. {
  515. build.m_Flags |= STATIC_PROP_NO_PER_VERTEX_LIGHTING;
  516. }
  517. if (IntForKey( &entities[i], "disableselfshadowing" ) == 1)
  518. {
  519. build.m_Flags |= STATIC_PROP_NO_SELF_SHADOWING;
  520. }
  521. if (IntForKey( &entities[i], "screenspacefade" ) == 1)
  522. {
  523. build.m_Flags |= STATIC_PROP_SCREEN_SPACE_FADE;
  524. }
  525. if (IntForKey( &entities[i], "generatelightmaps") == 0)
  526. {
  527. build.m_Flags |= STATIC_PROP_NO_PER_TEXEL_LIGHTING;
  528. build.m_LightmapResolutionX = 0;
  529. build.m_LightmapResolutionY = 0;
  530. }
  531. else
  532. {
  533. build.m_LightmapResolutionX = IntForKey( &entities[i], "lightmapresolutionx" );
  534. build.m_LightmapResolutionY = IntForKey( &entities[i], "lightmapresolutiony" );
  535. }
  536. const char *pKey = ValueForKey( &entities[i], "fadescale" );
  537. if ( pKey && pKey[0] )
  538. {
  539. build.m_flForcedFadeScale = FloatForKey( &entities[i], "fadescale" );
  540. }
  541. else
  542. {
  543. build.m_flForcedFadeScale = 1;
  544. }
  545. build.m_FadesOut = (build.m_FadeMaxDist > 0);
  546. build.m_pLightingOrigin = ValueForKey( &entities[i], "lightingorigin" );
  547. if (build.m_FadesOut)
  548. {
  549. build.m_FadeMinDist = FloatForKey( &entities[i], "fademindist" );
  550. if (build.m_FadeMinDist < 0)
  551. {
  552. build.m_FadeMinDist = build.m_FadeMaxDist;
  553. }
  554. }
  555. else
  556. {
  557. build.m_FadeMinDist = 0;
  558. }
  559. build.m_nMinDXLevel = (unsigned short)IntForKey( &entities[i], "mindxlevel" );
  560. build.m_nMaxDXLevel = (unsigned short)IntForKey( &entities[i], "maxdxlevel" );
  561. AddStaticPropToLump( build );
  562. // strip this ent from the .bsp file
  563. entities[i].epairs = 0;
  564. }
  565. }
  566. // Strip out lighting origins; has to be done here because they are used when
  567. // static props are made
  568. for ( i = s_LightingInfo.Count(); --i >= 0; )
  569. {
  570. // strip this ent from the .bsp file
  571. entities[s_LightingInfo[i]].epairs = 0;
  572. }
  573. SetLumpData( );
  574. }
  575. static studiohdr_t *g_pActiveStudioHdr;
  576. static void SetCurrentModel( studiohdr_t *pStudioHdr )
  577. {
  578. // track the correct model
  579. g_pActiveStudioHdr = pStudioHdr;
  580. }
  581. static void FreeCurrentModelVertexes()
  582. {
  583. Assert( g_pActiveStudioHdr );
  584. if ( g_pActiveStudioHdr->pVertexBase )
  585. {
  586. free( g_pActiveStudioHdr->pVertexBase );
  587. g_pActiveStudioHdr->pVertexBase = NULL;
  588. }
  589. }
  590. const vertexFileHeader_t * mstudiomodel_t::CacheVertexData( void * pModelData )
  591. {
  592. char fileName[260];
  593. FileHandle_t fileHandle;
  594. vertexFileHeader_t *pVvdHdr;
  595. Assert( pModelData == NULL );
  596. Assert( g_pActiveStudioHdr );
  597. if ( g_pActiveStudioHdr->pVertexBase )
  598. {
  599. return (vertexFileHeader_t *)g_pActiveStudioHdr->pVertexBase;
  600. }
  601. // mandatory callback to make requested data resident
  602. // load and persist the vertex file
  603. strcpy( fileName, "models/" );
  604. strcat( fileName, g_pActiveStudioHdr->pszName() );
  605. Q_StripExtension( fileName, fileName, sizeof( fileName ) );
  606. strcat( fileName, ".vvd" );
  607. // load the model
  608. fileHandle = g_pFileSystem->Open( fileName, "rb" );
  609. if ( !fileHandle )
  610. {
  611. Error( "Unable to load vertex data \"%s\"\n", fileName );
  612. }
  613. // Get the file size
  614. int size = g_pFileSystem->Size( fileHandle );
  615. if (size == 0)
  616. {
  617. g_pFileSystem->Close( fileHandle );
  618. Error( "Bad size for vertex data \"%s\"\n", fileName );
  619. }
  620. pVvdHdr = (vertexFileHeader_t *)malloc(size);
  621. g_pFileSystem->Read( pVvdHdr, size, fileHandle );
  622. g_pFileSystem->Close( fileHandle );
  623. // check header
  624. if (pVvdHdr->id != MODEL_VERTEX_FILE_ID)
  625. {
  626. Error("Error Vertex File %s id %d should be %d\n", fileName, pVvdHdr->id, MODEL_VERTEX_FILE_ID);
  627. }
  628. if (pVvdHdr->version != MODEL_VERTEX_FILE_VERSION)
  629. {
  630. Error("Error Vertex File %s version %d should be %d\n", fileName, pVvdHdr->version, MODEL_VERTEX_FILE_VERSION);
  631. }
  632. if (pVvdHdr->checksum != g_pActiveStudioHdr->checksum)
  633. {
  634. Error("Error Vertex File %s checksum %d should be %d\n", fileName, pVvdHdr->checksum, g_pActiveStudioHdr->checksum);
  635. }
  636. g_pActiveStudioHdr->pVertexBase = (void*)pVvdHdr;
  637. return pVvdHdr;
  638. }