| 1 | // Copyright 2009-2021 Intel Corporation |
| 2 | // SPDX-License-Identifier: Apache-2.0 |
| 3 | |
| 4 | #pragma once |
| 5 | |
| 6 | #include "../common/ray.h" |
| 7 | #include "curve_intersector_precalculations.h" |
| 8 | |
| 9 | |
| 10 | /* |
| 11 | |
| 12 | This file implements the intersection of a ray with a round linear |
| 13 | curve segment. We define the geometry of such a round linear curve |
| 14 | segment from point p0 with radius r0 to point p1 with radius r1 |
| 15 | using the cone that touches spheres p0/r0 and p1/r1 tangentially |
| 16 | plus the sphere p1/r1. We denote the tangentially touching cone from |
| 17 | p0/r0 to p1/r1 with cone(p0,r0,p1,r1) and the cone plus the ending |
| 18 | sphere with cone_sphere(p0,r0,p1,r1). |
| 19 | |
| 20 | For multiple connected round linear curve segments this construction |
| 21 | yield a proper shape when viewed from the outside. Using the |
| 22 | following CSG we can also handle the interior in most common cases: |
| 23 | |
| 24 | round_linear_curve(pl,rl,p0,r0,p1,r1,pr,rr) = |
| 25 | cone_sphere(p0,r0,p1,r1) - cone(pl,rl,p0,r0) - cone(p1,r1,pr,rr) |
| 26 | |
| 27 | Thus by subtracting the neighboring cone geometries, we cut away |
| 28 | parts of the center cone_sphere surface which lie inside the |
| 29 | combined curve. This approach works as long as geometry of the |
| 30 | current cone_sphere penetrates into direct neighbor segments only, |
| 31 | and not into segments further away. |
| 32 | |
| 33 | To construct a cone that touches two spheres at p0 and p1 with r0 |
| 34 | and r1, one has to increase the cone radius at r0 and r1 to obtain |
| 35 | larger radii w0 and w1, such that the infinite cone properly touches |
| 36 | the spheres. From the paper "Ray Tracing Generalized Tube |
| 37 | Primitives: Method and Applications" |
| 38 | (https://www.researchgate.net/publication/334378683_Ray_Tracing_Generalized_Tube_Primitives_Method_and_Applications) |
| 39 | one can derive the following equations for these increased |
| 40 | radii: |
| 41 | |
| 42 | sr = 1.0f / sqrt(1-sqr(dr)/sqr(p1-p0)) |
| 43 | w0 = sr*r0 |
| 44 | w1 = sr*r1 |
| 45 | |
| 46 | Further, we want the cone to start where it touches the sphere at p0 |
| 47 | and to end where it touches sphere at p1. Therefore, we need to |
| 48 | construct clipping locations y0 and y1 for the start and end of the |
| 49 | cone. These start and end clipping location of the cone can get |
| 50 | calculated as: |
| 51 | |
| 52 | Y0 = - r0 * (r1-r0) / length(p1-p0) |
| 53 | Y1 = length(p1-p0) - r1 * (r1-r0) / length(p1-p0) |
| 54 | |
| 55 | Where the cone starts a distance Y0 and ends a distance Y1 away of |
| 56 | point p0 along the cone center. The distance between Y1-Y0 can get |
| 57 | calculated as: |
| 58 | |
| 59 | dY = length(p1-p0) - (r1-r0)^2 / length(p1-p0) |
| 60 | |
| 61 | In the code below, Y will always be scaled by length(p1-p0) to |
| 62 | obtain y and you will find the terms r0*(r1-r0) and |
| 63 | (p1-p0)^2-(r1-r0)^2. |
| 64 | |
| 65 | */ |
| 66 | |
| 67 | namespace embree |
| 68 | { |
| 69 | namespace isa |
| 70 | { |
| 71 | template<int M> |
| 72 | struct RoundLineIntersectorHitM |
| 73 | { |
| 74 | __forceinline RoundLineIntersectorHitM() {} |
| 75 | |
| 76 | __forceinline RoundLineIntersectorHitM(const vfloat<M>& u, const vfloat<M>& v, const vfloat<M>& t, const Vec3vf<M>& Ng) |
| 77 | : vu(u), vv(v), vt(t), vNg(Ng) {} |
| 78 | |
| 79 | __forceinline void finalize() {} |
| 80 | |
| 81 | __forceinline Vec2f uv (const size_t i) const { return Vec2f(vu[i],vv[i]); } |
| 82 | __forceinline float t (const size_t i) const { return vt[i]; } |
| 83 | __forceinline Vec3fa Ng(const size_t i) const { return Vec3fa(vNg.x[i],vNg.y[i],vNg.z[i]); } |
| 84 | |
| 85 | __forceinline Vec2vf<M> uv() const { return Vec2vf<M>(vu,vv); } |
| 86 | __forceinline vfloat<M> t () const { return vt; } |
| 87 | __forceinline Vec3vf<M> Ng() const { return vNg; } |
| 88 | |
| 89 | public: |
| 90 | vfloat<M> vu; |
| 91 | vfloat<M> vv; |
| 92 | vfloat<M> vt; |
| 93 | Vec3vf<M> vNg; |
| 94 | }; |
| 95 | |
| 96 | namespace __roundline_internal |
| 97 | { |
| 98 | template<int M> |
| 99 | struct ConeGeometry |
| 100 | { |
| 101 | ConeGeometry (const Vec4vf<M>& a, const Vec4vf<M>& b) |
| 102 | : p0(a.xyz()), p1(b.xyz()), dP(p1-p0), dPdP(dot(dP,dP)), r0(a.w), sqr_r0(sqr(r0)), r1(b.w), dr(r1-r0), drdr(dr*dr), r0dr (r0*dr), g(dPdP - drdr) {} |
| 103 | |
| 104 | /* |
| 105 | |
| 106 | This function tests if a point is accepted by first cone |
| 107 | clipping plane. |
| 108 | |
| 109 | First, we need to project the point onto the line p0->p1: |
| 110 | |
| 111 | Y = (p-p0)*(p1-p0)/length(p1-p0) |
| 112 | |
| 113 | This value y is the distance to the projection point from |
| 114 | p0. The clip distances are calculated as: |
| 115 | |
| 116 | Y0 = - r0 * (r1-r0) / length(p1-p0) |
| 117 | Y1 = length(p1-p0) - r1 * (r1-r0) / length(p1-p0) |
| 118 | |
| 119 | Thus to test if the point p is accepted by the first |
| 120 | clipping plane we need to test Y > Y0 and to test if it |
| 121 | is accepted by the second clipping plane we need to test |
| 122 | Y < Y1. |
| 123 | |
| 124 | By multiplying the calculations with length(p1-p0) these |
| 125 | calculation can get simplied to: |
| 126 | |
| 127 | y = (p-p0)*(p1-p0) |
| 128 | y0 = - r0 * (r1-r0) |
| 129 | y1 = (p1-p0)^2 - r1 * (r1-r0) |
| 130 | |
| 131 | and the test y > y0 and y < y1. |
| 132 | |
| 133 | */ |
| 134 | |
| 135 | __forceinline vbool<M> isClippedByPlane (const vbool<M>& valid_i, const Vec3vf<M>& p) const |
| 136 | { |
| 137 | const Vec3vf<M> p0p = p - p0; |
| 138 | const vfloat<M> y = dot(p0p,dP); |
| 139 | const vfloat<M> cap0 = -r0dr; |
| 140 | const vbool<M> inside_cone = y > cap0; |
| 141 | return valid_i & (p0.x != vfloat<M>(inf)) & (p1.x != vfloat<M>(inf)) & inside_cone; |
| 142 | } |
| 143 | |
| 144 | /* |
| 145 | |
| 146 | This function tests whether a point lies inside the capped cone |
| 147 | tangential to its ending spheres. |
| 148 | |
| 149 | Therefore one has to check if the point is inside the |
| 150 | region defined by the cone clipping planes, which is |
| 151 | performed similar as in the previous function. |
| 152 | |
| 153 | To perform the inside cone test we need to project the |
| 154 | point onto the line p0->p1: |
| 155 | |
| 156 | dP = p1-p0 |
| 157 | Y = (p-p0)*dP/length(dP) |
| 158 | |
| 159 | This value Y is the distance to the projection point from |
| 160 | p0. To obtain a parameter value u going from 0 to 1 along |
| 161 | the line p0->p1 we calculate: |
| 162 | |
| 163 | U = Y/length(dP) |
| 164 | |
| 165 | The radii to use at points p0 and p1 are: |
| 166 | |
| 167 | w0 = sr * r0 |
| 168 | w1 = sr * r1 |
| 169 | dw = w1-w0 |
| 170 | |
| 171 | Using these radii and u one can directly test if the point |
| 172 | lies inside the cone using the formula dP*dP < wy*wy with: |
| 173 | |
| 174 | wy = w0 + u*dw |
| 175 | py = p0 + u*dP - p |
| 176 | |
| 177 | By multiplying the calculations with length(p1-p0) and |
| 178 | inserting the definition of w can obtain simpler equations: |
| 179 | |
| 180 | y = (p-p0)*dP |
| 181 | ry = r0 + y/dP^2 * dr |
| 182 | wy = sr*ry |
| 183 | py = p0 + y/dP^2*dP - p |
| 184 | y0 = - r0 * dr |
| 185 | y1 = dP^2 - r1 * dr |
| 186 | |
| 187 | Thus for the in-cone test we get: |
| 188 | |
| 189 | py^2 < wy^2 |
| 190 | <=> py^2 < sr^2 * ry^2 |
| 191 | <=> py^2 * ( dP^2 - dr^2 ) < dP^2 * ry^2 |
| 192 | |
| 193 | This can further get simplified to: |
| 194 | |
| 195 | (p0-p)^2 * (dP^2 - dr^2) - y^2 < dP^2 * r0^2 + 2.0f*r0*dr*y; |
| 196 | |
| 197 | */ |
| 198 | |
| 199 | __forceinline vbool<M> isInsideCappedCone (const vbool<M>& valid_i, const Vec3vf<M>& p) const |
| 200 | { |
| 201 | const Vec3vf<M> p0p = p - p0; |
| 202 | const vfloat<M> y = dot(p0p,dP); |
| 203 | const vfloat<M> cap0 = -r0dr+vfloat<M>(ulp); |
| 204 | const vfloat<M> cap1 = -r1*dr + dPdP; |
| 205 | |
| 206 | vbool<M> inside_cone = valid_i & (p0.x != vfloat<M>(inf)) & (p1.x != vfloat<M>(inf)); |
| 207 | inside_cone &= y > cap0; // start clipping plane |
| 208 | inside_cone &= y < cap1; // end clipping plane |
| 209 | inside_cone &= sqr(p0p)*g - sqr(y) < dPdP * sqr_r0 + 2.0f*r0dr*y; // in cone test |
| 210 | return inside_cone; |
| 211 | } |
| 212 | |
| 213 | protected: |
| 214 | Vec3vf<M> p0; |
| 215 | Vec3vf<M> p1; |
| 216 | Vec3vf<M> dP; |
| 217 | vfloat<M> dPdP; |
| 218 | vfloat<M> r0; |
| 219 | vfloat<M> sqr_r0; |
| 220 | vfloat<M> r1; |
| 221 | vfloat<M> dr; |
| 222 | vfloat<M> drdr; |
| 223 | vfloat<M> r0dr; |
| 224 | vfloat<M> g; |
| 225 | }; |
| 226 | |
| 227 | template<int M> |
| 228 | struct ConeGeometryIntersector : public ConeGeometry<M> |
| 229 | { |
| 230 | using ConeGeometry<M>::p0; |
| 231 | using ConeGeometry<M>::p1; |
| 232 | using ConeGeometry<M>::dP; |
| 233 | using ConeGeometry<M>::dPdP; |
| 234 | using ConeGeometry<M>::r0; |
| 235 | using ConeGeometry<M>::sqr_r0; |
| 236 | using ConeGeometry<M>::r1; |
| 237 | using ConeGeometry<M>::dr; |
| 238 | using ConeGeometry<M>::r0dr; |
| 239 | using ConeGeometry<M>::g; |
| 240 | |
| 241 | ConeGeometryIntersector (const Vec3vf<M>& ray_org, const Vec3vf<M>& ray_dir, const vfloat<M>& dOdO, const vfloat<M>& rcp_dOdO, const Vec4vf<M>& a, const Vec4vf<M>& b) |
| 242 | : ConeGeometry<M>(a,b), org(ray_org), O(ray_org-p0), dO(ray_dir), dOdO(dOdO), rcp_dOdO(rcp_dOdO), OdP(dot(dP,O)), dOdP(dot(dP,dO)), yp(OdP + r0dr) {} |
| 243 | |
| 244 | /* |
| 245 | |
| 246 | This function intersects a ray with a cone that touches a |
| 247 | start sphere p0/r0 and end sphere p1/r1. |
| 248 | |
| 249 | To find this ray/cone intersections one could just |
| 250 | calculate radii w0 and w1 as described above and use a |
| 251 | standard ray/cone intersection routine with these |
| 252 | radii. However, it turns out that calculations can get |
| 253 | simplified when deriving a specialized ray/cone |
| 254 | intersection for this special case. We perform |
| 255 | calculations relative to the cone origin p0 and define: |
| 256 | |
| 257 | O = ray_org - p0 |
| 258 | dO = ray_dir |
| 259 | dP = p1-p0 |
| 260 | dr = r1-r0 |
| 261 | dw = w1-w0 |
| 262 | |
| 263 | For some t we can compute the potential hit point h = O + t*dO and |
| 264 | project it onto the cone vector dP to obtain u = (h*dP)/(dP*dP). In |
| 265 | case of an intersection, the squared distance from the hit point |
| 266 | projected onto the cone center line to the hit point should be equal |
| 267 | to the squared cone radius at u: |
| 268 | |
| 269 | (u*dP - h)^2 = (w0 + u*dw)^2 |
| 270 | |
| 271 | Inserting the definition of h, u, w0, and dw into this formula, then |
| 272 | factoring out all terms, and sorting by t^2, t^1, and t^0 terms |
| 273 | yields a quadratic equation to solve. |
| 274 | |
| 275 | Inserting u: |
| 276 | ( (h*dP)*dP/dP^2 - h )^2 = ( w0 + (h*dP)*dw/dP^2 )^2 |
| 277 | |
| 278 | Multiplying by dP^4: |
| 279 | ( (h*dP)*dP - h*dP^2 )^2 = ( w0*dP^2 + (h*dP)*dw )^2 |
| 280 | |
| 281 | Inserting w0 and dw: |
| 282 | ( (h*dP)*dP - h*dP^2 )^2 = ( r0*dP^2 + (h*dP)*dr )^2 / (1-dr^2/dP^2) |
| 283 | ( (h*dP)*dP - h*dP^2 )^2 *(dP^2 - dr^2) = dP^2 * ( r0*dP^2 + (h*dP)*dr )^2 |
| 284 | |
| 285 | Now one can insert the definition of h, factor out, and presort by t: |
| 286 | ( ((O + t*dO)*dP)*dP - (O + t*dO)*dP^2 )^2 *(dP^2 - dr^2) = dP^2 * ( r0*dP^2 + ((O + t*dO)*dP)*dr )^2 |
| 287 | ( (O*dP)*dP-O*dP^2 + t*( (dO*dP)*dP - dO*dP^2 ) )^2 *(dP^2 - dr^2) = dP^2 * ( r0*dP^2 + (O*dP)*dr + t*(dO*dP)*dr )^2 |
| 288 | |
| 289 | Factoring out further and sorting by t^2, t^1 and t^0 yields: |
| 290 | |
| 291 | 0 = t^2 * [ ((dO*dP)*dP - dO-dP^2)^2 * (dP^2 - dr^2) - dP^2*(dO*dP)^2*dr^2 ] |
| 292 | + 2*t^1 * [ ((O*dP)*dP - O*dP^2) * ((dO*dP)*dP - dO*dP^2) * (dP^2 - dr^2) - dP^2*(r0*dP^2 + (O*dP)*dr)*(dO*dP)*dr ] |
| 293 | + t^0 * [ ( (O*dP)*dP - O*dP^2)^2 * (dP^2-dr^2) - dP^2*(r0*dP^2 + (O*dP)*dr)^2 ] |
| 294 | |
| 295 | This can be simplified to: |
| 296 | |
| 297 | 0 = t^2 * [ (dP^2 - dr^2)*dO^2 - (dO*dP)^2 ] |
| 298 | + 2*t^1 * [ (dP^2 - dr^2)*(O*dO) - (dO*dP)*(O*dP + r0*dr) ] |
| 299 | + t^0 * [ (dP^2 - dr^2)*O^2 - (O*dP)^2 - r0^2*dP^2 - 2.0f*r0*dr*(O*dP) ] |
| 300 | |
| 301 | Solving this quadratic equation yields the values for t at which the |
| 302 | ray intersects the cone. |
| 303 | |
| 304 | */ |
| 305 | |
| 306 | __forceinline bool intersectCone(vbool<M>& valid, vfloat<M>& lower, vfloat<M>& upper) |
| 307 | { |
| 308 | /* return no hit by default */ |
| 309 | lower = pos_inf; |
| 310 | upper = neg_inf; |
| 311 | |
| 312 | /* compute quadratic equation A*t^2 + B*t + C = 0 */ |
| 313 | const vfloat<M> OO = dot(O,O); |
| 314 | const vfloat<M> OdO = dot(dO,O); |
| 315 | const vfloat<M> A = g * dOdO - sqr(dOdP); |
| 316 | const vfloat<M> B = 2.0f * (g*OdO - dOdP*yp); |
| 317 | const vfloat<M> C = g*OO - sqr(OdP) - sqr_r0*dPdP - 2.0f*r0dr*OdP; |
| 318 | |
| 319 | /* we miss the cone if determinant is smaller than zero */ |
| 320 | const vfloat<M> D = B*B - 4.0f*A*C; |
| 321 | valid &= (D >= 0.0f & g > 0.0f); // if g <= 0 then the cone is inside a sphere end |
| 322 | |
| 323 | /* When rays are parallel to the cone surface, then the |
| 324 | * ray may be inside or outside the cone. We just assume a |
| 325 | * miss in that case, which is fine as rays inside the |
| 326 | * cone would anyway hit the ending spheres in that |
| 327 | * case. */ |
| 328 | valid &= abs(A) > min_rcp_input; |
| 329 | if (unlikely(none(valid))) { |
| 330 | return false; |
| 331 | } |
| 332 | |
| 333 | /* compute distance to front and back hit */ |
| 334 | const vfloat<M> Q = sqrt(D); |
| 335 | const vfloat<M> rcp_2A = rcp(2.0f*A); |
| 336 | t_cone_front = (-B-Q)*rcp_2A; |
| 337 | y_cone_front = yp + t_cone_front*dOdP; |
| 338 | lower = select( (y_cone_front > -(float)ulp) & (y_cone_front <= g) & (g > 0.0f), t_cone_front, vfloat<M>(pos_inf)); |
| 339 | #if !defined (EMBREE_BACKFACE_CULLING_CURVES) |
| 340 | t_cone_back = (-B+Q)*rcp_2A; |
| 341 | y_cone_back = yp + t_cone_back *dOdP; |
| 342 | upper = select( (y_cone_back > -(float)ulp) & (y_cone_back <= g) & (g > 0.0f), t_cone_back , vfloat<M>(neg_inf)); |
| 343 | #endif |
| 344 | return true; |
| 345 | } |
| 346 | |
| 347 | /* |
| 348 | This function intersects the ray with the end sphere at |
| 349 | p1. We already clip away hits that are inside the |
| 350 | neighboring cone segment. |
| 351 | |
| 352 | */ |
| 353 | |
| 354 | __forceinline void intersectEndSphere(vbool<M>& valid, |
| 355 | const ConeGeometry<M>& coneR, |
| 356 | vfloat<M>& lower, vfloat<M>& upper) |
| 357 | { |
| 358 | /* calculate front and back hit with end sphere */ |
| 359 | const Vec3vf<M> O1 = org - p1; |
| 360 | const vfloat<M> O1dO = dot(O1,dO); |
| 361 | const vfloat<M> h2 = sqr(O1dO) - dOdO*(sqr(O1) - sqr(r1)); |
| 362 | const vfloat<M> rhs1 = select( h2 >= 0.0f, sqrt(h2), vfloat<M>(neg_inf) ); |
| 363 | |
| 364 | /* clip away front hit if it is inside next cone segment */ |
| 365 | t_sph1_front = (-O1dO - rhs1)*rcp_dOdO; |
| 366 | const Vec3vf<M> hit_front = org + t_sph1_front*dO; |
| 367 | vbool<M> valid_sph1_front = h2 >= 0.0f & yp + t_sph1_front*dOdP > g & !coneR.isClippedByPlane (valid, hit_front); |
| 368 | lower = select(valid_sph1_front, t_sph1_front, vfloat<M>(pos_inf)); |
| 369 | |
| 370 | #if !defined(EMBREE_BACKFACE_CULLING_CURVES) |
| 371 | /* clip away back hit if it is inside next cone segment */ |
| 372 | t_sph1_back = (-O1dO + rhs1)*rcp_dOdO; |
| 373 | const Vec3vf<M> hit_back = org + t_sph1_back*dO; |
| 374 | vbool<M> valid_sph1_back = h2 >= 0.0f & yp + t_sph1_back*dOdP > g & !coneR.isClippedByPlane (valid, hit_back); |
| 375 | upper = select(valid_sph1_back, t_sph1_back, vfloat<M>(neg_inf)); |
| 376 | #else |
| 377 | upper = vfloat<M>(neg_inf); |
| 378 | #endif |
| 379 | } |
| 380 | |
| 381 | __forceinline void intersectBeginSphere(const vbool<M>& valid, |
| 382 | vfloat<M>& lower, vfloat<M>& upper) |
| 383 | { |
| 384 | /* calculate front and back hit with end sphere */ |
| 385 | const Vec3vf<M> O1 = org - p0; |
| 386 | const vfloat<M> O1dO = dot(O1,dO); |
| 387 | const vfloat<M> h2 = sqr(O1dO) - dOdO*(sqr(O1) - sqr(r0)); |
| 388 | const vfloat<M> rhs1 = select( h2 >= 0.0f, sqrt(h2), vfloat<M>(neg_inf) ); |
| 389 | |
| 390 | /* clip away front hit if it is inside next cone segment */ |
| 391 | t_sph0_front = (-O1dO - rhs1)*rcp_dOdO; |
| 392 | vbool<M> valid_sph1_front = valid & h2 >= 0.0f & yp + t_sph0_front*dOdP < 0; |
| 393 | lower = select(valid_sph1_front, t_sph0_front, vfloat<M>(pos_inf)); |
| 394 | |
| 395 | #if !defined(EMBREE_BACKFACE_CULLING_CURVES) |
| 396 | /* clip away back hit if it is inside next cone segment */ |
| 397 | t_sph0_back = (-O1dO + rhs1)*rcp_dOdO; |
| 398 | vbool<M> valid_sph1_back = valid & h2 >= 0.0f & yp + t_sph0_back*dOdP < 0; |
| 399 | upper = select(valid_sph1_back, t_sph0_back, vfloat<M>(neg_inf)); |
| 400 | #else |
| 401 | upper = vfloat<M>(neg_inf); |
| 402 | #endif |
| 403 | } |
| 404 | |
| 405 | /* |
| 406 | |
| 407 | This function calculates the geometry normal of some cone hit. |
| 408 | |
| 409 | For a given hit point h (relative to p0) with a cone |
| 410 | starting at p0 with radius w0 and ending at p1 with |
| 411 | radius w1 one normally calculates the geometry normal by |
| 412 | first calculating the parmetric u hit location along the |
| 413 | cone: |
| 414 | |
| 415 | u = dot(h,dP)/dP^2 |
| 416 | |
| 417 | Using this value one can now directly calculate the |
| 418 | geometry normal by bending the connection vector (h-u*dP) |
| 419 | from hit to projected hit with some cone dependent value |
| 420 | dw/sqrt(dP^2) * normalize(dP): |
| 421 | |
| 422 | Ng = normalize(h-u*dP) - dw/length(dP) * normalize(dP) |
| 423 | |
| 424 | The length of the vector (h-u*dP) can also get calculated |
| 425 | by interpolating the radii as w0+u*dw which yields: |
| 426 | |
| 427 | Ng = (h-u*dP)/(w0+u*dw) - dw/dP^2 * dP |
| 428 | |
| 429 | Multiplying with (w0+u*dw) yield a scaled Ng': |
| 430 | |
| 431 | Ng' = (h-u*dP) - (w0+u*dw)*dw/dP^2*dP |
| 432 | |
| 433 | Inserting the definition of w0 and dw and refactoring |
| 434 | yield a further scaled Ng'': |
| 435 | |
| 436 | Ng'' = (dP^2 - dr^2) (h-q) - (r0+u*dr)*dr*dP |
| 437 | |
| 438 | Now inserting the definition of u gives and multiplying |
| 439 | with the denominator yields: |
| 440 | |
| 441 | Ng''' = (dP^2-dr^2)*(dP^2*h-dot(h,dP)*dP) - (dP^2*r0+dot(h,dP)*dr)*dr*dP |
| 442 | |
| 443 | Factoring out, cancelling terms, dividing by dP^2, and |
| 444 | factoring again yields finally: |
| 445 | |
| 446 | Ng'''' = (dP^2-dr^2)*h - dP*(dot(h,dP) + r0*dr) |
| 447 | |
| 448 | */ |
| 449 | |
| 450 | __forceinline Vec3vf<M> Ng_cone(const vbool<M>& front_hit) const |
| 451 | { |
| 452 | #if !defined(EMBREE_BACKFACE_CULLING_CURVES) |
| 453 | const vfloat<M> y = select(front_hit, y_cone_front, y_cone_back); |
| 454 | const vfloat<M> t = select(front_hit, t_cone_front, t_cone_back); |
| 455 | const Vec3vf<M> h = O + t*dO; |
| 456 | return g*h-dP*y; |
| 457 | #else |
| 458 | const Vec3vf<M> h = O + t_cone_front*dO; |
| 459 | return g*h-dP*y_cone_front; |
| 460 | #endif |
| 461 | } |
| 462 | |
| 463 | /* compute geometry normal of sphere hit as the difference |
| 464 | * vector from hit point to sphere center */ |
| 465 | |
| 466 | __forceinline Vec3vf<M> Ng_sphere1(const vbool<M>& front_hit) const |
| 467 | { |
| 468 | #if !defined(EMBREE_BACKFACE_CULLING_CURVES) |
| 469 | const vfloat<M> t_sph1 = select(front_hit, t_sph1_front, t_sph1_back); |
| 470 | return org+t_sph1*dO-p1; |
| 471 | #else |
| 472 | return org+t_sph1_front*dO-p1; |
| 473 | #endif |
| 474 | } |
| 475 | |
| 476 | __forceinline Vec3vf<M> Ng_sphere0(const vbool<M>& front_hit) const |
| 477 | { |
| 478 | #if !defined(EMBREE_BACKFACE_CULLING_CURVES) |
| 479 | const vfloat<M> t_sph0 = select(front_hit, t_sph0_front, t_sph0_back); |
| 480 | return org+t_sph0*dO-p0; |
| 481 | #else |
| 482 | return org+t_sph0_front*dO-p0; |
| 483 | #endif |
| 484 | } |
| 485 | |
| 486 | /* |
| 487 | This function calculates the u coordinate of a |
| 488 | hit. Therefore we use the hit distance y (which is zero |
| 489 | at the first cone clipping plane) and divide by distance |
| 490 | g between the clipping planes. |
| 491 | |
| 492 | */ |
| 493 | |
| 494 | __forceinline vfloat<M> u_cone(const vbool<M>& front_hit) const |
| 495 | { |
| 496 | #if !defined(EMBREE_BACKFACE_CULLING_CURVES) |
| 497 | const vfloat<M> y = select(front_hit, y_cone_front, y_cone_back); |
| 498 | return clamp(y*rcp(g)); |
| 499 | #else |
| 500 | return clamp(y_cone_front*rcp(g)); |
| 501 | #endif |
| 502 | } |
| 503 | |
| 504 | private: |
| 505 | Vec3vf<M> org; |
| 506 | Vec3vf<M> O; |
| 507 | Vec3vf<M> dO; |
| 508 | vfloat<M> dOdO; |
| 509 | vfloat<M> rcp_dOdO; |
| 510 | vfloat<M> OdP; |
| 511 | vfloat<M> dOdP; |
| 512 | |
| 513 | /* for ray/cone intersection */ |
| 514 | private: |
| 515 | vfloat<M> yp; |
| 516 | vfloat<M> y_cone_front; |
| 517 | vfloat<M> t_cone_front; |
| 518 | #if !defined (EMBREE_BACKFACE_CULLING_CURVES) |
| 519 | vfloat<M> y_cone_back; |
| 520 | vfloat<M> t_cone_back; |
| 521 | #endif |
| 522 | |
| 523 | /* for ray/sphere intersection */ |
| 524 | private: |
| 525 | vfloat<M> t_sph1_front; |
| 526 | vfloat<M> t_sph0_front; |
| 527 | #if !defined (EMBREE_BACKFACE_CULLING_CURVES) |
| 528 | vfloat<M> t_sph1_back; |
| 529 | vfloat<M> t_sph0_back; |
| 530 | #endif |
| 531 | }; |
| 532 | |
| 533 | |
| 534 | template<int M, typename Epilog, typename ray_tfar_func> |
| 535 | static __forceinline bool intersectConeSphere(const vbool<M>& valid_i, |
| 536 | const Vec3vf<M>& ray_org_in, const Vec3vf<M>& ray_dir, |
| 537 | const vfloat<M>& ray_tnear, const ray_tfar_func& ray_tfar, |
| 538 | const Vec4vf<M>& v0, const Vec4vf<M>& v1, |
| 539 | const Vec4vf<M>& vL, const Vec4vf<M>& vR, |
| 540 | const Epilog& epilog) |
| 541 | { |
| 542 | vbool<M> valid = valid_i; |
| 543 | |
| 544 | /* move ray origin closer to make calculations numerically stable */ |
| 545 | const vfloat<M> dOdO = sqr(ray_dir); |
| 546 | const vfloat<M> rcp_dOdO = rcp(dOdO); |
| 547 | const Vec3vf<M> center = vfloat<M>(0.5f)*(v0.xyz()+v1.xyz()); |
| 548 | const vfloat<M> dt = dot(center-ray_org_in,ray_dir)*rcp_dOdO; |
| 549 | const Vec3vf<M> ray_org = ray_org_in + dt*ray_dir; |
| 550 | |
| 551 | /* intersect with cone from v0 to v1 */ |
| 552 | vfloat<M> t_cone_lower, t_cone_upper; |
| 553 | ConeGeometryIntersector<M> cone (ray_org, ray_dir, dOdO, rcp_dOdO, v0, v1); |
| 554 | vbool<M> validCone = valid; |
| 555 | cone.intersectCone(validCone, t_cone_lower, t_cone_upper); |
| 556 | |
| 557 | valid &= (validCone | (cone.g <= 0.0f)); // if cone is entirely in sphere end - check sphere |
| 558 | if (unlikely(none(valid))) |
| 559 | return false; |
| 560 | |
| 561 | /* cone hits inside the neighboring capped cones are inside the geometry and thus ignored */ |
| 562 | const ConeGeometry<M> coneL (v0, vL); |
| 563 | const ConeGeometry<M> coneR (v1, vR); |
| 564 | #if !defined(EMBREE_BACKFACE_CULLING_CURVES) |
| 565 | const Vec3vf<M> hit_lower = ray_org + t_cone_lower*ray_dir; |
| 566 | const Vec3vf<M> hit_upper = ray_org + t_cone_upper*ray_dir; |
| 567 | t_cone_lower = select (!coneL.isInsideCappedCone (validCone, hit_lower) & !coneR.isInsideCappedCone (validCone, hit_lower), t_cone_lower, vfloat<M>(pos_inf)); |
| 568 | t_cone_upper = select (!coneL.isInsideCappedCone (validCone, hit_upper) & !coneR.isInsideCappedCone (validCone, hit_upper), t_cone_upper, vfloat<M>(neg_inf)); |
| 569 | #endif |
| 570 | |
| 571 | /* intersect ending sphere */ |
| 572 | vfloat<M> t_sph1_lower, t_sph1_upper; |
| 573 | vfloat<M> t_sph0_lower = vfloat<M>(pos_inf); |
| 574 | vfloat<M> t_sph0_upper = vfloat<M>(neg_inf); |
| 575 | cone.intersectEndSphere(valid, coneR, t_sph1_lower, t_sph1_upper); |
| 576 | |
| 577 | const vbool<M> isBeginPoint = valid & (vL[0] == vfloat<M>(pos_inf)); |
| 578 | if (unlikely(any(isBeginPoint))) { |
| 579 | cone.intersectBeginSphere (isBeginPoint, t_sph0_lower, t_sph0_upper); |
| 580 | } |
| 581 | |
| 582 | /* CSG union of cone and end sphere */ |
| 583 | vfloat<M> t_sph_lower = min(t_sph0_lower, t_sph1_lower); |
| 584 | vfloat<M> t_cone_sphere_lower = min(t_cone_lower, t_sph_lower); |
| 585 | #if !defined (EMBREE_BACKFACE_CULLING_CURVES) |
| 586 | vfloat<M> t_sph_upper = max(t_sph0_upper, t_sph1_upper); |
| 587 | vfloat<M> t_cone_sphere_upper = max(t_cone_upper, t_sph_upper); |
| 588 | |
| 589 | /* filter out hits that are not in tnear/tfar range */ |
| 590 | const vbool<M> valid_lower = valid & ray_tnear <= dt+t_cone_sphere_lower & dt+t_cone_sphere_lower <= ray_tfar() & t_cone_sphere_lower != vfloat<M>(pos_inf); |
| 591 | const vbool<M> valid_upper = valid & ray_tnear <= dt+t_cone_sphere_upper & dt+t_cone_sphere_upper <= ray_tfar() & t_cone_sphere_upper != vfloat<M>(neg_inf); |
| 592 | |
| 593 | /* check if there is a first hit */ |
| 594 | const vbool<M> valid_first = valid_lower | valid_upper; |
| 595 | if (unlikely(none(valid_first))) |
| 596 | return false; |
| 597 | |
| 598 | /* construct first hit */ |
| 599 | const vfloat<M> t_first = select(valid_lower, t_cone_sphere_lower, t_cone_sphere_upper); |
| 600 | const vbool<M> cone_hit_first = t_first == t_cone_lower | t_first == t_cone_upper; |
| 601 | const vbool<M> sph0_hit_first = t_first == t_sph0_lower | t_first == t_sph0_upper; |
| 602 | const Vec3vf<M> Ng_first = select(cone_hit_first, cone.Ng_cone(valid_lower), select (sph0_hit_first, cone.Ng_sphere0(valid_lower), cone.Ng_sphere1(valid_lower))); |
| 603 | const vfloat<M> u_first = select(cone_hit_first, cone.u_cone(valid_lower), select (sph0_hit_first, vfloat<M>(zero), vfloat<M>(one))); |
| 604 | |
| 605 | /* invoke intersection filter for first hit */ |
| 606 | RoundLineIntersectorHitM<M> hit(u_first,zero,dt+t_first,Ng_first); |
| 607 | const bool is_hit_first = epilog(valid_first, hit); |
| 608 | |
| 609 | /* check for possible second hits before potentially accepted hit */ |
| 610 | const vfloat<M> t_second = t_cone_sphere_upper; |
| 611 | const vbool<M> valid_second = valid_lower & valid_upper & (dt+t_cone_sphere_upper <= ray_tfar()); |
| 612 | if (unlikely(none(valid_second))) |
| 613 | return is_hit_first; |
| 614 | |
| 615 | /* invoke intersection filter for second hit */ |
| 616 | const vbool<M> cone_hit_second = t_second == t_cone_lower | t_second == t_cone_upper; |
| 617 | const vbool<M> sph0_hit_second = t_second == t_sph0_lower | t_second == t_sph0_upper; |
| 618 | const Vec3vf<M> Ng_second = select(cone_hit_second, cone.Ng_cone(false), select (sph0_hit_second, cone.Ng_sphere0(false), cone.Ng_sphere1(false))); |
| 619 | const vfloat<M> u_second = select(cone_hit_second, cone.u_cone(false), select (sph0_hit_second, vfloat<M>(zero), vfloat<M>(one))); |
| 620 | |
| 621 | hit = RoundLineIntersectorHitM<M>(u_second,zero,dt+t_second,Ng_second); |
| 622 | const bool is_hit_second = epilog(valid_second, hit); |
| 623 | |
| 624 | return is_hit_first | is_hit_second; |
| 625 | #else |
| 626 | /* filter out hits that are not in tnear/tfar range */ |
| 627 | const vbool<M> valid_lower = valid & ray_tnear <= dt+t_cone_sphere_lower & dt+t_cone_sphere_lower <= ray_tfar() & t_cone_sphere_lower != vfloat<M>(pos_inf); |
| 628 | |
| 629 | /* check if there is a valid hit */ |
| 630 | if (unlikely(none(valid_lower))) |
| 631 | return false; |
| 632 | |
| 633 | /* construct first hit */ |
| 634 | const vbool<M> cone_hit_first = t_cone_sphere_lower == t_cone_lower | t_cone_sphere_lower == t_cone_upper; |
| 635 | const vbool<M> sph0_hit_first = t_cone_sphere_lower == t_sph0_lower | t_cone_sphere_lower == t_sph0_upper; |
| 636 | const Vec3vf<M> Ng_first = select(cone_hit_first, cone.Ng_cone(valid_lower), select (sph0_hit_first, cone.Ng_sphere0(valid_lower), cone.Ng_sphere1(valid_lower))); |
| 637 | const vfloat<M> u_first = select(cone_hit_first, cone.u_cone(valid_lower), select (sph0_hit_first, vfloat<M>(zero), vfloat<M>(one))); |
| 638 | |
| 639 | /* invoke intersection filter for first hit */ |
| 640 | RoundLineIntersectorHitM<M> hit(u_first,zero,dt+t_cone_sphere_lower,Ng_first); |
| 641 | const bool is_hit_first = epilog(valid_lower, hit); |
| 642 | |
| 643 | return is_hit_first; |
| 644 | #endif |
| 645 | } |
| 646 | |
| 647 | } // end namespace __roundline_internal |
| 648 | |
| 649 | template<int M> |
| 650 | struct RoundLinearCurveIntersector1 |
| 651 | { |
| 652 | typedef CurvePrecalculations1 Precalculations; |
| 653 | |
| 654 | template<typename Ray> |
| 655 | struct ray_tfar { |
| 656 | Ray& ray; |
| 657 | __forceinline ray_tfar(Ray& ray) : ray(ray) {} |
| 658 | __forceinline vfloat<M> operator() () const { return ray.tfar; }; |
| 659 | }; |
| 660 | |
| 661 | template<typename Ray, typename Epilog> |
| 662 | static __forceinline bool intersect(const vbool<M>& valid_i, |
| 663 | Ray& ray, |
| 664 | IntersectContext* context, |
| 665 | const LineSegments* geom, |
| 666 | const Precalculations& pre, |
| 667 | const Vec4vf<M>& v0i, const Vec4vf<M>& v1i, |
| 668 | const Vec4vf<M>& vLi, const Vec4vf<M>& vRi, |
| 669 | const Epilog& epilog) |
| 670 | { |
| 671 | const Vec3vf<M> ray_org(ray.org.x, ray.org.y, ray.org.z); |
| 672 | const Vec3vf<M> ray_dir(ray.dir.x, ray.dir.y, ray.dir.z); |
| 673 | const vfloat<M> ray_tnear(ray.tnear()); |
| 674 | const Vec4vf<M> v0 = enlargeRadiusToMinWidth<M>(context,geom,ray_org,v0i); |
| 675 | const Vec4vf<M> v1 = enlargeRadiusToMinWidth<M>(context,geom,ray_org,v1i); |
| 676 | const Vec4vf<M> vL = enlargeRadiusToMinWidth<M>(context,geom,ray_org,vLi); |
| 677 | const Vec4vf<M> vR = enlargeRadiusToMinWidth<M>(context,geom,ray_org,vRi); |
| 678 | return __roundline_internal::intersectConeSphere<M>(valid_i,ray_org,ray_dir,ray_tnear,ray_tfar<Ray>(ray),v0,v1,vL,vR,epilog); |
| 679 | } |
| 680 | }; |
| 681 | |
| 682 | template<int M, int K> |
| 683 | struct RoundLinearCurveIntersectorK |
| 684 | { |
| 685 | typedef CurvePrecalculationsK<K> Precalculations; |
| 686 | |
| 687 | struct ray_tfar { |
| 688 | RayK<K>& ray; |
| 689 | size_t k; |
| 690 | __forceinline ray_tfar(RayK<K>& ray, size_t k) : ray(ray), k(k) {} |
| 691 | __forceinline vfloat<M> operator() () const { return ray.tfar[k]; }; |
| 692 | }; |
| 693 | |
| 694 | template<typename Epilog> |
| 695 | static __forceinline bool intersect(const vbool<M>& valid_i, |
| 696 | RayK<K>& ray, size_t k, |
| 697 | IntersectContext* context, |
| 698 | const LineSegments* geom, |
| 699 | const Precalculations& pre, |
| 700 | const Vec4vf<M>& v0i, const Vec4vf<M>& v1i, |
| 701 | const Vec4vf<M>& vLi, const Vec4vf<M>& vRi, |
| 702 | const Epilog& epilog) |
| 703 | { |
| 704 | const Vec3vf<M> ray_org(ray.org.x[k], ray.org.y[k], ray.org.z[k]); |
| 705 | const Vec3vf<M> ray_dir(ray.dir.x[k], ray.dir.y[k], ray.dir.z[k]); |
| 706 | const vfloat<M> ray_tnear = ray.tnear()[k]; |
| 707 | const Vec4vf<M> v0 = enlargeRadiusToMinWidth<M>(context,geom,ray_org,v0i); |
| 708 | const Vec4vf<M> v1 = enlargeRadiusToMinWidth<M>(context,geom,ray_org,v1i); |
| 709 | const Vec4vf<M> vL = enlargeRadiusToMinWidth<M>(context,geom,ray_org,vLi); |
| 710 | const Vec4vf<M> vR = enlargeRadiusToMinWidth<M>(context,geom,ray_org,vRi); |
| 711 | return __roundline_internal::intersectConeSphere<M>(valid_i,ray_org,ray_dir,ray_tnear,ray_tfar(ray,k),v0,v1,vL,vR,epilog); |
| 712 | } |
| 713 | }; |
| 714 | } |
| 715 | } |
| 716 | |