// Copyright 2009-2021 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #pragma once #include "../common/ray.h" namespace embree { namespace isa { struct Cylinder { const Vec3fa p0; //!< start location const Vec3fa p1; //!< end position const float rr; //!< squared radius of cylinder __forceinline Cylinder(const Vec3fa& p0, const Vec3fa& p1, const float r) : p0(p0), p1(p1), rr(sqr(r)) {} __forceinline Cylinder(const Vec3fa& p0, const Vec3fa& p1, const float rr, bool) : p0(p0), p1(p1), rr(rr) {} __forceinline bool intersect(const Vec3fa& org, const Vec3fa& dir, BBox1f& t_o, float& u0_o, Vec3fa& Ng0_o, float& u1_o, Vec3fa& Ng1_o) const { /* calculate quadratic equation to solve */ const float rl = rcp_length(p1-p0); const Vec3fa P0 = p0, dP = (p1-p0)*rl; const Vec3fa O = org-P0, dO = dir; const float dOdO = dot(dO,dO); const float OdO = dot(dO,O); const float OO = dot(O,O); const float dOz = dot(dP,dO); const float Oz = dot(dP,O); const float A = dOdO - sqr(dOz); const float B = 2.0f * (OdO - dOz*Oz); const float C = OO - sqr(Oz) - rr; /* we miss the cylinder if determinant is smaller than zero */ const float D = B*B - 4.0f*A*C; if (D < 0.0f) { t_o = BBox1f(pos_inf,neg_inf); return false; } /* special case for rays that are parallel to the cylinder */ const float eps = 16.0f*float(ulp)*max(abs(dOdO),abs(sqr(dOz))); if (abs(A) < eps) { if (C <= 0.0f) { t_o = BBox1f(neg_inf,pos_inf); return true; } else { t_o = BBox1f(pos_inf,neg_inf); return false; } } /* standard case for rays that are not parallel to the cylinder */ const float Q = sqrt(D); const float rcp_2A = rcp(2.0f*A); const float t0 = (-B-Q)*rcp_2A; const float t1 = (-B+Q)*rcp_2A; /* calculates u and Ng for near hit */ { u0_o = madd(t0,dOz,Oz)*rl; const Vec3fa Pr = t0*dir; const Vec3fa Pl = madd(u0_o,p1-p0,p0); Ng0_o = Pr-Pl; } /* calculates u and Ng for far hit */ { u1_o = madd(t1,dOz,Oz)*rl; const Vec3fa Pr = t1*dir; const Vec3fa Pl = madd(u1_o,p1-p0,p0); Ng1_o = Pr-Pl; } t_o.lower = t0; t_o.upper = t1; return true; } __forceinline bool intersect(const Vec3fa& org_i, const Vec3fa& dir, BBox1f& t_o) const { float u0_o; Vec3fa Ng0_o; float u1_o; Vec3fa Ng1_o; return intersect(org_i,dir,t_o,u0_o,Ng0_o,u1_o,Ng1_o); } static bool verify(const size_t id, const Cylinder& cylinder, const RayHit& ray, bool shouldhit, const float t0, const float t1) { float eps = 0.001f; BBox1f t; bool hit; hit = cylinder.intersect(ray.org,ray.dir,t); bool failed = hit != shouldhit; if (shouldhit) failed |= std::isinf(t0) ? t0 != t.lower : abs(t0-t.lower) > eps; if (shouldhit) failed |= std::isinf(t1) ? t1 != t.upper : abs(t1-t.upper) > eps; if (!failed) return true; embree_cout << "Cylinder test " << id << " failed: cylinder = " << cylinder << ", ray = " << ray << ", hit = " << hit << ", t = " << t << embree_endl; return false; } /* verify cylinder class */ static bool verify() { bool passed = true; const Cylinder cylinder(Vec3fa(0.0f,0.0f,0.0f),Vec3fa(1.0f,0.0f,0.0f),1.0f); passed &= verify(0,cylinder,RayHit(Vec3fa(-2.0f,1.0f,0.0f),Vec3fa( 0.0f,-1.0f,+0.0f),0.0f,float(inf)),true,0.0f,2.0f); passed &= verify(1,cylinder,RayHit(Vec3fa(+2.0f,1.0f,0.0f),Vec3fa( 0.0f,-1.0f,+0.0f),0.0f,float(inf)),true,0.0f,2.0f); passed &= verify(2,cylinder,RayHit(Vec3fa(+2.0f,1.0f,2.0f),Vec3fa( 0.0f,-1.0f,+0.0f),0.0f,float(inf)),false,0.0f,0.0f); passed &= verify(3,cylinder,RayHit(Vec3fa(+0.0f,0.0f,0.0f),Vec3fa( 1.0f, 0.0f,+0.0f),0.0f,float(inf)),true,neg_inf,pos_inf); passed &= verify(4,cylinder,RayHit(Vec3fa(+0.0f,0.0f,0.0f),Vec3fa(-1.0f, 0.0f,+0.0f),0.0f,float(inf)),true,neg_inf,pos_inf); passed &= verify(5,cylinder,RayHit(Vec3fa(+0.0f,2.0f,0.0f),Vec3fa( 1.0f, 0.0f,+0.0f),0.0f,float(inf)),false,pos_inf,neg_inf); passed &= verify(6,cylinder,RayHit(Vec3fa(+0.0f,2.0f,0.0f),Vec3fa(-1.0f, 0.0f,+0.0f),0.0f,float(inf)),false,pos_inf,neg_inf); return passed; } /*! output operator */ friend __forceinline embree_ostream operator<<(embree_ostream cout, const Cylinder& c) { return cout << "Cylinder { p0 = " << c.p0 << ", p1 = " << c.p1 << ", r = " << sqrtf(c.rr) << "}"; } }; template struct CylinderN { const Vec3vf p0; //!< start location const Vec3vf p1; //!< end position const vfloat rr; //!< squared radius of cylinder __forceinline CylinderN(const Vec3vf& p0, const Vec3vf& p1, const vfloat& r) : p0(p0), p1(p1), rr(sqr(r)) {} __forceinline CylinderN(const Vec3vf& p0, const Vec3vf& p1, const vfloat& rr, bool) : p0(p0), p1(p1), rr(rr) {} __forceinline vbool intersect(const Vec3fa& org, const Vec3fa& dir, BBox>& t_o, vfloat& u0_o, Vec3vf& Ng0_o, vfloat& u1_o, Vec3vf& Ng1_o) const { /* calculate quadratic equation to solve */ const vfloat rl = rcp_length(p1-p0); const Vec3vf P0 = p0, dP = (p1-p0)*rl; const Vec3vf O = Vec3vf(org)-P0, dO = dir; const vfloat dOdO = dot(dO,dO); const vfloat OdO = dot(dO,O); const vfloat OO = dot(O,O); const vfloat dOz = dot(dP,dO); const vfloat Oz = dot(dP,O); const vfloat A = dOdO - sqr(dOz); const vfloat B = 2.0f * (OdO - dOz*Oz); const vfloat C = OO - sqr(Oz) - rr; /* we miss the cylinder if determinant is smaller than zero */ const vfloat D = B*B - 4.0f*A*C; vbool valid = D >= 0.0f; if (none(valid)) { t_o = BBox>(empty); return valid; } /* standard case for rays that are not parallel to the cylinder */ const vfloat Q = sqrt(D); const vfloat rcp_2A = rcp(2.0f*A); const vfloat t0 = (-B-Q)*rcp_2A; const vfloat t1 = (-B+Q)*rcp_2A; /* calculates u and Ng for near hit */ { u0_o = madd(t0,dOz,Oz)*rl; const Vec3vf Pr = t0*Vec3vf(dir); const Vec3vf Pl = madd(u0_o,p1-p0,p0); Ng0_o = Pr-Pl; } /* calculates u and Ng for far hit */ { u1_o = madd(t1,dOz,Oz)*rl; const Vec3vf Pr = t1*Vec3vf(dir); const Vec3vf Pl = madd(u1_o,p1-p0,p0); Ng1_o = Pr-Pl; } t_o.lower = select(valid, t0, vfloat(pos_inf)); t_o.upper = select(valid, t1, vfloat(neg_inf)); /* special case for rays that are parallel to the cylinder */ const vfloat eps = 16.0f*float(ulp)*max(abs(dOdO),abs(sqr(dOz))); vbool validt = valid & (abs(A) < eps); if (unlikely(any(validt))) { vbool inside = C <= 0.0f; t_o.lower = select(validt,select(inside,vfloat(neg_inf),vfloat(pos_inf)),t_o.lower); t_o.upper = select(validt,select(inside,vfloat(pos_inf),vfloat(neg_inf)),t_o.upper); valid &= !validt | inside; } return valid; } __forceinline vbool intersect(const Vec3fa& org_i, const Vec3fa& dir, BBox>& t_o) const { vfloat u0_o; Vec3vf Ng0_o; vfloat u1_o; Vec3vf Ng1_o; return intersect(org_i,dir,t_o,u0_o,Ng0_o,u1_o,Ng1_o); } }; } }