/**************************************************************************** * csg.c * * This module implements routines for constructive solid geometry. * * 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 "csg.h" #include "hfield.h" #include "matrices.h" #include "objects.h" #include "planes.h" #include "quadrics.h" /***************************************************************************** * Local preprocessor defines ******************************************************************************/ /***************************************************************************** * Static functions ******************************************************************************/ static int All_CSG_Union_Intersections PARAMS((OBJECT *Object, RAY *Ray, ISTACK *Depth_Stack)); static int All_CSG_Merge_Intersections PARAMS((OBJECT *Object, RAY *Ray, ISTACK *Depth_Stack)); static int All_CSG_Intersect_Intersections PARAMS((OBJECT *Object, RAY *Ray, ISTACK *Depth_Stack)); static int Inside_CSG_Union PARAMS((VECTOR point, OBJECT *Object)); static int Inside_CSG_Intersection PARAMS((VECTOR point, OBJECT *Object)); static void *Copy_CSG PARAMS((OBJECT *Object)); static void Translate_CSG PARAMS((OBJECT *Object, VECTOR Vector, TRANSFORM *Trans)); static void Rotate_CSG PARAMS((OBJECT *Object, VECTOR Vector, TRANSFORM *Trans)); static void Scale_CSG PARAMS((OBJECT *Object, VECTOR Vector, TRANSFORM *Trans)); static void Transform_CSG PARAMS((OBJECT *Object, TRANSFORM *Trans)); static void Destroy_CSG PARAMS((OBJECT *Object)); static void Invert_CSG_Union PARAMS((OBJECT *Object)); static void Invert_CSG_Intersection PARAMS((OBJECT *Object)); /***************************************************************************** * Local variables ******************************************************************************/ METHODS CSG_Union_Methods = { All_CSG_Union_Intersections, Inside_CSG_Union, NULL /*Normal*/, Copy_CSG, Translate_CSG, Rotate_CSG, Scale_CSG, Transform_CSG, Invert_CSG_Union, Destroy_CSG }; METHODS CSG_Merge_Methods = { All_CSG_Merge_Intersections, Inside_CSG_Union, NULL /*Normal*/, Copy_CSG, Translate_CSG, Rotate_CSG, Scale_CSG, Transform_CSG, Invert_CSG_Union, Destroy_CSG }; METHODS CSG_Intersection_Methods = { All_CSG_Intersect_Intersections, Inside_CSG_Intersection, NULL /*Normal*/, Copy_CSG, Translate_CSG, Rotate_CSG, Scale_CSG, Transform_CSG, Invert_CSG_Intersection, Destroy_CSG }; /***************************************************************************** * * FUNCTION * * All_CSG_Union_Intersections * * INPUT * * OUTPUT * * RETURNS * * AUTHOR * * POV-Ray Team * * DESCRIPTION * * - * * CHANGES * * Sep 1994 : Added code to count intersection tests. [DB] * ******************************************************************************/ static int All_CSG_Union_Intersections (Object, Ray, Depth_Stack) OBJECT *Object; RAY *Ray; ISTACK *Depth_Stack; { int Found; OBJECT *Current_Sib, *Clip; ISTACK *Local_Stack; INTERSECTION *Sibling_Intersection; Increase_Counter(stats[Ray_CSG_Union_Tests]); Found = FALSE; /* Use shortcut if no clip. */ if ((Clip = Object->Clip) == NULL) { for (Current_Sib = ((CSG *)Object)->Children; Current_Sib != NULL; Current_Sib = Current_Sib->Sibling) { if (Ray_In_Bound (Ray, Current_Sib->Bound)) { if (All_Intersections (Current_Sib, Ray, Depth_Stack)) { Found = TRUE; } } } } else { Local_Stack = open_istack(); for (Current_Sib = ((CSG *)Object)->Children; Current_Sib != NULL; Current_Sib = Current_Sib->Sibling) { if (Ray_In_Bound (Ray, Current_Sib->Bound)) { if (All_Intersections (Current_Sib, Ray, Local_Stack)) { while ((Sibling_Intersection = pop_entry(Local_Stack)) != NULL) { if (Point_In_Clip (Sibling_Intersection->IPoint, Clip)) { push_copy (Depth_Stack, Sibling_Intersection); Found = TRUE; } } } } } close_istack (Local_Stack); } if (Found) { Increase_Counter(stats[Ray_CSG_Union_Tests_Succeeded]); } return (Found); } /***************************************************************************** * * FUNCTION * * All_CSG_Intersection_Intersections * * INPUT * * OUTPUT * * RETURNS * * AUTHOR * * POV-Ray Team * * DESCRIPTION * * - * * CHANGES * * Sep 1994 : Added code to count intersection tests. [DB] * ******************************************************************************/ static int All_CSG_Intersect_Intersections (Object, Ray, Depth_Stack) OBJECT *Object; RAY *Ray; ISTACK *Depth_Stack; { int Maybe_Found, Found; OBJECT *Current_Sib, *Inside_Sib; ISTACK *Local_Stack; INTERSECTION *Sibling_Intersection; Increase_Counter(stats[Ray_CSG_Intersection_Tests]); Local_Stack = open_istack (); Found = FALSE; for (Current_Sib = ((CSG *)Object)->Children; Current_Sib != NULL; Current_Sib = Current_Sib->Sibling) { if (Ray_In_Bound (Ray, Current_Sib->Bound)) { if (All_Intersections (Current_Sib, Ray, Local_Stack)) { while ((Sibling_Intersection = pop_entry(Local_Stack)) != NULL) { Maybe_Found = TRUE; for (Inside_Sib = ((CSG *)Object)->Children; Inside_Sib != NULL; Inside_Sib = Inside_Sib->Sibling) { if (Inside_Sib != Current_Sib) { if (!Inside_Object (Sibling_Intersection->IPoint, Inside_Sib)) { Maybe_Found = FALSE; break; } } } if (Maybe_Found) { if (Point_In_Clip (Sibling_Intersection->IPoint, Object->Clip)) { push_copy(Depth_Stack, Sibling_Intersection); Found = TRUE; } } } } } } close_istack (Local_Stack); if (Found) { Increase_Counter(stats[Ray_CSG_Intersection_Tests_Succeeded]); } return (Found); } /***************************************************************************** * * FUNCTION * * All_CSG_Merge_Intersections * * INPUT * * OUTPUT * * RETURNS * * AUTHOR * * POV-Ray Team * * DESCRIPTION * * - * * CHANGES * * Sep 1994 : Added code to count intersection tests. [DB] * ******************************************************************************/ static int All_CSG_Merge_Intersections (Object, Ray, Depth_Stack) OBJECT *Object; RAY *Ray; ISTACK *Depth_Stack; { int Found, inside_flag; OBJECT *Sib1, *Sib2; ISTACK *Local_Stack; INTERSECTION *Sibling_Intersection; Increase_Counter(stats[Ray_CSG_Merge_Tests]); Found = FALSE; Local_Stack = open_istack (); for (Sib1 = ((CSG *)Object)->Children; Sib1 != NULL; Sib1 = Sib1->Sibling) { if (Ray_In_Bound (Ray, Sib1->Bound)) { if (All_Intersections (Sib1, Ray, Local_Stack)) { while ((Sibling_Intersection = pop_entry (Local_Stack)) != NULL) { if (Point_In_Clip (Sibling_Intersection->IPoint, Object->Clip)) { inside_flag = TRUE; for (Sib2 = ((CSG *)Object)->Children; Sib2 != NULL && inside_flag == TRUE; Sib2 = Sib2->Sibling) { if (Sib1 != Sib2) { if (Inside_Object(Sibling_Intersection->IPoint, Sib2)) { inside_flag = FALSE; } } } if (inside_flag == TRUE) { Found = TRUE; push_copy (Depth_Stack, Sibling_Intersection); } } } } } } close_istack (Local_Stack); if (Found) { Increase_Counter(stats[Ray_CSG_Merge_Tests_Succeeded]); } return (Found); } /***************************************************************************** * * FUNCTION * * Inside_CSG_Union * * INPUT * * OUTPUT * * RETURNS * * AUTHOR * * POV-Ray Team * * DESCRIPTION * * - * * CHANGES * * - * ******************************************************************************/ static int Inside_CSG_Union (IPoint, Object) VECTOR IPoint; OBJECT *Object; { OBJECT *Current_Sib; for (Current_Sib = ((CSG *)Object)->Children; Current_Sib != NULL; Current_Sib = Current_Sib->Sibling) { if (Inside_Object (IPoint, Current_Sib)) { return (TRUE); } } return (FALSE); } /***************************************************************************** * * FUNCTION * * Inside_CSG_Intersection * * INPUT * * OUTPUT * * RETURNS * * AUTHOR * * POV-Ray Team * * DESCRIPTION * * - * * CHANGES * * - * ******************************************************************************/ static int Inside_CSG_Intersection (IPoint, Object) OBJECT *Object; VECTOR IPoint; { OBJECT *Current_Sib; for (Current_Sib = ((CSG *)Object)->Children; Current_Sib != NULL; Current_Sib = Current_Sib->Sibling) { if (!Inside_Object (IPoint, Current_Sib)) { return (FALSE); } } return (TRUE); } /***************************************************************************** * * FUNCTION * * Translate_CSG * * INPUT * * OUTPUT * * RETURNS * * AUTHOR * * POV-Ray Team * * DESCRIPTION * * - * * CHANGES * * - * ******************************************************************************/ static void Translate_CSG (Object, Vector, Trans) OBJECT *Object; VECTOR Vector; TRANSFORM *Trans; { OBJECT *Sib; for (Sib = ((CSG *) Object)->Children; Sib != NULL; Sib = Sib->Sibling) { Translate_Object(Sib, Vector, Trans); } Recompute_BBox(&Object->BBox, Trans); } /***************************************************************************** * * FUNCTION * * Rotate_CSG * * INPUT * * OUTPUT * * RETURNS * * AUTHOR * * POV-Ray Team * * DESCRIPTION * * - * * CHANGES * * - * ******************************************************************************/ static void Rotate_CSG (Object, Vector, Trans) OBJECT *Object; VECTOR Vector; TRANSFORM *Trans; { OBJECT *Sib; for (Sib = ((CSG *) Object)->Children; Sib != NULL; Sib = Sib->Sibling) { Rotate_Object(Sib, Vector, Trans); } Recompute_BBox(&Object->BBox, Trans); } /***************************************************************************** * * FUNCTION * * Scale_CSG * * INPUT * * OUTPUT * * RETURNS * * AUTHOR * * POV-Ray Team * * DESCRIPTION * * - * * CHANGES * * - * ******************************************************************************/ static void Scale_CSG (Object, Vector, Trans) OBJECT *Object; VECTOR Vector; TRANSFORM *Trans; { OBJECT *Sib; for (Sib = ((CSG *) Object)->Children; Sib != NULL; Sib = Sib->Sibling) { Scale_Object(Sib, Vector, Trans); } Recompute_BBox(&Object->BBox, Trans); } /***************************************************************************** * * FUNCTION * * Transform_CSG * * INPUT * * OUTPUT * * RETURNS * * AUTHOR * * POV-Ray Team * * DESCRIPTION * * - * * CHANGES * * - * ******************************************************************************/ static void Transform_CSG (Object, Trans) OBJECT *Object; TRANSFORM *Trans; { OBJECT *Sib; for (Sib = ((CSG *) Object)->Children; Sib != NULL; Sib = Sib->Sibling) { Transform_Object (Sib, Trans); } Recompute_BBox(&Object->BBox, Trans); } /***************************************************************************** * * FUNCTION * * Invert_CSG_Union * * INPUT * * OUTPUT * * RETURNS * * AUTHOR * * POV-Ray Team * * DESCRIPTION * * - * * CHANGES * * - * ******************************************************************************/ static void Invert_CSG_Union (Object) OBJECT *Object; { OBJECT *Sib; Object->Methods = &CSG_Intersection_Methods; for (Sib = ((CSG *)Object)->Children; Sib != NULL; Sib = Sib->Sibling) { Invert_Object (Sib); } Invert_Flag(Object, INVERTED_FLAG); } /***************************************************************************** * * FUNCTION * * Invert_CSG_Intersection * * INPUT * * OUTPUT * * RETURNS * * AUTHOR * * POV-Ray Team * * DESCRIPTION * * - * * CHANGES * * - * ******************************************************************************/ static void Invert_CSG_Intersection (Object) OBJECT *Object; { OBJECT *Sib; Object->Methods = &CSG_Union_Methods; for (Sib = ((CSG *)Object)->Children; Sib != NULL; Sib = Sib->Sibling) { Invert_Object (Sib); } Invert_Flag(Object, INVERTED_FLAG); } /***************************************************************************** * * FUNCTION * * Create_CSG_Union * * INPUT * * OUTPUT * * RETURNS * * AUTHOR * * POV-Ray Team * * DESCRIPTION * * - * * CHANGES * * - * ******************************************************************************/ CSG *Create_CSG_Union () { CSG *New; New = (CSG *)POV_MALLOC(sizeof (CSG), "union"); INIT_OBJECT_FIELDS(New, UNION_OBJECT, &CSG_Union_Methods) New->Children = NULL; return (New); } /***************************************************************************** * * FUNCTION * * Create_CSG_Merge * * INPUT * * OUTPUT * * RETURNS * * AUTHOR * * POV-Ray Team * * DESCRIPTION * * - * * CHANGES * * - * ******************************************************************************/ CSG *Create_CSG_Merge () { CSG *New; New = (CSG *)POV_MALLOC(sizeof (CSG), "merge"); INIT_OBJECT_FIELDS(New, MERGE_OBJECT, &CSG_Merge_Methods) New->Children = NULL; return (New); } /***************************************************************************** * * FUNCTION * * Create_CSG_Intersection * * INPUT * * OUTPUT * * RETURNS * * AUTHOR * * POV-Ray Team * * DESCRIPTION * * - * * CHANGES * * - * ******************************************************************************/ CSG *Create_CSG_Intersection () { CSG *New; New = (CSG *)POV_MALLOC(sizeof (CSG), "intersection"); INIT_OBJECT_FIELDS(New, INTERSECTION_OBJECT, &CSG_Intersection_Methods) New->Children = NULL; return (New); } /***************************************************************************** * * FUNCTION * * Copy_CSG * * INPUT * * OUTPUT * * RETURNS * * AUTHOR * * POV-Ray Team * * DESCRIPTION * * - * * CHANGES * * - * ******************************************************************************/ static void *Copy_CSG (Object) OBJECT *Object; { CSG *New; OBJECT *Old_Sib, *New_Sib, *Prev_Sib; New = (CSG *)POV_MALLOC(sizeof (CSG), "csg"); *New = *(CSG *)Object; New->Children = Prev_Sib = NULL; for (Old_Sib = ((CSG *)Object)->Children; Old_Sib != NULL; Old_Sib = Old_Sib->Sibling) { New_Sib = Copy_Object (Old_Sib); if (New->Children == NULL) { New->Children = New_Sib; } if (Prev_Sib != NULL) { Prev_Sib->Sibling = New_Sib; } Prev_Sib = New_Sib; } return (New); } /***************************************************************************** * * FUNCTION * * Destroy_CSG * * INPUT * * OUTPUT * * RETURNS * * AUTHOR * * POV-Ray Team * * DESCRIPTION * * - * * CHANGES * * - * ******************************************************************************/ static void Destroy_CSG (Object) OBJECT *Object; { Destroy_Object (((CSG *) Object)->Children); POV_FREE (Object); } /***************************************************************************** * * FUNCTION * * Compute_CSG_BBox * * INPUT * * OUTPUT * * RETURNS * * AUTHOR * * POV-Ray Team * * DESCRIPTION * * - * * CHANGES * * Sep 1994 : Improved bounding of quadrics used in CSG intersections. [DB] * ******************************************************************************/ void Compute_CSG_BBox (Object) OBJECT *Object; { int i, count; DBL Old_Volume, New_Volume; VECTOR NewMin, NewMax, TmpMin, TmpMax, Min, Max; OBJECT *Sib; QUADRIC **Quadrics; if (Object->Methods == &CSG_Intersection_Methods) { /* * Calculate the bounding box of a CSG intersection * by intersecting the bounding boxes of all children. */ Make_Vector(NewMin, -BOUND_HUGE, -BOUND_HUGE, -BOUND_HUGE); Make_Vector(NewMax, BOUND_HUGE, BOUND_HUGE, BOUND_HUGE); count = 0; Quadrics = NULL; /* Process all children. */ for (Sib = ((CSG *)Object)->Children; Sib != NULL; Sib = Sib->Sibling) { /* Inverted objects and height fields mustn't be considered */ if (!Test_Flag(Sib, INVERTED_FLAG) && (Sib->Methods != &HField_Methods)) { /* Store quadrics since they'll be processed last. */ if (Sib->Methods == &Quadric_Methods) { Quadrics = (QUADRIC **)POV_REALLOC(Quadrics, (count+1)*sizeof(QUADRIC *), "temporary quadric list"); Quadrics[count++] = (QUADRIC *)Sib; } else { if (Sib->Methods == &Plane_Methods) { Compute_Plane_Min_Max((PLANE *)Sib, TmpMin, TmpMax); } else { Make_min_max_from_BBox(TmpMin, TmpMax, Sib->BBox); } NewMin[X] = max(NewMin[X], TmpMin[X]); NewMin[Y] = max(NewMin[Y], TmpMin[Y]); NewMin[Z] = max(NewMin[Z], TmpMin[Z]); NewMax[X] = min(NewMax[X], TmpMax[X]); NewMax[Y] = min(NewMax[Y], TmpMax[Y]); NewMax[Z] = min(NewMax[Z], TmpMax[Z]); } } } /* Process any quadrics. */ for (i = 0; i < count; i++) { Assign_Vector(Min, NewMin); Assign_Vector(Max, NewMax); Compute_Quadric_BBox(Quadrics[i], Min, Max); Make_min_max_from_BBox(TmpMin, TmpMax, Quadrics[i]->BBox); NewMin[X] = max(NewMin[X], TmpMin[X]); NewMin[Y] = max(NewMin[Y], TmpMin[Y]); NewMin[Z] = max(NewMin[Z], TmpMin[Z]); NewMax[X] = min(NewMax[X], TmpMax[X]); NewMax[Y] = min(NewMax[Y], TmpMax[Y]); NewMax[Z] = min(NewMax[Z], TmpMax[Z]); } if (Quadrics != NULL) { POV_FREE(Quadrics); } } else { /* Calculate the bounding box of a CSG merge/union object. */ Make_Vector(NewMin, BOUND_HUGE, BOUND_HUGE, BOUND_HUGE); Make_Vector(NewMax, -BOUND_HUGE, -BOUND_HUGE, -BOUND_HUGE); for (Sib = ((CSG *)Object)->Children; Sib != NULL; Sib = Sib->Sibling) { Make_min_max_from_BBox(TmpMin, TmpMax, Sib->BBox); NewMin[X] = min(NewMin[X], TmpMin[X]); NewMin[Y] = min(NewMin[Y], TmpMin[Y]); NewMin[Z] = min(NewMin[Z], TmpMin[Z]); NewMax[X] = max(NewMax[X], TmpMax[X]); NewMax[Y] = max(NewMax[Y], TmpMax[Y]); NewMax[Z] = max(NewMax[Z], TmpMax[Z]); } } if ((NewMin[X] > NewMax[X]) || (NewMin[Y] > NewMax[Y]) || (NewMin[Z] > NewMax[Z])) { Warning(0.0, "Degenerate CSG bounding box (not used!).\n"); } else { New_Volume = (NewMax[X] - NewMin[X]) * (NewMax[Y] - NewMin[Y]) * (NewMax[Z] - NewMin[Z]); BOUNDS_VOLUME(Old_Volume, Object->BBox); if (New_Volume < Old_Volume) { Make_BBox_from_min_max(Object->BBox, NewMin, NewMax); /* Beware of bounding boxes to large. */ if ((Object->BBox.Lengths[X] > CRITICAL_LENGTH) || (Object->BBox.Lengths[Y] > CRITICAL_LENGTH) || (Object->BBox.Lengths[Z] > CRITICAL_LENGTH)) { Make_BBox(Object->BBox, -BOUND_HUGE/2, -BOUND_HUGE/2, -BOUND_HUGE/2, BOUND_HUGE, BOUND_HUGE, BOUND_HUGE); } } } }