llcamera.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422
  1. /**
  2. * @file llcamera.cpp
  3. * @brief Implementation of the LLCamera class.
  4. *
  5. * $LicenseInfo:firstyear=2000&license=viewergpl$
  6. *
  7. * Copyright (c) 2000-2009, Linden Research, Inc.
  8. *
  9. * Second Life Viewer Source Code
  10. * The source code in this file ("Source Code") is provided by Linden Lab
  11. * to you under the terms of the GNU General Public License, version 2.0
  12. * ("GPL"), unless you have obtained a separate licensing agreement
  13. * ("Other License"), formally executed by you and Linden Lab. Terms of
  14. * the GPL can be found in doc/GPL-license.txt in this distribution, or
  15. * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
  16. *
  17. * There are special exceptions to the terms and conditions of the GPL as
  18. * it is applied to this Source Code. View the full text of the exception
  19. * in the file doc/FLOSS-exception.txt in this software distribution, or
  20. * online at
  21. * http://secondlifegrid.net/programs/open_source/licensing/flossexception
  22. *
  23. * By copying, modifying or distributing this software, you acknowledge
  24. * that you have read and understood your obligations described above,
  25. * and agree to abide by those obligations.
  26. *
  27. * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
  28. * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
  29. * COMPLETENESS OR PERFORMANCE.
  30. * $/LicenseInfo$
  31. */
  32. #include "linden_common.h"
  33. #include "llmath.h"
  34. #include "llcamera.h"
  35. LLCamera::LLCamera()
  36. : mView(DEFAULT_FIELD_OF_VIEW),
  37. mAspect(DEFAULT_ASPECT_RATIO),
  38. mViewHeightInPixels(-1), // Invalid height
  39. mNearPlane(DEFAULT_NEAR_PLANE),
  40. mFarPlane(DEFAULT_FAR_PLANE),
  41. mFixedDistance(-1.f),
  42. mPlaneCount(6),
  43. mFrustumCornerDist(0.f)
  44. {
  45. for (U32 i = 0; i < PLANE_MASK_NUM; ++i)
  46. {
  47. mPlaneMask[i] = PLANE_MASK_NONE;
  48. }
  49. calculateFrustumPlanes();
  50. }
  51. LLCamera::LLCamera(F32 vertical_fov_rads, F32 aspect_ratio,
  52. S32 view_height_in_pixels, F32 near_plane, F32 far_plane)
  53. : mViewHeightInPixels(view_height_in_pixels),
  54. mFixedDistance(-1.f),
  55. mPlaneCount(6),
  56. mFrustumCornerDist(0.f)
  57. {
  58. for (U32 i = 0; i < PLANE_MASK_NUM; ++i)
  59. {
  60. mPlaneMask[i] = PLANE_MASK_NONE;
  61. }
  62. mAspect = llclamp(aspect_ratio, MIN_ASPECT_RATIO, MAX_ASPECT_RATIO);
  63. mNearPlane = llclamp(near_plane, MIN_NEAR_PLANE, MAX_NEAR_PLANE);
  64. if (far_plane < 0.f)
  65. {
  66. far_plane = DEFAULT_FAR_PLANE;
  67. }
  68. mFarPlane = llclamp(far_plane, MIN_FAR_PLANE, MAX_FAR_PLANE);
  69. setView(vertical_fov_rads);
  70. }
  71. size_t LLCamera::writeFrustumToBuffer(char* buffer) const
  72. {
  73. memcpy(buffer, &mView, sizeof(F32));
  74. buffer += sizeof(F32);
  75. memcpy(buffer, &mAspect, sizeof(F32));
  76. buffer += sizeof(F32);
  77. memcpy(buffer, &mNearPlane, sizeof(F32));
  78. buffer += sizeof(F32);
  79. memcpy(buffer, &mFarPlane, sizeof(F32));
  80. return 4 * sizeof(F32);
  81. }
  82. size_t LLCamera::readFrustumFromBuffer(const char* buffer)
  83. {
  84. memcpy(&mView, buffer, sizeof(F32));
  85. buffer += sizeof(F32);
  86. memcpy(&mAspect, buffer, sizeof(F32));
  87. buffer += sizeof(F32);
  88. memcpy(&mNearPlane, buffer, sizeof(F32));
  89. buffer += sizeof(F32);
  90. memcpy(&mFarPlane, buffer, sizeof(F32));
  91. return 4 * sizeof(F32);
  92. }
  93. static const LLVector4a sFrustumScaler[] =
  94. {
  95. LLVector4a(-1.f, -1.f, -1.f),
  96. LLVector4a(1.f, -1.f, -1.f),
  97. LLVector4a(-1.f, 1.f, -1.f),
  98. LLVector4a(1.f, 1.f, -1.f),
  99. LLVector4a(-1.f, -1.f, 1.f),
  100. LLVector4a(1.f, -1.f, 1.f),
  101. LLVector4a(-1.f, 1.f, 1.f),
  102. LLVector4a(1.f, 1.f, 1.f)
  103. };
  104. bool LLCamera::isChanged()
  105. {
  106. bool changed = false;
  107. for (U32 i = 0; i < mPlaneCount; ++i)
  108. {
  109. U8 mask = mPlaneMask[i];
  110. if (mask != 0xff && !changed)
  111. {
  112. changed = !mAgentPlanes[i].equal(mLastAgentPlanes[i]);
  113. }
  114. mLastAgentPlanes[i].set(mAgentPlanes[i]);
  115. }
  116. return changed;
  117. }
  118. S32 LLCamera::AABBInFrustum(const LLVector4a& center, const LLVector4a& radius,
  119. const LLPlane* planes)
  120. {
  121. if (!planes)
  122. {
  123. // Use agent space
  124. planes = mAgentPlanes;
  125. }
  126. U8 mask = 0;
  127. bool result = false;
  128. LLVector4a rscale, maxp, minp;
  129. LLSimdScalar d;
  130. // mAgentPlanes[] size is 7
  131. U32 max_planes = llmin(mPlaneCount, (U32)AGENT_PLANE_USER_CLIP_NUM);
  132. for (U32 i = 0; i < max_planes; ++i)
  133. {
  134. mask = mPlaneMask[i];
  135. if (mask < PLANE_MASK_NUM)
  136. {
  137. const LLPlane& p(planes[i]);
  138. p.getAt<3>(d);
  139. rscale.setMul(radius, sFrustumScaler[mask]);
  140. minp.setSub(center, rscale);
  141. d = -d;
  142. if (p.dot3(minp).getF32() > d)
  143. {
  144. return 0;
  145. }
  146. if (!result)
  147. {
  148. maxp.setAdd(center, rscale);
  149. result = (p.dot3(maxp).getF32() > d);
  150. }
  151. }
  152. }
  153. return result ? 1 : 2;
  154. }
  155. S32 LLCamera::AABBInFrustumNoFarClip(const LLVector4a& center,
  156. const LLVector4a& radius,
  157. const LLPlane* planes)
  158. {
  159. if (!planes)
  160. {
  161. // Use agent space
  162. planes = mAgentPlanes;
  163. }
  164. U8 mask = 0;
  165. bool result = false;
  166. LLVector4a rscale, maxp, minp;
  167. LLSimdScalar d;
  168. // mAgentPlanes[] size is 7
  169. U32 max_planes = llmin(mPlaneCount, (U32)AGENT_PLANE_USER_CLIP_NUM);
  170. for (U32 i = 0; i < max_planes; ++i)
  171. {
  172. mask = mPlaneMask[i];
  173. if (i != 5 && mask < PLANE_MASK_NUM)
  174. {
  175. const LLPlane& p(planes[i]);
  176. p.getAt<3>(d);
  177. rscale.setMul(radius, sFrustumScaler[mask]);
  178. minp.setSub(center, rscale);
  179. d = -d;
  180. if (p.dot3(minp).getF32() > d)
  181. {
  182. return 0;
  183. }
  184. if (!result)
  185. {
  186. maxp.setAdd(center, rscale);
  187. result = p.dot3(maxp).getF32() > d;
  188. }
  189. }
  190. }
  191. return result ? 1 : 2;
  192. }
  193. S32 LLCamera::sphereInFrustumQuick(const LLVector3& sphere_center,
  194. F32 radius)
  195. {
  196. LLVector3 dist = sphere_center - mFrustCenter;
  197. F32 dsq = dist * dist;
  198. F32 rsq = mFarPlane * 0.5f + radius;
  199. rsq *= rsq;
  200. return dsq < rsq ? 1 : 0;
  201. }
  202. // Returns 1 if sphere is in frustum, 2 if fully in frustum, otherwise 0.
  203. // NOTE: 'center' is in absolute frame.
  204. S32 LLCamera::sphereInFrustum(const LLVector3& sphere_center,
  205. F32 radius) const
  206. {
  207. // Returns 1 if sphere is in frustum, 0 if not.
  208. bool res = false;
  209. for (S32 i = 0; i < 6; ++i)
  210. {
  211. if (mPlaneMask[i] != PLANE_MASK_NONE)
  212. {
  213. F32 d = mAgentPlanes[i].dist(sphere_center);
  214. if (d > radius)
  215. {
  216. return 0;
  217. }
  218. res = res || d > -radius;
  219. }
  220. }
  221. return res ? 1 : 2;
  222. }
  223. // Returns height of a sphere of given radius, located at center, in pixels
  224. F32 LLCamera::heightInPixels(const LLVector3& center, F32 radius) const
  225. {
  226. if (radius == 0.f)
  227. {
  228. return 0.f;
  229. }
  230. // If height initialized
  231. if (mViewHeightInPixels > -1)
  232. {
  233. // Convert sphere to coord system with 0,0,0 at camera
  234. LLVector3 vec = center - mOrigin;
  235. // Compute distance to sphere
  236. F32 dist = vec.length();
  237. // Calculate angle of whole object
  238. F32 angle = 2.f * atan2f(radius, dist);
  239. // Calculate fraction of field of view
  240. F32 fraction_of_fov = angle / mView;
  241. // Compute number of pixels tall, based on vertical field of view
  242. return (fraction_of_fov * mViewHeightInPixels);
  243. }
  244. // Return an invalid height
  245. return -1.f;
  246. }
  247. std::ostream& operator<<(std::ostream& s, const LLCamera& C)
  248. {
  249. s << "{ \n Center = " << C.getOrigin() << "\n";
  250. s << " AtAxis = " << C.getXAxis() << "\n";
  251. s << " LeftAxis = " << C.getYAxis() << "\n";
  252. s << " UpAxis = " << C.getZAxis() << "\n";
  253. s << " View = " << C.getView() << "\n";
  254. s << " Aspect = " << C.getAspect() << "\n";
  255. s << " NearPlane = " << C.mNearPlane << "\n";
  256. s << " FarPlane = " << C.mFarPlane << "\n}";
  257. return s;
  258. }
  259. void LLCamera::calculateFrustumPlanes()
  260. {
  261. // The planes only change when any of the frustum descriptions change.
  262. // They are not affected by changes of the position of the Frustum
  263. // because they are known in the view frame and the position merely
  264. // provides information on how to get from the absolute frame to the
  265. // view frame.
  266. F32 top = mFarPlane * tanf(0.5f * mView);
  267. F32 left = top * mAspect;
  268. calculateFrustumPlanes(left, -left, top, -top);
  269. }
  270. LL_INLINE static LLPlane plane_from_points(const LLVector3& p1,
  271. const LLVector3& p2,
  272. const LLVector3& p3)
  273. {
  274. LLVector3 n = (p2 - p1) % (p3 - p1);
  275. n.normalize();
  276. return LLPlane(p1, n);
  277. }
  278. void LLCamera::calcAgentFrustumPlanes(LLVector3* frust)
  279. {
  280. for (U32 i = 0; i < AGENT_FRUSTRUM_NUM; ++i)
  281. {
  282. mAgentFrustum[i] = frust[i];
  283. }
  284. mFrustumCornerDist = (frust[5] - getOrigin()).length();
  285. // Frust contains the 8 points of the frustum, calculate 6 planes
  286. // Order of planes is important, keep most likely to fail in the front of
  287. // the list
  288. // near - frust[0], frust[1], frust[2]
  289. mAgentPlanes[AGENT_PLANE_NEAR] = plane_from_points(frust[0], frust[1],
  290. frust[2]);
  291. // Far
  292. mAgentPlanes[AGENT_PLANE_FAR] = plane_from_points(frust[5], frust[4],
  293. frust[6]);
  294. // Left
  295. mAgentPlanes[AGENT_PLANE_LEFT] = plane_from_points(frust[4], frust[0],
  296. frust[7]);
  297. // Right
  298. mAgentPlanes[AGENT_PLANE_RIGHT] = plane_from_points(frust[1], frust[5],
  299. frust[6]);
  300. // Top
  301. mAgentPlanes[AGENT_PLANE_TOP] = plane_from_points(frust[3], frust[2],
  302. frust[6]);
  303. // Bottom
  304. mAgentPlanes[AGENT_PLANE_BOTTOM] = plane_from_points(frust[1], frust[0],
  305. frust[4]);
  306. // Cache plane octant facing mask for use in AABBInFrustum
  307. for (U32 i = 0; i < mPlaneCount; ++i)
  308. {
  309. mPlaneMask[i] = mAgentPlanes[i].calcPlaneMask();
  310. }
  311. }
  312. // Calculate regional planes from mAgentPlanes. Vector "shift" is the vector of
  313. // the region origin in the agent space.
  314. void LLCamera::calcRegionFrustumPlanes(const LLVector3& shift,
  315. F32 far_clip_distance)
  316. {
  317. LLVector3 p = getOrigin();
  318. LLVector3 n(mAgentPlanes[5][0], mAgentPlanes[5][1], mAgentPlanes[5][2]);
  319. F32 dd = n * p;
  320. F32 far_w;
  321. if (dd + mAgentPlanes[5][3] < 0) // Signed distance
  322. {
  323. far_w = -far_clip_distance - dd;
  324. }
  325. else
  326. {
  327. far_w = far_clip_distance - dd;
  328. }
  329. far_w += n * shift;
  330. F32 d;
  331. for (S32 i = 0 ; i < 7; ++i)
  332. {
  333. if (mPlaneMask[i] != 0xff)
  334. {
  335. n.set(mAgentPlanes[i][0], mAgentPlanes[i][1], mAgentPlanes[i][2]);
  336. if (i != 5)
  337. {
  338. d = mAgentPlanes[i][3] + n * shift;
  339. }
  340. else
  341. {
  342. d = far_w;
  343. }
  344. mRegionPlanes[i].setVec(n, d);
  345. }
  346. }
  347. }
  348. void LLCamera::calculateFrustumPlanes(F32 left, F32 right, F32 top, F32 bottom)
  349. {
  350. // Calculate center and radius squared of frustum in world absolute
  351. // coordinates
  352. mFrustCenter = LLVector3::x_axis * mFarPlane * 0.5f;
  353. mFrustCenter = transformToAbsolute(mFrustCenter);
  354. mFrustRadiusSquared = mFarPlane * 0.5f;
  355. // Pad radius squared by 5%
  356. mFrustRadiusSquared *= mFrustRadiusSquared * 1.05f;
  357. }
  358. // x and y are in WINDOW space, so x = Y-Axis (left/right), y= Z-Axis(Up/Down)
  359. void LLCamera::calculateFrustumPlanesFromWindow(F32 x1, F32 y1, F32 x2, F32 y2)
  360. {
  361. F32 view_height = (F32)tanf(0.5f * mView) * mFarPlane;
  362. F32 view_width = view_height * mAspect;
  363. F32 left = x1 * -2.f * view_width;
  364. F32 right = x2 * -2.f * view_width;
  365. F32 bottom = y1 * 2.f * view_height;
  366. F32 top = y2 * 2.f * view_height;
  367. calculateFrustumPlanes(left, right, top, bottom);
  368. }