Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-04-09 07:48:55

0001 #pragma once
0002 
0003 /**
0004 intersect_leaf_thetacut
0005 --------------------------
0006 
0007 
0008 
0009 
0010            .       \             /       .
0011              .      \           /      .
0012                .     \         /     .
0013                  .    \       /    .
0014                    .   \     /   . 
0015         ------------0---1---2---3----------------
0016                        . \ / .
0017           - - - - - - - - O - - - - - - - - -   
0018 
0019 
0020                   
0021 
0022 
0023 
0024              - - - - - - . O .- - - - - - - -
0025                        .  / \  .
0026              --------0---1---2---3-----------
0027                    .    /     \    .
0028                  .     /       \     .
0029                .      /         \      .
0030              .       /           \       . 
0031 
0032 
0033 
0034 There are lots of cases to consider, so 
0035 it is highly preferable to not make assumptions
0036 and keep all root alive and make decision of
0037 which root to use once. 
0038 
0039 
0040 
0041 Because its unbounded need some special
0042 handling of MISS that would intersect with 
0043 the "otherside" arc at infinity. 
0044 
0045 As the relevant angles are geospecific it 
0046 needs to be done here in the leaf. 
0047 This is a bit similar to the handling of complemented solids 
0048 but there is a difference in that the otherside intersects
0049 of a complemented solid are not restricted in angle. 
0050 
0051 Need a way to signal that an unbounded MISS 
0052 should be promoted to an EXIT at infinity. 
0053 
0054 See CSG/tests/CSGClassifyTest.cc for thoughts on how 
0055 to do this.
0056 
0057 
0058 
0059                         unbounded
0060                             MISS
0061                               1
0062                               |
0063                     .         |         .
0064                               |     |      .
0065                  .    \       0     | /       .
0066                        \            |/
0067                .        \           1          .
0068                          \         /|
0069               .           \       / |            .
0070                            \     /  |
0071              .         - - -0- - 1 -|- - - - -   
0072                              \ /    |            .  
0073              .                O     |     0 - - - - - - - - - - 1
0074                             .   .   |           .           (unbounded MISS -> EXIT)
0075               .           .       . |
0076                         .           0         . 
0077                 .     .             | .     
0078                     .               |   .   .
0079                   .                       .
0080 
0081 
0082 Notice: 
0083 
0084 1. rays from origin can never intersect the thetacut, at least not until infinity
0085 2. thetacut is an unbounded shape : which means there is an "arc" at infinity 
0086  
0087 The two cones are rotationally symmetric about Z axis : so consider (radial, vertical) (R,Z) 2D space
0088 
0089      Z
0090      |
0091      |            /
0092      |           /
0093      |          /
0094      |         /  
0095      |        +(sinTheta0, cosTheta0)  = (0,1) when theta0_pi = 0.
0096      |       /              
0097      |      /
0098      |     /     +  +  
0099      |    /   d.z| /  ( d_xy , d.z )   (radial, vertical) components of ray direction  
0100      |   /       |/
0101      |  /        +--+  
0102      | /             sqrt(d.x*d.x+d.y*d.y) = d_xy
0103      |/
0104      +- - - - - - - - - - -  R
0105      |\
0106      | \
0107      |  \
0108      |   \
0109      |    \
0110      |     \
0111      |      \
0112      |       \
0113      |        \
0114      |         + (sinTheta1, cosTheta1) = (0, -1)   when theta1_pi = 1.0 
0115      |          \
0116      |           \
0117     
0118 
0119 2D cross product (embedded in 3D RZW space at W=0) between theta vectors shown 
0120 above and ray direction is proportional to the sine of the angle between them.  
0121 
0122 Using adhoc sign convention::
0123 
0124    (v0.xy, v0.z ) ^ ( d_xy, d.z ) =  v0.z*d_xy - v0.xy*d.z ; 
0125 
0126    cross0 =  (sinTheta0, cosTheta0 ) ^ (d_xy, d.z )  = cosTheta0*d_xy - sinTheta0*d.z  
0127    cross1 =  (sinTheta1, cosTheta1 ) ^ (d_xy, d.z )  = cosTheta1*d_xy - sinTheta1*d.z  
0128 
0129 cross0 > 0.f && cross1 < 0.f 
0130     means the ray direction is between the theta0 and theta1 cone directions 
0131     so a "miss" would eventually exit the "otherside" of the unbounded thetacut at infinity
0132     (assuming the ray starts within the shape) 
0133     HMM: currently not checking are inside shape but it seems not be matter 
0134     (could check are inside by similar cross-trick with the ray_origin)
0135 
0136 **/
0137 
0138 LEAF_FUNC
0139 void intersect_leaf_thetacut(bool& valid_isect, float4& isect, const quad& q0, const quad& q1, const float t_min, const float3& o, const float3& d)
0140 {   
0141     const float& cosTheta0   = q0.f.x ; 
0142     const float& sinTheta0   = q0.f.y ;
0143     const float& cosTheta1   = q0.f.z ; 
0144     const float& sinTheta1   = q0.f.w ;
0145     const float& tanTheta0sq = q1.f.x ; 
0146     const float& tanTheta1sq = q1.f.y ; 
0147 
0148     // HMM: the signs are just used to check sign of the intersect indicates are not intersecting the mirror cone 
0149     //     so could just use cosTheta itself ? 
0150     const float cosTheta0Sign = cosTheta0 < 0.f ? -1.f : 1.f ;
0151     const float cosTheta1Sign = cosTheta1 < 0.f ? -1.f : 1.f ;
0152   
0153 
0154     const float dxx_dyy = d.x * d.x + d.y * d.y ;  
0155     const float d_xy = sqrt( dxx_dyy ); 
0156     const float cross0 = cosTheta0*d_xy - sinTheta0*d.z; 
0157     const float cross1 = cosTheta1*d_xy - sinTheta1*d.z; 
0158     bool unbounded_exit = cross0 > 0.f && cross1 < 0.f ;   
0159 
0160 #ifdef DEBUG
0161     printf("//intersect_leaf_thetacut q0.f  (%10.4f %10.4f %10.4f %10.4f) %s \n" , q0.f.x, q0.f.y, q0.f.z, q0.f.w, "cosTheta0/sinTheta0/cosTheta1/sinTheta1"  ) ; 
0162     printf("//intersect_leaf_thetacut q1.f  (%10.4f %10.4f %10.4f %10.4f)\n" , q1.f.x, q1.f.y, q1.f.z, q1.f.w ) ; 
0163     printf("//intersect_leaf_thetacut dxx_dyy %10.4f d_xy %10.4f d.z %10.4f \n", dxx_dyy, d_xy, d.z  ); 
0164     printf("//intersect_leaf_thetacut cross0 %10.4f cosTheta0*d_xy - sinTheta0*d.z \n", cross0 );
0165     printf("//intersect_leaf_thetacut cross1 %10.4f cosTheta1*d_xy - sinTheta1*d.z\n", cross1 );
0166     printf("//intersect_leaf_thetacut unbounded_exit %d \n", unbounded_exit ); 
0167 #endif
0168 
0169 
0170     // quadratic coefficients for intersection of ray with theta0 cone     
0171     float dd  = dxx_dyy - d.z * d.z * tanTheta0sq ;
0172     float od  = o.x * d.x + o.y * d.y - o.z * d.z * tanTheta0sq ;
0173     float oo  = o.x * o.x + o.y * o.y - o.z * o.z * tanTheta0sq ;
0174     float disc = od * od - oo * dd ;
0175     bool intersects = disc > 0.f; 
0176     float discRoot = intersects ? sqrt(disc) : 0.f; 
0177 
0178     float t_cand = intersects ? (-od + discRoot) / dd : RT_DEFAULT_MAX;
0179     float t0     = intersects ? (-od - discRoot) / dd : RT_DEFAULT_MAX;
0180 
0181 
0182     // intersect on z-mirror cone  or too close   
0183     if (cosTheta0Sign * (t_cand * d.z + o.z) < 0.f  || t_cand <= t_min) t_cand = RT_DEFAULT_MAX;   
0184 
0185     // intersect not on z-mirror cone and not too close 
0186     if (cosTheta0Sign * (t0     * d.z + o.z) > 0.f  && t0 > t_min     ) t_cand = fminf(t_cand, t0); 
0187 
0188 
0189     /*
0190     THIS IS TRYING TO AVOID KEEPING ALL THE  ROOTS ALIVE AT ONCE TO REDUCE RESOURCES : 
0191     BUT IN THE PROCESS IT MAKES ASSUMPTIONS THAT MAY NOT ALWAYS BE TRUE.
0192 
0193     TO WORK IN CSG COMBINATION IT MUST BE POSSIBLE FOR t_min CUTTING 
0194     TO INVALIDATE ANY ROOT : SO IT IS WRONG TO TRY TO CHOOSE A 
0195     ROOT FROM ONE CONE BEFORE CONSIDERING THE ROOTS FROM THE OTHER 
0196 
0197     TODO: try to tickle this itch by choosing an appropriate t_min, ray_origin, ray_direction 
0198     */
0199 
0200     // modify quadratic coefficients to hop to the other cone 
0201     dd += d.z * d.z * (tanTheta0sq - tanTheta1sq );
0202     od += o.z * d.z * (tanTheta0sq - tanTheta1sq );
0203     oo += o.z * o.z * (tanTheta0sq - tanTheta1sq );
0204     disc = od * od - oo * dd ;
0205 
0206     intersects = disc > 0.f;
0207     discRoot = intersects ? sqrt(disc) : 0.f;
0208 
0209     t0 =             intersects ? (-od + discRoot) / dd : RT_DEFAULT_MAX;
0210     const float t1 = intersects ? (-od - discRoot) / dd : RT_DEFAULT_MAX;
0211 
0212     if (cosTheta1Sign * (t0 * d.z + o.z) > 0.f && t0 > t_min) t_cand = fminf(t_cand, t0);
0213     if (cosTheta1Sign * (t1 * d.z + o.z) > 0.f && t1 > t_min) t_cand = fminf(t_cand, t1);
0214 
0215     /*
0216 
0217          n   = [0,0,1]  normal the plane : for when cones degenerate into plane 
0218          p   = o + t*d 
0219          p.z = o.z + t*d.z = 0                 
0220 
0221 
0222           -------*------------- z = 0 
0223                 /
0224                /
0225               /        t_plane = -o.z /d.z 
0226              /
0227             o
0228 
0229     */
0230 
0231     const float t_plane = -o.z / d.z;
0232     const bool plane = cosTheta0 * cosTheta1 == 0.0 && t_plane > t_min && t_cand > t_plane ;
0233 
0234 /**
0235 plane:true
0236     one of the cones has degenerated to a plane (theta 0.5) and has a candidate intersect 
0237     (cosTheta0/1 are arranged to be precisely zero for angle 0.5) 
0238 
0239 hmm: thats a bit funny the imprecise intersect from the degenerate cone may be competing 
0240 here with the one from the more precise plane 
0241 **/
0242 
0243     valid_isect = t_cand < RT_DEFAULT_MAX || plane;
0244 
0245     if (valid_isect) {
0246         const bool side = t_cand == t0 || t_cand == t1; 
0247         // when t_cand is equal to the current t0 or t1 it means that the itersect is with the theta1 cone and not the theta0 cone
0248 
0249         // XY cross section of the two cones are two circles : with .xy components of normals radially outwards and inwards    
0250         isect.x = plane ? 0.f                             : (side ?  cosTheta1Sign * (o.x + t_cand * d.x)                : -cosTheta0Sign * (o.x + t_cand * d.x));
0251         isect.y = plane ? 0.f                             : (side ?  cosTheta1Sign * (o.y + t_cand * d.y)                : -cosTheta0Sign * (o.y + t_cand * d.y));
0252         isect.z = plane ? (cosTheta0 == 0.f ? 1.f : -1.f) : (side ? -cosTheta1Sign * (o.z + t_cand * d.z) * tanTheta1sq  :  cosTheta0Sign * (o.z + t_cand * d.z) * tanTheta0sq );
0253         isect = normalize(isect);   
0254         // SCB: normalizing a float4 : unfounded assumption that isect.w = 0 
0255 
0256         isect.w = plane ? t_plane : t_cand;
0257     }
0258     else if( unbounded_exit )
0259     {
0260         isect.y = -isect.y ;  // -0.f signflip signalling that can promote MISS to EXIT at infinity 
0261         // TODO:maybe better return an int not a bool, so can signal more clearly  
0262     }
0263 
0264 #ifdef DEBUG
0265     printf("//intersect_leaf_thetacut isect (%10.4f %10.4f %10.4f %10.4f) valid_isect %d  \n" , isect.x, isect.y, isect.z, isect.w, valid_isect ) ; 
0266 #endif
0267 }
0268 
0269 
0270 /**
0271 SCB comments on intersect_leaf_thetacut_lucas
0272 
0273 1. normalize(isect) a float4 is a bug : you are requiring isect.w to be zero 
0274 
0275 2. you say same maths as intersect_node_cone (now intersect_leaf_cone)
0276    but you use entirely different language 
0277 
0278 3. invalidate candidates by setting to t_min is needed for the shape to 
0279    work in CSG combinations as need expected behaviour as t_min is varied
0280 
0281 
0282 
0283 intersect_leaf_thetacut_lucas
0284 --------------------------------
0285 Based on same maths behind intersect_node_cone, see there for explanation.
0286 
0287 WORKS FOR 0 <= THETA <= 180 BUT BEWARE: USER NEEDS TO BE CAREFUL WHEN DEFINING QUAD, MUST BE SET
0288     //    q.f.x = theta0 == 0.5 ? 0.0 : cos(theta0 * pi ) / abs(cos(theta0 * pi));
0289     //    q.f.y = theta0 == 0.5 ? 0.0 : tan(theta0 * pi) * tan(theta0 * pi);
0290     //    q.f.z = theta1 == 0.5 ? 0.0 :  cos(theta1 * pi) / abs(cos(theta1 * pi));
0291     //    q.f.w = theta1 == 0.5 ? 0.0 : tan(theta1 * pi) * tan(theta1 * pi);
0292     // if .x and .z are not set 0.0 cos(...) float inaccuracy will mean plane not recognised.
0293     // if .y and .w are not set 0.0 magnitudes will give wacky values, not worth the risk.
0294     
0295 **/
0296 LEAF_FUNC
0297 void intersect_leaf_thetacut_lucas(bool& valid_isect, float4& isect, const quad& thetaDat, const float t_min, const float3& rayOrigin, const float3& rayDirection)
0298 {   //thetaData contains x = cos(theta0)/abs(cos(theta0)), y = tan^2 (theta0), z = cos(theta1)/abs(cos(theta1)), w = tan^2 (theta1)
0299 
0300     float dirMag = rayDirection.x * rayDirection.x + rayDirection.y * rayDirection.y - rayDirection.z * rayDirection.z * thetaDat.f.y;
0301     float originDirMag = rayOrigin.x * rayDirection.x + rayOrigin.y * rayDirection.y - rayOrigin.z * rayDirection.z * thetaDat.f.y;
0302     float originMag = rayOrigin.x * rayOrigin.x + rayOrigin.y * rayOrigin.y - rayOrigin.z * rayOrigin.z * thetaDat.f.y;
0303     float disc = originDirMag * originDirMag - originMag * dirMag;
0304 
0305     bool intersects = disc > 0.f; 
0306     float discRoot = intersects ? sqrt(disc) : 0.f; //avoids sqrt(NEGATIVE)
0307 
0308     float t_cand = intersects ? (-originDirMag + discRoot) / dirMag : RT_DEFAULT_MAX; //beginning on t_cand saves defining extra variable
0309 
0310     if (thetaDat.f.x * (t_cand * rayDirection.z + rayOrigin.z) < 0.f || t_cand <= t_min) t_cand = RT_DEFAULT_MAX; //eliminates bad t_cand/mirror cone 
0311 
0312     float t0 = intersects ? (-originDirMag - discRoot) / dirMag : RT_DEFAULT_MAX;
0313     if (thetaDat.f.x * (t0 * rayDirection.z + rayOrigin.z) > 0.f && t0 > t_min) t_cand = fminf(t_cand, t0); 
0314     //works here since t_cand will already be either valid or INF
0315 
0316     dirMag += rayDirection.z * rayDirection.z * (thetaDat.f.y - thetaDat.f.w);
0317     originDirMag += rayOrigin.z * rayDirection.z * (thetaDat.f.y - thetaDat.f.w);
0318     originMag += rayOrigin.z * rayOrigin.z * (thetaDat.f.y - thetaDat.f.w);
0319     disc = originDirMag * originDirMag - originMag * dirMag;
0320 
0321     intersects = disc > 0.f;
0322     discRoot = intersects ? sqrt(disc) : 0.f;
0323 
0324     t0 = intersects ? (-originDirMag + discRoot) / dirMag : RT_DEFAULT_MAX;
0325     if (thetaDat.f.z * (t0 * rayDirection.z + rayOrigin.z) > 0.f && t0 > t_min) t_cand = fminf(t_cand, t0);
0326 
0327     const float t1 = intersects ? (-originDirMag - discRoot) / dirMag : RT_DEFAULT_MAX;
0328     if (thetaDat.f.z * (t1 * rayDirection.z + rayOrigin.z) > 0.f && t1 > t_min) t_cand = fminf(t_cand, t1);
0329 
0330 
0331     const float t_plane = -rayOrigin.z / rayDirection.z;
0332     const bool plane = thetaDat.f.x * thetaDat.f.z == 0.0 && t_plane > t_min && t_cand > t_plane;
0333     // SCB                                 ^^^^^^^^^^^^^^^^^  
0334     valid_isect = t_cand < RT_DEFAULT_MAX || plane;
0335 
0336     if (valid_isect) {
0337         const bool side = t_cand == t0 || t_cand == t1; //corrects normals for both cones/planes around 90 degrees
0338 
0339         isect.x = plane ? 0.0 : (side ? thetaDat.f.z * (rayOrigin.x + t_cand * rayDirection.x)
0340                                        : - thetaDat.f.x * (rayOrigin.x + t_cand * rayDirection.x));
0341 
0342         //SCB            ^^^^ ALLWAYS 0.f OTHERWISE POINTLESS DOUBLES : BAD FOR PERFORMANCE ON GPU  
0343 
0344         isect.y = plane ? 0.0 : (side ? thetaDat.f.z * (rayOrigin.y + t_cand * rayDirection.y)
0345                                        : - thetaDat.f.x * (rayOrigin.y + t_cand * rayDirection.y));
0346 
0347         //SCB              ^^^^^^^^  : DITTO
0348         isect.z = plane ? (thetaDat.f.x == 0.0 ? 1.0 : -1.0)
0349         //SCB                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^ DITTO
0350         //SCB         using the special setting of zero to determine which of the cones degenerated to the plane 
0351 
0352                         : ( side ? - thetaDat.f.z * (rayOrigin.z + t_cand * rayDirection.z) * thetaDat.f.w
0353                                  : thetaDat.f.x * (rayOrigin.z + t_cand * rayDirection.z) * thetaDat.f.y );
0354         isect = normalize(isect);
0355         isect.w = plane ? t_plane : t_cand;
0356     }
0357 
0358 
0359 #ifdef DEBUG
0360     const quad& q0 = thetaDat ; 
0361     printf("//intersect_leaf_thetacut_lucas q0.f (%10.4f %10.4f %10.4f %10.4f) valid_isect %d  isect  (%10.4f %10.4f %10.4f %10.4f) \n" , 
0362            q0.f.x, q0.f.y, q0.f.z, q0.f.z, valid_isect, isect.x, isect.y, isect.z, isect.w ) ; 
0363 #endif
0364 
0365 }
0366 
0367