diff --git a/panda/src/particlesystem/spriteParticleRenderer.I b/panda/src/particlesystem/spriteParticleRenderer.I index d296e90b2c..42393ad3b3 100644 --- a/panda/src/particlesystem/spriteParticleRenderer.I +++ b/panda/src/particlesystem/spriteParticleRenderer.I @@ -16,17 +16,6 @@ // //////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////// -// Function : SpriteParticleRenderer::get_source_type -// Access : public -// Description : Returns an indication of whether the texture for this -// renderer was set via a call to set_texture(), or via -// set_from_node(). -//////////////////////////////////////////////////////////////////// -INLINE SpriteParticleRenderer::SourceType SpriteParticleRenderer:: -get_source_type() const { - return _source_type; -} //////////////////////////////////////////////////////////////////// // Function : SpriteParticleRenderer::set_texture @@ -35,20 +24,83 @@ get_source_type() const { // image. The scale of each particle is based on the // size of the texture in each dimension, modified by // texels_per_unit. +// +// Used to set the size of the particles. Will clear +// all previously loaded textures and animations. //////////////////////////////////////////////////////////////////// INLINE void SpriteParticleRenderer:: -set_texture(Texture *tex, float texels_per_unit) { - _texture = tex; - set_ll_uv(TexCoordf(0.0f, 0.0f)); - set_ur_uv(TexCoordf(1.0f, 1.0f)); - _source_type = ST_texture; +set_texture(Texture *tex, const string &tex_path, float texels_per_unit) { + set_texture(tex,texels_per_unit); + get_last_anim()->set_source_info(tex_path); +} - // We scale the particle size by the size of the texture. - if (_texture != (Texture *)NULL) { - set_size(_texture->get_x_size() / texels_per_unit, - _texture->get_y_size() / texels_per_unit); +INLINE void SpriteParticleRenderer:: +set_texture(Texture *tex, float texels_per_unit) { + if (tex != (Texture *)NULL) { + // Clear all texture information + _anims.clear(); + + // Insert the single texture + _anims.push_back(new SpriteAnim(tex,TexCoordf(0.0f, 0.0f),TexCoordf(1.0f, 1.0f))); + + // We scale the particle size by the size of the texture. + set_size(tex->get_x_size() / texels_per_unit, + tex->get_y_size() / texels_per_unit); + } + init_geoms(); +} + +//////////////////////////////////////////////////////////////////// +// Function : SpriteParticleRenderer::add_texture +// Access : Published +// Description : Adds texture to image pool, effectively creating a +// single frame animation that can be selected at +// particle birth. This should only be called after +// a previous call to set_texture(). +//////////////////////////////////////////////////////////////////// +INLINE void SpriteParticleRenderer:: +add_texture(Texture *tex, const string &tex_path, float texels_per_unit, bool resize) { + add_texture(tex,texels_per_unit,resize); + get_last_anim()->set_source_info(tex_path); +} + +INLINE void SpriteParticleRenderer:: +add_texture(Texture *tex, float texels_per_unit, bool resize) { + if (_anims.size() == 0) { + set_texture(tex, texels_per_unit); + } else { + if(tex != (Texture *)NULL) { + if (tex != (Texture *)NULL) { + _anims.push_back(new SpriteAnim(tex,TexCoordf(0.0f, 0.0f),TexCoordf(1.0f, 1.0f))); + } + if(resize) { + // We scale the particle size by the size of the texture. + set_size(tex->get_x_size() / texels_per_unit, + tex->get_y_size() / texels_per_unit); + } + init_geoms(); + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function : SpriteParticleRenderer::remove_animation +// Access : Published +// Description : Removes an animation texture set from the renderer. +//////////////////////////////////////////////////////////////////// +INLINE void SpriteParticleRenderer:: +remove_animation(const int n) { + nassertv(n < (int)_anims.size()); + int i,j; + + for (i = 0; i < (int)_anims.size(); ++i) { + for (j = 0; j < (int)_anim_size[i]; ++j) { + _sprites[i][j]->clear_vertices(); + } } + _anims.erase(_anims.begin()+n); + _animation_removed = true; init_geoms(); } @@ -62,7 +114,22 @@ set_texture(Texture *tex, float texels_per_unit) { //////////////////////////////////////////////////////////////////// INLINE void SpriteParticleRenderer:: set_ll_uv(const TexCoordf &ll_uv) { - _ll_uv = ll_uv; + set_ll_uv(ll_uv,0,0); +} + +//////////////////////////////////////////////////////////////////// +// Function : SpriteParticleRenderer::set_ll_uv +// Access : public +// Description : Sets the UV coordinate of the lower-left corner of +// all the sprites generated by this renderer. Normally +// this is (0, 0), but it might be set to something else +// to use only a portion of the texture. +//////////////////////////////////////////////////////////////////// +INLINE void SpriteParticleRenderer:: +set_ll_uv(const TexCoordf &ll_uv, const int anim, const int frame) { + if(anim < (int)_anims.size() && frame < (int)_anims[anim]->get_num_frames()) { + _anims[anim]->set_ll(frame,ll_uv); + } } //////////////////////////////////////////////////////////////////// @@ -75,7 +142,22 @@ set_ll_uv(const TexCoordf &ll_uv) { //////////////////////////////////////////////////////////////////// INLINE void SpriteParticleRenderer:: set_ur_uv(const TexCoordf &ur_uv) { - _ur_uv = ur_uv; + set_ur_uv(ur_uv,0,0); +} + +//////////////////////////////////////////////////////////////////// +// Function : SpriteParticleRenderer::set_ur_uv +// Access : public +// Description : Sets the UV coordinate of the upper-right corner of +// all the sprites generated by this renderer. Normally +// this is (1, 1), but it might be set to something else +// to use only a portion of the texture. +//////////////////////////////////////////////////////////////////// +INLINE void SpriteParticleRenderer:: +set_ur_uv(const TexCoordf &ur_uv, const int anim, const int frame) { + if(anim < (int)_anims.size() && frame < (int)_anims[anim]->get_num_frames()) { + _anims[anim]->set_ur(frame,ur_uv); + } } //////////////////////////////////////////////////////////////////// @@ -195,13 +277,77 @@ set_alpha_disable(bool ad) { _alpha_disable = ad; } +//////////////////////////////////////////////////////////////////// +// Function : SpriteParticleRenderer::set_animate_frames_enable +// Access : public +//////////////////////////////////////////////////////////////////// +INLINE void SpriteParticleRenderer:: +set_animate_frames_enable(bool an) { + _animate_frames = an; +} + +//////////////////////////////////////////////////////////////////// +// Function : SpriteParticleRenderer::set_animate_frames_rate +// Access : public +//////////////////////////////////////////////////////////////////// +INLINE void SpriteParticleRenderer:: +set_animate_frames_rate(float r) { + nassertv( r >= 0.0); + _animate_frames_rate = r; +} + +//////////////////////////////////////////////////////////////////// +// Function : SpriteParticleRenderer::set_animate_frames_index +// Access : public +// Purpose : Sets the frame to be used when animation is disabled. +//////////////////////////////////////////////////////////////////// +INLINE void SpriteParticleRenderer:: +set_animate_frames_index(int i) { + nassertv(i < (int)_anims[0]->get_num_frames()); + _animate_frames_index = i; +} + //////////////////////////////////////////////////////////////////// // Function : SpriteParticleRenderer::get_texture // Access : public //////////////////////////////////////////////////////////////////// INLINE Texture *SpriteParticleRenderer:: get_texture() const { - return _texture; + return get_texture(0,0); +} + +//////////////////////////////////////////////////////////////////// +// Function : SpriteParticleRenderer::get_texture +// Access : public +//////////////////////////////////////////////////////////////////// +INLINE Texture *SpriteParticleRenderer:: +get_texture(const int anim, const int frame) const { + if(_anims.size() == 0) { + return (Texture*)NULL; + } + nassertr(anim < (int)_anims.size() && anim >= 0, (Texture*)NULL); + nassertr(frame < (int)_anims[anim]->get_num_frames() && frame >= 0,_anims[anim]->get_frame(0)); + return _anims[anim]->get_frame(frame); +} + +INLINE int SpriteParticleRenderer:: +get_num_anims() const { + return _anims.size(); +} + +INLINE SpriteAnim *SpriteParticleRenderer:: +get_anim(const int n) const { + nassertr(n < (int)_anims.size(), (SpriteAnim*)NULL); + return _anims[n]; +} + +INLINE SpriteAnim *SpriteParticleRenderer:: +get_last_anim() const { + if (_anims.size()) { + return *(_anims.end()-1); + } else { + return (SpriteAnim *)NULL; + } } //////////////////////////////////////////////////////////////////// @@ -210,9 +356,33 @@ get_texture() const { // Description : Returns the UV coordinate of the lower-left corner; // see set_ll_uv(). //////////////////////////////////////////////////////////////////// -INLINE const TexCoordf &SpriteParticleRenderer:: +INLINE TexCoordf SpriteParticleRenderer:: get_ll_uv() const { - return _ll_uv; + return get_ll_uv(0,0); +} + +//////////////////////////////////////////////////////////////////// +// Function : SpriteParticleRenderer::get_ll_uv +// Access : public +// Description : Returns the UV coordinate of the lower-left corner; +// see set_ll_uv(). +//////////////////////////////////////////////////////////////////// +INLINE TexCoordf SpriteParticleRenderer:: +get_ll_uv(const int anim, const int frame) const { + int a = anim < (int)_anims.size()?anim:0; + int f = frame < (int)_anims[a]->get_num_frames()?frame:0; + return _anims[a]->get_ll(f); +} + +//////////////////////////////////////////////////////////////////// +// Function : SpriteParticleRenderer::get_ur_uv +// Access : public +// Description : Returns the UV coordinate of the lower-left corner; +// see set_ur_uv(). +//////////////////////////////////////////////////////////////////// +INLINE TexCoordf SpriteParticleRenderer:: +get_ur_uv() const { + return get_ur_uv(0,0); } //////////////////////////////////////////////////////////////////// @@ -221,9 +391,11 @@ get_ll_uv() const { // Description : Returns the UV coordinate of the upper-right corner; // see set_ur_uv(). //////////////////////////////////////////////////////////////////// -INLINE const TexCoordf &SpriteParticleRenderer:: -get_ur_uv() const { - return _ur_uv; +INLINE TexCoordf SpriteParticleRenderer:: +get_ur_uv(const int anim, const int frame) const { + int a = anim < (int)_anims.size()?anim:0; + int f = frame < (int)_anims[a]->get_num_frames()?frame:0; + return _anims[a]->get_ur(f); } //////////////////////////////////////////////////////////////////// @@ -345,6 +517,34 @@ get_alpha_disable() const { return _alpha_disable; } +//////////////////////////////////////////////////////////////////// +// Function : SpriteParticleRenderer::get_animate_frames_enable +// Access : public +//////////////////////////////////////////////////////////////////// +INLINE bool SpriteParticleRenderer:: +get_animate_frames_enable() const { + return _animate_frames; +} + +//////////////////////////////////////////////////////////////////// +// Function : SpriteParticleRenderer::get_animate_frames_rate +// Access : public +//////////////////////////////////////////////////////////////////// +INLINE float SpriteParticleRenderer:: +get_animate_frames_rate() const { + return _animate_frames_rate; +} + +//////////////////////////////////////////////////////////////////// +// Function : SpriteParticleRenderer::get_animate_frames_index +// Access : public +// Purpose : Gets the frame to be used when animation is disabled. +//////////////////////////////////////////////////////////////////// +INLINE int SpriteParticleRenderer:: +get_animate_frames_index() const { + return _animate_frames_index; +} + //////////////////////////////////////////////////////////////////// // Function : SpriteParticleRenderer::get_color_interpolation_manager // Access : public diff --git a/panda/src/particlesystem/spriteParticleRenderer.cxx b/panda/src/particlesystem/spriteParticleRenderer.cxx index 345f5fa9b0..44a04cf7f3 100644 --- a/panda/src/particlesystem/spriteParticleRenderer.cxx +++ b/panda/src/particlesystem/spriteParticleRenderer.cxx @@ -19,6 +19,7 @@ #include "spriteParticleRenderer.h" #include "boundingSphere.h" #include "geomNode.h" +#include "sequenceNode.h" #include "nodePath.h" #include "dcast.h" #include "geom.h" @@ -28,6 +29,8 @@ #include "texMatrixAttrib.h" #include "texGenAttrib.h" #include "textureAttrib.h" +#include "textureCollection.h" +#include "nodePathCollection.h" #include "indent.h" //////////////////////////////////////////////////////////////////// @@ -39,8 +42,6 @@ SpriteParticleRenderer:: SpriteParticleRenderer(Texture *tex) : BaseParticleRenderer(PR_ALPHA_NONE), _color(Colorf(1.0f, 1.0f, 1.0f, 1.0f)), - _ll_uv(0.0f, 0.0f), - _ur_uv(1.0f, 1.0f), _height(1.0f), _width(1.0f), _initial_x_scale(0.02f), @@ -50,15 +51,18 @@ SpriteParticleRenderer(Texture *tex) : _theta(0.0f), _base_y_scale(1.0f), _aspect_ratio(1.0f), + _animate_frames_rate(0.0f), + _animate_frames_index(0), _animate_x_ratio(false), _animate_y_ratio(false), _animate_theta(false), _alpha_disable(false), + _animate_frames(false), + _animation_removed(true), _blend_method(PP_BLEND_LINEAR), _color_interpolation_manager(new ColorInterpolationManager(_color)), - _pool_size(0), - _source_type(ST_texture) -{ + _pool_size(0) { + set_texture(tex); init_geoms(); } @@ -69,21 +73,30 @@ SpriteParticleRenderer(Texture *tex) : //////////////////////////////////////////////////////////////////// SpriteParticleRenderer:: SpriteParticleRenderer(const SpriteParticleRenderer& copy) : - BaseParticleRenderer(copy), _pool_size(0) { - _texture = copy._texture; - _animate_x_ratio = copy._animate_x_ratio; - _animate_y_ratio = copy._animate_y_ratio; - _animate_theta = copy._animate_theta; - _alpha_disable = copy._alpha_disable; - _blend_method = copy._blend_method; - _ll_uv = copy._ll_uv; - _ur_uv = copy._ur_uv; - _initial_x_scale = copy._initial_x_scale; - _final_x_scale = copy._final_x_scale; - _initial_y_scale = copy._initial_y_scale; - _final_y_scale = copy._final_y_scale; - _theta = copy._theta; - _color = copy._color; + BaseParticleRenderer(copy), + _color(copy._color), + _height(copy._height), + _width(copy._width), + _initial_x_scale(copy._initial_x_scale), + _final_x_scale(copy._final_x_scale), + _initial_y_scale(copy._initial_y_scale), + _final_y_scale(copy._final_y_scale), + _theta(copy._theta), + _base_y_scale(copy._base_y_scale), + _aspect_ratio(copy._aspect_ratio), + _animate_frames_rate(copy._animate_frames_rate), + _animate_frames_index(copy._animate_frames_index), + _animate_x_ratio(copy._animate_x_ratio), + _animate_y_ratio(copy._animate_y_ratio), + _animate_theta(copy._animate_theta), + _alpha_disable(copy._alpha_disable), + _animate_frames(copy._animate_frames), + _animation_removed(true), + _blend_method(copy._blend_method), + _color_interpolation_manager(copy._color_interpolation_manager), + _pool_size(0), + _anims(copy._anims), + _birth_list(copy._birth_list) { init_geoms(); } @@ -94,6 +107,7 @@ SpriteParticleRenderer(const SpriteParticleRenderer& copy) : //////////////////////////////////////////////////////////////////// SpriteParticleRenderer:: ~SpriteParticleRenderer() { + get_render_node()->remove_all_geoms(); } //////////////////////////////////////////////////////////////////// @@ -106,11 +120,230 @@ make_copy() { return new SpriteParticleRenderer(*this); } + +//////////////////////////////////////////////////////////////////// +// Function : SpriteParticleRenderer::extract_textures_from_node +// Access : public +// Description : Pull either a set of textures from a SequenceNode or +// a single texture from a GeomNode. This function is called +// in both set_from_node() and add_from_node(). Notice the +// second parameter. This nodepath will reference the GeomNode +// holding the first texture in the returned TextureCollection. +//////////////////////////////////////////////////////////////////// +int SpriteParticleRenderer:: +extract_textures_from_node(const NodePath &node_path, NodePathCollection &np_col, TextureCollection &tex_col) { + NodePath tex_node_path = node_path, geom_node_path; + + // Look for a sequence node first, in case they want animated texture sprites + if (tex_node_path.node()->get_type() != SequenceNode::get_class_type()) { + tex_node_path = node_path.find("**/+SequenceNode"); + } + + // Nodepath contains a sequence node, attempt to read its textures. + if (!tex_node_path.is_empty()) { + int frame_count = tex_node_path.get_num_children(); + // We do it this way in order to preserve the order of the textures in the sequence. + // If we use a find_all_textures() that order is lost. + for (int i = 0; i < frame_count; ++i) { + geom_node_path = tex_node_path.get_child(i); + if (!geom_node_path.is_empty()) { + // Since this is a SequenceNode, there will be only one texture on this geom_node_path. + tex_col.add_textures_from(geom_node_path.find_all_textures()); + np_col.add_path(geom_node_path); + } + } + // If unsuccessful, try again as if the node were a normal GeomNode. + if (tex_col.get_num_textures() == 0) { + geom_node_path = NodePath(); + tex_col.clear(); + np_col.clear(); + } + } + + // If a sequence node is not found, we just want to look for a regular geom node. + if (geom_node_path.is_empty()) { + // Find the first GeomNode. + if (node_path.node()->get_type() != GeomNode::get_class_type()) { + geom_node_path = node_path.find("**/+GeomNode"); + if (geom_node_path.is_empty()) { + particlesystem_cat.error(); + return 0; + } + } else { + geom_node_path = node_path; + } + + // Grab the first texture. + tex_col.add_texture(geom_node_path.find_texture("*")); + if (tex_col.get_num_textures() < 1) { + particlesystem_cat.error() + << geom_node_path << " does not contain a texture.\n"; + return 0; + } else { + np_col.add_path(geom_node_path); + } + } + return 1; +} + //////////////////////////////////////////////////////////////////// // Function : SpriteParticleRenderer::set_from_node // Access : public // Description : Sets the properties on this render from the geometry // referenced by the indicated NodePath. This should be +// a reference to a GeomNode or a SequenceNode; it +// extracts out the texture and UV range from the node. +// +// If node_path references a SequenceNode with multiple +// GeomNodes beneath it, the size data will correspond +// to the first GeomNode found with a valid texture, and +// the texture and UV information will be stored for each +// individual node. +// +// If size_from_texels is true, the particle size is +// based on the number of texels in the source image; +// otherwise, it is based on the size of the first +// polygon found in the node. +//////////////////////////////////////////////////////////////////// +void SpriteParticleRenderer:: +set_from_node(const NodePath &node_path, const string &model, const string &node, bool size_from_texels) { + set_from_node(node_path,size_from_texels); + get_last_anim()->set_source_info(model,node); +} + +void SpriteParticleRenderer:: +set_from_node(const NodePath &node_path, bool size_from_texels) { + nassertv(!node_path.is_empty()); + + NodePathCollection np_col; + TextureCollection tex_col; + pvector< TexCoordf > ll,ur; + GeomNode *gnode = NULL; + const Geom *geom; + const GeomPrimitive *primitive; + bool got_texcoord,got_vertex; + + // Clear all texture information + _anims.clear(); + + // Load the found textures into the renderer. + if (extract_textures_from_node(node_path,np_col,tex_col)) { + for (int i = 0; i < np_col.get_num_paths(); ++i) { + // Get the node from which we'll extract the geometry information. + gnode = DCAST(GeomNode, np_col[i].node()); + + // Now examine the UV's of the first Geom within the GeomNode. + nassertv(gnode->get_num_geoms() > 0); + geom = gnode->get_geom(0); + + got_texcoord = false; + TexCoordf min_uv(0.0f, 0.0f); + TexCoordf max_uv(0.0f, 0.0f); + + GeomVertexReader texcoord(geom->get_vertex_data(), + InternalName::get_texcoord()); + if (texcoord.has_column()) { + for (int pi = 0; pi < geom->get_num_primitives(); ++pi) { + primitive = geom->get_primitive(pi); + for (int vi = 0; vi < primitive->get_num_vertices(); ++vi) { + int vert = primitive->get_vertex(vi); + texcoord.set_row(vert); + + if (!got_texcoord) { + min_uv = max_uv = texcoord.get_data2f(); + got_texcoord = true; + + } else { + const LVecBase2f &uv = texcoord.get_data2f(); + + min_uv[0] = min(min_uv[0], uv[0]); + max_uv[0] = max(max_uv[0], uv[0]); + min_uv[1] = min(min_uv[1], uv[1]); + max_uv[1] = max(max_uv[1], uv[1]); + } + } + } + } + + if (got_texcoord) { + // We don't really pay attention to orientation of UV's here; a + // minor flaw. We assume the minimum is in the lower-left, and + // the maximum is in the upper-right. + ll.push_back(min_uv); + ur.push_back(max_uv); + // set_ll_uv(min_uv); + // set_ur_uv(max_uv); + } + } + + _anims.push_back(new SpriteAnim(tex_col,ll,ur)); + + gnode = DCAST(GeomNode, np_col[0].node()); + geom = gnode->get_geom(0); + + got_vertex = false; + Vertexf min_xyz(0.0f, 0.0f, 0.0f); + Vertexf max_xyz(0.0f, 0.0f, 0.0f); + + GeomVertexReader vertex(geom->get_vertex_data(), + InternalName::get_vertex()); + if (vertex.has_column()) { + for (int pi = 0; pi < geom->get_num_primitives(); ++pi) { + const GeomPrimitive *primitive = geom->get_primitive(pi); + for (int vi = 0; vi < primitive->get_num_vertices(); ++vi) { + int vert = primitive->get_vertex(vi); + vertex.set_row(vert); + + if (!got_vertex) { + min_xyz = max_xyz = vertex.get_data3f(); + got_vertex = true; + + } else { + const LVecBase3f &xyz = vertex.get_data3f(); + + min_xyz[0] = min(min_xyz[0], xyz[0]); + max_xyz[0] = max(max_xyz[0], xyz[0]); + min_xyz[1] = min(min_xyz[1], xyz[1]); + max_xyz[1] = max(max_xyz[1], xyz[1]); + min_xyz[2] = min(min_xyz[2], xyz[2]); + max_xyz[2] = max(max_xyz[2], xyz[2]); + } + } + } + } + + if (got_vertex) { + float width = max_xyz[0] - min_xyz[0]; + float height = max(max_xyz[1] - min_xyz[1], + max_xyz[2] - min_xyz[2]); + + if (size_from_texels && got_texcoord) { + // If size_from_texels is true, we get the particle size from the + // number of texels in the source image. + float y_texels = _anims[0]->get_frame(0)->get_y_size() * fabs(_anims[0]->get_ur(0)[1] - _anims[0]->get_ll(0)[1]); + set_size(y_texels * width / height, y_texels); + + } else { + // If size_from_texels is false, we get the particle size from + // the size of the polygon. + set_size(width, height); + } + + } else { + // With no vertices, just punt. + set_size(1.0f, 1.0f); + } + + init_geoms(); + } +} + + +//////////////////////////////////////////////////////////////////// +// Function : SpriteParticleRenderer::add_from_node +// Access : public +// Description : Sets the properties on this render from the geometry +// referenced by the indicated NodePath. This should be // a reference to a GeomNode; it extracts out the // Texture and UV range from the GeomNode. // @@ -120,129 +353,133 @@ make_copy() { // found in the GeomNode. //////////////////////////////////////////////////////////////////// void SpriteParticleRenderer:: -set_from_node(const NodePath &node_path, bool size_from_texels) { +add_from_node(const NodePath &node_path, const string &model, const string &node, bool size_from_texels, bool resize) { + add_from_node(node_path,size_from_texels,resize); + get_last_anim()->set_source_info(model,node); +} + +void SpriteParticleRenderer:: +add_from_node(const NodePath &node_path, bool size_from_texels, bool resize) { nassertv(!node_path.is_empty()); - // The bottom node must be a GeomNode. If it is not, find the first - // GeomNode beneath it. - NodePath geom_node_path = node_path; - if (!geom_node_path.node()->is_geom_node()) { - geom_node_path = node_path.find("**/+GeomNode"); - if (geom_node_path.is_empty()) { - particlesystem_cat.error() - << node_path << " does not contain a GeomNode.\n"; - return; - } - } - GeomNode *gnode = DCAST(GeomNode, geom_node_path.node()); + pvector< TexCoordf > ll,ur; + GeomNode *gnode = NULL; + NodePathCollection np_col; + TextureCollection tex_col; + const Geom *geom; + const GeomPrimitive *primitive; + bool got_texcoord,got_vertex; - // Get the texture off the node. We'll take just the first texture. - Texture *tex = geom_node_path.find_texture("*"); - - if (tex == (Texture *)NULL) { - particlesystem_cat.error() - << geom_node_path << " has no texture.\n"; - return; - } - - // Now examine the UV's of the first Geom within the GeomNode. - nassertv(gnode->get_num_geoms() > 0); - const Geom *geom = gnode->get_geom(0); - - bool got_texcoord = false; - TexCoordf min_uv(0.0f, 0.0f); - TexCoordf max_uv(0.0f, 0.0f); - - bool got_vertex = false; - Vertexf min_xyz(0.0f, 0.0f, 0.0f); - Vertexf max_xyz(0.0f, 0.0f, 0.0f); - - GeomVertexReader texcoord(geom->get_vertex_data(), - InternalName::get_texcoord()); - if (texcoord.has_column()) { - for (int pi = 0; pi < geom->get_num_primitives(); ++pi) { - const GeomPrimitive *primitive = geom->get_primitive(pi); - for (int vi = 0; vi < primitive->get_num_vertices(); ++vi) { - int vert = primitive->get_vertex(vi); - texcoord.set_row(vert); - - if (!got_texcoord) { - min_uv = max_uv = texcoord.get_data2f(); - got_texcoord = true; - - } else { - const LVecBase2f &uv = texcoord.get_data2f(); - - min_uv[0] = min(min_uv[0], uv[0]); - max_uv[0] = max(max_uv[0], uv[0]); - min_uv[1] = min(min_uv[1], uv[1]); - max_uv[1] = max(max_uv[1], uv[1]); - } - } - } - } - - GeomVertexReader vertex(geom->get_vertex_data(), - InternalName::get_vertex()); - if (vertex.has_column()) { - for (int pi = 0; pi < geom->get_num_primitives(); ++pi) { - const GeomPrimitive *primitive = geom->get_primitive(pi); - for (int vi = 0; vi < primitive->get_num_vertices(); ++vi) { - int vert = primitive->get_vertex(vi); - vertex.set_row(vert); - - if (!got_vertex) { - min_xyz = max_xyz = vertex.get_data3f(); - got_vertex = true; - - } else { - const LVecBase3f &xyz = vertex.get_data3f(); - - min_xyz[0] = min(min_xyz[0], xyz[0]); - max_xyz[0] = max(max_xyz[0], xyz[0]); - min_xyz[1] = min(min_xyz[1], xyz[1]); - max_xyz[1] = max(max_xyz[1], xyz[1]); - min_xyz[2] = min(min_xyz[2], xyz[2]); - max_xyz[2] = max(max_xyz[2], xyz[2]); - } - } - } - } - - _texture = tex; - if (got_texcoord) { - // We don't really pay attention to orientation of UV's here; a - // minor flaw. We assume the minimum is in the lower-left, and - // the maximum is in the upper-right. - set_ll_uv(min_uv); - set_ur_uv(max_uv); - } - - if (got_vertex) { - float width = max_xyz[0] - min_xyz[0]; - float height = max(max_xyz[1] - min_xyz[1], - max_xyz[2] - min_xyz[2]); - - if (size_from_texels && got_texcoord) { - // If size_from_texels is true, we get the particle size from the - // number of texels in the source image. - float y_texels = _texture->get_y_size() * fabs(_ur_uv[1] - _ll_uv[1]); - set_size(y_texels * width / height, y_texels); + // Load the found textures into the renderer. + if (extract_textures_from_node(node_path,np_col,tex_col)) { + for (int i = 0; i < np_col.get_num_paths(); ++i) { + // Get the node from which we'll extract the geometry information. + gnode = DCAST(GeomNode, np_col[i].node()); - } else { - // If size_from_texels is false, we get the particle size from - // the size of the polygon. - set_size(width, height); + // Now examine the UV's of the first Geom within the GeomNode. + nassertv(gnode->get_num_geoms() > 0); + geom = gnode->get_geom(0); + + got_texcoord = false; + TexCoordf min_uv(0.0f, 0.0f); + TexCoordf max_uv(0.0f, 0.0f); + + GeomVertexReader texcoord(geom->get_vertex_data(), + InternalName::get_texcoord()); + if (texcoord.has_column()) { + for (int pi = 0; pi < geom->get_num_primitives(); ++pi) { + primitive = geom->get_primitive(pi); + for (int vi = 0; vi < primitive->get_num_vertices(); ++vi) { + int vert = primitive->get_vertex(vi); + texcoord.set_row(vert); + + if (!got_texcoord) { + min_uv = max_uv = texcoord.get_data2f(); + got_texcoord = true; + + } else { + const LVecBase2f &uv = texcoord.get_data2f(); + + min_uv[0] = min(min_uv[0], uv[0]); + max_uv[0] = max(max_uv[0], uv[0]); + min_uv[1] = min(min_uv[1], uv[1]); + max_uv[1] = max(max_uv[1], uv[1]); + } + } + } + } + + if (got_texcoord) { + // We don't really pay attention to orientation of UV's here; a + // minor flaw. We assume the minimum is in the lower-left, and + // the maximum is in the upper-right. + ll.push_back(min_uv); + ur.push_back(max_uv); + } + } + + _anims.push_back(new SpriteAnim(tex_col,ll,ur)); + + if (resize) { + gnode = DCAST(GeomNode, np_col[0].node()); + geom = gnode->get_geom(0); + + got_vertex = false; + Vertexf min_xyz(0.0f, 0.0f, 0.0f); + Vertexf max_xyz(0.0f, 0.0f, 0.0f); + + GeomVertexReader vertex(geom->get_vertex_data(), + InternalName::get_vertex()); + if (vertex.has_column()) { + for (int pi = 0; pi < geom->get_num_primitives(); ++pi) { + const GeomPrimitive *primitive = geom->get_primitive(pi); + for (int vi = 0; vi < primitive->get_num_vertices(); ++vi) { + int vert = primitive->get_vertex(vi); + vertex.set_row(vert); + + if (!got_vertex) { + min_xyz = max_xyz = vertex.get_data3f(); + got_vertex = true; + + } else { + const LVecBase3f &xyz = vertex.get_data3f(); + + min_xyz[0] = min(min_xyz[0], xyz[0]); + max_xyz[0] = max(max_xyz[0], xyz[0]); + min_xyz[1] = min(min_xyz[1], xyz[1]); + max_xyz[1] = max(max_xyz[1], xyz[1]); + min_xyz[2] = min(min_xyz[2], xyz[2]); + max_xyz[2] = max(max_xyz[2], xyz[2]); + } + } + } + } + + if (got_vertex) { + float width = max_xyz[0] - min_xyz[0]; + float height = max(max_xyz[1] - min_xyz[1], + max_xyz[2] - min_xyz[2]); + + if (size_from_texels && got_texcoord) { + // If size_from_texels is true, we get the particle size from the + // number of texels in the source image. + float y_texels = _anims[0]->get_frame(0)->get_y_size() * fabs(_anims[0]->get_ur(0)[1] - _anims[0]->get_ll(0)[1]); + set_size(y_texels * width / height, y_texels); + + } else { + // If size_from_texels is false, we get the particle size from + // the size of the polygon. + set_size(width, height); + } + + } else { + // With no vertices, just punt. + set_size(1.0f, 1.0f); + } } - } else { - // With no vertices, just punt. - set_size(1.0f, 1.0f); - } - - _source_type = ST_from_node; - - init_geoms(); + init_geoms(); + } } //////////////////////////////////////////////////////////////////// @@ -253,15 +490,14 @@ set_from_node(const NodePath &node_path, bool size_from_texels) { void SpriteParticleRenderer:: resize_pool(int new_size) { if (new_size != _pool_size) { - _pool_size = new_size; - + _pool_size = new_size; init_geoms(); } } //////////////////////////////////////////////////////////////////// // Function : SpriteParticleRenderer::init_geoms -// Access : private +// Access : public // Description : initializes everything, called on traumatic events // such as construction and serious particlesystem // modifications @@ -269,10 +505,11 @@ resize_pool(int new_size) { void SpriteParticleRenderer:: init_geoms() { CPT(RenderState) state = _render_state; + SpriteAnim *anim; + int anim_count = _anims.size(); + int i,j; - PT(Geom) geom = new Geom; - _sprite_primitive = geom; - + // Setup format PT(GeomVertexArrayFormat) array_format = new GeomVertexArrayFormat (InternalName::get_vertex(), 3, Geom::NT_float32, Geom::C_point, InternalName::get_color(), 1, Geom::NT_packed_dabc, Geom::C_color); @@ -281,7 +518,7 @@ init_geoms() { array_format->add_column (InternalName::get_rotate(), 1, Geom::NT_float32, Geom::C_other); } - + _base_y_scale = _initial_y_scale; _aspect_ratio = _width / _height; @@ -304,35 +541,73 @@ init_geoms() { CPT(GeomVertexFormat) format = GeomVertexFormat::register_format (new GeomVertexFormat(array_format)); - _vdata = new GeomVertexData - ("particles", format, Geom::UH_dynamic); - geom->set_vertex_data(_vdata); - _sprites = new GeomPoints(Geom::UH_dynamic); - geom->add_primitive(_sprites); - - state = state->add_attrib(RenderModeAttrib::make(RenderModeAttrib::M_unchanged, _base_y_scale * _height, true)); - - if (_texture != (Texture *)NULL) { - state = state->add_attrib(TextureAttrib::make(_texture)); - state = state->add_attrib(TexGenAttrib::make(TextureStage::get_default(), TexGenAttrib::M_point_sprite)); - - // Build a matrix to convert the texture coordinates to the ul, lr - // space. - LPoint2f ul(_ll_uv[0], _ur_uv[1]); - LPoint2f lr(_ur_uv[0], _ll_uv[1]); - LVector2f sc = lr - ul; - - LMatrix4f mat - (sc[0], 0.0f, 0.0f, 0.0f, - 0.0f, sc[1], 0.0f, 0.0f, - 0.0f, 0.0f, 1.0f, 0.0f, - ul[0], ul[1], 0.0f, 1.0f); - state = state->add_attrib(TexMatrixAttrib::make(mat)); + // Reset render() data structures + for (i = 0; i < (int)_ttl_count.size(); ++i) { + delete [] _ttl_count[i]; } + _anim_size.resize(anim_count); + _ttl_count.clear(); + _ttl_count.resize(anim_count); + + // Reset sprite primitive data in order to prepare for next pass. + _sprite_primitive.clear(); + _sprites.clear(); + _vdata.clear(); + _sprite_writer.clear(); GeomNode *render_node = get_render_node(); render_node->remove_all_geoms(); - render_node->add_geom(_sprite_primitive, state); + + // For each animation... + for (i = 0; i < anim_count; ++i) { + anim = _anims[i]; + _anim_size[i] = anim->get_num_frames(); + + _sprite_primitive.push_back(pvector()); + _sprites.push_back(pvector()); + _vdata.push_back(pvector()); + _sprite_writer.push_back(pvector()); + + // For each frame of the animation... + for (j = 0; j < _anim_size[i]; ++j) { + _ttl_count[i] = new int[_anim_size[i]]; + PT(Geom) geom = new Geom; + _sprite_primitive[i].push_back((Geom*)geom); + _vdata[i].push_back(new GeomVertexData("particles", format, Geom::UH_dynamic)); + geom->set_vertex_data(_vdata[i][j]); + _sprites[i].push_back(new GeomPoints(Geom::UH_dynamic)); + geom->add_primitive(_sprites[i][j]); + + // This will be overwritten in render(), but we had to have some initial value + // since there are no default constructors for GeomVertexWriter. + _sprite_writer[i].push_back(SpriteWriter(GeomVertexWriter(_vdata[i][j], InternalName::get_vertex()), + GeomVertexWriter(_vdata[i][j], InternalName::get_color()), + GeomVertexWriter(_vdata[i][j], InternalName::get_rotate()), + GeomVertexWriter(_vdata[i][j], InternalName::get_size()), + GeomVertexWriter(_vdata[i][j], InternalName::get_aspect_ratio()))); + + state = state->add_attrib(RenderModeAttrib::make(RenderModeAttrib::M_unchanged, _base_y_scale * _height, true)); + if (anim->get_frame(j) != (Texture *)NULL) { + state = state->add_attrib(TextureAttrib::make(anim->get_frame(j))); + state = state->add_attrib(TexGenAttrib::make(TextureStage::get_default(), TexGenAttrib::M_point_sprite)); + + // Build a matrix to convert the texture coordinates to the ll, ur + // space. + LPoint2f ul(anim->get_ur(j)[0], anim->get_ur(j)[1]); + LPoint2f lr(anim->get_ll(j)[0], anim->get_ll(j)[1]); + LVector2f sc = lr - ul; + + LMatrix4f mat + (sc[0], 0.0f, 0.0f, 0.0f, + 0.0f, sc[1], 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + ul[0], ul[1], 0.0f, 1.0f); + state = state->add_attrib(TexMatrixAttrib::make(mat)); + + render_node->add_geom(_sprite_primitive[i][j], state); + } + } + } } //////////////////////////////////////////////////////////////////// @@ -343,7 +618,8 @@ init_geoms() { // out we don't really want it. //////////////////////////////////////////////////////////////////// void SpriteParticleRenderer:: -birth_particle(int) { +birth_particle(int index) { + _birth_list.push_back(index); } //////////////////////////////////////////////////////////////////// @@ -362,16 +638,52 @@ kill_particle(int) { //////////////////////////////////////////////////////////////////// void SpriteParticleRenderer:: render(pvector< PT(PhysicsObject) >& po_vector, int ttl_particles) { + // There is no texture data available, exit. + if (_anims.empty()) { + return; + } + BaseParticle *cur_particle; - int remaining_particles = ttl_particles; - int i; - - GeomVertexWriter vertex(_vdata, InternalName::get_vertex()); - GeomVertexWriter color(_vdata, InternalName::get_color()); - GeomVertexWriter rotate(_vdata, InternalName::get_rotate()); - GeomVertexWriter size(_vdata, InternalName::get_size()); - GeomVertexWriter aspect_ratio(_vdata, InternalName::get_aspect_ratio()); + int i,j; // loop counters + int anim_count = _anims.size(); // number of animations + int frame; // frame index, used in indicating which frame to use when not animated + // First, since this is the only time we have access to the actual particles, do some delayed initialization. + if (_animate_frames || anim_count) { + if (!_birth_list.empty()) { + for (pvector::iterator vIter = _birth_list.begin(); vIter != _birth_list.end(); ++vIter) { + cur_particle = (BaseParticle*)po_vector[*vIter].p(); + i = int(NORMALIZED_RAND()*anim_count); + + // If there are multiple animations to choose from, choose one at random for this new particle + cur_particle->set_index(i < anim_count?i:i-1); + + // This is an experimental age offset so that the animations don't appear synchronized. + // If we are using animations, try to vary the frame flipping a bit for particles in the same litter. + // A similar effect might be a achieved by using a small lifespan spread value on the factory. + if (_animate_frames) { + cur_particle->set_age(cur_particle->get_age()+i/10.0*cur_particle->get_lifespan()); + } + } + } + } + _birth_list.clear(); + + // Create vertex writers for each of the possible geoms. + // Could possibly be changed to only create writers for geoms that would be used + // according to the animation configuration. + for (i = 0; i < anim_count; ++i) { + for (j = 0; j < _anim_size[i]; ++j) { + // Set the particle per frame counts to 0. + memset(_ttl_count[i],NULL,_anim_size[i]*sizeof(int)); + + _sprite_writer[i][j].vertex = GeomVertexWriter(_vdata[i][j], InternalName::get_vertex()); + _sprite_writer[i][j].color = GeomVertexWriter(_vdata[i][j], InternalName::get_color()); + _sprite_writer[i][j].rotate = GeomVertexWriter(_vdata[i][j], InternalName::get_rotate()); + _sprite_writer[i][j].size = GeomVertexWriter(_vdata[i][j], InternalName::get_size()); + _sprite_writer[i][j].aspect_ratio = GeomVertexWriter(_vdata[i][j], InternalName::get_aspect_ratio()); + } + } // init the aabb _aabb_min.set(99999.0f, 99999.0f, 99999.0f); @@ -381,8 +693,9 @@ render(pvector< PT(PhysicsObject) >& po_vector, int ttl_particles) { for (i = 0; i < (int)po_vector.size(); i++) { cur_particle = (BaseParticle *) po_vector[i].p(); - if (!cur_particle->get_alive()) + if (!cur_particle->get_alive()) { continue; + } LPoint3f position = cur_particle->get_position(); @@ -406,12 +719,32 @@ render(pvector< PT(PhysicsObject) >& po_vector, int ttl_particles) { float t = cur_particle->get_parameterized_age(); + int anim_index = cur_particle->get_index(); + + if(_animation_removed && (anim_index >= anim_count)) { + anim_index = int(NORMALIZED_RAND()*anim_count); + anim_index = anim_indexset_index(anim_index); + } + + // Find the frame + if (_animate_frames) { + if (_animate_frames_rate == 0.0f) { + frame = (int)(t*_anim_size[anim_index]); + } else { + frame = (int)fmod(cur_particle->get_age()*_animate_frames_rate+1,_anim_size[anim_index]); + } + } else { + frame = _animate_frames_index; + } + + // Quick check make sure our math above didn't result in an invalid frame. + frame = (frame < _anim_size[anim_index]) ? frame : (_anim_size[anim_index]-1); + ++_ttl_count[anim_index][frame]; // Calculate the color // This is where we'll want to give the renderer the new color - //Colorf c = _color; - Colorf c = _color_interpolation_manager->generateColor(t); - + Colorf c = _color_interpolation_manager->generateColor(t); int alphamode=get_alpha_mode(); if (alphamode != PR_ALPHA_NONE) { @@ -428,14 +761,15 @@ render(pvector< PT(PhysicsObject) >& po_vector, int ttl_particles) { } } - vertex.add_data3f(position); - color.add_data4f(c); - + // Send the data on its way... + // if(anim_index>_anims.size() || frame > _sprite_writer[anim_index].size()) + _sprite_writer[anim_index][frame].vertex.add_data3f(position); + _sprite_writer[anim_index][frame].color.add_data4f(c); + float current_x_scale = _initial_x_scale; float current_y_scale = _initial_y_scale; if (_animate_x_ratio || _animate_y_ratio) { - float t = cur_particle->get_parameterized_age(); if (_blend_method == PP_BLEND_CUBIC) { t = CUBIC_T(t); } @@ -450,17 +784,16 @@ render(pvector< PT(PhysicsObject) >& po_vector, int ttl_particles) { } } - if (size.has_column()) { - size.add_data1f(current_y_scale * _height); + if (_sprite_writer[anim_index][frame].size.has_column()) { + _sprite_writer[anim_index][frame].size.add_data1f(current_y_scale * _height); } - if (aspect_ratio.has_column()) { - aspect_ratio.add_data1f(_aspect_ratio * current_x_scale / current_y_scale); + if (_sprite_writer[anim_index][frame].aspect_ratio.has_column()) { + _sprite_writer[anim_index][frame].aspect_ratio.add_data1f(_aspect_ratio * current_x_scale / current_y_scale); } - if (_animate_theta) { - rotate.add_data1f(cur_particle->get_theta()); - } else if (rotate.has_column()) { - rotate.add_data1f(_theta); + _sprite_writer[anim_index][frame].rotate.add_data1f(cur_particle->get_theta()); + } else if (_sprite_writer[anim_index][frame].rotate.has_column()) { + _sprite_writer[anim_index][frame].rotate.add_data1f(_theta); } // maybe jump out early? @@ -470,15 +803,36 @@ render(pvector< PT(PhysicsObject) >& po_vector, int ttl_particles) { } } - _sprites->clear_vertices(); - _sprites->add_next_vertices(ttl_particles); + for (i = 0; i < anim_count; ++i) { + for (j = 0; j < _anim_size[i]; ++j) { + _sprites[i][j]->clear_vertices(); + } + } + + if (_animate_frames) { + for (i = 0; i < anim_count; ++i) { + for (j = 0; j < _anim_size[i]; ++j) { + _sprites[i][j]->add_next_vertices(_ttl_count[i][j]); + } + } + } else { + for (i = 0; i < anim_count; ++i) { + _sprites[i][_animate_frames_index]->add_next_vertices(_ttl_count[i][_animate_frames_index]); + } + } // done filling geompoint node, now do the bb stuff LPoint3f aabb_center = _aabb_min + ((_aabb_max - _aabb_min) * 0.5f); float radius = (aabb_center - _aabb_min).length(); - _sprite_primitive->set_bound(BoundingSphere(aabb_center, radius)); + for (i = 0; i < anim_count; ++i) { + for (j = 0; j < _anim_size[i]; ++j) { + _sprite_primitive[i][j]->set_bound(BoundingSphere(aabb_center, radius)); + } + } + get_render_node()->mark_bound_stale(); + _animation_removed = false; } //////////////////////////////////////////////////////////////////// @@ -503,7 +857,7 @@ output(ostream &out) const { void SpriteParticleRenderer:: write(ostream &out, int indent_level) const { indent(out, indent_level) << "SpriteParticleRenderer:\n"; - indent(out, indent_level + 2) << "_sprite_primitive "<<_sprite_primitive<<"\n"; + // indent(out, indent_level + 2) << "_sprite_primitive "<<_sprite_primitive<<"\n"; indent(out, indent_level + 2) << "_color "<<_color<<"\n"; indent(out, indent_level + 2) << "_initial_x_scale "<<_initial_x_scale<<"\n"; indent(out, indent_level + 2) << "_final_x_scale "<<_final_x_scale<<"\n"; @@ -517,6 +871,5 @@ write(ostream &out, int indent_level) const { indent(out, indent_level + 2) << "_aabb_min "<<_aabb_min<<"\n"; indent(out, indent_level + 2) << "_aabb_max "<<_aabb_max<<"\n"; indent(out, indent_level + 2) << "_pool_size "<<_pool_size<<"\n"; - indent(out, indent_level + 2) << "_source_type "<<_source_type<<"\n"; BaseParticleRenderer::write(out, indent_level + 2); } diff --git a/panda/src/particlesystem/spriteParticleRenderer.h b/panda/src/particlesystem/spriteParticleRenderer.h index 9909c11125..ab0d8d2546 100644 --- a/panda/src/particlesystem/spriteParticleRenderer.h +++ b/panda/src/particlesystem/spriteParticleRenderer.h @@ -20,6 +20,7 @@ #define SPRITEPARTICLERENDERER_H #include "pandabase.h" +#include "pvector.h" #include "baseParticleRenderer.h" #include "baseParticle.h" #include "texture.h" @@ -28,9 +29,121 @@ #include "geomVertexData.h" #include "geomPoints.h" #include "colorInterpolationManager.h" +#include "geomVertexWriter.h" +#include "textureCollection.h" +#include "nodePathCollection.h" class NodePath; +class SpriteWriter { +public: + SpriteWriter(GeomVertexWriter v, + GeomVertexWriter c, + GeomVertexWriter r, + GeomVertexWriter s, + GeomVertexWriter a): + vertex(v), + color(c), + rotate(r), + size(s), + aspect_ratio(a){ + }; + + SpriteWriter(const SpriteWriter ©): + vertex(copy.vertex), + color(copy.color), + rotate(copy.rotate), + size(copy.size), + aspect_ratio(copy.aspect_ratio) { + }; + + GeomVertexWriter vertex; + GeomVertexWriter color; + GeomVertexWriter rotate; + GeomVertexWriter size; + GeomVertexWriter aspect_ratio; +}; + +class SpriteAnim : public ReferenceCount{ +PUBLISHED: + enum SourceType { + ST_texture, + ST_from_node, + }; + + void set_source_info(const string &tex) { + _source_type = ST_texture; + _source_tex = tex; + } + + void set_source_info(const string &model, const string &node) { + _source_type = ST_from_node; + _source_model = model; + _source_node = node; + } + + SourceType get_source_type() const { + return _source_type; + } + + string get_tex_source() const { + return _source_tex; + } + + string get_model_source() const { + return _source_model; + } + + string get_node_source() const { + return _source_node; + } + + int get_num_frames(void) const { + return textures.size(); + } + +public: + SpriteAnim(Texture* t, TexCoordf ll, TexCoordf ur) { + textures.push_back(t); + this->ll.push_back(ll); + this->ur.push_back(ur); + }; + + SpriteAnim(const TextureCollection &t, const pvector< TexCoordf > &lls, const pvector< TexCoordf > &urs) : + ll(lls), + ur(urs) { + for (int i = 0; i < t.get_num_textures(); ++i) { + textures.push_back(t.get_texture(i)); + } + }; + + void set_ll(const int n, TexCoordf c) { + ll[n] = c; + } + + void set_ur(const int n, TexCoordf c) { + ur[n] = c; + } + + Texture *get_frame(const int n) const { + return textures[n]; + }; + + TexCoordf get_ll(const int n) const { + return ll[n]; + } + + TexCoordf get_ur(const int n) const { + return ur[n]; + } + +private: + pvector< PT(Texture) > textures; + pvector< TexCoordf > ll,ur; + SourceType _source_type; + string _source_tex,_source_model,_source_node; +}; + //////////////////////////////////////////////////////////////////// // Class : SpriteParticleRenderer // Description : Renders a particle system with high-speed nasty @@ -38,44 +151,52 @@ class NodePath; //////////////////////////////////////////////////////////////////// class EXPCL_PANDAPHYSICS SpriteParticleRenderer : public BaseParticleRenderer { PUBLISHED: - // This enumerated type indicates the source of the sprite texture: - // whether it came from an explicit call to set_texture(), or - // whether from a call to set_from_node(). - enum SourceType { - ST_texture, - ST_from_node, - }; - SpriteParticleRenderer(Texture *tex = (Texture *) NULL); SpriteParticleRenderer(const SpriteParticleRenderer ©); virtual ~SpriteParticleRenderer(); virtual BaseParticleRenderer *make_copy(); - INLINE SourceType get_source_type() const; - void set_from_node(const NodePath &node_path, bool size_from_texels = true); + void set_from_node(const NodePath &node_path, const string &model, const string &node, bool size_from_texels = true); + void add_from_node(const NodePath &node_path, bool size_from_texels = true, bool resize = false); + void add_from_node(const NodePath &node_path, const string &model, const string &node, bool size_from_texels = true, bool resize = false); - INLINE void set_texture(Texture *tex, float texels_per_unit = 1.0); + INLINE void set_texture(Texture *tex, float texels_per_unit = 1.0f); + INLINE void set_texture(Texture *tex, const string &tex_path, float texels_per_unit = 1.0f); + INLINE void add_texture(Texture *tex, float texels_per_unit = 1.0f, bool resize = false); + INLINE void add_texture(Texture *tex, const string &tex_path, float texels_per_unit = 1.0f, bool resize = false); + INLINE void remove_animation(const int n); INLINE void set_ll_uv(const TexCoordf &ll_uv); + INLINE void set_ll_uv(const TexCoordf &ll_uv, const int anim, const int frame); INLINE void set_ur_uv(const TexCoordf &ur_uv); + INLINE void set_ur_uv(const TexCoordf &ur_uv, const int anim, const int frame); INLINE void set_size(float width, float height); INLINE void set_color(const Colorf &color); INLINE void set_x_scale_flag(bool animate_x_ratio); INLINE void set_y_scale_flag(bool animate_y_ratio); INLINE void set_anim_angle_flag(bool animate_theta); INLINE void set_initial_x_scale(float initial_x_scale); - INLINE void set_final_x_scale(float final_x_scale); + INLINE void set_final_x_scale(float final_x_scale); INLINE void set_initial_y_scale(float initial_y_scale); INLINE void set_final_y_scale(float final_y_scale); INLINE void set_nonanimated_theta(float theta); INLINE void set_alpha_blend_method(ParticleRendererBlendMethod bm); INLINE void set_alpha_disable(bool ad); + INLINE void set_animate_frames_enable(bool an); + INLINE void set_animate_frames_rate(float r); + INLINE void set_animate_frames_index(int i); INLINE Texture *get_texture() const; + INLINE Texture *get_texture(const int anim, const int frame) const; + INLINE int get_num_anims() const; + INLINE SpriteAnim *get_anim(const int n) const; + INLINE SpriteAnim *get_last_anim() const; INLINE ColorInterpolationManager* get_color_interpolation_manager() const; - INLINE const TexCoordf &get_ll_uv() const; - INLINE const TexCoordf &get_ur_uv() const; + INLINE TexCoordf get_ll_uv() const; + INLINE TexCoordf get_ll_uv(const int anim, const int frame) const; + INLINE TexCoordf get_ur_uv() const; + INLINE TexCoordf get_ur_uv(const int anim, const int frame) const; INLINE float get_width() const; INLINE float get_height() const; INLINE Colorf get_color() const; @@ -89,20 +210,23 @@ PUBLISHED: INLINE float get_nonanimated_theta() const; INLINE ParticleRendererBlendMethod get_alpha_blend_method() const; INLINE bool get_alpha_disable() const; + INLINE bool get_animate_frames_enable() const; + INLINE float get_animate_frames_rate() const; + INLINE int get_animate_frames_index() const; virtual void output(ostream &out) const; virtual void write(ostream &out, int indent_level = 0) const; private: - PT(Geom) _sprite_primitive; - PT(GeomPoints) _sprites; - PT(Texture) _texture; + pvector< pvector< PT(Geom) > > _sprite_primitive; + pvector< pvector< PT(GeomPoints) > > _sprites; + pvector< pvector< SpriteWriter > > _sprite_writer; + pvector< pvector< PT(GeomVertexData) > > _vdata; - PT(GeomVertexData) _vdata; + pvector< PT(SpriteAnim) > _anims; // Stores texture references and UV info for each geom. Colorf _color; - TexCoordf _ll_uv, _ur_uv; float _height; float _width; float _initial_x_scale; @@ -112,11 +236,15 @@ private: float _theta; float _base_y_scale; float _aspect_ratio; + float _animate_frames_rate; + int _animate_frames_index; bool _animate_x_ratio; bool _animate_y_ratio; bool _animate_theta; bool _alpha_disable; + bool _animate_frames; + bool _animation_removed; ParticleRendererBlendMethod _blend_method; PT(ColorInterpolationManager) _color_interpolation_manager; @@ -125,7 +253,6 @@ private: Vertexf _aabb_max; int _pool_size; - SourceType _source_type; virtual void birth_particle(int index); virtual void kill_particle(int index); @@ -133,6 +260,12 @@ private: virtual void render(pvector< PT(PhysicsObject) > &po_vector, int ttl_particles); virtual void resize_pool(int new_size); + int extract_textures_from_node(const NodePath &node_path, NodePathCollection &np_col, TextureCollection &tex_col); + + pvector _anim_size; // Holds the number of frames in each animation. + pvector _ttl_count; // _ttl_count[i][j] holds the number of particles attached to animation 'i' at frame 'j'. + pvector _birth_list; // Holds the list of particles that need a new random animation to start on. + }; #include "spriteParticleRenderer.I"