Panda3D
spriteParticleRenderer.cxx
Go to the documentation of this file.
1 /**
2  * PANDA 3D SOFTWARE
3  * Copyright (c) Carnegie Mellon University. All rights reserved.
4  *
5  * All use of this software is subject to the terms of the revised BSD
6  * license. You should have received a copy of this license along
7  * with this source code in a file named "LICENSE."
8  *
9  * @file spriteParticleRenderer.cxx
10  * @author charles
11  * @date 2000-07-13
12  */
13 
14 #include "spriteParticleRenderer.h"
15 #include "boundingSphere.h"
16 #include "geomNode.h"
17 #include "sequenceNode.h"
18 #include "nodePath.h"
19 #include "dcast.h"
20 #include "geom.h"
21 #include "geomVertexReader.h"
22 #include "geomVertexWriter.h"
23 #include "renderModeAttrib.h"
24 #include "texMatrixAttrib.h"
25 #include "texGenAttrib.h"
26 #include "textureAttrib.h"
27 #include "textureCollection.h"
28 #include "nodePathCollection.h"
29 #include "indent.h"
30 #include "config_particlesystem.h"
31 #include "pStatTimer.h"
32 
33 using std::max;
34 using std::min;
35 
36 PStatCollector SpriteParticleRenderer::_render_collector("App:Particles:Sprite:Render");
37 
38 /**
39  * 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  * copy constructor
71  */
75  _anims(copy._anims),
76  _color(copy._color),
77  _height(copy._height),
78  _width(copy._width),
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),
83  _theta(copy._theta),
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),
96  _pool_size(0),
97  _birth_list(copy._birth_list) {
98  init_geoms();
99 }
100 
101 /**
102  * destructor
103  */
107 }
108 
109 /**
110  * child dynamic copy
111  */
114  return new SpriteParticleRenderer(*this);
115 }
116 
117 
118 /**
119  * Pull either a set of textures from a SequenceNode or a single texture from
120  * a GeomNode. This function is called in both set_from_node() and
121  * add_from_node(). Notice the second parameter. This nodepath will
122  * reference the GeomNode holding the first texture in the returned
123  * TextureCollection.
124  */
125 int SpriteParticleRenderer::
126 extract_textures_from_node(const NodePath &node_path, NodePathCollection &np_col, TextureCollection &tex_col) {
127  NodePath tex_node_path = node_path;
128  NodePath geom_node_path;
129 
130  // Look for a sequence node first, in case they want animated texture
131  // sprites
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");
134  }
135 
136  // Nodepath contains a sequence node, attempt to read its textures.
137  if (!tex_node_path.is_empty()) {
138  int frame_count = tex_node_path.get_num_children();
139  // We do it this way in order to preserve the order of the textures in the
140  // sequence. If we use a find_all_textures() that order is lost.
141  for (int i = 0; i < frame_count; ++i) {
142  geom_node_path = tex_node_path.get_child(i);
143  if (!geom_node_path.is_empty()) {
144  // Since this is a SequenceNode, there will be only one texture on
145  // this geom_node_path.
146  tex_col.add_textures_from(geom_node_path.find_all_textures());
147  np_col.add_path(geom_node_path);
148  }
149  }
150  // If unsuccessful, try again as if the node were a normal GeomNode.
151  if (tex_col.get_num_textures() == 0) {
152  geom_node_path = NodePath();
153  tex_col.clear();
154  np_col.clear();
155  }
156  }
157 
158  // If a sequence node is not found, we just want to look for a regular geom
159  // node.
160  if (geom_node_path.is_empty()) {
161  // Find the first GeomNode.
162  if (!node_path.is_empty() && node_path.node()->get_type() != GeomNode::get_class_type()) {
163  geom_node_path = node_path.find("**/+GeomNode");
164  if (geom_node_path.is_empty()) {
165  particlesystem_cat.error();
166  return 0;
167  }
168  } else {
169  geom_node_path = node_path;
170  }
171 
172  // Grab the first texture.
173  tex_col.add_texture(geom_node_path.find_texture("*"));
174  if (tex_col.get_num_textures() < 1) {
175  particlesystem_cat.error()
176  << geom_node_path << " does not contain a texture.\n";
177  return 0;
178  } else {
179  np_col.add_path(geom_node_path);
180  }
181  }
182  return 1;
183 }
184 
185 /**
186  * If the source type is important, use this one.
187  *
188  * model and node should lead to node_path like this: node_path =
189  * loader.loadModel(model).find(node)
190  *
191  * This will remove all previously add textures and resize the renderer to
192  * match the new geometry.
193  */
195 set_from_node(const NodePath &node_path, const std::string &model, const std::string &node, bool size_from_texels) {
196  // Clear all texture information
197  _anims.clear();
198  add_from_node(node_path,model,node,size_from_texels,true);
199 }
200 
201 /**
202  * Sets the properties on this renderer from the geometry referenced by the
203  * indicated NodePath. This should be a reference to a GeomNode or a
204  * SequenceNode; it extracts out the texture and UV range from the node.
205  *
206  * This will remove all previously added textures and animations. It will
207  * also resize the renderer to match this new geometry.
208  *
209  * If node_path refers to a GeomNode(or has one beneath it) the texture, its
210  * size, and UV data will be extracted from that.
211  *
212  * If node_path references a SequenceNode(or has one beneath it) with multiple
213  * GeomNodes beneath it, the size data will correspond only to the first
214  * GeomNode found with a valid texture, while the texture and UV information
215  * will be stored for each individual node.
216  *
217  * If size_from_texels is true, the particle size is based on the number of
218  * texels in the source image; otherwise, it is based on the size of the first
219  * polygon found in the node.
220  *
221  * model and node are the two items used to construct node_path. If the
222  * source type is important, use set_from_node(NodePath,string,string,bool)
223  * instead.
224  */
226 set_from_node(const NodePath &node_path, bool size_from_texels) {
227  nassertv(!node_path.is_empty());
228  // Clear all texture information
229  _anims.clear();
230  add_from_node(node_path,size_from_texels,true);
231 }
232 
233 /**
234  * This will allow the renderer to randomly choose from more than one texture
235  * or sequence at particle birth.
236  *
237  * If the source type is important, use this one.
238  *
239  * model and node should lead to node_path like this: node_path =
240  * loader.loadModel(model).find(node)
241  *
242  * If resize is true, or if there are no textures currently on the renderer,
243  * it will force the renderer to use the size information from this node from
244  * now on. (Default is false)
245  */
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();
249  if (anim_count == 0)
250  resize = true;
251  add_from_node(node_path,size_from_texels,resize);
252  if (anim_count < (int)_anims.size()) {
253  get_last_anim()->set_source_info(model,node);
254  }
255 }
256 
257 /**
258  * This will allow the renderer to randomly choose from more than one texture
259  * or sequence at particle birth.
260  *
261  * If resize is true, or if there are no textures currently on the renderer,
262  * it will force the renderer to use the size information from this node from
263  * now on. (Default is false)
264  */
266 add_from_node(const NodePath &node_path, bool size_from_texels, bool resize) {
267  nassertv(!node_path.is_empty());
268 
269  NodePathCollection np_col;
270  TextureCollection tex_col;
271 
272  if (_anims.empty())
273  resize = true;
274 
275  // Load the found textures into the renderer.
276  if (extract_textures_from_node(node_path,np_col,tex_col)) {
277  pvector< LTexCoord > ll,ur;
278  GeomNode *gnode = nullptr;
279  const Geom *geom;
280  const GeomPrimitive *primitive;
281 
282  for (int i = 0; i < np_col.get_num_paths(); ++i) {
283  // Get the node from which we'll extract the geometry information.
284  gnode = DCAST(GeomNode, np_col[i].node());
285 
286  // Now examine the UV's of the first Geom within the GeomNode.
287  nassertv(gnode->get_num_geoms() > 0);
288  geom = gnode->get_geom(0);
289 
290  bool got_texcoord = false;
291  LTexCoord min_uv(0.0f, 0.0f);
292  LTexCoord max_uv(0.0f, 0.0f);
293 
294  GeomVertexReader texcoord(geom->get_vertex_data(),
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);
299  for (int vi = 0; vi < primitive->get_num_vertices(); ++vi) {
300  int vert = primitive->get_vertex(vi);
301  texcoord.set_row_unsafe(vert);
302 
303  if (!got_texcoord) {
304  min_uv = max_uv = texcoord.get_data2();
305  got_texcoord = true;
306 
307  } else {
308  const LVecBase2 &uv = texcoord.get_data2();
309 
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]);
314  }
315  }
316  }
317  }
318 
319  if (got_texcoord) {
320  // We don't really pay attention to orientation of UV's here; a minor
321  // flaw. We assume the minimum is in the lower-left, and the maximum
322  // is in the upper-right.
323  ll.push_back(min_uv);
324  ur.push_back(max_uv);
325  }
326  }
327 
328  _anims.push_back(new SpriteAnim(tex_col,ll,ur));
329 
330  if (resize) {
331  gnode = DCAST(GeomNode, np_col[0].node());
332  geom = gnode->get_geom(0);
333 
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);
337 
338  GeomVertexReader vertex(geom->get_vertex_data(),
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);
343  for (int vi = 0; vi < primitive->get_num_vertices(); ++vi) {
344  int vert = primitive->get_vertex(vi);
345  vertex.set_row_unsafe(vert);
346 
347  if (!got_vertex) {
348  min_xyz = max_xyz = vertex.get_data3();
349  got_vertex = true;
350 
351  } else {
352  const LVecBase3 &xyz = vertex.get_data3();
353 
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]);
360  }
361  }
362  }
363  }
364 
365  if (got_vertex) {
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]);
369 
370  if (size_from_texels) {
371  // If size_from_texels is true, we get the particle size from the
372  // number of texels in the source image.
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);
375  } else {
376  // If size_from_texels is false, we get the particle size from the
377  // size of the polygon.
378  set_size(width, height);
379  }
380 
381  } else {
382  // With no vertices, just punt.
383  set_size(1.0f, 1.0f);
384  }
385  }
386  init_geoms();
387  }
388 }
389 
390 /**
391  * reallocate the vertex pool.
392  */
393 void SpriteParticleRenderer::
394 resize_pool(int new_size) {
395  if (new_size != _pool_size) {
396  _pool_size = new_size;
397  init_geoms();
398  }
399 }
400 
401 /**
402  * initializes everything, called on traumatic events such as construction and
403  * serious particlesystem modifications
404  */
405 void SpriteParticleRenderer::
406 init_geoms() {
407  CPT(RenderState) state = _render_state;
408  SpriteAnim *anim;
409  int anim_count = _anims.size();
410  int i,j;
411 
412  // Setup format
413  PT(GeomVertexArrayFormat) array_format = new GeomVertexArrayFormat
414  (InternalName::get_vertex(), 3, Geom::NT_stdfloat, Geom::C_point,
415  InternalName::get_color(), 1, Geom::NT_packed_dabc, Geom::C_color);
416 
417  if (_animate_theta || _theta != 0.0f) {
418  array_format->add_column
419  (InternalName::get_rotate(), 1, Geom::NT_stdfloat, Geom::C_other);
420  }
421 
422  _base_y_scale = _initial_y_scale;
423  _aspect_ratio = _width / _height;
424 
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;
427 
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);
432  }
433 
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,
438  Geom::C_other);
439  }
440 
441  CPT(GeomVertexFormat) format = GeomVertexFormat::register_format
442  (new GeomVertexFormat(array_format));
443 
444  // Reset render() data structures
445  for (i = 0; i < (int)_ttl_count.size(); ++i) {
446  PANDA_FREE_ARRAY(_ttl_count[i]);
447  }
448  _anim_size.resize(anim_count);
449  _ttl_count.clear();
450  _ttl_count.resize(anim_count);
451 
452  // Reset sprite primitive data in order to prepare for next pass.
453  _sprite_primitive.clear();
454  _sprites.clear();
455  _vdata.clear();
456  _sprite_writer.clear();
457 
458  GeomNode *render_node = get_render_node();
459  render_node->remove_all_geoms();
460 
461  // For each animation...
462  for (i = 0; i < anim_count; ++i) {
463  anim = _anims[i];
464  _anim_size[i] = anim->get_num_frames();
465 
466  _sprite_primitive.push_back(pvector<PT(Geom)>());
467  _sprites.push_back(pvector<PT(GeomPoints)>());
468  _vdata.push_back(pvector<PT(GeomVertexData)>());
469  _sprite_writer.push_back(pvector<SpriteWriter>());
470 
471  // For each frame of the animation...
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));
478  geom->add_primitive(_sprites[i][j]);
479 
480  // This will be overwritten in render(), but we had to have some initial
481  // value
482  _sprite_writer[i].push_back(SpriteWriter());
483 
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));
488 
489  // Build a transform to convert the texture coordinates to the ll, ur
490  // space.
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;
494 
495  CPT(TransformState) ts = TransformState::make_pos_rotate_scale2d(ul, 0.0f, sc);
496  state = state->add_attrib(TexMatrixAttrib::make(TextureStage::get_default(), ts));
497  }
498 
499  render_node->add_geom(_sprite_primitive[i][j], state);
500  }
501  }
502 
503  nassertv(render_node->check_valid());
504 }
505 
506 /**
507  * child birth, one of those 'there-if-we-want-it' things. not really too
508  * useful here, so it turns out we don't really want it.
509  */
510 void SpriteParticleRenderer::
511 birth_particle(int index) {
512  _birth_list.push_back(index);
513 }
514 
515 /**
516  * child death
517  */
518 void SpriteParticleRenderer::
519 kill_particle(int) {
520 }
521 
522 /**
523  * big child render. populates the geom node.
524  */
525 void SpriteParticleRenderer::
526 render(pvector< PT(PhysicsObject) >& po_vector, int ttl_particles) {
527  PStatTimer t1(_render_collector);
528  // There is no texture data available, exit.
529  if (_anims.empty()) {
530  return;
531  }
532 
533  BaseParticle *cur_particle;
534  int remaining_particles = ttl_particles;
535  int i,j; // loop counters
536  int anim_count = _anims.size(); // number of animations
537  int frame; // frame index, used in indicating which frame to use when not animated
538  // First, since this is the only time we have access to the actual
539  // particles, do some delayed initialization.
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) {
543  cur_particle = (BaseParticle*)po_vector[*vIter].p();
544  i = int(NORMALIZED_RAND()*anim_count);
545 
546  // If there are multiple animations to choose from, choose one at
547  // random for this new particle
548  cur_particle->set_index(i < anim_count?i:i-1);
549 
550  // This is an experimental age offset so that the animations don't
551  // appear synchronized. If we are using animations, try to vary the
552  // frame flipping a bit for particles in the same litter. A similar
553  // effect might be a achieved by using a small lifespan spread value
554  // on the factory.
555 
556  // Perhaps we should look into other methods. The age offset doesn't
557  // seem to be cutting it.
558  if (_animate_frames) {
559  cur_particle->set_age(cur_particle->get_age()+i/10.0*cur_particle->get_lifespan());
560  }
561  }
562  }
563  }
564  _birth_list.clear();
565 
566  // Create vertex writers for each of the possible geoms. Could possibly be
567  // changed to only create writers for geoms that would be used according to
568  // the animation configuration.
569  for (i = 0; i < anim_count; ++i) {
570  for (j = 0; j < _anim_size[i]; ++j) {
571  // Set the particle per frame counts to 0.
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());
578  }
579  }
580 
581  // init the aabb
582  _aabb_min.set(99999.0f, 99999.0f, 99999.0f);
583  _aabb_max.set(-99999.0f, -99999.0f, -99999.0f);
584 
585  // run through every filled slot
586  for (i = 0; i < (int)po_vector.size(); i++) {
587  cur_particle = (BaseParticle *) po_vector[i].p();
588 
589  if (!cur_particle->get_alive()) {
590  continue;
591  }
592 
593  LPoint3 position = cur_particle->get_position();
594 
595  // x aabb adjust
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];
600 
601  // y aabb adjust
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];
606 
607  // z aabb adjust
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];
612 
613 
614  PN_stdfloat t = cur_particle->get_parameterized_age();
615  int anim_index = cur_particle->get_index();
616 
617  // If an animation has been removed, we need to reassign those particles
618  // assigned to the removed animation.
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);
623  }
624 
625  // Find the frame
626  if (_animate_frames) {
627  if (_animate_frames_rate == 0.0f) {
628  frame = (int)(t*_anim_size[anim_index]);
629  } else {
630  frame = (int)fmod(cur_particle->get_age()*_animate_frames_rate+1,_anim_size[anim_index]);
631  }
632  } else {
633  frame = _animate_frames_index;
634  }
635 
636  // Quick check make sure our math above didn't result in an invalid frame.
637  frame = (frame < _anim_size[anim_index]) ? frame : (_anim_size[anim_index]-1);
638  ++_ttl_count[anim_index][frame];
639 
640  // Calculate the color This is where we'll want to give the renderer the
641  // new color
642  LColor c = _color_interpolation_manager->generateColor(t);
643 
644  int alphamode=get_alpha_mode();
645  if (alphamode != PR_ALPHA_NONE) {
646  if (alphamode == PR_ALPHA_OUT)
647  c[3] *= (1.0f - t) * get_user_alpha();
648  else if (alphamode == PR_ALPHA_IN)
649  c[3] *= t * get_user_alpha();
650  else if (alphamode == PR_ALPHA_IN_OUT) {
651  c[3] *= 2.0f * min(t, 1.0f - t) * get_user_alpha();
652  }
653  else {
654  assert(alphamode == PR_ALPHA_USER);
655  c[3] *= get_user_alpha();
656  }
657  }
658 
659  // Send the data on its way...
660  _sprite_writer[anim_index][frame].vertex.add_data3(position);
661  _sprite_writer[anim_index][frame].color.add_data4(c);
662 
663  PN_stdfloat current_x_scale = _initial_x_scale;
664  PN_stdfloat current_y_scale = _initial_y_scale;
665 
666  if (_animate_x_ratio || _animate_y_ratio) {
667  if (_blend_method == PP_BLEND_CUBIC) {
668  t = CUBIC_T(t);
669  }
670 
671  if (_animate_x_ratio) {
672  current_x_scale = (_initial_x_scale +
673  (t * (_final_x_scale - _initial_x_scale)));
674  }
675  if (_animate_y_ratio) {
676  current_y_scale = (_initial_y_scale +
677  (t * (_final_y_scale - _initial_y_scale)));
678  }
679  }
680 
681  if (_sprite_writer[anim_index][frame].size.has_column()) {
682  _sprite_writer[anim_index][frame].size.add_data1f(current_y_scale * _height);
683  }
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);
686  }
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);
691  }
692 
693  // maybe jump out early?
694  remaining_particles--;
695  if (remaining_particles == 0) {
696  break;
697  }
698  }
699  int n = 0;
700  GeomNode *render_node = get_render_node();
701 
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();
706 
707  // We have to reassign the GeomVertexData and GeomPrimitive to the Geom,
708  // and the Geom to the GeomNode, in case it got flattened away.
709  _sprite_primitive[i][j]->set_primitive(0, _sprites[i][j]);
710  _sprite_primitive[i][j]->set_vertex_data(_vdata[i][j]);
711 
712  render_node->set_geom(n, _sprite_primitive[i][j]);
713  ++n;
714  }
715  }
716 
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]);
721  }
722  }
723  } else {
724  for (i = 0; i < anim_count; ++i) {
725  _sprites[i][_animate_frames_index]->add_next_vertices(_ttl_count[i][_animate_frames_index]);
726  }
727  }
728 
729  // done filling geompoint node, now do the bb stuff
730  LPoint3 aabb_center = _aabb_min + ((_aabb_max - _aabb_min) * 0.5f);
731  PN_stdfloat radius = (aabb_center - _aabb_min).length();
732 
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());
736  BoundingSphere sphere(aabb_center, radius);
737  _sprite_primitive[i][j]->set_bounds(&sphere);
738  }
739  }
740 
741  get_render_node()->mark_internal_bounds_stale();
742  nassertv(render_node->check_valid());
743  _animation_removed = false;
744 }
745 
746 /**
747  * Write a string representation of this instance to <out>.
748  */
750 output(std::ostream &out) const {
751  #ifndef NDEBUG //[
752  out<<"SpriteParticleRenderer";
753  #endif //] NDEBUG
754 }
755 
756 /**
757  * Write a string representation of this instance to <out>.
758  */
760 write(std::ostream &out, int indent_level) const {
761  indent(out, indent_level) << "SpriteParticleRenderer:\n";
762  // indent(out, indent_level + 2) << "_sprite_primitive
763  // "<<_sprite_primitive<<"\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";
777  BaseParticleRenderer::write(out, indent_level + 2);
778 }
Geom
A container for geometry primitives.
Definition: geom.h:54
SpriteParticleRenderer::set_size
void set_size(PN_stdfloat width, PN_stdfloat height)
Sets the size of each particle in world units.
Definition: spriteParticleRenderer.I:131
SpriteParticleRenderer::SpriteParticleRenderer
SpriteParticleRenderer(Texture *tex=nullptr)
constructor
Definition: spriteParticleRenderer.cxx:42
NodePath::get_child
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:337
indent
std::ostream & indent(std::ostream &out, int indent_level)
A handy function for doing text formatting.
Definition: indent.cxx:20
nodePath.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
SpriteParticleRenderer::set_texture
void set_texture(Texture *tex, PN_stdfloat texels_per_unit=1.0f)
Sets the renderer up to render the entire texture image.
Definition: spriteParticleRenderer.I:23
geomVertexWriter.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
BaseParticleRenderer::write
virtual void write(std::ostream &out, int indent=0) const
Write a string representation of this instance to <out>.
Definition: baseParticleRenderer.cxx:92
sequenceNode.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
TextureCollection
Manages a list of Texture objects, as returned by TexturePool::find_all_textures().
Definition: textureCollection.h:25
pvector< LTexCoord >
NodePath::find_all_textures
TextureCollection find_all_textures() const
Returns a list of a textures applied to geometry at this node and below.
Definition: nodePath.cxx:3933
GeomVertexData
This defines the actual numeric vertex data stored in a Geom, in the structure defined by a particula...
Definition: geomVertexData.h:68
BoundingSphere
This defines a bounding sphere, consisting of a center and a radius.
Definition: boundingSphere.h:25
NodePathCollection::add_path
void add_path(const NodePath &node_path)
Adds a new NodePath to the collection.
Definition: nodePathCollection.cxx:29
GeomVertexReader::has_column
bool has_column() const
Returns true if a valid data type has been successfully set, or false if the data type does not exist...
Definition: geomVertexReader.I:284
geomVertexReader.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
spriteParticleRenderer.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
dcast.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
GeomNode::set_geom
void set_geom(int n, Geom *geom)
Replaces the nth Geom of the node with a new pointer.
Definition: geomNode.cxx:635
GeomPoints
Defines a series of disconnected points.
Definition: geomPoints.h:23
pStatTimer.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
GeomVertexWriter
This object provides a high-level interface for quickly writing a sequence of numeric values from a v...
Definition: geomVertexWriter.h:55
GeomVertexReader
This object provides a high-level interface for quickly reading a sequence of numeric values from a v...
Definition: geomVertexReader.h:47
Texture
Represents a texture object, which is typically a single 2-d image but may also represent a 1-d or 3-...
Definition: texture.h:71
NodePath::get_num_children
int get_num_children(Thread *current_thread=Thread::get_current_thread()) const
Returns the number of children of the referenced node.
Definition: nodePath.I:328
GeomPrimitive::get_vertex
get_vertex
Returns the ith vertex index in the table.
Definition: geomPrimitive.h:99
SpriteParticleRenderer::set_from_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.
Definition: spriteParticleRenderer.cxx:226
SpriteParticleRenderer::add_from_node
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...
Definition: spriteParticleRenderer.cxx:266
GeomNode::get_num_geoms
get_num_geoms
Returns the number of geoms in the node.
Definition: geomNode.h:71
RenderState
This represents a unique collection of RenderAttrib objects that correspond to a particular renderabl...
Definition: renderState.h:47
textureCollection.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
SpriteParticleRenderer::make_copy
virtual BaseParticleRenderer * make_copy()
child dynamic copy
Definition: spriteParticleRenderer.cxx:113
PStatTimer
A lightweight class that can be used to automatically start and stop a PStatCollector around a sectio...
Definition: pStatTimer.h:30
GeomNode
A node that holds Geom objects, renderable pieces of geometry.
Definition: geomNode.h:34
texGenAttrib.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
SpriteParticleRenderer::output
virtual void output(std::ostream &out) const
Write a string representation of this instance to <out>.
Definition: spriteParticleRenderer.cxx:750
renderModeAttrib.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
SpriteAnim
Helper class used by SpriteParticleRenderer to keep track of its textures and their respective UVs an...
Definition: spriteParticleRenderer.h:71
TextureCollection::add_texture
void add_texture(Texture *texture)
Adds a new Texture to the collection.
Definition: textureCollection.cxx:45
ColorInterpolationManager
High level class for color interpolation.
Definition: colorInterpolationManager.h:269
GeomPrimitive::get_num_vertices
get_num_vertices
Returns the number of indices used by all the primitives in this object.
Definition: geomPrimitive.h:99
TransformState
Indicates a coordinate-system transform on vertices.
Definition: transformState.h:54
boundingSphere.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PStatCollector
A lightweight class that represents a single element that may be timed and/or counted via stats.
Definition: pStatCollector.h:43
PhysicsObject::get_position
LPoint3 get_position() const
Position Query.
Definition: physicsObject.I:161
TextureCollection::clear
void clear()
Removes all Textures from the collection.
Definition: textureCollection.cxx:166
geom.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
NodePath::find
NodePath find(const std::string &path) const
Searches for a node below the referenced node that matches the indicated string.
Definition: nodePath.cxx:314
GeomVertexReader::get_data3
const LVecBase3 & get_data3()
Returns the data associated with the read row, expressed as a 3-component value, and advances the rea...
Definition: geomVertexReader.I:577
GeomVertexFormat
This class defines the physical layout of the vertex data stored within a Geom.
Definition: geomVertexFormat.h:55
TextureCollection::add_textures_from
void add_textures_from(const TextureCollection &other)
Adds all the Textures indicated in the other collection to this texture.
Definition: textureCollection.cxx:97
Geom::add_primitive
void add_primitive(const GeomPrimitive *primitive)
Inserts a new GeomPrimitive structure to the Geom object.
Definition: geom.I:116
NodePath
NodePath is the fundamental system for disambiguating instances, and also provides a higher-level int...
Definition: nodePath.h:159
PhysicsObject
A body on which physics will be applied.
Definition: physicsObject.h:27
config_particlesystem.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
TextureCollection::get_num_textures
get_num_textures
Returns the number of Textures in the collection.
Definition: textureCollection.h:50
texMatrixAttrib.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
nodePathCollection.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
textureAttrib.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
NodePathCollection::get_num_paths
get_num_paths
Returns the number of NodePaths in the collection.
Definition: nodePathCollection.h:47
BaseParticle
An individual, physically-modelable particle abstract base class.
Definition: baseParticle.h:23
BaseParticleRenderer::get_user_alpha
PN_stdfloat get_user_alpha() const
gets alpha for "user" alpha mode
Definition: baseParticleRenderer.I:59
SpriteWriter
Helper class used by SpriteParticleRenderer to keep track of the various GeomVertexWriters associated...
Definition: spriteParticleRenderer.h:40
NodePath::find_texture
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 ...
Definition: nodePath.cxx:3912
GeomVertexReader::set_row_unsafe
void set_row_unsafe(int row)
Sets the start row to the indicated value, without internal checks.
Definition: geomVertexReader.I:316
geomNode.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
TextureStage::get_default
get_default
Returns the default TextureStage that will be used for all texturing that does not name a particular ...
Definition: textureStage.h:205
BaseParticle::get_theta
virtual PN_stdfloat get_theta() const
for spriteParticleRenderer
Definition: baseParticle.cxx:46
indent.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
SpriteParticleRenderer::write
virtual void write(std::ostream &out, int indent_level=0) const
Write a string representation of this instance to <out>.
Definition: spriteParticleRenderer.cxx:760
GeomVertexReader::get_data2
const LVecBase2 & get_data2()
Returns the data associated with the read row, expressed as a 2-component value, and advances the rea...
Definition: geomVertexReader.I:564
GeomNode::check_valid
bool check_valid() const
Verifies that the each Geom within the GeomNode reference vertices that actually exist within its Geo...
Definition: geomNode.cxx:653
SpriteParticleRenderer
Renders a particle system with high-speed nasty trick sprites.
Definition: spriteParticleRenderer.h:154
NodePath::node
PandaNode * node() const
Returns the referenced node of the path.
Definition: nodePath.I:227
GeomNode::add_geom
void add_geom(Geom *geom, const RenderState *state=RenderState::make_empty())
Adds a new Geom to the node.
Definition: geomNode.cxx:584
BaseParticleRenderer
Pure virtual particle renderer base class.
Definition: baseParticleRenderer.h:32
BaseParticleRenderer::get_render_node
GeomNode * get_render_node() const
Query the geomnode pointer.
Definition: baseParticleRenderer.I:18
NodePathCollection::clear
void clear()
Removes all NodePaths from the collection.
Definition: nodePathCollection.cxx:150
GeomNode::remove_all_geoms
void remove_all_geoms()
Removes all the geoms from the node at once.
Definition: geomNode.I:126
GeomVertexArrayFormat
This describes the structure of a single array within a Geom data.
Definition: geomVertexArrayFormat.h:47
SpriteParticleRenderer::~SpriteParticleRenderer
virtual ~SpriteParticleRenderer()
destructor
Definition: spriteParticleRenderer.cxx:105
GeomPrimitive
This is an abstract base class for a family of classes that represent the fundamental geometry primit...
Definition: geomPrimitive.h:56
NodePath::is_empty
bool is_empty() const
Returns true if the NodePath contains no nodes.
Definition: nodePath.I:188
NodePathCollection
This is a set of zero or more NodePaths.
Definition: nodePathCollection.h:26