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.

819 lines
24 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 "stdafx.h"
  9. #include "collisionutils.h"
  10. #include "const.h"
  11. #include "interface.h"
  12. #include "KeyValues.h"
  13. #include "UtlSymbol.h"
  14. #include "UtlVector.h"
  15. #include "utilmatlib.h"
  16. #include "mathlib/VMatrix.h"
  17. #include "vstdlib/random.h"
  18. #include "builddisp.h"
  19. #include "UtlBuffer.h"
  20. #include "IEditorTexture.h"
  21. #include "materialsystem/IMaterialVar.h"
  22. #include "materialsystem/IMaterial.h"
  23. #include "mapface.h"
  24. #include "MapDoc.h" // TERROR
  25. #include "camera.h"
  26. #include "options.h"
  27. #include "hammer.h"
  28. // Actually, this is the max per map, but for now this is better than no limit at all.
  29. #define MAX_DETAIL_SPRITES_PER_FACE 65535
  30. // memdbgon must be the last include file in a .cpp file!!!
  31. #include "tier0/memdbgon.h"
  32. //IMPLEMENT_MAPCLASS(DetailObjects)
  33. bool DetailObjects::s_bBuildDetailObjects = true;
  34. // Defaults to match the parsing defaults in ParseDetailGroup -- code path defaults may/may not execute
  35. DetailObjects::~DetailObjects()
  36. {
  37. m_DetailModels.PurgeAndDeleteElements();
  38. m_DetailSprites.PurgeAndDeleteElements();
  39. }
  40. DetailObjects::DetailModel_t::DetailModel_t() : m_ModelName()
  41. {
  42. m_Amount = 0.0;
  43. m_MinCosAngle = -1.0;
  44. m_MaxCosAngle = -1.0;
  45. m_Flags = 0;
  46. m_Orientation = 0;
  47. m_Type = DETAIL_PROP_TYPE_SPRITE;
  48. m_Pos[0] = Vector2D(-10, 20);
  49. m_Pos[1] = Vector2D(10, 0);
  50. m_Tex[0] = Vector2D(0.5/512, 0.5/512);
  51. m_Tex[1] = Vector2D(63.5/512, 63.5/512);
  52. m_flRandomScaleStdDev = 0.0;
  53. m_ShapeSize = 0;
  54. m_ShapeAngle = 0;
  55. m_SwayAmount = 0;
  56. }
  57. CUtlVector<DetailObjects::DetailObject_t> DetailObjects::s_DetailObjectDict; // static members?
  58. //-----------------------------------------------------------------------------
  59. // Parses the key-value pairs in the detail.rad file
  60. //-----------------------------------------------------------------------------
  61. void DetailObjects::ParseDetailGroup( int detailId, KeyValues* pGroupKeyValues )
  62. {
  63. // Sort the group by alpha
  64. float alpha = pGroupKeyValues->GetFloat( "alpha", 1.0f );
  65. int i = s_DetailObjectDict[detailId].m_Groups.Count();
  66. while ( --i >= 0 )
  67. {
  68. if (alpha > s_DetailObjectDict[detailId].m_Groups[i].m_Alpha)
  69. break;
  70. }
  71. // Insert after the first guy who's more transparent that we are!
  72. i = s_DetailObjectDict[detailId].m_Groups.InsertAfter(i);
  73. DetailObjectGroup_t& group = s_DetailObjectDict[detailId].m_Groups[i];
  74. group.m_Alpha = alpha;
  75. // Add in all the model groups
  76. KeyValues* pIter = pGroupKeyValues->GetFirstSubKey();
  77. float totalAmount = 0.0f;
  78. while( pIter )
  79. {
  80. if (pIter->GetFirstSubKey())
  81. {
  82. int i = group.m_Models.AddToTail();
  83. DetailModel_t &model = group.m_Models[i];
  84. model.m_ModelName = pIter->GetString( "model", 0 );
  85. if (model.m_ModelName != UTL_INVAL_SYMBOL)
  86. {
  87. model.m_Type = DETAIL_PROP_TYPE_MODEL;
  88. }
  89. else
  90. {
  91. const char *pSpriteData = pIter->GetString( "sprite", 0 );
  92. if (pSpriteData)
  93. {
  94. const char *pProcModelType = pIter->GetString( "sprite_shape", 0 );
  95. if ( pProcModelType )
  96. {
  97. if ( !Q_stricmp( pProcModelType, "cross" ) )
  98. {
  99. model.m_Type = DETAIL_PROP_TYPE_SHAPE_CROSS;
  100. }
  101. else if ( !Q_stricmp( pProcModelType, "tri" ) )
  102. {
  103. model.m_Type = DETAIL_PROP_TYPE_SHAPE_TRI;
  104. }
  105. else
  106. model.m_Type = DETAIL_PROP_TYPE_SPRITE;
  107. }
  108. else
  109. {
  110. // card sprite
  111. model.m_Type = DETAIL_PROP_TYPE_SPRITE;
  112. }
  113. model.m_Tex[0].Init();
  114. model.m_Tex[1].Init();
  115. float x = 0, y = 0, flWidth = 64, flHeight = 64, flTextureSize = 512;
  116. int nValid = sscanf( pSpriteData, "%f %f %f %f %f", &x, &y, &flWidth, &flHeight, &flTextureSize );
  117. if ( (nValid != 5) || (flTextureSize == 0) )
  118. {
  119. Error( "Invalid arguments to \"sprite\" in detail.vbsp (model %s)!\n", model.m_ModelName );
  120. }
  121. model.m_Tex[0].x = ( x + 0.5f ) / flTextureSize;
  122. model.m_Tex[0].y = ( y + 0.5f ) / flTextureSize;
  123. model.m_Tex[1].x = ( x + flWidth - 0.5f ) / flTextureSize;
  124. model.m_Tex[1].y = ( y + flHeight - 0.5f ) / flTextureSize;
  125. model.m_Pos[0].Init( -10, 20 );
  126. model.m_Pos[1].Init( 10, 0 );
  127. pSpriteData = pIter->GetString( "spritesize", 0 );
  128. if (pSpriteData)
  129. {
  130. sscanf( pSpriteData, "%f %f %f %f", &x, &y, &flWidth, &flHeight );
  131. float ox = flWidth * x;
  132. float oy = flHeight * y;
  133. model.m_Pos[0].x = -ox;
  134. model.m_Pos[0].y = flHeight - oy;
  135. model.m_Pos[1].x = flWidth - ox;
  136. model.m_Pos[1].y = -oy;
  137. }
  138. model.m_flRandomScaleStdDev = pIter->GetFloat( "spriterandomscale", 0.0f );
  139. // sway is a percent of max sway, cl_detail_max_sway
  140. float flSway = clamp( pIter->GetFloat( "sway", 0.0f ), 0.0, 1.0 );
  141. model.m_SwayAmount = (unsigned char)( 255.0 * flSway );
  142. // shape angle
  143. // for the tri shape, this is the angle each side is fanned out
  144. model.m_ShapeAngle = pIter->GetInt( "shape_angle", 0 );
  145. // shape size
  146. // for the tri shape, this is the distance from the origin to the center of a side
  147. float flShapeSize = clamp( pIter->GetFloat( "shape_size", 0.0f ), 0.0, 1.0 );
  148. model.m_ShapeSize = (unsigned char)( 255.0 * flShapeSize );
  149. }
  150. }
  151. model.m_Amount = pIter->GetFloat( "amount", 1.0 ) + totalAmount;
  152. totalAmount = model.m_Amount;
  153. model.m_Flags = 0;
  154. if (pIter->GetInt( "upright", 0 ))
  155. {
  156. model.m_Flags |= MODELFLAG_UPRIGHT;
  157. }
  158. // These are used to prevent emission on steep surfaces
  159. float minAngle = pIter->GetFloat( "minAngle", 180 );
  160. float maxAngle = pIter->GetFloat( "maxAngle", 180 );
  161. model.m_MinCosAngle = cos(minAngle * M_PI / 180.f);
  162. model.m_MaxCosAngle = cos(maxAngle * M_PI / 180.f);
  163. model.m_Orientation = pIter->GetInt( "detailOrientation", 0 );
  164. // Make sure minAngle < maxAngle
  165. if ( model.m_MinCosAngle < model.m_MaxCosAngle)
  166. {
  167. model.m_MinCosAngle = model.m_MaxCosAngle;
  168. }
  169. }
  170. pIter = pIter->GetNextKey();
  171. }
  172. // renormalize the amount if the total > 1
  173. if (totalAmount > 1.0f)
  174. {
  175. for (i = 0; i < group.m_Models.Count(); ++i)
  176. {
  177. group.m_Models[i].m_Amount /= totalAmount;
  178. }
  179. }
  180. }
  181. //-----------------------------------------------------------------------------
  182. // Parses the key-value pairs in the detail.vbsp file
  183. //-----------------------------------------------------------------------------
  184. void DetailObjects::ParseDetailObjectFile( KeyValues& keyValues )
  185. {
  186. // Iterate over all detail object groups...
  187. KeyValues* pIter;
  188. for( pIter = keyValues.GetFirstSubKey(); pIter; pIter = pIter->GetNextKey() )
  189. {
  190. if (!pIter->GetFirstSubKey())
  191. continue;
  192. int i = s_DetailObjectDict.AddToTail( );
  193. s_DetailObjectDict[i].m_Name = pIter->GetName() ;
  194. s_DetailObjectDict[i].m_Density = pIter->GetFloat( "density", 0.0f );
  195. // Iterate over all detail object groups...
  196. KeyValues* pIterGroups = pIter->GetFirstSubKey();
  197. while( pIterGroups )
  198. {
  199. if (pIterGroups->GetFirstSubKey())
  200. {
  201. ParseDetailGroup( i, pIterGroups );
  202. }
  203. pIterGroups = pIterGroups->GetNextKey();
  204. }
  205. }
  206. }
  207. //-----------------------------------------------------------------------------
  208. // Finds the name of the detail.vbsp file to use
  209. //-----------------------------------------------------------------------------
  210. const char *DetailObjects::FindDetailVBSPName( void )
  211. {
  212. #if 0
  213. for( int i = 0; i < num_entities; i++ )
  214. {
  215. char* pEntity = ValueForKey( &entities[i], "classname" );
  216. if ( !strcmp( pEntity, "worldspawn" ) )
  217. {
  218. const char *pDetailVBSP = ValueForKey( &entities[i], "detailvbsp" );
  219. if ( !pDetailVBSP || !pDetailVBSP[0] )
  220. {
  221. pDetailVBSP = "detail.vbsp";
  222. }
  223. return pDetailVBSP;
  224. }
  225. }
  226. #endif
  227. return "detail.vbsp";
  228. }
  229. #include "tier0\memdbgoff.h"
  230. //-----------------------------------------------------------------------------
  231. // Loads up the detail object dictionary
  232. //-----------------------------------------------------------------------------
  233. void DetailObjects::LoadEmitDetailObjectDictionary( const char* pGameDir )
  234. {
  235. // Set the required global lights filename and try looking in qproject
  236. const char *pDetailVBSP = FindDetailVBSPName();
  237. KeyValues * values = new KeyValues( pDetailVBSP );
  238. if ( values->LoadFromFile( g_pFileSystem, pDetailVBSP ) )
  239. {
  240. ParseDetailObjectFile( *values );
  241. }
  242. values->deleteThis();
  243. }
  244. //-----------------------------------------------------------------------------
  245. // Selects a detail group
  246. //-----------------------------------------------------------------------------
  247. int DetailObjects::SelectGroup( const DetailObject_t& detail, float alpha )
  248. {
  249. // Find the two groups whose alpha we're between...
  250. int start, end;
  251. for ( start = 0; start < detail.m_Groups.Count() - 1; ++start )
  252. {
  253. if (alpha < detail.m_Groups[start+1].m_Alpha)
  254. break;
  255. }
  256. end = start + 1;
  257. if (end >= detail.m_Groups.Count())
  258. --end;
  259. if (start == end)
  260. return start;
  261. // Figure out how far we are between start and end...
  262. float dist = 0.0f;
  263. float dAlpha = (detail.m_Groups[end].m_Alpha - detail.m_Groups[start].m_Alpha);
  264. if (dAlpha != 0.0f)
  265. {
  266. dist = (alpha - detail.m_Groups[start].m_Alpha) / dAlpha;
  267. }
  268. // Pick a number, any number...
  269. float r = rand() / (float)VALVE_RAND_MAX;
  270. // When dist == 0, we *always* want start.
  271. // When dist == 1, we *always* want end
  272. // That's why this logic looks a little reversed
  273. return (r > dist) ? start : end;
  274. }
  275. //-----------------------------------------------------------------------------
  276. // Selects a detail object
  277. //-----------------------------------------------------------------------------
  278. int DetailObjects::SelectDetail( DetailObjectGroup_t const& group )
  279. {
  280. // Pick a number, any number...
  281. float r = rand() / (float)VALVE_RAND_MAX;
  282. // Look through the list of models + pick the one associated with this number
  283. for ( int i = 0; i < group.m_Models.Count(); ++i )
  284. {
  285. if (r <= group.m_Models[i].m_Amount)
  286. return i;
  287. }
  288. return -1;
  289. }
  290. //-----------------------------------------------------------------------------
  291. // Add a detail to the lump.
  292. //-----------------------------------------------------------------------------
  293. void DetailObjects::AddDetailModelToFace( const char* pModelName, const Vector& pt, const QAngle& angles, int nOrientation )
  294. {
  295. StudioModel *pStudioModel = new StudioModel();
  296. m_DetailModels.AddToTail( pStudioModel );
  297. pStudioModel->LoadModel( pModelName );
  298. pStudioModel->SetOrigin( pt.x, pt.y, pt.z );
  299. QAngle modelangle = angles;
  300. pStudioModel->SetAngles( modelangle );
  301. }
  302. //-----------------------------------------------------------------------------
  303. // Add a detail sprite to the lump.
  304. //-----------------------------------------------------------------------------
  305. void DetailObjects::AddDetailSpriteToFace( const Vector &vecOrigin, const QAngle &vecAngles, DetailModel_t const& model, float flScale )
  306. {
  307. CSpriteModel *pSpriteModel = new CSpriteModel;
  308. m_DetailSprites.AddToTail(pSpriteModel);
  309. const char szSpriteName[_MAX_PATH] = "detail/detailsprites";
  310. pSpriteModel->LoadSprite( szSpriteName );
  311. pSpriteModel->SetRenderMode( kRenderNormal );
  312. pSpriteModel->SetMaterialPrimitiveType( MATERIAL_POLYGON );
  313. pSpriteModel->SetOrigin( vecOrigin );
  314. pSpriteModel->SetAngles( vecAngles );
  315. pSpriteModel->SetScale( flScale );
  316. pSpriteModel->SetInvert( true );
  317. pSpriteModel->SetExtent( model.m_Pos[0], model.m_Pos[1] );
  318. pSpriteModel->SetTextureExtent( model.m_Tex[0], model.m_Tex[1] );
  319. }
  320. //-----------------------------------------------------------------------------
  321. // Got a detail! Place it on the surface...
  322. //-----------------------------------------------------------------------------
  323. // BUGBUG: When the global optimizer is on, "normal" gets trashed in this function
  324. // (only when not in the debugger?)
  325. // Printing the values of normal at the bottom of the function fixes it as does
  326. // disabling global optimizations.
  327. void DetailObjects::PlaceDetail( DetailModel_t const& model, const Vector& pt, const Vector& normal )
  328. {
  329. // But only place it on the surface if it meets the angle constraints...
  330. float cosAngle = normal.z;
  331. // Never emit if the angle's too steep
  332. if (cosAngle < model.m_MaxCosAngle)
  333. return;
  334. // If it's between min + max, flip a coin...
  335. if (cosAngle < model.m_MinCosAngle)
  336. {
  337. float probability = (cosAngle - model.m_MaxCosAngle) /
  338. (model.m_MinCosAngle - model.m_MaxCosAngle);
  339. float t = rand() / (float)VALVE_RAND_MAX;
  340. if (t > probability)
  341. return;
  342. }
  343. // Compute the orientation of the detail
  344. QAngle angles;
  345. if (model.m_Flags & MODELFLAG_UPRIGHT)
  346. {
  347. // If it's upright, we just select a random yaw
  348. angles.Init( 0, 360.0f * rand() / (float)VALVE_RAND_MAX, 0.0f );
  349. }
  350. else
  351. {
  352. // It's not upright, so it must conform to the ground. Choose
  353. // a random orientation based on the surface normal
  354. Vector zaxis;
  355. VectorCopy( normal, zaxis );
  356. VectorNormalize( zaxis );
  357. // Choose any two arbitrary axes which are perpendicular to the normal
  358. Vector xaxis( 1, 0, 0 );
  359. if (fabs(xaxis.Dot(zaxis)) - 1.0 > -1e-3)
  360. xaxis.Init( 0, 1, 0 );
  361. Vector yaxis;
  362. CrossProduct( zaxis, xaxis, yaxis );
  363. VectorNormalize( yaxis );
  364. CrossProduct( yaxis, zaxis, xaxis );
  365. VectorNormalize( xaxis );
  366. VMatrix matrix;
  367. matrix.SetBasisVectors( xaxis, yaxis, zaxis );
  368. matrix.SetTranslation( vec3_origin );
  369. float rotAngle = 360.0f * rand() / (float)VALVE_RAND_MAX;
  370. VMatrix rot = SetupMatrixAxisRot( Vector( 0, 0, 1 ), rotAngle );
  371. matrix = matrix * rot;
  372. MatrixToAngles( matrix, angles );
  373. }
  374. // FIXME: We may also want a purely random rotation too
  375. // Insert an element into the object dictionary if it aint there...
  376. switch ( model.m_Type )
  377. {
  378. case DETAIL_PROP_TYPE_MODEL:
  379. AddDetailModelToFace( model.m_ModelName.String(), pt, angles, model.m_Orientation );
  380. break;
  381. // Sprites and procedural models made from sprites
  382. case DETAIL_PROP_TYPE_SPRITE:
  383. default:
  384. {
  385. float flScale = 1.0f;
  386. if ( model.m_flRandomScaleStdDev != 0.0f )
  387. {
  388. flScale = fabs( RandomGaussianFloat( 1.0f, model.m_flRandomScaleStdDev ) );
  389. }
  390. AddDetailSpriteToFace( pt, angles, model, flScale );
  391. }
  392. break;
  393. }
  394. }
  395. //-----------------------------------------------------------------------------
  396. // Places Detail Objects on a face
  397. //-----------------------------------------------------------------------------
  398. void DetailObjects::EmitDetailObjectsOnFace( CMapFace *pMapFace, DetailObject_t& detail )
  399. {
  400. // See how many points define this particular face
  401. int nPoints = pMapFace->GetPointCount();
  402. // Faces with detail props need at least 3 point to form a plane
  403. if (nPoints < 3)
  404. return;
  405. // TERROR:
  406. CMapDoc *pDoc = CMapDoc::GetActiveMapDoc();
  407. CMapEntityList detailBlockers;
  408. pDoc->FindEntitiesByClassName( detailBlockers, "func_detail_blocker", false );
  409. // Get the first point of the face
  410. Vector p0;
  411. pMapFace->GetPoint(p0,0);
  412. // Get next points on the face in pairs -- ie get points necessary to tesselate the face into triangles
  413. for (int i = 1; i < nPoints-1; i++ )
  414. {
  415. // Get the next two points of the face
  416. Vector p1, p2;
  417. pMapFace->GetPoint(p1,i);
  418. pMapFace->GetPoint(p2,i+1);
  419. // For the edges of the current triangle tesselating a portion of the face
  420. Vector e1, e2;
  421. VectorSubtract( p1, p0, e1 );
  422. VectorSubtract( p2, p0, e2 );
  423. // Calculate the area of the tesselated triange using half the crossproduct of the edges
  424. Vector areaVec;
  425. CrossProduct( e1, e2, areaVec );
  426. float normalLength = areaVec.Length();
  427. float area = 0.5 * normalLength;
  428. // Calculate the detail prop density based on the expected density and the tesselated triangle area
  429. int numSamples = clamp( area * detail.m_Density * 0.000001, 0, MAX_DETAIL_SPRITES_PER_FACE );
  430. // For each possible sample, attempt to randomly place a detail object there
  431. for (int i = 0; i < numSamples; ++i )
  432. {
  433. // Create a random sample location...
  434. float u = rand() / (float)VALVE_RAND_MAX;
  435. float v = rand() / (float)VALVE_RAND_MAX;
  436. // Make sure the u,v coordinate stay within the triangle boundaries (ie they NOT in the far half of the parallelogram)
  437. if (v > 1.0f - u)
  438. {
  439. // Triangle is out of bounds, flip the coordinates so they are in the near half of the parallelogram
  440. u = 1.0f - u;
  441. v = 1.0f - v;
  442. assert( u + v <= 1.0f );
  443. }
  444. // Compute alpha - assumed to be 1.0 across entire face for non-displacement map faces, since there is no alpha channel
  445. float alpha = 1.0f;
  446. // Select a group based on the alpha value
  447. int group = SelectGroup( detail, alpha );
  448. // Now that we've got a group, choose a detail
  449. int model = SelectDetail( detail.m_Groups[group] );
  450. if (model < 0)
  451. continue;
  452. // Got a detail! Place it on the surface...
  453. Vector pt, normal;
  454. VectorMA( p0, u, e1, pt );
  455. VectorMA( pt, v, e2, pt );
  456. VectorDivide( areaVec, -normalLength, normal );
  457. bool blocked = false;
  458. for ( int b=0; b<detailBlockers.Count(); ++b )
  459. {
  460. CMapEntity *blocker = detailBlockers[b];
  461. if ( blocker->ContainsPoint( pt ) )
  462. {
  463. blocked = true;
  464. break;
  465. }
  466. }
  467. if ( blocked )
  468. continue;
  469. PlaceDetail( detail.m_Groups[group].m_Models[model], pt, normal );
  470. }
  471. }
  472. }
  473. //-----------------------------------------------------------------------------
  474. // Places Detail Objects on a face
  475. //-----------------------------------------------------------------------------
  476. float DetailObjects::ComputeDisplacementFaceArea( CMapFace *pMapFace )
  477. {
  478. float area = 0.0f;
  479. // Compute the area of the base face
  480. // Displacement base faces must be quads.
  481. Vector edge[4];
  482. for( int i=0; i<4; i++ )
  483. {
  484. Vector p0, p1;
  485. pMapFace->GetPoint( p0, i );
  486. pMapFace->GetPoint( p1, (i+1)%4 );
  487. VectorSubtract( p1, p0, edge[i] );
  488. }
  489. Vector area_01, area_23;
  490. CrossProduct( edge[0], edge[1], area_01 );
  491. CrossProduct( edge[2], edge[3], area_23 );
  492. area = ( area_01.Length() + area_23.Length() ) * 0.5f;
  493. return area;
  494. }
  495. //-----------------------------------------------------------------------------
  496. // Places Detail Objects on a face
  497. //-----------------------------------------------------------------------------
  498. void DetailObjects::EmitDetailObjectsOnDisplacementFace( CMapFace *pMapFace,
  499. DetailObject_t& detail )
  500. {
  501. assert(pMapFace->GetPointCount() == 4);
  502. // TERROR:
  503. CMapDoc *pDoc = CMapDoc::GetActiveMapDoc();
  504. CMapEntityList detailBlockers;
  505. pDoc->FindEntitiesByClassName( detailBlockers, "func_detail_blocker", false );
  506. // We're going to pick a bunch of random points, and then probabilistically
  507. // decide whether or not to plant a detail object there.
  508. // Compute the area of the base face
  509. float area = ComputeDisplacementFaceArea( pMapFace );
  510. // Compute the number of samples to take
  511. int numSamples = area * detail.m_Density * 0.000001;
  512. EditDispHandle_t editdisphandle = pMapFace->GetDisp();
  513. CMapDisp *pMapDisp = EditDispMgr()->GetDisp(editdisphandle);
  514. CCoreDispInfo *pCoreDispInfo = pMapDisp->GetCoreDispInfo();
  515. // Now take a sample, and randomly place an object there
  516. for (int i = 0; i < numSamples; ++i )
  517. {
  518. // Create a random sample...
  519. float u = rand() / (float)VALVE_RAND_MAX;
  520. float v = rand() / (float)VALVE_RAND_MAX;
  521. // Compute alpha
  522. float alpha;
  523. Vector pt, normal;
  524. pCoreDispInfo->GetPositionOnSurface( u, v, pt, &normal, &alpha );
  525. alpha /= 255.0f;
  526. bool blocked = false;
  527. for ( int b=0; b<detailBlockers.Count(); ++b )
  528. {
  529. CMapEntity *blocker = detailBlockers[b];
  530. if ( blocker->ContainsPoint( pt ) )
  531. {
  532. blocked = true;
  533. break;
  534. }
  535. }
  536. if ( blocked )
  537. continue;
  538. // Select a group based on the alpha value
  539. int group = SelectGroup( detail, alpha );
  540. // Now that we've got a group, choose a detail
  541. int model = SelectDetail( detail.m_Groups[group] );
  542. if (model < 0)
  543. continue;
  544. // Got a detail! Place it on the surface...
  545. PlaceDetail( detail.m_Groups[group].m_Models[model], pt, normal );
  546. }
  547. }
  548. //-----------------------------------------------------------------------------
  549. // Builds Detail Objects for a particular face
  550. //-----------------------------------------------------------------------------
  551. bool DetailObjects::ShouldRenderLast(void)
  552. {
  553. return true;
  554. }
  555. //-----------------------------------------------------------------------------
  556. // Builds Detail Objects for a particular face
  557. //-----------------------------------------------------------------------------
  558. void DetailObjects::BuildAnyDetailObjects(CMapFace *pMapFace)
  559. {
  560. // Ignore this call while loading the VMF or else we'll generate a lot of redundant ones.
  561. if ( !s_bBuildDetailObjects )
  562. return;
  563. if ( pMapFace->IsCordonFace() )
  564. return;
  565. // Try to get at the material
  566. bool found;
  567. IEditorTexture *pEditorTexture = pMapFace->GetTexture();
  568. if ( !pEditorTexture )
  569. return;
  570. IMaterial *pMaterial = pEditorTexture->GetMaterial();
  571. if ( !pMaterial )
  572. return;
  573. IMaterialVar *pMaterialVar = pMaterial->FindVar("%detailtype", &found, false );
  574. if ( !found || !pMaterialVar )
  575. return;
  576. const char* pDetailType = pMaterialVar->GetStringValue();
  577. if ( !pDetailType )
  578. return;
  579. // Get the detail type...
  580. DetailObject_t search;
  581. search.m_Name = pDetailType;
  582. DetailObjects *pDetails = pMapFace->m_pDetailObjects;
  583. if ( pMapFace->m_pDetailObjects )
  584. {
  585. pDetails->m_DetailModels.PurgeAndDeleteElements();
  586. pDetails->m_DetailSprites.PurgeAndDeleteElements();
  587. }
  588. else
  589. {
  590. pMapFace->m_pDetailObjects = pDetails = new DetailObjects;
  591. }
  592. if ( pDetails )
  593. {
  594. // Set the center the "detailobjects" to be the average of the face points
  595. int nPoints = pMapFace->GetPointCount();
  596. Vector faceCenter, faceCorner;
  597. faceCenter.Init();
  598. for ( int point=0; point < nPoints; point++ )
  599. {
  600. pMapFace->GetPoint(faceCorner,point);
  601. faceCenter += faceCorner;
  602. }
  603. faceCenter /= nPoints;
  604. pDetails->SetOrigin( faceCenter );
  605. int objectType = s_DetailObjectDict.Find(search);
  606. if (objectType < 0)
  607. {
  608. char szTextureName[MAX_PATH];
  609. pMapFace->GetTextureName(szTextureName);
  610. Warning("Material %s uses unknown detail object type %s!\n", szTextureName, pDetailType);
  611. return;
  612. }
  613. // Emit objects on a particular face
  614. DetailObject_t& detail = s_DetailObjectDict[objectType];
  615. // Initialize the Random Number generators for detail prop placement based on the origFace num.
  616. int detailpropseed = pMapFace->GetFaceID();
  617. #ifdef WARNSEEDNUMBER
  618. Warning("[%d]\n",detailpropseed);
  619. #endif
  620. srand( detailpropseed );
  621. RandomSeed( detailpropseed );
  622. if ( pMapFace->HasDisp() )
  623. {
  624. pDetails->EmitDetailObjectsOnDisplacementFace( pMapFace, detail );
  625. }
  626. else
  627. {
  628. pDetails->EmitDetailObjectsOnFace( pMapFace, detail );
  629. }
  630. }
  631. else
  632. {
  633. Warning("Could not allocate DetailObject for CMapFace!\n");
  634. }
  635. }
  636. void DetailObjects::EnableBuildDetailObjects( bool bEnable )
  637. {
  638. s_bBuildDetailObjects = bEnable;
  639. }
  640. void DetailObjects::Render3D(CRender3D *pRender)
  641. {
  642. Vector Mins, Maxs;
  643. float fDetailDistance = Options.view3d.nDetailDistance;
  644. Vector viewPoint; pRender->GetCamera()->GetViewPoint( viewPoint );
  645. int models = m_DetailModels.Count();
  646. if ( models )
  647. {
  648. pRender->PushRenderMode( RENDER_MODE_DEFAULT );
  649. for ( int i = 0; i < models; i++ )
  650. {
  651. StudioModel *pModel = m_DetailModels[i];
  652. pModel->GetOrigin(Mins);
  653. pModel->GetOrigin(Maxs);
  654. for( int j=0; j<3; j++ )
  655. {
  656. Mins[j] -= fDetailDistance;
  657. Maxs[j] += fDetailDistance;
  658. }
  659. if ( IsPointInBox( viewPoint, Mins, Maxs ) )
  660. pModel->DrawModel3D( pRender, Color(255, 255, 255, 255), 1, false );
  661. }
  662. pRender->PopRenderMode();
  663. }
  664. int sprites = m_DetailSprites.Count();
  665. if ( sprites )
  666. {
  667. unsigned char color[3] = { 255, 255, 255 };
  668. pRender->PushRenderMode( RENDER_MODE_DEFAULT );
  669. for ( int i = 0; i < sprites; i++ )
  670. {
  671. CSpriteModel *pSprite = m_DetailSprites[i];
  672. pSprite->GetOrigin(Mins);
  673. pSprite->GetOrigin(Maxs);
  674. for( int j=0; j<3; j++ )
  675. {
  676. Mins[j] -= fDetailDistance;
  677. Maxs[j] += fDetailDistance;
  678. }
  679. if ( IsPointInBox( viewPoint, Mins, Maxs ) )
  680. pSprite->DrawSprite3D( pRender, color );
  681. }
  682. pRender->PopRenderMode();
  683. }
  684. }
  685. // EOF