Frustum.cpp 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852
  1. /******************************************************************************/
  2. #include "stdafx.h"
  3. namespace EE{
  4. /******************************************************************************
  5. Order of planes is set based on performance tests.
  6. /******************************************************************************/
  7. FrustumClass Frustum ,
  8. FrustumMain ,
  9. FrustumGrass;
  10. /******************************************************************************/
  11. void FrustumClass::set(Flt range, C Vec2 &fov, C MatrixM &camera)
  12. {
  13. T.range =range;
  14. T.matrix=camera;
  15. // set planes
  16. if(persp=FovPerspective(D._view_active.fov_mode))
  17. {
  18. Vec2 fov_sin, fov_cos;
  19. if(VR.active() && D._allow_stereo)
  20. {
  21. eye=D.eyeDistance()*0.5f;
  22. fov_tan.y=D._view_active.fov_tan.y;
  23. fov_sin.y=D._view_active.fov_sin.y;
  24. fov_cos.y=D._view_active.fov_cos.y;
  25. // proj_center=desc.DefaultEyeFov[0].LeftTan/(desc.DefaultEyeFov[0].LeftTan+desc.DefaultEyeFov[0].RightTan);
  26. // fov_tan= 0.5f*(desc.DefaultEyeFov[0].LeftTan+desc.DefaultEyeFov[0].RightTan);
  27. // desc.DefaultEyeFov[0].LeftTan=proj_center*fov_tan*2;
  28. Flt proj_center=ProjMatrixEyeOffset[0]*0.5f+0.5f;
  29. fov_tan.x=proj_center*D._view_active.fov_tan.x*2; // outer tangent, this is correct, because when *0.95 multiplier was applied, then objects were culled near the edge of the screen
  30. CosSin(fov_cos.x, fov_sin.x, Atan(fov_tan.x));
  31. }else
  32. {
  33. eye=0;
  34. fov_tan=D._view_active.fov_tan;
  35. fov_sin=D._view_active.fov_sin;
  36. fov_cos=D._view_active.fov_cos;
  37. }
  38. fov_cos_inv=1.0f/fov_cos; // calculate inverse, because later we'll be able to use "*fov_cos_inv" instead of "/fov_cos", as multiplication is faster than division
  39. Vec view_quad_max(fov_tan.x*D._view_active.from, fov_tan.y*D._view_active.from, D._view_active.from);
  40. view_quad_max_dist=view_quad_max.length();
  41. plane[DIR_RIGHT ].normal.set( fov_cos.x, 0, -fov_sin.x); plane[DIR_RIGHT ].pos.set ( eye, 0, 0 ); // right
  42. plane[DIR_LEFT ].normal.set( -fov_cos.x, 0, -fov_sin.x); plane[DIR_LEFT ].pos.set (-eye, 0, 0 ); // left
  43. plane[DIR_UP ].normal.set(0, fov_cos.y, -fov_sin.y); plane[DIR_UP ].pos.zero( ); // up
  44. plane[DIR_DOWN ].normal.set(0, -fov_cos.y, -fov_sin.y); plane[DIR_DOWN ].pos.zero( ); // down
  45. plane[DIR_FORWARD].normal.set(0, 0, 1 ); plane[DIR_FORWARD].pos.set (0, 0, range); // front
  46. plane[DIR_BACK ].normal.set(0, 0, -1 ); plane[DIR_BACK ].pos.set (0, 0, D._view_active.from ); // back
  47. }else
  48. {
  49. view_quad_max_dist=0;
  50. eye=0;
  51. size.xy=fov;
  52. size.z = range*0.5f; // set as half because we're extending both ways
  53. matrix.pos+=matrix.z*size.z; // set 'pos' in the center, ortho mode was designed to always have the position in the center so we can use fast frustum culling for Z axis the same way as for XY axes
  54. plane[DIR_RIGHT ].normal.set( 1, 0, 0); plane[DIR_RIGHT ].pos.set( size.x, 0, 0); // right
  55. plane[DIR_LEFT ].normal.set(-1, 0, 0); plane[DIR_LEFT ].pos.set( -size.x, 0, 0); // left
  56. plane[DIR_UP ].normal.set( 0, 1, 0); plane[DIR_UP ].pos.set(0, size.y, 0 ); // up
  57. plane[DIR_DOWN ].normal.set( 0,-1, 0); plane[DIR_DOWN ].pos.set(0, -size.y, 0 ); // down
  58. plane[DIR_FORWARD].normal.set( 0, 0, 1); plane[DIR_FORWARD].pos.set(0, 0, size.z ); // front
  59. plane[DIR_BACK ].normal.set( 0, 0,-1); plane[DIR_BACK ].pos.set(0, 0, -size.z ); // back
  60. }
  61. REPA(plane)
  62. {
  63. plane[i].pos *=matrix;
  64. plane[i].normal*=matrix.orn();
  65. plane_n_abs[i] =Abs(plane[i].normal);
  66. }
  67. use_extra_plane=false;
  68. if(Renderer.mirror())
  69. {
  70. use_extra_plane=true;
  71. extra_plane=Renderer._mirror_plane;
  72. extra_plane.normal.chs();
  73. extra_plane_n_abs=Abs(extra_plane.normal);
  74. }/*else
  75. if(Water.draw_plane_surface)
  76. {
  77. Flt density=Water.density+Water.density_add;
  78. if( density>0)
  79. {
  80. planes++;
  81. plane[6]=Water.plane;
  82. plane[6].normal.chs();
  83. if(density<1)
  84. {
  85. // underwater opacity = Sat(pow(1-WaterDns, x)-WaterDnsAdd);
  86. // pow(1-WaterDns, x)-WaterDnsAdd = eps
  87. // pow(1-WaterDns, x) = eps+WaterDnsAdd
  88. // a**c=b <=> loga(b)=c
  89. // (1-WaterDns)**x=eps+WaterDnsAdd <=> log 1-WaterDns (eps+WaterDnsAdd)=x
  90. plane[6]+=plane[6].normal*Max(0,Log(0.015f+Water.density_add,1-Water.density));
  91. }
  92. plane[6]+=plane[6].normal*Water.wave_scale;
  93. }
  94. }*/
  95. // points and edges
  96. if(persp)
  97. {
  98. // points
  99. {
  100. Flt z=range,
  101. x=fov_tan.x*z,
  102. y=fov_tan.y*z;
  103. point[0]=matrix.pos;
  104. // set in clock-wise order, do not change the order as it is used in 'ShadowMap'
  105. point[1].set(-x, y, z);
  106. point[2].set( x, y, z);
  107. point[3].set( x, -y, z);
  108. point[4].set(-x, -y, z);
  109. Transform(point+1, matrix, 4);
  110. }
  111. // edges
  112. {
  113. edge[0].set(0, 1);
  114. edge[1].set(0, 2);
  115. edge[2].set(0, 3);
  116. edge[3].set(0, 4);
  117. edge[4].set(1, 2);
  118. edge[5].set(2, 3);
  119. edge[6].set(3, 4);
  120. edge[7].set(4, 1);
  121. }
  122. points=5;
  123. edges =8;
  124. }else
  125. {
  126. // points
  127. {
  128. Flt x=size.x,
  129. y=size.y,
  130. z=size.z;
  131. // set in clock-wise order, do not change the order as it is used in 'ShadowMap' and 'ConnectedPoints'
  132. point[0].set(-x, y,-z);
  133. point[1].set( x, y,-z);
  134. point[2].set( x,-y,-z);
  135. point[3].set(-x,-y,-z);
  136. point[4].set(-x, y, z);
  137. point[5].set( x, y, z);
  138. point[6].set( x,-y, z);
  139. point[7].set(-x,-y, z);
  140. Transform(point, matrix, 8);
  141. }
  142. // edges
  143. {
  144. edge[ 0].set(0, 4);
  145. edge[ 1].set(1, 5);
  146. edge[ 2].set(2, 6);
  147. edge[ 3].set(3, 7);
  148. edge[ 4].set(0, 1);
  149. edge[ 5].set(1, 2);
  150. edge[ 6].set(2, 3);
  151. edge[ 7].set(3, 0);
  152. edge[ 8].set(4, 5);
  153. edge[ 9].set(5, 6);
  154. edge[10].set(6, 7);
  155. edge[11].set(7, 4);
  156. }
  157. points=8;
  158. edges =12;
  159. }
  160. }
  161. void FrustumClass::set()
  162. {
  163. set(D._view_active.range, D._view_active.fov, CamMatrix);
  164. if(Renderer()!=RM_SHADOW)
  165. {
  166. FrustumMain=T;
  167. FrustumGrass.set(Min(D._view_active.range, D.grassRange()), D._view_active.fov, CamMatrix);
  168. }
  169. }
  170. /******************************************************************************/
  171. void FrustumClass::from(C BoxD &box)
  172. {
  173. // set planes
  174. plane[DIR_RIGHT ].normal.set( 1, 0, 0); plane[DIR_RIGHT ].pos.set(box.max.x, 0, 0); // right
  175. plane[DIR_LEFT ].normal.set(-1, 0, 0); plane[DIR_LEFT ].pos.set(box.min.x, 0, 0); // left
  176. plane[DIR_UP ].normal.set( 0, 1, 0); plane[DIR_UP ].pos.set(0, box.max.y, 0); // up
  177. plane[DIR_DOWN ].normal.set( 0,-1, 0); plane[DIR_DOWN ].pos.set(0, box.min.y, 0); // down
  178. plane[DIR_FORWARD].normal.set( 0, 0, 1); plane[DIR_FORWARD].pos.set(0, 0, box.max.z); // front
  179. plane[DIR_BACK ].normal.set( 0, 0,-1); plane[DIR_BACK ].pos.set(0, 0, box.min.z); // back
  180. // set helpers
  181. use_extra_plane=false;
  182. view_quad_max_dist=0;
  183. eye =0;
  184. persp =false;
  185. size =box.size()*0.5f;
  186. matrix.setPos(box.center());
  187. REPA(plane)plane_n_abs[i]=Abs(plane[i].normal);
  188. // points
  189. {
  190. point[0].set(box.min.x, box.max.y, box.min.z);
  191. point[1].set(box.max.x, box.max.y, box.min.z);
  192. point[2].set(box.max.x, box.min.y, box.min.z);
  193. point[3].set(box.min.x, box.min.y, box.min.z);
  194. point[4].set(box.min.x, box.max.y, box.max.z);
  195. point[5].set(box.max.x, box.max.y, box.max.z);
  196. point[6].set(box.max.x, box.min.y, box.max.z);
  197. point[7].set(box.min.x, box.min.y, box.max.z);
  198. }
  199. // edges
  200. {
  201. edge[ 0].set(0, 4);
  202. edge[ 1].set(1, 5);
  203. edge[ 2].set(2, 6);
  204. edge[ 3].set(3, 7);
  205. edge[ 4].set(0, 1);
  206. edge[ 5].set(1, 2);
  207. edge[ 6].set(2, 3);
  208. edge[ 7].set(3, 0);
  209. edge[ 8].set(4, 5);
  210. edge[ 9].set(5, 6);
  211. edge[10].set(6, 7);
  212. edge[11].set(7, 4);
  213. }
  214. points=8;
  215. edges =12;
  216. // this method is called only inside 'Light.drawForward', we don't set here FrustumMain
  217. }
  218. /******************************************************************************/
  219. void FrustumClass::from(C PyramidM &pyramid)
  220. {
  221. // set planes
  222. Vec2 d(pyramid.scale, 1); d.normalize();
  223. /*plane[DIR_RIGHT ].normal.set( D._view_active.fov_cos.x, 0, -D._view_active.fov_sin.x); plane[DIR_RIGHT ].pos.zero( ); // right
  224. plane[DIR_LEFT ].normal.set( -D._view_active.fov_cos.x, 0, -D._view_active.fov_sin.x); plane[DIR_LEFT ].pos.zero( ); // left
  225. plane[DIR_UP ].normal.set(0, D._view_active.fov_cos.y, -D._view_active.fov_sin.y); plane[DIR_UP ].pos.zero( ); // up
  226. plane[DIR_DOWN ].normal.set(0, -D._view_active.fov_cos.y, -D._view_active.fov_sin.y); plane[DIR_DOWN ].pos.zero( ); // down
  227. plane[DIR_FORWARD].normal.set(0, 0, 1 ); plane[DIR_FORWARD].pos.set (0, 0, D._view_active.range); // front
  228. plane[DIR_BACK ].normal.set(0, 0, -1 ); plane[DIR_BACK ].pos.set (0, 0, D._view_active.from ); // back*/
  229. plane[DIR_FORWARD].normal= pyramid.dir; plane[DIR_FORWARD].pos=pyramid.pos+pyramid.dir*pyramid.h; // front
  230. plane[DIR_BACK ].normal=-pyramid.dir; plane[DIR_BACK ].pos=pyramid.pos ; // back
  231. plane[DIR_RIGHT].normal.set( d.y, 0, -d.x); plane[DIR_RIGHT].pos.zero(); // right
  232. plane[DIR_LEFT ].normal.set( -d.y, 0, -d.x); plane[DIR_LEFT ].pos.zero(); // left
  233. plane[DIR_UP ].normal.set(0, d.y, -d.x); plane[DIR_UP ].pos.zero(); // up
  234. plane[DIR_DOWN ].normal.set(0, -d.y, -d.x); plane[DIR_DOWN ].pos.zero(); // down
  235. matrix=pyramid;
  236. DIR_ENUM dir[]={DIR_RIGHT, DIR_LEFT, DIR_UP, DIR_DOWN};
  237. REPA(dir)
  238. {
  239. plane[dir[i]].pos *=matrix;
  240. plane[dir[i]].normal*=matrix.orn();
  241. }
  242. // set helpers
  243. eye=0;
  244. view_quad_max_dist=0;
  245. use_extra_plane=false;
  246. persp =true;
  247. fov_tan=pyramid.scale;
  248. Vec2 fov_cos=d.y; fov_cos_inv=1.0f/fov_cos;
  249. range =pyramid.h;
  250. REPA(plane)plane_n_abs[i]=Abs(plane[i].normal);
  251. // points
  252. {
  253. point[0]=matrix.pos;
  254. point[1]=matrix.pos+(matrix.z+(-matrix.x+matrix.y)*pyramid.scale)*pyramid.h;
  255. point[2]=matrix.pos+(matrix.z+( matrix.x+matrix.y)*pyramid.scale)*pyramid.h;
  256. point[3]=matrix.pos+(matrix.z+( matrix.x-matrix.y)*pyramid.scale)*pyramid.h;
  257. point[4]=matrix.pos+(matrix.z+(-matrix.x-matrix.y)*pyramid.scale)*pyramid.h;
  258. }
  259. // edges
  260. {
  261. edge[0].set(0, 1);
  262. edge[1].set(0, 2);
  263. edge[2].set(0, 3);
  264. edge[3].set(0, 4);
  265. edge[4].set(1, 2);
  266. edge[5].set(2, 3);
  267. edge[6].set(3, 4);
  268. edge[7].set(4, 1);
  269. }
  270. points=5;
  271. edges =8;
  272. // this method is called only inside 'Light.drawForward', we don't set here FrustumMain
  273. }
  274. /******************************************************************************/
  275. Bool FrustumClass::operator()(C Vec &point)C
  276. {
  277. Vec pos=point-matrix.pos; // no need for 'VecD'
  278. if(persp)
  279. {
  280. Flt z=Dot(pos, matrix.z); if(z<0 || z>range)return false;
  281. Flt x=Dot(pos, matrix.x), bx=fov_tan.x*z+eye; if(Abs(x)>bx)return false;
  282. Flt y=Dot(pos, matrix.y), by=fov_tan.y*z ; if(Abs(y)>by)return false;
  283. if(use_extra_plane && Dist(point, extra_plane)>0)return false;
  284. }else
  285. {
  286. Flt y=Dot(pos, matrix.y), by=size.y; if(Abs(y)>by)return false;
  287. Flt x=Dot(pos, matrix.x), bx=size.x; if(Abs(x)>bx)return false;
  288. Flt z=Dot(pos, matrix.z), bz=size.z; if(Abs(z)>bz)return false;
  289. }
  290. return true;
  291. }
  292. Bool FrustumClass::operator()(C VecD &point)C
  293. {
  294. Vec pos=point-matrix.pos; // no need for 'VecD'
  295. if(persp)
  296. {
  297. Flt z=Dot(pos, matrix.z); if(z<0 || z>range)return false;
  298. Flt x=Dot(pos, matrix.x), bx=fov_tan.x*z+eye; if(Abs(x)>bx)return false;
  299. Flt y=Dot(pos, matrix.y), by=fov_tan.y*z ; if(Abs(y)>by)return false;
  300. if(use_extra_plane && Dist(point, extra_plane)>0)return false;
  301. }else
  302. {
  303. Flt y=Dot(pos, matrix.y), by=size.y; if(Abs(y)>by)return false;
  304. Flt x=Dot(pos, matrix.x), bx=size.x; if(Abs(x)>bx)return false;
  305. Flt z=Dot(pos, matrix.z), bz=size.z; if(Abs(z)>bz)return false;
  306. }
  307. return true;
  308. }
  309. /******************************************************************************/
  310. Bool FrustumClass::operator()(C Ball &ball)C
  311. {
  312. Vec pos=ball.pos-matrix.pos; // no need for 'VecD'
  313. if(persp)
  314. {
  315. Flt z=Dot(pos, matrix.z); if(z<-ball.r || z>range+ball.r)return false; MAX(z, 0);
  316. Flt x=Dot(pos, matrix.x), bx=fov_tan.x*z+ball.r*fov_cos_inv.x+eye; if(Abs(x)>bx)return false;
  317. Flt y=Dot(pos, matrix.y), by=fov_tan.y*z+ball.r*fov_cos_inv.y ; if(Abs(y)>by)return false;
  318. if(use_extra_plane && Dist(ball, extra_plane)>0)return false;
  319. }else
  320. {
  321. Flt y=Dot(pos, matrix.y), by=size.y+ball.r; if(Abs(y)>by)return false;
  322. Flt x=Dot(pos, matrix.x), bx=size.x+ball.r; if(Abs(x)>bx)return false;
  323. Flt z=Dot(pos, matrix.z), bz=size.z+ball.r; if(Abs(z)>bz)return false;
  324. }
  325. return true;
  326. }
  327. Bool FrustumClass::operator()(C BallM &ball)C
  328. {
  329. Vec pos=ball.pos-matrix.pos; // no need for 'VecD'
  330. if(persp)
  331. {
  332. Flt z=Dot(pos, matrix.z); if(z<-ball.r || z>range+ball.r)return false; MAX(z, 0);
  333. Flt x=Dot(pos, matrix.x), bx=fov_tan.x*z+ball.r*fov_cos_inv.x+eye; if(Abs(x)>bx)return false;
  334. Flt y=Dot(pos, matrix.y), by=fov_tan.y*z+ball.r*fov_cos_inv.y ; if(Abs(y)>by)return false;
  335. if(use_extra_plane && Dist(ball, extra_plane)>0)return false;
  336. }else
  337. {
  338. Flt y=Dot(pos, matrix.y), by=size.y+ball.r; if(Abs(y)>by)return false;
  339. Flt x=Dot(pos, matrix.x), bx=size.x+ball.r; if(Abs(x)>bx)return false;
  340. Flt z=Dot(pos, matrix.z), bz=size.z+ball.r; if(Abs(z)>bz)return false;
  341. }
  342. return true;
  343. }
  344. Bool FrustumClass::operator()(C Capsule &capsule)C
  345. {
  346. Vec up =capsule.pos-matrix.pos, // no need for 'VecD'
  347. down=up,
  348. d =capsule.up*(capsule.h*0.5f-capsule.r);
  349. up +=d;
  350. down-=d;
  351. if(persp)
  352. {
  353. Flt zu=Dot(up, matrix.z), zd=Dot(down, matrix.z); if((zu<-capsule.r && zd<-capsule.r) || (zu>range+capsule.r && zd>range+capsule.r))return false; MAX(zu, 0); MAX(zd, 0);
  354. Flt xu=Dot(up, matrix.x), xd=Dot(down, matrix.x), bx=capsule.r*fov_cos_inv.x+eye; if(Abs(xu)>bx+fov_tan.x*zu && Abs(xd)>bx+fov_tan.x*zd)return false;
  355. Flt yu=Dot(up, matrix.y), yd=Dot(down, matrix.y), by=capsule.r*fov_cos_inv.y ; if(Abs(yu)>by+fov_tan.y*zu && Abs(yd)>by+fov_tan.y*zd)return false;
  356. if(use_extra_plane && Dist(capsule, extra_plane)>0)return false;
  357. }else
  358. {
  359. Flt yu=Dot(up, matrix.y), yd=Dot(down, matrix.y), by=size.y+capsule.r; if(Abs(yu)>by && Abs(yd)>by)return false;
  360. Flt xu=Dot(up, matrix.x), xd=Dot(down, matrix.x), bx=size.x+capsule.r; if(Abs(xu)>bx && Abs(xd)>bx)return false;
  361. Flt zu=Dot(up, matrix.z), zd=Dot(down, matrix.z), bz=size.z+capsule.r; if(Abs(zu)>bz && Abs(zd)>bz)return false;
  362. }
  363. return true;
  364. }
  365. Bool FrustumClass::operator()(C CapsuleM &capsule)C
  366. {
  367. Vec up =capsule.pos-matrix.pos, // no need for 'VecD'
  368. down=up,
  369. d =capsule.up*(capsule.h*0.5f-capsule.r);
  370. up +=d;
  371. down-=d;
  372. if(persp)
  373. {
  374. Flt zu=Dot(up, matrix.z), zd=Dot(down, matrix.z); if((zu<-capsule.r && zd<-capsule.r) || (zu>range+capsule.r && zd>range+capsule.r))return false; MAX(zu, 0); MAX(zd, 0);
  375. Flt xu=Dot(up, matrix.x), xd=Dot(down, matrix.x), bx=capsule.r*fov_cos_inv.x+eye; if(Abs(xu)>bx+fov_tan.x*zu && Abs(xd)>bx+fov_tan.x*zd)return false;
  376. Flt yu=Dot(up, matrix.y), yd=Dot(down, matrix.y), by=capsule.r*fov_cos_inv.y ; if(Abs(yu)>by+fov_tan.y*zu && Abs(yd)>by+fov_tan.y*zd)return false;
  377. if(use_extra_plane && Dist(capsule, extra_plane)>0)return false;
  378. }else
  379. {
  380. Flt yu=Dot(up, matrix.y), yd=Dot(down, matrix.y), by=size.y+capsule.r; if(Abs(yu)>by && Abs(yd)>by)return false;
  381. Flt xu=Dot(up, matrix.x), xd=Dot(down, matrix.x), bx=size.x+capsule.r; if(Abs(xu)>bx && Abs(xd)>bx)return false;
  382. Flt zu=Dot(up, matrix.z), zd=Dot(down, matrix.z), bz=size.z+capsule.r; if(Abs(zu)>bz && Abs(zd)>bz)return false;
  383. }
  384. return true;
  385. }
  386. /******************************************************************************/
  387. INLINE Flt BoxLength(C Vec &size, C Vec &dir) // box length along direction
  388. {
  389. return Abs(dir.x)*size.x
  390. + Abs(dir.y)*size.y
  391. + Abs(dir.z)*size.z;
  392. }
  393. INLINE Flt BoxLengthAbs(C Vec &size, C Vec &dir_abs) // box length along direction, assuming that direction has absolute components (non-negative)
  394. {
  395. return dir_abs.x*size.x
  396. + dir_abs.y*size.y
  397. + dir_abs.z*size.z;
  398. }
  399. INLINE Flt OBoxLength(C Vec &x, C Vec &y, C Vec &z, C Vec &dir) // obox length along direction
  400. {
  401. return Abs(Dot(x, dir))
  402. + Abs(Dot(y, dir))
  403. + Abs(Dot(z, dir));
  404. }
  405. Bool FrustumClass::operator()(C Extent &ext)C
  406. {
  407. Vec pos=ext.pos-matrix.pos; // no need for 'VecD'
  408. if(persp)
  409. {
  410. Flt z=Dot(pos, matrix.z);
  411. if( z<0 || z>range)
  412. {
  413. Flt bz=BoxLengthAbs(ext.ext, plane_n_abs[DIR_FORWARD]);
  414. if(z<-bz || z>range+bz)return false; // fb
  415. MAX(z, 0);
  416. }
  417. Flt x=Dot(pos, matrix.x), bx=fov_tan.x*z+eye;
  418. if(Abs(x)>bx)
  419. {
  420. if(x> bx + BoxLengthAbs(ext.ext, plane_n_abs[DIR_RIGHT])*fov_cos_inv.x)return false; // r
  421. if(x<-bx - BoxLengthAbs(ext.ext, plane_n_abs[DIR_LEFT ])*fov_cos_inv.x)return false; // l
  422. }
  423. Flt y=Dot(pos, matrix.y), by=fov_tan.y*z;
  424. if(Abs(y)>by)
  425. {
  426. if(y> by + BoxLengthAbs(ext.ext, plane_n_abs[DIR_UP ])*fov_cos_inv.y)return false; // u
  427. if(y<-by - BoxLengthAbs(ext.ext, plane_n_abs[DIR_DOWN ])*fov_cos_inv.y)return false; // d
  428. }
  429. if(use_extra_plane)
  430. {
  431. Flt e=Dist(pos+matrix.pos, extra_plane);
  432. if( e>0)if(e>BoxLengthAbs(ext.ext, extra_plane_n_abs))return false;
  433. }
  434. }else
  435. {
  436. Flt y=Abs(Dot(pos, matrix.y)), by=T.size.y;
  437. if( y>by)if(y>by+BoxLengthAbs(ext.ext, plane_n_abs[DIR_UP ]))return false; // ud
  438. Flt x=Abs(Dot(pos, matrix.x)), bx=T.size.x;
  439. if( x>bx)if(x>bx+BoxLengthAbs(ext.ext, plane_n_abs[DIR_RIGHT ]))return false; // rl
  440. Flt z=Abs(Dot(pos, matrix.z)), bz=T.size.z;
  441. if( z>bz)if(z>bz+BoxLengthAbs(ext.ext, plane_n_abs[DIR_FORWARD]))return false; // fb
  442. }
  443. return true;
  444. }
  445. Bool FrustumClass::operator()(C Extent &ext, C Matrix &matrix)C
  446. {
  447. Vec dx =ext.ext.x*matrix.x,
  448. dy =ext.ext.y*matrix.y,
  449. dz =ext.ext.z*matrix.z,
  450. #if 0
  451. pos=ext.pos*matrix-T.matrix.pos; // no need for 'VecD'
  452. #else // faster than above
  453. pos=ext.pos; pos*=matrix; pos-=T.matrix.pos; // #VecMulMatrix
  454. #endif
  455. if(persp)
  456. {
  457. Flt z=Dot(pos, T.matrix.z);
  458. if( z<0 || z>range)
  459. {
  460. Flt bz=OBoxLength(dx, dy, dz, plane[DIR_FORWARD].normal);
  461. if(z<-bz || z>range+bz)return false; // fb
  462. MAX(z, 0);
  463. }
  464. Flt x=Dot(pos, T.matrix.x), bx=fov_tan.x*z+eye;
  465. if(Abs(x)>bx)
  466. {
  467. if(x> bx + OBoxLength(dx, dy, dz, plane[DIR_RIGHT].normal)*fov_cos_inv.x)return false; // r
  468. if(x<-bx - OBoxLength(dx, dy, dz, plane[DIR_LEFT ].normal)*fov_cos_inv.x)return false; // l
  469. }
  470. Flt y=Dot(pos, T.matrix.y), by=fov_tan.y*z;
  471. if(Abs(y)>by)
  472. {
  473. if(y> by + OBoxLength(dx, dy, dz, plane[DIR_UP ].normal)*fov_cos_inv.y)return false; // u
  474. if(y<-by - OBoxLength(dx, dy, dz, plane[DIR_DOWN ].normal)*fov_cos_inv.y)return false; // d
  475. }
  476. if(use_extra_plane)
  477. {
  478. Flt e=Dist(pos+T.matrix.pos, extra_plane);
  479. if( e>0)if(e>OBoxLength(dx, dy, dz, extra_plane.normal))return false;
  480. }
  481. }else
  482. {
  483. Flt y=Abs(Dot(pos, T.matrix.y)), by=T.size.y;
  484. if( y>by)if(y>by+OBoxLength(dx, dy, dz, plane[DIR_UP ].normal))return false; // ud
  485. Flt x=Abs(Dot(pos, T.matrix.x)), bx=T.size.x;
  486. if( x>bx)if(x>bx+OBoxLength(dx, dy, dz, plane[DIR_RIGHT ].normal))return false; // rl
  487. Flt z=Abs(Dot(pos, T.matrix.z)), bz=T.size.z;
  488. if( z>bz)if(z>bz+OBoxLength(dx, dy, dz, plane[DIR_FORWARD].normal))return false; // fb
  489. }
  490. return true;
  491. }
  492. Bool FrustumClass::operator()(C Extent &ext, C MatrixM &matrix)C
  493. {
  494. Vec dx =ext.ext.x*matrix.x,
  495. dy =ext.ext.y*matrix.y,
  496. dz =ext.ext.z*matrix.z,
  497. pos=ext.pos *matrix-T.matrix.pos; // no need for 'VecD' if all computations done before setting to 'Vec'
  498. if(persp)
  499. {
  500. Flt z=Dot(pos, T.matrix.z);
  501. if( z<0 || z>range)
  502. {
  503. Flt bz=OBoxLength(dx, dy, dz, plane[DIR_FORWARD].normal);
  504. if(z<-bz || z>range+bz)return false; // fb
  505. MAX(z, 0);
  506. }
  507. Flt x=Dot(pos, T.matrix.x), bx=fov_tan.x*z+eye;
  508. if(Abs(x)>bx)
  509. {
  510. if(x> bx + OBoxLength(dx, dy, dz, plane[DIR_RIGHT].normal)*fov_cos_inv.x)return false; // r
  511. if(x<-bx - OBoxLength(dx, dy, dz, plane[DIR_LEFT ].normal)*fov_cos_inv.x)return false; // l
  512. }
  513. Flt y=Dot(pos, T.matrix.y), by=fov_tan.y*z;
  514. if(Abs(y)>by)
  515. {
  516. if(y> by + OBoxLength(dx, dy, dz, plane[DIR_UP ].normal)*fov_cos_inv.y)return false; // u
  517. if(y<-by - OBoxLength(dx, dy, dz, plane[DIR_DOWN ].normal)*fov_cos_inv.y)return false; // d
  518. }
  519. if(use_extra_plane)
  520. {
  521. Flt e=Dist(pos+T.matrix.pos, extra_plane);
  522. if( e>0)if(e>OBoxLength(dx, dy, dz, extra_plane.normal))return false;
  523. }
  524. }else
  525. {
  526. Flt y=Abs(Dot(pos, T.matrix.y)), by=T.size.y;
  527. if( y>by)if(y>by+OBoxLength(dx, dy, dz, plane[DIR_UP ].normal))return false; // ud
  528. Flt x=Abs(Dot(pos, T.matrix.x)), bx=T.size.x;
  529. if( x>bx)if(x>bx+OBoxLength(dx, dy, dz, plane[DIR_RIGHT ].normal))return false; // rl
  530. Flt z=Abs(Dot(pos, T.matrix.z)), bz=T.size.z;
  531. if( z>bz)if(z>bz+OBoxLength(dx, dy, dz, plane[DIR_FORWARD].normal))return false; // fb
  532. }
  533. return true;
  534. }
  535. Bool FrustumClass::operator()(C OBox &obox)C // here we assume that 'obox.matrix' can be scaled
  536. {
  537. Vec dx =(obox.box.w()*0.5f)*obox.matrix.x,
  538. dy =(obox.box.h()*0.5f)*obox.matrix.y,
  539. dz =(obox.box.d()*0.5f)*obox.matrix.z,
  540. #if 0
  541. pos= obox.center()-matrix.pos; // no need for 'VecD'
  542. #else // faster than above
  543. pos= obox.box.center(); pos*=obox.matrix; pos-=matrix.pos; // #VecMulMatrix
  544. #endif
  545. if(persp)
  546. {
  547. Flt z=Dot(pos, matrix.z);
  548. if( z<0 || z>range)
  549. {
  550. Flt bz=OBoxLength(dx, dy, dz, plane[DIR_FORWARD].normal);
  551. if(z<-bz || z>range+bz)return false; // fb
  552. MAX(z, 0);
  553. }
  554. Flt x=Dot(pos, matrix.x), bx=fov_tan.x*z+eye;
  555. if(Abs(x)>bx)
  556. {
  557. if(x> bx + OBoxLength(dx, dy, dz, plane[DIR_RIGHT].normal)*fov_cos_inv.x)return false; // r
  558. if(x<-bx - OBoxLength(dx, dy, dz, plane[DIR_LEFT ].normal)*fov_cos_inv.x)return false; // l
  559. }
  560. Flt y=Dot(pos, matrix.y), by=fov_tan.y*z;
  561. if(Abs(y)>by)
  562. {
  563. if(y> by + OBoxLength(dx, dy, dz, plane[DIR_UP ].normal)*fov_cos_inv.y)return false; // u
  564. if(y<-by - OBoxLength(dx, dy, dz, plane[DIR_DOWN ].normal)*fov_cos_inv.y)return false; // d
  565. }
  566. if(use_extra_plane)
  567. {
  568. Flt e=Dist(pos+matrix.pos, extra_plane);
  569. if( e>0)if(e>OBoxLength(dx, dy, dz, extra_plane.normal))return false;
  570. }
  571. }else
  572. {
  573. Flt y=Abs(Dot(pos, matrix.y)), by=T.size.y;
  574. if( y>by)if(y>by+OBoxLength(dx, dy, dz, plane[DIR_UP ].normal))return false; // ud
  575. Flt x=Abs(Dot(pos, matrix.x)), bx=T.size.x;
  576. if( x>bx)if(x>bx+OBoxLength(dx, dy, dz, plane[DIR_RIGHT ].normal))return false; // rl
  577. Flt z=Abs(Dot(pos, matrix.z)), bz=T.size.z;
  578. if( z>bz)if(z>bz+OBoxLength(dx, dy, dz, plane[DIR_FORWARD].normal))return false; // fb
  579. }
  580. return true;
  581. }
  582. /******************************************************************************/
  583. Bool FrustumClass::operator()(C Extent &ext, Bool &fully_inside)C
  584. {
  585. fully_inside=true;
  586. Vec pos=ext.pos-matrix.pos; // no need for 'VecD'
  587. if(persp)
  588. {
  589. Flt z=Dot(pos, matrix.z);
  590. //if( z<0 || z>range)
  591. {
  592. Flt bz=BoxLengthAbs(ext.ext, plane_n_abs[DIR_FORWARD]);
  593. if(z< bz){if(z< -bz)return false; fully_inside=false;} // b
  594. if(z>range-bz){if(z>range+bz)return false; fully_inside=false;} // f
  595. MAX(z, 0);
  596. }
  597. Flt x=Dot(pos, matrix.x), bx=fov_tan.x*z+eye;
  598. //if(Abs(x)>bx)
  599. {
  600. Flt bxr=BoxLengthAbs(ext.ext, plane_n_abs[DIR_RIGHT])*fov_cos_inv.x; if(x> bx-bxr){if(x> bx+bxr)return false; fully_inside=false;} // r
  601. Flt bxl=BoxLengthAbs(ext.ext, plane_n_abs[DIR_LEFT ])*fov_cos_inv.x; if(x<-bx+bxl){if(x<-bx-bxl)return false; fully_inside=false;} // l
  602. }
  603. Flt y=Dot(pos, matrix.y), by=fov_tan.y*z;
  604. //if(Abs(y)>by)
  605. {
  606. Flt bxu=BoxLengthAbs(ext.ext, plane_n_abs[DIR_UP ])*fov_cos_inv.y; if(y> by-bxu){if(y> by+bxu)return false; fully_inside=false;} // u
  607. Flt bxd=BoxLengthAbs(ext.ext, plane_n_abs[DIR_DOWN ])*fov_cos_inv.y; if(y<-by+bxd){if(y<-by-bxd)return false; fully_inside=false;} // d
  608. }
  609. if(use_extra_plane)
  610. {
  611. Flt e=Dist(pos+matrix.pos, extra_plane), be=BoxLengthAbs(ext.ext, extra_plane_n_abs); if(e>-be){if(e>be)return false; fully_inside=false;}
  612. }
  613. }else
  614. {
  615. Flt y=Abs(Dot(pos, matrix.y))-T.size.y, by=BoxLengthAbs(ext.ext, plane_n_abs[DIR_UP ]); if(y>-by){if(y>by)return false; fully_inside=false;} // ud
  616. Flt x=Abs(Dot(pos, matrix.x))-T.size.x, bx=BoxLengthAbs(ext.ext, plane_n_abs[DIR_RIGHT ]); if(x>-bx){if(x>bx)return false; fully_inside=false;} // rl
  617. Flt z=Abs(Dot(pos, matrix.z))-T.size.z, bz=BoxLengthAbs(ext.ext, plane_n_abs[DIR_FORWARD]); if(z>-bz){if(z>bz)return false; fully_inside=false;} // fb
  618. }
  619. return true;
  620. }
  621. /******************************************************************************/
  622. Bool FrustumClass::operator()(C Box &box )C {return T(Extent(box) );}
  623. Bool FrustumClass::operator()(C Box &box, C Matrix &matrix )C {return T(Extent(box), matrix );}
  624. Bool FrustumClass::operator()(C Box &box, C MatrixM &matrix )C {return T(Extent(box), matrix );}
  625. Bool FrustumClass::operator()(C Box &box, Bool &fully_inside)C {return T(Extent(box), fully_inside);}
  626. /******************************************************************************/
  627. Bool FrustumClass::operator()(C Shape &shape)C
  628. {
  629. switch(shape.type)
  630. {
  631. case SHAPE_POINT : return T(shape.point );
  632. case SHAPE_BOX : return T(shape.box );
  633. case SHAPE_OBOX : return T(shape.obox );
  634. case SHAPE_BALL : return T(shape.ball );
  635. case SHAPE_CAPSULE: return T(shape.capsule);
  636. }
  637. return false;
  638. }
  639. Bool FrustumClass::operator()(C Shape *shape, Int shapes)C
  640. {
  641. REP(shapes)if(T(shape[i]))return true;
  642. return false;
  643. }
  644. /******************************************************************************/
  645. Bool FrustumClass::operator()(C FrustumClass &frustum)C // assumes that one frustum is not entirely inside the other frustum (which means that there is contact between edge and faces)
  646. {
  647. // comparing edges with faces is more precise than comparing points only
  648. REPD(f, 2)
  649. {
  650. C FrustumClass &a=(f ? T : frustum),
  651. &b=(f ? frustum : T);
  652. REPD(e, a.edges)
  653. {
  654. VecD contact;
  655. EdgeD ed(a.point[a.edge[e].x], a.point[a.edge[e].y]);
  656. REPA(b.plane)if(Cuts(ed, b.plane[i], &contact))if(b(contact-b.plane[i].normal*EPSL))return true; // use epsilon to make sure that we're under plane surface
  657. }
  658. }
  659. return false;
  660. }
  661. /******************************************************************************/
  662. static INLINE void ProcessPos(C VecI2 &pos, C RectI &rect, Memt<VecI2> &row_min_max_x)
  663. {
  664. if(rect.includesY(pos.y))
  665. {
  666. VecI2 &min_max_x=row_min_max_x[pos.y-rect.min.y];
  667. if(min_max_x.y<min_max_x.x)min_max_x=pos.x;else // if invalid (not yet set) then set,
  668. { // extend
  669. if(pos.x<min_max_x.x)min_max_x.x=pos.x;else
  670. if(pos.x>min_max_x.y)min_max_x.y=pos.x;
  671. }
  672. }
  673. }
  674. void FrustumClass::getIntersectingAreas(MemPtr<VecI2> area_pos, Flt area_size, Bool distance_check, Bool sort_by_distance, Bool extend, C RectI *clamp)C
  675. {
  676. area_pos.clear();
  677. Memt<VecD2> convex_points; CreateConvex2Dxz(convex_points, point, points); if(!convex_points.elms())return;
  678. RectI rect; // inclusive
  679. Bool is;
  680. // set min_y..max_y visibility
  681. is=false; REPA(convex_points)
  682. {
  683. VecD2 &p=convex_points[i]; p/=area_size;
  684. Int y=Floor(extend ? p.y-0.5f : p.y);
  685. if(is)rect.includeY(y);else{rect.setY(y); is=true;}
  686. }
  687. if(extend)rect.max.y++;
  688. if(clamp )rect&=*clamp;
  689. if(!rect.validY())return;
  690. // set min_x..max_x per row in 'row_min_max_x'
  691. Memt<VecI2> row_min_max_x; row_min_max_x.setNum(rect.h()+1); // +1 because it's inclusive
  692. REPAO(row_min_max_x).set(0, -1); // on start set invalid range ("max<min")
  693. REPA(convex_points) // warning: this needs to work as well for "convex_points.elms()==1"
  694. {
  695. Vec2 start=convex_points[i], end=convex_points[(i+1)%convex_points.elms()];
  696. if(extend)
  697. {
  698. // add corner point first as a 2x2 block (needs to process 2x2 because just 1 corner didn't cover all areas, the same was for Avg of 2 Perps)
  699. RectI corner; corner.min=Floor(start); VecI2 pos;
  700. if(start.x-corner.min.x<0.5f)corner.max.x=corner.min.x--;else corner.max.x=corner.min.x+1; // if point is on the left side (frac<0.5), then process from pos-1..pos, otherwise from pos..pos+1
  701. if(start.y-corner.min.y<0.5f)corner.max.y=corner.min.y--;else corner.max.y=corner.min.y+1; // if point is on the bottom side (frac<0.5), then process from pos-1..pos, otherwise from pos..pos+1
  702. for(pos.y=corner.min.y; pos.y<=corner.max.y; pos.y++)
  703. for(pos.x=corner.min.x; pos.x<=corner.max.x; pos.x++)ProcessPos(pos, rect, row_min_max_x);
  704. Vec2 perp=Perp(start-end); if(Flt max=Abs(perp).max()){perp*=0.5f/max; start+=perp; end+=perp;} // use "Abs(perp).max()" instead of "perp.length()" because we need to extend orthogonally (because we're using extend for the purpose of detecting objects from neighborhood areas that extend over to other areas, and this extend is allowed orthogonally)
  705. }
  706. for(PixelWalker walker(start, end); walker.active(); walker.step())ProcessPos(walker.pos(), rect, row_min_max_x);
  707. }
  708. // set min_x..max_x visibility (this is more precise than what can be calculated from just 'convex_points', because here, we've clipped the edges to min_y..max_y range)
  709. is=false; REPA(row_min_max_x)
  710. {
  711. VecI2 &min_max_x=row_min_max_x[i];
  712. if(min_max_x.y>=min_max_x.x) // if valid
  713. {
  714. if(is)rect.includeX(min_max_x.x, min_max_x.y);else{rect.setX(min_max_x.x, min_max_x.y); is=true;}
  715. }
  716. }
  717. if(clamp)rect&=*clamp;
  718. if(!is || !rect.validX())return;
  719. const Bool fast=true; // if use ~2x faster 'Dist2PointSquare' instead of 'Dist2(Vec2 point, RectI rect)'
  720. Vec2 distance_pos;
  721. Flt distance_range2;
  722. if( distance_check&=persp) // can do range tests only in perspective mode (orthogonal mode is used for shadows, and there we need full range, and also in that mode 'matrix.pos' is in the center, so it can't be used as 'distance_pos')
  723. { // convert to area space
  724. distance_pos =matrix.pos.xz()/area_size; if(fast )distance_pos -=0.5f; // since in fast mode we're testing against a square of radius 0.5, instead of setting each square as "pos+0.5f", we offset the 'distance_pos' by the negative (this was tested and works OK - the same results as when 'fast'=false)
  725. distance_range2= range/area_size; if(extend)distance_range2+=0.5f; distance_range2*=distance_range2;
  726. }
  727. // set areas for drawing
  728. if(sort_by_distance) // in look order (from camera/foreground to background)
  729. {
  730. Vec2 look_dir=matrix.z.xz();
  731. Flt max =Abs(look_dir).max();
  732. VecI2 dir =(max ? Round(look_dir/max) : VecI2(0, 1)), // (-1, -1) .. (1, 1)
  733. perp =Perp(dir); // parallel to direction
  734. if((dir.x== 1 && dir.y== 1)
  735. || (dir.x== 1 && dir.y== 0)
  736. || (dir.x==-1 && dir.y==-1)
  737. || (dir.x== 0 && dir.y==-1))perp.chs();
  738. for(VecI2 edge((dir.x<0) ? rect.max.x : rect.min.x, (dir.y<0) ? rect.max.y : rect.min.y); ; )
  739. {
  740. for(VecI2 pos=edge; ; )
  741. {
  742. VecI2 &min_max_x=row_min_max_x[pos.y-rect.min.y];
  743. if(pos.x>=min_max_x.x && pos.x<=min_max_x.y)
  744. if(!distance_check || (fast ? Dist2PointSquare(distance_pos, pos, 0.5f) : Dist2(distance_pos, RectI(pos, pos+1)))<=distance_range2)
  745. area_pos.add(pos); // add to array
  746. pos+=perp; if(!rect.includes(pos))break; // go along the parallel until you can't
  747. }
  748. if(dir.x && rect.includesX(edge.x+dir.x))edge.x+=dir.x;else // first travel on the x-edge until you can't
  749. if(dir.y && rect.includesY(edge.y+dir.y))edge.y+=dir.y;else break; // then travel on the y-edge until you can't, after that get out of the loop
  750. }
  751. }else
  752. {
  753. VecI2 pos; for(pos.y=rect.min.y; pos.y<=rect.max.y; pos.y++)
  754. {
  755. VecI2 min_max_x=row_min_max_x[pos.y-rect.min.y];
  756. MAX(min_max_x.x, rect.min.x);
  757. MIN(min_max_x.y, rect.max.x);
  758. for(pos.x=min_max_x.x; pos.x<=min_max_x.y; pos.x++)
  759. if(!distance_check || (fast ? Dist2PointSquare(distance_pos, pos, 0.5f) : Dist2(distance_pos, RectI(pos, pos+1)))<=distance_range2)
  760. area_pos.add(pos); // add to array
  761. }
  762. }
  763. }
  764. /******************************************************************************/
  765. void FrustumClass::draw(C Color &col)C
  766. {
  767. VI.color(col);
  768. REP(edges)VI.line (point[edge[i].x], point[edge[i].y]);
  769. VI.end ();
  770. //REPA(plane)DrawArrow(RED, plane[i].p, plane[i].p+plane[i].normal*3);
  771. }
  772. /******************************************************************************/
  773. }
  774. /******************************************************************************/