// Filename: sparkleParticleRenderer.cxx
// Created by:  charles (27Jun00)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license.  You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
//
// To contact the maintainers of this program write to
// panda3d-general@lists.sourceforge.net .
//
////////////////////////////////////////////////////////////////////

#include "sparkleParticleRenderer.h"

#include "boundingSphere.h"
#include "geomNode.h"

////////////////////////////////////////////////////////////////////
//    Function : SparkleParticleRenderer
//      Access : Public
// Description : Default Constructor
////////////////////////////////////////////////////////////////////
SparkleParticleRenderer::
SparkleParticleRenderer() :
  BaseParticleRenderer(PR_ALPHA_NONE),
  _center_color(Colorf(1.0f, 1.0f, 1.0f, 1.0f)),
  _edge_color(Colorf(1.0f, 1.0f, 1.0f, 1.0f)),
  _birth_radius(0.1f), _death_radius(0.1f)
{
  _line_primitive = new GeomLine;
  init_geoms();
}

////////////////////////////////////////////////////////////////////
//    Function : SparkleParticleRenderer
//      Access : Public
// Description : Constructor
////////////////////////////////////////////////////////////////////
SparkleParticleRenderer::
SparkleParticleRenderer(const Colorf& center, const Colorf& edge,
                        float birth_radius, float death_radius,
                        SparkleParticleLifeScale life_scale,
                        ParticleRendererAlphaMode alpha_mode) :
  BaseParticleRenderer(alpha_mode),
  _center_color(center), _edge_color(edge), _birth_radius(birth_radius),
  _death_radius(death_radius), _life_scale(life_scale)
{
  _line_primitive = new GeomLine;
  init_geoms();
}

////////////////////////////////////////////////////////////////////
//    Function : SparkleParticleRenderer
//      Access : Public
// Description : Copy Constructor
////////////////////////////////////////////////////////////////////
SparkleParticleRenderer::
SparkleParticleRenderer(const SparkleParticleRenderer& copy) :
  BaseParticleRenderer(copy) {
  _center_color = copy._center_color;
  _edge_color = copy._edge_color;
  _birth_radius = copy._birth_radius;
  _death_radius = copy._death_radius;
  _life_scale = copy._life_scale;

  _line_primitive = new GeomLine;
  init_geoms();
}

////////////////////////////////////////////////////////////////////
//    Function : ~SparkleParticleRenderer
//      Access : Public
// Description : Destructor
////////////////////////////////////////////////////////////////////
SparkleParticleRenderer::
~SparkleParticleRenderer() {
}

////////////////////////////////////////////////////////////////////
//    Function : make copy
//      Access : Public
// Description : child virtual for spawning systems
////////////////////////////////////////////////////////////////////
BaseParticleRenderer *SparkleParticleRenderer::
make_copy() {
  return new SparkleParticleRenderer(*this);
}

////////////////////////////////////////////////////////////////////
//    Function : birth_particle
//      Access : Private, virtual
// Description : child birth
////////////////////////////////////////////////////////////////////
void SparkleParticleRenderer::
birth_particle(int) {
}

////////////////////////////////////////////////////////////////////
//    Function : kill_particle
//      Access : Private, virtual
// Description : child kill
////////////////////////////////////////////////////////////////////
void SparkleParticleRenderer::
kill_particle(int) {
}

////////////////////////////////////////////////////////////////////
//    Function : resize_pool
//      Access : private
// Description : resizes the render pool.  Reference counting
//               makes this easy.
////////////////////////////////////////////////////////////////////
void SparkleParticleRenderer::
resize_pool(int new_size) {
  _vertex_array = PTA_Vertexf::empty_array(new_size * 12);
  _color_array = PTA_Colorf::empty_array(new_size * 12);

  _line_primitive->set_coords(_vertex_array);
  _line_primitive->set_colors(_color_array, G_PER_VERTEX);

  _max_pool_size = new_size;

  init_geoms();
}

////////////////////////////////////////////////////////////////////
//    Function : init_geoms
//      Access : private
// Description : initializes the geomnodes
////////////////////////////////////////////////////////////////////
void SparkleParticleRenderer::
init_geoms() {
  _line_primitive->set_num_prims(0);

  GeomNode *render_node = get_render_node();
  render_node->remove_all_geoms();
  render_node->add_geom(_line_primitive, _render_state);
}

////////////////////////////////////////////////////////////////////
//    Function : render
//      Access : private
// Description : populates the GeomLine
////////////////////////////////////////////////////////////////////
void SparkleParticleRenderer::
render(pvector< PT(PhysicsObject) >& po_vector, int ttl_particles) {

  if (!ttl_particles)
    return;

  BaseParticle *cur_particle;

  int remaining_particles = ttl_particles;
  int i;

  Vertexf *cur_vert = &_vertex_array[0];
  Colorf *cur_color = &_color_array[0];

  // init the aabb

  _aabb_min.set(99999.0f, 99999.0f, 99999.0f);
  _aabb_max.set(-99999.0f, -99999.0f, -99999.0f);

  // run through the array

  for (i = 0; i < (int)po_vector.size(); i++) {
    cur_particle = (BaseParticle *) po_vector[i].p();

    if (cur_particle->get_alive() == false)
      continue;

    // adjust the aabb

    if (cur_particle->get_position().get_x() > _aabb_max.get_x())
      _aabb_max[0] = cur_particle->get_position().get_x();
    else if (cur_particle->get_position().get_x() < _aabb_min.get_x())
      _aabb_min[0] = cur_particle->get_position().get_x();

    if (cur_particle->get_position().get_y() > _aabb_max.get_y())
      _aabb_max[1] = cur_particle->get_position().get_y();
    else if (cur_particle->get_position().get_y() < _aabb_min.get_y())
      _aabb_min[1] = cur_particle->get_position().get_y();

    if (cur_particle->get_position().get_z() > _aabb_max.get_z())
      _aabb_max[2] = cur_particle->get_position().get_z();
    else if (cur_particle->get_position().get_z() < _aabb_min.get_z())
      _aabb_min[2] = cur_particle->get_position().get_z();

    // draw the particle.

    float radius = get_radius(cur_particle);
    float neg_radius = -radius;
    float alpha;

    LPoint3f pos = cur_particle->get_position();
    Colorf center_color = _center_color;
    Colorf edge_color = _edge_color;

    // handle alpha

    if (_alpha_mode != PR_ALPHA_NONE) {
      if(_alpha_mode == PR_ALPHA_USER) {
        alpha = get_user_alpha();
      } else {
        alpha = cur_particle->get_parameterized_age();
        if (_alpha_mode == PR_ALPHA_OUT)
          alpha = 1.0f - alpha;

        alpha *= get_user_alpha();
      }

      center_color[3] = alpha;
      edge_color[3] = alpha;
    }

    // 6 lines coming from the center point.

    *cur_vert++ = pos;
    *cur_vert++ = pos + Vertexf(radius, 0.0f, 0.0f);
    *cur_vert++ = pos;
    *cur_vert++ = pos + Vertexf(neg_radius, 0.0f, 0.0f);
    *cur_vert++ = pos;
    *cur_vert++ = pos + Vertexf(0.0f, radius, 0.0f);
    *cur_vert++ = pos;
    *cur_vert++ = pos + Vertexf(0.0f, neg_radius, 0.0f);
    *cur_vert++ = pos;
    *cur_vert++ = pos + Vertexf(0.0f, 0.0f, radius);
    *cur_vert++ = pos;
    *cur_vert++ = pos + Vertexf(0.0f, 0.0f, neg_radius);

    *cur_color++ = center_color;
    *cur_color++ = edge_color;
    *cur_color++ = center_color;
    *cur_color++ = edge_color;
    *cur_color++ = center_color;
    *cur_color++ = edge_color;
    *cur_color++ = center_color;
    *cur_color++ = edge_color;
    *cur_color++ = center_color;
    *cur_color++ = edge_color;
    *cur_color++ = center_color;
    *cur_color++ = edge_color;

    remaining_particles--;
    if (remaining_particles == 0)
      break;
  }

  _line_primitive->set_num_prims(6 * ttl_particles);

  // done filling geomline node, now do the bb stuff

  LPoint3f aabb_center = _aabb_min + ((_aabb_max - _aabb_min) * 0.5f);
  float radius = (aabb_center - _aabb_min).length();

  _line_primitive->set_bound(BoundingSphere(aabb_center, radius));
  get_render_node()->mark_bound_stale();
}

////////////////////////////////////////////////////////////////////
//     Function : output
//       Access : Public
//  Description : Write a string representation of this instance to
//                <out>.
////////////////////////////////////////////////////////////////////
void SparkleParticleRenderer::
output(ostream &out) const {
  #ifndef NDEBUG //[
  out<<"SparkleParticleRenderer";
  #endif //] NDEBUG
}

////////////////////////////////////////////////////////////////////
//     Function : write
//       Access : Public
//  Description : Write a string representation of this instance to
//                <out>.
////////////////////////////////////////////////////////////////////
void SparkleParticleRenderer::
write(ostream &out, int indent) const {
  #ifndef NDEBUG //[
  out.width(indent); out<<""; out<<"SparkleParticleRenderer:\n";
  out.width(indent+2); out<<""; out<<"_center_color "<<_center_color<<"\n";
  out.width(indent+2); out<<""; out<<"_edge_color "<<_edge_color<<"\n";
  out.width(indent+2); out<<""; out<<"_birth_radius "<<_birth_radius<<"\n";
  out.width(indent+2); out<<""; out<<"_death_radius "<<_death_radius<<"\n";
  out.width(indent+2); out<<""; out<<"_line_primitive "<<_line_primitive<<"\n";
  out.width(indent+2); out<<""; out<<"_vertex_array "<<_vertex_array<<"\n";
  out.width(indent+2); out<<""; out<<"_color_array "<<_color_array<<"\n";
  out.width(indent+2); out<<""; out<<"_max_pool_size "<<_max_pool_size<<"\n";
  out.width(indent+2); out<<""; out<<"_life_scale "<<_life_scale<<"\n";
  out.width(indent+2); out<<""; out<<"_aabb_min "<<_aabb_min<<"\n";
  out.width(indent+2); out<<""; out<<"_aabb_max "<<_aabb_max<<"\n";
  BaseParticleRenderer::write(out, indent+2);
  #endif //] NDEBUG
}
