Panda3D
 All Classes Functions Variables Enumerations
spriteParticleRenderer.cxx
1 // Filename: spriteParticleRenderer.cxx
2 // Created by: charles (13Jul00)
3 //
4 ////////////////////////////////////////////////////////////////////
5 //
6 // PANDA 3D SOFTWARE
7 // Copyright (c) Carnegie Mellon University. All rights reserved.
8 //
9 // All use of this software is subject to the terms of the revised BSD
10 // license. You should have received a copy of this license along
11 // with this source code in a file named "LICENSE."
12 //
13 ////////////////////////////////////////////////////////////////////
14 
15 #include "spriteParticleRenderer.h"
16 #include "boundingSphere.h"
17 #include "geomNode.h"
18 #include "sequenceNode.h"
19 #include "nodePath.h"
20 #include "dcast.h"
21 #include "geom.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"
30 #include "indent.h"
31 #include "config_particlesystem.h"
32 #include "pStatTimer.h"
33 
34 PStatCollector SpriteParticleRenderer::_render_collector("App:Particles:Sprite:Render");
35 
36 ////////////////////////////////////////////////////////////////////
37 // Function : SpriteParticleRenderer::SpriteParticleRenderer
38 // Access : public
39 // Description : constructor
40 ////////////////////////////////////////////////////////////////////
43  BaseParticleRenderer(PR_ALPHA_NONE),
44  _color(LColor(1.0f, 1.0f, 1.0f, 1.0f)),
45  _height(1.0f),
46  _width(1.0f),
47  _initial_x_scale(1.0f),
48  _final_x_scale(1.0f),
49  _initial_y_scale(1.0f),
50  _final_y_scale(1.0f),
51  _theta(0.0f),
52  _base_y_scale(1.0f),
53  _aspect_ratio(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),
63  _color_interpolation_manager(new ColorInterpolationManager(_color)),
64  _pool_size(0) {
65  set_texture(tex);
66  init_geoms();
67 }
68 
69 ////////////////////////////////////////////////////////////////////
70 // Function : SpriteParticleRenderer::SpriteParticleRenderer
71 // Access : public
72 // Description : copy constructor
73 ////////////////////////////////////////////////////////////////////
76  BaseParticleRenderer(copy),
77  _anims(copy._anims),
78  _color(copy._color),
79  _height(copy._height),
80  _width(copy._width),
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),
85  _theta(copy._theta),
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),
98  _pool_size(0),
99  _birth_list(copy._birth_list) {
100  init_geoms();
101 }
102 
103 ////////////////////////////////////////////////////////////////////
104 // Function : SpriteParticleRenderer::~SpriteParticleRenderer
105 // Access : public
106 // Description : destructor
107 ////////////////////////////////////////////////////////////////////
111 }
112 
113 ////////////////////////////////////////////////////////////////////
114 // Function : SpriteParticleRenderer::make_copy
115 // Access : public
116 // Description : child dynamic copy
117 ////////////////////////////////////////////////////////////////////
120  return new SpriteParticleRenderer(*this);
121 }
122 
123 
124 ////////////////////////////////////////////////////////////////////
125 // Function : SpriteParticleRenderer::extract_textures_from_node
126 // Access : public
127 // Description : Pull either a set of textures from a SequenceNode or
128 // a single texture from a GeomNode. This function is called
129 // in both set_from_node() and add_from_node(). Notice the
130 // second parameter. This nodepath will reference the GeomNode
131 // holding the first texture in the returned TextureCollection.
132 ////////////////////////////////////////////////////////////////////
133 int SpriteParticleRenderer::
134 extract_textures_from_node(const NodePath &node_path, NodePathCollection &np_col, TextureCollection &tex_col) {
135  NodePath tex_node_path = node_path;
136  NodePath geom_node_path;
137 
138  // Look for a sequence node first, in case they want animated texture sprites
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");
141  }
142 
143  // Nodepath contains a sequence node, attempt to read its textures.
144  if (!tex_node_path.is_empty()) {
145  int frame_count = tex_node_path.get_num_children();
146  // We do it this way in order to preserve the order of the textures in the sequence.
147  // If we use a find_all_textures() that order is lost.
148  for (int i = 0; i < frame_count; ++i) {
149  geom_node_path = tex_node_path.get_child(i);
150  if (!geom_node_path.is_empty()) {
151  // Since this is a SequenceNode, there will be only one texture on this geom_node_path.
152  tex_col.add_textures_from(geom_node_path.find_all_textures());
153  np_col.add_path(geom_node_path);
154  }
155  }
156  // If unsuccessful, try again as if the node were a normal GeomNode.
157  if (tex_col.get_num_textures() == 0) {
158  geom_node_path = NodePath();
159  tex_col.clear();
160  np_col.clear();
161  }
162  }
163 
164  // If a sequence node is not found, we just want to look for a regular geom node.
165  if (geom_node_path.is_empty()) {
166  // Find the first GeomNode.
167  if (!node_path.is_empty() && node_path.node()->get_type() != GeomNode::get_class_type()) {
168  geom_node_path = node_path.find("**/+GeomNode");
169  if (geom_node_path.is_empty()) {
170  particlesystem_cat.error();
171  return 0;
172  }
173  } else {
174  geom_node_path = node_path;
175  }
176 
177  // Grab the first texture.
178  tex_col.add_texture(geom_node_path.find_texture("*"));
179  if (tex_col.get_num_textures() < 1) {
180  particlesystem_cat.error()
181  << geom_node_path << " does not contain a texture.\n";
182  return 0;
183  } else {
184  np_col.add_path(geom_node_path);
185  }
186  }
187  return 1;
188 }
189 
190 ////////////////////////////////////////////////////////////////////
191 // Function : SpriteParticleRenderer::set_from_node
192 // Access : public
193 // Description : If the source type is important, use this one.
194 //
195 // model and node should lead to node_path like this:
196 // node_path = loader.loadModel(model).find(node)
197 //
198 // This will remove all previously add textures and
199 // resize the renderer to match the new geometry.
200 ////////////////////////////////////////////////////////////////////
202 set_from_node(const NodePath &node_path, const string &model, const string &node, bool size_from_texels) {
203  // Clear all texture information
204  _anims.clear();
205  add_from_node(node_path,model,node,size_from_texels,true);
206 }
207 
208 ////////////////////////////////////////////////////////////////////
209 // Function : SpriteParticleRenderer::set_from_node
210 // Access : public
211 // Description : Sets the properties on this renderer from the geometry
212 // referenced by the indicated NodePath. This should be
213 // a reference to a GeomNode or a SequenceNode; it
214 // extracts out the texture and UV range from the node.
215 //
216 // This will remove all previously added textures and
217 // animations. It will also resize the renderer to match
218 // this new geometry.
219 //
220 // If node_path refers to a GeomNode(or has one beneath it)
221 // the texture, its size, and UV data will be extracted
222 // from that.
223 //
224 // If node_path references a SequenceNode(or has one
225 // beneath it) with multiple GeomNodes beneath it,
226 // the size data will correspond only to the first
227 // GeomNode found with a valid texture, while the texture
228 // and UV information will be stored for each individual
229 // node.
230 //
231 // If size_from_texels is true, the particle size is
232 // based on the number of texels in the source image;
233 // otherwise, it is based on the size of the first
234 // polygon found in the node.
235 //
236 // model and node are the two items used to construct
237 // node_path. If the source type is important, use
238 // set_from_node(NodePath,string,string,bool) instead.
239 ////////////////////////////////////////////////////////////////////
241 set_from_node(const NodePath &node_path, bool size_from_texels) {
242  nassertv(!node_path.is_empty());
243  // Clear all texture information
244  _anims.clear();
245  add_from_node(node_path,size_from_texels,true);
246 }
247 
248 ////////////////////////////////////////////////////////////////////
249 // Function : SpriteParticleRenderer::add_from_node
250 // Access : public
251 // Description : This will allow the renderer to randomly choose
252 // from more than one texture or sequence at particle
253 // birth.
254 //
255 // If the source type is important, use this one.
256 //
257 // model and node should lead to node_path like this:
258 // node_path = loader.loadModel(model).find(node)
259 //
260 // If resize is true, or if there are no textures
261 // currently on the renderer, it will force the
262 // renderer to use the size information from this
263 // node from now on. (Default is false)
264 ////////////////////////////////////////////////////////////////////
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();
268  if (anim_count == 0)
269  resize = true;
270  add_from_node(node_path,size_from_texels,resize);
271  if (anim_count < (int)_anims.size()) {
272  get_last_anim()->set_source_info(model,node);
273  }
274 }
275 
276 ////////////////////////////////////////////////////////////////////
277 // Function : SpriteParticleRenderer::add_from_node
278 // Access : public
279 // Description : This will allow the renderer to randomly choose
280 // from more than one texture or sequence at particle
281 // birth.
282 //
283 // If resize is true, or if there are no textures
284 // currently on the renderer, it will force the
285 // renderer to use the size information from this
286 // node from now on. (Default is false)
287 ////////////////////////////////////////////////////////////////////
289 add_from_node(const NodePath &node_path, bool size_from_texels, bool resize) {
290  nassertv(!node_path.is_empty());
291 
292  NodePathCollection np_col;
293  TextureCollection tex_col;
294 
295  if (_anims.empty())
296  resize = true;
297 
298  // Load the found textures into the renderer.
299  if (extract_textures_from_node(node_path,np_col,tex_col)) {
300  pvector< LTexCoord > ll,ur;
301  GeomNode *gnode = NULL;
302  const Geom *geom;
303  const GeomPrimitive *primitive;
304 
305  for (int i = 0; i < np_col.get_num_paths(); ++i) {
306  // Get the node from which we'll extract the geometry information.
307  gnode = DCAST(GeomNode, np_col[i].node());
308 
309  // Now examine the UV's of the first Geom within the GeomNode.
310  nassertv(gnode->get_num_geoms() > 0);
311  geom = gnode->get_geom(0);
312 
313  bool got_texcoord = false;
314  LTexCoord min_uv(0.0f, 0.0f);
315  LTexCoord max_uv(0.0f, 0.0f);
316 
317  GeomVertexReader texcoord(geom->get_vertex_data(),
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);
322  for (int vi = 0; vi < primitive->get_num_vertices(); ++vi) {
323  int vert = primitive->get_vertex(vi);
324  texcoord.set_row_unsafe(vert);
325 
326  if (!got_texcoord) {
327  min_uv = max_uv = texcoord.get_data2();
328  got_texcoord = true;
329 
330  } else {
331  const LVecBase2 &uv = texcoord.get_data2();
332 
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]);
337  }
338  }
339  }
340  }
341 
342  if (got_texcoord) {
343  // We don't really pay attention to orientation of UV's here; a
344  // minor flaw. We assume the minimum is in the lower-left, and
345  // the maximum is in the upper-right.
346  ll.push_back(min_uv);
347  ur.push_back(max_uv);
348  }
349  }
350 
351  _anims.push_back(new SpriteAnim(tex_col,ll,ur));
352 
353  if (resize) {
354  gnode = DCAST(GeomNode, np_col[0].node());
355  geom = gnode->get_geom(0);
356 
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);
360 
361  GeomVertexReader vertex(geom->get_vertex_data(),
362  InternalName::get_vertex());
363  if (vertex.has_column()) {
364  for (int pi = 0; pi < geom->get_num_primitives(); ++pi) {
365  primitive = geom->get_primitive(pi);
366  for (int vi = 0; vi < primitive->get_num_vertices(); ++vi) {
367  int vert = primitive->get_vertex(vi);
368  vertex.set_row_unsafe(vert);
369 
370  if (!got_vertex) {
371  min_xyz = max_xyz = vertex.get_data3();
372  got_vertex = true;
373 
374  } else {
375  const LVecBase3 &xyz = vertex.get_data3();
376 
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]);
383  }
384  }
385  }
386  }
387 
388  if (got_vertex) {
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]);
392 
393  if (size_from_texels) {
394  // If size_from_texels is true, we get the particle size from the
395  // number of texels in the source image.
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);
398  } else {
399  // If size_from_texels is false, we get the particle size from
400  // the size of the polygon.
401  set_size(width, height);
402  }
403 
404  } else {
405  // With no vertices, just punt.
406  set_size(1.0f, 1.0f);
407  }
408  }
409  init_geoms();
410  }
411 }
412 
413 ////////////////////////////////////////////////////////////////////
414 // Function : SpriteParticleRenderer::resize_pool
415 // Access : private
416 // Description : reallocate the vertex pool.
417 ////////////////////////////////////////////////////////////////////
418 void SpriteParticleRenderer::
419 resize_pool(int new_size) {
420  if (new_size != _pool_size) {
421  _pool_size = new_size;
422  init_geoms();
423  }
424 }
425 
426 ////////////////////////////////////////////////////////////////////
427 // Function : SpriteParticleRenderer::init_geoms
428 // Access : public
429 // Description : initializes everything, called on traumatic events
430 // such as construction and serious particlesystem
431 // modifications
432 ////////////////////////////////////////////////////////////////////
433 void SpriteParticleRenderer::
434 init_geoms() {
435  CPT(RenderState) state = _render_state;
436  SpriteAnim *anim;
437  int anim_count = _anims.size();
438  int i,j;
439 
440  // Setup format
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);
444 
445  if (_animate_theta || _theta != 0.0f) {
446  array_format->add_column
447  (InternalName::get_rotate(), 1, Geom::NT_stdfloat, Geom::C_other);
448  }
449 
450  _base_y_scale = _initial_y_scale;
451  _aspect_ratio = _width / _height;
452 
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;
455 
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);
460  }
461 
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,
466  Geom::C_other);
467  }
468 
469  CPT(GeomVertexFormat) format = GeomVertexFormat::register_format
470  (new GeomVertexFormat(array_format));
471 
472  // Reset render() data structures
473  for (i = 0; i < (int)_ttl_count.size(); ++i) {
474  PANDA_FREE_ARRAY(_ttl_count[i]);
475  }
476  _anim_size.resize(anim_count);
477  _ttl_count.clear();
478  _ttl_count.resize(anim_count);
479 
480  // Reset sprite primitive data in order to prepare for next pass.
481  _sprite_primitive.clear();
482  _sprites.clear();
483  _vdata.clear();
484  _sprite_writer.clear();
485 
486  GeomNode *render_node = get_render_node();
487  render_node->remove_all_geoms();
488 
489  // For each animation...
490  for (i = 0; i < anim_count; ++i) {
491  anim = _anims[i];
492  _anim_size[i] = anim->get_num_frames();
493 
494  _sprite_primitive.push_back(pvector<PT(Geom)>());
495  _sprites.push_back(pvector<PT(GeomPoints)>());
496  _vdata.push_back(pvector<PT(GeomVertexData)>());
497  _sprite_writer.push_back(pvector<SpriteWriter>());
498 
499  // For each frame of the animation...
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);
505  _sprites[i].push_back(new GeomPoints(Geom::UH_stream));
506  geom->add_primitive(_sprites[i][j]);
507 
508  // This will be overwritten in render(), but we had to have some initial value
509  _sprite_writer[i].push_back(SpriteWriter());
510 
511  state = state->add_attrib(RenderModeAttrib::make(RenderModeAttrib::M_unchanged, _base_y_scale * _height, true));
512  if (anim->get_frame(j) != (Texture *)NULL) {
513  state = state->add_attrib(TextureAttrib::make(anim->get_frame(j)));
514  state = state->add_attrib(TexGenAttrib::make(TextureStage::get_default(), TexGenAttrib::M_point_sprite));
515 
516  // Build a transform to convert the texture coordinates to the
517  // ll, ur space.
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]);
520  LVector2 sc = lr - ul;
521 
522  CPT(TransformState) ts = TransformState::make_pos_rotate_scale2d(ul, 0.0f, sc);
523  state = state->add_attrib(TexMatrixAttrib::make(TextureStage::get_default(), ts));
524  }
525 
526  render_node->add_geom(_sprite_primitive[i][j], state);
527  }
528  }
529 
530  nassertv(render_node->check_valid());
531 }
532 
533 ////////////////////////////////////////////////////////////////////
534 // Function : SpriteParticleRenderer::birth_particle
535 // Access : private
536 // Description : child birth, one of those 'there-if-we-want-it'
537 // things. not really too useful here, so it turns
538 // out we don't really want it.
539 ////////////////////////////////////////////////////////////////////
541 birth_particle(int index) {
542  _birth_list.push_back(index);
543 }
544 
545 ////////////////////////////////////////////////////////////////////
546 // Function : SpriteParticleRenderer::kill_particle
547 // Access : private
548 // Description : child death
549 ////////////////////////////////////////////////////////////////////
550 void SpriteParticleRenderer::
551 kill_particle(int) {
552 }
553 
554 ////////////////////////////////////////////////////////////////////
555 // Function : SpriteParticleRenderer::render
556 // Access : private
557 // Description : big child render. populates the geom node.
558 ////////////////////////////////////////////////////////////////////
559 void SpriteParticleRenderer::
560 render(pvector< PT(PhysicsObject) >& po_vector, int ttl_particles) {
561  PStatTimer t1(_render_collector);
562  // There is no texture data available, exit.
563  if (_anims.empty()) {
564  return;
565  }
566 
567  BaseParticle *cur_particle;
568  int remaining_particles = ttl_particles;
569  int i,j; // loop counters
570  int anim_count = _anims.size(); // number of animations
571  int frame; // frame index, used in indicating which frame to use when not animated
572  // First, since this is the only time we have access to the actual particles, do some delayed initialization.
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) {
576  cur_particle = (BaseParticle*)po_vector[*vIter].p();
577  i = int(NORMALIZED_RAND()*anim_count);
578 
579  // If there are multiple animations to choose from, choose one at random for this new particle
580  cur_particle->set_index(i < anim_count?i:i-1);
581 
582  // This is an experimental age offset so that the animations don't appear synchronized.
583  // If we are using animations, try to vary the frame flipping a bit for particles in the same litter.
584  // A similar effect might be a achieved by using a small lifespan spread value on the factory.
585 
586  // Perhaps we should look into other methods. The age offset doesn't seem to be cutting it.
587  if (_animate_frames) {
588  cur_particle->set_age(cur_particle->get_age()+i/10.0*cur_particle->get_lifespan());
589  }
590  }
591  }
592  }
593  _birth_list.clear();
594 
595  // Create vertex writers for each of the possible geoms.
596  // Could possibly be changed to only create writers for geoms that would be used
597  // according to the animation configuration.
598  for (i = 0; i < anim_count; ++i) {
599  for (j = 0; j < _anim_size[i]; ++j) {
600  // Set the particle per frame counts to 0.
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());
607  }
608  }
609 
610  // init the aabb
611  _aabb_min.set(99999.0f, 99999.0f, 99999.0f);
612  _aabb_max.set(-99999.0f, -99999.0f, -99999.0f);
613 
614  // run through every filled slot
615  for (i = 0; i < (int)po_vector.size(); i++) {
616  cur_particle = (BaseParticle *) po_vector[i].p();
617 
618  if (!cur_particle->get_alive()) {
619  continue;
620  }
621 
622  LPoint3 position = cur_particle->get_position();
623 
624  // x aabb adjust
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];
629 
630  // y aabb adjust
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];
635 
636  // z aabb adjust
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];
641 
642 
643  PN_stdfloat t = cur_particle->get_parameterized_age();
644  int anim_index = cur_particle->get_index();
645 
646  // If an animation has been removed, we need to reassign
647  // those particles assigned to the removed animation.
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);
652  }
653 
654  // Find the frame
655  if (_animate_frames) {
656  if (_animate_frames_rate == 0.0f) {
657  frame = (int)(t*_anim_size[anim_index]);
658  } else {
659  frame = (int)fmod(cur_particle->get_age()*_animate_frames_rate+1,_anim_size[anim_index]);
660  }
661  } else {
662  frame = _animate_frames_index;
663  }
664 
665  // Quick check make sure our math above didn't result in an invalid frame.
666  frame = (frame < _anim_size[anim_index]) ? frame : (_anim_size[anim_index]-1);
667  ++_ttl_count[anim_index][frame];
668 
669  // Calculate the color
670  // This is where we'll want to give the renderer the new color
671  LColor c = _color_interpolation_manager->generateColor(t);
672 
673  int alphamode=get_alpha_mode();
674  if (alphamode != PR_ALPHA_NONE) {
675  if (alphamode == PR_ALPHA_OUT)
676  c[3] *= (1.0f - t) * get_user_alpha();
677  else if (alphamode == PR_ALPHA_IN)
678  c[3] *= t * get_user_alpha();
679  else if (alphamode == PR_ALPHA_IN_OUT) {
680  c[3] *= 2.0f * min(t, 1.0f - t) * get_user_alpha();
681  }
682  else {
683  assert(alphamode == PR_ALPHA_USER);
684  c[3] *= get_user_alpha();
685  }
686  }
687 
688  // Send the data on its way...
689  _sprite_writer[anim_index][frame].vertex.add_data3(position);
690  _sprite_writer[anim_index][frame].color.add_data4(c);
691 
692  PN_stdfloat current_x_scale = _initial_x_scale;
693  PN_stdfloat current_y_scale = _initial_y_scale;
694 
695  if (_animate_x_ratio || _animate_y_ratio) {
696  if (_blend_method == PP_BLEND_CUBIC) {
697  t = CUBIC_T(t);
698  }
699 
700  if (_animate_x_ratio) {
701  current_x_scale = (_initial_x_scale +
702  (t * (_final_x_scale - _initial_x_scale)));
703  }
704  if (_animate_y_ratio) {
705  current_y_scale = (_initial_y_scale +
706  (t * (_final_y_scale - _initial_y_scale)));
707  }
708  }
709 
710  if (_sprite_writer[anim_index][frame].size.has_column()) {
711  _sprite_writer[anim_index][frame].size.add_data1f(current_y_scale * _height);
712  }
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);
715  }
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);
720  }
721 
722  // maybe jump out early?
723  remaining_particles--;
724  if (remaining_particles == 0) {
725  break;
726  }
727  }
728  int n = 0;
729  GeomNode *render_node = get_render_node();
730 
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();
735 
736  // We have to reassign the GeomVertexData and GeomPrimitive to
737  // the Geom, and the Geom to the GeomNode, in case it got
738  // flattened away.
739  _sprite_primitive[i][j]->set_primitive(0, _sprites[i][j]);
740  _sprite_primitive[i][j]->set_vertex_data(_vdata[i][j]);
741 
742  render_node->set_geom(n, _sprite_primitive[i][j]);
743  ++n;
744  }
745  }
746 
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]);
751  }
752  }
753  } else {
754  for (i = 0; i < anim_count; ++i) {
755  _sprites[i][_animate_frames_index]->add_next_vertices(_ttl_count[i][_animate_frames_index]);
756  }
757  }
758 
759  // done filling geompoint node, now do the bb stuff
760  LPoint3 aabb_center = _aabb_min + ((_aabb_max - _aabb_min) * 0.5f);
761  PN_stdfloat radius = (aabb_center - _aabb_min).length();
762 
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());
766  BoundingSphere sphere(aabb_center, radius);
767  _sprite_primitive[i][j]->set_bounds(&sphere);
768  }
769  }
770 
772  nassertv(render_node->check_valid());
773  _animation_removed = false;
774 }
775 
776 ////////////////////////////////////////////////////////////////////
777 // Function : output
778 // Access : Public
779 // Description : Write a string representation of this instance to
780 // <out>.
781 ////////////////////////////////////////////////////////////////////
783 output(ostream &out) const {
784  #ifndef NDEBUG //[
785  out<<"SpriteParticleRenderer";
786  #endif //] NDEBUG
787 }
788 
789 ////////////////////////////////////////////////////////////////////
790 // Function : write
791 // Access : Public
792 // Description : Write a string representation of this instance to
793 // <out>.
794 ////////////////////////////////////////////////////////////////////
796 write(ostream &out, int indent_level) const {
797  indent(out, indent_level) << "SpriteParticleRenderer:\n";
798  // indent(out, indent_level + 2) << "_sprite_primitive "<<_sprite_primitive<<"\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";
812  BaseParticleRenderer::write(out, indent_level + 2);
813 }
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.
Definition: geomNode.cxx:699
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.
Definition: lvecBase3.h:105
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.
Definition: nodePath.cxx:434
Defines a series of disconnected points.
Definition: geomPoints.h:25
PN_stdfloat get_user_alpha() const
gets alpha for &quot;user&quot; 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 &lt;out&gt;.
virtual PN_stdfloat get_theta() const
for spriteParticleRenderer
A body on which physics will be applied.
Definition: physicsObject.h:29
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-...
Definition: texture.h:75
int get_num_children(Thread *current_thread=Thread::get_current_thread()) const
Returns the number of children of the referenced node.
Definition: nodePath.I:406
This is an abstract base class for a family of classes that represent the fundamental geometry primit...
Definition: geomPrimitive.h:63
SpriteParticleRenderer(Texture *tex=(Texture *) NULL)
constructor
PandaNode * node() const
Returns the referenced node of the path.
Definition: nodePath.I:284
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...
Definition: pStatTimer.h:34
This is a three-component point in space (as opposed to a three-component vector, which represents a ...
Definition: lpoint3.h:99
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.
Definition: pvector.h:39
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...
Definition: pandaNode.cxx:2467
NodePath get_child(int n, Thread *current_thread=Thread::get_current_thread()) const
Returns a NodePath representing the nth child of the referenced node.
Definition: nodePath.I:418
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.
Definition: geom.h:58
High level class for color interpolation.
This is the base class for all two-component vectors and points.
Definition: lvecBase2.h:105
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 &lt;out&gt;.
This represents a unique collection of RenderAttrib objects that correspond to a particular renderabl...
Definition: renderState.h:53
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.
Definition: lvecBase4.h:111
Applies a transform matrix to UV&#39;s before they are rendered.
This is a two-component vector offset.
Definition: lvector2.h:91
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.
Definition: baseParticle.h:26
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 &lt;out&gt;.
bool is_empty() const
Returns true if the NodePath contains no nodes.
Definition: nodePath.I:236
Specifies how polygons are to be drawn.
This is a two-component point in space.
Definition: lpoint2.h:92
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.
Definition: nodePath.cxx:4532
bool check_valid() const
Verifies that the each Geom within the GeomNode reference vertices that actually exist within its Geo...
Definition: geomNode.cxx:720
static TextureStage * get_default()
Returns the default TextureStage that will be used for all texturing that does not name a particular ...
Definition: textureStage.I:766
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.
Definition: textureStage.h:38
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...
Definition: nodePath.h:165
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.
Definition: geomNode.h:37
int get_num_geoms() const
Returns the number of geoms in the node.
Definition: geomNode.I:46
Texture * find_texture(const string &name) const
Returns the first texture found applied to geometry at this node or below that matches the indicated ...
Definition: nodePath.cxx:4505
int get_num_primitives() const
Returns the number of GeomPrimitive objects stored within the Geom, each of which represents a number...
Definition: geom.I:110
This is a set of zero or more NodePaths.
void remove_all_geoms()
Removes all the geoms from the node at once.
Definition: geomNode.I:159