1 /*
2 	trace.c
3 
4 	(description)
5 
6 	Copyright (C) 1996-1997  Id Software, Inc.
7 	Copyright (C) 2002 Colin Thompson
8 
9 	This program is free software; you can redistribute it and/or
10 	modify it under the terms of the GNU General Public License
11 	as published by the Free Software Foundation; either version 2
12 	of the License, or (at your option) any later version.
13 
14 	This program is distributed in the hope that it will be useful,
15 	but WITHOUT ANY WARRANTY; without even the implied warranty of
16 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17 
18 	See the GNU General Public License for more details.
19 
20 	You should have received a copy of the GNU General Public License
21 	along with this program; if not, write to:
22 
23 		Free Software Foundation, Inc.
24 		59 Temple Place - Suite 330
25 		Boston, MA  02111-1307, USA
26 
27 */
28 #ifdef HAVE_CONFIG_H
29 # include "config.h"
30 #endif
31 
32 #ifdef HAVE_IO_H
33 # include <io.h>
34 #endif
35 #ifdef HAVE_STRING_H
36 # include <string.h>
37 #endif
38 #ifdef HAVE_STRINGS_H
39 # include <strings.h>
40 #endif
41 #ifdef HAVE_UNISTD_H
42 # include <unistd.h>
43 #endif
44 
45 #include <stdlib.h>
46 
47 #include "QF/bspfile.h"
48 #include "QF/dstring.h"
49 #include "QF/mathlib.h"
50 #include "QF/qfplist.h"
51 #include "QF/qtypes.h"
52 #include "QF/quakefs.h"
53 #include "QF/script.h"
54 #include "QF/sys.h"
55 
56 #include "light.h"
57 #include "threads.h"
58 #include "entities.h"
59 #include "options.h"
60 #include "properties.h"
61 
62 entity_t *entities;
63 int num_entities;
64 static int max_entities;
65 
66 /*
67 	ENTITY FILE PARSING
68 
69 	If a light has a targetname, generate a unique style in the 32-63 range
70 */
71 
72 int numlighttargets;
73 const char *lighttargets[32];
74 
75 
76 static int
LightStyleForTargetname(const char * targetname,qboolean alloc)77 LightStyleForTargetname (const char *targetname, qboolean alloc)
78 {
79 	int		i;
80 
81 	for (i = 0; i < numlighttargets; i++)
82 		if (!strcmp (lighttargets[i], targetname))
83 			return 32 + i;
84 
85 	if (!alloc)
86 		return -1;
87 
88 	lighttargets[i] = targetname;
89 	numlighttargets++;
90 	return numlighttargets - 1 + 32;
91 }
92 
93 static void
MatchTargets(void)94 MatchTargets (void)
95 {
96 	int			i, j;
97 
98 	for (i = 0; i < num_entities; i++) {
99 		if (!entities[i].target || !entities[i].target[0])
100 			continue;
101 
102 		for (j = 0; j < num_entities; j++)
103 			if (entities[j].targetname
104 				&& !strcmp (entities[j].targetname, entities[i].target)) {
105 				entities[i].targetent = &entities[j];
106 				// set up spotlight values for lighting code to use
107 				VectorSubtract (entities[i].targetent->origin,
108 								entities[i].origin, entities[i].spotdir);
109 				VectorNormalize (entities[i].spotdir);
110 				if (!entities[i].angle)
111 					entities[i].spotcone = -cos(20 * M_PI / 180);
112 				else
113 					entities[i].spotcone = -cos(entities[i].angle * M_PI / 360);
114 				break;
115 			}
116 		if (j == num_entities) {
117 			printf ("WARNING: entity at (%i,%i,%i) (%s) has unmatched "
118 					"target\n",
119 					(int) entities[i].origin[0], (int) entities[i].origin[1],
120 					(int) entities[i].origin[2], entities[i].classname);
121 			continue;
122 		}
123 		// set the style on the source ent for switchable lights
124 		if (entities[j].style) {
125 			char s[16];
126 
127 			entities[i].style = entities[j].style;
128 			sprintf (s, "%i", entities[i].style);
129 			SetKeyValue (&entities[i], "style", s);
130 		}
131 
132 		if (entities[i].spotcone >= 0) {
133 			VectorZero (entities[i].spotdir);
134 			entities[i].spotcone = 0;
135 		}
136 	}
137 }
138 
139 static void
WriteLights(void)140 WriteLights (void)
141 {
142 	int         i;
143 	FILE       *f;
144 	entity_t   *e;
145 
146 	if (!options.lightsfilename)
147 		return;
148 	printf ("building .lights file\n");
149 	f = fopen (options.lightsfilename, "wb");
150 	for (i = 0; i < num_entities; i++) {
151 		e = entities + i;
152 		if (e->light)
153 			fprintf(f, "%f %f %f %f %f %f %f %f %f %f %f %f %f %d\n",
154 					e->origin[0], e->origin[1], e->origin[2],
155 					e->falloff,
156 					e->color[0], e->color[1], e->color[2],
157 					e->subbrightness,
158 					e->spotdir[0], e->spotdir[1], e->spotdir[2],
159 					e->spotcone, e->lightoffset, e->style);
160 	}
161 	fclose (f);
162 }
163 
164 void
LoadEntities(void)165 LoadEntities (void)
166 {
167 	const char *key;
168 	double      vec[4];
169 	entity_t   *entity;
170 	epair_t    *epair;
171 	float       cutoff_range;
172 	float       intensity;
173 	plitem_t   *dict;
174 	script_t   *script;
175 
176 	script = Script_New ();
177 	Script_Start (script, "ent data", bsp->entdata);
178 
179 	// start parsing
180 	max_entities = num_entities = 0;
181 	entities = 0;
182 
183 	// go through all the entities
184 	while (Script_GetToken (script, 1)) {
185 		// parse the opening brace
186 		if (strcmp (script->token->str, "{"))
187 			fprintf (stderr, "LoadEntities: found %s when expecting {\n",
188 					 script->token->str);
189 
190 		if (num_entities == max_entities) {
191 			max_entities += 128;
192 			entities = realloc (entities, max_entities * sizeof (entity_t));
193 		}
194 		entity = &entities[num_entities];
195 		num_entities++;
196 
197 		memset (entity, 0, sizeof (*entity));
198 		entity->color[0] = entity->color[1] = entity->color[2] = 1.0f;
199 		VectorCopy (entity->color, entity->color2);
200 		entity->falloff = DEFAULTFALLOFF * DEFAULTFALLOFF;
201 		entity->lightradius = 0;
202 		entity->lightoffset = LIGHTDISTBIAS;
203 		entity->attenuation = options.attenuation;
204 
205 		dict = PL_NewDictionary ();
206 
207 		// go through all the keys in this entity
208 		while (1) {
209 			// parse key
210 			if (!Script_GetToken (script, 1))
211 				fprintf (stderr, "LoadEntities: EOF without closing brace");
212 			if (!strcmp (script->token->str, "}"))
213 				break;
214 			key = strdup (script->token->str);
215 
216 			// parse value
217 			// FIXME shouldn't cross line
218 			if (!Script_GetToken (script, 1))
219 				fprintf (stderr, "LoadEntities: EOF without closing brace");
220 			if (!strcmp (script->token->str, "}"))
221 				fprintf (stderr, "LoadEntities: closing brace without data");
222 
223 			epair = calloc (1, sizeof (epair_t));
224 			epair->key = key;
225 			epair->value = strdup (script->token->str);
226 			epair->next = entity->epairs;
227 			entity->epairs = epair;
228 
229 			PL_D_AddObject (dict, key, PL_NewString (script->token->str));
230 
231 			if (!strcmp (key, "classname"))
232 				entity->classname = epair->value;
233 			else if (!strcmp (key, "target"))
234 				entity->target = epair->value;
235 			else if (!strcmp (key, "targetname"))
236 				entity->targetname = epair->value;
237 			else if (!strcmp (key, "origin")) {
238 				// scan into doubles, then assign
239 				// which makes it vec_t size independent
240 				if (sscanf (script->token->str, "%lf %lf %lf",
241 							&vec[0], &vec[1], &vec[2]) != 3)
242 					fprintf (stderr, "LoadEntities: not 3 values for origin");
243 				else
244 					VectorCopy (vec, entity->origin);
245 			}
246 		}
247 
248 		if (options.verbosity > 1 && entity->targetname)
249 			printf ("%s %d %d\n", entity->targetname, entity->light,
250 					entity->style);
251 
252 		// all fields have been parsed
253 		if (entity->classname && !strncmp (entity->classname, "light", 5)) {
254 			set_properties (entity, dict);
255 			if (!entity->light)
256 				entity->light = DEFAULTLIGHTLEVEL;
257 			if (!entity->noise)
258 				entity->noise = options.noise;
259 			if (!entity->persistence)
260 				entity->persistence = 1;
261 		}
262 		PL_Free (dict);
263 
264 		if (entity->light) {
265 			// convert to subtraction to the brightness for the whole light,
266 			// so it will fade nicely, rather than being clipped off
267 			VectorScale (entity->color2,
268 						 entity->light * 16384.0 * options.globallightscale,
269 						 entity->color2);
270 			entity->color[0] *= entity->color2[0];
271 			entity->color[1] *= entity->color2[1];
272 			entity->color[2] *= entity->color2[2];
273 
274 			if (entity->lightradius)
275 				entity->subbrightness = 1.0 / (entity->lightradius
276 											   * entity->lightradius
277 											   * entity->falloff
278 											   + LIGHTDISTBIAS);
279 			if (entity->subbrightness < (1.0 / 1048576.0))
280 				entity->subbrightness = (1.0 / 1048576.0);
281 
282 			intensity = entity->light;
283 			if (intensity < 0)
284 				intensity = -intensity;
285 			switch (entity->attenuation) {
286 				case LIGHT_RADIUS:
287 					// default radius is intensity (same as LIGHT_LINEAR)
288 					if (!entity->radius)
289 						entity->radius = intensity;
290 					break;
291 				case LIGHT_LINEAR:
292 					// don't allow radius to be greater than intensity
293 					if (entity->radius > intensity || entity->radius == 0)
294 						entity->radius = intensity;
295 					break;
296 				case LIGHT_REALISTIC:
297 					if (options.cutoff) {
298 						// approximate limit for faster lighting
299 						cutoff_range = sqrt (intensity / options.cutoff);
300 						if (!entity->radius || cutoff_range < entity->radius)
301 							entity->radius = cutoff_range;
302 					}
303 					break;
304 				case LIGHT_INVERSE:
305 					if (options.cutoff) {
306 						// approximate limit for faster lighting
307 						cutoff_range = intensity / options.cutoff;
308 						if (!entity->radius || cutoff_range < entity->radius)
309 							entity->radius = cutoff_range;
310 					}
311 					break;
312 				case LIGHT_NO_ATTEN:
313 					break;
314 				case LIGHT_LH:
315 					break;
316 			}
317 		}
318 
319 		if (entity->classname && !strcmp (entity->classname, "light")) {
320 			if (entity->targetname && entity->targetname[0]
321 				&& !entity->style) {
322 				char s[16];
323 
324 				entity->style = LightStyleForTargetname (entity->targetname,
325 														 true);
326 				sprintf (s, "%i", entity->style);
327 				SetKeyValue (entity, "style", s);
328 			}
329 		}
330 	}
331 
332 	if (options.verbosity >= 0)
333 		printf ("%d entities read\n", num_entities);
334 
335 	MatchTargets ();
336 
337 	WriteLights();
338 
339 	novislights = (entity_t **)calloc (num_entities, sizeof (entity_t *));
340 }
341 
342 const char *
ValueForKey(entity_t * ent,const char * key)343 ValueForKey (entity_t *ent, const char *key)
344 {
345 	epair_t		*ep;
346 
347 	for (ep = ent->epairs; ep; ep = ep->next)
348 		if (!strcmp (ep->key, key))
349 			return ep->value;
350 	return "";
351 }
352 
353 void
SetKeyValue(entity_t * ent,const char * key,const char * value)354 SetKeyValue (entity_t *ent, const char *key, const char *value)
355 {
356 	epair_t		*ep;
357 
358 	for (ep = ent->epairs; ep; ep = ep->next)
359 		if (!strcmp (ep->key, key)) {
360 			ep->value = strdup (value);
361 			return;
362 		}
363 	ep = malloc (sizeof (*ep));
364 	ep->next = ent->epairs;
365 	ent->epairs = ep;
366 	ep->key = strdup (key);
367 	ep->value = strdup (value);
368 }
369 
370 float
FloatForKey(entity_t * ent,const char * key)371 FloatForKey (entity_t *ent, const char *key)
372 {
373 	const char*k;
374 
375 	k = ValueForKey (ent, key);
376 	return atof (k);
377 }
378 
379 entity_t *
FindEntityWithKeyPair(const char * key,const char * value)380 FindEntityWithKeyPair (const char *key, const char *value)
381 {
382 	entity_t   *ent;
383 	epair_t    *ep;
384 	int         i;
385 
386 	for (i = 0; i < num_entities; i++) {
387 		ent = &entities[i];
388 		for (ep = ent->epairs; ep; ep = ep->next) {
389 			if (!strcmp (ep->key, key)) {
390 				if (!strcmp (ep->value, value))
391 					return ent;
392 				break;
393 			}
394 		}
395 	}
396 	return 0;
397 }
398 
399 void
GetVectorForKey(entity_t * ent,const char * key,vec3_t vec)400 GetVectorForKey (entity_t *ent, const char *key, vec3_t vec)
401 {
402 	const char *k;
403 
404 	k = ValueForKey (ent, key);
405 	sscanf (k, "%f %f %f", &vec[0], &vec[1], &vec[2]);
406 }
407 
408 void
WriteEntitiesToString(void)409 WriteEntitiesToString (void)
410 {
411 	dstring_t  *buf;
412 	epair_t		*ep;
413 	int			i;
414 
415 	buf = dstring_newstr ();
416 
417 	if (options.verbosity >= 0)
418 		printf ("%i switchable light styles\n", numlighttargets);
419 
420 	for (i = 0; i < num_entities; i++) {
421 		ep = entities[i].epairs;
422 		if (!ep)
423 			continue;		// ent got removed
424 
425 		dstring_appendstr (buf, "{\n");
426 
427 		for (ep = entities[i].epairs; ep; ep = ep->next) {
428 			dasprintf (buf, "\"%s\" \"%s\"\n", ep->key, ep->value);
429 		}
430 		dstring_appendstr (buf, "}\n");
431 	}
432 	BSP_AddEntities (bsp, buf->str, buf->size);
433 }
434