made most particles additive (speedup in fog)
[divverent/darkplaces.git] / r_particles.c
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
23 static rtexturepool_t *particletexturepool;
24
25 // these are used by the decal system so they can't be static
26 rtexture_t *particlefonttexture;
27 // [0] is normal, [1] is fog, they may be the same
28 particletexture_t particletexture[MAX_PARTICLETEXTURES][2];
29
30 static cvar_t r_drawparticles = {0, "r_drawparticles", "1"};
31 static cvar_t r_particles_lighting = {0, "r_particles_lighting", "1"};
32
33 static byte shadebubble(float dx, float dy, vec3_t light)
34 {
35         float   dz, f, dot;
36         vec3_t  normal;
37         dz = 1 - (dx*dx+dy*dy);
38         if (dz > 0) // it does hit the sphere
39         {
40                 f = 0;
41                 // back side
42                 normal[0] = dx;normal[1] = dy;normal[2] = dz;
43                 VectorNormalize(normal);
44                 dot = DotProduct(normal, light);
45                 if (dot > 0.5) // interior reflection
46                         f += ((dot *  2) - 1);
47                 else if (dot < -0.5) // exterior reflection
48                         f += ((dot * -2) - 1);
49                 // front side
50                 normal[0] = dx;normal[1] = dy;normal[2] = -dz;
51                 VectorNormalize(normal);
52                 dot = DotProduct(normal, light);
53                 if (dot > 0.5) // interior reflection
54                         f += ((dot *  2) - 1);
55                 else if (dot < -0.5) // exterior reflection
56                         f += ((dot * -2) - 1);
57                 f *= 128;
58                 f += 16; // just to give it a haze so you can see the outline
59                 f = bound(0, f, 255);
60                 return (byte) f;
61         }
62         else
63                 return 0;
64 }
65
66 static void setuptex(int cltexnum, int fog, int rtexnum, byte *data, byte *particletexturedata)
67 {
68         int basex, basey, y;
69         basex = ((rtexnum >> 0) & 7) * 32;
70         basey = ((rtexnum >> 3) & 7) * 32;
71         particletexture[cltexnum][fog].s1 = (basex + 1) / 256.0f;
72         particletexture[cltexnum][fog].t1 = (basey + 1) / 256.0f;
73         particletexture[cltexnum][fog].s2 = (basex + 31) / 256.0f;
74         particletexture[cltexnum][fog].t2 = (basey + 31) / 256.0f;
75         for (y = 0;y < 32;y++)
76                 memcpy(particletexturedata + ((basey + y) * 256 + basex) * 4, data + y * 32 * 4, 32 * 4);
77 }
78
79 static void R_InitParticleTexture (void)
80 {
81         int             x,y,d,i,m;
82         float   dx, dy, radius, f, f2;
83         byte    data[32][32][4], noise1[64][64], noise2[64][64];
84         vec3_t  light;
85         byte    particletexturedata[256*256*4];
86
87         memset(particletexturedata, 255, sizeof(particletexturedata));
88
89         // the particletexture[][] array numbers must match the cl_part.c textures
90         // smoke/blood
91         for (i = 0;i < 8;i++)
92         {
93                 do
94                 {
95                         fractalnoise(&noise1[0][0], 64, 4);
96                         fractalnoise(&noise2[0][0], 64, 8);
97                         m = 0;
98                         for (y = 0;y < 32;y++)
99                         {
100                                 dy = y - 16;
101                                 for (x = 0;x < 32;x++)
102                                 {
103                                         d = (noise1[y][x] - 128) * 2 + 64; // was + 128
104                                         d = bound(0, d, 255);
105                                         data[y][x][0] = data[y][x][1] = data[y][x][2] = d;
106                                         dx = x - 16;
107                                         d = (noise2[y][x] - 128) * 3 + 192;
108                                         if (d > 0)
109                                                 d = (d * (256 - (int) (dx*dx+dy*dy))) >> 8;
110                                         d = bound(0, d, 255);
111                                         data[y][x][3] = (byte) d;
112                                         if (m < d)
113                                                 m = d;
114                                 }
115                         }
116                 }
117                 while (m < 224);
118
119                 setuptex(i + 0, 0, i + 0, &data[0][0][0], particletexturedata);
120                 for (y = 0;y < 32;y++)
121                         for (x = 0;x < 32;x++)
122                                 data[y][x][0] = data[y][x][1] = data[y][x][2] = 255;
123                 setuptex(i + 0, 1, i + 8, &data[0][0][0], particletexturedata);
124         }
125
126         // bullet hole
127         for (i = 0;i < 8;i++)
128         {
129                 float p[32][32];
130                 fractalnoise(&noise1[0][0], 64, 8);
131                 for (y = 0;y < 32;y++)
132                         for (x = 0;x < 32;x++)
133                                 p[y][x] = (noise1[y][x] / 8.0f) - 64.0f;
134                 for (m = 0;m < 32;m++)
135                 {
136                         int j;
137                         float fx, fy, f;
138                         fx = lhrandom(14, 18);
139                         fy = lhrandom(14, 18);
140                         do
141                         {
142                                 dx = lhrandom(-1, 1);
143                                 dy = lhrandom(-1, 1);
144                                 f = (dx * dx + dy * dy);
145                         }
146                         while(f < 0.125f || f > 1.0f);
147                         f = (m + 1) / 40.0f; //lhrandom(0.0f, 1.0);
148                         dx *= 1.0f / 32.0f;
149                         dy *= 1.0f / 32.0f;
150                         for (j = 0;f > 0 && j < (32 * 14);j++)
151                         {
152                                 y = fy;
153                                 x = fx;
154                                 fx += dx;
155                                 fy += dy;
156                                 if (x < 1 || y < 1 || x >= 31 || y >= 31)
157                                         break;
158                                 p[y - 1][x - 1] += f * 0.125f;
159                                 p[y - 1][x    ] += f * 0.25f;
160                                 p[y - 1][x + 1] += f * 0.125f;
161                                 p[y    ][x - 1] += f * 0.25f;
162                                 p[y    ][x    ] += f;
163                                 p[y    ][x + 1] += f * 0.25f;
164                                 p[y + 1][x - 1] += f * 0.125f;
165                                 p[y + 1][x    ] += f * 0.25f;
166                                 p[y + 1][x + 1] += f * 0.125f;
167 //                              f -= (0.5f / (32 * 16));
168                         }
169                 }
170                 for (y = 0;y < 32;y++)
171                 {
172                         for (x = 0;x < 32;x++)
173                         {
174                                 m = p[y][x];
175                                 data[y][x][0] = data[y][x][1] = data[y][x][2] = 255;
176                                 data[y][x][3] = (byte) bound(0, m, 255);
177                         }
178                 }
179
180                 setuptex(i + 8, 0, i + 16, &data[0][0][0], particletexturedata);
181                 setuptex(i + 8, 1, i + 16, &data[0][0][0], particletexturedata);
182         }
183
184         // rain splash
185         for (i = 0;i < 16;i++)
186         {
187                 radius = i * 3.0f / 16.0f;
188                 f2 = 255.0f * ((15.0f - i) / 15.0f);
189                 for (y = 0;y < 32;y++)
190                 {
191                         dy = (y - 16) * 0.25f;
192                         for (x = 0;x < 32;x++)
193                         {
194                                 dx = (x - 16) * 0.25f;
195                                 data[y][x][0] = data[y][x][1] = data[y][x][2] = 255;
196                                 f = (1.0 - fabs(radius - sqrt(dx*dx+dy*dy))) * f2;
197                                 f = bound(0.0f, f, 255.0f);
198                                 data[y][x][3] = (int) f;
199                         }
200                 }
201                 setuptex(i + 16, 0, i + 24, &data[0][0][0], particletexturedata);
202                 setuptex(i + 16, 1, i + 24, &data[0][0][0], particletexturedata);
203         }
204
205         // normal particle
206         for (y = 0;y < 32;y++)
207         {
208                 dy = y - 16;
209                 for (x = 0;x < 32;x++)
210                 {
211                         data[y][x][0] = data[y][x][1] = data[y][x][2] = 255;
212                         dx = x - 16;
213                         d = (256 - (dx*dx+dy*dy));
214                         d = bound(0, d, 255);
215                         data[y][x][3] = (byte) d;
216                 }
217         }
218         setuptex(32, 0, 40, &data[0][0][0], particletexturedata);
219         setuptex(32, 1, 40, &data[0][0][0], particletexturedata);
220
221         // rain
222         light[0] = 1;light[1] = 1;light[2] = 1;
223         VectorNormalize(light);
224         for (y = 0;y < 32;y++)
225         {
226                 for (x = 0;x < 32;x++)
227                 {
228                         data[y][x][0] = data[y][x][1] = data[y][x][2] = 255;
229                         data[y][x][3] = shadebubble((x - 16) * (1.0 / 8.0), y < 24 ? (y - 24) * (1.0 / 24.0) : (y - 24) * (1.0 / 8.0), light);
230                 }
231         }
232         setuptex(33, 0, 41, &data[0][0][0], particletexturedata);
233         setuptex(33, 1, 41, &data[0][0][0], particletexturedata);
234
235         // bubble
236         light[0] = 1;light[1] = 1;light[2] = 1;
237         VectorNormalize(light);
238         for (y = 0;y < 32;y++)
239         {
240                 for (x = 0;x < 32;x++)
241                 {
242                         data[y][x][0] = data[y][x][1] = data[y][x][2] = 255;
243                         data[y][x][3] = shadebubble((x - 16) * (1.0 / 16.0), (y - 16) * (1.0 / 16.0), light);
244                 }
245         }
246         setuptex(34, 0, 42, &data[0][0][0], particletexturedata);
247         setuptex(34, 1, 42, &data[0][0][0], particletexturedata);
248
249         // rocket flare
250         for (y = 0;y < 32;y++)
251         {
252                 dy = y - 16;
253                 for (x = 0;x < 32;x++)
254                 {
255                         dx = x - 16;
256                         d = (2048.0f / (dx*dx+dy*dy+1)) - 8.0f;
257                         data[y][x][0] = bound(0, d * 1.0f, 255);
258                         data[y][x][1] = bound(0, d * 0.8f, 255);
259                         data[y][x][2] = bound(0, d * 0.5f, 255);
260                         data[y][x][3] = bound(0, d * 1.0f, 255);
261                 }
262         }
263         setuptex(35, 0, 43, &data[0][0][0], particletexturedata);
264         for (y = 0;y < 32;y++)
265                 for (x = 0;x < 32;x++)
266                         data[y][x][0] = data[y][x][1] = data[y][x][2] = 255;
267         setuptex(35, 1, 44, &data[0][0][0], particletexturedata);
268
269         particlefonttexture = R_LoadTexture (particletexturepool, "particlefont", 256, 256, particletexturedata, TEXTYPE_RGBA, TEXF_ALPHA | TEXF_PRECACHE);
270 }
271
272 static void r_part_start(void)
273 {
274         particletexturepool = R_AllocTexturePool();
275         R_InitParticleTexture ();
276 }
277
278 static void r_part_shutdown(void)
279 {
280         R_FreeTexturePool(&particletexturepool);
281 }
282
283 static void r_part_newmap(void)
284 {
285 }
286
287 void R_Particles_Init (void)
288 {
289         Cvar_RegisterVariable(&r_drawparticles);
290         Cvar_RegisterVariable(&r_particles_lighting);
291         R_RegisterModule("R_Particles", r_part_start, r_part_shutdown, r_part_newmap);
292 }
293
294 //int partindexarray[6] = {0, 1, 2, 0, 2, 3};
295
296 void R_DrawParticles (void)
297 {
298         renderparticle_t *r;
299         int i, lighting;
300         float minparticledist, org[3], uprightangles[3], up2[3], right2[3], v[3], right[3], up[3], tvxyz[4][4], tvst[4][2], fog, ifog, fogvec[3];
301         mleaf_t *leaf;
302         particletexture_t *tex, *texfog;
303         rmeshinfo_t m;
304
305         // LordHavoc: early out conditions
306         if ((!r_refdef.numparticles) || (!r_drawparticles.integer))
307                 return;
308
309         lighting = r_particles_lighting.integer;
310         if (!r_dynamic.integer)
311                 lighting = 0;
312
313         c_particles += r_refdef.numparticles;
314
315         uprightangles[0] = 0;
316         uprightangles[1] = r_refdef.viewangles[1];
317         uprightangles[2] = 0;
318         AngleVectors (uprightangles, NULL, right2, up2);
319
320         minparticledist = DotProduct(r_origin, vpn) + 16.0f;
321
322         // LordHavoc: this meshinfo must match up with R_Mesh_DrawDecal
323         // LordHavoc: the commented out lines are hardwired behavior in R_Mesh_DrawDecal
324         memset(&m, 0, sizeof(m));
325         m.transparent = true;
326         m.blendfunc1 = GL_SRC_ALPHA;
327         m.blendfunc2 = GL_ONE_MINUS_SRC_ALPHA;
328         //m.numtriangles = 2;
329         //m.index = partindexarray;
330         //m.numverts = 4;
331         m.vertex = &tvxyz[0][0];
332         //m.vertexstep = sizeof(float[4]);
333         m.tex[0] = R_GetTexture(particlefonttexture);
334         m.texcoords[0] = &tvst[0][0];
335         //m.texcoordstep[0] = sizeof(float[2]);
336
337         for (i = 0, r = r_refdef.particles;i < r_refdef.numparticles;i++, r++)
338         {
339                 // LordHavoc: only render if not too close
340                 if (DotProduct(r->org, vpn) < minparticledist)
341                         continue;
342
343                 // LordHavoc: check if it's in a visible leaf
344                 leaf = Mod_PointInLeaf(r->org, cl.worldmodel);
345                 if (leaf->visframe != r_framecount)
346                         continue;
347
348                 VectorCopy(r->org, org);
349                 if (r->orientation == PARTICLE_BILLBOARD)
350                 {
351                         VectorScale(vright, r->scalex, right);
352                         VectorScale(vup, r->scaley, up);
353                 }
354                 else if (r->orientation == PARTICLE_UPRIGHT_FACING)
355                 {
356                         VectorScale(right2, r->scalex, right);
357                         VectorScale(up2, r->scaley, up);
358                 }
359                 else if (r->orientation == PARTICLE_ORIENTED_DOUBLESIDED)
360                 {
361                         // double-sided
362                         if (DotProduct(r->dir, r_origin) > DotProduct(r->dir, org))
363                         {
364                                 VectorNegate(r->dir, v);
365                                 VectorVectors(v, right, up);
366                         }
367                         else
368                                 VectorVectors(r->dir, right, up);
369                         VectorScale(right, r->scalex, right);
370                         VectorScale(up, r->scaley, up);
371                 }
372                 else
373                         Host_Error("R_DrawParticles: unknown particle orientation %i\n", r->orientation);
374
375                 m.cr = r->color[0];
376                 m.cg = r->color[1];
377                 m.cb = r->color[2];
378                 m.ca = r->color[3];
379                 if (lighting >= 1 && (r->dynlight || lighting >= 2))
380                 {
381                         R_CompleteLightPoint(v, org, true, leaf);
382                         m.cr *= v[0];
383                         m.cg *= v[1];
384                         m.cb *= v[2];
385                 }
386
387                 tex = &particletexture[r->tex][0];
388
389                 tvxyz[0][0] = org[0] - right[0] - up[0];
390                 tvxyz[0][1] = org[1] - right[1] - up[1];
391                 tvxyz[0][2] = org[2] - right[2] - up[2];
392                 tvxyz[1][0] = org[0] - right[0] + up[0];
393                 tvxyz[1][1] = org[1] - right[1] + up[1];
394                 tvxyz[1][2] = org[2] - right[2] + up[2];
395                 tvxyz[2][0] = org[0] + right[0] + up[0];
396                 tvxyz[2][1] = org[1] + right[1] + up[1];
397                 tvxyz[2][2] = org[2] + right[2] + up[2];
398                 tvxyz[3][0] = org[0] + right[0] - up[0];
399                 tvxyz[3][1] = org[1] + right[1] - up[1];
400                 tvxyz[3][2] = org[2] + right[2] - up[2];
401                 tvst[0][0] = tex->s1;
402                 tvst[0][1] = tex->t1;
403                 tvst[1][0] = tex->s1;
404                 tvst[1][1] = tex->t2;
405                 tvst[2][0] = tex->s2;
406                 tvst[2][1] = tex->t2;
407                 tvst[3][0] = tex->s2;
408                 tvst[3][1] = tex->t1;
409
410                 if (r->additive)
411                 {
412                         m.blendfunc2 = GL_ONE;
413                         fog = 0;
414                         if (fogenabled)
415                         {
416                                 texfog = &particletexture[r->tex][1];
417                                 VectorSubtract(org, r_origin, fogvec);
418                                 ifog = 1 - exp(fogdensity/DotProduct(fogvec,fogvec));
419                                 if (ifog < (1.0f - (1.0f / 64.0f)))
420                                 {
421                                         if (ifog >= (1.0f / 64.0f))
422                                         {
423                                                 // partially fogged, darken it
424                                                 m.cr *= ifog;
425                                                 m.cg *= ifog;
426                                                 m.cb *= ifog;
427                                                 R_Mesh_DrawDecal(&m);
428                                         }
429                                 }
430                                 else
431                                         R_Mesh_DrawDecal(&m);
432                         }
433                         else
434                                 R_Mesh_DrawDecal(&m);
435                 }
436                 else
437                 {
438                         m.blendfunc2 = GL_ONE_MINUS_SRC_ALPHA;
439                         fog = 0;
440                         if (fogenabled)
441                         {
442                                 texfog = &particletexture[r->tex][1];
443                                 VectorSubtract(org, r_origin, fogvec);
444                                 fog = exp(fogdensity/DotProduct(fogvec,fogvec));
445                                 if (fog >= (1.0f / 64.0f))
446                                 {
447                                         if (fog >= (1.0f - (1.0f / 64.0f)))
448                                         {
449                                                 // fully fogged, just use the fog texture and render as alpha
450                                                 m.cr = fogcolor[0];
451                                                 m.cg = fogcolor[1];
452                                                 m.cb = fogcolor[2];
453                                                 m.ca = r->color[3];
454                                                 tvst[0][0] = texfog->s1;
455                                                 tvst[0][1] = texfog->t1;
456                                                 tvst[1][0] = texfog->s1;
457                                                 tvst[1][1] = texfog->t2;
458                                                 tvst[2][0] = texfog->s2;
459                                                 tvst[2][1] = texfog->t2;
460                                                 tvst[3][0] = texfog->s2;
461                                                 tvst[3][1] = texfog->t1;
462                                                 R_Mesh_DrawDecal(&m);
463                                         }
464                                         else
465                                         {
466                                                 // partially fogged, darken the first pass
467                                                 ifog = 1 - fog;
468                                                 m.cr *= ifog;
469                                                 m.cg *= ifog;
470                                                 m.cb *= ifog;
471                                                 if (tex->s1 == texfog->s1 && tex->t1 == texfog->t1)
472                                                 {
473                                                         // fog texture is the same as the base, just change the color
474                                                         m.cr += fogcolor[0] * fog;
475                                                         m.cg += fogcolor[1] * fog;
476                                                         m.cb += fogcolor[2] * fog;
477                                                         R_Mesh_DrawDecal(&m);
478                                                 }
479                                                 else
480                                                 {
481                                                         // render the first pass (alpha), then do additive fog
482                                                         R_Mesh_DrawDecal(&m);
483
484                                                         m.blendfunc2 = GL_ONE;
485                                                         m.cr = fogcolor[0];
486                                                         m.cg = fogcolor[1];
487                                                         m.cb = fogcolor[2];
488                                                         m.ca = r->color[3] * fog;
489                                                         tvst[0][0] = texfog->s1;
490                                                         tvst[0][1] = texfog->t1;
491                                                         tvst[1][0] = texfog->s1;
492                                                         tvst[1][1] = texfog->t2;
493                                                         tvst[2][0] = texfog->s2;
494                                                         tvst[2][1] = texfog->t2;
495                                                         tvst[3][0] = texfog->s2;
496                                                         tvst[3][1] = texfog->t1;
497                                                         R_Mesh_DrawDecal(&m);
498                                                 }
499                                         }
500                                 }
501                                 else
502                                         R_Mesh_DrawDecal(&m);
503                         }
504                         else
505                                 R_Mesh_DrawDecal(&m);
506                 }
507         }
508 }