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.

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