// **********************************************************
// OBJECT EXPLODER INCLUDE FILE FOR PERSISTENCE OF VISION 3.x
// **********************************************************
//
// Created by Chris Colefax, March 1997
//
// See "Explode.txt" for more information.
//
// **********************************************************

// CHECK EXPLODE OBJECT AND CLOCK
// ******************************
   #declare _EX_tempver = version #version 3.0
   #ifndef (explode_object) #warning "No object specified for explosion!\n" #else
   #ifndef (explode_clock) #declare explode_clock = clock #end
   #if (explode_clock <= 0) object {explode_object} #else

// DETERMINE OBJECT DIMENSIONS
// ***************************
   #ifdef  (object_center) #declare _EX_ocentre   = object_center * <1, 1, 1> #end
   #ifdef  (object_centre) #declare _EX_ocentre   = object_centre * <1, 1, 1> #end
   #ifdef  (object_size)   #declare _EX_osize     = object_size   * <1, 1, 1> #end
   #ifndef (object_hollow) #declare object_hollow = true #end

// DETERMINE OBJECT SIZE IF NOT SPECIFIED
// **************************************
   #ifdef (_EX_ocentre) #ifndef (_EX_osize)
         #ifdef (object_corner1)
            #declare _EX_osize = 2 * (_EX_ocentre - object_corner1)
         #else #ifdef (object_corner2)
            #declare _EX_osize = 2 * (_EX_ocentre - object_corner2)
         #else #declare _EX_osize = <2, 2, 2>
   #end #end #end

// DETERMINE OBJECT CENTRE IF NOT SPECIFIED
// ****************************************
   #else #ifdef (_EX_osize)
      #ifdef (object_corner1)
         #declare _EX_ocentre = object_corner1 + (_EX_osize / 2)
      #else #ifdef (object_corner2)
         #declare _EX_ocentre = object_corner2 - (_EX_osize / 2)
         #else #declare _EX_ocentre = <0, 0, 0>
      #end #end

// DETERMINE OBJECT CENTRE & SIZE IF NEITHER SPECIFIED
// ***************************************************
   #else #ifdef (object_corner1) #ifdef (object_corner2)
      #declare _EX_ocentre = (object_corner1 + object_corner2) / 2
      #declare _EX_osize   =  object_corner1 - object_corner2
   #else
      #declare _EX_ocentre = <0, 0, 0>
      #declare _EX_osize   = 2 * object_corner1
   #end #else
      #ifdef (object_corner2)
         #declare _EX_ocentre = <0, 0, 0>
         #declare _EX_osize   = 2 * object_corner2
      #else
         #declare _EX_ocentre = <0, 0, 0>
         #declare _EX_osize   = <2, 2, 2>
   #end #end #end #end
   #declare _EX_osize = <abs(_EX_osize.x), abs(_EX_osize.y), abs(_EX_osize.z)>

// DETERMINE PARTICLE PARAMETERS
// *****************************
   #ifdef (particle_res)
      #declare _EX_pres = particle_res * <1, 1, 1>
      #declare _EX_pres = <abs(int(_EX_pres.x)), abs(int(_EX_pres.y)), abs(int(_EX_pres.z))>
      #if (_EX_pres.x < 1) #declare _EX_pres = <1, _EX_pres.y, _EX_pres.z> #end
      #if (_EX_pres.y < 1) #declare _EX_pres = <_EX_pres.x, 1, _EX_pres.z> #end
      #if (_EX_pres.z < 1) #declare _EX_pres = <_EX_pres.x, _EX_pres.y, 1> #end
   #else
      #declare _EX_pres = <3, 3, 3>
   #end

   #ifndef (particle_object)  #declare particle_object  = box {<-.5, -.5, -.5>, <.5, .5, .5>}  #end
   #ifndef (particle_texture) #declare particle_texture = texture {pigment {rgb <.5, .5, .5>}} #end
   #declare _EX_gridsize    = _EX_osize / _EX_pres
   #declare _EX_gridstart   = _EX_ocentre + (_EX_gridsize / 2) - (_EX_osize / 2)
   #declare _EX_gridstop    = _EX_ocentre + (_EX_osize / 2)
   #declare _EX_pobject = object {particle_object scale _EX_gridsize}

// DETERMINE EXPLOSION PARAMETERS
// ******************************
   #ifdef (exp_location) #declare _EX_eloc = exp_location * <1, 1, 1>
      #else #declare _EX_eloc = <0, 0, 0> #end
   #ifdef (exp_spin) #declare _EX_spin = exp_spin * <1, 1, 1>
      #else #declare _EX_spin = <0, 0, 0> #end
   #ifndef (exp_strength) #declare exp_strength = 10 #end
   #ifndef (exp_falloff)  #declare exp_falloff  =  0 #end
   #ifndef (exp_gravity)  #declare exp_gravity  =  0 #end
   #ifndef (exp_sky) #declare exp_sky = y
      #else #declare exp_sky = vnormalize(exp_sky) #end
   #declare _EX_grav = -exp_sky * abs(exp_gravity)

// DETERMINE GROUND PARAMETERS
// ***************************
   #ifndef (ground_plane) #declare ground_plane = false
      #else #if (ground_plane != false)
         #ifndef (ground_dist) #declare ground_dist = 0 #end
         #ifndef (ground_reflection) #declare ground_reflection = 0
            #else #declare ground_reflection = abs(ground_reflection) #end
         #ifndef (max_bounces) #declare max_bounces = 1
            #else #declare max_bounces = abs(max_bounces) #end
         #if (exp_sky.y != 1)
            #declare _EX_wrotx = degrees(atan2(vlength(exp_sky * <1, 0, 1>), exp_sky.y))
            #declare _EX_wroty = degrees(atan2(exp_sky.x, exp_sky.z))
            #declare _EX_grav  = -y * abs(exp_gravity)
   #end #end #end

// DETERMINE TURBULENCE (RANDOMNESS) PARAMETERS
// ********************************************
   #ifndef (exp_turb) #declare exp_turb = 0
      #else #if (exp_turb != 0)
         #ifdef (exp_seed) #declare _EX_rand = seed(exp_seed)
            #else #declare _EX_rand = seed(0) #end
         #ifndef (scale_turb) #declare _EX_sturb = exp_turb
            #else #declare _EX_sturb = scale_turb * exp_turb #end
         #ifndef (rotate_turb) #declare _EX_rturb = exp_turb
            #else #declare _EX_rturb = rotate_turb * exp_turb #end
         #ifndef (vel_turb) #declare _EX_vturb = exp_turb
            #else #declare _EX_vturb = vel_turb * exp_turb #end
         #ifndef (dir_turb) #declare _EX_dturb = exp_turb
            #else #declare _EX_dturb = dir_turb * exp_turb #end
         #ifndef (spin_turb) #declare _EX_spturb = exp_turb
            #else #declare _EX_spturb = spin_turb * exp_turb #end
   #end #end

// CREATE EXPLOSION
// ****************
   union {
      #declare _EX_gridx = _EX_gridstart.x #while (_EX_gridx < _EX_gridstop.x)
         #declare _EX_gridy = _EX_gridstart.y #while (_EX_gridy < _EX_gridstop.y)
            #declare _EX_gridz = _EX_gridstart.z #while (_EX_gridz < _EX_gridstop.z)

// CREATE PARTICLE FROM ORIGINAL OBJECT
// ************************************
   #if (exp_turb = 0)
      #declare _EX_pscale  = <1, 1, 1>
      #declare _EX_protate = <0, 0, 0>
   #else
      #declare _EX_pscale  = 1 + ((<rand(_EX_rand), rand(_EX_rand), rand(_EX_rand)> - .5) * _EX_sturb * 2)
      #declare _EX_protate = (1 + ((<rand(_EX_rand), rand(_EX_rand), rand(_EX_rand)> - .5) * _EX_rturb)) * 360
   #end
   #declare _EX_ptrans = <_EX_gridx, _EX_gridy, _EX_gridz>

   #declare _EX_particle = #if (object_hollow = false)
      intersection {object {explode_object}
         object {_EX_pobject scale _EX_pscale * 1.01 rotate _EX_protate
            translate _EX_ptrans texture {particle_texture}}
   #else object {explode_object #end
      clipped_by {_EX_pobject scale _EX_pscale * 1.02 rotate _EX_protate
         translate _EX_ptrans}
      translate -_EX_ptrans}

// CALCULATE INITIAL PARTICLE VELOCITY
// ***********************************
   #if (exp_strength = 0) #if (exp_turb = 0)
      #declare _EX_pvel = <0, 0, 0>
      #else
         #declare _EX_pvel = (<rand(_EX_rand), rand(_EX_rand), rand(_EX_rand)> - .5) * _EX_gridsize * _EX_vturb * 2
      #end
   #else
      #declare _EX_pvel = _EX_ptrans - _EX_eloc
      #if (exp_falloff = 0)
         #declare _EX_pvel = vnormalize(_EX_pvel) * exp_strength
      #else
         #declare _EX_pvel = vnormalize(_EX_pvel) * pow(.25, vlength(_EX_pvel) / exp_falloff) * exp_strength
      #end
      #if (exp_turb != 0)
         #declare _EX_pvel = _EX_pvel * (1 + ((<rand(_EX_rand), rand(_EX_rand), rand(_EX_rand)> - .5) * _EX_vturb * 2))
         #declare _EX_pvel = vrotate(_EX_pvel, (<rand(_EX_rand), rand(_EX_rand), rand(_EX_rand)> - .5) * _EX_dturb * 360)
      #end
   #end

// CALCULATE PARTICLE LOCATION & SPIN
// **********************************
   #if (ground_plane = false)
      object {_EX_particle
      #if (exp_strength != 0 & vlength(_EX_spin) != 0)
         #declare _EX_pspin = _EX_spin * vlength(_EX_pvel) / exp_strength
         #if (exp_turb != 0) #declare _EX_pspin = _EX_pspin * (1 + ((<rand(_EX_rand), rand(_EX_rand), rand(_EX_rand)> - .5) * _EX_spturb * 2)) #end
         rotate _EX_pspin * explode_clock * 360
      #end
      translate _EX_ptrans + (_EX_pvel * explode_clock) + (.5 * _EX_grav * explode_clock * explode_clock)
      }

// ROTATE VECTORS IF SKY IS NOT +Y
// *******************************
   #else #if (exp_sky.y = 1)
      #declare _EX_iloc = _EX_ptrans
      #declare _EX_ivel = _EX_pvel
   #else
      #declare _EX_iloc = vrotate(vrotate(_EX_ptrans, -y * _EX_wroty), -x * _EX_wrotx)
      #declare _EX_ivel = vrotate(vrotate(_EX_pvel,   -y * _EX_wroty), -x * _EX_wrotx)
   #end

// SHIFT IF GROUND DISTANCE IS NOT 0
// *********************************
   #if (ground_dist != 0) #declare _EX_iloc = _EX_iloc - ground_dist #end

// CREATE PARTICLE IF ABOVE GROUND
// *******************************
   #if (_EX_iloc.y >= 0)

// CALCULATE PARTICLE POSITION
// ***************************
   #declare _EX_cclk = explode_clock
   #declare _EX_iclk = 0 #declare _EX_fclk = 0 #declare _EX_bounces = 0
   #declare _EX_cloc = _EX_iloc + (_EX_ivel * _EX_cclk) + (.5 * _EX_grav * _EX_cclk * _EX_cclk)
   #while (_EX_cloc.y < 0)

// CALCULATE TIME OF INTERSECTION WITH GROUND PLANE
// ************************************************
   #if (_EX_grav.y = 0) #declare _EX_iclk = _EX_iloc.y / -_EX_ivel.y
      #else #declare _EX_iclk = (-_EX_ivel.y - sqrt (_EX_ivel.y * _EX_ivel.y - (2 * _EX_grav.y * _EX_iloc.y))) / _EX_grav.y #end

   #declare _EX_iloc = _EX_iloc + (_EX_ivel * _EX_iclk) + (.5 * _EX_grav * _EX_iclk * _EX_iclk)
   #declare _EX_ivel = (_EX_ivel + (_EX_grav * _EX_iclk)) * <1, -ground_reflection, 1>
   #declare _EX_fclk = _EX_fclk + _EX_iclk

// CHECK IF PARTICLE HAS STOPPED MOVING
// ************************************
   #if (ground_reflection = 0 | _EX_bounces >= max_bounces)
      #declare _EX_cloc = _EX_iloc * <1, 0, 1>
      #declare _EX_cclk = 0
   #else
      #declare _EX_cclk = _EX_cclk - _EX_iclk
      #declare _EX_bounces = _EX_bounces + 1
      #declare _EX_cloc = _EX_iloc + (_EX_ivel * _EX_cclk) + (.5 * _EX_grav * _EX_cclk * _EX_cclk)
   #end #end
   #declare _EX_fclk = _EX_fclk + _EX_cclk
   #declare _EX_ploc = _EX_cloc

// UNDO GROUND DISTANCE SHIFT AND SKY VECTOR ROTATION
// **************************************************
   #if (ground_dist != 0) #declare _EX_ploc = _EX_ploc + ground_dist #end
   #if (exp_sky.y != 1)   #declare _EX_ploc = vrotate(vrotate(_EX_ploc, x * _EX_wrotx), y * _EX_wroty) #end

// SPIN PARTICLE
// *************
   object {_EX_particle
      #if (exp_strength != 0 & vlength(_EX_spin) != 0)
         #declare _EX_pspin = _EX_spin * vlength(_EX_pvel) / exp_strength
         #if (exp_turb != 0) #declare _EX_pspin = _EX_pspin * (1 + ((<rand(_EX_rand), rand(_EX_rand), rand(_EX_rand)> - .5) * _EX_spturb * 2)) #end
         rotate _EX_pspin * _EX_fclk * 360
      #end
   translate _EX_ploc}
   #end #end

// LOOP THROUGH PARTICLE GRID
// **************************
         #declare _EX_gridz = _EX_gridz + _EX_gridsize.z #end
      #declare _EX_gridy = _EX_gridy + _EX_gridsize.y #end
   #declare _EX_gridx = _EX_gridx + _EX_gridsize.x #end
   }

   #end #end
   #version _EX_tempver
