rewrote memory system entirely (hunk, cache, and zone are gone, memory pools replaced...
[xonotic/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         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 + 64; // was + 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         for (i = 0;i < 8;i++)
126         {
127                 float p[32][32];
128                 fractalnoise(&noise1[0][0], 64, 8);
129                 for (y = 0;y < 32;y++)
130                         for (x = 0;x < 32;x++)
131                                 p[y][x] = (noise1[y][x] / 8.0f) - 64.0f;
132                 for (m = 0;m < 32;m++)
133                 {
134                         int j;
135                         float fx, fy, f;
136                         fx = lhrandom(14, 18);
137                         fy = lhrandom(14, 18);
138                         do
139                         {
140                                 dx = lhrandom(-1, 1);
141                                 dy = lhrandom(-1, 1);
142                                 f = (dx * dx + dy * dy);
143                         }
144                         while(f < 0.125f || f > 1.0f);
145                         f = (m + 1) / 40.0f; //lhrandom(0.0f, 1.0);
146                         dx *= 1.0f / 32.0f;
147                         dy *= 1.0f / 32.0f;
148                         for (j = 0;f > 0 && j < (32 * 14);j++)
149                         {
150                                 y = fy;
151                                 x = fx;
152                                 fx += dx;
153                                 fy += dy;
154                                 if (x < 1 || y < 1 || x >= 31 || y >= 31)
155                                         break;
156                                 p[y - 1][x - 1] += f * 0.125f;
157                                 p[y - 1][x    ] += f * 0.25f;
158                                 p[y - 1][x + 1] += f * 0.125f;
159                                 p[y    ][x - 1] += f * 0.25f;
160                                 p[y    ][x    ] += f;
161                                 p[y    ][x + 1] += f * 0.25f;
162                                 p[y + 1][x - 1] += f * 0.125f;
163                                 p[y + 1][x    ] += f * 0.25f;
164                                 p[y + 1][x + 1] += f * 0.125f;
165 //                              f -= (0.5f / (32 * 16));
166                         }
167                 }
168                 for (y = 0;y < 32;y++)
169                 {
170                         for (x = 0;x < 32;x++)
171                         {
172                                 m = p[y][x];
173                                 data[y][x][0] = data[y][x][1] = data[y][x][2] = 255;
174                                 data[y][x][3] = (byte) bound(0, m, 255);
175                         }
176                 }
177
178                 setuptex(i + 8, 0, i + 16, &data[0][0][0], particletexturedata);
179                 setuptex(i + 8, 1, i + 16, &data[0][0][0], particletexturedata);
180         }
181
182         for (i = 0;i < 16;i++)
183         {
184                 radius = i * 3.0f / 16.0f;
185                 f2 = 255.0f * ((15.0f - i) / 15.0f);
186                 for (y = 0;y < 32;y++)
187                 {
188                         dy = (y - 16) * 0.25f;
189                         for (x = 0;x < 32;x++)
190                         {
191                                 dx = (x - 16) * 0.25f;
192                                 data[y][x][0] = data[y][x][1] = data[y][x][2] = 255;
193                                 f = (1.0 - fabs(radius - sqrt(dx*dx+dy*dy))) * f2;
194                                 f = bound(0.0f, f, 255.0f);
195                                 data[y][x][3] = (int) f;
196                         }
197                 }
198                 setuptex(i + 16, 0, i + 24, &data[0][0][0], particletexturedata);
199                 setuptex(i + 16, 1, i + 24, &data[0][0][0], particletexturedata);
200         }
201
202         for (y = 0;y < 32;y++)
203         {
204                 dy = y - 16;
205                 for (x = 0;x < 32;x++)
206                 {
207                         data[y][x][0] = data[y][x][1] = data[y][x][2] = 255;
208                         dx = x - 16;
209                         d = (256 - (dx*dx+dy*dy));
210                         d = bound(0, d, 255);
211                         data[y][x][3] = (byte) d;
212                 }
213         }
214         setuptex(32, 0, 40, &data[0][0][0], particletexturedata);
215         setuptex(32, 1, 40, &data[0][0][0], particletexturedata);
216
217         light[0] = 1;light[1] = 1;light[2] = 1;
218         VectorNormalize(light);
219         for (y = 0;y < 32;y++)
220         {
221                 for (x = 0;x < 32;x++)
222                 {
223                         data[y][x][0] = data[y][x][1] = data[y][x][2] = 255;
224                         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);
225                 }
226         }
227         setuptex(33, 0, 41, &data[0][0][0], particletexturedata);
228         setuptex(33, 1, 41, &data[0][0][0], particletexturedata);
229
230         light[0] = 1;light[1] = 1;light[2] = 1;
231         VectorNormalize(light);
232         for (y = 0;y < 32;y++)
233         {
234                 for (x = 0;x < 32;x++)
235                 {
236                         data[y][x][0] = data[y][x][1] = data[y][x][2] = 255;
237                         data[y][x][3] = shadebubble((x - 16) * (1.0 / 16.0), (y - 16) * (1.0 / 16.0), light);
238                 }
239         }
240         setuptex(34, 0, 42, &data[0][0][0], particletexturedata);
241         setuptex(34, 1, 42, &data[0][0][0], particletexturedata);
242
243         for (y = 0;y < 32;y++)
244         {
245                 dy = y - 16;
246                 for (x = 0;x < 32;x++)
247                 {
248                         dx = x - 16;
249                         d = (2048.0f / (dx*dx+dy*dy+1)) - 8.0f;
250                         data[y][x][0] = bound(0, d * 1.0f, 255);
251                         data[y][x][1] = bound(0, d * 0.8f, 255);
252                         data[y][x][2] = bound(0, d * 0.5f, 255);
253                         data[y][x][3] = bound(0, d * 1.0f, 255);
254                 }
255         }
256         setuptex(35, 0, 43, &data[0][0][0], particletexturedata);
257         for (y = 0;y < 32;y++)
258                 for (x = 0;x < 32;x++)
259                         data[y][x][0] = data[y][x][1] = data[y][x][2] = 255;
260         setuptex(35, 1, 44, &data[0][0][0], particletexturedata);
261
262         particlefonttexture = R_LoadTexture (particletexturepool, "particlefont", 256, 256, particletexturedata, TEXTYPE_RGBA, TEXF_ALPHA | TEXF_PRECACHE);
263 }
264
265 static void r_part_start(void)
266 {
267         particletexturepool = R_AllocTexturePool();
268         R_InitParticleTexture ();
269 }
270
271 static void r_part_shutdown(void)
272 {
273         R_FreeTexturePool(&particletexturepool);
274 }
275
276 static void r_part_newmap(void)
277 {
278 }
279
280 void R_Particles_Init (void)
281 {
282         Cvar_RegisterVariable(&r_drawparticles);
283         Cvar_RegisterVariable(&r_particles_lighting);
284         R_RegisterModule("R_Particles", r_part_start, r_part_shutdown, r_part_newmap);
285 }
286
287 int partindexarray[6] = {0, 1, 2, 0, 2, 3};
288
289 void R_DrawParticles (void)
290 {
291         renderparticle_t *r;
292         int i, lighting;
293         float minparticledist, org[3], uprightangles[3], up2[3], right2[3], v[3], right[3], up[3], tv[4][5], fog, diff[3];
294         mleaf_t *leaf;
295         particletexture_t *tex, *texfog;
296         rmeshinfo_t m;
297
298         // LordHavoc: early out conditions
299         if ((!r_refdef.numparticles) || (!r_drawparticles.integer))
300                 return;
301
302         lighting = r_particles_lighting.integer;
303         if (!r_dynamic.integer)
304                 lighting = 0;
305
306         c_particles += r_refdef.numparticles;
307
308         uprightangles[0] = 0;
309         uprightangles[1] = r_refdef.viewangles[1];
310         uprightangles[2] = 0;
311         AngleVectors (uprightangles, NULL, right2, up2);
312
313         minparticledist = DotProduct(r_origin, vpn) + 16.0f;
314
315         memset(&m, 0, sizeof(m));
316         m.transparent = true;
317         m.blendfunc1 = GL_SRC_ALPHA;
318         m.blendfunc2 = GL_ONE_MINUS_SRC_ALPHA;
319         m.numtriangles = 2;
320         m.index = partindexarray;
321         m.numverts = 4;
322         m.vertex = &tv[0][0];
323         m.vertexstep = sizeof(float[5]);
324         m.tex[0] = R_GetTexture(particlefonttexture);
325         m.texcoords[0] = &tv[0][3];
326         m.texcoordstep[0] = sizeof(float[5]);
327
328         for (i = 0, r = r_refdef.particles;i < r_refdef.numparticles;i++, r++)
329         {
330                 // LordHavoc: only render if not too close
331                 if (DotProduct(r->org, vpn) < minparticledist)
332                         continue;
333
334                 // LordHavoc: check if it's in a visible leaf
335                 leaf = Mod_PointInLeaf(r->org, cl.worldmodel);
336                 if (leaf->visframe != r_framecount)
337                         continue;
338
339                 VectorCopy(r->org, org);
340                 if (r->orientation == PARTICLE_BILLBOARD)
341                 {
342                         VectorScale(vright, r->scale, right);
343                         VectorScale(vup, r->scale, up);
344                 }
345                 else if (r->orientation == PARTICLE_UPRIGHT_FACING)
346                 {
347                         VectorScale(right2, r->scale, right);
348                         VectorScale(up2, r->scale, up);
349                 }
350                 else if (r->orientation == PARTICLE_ORIENTED_DOUBLESIDED)
351                 {
352                         // double-sided
353                         if (DotProduct(r->dir, r_origin) > DotProduct(r->dir, org))
354                         {
355                                 VectorNegate(r->dir, v);
356                                 VectorVectors(v, right, up);
357                         }
358                         else
359                                 VectorVectors(r->dir, right, up);
360                         VectorScale(right, r->scale, right);
361                         VectorScale(up, r->scale, up);
362                 }
363                 else
364                         Host_Error("R_DrawParticles: unknown particle orientation %i\n", r->orientation);
365
366                 m.cr = r->color[0];
367                 m.cg = r->color[1];
368                 m.cb = r->color[2];
369                 m.ca = r->color[3];
370                 if (lighting >= 1 && (r->dynlight || lighting >= 2))
371                 {
372                         R_CompleteLightPoint(v, org, true, leaf);
373                         m.cr *= v[0];
374                         m.cg *= v[1];
375                         m.cb *= v[2];
376                 }
377
378                 tex = &particletexture[r->tex][0];
379                 texfog = &particletexture[r->tex][1];
380
381                 fog = 0;
382                 if (fogenabled)
383                 {
384                         VectorSubtract(org, r_origin, diff);
385                         fog = exp(fogdensity/DotProduct(diff,diff));
386                         if (fog >= 0.01f)
387                         {
388                                 if (fog > 1)
389                                         fog = 1;
390                                 m.cr *= 1 - fog;
391                                 m.cg *= 1 - fog;
392                                 m.cb *= 1 - fog;
393                                 if (tex->s1 == texfog->s1 && tex->t1 == texfog->t1)
394                                 {
395                                         m.cr += fogcolor[0] * fog;
396                                         m.cg += fogcolor[1] * fog;
397                                         m.cb += fogcolor[2] * fog;
398                                 }
399                         }
400                         else
401                                 fog = 0;
402                 }
403
404                 tv[0][0] = org[0] - right[0] - up[0];
405                 tv[0][1] = org[1] - right[1] - up[1];
406                 tv[0][2] = org[2] - right[2] - up[2];
407                 tv[0][3] = tex->s1;
408                 tv[0][4] = tex->t1;
409                 tv[1][0] = org[0] - right[0] + up[0];
410                 tv[1][1] = org[1] - right[1] + up[1];
411                 tv[1][2] = org[2] - right[2] + up[2];
412                 tv[1][3] = tex->s1;
413                 tv[1][4] = tex->t2;
414                 tv[2][0] = org[0] + right[0] + up[0];
415                 tv[2][1] = org[1] + right[1] + up[1];
416                 tv[2][2] = org[2] + right[2] + up[2];
417                 tv[2][3] = tex->s2;
418                 tv[2][4] = tex->t2;
419                 tv[3][0] = org[0] + right[0] - up[0];
420                 tv[3][1] = org[1] + right[1] - up[1];
421                 tv[3][2] = org[2] + right[2] - up[2];
422                 tv[3][3] = tex->s2;
423                 tv[3][4] = tex->t1;
424
425                 R_Mesh_Draw(&m);
426
427                 if (fog && (tex->s1 != texfog->s1 || tex->t1 != texfog->t1))
428                 {
429                         m.blendfunc2 = GL_ONE;
430                         m.cr = fogcolor[0];
431                         m.cg = fogcolor[1];
432                         m.cb = fogcolor[2];
433                         m.ca = r->color[3] * fog;
434
435                         tv[0][3] = texfog->s1;
436                         tv[0][4] = texfog->t1;
437                         tv[1][3] = texfog->s1;
438                         tv[1][4] = texfog->t2;
439                         tv[2][3] = texfog->s2;
440                         tv[2][4] = texfog->t2;
441                         tv[3][3] = texfog->s2;
442                         tv[3][4] = texfog->t1;
443
444                         R_Mesh_Draw(&m);
445                         m.blendfunc2 = GL_ONE_MINUS_SRC_ALPHA;
446                 }
447         }
448 }