/**************************************************************************** * atmosph.c * * This module contains all functions for atmospheric effects. * * from Persistence of Vision(tm) Ray Tracer * Copyright 1996,1999 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 email to team-coord@povray.org or visit us on the web at * http://www.povray.org. The latest version of POV-Ray may be found at this site. * * 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 "chi2.h" #include "colour.h" #include "povray.h" #include "texture.h" #include "pigment.h" #include "objects.h" #include "lighting.h" #include "matrices.h" #include "media.h" #include "texture.h" #include "ray.h" /***************************************************************************** * Local preprocessor defines ******************************************************************************/ #define BLACK_LEVEL 0.0001 /***************************************************************************** * Local typedefs ******************************************************************************/ /***************************************************************************** * Local variables ******************************************************************************/ /***************************************************************************** * Static functions ******************************************************************************/ static DBL constant_fog (RAY *Ray, DBL Depth, DBL Width, FOG *Fog, COLOUR Colour); static DBL ground_fog (RAY *Ray, DBL Depth, DBL Width, FOG *Fog, COLOUR Colour); static void do_fog (RAY *Ray, INTERSECTION *Intersection, COLOUR Colour, int Light_Ray_Flag); static void do_rainbow (RAY *Ray, INTERSECTION *Intersection, COLOUR Colour); static void do_skysphere (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() { } /***************************************************************************** * * FUNCTION * * Deinitialize_Atmosphere_Code * * INPUT * * OUTPUT * * RETURNS * * AUTHOR * * Dieter Bayer * * DESCRIPTION * * Deinitialize atmosphere specific variables. * * CHANGES * * Aug 1995 : Creation. * ******************************************************************************/ void Deinitialize_Atmosphere_Code() { } /***************************************************************************** * * 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 *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 *Ray, INTERSECTION *Intersection, COLOUR Colour, int Light_Ray_Flag) { IMEDIA *Media_List[2]; if (!Light_Ray_Flag) { do_rainbow(Ray, Intersection, Colour); } Media_List[0] = Frame.Atmosphere; Media_List[1] = NULL; Simulate_Media(Media_List, Ray, Intersection, Colour, Light_Ray_Flag); do_fog(Ray, Intersection, Colour, Light_Ray_Flag); } /***************************************************************************** * * 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 *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. */ Colour[RED] = sum_col[RED] + sum_att[RED] * Colour[RED]; Colour[GREEN] = sum_col[GREEN] + sum_att[GREEN] * Colour[GREEN]; Colour[BLUE] = sum_col[BLUE] + sum_att[BLUE] * Colour[BLUE]; Colour[FILTER] = sum_col[FILTER] + sum_att[FILTER] * Colour[FILTER]; Colour[TRANSM] = sum_col[TRANSM] + sum_att[TRANSM] * Colour[TRANSM]; } /***************************************************************************** * * 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 *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 *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_Colour(Col, 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 *Ray, DBL Depth, DBL 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 *Ray, DBL Depth, DBL 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 * * 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. * ******************************************************************************/ FOG *Copy_Fog(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) { 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. * ******************************************************************************/ RAINBOW *Copy_Rainbow(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) { 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. * ******************************************************************************/ SKYSPHERE *Copy_Skysphere(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) { 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 *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 *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 *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 *Skysphere, TRANSFORM *Trans) { if (Skysphere->Trans == NULL) { Skysphere->Trans = Create_Transform(); } Compose_Transforms(Skysphere->Trans, Trans); }