cea29fdbb6bd12f8236aca654c3799c4e0c08e41
[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 void R_Decal(vec3_t org, rtexture_t *tex, float scale, int cred, int cgreen, int cblue, int alpha)
71 {
72         int i, ds, dt, bestlightmapofs;
73         float bestdist, dist;
74         vec3_t impact, right, up;
75         decal_t *decal;
76 //      mleaf_t *leaf;
77         msurface_t *surf/*, **mark, **endmark*/, *bestsurf;
78
79         if (alpha < 1)
80                 return;
81
82 //      leaf = Mod_PointInLeaf(org, cl.worldmodel);
83 //      if (!leaf->nummarksurfaces)
84 //              return;
85
86 //      mark = leaf->firstmarksurface;
87 //      endmark = mark + leaf->nummarksurfaces;
88
89         // find the best surface to place the decal on
90         bestsurf = NULL;
91         bestdist = 16;
92         bestlightmapofs = 0;
93 //      while(mark < endmark)
94 //      {
95 //              surf = *mark++;
96         surf = &cl.worldmodel->surfaces[cl.worldmodel->firstmodelsurface];
97         for (i = 0;i < cl.worldmodel->nummodelsurfaces;i++, surf++)
98         {
99                 if (surf->flags & SURF_DRAWTILED)
100                         continue;       // no lightmaps
101
102                 dist = PlaneDiff(org, surf->plane);
103                 if (surf->flags & SURF_PLANEBACK)
104                         dist = -dist;
105                 if (dist < 0)
106                         continue;
107                 if (dist >= bestdist)
108                         continue;
109
110                 impact[0] = org[0] - surf->plane->normal[0] * dist;
111                 impact[1] = org[1] - surf->plane->normal[1] * dist;
112                 impact[2] = org[2] - surf->plane->normal[2] * dist;
113
114                 ds = (int) (DotProduct(impact, surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3]);
115                 dt = (int) (DotProduct(impact, surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3]);
116
117                 if (ds < surf->texturemins[0] || dt < surf->texturemins[1])
118                         continue;
119                 
120                 ds -= surf->texturemins[0];
121                 dt -= surf->texturemins[1];
122                 
123                 if (ds > surf->extents[0] || dt > surf->extents[1])
124                         continue;
125
126                 bestsurf = surf;
127                 bestdist = dist;
128                 bestlightmapofs = (dt >> 4) * ((surf->extents[0] >> 4) + 1) + (ds >> 4);
129         }
130         // abort if no suitable surface was found
131         if (bestsurf == NULL)
132                 return;
133
134         // grab a decal from the array and advance to the next decal to replace, wrapping to replace an old decal if necessary
135         decal = decals + currentdecal;
136         currentdecal++;
137         if (currentdecal >= MAX_DECALS)
138                 currentdecal = 0;
139         decal->tex = tex;
140         // reverse direction
141         if (bestsurf->flags & SURF_PLANEBACK)
142         {
143                 VectorCopy(bestsurf->plane->normal, decal->direction);
144         }
145         else
146         {
147                 VectorNegate(bestsurf->plane->normal, decal->direction);
148         }
149         // - 0.25 to push it off the surface a bit
150         decal->org[0] = impact[0] = org[0] + decal->direction[0] * (bestdist - 0.25f);
151         decal->org[1] = impact[1] = org[1] + decal->direction[1] * (bestdist - 0.25f);
152         decal->org[2] = impact[2] = org[2] + decal->direction[2] * (bestdist - 0.25f);
153         // set up the 4 corners
154         scale *= 0.5f;
155         VectorVectors(decal->direction, right, up);
156         decal->vert[0][0] = impact[0] - up[0] * scale - right[0] * scale;
157         decal->vert[0][1] = impact[1] - up[1] * scale - right[1] * scale;
158         decal->vert[0][2] = impact[2] - up[2] * scale - right[2] * scale;
159         decal->vert[1][0] = impact[0] + up[0] * scale - right[0] * scale;
160         decal->vert[1][1] = impact[1] + up[1] * scale - right[1] * scale;
161         decal->vert[1][2] = impact[2] + up[2] * scale - right[2] * scale;
162         decal->vert[2][0] = impact[0] + up[0] * scale + right[0] * scale;
163         decal->vert[2][1] = impact[1] + up[1] * scale + right[1] * scale;
164         decal->vert[2][2] = impact[2] + up[2] * scale + right[2] * scale;
165         decal->vert[3][0] = impact[0] - up[0] * scale + right[0] * scale;
166         decal->vert[3][1] = impact[1] - up[1] * scale + right[1] * scale;
167         decal->vert[3][2] = impact[2] - up[2] * scale + right[2] * scale;
168         // store the color
169         decal->color[0] = (byte) bound(0, cred, 255);
170         decal->color[1] = (byte) bound(0, cgreen, 255);
171         decal->color[2] = (byte) bound(0, cblue, 255);
172         decal->color[3] = (byte) bound(0, alpha, 255);
173         // store the surface information for lighting
174         decal->surface = bestsurf;
175         decal->lightmapstep = ((bestsurf->extents[0]>>4)+1) * ((bestsurf->extents[1]>>4)+1)*3; // LordHavoc: *3 for colored lighting
176         if (bestsurf->samples)
177                 decal->lightmapaddress = bestsurf->samples + bestlightmapofs * 3; // LordHavoc: *3 for colored lighitng
178         else
179                 decal->lightmapaddress = NULL;
180 }
181
182 void R_DrawDecals (void)
183 {
184         decal_t *p;
185         int i, j, k, dynamiclight, ir, ig, ib, maps, bits;
186         byte br, bg, bb, ba;
187         float scale, fr, fg, fb, dist, rad, mindist;
188         byte *lightmap;
189         vec3_t v;
190         msurface_t *surf;
191         dlight_t *dl;
192
193         if (!r_drawdecals.value)
194                 return;
195
196         dynamiclight = (int) r_dynamic.value != 0 && (int) r_decals_lighting.value != 0;
197
198         mindist = DotProduct(r_refdef.vieworg, vpn) + 4.0f;
199
200         for (i = 0, p = decals;i < MAX_DECALS;i++, p++)
201         {
202                 if (p->tex == NULL)
203                         break;
204
205                 // do not render if the decal is behind the view
206                 if (DotProduct(p->org, vpn) < mindist)
207                         continue;
208
209                 // do not render if the view origin is behind the decal
210                 VectorSubtract(p->org, r_refdef.vieworg, v);
211                 if (DotProduct(p->direction, v) < 0)
212                         continue;
213
214                 // get the surface lighting
215                 surf = p->surface;
216                 lightmap = p->lightmapaddress;
217                 fr = fg = fb = 0.0f;
218                 if (lightmap)
219                 {
220                         for (maps = 0;maps < MAXLIGHTMAPS && surf->styles[maps] != 255;maps++)
221                         {
222                                 scale = d_lightstylevalue[surf->styles[maps]];
223                                 fr += lightmap[0] * scale;
224                                 fg += lightmap[1] * scale;
225                                 fb += lightmap[2] * scale;
226                                 lightmap += p->lightmapstep;
227                         }
228                 }
229                 fr *= (1.0f / 256.0f);
230                 fg *= (1.0f / 256.0f);
231                 fb *= (1.0f / 256.0f);
232                 // dynamic lighting
233                 if (dynamiclight)
234                 {
235                         if (surf->dlightframe == r_dlightframecount)
236                         {
237                                 for (j = 0;j < 8;j++)
238                                 {
239                                         bits = surf->dlightbits[j];
240                                         if (bits)
241                                         {
242                                                 for (k = 0, dl = cl_dlights + j * 32;bits;k++, dl++)
243                                                 {
244                                                         if (bits & (1 << k))
245                                                         {
246                                                                 bits -= 1 << k;
247                                                                 VectorSubtract(p->org, dl->origin, v);
248                                                                 dist = DotProduct(v, v) + LIGHTOFFSET;
249                                                                 rad = dl->radius * dl->radius;
250                                                                 if (dist < rad)
251                                                                 {
252                                                                         rad *= 128.0f / dist;
253                                                                         fr += rad * dl->color[0];
254                                                                         fg += rad * dl->color[1];
255                                                                         fb += rad * dl->color[2];
256                                                                 }
257                                                         }
258                                                 }
259                                         }
260                                 }
261                         }
262                 }
263                 // apply color to lighting
264                 ir = (int) (fr * p->color[0] * (1.0f / 128.0f));
265                 ig = (int) (fg * p->color[1] * (1.0f / 128.0f));
266                 ib = (int) (fb * p->color[2] * (1.0f / 128.0f));
267                 // compute byte color
268                 br = (byte) min(ir, 255);
269                 bg = (byte) min(ig, 255);
270                 bb = (byte) min(ib, 255);
271                 ba = p->color[3];
272                 // put into transpoly system for sorted drawing later
273                 transpolybegin(R_GetTexture(p->tex), 0, R_GetTexture(p->tex), TPOLYTYPE_ALPHA);
274                 transpolyvertub(p->vert[0][0], p->vert[0][1], p->vert[0][2], 0,1,br,bg,bb,ba);
275                 transpolyvertub(p->vert[1][0], p->vert[1][1], p->vert[1][2], 0,0,br,bg,bb,ba);
276                 transpolyvertub(p->vert[2][0], p->vert[2][1], p->vert[2][2], 1,0,br,bg,bb,ba);
277                 transpolyvertub(p->vert[3][0], p->vert[3][1], p->vert[3][2], 1,1,br,bg,bb,ba);
278                 transpolyend();
279         }
280 }