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.

966 lines
29 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 <windows.h>
  9. #include "vbsp.h"
  10. #include "bsplib.h"
  11. #include "KeyValues.h"
  12. #include "utlsymbol.h"
  13. #include "utlvector.h"
  14. #include <io.h>
  15. #include "bspfile.h"
  16. #include "utilmatlib.h"
  17. #include "gamebspfile.h"
  18. #include "mathlib/VMatrix.h"
  19. #include "materialpatch.h"
  20. #include "pacifier.h"
  21. #include "vstdlib/random.h"
  22. #include "builddisp.h"
  23. #include "disp_vbsp.h"
  24. #include "UtlBuffer.h"
  25. #include "CollisionUtils.h"
  26. #include <float.h>
  27. #include "UtlLinkedList.h"
  28. #include "byteswap.h"
  29. #include "writebsp.h"
  30. //-----------------------------------------------------------------------------
  31. // Information about particular detail object types
  32. //-----------------------------------------------------------------------------
  33. enum
  34. {
  35. MODELFLAG_UPRIGHT = 0x1,
  36. };
  37. struct DetailModel_t
  38. {
  39. CUtlSymbol m_ModelName;
  40. float m_Amount;
  41. float m_MinCosAngle;
  42. float m_MaxCosAngle;
  43. int m_Flags;
  44. int m_Orientation;
  45. int m_Type;
  46. Vector2D m_Pos[2];
  47. Vector2D m_Tex[2];
  48. float m_flRandomScaleStdDev;
  49. unsigned char m_ShapeSize;
  50. unsigned char m_ShapeAngle;
  51. unsigned char m_SwayAmount;
  52. };
  53. struct DetailObjectGroup_t
  54. {
  55. float m_Alpha;
  56. CUtlVector< DetailModel_t > m_Models;
  57. };
  58. struct DetailObject_t
  59. {
  60. CUtlSymbol m_Name;
  61. float m_Density;
  62. CUtlVector< DetailObjectGroup_t > m_Groups;
  63. bool operator==(const DetailObject_t& src ) const
  64. {
  65. return src.m_Name == m_Name;
  66. }
  67. };
  68. static CUtlVector<DetailObject_t> s_DetailObjectDict;
  69. //-----------------------------------------------------------------------------
  70. // Error checking.. make sure the model is valid + is a static prop
  71. //-----------------------------------------------------------------------------
  72. struct StaticPropLookup_t
  73. {
  74. CUtlSymbol m_ModelName;
  75. bool m_IsValid;
  76. };
  77. static bool StaticLess( StaticPropLookup_t const& src1, StaticPropLookup_t const& src2 )
  78. {
  79. return src1.m_ModelName < src2.m_ModelName;
  80. }
  81. static CUtlRBTree< StaticPropLookup_t, unsigned short > s_StaticPropLookup( 0, 32, StaticLess );
  82. //-----------------------------------------------------------------------------
  83. // These puppies are used to construct the game lumps
  84. //-----------------------------------------------------------------------------
  85. static CUtlVector<DetailObjectDictLump_t> s_DetailObjectDictLump;
  86. static CUtlVector<DetailObjectLump_t> s_DetailObjectLump;
  87. static CUtlVector<DetailSpriteDictLump_t> s_DetailSpriteDictLump;
  88. //-----------------------------------------------------------------------------
  89. // Parses the key-value pairs in the detail.rad file
  90. //-----------------------------------------------------------------------------
  91. static void ParseDetailGroup( int detailId, KeyValues* pGroupKeyValues )
  92. {
  93. // Sort the group by alpha
  94. float alpha = pGroupKeyValues->GetFloat( "alpha", 1.0f );
  95. int i = s_DetailObjectDict[detailId].m_Groups.Count();
  96. while ( --i >= 0 )
  97. {
  98. if (alpha > s_DetailObjectDict[detailId].m_Groups[i].m_Alpha)
  99. break;
  100. }
  101. // Insert after the first guy who's more transparent that we are!
  102. i = s_DetailObjectDict[detailId].m_Groups.InsertAfter(i);
  103. DetailObjectGroup_t& group = s_DetailObjectDict[detailId].m_Groups[i];
  104. group.m_Alpha = alpha;
  105. // Add in all the model groups
  106. KeyValues* pIter = pGroupKeyValues->GetFirstSubKey();
  107. float totalAmount = 0.0f;
  108. while( pIter )
  109. {
  110. if (pIter->GetFirstSubKey())
  111. {
  112. int i = group.m_Models.AddToTail();
  113. DetailModel_t &model = group.m_Models[i];
  114. model.m_ModelName = pIter->GetString( "model", 0 );
  115. if (model.m_ModelName != UTL_INVAL_SYMBOL)
  116. {
  117. model.m_Type = DETAIL_PROP_TYPE_MODEL;
  118. }
  119. else
  120. {
  121. const char *pSpriteData = pIter->GetString( "sprite", 0 );
  122. if (pSpriteData)
  123. {
  124. const char *pProcModelType = pIter->GetString( "sprite_shape", 0 );
  125. if ( pProcModelType )
  126. {
  127. if ( !Q_stricmp( pProcModelType, "cross" ) )
  128. {
  129. model.m_Type = DETAIL_PROP_TYPE_SHAPE_CROSS;
  130. }
  131. else if ( !Q_stricmp( pProcModelType, "tri" ) )
  132. {
  133. model.m_Type = DETAIL_PROP_TYPE_SHAPE_TRI;
  134. }
  135. else
  136. model.m_Type = DETAIL_PROP_TYPE_SPRITE;
  137. }
  138. else
  139. {
  140. // card sprite
  141. model.m_Type = DETAIL_PROP_TYPE_SPRITE;
  142. }
  143. model.m_Tex[0].Init();
  144. model.m_Tex[1].Init();
  145. float x = 0, y = 0, flWidth = 64, flHeight = 64, flTextureSize = 512;
  146. int nValid = sscanf( pSpriteData, "%f %f %f %f %f", &x, &y, &flWidth, &flHeight, &flTextureSize );
  147. if ( (nValid != 5) || (flTextureSize == 0) )
  148. {
  149. Error( "Invalid arguments to \"sprite\" in detail.vbsp (model %s)!\n", model.m_ModelName.String() );
  150. }
  151. model.m_Tex[0].x = ( x + 0.5f ) / flTextureSize;
  152. model.m_Tex[0].y = ( y + 0.5f ) / flTextureSize;
  153. model.m_Tex[1].x = ( x + flWidth - 0.5f ) / flTextureSize;
  154. model.m_Tex[1].y = ( y + flHeight - 0.5f ) / flTextureSize;
  155. model.m_Pos[0].Init( -10, 20 );
  156. model.m_Pos[1].Init( 10, 0 );
  157. pSpriteData = pIter->GetString( "spritesize", 0 );
  158. if (pSpriteData)
  159. {
  160. sscanf( pSpriteData, "%f %f %f %f", &x, &y, &flWidth, &flHeight );
  161. float ox = flWidth * x;
  162. float oy = flHeight * y;
  163. model.m_Pos[0].x = -ox;
  164. model.m_Pos[0].y = flHeight - oy;
  165. model.m_Pos[1].x = flWidth - ox;
  166. model.m_Pos[1].y = -oy;
  167. }
  168. model.m_flRandomScaleStdDev = pIter->GetFloat( "spriterandomscale", 0.0f );
  169. // sway is a percent of max sway, cl_detail_max_sway
  170. float flSway = clamp( pIter->GetFloat( "sway", 0.0f ), 0.0, 1.0 );
  171. model.m_SwayAmount = (unsigned char)( 255.0 * flSway );
  172. // shape angle
  173. // for the tri shape, this is the angle each side is fanned out
  174. model.m_ShapeAngle = pIter->GetInt( "shape_angle", 0 );
  175. // shape size
  176. // for the tri shape, this is the distance from the origin to the center of a side
  177. float flShapeSize = clamp( pIter->GetFloat( "shape_size", 0.0f ), 0.0, 1.0 );
  178. model.m_ShapeSize = (unsigned char)( 255.0 * flShapeSize );
  179. }
  180. }
  181. model.m_Amount = pIter->GetFloat( "amount", 1.0 ) + totalAmount;
  182. totalAmount = model.m_Amount;
  183. model.m_Flags = 0;
  184. if (pIter->GetInt( "upright", 0 ))
  185. {
  186. model.m_Flags |= MODELFLAG_UPRIGHT;
  187. }
  188. // These are used to prevent emission on steep surfaces
  189. float minAngle = pIter->GetFloat( "minAngle", 180 );
  190. float maxAngle = pIter->GetFloat( "maxAngle", 180 );
  191. model.m_MinCosAngle = cos(minAngle * M_PI / 180.f);
  192. model.m_MaxCosAngle = cos(maxAngle * M_PI / 180.f);
  193. model.m_Orientation = pIter->GetInt( "detailOrientation", 0 );
  194. // Make sure minAngle < maxAngle
  195. if ( model.m_MinCosAngle < model.m_MaxCosAngle)
  196. {
  197. model.m_MinCosAngle = model.m_MaxCosAngle;
  198. }
  199. }
  200. pIter = pIter->GetNextKey();
  201. }
  202. // renormalize the amount if the total > 1
  203. if (totalAmount > 1.0f)
  204. {
  205. for (i = 0; i < group.m_Models.Count(); ++i)
  206. {
  207. group.m_Models[i].m_Amount /= totalAmount;
  208. }
  209. }
  210. }
  211. //-----------------------------------------------------------------------------
  212. // Parses the key-value pairs in the detail.vbsp file
  213. //-----------------------------------------------------------------------------
  214. static void ParseDetailObjectFile( KeyValues& keyValues )
  215. {
  216. // Iterate over all detail object groups...
  217. KeyValues* pIter;
  218. for( pIter = keyValues.GetFirstSubKey(); pIter; pIter = pIter->GetNextKey() )
  219. {
  220. if (!pIter->GetFirstSubKey())
  221. continue;
  222. int i = s_DetailObjectDict.AddToTail( );
  223. s_DetailObjectDict[i].m_Name = pIter->GetName() ;
  224. s_DetailObjectDict[i].m_Density = pIter->GetFloat( "density", 0.0f );
  225. // Iterate over all detail object groups...
  226. KeyValues* pIterGroups = pIter->GetFirstSubKey();
  227. while( pIterGroups )
  228. {
  229. if (pIterGroups->GetFirstSubKey())
  230. {
  231. ParseDetailGroup( i, pIterGroups );
  232. }
  233. pIterGroups = pIterGroups->GetNextKey();
  234. }
  235. }
  236. }
  237. //-----------------------------------------------------------------------------
  238. // Finds the name of the detail.vbsp file to use
  239. //-----------------------------------------------------------------------------
  240. static const char *FindDetailVBSPName( void )
  241. {
  242. for( int i = 0; i < num_entities; i++ )
  243. {
  244. char* pEntity = ValueForKey( &entities[i], "classname" );
  245. if ( !strcmp( pEntity, "worldspawn" ) )
  246. {
  247. const char *pDetailVBSP = ValueForKey( &entities[i], "detailvbsp" );
  248. if ( !pDetailVBSP || !pDetailVBSP[0] )
  249. {
  250. pDetailVBSP = "detail.vbsp";
  251. }
  252. return pDetailVBSP;
  253. }
  254. }
  255. return "detail.vbsp";
  256. }
  257. //-----------------------------------------------------------------------------
  258. // Loads up the detail object dictionary
  259. //-----------------------------------------------------------------------------
  260. void LoadEmitDetailObjectDictionary( const char* pGameDir )
  261. {
  262. // Set the required global lights filename and try looking in qproject
  263. const char *pDetailVBSP = FindDetailVBSPName();
  264. KeyValues * values = new KeyValues( pDetailVBSP );
  265. if ( values->LoadFromFile( g_pFileSystem, pDetailVBSP ) )
  266. {
  267. ParseDetailObjectFile( *values );
  268. }
  269. values->deleteThis();
  270. }
  271. //-----------------------------------------------------------------------------
  272. // Selects a detail group
  273. //-----------------------------------------------------------------------------
  274. static int SelectGroup( const DetailObject_t& detail, float alpha )
  275. {
  276. // Find the two groups whose alpha we're between...
  277. int start, end;
  278. for ( start = 0; start < detail.m_Groups.Count() - 1; ++start )
  279. {
  280. if (alpha < detail.m_Groups[start+1].m_Alpha)
  281. break;
  282. }
  283. end = start + 1;
  284. if (end >= detail.m_Groups.Count())
  285. --end;
  286. if (start == end)
  287. return start;
  288. // Figure out how far we are between start and end...
  289. float dist = 0.0f;
  290. float dAlpha = (detail.m_Groups[end].m_Alpha - detail.m_Groups[start].m_Alpha);
  291. if (dAlpha != 0.0f)
  292. {
  293. dist = (alpha - detail.m_Groups[start].m_Alpha) / dAlpha;
  294. }
  295. // Pick a number, any number...
  296. float r = rand() / (float)VALVE_RAND_MAX;
  297. // When dist == 0, we *always* want start.
  298. // When dist == 1, we *always* want end
  299. // That's why this logic looks a little reversed
  300. return (r > dist) ? start : end;
  301. }
  302. //-----------------------------------------------------------------------------
  303. // Selects a detail object
  304. //-----------------------------------------------------------------------------
  305. static int SelectDetail( DetailObjectGroup_t const& group )
  306. {
  307. // Pick a number, any number...
  308. float r = rand() / (float)VALVE_RAND_MAX;
  309. // Look through the list of models + pick the one associated with this number
  310. for ( int i = 0; i < group.m_Models.Count(); ++i )
  311. {
  312. if (r <= group.m_Models[i].m_Amount)
  313. return i;
  314. }
  315. return -1;
  316. }
  317. //-----------------------------------------------------------------------------
  318. // Adds a detail dictionary element (expected to oftentimes be shared)
  319. //-----------------------------------------------------------------------------
  320. static int AddDetailDictLump( const char* pModelName )
  321. {
  322. DetailObjectDictLump_t dictLump;
  323. strncpy( dictLump.m_Name, pModelName, DETAIL_NAME_LENGTH );
  324. for (int i = s_DetailObjectDictLump.Count(); --i >= 0; )
  325. {
  326. if (!memcmp(&s_DetailObjectDictLump[i], &dictLump, sizeof(dictLump) ))
  327. return i;
  328. }
  329. return s_DetailObjectDictLump.AddToTail( dictLump );
  330. }
  331. static int AddDetailSpriteDictLump( const Vector2D *pPos, const Vector2D *pTex )
  332. {
  333. DetailSpriteDictLump_t dictLump;
  334. dictLump.m_UL = pPos[0];
  335. dictLump.m_LR = pPos[1];
  336. dictLump.m_TexUL = pTex[0];
  337. dictLump.m_TexLR = pTex[1];
  338. for (int i = s_DetailSpriteDictLump.Count(); --i >= 0; )
  339. {
  340. if (!memcmp(&s_DetailSpriteDictLump[i], &dictLump, sizeof(dictLump) ))
  341. return i;
  342. }
  343. return s_DetailSpriteDictLump.AddToTail( dictLump );
  344. }
  345. //-----------------------------------------------------------------------------
  346. // Computes the leaf that the detail lies in
  347. //-----------------------------------------------------------------------------
  348. static int ComputeDetailLeaf( const Vector& pt )
  349. {
  350. int node = 0;
  351. while( node >= 0 )
  352. {
  353. dnode_t* pNode = &dnodes[node];
  354. dplane_t* pPlane = &dplanes[pNode->planenum];
  355. if (DotProduct(pt, pPlane->normal) < pPlane->dist)
  356. node = pNode->children[1];
  357. else
  358. node = pNode->children[0];
  359. }
  360. return - node - 1;
  361. }
  362. //-----------------------------------------------------------------------------
  363. // Make sure the details are compiled with static prop
  364. //-----------------------------------------------------------------------------
  365. static bool IsModelValid( const char* pModelName )
  366. {
  367. StaticPropLookup_t lookup;
  368. lookup.m_ModelName = pModelName;
  369. int i = s_StaticPropLookup.Find( lookup );
  370. if (i != s_StaticPropLookup.InvalidIndex() )
  371. return s_StaticPropLookup[i].m_IsValid;
  372. CUtlBuffer buf;
  373. lookup.m_IsValid = LoadStudioModel( pModelName, "detail_prop", buf );
  374. if (!lookup.m_IsValid)
  375. {
  376. Warning("Error loading studio model \"%s\"!\n", pModelName );
  377. }
  378. s_StaticPropLookup.Insert( lookup );
  379. return lookup.m_IsValid;
  380. }
  381. //-----------------------------------------------------------------------------
  382. // Add a detail to the lump.
  383. //-----------------------------------------------------------------------------
  384. static int s_nDetailOverflow = 0;
  385. static void AddDetailToLump( const char* pModelName, const Vector& pt, const QAngle& angles, int nOrientation )
  386. {
  387. Assert( pt.IsValid() && angles.IsValid() );
  388. // Make sure the model is valid...
  389. if (!IsModelValid(pModelName))
  390. return;
  391. if (s_DetailObjectLump.Count() == 65535)
  392. {
  393. ++s_nDetailOverflow;
  394. return;
  395. }
  396. // Insert an element into the object dictionary if it aint there...
  397. int i = s_DetailObjectLump.AddToTail( );
  398. DetailObjectLump_t& objectLump = s_DetailObjectLump[i];
  399. objectLump.m_DetailModel = AddDetailDictLump( pModelName );
  400. VectorCopy( angles, objectLump.m_Angles );
  401. VectorCopy( pt, objectLump.m_Origin );
  402. objectLump.m_Leaf = ComputeDetailLeaf(pt);
  403. objectLump.m_Lighting.r = 255;
  404. objectLump.m_Lighting.g = 255;
  405. objectLump.m_Lighting.b = 255;
  406. objectLump.m_Lighting.exponent = 0;
  407. objectLump.m_LightStyles = 0;
  408. objectLump.m_LightStyleCount = 0;
  409. objectLump.m_Orientation = nOrientation;
  410. objectLump.m_Type = DETAIL_PROP_TYPE_MODEL;
  411. }
  412. //-----------------------------------------------------------------------------
  413. // Add a detail sprite to the lump.
  414. //-----------------------------------------------------------------------------
  415. static void AddDetailSpriteToLump( const Vector &vecOrigin, const QAngle &vecAngles, int nOrientation,
  416. const Vector2D *pPos, const Vector2D *pTex, float flScale, int iType,
  417. int iShapeAngle = 0, int iShapeSize = 0, int iSwayAmount = 0 )
  418. {
  419. // Insert an element into the object dictionary if it aint there...
  420. int i = s_DetailObjectLump.AddToTail( );
  421. if (i >= 65535)
  422. {
  423. Error( "Error! Too many detail props emitted on this map! (64K max!)n" );
  424. }
  425. DetailObjectLump_t& objectLump = s_DetailObjectLump[i];
  426. objectLump.m_DetailModel = AddDetailSpriteDictLump( pPos, pTex );
  427. VectorCopy( vecAngles, objectLump.m_Angles );
  428. VectorCopy( vecOrigin, objectLump.m_Origin );
  429. objectLump.m_Leaf = ComputeDetailLeaf(vecOrigin);
  430. objectLump.m_Lighting.r = 255;
  431. objectLump.m_Lighting.g = 255;
  432. objectLump.m_Lighting.b = 255;
  433. objectLump.m_Lighting.exponent = 0;
  434. objectLump.m_LightStyles = 0;
  435. objectLump.m_LightStyleCount = 0;
  436. objectLump.m_Orientation = nOrientation;
  437. objectLump.m_Type = iType;
  438. objectLump.m_flScale = flScale;
  439. objectLump.m_ShapeAngle = iShapeAngle;
  440. objectLump.m_ShapeSize = iShapeSize;
  441. objectLump.m_SwayAmount = iSwayAmount;
  442. }
  443. static void AddDetailSpriteToLump( const Vector &vecOrigin, const QAngle &vecAngles, DetailModel_t const& model, float flScale )
  444. {
  445. AddDetailSpriteToLump( vecOrigin,
  446. vecAngles,
  447. model.m_Orientation,
  448. model.m_Pos,
  449. model.m_Tex,
  450. flScale,
  451. model.m_Type,
  452. model.m_ShapeAngle,
  453. model.m_ShapeSize,
  454. model.m_SwayAmount );
  455. }
  456. //-----------------------------------------------------------------------------
  457. // Got a detail! Place it on the surface...
  458. //-----------------------------------------------------------------------------
  459. // BUGBUG: When the global optimizer is on, "normal" gets trashed in this function
  460. // (only when not in the debugger?)
  461. // Printing the values of normal at the bottom of the function fixes it as does
  462. // disabling global optimizations.
  463. static void PlaceDetail( DetailModel_t const& model, const Vector& pt, const Vector& normal )
  464. {
  465. // But only place it on the surface if it meets the angle constraints...
  466. float cosAngle = normal.z;
  467. // Never emit if the angle's too steep
  468. if (cosAngle < model.m_MaxCosAngle)
  469. return;
  470. // If it's between min + max, flip a coin...
  471. if (cosAngle < model.m_MinCosAngle)
  472. {
  473. float probability = (cosAngle - model.m_MaxCosAngle) /
  474. (model.m_MinCosAngle - model.m_MaxCosAngle);
  475. float t = rand() / (float)VALVE_RAND_MAX;
  476. if (t > probability)
  477. return;
  478. }
  479. // Compute the orientation of the detail
  480. QAngle angles;
  481. if (model.m_Flags & MODELFLAG_UPRIGHT)
  482. {
  483. // If it's upright, we just select a random yaw
  484. angles.Init( 0, 360.0f * rand() / (float)VALVE_RAND_MAX, 0.0f );
  485. }
  486. else
  487. {
  488. // It's not upright, so it must conform to the ground. Choose
  489. // a random orientation based on the surface normal
  490. Vector zaxis;
  491. VectorCopy( normal, zaxis );
  492. VectorNormalize( zaxis );
  493. // Choose any two arbitrary axes which are perpendicular to the normal
  494. Vector xaxis( 1, 0, 0 );
  495. if (fabs(xaxis.Dot(zaxis)) - 1.0 > -1e-3)
  496. xaxis.Init( 0, 1, 0 );
  497. Vector yaxis;
  498. CrossProduct( zaxis, xaxis, yaxis );
  499. VectorNormalize( yaxis );
  500. CrossProduct( yaxis, zaxis, xaxis );
  501. VectorNormalize( xaxis );
  502. VMatrix matrix;
  503. matrix.SetBasisVectors( xaxis, yaxis, zaxis );
  504. matrix.SetTranslation( vec3_origin );
  505. float rotAngle = 360.0f * rand() / (float)VALVE_RAND_MAX;
  506. VMatrix rot = SetupMatrixAxisRot( Vector( 0, 0, 1 ), rotAngle );
  507. matrix = matrix * rot;
  508. MatrixToAngles( matrix, angles );
  509. }
  510. // FIXME: We may also want a purely random rotation too
  511. // Insert an element into the object dictionary if it aint there...
  512. switch ( model.m_Type )
  513. {
  514. case DETAIL_PROP_TYPE_MODEL:
  515. AddDetailToLump( model.m_ModelName.String(), pt, angles, model.m_Orientation );
  516. break;
  517. // Sprites and procedural models made from sprites
  518. case DETAIL_PROP_TYPE_SPRITE:
  519. default:
  520. {
  521. float flScale = 1.0f;
  522. if ( model.m_flRandomScaleStdDev != 0.0f )
  523. {
  524. flScale = fabs( RandomGaussianFloat( 1.0f, model.m_flRandomScaleStdDev ) );
  525. }
  526. AddDetailSpriteToLump( pt, angles, model, flScale );
  527. }
  528. break;
  529. }
  530. }
  531. //-----------------------------------------------------------------------------
  532. // Places Detail Objects on a face
  533. //-----------------------------------------------------------------------------
  534. static void EmitDetailObjectsOnFace( dface_t* pFace, DetailObject_t& detail )
  535. {
  536. if (pFace->numedges < 3)
  537. return;
  538. // We're going to pick a bunch of random points, and then probabilistically
  539. // decide whether or not to plant a detail object there.
  540. // Turn the face into a bunch of polygons, and compute the area of each
  541. int* pSurfEdges = &dsurfedges[pFace->firstedge];
  542. int vertexIdx = (pSurfEdges[0] < 0);
  543. int firstVertexIndex = dedges[abs(pSurfEdges[0])].v[vertexIdx];
  544. dvertex_t* pFirstVertex = &dvertexes[firstVertexIndex];
  545. for (int i = 1; i < pFace->numedges - 1; ++i )
  546. {
  547. int vertexIdx = (pSurfEdges[i] < 0);
  548. dedge_t* pEdge = &dedges[abs(pSurfEdges[i])];
  549. // Compute two triangle edges
  550. Vector e1, e2;
  551. VectorSubtract( dvertexes[pEdge->v[vertexIdx]].point, pFirstVertex->point, e1 );
  552. VectorSubtract( dvertexes[pEdge->v[1 - vertexIdx]].point, pFirstVertex->point, e2 );
  553. // Compute the triangle area
  554. Vector areaVec;
  555. CrossProduct( e1, e2, areaVec );
  556. float normalLength = areaVec.Length();
  557. float area = 0.5f * normalLength;
  558. // Compute the number of samples to take
  559. int numSamples = area * detail.m_Density * 0.000001;
  560. // Now take a sample, and randomly place an object there
  561. for (int i = 0; i < numSamples; ++i )
  562. {
  563. // Create a random sample...
  564. float u = rand() / (float)VALVE_RAND_MAX;
  565. float v = rand() / (float)VALVE_RAND_MAX;
  566. if (v > 1.0f - u)
  567. {
  568. u = 1.0f - u;
  569. v = 1.0f - v;
  570. assert( u + v <= 1.0f );
  571. }
  572. // Compute alpha
  573. float alpha = 1.0f;
  574. // Select a group based on the alpha value
  575. int group = SelectGroup( detail, alpha );
  576. // Now that we've got a group, choose a detail
  577. int model = SelectDetail( detail.m_Groups[group] );
  578. if (model < 0)
  579. continue;
  580. // Got a detail! Place it on the surface...
  581. Vector pt, normal;
  582. VectorMA( pFirstVertex->point, u, e1, pt );
  583. VectorMA( pt, v, e2, pt );
  584. VectorDivide( areaVec, -normalLength, normal );
  585. PlaceDetail( detail.m_Groups[group].m_Models[model], pt, normal );
  586. }
  587. }
  588. }
  589. //-----------------------------------------------------------------------------
  590. // Places Detail Objects on a face
  591. //-----------------------------------------------------------------------------
  592. static float ComputeDisplacementFaceArea( dface_t* pFace )
  593. {
  594. float area = 0.0f;
  595. // Compute the area of the base face
  596. int* pSurfEdges = &dsurfedges[pFace->firstedge];
  597. int vertexIdx = (pSurfEdges[0] < 0);
  598. int firstVertexIndex = dedges[abs(pSurfEdges[0])].v[vertexIdx];
  599. dvertex_t* pFirstVertex = &dvertexes[firstVertexIndex];
  600. for (int i = 1; i <= 2; ++i )
  601. {
  602. int vertexIdx = (pSurfEdges[i] < 0);
  603. dedge_t* pEdge = &dedges[abs(pSurfEdges[i])];
  604. // Compute two triangle edges
  605. Vector e1, e2;
  606. VectorSubtract( dvertexes[pEdge->v[vertexIdx]].point, pFirstVertex->point, e1 );
  607. VectorSubtract( dvertexes[pEdge->v[1 - vertexIdx]].point, pFirstVertex->point, e2 );
  608. // Compute the triangle area
  609. Vector areaVec;
  610. CrossProduct( e1, e2, areaVec );
  611. float normalLength = areaVec.Length();
  612. area += 0.5f * normalLength;
  613. }
  614. return area;
  615. }
  616. //-----------------------------------------------------------------------------
  617. // Places Detail Objects on a face
  618. //-----------------------------------------------------------------------------
  619. static void EmitDetailObjectsOnDisplacementFace( dface_t* pFace,
  620. DetailObject_t& detail, CCoreDispInfo& coreDispInfo )
  621. {
  622. assert(pFace->numedges == 4);
  623. // We're going to pick a bunch of random points, and then probabilistically
  624. // decide whether or not to plant a detail object there.
  625. // Compute the area of the base face
  626. float area = ComputeDisplacementFaceArea( pFace );
  627. // Compute the number of samples to take
  628. int numSamples = area * detail.m_Density * 0.000001;
  629. // Now take a sample, and randomly place an object there
  630. for (int i = 0; i < numSamples; ++i )
  631. {
  632. // Create a random sample...
  633. float u = rand() / (float)VALVE_RAND_MAX;
  634. float v = rand() / (float)VALVE_RAND_MAX;
  635. // Compute alpha
  636. float alpha;
  637. Vector pt, normal;
  638. coreDispInfo.GetPositionOnSurface( u, v, pt, &normal, &alpha );
  639. alpha /= 255.0f;
  640. // Select a group based on the alpha value
  641. int group = SelectGroup( detail, alpha );
  642. // Now that we've got a group, choose a detail
  643. int model = SelectDetail( detail.m_Groups[group] );
  644. if (model < 0)
  645. continue;
  646. // Got a detail! Place it on the surface...
  647. PlaceDetail( detail.m_Groups[group].m_Models[model], pt, normal );
  648. }
  649. }
  650. //-----------------------------------------------------------------------------
  651. // Sort detail objects by leaf
  652. //-----------------------------------------------------------------------------
  653. static int SortFunc( const void *arg1, const void *arg2 )
  654. {
  655. int nDelta = ((DetailObjectLump_t*)arg1)->m_Leaf - ((DetailObjectLump_t*)arg2)->m_Leaf;
  656. if ( nDelta < 0 )
  657. return -1;
  658. if ( nDelta > 0 )
  659. return 1;
  660. return 0;
  661. }
  662. //-----------------------------------------------------------------------------
  663. // Places Detail Objects in the lump
  664. //-----------------------------------------------------------------------------
  665. static void SetLumpData( )
  666. {
  667. // Sort detail props by leaf
  668. qsort( s_DetailObjectLump.Base(), s_DetailObjectLump.Count(), sizeof(DetailObjectLump_t), SortFunc );
  669. GameLumpHandle_t handle = g_GameLumps.GetGameLumpHandle(GAMELUMP_DETAIL_PROPS);
  670. if (handle != g_GameLumps.InvalidGameLump())
  671. {
  672. g_GameLumps.DestroyGameLump(handle);
  673. }
  674. int nDictSize = s_DetailObjectDictLump.Count() * sizeof(DetailObjectDictLump_t);
  675. int nSpriteDictSize = s_DetailSpriteDictLump.Count() * sizeof(DetailSpriteDictLump_t);
  676. int nObjSize = s_DetailObjectLump.Count() * sizeof(DetailObjectLump_t);
  677. int nSize = nDictSize + nSpriteDictSize + nObjSize + (3 * sizeof(int));
  678. handle = g_GameLumps.CreateGameLump( GAMELUMP_DETAIL_PROPS, nSize, 0, GAMELUMP_DETAIL_PROPS_VERSION );
  679. // Serialize the data
  680. CUtlBuffer buf( g_GameLumps.GetGameLump(handle), nSize );
  681. buf.PutInt( s_DetailObjectDictLump.Count() );
  682. if (nDictSize)
  683. {
  684. buf.Put( s_DetailObjectDictLump.Base(), nDictSize );
  685. }
  686. buf.PutInt( s_DetailSpriteDictLump.Count() );
  687. if (nSpriteDictSize)
  688. {
  689. buf.Put( s_DetailSpriteDictLump.Base(), nSpriteDictSize );
  690. }
  691. buf.PutInt( s_DetailObjectLump.Count() );
  692. if (nObjSize)
  693. {
  694. buf.Put( s_DetailObjectLump.Base(), nObjSize );
  695. }
  696. }
  697. //-----------------------------------------------------------------------------
  698. // Places Detail Objects in the level
  699. //-----------------------------------------------------------------------------
  700. void EmitDetailModels()
  701. {
  702. StartPacifier("Placing detail props : ");
  703. // Place stuff on each face
  704. dface_t* pFace = dfaces;
  705. for (int j = 0; j < numfaces; ++j)
  706. {
  707. UpdatePacifier( (float)j / (float)numfaces );
  708. // Get at the material associated with this face
  709. texinfo_t* pTexInfo = &texinfo[pFace[j].texinfo];
  710. dtexdata_t* pTexData = GetTexData( pTexInfo->texdata );
  711. // Try to get at the material
  712. bool found;
  713. MaterialSystemMaterial_t handle =
  714. FindOriginalMaterial( TexDataStringTable_GetString( pTexData->nameStringTableID ),
  715. &found, false );
  716. if (!found)
  717. continue;
  718. // See if its got any detail objects on it
  719. const char* pDetailType = GetMaterialVar( handle, "%detailtype" );
  720. if (!pDetailType)
  721. continue;
  722. // Get the detail type...
  723. DetailObject_t search;
  724. search.m_Name = pDetailType;
  725. int objectType = s_DetailObjectDict.Find(search);
  726. if (objectType < 0)
  727. {
  728. Warning("Material %s uses unknown detail object type %s!\n",
  729. TexDataStringTable_GetString( pTexData->nameStringTableID ),
  730. pDetailType);
  731. continue;
  732. }
  733. // Emit objects on a particular face
  734. DetailObject_t& detail = s_DetailObjectDict[objectType];
  735. // Initialize the Random Number generators for detail prop placement based on the hammer Face num.
  736. int detailpropseed = dfaceids[j].hammerfaceid;
  737. #ifdef WARNSEEDNUMBER
  738. Warning( "[%d]\n",detailpropseed );
  739. #endif
  740. srand( detailpropseed );
  741. RandomSeed( detailpropseed );
  742. if (pFace[j].dispinfo < 0)
  743. {
  744. EmitDetailObjectsOnFace( &pFace[j], detail );
  745. }
  746. else
  747. {
  748. // Get a CCoreDispInfo. All we need is the triangles and lightmap texture coordinates.
  749. mapdispinfo_t *pMapDisp = &mapdispinfo[pFace[j].dispinfo];
  750. CCoreDispInfo coreDispInfo;
  751. DispMapToCoreDispInfo( pMapDisp, &coreDispInfo, NULL, NULL );
  752. EmitDetailObjectsOnDisplacementFace( &pFace[j], detail, coreDispInfo );
  753. }
  754. }
  755. // Emit specifically specified detail props
  756. Vector origin;
  757. QAngle angles;
  758. Vector2D pos[2];
  759. Vector2D tex[2];
  760. for (int i = 0; i < num_entities; ++i)
  761. {
  762. char* pEntity = ValueForKey(&entities[i], "classname");
  763. if (!strcmp(pEntity, "detail_prop") || !strcmp(pEntity, "prop_detail"))
  764. {
  765. GetVectorForKey( &entities[i], "origin", origin );
  766. GetAnglesForKey( &entities[i], "angles", angles );
  767. char* pModelName = ValueForKey( &entities[i], "model" );
  768. int nOrientation = IntForKey( &entities[i], "detailOrientation" );
  769. AddDetailToLump( pModelName, origin, angles, nOrientation );
  770. // strip this ent from the .bsp file
  771. entities[i].epairs = 0;
  772. continue;
  773. }
  774. if (!strcmp(pEntity, "prop_detail_sprite"))
  775. {
  776. GetVectorForKey( &entities[i], "origin", origin );
  777. GetAnglesForKey( &entities[i], "angles", angles );
  778. int nOrientation = IntForKey( &entities[i], "detailOrientation" );
  779. GetVector2DForKey( &entities[i], "position_ul", pos[0] );
  780. GetVector2DForKey( &entities[i], "position_lr", pos[1] );
  781. GetVector2DForKey( &entities[i], "tex_ul", tex[0] );
  782. GetVector2DForKey( &entities[i], "tex_size", tex[1] );
  783. float flTextureSize = FloatForKey( &entities[i], "tex_total_size" );
  784. tex[1].x += tex[0].x - 0.5f;
  785. tex[1].y += tex[0].y - 0.5f;
  786. tex[0].x += 0.5f;
  787. tex[0].y += 0.5f;
  788. tex[0] /= flTextureSize;
  789. tex[1] /= flTextureSize;
  790. AddDetailSpriteToLump( origin, angles, nOrientation, pos, tex, 1.0f, DETAIL_PROP_TYPE_SPRITE );
  791. // strip this ent from the .bsp file
  792. entities[i].epairs = 0;
  793. continue;
  794. }
  795. }
  796. EndPacifier( true );
  797. }
  798. //-----------------------------------------------------------------------------
  799. // Places Detail Objects in the level
  800. //-----------------------------------------------------------------------------
  801. void EmitDetailObjects()
  802. {
  803. EmitDetailModels();
  804. // Done! Now lets add the lumps (destroy previous ones)
  805. SetLumpData( );
  806. if ( s_nDetailOverflow != 0 )
  807. {
  808. Warning( "Error! Too many detail props on this map. %d were not emitted!\n", s_nDetailOverflow );
  809. }
  810. }