2 Copyright (C) 1996-1997 Id Software, Inc.
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.
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.
13 See the GNU General Public License for more details.
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.
23 static rtexturepool_t *particletexturepool;
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];
30 static cvar_t r_drawparticles = {0, "r_drawparticles", "1"};
31 static cvar_t r_particles_lighting = {0, "r_particles_lighting", "1"};
33 static byte shadebubble(float dx, float dy, vec3_t light)
37 dz = 1 - (dx*dx+dy*dy);
38 if (dz > 0) // it does hit the sphere
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
47 else if (dot < -0.5) // exterior reflection
48 f += ((dot * -2) - 1);
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
55 else if (dot < -0.5) // exterior reflection
56 f += ((dot * -2) - 1);
58 f += 16; // just to give it a haze so you can see the outline
66 static void setuptex(int cltexnum, int fog, int rtexnum, byte *data, byte *particletexturedata)
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);
79 static void R_InitParticleTexture (void)
82 float dx, dy, radius, f, f2;
83 byte data[32][32][4], noise1[64][64], noise2[64][64];
85 byte particletexturedata[256*256*4];
87 memset(particletexturedata, 255, sizeof(particletexturedata));
89 // the particletexture[][] array numbers must match the cl_part.c textures
94 fractalnoise(&noise1[0][0], 64, 4);
95 fractalnoise(&noise2[0][0], 64, 8);
97 for (y = 0;y < 32;y++)
100 for (x = 0;x < 32;x++)
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;
106 d = (noise2[y][x] - 128) * 3 + 192;
108 d = (d * (256 - (int) (dx*dx+dy*dy))) >> 8;
109 d = bound(0, d, 255);
110 data[y][x][3] = (byte) d;
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);
125 for (i = 0;i < 8;i++)
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++)
136 fx = lhrandom(14, 18);
137 fy = lhrandom(14, 18);
140 dx = lhrandom(-1, 1);
141 dy = lhrandom(-1, 1);
142 f = (dx * dx + dy * dy);
144 while(f < 0.125f || f > 1.0f);
145 f = (m + 1) / 40.0f; //lhrandom(0.0f, 1.0);
148 for (j = 0;f > 0 && j < (32 * 14);j++)
154 if (x < 1 || y < 1 || x >= 31 || y >= 31)
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;
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));
168 for (y = 0;y < 32;y++)
170 for (x = 0;x < 32;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);
178 setuptex(i + 8, 0, i + 16, &data[0][0][0], particletexturedata);
179 setuptex(i + 8, 1, i + 16, &data[0][0][0], particletexturedata);
182 for (i = 0;i < 16;i++)
184 radius = i * 3.0f / 16.0f;
185 f2 = 255.0f * ((15.0f - i) / 15.0f);
186 for (y = 0;y < 32;y++)
188 dy = (y - 16) * 0.25f;
189 for (x = 0;x < 32;x++)
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;
198 setuptex(i + 16, 0, i + 24, &data[0][0][0], particletexturedata);
199 setuptex(i + 16, 1, i + 24, &data[0][0][0], particletexturedata);
202 for (y = 0;y < 32;y++)
205 for (x = 0;x < 32;x++)
207 data[y][x][0] = data[y][x][1] = data[y][x][2] = 255;
209 d = (256 - (dx*dx+dy*dy));
210 d = bound(0, d, 255);
211 data[y][x][3] = (byte) d;
214 setuptex(32, 0, 40, &data[0][0][0], particletexturedata);
215 setuptex(32, 1, 40, &data[0][0][0], particletexturedata);
217 light[0] = 1;light[1] = 1;light[2] = 1;
218 VectorNormalize(light);
219 for (y = 0;y < 32;y++)
221 for (x = 0;x < 32;x++)
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);
227 setuptex(33, 0, 41, &data[0][0][0], particletexturedata);
228 setuptex(33, 1, 41, &data[0][0][0], particletexturedata);
230 light[0] = 1;light[1] = 1;light[2] = 1;
231 VectorNormalize(light);
232 for (y = 0;y < 32;y++)
234 for (x = 0;x < 32;x++)
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);
240 setuptex(34, 0, 42, &data[0][0][0], particletexturedata);
241 setuptex(34, 1, 42, &data[0][0][0], particletexturedata);
243 for (y = 0;y < 32;y++)
246 for (x = 0;x < 32;x++)
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);
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);
262 particlefonttexture = R_LoadTexture (particletexturepool, "particlefont", 256, 256, particletexturedata, TEXTYPE_RGBA, TEXF_ALPHA | TEXF_PRECACHE);
265 static void r_part_start(void)
267 particletexturepool = R_AllocTexturePool();
268 R_InitParticleTexture ();
271 static void r_part_shutdown(void)
273 R_FreeTexturePool(&particletexturepool);
276 static void r_part_newmap(void)
280 void R_Particles_Init (void)
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);
287 int partindexarray[6] = {0, 1, 2, 0, 2, 3};
289 void R_DrawParticles (void)
293 float minparticledist, org[3], uprightangles[3], up2[3], right2[3], v[3], right[3], up[3], tv[4][5], fog, diff[3];
295 particletexture_t *tex, *texfog;
298 // LordHavoc: early out conditions
299 if ((!r_refdef.numparticles) || (!r_drawparticles.integer))
302 lighting = r_particles_lighting.integer;
303 if (!r_dynamic.integer)
306 c_particles += r_refdef.numparticles;
308 uprightangles[0] = 0;
309 uprightangles[1] = r_refdef.viewangles[1];
310 uprightangles[2] = 0;
311 AngleVectors (uprightangles, NULL, right2, up2);
313 minparticledist = DotProduct(r_origin, vpn) + 16.0f;
315 memset(&m, 0, sizeof(m));
316 m.transparent = true;
317 m.blendfunc1 = GL_SRC_ALPHA;
318 m.blendfunc2 = GL_ONE_MINUS_SRC_ALPHA;
320 m.index = partindexarray;
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]);
328 for (i = 0, r = r_refdef.particles;i < r_refdef.numparticles;i++, r++)
330 // LordHavoc: only render if not too close
331 if (DotProduct(r->org, vpn) < minparticledist)
334 // LordHavoc: check if it's in a visible leaf
335 leaf = Mod_PointInLeaf(r->org, cl.worldmodel);
336 if (leaf->visframe != r_framecount)
339 VectorCopy(r->org, org);
340 if (r->orientation == PARTICLE_BILLBOARD)
342 VectorScale(vright, r->scale, right);
343 VectorScale(vup, r->scale, up);
345 else if (r->orientation == PARTICLE_UPRIGHT_FACING)
347 VectorScale(right2, r->scale, right);
348 VectorScale(up2, r->scale, up);
350 else if (r->orientation == PARTICLE_ORIENTED_DOUBLESIDED)
353 if (DotProduct(r->dir, r_origin) > DotProduct(r->dir, org))
355 VectorNegate(r->dir, v);
356 VectorVectors(v, right, up);
359 VectorVectors(r->dir, right, up);
360 VectorScale(right, r->scale, right);
361 VectorScale(up, r->scale, up);
364 Host_Error("R_DrawParticles: unknown particle orientation %i\n", r->orientation);
370 if (lighting >= 1 && (r->dynlight || lighting >= 2))
372 R_CompleteLightPoint(v, org, true, leaf);
378 tex = &particletexture[r->tex][0];
379 texfog = &particletexture[r->tex][1];
384 VectorSubtract(org, r_origin, diff);
385 fog = exp(fogdensity/DotProduct(diff,diff));
393 if (tex->s1 == texfog->s1 && tex->t1 == texfog->t1)
395 m.cr += fogcolor[0] * fog;
396 m.cg += fogcolor[1] * fog;
397 m.cb += fogcolor[2] * fog;
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];
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];
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];
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];
427 if (fog && (tex->s1 != texfog->s1 || tex->t1 != texfog->t1))
429 m.blendfunc2 = GL_ONE;
433 m.ca = r->color[3] * fog;
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;
445 m.blendfunc2 = GL_ONE_MINUS_SRC_ALPHA;