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.

653 lines
16 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //
  7. //===========================================================================//
  8. // trace.c
  9. //=============================================================================
  10. #include "vrad.h"
  11. #include "trace.h"
  12. #include "Cmodel.h"
  13. #include "mathlib/vmatrix.h"
  14. //=============================================================================
  15. class CToolTrace : public CBaseTrace
  16. {
  17. public:
  18. CToolTrace() {}
  19. Vector mins;
  20. Vector maxs;
  21. Vector extents;
  22. texinfo_t *surface;
  23. qboolean ispoint;
  24. private:
  25. CToolTrace( const CToolTrace& );
  26. };
  27. // 1/32 epsilon to keep floating point happy
  28. #define DIST_EPSILON (0.03125)
  29. // JAYHL2: This used to be -1, but that caused lots of epsilon issues
  30. // around slow sloping planes. Perhaps Quake2 limited maps to a certain
  31. // slope / angle on walkable ground. It has to be a negative number
  32. // so that the tests work out.
  33. #define NEVER_UPDATED -9999
  34. //=============================================================================
  35. bool DM_RayDispIntersectTest( CVRADDispColl *pTree, Vector& rayStart, Vector& rayEnd, CToolTrace *pTrace );
  36. void DM_ClipBoxToBrush( CToolTrace *trace, const Vector & mins, const Vector & maxs, const Vector& p1, const Vector& p2, dbrush_t *brush );
  37. //=============================================================================
  38. float TraceLeafBrushes( int leafIndex, const Vector &start, const Vector &end, CBaseTrace &traceOut )
  39. {
  40. dleaf_t *pLeaf = dleafs + leafIndex;
  41. CToolTrace trace;
  42. memset( &trace, 0, sizeof(trace) );
  43. trace.ispoint = true;
  44. trace.startsolid = false;
  45. trace.fraction = 1.0;
  46. for ( int i = 0; i < pLeaf->numleafbrushes; i++ )
  47. {
  48. int brushnum = dleafbrushes[pLeaf->firstleafbrush+i];
  49. dbrush_t *b = &dbrushes[brushnum];
  50. if ( !(b->contents & MASK_OPAQUE))
  51. continue;
  52. Vector zeroExtents = vec3_origin;
  53. DM_ClipBoxToBrush( &trace, zeroExtents, zeroExtents, start, end, b);
  54. if ( trace.fraction != 1.0 || trace.startsolid )
  55. {
  56. if ( trace.startsolid )
  57. trace.fraction = 0.0f;
  58. traceOut = trace;
  59. return trace.fraction;
  60. }
  61. }
  62. traceOut = trace;
  63. return 1.0f;
  64. }
  65. DispTested_t s_DispTested[MAX_TOOL_THREADS+1];
  66. // this just uses the average coverage for the triangle
  67. class CCoverageCount : public ITransparentTriangleCallback
  68. {
  69. public:
  70. CCoverageCount()
  71. {
  72. m_coverage = Four_Zeros;
  73. }
  74. virtual bool VisitTriangle_ShouldContinue( const TriIntersectData_t &triangle, const FourRays &rays, fltx4 *pHitMask, fltx4 *b0, fltx4 *b1, fltx4 *b2, int32 hitID )
  75. {
  76. float color = g_RtEnv.GetTriangleColor( hitID ).x;
  77. m_coverage = AddSIMD( m_coverage, AndSIMD ( *pHitMask, ReplicateX4 ( color ) ) );
  78. m_coverage = MinSIMD( m_coverage, Four_Ones );
  79. fltx4 onesMask = CmpEqSIMD( m_coverage, Four_Ones );
  80. // we should continue if the ones that hit the triangle have onesMask set to zero
  81. // so hitMask & onesMask != hitMask
  82. // so hitMask & onesMask == hitMask means we're done
  83. // so ts(hitMask & onesMask == hitMask) != 0xF says go on
  84. return 0xF != TestSignSIMD ( CmpEqSIMD ( AndSIMD( *pHitMask, onesMask ), *pHitMask ) );
  85. }
  86. fltx4 GetCoverage()
  87. {
  88. return m_coverage;
  89. }
  90. fltx4 GetFractionVisible()
  91. {
  92. return SubSIMD ( Four_Ones, m_coverage );
  93. }
  94. fltx4 m_coverage;
  95. };
  96. // this will sample the texture to get a coverage at the ray intersection point
  97. class CCoverageCountTexture : public CCoverageCount
  98. {
  99. public:
  100. virtual bool VisitTriangle_ShouldContinue( const TriIntersectData_t &triangle, const FourRays &rays, fltx4 *pHitMask, fltx4 *b0, fltx4 *b1, fltx4 *b2, int32 hitID )
  101. {
  102. int sign = TestSignSIMD( *pHitMask );
  103. float addedCoverage[4];
  104. for ( int s = 0; s < 4; s++)
  105. {
  106. addedCoverage[s] = 0.0f;
  107. if ( ( sign >> s) & 0x1 )
  108. {
  109. addedCoverage[s] = ComputeCoverageFromTexture( b0->m128_f32[s], b1->m128_f32[s], b2->m128_f32[s], hitID );
  110. }
  111. }
  112. m_coverage = AddSIMD( m_coverage, LoadUnalignedSIMD( addedCoverage ) );
  113. m_coverage = MinSIMD( m_coverage, Four_Ones );
  114. fltx4 onesMask = CmpEqSIMD( m_coverage, Four_Ones );
  115. // we should continue if the ones that hit the triangle have onesMask set to zero
  116. // so hitMask & onesMask != hitMask
  117. // so hitMask & onesMask == hitMask means we're done
  118. // so ts(hitMask & onesMask == hitMask) != 0xF says go on
  119. return 0xF != TestSignSIMD ( CmpEqSIMD ( AndSIMD( *pHitMask, onesMask ), *pHitMask ) );
  120. }
  121. };
  122. void TestLine( const FourVectors& start, const FourVectors& stop,
  123. fltx4 *pFractionVisible, int static_prop_index_to_ignore )
  124. {
  125. FourRays myrays;
  126. myrays.origin = start;
  127. myrays.direction = stop;
  128. myrays.direction -= myrays.origin;
  129. fltx4 len = myrays.direction.length();
  130. myrays.direction *= ReciprocalSIMD( len );
  131. RayTracingResult rt_result;
  132. CCoverageCountTexture coverageCallback;
  133. g_RtEnv.Trace4Rays(myrays, Four_Zeros, len, &rt_result, TRACE_ID_STATICPROP | static_prop_index_to_ignore, g_bTextureShadows ? &coverageCallback : 0 );
  134. // Assume we can see the targets unless we get hits
  135. float visibility[4];
  136. for ( int i = 0; i < 4; i++ )
  137. {
  138. visibility[i] = 1.0f;
  139. if ( ( rt_result.HitIds[i] != -1 ) &&
  140. ( rt_result.HitDistance.m128_f32[i] < len.m128_f32[i] ) )
  141. {
  142. visibility[i] = 0.0f;
  143. }
  144. }
  145. *pFractionVisible = LoadUnalignedSIMD( visibility );
  146. if ( g_bTextureShadows )
  147. *pFractionVisible = MinSIMD( *pFractionVisible, coverageCallback.GetFractionVisible() );
  148. }
  149. /*
  150. ================
  151. DM_ClipBoxToBrush
  152. ================
  153. */
  154. void DM_ClipBoxToBrush( CToolTrace *trace, const Vector& mins, const Vector& maxs, const Vector& p1, const Vector& p2,
  155. dbrush_t *brush)
  156. {
  157. dplane_t *plane, *clipplane;
  158. float dist;
  159. Vector ofs;
  160. float d1, d2;
  161. float f;
  162. dbrushside_t *side, *leadside;
  163. if (!brush->numsides)
  164. return;
  165. float enterfrac = NEVER_UPDATED;
  166. float leavefrac = 1.f;
  167. clipplane = NULL;
  168. bool getout = false;
  169. bool startout = false;
  170. leadside = NULL;
  171. // Loop interchanged, so we don't have to check trace->ispoint every side.
  172. if ( !trace->ispoint )
  173. {
  174. for (int i=0 ; i<brush->numsides ; ++i)
  175. {
  176. side = &dbrushsides[brush->firstside+i];
  177. plane = dplanes + side->planenum;
  178. // FIXME: special case for axial
  179. // general box case
  180. // push the plane out apropriately for mins/maxs
  181. // FIXME: use signbits into 8 way lookup for each mins/maxs
  182. ofs.x = (plane->normal.x < 0) ? maxs.x : mins.x;
  183. ofs.y = (plane->normal.y < 0) ? maxs.y : mins.y;
  184. ofs.z = (plane->normal.z < 0) ? maxs.z : mins.z;
  185. // for (j=0 ; j<3 ; j++)
  186. // {
  187. // Set signmask to either 0 if the sign is negative, or 0xFFFFFFFF is the sign is positive:
  188. //int signmask = (((*(int *)&(plane->normal[j]))&0x80000000) >> 31) - 1;
  189. //float temp = maxs[j];
  190. //*(int *)&(ofs[j]) = (~signmask) & (*(int *)&temp);
  191. //float temp1 = mins[j];
  192. //*(int *)&(ofs[j]) |= (signmask) & (*(int *)&temp1);
  193. // }
  194. dist = DotProduct (ofs, plane->normal);
  195. dist = plane->dist - dist;
  196. d1 = DotProduct (p1, plane->normal) - dist;
  197. d2 = DotProduct (p2, plane->normal) - dist;
  198. // if completely in front of face, no intersection
  199. if (d1 > 0 && d2 > 0)
  200. return;
  201. if (d2 > 0)
  202. getout = true; // endpoint is not in solid
  203. if (d1 > 0)
  204. startout = true;
  205. if (d1 <= 0 && d2 <= 0)
  206. continue;
  207. // crosses face
  208. if (d1 > d2)
  209. { // enter
  210. f = (d1-DIST_EPSILON) / (d1-d2);
  211. if (f > enterfrac)
  212. {
  213. enterfrac = f;
  214. clipplane = plane;
  215. leadside = side;
  216. }
  217. }
  218. else
  219. { // leave
  220. f = (d1+DIST_EPSILON) / (d1-d2);
  221. if (f < leavefrac)
  222. leavefrac = f;
  223. }
  224. }
  225. }
  226. else
  227. {
  228. for (int i=0 ; i<brush->numsides ; ++i)
  229. {
  230. side = &dbrushsides[brush->firstside+i];
  231. plane = dplanes + side->planenum;
  232. // FIXME: special case for axial
  233. // special point case
  234. // don't ray trace against bevel planes
  235. if( side->bevel == 1 )
  236. continue;
  237. dist = plane->dist;
  238. d1 = DotProduct (p1, plane->normal) - dist;
  239. d2 = DotProduct (p2, plane->normal) - dist;
  240. // if completely in front of face, no intersection
  241. if (d1 > 0 && d2 > 0)
  242. return;
  243. if (d2 > 0)
  244. getout = true; // endpoint is not in solid
  245. if (d1 > 0)
  246. startout = true;
  247. if (d1 <= 0 && d2 <= 0)
  248. continue;
  249. // crosses face
  250. if (d1 > d2)
  251. { // enter
  252. f = (d1-DIST_EPSILON) / (d1-d2);
  253. if (f > enterfrac)
  254. {
  255. enterfrac = f;
  256. clipplane = plane;
  257. leadside = side;
  258. }
  259. }
  260. else
  261. { // leave
  262. f = (d1+DIST_EPSILON) / (d1-d2);
  263. if (f < leavefrac)
  264. leavefrac = f;
  265. }
  266. }
  267. }
  268. if (!startout)
  269. { // original point was inside brush
  270. trace->startsolid = true;
  271. if (!getout)
  272. trace->allsolid = true;
  273. return;
  274. }
  275. if (enterfrac < leavefrac)
  276. {
  277. if (enterfrac > NEVER_UPDATED && enterfrac < trace->fraction)
  278. {
  279. if (enterfrac < 0)
  280. enterfrac = 0;
  281. trace->fraction = enterfrac;
  282. trace->plane.dist = clipplane->dist;
  283. trace->plane.normal = clipplane->normal;
  284. trace->plane.type = clipplane->type;
  285. if (leadside->texinfo!=-1)
  286. trace->surface = &texinfo[leadside->texinfo];
  287. else
  288. trace->surface = 0;
  289. trace->contents = brush->contents;
  290. }
  291. }
  292. }
  293. void TestLine_DoesHitSky( FourVectors const& start, FourVectors const& stop,
  294. fltx4 *pFractionVisible, bool canRecurse, int static_prop_to_skip, bool bDoDebug )
  295. {
  296. FourRays myrays;
  297. myrays.origin = start;
  298. myrays.direction = stop;
  299. myrays.direction -= myrays.origin;
  300. fltx4 len = myrays.direction.length();
  301. myrays.direction *= ReciprocalSIMD( len );
  302. RayTracingResult rt_result;
  303. CCoverageCountTexture coverageCallback;
  304. g_RtEnv.Trace4Rays(myrays, Four_Zeros, len, &rt_result, TRACE_ID_STATICPROP | static_prop_to_skip, g_bTextureShadows? &coverageCallback : 0);
  305. if ( bDoDebug )
  306. {
  307. WriteTrace( "trace.txt", myrays, rt_result );
  308. }
  309. float aOcclusion[4];
  310. for ( int i = 0; i < 4; i++ )
  311. {
  312. aOcclusion[i] = 0.0f;
  313. if ( ( rt_result.HitIds[i] != -1 ) &&
  314. ( rt_result.HitDistance.m128_f32[i] < len.m128_f32[i] ) )
  315. {
  316. int id = g_RtEnv.OptimizedTriangleList[rt_result.HitIds[i]].m_Data.m_IntersectData.m_nTriangleID;
  317. if ( !( id & TRACE_ID_SKY ) )
  318. aOcclusion[i] = 1.0f;
  319. }
  320. }
  321. fltx4 occlusion = LoadUnalignedSIMD( aOcclusion );
  322. if (g_bTextureShadows)
  323. occlusion = MaxSIMD ( occlusion, coverageCallback.GetCoverage() );
  324. bool fullyOccluded = ( TestSignSIMD( CmpGeSIMD( occlusion, Four_Ones ) ) == 0xF );
  325. // if we hit sky, and we're not in a sky camera's area, try clipping into the 3D sky boxes
  326. if ( (! fullyOccluded) && canRecurse && (! g_bNoSkyRecurse ) )
  327. {
  328. FourVectors dir = stop;
  329. dir -= start;
  330. dir.VectorNormalize();
  331. int leafIndex = -1;
  332. leafIndex = PointLeafnum( start.Vec( 0 ) );
  333. if ( leafIndex >= 0 )
  334. {
  335. int area = dleafs[leafIndex].area;
  336. if (area >= 0 && area < numareas)
  337. {
  338. if (area_sky_cameras[area] < 0)
  339. {
  340. int cam;
  341. for (cam = 0; cam < num_sky_cameras; ++cam)
  342. {
  343. FourVectors skystart, skytrans, skystop;
  344. skystart.DuplicateVector( sky_cameras[cam].origin );
  345. skystop = start;
  346. skystop *= sky_cameras[cam].world_to_sky;
  347. skystart += skystop;
  348. skystop = dir;
  349. skystop *= MAX_TRACE_LENGTH;
  350. skystop += skystart;
  351. TestLine_DoesHitSky ( skystart, skystop, pFractionVisible, false, static_prop_to_skip, bDoDebug );
  352. occlusion = AddSIMD ( occlusion, Four_Ones );
  353. occlusion = SubSIMD ( occlusion, *pFractionVisible );
  354. }
  355. }
  356. }
  357. }
  358. }
  359. occlusion = MaxSIMD( occlusion, Four_Zeros );
  360. occlusion = MinSIMD( occlusion, Four_Ones );
  361. *pFractionVisible = SubSIMD( Four_Ones, occlusion );
  362. }
  363. //-----------------------------------------------------------------------------
  364. //-----------------------------------------------------------------------------
  365. int PointLeafnum_r( const Vector &point, int ndxNode )
  366. {
  367. // while loop here is to avoid recursion overhead
  368. while( ndxNode >= 0 )
  369. {
  370. dnode_t *pNode = dnodes + ndxNode;
  371. dplane_t *pPlane = dplanes + pNode->planenum;
  372. float dist;
  373. if( pPlane->type < 3 )
  374. {
  375. dist = point[pPlane->type] - pPlane->dist;
  376. }
  377. else
  378. {
  379. dist = DotProduct( pPlane->normal, point ) - pPlane->dist;
  380. }
  381. if( dist < 0.0f )
  382. {
  383. ndxNode = pNode->children[1];
  384. }
  385. else
  386. {
  387. ndxNode = pNode->children[0];
  388. }
  389. }
  390. return ( -1 - ndxNode );
  391. }
  392. //-----------------------------------------------------------------------------
  393. //-----------------------------------------------------------------------------
  394. int PointLeafnum( const Vector &point )
  395. {
  396. return PointLeafnum_r( point, 0 );
  397. }
  398. // this iterates the list of entities looking for _vradshadows 1
  399. // each brush entity containing this key is added to the raytracing environment
  400. // as a triangle soup model.
  401. dmodel_t *BrushmodelForEntity( entity_t *pEntity )
  402. {
  403. const char *pModelname = ValueForKey( pEntity, "model" );
  404. if ( Q_strlen(pModelname) > 1 )
  405. {
  406. int modelIndex = atol( pModelname + 1 );
  407. if ( modelIndex > 0 && modelIndex < nummodels )
  408. {
  409. return &dmodels[modelIndex];
  410. }
  411. }
  412. return NULL;
  413. }
  414. void AddBrushToRaytraceEnvironment( dbrush_t *pBrush, const VMatrix &xform )
  415. {
  416. if ( !( pBrush->contents & MASK_OPAQUE ) )
  417. return;
  418. Vector v0, v1, v2;
  419. for (int i = 0; i < pBrush->numsides; i++ )
  420. {
  421. dbrushside_t *side = &dbrushsides[pBrush->firstside + i];
  422. dplane_t *plane = &dplanes[side->planenum];
  423. texinfo_t *tx = &texinfo[side->texinfo];
  424. winding_t *w = BaseWindingForPlane (plane->normal, plane->dist);
  425. if ( tx->flags & SURF_SKY || side->dispinfo )
  426. continue;
  427. for (int j=0 ; j<pBrush->numsides && w; j++)
  428. {
  429. if (i == j)
  430. continue;
  431. dbrushside_t *pOtherSide = &dbrushsides[pBrush->firstside + j];
  432. if (pOtherSide->bevel)
  433. continue;
  434. plane = &dplanes[pOtherSide->planenum^1];
  435. ChopWindingInPlace (&w, plane->normal, plane->dist, 0);
  436. }
  437. if ( w )
  438. {
  439. for ( int j = 2; j < w->numpoints; j++ )
  440. {
  441. v0 = xform.VMul4x3(w->p[0]);
  442. v1 = xform.VMul4x3(w->p[j-1]);
  443. v2 = xform.VMul4x3(w->p[j]);
  444. Vector fullCoverage;
  445. fullCoverage.x = 1.0f;
  446. g_RtEnv.AddTriangle(TRACE_ID_OPAQUE, v0, v1, v2, fullCoverage);
  447. }
  448. FreeWinding( w );
  449. }
  450. }
  451. }
  452. // recurse the bsp and build a list of brushes at the leaves under this node
  453. void GetBrushes_r( int node, CUtlVector<int> &list )
  454. {
  455. if ( node < 0 )
  456. {
  457. int leafIndex = -1 - node;
  458. // Add the solids in the leaf
  459. for ( int i = 0; i < dleafs[leafIndex].numleafbrushes; i++ )
  460. {
  461. int brushIndex = dleafbrushes[dleafs[leafIndex].firstleafbrush + i];
  462. if ( list.Find(brushIndex) < 0 )
  463. {
  464. list.AddToTail( brushIndex );
  465. }
  466. }
  467. }
  468. else
  469. {
  470. // recurse
  471. dnode_t *pnode = dnodes + node;
  472. GetBrushes_r( pnode->children[0], list );
  473. GetBrushes_r( pnode->children[1], list );
  474. }
  475. }
  476. void AddBrushes( dmodel_t *pModel, const VMatrix &xform )
  477. {
  478. if ( pModel )
  479. {
  480. CUtlVector<int> brushList;
  481. GetBrushes_r( pModel->headnode, brushList );
  482. for ( int i = 0; i < brushList.Count(); i++ )
  483. {
  484. int ndxBrush = brushList[i];
  485. AddBrushToRaytraceEnvironment( &dbrushes[ndxBrush], xform );
  486. }
  487. }
  488. }
  489. // Adds the brush entities that cast shadows to the raytrace environment
  490. void ExtractBrushEntityShadowCasters()
  491. {
  492. for ( int i = 0; i < num_entities; i++ )
  493. {
  494. if ( IntForKey( &entities[i], "vrad_brush_cast_shadows" ) != 0 )
  495. {
  496. Vector origin;
  497. QAngle angles;
  498. GetVectorForKey( &entities[i], "origin", origin );
  499. GetAnglesForKey( &entities[i], "angles", angles );
  500. VMatrix xform;
  501. xform.SetupMatrixOrgAngles( origin, angles );
  502. AddBrushes( BrushmodelForEntity( &entities[i] ), xform );
  503. }
  504. }
  505. }
  506. void AddBrushesForRayTrace( void )
  507. {
  508. if ( !nummodels )
  509. return;
  510. VMatrix identity;
  511. identity.Identity();
  512. CUtlVector<int> brushList;
  513. GetBrushes_r ( dmodels[0].headnode, brushList );
  514. for ( int i = 0; i < brushList.Size(); i++ )
  515. {
  516. dbrush_t *brush = &dbrushes[brushList[i]];
  517. AddBrushToRaytraceEnvironment ( brush, identity );
  518. }
  519. for ( int i = 0; i < dmodels[0].numfaces; i++ )
  520. {
  521. int ndxFace = dmodels[0].firstface + i;
  522. dface_t *face = &g_pFaces[ndxFace];
  523. texinfo_t *tx = &texinfo[face->texinfo];
  524. if ( !( tx->flags & SURF_SKY ) )
  525. continue;
  526. Vector points[MAX_POINTS_ON_WINDING];
  527. for ( int j = 0; j < face->numedges; j++ )
  528. {
  529. if ( j >= MAX_POINTS_ON_WINDING )
  530. Error( "***** ERROR! MAX_POINTS_ON_WINDING reached!" );
  531. if ( face->firstedge + j >= ARRAYSIZE( dsurfedges ) )
  532. Error( "***** ERROR! face->firstedge + j >= ARRAYSIZE( dsurfedges )!" );
  533. int surfEdge = dsurfedges[face->firstedge + j];
  534. unsigned short v;
  535. if (surfEdge < 0)
  536. v = dedges[-surfEdge].v[1];
  537. else
  538. v = dedges[surfEdge].v[0];
  539. if ( v >= ARRAYSIZE( dvertexes ) )
  540. Error( "***** ERROR! v(%u) >= ARRAYSIZE( dvertexes(%d) )!", ( unsigned int )v, ARRAYSIZE( dvertexes ) );
  541. dvertex_t *dv = &dvertexes[v];
  542. points[j] = dv->point;
  543. }
  544. for ( int j = 2; j < face->numedges; j++ )
  545. {
  546. Vector fullCoverage;
  547. fullCoverage.x = 1.0f;
  548. g_RtEnv.AddTriangle ( TRACE_ID_SKY, points[0], points[j - 1], points[j], fullCoverage );
  549. }
  550. }
  551. }