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.

1156 lines
37 KiB

  1. // $Id$
  2. #include "raytrace.h"
  3. #include <filesystem_tools.h>
  4. #include <cmdlib.h>
  5. #include <stdio.h>
  6. // NOTE: This has to be the last file included!
  7. #include "tier0/memdbgon.h"
  8. static bool SameSign(float a, float b)
  9. {
  10. int32 aa = *( (int * ) & a );
  11. int32 bb = *( (int * ) & b );
  12. return ( (aa ^ bb ) & 0x80000000 ) == 0;
  13. }
  14. int FourRays::CalculateDirectionSignMask(void) const
  15. {
  16. // this code treats the floats as integers since all it cares about is the sign bit and
  17. // floating point compares suck.
  18. int ret;
  19. int ormask;
  20. int andmask;
  21. int32 const *treat_as_int = ( (int32 const * ) ( &direction ));
  22. ormask = andmask =* ( treat_as_int++ );
  23. ormask |=* treat_as_int;
  24. andmask &=* ( treat_as_int++ );
  25. ormask |=* ( treat_as_int );
  26. andmask &=* ( treat_as_int++ );
  27. ormask |=* ( treat_as_int );
  28. andmask &=* ( treat_as_int++ );
  29. if ( ormask >= 0 )
  30. ret = 0;
  31. else
  32. {
  33. if ( andmask < 0 )
  34. ret = 1;
  35. else return - 1;
  36. }
  37. ormask = andmask =* ( treat_as_int++ );
  38. ormask |=* treat_as_int;
  39. andmask &=* ( treat_as_int++ );
  40. ormask |=* ( treat_as_int );
  41. andmask &=* ( treat_as_int++ );
  42. ormask |=* ( treat_as_int );
  43. andmask &=* ( treat_as_int++ );
  44. if ( ormask < 0 )
  45. {
  46. if ( andmask < 0 )
  47. ret |= 2;
  48. else return - 1;
  49. }
  50. ormask = andmask =* ( treat_as_int++ );
  51. ormask |= *treat_as_int;
  52. andmask &= *( treat_as_int++ );
  53. ormask |= *( treat_as_int );
  54. andmask &= *( treat_as_int++ );
  55. ormask |= *( treat_as_int );
  56. andmask &= *( treat_as_int++ );
  57. if ( ormask < 0 )
  58. {
  59. if ( andmask < 0 )
  60. ret |= 4;
  61. else return - 1;
  62. }
  63. return ret;
  64. }
  65. void RayTracingEnvironment::MakeRoomForTriangles( int ntris )
  66. {
  67. //OptimizedTriangleList.EnsureCapacity( ntris );
  68. if (! (Flags & RTE_FLAGS_DONT_STORE_TRIANGLE_COLORS))
  69. TriangleColors.EnsureCapacity( ntris );
  70. }
  71. void RayTracingEnvironment::AddTriangle(int32 id, const Vector &v1,
  72. const Vector &v2, const Vector &v3,
  73. const Vector &color)
  74. {
  75. AddTriangle( id, v1, v2, v3, color, 0, 0 );
  76. }
  77. void RayTracingEnvironment::AddTriangle(int32 id, const Vector &v1,
  78. const Vector &v2, const Vector &v3,
  79. const Vector &color, uint16 flags, int32 materialIndex)
  80. {
  81. CacheOptimizedTriangle tmptri;
  82. tmptri.m_Data.m_GeometryData.m_nTriangleID = id;
  83. tmptri.Vertex( 0 ) = v1;
  84. tmptri.Vertex( 1 ) = v2;
  85. tmptri.Vertex( 2 ) = v3;
  86. tmptri.m_Data.m_GeometryData.m_nFlags = flags;
  87. OptimizedTriangleList.AddToTail( tmptri );
  88. if ( ! ( Flags & RTE_FLAGS_DONT_STORE_TRIANGLE_COLORS ) )
  89. TriangleColors.AddToTail( color );
  90. if ( ! ( Flags & RTE_FLAGS_DONT_STORE_TRIANGLE_MATERIALS ) )
  91. TriangleMaterials.AddToTail( materialIndex );
  92. // printf("add triange from (%f %f %f),(%f %f %f),(%f %f %f) id %d\n",
  93. // XYZ(v1),XYZ(v2),XYZ(v3),id);
  94. }
  95. void RayTracingEnvironment::AddQuad(
  96. int32 id, const Vector &v1, const Vector &v2, const Vector &v3,
  97. const Vector &v4, // specify vertices in cw or ccw order
  98. const Vector &color)
  99. {
  100. AddTriangle( id, v1, v2, v3, color );
  101. AddTriangle( id + 1, v1, v3, v4, color );
  102. }
  103. void RayTracingEnvironment::AddAxisAlignedRectangularSolid(int id,Vector minc, Vector maxc,
  104. const Vector &color)
  105. {
  106. // "far" face
  107. AddQuad( id,
  108. Vector( minc.x, maxc.y, maxc.z ),
  109. Vector( maxc.x, maxc.y, maxc.z ), Vector( maxc.x, minc.y, maxc.z ),
  110. Vector( minc.x, minc.y, maxc.z ), color );
  111. // "near" face
  112. AddQuad( id,
  113. Vector( minc.x, maxc.y, minc.z ),
  114. Vector( maxc.x, maxc.y, minc.z ), Vector( maxc.x, minc.y, minc.z ),
  115. Vector( minc.x, minc.y, minc.z ), color );
  116. // "left" face
  117. AddQuad( id,
  118. Vector( minc.x, maxc.y, maxc.z ),
  119. Vector( minc.x, maxc.y, minc.z ),
  120. Vector( minc.x, minc.y, minc.z ),
  121. Vector( minc.x, minc.y, maxc.z ), color );
  122. // "right" face
  123. AddQuad( id,
  124. Vector( maxc.x, maxc.y, maxc.z ),
  125. Vector( maxc.x, maxc.y, minc.z ),
  126. Vector( maxc.x, minc.y, minc.z ),
  127. Vector( maxc.x, minc.y, maxc.z ), color );
  128. // "top" face
  129. AddQuad( id,
  130. Vector( minc.x, maxc.y, maxc.z ),
  131. Vector( maxc.x, maxc.y, maxc.z ),
  132. Vector( maxc.x, maxc.y, minc.z ),
  133. Vector( minc.x, maxc.y, minc.z ), color );
  134. // "bot" face
  135. AddQuad( id,
  136. Vector( minc.x, minc.y, maxc.z ),
  137. Vector( maxc.x, minc.y, maxc.z ),
  138. Vector( maxc.x, minc.y, minc.z ),
  139. Vector( minc.x, minc.y, minc.z ), color );
  140. }
  141. static Vector GetEdgeEquation( Vector p1, Vector p2, int c1, int c2, Vector InsidePoint )
  142. {
  143. float nx = p1[c2]- p2[c2];
  144. float ny = p2[c1]- p1[c1];
  145. float d =- ( nx * p1[c1]+ ny * p1[c2] );
  146. // assert(fabs(nx*p1[c1]+ny*p1[c2]+d)<0.01);
  147. // assert(fabs(nx*p2[c1]+ny*p2[c2]+d)<0.01);
  148. // use the convention that negative is "outside"
  149. float trial_dist = InsidePoint[c1]* nx + InsidePoint[c2]* ny + d;
  150. if ( trial_dist < 0 )
  151. {
  152. nx = -nx;
  153. ny = -ny;
  154. d = -d;
  155. trial_dist = -trial_dist;
  156. }
  157. nx /= trial_dist; // scale so that it will be =1.0 at the oppositve vertex
  158. ny /= trial_dist;
  159. d /= trial_dist;
  160. return Vector( nx, ny, d );
  161. }
  162. void CacheOptimizedTriangle::ChangeIntoIntersectionFormat(void)
  163. {
  164. // lose the vertices and use edge equations instead
  165. // grab the whole original triangle to we don't overwrite it
  166. TriGeometryData_t srcTri = m_Data.m_GeometryData;
  167. m_Data.m_IntersectData.m_nFlags = srcTri.m_nFlags;
  168. m_Data.m_IntersectData.m_nTriangleID = srcTri.m_nTriangleID;
  169. Vector p1 = srcTri.Vertex( 0 );
  170. Vector p2 = srcTri.Vertex( 1 );
  171. Vector p3 = srcTri.Vertex( 2 );
  172. Vector e1 = p2 - p1;
  173. Vector e2 = p3 - p1;
  174. Vector N = e1.Cross( e2 );
  175. N.NormalizeInPlace();
  176. // now, determine which axis to drop
  177. int drop_axis = 0;
  178. for(int c=1 ; c<3 ; c++)
  179. if ( fabs(N[c]) > fabs( N[drop_axis] ) )
  180. drop_axis = c;
  181. m_Data.m_IntersectData.m_flD = N.Dot( p1 );
  182. m_Data.m_IntersectData.m_flNx = N.x;
  183. m_Data.m_IntersectData.m_flNy = N.y;
  184. m_Data.m_IntersectData.m_flNz = N.z;
  185. // decide which axes to keep
  186. int nCoordSelect0 = ( drop_axis + 1 ) % 3;
  187. int nCoordSelect1 = ( drop_axis + 2 ) % 3;
  188. m_Data.m_IntersectData.m_nCoordSelect0 = nCoordSelect0;
  189. m_Data.m_IntersectData.m_nCoordSelect1 = nCoordSelect1;
  190. Vector edge1 = GetEdgeEquation( p1, p2, nCoordSelect0, nCoordSelect1, p3 );
  191. m_Data.m_IntersectData.m_ProjectedEdgeEquations[0] = edge1.x;
  192. m_Data.m_IntersectData.m_ProjectedEdgeEquations[1] = edge1.y;
  193. m_Data.m_IntersectData.m_ProjectedEdgeEquations[2] = edge1.z;
  194. Vector edge2 = GetEdgeEquation( p2, p3, nCoordSelect0, nCoordSelect1, p1 );
  195. m_Data.m_IntersectData.m_ProjectedEdgeEquations[3] = edge2.x;
  196. m_Data.m_IntersectData.m_ProjectedEdgeEquations[4] = edge2.y;
  197. m_Data.m_IntersectData.m_ProjectedEdgeEquations[5] = edge2.z;
  198. }
  199. int n_intersection_calculations=0;
  200. int CacheOptimizedTriangle::ClassifyAgainstAxisSplit(int split_plane, float split_value)
  201. {
  202. // classify a triangle against an axis-aligned plane
  203. float minc = Vertex( 0 )[split_plane];
  204. float maxc = minc;
  205. for( int v = 1; v < 3; v++ )
  206. {
  207. minc=MIN(minc,Vertex(v)[split_plane]);
  208. maxc=MAX(maxc,Vertex(v)[split_plane]);
  209. }
  210. if ( minc >= split_value )
  211. return PLANECHECK_POSITIVE;
  212. if ( maxc <= split_value )
  213. return PLANECHECK_NEGATIVE;
  214. if ( minc == maxc )
  215. return PLANECHECK_POSITIVE;
  216. return PLANECHECK_STRADDLING;
  217. }
  218. static Vector ProjectOntoPlaneFromAxis( const Vector &v, const Vector &vNormal, float flDist, int nAxis )
  219. {
  220. float flAxisDist = DotProduct( v, vNormal ) - flDist;
  221. flAxisDist /= vNormal[nAxis];
  222. Vector vOut = v;
  223. vOut[nAxis] -= flAxisDist;
  224. return vOut;
  225. }
  226. void CacheOptimizedTriangle::ExtractVerticesFromIntersectionFormat( Vector &v0, Vector &v1, Vector &v2 ) const
  227. {
  228. const TriIntersectData_t &data = m_Data.m_IntersectData;
  229. const float *pPlane0 = data.m_ProjectedEdgeEquations;
  230. const float *pPlane1 = data.m_ProjectedEdgeEquations + 3;
  231. Vector vNormal;
  232. vNormal.Init( data.m_flNx, data.m_flNy, data.m_flNz );
  233. int n0 = data.m_nCoordSelect0;
  234. int n1 = data.m_nCoordSelect1;
  235. int n2 = (n1+1)%3;
  236. float flOffset = -1.0f;
  237. float flOOscale = 1.0f / (pPlane0[0] * pPlane1[1] - pPlane1[0] * pPlane0[1]);
  238. // vert0 is at the intersection of the two edges with edge1's plane offset by 1
  239. v0[n0] = (pPlane0[1] * (pPlane1[2]+flOffset) - pPlane1[1] * pPlane0[2]) * flOOscale;
  240. v0[n1] = (pPlane1[0] * pPlane0[2] - pPlane0[0] * (pPlane1[2]+flOffset)) * flOOscale;
  241. v0[n2] = 0;
  242. // vert1 is at the intersection of the two edges with neither offset
  243. v1[n0] = (pPlane0[1] * pPlane1[2] - pPlane1[1] * pPlane0[2]) * flOOscale;
  244. v1[n1] = (pPlane1[0] * pPlane0[2] - pPlane0[0] * pPlane1[2]) * flOOscale;
  245. v1[n2] = 0;
  246. // vert2 is at the intersection of the two edges with edge0's plane offset by 1
  247. v2[n0] = (pPlane0[1] * pPlane1[2] - pPlane1[1] * (pPlane0[2]+flOffset)) * flOOscale;
  248. v2[n1] = (pPlane1[0] * (pPlane0[2]+flOffset) - pPlane0[0] * pPlane1[2]) * flOOscale;
  249. v2[n2] = 0;
  250. v0 = ProjectOntoPlaneFromAxis( v0, vNormal, data.m_flD, n2 );
  251. v1 = ProjectOntoPlaneFromAxis( v1, vNormal, data.m_flD, n2 );
  252. v2 = ProjectOntoPlaneFromAxis( v2, vNormal, data.m_flD, n2 );
  253. }
  254. #define MAILBOX_HASH_SIZE 256
  255. #define MAX_TREE_DEPTH 21
  256. #define MAX_NODE_STACK_LEN (40*MAX_TREE_DEPTH)
  257. struct NodeToVisit {
  258. CacheOptimizedKDNode const *node;
  259. fltx4 TMin;
  260. fltx4 TMax;
  261. };
  262. static fltx4 FourEpsilons={1.0e-10,1.0e-10,1.0e-10,1.0e-10};
  263. static fltx4 FourZeros={1.0e-10,1.0e-10,1.0e-10,1.0e-10};
  264. static fltx4 FourNegativeEpsilons={-1.0e-10,-1.0e-10,-1.0e-10,-1.0e-10};
  265. void RayTracingEnvironment::Trace4Rays( const FourRays &rays, fltx4 TMin, fltx4 TMax,
  266. RayTracingResult *rslt_out,
  267. int32 skip_id, ITransparentTriangleCallback *pCallback,
  268. RTECullMode_t cullMode )
  269. {
  270. int msk = rays.CalculateDirectionSignMask();
  271. if ( msk !=- 1 )
  272. Trace4Rays( rays, TMin, TMax, msk, rslt_out, skip_id, pCallback, cullMode );
  273. else
  274. {
  275. // sucky case - can't trace 4 rays at once. in the worst case, need to trace all 4
  276. // separately, but usually we will still get 2x, Since our tracer only does 4 at a
  277. // time, we will have to cover up the undesired rays with the desired ray
  278. //!! speed!! there is room for some sse-ization here
  279. FourRays tmprays;
  280. tmprays.origin = rays.origin;
  281. uint8 need_trace[4] = { 1, 1, 1, 1 };
  282. for( int try_trace = 0; try_trace < 4; try_trace++ )
  283. {
  284. if ( need_trace[try_trace] )
  285. {
  286. need_trace[try_trace] = 2; // going to trace it
  287. // replicate the ray being traced into all 4 rays
  288. tmprays.direction.x = ReplicateX4( rays.direction.X( try_trace ));
  289. tmprays.direction.y = ReplicateX4( rays.direction.Y( try_trace ));
  290. tmprays.direction.z = ReplicateX4( rays.direction.Z( try_trace ));
  291. // now, see if any of the other remaining rays can be handled at the same time.
  292. for( int try2 = try_trace + 1; try2 < 4; try2++ )
  293. if ( need_trace[try2] )
  294. {
  295. if (
  296. SameSign( rays.direction.X( try2 ),
  297. rays.direction.X( try_trace )) &&
  298. SameSign( rays.direction.Y( try2 ),
  299. rays.direction.Y( try_trace )) &&
  300. SameSign( rays.direction.Z( try2 ),
  301. rays.direction.Z( try_trace )) )
  302. {
  303. need_trace[try2] = 2;
  304. tmprays.direction.X( try2 ) = rays.direction.X( try2 );
  305. tmprays.direction.Y( try2 ) = rays.direction.Y( try2 );
  306. tmprays.direction.Z( try2 ) = rays.direction.Z( try2 );
  307. }
  308. }
  309. // ok, now trace between 1 and 3 rays, and output the results
  310. RayTracingResult tmpresults;
  311. msk = tmprays.CalculateDirectionSignMask();
  312. assert( msk !=- 1 );
  313. Trace4Rays( tmprays, TMin, TMax, msk, &tmpresults, skip_id, pCallback, cullMode );
  314. // now, move results to proper place
  315. for( int i = 0; i < 4; i++ )
  316. if ( need_trace[i] == 2 )
  317. {
  318. need_trace[i] = 0;
  319. rslt_out->HitIds[i] = tmpresults.HitIds[i];
  320. SubFloat( rslt_out->HitDistance, i ) = SubFloat( tmpresults.HitDistance, i );
  321. rslt_out->surface_normal.X( i ) = tmpresults.surface_normal.X( i );
  322. rslt_out->surface_normal.Y( i ) = tmpresults.surface_normal.Y( i );
  323. rslt_out->surface_normal.Z( i ) = tmpresults.surface_normal.Z( i );
  324. }
  325. }
  326. }
  327. }
  328. }
  329. template< RTECullMode_t cullMode >
  330. bi32x4 DidHit( fltx4 DDotN, bi32x4 epsilon_hit )
  331. {
  332. if ( cullMode == RTE_CULL_FRONT )
  333. {
  334. bi32x4 did_hit_back = CmpGtSIMD( DDotN, Four_Zeros );
  335. return AndSIMD( epsilon_hit, did_hit_back );
  336. }
  337. else if ( cullMode == RTE_CULL_BACK )
  338. {
  339. bi32x4 did_hit_front = CmpLtSIMD( DDotN, Four_Zeros );
  340. return AndSIMD( epsilon_hit, did_hit_front );
  341. }
  342. else
  343. {
  344. return epsilon_hit;
  345. }
  346. }
  347. // wrapper for the low level trace4 rays routine
  348. void RayTracingEnvironment::Trace4Rays( const FourRays &rays, fltx4 TMin, fltx4 TMax,int DirectionSignMask,
  349. RayTracingResult *rslt_out,
  350. int32 skip_id, ITransparentTriangleCallback *pCallback, RTECullMode_t cullMode )
  351. {
  352. switch ( cullMode )
  353. {
  354. case RTE_CULL_FRONT:
  355. Trace4Rays<RTE_CULL_FRONT>( rays, TMin, TMax, DirectionSignMask, rslt_out, skip_id, pCallback );
  356. break;
  357. case RTE_CULL_BACK:
  358. Trace4Rays<RTE_CULL_BACK>( rays, TMin, TMax, DirectionSignMask, rslt_out, skip_id, pCallback );
  359. break;
  360. default:
  361. Trace4Rays<RTE_CULL_NONE>( rays, TMin, TMax, DirectionSignMask, rslt_out, skip_id, pCallback );
  362. break;
  363. }
  364. }
  365. template <RTECullMode_t cullMode>
  366. void RayTracingEnvironment::Trace4Rays(const FourRays &rays, fltx4 TMin, fltx4 TMax,
  367. int DirectionSignMask, RayTracingResult *rslt_out,
  368. int32 skip_id, ITransparentTriangleCallback *pCallback )
  369. {
  370. rays.Check();
  371. memset( rslt_out->HitIds, 0xff, sizeof( rslt_out->HitIds ) );
  372. rslt_out->HitDistance = ReplicateX4( 1.0e23 );
  373. rslt_out->surface_normal.DuplicateVector( Vector( 0., 0., 0. ) );
  374. FourVectors OneOverRayDir = rays.direction;
  375. OneOverRayDir.MakeReciprocalSaturate();
  376. // now, clip rays against bounding box
  377. for( int c = 0; c < 3; c++ )
  378. {
  379. fltx4 isect_min_t =
  380. MulSIMD( SubSIMD( ReplicateX4( m_MinBound[c] ), rays.origin[c] ), OneOverRayDir[c] );
  381. fltx4 isect_max_t =
  382. MulSIMD( SubSIMD( ReplicateX4( m_MaxBound[c] ), rays.origin[c] ), OneOverRayDir[c] );
  383. TMin = MaxSIMD( TMin, MinSIMD( isect_min_t, isect_max_t ));
  384. TMax = MinSIMD( TMax, MaxSIMD( isect_min_t, isect_max_t ));
  385. }
  386. bi32x4 active = CmpLeSIMD( TMin, TMax ); // mask of which rays are active
  387. if ( ! IsAnyTrue( active ) )
  388. return; // missed bounding box
  389. int32 mailboxids[MAILBOX_HASH_SIZE]; // used to avoid redundant triangle tests
  390. memset( mailboxids, 0xff, sizeof( mailboxids )); // !!speed!! keep around?
  391. int front_idx[3], back_idx[3]; // based on ray direction, whether to
  392. // visit left or right node first
  393. if ( DirectionSignMask & 1 )
  394. {
  395. back_idx[0] = 0;
  396. front_idx[0] = 1;
  397. }
  398. else
  399. {
  400. back_idx[0] = 1;
  401. front_idx[0] = 0;
  402. }
  403. if ( DirectionSignMask & 2 )
  404. {
  405. back_idx[1] = 0;
  406. front_idx[1] = 1;
  407. }
  408. else
  409. {
  410. back_idx[1] = 1;
  411. front_idx[1] = 0;
  412. }
  413. if ( DirectionSignMask & 4 )
  414. {
  415. back_idx[2] = 0;
  416. front_idx[2] = 1;
  417. }
  418. else
  419. {
  420. back_idx[2] = 1;
  421. front_idx[2] = 0;
  422. }
  423. NodeToVisit NodeQueue[MAX_NODE_STACK_LEN];
  424. CacheOptimizedKDNode const *CurNode =& ( OptimizedKDTree[0] );
  425. NodeToVisit *stack_ptr = &NodeQueue[MAX_NODE_STACK_LEN];
  426. while( 1 )
  427. {
  428. while ( CurNode->NodeType() != KDNODE_STATE_LEAF ) // traverse until next leaf
  429. {
  430. int split_plane_number = CurNode->NodeType();
  431. CacheOptimizedKDNode const *FrontChild = &( OptimizedKDTree[CurNode->LeftChild()] );
  432. fltx4 dist_to_sep_plane = // dist=(split-org)/dir
  433. MulSIMD(
  434. SubSIMD( ReplicateX4( CurNode->SplittingPlaneValue ),
  435. rays.origin[split_plane_number] ), OneOverRayDir[split_plane_number] );
  436. bi32x4 active = CmpLeSIMD( TMin, TMax ); // mask of which rays are active
  437. // now, decide how to traverse children. can either do front,back, or do front and push
  438. // back.
  439. bi32x4 hits_front = AndSIMD( active, CmpGeSIMD( dist_to_sep_plane, TMin ));
  440. if ( ! IsAnyTrue( hits_front ))
  441. {
  442. // missed the front. only traverse back
  443. //printf("only visit back %d\n",CurNode->LeftChild()+back_idx[split_plane_number]);
  444. CurNode = FrontChild + back_idx[split_plane_number];
  445. TMin = MaxSIMD( TMin, dist_to_sep_plane );
  446. }
  447. else
  448. {
  449. bi32x4 hits_back = AndSIMD( active, CmpLeSIMD( dist_to_sep_plane, TMax ));
  450. if ( ! IsAnyTrue( hits_back ) )
  451. {
  452. // missed the back - only need to traverse front node
  453. //printf("only visit front %d\n",CurNode->LeftChild()+front_idx[split_plane_number]);
  454. CurNode = FrontChild + front_idx[split_plane_number];
  455. TMax = MinSIMD( TMax, dist_to_sep_plane );
  456. }
  457. else
  458. {
  459. // at least some rays hit both nodes.
  460. // must push far, traverse near
  461. //printf("visit %d,%d\n",CurNode->LeftChild()+front_idx[split_plane_number],
  462. // CurNode->LeftChild()+back_idx[split_plane_number]);
  463. assert( stack_ptr > NodeQueue );
  464. -- stack_ptr;
  465. stack_ptr->node = FrontChild + back_idx[split_plane_number];
  466. stack_ptr->TMin = MaxSIMD( TMin, dist_to_sep_plane );
  467. stack_ptr->TMax = TMax;
  468. CurNode = FrontChild + front_idx[split_plane_number];
  469. TMax = MinSIMD( TMax, dist_to_sep_plane );
  470. }
  471. }
  472. }
  473. // hit a leaf! must do intersection check
  474. int ntris = CurNode->NumberOfTrianglesInLeaf();
  475. if ( ntris )
  476. {
  477. int32 const * tlist =& ( TriangleIndexList[CurNode->TriangleIndexStart()] );
  478. do
  479. {
  480. int tnum =* ( tlist++ );
  481. //printf("try tri %d\n",tnum);
  482. // check mailbox
  483. int mbox_slot = tnum & ( MAILBOX_HASH_SIZE - 1 );
  484. TriIntersectData_t const * tri = &( OptimizedTriangleList[tnum].m_Data.m_IntersectData );
  485. if ( ( mailboxids[mbox_slot] != tnum ) && ( tri->m_nTriangleID != skip_id ) )
  486. {
  487. n_intersection_calculations++;
  488. mailboxids[mbox_slot] = tnum;
  489. // compute plane intersection
  490. FourVectors N;
  491. N.x = ReplicateX4( tri->m_flNx );
  492. N.y = ReplicateX4( tri->m_flNy );
  493. N.z = ReplicateX4( tri->m_flNz );
  494. fltx4 DDotN = rays.direction * N;
  495. bi32x4 did_hit = OrSIMD( CmpGtSIMD( DDotN, FourEpsilons ),
  496. CmpLtSIMD( DDotN, FourNegativeEpsilons ) );
  497. did_hit = DidHit<cullMode>( DDotN, did_hit );
  498. fltx4 numerator = SubSIMD( ReplicateX4( tri->m_flD ), rays.origin * N );
  499. fltx4 isect_t = DivSIMD( numerator, DDotN );
  500. // now, we have the distance to the plane. lets update our mask
  501. did_hit = AndSIMD( did_hit, CmpGtSIMD( isect_t, FourZeros ) );
  502. //did_hit=AndSIMD(did_hit,CmpLtSIMD(isect_t,TMax));
  503. did_hit = AndSIMD( did_hit, CmpLtSIMD( isect_t, rslt_out->HitDistance ) );
  504. if ( ! IsAnyTrue( did_hit ) )
  505. continue;
  506. // now, check 3 edges
  507. fltx4 hitc1 = AddSIMD( rays.origin[tri->m_nCoordSelect0],
  508. MulSIMD( isect_t, rays.direction[ tri->m_nCoordSelect0] ) );
  509. fltx4 hitc2 = AddSIMD( rays.origin[tri->m_nCoordSelect1],
  510. MulSIMD( isect_t, rays.direction[tri->m_nCoordSelect1] ) );
  511. // do barycentric coordinate check
  512. fltx4 B0 = MulSIMD( ReplicateX4( tri->m_ProjectedEdgeEquations[0] ), hitc1 );
  513. B0 = AddSIMD(
  514. B0,
  515. MulSIMD( ReplicateX4( tri->m_ProjectedEdgeEquations[1] ), hitc2 ) );
  516. B0 = AddSIMD(
  517. B0, ReplicateX4( tri->m_ProjectedEdgeEquations[2] ) );
  518. did_hit = AndSIMD( did_hit, CmpGeSIMD( B0, FourZeros ) );
  519. fltx4 B1 = MulSIMD( ReplicateX4( tri->m_ProjectedEdgeEquations[3] ), hitc1 );
  520. B1 = AddSIMD(
  521. B1,
  522. MulSIMD( ReplicateX4( tri->m_ProjectedEdgeEquations[4]), hitc2 ) );
  523. B1 = AddSIMD(
  524. B1, ReplicateX4( tri->m_ProjectedEdgeEquations[5] ) );
  525. did_hit = AndSIMD( did_hit, CmpGeSIMD( B1, FourZeros ) );
  526. fltx4 B2 = AddSIMD( B1, B0 );
  527. did_hit = AndSIMD( did_hit, CmpLeSIMD( B2, Four_Ones ) );
  528. if ( ! IsAnyTrue( did_hit ) )
  529. continue;
  530. // if the triangle is transparent
  531. if ( tri->m_nFlags & FCACHETRI_TRANSPARENT )
  532. {
  533. if ( pCallback )
  534. {
  535. // assuming a triangle indexed as v0, v1, v2
  536. // the projected edge equations are set up such that the vert opposite the first
  537. // equation is v2, and the vert opposite the second equation is v0
  538. // Therefore we pass them back in 1, 2, 0 order
  539. // Also B2 is currently B1 + B0 and needs to be 1 - (B1+B0) in order to be a real
  540. // barycentric coordinate. Compute that now and pass it to the callback
  541. fltx4 b2 = SubSIMD( Four_Ones, B2 );
  542. if ( pCallback->VisitTriangle_ShouldContinue( *tri, rays, &did_hit, &B1, &b2, &B0, tnum ) )
  543. {
  544. did_hit = (bi32x4)Four_Zeros;
  545. }
  546. }
  547. }
  548. // now, set the hit_id and closest_hit fields for any enabled rays
  549. i32x4 replicated_n = ReplicateIX4(tnum);
  550. StoreAlignedSIMD( (float * ) rslt_out->HitIds,
  551. OrSIMD( AndSIMD( (bi32x4)replicated_n, did_hit ),
  552. AndNotSIMD( did_hit, LoadAlignedSIMD(
  553. ( float * ) rslt_out->HitIds )) ));
  554. rslt_out->HitDistance = OrSIMD( AndSIMD( isect_t, did_hit ),
  555. AndNotSIMD( did_hit, rslt_out->HitDistance ));
  556. rslt_out->surface_normal.x = OrSIMD(
  557. AndSIMD( N.x, did_hit ),
  558. AndNotSIMD( did_hit, rslt_out->surface_normal.x ));
  559. rslt_out->surface_normal.y = OrSIMD(
  560. AndSIMD( N.y, did_hit ),
  561. AndNotSIMD( did_hit, rslt_out->surface_normal.y ));
  562. rslt_out->surface_normal.z = OrSIMD(
  563. AndSIMD( N.z, did_hit ),
  564. AndNotSIMD( did_hit, rslt_out->surface_normal.z ));
  565. }
  566. } while (--ntris);
  567. // now, check if all rays have terminated
  568. bi32x4 raydone = CmpLeSIMD( TMax, rslt_out->HitDistance );
  569. if (! IsAnyTrue(raydone))
  570. {
  571. return;
  572. }
  573. }
  574. if ( stack_ptr == &NodeQueue[MAX_NODE_STACK_LEN] )
  575. {
  576. return;
  577. }
  578. // pop stack!
  579. CurNode = stack_ptr->node;
  580. TMin = stack_ptr->TMin;
  581. TMax = stack_ptr->TMax;
  582. stack_ptr++;
  583. }
  584. }
  585. int RayTracingEnvironment::MakeLeafNode(int first_tri, int last_tri)
  586. {
  587. CacheOptimizedKDNode ret;
  588. ret.Children = KDNODE_STATE_LEAF + ( TriangleIndexList.Count() << 2 );
  589. ret.SetNumberOfTrianglesInLeafNode( 1 + ( last_tri - first_tri ));
  590. for( int tnum = first_tri; tnum <= last_tri; tnum++ )
  591. TriangleIndexList.AddToTail( tnum );
  592. OptimizedKDTree.AddToTail( ret );
  593. return OptimizedKDTree.Count() - 1;
  594. }
  595. void RayTracingEnvironment::CalculateTriangleListBounds(int32 const *tris,int ntris,
  596. Vector &minout, Vector &maxout)
  597. {
  598. minout = Vector( 1.0e23, 1.0e23, 1.0e23 );
  599. maxout = Vector( - 1.0e23, - 1.0e23, - 1.0e23 );
  600. for( int i = 0; i < ntris; i++ )
  601. {
  602. CacheOptimizedTriangle const &tri = OptimizedTriangleList[tris[i]];
  603. for( int v = 0; v < 3; v++ )
  604. for( int c = 0; c < 3; c++ )
  605. {
  606. minout[c]=MIN(minout[c],tri.Vertex(v)[c]);
  607. maxout[c]=MAX(maxout[c],tri.Vertex(v)[c]);
  608. }
  609. }
  610. }
  611. // Both the "quick" and regular kd tree building algorithms here use the "surface area heuristic":
  612. // the relative probability of hitting the "left" subvolume (Vl) from a split is equal to that
  613. // subvolume's surface area divided by its parent's surface area (Vp) : P(Vl | V) = SA(Vl)/SA(Vp).
  614. // The same holds for the right subvolume, Vp. Nl is the number of triangles in the left volume,
  615. // and Nr in the right volume. if Ct is the cost of traversing one tree node, and Ci is the cost of
  616. // intersection with the primitive, than the cost of splitting is estimated as:
  617. //
  618. // Ct+Ci*((SA(Vl)/SA(V))*Nl+(SA(Vr)/SA(V)*Nr)).
  619. // and the cost of not splitting is
  620. // Ci*N
  621. //
  622. // This both provides a metric to minimize when computing how and where to split, and also a
  623. // termination criterion.
  624. //
  625. // the "quick" method just splits down the middle, while the slow method splits at the best
  626. // discontinuity of the cost formula. The quick method splits along the longest axis ; the
  627. // regular algorithm tries all 3 to find which one results in the minimum cost
  628. //
  629. // both methods use the additional optimization of "growing" empty nodes - if the split results in
  630. // one side being devoid of triangles, the empty side is "grown" as much as possible.
  631. //
  632. #define COST_OF_TRAVERSAL 75 // approximate #operations
  633. #define COST_OF_INTERSECTION 167 // approximate #operations
  634. float RayTracingEnvironment::CalculateCostsOfSplit(
  635. int split_plane,int32 const *tri_list,int ntris,
  636. Vector MinBound,Vector MaxBound, float &split_value,
  637. int &nleft, int &nright, int &nboth)
  638. {
  639. // determine the costs of splitting on a given axis, and label triangles with respect to
  640. // that axis by storing the value in coordselect0. It will also return the number of
  641. // tris in the left, right, and nboth groups, in order to facilitate memory
  642. nleft = nboth = nright = 0;
  643. // now, label each triangle. Since we have not converted the triangles into
  644. // intersection fromat yet, we can use the CoordSelect0 field of each as a temp.
  645. nleft = 0;
  646. nright = 0;
  647. nboth = 0;
  648. float min_coord = 1.0e23, max_coord =- 1.0e23;
  649. for( int t = 0; t < ntris; t++ )
  650. {
  651. CacheOptimizedTriangle &tri = OptimizedTriangleList[tri_list[t]];
  652. // determine max and min coordinate values for later optimization
  653. for( int v = 0; v < 3; v++ )
  654. {
  655. min_coord = MIN( min_coord, tri.Vertex(v)[split_plane] );
  656. max_coord = MAX( max_coord, tri.Vertex(v)[split_plane] );
  657. }
  658. switch( tri.ClassifyAgainstAxisSplit( split_plane, split_value ))
  659. {
  660. case PLANECHECK_NEGATIVE:
  661. nleft++;
  662. tri.m_Data.m_GeometryData.m_nTmpData0 = PLANECHECK_NEGATIVE;
  663. break;
  664. case PLANECHECK_POSITIVE:
  665. nright++;
  666. tri.m_Data.m_GeometryData.m_nTmpData0 = PLANECHECK_POSITIVE;
  667. break;
  668. case PLANECHECK_STRADDLING:
  669. nboth++;
  670. tri.m_Data.m_GeometryData.m_nTmpData0 = PLANECHECK_STRADDLING;
  671. break;
  672. }
  673. }
  674. // now, if the split resulted in one half being empty, "grow" the empty half
  675. if ( nleft && ( nboth == 0 ) && ( nright == 0 ))
  676. split_value = max_coord;
  677. if ( nright && ( nboth == 0 ) && ( nleft == 0 ))
  678. split_value = min_coord;
  679. // now, perform surface area/cost check to determine whether this split was worth it
  680. Vector LeftMins = MinBound;
  681. Vector LeftMaxes = MaxBound;
  682. Vector RightMins = MinBound;
  683. Vector RightMaxes = MaxBound;
  684. LeftMaxes[split_plane] = split_value;
  685. RightMins[split_plane] = split_value;
  686. float SA_L = BoxSurfaceArea( LeftMins, LeftMaxes );
  687. float SA_R = BoxSurfaceArea( RightMins, RightMaxes );
  688. float ISA = 1.0 / BoxSurfaceArea( MinBound, MaxBound );
  689. float cost_of_split = COST_OF_TRAVERSAL + COST_OF_INTERSECTION * ( nboth +
  690. ( SA_L * ISA * ( nleft )) + ( SA_R * ISA * ( nright )) );
  691. return cost_of_split;
  692. }
  693. #define NEVER_SPLIT 0
  694. void RayTracingEnvironment::RefineNode( int node_number, int32 const * tri_list, int ntris,
  695. Vector MinBound, Vector MaxBound, int depth )
  696. {
  697. if ( ntris < 3 ) // never split empty lists
  698. {
  699. // no point in continuing
  700. OptimizedKDTree[node_number].Children = KDNODE_STATE_LEAF + ( TriangleIndexList.Count() << 2 );
  701. OptimizedKDTree[node_number].SetNumberOfTrianglesInLeafNode( ntris );
  702. #ifdef DEBUG_RAYTRACE
  703. OptimizedKDTree[node_number].vecMins = MinBound;
  704. OptimizedKDTree[node_number].vecMaxs = MaxBound;
  705. #endif
  706. for( int t = 0; t < ntris; t++ )
  707. TriangleIndexList.AddToTail( tri_list[t] );
  708. return;
  709. }
  710. float best_cost = 1.0e23;
  711. int best_nleft = 0, best_nright = 0, best_nboth = 0;
  712. float best_splitvalue = 0;
  713. int split_plane = 0;
  714. int tri_skip = 1 + ( ntris / 10 ); // don't try all trinagles as split
  715. // points when there are a lot of them
  716. for( int axis = 0; axis < 3; axis++ )
  717. {
  718. for( int ts =- 1; ts < ntris; ts += tri_skip )
  719. {
  720. for( int tv = 0; tv < 3; tv++ )
  721. {
  722. int trial_nleft, trial_nright, trial_nboth;
  723. float trial_splitvalue;
  724. if ( ts ==- 1 )
  725. trial_splitvalue = 0.5 * ( MinBound[axis]+ MaxBound[axis] );
  726. else
  727. {
  728. // else, split at the triangle vertex if possible
  729. CacheOptimizedTriangle &tri = OptimizedTriangleList[tri_list[ts]];
  730. trial_splitvalue = tri.Vertex( tv )[axis];
  731. if ( (trial_splitvalue > MaxBound[axis] ) || ( trial_splitvalue < MinBound[axis] ))
  732. continue; // don't try this vertex - not inside
  733. }
  734. // printf("ts=%d tv=%d tp=%f\n",ts,tv,trial_splitvalue);
  735. float trial_cost =
  736. CalculateCostsOfSplit( axis, tri_list, ntris, MinBound, MaxBound, trial_splitvalue,
  737. trial_nleft, trial_nright, trial_nboth );
  738. // printf("try %d cost=%f nl=%d nr=%d nb=%d sp=%f\n",axis,trial_cost,trial_nleft,trial_nright, trial_nboth,
  739. // trial_splitvalue);
  740. if ( trial_cost < best_cost )
  741. {
  742. split_plane = axis;
  743. best_cost = trial_cost;
  744. best_nleft = trial_nleft;
  745. best_nright = trial_nright;
  746. best_nboth = trial_nboth;
  747. best_splitvalue = trial_splitvalue;
  748. // save away the axis classification of each triangle
  749. for( int t = 0 ; t < ntris; t++ )
  750. {
  751. CacheOptimizedTriangle &tri = OptimizedTriangleList[tri_list[t]];
  752. tri.m_Data.m_GeometryData.m_nTmpData1 = tri.m_Data.m_GeometryData.m_nTmpData0;
  753. }
  754. }
  755. if ( ts ==- 1 )
  756. break;
  757. }
  758. }
  759. }
  760. float cost_of_no_split = COST_OF_INTERSECTION * ntris;
  761. if ( ( cost_of_no_split <= best_cost ) || NEVER_SPLIT || ( depth > MAX_TREE_DEPTH ))
  762. {
  763. // no benefit to splitting. just make this a leaf node
  764. OptimizedKDTree[node_number].Children = KDNODE_STATE_LEAF + ( TriangleIndexList.Count() << 2 );
  765. OptimizedKDTree[node_number].SetNumberOfTrianglesInLeafNode( ntris );
  766. #ifdef DEBUG_RAYTRACE
  767. OptimizedKDTree[node_number].vecMins = MinBound;
  768. OptimizedKDTree[node_number].vecMaxs = MaxBound;
  769. #endif
  770. for( int t = 0; t < ntris; t++ )
  771. TriangleIndexList.AddToTail( tri_list[t] );
  772. }
  773. else
  774. {
  775. // printf("best split was %d at %f (mid=%f,n=%d, sk=%d)\n",split_plane,best_splitvalue,
  776. // 0.5*(MinBound[split_plane]+MaxBound[split_plane]),ntris,tri_skip);
  777. // its worth splitting!
  778. // we will achieve the splitting without sorting by using a selection algorithm.
  779. int32 * new_triangle_list;
  780. new_triangle_list = new int32[ntris];
  781. // now, perform surface area/cost check to determine whether this split was worth it
  782. Vector LeftMins = MinBound;
  783. Vector LeftMaxes = MaxBound;
  784. Vector RightMins = MinBound;
  785. Vector RightMaxes = MaxBound;
  786. LeftMaxes[split_plane] = best_splitvalue;
  787. RightMins[split_plane] = best_splitvalue;
  788. int n_left_output = 0;
  789. int n_both_output = 0;
  790. int n_right_output = 0;
  791. for( int t = 0; t < ntris; t++ )
  792. {
  793. CacheOptimizedTriangle &tri = OptimizedTriangleList[tri_list[t]];
  794. switch( tri.m_Data.m_GeometryData.m_nTmpData1 )
  795. {
  796. case PLANECHECK_NEGATIVE:
  797. // printf("%d goes left\n",t);
  798. new_triangle_list[n_left_output++] = tri_list[t];
  799. break;
  800. case PLANECHECK_POSITIVE:
  801. n_right_output++;
  802. // printf("%d goes right\n",t);
  803. new_triangle_list[ntris - n_right_output] = tri_list[t];
  804. break;
  805. case PLANECHECK_STRADDLING:
  806. // printf("%d goes both\n",t);
  807. new_triangle_list[best_nleft + n_both_output] = tri_list[t];
  808. n_both_output++;
  809. break;
  810. }
  811. }
  812. int left_child = OptimizedKDTree.Count();
  813. int right_child = left_child + 1;
  814. // printf("node %d split on axis %d at %f, nl=%d nr=%d nb=%d lc=%d rc=%d\n",node_number,
  815. // split_plane,best_splitvalue,best_nleft,best_nright,best_nboth,
  816. // left_child,right_child);
  817. OptimizedKDTree[node_number].Children = split_plane + ( left_child << 2 );
  818. OptimizedKDTree[node_number].SplittingPlaneValue = best_splitvalue;
  819. #ifdef DEBUG_RAYTRACE
  820. OptimizedKDTree[node_number].vecMins = MinBound;
  821. OptimizedKDTree[node_number].vecMaxs = MaxBound;
  822. #endif
  823. CacheOptimizedKDNode newnode;
  824. OptimizedKDTree.AddToTail( newnode );
  825. OptimizedKDTree.AddToTail( newnode );
  826. // now, recurse!
  827. if ( ( ntris < 20 ) && ( (best_nleft == 0 ) || ( best_nright == 0 )) )
  828. depth += 100;
  829. RefineNode( left_child, new_triangle_list, best_nleft + best_nboth, LeftMins, LeftMaxes, depth + 1 );
  830. RefineNode( right_child, new_triangle_list + best_nleft, best_nright + best_nboth,
  831. RightMins, RightMaxes, depth + 1 );
  832. delete[] new_triangle_list;
  833. }
  834. }
  835. void RayTracingEnvironment::SetupAccelerationStructure( void )
  836. {
  837. CacheOptimizedKDNode root;
  838. OptimizedKDTree.AddToTail( root );
  839. int32 * root_triangle_list = new int32[OptimizedTriangleList.Count()];
  840. for( int t = 0; t < OptimizedTriangleList.Count(); t++ )
  841. root_triangle_list[t] = t;
  842. CalculateTriangleListBounds( root_triangle_list, OptimizedTriangleList.Count(), m_MinBound,
  843. m_MaxBound );
  844. RefineNode( 0, root_triangle_list, OptimizedTriangleList.Count(), m_MinBound, m_MaxBound, 0 );
  845. delete[] root_triangle_list;
  846. // now, convert all triangles to "intersection format"
  847. for( int i = 0; i < OptimizedTriangleList.Count(); i++ )
  848. OptimizedTriangleList[i].ChangeIntoIntersectionFormat();
  849. }
  850. void RayTracingEnvironment::AddInfinitePointLight( Vector position, Vector intensity )
  851. {
  852. LightDesc_t mylight( position, intensity );
  853. LightList.AddToTail( mylight );
  854. }
  855. #define RTENV_SERIALIZATION_VERSION 1
  856. struct RayTracingSerializationHeader
  857. {
  858. uint32 m_nVersionNumber;
  859. uint32 m_nSerializationFlags;
  860. uint32 m_nNumKDNodes;
  861. uint32 m_nNumTriangles;
  862. uint32 m_nNumTriangleIndices;
  863. uint32 m_nNumColors;
  864. Vector m_vMinBound;
  865. Vector m_vMaxBound;
  866. RayTracingSerializationHeader( void )
  867. {
  868. m_nVersionNumber = RTENV_SERIALIZATION_VERSION;
  869. }
  870. void Put( CUtlBuffer &outbuf )
  871. {
  872. outbuf.PutInt( m_nVersionNumber );
  873. outbuf.PutInt( m_nSerializationFlags );
  874. outbuf.PutInt( m_nNumKDNodes );
  875. outbuf.PutInt( m_nNumTriangles );
  876. outbuf.PutInt( m_nNumTriangleIndices );
  877. outbuf.PutInt( m_nNumColors );
  878. outbuf.PutFloat( m_vMinBound.x );
  879. outbuf.PutFloat( m_vMinBound.y );
  880. outbuf.PutFloat( m_vMinBound.z );
  881. outbuf.PutFloat( m_vMaxBound.x );
  882. outbuf.PutFloat( m_vMaxBound.y );
  883. outbuf.PutFloat( m_vMaxBound.z );
  884. }
  885. void Get( CUtlBuffer &inbuf )
  886. {
  887. m_nVersionNumber = inbuf.GetInt();
  888. m_nSerializationFlags = inbuf.GetInt();
  889. m_nNumKDNodes = inbuf.GetInt();
  890. m_nNumTriangles = inbuf.GetInt();
  891. m_nNumTriangleIndices = inbuf.GetInt();
  892. m_nNumColors = inbuf.GetInt();
  893. m_vMinBound.x = inbuf.GetFloat();
  894. m_vMinBound.y = inbuf.GetFloat();
  895. m_vMinBound.z = inbuf.GetFloat();
  896. m_vMaxBound.x = inbuf.GetFloat();
  897. m_vMaxBound.y = inbuf.GetFloat();
  898. m_vMaxBound.z = inbuf.GetFloat();
  899. }
  900. };
  901. size_t RayTracingEnvironment::GetSerializationNumBytes( uint32 nSerializationFlags ) const
  902. {
  903. size_t nRet = sizeof( RayTracingSerializationHeader );
  904. nRet += sizeof( CacheOptimizedKDNode ) * OptimizedKDTree.Count();
  905. nRet += sizeof( CacheOptimizedTriangle ) * OptimizedTriangleList.Count();
  906. nRet += sizeof( int32 ) * TriangleIndexList.Count();
  907. if ( nSerializationFlags & RT_ENV_SERIALIZE_COLORS )
  908. nRet += sizeof( Vector ) * TriangleColors.Count();
  909. return nRet;
  910. }
  911. void RayTracingEnvironment::Serialize( CUtlBuffer &outbuf, uint32 nSerializationFlags ) const
  912. {
  913. outbuf.ActivateByteSwappingIfBigEndian();
  914. RayTracingSerializationHeader hdr;
  915. hdr.m_nSerializationFlags = nSerializationFlags;
  916. hdr.m_nNumKDNodes = OptimizedKDTree.Count();
  917. hdr.m_nNumTriangles = OptimizedTriangleList.Count();
  918. hdr.m_nNumTriangleIndices = TriangleIndexList.Count();
  919. hdr.m_nNumColors = ( nSerializationFlags & RT_ENV_SERIALIZE_COLORS ) ? TriangleColors.Count() : 0;
  920. hdr.m_vMinBound = m_MinBound;
  921. hdr.m_vMaxBound = m_MaxBound;
  922. hdr.Put( outbuf );
  923. for( int i = 0 ; i < OptimizedKDTree.Count(); i++ )
  924. {
  925. CacheOptimizedKDNode const * pNode = &OptimizedKDTree[i];
  926. outbuf.PutInt( pNode->Children );
  927. if ( pNode->NodeType() == KDNODE_STATE_LEAF )
  928. outbuf.PutInt( * ( reinterpret_cast < int32 const *> ( &pNode->SplittingPlaneValue ) ) );
  929. else
  930. outbuf.PutFloat( pNode->SplittingPlaneValue );
  931. }
  932. for( int i = 0; i < OptimizedTriangleList.Count() ; i++ )
  933. {
  934. TriIntersectData_t const * pTri = &( OptimizedTriangleList[i].m_Data.m_IntersectData );
  935. outbuf.PutFloat( pTri->m_flNx );
  936. outbuf.PutFloat( pTri->m_flNy );
  937. outbuf.PutFloat( pTri->m_flNz );
  938. outbuf.PutFloat( pTri->m_flD );
  939. outbuf.PutFloat( pTri->m_nTriangleID );
  940. for( int j = 0; j < ARRAYSIZE( pTri->m_ProjectedEdgeEquations ); j++ )
  941. outbuf.PutFloat( pTri->m_ProjectedEdgeEquations[j] );
  942. outbuf.PutUnsignedChar( pTri->m_nCoordSelect0 );
  943. outbuf.PutUnsignedChar( pTri->m_nCoordSelect1 );
  944. outbuf.PutUnsignedChar( pTri->m_nFlags );
  945. outbuf.PutUnsignedChar( 0 ); // for unused.
  946. }
  947. for( int i = 0; i < TriangleIndexList.Count(); i++ )
  948. outbuf.PutInt( TriangleIndexList[i] );
  949. if ( nSerializationFlags & RT_ENV_SERIALIZE_COLORS )
  950. for( int i = 0 ; i < TriangleColors.Count() ; i++ )
  951. {
  952. Vector const &v = TriangleColors[i];
  953. outbuf.PutFloat( v.x );
  954. outbuf.PutFloat( v.y );
  955. outbuf.PutFloat( v.z );
  956. }
  957. }
  958. void RayTracingEnvironment::UnSerialize( CUtlBuffer &inbuf )
  959. {
  960. inbuf.ActivateByteSwappingIfBigEndian();
  961. RayTracingSerializationHeader hdr;
  962. hdr.Get( inbuf );
  963. m_MinBound = hdr.m_vMinBound;
  964. m_MaxBound = hdr.m_vMaxBound;
  965. OptimizedKDTree.SetCount( hdr.m_nNumKDNodes );
  966. for( int i = 0; i < hdr.m_nNumKDNodes; i++ )
  967. {
  968. CacheOptimizedKDNode *pNode = &OptimizedKDTree[i];
  969. pNode->Children = inbuf.GetInt();
  970. if ( pNode->NodeType() == KDNODE_STATE_LEAF )
  971. {
  972. *( ( int32 * ) &pNode->SplittingPlaneValue ) = inbuf.GetInt();
  973. }
  974. else
  975. {
  976. pNode->SplittingPlaneValue = inbuf.GetFloat();
  977. }
  978. }
  979. // now, read the triangles
  980. OptimizedTriangleList.SetCount( hdr.m_nNumTriangles );
  981. for( int i = 0; i < OptimizedTriangleList.Count() ; i++ )
  982. {
  983. TriIntersectData_t * pTri = &( OptimizedTriangleList[i].m_Data.m_IntersectData );
  984. pTri->m_flNx = inbuf.GetFloat();
  985. pTri->m_flNy = inbuf.GetFloat();
  986. pTri->m_flNz = inbuf.GetFloat();
  987. pTri->m_flD = inbuf.GetFloat();
  988. pTri->m_nTriangleID = inbuf.GetFloat();
  989. for( int j = 0; j < ARRAYSIZE( pTri->m_ProjectedEdgeEquations ); j++ )
  990. {
  991. pTri->m_ProjectedEdgeEquations[j] = inbuf.GetFloat();
  992. }
  993. pTri->m_nCoordSelect0 = inbuf.GetUnsignedChar();
  994. pTri->m_nCoordSelect1 = inbuf.GetUnsignedChar();
  995. pTri->m_nFlags = inbuf.GetUnsignedChar();
  996. inbuf.GetUnsignedChar(); // for unused.
  997. }
  998. TriangleIndexList.SetCount( hdr.m_nNumTriangleIndices );
  999. for( int i = 0; i < TriangleIndexList.Count(); i++ )
  1000. {
  1001. TriangleIndexList[i] = inbuf.GetInt();
  1002. }
  1003. TriangleColors.SetCount( hdr.m_nNumColors );
  1004. for( int i = 0 ; i < TriangleColors.Count() ; i++ )
  1005. {
  1006. Vector &v = TriangleColors[i];
  1007. v.x = inbuf.GetFloat();
  1008. v.y = inbuf.GetFloat();
  1009. v.z = inbuf.GetFloat();
  1010. }
  1011. }