rewrote memory system entirely (hunk, cache, and zone are gone, memory pools replaced...
[xonotic/darkplaces.git] / cl_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         entity_render_t *ent;
28         int tex;
29         model_t *model;
30         int surface;
31         float scale;
32         vec3_t org;
33         vec3_t dir;
34         float color[4];
35 }
36 decal_t;
37
38 static decal_t *cl_decals;
39 static int cl_currentdecal; // wraps around in decal array, replacing old ones when a new one is needed
40
41 static renderdecal_t *cl_renderdecals;
42
43 static mempool_t *cl_decal_mempool;
44
45 void CL_Decals_Clear(void)
46 {
47         memset(cl_decals, 0, MAX_DECALS * sizeof(decal_t));
48         cl_currentdecal = 0;
49 }
50
51 void CL_Decals_Init(void)
52 {
53         cl_decal_mempool = Mem_AllocPool("CL_Decals");
54         cl_decals = (decal_t *) Mem_Alloc(cl_decal_mempool, MAX_DECALS * sizeof(decal_t));
55         memset(cl_decals, 0, MAX_DECALS * sizeof(decal_t));
56         cl_currentdecal = 0;
57
58         // FIXME: r_refdef stuff should be allocated somewhere else?
59         r_refdef.decals = cl_renderdecals = Mem_Alloc(cl_decal_mempool, MAX_DECALS * sizeof(renderdecal_t));
60 }
61
62
63 // these are static globals only to avoid putting unnecessary things on the stack
64 static vec3_t decalorg, decalbestorg;
65 static float decalbestdist;
66 static msurface_t *decalbestsurf;
67 static entity_render_t *decalbestent, *decalent;
68 static model_t *decalmodel;
69 void CL_RecursiveDecalSurface (mnode_t *node)
70 {
71         // these are static because only one occurance of them need exist at once, so avoid putting them on the stack
72         static float ndist, dist;
73         static msurface_t *surf, *endsurf;
74         static vec3_t impact;
75         static int ds, dt;
76
77 loc0:
78         if (node->contents < 0)
79                 return;
80
81         ndist = PlaneDiff(decalorg, node->plane);
82
83         if (ndist > 16)
84         {
85                 node = node->children[0];
86                 goto loc0;
87         }
88         if (ndist < -16)
89         {
90                 node = node->children[1];
91                 goto loc0;
92         }
93
94 // mark the polygons
95         surf = decalmodel->surfaces + node->firstsurface;
96         endsurf = surf + node->numsurfaces;
97         for (;surf < endsurf;surf++)
98         {
99                 if (!(surf->flags & SURF_LIGHTMAP))
100                         continue;
101
102                 dist = PlaneDiff(decalorg, surf->plane);
103                 if (surf->flags & SURF_PLANEBACK)
104                         dist = -dist;
105                 if (dist < -1)
106                         continue;
107                 if (dist >= decalbestdist)
108                         continue;
109
110                 impact[0] = decalorg[0] - surf->plane->normal[0] * dist;
111                 impact[1] = decalorg[1] - surf->plane->normal[1] * dist;
112                 impact[2] = decalorg[2] - surf->plane->normal[2] * dist;
113
114                 ds = (int) (DotProduct(impact, surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3]) - surf->texturemins[0];
115                 dt = (int) (DotProduct(impact, surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3]) - surf->texturemins[1];
116
117                 if (ds < 0 || dt < 0 || ds > surf->extents[0] || dt > surf->extents[1])
118                         continue;
119
120                 VectorCopy(decalorg, decalbestorg);
121                 decalbestent = decalent;
122                 decalbestsurf = surf;
123                 decalbestdist = dist;
124         }
125
126         if (node->children[0]->contents >= 0)
127         {
128                 if (node->children[1]->contents >= 0)
129                 {
130                         CL_RecursiveDecalSurface (node->children[0]);
131                         node = node->children[1];
132                         goto loc0;
133                 }
134                 else
135                 {
136                         node = node->children[0];
137                         goto loc0;
138                 }
139         }
140         else if (node->children[1]->contents >= 0)
141         {
142                 node = node->children[1];
143                 goto loc0;
144         }
145 }
146
147 void CL_Decal(vec3_t origin, int tex, float scale, float red, float green, float blue, float alpha)
148 {
149         int i;
150         decal_t *decal;
151
152         if (alpha < (1.0f / 255.0f))
153                 return;
154
155         // find the best surface to place the decal on
156         decalbestent = NULL;
157         decalbestsurf = NULL;
158         decalbestdist = 16;
159
160         decalent = NULL;
161         decalmodel = cl.worldmodel;
162         Mod_CheckLoaded(decalmodel);
163         VectorCopy(origin, decalorg);
164         CL_RecursiveDecalSurface (decalmodel->nodes);
165
166         for (i = 1;i < MAX_EDICTS;i++)
167         {
168                 decalent = &cl_entities[i].render;
169                 decalmodel = decalent->model;
170                 if (decalmodel && decalmodel->name[0])
171                 {
172                         Mod_CheckLoaded(decalmodel);
173                         if (decalmodel->type == mod_brush)
174                         {
175                                 softwaretransformforentity(decalent);
176                                 softwareuntransform(origin, decalorg);
177                                 CL_RecursiveDecalSurface (decalmodel->nodes);
178                         }
179                 }
180         }
181
182         // abort if no suitable surface was found
183         if (decalbestsurf == NULL)
184                 return;
185
186         // grab a decal from the array and advance to the next decal to replace, wrapping to replace an old decal if necessary
187         decal = &cl_decals[cl_currentdecal++];
188         if (cl_currentdecal >= MAX_DECALS)
189                 cl_currentdecal = 0;
190         memset(decal, 0, sizeof(*decal));
191
192         decal->ent = decalbestent;
193         if (decal->ent)
194                 decal->model = decal->ent->model;
195         else
196                 decal->model = cl.worldmodel;
197
198         decal->tex = tex + 1; // our texture numbers are +1 to make 0 mean invisible
199         VectorNegate(decalbestsurf->plane->normal, decal->dir);
200         if (decalbestsurf->flags & SURF_PLANEBACK)
201                 VectorNegate(decal->dir, decal->dir);
202         // 0.25 to push it off the surface a bit
203         decalbestdist -= 0.25f;
204         decal->org[0] = decalbestorg[0] + decal->dir[0] * decalbestdist;
205         decal->org[1] = decalbestorg[1] + decal->dir[1] * decalbestdist;
206         decal->org[2] = decalbestorg[2] + decal->dir[2] * decalbestdist;
207         decal->scale = scale * 0.5f;
208         // store the color
209         decal->color[0] = red;
210         decal->color[1] = green;
211         decal->color[2] = blue;
212         decal->color[3] = alpha;
213         // store the surface information
214         decal->surface = decalbestsurf - decal->model->surfaces;
215 }
216
217 void CL_UpdateDecals (void)
218 {
219         int i;
220         decal_t *p;
221         renderdecal_t *r;
222
223         for (i = 0, p = cl_decals, r = r_refdef.decals;i < MAX_DECALS;i++, p++)
224         {
225                 if (p->tex == 0)
226                         continue;
227
228                 if (p->ent && p->ent->visframe == r_framecount && p->ent->model != p->model)
229                 {
230                         p->tex = 0;
231                         continue;
232                 }
233
234                 r->ent = p->ent;
235                 r->tex = p->tex - 1; // our texture numbers are +1 to make 0 mean invisible
236                 r->surface = p->surface;
237                 r->scale = p->scale;
238                 VectorCopy(p->org, r->org);
239                 VectorCopy(p->dir, r->dir);
240                 VectorCopy4(p->color, r->color);
241                 r++;
242         }
243         r_refdef.numdecals = r - r_refdef.decals;
244 }
245