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