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