/**************************************************************************** * spheres.c * * This module implements the sphere primitive. * * from Persistence of Vision Raytracer * Copyright 1993 Persistence of Vision Team *--------------------------------------------------------------------------- * NOTICE: This source code file is provided so that users may experiment * with enhancements to POV-Ray and to port the software to platforms other * than those supported by the POV-Ray Team. There are strict rules under * which you are permitted to use this file. The rules are in the file * named POVLEGAL.DOC which should be distributed with this file. If * POVLEGAL.DOC is not available or for more info please contact the POV-Ray * Team Coordinator by leaving a message in CompuServe's Graphics Developer's * Forum. The latest version of POV-Ray may be found there as well. * * This program is based on the popular DKB raytracer version 2.12. * DKBTrace was originally written by David K. Buck. * DKBTrace Ver 2.0-2.12 were written by David K. Buck & Aaron A. Collins. * *****************************************************************************/ #include "frame.h" #include "vector.h" #include "povproto.h" #ifndef Sphere_Tolerance #define Sphere_Tolerance 1.0e-8 #endif METHODS Sphere_Methods = { All_Sphere_Intersections, Inside_Sphere, Sphere_Normal, Copy_Sphere, Translate_Sphere, Rotate_Sphere, Scale_Sphere, Transform_Sphere, Invert_Sphere, Destroy_Sphere }; METHODS Ellipsoid_Methods = { All_Ellipsoid_Intersections, Inside_Ellipsoid, Ellipsoid_Normal, Copy_Sphere, Translate_Sphere, Rotate_Sphere, Scale_Sphere, Transform_Sphere, Invert_Sphere, Destroy_Sphere }; extern RAY *CM_Ray; extern long Ray_Sphere_Tests, Ray_Sphere_Tests_Succeeded; int All_Sphere_Intersections (Object, Ray, Depth_Stack) OBJECT *Object; RAY *Ray; ISTACK *Depth_Stack; { DBL Depth1, Depth2; VECTOR IPoint; register int Intersection_Found; Intersection_Found = FALSE; if (Intersect_Sphere (Ray, (SPHERE*) Object, &Depth1, &Depth2)) { VScale (IPoint, Ray->Direction, Depth1); VAddEq (IPoint, Ray->Initial); if (Point_In_Clip (&IPoint, Object->Clip)) { push_entry(Depth1,IPoint,Object,Depth_Stack); Intersection_Found = TRUE; } if (Depth2 != Depth1) { VScale (IPoint, Ray->Direction, Depth2); VAddEq (IPoint, Ray->Initial); if (Point_In_Clip (&IPoint, Object->Clip)) { push_entry(Depth2,IPoint,Object,Depth_Stack); Intersection_Found = TRUE; } } } return (Intersection_Found); } int All_Ellipsoid_Intersections (Object, Ray, Depth_Stack) OBJECT *Object; RAY *Ray; ISTACK *Depth_Stack; { DBL Depth1, Depth2, len; VECTOR IPoint, dv; register int Intersection_Found; RAY New_Ray; /* Transform the ray into the sphere's space */ MInvTransPoint(&New_Ray.Initial, &Ray->Initial, ((SPHERE *)Object)->Trans); MInvTransDirection(&New_Ray.Direction, &Ray->Direction, ((SPHERE *)Object)->Trans); VDot(len, New_Ray.Direction, New_Ray.Direction); if (len == 0.0) return 0; len = 1.0 / sqrt(len); VScaleEq(New_Ray.Direction, len); Intersection_Found = FALSE; if (Intersect_Sphere (&New_Ray, (SPHERE*) Object, &Depth1, &Depth2)) { VScale (IPoint, New_Ray.Direction, Depth1); VAddEq (IPoint, New_Ray.Initial); if (((SPHERE *)Object)->Trans != NULL) MTransPoint(&IPoint, &IPoint, ((SPHERE *)Object)->Trans); VSub(dv, IPoint, Ray->Initial); VLength(len, dv); if (Point_In_Clip (&IPoint, Object->Clip)) { push_entry(len,IPoint,Object,Depth_Stack); Intersection_Found = TRUE; } if (Depth2 != Depth1) { VScale (IPoint, New_Ray.Direction, Depth2); VAddEq (IPoint, New_Ray.Initial); if (((SPHERE *)Object)->Trans != NULL) MTransPoint(&IPoint, &IPoint, ((SPHERE *)Object)->Trans); VSub(dv, IPoint, Ray->Initial); VLength(len, dv); if (Point_In_Clip (&IPoint, Object->Clip)) { push_entry(len,IPoint,Object,Depth_Stack); Intersection_Found = TRUE; } } } return (Intersection_Found); } int Intersect_Sphere (Ray, Sphere, Depth1, Depth2) RAY *Ray; SPHERE *Sphere; DBL *Depth1, *Depth2; { VECTOR Origin_To_Center; DBL OCSquared, t_Closest_Approach, Half_Chord, t_Half_Chord_Squared; short inside; Ray_Sphere_Tests++; if (Ray == CM_Ray) { if (!Sphere->CMCached) { VSub (Sphere->CMOtoC, Sphere->Center, Ray->Initial); VDot (Sphere->CMOCSquared, Sphere->CMOtoC, Sphere->CMOtoC); Sphere->CMinside = (Sphere->CMOCSquared < Sphere->Radius_Squared); Sphere->CMCached = TRUE; } VDot (t_Closest_Approach, Sphere->CMOtoC, Ray->Direction); if (!Sphere->CMinside && (t_Closest_Approach < Sphere_Tolerance)) return (FALSE); t_Half_Chord_Squared = Sphere->Radius_Squared - Sphere->CMOCSquared + (t_Closest_Approach * t_Closest_Approach); } else { VSub (Origin_To_Center, Sphere->Center, Ray->Initial); VDot (OCSquared, Origin_To_Center, Origin_To_Center); inside = (OCSquared < Sphere->Radius_Squared); VDot (t_Closest_Approach, Origin_To_Center, Ray->Direction); if (!inside && (t_Closest_Approach < Sphere_Tolerance)) return (FALSE); t_Half_Chord_Squared = Sphere->Radius_Squared - OCSquared + (t_Closest_Approach * t_Closest_Approach); } if (t_Half_Chord_Squared < Sphere_Tolerance) return (FALSE); Half_Chord = sqrt (t_Half_Chord_Squared); *Depth1 = t_Closest_Approach + Half_Chord; *Depth2 = t_Closest_Approach - Half_Chord; if ((*Depth1 < Sphere_Tolerance) || (*Depth1 > Max_Distance)) if ((*Depth2 < Sphere_Tolerance) || (*Depth2 > Max_Distance)) return (FALSE); else *Depth1 = *Depth2; else if ((*Depth2 < Sphere_Tolerance) || (*Depth2 > Max_Distance)) *Depth2 = *Depth1; Ray_Sphere_Tests_Succeeded++; return (TRUE); } int Inside_Sphere (IPoint, Object) VECTOR *IPoint; OBJECT *Object; { VECTOR Origin_To_Center; DBL OCSquared; VSub (Origin_To_Center, ((SPHERE *)Object)->Center, *IPoint); VDot (OCSquared, Origin_To_Center, Origin_To_Center); if (((SPHERE *)Object)->Inverted) return (OCSquared - ((SPHERE *)Object)->Radius_Squared > Sphere_Tolerance); else return (OCSquared - ((SPHERE *)Object)->Radius_Squared < Sphere_Tolerance); } int Inside_Ellipsoid (IPoint, Object) VECTOR *IPoint; OBJECT *Object; { VECTOR Origin_To_Center; DBL OCSquared; VECTOR New_Point; /* Transform the point into the sphere's space */ MInvTransPoint(&New_Point, IPoint, ((SPHERE *)Object)->Trans); VSub (Origin_To_Center, ((SPHERE *)Object)->Center, New_Point); VDot (OCSquared, Origin_To_Center, Origin_To_Center); if (((SPHERE *)Object)->Inverted) return (OCSquared - ((SPHERE *)Object)->Radius_Squared > Sphere_Tolerance); else return (OCSquared - ((SPHERE *)Object)->Radius_Squared < Sphere_Tolerance); } void Sphere_Normal (Result, Object, IPoint) OBJECT *Object; VECTOR *Result, *IPoint; { VSub (*Result, *IPoint, ((SPHERE *)Object)->Center); VScaleEq (*Result, ((SPHERE *)Object)->Inverse_Radius); } void Ellipsoid_Normal (Result, Object, IPoint) OBJECT *Object; VECTOR *Result, *IPoint; { VECTOR New_Point; /* Transform the point into the sphere's space */ MInvTransPoint(&New_Point, IPoint, ((SPHERE *)Object)->Trans); VSub (*Result, New_Point, ((SPHERE *)Object)->Center); VScaleEq (*Result, ((SPHERE *)Object)->Inverse_Radius); MTransNormal(Result, Result, ((SPHERE *)Object)->Trans); VNormalize(*Result, *Result); } void *Copy_Sphere (Object) OBJECT *Object; { SPHERE *New; New = Create_Sphere (); *New = *((SPHERE *) Object); New->Trans = Copy_Transform(((SPHERE *)Object)->Trans); return (New); } void Translate_Sphere (Object, Vector) OBJECT *Object; VECTOR *Vector; { TRANSFORM Trans; if (((SPHERE *)Object)->Trans == NULL) { VAddEq (((SPHERE *) Object)->Center, *Vector); VAddEq(Object->Bounds.Lower_Left, *Vector); } else { Compute_Translation_Transform(&Trans, Vector); Transform_Sphere(Object, &Trans); } } void Rotate_Sphere (Object, Vector) OBJECT *Object; VECTOR *Vector; { TRANSFORM Trans; SPHERE *Sphere = (SPHERE *) Object; Compute_Rotation_Transform (&Trans, Vector); if (Sphere->Trans == NULL) { MTransPoint(&Sphere->Center, &Sphere->Center, &Trans); Make_Vector(&Sphere->Bounds.Lower_Left, Sphere->Center.x - Sphere->Radius, Sphere->Center.y - Sphere->Radius, Sphere->Center.z - Sphere->Radius); Make_Vector(&Sphere->Bounds.Lengths, 2.0 * Sphere->Radius, 2.0 * Sphere->Radius, 2.0 * Sphere->Radius); } else { Transform_Sphere (Object, &Trans); } } void Scale_Sphere (Object, Vector) OBJECT *Object; VECTOR *Vector; { SPHERE *Sphere = (SPHERE *) Object; TRANSFORM Trans; if ((Vector->x != Vector->y) || (Vector->x != Vector->z)) if (Sphere->Trans == NULL) { Sphere->Methods = &Ellipsoid_Methods; Sphere->Trans = Create_Transform(); } if (Sphere->Trans == NULL) { VScaleEq (Sphere->Center, Vector->x); Sphere->Radius *= Vector->x; Sphere->Radius_Squared = Sphere->Radius * Sphere->Radius; Sphere->Inverse_Radius = 1.0 / Sphere->Radius; Make_Vector(&Sphere->Bounds.Lower_Left, Sphere->Center.x - Sphere->Radius, Sphere->Center.y - Sphere->Radius, Sphere->Center.z - Sphere->Radius); Make_Vector(&Sphere->Bounds.Lengths, 2.0 * Sphere->Radius, 2.0 * Sphere->Radius, 2.0 * Sphere->Radius); } else { Compute_Scaling_Transform(&Trans, Vector); Transform_Sphere(Object, &Trans); } } void Invert_Sphere (Object) OBJECT *Object; { ((SPHERE *) Object)->Inverted ^= TRUE; } SPHERE *Create_Sphere () { SPHERE *New; if ((New = (SPHERE *) malloc (sizeof (SPHERE))) == NULL) MAError ("sphere"); INIT_OBJECT_FIELDS(New, SPHERE_OBJECT, &Sphere_Methods) Make_Vector (&(New->Center), 0.0, 0.0, 0.0); New->Radius = 1.0; New->Radius_Squared = 1.0; New->Inverse_Radius = 1.0; New->CMCached = FALSE; New->Trans = NULL; New->Inverted = FALSE; /* CMOtoC, CMOtoCSquared and CMinside are only valid when CMCached */ return (New); } void Transform_Sphere (Object, Trans) OBJECT *Object; TRANSFORM *Trans; { SPHERE *Sphere = (SPHERE *)Object; if (Sphere->Trans == NULL) { Sphere->Methods = &Ellipsoid_Methods; Sphere->Trans = Create_Transform(); } Compose_Transforms(Sphere->Trans, Trans); Make_Vector(&Sphere->Bounds.Lower_Left, Sphere->Center.x - Sphere->Radius, Sphere->Center.y - Sphere->Radius, Sphere->Center.z - Sphere->Radius); Make_Vector(&Sphere->Bounds.Lengths, 2.0 * Sphere->Radius, 2.0 * Sphere->Radius, 2.0 * Sphere->Radius); recompute_bbox(&Object->Bounds, Sphere->Trans); } void Destroy_Sphere (Object) OBJECT *Object; { Destroy_Transform(((SPHERE *)Object)->Trans); free (Object); }