ITriangle2D.h 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. // zlib open source license
  2. //
  3. // Copyright (c) 2017 to 2019 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_ITRIANGLE2D
  24. #define DFPSR_RENDER_ITRIANGLE2D
  25. #include <cstdint>
  26. #include <cassert>
  27. #include "ProjectedPoint.h"
  28. #include "../../math/FVector.h"
  29. #include "../../math/IVector.h"
  30. #include "../../math/IRect.h"
  31. #include "constants.h"
  32. namespace dsr {
  33. class RowInterval {
  34. public:
  35. // Start and end in exclusive pixel intervals
  36. int32_t left, right;
  37. // Constructors
  38. RowInterval() : left(0), right(0) {}
  39. RowInterval(int32_t left, int32_t right) : left(left), right(right) {}
  40. };
  41. // Get a pixel bound from sub-pixel 2D corners
  42. IRect getTriangleBound(LVector2D a, LVector2D b, LVector2D c);
  43. // Get a list of rows from a triangle of three 2D corners
  44. // Each corner is expressed in sub-pixels of constants::unitsPerPixel units per pixel
  45. // The rows must point to an array of at least clipBound.height() elements
  46. // Writing will be done to rows[r] for the whole range 0 <= r < clipBound.height()
  47. void rasterizeTriangle(const LVector2D& cornerA, const LVector2D& cornerB, const LVector2D& cornerC, RowInterval* rows, const IRect& clipBound);
  48. // The point should be expressed in the same coordinate system as the corners
  49. // Don't forget to add 0.5 if converting pixel indices to float centers
  50. FVector3D getAffineWeight(const FVector2D& cornerA, const FVector2D& cornerB, const FVector2D& cornerC, const FVector2D& point);
  51. template <typename T>
  52. T interpolateUsingAffineWeight(T valueA, T valueB, T valueC, FVector3D weight) {
  53. return valueA * weight.x + valueB * weight.y + valueC * weight.z;
  54. }
  55. class Projection {
  56. public:
  57. // W is the linear depth and 1/W is the reciprocal depth
  58. // When affine is true, the weights contain (W, U, V)
  59. // U and V are the affine vertex weights in a linear scale
  60. // When affine is false, the weights contain (1/W, U/W, V/W)
  61. // 1/W is the reciprocal weight used to get U and V
  62. // U/W and V/W are the vertex weights divided by the depth W
  63. bool affine = false;
  64. FVector3D pWeightStart; // Depth divided weights at the upper left corner of the target image
  65. FVector3D pWeightDx; // The difference when X increases by 1
  66. FVector3D pWeightDy; // The difference when Y increases by 1
  67. // Constructors
  68. Projection() {}
  69. Projection(bool affine, FVector3D pWeightStart, FVector3D pWeightDx, FVector3D pWeightDy) :
  70. affine(affine), pWeightStart(pWeightStart), pWeightDx(pWeightDx), pWeightDy(pWeightDy) {}
  71. // Affine interface
  72. // Precondition: affine is true
  73. FVector3D getWeight_affine(const IVector2D& screenPixel) const {
  74. assert(this->affine);
  75. // pWeightStart is relative to the target's upper left corner so we must add 0.5 to get the center of the pixel
  76. return this->pWeightStart + (this->pWeightDx * (screenPixel.x + 0.5f)) + (this->pWeightDy * (screenPixel.y + 0.5f));
  77. }
  78. // Returns the depth from a linear weight
  79. float getDepth_affine(const FVector3D& linearWeight) const {
  80. assert(this->affine);
  81. return linearWeight.x;
  82. }
  83. // Perspective interface
  84. // Precondition: affine is false
  85. // Returns (1/W, U/W, V/W) from the center of the pixel at screenPixel
  86. FVector3D getDepthDividedWeight_perspective(const IVector2D& screenPixel) const {
  87. assert(!(this->affine));
  88. // pWeightStart is relative to the target's upper left corner so we must add 0.5 to get the center of the pixel
  89. return this->pWeightStart + (this->pWeightDx * (screenPixel.x + 0.5f)) + (this->pWeightDy * (screenPixel.y + 0.5f));
  90. }
  91. // Returns (1/W, U/W, V/W) from screenPoint in floating pixel coordinates
  92. FVector3D getDepthDividedWeight_perspective(const FVector2D& screenPoint) const {
  93. assert(!(this->affine));
  94. return this->pWeightStart + (this->pWeightDx * screenPoint.x) + (this->pWeightDy * screenPoint.y);
  95. }
  96. // Returns the depth from a depth divided weight
  97. float getDepth_perspective(const FVector3D& depthDividedWeight) const {
  98. assert(!(this->affine));
  99. return 1.0f / depthDividedWeight.x;
  100. }
  101. FVector3D getWeight_perspective(const FVector3D& depthDividedWeight, float depth) const {
  102. assert(!(this->affine));
  103. FVector3D result;
  104. // Multiply U/W and V/W by W to get the U and V vertex weights
  105. result.y = depthDividedWeight.y * depth;
  106. result.z = depthDividedWeight.z * depth;
  107. // Replace calculate the UV complement now that we have used 1/W
  108. result.x = 1.0f - result.y - result.z;
  109. return result;
  110. }
  111. void sampleProjection_perspective(const IVector2D& screenPixel, FVector3D& weight, float& depth) const {
  112. assert(!(this->affine));
  113. FVector3D invWeight = this->getDepthDividedWeight_perspective(screenPixel);
  114. depth = this->getDepth_perspective(invWeight);
  115. weight = this->getWeight_perspective(invWeight, depth);
  116. }
  117. void sampleProjection_perspective(const FVector2D& screenPoint, FVector3D& weight, float& depth) const {
  118. assert(!(this->affine));
  119. FVector3D invWeight = this->getDepthDividedWeight_perspective(screenPoint);
  120. depth = this->getDepth_perspective(invWeight);
  121. weight = this->getWeight_perspective(invWeight, depth);
  122. }
  123. };
  124. class RowShape {
  125. public:
  126. // A collection of row intervals telling where pixels should be drawn
  127. const int startRow;
  128. const int rowCount;
  129. const RowInterval *rows;
  130. // Constructors
  131. RowShape() : startRow(0), rowCount(0), rows(nullptr) {}
  132. RowShape(int startRow, int rowCount, RowInterval* rows) : startRow(startRow), rowCount(rowCount), rows(rows) {}
  133. };
  134. // Any extra information will be given to the filling method as this only gives the shape and vertex interpolation data
  135. class ITriangle2D {
  136. public:
  137. // Per vertex (0, 1, 2)
  138. const ProjectedPoint position[3];
  139. // The unconstrained bound of the triangle to rasterize
  140. const IRect wholeBound;
  141. // Constructor that generates all data needed for fast rasterization
  142. ITriangle2D(ProjectedPoint posA, ProjectedPoint posB, ProjectedPoint posC);
  143. // Returns true iff the triangle is clockwise and may be drawn
  144. bool isFrontfacing() const;
  145. // Get the region to rasterize where the first and last rows may go outside of the clipBound with empty rows for alignment
  146. // Give a clipBound with top and bottom at even multiples of two if you don't want the result to go outside
  147. IRect getAlignedRasterBound(const IRect& clipBound, int alignX, int alignY) const;
  148. int getBufferSize(const IRect& clipBound, int alignX, int alignY) const;
  149. // Get the row intervals within clipBound into a buffer of a size given by getBufferSize with the same clipBound
  150. // Output
  151. // rows: The exclusive row interval for each row from top to bottom
  152. // startRow: The Y coordinate of the first row
  153. // Input
  154. // clipBound: The pixel region where the resulting rows may draw.
  155. void getShape(int& startRow, RowInterval* rows, const IRect& clipBound, int alignX, int alignY) const;
  156. // Returns the vertex weight projection for specified sub-vertex weights
  157. Projection getProjection(const FVector3D& subB, const FVector3D& subC, bool perspective) const;
  158. // Returns the vertex weight projection for default sub-vertex weights
  159. Projection getProjection(bool perspective) const;
  160. };
  161. }
  162. #endif