1 /* 2 Copyright (C) 1996-1997 Id Software, Inc. 3 4 This program is free software; you can redistribute it and/or 5 modify it under the terms of the GNU General Public License 6 as published by the Free Software Foundation; either version 2 7 of the License, or (at your option) any later version. 8 9 This program is distributed in the hope that it will be useful, 10 but WITHOUT ANY WARRANTY; without even the implied warranty of 11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 13 See the GNU General Public License for more details. 14 15 You should have received a copy of the GNU General Public License 16 along with this program; if not, write to the Free Software 17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 19 */ 20 21 #include "quakedef.h" 22 #include "r_local.h" 23 24 #define MAX_PARTICLES 2048 // default max # of particles at one 25 // time 26 #define ABSOLUTE_MIN_PARTICLES 512 // no fewer than this no matter what's 27 // on the command line 28 29 int ramp1[8] = {0x6f, 0x6d, 0x6b, 0x69, 0x67, 0x65, 0x63, 0x61}; 30 int ramp2[8] = {0x6f, 0x6e, 0x6d, 0x6c, 0x6b, 0x6a, 0x68, 0x66}; 31 int ramp3[8] = {0x6d, 0x6b, 6, 5, 4, 3}; 32 33 particle_t *active_particles, *free_particles; 34 35 particle_t *particles; 36 int r_numparticles; 37 38 vec3_t r_pright, r_pup, r_ppn; 39 40 41 /* 42 =============== 43 R_InitParticles 44 =============== 45 */ 46 void R_InitParticles (void) 47 { 48 int i; 49 50 i = COM_CheckParm ("-particles"); 51 52 if (i) 53 { 54 r_numparticles = (int)(Q_atoi(com_argv[i+1])); 55 if (r_numparticles < ABSOLUTE_MIN_PARTICLES) 56 r_numparticles = ABSOLUTE_MIN_PARTICLES; 57 } 58 else 59 { 60 r_numparticles = MAX_PARTICLES; 61 } 62 63 particles = (particle_t *) 64 Hunk_AllocName (r_numparticles * sizeof(particle_t), "particles"); 65 } 66 67 #ifdef QUAKE2 68 void R_DarkFieldParticles (entity_t *ent) 69 { 70 int i, j, k; 71 particle_t *p; 72 float vel; 73 vec3_t dir; 74 vec3_t org; 75 76 org[0] = ent->origin[0]; 77 org[1] = ent->origin[1]; 78 org[2] = ent->origin[2]; 79 for (i=-16 ; i<16 ; i+=8) 80 for (j=-16 ; j<16 ; j+=8) 81 for (k=0 ; k<32 ; k+=8) 82 { 83 if (!free_particles) 84 return; 85 p = free_particles; 86 free_particles = p->next; 87 p->next = active_particles; 88 active_particles = p; 89 90 p->die = cl.time + 0.2 + (rand()&7) * 0.02; 91 p->color = 150 + rand()%6; 92 p->type = pt_slowgrav; 93 94 dir[0] = j*8; 95 dir[1] = i*8; 96 dir[2] = k*8; 97 98 p->org[0] = org[0] + i + (rand()&3); 99 p->org[1] = org[1] + j + (rand()&3); 100 p->org[2] = org[2] + k + (rand()&3); 101 102 VectorNormalize (dir); 103 vel = 50 + (rand()&63); 104 VectorScale (dir, vel, p->vel); 105 } 106 } 107 #endif 108 109 110 /* 111 =============== 112 R_EntityParticles 113 =============== 114 */ 115 116 #define NUMVERTEXNORMALS 162 117 extern float r_avertexnormals[NUMVERTEXNORMALS][3]; 118 vec3_t avelocities[NUMVERTEXNORMALS]; 119 float beamlength = 16; 120 vec3_t avelocity = {23, 7, 3}; 121 float partstep = 0.01; 122 float timescale = 0.01; 123 124 void R_EntityParticles (entity_t *ent) 125 { 126 int count; 127 int i; 128 particle_t *p; 129 float angle; 130 float sr, sp, sy, cr, cp, cy; 131 vec3_t forward; 132 float dist; 133 134 dist = 64; 135 count = 50; 136 137 if (!avelocities[0][0]) 138 { 139 for (i=0 ; i<NUMVERTEXNORMALS*3 ; i++) 140 avelocities[i/3][i%3] = (rand()&255) * 0.01; 141 } 142 143 144 for (i=0 ; i<NUMVERTEXNORMALS ; i++) 145 { 146 angle = cl.time * avelocities[i][0]; 147 sy = sin(angle); 148 cy = cos(angle); 149 angle = cl.time * avelocities[i][1]; 150 sp = sin(angle); 151 cp = cos(angle); 152 angle = cl.time * avelocities[i][2]; 153 sr = sin(angle); 154 cr = cos(angle); 155 156 forward[0] = cp*cy; 157 forward[1] = cp*sy; 158 forward[2] = -sp; 159 160 if (!free_particles) 161 return; 162 p = free_particles; 163 free_particles = p->next; 164 p->next = active_particles; 165 active_particles = p; 166 167 p->die = cl.time + 0.01; 168 p->color = 0x6f; 169 p->type = pt_explode; 170 171 p->org[0] = ent->origin[0] + r_avertexnormals[i][0]*dist + forward[0]*beamlength; 172 p->org[1] = ent->origin[1] + r_avertexnormals[i][1]*dist + forward[1]*beamlength; 173 p->org[2] = ent->origin[2] + r_avertexnormals[i][2]*dist + forward[2]*beamlength; 174 } 175 } 176 177 178 /* 179 =============== 180 R_ClearParticles 181 =============== 182 */ 183 void R_ClearParticles (void) 184 { 185 int i; 186 187 free_particles = &particles[0]; 188 active_particles = NULL; 189 190 for (i=0 ;i<r_numparticles ; i++) 191 particles[i].next = &particles[i+1]; 192 particles[r_numparticles-1].next = NULL; 193 } 194 195 196 void R_ReadPointFile_f (void) 197 { 198 FILE *f; 199 vec3_t org; 200 int r; 201 int c; 202 particle_t *p; 203 char name[MAX_OSPATH]; 204 205 sprintf (name,"maps/%s.pts", sv.name); 206 207 COM_FOpenFile (name, &f); 208 if (!f) 209 { 210 Con_Printf ("couldn't open %s\n", name); 211 return; 212 } 213 214 Con_Printf ("Reading %s...\n", name); 215 c = 0; 216 for ( ;; ) 217 { 218 r = fscanf (f,"%f %f %f\n", &org[0], &org[1], &org[2]); 219 if (r != 3) 220 break; 221 c++; 222 223 if (!free_particles) 224 { 225 Con_Printf ("Not enough free particles\n"); 226 break; 227 } 228 p = free_particles; 229 free_particles = p->next; 230 p->next = active_particles; 231 active_particles = p; 232 233 p->die = 99999; 234 p->color = (-c)&15; 235 p->type = pt_static; 236 VectorCopy (vec3_origin, p->vel); 237 VectorCopy (org, p->org); 238 } 239 240 fclose (f); 241 Con_Printf ("%i points read\n", c); 242 } 243 244 /* 245 =============== 246 R_ParseParticleEffect 247 248 Parse an effect out of the server message 249 =============== 250 */ 251 void R_ParseParticleEffect (void) 252 { 253 vec3_t org, dir; 254 int i, count, msgcount, color; 255 256 for (i=0 ; i<3 ; i++) 257 org[i] = MSG_ReadCoord (); 258 for (i=0 ; i<3 ; i++) 259 dir[i] = MSG_ReadChar () * (1.0/16); 260 msgcount = MSG_ReadByte (); 261 color = MSG_ReadByte (); 262 263 if (msgcount == 255) 264 count = 1024; 265 else 266 count = msgcount; 267 268 R_RunParticleEffect (org, dir, color, count); 269 } 270 271 /* 272 =============== 273 R_ParticleExplosion 274 275 =============== 276 */ 277 void R_ParticleExplosion (vec3_t org) 278 { 279 int i, j; 280 particle_t *p; 281 282 for (i=0 ; i<1024 ; i++) 283 { 284 if (!free_particles) 285 return; 286 p = free_particles; 287 free_particles = p->next; 288 p->next = active_particles; 289 active_particles = p; 290 291 p->die = cl.time + 5; 292 p->color = ramp1[0]; 293 p->ramp = rand()&3; 294 if (i & 1) 295 { 296 p->type = pt_explode; 297 for (j=0 ; j<3 ; j++) 298 { 299 p->org[j] = org[j] + ((rand()%32)-16); 300 p->vel[j] = (rand()%512)-256; 301 } 302 } 303 else 304 { 305 p->type = pt_explode2; 306 for (j=0 ; j<3 ; j++) 307 { 308 p->org[j] = org[j] + ((rand()%32)-16); 309 p->vel[j] = (rand()%512)-256; 310 } 311 } 312 } 313 } 314 315 /* 316 =============== 317 R_ParticleExplosion2 318 319 =============== 320 */ 321 void R_ParticleExplosion2 (vec3_t org, int colorStart, int colorLength) 322 { 323 int i, j; 324 particle_t *p; 325 int colorMod = 0; 326 327 for (i=0; i<512; i++) 328 { 329 if (!free_particles) 330 return; 331 p = free_particles; 332 free_particles = p->next; 333 p->next = active_particles; 334 active_particles = p; 335 336 p->die = cl.time + 0.3; 337 p->color = colorStart + (colorMod % colorLength); 338 colorMod++; 339 340 p->type = pt_blob; 341 for (j=0 ; j<3 ; j++) 342 { 343 p->org[j] = org[j] + ((rand()%32)-16); 344 p->vel[j] = (rand()%512)-256; 345 } 346 } 347 } 348 349 /* 350 =============== 351 R_BlobExplosion 352 353 =============== 354 */ 355 void R_BlobExplosion (vec3_t org) 356 { 357 int i, j; 358 particle_t *p; 359 360 for (i=0 ; i<1024 ; i++) 361 { 362 if (!free_particles) 363 return; 364 p = free_particles; 365 free_particles = p->next; 366 p->next = active_particles; 367 active_particles = p; 368 369 p->die = cl.time + 1 + (rand()&8)*0.05; 370 371 if (i & 1) 372 { 373 p->type = pt_blob; 374 p->color = 66 + rand()%6; 375 for (j=0 ; j<3 ; j++) 376 { 377 p->org[j] = org[j] + ((rand()%32)-16); 378 p->vel[j] = (rand()%512)-256; 379 } 380 } 381 else 382 { 383 p->type = pt_blob2; 384 p->color = 150 + rand()%6; 385 for (j=0 ; j<3 ; j++) 386 { 387 p->org[j] = org[j] + ((rand()%32)-16); 388 p->vel[j] = (rand()%512)-256; 389 } 390 } 391 } 392 } 393 394 /* 395 =============== 396 R_RunParticleEffect 397 398 =============== 399 */ 400 void R_RunParticleEffect (vec3_t org, vec3_t dir, int color, int count) 401 { 402 int i, j; 403 particle_t *p; 404 405 for (i=0 ; i<count ; i++) 406 { 407 if (!free_particles) 408 return; 409 p = free_particles; 410 free_particles = p->next; 411 p->next = active_particles; 412 active_particles = p; 413 414 if (count == 1024) 415 { // rocket explosion 416 p->die = cl.time + 5; 417 p->color = ramp1[0]; 418 p->ramp = rand()&3; 419 if (i & 1) 420 { 421 p->type = pt_explode; 422 for (j=0 ; j<3 ; j++) 423 { 424 p->org[j] = org[j] + ((rand()%32)-16); 425 p->vel[j] = (rand()%512)-256; 426 } 427 } 428 else 429 { 430 p->type = pt_explode2; 431 for (j=0 ; j<3 ; j++) 432 { 433 p->org[j] = org[j] + ((rand()%32)-16); 434 p->vel[j] = (rand()%512)-256; 435 } 436 } 437 } 438 else 439 { 440 p->die = cl.time + 0.1*(rand()%5); 441 p->color = (color&~7) + (rand()&7); 442 p->type = pt_slowgrav; 443 for (j=0 ; j<3 ; j++) 444 { 445 p->org[j] = org[j] + ((rand()&15)-8); 446 p->vel[j] = dir[j]*15;// + (rand()%300)-150; 447 } 448 } 449 } 450 } 451 452 453 /* 454 =============== 455 R_LavaSplash 456 457 =============== 458 */ 459 void R_LavaSplash (vec3_t org) 460 { 461 int i, j, k; 462 particle_t *p; 463 float vel; 464 vec3_t dir; 465 466 for (i=-16 ; i<16 ; i++) 467 for (j=-16 ; j<16 ; j++) 468 for (k=0 ; k<1 ; k++) 469 { 470 if (!free_particles) 471 return; 472 p = free_particles; 473 free_particles = p->next; 474 p->next = active_particles; 475 active_particles = p; 476 477 p->die = cl.time + 2 + (rand()&31) * 0.02; 478 p->color = 224 + (rand()&7); 479 p->type = pt_slowgrav; 480 481 dir[0] = j*8 + (rand()&7); 482 dir[1] = i*8 + (rand()&7); 483 dir[2] = 256; 484 485 p->org[0] = org[0] + dir[0]; 486 p->org[1] = org[1] + dir[1]; 487 p->org[2] = org[2] + (rand()&63); 488 489 VectorNormalize (dir); 490 vel = 50 + (rand()&63); 491 VectorScale (dir, vel, p->vel); 492 } 493 } 494 495 /* 496 =============== 497 R_TeleportSplash 498 499 =============== 500 */ 501 void R_TeleportSplash (vec3_t org) 502 { 503 int i, j, k; 504 particle_t *p; 505 float vel; 506 vec3_t dir; 507 508 for (i=-16 ; i<16 ; i+=4) 509 for (j=-16 ; j<16 ; j+=4) 510 for (k=-24 ; k<32 ; k+=4) 511 { 512 if (!free_particles) 513 return; 514 p = free_particles; 515 free_particles = p->next; 516 p->next = active_particles; 517 active_particles = p; 518 519 p->die = cl.time + 0.2 + (rand()&7) * 0.02; 520 p->color = 7 + (rand()&7); 521 p->type = pt_slowgrav; 522 523 dir[0] = j*8; 524 dir[1] = i*8; 525 dir[2] = k*8; 526 527 p->org[0] = org[0] + i + (rand()&3); 528 p->org[1] = org[1] + j + (rand()&3); 529 p->org[2] = org[2] + k + (rand()&3); 530 531 VectorNormalize (dir); 532 vel = 50 + (rand()&63); 533 VectorScale (dir, vel, p->vel); 534 } 535 } 536 537 void R_RocketTrail (vec3_t start, vec3_t end, int type) 538 { 539 vec3_t vec; 540 float len; 541 int j; 542 particle_t *p; 543 int dec; 544 static int tracercount; 545 546 VectorSubtract (end, start, vec); 547 len = VectorNormalize (vec); 548 if (type < 128) 549 dec = 3; 550 else 551 { 552 dec = 1; 553 type -= 128; 554 } 555 556 while (len > 0) 557 { 558 len -= dec; 559 560 if (!free_particles) 561 return; 562 p = free_particles; 563 free_particles = p->next; 564 p->next = active_particles; 565 active_particles = p; 566 567 VectorCopy (vec3_origin, p->vel); 568 p->die = cl.time + 2; 569 570 switch (type) 571 { 572 case 0: // rocket trail 573 p->ramp = (rand()&3); 574 p->color = ramp3[(int)p->ramp]; 575 p->type = pt_fire; 576 for (j=0 ; j<3 ; j++) 577 p->org[j] = start[j] + ((rand()%6)-3); 578 break; 579 580 case 1: // smoke smoke 581 p->ramp = (rand()&3) + 2; 582 p->color = ramp3[(int)p->ramp]; 583 p->type = pt_fire; 584 for (j=0 ; j<3 ; j++) 585 p->org[j] = start[j] + ((rand()%6)-3); 586 break; 587 588 case 2: // blood 589 p->type = pt_grav; 590 p->color = 67 + (rand()&3); 591 for (j=0 ; j<3 ; j++) 592 p->org[j] = start[j] + ((rand()%6)-3); 593 break; 594 595 case 3: 596 case 5: // tracer 597 p->die = cl.time + 0.5; 598 p->type = pt_static; 599 if (type == 3) 600 p->color = 52 + ((tracercount&4)<<1); 601 else 602 p->color = 230 + ((tracercount&4)<<1); 603 604 tracercount++; 605 606 VectorCopy (start, p->org); 607 if (tracercount & 1) 608 { 609 p->vel[0] = 30*vec[1]; 610 p->vel[1] = 30*-vec[0]; 611 } 612 else 613 { 614 p->vel[0] = 30*-vec[1]; 615 p->vel[1] = 30*vec[0]; 616 } 617 break; 618 619 case 4: // slight blood 620 p->type = pt_grav; 621 p->color = 67 + (rand()&3); 622 for (j=0 ; j<3 ; j++) 623 p->org[j] = start[j] + ((rand()%6)-3); 624 len -= 3; 625 break; 626 627 case 6: // voor trail 628 p->color = 9*16 + 8 + (rand()&3); 629 p->type = pt_static; 630 p->die = cl.time + 0.3; 631 for (j=0 ; j<3 ; j++) 632 p->org[j] = start[j] + ((rand()&15)-8); 633 break; 634 } 635 636 637 VectorAdd (start, vec, start); 638 } 639 } 640 641 642 /* 643 =============== 644 R_DrawParticles 645 =============== 646 */ 647 extern cvar_t sv_gravity; 648 649 void R_DrawParticles (void) 650 { 651 particle_t *p, *kill; 652 float grav; 653 int i; 654 float time2, time3; 655 float time1; 656 float dvel; 657 float frametime; 658 #ifdef USE_OPENGLES 659 float* pPos = gVertexBuffer; 660 unsigned char* pColor = (unsigned char*) gColorBuffer; 661 unsigned char* pUV = (unsigned char*) gTexCoordBuffer; 662 int particleIndex = 0; 663 int maxParticleIndex = (int) sizeof(gVertexBuffer) / (sizeof(float) * 3) - 3; 664 #endif 665 #ifdef GLQUAKE 666 vec3_t up, right; 667 float scale; 668 669 GL_Bind(particletexture); 670 671 glEnable (GL_BLEND); 672 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); 673 674 #ifdef USE_OPENGLES 675 glEnableClientState(GL_COLOR_ARRAY); 676 glVertexPointer(3, GL_FLOAT, 0, gVertexBuffer); 677 glTexCoordPointer(2, GL_BYTE, 0, gTexCoordBuffer); 678 glColorPointer(4, GL_UNSIGNED_BYTE, 0, gColorBuffer); 679 glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST); 680 #else 681 glBegin (GL_TRIANGLES); 682 #endif 683 684 VectorScale (vup, 1.5, up); 685 VectorScale (vright, 1.5, right); 686 #else 687 D_StartParticles (); 688 689 VectorScale (vright, xscaleshrink, r_pright); 690 VectorScale (vup, yscaleshrink, r_pup); 691 VectorCopy (vpn, r_ppn); 692 #endif 693 frametime = cl.time - cl.oldtime; 694 time3 = frametime * 15; 695 time2 = frametime * 10; // 15; 696 time1 = frametime * 5; 697 grav = frametime * sv_gravity.value * 0.05; 698 dvel = 4*frametime; 699 700 for ( ;; ) 701 { 702 kill = active_particles; 703 if (kill && kill->die < cl.time) 704 { 705 active_particles = kill->next; 706 kill->next = free_particles; 707 free_particles = kill; 708 continue; 709 } 710 break; 711 } 712 713 for (p=active_particles ; p ; p=p->next) 714 { 715 for ( ;; ) 716 { 717 kill = p->next; 718 if (kill && kill->die < cl.time) 719 { 720 p->next = kill->next; 721 kill->next = free_particles; 722 free_particles = kill; 723 continue; 724 } 725 break; 726 } 727 728 #ifdef GLQUAKE 729 // hack a scale up to keep particles from disapearing 730 scale = (p->org[0] - r_origin[0])*vpn[0] + (p->org[1] - r_origin[1])*vpn[1] 731 + (p->org[2] - r_origin[2])*vpn[2]; 732 if (scale < 20) 733 scale = 1; 734 else 735 scale = 1 + scale * 0.004; 736 #ifdef USE_OPENGLES 737 738 if(particleIndex >= maxParticleIndex) 739 { 740 glDrawArrays(GL_TRIANGLES, 0, particleIndex); 741 particleIndex = 0; 742 pPos = gVertexBuffer; 743 pColor = (unsigned char*) gColorBuffer; 744 pUV = (unsigned char*) gTexCoordBuffer; 745 } 746 747 memcpy(pColor, (byte *)&d_8to24table[(int)p->color], 3); 748 pColor[3] = 255; 749 pColor += 4; 750 *pUV++ = 0; 751 *pUV++ = 0; 752 *pPos++ = p->org[0]; 753 *pPos++ = p->org[1]; 754 *pPos++ = p->org[2]; 755 756 memcpy(pColor, (byte *)&d_8to24table[(int)p->color], 3); 757 pColor[3] = 255; 758 pColor += 4; 759 *pUV++ = 1; 760 *pUV++ = 0; 761 *pPos++ = p->org[0] + up[0]*scale; 762 *pPos++ = p->org[1] + up[1]*scale; 763 *pPos++ = p->org[2] + up[2]*scale; 764 765 memcpy(pColor, (byte *)&d_8to24table[(int)p->color], 3); 766 pColor[3] = 255; 767 pColor += 4; 768 *pUV++ = 0; 769 *pUV++ = 1; 770 *pPos++ = p->org[0] + right[0]*scale; 771 *pPos++ = p->org[1] + right[1]*scale; 772 *pPos++ = p->org[2] + right[2]*scale; 773 774 particleIndex += 3; 775 776 #else 777 glColor3ubv ((byte *)&d_8to24table[(int)p->color]); 778 glTexCoord2f (0,0); 779 glVertex3fv (p->org); 780 glTexCoord2f (1,0); 781 glVertex3f (p->org[0] + up[0]*scale, p->org[1] + up[1]*scale, p->org[2] + up[2]*scale); 782 glTexCoord2f (0,1); 783 glVertex3f (p->org[0] + right[0]*scale, p->org[1] + right[1]*scale, p->org[2] + right[2]*scale); 784 #endif // !USE_OPENGLES 785 #else 786 D_DrawParticle (p); 787 #endif 788 p->org[0] += p->vel[0]*frametime; 789 p->org[1] += p->vel[1]*frametime; 790 p->org[2] += p->vel[2]*frametime; 791 792 switch (p->type) 793 { 794 case pt_static: 795 break; 796 case pt_fire: 797 p->ramp += time1; 798 if (p->ramp >= 6) 799 p->die = -1; 800 else 801 p->color = ramp3[(int)p->ramp]; 802 p->vel[2] += grav; 803 break; 804 805 case pt_explode: 806 p->ramp += time2; 807 if (p->ramp >=8) 808 p->die = -1; 809 else 810 p->color = ramp1[(int)p->ramp]; 811 for (i=0 ; i<3 ; i++) 812 p->vel[i] += p->vel[i]*dvel; 813 p->vel[2] -= grav; 814 break; 815 816 case pt_explode2: 817 p->ramp += time3; 818 if (p->ramp >=8) 819 p->die = -1; 820 else 821 p->color = ramp2[(int)p->ramp]; 822 for (i=0 ; i<3 ; i++) 823 p->vel[i] -= p->vel[i]*frametime; 824 p->vel[2] -= grav; 825 break; 826 827 case pt_blob: 828 for (i=0 ; i<3 ; i++) 829 p->vel[i] += p->vel[i]*dvel; 830 p->vel[2] -= grav; 831 break; 832 833 case pt_blob2: 834 for (i=0 ; i<2 ; i++) 835 p->vel[i] -= p->vel[i]*dvel; 836 p->vel[2] -= grav; 837 break; 838 839 case pt_grav: 840 #ifdef QUAKE2 841 p->vel[2] -= grav * 20; 842 break; 843 #endif 844 case pt_slowgrav: 845 p->vel[2] -= grav; 846 break; 847 default: 848 break; 849 } 850 } 851 852 #ifdef GLQUAKE 853 #ifdef USE_OPENGLES 854 glDrawArrays(GL_TRIANGLES, 0, particleIndex); 855 glDisableClientState(GL_COLOR_ARRAY); 856 glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); 857 #else 858 glEnd (); 859 #endif 860 glDisable (GL_BLEND); 861 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); 862 #else 863 D_EndParticles (); 864 #endif 865 } 866 867