@@ -192,28 +192,12 @@ bool SphereSweep::All_Intersections(const Ray& ray, IStack& Depth_Stack, TraceTh
192192 // Intersections with single spheres
193193 for (i = 0 ; i < Num_Spheres; i++)
194194 {
195- // Are there intersections with this sphere?
196- if ( Intersect_Sphere (New_Ray, &Sphere[i], Sphere_Isect) )
195+ // Test for end of vector
196+ if (Num_Isect + 2 <= SPHSWEEP_MAX_ISECT )
197197 {
198- // Test for end of vector
199- if (Num_Isect + 2 <= SPHSWEEP_MAX_ISECT)
200- {
201- // Valid intersection at Sphere_Isect[0]?
202- if ((Sphere_Isect[0 ].t > -MAX_DISTANCE) && (Sphere_Isect[0 ].t < MAX_DISTANCE))
203- {
204- // Add intersection
205- Isect[Num_Isect] = Sphere_Isect[0 ];
206- Num_Isect++;
207- }
208-
209- // Valid intersection at Sphere_Isect[1]?
210- if ((Sphere_Isect[1 ].t > -MAX_DISTANCE) && (Sphere_Isect[1 ].t < MAX_DISTANCE))
211- {
212- // Add intersection
213- Isect[Num_Isect] = Sphere_Isect[1 ];
214- Num_Isect++;
215- }
216- }
198+ // Are there intersections with this sphere?
199+ if (Intersect_Sphere (New_Ray, &Sphere[i], Isect + Num_Isect))
200+ Num_Isect += 2 ;
217201 }
218202 }
219203
@@ -349,7 +333,7 @@ bool SphereSweep::Intersect_Sphere(const BasicRay &ray, const SPHSWEEP_SPH *Sphe
349333 Inter[0 ].Point = ray.Evaluate (Inter[0 ].t );
350334
351335 // Calculate normal
352- Inter[0 ].Normal = (Inter[0 ].Point - Sphere->Center ) / Sphere->Radius ;
336+ Inter[0 ].Normal = (Inter[0 ].Point - Sphere->Center ) / fabs ( Sphere->Radius ) ;
353337
354338 // Calculate bigger depth
355339 Inter[1 ].t = t_Closest_Approach + Half_Chord;
@@ -358,7 +342,7 @@ bool SphereSweep::Intersect_Sphere(const BasicRay &ray, const SPHSWEEP_SPH *Sphe
358342 Inter[1 ].Point = ray.Evaluate (Inter[1 ].t );
359343
360344 // Calculate normal
361- Inter[1 ].Normal = (Inter[1 ].Point - Sphere->Center ) / Sphere->Radius ;
345+ Inter[1 ].Normal = (Inter[1 ].Point - Sphere->Center ) / fabs ( Sphere->Radius ) ;
362346
363347 return true ;
364348 }
@@ -410,7 +394,12 @@ bool SphereSweep::Intersect_Sphere(const BasicRay &ray, const SPHSWEEP_SPH *Sphe
410394int SphereSweep::Intersect_Segment (const BasicRay &ray, const SPHSWEEP_SEG *Segment, SPHSWEEP_INT *Isect, TraceThreadData *Thread)
411395{
412396 int Isect_Count;
397+ DBL Dot1, Dot2;
398+ DBL t1, t2;
413399 Vector3d Vector;
400+ Vector3d IPoint;
401+ Vector3d DCenter;
402+ DBL DCenterDot;
414403 DBL temp;
415404 DBL b, c, d, e, f, g, h, i, j, k, l;
416405 DBL Coef[11 ];
@@ -423,6 +412,79 @@ int SphereSweep::Intersect_Segment(const BasicRay &ray, const SPHSWEEP_SEG *Segm
423412
424413 Isect_Count = 0 ;
425414
415+ // Calculate intersections with closing discs near the ends of the spline segment.
416+ // Note that the positions of the closing discs account for the rate of change in radius
417+ // at the respective end of the spline segment.
418+
419+ // We need to compute these intersections even though we will inevitably discard them later,
420+ // because the algorithm downstream expects intersections to always come in pairs, one entering
421+ // the segment and the other one exiting it. The intersections with the closing discs will
422+ // take the place of any intersections we discard because they fall outside the interval [0,1].
423+ // (Also, if the spline happens to be parallel to the ray direction, we will get no roots
424+ // at all but still need to have these intersections.)
425+
426+ // Closing disk near u=1.
427+ Dot1 = dot (ray.Direction , Segment->Center_Deriv [0 ]);
428+ if (fabs (Dot1) > EPSILON)
429+ {
430+ Vector = ray.Origin - Segment->Closing_Sphere [0 ].Center ;
431+ Dot2 = dot (Vector, Segment->Center_Deriv [0 ]);
432+ t1 = -(Dot2 + Segment->Closing_Sphere [0 ].Radius * Segment->Radius_Deriv [0 ]) / Dot1;
433+ if ((t1 > -MAX_DISTANCE) && (t1 < MAX_DISTANCE))
434+ {
435+ // Calculate point
436+ IPoint = ray.Evaluate (t1);
437+
438+ // Is the point inside the closing sphere?
439+ DCenter = IPoint - Segment->Closing_Sphere [0 ].Center ;
440+ DCenterDot = DCenter.lengthSqr ();
441+ if (DCenterDot < Sqr (Segment->Closing_Sphere [0 ].Radius ))
442+ {
443+ // Add intersection
444+ Isect[Isect_Count].t = t1;
445+
446+ // Copy point
447+ Isect[Isect_Count].Point = IPoint;
448+
449+ // Calculate normal
450+ Isect[Isect_Count].Normal = -Segment->Center_Deriv [0 ].normalized ();
451+
452+ Isect_Count++;
453+ }
454+ }
455+ }
456+
457+ // Closing disk near u=1.
458+ Dot1 = dot (ray.Direction , Segment->Center_Deriv [1 ]);
459+ if (fabs (Dot1) > EPSILON)
460+ {
461+ Vector = ray.Origin - Segment->Closing_Sphere [1 ].Center ;
462+ Dot2 = dot (Vector, Segment->Center_Deriv [1 ]);
463+ t2 = -(Dot2 + Segment->Closing_Sphere [1 ].Radius * Segment->Radius_Deriv [1 ]) / Dot1;
464+ if ((t2 > -MAX_DISTANCE) && (t2 < MAX_DISTANCE))
465+ {
466+ // Calculate point
467+ IPoint = ray.Evaluate (t2);
468+
469+ // Is the point inside the closing sphere?
470+ DCenter = IPoint - Segment->Closing_Sphere [1 ].Center ;
471+ DCenterDot = DCenter.lengthSqr ();
472+ if (DCenterDot < Sqr (Segment->Closing_Sphere [1 ].Radius ))
473+ {
474+ // Add intersection
475+ Isect[Isect_Count].t = t2;
476+
477+ // Copy point
478+ Isect[Isect_Count].Point = IPoint;
479+
480+ // Calculate normal
481+ Isect[Isect_Count].Normal = Segment->Center_Deriv [1 ].normalized ();
482+
483+ Isect_Count++;
484+ }
485+ }
486+ }
487+
426488 // Calculate intersections with sides of the segment
427489
428490 switch (Segment->Num_Coefs )
@@ -537,7 +599,10 @@ int SphereSweep::Intersect_Segment(const BasicRay &ray, const SPHSWEEP_SEG *Segm
537599 break ;
538600 }
539601
540- // Remove roots not in interval [0, 1]
602+ // Remove roots not in interval [0, 1].
603+
604+ // Note that the corresponding intersections are either removed in balanced pairs, or are
605+ // balanced by intersections with the closing discs.
541606
542607 m = 0 ;
543608 while (m < Num_Poly_Roots)
@@ -1474,6 +1539,16 @@ void SphereSweep::Compute()
14741539
14751540 for (i = 0 ; i < Num_Segments; i++)
14761541 {
1542+ // Calculate closing sphere for u = 0
1543+
1544+ // Center
1545+ Segment[i].Closing_Sphere [0 ].Center =
1546+ Segment[i].Center_Coef [0 ];
1547+
1548+ // Radius
1549+ Segment[i].Closing_Sphere [0 ].Radius =
1550+ Segment[i].Radius_Coef [0 ];
1551+
14771552 // Calculate derivatives for u = 0
14781553
14791554 // Center
@@ -1484,6 +1559,27 @@ void SphereSweep::Compute()
14841559 Segment[i].Radius_Deriv [0 ] =
14851560 Segment[i].Radius_Coef [1 ];
14861561
1562+ // Calculate closing sphere for u = 1
1563+
1564+ // Center
1565+ Segment[i].Closing_Sphere [1 ].Center =
1566+ Segment[i].Center_Coef [0 ];
1567+
1568+ // Radius
1569+ Segment[i].Closing_Sphere [1 ].Radius =
1570+ Segment[i].Radius_Coef [0 ];
1571+
1572+ for (coef = 1 ; coef < Segment[i].Num_Coefs ; coef++)
1573+ {
1574+ // Center
1575+ Segment[i].Closing_Sphere [1 ].Center +=
1576+ Segment[i].Center_Coef [coef];
1577+
1578+ // Radius
1579+ Segment[i].Closing_Sphere [1 ].Radius +=
1580+ Segment[i].Radius_Coef [coef];
1581+ }
1582+
14871583 // Calculate derivatives for u = 1
14881584
14891585 // Center
@@ -1592,9 +1688,11 @@ int SphereSweep::Find_Valid_Points(SPHSWEEP_INT *Inter, int Num_Inter, const Bas
15921688 int i;
15931689 int j;
15941690 int Inside;
1595- int Keep;
1691+ bool Keep;
15961692 DBL NormalDotDirection;
15971693
1694+ // NB: First intersection point always enters, last one always leaves, so we're not explicitly testing them.
1695+
15981696 Inside = 1 ;
15991697 i = 1 ;
16001698 while (i < Num_Inter - 1 )
0 commit comments