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 static rtexture_t *particlefonttexture;
26 // [0] is normal, [1] is fog, they may be the same
27 static particletexture_t particletexture[MAX_PARTICLETEXTURES][2];
29 static cvar_t r_drawparticles = {0, "r_drawparticles", "1"};
30 static cvar_t r_particles_lighting = {0, "r_particles_lighting", "1"};
32 static qbyte shadebubble(float dx, float dy, vec3_t light)
36 dz = 1 - (dx*dx+dy*dy);
37 if (dz > 0) // it does hit the sphere
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
46 else if (dot < -0.5) // exterior reflection
47 f += ((dot * -2) - 1);
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
54 else if (dot < -0.5) // exterior reflection
55 f += ((dot * -2) - 1);
57 f += 16; // just to give it a haze so you can see the outline
65 static void setuptex(int cltexnum, int fog, int rtexnum, qbyte *data, qbyte *particletexturedata)
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);
78 static void R_InitParticleTexture (void)
81 float dx, dy, radius, f, f2;
82 qbyte data[32][32][4], noise1[64][64], noise2[64][64];
84 qbyte particletexturedata[256*256*4];
86 memset(particletexturedata, 255, sizeof(particletexturedata));
88 // 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 + 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] = (qbyte) 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);
126 for (i = 0;i < 16;i++)
128 radius = i * 3.0f / 16.0f;
129 f2 = 255.0f * ((15.0f - i) / 15.0f);
130 for (y = 0;y < 32;y++)
132 dy = (y - 16) * 0.25f;
133 for (x = 0;x < 32;x++)
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;
142 setuptex(i + 8, 0, i + 16, &data[0][0][0], particletexturedata);
143 setuptex(i + 8, 1, i + 16, &data[0][0][0], particletexturedata);
147 for (y = 0;y < 32;y++)
150 for (x = 0;x < 32;x++)
152 data[y][x][0] = data[y][x][1] = data[y][x][2] = 255;
154 d = (256 - (dx*dx+dy*dy));
155 d = bound(0, d, 255);
156 data[y][x][3] = (qbyte) d;
159 setuptex(24, 0, 32, &data[0][0][0], particletexturedata);
160 setuptex(24, 1, 32, &data[0][0][0], particletexturedata);
163 light[0] = 1;light[1] = 1;light[2] = 1;
164 VectorNormalize(light);
165 for (y = 0;y < 32;y++)
167 for (x = 0;x < 32;x++)
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);
173 setuptex(25, 0, 33, &data[0][0][0], particletexturedata);
174 setuptex(25, 1, 33, &data[0][0][0], particletexturedata);
177 light[0] = 1;light[1] = 1;light[2] = 1;
178 VectorNormalize(light);
179 for (y = 0;y < 32;y++)
181 for (x = 0;x < 32;x++)
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);
187 setuptex(26, 0, 34, &data[0][0][0], particletexturedata);
188 setuptex(26, 1, 34, &data[0][0][0], particletexturedata);
191 for (y = 0;y < 32;y++)
194 for (x = 0;x < 32;x++)
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);
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);
210 particlefonttexture = R_LoadTexture (particletexturepool, "particlefont", 256, 256, particletexturedata, TEXTYPE_RGBA, TEXF_ALPHA | TEXF_PRECACHE);
213 static void r_part_start(void)
215 particletexturepool = R_AllocTexturePool();
216 R_InitParticleTexture ();
219 static void r_part_shutdown(void)
221 R_FreeTexturePool(&particletexturepool);
224 static void r_part_newmap(void)
228 void R_Particles_Init (void)
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);
235 int partindexarray[6] = {0, 1, 2, 0, 2, 3};
237 void R_DrawParticles (void)
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];
243 particletexture_t *tex, *texfog;
246 // LordHavoc: early out conditions
247 if ((!r_refdef.numparticles) || (!r_drawparticles.integer))
250 lighting = r_particles_lighting.integer;
251 if (!r_dynamic.integer)
254 c_particles += r_refdef.numparticles;
256 uprightangles[0] = 0;
257 uprightangles[1] = r_refdef.viewangles[1];
258 uprightangles[2] = 0;
259 AngleVectors (uprightangles, NULL, right2, up2);
261 minparticledist = DotProduct(r_origin, vpn) + 16.0f;
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;
270 m.index = partindexarray;
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]);
278 for (i = 0, r = r_refdef.particles;i < r_refdef.numparticles;i++, r++)
280 // LordHavoc: only render if not too close
281 if (DotProduct(r->org, vpn) < minparticledist)
284 // LordHavoc: check if it's in a visible leaf
285 leaf = Mod_PointInLeaf(r->org, cl.worldmodel);
286 if (leaf->visframe != r_framecount)
289 VectorCopy(r->org, org);
290 if (r->orientation == PARTICLE_BILLBOARD)
292 VectorScale(vright, r->scalex, right);
293 VectorScale(vup, r->scaley, up);
295 else if (r->orientation == PARTICLE_UPRIGHT_FACING)
297 VectorScale(right2, r->scalex, right);
298 VectorScale(up2, r->scaley, up);
300 else if (r->orientation == PARTICLE_ORIENTED_DOUBLESIDED)
303 if (DotProduct(r->dir, r_origin) > DotProduct(r->dir, org))
305 VectorNegate(r->dir, v);
306 VectorVectors(v, right, up);
309 VectorVectors(r->dir, right, up);
310 VectorScale(right, r->scalex, right);
311 VectorScale(up, r->scaley, up);
314 Host_Error("R_DrawParticles: unknown particle orientation %i\n", r->orientation);
320 if (lighting >= 1 && (r->dynlight || lighting >= 2))
322 R_CompleteLightPoint(v, org, true, leaf);
328 tex = &particletexture[r->tex][0];
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;
353 m.blendfunc2 = GL_ONE;
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)))
362 if (ifog >= (1.0f / 64.0f))
364 // partially fogged, darken it
379 m.blendfunc2 = GL_ONE_MINUS_SRC_ALPHA;
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))
388 if (fog >= (1.0f - (1.0f / 64.0f)))
390 // fully fogged, just use the fog texture and render as alpha
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;
407 // partially fogged, darken the first pass
412 if (tex->s1 == texfog->s1 && tex->t1 == texfog->t1)
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;
422 // render the first pass (alpha), then do additive fog
425 m.blendfunc2 = GL_ONE;
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;