mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2024-11-22 20:41:20 +00:00
Add Hideo's WIP qflight & qfvis.
This commit is contained in:
parent
5567a694d3
commit
a3af2d9fa9
23 changed files with 3727 additions and 1 deletions
|
@ -1,2 +1,2 @@
|
|||
SUBDIRS=pak qfcc qfdefs qfprogs qwaq wav
|
||||
SUBDIRS=pak qfcc qfdefs qflight qfprogs qfvis qwaq wav
|
||||
bin_SCRIPTS=zpak
|
||||
|
|
7
tools/qflight/Makefile.am
Normal file
7
tools/qflight/Makefile.am
Normal file
|
@ -0,0 +1,7 @@
|
|||
AUTOMAKE_OPTIONS= foreign
|
||||
|
||||
SUBDIRS= include source
|
||||
|
||||
man_MANS= qflight.1
|
||||
|
||||
EXTRA_DIST= $(man_MANS)
|
3
tools/qflight/include/Makefile.am
Normal file
3
tools/qflight/include/Makefile.am
Normal file
|
@ -0,0 +1,3 @@
|
|||
AUTOMAKE_OPTIONS= foreign
|
||||
|
||||
EXTRA_DIST= entities.h light.h options.h threads.h
|
63
tools/qflight/include/entities.h
Normal file
63
tools/qflight/include/entities.h
Normal file
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
entities.h
|
||||
|
||||
(description)
|
||||
|
||||
Copyright (C) 2002 Colin Thompson
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to:
|
||||
|
||||
Free Software Foundation, Inc.
|
||||
59 Temple Place - Suite 330
|
||||
Boston, MA 02111-1307, USA
|
||||
|
||||
$Id$
|
||||
*/
|
||||
|
||||
#ifndef __entities_h
|
||||
#define __entities_h
|
||||
|
||||
#define DEFAULTLIGHTLEVEL 300
|
||||
|
||||
typedef struct epair_s {
|
||||
struct epair_s *next;
|
||||
char key[MAX_KEY];
|
||||
char value[MAX_VALUE];
|
||||
} epair_t;
|
||||
|
||||
typedef struct entity_s {
|
||||
char classname[64];
|
||||
vec3_t origin;
|
||||
float angle;
|
||||
int light;
|
||||
int style;
|
||||
char target[32];
|
||||
char targetname[32];
|
||||
struct epair_s *epairs;
|
||||
struct entity_s *targetent;
|
||||
} entity_t;
|
||||
|
||||
extern entity_t entities[MAX_MAP_ENTITIES];
|
||||
extern int num_entities;
|
||||
|
||||
char *ValueForKey (entity_t *ent, char *key);
|
||||
void SetKeyValue (entity_t *ent, char *key, char *value);
|
||||
float FloatForKey (entity_t *ent, char *key);
|
||||
void GetVectorForKey (entity_t *ent, char *key, vec3_t vec);
|
||||
|
||||
void LoadEntities (void);
|
||||
void WriteEntitiesToString (void);
|
||||
|
||||
#endif// __entities_h
|
63
tools/qflight/include/light.h
Normal file
63
tools/qflight/include/light.h
Normal file
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
light.h
|
||||
|
||||
Light tool
|
||||
|
||||
Copyright (C) 2002 Colin Thompson
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to:
|
||||
|
||||
Free Software Foundation, Inc.
|
||||
59 Temple Place - Suite 330
|
||||
Boston, MA 02111-1307, USA
|
||||
|
||||
$Id$
|
||||
*/
|
||||
|
||||
#ifndef __light_h
|
||||
#define __light_h
|
||||
|
||||
#define ON_EPSILON 0.1
|
||||
#define MAXLIGHTS 1024
|
||||
|
||||
extern float scaledist;
|
||||
extern float scalecos;
|
||||
extern float rangescale;
|
||||
|
||||
extern int c_culldistplane, c_proper;
|
||||
|
||||
extern byte *filebase;
|
||||
|
||||
extern vec3_t bsp_origin;
|
||||
extern vec3_t bsp_xvector;
|
||||
extern vec3_t bsp_yvector;
|
||||
|
||||
extern qboolean extrasamples;
|
||||
|
||||
extern float minlights[MAX_MAP_FACES];
|
||||
|
||||
void LoadNodes (char *file);
|
||||
qboolean TestLine (vec3_t start, vec3_t stop);
|
||||
|
||||
void LightFace (int surfnum);
|
||||
void LightLeaf (dleaf_t *leaf);
|
||||
|
||||
void MakeTnodes (dmodel_t *bm);
|
||||
byte *GetFileSpace (int size);
|
||||
void TransformSample (vec3_t in, vec3_t out);
|
||||
void RotateSample (vec3_t in, vec3_t out);
|
||||
|
||||
#endif// __light_h
|
||||
|
49
tools/qflight/include/options.h
Normal file
49
tools/qflight/include/options.h
Normal file
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
options.h
|
||||
|
||||
command line options handling
|
||||
|
||||
Copyright (C) 2002 Colin Thompson
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to:
|
||||
|
||||
Free Software Foundation, Inc.
|
||||
59 Temple Place - Suite 330
|
||||
Boston, MA 02111-1307, USA
|
||||
|
||||
$Id$
|
||||
*/
|
||||
|
||||
#ifndef __options_h
|
||||
#define __options_h
|
||||
|
||||
#include "QF/qtypes.h"
|
||||
|
||||
typedef struct {
|
||||
int verbosity; // 0=silent
|
||||
int threads;
|
||||
qboolean extra;
|
||||
float distance;
|
||||
float range;
|
||||
} options_t;
|
||||
|
||||
extern options_t options;
|
||||
extern char *bspfile;
|
||||
extern const char *this_program;
|
||||
|
||||
int DecodeArgs (int argc, char **argv);
|
||||
void usage (int status);
|
||||
|
||||
#endif//__options_h
|
49
tools/qflight/include/threads.h
Normal file
49
tools/qflight/include/threads.h
Normal file
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
threads.h
|
||||
|
||||
Thread management
|
||||
|
||||
Copyright (C) 2002 Colin Thompson
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to:
|
||||
|
||||
Free Software Foundation, Inc.
|
||||
59 Temple Place - Suite 330
|
||||
Boston, MA 02111-1307, USA
|
||||
|
||||
$Id$
|
||||
*/
|
||||
|
||||
#ifndef __threads_h
|
||||
#define __threads_h
|
||||
|
||||
#ifdef __alpha
|
||||
#include <pthread.h>
|
||||
extern pthread_mutex_t *my_mutex;
|
||||
#define LOCK pthread_mutex_lock (my_mutex)
|
||||
#define UNLOCK pthread_mutex_unlock (my_mutex)
|
||||
#else
|
||||
#define LOCK
|
||||
#define UNLOCK
|
||||
#endif
|
||||
|
||||
extern int numthreads;
|
||||
|
||||
typedef void (threadfunc_t) (void *);
|
||||
|
||||
void InitThreads (void);
|
||||
void RunThreadsOn (threadfunc_t func);
|
||||
|
||||
#endif// __threads_h
|
98
tools/qflight/qflight.1
Normal file
98
tools/qflight/qflight.1
Normal file
|
@ -0,0 +1,98 @@
|
|||
.\" hey, Emacs: -*- nroff -*-
|
||||
.\" qfbsp is free software; you can redistribute it and/or modify
|
||||
.\" it under the terms of the GNU General Public License as published by
|
||||
.\" the Free Software Foundation; either version 2 of the License, or
|
||||
.\" (at your option) any later version.
|
||||
.\"
|
||||
.\" This program is distributed in the hope that it will be useful,
|
||||
.\" but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
.\"
|
||||
.\" See the GNU General Public License for more details.
|
||||
.\"
|
||||
.\" You should have received a copy of the GNU General Public License
|
||||
.\" along with this program; see the file COPYING. If not, write to:
|
||||
.\"
|
||||
.\" Free Software Foundation, Inc.
|
||||
.\" 59 Temple Place, Suite 330
|
||||
.\" Boston, MA 02111-1307, USA
|
||||
.\"
|
||||
.\" Some roff macros, for reference:
|
||||
.\" .nh disable hyphenation
|
||||
.\" .hy enable hyphenation
|
||||
.\" .ad l left justify
|
||||
.\" .ad b justify to both left and right margins (default)
|
||||
.\" .nf disable filling
|
||||
.\" .fi enable filling
|
||||
.\" .br insert line break
|
||||
.\" .sp <n> insert n+1 empty lines
|
||||
.\" for manpage-specific macros, see man(7)
|
||||
.\"
|
||||
.TH QFBSP 1 "24 August, 2002" QuakeForge "QuakeForge User's Manual"
|
||||
.\" Please update the above date whenever this man page is modified.
|
||||
.SH NAME
|
||||
pak \- The QuakeForge BSP Tool
|
||||
.SH SYNOPSIS
|
||||
.B qfbsp
|
||||
<\fBcommand\fP> [\fBoptions\fP] \fIARCHIVE\fP \fIFILE\fP...
|
||||
.SH DESCRIPTION
|
||||
\fBqfbsp\fP is a utility for managing pack (*.pak) archives. The format used is
|
||||
compatible with Quake and Quake II. Pak archives are a convenient way to store
|
||||
groups of related files. Some engines allow pak archives to be compressed
|
||||
internally.
|
||||
.SH COMMANDS
|
||||
\fBqfbsp\fP accepts the following arguments as a command. Only one command may be
|
||||
used at a time.
|
||||
.TP
|
||||
.B \-c, \-\-create
|
||||
Create an archive. Overwrites any contents the archive may have had.
|
||||
.TP
|
||||
.B \-t, \-\-test
|
||||
Test an archive for valid formatting.
|
||||
.TP
|
||||
.B \-x, \-\-extract
|
||||
Extract an archive. Overwrites any files in the current directory with the same
|
||||
names as those inside the archive.
|
||||
.TP
|
||||
.B \-h, \-\-help
|
||||
Show summary of options.
|
||||
.TP
|
||||
.B \-V, \-\-version
|
||||
Show the version of pak.
|
||||
.SH OPTIONS
|
||||
\fBpak\fP takes the following arguments as options.
|
||||
.TP
|
||||
.B \-f, \-\-file ARCHIVE
|
||||
Use ARCHIVE as the archive filename, instead of the first non-option argument.
|
||||
This option is provided for compatibility with older versions of \fBpak\fP.
|
||||
.TP
|
||||
.B \-p, \-\-pad
|
||||
Can be useful when creating an archive. File space is padded to a 4\-byte
|
||||
boundary, assisting in the speed of loading files from the archive. The file
|
||||
data itself is not changed.
|
||||
.TP
|
||||
.B \-q, \-\-quiet
|
||||
Inhibit some of pak's normal output.
|
||||
.TP
|
||||
.B \-v, \-\-verbose
|
||||
Display more output than usual.
|
||||
.SH "EXIT STATUS"
|
||||
\fBpak\fP returns a zero exit status when it has completed a command
|
||||
successfully (without error). Otherwise, it returns a non-zero exit status.
|
||||
.SH BUGS
|
||||
.PP
|
||||
The program does not currently handle compression or decompression. This
|
||||
is planned for a future version.
|
||||
.PP
|
||||
The program does not currently handle extraction of individual files inside an
|
||||
archive. This is planned for a future version.
|
||||
.PP
|
||||
The program cannot modify an archive once it has been created.
|
||||
.SH AUTHORS
|
||||
Bill Currie (taniwha@quakeforge.net) wrote most of the program.
|
||||
.PP
|
||||
Jeff Teunissen (deek@quakeforge.net) wrote the command-line interface and the
|
||||
documentation.
|
||||
.SH "SEE ALSO"
|
||||
.BR quakeforge (1),
|
||||
.BR qfcc (1)
|
14
tools/qflight/source/Makefile.am
Normal file
14
tools/qflight/source/Makefile.am
Normal file
|
@ -0,0 +1,14 @@
|
|||
AUTOMAKE_OPTIONS= foreign
|
||||
|
||||
QFLIGHT_LIBS=@QFLIGHT_LIBS@
|
||||
QFLIGHT_DEPS=@QFLIGHT_DEPS@
|
||||
QFLIGHT_INCS=@QFLIGHT_INCS@
|
||||
|
||||
INCLUDES= -I$(top_srcdir)/include $(QFLIGHT_INCS)
|
||||
|
||||
bin_PROGRAMS= qflight
|
||||
|
||||
qflight_SOURCES= entities.c ltface.c options.c qflight.c threads.c trace.c
|
||||
|
||||
qflight_LDADD= $(QFLIGHT_LIBS)
|
||||
qflight_DEPENDENCIES= $(QFLIGHT_DEPS)
|
311
tools/qflight/source/entities.c
Normal file
311
tools/qflight/source/entities.c
Normal file
|
@ -0,0 +1,311 @@
|
|||
/*
|
||||
trace.c
|
||||
|
||||
(description)
|
||||
|
||||
Copyright (C) 2002 Colin Thompson
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to:
|
||||
|
||||
Free Software Foundation, Inc.
|
||||
59 Temple Place - Suite 330
|
||||
Boston, MA 02111-1307, USA
|
||||
|
||||
*/
|
||||
static const char rcsid[] =
|
||||
"$Id$";
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
#ifdef HAVE_IO_H
|
||||
# include <io.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRING_H
|
||||
# include <string.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRINGS_H
|
||||
# include <strings.h>
|
||||
#endif
|
||||
#ifdef HAVE_UNISTD_H
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "QF/bspfile.h"
|
||||
#include "QF/dstring.h"
|
||||
#include "QF/idparse.h"
|
||||
#include "QF/mathlib.h"
|
||||
#include "QF/qtypes.h"
|
||||
#include "QF/sys.h"
|
||||
#include "QF/vfile.h"
|
||||
#include "QF/vfs.h"
|
||||
|
||||
#include "light.h"
|
||||
#include "threads.h"
|
||||
#include "entities.h"
|
||||
#include "options.h"
|
||||
|
||||
entity_t entities[MAX_MAP_ENTITIES];
|
||||
int num_entities;
|
||||
|
||||
/*
|
||||
ENTITY FILE PARSING
|
||||
|
||||
If a light has a targetname, generate a unique style in the 32-63 range
|
||||
*/
|
||||
|
||||
int numlighttargets;
|
||||
char lighttargets[32][64];
|
||||
|
||||
|
||||
int
|
||||
LightStyleForTargetname (char *targetname, qboolean alloc)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < numlighttargets; i++)
|
||||
if (!strcmp (lighttargets[i], targetname))
|
||||
return 32 + i;
|
||||
|
||||
if (!alloc)
|
||||
return -1;
|
||||
|
||||
strcpy (lighttargets[i], targetname);
|
||||
numlighttargets++;
|
||||
return numlighttargets - 1 + 32;
|
||||
}
|
||||
|
||||
void
|
||||
MatchTargets (void)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
|
||||
for (i = 0; i < num_entities; i++) {
|
||||
if (!entities[i].target[0])
|
||||
continue;
|
||||
|
||||
for (j = 0; j < num_entities; j++)
|
||||
if (!strcmp (entities[j].targetname, entities[i].target)) {
|
||||
entities[i].targetent = &entities[j];
|
||||
break;
|
||||
}
|
||||
if (j == num_entities) {
|
||||
printf ("WARNING: entity at (%i,%i,%i) (%s) has unmatched "
|
||||
"target\n",
|
||||
(int) entities[i].origin[0], (int) entities[i].origin[1],
|
||||
(int) entities[i].origin[2], entities[i].classname);
|
||||
continue;
|
||||
}
|
||||
// set the style on the source ent for switchable lights
|
||||
if (entities[j].style) {
|
||||
char s[16];
|
||||
|
||||
entities[i].style = entities[j].style;
|
||||
sprintf (s, "%i", entities[i].style);
|
||||
SetKeyValue (&entities[i], "style", s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
LoadEntities (void)
|
||||
{
|
||||
const char *data;
|
||||
entity_t *entity;
|
||||
char key[64];
|
||||
epair_t *epair;
|
||||
double vec[3];
|
||||
int i;
|
||||
|
||||
data = dentdata;
|
||||
|
||||
// start parsing
|
||||
num_entities = 0;
|
||||
|
||||
// go through all the entities
|
||||
while (1) {
|
||||
// parse the opening brace
|
||||
data = COM_Parse (data);
|
||||
if (!data)
|
||||
break;
|
||||
if (com_token[0] != '{')
|
||||
fprintf (stderr, "LoadEntities: found %s when expecting {",
|
||||
com_token);
|
||||
|
||||
if (num_entities == MAX_MAP_ENTITIES)
|
||||
fprintf (stderr, "LoadEntities: MAX_MAP_ENTITIES");
|
||||
entity = &entities[num_entities];
|
||||
num_entities++;
|
||||
|
||||
// go through all the keys in this entity
|
||||
while (1) {
|
||||
int c;
|
||||
|
||||
// parse key
|
||||
data = COM_Parse (data);
|
||||
if (!data)
|
||||
fprintf (stderr, "LoadEntities: EOF without closing brace");
|
||||
if (!strcmp (com_token, "}"))
|
||||
break;
|
||||
strcpy (key, com_token);
|
||||
|
||||
// parse value
|
||||
data = COM_Parse (data);
|
||||
if (!data)
|
||||
fprintf (stderr, "LoadEntities: EOF without closing brace");
|
||||
c = com_token[0];
|
||||
if (c == '}')
|
||||
fprintf (stderr, "LoadEntities: closing brace without data");
|
||||
|
||||
epair = malloc (sizeof (epair_t));
|
||||
memset (epair, 0, sizeof (epair));
|
||||
strcpy (epair->key, key);
|
||||
strcpy (epair->value, com_token);
|
||||
epair->next = entity->epairs;
|
||||
entity->epairs = epair;
|
||||
|
||||
if (!strcmp (key, "classname"))
|
||||
strcpy (entity->classname, com_token);
|
||||
else if (!strcmp (key, "target"))
|
||||
strcpy (entity->target, com_token);
|
||||
else if (!strcmp (key, "targetname"))
|
||||
strcpy (entity->targetname, com_token);
|
||||
else if (!strcmp (key, "origin")) {
|
||||
// scan into doubles, then assign
|
||||
// which makes it vec_t size independent
|
||||
if (sscanf (com_token, "%lf %lf %lf",
|
||||
&vec[0], &vec[1], &vec[2]) != 3)
|
||||
fprintf (stderr, "LoadEntities: not 3 values for origin");
|
||||
for (i = 0; i < 3; i++)
|
||||
entity->origin[i] = vec[i];
|
||||
} else if (!strncmp (key, "light", 5) || !strcmp (key, "_light")) {
|
||||
entity->light = atof(com_token);
|
||||
} else if (!strcmp (key, "style")) {
|
||||
entity->style = atof (com_token);
|
||||
if ((unsigned) entity->style > 254)
|
||||
fprintf (stderr, "Bad light style %i (must be 0-254)",
|
||||
entity->style);
|
||||
} else if (!strcmp (key, "angle")) {
|
||||
entity->angle = atof(com_token);
|
||||
}
|
||||
}
|
||||
|
||||
// all fields have been parsed
|
||||
if (!strncmp (entity->classname, "light", 5) && !entity->light)
|
||||
entity->light = DEFAULTLIGHTLEVEL;
|
||||
|
||||
if (!strcmp (entity->classname, "light")) {
|
||||
if (entity->targetname[0] && !entity->style) {
|
||||
char s[16];
|
||||
|
||||
entity->style = LightStyleForTargetname (entity->targetname,
|
||||
true);
|
||||
sprintf (s, "%i", entity->style);
|
||||
SetKeyValue (entity, "style", s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (options.verbosity >= 0)
|
||||
printf ("%d entities read\n", num_entities);
|
||||
|
||||
MatchTargets ();
|
||||
}
|
||||
|
||||
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 "";
|
||||
}
|
||||
|
||||
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)) {
|
||||
strcpy (ep->value, value);
|
||||
return;
|
||||
}
|
||||
ep = malloc (sizeof (*ep));
|
||||
ep->next = ent->epairs;
|
||||
ent->epairs = ep;
|
||||
strcpy (ep->key, key);
|
||||
strcpy (ep->value, value);
|
||||
}
|
||||
|
||||
float
|
||||
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;
|
||||
|
||||
k = ValueForKey (ent, key);
|
||||
sscanf (k, "%f %f %f", &vec[0], &vec[1], &vec[2]);
|
||||
}
|
||||
|
||||
void
|
||||
WriteEntitiesToString (void)
|
||||
{
|
||||
char *buf, *end;
|
||||
char line[128];
|
||||
epair_t *ep;
|
||||
int i;
|
||||
|
||||
buf = dentdata;
|
||||
end = buf;
|
||||
*end = 0;
|
||||
|
||||
if (options.verbosity >= 0)
|
||||
printf ("%i switchable light styles\n", numlighttargets);
|
||||
|
||||
for (i = 0; i < num_entities; i++) {
|
||||
ep = entities[i].epairs;
|
||||
if (!ep)
|
||||
continue; // ent got removed
|
||||
|
||||
strcat (end, "{\n");
|
||||
end += 2;
|
||||
|
||||
for (ep = entities[i].epairs; ep; ep = ep->next) {
|
||||
sprintf (line, "\"%s\" \"%s\"\n", ep->key, ep->value);
|
||||
strcat (end, line);
|
||||
end += strlen (line);
|
||||
}
|
||||
strcat (end, "}\n");
|
||||
end += 2;
|
||||
|
||||
if (end > buf + MAX_MAP_ENTSTRING)
|
||||
fprintf (stderr, "Entity text too long");
|
||||
}
|
||||
entdatasize = end - buf + 1;
|
||||
}
|
571
tools/qflight/source/ltface.c
Normal file
571
tools/qflight/source/ltface.c
Normal file
|
@ -0,0 +1,571 @@
|
|||
/*
|
||||
trace.c
|
||||
|
||||
(description)
|
||||
|
||||
Copyright (C) 2002 Colin Thompson
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to:
|
||||
|
||||
Free Software Foundation, Inc.
|
||||
59 Temple Place - Suite 330
|
||||
Boston, MA 02111-1307, USA
|
||||
|
||||
*/
|
||||
static const char rcsid[] = "$Id$";
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_UNISTD_H
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
#ifdef HAVE_IO_H
|
||||
# include <io.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRING_H
|
||||
# include <string.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRINGS_H
|
||||
# include <strings.h>
|
||||
#endif
|
||||
#include <stdlib.h>
|
||||
|
||||
|
||||
#include "QF/qtypes.h"
|
||||
#include "QF/sys.h"
|
||||
#include "QF/dstring.h"
|
||||
#include "QF/vfile.h"
|
||||
#include "QF/vfs.h"
|
||||
#include "QF/bspfile.h"
|
||||
#include "QF/mathlib.h"
|
||||
#include "light.h"
|
||||
#include "entities.h"
|
||||
#include "options.h"
|
||||
|
||||
#define SINGLEMAP (18*18*4)
|
||||
|
||||
typedef struct {
|
||||
vec_t lightmaps[MAXLIGHTMAPS][SINGLEMAP];
|
||||
int numlightstyles;
|
||||
vec_t *light;
|
||||
vec_t facedist;
|
||||
vec3_t facenormal;
|
||||
|
||||
int numsurfpt;
|
||||
vec3_t surfpt[SINGLEMAP];
|
||||
|
||||
vec3_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];
|
||||
|
||||
int texmins[2], texsize[2];
|
||||
int lightstyles[256];
|
||||
int surfnum;
|
||||
dface_t *face;
|
||||
} lightinfo_t;
|
||||
|
||||
int c_bad;
|
||||
int c_culldistplane, c_proper;
|
||||
|
||||
/*
|
||||
CastRay
|
||||
|
||||
Returns the distance between the points, or -1 if blocked
|
||||
*/
|
||||
vec_t
|
||||
CastRay (vec3_t p1, vec3_t p2)
|
||||
{
|
||||
int i;
|
||||
vec_t t;
|
||||
qboolean trace;
|
||||
|
||||
|
||||
trace = TestLine (p1, p2);
|
||||
|
||||
if (!trace)
|
||||
return -1; // ray was blocked
|
||||
|
||||
t = 0;
|
||||
for (i = 0; i < 3; i++)
|
||||
t += (p2[i] - p1[i]) * (p2[i] - p1[i]);
|
||||
|
||||
if (t == 0)
|
||||
t = 1; // don't blow up...
|
||||
return sqrt (t);
|
||||
}
|
||||
|
||||
/*
|
||||
SAMPLE POINT DETERMINATION
|
||||
|
||||
void SetupBlock (dface_t *f) Returns with surfpt[] set
|
||||
|
||||
This is a little tricky because the lightmap covers more area than the face.
|
||||
If done in the straightforward fashion, some of the
|
||||
sample points will be inside walls or on the other side of walls, causing
|
||||
false shadows and light bleeds.
|
||||
|
||||
To solve this, I only consider a sample point valid if a line can be drawn
|
||||
between it and the exact midpoint of the face. If invalid, it is adjusted
|
||||
towards the center until it is valid.
|
||||
|
||||
(this doesn't completely work)
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
CalcFaceVectors
|
||||
|
||||
Fills in texorg, worldtotex. and textoworld
|
||||
*/
|
||||
void
|
||||
CalcFaceVectors (lightinfo_t *l)
|
||||
{
|
||||
texinfo_t *tex;
|
||||
int i, j;
|
||||
vec3_t texnormal;
|
||||
float distscale;
|
||||
vec_t dist, len;
|
||||
|
||||
|
||||
tex = &texinfo[l->face->texinfo];
|
||||
|
||||
// convert from float to vec_t
|
||||
for (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/T
|
||||
texnormal[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);
|
||||
|
||||
// flip it towards plane normal
|
||||
distscale = DotProduct (texnormal, l->facenormal);
|
||||
if (!distscale)
|
||||
fprintf (stderr, "Texture axis perpendicular to face");
|
||||
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 normal
|
||||
distscale = 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 plane
|
||||
for (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 plane
|
||||
dist = DotProduct (l->texorg, l->facenormal) - l->facedist - 1;
|
||||
dist *= distscale;
|
||||
VectorMA (l->texorg, -dist, texnormal, l->texorg);
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
CalcFaceExtents
|
||||
|
||||
Fills in s->texmins[] and s->texsize[]
|
||||
also sets exactmins[] and exactmaxs[]
|
||||
*/
|
||||
void
|
||||
CalcFaceExtents (lightinfo_t *l)
|
||||
{
|
||||
dface_t *s;
|
||||
vec_t mins[2], maxs[2], val;
|
||||
int i, j, e;
|
||||
dvertex_t *v;
|
||||
texinfo_t *tex;
|
||||
|
||||
|
||||
s = l->face;
|
||||
|
||||
mins[0] = mins[1] = 999999;
|
||||
maxs[0] = maxs[1] = -99999;
|
||||
|
||||
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];
|
||||
else
|
||||
v = dvertexes + dedges[-e].v[1];
|
||||
|
||||
for (j = 0; j < 2; j++) {
|
||||
val = v->point[0] * tex->vecs[j][0] +
|
||||
v->point[1] * tex->vecs[j][1] +
|
||||
v->point[2] * tex->vecs[j][2] + 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] / 16);
|
||||
maxs[i] = ceil (maxs[i] / 16);
|
||||
|
||||
l->texmins[i] = mins[i];
|
||||
l->texsize[i] = maxs[i] - mins[i];
|
||||
if (l->texsize[i] > 17)
|
||||
fprintf (stderr, "Bad surface extents");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
CalcPoints
|
||||
|
||||
For each texture aligned grid point, back project onto the plane
|
||||
to get the world xyz value of the sample point
|
||||
*/
|
||||
void
|
||||
CalcPoints (lightinfo_t *l)
|
||||
{
|
||||
int i;
|
||||
int s, t, j;
|
||||
int w, h, step;
|
||||
vec_t starts, startt, us, ut;
|
||||
vec_t *surf;
|
||||
vec_t mids, midt;
|
||||
vec3_t facemid, move;
|
||||
|
||||
|
||||
// fill in surforg
|
||||
// the points are biased towards the center of the surface
|
||||
// to help avoid edge cases just inside walls
|
||||
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;
|
||||
|
||||
if (extrasamples) {
|
||||
// extra filtering
|
||||
h = (l->texsize[1] + 1) * 2;
|
||||
w = (l->texsize[0] + 1) * 2;
|
||||
starts = (l->texmins[0] - 0.5) * 16;
|
||||
startt = (l->texmins[1] - 0.5) * 16;
|
||||
step = 8;
|
||||
} else {
|
||||
h = l->texsize[1] + 1;
|
||||
w = l->texsize[0] + 1;
|
||||
starts = l->texmins[0] * 16;
|
||||
startt = l->texmins[1] * 16;
|
||||
step = 16;
|
||||
}
|
||||
|
||||
l->numsurfpt = w * h;
|
||||
for (t = 0; t < h; t++) {
|
||||
for (s = 0; s < w; s++, surf += 3) {
|
||||
us = starts + s * step;
|
||||
ut = startt + t * step;
|
||||
|
||||
// if a line can be traced from surf to facemid,
|
||||
// the point is good
|
||||
for (i = 0; i < 6; i++) {
|
||||
// calculate texture point
|
||||
for (j = 0; j < 3; j++)
|
||||
surf[j] = l->texorg[j] +
|
||||
l->textoworld[0][j] * us +
|
||||
l->textoworld[1][j] * ut;
|
||||
|
||||
if (CastRay (facemid, surf) != -1)
|
||||
break; // got it
|
||||
if (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;
|
||||
}
|
||||
}
|
||||
|
||||
// move surf 8 pixels towards the center
|
||||
VectorSubtract (facemid, surf, move);
|
||||
VectorNormalize (move);
|
||||
VectorMA (surf, 8, move, surf);
|
||||
}
|
||||
if (i == 2)
|
||||
c_bad++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
SingleLightFace (entity_t *light, lightinfo_t *l)
|
||||
{
|
||||
vec_t dist;
|
||||
vec3_t incoming;
|
||||
vec_t angle;
|
||||
vec_t add;
|
||||
vec_t *surf;
|
||||
qboolean hit;
|
||||
int mapnum;
|
||||
int size;
|
||||
int c, i;
|
||||
vec3_t rel;
|
||||
vec3_t spotvec;
|
||||
vec_t falloff;
|
||||
vec_t *lightsamp;
|
||||
|
||||
|
||||
VectorSubtract (light->origin, bsp_origin, rel);
|
||||
dist = options.distance * (DotProduct (rel, l->facenormal) - l->facedist);
|
||||
|
||||
// don't bother with lights behind the surface
|
||||
if (dist <= 0)
|
||||
return;
|
||||
|
||||
// don't bother with light too far away
|
||||
if (dist > light->light) {
|
||||
c_culldistplane++;
|
||||
return;
|
||||
}
|
||||
|
||||
if (light->targetent) {
|
||||
VectorSubtract (light->targetent->origin, light->origin, spotvec);
|
||||
VectorNormalize (spotvec);
|
||||
if (!light->angle)
|
||||
falloff = -cos (20 * M_PI / 180);
|
||||
else
|
||||
falloff = -cos (light->angle / 2 * M_PI / 180);
|
||||
} else
|
||||
falloff = 0;
|
||||
mapnum = 0;
|
||||
for (mapnum = 0; mapnum < l->numlightstyles; mapnum++)
|
||||
if (l->lightstyles[mapnum] == light->style)
|
||||
break;
|
||||
lightsamp = l->lightmaps[mapnum];
|
||||
if (mapnum == l->numlightstyles) {
|
||||
// init a new light map
|
||||
if (mapnum == MAXLIGHTMAPS) {
|
||||
printf ("WARNING: Too many light styles on a face\n");
|
||||
return;
|
||||
}
|
||||
size = (l->texsize[1] + 1) * (l->texsize[0] + 1);
|
||||
for (i = 0; i < size; i++)
|
||||
lightsamp[i] = 0;
|
||||
}
|
||||
|
||||
// check it for real
|
||||
hit = false;
|
||||
c_proper++;
|
||||
|
||||
surf = l->surfpt[0];
|
||||
for (c = 0; c < l->numsurfpt; c++, surf += 3) {
|
||||
dist = CastRay (light->origin, surf) * options.distance;
|
||||
if (dist < 0)
|
||||
continue; // light doesn't reach
|
||||
|
||||
VectorSubtract (light->origin, surf, incoming);
|
||||
VectorNormalize (incoming);
|
||||
angle = DotProduct (incoming, l->facenormal);
|
||||
if (light->targetent) {
|
||||
// spotlight cutoff
|
||||
if (DotProduct (spotvec, incoming) > falloff)
|
||||
continue;
|
||||
}
|
||||
|
||||
angle = (1.0 - scalecos) + scalecos * angle;
|
||||
add = light->light - dist;
|
||||
add *= angle;
|
||||
if (add < 0)
|
||||
continue;
|
||||
lightsamp[c] += add;
|
||||
if (lightsamp[c] > 1)
|
||||
// ignore real tiny lights
|
||||
hit = true;
|
||||
}
|
||||
|
||||
if (mapnum == l->numlightstyles && hit) {
|
||||
l->lightstyles[mapnum] = light->style;
|
||||
l->numlightstyles++; // the style has some real data now
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
FixMinlight (lightinfo_t *l)
|
||||
{
|
||||
int i, j;
|
||||
float minlight;
|
||||
|
||||
|
||||
minlight = minlights[l->surfnum];
|
||||
|
||||
// if minlight is set, there must be a style 0 light map
|
||||
if (!minlight)
|
||||
return;
|
||||
|
||||
for (i = 0; i < l->numlightstyles; i++) {
|
||||
if (l->lightstyles[i] == 0)
|
||||
break;
|
||||
}
|
||||
if (i == l->numlightstyles) {
|
||||
if (l->numlightstyles == MAXLIGHTMAPS)
|
||||
return; // oh well..
|
||||
for (j = 0; j < l->numsurfpt; j++)
|
||||
l->lightmaps[i][j] = minlight;
|
||||
l->lightstyles[i] = 0;
|
||||
l->numlightstyles++;
|
||||
} else {
|
||||
for (j = 0; j < l->numsurfpt; j++)
|
||||
if (l->lightmaps[i][j] < minlight)
|
||||
l->lightmaps[i][j] = minlight;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
LightFace (int surfnum)
|
||||
{
|
||||
dface_t *f;
|
||||
lightinfo_t l;
|
||||
int s, t;
|
||||
int i, j, c;
|
||||
vec_t total;
|
||||
int size;
|
||||
int lightmapwidth, lightmapsize;
|
||||
byte *out;
|
||||
vec_t *light;
|
||||
int w, h;
|
||||
|
||||
|
||||
f = dfaces + surfnum;
|
||||
|
||||
// some surfaces don't need lightmaps
|
||||
f->lightofs = -1;
|
||||
for (j = 0; j < MAXLIGHTMAPS; j++)
|
||||
f->styles[j] = 255;
|
||||
|
||||
if (texinfo[f->texinfo].flags & TEX_SPECIAL)
|
||||
// non-lit texture
|
||||
return;
|
||||
|
||||
memset(&l, 0, sizeof (l));
|
||||
l.surfnum = surfnum;
|
||||
l.face = f;
|
||||
|
||||
// rotate plane
|
||||
VectorCopy (dplanes[f->planenum].normal, l.facenormal);
|
||||
l.facedist = dplanes[f->planenum].dist;
|
||||
if (f->side) {
|
||||
VectorSubtract (vec3_origin, l.facenormal, l.facenormal);
|
||||
l.facedist = -l.facedist;
|
||||
}
|
||||
|
||||
CalcFaceVectors (&l);
|
||||
CalcFaceExtents (&l);
|
||||
CalcPoints (&l);
|
||||
|
||||
lightmapwidth = l.texsize[0] + 1;
|
||||
|
||||
size = lightmapwidth * (l.texsize[1] + 1);
|
||||
if (size > SINGLEMAP)
|
||||
fprintf (stderr, "Bad lightmap size");
|
||||
|
||||
for (i = 0; i < MAXLIGHTMAPS; i++)
|
||||
l.lightstyles[i] = 255;
|
||||
|
||||
// cast all lights
|
||||
l.numlightstyles = 0;
|
||||
for (i = 0; i < num_entities; i++) {
|
||||
if (entities[i].light)
|
||||
SingleLightFace (&entities[i], &l);
|
||||
}
|
||||
|
||||
FixMinlight (&l);
|
||||
|
||||
if (!l.numlightstyles)
|
||||
// no light hitting it
|
||||
return;
|
||||
|
||||
// save out the values
|
||||
for (i = 0; i < MAXLIGHTMAPS; i++)
|
||||
f->styles[i] = l.lightstyles[i];
|
||||
|
||||
lightmapsize = size * l.numlightstyles;
|
||||
|
||||
out = GetFileSpace (lightmapsize);
|
||||
f->lightofs = out - filebase;
|
||||
|
||||
// extra filtering
|
||||
h = (l.texsize[1] + 1) * 2;
|
||||
w = (l.texsize[0] + 1) * 2;
|
||||
|
||||
for (i = 0; i < l.numlightstyles; i++) {
|
||||
if (l.lightstyles[i] == 0xff)
|
||||
fprintf (stderr, "Wrote empty lightmap");
|
||||
light = l.lightmaps[i];
|
||||
c = 0;
|
||||
for (t = 0; t <= l.texsize[1]; t++)
|
||||
for (s = 0; s <= l.texsize[0]; s++, c++) {
|
||||
if (extrasamples) {
|
||||
// filtered sample
|
||||
total = light[t * 2 * w + s * 2] +
|
||||
light[t * 2 * w + s * 2 + 1] +
|
||||
light[(t * 2 + 1) * w + s * 2] +
|
||||
light[(t * 2 + 1) * w + s * 2 + 1];
|
||||
total *= 0.25;
|
||||
} else
|
||||
total = light[c];
|
||||
total *= options.range; // scale before clamping
|
||||
if (total > 255)
|
||||
total = 255;
|
||||
if (total < 0)
|
||||
fprintf (stderr, "light < 0");
|
||||
*out++ = total;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
145
tools/qflight/source/options.c
Normal file
145
tools/qflight/source/options.c
Normal file
|
@ -0,0 +1,145 @@
|
|||
/*
|
||||
options.c
|
||||
|
||||
command line options handling
|
||||
|
||||
Copyright (C) 2002 Colin Thompson
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to:
|
||||
|
||||
Free Software Foundation, Inc.
|
||||
59 Temple Place - Suite 330
|
||||
Boston, MA 02111-1307, USA
|
||||
|
||||
*/
|
||||
static const char rcsid[] =
|
||||
"$Id$";
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
#ifdef HAVE_STRING_H
|
||||
# include <string.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRINGS_H
|
||||
# include <strings.h>
|
||||
#endif
|
||||
|
||||
#include <getopt.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "QF/qtypes.h"
|
||||
|
||||
#include "options.h"
|
||||
|
||||
const char *this_program;
|
||||
|
||||
static struct option const long_options[] = {
|
||||
{"quiet", no_argument, 0, 'q'},
|
||||
{"verbose", no_argument, 0, 'v'},
|
||||
{"help", no_argument, 0, 'h'},
|
||||
{"version", no_argument, 0, 'V'},
|
||||
{"threads", no_argument, 0, 't'},
|
||||
{"extra", no_argument, 0, 'e'},
|
||||
{"distance", no_argument, 0, 'd'},
|
||||
{"range", no_argument, 0, 'r'},
|
||||
{"file", no_argument, 0, 'f'},
|
||||
{NULL, 0, NULL, 0}
|
||||
};
|
||||
|
||||
static const char *short_options =
|
||||
"-" // magic option parsing mode doohicky (must come first)
|
||||
"q" // quiet
|
||||
"v" // verbose
|
||||
"h" // help
|
||||
"V" // version
|
||||
"t" // threads
|
||||
"e" // extra sampling
|
||||
"d" // scale distance
|
||||
"r" // scale range
|
||||
"f"
|
||||
;
|
||||
|
||||
void
|
||||
usage (int status)
|
||||
{
|
||||
printf ("%s - QuakeForge light tool\n", this_program);
|
||||
printf ("Usage: %s [options] bspfile\n", this_program);
|
||||
printf ("Options:\n"
|
||||
" -q, --quiet Inhibit usual output\n"
|
||||
" -v, --verbose Display more output than usual\n"
|
||||
" -h, --help Display this help and exit\n"
|
||||
" -V, --version Output version information and exit\n"
|
||||
" -t, --threads [num] Number of threads to use\n"
|
||||
" -e, --extra Apply extra sampling\n"
|
||||
" -d, --distance Scale distance\n"
|
||||
" -r, --range Scale range\n"
|
||||
" -f, --file BSP file\n\n");
|
||||
exit (status);
|
||||
}
|
||||
|
||||
int
|
||||
DecodeArgs (int argc, char **argv)
|
||||
{
|
||||
int c;
|
||||
|
||||
options.verbosity = 0;
|
||||
options.threads = 1;
|
||||
options.extra = false;
|
||||
options.distance = 1.0;
|
||||
options.range = 0.5;
|
||||
bspfile = NULL;
|
||||
|
||||
while ((c = getopt_long (argc, argv, short_options, long_options, 0))
|
||||
!= EOF) {
|
||||
switch (c) {
|
||||
case 'q': // quiet
|
||||
options.verbosity -= 1;
|
||||
break;
|
||||
case 'v': // verbose
|
||||
options.verbosity += 1;
|
||||
break;
|
||||
case 'h': // help
|
||||
usage (0);
|
||||
break;
|
||||
case 'V': // version
|
||||
printf ("%s version %s\n", PACKAGE, VERSION);
|
||||
exit (0);
|
||||
break;
|
||||
case 't': // threads
|
||||
options.threads = atoi (optarg);
|
||||
break;
|
||||
case 'e': // extra
|
||||
options.extra = true;
|
||||
break;
|
||||
case 'd': // scale distance
|
||||
options.distance = atof (optarg);
|
||||
break;
|
||||
case 'r': // scale range
|
||||
options.range = atof (optarg);
|
||||
break;
|
||||
case 'f':
|
||||
bspfile = strdup (optarg);
|
||||
break;
|
||||
default:
|
||||
usage (1);
|
||||
}
|
||||
}
|
||||
if ((!bspfile) && argv[optind] && *(argv[optind]))
|
||||
bspfile = strdup (argv[optind++]);
|
||||
|
||||
return optind;
|
||||
}
|
164
tools/qflight/source/qflight.c
Normal file
164
tools/qflight/source/qflight.c
Normal file
|
@ -0,0 +1,164 @@
|
|||
/*
|
||||
trace.c
|
||||
|
||||
(description)
|
||||
|
||||
Copyright (C) 2002 Colin Thompson
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to:
|
||||
|
||||
Free Software Foundation, Inc.
|
||||
59 Temple Place - Suite 330
|
||||
Boston, MA 02111-1307, USA
|
||||
|
||||
*/
|
||||
static const char rcsid[] = "$Id$";
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_UNISTD_H
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
#ifdef HAVE_IO_H
|
||||
# include <io.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRING_H
|
||||
# include <string.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRINGS_H
|
||||
# include <strings.h>
|
||||
#endif
|
||||
#include <getopt.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "QF/qtypes.h"
|
||||
#include "QF/sys.h"
|
||||
#include "QF/dstring.h"
|
||||
#include "QF/vfile.h"
|
||||
#include "QF/vfs.h"
|
||||
#include "QF/bspfile.h"
|
||||
#include "QF/mathlib.h"
|
||||
#include "light.h"
|
||||
#include "threads.h"
|
||||
#include "entities.h"
|
||||
#include "options.h"
|
||||
|
||||
options_t options;
|
||||
|
||||
char *bspfile;
|
||||
|
||||
float scalecos = 0.5;
|
||||
|
||||
byte *filebase, *file_p, *file_end;
|
||||
|
||||
dmodel_t *bspmodel;
|
||||
int bspfileface; // next surface to dispatch
|
||||
|
||||
vec3_t bsp_origin;
|
||||
|
||||
qboolean extrasamples;
|
||||
|
||||
float minlights[MAX_MAP_FACES];
|
||||
|
||||
|
||||
byte *
|
||||
GetFileSpace (int size)
|
||||
{
|
||||
byte *buf;
|
||||
|
||||
LOCK;
|
||||
file_p = (byte *) (((long) file_p + 3) & ~3);
|
||||
buf = file_p;
|
||||
file_p += size;
|
||||
UNLOCK;
|
||||
if (file_p > file_end)
|
||||
fprintf (stderr, "GetFileSpace: overrun");
|
||||
return buf;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
LightThread (void *junk)
|
||||
{
|
||||
int i;
|
||||
|
||||
while (1) {
|
||||
LOCK;
|
||||
i = bspfileface++;
|
||||
UNLOCK;
|
||||
if (i >= numfaces)
|
||||
return;
|
||||
|
||||
LightFace (i);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
LightWorld (void)
|
||||
{
|
||||
filebase = file_p = dlightdata;
|
||||
file_end = filebase + MAX_MAP_LIGHTING;
|
||||
|
||||
RunThreadsOn (LightThread);
|
||||
|
||||
lightdatasize = file_p - filebase;
|
||||
|
||||
if (options.verbosity >= 0)
|
||||
printf ("lightdatasize: %i\n", lightdatasize);
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char **argv)
|
||||
{
|
||||
double start, stop;
|
||||
|
||||
start = Sys_DoubleTime ();
|
||||
|
||||
this_program = argv[0];
|
||||
|
||||
DecodeArgs (argc, argv);
|
||||
|
||||
if (!bspfile) {
|
||||
fprintf (stderr, "%s: no bsp file specified.\n", this_program);
|
||||
usage (1);
|
||||
}
|
||||
|
||||
InitThreads ();
|
||||
|
||||
COM_StripExtension (bspfile, bspfile);
|
||||
COM_DefaultExtension (bspfile, ".bsp");
|
||||
|
||||
LoadBSPFile (bspfile);
|
||||
LoadEntities ();
|
||||
|
||||
MakeTnodes (&dmodels[0]);
|
||||
|
||||
LightWorld ();
|
||||
|
||||
WriteEntitiesToString ();
|
||||
WriteBSPFile (bspfile);
|
||||
|
||||
stop = Sys_DoubleTime ();
|
||||
|
||||
if (options.verbosity >= 0)
|
||||
printf ("%5.1f seconds elapsed\n", stop - start);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
106
tools/qflight/source/threads.c
Normal file
106
tools/qflight/source/threads.c
Normal file
|
@ -0,0 +1,106 @@
|
|||
/*
|
||||
trace.c
|
||||
|
||||
(description)
|
||||
|
||||
Copyright (C) 2002 Colin Thompson
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to:
|
||||
|
||||
Free Software Foundation, Inc.
|
||||
59 Temple Place - Suite 330
|
||||
Boston, MA 02111-1307, USA
|
||||
|
||||
*/
|
||||
static const char rcsid[] = "$Id$";
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_UNISTD_H
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
#ifdef HAVE_IO_H
|
||||
# include <io.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRING_H
|
||||
# include <string.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRINGS_H
|
||||
# include <strings.h>
|
||||
#endif
|
||||
|
||||
#include "QF/qtypes.h"
|
||||
#include "QF/qendian.h"
|
||||
#include "threads.h"
|
||||
|
||||
#ifdef __alpha
|
||||
int numthreads = 4;
|
||||
pthread_mutex_t *my_mutex;
|
||||
#else
|
||||
int numthreads = 1;
|
||||
#endif
|
||||
|
||||
void
|
||||
InitThreads (void)
|
||||
{
|
||||
#ifdef __alpha
|
||||
pthread_mutexattr_t mattrib;
|
||||
|
||||
my_mutex = malloc (sizeof (*my_mutex));
|
||||
if (pthread_mutexattr_create (&mattrib) == -1)
|
||||
fprintf (stderr, "pthread_mutex_attr_create failed");
|
||||
if (pthread_mutexattr_setkind_np (&mattrib, MUTEX_FAST_NP) == -1)
|
||||
fprintf (stderr, "pthread_mutexattr_setkind_np failed");
|
||||
if (pthread_mutex_init (my_mutex, mattrib) == -1)
|
||||
fprintf (stderr, "pthread_mutex_init failed");
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
RunThreadsOn (threadfunc_t func)
|
||||
{
|
||||
#ifdef __alpha
|
||||
pthread_t work_threads[256];
|
||||
pthread_addr_t status;
|
||||
pthread_attr_t attrib;
|
||||
int i;
|
||||
|
||||
|
||||
if (numthreads == 1) {
|
||||
func (NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
if (pthread_attr_create (&attrib) == -1)
|
||||
fprintf (stderr, "pthread_attr_create failed");
|
||||
if (pthread_attr_setstacksize (&attrib, 0x100000) == -1)
|
||||
fprintf (stderr, "pthread_attr_setstacksize failed");
|
||||
|
||||
for (i = 0; i < numthreads; i++) {
|
||||
if (pthread_create (&work_threads[i], attrib, (pthread_startroutine_t) func, (pthread_addr_t) i) == -1)
|
||||
fprintf (stderr, "pthread_create failed");
|
||||
}
|
||||
|
||||
for (i = 0; i < numthreads; i++) {
|
||||
if (pthread_join (work_threads[i], &status) == -1)
|
||||
fprintf (stderr, "pthread_join failed");
|
||||
}
|
||||
#else
|
||||
func (NULL);
|
||||
#endif
|
||||
}
|
231
tools/qflight/source/trace.c
Normal file
231
tools/qflight/source/trace.c
Normal file
|
@ -0,0 +1,231 @@
|
|||
/*
|
||||
trace.c
|
||||
|
||||
(description)
|
||||
|
||||
Copyright (C) 2002 Colin Thompson
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to:
|
||||
|
||||
Free Software Foundation, Inc.
|
||||
59 Temple Place - Suite 330
|
||||
Boston, MA 02111-1307, USA
|
||||
|
||||
*/
|
||||
static const char rcsid[] = "$Id$";
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_UNISTD_H
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
#ifdef HAVE_IO_H
|
||||
# include <io.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRING_H
|
||||
# include <string.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRINGS_H
|
||||
# include <strings.h>
|
||||
#endif
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "QF/qtypes.h"
|
||||
#include "QF/sys.h"
|
||||
#include "QF/dstring.h"
|
||||
#include "QF/vfile.h"
|
||||
#include "QF/vfs.h"
|
||||
#include "QF/bspfile.h"
|
||||
#include "QF/mathlib.h"
|
||||
#include "light.h"
|
||||
|
||||
typedef struct {
|
||||
int type;
|
||||
vec3_t normal;
|
||||
float dist;
|
||||
int children[2];
|
||||
int pad;
|
||||
} tnode_t;
|
||||
|
||||
typedef struct {
|
||||
vec3_t backpt;
|
||||
int side;
|
||||
int node;
|
||||
} tracestack_t;
|
||||
|
||||
tnode_t *tnodes, *tnode_p;
|
||||
|
||||
/*
|
||||
LINE TRACING
|
||||
|
||||
The major lighting operation is a point to point visibility test, performed
|
||||
by recursive subdivision of the line by the BSP tree.
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
MakeTnode
|
||||
|
||||
Converts the disk node structure into the efficient tracing structure
|
||||
*/
|
||||
void
|
||||
MakeTnode (int nodenum)
|
||||
{
|
||||
tnode_t *t;
|
||||
dplane_t *plane;
|
||||
int i;
|
||||
dnode_t *node;
|
||||
|
||||
|
||||
t = tnode_p++;
|
||||
|
||||
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;
|
||||
else {
|
||||
t->children[i] = tnode_p - tnodes;
|
||||
MakeTnode (node->children[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
MakeTnodes
|
||||
|
||||
Loads the node structure out of a .bsp file to be used for light occlusion
|
||||
*/
|
||||
void
|
||||
MakeTnodes (dmodel_t *bm)
|
||||
{
|
||||
tnode_p = tnodes = malloc (numnodes * sizeof (tnode_t));
|
||||
|
||||
MakeTnode (0);
|
||||
}
|
||||
|
||||
qboolean
|
||||
TestLine (vec3_t start, vec3_t stop)
|
||||
{
|
||||
int node;
|
||||
float front, back;
|
||||
tracestack_t *tstack_p;
|
||||
int 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) {
|
||||
while (node < 0 && node != CONTENTS_SOLID) {
|
||||
// pop up the stack for a back side
|
||||
tstack_p--;
|
||||
if (tstack_p < tracestack)
|
||||
return true;
|
||||
node = tstack_p->node;
|
||||
|
||||
// set the hit point for this plane
|
||||
frontx = backx;
|
||||
fronty = backy;
|
||||
frontz = backz;
|
||||
|
||||
// go down the back side
|
||||
backx = tstack_p->backpt[0];
|
||||
backy = tstack_p->backpt[1];
|
||||
backz = tstack_p->backpt[2];
|
||||
|
||||
node = tnodes[tstack_p->node].children[!tstack_p->side];
|
||||
}
|
||||
|
||||
if (node == CONTENTS_SOLID)
|
||||
return false; // DONE!
|
||||
|
||||
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];
|
||||
}
|
||||
}
|
||||
|
3
tools/qfvis/include/Makefile.am
Normal file
3
tools/qfvis/include/Makefile.am
Normal file
|
@ -0,0 +1,3 @@
|
|||
AUTOMAKE_OPTIONS= foreign
|
||||
|
||||
EXTRA_DIST= options.h vis.h
|
46
tools/qfvis/include/options.h
Normal file
46
tools/qfvis/include/options.h
Normal file
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
options.h
|
||||
|
||||
command line options handling
|
||||
|
||||
Copyright (C) 2002 Colin Thompson
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to:
|
||||
|
||||
Free Software Foundation, Inc.
|
||||
59 Temple Place - Suite 330
|
||||
Boston, MA 02111-1307, USA
|
||||
|
||||
$Id$
|
||||
*/
|
||||
|
||||
#ifndef __options_h
|
||||
#define __options_h
|
||||
|
||||
#include "QF/qtypes.h"
|
||||
|
||||
typedef struct {
|
||||
int verbosity; // 0=silent
|
||||
int threads;
|
||||
qboolean minimal;
|
||||
int level;
|
||||
char *bspfile;
|
||||
} options_t;
|
||||
|
||||
extern options_t options;
|
||||
int DecodeArgs (int argc, char **argv);
|
||||
void usage (int status);
|
||||
extern const char *this_program;
|
||||
#endif//__options_h
|
137
tools/qfvis/include/vis.h
Normal file
137
tools/qfvis/include/vis.h
Normal file
|
@ -0,0 +1,137 @@
|
|||
/*
|
||||
vis.h
|
||||
|
||||
PVS/PHS generation tool
|
||||
|
||||
Copyright (C) 2002 Colin Thompson
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to:
|
||||
|
||||
Free Software Foundation, Inc.
|
||||
59 Temple Place - Suite 330
|
||||
Boston, MA 02111-1307, USA
|
||||
|
||||
$Id$
|
||||
*/
|
||||
|
||||
#ifndef __vis_h
|
||||
#define __vis_h
|
||||
|
||||
#ifdef __alpha
|
||||
#include <pthread.h>
|
||||
extern pthread_mutex_t *my_mutex;
|
||||
#define LOCK pthread_mutex_lock (my_mutex)
|
||||
#define UNLOCK pthread_mutex_unlock (my_mutex)
|
||||
#else
|
||||
#define LOCK
|
||||
#define UNLOCK
|
||||
#endif
|
||||
|
||||
#define MAX_PORTALS 32768
|
||||
#define PORTALFILE "PRT1"
|
||||
#define ON_EPSILON 0.1
|
||||
#define MAX_POINTS_ON_WINDING 64
|
||||
#define MAX_PORTALS_ON_LEAF 128
|
||||
|
||||
typedef struct {
|
||||
vec3_t normal;
|
||||
float dist;
|
||||
} plane_t;
|
||||
|
||||
typedef struct {
|
||||
qboolean original; // don't free, it's part of the portal
|
||||
int numpoints;
|
||||
vec3_t points[8]; // variable sized
|
||||
} winding_t;
|
||||
|
||||
typedef enum {
|
||||
stat_none,
|
||||
stat_working,
|
||||
stat_done
|
||||
} vstatus_t;
|
||||
|
||||
typedef struct {
|
||||
plane_t plane; // normal pointing into neighbor
|
||||
int leaf; // neighbor
|
||||
winding_t *winding;
|
||||
vstatus_t status;
|
||||
byte *visbits;
|
||||
byte *mightsee;
|
||||
int nummightsee;
|
||||
int numcansee;
|
||||
} portal_t;
|
||||
|
||||
typedef struct seperating_plane_s {
|
||||
struct seperating_plane_s *next;
|
||||
plane_t plane; // from portal is on positive side
|
||||
} sep_t;
|
||||
|
||||
typedef struct passage_s {
|
||||
struct passage_s *next;
|
||||
int from, to; // leaf numbers
|
||||
sep_t *planes;
|
||||
} passage_t;
|
||||
|
||||
typedef struct leaf_s {
|
||||
int numportals;
|
||||
passage_t *passages;
|
||||
portal_t *portals[MAX_PORTALS_ON_LEAF];
|
||||
} leaf_t;
|
||||
|
||||
typedef struct pstack_s {
|
||||
struct pstack_s *next;
|
||||
leaf_t *leaf;
|
||||
portal_t *portal; // portal exiting
|
||||
winding_t *source, *pass;
|
||||
plane_t portalplane;
|
||||
byte *mightsee; // bit string
|
||||
} pstack_t;
|
||||
|
||||
typedef struct {
|
||||
byte *leafvis; // bit string
|
||||
portal_t *base;
|
||||
pstack_t pstack_head;
|
||||
} threaddata_t;
|
||||
|
||||
extern int numportals;
|
||||
extern int portalleafs;
|
||||
extern int c_portaltest;
|
||||
extern int c_portalpass;
|
||||
extern int c_portalcheck;
|
||||
extern int bitbytes;
|
||||
extern int bitlongs;
|
||||
|
||||
extern portal_t *portals;
|
||||
extern leaf_t *leafs;
|
||||
extern qboolean showgetleaf;
|
||||
extern byte *vismap, *vismap_p, *vismap_end; // past visfile
|
||||
extern byte *uncompressed;
|
||||
|
||||
extern int c_portalskip, c_leafskip;
|
||||
extern int c_vistest, c_mighttest;
|
||||
extern int c_chains;
|
||||
|
||||
void FreeWinding (winding_t *winding);
|
||||
winding_t *NewWinding (int points);
|
||||
winding_t *ClipWinding (winding_t *in, plane_t *split, qboolean keepon);
|
||||
winding_t *CopyWinding (winding_t *winding);
|
||||
|
||||
void LeafFlow (int leafnum);
|
||||
void BasePortalVis (void);
|
||||
void PortalFlow (portal_t *portal);
|
||||
void CalcAmbientSounds (void);
|
||||
|
||||
#endif// __vis_h
|
||||
|
14
tools/qfvis/source/Makefile.am
Normal file
14
tools/qfvis/source/Makefile.am
Normal file
|
@ -0,0 +1,14 @@
|
|||
AUTOMAKE_OPTIONS= foreign
|
||||
|
||||
QFVIS_LIBS=@QFVIS_LIBS@
|
||||
QFVIS_DEPS=@QFVIS_DEPS@
|
||||
QFVIS_INCS=@QFVIS_INCS@
|
||||
|
||||
INCLUDES= -I$(top_srcdir)/include $(QFVIS_INCS)
|
||||
|
||||
bin_PROGRAMS= qfvis
|
||||
|
||||
qfvis_SOURCES= flow.c options.c qfvis.c soundphs.c
|
||||
|
||||
qfvis_LDADD= $(QFVIS_LIBS)
|
||||
qfvis_DEPENDENCIES= $(QFVIS_DEPS)
|
457
tools/qfvis/source/flow.c
Normal file
457
tools/qfvis/source/flow.c
Normal file
|
@ -0,0 +1,457 @@
|
|||
/*
|
||||
flow.c
|
||||
|
||||
PVS PHS generator tool
|
||||
|
||||
Copyright (C) 2002 Colin Thompson
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to:
|
||||
|
||||
Free Software Foundation, Inc.
|
||||
59 Temple Place - Suite 330
|
||||
Boston, MA 02111-1307, USA
|
||||
|
||||
*/
|
||||
static const char rcsid[] =
|
||||
"$Id$";
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#include <getopt.h>
|
||||
#include <stdlib.h>
|
||||
#ifdef HAVE_UNISTD_H
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
#ifdef HAVE_IO_H
|
||||
# include <io.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRING_H
|
||||
# include <string.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRINGS_H
|
||||
# include <strings.h>
|
||||
#endif
|
||||
#include <getopt.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "QF/cmd.h"
|
||||
#include "QF/sys.h"
|
||||
#include "QF/vfile.h"
|
||||
#include "QF/vfs.h"
|
||||
#include "QF/bspfile.h"
|
||||
#include "QF/mathlib.h"
|
||||
#include "vis.h"
|
||||
#include "options.h"
|
||||
|
||||
int c_chains;
|
||||
int c_portalskip, c_leafskip;
|
||||
int c_vistest, c_mighttest;
|
||||
int c_leafsee, c_portalsee;
|
||||
int active;
|
||||
|
||||
byte portalsee[MAX_PORTALS];
|
||||
|
||||
|
||||
void
|
||||
CheckStack (leaf_t *leaf, threaddata_t *thread)
|
||||
{
|
||||
pstack_t *p;
|
||||
|
||||
|
||||
for (p = thread->pstack_head.next; p; p = p->next)
|
||||
if (p->leaf == leaf)
|
||||
fprintf (stderr, "CheckStack: leaf recursion");
|
||||
}
|
||||
|
||||
/*
|
||||
ClipToSeperators
|
||||
|
||||
Source, pass, and target are an ordering of portals.
|
||||
|
||||
Generates seperating planes candidates by taking two points from source and
|
||||
one point 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 the
|
||||
order goes source, pass, target. If the order goes pass, source, target then
|
||||
flipclip should be set.
|
||||
*/
|
||||
winding_t *
|
||||
ClipToSeperators (winding_t *source, winding_t *pass, winding_t *target,
|
||||
qboolean flipclip)
|
||||
{
|
||||
int i, j, k, l;
|
||||
plane_t plane;
|
||||
vec3_t v1, v2;
|
||||
float d;
|
||||
vec_t length;
|
||||
int counts[3];
|
||||
qboolean fliptest;
|
||||
|
||||
|
||||
// check all combinations
|
||||
for (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 side
|
||||
for (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 it
|
||||
length = 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
|
||||
fliptest = 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 side
|
||||
fliptest = false;
|
||||
break;
|
||||
} else if (d > ON_EPSILON) {
|
||||
// source is on the positive side, so we want all
|
||||
// pass and target on the negative side
|
||||
fliptest = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (k == source->numpoints)
|
||||
continue; // planar with source portal
|
||||
|
||||
// flip the normal if the source portal is backwards
|
||||
if (fliptest) {
|
||||
VectorSubtract (vec3_origin, plane.normal, plane.normal);
|
||||
plane.dist = -plane.dist;
|
||||
}
|
||||
|
||||
// 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]++;
|
||||
else
|
||||
counts[2]++;
|
||||
}
|
||||
if (k != pass->numpoints)
|
||||
continue; // points on negative side, not a seperating plane
|
||||
|
||||
if (!counts[0]) {
|
||||
continue; // planar with seperating plane
|
||||
}
|
||||
|
||||
// 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 = ClipWinding (target, &plane, false);
|
||||
if (!target)
|
||||
return NULL; // target is not visible
|
||||
}
|
||||
}
|
||||
return target;
|
||||
}
|
||||
|
||||
/*
|
||||
RecursiveLeafFlow
|
||||
|
||||
Flood fill through the leafs
|
||||
If src_portal is NULL, this is the originating leaf
|
||||
*/
|
||||
void
|
||||
RecursiveLeafFlow (int leafnum, threaddata_t *thread, pstack_t *prevstack)
|
||||
{
|
||||
int i, j;
|
||||
pstack_t stack;
|
||||
portal_t *p;
|
||||
plane_t backplane;
|
||||
winding_t *source, *target;
|
||||
leaf_t *leaf;
|
||||
long *test, *might, *vis;
|
||||
qboolean more;
|
||||
|
||||
|
||||
c_chains++;
|
||||
|
||||
leaf = &leafs[leafnum];
|
||||
CheckStack(leaf, thread);
|
||||
|
||||
// mark the leaf as visible
|
||||
if (!(thread->leafvis[leafnum >> 3] & (1 << (leafnum & 7)))) {
|
||||
thread->leafvis[leafnum >> 3] |= 1 << (leafnum & 7);
|
||||
thread->base->numcansee++;
|
||||
}
|
||||
|
||||
prevstack->next = &stack;
|
||||
stack.next = NULL;
|
||||
stack.leaf = leaf;
|
||||
stack.portal = NULL;
|
||||
stack.mightsee = malloc(bitbytes);
|
||||
might = (long *) stack.mightsee;
|
||||
vis = (long *) thread->leafvis;
|
||||
|
||||
// check all portals for flowing into other leafs
|
||||
for (i = 0; i < leaf->numportals; i++) {
|
||||
p = leaf->portals[i];
|
||||
|
||||
if (!(prevstack->mightsee[p->leaf >> 3] & (1 << (p->leaf & 7)))) {
|
||||
c_leafskip++;
|
||||
continue; // can't possibly see it
|
||||
}
|
||||
// if the portal can't see anything we haven't already seen, skip it
|
||||
if (p->status == stat_done) {
|
||||
c_vistest++;
|
||||
test = (long *) p->visbits;
|
||||
} else {
|
||||
c_mighttest++;
|
||||
test = (long *) p->mightsee;
|
||||
}
|
||||
more = false;
|
||||
for (j = 0; j < bitlongs; j++) {
|
||||
might[j] = ((long *) prevstack->mightsee)[j] & test[j];
|
||||
if (might[j] & ~vis[j])
|
||||
more = true;
|
||||
}
|
||||
|
||||
if (!more) { // can't see anything new
|
||||
c_portalskip++;
|
||||
continue;
|
||||
}
|
||||
// get plane of portal, point normal into the neighbor leaf
|
||||
stack.portalplane = p->plane;
|
||||
VectorSubtract (vec3_origin, p->plane.normal, backplane.normal);
|
||||
backplane.dist = -p->plane.dist;
|
||||
|
||||
if (VectorCompare (prevstack->portalplane.normal, backplane.normal))
|
||||
continue; // can't go out a coplanar face
|
||||
|
||||
c_portalcheck++;
|
||||
|
||||
stack.portal = p;
|
||||
stack.next = NULL;
|
||||
|
||||
target = ClipWinding(p->winding, &thread->pstack_head.portalplane, false);
|
||||
if (!target)
|
||||
continue;
|
||||
|
||||
if (!prevstack->pass) { // the second leaf can only be blocked if coplanar
|
||||
|
||||
stack.source = prevstack->source;
|
||||
stack.pass = target;
|
||||
RecursiveLeafFlow (p->leaf, thread, &stack);
|
||||
FreeWinding (target);
|
||||
continue;
|
||||
}
|
||||
|
||||
target = ClipWinding (target, &prevstack->portalplane, false);
|
||||
if (!target)
|
||||
continue;
|
||||
|
||||
source = CopyWinding (prevstack->source);
|
||||
|
||||
source = ClipWinding (source, &backplane, false);
|
||||
if (!source) {
|
||||
FreeWinding (target);
|
||||
continue;
|
||||
}
|
||||
|
||||
c_portaltest++;
|
||||
|
||||
if (options.level > 0) {
|
||||
target = ClipToSeperators (source, prevstack->pass, target, false);
|
||||
if (!target) {
|
||||
FreeWinding (source);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (options.level > 1) {
|
||||
target = ClipToSeperators (prevstack->pass, source, target, true);
|
||||
if (!target) {
|
||||
FreeWinding (source);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (options.level > 2) {
|
||||
source = ClipToSeperators (target, prevstack->pass, source, false);
|
||||
if (!source) {
|
||||
FreeWinding (target);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (options.level > 3) {
|
||||
source = ClipToSeperators (prevstack->pass, target, source, true);
|
||||
if (!source) {
|
||||
FreeWinding (target);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
stack.source = source;
|
||||
stack.pass = target;
|
||||
|
||||
c_portalpass++;
|
||||
|
||||
// flow through it for real
|
||||
RecursiveLeafFlow (p->leaf, thread, &stack);
|
||||
|
||||
FreeWinding (source);
|
||||
FreeWinding (target);
|
||||
}
|
||||
|
||||
free (stack.mightsee);
|
||||
}
|
||||
|
||||
void
|
||||
PortalFlow (portal_t *p)
|
||||
{
|
||||
threaddata_t data;
|
||||
|
||||
|
||||
if (p->status != stat_working)
|
||||
fprintf (stderr, "PortalFlow: reflowed");
|
||||
p->status = stat_working;
|
||||
|
||||
p->visbits = malloc (bitbytes);
|
||||
memset (p->visbits, 0, bitbytes);
|
||||
|
||||
memset (&data, 0, sizeof (data));
|
||||
data.leafvis = p->visbits;
|
||||
data.base = p;
|
||||
|
||||
data.pstack_head.portal = p;
|
||||
data.pstack_head.source = p->winding;
|
||||
data.pstack_head.portalplane = p->plane;
|
||||
data.pstack_head.mightsee = p->mightsee;
|
||||
|
||||
RecursiveLeafFlow (p->leaf, &data, &data.pstack_head);
|
||||
|
||||
p->status = stat_done;
|
||||
}
|
||||
|
||||
/*
|
||||
This is a rough first-order aproximation that is used to trivially reject some
|
||||
of the final calculations.
|
||||
*/
|
||||
void
|
||||
SimpleFlood (portal_t *srcportal, int leafnum)
|
||||
{
|
||||
int i;
|
||||
leaf_t *leaf;
|
||||
portal_t *p;
|
||||
|
||||
|
||||
if (srcportal->mightsee[leafnum >> 3] & (1 << (leafnum & 7)))
|
||||
return;
|
||||
srcportal->mightsee[leafnum >> 3] |= (1 << (leafnum & 7));
|
||||
c_leafsee++;
|
||||
|
||||
leaf = &leafs[leafnum];
|
||||
|
||||
for (i = 0; i < leaf->numportals; i++) {
|
||||
p = leaf->portals[i];
|
||||
if (!portalsee[p - portals])
|
||||
continue;
|
||||
SimpleFlood (srcportal, p->leaf);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
BasePortalVis (void)
|
||||
{
|
||||
int i, j, k;
|
||||
float d;
|
||||
portal_t *tp, *p;
|
||||
winding_t *winding;
|
||||
|
||||
|
||||
for (i = 0, p = portals; i < numportals * 2; i++, p++) {
|
||||
p->mightsee = malloc (bitbytes);
|
||||
memset (p->mightsee, 0, bitbytes);
|
||||
|
||||
c_portalsee = 0;
|
||||
memset (portalsee, 0, numportals * 2);
|
||||
|
||||
for (j = 0, tp = portals; j < numportals * 2; j++, tp++) {
|
||||
if (j == i)
|
||||
continue;
|
||||
|
||||
winding = tp->winding;
|
||||
for (k = 0; k < winding->numpoints; k++) {
|
||||
d = DotProduct (winding->points[k], p->plane.normal) - p->plane.dist;
|
||||
if (d > ON_EPSILON)
|
||||
break;
|
||||
}
|
||||
if (k == winding->numpoints)
|
||||
continue; // no points on front
|
||||
|
||||
winding = p->winding;
|
||||
for (k = 0; k < winding->numpoints; k++) {
|
||||
d = DotProduct (winding->points[k], tp->plane.normal) - tp->plane.dist;
|
||||
if (d < -ON_EPSILON)
|
||||
break;
|
||||
}
|
||||
if (k == winding->numpoints)
|
||||
continue; // no points on front
|
||||
|
||||
portalsee[j] = 1;
|
||||
c_portalsee++;
|
||||
}
|
||||
|
||||
c_leafsee = 0;
|
||||
SimpleFlood (p, p->leaf);
|
||||
p->nummightsee = c_leafsee;
|
||||
}
|
||||
}
|
||||
|
135
tools/qfvis/source/options.c
Normal file
135
tools/qfvis/source/options.c
Normal file
|
@ -0,0 +1,135 @@
|
|||
/*
|
||||
options.c
|
||||
|
||||
command line options handling
|
||||
|
||||
Copyright (C) 2002 Colin Thompson
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to:
|
||||
|
||||
Free Software Foundation, Inc.
|
||||
59 Temple Place - Suite 330
|
||||
Boston, MA 02111-1307, USA
|
||||
|
||||
*/
|
||||
static const char rcsid[] =
|
||||
"$Id$";
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_STRING_H
|
||||
# include <string.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRINGS_H
|
||||
# include <strings.h>
|
||||
#endif
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <getopt.h>
|
||||
|
||||
#include "options.h"
|
||||
|
||||
const char *this_program;
|
||||
|
||||
static struct option const long_options[] = {
|
||||
{"quiet", no_argument, 0, 'q'},
|
||||
{"verbose", no_argument, 0, 'v'},
|
||||
{"help", no_argument, 0, 'h'},
|
||||
{"version", no_argument, 0, 'V'},
|
||||
{"threads", no_argument, 0, 't'},
|
||||
{"minimal", no_argument, 0, 'm'},
|
||||
{"level", no_argument, 0, 'l'},
|
||||
{"file", required_argument, 0, 'f'},
|
||||
{NULL, 0, NULL, 0}
|
||||
};
|
||||
|
||||
static const char *short_options =
|
||||
"-" // magic option parsing mode doohicky (must come first)
|
||||
"q" // quiet
|
||||
"v" // verbose
|
||||
"h" // help
|
||||
"V" // version
|
||||
"t" // threads
|
||||
"m" // minimal vis
|
||||
"l" // level
|
||||
"f" // file
|
||||
;
|
||||
|
||||
void
|
||||
usage (int status)
|
||||
{
|
||||
printf ("%s - QuakeForge PVS/PHS generation tool\n", this_program);
|
||||
printf ("Usage: %s [options]\n", this_program);
|
||||
printf (
|
||||
"Options:\n"
|
||||
" -q, --quiet Inhibit usual output\n"
|
||||
" -v, --verbose Display more output than usual\n"
|
||||
" -h, --help Display this help and exit\n"
|
||||
" -V, --version Output version information and exit\n"
|
||||
" -t, --threads [num] Number of threads to use\n"
|
||||
" -m, --minimal Perform minimal vis'ing\n"
|
||||
" -l, --level Inhibit usual output\n"
|
||||
" -f, --file BSP file to vis\n\n"
|
||||
);
|
||||
exit (status);
|
||||
}
|
||||
|
||||
int
|
||||
DecodeArgs (int argc, char **argv)
|
||||
{
|
||||
int c;
|
||||
|
||||
options.verbosity = 0;
|
||||
options.bspfile = NULL;
|
||||
|
||||
while ((c = getopt_long (argc, argv, short_options, long_options, 0))
|
||||
!= EOF) {
|
||||
switch (c) {
|
||||
case 'q': // quiet
|
||||
options.verbosity -= 1;
|
||||
break;
|
||||
case 'v': // verbose
|
||||
options.verbosity += 1;
|
||||
break;
|
||||
case 'h': // help
|
||||
usage (0);
|
||||
break;
|
||||
case 'V': // version
|
||||
printf ("%s version %s\n", PACKAGE, VERSION);
|
||||
exit (0);
|
||||
break;
|
||||
case 't': // threads
|
||||
options.threads = atoi (optarg);
|
||||
break;
|
||||
case 'm': // minimal vis
|
||||
options.minimal = true;
|
||||
break;
|
||||
case 'l': // level
|
||||
options.level = atoi (optarg);
|
||||
break;
|
||||
case 'f': // set filename
|
||||
options.bspfile = strdup (optarg);
|
||||
break;
|
||||
default:
|
||||
usage (1);
|
||||
}
|
||||
}
|
||||
if ((!options.bspfile) && argv[optind] && *(argv[optind]))
|
||||
options.bspfile = strdup (argv[optind++]);
|
||||
|
||||
return optind;
|
||||
}
|
884
tools/qfvis/source/qfvis.c
Normal file
884
tools/qfvis/source/qfvis.c
Normal file
|
@ -0,0 +1,884 @@
|
|||
/*
|
||||
vis.c
|
||||
|
||||
PVS/PHS generation tool
|
||||
|
||||
Copyright (C) 2002 Colin Thompson
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to:
|
||||
|
||||
Free Software Foundation, Inc.
|
||||
59 Temple Place - Suite 330
|
||||
Boston, MA 02111-1307, USA
|
||||
|
||||
*/
|
||||
static const char rcsid[] =
|
||||
"$Id$";
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_UNISTD_H
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
#ifdef HAVE_IO_H
|
||||
# include <io.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRING_H
|
||||
# include <string.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRINGS_H
|
||||
# include <strings.h>
|
||||
#endif
|
||||
#include <getopt.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "QF/qtypes.h"
|
||||
#include "QF/cmd.h"
|
||||
#include "QF/sys.h"
|
||||
#include "QF/dstring.h"
|
||||
#include "QF/vfile.h"
|
||||
#include "QF/vfs.h"
|
||||
#include "QF/bspfile.h"
|
||||
#include "QF/mathlib.h"
|
||||
#include "vis.h"
|
||||
#include "options.h"
|
||||
|
||||
#define MAX_THREADS 4
|
||||
|
||||
#ifdef __alpha
|
||||
pthread_mutex_t *my_mutex;
|
||||
int numthreads = 4;
|
||||
#else
|
||||
int numthreads = 1;
|
||||
#endif
|
||||
|
||||
options_t options;
|
||||
|
||||
int numportals;
|
||||
int portalleafs;
|
||||
int c_portaltest;
|
||||
int c_portalpass;
|
||||
int c_portalcheck;
|
||||
int originalvismapsize;
|
||||
int totalvis;
|
||||
int count_sep;
|
||||
int bitbytes; // (portalleafs + 63)>>3
|
||||
int bitlongs;
|
||||
int leafon; // the next leaf to be given to a thread to process
|
||||
|
||||
portal_t *portals;
|
||||
leaf_t *leafs;
|
||||
qboolean showgetleaf = true;
|
||||
byte *vismap, *vismap_p, *vismap_end; // past visfile
|
||||
byte *uncompressed; // [bitbytes * portalleafs]
|
||||
|
||||
|
||||
#if 0
|
||||
void
|
||||
NormalizePlane (plane_t *dp)
|
||||
{
|
||||
vec_t ax, ay, az;
|
||||
|
||||
|
||||
if (dp->normal[0] == -1.0) {
|
||||
dp->normal[0] = 1.0;
|
||||
dp->dist = -dp->dist;
|
||||
return;
|
||||
}
|
||||
if (dp->normal[1] == -1.0) {
|
||||
dp->normal[1] = 1.0;
|
||||
dp->dist = -dp->dist;
|
||||
return;
|
||||
}
|
||||
if (dp->normal[2] == -1.0) {
|
||||
dp->normal[2] = 1.0;
|
||||
dp->dist = -dp->dist;
|
||||
return;
|
||||
}
|
||||
|
||||
ax = fabs (dp->normal[0]);
|
||||
ay = fabs (dp->normal[1]);
|
||||
az = fabs (dp->normal[2]);
|
||||
|
||||
if (ax >= ay && ax >= az) {
|
||||
if (dp->normal[0] < 0) {
|
||||
VectorSubtract (vec3_origin, dp->normal, dp->normal);
|
||||
dp->dist = -dp->dist;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (ay >= ax && ay >= az) {
|
||||
if (dp->normal[1] < 0) {
|
||||
VectorSubtract (vec3_origin, dp->normal, dp->normal);
|
||||
dp->dist = -dp->dist;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (dp->normal[2] < 0) {
|
||||
VectorSubtract (vec3_origin, dp->normal, dp->normal);
|
||||
dp->dist = -dp->dist;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
PlaneFromWinding (winding_t *winding, plane_t *plane)
|
||||
{
|
||||
vec3_t v1, v2;
|
||||
|
||||
|
||||
// calc plane
|
||||
VectorSubtract (winding->points[2], winding->points[1], v1);
|
||||
VectorSubtract (winding->points[0], winding->points[1], v2);
|
||||
CrossProduct (v2, v1, plane->normal);
|
||||
VectorNormalize (plane->normal);
|
||||
plane->dist = DotProduct (winding->points[0], plane->normal);
|
||||
}
|
||||
|
||||
winding_t *
|
||||
NewWinding (int points)
|
||||
{
|
||||
winding_t *winding;
|
||||
int size;
|
||||
|
||||
|
||||
if (points > MAX_POINTS_ON_WINDING)
|
||||
fprintf (stderr, "NewWinding: %i points", points);
|
||||
|
||||
size = (int) ((winding_t *) 0)->points[points];
|
||||
winding = malloc (size);
|
||||
memset (winding, 0, size);
|
||||
|
||||
return winding;
|
||||
}
|
||||
|
||||
void
|
||||
FreeWinding (winding_t *winding)
|
||||
{
|
||||
if (!winding->original)
|
||||
free (winding);
|
||||
}
|
||||
|
||||
// FIXME: currently unused
|
||||
void
|
||||
pw (winding_t *winding)
|
||||
{
|
||||
int i;
|
||||
|
||||
|
||||
for (i = 0; i < winding->numpoints; i++)
|
||||
printf ("(%5.1f, %5.1f, %5.1f)\n", winding->points[i][0],
|
||||
winding->points[i][1],
|
||||
winding->points[i][2]);
|
||||
}
|
||||
|
||||
// FIXME: currently unused
|
||||
void
|
||||
prl (leaf_t *leaf)
|
||||
{
|
||||
int i;
|
||||
portal_t *portal;
|
||||
plane_t plane;
|
||||
|
||||
|
||||
for (i = 0; i < leaf->numportals; i++) {
|
||||
portal = leaf->portals[i];
|
||||
plane = portal->plane;
|
||||
printf ("portal %4i to leaf %4i : %7.1f : (%4.1f, %4.1f, %4.1f)\n",
|
||||
(int) (portal - portals),
|
||||
portal->leaf, plane.dist,
|
||||
plane.normal[0], plane.normal[1], plane.normal[2]);
|
||||
}
|
||||
}
|
||||
|
||||
winding_t *
|
||||
CopyWinding (winding_t *winding)
|
||||
{
|
||||
int size;
|
||||
winding_t *copy;
|
||||
|
||||
|
||||
size = (int) ((winding_t *) 0)->points[winding->numpoints];
|
||||
copy = malloc (size);
|
||||
memcpy (copy, winding, size);
|
||||
copy->original = false;
|
||||
return copy;
|
||||
}
|
||||
|
||||
/*
|
||||
ClipWinding
|
||||
|
||||
Clips the winding to the plane, returning the new winding on the positive side
|
||||
Frees the input winding.
|
||||
If keepon is true, an exactly on-plane winding will be saved, otherwise
|
||||
it will be clipped away.
|
||||
*/
|
||||
winding_t *
|
||||
ClipWinding (winding_t *in, plane_t *split, qboolean keepon)
|
||||
{
|
||||
int i, j;
|
||||
vec_t dists[MAX_POINTS_ON_WINDING];
|
||||
int sides[MAX_POINTS_ON_WINDING];
|
||||
int counts[3];
|
||||
vec_t dot;
|
||||
vec_t *p1, *p2;
|
||||
vec3_t mid;
|
||||
winding_t *neww;
|
||||
int maxpts;
|
||||
|
||||
|
||||
counts[0] = counts[1] = counts[2] = 0;
|
||||
|
||||
// determine sides for each point
|
||||
for (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]]++;
|
||||
}
|
||||
sides[i] = sides[0];
|
||||
dists[i] = dists[0];
|
||||
|
||||
if (keepon && !counts[0] && !counts[1])
|
||||
return in;
|
||||
|
||||
if (!counts[0]) {
|
||||
FreeWinding (in);
|
||||
return NULL;
|
||||
}
|
||||
if (!counts[1])
|
||||
return in;
|
||||
|
||||
maxpts = in->numpoints + 4; // can't use counts[0] + 2 because
|
||||
// of fp grouping errors
|
||||
neww = NewWinding (maxpts);
|
||||
|
||||
for (i = 0; i < in->numpoints; i++) {
|
||||
p1 = in->points[i];
|
||||
|
||||
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;
|
||||
|
||||
// generate a split point
|
||||
p2 = 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 possible
|
||||
if (split->normal[j] == 1)
|
||||
mid[j] = split->dist;
|
||||
else if (split->normal[j] == -1)
|
||||
mid[j] = -split->dist;
|
||||
else
|
||||
mid[j] = p1[j] + dot * (p2[j] - p1[j]);
|
||||
}
|
||||
|
||||
VectorCopy (mid, neww->points[neww->numpoints]);
|
||||
neww->numpoints++;
|
||||
}
|
||||
|
||||
if (neww->numpoints > maxpts)
|
||||
fprintf (stderr, "ClipWinding: points exceeded estimate");
|
||||
// free the original winding
|
||||
FreeWinding (in);
|
||||
|
||||
return neww;
|
||||
}
|
||||
|
||||
/*
|
||||
GetNextPortal
|
||||
|
||||
Returns the next portal for a thread to work on
|
||||
Returns the portals from the least complex, so the later ones can reuse
|
||||
the earlier information.
|
||||
*/
|
||||
portal_t *
|
||||
GetNextPortal (void)
|
||||
{
|
||||
int j;
|
||||
portal_t *p, *tp;
|
||||
int min;
|
||||
|
||||
|
||||
LOCK;
|
||||
|
||||
min = 99999;
|
||||
p = NULL;
|
||||
|
||||
for (j = 0, tp = portals; j < numportals * 2; j++, tp++) {
|
||||
if (tp->nummightsee < min && tp->status == stat_none) {
|
||||
min = tp->nummightsee;
|
||||
p = tp;
|
||||
}
|
||||
}
|
||||
|
||||
if (p)
|
||||
p->status = stat_working;
|
||||
|
||||
UNLOCK;
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
#ifdef __alpha
|
||||
pthread_addr_t LeafThread (pthread_addr_t thread)
|
||||
#else
|
||||
void *LeafThread (int thread)
|
||||
#endif
|
||||
{
|
||||
portal_t *portal;
|
||||
|
||||
|
||||
do {
|
||||
portal = GetNextPortal ();
|
||||
if (!portal)
|
||||
break;
|
||||
|
||||
PortalFlow (portal);
|
||||
|
||||
if (options.verbosity >= 0)
|
||||
printf ("portal:%4i mightsee:%4i cansee:%4i\n",
|
||||
(int) (portal - portals),
|
||||
portal->nummightsee,
|
||||
portal->numcansee);
|
||||
} while (1);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int
|
||||
CompressRow (byte *vis, byte *dest)
|
||||
{
|
||||
int j;
|
||||
int rep;
|
||||
int visrow;
|
||||
byte *dest_p;
|
||||
|
||||
|
||||
dest_p = dest;
|
||||
visrow = (portalleafs + 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;
|
||||
else
|
||||
rep++;
|
||||
*dest_p++ = rep;
|
||||
j--;
|
||||
}
|
||||
|
||||
return dest_p - dest;
|
||||
}
|
||||
|
||||
/*
|
||||
LeafFlow
|
||||
|
||||
Builds the entire visibility list for a leaf
|
||||
*/
|
||||
void
|
||||
LeafFlow (int leafnum)
|
||||
{
|
||||
int i, j;
|
||||
leaf_t *leaf;
|
||||
byte *outbuffer;
|
||||
byte compressed[MAX_MAP_LEAFS / 8];
|
||||
int numvis;
|
||||
byte *dest;
|
||||
portal_t *portal;
|
||||
|
||||
|
||||
// flow through all portals, collecting visible bits
|
||||
outbuffer = uncompressed + leafnum * bitbytes;
|
||||
leaf = &leafs[leafnum];
|
||||
for (i = 0; i < leaf->numportals; i++) {
|
||||
portal = leaf->portals[i];
|
||||
if (portal->status != stat_done)
|
||||
fprintf (stderr, "portal not done");
|
||||
for (j = 0; j < bitbytes; j++)
|
||||
outbuffer[j] |= portal->visbits[j];
|
||||
}
|
||||
|
||||
if (outbuffer[leafnum >> 3] & (1 << (leafnum & 7)))
|
||||
fprintf (stderr, "Leaf portals saw into leaf");
|
||||
|
||||
outbuffer[leafnum >> 3] |= (1 << (leafnum & 7));
|
||||
|
||||
numvis = 0;
|
||||
for (i = 0; i < portalleafs; i++)
|
||||
if (outbuffer[i >> 3] & (1 << (i & 3)))
|
||||
numvis++;
|
||||
|
||||
// compress the bit string
|
||||
if (options.verbosity >= 0)
|
||||
printf ("leaf %4i : %4i visible\n", leafnum, numvis);
|
||||
totalvis += numvis;
|
||||
|
||||
#if 0
|
||||
i = (portalleafs + 7) >> 3;
|
||||
memcpy (compressed, outbuffer, i);
|
||||
#else
|
||||
i = CompressRow (outbuffer, compressed);
|
||||
#endif
|
||||
|
||||
dest = vismap_p;
|
||||
vismap_p += i;
|
||||
|
||||
if (vismap_p > vismap_end)
|
||||
fprintf (stderr, "Vismap expansion overflow");
|
||||
|
||||
dleafs[leafnum + 1].visofs = dest - vismap; // leaf 0 is a common solid
|
||||
|
||||
memcpy (dest, compressed, i);
|
||||
}
|
||||
|
||||
void
|
||||
CalcPortalVis (void)
|
||||
{
|
||||
int i;
|
||||
|
||||
|
||||
// fastvis just uses mightsee for a very loose bound
|
||||
if (options.minimal) {
|
||||
for (i = 0; i < numportals * 2; i++) {
|
||||
portals[i].visbits = portals[i].mightsee;
|
||||
portals[i].status = stat_done;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
leafon = 0;
|
||||
|
||||
#ifdef __alpha
|
||||
{
|
||||
pthread_t work_threads[MAX_THREADS];
|
||||
pthread_addr_t status;
|
||||
pthread_attr_t attrib;
|
||||
pthread_mutexattr_t mattrib;
|
||||
|
||||
|
||||
my_mutex = malloc (sizeof (*my_mutex));
|
||||
if (pthread_mutexattr_create (&mattrib) == -1)
|
||||
fprintf (stderr, "pthread_mutex_attr_create failed");
|
||||
if (pthread_mutexattr_setkind_np (&mattrib, MUTEX_FAST_NP) == -1)
|
||||
fprintf (stderr, "pthread_mutexattr_setkind_np failed");
|
||||
if (pthread_mutex_init (my_mutex, mattrib) == -1)
|
||||
fprintf (stderr, "pthread_mutex_init failed");
|
||||
if (pthread_attr_create (&attrib) == -1)
|
||||
fprintf (stderr, "pthread_attr_create failed");
|
||||
if (pthread_attr_setstacksize (&attrib, 0x100000) == -1)
|
||||
fprintf (stderr, "pthread_attr_setstacksize failed");
|
||||
for (i = 0; i < numthreads; i++) {
|
||||
if (pthread_create (&work_threads[i], attrib, LeafThread, (pthread_addr_t) i) == -1)
|
||||
fprintf (stderr, "pthread_create failed");
|
||||
}
|
||||
|
||||
for (i = 0; i < numthreads; i++) {
|
||||
if (pthread_join (work_threads[i], &status) == -1)
|
||||
fprintf (stderr, "pthread_join failed");
|
||||
}
|
||||
|
||||
if (pthread_mutex_destroy (my_mutex) == -1)
|
||||
fprintf (stderr, "pthread_mutex_destroy failed");
|
||||
}
|
||||
#else
|
||||
LeafThread (0);
|
||||
#endif
|
||||
|
||||
if (options.verbosity >= 0) {
|
||||
printf ("portalcheck: %i portaltest: %i portalpass: %i\n", c_portalcheck, c_portaltest, c_portalpass);
|
||||
printf ("c_vistest: %i c_mighttest: %i\n", c_vistest, c_mighttest);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
CalcVis (void)
|
||||
{
|
||||
int i;
|
||||
|
||||
|
||||
BasePortalVis ();
|
||||
|
||||
CalcPortalVis ();
|
||||
|
||||
// assemble the leaf vis lists by oring and compressing the portal lists
|
||||
for (i = 0; i < portalleafs; i++)
|
||||
LeafFlow (i);
|
||||
|
||||
if (options.verbosity >= 0)
|
||||
printf ("average leafs visible: %i\n", totalvis / portalleafs);
|
||||
}
|
||||
|
||||
qboolean
|
||||
PlaneCompare (plane_t *p1, plane_t *p2)
|
||||
{
|
||||
int i;
|
||||
|
||||
|
||||
if (fabs (p1->dist - p2->dist) > 0.01)
|
||||
return false;
|
||||
|
||||
for (i = 0; i < 3; i++)
|
||||
if (fabs (p1->normal[i] - p2->normal[i]) > 0.001)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
sep_t *
|
||||
Findpassages (winding_t *source, winding_t *pass)
|
||||
{
|
||||
int i, j, k, l;
|
||||
plane_t plane;
|
||||
vec3_t v1, v2;
|
||||
float d;
|
||||
double length;
|
||||
int counts[3];
|
||||
qboolean fliptest;
|
||||
sep_t *sep, *list;
|
||||
|
||||
|
||||
list = NULL;
|
||||
|
||||
// check all combinations
|
||||
for (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 side
|
||||
for (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 it
|
||||
length = 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
|
||||
fliptest = 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 side
|
||||
fliptest = false;
|
||||
break;
|
||||
} else if (d > ON_EPSILON) {
|
||||
// source is on the positive side, so we want all
|
||||
// pass and target on the negative side
|
||||
fliptest = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (k == source->numpoints)
|
||||
continue; // planar with source portal
|
||||
|
||||
// flip the normal if the source portal is backwards
|
||||
if (fliptest) {
|
||||
VectorSubtract (vec3_origin, plane.normal, plane.normal);
|
||||
plane.dist = -plane.dist;
|
||||
}
|
||||
|
||||
// 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]++;
|
||||
else
|
||||
counts[2]++;
|
||||
}
|
||||
if (k != pass->numpoints)
|
||||
continue; // points on negative side, not a seperating plane
|
||||
|
||||
if (!counts[0])
|
||||
continue; // planar with pass portal
|
||||
|
||||
// save this out
|
||||
count_sep++;
|
||||
|
||||
sep = malloc (sizeof (*sep));
|
||||
sep->next = list;
|
||||
list = sep;
|
||||
sep->plane = plane;
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
void
|
||||
CalcPassages (void)
|
||||
{
|
||||
int i, j, k;
|
||||
int count, count2;
|
||||
leaf_t *leaf;
|
||||
portal_t *p1, *p2;
|
||||
sep_t *sep;
|
||||
passage_t *passages;
|
||||
|
||||
|
||||
if (options.verbosity >= 0)
|
||||
printf ("building passages...\n");
|
||||
|
||||
count = count2 = 0;
|
||||
for (i = 0; i < portalleafs; i++) {
|
||||
leaf = &leafs[i];
|
||||
|
||||
for (j = 0; j < leaf->numportals; j++) {
|
||||
p1 = leaf->portals[j];
|
||||
for (k = 0; k < leaf->numportals; k++) {
|
||||
if (k == j)
|
||||
continue;
|
||||
|
||||
count++;
|
||||
p2 = leaf->portals[k];
|
||||
|
||||
// definately can't see into a coplanar portal
|
||||
if (PlaneCompare (&p1->plane, &p2->plane))
|
||||
continue;
|
||||
|
||||
count2++;
|
||||
|
||||
sep = Findpassages (p1->winding, p2->winding);
|
||||
if (!sep) {
|
||||
count_sep++;
|
||||
sep = malloc (sizeof (*sep));
|
||||
sep->next = NULL;
|
||||
sep->plane = p1->plane;
|
||||
}
|
||||
passages = malloc (sizeof (*passages));
|
||||
passages->planes = sep;
|
||||
passages->from = p1->leaf;
|
||||
passages->to = p2->leaf;
|
||||
passages->next = leaf->passages;
|
||||
leaf->passages = passages;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (options.verbosity >= 0) {
|
||||
printf ("numpassages: %i (%i)\n", count2, count);
|
||||
printf ("total passages: %i\n", count_sep);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
LoadPortals (char *name)
|
||||
{
|
||||
int i, j;
|
||||
portal_t *portal;
|
||||
leaf_t *leaf;
|
||||
winding_t *winding;
|
||||
plane_t plane;
|
||||
char magic[80];
|
||||
FILE *f;
|
||||
int numpoints;
|
||||
int leafnums[2];
|
||||
|
||||
|
||||
if (!strcmp (name, "-"))
|
||||
f = stdin;
|
||||
else {
|
||||
f = fopen (name, "r");
|
||||
if (!f) {
|
||||
printf ("LoadPortals: couldn't read %s\n", name);
|
||||
printf ("No vising performed.\n");
|
||||
exit (1);
|
||||
}
|
||||
}
|
||||
|
||||
if (fscanf (f, "%79s\n%i\n%i\n", magic, &portalleafs, &numportals) != 3)
|
||||
fprintf (stderr, "LoadPortals: failed to read header");
|
||||
if (strcmp (magic, PORTALFILE))
|
||||
fprintf (stderr, "LoadPortals: not a portal file");
|
||||
|
||||
if (options.verbosity >= 0) {
|
||||
printf ("%4i portalleafs\n", portalleafs);
|
||||
printf ("%4i numportals\n", numportals);
|
||||
}
|
||||
|
||||
bitbytes = ((portalleafs + 63) & ~63) >> 3;
|
||||
bitlongs = bitbytes / sizeof (long);
|
||||
|
||||
// each file portal is split into two memory portals
|
||||
portals = malloc (2 * numportals * sizeof (portal_t));
|
||||
memset (portals, 0, 2 * numportals * sizeof (portal_t));
|
||||
|
||||
leafs = malloc (portalleafs * sizeof (leaf_t));
|
||||
memset (leafs, 0, portalleafs * sizeof (leaf_t));
|
||||
|
||||
originalvismapsize = portalleafs * ((portalleafs + 7) / 8);
|
||||
|
||||
vismap = vismap_p = dvisdata;
|
||||
vismap_end = vismap + MAX_MAP_VISIBILITY;
|
||||
|
||||
for (i = 0, portal = portals; i < numportals; i++) {
|
||||
if (fscanf (f, "%i %i %i ", &numpoints, &leafnums[0], &leafnums[1]) != 3)
|
||||
fprintf (stderr, "LoadPortals: reading portal %i", i);
|
||||
if (numpoints > MAX_POINTS_ON_WINDING)
|
||||
fprintf (stderr, "LoadPortals: portal %i has too many points", i);
|
||||
if ((unsigned) leafnums[0] > portalleafs || (unsigned) leafnums[1] > portalleafs)
|
||||
fprintf (stderr, "LoadPortals: reading portal %i", i);
|
||||
|
||||
winding = portal->winding = NewWinding (numpoints);
|
||||
winding->original = true;
|
||||
winding->numpoints = numpoints;
|
||||
|
||||
for (j = 0; j < numpoints; j++) {
|
||||
|
||||
double v[3];
|
||||
int k;
|
||||
|
||||
// scanf into double, then assign to vec_t
|
||||
if (fscanf (f, "(%lf %lf %lf ) ", &v[0], &v[1], &v[2]) != 3)
|
||||
fprintf (stderr, "LoadPortals: reading portal %i", i);
|
||||
|
||||
for (k = 0; k < 3; k++)
|
||||
winding->points[j][k] = v[k];
|
||||
}
|
||||
|
||||
fscanf (f, "\n");
|
||||
|
||||
// calc plane
|
||||
PlaneFromWinding (winding, &plane);
|
||||
|
||||
// create forward portal
|
||||
leaf = &leafs[leafnums[0]];
|
||||
if (leaf->numportals == MAX_PORTALS_ON_LEAF)
|
||||
fprintf (stderr, "Leaf with too many portals");
|
||||
leaf->portals[leaf->numportals] = portal;
|
||||
leaf->numportals++;
|
||||
|
||||
portal->winding = winding;
|
||||
VectorSubtract (vec3_origin, plane.normal, portal->plane.normal);
|
||||
portal->plane.dist = -plane.dist;
|
||||
portal->leaf = leafnums[1];
|
||||
portal++;
|
||||
|
||||
// create backwards portal
|
||||
leaf = &leafs[leafnums[1]];
|
||||
if (leaf->numportals == MAX_PORTALS_ON_LEAF)
|
||||
fprintf (stderr, "Leaf with too many portals");
|
||||
leaf->portals[leaf->numportals] = portal;
|
||||
leaf->numportals++;
|
||||
|
||||
portal->winding = winding;
|
||||
portal->plane = plane;
|
||||
portal->leaf = leafnums[0];
|
||||
portal++;
|
||||
}
|
||||
fclose (f);
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char **argv)
|
||||
{
|
||||
double start, stop;
|
||||
|
||||
start = Sys_DoubleTime ();
|
||||
|
||||
this_program = argv[0];
|
||||
|
||||
DecodeArgs (argc, argv);
|
||||
|
||||
if (!options.bspfile) {
|
||||
fprintf (stderr, "%s: no bsp file specified.\n", this_program);
|
||||
usage (1);
|
||||
}
|
||||
|
||||
COM_StripExtension (options.bspfile, options.bspfile);
|
||||
COM_DefaultExtension (options.bspfile, ".bsp");
|
||||
|
||||
LoadBSPFile (options.bspfile);
|
||||
|
||||
// LoadPortals (portalfile);
|
||||
|
||||
// uncompressed = malloc (bitbytes * portalleafs);
|
||||
// memset (uncompressed, 0, bitbytes * portalleafs);
|
||||
|
||||
CalcVis ();
|
||||
|
||||
if (options.verbosity >= 0)
|
||||
printf ("c_chains: %i\n", c_chains);
|
||||
|
||||
visdatasize = vismap_p - dvisdata;
|
||||
if (options.verbosity >= 0)
|
||||
printf ("visdatasize:%i compressed from %i\n", visdatasize, originalvismapsize);
|
||||
|
||||
CalcAmbientSounds ();
|
||||
|
||||
WriteBSPFile (options.bspfile);
|
||||
|
||||
stop = Sys_DoubleTime ();
|
||||
|
||||
if (options.verbosity >= 0)
|
||||
printf ("%5.1f seconds elapsed\n", stop - start);
|
||||
|
||||
return 0;
|
||||
}
|
176
tools/qfvis/source/soundphs.c
Normal file
176
tools/qfvis/source/soundphs.c
Normal file
|
@ -0,0 +1,176 @@
|
|||
/*
|
||||
soundphs.c
|
||||
|
||||
PHS (possible hearable set) for ambient brushes
|
||||
|
||||
Copyright (C) 2002 Colin Thompson
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to:
|
||||
|
||||
Free Software Foundation, Inc.
|
||||
59 Temple Place - Suite 330
|
||||
Boston, MA 02111-1307, USA
|
||||
|
||||
*/
|
||||
static const char rcsid[] =
|
||||
"$Id$";
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#include <getopt.h>
|
||||
#include <stdlib.h>
|
||||
#ifdef HAVE_UNISTD_H
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
#ifdef HAVE_IO_H
|
||||
# include <io.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRING_H
|
||||
# include <string.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRINGS_H
|
||||
# include <strings.h>
|
||||
#endif
|
||||
#include <getopt.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "QF/cmd.h"
|
||||
#include "QF/sys.h"
|
||||
#include "QF/vfile.h"
|
||||
#include "QF/vfs.h"
|
||||
#include "QF/bspfile.h"
|
||||
#include "QF/mathlib.h"
|
||||
#include "vis.h"
|
||||
|
||||
/*
|
||||
Some textures (sky, water, slime, lava) are considered ambient sound emiters.
|
||||
Find an aproximate distance to the nearest emiter of each class for each leaf.
|
||||
*/
|
||||
|
||||
void
|
||||
SurfaceBBox (dface_t *s, vec3_t mins, vec3_t maxs)
|
||||
{
|
||||
int i, j;
|
||||
int e;
|
||||
int vi;
|
||||
float *v;
|
||||
|
||||
mins[0] = mins[1] = 999999;
|
||||
maxs[0] = maxs[1] = -99999;
|
||||
|
||||
for (i = 0; i < s->numedges; i++) {
|
||||
e = dsurfedges[s->firstedge + i];
|
||||
if (e >= 0)
|
||||
vi = dedges[e].v[0];
|
||||
else
|
||||
vi = dedges[-e].v[1];
|
||||
v = dvertexes[vi].point;
|
||||
|
||||
for (j = 0; j < 3; j++) {
|
||||
if (v[j] < mins[j])
|
||||
mins[j] = v[j];
|
||||
if (v[j] > maxs[j])
|
||||
maxs[j] = v[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CalcAmbientSounds (void)
|
||||
{
|
||||
int i, j, k, l;
|
||||
dleaf_t *leaf, *hit;
|
||||
byte *vis;
|
||||
dface_t *surf;
|
||||
vec3_t mins, maxs;
|
||||
float d, maxd;
|
||||
int ambient_type;
|
||||
texinfo_t *info;
|
||||
miptex_t *miptex;
|
||||
int ofs;
|
||||
float dists[NUM_AMBIENTS];
|
||||
float vol;
|
||||
|
||||
for (i = 0; i < portalleafs; i++) {
|
||||
leaf = &dleafs[i + 1];
|
||||
|
||||
// clear ambients
|
||||
for (j = 0; j < NUM_AMBIENTS; j++)
|
||||
dists[j] = 1020;
|
||||
|
||||
vis = &uncompressed[i * bitbytes];
|
||||
|
||||
for (j = 0; j < portalleafs; j++) {
|
||||
if (!(vis[j >> 3] & (1 << (j & 7))))
|
||||
continue;
|
||||
|
||||
// check this leaf for sound textures
|
||||
hit = &dleafs[j + 1];
|
||||
|
||||
for (k = 0; k < hit->nummarksurfaces; k++) {
|
||||
surf = &dfaces[dmarksurfaces[hit->firstmarksurface + k]];
|
||||
info = &texinfo[surf->texinfo];
|
||||
ofs = ((dmiptexlump_t *) dtexdata)->dataofs[info->miptex];
|
||||
miptex = (miptex_t *) (&dtexdata[ofs]);
|
||||
|
||||
if (!strncasecmp (miptex->name, "*water", 6))
|
||||
ambient_type = AMBIENT_WATER;
|
||||
else if (!strncasecmp (miptex->name, "sky", 3))
|
||||
ambient_type = AMBIENT_SKY;
|
||||
else if (!strncasecmp (miptex->name, "*slime", 6))
|
||||
ambient_type = AMBIENT_WATER;
|
||||
else if (!strncasecmp (miptex->name, "*lava", 6))
|
||||
ambient_type = AMBIENT_LAVA;
|
||||
else if (!strncasecmp (miptex->name, "*04water", 8))
|
||||
ambient_type = AMBIENT_WATER;
|
||||
else
|
||||
continue;
|
||||
|
||||
// find distance from source leaf to polygon
|
||||
SurfaceBBox (surf, mins, maxs);
|
||||
maxd = 0;
|
||||
for (l = 0; l < 3; l++) {
|
||||
if (mins[l] > leaf->maxs[l])
|
||||
d = mins[l] - leaf->maxs[l];
|
||||
else if (maxs[l] < leaf->mins[l])
|
||||
d = leaf->mins[l] - mins[l];
|
||||
else
|
||||
d = 0;
|
||||
if (d > maxd)
|
||||
maxd = d;
|
||||
}
|
||||
|
||||
maxd = 0.25;
|
||||
if (maxd < dists[ambient_type])
|
||||
dists[ambient_type] = maxd;
|
||||
}
|
||||
}
|
||||
|
||||
for (j = 0; j < NUM_AMBIENTS; j++) {
|
||||
if (dists[j] < 100)
|
||||
vol = 1.0;
|
||||
else {
|
||||
vol = 1.0 - dists[2] * 0.002;
|
||||
if (vol < 0)
|
||||
vol = 0;
|
||||
}
|
||||
leaf->ambient_level[j] = vol * 255;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue