/************************************************************************
****
* blob.cpp
*
* This module implements functions that manipulate blobs.
*
* The original file was written by Alexander Enzmann.
* He wrote the code for blobs and generously provided us these
enhancements.
*
* Modifications and enhancements by Dieter Bayer [DB].
*
* from Persistence of Vision(tm) Ray Tracer
* Copyright 1996-2002 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 it may be found online at -
*
* http://www.povray.org/povlegal.html.
*
* 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.
*
* $File: //depot/povray/3.5/source/blob.cpp $
* $Revision: #12 $
* $Change: 1817 $
* $DateTime: 2002/07/27 10:45:37 $
* $Author: chrisc $
* $Log$
*
*************************************************************************
****/
/************************************************************************
****
*
* Explanation:
*
* -
*
* Syntax:
*
* blob
* {
* threshold THRESHOLD_VALUE
*
* component STRENGTH, RADIUS,
*
* sphere { , RADIUS, [strength] STRENGTH
* [ translate ]
* [ rotate ]
* [ scale ]
* [ finish { ... } ]
* [ pigment { ... } ]
* [ tnormal { ... } ]
* [ texture { ... } ]
* }
*
* cylinder { , , RADIUS, [strength] STRENGTH
* [ translate ]
* [ rotate ]
* [ scale ]
* [ finish { ... } ]
* [ pigment { ... } ]
* [ tnormal { ... } ]
* [ texture { ... } ]
* }
*
* [ sturm ]
* [ hierarchy FLAG ]
* }
*
* ---
*
* Jul 1994 : Most functions rewritten, bounding hierarchy added. [DB]
*
* Aug 1994 : Cylindrical blobs added. [DB]
*
* Sep 1994 : Multi-texturing added (each component can have its own
texture).
* Translation, rotation and scaling of each component added.
[DB]
*
* Oct 1994 : Adopted the method for the bounding slab creation to build
the
* bounding sphere hierarchy of the blob to get a much better
* hierarchy. Improved bounding sphere calculation for tighter
* bounds. [DB]
*
* Dec 1994 : Added code for dynamic blob queue allocation. [DB]
*
* Feb 1995 : Moved bounding sphere stuff into a seperate file. [DB]
*
*************************************************************************
****/
#include "frame.h"
#include "povray.h"
#include "vector.h"
#include "povproto.h"
#include "blob.h"
#include "bbox.h"
#include "bsphere.h"
#include "lighting.h"
#include "matrices.h"
#include "objects.h"
#include "polysolv.h"
#include "texture.h"
#include
using namespace std;
/************************************************************************
*****
* Local preprocessor defines
*************************************************************************
*****/
/* Minimal intersection depth for a valid intersection. */
#define DEPTH_TOLERANCE 1.0e-2
/* Tolerance for inside test. */
#define INSIDE_TOLERANCE 1.0e-6
/* Ray enters/exits a component. */
#define ENTERING 0
#define EXITING 1
/************************************************************************
*****
* Local typedefs
*************************************************************************
*****/
/************************************************************************
*****
* Static functions
*************************************************************************
*****/
static void element_normal (VECTOR Result, VECTOR P, BLOB_ELEMENT
*Element);
static int intersect_element (VECTOR P, VECTOR D, BLOB_ELEMENT *Element,
DBL mindist, DBL *t0, DBL *t1);
static void insert_hit (BLOB_ELEMENT *Element, DBL t0, DBL t1,
BLOB_INTERVAL *intervals, int *cnt);
static int determine_influences (VECTOR P, VECTOR D, BLOB *Blob, DBL
mindist, BLOB_INTERVAL *intervals);
static DBL calculate_field_value (BLOB *Blob, VECTOR P);
static DBL calculate_element_field (BLOB_ELEMENT *Element, VECTOR P);
static int intersect_cylinder (BLOB_ELEMENT *Element, VECTOR P, VECTOR
D, DBL mindist, DBL *tmin, DBL *tmax);
static int intersect_hemisphere (BLOB_ELEMENT *Element, VECTOR P, VECTOR
D, DBL mindist, DBL *tmin, DBL *tmax);
static int intersect_sphere (BLOB_ELEMENT *Element, VECTOR P, VECTOR D,
DBL mindist, DBL *tmin, DBL *tmax);
static int intersect_ellipsoid (BLOB_ELEMENT *Element, VECTOR P, VECTOR
D, DBL mindist, DBL *tmin, DBL *tmax);
static void get_element_bounding_sphere (BLOB_ELEMENT *Element, VECTOR
Center, DBL *Radius2);
static void build_bounding_hierarchy (BLOB *Blob);
static void init_blob_element (BLOB_ELEMENT *Element);
static void determine_element_texture (BLOB *Blob,
BLOB_ELEMENT *Element, TEXTURE *Texture, VECTOR P, int *Count,
TEXTURE **Textures, DBL *Weights);
static void insert_node (BSPHERE_TREE *Node, int *size);
static int All_Blob_Intersections (OBJECT *Object, RAY *Ray, ISTACK
*Depth_Stack);
static int Inside_Blob (VECTOR point, OBJECT *Object);
static void Blob_Normal (VECTOR Result, OBJECT *Object, INTERSECTION
*Inter);
static BLOB *Copy_Blob (OBJECT *Object);
static void Translate_Blob (OBJECT *Object, VECTOR Vector, TRANSFORM
*Trans);
static void Rotate_Blob (OBJECT *Object, VECTOR Vector, TRANSFORM
*Trans);
static void Scale_Blob (OBJECT *Object, VECTOR Vector, TRANSFORM *Trans);
static void Invert_Blob (OBJECT *Object);
static void Transform_Blob (OBJECT *Object, TRANSFORM *Trans);
static void Destroy_Blob (OBJECT *Object);
static void Compute_Blob_BBox (BLOB *Blob);
/************************************************************************
*****
* Local variables
*************************************************************************
*****/
METHODS Blob_Methods =
{
All_Blob_Intersections,
Inside_Blob, Blob_Normal, Default_UVCoord,
(COPY_METHOD)Copy_Blob,
Translate_Blob, Rotate_Blob, Scale_Blob, Transform_Blob,
Invert_Blob, Destroy_Blob
};
static BSPHERE_TREE **Queue;
/* Maximum number of entries in queue during hierarchy traversal. */
static unsigned Max_Queue_Size = 1024;
/************************************************************************
*****
*
* FUNCTION
*
* All_Blob_Intersections
*
* INPUT
*
* Object - Object
* Ray - Ray
*
* OUTPUT
*
* Depth_Stack - Intersection stack
*
* RETURNS
*
* int - true, if a intersection was found
*
* AUTHOR
*
* Alexander Enzmann
*
* DESCRIPTION
*
* Generate intervals of influence for each component. After these
* are made, determine their aggregate effect on the ray. As the
* individual intervals are checked, a quartic is generated
* that represents the density at a particular point on the ray.
*
* Explanation for spherical components:
*
* After making the substitutions in MakeBlob, there is a formula
* for each component that has the form:
*
* c0 * r^4 + c1 * r^2 + c2.
*
* In order to determine the influence on the ray of all of the
* individual components, we start by determining the distance
* from any point on the ray to the specified point. This can
* be found using the pythagorean theorem, using C as the center
* of this component, P as the start of the ray, and D as the
* direction of travel of the ray:
*
* r^2 = (t * D + P - C) . (t * D + P - C)
*
* we insert this equation for each appearance of r^2 in the
* components' formula, giving:
*
* r^2 = D.D t^2 + 2 t D . (P - C) + (P - C) . (P - C)
*
* Since the direction vector has been normalized, D.D = 1.
* Using the substitutions:
*
* t0 = (P - C) . (P - C),
* t1 = D . (P - C)
*
* We can write the formula as:
*
* r^2 = t0 + 2 t t1 + t^2
*
* Taking r^2 and substituting into the formula for this component
* of the Blob we get the formula:
*
* density = c0 * (r^2)^2 + c1 * r^2 + c2,
*
* or:
*
* density = c0 * (t0 + 2 t t1 + t^2)^2 +
* c1 * (t0 + 2 t t1 + t^2) +
* c2
*
* Expanding terms and collecting with respect to "t" gives:
*
* t^4 * c0 +
* t^3 * 4 c0 t1 +
* t^2 * (c1 + 2 * c0 t0 + 4 c0 t1^2)
* t * 2 (c1 t1 + 2 c0 t0 t1) +
* c2 + c1*t0 + c0*t0^2
*
* This formula can now be solved for "t" by any of the quartic
* root solvers that are available.
*
* CHANGES
*
* Jul 1994 : Added code for cylindrical and ellipsoidical blobs. [DB]
*
* Oct 1994 : Added code to convert polynomial into a bezier curve for
* a quick test if an intersection exists in an interval.
[DB]
*
* Sep 1995 : Added code to avoid numerical problems with distant
blobs. [DB]
*
*************************************************************************
*****/
static int All_Blob_Intersections(OBJECT *Object, RAY *Ray, ISTACK
*Depth_Stack)
{
int i, j, cnt;
int root_count, in_flag;
int Intersection_Found = false;
DBL t0, t1, t2, c0, c1, c2, dist, len, start_dist;
DBL *fcoeffs;
DBL coeffs[5];
DBL roots[4];
VECTOR P, D, V1, PP, DD;
VECTOR IPoint;
BLOB_INTERVAL *intervals;
BLOB_ELEMENT *Element;
BLOB *Blob = (BLOB *)Object;
DBL newcoeffs[5], dk[5];
DBL l, w;
Increase_Counter(stats[Ray_Blob_Tests]);
/* Transform the ray into blob space. */
if (Blob->Trans != NULL)
{
MInvTransPoint(P, Ray->Initial, Blob->Trans);
MInvTransDirection(D, Ray->Direction, Blob->Trans);
VLength(len, D);
VInverseScaleEq(D, len);
}
else
{
Assign_Vector(P, Ray->Initial);
Assign_Vector(D, Ray->Direction);
len = 1.0;
}
/* Get the intervals along the ray where each component has an effect.
*/
intervals = Blob->Data->Intervals;
if ((cnt = determine_influences(P, D, Blob, DEPTH_TOLERANCE,
intervals)) == 0)
{
/* Ray doesn't hit any of the component elements. */
return (false);
}
/* To avoid numerical problems we start at the first interval. */
if ((start_dist = intervals[0].bound) < Small_Tolerance)
{
start_dist = 0.0;
}
DBL Max_Bound = intervals[0].bound;
for (i = 0; i < cnt; i++)
{
if (intervals[i].bound > Max_Bound)
Max_Bound = intervals[i].bound;
intervals[i].bound -= start_dist;
}
/* Get the new starting point. */
VAddScaledEq(P, start_dist, D);
if (Max_Bound != 0)
{
VScaleEq(D, Max_Bound);
for (i = 0; i < cnt; i++)
{
intervals[i].bound /= Max_Bound;
}
}
else
{
Max_Bound = 1;
}
/* Clear out the coefficients. */
coeffs[0] =
coeffs[1] =
coeffs[2] =
coeffs[3] = 0.0;
coeffs[4] = - Blob->Data->Threshold;
/*
* Step through the list of intersection points, adding the
* influence of each component as it appears.
*/
fcoeffs = NULL;
for (i = in_flag = 0; i < cnt; i++)
{
if ((intervals[i].type & 1) == ENTERING)
{
/*
* Something is just starting to influence the ray, so calculate
* its coefficients and add them into the pot.
*/
in_flag++;
Element = intervals[i].Element;
switch (Element->Type)
{
case BLOB_SPHERE:
VSub(V1, P, Element->O);
VDot(t0, V1, V1);
VDot(t1, V1, D);
t2 = Max_Bound * Max_Bound;
c0 = Element->c[0];
c1 = Element->c[1];
c2 = Element->c[2];
fcoeffs = &(Element->f[0]);
fcoeffs[0] = c0 * t2 * t2;
fcoeffs[1] = 4.0 * c0 * t1 * t2;
fcoeffs[2] = 2.0 * c0 * (2.0 * t1 * t1 + t0 * t2) + c1 * t2;
fcoeffs[3] = 2.0 * t1 * (2.0 * c0 * t0 + c1);
fcoeffs[4] = t0 * (c0 * t0 + c1) + c2;
break;
case BLOB_ELLIPSOID:
MInvTransPoint(PP, P, Element->Trans);
MInvTransDirection(DD, D, Element->Trans);
VSub(V1, PP, Element->O);
VDot(t0, V1, V1);
VDot(t1, V1, DD);
VDot(t2, DD, DD);
c0 = Element->c[0];
c1 = Element->c[1];
c2 = Element->c[2];
fcoeffs = &(Element->f[0]);
fcoeffs[0] = c0 * t2 * t2;
fcoeffs[1] = 4.0 * c0 * t1 * t2;
fcoeffs[2] = 2.0 * c0 * (2.0 * t1 * t1 + t0 * t2) + c1 * t2;
fcoeffs[3] = 2.0 * t1 * (2.0 * c0 * t0 + c1);
fcoeffs[4] = t0 * (c0 * t0 + c1) + c2;
break;
case BLOB_BASE_HEMISPHERE:
case BLOB_APEX_HEMISPHERE:
MInvTransPoint(PP, P, Element->Trans);
MInvTransDirection(DD, D, Element->Trans);
if (Element->Type == BLOB_APEX_HEMISPHERE)
{
PP[Z] -= Element->len;
}
VDot(t0, PP, PP);
VDot(t1, PP, DD);
VDot(t2, DD, DD);
c0 = Element->c[0];
c1 = Element->c[1];
c2 = Element->c[2];
fcoeffs = &(Element->f[0]);
fcoeffs[0] = c0 * t2 * t2;
fcoeffs[1] = 4.0 * c0 * t1 * t2;
fcoeffs[2] = 2.0 * c0 * (2.0 * t1 * t1 + t0 * t2) + c1 * t2;
fcoeffs[3] = 2.0 * t1 * (2.0 * c0 * t0 + c1);
fcoeffs[4] = t0 * (c0 * t0 + c1) + c2;
break;
case BLOB_CYLINDER:
/* Transform ray into cylinder space. */
MInvTransPoint(PP, P, Element->Trans);
MInvTransDirection(DD, D, Element->Trans);
t0 = PP[X] * PP[X] + PP[Y] * PP[Y];
t1 = PP[X] * DD[X] + PP[Y] * DD[Y];
t2 = DD[X] * DD[X] + DD[Y] * DD[Y];
c0 = Element->c[0];
c1 = Element->c[1];
c2 = Element->c[2];
fcoeffs = &(Element->f[0]);
fcoeffs[0] = c0 * t2 * t2;
fcoeffs[1] = 4.0 * c0 * t1 * t2;
fcoeffs[2] = 2.0 * c0 * (2.0 * t1 * t1 + t0 * t2) + c1 * t2;
fcoeffs[3] = 2.0 * t1 * (2.0 * c0 * t0 + c1);
fcoeffs[4] = t0 * (c0 * t0 + c1) + c2;
break;
default:
Error("Unknown blob component in All_Blob_Intersections().");
}
for (j = 0; j < 5; j++)
{
coeffs[j] += fcoeffs[j];
}
}
else
{
/*
* We are losing the influence of a component -->
* subtract off its coefficients.
*/
fcoeffs = intervals[i].Element->f;
for (j = 0; j < 5; j++)
{
coeffs[j] -= fcoeffs[j];
}
/* If no components are currently affecting the ray ---> skip
ahead. */
if (--in_flag == 0)
{
continue;
}
}
/*
* If the following intersection lies close to the current
intersection
* then first add/subtract next region before testing. [DB 7/94]
*/
if ((i + 1 < cnt) && (fabs(intervals[i].bound - intervals[i +
1].bound) < EPSILON))
{
continue;
}
/*
* Transform polynomial in a way that the interval boundaries are
moved
* to 0 and 1, i. e. the roots of interest are between 0 and 1. [DB
10/94]
*/
l = intervals[i].bound;
w = intervals[i+1].bound - l;
newcoeffs[0] = coeffs[0] * w * w * w * w;
newcoeffs[1] = (coeffs[1] + 4.0 * coeffs[0] * l) * w * w * w;
newcoeffs[2] = (3.0 * l * (2.0 * coeffs[0] * l + coeffs[1]) +
coeffs[2]) * w * w;
newcoeffs[3] = (2.0 * l * (2.0 * l * (coeffs[0] * l + 0.75 *
coeffs[1]) + coeffs[2]) + coeffs[3]) * w;
newcoeffs[4] = l * (l * (l * (coeffs[0] * l + coeffs[1]) +
coeffs[2]) + coeffs[3]) + coeffs[4];
/* Calculate coefficients of corresponding bezier curve. [DB 10/94]
*/
dk[0] = newcoeffs[4];
dk[1] = newcoeffs[4] + 0.25 * newcoeffs[3];
dk[2] = newcoeffs[4] + 0.50 * (newcoeffs[3] + newcoeffs[2] / 12.0);
dk[3] = newcoeffs[4] + 0.50 * (0.375 * newcoeffs[3] + newcoeffs[2]
+ 0.125 * newcoeffs[1]);
dk[4] = newcoeffs[4] + newcoeffs[3] + newcoeffs[2] + newcoeffs[1]
+ newcoeffs[0];
/*
* Skip this interval if the ray doesn't intersect the convex hull
of the
* bezier curve, because no valid intersection will be found. [DB
10/94]
*/
if (((dk[0] >= 0.0) && (dk[1] >= 0.0) && (dk[2] >= 0.0) &&
(dk[3] >= 0.0) && (dk[4] >= 0.0)) ||
((dk[0] <= 0.0) && (dk[1] <= 0.0) && (dk[2] <= 0.0) &&
(dk[3] <= 0.0) && (dk[4] <= 0.0)))
{
continue;
}
/*
* Now we could do bezier clipping to find the roots
* but I have no idea how this works. [DB 2/95]
*/
/* Solve polynomial. */
root_count = Solve_Polynomial(4, coeffs, roots, Test_Flag(Blob,
STURM_FLAG), 1.0e-11);
/* See if any of the roots are valid. */
for (j = 0; j < root_count; j++)
{
dist = roots[j];
/*
* First see if the root is in the interval of influence of
* the currently active components.
*/
if ((dist >= intervals[i].bound) &&
(dist <= intervals[i+1].bound))
{
/* Correct distance. */
dist = (dist * Max_Bound + start_dist) / len;
if ((dist > DEPTH_TOLERANCE) && (dist < Max_Distance))
{
VEvaluateRay(IPoint, Ray->Initial, dist, Ray->Direction);
if (Point_In_Clip(IPoint, Object->Clip))
{
push_entry(dist, IPoint, Object, Depth_Stack);
Intersection_Found = true;
}
}
}
}
/*
* If the blob isn't used inside a CSG and we have found at least
* one intersection then we are ready, because all possible
intersections
* will be further away (we have a sorted list!). [DB 7/94]
*/
if (!(Blob->Type & IS_CHILD_OBJECT) && (Intersection_Found))
{
break;
}
}
if (Intersection_Found)
{
Increase_Counter(stats[Ray_Blob_Tests_Succeeded]);
}
return (Intersection_Found);
}
/************************************************************************
*****
*
* FUNCTION
*
* insert_hit
*
* INPUT
*
* Blob - Pointer to blob structure
* Element - Element to insert
* t0, t1 - Intersection depths
*
* OUTPUT
*
* intervals - Pointer to sorted list of hits
* cnt - Number of hits in intervals
*
* RETURNS
*
* AUTHOR
*
* Alexander Enzmann
*
* DESCRIPTION
*
* Store the points of intersection. Keep track of: whether this is
* the start or end point of the hit, which component was pierced
* by the ray, and the point along the ray that the hit occured at.
*
* CHANGES
*
* Oct 1994 : Modified to use memmove instead of loops for copying. [DB]
* Sep 1995 : Changed to allow use of memcpy if memmove isn't
available. [AED]
* Jul 1996 : Changed to use POV_MEMMOVE, which can be memmove or
pov_memmove.
* Oct 1996 : Changed to avoid unnecessary compares. [DB]
*
*************************************************************************
*****/
static void insert_hit(BLOB_ELEMENT *Element, DBL t0, DBL t1,
BLOB_INTERVAL *intervals, int *cnt)
{
int k;
/* We are entering the component. */
intervals[*cnt].type = Element->Type | ENTERING;
intervals[*cnt].bound = t0;
intervals[*cnt].Element = Element;
for (k = 0; t0 > intervals[k].bound; k++);
if (k < *cnt)
{
/*
* This hit point is smaller than one that already exists -->
* bump the rest and insert it here.
*/
POV_MEMMOVE(&intervals[k+1], &intervals[k],
(*cnt-k)*sizeof(BLOB_INTERVAL));
/* We are entering the component. */
intervals[k].type = Element->Type | ENTERING;
intervals[k].bound = t0;
intervals[k].Element = Element;
(*cnt)++;
/* We are exiting the component. */
intervals[*cnt].type = Element->Type | EXITING;
intervals[*cnt].bound = t1;
intervals[*cnt].Element = Element;
for (k = k + 1; t1 > intervals[k].bound; k++);
if (k < *cnt)
{
POV_MEMMOVE(&intervals[k+1], &intervals[k],
(*cnt-k)*sizeof(BLOB_INTERVAL));
/* We are exiting the component. */
intervals[k].type = Element->Type | EXITING;
intervals[k].bound = t1;
intervals[k].Element = Element;
}
(*cnt)++;
}
else
{
/* Just plop the start and end points at the end of the list */
(*cnt)++;
/* We are exiting the component. */
intervals[*cnt].type = Element->Type | EXITING;
intervals[*cnt].bound = t1;
intervals[*cnt].Element = Element;
(*cnt)++;
}
}
/************************************************************************
*****
*
* FUNCTION
*
* intersect_cylinder
*
* INPUT
*
* Element - Pointer to element structure
* P, D - Ray = P + t * D
* mindist - Min. valid distance
*
* OUTPUT
*
* tmin, tmax - Intersection depths found
*
* RETURNS
*
* AUTHOR
*
* Dieter Bayer (with help from Alexander Enzmann)
*
* DESCRIPTION
*
* -
*
* CHANGES
*
* Jul 1994 : Creation.
*
*************************************************************************
*****/
static int intersect_cylinder(BLOB_ELEMENT *Element, VECTOR P, VECTOR
D, DBL mindist, DBL *tmin, DBL *tmax)
{
DBL a, b, c, d, t, u, v, w, len;
VECTOR PP, DD;
/* Transform ray into cylinder space. */
MInvTransPoint(PP, P, Element->Trans);
MInvTransDirection(DD, D, Element->Trans);
VLength(len, DD);
VInverseScaleEq(DD, len);
/* Intersect ray with cylinder. */
a = DD[X] * DD[X] + DD[Y] * DD[Y];
if (a > EPSILON)
{
b = PP[X] * DD[X] + PP[Y] * DD[Y];
c = PP[X] * PP[X] + PP[Y] * PP[Y] - Element->rad2;
d = b * b - a * c;
if (d > EPSILON)
{
d = sqrt(d);
t = ( - b + d) / a;
w = PP[Z] + t * DD[Z];
if ((w >= 0.0) && (w <= Element->len))
{
if (t < *tmin) { *tmin = t; }
if (t > *tmax) { *tmax = t; }
}
t = ( - b - d) / a;
w = PP[Z] + t * DD[Z];
if ((w >= 0.0) && (w <= Element->len))
{
if (t < *tmin) { *tmin = t; }
if (t > *tmax) { *tmax = t; }
}
}
}
/* Intersect base/cap plane. */
if (fabs(DD[Z]) > EPSILON)
{
/* Intersect base plane. */
t = - PP[Z] / DD[Z];
u = PP[X] + t * DD[X];
v = PP[Y] + t * DD[Y];
if ((u * u + v * v) <= Element->rad2)
{
if (t < *tmin) { *tmin = t; }
if (t > *tmax) { *tmax = t; }
}
/* Intersect cap plane. */
t = (Element->len - PP[Z]) / DD[Z];
u = PP[X] + t * DD[X];
v = PP[Y] + t * DD[Y];
if ((u * u + v * v) <= Element->rad2)
{
if (t < *tmin) { *tmin = t; }
if (t > *tmax) { *tmax = t; }
}
}
/* Check if the intersections are valid. */
*tmin /= len;
*tmax /= len;
if (*tmin < mindist) { *tmin = 0.0; }
if (*tmax < mindist) { *tmax = 0.0; }
if (*tmin >= *tmax)
{
return (false);
}
return (true);
}
/************************************************************************
*****
*
* FUNCTION
*
* intersect_ellipsoid
*
* INPUT
*
* Element - Pointer to element structure
* P, D - Ray = P + t * D
* mindist - Min. valid distance
*
* OUTPUT
*
* tmin, tmax - Intersection depths found
*
* RETURNS
*
* AUTHOR
*
* Dieter Bayer
*
* DESCRIPTION
*
* -
*
* CHANGES
*
* Sep 1994 : Creation.
*
*************************************************************************
*****/
static int intersect_ellipsoid(BLOB_ELEMENT *Element, VECTOR P, VECTOR
D, DBL mindist, DBL *tmin, DBL *tmax)
{
DBL b, d, t, len;
VECTOR V1, PP, DD;
MInvTransPoint(PP, P, Element->Trans);
MInvTransDirection(DD, D, Element->Trans);
VLength(len, DD);
VInverseScaleEq(DD, len);
VSub(V1, PP, Element->O);
VDot(b, V1, DD);
VDot(t, V1, V1);
d = b * b - t + Element->rad2;
if (d < EPSILON)
{
return (false);
}
d = sqrt(d);
*tmax = ( - b + d) / len; if (*tmax < mindist) { *tmax = 0.0; }
*tmin = ( - b - d) / len; if (*tmin < mindist) { *tmin = 0.0; }
if (*tmax == *tmin)
{
return (false);
}
else
{
if (*tmax < *tmin)
{
d = *tmin; *tmin = *tmax; *tmax = d;
}
}
return (true);
}
/************************************************************************
*****
*
* FUNCTION
*
* intersect_hemisphere
*
* INPUT
*
* Element - Pointer to element structure
* P, D - Ray = P + t * D
* mindist - Min. valid distance
*
* OUTPUT
*
* tmin, tmax - Intersection depths found
*
* RETURNS
*
* AUTHOR
*
* Dieter Bayer
*
* DESCRIPTION
*
* -
*
* CHANGES
*
* Jul 1994 : Creation (with help from Alexander Enzmann).
*
*************************************************************************
*****/
static int intersect_hemisphere(BLOB_ELEMENT *Element, VECTOR P, VECTOR
D, DBL mindist, DBL *tmin, DBL *tmax)
{
DBL b, d, t, z1, z2, len;
VECTOR PP, DD;
/* Transform ray into hemisphere space. */
MInvTransPoint(PP, P, Element->Trans);
MInvTransDirection(DD, D, Element->Trans);
VLength(len, DD);
VInverseScaleEq(DD, len);
if (Element->Type == BLOB_BASE_HEMISPHERE)
{
VDot(b, PP, DD);
VDot(t, PP, PP);
d = b * b - t + Element->rad2;
if (d < EPSILON)
{
return (false);
}
d = sqrt(d);
*tmax = - b + d;
*tmin = - b - d;
if (*tmax < *tmin)
{
d = *tmin; *tmin = *tmax; *tmax = d;
}
/* Cut intersection at the plane. */
z1 = PP[Z] + *tmin * DD[Z];
z2 = PP[Z] + *tmax * DD[Z];
/* If both points are inside --> no intersection */
if ((z1 >= 0.0) && (z2 >= 0.0))
{
return (false);
}
/* If both points are outside --> intersections found */
if ((z1 < 0.0) && (z2 < 0.0))
{
*tmin /= len;
*tmax /= len;
return (true);
}
/* Determine intersection with plane. */
t = - PP[Z] / DD[Z];
if (z1 >= 0.0)
{
/* Ray is crossing the plane from inside to outside. */
*tmin = (t < mindist) ? 0.0 : t;
}
else
{
/* Ray is crossing the plane from outside to inside. */
*tmax = (t < mindist) ? 0.0 : t;
}
*tmin /= len;
*tmax /= len;
return (true);
}
else
{
PP[Z] -= Element->len;
VDot(b, PP, DD);
VDot(t, PP, PP);
d = b * b - t + Element->rad2;
if (d < EPSILON)
{
return (false);
}
d = sqrt(d);
*tmax = - b + d;
*tmin = - b - d;
if (*tmax < *tmin)
{
d = *tmin; *tmin = *tmax; *tmax = d;
}
/* Cut intersection at the plane. */
z1 = PP[Z] + *tmin * DD[Z];
z2 = PP[Z] + *tmax * DD[Z];
/* If both points are inside --> no intersection */
if ((z1 <= 0.0) && (z2 <= 0.0))
{
return (false);
}
/* If both points are outside --> intersections found */
if ((z1 > 0.0) && (z2 > 0.0))
{
*tmin /= len;
*tmax /= len;
return (true);
}
/* Determine intersection with plane. */
t = - PP[Z] / DD[Z];
if (z1 <= 0.0)
{
/* Ray is crossing the plane from inside to outside. */
*tmin = (t < mindist) ? 0.0 : t;
}
else
{
/* Ray is crossing the plane from outside to inside. */
*tmax = (t < mindist) ? 0.0 : t;
}
*tmin /= len;
*tmax /= len;
return (true);
}
}
/************************************************************************
*****
*
* FUNCTION
*
* intersect_sphere
*
* INPUT
*
* Element - Pointer to element structure
* P, D - Ray = P + t * D
* mindist - Min. valid distance
*
* OUTPUT
*
* tmin, tmax - Intersection depths found
*
* RETURNS
*
* AUTHOR
*
* Dieter Bayer
*
* DESCRIPTION
*
* -
*
* CHANGES
*
* Jul 1994 : Creation (with help from Alexander Enzmann).
*
*************************************************************************
*****/
static int intersect_sphere(BLOB_ELEMENT *Element, VECTOR P, VECTOR D,
DBL mindist, DBL *tmin, DBL *tmax)
{
DBL b, d, t;
VECTOR V1;
VSub(V1, P, Element->O);
VDot(b, V1, D);
VDot(t, V1, V1);
d = b * b - t + Element->rad2;
if (d < EPSILON)
{
return (false);
}
d = sqrt(d);
*tmax = - b + d; if (*tmax < mindist) { *tmax = 0.0; }
*tmin = - b - d; if (*tmin < mindist) { *tmin = 0.0; }
if (*tmax == *tmin)
{
return (false);
}
else
{
if (*tmax < *tmin)
{
d = *tmin; *tmin = *tmax; *tmax = d;
}
}
return (true);
}
/************************************************************************
*****
*
* FUNCTION
*
* intersect_element
*
* INPUT
*
* P, D - Ray = P + t * D
* Element - Pointer to element structure
* mindist - Min. valid distance
*
* OUTPUT
*
* tmin, tmax - Intersection depths found
*
* RETURNS
*
* AUTHOR
*
* Dieter Bayer
*
* DESCRIPTION
*
* -
*
* CHANGES
*
* Jul 1994 : Creation.
*
*************************************************************************
*****/
static int intersect_element(VECTOR P, VECTOR D, BLOB_ELEMENT *Element,
DBL mindist, DBL *tmin, DBL *tmax)
{
#ifdef BLOB_EXTRA_STATS
Increase_Counter(stats[Blob_Element_Tests]);
#endif
*tmin = BOUND_HUGE;
*tmax = - BOUND_HUGE;
switch (Element->Type)
{
case BLOB_SPHERE:
if (!intersect_sphere(Element, P, D, mindist, tmin, tmax))
{
return (false);
}
break;
case BLOB_ELLIPSOID:
if (!intersect_ellipsoid(Element, P, D, mindist, tmin, tmax))
{
return (false);
}
break;
case BLOB_BASE_HEMISPHERE:
case BLOB_APEX_HEMISPHERE:
if (!intersect_hemisphere(Element, P, D, mindist, tmin, tmax))
{
return (false);
}
break;
case BLOB_CYLINDER:
if (!intersect_cylinder(Element, P, D, mindist, tmin, tmax))
{
return (false);
}
break;
}
#ifdef BLOB_EXTRA_STATS
Increase_Counter(stats[Blob_Element_Tests_Succeeded]);
#endif
return (true);
}
/************************************************************************
*****
*
* FUNCTION
*
* determine_influences
*
* INPUT
*
* P, D - Ray = P + t * D
* Blob - Pointer to blob structure
* mindist - Min. valid distance
*
* OUTPUT
*
* intervals - Sorted list of intersections found
*
* RETURNS
*
* int - Number of intersection found
*
* AUTHOR
*
* Dieter Bayer
*
* DESCRIPTION
*
* Make a sorted list of points along the ray at which the various blob
* components start and stop adding their influence.
*
* CHANGES
*
* Jul 1994 : Added code for bounding hierarchy traversal. [DB]
*
*************************************************************************
*****/
static int determine_influences(VECTOR P, VECTOR D, BLOB *Blob, DBL
mindist, BLOB_INTERVAL *intervals)
{
int i;
int cnt, size;
DBL b, t, t0, t1;
VECTOR V1;
BSPHERE_TREE *Tree;
cnt = 0;
if (Blob->Data->Tree == NULL)
{
/* There's no bounding hierarchy so just step through all elements.
*/
for (i = 0; i < Blob->Data->Number_Of_Components; i++)
{
if (intersect_element(P, D, &Blob->Data->Entry[i], mindist, &t0,
&t1))
{
insert_hit(&Blob->Data->Entry[i], t0, t1, intervals, &cnt);
}
}
}
else
{
/* Use blob's bounding hierarchy. */
size = 0;
Queue[size++] = Blob->Data->Tree;
while (size > 0)
{
Tree = Queue[--size];
/* Test if current node is a leaf. */
if (Tree->Entries <= 0)
{
/* Test element. */
if (intersect_element(P, D, (BLOB_ELEMENT *)Tree->Node, mindist,
&t0, &t1))
{
insert_hit((BLOB_ELEMENT *)Tree->Node, t0, t1, intervals,
&cnt);
}
}
else
{
/* Test all sub-nodes. */
for (i = 0; i < (int)Tree->Entries; i++)
{
#ifdef BLOB_EXTRA_STATS
Increase_Counter(stats[Blob_Bound_Tests]);
#endif
VSub(V1, Tree->Node[i]->C, P);
VDot(b, V1, D);
VDot(t, V1, V1);
if ((t - Sqr(b)) <= Tree->Node[i]->r2)
{
#ifdef BLOB_EXTRA_STATS
Increase_Counter(stats[Blob_Bound_Tests_Succeeded]);
#endif
insert_node(Tree->Node[i], &size);
}
}
}
}
}
return (cnt);
}
/************************************************************************
*****
*
* FUNCTION
*
* calculate_element_field
*
* INPUT
*
* Element - Pointer to element structure
* P - Point whos field value is calculated
*
* OUTPUT
*
* RETURNS
*
* DBL - Field value
*
* AUTHOR
*
* Alexander Enzmann
*
* DESCRIPTION
*
* Calculate the field value of a single element in a given point P
* (which must already have been transformed into blob space).
*
* CHANGES
*
* Jul 1994 : Added code for cylindrical and ellipsoidical blobs. [DB]
*
*************************************************************************
*****/
static DBL calculate_element_field(BLOB_ELEMENT *Element, VECTOR P)
{
DBL rad2, density;
VECTOR V1, PP;
density = 0.0;
switch (Element->Type)
{
case BLOB_SPHERE:
VSub(V1, P, Element->O);
VDot(rad2, V1, V1);
if (rad2 < Element->rad2)
{
density = rad2 * (rad2 * Element->c[0] + Element->c[1]) +
Element->c[2];
}
break;
case BLOB_ELLIPSOID:
MInvTransPoint(PP, P, Element->Trans);
VSub(V1, PP, Element->O);
VDot(rad2, V1, V1);
if (rad2 < Element->rad2)
{
density = rad2 * (rad2 * Element->c[0] + Element->c[1]) +
Element->c[2];
}
break;
case BLOB_BASE_HEMISPHERE:
MInvTransPoint(PP, P, Element->Trans);
if (PP[Z] <= 0.0)
{
VDot(rad2, PP, PP);
if (rad2 <= Element->rad2)
{
density = rad2 * (rad2 * Element->c[0] + Element->c[1]) +
Element->c[2];
}
}
break;
case BLOB_APEX_HEMISPHERE:
MInvTransPoint(PP, P, Element->Trans);
PP[Z] -= Element->len;
if (PP[Z] >= 0.0)
{
VDot(rad2, PP, PP);
if (rad2 <= Element->rad2)
{
density = rad2 * (rad2 * Element->c[0] + Element->c[1]) +
Element->c[2];
}
}
break;
case BLOB_CYLINDER:
MInvTransPoint(PP, P, Element->Trans);
if ((PP[Z] >= 0.0) && (PP[Z] <= Element->len))
{
if ((rad2 = Sqr(PP[X]) + Sqr(PP[Y])) <= Element->rad2)
{
density = rad2 * (rad2 * Element->c[0] + Element->c[1]) +
Element->c[2];
}
}
break;
}
return (density);
}
/************************************************************************
*****
*
* FUNCTION
*
* calculate_field_value
*
* INPUT
*
* Blob - Pointer to blob structure
* P - Point whos field value is calculated
*
* OUTPUT
*
* RETURNS
*
* DBL - Field value
*
* AUTHOR
*
* Dieter Bayer
*
* DESCRIPTION
*
* Calculate the field value of a blob in a given point P
* (which must already have been transformed into blob space).
*
* CHANGES
*
* Jul 1994 : Added code for bounding hierarchy traversal. [DB]
*
*************************************************************************
*****/
static DBL calculate_field_value(BLOB *Blob, VECTOR P)
{
int i;
int size;
DBL density, rad2;
VECTOR V1;
BSPHERE_TREE *Tree;
density = 0.0;
if (Blob->Data->Tree == NULL)
{
/* There's no tree --> step through all elements. */
for (i = 0; i < Blob->Data->Number_Of_Components; i++)
{
density += calculate_element_field(&Blob->Data->Entry[i], P);
}
}
else
{
/* A tree exists --> step through the tree. */
size = 0;
Queue[size++] = Blob->Data->Tree;
while (size > 0)
{
Tree = Queue[--size];
/* Test if current node is a leaf. */
if (Tree->Entries <= 0)
{
density += calculate_element_field((BLOB_ELEMENT *)Tree->Node,
P);
}
else
{
/* Test all sub-nodes. */
for (i = 0; i < (int)Tree->Entries; i++)
{
/* Insert sub-node if we are inside. */
VSub(V1, P, Tree->Node[i]->C);
VDot(rad2, V1, V1);
if (rad2 <= Tree->Node[i]->r2)
{
insert_node(Tree->Node[i], &size);
}
}
}
}
}
return (density);
}
/************************************************************************
*****
*
* FUNCTION
*
* Inside_Blob
*
* INPUT
*
* Test_Point - Point to test
* Object - Pointer to blob structure
*
* OUTPUT
*
* RETURNS
*
* int - true if Test_Point is inside
*
* AUTHOR
*
* Alexander Enzmann
*
* DESCRIPTION
*
* Calculate the density at the given point and then compare to
* the threshold to see if we are in or out of the blob.
*
* CHANGES
*
* -
*
*************************************************************************
*****/
static int Inside_Blob(VECTOR Test_Point, OBJECT *Object)
{
VECTOR New_Point;
BLOB *Blob = (BLOB *) Object;
/* Transform the point into blob space. */
if (Blob->Trans != NULL)
{
MInvTransPoint(New_Point, Test_Point, Blob->Trans);
}
else
{
Assign_Vector(New_Point, Test_Point);
}
if (calculate_field_value(Blob, New_Point) > Blob->Data->Threshold -
INSIDE_TOLERANCE)
{
/* We are inside. */
return (!Test_Flag(Blob, INVERTED_FLAG));
}
else
{
/* We are outside. */
return (Test_Flag(Blob, INVERTED_FLAG));
}
}
/************************************************************************
*****
*
* FUNCTION
*
* element_normal
*
* INPUT
*
* P - Surface point
* Element - Pointer to element structure
*
* OUTPUT
*
* Result - Element's normal
*
* RETURNS
*
* AUTHOR
*
* Dieter Bayer
*
* DESCRIPTION
*
* Calculate the normal of a single element in the point P.
*
* CHANGES
*
* Jul 1994 : Creation (with help from Alexander Enzmann).
*
*************************************************************************
*****/
static void element_normal(VECTOR Result, VECTOR P, BLOB_ELEMENT
*Element)
{
DBL val, dist;
VECTOR V1, PP;
switch (Element->Type)
{
case BLOB_SPHERE:
VSub(V1, P, Element->O);
VDot(dist, V1, V1);
if (dist <= Element->rad2)
{
val = -2.0 * Element->c[0] * dist - Element->c[1];
VAddScaledEq(Result, val, V1);
}
break;
case BLOB_ELLIPSOID:
MInvTransPoint(PP, P, Element->Trans);
VSub(V1, PP, Element->O);
VDot(dist, V1, V1);
if (dist <= Element->rad2)
{
val = -2.0 * Element->c[0] * dist - Element->c[1];
MTransNormal(V1, V1, Element->Trans);
VAddScaledEq(Result, val, V1);
}
break;
case BLOB_BASE_HEMISPHERE:
MInvTransPoint(PP, P, Element->Trans);
if (PP[Z] <= 0.0)
{
VDot(dist, PP, PP);
if (dist <= Element->rad2)
{
val = -2.0 * Element->c[0] * dist - Element->c[1];
MTransNormal(PP, PP, Element->Trans);
VAddScaledEq(Result, val, PP);
}
}
break;
case BLOB_APEX_HEMISPHERE:
MInvTransPoint(PP, P, Element->Trans);
PP[Z] -= Element->len;
if (PP[Z] >= 0.0)
{
VDot(dist, PP, PP);
if (dist <= Element->rad2)
{
val = -2.0 * Element->c[0] * dist - Element->c[1];
MTransNormal(PP, PP, Element->Trans);
VAddScaledEq(Result, val, PP);
}
}
break;
case BLOB_CYLINDER:
MInvTransPoint(PP, P, Element->Trans);
if ((PP[Z] >= 0.0) && (PP[Z] <= Element->len))
{
if ((dist = Sqr(PP[X]) + Sqr(PP[Y])) <= Element->rad2)
{
val = -2.0 * Element->c[0] * dist - Element->c[1];
PP[Z] = 0.0;
MTransNormal(PP, PP, Element->Trans);
VAddScaledEq(Result, val, PP);
}
}
break;
}
}
/************************************************************************
*****
*
* FUNCTION
*
* Blob_Normal
*
* INPUT
*
* Object - Pointer to blob structure
* Inter - Pointer to intersection
*
* OUTPUT
*
* Result - Blob's normal
*
* RETURNS
*
* AUTHOR
*
* Alexander Enzmann
*
* DESCRIPTION
*
* Calculate the blob's surface normal in the intersection point.
*
* CHANGES
*
* Jul 1994 : Added code for bounding hierarchy traversal. [DB]
*
*************************************************************************
*****/
static void Blob_Normal(VECTOR Result, OBJECT *Object, INTERSECTION
*Inter)
{
int i;
int size;
DBL dist, val;
VECTOR New_Point, V1;
BLOB *Blob = (BLOB *) Object;
BSPHERE_TREE *Tree;
/* Transform the point into the blob space. */
if (Blob->Trans != NULL)
{
MInvTransPoint(New_Point, Inter->IPoint, Blob->Trans);
}
else
{
Assign_Vector(New_Point, Inter->IPoint);
}
Make_Vector(Result, 0.0, 0.0, 0.0);
/* For each component that contributes to this point, add its bit to
the normal */
if (Blob->Data->Tree == NULL)
{
/* There's no tree --> step through all elements. */
for (i = 0; i < Blob->Data->Number_Of_Components; i++)
{
element_normal(Result, New_Point, &(Blob->Data->Entry[i]));
}
}
else
{
/* A tree exists --> step through the tree. */
size = 0;
Queue[size++] = Blob->Data->Tree;
while (size > 0)
{
Tree = Queue[--size];
/* Test if current node is a leaf. */
if (Tree->Entries <= 0)
{
element_normal(Result, New_Point, (BLOB_ELEMENT *)Tree->Node);
}
else
{
/* Test all sub-nodes. */
for (i = 0; i < (int)Tree->Entries; i++)
{
/* Insert sub-node if we are inside. */
VSub(V1, New_Point, Tree->Node[i]->C);
VDot(dist, V1, V1);
if (dist <= Tree->Node[i]->r2)
{
insert_node(Tree->Node[i], &size);
}
}
}
}
}
VDot(val, Result, Result);
if (val == 0.0)
{
Make_Vector(Result, 1.0, 0.0, 0.0);
}
else
{
/* Normalize normal vector. */
val = 1.0 / sqrt(val);
VScaleEq(Result, val);
}
/* Transform back to world space. */
if (Blob->Trans != NULL)
{
MTransNormal(Result, Result, Blob->Trans);
VNormalize(Result, Result);
}
}
/************************************************************************
*****
*
* FUNCTION
*
* Translate_Blob
*
* INPUT
*
* Vector - Translation vector
*
* OUTPUT
*
* Object - Pointer to blob structure
*
* RETURNS
*
* AUTHOR
*
* Alexander Enzmann
*
* DESCRIPTION
*
* Translate a blob.
*
* CHANGES
*
* -
*
*************************************************************************
*****/
static void Translate_Blob(OBJECT *Object, VECTOR, TRANSFORM *Trans)
{
Transform_Blob(Object, Trans);
}
/************************************************************************
*****
*
* FUNCTION
*
* Rotate_Blob
*
* INPUT
*
* Vector - Rotation vector
*
* OUTPUT
*
* Object - Pointer to blob structure
*
* RETURNS
*
* AUTHOR
*
* Alexander Enzmann
*
* DESCRIPTION
*
* Rotate a blob.
*
* CHANGES
*
* -
*
*************************************************************************
*****/
static void Rotate_Blob(OBJECT *Object, VECTOR, TRANSFORM *Trans)
{
Transform_Blob(Object, Trans);
}
/************************************************************************
*****
*
* FUNCTION
*
* Scale_Blob
*
* INPUT
*
* Vector - Scaling vector
*
* OUTPUT
*
* Object - Pointer to blob structure
*
* RETURNS
*
* AUTHOR
*
* Alexander Enzmann
*
* DESCRIPTION
*
* Scale a blob.
*
* CHANGES
*
* -
*
*************************************************************************
*****/
static void Scale_Blob(OBJECT *Object, VECTOR, TRANSFORM *Trans)
{
Transform_Blob(Object, Trans);
}
/************************************************************************
*****
*
* FUNCTION
*
* Transform_Blob
*
* INPUT
*
* Trans - Pointer to transformation
*
* OUTPUT
*
* Object - Pointer to blob structure
*
* RETURNS
*
* AUTHOR
*
* Alexander Enzmann
*
* DESCRIPTION
*
* Transform a blob.
*
* CHANGES
*
* -
*
*************************************************************************
*****/
static void Transform_Blob(OBJECT *Object, TRANSFORM *Trans)
{
int i;
BLOB *Blob = (BLOB *)Object;
if (Blob->Trans == NULL)
{
Blob->Trans = Create_Transform();
}
Recompute_BBox(&Object->BBox, Trans);
Compose_Transforms(Blob->Trans, Trans);
for (i = 0; i < Blob->Data->Number_Of_Components; i++)
{
Transform_Textures(Blob->Element_Texture[i], Trans);
}
}
/************************************************************************
*****
*
* FUNCTION
*
* Invert_Blob
*
* INPUT
*
* Object - Pointer to blob structure
*
* OUTPUT
*
* Object
*
* RETURNS
*
* AUTHOR
*
* Alexander Enzmann
*
* DESCRIPTION
*
* Invert a blob.
*
* CHANGES
*
* -
*
*************************************************************************
*****/
static void Invert_Blob(OBJECT *Object)
{
Invert_Flag(Object, INVERTED_FLAG);
}
/************************************************************************
*****
*
* FUNCTION
*
* Create_Blob
*
* INPUT
*
* Object - Pointer to blob structure
*
* OUTPUT
*
* Object
*
* RETURNS
*
* AUTHOR
*
* Alexander Enzmann
*
* DESCRIPTION
*
* Create a new blob.
*
* CHANGES
*
* -
*
*************************************************************************
*****/
BLOB *Create_Blob()
{
BLOB *New;
New = (BLOB *)POV_MALLOC(sizeof (BLOB), "blob");
INIT_OBJECT_FIELDS(New, BLOB_OBJECT, &Blob_Methods)
Set_Flag(New, HIERARCHY_FLAG);
New->Trans = NULL;
New->Data = NULL;
New->Element_Texture = NULL;
return (New);
}
/************************************************************************
*****
*
* FUNCTION
*
* Copy_Blob
*
* INPUT
*
* Object - Pointer to blob structure
*
* OUTPUT
*
* Object
*
* RETURNS
*
* AUTHOR
*
* Alexander Enzmann
*
* DESCRIPTION
*
* Copy a blob.
*
* NOTE: The components are not copied, only the number of references is
* counted, so that Destroy_Blob() knows if they can be destroyed.
*
* CHANGES
*
* Jul 1994 : Added code for blob data reference counting. [DB]
*
*************************************************************************
*****/
static BLOB *Copy_Blob(OBJECT *Object)
{
int i;
BLOB *New, *Old = (BLOB *)Object;
New = Create_Blob();
/* Copy blob. */
*New = *Old;
New->Trans = Copy_Transform(New->Trans);
New->Data->References++;
New->Element_Texture = (TEXTURE
**)POV_MALLOC(New->Data->Number_Of_Components*sizeof(TEXTURE *), "blob
texture list");
for (i = 0; i < New->Data->Number_Of_Components; i++)
{
New->Element_Texture[i] = Copy_Textures(Old->Element_Texture[i]);
}
return (New);
}
/************************************************************************
*****
*
* FUNCTION
*
* Create_Blob_List_Element
*
* INPUT
*
* OUTPUT
*
* RETURNS
*
* BLOB_LIST * - Pointer to blob element
*
* AUTHOR
*
* Dieter Bayer
*
* DESCRIPTION
*
* Create a new blob element in the component list used during parsing.
*
* CHANGES
*
* Sep 1994 : Creation.
*
*************************************************************************
*****/
BLOB_LIST *Create_Blob_List_Element()
{
BLOB_LIST *New;
New = (BLOB_LIST *)POV_MALLOC(sizeof(BLOB_LIST), "blob component");
init_blob_element(&New->elem);
return (New);
}
/************************************************************************
*****
*
* FUNCTION
*
* Destroy_Blob
*
* INPUT
*
* Object - Pointer to blob structure
*
* OUTPUT
*
* Object
*
* RETURNS
*
* AUTHOR
*
* Alexander Enzmann
*
* DESCRIPTION
*
* Destroy a blob.
*
* NOTE: The blob data is destroyed if they are no longer used by any
copy.
*
* CHANGES
*
* Jul 1994 : Added code for blob data reference counting. [DB]
*
* Dec 1994 : Fixed memory leakage. [DB]
*
* Aug 1995 : Fixed freeing of already freed memory. [DB]
*
*************************************************************************
*****/
static void Destroy_Blob(OBJECT *Object)
{
int i;
BLOB *Blob = (BLOB *)Object;
Destroy_Transform(Blob->Trans);
for (i = 0; i < Blob->Data->Number_Of_Components; i++)
{
Destroy_Textures(Blob->Element_Texture[i]);
}
POV_FREE(Blob->Element_Texture);
if (--(Blob->Data->References) == 0)
{
Destroy_Bounding_Sphere_Hierarchy(Blob->Data->Tree);
for (i = 0; i < Blob->Data->Number_Of_Components; i++)
{
/*
* Make sure to destroy multiple references of a texture
* and/or transformation only once. Multiple references
* are only used with cylindrical blobs. Thus it's
* enough to ignore all cylinder caps.
*/
if ((Blob->Data->Entry[i].Type == BLOB_SPHERE) ||
(Blob->Data->Entry[i].Type == BLOB_ELLIPSOID) ||
(Blob->Data->Entry[i].Type == BLOB_CYLINDER))
{
Destroy_Transform(Blob->Data->Entry[i].Trans);
Blob->Data->Entry[i].Trans = NULL;
Blob->Data->Entry[i].Texture = NULL;
}
}
POV_FREE(Blob->Data->Entry);
POV_FREE(Blob->Data->Intervals);
POV_FREE(Blob->Data);
}
POV_FREE(Object);
}
/************************************************************************
*****
*
* FUNCTION
*
* Compute_Blob_BBox
*
* INPUT
*
* Blob - Blob
*
* OUTPUT
*
* Blob
*
* RETURNS
*
* AUTHOR
*
* Dieter Bayer
*
* DESCRIPTION
*
* Calculate the bounding box of a blob.
*
* CHANGES
*
* Aug 1994 : Creation.
*
*************************************************************************
*****/
static void Compute_Blob_BBox(BLOB *Blob)
{
int i;
DBL radius, radius2;
VECTOR Center, Min, Max;
Make_Vector(Min, BOUND_HUGE, BOUND_HUGE, BOUND_HUGE);
Make_Vector(Max, - BOUND_HUGE, - BOUND_HUGE, - BOUND_HUGE);
for (i = 0; i < Blob->Data->Number_Of_Components; i++)
{
if (Blob->Data->Entry[i].c[2] > 0.0)
{
get_element_bounding_sphere(&Blob->Data->Entry[i], Center,
&radius2);
radius = sqrt(radius2);
Min[X] = min(Min[X], Center[X] - radius);
Min[Y] = min(Min[Y], Center[Y] - radius);
Min[Z] = min(Min[Z], Center[Z] - radius);
Max[X] = max(Max[X], Center[X] + radius);
Max[Y] = max(Max[Y], Center[Y] + radius);
Max[Z] = max(Max[Z], Center[Z] + radius);
}
}
Make_BBox_from_min_max(Blob->BBox, Min, Max);
if (Blob->Trans != NULL)
{
Recompute_BBox(&Blob->BBox, Blob->Trans);
}
}
/************************************************************************
*****
*
* FUNCTION
*
* get_element_bounding_sphere
*
* INPUT
*
* Element - Pointer to element
* Center - Bounding sphere's center
* Radius2 - Bounding sphere's squared radius
*
* OUTPUT
*
* Center, Radius2
*
* RETURNS
*
* AUTHOR
*
* Dieter Bayer
*
* DESCRIPTION
*
* Calculate the bounding sphere of a blob element.
*
* CHANGES
*
* Sep 1994 : Creation.
*
*************************************************************************
*****/
static void get_element_bounding_sphere(BLOB_ELEMENT *Element, VECTOR
Center, DBL *Radius2)
{
DBL r, r2 = 0.0;
VECTOR C;
BBOX local_BBox;
switch (Element->Type)
{
case BLOB_SPHERE:
case BLOB_ELLIPSOID:
r2 = Element->rad2;
Assign_Vector(C, Element->O);
break;
case BLOB_BASE_HEMISPHERE:
r2 = Element->rad2;
Make_Vector(C, 0.0, 0.0, 0.0);
break;
case BLOB_APEX_HEMISPHERE:
r2 = Element->rad2;
Make_Vector(C, 0.0, 0.0, Element->len);
break;
case BLOB_CYLINDER :
Make_Vector(C, 0.0, 0.0, 0.5 * Element->len);
r2 = Element->rad2 + Sqr(0.5 * Element->len);
break;
}
/* Transform bounding sphere if necessary. */
if (Element->Trans != NULL)
{
r = sqrt(r2);
MTransPoint(C, C, Element->Trans);
Make_BBox(local_BBox, 0, 0, 0, r, r, r);
Recompute_BBox(&local_BBox, Element->Trans);
r = max(max(fabs(local_BBox.Lengths[X]),
fabs(local_BBox.Lengths[Y])),
fabs(local_BBox.Lengths[Z]));
r2 = Sqr(r) + EPSILON;
}
Assign_Vector(Center, C);
*Radius2 = r2;
}
/************************************************************************
*****
*
* FUNCTION
*
* init_blob_element
*
* INPUT
*
* Element - Pointer to blob element
*
* OUTPUT
*
* Element
*
* RETURNS
*
* AUTHOR
*
* Dieter Bayer
*
* DESCRIPTION
*
* Init blob element.
*
* CHANGES
*
* Sep 1994 : Creation.
*
*************************************************************************
*****/
static void init_blob_element(BLOB_ELEMENT *Element)
{
Element->Type = 0;
Element->index = 0;
Element->len =
Element->rad2 = 0.0;
Element->c[0] =
Element->c[1] =
Element->c[2] =
Element->f[0] =
Element->f[1] =
Element->f[2] =
Element->f[3] =
Element->f[4] = 0.0;
Make_Vector(Element->O, 0.0, 0.0, 0.0);
Element->Texture = NULL;
Element->Trans = NULL;
}
/************************************************************************
*****
*
* FUNCTION
*
* Make_Blob
*
* INPUT
*
* Blob - Pointer to blob structure
* threshold - Blob's threshold
* BlobList - Pointer to elements
* npoints - Number of elements
*
* OUTPUT
*
* Blob
*
* RETURNS
*
* AUTHOR
*
* Alexander Enzmann
*
* DESCRIPTION
*
* Create a blob after it was read from the scene file.
*
* Starting with the density function: (1-r^2)^2, we have a field
* that varies in strength from 1 at r = 0 to 0 at r = 1. By
* substituting r/rad for r, we can adjust the range of influence
* of a particular component. By multiplication by coeff, we can
* adjust the amount of total contribution, giving the formula:
*
* coeff * (1 - (r/rad)^2)^2
*
* This varies in strength from coeff at r = 0, to 0 at r = rad.
*
* CHANGES
*
* Jul 1994 : Added code for cylindrical and ellipsoidical blobs. [DB]
*
*************************************************************************
*****/
void Make_Blob(BLOB *Blob, DBL threshold, BLOB_LIST *BlobList, int
npoints)
{
int i, count;
DBL rad2, coeff;
BLOB_LIST *temp;
BLOB_ELEMENT *Entry;
if (npoints < 1)
{
Error("Need at least one component in a blob.");
}
/* Figure out how many components there will be. */
temp = BlobList;
for (i = count = 0; i < npoints; i++)
{
if (temp->elem.Type & BLOB_CYLINDER)
{
count += 3;
}
else
{
count++;
}
temp = temp->next;
/* Test for too many components. [DB 12/94] */
if (count >= MAX_BLOB_COMPONENTS)
{
Error("There are more than %d components in a blob.",
MAX_BLOB_COMPONENTS);
}
}
/* Initialize the blob data. */
Blob->Data->Threshold = threshold;
Entry = Blob->Data->Entry;
for (i = 0; i < npoints; i++)
{
temp = BlobList;
if ((fabs(temp->elem.c[2]) < EPSILON) || (temp->elem.rad2 < EPSILON))
{
Warning(0, "Degenerate Blob element");
}
/* Initialize component. */
*Entry = temp->elem;
/* We have a multi-texture blob. */
if (Entry->Texture != NULL)
{
Set_Flag(Blob, MULTITEXTURE_FLAG);
}
/* Store blob specific information. */
switch (temp->elem.Type)
{
case BLOB_ELLIPSOID :
case BLOB_SPHERE :
rad2 = temp->elem.rad2;
coeff = temp->elem.c[2];
Entry->c[0] = coeff / (rad2 * rad2);
Entry->c[1] = -(2.0 * coeff) / rad2;
Entry->c[2] = coeff;
Entry++;
break;
case BLOB_CYLINDER :
rad2 = temp->elem.rad2;
coeff = temp->elem.c[2];
/* Create cylindrical component. */
Entry->c[0] = coeff / (rad2 * rad2);
Entry->c[1] = -(2.0 * coeff) / rad2;
Entry->c[2] = coeff;
Entry++;
/* Create hemispherical component at the base. */
*Entry = temp->elem;
Entry->Type = BLOB_BASE_HEMISPHERE;
Entry->c[0] = coeff / (rad2 * rad2);
Entry->c[1] = -(2.0 * coeff) / rad2;
Entry->c[2] = coeff;
Entry++;
/* Create hemispherical component at the apex. */
*Entry = temp->elem;
Entry->Type = BLOB_APEX_HEMISPHERE;
Entry->c[0] = coeff / (rad2 * rad2);
Entry->c[1] = -(2.0 * coeff) / rad2;
Entry->c[2] = coeff;
Entry++;
break;
default :
Error("Unknown blob component.");
}
/* Get rid of texture non longer needed. */
BlobList = BlobList->next;
Destroy_Textures(temp->elem.Texture);
POV_FREE(temp);
}
for (i = 0; i < count; i++)
{
Blob->Data->Entry[i].index = i;
}
/* Compute bounding box. */
Compute_Blob_BBox(Blob);
/* Create bounding sphere hierarchy. */
if (Test_Flag(Blob, HIERARCHY_FLAG))
{
build_bounding_hierarchy(Blob);
}
}
/************************************************************************
*****
*
* FUNCTION
*
* Test_Blob_Opacity
*
* INPUT
*
* Blob - Pointer to blob structure
*
* OUTPUT
*
* Blob
*
* RETURNS
*
* AUTHOR
*
* Dieter Bayer
*
* DESCRIPTION
*
* Set the opacity flag of the blob according to the opacity
* of the blob's texture(s).
*
* CHANGES
*
* Apr 1996 : Creation.
*
*************************************************************************
*****/
void Test_Blob_Opacity(BLOB *Blob)
{
int i;
/* Initialize opacity flag to the opacity of the object's texture. */
if ((Blob->Texture == NULL) || (Test_Opacity(Blob->Texture)))
{
Set_Flag(Blob, OPAQUE_FLAG);
}
if (Test_Flag(Blob, MULTITEXTURE_FLAG))
{
for (i = 0; i < Blob->Data->Number_Of_Components; i++)
{
if (Blob->Element_Texture[i] != NULL)
{
/* If component's texture isn't opaque the blob is neither. */
if (!Test_Opacity(Blob->Element_Texture[i]))
{
Clear_Flag(Blob, OPAQUE_FLAG);
}
}
}
}
}
/************************************************************************
*****
*
* FUNCTION
*
* build_bounding_hierarchy
*
* INPUT
*
* OUTPUT
*
* RETURNS
*
* AUTHOR
*
* Dieter Bayer
*
* DESCRIPTION
*
* Create the bounding sphere hierarchy.
*
* CHANGES
*
* Oct 1994 : Creation. (Derived from the bounding slab creation code)
*
*************************************************************************
*****/
static void build_bounding_hierarchy(BLOB *Blob)
{
int i, nElem, maxelements;
BSPHERE_TREE **Elements;
nElem = (int)Blob->Data->Number_Of_Components;
maxelements = 2 * nElem;
/*
* Now allocate an array to hold references to these elements.
*/
Elements = (BSPHERE_TREE
**)POV_MALLOC(maxelements*sizeof(BSPHERE_TREE *), "blob bounding
hierarchy");
/* Init list with blob elements. */
for (i = 0; i < nElem; i++)
{
Elements[i] = (BSPHERE_TREE *)POV_MALLOC(sizeof(BSPHERE_TREE),
"blob bounding hierarchy");
Elements[i]->Entries = 0;
Elements[i]->Node = (BSPHERE_TREE **)&Blob->Data->Entry[i];
get_element_bounding_sphere(&Blob->Data->Entry[i], Elements[i]->C,
&Elements[i]->r2);
}
Build_Bounding_Sphere_Hierarchy(&Blob->Data->Tree, nElem, Elements);
/* Get rid of the Elements array. */
POV_FREE(Elements);
}
/************************************************************************
*****
*
* FUNCTION
*
* Determine_Blob_Textures
*
* INPUT
*
* OUTPUT
*
* RETURNS
*
* AUTHOR
*
* Dieter Bayer
*
* DESCRIPTION
*
* Determine the textures and weights of all components affecting
* the given intersection point. The weights are calculated from
* the field values and sum to 1.
*
* CHANGES
*
* Sep 1994 : Creation.
*
* Mar 1996 : Make the call to resize the textures/weights list just
once
* at the beginning instead of doing it for every element.
[DB]
*
*************************************************************************
*****/
void Determine_Blob_Textures(BLOB *Blob, VECTOR IPoint, int *Count,
TEXTURE **Textures, DBL *Weights)
{
int i;
int size;
DBL rad2, sum;
VECTOR V1, P;
BLOB_ELEMENT *Element;
BSPHERE_TREE *Tree;
/* Make sure we have enough room in the textures/weights list. */
Reinitialize_Lighting_Code(Blob->Data->Number_Of_Components,
&Textures, &Weights);
/* Transform the point into the blob space. */
if (Blob->Trans != NULL)
{
MInvTransPoint(P, IPoint, Blob->Trans);
}
else
{
Assign_Vector(P, IPoint);
}
*Count = 0;
if (Blob->Data->Tree == NULL)
{
/* There's no tree --> step through all elements. */
for (i = 0; i < Blob->Data->Number_Of_Components; i++)
{
Element = &Blob->Data->Entry[i];
determine_element_texture(Blob, Element, Blob->Element_Texture[i],
P, Count, Textures, Weights);
}
}
else
{
/* A tree exists --> step through the tree. */
size = 0;
Queue[size++] = Blob->Data->Tree;
while (size > 0)
{
Tree = Queue[--size];
/* Test if current node is a leaf. */
if (Tree->Entries <= 0)
{
determine_element_texture(Blob, (BLOB_ELEMENT *)Tree->Node,
Blob->Element_Texture[((BLOB_ELEMENT *)Tree->Node)->index], P, Count,
Textures, Weights);
}
else
{
/* Test all sub-nodes. */
for (i = 0; i < (int)Tree->Entries; i++)
{
/* Insert sub-node if we are inside. */
VSub(V1, P, Tree->Node[i]->C);
VDot(rad2, V1, V1);
if (rad2 <= Tree->Node[i]->r2)
{
insert_node(Tree->Node[i], &size);
}
}
}
}
}
/* Normalize weights so that their sum is 1. */
if (*Count > 0)
{
sum = 0.0;
for (i = 0; i < *Count; i++)
{
sum += Weights[i];
}
if (sum > 0.0)
{
for (i = 0; i < *Count; i++)
{
Weights[i] /= sum;
}
}
}
}
/************************************************************************
*****
*
* FUNCTION
*
* determine_element_texture
*
* INPUT
*
* OUTPUT
*
* RETURNS
*
* AUTHOR
*
* Dieter Bayer
*
* DESCRIPTION
*
* If the intersection point is inside the component calculate
* the field density and store the element's texture and the field
* value in the texture/weight list.
*
* CHANGES
*
* Sep 1994 : Creation.
*
*************************************************************************
*****/
static void determine_element_texture(BLOB *Blob, BLOB_ELEMENT *Element,
TEXTURE *Texture, VECTOR P, int *Count, TEXTURE **Textures, DBL
*Weights)
{
int i;
DBL density;
density = fabs(calculate_element_field(Element, P));
if (density > 0.0)
{
if (Texture == NULL)
{
Textures[*Count] = Blob->Texture;
}
else
{
Textures[*Count] = Texture;
}
/* Test if this texture is already used. */
for (i = 0; i < *Count; i++)
{
if (Textures[i] == Textures[*Count])
{
/* Add current weight to already existing texture weight. */
Weights[i] += density;
/* Any texture can only be in the list once --> exit. */
return;
}
}
Weights[(*Count)++] = density;
}
}
/************************************************************************
*****
*
* FUNCTION
*
* Translate_Blob_Element
*
* INPUT
*
* Element - Pointer to blob element
* Vector - Translation vector
*
* OUTPUT
*
* Object
*
* RETURNS
*
* AUTHOR
*
* Dieter Bayer
*
* DESCRIPTION
*
* Translate a blob element.
*
* CHANGES
*
* Sep 1994 : Creation.
*
*************************************************************************
*****/
void Translate_Blob_Element(BLOB_ELEMENT *Element, VECTOR Vector)
{
TRANSFORM Trans;
Compute_Translation_Transform(&Trans, Vector);
if (Element->Trans == NULL)
{
/* This is a sphere component. */
VAddEq(Element->O, Vector);
Transform_Textures(Element->Texture, &Trans);
}
else
{
/* This is one of the other components. */
Transform_Blob_Element(Element, &Trans);
}
}
/************************************************************************
*****
*
* FUNCTION
*
* Rotate_Blob_Element
*
* INPUT
*
* Element - Pointer to blob element
* Vector - Translation vector
*
* OUTPUT
*
* Object
*
* RETURNS
*
* AUTHOR
*
* Dieter Bayer
*
* DESCRIPTION
*
* Rotate a blob element.
*
* CHANGES
*
* Sep 1994 : Creation.
*
*************************************************************************
*****/
void Rotate_Blob_Element(BLOB_ELEMENT *Element, VECTOR Vector)
{
TRANSFORM Trans;
Compute_Rotation_Transform(&Trans, Vector);
if (Element->Trans == NULL)
{
/* This is a sphere component. */
MTransPoint(Element->O, Element->O, &Trans);
Transform_Textures(Element->Texture, &Trans);
}
else
{
/* This is one of the other components. */
Transform_Blob_Element(Element, &Trans);
}
}
/************************************************************************
*****
*
* FUNCTION
*
* Scale_Blob_Element
*
* INPUT
*
* Element - Pointer to blob element
* Vector - Translation vector
*
* OUTPUT
*
* Object
*
* RETURNS
*
* AUTHOR
*
* Dieter Bayer
*
* DESCRIPTION
*
* Scale a blob element.
*
* CHANGES
*
* Sep 1994 : Creation.
*
*************************************************************************
*****/
void Scale_Blob_Element(BLOB_ELEMENT *Element, VECTOR Vector)
{
TRANSFORM Trans;
if ((Vector[X] != Vector[Y]) || (Vector[X] != Vector[Z]))
{
if (Element->Trans == NULL)
{
/* This is a sphere component --> change to ellipsoid component. */
Element->Type = BLOB_ELLIPSOID;
Element->Trans = Create_Transform();
}
}
Compute_Scaling_Transform(&Trans, Vector);
if (Element->Trans == NULL)
{
/* This is a sphere component. */
VScaleEq(Element->O, Vector[X]);
Element->rad2 *= Sqr(Vector[X]);
Transform_Textures(Element->Texture, &Trans);
}
else
{
/* This is one of the other components. */
Transform_Blob_Element(Element, &Trans);
}
}
/************************************************************************
*****
*
* FUNCTION
*
* Transform_Blob_Element
*
* INPUT
*
* Element - Pointer to blob element
* Trans - Transformation
*
* OUTPUT
*
* Object
*
* RETURNS
*
* AUTHOR
*
* Dieter Bayer
*
* DESCRIPTION
*
* Transform a blob element.
*
* CHANGES
*
* Sep 1994 : Creation.
*
*************************************************************************
*****/
void Transform_Blob_Element(BLOB_ELEMENT *Element, TRANSFORM *Trans)
{
if (Element->Trans == NULL)
{
/* This is a sphere component --> change to ellipsoid component. */
Element->Type = BLOB_ELLIPSOID;
Element->Trans = Create_Transform();
}
Compose_Transforms(Element->Trans, Trans);
Transform_Textures(Element->Texture, Trans);
}
/************************************************************************
*****
*
* FUNCTION
*
* Invert_Blob_Element
*
* INPUT
*
* Element - Pointer to blob element
*
* OUTPUT
*
* Object
*
* RETURNS
*
* AUTHOR
*
* Dieter Bayer
*
* DESCRIPTION
*
* Invert blob element by negating its strength.
*
* CHANGES
*
* Sep 1994 : Creation.
*
*************************************************************************
*****/
void Invert_Blob_Element(BLOB_ELEMENT *Element)
{
Element->c[2] *= -1.0;
}
/************************************************************************
*****
*
* FUNCTION
*
* Init_Blob_Queue
*
* INPUT
*
* OUTPUT
*
* RETURNS
*
* AUTHOR
*
* Dieter Bayer
*
* DESCRIPTION
*
* Init queues for blob intersections.
*
* CHANGES
*
* Dec 1994 : Creation.
*
*************************************************************************
*****/
void Init_Blob_Queue()
{
Queue = (BSPHERE_TREE
**)POV_MALLOC(Max_Queue_Size*sizeof(BSPHERE_TREE *), "blob queue");
}
/************************************************************************
*****
*
* FUNCTION
*
* Destroy_Blob_Queue
*
* INPUT
*
* OUTPUT
*
* RETURNS
*
* AUTHOR
*
* Dieter Bayer
*
* DESCRIPTION
*
* Destroy queues for blob intersections.
*
* CHANGES
*
* Dec 1994 : Creation.
*
*************************************************************************
*****/
void Destroy_Blob_Queue()
{
if (Queue != NULL)
{
POV_FREE(Queue);
}
Queue = NULL;
}
/************************************************************************
*****
*
* FUNCTION
*
* insert_node
*
* INPUT
*
* OUTPUT
*
* RETURNS
*
* AUTHOR
*
* Dieter Bayer
*
* DESCRIPTION
*
* Insert a node into the node queue.
*
* CHANGES
*
* Feb 1995 : Creation.
*
*************************************************************************
*****/
static void insert_node(BSPHERE_TREE *Node, int *size)
{
/* Resize queue if necessary. */
if (*size >= (int)Max_Queue_Size)
{
if (Max_Queue_Size >= INT_MAX/2)
{
Error("Blob queue overflow!");
}
Max_Queue_Size *= 2;
Queue = (BSPHERE_TREE **)POV_REALLOC(Queue,
Max_Queue_Size*sizeof(BSPHERE_TREE *), "blob queue");
}
Queue[(*size)++] = Node;
}
/************************************************************************
*****
*
* FUNCTION
*
* Create_Blob_Element_Texture_List
*
* INPUT
*
* OUTPUT
*
* RETURNS
*
* AUTHOR
*
* Dieter Bayer
*
* DESCRIPTION
*
* Create a list of all textures in the blob.
*
* The list actually contains copies of the textures not
* just references to them.
*
* CHANGES
*
* Mar 1996 : Created.
*
*************************************************************************
*****/
void Create_Blob_Element_Texture_List(BLOB *Blob, BLOB_LIST *BlobList,
int npoints)
{
int i, element_count, count;
BLOB_LIST *temp;
if (npoints < 1)
{
Error("Need at least one component in a blob.");
}
/* Figure out how many components there will be. */
temp = BlobList;
for (i = count = 0; i < npoints; i++)
{
if (temp->elem.Type & BLOB_CYLINDER)
{
count += 3;
}
else
{
count++;
}
temp = temp->next;
/* Test for too many components. [DB 12/94] */
if (count >= MAX_BLOB_COMPONENTS)
{
Error("There are more than %d components in a blob.",
MAX_BLOB_COMPONENTS);
}
}
/* Allocate memory for components. */
Blob->Data = (BLOB_DATA *)POV_MALLOC(sizeof(BLOB_DATA), "blob data");
Blob->Data->References = 1;
Blob->Data->Number_Of_Components = count;
Blob->Data->Entry = (BLOB_ELEMENT
*)POV_MALLOC(count*sizeof(BLOB_ELEMENT), "blob data");
Blob->Data->Intervals = (BLOB_INTERVAL
*)POV_MALLOC(2*Blob->Data->Number_Of_Components*sizeof(BLOB_INTERVAL),
"blob intervals");
Blob->Data->Tree = NULL;
/* Init components. */
for (i = 0; i < count; i++)
{
init_blob_element(&Blob->Data->Entry[i]);
}
/* Allocate memory for list. */
Blob->Element_Texture = (TEXTURE **)POV_MALLOC(count*sizeof(TEXTURE
*), "blob texture list");
for (i = 0; i < count; i++)
{
Blob->Element_Texture[i] = NULL;
}
for (i = element_count = 0; i < npoints; i++)
{
temp = BlobList;
/* Copy textures. */
switch (temp->elem.Type)
{
case BLOB_ELLIPSOID :
case BLOB_SPHERE :
/*
* Copy texture into element texture list. This is neccessary
* because individual textures have to be transformed too if
* copies of the blob are transformed.
*/
Blob->Element_Texture[element_count++] =
Copy_Textures(temp->elem.Texture);
break;
case BLOB_CYLINDER :
/*
* Copy texture into element texture list. This is neccessary
* because individual textures have to be transformed too if
* copies of the blob are transformed.
*/
Blob->Element_Texture[element_count++] =
Copy_Textures(temp->elem.Texture);
Blob->Element_Texture[element_count++] =
Copy_Textures(temp->elem.Texture);
Blob->Element_Texture[element_count++] =
Copy_Textures(temp->elem.Texture);
break;
}
BlobList = BlobList->next;
}
}