1#include <Core/Geometry/RayCast.hpp>
2#include <Core/Geometry/TriangleMesh.hpp>
3#include <Core/Math/LinearAlgebra.hpp>
10bool RayCastAabb(
const Ray& r,
const Core::Aabb& aabb, Scalar& hitOut, Vector3& normalOut ) {
15 CORE_ASSERT( r.direction().squaredNorm() > 0.f,
"Invalid Ray" );
16 CORE_ASSERT( !aabb.isEmpty(),
"Empty AABB" );
19 auto nEqualZero = r.direction().array() != Core::Vector3::Zero().array();
23 auto infMin = r.origin().array() < aabb.min().array();
24 auto supMax = r.origin().array() > aabb.max().array();
27 if ( !( infMin.any() ) && !( supMax.any() ) ) {
29 normalOut = -r.direction();
34 const Core::Vector3 invDir = r.direction().cwiseInverse();
35 const Core::Vector3 minOrig = ( aabb.min() - r.origin() ).cwiseProduct( invDir );
36 const Core::Vector3 maxOrig = ( aabb.max() - r.origin() ).cwiseProduct( invDir );
44 auto maxT = nEqualZero.select(
45 infMin.select( minOrig.array(),
51 const Scalar t = maxT.maxCoeff( &i );
54 const Vector3 p = r.pointAt( t );
55 const Vector3 s = Vector3::Unit( i );
58 s.select( 0, ( p.array() < aabb.min().array() || p.array() > aabb.max().array() ) );
61 if ( t >= 0 && !inFace.any() ) {
63 normalOut = -s *
Math::sign( r.direction()[i] );
69bool RayCastSphere(
const Ray& r,
70 const Core::Vector3& center,
74 CORE_ASSERT( r.direction().squaredNorm() > 0.f,
"Invalid Ray" );
75 CORE_ASSERT( radius > 0.f,
"Invalid radius" );
81 const Core::Vector3 co = r.origin() - center;
82 const Scalar co2 = co.squaredNorm();
83 const Scalar dirDotCO = r.direction().dot( co );
87 const Scalar c = co2 - ( radius * radius );
88 const Scalar b = 2.f * dirDotCO;
90 const Scalar delta = ( b * b ) - ( 4.f * c );
93 const Scalar t = -b * 0.5f;
94 const bool tPositive = ( t >= 0.f );
95 if ( tPositive ) { hitsOut.
push_back( t ); }
98 else if ( delta > 0.f ) {
99 const Scalar t1 = ( -b -
std::sqrt( delta ) ) * 0.5f;
100 const Scalar t2 = ( -b +
std::sqrt( delta ) ) * 0.5f;
103 CORE_ASSERT( t1 < t2,
"Your math is wrong." );
105 const bool t1Positive = ( t1 >= 0.f );
106 const bool t2Positive = ( t2 >= 0.f );
107 if ( t1Positive ) { hitsOut.
push_back( t1 ); }
108 if ( t2Positive ) { hitsOut.
push_back( t2 ); }
110 return ( t1Positive || t2Positive );
115bool RayCastPlane(
const Ray& r,
116 const Core::Vector3& a,
117 const Core::Vector3& normal,
119 CORE_ASSERT( r.direction().squaredNorm() > 0.f,
"Invalid Ray" );
120 CORE_ASSERT( normal.squaredNorm() > 0.f,
"Invalid plane normal" );
127 const Scalar ddotn = r.direction().dot( normal );
128 const Scalar OAdotn = ( a - r.origin() ).dot( normal );
132 if ( ddotn != 0 && ( ddotn * OAdotn ) >= 0 ) {
138 else if ( ddotn == 0 && OAdotn == 0 ) {
147bool RayCastCylinder(
const Ray& r,
148 const Core::Vector3& a,
149 const Core::Vector3& b,
154 const Scalar radiusSquared = radius * radius;
156 const Core::Vector3 cylAxis = b - a;
157 const Core::Vector3 ao = r.origin() - a;
161 const bool vsA = RayCastPlane( r, a, cylAxis, hitsA );
162 const Scalar hitA = vsA ? hitsA[0] : -1.f;
165 const bool vsB = RayCastPlane( r, b, cylAxis, hitsB );
166 const Scalar hitB = vsB ? hitsB[0] : -1.f;
168 auto n = r.direction().cross( cylAxis );
170 if ( UNLIKELY( n.squaredNorm() == 0 ) ) {
172 const Scalar distSquared =
173 ao.cross( r.direction() ).squaredNorm() / r.direction().squaredNorm();
176 if ( distSquared <= ( radiusSquared ) ) {
178 CORE_ASSERT( vsA || vsB,
"Ray must hit at least one of the planes !" );
182 if ( LIKELY( vsA && vsB ) ) {
183 CORE_ASSERT(
std::min( hitA, hitB ) >= 0,
"Invalid hit result" );
198 const Scalar ln = n.norm();
202 const Scalar dist = std::abs( ao.dot( n ) );
207 if ( dist <= radius ) {
209 auto v1 = cylAxis.cross( ao );
210 const Scalar t = v1.dot( n ) / ln;
211 auto v2 = n.cross( cylAxis ).normalized();
213 std::sqrt( radiusSquared - ( dist * dist ) ) / std::abs( r.direction().dot( v2 ) );
217 CORE_ASSERT( tIn <= tOut,
" " );
221 const Scalar ddotAxis = r.direction().dot( cylAxis );
225 if ( ddotAxis < 0 ) {
226 const Scalar tInPlaneB = ( b - r.origin() ).dot( cylAxis ) / ddotAxis;
227 const Scalar tOutPlaneA = ( a - r.origin() ).dot( cylAxis ) / ddotAxis;
230 if ( tInPlaneB > tOut || tOutPlaneA < tIn ) {
return false; }
232 if ( tInPlaneB > tIn && tInPlaneB < tOut ) { tIn = tInPlaneB; }
233 if ( tOutPlaneA > tIn && tOutPlaneA < tOut ) { tOut = tOutPlaneA; }
237 else if ( ddotAxis > 0 ) {
238 const Scalar tInPlaneA = ( a - r.origin() ).dot( cylAxis ) / ddotAxis;
239 const Scalar tOutPlaneB = ( b - r.origin() ).dot( cylAxis ) / ddotAxis;
242 if ( tInPlaneA > tOut || tOutPlaneB < tIn ) {
return false; }
244 if ( tInPlaneA > tIn && tInPlaneA < tOut ) { tIn = tInPlaneA; }
246 if ( tOutPlaneB > tIn && tOutPlaneB < tOut ) { tOut = tOutPlaneB; }
250 else if ( UNLIKELY( ddotAxis == 0 ) ) {
251 const Scalar h = ao.dot( cylAxis );
252 const Scalar lAxisSq = cylAxis.squaredNorm();
253 if ( h < 0 || h > lAxisSq ) {
return false; }
264 else if ( tIn * tOut < 0 ) {
274bool RayCastTriangle(
const Ray& ray,
279 const Vector3 ab = b - a;
280 const Vector3 ac = c - a;
282 ON_ASSERT(
const Vector3 n = ab.cross( ac ) );
283 CORE_ASSERT( n.squaredNorm() > 0,
"Degenerate triangle" );
286 const Vector3 pvec = ray.direction().cross( ac );
287 const Scalar det = ab.dot( pvec );
289 const Vector3 tvec = ray.origin() - a;
290 const Scalar inv_det = 1.f / det;
292 const Vector3 qvec = tvec.cross( ab );
295 Scalar u = tvec.dot( pvec );
296 if ( u < 0 || u > det ) {
300 Scalar v = ray.direction().dot( qvec );
301 if ( v < 0 || u + v > det ) {
return false; }
303 else if ( det < 0 ) {
304 Scalar u = tvec.dot( pvec );
305 if ( u > 0 || u < det ) {
return false; }
307 Scalar v = ray.direction().dot( qvec );
308 if ( v > 0 || u + v < det ) {
return false; }
316 const Scalar t = ac.dot( qvec ) * inv_det;
317 if ( t >= 0 ) { hitsOut.
push_back( t ); }
321bool RayCastTriangleMesh(
const Ray& r,
322 const TriangleMesh& mesh,
326 for (
size_t i = 0; i < mesh.getIndices().size(); ++i ) {
327 const auto& t = mesh.getIndices()[i];
328 const Vector3& a = mesh.vertices()[t[0]];
329 const Vector3& b = mesh.vertices()[t[1]];
330 const Vector3& c = mesh.vertices()[t[2]];
331 if ( RayCastTriangle( r, a, b, c, hitsOut ) ) {
constexpr int sign(const T &val)
Returns the sign of any numeric type as { -1, 0, 1}.
@ Geometry
"Geometry" render objects are those loaded using Radium::IO and generated by GeometrySystem
hepler function to manage enum as underlying types in VariableSet