Counter Strike : Global Offensive Source Code
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.

999 lines
29 KiB

  1. //========= Copyright � 1996-2005, 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 "KeyValues.h"
  11. #include "UtlSymbol.h"
  12. #include "UtlVector.h"
  13. #include <io.h>
  14. #include "bspfile.h"
  15. #include "utilmatlib.h"
  16. #include "gamebspfile.h"
  17. #include "mathlib/VMatrix.h"
  18. #include "materialpatch.h"
  19. #include "pacifier.h"
  20. #include "vstdlib/random.h"
  21. #include "builddisp.h"
  22. #include "disp_vbsp.h"
  23. #include "UtlBuffer.h"
  24. #include "CollisionUtils.h"
  25. #include <float.h>
  26. #include "UtlLinkedList.h"
  27. #include "byteswap.h"
  28. #include "writebsp.h"
  29. //-----------------------------------------------------------------------------
  30. // Information about particular detail object types
  31. //-----------------------------------------------------------------------------
  32. enum
  33. {
  34. MODELFLAG_UPRIGHT = 0x1,
  35. };
  36. struct DetailModel_t
  37. {
  38. CUtlSymbol m_ModelName;
  39. float m_Amount;
  40. float m_MinCosAngle;
  41. float m_MaxCosAngle;
  42. int m_Flags;
  43. int m_Orientation;
  44. int m_Type;
  45. Vector2D m_Pos[2];
  46. Vector2D m_Tex[2];
  47. float m_flRandomScaleStdDev;
  48. unsigned char m_ShapeSize;
  49. unsigned char m_ShapeAngle;
  50. unsigned char m_SwayAmount;
  51. };
  52. struct DetailObjectGroup_t
  53. {
  54. float m_Alpha;
  55. CUtlVector< DetailModel_t > m_Models;
  56. };
  57. struct DetailObject_t
  58. {
  59. CUtlSymbol m_Name;
  60. float m_Density;
  61. CUtlVector< DetailObjectGroup_t > m_Groups;
  62. bool operator==(const DetailObject_t& src ) const
  63. {
  64. return src.m_Name == m_Name;
  65. }
  66. };
  67. static CUtlVector<DetailObject_t> s_DetailObjectDict;
  68. static CUtlVector<entity_t *> g_BlockerList;
  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 );
  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. #define MAX_DETAIL_SPRITES 65535 * 32
  416. static void AddDetailSpriteToLump( const Vector &vecOrigin, const QAngle &vecAngles, int nOrientation,
  417. const Vector2D *pPos, const Vector2D *pTex, float flScale, int iType,
  418. int iShapeAngle = 0, int iShapeSize = 0, int iSwayAmount = 0 )
  419. {
  420. // Insert an element into the object dictionary if it aint there...
  421. int i = s_DetailObjectLump.AddToTail( );
  422. if (i >= MAX_DETAIL_SPRITES)
  423. {
  424. Error( "Error! Too many detail props emitted on this map!\n" );
  425. }
  426. DetailObjectLump_t& objectLump = s_DetailObjectLump[i];
  427. objectLump.m_DetailModel = AddDetailSpriteDictLump( pPos, pTex );
  428. VectorCopy( vecAngles, objectLump.m_Angles );
  429. VectorCopy( vecOrigin, objectLump.m_Origin );
  430. objectLump.m_Leaf = ComputeDetailLeaf(vecOrigin);
  431. objectLump.m_Lighting.r = 255;
  432. objectLump.m_Lighting.g = 255;
  433. objectLump.m_Lighting.b = 255;
  434. objectLump.m_Lighting.exponent = 0;
  435. objectLump.m_LightStyles = 0;
  436. objectLump.m_LightStyleCount = 0;
  437. objectLump.m_Orientation = nOrientation;
  438. objectLump.m_Type = iType;
  439. objectLump.m_flScale = flScale;
  440. objectLump.m_ShapeAngle = iShapeAngle;
  441. objectLump.m_ShapeSize = iShapeSize;
  442. objectLump.m_SwayAmount = iSwayAmount;
  443. }
  444. static void AddDetailSpriteToLump( const Vector &vecOrigin, const QAngle &vecAngles, DetailModel_t const& model, float flScale )
  445. {
  446. AddDetailSpriteToLump( vecOrigin,
  447. vecAngles,
  448. model.m_Orientation,
  449. model.m_Pos,
  450. model.m_Tex,
  451. flScale,
  452. model.m_Type,
  453. model.m_ShapeAngle,
  454. model.m_ShapeSize,
  455. model.m_SwayAmount );
  456. }
  457. //-----------------------------------------------------------------------------
  458. // Got a detail! Place it on the surface...
  459. //-----------------------------------------------------------------------------
  460. // BUGBUG: When the global optimizer is on, "normal" gets trashed in this function
  461. // (only when not in the debugger?)
  462. // Printing the values of normal at the bottom of the function fixes it as does
  463. // disabling global optimizations.
  464. static void PlaceDetail( DetailModel_t const& model, const Vector& pt, const Vector& normal )
  465. {
  466. // But only place it on the surface if it meets the angle constraints...
  467. float cosAngle = normal.z;
  468. // Never emit if the angle's too steep
  469. if (cosAngle < model.m_MaxCosAngle)
  470. return;
  471. // If it's between min + max, flip a coin...
  472. if (cosAngle < model.m_MinCosAngle)
  473. {
  474. float probability = (cosAngle - model.m_MaxCosAngle) /
  475. (model.m_MinCosAngle - model.m_MaxCosAngle);
  476. float t = rand() / (float)VALVE_RAND_MAX;
  477. if (t > probability)
  478. return;
  479. }
  480. // Compute the orientation of the detail
  481. QAngle angles;
  482. if (model.m_Flags & MODELFLAG_UPRIGHT)
  483. {
  484. // If it's upright, we just select a random yaw
  485. angles.Init( 0, 360.0f * rand() / (float)VALVE_RAND_MAX, 0.0f );
  486. }
  487. else
  488. {
  489. // It's not upright, so it must conform to the ground. Choose
  490. // a random orientation based on the surface normal
  491. Vector zaxis;
  492. VectorCopy( normal, zaxis );
  493. VectorNormalize( zaxis );
  494. // Choose any two arbitrary axes which are perpendicular to the normal
  495. Vector xaxis( 1, 0, 0 );
  496. if (fabs(xaxis.Dot(zaxis)) - 1.0 > -1e-3)
  497. xaxis.Init( 0, 1, 0 );
  498. Vector yaxis;
  499. CrossProduct( zaxis, xaxis, yaxis );
  500. VectorNormalize( yaxis );
  501. CrossProduct( yaxis, zaxis, xaxis );
  502. VectorNormalize( xaxis );
  503. VMatrix matrix;
  504. matrix.SetBasisVectors( xaxis, yaxis, zaxis );
  505. matrix.SetTranslation( vec3_origin );
  506. float rotAngle = 360.0f * rand() / (float)VALVE_RAND_MAX;
  507. VMatrix rot = SetupMatrixAxisRot( Vector( 0, 0, 1 ), rotAngle );
  508. matrix = matrix * rot;
  509. MatrixToAngles( matrix, angles );
  510. }
  511. // FIXME: We may also want a purely random rotation too
  512. // TERROR:
  513. for( int i = 0; i < g_BlockerList.Count(); ++i )
  514. {
  515. entity_t *ent = g_BlockerList[i];
  516. {
  517. for ( int j = 0; j < ent->numbrushes; ++j )
  518. {
  519. int brushnum = ent->firstbrush + j;
  520. mapbrush_t *brush = &g_MainMap->mapbrushes[ brushnum ];
  521. if ( IsPointInBox( pt, brush->mins, brush->maxs ) )
  522. {
  523. return;
  524. }
  525. }
  526. }
  527. }
  528. // Insert an element into the object dictionary if it aint there...
  529. switch ( model.m_Type )
  530. {
  531. case DETAIL_PROP_TYPE_MODEL:
  532. AddDetailToLump( model.m_ModelName.String(), pt, angles, model.m_Orientation );
  533. break;
  534. // Sprites and procedural models made from sprites
  535. case DETAIL_PROP_TYPE_SPRITE:
  536. default:
  537. {
  538. float flScale = 1.0f;
  539. if ( model.m_flRandomScaleStdDev != 0.0f )
  540. {
  541. flScale = fabs( RandomGaussianFloat( 1.0f, model.m_flRandomScaleStdDev ) );
  542. }
  543. AddDetailSpriteToLump( pt, angles, model, flScale );
  544. }
  545. break;
  546. }
  547. }
  548. //-----------------------------------------------------------------------------
  549. // Places Detail Objects on a face
  550. //-----------------------------------------------------------------------------
  551. static void EmitDetailObjectsOnFace( dface_t* pFace, DetailObject_t& detail )
  552. {
  553. if (pFace->numedges < 3)
  554. return;
  555. // We're going to pick a bunch of random points, and then probabilistically
  556. // decide whether or not to plant a detail object there.
  557. // Turn the face into a bunch of polygons, and compute the area of each
  558. int* pSurfEdges = &dsurfedges[pFace->firstedge];
  559. int vertexIdx = (pSurfEdges[0] < 0);
  560. int firstVertexIndex = dedges[abs(pSurfEdges[0])].v[vertexIdx];
  561. dvertex_t* pFirstVertex = &dvertexes[firstVertexIndex];
  562. for (int i = 1; i < pFace->numedges - 1; ++i )
  563. {
  564. int vertexIdx = (pSurfEdges[i] < 0);
  565. dedge_t* pEdge = &dedges[abs(pSurfEdges[i])];
  566. // Compute two triangle edges
  567. Vector e1, e2;
  568. VectorSubtract( dvertexes[pEdge->v[vertexIdx]].point, pFirstVertex->point, e1 );
  569. VectorSubtract( dvertexes[pEdge->v[1 - vertexIdx]].point, pFirstVertex->point, e2 );
  570. // Compute the triangle area
  571. Vector areaVec;
  572. CrossProduct( e1, e2, areaVec );
  573. float normalLength = areaVec.Length();
  574. float area = 0.5f * normalLength;
  575. // Compute the number of samples to take
  576. int numSamples = area * detail.m_Density * 0.000001;
  577. // Now take a sample, and randomly place an object there
  578. for (int i = 0; i < numSamples; ++i )
  579. {
  580. // Create a random sample...
  581. float u = rand() / (float)VALVE_RAND_MAX;
  582. float v = rand() / (float)VALVE_RAND_MAX;
  583. if (v > 1.0f - u)
  584. {
  585. u = 1.0f - u;
  586. v = 1.0f - v;
  587. assert( u + v <= 1.0f );
  588. }
  589. // Compute alpha
  590. float alpha = 1.0f;
  591. // Select a group based on the alpha value
  592. int group = SelectGroup( detail, alpha );
  593. // Now that we've got a group, choose a detail
  594. int model = SelectDetail( detail.m_Groups[group] );
  595. if (model < 0)
  596. continue;
  597. // Got a detail! Place it on the surface...
  598. Vector pt, normal;
  599. VectorMA( pFirstVertex->point, u, e1, pt );
  600. VectorMA( pt, v, e2, pt );
  601. VectorDivide( areaVec, -normalLength, normal );
  602. PlaceDetail( detail.m_Groups[group].m_Models[model], pt, normal );
  603. }
  604. }
  605. }
  606. //-----------------------------------------------------------------------------
  607. // Places Detail Objects on a face
  608. //-----------------------------------------------------------------------------
  609. static float ComputeDisplacementFaceArea( dface_t* pFace )
  610. {
  611. float area = 0.0f;
  612. // Compute the area of the base face
  613. int* pSurfEdges = &dsurfedges[pFace->firstedge];
  614. int vertexIdx = (pSurfEdges[0] < 0);
  615. int firstVertexIndex = dedges[abs(pSurfEdges[0])].v[vertexIdx];
  616. dvertex_t* pFirstVertex = &dvertexes[firstVertexIndex];
  617. for (int i = 1; i <= 2; ++i )
  618. {
  619. int vertexIdx = (pSurfEdges[i] < 0);
  620. dedge_t* pEdge = &dedges[abs(pSurfEdges[i])];
  621. // Compute two triangle edges
  622. Vector e1, e2;
  623. VectorSubtract( dvertexes[pEdge->v[vertexIdx]].point, pFirstVertex->point, e1 );
  624. VectorSubtract( dvertexes[pEdge->v[1 - vertexIdx]].point, pFirstVertex->point, e2 );
  625. // Compute the triangle area
  626. Vector areaVec;
  627. CrossProduct( e1, e2, areaVec );
  628. float normalLength = areaVec.Length();
  629. area += 0.5f * normalLength;
  630. }
  631. return area;
  632. }
  633. //-----------------------------------------------------------------------------
  634. // Places Detail Objects on a face
  635. //-----------------------------------------------------------------------------
  636. static void EmitDetailObjectsOnDisplacementFace( dface_t* pFace,
  637. DetailObject_t& detail, CCoreDispInfo& coreDispInfo )
  638. {
  639. assert(pFace->numedges == 4);
  640. // We're going to pick a bunch of random points, and then probabilistically
  641. // decide whether or not to plant a detail object there.
  642. // Compute the area of the base face
  643. float area = ComputeDisplacementFaceArea( pFace );
  644. // Compute the number of samples to take
  645. int numSamples = area * detail.m_Density * 0.000001;
  646. // Now take a sample, and randomly place an object there
  647. for (int i = 0; i < numSamples; ++i )
  648. {
  649. // Create a random sample...
  650. float u = rand() / (float)VALVE_RAND_MAX;
  651. float v = rand() / (float)VALVE_RAND_MAX;
  652. // Compute alpha
  653. float alpha;
  654. Vector pt, normal;
  655. coreDispInfo.GetPositionOnSurface( u, v, pt, &normal, &alpha );
  656. alpha /= 255.0f;
  657. // Select a group based on the alpha value
  658. int group = SelectGroup( detail, alpha );
  659. // Now that we've got a group, choose a detail
  660. int model = SelectDetail( detail.m_Groups[group] );
  661. if (model < 0)
  662. continue;
  663. // Got a detail! Place it on the surface...
  664. PlaceDetail( detail.m_Groups[group].m_Models[model], pt, normal );
  665. }
  666. }
  667. //-----------------------------------------------------------------------------
  668. // Sort detail objects by leaf
  669. //-----------------------------------------------------------------------------
  670. static int SortFunc( const void *arg1, const void *arg2 )
  671. {
  672. int nDelta = ((DetailObjectLump_t*)arg1)->m_Leaf - ((DetailObjectLump_t*)arg2)->m_Leaf;
  673. if ( nDelta < 0 )
  674. return -1;
  675. if ( nDelta > 0 )
  676. return 1;
  677. return 0;
  678. }
  679. //-----------------------------------------------------------------------------
  680. // Places Detail Objects in the lump
  681. //-----------------------------------------------------------------------------
  682. static void SetLumpData( )
  683. {
  684. // Sort detail props by leaf
  685. qsort( s_DetailObjectLump.Base(), s_DetailObjectLump.Count(), sizeof(DetailObjectLump_t), SortFunc );
  686. GameLumpHandle_t handle = g_GameLumps.GetGameLumpHandle(GAMELUMP_DETAIL_PROPS);
  687. if (handle != g_GameLumps.InvalidGameLump())
  688. {
  689. g_GameLumps.DestroyGameLump(handle);
  690. }
  691. int nDictSize = s_DetailObjectDictLump.Count() * sizeof(DetailObjectDictLump_t);
  692. int nSpriteDictSize = s_DetailSpriteDictLump.Count() * sizeof(DetailSpriteDictLump_t);
  693. int nObjSize = s_DetailObjectLump.Count() * sizeof(DetailObjectLump_t);
  694. int nSize = nDictSize + nSpriteDictSize + nObjSize + (3 * sizeof(int));
  695. handle = g_GameLumps.CreateGameLump( GAMELUMP_DETAIL_PROPS, nSize, 0, GAMELUMP_DETAIL_PROPS_VERSION );
  696. // Serialize the data
  697. CUtlBuffer buf( g_GameLumps.GetGameLump(handle), nSize );
  698. buf.PutInt( s_DetailObjectDictLump.Count() );
  699. if (nDictSize)
  700. {
  701. buf.Put( s_DetailObjectDictLump.Base(), nDictSize );
  702. }
  703. buf.PutInt( s_DetailSpriteDictLump.Count() );
  704. if (nSpriteDictSize)
  705. {
  706. buf.Put( s_DetailSpriteDictLump.Base(), nSpriteDictSize );
  707. }
  708. buf.PutInt( s_DetailObjectLump.Count() );
  709. if (nObjSize)
  710. {
  711. buf.Put( s_DetailObjectLump.Base(), nObjSize );
  712. }
  713. }
  714. //-----------------------------------------------------------------------------
  715. // Places Detail Objects in the level
  716. //-----------------------------------------------------------------------------
  717. void EmitDetailModels()
  718. {
  719. StartPacifier("Placing detail props : ");
  720. // build detail blocker list
  721. g_BlockerList.RemoveAll();
  722. for( int i = 0; i < num_entities; ++i )
  723. {
  724. entity_t *ent = &entities[i];
  725. char* classname = ValueForKey( ent, "classname" );
  726. if ( !strcmp( classname, "func_detail_blocker" ) )
  727. {
  728. g_BlockerList.AddToTail(ent);
  729. }
  730. }
  731. // Place stuff on each face
  732. dface_t* pFace = dfaces;
  733. for (int j = 0; j < numfaces; ++j)
  734. {
  735. UpdatePacifier( (float)j / (float)numfaces );
  736. // Get at the material associated with this face
  737. texinfo_t* pTexInfo = &texinfo[pFace[j].texinfo];
  738. dtexdata_t* pTexData = GetTexData( pTexInfo->texdata );
  739. // Try to get at the material
  740. bool found;
  741. MaterialSystemMaterial_t handle =
  742. FindOriginalMaterial( TexDataStringTable_GetString( pTexData->nameStringTableID ),
  743. &found, false );
  744. if (!found)
  745. continue;
  746. // See if its got any detail objects on it
  747. const char* pDetailType = GetMaterialVar( handle, "%detailtype" );
  748. if (!pDetailType)
  749. continue;
  750. // Get the detail type...
  751. DetailObject_t search;
  752. search.m_Name = pDetailType;
  753. int objectType = s_DetailObjectDict.Find(search);
  754. if (objectType < 0)
  755. {
  756. Warning("Material %s uses unknown detail object type %s!\n",
  757. TexDataStringTable_GetString( pTexData->nameStringTableID ),
  758. pDetailType);
  759. continue;
  760. }
  761. // Emit objects on a particular face
  762. DetailObject_t& detail = s_DetailObjectDict[objectType];
  763. // Initialize the Random Number generators for detail prop placement based on the hammer Face num.
  764. int detailpropseed = dfaceids[j].hammerfaceid;
  765. #ifdef WARNSEEDNUMBER
  766. Warning( "[%d]\n",detailpropseed );
  767. #endif
  768. srand( detailpropseed );
  769. RandomSeed( detailpropseed );
  770. if (pFace[j].dispinfo < 0)
  771. {
  772. EmitDetailObjectsOnFace( &pFace[j], detail );
  773. }
  774. else
  775. {
  776. // Get a CCoreDispInfo. All we need is the triangles and lightmap texture coordinates.
  777. mapdispinfo_t *pMapDisp = &mapdispinfo[pFace[j].dispinfo];
  778. CCoreDispInfo coreDispInfo;
  779. DispMapToCoreDispInfo( pMapDisp, &coreDispInfo, NULL, NULL );
  780. EmitDetailObjectsOnDisplacementFace( &pFace[j], detail, coreDispInfo );
  781. }
  782. }
  783. // Emit specifically specified detail props
  784. Vector origin;
  785. QAngle angles;
  786. Vector2D pos[2];
  787. Vector2D tex[2];
  788. for (int i = 0; i < num_entities; ++i)
  789. {
  790. char* pEntity = ValueForKey(&entities[i], "classname");
  791. if (!strcmp(pEntity, "detail_prop") || !strcmp(pEntity, "prop_detail"))
  792. {
  793. GetVectorForKey( &entities[i], "origin", origin );
  794. GetAnglesForKey( &entities[i], "angles", angles );
  795. char* pModelName = ValueForKey( &entities[i], "model" );
  796. int nOrientation = IntForKey( &entities[i], "detailOrientation" );
  797. AddDetailToLump( pModelName, origin, angles, nOrientation );
  798. // strip this ent from the .bsp file
  799. entities[i].epairs = 0;
  800. continue;
  801. }
  802. if (!strcmp(pEntity, "prop_detail_sprite"))
  803. {
  804. GetVectorForKey( &entities[i], "origin", origin );
  805. GetAnglesForKey( &entities[i], "angles", angles );
  806. int nOrientation = IntForKey( &entities[i], "detailOrientation" );
  807. GetVector2DForKey( &entities[i], "position_ul", pos[0] );
  808. GetVector2DForKey( &entities[i], "position_lr", pos[1] );
  809. GetVector2DForKey( &entities[i], "tex_ul", tex[0] );
  810. GetVector2DForKey( &entities[i], "tex_size", tex[1] );
  811. float flTextureSize = FloatForKey( &entities[i], "tex_total_size" );
  812. tex[1].x += tex[0].x - 0.5f;
  813. tex[1].y += tex[0].y - 0.5f;
  814. tex[0].x += 0.5f;
  815. tex[0].y += 0.5f;
  816. tex[0] /= flTextureSize;
  817. tex[1] /= flTextureSize;
  818. AddDetailSpriteToLump( origin, angles, nOrientation, pos, tex, 1.0f, DETAIL_PROP_TYPE_SPRITE );
  819. // strip this ent from the .bsp file
  820. entities[i].epairs = 0;
  821. continue;
  822. }
  823. }
  824. EndPacifier( true );
  825. }
  826. //-----------------------------------------------------------------------------
  827. // Places Detail Objects in the level
  828. //-----------------------------------------------------------------------------
  829. void EmitDetailObjects()
  830. {
  831. EmitDetailModels();
  832. // Done! Now lets add the lumps (destroy previous ones)
  833. SetLumpData( );
  834. if ( s_nDetailOverflow != 0 )
  835. {
  836. Warning( "Error! Too many detail props on this map. %d were not emitted!\n", s_nDetailOverflow );
  837. }
  838. }