Photon Engine 2.0.0-beta
A physically based renderer.
Loading...
Searching...
No Matches
TWatertightTriangle.ipp
Go to the documentation of this file.
1#pragma once
2
4#include "Math/TVector3.h"
5
6#include <Common/assertion.h>
7#include <Common/primitive_type.h>
8
9#include <string>
10#include <cmath>
11
12namespace ph::math
13{
14
15template<typename T>
17 const TLineSegment<T>& segment,
18 T* const out_hitT,
19 TVector3<T>* const out_hitBarycentricCoords) const
20{
21 PH_ASSERT(out_hitT);
22 PH_ASSERT(out_hitBarycentricCoords);
23
24 TVector3<T> segmentDir = segment.getDir();
25 TVector3<T> vAt = this->getVa().sub(segment.getOrigin());
26 TVector3<T> vBt = this->getVb().sub(segment.getOrigin());
27 TVector3<T> vCt = this->getVc().sub(segment.getOrigin());
28
29 // Find the dominant dimension of ray direction and make it Z; the rest
30 // dimensions are arbitrarily assigned
31 if(std::abs(segmentDir.x()) > std::abs(segmentDir.y()))
32 {
33 // X dominant
34 if(std::abs(segmentDir.x()) > std::abs(segmentDir.z()))
35 {
36 segmentDir.set({segmentDir.y(), segmentDir.z(), segmentDir.x()});
37 vAt.set({vAt.y(), vAt.z(), vAt.x()});
38 vBt.set({vBt.y(), vBt.z(), vBt.x()});
39 vCt.set({vCt.y(), vCt.z(), vCt.x()});
40 }
41 // Z dominant
42 else
43 {
44 // left as-is
45 }
46 }
47 else
48 {
49 // Y dominant
50 if(std::abs(segmentDir.y()) > std::abs(segmentDir.z()))
51 {
52 segmentDir.set({segmentDir.z(), segmentDir.x(), segmentDir.y()});
53 vAt.set({vAt.z(), vAt.x(), vAt.y()});
54 vBt.set({vBt.z(), vBt.x(), vBt.y()});
55 vCt.set({vCt.z(), vCt.x(), vCt.y()});
56 }
57 // Z dominant
58 else
59 {
60 // left as-is
61 }
62 }
63
64 PH_ASSERT_MSG(segmentDir.z() != static_cast<T>(0) && std::isfinite(segmentDir.z()),
65 std::to_string(segmentDir.z()));
66
67 const T rcpSegmentDirZ = T(1) / segmentDir.z();
68 const T shearX = -segmentDir.x() * rcpSegmentDirZ;
69 const T shearY = -segmentDir.y() * rcpSegmentDirZ;
70 const T shearZ = rcpSegmentDirZ;
71
72 vAt.x() += shearX * vAt.z();
73 vAt.y() += shearY * vAt.z();
74 vBt.x() += shearX * vBt.z();
75 vBt.y() += shearY * vBt.z();
76 vCt.x() += shearX * vCt.z();
77 vCt.y() += shearY * vCt.z();
78
79 T funcEa = vBt.x() * vCt.y() - vBt.y() * vCt.x();
80 T funcEb = vCt.x() * vAt.y() - vCt.y() * vAt.x();
81 T funcEc = vAt.x() * vBt.y() - vAt.y() * vBt.x();
82
83 // FIXME: properly check if T is of lower precision than float64
84 // Possibly fallback to higher precision test for triangle edges
85 if constexpr(sizeof(T) < sizeof(float64))
86 {
87 if(funcEa == static_cast<T>(0) || funcEb == static_cast<T>(0) || funcEc == static_cast<T>(0))
88 {
89 const float64 funcEa64 = static_cast<float64>(vBt.x()) * static_cast<float64>(vCt.y()) -
90 static_cast<float64>(vBt.y()) * static_cast<float64>(vCt.x());
91 const float64 funcEb64 = static_cast<float64>(vCt.x()) * static_cast<float64>(vAt.y()) -
92 static_cast<float64>(vCt.y()) * static_cast<float64>(vAt.x());
93 const float64 funcEc64 = static_cast<float64>(vAt.x()) * static_cast<float64>(vBt.y()) -
94 static_cast<float64>(vAt.y()) * static_cast<float64>(vBt.x());
95
96 funcEa = static_cast<T>(funcEa64);
97 funcEb = static_cast<T>(funcEb64);
98 funcEc = static_cast<T>(funcEc64);
99 }
100 }
101
102 if((funcEa < static_cast<T>(0) || funcEb < static_cast<T>(0) || funcEc < static_cast<T>(0)) &&
103 (funcEa > static_cast<T>(0) || funcEb > static_cast<T>(0) || funcEc > static_cast<T>(0)))
104 {
105 return false;
106 }
107
108 // In addition to 0, also reject NaN and Inf (they will sabotage the barycentric coordinates)
109 const T determinant = funcEa + funcEb + funcEc;
110 if(determinant == static_cast<T>(0) || !std::isfinite(determinant))
111 {
112 return false;
113 }
114
115 vAt.z() *= shearZ;
116 vBt.z() *= shearZ;
117 vCt.z() *= shearZ;
118
119 const T hitTscaled = funcEa * vAt.z() + funcEb * vBt.z() + funcEc * vCt.z();
120 if(determinant > static_cast<T>(0))
121 {
122 if(hitTscaled < segment.getMinT() * determinant || hitTscaled > segment.getMaxT() * determinant)
123 {
124 return false;
125 }
126 }
127 else
128 {
129 if(hitTscaled > segment.getMinT() * determinant || hitTscaled < segment.getMaxT() * determinant)
130 {
131 return false;
132 }
133 }
134
135 // So the ray intersects the triangle
136
137 PH_ASSERT_MSG(determinant != static_cast<T>(0) && std::isfinite(determinant),
138 std::to_string(determinant));
139
140 const T rcpDeterminant = static_cast<T>(1) / determinant;
141 const T baryA = funcEa * rcpDeterminant;
142 const T baryB = funcEb * rcpDeterminant;
143 const T baryC = funcEc * rcpDeterminant;
144 const T hitT = hitTscaled * rcpDeterminant;
145
146 *out_hitT = hitT;
147 *out_hitBarycentricCoords = TVector3<T>(baryA, baryB, baryC);
148 return true;
149}
150
151//template<typename T>
152//inline bool TWatertightTriangle<T>::isIntersectingRefined(
153// const TLineSegment<T>& segment,
154// T* const out_hitT,
155// TVector3<T>* const out_hitBarycentricCoords) const
156//{
157// const auto segmentOriginToCentroid = this->getCentroid() - segment.getOrigin();
158// const auto approxHitT = segmentOriginToCentroid.dot(segment.getDirection());
159//
160// const TLineSegment<T> shiftedSegment(
161// segment.getPoint(approxHitT),
162// segment.getDirection(),
163// segment.getMinT() - approxHitT,
164// segment.getMaxT() - approxHitT);
165// if(!isIntersecting(shiftedSegment, out_hitT, out_hitBarycentricCoords))
166// {
167// return false;
168// }
169//
170// *out_hitT += approxHitT;
171// return true;
172//}
173
174}// end namespace ph::math
Represents a line segment in space.
Definition TLineSegment.h:25
T getMinT() const
Definition TLineSegment.ipp:94
const TVector3< T > & getOrigin() const
Definition TLineSegment.ipp:82
const TVector3< T > & getDir() const
Definition TLineSegment.ipp:88
T getMaxT() const
Definition TLineSegment.ipp:100
Represents a 3-D vector.
Definition TVector3.h:17
T & y()
Definition TVector3.ipp:189
T & z()
Definition TVector3.ipp:195
T & x()
Definition TVector3.ipp:183
Derived & set(T value)
Definition TArithmeticArrayBase.ipp:604
Derived sub(const Derived &rhs) const
Definition TArithmeticArrayBase.ipp:62
bool isIntersecting(const TLineSegment< T > &segment, T *out_hitT, TVector3< T > *out_hitBarycentricCoords) const
Checks whether the segment is interseting with this triangle.
Definition TWatertightTriangle.ipp:16
Math functions and utilities.
Definition TransformInfo.h:10