| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177 |
- // zlib open source license
- //
- // Copyright (c) 2017 to 2019 David Forsgren Piuva
- //
- // This software is provided 'as-is', without any express or implied
- // warranty. In no event will the authors be held liable for any damages
- // arising from the use of this software.
- //
- // Permission is granted to anyone to use this software for any purpose,
- // including commercial applications, and to alter it and redistribute it
- // freely, subject to the following restrictions:
- //
- // 1. The origin of this software must not be misrepresented; you must not
- // claim that you wrote the original software. If you use this software
- // in a product, an acknowledgment in the product documentation would be
- // appreciated but is not required.
- //
- // 2. Altered source versions must be plainly marked as such, and must not be
- // misrepresented as being the original software.
- //
- // 3. This notice may not be removed or altered from any source
- // distribution.
- #ifndef DFPSR_RENDER_ITRIANGLE2D
- #define DFPSR_RENDER_ITRIANGLE2D
- #include <cstdint>
- #include <cassert>
- #include "ProjectedPoint.h"
- #include "../../math/FVector.h"
- #include "../../math/IVector.h"
- #include "../../math/IRect.h"
- #include "constants.h"
- namespace dsr {
- class RowInterval {
- public:
- // Start and end in exclusive pixel intervals
- int32_t left, right;
- // Constructors
- RowInterval() : left(0), right(0) {}
- RowInterval(int32_t left, int32_t right) : left(left), right(right) {}
- };
- // Get a pixel bound from sub-pixel 2D corners
- IRect getTriangleBound(LVector2D a, LVector2D b, LVector2D c);
- // Get a list of rows from a triangle of three 2D corners
- // Each corner is expressed in sub-pixels of constants::unitsPerPixel units per pixel
- // The rows must point to an array of at least clipBound.height() elements
- // Writing will be done to rows[r] for the whole range 0 <= r < clipBound.height()
- void rasterizeTriangle(const LVector2D& cornerA, const LVector2D& cornerB, const LVector2D& cornerC, RowInterval* rows, const IRect& clipBound);
- // The point should be expressed in the same coordinate system as the corners
- // Don't forget to add 0.5 if converting pixel indices to float centers
- FVector3D getAffineWeight(const FVector2D& cornerA, const FVector2D& cornerB, const FVector2D& cornerC, const FVector2D& point);
- template <typename T>
- T interpolateUsingAffineWeight(T valueA, T valueB, T valueC, FVector3D weight) {
- return valueA * weight.x + valueB * weight.y + valueC * weight.z;
- }
- class Projection {
- public:
- // W is the linear depth and 1/W is the reciprocal depth
- // When affine is true, the weights contain (W, U, V)
- // U and V are the affine vertex weights in a linear scale
- // When affine is false, the weights contain (1/W, U/W, V/W)
- // 1/W is the reciprocal weight used to get U and V
- // U/W and V/W are the vertex weights divided by the depth W
- bool affine = false;
- FVector3D pWeightStart; // Depth divided weights at the upper left corner of the target image
- FVector3D pWeightDx; // The difference when X increases by 1
- FVector3D pWeightDy; // The difference when Y increases by 1
- // Constructors
- Projection() {}
- Projection(bool affine, FVector3D pWeightStart, FVector3D pWeightDx, FVector3D pWeightDy) :
- affine(affine), pWeightStart(pWeightStart), pWeightDx(pWeightDx), pWeightDy(pWeightDy) {}
- // Affine interface
- // Precondition: affine is true
- FVector3D getWeight_affine(const IVector2D& screenPixel) const {
- assert(this->affine);
- // pWeightStart is relative to the target's upper left corner so we must add 0.5 to get the center of the pixel
- return this->pWeightStart + (this->pWeightDx * (screenPixel.x + 0.5f)) + (this->pWeightDy * (screenPixel.y + 0.5f));
- }
- // Returns the depth from a linear weight
- float getDepth_affine(const FVector3D& linearWeight) const {
- assert(this->affine);
- return linearWeight.x;
- }
- // Perspective interface
- // Precondition: affine is false
- // Returns (1/W, U/W, V/W) from the center of the pixel at screenPixel
- FVector3D getDepthDividedWeight_perspective(const IVector2D& screenPixel) const {
- assert(!(this->affine));
- // pWeightStart is relative to the target's upper left corner so we must add 0.5 to get the center of the pixel
- return this->pWeightStart + (this->pWeightDx * (screenPixel.x + 0.5f)) + (this->pWeightDy * (screenPixel.y + 0.5f));
- }
- // Returns (1/W, U/W, V/W) from screenPoint in floating pixel coordinates
- FVector3D getDepthDividedWeight_perspective(const FVector2D& screenPoint) const {
- assert(!(this->affine));
- return this->pWeightStart + (this->pWeightDx * screenPoint.x) + (this->pWeightDy * screenPoint.y);
- }
- // Returns the depth from a depth divided weight
- float getDepth_perspective(const FVector3D& depthDividedWeight) const {
- assert(!(this->affine));
- return 1.0f / depthDividedWeight.x;
- }
- FVector3D getWeight_perspective(const FVector3D& depthDividedWeight, float depth) const {
- assert(!(this->affine));
- FVector3D result;
- // Multiply U/W and V/W by W to get the U and V vertex weights
- result.y = depthDividedWeight.y * depth;
- result.z = depthDividedWeight.z * depth;
- // Replace calculate the UV complement now that we have used 1/W
- result.x = 1.0f - result.y - result.z;
- return result;
- }
- void sampleProjection_perspective(const IVector2D& screenPixel, FVector3D& weight, float& depth) const {
- assert(!(this->affine));
- FVector3D invWeight = this->getDepthDividedWeight_perspective(screenPixel);
- depth = this->getDepth_perspective(invWeight);
- weight = this->getWeight_perspective(invWeight, depth);
- }
- void sampleProjection_perspective(const FVector2D& screenPoint, FVector3D& weight, float& depth) const {
- assert(!(this->affine));
- FVector3D invWeight = this->getDepthDividedWeight_perspective(screenPoint);
- depth = this->getDepth_perspective(invWeight);
- weight = this->getWeight_perspective(invWeight, depth);
- }
- };
- class RowShape {
- public:
- // A collection of row intervals telling where pixels should be drawn
- const int startRow;
- const int rowCount;
- const RowInterval *rows;
- // Constructors
- RowShape() : startRow(0), rowCount(0), rows(nullptr) {}
- RowShape(int startRow, int rowCount, RowInterval* rows) : startRow(startRow), rowCount(rowCount), rows(rows) {}
- };
- // Any extra information will be given to the filling method as this only gives the shape and vertex interpolation data
- class ITriangle2D {
- public:
- // Per vertex (0, 1, 2)
- const ProjectedPoint position[3];
- // The unconstrained bound of the triangle to rasterize
- const IRect wholeBound;
- // Constructor that generates all data needed for fast rasterization
- ITriangle2D(ProjectedPoint posA, ProjectedPoint posB, ProjectedPoint posC);
- // Returns true iff the triangle is clockwise and may be drawn
- bool isFrontfacing() const;
- // Get the region to rasterize where the first and last rows may go outside of the clipBound with empty rows for alignment
- // Give a clipBound with top and bottom at even multiples of two if you don't want the result to go outside
- IRect getAlignedRasterBound(const IRect& clipBound, int alignX, int alignY) const;
- int getBufferSize(const IRect& clipBound, int alignX, int alignY) const;
- // Get the row intervals within clipBound into a buffer of a size given by getBufferSize with the same clipBound
- // Output
- // rows: The exclusive row interval for each row from top to bottom
- // startRow: The Y coordinate of the first row
- // Input
- // clipBound: The pixel region where the resulting rows may draw.
- void getShape(int& startRow, RowInterval* rows, const IRect& clipBound, int alignX, int alignY) const;
- // Returns the vertex weight projection for specified sub-vertex weights
- Projection getProjection(const FVector3D& subB, const FVector3D& subC, bool perspective) const;
- // Returns the vertex weight projection for default sub-vertex weights
- Projection getProjection(bool perspective) const;
- };
- }
- #endif
|