1 /*
2 Copyright (C) 1997-2001 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 // cg_decals.c
22 //
23
24 #include "cg_local.h"
25
26 static cgDecal_t *cg_freeDecals;
27 static cgDecal_t cg_decalHeadNode, cg_decalList[MAX_REF_DECALS]; // FIXME: 1.2MB array!
28 static int cg_numDecals;
29
30 /*
31 =============================================================================
32
33 DECAL IMAGING
34
35 =============================================================================
36 */
37
dRandBloodMark(void)38 int dRandBloodMark (void) { return DT_BLOOD01 + (rand()&15); }
dRandGrnBloodMark(void)39 int dRandGrnBloodMark (void){ return DT_BLOOD01_GRN + (rand()&15); }
dRandExploMark(void)40 int dRandExploMark (void) { return DT_EXPLOMARK + (rand()%3); }
dRandSlashMark(void)41 int dRandSlashMark (void) { return DT_SLASH + (rand()%3); }
42
43 /*
44 =============================================================================
45
46 DECAL MANAGEMENT
47
48 =============================================================================
49 */
50
51 /*
52 ===============
53 CG_AllocDecal
54 ===============
55 */
CG_AllocDecal(void)56 static cgDecal_t *CG_AllocDecal (void)
57 {
58 cgDecal_t *d;
59
60 // Take a free decal spot if possible, otherwise steal the oldest one
61 if (cg_freeDecals && cg_numDecals+1 < cg_decalMax->intVal) {
62 d = cg_freeDecals;
63 cg_freeDecals = d->next;
64 }
65 else {
66 d = cg_decalHeadNode.prev;
67 d->prev->next = d->next;
68 d->next->prev = d->prev;
69
70 cgi.R_FreeDecal (&d->refDecal);
71 cg_numDecals--;
72 }
73
74 // Move to the beginning of the list
75 d->prev = &cg_decalHeadNode;
76 d->next = cg_decalHeadNode.next;
77 d->next->prev = d;
78 d->prev->next = d;
79
80 cg_numDecals++;
81 return d;
82 }
83
84
85 /*
86 ===============
87 CG_FreeDecal
88 ===============
89 */
CG_FreeDecal(cgDecal_t * d)90 static inline void CG_FreeDecal (cgDecal_t *d)
91 {
92 // Remove from linked active list
93 d->prev->next = d->next;
94 d->next->prev = d->prev;
95
96 // Insert into linked free list
97 d->next = cg_freeDecals;
98 cg_freeDecals = d;
99
100 // Free in renderer
101 cgi.R_FreeDecal (&d->refDecal);
102 cg_numDecals--;
103 }
104
105
106 /*
107 ===============
108 CG_SpawnDecal
109 ===============
110 */
CG_SpawnDecal(float org0,float org1,float org2,float dir0,float dir1,float dir2,float red,float green,float blue,float redVel,float greenVel,float blueVel,float alpha,float alphaVel,float size,int type,uint32 flags,void (* think)(struct cgDecal_s * d,vec4_t color,int * type,uint32 * flags),qBool thinkNext,float lifeTime,float angle)111 cgDecal_t *CG_SpawnDecal (float org0, float org1, float org2,
112 float dir0, float dir1, float dir2,
113 float red, float green, float blue,
114 float redVel, float greenVel, float blueVel,
115 float alpha, float alphaVel,
116 float size,
117 int type, uint32 flags,
118 void (*think)(struct cgDecal_s *d, vec4_t color, int *type, uint32 *flags),
119 qBool thinkNext,
120 float lifeTime, float angle)
121 {
122 vec3_t origin, dir;
123 cgDecal_t *d;
124
125 // Decal toggling
126 if (!cg_decals->intVal)
127 return NULL;
128 if (flags & DF_USE_BURNLIFE) {
129 if (!cg_decalBurnLife->floatVal)
130 return NULL;
131 }
132 else if (!cg_decalLife->floatVal)
133 return NULL;
134
135 // Copy values
136 Vec3Set (dir, dir0, dir1, dir2);
137 Vec3Set (origin, org0, org1, org2);
138
139 // Create the decal
140 d = CG_AllocDecal ();
141 if (!cgi.R_CreateDecal (&d->refDecal, origin, dir, angle, size)) {
142 CG_FreeDecal (d);
143 return NULL;
144 }
145
146 // Store values
147 d->time = (float)cg.realTime;
148 d->lifeTime = lifeTime;
149
150 Vec4Set (d->color, red, green, blue, alpha);
151 Vec4Set (d->colorVel, redVel, greenVel, blueVel, alphaVel);
152
153 d->size = size;
154
155 d->shader = cgMedia.decalTable[type%DT_PICTOTAL];
156 d->flags = flags;
157
158 d->think = think;
159 d->thinkNext = thinkNext;
160 return d;
161 }
162
163
164 /*
165 ===============
166 CG_ClearDecals
167 ===============
168 */
CG_ClearDecals(void)169 void CG_ClearDecals (void)
170 {
171 int i;
172
173 cg_numDecals = 0;
174
175 // Link decals
176 cg_freeDecals = &cg_decalList[0];
177 cg_decalHeadNode.prev = &cg_decalHeadNode;
178 cg_decalHeadNode.next = &cg_decalHeadNode;
179 for (i=0 ; i<MAX_REF_DECALS ; i++) {
180 if (i < MAX_REF_DECALS-1)
181 cg_decalList[i].next = &cg_decalList[i+1];
182
183 cgi.R_FreeDecal (&cg_decalList[i].refDecal);
184 }
185 cg_decalList[MAX_REF_DECALS-1].next = NULL;
186 }
187
188
189 /*
190 ===============
191 CG_AddDecals
192 ===============
193 */
CG_AddDecals(void)194 void CG_AddDecals (void)
195 {
196 cgDecal_t *d, *next, *hNode;
197 float lifeTime, finalTime;
198 float fade;
199 int i, flags, type;
200 vec4_t color;
201 vec3_t temp;
202 bvec4_t outColor;
203 int num;
204
205 if (!cg_decals->intVal)
206 return;
207
208 // Add to list
209 num = 0;
210 hNode = &cg_decalHeadNode;
211 for (d=hNode->prev ; d!=hNode ; d=next) {
212 next = d->prev;
213 num++;
214
215 if (d->colorVel[3] > DECAL_INSTANT) {
216 // Determine how long this decal shall live for
217 if (d->flags & DF_FIXED_LIFE)
218 lifeTime = d->lifeTime;
219 else if (d->flags & DF_USE_BURNLIFE)
220 lifeTime = d->lifeTime + cg_decalBurnLife->floatVal;
221 else
222 lifeTime = d->lifeTime + cg_decalLife->floatVal;
223
224 // Start fading
225 finalTime = d->time + (lifeTime * 1000);
226 if ((float)cg.realTime > finalTime) {
227 // Finished the life, fade for cg_decalFadeTime
228 if (cg_decalFadeTime->floatVal) {
229 lifeTime = cg_decalFadeTime->floatVal;
230
231 // final alpha * ((fade time - time since death) / fade time)
232 color[3] = d->colorVel[3] * ((lifeTime - (((float)cg.realTime - finalTime) * 0.001f)) / lifeTime);
233 }
234 else
235 color[3] = 0.0f;
236 }
237 else {
238 // Not done living, fade between start/final alpha
239 fade = (lifeTime - (((float)cg.realTime - d->time) * 0.001f)) / lifeTime;
240 color[3] = (fade * d->color[3]) + ((1.0f - fade) * d->colorVel[3]);
241 }
242 }
243 else {
244 color[3] = d->color[3];
245 }
246
247 // Faded out
248 if (color[3] <= 0.0001f || num > cg_decalMax->intVal) {
249 CG_FreeDecal (d);
250 continue;
251 }
252
253 if (color[3] > 1.0f)
254 color[3] = 1.0f;
255
256 // Small decal lod
257 if (cg_decalLOD->intVal && d->size < 12) {
258 Vec3Subtract (cg.refDef.viewOrigin, d->refDecal.origin, temp);
259 if (DotProduct(temp, temp)/15000 > 100*d->size)
260 goto nextDecal;
261 }
262
263 // ColorVel calcs
264 if (d->color[3] > DECAL_INSTANT) {
265 for (i=0 ; i<3 ; i++) {
266 if (d->color[i] != d->colorVel[i]) {
267 if (d->color[i] > d->colorVel[i])
268 color[i] = d->color[i] - ((d->color[i] - d->colorVel[i]) * (d->color[3] - color[3]));
269 else
270 color[i] = d->color[i] + ((d->colorVel[i] - d->color[i]) * (d->color[3] - color[3]));
271 }
272 else {
273 color[i] = d->color[i];
274 }
275
276 color[i] = clamp (color[i], 0, 255);
277 }
278 }
279 else {
280 Vec3Copy (d->color, color);
281 }
282
283 // Adjust ramp to desired initial and final alpha settings
284 color[3] = (color[3] * d->color[3]) + ((1 - color[3]) * d->colorVel[3]);
285
286 if (d->flags & DF_ALPHACOLOR)
287 Vec3Scale (color, color[3], color);
288
289 // Think func
290 flags = d->flags;
291 if (d->think && d->thinkNext) {
292 d->thinkNext = qFalse;
293 d->think (d, color, &type, &flags);
294 }
295
296 if (color[3] <= 0.0f)
297 goto nextDecal;
298
299 // Render it
300 outColor[0] = color[0];
301 outColor[1] = color[1];
302 outColor[2] = color[2];
303 outColor[3] = color[3] * 255;
304
305 cgi.R_AddDecal (&d->refDecal, outColor, d->shader, 0);
306
307 nextDecal:
308 // Kill if instant
309 if (d->colorVel[3] <= DECAL_INSTANT) {
310 d->color[3] = 0.0;
311 d->colorVel[3] = 0.0;
312 }
313 }
314 }
315