1 /* 2 Copyright (c) 2008 Seneca College 3 Licenced under the MIT License (http://www.c3dl.org/index.php/mit-license/) 4 */ 5 6 7 /** 8 @class ParticleSystem is used to simulate phenomena such as fire, smoke, rain, etc. 9 */ 10 c3dl.ParticleSystem = function() 11 { 12 // particle uv's won't change so instead of keeping 13 // a copy of uv coords in each particle, keep one copy 14 // in the particle system. 15 this.particleUVs = [1,1, // 16 1,0, // 17 0,0, // 18 0,1]; // 19 20 // winding order of these verts is counter-clockwise, the same as models. This 21 // prevents having to change the winding order state in openGL when rendering. 22 this.billboardVerts =[ 1,-1, 0, // bottom right 23 1, 1, 0, // top right 24 -1, 1, 0, // top left 25 -1,-1, 0]; // bottom left 26 27 // this particle system's transformation matrix 28 this.mat = [1,0,0,0, 29 0,1,0,0, 30 0,0,1,0, 31 0,0,0,1]; 32 33 // list of the Particle objects. 34 this.particles; 35 36 // keep a count of the number of dead particles we have 37 // this turns some operations from O(n) to O(1). 38 this.numDeadParticles; 39 40 // every particle in this sytem will have the same texture 41 this.texture; 42 43 // velocity range of the particles. When particles 44 // are born they are assigned a range between these two 45 // values. 46 this.minVelocity = [0,0,0]; 47 this.maxVelocity = [0,0,0]; 48 49 this.maxAngVel = 0; 50 this.minAngVel = 0; 51 52 // lifetime range of the particles in seconds. Once the 53 // age of a particle has surpassed its lifetime, the particle 54 // is no longer updated and rendered. 55 this.minLifetime = 0; 56 this.maxLifetime = 0; 57 58 // the color range of the particles. The color also contains an 59 // alpha component. 60 this.minColor = [0,0,0,0]; 61 this.maxColor = [0,0,0,0]; 62 63 // 64 // 65 this.minSize = 1; 66 this.maxSize = 1; 67 68 // acceleration is a property of the subsystem. Every 69 // particle in the subsystem will share the same 70 // acceleration. 71 this.acceleration = [0,0,0,0]; 72 73 // blend modes 74 this.dstBlend = c3dl.ZERO; 75 this.srcBlend = c3dl.ZERO; 76 77 this.blendEq = c3dl.FUNC_ADD; 78 79 // we need the camera vectors to create a billboard. 80 // on each update, we check if our local values are the 81 // same as the scene's camera. if the scene's camera 82 // was updated, we recalculate our billboard. 83 this.camUp = [0,0,0]; 84 this.camLeft = [0,0,0]; 85 this.camDir = [0,0,0]; 86 87 // if the particle system is playing then the particles are 88 // updated and rendered. Otherwise they are 89 this.isPlaying = false; 90 91 // how many particles are emitted per second? 92 this.emitRate = 0; 93 94 // these are used to calculate how many particles to emit on update. 95 this.timeCounter = 0; 96 this.isTimeCounterSetup = false; 97 98 // this will be passed to the renderer 99 this.particleVerts = null; 100 this.particleColors = null; 101 this.particleTexCoords = null; 102 103 // VBOS 104 this.VBOVertices = null; 105 this.VBOColors = null; 106 this.VBOTexCoords = null; 107 this.firstTimeRender = true; 108 109 /** 110 Emit a number of particles all at once. If the amount of particles 111 to emit exceeds the amount which are available, the last remaining 112 particles are emitted. If there are no particles available to emit, 113 none are emitted. 114 115 @param numToEmit 116 */ 117 this.emit = function(numToEmit) 118 { 119 // only emit particles if the user passed in a valid number 120 // and there is actually space left to emit. 121 // If we have a dead particle, that means we can recycle it. 122 if( numToEmit <= 0 || this.numDeadParticles == 0) 123 { 124 return; 125 } 126 127 // if we tried to emit more particles than there is enough 128 // space for, just emit the last remaining particles, since 129 // our array is static, there is nothing we can do except wait 130 // for particles to die off. 131 // 132 // cap the amount of particles to emit. 133 numToEmit = (numToEmit > this.numDeadParticles) ? this.numDeadParticles: numToEmit; 134 135 // stay within the bounds and don't emit more than we have. 136 // we count down until numParticlesToEmit is zero. 137 for (var i = 0; i < this.particles.length && numToEmit > 0; i++) 138 { 139 // if we found a dead particle, recycle it. 140 if( this.particles[i].isAlive() == false) 141 { 142 // emit the particle at index i which is recyclable. 143 this.emitParticle(i); 144 145 // one less we have to emit 146 numToEmit--; 147 } 148 } 149 } 150 151 /** 152 @private 153 Emit a particle at index 'index'. 154 155 @param index 156 */ 157 this.emitParticle = function(index) 158 { 159 if( index >= 0 && index < this.particles.length) 160 { 161 this.particles[index].setVelocity([ 162 c3dl.getRandom(this.minVelocity[0],this.maxVelocity[0]), 163 c3dl.getRandom(this.minVelocity[1],this.maxVelocity[1]), 164 c3dl.getRandom(this.minVelocity[2],this.maxVelocity[2]) 165 ]); 166 167 this.particles[index].setAge(0); 168 this.particles[index].setLifetime(c3dl.getRandom(this.minLifetime, this.maxLifetime)); 169 this.particles[index].setAlive(true); 170 171 // when the particle is emitted, we assign it the position of the particle system 172 // By doing this, moving the particle system will not move the particles. 173 this.particles[index].setPosition([this.mat[12],this.mat[13],this.mat[14]]); 174 175 this.particles[index].setColor([ 176 c3dl.getRandom(this.minColor[0], this.maxColor[0]), 177 c3dl.getRandom(this.minColor[1], this.maxColor[1]), 178 c3dl.getRandom(this.minColor[2], this.maxColor[2]), 179 c3dl.getRandom(this.minColor[3], this.maxColor[3]), 180 ]); 181 182 this.particles[index].setSize(c3dl.getRandom(this.minSize, this.maxSize)); 183 184 // we now have one less particle to recycle. 185 this.numDeadParticles--; 186 } 187 } 188 189 190 /** 191 Initialize the subsystem. This must be called before the particle 192 system is actually used. 193 194 @param {integer} numParticles 195 */ 196 this.init = function(numParticles) 197 { 198 // allocate once since allocation is expensive. We will just recycle the 199 // particles when they die. 200 this.particles = new Array(numParticles); 201 202 for (var i = 0; i < numParticles; i++) 203 { 204 this.particles[i] = new c3dl.Particle(); 205 } 206 207 this.particleVerts = new Array(this.particles.length * 3 * 4); 208 this.particleColors = new Array(this.particles.length * 4 * 4); 209 this.particleTexCoords = new Array(this.particles.length * 2 * 4 ); 210 211 for(var i = 0; i < this.particleColors.length; i++) 212 { 213 this.particleColors[i] = 0.0; 214 } 215 for(var i = 0; i < this.particleVerts.length; i++) 216 { 217 this.particleVerts[i] = 0.0; 218 } 219 for(var i = 0; i < this.particleTexCoords.length; i++) 220 { 221 this.particleTexCoords[i] = 0; 222 } 223 224 // fix this depending on emission 225 this.isPlaying = true; 226 this.numDeadParticles = this.particles.length; 227 } 228 229 /** 230 @private 231 is the subsystem ready to render? 232 233 @return true if the subsystem is ready to render. 234 */ 235 this.isReady = function() 236 { 237 return (this.particles instanceof Array); 238 } 239 240 /** 241 Get the total amount of particles in the system, including dead ones. 242 243 return {int} 244 */ 245 this.getNumParticles = function() 246 { 247 return this.particles.length; 248 } 249 250 /** 251 Get the particle at index i. 252 253 @param {int} i the index of the particle to get. 254 */ 255 this.getParticle = function(i) 256 { 257 if( i >= 0 && i < this.particles.length) 258 { 259 return this.particles[i]; 260 } 261 } 262 263 /** 264 @private 265 */ 266 this.getVertices = function() 267 { 268 return this.billboardVerts; 269 } 270 271 /** 272 @private 273 */ 274 this.getTexCoords = function() 275 { 276 return this.particleUVs; 277 } 278 279 /** 280 @private 281 Removed the particle at index 'index' from being update and rendered. 282 283 @param 284 */ 285 this.killParticle = function(index) 286 { 287 if(index > 0 && index < this.particles.length) 288 { 289 this.particles[index].setAlive(false); 290 this.numDeadParticles++; 291 } 292 } 293 294 /** 295 Set the amount of particles to emit per second. To stop emission, pass in zero. 296 297 @param particlesPerSecond 298 */ 299 this.setEmitRate = function(particlesPerSecond) 300 { 301 if(particlesPerSecond == 0) 302 { 303 this.emitRate = 0; 304 this.isTimeCounterSetup = false; 305 } 306 else if( particlesPerSecond > 0) 307 { 308 this.emitRate = particlesPerSecond; 309 } 310 } 311 312 313 /** 314 @private 315 testing funciton 316 */ 317 this.translate = function(vec) 318 { 319 this.mat[12] += vec[0]; 320 this.mat[13] += vec[1]; 321 this.mat[14] += vec[2]; 322 } 323 324 /** 325 Set the position of the particle system. 326 327 @param {Array} 328 */ 329 this.setPosition = function(vec) 330 { 331 this.mat[12] = vec[0]; 332 this.mat[13] = vec[1]; 333 this.mat[14] = vec[2]; 334 } 335 336 337 /** 338 Get the acceleration of all the particles. This is usually 339 gravity, but does not have to be. 340 */ 341 this.getAcceleration = function() 342 { 343 return this.acceleration; 344 } 345 346 /** 347 Get blend equation 348 */ 349 this.getBlendEquation = function() 350 { 351 return this.blendEq; 352 } 353 354 /** 355 get the desination blend factor. 356 357 @return {int} the destination blend factor. 358 */ 359 this.getDstBlend = function() 360 { 361 return this.dstBlend; 362 } 363 364 /** 365 Get the maximum color range. The max color range 366 is an array of components which each contain the 367 maximum value each component can be assigned. components 368 range from 0 to 1. 369 370 @returns {Array} 371 */ 372 this.getMaxColor = function() 373 { 374 return this.maxColor; 375 } 376 377 /** 378 Get the minimum color range. 379 @returns {Array} 380 */ 381 this.getMinColor = function() 382 { 383 return this.minColor; 384 } 385 386 /** 387 Get the maximum number of seconds for which any particle can live. 388 389 @return the maximum number of seconds for which any particle 390 can live. 391 */ 392 this.getMaxLifetime = function() 393 { 394 return this.maxLifetime; 395 } 396 397 /** 398 Get the minimum amount of seconds for which any particle can live. 399 400 @return the minimum amount of seconds for which any particle can live. 401 */ 402 this.getMinLifetime = function() 403 { 404 return this.minLifetime; 405 } 406 407 /** 408 */ 409 this.getMinVelocity = function() 410 { 411 return this.minVelocity; 412 } 413 414 /** 415 Get the maximum values for each xyz component any particle can 416 be assigned when emitted. 417 418 @return {Array} the maximum values for each xyz component any particle can 419 be assigned when emitted. 420 */ 421 this.getMaxVelocity = function() 422 { 423 return this.maxVelocity; 424 } 425 426 /** 427 Get the texture which all particles are assigned. 428 429 @return {String} the texture which all particles are assigned. 430 */ 431 this.getTexture = function() 432 { 433 return this.texture; 434 } 435 436 /** 437 Get the source blending factor. 438 439 @return {int} the source blending factor. 440 */ 441 this.getSrcBlend = function() 442 { 443 return this.srcBlend; 444 } 445 446 /** 447 Set the acceleration of this subsystem. This is commonly gravity, but can be 448 any valid vector. 449 450 @param {Array} acceleration 451 */ 452 this.setAcceleration = function(acceleration) 453 { 454 if(c3dl.isValidVector(acceleration)) 455 { 456 this.acceleration = acceleration; 457 } 458 } 459 460 /** 461 Set the destination blend factor. parameter must be one of: 462 c3dl.ZERO, 463 c3dl.ONE, 464 c3dl.SRC_COLOR, 465 c3dl.ONE_MINUS_SRC_COLOR, 466 c3dl.SRC_ALPHA, 467 c3dl.ONE_MINUS_SRC_ALPHA, 468 c3dl.DST_ALPHA, 469 c3dl.ONE_MINUS_DST_ALPHA, 470 c3dl.DST_COLOR, 471 c3dl.ONE_MINUS_DST_COLOR or 472 c3dl.SRC_ALPHA_SATURATE 473 474 @param {int} dstBlend 475 */ 476 this.setDstBlend = function(dstBlend) 477 { 478 switch(dstBlend) 479 { 480 case c3dl.ZERO: case c3dl.ONE: 481 case c3dl.SRC_COLOR: case c3dl.ONE_MINUS_SRC_COLOR: 482 case c3dl.SRC_ALPHA: case c3dl.ONE_MINUS_SRC_ALPHA: 483 case c3dl.DST_ALPHA: case c3dl.ONE_MINUS_DST_ALPHA: 484 case c3dl.DST_COLOR: case c3dl.ONE_MINUS_DST_COLOR: 485 case c3dl.SRC_ALPHA_SATURATE: 486 this.dstBlend = dstBlend; 487 break; 488 } 489 } 490 491 /** 492 Set the maximum color range a particle can be assigned. Each component 493 must range from 0 to 1 inclusive. 494 495 @param {Array} maxColor - array of 4 floating point values ranging from 0 to 1 inclusive. 496 */ 497 this.setMaxColor = function(maxColor) 498 { 499 if(c3dl.isValidColor(maxColor)) 500 { 501 this.maxColor = maxColor; 502 } 503 } 504 505 /** 506 Set the minimum color values each particle can be. 507 508 @param {Array} minParticleColor the minimum Color values each particle 509 can be. 510 */ 511 this.setMinColor = function(minColor) 512 { 513 if(c3dl.isValidColor(minColor)) 514 { 515 this.minColor = minColor; 516 } 517 } 518 519 /** 520 Set the maximum number of seconds for which any particle can live. 521 Value must be greater than zero. 522 523 @param {float} maxLifetime 524 */ 525 this.setMaxLifetime = function(maxLifetime) 526 { 527 if(maxLifetime > 0) 528 { 529 this.maxLifetime = maxLifetime; 530 } 531 } 532 533 /** 534 Set the minimum amount of seconds for which particles will live. 535 Value must be greater than zero. 536 537 @param minParticleLifetime the minimum amount of seconds for 538 which the particles can live. 539 */ 540 this.setMinLifetime = function(minLifetime) 541 { 542 if(minLifetime > 0) 543 { 544 this.minLifetime = minLifetime; 545 } 546 } 547 548 /** 549 550 */ 551 this.setMaxSize = function(maxSize) 552 { 553 if(maxSize > 0) 554 { 555 this.maxSize = maxSize; 556 } 557 } 558 559 /** 560 561 */ 562 this.setMinSize = function(minSize) 563 { 564 if(minSize > 0) 565 { 566 this.minSize = minSize; 567 } 568 } 569 570 /** 571 Set the minimum velocity of all the particles. 572 573 @param {Array} minVelocity the minimum velocity of all the particles. 574 */ 575 this.setMinVelocity = function(minVelocity) 576 { 577 if(c3dl.isValidVector(minVelocity)) 578 { 579 this.minVelocity = minVelocity; 580 } 581 } 582 583 /** 584 Set the maximum velocity of all the particles. 585 586 @param {Array} maxVelocity the maximum velocity of all the particles. 587 */ 588 this.setMaxVelocity = function(maxVelocity) 589 { 590 if(c3dl.isValidVector(maxVelocity)) 591 { 592 this.maxVelocity = maxVelocity; 593 } 594 } 595 596 /** 597 */ 598 this.setMaxAngularVelocity = function(maxAngVel) 599 { 600 601 } 602 603 /** 604 */ 605 this.setMinAngularVelocity = function(minAngVel) 606 { 607 } 608 609 /** 610 (src * srcBlend) Eq (dst * dstBlend) 611 612 @param {int} blenEq 613 */ 614 this.setBlendEquation = function(blendEq) 615 { 616 switch(blendEq) 617 { 618 case c3dl.FUNC_ADD: 619 case c3dl.FUNC_SUBTRACT: 620 case c3dl.FUNC_REVERSE_SUBTRACT: 621 this.blendEq = blendEq; 622 break; 623 } 624 } 625 626 627 /** 628 Set the Source blending factor. parameter must be one of: 629 c3dl.ZERO, 630 c3dl.ONE, 631 c3dl.SRC_COLOR, 632 c3dl.ONE_MINUS_SRC_COLOR, 633 c3dl.SRC_ALPHA, 634 c3dl.ONE_MINUS_SRC_ALPHA, 635 c3dl.DST_ALPHA, 636 c3dl.ONE_MINUS_DST_ALPHA, 637 c3dl.DST_COLOR, 638 c3dl.ONE_MINUS_DST_COLOR or 639 c3dl.SRC_ALPHA_SATURATE 640 641 @param {int} srcBlend 642 */ 643 this.setSrcBlend = function(srcBlend) 644 { 645 switch(srcBlend) 646 { 647 case c3dl.ZERO: case c3dl.ONE: 648 case c3dl.SRC_COLOR: case c3dl.ONE_MINUS_SRC_COLOR: 649 case c3dl.SRC_ALPHA: case c3dl.ONE_MINUS_SRC_ALPHA: 650 case c3dl.DST_ALPHA: case c3dl.ONE_MINUS_DST_ALPHA: 651 case c3dl.DST_COLOR: case c3dl.ONE_MINUS_DST_COLOR: 652 case c3dl.SRC_ALPHA_SATURATE: 653 this.srcBlend = srcBlend; 654 break; 655 } 656 } 657 658 659 /** 660 Set the texure of the particles. 661 662 @param {String} textureName 663 */ 664 this.setTexture = function(textureName) 665 { 666 this.texture = textureName; 667 } 668 669 /** 670 @private 671 Update the positions of the particles if they are alive. Also, 672 calculate how many particles to emit if the emit rate has been 673 set. 674 675 @param {float} timeStep 676 */ 677 this.update = function(timeStep) 678 { 679 // only calculate how many to emit if we actually want to 680 // emit particles 681 if( this.emitRate > 0) 682 { 683 // get the time 684 if(this.isTimeCounterSetup == false) 685 { 686 this.timeCounter = timeStep; 687 this.isTimeCounterSetup = true; 688 } 689 else 690 { 691 this.timeCounter += timeStep; 692 } 693 694 // The user supplies the amount of particles to emit per second since people are 695 // more accustomed to using seconds than milliseconds. However timeStep is in 696 // milliseconds, so we calculate how many to emit in 1000 milliseconds. 697 var numToEmit = this.timeCounter * this.emitRate / 1000.0; 698 699 // if enough time has elapsed to emit at least one particle, emit however 700 // many particles. Otherwise we will have to wait until next update and 701 // check again until we can emit at least one. 702 if( numToEmit >= 1 ) 703 { 704 // numToEmit may be a float, but emit should only be passed 705 // an integer 706 this.emit(numToEmit); 707 708 // subtract time from the timeCounter to prevent too 709 // many paritcles from being emitted on the next update 710 this.timeCounter -= numToEmit / this.emitRate * 1000.0; 711 } 712 } 713 714 var p = 0, j = 0; 715 for(var i = 0; i < this.particleColors.length; i++, j++) 716 { 717 if(i!=0 && i%16 ==0){ 718 p++ 719 // c3dl.debug.logWarning(p + " " + this.particles[p].getColor()); 720 } 721 722 if(j > 3){j=0;} 723 this.particleColors[i] = this.particles[p].getColor()[j]; 724 } 725 // c3dl.debug.logWarning(p + " . " + this.particleColors); 726 727 // now update the particles 728 for(var i = 0; i < this.particles.length; i++) 729 { 730 // don't update the particle unless its alive. 731 if(this.particles[i].isAlive()) 732 { 733 var timeInSeconds = timeStep/1000; 734 735 // make shorter variable names to prevent clutter. 736 var pos = this.particles[i].getPosition(); 737 var vel = this.particles[i].getVelocity(); 738 739 this.particles[i].translate([ 740 (vel[0] * timeInSeconds) + this.acceleration[0] * timeInSeconds * timeInSeconds * 0.5, 741 (vel[1] * timeInSeconds) + this.acceleration[1] * timeInSeconds * timeInSeconds * 0.5, 742 (vel[2] * timeInSeconds) + this.acceleration[2] * timeInSeconds * timeInSeconds * 0.5 743 ]); 744 745 // 746 for(var p = 0, j=0; p < 12; p++,j++) 747 { 748 if(j > 2){j=0;} 749 this.particleVerts[i*12+p] = this.particles[i].getPosition()[j] + this.getVertices()[p]; 750 } 751 752 753 754 // update the velocity 755 this.particles[i].setVelocity([ 756 vel[0] + (this.acceleration[0] * timeInSeconds), 757 vel[1] + (this.acceleration[1] * timeInSeconds), 758 vel[2] + (this.acceleration[2] * timeInSeconds) 759 ]); 760 761 // Age the particle 762 this.particles[i].setAge( this.particles[i].getAge() + timeInSeconds); 763 764 // kill the particle if it went past its lifetime. If the particle 765 // is dead, it won't be updated or rendered until it is recycled. 766 if( this.particles[i].getAge() > this.particles[i].getLifetime() ) 767 { 768 this.killParticle(i); 769 } 770 } 771 } 772 } 773 774 this.getVBOTexCoords = function() 775 { 776 return this.VBOTexCoords; 777 } 778 779 /** 780 */ 781 this.getVBOVertices = function() 782 { 783 return this.VBOVertices; 784 } 785 786 /** 787 */ 788 this.getVBOColors = function() 789 { 790 return this.VBOColors; 791 } 792 793 /** 794 @private 795 prepare to render the particles. This includes turning off lighting, enabling blending, etc. 796 797 @param glCanvas3D 798 @param {Scene} scene 799 */ 800 this.preRender = function(glCanvas3D,scene) 801 { 802 if(this.firstTimeRender === true) 803 { 804 for(var i = 0, j = 0; i < this.particleTexCoords.length; i++, j++) 805 { 806 if(j > 7){ j= 0;} 807 this.particleTexCoords[i] = this.particleUVs[j]; 808 } 809 810 this.VBOColors = glCanvas3D.createBuffer(); 811 glCanvas3D.bindBuffer(glCanvas3D.ARRAY_BUFFER, this.VBOColors); 812 glCanvas3D.bufferData(glCanvas3D.ARRAY_BUFFER, new WebGLFloatArray(this.particleColors), glCanvas3D.STREAM_DRAW); 813 814 this.VBOVertices = glCanvas3D.createBuffer(); 815 glCanvas3D.bindBuffer(glCanvas3D.ARRAY_BUFFER, this.VBOVertices); 816 glCanvas3D.bufferData(glCanvas3D.ARRAY_BUFFER, new WebGLFloatArray(this.particleVerts), glCanvas3D.STREAM_DRAW); 817 818 this.VBOTexCoords = glCanvas3D.createBuffer(); 819 glCanvas3D.bindBuffer(glCanvas3D.ARRAY_BUFFER, this.VBOTexCoords); 820 glCanvas3D.bufferData(glCanvas3D.ARRAY_BUFFER, new WebGLFloatArray(this.particleTexCoords), glCanvas3D.STREAM_DRAW); 821 822 this.firstTimeRender = 0; 823 } 824 else 825 { 826 glCanvas3D.bindBuffer(glCanvas3D.ARRAY_BUFFER, this.VBOColors); 827 glCanvas3D.bufferData(glCanvas3D.ARRAY_BUFFER, new WebGLFloatArray(this.particleColors), glCanvas3D.STREAM_DRAW); 828 829 glCanvas3D.bindBuffer(glCanvas3D.ARRAY_BUFFER, this.VBOVertices); 830 glCanvas3D.bufferData(glCanvas3D.ARRAY_BUFFER, new WebGLFloatArray(this.particleVerts), glCanvas3D.STREAM_DRAW); 831 832 glCanvas3D.bindBuffer(glCanvas3D.ARRAY_BUFFER, this.VBOTexCoords); 833 glCanvas3D.bufferData(glCanvas3D.ARRAY_BUFFER, new WebGLFloatArray(this.particleTexCoords), glCanvas3D.STREAM_DRAW); 834 } 835 836 // disable writing into the depth buffer. This will prevent 837 // the corners of texture overlapping each other. 838 glCanvas3D.depthMask(false); 839 840 // blending is expensive, so only enable for things that need it. 841 glCanvas3D.enable(glCanvas3D.BLEND); 842 843 // 844 glCanvas3D.blendEquation(this.blendEq); 845 846 // 847 glCanvas3D.blendFunc(this.getSrcBlend(), this.getDstBlend()); 848 } 849 850 /** 851 @private 852 Re-enable the depth testing, lighting, etc. 853 854 @param glCanvas3D 855 @param {Scene} scene 856 */ 857 this.postRender = function(glCanvas3D,scene) 858 { 859 // blending is expensive so turn it off when not needed. 860 glCanvas3D.disable(glCanvas3D.BLEND); 861 glCanvas3D.depthMask(true); 862 } 863 864 /** 865 @private 866 Draw all the particles which are alive. 867 868 @param glCanvas3D 869 @param {Scene} scene 870 */ 871 this.render = function(glCanvas3D,scene) 872 { 873 // 874 this.recalculateBillboard(glCanvas3D, scene); 875 876 this.preRender(glCanvas3D,scene); 877 878 scene.getRenderer().renderParticleSystem(this); 879 880 this.postRender(glCanvas3D,scene); 881 } 882 883 /** 884 */ 885 this.getObjectType = function() 886 { 887 return c3dl.PARTICLE_SYSTEM; 888 } 889 890 891 /** 892 @private 893 */ 894 this.recalculateBillboard = function(glCanvas3D, scene) 895 { 896 // if any of the vectors have changed, we have 897 // to recalculate the billboard. 898 899 // if they are equal we don't have to do any more 900 if( !(c3dl.isVectorEqual(this.camUp, scene.getCamera().getUp()) && 901 c3dl.isVectorEqual(this.camLeft, scene.getCamera().getLeft()) && 902 c3dl.isVectorEqual(this.camDir, scene.getCamera().getDir()) )) 903 { 904 // get local copies of the camera vectors. 905 this.camUp = scene.getCamera().getUp(); 906 this.camLeft = scene.getCamera().getLeft(); 907 this.camDir = scene.getCamera().getDir(); 908 909 var camRight = [-this.camLeft[0],-this.camLeft[1],-this.camLeft[2]]; 910 911 var bottomRight = c3dl.subtractVectors(camRight, this.camUp); 912 var bottomLeft = c3dl.subtractVectors(this.camLeft, this.camUp); 913 var topLeft = c3dl.addVectors(this.camLeft, this.camUp); 914 var topRight = c3dl.addVectors(camRight, this.camUp); 915 916 // use counter clockwise order since models vertices are also counter clockwise. 917 // This prevents having to change the openGL state of the winding order when 918 // switching between rendering models and particle systems. 919 this.billboardVerts = [ bottomRight[0], bottomRight[1], bottomRight [2], 920 topRight[0], topRight[1], topRight[2], 921 topLeft[0], topLeft[1], topLeft[2], 922 bottomLeft[0], bottomLeft[1], bottomLeft[2]]; 923 } 924 } 925 } 926