2022-10-28 09:31:13 +02:00
|
|
|
|
#include "primitive/triangle.h"
|
|
|
|
|
#include <algorithm>
|
|
|
|
|
|
|
|
|
|
// Constructor /////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
Triangle::Triangle(std::shared_ptr<Shader> const &shader) : Primitive(shader) {}
|
|
|
|
|
|
2022-11-11 14:27:43 +01:00
|
|
|
|
Triangle::Triangle(Vector3d const &a, Vector3d const &b, Vector3d const &c, std::shared_ptr<Shader> const &shader) : Primitive(shader), vertex{a, b, c} {}
|
2022-10-28 09:31:13 +02:00
|
|
|
|
|
2022-11-11 14:27:43 +01:00
|
|
|
|
Triangle::Triangle(Vector3d const &a, Vector3d const &b, Vector3d const &c, Vector3d const &na, Vector3d const &nb, Vector3d const &nc, std::shared_ptr<Shader> const &shader) : Primitive(shader), vertex{a, b, c}, normal{na, nb, nc} {}
|
2022-10-28 09:31:13 +02:00
|
|
|
|
|
2022-11-11 14:27:43 +01:00
|
|
|
|
Triangle::Triangle(Vector3d const &a, Vector3d const &b, Vector3d const &c, Vector3d const &na, Vector3d const &nb, Vector3d const &nc, Vector2d const &ta, Vector2d const &tb, Vector2d const &tc, std::shared_ptr<Shader> const &shader)
|
2022-10-28 09:31:13 +02:00
|
|
|
|
: Primitive(shader), vertex{a, b, c}, normal{na, nb, nc}, surface{ta, tb, tc} {}
|
|
|
|
|
|
2022-11-11 14:27:43 +01:00
|
|
|
|
Triangle::Triangle(Vector3d const &a, Vector3d const &b, Vector3d const &c, Vector3d const &na, Vector3d const &nb, Vector3d const &nc, Vector3d const &tana, Vector3d const &tanb, Vector3d const &tanc, Vector3d const &ba,
|
|
|
|
|
Vector3d const &bb, Vector3d const &bc, Vector2d const &ta, Vector2d const &tb, Vector2d const &tc, std::shared_ptr<Shader> const &shader)
|
|
|
|
|
: Primitive(shader), vertex{a, b, c}, normal{na, nb, nc}, tangent{tana, tanb, tanc}, bitangent{ba, bb, bc}, surface{ta, tb, tc} {}
|
|
|
|
|
|
2022-10-28 09:31:13 +02:00
|
|
|
|
// Primitive functions /////////////////////////////////////////////////////////
|
|
|
|
|
|
2022-11-11 14:27:43 +01:00
|
|
|
|
bool Triangle::intersectArea(Ray &ray) const {
|
|
|
|
|
// alternative triangle test
|
|
|
|
|
// "signed" triangle area with respect to triangle normal
|
|
|
|
|
auto triangleArea = [](Vector3d const &v0, Vector3d const &v1, Vector3d const &v2, Vector3d const &normal = Vector3d(0, 0, 0)) {
|
|
|
|
|
if (length(normal) < EPSILON) {
|
|
|
|
|
return length(crossProduct(v2 - v0, v1 - v0)) / 2.0f;
|
|
|
|
|
} else {
|
|
|
|
|
Vector3d const cp = crossProduct(v2 - v0, v1 - v0);
|
|
|
|
|
return dotProduct(cp, normal) > 0.0f ? length(cp) / 2.0f : -length(cp) / 2.0f;
|
|
|
|
|
}
|
|
|
|
|
};
|
2022-10-28 09:31:13 +02:00
|
|
|
|
|
2022-11-11 14:27:43 +01:00
|
|
|
|
// begin ray-plane intersection ----------------------------
|
|
|
|
|
Vector3d normal = normalized(crossProduct(vertex[2] - vertex[0], vertex[1] - vertex[0]));
|
|
|
|
|
|
|
|
|
|
float const cosine = dotProduct(ray.direction, normal);
|
|
|
|
|
|
2022-11-17 16:16:32 +01:00
|
|
|
|
if (std::abs(cosine) < EPSILON)
|
2022-11-11 14:27:43 +01:00
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
float const t = dotProduct(vertex[0] - ray.origin, normal) / cosine;
|
|
|
|
|
|
|
|
|
|
if (t < EPSILON || ray.length < t)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
Vector3d const p = ray.origin + t * ray.direction;
|
|
|
|
|
// end ray-plane intersection ----------------------------
|
|
|
|
|
|
|
|
|
|
float const fullArea = triangleArea(vertex[0], vertex[1], vertex[2]);
|
|
|
|
|
float const a = triangleArea(p, vertex[0], vertex[1], normal) / fullArea;
|
|
|
|
|
float const b = triangleArea(p, vertex[2], vertex[0], normal) / fullArea;
|
|
|
|
|
|
|
|
|
|
if ((a < 0.0f) || (a > 1.0f) || (b < 0.0f) || (a + b > 1.0f))
|
|
|
|
|
return false;
|
2022-10-28 09:31:13 +02:00
|
|
|
|
|
2022-11-11 14:27:43 +01:00
|
|
|
|
// Set the surface position (barycentric coordinates) and tangent Vector
|
|
|
|
|
ray.surface = a * this->surface[1] + b * this->surface[2] + (1 - a - b) * this->surface[0];
|
|
|
|
|
|
|
|
|
|
// Set the new length and the current primitive
|
2022-11-17 16:16:32 +01:00
|
|
|
|
ray.normal = normal;
|
2022-11-11 14:27:43 +01:00
|
|
|
|
ray.length = t;
|
|
|
|
|
ray.primitive = this;
|
|
|
|
|
|
|
|
|
|
// True, because the primitive was hit
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool Triangle::intersect(Ray &ray) const {
|
|
|
|
|
// We use the Möller–Trumbore intersection algorithm
|
|
|
|
|
|
|
|
|
|
// Determine two neighboring edge vectors
|
|
|
|
|
Vector3d const edge1 = this->vertex[1] - this->vertex[0];
|
|
|
|
|
Vector3d const edge2 = this->vertex[2] - this->vertex[0];
|
|
|
|
|
|
|
|
|
|
// Begin calculating determinant
|
|
|
|
|
Vector3d const pVec = crossProduct(ray.direction, edge2);
|
|
|
|
|
|
|
|
|
|
// Make sure the ray is not parallel to the triangle
|
|
|
|
|
float const det = dotProduct(edge1, pVec);
|
|
|
|
|
if (fabs(det) < EPSILON)
|
|
|
|
|
return false;
|
|
|
|
|
float const inv_det = 1.0f / det;
|
|
|
|
|
|
|
|
|
|
// Calculate u and test bound
|
|
|
|
|
Vector3d const tVec = ray.origin - this->vertex[0];
|
|
|
|
|
float const u = dotProduct(tVec, pVec) * inv_det;
|
|
|
|
|
// Test whether the intersection lies outside the triangle
|
|
|
|
|
if (0.0f > u || u > 1.0f)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
// Calculate v and test bound
|
|
|
|
|
Vector3d const qVec = crossProduct(tVec, edge1);
|
|
|
|
|
float const v = dotProduct(ray.direction, qVec) * inv_det;
|
|
|
|
|
// Test whether the intersection lies outside the triangle
|
|
|
|
|
if (0.0f > v || u + v > 1.0f)
|
|
|
|
|
return false;
|
2022-10-28 09:31:13 +02:00
|
|
|
|
|
|
|
|
|
// Test whether this is the foremost primitive in front of the camera
|
2022-11-11 14:27:43 +01:00
|
|
|
|
float const t = dotProduct(edge2, qVec) * inv_det;
|
|
|
|
|
if (t < EPSILON || ray.length < t)
|
|
|
|
|
return false;
|
2022-10-28 09:31:13 +02:00
|
|
|
|
|
2022-11-11 14:27:43 +01:00
|
|
|
|
// Calculate the normal
|
2022-11-17 19:22:30 +01:00
|
|
|
|
ray.normal = normalized(crossProduct(edge1, edge1));
|
2022-10-28 09:31:13 +02:00
|
|
|
|
|
2022-11-11 14:27:43 +01:00
|
|
|
|
// Calculate the surface position
|
|
|
|
|
ray.surface = u * this->surface[1] + v * this->surface[2] + (1 - u - v) * this->surface[0];
|
2022-10-28 09:31:13 +02:00
|
|
|
|
|
|
|
|
|
// Set the new length and the current primitive
|
2022-11-11 14:27:43 +01:00
|
|
|
|
ray.length = t;
|
|
|
|
|
ray.primitive = this;
|
2022-10-28 09:31:13 +02:00
|
|
|
|
|
2022-11-11 14:27:43 +01:00
|
|
|
|
// True, because the primitive was hit
|
|
|
|
|
return true;
|
2022-10-28 09:31:13 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Bounding box ////////////////////////////////////////////////////////////////
|
|
|
|
|
|
2022-11-11 14:27:43 +01:00
|
|
|
|
float Triangle::minimumBounds(int dimension) const { return std::min(this->vertex[0][dimension], std::min(this->vertex[1][dimension], this->vertex[2][dimension])); }
|
2022-10-28 09:31:13 +02:00
|
|
|
|
|
2022-11-11 14:27:43 +01:00
|
|
|
|
float Triangle::maximumBounds(int dimension) const { return std::max(this->vertex[0][dimension], std::max(this->vertex[1][dimension], this->vertex[2][dimension])); }
|