36 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),
77 _height(copy._height),
79 _initial_x_scale(copy._initial_x_scale),
80 _final_x_scale(copy._final_x_scale),
81 _initial_y_scale(copy._initial_y_scale),
82 _final_y_scale(copy._final_y_scale),
84 _base_y_scale(copy._base_y_scale),
85 _aspect_ratio(copy._aspect_ratio),
86 _animate_frames_rate(copy._animate_frames_rate),
87 _animate_frames_index(copy._animate_frames_index),
88 _animate_x_ratio(copy._animate_x_ratio),
89 _animate_y_ratio(copy._animate_y_ratio),
90 _animate_theta(copy._animate_theta),
91 _alpha_disable(copy._alpha_disable),
92 _animate_frames(copy._animate_frames),
93 _animation_removed(true),
94 _blend_method(copy._blend_method),
95 _color_interpolation_manager(copy._color_interpolation_manager),
97 _birth_list(copy._birth_list) {
125 int SpriteParticleRenderer::
132 if (!tex_node_path.
is_empty() && tex_node_path.
node()->get_type() != SequenceNode::get_class_type()) {
133 tex_node_path = node_path.
find(
"**/+SequenceNode");
141 for (
int i = 0; i < frame_count; ++i) {
142 geom_node_path = tex_node_path.
get_child(i);
162 if (!node_path.
is_empty() && node_path.
node()->get_type() != GeomNode::get_class_type()) {
163 geom_node_path = node_path.
find(
"**/+GeomNode");
165 particlesystem_cat.error();
169 geom_node_path = node_path;
175 particlesystem_cat.error()
176 << geom_node_path <<
" does not contain a texture.\n";
195 set_from_node(
const NodePath &node_path,
const std::string &model,
const std::string &node,
bool size_from_texels) {
247 add_from_node(
const NodePath &node_path,
const std::string &model,
const std::string &node,
bool size_from_texels,
bool resize) {
248 int anim_count = _anims.size();
252 if (anim_count < (
int)_anims.size()) {
253 get_last_anim()->set_source_info(model,node);
276 if (extract_textures_from_node(node_path,np_col,tex_col)) {
282 for (
int i = 0; i < np_col.get_num_paths(); ++i) {
284 gnode = DCAST(
GeomNode, np_col[i].node());
288 geom = gnode->get_geom(0);
290 bool got_texcoord =
false;
291 LTexCoord min_uv(0.0f, 0.0f);
292 LTexCoord max_uv(0.0f, 0.0f);
295 InternalName::get_texcoord());
296 if (texcoord.has_column()) {
297 for (
size_t pi = 0; pi < geom->get_num_primitives(); ++pi) {
298 primitive = geom->get_primitive(pi);
301 texcoord.set_row_unsafe(vert);
304 min_uv = max_uv = texcoord.get_data2();
308 const LVecBase2 &uv = texcoord.get_data2();
310 min_uv[0] = min(min_uv[0], uv[0]);
311 max_uv[0] = max(max_uv[0], uv[0]);
312 min_uv[1] = min(min_uv[1], uv[1]);
313 max_uv[1] = max(max_uv[1], uv[1]);
323 ll.push_back(min_uv);
324 ur.push_back(max_uv);
328 _anims.push_back(
new SpriteAnim(tex_col,ll,ur));
331 gnode = DCAST(
GeomNode, np_col[0].node());
332 geom = gnode->get_geom(0);
334 bool got_vertex =
false;
335 LVertex min_xyz(0.0f, 0.0f, 0.0f);
336 LVertex max_xyz(0.0f, 0.0f, 0.0f);
339 InternalName::get_vertex());
340 if (vertex.has_column()) {
341 for (
size_t pi = 0; pi < geom->get_num_primitives(); ++pi) {
342 primitive = geom->get_primitive(pi);
345 vertex.set_row_unsafe(vert);
348 min_xyz = max_xyz = vertex.get_data3();
352 const LVecBase3 &xyz = vertex.get_data3();
354 min_xyz[0] = min(min_xyz[0], xyz[0]);
355 max_xyz[0] = max(max_xyz[0], xyz[0]);
356 min_xyz[1] = min(min_xyz[1], xyz[1]);
357 max_xyz[1] = max(max_xyz[1], xyz[1]);
358 min_xyz[2] = min(min_xyz[2], xyz[2]);
359 max_xyz[2] = max(max_xyz[2], xyz[2]);
366 PN_stdfloat width = max_xyz[0] - min_xyz[0];
367 PN_stdfloat height = max(max_xyz[1] - min_xyz[1],
368 max_xyz[2] - min_xyz[2]);
370 if (size_from_texels) {
373 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]);
374 set_size(y_texels * width / height, y_texels);
393 void SpriteParticleRenderer::
394 resize_pool(
int new_size) {
395 if (new_size != _pool_size) {
396 _pool_size = new_size;
405 void SpriteParticleRenderer::
409 int anim_count = _anims.size();
414 (InternalName::get_vertex(), 3, Geom::NT_stdfloat, Geom::C_point,
415 InternalName::get_color(), 1, Geom::NT_packed_dabc, Geom::C_color);
417 if (_animate_theta || _theta != 0.0f) {
418 array_format->add_column
419 (InternalName::get_rotate(), 1, Geom::NT_stdfloat, Geom::C_other);
422 _base_y_scale = _initial_y_scale;
423 _aspect_ratio = _width / _height;
425 PN_stdfloat final_x_scale = _animate_x_ratio ? _final_x_scale : _initial_x_scale;
426 PN_stdfloat final_y_scale = _animate_y_ratio ? _final_y_scale : _initial_y_scale;
428 if (_animate_y_ratio) {
429 _base_y_scale = max(_initial_y_scale, _final_y_scale);
430 array_format->add_column
431 (InternalName::get_size(), 1, Geom::NT_stdfloat, Geom::C_other);
434 if (_aspect_ratio * _initial_x_scale != _initial_y_scale ||
435 _aspect_ratio * final_x_scale != final_y_scale) {
436 array_format->add_column
437 (InternalName::get_aspect_ratio(), 1, Geom::NT_stdfloat,
445 for (i = 0; i < (int)_ttl_count.size(); ++i) {
446 PANDA_FREE_ARRAY(_ttl_count[i]);
448 _anim_size.resize(anim_count);
450 _ttl_count.resize(anim_count);
453 _sprite_primitive.clear();
456 _sprite_writer.clear();
462 for (i = 0; i < anim_count; ++i) {
464 _anim_size[i] = anim->get_num_frames();
472 for (j = 0; j < _anim_size[i]; ++j) {
473 _ttl_count[i] = (
int *)PANDA_MALLOC_ARRAY(_anim_size[i] *
sizeof(
int));
474 _vdata[i].push_back(
new GeomVertexData(
"sprite_particles", format, Geom::UH_stream));
475 PT(
Geom) geom =
new Geom(_vdata[i][j]);
476 _sprite_primitive[i].push_back((
Geom*)geom);
477 _sprites[i].push_back(
new GeomPoints(Geom::UH_stream));
484 state = state->add_attrib(RenderModeAttrib::make(RenderModeAttrib::M_unchanged, _base_y_scale * _height,
true));
485 if (anim->get_frame(j) !=
nullptr) {
486 state = state->add_attrib(TextureAttrib::make(anim->get_frame(j)));
487 state = state->add_attrib(TexGenAttrib::make(TextureStage::get_default(), TexGenAttrib::M_point_sprite));
491 LPoint2 ul(anim->get_ll(j)[0], anim->get_ur(j)[1]);
492 LPoint2 lr(anim->get_ur(j)[0], anim->get_ll(j)[1]);
493 LVector2 sc = lr - ul;
495 CPT(
TransformState) ts = TransformState::make_pos_rotate_scale2d(ul, 0.0f, sc);
496 state = state->add_attrib(TexMatrixAttrib::make(TextureStage::get_default(), ts));
499 render_node->
add_geom(_sprite_primitive[i][j], state);
510 void SpriteParticleRenderer::
511 birth_particle(
int index) {
512 _birth_list.push_back(index);
518 void SpriteParticleRenderer::
525 void SpriteParticleRenderer::
529 if (_anims.empty()) {
534 int remaining_particles = ttl_particles;
536 int anim_count = _anims.size();
540 if (_animate_frames || anim_count) {
541 if (!_birth_list.empty()) {
542 for (vector_int::iterator vIter = _birth_list.begin(); vIter != _birth_list.end(); ++vIter) {
544 i = int(NORMALIZED_RAND()*anim_count);
548 cur_particle->set_index(i < anim_count?i:i-1);
558 if (_animate_frames) {
559 cur_particle->set_age(cur_particle->get_age()+i/10.0*cur_particle->get_lifespan());
569 for (i = 0; i < anim_count; ++i) {
570 for (j = 0; j < _anim_size[i]; ++j) {
572 memset(_ttl_count[i], 0, _anim_size[i]*
sizeof(
int));
573 _sprite_writer[i][j].vertex =
GeomVertexWriter(_vdata[i][j], InternalName::get_vertex());
574 _sprite_writer[i][j].color =
GeomVertexWriter(_vdata[i][j], InternalName::get_color());
575 _sprite_writer[i][j].rotate =
GeomVertexWriter(_vdata[i][j], InternalName::get_rotate());
576 _sprite_writer[i][j].size =
GeomVertexWriter(_vdata[i][j], InternalName::get_size());
577 _sprite_writer[i][j].aspect_ratio =
GeomVertexWriter(_vdata[i][j], InternalName::get_aspect_ratio());
582 _aabb_min.set(99999.0f, 99999.0f, 99999.0f);
583 _aabb_max.set(-99999.0f, -99999.0f, -99999.0f);
586 for (i = 0; i < (int)po_vector.size(); i++) {
589 if (!cur_particle->get_alive()) {
596 if (position[0] > _aabb_max[0])
597 _aabb_max[0] = position[0];
598 else if (position[0] < _aabb_min[0])
599 _aabb_min[0] = position[0];
602 if (position[1] > _aabb_max[1])
603 _aabb_max[1] = position[1];
604 else if (position[1] < _aabb_min[1])
605 _aabb_min[1] = position[1];
608 if (position[2] > _aabb_max[2])
609 _aabb_max[2] = position[2];
610 else if (position[2] < _aabb_min[2])
611 _aabb_min[2] = position[2];
614 PN_stdfloat t = cur_particle->get_parameterized_age();
615 int anim_index = cur_particle->get_index();
619 if(_animation_removed && (anim_index >= anim_count)) {
620 anim_index = int(NORMALIZED_RAND()*anim_count);
621 anim_index = anim_index<anim_count?anim_index:anim_index-1;
622 cur_particle->set_index(anim_index);
626 if (_animate_frames) {
627 if (_animate_frames_rate == 0.0f) {
628 frame = (int)(t*_anim_size[anim_index]);
630 frame = (int)fmod(cur_particle->get_age()*_animate_frames_rate+1,_anim_size[anim_index]);
633 frame = _animate_frames_index;
637 frame = (frame < _anim_size[anim_index]) ? frame : (_anim_size[anim_index]-1);
638 ++_ttl_count[anim_index][frame];
642 LColor c = _color_interpolation_manager->generateColor(t);
644 int alphamode=get_alpha_mode();
645 if (alphamode != PR_ALPHA_NONE) {
646 if (alphamode == PR_ALPHA_OUT)
648 else if (alphamode == PR_ALPHA_IN)
650 else if (alphamode == PR_ALPHA_IN_OUT) {
654 assert(alphamode == PR_ALPHA_USER);
660 _sprite_writer[anim_index][frame].vertex.add_data3(position);
661 _sprite_writer[anim_index][frame].color.add_data4(c);
663 PN_stdfloat current_x_scale = _initial_x_scale;
664 PN_stdfloat current_y_scale = _initial_y_scale;
666 if (_animate_x_ratio || _animate_y_ratio) {
667 if (_blend_method == PP_BLEND_CUBIC) {
671 if (_animate_x_ratio) {
672 current_x_scale = (_initial_x_scale +
673 (t * (_final_x_scale - _initial_x_scale)));
675 if (_animate_y_ratio) {
676 current_y_scale = (_initial_y_scale +
677 (t * (_final_y_scale - _initial_y_scale)));
681 if (_sprite_writer[anim_index][frame].size.has_column()) {
682 _sprite_writer[anim_index][frame].size.add_data1f(current_y_scale * _height);
684 if (_sprite_writer[anim_index][frame].aspect_ratio.has_column()) {
685 _sprite_writer[anim_index][frame].aspect_ratio.add_data1f(_aspect_ratio * current_x_scale / current_y_scale);
687 if (_animate_theta) {
688 _sprite_writer[anim_index][frame].rotate.add_data1f(cur_particle->
get_theta());
689 }
else if (_sprite_writer[anim_index][frame].rotate.has_column()) {
690 _sprite_writer[anim_index][frame].rotate.add_data1f(_theta);
694 remaining_particles--;
695 if (remaining_particles == 0) {
702 for (i = 0; i < anim_count; ++i) {
703 for (j = 0; j < _anim_size[i]; ++j) {
704 _sprites[i][j]->clear_vertices();
705 _sprite_writer[i][j].clear();
709 _sprite_primitive[i][j]->set_primitive(0, _sprites[i][j]);
710 _sprite_primitive[i][j]->set_vertex_data(_vdata[i][j]);
712 render_node->
set_geom(n, _sprite_primitive[i][j]);
717 if (_animate_frames) {
718 for (i = 0; i < anim_count; ++i) {
719 for (j = 0; j < _anim_size[i]; ++j) {
720 _sprites[i][j]->add_next_vertices(_ttl_count[i][j]);
724 for (i = 0; i < anim_count; ++i) {
725 _sprites[i][_animate_frames_index]->add_next_vertices(_ttl_count[i][_animate_frames_index]);
730 LPoint3 aabb_center = _aabb_min + ((_aabb_max - _aabb_min) * 0.5f);
731 PN_stdfloat radius = (aabb_center - _aabb_min).length();
733 for (i = 0; i < anim_count; ++i) {
734 for (j = 0; j < _anim_size[i]; ++j) {
735 nassertv(_sprite_primitive[i][j]->check_valid());
737 _sprite_primitive[i][j]->set_bounds(&sphere);
743 _animation_removed =
false;
752 out<<
"SpriteParticleRenderer";
760 write(std::ostream &out,
int indent_level)
const {
761 indent(out, indent_level) <<
"SpriteParticleRenderer:\n";
764 indent(out, indent_level + 2) <<
"_color "<<_color<<
"\n";
765 indent(out, indent_level + 2) <<
"_initial_x_scale "<<_initial_x_scale<<
"\n";
766 indent(out, indent_level + 2) <<
"_final_x_scale "<<_final_x_scale<<
"\n";
767 indent(out, indent_level + 2) <<
"_initial_y_scale "<<_initial_y_scale<<
"\n";
768 indent(out, indent_level + 2) <<
"_final_y_scale "<<_final_y_scale<<
"\n";
769 indent(out, indent_level + 2) <<
"_theta "<<_theta<<
"\n";
770 indent(out, indent_level + 2) <<
"_animate_x_ratio "<<_animate_x_ratio<<
"\n";
771 indent(out, indent_level + 2) <<
"_animate_y_ratio "<<_animate_y_ratio<<
"\n";
772 indent(out, indent_level + 2) <<
"_animate_theta "<<_animate_theta<<
"\n";
773 indent(out, indent_level + 2) <<
"_blend_method "<<_blend_method<<
"\n";
774 indent(out, indent_level + 2) <<
"_aabb_min "<<_aabb_min<<
"\n";
775 indent(out, indent_level + 2) <<
"_aabb_max "<<_aabb_max<<
"\n";
776 indent(out, indent_level + 2) <<
"_pool_size "<<_pool_size<<
"\n";
int get_num_children(Thread *current_thread=Thread::get_current_thread()) const
Returns the number of children of the referenced node.
GeomNode * get_render_node() const
Query the geomnode pointer.
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...
NodePath find(const std::string &path) const
Searches for a node below the referenced node that matches the indicated string.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
virtual void write(std::ostream &out, int indent_level=0) const
Write a string representation of this instance to <out>.
void add_path(const NodePath &node_path)
Adds a new NodePath to the collection.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
virtual void write(std::ostream &out, int indent=0) const
Write a string representation of this instance to <out>.
Defines a series of disconnected points.
bool is_empty() const
Returns true if the NodePath contains no nodes.
bool check_valid() const
Verifies that the each Geom within the GeomNode reference vertices that actually exist within its Geo...
get_vertex
Returns the ith vertex index in the table.
void clear()
Removes all NodePaths from the collection.
This defines a bounding sphere, consisting of a center and a radius.
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...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
Represents a texture object, which is typically a single 2-d image but may also represent a 1-d or 3-...
This is an abstract base class for a family of classes that represent the fundamental geometry primit...
get_num_vertices
Returns the number of indices used by all the primitives in this object.
void add_primitive(const GeomPrimitive *primitive)
Inserts a new GeomPrimitive structure to the Geom object.
A lightweight class that can be used to automatically start and stop a PStatCollector around a sectio...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void add_texture(Texture *texture)
Adds a new Texture to the collection.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
virtual ~SpriteParticleRenderer()
destructor
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
TextureCollection find_all_textures() const
Returns a list of a textures applied to geometry at this node and below.
A lightweight class that represents a single element that may be timed and/or counted via stats.
Pure virtual particle renderer base class.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
std::ostream & indent(std::ostream &out, int indent_level)
A handy function for doing text formatting.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
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...
Texture * find_texture(const std::string &name) const
Returns the first texture found applied to geometry at this node or below that matches the indicated ...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
A container for geometry primitives.
High level class for color interpolation.
Manages a list of Texture objects, as returned by TexturePool::find_all_textures().
virtual PN_stdfloat get_theta() const
for spriteParticleRenderer
PN_stdfloat get_user_alpha() const
gets alpha for "user" alpha mode
get_num_textures
Returns the number of Textures in the collection.
void clear()
Removes all Textures from the collection.
virtual BaseParticleRenderer * make_copy()
child dynamic copy
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.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PandaNode * node() const
Returns the referenced node of the path.
An individual, physically-modelable particle abstract base class.
get_num_geoms
Returns the number of geoms in the node.
This object provides a high-level interface for quickly reading a sequence of numeric values from a v...
LPoint3 get_position() const
Position Query.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
Helper class used by SpriteParticleRenderer to keep track of the various GeomVertexWriters associated...
void set_texture(Texture *tex, PN_stdfloat texels_per_unit=1.0f)
Sets the renderer up to render the entire texture image.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
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.
NodePath get_child(int n, Thread *current_thread=Thread::get_current_thread()) const
Returns a NodePath representing the nth child of the referenced node.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
A node that holds Geom objects, renderable pieces of geometry.
void add_geom(Geom *geom, const RenderState *state=RenderState::make_empty())
Adds a new Geom to the node.
virtual void output(std::ostream &out) const
Write a string representation of this instance to <out>.
SpriteParticleRenderer(Texture *tex=nullptr)
constructor
This is a set of zero or more NodePaths.
void remove_all_geoms()
Removes all the geoms from the node at once.