Panda3D
Loading...
Searching...
No Matches
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
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"
31#include "pStatTimer.h"
32
33using std::max;
34using std::min;
35
36PStatCollector 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 */
108
109/**
110 * child dynamic copy
111 */
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 */
125int SpriteParticleRenderer::
126extract_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 */
195set_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 */
226set_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 */
247add_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 */
266add_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)) {
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 */
393void SpriteParticleRenderer::
394resize_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 */
405void SpriteParticleRenderer::
406init_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 */
510void SpriteParticleRenderer::
511birth_particle(int index) {
512 _birth_list.push_back(index);
513}
514
515/**
516 * child death
517 */
518void SpriteParticleRenderer::
519kill_particle(int) {
520}
521
522/**
523 * big child render. populates the geom node.
524 */
525void SpriteParticleRenderer::
526render(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 */
750output(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 */
760write(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}
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
Pure virtual particle renderer base class.
GeomNode * get_render_node() const
Query the geomnode pointer.
PN_stdfloat get_user_alpha() const
gets alpha for "user" alpha mode
virtual void write(std::ostream &out, int indent=0) const
Write a string representation of this instance to <out>.
An individual, physically-modelable particle abstract base class.
virtual PN_stdfloat get_theta() const
for spriteParticleRenderer
This defines a bounding sphere, consisting of a center and a radius.
High level class for color interpolation.
A node that holds Geom objects, renderable pieces of geometry.
Definition geomNode.h:34
void add_geom(Geom *geom, const RenderState *state=RenderState::make_empty())
Adds a new Geom to the node.
Definition geomNode.cxx:612
get_num_geoms
Returns the number of geoms in the node.
Definition geomNode.h:71
void remove_all_geoms()
Removes all the geoms from the node at once.
Definition geomNode.I:126
void set_geom(int n, Geom *geom)
Replaces the nth Geom of the node with a new pointer.
Definition geomNode.cxx:663
bool check_valid() const
Verifies that the each Geom within the GeomNode reference vertices that actually exist within its Geo...
Definition geomNode.cxx:681
Defines a series of disconnected points.
Definition geomPoints.h:23
This is an abstract base class for a family of classes that represent the fundamental geometry primit...
get_vertex
Returns the ith vertex index in the table.
get_num_vertices
Returns the number of indices used by all the primitives in this object.
This describes the structure of a single array within a Geom data.
This defines the actual numeric vertex data stored in a Geom, in the structure defined by a particula...
This class defines the physical layout of the vertex data stored within a Geom.
This object provides a high-level interface for quickly reading a sequence of numeric values from a v...
void set_row_unsafe(int row)
Sets the start row to the indicated value, without internal checks.
const LVecBase2 & get_data2()
Returns the data associated with the read row, expressed as a 2-component value, and advances the rea...
bool has_column() const
Returns true if a valid data type has been successfully set, or false if the data type does not exist...
const LVecBase3 & get_data3()
Returns the data associated with the read row, expressed as a 3-component value, and advances the rea...
This object provides a high-level interface for quickly writing a sequence of numeric values from a v...
A container for geometry primitives.
Definition geom.h:54
void add_primitive(const GeomPrimitive *primitive)
Inserts a new GeomPrimitive structure to the Geom object.
Definition geom.I:116
This is a set of zero or more NodePaths.
void add_path(const NodePath &node_path)
Adds a new NodePath to the collection.
get_num_paths
Returns the number of NodePaths in the collection.
void clear()
Removes all NodePaths from the collection.
NodePath is the fundamental system for disambiguating instances, and also provides a higher-level int...
Definition nodePath.h:159
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(const std::string &path) const
Searches for a node below the referenced node that matches the indicated string.
Definition nodePath.cxx:315
bool is_empty() const
Returns true if the NodePath contains no nodes.
Definition nodePath.I:188
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 ...
PandaNode * node() const
Returns the referenced node of the path.
Definition nodePath.I:227
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
TextureCollection find_all_textures() const
Returns a list of a textures applied to geometry at this node and below.
A lightweight class that represents a single element that may be timed and/or counted via stats.
A lightweight class that can be used to automatically start and stop a PStatCollector around a sectio...
Definition pStatTimer.h:30
A body on which physics will be applied.
get_position
Position Query.
This represents a unique collection of RenderAttrib objects that correspond to a particular renderabl...
Definition renderState.h:47
Helper class used by SpriteParticleRenderer to keep track of its textures and their respective UVs an...
Renders a particle system with high-speed nasty trick sprites.
void set_size(PN_stdfloat width, PN_stdfloat height)
Sets the size of each particle in world units.
virtual ~SpriteParticleRenderer()
destructor
SpriteParticleRenderer(Texture *tex=nullptr)
constructor
virtual BaseParticleRenderer * make_copy()
child dynamic copy
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...
virtual void output(std::ostream &out) const
Write a string representation of this instance to <out>.
void set_texture(Texture *tex, PN_stdfloat texels_per_unit=1.0f)
Sets the renderer up to render the entire texture image.
virtual void write(std::ostream &out, int indent_level=0) const
Write a string representation of this instance to <out>.
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.
Helper class used by SpriteParticleRenderer to keep track of the various GeomVertexWriters associated...
Manages a list of Texture objects, as returned by TexturePool::find_all_textures().
get_num_textures
Returns the number of Textures in the collection.
void add_texture(Texture *texture)
Adds a new Texture to the collection.
void add_textures_from(const TextureCollection &other)
Adds all the Textures indicated in the other collection to this texture.
void clear()
Removes all Textures from the collection.
get_default
Returns the default TextureStage that will be used for all texturing that does not name a particular ...
Represents a texture object, which is typically a single 2-d image but may also represent a 1-d or 3-...
Definition texture.h:72
Indicates a coordinate-system transform on vertices.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
std::ostream & indent(std::ostream &out, int indent_level)
A handy function for doing text formatting.
Definition indent.cxx:20
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.