15 #include "spriteParticleRenderer.h"
16 #include "boundingSphere.h"
18 #include "sequenceNode.h"
22 #include "geomVertexReader.h"
23 #include "geomVertexWriter.h"
24 #include "renderModeAttrib.h"
25 #include "texMatrixAttrib.h"
26 #include "texGenAttrib.h"
27 #include "textureAttrib.h"
28 #include "textureCollection.h"
29 #include "nodePathCollection.h"
31 #include "config_particlesystem.h"
32 #include "pStatTimer.h"
34 PStatCollector SpriteParticleRenderer::_render_collector(
"App:Particles:Sprite:Render");
44 _color(
LColor(1.0f, 1.0f, 1.0f, 1.0f)),
47 _initial_x_scale(1.0f),
49 _initial_y_scale(1.0f),
54 _animate_frames_rate(0.0f),
55 _animate_frames_index(0),
56 _animate_x_ratio(false),
57 _animate_y_ratio(false),
58 _animate_theta(false),
59 _alpha_disable(false),
60 _animate_frames(false),
61 _animation_removed(true),
62 _blend_method(PP_BLEND_LINEAR),
79 _height(copy._height),
81 _initial_x_scale(copy._initial_x_scale),
82 _final_x_scale(copy._final_x_scale),
83 _initial_y_scale(copy._initial_y_scale),
84 _final_y_scale(copy._final_y_scale),
86 _base_y_scale(copy._base_y_scale),
87 _aspect_ratio(copy._aspect_ratio),
88 _animate_frames_rate(copy._animate_frames_rate),
89 _animate_frames_index(copy._animate_frames_index),
90 _animate_x_ratio(copy._animate_x_ratio),
91 _animate_y_ratio(copy._animate_y_ratio),
92 _animate_theta(copy._animate_theta),
93 _alpha_disable(copy._alpha_disable),
94 _animate_frames(copy._animate_frames),
95 _animation_removed(true),
96 _blend_method(copy._blend_method),
97 _color_interpolation_manager(copy._color_interpolation_manager),
99 _birth_list(copy._birth_list) {
133 int SpriteParticleRenderer::
139 if (!tex_node_path.
is_empty() && tex_node_path.
node()->get_type() != SequenceNode::get_class_type()) {
140 tex_node_path = node_path.
find(
"**/+SequenceNode");
148 for (
int i = 0; i < frame_count; ++i) {
149 geom_node_path = tex_node_path.
get_child(i);
167 if (!node_path.
is_empty() && node_path.
node()->get_type() != GeomNode::get_class_type()) {
168 geom_node_path = node_path.
find(
"**/+GeomNode");
170 particlesystem_cat.error();
174 geom_node_path = node_path;
180 particlesystem_cat.error()
181 << geom_node_path <<
" does not contain a texture.\n";
266 add_from_node(
const NodePath &node_path,
const string &model,
const string &node,
bool size_from_texels,
bool resize) {
267 int anim_count = _anims.size();
271 if (anim_count < (
int)_anims.size()) {
272 get_last_anim()->set_source_info(model,node);
299 if (extract_textures_from_node(node_path,np_col,tex_col)) {
305 for (
int i = 0; i < np_col.get_num_paths(); ++i) {
307 gnode = DCAST(
GeomNode, np_col[i].node());
311 geom = gnode->get_geom(0);
313 bool got_texcoord =
false;
318 InternalName::get_texcoord());
319 if (texcoord.has_column()) {
320 for (
int pi = 0; pi < geom->get_num_primitives(); ++pi) {
321 primitive = geom->get_primitive(pi);
324 texcoord.set_row_unsafe(vert);
327 min_uv = max_uv = texcoord.get_data2();
331 const LVecBase2 &uv = texcoord.get_data2();
333 min_uv[0] = min(min_uv[0], uv[0]);
334 max_uv[0] = max(max_uv[0], uv[0]);
335 min_uv[1] = min(min_uv[1], uv[1]);
336 max_uv[1] = max(max_uv[1], uv[1]);
346 ll.push_back(min_uv);
347 ur.push_back(max_uv);
351 _anims.push_back(
new SpriteAnim(tex_col,ll,ur));
354 gnode = DCAST(
GeomNode, np_col[0].node());
355 geom = gnode->get_geom(0);
357 bool got_vertex =
false;
358 LVertex min_xyz(0.0f, 0.0f, 0.0f);
359 LVertex max_xyz(0.0f, 0.0f, 0.0f);
362 InternalName::get_vertex());
363 if (vertex.has_column()) {
365 primitive = geom->get_primitive(pi);
368 vertex.set_row_unsafe(vert);
371 min_xyz = max_xyz = vertex.get_data3();
375 const LVecBase3 &xyz = vertex.get_data3();
377 min_xyz[0] = min(min_xyz[0], xyz[0]);
378 max_xyz[0] = max(max_xyz[0], xyz[0]);
379 min_xyz[1] = min(min_xyz[1], xyz[1]);
380 max_xyz[1] = max(max_xyz[1], xyz[1]);
381 min_xyz[2] = min(min_xyz[2], xyz[2]);
382 max_xyz[2] = max(max_xyz[2], xyz[2]);
389 PN_stdfloat width = max_xyz[0] - min_xyz[0];
390 PN_stdfloat height = max(max_xyz[1] - min_xyz[1],
391 max_xyz[2] - min_xyz[2]);
393 if (size_from_texels) {
396 PN_stdfloat y_texels = _anims[0]->get_frame(0)->get_y_size() * fabs(_anims[0]->get_ur(0)[1] - _anims[0]->get_ll(0)[1]);
397 set_size(y_texels * width / height, y_texels);
418 void SpriteParticleRenderer::
419 resize_pool(
int new_size) {
420 if (new_size != _pool_size) {
421 _pool_size = new_size;
433 void SpriteParticleRenderer::
437 int anim_count = _anims.size();
441 PT(GeomVertexArrayFormat) array_format = new GeomVertexArrayFormat
442 (InternalName::get_vertex(), 3,
Geom::NT_stdfloat,
Geom::C_point,
443 InternalName::get_color(), 1,
Geom::NT_packed_dabc,
Geom::C_color);
445 if (_animate_theta || _theta != 0.0f) {
446 array_format->add_column
447 (InternalName::get_rotate(), 1, Geom::NT_stdfloat, Geom::C_other);
450 _base_y_scale = _initial_y_scale;
451 _aspect_ratio = _width / _height;
453 PN_stdfloat final_x_scale = _animate_x_ratio ? _final_x_scale : _initial_x_scale;
454 PN_stdfloat final_y_scale = _animate_y_ratio ? _final_y_scale : _initial_y_scale;
456 if (_animate_y_ratio) {
457 _base_y_scale = max(_initial_y_scale, _final_y_scale);
458 array_format->add_column
459 (InternalName::get_size(), 1, Geom::NT_stdfloat, Geom::C_other);
462 if (_aspect_ratio * _initial_x_scale != _initial_y_scale ||
463 _aspect_ratio * final_x_scale != final_y_scale) {
464 array_format->add_column
465 (InternalName::get_aspect_ratio(), 1, Geom::NT_stdfloat,
469 CPT(GeomVertexFormat) format = GeomVertexFormat::register_format
470 (new GeomVertexFormat(array_format));
473 for (i = 0; i < (
int)_ttl_count.size(); ++i) {
474 PANDA_FREE_ARRAY(_ttl_count[i]);
476 _anim_size.resize(anim_count);
478 _ttl_count.resize(anim_count);
481 _sprite_primitive.clear();
484 _sprite_writer.clear();
490 for (i = 0; i < anim_count; ++i) {
492 _anim_size[i] = anim->get_num_frames();
500 for (j = 0; j < _anim_size[i]; ++j) {
501 _ttl_count[i] = (
int *)PANDA_MALLOC_ARRAY(_anim_size[i] *
sizeof(
int));
502 _vdata[i].push_back(
new GeomVertexData(
"sprite_particles", format, Geom::UH_stream));
503 PT(
Geom) geom = new
Geom(_vdata[i][j]);
504 _sprite_primitive[i].push_back((
Geom*)geom);
506 geom->add_primitive(_sprites[i][j]);
512 if (anim->get_frame(j) != (
Texture *)NULL) {
513 state = state->add_attrib(TextureAttrib::make(anim->get_frame(j)));
518 LPoint2 ul(anim->get_ll(j)[0], anim->get_ur(j)[1]);
519 LPoint2 lr(anim->get_ur(j)[0], anim->get_ll(j)[1]);
522 CPT(TransformState) ts = TransformState::make_pos_rotate_scale2d(ul, 0.0f, sc);
526 render_node->add_geom(_sprite_primitive[i][j], state);
530 nassertv(render_node->check_valid());
541 birth_particle(
int index) {
542 _birth_list.push_back(index);
550 void SpriteParticleRenderer::
559 void SpriteParticleRenderer::
563 if (_anims.empty()) {
568 int remaining_particles = ttl_particles;
570 int anim_count = _anims.size();
573 if (_animate_frames || anim_count) {
574 if (!_birth_list.empty()) {
575 for (vector_int::iterator vIter = _birth_list.begin(); vIter != _birth_list.end(); ++vIter) {
577 i = int(NORMALIZED_RAND()*anim_count);
580 cur_particle->set_index(i < anim_count?i:i-1);
587 if (_animate_frames) {
588 cur_particle->set_age(cur_particle->get_age()+i/10.0*cur_particle->get_lifespan());
598 for (i = 0; i < anim_count; ++i) {
599 for (j = 0; j < _anim_size[i]; ++j) {
601 memset(_ttl_count[i], 0, _anim_size[i]*
sizeof(
int));
602 _sprite_writer[i][j].vertex =
GeomVertexWriter(_vdata[i][j], InternalName::get_vertex());
603 _sprite_writer[i][j].color =
GeomVertexWriter(_vdata[i][j], InternalName::get_color());
604 _sprite_writer[i][j].rotate =
GeomVertexWriter(_vdata[i][j], InternalName::get_rotate());
605 _sprite_writer[i][j].size =
GeomVertexWriter(_vdata[i][j], InternalName::get_size());
606 _sprite_writer[i][j].aspect_ratio =
GeomVertexWriter(_vdata[i][j], InternalName::get_aspect_ratio());
611 _aabb_min.set(99999.0f, 99999.0f, 99999.0f);
612 _aabb_max.set(-99999.0f, -99999.0f, -99999.0f);
615 for (i = 0; i < (int)po_vector.size(); i++) {
618 if (!cur_particle->get_alive()) {
625 if (position[0] > _aabb_max[0])
626 _aabb_max[0] = position[0];
627 else if (position[0] < _aabb_min[0])
628 _aabb_min[0] = position[0];
631 if (position[1] > _aabb_max[1])
632 _aabb_max[1] = position[1];
633 else if (position[1] < _aabb_min[1])
634 _aabb_min[1] = position[1];
637 if (position[2] > _aabb_max[2])
638 _aabb_max[2] = position[2];
639 else if (position[2] < _aabb_min[2])
640 _aabb_min[2] = position[2];
643 PN_stdfloat t = cur_particle->get_parameterized_age();
644 int anim_index = cur_particle->get_index();
648 if(_animation_removed && (anim_index >= anim_count)) {
649 anim_index = int(NORMALIZED_RAND()*anim_count);
650 anim_index = anim_index<anim_count?anim_index:anim_index-1;
651 cur_particle->set_index(anim_index);
655 if (_animate_frames) {
656 if (_animate_frames_rate == 0.0f) {
657 frame = (int)(t*_anim_size[anim_index]);
659 frame = (int)fmod(cur_particle->get_age()*_animate_frames_rate+1,_anim_size[anim_index]);
662 frame = _animate_frames_index;
666 frame = (frame < _anim_size[anim_index]) ? frame : (_anim_size[anim_index]-1);
667 ++_ttl_count[anim_index][frame];
671 LColor c = _color_interpolation_manager->generateColor(t);
673 int alphamode=get_alpha_mode();
674 if (alphamode != PR_ALPHA_NONE) {
675 if (alphamode == PR_ALPHA_OUT)
677 else if (alphamode == PR_ALPHA_IN)
679 else if (alphamode == PR_ALPHA_IN_OUT) {
683 assert(alphamode == PR_ALPHA_USER);
689 _sprite_writer[anim_index][frame].vertex.add_data3(position);
690 _sprite_writer[anim_index][frame].color.add_data4(c);
692 PN_stdfloat current_x_scale = _initial_x_scale;
693 PN_stdfloat current_y_scale = _initial_y_scale;
695 if (_animate_x_ratio || _animate_y_ratio) {
696 if (_blend_method == PP_BLEND_CUBIC) {
700 if (_animate_x_ratio) {
701 current_x_scale = (_initial_x_scale +
702 (t * (_final_x_scale - _initial_x_scale)));
704 if (_animate_y_ratio) {
705 current_y_scale = (_initial_y_scale +
706 (t * (_final_y_scale - _initial_y_scale)));
710 if (_sprite_writer[anim_index][frame].size.has_column()) {
711 _sprite_writer[anim_index][frame].size.add_data1f(current_y_scale * _height);
713 if (_sprite_writer[anim_index][frame].aspect_ratio.has_column()) {
714 _sprite_writer[anim_index][frame].aspect_ratio.add_data1f(_aspect_ratio * current_x_scale / current_y_scale);
716 if (_animate_theta) {
717 _sprite_writer[anim_index][frame].rotate.add_data1f(cur_particle->
get_theta());
718 }
else if (_sprite_writer[anim_index][frame].rotate.has_column()) {
719 _sprite_writer[anim_index][frame].rotate.add_data1f(_theta);
723 remaining_particles--;
724 if (remaining_particles == 0) {
731 for (i = 0; i < anim_count; ++i) {
732 for (j = 0; j < _anim_size[i]; ++j) {
733 _sprites[i][j]->clear_vertices();
734 _sprite_writer[i][j].clear();
739 _sprite_primitive[i][j]->set_primitive(0, _sprites[i][j]);
740 _sprite_primitive[i][j]->set_vertex_data(_vdata[i][j]);
742 render_node->
set_geom(n, _sprite_primitive[i][j]);
747 if (_animate_frames) {
748 for (i = 0; i < anim_count; ++i) {
749 for (j = 0; j < _anim_size[i]; ++j) {
750 _sprites[i][j]->add_next_vertices(_ttl_count[i][j]);
754 for (i = 0; i < anim_count; ++i) {
755 _sprites[i][_animate_frames_index]->add_next_vertices(_ttl_count[i][_animate_frames_index]);
760 LPoint3 aabb_center = _aabb_min + ((_aabb_max - _aabb_min) * 0.5f);
761 PN_stdfloat radius = (aabb_center - _aabb_min).length();
763 for (i = 0; i < anim_count; ++i) {
764 for (j = 0; j < _anim_size[i]; ++j) {
765 nassertv(_sprite_primitive[i][j]->check_valid());
767 _sprite_primitive[i][j]->set_bounds(&sphere);
773 _animation_removed =
false;
785 out<<
"SpriteParticleRenderer";
796 write(ostream &out,
int indent_level)
const {
797 indent(out, indent_level) <<
"SpriteParticleRenderer:\n";
799 indent(out, indent_level + 2) <<
"_color "<<_color<<
"\n";
800 indent(out, indent_level + 2) <<
"_initial_x_scale "<<_initial_x_scale<<
"\n";
801 indent(out, indent_level + 2) <<
"_final_x_scale "<<_final_x_scale<<
"\n";
802 indent(out, indent_level + 2) <<
"_initial_y_scale "<<_initial_y_scale<<
"\n";
803 indent(out, indent_level + 2) <<
"_final_y_scale "<<_final_y_scale<<
"\n";
804 indent(out, indent_level + 2) <<
"_theta "<<_theta<<
"\n";
805 indent(out, indent_level + 2) <<
"_animate_x_ratio "<<_animate_x_ratio<<
"\n";
806 indent(out, indent_level + 2) <<
"_animate_y_ratio "<<_animate_y_ratio<<
"\n";
807 indent(out, indent_level + 2) <<
"_animate_theta "<<_animate_theta<<
"\n";
808 indent(out, indent_level + 2) <<
"_blend_method "<<_blend_method<<
"\n";
809 indent(out, indent_level + 2) <<
"_aabb_min "<<_aabb_min<<
"\n";
810 indent(out, indent_level + 2) <<
"_aabb_max "<<_aabb_max<<
"\n";
811 indent(out, indent_level + 2) <<
"_pool_size "<<_pool_size<<
"\n";
Renders a particle system with high-speed nasty trick sprites.
void set_geom(int n, Geom *geom)
Replaces the nth Geom of the node with a new pointer.
Helper class used by SpriteParticleRenderer to keep track of its textures and their respective UVs an...
This object provides a high-level interface for quickly writing a sequence of numeric values from a v...
LPoint3 get_position() const
Position Query.
This is the base class for all three-component vectors and points.
void add_path(const NodePath &node_path)
Adds a new NodePath to the collection.
NodePath find(const string &path) const
Searches for a node below the referenced node that matches the indicated string.
Defines a series of disconnected points.
PN_stdfloat get_user_alpha() const
gets alpha for "user" alpha mode
void clear()
Removes all NodePaths from the collection.
This defines a bounding sphere, consisting of a center and a radius.
virtual void write(ostream &out, int indent_level=0) const
Write a string representation of this instance to <out>.
virtual PN_stdfloat get_theta() const
for spriteParticleRenderer
A body on which physics will be applied.
void add_from_node(const NodePath &node_path, bool size_from_texels=false, bool resize=false)
This will allow the renderer to randomly choose from more than one texture or sequence at particle bi...
Represents a texture object, which is typically a single 2-d image but may also represent a 1-d or 3-...
int get_num_children(Thread *current_thread=Thread::get_current_thread()) const
Returns the number of children of the referenced node.
This is an abstract base class for a family of classes that represent the fundamental geometry primit...
SpriteParticleRenderer(Texture *tex=(Texture *) NULL)
constructor
PandaNode * node() const
Returns the referenced node of the path.
GeomNode * get_render_node() const
Query the geomnode pointer.
A lightweight class that can be used to automatically start and stop a PStatCollector around a sectio...
This is a three-component point in space (as opposed to a three-component vector, which represents a ...
void add_texture(Texture *texture)
Adds a new Texture to the collection.
virtual ~SpriteParticleRenderer()
destructor
This is our own Panda specialization on the default STL vector.
A lightweight class that represents a single element that may be timed and/or counted via stats...
Pure virtual particle renderer base class.
void mark_internal_bounds_stale(Thread *current_thread=Thread::get_current_thread())
Should be called by a derived class to mark the internal bounding volume stale, so that compute_inter...
NodePath get_child(int n, Thread *current_thread=Thread::get_current_thread()) const
Returns a NodePath representing the nth child of the referenced node.
void set_from_node(const NodePath &node_path, bool size_from_texels=false)
Sets the properties on this renderer from the geometry referenced by the indicated NodePath...
This defines the actual numeric vertex data stored in a Geom, in the structure defined by a particula...
A container for geometry primitives.
High level class for color interpolation.
This is the base class for all two-component vectors and points.
Manages a list of Texture objects, as returned by TexturePool::find_all_textures().
void clear()
Removes all Textures from the collection.
virtual BaseParticleRenderer * make_copy()
child dynamic copy
virtual void output(ostream &out) const
Write a string representation of this instance to <out>.
This represents a unique collection of RenderAttrib objects that correspond to a particular renderabl...
void add_textures_from(const TextureCollection &other)
Adds all the Textures indicated in the other collection to this texture.
This is the base class for all three-component vectors and points.
Applies a transform matrix to UV's before they are rendered.
This is a two-component vector offset.
int get_num_vertices() const
Returns the number of indices used by all the primitives in this object.
An individual, physically-modelable particle abstract base class.
This object provides a high-level interface for quickly reading a sequence of numeric values from a v...
virtual void write(ostream &out, int indent=0) const
Write a string representation of this instance to <out>.
bool is_empty() const
Returns true if the NodePath contains no nodes.
Specifies how polygons are to be drawn.
This is a two-component point in space.
int get_num_textures() const
Returns the number of Textures in the collection.
Helper class used by SpriteParticleRenderer to keep track of the various GeomVertexWriters associated...
TextureCollection find_all_textures() const
Returns a list of a textures applied to geometry at this node and below.
bool check_valid() const
Verifies that the each Geom within the GeomNode reference vertices that actually exist within its Geo...
static TextureStage * get_default()
Returns the default TextureStage that will be used for all texturing that does not name a particular ...
void set_texture(Texture *tex, PN_stdfloat texels_per_unit=1.0f)
Sets the renderer up to render the entire texture image.
Defines the properties of a named stage of the multitexture pipeline.
int get_vertex(int i) const
Returns the ith vertex index in the table.
NodePath is the fundamental system for disambiguating instances, and also provides a higher-level int...
void set_size(PN_stdfloat width, PN_stdfloat height)
Sets the size of each particle in world units.
A node that holds Geom objects, renderable pieces of geometry.
int get_num_geoms() const
Returns the number of geoms in the node.
Texture * find_texture(const string &name) const
Returns the first texture found applied to geometry at this node or below that matches the indicated ...
int get_num_primitives() const
Returns the number of GeomPrimitive objects stored within the Geom, each of which represents a number...
This is a set of zero or more NodePaths.
void remove_all_geoms()
Removes all the geoms from the node at once.