/**************************************************************************** * boxes.c * * This module implements the box primitive. * This file was written by Alexander Enzmann. He wrote the code for * boxes and generously provided us these enhancements. * * from Persistence of Vision(tm) Ray Tracer * Copyright 1996 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 "povray.h" #include "vector.h" #include "povproto.h" #include "bbox.h" #include "boxes.h" #include "matrices.h" #include "objects.h" /***************************************************************************** * Local preprocessor defines ******************************************************************************/ /* Minimal intersection depth. */ #define DEPTH_TOLERANCE 1.0e-6 /* Two values are equal if their difference is small than CLOSE_TOLERANCE. */ #define CLOSE_TOLERANCE 1.0e-6 /* Side hit. */ #define SIDE_X_0 1 #define SIDE_X_1 2 #define SIDE_Y_0 3 #define SIDE_Y_1 4 #define SIDE_Z_0 5 #define SIDE_Z_1 6 /***************************************************************************** * Static functions ******************************************************************************/ static int All_Box_Intersections PARAMS((OBJECT *Object, RAY *Ray, ISTACK *Depth_Stack)); static int Inside_Box PARAMS((VECTOR point, OBJECT *Object)); static void Box_Normal PARAMS((VECTOR Result, OBJECT *Object, INTERSECTION *Inter)); static void Translate_Box PARAMS((OBJECT *Object, VECTOR Vector, TRANSFORM *Trans)); static void Rotate_Box PARAMS((OBJECT *Object, VECTOR Vector, TRANSFORM *Trans)); static void Scale_Box PARAMS((OBJECT *Object, VECTOR Vector, TRANSFORM *Trans)); static void Transform_Box PARAMS((OBJECT *Object, TRANSFORM *Trans)); static void Invert_Box PARAMS((OBJECT *Object)); /***************************************************************************** * Local variables ******************************************************************************/ METHODS Box_Methods = { All_Box_Intersections, Inside_Box, Box_Normal, Copy_Box, Translate_Box, Rotate_Box, Scale_Box, Transform_Box, Invert_Box, Destroy_Box }; /***************************************************************************** * * FUNCTION * * All_Box_Intersections * * INPUT * * OUTPUT * * RETURNS * * AUTHOR * * Alexander Enzmann * * DESCRIPTION * * - * * CHANGES * * - * ******************************************************************************/ static int All_Box_Intersections(Object, Ray, Depth_Stack) OBJECT *Object; RAY *Ray; ISTACK *Depth_Stack; { int Intersection_Found; int Side1, Side2; DBL Depth1, Depth2; VECTOR IPoint; Increase_Counter(stats[Ray_Box_Tests]); Intersection_Found = FALSE; if (Intersect_Box(Ray, (BOX *)Object, &Depth1, &Depth2, &Side1, &Side2)) { if (Depth1 > DEPTH_TOLERANCE) { VEvaluateRay(IPoint, Ray->Initial, Depth1, Ray->Direction); if (Point_In_Clip(IPoint, Object->Clip)) { push_entry_i1(Depth1,IPoint,Object,Side1,Depth_Stack); Intersection_Found = TRUE; } } VEvaluateRay(IPoint, Ray->Initial, Depth2, Ray->Direction); if (Point_In_Clip(IPoint, Object->Clip)) { push_entry_i1(Depth2,IPoint,Object,Side2,Depth_Stack); Intersection_Found = TRUE; } } if (Intersection_Found) { Increase_Counter(stats[Ray_Box_Tests_Succeeded]); } return (Intersection_Found); } /***************************************************************************** * * FUNCTION * * Intersect_Box * * INPUT * * OUTPUT * * RETURNS * * AUTHOR * * Alexander Enzmann * * DESCRIPTION * * - * * CHANGES * * Sep 1994 : Added code to decide which side was hit in the case * intersection points are close to each other. This removes * some ugly artefacts one could observe at the corners of * boxes due to the usage of the wrong normal vector. [DB] * ******************************************************************************/ int Intersect_Box(Ray, box, Depth1, Depth2, Side1, Side2) RAY *Ray; BOX *box; DBL *Depth1, *Depth2; int *Side1, *Side2; { int smin = 0, smax = 0; /* Side hit for min/max intersection. */ DBL t, tmin, tmax; VECTOR P, D; /* Transform the point into the boxes space */ if (box->Trans != NULL) { MInvTransPoint(P, Ray->Initial, box->Trans); MInvTransDirection(D, Ray->Direction, box->Trans); } else { Assign_Vector(P, Ray->Initial); Assign_Vector(D, Ray->Direction); } tmin = 0.0; tmax = BOUND_HUGE; /* * Sides first. */ if (D[X] < -EPSILON) { t = (box->bounds[0][X] - P[X]) / D[X]; if (t < tmin) return(FALSE); if (t <= tmax) { smax = SIDE_X_0; tmax = t; } t = (box->bounds[1][X] - P[X]) / D[X]; if (t >= tmin) { if (t > tmax) return(FALSE); smin = SIDE_X_1; tmin = t; } } else { if (D[X] > EPSILON) { t = (box->bounds[1][X] - P[X]) / D[X]; if (t < tmin) return(FALSE); if (t <= tmax) { smax = SIDE_X_1; tmax = t; } t = (box->bounds[0][X] - P[X]) / D[X]; if (t >= tmin) { if (t > tmax) return(FALSE); smin = SIDE_X_0; tmin = t; } } else { if ((P[X] < box->bounds[0][X]) || (P[X] > box->bounds[1][X])) { return(FALSE); } } } /* * Check Top/Bottom. */ if (D[Y] < -EPSILON) { t = (box->bounds[0][Y] - P[Y]) / D[Y]; if (t < tmin) return(FALSE); if (t <= tmax - CLOSE_TOLERANCE) { smax = SIDE_Y_0; tmax = t; } else { /* * If intersection points are close to each other find out * which side to use, i.e. is most probably hit. [DB 9/94] */ if (t <= tmax + CLOSE_TOLERANCE) { if (-D[Y] > fabs(D[X])) smax = SIDE_Y_0; } } t = (box->bounds[1][Y] - P[Y]) / D[Y]; if (t >= tmin + CLOSE_TOLERANCE) { if (t > tmax) return(FALSE); smin = SIDE_Y_1; tmin = t; } else { /* * If intersection points are close to each other find out * which side to use, i.e. is most probably hit. [DB 9/94] */ if (t >= tmin - CLOSE_TOLERANCE) { if (-D[Y] > fabs(D[X])) smin = SIDE_Y_1; } } } else { if (D[Y] > EPSILON) { t = (box->bounds[1][Y] - P[Y]) / D[Y]; if (t < tmin) return(FALSE); if (t <= tmax - CLOSE_TOLERANCE) { smax = SIDE_Y_1; tmax = t; } else { /* * If intersection points are close to each other find out * which side to use, i.e. is most probably hit. [DB 9/94] */ if (t <= tmax + CLOSE_TOLERANCE) { if (D[Y] > fabs(D[X])) smax = SIDE_Y_1; } } t = (box->bounds[0][Y] - P[Y]) / D[Y]; if (t >= tmin + CLOSE_TOLERANCE) { if (t > tmax) return(FALSE); smin = SIDE_Y_0; tmin = t; } else { /* * If intersection points are close to each other find out * which side to use, i.e. is most probably hit. [DB 9/94] */ if (t >= tmin - CLOSE_TOLERANCE) { if (D[Y] > fabs(D[X])) smin = SIDE_Y_0; } } } else { if ((P[Y] < box->bounds[0][Y]) || (P[Y] > box->bounds[1][Y])) { return(FALSE); } } } /* Now front/back */ if (D[Z] < -EPSILON) { t = (box->bounds[0][Z] - P[Z]) / D[Z]; if (t < tmin) return(FALSE); if (t <= tmax - CLOSE_TOLERANCE) { smax = SIDE_Z_0; tmax = t; } else { /* * If intersection points are close to each other find out * which side to use, i.e. is most probably hit. [DB 9/94] */ if (t <= tmax + CLOSE_TOLERANCE) { switch (smax) { case SIDE_X_0 : case SIDE_X_1 : if (-D[Z] > fabs(D[X])) smax = SIDE_Z_0; break; case SIDE_Y_0 : case SIDE_Y_1 : if (-D[Z] > fabs(D[Y])) smax = SIDE_Z_0; break; } } } t = (box->bounds[1][Z] - P[Z]) / D[Z]; if (t >= tmin + CLOSE_TOLERANCE) { if (t > tmax) return(FALSE); smin = SIDE_Z_1; tmin = t; } else { /* * If intersection points are close to each other find out * which side to use, i.e. is most probably hit. [DB 9/94] */ if (t >= tmin - CLOSE_TOLERANCE) { switch (smin) { case SIDE_X_0 : case SIDE_X_1 : if (-D[Z] > fabs(D[X])) smin = SIDE_Z_1; break; case SIDE_Y_0 : case SIDE_Y_1 : if (-D[Z] > fabs(D[Y])) smin = SIDE_Z_1; break; } } } } else { if (D[Z] > EPSILON) { t = (box->bounds[1][Z] - P[Z]) / D[Z]; if (t < tmin) return(FALSE); if (t <= tmax - CLOSE_TOLERANCE) { smax = SIDE_Z_1; tmax = t; } else { /* * If intersection points are close to each other find out * which side to use, i.e. is most probably hit. [DB 9/94] */ if (t <= tmax + CLOSE_TOLERANCE) { switch (smax) { case SIDE_X_0 : case SIDE_X_1 : if (D[Z] > fabs(D[X])) smax = SIDE_Z_1; break; case SIDE_Y_0 : case SIDE_Y_1 : if (D[Z] > fabs(D[Y])) smax = SIDE_Z_1; break; } } } t = (box->bounds[0][Z] - P[Z]) / D[Z]; if (t >= tmin + CLOSE_TOLERANCE) { if (t > tmax) return(FALSE); smin = SIDE_Z_0; tmin = t; } else { /* * If intersection points are close to each other find out * which side to use, i.e. is most probably hit. [DB 9/94] */ if (t >= tmin - CLOSE_TOLERANCE) { switch (smin) { case SIDE_X_0 : case SIDE_X_1 : if (D[Z] > fabs(D[X])) smin = SIDE_Z_0; break; case SIDE_Y_0 : case SIDE_Y_1 : if (D[Z] > fabs(D[Y])) smin = SIDE_Z_0; break; } } } } else { if ((P[Z] < box->bounds[0][Z]) || (P[Z] > box->bounds[1][Z])) { return(FALSE); } } } if (tmax < DEPTH_TOLERANCE) { return (FALSE); } *Depth1 = tmin; *Depth2 = tmax; *Side1 = smin; *Side2 = smax; return(TRUE); } /***************************************************************************** * * FUNCTION * * Inside_Box * * INPUT * * OUTPUT * * RETURNS * * AUTHOR * * Alexander Enzmann * * DESCRIPTION * * - * * CHANGES * * - * ******************************************************************************/ static int Inside_Box(IPoint, Object) VECTOR IPoint; OBJECT *Object; { VECTOR New_Point; BOX *box = (BOX *) Object; /* Transform the point into box space. */ if (box->Trans != NULL) { MInvTransPoint(New_Point, IPoint, box->Trans); } else { Assign_Vector(New_Point,IPoint); } /* Test to see if we are outside the box. */ if ((New_Point[X] < box->bounds[0][X]) || (New_Point[X] > box->bounds[1][X])) { return (Test_Flag(box, INVERTED_FLAG)); } if ((New_Point[Y] < box->bounds[0][Y]) || (New_Point[Y] > box->bounds[1][Y])) { return (Test_Flag(box, INVERTED_FLAG)); } if ((New_Point[Z] < box->bounds[0][Z]) || (New_Point[Z] > box->bounds[1][Z])) { return (Test_Flag(box, INVERTED_FLAG)); } /* Inside the box. */ return (!Test_Flag(box, INVERTED_FLAG)); } /***************************************************************************** * * FUNCTION * * Box_Normal * * INPUT * * OUTPUT * * RETURNS * * AUTHOR * * Alexander Enzmann * * DESCRIPTION * * - * * CHANGES * * - * ******************************************************************************/ static void Box_Normal(Result, Object, Inter) OBJECT *Object; VECTOR Result; INTERSECTION *Inter; { switch (Inter->i1) { case SIDE_X_0: Make_Vector(Result, -1.0, 0.0, 0.0); break; case SIDE_X_1: Make_Vector(Result, 1.0, 0.0, 0.0); break; case SIDE_Y_0: Make_Vector(Result, 0.0, -1.0, 0.0); break; case SIDE_Y_1: Make_Vector(Result, 0.0, 1.0, 0.0); break; case SIDE_Z_0: Make_Vector(Result, 0.0, 0.0, -1.0); break; case SIDE_Z_1: Make_Vector(Result, 0.0, 0.0, 1.0); break; default: Error("Unknown box side in Box_Normal().\n"); } /* Transform the point into the boxes space. */ if (((BOX *)Object)->Trans != NULL) { MTransNormal(Result, Result, ((BOX *)Object)->Trans); VNormalize(Result, Result); } } /***************************************************************************** * * FUNCTION * * Translate_Box * * INPUT * * OUTPUT * * RETURNS * * AUTHOR * * Alexander Enzmann * * DESCRIPTION * * - * * CHANGES * * - * ******************************************************************************/ static void Translate_Box(Object, Vector, Trans) OBJECT *Object; VECTOR Vector; TRANSFORM *Trans; { if (((BOX *)Object)->Trans == NULL) { VAddEq(((BOX *)Object)->bounds[0], Vector); VAddEq(((BOX *)Object)->bounds[1], Vector); Compute_Box_BBox((BOX *)Object); } else { Transform_Box(Object, Trans); } } /***************************************************************************** * * FUNCTION * * Rotate_Box * * INPUT * * OUTPUT * * RETURNS * * AUTHOR * * Alexander Enzmann * * DESCRIPTION * * - * * CHANGES * * - * ******************************************************************************/ static void Rotate_Box(Object, Vector, Trans) OBJECT *Object; VECTOR Vector; TRANSFORM *Trans; { Transform_Box(Object, Trans); } /***************************************************************************** * * FUNCTION * * Scale_Box * * INPUT * * OUTPUT * * RETURNS * * AUTHOR * * Alexander Enzmann * * DESCRIPTION * * - * * CHANGES * * - * ******************************************************************************/ static void Scale_Box(Object, Vector, Trans) OBJECT *Object; VECTOR Vector; TRANSFORM *Trans; { DBL temp; BOX *Box = (BOX *)Object; if (((BOX *)Object)->Trans == NULL) { VEvaluateEq(Box->bounds[0], Vector); VEvaluateEq(Box->bounds[1], Vector); if (Box->bounds[0][X] > Box->bounds[1][X]) { temp = Box->bounds[0][X]; Box->bounds[0][X] = Box->bounds[1][X]; Box->bounds[1][X] = temp; } if (Box->bounds[0][Y] > Box->bounds[1][Y]) { temp = Box->bounds[0][Y]; Box->bounds[0][Y] = Box->bounds[1][Y]; Box->bounds[1][Y] = temp; } if (Box->bounds[0][Z] > Box->bounds[1][Z]) { temp = Box->bounds[0][Z]; Box->bounds[0][Z] = Box->bounds[1][Z]; Box->bounds[1][Z] = temp; } Compute_Box_BBox((BOX *)Object); } else { Transform_Box(Object, Trans); } } /***************************************************************************** * * FUNCTION * * Invert_Box * * INPUT * * OUTPUT * * RETURNS * * AUTHOR * * Alexander Enzmann * * DESCRIPTION * * - * * CHANGES * * - * ******************************************************************************/ static void Invert_Box(Object) OBJECT *Object; { Invert_Flag(Object, INVERTED_FLAG); } /***************************************************************************** * * FUNCTION * * Transform_Box * * INPUT * * OUTPUT * * RETURNS * * AUTHOR * * Alexander Enzmann * * DESCRIPTION * * - * * CHANGES * * - * ******************************************************************************/ static void Transform_Box(Object, Trans) OBJECT *Object; TRANSFORM *Trans; { BOX *box = (BOX *)Object; if (box->Trans == NULL) { box->Trans = Create_Transform(); } Compose_Transforms(box->Trans, Trans); Compute_Box_BBox(box); } /***************************************************************************** * * FUNCTION * * Create_Box * * INPUT * * OUTPUT * * RETURNS * * AUTHOR * * Alexander Enzmann * * DESCRIPTION * * - * * CHANGES * * - * ******************************************************************************/ BOX *Create_Box() { BOX *New; New = (BOX *)POV_MALLOC(sizeof(BOX), "box"); INIT_OBJECT_FIELDS(New, BOX_OBJECT, &Box_Methods) Make_Vector(New->bounds[0], -1.0, -1.0, -1.0); Make_Vector(New->bounds[1], 1.0, 1.0, 1.0); Make_BBox(New->BBox, -1.0, -1.0, -1.0, 2.0, 2.0, 2.0); New->Trans = NULL; return (New); } /***************************************************************************** * * FUNCTION * * Copy_Box * * INPUT * * OUTPUT * * RETURNS * * AUTHOR * * Alexander Enzmann * * DESCRIPTION * * - * * CHANGES * * - * ******************************************************************************/ void *Copy_Box(Object) OBJECT *Object; { BOX *New; New = Create_Box(); /* Copy box. */ *New = *((BOX *)Object); New->Trans = Copy_Transform(((BOX *)Object)->Trans); return (New); } /***************************************************************************** * * FUNCTION * * Destroy_Box * * INPUT * * OUTPUT * * RETURNS * * AUTHOR * * Alexander Enzmann * * DESCRIPTION * * - * * CHANGES * * - * ******************************************************************************/ void Destroy_Box(Object) OBJECT *Object; { Destroy_Transform(((BOX *)Object)->Trans); POV_FREE (Object); } /***************************************************************************** * * FUNCTION * * Compute_Box_BBox * * INPUT * * Box - Box * * OUTPUT * * Box * * RETURNS * * AUTHOR * * Dieter Bayer * * DESCRIPTION * * Calculate the bounding box of a box. * * CHANGES * * Aug 1994 : Creation. * ******************************************************************************/ void Compute_Box_BBox(Box) BOX *Box; { Assign_BBox_Vect(Box->BBox.Lower_Left, Box->bounds[0]); VSub(Box->BBox.Lengths, Box->bounds[1], Box->bounds[0]); if (Box->Trans != NULL) { Recompute_BBox(&Box->BBox, Box->Trans); } }