From 26f38392768f3d0af058233ef349ccb6c753fdb6 Mon Sep 17 00:00:00 2001 From: Bill Currie Date: Tue, 9 Sep 2003 08:13:33 +0000 Subject: [PATCH] bring in lordhavoc's vis data optimisations (doesn't work properly yet) and parsing of his extra fields. --- tools/qflight/include/entities.h | 4 +- tools/qflight/include/light.h | 13 ++ tools/qflight/include/options.h | 7 +- tools/qflight/source/Makefile.am | 2 +- tools/qflight/source/entities.c | 147 +++++++++++++++++-- tools/qflight/source/ltface.c | 10 +- tools/qflight/source/options.c | 8 ++ tools/qflight/source/qflight.c | 22 ++- tools/qflight/source/vis.c | 239 +++++++++++++++++++++++++++++++ 9 files changed, 436 insertions(+), 16 deletions(-) create mode 100644 tools/qflight/source/vis.c diff --git a/tools/qflight/include/entities.h b/tools/qflight/include/entities.h index 55c01b131..e7a7632df 100644 --- a/tools/qflight/include/entities.h +++ b/tools/qflight/include/entities.h @@ -31,6 +31,7 @@ #define __entities_h #define DEFAULTLIGHTLEVEL 300 +#define DEFAULTFALLOFF 1.0f typedef struct epair_s { struct epair_s *next; @@ -41,7 +42,7 @@ typedef struct epair_s { typedef struct entity_s { const char *classname; vec3_t origin; - float angle; + vec_t angle; int light; // LordHavoc: added falloff (smaller fractions = bigger light area), // color, and lightradius (also subbrightness to implement lightradius) @@ -68,6 +69,7 @@ extern int num_entities; const char *ValueForKey (entity_t *ent, const char *key); void SetKeyValue (entity_t *ent, const char *key, const char *value); float FloatForKey (entity_t *ent, const char *key); +entity_t *FindEntityWithKeyPair(char *key, char *value); void GetVectorForKey (entity_t *ent, const char *key, vec3_t vec); void LoadEntities (void); diff --git a/tools/qflight/include/light.h b/tools/qflight/include/light.h index 3d22aa12c..53dd20635 100644 --- a/tools/qflight/include/light.h +++ b/tools/qflight/include/light.h @@ -32,6 +32,7 @@ #define ON_EPSILON 0.1 #define MAXLIGHTS 1024 +#define LIGHTDISTBIAS 65536.0 extern float scaledist; extern float scalecos; @@ -60,7 +61,19 @@ int GetFileSpace (int size); void TransformSample (vec3_t in, vec3_t out); void RotateSample (vec3_t in, vec3_t out); +void VisEntity (int ent_index); +void VisStats (void); + extern struct bsp_s *bsp; extern struct dstring_s *lightdata; +typedef struct lightchain_s { + struct lightchain_s *next; + struct entity_s *light; +} lightchain_t; + +extern lightchain_t **surfacelightchain; +extern struct entity_s **novislights; +extern int num_novislights; + #endif// __light_h diff --git a/tools/qflight/include/options.h b/tools/qflight/include/options.h index 92ef0dc4f..0d9b7f75d 100644 --- a/tools/qflight/include/options.h +++ b/tools/qflight/include/options.h @@ -34,9 +34,12 @@ typedef struct { int verbosity; // 0=silent int threads; + int novis; qboolean extra; - float distance; - float range; + vec_t distance; + vec_t range; + vec_t globallightscale; + const char *lightsfilename; } options_t; extern options_t options; diff --git a/tools/qflight/source/Makefile.am b/tools/qflight/source/Makefile.am index b68e760d4..5cc660d4d 100644 --- a/tools/qflight/source/Makefile.am +++ b/tools/qflight/source/Makefile.am @@ -17,7 +17,7 @@ endif bin_PROGRAMS= $(qflight) EXTRA_PROGRAMS= qflight -qflight_SOURCES= entities.c ltface.c options.c qflight.c threads.c trace.c +qflight_SOURCES= entities.c ltface.c options.c qflight.c threads.c trace.c vis.c qflight_LDFLAGS= $(PTHREAD_LDFLAGS) qflight_LDADD= $(QFLIGHT_LIBS) diff --git a/tools/qflight/source/entities.c b/tools/qflight/source/entities.c index 7edd42a90..31868ce62 100644 --- a/tools/qflight/source/entities.c +++ b/tools/qflight/source/entities.c @@ -104,6 +104,14 @@ MatchTargets (void) if (entities[j].targetname && !strcmp (entities[j].targetname, entities[i].target)) { entities[i].targetent = &entities[j]; + // set up spotlight values for lighting code to use + VectorSubtract (entities[i].targetent->origin, + entities[i].origin, entities[i].spotdir); + VectorNormalize (entities[i].spotdir); + if (!entities[i].angle) + entities[i].spotcone = -cos(20 * M_PI / 180); + else + entities[i].spotcone = -cos(entities[i].angle * M_PI / 360); break; } if (j == num_entities) { @@ -121,17 +129,49 @@ MatchTargets (void) sprintf (s, "%i", entities[i].style); SetKeyValue (&entities[i], "style", s); } + + if (entities[i].spotcone <= 0) { + VectorZero (entities[i].spotdir); + entities[i].spotcone = 0; + } } } +static void +WriteLights (void) +{ + int i; + FILE *f; + entity_t *e; + + if (!options.lightsfilename) + return; + printf ("building .lights file\n"); + f = fopen (options.lightsfilename, "wb"); + for (i = 0; i < num_entities; i++) { + e = entities + i; + if (e->light) + fprintf(f, "%f %f %f %f %f %f %f %f %f %f %f %f %f %d\n", + e->origin[0], e->origin[1], e->origin[2], + e->falloff, + e->color[0], e->color[1], e->color[2], + e->subbrightness, + e->spotdir[0], e->spotdir[1], e->spotdir[2], + e->spotcone, e->lightoffset, e->style); + } + fclose (f); +} + void LoadEntities (void) { const char *data; const char *key; - double vec[3]; + double vec[4]; + double temp, color2[3]; entity_t *entity; epair_t *epair; + int i; data = bsp->entdata; @@ -156,6 +196,13 @@ LoadEntities (void) entity = &entities[num_entities]; num_entities++; + memset (entity, 0, sizeof (*entity)); + entity->color[0] = entity->color[1] = entity->color[2] = 1.0f; + color2[0] = color2[1] = color2[2] = 1.0f; + entity->falloff = DEFAULTFALLOFF * DEFAULTFALLOFF; + entity->lightradius = 0; + entity->lightoffset = LIGHTDISTBIAS; + // go through all the keys in this entity while (1) { int c; @@ -196,7 +243,37 @@ LoadEntities (void) fprintf (stderr, "LoadEntities: not 3 values for origin"); VectorCopy (vec, entity->origin); } else if (!strncmp (key, "light", 5) || !strcmp (key, "_light")) { - entity->light = atof(com_token); + i = sscanf (com_token, "%lf %lf %lf %lf", + &vec[0], &vec[1], &vec[2], &vec[3]); + switch (i) { + case 4: // HalfLife light + entity->light = vec[3]; + entity->color[0] = vec[0] * (1.0f / 255.0f); + entity->color[1] = vec[1] * (1.0f / 255.0f); + entity->color[2] = vec[2] * (1.0f / 255.0f); + break; + case 3: + entity->light = 1; + entity->color[0] = vec[0]; + entity->color[1] = vec[1]; + entity->color[2] = vec[2]; + break; + case 1: + entity->light = vec[0]; + entity->color[0] = 1.0f; + entity->color[1] = 1.0f; + entity->color[2] = 1.0f; + break; + default: + Sys_Error ("LoadEntities: _light (or light) key must " + "be 1 (Quake), 4 (HalfLife), or 3 (HLight) " + "values, \"%s\" is not valid\n", com_token); + } + } else if (!strcmp (key, "wait")) { + entity->falloff = atof (com_token); + entity->falloff *= entity->falloff; // presquared + } else if (!strcmp (key, "_lightradius")) { + entity->lightradius = atof (com_token); } else if (!strcmp (key, "style")) { entity->style = atof (com_token); if ((unsigned) entity->style > 254) @@ -204,13 +281,45 @@ LoadEntities (void) entity->style); } else if (!strcmp (key, "angle")) { entity->angle = atof(com_token); + } else if (!strcmp (key, "color") || !strcmp (key, "_color")) { + if (sscanf (com_token, "%lf %lf %lf", + &vec[0], &vec[1], &vec[2]) != 3) + Sys_Error ("LoadEntities: not 3 values for color"); + // scale the color to have at least one component at 1.0 + temp = vec[0]; + if (vec[1] > temp) + temp = vec[1]; + if (vec[2] > temp) + temp = vec[2]; + if (temp != 0.0) + temp = 1.0 / temp; + VectorScale (vec, temp, color2); } } // all fields have been parsed - if (entity->classname && !strncmp (entity->classname, "light", 5) - && !entity->light) - entity->light = DEFAULTLIGHTLEVEL; + if (entity->classname && !strncmp (entity->classname, "light", 5)) + if (!entity->light) + entity->light = DEFAULTLIGHTLEVEL; + + if (entity->light) { + // convert to subtraction to the brightness for the whole light, + // so it will fade nicely, rather than being clipped off + VectorScale (color2, + entity->light * 16384.0 * options.globallightscale, + color2); + entity->color[0] *= color2[0]; + entity->color[1] *= color2[1]; + entity->color[2] *= color2[2]; + + if (entity->lightradius) + entity->subbrightness = 1.0 / (entity->lightradius + * entity->lightradius + * entity->falloff + + LIGHTDISTBIAS); + if (entity->subbrightness < (1.0 / 1048576.0)) + entity->subbrightness = (1.0 / 1048576.0); + } if (entity->classname && !strcmp (entity->classname, "light")) { if (entity->targetname && entity->targetname[0] @@ -229,6 +338,10 @@ LoadEntities (void) printf ("%d entities read\n", num_entities); MatchTargets (); + + WriteLights(); + + novislights = (entity_t **)calloc (num_entities, sizeof (entity_t *)); } const char * @@ -268,6 +381,26 @@ FloatForKey (entity_t *ent, const char *key) return atof (k); } +entity_t * +FindEntityWithKeyPair (char *key, char *value) +{ + entity_t *ent; + epair_t *ep; + int i; + + for (i = 0; i < num_entities; i++) { + ent = &entities[i]; + for (ep = ent->epairs; ep; ep = ep->next) { + if (!strcmp (ep->key, key)) { + if (!strcmp (ep->value, value)) + return ent; + break; + } + } + } + return 0; +} + void GetVectorForKey (entity_t *ent, const char *key, vec3_t vec) { @@ -281,7 +414,6 @@ void WriteEntitiesToString (void) { dstring_t *buf; - char line[128]; epair_t *ep; int i; @@ -298,8 +430,7 @@ WriteEntitiesToString (void) dstring_appendstr (buf, "{\n"); for (ep = entities[i].epairs; ep; ep = ep->next) { - sprintf (line, "\"%s\" \"%s\"\n", ep->key, ep->value); - dstring_appendstr (buf, line); + dasprintf (buf, "\"%s\" \"%s\"\n", ep->key, ep->value); } dstring_appendstr (buf, "}\n"); } diff --git a/tools/qflight/source/ltface.c b/tools/qflight/source/ltface.c index 8f67a29c8..f5c1fe258 100644 --- a/tools/qflight/source/ltface.c +++ b/tools/qflight/source/ltface.c @@ -461,6 +461,7 @@ LightFace (int surfnum) lightinfo_t l; vec_t total; vec_t *light; + lightchain_t *lightchain; f = bsp->faces + surfnum; @@ -500,9 +501,12 @@ LightFace (int surfnum) // cast all lights l.numlightstyles = 0; - for (i = 0; i < num_entities; i++) { - if (entities[i].light) - SingleLightFace (&entities[i], &l); + for (lightchain = surfacelightchain[surfnum]; lightchain; + lightchain = lightchain->next) { + SingleLightFace (lightchain->light, &l); + } + for (i = 0; i < num_novislights; i++) { + SingleLightFace (novislights[i], &l); } FixMinlight (&l); diff --git a/tools/qflight/source/options.c b/tools/qflight/source/options.c index 8bc11f08d..2e1883d1d 100644 --- a/tools/qflight/source/options.c +++ b/tools/qflight/source/options.c @@ -54,6 +54,7 @@ static struct option const long_options[] = { {"version", no_argument, 0, 'V'}, {"threads", required_argument, 0, 't'}, {"extra", no_argument, 0, 'e'}, + {"novis", no_argument, 0, 'N'}, {"distance", required_argument, 0, 'd'}, {"range", required_argument, 0, 'r'}, {"file", required_argument, 0, 'f'}, @@ -65,6 +66,7 @@ static const char *short_options = "v" // verbose "h" // help "V" // version + "N" // novis "t:" // threads "e" // extra sampling "d:" // scale distance @@ -83,6 +85,7 @@ usage (int status) " -v, --verbose Display more output than usual\n" " -h, --help Display this help and exit\n" " -V, --version Output version information and exit\n" + " -N, --novis Don't use vis data\n" " -t, --threads [num] Number of threads to use\n" " -e, --extra Apply extra sampling\n" " -d, --distance [scale] Scale distance\n" @@ -96,11 +99,13 @@ DecodeArgs (int argc, char **argv) { int c; + memset (&options, 0, sizeof (options)); options.verbosity = 0; options.threads = 1; options.extra = false; options.distance = 1.0; options.range = 0.5; + options.globallightscale = 1.0; bspfile = NULL; while ((c = getopt_long (argc, argv, short_options, long_options, 0)) @@ -115,6 +120,9 @@ DecodeArgs (int argc, char **argv) case 'h': // help usage (0); break; + case 'N': // novis + options.novis = 1; + break; case 'V': // version printf ("%s version %s\n", PACKAGE, VERSION); exit (0); diff --git a/tools/qflight/source/qflight.c b/tools/qflight/source/qflight.c index d8be9538b..5a30bc406 100644 --- a/tools/qflight/source/qflight.c +++ b/tools/qflight/source/qflight.c @@ -71,6 +71,7 @@ dstring_t *lightdata; dmodel_t *bspmodel; int bspfileface; // next surface to dispatch +int bspfileent; // next entity to dispatch vec3_t bsp_origin; @@ -93,10 +94,25 @@ GetFileSpace (int size) return ofs; } +static void * +VisThread (void *junk) +{ + int i; + + while (1) { + LOCK; + i = bspfileent++; + UNLOCK; + if (i >= num_entities) + return 0; + VisEntity (i); + } +} + static void * LightThread (void *junk) { - int i; + int i; while (1) { LOCK; @@ -113,7 +129,11 @@ static void LightWorld (void) { lightdata = dstring_new (); + surfacelightchain = (lightchain_t **) calloc (bsp->numfaces, + sizeof (lightchain_t *)); + VisThread (0); // not worth threading :/ + VisStats (); RunThreadsOn (LightThread); BSP_AddLighting (bsp, lightdata->str, lightdata->size); diff --git a/tools/qflight/source/vis.c b/tools/qflight/source/vis.c new file mode 100644 index 000000000..53e028b79 --- /dev/null +++ b/tools/qflight/source/vis.c @@ -0,0 +1,239 @@ +/* + vis.c + + PVS support to speed lighting calcs + + Copyright (C) 2003 Bill Currie + + Author: Bill Currie + Date: 2003/9/8 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +static __attribute__ ((unused)) const char rcsid[] = + "$Id$"; + +#include +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include "QF/bspfile.h" +#include "QF/mathlib.h" + +#include "entities.h" +#include "light.h" +#include "options.h" +#include "threads.h" + +static struct { + int empty; + int solid; + int water; + int slime; + int lava; + int sky; + int misc; + + int lights; + int cast; +} counts; + +static lightchain_t *free_lightchains; +lightchain_t **surfacelightchain; +entity_t **alllights; +int num_alllights; +entity_t **novislights; +int num_novislights; + +static dleaf_t * +Light_PointInLeaf (vec3_t point) +{ + int num = 0, side; + dnode_t *node; + dplane_t *plane; + + while (num >= 0) { + node = bsp->nodes + num; + plane = bsp->planes + node->planenum; + side = DotProduct (point, plane->normal) < plane->dist; + num = node->children[side]; + } + return bsp->leafs + (-1 - num); +} + +static void +DecompressVis (byte *in, byte *out, int size) +{ + byte *end = out + size; + int n; + + while (out < end) { + n = *in++; + if (n) { + *out++ = n; + } else { + n = *in++; + while (n-- && out < end) + *out++ = 0; + } + } +} + +static lightchain_t * +get_lightchain (void) +{ + lightchain_t *lc; + int i; + + if (!free_lightchains) { + lc = free_lightchains = malloc (1024 * sizeof (lightchain_t)); + for (i = 0; i < 1023; i++, lc++) + lc->next = lc + 1; + lc->next = 0; + } + lc = free_lightchains; + free_lightchains = lc->next; + return lc; +} + +static inline void +mark_face (int surfnum, byte *surfacehit, entity_t *entity) +{ + lightchain_t *lc; + + if (surfacehit[surfnum >> 3] & (1 << (surfnum & 7))) + return; + surfacehit[surfnum >> 3] |= (1 << (surfnum & 7)); + counts.cast++; + lc = get_lightchain (); + lc->next = surfacelightchain[surfnum]; + lc->light = entity; + surfacelightchain[surfnum] = lc; +} + +void +VisEntity (int ent_index) +{ + entity_t *entity = entities + ent_index; + dleaf_t *leaf; + int ignorevis = false; + int i, j; + unsigned short *mark; + byte *vis, *surfacehit; + int vis_size; + + if (!entity->light) + return; + + counts.lights++; + + leaf = Light_PointInLeaf (entity->origin); + + switch (leaf->contents) { + case CONTENTS_EMPTY: + counts.empty++; + break; + case CONTENTS_SOLID: + counts.solid++; + ignorevis = true; + break; + case CONTENTS_WATER: + counts.water++; + break; + case CONTENTS_SLIME: + counts.slime++; + break; + case CONTENTS_LAVA: + counts.lava++; + break; + case CONTENTS_SKY: + counts.sky++; + ignorevis = true; + break; + default: + counts.misc++; + break; + } + if (leaf->visofs == -1 || ignorevis || options.novis) { + counts.cast += bsp->numfaces; + novislights[num_novislights++] = entity; + } else { + vis_size = (bsp->numleafs + 7) / 8; + vis = alloca (vis_size + (bsp->numfaces + 7) / 8); + surfacehit = vis + vis_size; + memset (surfacehit, 0, (bsp->numfaces + 7) / 8); + + DecompressVis (bsp->visdata + leaf->visofs, vis, + (bsp->numleafs + 7) >> 3); + for (i = 1, leaf = bsp->leafs + 1; i < bsp->numleafs; i++, leaf++) { + if (!leaf->nummarksurfaces) + continue; + if (vis[i >> 3] & (1 << (i & 7))) { + for (j = 0, mark = bsp->marksurfaces + leaf->firstmarksurface; + j < leaf->nummarksurfaces; j++, mark++) { + mark_face (*mark, surfacehit, entity); + } + } + } + for (i = 1; i < bsp->nummodels; i++) { + for (j = 0; j < bsp->models[i].numfaces; j++) { + //FIXME vis + mark_face (bsp->models[i].firstface + j, surfacehit, entity); + } + } + } +} + +void +VisStats (void) +{ + int i, count; + + printf ("%4i lights\n", counts.lights); + printf ("%4i air\n", counts.empty); + printf ("%4i solid\n", counts.solid); + printf ("%4i water\n", counts.water); + printf ("%4i slime\n", counts.slime); + printf ("%4i lava\n", counts.lava); + printf ("%4i sky\n", counts.sky); + printf ("%4i unknown\n", counts.misc); + + for (i = count = 0; i < bsp->numfaces; i++) + if (surfacelightchain[i]) + count++; + printf ("%i faces, %i (%i%%) may receive light\n", bsp->numfaces, + count, count * 100 / bsp->numfaces); + if (counts.solid || counts.sky) + printf ("warning: %i lights of %i lights (%i%%) were found in sky\n" + "or solid and will not be accelerated using vis, move them\n" + "out of the solid or sky to accelerate compiling\n", + counts.solid + counts.sky, counts.lights, + (counts.solid + counts.sky) * 100 / counts.lights); + printf ("%i lights will be cast onto %i surfaces, %i casts will\n" + "be performed\n", counts.lights, bsp->numfaces, counts.cast); +}