2
0

Camera.h 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. // zlib open source license
  2. //
  3. // Copyright (c) 2017 to 2022 David Forsgren Piuva
  4. //
  5. // This software is provided 'as-is', without any express or implied
  6. // warranty. In no event will the authors be held liable for any damages
  7. // arising from the use of this software.
  8. //
  9. // Permission is granted to anyone to use this software for any purpose,
  10. // including commercial applications, and to alter it and redistribute it
  11. // freely, subject to the following restrictions:
  12. //
  13. // 1. The origin of this software must not be misrepresented; you must not
  14. // claim that you wrote the original software. If you use this software
  15. // in a product, an acknowledgment in the product documentation would be
  16. // appreciated but is not required.
  17. //
  18. // 2. Altered source versions must be plainly marked as such, and must not be
  19. // misrepresented as being the original software.
  20. //
  21. // 3. This notice may not be removed or altered from any source
  22. // distribution.
  23. #ifndef DFPSR_RENDER_CAMERA
  24. #define DFPSR_RENDER_CAMERA
  25. #include <stdint.h>
  26. #include <cassert>
  27. #include "../math/FVector.h"
  28. #include "../math/LVector.h"
  29. #include "../math/FPlane3D.h"
  30. #include "../math/Transform3D.h"
  31. #include "../math/scalar.h"
  32. #include "constants.h"
  33. #include "ProjectedPoint.h"
  34. #include <limits>
  35. namespace dsr {
  36. class ViewFrustum {
  37. private:
  38. FPlane3D planes[6];
  39. int planeCount;
  40. public:
  41. ViewFrustum() : planeCount(0) {}
  42. // Orthogonal view frustum in camera space
  43. ViewFrustum(float halfWidth, float halfHeight) : planeCount(4) {
  44. // Sides
  45. planes[0] = FPlane3D(FVector3D(1.0f, 0.0f, 0.0f), halfWidth + 0.1f);
  46. planes[1] = FPlane3D(FVector3D(-1.0f, 0.0f, 0.0f), halfWidth + 0.1f);
  47. planes[2] = FPlane3D(FVector3D(0.0f, 1.0f, 0.0f), halfHeight + 0.1f);
  48. planes[3] = FPlane3D(FVector3D(0.0f, -1.0f, 0.0f), halfHeight + 0.1f);
  49. }
  50. // Perspective view frustum in camera space
  51. ViewFrustum(float nearClip, float farClip, float widthSlope, float heightSlope) : planeCount(6) {
  52. // Sides
  53. planes[0] = FPlane3D(FVector3D(1.0f, 0.0f, -widthSlope - 0.01f), 0.0f);
  54. planes[1] = FPlane3D(FVector3D(-1.0f, 0.0f, -widthSlope - 0.01f), 0.0f);
  55. planes[2] = FPlane3D(FVector3D(0.0f, 1.0f, -heightSlope - 0.01f), 0.0f);
  56. planes[3] = FPlane3D(FVector3D(0.0f, -1.0f, -heightSlope - 0.01f), 0.0f);
  57. // Near and far clip planes
  58. planes[4] = FPlane3D(FVector3D(0.0f, 0.0f, 1.0f), farClip);
  59. planes[5] = FPlane3D(FVector3D(0.0f, 0.0f, -1.0f), -nearClip);
  60. }
  61. int getPlaneCount() const {
  62. return this->planeCount;
  63. }
  64. FPlane3D getPlane(int sideIndex) const {
  65. assert(sideIndex >= 0 && sideIndex < this->planeCount);
  66. return planes[sideIndex];
  67. }
  68. };
  69. static const float defaultNearClip = 0.01f;
  70. static const float defaultFarClip = 1000.0f;
  71. static const float clipRatio = 2.0f;
  72. // Just create a new camera on stack memory every time you need to render something
  73. class Camera {
  74. public: // Do not modify individual settings without assigning whole new cameras
  75. bool perspective; // When off, widthSlope and heightSlope will be used as halfWidth and halfHeight.
  76. Transform3D location; // Only translation and rotation allowed. Scaling and tilting will obviously not work for cameras.
  77. float widthSlope, heightSlope, invWidthSlope, invHeightSlope, imageWidth, imageHeight, nearClip, farClip;
  78. ViewFrustum cullFrustum, clipFrustum;
  79. Camera() :
  80. perspective(true), location(Transform3D()), widthSlope(0.0f), heightSlope(0.0f),
  81. invWidthSlope(0.0f), invHeightSlope(0.0f), imageWidth(0), imageHeight(0),
  82. nearClip(0.0f), farClip(0.0f), cullFrustum(ViewFrustum()), clipFrustum(ViewFrustum()) {}
  83. Camera(bool perspective, const Transform3D &location, float imageWidth, float imageHeight, float widthSlope, float heightSlope, float nearClip, float farClip, const ViewFrustum &cullFrustum, const ViewFrustum &clipFrustum) :
  84. perspective(perspective), location(location), widthSlope(widthSlope), heightSlope(heightSlope),
  85. invWidthSlope(0.5f / widthSlope), invHeightSlope(0.5f / heightSlope), imageWidth(imageWidth), imageHeight(imageHeight),
  86. nearClip(nearClip), farClip(farClip), cullFrustum(cullFrustum), clipFrustum(clipFrustum) {}
  87. public:
  88. static Camera createPerspective(const Transform3D &location, float imageWidth, float imageHeight, float widthSlope = 1.0f, float nearClip = defaultNearClip, float farClip = defaultFarClip) {
  89. float heightSlope = widthSlope * imageHeight / imageWidth;
  90. return Camera(true, location, imageWidth, imageHeight, widthSlope, heightSlope, nearClip, farClip,
  91. ViewFrustum(nearClip, farClip, widthSlope, heightSlope),
  92. ViewFrustum(nearClip, farClip, widthSlope * clipRatio, heightSlope * clipRatio));
  93. }
  94. // Orthogonal cameras doesn't have any near or far clip planes
  95. static Camera createOrthogonal(const Transform3D &location, float imageWidth, float imageHeight, float halfWidth) {
  96. float halfHeight = halfWidth * imageHeight / imageWidth;
  97. return Camera(false, location, imageWidth, imageHeight, halfWidth, halfHeight, -std::numeric_limits<float>::max(), std::numeric_limits<float>::max(),
  98. ViewFrustum(halfWidth, halfHeight),
  99. ViewFrustum(halfWidth * clipRatio, halfHeight * clipRatio));
  100. }
  101. FVector3D worldToCamera(const FVector3D &worldSpace) const {
  102. return this->location.transformPointTransposedInverse(worldSpace);
  103. }
  104. ProjectedPoint cameraToScreen(const FVector3D &cameraSpace) const {
  105. // Camera to image space
  106. if (this->perspective) {
  107. float invDepth;
  108. if (cameraSpace.z > 0.0f) {
  109. invDepth = 1.0f / cameraSpace.z;
  110. } else {
  111. invDepth = 0.0f;
  112. }
  113. float centerShear = cameraSpace.z * 0.5f;
  114. FVector2D preProjection = FVector2D(
  115. ( cameraSpace.x * this->invWidthSlope + centerShear) * this->imageWidth,
  116. (-cameraSpace.y * this->invHeightSlope + centerShear) * this->imageHeight
  117. );
  118. FVector2D projectedFloat = preProjection * invDepth;
  119. FVector2D subPixel = projectedFloat * constants::unitsPerPixel;
  120. LVector2D rounded = LVector2D(safeRoundInt64(subPixel.x), safeRoundInt64(subPixel.y));
  121. return ProjectedPoint(cameraSpace, projectedFloat, rounded);
  122. } else {
  123. FVector2D projectedFloat = FVector2D(
  124. ( cameraSpace.x * this->invWidthSlope + 0.5f) * this->imageWidth,
  125. (-cameraSpace.y * this->invHeightSlope + 0.5f) * this->imageHeight
  126. );
  127. FVector2D subPixel = projectedFloat * constants::unitsPerPixel;
  128. LVector2D rounded = LVector2D(safeRoundInt64(subPixel.x), safeRoundInt64(subPixel.y));
  129. return ProjectedPoint(cameraSpace, projectedFloat, rounded);
  130. }
  131. }
  132. ProjectedPoint worldToScreen(const FVector3D &worldSpace) const {
  133. return this->cameraToScreen(this->worldToCamera(worldSpace));
  134. }
  135. int getFrustumPlaneCount(bool clipping) const {
  136. return clipping ? this->clipFrustum.getPlaneCount() : this->cullFrustum.getPlaneCount();
  137. }
  138. FPlane3D getFrustumPlane(int sideIndex, bool clipping) const {
  139. return clipping ? this->clipFrustum.getPlane(sideIndex) : this->cullFrustum.getPlane(sideIndex);
  140. }
  141. // Returns false iff all 6 points from the box of minBound and maxBound multiplied by transform are outside of the same plane of cullFrustum
  142. // This is a quick indication to if something within that bound would be rendered
  143. bool isBoxSeen(const FVector3D& minBound, const FVector3D& maxBound, const Transform3D &modelToWorld) const {
  144. FVector3D corner[8] = {
  145. FVector3D(minBound.x, minBound.y, minBound.z),
  146. FVector3D(maxBound.x, minBound.y, minBound.z),
  147. FVector3D(minBound.x, maxBound.y, minBound.z),
  148. FVector3D(maxBound.x, maxBound.y, minBound.z),
  149. FVector3D(minBound.x, minBound.y, maxBound.z),
  150. FVector3D(maxBound.x, minBound.y, maxBound.z),
  151. FVector3D(minBound.x, maxBound.y, maxBound.z),
  152. FVector3D(maxBound.x, maxBound.y, maxBound.z)
  153. };
  154. for (int c = 0; c < 8; c++) {
  155. corner[c] = worldToCamera(modelToWorld.transformPoint(corner[c]));
  156. }
  157. for (int s = 0; s < this->cullFrustum.getPlaneCount(); s++) {
  158. FPlane3D plane = this->cullFrustum.getPlane(s);
  159. if (!(plane.inside(corner[0])
  160. || plane.inside(corner[1])
  161. || plane.inside(corner[2])
  162. || plane.inside(corner[3])
  163. || plane.inside(corner[4])
  164. || plane.inside(corner[5])
  165. || plane.inside(corner[6])
  166. || plane.inside(corner[7]))) {
  167. return false;
  168. }
  169. }
  170. return true;
  171. }
  172. };
  173. }
  174. #endif