#include #include #include #include "cloud.h" /* * The QAEB cloud program. * * Billowing functions module. * * "Realistic" cloud dynamics through simulation of "turning inside out." * * Copyright 1998 F. Kenton Musgrave * All rights reserved */ extern CameraType camera; extern OptionsType options; extern void ConstructRotMat(Vector axis, double theta, Matrix M); extern double Turbulence(Vector point, double lacunarity, double octaves); /* * Routine to animate billowing, ala smoke rings, of pseudo-spherical * cloudlets. * * This version returns the value of the fractal cloud function, to be * "shaped" (by the particle's field) into a cloudlet later. * * Note that billowing is along a given axis (the "forward" direction), * and that such cloudlets are not designed to be viewed from behind, * where a singularity will be evident. * * This is essentially a 2D construction, in the plane of the "forward" * vector and the vector from the cloudlet center to the sample point. * * The animation consists of rotating the sample about the cloudlet center, * towards the "forward" vector as a function of time. This has the net * effect of stretching the texture backwards around the cloudlet-sphere. * * New features are added with time, to replace the older, stretched-out * features. This is done by taking two differently-rotated hypertexture * samples, and weighting their contributions by time: * new ones ramp up from zero, then ramp down as they become "old." * * The amplitude of each signal adding feratures then varies as: * * /\ /\ /\ /\ /\ * / \/ \/ \/ \/ \... * * with the "stretch" factor reset to zero when amplitude is zero. * */ double Billow( Cloudlet *cloudlet, Vector sample_pt, double octaves ) { Vector p_vec; /* from cloudlet center to sample point */ Vector p_vec_norm; /* above, normalized */ Vector p_rot; /* rotated version of p_vec */ Vector r_vec; /* rotation axis */ Vector sample; /* xformed sample point in billow-space */ double theta; /* angle between "forward" and p_vec */ double theta_inc; /* angle to rotate at this frame */ double radius; /* particle radius */ double radius_inv; /* inverse of particle radius */ double local_octaves; /* local number of octaves for a cloudlet */ double density; /* overall density at sample_pt */ double local_den; /* local density of at sub-sample pt. */ double amplitude; /* relative amplitude of two components */ int time; /* periodic time for Shephard's tone scheme */ int period; /* the period of repetition of features */ int half_period; /* half the above period */ Matrix M; /* rotation matrix for coord xform */ radius = cloudlet->radius; /* get vector from cloudlet center to sample point */ VECSUB( sample_pt, cloudlet->pos, &p_vec ); /* get angle of sample from "forward" direction */ p_vec_norm = p_vec; VECNORM( &p_vec_norm ); /* cloudlet->direction is already nomalized */ theta = VECDOT( cloudlet->direction, p_vec_norm ); if ( theta > 1.0 ) theta = 0.0; if ( theta < -1.0 ) theta = M_PI; else theta = acos( theta ); /* scale p_vec to keep features constant as radius changes */ if ( radius > 1e-10 ) radius_inv = 1.0 / radius; else radius_inv = 0.0; VECSCALE( p_vec, radius_inv, &p_vec ); /* construct rotation axis */ VECCROSS( p_vec_norm, cloudlet->direction, &r_vec ); /* the constant in this numerator needs to be researched */ half_period = (int)(0.48 / cloudlet->billow_rate); period = half_period + half_period; /* get a periodic variable of time, i.e., frame # */ time = (half_period+options.frame) % period; /* get rotation amount as a function of time */ theta_inc = theta * time * cloudlet->billow_rate; /* construct xform matrix to rotate by angle theta around axis r_vec */ ConstructRotMat( r_vec, theta_inc, M ); /* use that matrix to xform the sample point */ p_rot = p_vec; VecRotate( &p_rot, M ); /* get new, transformed point in texture space */ VECADD( p_rot, cloudlet->tspace_pos, &sample ); VECSCALE( sample, options.tspace_scale, &sample ); /* adjust for detail lost to stretching */ local_octaves = octaves - LOG2(theta - theta_inc + 1.0); /* evaluate density of cloud function at sample point */ local_den = options.fractal_gain * (Turbulence(sample, options.lacunarity, local_octaves) - options.threshold ); amplitude = (time-1.0) / half_period; if ( amplitude > 1.0 ) amplitude = 2.0 - amplitude; density = amplitude * local_den; /* * Now perform transformation of the second sample point. */ /* get a periodic variable of time, i.e., frame # */ time = options.frame % period; /* get rotation amount as a function of time */ theta_inc = theta * time * cloudlet->billow_rate; /* construct xform matrix to rotate by angle theta around axis r_vec */ ConstructRotMat( r_vec, theta_inc, M ); /* use that matrix to xform the sample point */ p_rot = p_vec; VecRotate( &p_rot, M ); /* get new, transformed point in texture space */ VECADD( p_rot, cloudlet->tspace_pos, &sample ); VECSCALE( sample, options.tspace_scale, &sample ); /* adjust for detail lost to stretching */ local_octaves = octaves - LOG2(theta - theta_inc + 1.0); local_den = options.fractal_gain * (Turbulence(sample, options.lacunarity, local_octaves) - options.threshold ); density += (1.0-amplitude) * local_den; return( density ); } /* Billow() */ /* * Similar to above routine, only without billowing. * * Purpose: To get similar geometry, but faster, for test images. */ double NoBillow( Cloudlet *cloudlet, Vector sample_pt, double field_strength, double octaves ) { Vector p_vec; /* from cloudlet center to sample point */ Vector p_vec_norm; /* above, normalized */ Vector sample; /* xformed sample point in billow-space */ double radius; /* particle radius */ double radius_inv; /* inverse of particle radius */ double density; /* overall density at sample_pt */ /* get vector from cloudlet center to sample point */ VECSUB( sample_pt, cloudlet->pos, &p_vec ); /* get normalize copy of that vector */ p_vec_norm = p_vec; VECNORM( &p_vec_norm ); /* scale p_vec to keep features constant as radius changes */ radius = cloudlet->radius; if ( radius > 1e-10 ) radius_inv = 1.0 / radius; else radius_inv = 0.0; VECSCALE( p_vec, radius_inv, &p_vec ); /* get new, transformed point in texture space */ VECADD( sample_pt, cloudlet->tspace_pos, &sample ); VECSCALE( sample, options.tspace_scale, &sample ); /* evaluate density of cloud function at sample point */ density = field_strength * options.fractal_gain * (Turbulence(sample, options.lacunarity, octaves) - options.threshold ); return( density ); } /* NoBillow() */