***map loader generates portals for the map*** (can you tell this is a big deal? :)
[xonotic/darkplaces.git] / r_decals.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 #define MAX_DECALS 2048
24
25 typedef struct decal_s
26 {
27         vec3_t          org;
28         vec3_t          direction;
29         vec3_t          vert[4];
30         byte            color[4];
31         rtexture_t      *tex;
32         msurface_t      *surface;
33         byte            *lightmapaddress;
34         int                     lightmapstep;
35 }
36 decal_t;
37
38 decal_t *decals;
39 int currentdecal; // wraps around in decal array, replacing old ones when a new one is needed
40
41 cvar_t r_drawdecals = {"r_drawdecals", "1"};
42 cvar_t r_decals_lighting = {"r_decals_lighting", "1"};
43
44 void r_decals_start()
45 {
46         decals = (decal_t *) qmalloc(MAX_DECALS * sizeof(decal_t));
47         memset(decals, 0, MAX_DECALS * sizeof(decal_t));
48         currentdecal = 0;
49 }
50
51 void r_decals_shutdown()
52 {
53         qfree(decals);
54 }
55
56 void r_decals_newmap()
57 {
58         memset(decals, 0, MAX_DECALS * sizeof(decal_t));
59         currentdecal = 0;
60 }
61
62 void R_Decals_Init()
63 {
64         Cvar_RegisterVariable (&r_drawdecals);
65         Cvar_RegisterVariable (&r_decals_lighting);
66
67         R_RegisterModule("R_Decals", r_decals_start, r_decals_shutdown, r_decals_newmap);
68 }
69
70 // these are static globals only to avoid putting unnecessary things on the stack
71 static vec3_t decalorg;
72 static float decalbestdist;
73 static msurface_t *decalbestsurf;
74 static int decalbestlightmapofs;
75 void R_RecursiveDecalSurface (mnode_t *node)
76 {
77         // these are static because only one occurance of them need exist at once, so avoid putting them on the stack
78         static float ndist, dist;
79         static msurface_t *surf, *endsurf;
80         static vec3_t impact;
81         static int ds, dt;
82
83 loc0:
84         if (node->contents < 0)
85                 return;
86
87         ndist = PlaneDiff(decalorg, node->plane);
88         
89         if (ndist > 16)
90         {
91                 node = node->children[0];
92                 goto loc0;
93         }
94         if (ndist < -16)
95         {
96                 node = node->children[1];
97                 goto loc0;
98         }
99
100 // mark the polygons
101         surf = cl.worldmodel->surfaces + node->firstsurface;
102         endsurf = surf + node->numsurfaces;
103         for (;surf < endsurf;surf++)
104         {
105                 if (surf->flags & SURF_DRAWTILED)
106                         continue;       // no lightmaps
107
108                 dist = PlaneDiff(decalorg, surf->plane);
109                 if (surf->flags & SURF_PLANEBACK)
110                         dist = -dist;
111                 if (dist < 0)
112                         continue;
113                 if (dist >= decalbestdist)
114                         continue;
115
116                 impact[0] = decalorg[0] - surf->plane->normal[0] * dist;
117                 impact[1] = decalorg[1] - surf->plane->normal[1] * dist;
118                 impact[2] = decalorg[2] - surf->plane->normal[2] * dist;
119
120                 ds = (int) (DotProduct(impact, surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3]);
121                 dt = (int) (DotProduct(impact, surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3]);
122
123                 if (ds < surf->texturemins[0] || dt < surf->texturemins[1])
124                         continue;
125                 
126                 ds -= surf->texturemins[0];
127                 dt -= surf->texturemins[1];
128                 
129                 if (ds > surf->extents[0] || dt > surf->extents[1])
130                         continue;
131
132                 decalbestsurf = surf;
133                 decalbestdist = dist;
134                 decalbestlightmapofs = (dt >> 4) * ((surf->extents[0] >> 4) + 1) + (ds >> 4);
135         }
136
137         if (node->children[0]->contents >= 0)
138         {
139                 if (node->children[1]->contents >= 0)
140                 {
141                         R_RecursiveDecalSurface (node->children[0]);
142                         node = node->children[1];
143                         goto loc0;
144                 }
145                 else
146                 {
147                         node = node->children[0];
148                         goto loc0;
149                 }
150         }
151         else if (node->children[1]->contents >= 0)
152         {
153                 node = node->children[1];
154                 goto loc0;
155         }
156 }
157
158 void R_Decal(vec3_t org, rtexture_t *tex, float scale, int cred, int cgreen, int cblue, int alpha)
159 {
160         vec3_t center, right, up;
161         decal_t *decal;
162
163         if (alpha < 1)
164                 return;
165
166         // find the best surface to place the decal on
167         decalbestsurf = NULL;
168         decalbestdist = 16;
169         decalbestlightmapofs = 0;
170         VectorCopy(org, decalorg);
171
172         R_RecursiveDecalSurface (cl.worldmodel->nodes);
173
174         // abort if no suitable surface was found
175         if (decalbestsurf == NULL)
176                 return;
177
178         // grab a decal from the array and advance to the next decal to replace, wrapping to replace an old decal if necessary
179         decal = decals + currentdecal;
180         currentdecal++;
181         if (currentdecal >= MAX_DECALS)
182                 currentdecal = 0;
183         decal->tex = tex;
184         VectorCopy(decalbestsurf->plane->normal, decal->direction);
185         // reverse direction
186         if (decalbestsurf->flags & SURF_PLANEBACK)
187                 VectorNegate(decal->direction, decal->direction);
188         VectorNegate(decal->direction, decal->direction);
189         // 0.25 to push it off the surface a bit
190         decalbestdist -= 0.25f;
191         decal->org[0] = center[0] = org[0] + decal->direction[0] * decalbestdist;
192         decal->org[1] = center[1] = org[1] + decal->direction[1] * decalbestdist;
193         decal->org[2] = center[2] = org[2] + decal->direction[2] * decalbestdist;
194         // set up the 4 corners
195         scale *= 0.5f;
196         VectorVectors(decal->direction, right, up);
197         decal->vert[0][0] = center[0] - right[0] * scale - up[0] * scale; // texcoords 0 1
198         decal->vert[0][1] = center[1] - right[1] * scale - up[1] * scale;
199         decal->vert[0][2] = center[2] - right[2] * scale - up[2] * scale;
200         decal->vert[1][0] = center[0] - right[0] * scale + up[0] * scale; // texcoords 0 0
201         decal->vert[1][1] = center[1] - right[1] * scale + up[1] * scale;
202         decal->vert[1][2] = center[2] - right[2] * scale + up[2] * scale;
203         decal->vert[2][0] = center[0] + right[0] * scale + up[0] * scale; // texcoords 1 0
204         decal->vert[2][1] = center[1] + right[1] * scale + up[1] * scale;
205         decal->vert[2][2] = center[2] + right[2] * scale + up[2] * scale;
206         decal->vert[3][0] = center[0] + right[0] * scale - up[0] * scale; // texcoords 1 1
207         decal->vert[3][1] = center[1] + right[1] * scale - up[1] * scale;
208         decal->vert[3][2] = center[2] + right[2] * scale - up[2] * scale;
209         // store the color
210         decal->color[0] = (byte) bound(0, cred, 255);
211         decal->color[1] = (byte) bound(0, cgreen, 255);
212         decal->color[2] = (byte) bound(0, cblue, 255);
213         decal->color[3] = (byte) bound(0, alpha, 255);
214         // store the surface information for lighting
215         decal->surface = decalbestsurf;
216         decal->lightmapstep = ((decalbestsurf->extents[0]>>4)+1) * ((decalbestsurf->extents[1]>>4)+1)*3; // LordHavoc: *3 for colored lighting
217         if (decalbestsurf->samples)
218                 decal->lightmapaddress = decalbestsurf->samples + decalbestlightmapofs * 3; // LordHavoc: *3 for colored lighitng
219         else
220                 decal->lightmapaddress = NULL;
221 }
222
223 void GL_DrawDecals (void)
224 {
225         decal_t *p;
226         int i, j, k, dynamiclight, bits, texnum;
227         float scale, fr, fg, fb, dist, rad, mindist;
228         byte *lightmap;
229         vec3_t v;
230         msurface_t *surf;
231         dlight_t *dl;
232
233         if (!r_drawdecals.value)
234                 return;
235
236         dynamiclight = (int) r_dynamic.value != 0 && (int) r_decals_lighting.value != 0;
237
238         mindist = DotProduct(r_refdef.vieworg, vpn) + 4.0f;
239
240         if (r_render.value)
241         {
242                 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
243                 glEnable(GL_BLEND);
244                 glShadeModel(GL_FLAT);
245                 glDepthMask(0); // disable zbuffer updates
246                 glDisable(GL_ALPHA_TEST);
247                 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
248         }
249         texnum = -1;
250
251         for (i = 0, p = decals;i < MAX_DECALS;i++, p++)
252         {
253                 if (p->tex == NULL)
254                         break;
255                 // skip decals on surfaces that aren't visible in this frame
256                 if (p->surface->visframe != r_framecount)
257                         continue;
258
259                 // do not render if the decal is behind the view
260                 if (DotProduct(p->org, vpn) < mindist)
261                         continue;
262
263                 // do not render if the view origin is behind the decal
264                 VectorSubtract(p->org, r_refdef.vieworg, v);
265                 if (DotProduct(p->direction, v) < 0)
266                         continue;
267
268                 // get the surface lighting
269                 surf = p->surface;
270                 lightmap = p->lightmapaddress;
271                 fr = fg = fb = 0.0f;
272                 if (lightmap)
273                 {
274                         if (surf->styles[0] != 255)
275                         {
276                                 scale = d_lightstylevalue[surf->styles[0]] * (1.0f / 256.0f);
277                                 fr += lightmap[0] * scale;
278                                 fg += lightmap[1] * scale;
279                                 fb += lightmap[2] * scale;
280                                 if (surf->styles[1] != 255)
281                                 {
282                                         lightmap += p->lightmapstep;
283                                         scale = d_lightstylevalue[surf->styles[1]] * (1.0f / 256.0f);
284                                         fr += lightmap[0] * scale;
285                                         fg += lightmap[1] * scale;
286                                         fb += lightmap[2] * scale;
287                                         if (surf->styles[2] != 255)
288                                         {
289                                                 lightmap += p->lightmapstep;
290                                                 scale = d_lightstylevalue[surf->styles[2]] * (1.0f / 256.0f);
291                                                 fr += lightmap[0] * scale;
292                                                 fg += lightmap[1] * scale;
293                                                 fb += lightmap[2] * scale;
294                                                 if (surf->styles[3] != 255)
295                                                 {
296                                                         lightmap += p->lightmapstep;
297                                                         scale = d_lightstylevalue[surf->styles[3]] * (1.0f / 256.0f);
298                                                         fr += lightmap[0] * scale;
299                                                         fg += lightmap[1] * scale;
300                                                         fb += lightmap[2] * scale;
301                                                 }
302                                         }
303                                 }
304                         }
305                         /*
306                         for (j = 0;j < MAXLIGHTMAPS && surf->styles[j] != 255;j++)
307                         {
308                                 scale = d_lightstylevalue[surf->styles[j]] * (1.0f / 256.0f);
309                                 fr += lightmap[0] * scale;
310                                 fg += lightmap[1] * scale;
311                                 fb += lightmap[2] * scale;
312                                 lightmap += p->lightmapstep;
313                         }
314                         */
315                 }
316                 // dynamic lighting
317                 if (dynamiclight)
318                 {
319                         if (surf->dlightframe == r_framecount)
320                         {
321                                 for (j = 0;j < 8;j++)
322                                 {
323                                         bits = surf->dlightbits[j];
324                                         if (bits)
325                                         {
326                                                 for (k = 0, dl = cl_dlights + j * 32;bits;k++, dl++)
327                                                 {
328                                                         if (bits & (1 << k))
329                                                         {
330                                                                 bits -= 1 << k;
331                                                                 VectorSubtract(p->org, dl->origin, v);
332                                                                 dist = DotProduct(v, v) + LIGHTOFFSET;
333                                                                 rad = dl->radius * dl->radius;
334                                                                 if (dist < rad)
335                                                                 {
336                                                                         rad *= 128.0f / dist;
337                                                                         fr += rad * dl->color[0];
338                                                                         fg += rad * dl->color[1];
339                                                                         fb += rad * dl->color[2];
340                                                                 }
341                                                         }
342                                                 }
343                                         }
344                                 }
345                         }
346                 }
347                 /*
348                 {
349                         int ir, ig, ib;
350                         byte br, bg, bb, ba;
351                         // apply color to lighting
352                         ir = (int) (fr * p->color[0] * (1.0f / 128.0f));
353                         ig = (int) (fg * p->color[1] * (1.0f / 128.0f));
354                         ib = (int) (fb * p->color[2] * (1.0f / 128.0f));
355                         // compute byte color
356                         br = (byte) min(ir, 255);
357                         bg = (byte) min(ig, 255);
358                         bb = (byte) min(ib, 255);
359                         ba = p->color[3];
360                         // put into transpoly system for sorted drawing later
361                         transpolybegin(R_GetTexture(p->tex), 0, R_GetTexture(p->tex), TPOLYTYPE_ALPHA);
362                         transpolyvertub(p->vert[0][0], p->vert[0][1], p->vert[0][2], 0,1,br,bg,bb,ba);
363                         transpolyvertub(p->vert[1][0], p->vert[1][1], p->vert[1][2], 0,0,br,bg,bb,ba);
364                         transpolyvertub(p->vert[2][0], p->vert[2][1], p->vert[2][2], 1,0,br,bg,bb,ba);
365                         transpolyvertub(p->vert[3][0], p->vert[3][1], p->vert[3][2], 1,1,br,bg,bb,ba);
366                         transpolyend();
367                 }
368                 */
369                 if (r_render.value)
370                 {
371                         j = R_GetTexture(p->tex);
372                         if (texnum != j)
373                         {
374                                 glEnd();
375                                 texnum = j;
376                                 glBindTexture(GL_TEXTURE_2D, texnum);
377                                 glBegin(GL_QUADS);
378                         }
379                         if (lighthalf)
380                                 glColor4f(fr * p->color[0] * (1.0f / 255.0f / 256.0f), fg * p->color[1] * (1.0f / 255.0f / 256.0f), fb * p->color[2] * (1.0f / 255.0f / 256.0f), p->color[3] * (1.0f / 255.0f));
381                         else
382                                 glColor4f(fr * p->color[0] * (1.0f / 255.0f / 128.0f), fg * p->color[1] * (1.0f / 255.0f / 128.0f), fb * p->color[2] * (1.0f / 255.0f / 128.0f), p->color[3] * (1.0f / 255.0f));
383                         glTexCoord2f(0, 1);
384                         glVertex3fv(p->vert[0]);
385                         glTexCoord2f(0, 0);
386                         glVertex3fv(p->vert[1]);
387                         glTexCoord2f(1, 0);
388                         glVertex3fv(p->vert[2]);
389                         glTexCoord2f(1, 1);
390                         glVertex3fv(p->vert[3]);
391                 }
392         }
393         
394         if (r_render.value)
395         {
396                 glEnd();
397
398                 glDepthMask(1); // enable zbuffer updates
399                 glDisable(GL_ALPHA_TEST);
400         }
401 }