PageRenderTime 27ms CodeModel.GetById 26ms RepoModel.GetById 0ms app.codeStats 0ms

/src/engine/renderer/tr_main.cpp

https://gitlab.com/xonotic/daemon
C++ | 2811 lines | 1837 code | 496 blank | 478 comment | 271 complexity | 21b69cf8b84a983eba5874608849d81f MD5 | raw file
Possible License(s): BSD-3-Clause

Large files files are truncated, but you can click here to view the full file

  1. /*
  2. ===========================================================================
  3. Copyright (C) 1999-2005 Id Software, Inc.
  4. Copyright (C) 2006-2011 Robert Beckebans <trebor_7@users.sourceforge.net>
  5. This file is part of Daemon source code.
  6. Daemon source code is free software; you can redistribute it
  7. and/or modify it under the terms of the GNU General Public License as
  8. published by the Free Software Foundation; either version 2 of the License,
  9. or (at your option) any later version.
  10. Daemon source code is distributed in the hope that it will be
  11. useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. GNU General Public License for more details.
  14. You should have received a copy of the GNU General Public License
  15. along with Daemon source code; if not, write to the Free Software
  16. Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
  17. ===========================================================================
  18. */
  19. // tr_main.c -- main control flow for each frame
  20. #include "tr_local.h"
  21. trGlobals_t tr;
  22. // convert from our coordinate system (looking down X)
  23. // to OpenGL's coordinate system (looking down -Z)
  24. const matrix_t quakeToOpenGLMatrix =
  25. {
  26. 0, 0, -1, 0,
  27. -1, 0, 0, 0,
  28. 0, 1, 0, 0,
  29. 0, 0, 0, 1
  30. };
  31. int shadowMapResolutions[ 5 ] = { 2048, 1024, 512, 256, 128 };
  32. int sunShadowMapResolutions[ 5 ] = { 2048, 2048, 1024, 1024, 1024 };
  33. refimport_t ri;
  34. // entities that will have procedurally generated surfaces will just
  35. // point at this for their sorting surface
  36. surfaceType_t entitySurface = surfaceType_t::SF_ENTITY;
  37. /*
  38. =============
  39. R_CalcFaceNormal
  40. =============
  41. */
  42. void R_CalcFaceNormal( vec3_t normal,
  43. const vec3_t v0, const vec3_t v1, const vec3_t v2 )
  44. {
  45. vec3_t u, v;
  46. // compute the face normal based on vertex points
  47. VectorSubtract( v2, v0, u );
  48. VectorSubtract( v1, v0, v );
  49. CrossProduct( u, v, normal );
  50. VectorNormalize( normal );
  51. }
  52. /*
  53. =============
  54. R_CalcTangents
  55. =============
  56. */
  57. void R_CalcTangents( vec3_t tangent, vec3_t binormal,
  58. const vec3_t v0, const vec3_t v1, const vec3_t v2,
  59. const vec2_t t0, const vec2_t t1, const vec2_t t2 )
  60. {
  61. vec3_t dpx, dpy;
  62. vec2_t dtx, dty;
  63. VectorSubtract(v1, v0, dpx);
  64. VectorSubtract(v2, v0, dpy);
  65. Vector2Subtract(t1, t0, dtx);
  66. Vector2Subtract(t2, t0, dty);
  67. float area = dtx[0] * dty[1] - dtx[1] * dty[0];
  68. if( area < 0.0f ) {
  69. dtx[0] = -dtx[0];
  70. dtx[1] = -dtx[1];
  71. dty[0] = -dty[0];
  72. dty[1] = -dty[1];
  73. }
  74. tangent[0] = dpx[0] * dty[1] - dtx[1] * dpy[0];
  75. tangent[1] = dpx[1] * dty[1] - dtx[1] * dpy[1];
  76. tangent[2] = dpx[2] * dty[1] - dtx[1] * dpy[2];
  77. binormal[0] = dtx[0] * dpy[0] - dpx[0] * dty[0];
  78. binormal[1] = dtx[0] * dpy[1] - dpx[1] * dty[0];
  79. binormal[2] = dtx[0] * dpy[2] - dpx[2] * dty[0];
  80. VectorNormalize( tangent );
  81. VectorNormalize( binormal );
  82. }
  83. void R_CalcTangents( vec3_t tangent, vec3_t binormal,
  84. const vec3_t v0, const vec3_t v1, const vec3_t v2,
  85. const i16vec2_t t0, const i16vec2_t t1, const i16vec2_t t2 )
  86. {
  87. vec2_t t0f, t1f, t2f;
  88. t0f[ 0 ] = halfToFloat( t0[ 0 ] );
  89. t0f[ 1 ] = halfToFloat( t0[ 1 ] );
  90. t1f[ 0 ] = halfToFloat( t1[ 0 ] );
  91. t1f[ 1 ] = halfToFloat( t1[ 1 ] );
  92. t2f[ 0 ] = halfToFloat( t2[ 0 ] );
  93. t2f[ 1 ] = halfToFloat( t2[ 1 ] );
  94. vec3_t dpx, dpy;
  95. vec2_t dtx, dty;
  96. VectorSubtract(v1, v0, dpx);
  97. VectorSubtract(v2, v0, dpy);
  98. Vector2Subtract(t1f, t0f, dtx);
  99. Vector2Subtract(t2f, t0f, dty);
  100. float area = dtx[0] * dty[1] - dtx[1] * dty[0];
  101. if( area < 0.0f ) {
  102. dtx[0] = -dtx[0];
  103. dtx[1] = -dtx[1];
  104. dty[0] = -dty[0];
  105. dty[1] = -dty[1];
  106. }
  107. tangent[0] = dpx[0] * dty[1] - dtx[1] * dpy[0];
  108. tangent[1] = dpx[1] * dty[1] - dtx[1] * dpy[1];
  109. tangent[2] = dpx[2] * dty[1] - dtx[1] * dpy[2];
  110. binormal[0] = dtx[0] * dpy[0] - dpx[0] * dty[0];
  111. binormal[1] = dtx[0] * dpy[1] - dpx[1] * dty[0];
  112. binormal[2] = dtx[0] * dpy[2] - dpx[2] * dty[0];
  113. VectorNormalize( tangent );
  114. VectorNormalize( binormal );
  115. }
  116. void R_QtangentsToTBN( const i16vec4_t qtangent, vec3_t tangent,
  117. vec3_t binormal, vec3_t normal )
  118. {
  119. vec4_t quat;
  120. const vec3_t x = { 1.0f, 0.0f, 0.0f };
  121. const vec3_t y = { 0.0f, 1.0f, 0.0f };
  122. const vec3_t z = { 0.0f, 0.0f, 1.0f };
  123. snorm16ToFloat( qtangent, quat );
  124. QuatTransformVector( quat, x, tangent );
  125. QuatTransformVector( quat, y, binormal );
  126. QuatTransformVector( quat, z, normal );
  127. if( quat[ 3 ] < 0.0f ) {
  128. VectorNegate( tangent, tangent );
  129. }
  130. }
  131. void R_QtangentsToNormal( const i16vec4_t qtangent, vec3_t normal )
  132. {
  133. vec4_t quat;
  134. const vec3_t z = { 0.0f, 0.0f, 1.0f };
  135. snorm16ToFloat( qtangent, quat );
  136. QuatTransformVector( quat, z, normal );
  137. }
  138. void R_TBNtoQtangents( const vec3_t tangent, const vec3_t binormal,
  139. const vec3_t normal, i16vec4_t qtangent )
  140. {
  141. vec3_t tangent2, binormal2, normal2;
  142. vec4_t q;
  143. bool flipped = false;
  144. float trace, scale, dot;
  145. vec3_t mid, tangent3, binormal3;
  146. // orthogonalize the input vectors
  147. // preserve normal vector as precise as possible
  148. if( VectorLengthSquared( normal ) < 0.001f ) {
  149. // degenerate case, compute new normal
  150. CrossProduct( tangent, binormal, normal2 );
  151. VectorNormalizeFast( normal2 );
  152. } else {
  153. VectorCopy( normal, normal2 );
  154. }
  155. // project tangent and binormal onto the normal orthogonal plane
  156. VectorMA(tangent, -DotProduct(normal2, tangent), normal2, tangent2 );
  157. VectorMA(binormal, -DotProduct(normal2, binormal), normal2, binormal2 );
  158. // check for several degenerate cases
  159. if( VectorLengthSquared( tangent2 ) < 0.001f ) {
  160. if( VectorLengthSquared( binormal2 ) < 0.001f ) {
  161. PerpendicularVector( tangent2, normal2 );
  162. CrossProduct( normal2, tangent2, binormal2 );
  163. } else {
  164. VectorNormalizeFast( binormal2 );
  165. CrossProduct( binormal2, normal2, tangent2 );
  166. }
  167. } else {
  168. VectorNormalizeFast( tangent2 );
  169. if( VectorLengthSquared( binormal2 ) < 0.001f ) {
  170. CrossProduct( normal2, tangent2, binormal2 );
  171. } else {
  172. // compute mid vector and project into mid-orthogonal plane
  173. VectorNormalizeFast( binormal2 );
  174. VectorAdd( tangent2, binormal2, mid );
  175. if( VectorLengthSquared( mid ) < 0.001f ) {
  176. CrossProduct( binormal2, normal2, mid );
  177. } else {
  178. VectorNormalizeFast( mid );
  179. }
  180. VectorMA(tangent2, -DotProduct(mid, tangent2), mid, tangent3 );
  181. VectorMA(binormal2, -DotProduct(mid, binormal2), mid, binormal3 );
  182. if( VectorLengthSquared( tangent3 ) < 0.001f ) {
  183. CrossProduct( mid, normal2, tangent3 );
  184. VectorNegate( tangent3, binormal3 );
  185. }
  186. VectorNormalizeFast( tangent3 );
  187. VectorNormalizeFast( binormal3 );
  188. VectorAdd( mid, tangent3, tangent2 );
  189. VectorAdd( mid, binormal3, binormal2 );
  190. VectorNormalizeFast( tangent2 );
  191. VectorNormalizeFast( binormal2 );
  192. }
  193. }
  194. // check of orientation
  195. CrossProduct( binormal2, normal2, tangent3 );
  196. dot = DotProduct( tangent2, tangent3 );
  197. if( dot < 0.0f ) {
  198. flipped = true;
  199. VectorNegate( tangent2, tangent2 );
  200. }
  201. if ( ( trace = tangent2[ 0 ] + binormal2[ 1 ] + normal2[ 2 ] ) > 0.0f )
  202. {
  203. trace += 1.0f;
  204. scale = 0.5f * Q_rsqrt( trace );
  205. q[ 3 ] = trace * scale;
  206. q[ 2 ] = ( tangent2 [ 1 ] - binormal2[ 0 ] ) * scale;
  207. q[ 1 ] = ( normal2 [ 0 ] - tangent2 [ 2 ] ) * scale;
  208. q[ 0 ] = ( binormal2[ 2 ] - normal2 [ 1 ] ) * scale;
  209. }
  210. else if ( tangent2[ 0 ] > binormal2[ 1 ] && tangent2[ 0 ] > normal2[ 2 ] )
  211. {
  212. trace = tangent2[ 0 ] - binormal2[ 1 ] - normal2[ 2 ] + 1.0f;
  213. scale = 0.5f * Q_rsqrt( trace );
  214. q[ 0 ] = trace * scale;
  215. q[ 1 ] = ( tangent2 [ 1 ] + binormal2[ 0 ] ) * scale;
  216. q[ 2 ] = ( normal2 [ 0 ] + tangent2 [ 2 ] ) * scale;
  217. q[ 3 ] = ( binormal2[ 2 ] - normal2 [ 1 ] ) * scale;
  218. }
  219. else if ( binormal2[ 1 ] > normal2[ 2 ] )
  220. {
  221. trace = -tangent2[ 0 ] + binormal2[ 1 ] - normal2[ 2 ] + 1.0f;
  222. scale = 0.5f * Q_rsqrt( trace );
  223. q[ 1 ] = trace * scale;
  224. q[ 0 ] = ( tangent2 [ 1 ] + binormal2[ 0 ] ) * scale;
  225. q[ 3 ] = ( normal2 [ 0 ] - tangent2 [ 2 ] ) * scale;
  226. q[ 2 ] = ( binormal2[ 2 ] + normal2 [ 1 ] ) * scale;
  227. }
  228. else
  229. {
  230. trace = -tangent2[ 0 ] - binormal2[ 1 ] + normal2[ 2 ] + 1.0f;
  231. scale = 0.5f * Q_rsqrt( trace );
  232. q[ 2 ] = trace * scale;
  233. q[ 3 ] = ( tangent2 [ 1 ] - binormal2[ 0 ] ) * scale;
  234. q[ 0 ] = ( normal2 [ 0 ] + tangent2 [ 2 ] ) * scale;
  235. q[ 1 ] = ( binormal2[ 2 ] + normal2 [ 1 ] ) * scale;
  236. }
  237. if( q[ 3 ] < 0.0f ) {
  238. q[ 0 ] = -q[ 0 ];
  239. q[ 1 ] = -q[ 1 ];
  240. q[ 2 ] = -q[ 2 ];
  241. q[ 3 ] = -q[ 3 ];
  242. }
  243. i16vec4_t resqtangent;
  244. floatToSnorm16( q, resqtangent );
  245. if( resqtangent[ 3 ] == 0 )
  246. {
  247. resqtangent[ 3 ] = 1;
  248. }
  249. if( flipped )
  250. {
  251. qtangent[ 0 ] = -resqtangent[ 0 ];
  252. qtangent[ 1 ] = -resqtangent[ 1 ];
  253. qtangent[ 2 ] = -resqtangent[ 2 ];
  254. qtangent[ 3 ] = -resqtangent[ 3 ];
  255. }
  256. else
  257. {
  258. qtangent[ 0 ] = resqtangent[ 0 ];
  259. qtangent[ 1 ] = resqtangent[ 1 ];
  260. qtangent[ 2 ] = resqtangent[ 2 ];
  261. qtangent[ 3 ] = resqtangent[ 3 ];
  262. }
  263. if (false) {
  264. vec3_t T, B, N;
  265. R_QtangentsToTBN( qtangent, T, B, N );
  266. ASSERT_LT(Distance(N, normal2), 0.01f);
  267. if( flipped ) {
  268. VectorNegate( T, T );
  269. }
  270. ASSERT_LT(Distance(T, tangent2), 0.01f);
  271. ASSERT_LT(Distance(B, binormal2), 0.01f);
  272. }
  273. }
  274. /*
  275. =================
  276. R_CullBox
  277. Returns CULL_IN, CULL_CLIP, or CULL_OUT
  278. =================
  279. */
  280. cullResult_t R_CullBox( vec3_t worldBounds[ 2 ] )
  281. {
  282. bool anyClip;
  283. cplane_t *frust;
  284. int i, r;
  285. if ( r_nocull->integer )
  286. {
  287. return cullResult_t::CULL_CLIP;
  288. }
  289. // check against frustum planes
  290. anyClip = false;
  291. for ( i = 0; i < FRUSTUM_PLANES; i++ )
  292. {
  293. frust = &tr.viewParms.frustums[ 0 ][ i ];
  294. r = BoxOnPlaneSide( worldBounds[ 0 ], worldBounds[ 1 ], frust );
  295. if ( r == 2 )
  296. {
  297. // completely outside frustum
  298. return cullResult_t::CULL_OUT;
  299. }
  300. if ( r == 3 )
  301. {
  302. anyClip = true;
  303. }
  304. }
  305. if ( !anyClip )
  306. {
  307. // completely inside frustum
  308. return cullResult_t::CULL_IN;
  309. }
  310. // partially clipped
  311. return cullResult_t::CULL_CLIP;
  312. }
  313. /*
  314. =================
  315. R_CullLocalBox
  316. Returns CULL_IN, CULL_CLIP, or CULL_OUT
  317. =================
  318. */
  319. cullResult_t R_CullLocalBox( vec3_t localBounds[ 2 ] )
  320. {
  321. vec3_t worldBounds[ 2 ];
  322. // transform into world space
  323. MatrixTransformBounds(tr.orientation.transformMatrix, localBounds[0], localBounds[1], worldBounds[0], worldBounds[1]);
  324. return R_CullBox( worldBounds );
  325. }
  326. /*
  327. =================
  328. R_CullLocalPointAndRadius
  329. =================
  330. */
  331. cullResult_t R_CullLocalPointAndRadius( vec3_t pt, float radius )
  332. {
  333. vec3_t transformed;
  334. R_LocalPointToWorld( pt, transformed );
  335. return R_CullPointAndRadius( transformed, radius );
  336. }
  337. /*
  338. =================
  339. R_CullPointAndRadius
  340. =================
  341. */
  342. cullResult_t R_CullPointAndRadius( vec3_t pt, float radius )
  343. {
  344. int i;
  345. float dist;
  346. cplane_t *frust;
  347. bool mightBeClipped = false;
  348. if ( r_nocull->integer )
  349. {
  350. return cullResult_t::CULL_CLIP;
  351. }
  352. // check against frustum planes
  353. for ( i = 0; i < Util::ordinal(frustumBits_t::FRUSTUM_PLANES); i++ )
  354. {
  355. frust = &tr.viewParms.frustums[ 0 ][ i ];
  356. dist = DotProduct( pt, frust->normal ) - frust->dist;
  357. if ( dist < -radius )
  358. {
  359. return cullResult_t::CULL_OUT;
  360. }
  361. else if ( dist <= radius )
  362. {
  363. mightBeClipped = true;
  364. }
  365. }
  366. if ( mightBeClipped )
  367. {
  368. return cullResult_t::CULL_CLIP;
  369. }
  370. return cullResult_t::CULL_IN; // completely inside frustum
  371. }
  372. /*
  373. =================
  374. R_FogWorldBox
  375. =================
  376. */
  377. int R_FogWorldBox( vec3_t bounds[ 2 ] )
  378. {
  379. int i, j;
  380. fog_t *fog;
  381. if ( tr.refdef.rdflags & RDF_NOWORLDMODEL )
  382. {
  383. return 0;
  384. }
  385. for ( i = 1; i < tr.world->numFogs; i++ )
  386. {
  387. fog = &tr.world->fogs[ i ];
  388. for ( j = 0; j < 3; j++ )
  389. {
  390. if ( bounds[ 0 ][ j ] >= fog->bounds[ 1 ][ j ] )
  391. {
  392. break;
  393. }
  394. if ( bounds[ 1 ][ j ] <= fog->bounds[ 0 ][ j ] )
  395. {
  396. break;
  397. }
  398. }
  399. if ( j == 3 )
  400. {
  401. return i;
  402. }
  403. }
  404. return 0;
  405. }
  406. /*
  407. =================
  408. R_LocalNormalToWorld
  409. =================
  410. */
  411. void R_LocalNormalToWorld( const vec3_t local, vec3_t world )
  412. {
  413. MatrixTransformNormal( tr.orientation.transformMatrix, local, world );
  414. }
  415. /*
  416. =================
  417. R_LocalPointToWorld
  418. =================
  419. */
  420. void R_LocalPointToWorld( const vec3_t local, vec3_t world )
  421. {
  422. MatrixTransformPoint( tr.orientation.transformMatrix, local, world );
  423. }
  424. /*
  425. ==========================
  426. R_TransformWorldToClip
  427. ==========================
  428. */
  429. void R_TransformWorldToClip( const vec3_t src, const float *cameraViewMatrix, const float *projectionMatrix, vec4_t eye,
  430. vec4_t dst )
  431. {
  432. vec4_t src2;
  433. VectorCopy( src, src2 );
  434. src2[ 3 ] = 1;
  435. MatrixTransform4( cameraViewMatrix, src2, eye );
  436. MatrixTransform4( projectionMatrix, eye, dst );
  437. }
  438. /*
  439. ==========================
  440. R_TransformModelToClip
  441. ==========================
  442. */
  443. void R_TransformModelToClip( const vec3_t src, const float *modelViewMatrix, const float *projectionMatrix, vec4_t eye, vec4_t dst )
  444. {
  445. vec4_t src2;
  446. VectorCopy( src, src2 );
  447. src2[ 3 ] = 1;
  448. MatrixTransform4( modelViewMatrix, src2, eye );
  449. MatrixTransform4( projectionMatrix, eye, dst );
  450. }
  451. /*
  452. ==========================
  453. R_TransformClipToWindow
  454. ==========================
  455. */
  456. void R_TransformClipToWindow( const vec4_t clip, const viewParms_t *view, vec4_t normalized, vec4_t window )
  457. {
  458. normalized[ 0 ] = clip[ 0 ] / clip[ 3 ];
  459. normalized[ 1 ] = clip[ 1 ] / clip[ 3 ];
  460. normalized[ 2 ] = ( clip[ 2 ] + clip[ 3 ] ) / ( 2 * clip[ 3 ] );
  461. window[ 0 ] = view->viewportX + ( 0.5f * ( 1.0f + normalized[ 0 ] ) * view->viewportWidth );
  462. window[ 1 ] = view->viewportY + ( 0.5f * ( 1.0f + normalized[ 1 ] ) * view->viewportHeight );
  463. window[ 2 ] = normalized[ 2 ];
  464. window[ 0 ] = ( int )( window[ 0 ] + 0.5 );
  465. window[ 1 ] = ( int )( window[ 1 ] + 0.5 );
  466. }
  467. /*
  468. ================
  469. R_ProjectRadius
  470. ================
  471. */
  472. float R_ProjectRadius( float r, vec3_t location )
  473. {
  474. float pr;
  475. float dist;
  476. float c;
  477. vec3_t p;
  478. float projected[ 4 ];
  479. c = DotProduct( tr.viewParms.orientation.axis[ 0 ], tr.viewParms.orientation.origin );
  480. dist = DotProduct( tr.viewParms.orientation.axis[ 0 ], location ) - c;
  481. if ( dist <= 0 )
  482. {
  483. return 0;
  484. }
  485. p[ 0 ] = 0;
  486. p[ 1 ] = fabs( r );
  487. p[ 2 ] = -dist;
  488. projected[ 0 ] = p[ 0 ] * tr.viewParms.projectionMatrix[ 0 ] +
  489. p[ 1 ] * tr.viewParms.projectionMatrix[ 4 ] + p[ 2 ] * tr.viewParms.projectionMatrix[ 8 ] + tr.viewParms.projectionMatrix[ 12 ];
  490. projected[ 1 ] = p[ 0 ] * tr.viewParms.projectionMatrix[ 1 ] +
  491. p[ 1 ] * tr.viewParms.projectionMatrix[ 5 ] + p[ 2 ] * tr.viewParms.projectionMatrix[ 9 ] + tr.viewParms.projectionMatrix[ 13 ];
  492. projected[ 2 ] = p[ 0 ] * tr.viewParms.projectionMatrix[ 2 ] +
  493. p[ 1 ] * tr.viewParms.projectionMatrix[ 6 ] + p[ 2 ] * tr.viewParms.projectionMatrix[ 10 ] + tr.viewParms.projectionMatrix[ 14 ];
  494. projected[ 3 ] = p[ 0 ] * tr.viewParms.projectionMatrix[ 3 ] +
  495. p[ 1 ] * tr.viewParms.projectionMatrix[ 7 ] + p[ 2 ] * tr.viewParms.projectionMatrix[ 11 ] + tr.viewParms.projectionMatrix[ 15 ];
  496. pr = projected[ 1 ] / projected[ 3 ];
  497. if ( pr > 1.0f )
  498. {
  499. pr = 1.0f;
  500. }
  501. return pr;
  502. }
  503. /*
  504. =================
  505. R_SetupEntityWorldBounds
  506. Tr3B - needs R_RotateEntityForViewParms
  507. =================
  508. */
  509. void R_SetupEntityWorldBounds( trRefEntity_t *ent )
  510. {
  511. MatrixTransformBounds(tr.orientation.transformMatrix, ent->localBounds[0], ent->localBounds[1], ent->worldBounds[0], ent->worldBounds[1]);
  512. }
  513. /*
  514. =================
  515. R_RotateEntityForViewParms
  516. Generates an orientation for an entity and viewParms
  517. Does NOT produce any GL calls
  518. Called by both the front end and the back end
  519. =================
  520. */
  521. void R_RotateEntityForViewParms( const trRefEntity_t *ent, const viewParms_t *viewParms, orientationr_t * orientation )
  522. {
  523. vec3_t delta;
  524. float axisLength;
  525. if ( ent->e.reType != refEntityType_t::RT_MODEL )
  526. {
  527. * orientation = viewParms->world;
  528. return;
  529. }
  530. VectorCopy( ent->e.origin, orientation ->origin );
  531. VectorCopy( ent->e.axis[ 0 ], orientation ->axis[ 0 ] );
  532. VectorCopy( ent->e.axis[ 1 ], orientation ->axis[ 1 ] );
  533. VectorCopy( ent->e.axis[ 2 ], orientation ->axis[ 2 ] );
  534. MatrixSetupTransformFromVectorsFLU( orientation ->transformMatrix, orientation ->axis[ 0 ], orientation ->axis[ 1 ], orientation ->axis[ 2 ], orientation ->origin );
  535. MatrixAffineInverse( orientation ->transformMatrix, orientation ->viewMatrix );
  536. MatrixMultiply( viewParms->world.viewMatrix, orientation ->transformMatrix, orientation ->modelViewMatrix );
  537. // calculate the viewer origin in the model's space
  538. // needed for fog, specular, and environment mapping
  539. VectorSubtract( viewParms->orientation.origin, orientation ->origin, delta );
  540. // compensate for scale in the axes if necessary
  541. if ( ent->e.nonNormalizedAxes )
  542. {
  543. axisLength = VectorLength( ent->e.axis[ 0 ] );
  544. if ( !axisLength )
  545. {
  546. axisLength = 0;
  547. }
  548. else
  549. {
  550. axisLength = 1.0f / axisLength;
  551. }
  552. }
  553. else
  554. {
  555. axisLength = 1.0f;
  556. }
  557. orientation ->viewOrigin[ 0 ] = DotProduct( delta, orientation ->axis[ 0 ] ) * axisLength;
  558. orientation ->viewOrigin[ 1 ] = DotProduct( delta, orientation ->axis[ 1 ] ) * axisLength;
  559. orientation ->viewOrigin[ 2 ] = DotProduct( delta, orientation ->axis[ 2 ] ) * axisLength;
  560. }
  561. /*
  562. =================
  563. R_RotateEntityForLight
  564. Generates an orientation for an entity and light
  565. Does NOT produce any GL calls
  566. Called by both the front end and the back end
  567. =================
  568. */
  569. void R_RotateEntityForLight( const trRefEntity_t *ent, const trRefLight_t *light, orientationr_t * orientation )
  570. {
  571. vec3_t delta;
  572. float axisLength;
  573. if ( ent->e.reType != refEntityType_t::RT_MODEL )
  574. {
  575. Com_Memset( orientation , 0, sizeof( * orientation ) );
  576. orientation ->axis[ 0 ][ 0 ] = 1;
  577. orientation ->axis[ 1 ][ 1 ] = 1;
  578. orientation ->axis[ 2 ][ 2 ] = 1;
  579. VectorCopy( light->l.origin, orientation ->viewOrigin );
  580. MatrixIdentity( orientation ->transformMatrix );
  581. MatrixMultiply( light->viewMatrix, orientation ->transformMatrix, orientation ->viewMatrix );
  582. MatrixCopy( orientation ->viewMatrix, orientation ->modelViewMatrix );
  583. return;
  584. }
  585. VectorCopy( ent->e.origin, orientation ->origin );
  586. VectorCopy( ent->e.axis[ 0 ], orientation ->axis[ 0 ] );
  587. VectorCopy( ent->e.axis[ 1 ], orientation ->axis[ 1 ] );
  588. VectorCopy( ent->e.axis[ 2 ], orientation ->axis[ 2 ] );
  589. MatrixSetupTransformFromVectorsFLU( orientation ->transformMatrix, orientation ->axis[ 0 ], orientation ->axis[ 1 ], orientation ->axis[ 2 ], orientation ->origin );
  590. MatrixAffineInverse( orientation ->transformMatrix, orientation ->viewMatrix );
  591. MatrixMultiply( light->viewMatrix, orientation ->transformMatrix, orientation ->modelViewMatrix );
  592. // calculate the viewer origin in the model's space
  593. // needed for fog, specular, and environment mapping
  594. VectorSubtract( light->l.origin, orientation ->origin, delta );
  595. // compensate for scale in the axes if necessary
  596. if ( ent->e.nonNormalizedAxes )
  597. {
  598. axisLength = VectorLength( ent->e.axis[ 0 ] );
  599. if ( !axisLength )
  600. {
  601. axisLength = 0;
  602. }
  603. else
  604. {
  605. axisLength = 1.0f / axisLength;
  606. }
  607. }
  608. else
  609. {
  610. axisLength = 1.0f;
  611. }
  612. orientation ->viewOrigin[ 0 ] = DotProduct( delta, orientation ->axis[ 0 ] ) * axisLength;
  613. orientation ->viewOrigin[ 1 ] = DotProduct( delta, orientation ->axis[ 1 ] ) * axisLength;
  614. orientation ->viewOrigin[ 2 ] = DotProduct( delta, orientation ->axis[ 2 ] ) * axisLength;
  615. }
  616. /*
  617. =================
  618. R_RotateLightForViewParms
  619. =================
  620. */
  621. void R_RotateLightForViewParms( const trRefLight_t *light, const viewParms_t *viewParms, orientationr_t * orientation )
  622. {
  623. vec3_t delta;
  624. VectorCopy( light->l.origin, orientation ->origin );
  625. QuatToAxis( light->l.rotation, orientation ->axis );
  626. MatrixSetupTransformFromVectorsFLU( orientation ->transformMatrix, orientation ->axis[ 0 ], orientation ->axis[ 1 ], orientation ->axis[ 2 ], orientation ->origin );
  627. MatrixAffineInverse( orientation ->transformMatrix, orientation ->viewMatrix );
  628. MatrixMultiply( viewParms->world.viewMatrix, orientation ->transformMatrix, orientation ->modelViewMatrix );
  629. // calculate the viewer origin in the light's space
  630. // needed for fog, specular, and environment mapping
  631. VectorSubtract( viewParms->orientation.origin, orientation ->origin, delta );
  632. orientation ->viewOrigin[ 0 ] = DotProduct( delta, orientation ->axis[ 0 ] );
  633. orientation ->viewOrigin[ 1 ] = DotProduct( delta, orientation ->axis[ 1 ] );
  634. orientation ->viewOrigin[ 2 ] = DotProduct( delta, orientation ->axis[ 2 ] );
  635. }
  636. /*
  637. =================
  638. R_RotateForViewer
  639. Sets up the modelview matrix for a given viewParm
  640. =================
  641. */
  642. void R_RotateForViewer()
  643. {
  644. matrix_t transformMatrix;
  645. Com_Memset( &tr.orientation, 0, sizeof( tr.orientation ) );
  646. tr.orientation.axis[ 0 ][ 0 ] = 1;
  647. tr.orientation.axis[ 1 ][ 1 ] = 1;
  648. tr.orientation.axis[ 2 ][ 2 ] = 1;
  649. VectorCopy( tr.viewParms.orientation.origin, tr.orientation.viewOrigin );
  650. MatrixIdentity( tr.orientation.transformMatrix );
  651. // transform by the camera placement
  652. MatrixSetupTransformFromVectorsFLU( transformMatrix,
  653. tr.viewParms.orientation.axis[ 0 ], tr.viewParms.orientation.axis[ 1 ], tr.viewParms.orientation.axis[ 2 ], tr.viewParms.orientation.origin );
  654. MatrixAffineInverse( transformMatrix, tr.orientation.viewMatrix2 );
  655. // convert from our right handed coordinate system (looking down X)
  656. // to OpenGL's right handed coordinate system (looking down -Z)
  657. MatrixMultiply( quakeToOpenGLMatrix, tr.orientation.viewMatrix2, tr.orientation.viewMatrix );
  658. MatrixCopy( tr.orientation.viewMatrix, tr.orientation.modelViewMatrix );
  659. tr.viewParms.world = tr.orientation;
  660. }
  661. /*
  662. ** SetFarClip
  663. */
  664. static void SetFarClip()
  665. {
  666. float farthestCornerDistance;
  667. int i;
  668. // if not rendering the world (icons, menus, etc)
  669. // set a 2k far clip plane
  670. if ( tr.refdef.rdflags & RDF_NOWORLDMODEL )
  671. {
  672. tr.viewParms.zFar = 2048;
  673. return;
  674. }
  675. //
  676. // set far clipping planes dynamically
  677. //
  678. farthestCornerDistance = 0;
  679. // check visBounds
  680. for ( i = 0; i < 8; i++ )
  681. {
  682. vec3_t v;
  683. float distance;
  684. if ( i & 1 )
  685. {
  686. v[ 0 ] = tr.viewParms.visBounds[ 0 ][ 0 ];
  687. }
  688. else
  689. {
  690. v[ 0 ] = tr.viewParms.visBounds[ 1 ][ 0 ];
  691. }
  692. if ( i & 2 )
  693. {
  694. v[ 1 ] = tr.viewParms.visBounds[ 0 ][ 1 ];
  695. }
  696. else
  697. {
  698. v[ 1 ] = tr.viewParms.visBounds[ 1 ][ 1 ];
  699. }
  700. if ( i & 4 )
  701. {
  702. v[ 2 ] = tr.viewParms.visBounds[ 0 ][ 2 ];
  703. }
  704. else
  705. {
  706. v[ 2 ] = tr.viewParms.visBounds[ 1 ][ 2 ];
  707. }
  708. distance = DistanceSquared( v, tr.viewParms.orientation.origin );
  709. if ( distance > farthestCornerDistance )
  710. {
  711. farthestCornerDistance = distance;
  712. }
  713. }
  714. tr.viewParms.zFar = sqrt( farthestCornerDistance );
  715. }
  716. /*
  717. ===============
  718. R_SetupProjection
  719. ===============
  720. */
  721. // *INDENT-OFF*
  722. static void R_SetupProjection( bool infiniteFarClip )
  723. {
  724. float zNear, zFar;
  725. float *proj = tr.viewParms.projectionMatrix;
  726. // dynamically compute far clip plane distance
  727. SetFarClip();
  728. // portal views are constrained to their surface plane
  729. if ( tr.viewParms.portalLevel == 0 )
  730. {
  731. tr.viewParms.zNear = r_znear->value;
  732. }
  733. zNear = tr.viewParms.zNear;
  734. if ( r_zfar->value )
  735. {
  736. zFar = tr.viewParms.zFar = std::max( tr.viewParms.zFar, r_zfar->value );
  737. }
  738. else if ( infiniteFarClip )
  739. {
  740. zFar = tr.viewParms.zFar = 0;
  741. }
  742. else
  743. {
  744. zFar = tr.viewParms.zFar;
  745. }
  746. if ( zFar <= 0 || infiniteFarClip ) // || r_showBspNodes->integer)
  747. {
  748. MatrixPerspectiveProjectionFovXYInfiniteRH( proj, tr.refdef.fov_x, tr.refdef.fov_y, zNear );
  749. }
  750. else
  751. {
  752. MatrixPerspectiveProjectionFovXYRH( proj, tr.refdef.fov_x, tr.refdef.fov_y, zNear, zFar );
  753. }
  754. }
  755. // *INDENT-ON*
  756. /*
  757. =================
  758. R_SetupUnprojection
  759. create a matrix with similar functionality like gluUnproject, project from window space to world space
  760. =================
  761. */
  762. static void R_SetupUnprojection()
  763. {
  764. float *unprojectMatrix = tr.viewParms.unprojectionMatrix;
  765. MatrixCopy( tr.viewParms.projectionMatrix, unprojectMatrix );
  766. MatrixMultiply2( unprojectMatrix, quakeToOpenGLMatrix );
  767. MatrixMultiply2( unprojectMatrix, tr.viewParms.world.viewMatrix2 );
  768. MatrixInverse( unprojectMatrix );
  769. MatrixMultiplyTranslation( unprojectMatrix, -1.0, -1.0, -1.0 );
  770. MatrixMultiplyScale( unprojectMatrix, 2.0f / glConfig.vidWidth, 2.0f / glConfig.vidHeight, 2.0 );
  771. }
  772. /*
  773. =================
  774. R_SetupFrustum
  775. Setup that culling frustum planes for the current view
  776. =================
  777. */
  778. static void R_SetupFrustum()
  779. {
  780. int i;
  781. float xs, xc;
  782. float ang;
  783. vec3_t planeOrigin;
  784. if ( tr.viewParms.portalLevel > 0 )
  785. {
  786. // this is a portal, so constrain the culling frustum to the portal surface
  787. matrix_t invTransform;
  788. MatrixAffineInverse(tr.viewParms.world.viewMatrix, invTransform);
  789. //transform planes back to world space for culling
  790. for (int i = 0; i <= FRUSTUM_NEAR; i++)
  791. {
  792. vec4_t plane;
  793. VectorCopy(tr.viewParms.portalFrustum[i].normal, plane);
  794. plane[3] = tr.viewParms.portalFrustum[i].dist;
  795. MatrixTransformPlane2(invTransform, plane);
  796. VectorCopy(plane, tr.viewParms.frustums[0][i].normal);
  797. tr.viewParms.frustums[0][i].dist = plane[3];
  798. SetPlaneSignbits(&tr.viewParms.frustums[0][i]);
  799. tr.viewParms.frustums[0][i].type = PLANE_NON_AXIAL;
  800. }
  801. }
  802. else
  803. {
  804. ang = tr.viewParms.fovX / 180 * M_PI * 0.5f;
  805. xs = sin( ang );
  806. xc = cos( ang );
  807. VectorScale( tr.viewParms.orientation.axis[ 0 ], xs, tr.viewParms.frustums[ 0 ][ 0 ].normal );
  808. VectorMA( tr.viewParms.frustums[ 0 ][ 0 ].normal, xc, tr.viewParms.orientation.axis[ 1 ], tr.viewParms.frustums[ 0 ][ 0 ].normal );
  809. VectorScale( tr.viewParms.orientation.axis[ 0 ], xs, tr.viewParms.frustums[ 0 ][ 1 ].normal );
  810. VectorMA( tr.viewParms.frustums[ 0 ][ 1 ].normal, -xc, tr.viewParms.orientation.axis[ 1 ], tr.viewParms.frustums[ 0 ][ 1 ].normal );
  811. ang = tr.viewParms.fovY / 180 * M_PI * 0.5f;
  812. xs = sin( ang );
  813. xc = cos( ang );
  814. VectorScale( tr.viewParms.orientation.axis[ 0 ], xs, tr.viewParms.frustums[ 0 ][ 2 ].normal );
  815. VectorMA( tr.viewParms.frustums[ 0 ][ 2 ].normal, xc, tr.viewParms.orientation.axis[ 2 ], tr.viewParms.frustums[ 0 ][ 2 ].normal );
  816. VectorScale( tr.viewParms.orientation.axis[ 0 ], xs, tr.viewParms.frustums[ 0 ][ 3 ].normal );
  817. VectorMA( tr.viewParms.frustums[ 0 ][ 3 ].normal, -xc, tr.viewParms.orientation.axis[ 2 ], tr.viewParms.frustums[ 0 ][ 3 ].normal );
  818. for ( i = 0; i < 4; i++ )
  819. {
  820. tr.viewParms.frustums[ 0 ][ i ].type = PLANE_NON_AXIAL;
  821. tr.viewParms.frustums[ 0 ][ i ].dist = DotProduct( tr.viewParms.orientation.origin, tr.viewParms.frustums[ 0 ][ i ].normal );
  822. SetPlaneSignbits( &tr.viewParms.frustums[ 0 ][ i ] );
  823. }
  824. // Tr3B: set extra near plane which is required by the dynamic occlusion culling
  825. tr.viewParms.frustums[ 0 ][ FRUSTUM_NEAR ].type = PLANE_NON_AXIAL;
  826. VectorCopy( tr.viewParms.orientation.axis[ 0 ], tr.viewParms.frustums[ 0 ][ FRUSTUM_NEAR ].normal );
  827. VectorMA( tr.viewParms.orientation.origin, r_znear->value, tr.viewParms.frustums[ 0 ][ FRUSTUM_NEAR ].normal, planeOrigin );
  828. tr.viewParms.frustums[ 0 ][ FRUSTUM_NEAR ].dist = DotProduct( planeOrigin, tr.viewParms.frustums[ 0 ][ FRUSTUM_NEAR ].normal );
  829. SetPlaneSignbits( &tr.viewParms.frustums[ 0 ][ FRUSTUM_NEAR ] );
  830. }
  831. }
  832. /*
  833. =================
  834. R_SetupFrustum
  835. Setup that culling frustum planes for the current view
  836. =================
  837. */
  838. // *INDENT-OFF*
  839. void R_SetupFrustum2( frustum_t frustum, const matrix_t mvp )
  840. {
  841. // http://www2.ravensoft.com/users/ggribb/plane%20extraction.pdf
  842. int i;
  843. // left
  844. frustum[ FRUSTUM_LEFT ].normal[ 0 ] = mvp[ 3 ] + mvp[ 0 ];
  845. frustum[ FRUSTUM_LEFT ].normal[ 1 ] = mvp[ 7 ] + mvp[ 4 ];
  846. frustum[ FRUSTUM_LEFT ].normal[ 2 ] = mvp[ 11 ] + mvp[ 8 ];
  847. frustum[ FRUSTUM_LEFT ].dist = - ( mvp[ 15 ] + mvp[ 12 ] );
  848. // right
  849. frustum[ FRUSTUM_RIGHT ].normal[ 0 ] = mvp[ 3 ] - mvp[ 0 ];
  850. frustum[ FRUSTUM_RIGHT ].normal[ 1 ] = mvp[ 7 ] - mvp[ 4 ];
  851. frustum[ FRUSTUM_RIGHT ].normal[ 2 ] = mvp[ 11 ] - mvp[ 8 ];
  852. frustum[ FRUSTUM_RIGHT ].dist = - ( mvp[ 15 ] - mvp[ 12 ] );
  853. // bottom
  854. frustum[ FRUSTUM_BOTTOM ].normal[ 0 ] = mvp[ 3 ] + mvp[ 1 ];
  855. frustum[ FRUSTUM_BOTTOM ].normal[ 1 ] = mvp[ 7 ] + mvp[ 5 ];
  856. frustum[ FRUSTUM_BOTTOM ].normal[ 2 ] = mvp[ 11 ] + mvp[ 9 ];
  857. frustum[ FRUSTUM_BOTTOM ].dist = - ( mvp[ 15 ] + mvp[ 13 ] );
  858. // top
  859. frustum[ FRUSTUM_TOP ].normal[ 0 ] = mvp[ 3 ] - mvp[ 1 ];
  860. frustum[ FRUSTUM_TOP ].normal[ 1 ] = mvp[ 7 ] - mvp[ 5 ];
  861. frustum[ FRUSTUM_TOP ].normal[ 2 ] = mvp[ 11 ] - mvp[ 9 ];
  862. frustum[ FRUSTUM_TOP ].dist = - ( mvp[ 15 ] - mvp[ 13 ] );
  863. // near
  864. frustum[ FRUSTUM_NEAR ].normal[ 0 ] = mvp[ 3 ] + mvp[ 2 ];
  865. frustum[ FRUSTUM_NEAR ].normal[ 1 ] = mvp[ 7 ] + mvp[ 6 ];
  866. frustum[ FRUSTUM_NEAR ].normal[ 2 ] = mvp[ 11 ] + mvp[ 10 ];
  867. frustum[ FRUSTUM_NEAR ].dist = - ( mvp[ 15 ] + mvp[ 14 ] );
  868. // far
  869. frustum[ FRUSTUM_FAR ].normal[ 0 ] = mvp[ 3 ] - mvp[ 2 ];
  870. frustum[ FRUSTUM_FAR ].normal[ 1 ] = mvp[ 7 ] - mvp[ 6 ];
  871. frustum[ FRUSTUM_FAR ].normal[ 2 ] = mvp[ 11 ] - mvp[ 10 ];
  872. frustum[ FRUSTUM_FAR ].dist = - ( mvp[ 15 ] - mvp[ 14 ] );
  873. for ( i = 0; i < 6; i++ )
  874. {
  875. vec_t length, ilength;
  876. frustum[ i ].type = PLANE_NON_AXIAL;
  877. // normalize
  878. length = VectorLength( frustum[ i ].normal );
  879. if ( length )
  880. {
  881. ilength = 1.0 / length;
  882. frustum[ i ].normal[ 0 ] *= ilength;
  883. frustum[ i ].normal[ 1 ] *= ilength;
  884. frustum[ i ].normal[ 2 ] *= ilength;
  885. frustum[ i ].dist *= ilength;
  886. }
  887. SetPlaneSignbits( &frustum[ i ] );
  888. }
  889. }
  890. // *INDENT-ON*
  891. void R_CalcFrustumNearCorners( const vec4_t frustum[ FRUSTUM_PLANES ], vec3_t corners[ 4 ] )
  892. {
  893. PlanesGetIntersectionPoint( frustum[ FRUSTUM_LEFT ], frustum[ FRUSTUM_TOP ], frustum[ FRUSTUM_NEAR ], corners[ 0 ] );
  894. PlanesGetIntersectionPoint( frustum[ FRUSTUM_RIGHT ], frustum[ FRUSTUM_TOP ], frustum[ FRUSTUM_NEAR ], corners[ 1 ] );
  895. PlanesGetIntersectionPoint( frustum[ FRUSTUM_RIGHT ], frustum[ FRUSTUM_BOTTOM ], frustum[ FRUSTUM_NEAR ], corners[ 2 ] );
  896. PlanesGetIntersectionPoint( frustum[ FRUSTUM_LEFT ], frustum[ FRUSTUM_BOTTOM ], frustum[ FRUSTUM_NEAR ], corners[ 3 ] );
  897. }
  898. void R_CalcFrustumFarCorners( const vec4_t frustum[ FRUSTUM_PLANES ], vec3_t corners[ 4 ] )
  899. {
  900. PlanesGetIntersectionPoint( frustum[ FRUSTUM_LEFT ], frustum[ FRUSTUM_TOP ], frustum[ FRUSTUM_FAR ], corners[ 0 ] );
  901. PlanesGetIntersectionPoint( frustum[ FRUSTUM_RIGHT ], frustum[ FRUSTUM_TOP ], frustum[ FRUSTUM_FAR ], corners[ 1 ] );
  902. PlanesGetIntersectionPoint( frustum[ FRUSTUM_RIGHT ], frustum[ FRUSTUM_BOTTOM ], frustum[ FRUSTUM_FAR ], corners[ 2 ] );
  903. PlanesGetIntersectionPoint( frustum[ FRUSTUM_LEFT ], frustum[ FRUSTUM_BOTTOM ], frustum[ FRUSTUM_FAR ], corners[ 3 ] );
  904. }
  905. static void CopyPlane( const cplane_t *in, cplane_t *out )
  906. {
  907. VectorCopy( in->normal, out->normal );
  908. out->dist = in->dist;
  909. out->type = in->type;
  910. out->signbits = in->signbits;
  911. out->pad[ 0 ] = in->pad[ 0 ];
  912. out->pad[ 1 ] = in->pad[ 1 ];
  913. }
  914. static void R_SetupSplitFrustums()
  915. {
  916. int i, j;
  917. float lambda;
  918. float ratio;
  919. vec3_t planeOrigin;
  920. float zNear, zFar;
  921. lambda = r_parallelShadowSplitWeight->value;
  922. ratio = tr.viewParms.zFar / tr.viewParms.zNear;
  923. for ( j = 0; j < 5; j++ )
  924. {
  925. CopyPlane( &tr.viewParms.frustums[ 0 ][ j ], &tr.viewParms.frustums[ 1 ][ j ] );
  926. }
  927. for ( i = 1; i <= ( r_parallelShadowSplits->integer + 1 ); i++ )
  928. {
  929. float si = i / ( float )( r_parallelShadowSplits->integer + 1 );
  930. zFar = 1.005f * lambda * ( tr.viewParms.zNear * powf( ratio, si ) ) + ( 1 - lambda ) * ( tr.viewParms.zNear + ( tr.viewParms.zFar - tr.viewParms.zNear ) * si );
  931. if ( i <= r_parallelShadowSplits->integer )
  932. {
  933. tr.viewParms.parallelSplitDistances[ i - 1 ] = zFar;
  934. }
  935. tr.viewParms.frustums[ i ][ FRUSTUM_FAR ].type = PLANE_NON_AXIAL;
  936. VectorNegate( tr.viewParms.orientation.axis[ 0 ], tr.viewParms.frustums[ i ][ FRUSTUM_FAR ].normal );
  937. VectorMA( tr.viewParms.orientation.origin, zFar, tr.viewParms.orientation.axis[ 0 ], planeOrigin );
  938. tr.viewParms.frustums[ i ][ FRUSTUM_FAR ].dist = DotProduct( planeOrigin, tr.viewParms.frustums[ i ][ FRUSTUM_FAR ].normal );
  939. SetPlaneSignbits( &tr.viewParms.frustums[ i ][ FRUSTUM_FAR ] );
  940. if ( i <= ( r_parallelShadowSplits->integer ) )
  941. {
  942. zNear = zFar - ( zFar * 0.005f );
  943. tr.viewParms.frustums[ i + 1 ][ FRUSTUM_NEAR ].type = PLANE_NON_AXIAL;
  944. VectorCopy( tr.viewParms.orientation.axis[ 0 ], tr.viewParms.frustums[ i + 1 ][ FRUSTUM_NEAR ].normal );
  945. VectorMA( tr.viewParms.orientation.origin, zNear, tr.viewParms.orientation.axis[ 0 ], planeOrigin );
  946. tr.viewParms.frustums[ i + 1 ][ FRUSTUM_NEAR ].dist = DotProduct( planeOrigin, tr.viewParms.frustums[ i + 1 ][ FRUSTUM_NEAR ].normal );
  947. SetPlaneSignbits( &tr.viewParms.frustums[ i + 1 ][ FRUSTUM_NEAR ] );
  948. }
  949. for ( j = 0; j < 4; j++ )
  950. {
  951. CopyPlane( &tr.viewParms.frustums[ 0 ][ j ], &tr.viewParms.frustums[ i ][ j ] );
  952. }
  953. }
  954. }
  955. /*
  956. =================
  957. R_MirrorPoint
  958. =================
  959. */
  960. void R_MirrorPoint( vec3_t in, orientation_t *surface, orientation_t *camera, vec3_t out )
  961. {
  962. int i;
  963. vec3_t local;
  964. vec3_t transformed;
  965. float d;
  966. VectorSubtract( in, surface->origin, local );
  967. VectorClear( transformed );
  968. for ( i = 0; i < 3; i++ )
  969. {
  970. d = DotProduct( local, surface->axis[ i ] );
  971. VectorMA( transformed, d, camera->axis[ i ], transformed );
  972. }
  973. VectorAdd( transformed, camera->origin, out );
  974. }
  975. void R_MirrorVector( vec3_t in, orientation_t *surface, orientation_t *camera, vec3_t out )
  976. {
  977. int i;
  978. float d;
  979. VectorClear( out );
  980. for ( i = 0; i < 3; i++ )
  981. {
  982. d = DotProduct( in, surface->axis[ i ] );
  983. VectorMA( out, d, camera->axis[ i ], out );
  984. }
  985. }
  986. /*
  987. =============
  988. R_PlaneForSurface
  989. =============
  990. */
  991. void R_PlaneForSurface( surfaceType_t *surfType, cplane_t *plane )
  992. {
  993. srfTriangles_t *tri;
  994. srfPoly_t *poly;
  995. srfVert_t *v1, *v2, *v3;
  996. vec4_t plane4;
  997. if ( !surfType )
  998. {
  999. Com_Memset( plane, 0, sizeof( *plane ) );
  1000. plane->normal[ 0 ] = 1;
  1001. return;
  1002. }
  1003. switch ( *surfType )
  1004. {
  1005. case surfaceType_t::SF_FACE:
  1006. *plane = ( ( srfSurfaceFace_t * ) surfType )->plane;
  1007. return;
  1008. case surfaceType_t::SF_TRIANGLES:
  1009. tri = ( srfTriangles_t * ) surfType;
  1010. v1 = tri->verts + tri->triangles[ 0 ].indexes[ 0 ];
  1011. v2 = tri->verts + tri->triangles[ 0 ].indexes[ 1 ];
  1012. v3 = tri->verts + tri->triangles[ 0 ].indexes[ 2 ];
  1013. PlaneFromPoints( plane4, v1->xyz, v2->xyz, v3->xyz );
  1014. VectorCopy( plane4, plane->normal );
  1015. plane->dist = plane4[ 3 ];
  1016. return;
  1017. case surfaceType_t::SF_POLY:
  1018. poly = ( srfPoly_t * ) surfType;
  1019. PlaneFromPoints( plane4, poly->verts[ 0 ].xyz, poly->verts[ 1 ].xyz, poly->verts[ 2 ].xyz );
  1020. VectorCopy( plane4, plane->normal );
  1021. plane->dist = plane4[ 3 ];
  1022. return;
  1023. default:
  1024. Com_Memset( plane, 0, sizeof( *plane ) );
  1025. plane->normal[ 0 ] = 1;
  1026. return;
  1027. }
  1028. }
  1029. /*
  1030. =================
  1031. R_GetPortalOrientations
  1032. entityNum is the entity that the portal surface is a part of, which may
  1033. be moving and rotating.
  1034. Returns true if it should be mirrored
  1035. =================
  1036. */
  1037. static bool R_GetPortalOrientations( drawSurf_t *drawSurf, orientation_t *surface, orientation_t *camera, vec3_t pvsOrigin,
  1038. bool *mirror )
  1039. {
  1040. int i;
  1041. cplane_t originalPlane, plane;
  1042. trRefEntity_t *e;
  1043. float d;
  1044. vec3_t transformed;
  1045. // create plane axis for the portal we are seeing
  1046. R_PlaneForSurface( drawSurf->surface, &originalPlane );
  1047. // rotate the plane if necessary
  1048. if ( drawSurf->entity != &tr.worldEntity )
  1049. {
  1050. tr.currentEntity = drawSurf->entity;
  1051. // get the orientation of the entity
  1052. R_RotateEntityForViewParms( tr.currentEntity, &tr.viewParms, &tr.orientation );
  1053. // rotate the plane, but keep the non-rotated version for matching
  1054. // against the portalSurface entities
  1055. R_LocalNormalToWorld( originalPlane.normal, plane.normal );
  1056. plane.dist = originalPlane.dist + DotProduct( plane.normal, tr.orientation.origin );
  1057. // translate the original plane
  1058. originalPlane.dist = originalPlane.dist + DotProduct( originalPlane.normal, tr.orientation.origin );
  1059. }
  1060. else
  1061. {
  1062. plane = originalPlane;
  1063. }
  1064. VectorCopy( plane.normal, surface->axis[ 0 ] );
  1065. PerpendicularVector( surface->axis[ 1 ], surface->axis[ 0 ] );
  1066. CrossProduct( surface->axis[ 0 ], surface->axis[ 1 ], surface->axis[ 2 ] );
  1067. // locate the portal entity closest to this plane.
  1068. // origin will be the origin of the portal, origin2 will be
  1069. // the origin of the camera
  1070. for ( i = 0; i < tr.refdef.numEntities; i++ )
  1071. {
  1072. e = &tr.refdef.entities[ i ];
  1073. if ( e->e.reType != refEntityType_t::RT_PORTALSURFACE )
  1074. {
  1075. continue;
  1076. }
  1077. d = DotProduct( e->e.origin, originalPlane.normal ) - originalPlane.dist;
  1078. if ( d > 64 || d < -64 )
  1079. {
  1080. continue;
  1081. }
  1082. // get the pvsOrigin from the entity
  1083. VectorCopy( e->e.oldorigin, pvsOrigin );
  1084. // if the entity is just a mirror, don't use as a camera point
  1085. if ( e->e.oldorigin[ 0 ] == e->e.origin[ 0 ] && e->e.oldorigin[ 1 ] == e->e.origin[ 1 ] && e->e.oldorigin[ 2 ] == e->e.origin[ 2 ] )
  1086. {
  1087. VectorScale( plane.normal, plane.dist, surface->origin );
  1088. VectorCopy( surface->origin, camera->origin );
  1089. VectorSubtract( vec3_origin, surface->axis[ 0 ], camera->axis[ 0 ] );
  1090. VectorCopy( surface->axis[ 1 ], camera->axis[ 1 ] );
  1091. VectorCopy( surface->axis[ 2 ], camera->axis[ 2 ] );
  1092. *mirror = true;
  1093. return true;
  1094. }
  1095. // project the origin onto the surface plane to get
  1096. // an origin point we can rotate around
  1097. d = DotProduct( e->e.origin, plane.normal ) - plane.dist;
  1098. VectorMA( e->e.origin, -d, surface->axis[ 0 ], surface->origin );
  1099. // now get the camera origin and orientation
  1100. VectorCopy( e->e.oldorigin, camera->origin );
  1101. AxisCopy( e->e.axis, camera->axis );
  1102. VectorSubtract( vec3_origin, camera->axis[ 0 ], camera->axis[ 0 ] );
  1103. VectorSubtract( vec3_origin, camera->axis[ 1 ], camera->axis[ 1 ] );
  1104. // optionally rotate
  1105. if ( e->e.oldframe )
  1106. {
  1107. // if a speed is specified
  1108. if ( e->e.frame )
  1109. {
  1110. // continuous rotate
  1111. d = ( tr.refdef.time / 1000.0f ) * e->e.frame;
  1112. VectorCopy( camera->axis[ 1 ], transformed );
  1113. RotatePointAroundVector( camera->axis[ 1 ], camera->axis[ 0 ], transformed, d );
  1114. CrossProduct( camera->axis[ 0 ], camera->axis[ 1 ], camera->axis[ 2 ] );
  1115. }
  1116. else
  1117. {
  1118. // bobbing rotate, with skinNum being the rotation offset
  1119. d = sin( tr.refdef.time * 0.003f );
  1120. d = e->e.skinNum + d * 4;
  1121. VectorCopy( camera->axis[ 1 ], transformed );
  1122. RotatePointAroundVector( camera->axis[ 1 ], camera->axis[ 0 ], transformed, d );
  1123. CrossProduct( camera->axis[ 0 ], camera->axis[ 1 ], camera->axis[ 2 ] );
  1124. }
  1125. }
  1126. else if ( e->e.skinNum )
  1127. {
  1128. d = e->e.skinNum;
  1129. VectorCopy( camera->axis[ 1 ], transformed );
  1130. RotatePointAroundVector( camera->axis[ 1 ], camera->axis[ 0 ], transformed, d );
  1131. CrossProduct( camera->axis[ 0 ], camera->axis[ 1 ], camera->axis[ 2 ] );
  1132. }
  1133. *mirror = false;
  1134. return true;
  1135. }
  1136. // if we didn't locate a portal entity, don't render anything.
  1137. // We don't want to just treat it as a mirror, because without a
  1138. // portal entity the server won't have communicated a proper entity set
  1139. // in the snapshot
  1140. // unfortunately, with local movement prediction it is easily possible
  1141. // to see a surface before the server has communicated the matching
  1142. // portal surface entity, so we don't want to print anything here...
  1143. return false;
  1144. }
  1145. static bool IsMirror( const drawSurf_t *drawSurf )
  1146. {
  1147. int i;
  1148. cplane_t originalPlane, plane;
  1149. trRefEntity_t *e;
  1150. float d;
  1151. // create plane axis for the portal we are seeing
  1152. R_PlaneForSurface( drawSurf->surface, &originalPlane );
  1153. // rotate the plane if necessary
  1154. if ( tr.currentEntity != &tr.worldEntity )
  1155. {
  1156. // get the orientation of the entity
  1157. R_RotateEntityForViewParms( tr.currentEntity, &tr.viewParms, &tr.orientation );
  1158. // rotate the plane, but keep the non-rotated version for matching
  1159. // against the portalSurface entities
  1160. R_LocalNormalToWorld( originalPlane.normal, plane.normal );
  1161. plane.dist = originalPlane.dist + DotProduct( plane.normal, tr.orientation.origin );
  1162. // translate the original plane
  1163. originalPlane.dist = originalPlane.dist + DotProduct( originalPlane.normal, tr.orientation.origin );
  1164. }
  1165. else
  1166. {
  1167. plane = originalPlane;
  1168. }
  1169. // locate the portal entity closest to this plane.
  1170. // origin will be the origin of the portal, origin2 will be
  1171. // the origin of the camera
  1172. for ( i = 0; i < tr.refdef.numEntities; i++ )
  1173. {
  1174. e = &tr.refdef.entities[ i ];
  1175. if ( e->e.reType != refEntityType_t::RT_PORTALSURFACE )
  1176. {
  1177. continue;
  1178. }
  1179. d = DotProduct( e->e.origin, originalPlane.normal ) - originalPlane.dist;
  1180. if ( d > 64 || d < -64 )
  1181. {
  1182. continue;
  1183. }
  1184. // if the entity is just a mirror, don't use as a camera point
  1185. if ( e->e.oldorigin[ 0 ] == e->e.origin[ 0 ] && e->e.oldorigin[ 1 ] == e->e.origin[ 1 ] && e->e.oldorigin[ 2 ] == e->e.origin[ 2 ] )
  1186. {
  1187. return true;
  1188. }
  1189. return false;
  1190. }
  1191. return false;
  1192. }
  1193. /*
  1194. ** SurfBoxIsOffscreen
  1195. **
  1196. ** Determines if a surface's AABB is completely offscreen
  1197. ** also computes a conservative screen rectangle bounds for the surface
  1198. */
  1199. static bool SurfBoxIsOffscreen(const drawSurf_t *drawSurf, screenRect_t& surfRect)
  1200. {
  1201. shader_t *shader;
  1202. screenRect_t parentRect;
  1203. parentRect.coords[0] = tr.viewParms.scissorX;
  1204. parentRect.coords[1] = tr.viewParms.scissorY;
  1205. parentRect.coords[2] = tr.viewParms.scissorX + tr.viewParms.scissorWidth - 1;
  1206. parentRect.coords[3] = tr.viewParms.scissorY + tr.viewParms.scissorHeight - 1;
  1207. surfRect = parentRect;
  1208. // only these surfaces supported for now
  1209. if (*drawSurf->surface != surfaceType_t::SF_FACE &&
  1210. *drawSurf->surface != surfaceType_t::SF_TRIANGLES &&
  1211. *drawSurf->surface != surfaceType_t::SF_GRID &&
  1212. *drawSurf->surface != surfaceType_t::SF_VBO_MESH)
  1213. {
  1214. return false;
  1215. }
  1216. tr.currentEntity = drawSurf->entity;
  1217. shader = drawSurf->shader;
  1218. shader = (shader->remappedShader) ? shader->remappedShader : shader;
  1219. // deforms need tess subsystem for support
  1220. if (shader->numDeforms > 0)
  1221. {
  1222. return false;
  1223. }
  1224. // rotate if necessary
  1225. if (tr.currentEntity != &tr.worldEntity)
  1226. {
  1227. R_RotateEntityForViewParms(tr.currentEntity, &tr.viewParms, &tr.orientation);
  1228. }
  1229. else
  1230. {
  1231. tr.orientation = tr.viewParms.world;
  1232. }
  1233. srfGeneric_t* srf = reinterpret_cast<srfGeneric_t*>(drawSurf->surface);
  1234. vec3_t v;
  1235. vec4_t eye, clip;
  1236. screenRect_t newRect;
  1237. float shortest = 100000000;
  1238. Vector4Set(newRect.coords, 999999, 999999, -999999, -999999);
  1239. unsigned int pointOr = 0;
  1240. unsigned int pointAnd = (unsigned int)~0;
  1241. for (int i = 0; i < 8; i++)
  1242. {
  1243. vec3_t transPoint;
  1244. vec4_t normalized;
  1245. vec4_t window;
  1246. unsigned int pointFlags = 0;
  1247. v[0] = srf->bounds[i & 1][0];
  1248. v[1] = srf->bounds[(i >> 1) & 1][1];
  1249. v[2] = srf->bounds[(i >> 2) & 1][2];
  1250. R_LocalPointToWorld(v, transPoint);
  1251. R_TransformModelToClip(transPoint, tr.orientation.modelViewMatrix, tr.viewParms.projectionMatrix, eye, clip);
  1252. float distSq = DotProduct(eye, eye);
  1253. if (distSq < shortest)
  1254. {
  1255. shortest = distSq;
  1256. }
  1257. R_TransformClipToWindow(clip, &tr.viewParms, normalized, window);
  1258. newRect.coords[0] = std::min(newRect.coords[0], (int)window[0]);
  1259. newRect.coords[1] = std::min(newRect.coords[1], (int)window[1]);
  1260. newRect.coords[2] = std::max(newRect.coords[2], (int)window[0]);
  1261. newRect.coords[3] = std::max(newRect.coords[3], (int)window[1]);
  1262. for (int j = 0; j < 3; j++)
  1263. {
  1264. if (clip[j] >= clip[3])
  1265. {
  1266. pointFlags |= (1 << (j * 2));
  1267. }
  1268. else if (clip[j] <= -clip[3])
  1269. {
  1270. pointFlags |= (1 << (j * 2 + 1));
  1271. }
  1272. }
  1273. pointAnd &= pointFlags;
  1274. pointOr |= pointFlags;
  1275. }
  1276. // if the surface intersects the near plane, then expand the scissor rect to cover the screen because of back projection
  1277. // OPTIMIZE: can be avoided by clipping box edges with the near plane
  1278. if (pointOr & 0x20)
  1279. {
  1280. newRect = parentRect;
  1281. }
  1282. surfRect.coords[0] = std::max(newRect.coords[0], surfRect.coords[0]);
  1283. surfRect.coords[1] = std::max(newRect.coords[1], surfRect.coords[1]);
  1284. surfRect.coords[2] = std::min(newRect.coords[2], surfRect.coords[2]);
  1285. surfRect.coords[3] = std::min(newRect.coords[3], surfRect.coords[3]);
  1286. // trivially reject
  1287. if (pointAnd)
  1288. {
  1289. return true;
  1290. }
  1291. // mirrors can early out at this point, since we don't do a fade over distance
  1292. // with them (although we could)
  1293. if (IsMirror(drawSurf))
  1294. {
  1295. return false;
  1296. }
  1297. if (shortest > (shader->portalRange * shader->portalRange))
  1298. {
  1299. return true;
  1300. }
  1301. return false;
  1302. }
  1303. /*
  1304. ** SurfIsOffscreen
  1305. **
  1306. ** Determines if a surface is completely offscreen.
  1307. ** also computes a conservative screen rectangle bounds for the surface
  1308. */
  1309. static bool SurfIsOffscreen( const drawSurf_t *drawSurf, screenRect_t& surfRect )
  1310. {
  1311. float shortest = 100000000;
  1312. shader_t *shader;
  1313. int numTriangles;
  1314. vec4_t clip, eye;
  1315. unsigned int pointOr = 0;
  1316. unsigned int pointAnd = ( unsigned int ) ~0;
  1317. screenRect_t parentRect;
  1318. parentRect.coords[0] = tr.viewParms.scissorX;
  1319. parentRect.coords[1] = tr.viewParms.scissorY;
  1320. parentRect.coords[2] = tr.viewParms.scissorX + tr.viewParms.scissorWidth - 1;
  1321. parentRect.coords[3] = tr.viewParms.scissorY + tr.viewParms.scissorHeight - 1;
  1322. surfRect = parentRect;
  1323. if ( glConfig.smpActive )
  1324. {
  1325. // FIXME! we can't do Tess_Begin/Tess_End stuff with smp!
  1326. return SurfBoxIsOffscreen(drawSurf, surfRect);
  1327. }
  1328. tr.currentEntity = drawSurf->entity;
  1329. shader = drawSurf->shader;
  1330. // rotate if necessary
  1331. if ( tr.currentEntity != &tr.worldEntity )
  1332. {
  1333. R_RotateEntityForViewParms( tr.currentEntity, &tr.viewParms, &tr.orientation );
  1334. }
  1335. else
  1336. {
  1337. tr.orientation = tr.viewParms.world;
  1338. }
  1339. Tess_Begin( Tess_StageIteratorGeneric, nullptr, shader, nullptr, true, true, -1, 0 );
  1340. rb_surfaceTable[ Util::ordinal(*drawSurf->surface) ]( drawSurf->surface );
  1341. // Tr3B: former assertion
  1342. if ( tess.numVertexes >= 128 )
  1343. {
  1344. return SurfBoxIsOffscreen(drawSurf, surfRect);
  1345. }
  1346. screenRect_t newRect;
  1347. Vector4Set(newRect.coords, 999999, 999999, -999999, -999999);
  1348. for (unsigned i = 0; i < tess.numVertexes; i++ )
  1349. {
  1350. int j;
  1351. unsigned int pointFlags = 0;
  1352. vec4_t normalized;
  1353. vec4_t window;
  1354. R_TransformModelToClip( tess.verts[ i ].xyz, tr.orientation.modelViewMatrix, tr.viewParms.projectionMatrix, eye, clip );
  1355. R_TransformClipToWindow(clip, &tr.viewParms, normalized, window);
  1356. newRect.coords[0] = std::min(newRect.coords[0], (int)window[0]);
  1357. newRect.coords[1] = std::min(newRect.coords[1], (int)window[1]);
  1358. newRect.coords[2] = std::max(newRect.coords[2], (int)window[0]);
  1359. newRect.coords[3] = std::max(newRect.coords[3], (int)window[1]);
  1360. for ( j = 0; j < 3; j++ )
  1361. {
  1362. if ( clip[ j ] >= clip[ 3 ] )
  1363. {
  1364. pointFlags |= ( 1 << ( j * 2 ) );
  1365. }
  1366. else if ( clip[ j ] <= -clip[ 3 ] )
  1367. {
  1368. pointFlags |= ( 1 << ( j * 2 + 1 ) );
  1369. }
  1370. }
  1371. pointAnd &= pointFlags;
  1372. pointOr |= pointFlags;
  1373. }
  1374. // if the surface intersects the near plane, then expand the scissor rect to cover the screen because of back projection
  1375. // OPTIMIZE: can be avoided by clipping triangle edges with the near plane
  1376. if (pointOr & 0x20)
  1377. {
  1378. newRect = parentRect;
  1379. }
  1380. surfRect.coords[0] = std::max(newRect.coords[0], surfRect.coords[0]);
  1381. surfRect.coords[1] = std::max(newRect.coords[1], surfRect.coords[1]);
  1382. surfRect.coords[2] = std::min(newRect.coords[2], surfRect.coords[2]);
  1383. surfRect.coords[3] = std::min(newRect.coords[3], surfRect.coords[3]);
  1384. // trivially reject
  1385. if ( pointAnd )
  1386. {
  1387. return true;
  1388. }
  1389. // determine if this surface is backfaced and also determine the distance
  1390. // to the nearest vertex so we can cull based on portal range. Culling
  1391. // based on vertex distance isn't 100% correct (we should be checking for
  1392. // range to the surface), but it's good enough for the types of portals
  1393. // we have in the game right now.
  1394. numTriangles = tess.numIndexes / 3;
  1395. for (unsigned i = 0; i < tess.numIndexes; i += 3 )
  1396. {
  1397. vec3_t normal;
  1398. float dot;
  1399. float len;
  1400. vec3_t qnormal;
  1401. VectorSubtract( tess.verts[ tess.indexes[ i ] ].xyz, tr.orientation.viewOrigin, normal );
  1402. len = VectorLengthSquared( normal ); // lose the sqrt
  1403. if ( len < shortest )
  1404. {
  1405. shortest = len;
  1406. }
  1407. R_QtangentsToNormal( tess.verts[ tess.indexes[ i ] ].qtangents, qnormal );
  1408. if ( ( dot = DotProduct( normal, qnormal ) ) >= 0 )
  1409. {
  1410. numTriangles--;
  1411. }
  1412. }
  1413. if ( !numTriangles )
  1414. {
  1415. return true;
  1416. }
  1417. // mirrors can early out at this point, since we don't do a fade over distance
  1418. // with them (although we could)
  1419. if ( IsMirror( drawSurf ) )
  1420. {
  1421. return false;
  1422. }
  1423. if ( shortest > ( tess.surfaceShader->portalRange * tess.surfaceShader->portalRange ) )
  1424. {
  1425. return true;
  1426. }
  1427. return false;
  1428. }
  1429. /*
  1430. ========================
  1431. R_SetupPortalFrustum
  1432. Creates an oblique view space frustum that closely bounds the part
  1433. of the portal render view that is on screen
  1434. This frustum can be used for culling surfaces when rendering the portal view
  1435. if the frustum planes are transformed using the inverse view matrix
  1436. ========================
  1437. */
  1438. static void R_SetupPortalFrustum( const viewParms_t& oldParms, const orientation_t& camera, viewParms_t& newParms )
  1439. {
  1440. // points of the bounding screen rectangle for the portal surface
  1441. vec3_t sbottomleft = { float(newParms.scissorX), float(newParms.scissorY), -1.0f };
  1442. vec3_t stopright = { float(newParms.scissorX + newParms.scissorWidth - 1), float(newParms.scissorY + newParms.scissorHeight - 1), -1.0f };
  1443. vec3_t sbottomright = { stopright[0], sbottomleft[1], -1.0f };
  1444. vec3_t stopleft = { sbottomleft[0], stopright[1], -1.0f };
  1445. vec3_t bottomleft, bottomright, topright, topleft;
  1446. matrix_t invProjViewPortMatrix;
  1447. ASSERT(newParms.portalLevel > 0);
  1448. // need to unproject the bounding rectangle to view space
  1449. MatrixCopy(oldParms.projectionMatrix, invProjViewPortMatrix);
  1450. MatrixInverse(invProjViewPortMatrix);
  1451. MatrixMultiplyTranslation(invProjViewPortMatrix, -1.0, -1.0, -1.0);
  1452. MatrixMultiplyScale(invProjViewPortMatrix, 2.0f / glConfig.vidWidth, 2.0f / glConfig.vidHeight, 2.0);
  1453. frustum_t& frustum = newParms.portalFrustum;
  1454. MatrixTransformPoint(invProjViewPortMatrix, sbottomleft, bottomleft);
  1455. MatrixTransformPoint(invProjViewPortMatrix, sbottomright, bottomright);
  1456. MatrixTransformPoint(invProjViewPortMatrix, stopright, topright);
  1457. MatrixTransformPoint(invProjViewPortMatrix, stopleft, topleft);
  1458. // create frustum planes for the sides of the view space region
  1459. CrossProduct(bottomright, bottomleft, frustum[F

Large files files are truncated, but you can click here to view the full file