Panda3D
|
00001 // Filename: spriteParticleRenderer.cxx 00002 // Created by: charles (13Jul00) 00003 // 00004 //////////////////////////////////////////////////////////////////// 00005 // 00006 // PANDA 3D SOFTWARE 00007 // Copyright (c) Carnegie Mellon University. All rights reserved. 00008 // 00009 // All use of this software is subject to the terms of the revised BSD 00010 // license. You should have received a copy of this license along 00011 // with this source code in a file named "LICENSE." 00012 // 00013 //////////////////////////////////////////////////////////////////// 00014 00015 #include "spriteParticleRenderer.h" 00016 #include "boundingSphere.h" 00017 #include "geomNode.h" 00018 #include "sequenceNode.h" 00019 #include "nodePath.h" 00020 #include "dcast.h" 00021 #include "geom.h" 00022 #include "geomVertexReader.h" 00023 #include "geomVertexWriter.h" 00024 #include "renderModeAttrib.h" 00025 #include "texMatrixAttrib.h" 00026 #include "texGenAttrib.h" 00027 #include "textureAttrib.h" 00028 #include "textureCollection.h" 00029 #include "nodePathCollection.h" 00030 #include "indent.h" 00031 #include "config_particlesystem.h" 00032 #include "pStatTimer.h" 00033 00034 PStatCollector SpriteParticleRenderer::_render_collector("App:Particles:Sprite:Render"); 00035 00036 //////////////////////////////////////////////////////////////////// 00037 // Function : SpriteParticleRenderer::SpriteParticleRenderer 00038 // Access : public 00039 // Description : constructor 00040 //////////////////////////////////////////////////////////////////// 00041 SpriteParticleRenderer:: 00042 SpriteParticleRenderer(Texture *tex) : 00043 BaseParticleRenderer(PR_ALPHA_NONE), 00044 _color(LColor(1.0f, 1.0f, 1.0f, 1.0f)), 00045 _height(1.0f), 00046 _width(1.0f), 00047 _initial_x_scale(1.0f), 00048 _final_x_scale(1.0f), 00049 _initial_y_scale(1.0f), 00050 _final_y_scale(1.0f), 00051 _theta(0.0f), 00052 _base_y_scale(1.0f), 00053 _aspect_ratio(1.0f), 00054 _animate_frames_rate(0.0f), 00055 _animate_frames_index(0), 00056 _animate_x_ratio(false), 00057 _animate_y_ratio(false), 00058 _animate_theta(false), 00059 _alpha_disable(false), 00060 _animate_frames(false), 00061 _animation_removed(true), 00062 _blend_method(PP_BLEND_LINEAR), 00063 _color_interpolation_manager(new ColorInterpolationManager(_color)), 00064 _pool_size(0) { 00065 set_texture(tex); 00066 init_geoms(); 00067 } 00068 00069 //////////////////////////////////////////////////////////////////// 00070 // Function : SpriteParticleRenderer::SpriteParticleRenderer 00071 // Access : public 00072 // Description : copy constructor 00073 //////////////////////////////////////////////////////////////////// 00074 SpriteParticleRenderer:: 00075 SpriteParticleRenderer(const SpriteParticleRenderer& copy) : 00076 BaseParticleRenderer(copy), 00077 _anims(copy._anims), 00078 _color(copy._color), 00079 _height(copy._height), 00080 _width(copy._width), 00081 _initial_x_scale(copy._initial_x_scale), 00082 _final_x_scale(copy._final_x_scale), 00083 _initial_y_scale(copy._initial_y_scale), 00084 _final_y_scale(copy._final_y_scale), 00085 _theta(copy._theta), 00086 _base_y_scale(copy._base_y_scale), 00087 _aspect_ratio(copy._aspect_ratio), 00088 _animate_frames_rate(copy._animate_frames_rate), 00089 _animate_frames_index(copy._animate_frames_index), 00090 _animate_x_ratio(copy._animate_x_ratio), 00091 _animate_y_ratio(copy._animate_y_ratio), 00092 _animate_theta(copy._animate_theta), 00093 _alpha_disable(copy._alpha_disable), 00094 _animate_frames(copy._animate_frames), 00095 _animation_removed(true), 00096 _blend_method(copy._blend_method), 00097 _color_interpolation_manager(copy._color_interpolation_manager), 00098 _pool_size(0), 00099 _birth_list(copy._birth_list) { 00100 init_geoms(); 00101 } 00102 00103 //////////////////////////////////////////////////////////////////// 00104 // Function : SpriteParticleRenderer::~SpriteParticleRenderer 00105 // Access : public 00106 // Description : destructor 00107 //////////////////////////////////////////////////////////////////// 00108 SpriteParticleRenderer:: 00109 ~SpriteParticleRenderer() { 00110 get_render_node()->remove_all_geoms(); 00111 } 00112 00113 //////////////////////////////////////////////////////////////////// 00114 // Function : SpriteParticleRenderer::make_copy 00115 // Access : public 00116 // Description : child dynamic copy 00117 //////////////////////////////////////////////////////////////////// 00118 BaseParticleRenderer *SpriteParticleRenderer:: 00119 make_copy() { 00120 return new SpriteParticleRenderer(*this); 00121 } 00122 00123 00124 //////////////////////////////////////////////////////////////////// 00125 // Function : SpriteParticleRenderer::extract_textures_from_node 00126 // Access : public 00127 // Description : Pull either a set of textures from a SequenceNode or 00128 // a single texture from a GeomNode. This function is called 00129 // in both set_from_node() and add_from_node(). Notice the 00130 // second parameter. This nodepath will reference the GeomNode 00131 // holding the first texture in the returned TextureCollection. 00132 //////////////////////////////////////////////////////////////////// 00133 int SpriteParticleRenderer:: 00134 extract_textures_from_node(const NodePath &node_path, NodePathCollection &np_col, TextureCollection &tex_col) { 00135 NodePath tex_node_path = node_path; 00136 NodePath geom_node_path; 00137 00138 // Look for a sequence node first, in case they want animated texture sprites 00139 if (!tex_node_path.is_empty() && tex_node_path.node()->get_type() != SequenceNode::get_class_type()) { 00140 tex_node_path = node_path.find("**/+SequenceNode"); 00141 } 00142 00143 // Nodepath contains a sequence node, attempt to read its textures. 00144 if (!tex_node_path.is_empty()) { 00145 int frame_count = tex_node_path.get_num_children(); 00146 // We do it this way in order to preserve the order of the textures in the sequence. 00147 // If we use a find_all_textures() that order is lost. 00148 for (int i = 0; i < frame_count; ++i) { 00149 geom_node_path = tex_node_path.get_child(i); 00150 if (!geom_node_path.is_empty()) { 00151 // Since this is a SequenceNode, there will be only one texture on this geom_node_path. 00152 tex_col.add_textures_from(geom_node_path.find_all_textures()); 00153 np_col.add_path(geom_node_path); 00154 } 00155 } 00156 // If unsuccessful, try again as if the node were a normal GeomNode. 00157 if (tex_col.get_num_textures() == 0) { 00158 geom_node_path = NodePath(); 00159 tex_col.clear(); 00160 np_col.clear(); 00161 } 00162 } 00163 00164 // If a sequence node is not found, we just want to look for a regular geom node. 00165 if (geom_node_path.is_empty()) { 00166 // Find the first GeomNode. 00167 if (!node_path.is_empty() && node_path.node()->get_type() != GeomNode::get_class_type()) { 00168 geom_node_path = node_path.find("**/+GeomNode"); 00169 if (geom_node_path.is_empty()) { 00170 particlesystem_cat.error(); 00171 return 0; 00172 } 00173 } else { 00174 geom_node_path = node_path; 00175 } 00176 00177 // Grab the first texture. 00178 tex_col.add_texture(geom_node_path.find_texture("*")); 00179 if (tex_col.get_num_textures() < 1) { 00180 particlesystem_cat.error() 00181 << geom_node_path << " does not contain a texture.\n"; 00182 return 0; 00183 } else { 00184 np_col.add_path(geom_node_path); 00185 } 00186 } 00187 return 1; 00188 } 00189 00190 //////////////////////////////////////////////////////////////////// 00191 // Function : SpriteParticleRenderer::set_from_node 00192 // Access : public 00193 // Description : If the source type is important, use this one. 00194 // 00195 // model and node should lead to node_path like this: 00196 // node_path = loader.loadModel(model).find(node) 00197 // 00198 // This will remove all previously add textures and 00199 // resize the renderer to match the new geometry. 00200 //////////////////////////////////////////////////////////////////// 00201 void SpriteParticleRenderer:: 00202 set_from_node(const NodePath &node_path, const string &model, const string &node, bool size_from_texels) { 00203 // Clear all texture information 00204 _anims.clear(); 00205 add_from_node(node_path,model,node,size_from_texels,true); 00206 } 00207 00208 //////////////////////////////////////////////////////////////////// 00209 // Function : SpriteParticleRenderer::set_from_node 00210 // Access : public 00211 // Description : Sets the properties on this renderer from the geometry 00212 // referenced by the indicated NodePath. This should be 00213 // a reference to a GeomNode or a SequenceNode; it 00214 // extracts out the texture and UV range from the node. 00215 // 00216 // This will remove all previously added textures and 00217 // animations. It will also resize the renderer to match 00218 // this new geometry. 00219 // 00220 // If node_path refers to a GeomNode(or has one beneath it) 00221 // the texture, its size, and UV data will be extracted 00222 // from that. 00223 // 00224 // If node_path references a SequenceNode(or has one 00225 // beneath it) with multiple GeomNodes beneath it, 00226 // the size data will correspond only to the first 00227 // GeomNode found with a valid texture, while the texture 00228 // and UV information will be stored for each individual 00229 // node. 00230 // 00231 // If size_from_texels is true, the particle size is 00232 // based on the number of texels in the source image; 00233 // otherwise, it is based on the size of the first 00234 // polygon found in the node. 00235 // 00236 // model and node are the two items used to construct 00237 // node_path. If the source type is important, use 00238 // set_from_node(NodePath,string,string,bool) instead. 00239 //////////////////////////////////////////////////////////////////// 00240 void SpriteParticleRenderer:: 00241 set_from_node(const NodePath &node_path, bool size_from_texels) { 00242 nassertv(!node_path.is_empty()); 00243 // Clear all texture information 00244 _anims.clear(); 00245 add_from_node(node_path,size_from_texels,true); 00246 } 00247 00248 //////////////////////////////////////////////////////////////////// 00249 // Function : SpriteParticleRenderer::add_from_node 00250 // Access : public 00251 // Description : This will allow the renderer to randomly choose 00252 // from more than one texture or sequence at particle 00253 // birth. 00254 // 00255 // If the source type is important, use this one. 00256 // 00257 // model and node should lead to node_path like this: 00258 // node_path = loader.loadModel(model).find(node) 00259 // 00260 // If resize is true, or if there are no textures 00261 // currently on the renderer, it will force the 00262 // renderer to use the size information from this 00263 // node from now on. (Default is false) 00264 //////////////////////////////////////////////////////////////////// 00265 void SpriteParticleRenderer:: 00266 add_from_node(const NodePath &node_path, const string &model, const string &node, bool size_from_texels, bool resize) { 00267 int anim_count = _anims.size(); 00268 if (anim_count == 0) 00269 resize = true; 00270 add_from_node(node_path,size_from_texels,resize); 00271 if (anim_count < (int)_anims.size()) { 00272 get_last_anim()->set_source_info(model,node); 00273 } 00274 } 00275 00276 //////////////////////////////////////////////////////////////////// 00277 // Function : SpriteParticleRenderer::add_from_node 00278 // Access : public 00279 // Description : This will allow the renderer to randomly choose 00280 // from more than one texture or sequence at particle 00281 // birth. 00282 // 00283 // If resize is true, or if there are no textures 00284 // currently on the renderer, it will force the 00285 // renderer to use the size information from this 00286 // node from now on. (Default is false) 00287 //////////////////////////////////////////////////////////////////// 00288 void SpriteParticleRenderer:: 00289 add_from_node(const NodePath &node_path, bool size_from_texels, bool resize) { 00290 nassertv(!node_path.is_empty()); 00291 00292 NodePathCollection np_col; 00293 TextureCollection tex_col; 00294 00295 if (_anims.empty()) 00296 resize = true; 00297 00298 // Load the found textures into the renderer. 00299 if (extract_textures_from_node(node_path,np_col,tex_col)) { 00300 pvector< LTexCoord > ll,ur; 00301 GeomNode *gnode = NULL; 00302 const Geom *geom; 00303 const GeomPrimitive *primitive; 00304 00305 for (int i = 0; i < np_col.get_num_paths(); ++i) { 00306 // Get the node from which we'll extract the geometry information. 00307 gnode = DCAST(GeomNode, np_col[i].node()); 00308 00309 // Now examine the UV's of the first Geom within the GeomNode. 00310 nassertv(gnode->get_num_geoms() > 0); 00311 geom = gnode->get_geom(0); 00312 00313 bool got_texcoord = false; 00314 LTexCoord min_uv(0.0f, 0.0f); 00315 LTexCoord max_uv(0.0f, 0.0f); 00316 00317 GeomVertexReader texcoord(geom->get_vertex_data(), 00318 InternalName::get_texcoord()); 00319 if (texcoord.has_column()) { 00320 for (int pi = 0; pi < geom->get_num_primitives(); ++pi) { 00321 primitive = geom->get_primitive(pi); 00322 for (int vi = 0; vi < primitive->get_num_vertices(); ++vi) { 00323 int vert = primitive->get_vertex(vi); 00324 texcoord.set_row_unsafe(vert); 00325 00326 if (!got_texcoord) { 00327 min_uv = max_uv = texcoord.get_data2(); 00328 got_texcoord = true; 00329 00330 } else { 00331 const LVecBase2 &uv = texcoord.get_data2(); 00332 00333 min_uv[0] = min(min_uv[0], uv[0]); 00334 max_uv[0] = max(max_uv[0], uv[0]); 00335 min_uv[1] = min(min_uv[1], uv[1]); 00336 max_uv[1] = max(max_uv[1], uv[1]); 00337 } 00338 } 00339 } 00340 } 00341 00342 if (got_texcoord) { 00343 // We don't really pay attention to orientation of UV's here; a 00344 // minor flaw. We assume the minimum is in the lower-left, and 00345 // the maximum is in the upper-right. 00346 ll.push_back(min_uv); 00347 ur.push_back(max_uv); 00348 } 00349 } 00350 00351 _anims.push_back(new SpriteAnim(tex_col,ll,ur)); 00352 00353 if (resize) { 00354 gnode = DCAST(GeomNode, np_col[0].node()); 00355 geom = gnode->get_geom(0); 00356 00357 bool got_vertex = false; 00358 LVertex min_xyz(0.0f, 0.0f, 0.0f); 00359 LVertex max_xyz(0.0f, 0.0f, 0.0f); 00360 00361 GeomVertexReader vertex(geom->get_vertex_data(), 00362 InternalName::get_vertex()); 00363 if (vertex.has_column()) { 00364 for (int pi = 0; pi < geom->get_num_primitives(); ++pi) { 00365 primitive = geom->get_primitive(pi); 00366 for (int vi = 0; vi < primitive->get_num_vertices(); ++vi) { 00367 int vert = primitive->get_vertex(vi); 00368 vertex.set_row_unsafe(vert); 00369 00370 if (!got_vertex) { 00371 min_xyz = max_xyz = vertex.get_data3(); 00372 got_vertex = true; 00373 00374 } else { 00375 const LVecBase3 &xyz = vertex.get_data3(); 00376 00377 min_xyz[0] = min(min_xyz[0], xyz[0]); 00378 max_xyz[0] = max(max_xyz[0], xyz[0]); 00379 min_xyz[1] = min(min_xyz[1], xyz[1]); 00380 max_xyz[1] = max(max_xyz[1], xyz[1]); 00381 min_xyz[2] = min(min_xyz[2], xyz[2]); 00382 max_xyz[2] = max(max_xyz[2], xyz[2]); 00383 } 00384 } 00385 } 00386 } 00387 00388 if (got_vertex) { 00389 PN_stdfloat width = max_xyz[0] - min_xyz[0]; 00390 PN_stdfloat height = max(max_xyz[1] - min_xyz[1], 00391 max_xyz[2] - min_xyz[2]); 00392 00393 if (size_from_texels) { 00394 // If size_from_texels is true, we get the particle size from the 00395 // number of texels in the source image. 00396 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]); 00397 set_size(y_texels * width / height, y_texels); 00398 } else { 00399 // If size_from_texels is false, we get the particle size from 00400 // the size of the polygon. 00401 set_size(width, height); 00402 } 00403 00404 } else { 00405 // With no vertices, just punt. 00406 set_size(1.0f, 1.0f); 00407 } 00408 } 00409 init_geoms(); 00410 } 00411 } 00412 00413 //////////////////////////////////////////////////////////////////// 00414 // Function : SpriteParticleRenderer::resize_pool 00415 // Access : private 00416 // Description : reallocate the vertex pool. 00417 //////////////////////////////////////////////////////////////////// 00418 void SpriteParticleRenderer:: 00419 resize_pool(int new_size) { 00420 if (new_size != _pool_size) { 00421 _pool_size = new_size; 00422 init_geoms(); 00423 } 00424 } 00425 00426 //////////////////////////////////////////////////////////////////// 00427 // Function : SpriteParticleRenderer::init_geoms 00428 // Access : public 00429 // Description : initializes everything, called on traumatic events 00430 // such as construction and serious particlesystem 00431 // modifications 00432 //////////////////////////////////////////////////////////////////// 00433 void SpriteParticleRenderer:: 00434 init_geoms() { 00435 CPT(RenderState) state = _render_state; 00436 SpriteAnim *anim; 00437 int anim_count = _anims.size(); 00438 int i,j; 00439 00440 // Setup format 00441 PT(GeomVertexArrayFormat) array_format = new GeomVertexArrayFormat 00442 (InternalName::get_vertex(), 3, Geom::NT_stdfloat, Geom::C_point, 00443 InternalName::get_color(), 1, Geom::NT_packed_dabc, Geom::C_color); 00444 00445 if (_animate_theta || _theta != 0.0f) { 00446 array_format->add_column 00447 (InternalName::get_rotate(), 1, Geom::NT_stdfloat, Geom::C_other); 00448 } 00449 00450 _base_y_scale = _initial_y_scale; 00451 _aspect_ratio = _width / _height; 00452 00453 PN_stdfloat final_x_scale = _animate_x_ratio ? _final_x_scale : _initial_x_scale; 00454 PN_stdfloat final_y_scale = _animate_y_ratio ? _final_y_scale : _initial_y_scale; 00455 00456 if (_animate_y_ratio) { 00457 _base_y_scale = max(_initial_y_scale, _final_y_scale); 00458 array_format->add_column 00459 (InternalName::get_size(), 1, Geom::NT_stdfloat, Geom::C_other); 00460 } 00461 00462 if (_aspect_ratio * _initial_x_scale != _initial_y_scale || 00463 _aspect_ratio * final_x_scale != final_y_scale) { 00464 array_format->add_column 00465 (InternalName::get_aspect_ratio(), 1, Geom::NT_stdfloat, 00466 Geom::C_other); 00467 } 00468 00469 CPT(GeomVertexFormat) format = GeomVertexFormat::register_format 00470 (new GeomVertexFormat(array_format)); 00471 00472 // Reset render() data structures 00473 for (i = 0; i < (int)_ttl_count.size(); ++i) { 00474 PANDA_FREE_ARRAY(_ttl_count[i]); 00475 } 00476 _anim_size.resize(anim_count); 00477 _ttl_count.clear(); 00478 _ttl_count.resize(anim_count); 00479 00480 // Reset sprite primitive data in order to prepare for next pass. 00481 _sprite_primitive.clear(); 00482 _sprites.clear(); 00483 _vdata.clear(); 00484 _sprite_writer.clear(); 00485 00486 GeomNode *render_node = get_render_node(); 00487 render_node->remove_all_geoms(); 00488 00489 // For each animation... 00490 for (i = 0; i < anim_count; ++i) { 00491 anim = _anims[i]; 00492 _anim_size[i] = anim->get_num_frames(); 00493 00494 _sprite_primitive.push_back(pvector<PT(Geom)>()); 00495 _sprites.push_back(pvector<PT(GeomPoints)>()); 00496 _vdata.push_back(pvector<PT(GeomVertexData)>()); 00497 _sprite_writer.push_back(pvector<SpriteWriter>()); 00498 00499 // For each frame of the animation... 00500 for (j = 0; j < _anim_size[i]; ++j) { 00501 _ttl_count[i] = (int *)PANDA_MALLOC_ARRAY(_anim_size[i] * sizeof(int)); 00502 _vdata[i].push_back(new GeomVertexData("sprite_particles", format, Geom::UH_stream)); 00503 PT(Geom) geom = new Geom(_vdata[i][j]); 00504 _sprite_primitive[i].push_back((Geom*)geom); 00505 _sprites[i].push_back(new GeomPoints(Geom::UH_stream)); 00506 geom->add_primitive(_sprites[i][j]); 00507 00508 // This will be overwritten in render(), but we had to have some initial value 00509 _sprite_writer[i].push_back(SpriteWriter()); 00510 00511 state = state->add_attrib(RenderModeAttrib::make(RenderModeAttrib::M_unchanged, _base_y_scale * _height, true)); 00512 if (anim->get_frame(j) != (Texture *)NULL) { 00513 state = state->add_attrib(TextureAttrib::make(anim->get_frame(j))); 00514 state = state->add_attrib(TexGenAttrib::make(TextureStage::get_default(), TexGenAttrib::M_point_sprite)); 00515 00516 // Build a transform to convert the texture coordinates to the 00517 // ll, ur space. 00518 LPoint2 ul(anim->get_ll(j)[0], anim->get_ur(j)[1]); 00519 LPoint2 lr(anim->get_ur(j)[0], anim->get_ll(j)[1]); 00520 LVector2 sc = lr - ul; 00521 00522 CPT(TransformState) ts = TransformState::make_pos_rotate_scale2d(ul, 0.0f, sc); 00523 state = state->add_attrib(TexMatrixAttrib::make(TextureStage::get_default(), ts)); 00524 } 00525 00526 render_node->add_geom(_sprite_primitive[i][j], state); 00527 } 00528 } 00529 00530 nassertv(render_node->check_valid()); 00531 } 00532 00533 //////////////////////////////////////////////////////////////////// 00534 // Function : SpriteParticleRenderer::birth_particle 00535 // Access : private 00536 // Description : child birth, one of those 'there-if-we-want-it' 00537 // things. not really too useful here, so it turns 00538 // out we don't really want it. 00539 //////////////////////////////////////////////////////////////////// 00540 void SpriteParticleRenderer:: 00541 birth_particle(int index) { 00542 _birth_list.push_back(index); 00543 } 00544 00545 //////////////////////////////////////////////////////////////////// 00546 // Function : SpriteParticleRenderer::kill_particle 00547 // Access : private 00548 // Description : child death 00549 //////////////////////////////////////////////////////////////////// 00550 void SpriteParticleRenderer:: 00551 kill_particle(int) { 00552 } 00553 00554 //////////////////////////////////////////////////////////////////// 00555 // Function : SpriteParticleRenderer::render 00556 // Access : private 00557 // Description : big child render. populates the geom node. 00558 //////////////////////////////////////////////////////////////////// 00559 void SpriteParticleRenderer:: 00560 render(pvector< PT(PhysicsObject) >& po_vector, int ttl_particles) { 00561 PStatTimer t1(_render_collector); 00562 // There is no texture data available, exit. 00563 if (_anims.empty()) { 00564 return; 00565 } 00566 00567 BaseParticle *cur_particle; 00568 int remaining_particles = ttl_particles; 00569 int i,j; // loop counters 00570 int anim_count = _anims.size(); // number of animations 00571 int frame; // frame index, used in indicating which frame to use when not animated 00572 // First, since this is the only time we have access to the actual particles, do some delayed initialization. 00573 if (_animate_frames || anim_count) { 00574 if (!_birth_list.empty()) { 00575 for (vector_int::iterator vIter = _birth_list.begin(); vIter != _birth_list.end(); ++vIter) { 00576 cur_particle = (BaseParticle*)po_vector[*vIter].p(); 00577 i = int(NORMALIZED_RAND()*anim_count); 00578 00579 // If there are multiple animations to choose from, choose one at random for this new particle 00580 cur_particle->set_index(i < anim_count?i:i-1); 00581 00582 // This is an experimental age offset so that the animations don't appear synchronized. 00583 // If we are using animations, try to vary the frame flipping a bit for particles in the same litter. 00584 // A similar effect might be a achieved by using a small lifespan spread value on the factory. 00585 00586 // Perhaps we should look into other methods. The age offset doesn't seem to be cutting it. 00587 if (_animate_frames) { 00588 cur_particle->set_age(cur_particle->get_age()+i/10.0*cur_particle->get_lifespan()); 00589 } 00590 } 00591 } 00592 } 00593 _birth_list.clear(); 00594 00595 // Create vertex writers for each of the possible geoms. 00596 // Could possibly be changed to only create writers for geoms that would be used 00597 // according to the animation configuration. 00598 for (i = 0; i < anim_count; ++i) { 00599 for (j = 0; j < _anim_size[i]; ++j) { 00600 // Set the particle per frame counts to 0. 00601 memset(_ttl_count[i], 0, _anim_size[i]*sizeof(int)); 00602 _sprite_writer[i][j].vertex = GeomVertexWriter(_vdata[i][j], InternalName::get_vertex()); 00603 _sprite_writer[i][j].color = GeomVertexWriter(_vdata[i][j], InternalName::get_color()); 00604 _sprite_writer[i][j].rotate = GeomVertexWriter(_vdata[i][j], InternalName::get_rotate()); 00605 _sprite_writer[i][j].size = GeomVertexWriter(_vdata[i][j], InternalName::get_size()); 00606 _sprite_writer[i][j].aspect_ratio = GeomVertexWriter(_vdata[i][j], InternalName::get_aspect_ratio()); 00607 } 00608 } 00609 00610 // init the aabb 00611 _aabb_min.set(99999.0f, 99999.0f, 99999.0f); 00612 _aabb_max.set(-99999.0f, -99999.0f, -99999.0f); 00613 00614 // run through every filled slot 00615 for (i = 0; i < (int)po_vector.size(); i++) { 00616 cur_particle = (BaseParticle *) po_vector[i].p(); 00617 00618 if (!cur_particle->get_alive()) { 00619 continue; 00620 } 00621 00622 LPoint3 position = cur_particle->get_position(); 00623 00624 // x aabb adjust 00625 if (position[0] > _aabb_max[0]) 00626 _aabb_max[0] = position[0]; 00627 else if (position[0] < _aabb_min[0]) 00628 _aabb_min[0] = position[0]; 00629 00630 // y aabb adjust 00631 if (position[1] > _aabb_max[1]) 00632 _aabb_max[1] = position[1]; 00633 else if (position[1] < _aabb_min[1]) 00634 _aabb_min[1] = position[1]; 00635 00636 // z aabb adjust 00637 if (position[2] > _aabb_max[2]) 00638 _aabb_max[2] = position[2]; 00639 else if (position[2] < _aabb_min[2]) 00640 _aabb_min[2] = position[2]; 00641 00642 00643 PN_stdfloat t = cur_particle->get_parameterized_age(); 00644 int anim_index = cur_particle->get_index(); 00645 00646 // If an animation has been removed, we need to reassign 00647 // those particles assigned to the removed animation. 00648 if(_animation_removed && (anim_index >= anim_count)) { 00649 anim_index = int(NORMALIZED_RAND()*anim_count); 00650 anim_index = anim_index<anim_count?anim_index:anim_index-1; 00651 cur_particle->set_index(anim_index); 00652 } 00653 00654 // Find the frame 00655 if (_animate_frames) { 00656 if (_animate_frames_rate == 0.0f) { 00657 frame = (int)(t*_anim_size[anim_index]); 00658 } else { 00659 frame = (int)fmod(cur_particle->get_age()*_animate_frames_rate+1,_anim_size[anim_index]); 00660 } 00661 } else { 00662 frame = _animate_frames_index; 00663 } 00664 00665 // Quick check make sure our math above didn't result in an invalid frame. 00666 frame = (frame < _anim_size[anim_index]) ? frame : (_anim_size[anim_index]-1); 00667 ++_ttl_count[anim_index][frame]; 00668 00669 // Calculate the color 00670 // This is where we'll want to give the renderer the new color 00671 LColor c = _color_interpolation_manager->generateColor(t); 00672 00673 int alphamode=get_alpha_mode(); 00674 if (alphamode != PR_ALPHA_NONE) { 00675 if (alphamode == PR_ALPHA_OUT) 00676 c[3] *= (1.0f - t) * get_user_alpha(); 00677 else if (alphamode == PR_ALPHA_IN) 00678 c[3] *= t * get_user_alpha(); 00679 else if (alphamode == PR_ALPHA_IN_OUT) { 00680 c[3] *= 2.0f * min(t, 1.0f - t) * get_user_alpha(); 00681 } 00682 else { 00683 assert(alphamode == PR_ALPHA_USER); 00684 c[3] *= get_user_alpha(); 00685 } 00686 } 00687 00688 // Send the data on its way... 00689 _sprite_writer[anim_index][frame].vertex.add_data3(position); 00690 _sprite_writer[anim_index][frame].color.add_data4(c); 00691 00692 PN_stdfloat current_x_scale = _initial_x_scale; 00693 PN_stdfloat current_y_scale = _initial_y_scale; 00694 00695 if (_animate_x_ratio || _animate_y_ratio) { 00696 if (_blend_method == PP_BLEND_CUBIC) { 00697 t = CUBIC_T(t); 00698 } 00699 00700 if (_animate_x_ratio) { 00701 current_x_scale = (_initial_x_scale + 00702 (t * (_final_x_scale - _initial_x_scale))); 00703 } 00704 if (_animate_y_ratio) { 00705 current_y_scale = (_initial_y_scale + 00706 (t * (_final_y_scale - _initial_y_scale))); 00707 } 00708 } 00709 00710 if (_sprite_writer[anim_index][frame].size.has_column()) { 00711 _sprite_writer[anim_index][frame].size.add_data1f(current_y_scale * _height); 00712 } 00713 if (_sprite_writer[anim_index][frame].aspect_ratio.has_column()) { 00714 _sprite_writer[anim_index][frame].aspect_ratio.add_data1f(_aspect_ratio * current_x_scale / current_y_scale); 00715 } 00716 if (_animate_theta) { 00717 _sprite_writer[anim_index][frame].rotate.add_data1f(cur_particle->get_theta()); 00718 } else if (_sprite_writer[anim_index][frame].rotate.has_column()) { 00719 _sprite_writer[anim_index][frame].rotate.add_data1f(_theta); 00720 } 00721 00722 // maybe jump out early? 00723 remaining_particles--; 00724 if (remaining_particles == 0) { 00725 break; 00726 } 00727 } 00728 int n = 0; 00729 GeomNode *render_node = get_render_node(); 00730 00731 for (i = 0; i < anim_count; ++i) { 00732 for (j = 0; j < _anim_size[i]; ++j) { 00733 _sprites[i][j]->clear_vertices(); 00734 _sprite_writer[i][j].clear(); 00735 00736 // We have to reassign the GeomVertexData and GeomPrimitive to 00737 // the Geom, and the Geom to the GeomNode, in case it got 00738 // flattened away. 00739 _sprite_primitive[i][j]->set_primitive(0, _sprites[i][j]); 00740 _sprite_primitive[i][j]->set_vertex_data(_vdata[i][j]); 00741 00742 render_node->set_geom(n, _sprite_primitive[i][j]); 00743 ++n; 00744 } 00745 } 00746 00747 if (_animate_frames) { 00748 for (i = 0; i < anim_count; ++i) { 00749 for (j = 0; j < _anim_size[i]; ++j) { 00750 _sprites[i][j]->add_next_vertices(_ttl_count[i][j]); 00751 } 00752 } 00753 } else { 00754 for (i = 0; i < anim_count; ++i) { 00755 _sprites[i][_animate_frames_index]->add_next_vertices(_ttl_count[i][_animate_frames_index]); 00756 } 00757 } 00758 00759 // done filling geompoint node, now do the bb stuff 00760 LPoint3 aabb_center = _aabb_min + ((_aabb_max - _aabb_min) * 0.5f); 00761 PN_stdfloat radius = (aabb_center - _aabb_min).length(); 00762 00763 for (i = 0; i < anim_count; ++i) { 00764 for (j = 0; j < _anim_size[i]; ++j) { 00765 nassertv(_sprite_primitive[i][j]->check_valid()); 00766 BoundingSphere sphere(aabb_center, radius); 00767 _sprite_primitive[i][j]->set_bounds(&sphere); 00768 } 00769 } 00770 00771 get_render_node()->mark_internal_bounds_stale(); 00772 nassertv(render_node->check_valid()); 00773 _animation_removed = false; 00774 } 00775 00776 //////////////////////////////////////////////////////////////////// 00777 // Function : output 00778 // Access : Public 00779 // Description : Write a string representation of this instance to 00780 // <out>. 00781 //////////////////////////////////////////////////////////////////// 00782 void SpriteParticleRenderer:: 00783 output(ostream &out) const { 00784 #ifndef NDEBUG //[ 00785 out<<"SpriteParticleRenderer"; 00786 #endif //] NDEBUG 00787 } 00788 00789 //////////////////////////////////////////////////////////////////// 00790 // Function : write 00791 // Access : Public 00792 // Description : Write a string representation of this instance to 00793 // <out>. 00794 //////////////////////////////////////////////////////////////////// 00795 void SpriteParticleRenderer:: 00796 write(ostream &out, int indent_level) const { 00797 indent(out, indent_level) << "SpriteParticleRenderer:\n"; 00798 // indent(out, indent_level + 2) << "_sprite_primitive "<<_sprite_primitive<<"\n"; 00799 indent(out, indent_level + 2) << "_color "<<_color<<"\n"; 00800 indent(out, indent_level + 2) << "_initial_x_scale "<<_initial_x_scale<<"\n"; 00801 indent(out, indent_level + 2) << "_final_x_scale "<<_final_x_scale<<"\n"; 00802 indent(out, indent_level + 2) << "_initial_y_scale "<<_initial_y_scale<<"\n"; 00803 indent(out, indent_level + 2) << "_final_y_scale "<<_final_y_scale<<"\n"; 00804 indent(out, indent_level + 2) << "_theta "<<_theta<<"\n"; 00805 indent(out, indent_level + 2) << "_animate_x_ratio "<<_animate_x_ratio<<"\n"; 00806 indent(out, indent_level + 2) << "_animate_y_ratio "<<_animate_y_ratio<<"\n"; 00807 indent(out, indent_level + 2) << "_animate_theta "<<_animate_theta<<"\n"; 00808 indent(out, indent_level + 2) << "_blend_method "<<_blend_method<<"\n"; 00809 indent(out, indent_level + 2) << "_aabb_min "<<_aabb_min<<"\n"; 00810 indent(out, indent_level + 2) << "_aabb_max "<<_aabb_max<<"\n"; 00811 indent(out, indent_level + 2) << "_pool_size "<<_pool_size<<"\n"; 00812 BaseParticleRenderer::write(out, indent_level + 2); 00813 }