Added animated and multiple sprites capability.

This commit is contained in:
Josh Wilson 2005-07-14 18:22:01 +00:00
parent 5ece0deebf
commit acdba7e61b
3 changed files with 933 additions and 247 deletions

View File

@ -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

View File

@ -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<PT(Geom)>());
_sprites.push_back(pvector<PT(GeomPoints)>());
_vdata.push_back(pvector<PT(GeomVertexData)>());
_sprite_writer.push_back(pvector<SpriteWriter>());
// 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<int>::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_index<anim_count?anim_index:anim_index-1;
cur_particle->set_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);
}

View File

@ -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 &copy):
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 &copy);
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<int> _anim_size; // Holds the number of frames in each animation.
pvector<int*> _ttl_count; // _ttl_count[i][j] holds the number of particles attached to animation 'i' at frame 'j'.
pvector<int> _birth_list; // Holds the list of particles that need a new random animation to start on.
};
#include "spriteParticleRenderer.I"