/****************************************************************************
* atmosph.c
*
* This module contains all functions for atmospheric effects.
*
* 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 "vector.h"
#include "povproto.h"
#include "atmosph.h"
#include "colour.h"
#include "povray.h"
#include "texture.h"
#include "pigment.h"
#include "objects.h"
#include "lighting.h"
#include "matrices.h"
#include "texture.h"
/*****************************************************************************
* Local preprocessor defines
******************************************************************************/
#define BLACK_LEVEL 0.0001
/*****************************************************************************
* Local typedefs
******************************************************************************/
typedef struct Light_List_Struct LIGHT_LIST;
struct Light_List_Struct
{
int active;
DBL t1, t2;
LIGHT_SOURCE *Light;
};
/*****************************************************************************
* Local variables
******************************************************************************/
/*
* Atmosphere stuff.
*/
/* List of active lights. */
static LIGHT_LIST *Light_List;
/* Min. and max. intersetcion depths with light source areas. */
static DBL *tmin, *tmax;
/*****************************************************************************
* Static functions
******************************************************************************/
static DBL constant_fog PARAMS((RAY *Ray, DBL Depth, DBL Width, FOG *Fog, COLOUR Colour));
static DBL ground_fog PARAMS((RAY *Ray, DBL Depth, DBL Width, FOG *Fog, COLOUR Colour));
static void supersample_atmosphere PARAMS((LIGHT_LIST *Light_List,
int level, RAY *Ray, ATMOSPHERE *Atmosphere,
DBL d1, COLOUR C1, DBL d3, COLOUR C3));
static void sample_atmosphere PARAMS((LIGHT_LIST *Light_List, DBL dist,
RAY *Ray, ATMOSPHERE *Atmosphere, COLOUR Col));
static int intersect_spotlight PARAMS((RAY *Ray, LIGHT_SOURCE *Light, DBL *d1, DBL *d2));
static int intersect_cylinderlight PARAMS((RAY *Ray, LIGHT_SOURCE *Light, DBL *d1, DBL *d2));
static void do_atmospheric_scattering PARAMS((RAY *Ray, INTERSECTION *Intersection, COLOUR Colour, int Light_Ray_Flag));
static void do_fog PARAMS((RAY *Ray, INTERSECTION *Intersection, COLOUR Colour, int Light_Ray_Flag));
static void do_rainbow PARAMS((RAY *Ray, INTERSECTION *Intersection, COLOUR Colour));
static void do_skysphere PARAMS((RAY *Ray, COLOUR Colour));
/*****************************************************************************
*
* FUNCTION
*
* Initialize_Atmosphere_Code
*
* INPUT
*
* OUTPUT
*
* RETURNS
*
* AUTHOR
*
* Dieter Bayer
*
* DESCRIPTION
*
* Initialize atmosphere specific variables.
*
* CHANGES
*
* Aug 1995 : Creation.
*
******************************************************************************/
void Initialize_Atmosphere_Code()
{
Light_List = NULL;
tmin = NULL;
tmax = NULL;
/* Allocate memory for atmosphere. */
if ((Frame.Atmosphere != NULL) &&
(fabs(Frame.Atmosphere->Distance) > EPSILON) &&
(Frame.Number_Of_Light_Sources > 0))
{
Light_List = (LIGHT_LIST *)POV_MALLOC(Frame.Number_Of_Light_Sources * sizeof(LIGHT_LIST), "atmosphere sampling lists");
tmin = (DBL *)POV_MALLOC(Frame.Number_Of_Light_Sources * sizeof(DBL), "atmosphere sampling lists");
tmax = (DBL *)POV_MALLOC(Frame.Number_Of_Light_Sources * sizeof(DBL), "atmosphere sampling lists");
}
}
/*****************************************************************************
*
* FUNCTION
*
* Deinitialize_Atmosphere_Code
*
* INPUT
*
* OUTPUT
*
* RETURNS
*
* AUTHOR
*
* Dieter Bayer
*
* DESCRIPTION
*
* Deinitialize atmosphere specific variables.
*
* CHANGES
*
* Aug 1995 : Creation.
*
******************************************************************************/
void Deinitialize_Atmosphere_Code()
{
/* Free memory used by atmosphere. */
if (Light_List != NULL)
{
POV_FREE(Light_List);
POV_FREE(tmin);
POV_FREE(tmax);
}
Light_List = NULL;
tmin = NULL;
tmax = NULL;
}
/*****************************************************************************
*
* FUNCTION
*
* Do_Infinite_Atmosphere
*
* INPUT
*
* Ray - Current ray
*
* OUTPUT
*
* Colour - Color of the current ray
*
* RETURNS
*
* AUTHOR
*
* Dieter Bayer
*
* DESCRIPTION
*
* Apply atmospheric effects to an infinite ray.
*
* CHANGES
*
* Feb 1995 : Creation.
*
* Jun 1995 : Added code for alpha channel support. [DB]
*
******************************************************************************/
void Do_Infinite_Atmosphere(Ray, Colour)
RAY *Ray;
COLOUR Colour;
{
/* Set background color. */
Assign_Colour(Colour, Frame.Background_Colour);
Colour[FILTER] = 0.0;
Colour[TRANSM] = 1.0;
/* Determine atmospheric effects for infinite ray. */
do_skysphere(Ray, Colour);
}
/*****************************************************************************
*
* FUNCTION
*
* Do_Finite_Atmosphere
*
* INPUT
*
* Ray - Current ray
* Intersection - Current intersection
* Light_Ray_Flag - TRUE if ray is a light source ray
*
* OUTPUT
*
* Colour - Color of the current ray
*
* RETURNS
*
* AUTHOR
*
* Dieter Bayer
*
* DESCRIPTION
*
* Apply atmospheric effects to a finite ray.
*
* CHANGES
*
* Feb 1995 : Creation.
*
******************************************************************************/
void Do_Finite_Atmosphere(Ray, Intersection, Colour, Light_Ray_Flag)
RAY *Ray;
INTERSECTION *Intersection;
COLOUR Colour;
int Light_Ray_Flag;
{
if (!Light_Ray_Flag)
{
do_rainbow(Ray, Intersection, Colour);
}
do_atmospheric_scattering(Ray, Intersection, Colour, Light_Ray_Flag);
do_fog(Ray, Intersection, Colour, Light_Ray_Flag);
}
/*****************************************************************************
*
* FUNCTION
*
* do_atmospheric_scattering
*
* INPUT
*
* Ray - Current ray
* Intersection - Current intersection
* Light_Ray_Flag - TRUE if ray is a light source ray
*
* OUTPUT
*
* Colour - Color of the current ray
*
* RETURNS
*
* AUTHOR
*
* Dieter Bayer
*
* DESCRIPTION
*
* Simulate atmospheric scattering using volume sampling.
*
* Ideas for the atmospheric scattering were taken from:
*
* - M. Inakage, "An Illumination Model for Atmospheric Environments", ..
*
* - Nishita, T., Miyawaki, Y. and Nakamae, E., "A Shading Model for
* Atmospheric Scattering Considering Luminous Intensity Distribution
* of Light Sources", Computer Graphics, 21, 4, 1987, 303-310
*
* CHANGES
*
* Nov 1994 : Creation.
*
* Jan 1995 : Added support of cylindrical light sources. [DB]
*
* Jun 1995 : Added code for alpha channel support. [DB]
*
******************************************************************************/
static void do_atmospheric_scattering(Ray, Intersection, Colour, Light_Ray_Flag)
RAY *Ray;
INTERSECTION *Intersection;
COLOUR Colour;
int Light_Ray_Flag;
{
int i, j;
int active, interval, intervals, insert;
DBL k, jdist, t1, t2, f, fi;
DBL dist, dist_prev;
DBL step;
COLOUR Col_total, Col, Col_prev, Max_Colour, C1, C2;
LIGHT_SOURCE *Light;
ATMOSPHERE *Atmosphere;
/* Why are we here? */
if (((Atmosphere = Frame.Atmosphere) == NULL) ||
(Frame.Atmosphere->Distance == 0.0) ||
(Frame.Number_Of_Light_Sources == 0))
{
return;
}
/*
* If we have a light source ray we only have to attenuate
* the light source color due to the distance traveled.
*/
if (Light_Ray_Flag)
{
k = exp(-Intersection->Depth / Atmosphere->Distance);
/* Check for minimum transmittance. */
if (k < Atmosphere->Colour[TRANSM])
{
k = Atmosphere->Colour[TRANSM];
}
VScaleEq(Colour, k);
return;
}
/* Init light list and sampling intervals. */
for (i = 0; i < Frame.Number_Of_Light_Sources; i++)
{
Light_List[i].active = FALSE;
Light_List[i].t1 = 0.0;
Light_List[i].t2 = Max_Distance;
Light_List[i].Light = NULL;
tmin[i] = BOUND_HUGE;
tmax[i] = -BOUND_HUGE;
}
/* Get depths for all light sources and disconnected sampling intervals. */
active = 0;
intervals = 0;
t1 = t2 = 0.0;
Make_Colour(Max_Colour, 0.0, 0.0, 0.0);
for (i = 0, Light = Frame.Light_Sources; Light != NULL; Light = Light->Next_Light_Source, i++)
{
insert = FALSE;
Light_List[i].Light = Light;
if (!Light->Atmosphere_Interaction)
{
continue;
}
switch (Light->Light_Type)
{
case CYLINDER_SOURCE:
if (intersect_cylinderlight(Ray, Light, &t1, &t2))
{
if ((t1 < Intersection->Depth) && (t2 > Small_Tolerance))
{
insert = TRUE;
}
}
break;
case POINT_SOURCE:
t1 = 0.0;
t2 = Intersection->Depth;
insert = TRUE;
break;
case SPOT_SOURCE:
if (intersect_spotlight(Ray, Light, &t1, &t2))
{
if ((t1 < Intersection->Depth) && (t2 > Small_Tolerance))
{
insert = TRUE;
}
}
break;
}
/* Insert distances into sampling interval list. */
if (insert)
{
/* Add light color to maximum color. */
VAddEq(Max_Colour, Light->Colour);
/* Number of active light sources. */
active ++;
/* Insert light source intersections into light list. */
t1 = max(t1, 0.0);
t2 = min(t2, Intersection->Depth);
Light_List[i].active = TRUE;
Light_List[i].t1 = t1;
Light_List[i].t2 = t2;
/* Test if there's an overlapping interval. */
for (j = 0; j < intervals; j++)
{
if (!((t2 < tmin[j]) || (t1 > tmax[j])))
{
/* The intervals are overlapping. */
break;
}
}
if (j >= intervals)
{
j = intervals;
intervals++;
}
/* Adjust interval. */
tmin[j] = min(tmin[j], t1);
tmax[j] = max(tmax[j], t2);
}
}
/* Initialize maximum color. */
Max_Colour[RED] = (Max_Colour[RED] > EPSILON) ? 1.0 : 0.0;
Max_Colour[GREEN] = (Max_Colour[GREEN] > EPSILON) ? 1.0 : 0.0;
Max_Colour[BLUE] = (Max_Colour[BLUE] > EPSILON) ? 1.0 : 0.0;
/*
* If there are no active lights, we can skip ahead because no
* light from any light source will reach the viewer.
*/
Make_Colour(Col_total, 0.0, 0.0, 0.0);
if (active > 0)
{
for (interval = 0; interval < intervals; interval++)
{
/* Get sampling step distance. */
step = min((tmax[interval] - tmin[interval]), Atmosphere->Distance) / (DBL)Atmosphere->Samples;
/* Set up distance. */
dist = dist_prev = 0.0;
/* Sample along the ray. */
Make_Colour(Col, 0.0, 0.0, 0.0);
for (i = 0; dist < tmax[interval]; i++)
{
/* Step to next sample point. */
dist = tmin[interval] + ((DBL)i + 0.5) * step;
/* Get distance of current sampling point. */
jdist = dist + Atmosphere->Jitter * step * (FRAND() - 0.5);
/* If we're behind the intersection point we can quit. */
if (jdist >= Intersection->Depth)
{
break;
}
sample_atmosphere(Light_List, jdist, Ray, Atmosphere, Col);
/* Add previous result to the total color. */
if (i)
{
/* Do the current and previous colours differ too much? */
if ((Atmosphere->AA_Level > 0) && (Colour_Distance(Col, Col_prev) >= Atmosphere->AA_Threshold))
{
/* Supersample between current and previous point. */
supersample_atmosphere(Light_List, 1, Ray, Atmosphere, dist_prev, Col_prev, dist, Col);
Col[RED] = Col_prev[RED] = 0.5 * (Col[RED] + Col_prev[RED]);
Col[GREEN] = Col_prev[GREEN] = 0.5 * (Col[GREEN] + Col_prev[GREEN]);
Col[BLUE] = Col_prev[BLUE] = 0.5 * (Col[BLUE] + Col_prev[BLUE]);
}
/* Get attenuation due to distance from view point. */
k = step * exp(-dist_prev / Atmosphere->Distance);
/* If the contribution is too small we can stop. */
if (k < BLACK_LEVEL)
{
break;
}
Col_total[RED] += k * Col_prev[RED];
Col_total[GREEN] += k * Col_prev[GREEN];
Col_total[BLUE] += k * Col_prev[BLUE];
/* If the total color is larger than max. value we can stop now. */
if ((Col_total[RED] >= Max_Colour[RED]) &&
(Col_total[GREEN] >= Max_Colour[GREEN]) &&
(Col_total[BLUE] >= Max_Colour[BLUE]))
{
break;
}
}
dist_prev = dist;
Assign_Colour(Col_prev, Col);
}
/* Add last result to the total color. */
/* Get attenuation due to distance from view point. */
k = step * exp(-dist / Atmosphere->Distance);
Col_total[RED] += k * Col_prev[RED];
Col_total[GREEN] += k * Col_prev[GREEN];
Col_total[BLUE] += k * Col_prev[BLUE];
/* If the total color is white already we can stop now. */
if ((Col_total[RED] >= 1.0) && (Col_total[GREEN] >= 1.0) && (Col_total[BLUE] >= 1.0))
{
break;
}
}
}
/* Add attenuated background color. */
k = exp(-Intersection->Depth / Atmosphere->Distance);
/* Check for minimum transmittance. */
if (k < Atmosphere->Colour[TRANSM])
{
k = Atmosphere->Colour[TRANSM];
}
/*
ki = 1.0 - k;
VLinComb3(Colour, 1.0, Col_total, k, Colour, ki, Atmosphere->Colour);
*/
/* Attenuate color due to atmosphere color. */
f = Atmosphere->Colour[FILTER];
fi = 1.0 - f;
C1[RED] = Col_total[RED] * (fi + f * Atmosphere->Colour[RED]);
C1[GREEN] = Col_total[GREEN] * (fi + f * Atmosphere->Colour[GREEN]);
C1[BLUE] = Col_total[BLUE] * (fi + f * Atmosphere->Colour[BLUE]);
C2[RED] = Colour[RED] * (fi + f * Atmosphere->Colour[RED]);
C2[GREEN] = Colour[GREEN] * (fi + f * Atmosphere->Colour[GREEN]);
C2[BLUE] = Colour[BLUE] * (fi + f * Atmosphere->Colour[BLUE]);
VLinComb2(Colour, 1.0, C1, k, C2);
Colour[FILTER] = k * Colour[FILTER];
Colour[TRANSM] = k * Colour[TRANSM];
}
/*****************************************************************************
*
* FUNCTION
*
* supersample_atmosphere
*
* INPUT
*
* Light_List - array containing light source information
* level - level of recursion
* Ray - pointer to ray
* Atmosphere - pointer to atmosphere to use
* d1 - distance to lower sample
* d3 - distance to upper sample
*
* OUTPUT
*
* C1 - Color of lower sample
* C3 - Color of upper sample
*
* RETURNS
*
* AUTHOR
*
* Dieter Bayer
*
* DESCRIPTION
*
* Recursevily supersample between two points on the ray.
*
* CHANGES
*
* Nov 1994 : Creation.
*
******************************************************************************/
static void supersample_atmosphere(Light_List, level, Ray, Atmosphere, d1, C1, d3, C3)
LIGHT_LIST *Light_List;
int level;
RAY *Ray;
ATMOSPHERE *Atmosphere;
DBL d1, d3;
COLOUR C1, C3;
{
DBL d2, jdist;
COLOUR C2;
Increase_Counter(stats[Atmosphere_Supersamples]);
/* Sample between lower and upper point. */
d2 = 0.5 * (d1 + d3);
jdist = d2 + Atmosphere->Jitter * 0.5 * (d3 - d1) * (FRAND() - 0.5);
sample_atmosphere(Light_List, jdist, Ray, Atmosphere, C2);
/* Test for further supersampling. */
if (level < Atmosphere->AA_Level)
{
if (Colour_Distance(C1, C2) >= Atmosphere->AA_Threshold)
{
/* Supersample between lower and middle point. */
supersample_atmosphere(Light_List, level+1, Ray, Atmosphere, d1, C1, d2, C2);
}
if (Colour_Distance(C2, C3) >= Atmosphere->AA_Threshold)
{
/* Supersample between current and higher point. */
supersample_atmosphere(Light_List, level+1, Ray, Atmosphere, d2, C2, d3, C3);
}
}
/* Add supersampled colors. */
VLinComb2(C1, 0.75, C1, 0.25, C2);
VLinComb2(C3, 0.25, C2, 0.75, C3);
}
/*****************************************************************************
*
* FUNCTION
*
* sample_atmosphere
*
* INPUT
*
* Light_List - array containing light source information
* dist - distance of current sample
* Ray - pointer to ray
* Atmosphere - pointer to atmosphere to use
*
* OUTPUT
*
* Col - color of current sample
*
* RETURNS
*
* AUTHOR
*
* Dieter Bayer
*
* DESCRIPTION
*
* Calculate the color of the current atmosphere sample, i.e. the
* sample at distance dist, by summing the light reaching that point
* from all light sources.
*
* CHANGES
*
* Nov 1994 : Creation.
*
******************************************************************************/
static void sample_atmosphere(Light_List, dist, Ray, Atmosphere, Col)
LIGHT_LIST *Light_List;
DBL dist;
RAY *Ray;
ATMOSPHERE *Atmosphere;
COLOUR Col;
{
int i;
DBL alpha, len, k, g, g2;
VECTOR P;
COLOUR Light_Colour;
RAY Light_Ray;
Increase_Counter(stats[Atmosphere_Samples]);
/* Get current sample point. */
VEvaluateRay(P, Ray->Initial, dist, Ray->Direction);
/* Process all light sources. */
Make_Colour(Col, 0.0, 0.0, 0.0);
for (i = 0; i < Frame.Number_Of_Light_Sources; i++)
{
/* Use light only if active and within it's boundaries. */
if (Light_List[i].active && (dist >= Light_List[i].t1) && (dist <= Light_List[i].t2))
{
if (!(Test_Shadow(Light_List[i].Light, &len, &Light_Ray, Ray, P, Light_Colour)))
{
VDot(alpha, Light_Ray.Direction, Ray->Direction);
/* Get attenuation due to scattering. */
switch (Atmosphere->Type)
{
case RAYLEIGH_SCATTERING:
k = (1.0 + Sqr(alpha)) / 2.0;
break;
case MIE_HAZY_SCATTERING:
k = 0.1 * (1.0 + 0.03515625 * pow(1.0 + alpha, 8.0));
break;
case MIE_MURKY_SCATTERING:
k = 0.019607843 * (1.0 + 1.1641532e-8 * pow(1.0 + alpha, 32.0));
break;
case HENYEY_GREENSTEIN_SCATTERING:
g = Atmosphere->Eccentricity;
g2 = Sqr(g);
k = (1.0 - g2) / pow(1.0 + g2 - 2.0 * g * alpha, 1.5);
break;
case ISOTROPIC_SCATTERING:
default:
k = 1.0;
break;
}
k *= Atmosphere->Scattering;
VAddScaledEq(Col, k, Light_Colour);
}
}
}
}
/*****************************************************************************
*
* FUNCTION
*
* do_fog
*
* INPUT
*
* Ray - current ray
* Intersection - current intersection
* Light_Ray_Flag - TRUE if ray is a light source ray
*
* OUTPUT
*
* Colour - color of current ray
*
* RETURNS
*
* AUTHOR
*
* POV-Ray Team
*
* DESCRIPTION
*
* Evaluate all fogs for the current ray and intersection.
*
* CHANGES
*
* Dec 1994 : Rewritten to allow multiple fogs. [DB]
*
* Apr 1995 : Added transmittance threshold and filtering. [DB]
*
* Jun 1995 : Added code for alpha channel support. [DB]
*
******************************************************************************/
static void do_fog(Ray, Intersection, Colour, Light_Ray_Flag)
RAY *Ray;
INTERSECTION *Intersection;
COLOUR Colour;
int Light_Ray_Flag;
{
DBL att, att_inv, width;
COLOUR Col_Fog;
COLOUR sum_att; /* total attenuation. */
COLOUR sum_col; /* total color. */
FOG *Fog;
/* Why are we here. */
if (Frame.Fog == NULL)
{
return;
}
/* Init total attenuation and total color. */
Make_ColourA(sum_att, 1.0, 1.0, 1.0, 1.0, 1.0);
Make_ColourA(sum_col, 0.0, 0.0, 0.0, 0.0, 0.0);
/* Loop over all fogs. */
for (Fog = Frame.Fog; Fog != NULL; Fog = Fog->Next)
{
/* Don't care about fogs with zero distance. */
if (fabs(Fog->Distance) > EPSILON)
{
width = Intersection->Depth;
switch (Fog->Type)
{
case GROUND_MIST:
att = ground_fog(Ray, 0.0, width, Fog, Col_Fog);
break;
default:
att = constant_fog(Ray, 0.0, width, Fog, Col_Fog);
break;
}
/* Check for minimum transmittance. */
if (att < Col_Fog[TRANSM])
{
att = Col_Fog[TRANSM];
}
/* Get attenuation sum due to filtered/unfiltered translucency. */
sum_att[RED] *= att * ((1.0 - Col_Fog[FILTER]) + Col_Fog[FILTER] * Col_Fog[RED]);
sum_att[GREEN] *= att * ((1.0 - Col_Fog[FILTER]) + Col_Fog[FILTER] * Col_Fog[GREEN]);
sum_att[BLUE] *= att * ((1.0 - Col_Fog[FILTER]) + Col_Fog[FILTER] * Col_Fog[BLUE]);
sum_att[FILTER] *= att * Col_Fog[FILTER];
sum_att[TRANSM] *= att * Col_Fog[TRANSM];
if (!Light_Ray_Flag)
{
att_inv = 1.0 - att;
VAddScaledEq(sum_col, att_inv, Col_Fog);
}
}
}
/* Add light coming from background. */
sum_col[RED] += sum_att[RED] * Colour[RED];
sum_col[GREEN] += sum_att[GREEN] * Colour[GREEN];
sum_col[BLUE] += sum_att[BLUE] * Colour[BLUE];
sum_col[FILTER] += sum_att[FILTER] * Colour[FILTER];
sum_col[TRANSM] += sum_att[TRANSM] * Colour[TRANSM];
Assign_Colour(Colour, sum_col);
}
/*****************************************************************************
*
* FUNCTION
*
* do_rainbow
*
* INPUT
*
* Ray - Current ray
* Intersection - Cuurent intersection
*
* OUTPUT
*
* Colour - Current colour
*
* RETURNS
*
* AUTHOR
*
* Dieter Bayer
*
* DESCRIPTION
*
* Create a rainbow using an impressionistic model.
*
* The model was taken from:
*
* Musgrave, F. Kenton, "Prisms and Rainbows: a Dispersion Model
* for Computer Graphics", Proceedings of Graphics Interface '89 -
* Vision Interface '89, p. 227-234.
*
* CHANGES
*
* Jul 1994 : Creation.
*
* Dec 1994 : Modified to allow multiple rainbows. [DB]
*
* Apr 1995 : Added rainbow arcs and filtering. [DB]
*
* Jun 1995 : Added code for alpha channel support. [DB]
*
******************************************************************************/
static void do_rainbow(Ray, Intersection, Colour)
RAY *Ray;
INTERSECTION *Intersection;
COLOUR Colour;
{
int n;
DBL dot, k, ki, index, x, y, l, angle, fade, f;
VECTOR Temp;
COLOUR Cr, Ct;
RAINBOW *Rainbow;
/* Why are we here. */
if (Frame.Rainbow == NULL)
{
return;
}
Make_ColourA(Ct, 0.0, 0.0, 0.0, 1.0, 1.0);
n = 0;
for (Rainbow = Frame.Rainbow; Rainbow != NULL; Rainbow = Rainbow->Next)
{
if ((Rainbow->Pigment != NULL) && (Rainbow->Distance != 0.0) && (Rainbow->Width != 0.0))
{
/* Get angle between ray direction and rainbow's up vector. */
VDot(x, Ray->Direction, Rainbow->Right_Vector);
VDot(y, Ray->Direction, Rainbow->Up_Vector);
l = Sqr(x) + Sqr(y);
if (l > 0.0)
{
l = sqrt(l);
y /= l;
}
angle = fabs(acos(y));
if (angle <= Rainbow->Arc_Angle)
{
/* Get dot product between ray direction and antisolar vector. */
VDot(dot, Ray->Direction, Rainbow->Antisolar_Vector);
if (dot >= 0.0)
{
/* Get index ([0;1]) into rainbow's colour map. */
index = (acos(dot) - Rainbow->Angle) / Rainbow->Width;
/* Jitter index. */
if (Rainbow->Jitter > 0.0)
{
index += (2.0 * FRAND() - 1.0) * Rainbow->Jitter;
}
if ((index >= 0.0) && (index <= 1.0 - EPSILON))
{
/* Get colour from rainbow's colour map. */
Make_Vector(Temp, index, 0.0, 0.0);
Compute_Pigment(Cr, Rainbow->Pigment, Temp);
/* Get fading value for falloff. */
if ((Rainbow->Falloff_Width > 0.0) && (angle > Rainbow->Falloff_Angle))
{
fade = (angle - Rainbow->Falloff_Angle) / Rainbow->Falloff_Width;
fade = (3.0 - 2.0 * fade) * fade * fade;
}
else
{
fade = 0.0;
}
/* Get attenuation factor due to distance. */
k = exp(-Intersection->Depth / Rainbow->Distance);
/* Colour's transm value is used as minimum attenuation value. */
k = max(k, fade * (1.0 - Cr[TRANSM]) + Cr[TRANSM]);
/* Now interpolate the colours. */
ki = 1.0 - k;
/* Attenuate filter value. */
f = Cr[FILTER] * ki;
Ct[RED] += k * Colour[RED] * ((1.0 - f) + f * Cr[RED]) + ki * Cr[RED];
Ct[GREEN] += k * Colour[GREEN] * ((1.0 - f) + f * Cr[GREEN]) + ki * Cr[GREEN];
Ct[BLUE] += k * Colour[BLUE] * ((1.0 - f) + f * Cr[BLUE]) + ki * Cr[BLUE];
Ct[FILTER] *= k * Cr[FILTER];
Ct[TRANSM] *= k * Cr[TRANSM];
n++;
}
}
}
}
}
if (n > 0)
{
VInverseScale(Colour, Ct, (DBL)n);
Colour[FILTER] *= Ct[FILTER];
Colour[TRANSM] *= Ct[TRANSM];
}
}
/*****************************************************************************
*
* FUNCTION
*
* do_skysphere
*
* INPUT
*
* Ray - Current ray
*
* OUTPUT
*
* Colour - Current color
*
* RETURNS
*
* AUTHOR
*
* Dieter Bayer
*
* DESCRIPTION
*
* Calculate color of the sky.
*
* Use the ray direction as a point on the skysphere. Thus the sky can
* easily be colored with all kinds of pigments.
*
* CHANGES
*
* Jul 1994 : Creation.
*
* Dec 1994 : Modified to allow layered pigments. [DB]
*
* Jun 1995 : Added code for alpha channel support. [DB]
*
******************************************************************************/
static void do_skysphere(Ray, Colour)
RAY *Ray;
COLOUR Colour;
{
int i;
DBL att, trans;
COLOUR Col, Col_Temp, Filter;
VECTOR P;
SKYSPHERE *Skysphere;
/* Why are we here. */
if (Frame.Skysphere == NULL)
{
return;
}
Make_ColourA(Col, 0.0, 0.0, 0.0, 0.0, 0.0);
if (((Skysphere = Frame.Skysphere) != NULL) && (Skysphere->Pigments != NULL))
{
Make_ColourA(Filter, 1.0, 1.0, 1.0, 1.0, 1.0);
trans = 1.0;
/* Transform point on unit sphere. */
if (Skysphere->Trans != NULL)
{
MInvTransPoint(P, Ray->Direction, Skysphere->Trans);
}
else
{
Assign_Vector(P, Ray->Direction);
}
for (i = Skysphere->Count-1; i >= 0; i--)
{
/* Compute sky colour from colour map. */
Compute_Pigment(Col_Temp, Skysphere->Pigments[i], P);
att = trans * (1.0 - Col_Temp[FILTER] - Col_Temp[TRANSM]);
VAddScaledEq(Col, att, Col_Temp);
Filter[RED] *= Col_Temp[RED];
Filter[GREEN] *= Col_Temp[GREEN];
Filter[BLUE] *= Col_Temp[BLUE];
Filter[FILTER] *= Col_Temp[FILTER];
Filter[TRANSM] *= Col_Temp[TRANSM];
trans = fabs(Filter[FILTER]) + fabs(Filter[TRANSM]);
}
Colour[RED] = Col[RED] + Colour[RED] * (Filter[RED] * Filter[FILTER] + Filter[TRANSM]);
Colour[GREEN] = Col[GREEN] + Colour[GREEN] * (Filter[GREEN] * Filter[FILTER] + Filter[TRANSM]);
Colour[BLUE] = Col[BLUE] + Colour[BLUE] * (Filter[BLUE] * Filter[FILTER] + Filter[TRANSM]);
Colour[FILTER] = Colour[FILTER] * Filter[FILTER];
Colour[TRANSM] = Colour[TRANSM] * Filter[TRANSM];
}
}
/*****************************************************************************
*
* FUNCTION
*
* constant_fog
*
* INPUT
*
* Ray - current ray
* Depth - intersection depth with fog's boundary
* Width - width of the fog along the ray
* Fog - current fog
*
* OUTPUT
*
* Colour - color of the fog
*
* RETURNS
*
* AUTHOR
*
* POV-Ray Team
*
* DESCRIPTION
*
* Apply distance attenuated fog.
*
* CHANGES
*
* Dec 1994 : Modified to work with multiple fogs. [DB]
*
******************************************************************************/
static DBL constant_fog(Ray, Depth, Width, Fog, Colour)
RAY *Ray;
DBL Depth, Width;
FOG *Fog;
COLOUR Colour;
{
DBL k;
VECTOR P;
if (Fog->Turb != NULL)
{
Depth += Width / 2.0;
VEvaluateRay(P, Ray->Initial, Depth, Ray->Direction);
VEvaluateEq(P, Fog->Turb->Turbulence);
/* The further away the less influence turbulence has. */
k = exp(-Width / Fog->Distance);
Width *= 1.0 - k * min(1.0, Turbulence(P, Fog->Turb)*Fog->Turb_Depth);
}
Assign_Colour(Colour, Fog->Colour);
return (exp(-Width / Fog->Distance));
}
/*****************************************************************************
*
* FUNCTION
*
* ground_fog
*
* INPUT
*
* Ray - current ray
* Depth - intersection depth with fog's boundary
* Width - width of the fog along the ray
* Fog - current fog
*
* OUTPUT
*
* Colour - color of the fog
*
* RETURNS
*
* AUTHOR
*
* Eric Barish
*
* DESCRIPTION
*
* Here is an ascii graph of the ground fog density, it has a maximum
* density of 1.0 at Y <= 0, and approaches 0.0 as Y goes up:
*
* ***********************************
* | | | ****
* | | | ***
* | | | ***
* | | | | ****
* | | | | *****
* | | | | *******
* -----+-----------+------------+------------+-----------+-----
* Y=-2 Y=-1 Y=0 Y=1 Y=2
*
* ground fog density is 1 / (Y*Y+1) for Y >= 0 and equals 1.0 for Y <= 0.
* (It behaves like regular fog for Y <= 0.)
*
* The integral of the density is atan(Y) (for Y >= 0).
*
* CHANGES
*
* Feb 1996 : Changed to behave like normal fog for Y <= 0.
* Fixed bug with reversed offset effect. [DB]
*
******************************************************************************/
static DBL ground_fog(Ray, Depth, Width, Fog, Colour)
RAY *Ray;
DBL Depth, Width;
FOG *Fog;
COLOUR Colour;
{
DBL fog_density, delta;
DBL start, end;
DBL y1, y2, k;
VECTOR P, P1, P2;
/* Get start point. */
VEvaluateRay(P1, Ray->Initial, Depth, Ray->Direction);
/* Get end point. */
VLinComb2(P2, 1.0, P1, Width, Ray->Direction);
/*
* Could preform transfomation here to translate Start and End
* points into ground fog space.
*/
VDot(y1, P1, Fog->Up);
VDot(y2, P2, Fog->Up);
start = (y1 - Fog->Offset) / Fog->Alt;
end = (y2 - Fog->Offset) / Fog->Alt;
/* Get integral along y-axis from start to end. */
if (start <= 0.0)
{
if (end <= 0.0)
{
fog_density = 1.0;
}
else
{
fog_density = (atan(end) - start) / (end - start);
}
}
else
{
if (end <= 0.0)
{
fog_density = (atan(start) - end) / (start - end);
}
else
{
delta = start - end;
if (fabs(delta) > EPSILON)
{
fog_density = (atan(start) - atan(end)) / delta;
}
else
{
fog_density = 1.0 / (Sqr(start) + 1.0);
}
}
}
/* Apply turbulence. */
if (Fog->Turb != NULL)
{
VHalf(P, P1, P2);
VEvaluateEq(P, Fog->Turb->Turbulence);
/* The further away the less influence turbulence has. */
k = exp(-Width / Fog->Distance);
Width *= 1.0 - k * min(1.0, Turbulence(P, Fog->Turb)*Fog->Turb_Depth);
}
Assign_Colour(Colour, Fog->Colour);
return (exp(-Width * fog_density / Fog->Distance));
}
/*****************************************************************************
*
* FUNCTION
*
* intersect_spotlight
*
* INPUT
*
* Ray - current ray
* Light - current light source
*
* OUTPUT
*
* d1, d2 - intersection depths
*
* RETURNS
*
* int - TRUE, if hit
*
* AUTHOR
*
* Dieter Bayer
*
* DESCRIPTION
*
* Intersect a ray with the light cone of a spotlight.
*
* CHANGES
*
* Nov 1994 : Creation.
*
******************************************************************************/
static int intersect_spotlight(Ray, Light, d1, d2)
RAY *Ray;
LIGHT_SOURCE *Light;
DBL *d1, *d2;
{
int viewpoint_is_in_cone;
DBL a, b, c, d, m, l, l1, l2, t, t1, t2, k1, k2, k3, k4;
VECTOR V1;
/* Get cone's slope. Note that cos(falloff) is stored in Falloff! */
a = acos(Light->Falloff);
/* This only works for a < 180 degrees! */
m = tan(a);
m = 1.0 + Sqr(m);
VSub(V1, Ray->Initial, Light->Center);
VDot(k1, Ray->Direction, Light->Direction);
VDot(k2, V1, Light->Direction);
VLength(l, V1);
if (l > EPSILON)
{
viewpoint_is_in_cone = (k2 / l >= Light->Falloff);
}
else
{
viewpoint_is_in_cone = FALSE;
}
if ((k1 <= 0.0) && (k2 < 0.0))
{
return (FALSE);
}
VDot(k3, V1, Ray->Direction);
VDot(k4, V1, V1);
a = 1.0 - Sqr(k1) * m;
b = k3 - k1 * k2 * m;
c = k4 - Sqr(k2) * m;
if (a != 0.0)
{
d = Sqr(b) - a * c;
if (d > EPSILON)
{
d = sqrt(d);
t1 = (-b + d) / a;
t2 = (-b - d) / a;
if (t1 > t2)
{
t = t1; t1 = t2; t2 = t;
}
l1 = k2 + t1 * k1;
l2 = k2 + t2 * k1;
if ((l1 <= 0.0) && (l2 <= 0.0))
{
return (FALSE);
}
if ((l1 <= 0.0) || (l2 <= 0.0))
{
if (l1 <= 0.0)
{
if (viewpoint_is_in_cone)
{
t1 = 0.0;
t2 = (t2 > 0.0) ? (t2) : (Max_Distance);
}
else
{
t1 = t2;
t2 = Max_Distance;
}
}
else
{
if (viewpoint_is_in_cone)
{
t2 = t1;
t1 = 0.0;
}
else
{
t2 = Max_Distance;
}
}
}
*d1 = t1;
*d2 = t2;
return (TRUE);
}
else
{
if (d > -EPSILON)
{
if (viewpoint_is_in_cone)
{
*d1 = 0.0;
*d2 = -b / a;
}
else
{
*d1 = -b / a;
*d2 = Max_Distance;
}
return(TRUE);
}
}
}
else
{
if (viewpoint_is_in_cone)
{
*d1 = 0.0;
*d2 = -c/b;
return(TRUE);
}
}
return (FALSE);
}
/*****************************************************************************
*
* FUNCTION
*
* intersect_cylinderlight
*
* INPUT
*
* Ray - current ray
* Light - current light source
*
* OUTPUT
*
* d1, d2 - intersection depths
*
* RETURNS
*
* int - TRUE, if hit
*
* AUTHOR
*
* Dieter Bayer
*
* DESCRIPTION
*
* Intersect a ray with the light cylinder of a cylinderlight.
*
* CHANGES
*
* Jan 1995 : Creation.
*
******************************************************************************/
static int intersect_cylinderlight(Ray, Light, d1, d2)
RAY *Ray;
LIGHT_SOURCE *Light;
DBL *d1, *d2;
{
DBL a, b, c, d, l1, l2, t, t1, t2, k1, k2, k3, k4;
VECTOR V1;
VSub(V1, Ray->Initial, Light->Center);
VDot(k1, Ray->Direction, Light->Direction);
VDot(k2, V1, Light->Direction);
if ((k1 <= 0.0) && (k2 < 0.0))
{
return (FALSE);
}
a = 1.0 - Sqr(k1);
if (a != 0.0)
{
VDot(k3, V1, Ray->Direction);
VDot(k4, V1, V1);
b = k3 - k1 * k2;
c = k4 - Sqr(k2) - Sqr(Light->Falloff);
d = Sqr(b) - a * c;
if (d > EPSILON)
{
d = sqrt(d);
t1 = (-b + d) / a;
t2 = (-b - d) / a;
if (t1 > t2)
{
t = t1; t1 = t2; t2 = t;
}
l1 = k2 + t1 * k1;
l2 = k2 + t2 * k1;
if ((l1 <= 0.0) && (l2 <= 0.0))
{
return (FALSE);
}
if ((l1 <= 0.0) || (l2 <= 0.0))
{
if (l1 <= 0.0)
{
t1 = 0.0;
}
else
{
t2 = (Max_Distance - k2) / k1;
}
}
*d1 = t1;
*d2 = t2;
return (TRUE);
}
}
return (FALSE);
}
/*****************************************************************************
*
* FUNCTION
*
* Create_Atmosphere
*
* INPUT
*
* OUTPUT
*
* RETURNS
*
* ATMOSPHERE * - created atmosphere
*
* AUTHOR
*
* Dieter Bayer
*
* DESCRIPTION
*
* Create an atmosphere.
*
* CHANGES
*
* Dec 1994 : Creation.
*
******************************************************************************/
ATMOSPHERE *Create_Atmosphere()
{
ATMOSPHERE *New;
New = (ATMOSPHERE *)POV_MALLOC(sizeof(ATMOSPHERE), "fog");
New->Type = ISOTROPIC_SCATTERING;
New->Samples = 100;
New->Distance = 0.0;
New->Distance_Threshold = 0.005;
New->Scattering = 1.0;
New->Eccentricity = 0.0;
Make_Colour(New->Colour, 0.0, 0.0, 0.0);
New->AA_Level = 0;
New->AA_Threshold = 0.3;
New->Jitter = 0.0;
return (New);
}
/*****************************************************************************
*
* FUNCTION
*
* Copy_Atmosphere
*
* INPUT
*
* Old - atmosphere to copy
*
* OUTPUT
*
* RETURNS
*
* ATMOSPHERE * - new atmosphere
*
* AUTHOR
*
* Dieter Bayer
*
* DESCRIPTION
*
* Copy an atmosphere.
*
* CHANGES
*
* Dec 1994 : Creation.
*
******************************************************************************/
void *Copy_Atmosphere(Old)
ATMOSPHERE *Old;
{
ATMOSPHERE *New;
New = Create_Atmosphere();
*New = *Old;
return (New);
}
/*****************************************************************************
*
* FUNCTION
*
* Destroy_Atmosphere
*
* INPUT
*
* Atmosphere - atmosphere to destroy
*
* OUTPUT
*
* RETURNS
*
* AUTHOR
*
* Dieter Bayer
*
* DESCRIPTION
*
* Destroy an atmosphere.
*
* CHANGES
*
* Dec 1994 : Creation.
*
******************************************************************************/
void Destroy_Atmosphere(Atmosphere)
ATMOSPHERE *Atmosphere;
{
if (Atmosphere != NULL)
{
POV_FREE(Atmosphere);
}
}
/*****************************************************************************
*
* FUNCTION
*
* Create_Fog
*
* INPUT
*
* OUTPUT
*
* RETURNS
*
* FOG * - created fog
*
* AUTHOR
*
* Dieter Bayer
*
* DESCRIPTION
*
* Create a fog.
*
* CHANGES
*
* Dec 1994 : Creation.
*
******************************************************************************/
FOG *Create_Fog()
{
FOG *New;
New = (FOG *)POV_MALLOC(sizeof(FOG), "fog");
New->Type = ORIG_FOG;
New->Distance = 0.0;
New->Alt = 0.0;
New->Offset = 0.0;
Make_ColourA(New->Colour, 0.0, 0.0, 0.0, 0.0, 0.0);
Make_Vector(New->Up, 0.0, 1.0, 0.0);
New->Turb = NULL;
New->Turb_Depth = 0.5;
New->Next = NULL;
return (New);
}
/*****************************************************************************
*
* FUNCTION
*
* Copy_Fog
*
* INPUT
*
* Old - fog to copy
*
* OUTPUT
*
* RETURNS
*
* FOG * - new fog
*
* AUTHOR
*
* Dieter Bayer
*
* DESCRIPTION
*
* Copy a fog.
*
* CHANGES
*
* Dec 1994 : Creation.
*
******************************************************************************/
void *Copy_Fog(Old)
FOG *Old;
{
FOG *New;
New = Create_Fog();
*New = *Old;
New->Turb = (TURB *)Copy_Warps(((WARP *)Old->Turb));
return (New);
}
/*****************************************************************************
*
* FUNCTION
*
* Destroy_Fog
*
* INPUT
*
* Fog - fog to destroy
*
* OUTPUT
*
* RETURNS
*
* AUTHOR
*
* Dieter Bayer
*
* DESCRIPTION
*
* Destroy a fog.
*
* CHANGES
*
* Dec 1994 : Creation.
*
******************************************************************************/
void Destroy_Fog(Fog)
FOG *Fog;
{
if (Fog != NULL)
{
Destroy_Turb(Fog->Turb);
POV_FREE(Fog);
}
}
/*****************************************************************************
*
* FUNCTION
*
* Create_Rainbow
*
* INPUT
*
* OUTPUT
*
* RETURNS
*
* RAINBOW * - created rainbow
*
* AUTHOR
*
* Dieter Bayer
*
* DESCRIPTION
*
* Create a rainbow.
*
* CHANGES
*
* Dec 1994 : Creation.
*
******************************************************************************/
RAINBOW *Create_Rainbow()
{
RAINBOW *New;
New = (RAINBOW *)POV_MALLOC(sizeof(RAINBOW), "fog");
New->Distance = Max_Distance;
New->Jitter = 0.0;
New->Angle = 0.0;
New->Width = 0.0;
New->Falloff_Width = 0.0;
New->Arc_Angle = 180.0;
New->Falloff_Angle = 180.0;
New->Pigment = NULL;
Make_Vector(New->Antisolar_Vector, 0.0, 0.0, 0.0);
Make_Vector(New->Right_Vector, 1.0, 0.0, 0.0);
Make_Vector(New->Up_Vector, 0.0, 1.0, 0.0);
New->Next = NULL;
return (New);
}
/*****************************************************************************
*
* FUNCTION
*
* Copy_Rainbow
*
* INPUT
*
* Old - rainbow to copy
*
* OUTPUT
*
* RETURNS
*
* RAINBOW * - new rainbow
*
* AUTHOR
*
* Dieter Bayer
*
* DESCRIPTION
*
* Copy a rainbow.
*
* CHANGES
*
* Dec 1994 : Creation.
*
******************************************************************************/
void *Copy_Rainbow(Old)
RAINBOW *Old;
{
RAINBOW *New;
New = Create_Rainbow();
*New = *Old;
return (New);
}
/*****************************************************************************
*
* FUNCTION
*
* Destroy_Rainbow
*
* INPUT
*
* Rainbow - rainbow to destroy
*
* OUTPUT
*
* RETURNS
*
* AUTHOR
*
* Dieter Bayer
*
* DESCRIPTION
*
* Destroy a rainbow.
*
* CHANGES
*
* Dec 1994 : Creation.
*
******************************************************************************/
void Destroy_Rainbow(Rainbow)
RAINBOW *Rainbow;
{
if (Rainbow != NULL)
{
Destroy_Pigment(Rainbow->Pigment);
POV_FREE(Rainbow);
}
}
/*****************************************************************************
*
* FUNCTION
*
* Create_Skysphere
*
* INPUT
*
* OUTPUT
*
* RETURNS
*
* SKYSPHERE * - created skysphere
*
* AUTHOR
*
* Dieter Bayer
*
* DESCRIPTION
*
* Create a skysphere.
*
* CHANGES
*
* Dec 1994 : Creation.
*
******************************************************************************/
SKYSPHERE *Create_Skysphere()
{
SKYSPHERE *New;
New = (SKYSPHERE *)POV_MALLOC(sizeof(SKYSPHERE), "fog");
New->Count = 0;
New->Pigments = NULL;
New->Trans = Create_Transform();
return (New);
}
/*****************************************************************************
*
* FUNCTION
*
* Copy_Skysphere
*
* INPUT
*
* Old - skysphere to copy
*
* OUTPUT
*
* RETURNS
*
* SKYSPHERE * - copied skysphere
*
* AUTHOR
*
* Dieter Bayer
*
* DESCRIPTION
*
* Copy a skysphere.
*
* CHANGES
*
* Dec 1994 : Creation.
*
******************************************************************************/
void *Copy_Skysphere(Old)
SKYSPHERE *Old;
{
int i;
SKYSPHERE *New;
New = Create_Skysphere();
Destroy_Transform(New->Trans);
*New = *Old;
New->Trans = Copy_Transform(Old->Trans);
if (New->Count > 0)
{
New->Pigments = (PIGMENT **)POV_MALLOC(New->Count*sizeof(PIGMENT *), "skysphere pigment");
for (i = 0; i < New->Count; i++)
{
New->Pigments[i] = Copy_Pigment(Old->Pigments[i]);
}
}
return (New);
}
/*****************************************************************************
*
* FUNCTION
*
* Destroy_Skysphere
*
* INPUT
*
* Skysphere - skysphere to destroy
*
* OUTPUT
*
* RETURNS
*
* AUTHOR
*
* Dieter Bayer
*
* DESCRIPTION
*
* Destroy a skysphere.
*
* CHANGES
*
* Dec 1994 : Creation.
*
******************************************************************************/
void Destroy_Skysphere(Skysphere)
SKYSPHERE *Skysphere;
{
int i;
if (Skysphere != NULL)
{
for (i = 0; i < Skysphere->Count; i++)
{
Destroy_Pigment(Skysphere->Pigments[i]);
}
POV_FREE(Skysphere->Pigments);
Destroy_Transform(Skysphere->Trans);
POV_FREE(Skysphere);
}
}
/*****************************************************************************
*
* FUNCTION
*
* Rotate_Skysphere
*
* INPUT
*
* Vector - Rotation vector
*
* OUTPUT
*
* Skysphere - Pointer to skysphere structure
*
* RETURNS
*
* AUTHOR
*
* Dieter Bayer
*
* DESCRIPTION
*
* Rotate a skysphere.
*
* CHANGES
*
* Feb 1996 : Creation.
*
******************************************************************************/
void Rotate_Skysphere(Skysphere, Vector)
SKYSPHERE *Skysphere;
VECTOR Vector;
{
TRANSFORM Trans;
Compute_Rotation_Transform(&Trans, Vector);
Transform_Skysphere(Skysphere, &Trans);
}
/*****************************************************************************
*
* FUNCTION
*
* Scale_Skysphere
*
* INPUT
*
* Vector - Scaling vector
*
* OUTPUT
*
* Skysphere - Pointer to skysphere structure
*
* RETURNS
*
* AUTHOR
*
* Dieter Bayer
*
* DESCRIPTION
*
* Scale a skysphere.
*
* CHANGES
*
* Feb 1996 : Creation.
*
******************************************************************************/
void Scale_Skysphere(Skysphere, Vector)
SKYSPHERE *Skysphere;
VECTOR Vector;
{
TRANSFORM Trans;
Compute_Scaling_Transform(&Trans, Vector);
Transform_Skysphere(Skysphere, &Trans);
}
/*****************************************************************************
*
* FUNCTION
*
* Translate_Skysphere
*
* INPUT
*
* Vector - Translation vector
*
* OUTPUT
*
* Skysphere - Pointer to skysphere structure
*
* RETURNS
*
* AUTHOR
*
* Dieter Bayer
*
* DESCRIPTION
*
* Translate a skysphere.
*
* CHANGES
*
* Feb 1996 : Creation.
*
******************************************************************************/
void Translate_Skysphere(Skysphere, Vector)
SKYSPHERE *Skysphere;
VECTOR Vector;
{
TRANSFORM Trans;
Compute_Translation_Transform(&Trans, Vector);
Transform_Skysphere(Skysphere, &Trans);
}
/*****************************************************************************
*
* FUNCTION
*
* Transform_Skysphere
*
* INPUT
*
* Trans - Pointer to transformation
*
* OUTPUT
*
* Skysphere - Pointer to skysphere structure
*
* RETURNS
*
* AUTHOR
*
* Dieter Bayer
*
* DESCRIPTION
*
* Transform a skysphere.
*
* CHANGES
*
* Feb 1996 : Creation.
*
******************************************************************************/
void Transform_Skysphere(Skysphere, Trans)
SKYSPHERE *Skysphere;
TRANSFORM *Trans;
{
if (Skysphere->Trans == NULL)
{
Skysphere->Trans = Create_Transform();
}
Compose_Transforms(Skysphere->Trans, Trans);
}