Panda3D
 All Classes Functions Variables Enumerations
spriteParticleRenderer.cxx
00001 // Filename: spriteParticleRenderer.cxx
00002 // Created by:  charles (13Jul00)
00003 //
00004 ////////////////////////////////////////////////////////////////////
00005 //
00006 // PANDA 3D SOFTWARE
00007 // Copyright (c) Carnegie Mellon University.  All rights reserved.
00008 //
00009 // All use of this software is subject to the terms of the revised BSD
00010 // license.  You should have received a copy of this license along
00011 // with this source code in a file named "LICENSE."
00012 //
00013 ////////////////////////////////////////////////////////////////////
00014 
00015 #include "spriteParticleRenderer.h"
00016 #include "boundingSphere.h"
00017 #include "geomNode.h"
00018 #include "sequenceNode.h"
00019 #include "nodePath.h"
00020 #include "dcast.h"
00021 #include "geom.h"
00022 #include "geomVertexReader.h"
00023 #include "geomVertexWriter.h"
00024 #include "renderModeAttrib.h"
00025 #include "texMatrixAttrib.h"
00026 #include "texGenAttrib.h"
00027 #include "textureAttrib.h"
00028 #include "textureCollection.h"
00029 #include "nodePathCollection.h"
00030 #include "indent.h"
00031 #include "config_particlesystem.h"
00032 #include "pStatTimer.h"
00033 
00034 PStatCollector SpriteParticleRenderer::_render_collector("App:Particles:Sprite:Render");
00035 
00036 ////////////////////////////////////////////////////////////////////
00037 //    Function : SpriteParticleRenderer::SpriteParticleRenderer
00038 //      Access : public
00039 // Description : constructor
00040 ////////////////////////////////////////////////////////////////////
00041 SpriteParticleRenderer::
00042 SpriteParticleRenderer(Texture *tex) :
00043   BaseParticleRenderer(PR_ALPHA_NONE),
00044   _color(LColor(1.0f, 1.0f, 1.0f, 1.0f)),
00045   _height(1.0f),
00046   _width(1.0f),
00047   _initial_x_scale(1.0f),
00048   _final_x_scale(1.0f),
00049   _initial_y_scale(1.0f),
00050   _final_y_scale(1.0f),
00051   _theta(0.0f),
00052   _base_y_scale(1.0f),
00053   _aspect_ratio(1.0f),
00054   _animate_frames_rate(0.0f),
00055   _animate_frames_index(0),
00056   _animate_x_ratio(false),
00057   _animate_y_ratio(false),
00058   _animate_theta(false),
00059   _alpha_disable(false),
00060   _animate_frames(false),
00061   _animation_removed(true),
00062   _blend_method(PP_BLEND_LINEAR),
00063   _color_interpolation_manager(new ColorInterpolationManager(_color)),
00064   _pool_size(0) {
00065   set_texture(tex);
00066   init_geoms();  
00067 }
00068 
00069 ////////////////////////////////////////////////////////////////////
00070 //    Function : SpriteParticleRenderer::SpriteParticleRenderer
00071 //      Access : public
00072 // Description : copy constructor
00073 ////////////////////////////////////////////////////////////////////
00074 SpriteParticleRenderer::
00075 SpriteParticleRenderer(const SpriteParticleRenderer& copy) :
00076   BaseParticleRenderer(copy), 
00077   _anims(copy._anims),
00078   _color(copy._color),
00079   _height(copy._height),
00080   _width(copy._width),
00081   _initial_x_scale(copy._initial_x_scale),
00082   _final_x_scale(copy._final_x_scale),
00083   _initial_y_scale(copy._initial_y_scale),
00084   _final_y_scale(copy._final_y_scale),
00085   _theta(copy._theta),
00086   _base_y_scale(copy._base_y_scale),
00087   _aspect_ratio(copy._aspect_ratio),
00088   _animate_frames_rate(copy._animate_frames_rate),
00089   _animate_frames_index(copy._animate_frames_index),
00090   _animate_x_ratio(copy._animate_x_ratio),
00091   _animate_y_ratio(copy._animate_y_ratio),
00092   _animate_theta(copy._animate_theta),
00093   _alpha_disable(copy._alpha_disable),
00094   _animate_frames(copy._animate_frames),
00095   _animation_removed(true),
00096   _blend_method(copy._blend_method),
00097   _color_interpolation_manager(copy._color_interpolation_manager),
00098   _pool_size(0),
00099   _birth_list(copy._birth_list) {
00100   init_geoms();
00101 }
00102 
00103 ////////////////////////////////////////////////////////////////////
00104 //    Function : SpriteParticleRenderer::~SpriteParticleRenderer
00105 //      Access : public
00106 // Description : destructor
00107 ////////////////////////////////////////////////////////////////////
00108 SpriteParticleRenderer::
00109 ~SpriteParticleRenderer() {
00110   get_render_node()->remove_all_geoms();
00111 }
00112 
00113 ////////////////////////////////////////////////////////////////////
00114 //    Function : SpriteParticleRenderer::make_copy
00115 //      Access : public
00116 // Description : child dynamic copy
00117 ////////////////////////////////////////////////////////////////////
00118 BaseParticleRenderer *SpriteParticleRenderer::
00119 make_copy() {
00120   return new SpriteParticleRenderer(*this);
00121 }
00122 
00123 
00124 ////////////////////////////////////////////////////////////////////
00125 //    Function : SpriteParticleRenderer::extract_textures_from_node
00126 //      Access : public
00127 // Description : Pull either a set of textures from a SequenceNode or
00128 //               a single texture from a GeomNode.  This function is called
00129 //               in both set_from_node() and add_from_node().  Notice the
00130 //               second parameter.  This nodepath will reference the GeomNode
00131 //               holding the first texture in the returned TextureCollection.
00132 ////////////////////////////////////////////////////////////////////
00133 int SpriteParticleRenderer::
00134 extract_textures_from_node(const NodePath &node_path, NodePathCollection &np_col, TextureCollection &tex_col) {
00135   NodePath tex_node_path = node_path;
00136   NodePath geom_node_path;
00137 
00138   // Look for a sequence node first, in case they want animated texture sprites
00139   if (!tex_node_path.is_empty() && tex_node_path.node()->get_type() != SequenceNode::get_class_type()) {
00140     tex_node_path = node_path.find("**/+SequenceNode");
00141   }
00142 
00143   // Nodepath contains a sequence node, attempt to read its textures.
00144   if (!tex_node_path.is_empty()) {
00145     int frame_count = tex_node_path.get_num_children();
00146     // We do it this way in order to preserve the order of the textures in the sequence.
00147     // If we use a find_all_textures() that order is lost.
00148     for (int i = 0; i < frame_count; ++i) {
00149       geom_node_path = tex_node_path.get_child(i);
00150       if (!geom_node_path.is_empty()) {
00151         // Since this is a SequenceNode, there will be only one texture on this geom_node_path.
00152         tex_col.add_textures_from(geom_node_path.find_all_textures());
00153         np_col.add_path(geom_node_path);
00154       }
00155     }
00156     // If unsuccessful, try again as if the node were a normal GeomNode.
00157     if (tex_col.get_num_textures() == 0) {
00158       geom_node_path = NodePath();
00159       tex_col.clear();
00160       np_col.clear();
00161     }
00162   }
00163 
00164   // If a sequence node is not found, we just want to look for a regular geom node.
00165   if (geom_node_path.is_empty()) {  
00166     // Find the first GeomNode.
00167     if (!node_path.is_empty() && node_path.node()->get_type() != GeomNode::get_class_type()) {
00168       geom_node_path = node_path.find("**/+GeomNode");
00169       if (geom_node_path.is_empty()) {
00170         particlesystem_cat.error();
00171         return 0;
00172       }  
00173     } else {
00174       geom_node_path = node_path;
00175     }
00176 
00177     // Grab the first texture.
00178     tex_col.add_texture(geom_node_path.find_texture("*"));
00179     if (tex_col.get_num_textures() < 1) {
00180       particlesystem_cat.error()
00181         << geom_node_path << " does not contain a texture.\n";
00182       return 0;
00183     } else {
00184       np_col.add_path(geom_node_path);
00185     }
00186   }
00187   return 1;
00188 }
00189 
00190 ////////////////////////////////////////////////////////////////////
00191 //    Function : SpriteParticleRenderer::set_from_node
00192 //      Access : public
00193 // Description : If the source type is important, use this one.
00194 //
00195 //               model and node should lead to node_path like this:
00196 //               node_path = loader.loadModel(model).find(node)
00197 //
00198 //               This will remove all previously add textures and 
00199 //               resize the renderer to match the new geometry.
00200 ////////////////////////////////////////////////////////////////////
00201 void SpriteParticleRenderer::
00202 set_from_node(const NodePath &node_path, const string &model, const string &node, bool size_from_texels) {
00203   // Clear all texture information
00204   _anims.clear();
00205   add_from_node(node_path,model,node,size_from_texels,true);
00206 }
00207 
00208 ////////////////////////////////////////////////////////////////////
00209 //    Function : SpriteParticleRenderer::set_from_node
00210 //      Access : public
00211 // Description : Sets the properties on this renderer from the geometry
00212 //               referenced by the indicated NodePath.  This should be
00213 //               a reference to a GeomNode or a SequenceNode; it
00214 //               extracts out the texture and UV range from the node.
00215 //
00216 //               This will remove all previously added textures and
00217 //               animations.  It will also resize the renderer to match
00218 //               this new geometry.
00219 //
00220 //               If node_path refers to a GeomNode(or has one beneath it)
00221 //               the texture, its size, and UV data will be extracted
00222 //               from that.
00223 //
00224 //               If node_path references a SequenceNode(or has one 
00225 //               beneath it) with multiple GeomNodes beneath it, 
00226 //               the size data will correspond only to the first 
00227 //               GeomNode found with a valid texture, while the texture
00228 //               and UV information will be stored for each individual
00229 //               node.
00230 //
00231 //               If size_from_texels is true, the particle size is
00232 //               based on the number of texels in the source image;
00233 //               otherwise, it is based on the size of the first 
00234 //               polygon found in the node.
00235 //
00236 //               model and node are the two items used to construct
00237 //               node_path.  If the source type is important, use
00238 //               set_from_node(NodePath,string,string,bool) instead.
00239 ////////////////////////////////////////////////////////////////////
00240 void SpriteParticleRenderer::
00241 set_from_node(const NodePath &node_path, bool size_from_texels) {
00242   nassertv(!node_path.is_empty());
00243   // Clear all texture information
00244   _anims.clear();
00245   add_from_node(node_path,size_from_texels,true);
00246 }
00247 
00248 ////////////////////////////////////////////////////////////////////
00249 //    Function : SpriteParticleRenderer::add_from_node
00250 //      Access : public
00251 // Description : This will allow the renderer to randomly choose
00252 //               from more than one texture or sequence at particle
00253 //               birth.
00254 //
00255 //               If the source type is important, use this one.
00256 //
00257 //               model and node should lead to node_path like this:
00258 //               node_path = loader.loadModel(model).find(node)
00259 //
00260 //               If resize is true, or if there are no textures 
00261 //               currently on the renderer, it will force the 
00262 //               renderer to use the size information from this 
00263 //               node from now on. (Default is false)
00264 ////////////////////////////////////////////////////////////////////
00265 void SpriteParticleRenderer::
00266 add_from_node(const NodePath &node_path, const string &model, const string &node, bool size_from_texels, bool resize) {
00267   int anim_count = _anims.size();
00268   if (anim_count == 0)
00269     resize = true;
00270   add_from_node(node_path,size_from_texels,resize);
00271   if (anim_count < (int)_anims.size()) {
00272     get_last_anim()->set_source_info(model,node);
00273   }
00274 }
00275 
00276 ////////////////////////////////////////////////////////////////////
00277 //    Function : SpriteParticleRenderer::add_from_node
00278 //      Access : public
00279 // Description : This will allow the renderer to randomly choose
00280 //               from more than one texture or sequence at particle
00281 //               birth.
00282 //
00283 //               If resize is true, or if there are no textures 
00284 //               currently on the renderer, it will force the 
00285 //               renderer to use the size information from this 
00286 //               node from now on. (Default is false)
00287 ////////////////////////////////////////////////////////////////////
00288 void SpriteParticleRenderer::
00289 add_from_node(const NodePath &node_path, bool size_from_texels, bool resize) {
00290   nassertv(!node_path.is_empty());
00291 
00292   NodePathCollection np_col;
00293   TextureCollection tex_col;
00294 
00295   if (_anims.empty())
00296     resize = true;
00297 
00298   // Load the found textures into the renderer.
00299   if (extract_textures_from_node(node_path,np_col,tex_col)) {
00300     pvector< LTexCoord > ll,ur;
00301     GeomNode *gnode = NULL;
00302     const Geom *geom;
00303     const GeomPrimitive *primitive;
00304 
00305     for (int i = 0; i < np_col.get_num_paths(); ++i) {
00306       // Get the node from which we'll extract the geometry information.
00307       gnode = DCAST(GeomNode, np_col[i].node()); 
00308       
00309       // Now examine the UV's of the first Geom within the GeomNode.
00310       nassertv(gnode->get_num_geoms() > 0);
00311       geom = gnode->get_geom(0);
00312       
00313       bool got_texcoord = false;
00314       LTexCoord min_uv(0.0f, 0.0f);
00315       LTexCoord max_uv(0.0f, 0.0f);
00316       
00317       GeomVertexReader texcoord(geom->get_vertex_data(),
00318                                 InternalName::get_texcoord());
00319       if (texcoord.has_column()) {
00320         for (int pi = 0; pi < geom->get_num_primitives(); ++pi) {
00321           primitive = geom->get_primitive(pi);
00322           for (int vi = 0; vi < primitive->get_num_vertices(); ++vi) {
00323             int vert = primitive->get_vertex(vi);
00324             texcoord.set_row_unsafe(vert);
00325             
00326             if (!got_texcoord) {
00327                 min_uv = max_uv = texcoord.get_data2();
00328                 got_texcoord = true;
00329                 
00330             } else {
00331               const LVecBase2 &uv = texcoord.get_data2();
00332 
00333               min_uv[0] = min(min_uv[0], uv[0]);
00334               max_uv[0] = max(max_uv[0], uv[0]);
00335               min_uv[1] = min(min_uv[1], uv[1]);
00336               max_uv[1] = max(max_uv[1], uv[1]);
00337             }
00338           }
00339         }
00340       }
00341         
00342       if (got_texcoord) {
00343         // We don't really pay attention to orientation of UV's here; a
00344         // minor flaw.  We assume the minimum is in the lower-left, and
00345         // the maximum is in the upper-right.
00346         ll.push_back(min_uv);
00347         ur.push_back(max_uv);
00348       }
00349     }
00350     
00351     _anims.push_back(new SpriteAnim(tex_col,ll,ur));
00352 
00353     if (resize) {
00354       gnode = DCAST(GeomNode, np_col[0].node());
00355       geom = gnode->get_geom(0);
00356 
00357       bool got_vertex = false;
00358       LVertex min_xyz(0.0f, 0.0f, 0.0f);
00359       LVertex max_xyz(0.0f, 0.0f, 0.0f);
00360       
00361       GeomVertexReader vertex(geom->get_vertex_data(),
00362                               InternalName::get_vertex());
00363       if (vertex.has_column()) {
00364         for (int pi = 0; pi < geom->get_num_primitives(); ++pi) {
00365           primitive = geom->get_primitive(pi);
00366           for (int vi = 0; vi < primitive->get_num_vertices(); ++vi) {
00367             int vert = primitive->get_vertex(vi);
00368             vertex.set_row_unsafe(vert);
00369             
00370             if (!got_vertex) {
00371               min_xyz = max_xyz = vertex.get_data3();
00372               got_vertex = true;
00373               
00374             } else {
00375               const LVecBase3 &xyz = vertex.get_data3();
00376               
00377               min_xyz[0] = min(min_xyz[0], xyz[0]);
00378               max_xyz[0] = max(max_xyz[0], xyz[0]);
00379               min_xyz[1] = min(min_xyz[1], xyz[1]);
00380               max_xyz[1] = max(max_xyz[1], xyz[1]);
00381               min_xyz[2] = min(min_xyz[2], xyz[2]);
00382               max_xyz[2] = max(max_xyz[2], xyz[2]);
00383             }
00384           }
00385         }
00386       }
00387 
00388       if (got_vertex) {
00389         PN_stdfloat width = max_xyz[0] - min_xyz[0];
00390         PN_stdfloat height = max(max_xyz[1] - min_xyz[1],
00391                            max_xyz[2] - min_xyz[2]);
00392       
00393         if (size_from_texels) {
00394           // If size_from_texels is true, we get the particle size from the
00395           // number of texels in the source image.
00396           PN_stdfloat y_texels = _anims[0]->get_frame(0)->get_y_size() * fabs(_anims[0]->get_ur(0)[1] - _anims[0]->get_ll(0)[1]);
00397           set_size(y_texels * width / height, y_texels);
00398         } else {
00399           // If size_from_texels is false, we get the particle size from
00400           // the size of the polygon.
00401           set_size(width, height);
00402         }
00403         
00404       } else {
00405         // With no vertices, just punt.
00406         set_size(1.0f, 1.0f);
00407       }
00408     }
00409     init_geoms();
00410   }
00411 }
00412 
00413 ////////////////////////////////////////////////////////////////////
00414 //    Function : SpriteParticleRenderer::resize_pool
00415 //      Access : private
00416 // Description : reallocate the vertex pool.
00417 ////////////////////////////////////////////////////////////////////
00418 void SpriteParticleRenderer::
00419 resize_pool(int new_size) {
00420   if (new_size != _pool_size) {
00421     _pool_size = new_size;    
00422     init_geoms();
00423   }
00424 }
00425 
00426 ////////////////////////////////////////////////////////////////////
00427 //    Function : SpriteParticleRenderer::init_geoms
00428 //      Access : public
00429 // Description : initializes everything, called on traumatic events
00430 //               such as construction and serious particlesystem
00431 //               modifications
00432 ////////////////////////////////////////////////////////////////////
00433 void SpriteParticleRenderer::
00434 init_geoms() {
00435   CPT(RenderState) state = _render_state;
00436   SpriteAnim *anim;
00437   int anim_count = _anims.size();
00438   int i,j;
00439 
00440   // Setup format
00441   PT(GeomVertexArrayFormat) array_format = new GeomVertexArrayFormat
00442     (InternalName::get_vertex(), 3, Geom::NT_stdfloat, Geom::C_point,
00443      InternalName::get_color(), 1, Geom::NT_packed_dabc, Geom::C_color);
00444   
00445   if (_animate_theta || _theta != 0.0f) {
00446     array_format->add_column
00447       (InternalName::get_rotate(), 1, Geom::NT_stdfloat, Geom::C_other);
00448   }
00449 
00450   _base_y_scale = _initial_y_scale;
00451   _aspect_ratio = _width / _height;
00452   
00453   PN_stdfloat final_x_scale = _animate_x_ratio ? _final_x_scale : _initial_x_scale;
00454   PN_stdfloat final_y_scale = _animate_y_ratio ? _final_y_scale : _initial_y_scale;
00455   
00456   if (_animate_y_ratio) {
00457     _base_y_scale = max(_initial_y_scale, _final_y_scale);
00458     array_format->add_column
00459       (InternalName::get_size(), 1, Geom::NT_stdfloat, Geom::C_other);
00460   }
00461   
00462   if (_aspect_ratio * _initial_x_scale != _initial_y_scale ||
00463       _aspect_ratio * final_x_scale != final_y_scale) {
00464     array_format->add_column
00465       (InternalName::get_aspect_ratio(), 1, Geom::NT_stdfloat,
00466        Geom::C_other);
00467   }
00468   
00469   CPT(GeomVertexFormat) format = GeomVertexFormat::register_format
00470     (new GeomVertexFormat(array_format));
00471   
00472   // Reset render() data structures
00473   for (i = 0; i < (int)_ttl_count.size(); ++i) {
00474     PANDA_FREE_ARRAY(_ttl_count[i]);
00475   }
00476   _anim_size.resize(anim_count);
00477   _ttl_count.clear();
00478   _ttl_count.resize(anim_count);
00479 
00480   // Reset sprite primitive data in order to prepare for next pass.
00481   _sprite_primitive.clear();
00482   _sprites.clear();
00483   _vdata.clear();
00484   _sprite_writer.clear();
00485 
00486   GeomNode *render_node = get_render_node();
00487   render_node->remove_all_geoms();
00488 
00489   // For each animation...
00490   for (i = 0; i < anim_count; ++i) {
00491     anim = _anims[i];
00492     _anim_size[i] = anim->get_num_frames();    
00493 
00494     _sprite_primitive.push_back(pvector<PT(Geom)>());
00495     _sprites.push_back(pvector<PT(GeomPoints)>());
00496     _vdata.push_back(pvector<PT(GeomVertexData)>());
00497     _sprite_writer.push_back(pvector<SpriteWriter>());
00498 
00499     // For each frame of the animation...
00500     for (j = 0; j < _anim_size[i]; ++j) {
00501       _ttl_count[i] = (int *)PANDA_MALLOC_ARRAY(_anim_size[i] * sizeof(int));
00502       _vdata[i].push_back(new GeomVertexData("sprite_particles", format, Geom::UH_stream));
00503       PT(Geom) geom = new Geom(_vdata[i][j]);
00504       _sprite_primitive[i].push_back((Geom*)geom);
00505       _sprites[i].push_back(new GeomPoints(Geom::UH_stream));
00506       geom->add_primitive(_sprites[i][j]);
00507       
00508       // This will be overwritten in render(), but we had to have some initial value 
00509       _sprite_writer[i].push_back(SpriteWriter());
00510 
00511       state = state->add_attrib(RenderModeAttrib::make(RenderModeAttrib::M_unchanged, _base_y_scale * _height, true));
00512       if (anim->get_frame(j) != (Texture *)NULL) {
00513         state = state->add_attrib(TextureAttrib::make(anim->get_frame(j)));
00514         state = state->add_attrib(TexGenAttrib::make(TextureStage::get_default(), TexGenAttrib::M_point_sprite));
00515         
00516         // Build a transform to convert the texture coordinates to the
00517         // ll, ur space.
00518         LPoint2 ul(anim->get_ll(j)[0], anim->get_ur(j)[1]);
00519         LPoint2 lr(anim->get_ur(j)[0], anim->get_ll(j)[1]);
00520         LVector2 sc = lr - ul;
00521 
00522         CPT(TransformState) ts = TransformState::make_pos_rotate_scale2d(ul, 0.0f, sc);
00523         state = state->add_attrib(TexMatrixAttrib::make(TextureStage::get_default(), ts));
00524       }
00525 
00526       render_node->add_geom(_sprite_primitive[i][j], state);
00527     }
00528   }
00529 
00530   nassertv(render_node->check_valid());
00531 }
00532 
00533 ////////////////////////////////////////////////////////////////////
00534 //    Function : SpriteParticleRenderer::birth_particle
00535 //      Access : private
00536 // Description : child birth, one of those 'there-if-we-want-it'
00537 //               things.  not really too useful here, so it turns
00538 //               out we don't really want it.
00539 ////////////////////////////////////////////////////////////////////
00540 void SpriteParticleRenderer::
00541 birth_particle(int index) {
00542   _birth_list.push_back(index);
00543 }
00544 
00545 ////////////////////////////////////////////////////////////////////
00546 //    Function : SpriteParticleRenderer::kill_particle
00547 //      Access : private
00548 // Description : child death
00549 ////////////////////////////////////////////////////////////////////
00550 void SpriteParticleRenderer::
00551 kill_particle(int) {
00552 }
00553 
00554 ////////////////////////////////////////////////////////////////////
00555 //    Function : SpriteParticleRenderer::render
00556 //      Access : private
00557 // Description : big child render.  populates the geom node.
00558 ////////////////////////////////////////////////////////////////////
00559 void SpriteParticleRenderer::
00560 render(pvector< PT(PhysicsObject) >& po_vector, int ttl_particles) { 
00561   PStatTimer t1(_render_collector);
00562   // There is no texture data available, exit.
00563   if (_anims.empty()) {
00564     return;
00565   }
00566   
00567   BaseParticle *cur_particle;
00568   int remaining_particles = ttl_particles;
00569   int i,j;                                  // loop counters
00570   int anim_count = _anims.size();           // number of animations
00571   int frame;                                // frame index, used in indicating which frame to use when not animated
00572   // First, since this is the only time we have access to the actual particles, do some delayed initialization.
00573   if (_animate_frames || anim_count) {
00574     if (!_birth_list.empty()) {
00575       for (vector_int::iterator vIter = _birth_list.begin(); vIter != _birth_list.end(); ++vIter) {
00576         cur_particle = (BaseParticle*)po_vector[*vIter].p();
00577         i = int(NORMALIZED_RAND()*anim_count);
00578         
00579         // If there are multiple animations to choose from, choose one at random for this new particle
00580         cur_particle->set_index(i < anim_count?i:i-1);
00581         
00582         // This is an experimental age offset so that the animations don't appear synchronized.
00583         // If we are using animations, try to vary the frame flipping a bit for particles in the same litter.
00584         // A similar effect might be a achieved by using a small lifespan spread value on the factory.
00585 
00586         // Perhaps we should look into other methods. The age offset doesn't seem to be cutting it.
00587         if (_animate_frames) {
00588           cur_particle->set_age(cur_particle->get_age()+i/10.0*cur_particle->get_lifespan());
00589         }
00590       }
00591     }
00592   }
00593   _birth_list.clear();
00594   
00595   // Create vertex writers for each of the possible geoms.
00596   // Could possibly be changed to only create writers for geoms that would be used 
00597   //    according to the animation configuration.
00598   for (i = 0; i < anim_count; ++i) {
00599     for (j = 0; j < _anim_size[i]; ++j) {
00600       // Set the particle per frame counts to 0.
00601       memset(_ttl_count[i], 0, _anim_size[i]*sizeof(int));
00602       _sprite_writer[i][j].vertex = GeomVertexWriter(_vdata[i][j], InternalName::get_vertex());
00603       _sprite_writer[i][j].color = GeomVertexWriter(_vdata[i][j], InternalName::get_color());
00604       _sprite_writer[i][j].rotate = GeomVertexWriter(_vdata[i][j], InternalName::get_rotate());
00605       _sprite_writer[i][j].size = GeomVertexWriter(_vdata[i][j], InternalName::get_size());
00606       _sprite_writer[i][j].aspect_ratio = GeomVertexWriter(_vdata[i][j], InternalName::get_aspect_ratio());
00607     }
00608   }
00609 
00610   // init the aabb
00611   _aabb_min.set(99999.0f, 99999.0f, 99999.0f);
00612   _aabb_max.set(-99999.0f, -99999.0f, -99999.0f);
00613 
00614   // run through every filled slot
00615   for (i = 0; i < (int)po_vector.size(); i++) {
00616     cur_particle = (BaseParticle *) po_vector[i].p();
00617 
00618     if (!cur_particle->get_alive()) {
00619       continue;
00620     }
00621 
00622     LPoint3 position = cur_particle->get_position();
00623 
00624     // x aabb adjust
00625     if (position[0] > _aabb_max[0])
00626       _aabb_max[0] = position[0];
00627     else if (position[0] < _aabb_min[0])
00628       _aabb_min[0] = position[0];
00629 
00630     // y aabb adjust
00631     if (position[1] > _aabb_max[1])
00632       _aabb_max[1] = position[1];
00633     else if (position[1] < _aabb_min[1])
00634       _aabb_min[1] = position[1];
00635 
00636     // z aabb adjust
00637     if (position[2] > _aabb_max[2])
00638       _aabb_max[2] = position[2];
00639     else if (position[2] < _aabb_min[2])
00640       _aabb_min[2] = position[2];
00641 
00642 
00643     PN_stdfloat t = cur_particle->get_parameterized_age();
00644     int anim_index = cur_particle->get_index();
00645 
00646     // If an animation has been removed, we need to reassign
00647     // those particles assigned to the removed animation.
00648     if(_animation_removed && (anim_index >= anim_count)) {
00649       anim_index = int(NORMALIZED_RAND()*anim_count);
00650       anim_index = anim_index<anim_count?anim_index:anim_index-1;
00651       cur_particle->set_index(anim_index);
00652     }
00653 
00654     // Find the frame
00655     if (_animate_frames) {
00656       if (_animate_frames_rate == 0.0f) {
00657         frame = (int)(t*_anim_size[anim_index]);
00658       } else {
00659         frame = (int)fmod(cur_particle->get_age()*_animate_frames_rate+1,_anim_size[anim_index]);
00660       }
00661     } else {
00662       frame = _animate_frames_index;
00663     }
00664 
00665     // Quick check make sure our math above didn't result in an invalid frame.
00666     frame = (frame < _anim_size[anim_index]) ? frame : (_anim_size[anim_index]-1);
00667     ++_ttl_count[anim_index][frame];
00668 
00669     // Calculate the color
00670     // This is where we'll want to give the renderer the new color
00671     LColor c = _color_interpolation_manager->generateColor(t);
00672 
00673     int alphamode=get_alpha_mode();
00674     if (alphamode != PR_ALPHA_NONE) {
00675       if (alphamode == PR_ALPHA_OUT)
00676         c[3] *= (1.0f - t) * get_user_alpha();
00677       else if (alphamode == PR_ALPHA_IN)
00678         c[3] *= t * get_user_alpha();
00679       else if (alphamode == PR_ALPHA_IN_OUT) {
00680         c[3] *= 2.0f * min(t, 1.0f - t) * get_user_alpha();
00681       }
00682       else {
00683         assert(alphamode == PR_ALPHA_USER);
00684         c[3] *= get_user_alpha();
00685       }
00686     }
00687           
00688     // Send the data on its way...
00689     _sprite_writer[anim_index][frame].vertex.add_data3(position);
00690     _sprite_writer[anim_index][frame].color.add_data4(c);
00691     
00692     PN_stdfloat current_x_scale = _initial_x_scale;
00693     PN_stdfloat current_y_scale = _initial_y_scale;
00694     
00695     if (_animate_x_ratio || _animate_y_ratio) {
00696       if (_blend_method == PP_BLEND_CUBIC) {
00697         t = CUBIC_T(t);
00698       }
00699       
00700       if (_animate_x_ratio) {
00701         current_x_scale = (_initial_x_scale +
00702                            (t * (_final_x_scale - _initial_x_scale)));
00703       }
00704       if (_animate_y_ratio) {
00705         current_y_scale = (_initial_y_scale +
00706                            (t * (_final_y_scale - _initial_y_scale)));
00707       }
00708     }
00709 
00710     if (_sprite_writer[anim_index][frame].size.has_column()) {
00711       _sprite_writer[anim_index][frame].size.add_data1f(current_y_scale * _height);
00712     }
00713     if (_sprite_writer[anim_index][frame].aspect_ratio.has_column()) {
00714       _sprite_writer[anim_index][frame].aspect_ratio.add_data1f(_aspect_ratio * current_x_scale / current_y_scale);
00715     }
00716     if (_animate_theta) {
00717       _sprite_writer[anim_index][frame].rotate.add_data1f(cur_particle->get_theta());
00718     } else if (_sprite_writer[anim_index][frame].rotate.has_column()) {
00719       _sprite_writer[anim_index][frame].rotate.add_data1f(_theta);
00720     }
00721 
00722     // maybe jump out early?
00723     remaining_particles--;
00724     if (remaining_particles == 0) {
00725       break;
00726     }
00727   }
00728   int n = 0;
00729   GeomNode *render_node = get_render_node();
00730   
00731   for (i = 0; i < anim_count; ++i) {
00732     for (j = 0; j < _anim_size[i]; ++j) {
00733       _sprites[i][j]->clear_vertices();
00734       _sprite_writer[i][j].clear();      
00735 
00736       // We have to reassign the GeomVertexData and GeomPrimitive to
00737       // the Geom, and the Geom to the GeomNode, in case it got
00738       // flattened away.
00739       _sprite_primitive[i][j]->set_primitive(0, _sprites[i][j]);
00740       _sprite_primitive[i][j]->set_vertex_data(_vdata[i][j]);
00741 
00742       render_node->set_geom(n, _sprite_primitive[i][j]);
00743       ++n;
00744     }
00745   }
00746 
00747   if (_animate_frames) {
00748     for (i = 0; i < anim_count; ++i) {
00749       for (j = 0; j < _anim_size[i]; ++j) {
00750         _sprites[i][j]->add_next_vertices(_ttl_count[i][j]);
00751       }
00752     }
00753   } else {
00754     for (i = 0; i < anim_count; ++i) {
00755       _sprites[i][_animate_frames_index]->add_next_vertices(_ttl_count[i][_animate_frames_index]);
00756     }
00757   }
00758 
00759   // done filling geompoint node, now do the bb stuff
00760   LPoint3 aabb_center = _aabb_min + ((_aabb_max - _aabb_min) * 0.5f);
00761   PN_stdfloat radius = (aabb_center - _aabb_min).length();
00762 
00763   for (i = 0; i < anim_count; ++i) {
00764     for (j = 0; j < _anim_size[i]; ++j) {
00765       nassertv(_sprite_primitive[i][j]->check_valid());
00766       BoundingSphere sphere(aabb_center, radius);
00767       _sprite_primitive[i][j]->set_bounds(&sphere);
00768     }
00769   }
00770 
00771   get_render_node()->mark_internal_bounds_stale();
00772   nassertv(render_node->check_valid());
00773   _animation_removed = false;
00774 }
00775 
00776 ////////////////////////////////////////////////////////////////////
00777 //     Function : output
00778 //       Access : Public
00779 //  Description : Write a string representation of this instance to
00780 //                <out>.
00781 ////////////////////////////////////////////////////////////////////
00782 void SpriteParticleRenderer::
00783 output(ostream &out) const {
00784   #ifndef NDEBUG //[
00785   out<<"SpriteParticleRenderer";
00786   #endif //] NDEBUG
00787 }
00788 
00789 ////////////////////////////////////////////////////////////////////
00790 //     Function : write
00791 //       Access : Public
00792 //  Description : Write a string representation of this instance to
00793 //                <out>.
00794 ////////////////////////////////////////////////////////////////////
00795 void SpriteParticleRenderer::
00796 write(ostream &out, int indent_level) const {
00797   indent(out, indent_level) << "SpriteParticleRenderer:\n";
00798   //  indent(out, indent_level + 2) << "_sprite_primitive "<<_sprite_primitive<<"\n";
00799   indent(out, indent_level + 2) << "_color "<<_color<<"\n";
00800   indent(out, indent_level + 2) << "_initial_x_scale "<<_initial_x_scale<<"\n";
00801   indent(out, indent_level + 2) << "_final_x_scale "<<_final_x_scale<<"\n";
00802   indent(out, indent_level + 2) << "_initial_y_scale "<<_initial_y_scale<<"\n";
00803   indent(out, indent_level + 2) << "_final_y_scale "<<_final_y_scale<<"\n";
00804   indent(out, indent_level + 2) << "_theta "<<_theta<<"\n";
00805   indent(out, indent_level + 2) << "_animate_x_ratio "<<_animate_x_ratio<<"\n";
00806   indent(out, indent_level + 2) << "_animate_y_ratio "<<_animate_y_ratio<<"\n";
00807   indent(out, indent_level + 2) << "_animate_theta "<<_animate_theta<<"\n";
00808   indent(out, indent_level + 2) << "_blend_method "<<_blend_method<<"\n";
00809   indent(out, indent_level + 2) << "_aabb_min "<<_aabb_min<<"\n";
00810   indent(out, indent_level + 2) << "_aabb_max "<<_aabb_max<<"\n";
00811   indent(out, indent_level + 2) << "_pool_size "<<_pool_size<<"\n";
00812   BaseParticleRenderer::write(out, indent_level + 2);
00813 }
 All Classes Functions Variables Enumerations