BEU5APLPKXWD7RKKWDR6ACTC7KIVWPV4F62OLQPLCLKPRIWBUZ2QC #include "common/cmdlib.h"#include "common/mathlib.h"#include "common/qfiles.h"#include "common/threads.h"static char * help_string ="4bld supporting v38 and v220 map formats plus QBSP extended limits.\n""Usage: 4bld [options] [mapfile | bspfile]\n\n"" -moddir [path]: Set a mod directory. Default is parent dir of map file.\n"" -basedir [path]: Set the directory for assets not in moddir. Default is moddir.\n"" -gamedir [path]: Set game directory, the folder with game executable.\n"" -threads #: number of CPU threads to use\n""BSP pass:\n"" -bsp: enable bsp pass, requires a .map file as input\n"" -chop #: Subdivide size.\n"" Default: 240 Range: 32-1024\n"" -choplight #: Subdivide size for surface lights.\n"" Default: 240 Range: 32-1024\n"" -largebounds or -lb: Increase max map size for supporting engines.\n"" -micro #: Minimum microbrush size. Default: 0.02\n"" Suggested range: 0.02 - 1.0\n"" -nosubdiv: Disable subdivision.\n"" -qbsp: Greatly expanded map and entity limits for supporting engines.\n""VIS pass:\n"" -vis: enable vis pass, requires a .bsp file as input or bsp pass enabled\n"" -fast: fast single vis pass""RAD pass:\n"" -rad: enable rad pass, requires a .bsp file as input or bsp and vis passes enabled\n"" -ambient #\n"" -bounce #\n"" -dice\n"" -direct #\n"" -entity #\n"" -extra\n"" -maxdata #\n"" -maxlight #\n"" -noedgefix\n"" -nudge #\n"" -saturate #\n"" -scale #\n"" -smooth #\n"" -subdiv\n"" -sunradscale #\n""Debugging tools:\n"" -block # #: Division tree block size, square\n"" -blocks # # # #: Div tree block size, rectangular\n"" -blocksize: map cube size for processing. Default: 1024\n"" -fulldetail: Change most brushes to detail.\n"" -leaktest: Perform leak test only.\n"" -nocsg: No constructive solid geometry.\n"" -nodetail: No detail brushes.\n"" -nomerge: Don't merge visible faces per node.\n"" -noorigfix: Disable texture fix for origin offsets.\n"" -noprune: Disable node pruning.\n"" -noshare: Don't look for shared edges on save.\n"" -noskipfix: Do not automatically set skip contents to zero.\n"" -notjunc: Disable edge cleanup.\n"" -nowater: Ignore warp surfaces.\n"" -noweld: Disable vertex welding.\n"" -onlyents: Grab the entites and resave.\n"" -v: Display more verbose output.\n"" -dump\n"" -noblock\n"" -nopvs\n"" -savetrace\n"" -tmpin\n"" -tmpout\n";extern qboolean origfix;extern qboolean noweld;extern qboolean nocsg;extern qboolean noshare;extern qboolean notjunc;extern qboolean nowater;extern qboolean noprune;extern qboolean nomerge;extern qboolean nosubdiv;extern qboolean nodetail;extern qboolean fulldetail;extern qboolean onlyents;extern float microvolume;extern qboolean leaktest;extern qboolean use_qbsp;extern int32_t max_entities;extern int32_t max_bounds;extern int32_t block_size;extern qboolean noskipfix;extern float subdivide_size;extern float sublight_size;extern int32_t block_xl;extern int32_t block_yl;extern int32_t block_xh;extern int32_t block_yh;extern char inbase[32];extern char outbase[32];extern qboolean fastvis;extern qboolean nosort;extern qboolean dumppatches;extern int32_t numbounce;extern qboolean extrasamples;extern qboolean noedgefix;extern int32_t maxdata;extern float lightscale;extern float sunradscale;extern float patch_cutoff;extern float direct_scale;extern float entity_scale;extern qboolean noblock;extern float smoothing_value;extern float sample_nudge;extern float ambient;extern qboolean save_trace;extern float maxlight;extern qboolean dicepatches;extern float saturation;extern qboolean nopvs;void BSP_ProcessArgument(const char * arg);void VIS_ProcessArgument(const char * arg);void RAD_ProcessArgument(const char * arg);int main(int argc, char *argv []) {char tgamedir[1024] = "";char tbasedir[1024] = "";char tmoddir[1024] = "";qboolean do_bsp = false;qboolean do_vis = false;qboolean do_rad = false;ThreadSetDefault();int32_t i;for (i = 1; i < argc; i++) {if (!strcmp(argv[i], "-bsp")) {do_bsp = true;} else if (!strcmp(argv[i], "-vis")) {do_vis = true;} else if (!strcmp(argv[i], "-rad")) {do_rad = true;} else if (!strcmp(argv[i], "-noorigfix")) {printf("origfix = false\n");origfix = false;} else if (!strcmp(argv[i], "-v")) {printf("verbose = true\n");verbose = true;} else if (!strcmp(argv[i], "-help")) {printf("%s\n", help_string);exit(1);} else if (!strcmp(argv[i],"-threads")) {numthreads = atoi(argv[i+1]);printf("threads = %i\n", numthreads);i++;} else if (!strcmp(argv[i], "-noweld")) {printf("noweld = true\n");noweld = true;} else if (!strcmp(argv[i], "-nocsg")) {printf("nocsg = true\n");nocsg = true;} else if (!strcmp(argv[i], "-noshare")) {printf("noshare = true\n");noshare = true;} else if (!strcmp(argv[i], "-notjunc")) {printf("notjunc = true\n");notjunc = true;} else if (!strcmp(argv[i], "-nowater")) {printf("nowater = true\n");nowater = true;} else if (!strcmp(argv[i], "-noprune")) {printf("noprune = true\n");noprune = true;} else if (!strcmp(argv[i], "-nomerge")) {printf("nomerge = true\n");nomerge = true;} else if (!strcmp(argv[i], "-nosubdiv")) {printf("nosubdiv = true\n");nosubdiv = true;} else if (!strcmp(argv[i], "-nodetail")) {printf("nodetail = true\n");nodetail = true;} else if (!strcmp(argv[i], "-fulldetail")) {printf("fulldetail = true\n");fulldetail = true;} else if (!strcmp(argv[i], "-onlyents")) {printf("onlyents = true\n");onlyents = true;} else if (!strcmp(argv[i], "-micro")) {microvolume = atof(argv[i + 1]);i++;} else if (!strcmp(argv[i], "-leaktest")) {printf("leaktest = true\n");leaktest = true;} else if (!strcmp(argv[i], "-qbsp")) {// qb: qbspprintf("use_qbsp = true\n");use_qbsp = true;max_entities = MAX_MAP_ENTITIES_QBSP;max_bounds = MAX_MAP_SIZE;block_size = MAX_BLOCK_SIZE; // qb: otherwise limits map range} else if (!strcmp(argv[i], "-noskipfix")) {printf("noskipfix = true\n");noskipfix = true;} else if (!strcmp(argv[i], "-largebounds") || !strcmp(argv[i], "-lb")) {// qb: from kmqbsp3- Knightmare addedif (use_qbsp) {printf("[-largebounds is not required with -qbsp]\n");} else {max_bounds = MAX_MAP_SIZE;block_size = MAX_BLOCK_SIZE; // qb: otherwise limits map rangeprintf("largebounds: using max bound size of %i\n", MAX_MAP_SIZE);}}// qb: set gamedir, moddir, and basedirelse if (!strcmp(argv[i], "-gamedir")) {strcpy(tgamedir, argv[i + 1]);i++;} else if (!strcmp(argv[i], "-moddir")) {strcpy(tmoddir, argv[i + 1]);i++;} else if (!strcmp(argv[i], "-basedir")) {strcpy(tbasedir, argv[i + 1]);i++;} else if((!strcmp(argv[i], "-chop")) || (!strcmp(argv[i], "-subdiv"))) {subdivide_size = atof(argv[i + 1]);if (subdivide_size < 32) {subdivide_size = 32;printf("subdivide_size set to minimum size: 32\n");}if (subdivide_size > 1024) {subdivide_size = 1024;printf("subdivide_size set to maximum size: 1024\n");}printf("subdivide_size = %f\n", subdivide_size);i++;} else if((!strcmp(argv[i], "-choplight")) || (!strcmp(argv[i], "-choplights")) || (!strcmp(argv[i], "-subdivlight"))) {// qb: chop surf lights independentlysublight_size = atof(argv[i + 1]);if (sublight_size < 32) {sublight_size = 32;printf("sublight_size set to minimum size: 32\n");}if (sublight_size > 1024) {sublight_size = 1024;printf("sublight_size set to maximum size: 1024\n");}printf("sublight_size = %f\n", sublight_size);i++;} else if(!strcmp(argv[i], "-blocksize")) {block_size = atof(argv[i + 1]);if (block_size < 128) {block_size = 128;printf("block_size set to minimum size: 128\n");}if (block_size > MAX_BLOCK_SIZE) {block_size = MAX_BLOCK_SIZE;printf("block_size set to minimum size: MAX_BLOCK_SIZE\n");}printf("blocksize: %i\n", block_size);i++;} else if (!strcmp(argv[i], "-block")) {block_xl = block_yl = atoi(argv[i + 1]); // qb: fixed... has it always been wrong? was xl = xh and yl = yhblock_xh = block_yh = atoi(argv[i + 2]);printf("block: %i,%i\n", block_xl, block_xh);i += 2;} else if (!strcmp(argv[i], "-blocks")) {block_xl = atoi(argv[i + 1]);block_yl = atoi(argv[i + 2]);block_xh = atoi(argv[i + 3]);block_yh = atoi(argv[i + 4]);printf("blocks: %i,%i to %i,%i\n",block_xl, block_yl, block_xh, block_yh);i += 4;} else if (!strcmp(argv[i], "-tmpin"))strcpy(inbase, "/tmp");else if (!strcmp(argv[i], "-tmpout"))strcpy(outbase, "/tmp");else if (!strcmp(argv[i], "-fast")) {printf("fastvis = true\n");fastvis = true;} else if (!strcmp(argv[i], "-nosort")) {printf("nosort = true\n");nosort = true;} else if (!strcmp(argv[i], "-dump")) {printf("dicepatches = true\n");dumppatches = true;} else if (!strcmp(argv[i], "-bounce")) {numbounce = atoi(argv[i + 1]);i++;} else if (!strcmp(argv[i], "-extra")) {extrasamples = true;printf("extrasamples = true\n");} else if (!strcmp(argv[i], "-noedgefix")) {// qb: light warp surfacesnoedgefix = true;printf("no edge fix = true\n");} else if (!strcmp(argv[i], "-dice")) {dicepatches = true;printf("dicepatches = true\n");} else if (!strcmp(argv[i], "-threads")) {numthreads = atoi(argv[i + 1]);i++;} else if (!strcmp(argv[i], "-maxdata")) { // qb: allows increase for some enginesmaxdata = atoi(argv[i + 1]);i++;if (maxdata > DEFAULT_MAP_LIGHTING) {printf("lighting maxdata (%i) exceeds typical limit (%i).\n", maxdata, DEFAULT_MAP_LIGHTING);}} else if (!strcmp(argv[i], "-scale")) {lightscale = atof(argv[i + 1]);i++;} else if (!strcmp(argv[i], "-sunradscale")) {sunradscale = atof(argv[i + 1]);if (sunradscale < 0) {sunradscale = 0;printf("sunradscale set to minimum: 0\n");}printf("sunradscale = %f\n", sunradscale);i++;} else if (!strcmp(argv[i], "-saturation")) {saturation = atof(argv[i + 1]);i++;} else if (!strcmp(argv[i], "-radmin")) {patch_cutoff = atof(argv[i + 1]);i++;} else if (!strcmp(argv[i], "-direct")) {direct_scale *= atof(argv[i + 1]);// printf ("direct light scaling at %f\n", direct_scale);i++;} else if (!strcmp(argv[i], "-entity")) {entity_scale *= atof(argv[i + 1]);// printf ("entity light scaling at %f\n", entity_scale);i++;} else if (!strcmp(argv[i], "-nopvs")) {nopvs = true;printf("nopvs = true\n");} else if (!strcmp(argv[i], "-noblock")) {noblock = true;printf("noblock = true\n");} else if (!strcmp(argv[i], "-smooth")) {// qb: limit rangesmoothing_value = BOUND(0, atof(argv[i + 1]), 90);i++;} else if (!strcmp(argv[i], "-nudge")) {sample_nudge = atof(argv[i + 1]);// qb: nah, go crazy. sample_nudge = BOUND(0, sample_nudge, 1.0);i++;} else if (!strcmp(argv[i], "-ambient")) {ambient = BOUND(0, atof(argv[i + 1]), 255);i++;} else if (!strcmp(argv[i], "-savetrace")) {save_trace = true;printf("savetrace = true\n");} else if (!strcmp(argv[i], "-maxlight")) {maxlight = BOUND(0, atof(argv[i + 1]), 255);} elsebreak;}for(; i < argc; i++) {size_t input_length = strlen(argv[i]);qboolean is_map = strcmp(argv[i] + input_length - 4, ".map") == 0;if(do_bsp) {if(!is_map)Error("bsp operation requires a map file input.");} else if(do_vis || do_rad) {if(is_map)Error("bsp operation requires a bsp file input.");} else {Error("no operations chosen to be performed on input.");}SetQdirFromPath(argv[i]);if (strcmp(tmoddir, "")) {strcpy(moddir, tmoddir);Q_pathslash(moddir);strcpy(basedir, moddir);}if (strcmp(tbasedir, "")) {strcpy(basedir, tbasedir);Q_pathslash(basedir);if (!strcmp(tmoddir, ""))strcpy(moddir, basedir);}if (strcmp(tgamedir, "")) {strcpy(gamedir, tgamedir);Q_pathslash(gamedir);}// qb: display dirsprintf("moddir = %s\n", moddir);printf("basedir = %s\n", basedir);printf("gamedir = %s\n", gamedir);if(do_bsp) {int32_t old_numthreads = numthreads;// qb: below is from original source release. On Windows, multi threads cause false leak errors.numthreads = 1; // multiple threads aren't helping...BSP_ProcessArgument(argv[i]);numthreads = old_numthreads;}if(do_vis || (do_bsp && do_rad && is_map)) {VIS_ProcessArgument(argv[i]);}if(do_rad) {RAD_ProcessArgument(argv[i]);}}}
/*===========================================================================Copyright (C) 1997-2006 Id Software, Inc.This program is free software; you can redistribute it and/or modifyit under the terms of the GNU General Public License as published bythe 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 ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See theGNU General Public License for more details.You should have received a copy of the GNU General Public License alongwith this program; if not, write to the Free Software Foundation, Inc.,51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.===========================================================================*///// trilib.c: library for loading triangles from an Alias triangle file//#include <stdio.h>#include "cmdlib.h"#include "mathlib.h"#include "4data.h"// on disk representation of a face#define FLOAT_START BOGUS_RANGE#define FLOAT_END -FLOAT_START#define MAGIC 123322//#define NOISY 1typedef struct {float v[3];} vector;typedef struct{vector n; /* normal */vector p; /* point */vector c; /* color */float u; /* u */float v; /* v */} aliaspoint_t;typedef struct {aliaspoint_t pt[3];} tf_triangle;void ByteSwapTri(tf_triangle *tri) {int32_t i;for (i = 0; i < sizeof(tf_triangle) / 4; i++) {((int32_t *)tri)[i] = BigLong(((int32_t *)tri)[i]);}}void LoadTriangleList(char *filename, triangle_t **pptri, int32_t *numtriangles) {FILE *input;float start;char name[256], tex[256];int32_t i, count, magic;tf_triangle tri;triangle_t *ptri;int32_t iLevel;int32_t exitpattern;float t;t = -FLOAT_START;*((uint8_t *)&exitpattern + 0) = *((uint8_t *)&t + 3);*((uint8_t *)&exitpattern + 1) = *((uint8_t *)&t + 2);*((uint8_t *)&exitpattern + 2) = *((uint8_t *)&t + 1);*((uint8_t *)&exitpattern + 3) = *((uint8_t *)&t + 0);if ((input = fopen(filename, "rb")) == 0)Error("reader: could not open file '%s'", filename);iLevel = 0;fread(&magic, sizeof(int32_t), 1, input);if (BigLong(magic) != MAGIC)Error("%s is not a Alias object separated triangle file, magic number is wrong.", filename);ptri = malloc(MAXTRIANGLES * sizeof(triangle_t));*pptri = ptri;while (feof(input) == 0) {if (fread(&start, sizeof(float), 1, input) < 1)break;*(int32_t *)&start = BigLong(*(int32_t *)&start);if (*(int32_t *)&start != exitpattern) {if (start == FLOAT_START) {/* Start of an object or group of objects. */i = -1;do {/* There are probably better ways to read a string from *//* a file, but this does allow you to do error checking *//* (which I'm not doing) on a per character basis. */++i;fread(&(name[i]), sizeof(char), 1, input);} while (name[i] != '\0');// indent();// fprintf(stdout,"OBJECT START: %s\n",name);fread(&count, sizeof(int32_t), 1, input);count = BigLong(count);++iLevel;if (count != 0) {// indent();// fprintf(stdout,"NUMBER OF TRIANGLES: %d\n",count);i = -1;do {++i;fread(&(tex[i]), sizeof(char), 1, input);} while (tex[i] != '\0');// indent();// fprintf(stdout," Object texture name: '%s'\n",tex);}/* Else (count == 0) this is the start of a group, and *//* no texture name is present. */} else if (start == FLOAT_END) {/* End of an object or group. Yes, the name should be *//* obvious from context, but it is in here just to be *//* safe and to provide a little extra information for *//* those who do not wish to write a recursive reader. *//* Mia culpa. */--iLevel;i = -1;do {++i;fread(&(name[i]), sizeof(char), 1, input);} while (name[i] != '\0');// indent();// fprintf(stdout,"OBJECT END: %s\n",name);continue;}}//// read the triangles//for (i = 0; i < count; ++i) {int32_t j;fread(&tri, sizeof(tf_triangle), 1, input);ByteSwapTri(&tri);for (j = 0; j < 3; j++) {int32_t k;for (k = 0; k < 3; k++) {ptri->verts[j][k] = tri.pt[j].p.v[k];}}ptri++;if ((ptri - *pptri) >= MAXTRIANGLES)Error("Error: too many triangles; increase MAXTRIANGLES\n");}}*numtriangles = ptri - *pptri;fclose(input);free(ptri); // qb: stop mem leak}
/*===========================================================================Copyright (C) 1997-2006 Id Software, Inc.This program is free software; you can redistribute it and/or modifyit under the terms of the GNU General Public License as published bythe 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 ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See theGNU General Public License for more details.You should have received a copy of the GNU General Public License alongwith this program; if not, write to the Free Software Foundation, Inc.,51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.===========================================================================*/#include "cmdlib.h"#include "threads.h"#define MAX_THREADS 64int32_t dispatch;int32_t workcount;int32_t oldf;qboolean pacifier;qboolean threaded;/*=============GetThreadWork=============*/int32_t GetThreadWork(void) {int32_t r;int32_t f;ThreadLock();if (dispatch == workcount) {ThreadUnlock();return -1;}f = 10 * dispatch / workcount;if (f != oldf) {oldf = f;if (pacifier) {printf("%i...", f);fflush(stdout);}}r = dispatch;dispatch++;ThreadUnlock();return r;}void (*workfunction)(int32_t);void ThreadWorkerFunction(int32_t threadnum) {int32_t work;while (1) {work = GetThreadWork();if (work == -1)break;// printf ("thread %i, work %i\n", threadnum, work);workfunction(work);}}void RunThreadsOnIndividual(int32_t workcnt, qboolean showpacifier, void (*func)(int32_t)) {if (numthreads == -1)ThreadSetDefault();workfunction = func;RunThreadsOn(workcnt, showpacifier, ThreadWorkerFunction);}#ifdef USE_PTHREADS#ifdef WIN32#define USED#include <windows.h>int32_t numthreads = -1;CRITICAL_SECTION crit;static int32_t enter;void ThreadSetDefault(void) {SYSTEM_INFO info;if (numthreads == -1) // not set manually{GetSystemInfo(&info);numthreads = info.dwNumberOfProcessors;if (numthreads < 1 || numthreads > 32)numthreads = 1;}qprintf("%i threads\n", numthreads);}void ThreadLock(void) {if (!threaded)return;EnterCriticalSection(&crit);if (enter)Error("Recursive ThreadLock\n");enter = 1;}void ThreadUnlock(void) {if (!threaded)return;if (!enter)Error("ThreadUnlock without lock\n");enter = 0;LeaveCriticalSection(&crit);}/*=============RunThreadsOn=============*/void RunThreadsOn(int32_t workcnt, qboolean showpacifier, void (*func)(int32_t)) {int32_t threadid[MAX_THREADS];HANDLE threadhandle[MAX_THREADS];int32_t i;int32_t start, end;start = I_FloatTime();dispatch = 0;workcount = workcnt;oldf = -1;pacifier = showpacifier;threaded = true;//// run threads in parallel//InitializeCriticalSection(&crit);if (numthreads == 1) { // use same threadfunc(0);} else {for (i = 0; i < numthreads; i++) {threadhandle[i] = CreateThread(NULL, // LPSECURITY_ATTRIBUTES lpsa,0, // DWORD cbStack,(LPTHREAD_START_ROUTINE)func, // LPTHREAD_START_ROUTINE lpStartAddr,(LPVOID)i, // LPVOID lpvThreadParm,0, // DWORD fdwCreate,&threadid[i]);}for (i = 0; i < numthreads; i++)WaitForSingleObject(threadhandle[i], INFINITE);}DeleteCriticalSection(&crit);threaded = false;end = I_FloatTime();if (pacifier)printf(" (%i)\n", end - start);}#else#define USEDint32_t numthreads = 4;void ThreadSetDefault(void) {if (numthreads == -1) // not set manually{numthreads = 4;}}#include <pthread.h>pthread_mutex_t *my_mutex;void ThreadLock(void) {if (my_mutex)pthread_mutex_lock(my_mutex);}void ThreadUnlock(void) {if (my_mutex)pthread_mutex_unlock(my_mutex);}/*=============RunThreadsOn=============*/void RunThreadsOn(int32_t workcnt, qboolean showpacifier, void (*func)(int32_t)) {int32_t i;pthread_t work_threads[MAX_THREADS];void *status;pthread_attr_t attrib;pthread_mutexattr_t mattrib;int32_t start, end;start = I_FloatTime();dispatch = 0;workcount = workcnt;oldf = -1;pacifier = showpacifier;threaded = true;if (pacifier)setbuf(stdout, NULL);if (!my_mutex) {my_mutex = malloc(sizeof(*my_mutex));if (pthread_mutexattr_init(&mattrib) == -1)Error("pthread_mutex_attr_create failed");if (pthread_mutex_init(my_mutex, &mattrib) == -1)Error("pthread_mutex_init failed");}if (pthread_attr_init(&attrib) == -1)Error("pthread_attr_create failed");if (pthread_attr_setstacksize(&attrib, 0x1000000) == -1)Error("pthread_attr_setstacksize failed");for (i = 0; i < numthreads; i++) {if (pthread_create(&work_threads[i], &attrib, (void *)func, &i) == -1)Error("pthread_create failed");}for (i = 0; i < numthreads; i++) {if (pthread_join(work_threads[i], &status) == -1)Error("pthread_join failed");}threaded = false;end = I_FloatTime();if (pacifier)printf(" (%i)\n", end - start);}#endif#endif/*=======================================================================SINGLE THREAD=======================================================================*/#ifndef USEDint32_t numthreads = 1;void ThreadSetDefault(void) {numthreads = 1;}void ThreadLock(void) {}void ThreadUnlock(void) {}/*=============RunThreadsOn=============*/void RunThreadsOn(int32_t workcnt, qboolean showpacifier, void (*func)(int32_t)) {int32_t start, end;dispatch = 0;workcount = workcnt;oldf = -1;pacifier = showpacifier;start = I_FloatTime();#ifdef NeXTif (pacifier)setbuf(stdout, NULL);#endiffunc(0);end = I_FloatTime();if (pacifier)printf(" (%i)\n", end - start);}#endif
/*===========================================================================Copyright (C) 1997-2006 Id Software, Inc.This program is free software; you can redistribute it and/or modifyit under the terms of the GNU General Public License as published bythe 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 ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See theGNU General Public License for more details.You should have received a copy of the GNU General Public License alongwith this program; if not, write to the Free Software Foundation, Inc.,51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.===========================================================================*/// scriplib.c#include "cmdlib.h"#include "scriplib.h"/*=============================================================================PARSING STUFF=============================================================================*/typedef struct{char filename[1024];char *buffer, *script_p, *end_p;int32_t line;} script_t;#define MAX_INCLUDES 8script_t scriptstack[MAX_INCLUDES];script_t *script;int32_t scriptline;char token[MAXTOKEN];qboolean endofscript;qboolean tokenready; // only true if UnGetToken was just called// qb: brush info from AA toolschar brush_info[2000] = "No brushes processed yet. Look near beginning of map";static int32_t brush_begin = 1;void MarkBrushBegin() {brush_begin = 1;}void TestBrushBegin() {if (!brush_begin)return;brush_begin = 0;sprintf(brush_info, "Line %d, file %s", script->line, script->filename);}/*==============AddScriptToStack==============*/void AddScriptToStack(char *filename) {int32_t size;script++;if (script == &scriptstack[MAX_INCLUDES])Error("script file exceeded MAX_INCLUDES");strcpy(script->filename, ExpandPath(filename));size = LoadFile(script->filename, (void **)&script->buffer);printf("entering %s\n", script->filename);script->line = 1;script->script_p = script->buffer;script->end_p = script->buffer + size;}/*==============LoadScriptFile==============*/void LoadScriptFile(char *filename) {script = scriptstack;AddScriptToStack(filename);endofscript = false;tokenready = false;}/*==============ParseFromMemory==============*/void ParseFromMemory(char *buffer, int32_t size) {script = scriptstack;script++;if (script == &scriptstack[MAX_INCLUDES])Error("script file exceeded MAX_INCLUDES");strcpy(script->filename, "memory buffer");script->buffer = buffer;script->line = 1;script->script_p = script->buffer;script->end_p = script->buffer + size;endofscript = false;tokenready = false;}/*==============UnGetTokenSignals that the current token was not used, and should be reportedfor the next GetToken. Note thatGetToken (true);UnGetToken ();GetToken (false);could cross a line boundary.==============*/void UnGetToken(void) {tokenready = true;}qboolean EndOfScript(qboolean crossline) {if (!crossline)Error("Line %i is incomplete\n", scriptline);if (!strcmp(script->filename, "memory buffer")) {endofscript = true;return false;}free(script->buffer);if (script == scriptstack + 1) {endofscript = true;return false;}script--;scriptline = script->line;printf("returning to %s\n", script->filename);return GetToken(crossline);}/*==============GetToken==============*/qboolean GetToken(qboolean crossline) {char *token_p;if (tokenready) // is a token allready waiting?{tokenready = false;return true;}if (script->script_p >= script->end_p)return EndOfScript(crossline);TestBrushBegin();//// skip space//skipspace:while (*script->script_p <= 32) {if (script->script_p >= script->end_p)return EndOfScript(crossline);if (*script->script_p++ == '\n') {if (!crossline)Error("Line %i is incomplete\n", scriptline);scriptline = script->line++;}}if (script->script_p >= script->end_p)return EndOfScript(crossline);// ; # // commentsif (*script->script_p == ';' || *script->script_p == '#' || (script->script_p[0] == '/' && script->script_p[1] == '/')) {if (!crossline)Error("Line %i is incomplete\n", scriptline);while (*script->script_p++ != '\n')if (script->script_p >= script->end_p)return EndOfScript(crossline);scriptline = script->line++;goto skipspace;}// /* */ commentsif (script->script_p[0] == '/' && script->script_p[1] == '*') {if (!crossline)Error("Line %i is incomplete\n", scriptline);script->script_p += 2;while (script->script_p[0] != '*' && script->script_p[1] != '/') {if (script->script_p[0] == '\n')scriptline = script->line++;script->script_p++;if (script->script_p >= script->end_p)return EndOfScript(crossline);}script->script_p += 2;goto skipspace;}//// copy token//token_p = token;if (*script->script_p == '"') {// quoted tokenscript->script_p++;while (*script->script_p != '"') {*token_p++ = *script->script_p++;if (script->script_p == script->end_p)break;if (token_p == &token[MAXTOKEN])Error("Token too large on line %i\n", scriptline);}script->script_p++;} else // regular tokenwhile (*script->script_p > 32 && *script->script_p != ';') {*token_p++ = *script->script_p++;if (script->script_p == script->end_p)break;if (token_p == &token[MAXTOKEN])Error("Token too large on line %i\n", scriptline);}*token_p = 0;if (!strcmp(token, "$include")) {GetToken(false);AddScriptToStack(token);return GetToken(crossline);}return true;}/*==============TokenAvailableReturns true if there is another token on the line==============*/qboolean TokenAvailable(void) {char *search_p;search_p = script->script_p;if (search_p >= script->end_p)return false;while (*search_p <= 32) {if (*search_p == '\n')return false;search_p++;if (search_p == script->end_p)return false;}if (*search_p == ';')return false;return true;}
/*===========================================================================Copyright (C) 1997-2006 Id Software, Inc.This program is free software; you can redistribute it and/or modifyit under the terms of the GNU General Public License as published bythe 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 ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See theGNU General Public License for more details.You should have received a copy of the GNU General Public License alongwith this program; if not, write to the Free Software Foundation, Inc.,51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.===========================================================================*/#include "cmdlib.h"#include "mathlib.h"#include "polylib.h"extern int32_t numthreads;// counters are only bumped when running single threaded,// because they are an awefull coherence problemint32_t c_active_windings;int32_t c_peak_windings;int32_t c_winding_allocs;int32_t c_winding_points;void pw(winding_t *w) {int32_t i;for (i = 0; i < w->numpoints; i++)printf("(%5.1f, %5.1f, %5.1f)\n", w->p[i][0], w->p[i][1], w->p[i][2]);}/*=============AllocWinding=============*/winding_t *AllocWinding(int32_t points) {winding_t *w;int32_t s;if (numthreads == 1) {c_winding_allocs++;c_winding_points += points;c_active_windings++;if (c_active_windings > c_peak_windings)c_peak_windings = c_active_windings;}s = sizeof(vec_t) * 3 * points + sizeof(int32_t);w = malloc(s);memset(w, 0, s);return w;}void FreeWinding(winding_t *w) {if (*(unsigned *)w == 0xdeaddead)Error("FreeWinding: freed a freed winding");*(unsigned *)w = 0xdeaddead;if (numthreads == 1)c_active_windings--;free(w);}/*============RemoveColinearPoints============*/int32_t c_removed;void RemoveColinearPoints(winding_t *w) {int32_t i, j, k;vec3_t v1, v2;int32_t nump;vec3_t p[MAX_POINTS_ON_WINDING];nump = 0;for (i = 0; i < w->numpoints; i++) {j = (i + 1) % w->numpoints;k = (i + w->numpoints - 1) % w->numpoints;VectorSubtract(w->p[j], w->p[i], v1);VectorSubtract(w->p[i], w->p[k], v2);VectorNormalize(v1, v1);VectorNormalize(v2, v2);if (DotProduct(v1, v2) < 0.999) {VectorCopy(w->p[i], p[nump]);nump++;}}if (nump == w->numpoints)return;if (numthreads == 1)c_removed += w->numpoints - nump;w->numpoints = nump;memcpy(w->p, p, nump * sizeof(p[0]));}/*============WindingPlane============*/void WindingPlane(winding_t *w, vec3_t normal, vec_t *dist) {vec3_t v1, v2;VectorSubtract(w->p[1], w->p[0], v1);VectorSubtract(w->p[2], w->p[0], v2);CrossProduct(v2, v1, normal);VectorNormalize(normal, normal);*dist = DotProduct(w->p[0], normal);}/*=============WindingArea=============*/vec_t WindingArea(winding_t *w) {int32_t i;vec3_t d1, d2, cross;vec_t total;total = 0;for (i = 2; i < w->numpoints; i++) {VectorSubtract(w->p[i - 1], w->p[0], d1);VectorSubtract(w->p[i], w->p[0], d2);CrossProduct(d1, d2, cross);total += 0.5 * VectorLength(cross);}return total;}void WindingBounds(winding_t *w, vec3_t mins, vec3_t maxs) {vec_t v;int32_t i, j;mins[0] = mins[1] = mins[2] = BOGUS_RANGE;maxs[0] = maxs[1] = maxs[2] = -BOGUS_RANGE;for (i = 0; i < w->numpoints; i++) {for (j = 0; j < 3; j++) {v = w->p[i][j];if (v < mins[j])mins[j] = v;if (v > maxs[j])maxs[j] = v;}}}/*=============WindingCenter=============*/void WindingCenter(winding_t *w, vec3_t center) {int32_t i;float scale;VectorCopy(vec3_origin, center);for (i = 0; i < w->numpoints; i++)VectorAdd(w->p[i], center, center);scale = 1.0 / w->numpoints;VectorScale(center, scale, center);}/*=================BaseWindingForPlane=================*/winding_t *BaseWindingForPlane(vec3_t normal, vec_t dist) {int32_t i, x;vec_t max, v;vec3_t org, vright, vup;winding_t *w;// find the major axismax = -BOGUS_RANGE;x = -1;for (i = 0; i < 3; i++) {v = fabs(normal[i]);if (v > max) {x = i;max = v;}}if (x == -1)Error("BaseWindingForPlane: no axis found");VectorCopy(vec3_origin, vup);switch (x) {case 0:case 1:vup[2] = 1;break;case 2:vup[0] = 1;break;}v = DotProduct(vup, normal);VectorMA(vup, -v, normal, vup);VectorNormalize(vup, vup);VectorScale(normal, dist, org);CrossProduct(vup, normal, vright);VectorScale(vup, BOGUS_RANGE, vup);VectorScale(vright, BOGUS_RANGE, vright);// project a really big axis aligned box onto the planew = AllocWinding(4);VectorSubtract(org, vright, w->p[0]);VectorAdd(w->p[0], vup, w->p[0]);VectorAdd(org, vright, w->p[1]);VectorAdd(w->p[1], vup, w->p[1]);VectorAdd(org, vright, w->p[2]);VectorSubtract(w->p[2], vup, w->p[2]);VectorSubtract(org, vright, w->p[3]);VectorSubtract(w->p[3], vup, w->p[3]);w->numpoints = 4;return w;}/*==================CopyWinding==================*/winding_t *CopyWinding(const winding_t *w) {int32_t size;winding_t *c;c = AllocWinding(w->numpoints);size = (intptr_t)((winding_t *)0)->p[w->numpoints];memcpy(c, w, size);return c;}/*==================ReverseWinding==================*/winding_t *ReverseWinding(winding_t *w) {int32_t i;winding_t *c;c = AllocWinding(w->numpoints);for (i = 0; i < w->numpoints; i++) {VectorCopy(w->p[w->numpoints - 1 - i], c->p[i]);}c->numpoints = w->numpoints;return c;}/*=============ClipWindingEpsilon=============*/void ClipWindingEpsilon(const winding_t *in,const vec3_t normal,const vec_t dist,const vec_t epsilon,winding_t **front, winding_t **back) {vec_t dists[MAX_POINTS_ON_WINDING + 4];int32_t sides[MAX_POINTS_ON_WINDING + 4];int32_t counts[3];static vec_t dot; // VC 4.2 optimizer bug if not staticint32_t i, j;vec_t *p1, *p2;vec3_t mid;winding_t *f, *b;int32_t maxpts;counts[0] = counts[1] = counts[2] = 0;sides[0] = dists[0] = 0;// determine sides for each pointfor (i = 0; i < in->numpoints; i++) {dot = DotProduct(in->p[i], normal);dot -= dist;dists[i] = dot;if (dot > epsilon)sides[i] = SIDE_FRONT;else if (dot < -epsilon)sides[i] = SIDE_BACK;elsesides[i] = SIDE_ON;counts[sides[i]]++;}sides[i] = sides[0];dists[i] = dists[0];*front = *back = NULL;if (!counts[0]) {*back = CopyWinding(in);return;}if (!counts[1]) {*front = CopyWinding(in);return;}maxpts = in->numpoints + 4; // cant use counts[0]+2 because of fp grouping errors*front = f = AllocWinding(maxpts);*back = b = AllocWinding(maxpts);for (i = 0; i < in->numpoints; i++) {p1 = ((winding_t *)in)->p[i];if (sides[i] == SIDE_ON) {VectorCopy(p1, f->p[f->numpoints]);f->numpoints++;VectorCopy(p1, b->p[b->numpoints]);b->numpoints++;continue;}if (sides[i] == SIDE_FRONT) {VectorCopy(p1, f->p[f->numpoints]);f->numpoints++;}if (sides[i] == SIDE_BACK) {VectorCopy(p1, b->p[b->numpoints]);b->numpoints++;}if (sides[i + 1] == SIDE_ON || sides[i + 1] == sides[i])continue;// generate a split pointp2 = ((winding_t *)in)->p[(i + 1) % in->numpoints];dot = dists[i] / (dists[i] - dists[i + 1]);for (j = 0; j < 3; j++) {// avoid round off error when possibleif (normal[j] == 1)mid[j] = dist;else if (normal[j] == -1)mid[j] = -dist;elsemid[j] = p1[j] + dot * (p2[j] - p1[j]);}VectorCopy(mid, f->p[f->numpoints]);f->numpoints++;VectorCopy(mid, b->p[b->numpoints]);b->numpoints++;}if (f->numpoints > maxpts || b->numpoints > maxpts)Error("ClipWinding: points exceeded estimate");if (f->numpoints > MAX_POINTS_ON_WINDING || b->numpoints > MAX_POINTS_ON_WINDING)Error("ClipWinding: MAX_POINTS_ON_WINDING");}/*=============ChopWindingInPlace=============*/void ChopWindingInPlace(winding_t **inout, vec3_t normal, vec_t dist, vec_t epsilon) {winding_t *in;vec_t dists[MAX_POINTS_ON_WINDING + 4];int32_t sides[MAX_POINTS_ON_WINDING + 4];int32_t counts[3];static vec_t dot; // VC 4.2 optimizer bug if not staticint32_t i, j;vec_t *p1, *p2;vec3_t mid;winding_t *f;int32_t maxpts;in = *inout;counts[0] = counts[1] = counts[2] = 0;// determine sides for each pointfor (i = 0; i < in->numpoints; i++) {dot = DotProduct(in->p[i], normal);dot -= dist;dists[i] = dot;if (dot > epsilon)sides[i] = SIDE_FRONT;else if (dot < -epsilon)sides[i] = SIDE_BACK;else {sides[i] = SIDE_ON;}counts[sides[i]]++;}sides[i] = sides[0];dists[i] = dists[0];if (!counts[0]) {FreeWinding(in);*inout = NULL;return;}if (!counts[1])return; // inout stays the samemaxpts = in->numpoints + 4; // can't use counts[0]+2 because of fp grouping errorsf = AllocWinding(maxpts);for (i = 0; i < in->numpoints; i++) {p1 = in->p[i];if (sides[i] == SIDE_ON) {VectorCopy(p1, f->p[f->numpoints]);f->numpoints++;continue;}if (sides[i] == SIDE_FRONT) {VectorCopy(p1, f->p[f->numpoints]);f->numpoints++;}if (sides[i + 1] == SIDE_ON || sides[i + 1] == sides[i])continue;// generate a split pointp2 = in->p[(i + 1) % in->numpoints];dot = dists[i] / (dists[i] - dists[i + 1]);for (j = 0; j < 3; j++) {// avoid round off error when possibleif (normal[j] >= 1)mid[j] = dist;else if (normal[j] <= -1)mid[j] = -dist;elsemid[j] = p1[j] + dot * (p2[j] - p1[j]);}VectorCopy(mid, f->p[f->numpoints]);f->numpoints++;}if (f->numpoints > maxpts)Error("ClipWinding: points exceeded estimate");if (f->numpoints > MAX_POINTS_ON_WINDING)Error("ClipWinding: MAX_POINTS_ON_WINDING");FreeWinding(in);*inout = f;}/*=================ChopWindingReturns the fragment of in that is on the front sideof the cliping plane. The original is freed.=================*/winding_t *ChopWinding(winding_t *in, vec3_t normal, vec_t dist) {winding_t *f, *b;ClipWindingEpsilon(in, normal, dist, ON_EPSILON, &f, &b);FreeWinding(in);if (b)FreeWinding(b);return f;}/*=================CheckWinding=================*/void CheckWinding(winding_t *w) {int32_t i, j;vec_t *p1, *p2;vec_t d, edgedist;vec3_t dir, edgenormal, facenormal;vec_t area;vec_t facedist;if (w->numpoints < 3)Error("CheckWinding: %i points", w->numpoints);area = WindingArea(w);if (area < 1)Error("CheckWinding: %f area", area);WindingPlane(w, facenormal, &facedist);for (i = 0; i < w->numpoints; i++) {p1 = w->p[i];for (j = 0; j < 3; j++)if (p1[j] > BOGUS_RANGE || p1[j] < -BOGUS_RANGE)Error("CheckFace: BUGUS_RANGE: %f", p1[j]);j = i + 1 == w->numpoints ? 0 : i + 1;// check the point is on the face planed = DotProduct(p1, facenormal) - facedist;if (d < -ON_EPSILON || d > ON_EPSILON)Error("CheckWinding: point off plane");// check the edge isnt degeneratep2 = w->p[j];VectorSubtract(p2, p1, dir);if (VectorLength(dir) < ON_EPSILON)Error("CheckWinding: degenerate edge");CrossProduct(facenormal, dir, edgenormal);VectorNormalize(edgenormal, edgenormal);edgedist = DotProduct(p1, edgenormal);edgedist += ON_EPSILON;// all other points must be on front sidefor (j = 0; j < w->numpoints; j++) {if (j == i)continue;d = DotProduct(w->p[j], edgenormal);if (d > edgedist)Error("CheckWinding: non-convex");}}}/*============WindingOnPlaneSide============*/int32_t WindingOnPlaneSide(winding_t *w, vec3_t normal, vec_t dist) {qboolean front, back;int32_t i;vec_t d;front = false;back = false;for (i = 0; i < w->numpoints; i++) {d = DotProduct(w->p[i], normal) - dist;if (d < -ON_EPSILON) {if (front)return SIDE_CROSS;back = true;continue;}if (d > ON_EPSILON) {if (back)return SIDE_CROSS;front = true;continue;}}if (back)return SIDE_BACK;if (front)return SIDE_FRONT;return SIDE_ON;}
/*mdfour.cAn implementation of MD4 designed for use in the samba SMBauthentication protocolCopyright (C) 1997-1998 Andrew TridgellThis program is free software; you can redistribute it and/ormodify it under the terms of the GNU General Public Licenseas published by the Free Software Foundation; either version 2of 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 ofMERCHANTABILITY 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 Licensealong with this program; if not, write to:Free Software Foundation, Inc.59 Temple Place - Suite 330Boston, MA 02111-1307, USA*/#include <stdint.h>#include <string.h> /* XoXus: needed for memset call */#include "mdfour.h"/* NOTE: This code makes no attempt to be fast!It assumes that a int32_t is at least 32 bits long*/static struct mdfour *m;#define F(X, Y, Z) (((X) & (Y)) | ((~(X)) & (Z)))#define G(X, Y, Z) (((X) & (Y)) | ((X) & (Z)) | ((Y) & (Z)))#define H(X, Y, Z) ((X) ^ (Y) ^ (Z))#ifdef LARGE_INT32#define lshift(x, s) ((((x) << (s)) & 0xFFFFFFFF) | (((x) >> (32 - (s))) & 0xFFFFFFFF))#else#define lshift(x, s) (((x) << (s)) | ((x) >> (32 - (s))))#endif#define ROUND1(a, b, c, d, k, s) a = lshift(a + F(b, c, d) + X[k], s)#define ROUND2(a, b, c, d, k, s) a = lshift(a + G(b, c, d) + X[k] + 0x5A827999, s)#define ROUND3(a, b, c, d, k, s) a = lshift(a + H(b, c, d) + X[k] + 0x6ED9EBA1, s)/* this applies md4 to 64 byte chunks */static void mdfour64(uint32_t *M) {int32_t j;uint32_t AA, BB, CC, DD;uint32_t X[16];uint32_t A, B, C, D;for (j = 0; j < 16; j++)X[j] = M[j];A = m->A;B = m->B;C = m->C;D = m->D;AA = A;BB = B;CC = C;DD = D;ROUND1(A, B, C, D, 0, 3);ROUND1(D, A, B, C, 1, 7);ROUND1(C, D, A, B, 2, 11);ROUND1(B, C, D, A, 3, 19);ROUND1(A, B, C, D, 4, 3);ROUND1(D, A, B, C, 5, 7);ROUND1(C, D, A, B, 6, 11);ROUND1(B, C, D, A, 7, 19);ROUND1(A, B, C, D, 8, 3);ROUND1(D, A, B, C, 9, 7);ROUND1(C, D, A, B, 10, 11);ROUND1(B, C, D, A, 11, 19);ROUND1(A, B, C, D, 12, 3);ROUND1(D, A, B, C, 13, 7);ROUND1(C, D, A, B, 14, 11);ROUND1(B, C, D, A, 15, 19);ROUND2(A, B, C, D, 0, 3);ROUND2(D, A, B, C, 4, 5);ROUND2(C, D, A, B, 8, 9);ROUND2(B, C, D, A, 12, 13);ROUND2(A, B, C, D, 1, 3);ROUND2(D, A, B, C, 5, 5);ROUND2(C, D, A, B, 9, 9);ROUND2(B, C, D, A, 13, 13);ROUND2(A, B, C, D, 2, 3);ROUND2(D, A, B, C, 6, 5);ROUND2(C, D, A, B, 10, 9);ROUND2(B, C, D, A, 14, 13);ROUND2(A, B, C, D, 3, 3);ROUND2(D, A, B, C, 7, 5);ROUND2(C, D, A, B, 11, 9);ROUND2(B, C, D, A, 15, 13);ROUND3(A, B, C, D, 0, 3);ROUND3(D, A, B, C, 8, 9);ROUND3(C, D, A, B, 4, 11);ROUND3(B, C, D, A, 12, 15);ROUND3(A, B, C, D, 2, 3);ROUND3(D, A, B, C, 10, 9);ROUND3(C, D, A, B, 6, 11);ROUND3(B, C, D, A, 14, 15);ROUND3(A, B, C, D, 1, 3);ROUND3(D, A, B, C, 9, 9);ROUND3(C, D, A, B, 5, 11);ROUND3(B, C, D, A, 13, 15);ROUND3(A, B, C, D, 3, 3);ROUND3(D, A, B, C, 11, 9);ROUND3(C, D, A, B, 7, 11);ROUND3(B, C, D, A, 15, 15);A += AA;B += BB;C += CC;D += DD;#ifdef LARGE_INT32A &= 0xFFFFFFFF;B &= 0xFFFFFFFF;C &= 0xFFFFFFFF;D &= 0xFFFFFFFF;#endiffor (j = 0; j < 16; j++)X[j] = 0;m->A = A;m->B = B;m->C = C;m->D = D;}static void copy64(uint32_t *M, uint8_t *in) {int32_t i;for (i = 0; i < 16; i++)M[i] = (in[i * 4 + 3] << 24) | (in[i * 4 + 2] << 16) |(in[i * 4 + 1] << 8) | (in[i * 4 + 0] << 0);}static void copy4(uint8_t *out, uint32_t x) {out[0] = x & 0xFF;out[1] = (x >> 8) & 0xFF;out[2] = (x >> 16) & 0xFF;out[3] = (x >> 24) & 0xFF;}void mdfour_begin(struct mdfour *md) {md->A = 0x67452301;md->B = 0xefcdab89;md->C = 0x98badcfe;md->D = 0x10325476;md->totalN = 0;}static void mdfour_tail(uint8_t *in, int32_t n) {uint8_t buf[128];uint32_t M[16];uint32_t b;m->totalN += n;b = m->totalN * 8;memset(buf, 0, 128);if (n)memcpy(buf, in, n);buf[n] = 0x80;if (n <= 55) {copy4(buf + 56, b);copy64(M, buf);mdfour64(M);} else {copy4(buf + 120, b);copy64(M, buf);mdfour64(M);copy64(M, buf + 64);mdfour64(M);}}void mdfour_update(struct mdfour *md, uint8_t *in, int32_t n) {uint32_t M[16];if (n == 0)mdfour_tail(in, n);m = md;while (n >= 64) {copy64(M, in);mdfour64(M);in += 64;n -= 64;m->totalN += 64;}mdfour_tail(in, n);}void mdfour_result(struct mdfour *md, uint8_t *out) {m = md;copy4(out, m->A);copy4(out + 4, m->B);copy4(out + 8, m->C);copy4(out + 12, m->D);}void mdfour(uint8_t *out, uint8_t *in, int32_t n) {struct mdfour md;mdfour_begin(&md);mdfour_update(&md, in, n);mdfour_result(&md, out);}///////////////////////////////////////////////////////////////// MD4-based checksum utility functions//// Copyright (C) 2000 Jeff Teunissen <d2deek@pmail.net>//// Author: Jeff Teunissen <d2deek@pmail.net>// Date: 01 Jan 2000unsigned Com_BlockChecksum(void *buffer, int32_t length) {int32_t digest[4];unsigned val;mdfour((uint8_t *)digest, (uint8_t *)buffer, length);val = digest[0] ^ digest[1] ^ digest[2] ^ digest[3];return val;}void Com_BlockFullChecksum(void *buffer, int32_t len, uint8_t *outbuf) {mdfour(outbuf, (uint8_t *)buffer, len);}
/*===========================================================================Copyright (C) 1997-2006 Id Software, Inc.This program is free software; you can redistribute it and/or modifyit under the terms of the GNU General Public License as published bythe 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 ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See theGNU General Public License for more details.You should have received a copy of the GNU General Public License alongwith this program; if not, write to the Free Software Foundation, Inc.,51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.===========================================================================*/// mathlib.c -- math primitives#include "cmdlib.h"#include "mathlib.h"vec3_t vec3_origin = {0, 0, 0};// qb: inline math from AA tools#ifndef MATH_INLINEdouble VectorLength(vec3_t v) {int32_t i;double length;length = 0;for (i = 0; i < 3; i++)length += v[i] * v[i];length = sqrt(length); // FIXMEreturn length;}qboolean VectorCompare(vec3_t v1, vec3_t v2) {int32_t i;for (i = 0; i < 3; i++)if (fabs(v1[i] - v2[i]) > EQUAL_EPSILON)return false;return true;}vec_t Q_rint(vec_t in) {return floor(in + 0.5);}void VectorMA(vec3_t va, double scale, vec3_t vb, vec3_t vc) {vc[0] = va[0] + scale * vb[0];vc[1] = va[1] + scale * vb[1];vc[2] = va[2] + scale * vb[2];}void CrossProduct(vec3_t v1, vec3_t v2, vec3_t cross) {cross[0] = v1[1] * v2[2] - v1[2] * v2[1];cross[1] = v1[2] * v2[0] - v1[0] * v2[2];cross[2] = v1[0] * v2[1] - v1[1] * v2[0];}vec_t _DotProduct(vec3_t v1, vec3_t v2) {return v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2];}void _VectorSubtract(vec3_t va, vec3_t vb, vec3_t out) {out[0] = va[0] - vb[0];out[1] = va[1] - vb[1];out[2] = va[2] - vb[2];}void _VectorAdd(vec3_t va, vec3_t vb, vec3_t out) {out[0] = va[0] + vb[0];out[1] = va[1] + vb[1];out[2] = va[2] + vb[2];}void _VectorCopy(vec3_t in, vec3_t out) {out[0] = in[0];out[1] = in[1];out[2] = in[2];}void _VectorScale(vec3_t v, vec_t scale, vec3_t out) {out[0] = v[0] * scale;out[1] = v[1] * scale;out[2] = v[2] * scale;}vec_t VectorNormalize(vec3_t in, vec3_t out) {vec_t length, ilength;length = sqrt(in[0] * in[0] + in[1] * in[1] + in[2] * in[2]);if (length == 0) {VectorClear(out);return 0;}ilength = 1.0 / length;out[0] = in[0] * ilength;out[1] = in[1] * ilength;out[2] = in[2] * ilength;return length;}vec_t ColorNormalize(vec3_t in, vec3_t out) {float max, scale;max = in[0];if (in[1] > max)max = in[1];if (in[2] > max)max = in[2];if (max == 0)return 0;scale = 1.0 / max;VectorScale(in, scale, out);return max;}void VectorInverse(vec3_t v) {v[0] = -v[0];v[1] = -v[1];v[2] = -v[2];}void ClearBounds(vec3_t mins, vec3_t maxs) {mins[0] = mins[1] = mins[2] = BOGUS_RANGE;maxs[0] = maxs[1] = maxs[2] = -BOGUS_RANGE;}void AddPointToBounds(vec3_t v, vec3_t mins, vec3_t maxs) {int32_t i;vec_t val;for (i = 0; i < 3; i++) {val = v[i];if (val < mins[i])mins[i] = val;if (val > maxs[i])maxs[i] = val;}}// qb: from AA toolsqboolean RayPlaneIntersect(vec3_t p_n, vec_t p_d, vec3_t l_o, vec3_t l_n,vec3_t res) {float dot, t;dot = DotProduct(p_n, l_n);if (dot > -0.001)return false;t = (p_d - (l_o[0] * p_n[0]) - (l_o[1] * p_n[1]) - (l_o[2] * p_n[2])) / dot;res[0] = l_o[0] + (t * l_n[0]);res[1] = l_o[1] + (t * l_n[1]);res[2] = l_o[2] + (t * l_n[2]);return true;}#endif
//// llwolib.c: library for loading triangles from a Lightwave triangle file//#include <stdio.h>#include "cmdlib.h"#include "mathlib.h"#include "4data.h"#define MAXPOINTS 2000#define MAXPOLYS 2000struct {float p[3];} pnt[MAXPOINTS];struct {short pl[3];} ply[MAXPOLYS];char trashcan[10000];char buffer[1000];long counter;union {char c[4];float f;long l;} buffer4;union {char c[2];short i;} buffer2;FILE *lwo;short numpoints, numpolys;long lflip(long x) {union {char b[4];long l;} in, out;in.l = x;out.b[0] = in.b[3];out.b[1] = in.b[2];out.b[2] = in.b[1];out.b[3] = in.b[0];return out.l;}float fflip(float x) {union {char b[4];float l;} in, out;in.l = x;out.b[0] = in.b[3];out.b[1] = in.b[2];out.b[2] = in.b[1];out.b[3] = in.b[0];return out.l;}int sflip(short x) {union {char b[2];short i;} in, out;in.i = x;out.b[0] = in.b[1];out.b[1] = in.b[0];return out.i;}void skipchunk() {long chunksize;fread(buffer4.c, 4, 1, lwo);counter -= 8;chunksize = lflip(buffer4.l);fread(trashcan, chunksize, 1, lwo);counter -= chunksize;};void getpoints() {long chunksize;short i, j;fread(buffer4.c, 4, 1, lwo);counter -= 8;chunksize = lflip(buffer4.l);counter -= chunksize;numpoints = chunksize / 12;if (numpoints > MAXPOINTS) {fprintf(stderr, "reader: Too many points!!!");exit(0);}for (i = 0; i < numpoints; i++) {for (j = 0; j < 3; j++) {fread(buffer4.c, 4, 1, lwo);pnt[i].p[j] = fflip(buffer4.f);}}}void getpolys() {short temp, i, j;short polypoints;long chunksize;fread(buffer4.c, 4, 1, lwo);counter -= 8;chunksize = lflip(buffer4.l);counter -= chunksize;numpolys = 0;for (i = 0; i < chunksize;) {fread(buffer2.c, 2, 1, lwo);polypoints = sflip(buffer2.i);if (polypoints != 3) {fprintf(stderr, "reader: Not a triangle!!!");exit(0);}i += 10;for (j = 0; j < 3; j++) {fread(buffer2.c, 2, 1, lwo);temp = sflip(buffer2.i);ply[numpolys].pl[j] = temp;}fread(buffer2.c, 2, 1, lwo);numpolys++;if (numpolys > MAXPOLYS) {fprintf(stderr, "reader: Too many polygons!!!\n");exit(0);}}}void LoadLWOTriangleList(char *filename, triangle_t **pptri, int32_t *numtriangles) {int32_t i, j;triangle_t *ptri;if ((lwo = fopen(filename, "rb")) == 0) {fprintf(stderr, "reader: could not open file '%s'\n", filename);exit(0);}fread(buffer4.c, 4, 1, lwo);fread(buffer4.c, 4, 1, lwo);counter = lflip(buffer4.l) - 4;fread(buffer4.c, 4, 1, lwo);while (counter > 0) {fread(buffer4.c, 4, 1, lwo);if (!strncmp(buffer4.c, "PNTS", 4)) {getpoints();} else {if (!strncmp(buffer4.c, "POLS", 4)) {getpolys();} else {skipchunk();}}}fclose(lwo);ptri = malloc(MAXTRIANGLES * sizeof(triangle_t));*pptri = ptri;for (i = 0; i < numpolys; i++) {for (j = 0; j < 3; j++) {ptri[i].verts[j][0] = pnt[ply[i].pl[j]].p[0];ptri[i].verts[j][1] = pnt[ply[i].pl[j]].p[2];ptri[i].verts[j][2] = pnt[ply[i].pl[j]].p[1];}}*numtriangles = numpolys;}
/*===========================================================================Copyright (C) 1997-2006 Id Software, Inc.This program is free software; you can redistribute it and/or modifyit under the terms of the GNU General Public License as published bythe 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 ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See theGNU General Public License for more details.You should have received a copy of the GNU General Public License alongwith this program; if not, write to the Free Software Foundation, Inc.,51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.===========================================================================*/// lbmlib.c#include "cmdlib.h"#include "lbmlib.h"/*============================================================================LBM STUFF============================================================================*/typedef uint8_t UBYTE;// conflicts with windows typedef short WORD;typedef uint16_t UWORD;typedef long LONG;typedef enum { ms_none, ms_mask, ms_transcolor, ms_lasso } mask_t;typedef enum { cm_none, cm_rle1 } compress_t;typedef struct {UWORD w, h;short x, y;UBYTE nPlanes;UBYTE masking;UBYTE compression;UBYTE pad1;UWORD transparentColor;UBYTE xAspect, yAspect;short pageWidth, pageHeight;} bmhd_t;extern bmhd_t bmhd; // will be in native byte order#define FORMID ('F' + ('O' << 8) + ((int32_t)'R' << 16) + ((int32_t)'M' << 24))#define ILBMID ('I' + ('L' << 8) + ((int32_t)'B' << 16) + ((int32_t)'M' << 24))#define PBMID ('P' + ('B' << 8) + ((int32_t)'M' << 16) + ((int32_t)' ' << 24))#define BMHDID ('B' + ('M' << 8) + ((int32_t)'H' << 16) + ((int32_t)'D' << 24))#define BODYID ('B' + ('O' << 8) + ((int32_t)'D' << 16) + ((int32_t)'Y' << 24))#define CMAPID ('C' + ('M' << 8) + ((int32_t)'A' << 16) + ((int32_t)'P' << 24))bmhd_t bmhd;int32_t Align(int32_t l) {if(l & 1)return l + 1;return l;}/*================LBMRLEdecompressSource must be evenly aligned!================*/byte *LBMRLEDecompress(byte *source, byte *unpacked, int32_t bpwidth) {int32_t count;byte b, rept;count = 0;do {rept = *source++;if(rept > 0x80) {rept = (rept ^ 0xff) + 2;b = *source++;memset(unpacked, b, rept);unpacked += rept;} else if(rept < 0x80) {rept++;memcpy(unpacked, source, rept);unpacked += rept;source += rept;} elserept = 0; // rept of 0x80 is NOPcount += rept;} while(count < bpwidth);if(count > bpwidth)Error("Decompression exceeded width!\n");return source;}/*=================LoadLBM=================*/void LoadLBM(char *filename, byte **picture, byte **palette) {byte *LBMbuffer, *picbuffer, *cmapbuffer;int32_t y;byte *LBM_P, *LBMEND_P;byte *pic_p;byte *body_p;int32_t formtype, formlength;int32_t chunktype, chunklength;// qiet compiler warningspicbuffer = NULL;cmapbuffer = NULL;//// load the LBM//LoadFile(filename, (void **)&LBMbuffer);//// parse the LBM header//LBM_P = LBMbuffer;if(*(int32_t *)LBMbuffer != LittleLong(FORMID))Error("No FORM ID at start of file!\n");LBM_P += 4;formlength = BigLong(*(int32_t *)LBM_P);LBM_P += 4;LBMEND_P = LBM_P + Align(formlength);formtype = LittleLong(*(int32_t *)LBM_P);if(formtype != ILBMID && formtype != PBMID)Error("Unrecognized form type: %c%c%c%c\n", formtype & 0xff, (formtype >> 8) & 0xff, (formtype >> 16) & 0xff,(formtype >> 24) & 0xff);LBM_P += 4;//// parse chunks//while(LBM_P < LBMEND_P) {chunktype = LBM_P[0] + (LBM_P[1] << 8) + (LBM_P[2] << 16) + (LBM_P[3] << 24);LBM_P += 4;chunklength = LBM_P[3] + (LBM_P[2] << 8) + (LBM_P[1] << 16) + (LBM_P[0] << 24);LBM_P += 4;switch(chunktype) {case BMHDID:memcpy(&bmhd, LBM_P, sizeof(bmhd));bmhd.w = BigShort(bmhd.w);bmhd.h = BigShort(bmhd.h);bmhd.x = BigShort(bmhd.x);bmhd.y = BigShort(bmhd.y);bmhd.pageWidth = BigShort(bmhd.pageWidth);bmhd.pageHeight = BigShort(bmhd.pageHeight);break;case CMAPID:cmapbuffer = malloc(768);memset(cmapbuffer, 0, 768);memcpy(cmapbuffer, LBM_P, chunklength);break;case BODYID:body_p = LBM_P;pic_p = picbuffer = malloc(bmhd.w * bmhd.h);if(formtype == PBMID) {//// unpack PBM//for(y = 0; y < bmhd.h; y++, pic_p += bmhd.w) {if(bmhd.compression == cm_rle1)body_p = LBMRLEDecompress((byte *)body_p, pic_p, bmhd.w);else if(bmhd.compression == cm_none) {memcpy(pic_p, body_p, bmhd.w);body_p += Align(bmhd.w);}}} else {//// unpack ILBM//Error("%s is an interlaced LBM, not packed", filename);}break;}LBM_P += Align(chunklength);}free(LBMbuffer);*picture = picbuffer;if(palette)*palette = cmapbuffer;}/*============================================================================WRITE LBM============================================================================*//*==============WriteLBMfile==============*/void WriteLBMfile(char *filename, byte *data, int32_t width, int32_t height, byte *palette) {byte *lbm, *lbmptr;int32_t *formlength, *bmhdlength, *cmaplength, *bodylength;int32_t length;bmhd_t basebmhd;lbm = lbmptr = malloc(width * height + 1000);if(!lbm)return; // qb: NULL if width or height is zero.//// start FORM//*lbmptr++ = 'F';*lbmptr++ = 'O';*lbmptr++ = 'R';*lbmptr++ = 'M';formlength = (int32_t *)lbmptr;lbmptr += 4; // leave space for length*lbmptr++ = 'P';*lbmptr++ = 'B';*lbmptr++ = 'M';*lbmptr++ = ' ';//// write BMHD//*lbmptr++ = 'B';*lbmptr++ = 'M';*lbmptr++ = 'H';*lbmptr++ = 'D';bmhdlength = (int32_t *)lbmptr;lbmptr += 4; // leave space for lengthmemset(&basebmhd, 0, sizeof(basebmhd));basebmhd.w = BigShort((short)width);basebmhd.h = BigShort((short)height);basebmhd.nPlanes = BigShort(8);basebmhd.xAspect = BigShort(5);basebmhd.yAspect = BigShort(6);basebmhd.pageWidth = BigShort((short)width);basebmhd.pageHeight = BigShort((short)height);memcpy(lbmptr, &basebmhd, sizeof(basebmhd));lbmptr += sizeof(basebmhd);length = lbmptr - (byte *)bmhdlength - 4;*bmhdlength = BigLong(length);if(length & 1)*lbmptr++ = 0; // pad chunk to even offset//// write CMAP//*lbmptr++ = 'C';*lbmptr++ = 'M';*lbmptr++ = 'A';*lbmptr++ = 'P';cmaplength = (int32_t *)lbmptr;lbmptr += 4; // leave space for lengthmemcpy(lbmptr, palette, 768);lbmptr += 768;length = lbmptr - (byte *)cmaplength - 4;*cmaplength = BigLong(length);if(length & 1)*lbmptr++ = 0; // pad chunk to even offset//// write BODY//*lbmptr++ = 'B';*lbmptr++ = 'O';*lbmptr++ = 'D';*lbmptr++ = 'Y';bodylength = (int32_t *)lbmptr;lbmptr += 4; // leave space for lengthmemcpy(lbmptr, data, width * height);lbmptr += width * height;length = lbmptr - (byte *)bodylength - 4;*bodylength = BigLong(length);if(length & 1)*lbmptr++ = 0; // pad chunk to even offset//// done//length = lbmptr - (byte *)formlength - 4;*formlength = BigLong(length);if(length & 1)*lbmptr++ = 0; // pad chunk to even offset//// write output file//SaveFile(filename, lbm, lbmptr - lbm);free(lbm);}/*============================================================================LOAD PCX============================================================================*/typedef struct {char manufacturer;char version;char encoding;char bits_per_pixel;uint16_t xmin, ymin, xmax, ymax;uint16_t hres, vres;uint8_t palette[48];char reserved;char color_planes;uint16_t bytes_per_line;uint16_t palette_type;char filler[58];uint8_t data; // unbounded} pcx_t;/*==============LoadPCX==============*/void LoadPCX(char *filename, byte **pic, byte **palette, int32_t *width, int32_t *height) {byte *raw;pcx_t *pcx;int32_t x, y;int32_t len;int32_t dataByte, runLength;byte *out, *pix;//// load the file//len = LoadFile(filename, (void **)&raw);//// parse the PCX file//pcx = (pcx_t *)raw;raw = &pcx->data;pcx->xmin = LittleShort(pcx->xmin);pcx->ymin = LittleShort(pcx->ymin);pcx->xmax = LittleShort(pcx->xmax);pcx->ymax = LittleShort(pcx->ymax);pcx->hres = LittleShort(pcx->hres);pcx->vres = LittleShort(pcx->vres);pcx->bytes_per_line = LittleShort(pcx->bytes_per_line);pcx->palette_type = LittleShort(pcx->palette_type);if(pcx->manufacturer != 0x0a || pcx->version != 5 || pcx->encoding != 1 || pcx->bits_per_pixel != 8 ||pcx->xmax >= 640 || pcx->ymax >= 480)Error("Bad pcx file %s", filename);if(palette) {*palette = malloc(768);memcpy(*palette, (byte *)pcx + len - 768, 768);}if(width)*width = pcx->xmax + 1;if(height)*height = pcx->ymax + 1;if(!pic)return;out = malloc((pcx->ymax + 1) * (pcx->xmax + 1));if(!out)Error("Skin_Cache: couldn't allocate");*pic = out;pix = out;for(y = 0; y <= pcx->ymax; y++, pix += pcx->xmax + 1) {for(x = 0; x <= pcx->xmax;) {dataByte = *raw++;if((dataByte & 0xC0) == 0xC0) {runLength = dataByte & 0x3F;dataByte = *raw++;} elserunLength = 1;while(runLength-- > 0)pix[x++] = dataByte;}}if(raw - (byte *)pcx > len)Error("PCX file %s was malformed", filename);free(pcx);}/*==============WritePCXfile==============*/void WritePCXfile(char *filename, byte *data, int32_t width, int32_t height, byte *palette) {int32_t i, j, length;pcx_t *pcx;byte *pack;pcx = malloc(width * height * 2 + 1000);memset(pcx, 0, sizeof(*pcx));pcx->manufacturer = 0x0a; // PCX idpcx->version = 5; // 256 colorpcx->encoding = 1; // uncompressedpcx->bits_per_pixel = 8; // 256 colorpcx->xmin = 0;pcx->ymin = 0;pcx->xmax = LittleShort((short)(width - 1));pcx->ymax = LittleShort((short)(height - 1));pcx->hres = LittleShort((short)width);pcx->vres = LittleShort((short)height);pcx->color_planes = 1; // chunky imagepcx->bytes_per_line = LittleShort((short)width);pcx->palette_type = LittleShort(2); // not a grey scale// pack the imagepack = &pcx->data;for(i = 0; i < height; i++) {for(j = 0; j < width; j++) {if((*data & 0xc0) != 0xc0)*pack++ = *data++;else {*pack++ = 0xc1;*pack++ = *data++;}}}// write the palette*pack++ = 0x0c; // palette ID bytefor(i = 0; i < 768; i++)*pack++ = *palette++;// write output filelength = pack - (byte *)pcx;SaveFile(filename, pcx, length);free(pcx);}/*============================================================================LOAD IMAGE============================================================================*//*==============Load256ImageWill load either an lbm or pcx, depending on extension.Any of the return pointers can be NULL if you don't want them.==============*/void Load256Image(char *name, byte **pixels, byte **palette, int32_t *width, int32_t *height) {char ext[128];ExtractFileExtension(name, ext);if(!Q_strcasecmp(ext, "lbm")) {LoadLBM(name, pixels, palette);if(width)*width = bmhd.w;if(height)*height = bmhd.h;} else if(!Q_strcasecmp(ext, "pcx")) {LoadPCX(name, pixels, palette, width, height);} elseError("%s doesn't have a known image extension", name);}/*==============Save256ImageWill save either an lbm or pcx, depending on extension.==============*/void Save256Image(char *name, byte *pixels, byte *palette, int32_t width, int32_t height) {char ext[128];ExtractFileExtension(name, ext);if(!Q_strcasecmp(ext, "lbm")) {WriteLBMfile(name, pixels, width, height, palette);} else if(!Q_strcasecmp(ext, "pcx")) {WritePCXfile(name, pixels, width, height, palette);} elseError("%s doesn't have a known image extension", name);}/*============================================================================TARGA IMAGE============================================================================*/typedef struct _TargaHeader {uint8_t id_length, colormap_type, image_type;uint16_t colormap_index, colormap_length;uint8_t colormap_size;uint16_t x_origin, y_origin, width, height;uint8_t pixel_size, attributes;} TargaHeader;int32_t fgetLittleShort(FILE *f) {byte b1, b2;b1 = fgetc(f);b2 = fgetc(f);return (short)(b1 + b2 * 256);}int32_t fgetLittleLong(FILE *f) {byte b1, b2, b3, b4;b1 = fgetc(f);b2 = fgetc(f);b3 = fgetc(f);b4 = fgetc(f);return b1 + (b2 << 8) + (b3 << 16) + (b4 << 24);}/*=============LoadTGA=============*/void LoadTGA(char *name, byte **pixels, int32_t *width, int32_t *height) {int32_t columns, rows, numPixels;byte *pixbuf;int32_t row, column;FILE *fin;byte *targa_rgba;TargaHeader targa_header;fin = fopen(name, "rb");if(!fin)Error("Couldn't read %s", name);targa_header.id_length = fgetc(fin);targa_header.colormap_type = fgetc(fin);targa_header.image_type = fgetc(fin);targa_header.colormap_index = fgetLittleShort(fin);targa_header.colormap_length = fgetLittleShort(fin);targa_header.colormap_size = fgetc(fin);targa_header.x_origin = fgetLittleShort(fin);targa_header.y_origin = fgetLittleShort(fin);targa_header.width = fgetLittleShort(fin);targa_header.height = fgetLittleShort(fin);targa_header.pixel_size = fgetc(fin);targa_header.attributes = fgetc(fin);if(targa_header.image_type != 2 && targa_header.image_type != 10)Error("LoadTGA %s: Only type 2 and 10 targa RGB images supported\n", name);if(targa_header.colormap_type != 0 || (targa_header.pixel_size != 32 && targa_header.pixel_size != 24))Error("LoadTGA %s: Only 32 or 24 bit images supported (no colormaps)\n", name);columns = targa_header.width;rows = targa_header.height;numPixels = columns * rows;if(width)*width = columns;if(height)*height = rows;targa_rgba = malloc(numPixels * 4);*pixels = targa_rgba;if(targa_header.id_length != 0)fseek(fin, targa_header.id_length, SEEK_CUR); // skip TARGA image commentif(targa_header.image_type == 2) { // Uncompressed, RGB imagesfor(row = rows - 1; row >= 0; row--) {pixbuf = targa_rgba + row * columns * 4;for(column = 0; column < columns; column++) {uint8_t red, green, blue, alphabyte;switch(targa_header.pixel_size) {case 24:blue = getc(fin);green = getc(fin);red = getc(fin);*pixbuf++ = red;*pixbuf++ = green;*pixbuf++ = blue;*pixbuf++ = 255;break;case 32:blue = getc(fin);green = getc(fin);red = getc(fin);alphabyte = getc(fin);*pixbuf++ = red;*pixbuf++ = green;*pixbuf++ = blue;*pixbuf++ = alphabyte;break;}}}} else if(targa_header.image_type == 10) { // Runlength encoded RGB images// qb: set defaults. from AAtoolsuint8_t red = 255, green = 255, blue = 255, alphabyte = 255, packetHeader, packetSize, j;for(row = rows - 1; row >= 0; row--) {pixbuf = targa_rgba + row * columns * 4;for(column = 0; column < columns;) {packetHeader = getc(fin);packetSize = 1 + (packetHeader & 0x7f);if(packetHeader & 0x80) { // run-length packetswitch(targa_header.pixel_size) {case 24:blue = getc(fin);green = getc(fin);red = getc(fin);alphabyte = 255;break;case 32:blue = getc(fin);green = getc(fin);red = getc(fin);alphabyte = getc(fin);break;}for(j = 0; j < packetSize; j++) {*pixbuf++ = red;*pixbuf++ = green;*pixbuf++ = blue;*pixbuf++ = alphabyte;column++;if(column == columns) { // run spans across rowscolumn = 0;if(row > 0)row--;elsegoto breakOut;pixbuf = targa_rgba + row * columns * 4;}}} else { // non run-length packetfor(j = 0; j < packetSize; j++) {switch(targa_header.pixel_size) {case 24:blue = getc(fin);green = getc(fin);red = getc(fin);*pixbuf++ = red;*pixbuf++ = green;*pixbuf++ = blue;*pixbuf++ = 255;break;case 32:blue = getc(fin);green = getc(fin);red = getc(fin);alphabyte = getc(fin);*pixbuf++ = red;*pixbuf++ = green;*pixbuf++ = blue;*pixbuf++ = alphabyte;break;}column++;if(column == columns) { // pixel packet run spans across rowscolumn = 0;if(row > 0)row--;elsegoto breakOut;pixbuf = targa_rgba + row * columns * 4;}}}}breakOut:;}}fclose(fin);}
/*===========================================================================Copyright (C) 1997-2006 Id Software, Inc.This program is free software; you can redistribute it and/or modifyit under the terms of the GNU General Public License as published bythe 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 ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See theGNU General Public License for more details.You should have received a copy of the GNU General Public License alongwith this program; if not, write to the Free Software Foundation, Inc.,51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.===========================================================================*///// l3dslib.c: library for loading triangles from an Alias triangle file//#include <stdio.h>#include "cmdlib.h"#include "mathlib.h"#include "4data.h"#define MAIN3DS 0x4D4D#define EDIT3DS 0x3D3D // this is the start of the editor config#define EDIT_OBJECT 0x4000#define OBJ_TRIMESH 0x4100#define TRI_VERTEXL 0x4110#define TRI_FACEL1 0x4120#define MAXVERTS 2000typedef struct {int32_t v[4];} tri;float fverts[MAXVERTS][3];tri tris[MAXTRIANGLES];int32_t bytesread, level, numtris, totaltris;int32_t vertsfound, trisfound;triangle_t *ptri;// Alias stores triangles as 3 explicit vertices in .tri files, so even though we// start out with a vertex pool and vertex indices for triangles, we have to convert// to raw, explicit trianglesvoid StoreAliasTriangles(void) {int32_t i, j, k;if ((totaltris + numtris) > MAXTRIANGLES)Error("Error: Too many triangles");for (i = 0; i < numtris; i++) {for (j = 0; j < 3; j++) {for (k = 0; k < 3; k++) {ptri[i + totaltris].verts[j][k] = fverts[tris[i].v[j]][k];}}}totaltris += numtris;numtris = 0;vertsfound = 0;trisfound = 0;}int32_t ParseVertexL(FILE *input) {int32_t i, j, startbytesread, numverts;uint16_t tshort;if (vertsfound)Error("Error: Multiple vertex chunks");vertsfound = 1;startbytesread = bytesread;if (feof(input))Error("Error: unexpected end of file");fread(&tshort, sizeof(tshort), 1, input);bytesread += sizeof(tshort);numverts = (int32_t)tshort;if (numverts > MAXVERTS)Error("Error: Too many vertices");for (i = 0; i < numverts; i++) {for (j = 0; j < 3; j++) {if (feof(input))Error("Error: unexpected end of file");fread(&fverts[i][j], sizeof(float), 1, input);bytesread += sizeof(float);}}if (vertsfound && trisfound)StoreAliasTriangles();return bytesread - startbytesread;}int32_t ParseFaceL1(FILE *input) {int32_t i, j, startbytesread;uint16_t tshort;if (trisfound)Error("Error: Multiple face chunks");trisfound = 1;startbytesread = bytesread;if (feof(input))Error("Error: unexpected end of file");fread(&tshort, sizeof(tshort), 1, input);bytesread += sizeof(tshort);numtris = (int32_t)tshort;if (numtris > MAXTRIANGLES)Error("Error: Too many triangles");for (i = 0; i < numtris; i++) {for (j = 0; j < 4; j++) {if (feof(input))Error("Error: unexpected end of file");fread(&tshort, sizeof(tshort), 1, input);bytesread += sizeof(tshort);tris[i].v[j] = (int32_t)tshort;}}if (vertsfound && trisfound)StoreAliasTriangles();return bytesread - startbytesread;}int32_t ParseChunk(FILE *input) {#define CHUNK_SIZE 4096char temp[CHUNK_SIZE];uint16_t type;int32_t i, length, w, t, retval;level++;retval = 0;// chunk typeif (feof(input))Error("Error: unexpected end of file");fread(&type, sizeof(type), 1, input);bytesread += sizeof(type);// chunk lengthif (feof(input))Error("Error: unexpected end of file");fread(&length, sizeof(length), 1, input);bytesread += sizeof(length);w = length - 6;// process chunk if we care about it, otherwise skip itswitch (type) {case TRI_VERTEXL:w -= ParseVertexL(input);goto ParseSubchunk;case TRI_FACEL1:w -= ParseFaceL1(input);goto ParseSubchunk;case EDIT_OBJECT:// read the namei = 0;do {if (feof(input))Error("Error: unexpected end of file");fread(&temp[i], 1, 1, input);i++;w--;bytesread++;} while (temp[i - 1]);case MAIN3DS:case OBJ_TRIMESH:case EDIT3DS:// parse through subchunksParseSubchunk:while (w > 0) {w -= ParseChunk(input);}retval = length;goto Done;default:// skip other chunkswhile (w > 0) {t = w;if (t > CHUNK_SIZE)t = CHUNK_SIZE;if (feof(input))Error("Error: unexpected end of file");fread(&temp, t, 1, input);bytesread += t;w -= t;}retval = length;goto Done;}Done:level--;return retval;}void Load3DSTriangleList(char *filename, triangle_t **pptri, int32_t *numtriangles) {FILE *input;int16_t tshort;bytesread = 0;level = 0;numtris = 0;totaltris = 0;vertsfound = 0;trisfound = 0;if ((input = fopen(filename, "rb")) == 0) {fprintf(stderr, "reader: could not open file '%s'\n", filename);exit(0);}fread(&tshort, sizeof(tshort), 1, input);// should only be MAIN3DS, but some files seem to start with EDIT3DS, with// no MAIN3DSif ((tshort != MAIN3DS) && (tshort != EDIT3DS)) {fprintf(stderr, "File is not a 3DS file.\n");exit(0);}// back to top of file so we can parse the first chunk descriptorfseek(input, 0, SEEK_SET);ptri = malloc(MAXTRIANGLES * sizeof(triangle_t));*pptri = ptri;// parse through looking for the relevant chunk tree (MAIN3DS | EDIT3DS | EDIT_OBJECT |// OBJ_TRIMESH | {TRI_VERTEXL, TRI_FACEL1}) and skipping other chunksParseChunk(input);if (vertsfound || trisfound)Error("Incomplete triangle set");*numtriangles = totaltris;fclose(input);free(ptri); // qb: stop mem leak}
/*===========================================================================Copyright (C) 1997-2006 Id Software, Inc.This program is free software; you can redistribute it and/or modifyit under the terms of the GNU General Public License as published bythe 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 ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See theGNU General Public License for more details.You should have received a copy of the GNU General Public License alongwith this program; if not, write to the Free Software Foundation, Inc.,51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.===========================================================================*/// cmdlib.c#include "cmdlib.h"#include <sys/types.h>#include <sys/stat.h>#ifdef WIN32#include <direct.h>#endif#ifdef NeXT#include <libc.h>#endif#ifdef __unix__#include <unistd.h>#endif#define PATHSEPERATOR '/'// set these before calling CheckParmint32_t myargc;char **myargv;char com_token[1024];qboolean com_eof;qboolean archive;char archivedir[1024];char inbase[32];char outbase[32];char source[1024];/*===================ExpandWildcardsMimic unix command line expansion===================*/#define MAX_EX_ARGC 1024int32_t ex_argc;char *ex_argv[MAX_EX_ARGC];#ifdef _WIN32#include "io.h"void ExpandWildcards(int32_t *argc, char ***argv) {struct _finddata_t fileinfo;int32_t handle;int32_t i;char filename[1024];char filebase[1024];char *path;ex_argc = 0;for (i = 0; i < *argc; i++) {path = (*argv)[i];if (path[0] == '-' || (!strstr(path, "*") && !strstr(path, "?"))) {ex_argv[ex_argc++] = path;continue;}handle = _findfirst(path, &fileinfo);if (handle == -1)return;ExtractFilePath(path, filebase);do {sprintf(filename, "%s%s", filebase, fileinfo.name);ex_argv[ex_argc++] = copystring(filename);} while (_findnext(handle, &fileinfo) != -1);_findclose(handle);}*argc = ex_argc;*argv = ex_argv;}#elsevoid ExpandWildcards(int32_t *argc, char ***argv) {}#endif#ifdef WIN_ERROR#include <windows.h>/*=================ErrorFor abnormal program terminations in windowed apps=================*/void Error(char *error, ...) {va_list argptr;char text[1024];char text2[1024];int32_t err;err = GetLastError();va_start(argptr, error);vsprintf(text, error, argptr);va_end(argptr);sprintf(text2, "%s\nGetLastError() = %i", text, err);MessageBox(NULL, text2, "Error", 0 /* MB_OK */);exit(1);}#else/*=================ErrorFor abnormal program terminations in console apps=================*/void Error(char *error, ...) {va_list argptr;printf("\n************ ERROR ************\n");va_start(argptr, error);vprintf(error, argptr);va_end(argptr);printf("\n");exit(1);}#endif// only qprintf if in verbose modeqboolean verbose = false;void qprintf(char *format, ...) {va_list argptr;if (!verbose)return;va_start(argptr, format);vprintf(format, argptr);va_end(argptr);fflush(stdout);}// qb: similar to AAtools, except basedir defaults to moddir/** Path determination** assumes* moddir is parent of whatever directory contains the .map/.bsp* basedir is moddir* gamedir is parent of moddir* qdir is parent of gamedir*/char qdir[1024] = "";char gamedir[1024] = "";char moddir[1024] = "";char basedir[1024] = "";void SetQdirFromPath(char *path) {char cwd_map[512];char *lastslash;memset(cwd_map, 0, sizeof(cwd_map));strncpy(cwd_map, path, sizeof(cwd_map) - 1);#ifdef WIN32lastslash = strrchr(cwd_map, '\\');// I believe Win32 can use either slash type. -Maxif (lastslash == NULL)#endiflastslash = strrchr(cwd_map, '/');if (lastslash == NULL) {// no slashes: CWDQ_getwd(cwd_map); // appends path separator} else {// get rid of everything after last path separatorlastslash++;*lastslash = '\0';}strcpy(moddir, cwd_map);#ifdef WIN32strcat(moddir, "..\\");#elsestrcat(moddir, "../");#endifstrcpy(basedir, moddir);strcpy(gamedir, moddir);#ifdef WIN32strcat(gamedir, "..\\");#elsestrcat(gamedir, "../");#endifstrcpy(qdir, gamedir);#ifdef WIN32strcat(qdir, "..\\");#elsestrcat(qdir, "../");#endif}char *ExpandArg(const char *path) {static char full[1024];if (path[0] != '/' && path[0] != '\\' && path[1] != ':') {Q_getwd(full);strcat(full, path);} elsestrcpy(full, path);return full;}char *ExpandPath(char *path) {static char full[1024];if (!qdir[0])Error("ExpandPath called without qdir set");if (path[0] == '/' || path[0] == '\\' || path[1] == ':')return path;sprintf(full, "%s%s", qdir, path);return full;}char *ExpandPathAndArchive(char *path) {char *expanded;char archivename[2060];expanded = ExpandPath(path);if (archive) {sprintf(archivename, "%s/%s", archivedir, path);QCopyFile(expanded, archivename);}return expanded;}char *copystring(char *s) {char *b;b = malloc(strlen(s) + 1);strcpy(b, s);return b;}/*================I_FloatTime================*/double I_FloatTime(void) {time_t t;time(&t);return t;#if 0// more precise, less portablestruct timeval tp;struct timezone tzp;static int32_t secbase;gettimeofday(&tp, &tzp);if (!secbase){secbase = tp.tv_sec;return tp.tv_usec / 1000000.0;}return (tp.tv_sec - secbase) + tp.tv_usec / 1000000.0;#endif}void Q_pathslash(char *out) { // qb: addedqboolean lastslash;#ifdef WIN32lastslash = (*out && out[strlen(out + 1)] == '\\');// I believe Win32 can use either slash type. -Maxif (!lastslash)#endiflastslash = (*out && out[strlen(out + 1)] == '/');if (!lastslash)#ifdef WIN32strcat(out, "\\");#elsestrcat(out, "/");#endif}void Q_getwd(char *out) {#ifdef WIN32if (!_getcwd(out, 256))Error("_getcwd failed");strcat(out, "\\");#elseif (!getcwd(out, 256)) // qb: was getwd(out)Error("getcwd failed");strcat(out, "/");#endif}void Q_mkdir(char *path) {#ifdef WIN32if (_mkdir(path) != -1)return;#elseif (mkdir(path, 0777) != -1)return;#endifif (errno != EEXIST)Error("mkdir %s: %s", path, strerror(errno));}/*============FileTimereturns -1 if not present============*/int32_t FileTime(char *path) {struct stat buf;if (stat(path, &buf) == -1)return -1;return buf.st_mtime;}/*==============COM_ParseParse a token out of a string==============*/char *COM_Parse(char *data) {int32_t c;int32_t len;len = 0;com_token[0] = 0;if (!data)return NULL;// skip whitespaceskipwhite:while ((c = *data) <= ' ') {if (c == 0) {com_eof = true;return NULL; // end of file;}data++;}// skip // commentsif (c == '/' && data[1] == '/') {while (*data && *data != '\n')data++;goto skipwhite;}// handle quoted strings speciallyif (c == '\"') {data++;do {c = *data++;if (c == '\"') {com_token[len] = 0;return data;}com_token[len] = c;len++;} while (1);}// parse single charactersif (c == '{' || c == '}' || c == ')' || c == '(' || c == '\'' || c == ':') {com_token[len] = c;len++;com_token[len] = 0;return data + 1;}// parse a regular worddo {com_token[len] = c;data++;len++;c = *data;if (c == '{' || c == '}' || c == ')' || c == '(' || c == '\'' || c == ':')break;} while (c > 32);com_token[len] = 0;return data;}int32_t Q_strncasecmp(char *s1, char *s2, int32_t n) {int32_t c1, c2;do {c1 = *s1++;c2 = *s2++;if (!n--)return 0; // strings are equal until end pointif (c1 != c2) {if (c1 >= 'a' && c1 <= 'z')c1 -= ('a' - 'A');if (c2 >= 'a' && c2 <= 'z')c2 -= ('a' - 'A');if (c1 != c2)return -1; // strings not equal}} while (c1);return 0; // strings are equal}int32_t Q_strcasecmp(char *s1, char *s2) {return Q_strncasecmp(s1, s2, 99999);}char *strtoupper(char *start) {char *in;in = start;while (*in) {*in = toupper(*in);in++;}return start;}char *strlower(char *start) {char *in;in = start;while (*in) {*in = tolower(*in);in++;}return start;}/*=============================================================================MISC FUNCTIONS=============================================================================*//*=================CheckParmChecks for the given parameter in the program's command line argumentsReturns the argument number (1 to argc-1) or 0 if not present=================*/int32_t CheckParm(char *check) {int32_t i;for (i = 1; i < myargc; i++) {if (!Q_strcasecmp(check, myargv[i]))return i;}return 0;}/*================Q_filelength================*/int32_t Q_filelength(FILE *f) {int32_t pos;int32_t end;pos = ftell(f);fseek(f, 0, SEEK_END);end = ftell(f);fseek(f, pos, SEEK_SET);return end;}FILE *SafeOpenWrite(char *filename) {FILE *f;f = fopen(filename, "wb");if (!f)Error("Error opening %s: %s", filename, strerror(errno));return f;}FILE *SafeOpenRead(char *filename) {FILE *f;f = fopen(filename, "rb");if (!f)Error("Error opening %s: %s", filename, strerror(errno));return f;}void SafeRead(FILE *f, void *buffer, int32_t count) {if (fread(buffer, 1, count, f) != (size_t)count)Error("File read failure");}void SafeWrite(FILE *f, void *buffer, int32_t count) {if (fwrite(buffer, 1, count, f) != (size_t)count)Error("File write failure");}/*==============FileExists==============*/qboolean FileExists(char *filename) {FILE *f;f = fopen(filename, "r");if (!f)return false;fclose(f);return true;}/*==============LoadFile==============*/int32_t LoadFile(char *filename, void **bufferptr) {FILE *f;int32_t length;void *buffer;f = SafeOpenRead(filename);length = Q_filelength(f);buffer = malloc(length + 1);((char *)buffer)[length] = 0;SafeRead(f, buffer, length);fclose(f);*bufferptr = buffer;return length;}/*==============TryLoadFileAllows failure==============*/int32_t TryLoadFile(char *filename, void **bufferptr, int32_t print_error) {FILE *f;int32_t length;void *buffer;*bufferptr = NULL;f = fopen(filename, "rb");if (!f) // qb: print error - GDD tools{if (print_error)printf(" File %s failed to open\n", filename);return -1;}length = Q_filelength(f);buffer = malloc(length + 1);((char *)buffer)[length] = 0;SafeRead(f, buffer, length);fclose(f);*bufferptr = buffer;return length;}/*==============TryLoadFileFromPak //qb: GDD toolsAllows failure==============*/typedef struct{uint8_t magic[4]; // Name of the new WAD formatlong diroffset; // Position of WAD directory from start of filelong dirsize; // Number of entries * 0x40 (64 char)uint8_t bogus[50];} pakheader_t;typedef struct{uint8_t filename[0x38]; // Name of the file, Unix style, with extension,// 56 chars, padded with '\0'.long offset; // Position of the entry in PACK filelong size; // Size of the entry in PACK file} pakentry_t;int32_t TryLoadFileFromPak(char *filename, void **bufferptr, char *gd) {FILE *f;int32_t n, i, ret_len;long dir_ents;void *buffer;pakheader_t pak_header;pakentry_t *pak_entry;char file[1024];for (n = 0; filename[n] != 0; n++) {if (filename[n] == '\\')filename[n] = '/';}*bufferptr = NULL;ret_len = -1;for (n = 0;; n++) {if (ret_len != -1)break;sprintf(file, "%spak%d.pak", gd, n);f = fopen(file, "rb");if (!f) {// if(errno == ENOENT) jitreturn -1;continue;}SafeRead(f, &pak_header, sizeof(pakheader_t));dir_ents = pak_header.dirsize / sizeof(pakentry_t);pak_entry = malloc(pak_header.dirsize);if (pak_entry != NULL) {if (!fseek(f, pak_header.diroffset, SEEK_SET)) {memset(pak_entry, 0, pak_header.dirsize);SafeRead(f, pak_entry, pak_header.dirsize);for (i = 0; i < dir_ents; i++) {for (n = 0; pak_entry[i].filename[n] != 0; n++) {if (pak_entry[i].filename[n] == '\\')pak_entry[i].filename[n] = '/';}if (!Q_strncasecmp((char *)pak_entry[i].filename, filename, 56)) {if (!fseek(f, pak_entry[i].offset, SEEK_SET)) {buffer = malloc(pak_entry[i].size + 1);((char *)buffer)[pak_entry[i].size] = 0;SafeRead(f, buffer, pak_entry[i].size);*bufferptr = buffer;ret_len = pak_entry[i].size;}break;}}}}if (pak_entry != NULL)free(pak_entry);fclose(f);}return ret_len;}/**//*==============SaveFile==============*/void SaveFile(char *filename, void *buffer, int32_t count) {FILE *f;f = SafeOpenWrite(filename);SafeWrite(f, buffer, count);fclose(f);}void DefaultExtension(char *path, char *extension) {char *src;//// if path doesnt have a .EXT, append extension// (extension should include the .)//src = path + strlen(path) - 1;while (*src != PATHSEPERATOR && src != path) {if (*src == '.')return; // it has an extensionsrc--;}strcat(path, extension);}void DefaultPath(char *path, char *basepath) {char temp[128];if (path[0] == PATHSEPERATOR)return; // absolute path locationstrcpy(temp, path);strcpy(path, basepath);strcat(path, temp);}void StripFilename(char *path) {int32_t length;length = strlen(path) - 1;while (length > 0 && path[length] != PATHSEPERATOR)length--;path[length] = 0;}void StripExtension(char *path) {int32_t length;length = strlen(path) - 1;while (length > 0 && path[length] != '.') {length--;if (path[length] == '/' || path[length] == '\\')return; // no extension}if (length)path[length] = 0;}/*====================Extract file parts====================*/// FIXME: should include the slash, otherwise// backing to an empty path will be wrong when appending a slashvoid ExtractFilePath(char *path, char *dest) {char *src;src = path + strlen(path) - 1;//// back up until a \ or the start//while (src != path && *(src - 1) != '\\' && *(src - 1) != '/')src--;memcpy(dest, path, src - path);dest[src - path] = 0;}void ExtractFileBase(char *path, char *dest) {char *src;src = path + strlen(path) - 1;//// back up until a \ or the start//while (src != path && *(src - 1) != PATHSEPERATOR)src--;while (*src && *src != '.') {*dest++ = *src++;}*dest = 0;}void ExtractFileExtension(char *path, char *dest) {char *src;src = path + strlen(path) - 1;//// back up until a . or the start//while (src != path && *(src - 1) != '.')src--;if (src == path) {*dest = 0; // no extensionreturn;}strcpy(dest, src);}/*==============ParseNum / ParseHex==============*/int32_t ParseHex(char *hex) {char *str;int32_t num;num = 0;str = hex;while (*str) {num <<= 4;if (*str >= '0' && *str <= '9')num += *str - '0';else if (*str >= 'a' && *str <= 'f')num += 10 + *str - 'a';else if (*str >= 'A' && *str <= 'F')num += 10 + *str - 'A';elseError("Bad hex number: %s", hex);str++;}return num;}int32_t ParseNum(char *str) {if (str[0] == '$')return ParseHex(str + 1);if (str[0] == '0' && str[1] == 'x')return ParseHex(str + 2);return atol(str);}/*============================================================================BYTE ORDER FUNCTIONS============================================================================*/#ifdef _SGI_SOURCE#define __BIG_ENDIAN__#endif#ifdef __BIG_ENDIAN__short LittleShort(short l) {byte b1, b2;b1 = l & 255;b2 = (l >> 8) & 255;return (b1 << 8) + b2;}short BigShort(short l) {return l;}int32_t LittleLong(int32_t l) {byte b1, b2, b3, b4;b1 = l & 255;b2 = (l >> 8) & 255;b3 = (l >> 16) & 255;b4 = (l >> 24) & 255;return ((int32_t)b1 << 24) + ((int32_t)b2 << 16) + ((int32_t)b3 << 8) + b4;}int32_t BigLong(int32_t l) {return l;}float LittleFloat(float l) {union {byte b[4];float f;} in, out;in.f = l;out.b[0] = in.b[3];out.b[1] = in.b[2];out.b[2] = in.b[1];out.b[3] = in.b[0];return out.f;}float BigFloat(float l) {return l;}#elseshort BigShort(short l) {byte b1, b2;b1 = l & 255;b2 = (l >> 8) & 255;return (b1 << 8) + b2;}short LittleShort(short l) {return l;}int32_t BigLong(int32_t l) {byte b1, b2, b3, b4;b1 = l & 255;b2 = (l >> 8) & 255;b3 = (l >> 16) & 255;b4 = (l >> 24) & 255;return ((int32_t)b1 << 24) + ((int32_t)b2 << 16) + ((int32_t)b3 << 8) + b4;}int32_t LittleLong(int32_t l) {return l;}float BigFloat(float l) {union {byte b[4];float f;} in, out;in.f = l;out.b[0] = in.b[3];out.b[1] = in.b[2];out.b[2] = in.b[1];out.b[3] = in.b[0];return out.f;}float LittleFloat(float l) {return l;}#endif//=======================================================// FIXME: byte swap?// this is a 16 bit, non-reflected CRC using the polynomial 0x1021// and the initial and final xor values shown below... in other words, the// CCITT standard CRC used by XMODEM#define CRC_INIT_VALUE 0xffff#define CRC_XOR_VALUE 0x0000static uint16_t crctable[256] ={0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7,0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef,0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6,0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de,0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485,0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d,0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4,0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc,0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823,0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b,0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12,0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a,0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41,0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49,0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70,0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78,0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f,0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067,0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e,0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256,0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d,0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c,0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634,0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab,0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3,0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a,0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92,0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9,0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1,0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8,0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0};void CRC_Init(uint16_t *crcvalue) {*crcvalue = CRC_INIT_VALUE;}void CRC_ProcessByte(uint16_t *crcvalue, byte data) {*crcvalue = (*crcvalue << 8) ^ crctable[(*crcvalue >> 8) ^ data];}uint16_t CRC_Value(uint16_t crcvalue) {return crcvalue ^ CRC_XOR_VALUE;}//=============================================================================/*============CreatePath============*/void CreatePath(char *path) {char *ofs, c;if (path[1] == ':')path += 2;for (ofs = path + 1; *ofs; ofs++) {c = *ofs;if (c == '/' || c == '\\') { // create the directory*ofs = 0;Q_mkdir(path);*ofs = c;}}}/*============QCopyFileUsed to archive source files============*/void QCopyFile(char *from, char *to) {void *buffer;int32_t length;length = LoadFile(from, &buffer);CreatePath(to);SaveFile(to, buffer, length);free(buffer);}
/*===========================================================================Copyright (C) 1997-2006 Id Software, Inc.This program is free software; you can redistribute it and/or modifyit under the terms of the GNU General Public License as published bythe 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 ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See theGNU General Public License for more details.You should have received a copy of the GNU General Public License alongwith this program; if not, write to the Free Software Foundation, Inc.,51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.===========================================================================*/#include "cmdlib.h"#include "mathlib.h"#include "bspfile.h"#include "scriplib.h"qboolean use_qbsp = false; // qb: huge map supportqboolean noskipfix = false; // qb: warn about SURF_SKIP contents rather than silently changing to zerovoid GetLeafNums(void);//=============================================================================// qb: add qbsp typesint32_t nummodels;dmodel_t * dmodels;//[MAX_MAP_MODELS_QBSP];int32_t visdatasize;byte * dvisdata;//[MAX_MAP_VISIBILITY_QBSP];dvis_t *dvis;// = (dvis_t *)dvisdata;int32_t lightdatasize;byte * dlightdata;//[MAX_MAP_LIGHTING_QBSP];int32_t entdatasize;char * dentdata;//[MAX_MAP_ENTSTRING_QBSP];int32_t numleafs;dleaf_t * dleafs;//[MAX_MAP_LEAFS];dleaf_tx * dleafsX;//[MAX_MAP_LEAFS_QBSP];int32_t numplanes;dplane_t * dplanes;//[MAX_MAP_PLANES_QBSP];int32_t numvertexes;dvertex_t * dvertexes;//[MAX_MAP_VERTS_QBSP];int32_t numnodes;dnode_t * dnodes;//[MAX_MAP_NODES];dnode_tx * dnodesX;//[MAX_MAP_NODES_QBSP];int32_t numtexinfo;texinfo_t * texinfo;//[MAX_MAP_TEXINFO_QBSP];int32_t numfaces;dface_t * dfaces;//[MAX_MAP_FACES];dface_tx * dfacesX;//[MAX_MAP_FACES_QBSP];int32_t numedges;dedge_t * dedges;//[MAX_MAP_EDGES];dedge_tx * dedgesX;//[MAX_MAP_EDGES_QBSP];int32_t numleaffaces;uint16_t * dleaffaces;//[MAX_MAP_LEAFFACES];uint32_t * dleaffacesX;//[MAX_MAP_LEAFFACES_QBSP];int32_t numleafbrushes;uint16_t * dleafbrushes;//[MAX_MAP_LEAFBRUSHES];uint32_t * dleafbrushesX;//[MAX_MAP_LEAFBRUSHES_QBSP];int32_t numsurfedges;int32_t * dsurfedges;//[MAX_MAP_SURFEDGES_QBSP];int32_t numbrushes;dbrush_t * dbrushes;//[MAX_MAP_BRUSHES_QBSP];int32_t numbrushsides;dbrushside_t * dbrushsides;//[MAX_MAP_BRUSHSIDES];dbrushside_tx * dbrushsidesX;//[MAX_MAP_BRUSHSIDES_QBSP];int32_t numareas;darea_t * dareas;//[MAX_MAP_AREAS];int32_t numareaportals;dareaportal_t * dareaportals;//[MAX_MAP_AREAPORTALS];byte dpop[256];void InitBSPFile(void) {static qboolean init = false;if(!init) {init = true;dmodels = (dmodel_t*)malloc(sizeof(*dmodels) * MAX_MAP_MODELS_QBSP);dvisdata = (byte*)malloc(sizeof(*dvisdata) * MAX_MAP_VISIBILITY_QBSP);dvis = (dvis_t *)dvisdata;dlightdata = (byte*)malloc(sizeof(*dlightdata) * MAX_MAP_LIGHTING_QBSP);dentdata = (char*)malloc(sizeof(*dentdata) * MAX_MAP_ENTSTRING_QBSP);dleafs = (dleaf_t*)malloc(sizeof(*dleafs) * MAX_MAP_LEAFS);dleafsX = (dleaf_tx*)malloc(sizeof(*dleafsX) * MAX_MAP_LEAFS_QBSP);dplanes = (dplane_t*)malloc(sizeof(*dplanes) * MAX_MAP_PLANES_QBSP);dvertexes = (dvertex_t*)malloc(sizeof(*dvertexes) * MAX_MAP_VERTS_QBSP);dnodes = (dnode_t*)malloc(sizeof(*dnodes) * MAX_MAP_NODES);dnodesX = (dnode_tx*)malloc(sizeof(*dnodesX) * MAX_MAP_NODES_QBSP);texinfo = (texinfo_t*)malloc(sizeof(*texinfo) * MAX_MAP_TEXINFO_QBSP);dfaces = (dface_t*)malloc(sizeof(*dfaces) * MAX_MAP_FACES);dfacesX = (dface_tx*)malloc(sizeof(*dfacesX) * MAX_MAP_FACES_QBSP);dedges = (dedge_t*)malloc(sizeof(*dedges) * MAX_MAP_EDGES);dedgesX = (dedge_tx*)malloc(sizeof(*dedgesX) * MAX_MAP_EDGES_QBSP);dleaffaces = (uint16_t*)malloc(sizeof(*dleaffaces) * MAX_MAP_LEAFFACES);dleaffacesX = (uint32_t*)malloc(sizeof(*dleaffacesX) * MAX_MAP_LEAFFACES_QBSP);dleafbrushes = (uint16_t*)malloc(sizeof(*dleafbrushes) * MAX_MAP_LEAFBRUSHES);dleafbrushesX = (uint32_t*)malloc(sizeof(*dleafbrushesX) * MAX_MAP_LEAFBRUSHES_QBSP);dsurfedges = (int32_t*)malloc(sizeof(*dsurfedges) * MAX_MAP_SURFEDGES_QBSP);dbrushes = (dbrush_t*)malloc(sizeof(*dbrushes) * MAX_MAP_BRUSHES_QBSP);dbrushsides = (dbrushside_t*)malloc(sizeof(*dbrushsides) * MAX_MAP_BRUSHSIDES);dbrushsidesX = (dbrushside_tx*)malloc(sizeof(*dbrushsidesX) * MAX_MAP_BRUSHSIDES_QBSP);dareas = (darea_t*)malloc(sizeof(*dareas) * MAX_MAP_AREAS);dareaportals = (dareaportal_t*)malloc(sizeof(*dareaportals) * MAX_MAP_AREAPORTALS);}}/*===============CompressVis===============*/int32_t CompressVis(byte *vis, byte *dest) {int32_t j;int32_t rep;int32_t visrow;byte *dest_p;dest_p = dest;// visrow = (r_numvisleafs + 7)>>3;visrow = (dvis->numclusters + 7) >> 3;for (j = 0; j < visrow; j++) {*dest_p++ = vis[j];if (vis[j])continue;rep = 1;for (j++; j < visrow; j++)if (vis[j] || rep == 255)break;elserep++;*dest_p++ = rep;j--;}return dest_p - dest;}/*===================DecompressVis===================*/void DecompressVis(byte *in, byte *decompressed) {int32_t c;byte *out;int32_t row;// row = (r_numvisleafs+7)>>3;row = (dvis->numclusters + 7) >> 3;out = decompressed;do {if (*in) {*out++ = *in++;continue;}c = in[1];if (!c)Error("DecompressVis: 0 repeat");in += 2;while (c) {*out++ = 0;c--;}} while (out - decompressed < row);}//=============================================================================/*=============SwapBSPFileByte swaps all data in a bsp file.=============*/void SwapBSPFile(qboolean todisk) {int32_t i, j;dmodel_t *d;// modelsfor (i = 0; i < nummodels; i++) {d = &dmodels[i];d->firstface = LittleLong(d->firstface);d->numfaces = LittleLong(d->numfaces);d->headnode = LittleLong(d->headnode);for (j = 0; j < 3; j++) {d->mins[j] = LittleFloat(d->mins[j]);d->maxs[j] = LittleFloat(d->maxs[j]);d->origin[j] = LittleFloat(d->origin[j]);}}//// vertexes//for (i = 0; i < numvertexes; i++) {for (j = 0; j < 3; j++)dvertexes[i].point[j] = LittleFloat(dvertexes[i].point[j]);}//// planes//for (i = 0; i < numplanes; i++) {for (j = 0; j < 3; j++)dplanes[i].normal[j] = LittleFloat(dplanes[i].normal[j]);dplanes[i].dist = LittleFloat(dplanes[i].dist);dplanes[i].type = LittleLong(dplanes[i].type);}//// texinfos//for (i = 0; i < numtexinfo; i++) {texinfo[i].flags = LittleLong(texinfo[i].flags);texinfo[i].value = LittleLong(texinfo[i].value);texinfo[i].nexttexinfo = LittleLong(texinfo[i].nexttexinfo);}//// faces nodes leafs brushsides leafbrushes leaffaces//if (use_qbsp) {for (i = 0; i < numfaces; i++) {dfacesX[i].texinfo = LittleLong(dfacesX[i].texinfo);dfacesX[i].planenum = LittleLong(dfacesX[i].planenum);dfacesX[i].side = LittleLong(dfacesX[i].side);dfacesX[i].lightofs = LittleLong(dfacesX[i].lightofs);dfacesX[i].firstedge = LittleLong(dfacesX[i].firstedge);dfacesX[i].numedges = LittleLong(dfacesX[i].numedges);}for (i = 0; i < numnodes; i++) {dnodesX[i].planenum = LittleLong(dnodesX[i].planenum);for (j = 0; j < 3; j++) {dnodesX[i].mins[j] = LittleFloat(dnodesX[i].mins[j]);dnodesX[i].maxs[j] = LittleFloat(dnodesX[i].maxs[j]);}dnodesX[i].children[0] = LittleLong(dnodesX[i].children[0]);dnodesX[i].children[1] = LittleLong(dnodesX[i].children[1]);dnodesX[i].firstface = LittleLong(dnodesX[i].firstface);dnodesX[i].numfaces = LittleLong(dnodesX[i].numfaces);}for (i = 0; i < numleafs; i++) {dleafsX[i].contents = LittleLong(dleafsX[i].contents);dleafsX[i].cluster = LittleLong(dleafsX[i].cluster);dleafsX[i].area = LittleLong(dleafsX[i].area);for (j = 0; j < 3; j++) {dleafsX[i].mins[j] = LittleFloat(dleafsX[i].mins[j]);dleafsX[i].maxs[j] = LittleFloat(dleafsX[i].maxs[j]);}dleafsX[i].firstleafface = LittleLong(dleafsX[i].firstleafface);dleafsX[i].numleaffaces = LittleLong(dleafsX[i].numleaffaces);dleafsX[i].firstleafbrush = LittleLong(dleafsX[i].firstleafbrush);dleafsX[i].numleafbrushes = LittleLong(dleafsX[i].numleafbrushes);}for (i = 0; i < numbrushsides; i++) {dbrushsidesX[i].planenum = LittleLong(dbrushsidesX[i].planenum);dbrushsidesX[i].texinfo = LittleLong(dbrushsidesX[i].texinfo);}for (i = 0; i < numleafbrushes; i++)dleafbrushesX[i] = LittleLong(dleafbrushesX[i]);for (i = 0; i < numleaffaces; i++)dleaffacesX[i] = LittleLong(dleaffacesX[i]);} else {for (i = 0; i < numfaces; i++) {dfaces[i].texinfo = LittleShort(dfaces[i].texinfo);dfaces[i].planenum = LittleShort(dfaces[i].planenum);dfaces[i].side = LittleShort(dfaces[i].side);dfaces[i].lightofs = LittleLong(dfaces[i].lightofs);dfaces[i].firstedge = LittleLong(dfaces[i].firstedge);dfaces[i].numedges = LittleShort(dfaces[i].numedges);}for (i = 0; i < numnodes; i++) {dnodes[i].planenum = LittleLong(dnodes[i].planenum);for (j = 0; j < 3; j++) {dnodes[i].mins[j] = LittleShort(dnodes[i].mins[j]);dnodes[i].maxs[j] = LittleShort(dnodes[i].maxs[j]);}dnodes[i].children[0] = LittleLong(dnodes[i].children[0]);dnodes[i].children[1] = LittleLong(dnodes[i].children[1]);dnodes[i].firstface = LittleShort(dnodes[i].firstface);dnodes[i].numfaces = LittleShort(dnodes[i].numfaces);}for (i = 0; i < numleafs; i++) {dleafs[i].contents = LittleLong(dleafs[i].contents);dleafs[i].cluster = LittleShort(dleafs[i].cluster);dleafs[i].area = LittleShort(dleafs[i].area);for (j = 0; j < 3; j++) {dleafs[i].mins[j] = LittleShort(dleafs[i].mins[j]);dleafs[i].maxs[j] = LittleShort(dleafs[i].maxs[j]);}dleafs[i].firstleafface = LittleShort(dleafs[i].firstleafface);dleafs[i].numleaffaces = LittleShort(dleafs[i].numleaffaces);dleafs[i].firstleafbrush = LittleShort(dleafs[i].firstleafbrush);dleafs[i].numleafbrushes = LittleShort(dleafs[i].numleafbrushes);}for (i = 0; i < numbrushsides; i++) {dbrushsides[i].planenum = LittleShort(dbrushsides[i].planenum);dbrushsides[i].texinfo = LittleShort(dbrushsides[i].texinfo);}for (i = 0; i < numleafbrushes; i++)dleafbrushes[i] = LittleShort(dleafbrushes[i]);for (i = 0; i < numleaffaces; i++)dleaffaces[i] = LittleShort(dleaffaces[i]);}//// surfedges//for (i = 0; i < numsurfedges; i++)dsurfedges[i] = LittleLong(dsurfedges[i]);//// edges//if (use_qbsp)for (i = 0; i < numedges; i++) {dedgesX[i].v[0] = LittleLong(dedgesX[i].v[0]);dedgesX[i].v[1] = LittleLong(dedgesX[i].v[1]);}elsefor (i = 0; i < numedges; i++) {dedges[i].v[0] = LittleShort(dedges[i].v[0]);dedges[i].v[1] = LittleShort(dedges[i].v[1]);}//// brushes//for (i = 0; i < numbrushes; i++) {dbrushes[i].firstside = LittleLong(dbrushes[i].firstside);dbrushes[i].numsides = LittleLong(dbrushes[i].numsides);dbrushes[i].contents = LittleLong(dbrushes[i].contents);}//// areas//for (i = 0; i < numareas; i++) {dareas[i].numareaportals = LittleLong(dareas[i].numareaportals);dareas[i].firstareaportal = LittleLong(dareas[i].firstareaportal);}//// areasportals//for (i = 0; i < numareaportals; i++) {dareaportals[i].portalnum = LittleLong(dareaportals[i].portalnum);dareaportals[i].otherarea = LittleLong(dareaportals[i].otherarea);}//// visibility//if (todisk)j = dvis->numclusters;elsej = LittleLong(dvis->numclusters);dvis->numclusters = LittleLong(dvis->numclusters);for (i = 0; i < j; i++) {dvis->bitofs[i][0] = LittleLong(dvis->bitofs[i][0]);dvis->bitofs[i][1] = LittleLong(dvis->bitofs[i][1]);}}dheader_t *header;int32_t CopyLump(int32_t lump, void *dest, int32_t size) {int32_t length, ofs;length = header->lumps[lump].filelen;ofs = header->lumps[lump].fileofs;if (length % size)Error("LoadBSPFile: odd lump size (length: %i size: %i remainder: %i)", length, size, length % size);memcpy(dest, (byte *)header + ofs, length);return length / size;}/*=============LoadBSPFile=============*/void LoadBSPFile(char *filename) {int32_t i;InitBSPFile();//// load the file header//LoadFile(filename, (void **)&header);// swap the headerfor (i = 0; i < sizeof(dheader_t) / 4; i++)((int32_t *)header)[i] = LittleLong(((int32_t *)header)[i]);// qb: qbspuse_qbsp = false;switch (header->ident) {case IDBSPHEADER:break;case QBSPHEADER:use_qbsp = true;printf("using QBSP extended limits \n");break;default:Error("%s is not a recognized BSP file (IBSP or QBSP).",filename);}if (header->version != BSPVERSION)Error("%s is version %i, not %i", filename, header->version, BSPVERSION);nummodels = CopyLump(LUMP_MODELS, dmodels, sizeof(dmodel_t));numvertexes = CopyLump(LUMP_VERTEXES, dvertexes, sizeof(dvertex_t));numplanes = CopyLump(LUMP_PLANES, dplanes, sizeof(dplane_t));if (use_qbsp) {numleafs = CopyLump(LUMP_LEAFS, dleafsX, sizeof(dleaf_tx));numnodes = CopyLump(LUMP_NODES, dnodesX, sizeof(dnode_tx));numtexinfo = CopyLump(LUMP_TEXINFO, texinfo, sizeof(texinfo_t));numfaces = CopyLump(LUMP_FACES, dfacesX, sizeof(dface_tx));numleaffaces = CopyLump(LUMP_LEAFFACES, dleaffacesX, sizeof(dleaffacesX[0]));numleafbrushes = CopyLump(LUMP_LEAFBRUSHES, dleafbrushesX, sizeof(dleafbrushesX[0]));} else {numleafs = CopyLump(LUMP_LEAFS, dleafs, sizeof(dleaf_t));numnodes = CopyLump(LUMP_NODES, dnodes, sizeof(dnode_t));numtexinfo = CopyLump(LUMP_TEXINFO, texinfo, sizeof(texinfo_t));numfaces = CopyLump(LUMP_FACES, dfaces, sizeof(dface_t));numleaffaces = CopyLump(LUMP_LEAFFACES, dleaffaces, sizeof(dleaffaces[0]));numleafbrushes = CopyLump(LUMP_LEAFBRUSHES, dleafbrushes, sizeof(dleafbrushes[0]));}numsurfedges = CopyLump(LUMP_SURFEDGES, dsurfedges, sizeof(dsurfedges[0]));if (use_qbsp)numedges = CopyLump(LUMP_EDGES, dedgesX, sizeof(dedge_tx));elsenumedges = CopyLump(LUMP_EDGES, dedges, sizeof(dedge_t));numbrushes = CopyLump(LUMP_BRUSHES, dbrushes, sizeof(dbrush_t));if (use_qbsp)numbrushsides = CopyLump(LUMP_BRUSHSIDES, dbrushsidesX, sizeof(dbrushside_tx));elsenumbrushsides = CopyLump(LUMP_BRUSHSIDES, dbrushsides, sizeof(dbrushside_t));numareas = CopyLump(LUMP_AREAS, dareas, sizeof(darea_t));numareaportals = CopyLump(LUMP_AREAPORTALS, dareaportals, sizeof(dareaportal_t));visdatasize = CopyLump(LUMP_VISIBILITY, dvisdata, 1);lightdatasize = CopyLump(LUMP_LIGHTING, dlightdata, 1);entdatasize = CopyLump(LUMP_ENTITIES, dentdata, 1);CopyLump(LUMP_POP, dpop, 1);free(header); // everything has been copied out//// swap everything//SwapBSPFile(false);}/*=============LoadBSPFileTexinfoOnly loads the texinfo lump, so 4data can scan for textures=============*/void LoadBSPFileTexinfo(char *filename) {int32_t i;FILE *f;int32_t length, ofs;header = malloc(sizeof(dheader_t));f = fopen(filename, "rb");if (!fread(header, sizeof(dheader_t), 1, f))Error("Texinfo header read error");// swap the headerfor (i = 0; i < sizeof(dheader_t) / 4; i++)((int32_t *)header)[i] = LittleLong(((int32_t *)header)[i]);if (header->ident != IDBSPHEADER && header->ident != QBSPHEADER)Error("%s is not an IBSP or QBSP file", filename);if (header->version != BSPVERSION)Error("%s is version %i, not %i", filename, header->version, BSPVERSION);length = header->lumps[LUMP_TEXINFO].filelen;ofs = header->lumps[LUMP_TEXINFO].fileofs;fseek(f, ofs, SEEK_SET);if (!fread(texinfo, length, 1, f))Error("Texinfo lump read error");fclose(f);numtexinfo = length / sizeof(texinfo_t);free(header); // everything has been copied outSwapBSPFile(false);}//============================================================================FILE *wadfile;dheader_t outheader;void AddLump(int32_t lumpnum, void *data, int32_t len) {lump_t *lump;lump = &header->lumps[lumpnum];lump->fileofs = LittleLong(ftell(wadfile));lump->filelen = LittleLong(len);SafeWrite(wadfile, data, (len + 3) & ~3);}/*=============WriteBSPFileSwaps the bsp file in place, so it should not be referenced again=============*/void WriteBSPFile(char *filename) {header = &outheader;memset(header, 0, sizeof(dheader_t));SwapBSPFile(true);if (use_qbsp)header->ident = LittleLong(QBSPHEADER);elseheader->ident = LittleLong(IDBSPHEADER);header->version = LittleLong(BSPVERSION);wadfile = SafeOpenWrite(filename);SafeWrite(wadfile, header, sizeof(dheader_t)); // overwritten laterAddLump(LUMP_PLANES, dplanes, numplanes * sizeof(dplane_t));if (use_qbsp)AddLump(LUMP_LEAFS, dleafsX, numleafs * sizeof(dleaf_tx));elseAddLump(LUMP_LEAFS, dleafs, numleafs * sizeof(dleaf_t));AddLump(LUMP_VERTEXES, dvertexes, numvertexes * sizeof(dvertex_t));if (use_qbsp)AddLump(LUMP_NODES, dnodesX, numnodes * sizeof(dnode_tx));elseAddLump(LUMP_NODES, dnodes, numnodes * sizeof(dnode_t));AddLump(LUMP_TEXINFO, texinfo, numtexinfo * sizeof(texinfo_t));if (use_qbsp)AddLump(LUMP_FACES, dfacesX, numfaces * sizeof(dface_tx));elseAddLump(LUMP_FACES, dfaces, numfaces * sizeof(dface_t));AddLump(LUMP_BRUSHES, dbrushes, numbrushes * sizeof(dbrush_t));if (use_qbsp) {AddLump(LUMP_BRUSHSIDES, dbrushsidesX, numbrushsides * sizeof(dbrushside_tx));AddLump(LUMP_LEAFFACES, dleaffacesX, numleaffaces * sizeof(dleaffacesX[0]));AddLump(LUMP_LEAFBRUSHES, dleafbrushesX, numleafbrushes * sizeof(dleafbrushesX[0]));} else {AddLump(LUMP_BRUSHSIDES, dbrushsides, numbrushsides * sizeof(dbrushside_t));AddLump(LUMP_LEAFFACES, dleaffaces, numleaffaces * sizeof(dleaffaces[0]));AddLump(LUMP_LEAFBRUSHES, dleafbrushes, numleafbrushes * sizeof(dleafbrushes[0]));}AddLump(LUMP_SURFEDGES, dsurfedges, numsurfedges * sizeof(dsurfedges[0]));if (use_qbsp)AddLump(LUMP_EDGES, dedgesX, numedges * sizeof(dedge_tx));elseAddLump(LUMP_EDGES, dedges, numedges * sizeof(dedge_t));AddLump(LUMP_MODELS, dmodels, nummodels * sizeof(dmodel_t));AddLump(LUMP_AREAS, dareas, numareas * sizeof(darea_t));AddLump(LUMP_AREAPORTALS, dareaportals, numareaportals * sizeof(dareaportal_t));AddLump(LUMP_LIGHTING, dlightdata, lightdatasize);AddLump(LUMP_VISIBILITY, dvisdata, visdatasize);AddLump(LUMP_ENTITIES, dentdata, entdatasize);AddLump(LUMP_POP, dpop, sizeof(dpop));fseek(wadfile, 0, SEEK_SET);SafeWrite(wadfile, header, sizeof(dheader_t));fclose(wadfile);}//============================================================================/*=============PrintBSPFileSizesDumps info about current file=============*/void PrintBSPFileSizes(void) {if (!num_entities)ParseEntities();printf("\n<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< FILE STATS >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n");printf("models: %7i size: %7i\n", nummodels, (int32_t)(nummodels * sizeof(dmodel_t)));printf("brushes: %7i size: %7i\n", numbrushes, (int32_t)(numbrushes * sizeof(dbrush_t)));if (use_qbsp)printf("brushsides: %7i size: %7i\n", numbrushsides, (int32_t)(numbrushsides * sizeof(dbrushside_tx)));elseprintf("brushsides: %7i size: %7i\n", numbrushsides, (int32_t)(numbrushsides * sizeof(dbrushside_t)));printf("planes: %7i size: %7i\n", numplanes, (int32_t)(numplanes * sizeof(dplane_t)));printf("texinfo: %7i size: %7i\n", numtexinfo, (int32_t)(numtexinfo * sizeof(texinfo_t)));printf("entdata: %7i size: %7i\n", num_entities, entdatasize);printf("vertices: %7i size: %7i\n", numvertexes, (int32_t)(numvertexes * sizeof(dvertex_t)));if (use_qbsp) {printf("nodes: %7i size: %7i\n", numnodes, (int32_t)(numnodes * sizeof(dnode_tx)));printf("faces: %7i size: %7i\n", numfaces, (int32_t)(numfaces * sizeof(dface_tx)));printf("leafs: %7i size: %7i\n", numleafs, (int32_t)(numleafs * sizeof(dleaf_tx)));printf("leaffaces: %7i size: %7i\n", numleaffaces, (int32_t)(numleaffaces * sizeof(dleaffacesX[0])));printf("leafbrushes: %7i size: %7i\n", numleafbrushes, (int32_t)(numleafbrushes * sizeof(dleafbrushesX[0])));printf("edges: %7i size: %7i\n", numedges, (int32_t)(numedges * sizeof(dedge_tx)));} else {printf("nodes: %7i size: %7i\n", numnodes, (int32_t)(numnodes * sizeof(dnode_t)));printf("faces: %7i size: %7i\n", numfaces, (int32_t)(numfaces * sizeof(dface_t)));printf("leafs: %7i size: %7i\n", numleafs, (int32_t)(numleafs * sizeof(dleaf_t)));printf("leaffaces: %7i size: %7i\n", numleaffaces, (int32_t)(numleaffaces * sizeof(dleaffaces[0])));printf("leafbrushes: %7i size: %7i\n", numleafbrushes, (int32_t)(numleafbrushes * sizeof(dleafbrushes[0])));printf("edges: %7i size: %7i\n", numedges, (int32_t)(numedges * sizeof(dedge_t)));}printf("surfedges: %7i size: %7i\n", numsurfedges, (int32_t)(numsurfedges * sizeof(dsurfedges[0])));printf(" lightdata size: %7i\n", lightdatasize);printf(" visdata size: %7i\n", visdatasize);}//============================================int32_t num_entities;entity_t entities[MAX_MAP_ENTITIES_QBSP];void StripTrailing(char *e) {char *s;s = e + strlen(e) - 1;while (s >= e && *s <= 32) {*s = 0;s--;}}/*=================ParseEpair=================*/epair_t *ParseEpair(void) {epair_t *e;e = malloc(sizeof(epair_t));memset(e, 0, sizeof(epair_t));if (strlen(token) >= MAX_KEY - 1)Error("ParseEpar: token too long");e->key = copystring(token);GetToken(false);if (strlen(token) >= MAX_VALUE - 1)Error("ParseEpar: token too long");e->value = copystring(token);// strip trailing spacesStripTrailing(e->key);StripTrailing(e->value);return e;}/*================ParseEntity================*/qboolean ParseEntity(void) {epair_t *e;entity_t *mapent;if (!GetToken(true))return false;if (strcmp(token, "{"))Error("ParseEntity: { not found");if (use_qbsp) {if (num_entities == MAX_MAP_ENTITIES_QBSP)Error("num_entities == MAX_MAP_ENTITIES_QBSP (%i)", MAX_MAP_ENTITIES_QBSP);} else if (num_entities == MAX_MAP_ENTITIES)Error("num_entities == MAX_MAP_ENTITIES (%i)", MAX_MAP_ENTITIES);mapent = &entities[num_entities];num_entities++;do {if (!GetToken(true))Error("ParseEntity: EOF without closing brace");if (!strcmp(token, "}"))break;e = ParseEpair();e->next = mapent->epairs;mapent->epairs = e;} while (1);return true;}/*================ParseEntitiesParses the dentdata string into entities================*/void ParseEntities(void) {num_entities = 0;ParseFromMemory(dentdata, entdatasize);while (ParseEntity()) {}}/*================UnparseEntitiesGenerates the dentdata string from all the entities================*/void UnparseEntities(void) {char *buf, *end;epair_t *ep;char line[2060];int32_t i;char key[1024], value[1024];buf = dentdata;end = buf;*end = 0;for (i = 0; i < num_entities; i++) {ep = entities[i].epairs;if (!ep)continue; // ent got removedstrcat(end, "{\n");end += 2;for (ep = entities[i].epairs; ep; ep = ep->next) {strcpy(key, ep->key);StripTrailing(key);strcpy(value, ep->value);StripTrailing(value);sprintf(line, "\"%s\" \"%s\"\n", key, value);strcat(end, line);end += strlen(line);}strcat(end, "}\n");end += 2;if (use_qbsp) {if (end > buf + MAX_MAP_ENTSTRING_QBSP)Error("QBSP Entity text too long");} else if (end > buf + MAX_MAP_ENTSTRING)Error("Entity text too long");}entdatasize = end - buf + 1;}void PrintEntity(entity_t *ent) {epair_t *ep;printf("------- entity %p -------\n", ent);for (ep = ent->epairs; ep; ep = ep->next) {printf("%s = %s\n", ep->key, ep->value);}}void SetKeyValue(entity_t *ent, char *key, char *value) {epair_t *ep;for (ep = ent->epairs; ep; ep = ep->next)if (!strcmp(ep->key, key)) {free(ep->value);ep->value = copystring(value);return;}ep = malloc(sizeof(*ep));if (ep) // qb: gcc -fanalyzer: could be NULL{ep->next = ent->epairs;ent->epairs = ep;ep->key = copystring(key);ep->value = copystring(value);}}char *ValueForKey(entity_t *ent, char *key) {epair_t *ep;for (ep = ent->epairs; ep; ep = ep->next)if (!strcmp(ep->key, key))return ep->value;return "";}vec_t FloatForKey(entity_t *ent, char *key) {char *k;k = ValueForKey(ent, key);return atof(k);}void GetVectorForKey(entity_t *ent, char *key, vec3_t vec) {char *k;double v1, v2, v3;k = ValueForKey(ent, key);// scanf into doubles, then assign, so it is vec_t size independentv1 = v2 = v3 = 0;sscanf(k, "%lf %lf %lf", &v1, &v2, &v3);vec[0] = v1;vec[1] = v2;vec[2] = v3;}void RemoveLastEpair(entity_t *ent) {epair_t *e = ent->epairs->next;if (ent->epairs->key)free(ent->epairs->key);if (ent->epairs->value)free(ent->epairs->value);free(ent->epairs);ent->epairs = e;}
GNU GENERAL PUBLIC LICENSEVersion 2, June 1991Copyright (C) 1989, 1991 Free Software Foundation, Inc.,51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USAEveryone is permitted to copy and distribute verbatim copiesof this license document, but changing it is not allowed.PreambleThe licenses for most software are designed to take away yourfreedom to share and change it. By contrast, the GNU General PublicLicense is intended to guarantee your freedom to share and change freesoftware--to make sure the software is free for all its users. ThisGeneral Public License applies to most of the Free SoftwareFoundation's software and to any other program whose authors commit tousing it. (Some other Free Software Foundation software is covered bythe GNU Lesser General Public License instead.) You can apply it toyour programs, too.When we speak of free software, we are referring to freedom, notprice. Our General Public Licenses are designed to make sure that youhave the freedom to distribute copies of free software (and charge forthis service if you wish), that you receive source code or can get itif you want it, that you can change the software or use pieces of itin new free programs; and that you know you can do these things.To protect your rights, we need to make restrictions that forbidanyone to deny you these rights or to ask you to surrender the rights.These restrictions translate to certain responsibilities for you if youdistribute copies of the software, or if you modify it.For example, if you distribute copies of such a program, whethergratis or for a fee, you must give the recipients all the rights thatyou have. You must make sure that they, too, receive or can get thesource code. And you must show them these terms so they know theirrights.We protect your rights with two steps: (1) copyright the software, and(2) offer you this license which gives you legal permission to copy,distribute and/or modify the software.Also, for each author's protection and ours, we want to make certainthat everyone understands that there is no warranty for this freesoftware. If the software is modified by someone else and passed on, wewant its recipients to know that what they have is not the original, sothat any problems introduced by others will not reflect on the originalauthors' reputations.Finally, any free program is threatened constantly by softwarepatents. We wish to avoid the danger that redistributors of a freeprogram will individually obtain patent licenses, in effect making theprogram proprietary. To prevent this, we have made it clear that anypatent must be licensed for everyone's free use or not licensed at all.The precise terms and conditions for copying, distribution andmodification follow.GNU GENERAL PUBLIC LICENSETERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION0. This License applies to any program or other work which containsa notice placed by the copyright holder saying it may be distributedunder the terms of this General Public License. The "Program", below,refers to any such program or work, and a "work based on the Program"means either the Program or any derivative work under copyright law:that is to say, a work containing the Program or a portion of it,either verbatim or with modifications and/or translated into anotherlanguage. (Hereinafter, translation is included without limitation inthe term "modification".) Each licensee is addressed as "you".Activities other than copying, distribution and modification are notcovered by this License; they are outside its scope. The act ofrunning the Program is not restricted, and the output from the Programis covered only if its contents constitute a work based on theProgram (independent of having been made by running the Program).Whether that is true depends on what the Program does.1. You may copy and distribute verbatim copies of the Program'ssource code as you receive it, in any medium, provided that youconspicuously and appropriately publish on each copy an appropriatecopyright notice and disclaimer of warranty; keep intact all thenotices that refer to this License and to the absence of any warranty;and give any other recipients of the Program a copy of this Licensealong with the Program.You may charge a fee for the physical act of transferring a copy, andyou may at your option offer warranty protection in exchange for a fee.2. You may modify your copy or copies of the Program or any portionof it, thus forming a work based on the Program, and copy anddistribute such modifications or work under the terms of Section 1above, provided that you also meet all of these conditions:a) You must cause the modified files to carry prominent noticesstating that you changed the files and the date of any change.b) You must cause any work that you distribute or publish, that inwhole or in part contains or is derived from the Program or anypart thereof, to be licensed as a whole at no charge to all thirdparties under the terms of this License.c) If the modified program normally reads commands interactivelywhen run, you must cause it, when started running for suchinteractive use in the most ordinary way, to print or display anannouncement including an appropriate copyright notice and anotice that there is no warranty (or else, saying that you providea warranty) and that users may redistribute the program underthese conditions, and telling the user how to view a copy of thisLicense. (Exception: if the Program itself is interactive butdoes not normally print such an announcement, your work based onthe Program is not required to print an announcement.)These requirements apply to the modified work as a whole. Ifidentifiable sections of that work are not derived from the Program,and can be reasonably considered independent and separate works inthemselves, then this License, and its terms, do not apply to thosesections when you distribute them as separate works. But when youdistribute the same sections as part of a whole which is a work basedon the Program, the distribution of the whole must be on the terms ofthis License, whose permissions for other licensees extend to theentire whole, and thus to each and every part regardless of who wrote it.Thus, it is not the intent of this section to claim rights or contestyour rights to work written entirely by you; rather, the intent is toexercise the right to control the distribution of derivative orcollective works based on the Program.In addition, mere aggregation of another work not based on the Programwith the Program (or with a work based on the Program) on a volume ofa storage or distribution medium does not bring the other work underthe scope of this License.3. You may copy and distribute the Program (or a work based on it,under Section 2) in object code or executable form under the terms ofSections 1 and 2 above provided that you also do one of the following:a) Accompany it with the complete corresponding machine-readablesource code, which must be distributed under the terms of Sections1 and 2 above on a medium customarily used for software interchange; or,b) Accompany it with a written offer, valid for at least threeyears, to give any third party, for a charge no more than yourcost of physically performing source distribution, a completemachine-readable copy of the corresponding source code, to bedistributed under the terms of Sections 1 and 2 above on a mediumcustomarily used for software interchange; or,c) Accompany it with the information you received as to the offerto distribute corresponding source code. (This alternative isallowed only for noncommercial distribution and only if youreceived the program in object code or executable form with suchan offer, in accord with Subsection b above.)The source code for a work means the preferred form of the work formaking modifications to it. For an executable work, complete sourcecode means all the source code for all modules it contains, plus anyassociated interface definition files, plus the scripts used tocontrol compilation and installation of the executable. However, as aspecial exception, the source code distributed need not includeanything that is normally distributed (in either source or binaryform) with the major components (compiler, kernel, and so on) of theoperating system on which the executable runs, unless that componentitself accompanies the executable.If distribution of executable or object code is made by offeringaccess to copy from a designated place, then offering equivalentaccess to copy the source code from the same place counts asdistribution of the source code, even though third parties are notcompelled to copy the source along with the object code.4. You may not copy, modify, sublicense, or distribute the Programexcept as expressly provided under this License. Any attemptotherwise to copy, modify, sublicense or distribute the Program isvoid, and will automatically terminate your rights under this License.However, parties who have received copies, or rights, from you underthis License will not have their licenses terminated so long as suchparties remain in full compliance.5. You are not required to accept this License, since you have notsigned it. However, nothing else grants you permission to modify ordistribute the Program or its derivative works. These actions areprohibited by law if you do not accept this License. Therefore, bymodifying or distributing the Program (or any work based on theProgram), you indicate your acceptance of this License to do so, andall its terms and conditions for copying, distributing or modifyingthe Program or works based on it.6. Each time you redistribute the Program (or any work based on theProgram), the recipient automatically receives a license from theoriginal licensor to copy, distribute or modify the Program subject tothese terms and conditions. You may not impose any furtherrestrictions on the recipients' exercise of the rights granted herein.You are not responsible for enforcing compliance by third parties tothis License.7. If, as a consequence of a court judgment or allegation of patentinfringement or for any other reason (not limited to patent issues),conditions are imposed on you (whether by court order, agreement orotherwise) that contradict the conditions of this License, they do notexcuse you from the conditions of this License. If you cannotdistribute so as to satisfy simultaneously your obligations under thisLicense and any other pertinent obligations, then as a consequence youmay not distribute the Program at all. For example, if a patentlicense would not permit royalty-free redistribution of the Program byall those who receive copies directly or indirectly through you, thenthe only way you could satisfy both it and this License would be torefrain entirely from distribution of the Program.If any portion of this section is held invalid or unenforceable underany particular circumstance, the balance of the section is intended toapply and the section as a whole is intended to apply in othercircumstances.It is not the purpose of this section to induce you to infringe anypatents or other property right claims or to contest validity of anysuch claims; this section has the sole purpose of protecting theintegrity of the free software distribution system, which isimplemented by public license practices. Many people have madegenerous contributions to the wide range of software distributedthrough that system in reliance on consistent application of thatsystem; it is up to the author/donor to decide if he or she is willingto distribute software through any other system and a licensee cannotimpose that choice.This section is intended to make thoroughly clear what is believed tobe a consequence of the rest of this License.8. If the distribution and/or use of the Program is restricted incertain countries either by patents or by copyrighted interfaces, theoriginal copyright holder who places the Program under this Licensemay add an explicit geographical distribution limitation excludingthose countries, so that distribution is permitted only in or amongcountries not thus excluded. In such case, this License incorporatesthe limitation as if written in the body of this License.9. The Free Software Foundation may publish revised and/or new versionsof the General Public License from time to time. Such new versions willbe similar in spirit to the present version, but may differ in detail toaddress new problems or concerns.Each version is given a distinguishing version number. If the Programspecifies a version number of this License which applies to it and "anylater version", you have the option of following the terms and conditionseither of that version or of any later version published by the FreeSoftware Foundation. If the Program does not specify a version number ofthis License, you may choose any version ever published by the Free SoftwareFoundation.10. If you wish to incorporate parts of the Program into other freeprograms whose distribution conditions are different, write to the authorto ask for permission. For software which is copyrighted by the FreeSoftware Foundation, write to the Free Software Foundation; we sometimesmake exceptions for this. Our decision will be guided by the two goalsof preserving the free status of all derivatives of our free software andof promoting the sharing and reuse of software generally.NO WARRANTY11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTYFOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHENOTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIESPROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSEDOR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OFMERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK ASTO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THEPROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,REPAIR OR CORRECTION.12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITINGWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/ORREDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISINGOUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITEDTO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BYYOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHERPROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THEPOSSIBILITY OF SUCH DAMAGES.END OF TERMS AND CONDITIONSHow to Apply These Terms to Your New ProgramsIf you develop a new program, and you want it to be of the greatestpossible use to the public, the best way to achieve this is to make itfree software which everyone can redistribute and change under these terms.To do so, attach the following notices to the program. It is safestto attach them to the start of each source file to most effectivelyconvey the exclusion of warranty; and each file should have at leastthe "copyright" line and a pointer to where the full notice is found.<one line to give the program's name and a brief idea of what it does.>Copyright (C) <year> <name of author>This program is free software; you can redistribute it and/or modifyit under the terms of the GNU General Public License as published bythe 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 ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See theGNU General Public License for more details.You should have received a copy of the GNU General Public License alongwith this program; if not, write to the Free Software Foundation, Inc.,51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.Also add information on how to contact you by electronic and paper mail.If the program is interactive, make it output a short notice like thiswhen it starts in an interactive mode:Gnomovision version 69, Copyright (C) year name of authorGnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.This is free software, and you are welcome to redistribute itunder certain conditions; type `show c' for details.The hypothetical commands `show w' and `show c' should show the appropriateparts of the General Public License. Of course, the commands you use maybe called something other than `show w' and `show c'; they could even bemouse-clicks or menu items--whatever suits your program.You should also get your employer (if you work as a programmer) or yourschool, if any, to sign a "copyright disclaimer" for the program, ifnecessary. Here is a sample; alter the names:Yoyodyne, Inc., hereby disclaims all copyright interest in the program`Gnomovision' (which makes passes at compilers) written by James Hacker.<signature of Ty Coon>, 1 April 1989Ty Coon, President of ViceThis General Public License does not permit incorporating your program intoproprietary programs. If your program is a subroutine library, you mayconsider it more useful to permit linking proprietary applications with thelibrary. If this is what you want to do, use the GNU Lesser GeneralPublic License instead of this License.
/*===========================================================================Copyright (C) 1997-2006 Id Software, Inc.This program is free software; you can redistribute it and/or modifyit under the terms of the GNU General Public License as published bythe 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 ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See theGNU General Public License for more details.You should have received a copy of the GNU General Public License alongwith this program; if not, write to the Free Software Foundation, Inc.,51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.===========================================================================*/#include "vis.h"/*each portal will have a list of all possible to see from first portalif (!thread->portalmightsee[portalnum])portal mightseefor p2 = all other portals in leafget sperating planesfor all portals that might be seen by p2mark as unseen if not present in seperating planeflood fill a new mightseesave as passagemightseevoid CalcMightSee (leaf_t *leaf,*/int32_t CountBits(byte *bits, int32_t numbits) {int32_t i;int32_t c;c = 0;for (i = 0; i < numbits; i++)if (bits[i >> 3] & (1 << (i & 7)))c++;return c;}int32_t c_fullskip;int32_t c_portalskip, c_leafskip;int32_t c_vistest, c_mighttest;int32_t c_chop, c_nochop;int32_t active;void CheckStack(leaf_t *leaf, threaddata_t *thread) {pstack_t *p, *p2;for (p = thread->pstack_head.next; p; p = p->next) {// printf ("=");if (p->leaf == leaf)Error("CheckStack: leaf recursion");for (p2 = thread->pstack_head.next; p2 != p; p2 = p2->next)if (p2->leaf == p->leaf)Error("CheckStack: late leaf recursion");}// printf ("\n");}winding_t *AllocStackWinding(pstack_t *stack) {int32_t i;for (i = 0; i < 3; i++) {if (stack->freewindings[i]) {stack->freewindings[i] = 0;return &stack->windings[i];}}Error("AllocStackWinding: failed");return NULL;}void FreeStackWinding(winding_t *w, pstack_t *stack) {int32_t i;i = w - stack->windings;if (i < 0 || i > 2)return; // not from localif (stack->freewindings[i])Error("FreeStackWinding: allready free");stack->freewindings[i] = 1;}/*==============ChopWinding_flow==============*/winding_t *ChopWinding_flow(winding_t *in, pstack_t *stack, plane_t *split) // qb: renamed, dupe in polylib.c{vec_t dists[128];int32_t sides[128];int32_t counts[3];vec_t dot;int32_t i, j;vec_t *p1, *p2;vec3_t mid;winding_t *neww;counts[0] = counts[1] = counts[2] = 0;// determine sides for each pointfor (i = 0; i < in->numpoints; i++) {dot = DotProduct(in->points[i], split->normal);dot -= split->dist;dists[i] = dot;if (dot > ON_EPSILON)sides[i] = SIDE_FRONT;else if (dot < -ON_EPSILON)sides[i] = SIDE_BACK;else {sides[i] = SIDE_ON;}counts[sides[i]]++;}if (!counts[1])return in; // completely on front sideif (!counts[0]) {FreeStackWinding(in, stack);return NULL;}sides[i] = sides[0];dists[i] = dists[0];neww = AllocStackWinding(stack);neww->numpoints = 0;for (i = 0; i < in->numpoints; i++) {p1 = in->points[i];if (neww->numpoints == MAX_POINTS_ON_FIXED_WINDING) {FreeStackWinding(neww, stack);return in; // can't chop -- fall back to original}if (sides[i] == SIDE_ON) {VectorCopy(p1, neww->points[neww->numpoints]);neww->numpoints++;continue;}if (sides[i] == SIDE_FRONT) {VectorCopy(p1, neww->points[neww->numpoints]);neww->numpoints++;}if (sides[i + 1] == SIDE_ON || sides[i + 1] == sides[i])continue;if (neww->numpoints == MAX_POINTS_ON_FIXED_WINDING) {FreeStackWinding(neww, stack);return in; // can't chop -- fall back to original}// generate a split pointp2 = in->points[(i + 1) % in->numpoints];dot = dists[i] / (dists[i] - dists[i + 1]);for (j = 0; j < 3; j++) { // avoid round off error when possibleif (split->normal[j] == 1)mid[j] = split->dist;else if (split->normal[j] == -1)mid[j] = -split->dist;elsemid[j] = p1[j] + dot * (p2[j] - p1[j]);}VectorCopy(mid, neww->points[neww->numpoints]);neww->numpoints++;}// free the original windingFreeStackWinding(in, stack);return neww;}/*==============ClipToSeperatorsSource, pass, and target are an ordering of portals.Generates seperating planes canidates by taking two points from source and onepoint from pass, and clips target by them.If target is totally clipped away, that portal can not be seen through.Normal clip keeps target on the same side as pass, which is correct if theorder goes source, pass, target. If the order goes pass, source, target thenflipclip should be set.==============*/winding_t *ClipToSeperators(winding_t *source, winding_t *pass, winding_t *target, qboolean flipclip, pstack_t *stack) {int32_t i, j, k, l;plane_t plane;vec3_t v1, v2;float d;vec_t length;int32_t counts[3];qboolean fliptest;// check all combinationsfor (i = 0; i < source->numpoints; i++) {l = (i + 1) % source->numpoints;VectorSubtract(source->points[l], source->points[i], v1);// fing a vertex of pass that makes a plane that puts all of the// vertexes of pass on the front side and all of the vertexes of// source on the back sidefor (j = 0; j < pass->numpoints; j++) {VectorSubtract(pass->points[j], source->points[i], v2);plane.normal[0] = v1[1] * v2[2] - v1[2] * v2[1];plane.normal[1] = v1[2] * v2[0] - v1[0] * v2[2];plane.normal[2] = v1[0] * v2[1] - v1[1] * v2[0];// if points don't make a valid plane, skip itlength = plane.normal[0] * plane.normal[0] + plane.normal[1] * plane.normal[1] + plane.normal[2] * plane.normal[2];if (length < ON_EPSILON)continue;length = 1 / sqrt(length);plane.normal[0] *= length;plane.normal[1] *= length;plane.normal[2] *= length;plane.dist = DotProduct(pass->points[j], plane.normal);//// find out which side of the generated seperating plane has the// source portal//#if 1fliptest = false;for (k = 0; k < source->numpoints; k++) {if (k == i || k == l)continue;d = DotProduct(source->points[k], plane.normal) - plane.dist;if (d < -ON_EPSILON) { // source is on the negative side, so we want all// pass and target on the positive sidefliptest = false;break;} else if (d > ON_EPSILON) { // source is on the positive side, so we want all// pass and target on the negative sidefliptest = true;break;}}if (k == source->numpoints)continue; // planar with source portal#elsefliptest = flipclip;#endif//// flip the normal if the source portal is backwards//if (fliptest) {VectorSubtract(vec3_origin, plane.normal, plane.normal);plane.dist = -plane.dist;}#if 1//// if all of the pass portal points are now on the positive side,// this is the seperating plane//counts[0] = counts[1] = counts[2] = 0;for (k = 0; k < pass->numpoints; k++) {if (k == j)continue;d = DotProduct(pass->points[k], plane.normal) - plane.dist;if (d < -ON_EPSILON)break;else if (d > ON_EPSILON)counts[0]++;elsecounts[2]++;}if (k != pass->numpoints)continue; // points on negative side, not a seperating planeif (!counts[0])continue; // planar with seperating plane#elsek = (j + 1) % pass->numpoints;d = DotProduct(pass->points[k], plane.normal) - plane.dist;if (d < -ON_EPSILON)continue;k = (j + pass->numpoints - 1) % pass->numpoints;d = DotProduct(pass->points[k], plane.normal) - plane.dist;if (d < -ON_EPSILON)continue;#endif//// flip the normal if we want the back side//if (flipclip) {VectorSubtract(vec3_origin, plane.normal, plane.normal);plane.dist = -plane.dist;}//// clip target by the seperating plane//target = ChopWinding_flow(target, stack, &plane);if (!target)return NULL; // target is not visible}}return target;}/*==================RecursiveLeafFlowFlood fill through the leafsIf src_portal is NULL, this is the originating leaf==================*/void RecursiveLeafFlow(int32_t leafnum, threaddata_t *thread, pstack_t *prevstack) {pstack_t stack;portal_t *p;plane_t backplane;leaf_t *leaf;int32_t i, j;long *test, *might, *vis, more;int32_t pnum;thread->c_chains++;leaf = &leafs[leafnum];// CheckStack (leaf, thread);prevstack->next = &stack;stack.next = NULL;stack.leaf = leaf;stack.portal = NULL;might = (long *)stack.mightsee;vis = (long *)thread->base->portalvis;// check all portals for flowing into other leafsfor (i = 0; i < leaf->numportals; i++) {p = leaf->portals[i];pnum = p - portals;if (!(prevstack->mightsee[pnum >> 3] & (1 << (pnum & 7)))) {continue; // can't possibly see it}// if the portal can't see anything we haven't allready seen, skip itif (p->status == stat_done) {test = (long *)p->portalvis;} else {test = (long *)p->portalflood;}more = 0;for (j = 0; j < portallongs; j++) {might[j] = ((long *)prevstack->mightsee)[j] & test[j];more |= (might[j] & ~vis[j]);}if (!more &&(thread->base->portalvis[pnum >> 3] & (1 << (pnum & 7)))) { // can't see anything newcontinue;}// get plane of portal, point normal into the neighbor leafstack.portalplane = p->plane;VectorSubtract(vec3_origin, p->plane.normal, backplane.normal);backplane.dist = -p->plane.dist;// c_portalcheck++;stack.portal = p;stack.next = NULL;stack.freewindings[0] = 1;stack.freewindings[1] = 1;stack.freewindings[2] = 1;#if 1{float d;d = DotProduct(p->origin, thread->pstack_head.portalplane.normal);d -= thread->pstack_head.portalplane.dist;if (d < -p->radius) {continue;} else if (d > p->radius) {stack.pass = p->winding;} else {stack.pass = ChopWinding_flow(p->winding, &stack, &thread->pstack_head.portalplane);if (!stack.pass)continue;}}#elsestack.pass = ChopWinding_flow(p->winding, &stack, &thread->pstack_head.portalplane);if (!stack.pass)continue;#endif#if 1{float d;d = DotProduct(thread->base->origin, p->plane.normal);d -= p->plane.dist;// if (d > p->radius) qb: GDD tools fixif (d > thread->base->radius) {continue;}// else if (d < -p->radius)else if (d < -thread->base->radius) {stack.source = prevstack->source;} else {stack.source = ChopWinding_flow(prevstack->source, &stack, &backplane);if (!stack.source)continue;}}#elsestack.source = ChopWinding_flow(prevstack->source, &stack, &backplane);if (!stack.source)continue;#endifif (!prevstack->pass) { // the second leaf can only be blocked if coplanar// mark the portal as visiblethread->base->portalvis[pnum >> 3] |= (1 << (pnum & 7));RecursiveLeafFlow(p->leaf, thread, &stack);continue;}stack.pass = ClipToSeperators(stack.source, prevstack->pass, stack.pass, false, &stack);if (!stack.pass)continue;stack.pass = ClipToSeperators(prevstack->pass, stack.source, stack.pass, true, &stack);if (!stack.pass)continue;// mark the portal as visiblethread->base->portalvis[pnum >> 3] |= (1 << (pnum & 7));// flow through it for realRecursiveLeafFlow(p->leaf, thread, &stack);}}/*===============PortalFlowgenerates the portalvis bit vector===============*/void PortalFlow(int32_t portalnum) {threaddata_t data;int32_t i;portal_t *p;int32_t c_might, c_can;p = sorted_portals[portalnum];p->status = stat_working;c_might = CountBits(p->portalflood, numportals * 2);memset(&data, 0, sizeof(data));data.base = p;data.pstack_head.portal = p;data.pstack_head.source = p->winding;data.pstack_head.portalplane = p->plane;for (i = 0; i < portallongs; i++)((long *)data.pstack_head.mightsee)[i] = ((long *)p->portalflood)[i];RecursiveLeafFlow(p->leaf, &data, &data.pstack_head);p->status = stat_done;c_can = CountBits(p->portalvis, numportals * 2);qprintf("portal:%4i mightsee:%4i cansee:%4i (%i chains)\n",(int32_t)(p - portals), c_might, c_can, data.c_chains);}/*===============================================================================This is a rough first-order aproximation that is used to trivially reject someof the final calculations.Calculates portalfront and portalflood bit vectorsthinking about:typedef struct passage_s{struct passage_s *next;struct portal_s *to;stryct sep_s *seperators;byte *mightsee;} passage_t;typedef struct portal_s{struct passage_s *passages;int32_t leaf; // leaf portal faces into} portal_s;leaf = portal->leafclearfor all portalscalc portal visibilityclear bit vectorfor all passagespassage visibilityfor a portal to be visible to a passage, it must be on the front ofall seperating planes, and both portals must be behind the mew portal===============================================================================*/int32_t c_flood, c_vis;char test_leaf[MAX_MAP_LEAFS_QBSP];/*==================SimpleFlood==================*/void SimpleFlood(portal_t *srcportal, int32_t leafnum) {int32_t i;leaf_t *leaf;portal_t *p;int32_t pnum;leaf = &leafs[leafnum];for (i = 0; i < leaf->numportals; i++) {p = leaf->portals[i];pnum = p - portals;if (!(srcportal->portalfront[pnum >> 3] & (1 << (pnum & 7))))continue;if (srcportal->portalflood[pnum >> 3] & (1 << (pnum & 7)))continue;srcportal->portalflood[pnum >> 3] |= (1 << (pnum & 7));SimpleFlood(srcportal, p->leaf);}}/*==============BasePortalVis==============*/void BasePortalVis(int32_t portalnum) {int32_t j, k;portal_t *tp, *p;float d;winding_t *w;p = portals + portalnum;p->portalfront = malloc(portalbytes);memset(p->portalfront, 0, portalbytes);p->portalflood = malloc(portalbytes);memset(p->portalflood, 0, portalbytes);p->portalvis = malloc(portalbytes);memset(p->portalvis, 0, portalbytes);for (j = 0, tp = portals; j < numportals * 2; j++, tp++) {if (j == portalnum)continue;w = tp->winding;for (k = 0; k < w->numpoints; k++) {d = DotProduct(w->points[k], p->plane.normal) - p->plane.dist;if (d > ON_EPSILON)break;}if (k == w->numpoints)continue; // no points on frontw = p->winding;for (k = 0; k < w->numpoints; k++) {d = DotProduct(w->points[k], tp->plane.normal) - tp->plane.dist;if (d < -ON_EPSILON)break;}if (k == w->numpoints)continue; // no points on frontp->portalfront[j >> 3] |= (1 << (j & 7));}SimpleFlood(p, p->leaf);p->nummightsee = CountBits(p->portalflood, numportals * 2);// printf ("portal %i: %i mightsee\n", portalnum, p->nummightsee);c_flood += p->nummightsee;}/*===============================================================================This is a second order aproximationCalculates portalvis bit vectorWAAAAAAY too slow.===============================================================================*//*==================RecursiveLeafBitFlow==================*/void RecursiveLeafBitFlow(int32_t leafnum, byte *mightsee, byte *cansee) {portal_t *p;leaf_t *leaf;int32_t i, j;long more;int32_t pnum;byte newmight[MAX_MAP_PORTALS_QBSP / 8];leaf = &leafs[leafnum];// check all portals for flowing into other leafsfor (i = 0; i < leaf->numportals; i++) {p = leaf->portals[i];pnum = p - portals;// if some previous portal can't see it, skipif (!(mightsee[pnum >> 3] & (1 << (pnum & 7))))continue;// if this portal can see some portals we mightsee, recursemore = 0;for (j = 0; j < portallongs; j++) {((long *)newmight)[j] = ((long *)mightsee)[j] & ((long *)p->portalflood)[j];more |= ((long *)newmight)[j] & ~((long *)cansee)[j];}if (!more)continue; // can't see anything newcansee[pnum >> 3] |= (1 << (pnum & 7));RecursiveLeafBitFlow(p->leaf, newmight, cansee);}}/*==============BetterPortalVis==============*/void BetterPortalVis(int32_t portalnum) {portal_t *p;p = portals + portalnum;RecursiveLeafBitFlow(p->leaf, p->portalflood, p->portalvis);// build leaf vis informationp->nummightsee = CountBits(p->portalvis, numportals * 2);c_vis += p->nummightsee;}
#include "stdlib.h"#include "vis.h"#include "threads.h"extern qboolean fastvis;extern qboolean nosort;extern char inbase[32];extern char outbase[32];void VIS_ProcessArgument(const char * arg);int32_t main(int32_t argc, char **argv) {int32_t i;char tgamedir[1024] = "", tbasedir[1024] = "", tmoddir[1024] = "";printf("\n\n<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< 4vis >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n");printf("visibility compiler build " __DATE__ "\n");verbose = false;for (i = 1; i < argc; i++) {if (!strcmp(argv[i], "-threads")) {numthreads = atoi(argv[i + 1]);i++;} else if (!strcmp(argv[i], "-help")) {printf("usage: 4vis [options] [mapname]\n\n"" -fast: uses 'might see' for a quick loose bound\n"" -threads #: number of CPU threads to use\n"" -tmpin: read map from 'tmp' folder\n"" -tmpout: write map to 'tmp' folder\n"" -moddir [path]: Set a mod directory. Default is parent dir of map file.\n"" -basedir [path]: Set the directory for assets not in moddir. Default is moddir.\n"" -gamedir [path]: Set game directory, the folder with game executable.\n"" -v: extra verbose console output\n\n");printf("<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< 4vis HELP >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n\n");exit(1);} else if (!strcmp(argv[i], "-fast")) {printf("fastvis = true\n");fastvis = true;} else if (!strcmp(argv[i], "-v")) {printf("verbose = true\n");verbose = true;} else if (!strcmp(argv[i], "-nosort")) {printf("nosort = true\n");nosort = true;}// qb: set gamedir and basedirelse if (!strcmp(argv[i], "-gamedir")) {strcpy(tgamedir, argv[i + 1]);i++;} else if (!strcmp(argv[i], "-basedir")) {strcpy(tbasedir, argv[i + 1]);i++;} else if (!strcmp(argv[i], "-moddir")) {strcpy(tmoddir, argv[i + 1]);i++;}else if (!strcmp(argv[i], "-tmpin"))strcpy(inbase, "/tmp");else if (!strcmp(argv[i], "-tmpout"))strcpy(outbase, "/tmp");else if (argv[i][0] == '-')Error("Unknown option \"%s\"", argv[i]);elsebreak;}if (i != argc - 1) {printf("usage: 4vis [options] mapfile\n"" -fast -help -threads #\n"" -basedir [path] -gamedir [path] \n"" -tmpin -tmpout -v (verbose)\n\n");exit(1);}ThreadSetDefault();SetQdirFromPath(argv[i]);if (strcmp(tmoddir, "")) {strcpy(moddir, tmoddir);Q_pathslash(moddir);strcpy(basedir, moddir);}if (strcmp(tbasedir, "")) {strcpy(basedir, tbasedir);Q_pathslash(basedir);if (!strcmp(tmoddir, ""))strcpy(moddir, basedir);}if (strcmp(tgamedir, "")) {strcpy(gamedir, tgamedir);Q_pathslash(gamedir);}// qb: display dirsprintf("moddir = %s\n", moddir);printf("basedir = %s\n", basedir);printf("gamedir = %s\n", gamedir);VIS_ProcessArgument(argv[i]);return 0;}
/*===========================================================================Copyright (C) 1997-2006 Id Software, Inc.This program is free software; you can redistribute it and/or modifyit under the terms of the GNU General Public License as published bythe 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 ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See theGNU General Public License for more details.You should have received a copy of the GNU General Public License alongwith this program; if not, write to the Free Software Foundation, Inc.,51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.===========================================================================*/#include "vis.h"#include "threads.h"#include "stdlib.h"extern qboolean use_qbsp;int32_t numportals;int32_t portalclusters;portal_t *portals;leaf_t *leafs;int32_t c_portaltest, c_portalpass, c_portalcheck;byte *uncompressedvis;byte *vismap, *vismap_p, *vismap_end; // past visfileint32_t originalvismapsize;int32_t leafbytes; // (portalclusters+63)>>3int32_t leaflongs;int32_t portalbytes, portallongs;qboolean fastvis;qboolean nosort;int32_t totalvis;portal_t *sorted_portals[MAX_MAP_PORTALS_QBSP * 2];extern char inbase[32];extern char outbase[32];//=============================================================================void PlaneFromWinding(winding_t *w, plane_t *plane) {vec3_t v1, v2;// calc planeVectorSubtract(w->points[2], w->points[1], v1);VectorSubtract(w->points[0], w->points[1], v2);CrossProduct(v2, v1, plane->normal);VectorNormalize(plane->normal, plane->normal);plane->dist = DotProduct(w->points[0], plane->normal);}/*==================NewWinding==================*/winding_t *NewWinding(int32_t points) {winding_t *w;int32_t size;if (points > MAX_POINTS_ON_WINDING)Error("NewWinding: %i points", points);size = (intptr_t)((winding_t *)0)->points[points];w = malloc(size);memset(w, 0, size);return w;}void prl(leaf_t *l) {int32_t i;portal_t *p;plane_t pl;for (i = 0; i < l->numportals; i++) {p = l->portals[i];pl = p->plane;printf("portal %4i to leaf %4i : %7.1f : (%4.1f, %4.1f, %4.1f)\n", (int32_t)(p - portals), p->leaf, pl.dist, pl.normal[0], pl.normal[1], pl.normal[2]);}}//=============================================================================/*=============SortPortalsSorts the portals from the least complex, so the later ones can reusethe earlier information.=============*/int32_t PComp(const void *a, const void *b) {if ((*(portal_t **)a)->nummightsee == (*(portal_t **)b)->nummightsee)return 0;if ((*(portal_t **)a)->nummightsee < (*(portal_t **)b)->nummightsee)return -1;return 1;}void SortPortals(void) {int32_t i;for (i = 0; i < numportals * 2; i++)sorted_portals[i] = &portals[i];if (nosort)return;qsort(sorted_portals, numportals * 2, sizeof(sorted_portals[0]), PComp);}/*==============LeafVectorFromPortalVector==============*/int32_t LeafVectorFromPortalVector(byte *portalbits, byte *leafbits) {int32_t i;portal_t *p;int32_t c_leafs;memset(leafbits, 0, leafbytes);for (i = 0; i < numportals * 2; i++) {if (portalbits[i >> 3] & (1 << (i & 7))) {p = portals + i;leafbits[p->leaf >> 3] |= (1 << (p->leaf & 7));}}c_leafs = CountBits(leafbits, portalclusters);return c_leafs;}/*===============ClusterMergeMerges the portal visibility for a leaf===============*/void ClusterMerge(int32_t leafnum) {leaf_t *leaf;byte portalvector[MAX_PORTALS_QBSP / 8];byte uncompressed[MAX_MAP_LEAFS_QBSP / 8];byte compressed[MAX_MAP_LEAFS_QBSP / 8];int32_t i, j;int32_t numvis;byte *dest;portal_t *p;int32_t pnum;// OR together all the portalvis bitsmemset(portalvector, 0, portalbytes);leaf = &leafs[leafnum];for (i = 0; i < leaf->numportals; i++) {p = leaf->portals[i];if (p->status != stat_done)Error("portal not done");for (j = 0; j < portallongs; j++)((long *)portalvector)[j] |= ((long *)p->portalvis)[j];pnum = p - portals;portalvector[pnum >> 3] |= 1 << (pnum & 7);}// convert portal bits to leaf bitsnumvis = LeafVectorFromPortalVector(portalvector, uncompressed);if (uncompressed[leafnum >> 3] & (1 << (leafnum & 7)))printf("WARNING: Leaf portals saw into leaf\n");uncompressed[leafnum >> 3] |= (1 << (leafnum & 7));numvis++; // count the leaf itself// save uncompressed for PHS calculationmemcpy(uncompressedvis + leafnum * leafbytes, uncompressed, leafbytes);//// compress the bit string//qprintf("cluster %4i : %4i visible\n", leafnum, numvis);totalvis += numvis;i = CompressVis(uncompressed, compressed);dest = vismap_p;vismap_p += i;if (vismap_p > vismap_end)Error("Vismap expansion overflow. Exceeds extended limit");dvis->bitofs[leafnum][DVIS_PVS] = dest - vismap;memcpy(dest, compressed, i);}/*==================CalcPortalVis==================*/void CalcPortalVis(void) {int32_t i;// fastvis just uses mightsee for a very loose boundif (fastvis) {for (i = 0; i < numportals * 2; i++) {portals[i].portalvis = portals[i].portalflood;portals[i].status = stat_done;}return;}RunThreadsOnIndividual(numportals * 2, true, PortalFlow);}/*==================CalcVis==================*/void CalcVis(void) {int32_t i;RunThreadsOnIndividual(numportals * 2, true, BasePortalVis);// RunThreadsOnIndividual (numportals*2, true, BetterPortalVis);SortPortals();CalcPortalVis();//// assemble the leaf vis lists by oring and compressing the portal lists//for (i = 0; i < portalclusters; i++)ClusterMerge(i);printf("Average clusters visible: %i\n", totalvis / portalclusters);}void SetPortalSphere(portal_t *p) {int32_t i;vec3_t total, dist;winding_t *w;float r, bestr;w = p->winding;VectorCopy(vec3_origin, total);for (i = 0; i < w->numpoints; i++) {VectorAdd(total, w->points[i], total);}for (i = 0; i < 3; i++)total[i] /= w->numpoints;bestr = 0;for (i = 0; i < w->numpoints; i++) {VectorSubtract(w->points[i], total, dist);r = VectorLength(dist);if (r > bestr)bestr = r;}VectorCopy(total, p->origin);p->radius = bestr;}/*============LoadPortals============*/void LoadPortals(char *name) {int32_t i, j;portal_t *p;leaf_t *l;char magic[80];FILE *f;int32_t numpoints;winding_t *w;int32_t leafnums[2];plane_t plane;if (!strcmp(name, "-"))f = stdin;else {f = fopen(name, "r");if (!f)Error("LoadPortals: couldn't read %s\n", name);}if (fscanf(f, "%79s\n%i\n%i\n", magic, &portalclusters, &numportals) != 3)Error("LoadPortals: failed to read header");if (strcmp(magic, PORTALFILE))Error("LoadPortals: not a portal file");printf("%4i portalclusters\n", portalclusters);printf("%4i numportals\n", numportals);// these counts should take advantage of 64 bit systems automaticallyleafbytes = ((portalclusters + 63) & ~63) >> 3;leaflongs = leafbytes / sizeof(long);portalbytes = ((numportals * 2 + 63) & ~63) >> 3;portallongs = portalbytes / sizeof(long);// each file portal is split into two memory portalsportals = malloc(2 * numportals * sizeof(portal_t));memset(portals, 0, 2 * numportals * sizeof(portal_t));leafs = malloc(portalclusters * sizeof(leaf_t));memset(leafs, 0, portalclusters * sizeof(leaf_t));originalvismapsize = portalclusters * leafbytes;uncompressedvis = malloc(originalvismapsize);vismap = vismap_p = dvisdata;dvis->numclusters = portalclusters;vismap_p = (byte *)&dvis->bitofs[portalclusters];vismap_end = vismap + MAX_MAP_VISIBILITY_QBSP;for (i = 0, p = portals; i < numportals; i++) {if (fscanf(f, "%i %i %i ", &numpoints, &leafnums[0], &leafnums[1]) != 3)Error("LoadPortals: reading portal %i", i);if (numpoints > MAX_POINTS_ON_WINDING)Error("LoadPortals: portal %i has too many points", i);if ((unsigned)leafnums[0] > portalclusters || (unsigned)leafnums[1] > portalclusters)Error("LoadPortals: reading portal %i", i);w = p->winding = NewWinding(numpoints);w->original = true;w->numpoints = numpoints;for (j = 0; j < numpoints; j++) {double v[3];int32_t k;// scanf into double, then assign to vec_t// so we don't care what size vec_t isif (fscanf(f, "(%lf %lf %lf ) ", &v[0], &v[1], &v[2]) != 3)Error("LoadPortals: reading portal %i", i);for (k = 0; k < 3; k++)w->points[j][k] = v[k];}fscanf(f, "\n");// calc planePlaneFromWinding(w, &plane);// create forward portall = &leafs[leafnums[0]];if (l->numportals == MAX_PORTALS_ON_LEAF)Error("Leaf with too many portals");l->portals[l->numportals] = p;l->numportals++;p->winding = w;VectorSubtract(vec3_origin, plane.normal, p->plane.normal);p->plane.dist = -plane.dist;p->leaf = leafnums[1];SetPortalSphere(p);p++;// create backwards portall = &leafs[leafnums[1]];if (l->numportals == MAX_PORTALS_ON_LEAF)Error("Leaf with too many portals");l->portals[l->numportals] = p;l->numportals++;p->winding = NewWinding(w->numpoints);p->winding->numpoints = w->numpoints;for (j = 0; j < w->numpoints; j++) {VectorCopy(w->points[w->numpoints - 1 - j], p->winding->points[j]);}p->plane = plane;p->leaf = leafnums[0];SetPortalSphere(p);p++;}fclose(f);}/*================CalcPHSCalculate the PHS (Potentially Hearable Set)by ORing together all the PVS visible from a leaf================*/void CalcPHS(void) {int32_t i, j, k, l, index;int32_t bitbyte;long *dest, *src;byte *scan;int32_t count;byte uncompressed[MAX_MAP_LEAFS_QBSP / 8];byte compressed[MAX_MAP_LEAFS_QBSP / 8];printf("Building PHS...\n");count = 0;for (i = 0; i < portalclusters; i++) {scan = uncompressedvis + i * leafbytes;memcpy(uncompressed, scan, leafbytes);for (j = 0; j < leafbytes; j++) {bitbyte = scan[j];if (!bitbyte)continue;for (k = 0; k < 8; k++) {if (!(bitbyte & (1 << k)))continue;// OR this pvs row into the phsindex = ((j << 3) + k);if (index >= portalclusters)Error("Bad bit in PVS"); // pad bits should be 0src = (long *)(uncompressedvis + index * leafbytes);dest = (long *)uncompressed;for (l = 0; l < leaflongs; l++)((long *)uncompressed)[l] |= src[l];}}for (j = 0; j < portalclusters; j++)if (uncompressed[j >> 3] & (1 << (j & 7)))count++;//// compress the bit string//j = CompressVis(uncompressed, compressed);dest = (long *)vismap_p;vismap_p += j;if (vismap_p > vismap_end)Error("Vismap expansion overflow. Exceeds extended limit");dvis->bitofs[i][DVIS_PHS] = (byte *)dest - vismap;memcpy(dest, compressed, j);}printf("Average clusters hearable: %i\n", count / portalclusters);}/*===========main===========*/void VIS_ProcessArgument(const char * arg) {char portalfile[1024];char source[1024];char name[1060];strcpy(source, ExpandArg(arg));StripExtension(source);DefaultExtension(source, ".bsp");sprintf(name, "%s%s", inbase, source);printf("reading %s\n", name);LoadBSPFile(name);if (numnodes == 0 || numfaces == 0)Error("Empty map");sprintf(portalfile, "%s%s", inbase, ExpandArg(arg));StripExtension(portalfile);strcat(portalfile, ".prt");printf("reading %s\n", portalfile);LoadPortals(portalfile);CalcVis();CalcPHS();visdatasize = vismap_p - dvisdata;printf("visdatasize: %i compressed from %i\n", visdatasize, originalvismapsize * 2);if (!use_qbsp && vismap_p > (vismap + DEFAULT_MAP_VISIBILITY))printf("\nWARNING: visdatasize exceeds default limit of %i\n\n", DEFAULT_MAP_VISIBILITY);sprintf(name, "%s%s", outbase, source);WriteBSPFile(name);PrintBSPFileSizes();printf("<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< END 4vis >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n\n");}
/*===========================================================================Copyright (C) 1997-2006 Id Software, Inc.This program is free software; you can redistribute it and/or modifyit under the terms of the GNU General Public License as published bythe 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 ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See theGNU General Public License for more details.You should have received a copy of the GNU General Public License alongwith this program; if not, write to the Free Software Foundation, Inc.,51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.===========================================================================*/#include "qrad.h"#include <assert.h>typedef struct tnode_s {int32_t type;vec3_t normal;float dist;int32_t children[2];int32_t children_leaf[2]; // valid if the corresponding child is a leafint32_t pad;} tnode_t;tnode_t *tnodes, *tnode_p;/*==============MakeTnodeConverts the disk node structure into the efficient tracing structure==============*/static int32_t tnode_mask;void MakeTnode(int32_t nodenum) {tnode_t *t;dplane_t *plane;int32_t i;t = tnode_p++;if (use_qbsp) {dnode_tx *node;node = dnodesX + nodenum;plane = dplanes + node->planenum;t->type = plane->type;VectorCopy(plane->normal, t->normal);t->dist = plane->dist;for (i = 0; i < 2; i++) {if (node->children[i] < 0) {t->children[i] = (dleafsX[-node->children[i] - 1].contents & tnode_mask) | (1 << 31);t->children_leaf[i] = -node->children[i] - 1;} else {t->children[i] = tnode_p - tnodes;MakeTnode(node->children[i]);}}} else {dnode_t *node;node = dnodes + nodenum;plane = dplanes + node->planenum;t->type = plane->type;VectorCopy(plane->normal, t->normal);t->dist = plane->dist;for (i = 0; i < 2; i++) {if (node->children[i] < 0) {t->children[i] = (dleafs[-node->children[i] - 1].contents & tnode_mask) | (1 << 31);t->children_leaf[i] = -node->children[i] - 1;} else {t->children[i] = tnode_p - tnodes;MakeTnode(node->children[i]);}}}}/*=============MakeTnodesLoads the node structure out of a .bsp file to be used for light occlusion=============*/void MakeTnodes(dmodel_t *bm) {// 32 byte align the structstnodes = malloc((numnodes + 1) * sizeof(tnode_t));tnodes = (tnode_t *)(((intptr_t)tnodes + 31) & ~31);tnode_p = tnodes;tnode_mask = CONTENTS_SOLID | CONTENTS_WINDOW;// TODO: or-in CONTENTS_WINDOW in response to a command-line argumentMakeTnode(0);}//==========================================================/*=============BuildPolygonsApplies the same basic algorithm as r_surf.c:BSP_BuildPolygonFromSurface toevery surface in the BSP, precomputing the xyz and st coordinates of eachvertex of each polygon. Skip non-translucent surfaces for now.=============*/int32_t PointInNodenum(vec3_t point) {int32_t nodenum, oldnodenum;vec_t dist;dplane_t *plane;nodenum = 0;if (use_qbsp) {dnode_tx *node;while (nodenum >= 0) {node = &dnodesX[nodenum];plane = &dplanes[node->planenum];dist = DotProduct(point, plane->normal) - plane->dist;oldnodenum = nodenum;if (dist > 0)nodenum = node->children[0];elsenodenum = node->children[1];}} else {dnode_t *node;while (nodenum >= 0) {node = &dnodes[nodenum];plane = &dplanes[node->planenum];dist = DotProduct(point, plane->normal) - plane->dist;oldnodenum = nodenum;if (dist > 0)nodenum = node->children[0];elsenodenum = node->children[1];}}return oldnodenum;}int32_t TestLine_r(int32_t node, vec3_t set_start, vec3_t stop) {tnode_t *tnode;float front, back;vec3_t mid, _start;vec_t *start;float frac;int32_t side;int32_t r;start = set_start;re_test:r = 0;if (node & (1 << 31)) {if ((r = node & ~(1 << 31)) != CONTENTS_WINDOW) {return r;}return 0;}tnode = &tnodes[node];switch (tnode->type) {case PLANE_X:front = start[0] - tnode->dist;back = stop[0] - tnode->dist;break;case PLANE_Y:front = start[1] - tnode->dist;back = stop[1] - tnode->dist;break;case PLANE_Z:front = start[2] - tnode->dist;back = stop[2] - tnode->dist;break;default:front = (start[0] * tnode->normal[0] + start[1] * tnode->normal[1] + start[2] * tnode->normal[2]) - tnode->dist;back = (stop[0] * tnode->normal[0] + stop[1] * tnode->normal[1] + stop[2] * tnode->normal[2]) - tnode->dist;break;}if (front >= -ON_EPSILON && back >= -ON_EPSILON) {node = tnode->children[0];goto re_test;}if (front < ON_EPSILON && back < ON_EPSILON) {node = tnode->children[1];goto re_test;}side = front < 0;frac = front / (front - back);mid[0] = start[0] + (stop[0] - start[0]) * frac;mid[1] = start[1] + (stop[1] - start[1]) * frac;mid[2] = start[2] + (stop[2] - start[2]) * frac;if ((r = TestLine_r(tnode->children[side], start, mid)))return r;node = tnode->children[!side];start = _start;start[0] = mid[0];start[1] = mid[1];start[2] = mid[2];goto re_test;}int32_t TestLine(vec3_t start, vec3_t stop) {vec3_t occluded;occluded[0] = occluded[1] = occluded[2] = 1.0;return TestLine_r(0, start, stop);}int32_t TestLine_color(int32_t node, vec3_t start, vec3_t stop, vec3_t occluded) {occluded[0] = occluded[1] = occluded[2] = 1.0;return TestLine_r(node, start, stop);}/*==============================================================================LINE TRACINGThe major lighting operation is a point to point visibility test, performedby recursive subdivision of the line by the BSP tree.==============================================================================*/typedef struct{vec3_t backpt;int32_t side;int32_t node;} tracestack_t;/*==============TestLine==============*/qboolean _TestLine(vec3_t start, vec3_t stop) {int32_t node;float front, back;tracestack_t *tstack_p;int32_t side;float frontx, fronty, frontz, backx, backy, backz;tracestack_t tracestack[64];tnode_t *tnode;frontx = start[0];fronty = start[1];frontz = start[2];backx = stop[0];backy = stop[1];backz = stop[2];tstack_p = tracestack;node = 0;while (1) {if (node == CONTENTS_SOLID) {#if 0float d1, d2, d3;d1 = backx - frontx;d2 = backy - fronty;d3 = backz - frontz;if (d1*d1 + d2*d2 + d3*d3 > 1)#endifreturn false; // DONE!}while (node < 0) {// pop up the stack for a back sidetstack_p--;if (tstack_p < tracestack)return true;node = tstack_p->node;// set the hit point for this planefrontx = backx;fronty = backy;frontz = backz;// go down the back sidebackx = tstack_p->backpt[0];backy = tstack_p->backpt[1];backz = tstack_p->backpt[2];node = tnodes[tstack_p->node].children[!tstack_p->side];}tnode = &tnodes[node];switch (tnode->type) {case PLANE_X:front = frontx - tnode->dist;back = backx - tnode->dist;break;case PLANE_Y:front = fronty - tnode->dist;back = backy - tnode->dist;break;case PLANE_Z:front = frontz - tnode->dist;back = backz - tnode->dist;break;default:front = (frontx * tnode->normal[0] + fronty * tnode->normal[1] + frontz * tnode->normal[2]) - tnode->dist;back = (backx * tnode->normal[0] + backy * tnode->normal[1] + backz * tnode->normal[2]) - tnode->dist;break;}if (front > -ON_EPSILON && back > -ON_EPSILON)// if (front > 0 && back > 0){node = tnode->children[0];continue;}if (front < ON_EPSILON && back < ON_EPSILON)// if (front <= 0 && back <= 0){node = tnode->children[1];continue;}side = front < 0;front = front / (front - back);tstack_p->node = node;tstack_p->side = side;tstack_p->backpt[0] = backx;tstack_p->backpt[1] = backy;tstack_p->backpt[2] = backz;tstack_p++;backx = frontx + front * (backx - frontx);backy = fronty + front * (backy - fronty);backz = frontz + front * (backz - frontz);node = tnode->children[side];}}
/*===========================================================================Copyright (C) 1997-2006 Id Software, Inc.This program is free software; you can redistribute it and/or modifyit under the terms of the GNU General Public License as published bythe 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 ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See theGNU General Public License for more details.You should have received a copy of the GNU General Public License alongwith this program; if not, write to the Free Software Foundation, Inc.,51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.===========================================================================*/#include "qrad.h"vec3_t texture_reflectivity[MAX_MAP_TEXINFO_QBSP];int32_t cluster_neg_one = 0;int32_t texture_sizes[MAX_MAP_TEXINFO_QBSP][2];/*===================================================================TEXTURE LIGHT VALUES===================================================================*//*======================CalcTextureReflectivity======================*/void CalcTextureReflectivity(void) {int32_t i, j, k, count;int32_t texels, texel;qboolean wal_tex;float color[3], cur_color[3], tex_a, a;char path[1200];float *r, *g, *b;float c;byte *pbuffer = NULL; // mxd. "potentially uninitialized local pointer variable" in VS2017 if uninitializedbyte *palette_frompak = NULL;byte *ptexel;byte *palette;miptex_t *mt = NULL; // mxd. "potentially uninitialized local pointer variable" in VS2017 if uninitializedfloat *fbuffer, *ftexel;int32_t width, height;// for TGA RGBA texture imageswal_tex = false;// get the game palette// qb: looks in moddir then basedirsprintf(path, "%spics/colormap.pcx", moddir);if(FileExists(path)) {Load256Image(path, NULL, &palette, NULL, NULL);} else {sprintf(path, "%spics/colormap.pcx", basedir);if(FileExists(path)) {Load256Image(path, NULL, &palette, NULL, NULL);} else if((i = TryLoadFileFromPak("pics/colormap.pcx", (void **)&palette_frompak, moddir)) != -1) {// unicat: load from pack files, palette is loaded from the last 768 bytespalette = palette_frompak - (i - 768);} else {Error("unable to load pics/colormap.pcx");}}// always set index 0 even if no texturestexture_reflectivity[0][0] = 0.5;texture_reflectivity[0][1] = 0.5;texture_reflectivity[0][2] = 0.5;for(i = 0; i < numtexinfo; i++) {// defaulttexture_reflectivity[i][0] = 0.5f;texture_reflectivity[i][1] = 0.5f;texture_reflectivity[i][2] = 0.5f;// see if an earlier texinfo already got the valuefor(j = 0; j < i; j++) {if(!strcmp(texinfo[i].texture, texinfo[j].texture)) {VectorCopy(texture_reflectivity[j], texture_reflectivity[i]);texture_sizes[i][0] = texture_sizes[j][0];texture_sizes[i][1] = texture_sizes[j][1];break;}}if(j != i)continue;// buffer is RGBA (A set to 255 for 24 bit format)// qb: looks in moddir then basedirsprintf(path, "%stextures/%s.tga", moddir, texinfo[i].texture);if(FileExists(path)) // LoadTGA expects file to exist{LoadTGA(path, &pbuffer, &width, &height); // load rgba dataqprintf("load %s\n", path);} else {// look for wal file in moddirsprintf(path, "%stextures/%s.wal", moddir, texinfo[i].texture);qprintf("attempting %s\n", path);// load the miptex to get the flags and valuesif(FileExists(path)) // qb: linux segfault if not exist{if(TryLoadFile(path, (void **)&mt, false) != -1)wal_tex = true;} else {// look for TGA in basedir sprintf(path, "%stextures/%s.tga", basedir, texinfo[i].texture);if(FileExists(path)) {LoadTGA(path, &pbuffer, &width, &height); // load rgba dataqprintf("load %s\n", path);} else {// look for wal file in base dirsprintf(path, "%stextures/%s.wal", basedir, texinfo[i].texture);qprintf("load %s\n", path);// load the miptex to get the flags and valuesif(FileExists(path)) // qb: linux segfault if not exist{if(TryLoadFile(path, (void **)&mt, false) != -1)wal_tex = true;} else {qprintf("NOT FOUND %s\n", path);continue;}}}}//// Calculate the "average color" for the texture//if(wal_tex) {texels = LittleLong(mt->width) * LittleLong(mt->height);color[0] = color[1] = color[2] = 0;for(j = 0; j < texels; j++) {texel = ((byte *)mt)[LittleLong(mt->offsets[0]) + j];for(k = 0; k < 3; k++)color[k] += palette[texel * 3 + k];}} else {texels = width * height;if(texels <= 0) {qprintf("tex %i (%s) no rgba data (file broken?)\n", i, path);continue; // empty texture, possible bad file}color[0] = color[1] = color[2] = 0.0f;ptexel = pbuffer;// fbuffer = malloc(texels * 4 * sizeof(float));// ftexel = fbuffer;for(count = texels; count--;) {cur_color[0] = (float)(*ptexel++); // rcur_color[1] = (float)(*ptexel++); // gcur_color[2] = (float)(*ptexel++); // btex_a = (float)(*ptexel++);if(texinfo[i].flags & (SURF_WARP | SURF_NODRAW)) {a = 0.0;} else if((texinfo[i].flags & SURF_TRANS33) && (texinfo[i].flags & SURF_TRANS66)) {a = tex_a / 511.0;} else if(texinfo[i].flags & SURF_TRANS33) {a = tex_a / 765.0;} else if(texinfo[i].flags & SURF_TRANS66) {a = tex_a / 382.5;} else {a = 1.0;}for(j = 0; j < 3; j++) {color[j] += cur_color[j] * a;}}free(pbuffer);}for(j = 0; j < 3; j++) {// average RGB for the texture to 0.0..1.0 rangec = color[j] / (float)texels / 255.0f;texture_reflectivity[i][j] = c;}// reflectivity saturation#define Pr .299#define Pg .587#define Pb .114// public-domain function by Darel Rex Finley//// The passed-in RGB values can be on any desired scale, such as 0 to// to 1, or 0 to 255. (But use the same scale for all three!)//// The "saturation" parameter works like this:// 0.0 creates a black-and-white image.// 0.5 reduces the color saturation by half.// 1.0 causes no change.// 2.0 doubles the color saturation.// Note: A "change" value greater than 1.0 may project your RGB values// beyond their normal range, in which case you probably should truncate// them to the desired range before trying to use them in an image.r = &texture_reflectivity[i][0];g = &texture_reflectivity[i][1];b = &texture_reflectivity[i][2];float P = sqrt((*r) * (*r) * Pr + (*g) * (*g) * Pg + (*b) * (*b) * Pb);*r = BOUND(0, P + (*r - P) * saturation, 255);*g = BOUND(0, P + (*g - P) * saturation, 255);*b = BOUND(0, P + (*b - P) * saturation, 255);qprintf("tex %i (%s) avg rgb [ %f, %f, %f ]\n", i, path, texture_reflectivity[i][0], texture_reflectivity[i][1],texture_reflectivity[i][2]);}if(palette_frompak) {free(palette_frompak);} else {free(palette);}}/*=======================================================================MAKE FACES=======================================================================*//*=============WindingFromFace=============*/winding_t *WindingFromFaceX(dface_tx *f) {int32_t i;int32_t se;dvertex_t *dv;int32_t v;winding_t *w;w = AllocWinding(f->numedges);w->numpoints = f->numedges;for(i = 0; i < f->numedges; i++) {se = dsurfedges[f->firstedge + i];if(se < 0)v = dedgesX[-se].v[1];elsev = dedgesX[se].v[0];dv = &dvertexes[v];VectorCopy(dv->point, w->p[i]);}RemoveColinearPoints(w);return w;}winding_t *WindingFromFace(dface_t *f) {int32_t i;int32_t se;dvertex_t *dv;int32_t v;winding_t *w;w = AllocWinding(f->numedges);w->numpoints = f->numedges;for(i = 0; i < f->numedges; i++) {se = dsurfedges[f->firstedge + i];if(se < 0)v = dedges[-se].v[1];elsev = dedges[se].v[0];dv = &dvertexes[v];VectorCopy(dv->point, w->p[i]);}RemoveColinearPoints(w);return w;}/*=============BaseLightForFace=============*/struct SH1 BaseLightForFaceX(dface_tx *f) {texinfo_t *tx;//// check for light emited by texture//tx = &texinfo[f->texinfo];if(!(tx->flags & SURF_LIGHT) || tx->value == 0) {if(tx->flags & SURF_LIGHT) {printf("Surface light has 0 intensity.\n");}return SH1_Clear();}// non-standard use, get a more diffuse baselight from short direction vectorvec3_t normal, color;VectorScale((f->side ? backplanes[f->planenum].normal : dplanes[f->planenum].normal), -0.25, normal);VectorScale(texture_reflectivity[f->texinfo], tx->value, color);return SH1_FromDirectionalLight(normal, color);}struct SH1 BaseLightForFaceI(dface_t *f) {texinfo_t *tx;//// check for light emited by texture//tx = &texinfo[f->texinfo];if(!(tx->flags & SURF_LIGHT) || tx->value == 0) {if(tx->flags & SURF_LIGHT) {printf("Surface light has 0 intensity.\n");}return SH1_Clear();}// non-standard use, get a more diffuse baselight from short direction vectorvec3_t normal, color;VectorScale(f->side ? backplanes[f->planenum].normal : dplanes[f->planenum].normal, 0.25, normal);VectorScale(texture_reflectivity[f->texinfo], tx->value, color);return SH1_FromDirectionalLight(normal, color);}qboolean IsSkyX(dface_tx *f) {texinfo_t *tx;tx = &texinfo[f->texinfo];if(tx->flags & SURF_SKY)return true;return false;}qboolean IsSkyI(dface_t *f) {texinfo_t *tx;tx = &texinfo[f->texinfo];if(tx->flags & SURF_SKY)return true;return false;}/*=============MakePatchForFace=============*/float totalarea;void MakePatchForFace(int32_t fn, winding_t *w) {float area;patch_t *patch;dplane_t *pl;int32_t i;vec3_t color = {1.0f, 1.0f, 1.0f};area = WindingArea(w);totalarea += area;patch = &patches[num_patches];if(use_qbsp) {if(num_patches == MAX_PATCHES_QBSP)Error("Exceeded MAX_PATCHES_QBSP %i", MAX_PATCHES_QBSP);} else if(num_patches == MAX_PATCHES)Error("Exceeded MAX_PATCHES %i", MAX_PATCHES);patch->next = face_patches[fn];face_patches[fn] = patch;patch->winding = w;if(use_qbsp) {dface_tx *f;dleaf_tx *leaf;f = &dfacesX[fn];if(f->side)patch->plane = &backplanes[f->planenum];elsepatch->plane = &dplanes[f->planenum];if(face_offset[fn][0] || face_offset[fn][1] || face_offset[fn][2]) {// origin offset faces must create new planesif(use_qbsp) {if(numplanes + fakeplanes >= MAX_MAP_PLANES_QBSP)Error("numplanes + fakeplanes >= MAX_MAP_PLANES_QBSP");} else if(numplanes + fakeplanes >= MAX_MAP_PLANES)Error("numplanes + fakeplanes >= MAX_MAP_PLANES");pl = &dplanes[numplanes + fakeplanes];fakeplanes++;*pl = *(patch->plane);pl->dist += DotProduct(face_offset[fn], pl->normal);patch->plane = pl;}WindingCenter(w, patch->origin);VectorAdd(patch->origin, patch->plane->normal, patch->origin);leaf = RadPointInLeafX(patch->origin);patch->cluster = leaf->cluster;if(patch->cluster == -1) {// qprintf ("patch->cluster == -1\n");++cluster_neg_one;}patch->faceNumber = fn; // qb: for patch sortingpatch->area = area;if(patch->area <= 1)patch->area = 1;patch->sky = IsSkyX(f);VectorCopy(texture_reflectivity[f->texinfo], patch->reflectivity);// non-bmodel patches can emit lightif(fn < dmodels[0].numfaces) {ColorNormalize(patch->reflectivity, color);patch->baselight = SH1_ColorScale(BaseLightForFaceX(f), color);// VectorCopy(patch->baselight, patch->totallight);patch->totallight = patch->baselight;}} else {dface_t *f;dleaf_t *leaf;f = &dfaces[fn];if(f->side)patch->plane = &backplanes[f->planenum];elsepatch->plane = &dplanes[f->planenum];if(face_offset[fn][0] || face_offset[fn][1] || face_offset[fn][2]) {// origin offset faces must create new planesif(use_qbsp) {if(numplanes + fakeplanes >= MAX_MAP_PLANES_QBSP)Error("numplanes + fakeplanes >= MAX_MAP_PLANES_QBSP");} else if(numplanes + fakeplanes >= MAX_MAP_PLANES)Error("numplanes + fakeplanes >= MAX_MAP_PLANES");pl = &dplanes[numplanes + fakeplanes];fakeplanes++;*pl = *(patch->plane);pl->dist += DotProduct(face_offset[fn], pl->normal);patch->plane = pl;}WindingCenter(w, patch->origin);VectorAdd(patch->origin, patch->plane->normal, patch->origin);leaf = RadPointInLeaf(patch->origin);patch->cluster = leaf->cluster;if(patch->cluster == -1) {// qprintf ("patch->cluster == -1\n");++cluster_neg_one;}patch->faceNumber = fn; // qb: for patch sortingpatch->area = area;if(patch->area <= 1)patch->area = 1;patch->sky = IsSkyI(f);VectorCopy(texture_reflectivity[f->texinfo], patch->reflectivity);// non-bmodel patches can emit lightif(fn < dmodels[0].numfaces) {ColorNormalize(patch->reflectivity, color);patch->baselight = SH1_ColorScale(BaseLightForFaceI(f), color);// VectorCopy(patch->baselight, patch->totallight);patch->totallight = patch->baselight;}}num_patches++;}entity_t *EntityForModel(int32_t modnum) {int32_t i;char *s;char name[16];sprintf(name, "*%i", modnum);// search the entities for one using modnumfor(i = 0; i < num_entities; i++) {s = ValueForKey(&entities[i], "model");if(!strcmp(s, name))return &entities[i];}return &entities[0];}/*=============MakePatches=============*/void MakePatches(void) {int32_t i, j, k;int32_t fn;winding_t *w;dmodel_t *mod;vec3_t origin;entity_t *ent;qprintf("%i faces\n", numfaces);for(i = 0; i < nummodels; i++) {mod = &dmodels[i];ent = EntityForModel(i);// bmodels with origin brushes need to be offset into their// in-use positionGetVectorForKey(ent, "origin", origin);// VectorCopy (vec3_origin, origin);for(j = 0; j < mod->numfaces; j++) {fn = mod->firstface + j;face_entity[fn] = ent;VectorCopy(origin, face_offset[fn]);if(use_qbsp) {dface_tx *f;f = &dfacesX[fn];w = WindingFromFaceX(f);} else {dface_t *f;f = &dfaces[fn];w = WindingFromFace(f);}for(k = 0; k < w->numpoints; k++) {VectorAdd(w->p[k], origin, w->p[k]);}MakePatchForFace(fn, w);}}qprintf("%i sqaure feet\n", (int32_t)(totalarea / 64));}/*=======================================================================SUBDIVIDE=======================================================================*/void FinishSplit(patch_t *patch, patch_t *newp) {newp->baselight = patch->baselight;newp->totallight = patch->totallight;VectorCopy(patch->reflectivity, newp->reflectivity);newp->plane = patch->plane;newp->sky = patch->sky;patch->area = WindingArea(patch->winding);newp->area = WindingArea(newp->winding);if(patch->area <= 1)patch->area = 1;if(newp->area <= 1)newp->area = 1;if(use_qbsp) {dleaf_tx *leaf;WindingCenter(patch->winding, patch->origin);VectorAdd(patch->origin, patch->plane->normal, patch->origin);leaf = RadPointInLeafX(patch->origin);patch->cluster = leaf->cluster;if(patch->cluster == -1)qprintf("patch->cluster == -1\n");WindingCenter(newp->winding, newp->origin);VectorAdd(newp->origin, newp->plane->normal, newp->origin);leaf = RadPointInLeafX(newp->origin);newp->cluster = leaf->cluster;if(newp->cluster == -1)qprintf("patch->cluster == -1\n");} else {dleaf_t *leaf;WindingCenter(patch->winding, patch->origin);VectorAdd(patch->origin, patch->plane->normal, patch->origin);leaf = RadPointInLeaf(patch->origin);patch->cluster = leaf->cluster;if(patch->cluster == -1)qprintf("patch->cluster == -1\n");WindingCenter(newp->winding, newp->origin);VectorAdd(newp->origin, newp->plane->normal, newp->origin);leaf = RadPointInLeaf(newp->origin);newp->cluster = leaf->cluster;if(newp->cluster == -1)qprintf("patch->cluster == -1\n");}}/*=============SubdividePatchChops the patch only if its local bounds exceed the max size=============*/void SubdividePatch(patch_t *patch) {winding_t *w, *o1, *o2;vec3_t mins, maxs, total;vec3_t split;vec_t dist;int32_t i, j;vec_t v;patch_t *newp;w = patch->winding;mins[0] = mins[1] = mins[2] = BOGUS_RANGE;maxs[0] = maxs[1] = maxs[2] = -BOGUS_RANGE;for(i = 0; i < w->numpoints; i++) {for(j = 0; j < 3; j++) {v = w->p[i][j];if(v < mins[j])mins[j] = v;if(v > maxs[j])maxs[j] = v;}}VectorSubtract(maxs, mins, total);for(i = 0; i < 3; i++)if(total[i] > (subdiv + 1))break;if(i == 3) {// no splitting neededreturn;}//// split the winding//VectorCopy(vec3_origin, split);split[i] = 1;dist = (mins[i] + maxs[i]) * 0.5;ClipWindingEpsilon(w, split, dist, ON_EPSILON, &o1, &o2);//// create a new patch//if(use_qbsp) {if(num_patches == MAX_PATCHES_QBSP)Error("Exceeded MAX_PATCHES_QBSP %i", MAX_PATCHES_QBSP);} else if(num_patches == MAX_PATCHES)Error("Exceeded MAX_PATCHES %i", MAX_PATCHES);newp = &patches[num_patches];num_patches++;newp->next = patch->next;patch->next = newp;patch->winding = o1;newp->winding = o2;FinishSplit(patch, newp);SubdividePatch(patch);SubdividePatch(newp);}/*=============DicePatchChops the patch by a global grid=============*/void DicePatch(patch_t *patch) {winding_t *w, *o1, *o2;vec3_t mins, maxs;vec3_t split;vec_t dist;int32_t i;patch_t *newp;w = patch->winding;WindingBounds(w, mins, maxs); // 3D AABB for polygonfor(i = 0; i < 3; i++)if(floor((mins[i] + 1) / subdiv) < floor((maxs[i] - 1) / subdiv))break;if(i == 3) {// no splitting neededreturn;}//// split the winding//VectorCopy(vec3_origin, split);split[i] = 1;dist = subdiv * (1 + floor((mins[i] + 1) / subdiv));ClipWindingEpsilon(w, split, dist, ON_EPSILON, &o1, &o2);//// create a new patch//if(use_qbsp) {if(num_patches == MAX_PATCHES_QBSP)Error("Exceeded MAX_PATCHES_QBSP %i", MAX_PATCHES_QBSP);} else if(num_patches == MAX_PATCHES)Error("Exceeded MAX_PATCHES %i", MAX_PATCHES);newp = &patches[num_patches];num_patches++;newp->next = patch->next;patch->next = newp;patch->winding = o1;newp->winding = o2;FinishSplit(patch, newp);DicePatch(patch);DicePatch(newp);}/*=============SubdividePatches=============*/void SubdividePatches(void) {int32_t i, num;if(subdiv < 1)return;num = num_patches; // because the list will growfor(i = 0; i < num; i++) {if(dicepatches)DicePatch(&patches[i]);elseSubdividePatch(&patches[i]);}for(i = 0; i < num_patches; i++)patches[i].nodenum = PointInNodenum(patches[i].origin);printf("%i subdiv patches\n", num_patches);printf("-------------------------\n");qprintf("[? patch->cluster=-1 count is %i ?in solid leaf?]\n", cluster_neg_one);}//=====================================================================
/*===========================================================================Copyright (C) 1997-2006 Id Software, Inc.This program is free software; you can redistribute it and/or modifyit under the terms of the GNU General Public License as published bythe 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 ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See theGNU General Public License for more details.You should have received a copy of the GNU General Public License alongwith this program; if not, write to the Free Software Foundation, Inc.,51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.===========================================================================*/#include "stddef.h"#include "assert.h"#include "qrad.h"#define MAX_LSTYLES 256#define SINGLEMAP (64 * 64 * 4)#define QBSP_SINGLEMAP (256 * 256 * 4) // qb: higher res lightmapstypedef struct{dface_t *faces[2];dface_tx *facesX[2];qboolean coplanar;qboolean smooth;vec_t cos_normals_angle;vec3_t interface_normal;vec3_t vertex_normal[2];} edgeshare_t;edgeshare_t edgeshare[MAX_MAP_EDGES_QBSP];int32_t facelinks[MAX_MAP_FACES_QBSP];int32_t planelinks[2][MAX_MAP_PLANES_QBSP];int32_t maxdata = DEFAULT_MAP_LIGHTING, step = LMSTEP;vec3_t face_texnormals[MAX_MAP_FACES_QBSP];float sunradscale = 0.5;byte *dlightdata_ptr;// qb: quemap- face extentstypedef struct face_extents_s {vec3_t mins, maxs;vec3_t center;vec_t st_mins[2], st_maxs[2];} face_extents_t;static face_extents_t face_extents[MAX_MAP_FACES_QBSP];const dplane_t *getPlaneFromFaceNumber(const uint32_t faceNumber) {if (use_qbsp) {dface_tx *face = &dfacesX[faceNumber];if (face->side) {return &backplanes[face->planenum];} else {return &dplanes[face->planenum];}} else {dface_t *face = &dfaces[faceNumber];if (face->side) {return &backplanes[face->planenum];} else {return &dplanes[face->planenum];}}}qboolean GetIntertexnormal(int32_t facenum1, int32_t facenum2) {vec3_t normal;const dplane_t *p1 = getPlaneFromFaceNumber(facenum1);const dplane_t *p2 = getPlaneFromFaceNumber(facenum2);VectorAdd(face_texnormals[facenum1], face_texnormals[facenum2], normal);if (!VectorNormalize(normal, normal) || DotProduct(normal, p1->normal) <= NORMAL_EPSILON || DotProduct(normal, p2->normal) <= NORMAL_EPSILON) {return false;}return true;}/*** @brief Populates face_extents for all d_bsp_face_t, prior to light creation.* This is done so that sample positions may be nudged outward along* the face normal and towards the face center to help with traces.*/void BuildFaceExtents(void) {const dvertex_t *v;int32_t i, j, k;if (use_qbsp)for (k = 0; k < numfaces; k++) {const dface_tx *s = &dfacesX[k];const texinfo_t *tex = &texinfo[s->texinfo];const size_t face_index = (ptrdiff_t)(s - dfacesX);vec_t *mins = face_extents[face_index].mins;vec_t *maxs = face_extents[face_index].maxs;vec_t *center = face_extents[face_index].center;vec_t *st_mins = face_extents[face_index].st_mins;vec_t *st_maxs = face_extents[face_index].st_maxs;mins[0] = mins[1] = BOGUS_RANGE;maxs[0] = maxs[1] = -BOGUS_RANGE;for (i = 0; i < s->numedges; i++) {const int32_t e = dsurfedges[s->firstedge + i];if (e >= 0) {v = dvertexes + dedgesX[e].v[0];} else {v = dvertexes + dedgesX[-e].v[1];}for (j = 0; j < 3; j++) // calculate mins, maxs{if (v->point[j] > maxs[j]) {maxs[j] = v->point[j];}if (v->point[j] < mins[j]) {mins[j] = v->point[j];}}/* qb: from ericw-tools light/ltface.cc:* The (long double) casts below are important: The original code* was written for x87 floating-point which uses 80-bit floats for* intermediate calculations. But if you compile it without the* casts for modern x86_64, the compiler will round each* intermediate result to a 32-bit float, which introduces extra* rounding error.** This becomes a problem if the rounding error causes the light* utilities and the engine to disagree about the lightmap size* for some surfaces.** Casting to (long double) keeps the intermediate values at at* least 64 bits of precision, probably 128.*/for (j = 0; j < 2; j++) // calculate st_mins, st_maxs{// const vec_t val = DotProduct(v->point, tex->vecs[j]) + tex->vecs[j][3];const vec_t val = (long double)v->point[0] * tex->vecs[j][0] +(long double)v->point[1] * tex->vecs[j][1] +(long double)v->point[2] * tex->vecs[j][2] +tex->vecs[j][3];if (val < st_mins[j]) {st_mins[j] = val;}if (val > st_maxs[j]) {st_maxs[j] = val;}}}for (i = 0; i < 3; i++) // calculate center{center[i] = (mins[i] + maxs[i]) / 2.0;}}else // ibspfor (k = 0; k < numfaces; k++) {const dface_t *s = &dfaces[k];const texinfo_t *tex = &texinfo[s->texinfo];const size_t face_index = (ptrdiff_t)(s - dfaces);vec_t *mins = face_extents[face_index].mins;vec_t *maxs = face_extents[face_index].maxs;vec_t *center = face_extents[face_index].center;vec_t *st_mins = face_extents[face_index].st_mins;vec_t *st_maxs = face_extents[face_index].st_maxs;mins[0] = mins[1] = BOGUS_RANGE;maxs[0] = maxs[1] = -BOGUS_RANGE;for (i = 0; i < s->numedges; i++) {const int32_t e = dsurfedges[s->firstedge + i];if (e >= 0) {v = dvertexes + dedges[e].v[0];} else {v = dvertexes + dedges[-e].v[1];}for (j = 0; j < 3; j++) // calculate mins, maxs{if (v->point[j] > maxs[j]) {maxs[j] = v->point[j];}if (v->point[j] < mins[j]) {mins[j] = v->point[j];}}for (j = 0; j < 2; j++) // calculate st_mins, st_maxs{// const vec_t val = DotProduct(v->point, tex->vecs[j]) + tex->vecs[j][3];const vec_t val = (long double)v->point[0] * tex->vecs[j][0] +(long double)v->point[1] * tex->vecs[j][1] +(long double)v->point[2] * tex->vecs[j][2] +tex->vecs[j][3];if (val < st_mins[j]) {st_mins[j] = val;}if (val > st_maxs[j]) {st_maxs[j] = val;}}}for (i = 0; i < 3; i++) // calculate center{center[i] = (mins[i] + maxs[i]) / 2.0;}}}/*============LinkPlaneFaces============*/void LinkPlaneFaces(void) {int32_t i;if (use_qbsp) {dface_tx *f;f = dfacesX;for (i = 0; i < numfaces; i++, f++) {facelinks[i] = planelinks[f->side][f->planenum];planelinks[f->side][f->planenum] = i;}} else {dface_t *f;f = dfaces;for (i = 0; i < numfaces; i++, f++) {facelinks[i] = planelinks[f->side][f->planenum];planelinks[f->side][f->planenum] = i;}}}const dplane_t *getPlaneFromFace(const dface_t *face) {if (!face) {Error("getPlaneFromFace face was NULL\n");}if (face->side) {return &backplanes[face->planenum];} else {return &dplanes[face->planenum];}}const dplane_t *getPlaneFromFaceX(const dface_tx *face) {if (!face) {Error("getPlaneFromFaceX face was NULL\n");}if (face->side) {return &backplanes[face->planenum];} else {return &dplanes[face->planenum];}}/*============PairEdges============*/// qb: VHLTint32_t AddFaceForVertexNormalX(const int32_t edgeabs, int32_t edgeabsnext, const int32_t edgeend, int32_t edgeendnext, dface_tx *const f, dface_tx *fnext, vec_t angle, vec3_t normal)// Must guarantee these faces will form a loop or a chain, otherwise will result in endless loop.//// e[end]/enext[endnext]// *// |\.// |a\ fnext// | \,// | f \.// | \.// e enext//{VectorCopy(getPlaneFromFaceX(f)->normal, normal);int32_t vnum = dedgesX[edgeabs].v[edgeend];int32_t edge = 0, edgenext = 0;int32_t i, e, count1, count2;vec_t dot;for (count1 = count2 = 0, i = 0; i < f->numedges; i++) {e = dsurfedges[f->firstedge + i];if (dedgesX[abs(e)].v[0] == dedgesX[abs(e)].v[1])continue;if (abs(e) == edgeabs) {edge = e;count1++;} else if (dedgesX[abs(e)].v[0] == vnum || dedgesX[abs(e)].v[1] == vnum) {edgenext = e;count2++;}}if (count1 != 1 || count2 != 1) {qprintf("AddFaceForVertexNormalX bad face: edgeabs=%d edgeend=%d\n", edgeabs, edgeend);return -1;}int32_t vnum11, vnum12, vnum21, vnum22;vec3_t vec1, vec2;vnum11 = dedgesX[abs(edge)].v[edge > 0 ? 0 : 1];vnum12 = dedgesX[abs(edge)].v[edge > 0 ? 1 : 0];vnum21 = dedgesX[abs(edgenext)].v[edgenext > 0 ? 0 : 1];vnum22 = dedgesX[abs(edgenext)].v[edgenext > 0 ? 1 : 0];if (vnum == vnum12 && vnum == vnum21 && vnum != vnum11 && vnum != vnum22) {VectorSubtract(dvertexes[vnum11].point, dvertexes[vnum].point, vec1);VectorSubtract(dvertexes[vnum22].point, dvertexes[vnum].point, vec2);edgeabsnext = abs(edgenext);edgeendnext = edgenext > 0 ? 0 : 1;} else if (vnum == vnum11 && vnum == vnum22 && vnum != vnum12 && vnum != vnum21) {VectorSubtract(dvertexes[vnum12].point, dvertexes[vnum].point, vec1);VectorSubtract(dvertexes[vnum21].point, dvertexes[vnum].point, vec2);edgeabsnext = abs(edgenext);edgeendnext = edgenext > 0 ? 1 : 0;} else {qprintf("AddFaceForVertexNormalX bad face: edgeabs=%d edgeend=%d\n", edgeabs, edgeend);return -1;}VectorNormalize(vec1, vec1);VectorNormalize(vec2, vec2);dot = DotProduct(vec1, vec2);dot = dot > 1 ? 1 : dot < -1 ? -1: dot;angle = acos(dot);edgeshare_t *es = &edgeshare[edgeabsnext];if (!(es->facesX[0] && es->facesX[1]))return 1;if (es->facesX[0] == f && es->facesX[1] != f)fnext = es->facesX[1];else if (es->facesX[1] == f && es->facesX[0] != f)fnext = es->facesX[0];else {qprintf("AddFaceForVertexNormalX bad face: edgeabs=%d edgeend=%d\n", edgeabs, edgeend);return -1;}return 0;}int32_t AddFaceForVertexNormal(const int32_t edgeabs, int32_t edgeabsnext, const int32_t edgeend, int32_t edgeendnext, dface_t *const f, dface_t *fnext, vec_t angle, vec3_t normal) {VectorCopy(getPlaneFromFace(f)->normal, normal);int32_t vnum = dedgesX[edgeabs].v[edgeend];int32_t edge = 0, edgenext = 0;int32_t i, e, count1, count2;vec_t dot;for (count1 = count2 = 0, i = 0; i < f->numedges; i++) {e = dsurfedges[f->firstedge + i];if (dedges[abs(e)].v[0] == dedges[abs(e)].v[1])continue;if (abs(e) == edgeabs) {edge = e;count1++;} else if (dedges[abs(e)].v[0] == vnum || dedges[abs(e)].v[1] == vnum) {edgenext = e;count2++;}}if (count1 != 1 || count2 != 1) {qprintf("AddFaceForVertexNormal bad face: edgeabs=%d edgeend=%d\n", edgeabs, edgeend);return -1;}int32_t vnum11, vnum12, vnum21, vnum22;vec3_t vec1, vec2;vnum11 = dedges[abs(edge)].v[edge > 0 ? 0 : 1];vnum12 = dedges[abs(edge)].v[edge > 0 ? 1 : 0];vnum21 = dedges[abs(edgenext)].v[edgenext > 0 ? 0 : 1];vnum22 = dedges[abs(edgenext)].v[edgenext > 0 ? 1 : 0];if (vnum == vnum12 && vnum == vnum21 && vnum != vnum11 && vnum != vnum22) {VectorSubtract(dvertexes[vnum11].point, dvertexes[vnum].point, vec1);VectorSubtract(dvertexes[vnum22].point, dvertexes[vnum].point, vec2);edgeabsnext = abs(edgenext);edgeendnext = edgenext > 0 ? 0 : 1;} else if (vnum == vnum11 && vnum == vnum22 && vnum != vnum12 && vnum != vnum21) {VectorSubtract(dvertexes[vnum12].point, dvertexes[vnum].point, vec1);VectorSubtract(dvertexes[vnum21].point, dvertexes[vnum].point, vec2);edgeabsnext = abs(edgenext);edgeendnext = edgenext > 0 ? 1 : 0;} else {qprintf("AddFaceForVertexNormal bad face: edgeabs=%d edgeend=%d\n", edgeabs, edgeend);return -1;}VectorNormalize(vec1, vec1);VectorNormalize(vec2, vec2);dot = DotProduct(vec1, vec2);dot = dot > 1 ? 1 : dot < -1 ? -1: dot;angle = acos(dot);edgeshare_t *es = &edgeshare[edgeabsnext];if (!(es->faces[0] && es->faces[1]))return 1;if (es->faces[0] == f && es->faces[1] != f)fnext = es->faces[1];else if (es->faces[1] == f && es->faces[0] != f)fnext = es->faces[0];else {qprintf("AddFaceForVertexNormal bad face: edgeabs=%d edgeend=%d\n", edgeabs, edgeend);return -1;}return 0;}// =====================================================================================// PairEdges// =====================================================================================void PairEdges() {int32_t i, j, k;edgeshare_t *e;memset(&edgeshare, 0, sizeof(edgeshare));if (use_qbsp) {dface_tx *f;f = dfacesX;for (i = 0; i < numfaces; i++, f++) {{const dplane_t *fp = getPlaneFromFaceX(f);vec3_t texnormal;const texinfo_t *tex = &texinfo[f->texinfo];CrossProduct(tex->vecs[1], tex->vecs[0], texnormal);VectorNormalize(texnormal, texnormal);if (DotProduct(texnormal, fp->normal) < 0) {VectorSubtract(vec3_origin, texnormal, texnormal);}VectorCopy(texnormal, face_texnormals[i]);}for (j = 0; j < f->numedges; j++) {k = dsurfedges[f->firstedge + j];if (k < 0) {e = &edgeshare[-k];assert(e->facesX[1] == NULL);e->facesX[1] = f;} else {e = &edgeshare[k];assert(e->facesX[0] == NULL);e->facesX[0] = f;}if (e->facesX[0] && e->facesX[1]) {// determine if coplanarif ((e->facesX[0]->planenum == e->facesX[1]->planenum) && (e->facesX[0]->side == e->facesX[1]->side)) {e->coplanar = true;VectorCopy(getPlaneFromFaceX(e->facesX[0])->normal, e->interface_normal);e->cos_normals_angle = 1.0;} else {// see if they fall into a "smoothing group" based on angle of the normalsvec3_t normals[2];VectorCopy(getPlaneFromFaceX(e->facesX[0])->normal, normals[0]);VectorCopy(getPlaneFromFaceX(e->facesX[1])->normal, normals[1]);e->cos_normals_angle = DotProduct(normals[0], normals[1]);if (e->cos_normals_angle > (1.0 - 0.01)) // qb: get sloppier than 1 - NORMAL_EPSILON{e->coplanar = true;VectorCopy(getPlaneFromFaceX(e->facesX[0])->normal, e->interface_normal);e->cos_normals_angle = 1.0;} else if (smoothing_threshold > 0.0) {if (e->cos_normals_angle >= smoothing_threshold) {num_smoothing += 1;VectorAdd(normals[0], normals[1], e->interface_normal);VectorNormalize(e->interface_normal, e->interface_normal);}}}if (!VectorCompare(e->interface_normal, vec3_origin)) {e->smooth = true;}if (!GetIntertexnormal(e->facesX[0] - dfacesX, e->facesX[1] - dfacesX)) {// printf ("!GetIntertexnormal hit.\n");e->coplanar = false;VectorClear(e->interface_normal);e->smooth = false;}}}}} else // ibsp{dface_t *f;f = dfaces;for (i = 0; i < numfaces; i++, f++) {{const dplane_t *fp = getPlaneFromFace(f);vec3_t texnormal;const texinfo_t *tex = &texinfo[f->texinfo];CrossProduct(tex->vecs[1], tex->vecs[0], texnormal);VectorNormalize(texnormal, texnormal);if (DotProduct(texnormal, fp->normal) < 0) {VectorSubtract(vec3_origin, texnormal, texnormal);}VectorCopy(texnormal, face_texnormals[i]);}for (j = 0; j < f->numedges; j++) {k = dsurfedges[f->firstedge + j];if (k < 0) {e = &edgeshare[-k];assert(e->faces[1] == NULL);e->faces[1] = f;} else {e = &edgeshare[k];assert(e->faces[0] == NULL);e->faces[0] = f;}if (e->faces[0] && e->faces[1]) {// determine if coplanarif ((e->faces[0]->planenum == e->faces[1]->planenum) && (e->faces[0]->side == e->faces[1]->side)) {e->coplanar = true;VectorCopy(getPlaneFromFace(e->faces[0])->normal, e->interface_normal);e->cos_normals_angle = 1.0;} else {// see if they fall into a "smoothing group" based on angle of the normalsvec3_t normals[2];VectorCopy(getPlaneFromFace(e->faces[0])->normal, normals[0]);VectorCopy(getPlaneFromFace(e->faces[1])->normal, normals[1]);e->cos_normals_angle = DotProduct(normals[0], normals[1]);if (e->cos_normals_angle > (1.0 - 0.01)) // qb: get sloppier than 1 - NORMAL_EPSILON{e->coplanar = true;VectorCopy(getPlaneFromFace(e->faces[0])->normal, e->interface_normal);e->cos_normals_angle = 1.0;} else if (smoothing_threshold > 0.0) {if (e->cos_normals_angle >= smoothing_threshold) {num_smoothing += 1;VectorAdd(normals[0], normals[1], e->interface_normal);VectorNormalize(e->interface_normal, e->interface_normal);}}}if (!VectorCompare(e->interface_normal, vec3_origin)) {e->smooth = true;}if (!GetIntertexnormal(e->faces[0] - dfaces, e->faces[1] - dfaces)) {// printf ("!GetIntertexnormal hit.\n");e->coplanar = false;VectorClear(e->interface_normal);e->smooth = false;}}}}}// qb: VHLT{int32_t edgeabs, edgeabsnext;int32_t edgeend, edgeendnext;int32_t d;vec_t angle = 0, angles = 0;vec3_t normal, normals;vec3_t edgenormal;int32_t r, count, mme;if (use_qbsp)mme = MAX_MAP_EDGES_QBSP;elsemme = MAX_MAP_EDGES;for (edgeabs = 0; edgeabs < mme; edgeabs++) {e = &edgeshare[edgeabs];if (!e->smooth)continue;VectorCopy(e->interface_normal, edgenormal);if (use_qbsp) {dface_tx *f, *fcurrent, *fnext;if (dedgesX[edgeabs].v[0] == dedgesX[edgeabs].v[1]) {vec3_t errorpos;VectorCopy(dvertexes[dedgesX[edgeabs].v[0]].point, errorpos);VectorAdd(errorpos, face_offset[e->facesX[0] - dfacesX], errorpos);Error("PairEdges: invalid edge at (%f,%f,%f)", errorpos[0], errorpos[1], errorpos[2]);VectorCopy(edgenormal, e->vertex_normal[0]);VectorCopy(edgenormal, e->vertex_normal[1]);} else {const dplane_t *p0 = getPlaneFromFaceX(e->facesX[0]);const dplane_t *p1 = getPlaneFromFaceX(e->facesX[1]);for (edgeend = 0; edgeend < 2; edgeend++) {vec3_t errorpos;VectorCopy(dvertexes[dedgesX[edgeabs].v[edgeend]].point, errorpos);VectorAdd(errorpos, face_offset[e->facesX[0] - dfacesX], errorpos);angles = 0;VectorClear(normals);for (d = 0; d < 2; d++) {f = e->facesX[d];count = 0, fnext = f, edgeabsnext = edgeabs, edgeendnext = edgeend;while (1) {fcurrent = fnext;r = AddFaceForVertexNormalX(edgeabsnext, edgeabsnext, edgeendnext, edgeendnext, fcurrent, fnext, angle, normal);count++;if (r == -1) {// qprintf("PairEdges: face edges mislink at (%f,%f,%f)", errorpos[0], errorpos[1], errorpos[2]);break;}if (count >= 100) {// qprintf("PairEdges: faces mislink at (%f,%f,%f)", errorpos[0], errorpos[1], errorpos[2]);break;}if (DotProduct(normal, p0->normal) <= NORMAL_EPSILON || DotProduct(normal, p1->normal) <= NORMAL_EPSILON)break;if (DotProduct(edgenormal, normal) + NORMAL_EPSILON < smoothing_threshold)break;if (!GetIntertexnormal(fcurrent - dfacesX, e->facesX[0] - dfacesX) || !GetIntertexnormal(fcurrent - dfacesX, e->facesX[1] - dfacesX))break;angles += angle;VectorMA(normals, angle, normal, normals);if (r != 0 || fnext == f)break;}}if (angles < NORMAL_EPSILON) {VectorCopy(edgenormal, e->vertex_normal[edgeend]);// qprintf("PairEdges: no valid faces at (%f,%f,%f)", errorpos[0], errorpos[1], errorpos[2]);} else {VectorNormalize(normals, e->vertex_normal[edgeend]);}}}} else // ibsp{dface_t *f, *fcurrent, *fnext;if (dedges[edgeabs].v[0] == dedges[edgeabs].v[1]) {vec3_t errorpos;VectorCopy(dvertexes[dedges[edgeabs].v[0]].point, errorpos);VectorAdd(errorpos, face_offset[e->faces[0] - dfaces], errorpos);Error("PairEdges: invalid edge at (%f,%f,%f)", errorpos[0], errorpos[1], errorpos[2]);VectorCopy(edgenormal, e->vertex_normal[0]);VectorCopy(edgenormal, e->vertex_normal[1]);} else {const dplane_t *p0 = getPlaneFromFace(e->faces[0]);const dplane_t *p1 = getPlaneFromFace(e->faces[1]);for (edgeend = 0; edgeend < 2; edgeend++) {vec3_t errorpos;VectorCopy(dvertexes[dedges[edgeabs].v[edgeend]].point, errorpos);VectorAdd(errorpos, face_offset[e->faces[0] - dfaces], errorpos);angles = 0;VectorClear(normals);for (d = 0; d < 2; d++) {f = e->faces[d];count = 0, fnext = f, edgeabsnext = edgeabs, edgeendnext = edgeend;while (1) {fcurrent = fnext;r = AddFaceForVertexNormal(edgeabsnext, edgeabsnext, edgeendnext, edgeendnext, fcurrent, fnext, angle, normal);count++;if (r == -1) {// qprintf("PairEdges: face edges mislink at (%f,%f,%f)", errorpos[0], errorpos[1], errorpos[2]);break;}if (count >= 100) {// qprintf("PairEdges: faces mislink at (%f,%f,%f)", errorpos[0], errorpos[1], errorpos[2]);break;}if (DotProduct(normal, p0->normal) <= NORMAL_EPSILON || DotProduct(normal, p1->normal) <= NORMAL_EPSILON)break;if (DotProduct(edgenormal, normal) + NORMAL_EPSILON < smoothing_threshold)break;if (!GetIntertexnormal(fcurrent - dfaces, e->faces[0] - dfaces) || !GetIntertexnormal(fcurrent - dfaces, e->faces[1] - dfaces))break;angles += angle;VectorMA(normals, angle, normal, normals);if (r != 0 || fnext == f)break;}}if (angles < NORMAL_EPSILON) {VectorCopy(edgenormal, e->vertex_normal[edgeend]);// qprintf("PairEdges: no valid faces at (%f,%f,%f)", errorpos[0], errorpos[1], errorpos[2]);} else {VectorNormalize(normals, e->vertex_normal[edgeend]);}}}}if (e->coplanar) {if (!VectorCompare(e->vertex_normal[0], e->interface_normal) || !VectorCompare(e->vertex_normal[1], e->interface_normal)) {e->coplanar = false;}}}}}/*=================================================================POINT TRIANGULATION=================================================================*/typedef struct triedge_s {int32_t p0, p1;vec3_t normal;vec_t dist;struct triangle_s *tri;} triedge_t;typedef struct triangle_s {triedge_t *edges[3];} triangle_t;#define MAX_TRI_POINTS 1024#define MAX_TRI_EDGES (MAX_TRI_POINTS * 6)#define MAX_TRI_TRIS (MAX_TRI_POINTS * 2)typedef struct{int32_t numpoints;int32_t numedges;int32_t numtris;dplane_t *plane;triedge_t *edgematrix[MAX_TRI_POINTS][MAX_TRI_POINTS];patch_t *points[MAX_TRI_POINTS];triedge_t edges[MAX_TRI_EDGES];triangle_t tris[MAX_TRI_TRIS];} triangulation_t;/*===============AllocTriangulation===============*/triangulation_t *AllocTriangulation(dplane_t *plane) {triangulation_t *t;t = malloc(sizeof(triangulation_t));t->numpoints = 0;t->numedges = 0;t->numtris = 0;t->plane = plane;// memset (t->edgematrix, 0, sizeof(t->edgematrix));return t;}/*===============FreeTriangulation===============*/void FreeTriangulation(triangulation_t *tr) {free(tr);}triedge_t *FindEdge(triangulation_t *trian, int32_t p0, int32_t p1) {triedge_t *e, *be;vec3_t v1;vec3_t normal;vec_t dist;if (trian->edgematrix[p0][p1])return trian->edgematrix[p0][p1];if (trian->numedges > MAX_TRI_EDGES - 2)Error("trian->numedges > MAX_TRI_EDGES-2");VectorSubtract(trian->points[p1]->origin, trian->points[p0]->origin, v1);VectorNormalize(v1, v1);CrossProduct(v1, trian->plane->normal, normal);dist = DotProduct(trian->points[p0]->origin, normal);e = &trian->edges[trian->numedges];e->p0 = p0;e->p1 = p1;e->tri = NULL;VectorCopy(normal, e->normal);e->dist = dist;trian->numedges++;trian->edgematrix[p0][p1] = e;be = &trian->edges[trian->numedges];be->p0 = p1;be->p1 = p0;be->tri = NULL;VectorSubtract(vec3_origin, normal, be->normal);be->dist = -dist;trian->numedges++;trian->edgematrix[p1][p0] = be;return e;}triangle_t *AllocTriangle(triangulation_t *trian) {triangle_t *t;if (trian->numtris >= MAX_TRI_TRIS)Error("trian->numtris >= MAX_TRI_TRIS");t = &trian->tris[trian->numtris];trian->numtris++;return t;}/*============TriEdge_r============*/void TriEdge_r(triangulation_t *trian, triedge_t *e) {int32_t i, bestp = 0;vec3_t v1, v2;vec_t *p0, *p1, *p;vec_t best, ang;triangle_t *nt;if (e->tri)return; // allready connected by someone// find the point with the best anglep0 = trian->points[e->p0]->origin;p1 = trian->points[e->p1]->origin;best = 1.1;for (i = 0; i < trian->numpoints; i++) {p = trian->points[i]->origin;// a 0 dist will form a degenerate triangleif (DotProduct(p, e->normal) - e->dist < 0)continue; // behind edgeVectorSubtract(p0, p, v1);VectorSubtract(p1, p, v2);if (!VectorNormalize(v1, v1))continue;if (!VectorNormalize(v2, v2))continue;ang = DotProduct(v1, v2);if (ang < best) {best = ang;bestp = i;}}if (best >= 1)return; // edge doesn't match anything// make a new trianglent = AllocTriangle(trian);nt->edges[0] = e;nt->edges[1] = FindEdge(trian, e->p1, bestp);nt->edges[2] = FindEdge(trian, bestp, e->p0);for (i = 0; i < 3; i++)nt->edges[i]->tri = nt;TriEdge_r(trian, FindEdge(trian, bestp, e->p1));TriEdge_r(trian, FindEdge(trian, e->p0, bestp));}/*============TriangulatePoints============*/void TriangulatePoints(triangulation_t *trian) {vec_t d, bestd;vec3_t v1;int32_t bp1 = 0, bp2 = 0, i, j;vec_t *p1, *p2;triedge_t *e, *e2;if (trian->numpoints < 2)return;// find the two closest pointsbestd = BOGUS_RANGE;for (i = 0; i < trian->numpoints; i++) {p1 = trian->points[i]->origin;for (j = i + 1; j < trian->numpoints; j++) {p2 = trian->points[j]->origin;VectorSubtract(p2, p1, v1);d = VectorLength(v1);if (d < bestd) {bestd = d;bp1 = i;bp2 = j;}}}e = FindEdge(trian, bp1, bp2);e2 = FindEdge(trian, bp2, bp1);TriEdge_r(trian, e);TriEdge_r(trian, e2);}/*===============AddPointToTriangulation===============*/void AddPointToTriangulation(patch_t *patch, triangulation_t *trian) {int32_t pnum;pnum = trian->numpoints;if (pnum == MAX_TRI_POINTS)Error("trian->numpoints == MAX_TRI_POINTS");trian->points[pnum] = patch;trian->numpoints++;}/*===============LerpTriangle===============*/struct SH1 LerpTriangle(triangulation_t *trian, triangle_t *t, vec3_t point) {patch_t *p1 = trian->points[t->edges[0]->p0];patch_t *p2 = trian->points[t->edges[1]->p0];patch_t *p3 = trian->points[t->edges[2]->p0];float x1 = DotProduct(p3->origin, t->edges[0]->normal) - t->edges[0]->dist;float y1 = DotProduct(p2->origin, t->edges[2]->normal) - t->edges[2]->dist;float c2 = (fabs(x1) >= ON_EPSILON) ? (DotProduct(point, t->edges[0]->normal) - t->edges[0]->dist) / x1 : 0;float c3 = (fabs(y1) >= ON_EPSILON) ? (DotProduct(point, t->edges[2]->normal) - t->edges[2]->dist) / y1 : 0;float c1 = 1.0f - (c2 + c3);return SH1_Add(SH1_Add(SH1_Scale(p1->totallight, c1),SH1_Scale(p2->totallight, c2)), SH1_Scale(p3->totallight, c3));}qboolean PointInTriangle(vec3_t point, triangle_t *t) {int32_t i;triedge_t *e;vec_t d;for (i = 0; i < 3; i++) {e = t->edges[i];d = DotProduct(e->normal, point) - e->dist;if (d < 0)return false; // not inside}return true;}/*===============SampleTriangulation===============*/struct SH1 SampleTriangulation(vec3_t point, triangulation_t *trian, triangle_t **last_valid) {triangle_t *t;triedge_t *e;vec_t d, best;patch_t *p0, *p1;vec3_t v1, v2;int32_t i, j;if (trian->numpoints == 0) {return SH1_Clear();}if (trian->numpoints == 1) {return trian->points[0]->totallight;}// try the last oneif (*last_valid) {if (PointInTriangle(point, *last_valid)) {return LerpTriangle(trian, *last_valid, point);}}// search for trianglesfor (t = trian->tris, j = 0; j < trian->numtris; t++, j++) {if (t == *last_valid)continue;if (!PointInTriangle(point, t))continue;*last_valid = t;return LerpTriangle(trian, t, point);}// search for exterior edgefor (e = trian->edges, j = 0; j < trian->numedges; e++, j++) {if (e->tri)continue; // not an exterior edged = DotProduct(point, e->normal) - e->dist;if (d < 0)continue; // not in front of edgep0 = trian->points[e->p0];p1 = trian->points[e->p1];VectorSubtract(p1->origin, p0->origin, v1);VectorNormalize(v1, v1);VectorSubtract(point, p0->origin, v2);d = DotProduct(v2, v1);if (d < 0)continue;if (d > 1)continue;return SH1_Add(SH1_Scale(p0->totallight, 1.0f - d), SH1_Scale(p1->totallight, d));}// search for nearest pointbest = BOGUS_RANGE;p1 = NULL;for (j = 0; j < trian->numpoints; j++) {p0 = trian->points[j];VectorSubtract(point, p0->origin, v1);d = VectorLength(v1);if (d < best) {best = d;p1 = p0;}}if (!p1)Error("SampleTriangulation: no points");return p1->totallight;}/*=================================================================LIGHTMAP SAMPLE GENERATION=================================================================*/typedef struct{vec_t facedist;vec3_t facenormal;int32_t numsurfpt;vec3_t surfpt[QBSP_SINGLEMAP];vec3_t modelorg; // for origined bmodelsvec3_t texorg;vec3_t worldtotex[2]; // s = (world - texorg) . worldtotex[0]vec3_t textoworld[2]; // world = texorg + s * textoworld[0]vec_t exactmins[2], exactmaxs[2];int32_t texmins[2], texsize[2];int32_t surfnum;dface_t *face;dface_tx *faceX;} lightinfo_t;/*================CalcFaceExtentsFills in s->texmins[] and s->texsize[]also sets exactmins[] and exactmaxs[]================*/void CalcFaceExtents(lightinfo_t *l) {vec_t mins[2], maxs[2], val;int32_t i, j, e, map = SINGLEMAP;dvertex_t *v;texinfo_t *tex;vec3_t vt;if (use_qbsp) {map = QBSP_SINGLEMAP;dface_tx *s;s = l->faceX;mins[0] = mins[1] = BOGUS_RANGE;maxs[0] = maxs[1] = -BOGUS_RANGE;tex = &texinfo[s->texinfo];for (i = 0; i < s->numedges; i++) {e = dsurfedges[s->firstedge + i];if (e >= 0)v = dvertexes + dedgesX[e].v[0];elsev = dvertexes + dedgesX[-e].v[1];// VectorAdd (v->point, l->modelorg, vt);VectorCopy(v->point, vt);for (j = 0; j < 2; j++) {val = DotProduct(vt, tex->vecs[j]) + tex->vecs[j][3];if (val < mins[j])mins[j] = val;if (val > maxs[j])maxs[j] = val;}}} else {dface_t *s;s = l->face;mins[0] = mins[1] = BOGUS_RANGE;maxs[0] = maxs[1] = -BOGUS_RANGE;tex = &texinfo[s->texinfo];for (i = 0; i < s->numedges; i++) {e = dsurfedges[s->firstedge + i];if (e >= 0)v = dvertexes + dedges[e].v[0];elsev = dvertexes + dedges[-e].v[1];// VectorAdd (v->point, l->modelorg, vt);VectorCopy(v->point, vt);for (j = 0; j < 2; j++) {val = DotProduct(vt, tex->vecs[j]) + tex->vecs[j][3];if (val < mins[j])mins[j] = val;if (val > maxs[j])maxs[j] = val;}}}for (i = 0; i < 2; i++) {l->exactmins[i] = mins[i];l->exactmaxs[i] = maxs[i];mins[i] = floor(mins[i] / step);maxs[i] = ceil(maxs[i] / step);l->texmins[i] = mins[i];l->texsize[i] = maxs[i] - mins[i];}if (l->texsize[0] * l->texsize[1] > map / 4) // div 4 for extrasamples{char s[3] = {'X', 'Y', 'Z'};for (i = 0; i < 2; i++) {printf("Axis: %c\n", s[i]);l->exactmins[i] = mins[i];l->exactmaxs[i] = maxs[i];mins[i] = floor(mins[i] / step);maxs[i] = ceil(maxs[i] / step);l->texmins[i] = mins[i];l->texsize[i] = maxs[i] - mins[i];printf(" Mins = %10.3f, Maxs = %10.3f, Size = %10.3f\n", (double)mins[i], (double)maxs[i], (double)(maxs[i] - mins[i]));}Error("Surface too large to map");}}/*================CalcFaceVectorsFills in texorg, worldtotex. and textoworld================*/void CalcFaceVectors(lightinfo_t *l) {texinfo_t *tex;int32_t i, j;vec3_t texnormal;vec_t distscale;vec_t dist, len;int32_t w, h;if (use_qbsp)tex = &texinfo[l->faceX->texinfo];elsetex = &texinfo[l->face->texinfo];// convert from float to doublefor (i = 0; i < 2; i++)for (j = 0; j < 3; j++)l->worldtotex[i][j] = tex->vecs[i][j];// calculate a normal to the texture axis. points can be moved along this// without changing their S/Ttexnormal[0] = tex->vecs[1][1] * tex->vecs[0][2] - tex->vecs[1][2] * tex->vecs[0][1];texnormal[1] = tex->vecs[1][2] * tex->vecs[0][0] - tex->vecs[1][0] * tex->vecs[0][2];texnormal[2] = tex->vecs[1][0] * tex->vecs[0][1] - tex->vecs[1][1] * tex->vecs[0][0];VectorNormalize(texnormal, texnormal);// flip it towards plane normaldistscale = DotProduct(texnormal, l->facenormal);if (!distscale) {qprintf("WARNING: Texture axis perpendicular to face\n");distscale = 1;}if (distscale < 0) {distscale = -distscale;VectorSubtract(vec3_origin, texnormal, texnormal);}// distscale is the ratio of the distance along the texture normal to// the distance along the plane normaldistscale = 1 / distscale;for (i = 0; i < 2; i++) {len = VectorLength(l->worldtotex[i]);dist = DotProduct(l->worldtotex[i], l->facenormal);dist *= distscale;VectorMA(l->worldtotex[i], -dist, texnormal, l->textoworld[i]);VectorScale(l->textoworld[i], (1 / len) * (1 / len), l->textoworld[i]);}// calculate texorg on the texture planefor (i = 0; i < 3; i++)l->texorg[i] = -tex->vecs[0][3] * l->textoworld[0][i] - tex->vecs[1][3] * l->textoworld[1][i];// project back to the face planedist = DotProduct(l->texorg, l->facenormal) - l->facedist - 1;dist *= distscale;VectorMA(l->texorg, -dist, texnormal, l->texorg);// compensate for org'd bmodelsVectorAdd(l->texorg, l->modelorg, l->texorg);// total sample counth = l->texsize[1] + 1;w = l->texsize[0] + 1;l->numsurfpt = w * h;}/*=================CalcPointsFor each texture aligned grid point, back project onto the planeto get the world xyz value of the sample point=================*/void CalcPoints(lightinfo_t *l, float sofs, float tofs) {int32_t i;int32_t s, t, j;int32_t w, h;vec_t starts, startt, us, ut;vec_t *surf;vec_t mids, midt;vec3_t facemid;surf = l->surfpt[0];mids = (l->exactmaxs[0] + l->exactmins[0]) / 2;midt = (l->exactmaxs[1] + l->exactmins[1]) / 2;for (j = 0; j < 3; j++)facemid[j] = l->texorg[j] + l->textoworld[0][j] * mids + l->textoworld[1][j] * midt;h = l->texsize[1] + 1;w = l->texsize[0] + 1;l->numsurfpt = w * h;starts = l->texmins[0] * step;startt = l->texmins[1] * step;for (t = 0; t < h; t++) {for (s = 0; s < w; s++, surf += 3) {us = starts + (s + sofs) * step;ut = startt + (t + tofs) * step;// if a line can be traced from surf to facemid, the point is goodfor (i = 0; i < 6; i++) {// calculate texture pointfor (j = 0; j < 3; j++)surf[j] = l->texorg[j] + l->textoworld[0][j] * us + l->textoworld[1][j] * ut;if (use_qbsp) {dleaf_tx *leaf;leaf = RadPointInLeafX(surf);if (leaf->contents != CONTENTS_SOLID) {if (!TestLine_r(0, facemid, surf))break; // got it}} else {dleaf_t *leaf;leaf = RadPointInLeaf(surf);if (leaf->contents != CONTENTS_SOLID) {if (!TestLine_r(0, facemid, surf))break; // got it}}// nudge itif (i & 1) {if (us > mids) {us -= 8;if (us < mids)us = mids;} else {us += 8;if (us > mids)us = mids;}} else {if (ut > midt) {ut -= 8;if (ut < midt)ut = midt;} else {ut += 8;if (ut > midt)ut = midt;}}}}}}//==============================================================#define MAX_STYLES 32typedef struct{int32_t numsamples;float *origins;int32_t numstyles;int32_t stylenums[MAX_STYLES];struct SH1 *samples[MAX_STYLES];} facelight_t;directlight_t *directlights[MAX_MAP_LEAFS_QBSP];facelight_t facelight[MAX_MAP_FACES_QBSP];int32_t numdlights;/*==================FindTargetEntity==================*/entity_t *FindTargetEntity(char *target) {int32_t i;char *n;for (i = 0; i < num_entities; i++) {n = ValueForKey(&entities[i], "targetname");if (!strcmp(n, target))return &entities[i];}return NULL;}//#define DIRECT_LIGHT 3000#define DIRECT_LIGHT 3/*=============CreateDirectLights=============*/void CreateDirectLights(void) {int32_t i;patch_t *p;directlight_t *dl;dleaf_t *leaf;dleaf_tx *leafX;int32_t cluster;entity_t *e, *e2;char *name;char *target;float angle;vec3_t dest;char *_color;float intensity;char *sun_target = NULL;char *proc_num;//// entities//for (i = 0; i < num_entities; i++) {e = &entities[i];name = ValueForKey(e, "classname");if (strncmp(name, "light", 5)) {if (!strncmp(name, "worldspawn", 10)) {sun_target = ValueForKey(e, "_sun");if (strlen(sun_target) > 0) {printf("Sun activated.\n");printf("Sky radiosity (sunradscale): %f \n", sunradscale);sun = true;}proc_num = ValueForKey(e, "_sun_ambient");if (strlen(proc_num) > 0) {sun_ambient = atof(proc_num);}proc_num = ValueForKey(e, "_sun_light");if (strlen(proc_num) > 0) {sun_main = atof(proc_num);}proc_num = ValueForKey(e, "_sun_color");if (strlen(proc_num) > 0) {GetVectorForKey(e, "_sun_color", sun_color);sun_alt_color = true;ColorNormalize(sun_color, sun_color);}}continue;}target = ValueForKey(e, "target");if (strlen(target) >= 1 && sun_target && !strcmp(target, sun_target)) // qb: add sun_target check{vec3_t sun_s, sun_t;printf("Sun target found.\n");GetVectorForKey(e, "origin", sun_s);e2 = FindTargetEntity(target);if (!e2) {printf("WARNING: sun missing target, 0,0,0 used\n");sun_t[0] = 0;sun_t[1] = 0;sun_t[2] = 0;} else {GetVectorForKey(e2, "origin", sun_t);}VectorSubtract(sun_s, sun_t, sun_pos);VectorNormalize(sun_pos, sun_pos);printf("SUN VECTOR: %f, %f, %f\n", sun_pos[0], sun_pos[1], sun_pos[2]);continue;}numdlights++;dl = malloc(sizeof(directlight_t));memset(dl, 0, sizeof(*dl));GetVectorForKey(e, "origin", dl->origin);dl->style = FloatForKey(e, "_style");if (!dl->style)dl->style = FloatForKey(e, "style");if (dl->style < 0 || dl->style >= MAX_LSTYLES)dl->style = 0;dl->nodenum = PointInNodenum(dl->origin);if (use_qbsp) {leafX = RadPointInLeafX(dl->origin);cluster = leafX->cluster;} else {leaf = RadPointInLeaf(dl->origin);cluster = leaf->cluster;}dl->next = directlights[cluster];directlights[cluster] = dl;proc_num = ValueForKey(e, "_wait");if (strlen(proc_num) > 0)dl->wait = atof(proc_num);else {proc_num = ValueForKey(e, "wait");if (strlen(proc_num) > 0)dl->wait = atof(proc_num);elsedl->wait = 1.0f;}if (dl->wait <= EQUAL_EPSILON)dl->wait = 1.0f;proc_num = ValueForKey(e, "_angwait");if (strlen(proc_num) > 0)dl->adjangle = atof(proc_num);elsedl->adjangle = 1.0f;// [slipyx] add _falloffdl->falloff = atoi(ValueForKey(e, "_falloff"));if (dl->falloff < 0)dl->falloff = 0;intensity = FloatForKey(e, "light");if (!intensity)intensity = FloatForKey(e, "_light");if (!intensity)intensity = 300;_color = ValueForKey(e, "_color");if (_color && _color[0]) {sscanf(_color, "%f %f %f", &dl->color.f[0], &dl->color.f[4], &dl->color.f[8]);dl->color = SH1_Normalize(dl->color, NULL);} elsedl->color.f[0] = dl->color.f[4] = dl->color.f[8] = 1.0;dl->intensity = intensity * entity_scale;dl->type = emit_point;target = ValueForKey(e, "target");if (!strcmp(name, "light_spot") || target[0]) {dl->type = emit_spotlight;dl->spotlight.dot = FloatForKey(e, "_cone");if (!dl->spotlight.dot)dl->spotlight.dot = 20; // qb: doubled for new calcdl->spotlight.dot = cos(dl->spotlight.dot / 90 * 3.14159); // qb: doubled for new calcif (target[0]) {// point towards targete2 = FindTargetEntity(target);if (!e2)printf("WARNING: light at (%i %i %i) has missing target\n",(int32_t)dl->origin[0], (int32_t)dl->origin[1], (int32_t)dl->origin[2]);else {GetVectorForKey(e2, "origin", dest);VectorSubtract(dest, dl->origin, dl->spotlight.normal);VectorNormalize(dl->spotlight.normal, dl->spotlight.normal);}} else {// point down angleangle = FloatForKey(e, "angle");if (angle == ANGLE_UP) {dl->spotlight.normal[0] = dl->spotlight.normal[1] = 0;dl->spotlight.normal[2] = 1;} else if (angle == ANGLE_DOWN) {dl->spotlight.normal[0] = dl->spotlight.normal[1] = 0;dl->spotlight.normal[2] = -1;} else {dl->spotlight.normal[2] = 0;dl->spotlight.normal[0] = cos(angle / 180 * 3.14159);dl->spotlight.normal[1] = sin(angle / 180 * 3.14159);}}}}//// surfaces//for (i = 0, p = patches; i < num_patches; i++, p++) {if ((!sun || !p->sky) && p->totallight.f[0] < DIRECT_LIGHT && p->totallight.f[4] < DIRECT_LIGHT && p->totallight.f[8] < DIRECT_LIGHT)continue;numdlights++;dl = malloc(sizeof(directlight_t));memset(dl, 0, sizeof(*dl));VectorCopy(p->origin, dl->origin);if (use_qbsp) {leafX = RadPointInLeafX(dl->origin);cluster = leafX->cluster;dl->leafX = leafX;} else {leaf = RadPointInLeaf(dl->origin);cluster = leaf->cluster;dl->leaf = leaf;}dl->next = directlights[cluster];directlights[cluster] = dl;if (sun && p->sky) {dl->plane = p->plane;dl->type = emit_sky;// qb: for sky radiosity, was dl->intensity = 1.0f;dl->color = SH1_Normalize(p->totallight, &dl->intensity);dl->intensity *= p->area * direct_scale;VectorCopy(p->plane->normal, dl->sky.normal);} else {dl->type = emit_surface;dl->color = SH1_Normalize(p->totallight, &dl->intensity);dl->intensity *= p->area * direct_scale;VectorCopy(p->plane->normal, dl->surface.normal);dl->surface.winding = p->winding;}p->totallight = SH1_Clear();}printf("%i direct lights\n", numdlights);}#ifdef WIN32static inline int32_t lowestCommonNode(int32_t nodeNum1, int32_t nodeNum2)#elsestatic inline int32_t lowestCommonNode(int32_t nodeNum1, int32_t nodeNum2)#endif{int32_t child1, tmp, headNode = 0;if (nodeNum1 > nodeNum2) {tmp = nodeNum1;nodeNum1 = nodeNum2;nodeNum2 = tmp;}re_test:// headNode is guaranteed to be <= nodeNum1 and nodeNum1 is < nodeNum2if (headNode == nodeNum1)return headNode;if (use_qbsp) {dnode_tx *node;child1 = (node = dnodesX + headNode)->children[1];if (nodeNum2 < child1)// Both nodeNum1 and nodeNum2 are less than child1.// In this case, child0 is always a node, not a leaf, so we don't need// to check to make sure.headNode = node->children[0];else if (nodeNum1 < child1)// Child1 sits between nodeNum1 and nodeNum2.// This means that headNode is the lowest node which contains both// nodeNum1 and nodeNum2.return headNode;else if (child1 > 0)// Both nodeNum1 and nodeNum2 are greater than child1.// If child1 is a node, that means it contains both nodeNum1 and// nodeNum2.headNode = child1;else// Child1 is a leaf, therefore by process of elimination child0 must be// a node and must contain boste nodeNum1 and nodeNum2.headNode = node->children[0];// goto instead of while(1) because it makes the CPU branch predict easier} else {dnode_t *node;child1 = (node = dnodes + headNode)->children[1];if (nodeNum2 < child1)headNode = node->children[0];else if (nodeNum1 < child1)return headNode;else if (child1 > 0)headNode = child1;elseheadNode = node->children[0];}goto re_test;}/*=============LightContributionToPoint=============*/static struct SH1 LightContributionToPoint(directlight_t *l, vec3_t pos, int32_t nodenum,vec3_t normal, // vec3_t color,float lightscale2, qboolean *sun_main_once, qboolean *sun_ambient_once) {struct SH1 result = SH1_Clear();// check for simple occlusionint32_t common_node = lowestCommonNode(nodenum, l->nodenum);if(!noblock && TestLine_r(common_node, pos, l->origin))return result;float total_scale = lightscale2 * 0.25;vec3_t direction, color;float distance, source_dot, scale;VectorSubtract(l->origin, pos, direction);if(DotProduct(direction, normal) <= EQUAL_EPSILON) {if(l->type == emit_sky)goto do_sky;return result;}switch(l->type) {case emit_surface:case emit_sky: {int hits = 0;for(int j = 0; j < l->surface.winding->numpoints; j++) {VectorSubtract(l->surface.winding->p[j], pos, direction);distance = VectorNormalize(direction, direction);source_dot = -DotProduct(direction, l->surface.normal);if(source_dot <= 0)continue;scale = l->intensity / (distance * distance);SH1_Sample(SH1_Scale(l->color, scale), direction, color);result = SH1_Add(result, SH1_FromDirectionalLight(direction, color));hits++;}if(hits == 0)return result;result = SH1_Scale(result, 1.0f / (float)hits);if(l->type == emit_sky) {result = SH1_Scale(result, total_scale);goto do_sky;}break;}case emit_point: {distance = VectorNormalize(direction, direction);scale = l->falloff == 0 ? (l->intensity - l->wait * distance): l->falloff == 1 ? (l->intensity / distance): (l->intensity / (distance * distance));if(scale < 0)return result;SH1_Sample(SH1_Scale(l->color, scale), direction, color);result = SH1_FromDirectionalLight(direction, color);break;}case emit_spotlight: {distance = VectorNormalize(direction, direction);source_dot = -DotProduct(direction, l->spotlight.normal);if(source_dot <= l->spotlight.dot)return result;scale = (l->intensity - l->wait * distance) * powf(source_dot, 25) * 15;SH1_Sample(SH1_Scale(l->color, scale), direction, color);result = SH1_FromDirectionalLight(direction, color);break;}}return SH1_Scale(result, total_scale);do_sky:if(!*sun_ambient_once) {// TODO}if(!*sun_main_once) {// TODO}return result;}/*=============GatherSampleLightLightscale2 is the normalizer for multisampling, -extra cmd line arg=============*/void GatherSampleLight(vec3_t pos, vec3_t normal, struct SH1 **styletable, int32_t offset, int32_t mapsize,float lightscale2, qboolean *sun_main_once, qboolean *sun_ambient_once, byte *pvs) {int32_t i;directlight_t *l;int32_t nodenum;struct SH1 colors[MAX_LSTYLES];memset(colors, 0, sizeof(colors));// get the PVS for the pos to limit the number of checksif(!PvsForOrigin(pos, pvs)) {return;}nodenum = PointInNodenum(pos);for(i = 0; i < dvis->numclusters; i++) {if(!(pvs[i >> 3] & (1 << (i & 7))))continue;for(l = directlights[i]; l; l = l->next) {struct SH1 sh1 =LightContributionToPoint(l, pos, nodenum, normal, /* color, */ lightscale2, sun_main_once, sun_ambient_once);colors[l->style] = SH1_Add(colors[l->style], sh1);}}for(i = 0; i < MAX_LSTYLES; i++) {// no contributionif(fabs(colors[i].f[0]) <= EQUAL_EPSILON && fabs(colors[i].f[4]) <= EQUAL_EPSILON && fabs(colors[i].f[8]) <= EQUAL_EPSILON)continue;// if this style doesn't have a table yet, allocate oneif(!styletable[i]) {styletable[i] = malloc(mapsize);memset(styletable[i], 0, mapsize);}styletable[i][offset] = SH1_Add(styletable[i][offset], colors[i]);}}/*=============AddSampleToPatchTake the sample's collected light andadd it back into the apropriate patchfor the radiosity pass.The sample is added to all patches that might includeany part of it. They are counted and averaged, so itdoesn't generate extra light.=============*/void AddSampleToPatch(vec3_t pos, struct SH1 sh1, int32_t facenum) {patch_t *patch;vec3_t mins, maxs;int32_t i;if (numbounce == 0)return;for (patch = face_patches[facenum]; patch; patch = patch->next) {// see if the point is in this patch (roughly)WindingBounds(patch->winding, mins, maxs);for (i = 0; i < 3; i++) {if (mins[i] > pos[i] + step)goto nextpatch;if (maxs[i] < pos[i] - step)goto nextpatch;}// add the sample to the patchpatch->samples++;patch->samplelight = SH1_Add(patch->samplelight, sh1);nextpatch:;}}// qb: phong from vluzacn VHLT// =====================================================================================// GetPhongNormal// =====================================================================================void GetPhongNormal(int32_t facenum, vec3_t spot, vec3_t phongnormal) {int32_t j, ne;int32_t s; // split every edge into two partsvec3_t facenormal;// Calculate modified point normal for surface// Use the edge normals if they are defined. Bend the surface towards the edge normal(s)// Crude first attempt: find nearest edge normal and do a simple interpolation with facenormal.// Second attempt: find edge points+center that bound the point and do a three-point triangulation(baricentric)// Better third attempt: generate the point normals for all vertices and do baricentric triangulation.const dface_tx *fx = dfacesX + facenum;const dface_t *fi = dfaces + facenum;if (use_qbsp) {const dplane_t *p = getPlaneFromFaceX(fx);ne = fx->numedges;VectorCopy(p->normal, facenormal);} else {const dplane_t *p = getPlaneFromFace(fi);ne = fi->numedges;VectorCopy(p->normal, facenormal);}VectorCopy(facenormal, phongnormal);for (j = 0; j < ne; j++) {vec3_t p1;vec3_t p2;vec3_t v1;vec3_t v2;vec3_t vspot;unsigned prev_edge;unsigned next_edge;int32_t e;int32_t e1;int32_t e2;edgeshare_t *es;edgeshare_t *es1;edgeshare_t *es2;float a1;float a2;float aa;float bb;float ab;if (use_qbsp) {if (j) {prev_edge = fx->firstedge + ((j + fx->numedges - 1) % fx->numedges);} else {prev_edge = fx->firstedge + fx->numedges - 1;}if ((j + 1) != fx->numedges) {next_edge = fx->firstedge + ((j + 1) % fx->numedges);} else {next_edge = fx->firstedge;}e = dsurfedges[fx->firstedge + j];e1 = dsurfedges[prev_edge];e2 = dsurfedges[next_edge];es = &edgeshare[abs(e)];es1 = &edgeshare[abs(e1)];es2 = &edgeshare[abs(e2)];if ((!es->smooth || es->coplanar) && (!es1->smooth || es1->coplanar) && (!es2->smooth || es2->coplanar)) {continue;}if (e > 0) {VectorCopy(dvertexes[dedgesX[e].v[0]].point, p1);VectorCopy(dvertexes[dedgesX[e].v[1]].point, p2);} else {VectorCopy(dvertexes[dedgesX[-e].v[1]].point, p1);VectorCopy(dvertexes[dedgesX[-e].v[0]].point, p2);}}else {if (j) {prev_edge = fi->firstedge + ((j + fi->numedges - 1) % fi->numedges);} else {prev_edge = fi->firstedge + fi->numedges - 1;}if ((j + 1) != fi->numedges) {next_edge = fi->firstedge + ((j + 1) % fi->numedges);} else {next_edge = fi->firstedge;}e = dsurfedges[fi->firstedge + j];e1 = dsurfedges[prev_edge];e2 = dsurfedges[next_edge];es = &edgeshare[abs(e)];es1 = &edgeshare[abs(e1)];es2 = &edgeshare[abs(e2)];if ((!es->smooth || es->coplanar) && (!es1->smooth || es1->coplanar) && (!es2->smooth || es2->coplanar)) {continue;}if (e > 0) {VectorCopy(dvertexes[dedges[e].v[0]].point, p1);VectorCopy(dvertexes[dedges[e].v[1]].point, p2);} else {VectorCopy(dvertexes[dedges[-e].v[1]].point, p1);VectorCopy(dvertexes[dedges[-e].v[0]].point, p2);}}// Adjust for origin-based modelsVectorAdd(p1, face_offset[facenum], p1);VectorAdd(p2, face_offset[facenum], p2);for (s = 0; s < 2; s++) {vec3_t s1, s2;if (s == 0) {VectorCopy(p1, s1);} else {VectorCopy(p2, s1);}VectorAdd(p1, p2, s2); // edge centerVectorScale(s2, 0.5, s2);VectorSubtract(s1, face_extents[facenum].center, v1);VectorSubtract(s2, face_extents[facenum].center, v2);VectorSubtract(spot, face_extents[facenum].center, vspot);aa = DotProduct(v1, v1);bb = DotProduct(v2, v2);ab = DotProduct(v1, v2);a1 = (bb * DotProduct(v1, vspot) - ab * DotProduct(vspot, v2)) / (aa * bb - ab * ab);a2 = (DotProduct(vspot, v2) - a1 * ab) / bb;// Test center to sample vector for inclusion between center to vertex vectors (Use dot product of vectors)if (a1 >= -0.01 && a2 >= -0.01) {// calculate distance from edge to posvec3_t n1, n2;vec3_t temp;if (es->smooth)if (s == 0) {VectorCopy(es->vertex_normal[e > 0 ? 0 : 1], n1);} else {VectorCopy(es->vertex_normal[e > 0 ? 1 : 0], n1);}else if (s == 0 && es1->smooth) {VectorCopy(es1->vertex_normal[e1 > 0 ? 1 : 0], n1);} else if (s == 1 && es2->smooth) {VectorCopy(es2->vertex_normal[e2 > 0 ? 0 : 1], n1);} else {VectorCopy(facenormal, n1);}if (es->smooth) {VectorCopy(es->interface_normal, n2);} else {VectorCopy(facenormal, n2);}// Interpolate between the center and edge normals based on sample position// VectorScale(facenormal, 1.0 - a1 - a2, phongnormal);VectorScale(facenormal, fabs((1.0 - a1) - a2), phongnormal); // qb: eureka... need that fabs()!VectorScale(n1, a1, temp);VectorAdd(phongnormal, temp, phongnormal);VectorScale(n2, a2, temp);VectorAdd(phongnormal, temp, phongnormal);VectorNormalize(phongnormal, phongnormal);break;}}}}/*** @brief Move the incoming sample position towards the surface center and along the* surface normal to reduce false-positive traces. Test the PVS at the new* position, returning true if the new point is valid, false otherwise.*/static qboolean NudgeSamplePosition(const vec3_t in, const vec3_t normal, const vec3_t center,vec3_t out, byte *pvs) {vec3_t dir;VectorCopy(in, out);// move into the level using the normal and surface centerVectorSubtract(out, center, dir);VectorNormalize(dir, dir);VectorMA(out, sample_nudge, dir, out);VectorMA(out, sample_nudge, normal, out);return PvsForOrigin(out, pvs);}/*=============BuildFacelights=============*/float sampleofs[5][2] ={{0, 0}, {-0.25, -0.25}, {0.25, -0.25}, {0.25, 0.25}, {-0.25, 0.25}};void BuildFacelights(int32_t facenum) {lightinfo_t * liteinfo;//[5];struct SH1 **styletable;//[MAX_LSTYLES];int32_t i, j;float *spot;patch_t *patch;int32_t numsamples;int32_t tablesize;facelight_t *fl;qboolean sun_main_once, sun_ambient_once;vec_t *center;vec3_t pos;vec3_t pointnormal;liteinfo = malloc(sizeof(*liteinfo) * 5);styletable = malloc(sizeof(*styletable) * MAX_LSTYLES);if (use_qbsp) {dface_tx *this_face;this_face = &dfacesX[facenum];if (texinfo[this_face->texinfo].flags & (SURF_WARP | SURF_SKY))goto cleanup; // non-lit texturememset(styletable, 0, sizeof(*styletable) * MAX_LSTYLES);if (extrasamples) // set with -extra optionnumsamples = 5;elsenumsamples = 1;for (i = 0; i < numsamples; i++) {memset(&liteinfo[i], 0, sizeof(liteinfo[i]));liteinfo[i].surfnum = facenum;liteinfo[i].faceX = this_face;VectorCopy(dplanes[this_face->planenum].normal, liteinfo[i].facenormal);liteinfo[i].facedist = dplanes[this_face->planenum].dist;if (this_face->side) {VectorSubtract(vec3_origin, liteinfo[i].facenormal, liteinfo[i].facenormal);liteinfo[i].facedist = -liteinfo[i].facedist;}// get the origin offset for rotating bmodelsVectorCopy(face_offset[facenum], liteinfo[i].modelorg);CalcFaceVectors(&liteinfo[i]);CalcFaceExtents(&liteinfo[i]);CalcPoints(&liteinfo[i], sampleofs[i][0], sampleofs[i][1]);}} else {dface_t *this_face;this_face = &dfaces[facenum];if (texinfo[this_face->texinfo].flags & (SURF_WARP | SURF_SKY))goto cleanup; // non-lit texturememset(styletable, 0, sizeof(*styletable) * MAX_LSTYLES);if (extrasamples) // set with -extra optionnumsamples = 5;elsenumsamples = 1;for (i = 0; i < numsamples; i++) {memset(&liteinfo[i], 0, sizeof(liteinfo[i]));liteinfo[i].surfnum = facenum;liteinfo[i].face = this_face;VectorCopy(dplanes[this_face->planenum].normal, liteinfo[i].facenormal);liteinfo[i].facedist = dplanes[this_face->planenum].dist;if (this_face->side) {VectorSubtract(vec3_origin, liteinfo[i].facenormal, liteinfo[i].facenormal);liteinfo[i].facedist = -liteinfo[i].facedist;}// get the origin offset for rotating bmodelsVectorCopy(face_offset[facenum], liteinfo[i].modelorg);CalcFaceVectors(&liteinfo[i]);CalcFaceExtents(&liteinfo[i]);CalcPoints(&liteinfo[i], sampleofs[i][0], sampleofs[i][1]);}}tablesize = liteinfo[0].numsurfpt * sizeof(struct SH1);styletable[0] = malloc(tablesize);memset(styletable[0], 0, tablesize);fl = &facelight[facenum];fl->numsamples = liteinfo[0].numsurfpt;fl->origins = malloc(tablesize);memcpy(fl->origins, liteinfo[0].surfpt, tablesize);center = face_extents[facenum].center; // center of the facefor (i = 0; i < liteinfo[0].numsurfpt; i++) {sun_ambient_once = false;sun_main_once = false;for (j = 0; j < numsamples; j++) {byte pvs[(MAX_MAP_LEAFS_QBSP + 7) / 8];if (numsamples > 1) {if (!NudgeSamplePosition(liteinfo[j].surfpt[i], liteinfo[0].facenormal, center, pos, pvs)) {continue; // not a valid point}} elseVectorCopy(liteinfo[j].surfpt[i], pos);if (smoothing_threshold > 0.0)GetPhongNormal(facenum, pos, pointnormal); // qb: VHLTelseVectorCopy(liteinfo[0].facenormal, pointnormal);GatherSampleLight(pos, pointnormal, styletable, i, tablesize, 1.0 / numsamples,&sun_main_once, &sun_ambient_once, pvs);}// contribute the sample to one or more patchesAddSampleToPatch(liteinfo[0].surfpt[i], styletable[0][i], facenum);}// average up the direct light on each patch for radiosityfor (patch = face_patches[facenum]; patch; patch = patch->next) {if (patch->samples) {vec3_t normal;VectorCopy(patch->plane->normal, normal);patch->samplelight = SH1_Reflect(SH1_Scale(patch->samplelight, 1.0f / patch->samples), normal);}}for (i = 0; i < MAX_LSTYLES; i++) {if (!styletable[i])continue;if (fl->numstyles == MAX_STYLES)break;fl->samples[fl->numstyles] = styletable[i];fl->stylenums[fl->numstyles] = i;fl->numstyles++;}// the light from DIRECT_LIGHTS is sent out, but the// texture itself should still be full brightif (face_patches[facenum]->baselight.f[0] >= DIRECT_LIGHT ||face_patches[facenum]->baselight.f[4] >= DIRECT_LIGHT ||face_patches[facenum]->baselight.f[8] >= DIRECT_LIGHT) {for (i = 0; i < liteinfo[0].numsurfpt; i++) {fl->samples[0][i] = SH1_Add(fl->samples[0][i], face_patches[facenum]->baselight);}}cleanup:free(liteinfo);free(styletable);}/*=============FinalLightFaceAdd the indirect lighting on top of the directlighting and save into final map format=============*/void FinalLightFace(int32_t facenum) {int32_t i, j, st;vec3_t lb;patch_t *patch;triangulation_t *trian = NULL;facelight_t *fl;float max;float newmax;byte *dest_rgb0, *dest_r1, *dest_g1, *dest_b1;triangle_t *last_valid;int32_t pfacenum;vec3_t facemins, facemaxs;fl = &facelight[facenum];ThreadLock();i = lightdatasize;lightdatasize += fl->numstyles * (fl->numsamples * 3 * 4);if (lightdatasize > maxdata) {printf("face %d of %d\n", facenum, numfaces);Error("lightdatasize %i > maxdata %i", lightdatasize, maxdata);}ThreadUnlock();if (use_qbsp) {// dface_tx *f;// f = &dfacesX[facenum];// if (texinfo[f->texinfo].flags & (SURF_WARP | SURF_SKY))// return; // non-lit texture// f->lightofs = i;// f->styles[0] = 0;// f->styles[1] = f->styles[2] = f->styles[3] = 0xff;// //// // set up the triangulation// //// if (numbounce > 0) {// ClearBounds(facemins, facemaxs);// for (i = 0; i < f->numedges; i++) {// int32_t ednum;// ednum = dsurfedges[f->firstedge + i];// if (ednum >= 0)// AddPointToBounds(dvertexes[dedgesX[ednum].v[0]].point,// facemins, facemaxs);// else// AddPointToBounds(dvertexes[dedgesX[-ednum].v[1]].point,// facemins, facemaxs);// }// trian = AllocTriangulation(&dplanes[f->planenum]);// // for all faces on the plane, add the nearby patches// // to the triangulation// for (pfacenum = planelinks[f->side][f->planenum]; pfacenum; pfacenum = facelinks[pfacenum]) {// for (patch = face_patches[pfacenum]; patch; patch = patch->next) {// for (i = 0; i < 3; i++) {// if (facemins[i] - patch->origin[i] > subdiv * 2)// break;// if (patch->origin[i] - facemaxs[i] > subdiv * 2)// break;// }// if (i != 3)// continue; // not needed for this face// AddPointToTriangulation(patch, trian);// }// }// for (i = 0; i < trian->numpoints; i++)// memset(trian->edgematrix[i], 0, trian->numpoints * sizeof(trian->edgematrix[0][0]));// TriangulatePoints(trian);// }// //// // sample the triangulation// //// dest = &dlightdata_ptr[f->lightofs];// if (fl->numstyles > MAXLIGHTMAPS) {// fl->numstyles = MAXLIGHTMAPS;// // printf ("face with too many lightstyles: (%f %f %f)\n",// // face_patches[facenum]->origin[0],// // face_patches[facenum]->origin[1],// // face_patches[facenum]->origin[2]// // );// }// for (st = 0; st < fl->numstyles; st++) {// last_valid = NULL;// f->styles[st] = fl->stylenums[st];// for (j = 0; j < fl->numsamples; j++) {// struct SH1 color = fl->samples[st][j];// if (numbounce > 0 && st == 0) {// color = SH1_Add(color, SampleTriangulation(fl->origins + j * 3, trian, &last_valid));// }// SH1_Sample(color, f->side ? backplanes[f->planenum].normal : dplanes[f->planenum].normal, lb);// /*// * to allow experimenting, ambient and lightscale are not limited// * to reasonable ranges.// */// if (ambient >= -255.0f && ambient <= 255.0f) {// // add fixed white ambient.// lb[0] += ambient;// lb[1] += ambient;// lb[2] += ambient;// }// if (lightscale > 0.0f) {// // apply lightscale, scale down or up// lb[0] *= lightscale;// lb[1] *= lightscale;// lb[2] *= lightscale;// }// // negative values not allowed// lb[0] = (lb[0] < 0.0f) ? 0.0f : lb[0];// lb[1] = (lb[1] < 0.0f) ? 0.0f : lb[1];// lb[2] = (lb[2] < 0.0f) ? 0.0f : lb[2];// /* qprintf("{%f %f %f}:",lb[0],lb[1],lb[2]);*/// // determine max of R,G,B// max = lb[0] > lb[1] ? lb[0] : lb[1];// max = max > lb[2] ? max : lb[2];// if (max < 1.0f)// max = 1.0f;// // note that maxlight based scaling is per-sample based on// // highest value of R, G, and B// // adjust for -maxlight option// newmax = max;// if (max > maxlight) {// newmax = maxlight;// newmax /= max; // scaling factor 0.0..1.0// // scale into 0.0..maxlight range// lb[0] *= newmax;// lb[1] *= newmax;// lb[2] *= newmax;// }// // and output to 8:8:8 RGB// *dest++ = (byte)(lb[0] + 0.5);// *dest++ = (byte)(lb[1] + 0.5);// *dest++ = (byte)(lb[2] + 0.5);// }// }} else {// ibspdface_t *f;f = &dfaces[facenum];if (texinfo[f->texinfo].flags & (SURF_WARP | SURF_SKY))return; // non-lit texturef->lightofs = i;f->styles[0] = 0;f->styles[1] = f->styles[2] = f->styles[3] = 0xff;//// set up the triangulation//if (numbounce > 0) {ClearBounds(facemins, facemaxs);for (i = 0; i < f->numedges; i++) {int32_t ednum;ednum = dsurfedges[f->firstedge + i];if (ednum >= 0)AddPointToBounds(dvertexes[dedges[ednum].v[0]].point,facemins, facemaxs);elseAddPointToBounds(dvertexes[dedges[-ednum].v[1]].point,facemins, facemaxs);}trian = AllocTriangulation(&dplanes[f->planenum]);// for all faces on the plane, add the nearby patches// to the triangulationfor (pfacenum = planelinks[f->side][f->planenum]; pfacenum; pfacenum = facelinks[pfacenum]) {for (patch = face_patches[pfacenum]; patch; patch = patch->next) {for (i = 0; i < 3; i++) {if (facemins[i] - patch->origin[i] > subdiv * 2)break;if (patch->origin[i] - facemaxs[i] > subdiv * 2)break;}if (i != 3)continue; // not needed for this faceAddPointToTriangulation(patch, trian);}}for (i = 0; i < trian->numpoints; i++)memset(trian->edgematrix[i], 0, trian->numpoints * sizeof(trian->edgematrix[0][0]));TriangulatePoints(trian);}//// sample the triangulation//dest_rgb0 = &dlightdata_ptr[f->lightofs + (fl->numsamples * 3 * 0)];dest_r1 = &dlightdata_ptr[f->lightofs + (fl->numsamples * 3 * 1)];dest_g1 = &dlightdata_ptr[f->lightofs + (fl->numsamples * 3 * 2)];dest_b1 = &dlightdata_ptr[f->lightofs + (fl->numsamples * 3 * 3)];if (fl->numstyles > MAXLIGHTMAPS) {fl->numstyles = MAXLIGHTMAPS;// printf ("face with too many lightstyles: (%f %f %f)\n",// face_patches[facenum]->origin[0],// face_patches[facenum]->origin[1],// face_patches[facenum]->origin[2]// );}for (st = 0; st < fl->numstyles; st++) {last_valid = NULL;f->styles[st] = fl->stylenums[st];for (j = 0; j < fl->numsamples; j++) {struct SH1 color = fl->samples[st][j];if (numbounce > 0 && st == 0) {color = SH1_Add(color, SampleTriangulation(fl->origins + j * 3, trian, &last_valid));}/** to allow experimenting, ambient and lightscale are not limited* to reasonable ranges.*/if (ambient >= -255.0f && ambient <= 255.0f) {// add fixed white ambient.color.f[0] += ambient;color.f[4] += ambient;color.f[8] += ambient;}if (lightscale > 0.0f) {// apply lightscale, scale down or upcolor = SH1_Scale(color, lightscale);}float color_max;color = SH1_NormalizeMaximum(color, maxlight - 0.5f, &color_max);for(int component = 0; component < 3; component++) {if(color.f[component * 4 + 0] > maxlight)assert(0);for(int basis = 0; basis < 3; basis++) {color.f[component * 4 + basis + 1] *= 0.5f;color.f[component * 4 + basis + 1] += 127.0f;if(color.f[component * 4 + basis + 1] > 256)assert(0);}}// and output to 8:8:8 RGB*dest_rgb0++ = (byte)(color.f[0] + 0.5f);*dest_r1++ = (byte)(color.f[1] + 0.5f);*dest_r1++ = (byte)(color.f[2] + 0.5f);*dest_r1++ = (byte)(color.f[3] + 0.5f);*dest_rgb0++ = (byte)(color.f[4] + 0.5f);*dest_g1++ = (byte)(color.f[5] + 0.5f);*dest_g1++ = (byte)(color.f[6] + 0.5f);*dest_g1++ = (byte)(color.f[7] + 0.5f);*dest_rgb0++ = (byte)(color.f[8] + 0.5f);*dest_b1++ = (byte)(color.f[9] + 0.5f);*dest_b1++ = (byte)(color.f[10] + 0.5f);*dest_b1++ = (byte)(color.f[11] + 0.5f);}}}if (numbounce > 0)FreeTriangulation(trian);}
#include "qrad.h"extern qboolean dumppatches;extern qboolean nopvs;extern qboolean save_trace;extern float patch_cutoff;extern char inbase[32];extern char outbase[32];void RAD_ProcessArgument(const char * arg);int32_t main(int32_t argc, char **argv) {int32_t i;char tgamedir[1024] = "", tbasedir[1024] = "", tmoddir[1024] = "";printf("\n\n<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< 4rad >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n");printf("radiosity compiler build " __DATE__ "\n");verbose = false;numthreads = -1;maxdata = DEFAULT_MAP_LIGHTING;step = LMSTEP;for (i = 1; i < argc; i++) {if (!strcmp(argv[i], "-dump"))dumppatches = true;else if (!strcmp(argv[i], "-bounce")) {numbounce = atoi(argv[i + 1]);i++;} else if (!strcmp(argv[i], "-v")) {verbose = true;} else if (!strcmp(argv[i], "-help")) {printf("4rad with automatic phong and QBSP extended limit support.\n""Usage: 4rad [options] [mapname]\n\n"" -ambient #: Minimum light level.\n"" range: 0 to 255.\n"" -moddir [path]: Set a mod directory. Default is parent dir of map file.\n"" -basedir [path]: Set the directory for assets not in moddir. Default is moddir.\n"" -gamedir [path]: Set game directory, the folder with game executable.\n"" -bounce #: Max number of light bounces for radiosity.\n"" -dice: Subdivide patches with a global grid rather than per patch.\n"" -direct #: Direct light scale factor.\n"" -entity #: Entity light scale factor.\n"" -extra: Use extra samples to smooth lighting.\n"" -maxdata #: 2097152 is default max. Not needed for QBSP format.\n"" Increase requires a supporting engine.\n"" -maxlight #: Maximium light level.\n"" range: 0 to 255.\n"" -noedgefix: disable dark edges at sky fix. More of a hack, really.\n"" -nudge #: Nudge factor for samples. Distance fraction from center.\n"" -saturate #: Saturation factor of light bounced off surfaces.\n"" -scale #: Light intensity multiplier.\n"" -smooth #: Threshold angle (# and 180deg - #) for phong smoothing.\n"" -subdiv (or -chop) #: Maximum patch size. Default: 64\n"" -sunradscale #: Sky light intensity scale when sun is active.\n"" -threads #: Number of CPU cores to use.\n""Debugging tools:\n"" -dump: Dump patches to a text file.\n"" -noblock: Brushes don't block lighting path.\n"" -nopvs: Don't do potential visibility set check.\n"" -savetrace: Test traces and report errors.\n"" -tmpin: Read from 'tmp' directory.\n"" -tmpout: Write to 'tmp' directory.\n"" -v: Verbose output for debugging.\n\n");printf("<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< 4rad HELP >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n\n");exit(1);}else if (!strcmp(argv[i], "-extra")) {extrasamples = true;printf("extrasamples = true\n");} else if (!strcmp(argv[i], "-noedgefix")) // qb: light warp surfaces{noedgefix = true;printf("no edge fix = true\n");} else if (!strcmp(argv[i], "-dice")) {dicepatches = true;printf("dicepatches = true\n");} else if (!strcmp(argv[i], "-threads")) {numthreads = atoi(argv[i + 1]);i++;} else if (!strcmp(argv[i], "-maxdata")) { // qb: allows increase for some enginesmaxdata = atoi(argv[i + 1]);i++;if (maxdata > DEFAULT_MAP_LIGHTING) {printf("lighting maxdata (%i) exceeds typical limit (%i).\n", maxdata, DEFAULT_MAP_LIGHTING);}}// qb: set gamedir, moddir, and basedirelse if (!strcmp(argv[i], "-gamedir")) {strcpy(tgamedir, argv[i + 1]);i++;} else if (!strcmp(argv[i], "-basedir")) {strcpy(tbasedir, argv[i + 1]);i++;} else if (!strcmp(argv[i], "-moddir")) {strcpy(tmoddir, argv[i + 1]);i++;}else if ((!strcmp(argv[i], "-chop")) || (!strcmp(argv[i], "-subdiv"))) {subdiv = atoi(argv[i + 1]);if (subdiv < 16) {subdiv = 16;printf("subdiv set to minimum: 16\n");} else if (subdiv > 1024) {subdiv = 1024;printf("subdiv set to maximum: 1024\n");}i++;}else if (!strcmp(argv[i], "-scale")) {lightscale = atof(argv[i + 1]);i++;} else if (!strcmp(argv[i], "-sunradscale")) {sunradscale = atof(argv[i + 1]);if (sunradscale < 0) {sunradscale = 0;printf("sunradscale set to minimum: 0\n");}printf("sunradscale = %f\n", sunradscale);i++;} else if (!strcmp(argv[i], "-saturation")) {saturation = atof(argv[i + 1]);i++;} else if (!strcmp(argv[i], "-radmin")) {patch_cutoff = atof(argv[i + 1]);i++;} else if (!strcmp(argv[i], "-direct")) {direct_scale *= atof(argv[i + 1]);// printf ("direct light scaling at %f\n", direct_scale);i++;} else if (!strcmp(argv[i], "-entity")) {entity_scale *= atof(argv[i + 1]);// printf ("entity light scaling at %f\n", entity_scale);i++;} else if (!strcmp(argv[i], "-nopvs")) {nopvs = true;printf("nopvs = true\n");} else if (!strcmp(argv[i], "-nopvs")) {nopvs = true;printf("nopvs = true\n");} else if (!strcmp(argv[i], "-noblock")) {noblock = true;printf("noblock = true\n");} else if (!strcmp(argv[i], "-smooth")) {// qb: limit rangesmoothing_value = BOUND(0, atof(argv[i + 1]), 90);i++;} else if (!strcmp(argv[i], "-nudge")) {sample_nudge = atof(argv[i + 1]);// qb: nah, go crazy. sample_nudge = BOUND(0, sample_nudge, 1.0);i++;} else if (!strcmp(argv[i], "-ambient")) {ambient = BOUND(0, atof(argv[i + 1]), 255);i++;} else if (!strcmp(argv[i], "-savetrace")) {save_trace = true;printf("savetrace = true\n");} else if (!strcmp(argv[i], "-maxlight")) {maxlight = BOUND(0, atof(argv[i + 1]), 255);i++;} else if (!strcmp(argv[i], "-tmpin"))strcpy(inbase, "/tmp");else if (!strcmp(argv[i], "-tmpout"))strcpy(outbase, "/tmp");elsebreak;}if (i != argc - 1) {printf("Usage: 4rad [options] [mapname]\n"" -ambient # -bounce #\n"" -dice -direct # -entity #\n"" -extra -help -maxdata #\n"" -maxlight # -noedgefix -nudge #\n"" -saturate # -scale # -smooth #\n"" -subdiv -sunradscale # -threads #\n"" -gamedir [path] -basedir [path] -moddir [path]\n""Debugging tools:\n"" -dump -noblock -nopvs\n"" -savetrace -tmpin -tmpout\n"" -v (verbose)\n\n");exit(1);}printf("sample nudge: %f\n", sample_nudge);printf("ambient : %f\n", ambient);printf("scale : %f\n", lightscale);printf("maxlight : %f\n", maxlight);printf("entity : %f\n", entity_scale);printf("direct : %f\n", direct_scale);printf("saturation : %f\n", saturation);printf("bounce : %d\n", numbounce);printf("radmin : %f\n", patch_cutoff);printf("subdiv : %f\n", subdiv);printf("smooth angle: %f\n", smoothing_value);printf("nudge : %f\n", sample_nudge);printf("threads : %d\n", numthreads);ThreadSetDefault();smoothing_threshold = (float)cos(smoothing_value * (Q_PI / 180.0));SetQdirFromPath(argv[i]);if (strcmp(tmoddir, "")) {strcpy(moddir, tmoddir);Q_pathslash(moddir);strcpy(basedir, moddir);}if (strcmp(tbasedir, "")) {strcpy(basedir, tbasedir);Q_pathslash(basedir);if (!strcmp(tmoddir, ""))strcpy(moddir, basedir);}if (strcmp(tgamedir, "")) {strcpy(gamedir, tgamedir);Q_pathslash(gamedir);}// qb: display dirsprintf("moddir = %s\n", moddir);printf("basedir = %s\n", basedir);printf("gamedir = %s\n", gamedir);RAD_ProcessArgument(argv[i]);return 0;}
/*===========================================================================Copyright (C) 1997-2006 Id Software, Inc.This program is free software; you can redistribute it and/or modifyit under the terms of the GNU General Public License as published bythe 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 ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See theGNU General Public License for more details.You should have received a copy of the GNU General Public License alongwith this program; if not, write to the Free Software Foundation, Inc.,51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.===========================================================================*/#include "qrad.h"/*NOTES-----every surface must be divided into at least two patches each axis*/patch_t *face_patches[MAX_MAP_FACES_QBSP];entity_t *face_entity[MAX_MAP_FACES_QBSP];patch_t *patches; //[MAX_PATCHES_QBSP];unsigned num_patches;int32_t num_smoothing; // qb: number of phong hitsstruct SH1 radiosity[MAX_PATCHES_QBSP]; // light leaving a patchstruct SH1 illumination[MAX_PATCHES_QBSP]; // light arriving at a patchvec3_t face_offset[MAX_MAP_FACES_QBSP]; // for rotating bmodelsdplane_t backplanes[MAX_MAP_PLANES_QBSP];extern char inbase[32], outbase[32];int32_t fakeplanes; // created planes for origin offsetint32_t numbounce = 4; // default was 8qboolean noblock = false; // when true, disables occlusion testing on light raysqboolean extrasamples = false;qboolean dicepatches = false;qboolean noedgefix = false;int32_t memory = false;float patch_cutoff = 0.0f; // set with -radmin 0.0..1.0, see MakeTransfers()float subdiv = 64;qboolean dumppatches;void BuildFaceExtents(void); // qb: from quemapint32_t TestLine(vec3_t start, vec3_t stop);float smoothing_threshold; // qb: phong from VHLTfloat smoothing_value = DEFAULT_SMOOTHING_VALUE;float sample_nudge = DEFAULT_NUDGE_VALUE; // qb: adjustable nudge for multisample/** 2010-09 Notes* These variables are somewhat confusing. The floating point color values are* in the range 0..255 (floating point color should be 0.0 .. 1.0 IMO.)* The following may or may not be precisely correct.* (There are other variables, surface flags, etc., affecting lighting. What* they do, or whether they work at all is "to be determined")** see lightmap.c:FinalLightFace()* sequence: ambient is added, lightscale is applied, RGB is "normalized" to* 0..255 range, grayscale is applied, RGB is clamped to maxlight.** ambient:* set with -ambient option, 0..255 (but only small numbers are useful)* adds the same value to R, G & B* default is 0** lightscale:* set with -scale option, 0.0..1.0* scales lightmap globally.** saturation:* set with -saturation, 0.0..1.0 //qb: does higher than 1 work?* proportionally saturation texture reflectivity** direct_scale:* set with -direct option, 0.0..1.0* controls reflection from emissive surfaces, i.e. brushes with a light value* research indicates it is not the usual practice to include this in* radiosity, so the default should be 0.0. (Would be nice to have this* a per-surface option where surfaces are used like point lights.)** entity_scale:* set with -entity option, 0.0..1.0* controls point light radiosity, i.e. light entities.* default is 1.0, no attenuation of point lights***/#include <assert.h>float ambient = 0.0f;float lightscale = 1.0f;float maxlight = 255.0f;// qboolean nocolor = false;float grayscale = 0.0f;float saturation = 1.0f; // qb: change desaturate to saturationfloat direct_scale = 1.0f;float entity_scale = 1.0f;/** 2010-09 Notes:* These are controlled by setting keys in the worldspawn entity. A light* entity targeting an info_null entity is used to determine the vector for* the sun directional lighting; variable name "sun_pos" is a misnomer, i think.* Example:* "_sun" "sun_target" # activates sun* "_sun_color" "1.0 1.0 0.0" # for yellow, sets sun_alt_color true* "_sun_light" "50" # light value, variable is sun_main* "_sun_ambient" "2" # an ambient light value in the sun color. variable is sun_ambient** It might or might not be the case:* if there is no info_null for the light entity with the "sun_target" to* target, then {0,0,0} is used for the target. If _sun_color is not specified* in the .map, the color of the light entity is used.*/qboolean sun = false;qboolean sun_alt_color = false;vec3_t sun_pos = {0.0f, 0.0f, 1.0f};float sun_main = 250.0f;float sun_ambient = 0.0f;vec3_t sun_color = {1, 1, 1};qboolean nopvs;qboolean save_trace = false;extern char source[1024];/*===================================================================MISC===================================================================*//*=============MakeBackplanes=============*/void MakeBackplanes(void) {int32_t i;for(i = 0; i < numplanes; i++) {backplanes[i].dist = -dplanes[i].dist;VectorSubtract(vec3_origin, dplanes[i].normal, backplanes[i].normal);}}int32_t leafparents[MAX_MAP_LEAFS_QBSP];int32_t nodeparents[MAX_MAP_NODES_QBSP];/*=============MakeParents=============*/void MakeParents(int32_t nodenum, int32_t parent) {int32_t i, j;nodeparents[nodenum] = parent;if(use_qbsp) {dnode_tx *node;node = &dnodesX[nodenum];for(i = 0; i < 2; i++) {j = node->children[i];if(j < 0)leafparents[-j - 1] = nodenum;elseMakeParents(j, nodenum);}} else {dnode_t *node;node = &dnodes[nodenum];for(i = 0; i < 2; i++) {j = node->children[i];if(j < 0)leafparents[-j - 1] = nodenum;elseMakeParents(j, nodenum);}}}/*===================================================================TRANSFER SCALES===================================================================*/int32_t PointInLeafnum(vec3_t point) {int32_t nodenum;vec_t dist;dplane_t *plane;nodenum = 0;if(use_qbsp) {dnode_tx *node;while(nodenum >= 0) {node = &dnodesX[nodenum];plane = &dplanes[node->planenum];dist = DotProduct(point, plane->normal) - plane->dist;if(dist > 0)nodenum = node->children[0];elsenodenum = node->children[1];}} else {dnode_t *node;while(nodenum >= 0) {node = &dnodes[nodenum];plane = &dplanes[node->planenum];dist = DotProduct(point, plane->normal) - plane->dist;if(dist > 0)nodenum = node->children[0];elsenodenum = node->children[1];}}return -nodenum - 1;}dleaf_tx *RadPointInLeafX(vec3_t point) {int32_t num;num = PointInLeafnum(point);return &dleafsX[num];}dleaf_t *RadPointInLeaf(vec3_t point) {int32_t num;num = PointInLeafnum(point);return &dleafs[num];}qboolean PvsForOrigin(vec3_t org, byte *pvs) {if(!visdatasize) {memset(pvs, 255, (numleafs + 7) / 8);return true;}if(use_qbsp) {dleaf_tx *leaf;leaf = RadPointInLeafX(org);if(leaf->cluster == -1)return false; // in solid leafDecompressVis(dvisdata + dvis->bitofs[leaf->cluster][DVIS_PVS], pvs);} else {dleaf_t *leaf;leaf = RadPointInLeaf(org);if(leaf->cluster == -1)return false; // in solid leafDecompressVis(dvisdata + dvis->bitofs[leaf->cluster][DVIS_PVS], pvs);}return true;}typedef struct tnode_s {int32_t type;vec3_t normal;float dist;int32_t children[2];int32_t pad;} tnode_t;extern tnode_t *tnodes;int32_t total_transfer;static long total_mem;static int32_t first_transfer = 1;#define MAX_TRACE_BUF ((MAX_PATCHES_QBSP + 7) / 8)#define TRACE_BYTE(x) (((x) + 7) >> 3)#define TRACE_BIT(x) ((x)&0x1F)static byte trace_buf[MAX_TRACE_BUF + 1];static byte trace_tmp[MAX_TRACE_BUF + 1];static int32_t trace_buf_size;int32_t CompressBytes(int32_t size, byte *source, byte *dest) {int32_t j;int32_t rep;byte *dest_p;dest_p = dest + 1;for(j = 0; j < size; j++) {*dest_p++ = source[j];if((dest_p - dest - 1) >= size) {memcpy(dest + 1, source, size);dest[0] = 0;return size + 1;}if(source[j])continue;rep = 1;for(j++; j < size; j++)if(source[j] || rep == 255)break;elserep++;*dest_p++ = rep;if((dest_p - dest - 1) >= size) {memcpy(dest + 1, source, size);dest[0] = 0;return size + 1;}j--;}dest[0] = 1;return dest_p - dest;}void DecompressBytes(int32_t size, byte *in, byte *decompressed) {int32_t c;byte *out;if(in[0] == 0) // not compressed{memcpy(decompressed, in + 1, size);return;}out = decompressed;in++;do {if(*in) {*out++ = *in++;continue;}c = in[1];if(!c)Error("DecompressBytes: 0 repeat");in += 2;while(c) {*out++ = 0;c--;}} while(out - decompressed < size);}static int32_t trace_bytes = 0;#ifdef WIN32static inline int32_t lowestCommonNode(int32_t nodeNum1, int32_t nodeNum2)#elsestatic inline int32_t lowestCommonNode(int32_t nodeNum1, int32_t nodeNum2)#endif{int32_t child1, tmp, headNode = 0;if(nodeNum1 > nodeNum2) {tmp = nodeNum1;nodeNum1 = nodeNum2;nodeNum2 = tmp;}re_test:// headNode is guaranteed to be <= nodeNum1 and nodeNum1 is < nodeNum2if(headNode == nodeNum1)return headNode;if(use_qbsp) {dnode_tx *node;child1 = (node = dnodesX + headNode)->children[1];if(nodeNum2 < child1)// Both nodeNum1 and nodeNum2 are less than child1.// In this case, child0 is always a node, not a leaf, so we don't need// to check to make sure.headNode = node->children[0];else if(nodeNum1 < child1)// Child1 sits between nodeNum1 and nodeNum2.// This means that headNode is the lowest node which contains both// nodeNum1 and nodeNum2.return headNode;else if(child1 > 0)// Both nodeNum1 and nodeNum2 are greater than child1.// If child1 is a node, that means it contains both nodeNum1 and// nodeNum2.headNode = child1;else// Child1 is a leaf, therefore by process of elimination child0 must be// a node and must contain boste nodeNum1 and nodeNum2.headNode = node->children[0];// goto instead of while(1) because it makes the CPU branch predict easier} else {dnode_t *node;child1 = (node = dnodes + headNode)->children[1];if(nodeNum2 < child1)headNode = node->children[0];else if(nodeNum1 < child1)return headNode;else if(child1 > 0)headNode = child1;elseheadNode = node->children[0];}goto re_test;}void MakeTransfers(int32_t i) {int32_t j;vec3_t delta;vec_t dist, inv_dist = 0, scale;float trans;int32_t itrans;patch_t *patch, *patch2;float total, inv_total;dplane_t plane;vec3_t origin;float *transfers; //[MAX_PATCHES_QBSP];int32_t s;int32_t itotal;byte pvs[(MAX_MAP_LEAFS_QBSP + 7) / 8];int32_t cluster;int32_t calc_trace, test_trace;patch = patches + i;total = 0;VectorCopy(patch->origin, origin);plane = *patch->plane;if(!PvsForOrigin(patch->origin, pvs))return;if(patch->area == 0)return;// find out which patches will collect light from patchpatch->numtransfers = 0;calc_trace = (save_trace && memory && first_transfer);test_trace = (save_trace && memory && !first_transfer);if(calc_trace) {memset(trace_buf, 0, trace_buf_size);} else if(test_trace) {DecompressBytes(trace_buf_size, patch->trace_hit, trace_buf);}transfers = malloc(sizeof(*transfers) * num_patches);for(j = 0, patch2 = patches; j < num_patches; j++, patch2++) {transfers[j] = 0;if(j == i)continue;if(patch2->area == 0)continue;// check pvs bitif(!nopvs) {cluster = patch2->cluster;if(cluster == -1)continue;if(!(pvs[cluster >> 3] & (1 << (cluster & 7))))continue; // not in pvs}if(test_trace && !(trace_buf[TRACE_BYTE(j)] & TRACE_BIT(j)))continue;// calculate vectorVectorSubtract(patch2->origin, origin, delta);dist = VectorNormalize(delta, delta);if(dist == 0) {continue;} else {dist = sqrt(dist);inv_dist = 1.0f / dist;delta[0] *= inv_dist;delta[1] *= inv_dist;delta[2] *= inv_dist;}// relative anglesfloat leaving_dot = DotProduct(delta, plane.normal);float incoming_dot = -DotProduct(delta, patch2->plane->normal);if(leaving_dot * incoming_dot <= 0)continue;// check exact transferscale = leaving_dot * incoming_dot;trans = scale * patch2->area * inv_dist * inv_dist;if(trans > patch_cutoff) {if(!test_trace && !noblock && patch2->nodenum != patch->nodenum &&TestLine_r(lowestCommonNode(patch->nodenum, patch2->nodenum), patch->origin, patch2->origin)) {transfers[j] = 0;continue;}transfers[j] = trans;total += trans;patch->numtransfers++;}}// copy the transfers out and normalize// total should be somewhere near PI if everything went right// because partial occlusion isn't accounted for, and nearby// patches have underestimated form factors, it will usually// be higher than PIif(patch->numtransfers) {transfer_t *t;if(patch->numtransfers < 0 || patch->numtransfers > MAX_PATCHES_QBSP)Error("Weird numtransfers");s = patch->numtransfers * sizeof(transfer_t);patch->transfers = malloc(s);total_mem += s;if(!patch->transfers)Error("Memory allocation failure");//// normalize all transfers so all of the light// is transfered to the surroundings//t = patch->transfers;itotal = 0;inv_total = 65536.0f / total;for(j = 0; j < num_patches; j++) {if(transfers[j] <= 0)continue;itrans = transfers[j] * inv_total;itotal += itrans;t->transfer = itrans;t->patch = j;t++;if(calc_trace) {trace_buf[TRACE_BYTE(j)] |= TRACE_BIT(j);}}}if(calc_trace) {j = CompressBytes(trace_buf_size, trace_buf, trace_tmp);patch->trace_hit = malloc(j);memcpy(patch->trace_hit, trace_tmp, j);trace_bytes += j;}// don't bother locking around this. not that important.total_transfer += patch->numtransfers;// cleanup:free(transfers);}/*=============FreeTransfers=============*/void FreeTransfers(void) {int32_t i;for(i = 0; i < num_patches; i++) {if(!memory) {free(patches[i].transfers);patches[i].transfers = NULL;} else if(patches[i].trace_hit != NULL) {free(patches[i].trace_hit);patches[i].trace_hit = NULL;}}}//===================================================================/*=============WriteWorld=============*/void WriteWorld(char *name) {int32_t i, j;FILE *out;patch_t *patch;winding_t *w;out = fopen(name, "w");if(!out)Error("Couldn't open %s", name);for(j = 0, patch = patches; j < num_patches; j++, patch++) {w = patch->winding;fprintf(out, "%i\n", w->numpoints);for(i = 0; i < w->numpoints; i++) {fprintf(out, "%5.2f %5.2f %5.2f %5.3f %5.3f %5.3f\n", w->p[i][0], w->p[i][1], w->p[i][2], patch->totallight.f[0],patch->totallight.f[4], patch->totallight.f[8]);}fprintf(out, "\n");}fclose(out);}//==============================================================/*=============CollectLight=============*/float CollectLight(void) {int32_t i, j;patch_t *patch;vec_t total;total = 0;for(i = 0, patch = patches; i < num_patches; i++, patch++) {// skys never collect light, it is just droppedif(patch->sky) {radiosity[i] = SH1_Clear();continue;}vec3_t normal;VectorCopy(patch->plane->normal, normal);struct SH1 illum = SH1_Reflect(illumination[i], normal);patch->totallight = SH1_Add(patch->totallight, SH1_Scale(illum, 1.0f / patch->area));radiosity[i] = SH1_ColorScale(illum, patch->reflectivity);total += radiosity[i].f[0] + radiosity[i].f[4] + radiosity[i].f[8];}memset(illumination, 0, sizeof(illumination[0]) * num_patches);return total;}/*=============ShootLightSend light out to other patchesRun multi-threaded=============*/int32_t c_progress;int32_t p_progress;void ShootLight(int32_t patchnum) {int32_t k, l;transfer_t *trans;int32_t num;patch_t *patch;// vec3_t send;// this is the amount of light we are distributing// prescale it so that multiplying by the 16 bit// transfer values gives a proper output valuestruct SH1 send = SH1_Scale(radiosity[patchnum], 1.0f / 0x10000);patch = &patches[patchnum];if(memory) {c_progress = 10 * patchnum / num_patches;if(c_progress != p_progress) {printf("%i...", c_progress);p_progress = c_progress;}MakeTransfers(patchnum);}trans = patch->transfers;num = patch->numtransfers;for(k = 0; k < num; k++, trans++) {vec3_t direction, color;VectorSubtract(patches[trans->patch].origin, patch->origin, direction);VectorNormalize(direction, direction);SH1_Sample(send, direction, color);illumination[trans->patch] =SH1_Add(illumination[trans->patch], SH1_Scale(SH1_FromDirectionalLight(direction, color), trans->transfer));// for (l = 0; l < 3; l++)// illumination[trans->patch][l] += send[l] * trans->transfer;}if(memory) {free(patches[patchnum].transfers);patches[patchnum].transfers = NULL;}}/*=============BounceLight=============*/void BounceLight(void) {int32_t i, j, start = 0, stop;float added;char name[64];patch_t *p;for(i = 0; i < num_patches; i++) {p = &patches[i];// for (j = 0; j < 3; j++) {// p->totallight[j] = p->samplelight[j];// radiosity[i][j] = p->samplelight[j] * p->reflectivity[j] * p->area;// }radiosity[i] = SH1_Scale(SH1_ColorScale(p->samplelight, p->reflectivity), p->area);}if(memory)trace_buf_size = (num_patches + 7) / 8;for(i = 0; i < numbounce; i++) {if(memory) {p_progress = -1;start = I_FloatTime();printf("[%d remaining] ", numbounce - i);total_mem = 0;}RunThreadsOnIndividual(num_patches, false, ShootLight);first_transfer = 0;if(memory) {stop = I_FloatTime();printf(" (%i)\n", stop - start);}added = CollectLight();qprintf("bounce:%i added:%f\n", i, added);if(dumppatches && (i == 0 || i == numbounce - 1)) {sprintf(name, "bounce%i.txt", i);WriteWorld(name);}}}//==============================================================void CheckPatches(void) {int32_t i;patch_t *patch;for(i = 0; i < num_patches; i++) {patch = &patches[i];if(patch->totallight.f[0] < 0 || patch->totallight.f[4] < 0 || patch->totallight.f[8] < 0)Error("negative patch totallight\n");}}/*=============RadWorld=============*/void RadWorld(void) {if(numnodes == 0 || numfaces == 0)Error("Empty map");MakeBackplanes();MakeParents(0, -1);MakeTnodes(&dmodels[0]);// turn each face into a single patchMakePatches();// subdivide patches to a maximum dimensionSubdividePatches();BuildFaceExtents(); // qb: from quetoo// create directlights out of patches and lightsCreateDirectLights();PairEdges(); // qb: moved here for phong// build initial facelightsRunThreadsOnIndividual(numfaces, true, BuildFacelights);if(numbounce > 0) {// build transfer listsif(!memory) {RunThreadsOnIndividual(num_patches, true, MakeTransfers);qprintf("transfer lists: %5.1f megs\n", (float)total_transfer * sizeof(transfer_t) / (1024 * 1024));}numthreads = 1;// spread light aroundBounceLight();FreeTransfers();CheckPatches();} elsenumthreads = 1;if(memory) {printf("Non-memory conservation would require %4.1f\n", (float)(total_mem - trace_bytes) / 1048576.0f);printf(" megabytes more memory then currently used\n");}// blend bounced light into direct light and saveLinkPlaneFaces();lightdatasize = 0;RunThreadsOnIndividual(numfaces, true, FinalLightFace);}/*========mainlight modelfile========*/void RAD_ProcessArgument(const char *arg) {double start, end;char *name;char *tgamedir = "";char *tbasedir = "";char *tmoddir = "";patches = malloc(sizeof(*patches) * MAX_PATCHES_QBSP);start = I_FloatTime();strcpy(source, ExpandArg(arg));StripExtension(source);DefaultExtension(source, ".bsp");// ReadLightFile ();name = (char *)malloc(strlen(inbase) + strlen(source) + 1);sprintf(name, "%s%s", inbase, source);printf("reading %s\n", name);LoadBSPFile(name);dlightdata_ptr = dlightdata;if(use_qbsp) {maxdata = MAX_MAP_LIGHTING_QBSP;step = QBSP_LMSTEP;}ParseEntities();CalcTextureReflectivity();if(!visdatasize) {printf("No vis information, direct lighting only.\n");numbounce = 0;ambient = 0.1;}RadWorld();if(smoothing_threshold > 0.0) {printf("Smoothing edges found: %i\n", num_smoothing);}sprintf(name, "%s%s", outbase, source);printf("writing %s\n", name);WriteBSPFile(name);end = I_FloatTime();printf("%5.0f seconds elapsed\n", end - start);printf("%i bytes light data used of %i max.\n", lightdatasize, maxdata);PrintBSPFileSizes();printf("<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< END 4rad >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n\n");free(patches);patches = NULL;}
/*===========================================================================Copyright (C) 1997-2006 Id Software, Inc.This program is free software; you can redistribute it and/or modifyit under the terms of the GNU General Public License as published bythe 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 ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See theGNU General Public License for more details.You should have received a copy of the GNU General Public License alongwith this program; if not, write to the Free Software Foundation, Inc.,51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.===========================================================================*/#include "qbsp.h"int32_t c_nofaces;int32_t c_facenodes;/*=========================================================ONLY SAVE OUT PLANES THAT ARE ACTUALLY USED AS NODES=========================================================*/int32_t planeused[MAX_MAP_PLANES_QBSP];/*============EmitPlanesThere is no oportunity to discard planes, because all of the originalbrushes will be saved in the map.============*/void EmitPlanes(void) {int32_t i;dplane_t *dp;plane_t *mp;mp = mapplanes;for (i = 0; i < nummapplanes; i++, mp++) {dp = &dplanes[numplanes];VectorCopy(mp->normal, dp->normal);dp->dist = mp->dist;dp->type = mp->type;numplanes++;}}//========================================================void EmitMarkFace(dleaf_t *leaf_p, face_t *f) {int32_t i;int32_t facenum;while (f->merged)f = f->merged;if (f->split[0]) {EmitMarkFace(leaf_p, f->split[0]);EmitMarkFace(leaf_p, f->split[1]);return;}facenum = f->outputnumber;if (facenum == -1)return; // degenerate faceif (facenum < 0 || facenum >= numfaces)Error("Bad leafface");for (i = leaf_p->firstleafface; i < numleaffaces; i++)if (dleaffaces[i] == facenum)break; // merged out faceif (i == numleaffaces) {if (numleaffaces >= MAX_MAP_LEAFFACES)Error("MAX_MAP_LEAFFACES");dleaffaces[numleaffaces] = facenum;numleaffaces++;}}void EmitMarkFaceX(dleaf_tx *leaf_p, face_t *f) {int32_t i;int32_t facenum;while (f->merged)f = f->merged;if (f->split[0]) {EmitMarkFaceX(leaf_p, f->split[0]);EmitMarkFaceX(leaf_p, f->split[1]);return;}facenum = f->outputnumber;if (facenum == -1)return; // degenerate faceif (facenum < 0 || facenum >= numfaces)Error("Bad leafface");for (i = leaf_p->firstleafface; i < numleaffaces; i++)if (dleaffacesX[i] == facenum)break; // merged out faceif (i == numleaffaces) {if (numleaffaces >= MAX_MAP_LEAFFACES_QBSP)Error("MAX_MAP_LEAFFACES_QBSP");dleaffacesX[numleaffaces] = facenum;numleaffaces++;}}/*==================EmitLeaf==================*/void EmitLeaf(node_t *node) {portal_t *p;int32_t s;face_t *f;bspbrush_t *b;int32_t i;int32_t brushnum;// emit a leafif (use_qbsp) // qb: qbsp{dleaf_tx *leaf_p;if (numleafs >= MAX_MAP_LEAFS_QBSP)Error("MAX_MAP_LEAFS_QBSP");leaf_p = &dleafsX[numleafs];numleafs++;leaf_p->contents = node->contents;leaf_p->cluster = node->cluster;leaf_p->area = node->area;//// write bounding box info//VectorCopy(node->mins, leaf_p->mins);VectorCopy(node->maxs, leaf_p->maxs);//// write the leafbrushes//leaf_p->firstleafbrush = numleafbrushes;for (b = node->brushlist; b; b = b->next) {if (numleafbrushes >= MAX_MAP_LEAFBRUSHES_QBSP)Error("MAX_MAP_LEAFBRUSHES_QBSP");brushnum = b->original - mapbrushes;for (i = leaf_p->firstleafbrush; i < numleafbrushes; i++)if (dleafbrushesX[i] == brushnum)break;if (i == numleafbrushes) {dleafbrushesX[numleafbrushes] = brushnum;numleafbrushes++;}}leaf_p->numleafbrushes = numleafbrushes - leaf_p->firstleafbrush;//// write the leaffaces//if (leaf_p->contents & CONTENTS_SOLID)return; // no leaffaces in solidsleaf_p->firstleafface = numleaffaces;for (p = node->portals; p; p = p->next[s]) {s = (p->nodes[1] == node);f = p->face[s];if (!f)continue; // not a visible portalEmitMarkFaceX(leaf_p, f);}leaf_p->numleaffaces = numleaffaces - leaf_p->firstleafface;}else {dleaf_t *leaf_p;if (numleafs >= MAX_MAP_LEAFS)Error("MAX_MAP_LEAFS");leaf_p = &dleafs[numleafs];numleafs++;leaf_p->contents = node->contents;leaf_p->cluster = node->cluster;leaf_p->area = node->area;//// write bounding box info//VectorCopy(node->mins, leaf_p->mins);VectorCopy(node->maxs, leaf_p->maxs);//// write the leafbrushes//leaf_p->firstleafbrush = numleafbrushes;for (b = node->brushlist; b; b = b->next) {if (numleafbrushes >= MAX_MAP_LEAFBRUSHES)Error("MAX_MAP_LEAFBRUSHES");brushnum = b->original - mapbrushes;for (i = leaf_p->firstleafbrush; i < numleafbrushes; i++)if (dleafbrushes[i] == brushnum)break;if (i == numleafbrushes) {dleafbrushes[numleafbrushes] = brushnum;numleafbrushes++;}}leaf_p->numleafbrushes = numleafbrushes - leaf_p->firstleafbrush;//// write the leaffaces//if (leaf_p->contents & CONTENTS_SOLID)return; // no leaffaces in solidsleaf_p->firstleafface = numleaffaces;for (p = node->portals; p; p = p->next[s]) {s = (p->nodes[1] == node);f = p->face[s];if (!f)continue; // not a visible portalEmitMarkFace(leaf_p, f);}leaf_p->numleaffaces = numleaffaces - leaf_p->firstleafface;}}/*==================EmitFace==================*/void EmitFace(face_t *f) {int32_t i;int32_t e;f->outputnumber = -1;if (f->numpoints < 3) {return; // degenerated}if (f->merged || f->split[0] || f->split[1]) {return; // not a final face}// save output number so leaffaces can usef->outputnumber = numfaces;if (use_qbsp) {dface_tx *df;if (numfaces >= MAX_MAP_FACES_QBSP)Error("numfaces == MAX_MAP_FACES_QBSP");df = &dfacesX[numfaces];numfaces++;// planenum is used by qlight, but not the enginedf->planenum = f->planenum & (~1);df->side = f->planenum & 1;df->firstedge = numsurfedges;df->numedges = f->numpoints;df->texinfo = f->texinfo;for (i = 0; i < f->numpoints; i++) {e = GetEdge(f->vertexnums[i], f->vertexnums[(i + 1) % f->numpoints], f);if (numsurfedges >= MAX_MAP_SURFEDGES_QBSP)Error("numsurfedges == MAX_MAP_SURFEDGES_QBSP");dsurfedges[numsurfedges] = e;numsurfedges++;}} else {dface_t *df;if (numfaces >= MAX_MAP_FACES)Error("numfaces == MAX_MAP_FACES");df = &dfaces[numfaces];numfaces++;// planenum is used by qlight, but not the enginedf->planenum = f->planenum & (~1);df->side = f->planenum & 1;df->firstedge = numsurfedges;df->numedges = f->numpoints;df->texinfo = f->texinfo;for (i = 0; i < f->numpoints; i++) {e = GetEdge(f->vertexnums[i], f->vertexnums[(i + 1) % f->numpoints], f);if (numsurfedges >= MAX_MAP_SURFEDGES)Error("numsurfedges == MAX_MAP_SURFEDGES");dsurfedges[numsurfedges] = e;numsurfedges++;}}}/*============EmitDrawingNode_r============*/int32_t EmitDrawNode_r(node_t *node) {face_t *f;int32_t i;if (node->planenum == PLANENUM_LEAF) {EmitLeaf(node);return -numleafs;}// emit a nodeif (use_qbsp) {dnode_tx *n;if (numnodes == MAX_MAP_NODES_QBSP)Error("MAX_MAP_NODES_QBSP");n = &dnodesX[numnodes];numnodes++;VectorCopy(node->mins, n->mins);VectorCopy(node->maxs, n->maxs);planeused[node->planenum]++;planeused[node->planenum ^ 1]++;if (node->planenum & 1)Error("WriteDrawNodes_r: odd planenum");n->planenum = node->planenum;n->firstface = numfaces;if (!node->faces)c_nofaces++;elsec_facenodes++;for (f = node->faces; f; f = f->next)EmitFace(f);n->numfaces = numfaces - n->firstface;// recursively output the other nodesfor (i = 0; i < 2; i++) {if (node->children[i]->planenum == PLANENUM_LEAF) {n->children[i] = -(numleafs + 1);EmitLeaf(node->children[i]);} else {n->children[i] = numnodes;EmitDrawNode_r(node->children[i]);}}return n - dnodesX;}else // ibsp{dnode_t *n;if (numnodes == MAX_MAP_NODES)Error("MAX_MAP_NODES");n = &dnodes[numnodes];numnodes++;VectorCopy(node->mins, n->mins);VectorCopy(node->maxs, n->maxs);planeused[node->planenum]++;planeused[node->planenum ^ 1]++;if (node->planenum & 1)Error("WriteDrawNodes_r: odd planenum");n->planenum = node->planenum;n->firstface = numfaces;if (!node->faces)c_nofaces++;elsec_facenodes++;for (f = node->faces; f; f = f->next)EmitFace(f);n->numfaces = numfaces - n->firstface;// recursively output the other nodesfor (i = 0; i < 2; i++) {if (node->children[i]->planenum == PLANENUM_LEAF) {n->children[i] = -(numleafs + 1);EmitLeaf(node->children[i]);} else {n->children[i] = numnodes;EmitDrawNode_r(node->children[i]);}}return n - dnodes;}}//=========================================================/*============WriteBSP============*/void WriteBSP(node_t *headnode) {int32_t oldfaces;c_nofaces = 0;c_facenodes = 0;qprintf("--- WriteBSP ---\n");oldfaces = numfaces;dmodels[nummodels].headnode = EmitDrawNode_r(headnode);EmitAreaPortals(headnode);qprintf("%5i nodes with faces\n", c_facenodes);qprintf("%5i nodes without faces\n", c_nofaces);qprintf("%5i faces\n", numfaces - oldfaces);}//===========================================================/*============SetModelNumbers============*/void SetModelNumbers(void) {int32_t i;int32_t models;char value[12];models = 1;for (i = 1; i < num_entities; i++) {if (entities[i].numbrushes) {sprintf(value, "*%i", models);models++;SetKeyValue(&entities[i], "model", value);}}}/*============SetLightStyles============*/#define MAX_SWITCHED_LIGHTS 32void SetLightStyles(void) {int32_t stylenum;char *t;entity_t *e;int32_t i, j;char value[11];char lighttargets[MAX_SWITCHED_LIGHTS][64];// any light that is controlled (has a targetname)// must have a unique style number generated for itstylenum = 0;for (i = 1; i < num_entities; i++) {e = &entities[i];t = ValueForKey(e, "classname");if (Q_strncasecmp(t, "light", 5))continue;t = ValueForKey(e, "targetname");if (!t[0])continue;// find this targetnamefor (j = 0; j < stylenum; j++)if (!strcmp(lighttargets[j], t))break;if (j == stylenum) {if (stylenum == MAX_SWITCHED_LIGHTS)Error("stylenum == MAX_SWITCHED_LIGHTS");strcpy(lighttargets[j], t);stylenum++;}sprintf(value, "%i", 32 + j);SetKeyValue(e, "style", value);}}//===========================================================/*============EmitBrushes============*/void EmitBrushes(void) {int32_t i, j, bnum, s, x;dbrush_t *db;mapbrush_t *b;vec3_t normal;vec_t dist;int32_t planenum;numbrushsides = 0;numbrushes = nummapbrushes;for (bnum = 0; bnum < nummapbrushes; bnum++) {b = &mapbrushes[bnum];db = &dbrushes[bnum];db->contents = b->contents;db->firstside = numbrushsides;db->numsides = b->numsides;if (use_qbsp) {dbrushside_tx *cp;for (j = 0; j < b->numsides; j++) {if (numbrushsides == MAX_MAP_BRUSHSIDES_QBSP)Error("MAX_MAP_BRUSHSIDES_QBSP");cp = &dbrushsidesX[numbrushsides];numbrushsides++;cp->planenum = b->original_sides[j].planenum;cp->texinfo = b->original_sides[j].texinfo;}} else {for (j = 0; j < b->numsides; j++) {dbrushside_t *cp;if (numbrushsides == MAX_MAP_BRUSHSIDES)Error("MAX_MAP_BRUSHSIDES");cp = &dbrushsides[numbrushsides];numbrushsides++;cp->planenum = b->original_sides[j].planenum;cp->texinfo = b->original_sides[j].texinfo;}}// add any axis planes not contained in the brush to bevel off cornersfor (x = 0; x < 3; x++)for (s = -1; s <= 1; s += 2) {// add the planeVectorCopy(vec3_origin, normal);normal[x] = s;if (s == -1)dist = -b->mins[x];elsedist = b->maxs[x];planenum = FindFloatPlane(normal, dist, b->brushnum);for (i = 0; i < b->numsides; i++)if (b->original_sides[i].planenum == planenum)break;if (i == b->numsides) {if (use_qbsp) {if (numbrushsides >= MAX_MAP_BRUSHSIDES_QBSP)Error("MAX_MAP_BRUSHSIDES_QBSP");dbrushsidesX[numbrushsides].planenum = planenum;dbrushsidesX[numbrushsides].texinfo = dbrushsidesX[numbrushsides - 1].texinfo;} else {if (numbrushsides >= MAX_MAP_BRUSHSIDES)Error("MAX_MAP_BRUSHSIDES");dbrushsides[numbrushsides].planenum = planenum;dbrushsides[numbrushsides].texinfo = dbrushsides[numbrushsides - 1].texinfo;}numbrushsides++;db->numsides++;}}}}//===========================================================/*==================BeginBSPFile==================*/void BeginBSPFile(void) {// these values may actually be initialized// if the file existed when loaded, so clear them explicitlynummodels = 0;numfaces = 0;numnodes = 0;numbrushsides = 0;numvertexes = 0;numleaffaces = 0;numleafbrushes = 0;numsurfedges = 0;// edge 0 is not used, because 0 can't be negatednumedges = 1;// leave vertex 0 as an errornumvertexes = 1;// leave leaf 0 as an errornumleafs = 1;dleafs[0].contents = CONTENTS_SOLID;dleafsX[0].contents = CONTENTS_SOLID;}/*============EndBSPFile============*/void EndBSPFile(void) {char path[1030];EmitBrushes();EmitPlanes();UnparseEntities();// load the pop#if 0sprintf (path, "%s/pics/pop.lmp", gamedir);len = LoadFile (path, &buf);memcpy (dpop, buf, sizeof(dpop));free (buf);#endif// write the mapsprintf(path, "%s.bsp", source);WriteBSPFile(path);}/*==================BeginModel==================*/int32_t firstmodleaf;extern int32_t firstmodeledge;extern int32_t firstmodelface;void BeginModel(void) {dmodel_t *mod;int32_t start, end;mapbrush_t *b;int32_t j;entity_t *e;vec3_t mins, maxs;if (use_qbsp) {if (nummodels == WARN_MAP_MODELS_QBSP)printf("WARNING: nummodels may exceed protocol limit (%i)\n", WARN_MAP_MODELS_QBSP);if (nummodels == MAX_MAP_MODELS_QBSP)Error("nummodels exceeds MAX_MAP_MODELS_QBSP");} else if (nummodels == MAX_MAP_MODELS)Error("nummodels exceeds MAX_MAP_MODELS");mod = &dmodels[nummodels];mod->firstface = numfaces;firstmodleaf = numleafs;firstmodeledge = numedges;firstmodelface = numfaces;//// bound the brushes//e = &entities[entity_num];start = e->firstbrush;end = start + e->numbrushes;ClearBounds(mins, maxs);for (j = start; j < end; j++) {b = &mapbrushes[j];if (!b->numsides)continue; // not a real brush (origin brush)AddPointToBounds(b->mins, mins, maxs);AddPointToBounds(b->maxs, mins, maxs);}VectorCopy(mins, mod->mins);VectorCopy(maxs, mod->maxs);}/*==================EndModel==================*/void EndModel(void) {dmodel_t *mod;mod = &dmodels[nummodels];mod->numfaces = numfaces - mod->firstface;nummodels++;}
/*===========================================================================Copyright (C) 1997-2006 Id Software, Inc.This program is free software; you can redistribute it and/or modifyit under the terms of the GNU General Public License as published bythe 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 ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See theGNU General Public License for more details.You should have received a copy of the GNU General Public License alongwith this program; if not, write to the Free Software Foundation, Inc.,51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.===========================================================================*/#include "qbsp.h"extern int32_t c_nodes;void RemovePortalFromNode(portal_t *portal, node_t *l);node_t *NodeForPoint(node_t *node, vec3_t origin) {plane_t *plane;vec_t d;while (node->planenum != PLANENUM_LEAF) {plane = &mapplanes[node->planenum];d = DotProduct(origin, plane->normal) - plane->dist;if (d >= 0)node = node->children[0];elsenode = node->children[1];}return node;}/*=============FreeTreePortals_r=============*/void FreeTreePortals_r(node_t *node) {portal_t *p, *nextp;int32_t s;// free childrenif (node->planenum != PLANENUM_LEAF) {FreeTreePortals_r(node->children[0]);FreeTreePortals_r(node->children[1]);}// free portalsfor (p = node->portals; p; p = nextp) {s = (p->nodes[1] == node);nextp = p->next[s];RemovePortalFromNode(p, p->nodes[!s]);FreePortal(p);}node->portals = NULL;}/*=============FreeTree_r=============*/void FreeTree_r(node_t *node) {face_t *f, *nextf;// free childrenif (node->planenum != PLANENUM_LEAF) {FreeTree_r(node->children[0]);FreeTree_r(node->children[1]);}// free bspbrushesFreeBrushList(node->brushlist);// free facesfor (f = node->faces; f; f = nextf) {nextf = f->next;FreeFace(f);}// free the nodeif (node->volume)FreeBrush(node->volume);if (numthreads == 1)c_nodes--;free(node);}/*=============FreeTree=============*/void FreeTree(tree_t *tree) {FreeTreePortals_r(tree->headnode);FreeTree_r(tree->headnode);free(tree);}//===============================================================void PrintTree_r(node_t *node, int32_t depth) {int32_t i;plane_t *plane;bspbrush_t *bb;for (i = 0; i < depth; i++)printf(" ");if (node->planenum == PLANENUM_LEAF) {if (!node->brushlist)printf("NULL\n");else {for (bb = node->brushlist; bb; bb = bb->next)printf("%i ", bb->original->brushnum);printf("\n");}return;}plane = &mapplanes[node->planenum];printf("#%i (%5.2f %5.2f %5.2f):%5.2f\n", node->planenum,plane->normal[0], plane->normal[1], plane->normal[2],plane->dist);PrintTree_r(node->children[0], depth + 1);PrintTree_r(node->children[1], depth + 1);}/*=========================================================NODES THAT DON'T SEPERATE DIFFERENT CONTENTS CAN BE PRUNED=========================================================*/int32_t c_pruned;/*============PruneNodes_r============*/void PruneNodes_r(node_t *node) {bspbrush_t *b, *next;if (node->planenum == PLANENUM_LEAF)return;PruneNodes_r(node->children[0]);PruneNodes_r(node->children[1]);if ((node->children[0]->contents & CONTENTS_SOLID) && (node->children[1]->contents & CONTENTS_SOLID)) {if (node->faces)Error("node->faces seperating CONTENTS_SOLID");if (node->children[0]->faces || node->children[1]->faces)Error("!node->faces with children");// FIXME: free stuffnode->planenum = PLANENUM_LEAF;node->contents = CONTENTS_SOLID;node->detail_seperator = false;if (node->brushlist)Error("PruneNodes: node->brushlist");// combine brush listsnode->brushlist = node->children[1]->brushlist;for (b = node->children[0]->brushlist; b; b = next) {next = b->next;b->next = node->brushlist;node->brushlist = b;}c_pruned++;}}void PruneNodes(node_t *node) {qprintf("--- PruneNodes ---\n");c_pruned = 0;PruneNodes_r(node);qprintf("%5i pruned nodes\n", c_pruned);}//===========================================================
/*===========================================================================Copyright (C) 1997-2006 Id Software, Inc.This program is free software; you can redistribute it and/or modifyit under the terms of the GNU General Public License as published bythe 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 ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See theGNU General Public License for more details.You should have received a copy of the GNU General Public License alongwith this program; if not, write to the Free Software Foundation, Inc.,51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.===========================================================================*/#include "qbsp.h"int32_t nummiptex;textureref_t textureref[MAX_MAP_TEXTURES];//==========================================================================int32_t FindMiptex(char *name) {int32_t i, mod_fail;char path[1080];char pakpath[56];miptex_t *mt;for (i = 0; i < nummiptex; i++)if (!strcmp(name, textureref[i].name)) {return i;}if (nummiptex == MAX_MAP_TEXTURES)Error("MAX_MAP_TEXTURES");strcpy(textureref[i].name, name);mod_fail = true;sprintf(pakpath, "textures/%s.wal", name);if (moddir[0] != 0) {sprintf(path, "%s%s", moddir, pakpath);// load the miptex to get the flags and valuesif (TryLoadFile(path, (void **)&mt, false) != -1 ||TryLoadFileFromPak(pakpath, (void **)&mt, moddir) != -1) {textureref[i].value = LittleLong(mt->value);textureref[i].flags = LittleLong(mt->flags);textureref[i].contents = LittleLong(mt->contents);strcpy(textureref[i].animname, mt->animname);free(mt);mod_fail = false;}}if (mod_fail) {// load the miptex to get the flags and valuessprintf(path, "%s%s", basedir, pakpath);if (TryLoadFile(path, (void **)&mt, false) != -1 ||TryLoadFileFromPak(pakpath, (void **)&mt, basedir) != -1) {textureref[i].value = LittleLong(mt->value);textureref[i].flags = LittleLong(mt->flags);textureref[i].contents = LittleLong(mt->contents);strcpy(textureref[i].animname, mt->animname);free(mt);mod_fail = false;}}if (mod_fail)printf("WARNING: couldn't locate texture %s\n", name);nummiptex++;if (textureref[i].animname[0])FindMiptex(textureref[i].animname);return i;}/*==================textureAxisFromPlane==================*/vec3_t baseaxis[18] ={{0, 0, 1}, {1, 0, 0}, {0, -1, 0}, // floor{0, 0, -1},{1, 0, 0},{0, -1, 0}, // ceiling{1, 0, 0},{0, 1, 0},{0, 0, -1}, // west wall{-1, 0, 0},{0, 1, 0},{0, 0, -1}, // east wall{0, 1, 0},{1, 0, 0},{0, 0, -1}, // south wall{0, -1, 0},{1, 0, 0},{0, 0, -1} // north wall};void TextureAxisFromPlane(plane_t *pln, vec3_t xv, vec3_t yv) {int32_t bestaxis;vec_t dot, best;int32_t i;best = 0;bestaxis = 0;for (i = 0; i < 6; i++) {dot = DotProduct(pln->normal, baseaxis[i * 3]);if (dot > best) {best = dot;bestaxis = i;}}VectorCopy(baseaxis[bestaxis * 3 + 1], xv);VectorCopy(baseaxis[bestaxis * 3 + 2], yv);}static inline void CheckTexinfoCount() // mxd{if (use_qbsp) {if (numtexinfo == MAX_MAP_TEXINFO_QBSP)printf("WARNING: texinfo count exceeds qbsp limit (%i).\n", MAX_MAP_TEXINFO_QBSP);} else if (numtexinfo == DEFAULT_MAP_TEXINFO)printf("WARNING: texinfo count exceeds vanilla limit (%i).\n", DEFAULT_MAP_TEXINFO);else if (numtexinfo >= MAX_MAP_TEXINFO)Error("ERROR: texinfo count exceeds extended limit (%i).\n", MAX_MAP_TEXINFO);}qboolean TexinfosMatch(texinfo_t t1, texinfo_t t2) // mxd{if (t1.flags != t2.flags || t1.value != t2.value || strcmp(t1.texture, t2.texture))return false;for (int32_t j = 0; j < 2; j++)for (int32_t k = 0; k < 4; k++)if ((int32_t)(t1.vecs[j][k] * 100) != (int32_t)(t2.vecs[j][k] * 100)) // qb: round to two decimal placesreturn false;return true;}// mxd. Applies origin brush offset to existing v220 texinfo...int32_t ApplyTexinfoOffset_UV(int32_t texinfoindex, const brush_texture_t *bt, const vec3_t origin) {if ((!origin[0] && !origin[1] && !origin[2]) || texinfoindex < 0)return texinfoindex;const texinfo_t otx = texinfo[texinfoindex];// Copy texinfotexinfo_t tx;memcpy(&tx, &otx, sizeof(tx));// Transform origin to UV space and add it to ST offsetstx.vecs[0][3] += origin[0] * tx.vecs[0][0] + origin[1] * tx.vecs[0][1] + origin[2] * tx.vecs[0][2];tx.vecs[1][3] += origin[0] * tx.vecs[1][0] + origin[1] * tx.vecs[1][1] + origin[2] * tx.vecs[1][2];// Find or replace texinfotexinfo_t *tc = texinfo;for (texinfoindex = 0; texinfoindex < numtexinfo; texinfoindex++, tc++)if (TexinfosMatch(*tc, tx))return texinfoindex;*tc = tx;numtexinfo++;CheckTexinfoCount();// Repeat for the next animation frameconst int32_t mt = FindMiptex(tx.texture);if (textureref[mt].animname[0]) {brush_texture_t anim = *bt;strcpy(anim.name, textureref[mt].animname);tc->nexttexinfo = ApplyTexinfoOffset_UV(tc->nexttexinfo, &anim, origin);} else {tc->nexttexinfo = -1;}// Return new texinfo index.return texinfoindex;}// DarkEssence: function for new #mapversion with UVaxisint32_t TexinfoForBrushTexture_UV(brush_texture_t *bt, vec_t *UVaxis) {if (!bt->name[0])return 0;texinfo_t tx;memset(&tx, 0, sizeof(tx));strcpy(tx.texture, bt->name);if (!bt->scale[0])bt->scale[0] = 1;if (!bt->scale[1])bt->scale[1] = 1;for (int32_t i = 0; i < 2; i++)for (int32_t j = 0; j < 3; j++)tx.vecs[i][j] = UVaxis[i * 3 + j] / bt->scale[i];tx.vecs[0][3] = bt->shift[0];tx.vecs[1][3] = bt->shift[1];tx.flags = bt->flags;tx.value = bt->value;// Find or replace texinfoint32_t texinfoindex;texinfo_t *tc = texinfo;for (texinfoindex = 0; texinfoindex < numtexinfo; texinfoindex++, tc++)if (TexinfosMatch(*tc, tx))return texinfoindex;*tc = tx;numtexinfo++;CheckTexinfoCount(); // mxd// Load the next animationconst int32_t mt = FindMiptex(bt->name);if (textureref[mt].animname[0]) {brush_texture_t anim = *bt;strcpy(anim.name, textureref[mt].animname);tc->nexttexinfo = TexinfoForBrushTexture_UV(&anim, UVaxis);} elsetc->nexttexinfo = -1;return texinfoindex;}extern qboolean origfix;int32_t TexinfoForBrushTexture(plane_t *plane, brush_texture_t *bt, vec3_t origin) {if (!bt->name[0])return 0;texinfo_t tx;memset(&tx, 0, sizeof(tx));strcpy(tx.texture, bt->name);vec3_t vecs[2];TextureAxisFromPlane(plane, vecs[0], vecs[1]);/* Originally:shift[0] = DotProduct (origin, vecs[0]);shift[1] = DotProduct (origin, vecs[1]);*/if (!bt->scale[0])bt->scale[0] = 1;if (!bt->scale[1])bt->scale[1] = 1;// DWH: Fix for scaled textures using an origin brushfloat shift[2];vec3_t scaled_origin;if (origfix) {VectorScale(origin, 1.0 / bt->scale[0], scaled_origin);shift[0] = DotProduct(scaled_origin, vecs[0]);VectorScale(origin, 1.0 / bt->scale[1], scaled_origin);shift[1] = DotProduct(scaled_origin, vecs[1]);} else {shift[0] = DotProduct(origin, vecs[0]);shift[1] = DotProduct(origin, vecs[1]);}// Rotate axisvec_t sinv, cosv;if (bt->rotate == 0) {sinv = 0;cosv = 1;} else if (bt->rotate == 90) {sinv = 1;cosv = 0;} else if (bt->rotate == 180) {sinv = 0;cosv = -1;} else if (bt->rotate == 270) {sinv = -1;cosv = 0;} else {const vec_t ang = bt->rotate / 180 * Q_PI;sinv = sin(ang);cosv = cos(ang);}// DWH: and again...if (origfix) {const vec_t ns = cosv * shift[0] - sinv * shift[1];const vec_t nt = sinv * shift[0] + cosv * shift[1];shift[0] = ns;shift[1] = nt;}int32_t sv, tv;if (vecs[0][0])sv = 0;else if (vecs[0][1])sv = 1;elsesv = 2;if (vecs[1][0])tv = 0;else if (vecs[1][1])tv = 1;elsetv = 2;for (int32_t i = 0; i < 2; i++) {const vec_t ns = cosv * vecs[i][sv] - sinv * vecs[i][tv];const vec_t nt = sinv * vecs[i][sv] + cosv * vecs[i][tv];vecs[i][sv] = ns;vecs[i][tv] = nt;}for (int32_t i = 0; i < 2; i++)for (int32_t j = 0; j < 3; j++)tx.vecs[i][j] = vecs[i][j] / bt->scale[i];tx.vecs[0][3] = bt->shift[0] + shift[0];tx.vecs[1][3] = bt->shift[1] + shift[1];tx.flags = bt->flags;tx.value = bt->value;// Find or replace texinfoint32_t texinfoindex;texinfo_t *tc = texinfo;for (texinfoindex = 0; texinfoindex < numtexinfo; texinfoindex++, tc++)if (TexinfosMatch(*tc, tx))return texinfoindex;*tc = tx;numtexinfo++;CheckTexinfoCount(); // mxd// load the next animationconst int32_t mt = FindMiptex(bt->name);if (textureref[mt].animname[0]) {brush_texture_t anim = *bt;strcpy(anim.name, textureref[mt].animname);tc->nexttexinfo = TexinfoForBrushTexture(plane, &anim, origin);} elsetc->nexttexinfo = -1;return texinfoindex;}
/*===========================================================================Copyright (C) 1997-2006 Id Software, Inc.This program is free software; you can redistribute it and/or modifyit under the terms of the GNU General Public License as published bythe 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 ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See theGNU General Public License for more details.You should have received a copy of the GNU General Public License alongwith this program; if not, write to the Free Software Foundation, Inc.,51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.===========================================================================*/#include "qbsp.h"/*==============================================================================PORTAL FILE GENERATIONSave out name.prt for qvis to read==============================================================================*/#define PORTALFILE "PRT1"extern qboolean use_qbsp;FILE *pf;int32_t num_visclusters; // clusters the player can be inint32_t num_visportals;void WriteFloat(FILE *f, vec_t v) {if (fabs(v - Q_rint(v)) < 0.001)fprintf(f, "%i ", (int32_t)Q_rint(v));elsefprintf(f, "%f ", v);}/*=================WritePortalFile_r=================*/void WritePortalFile_r(node_t *node) {int32_t i, s;portal_t *p;winding_t *w;vec3_t normal;vec_t dist;// decision nodeif (node->planenum != PLANENUM_LEAF && !node->detail_seperator) {WritePortalFile_r(node->children[0]);WritePortalFile_r(node->children[1]);return;}if (node->contents & CONTENTS_SOLID)return;for (p = node->portals; p; p = p->next[s]) {w = p->winding;s = (p->nodes[1] == node);if (w && p->nodes[0] == node) {if (!Portal_VisFlood(p))continue;// write out to the file// sometimes planes get turned around when they are very near// the changeover point between different axis. interpret the// plane the same way vis will, and flip the side orders if needed// FIXME: is this still relevent?WindingPlane(w, normal, &dist);if (DotProduct(p->plane.normal, normal) < 0.99) {// backwards...fprintf(pf, "%i %i %i ", w->numpoints, p->nodes[1]->cluster, p->nodes[0]->cluster);} elsefprintf(pf, "%i %i %i ", w->numpoints, p->nodes[0]->cluster, p->nodes[1]->cluster);for (i = 0; i < w->numpoints; i++) {fprintf(pf, "(");WriteFloat(pf, w->p[i][0]);WriteFloat(pf, w->p[i][1]);WriteFloat(pf, w->p[i][2]);fprintf(pf, ") ");}fprintf(pf, "\n");}}}/*================FillLeafNumbers_rAll of the leafs under node will have the same cluster================*/void FillLeafNumbers_r(node_t *node, int32_t num) {if (node->planenum == PLANENUM_LEAF) {if (node->contents & CONTENTS_SOLID)node->cluster = -1;elsenode->cluster = num;return;}node->cluster = num;FillLeafNumbers_r(node->children[0], num);FillLeafNumbers_r(node->children[1], num);}/*================NumberLeafs_r================*/void NumberLeafs_r(node_t *node) {portal_t *p;if (node->planenum != PLANENUM_LEAF && !node->detail_seperator) {// decision nodenode->cluster = -99;NumberLeafs_r(node->children[0]);NumberLeafs_r(node->children[1]);return;}// either a leaf or a detail clusterif (node->contents & CONTENTS_SOLID) {// solid block, viewpoint never insidenode->cluster = -1;return;}FillLeafNumbers_r(node, num_visclusters);num_visclusters++;// count the portalsfor (p = node->portals; p;) {if (p->nodes[0] == node) // only write out from first leaf{if (Portal_VisFlood(p))num_visportals++;p = p->next[0];} elsep = p->next[1];}}/*================CreateVisPortals_r================*/void CreateVisPortals_r(node_t *node) {// stop as soon as we get to a detail_seperator, which// means that everything below is in a single clusterif (node->planenum == PLANENUM_LEAF || node->detail_seperator)return;MakeNodePortal(node);SplitNodePortals(node);CreateVisPortals_r(node->children[0]);CreateVisPortals_r(node->children[1]);}/*================FinishVisPortals_r================*/void FinishVisPortals2_r(node_t *node) {if (node->planenum == PLANENUM_LEAF)return;MakeNodePortal(node);SplitNodePortals(node);FinishVisPortals2_r(node->children[0]);FinishVisPortals2_r(node->children[1]);}void FinishVisPortals_r(node_t *node) {if (node->planenum == PLANENUM_LEAF)return;if (node->detail_seperator) {FinishVisPortals2_r(node);return;}FinishVisPortals_r(node->children[0]);FinishVisPortals_r(node->children[1]);}int32_t clusterleaf;void SaveClusters_r(node_t *node) {if (node->planenum == PLANENUM_LEAF) {if (use_qbsp)dleafsX[clusterleaf++].cluster = node->cluster;elsedleafs[clusterleaf++].cluster = node->cluster;return;}SaveClusters_r(node->children[0]);SaveClusters_r(node->children[1]);}/*================WritePortalFile================*/void WritePortalFile(tree_t *tree) {char filename[1030];node_t *headnode;qprintf("--- WritePortalFile ---\n");headnode = tree->headnode;num_visclusters = 0;num_visportals = 0;FreeTreePortals_r(headnode);MakeHeadnodePortals(tree);CreateVisPortals_r(headnode);// set the cluster field in every leaf and count the total number of portalsNumberLeafs_r(headnode);// write the filesprintf(filename, "%s.prt", source);if (use_qbsp)printf("\ntexinfo count: %i of %i maximum\n", numtexinfo, MAX_MAP_TEXINFO_QBSP);elseprintf("\ntexinfo count: %i of %i maximum\n", numtexinfo, MAX_MAP_TEXINFO);if (use_qbsp)printf("brushsides count: %i of %i maximum\n", nummapbrushsides, MAX_MAP_BRUSHSIDES_QBSP);elseprintf("brushsides count: %i of %i maximum\n", nummapbrushsides, MAX_MAP_BRUSHSIDES);printf("writing %s\n", filename);pf = fopen(filename, "w");if (!pf)Error("Error opening %s", filename);fprintf(pf, "%s\n", PORTALFILE);fprintf(pf, "%i\n", num_visclusters);fprintf(pf, "%i\n", num_visportals);qprintf("%5i visclusters\n", num_visclusters);qprintf("%5i visportals\n", num_visportals);WritePortalFile_r(headnode);fclose(pf);// we need to store the clusters out now because ordering// issues made us do this after writebsp...clusterleaf = 1;SaveClusters_r(headnode);}
/*===========================================================================Copyright (C) 1997-2006 Id Software, Inc.This program is free software; you can redistribute it and/or modifyit under the terms of the GNU General Public License as published bythe 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 ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See theGNU General Public License for more details.You should have received a copy of the GNU General Public License alongwith this program; if not, write to the Free Software Foundation, Inc.,51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.===========================================================================*/#include "qbsp.h"int32_t c_active_portals;int32_t c_peak_portals;int32_t c_boundary;int32_t c_boundary_sides;/*===========AllocPortal===========*/portal_t *AllocPortal(void) {portal_t *p;if (numthreads == 1)c_active_portals++;if (c_active_portals > c_peak_portals)c_peak_portals = c_active_portals;p = malloc(sizeof(portal_t));memset(p, 0, sizeof(portal_t));return p;}void FreePortal(portal_t *p) {if (p->winding)FreeWinding(p->winding);if (numthreads == 1)c_active_portals--;free(p);}//==============================================================/*==============VisibleContentsReturns the single content bit of thestrongest visible content present==============*/int32_t VisibleContents(int32_t contents) {int32_t i;for (i = 1; i <= LAST_VISIBLE_CONTENTS; i <<= 1)if (contents & i)return i;return 0;}/*===============ClusterContents===============*/int32_t ClusterContents(node_t *node) {int32_t c1, c2, c;if (node->planenum == PLANENUM_LEAF)return node->contents;c1 = ClusterContents(node->children[0]);c2 = ClusterContents(node->children[1]);c = c1 | c2;// a cluster may include some solid detail areas, but// still be seen intoif (!(c1 & CONTENTS_SOLID) || !(c2 & CONTENTS_SOLID))c &= ~CONTENTS_SOLID;return c;}/*=============Portal_VisFloodReturns true if the portal is empty or translucent, allowingthe PVS calculation to see through it.The nodes on either side of the portal may actually be clusters,not leafs, so all contents should be ored together=============*/qboolean Portal_VisFlood(portal_t *p) {int32_t c1, c2;if (!p->onnode)return false; // to global outsideleafc1 = ClusterContents(p->nodes[0]);c2 = ClusterContents(p->nodes[1]);if (!VisibleContents(c1 ^ c2))return true;if (c1 & (CONTENTS_TRANSLUCENT | CONTENTS_DETAIL))c1 = 0;if (c2 & (CONTENTS_TRANSLUCENT | CONTENTS_DETAIL))c2 = 0;if ((c1 | c2) & CONTENTS_SOLID)return false; // can't see through solidif (!(c1 ^ c2))return true; // identical on both sidesif (!VisibleContents(c1 ^ c2))return true;return false;}/*===============Portal_EntityFloodThe entity flood determines which areas are"outside" on the map, which are then filled in.Flowing from side s to side !s===============*/qboolean Portal_EntityFlood(portal_t *p, int32_t s) {if (p->nodes[0]->planenum != PLANENUM_LEAF || p->nodes[1]->planenum != PLANENUM_LEAF)Error("Portal_EntityFlood: not a leaf");// can never cross to a solidif ((p->nodes[0]->contents & CONTENTS_SOLID) || (p->nodes[1]->contents & CONTENTS_SOLID))return false;// can flood through everything elsereturn true;}//=============================================================================int32_t c_tinyportals;/*=============AddPortalToNodes=============*/void AddPortalToNodes(portal_t *p, node_t *front, node_t *back) {if (p->nodes[0] || p->nodes[1])Error("AddPortalToNode: already included");p->nodes[0] = front;p->next[0] = front->portals;front->portals = p;p->nodes[1] = back;p->next[1] = back->portals;back->portals = p;}/*=============RemovePortalFromNode=============*/void RemovePortalFromNode(portal_t *portal, node_t *l) {portal_t **pp, *t;// remove reference to the current portalpp = &l->portals;while (1) {t = *pp;if (!t)Error("RemovePortalFromNode: portal not in leaf");if (t == portal)break;if (t->nodes[0] == l)pp = &t->next[0];else if (t->nodes[1] == l)pp = &t->next[1];elseError("RemovePortalFromNode: portal not bounding leaf");}if (portal->nodes[0] == l) {*pp = portal->next[0];portal->nodes[0] = NULL;} else if (portal->nodes[1] == l) {*pp = portal->next[1];portal->nodes[1] = NULL;}}//============================================================================void PrintPortal(portal_t *p) {int32_t i;winding_t *w;w = p->winding;for (i = 0; i < w->numpoints; i++)printf("(%5.0f,%5.0f,%5.0f)\n", w->p[i][0], w->p[i][1], w->p[i][2]);}/*================MakeHeadnodePortalsThe created portals will face the global outside_node================*/#define SIDESPACE 8void MakeHeadnodePortals(tree_t *tree) {vec3_t bounds[2];int32_t i, j, n;portal_t *p, *portals[6];plane_t bplanes[6], *pl;node_t *node;node = tree->headnode;// pad with some space so there will never be null volume leafsfor (i = 0; i < 3; i++) {bounds[0][i] = tree->mins[i] - SIDESPACE;bounds[1][i] = tree->maxs[i] + SIDESPACE;}tree->outside_node.planenum = PLANENUM_LEAF;tree->outside_node.brushlist = NULL;tree->outside_node.portals = NULL;tree->outside_node.contents = 0;for (i = 0; i < 3; i++)for (j = 0; j < 2; j++) {n = j * 3 + i;p = AllocPortal();portals[n] = p;pl = &bplanes[n];memset(pl, 0, sizeof(*pl));if (j) {pl->normal[i] = -1;pl->dist = -bounds[j][i];} else {pl->normal[i] = 1;pl->dist = bounds[j][i];}p->plane = *pl;p->winding = BaseWindingForPlane(pl->normal, pl->dist);AddPortalToNodes(p, node, &tree->outside_node);}// clip the basewindings by all the other planesfor (i = 0; i < 6; i++) {for (j = 0; j < 6; j++) {if (j == i)continue;ChopWindingInPlace(&portals[i]->winding, bplanes[j].normal, bplanes[j].dist, ON_EPSILON);}}}//===================================================/*================BaseWindingForNode================*/#define BASE_WINDING_EPSILON 0.001#define SPLIT_WINDING_EPSILON 0.001winding_t *BaseWindingForNode(node_t *node) {winding_t *w;node_t *n;plane_t *plane;vec3_t normal;vec_t dist;w = BaseWindingForPlane(mapplanes[node->planenum].normal,mapplanes[node->planenum].dist);// clip by all the parentsfor (n = node->parent; n && w;) {plane = &mapplanes[n->planenum];if (n->children[0] == node) { // take frontChopWindingInPlace(&w, plane->normal, plane->dist, BASE_WINDING_EPSILON);} else { // take backVectorSubtract(vec3_origin, plane->normal, normal);dist = -plane->dist;ChopWindingInPlace(&w, normal, dist, BASE_WINDING_EPSILON);}node = n;n = n->parent;}return w;}//============================================================qboolean WindingIsTiny(winding_t *w);/*==================MakeNodePortalcreate the new portal by taking the full plane winding for the cutting planeand clipping it by all of parents of this node==================*/void MakeNodePortal(node_t *node) {portal_t *new_portal, *p;winding_t *w;vec3_t normal;vec_t dist = 0;int32_t side = 0;w = BaseWindingForNode(node);// clip the portal by all the other portals in the nodefor (p = node->portals; p && w; p = p->next[side]) {if (p->nodes[0] == node) {side = 0;VectorCopy(p->plane.normal, normal);dist = p->plane.dist;} else if (p->nodes[1] == node) {side = 1;VectorSubtract(vec3_origin, p->plane.normal, normal);dist = -p->plane.dist;} elseError("CutNodePortals_r: mislinked portal");ChopWindingInPlace(&w, normal, dist, 0.1);}if (!w) {return;}if (WindingIsTiny(w)) {c_tinyportals++;FreeWinding(w);return;}new_portal = AllocPortal();new_portal->plane = mapplanes[node->planenum];new_portal->onnode = node;new_portal->winding = w;AddPortalToNodes(new_portal, node->children[0], node->children[1]);}/*==============SplitNodePortalsMove or split the portals that bound node so that the node'schildren have portals instead of node.==============*/void SplitNodePortals(node_t *node) {portal_t *p, *next_portal, *new_portal;node_t *f, *b, *other_node;int32_t side = 0;plane_t *plane;winding_t *frontwinding, *backwinding;plane = &mapplanes[node->planenum];f = node->children[0];b = node->children[1];for (p = node->portals; p; p = next_portal) {if (p->nodes[0] == node)side = 0;else if (p->nodes[1] == node)side = 1;elseError("CutNodePortals_r: mislinked portal");next_portal = p->next[side];other_node = p->nodes[!side];RemovePortalFromNode(p, p->nodes[0]);RemovePortalFromNode(p, p->nodes[1]);//// cut the portal into two portals, one on each side of the cut plane//ClipWindingEpsilon(p->winding, plane->normal, plane->dist,SPLIT_WINDING_EPSILON, &frontwinding, &backwinding);if (frontwinding && WindingIsTiny(frontwinding)) {FreeWinding(frontwinding);frontwinding = NULL;c_tinyportals++;}if (backwinding && WindingIsTiny(backwinding)) {FreeWinding(backwinding);backwinding = NULL;c_tinyportals++;}if (!frontwinding && !backwinding) { // tiny windings on both sidescontinue;}if (!frontwinding) {FreeWinding(backwinding);if (side == 0)AddPortalToNodes(p, b, other_node);elseAddPortalToNodes(p, other_node, b);continue;}if (!backwinding) {FreeWinding(frontwinding);if (side == 0)AddPortalToNodes(p, f, other_node);elseAddPortalToNodes(p, other_node, f);continue;}// the winding is splitnew_portal = AllocPortal();*new_portal = *p;new_portal->winding = backwinding;FreeWinding(p->winding);p->winding = frontwinding;if (side == 0) {AddPortalToNodes(p, f, other_node);AddPortalToNodes(new_portal, b, other_node);} else {AddPortalToNodes(p, other_node, f);AddPortalToNodes(new_portal, other_node, b);}}node->portals = NULL;}/*================CalcNodeBounds================*/void CalcNodeBounds(node_t *node) {portal_t *p;int32_t s;int32_t i;// calc mins/maxs for both leafs and nodesClearBounds(node->mins, node->maxs);for (p = node->portals; p; p = p->next[s]) {s = (p->nodes[1] == node);for (i = 0; i < p->winding->numpoints; i++)AddPointToBounds(p->winding->p[i], node->mins, node->maxs);}}/*==================MakeTreePortals_r==================*/void MakeTreePortals_r(node_t *node) {int32_t i;CalcNodeBounds(node);for (i = 0; i < 3; i++) {if (node->mins[i] < -2 * max_bounds || node->maxs[i] > 2 * max_bounds) {printf("WARNING: node with unbounded volume\n");printf(" Bounds: %g %g %g -> %g %g %g\n",node->mins[0], node->mins[1], node->mins[2], node->maxs[0], node->maxs[1], node->maxs[2]);break;}}if (node->planenum == PLANENUM_LEAF)return;MakeNodePortal(node);SplitNodePortals(node);MakeTreePortals_r(node->children[0]);MakeTreePortals_r(node->children[1]);}/*==================MakeTreePortals==================*/void MakeTreePortals(tree_t *tree) {MakeHeadnodePortals(tree);MakeTreePortals_r(tree->headnode);}/*=========================================================FLOOD ENTITIES=========================================================*//*=============FloodPortals_r=============*/void FloodPortals_r(node_t *node, int32_t dist) {portal_t *p;int32_t s;node->occupied = dist;for (p = node->portals; p; p = p->next[s]) {s = (p->nodes[1] == node);if (p->nodes[!s]->occupied)continue;if (!Portal_EntityFlood(p, s))continue;FloodPortals_r(p->nodes[!s], dist + 1);}}/*=============PlaceOccupant=============*/qboolean PlaceOccupant(node_t *headnode, vec3_t origin, entity_t *occupant) {node_t *node;vec_t d;plane_t *plane;// find the leaf to start innode = headnode;while (node->planenum != PLANENUM_LEAF) {plane = &mapplanes[node->planenum];d = DotProduct(origin, plane->normal) - plane->dist;if (d >= 0)node = node->children[0];elsenode = node->children[1];}if (node->contents == CONTENTS_SOLID)return false;node->occupant = occupant;FloodPortals_r(node, 1);return true;}/*=============FloodEntitiesMarks all nodes that can be reached by entites=============*/qboolean FloodEntities(tree_t *tree) {int32_t i;vec3_t origin;char *cl;qboolean inside;node_t *headnode;headnode = tree->headnode;qprintf("--- FloodEntities ---\n");inside = false;tree->outside_node.occupied = 0;for (i = 1; i < num_entities; i++) {GetVectorForKey(&entities[i], "origin", origin);if (VectorCompare(origin, vec3_origin))continue;cl = ValueForKey(&entities[i], "classname");origin[2] += 1; // so objects on floor are ok// nudge playerstart around if needed so clipping hulls allways// have a vlaid pointif (!strcmp(cl, "info_player_start")) {int32_t x, y;for (x = -16; x <= 16; x += 16) {for (y = -16; y <= 16; y += 16) {origin[0] += x;origin[1] += y;if (PlaceOccupant(headnode, origin, &entities[i])) {inside = true;goto gotit;}origin[0] -= x;origin[1] -= y;}}gotit:;} else {if (PlaceOccupant(headnode, origin, &entities[i]))inside = true;}}if (!inside) {qprintf("no entities in open -- no filling\n");} else if (tree->outside_node.occupied) {qprintf("entity reached from outside -- no filling\n");}return (qboolean)(inside && !tree->outside_node.occupied);}/*=========================================================FLOOD AREAS=========================================================*/int32_t c_areas;/*=============FloodAreas_r=============*/void FloodAreas_r(node_t *node) {portal_t *p;int32_t s;bspbrush_t *b;entity_t *e;if (node->contents == CONTENTS_AREAPORTAL) {// this node is part of an area portalb = node->brushlist;e = &entities[b->original->entitynum];// if the current area has allready touched this// portal, we are doneif (e->portalareas[0] == c_areas || e->portalareas[1] == c_areas)return;// note the current area as bounding the portalif (e->portalareas[1]) {printf("WARNING: areaportal entity %i touches > 2 areas\n Node Bounds: %g %g %g -> %g %g %g\n", b->original->entitynum,node->mins[0], node->mins[1], node->mins[2], node->maxs[0], node->maxs[1], node->maxs[2]);return;}if (e->portalareas[0])e->portalareas[1] = c_areas;elsee->portalareas[0] = c_areas;return;}if (node->area)return; // allready got itnode->area = c_areas;for (p = node->portals; p; p = p->next[s]) {s = (p->nodes[1] == node);#if 0if (p->nodes[!s]->occupied)continue;#endifif (!Portal_EntityFlood(p, s))continue;FloodAreas_r(p->nodes[!s]);}}/*=============FindAreas_rJust decend the tree, and for each node that hasn't had anarea set, flood fill out from there=============*/void FindAreas_r(node_t *node) {if (node->planenum != PLANENUM_LEAF) {FindAreas_r(node->children[0]);FindAreas_r(node->children[1]);return;}if (node->area)return; // allready got itif (node->contents & CONTENTS_SOLID)return;if (!node->occupied)return; // not reachable by entities// area portals are allways only flooded into, never// out ofif (node->contents == CONTENTS_AREAPORTAL)return;c_areas++;FloodAreas_r(node);}/*=============SetAreaPortalAreas_rJust decend the tree, and for each node that hasn't had anarea set, flood fill out from there=============*/void SetAreaPortalAreas_r(node_t *node) {bspbrush_t *b;entity_t *e;if (node->planenum != PLANENUM_LEAF) {SetAreaPortalAreas_r(node->children[0]);SetAreaPortalAreas_r(node->children[1]);return;}if (node->contents == CONTENTS_AREAPORTAL) {if (node->area)return; // allready setb = node->brushlist;e = &entities[b->original->entitynum];node->area = e->portalareas[0];if (!e->portalareas[1]) {printf("WARNING: areaportal entity %i doesn't touch two areas\n Node Bounds: %g %g %g -> %g %g %g\n", b->original->entitynum,node->mins[0], node->mins[1], node->mins[2], node->maxs[0], node->maxs[1], node->maxs[2]);return;}}}/*=============EmitAreaPortals=============*/void EmitAreaPortals(node_t *headnode) {int32_t i, j;entity_t *e;dareaportal_t *dp;if (c_areas > MAX_MAP_AREAS)Error("MAX_MAP_AREAS");numareas = c_areas + 1;numareaportals = 1; // leave 0 as an errorfor (i = 1; i <= c_areas; i++) {dareas[i].firstareaportal = numareaportals;for (j = 0; j < num_entities; j++) {e = &entities[j];if (!e->areaportalnum)continue;dp = &dareaportals[numareaportals];if (e->portalareas[0] == i) {dp->portalnum = e->areaportalnum;dp->otherarea = e->portalareas[1];numareaportals++;} else if (e->portalareas[1] == i) {dp->portalnum = e->areaportalnum;dp->otherarea = e->portalareas[0];numareaportals++;}}dareas[i].numareaportals = numareaportals - dareas[i].firstareaportal;}qprintf("%5i numareas\n", numareas);qprintf("%5i numareaportals\n", numareaportals);}/*=============FloodAreasMark each leaf with an area, bounded by CONTENTS_AREAPORTAL=============*/void FloodAreas(tree_t *tree) {qprintf("--- FloodAreas ---\n");FindAreas_r(tree->headnode);SetAreaPortalAreas_r(tree->headnode);qprintf("%5i areas\n", c_areas);}//======================================================int32_t c_outside;int32_t c_inside;int32_t c_solid;void FillOutside_r(node_t *node) {if (node->planenum != PLANENUM_LEAF) {FillOutside_r(node->children[0]);FillOutside_r(node->children[1]);return;}// anything not reachable by an entity// can be filled awayif (!node->occupied) {if (node->contents != CONTENTS_SOLID) {c_outside++;node->contents = CONTENTS_SOLID;} elsec_solid++;} elsec_inside++;}/*=============FillOutsideFill all nodes that can't be reached by entities=============*/void FillOutside(node_t *headnode) {c_outside = 0;c_inside = 0;c_solid = 0;qprintf("--- FillOutside ---\n");FillOutside_r(headnode);qprintf("%5i solid leafs\n", c_solid);qprintf("%5i leafs filled\n", c_outside);qprintf("%5i inside leafs\n", c_inside);}//==============================================================/*============FindPortalSideFinds a brush side to use for texturing the given portal============*/void FindPortalSide(portal_t *p) {int32_t viscontents;bspbrush_t *bb;mapbrush_t *brush;node_t *n;int32_t i, j;int32_t planenum;side_t *side, *bestside;vec_t dot, bestdot;plane_t *p1, *p2;// decide which content change is strongest// solid > lava > water, etcviscontents = VisibleContents(p->nodes[0]->contents ^ p->nodes[1]->contents);if (!viscontents)return;planenum = p->onnode->planenum;bestside = NULL;bestdot = 0;for (j = 0; j < 2; j++) {n = p->nodes[j];p1 = &mapplanes[p->onnode->planenum];for (bb = n->brushlist; bb; bb = bb->next) {brush = bb->original;if (!(brush->contents & viscontents))continue;for (i = 0; i < brush->numsides; i++) {side = &brush->original_sides[i];if (side->bevel)continue;if (side->texinfo == TEXINFO_NODE)continue; // non-visibleif ((side->planenum & ~1) == planenum) { // exact matchbestside = &brush->original_sides[i];goto gotit;}// see how close the match isp2 = &mapplanes[side->planenum & ~1];dot = DotProduct(p1->normal, p2->normal);if (dot > bestdot) {bestdot = dot;bestside = side;}}}}gotit:if (!bestside)qprintf("WARNING: side not found for portal\n");p->sidefound = true;p->side = bestside;}/*===============MarkVisibleSides_r===============*/void MarkVisibleSides_r(node_t *node) {portal_t *p;int32_t s;if (node->planenum != PLANENUM_LEAF) {MarkVisibleSides_r(node->children[0]);MarkVisibleSides_r(node->children[1]);return;}// empty leafs are never boundary leafsif (!node->contents)return;// see if there is a visible facefor (p = node->portals; p; p = p->next[!s]) {s = (p->nodes[0] == node);if (!p->onnode)continue; // edge of worldif (!p->sidefound)FindPortalSide(p);if (p->side)p->side->visible = true;}}/*=============MarkVisibleSides=============*/void MarkVisibleSides(tree_t *tree, int32_t startbrush, int32_t endbrush) {int32_t i, j;mapbrush_t *mb;int32_t numsides;qprintf("--- MarkVisibleSides ---\n");// clear all the visible flagsfor (i = startbrush; i < endbrush; i++) {mb = &mapbrushes[i];numsides = mb->numsides;for (j = 0; j < numsides; j++)mb->original_sides[j].visible = false;}// set visible flags on the sides that are used by portalsMarkVisibleSides_r(tree->headnode);}
/*===========================================================================Copyright (C) 1997-2006 Id Software, Inc.This program is free software; you can redistribute it and/or modifyit under the terms of the GNU General Public License as published bythe 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 ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See theGNU General Public License for more details.You should have received a copy of the GNU General Public License alongwith this program; if not, write to the Free Software Foundation, Inc.,51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.===========================================================================*/#include "qbsp.h"extern qboolean onlyents;int32_t nummapbrushes;mapbrush_t mapbrushes[MAX_MAP_BRUSHES_QBSP];int32_t nummapbrushsides;side_t brushsides[MAX_MAP_SIDES];brush_texture_t side_brushtextures[MAX_MAP_SIDES];int32_t nummapplanes;plane_t mapplanes[MAX_MAP_PLANES_QBSP];#define PLANE_HASHES 1024plane_t *planehash[PLANE_HASHES];vec3_t map_mins, map_maxs;void TestExpandBrushes(void);int32_t c_boxbevels;int32_t c_edgebevels;int32_t c_areaportals;int32_t c_clipbrushes;int32_t g_nMapFileVersion = 0; // DarkEssence: variable for check #mapversion// #mapversion in search to find in code/*=============================================================================PLANE FINDING=============================================================================*//*=================PlaneTypeForNormal=================*/int32_t PlaneTypeForNormal(vec3_t normal) {vec_t ax, ay, az;// NOTE: should these have an epsilon around 1.0?if (normal[0] >= 1.0 || normal[0] <= -1.0)return PLANE_X;if (normal[1] >= 1.0 || normal[1] <= -1.0)return PLANE_Y;if (normal[2] >= 1.0 || normal[2] <= -1.0)return PLANE_Z;ax = fabs(normal[0]);ay = fabs(normal[1]);az = fabs(normal[2]);if (ax >= ay && ax >= az)return PLANE_ANYX;if (ay >= ax && ay >= az)return PLANE_ANYY;return PLANE_ANYZ;}/*================PlaneEqual================*/#define DIST_EPSILON 0.01qboolean PlaneEqual(plane_t *p, vec3_t normal, vec_t dist) {#if 1if (fabs(p->normal[0] - normal[0]) < NORMAL_EPSILON && fabs(p->normal[1] - normal[1]) < NORMAL_EPSILON && fabs(p->normal[2] - normal[2]) < NORMAL_EPSILON && fabs(p->dist - dist) < DIST_EPSILON)return true;#elseif (p->normal[0] == normal[0] && p->normal[1] == normal[1] && p->normal[2] == normal[2] && p->dist == dist)return true;#endifreturn false;}/*================AddPlaneToHash================*/void AddPlaneToHash(plane_t *p) {int32_t hash;hash = (int32_t)fabs(p->dist) / 8;hash &= (PLANE_HASHES - 1);p->hash_chain = planehash[hash];planehash[hash] = p;}/*================CreateNewFloatPlane================*/int32_t CreateNewFloatPlane(vec3_t normal, vec_t dist, int32_t bnum) {plane_t *p, temp;if (VectorLength(normal) < 0.5)Error("FloatPlane: bad normal. Brush %i", bnum); // qb: add brushnum// create a new planeif (use_qbsp) {if (nummapplanes + 2 > MAX_MAP_PLANES_QBSP)Error("MAX_MAP_PLANES_QBSP");} else if (nummapplanes + 2 > MAX_MAP_PLANES)Error("MAX_MAP_PLANES");p = &mapplanes[nummapplanes];VectorCopy(normal, p->normal);p->dist = dist;p->type = (p + 1)->type = PlaneTypeForNormal(p->normal);VectorSubtract(vec3_origin, normal, (p + 1)->normal);(p + 1)->dist = -dist;nummapplanes += 2;// allways put axial planes facing positive firstif (p->type < 3) {if (p->normal[0] < 0 || p->normal[1] < 0 || p->normal[2] < 0) {// flip ordertemp = *p;*p = *(p + 1);*(p + 1) = temp;AddPlaneToHash(p);AddPlaneToHash(p + 1);return nummapplanes - 1;}}AddPlaneToHash(p);AddPlaneToHash(p + 1);return nummapplanes - 2;}/*==============SnapVector==============*/void SnapVector(vec3_t normal) {int32_t i;for (i = 0; i < 3; i++) {if (fabs(normal[i] - 1) < NORMAL_EPSILON) {VectorClear(normal);normal[i] = 1;break;}if (fabs(normal[i] - -1) < NORMAL_EPSILON) {VectorClear(normal);normal[i] = -1;break;}}}/*==============SnapPlane==============*/void SnapPlane(vec3_t normal, vec_t *dist) {SnapVector(normal);if (fabs(*dist - Q_rint(*dist)) < DIST_EPSILON)*dist = Q_rint(*dist);}/*=============FindFloatPlane=============*/int32_t FindFloatPlane(vec3_t normal, vec_t dist, int32_t bnum) {int32_t i;plane_t *p;int32_t hash, h;SnapPlane(normal, &dist);hash = (int32_t)fabs(dist) / 8;hash &= (PLANE_HASHES - 1);// search the border bins as wellfor (i = -1; i <= 1; i++) {h = (hash + i) & (PLANE_HASHES - 1);for (p = planehash[h]; p; p = p->hash_chain) {if (PlaneEqual(p, normal, dist))return p - mapplanes;}}return CreateNewFloatPlane(normal, dist, bnum);}/*================PlaneFromPoints================*/int32_t PlaneFromPoints(vec3_t p0, vec3_t p1, vec3_t p2, mapbrush_t *b) {vec3_t t1, t2, normal;vec_t dist;VectorSubtract(p0, p1, t1);VectorSubtract(p2, p1, t2);CrossProduct(t1, t2, normal);VectorNormalize(normal, normal);dist = DotProduct(p0, normal);return FindFloatPlane(normal, dist, b->brushnum);}//====================================================================/*===========BrushContents===========*/int32_t BrushContents(mapbrush_t *b) {int32_t contents;side_t *s;int32_t i;int32_t trans;s = &b->original_sides[0];contents = s->contents;trans = texinfo[s->texinfo].flags;for (i = 1; i < b->numsides; i++, s++) {s = &b->original_sides[i];trans |= texinfo[s->texinfo].flags;if (s->contents != contents) {printf("Entity %i, Brush %i, Line %i: mixed face contents\n", b->entitynum, b->brushnum, scriptline + 1); // qb: add scriptlinebreak;}}// if any side is translucent, mark the contents// and change solid to windowif (trans & (SURF_TRANS33 | SURF_TRANS66)) {contents |= CONTENTS_TRANSLUCENT;if (contents & CONTENTS_SOLID) {contents &= ~CONTENTS_SOLID;contents |= CONTENTS_WINDOW;}}return contents;}//============================================================================/*=================AddBrushBevelsAdds any additional planes necessary to allow the brush to be expandedagainst axial bounding boxes=================*/void AddBrushBevels(mapbrush_t *b) {int32_t axis, dir;int32_t i, j, k, l, order;side_t sidetemp;brush_texture_t tdtemp;side_t *s, *s2;vec3_t normal;vec_t dist;winding_t *w, *w2;vec3_t vec, vec2;vec_t d;//// add the axial planes//order = 0;for (axis = 0; axis < 3; axis++) {for (dir = -1; dir <= 1; dir += 2, order++) {// see if the plane is allready presentfor (i = 0, s = b->original_sides; i < b->numsides; i++, s++) {if (mapplanes[s->planenum].normal[axis] == dir)break;}if (i == b->numsides) {// add a new sideif (use_qbsp) {if (nummapbrushsides == MAX_MAP_BRUSHSIDES_QBSP)Error("MAX_MAP_BRUSHSIDES_QBSP");} else if (nummapbrushsides == MAX_MAP_BRUSHSIDES)Error("MAX_MAP_BRUSHSIDES");nummapbrushsides++;b->numsides++;VectorClear(normal);normal[axis] = dir;if (dir == 1)dist = b->maxs[axis];elsedist = -b->mins[axis];s->planenum = FindFloatPlane(normal, dist, b->brushnum);s->texinfo = b->original_sides[0].texinfo;s->contents = b->original_sides[0].contents;s->bevel = true;c_boxbevels++;}// if the plane is not in it canonical order, swap itif (i != order) {sidetemp = b->original_sides[order];b->original_sides[order] = b->original_sides[i];b->original_sides[i] = sidetemp;j = b->original_sides - brushsides;tdtemp = side_brushtextures[j + order];side_brushtextures[j + order] = side_brushtextures[j + i];side_brushtextures[j + i] = tdtemp;}}}//// add the edge bevels//if (b->numsides == 6)return; // pure axial// test the non-axial plane edgesfor (i = 6; i < b->numsides; i++) {s = b->original_sides + i;w = s->winding;if (!w)continue;for (j = 0; j < w->numpoints; j++) {k = (j + 1) % w->numpoints;VectorSubtract(w->p[j], w->p[k], vec);if (VectorNormalize(vec, vec) < 0.5)continue;SnapVector(vec);for (k = 0; k < 3; k++)if (vec[k] == -1 || vec[k] == 1)break; // axialif (k != 3)continue; // only test non-axial edges// try the six possible slanted axials from this edgefor (axis = 0; axis < 3; axis++) {for (dir = -1; dir <= 1; dir += 2) {// construct a planeVectorClear(vec2);vec2[axis] = dir;CrossProduct(vec, vec2, normal);if (VectorNormalize(normal, normal) < 0.5)continue;dist = DotProduct(w->p[j], normal);// if all the points on all the sides are// behind this plane, it is a proper edge bevelfor (k = 0; k < b->numsides; k++) {// if this plane has allready been used, skip itif (PlaneEqual(&mapplanes[b->original_sides[k].planenum], normal, dist))break;w2 = b->original_sides[k].winding;if (!w2)continue;for (l = 0; l < w2->numpoints; l++) {d = DotProduct(w2->p[l], normal) - dist;if (d > 0.1)break; // point in front}if (l != w2->numpoints)break;}if (k != b->numsides)continue; // wasn't part of the outer hull// add this planeif (use_qbsp) {if (nummapbrushsides == MAX_MAP_BRUSHSIDES_QBSP)Error("MAX_MAP_BRUSHSIDES_QBSP");} else if (nummapbrushsides == MAX_MAP_BRUSHSIDES)Error("MAX_MAP_BRUSHSIDES");nummapbrushsides++;s2 = &b->original_sides[b->numsides];s2->planenum = FindFloatPlane(normal, dist, b->brushnum);s2->texinfo = b->original_sides[0].texinfo;s2->contents = b->original_sides[0].contents;s2->bevel = true;c_edgebevels++;b->numsides++;}}}}}/*================MakeBrushWindingsmakes basewindigs for sides and mins / maxs for the brush================*/void MakeBrushWindings(mapbrush_t *ob) {int32_t i, j;winding_t *w;side_t *side;plane_t *plane;ClearBounds(ob->mins, ob->maxs);for (i = 0; i < ob->numsides; i++) {plane = &mapplanes[ob->original_sides[i].planenum];w = BaseWindingForPlane(plane->normal, plane->dist);for (j = 0; j < ob->numsides && w; j++) {if (i == j)continue;if (ob->original_sides[j].bevel)continue;plane = &mapplanes[ob->original_sides[j].planenum ^ 1];ChopWindingInPlace(&w, plane->normal, plane->dist, 0);}side = &ob->original_sides[i];side->winding = w;if (w) {side->visible = true;for (j = 0; j < w->numpoints; j++)AddPointToBounds(w->p[j], ob->mins, ob->maxs);}}for (i = 0; i < 3; i++) {if (ob->mins[i] < -max_bounds || ob->maxs[i] > max_bounds) {printf("Entity %i, Brush %i, Line %i: bounds out of range\n", ob->entitynum, ob->brushnum, scriptline + 1); // qb: add scriptlineprintf("bounds: %g %g %g -> %g %g %g\n",ob->mins[0], ob->mins[1], ob->mins[2], ob->maxs[0], ob->maxs[1], ob->maxs[2]);return;}if (ob->mins[i] > max_bounds || ob->maxs[i] < -max_bounds) {printf("Entity %i, Brush %i, Line %i: no visible sides on brush\n", ob->entitynum, ob->brushnum, scriptline + 1); // qb: add scriptlineprintf("bounds: %g %g %g -> %g %g %g\n",ob->mins[0], ob->mins[1], ob->mins[2], ob->maxs[0], ob->maxs[1], ob->maxs[2]);return;}}return;}/*=================ParseBrush=================*/void ParseBrush(entity_t *mapent) {mapbrush_t *b;int32_t i, j, k;int32_t mt;side_t *side, *s2;int32_t planenum;brush_texture_t td;vec3_t planepts[3];vec_t UVaxis[6]; // DarkEssence: UV axis in 220 #mapversionif (use_qbsp) {if (nummapbrushes == MAX_MAP_BRUSHES_QBSP)Error("nummapbrushes == MAX_MAP_BRUSHES_QBSP (%i)", MAX_MAP_BRUSHES_QBSP);} else if (nummapbrushes == MAX_MAP_BRUSHES)Error("nummapbrushes == MAX_MAP_BRUSHES (%i)", MAX_MAP_BRUSHES);b = &mapbrushes[nummapbrushes];b->original_sides = &brushsides[nummapbrushsides];b->entitynum = num_entities - 1;b->brushnum = nummapbrushes - mapent->firstbrush;do {if (!GetToken(true))break;if (!strcmp(token, "}"))break;if (use_qbsp) {if (nummapbrushsides == MAX_MAP_BRUSHSIDES_QBSP)Error("MAX_MAP_BRUSHSIDES_QBSP");} else if (nummapbrushsides == MAX_MAP_BRUSHSIDES)Error("MAX_MAP_BRUSHSIDES");side = &brushsides[nummapbrushsides];// read the three point plane definitionfor (i = 0; i < 3; i++) {if (i != 0)GetToken(true);if (strcmp(token, "("))Error("parsing brush %i", i + 1);for (j = 0; j < 3; j++) {GetToken(false);planepts[i][j] = atof(token);}GetToken(false);if (strcmp(token, ")"))Error("parsing brush %i", i + 1);}//// read the texturedef//GetToken(false);if (!strcmp(token, "__TB_empty")) {printf("Face without texture ( %s ) at line %i\n", token, scriptline + 1);}strcpy(td.name, token);// DarkEssence: take parms according to mapversionif (g_nMapFileVersion < 220) // old #mapversion{GetToken(false);td.shift[0] = atoi(token);GetToken(false);td.shift[1] = atoi(token);} else // new #mapversion{GetToken(false);if (strcmp(token, "[")) {Error("missing '[ in texturedef");}GetToken(false);UVaxis[0] = atof(token);GetToken(false);UVaxis[1] = atof(token);GetToken(false);UVaxis[2] = atof(token);GetToken(false);td.shift[0] = atof(token);GetToken(false);if (strcmp(token, "]")) {Error("missing ']' in texturedef");}// texture V axisGetToken(false);if (strcmp(token, "[")) {Error("missing '[ in texturedef");}GetToken(false);UVaxis[3] = atof(token);GetToken(false);UVaxis[4] = atof(token);GetToken(false);UVaxis[5] = atof(token);GetToken(false);td.shift[1] = atof(token);GetToken(false);if (strcmp(token, "]")) {Error("missing ']' in texturedef");}}GetToken(false);td.rotate = atoi(token);GetToken(false);td.scale[0] = atof(token);GetToken(false);td.scale[1] = atof(token);// find default flags and valuesmt = FindMiptex(td.name);td.flags = textureref[mt].flags;td.value = textureref[mt].value;side->contents = textureref[mt].contents;side->surf = td.flags = textureref[mt].flags;if (TokenAvailable()) {GetToken(false);side->contents = atoi(token);GetToken(false);side->surf = td.flags = atoi(token);GetToken(false);td.value = atoi(token);}// translucent objects are automatically classified as detailif (side->surf & (SURF_TRANS33 | SURF_TRANS66))side->contents |= CONTENTS_DETAIL;if (side->contents & (CONTENTS_PLAYERCLIP | CONTENTS_MONSTERCLIP))side->contents |= CONTENTS_DETAIL;if (fulldetail)side->contents &= ~CONTENTS_DETAIL;if (!(side->contents & ((LAST_VISIBLE_CONTENTS - 1) | CONTENTS_PLAYERCLIP | CONTENTS_MONSTERCLIP | CONTENTS_MIST)))side->contents |= CONTENTS_SOLID;// qb: don't change SURF_SKIP contentsif (noskipfix) {if (side->surf & SURF_HINT) {side->contents = 0;side->surf &= ~CONTENTS_DETAIL;}} else {// hints and skips have no contentsif (side->surf & (SURF_HINT | SURF_SKIP)) {side->contents = 0;side->surf &= ~CONTENTS_DETAIL;}}//// find the plane number//planenum = PlaneFromPoints(planepts[0], planepts[1], planepts[2], b);if (planenum == -1) {printf("Entity %i, Brush %i, Line %i: plane with no normal\n", b->entitynum, b->brushnum, scriptline + 1); // qb: add scriptlinecontinue;}//// see if the plane has been used already//for (k = 0; k < b->numsides; k++) {s2 = b->original_sides + k;if (s2->planenum == planenum) {printf("Entity %i, Brush %i, Line %i: duplicate plane\n", b->entitynum, b->brushnum, scriptline + 1); // qb: add scriptlinebreak;}if (s2->planenum == (planenum ^ 1)) {printf("Entity %i, Brush %i, Line %i: mirrored plane\n", b->entitynum, b->brushnum, scriptline + 1); // qb: add scriptlinebreak;}}if (k != b->numsides)continue; // duplicated//// keep this side//side = b->original_sides + b->numsides;side->planenum = planenum;if (g_nMapFileVersion < 220) // DarkEssence: texinfo #mapversionside->texinfo = TexinfoForBrushTexture(&mapplanes[planenum], &td, vec3_origin);else // texinfo for #mapversion 220side->texinfo = TexinfoForBrushTexture_UV(&td, UVaxis);// save the td off in case there is an origin brush and we have to recalculate the texinfoside_brushtextures[nummapbrushsides] = td;nummapbrushsides++;b->numsides++;} while (true);// get the content for the entire brushb->contents = BrushContents(b);// allow detail brushes to be removedif (nodetail && (b->contents & CONTENTS_DETAIL)) {b->numsides = 0;return;}// allow water brushes to be removedif (nowater && (b->contents & (CONTENTS_LAVA | CONTENTS_SLIME | CONTENTS_WATER))) {b->numsides = 0;return;}// create windings for sides and bounds for brushMakeBrushWindings(b);// brushes that will not be visible at all will never be// used as bsp splittersif (b->contents & (CONTENTS_PLAYERCLIP | CONTENTS_MONSTERCLIP)) {c_clipbrushes++;for (i = 0; i < b->numsides; i++)b->original_sides[i].texinfo = TEXINFO_NODE;}//// origin brushes are removed, but they set the rotation origin for the rest of the brushes in the entity.// After the entire entity is parsed, the planenums and texinfos will be adjusted for the origin brush//if (b->contents & CONTENTS_ORIGIN) {char string[32];vec3_t origin;if (num_entities == 1) {Error("Entity %i, Brush %i, Line %i: origin brushes not allowed in world", b->entitynum, b->brushnum, scriptline + 1); // qb: add scriptlinereturn;}VectorAdd(b->mins, b->maxs, origin);VectorScale(origin, 0.5, origin);sprintf(string, "%i %i %i", (int32_t)origin[0], (int32_t)origin[1], (int32_t)origin[2]);SetKeyValue(&entities[b->entitynum], "origin", string);VectorCopy(origin, entities[b->entitynum].origin);// don't keep this brushb->numsides = 0;return;}AddBrushBevels(b);nummapbrushes++;mapent->numbrushes++;}/*================MoveBrushesToWorldTakes all of the brushes from the current entity andadds them to the world's brush list.Used by func_group and func_areaportal================*/void MoveBrushesToWorld(entity_t *mapent) {int32_t newbrushes;int32_t worldbrushes;mapbrush_t *temp;int32_t i;// this is pretty gross, because the brushes are expected to be// in linear order for each entitynewbrushes = mapent->numbrushes;worldbrushes = entities[0].numbrushes;temp = malloc(newbrushes * sizeof(mapbrush_t));memcpy(temp, mapbrushes + mapent->firstbrush, newbrushes * sizeof(mapbrush_t));#if 0 // let them keep their original brush numbersfor (i=0 ; i<newbrushes ; i++)temp[i].entitynum = 0;#endif// make space to move the brushes (overlapped copy)memmove(mapbrushes + worldbrushes + newbrushes,mapbrushes + worldbrushes,sizeof(mapbrush_t) * (nummapbrushes - worldbrushes - newbrushes));// copy the new brushes downmemcpy(mapbrushes + worldbrushes, temp, sizeof(mapbrush_t) * newbrushes);// fix up indexesentities[0].numbrushes += newbrushes;for (i = 1; i < num_entities; i++)entities[i].firstbrush += newbrushes;free(temp);mapent->numbrushes = 0;}/*================ParseMapEntity================*/qboolean ParseMapEntity(void) {mapbrush_t *b;if (!GetToken(true))return false;if (strcmp(token, "{"))Error("ParseEntity: { not found");if (use_qbsp) {if (num_entities == WARN_MAP_ENTITIES_QBSP)printf("WARNING: num_entities may exceed protocol limit (%i)", WARN_MAP_ENTITIES_QBSP);if (num_entities == max_entities)Error("num_entities exceeds MAX_MAP_ENTITIES_QBSP (%i)", MAX_MAP_ENTITIES_QBSP);} else {if (num_entities == DEFAULT_MAP_ENTITIES)printf("WARNING: num_entities exceeds vanilla limit (%i)", DEFAULT_MAP_ENTITIES);if (num_entities == max_entities) // qb: from kmqbsp3 Knightmare changed- was MAX_MAP_ENTITIESError("num_entities exceeds MAX_MAP_ENTITIES (%i)", MAX_MAP_ENTITIES);}entity_t *mapent = &entities[num_entities];num_entities++;memset(mapent, 0, sizeof(*mapent));mapent->firstbrush = nummapbrushes;mapent->numbrushes = 0;// mapent->portalareas[0] = -1;// mapent->portalareas[1] = -1;do {if (!GetToken(true))Error("ParseEntity: EOF without closing brace");if (!strcmp(token, "}"))break;if (!strcmp(token, "{")) {ParseBrush(mapent);} else {epair_t *e = ParseEpair();e->next = mapent->epairs;mapent->epairs = e;if (!strcmp(e->key, "mapversion")) // DarkEssence: set #mapversion{g_nMapFileVersion = atoi(e->value); // or keep default value - 0RemoveLastEpair(mapent);}}} while (true);GetVectorForKey(mapent, "origin", mapent->origin);//// if there was an origin brush, offset all of the planes and texinfo//if (mapent->origin[0] || mapent->origin[1] || mapent->origin[2]) {for (int32_t i = 0; i < mapent->numbrushes; i++) {b = &mapbrushes[mapent->firstbrush + i];for (int32_t j = 0; j < b->numsides; j++) {side_t *s = &b->original_sides[j];const vec_t newdist = mapplanes[s->planenum].dist - DotProduct(mapplanes[s->planenum].normal, mapent->origin);s->planenum = FindFloatPlane(mapplanes[s->planenum].normal, newdist, b->brushnum);if (g_nMapFileVersion < 220) // mxds->texinfo = TexinfoForBrushTexture(&mapplanes[s->planenum], &side_brushtextures[s - brushsides], mapent->origin);elses->texinfo = ApplyTexinfoOffset_UV(s->texinfo, &side_brushtextures[s - brushsides], mapent->origin);}MakeBrushWindings(b);}}// group entities are just for editor convenience// toss all brushes into the world entityif (!strcmp("func_group", ValueForKey(mapent, "classname"))) {MoveBrushesToWorld(mapent);mapent->numbrushes = 0;return true;}// areaportal entities move their brushes, but don't eliminate// the entityif (!strcmp("func_areaportal", ValueForKey(mapent, "classname"))) {char str[128];if (mapent->numbrushes != 1)Error("Entity %i: func_areaportal can only be a single brush", num_entities - 1);b = &mapbrushes[nummapbrushes - 1];b->contents = CONTENTS_AREAPORTAL;c_areaportals++;mapent->areaportalnum = c_areaportals;// set the portal number as "style"sprintf(str, "%i", c_areaportals);SetKeyValue(mapent, "style", str);MoveBrushesToWorld(mapent);return true;}return true;}//===================================================================/*================LoadMapFile================*/void LoadMapFile(char *filename) {int32_t i;qprintf("--- LoadMapFile ---\n");LoadScriptFile(filename);nummapbrushsides = 0;num_entities = 0;while (ParseMapEntity()) {}ClearBounds(map_mins, map_maxs);for (i = 0; i < entities[0].numbrushes; i++) {if (mapbrushes[i].mins[0] > max_bounds)continue; // no valid pointsAddPointToBounds(mapbrushes[i].mins, map_mins, map_maxs);AddPointToBounds(mapbrushes[i].maxs, map_mins, map_maxs);}qprintf("%5i brushes\n", nummapbrushes);qprintf("%5i clipbrushes\n", c_clipbrushes);qprintf("%5i total sides\n", nummapbrushsides);qprintf("%5i boxbevels\n", c_boxbevels);qprintf("%5i edgebevels\n", c_edgebevels);qprintf("%5i entities\n", num_entities);qprintf("%5i planes\n", nummapplanes);qprintf("%5i areaportals\n", c_areaportals);qprintf("size: %5.0f,%5.0f,%5.0f to %5.0f,%5.0f,%5.0f\n", map_mins[0], map_mins[1], map_mins[2],map_maxs[0], map_maxs[1], map_maxs[2]);// TestExpandBrushes ();}//====================================================================/*================TestExpandBrushesExpands all the brush planes and saves a new map out================*/void TestExpandBrushes(void) {FILE *f;side_t *s;int32_t i, j, bn;winding_t *w;char *name = "expanded.map";mapbrush_t *brush;vec_t dist;f = fopen(name, "wb");if (!f)Error("Can't write %s\b", name);fprintf(f, "{\n\"classname\" \"worldspawn\"\n");for (bn = 0; bn < nummapbrushes; bn++) {brush = &mapbrushes[bn];fprintf(f, "{\n");for (i = 0; i < brush->numsides; i++) {s = brush->original_sides + i;dist = mapplanes[s->planenum].dist;for (j = 0; j < 3; j++)dist += fabs(16 * mapplanes[s->planenum].normal[j]);w = BaseWindingForPlane(mapplanes[s->planenum].normal, dist);fprintf(f, "( %g %g %g ) ", w->p[0][0], w->p[0][1], w->p[0][2]);fprintf(f, "( %g %g %g ) ", w->p[1][0], w->p[1][1], w->p[1][2]);fprintf(f, "( %g %g %g ) ", w->p[2][0], w->p[2][1], w->p[2][2]);fprintf(f, "%s 0 0 0 1 1\n", texinfo[s->texinfo].texture);FreeWinding(w);}fprintf(f, "}\n");}fprintf(f, "}\n");fclose(f);Error("can't proceed after expanding brushes");}
/*===========================================================================Copyright (C) 1997-2006 Id Software, Inc.This program is free software; you can redistribute it and/or modifyit under the terms of the GNU General Public License as published bythe 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 ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See theGNU General Public License for more details.You should have received a copy of the GNU General Public License alongwith this program; if not, write to the Free Software Foundation, Inc.,51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.===========================================================================*/#include "qbsp.h"/*==============================================================================LEAF FILE GENERATIONSave out name.line for qe3 to read==============================================================================*//*=============LeakFileFinds the shortest possible chain of portalsthat leads from the outside leaf to a specificallyoccupied leaf=============*/void LeakFile(tree_t *tree) {vec3_t mid;FILE *linefile;char filename[1030];node_t *node;int32_t count;if (!tree->outside_node.occupied)return;qprintf("--- LeakFile ---\n");//// write the points to the file//sprintf(filename, "%s.pts", source);linefile = fopen(filename, "w");if (!linefile)Error("Couldn't open %s\n", filename);count = 0;node = &tree->outside_node;while (node->occupied > 1) {int32_t next;portal_t *p, *nextportal = NULL;node_t *nextnode = NULL;int32_t s;// find the best portal exitnext = node->occupied;for (p = node->portals; p; p = p->next[!s]) {s = (p->nodes[0] == node);if (p->nodes[s]->occupied && p->nodes[s]->occupied < next) {nextportal = p;nextnode = p->nodes[s];next = nextnode->occupied;}}node = nextnode;if (nextportal) // qb: could be NULL{WindingCenter(nextportal->winding, mid);fprintf(linefile, "%f %f %f\n", mid[0], mid[1], mid[2]);}count++;}// add the occupant centerGetVectorForKey(node->occupant, "origin", mid);fprintf(linefile, "%f %f %f\n", mid[0], mid[1], mid[2]);qprintf("%5i point linefile\n", count + 1);fclose(linefile);}
/*===========================================================================Copyright (C) 1997-2006 Id Software, Inc.This program is free software; you can redistribute it and/or modifyit under the terms of the GNU General Public License as published bythe 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 ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See theGNU General Public License for more details.You should have received a copy of the GNU General Public License alongwith this program; if not, write to the Free Software Foundation, Inc.,51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.===========================================================================*/#include "qbsp.h"/*some faces will be removed before saving, but still form nodes:the insides of sky volumesmeeting planes of different water current volumes*/#define INTEGRAL_EPSILON 0.01// qb: was 0.5. fix by tapir to 0.1 via jdolan on insideqc.//qb: update- 0.1 causes gaps. Possibly just needs to be slightly less than OFF_EPSILON#define POINT_EPSILON 0.49#define OFF_EPSILON 0.5extern brush_texture_t side_brushtextures[MAX_MAP_SIDES]; // qb: kmbsp3: caulkint32_t c_merge;int32_t c_subdivide;int32_t c_totalverts;int32_t c_uniqueverts;int32_t c_degenerate;int32_t c_tjunctions;int32_t c_faceoverflows;int32_t c_facecollapse;int32_t c_badstartverts;#define MAX_SUPERVERTS 512int32_t superverts[MAX_SUPERVERTS];int32_t numsuperverts;face_t *edgefaces[MAX_MAP_EDGES_QBSP][2];int32_t firstmodeledge = 1;int32_t firstmodelface;int32_t c_tryedges;vec3_t edge_dir;vec3_t edge_start;vec_t edge_len;int32_t num_edge_verts;int32_t edge_verts[MAX_MAP_VERTS_QBSP];float subdivide_size = 240;float sublight_size = 240;face_t *NewFaceFromFace(face_t *f);//===========================================================================typedef struct hashvert_s {struct hashvert_s *next;int32_t num;} hashvert_t;#define HASH_SIZE MAX_POINTS_HASH // qb: per kmbsp3. Was 64int32_t vertexchain[MAX_MAP_VERTS_QBSP]; // the next vertex in a hash chainint32_t hashverts[HASH_SIZE * HASH_SIZE]; // a vertex number, or 0 for no verts//============================================================================unsigned HashVec(vec3_t vec) {int32_t x, y;x = (max_bounds + (int32_t)(vec[0] + 0.5)) >> 7;y = (max_bounds + (int32_t)(vec[1] + 0.5)) >> 7;if (x < 0 || x >= HASH_SIZE || y < 0 || y >= HASH_SIZE)Error("HashVec: point outside valid range");return y * HASH_SIZE + x;}vec_t g_min_vertex_diff_sq = BOGUS_RANGE; // jitdebugvec3_t g_min_vertex_pos; // jitdebug/*=============GetVertexUses hashing=============*/int32_t GetVertexnum(vec3_t in) {int32_t h;int32_t i;float *p;vec3_t vert;int32_t vnum;c_totalverts++;for (i = 0; i < 3; i++) {if (fabs(in[i] - Q_rint(in[i])) < INTEGRAL_EPSILON)vert[i] = Q_rint(in[i]);elsevert[i] = in[i];}h = HashVec(vert);for (vnum = hashverts[h]; vnum; vnum = vertexchain[vnum]) {vec3_t diff;vec_t length_sq; // jitp = dvertexes[vnum].point;VectorSubtract(p, vert, diff); // jitlength_sq = VectorLengthSq(diff); // jitif (length_sq < POINT_EPSILON * POINT_EPSILON)return vnum;if (length_sq < g_min_vertex_diff_sq) // jitdebug{g_min_vertex_diff_sq = length_sq; // jitdebugVectorCopy(p, g_min_vertex_pos);}}// emit a vertexif (use_qbsp) {if (numvertexes == MAX_MAP_VERTS_QBSP)Error("MAX_MAP_VERTS_QBSP");} else if (numvertexes == MAX_MAP_VERTS)Error("MAX_MAP_VERTS");dvertexes[numvertexes].point[0] = vert[0];dvertexes[numvertexes].point[1] = vert[1];dvertexes[numvertexes].point[2] = vert[2];vertexchain[numvertexes] = hashverts[h];hashverts[h] = numvertexes;c_uniqueverts++;numvertexes++;return numvertexes - 1;}/*==================FaceFromSupervertsThe faces vertexes have beeb added to the superverts[] array,and there may be more there than can be held in a face (MAXEDGES).If less, the faces vertexnums[] will be filled in, otherwiseface will reference a tree of split[] faces until all of thevertexnums can be added.superverts[base] will become face->vertexnums[0], and the otherswill be circularly filled in.==================*/void FaceFromSuperverts(node_t *node, face_t *f, int32_t base) {face_t *newf;int32_t remaining;int32_t i;remaining = numsuperverts;while (remaining > MAXEDGES) {// must split into two faces, because of vertex overloadc_faceoverflows++;newf = f->split[0] = NewFaceFromFace(f);newf = f->split[0];newf->next = node->faces;node->faces = newf;newf->numpoints = MAXEDGES;for (i = 0; i < MAXEDGES; i++)newf->vertexnums[i] = superverts[(i + base) % numsuperverts];f->split[1] = NewFaceFromFace(f);f = f->split[1];f->next = node->faces;node->faces = f;remaining -= (MAXEDGES - 2);base = (base + MAXEDGES - 1) % numsuperverts;}// copy the vertexes back to the facef->numpoints = remaining;for (i = 0; i < remaining; i++)f->vertexnums[i] = superverts[(i + base) % numsuperverts];}/*==================EmitFaceVertexes==================*/void EmitFaceVertexes(node_t *node, face_t *f) {winding_t *w;int32_t i;if (f->merged || f->split[0] || f->split[1])return;w = f->w;for (i = 0; i < w->numpoints; i++) {if (noweld) {// make every point uniqueif (use_qbsp) {if (numvertexes == MAX_MAP_VERTS_QBSP)Error("MAX_MAP_VERTS_QBSP");} else if (numvertexes == MAX_MAP_VERTS)Error("MAX_MAP_VERTS");superverts[i] = numvertexes;VectorCopy(w->p[i], dvertexes[numvertexes].point);numvertexes++;c_uniqueverts++;c_totalverts++;} elsesuperverts[i] = GetVertexnum(w->p[i]);}numsuperverts = w->numpoints;// this may fragment the face if > MAXEDGESFaceFromSuperverts(node, f, 0);}/*==================EmitVertexes_r==================*/void EmitVertexes_r(node_t *node) {int32_t i;face_t *f;if (node->planenum == PLANENUM_LEAF)return;for (f = node->faces; f; f = f->next) {EmitFaceVertexes(node, f);}for (i = 0; i < 2; i++)EmitVertexes_r(node->children[i]);}/*==========FindEdgeVertsUses the hash tables to cut down to a small number==========*/void FindEdgeVerts(vec3_t v1, vec3_t v2) {int32_t x1, x2, y1, y2, t;int32_t x, y;int32_t vnum;x1 = (max_bounds + (int32_t)(v1[0] + 0.5)) >> 7;y1 = (max_bounds + (int32_t)(v1[1] + 0.5)) >> 7;x2 = (max_bounds + (int32_t)(v2[0] + 0.5)) >> 7;y2 = (max_bounds + (int32_t)(v2[1] + 0.5)) >> 7;if (x1 > x2) {t = x1;x1 = x2;x2 = t;}if (y1 > y2) {t = y1;y1 = y2;y2 = t;}num_edge_verts = 0;for (x = x1; x <= x2; x++) {for (y = y1; y <= y2; y++) {for (vnum = hashverts[y * HASH_SIZE + x]; vnum; vnum = vertexchain[vnum]) {edge_verts[num_edge_verts++] = vnum;}}}}/*==========TestEdgeCan be recursively reentered==========*/void TestEdge(vec_t start, vec_t end, int32_t p1, int32_t p2, int32_t startvert) {int32_t j, k;vec_t dist;vec3_t delta;vec3_t exact;vec3_t off;vec_t error;vec3_t p;if (p1 == p2) {c_degenerate++;return; // degenerate edge}for (k = startvert; k < num_edge_verts; k++) {j = edge_verts[k];if (j == p1 || j == p2)continue;VectorCopy(dvertexes[j].point, p);VectorSubtract(p, edge_start, delta);dist = DotProduct(delta, edge_dir);if (dist <= start || dist >= end)continue; // off an endVectorMA(edge_start, dist, edge_dir, exact);VectorSubtract(p, exact, off);error = VectorLength(off);if (fabs(error) > OFF_EPSILON)continue; // not on the edge// break the edgec_tjunctions++;TestEdge(start, dist, p1, j, k + 1);TestEdge(dist, end, j, p2, k + 1);return;}// the edge p1 to p2 is now free of tjunctionsif (numsuperverts >= MAX_SUPERVERTS)Error("MAX_SUPERVERTS");superverts[numsuperverts] = p1;numsuperverts++;}/*==================FixFaceEdges==================*/void FixFaceEdges(node_t *node, face_t *f) {int32_t p1, p2;int32_t i;vec3_t e2;vec_t len;int32_t count[MAX_SUPERVERTS], start[MAX_SUPERVERTS];int32_t base;if (f->merged || f->split[0] || f->split[1])return;numsuperverts = 0;for (i = 0; i < f->numpoints; i++) {p1 = f->vertexnums[i];p2 = f->vertexnums[(i + 1) % f->numpoints];VectorCopy(dvertexes[p1].point, edge_start);VectorCopy(dvertexes[p2].point, e2);FindEdgeVerts(edge_start, e2);VectorSubtract(e2, edge_start, edge_dir);len = VectorNormalize(edge_dir, edge_dir);start[i] = numsuperverts;TestEdge(0, len, p1, p2, 0);count[i] = numsuperverts - start[i];}if (numsuperverts < 3) {// entire face collapsedf->numpoints = 0;c_facecollapse++;return;}// we want to pick a vertex that doesn't have tjunctions// on either side, which can cause artifacts on trifans,// especially underwaterfor (i = 0; i < f->numpoints; i++) {if (count[i] == 1 && count[(i + f->numpoints - 1) % f->numpoints] == 1)break;}if (i == f->numpoints) {f->badstartvert = true;c_badstartverts++;base = 0;} else {// rotate the vertex orderbase = start[i];}// this may fragment the face if > MAXEDGESFaceFromSuperverts(node, f, base);}/*==================FixEdges_r==================*/void FixEdges_r(node_t *node) {int32_t i;face_t *f;if (node->planenum == PLANENUM_LEAF)return;for (f = node->faces; f; f = f->next)FixFaceEdges(node, f);for (i = 0; i < 2; i++)FixEdges_r(node->children[i]);}/*===========FixTjuncs===========*/void FixTjuncs(node_t *headnode) {// snap and merge all vertexesqprintf("---- snap verts ----\n");memset(hashverts, 0, sizeof(hashverts));c_totalverts = 0;c_uniqueverts = 0;c_faceoverflows = 0;EmitVertexes_r(headnode);qprintf("%i unique from %i\n", c_uniqueverts, c_totalverts);// break edges on tjunctionsqprintf("---- tjunc ----\n");c_tryedges = 0;c_degenerate = 0;c_facecollapse = 0;c_tjunctions = 0;if (!notjunc)FixEdges_r(headnode);qprintf("%5i edges degenerated\n", c_degenerate);qprintf("%5i faces degenerated\n", c_facecollapse);qprintf("%5i edges added by tjunctions\n", c_tjunctions);qprintf("%5i faces added by tjunctions\n", c_faceoverflows);qprintf("%5i bad start verts\n", c_badstartverts);}//========================================================int32_t c_faces;face_t *AllocFace(void) {face_t *f;f = malloc(sizeof(face_t));memset(f, 0, sizeof(face_t));c_faces++;return f;}face_t *NewFaceFromFace(face_t *f) {face_t *newf;newf = AllocFace();*newf = *f;newf->merged = NULL;newf->split[0] = newf->split[1] = NULL;newf->w = NULL;return newf;}void FreeFace(face_t *f) {if (f->w)FreeWinding(f->w);free(f);c_faces--;}//========================================================/*==================GetEdgeCalled by writebsp.Don't allow four way edges==================*/int32_t GetEdge(int32_t v1, int32_t v2, face_t *f) {int32_t i;c_tryedges++;if (!noshare) {if (use_qbsp) {dedge_tx *edge;for (i = firstmodeledge; i < numedges; i++) {edge = &dedgesX[i];if (v1 == edge->v[1] && v2 == edge->v[0] && edgefaces[i][0]->contents == f->contents) {if (edgefaces[i][1])// printf ("WARNING: multiple backward edge\n");continue;edgefaces[i][1] = f;return -i;}}// emit an edgeif (numedges >= MAX_MAP_EDGES_QBSP)Error("numedges == MAX_MAP_EDGES_QBSP");edge = &dedgesX[numedges];numedges++;edge->v[0] = v1;edge->v[1] = v2;edgefaces[numedges - 1][0] = f;} else {dedge_t *edge;for (i = firstmodeledge; i < numedges; i++) {edge = &dedges[i];if (v1 == edge->v[1] && v2 == edge->v[0] && edgefaces[i][0]->contents == f->contents) {if (edgefaces[i][1])// printf ("WARNING: multiple backward edge\n");continue;edgefaces[i][1] = f;return -i;}}// emit an edgeif (numedges >= MAX_MAP_EDGES)Error("numedges == MAX_MAP_EDGES");edge = &dedges[numedges];numedges++;edge->v[0] = v1;edge->v[1] = v2;edgefaces[numedges - 1][0] = f;}}return numedges - 1;}/*===========================================================================FACE MERGING===========================================================================*/#define CONTINUOUS_EPSILON 0.001/*=============TryMergeWindingIf two polygons share a common edge and the edges that meet at thecommon points are both inside the other polygons, merge themReturns NULL if the faces couldn't be merged, or the new face.The originals will NOT be freed.=============*/winding_t *TryMergeWinding(winding_t *f1, winding_t *f2, vec3_t planenormal) {vec_t *p1, *p2, *p3, *p4, *back;winding_t *newf;int32_t i, j, k, l;vec3_t normal, delta;vec_t dot;qboolean keep1, keep2;//// find a common edge//p1 = p2 = NULL; // stop compiler warningj = 0; //for (i = 0; i < f1->numpoints; i++) {p1 = f1->p[i];p2 = f1->p[(i + 1) % f1->numpoints];for (j = 0; j < f2->numpoints; j++) {p3 = f2->p[j];p4 = f2->p[(j + 1) % f2->numpoints];for (k = 0; k < 3; k++) {if (fabs(p1[k] - p4[k]) > EQUAL_EPSILON)break;if (fabs(p2[k] - p3[k]) > EQUAL_EPSILON)break;}if (k == 3)break;}if (j < f2->numpoints)break;}if (i == f1->numpoints)return NULL; // no matching edges//// check slope of connected lines// if the slopes are colinear, the point can be removed//back = f1->p[(i + f1->numpoints - 1) % f1->numpoints];VectorSubtract(p1, back, delta);CrossProduct(planenormal, delta, normal);VectorNormalize(normal, normal);back = f2->p[(j + 2) % f2->numpoints];VectorSubtract(back, p1, delta);dot = DotProduct(delta, normal);if (dot > CONTINUOUS_EPSILON)return NULL; // not a convex polygonkeep1 = (qboolean)(dot < -CONTINUOUS_EPSILON);back = f1->p[(i + 2) % f1->numpoints];VectorSubtract(back, p2, delta);CrossProduct(planenormal, delta, normal);VectorNormalize(normal, normal);back = f2->p[(j + f2->numpoints - 1) % f2->numpoints];VectorSubtract(back, p2, delta);dot = DotProduct(delta, normal);if (dot > CONTINUOUS_EPSILON)return NULL; // not a convex polygonkeep2 = (qboolean)(dot < -CONTINUOUS_EPSILON);//// build the new polygon//newf = AllocWinding(f1->numpoints + f2->numpoints);// copy first polygonfor (k = (i + 1) % f1->numpoints; k != i; k = (k + 1) % f1->numpoints) {if (k == (i + 1) % f1->numpoints && !keep2)continue;VectorCopy(f1->p[k], newf->p[newf->numpoints]);newf->numpoints++;}// copy second polygonfor (l = (j + 1) % f2->numpoints; l != j; l = (l + 1) % f2->numpoints) {if (l == (j + 1) % f2->numpoints && !keep1)continue;VectorCopy(f2->p[l], newf->p[newf->numpoints]);newf->numpoints++;}return newf;}/*=============TryMergeIf two polygons share a common edge and the edges that meet at thecommon points are both inside the other polygons, merge themReturns NULL if the faces couldn't be merged, or the new face.The originals will NOT be freed.=============*/face_t *TryMerge(face_t *f1, face_t *f2, vec3_t planenormal) {face_t *newf;winding_t *nw;if (!f1->w || !f2->w)return NULL;if (f1->texinfo != f2->texinfo)return NULL;if (f1->planenum != f2->planenum) // on front and back sidesreturn NULL;if (f1->contents != f2->contents)return NULL;nw = TryMergeWinding(f1->w, f2->w, planenormal);if (!nw)return NULL;c_merge++;newf = NewFaceFromFace(f1);newf->w = nw;f1->merged = newf;f2->merged = newf;return newf;}/*===============MergeNodeFaces===============*/void MergeNodeFaces(node_t *node) {face_t *f1, *f2, *end;face_t *merged;plane_t *plane;merged = NULL;for (f1 = node->faces; f1; f1 = f1->next) {if (f1->merged || f1->split[0] || f1->split[1])continue;for (f2 = node->faces; f2 != f1; f2 = f2->next) {if (f2->merged || f2->split[0] || f2->split[1])continue;plane = &mapplanes[f1->planenum];merged = TryMerge(f1, f2, plane->normal);if (!merged)continue;// add merged to the end of the node face list// so it will be checked against all the faces againfor (end = node->faces; end->next; end = end->next);merged->next = NULL;end->next = merged;break;}}}//=====================================================================/*===============SubdivideFaceChop up faces that are larger than we want in the surface cache===============*/void SubdivideFace(node_t *node, face_t *f) {vec_t mins, maxs;vec_t v;int32_t axis, i;texinfo_t *tex;vec3_t temp;vec_t dist;winding_t *w, *frontw, *backw;if (f->merged)return;// special (non-surface cached) faces don't need subdivisiontex = &texinfo[f->texinfo];if (tex->flags & (SURF_WARP | SURF_SKY)) {return;}for (axis = 0; axis < 2; axis++) {while (1) {mins = BOGUS_RANGE;maxs = -BOGUS_RANGE;VectorCopy(tex->vecs[axis], temp);w = f->w;for (i = 0; i < w->numpoints; i++) {v = DotProduct(w->p[i], temp);if (v < mins)mins = v;if (v > maxs)maxs = v;}v = VectorNormalize(temp, temp);if (tex->flags & SURF_LIGHT) {if (maxs - mins <= sublight_size) // qb: control surf light chop independentlybreak;elsedist = (mins + sublight_size - 16) / v;} else {if (maxs - mins <= subdivide_size)break;elsedist = (mins + subdivide_size - 16) / v;}// split itc_subdivide++;ClipWindingEpsilon(w, temp, dist, ON_EPSILON, &frontw, &backw);if (!frontw || !backw)Error("SubdivideFace: didn't split the polygon\n Node Bounds: %g %g %g -> %g %g %g\n",node->mins[0], node->mins[1], node->mins[2], node->maxs[0], node->maxs[1], node->maxs[2]);f->split[0] = NewFaceFromFace(f);f->split[0]->w = frontw;f->split[0]->next = node->faces;node->faces = f->split[0];f->split[1] = NewFaceFromFace(f);f->split[1]->w = backw;f->split[1]->next = node->faces;node->faces = f->split[1];SubdivideFace(node, f->split[0]);SubdivideFace(node, f->split[1]);return;}}}void SubdivideNodeFaces(node_t *node) {face_t *f;for (f = node->faces; f; f = f->next) {SubdivideFace(node, f);}}//===========================================================================int32_t c_nodefaces;/*============FaceFromPortal============*/face_t *FaceFromPortal(portal_t *p, int32_t pside) {face_t *f;side_t *side;side = p->side;if (!side)return NULL; // portal does not bridge different visible contents// qb: Paril noted SURF_NODRAW was not processed anywhere, pulled here.// Turns out it was not implemented in original compiler tools or engine.// SURF_SKY textures are often also flagged nodraw but need to keep those.if (!strcmp(side_brushtextures[side - brushsides].name, "common/caulk") ||((side_brushtextures[side - brushsides].flags & SURF_NODRAW) && !(side_brushtextures[side - brushsides].flags & SURF_SKY)))return NULL;f = AllocFace();f->texinfo = side->texinfo;f->planenum = (side->planenum & ~1) | pside;f->portal = p;if ((p->nodes[pside]->contents & CONTENTS_WINDOW) && VisibleContents(p->nodes[!pside]->contents ^ p->nodes[pside]->contents) == CONTENTS_WINDOW)return NULL; // don't show insides of windowsif ((p->nodes[pside]->contents & CONTENTS_AUX) && VisibleContents(p->nodes[!pside]->contents ^ p->nodes[pside]->contents) == CONTENTS_AUX)return NULL; // qb: don't show insides of CONTENTS_AUXif (pside) {f->w = ReverseWinding(p->winding);f->contents = p->nodes[1]->contents;} else {f->w = CopyWinding(p->winding);f->contents = p->nodes[0]->contents;}return f;}/*===============MakeFaces_rIf a portal will make a visible face,mark the side that originally created itsolid / empty : solidsolid / water : solidwater / empty : waterwater / water : none===============*/void MakeFaces_r(node_t *node) {portal_t *p;int32_t s;// recurse down to leafsif (node->planenum != PLANENUM_LEAF) {MakeFaces_r(node->children[0]);MakeFaces_r(node->children[1]);// merge together all visible faces on the nodeif (!nomerge)MergeNodeFaces(node);if (!nosubdiv)SubdivideNodeFaces(node);return;}// solid leafs never have visible facesif (node->contents & CONTENTS_SOLID)return;// see which portals are validfor (p = node->portals; p; p = p->next[s]) {s = (p->nodes[1] == node);p->face[s] = FaceFromPortal(p, s);if (p->face[s]) {c_nodefaces++;p->face[s]->next = p->onnode->faces;p->onnode->faces = p->face[s];}}}/*============MakeFaces============*/void MakeFaces(node_t *node) {qprintf("--- MakeFaces ---\n");c_merge = 0;c_subdivide = 0;c_nodefaces = 0;MakeFaces_r(node);qprintf("%5i makefaces\n", c_nodefaces);qprintf("%5i merged\n", c_merge);qprintf("%5i subdivided\n", c_subdivide);}
/*===========================================================================Copyright (C) 1997-2006 Id Software, Inc.This program is free software; you can redistribute it and/or modifyit under the terms of the GNU General Public License as published bythe 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 ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See theGNU General Public License for more details.You should have received a copy of the GNU General Public License alongwith this program; if not, write to the Free Software Foundation, Inc.,51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.===========================================================================*/#include "qbsp.h"/*tag all brushes with original contentsbrushes may contain multiple contentsthere will be no brush overlap after csg phaseeach side has a count of the other sides it splitsthe best split will be the one that minimizes the total split countsof all remaining sidesprecalc side on plane tableevaluate split side{cost = 0for all sidesfor all sidesgetif side splits side and splitside is on same childcost++;}*/void SplitBrush2(bspbrush_t *brush, int32_t planenum,bspbrush_t **front, bspbrush_t **back) {SplitBrush(brush, planenum, front, back);#if 0if (*front && (*front)->sides[(*front)->numsides-1].texinfo == -1)(*front)->sides[(*front)->numsides-1].texinfo = (*front)->sides[0].texinfo; // not -1if (*back && (*back)->sides[(*back)->numsides-1].texinfo == -1)(*back)->sides[(*back)->numsides-1].texinfo = (*back)->sides[0].texinfo; // not -1#endif}/*===============SubtractBrushReturns a list of brushes that remain after B is subtracted from A.May by empty if A is contained inside B.The originals are undisturbed.===============*/bspbrush_t *SubtractBrush(bspbrush_t *a, bspbrush_t *b) {// a - b = out (list)int32_t i;bspbrush_t *front, *back;bspbrush_t *out, *in;in = a;out = NULL;for (i = 0; i < b->numsides && in; i++) {SplitBrush2(in, b->sides[i].planenum, &front, &back);if (in != a)FreeBrush(in);if (front) {// add to listfront->next = out;out = front;}in = back;}if (in)FreeBrush(in);else {// didn't really intersectFreeBrushList(out);return a;}return out;}/*===============IntersectBrushReturns a single brush made up by the intersection of thetwo provided brushes, or NULL if they are disjoint.The originals are undisturbed.===============*/bspbrush_t *IntersectBrush(bspbrush_t *a, bspbrush_t *b) {int32_t i;bspbrush_t *front, *back;bspbrush_t *in;in = a;for (i = 0; i < b->numsides && in; i++) {SplitBrush2(in, b->sides[i].planenum, &front, &back);if (in != a)FreeBrush(in);if (front)FreeBrush(front);in = back;}if (in == a)return NULL;in->next = NULL;return in;}/*===============BrushesDisjointReturns true if the two brushes definately do not intersect.There will be false negatives for some non-axial combinations.===============*/qboolean BrushesDisjoint(bspbrush_t *a, bspbrush_t *b) {int32_t i, j;// check bounding boxesfor (i = 0; i < 3; i++)if (a->mins[i] >= b->maxs[i] || a->maxs[i] <= b->mins[i])return true; // bounding boxes don't overlap// check for opposing planesfor (i = 0; i < a->numsides; i++) {for (j = 0; j < b->numsides; j++) {if (a->sides[i].planenum ==(b->sides[j].planenum ^ 1))return true; // opposite planes, so not touching}}return false; // might intersect}/*===============IntersectionContentsReturns a content word for the intersection of two brushes.Some combinations will generate a combination (water + clip),but most will be the stronger of the two contents.===============*/int32_t IntersectionContents(int32_t c1, int32_t c2) {int32_t out;out = c1 | c2;if (out & CONTENTS_SOLID)out = CONTENTS_SOLID;return out;}int32_t minplanenums[3];int32_t maxplanenums[3];/*===============ClipBrushToBoxAny planes shared with the box edge will be set to no texinfo===============*/bspbrush_t *ClipBrushToBox(bspbrush_t *brush, vec3_t clipmins, vec3_t clipmaxs) {int32_t i, j;bspbrush_t *front, *back;int32_t p;for (j = 0; j < 2; j++) {if (brush->maxs[j] > clipmaxs[j]) {SplitBrush(brush, maxplanenums[j], &front, &back);FreeBrush(brush); // AA tools fixif (front)FreeBrush(front);brush = back;if (!brush)return NULL;}if (brush->mins[j] < clipmins[j]) {SplitBrush(brush, minplanenums[j], &front, &back);FreeBrush(brush); // qb: AA tools fixif (back)FreeBrush(back);brush = front;if (!brush)return NULL;}}// remove any colinear facesfor (i = 0; i < brush->numsides; i++) {p = brush->sides[i].planenum & ~1;if (p == maxplanenums[0] || p == maxplanenums[1] || p == minplanenums[0] || p == minplanenums[1]) {brush->sides[i].texinfo = TEXINFO_NODE;brush->sides[i].visible = false;}}return brush;}/*===============MakeBspBrushList===============*/bspbrush_t *MakeBspBrushList(int32_t startbrush, int32_t endbrush,vec3_t clipmins, vec3_t clipmaxs) {mapbrush_t *mb;bspbrush_t *brushlist, *newbrush;int32_t i, j;int32_t c_faces;int32_t c_brushes;int32_t numsides;int32_t vis;vec3_t normal;vec_t dist; // jit (use higher precision, if enabled)for (i = 0; i < 2; i++) {VectorClear(normal);normal[i] = 1;dist = clipmaxs[i];maxplanenums[i] = FindFloatPlane(normal, dist, 0);dist = clipmins[i];minplanenums[i] = FindFloatPlane(normal, dist, 0);}brushlist = NULL;c_faces = 0;c_brushes = 0;for (i = startbrush; i < endbrush; i++) {mb = &mapbrushes[i];numsides = mb->numsides;if (!numsides)continue;// make sure the brush has at least one face showingvis = 0;for (j = 0; j < numsides; j++)if (mb->original_sides[j].visible && mb->original_sides[j].winding)vis++;#if 0if (!vis)continue; // no faces at all#endif// if the brush is outside the clip area, skip itfor (j = 0; j < 3; j++)if (mb->mins[j] >= clipmaxs[j] || mb->maxs[j] <= clipmins[j])break;if (j != 3)continue;//// make a copy of the brush//newbrush = AllocBrush(mb->numsides);newbrush->original = mb;newbrush->numsides = mb->numsides;memcpy(newbrush->sides, mb->original_sides, numsides * sizeof(side_t));for (j = 0; j < numsides; j++) {if (newbrush->sides[j].winding)newbrush->sides[j].winding = CopyWinding(newbrush->sides[j].winding);if (newbrush->sides[j].surf & SURF_HINT)newbrush->sides[j].visible = true; // hints are always visible}VectorCopy(mb->mins, newbrush->mins);VectorCopy(mb->maxs, newbrush->maxs);//// carve off anything outside the clip box//newbrush = ClipBrushToBox(newbrush, clipmins, clipmaxs);if (!newbrush)continue;c_faces += vis;c_brushes++;newbrush->next = brushlist;brushlist = newbrush;}return brushlist;}/*===============AddBspBrushListToTail===============*/bspbrush_t *AddBrushListToTail(bspbrush_t *list, bspbrush_t *tail) {bspbrush_t *walk, *next;for (walk = list; walk; walk = next) {// add to end of listnext = walk->next;walk->next = NULL;tail->next = walk;tail = walk;}return tail;}/*===========CullListBuilds a new list that doesn't hold the given brush===========*/bspbrush_t *CullList(bspbrush_t *list, bspbrush_t *skip1) {bspbrush_t *newlist;bspbrush_t *next;newlist = NULL;for (; list; list = next) {next = list->next;if (list == skip1) {FreeBrush(list);continue;}list->next = newlist;newlist = list;}return newlist;}/*==================WriteBrushMap==================*/void WriteBrushMap(char *name, bspbrush_t *list) {FILE *f;side_t *s;int32_t i;winding_t *w;if (use_qbsp)printf("\ntexinfo count: %i of %i maximum\n", numtexinfo, MAX_MAP_TEXINFO_QBSP);elseprintf("\ntexinfo count: %i of %i maximum\n", numtexinfo, MAX_MAP_TEXINFO);if (use_qbsp)printf("brushsides count: %i of %i maximum\n", nummapbrushsides, MAX_MAP_BRUSHSIDES_QBSP);elseprintf("brushsides count: %i of %i maximum\n", nummapbrushsides, MAX_MAP_BRUSHSIDES);printf("writing %s\n", name);f = fopen(name, "wb");if (!f)Error("Can't write %s\b", name);fprintf(f, "{\n\"classname\" \"worldspawn\"\n");for (; list; list = list->next) {fprintf(f, "{\n");for (i = 0, s = list->sides; i < list->numsides; i++, s++) {w = BaseWindingForPlane(mapplanes[s->planenum].normal, mapplanes[s->planenum].dist);fprintf(f, "( %i %i %i ) ", (int32_t)w->p[0][0], (int32_t)w->p[0][1], (int32_t)w->p[0][2]);fprintf(f, "( %i %i %i ) ", (int32_t)w->p[1][0], (int32_t)w->p[1][1], (int32_t)w->p[1][2]);fprintf(f, "( %i %i %i ) ", (int32_t)w->p[2][0], (int32_t)w->p[2][1], (int32_t)w->p[2][2]);fprintf(f, "%s 0 0 0 1 1\n", texinfo[s->texinfo].texture);FreeWinding(w);}fprintf(f, "}\n");}fprintf(f, "}\n");fclose(f);}/*==================BrushGEReturns true if b1 is allowed to bite b2==================*/qboolean BrushGE(bspbrush_t *b1, bspbrush_t *b2) {// detail brushes never bite structural brushesif ((b1->original->contents & CONTENTS_DETAIL) && !(b2->original->contents & CONTENTS_DETAIL))return false;if (b1->original->contents & CONTENTS_SOLID)return true;return false;}/*=================ChopBrushesCarves any intersecting solid brushes into the minimum numberof non-intersecting brushes.=================*/bspbrush_t *ChopBrushes(bspbrush_t *head) {bspbrush_t *b1, *b2, *next;bspbrush_t *tail;bspbrush_t *keep;bspbrush_t *sub, *sub2;int32_t c1, c2;qprintf("---- ChopBrushes ----\n");qprintf("original brushes: %i\n", CountBrushList(head));keep = NULL;newlist:// find tailif (!head)return NULL;for (tail = head; tail->next; tail = tail->next);for (b1 = head; b1; b1 = next) {next = b1->next;for (b2 = b1->next; b2; b2 = b2->next) {if (BrushesDisjoint(b1, b2))continue;sub = NULL;sub2 = NULL;c1 = BOGUS_RANGE;c2 = BOGUS_RANGE;if (BrushGE(b2, b1)) {sub = SubtractBrush(b1, b2);if (sub == b1)continue; // didn't really intersectif (!sub) {// b1 is swallowed by b2head = CullList(b1, b1);goto newlist;}c1 = CountBrushList(sub);}if (BrushGE(b1, b2)) {sub2 = SubtractBrush(b2, b1);if (sub2 == b2)continue; // didn't really intersectif (!sub2) {// b2 is swallowed by b1FreeBrushList(sub);head = CullList(b1, b2);goto newlist;}c2 = CountBrushList(sub2);}if (!sub && !sub2)continue; // neither one can bite// only accept if it didn't fragment// (commening this out allows full fragmentation)if (c1 > 1 && c2 > 1) {if (sub2)FreeBrushList(sub2);if (sub)FreeBrushList(sub);continue;}if (c1 < c2) {if (sub2)FreeBrushList(sub2);tail = AddBrushListToTail(sub, tail);head = CullList(b1, b1);goto newlist;} else {if (sub)FreeBrushList(sub);tail = AddBrushListToTail(sub2, tail);head = CullList(b1, b2);goto newlist;}}if (!b2) {// b1 is no longer intersecting anything, so keep itb1->next = keep;keep = b1;}}qprintf("output brushes: %i\n", CountBrushList(keep));return keep;}/*=================InitialBrushList=================*/bspbrush_t *InitialBrushList(bspbrush_t *list) {bspbrush_t *b;bspbrush_t *out, *newb;int32_t i;// only return brushes that have visible facesout = NULL;for (b = list; b; b = b->next) {#if 0for (i=0 ; i<b->numsides ; i++)if (b->sides[i].visible)break;if (i == b->numsides)continue;#endifnewb = CopyBrush(b);newb->next = out;out = newb;// clear visible, so it must be set by MarkVisibleFaces_r// to be used in the optimized listfor (i = 0; i < b->numsides; i++) {newb->sides[i].original = &b->sides[i];// newb->sides[i].visible = true;b->sides[i].visible = false;}}return out;}/*=================OptimizedBrushList=================*/bspbrush_t *OptimizedBrushList(bspbrush_t *list) {bspbrush_t *b;bspbrush_t *out, *newb;int32_t i;// only return brushes that have visible facesout = NULL;for (b = list; b; b = b->next) {for (i = 0; i < b->numsides; i++)if (b->sides[i].visible)break;if (i == b->numsides)continue;newb = CopyBrush(b);newb->next = out;out = newb;}return out;}
/*===========================================================================Copyright (C) 1997-2006 Id Software, Inc.This program is free software; you can redistribute it and/or modifyit under the terms of the GNU General Public License as published bythe 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 ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See theGNU General Public License for more details.You should have received a copy of the GNU General Public License alongwith this program; if not, write to the Free Software Foundation, Inc.,51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.===========================================================================*/#include "qbsp.h"int32_t c_nodes;int32_t c_nonvis;int32_t c_active_brushes;#define PSIDE_FRONT 1#define PSIDE_BACK 2#define PSIDE_BOTH (PSIDE_FRONT | PSIDE_BACK)#define PSIDE_FACING 4int32_t BrushMostlyOnSide(bspbrush_t *brush, plane_t *plane); // qb: GDD toolsvoid FindBrushInTree(node_t *node, int32_t brushnum) {bspbrush_t *b;if (node->planenum == PLANENUM_LEAF) {for (b = node->brushlist; b; b = b->next)if (b->original->brushnum == brushnum)return;}FindBrushInTree(node->children[0], brushnum);FindBrushInTree(node->children[1], brushnum);}//==================================================/*==================BoundBrushSets the mins/maxs based on the windings==================*/void BoundBrush(bspbrush_t *brush) {int32_t i, j;winding_t *w;ClearBounds(brush->mins, brush->maxs);for (i = 0; i < brush->numsides; i++) {w = brush->sides[i].winding;if (!w)continue;for (j = 0; j < w->numpoints; j++)AddPointToBounds(w->p[j], brush->mins, brush->maxs);}}/*==================CreateBrushWindings==================*/void CreateBrushWindings(bspbrush_t *brush) {int32_t i, j;winding_t *w;side_t *side;plane_t *plane;for (i = 0; i < brush->numsides; i++) {side = &brush->sides[i];plane = &mapplanes[side->planenum];w = BaseWindingForPlane(plane->normal, plane->dist);for (j = 0; j < brush->numsides && w; j++) {if (i == j)continue;if (brush->sides[j].bevel)continue;plane = &mapplanes[brush->sides[j].planenum ^ 1];ChopWindingInPlace(&w, plane->normal, plane->dist, 0);}side->winding = w;}BoundBrush(brush);}/*==================BrushFromBoundsCreates a new axial brush==================*/bspbrush_t *BrushFromBounds(vec3_t mins, vec3_t maxs) {bspbrush_t *b;int32_t i;vec3_t normal;vec_t dist;b = AllocBrush(6);b->numsides = 6;for (i = 0; i < 3; i++) {VectorClear(normal);normal[i] = 1;dist = maxs[i];b->sides[i].planenum = FindFloatPlane(normal, dist, 0);normal[i] = -1;dist = -mins[i];b->sides[3 + i].planenum = FindFloatPlane(normal, dist, 0);}CreateBrushWindings(b);return b;}/*==================BrushVolume==================*/vec_t BrushVolume(bspbrush_t *brush) {int32_t i;winding_t *w;vec3_t corner;vec_t d, area, volume;plane_t *plane;if (!brush)return 0;// grab the first valid point as the cornerw = NULL;for (i = 0; i < brush->numsides; i++) {w = brush->sides[i].winding;if (w)break;}if (!w)return 0;VectorCopy(w->p[0], corner);// make tetrahedrons to all other facesvolume = 0;for (; i < brush->numsides; i++) {w = brush->sides[i].winding;if (!w)continue;plane = &mapplanes[brush->sides[i].planenum];d = -(DotProduct(corner, plane->normal) - plane->dist);area = WindingArea(w);volume += d * area;}volume /= 3;return volume;}/*================CountBrushList================*/int32_t CountBrushList(bspbrush_t *brushes) {int32_t c;c = 0;for (; brushes; brushes = brushes->next)c++;return c;}/*================AllocTree================*/tree_t *AllocTree(void) {tree_t *tree;tree = malloc(sizeof(*tree));memset(tree, 0, sizeof(*tree));ClearBounds(tree->mins, tree->maxs);return tree;}/*================AllocNode================*/node_t *AllocNode(void) {node_t *node;node = malloc(sizeof(*node));memset(node, 0, sizeof(*node));return node;}/*================AllocBrush================*/bspbrush_t *AllocBrush(int32_t numsides) {bspbrush_t *bb;int32_t c;c = (intptr_t) & (((bspbrush_t *)0)->sides[numsides]);bb = malloc(c);memset(bb, 0, c);if (numthreads == 1)c_active_brushes++;return bb;}/*================FreeBrush================*/void FreeBrush(bspbrush_t *brushes) {int32_t i;for (i = 0; i < brushes->numsides; i++)if (brushes->sides[i].winding)FreeWinding(brushes->sides[i].winding);free(brushes);if (numthreads == 1)c_active_brushes--;}/*================FreeBrushList================*/void FreeBrushList(bspbrush_t *brushes) {bspbrush_t *next;for (; brushes; brushes = next) {next = brushes->next;FreeBrush(brushes);}}/*==================CopyBrushDuplicates the brush, the sides, and the windings==================*/bspbrush_t *CopyBrush(bspbrush_t *brush) {bspbrush_t *newbrush;int32_t size;int32_t i;size = (intptr_t) & (((bspbrush_t *)0)->sides[brush->numsides]);newbrush = AllocBrush(brush->numsides);memcpy(newbrush, brush, size);for (i = 0; i < brush->numsides; i++) {if (brush->sides[i].winding)newbrush->sides[i].winding = CopyWinding(brush->sides[i].winding);}return newbrush;}/*==================PointInLeaf==================*/node_t *PointInLeaf(node_t *node, vec3_t point) {vec_t d;plane_t *plane;while (node->planenum != PLANENUM_LEAF) {plane = &mapplanes[node->planenum];d = DotProduct(point, plane->normal) - plane->dist;if (d > 0)node = node->children[0];elsenode = node->children[1];}return node;}//========================================================/*==============BoxOnPlaneSideReturns PSIDE_FRONT, PSIDE_BACK, or PSIDE_BOTH==============*/int32_t BoxOnPlaneSide(vec3_t mins, vec3_t maxs, plane_t *plane) {int32_t side;int32_t i;vec3_t corners[2];vec_t dist1, dist2;// axial planes are easyif (plane->type < 3) {side = 0;if (maxs[plane->type] > plane->dist + PLANESIDE_EPSILON)side |= PSIDE_FRONT;if (mins[plane->type] < plane->dist - PLANESIDE_EPSILON)side |= PSIDE_BACK;return side;}// create the proper leading and trailing verts for the boxfor (i = 0; i < 3; i++) {if (plane->normal[i] < 0) {corners[0][i] = mins[i];corners[1][i] = maxs[i];} else {corners[1][i] = mins[i];corners[0][i] = maxs[i];}}dist1 = DotProduct(plane->normal, corners[0]) - plane->dist;dist2 = DotProduct(plane->normal, corners[1]) - plane->dist;side = 0;if (dist1 >= PLANESIDE_EPSILON)side = PSIDE_FRONT;if (dist2 < PLANESIDE_EPSILON)side |= PSIDE_BACK;return side;}/*============QuickTestBrushToPlanenum============*/int32_t QuickTestBrushToPlanenum(bspbrush_t *brush, int32_t planenum, int32_t *numsplits) {int32_t i, num;plane_t *plane;int32_t s;*numsplits = 0;// if the brush actually uses the planenum,// we can tell the side for surefor (i = 0; i < brush->numsides; i++) {num = brush->sides[i].planenum;if (num >= MAX_MAP_PLANES_QBSP)Error("bad planenum");if (num == planenum)return PSIDE_BACK | PSIDE_FACING;if (num == (planenum ^ 1))return PSIDE_FRONT | PSIDE_FACING;}// box on plane sideplane = &mapplanes[planenum];s = BoxOnPlaneSide(brush->mins, brush->maxs, plane);// if both sides, count the visible faces splitif (s == PSIDE_BOTH) {*numsplits += 3;}return s;}/*============TestBrushToPlanenum============*/// qb: GDD tools detailsplitint32_t TestBrushToPlanenum(bspbrush_t *brush, int32_t planenum,int32_t *numsplits, qboolean *hintsplit, qboolean *detailsplit, int32_t *epsilonbrush) {int32_t i, j, num;plane_t *plane;int32_t s;winding_t *w;vec_t d, d_front, d_back;int32_t front, back;*numsplits = 0;*hintsplit = false;// if the brush actually uses the planenum,// we can tell the side for surefor (i = 0; i < brush->numsides; i++) {num = brush->sides[i].planenum;if (num >= MAX_MAP_PLANES_QBSP)Error("bad planenum");if (num == planenum)return PSIDE_BACK | PSIDE_FACING;if (num == (planenum ^ 1))return PSIDE_FRONT | PSIDE_FACING;}// box on plane sideplane = &mapplanes[planenum];s = BoxOnPlaneSide(brush->mins, brush->maxs, plane);if (s != PSIDE_BOTH)return s;// if both sides, count the visible faces splitd_front = d_back = 0;for (i = 0; i < brush->numsides; i++) {if (brush->sides[i].texinfo == TEXINFO_NODE)continue; // on node, don't worry about splitsif (!brush->sides[i].visible)continue; // we don't care about non-visiblew = brush->sides[i].winding;if (!w)continue;front = back = 0;for (j = 0; j < w->numpoints; j++) {d = DotProduct(w->p[j], plane->normal) - plane->dist;if (d > d_front)d_front = d;if (d < d_back)d_back = d;if (d > ON_EPSILON) // qb: was 0.1front = 1;if (d < -ON_EPSILON) // qb: was -0.1back = 1;}if (front && back) {if (!(brush->sides[i].surf & SURF_SKIP)) {(*numsplits)++;if (brush->sides[i].surf & SURF_HINT)*hintsplit = true;if (brush->sides[i].contents & CONTENTS_DETAIL)*detailsplit = true;}}}if ((d_front > 0.0 && d_front < 1.0) || (d_back < 0.0 && d_back > -1.0))(*epsilonbrush)++;#if 0if (*numsplits == 0){// didn't really need to be splitif (front)s = PSIDE_FRONT;else if (back)s = PSIDE_BACK;elses = 0;}#endifreturn s;}//========================================================/*================WindingIsTinyReturns true if the winding would be crunched out ofexistance by the vertex snapping.================*/#define EDGE_LENGTH 0.2qboolean WindingIsTiny(winding_t *w) {#if 0if (WindingArea (w) < 1)return true;return false;#elseint32_t i, j;vec_t len;vec3_t delta;int32_t edges;edges = 0;for (i = 0; i < w->numpoints; i++) {j = i == w->numpoints - 1 ? 0 : i + 1;VectorSubtract(w->p[j], w->p[i], delta);len = VectorLength(delta);if (len > EDGE_LENGTH) {if (++edges == 3)return false;}}return true;#endif}/*================WindingIsHugeReturns true if the winding still has one of the pointsfrom basewinding for plane================*/qboolean WindingIsHuge(winding_t *w) {int32_t i, j;for (i = 0; i < w->numpoints; i++) {for (j = 0; j < 3; j++)if (w->p[i][j] < -2 * max_bounds || w->p[i][j] > 2 * max_bounds)return true;}return false;}//============================================================/*================Leafnode================*/void LeafNode(node_t *node, bspbrush_t *brushes) {bspbrush_t *b;int32_t i;node->planenum = PLANENUM_LEAF;node->contents = 0;for (b = brushes; b; b = b->next) {// if the brush is solid and all of its sides are on nodes,// it eats everythingif (b->original->contents & CONTENTS_SOLID) {for (i = 0; i < b->numsides; i++)if (b->sides[i].texinfo != TEXINFO_NODE)break;if (i == b->numsides) {node->contents = CONTENTS_SOLID;break;}}node->contents |= b->original->contents;}node->brushlist = brushes;}//============================================================// qb: GDD tools: brush info on errorvoid CheckPlaneAgainstParents(int32_t pnum, node_t *node, bspbrush_t *brush) {node_t *p;for (p = node->parent; p; p = p->parent) {if (p->planenum == pnum) {Error("Tried parent\n Brush Bounds: %g %g %g -> %g %g %g\n",brush->mins[0], brush->mins[1], brush->mins[2], brush->maxs[0], brush->maxs[1], brush->maxs[2]);}}}qboolean CheckPlaneAgainstVolume(int32_t pnum, node_t *node) {bspbrush_t *front, *back;qboolean good;SplitBrush(node->volume, pnum, &front, &back);good = (front && back);if (front)FreeBrush(front);if (back)FreeBrush(back);return good;}/*================SelectSplitSideUsing a hueristic, choses one of the sides out of the brushlistto partition the brushes with.Returns NULL if there are no valid planes to split with..================*/side_t *SelectSplitSide(bspbrush_t *brushes, node_t *node) {int32_t value, bestvalue;bspbrush_t *brush, *test;side_t *side, *bestside;int32_t i, j, pass, numpasses;int32_t pnum;int32_t s;int32_t front, back, both, facing, splits;int32_t bsplits;int32_t epsilonbrush;qboolean hintsplit, detailsplit;bestside = NULL;bestvalue = -BOGUS_RANGE;// the search order goes: visible-structural, visible-detail,// nonvisible-structural, nonvisible-detail.// If any valid plane is available in a pass, no further// passes will be tried.numpasses = 4;for (pass = 0; pass < numpasses; pass++) {for (brush = brushes; brush; brush = brush->next) {if ((pass & 1) && !(brush->original->contents & CONTENTS_DETAIL))continue;if (!(pass & 1) && (brush->original->contents & CONTENTS_DETAIL))continue;for (i = 0; i < brush->numsides; i++) {side = brush->sides + i;if (side->bevel)continue; // never use a bevel as a spliterif (!side->winding)continue; // nothing visible, so it can't splitif (side->texinfo == TEXINFO_NODE)continue; // allready a node splitterif (side->tested)continue; // we allready have metrics for this planeif (side->surf & SURF_SKIP)continue; // skip surfaces are never chosenif (side->visible ^ (pass < 2))continue; // only check visible faces on first passpnum = side->planenum;pnum &= ~1; // allways use positive facing planeCheckPlaneAgainstParents(pnum, node, brush);if (!CheckPlaneAgainstVolume(pnum, node))continue; // would produce a tiny volumefront = 0;back = 0;both = 0;facing = 0;splits = 0;epsilonbrush = 0;for (test = brushes; test; test = test->next) {s = TestBrushToPlanenum(test, pnum, &bsplits, &hintsplit, &detailsplit, &epsilonbrush);splits += bsplits;if (bsplits && (s & PSIDE_FACING))Error("PSIDE_FACING with splits");test->testside = s;// if the brush shares this face, don't bother// testing that facenum as a splitter againif (s & PSIDE_FACING) {facing++;for (j = 0; j < test->numsides; j++) {if ((test->sides[j].planenum & ~1) == pnum)test->sides[j].tested = true;}}if (s & PSIDE_FRONT)front++;if (s & PSIDE_BACK)back++;if (s == PSIDE_BOTH)both++;}// give a value estimate for using this planevalue = 5 * facing - 5 * splits - abs(front - back);// value = -5*splits;// value = 5*facing - 5*splits;if (mapplanes[pnum].type < 3)value += 5; // axial is bettervalue -= epsilonbrush * 1000; // avoid!// never split a hint side except with another hintif ((hintsplit && !(side->surf & SURF_HINT)) && (!detailsplit || (side->contents & CONTENTS_DETAIL)))value = -BOGUS_RANGE;// save off the side test so we don't need// to recalculate it when we actually seperate// the brushesif (value > bestvalue) {bestvalue = value;bestside = side;for (test = brushes; test; test = test->next)test->side = test->testside;}}}// if we found a good plane, don't bother trying any// other passesif (bestside) {if (pass > 1) {if (numthreads == 1)c_nonvis++;}if (pass > 0)node->detail_seperator = true; // not needed for visbreak;}}//// clear all the tested flags we set//for (brush = brushes; brush; brush = brush->next) {for (i = 0; i < brush->numsides; i++)brush->sides[i].tested = false;}return bestside;}/*==================BrushMostlyOnSide==================*/int32_t BrushMostlyOnSide(bspbrush_t *brush, plane_t *plane) {int32_t i, j;winding_t *w;vec_t d, max;int32_t side;max = 0;side = PSIDE_FRONT;for (i = 0; i < brush->numsides; i++) {w = brush->sides[i].winding;if (!w)continue;for (j = 0; j < w->numpoints; j++) {d = DotProduct(w->p[j], plane->normal) - plane->dist;if (d > max) {max = d;side = PSIDE_FRONT;}if (-d > max) {max = -d;side = PSIDE_BACK;}}}return side;}/*================SplitBrushGenerates two new brushes, leaving the originalunchanged================*/void SplitBrush(bspbrush_t *brush, int32_t planenum,bspbrush_t **front, bspbrush_t **back) {bspbrush_t *b[2];int32_t i, j;winding_t *w, *cw[2], *midwinding;plane_t *plane, *plane2;side_t *s, *cs;vec_t d, d_front, d_back;*front = *back = NULL;plane = &mapplanes[planenum];// check all pointsd_front = d_back = 0;for (i = 0; i < brush->numsides; i++) {w = brush->sides[i].winding;if (!w)continue;for (j = 0; j < w->numpoints; j++) {d = DotProduct(w->p[j], plane->normal) - plane->dist;if (d > 0 && d > d_front)d_front = d;if (d < 0 && d < d_back)d_back = d;}}// If the brush only overaps the plane by .1 units, don't bother splitting it.if (d_front < ON_EPSILON) // qb: was 0.1{// only on back*back = CopyBrush(brush);return;}if (d_back > -ON_EPSILON) // qb: was -0.1{// only on front*front = CopyBrush(brush);return;}// create a new winding from the split planew = BaseWindingForPlane(plane->normal, plane->dist);for (i = 0; i < brush->numsides && w; i++) {plane2 = &mapplanes[brush->sides[i].planenum ^ 1];ChopWindingInPlace(&w, plane2->normal, plane2->dist, 0); // PLANESIDE_EPSILON);}if (!w || WindingIsTiny(w)) {// the brush isn't really splitint32_t side;side = BrushMostlyOnSide(brush, plane);if (side == PSIDE_FRONT)*front = CopyBrush(brush);if (side == PSIDE_BACK)*back = CopyBrush(brush);return;}if (WindingIsHuge(w)) {qprintf("WARNING: huge winding\n");}midwinding = w;// split it for realfor (i = 0; i < 2; i++) {b[i] = AllocBrush(brush->numsides + 1);b[i]->original = brush->original;}// split all the current windingsfor (i = 0; i < brush->numsides; i++) {s = &brush->sides[i];w = s->winding;if (!w)continue;ClipWindingEpsilon(w, plane->normal, plane->dist,0 /*PLANESIDE_EPSILON*/, &cw[0], &cw[1]); // qb:reenabling leaves gaps between some planesfor (j = 0; j < 2; j++) {if (!cw[j])continue;#if 0if (WindingIsTiny (cw[j])){FreeWinding (cw[j]);continue;}#endifcs = &b[j]->sides[b[j]->numsides];b[j]->numsides++;*cs = *s;// cs->planenum = s->planenum;// cs->texinfo = s->texinfo;// cs->visible = s->visible;// cs->original = s->original;cs->winding = cw[j];cs->tested = false;}}// see if we have valid polygons on both sidesfor (i = 0; i < 2; i++) {BoundBrush(b[i]);for (j = 0; j < 3; j++) {if (b[i]->mins[j] < -max_bounds || b[i]->maxs[j] > max_bounds) {qprintf("bogus brush after clip\n");break;}}if (b[i]->numsides < 3 || j < 3) {FreeBrush(b[i]);b[i] = NULL;}}if (!(b[0] && b[1])) {if (!b[0] && !b[1])qprintf("split removed brush\n");elseqprintf("split not on both sides\n");if (b[0]) {FreeBrush(b[0]);*front = CopyBrush(brush);}if (b[1]) {FreeBrush(b[1]);*back = CopyBrush(brush);}return;}// add the midwinding to both sidesfor (i = 0; i < 2; i++) {cs = &b[i]->sides[b[i]->numsides];b[i]->numsides++;cs->planenum = planenum ^ i ^ 1;cs->texinfo = TEXINFO_NODE;cs->visible = false;cs->tested = false;if (i == 0)cs->winding = CopyWinding(midwinding);elsecs->winding = midwinding;}{vec_t v1;int32_t i;for (i = 0; i < 2; i++) {v1 = BrushVolume(b[i]);if (v1 < microvolume) // jit - allow for smaller brush volumes{FreeBrush(b[i]);b[i] = NULL;// qprintf ("tiny volume after clip\n");}}}*front = b[0];*back = b[1];}/*================SplitBrushList================*/void SplitBrushList(bspbrush_t *brushes,node_t *node, bspbrush_t **front, bspbrush_t **back) {bspbrush_t *brush, *newbrush, *newbrush2;side_t *side;int32_t sides;int32_t i;*front = *back = NULL;for (brush = brushes; brush; brush = brush->next) {sides = brush->side;if (sides == PSIDE_BOTH) {// split into two brushesSplitBrush(brush, node->planenum, &newbrush, &newbrush2);if (newbrush) {newbrush->next = *front;*front = newbrush;}if (newbrush2) {newbrush2->next = *back;*back = newbrush2;}continue;}newbrush = CopyBrush(brush);// if the planenum is actualy a part of the brush// find the plane and flag it as used so it won't be tried// as a splitter againif (sides & PSIDE_FACING) {for (i = 0; i < newbrush->numsides; i++) {side = newbrush->sides + i;if ((side->planenum & ~1) == node->planenum)side->texinfo = TEXINFO_NODE;}}if (sides & PSIDE_FRONT) {newbrush->next = *front;*front = newbrush;continue;}if (sides & PSIDE_BACK) {newbrush->next = *back;*back = newbrush;continue;}}}/*================BuildTree_r================*/node_t *BuildTree_r(node_t *node, bspbrush_t *brushes) {node_t *newnode;side_t *bestside;int32_t i;bspbrush_t *children[2];if (numthreads == 1)c_nodes++;// find the best plane to use as a splitterbestside = SelectSplitSide(brushes, node);if (!bestside) {// leaf nodenode->side = NULL;node->planenum = -1;LeafNode(node, brushes);return node;}// this is a splitplane nodenode->side = bestside;node->planenum = bestside->planenum & ~1; // always use front facingSplitBrushList(brushes, node, &children[0], &children[1]);FreeBrushList(brushes);// allocate children before recursingfor (i = 0; i < 2; i++) {newnode = AllocNode();newnode->parent = node;node->children[i] = newnode;}SplitBrush(node->volume, node->planenum, &node->children[0]->volume,&node->children[1]->volume);// recursively process childrenfor (i = 0; i < 2; i++) {node->children[i] = BuildTree_r(node->children[i], children[i]);}return node;}//===========================================================/*=================BrushBSPThe incoming list will be freed before exiting=================*/tree_t *BrushBSP(bspbrush_t *brushlist, vec3_t mins, vec3_t maxs) {node_t *node;bspbrush_t *b;int32_t c_faces, c_nonvisfaces;int32_t c_brushes;tree_t *tree;int32_t i;vec_t volume;qprintf("--- BrushBSP ---\n");tree = AllocTree();c_faces = 0;c_nonvisfaces = 0;c_brushes = 0;for (b = brushlist; b; b = b->next) {c_brushes++;volume = BrushVolume(b);if (volume < microvolume) {printf("WARNING: Entity %i, Brush %i, Line %i: microbrush\n Bounds: %g %g %g -> %g %g %g\n",b->original->entitynum, b->original->brushnum, scriptline + 1, // qb: add scriptlineb->mins[0], b->mins[1], b->mins[2], b->maxs[0], b->maxs[1], b->maxs[2]);}for (i = 0; i < b->numsides; i++) {if (b->sides[i].bevel)continue;if (!b->sides[i].winding)continue;if (b->sides[i].texinfo == TEXINFO_NODE)continue;if (b->sides[i].visible)c_faces++;elsec_nonvisfaces++;}AddPointToBounds(b->mins, tree->mins, tree->maxs);AddPointToBounds(b->maxs, tree->mins, tree->maxs);}qprintf("%5i brushes\n", c_brushes);qprintf("%5i visible faces\n", c_faces);qprintf("%5i nonvisible faces\n", c_nonvisfaces);c_nodes = 0;c_nonvis = 0;node = AllocNode();node->volume = BrushFromBounds(mins, maxs);tree->headnode = node;node = BuildTree_r(node, brushlist);qprintf("%5i visible nodes\n", c_nodes / 2 - c_nonvis);qprintf("%5i nonvis nodes\n", c_nonvis);qprintf("%5i leafs\n", (c_nodes + 1) / 2);#if 0{// debug codestatic node_t *tnode;vec3_t p;p[0] = -1469;p[1] = -118;p[2] = 119;tnode = PointInLeaf (tree->headnode, p);printf ("contents: %i\n", tnode->contents);p[0] = 0;}#endifreturn tree;}
#include "qbsp.h"extern char name[1024];extern qboolean origfix;extern qboolean nocsg;extern qboolean onlyents;extern qboolean leaktest;extern int32_t block_size;extern float subdivide_size;extern float sublight_size;extern int32_t block_xl;extern int32_t block_yl;extern int32_t block_xh;extern int32_t block_yh;void BSP_ProcessArgument(const char * arg) ;int32_t main(int32_t argc, char **argv) {int32_t i;char tgamedir[1024] = "", tbasedir[1024] = "", tmoddir[1024] = "";printf("\n\n<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< 4bsp >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n");printf("BSP compiler build " __DATE__ "\n");for (i = 1; i < argc; i++) {if (!strcmp(argv[i], "-noorigfix")) {printf("origfix = false\n");origfix = false;} else if (!strcmp(argv[i], "-v")) {printf("verbose = true\n");verbose = true;} else if (!strcmp(argv[i], "-help")) {printf("4bsp supporting v38 and v220 map formats plus QBSP extended limits.\n""Usage: 4bsp [options] [mapname]\n\n"" -chop #: Subdivide size.\n"" Default: 240 Range: 32-1024\n"" -choplight #: Subdivide size for surface lights.\n"" Default: 240 Range: 32-1024\n"" -largebounds: Increase max map size for supporting engines.\n"" -micro #: Minimum microbrush size. Default: 0.02\n"" Suggested range: 0.02 - 1.0\n"" -nosubdiv: Disable subdivision.\n"" -qbsp: Greatly expanded map and entity limits for supporting engines.\n"" -moddir [path]: Set a mod directory. Default is parent dir of map file.\n"" -basedir [path]: Set the directory for assets not in moddir. Default is moddir.\n"" -gamedir [path]: Set game directory, the folder with game executable.\n"//" -threads #: number of CPU threads to use\n""Debugging tools:\n"" -block # #: Division tree block size, square\n"" -blocks # # # #: Div tree block size, rectangular\n"" -blocksize: map cube size for processing. Default: 1024\n"" -fulldetail: Change most brushes to detail.\n"" -leaktest: Perform leak test only.\n"" -nocsg: No constructive solid geometry.\n"" -nodetail: No detail brushes.\n"" -nomerge: Don't merge visible faces per node.\n"" -noorigfix: Disable texture fix for origin offsets.\n"" -noprune: Disable node pruning.\n"" -noshare: Don't look for shared edges on save.\n"" -noskipfix: Do not automatically set skip contents to zero.\n"" -notjunc: Disable edge cleanup.\n"" -nowater: Ignore warp surfaces.\n"" -noweld: Disable vertex welding.\n"" -onlyents: Grab the entites and resave.\n"" -v: Display more verbose output.\n""<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< 4bsp HELP >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n\n");exit(1);}/*if (!strcmp(argv[i],"-threads")){numthreads = atoi (argv[i+1]);i++;}*/else if (!strcmp(argv[i], "-noweld")) {printf("noweld = true\n");noweld = true;} else if (!strcmp(argv[i], "-nocsg")) {printf("nocsg = true\n");nocsg = true;} else if (!strcmp(argv[i], "-noshare")) {printf("noshare = true\n");noshare = true;} else if (!strcmp(argv[i], "-notjunc")) {printf("notjunc = true\n");notjunc = true;} else if (!strcmp(argv[i], "-nowater")) {printf("nowater = true\n");nowater = true;} else if (!strcmp(argv[i], "-noprune")) {printf("noprune = true\n");noprune = true;} else if (!strcmp(argv[i], "-nomerge")) {printf("nomerge = true\n");nomerge = true;} else if (!strcmp(argv[i], "-nosubdiv")) {printf("nosubdiv = true\n");nosubdiv = true;} else if (!strcmp(argv[i], "-nodetail")) {printf("nodetail = true\n");nodetail = true;} else if (!strcmp(argv[i], "-fulldetail")) {printf("fulldetail = true\n");fulldetail = true;} else if (!strcmp(argv[i], "-onlyents")) {printf("onlyents = true\n");onlyents = true;} else if (!strcmp(argv[i], "-micro")) {microvolume = atof(argv[i + 1]);i++;} else if (!strcmp(argv[i], "-leaktest")) {printf("leaktest = true\n");leaktest = true;}// qb: qbspelse if (!strcmp(argv[i], "-qbsp")) {printf("use_qbsp = true\n");use_qbsp = true;max_entities = MAX_MAP_ENTITIES_QBSP;max_bounds = MAX_MAP_SIZE;block_size = MAX_BLOCK_SIZE; // qb: otherwise limits map range} else if (!strcmp(argv[i], "-noskipfix")) {printf("noskipfix = true\n");noskipfix = true;}// qb: from kmqbsp3- Knightmare addedelse if (!strcmp(argv[i], "-largebounds") || !strcmp(argv[i], "-lb")) {if (use_qbsp) {printf("[-largebounds is not required with -qbsp]\n");} else {max_bounds = MAX_MAP_SIZE;block_size = MAX_BLOCK_SIZE; // qb: otherwise limits map rangeprintf("largebounds: using max bound size of %i\n", MAX_MAP_SIZE);}}// qb: set gamedir, moddir, and basedirelse if (!strcmp(argv[i], "-gamedir")) {strcpy(tgamedir, argv[i + 1]);i++;} else if (!strcmp(argv[i], "-moddir")) {strcpy(tmoddir, argv[i + 1]);i++;} else if (!strcmp(argv[i], "-basedir")) {strcpy(tbasedir, argv[i + 1]);i++;}else if ((!strcmp(argv[i], "-chop")) || (!strcmp(argv[i], "-subdiv"))) {subdivide_size = atof(argv[i + 1]);if (subdivide_size < 32) {subdivide_size = 32;printf("subdivide_size set to minimum size: 32\n");}if (subdivide_size > 1024) {subdivide_size = 1024;printf("subdivide_size set to maximum size: 1024\n");}printf("subdivide_size = %f\n", subdivide_size);i++;} else if ((!strcmp(argv[i], "-choplight")) || (!strcmp(argv[i], "-choplights")) || (!strcmp(argv[i], "-subdivlight"))) // qb: chop surf lights independently{sublight_size = atof(argv[i + 1]);if (sublight_size < 32) {sublight_size = 32;printf("sublight_size set to minimum size: 32\n");}if (sublight_size > 1024) {sublight_size = 1024;printf("sublight_size set to maximum size: 1024\n");}printf("sublight_size = %f\n", sublight_size);i++;} else if (!strcmp(argv[i], "-blocksize")) {block_size = atof(argv[i + 1]);if (block_size < 128) {block_size = 128;printf("block_size set to minimum size: 128\n");}if (block_size > MAX_BLOCK_SIZE) {block_size = MAX_BLOCK_SIZE;printf("block_size set to minimum size: MAX_BLOCK_SIZE\n");}printf("blocksize: %i\n", block_size);i++;} else if (!strcmp(argv[i], "-block")) {block_xl = block_yl = atoi(argv[i + 1]); // qb: fixed... has it always been wrong? was xl = xh and yl = yhblock_xh = block_yh = atoi(argv[i + 2]);printf("block: %i,%i\n", block_xl, block_xh);i += 2;} else if (!strcmp(argv[i], "-blocks")) {block_xl = atoi(argv[i + 1]);block_yl = atoi(argv[i + 2]);block_xh = atoi(argv[i + 3]);block_yh = atoi(argv[i + 4]);printf("blocks: %i,%i to %i,%i\n",block_xl, block_yl, block_xh, block_yh);i += 4;} elsebreak;}if (i != argc - 1) {printf("Supporting v38 and v220 map formats plus QBSP extended limits.\n""Usage: 4bsp [options] [mapname]\n"" -chop # -choplight # -help\n"" -largebounds -micro # -nosubdiv\n"" -qbsp -gamedir -basedir\n""Debugging tools: -block # # -blocks # # # #\n"" -blocksize # -fulldetail -leaktest\n"" -nocsg -nodetail -nomerge\n"" -noorigfix -noprune -noshare\n"" -noskipfix -notjunc -nowater\n"" -noweld -onlyents -v (verbose)\n\n");exit(1);}ThreadSetDefault();// qb: below is from original source release. On Windows, multi threads cause false leak errors.numthreads = 1; // multiple threads aren't helping...SetQdirFromPath(argv[i]);if (strcmp(tmoddir, "")) {strcpy(moddir, tmoddir);Q_pathslash(moddir);strcpy(basedir, moddir);}if (strcmp(tbasedir, "")) {strcpy(basedir, tbasedir);Q_pathslash(basedir);if (!strcmp(tmoddir, ""))strcpy(moddir, basedir);}if (strcmp(tgamedir, "")) {strcpy(gamedir, tgamedir);Q_pathslash(gamedir);}printf("microvolume = %f\n\n", microvolume);// qb: display dirsprintf("moddir = %s\n", moddir);printf("basedir = %s\n", basedir);printf("gamedir = %s\n", gamedir);BSP_ProcessArgument(argv[i]);return 0;}
/*===========================================================================Copyright (C) 1997-2006 Id Software, Inc.This program is free software; you can redistribute it and/or modifyit under the terms of the GNU General Public License as published bythe 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 ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See theGNU General Public License for more details.You should have received a copy of the GNU General Public License alongwith this program; if not, write to the Free Software Foundation, Inc.,51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.===========================================================================*/#include "qbsp.h"extern float subdivide_size;extern float sublight_size;extern char source[1024];char name[1024];vec_t microvolume = 0.02f; // jit - was 1.0, but this messes up small brushesqboolean noprune = false;qboolean glview = false;qboolean nodetail = false;qboolean fulldetail = false;qboolean onlyents = false;qboolean nomerge = false;qboolean nowater = false;qboolean nocsg = false;qboolean noweld = false;qboolean noshare = false;qboolean nosubdiv = false;qboolean notjunc = false;qboolean leaktest = false;qboolean badnormal_check = false;qboolean origfix = true; // default to trueint32_t block_xl = -8, block_xh = 7, block_yl = -8, block_yh = 7;int32_t entity_num;int32_t max_entities = MAX_MAP_ENTITIES; // qb: from kmqbsp3- Knightmare- adjustable entity limitint32_t max_bounds = DEFAULT_MAP_SIZE; // Knightmare- adjustable max boundsint32_t block_size = 1024; // Knightmare- adjustable block sizenode_t *block_nodes[10][10];/*============BlockTree============*/node_t *BlockTree(int32_t xl, int32_t yl, int32_t xh, int32_t yh) {node_t *node;vec3_t normal;vec_t dist;int32_t mid;if (xl == xh && yl == yh) {node = block_nodes[xl + 5][yl + 5];if (!node) {// return an empty leafnode = AllocNode();node->planenum = PLANENUM_LEAF;node->contents = 0; // CONTENTS_SOLID;return node;}return node;}// create a seperator along the largest axisnode = AllocNode();if (xh - xl > yh - yl) {// split x axismid = xl + (xh - xl) / 2 + 1;normal[0] = 1;normal[1] = 0;normal[2] = 0;dist = mid * block_size;node->planenum = FindFloatPlane(normal, dist, 0);node->children[0] = BlockTree(mid, yl, xh, yh);node->children[1] = BlockTree(xl, yl, mid - 1, yh);} else {mid = yl + (yh - yl) / 2 + 1;normal[0] = 0;normal[1] = 1;normal[2] = 0;dist = mid * block_size;node->planenum = FindFloatPlane(normal, dist, 0);node->children[0] = BlockTree(xl, mid, xh, yh);node->children[1] = BlockTree(xl, yl, xh, mid - 1);}return node;}/*============ProcessBlock_Thread============*/int32_t brush_start, brush_end;void ProcessBlock_Thread(int32_t blocknum) {int32_t xblock, yblock;vec3_t mins, maxs;bspbrush_t *brushes;tree_t *tree;node_t *node;yblock = block_yl + blocknum / (block_xh - block_xl + 1);xblock = block_xl + blocknum % (block_xh - block_xl + 1);qprintf("############### block %2i,%2i ###############\n", xblock, yblock);mins[0] = xblock * block_size;mins[1] = yblock * block_size;mins[2] = -max_bounds; // was -4096maxs[0] = (xblock + 1) * block_size;maxs[1] = (yblock + 1) * block_size;maxs[2] = max_bounds; // was 4096// the makelist and chopbrushes could be cached between the passes...brushes = MakeBspBrushList(brush_start, brush_end, mins, maxs);if (!brushes) {node = AllocNode();node->planenum = PLANENUM_LEAF;node->contents = CONTENTS_SOLID;block_nodes[xblock + 5][yblock + 5] = node;return;}if (!nocsg)brushes = ChopBrushes(brushes);tree = BrushBSP(brushes, mins, maxs);block_nodes[xblock + 5][yblock + 5] = tree->headnode;}/*============ProcessWorldModel============*/void ProcessWorldModel(void) {entity_t *e;tree_t *tree;qboolean leaked;qboolean optimize;e = &entities[entity_num];brush_start = e->firstbrush;brush_end = brush_start + e->numbrushes;leaked = false;//// perform per-block operations//if (block_xh * block_size > map_maxs[0])block_xh = floor(map_maxs[0] / block_size);if ((block_xl + 1) * block_size < map_mins[0])block_xl = floor(map_mins[0] / block_size);if (block_yh * block_size > map_maxs[1])block_yh = floor(map_maxs[1] / block_size);if ((block_yl + 1) * block_size < map_mins[1])block_yl = floor(map_mins[1] / block_size);if (block_xl < -4)block_xl = -4;if (block_yl < -4)block_yl = -4;if (block_xh > 3)block_xh = 3;if (block_yh > 3)block_yh = 3;for (optimize = false; optimize <= true; optimize++) {qprintf("--------------------------------------------\n");RunThreadsOnIndividual((block_xh - block_xl + 1) * (block_yh - block_yl + 1),!verbose, ProcessBlock_Thread);//// build the division tree// oversizing the blocks guarantees that all the boundaries// will also get nodes.//qprintf("--------------------------------------------\n");tree = AllocTree();tree->headnode = BlockTree(block_xl - 1, block_yl - 1, block_xh + 1, block_yh + 1);tree->mins[0] = (block_xl)*block_size;tree->mins[1] = (block_yl)*block_size;tree->mins[2] = map_mins[2] - 8;tree->maxs[0] = (block_xh + 1) * block_size;tree->maxs[1] = (block_yh + 1) * block_size;tree->maxs[2] = map_maxs[2] + 8;//// perform the global operations//MakeTreePortals(tree);if (FloodEntities(tree))FillOutside(tree->headnode);else {printf("**** leaked ****\n");leaked = true;LeakFile(tree);if (leaktest) {printf("--- MAP LEAKED ---\n");exit(0);}}MarkVisibleSides(tree, brush_start, brush_end);if (leaked)break;if (!optimize) {FreeTree(tree);}}FloodAreas(tree);MakeFaces(tree->headnode);FixTjuncs(tree->headnode);if (!noprune)PruneNodes(tree->headnode);WriteBSP(tree->headnode);if (!leaked)WritePortalFile(tree);FreeTree(tree);}/*============ProcessSubModel============*/void ProcessSubModel(void) {entity_t *e;int32_t start, end;tree_t *tree;bspbrush_t *list;vec3_t mins, maxs;e = &entities[entity_num];start = e->firstbrush;end = start + e->numbrushes;mins[0] = mins[1] = mins[2] = -max_bounds;maxs[0] = maxs[1] = maxs[2] = max_bounds;list = MakeBspBrushList(start, end, mins, maxs);if (!nocsg)list = ChopBrushes(list);tree = BrushBSP(list, mins, maxs);MakeTreePortals(tree);MarkVisibleSides(tree, start, end);MakeFaces(tree->headnode);FixTjuncs(tree->headnode);WriteBSP(tree->headnode);FreeTree(tree);}/*============ProcessModels============*/void ProcessModels(void) {BeginBSPFile();for (entity_num = 0; entity_num < num_entities; entity_num++) {if (!entities[entity_num].numbrushes)continue;qprintf("############### model %i ###############\n", nummodels);BeginModel();if (entity_num == 0)ProcessWorldModel();elseProcessSubModel();EndModel();}EndBSPFile();}void BSP_ProcessArgument(const char * arg) {char path[2053];strcpy(source, ExpandArg(arg));StripExtension(source);// delete portal and line filessprintf(path, "%s.prt", source);remove(path);sprintf(path, "%s.pts", source);remove(path);strcpy(name, ExpandArg(arg));DefaultExtension(name, ".map"); // might be .regInitBSPFile();//// if onlyents, just grab the entites and resave//if (onlyents) {char out[2053];sprintf(out, "%s.bsp", source);LoadBSPFile(out);if (use_qbsp)printf("use_qbsp = true\n");num_entities = 0;LoadMapFile(name);SetModelNumbers();SetLightStyles();UnparseEntities();WriteBSPFile(out);} else {extern void ProcessModels();//// start from scratch//LoadMapFile(name);SetModelNumbers();SetLightStyles();ProcessModels();}PrintBSPFileSizes();printf("<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< END 4bsp >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n\n");}
per_platform executable ___-q2tools \--include-directory quake2/tools/common \--lflag -lm \quake2/tools/main.c \quake2/tools/4bsp/4bsp.c \quake2/tools/4bsp/brushbsp.c \quake2/tools/4bsp/csg.c \quake2/tools/4bsp/faces.c \quake2/tools/4bsp/leakfile.c \quake2/tools/4bsp/map.c \quake2/tools/4bsp/portals.c \quake2/tools/4bsp/prtfile.c \quake2/tools/4bsp/textures.c \quake2/tools/4bsp/tree.c \quake2/tools/4bsp/writebsp.c \quake2/tools/4rad/4rad.c \quake2/tools/4rad/lightmap.c \quake2/tools/4rad/patches.c \quake2/tools/4rad/trace.c \quake2/tools/4vis/4vis.c \quake2/tools/4vis/flow.c \quake2/tools/common/bspfile.c \quake2/tools/common/cmdlib.c \quake2/tools/common/l3dslib.c \quake2/tools/common/lbmlib.c \quake2/tools/common/llwolib.c \quake2/tools/common/mathlib.c \quake2/tools/common/mdfour.c \quake2/tools/common/polylib.c \quake2/tools/common/scriplib.c \quake2/tools/common/threads.c \quake2/tools/common/trilib.c#quake2/tools/4data/4data.c \#quake2/tools/4data/images.c \#quake2/tools/4data/models.c \#quake2/tools/4data/sprites.c \#quake2/tools/4data/tables.c \#quake2/tools/4data/video.c \