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 */
41 SpriteParticleRenderer::
42 SpriteParticleRenderer(Texture *tex) :
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 */
72 SpriteParticleRenderer::
73 SpriteParticleRenderer(const SpriteParticleRenderer& copy) :
74 BaseParticleRenderer(copy),
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 */
104 SpriteParticleRenderer::
105 ~SpriteParticleRenderer() {
106 get_render_node()->remove_all_geoms();
107 }
108
109 /**
110 * child dynamic copy
111 */
112 BaseParticleRenderer *SpriteParticleRenderer::
113 make_copy() {
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 */
194 void SpriteParticleRenderer::
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 */
225 void SpriteParticleRenderer::
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 */
246 void SpriteParticleRenderer::
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 */
265 void SpriteParticleRenderer::
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 */
749 void SpriteParticleRenderer::
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 */
759 void SpriteParticleRenderer::
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 }
boundingSphere.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
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
BaseParticleRenderer::get_user_alpha
PN_stdfloat get_user_alpha() const
gets alpha for "user" alpha mode
Definition: baseParticleRenderer.I:59
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
BaseParticle
An individual, physically-modelable particle abstract base class.
Definition: baseParticle.h:23
BaseParticle::get_theta
virtual PN_stdfloat get_theta() const
for spriteParticleRenderer
Definition: baseParticle.cxx:46
BoundingSphere
This defines a bounding sphere, consisting of a center and a radius.
Definition: boundingSphere.h:25
ColorInterpolationManager
High level class for color interpolation.
Definition: colorInterpolationManager.h:269
GeomNode
A node that holds Geom objects, renderable pieces of geometry.
Definition: geomNode.h:34
GeomNode::add_geom
void add_geom(Geom *geom, const RenderState *state=RenderState::make_empty())
Adds a new Geom to the node.
Definition: geomNode.cxx:586
GeomNode::get_num_geoms
get_num_geoms
Returns the number of geoms in the node.
Definition: geomNode.h:71
GeomNode::remove_all_geoms
void remove_all_geoms()
Removes all the geoms from the node at once.
Definition: geomNode.I:126
GeomNode::set_geom
void set_geom(int n, Geom *geom)
Replaces the nth Geom of the node with a new pointer.
Definition: geomNode.cxx:637
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:655
GeomPoints
Defines a series of disconnected points.
Definition: geomPoints.h:23
GeomPrimitive
This is an abstract base class for a family of classes that represent the fundamental geometry primit...
Definition: geomPrimitive.h:56
GeomPrimitive::get_vertex
get_vertex
Returns the ith vertex index in the table.
Definition: geomPrimitive.h:99
GeomPrimitive::get_num_vertices
get_num_vertices
Returns the number of indices used by all the primitives in this object.
Definition: geomPrimitive.h:99
GeomVertexArrayFormat
This describes the structure of a single array within a Geom data.
Definition: geomVertexArrayFormat.h:47
GeomVertexData
This defines the actual numeric vertex data stored in a Geom, in the structure defined by a particula...
Definition: geomVertexData.h:68
GeomVertexFormat
This class defines the physical layout of the vertex data stored within a Geom.
Definition: geomVertexFormat.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
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
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
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::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
GeomVertexWriter
This object provides a high-level interface for quickly writing a sequence of numeric values from a v...
Definition: geomVertexWriter.h:55
Geom
A container for geometry primitives.
Definition: geom.h:54
Geom::add_primitive
void add_primitive(const GeomPrimitive *primitive)
Inserts a new GeomPrimitive structure to the Geom object.
Definition: geom.I:116
NodePathCollection
This is a set of zero or more NodePaths.
Definition: nodePathCollection.h:26
NodePathCollection::add_path
void add_path(const NodePath &node_path)
Adds a new NodePath to the collection.
Definition: nodePathCollection.cxx:29
NodePathCollection::get_num_paths
get_num_paths
Returns the number of NodePaths in the collection.
Definition: nodePathCollection.h:47
NodePathCollection::clear
void clear()
Removes all NodePaths from the collection.
Definition: nodePathCollection.cxx:150
NodePath
NodePath is the fundamental system for disambiguating instances, and also provides a higher-level int...
Definition: nodePath.h:159
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
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
NodePath::is_empty
bool is_empty() const
Returns true if the NodePath contains no nodes.
Definition: nodePath.I:188
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:3975
NodePath::node
PandaNode * node() const
Returns the referenced node of the path.
Definition: nodePath.I:227
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
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:3996
PStatCollector
A lightweight class that represents a single element that may be timed and/or counted via stats.
Definition: pStatCollector.h:43
PStatTimer
A lightweight class that can be used to automatically start and stop a PStatCollector around a sectio...
Definition: pStatTimer.h:30
PhysicsObject
A body on which physics will be applied.
Definition: physicsObject.h:27
PhysicsObject::get_position
get_position
Position Query.
Definition: physicsObject.h:105
RenderState
This represents a unique collection of RenderAttrib objects that correspond to a particular renderabl...
Definition: renderState.h:47
SpriteAnim
Helper class used by SpriteParticleRenderer to keep track of its textures and their respective UVs an...
Definition: spriteParticleRenderer.h:71
SpriteParticleRenderer
Renders a particle system with high-speed nasty trick sprites.
Definition: spriteParticleRenderer.h:154
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
virtual ~SpriteParticleRenderer()
destructor
Definition: spriteParticleRenderer.cxx:105
SpriteParticleRenderer::SpriteParticleRenderer
SpriteParticleRenderer(Texture *tex=nullptr)
constructor
Definition: spriteParticleRenderer.cxx:42
SpriteParticleRenderer::make_copy
virtual BaseParticleRenderer * make_copy()
child dynamic copy
Definition: spriteParticleRenderer.cxx:113
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
SpriteParticleRenderer::output
virtual void output(std::ostream &out) const
Write a string representation of this instance to <out>.
Definition: spriteParticleRenderer.cxx:750
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
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
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
SpriteWriter
Helper class used by SpriteParticleRenderer to keep track of the various GeomVertexWriters associated...
Definition: spriteParticleRenderer.h:40
TextureCollection
Manages a list of Texture objects, as returned by TexturePool::find_all_textures().
Definition: textureCollection.h:25
TextureCollection::get_num_textures
get_num_textures
Returns the number of Textures in the collection.
Definition: textureCollection.h:50
TextureCollection::add_texture
void add_texture(Texture *texture)
Adds a new Texture to the collection.
Definition: textureCollection.cxx:45
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
TextureCollection::clear
void clear()
Removes all Textures from the collection.
Definition: textureCollection.cxx:166
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:207
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
TransformState
Indicates a coordinate-system transform on vertices.
Definition: transformState.h:54
pvector< LTexCoord >
config_particlesystem.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
dcast.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
geomNode.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
geomVertexReader.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
geomVertexWriter.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
geom.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
indent
std::ostream & indent(std::ostream &out, int indent_level)
A handy function for doing text formatting.
Definition: indent.cxx:20
indent.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
nodePathCollection.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
nodePath.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
pStatTimer.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
renderModeAttrib.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
sequenceNode.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
spriteParticleRenderer.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
texGenAttrib.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
texMatrixAttrib.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
textureAttrib.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
textureCollection.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.