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