The Quake III Arena sources as originally released under the GPL license on August 20, 2005.
This commit is contained in:
commit
dbe4ddb103
1409 changed files with 806066 additions and 0 deletions
861
q3map/brush.c
Normal file
861
q3map/brush.c
Normal file
|
@ -0,0 +1,861 @@
|
|||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 1999-2005 Id Software, Inc.
|
||||
|
||||
This file is part of Quake III Arena source code.
|
||||
|
||||
Quake III Arena source code 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.
|
||||
|
||||
Quake III Arena source code 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 Foobar; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
#include "qbsp.h"
|
||||
|
||||
|
||||
int c_active_brushes;
|
||||
|
||||
int c_nodes;
|
||||
|
||||
// if a brush just barely pokes onto the other side,
|
||||
// let it slide by without chopping
|
||||
#define PLANESIDE_EPSILON 0.001
|
||||
//0.1
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
================
|
||||
CountBrushList
|
||||
================
|
||||
*/
|
||||
int CountBrushList (bspbrush_t *brushes)
|
||||
{
|
||||
int c;
|
||||
|
||||
c = 0;
|
||||
for ( ; brushes ; brushes = brushes->next)
|
||||
c++;
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
================
|
||||
AllocBrush
|
||||
================
|
||||
*/
|
||||
bspbrush_t *AllocBrush (int numsides)
|
||||
{
|
||||
bspbrush_t *bb;
|
||||
int c;
|
||||
|
||||
c = (int)&(((bspbrush_t *)0)->sides[numsides]);
|
||||
bb = malloc(c);
|
||||
memset (bb, 0, c);
|
||||
if (numthreads == 1)
|
||||
c_active_brushes++;
|
||||
return bb;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
FreeBrush
|
||||
================
|
||||
*/
|
||||
void FreeBrush (bspbrush_t *brushes)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i=0 ; i<brushes->numsides ; i++)
|
||||
if (brushes->sides[i].winding)
|
||||
FreeWinding(brushes->sides[i].winding);
|
||||
free (brushes);
|
||||
if (numthreads == 1)
|
||||
c_active_brushes--;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
================
|
||||
FreeBrushList
|
||||
================
|
||||
*/
|
||||
void FreeBrushList (bspbrush_t *brushes)
|
||||
{
|
||||
bspbrush_t *next;
|
||||
|
||||
for ( ; brushes ; brushes = next)
|
||||
{
|
||||
next = brushes->next;
|
||||
|
||||
FreeBrush (brushes);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
CopyBrush
|
||||
|
||||
Duplicates the brush, the sides, and the windings
|
||||
==================
|
||||
*/
|
||||
bspbrush_t *CopyBrush (bspbrush_t *brush)
|
||||
{
|
||||
bspbrush_t *newbrush;
|
||||
int size;
|
||||
int i;
|
||||
|
||||
size = (int)&(((bspbrush_t *)0)->sides[brush->numsides]);
|
||||
|
||||
newbrush = AllocBrush (brush->numsides);
|
||||
memcpy (newbrush, brush, size);
|
||||
|
||||
for (i=0 ; i<brush->numsides ; i++)
|
||||
{
|
||||
if (brush->sides[i].winding)
|
||||
newbrush->sides[i].winding = CopyWinding (brush->sides[i].winding);
|
||||
}
|
||||
|
||||
return newbrush;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
================
|
||||
DrawBrushList
|
||||
================
|
||||
*/
|
||||
void DrawBrushList (bspbrush_t *brush)
|
||||
{
|
||||
int i;
|
||||
side_t *s;
|
||||
|
||||
GLS_BeginScene ();
|
||||
for ( ; brush ; brush=brush->next)
|
||||
{
|
||||
for (i=0 ; i<brush->numsides ; i++)
|
||||
{
|
||||
s = &brush->sides[i];
|
||||
if (!s->winding)
|
||||
continue;
|
||||
GLS_Winding (s->winding, 0);
|
||||
}
|
||||
}
|
||||
GLS_EndScene ();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
================
|
||||
WriteBrushList
|
||||
================
|
||||
*/
|
||||
void WriteBrushList (char *name, bspbrush_t *brush, qboolean onlyvis)
|
||||
{
|
||||
int i;
|
||||
side_t *s;
|
||||
FILE *f;
|
||||
|
||||
qprintf ("writing %s\n", name);
|
||||
f = SafeOpenWrite (name);
|
||||
|
||||
for ( ; brush ; brush=brush->next)
|
||||
{
|
||||
for (i=0 ; i<brush->numsides ; i++)
|
||||
{
|
||||
s = &brush->sides[i];
|
||||
if (!s->winding)
|
||||
continue;
|
||||
if (onlyvis && !s->visible)
|
||||
continue;
|
||||
OutputWinding (brush->sides[i].winding, f);
|
||||
}
|
||||
}
|
||||
|
||||
fclose (f);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=============
|
||||
PrintBrush
|
||||
=============
|
||||
*/
|
||||
void PrintBrush (bspbrush_t *brush)
|
||||
{
|
||||
int i;
|
||||
|
||||
_printf ("brush: %p\n", brush);
|
||||
for (i=0;i<brush->numsides ; i++)
|
||||
{
|
||||
pw(brush->sides[i].winding);
|
||||
_printf ("\n");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
BoundBrush
|
||||
|
||||
Sets the mins/maxs based on the windings
|
||||
returns false if the brush doesn't enclose a valid volume
|
||||
==================
|
||||
*/
|
||||
qboolean BoundBrush (bspbrush_t *brush)
|
||||
{
|
||||
int i, j;
|
||||
winding_t *w;
|
||||
|
||||
ClearBounds (brush->mins, brush->maxs);
|
||||
for (i=0 ; i<brush->numsides ; i++)
|
||||
{
|
||||
w = brush->sides[i].winding;
|
||||
if (!w)
|
||||
continue;
|
||||
for (j=0 ; j<w->numpoints ; j++)
|
||||
AddPointToBounds (w->p[j], brush->mins, brush->maxs);
|
||||
}
|
||||
|
||||
for (i=0 ; i<3 ; i++) {
|
||||
if (brush->mins[i] < MIN_WORLD_COORD || brush->maxs[i] > MAX_WORLD_COORD
|
||||
|| brush->mins[i] >= brush->maxs[i] ) {
|
||||
return qfalse;
|
||||
}
|
||||
}
|
||||
|
||||
return qtrue;
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
CreateBrushWindings
|
||||
|
||||
makes basewindigs for sides and mins / maxs for the brush
|
||||
returns false if the brush doesn't enclose a valid volume
|
||||
==================
|
||||
*/
|
||||
qboolean CreateBrushWindings (bspbrush_t *brush)
|
||||
{
|
||||
int i, j;
|
||||
winding_t *w;
|
||||
side_t *side;
|
||||
plane_t *plane;
|
||||
|
||||
for ( i = 0; i < brush->numsides; i++ )
|
||||
{
|
||||
side = &brush->sides[i];
|
||||
// don't create a winding for a bevel
|
||||
if ( side->bevel ) {
|
||||
continue;
|
||||
}
|
||||
plane = &mapplanes[side->planenum];
|
||||
w = BaseWindingForPlane (plane->normal, plane->dist);
|
||||
for ( j = 0; j < brush->numsides && w; j++ )
|
||||
{
|
||||
if (i == j)
|
||||
continue;
|
||||
if ( brush->sides[j].planenum == ( brush->sides[i].planenum ^ 1 ) )
|
||||
continue; // back side clipaway
|
||||
if (brush->sides[j].bevel)
|
||||
continue;
|
||||
if (brush->sides[j].backSide)
|
||||
continue;
|
||||
plane = &mapplanes[brush->sides[j].planenum^1];
|
||||
ChopWindingInPlace (&w, plane->normal, plane->dist, 0); //CLIP_EPSILON);
|
||||
}
|
||||
// free any existing winding
|
||||
if ( side->winding ) {
|
||||
FreeWinding( side->winding );
|
||||
}
|
||||
side->winding = w;
|
||||
}
|
||||
|
||||
return BoundBrush (brush);
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
BrushFromBounds
|
||||
|
||||
Creates a new axial brush
|
||||
==================
|
||||
*/
|
||||
bspbrush_t *BrushFromBounds (vec3_t mins, vec3_t maxs)
|
||||
{
|
||||
bspbrush_t *b;
|
||||
int i;
|
||||
vec3_t normal;
|
||||
vec_t dist;
|
||||
|
||||
b = AllocBrush (6);
|
||||
b->numsides = 6;
|
||||
for (i=0 ; i<3 ; i++)
|
||||
{
|
||||
VectorClear (normal);
|
||||
normal[i] = 1;
|
||||
dist = maxs[i];
|
||||
b->sides[i].planenum = FindFloatPlane (normal, dist);
|
||||
|
||||
normal[i] = -1;
|
||||
dist = -mins[i];
|
||||
b->sides[3+i].planenum = FindFloatPlane (normal, dist);
|
||||
}
|
||||
|
||||
CreateBrushWindings (b);
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
BrushVolume
|
||||
|
||||
==================
|
||||
*/
|
||||
vec_t BrushVolume (bspbrush_t *brush)
|
||||
{
|
||||
int i;
|
||||
winding_t *w;
|
||||
vec3_t corner;
|
||||
vec_t d, area, volume;
|
||||
plane_t *plane;
|
||||
|
||||
if (!brush)
|
||||
return 0;
|
||||
|
||||
// grab the first valid point as the corner
|
||||
|
||||
w = NULL;
|
||||
for (i=0 ; i<brush->numsides ; i++)
|
||||
{
|
||||
w = brush->sides[i].winding;
|
||||
if (w)
|
||||
break;
|
||||
}
|
||||
if (!w)
|
||||
return 0;
|
||||
VectorCopy (w->p[0], corner);
|
||||
|
||||
// make tetrahedrons to all other faces
|
||||
|
||||
volume = 0;
|
||||
for ( ; i<brush->numsides ; i++)
|
||||
{
|
||||
w = brush->sides[i].winding;
|
||||
if (!w)
|
||||
continue;
|
||||
plane = &mapplanes[brush->sides[i].planenum];
|
||||
d = -(DotProduct (corner, plane->normal) - plane->dist);
|
||||
area = WindingArea (w);
|
||||
volume += d*area;
|
||||
}
|
||||
|
||||
volume /= 3;
|
||||
return volume;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
==================
|
||||
WriteBspBrushMap
|
||||
==================
|
||||
*/
|
||||
void WriteBspBrushMap (char *name, bspbrush_t *list)
|
||||
{
|
||||
FILE *f;
|
||||
side_t *s;
|
||||
int i;
|
||||
winding_t *w;
|
||||
|
||||
_printf ("writing %s\n", name);
|
||||
f = fopen (name, "wb");
|
||||
if (!f)
|
||||
Error ("Can't write %s\b", name);
|
||||
|
||||
fprintf (f, "{\n\"classname\" \"worldspawn\"\n");
|
||||
|
||||
for ( ; list ; list=list->next )
|
||||
{
|
||||
fprintf (f, "{\n");
|
||||
for (i=0,s=list->sides ; i<list->numsides ; i++,s++)
|
||||
{
|
||||
w = BaseWindingForPlane (mapplanes[s->planenum].normal, mapplanes[s->planenum].dist);
|
||||
|
||||
fprintf (f,"( %i %i %i ) ", (int)w->p[0][0], (int)w->p[0][1], (int)w->p[0][2]);
|
||||
fprintf (f,"( %i %i %i ) ", (int)w->p[1][0], (int)w->p[1][1], (int)w->p[1][2]);
|
||||
fprintf (f,"( %i %i %i ) ", (int)w->p[2][0], (int)w->p[2][1], (int)w->p[2][2]);
|
||||
|
||||
fprintf (f, "notexture 0 0 0 1 1\n" );
|
||||
FreeWinding (w);
|
||||
}
|
||||
fprintf (f, "}\n");
|
||||
}
|
||||
fprintf (f, "}\n");
|
||||
|
||||
fclose (f);
|
||||
|
||||
}
|
||||
|
||||
|
||||
//=====================================================================================
|
||||
|
||||
/*
|
||||
====================
|
||||
FilterBrushIntoTree_r
|
||||
|
||||
====================
|
||||
*/
|
||||
int FilterBrushIntoTree_r( bspbrush_t *b, node_t *node ) {
|
||||
bspbrush_t *front, *back;
|
||||
int c;
|
||||
|
||||
if ( !b ) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// add it to the leaf list
|
||||
if ( node->planenum == PLANENUM_LEAF ) {
|
||||
b->next = node->brushlist;
|
||||
node->brushlist = b;
|
||||
|
||||
// classify the leaf by the structural brush
|
||||
if ( !b->detail ) {
|
||||
if ( b->opaque ) {
|
||||
node->opaque = qtrue;
|
||||
node->areaportal = qfalse;
|
||||
} else if ( b->contents & CONTENTS_AREAPORTAL ) {
|
||||
if ( !node->opaque ) {
|
||||
node->areaportal = qtrue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
// split it by the node plane
|
||||
SplitBrush ( b, node->planenum, &front, &back );
|
||||
FreeBrush( b );
|
||||
|
||||
c = 0;
|
||||
c += FilterBrushIntoTree_r( front, node->children[0] );
|
||||
c += FilterBrushIntoTree_r( back, node->children[1] );
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
/*
|
||||
=====================
|
||||
FilterDetailBrushesIntoTree
|
||||
|
||||
Fragment all the detail brushes into the structural leafs
|
||||
=====================
|
||||
*/
|
||||
void FilterDetailBrushesIntoTree( entity_t *e, tree_t *tree ) {
|
||||
bspbrush_t *b, *newb;
|
||||
int r;
|
||||
int c_unique, c_clusters;
|
||||
int i;
|
||||
|
||||
qprintf( "----- FilterDetailBrushesIntoTree -----\n");
|
||||
|
||||
c_unique = 0;
|
||||
c_clusters = 0;
|
||||
for ( b = e->brushes ; b ; b = b->next ) {
|
||||
if ( !b->detail ) {
|
||||
continue;
|
||||
}
|
||||
c_unique++;
|
||||
newb = CopyBrush( b );
|
||||
r = FilterBrushIntoTree_r( newb, tree->headnode );
|
||||
c_clusters += r;
|
||||
|
||||
// mark all sides as visible so drawsurfs are created
|
||||
if ( r ) {
|
||||
for ( i = 0 ; i < b->numsides ; i++ ) {
|
||||
if ( b->sides[i].winding ) {
|
||||
b->sides[i].visible = qtrue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
qprintf( "%5i detail brushes\n", c_unique );
|
||||
qprintf( "%5i cluster references\n", c_clusters );
|
||||
}
|
||||
|
||||
/*
|
||||
=====================
|
||||
FilterStructuralBrushesIntoTree
|
||||
|
||||
Mark the leafs as opaque and areaportals
|
||||
=====================
|
||||
*/
|
||||
void FilterStructuralBrushesIntoTree( entity_t *e, tree_t *tree ) {
|
||||
bspbrush_t *b, *newb;
|
||||
int r;
|
||||
int c_unique, c_clusters;
|
||||
int i;
|
||||
|
||||
qprintf( "----- FilterStructuralBrushesIntoTree -----\n");
|
||||
|
||||
c_unique = 0;
|
||||
c_clusters = 0;
|
||||
for ( b = e->brushes ; b ; b = b->next ) {
|
||||
if ( b->detail ) {
|
||||
continue;
|
||||
}
|
||||
c_unique++;
|
||||
newb = CopyBrush( b );
|
||||
r = FilterBrushIntoTree_r( newb, tree->headnode );
|
||||
c_clusters += r;
|
||||
|
||||
// mark all sides as visible so drawsurfs are created
|
||||
if ( r ) {
|
||||
for ( i = 0 ; i < b->numsides ; i++ ) {
|
||||
if ( b->sides[i].winding ) {
|
||||
b->sides[i].visible = qtrue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
qprintf( "%5i structural brushes\n", c_unique );
|
||||
qprintf( "%5i cluster references\n", c_clusters );
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
================
|
||||
AllocTree
|
||||
================
|
||||
*/
|
||||
tree_t *AllocTree (void)
|
||||
{
|
||||
tree_t *tree;
|
||||
|
||||
tree = malloc(sizeof(*tree));
|
||||
memset (tree, 0, sizeof(*tree));
|
||||
ClearBounds (tree->mins, tree->maxs);
|
||||
|
||||
return tree;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
AllocNode
|
||||
================
|
||||
*/
|
||||
node_t *AllocNode (void)
|
||||
{
|
||||
node_t *node;
|
||||
|
||||
node = malloc(sizeof(*node));
|
||||
memset (node, 0, sizeof(*node));
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
================
|
||||
WindingIsTiny
|
||||
|
||||
Returns true if the winding would be crunched out of
|
||||
existance by the vertex snapping.
|
||||
================
|
||||
*/
|
||||
#define EDGE_LENGTH 0.2
|
||||
qboolean WindingIsTiny (winding_t *w)
|
||||
{
|
||||
/*
|
||||
if (WindingArea (w) < 1)
|
||||
return qtrue;
|
||||
return qfalse;
|
||||
*/
|
||||
int i, j;
|
||||
vec_t len;
|
||||
vec3_t delta;
|
||||
int edges;
|
||||
|
||||
edges = 0;
|
||||
for (i=0 ; i<w->numpoints ; i++)
|
||||
{
|
||||
j = i == w->numpoints - 1 ? 0 : i+1;
|
||||
VectorSubtract (w->p[j], w->p[i], delta);
|
||||
len = VectorLength (delta);
|
||||
if (len > EDGE_LENGTH)
|
||||
{
|
||||
if (++edges == 3)
|
||||
return qfalse;
|
||||
}
|
||||
}
|
||||
return qtrue;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
WindingIsHuge
|
||||
|
||||
Returns true if the winding still has one of the points
|
||||
from basewinding for plane
|
||||
================
|
||||
*/
|
||||
qboolean WindingIsHuge (winding_t *w)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
for (i=0 ; i<w->numpoints ; i++)
|
||||
{
|
||||
for (j=0 ; j<3 ; j++)
|
||||
if (w->p[i][j] <= MIN_WORLD_COORD || w->p[i][j] >= MAX_WORLD_COORD)
|
||||
return qtrue;
|
||||
}
|
||||
return qfalse;
|
||||
}
|
||||
|
||||
//============================================================
|
||||
|
||||
/*
|
||||
==================
|
||||
BrushMostlyOnSide
|
||||
|
||||
==================
|
||||
*/
|
||||
int BrushMostlyOnSide (bspbrush_t *brush, plane_t *plane)
|
||||
{
|
||||
int i, j;
|
||||
winding_t *w;
|
||||
vec_t d, max;
|
||||
int side;
|
||||
|
||||
max = 0;
|
||||
side = PSIDE_FRONT;
|
||||
for (i=0 ; i<brush->numsides ; i++)
|
||||
{
|
||||
w = brush->sides[i].winding;
|
||||
if (!w)
|
||||
continue;
|
||||
for (j=0 ; j<w->numpoints ; j++)
|
||||
{
|
||||
d = DotProduct (w->p[j], plane->normal) - plane->dist;
|
||||
if (d > max)
|
||||
{
|
||||
max = d;
|
||||
side = PSIDE_FRONT;
|
||||
}
|
||||
if (-d > max)
|
||||
{
|
||||
max = -d;
|
||||
side = PSIDE_BACK;
|
||||
}
|
||||
}
|
||||
}
|
||||
return side;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
SplitBrush
|
||||
|
||||
Generates two new brushes, leaving the original
|
||||
unchanged
|
||||
================
|
||||
*/
|
||||
void SplitBrush (bspbrush_t *brush, int planenum,
|
||||
bspbrush_t **front, bspbrush_t **back)
|
||||
{
|
||||
bspbrush_t *b[2];
|
||||
int i, j;
|
||||
winding_t *w, *cw[2], *midwinding;
|
||||
plane_t *plane, *plane2;
|
||||
side_t *s, *cs;
|
||||
float d, d_front, d_back;
|
||||
|
||||
*front = *back = NULL;
|
||||
plane = &mapplanes[planenum];
|
||||
|
||||
// check all points
|
||||
d_front = d_back = 0;
|
||||
for (i=0 ; i<brush->numsides ; i++)
|
||||
{
|
||||
w = brush->sides[i].winding;
|
||||
if (!w)
|
||||
continue;
|
||||
for (j=0 ; j<w->numpoints ; j++)
|
||||
{
|
||||
d = DotProduct (w->p[j], plane->normal) - plane->dist;
|
||||
if (d > 0 && d > d_front)
|
||||
d_front = d;
|
||||
if (d < 0 && d < d_back)
|
||||
d_back = d;
|
||||
}
|
||||
}
|
||||
if (d_front < 0.1) // PLANESIDE_EPSILON)
|
||||
{ // only on back
|
||||
*back = CopyBrush (brush);
|
||||
return;
|
||||
}
|
||||
if (d_back > -0.1) // PLANESIDE_EPSILON)
|
||||
{ // only on front
|
||||
*front = CopyBrush (brush);
|
||||
return;
|
||||
}
|
||||
|
||||
// create a new winding from the split plane
|
||||
|
||||
w = BaseWindingForPlane (plane->normal, plane->dist);
|
||||
for (i=0 ; i<brush->numsides && w ; i++)
|
||||
{
|
||||
if ( brush->sides[i].backSide ) {
|
||||
continue; // fake back-sided polygons never split
|
||||
}
|
||||
plane2 = &mapplanes[brush->sides[i].planenum ^ 1];
|
||||
ChopWindingInPlace (&w, plane2->normal, plane2->dist, 0); // PLANESIDE_EPSILON);
|
||||
}
|
||||
|
||||
if (!w || WindingIsTiny (w) )
|
||||
{ // the brush isn't really split
|
||||
int side;
|
||||
|
||||
side = BrushMostlyOnSide (brush, plane);
|
||||
if (side == PSIDE_FRONT)
|
||||
*front = CopyBrush (brush);
|
||||
if (side == PSIDE_BACK)
|
||||
*back = CopyBrush (brush);
|
||||
return;
|
||||
}
|
||||
|
||||
if (WindingIsHuge (w))
|
||||
{
|
||||
qprintf ("WARNING: huge winding\n");
|
||||
}
|
||||
|
||||
midwinding = w;
|
||||
|
||||
// split it for real
|
||||
|
||||
for (i=0 ; i<2 ; i++)
|
||||
{
|
||||
b[i] = AllocBrush (brush->numsides+1);
|
||||
memcpy( b[i], brush, sizeof( bspbrush_t ) - sizeof( brush->sides ) );
|
||||
b[i]->numsides = 0;
|
||||
b[i]->next = NULL;
|
||||
b[i]->original = brush->original;
|
||||
}
|
||||
|
||||
// split all the current windings
|
||||
|
||||
for (i=0 ; i<brush->numsides ; i++)
|
||||
{
|
||||
s = &brush->sides[i];
|
||||
w = s->winding;
|
||||
if (!w)
|
||||
continue;
|
||||
ClipWindingEpsilon (w, plane->normal, plane->dist,
|
||||
0 /*PLANESIDE_EPSILON*/, &cw[0], &cw[1]);
|
||||
for (j=0 ; j<2 ; j++)
|
||||
{
|
||||
if (!cw[j])
|
||||
continue;
|
||||
/*
|
||||
if (WindingIsTiny (cw[j]))
|
||||
{
|
||||
FreeWinding (cw[j]);
|
||||
continue;
|
||||
}
|
||||
*/
|
||||
cs = &b[j]->sides[b[j]->numsides];
|
||||
b[j]->numsides++;
|
||||
*cs = *s;
|
||||
cs->winding = cw[j];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// see if we have valid polygons on both sides
|
||||
|
||||
for (i=0 ; i<2 ; i++)
|
||||
{
|
||||
BoundBrush (b[i]);
|
||||
for (j=0 ; j<3 ; j++)
|
||||
{
|
||||
if (b[i]->mins[j] < MIN_WORLD_COORD || b[i]->maxs[j] > MAX_WORLD_COORD)
|
||||
{
|
||||
qprintf ("bogus brush after clip\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (b[i]->numsides < 3 || j < 3)
|
||||
{
|
||||
FreeBrush (b[i]);
|
||||
b[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if ( !(b[0] && b[1]) )
|
||||
{
|
||||
if (!b[0] && !b[1])
|
||||
qprintf ("split removed brush\n");
|
||||
else
|
||||
qprintf ("split not on both sides\n");
|
||||
if (b[0])
|
||||
{
|
||||
FreeBrush (b[0]);
|
||||
*front = CopyBrush (brush);
|
||||
}
|
||||
if (b[1])
|
||||
{
|
||||
FreeBrush (b[1]);
|
||||
*back = CopyBrush (brush);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// add the midwinding to both sides
|
||||
for (i=0 ; i<2 ; i++)
|
||||
{
|
||||
cs = &b[i]->sides[b[i]->numsides];
|
||||
b[i]->numsides++;
|
||||
|
||||
cs->planenum = planenum^i^1;
|
||||
cs->shaderInfo = NULL;
|
||||
if (i==0)
|
||||
cs->winding = CopyWinding (midwinding);
|
||||
else
|
||||
cs->winding = midwinding;
|
||||
}
|
||||
|
||||
{
|
||||
vec_t v1;
|
||||
int i;
|
||||
|
||||
for (i=0 ; i<2 ; i++)
|
||||
{
|
||||
v1 = BrushVolume (b[i]);
|
||||
if (v1 < 1.0)
|
||||
{
|
||||
FreeBrush (b[i]);
|
||||
b[i] = NULL;
|
||||
// qprintf ("tiny volume after clip\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*front = b[0];
|
||||
*back = b[1];
|
||||
}
|
52
q3map/brush_primit.c
Normal file
52
q3map/brush_primit.c
Normal file
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 1999-2005 Id Software, Inc.
|
||||
|
||||
This file is part of Quake III Arena source code.
|
||||
|
||||
Quake III Arena source code 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.
|
||||
|
||||
Quake III Arena source code 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 Foobar; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
#include "qbsp.h"
|
||||
|
||||
|
||||
// global flag
|
||||
int g_bBrushPrimit;
|
||||
|
||||
// NOTE : ComputeAxisBase here and in editor code must always BE THE SAME !
|
||||
// WARNING : special case behaviour of atan2(y,x) <-> atan(y/x) might not be the same everywhere when x == 0
|
||||
// rotation by (0,RotY,RotZ) assigns X to normal
|
||||
void ComputeAxisBase(vec3_t normal,vec3_t texX,vec3_t texY)
|
||||
{
|
||||
vec_t RotY,RotZ;
|
||||
// do some cleaning
|
||||
if (fabs(normal[0])<1e-6)
|
||||
normal[0]=0.0f;
|
||||
if (fabs(normal[1])<1e-6)
|
||||
normal[1]=0.0f;
|
||||
if (fabs(normal[2])<1e-6)
|
||||
normal[2]=0.0f;
|
||||
// compute the two rotations around Y and Z to rotate X to normal
|
||||
RotY=-atan2(normal[2],sqrt(normal[1]*normal[1]+normal[0]*normal[0]));
|
||||
RotZ=atan2(normal[1],normal[0]);
|
||||
// rotate (0,1,0) and (0,0,1) to compute texX and texY
|
||||
texX[0]=-sin(RotZ);
|
||||
texX[1]=cos(RotZ);
|
||||
texX[2]=0;
|
||||
// the texY vector is along -Z ( T texture coorinates axis )
|
||||
texY[0]=-sin(RotY)*cos(RotZ);
|
||||
texY[1]=-sin(RotY)*sin(RotZ);
|
||||
texY[2]=-cos(RotY);
|
||||
}
|
605
q3map/bsp.c
Normal file
605
q3map/bsp.c
Normal file
|
@ -0,0 +1,605 @@
|
|||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 1999-2005 Id Software, Inc.
|
||||
|
||||
This file is part of Quake III Arena source code.
|
||||
|
||||
Quake III Arena source code 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.
|
||||
|
||||
Quake III Arena source code 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 Foobar; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
#include "qbsp.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#ifdef _TTIMOBUILD
|
||||
#include "pakstuff.h"
|
||||
#else
|
||||
#include "../libs/pakstuff.h"
|
||||
#endif
|
||||
extern HWND hwndOut;
|
||||
#endif
|
||||
|
||||
char source[1024];
|
||||
char tempsource[1024];
|
||||
char name[1024];
|
||||
|
||||
vec_t microvolume = 1.0;
|
||||
qboolean glview;
|
||||
qboolean nodetail;
|
||||
qboolean fulldetail;
|
||||
qboolean onlyents;
|
||||
qboolean onlytextures;
|
||||
qboolean nowater;
|
||||
qboolean nofill;
|
||||
qboolean noopt;
|
||||
qboolean leaktest;
|
||||
qboolean verboseentities;
|
||||
qboolean noCurveBrushes;
|
||||
qboolean fakemap;
|
||||
qboolean notjunc;
|
||||
qboolean nomerge;
|
||||
qboolean nofog;
|
||||
qboolean nosubdivide;
|
||||
qboolean testExpand;
|
||||
qboolean showseams;
|
||||
|
||||
char outbase[32];
|
||||
|
||||
int entity_num;
|
||||
|
||||
/*
|
||||
============
|
||||
ProcessWorldModel
|
||||
|
||||
============
|
||||
*/
|
||||
void ProcessWorldModel( void ) {
|
||||
entity_t *e;
|
||||
tree_t *tree;
|
||||
bspface_t *faces;
|
||||
qboolean leaked;
|
||||
|
||||
BeginModel();
|
||||
|
||||
e = &entities[0];
|
||||
e->firstDrawSurf = 0;//numMapDrawSurfs;
|
||||
|
||||
// check for patches with adjacent edges that need to LOD together
|
||||
PatchMapDrawSurfs( e );
|
||||
|
||||
// build an initial bsp tree using all of the sides
|
||||
// of all of the structural brushes
|
||||
faces = MakeStructuralBspFaceList ( entities[0].brushes );
|
||||
tree = FaceBSP( faces );
|
||||
MakeTreePortals (tree);
|
||||
FilterStructuralBrushesIntoTree( e, tree );
|
||||
|
||||
// see if the bsp is completely enclosed
|
||||
if ( FloodEntities (tree) ) {
|
||||
// rebuild a better bsp tree using only the
|
||||
// sides that are visible from the inside
|
||||
FillOutside (tree->headnode);
|
||||
|
||||
// chop the sides to the convex hull of
|
||||
// their visible fragments, giving us the smallest
|
||||
// polygons
|
||||
ClipSidesIntoTree( e, tree );
|
||||
|
||||
faces = MakeVisibleBspFaceList( entities[0].brushes );
|
||||
FreeTree (tree);
|
||||
tree = FaceBSP( faces );
|
||||
MakeTreePortals( tree );
|
||||
FilterStructuralBrushesIntoTree( e, tree );
|
||||
leaked = qfalse;
|
||||
} else {
|
||||
_printf ("**********************\n");
|
||||
_printf ("******* leaked *******\n");
|
||||
_printf ("**********************\n");
|
||||
LeakFile (tree);
|
||||
if ( leaktest ) {
|
||||
_printf ("--- MAP LEAKED, ABORTING LEAKTEST ---\n");
|
||||
exit (0);
|
||||
}
|
||||
leaked = qtrue;
|
||||
|
||||
// chop the sides to the convex hull of
|
||||
// their visible fragments, giving us the smallest
|
||||
// polygons
|
||||
ClipSidesIntoTree( e, tree );
|
||||
}
|
||||
|
||||
// save out information for visibility processing
|
||||
NumberClusters( tree );
|
||||
if ( !leaked ) {
|
||||
WritePortalFile( tree );
|
||||
}
|
||||
if ( glview ) {
|
||||
// dump the portals for debugging
|
||||
WriteGLView( tree, source );
|
||||
}
|
||||
FloodAreas (tree);
|
||||
|
||||
// add references to the detail brushes
|
||||
FilterDetailBrushesIntoTree( e, tree );
|
||||
|
||||
// create drawsurfs for triangle models
|
||||
AddTriangleModels( tree );
|
||||
|
||||
// drawsurfs that cross fog boundaries will need to
|
||||
// be split along the bound
|
||||
if ( !nofog ) {
|
||||
FogDrawSurfs(); // may fragment drawsurfs
|
||||
}
|
||||
|
||||
// subdivide each drawsurf as required by shader tesselation
|
||||
if ( !nosubdivide ) {
|
||||
SubdivideDrawSurfs( e, tree );
|
||||
}
|
||||
|
||||
// merge together all common shaders on the same plane and remove
|
||||
// all colinear points, so extra tjunctions won't be generated
|
||||
if ( !nomerge ) {
|
||||
MergeSides( e, tree ); // !@# testing
|
||||
}
|
||||
|
||||
// add in any vertexes required to fix tjunctions
|
||||
if ( !notjunc ) {
|
||||
FixTJunctions( e );
|
||||
}
|
||||
|
||||
// allocate lightmaps for faces and patches
|
||||
AllocateLightmaps( e );
|
||||
|
||||
// add references to the final drawsurfs in the apropriate clusters
|
||||
FilterDrawsurfsIntoTree( e, tree );
|
||||
|
||||
EndModel( tree->headnode );
|
||||
|
||||
FreeTree (tree);
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
ProcessSubModel
|
||||
|
||||
============
|
||||
*/
|
||||
void ProcessSubModel( void ) {
|
||||
entity_t *e;
|
||||
tree_t *tree;
|
||||
bspbrush_t *b, *bc;
|
||||
node_t *node;
|
||||
|
||||
BeginModel ();
|
||||
|
||||
e = &entities[entity_num];
|
||||
e->firstDrawSurf = numMapDrawSurfs;
|
||||
|
||||
PatchMapDrawSurfs( e );
|
||||
|
||||
// just put all the brushes in an empty leaf
|
||||
// FIXME: patches?
|
||||
node = AllocNode();
|
||||
node->planenum = PLANENUM_LEAF;
|
||||
for ( b = e->brushes ; b ; b = b->next ) {
|
||||
bc = CopyBrush( b );
|
||||
bc->next = node->brushlist;
|
||||
node->brushlist = bc;
|
||||
}
|
||||
|
||||
tree = AllocTree();
|
||||
tree->headnode = node;
|
||||
|
||||
ClipSidesIntoTree( e, tree );
|
||||
|
||||
// subdivide each drawsurf as required by shader tesselation or fog
|
||||
if ( !nosubdivide ) {
|
||||
SubdivideDrawSurfs( e, tree );
|
||||
}
|
||||
|
||||
// merge together all common shaders on the same plane and remove
|
||||
// all colinear points, so extra tjunctions won't be generated
|
||||
if ( !nomerge ) {
|
||||
MergeSides( e, tree ); // !@# testing
|
||||
}
|
||||
|
||||
// add in any vertexes required to fix tjunctions
|
||||
if ( !notjunc ) {
|
||||
FixTJunctions( e );
|
||||
}
|
||||
|
||||
// allocate lightmaps for faces and patches
|
||||
AllocateLightmaps( e );
|
||||
|
||||
// add references to the final drawsurfs in the apropriate clusters
|
||||
FilterDrawsurfsIntoTree( e, tree );
|
||||
|
||||
EndModel ( node );
|
||||
|
||||
FreeTree( tree );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
============
|
||||
ProcessModels
|
||||
============
|
||||
*/
|
||||
void ProcessModels (void)
|
||||
{
|
||||
qboolean oldVerbose;
|
||||
entity_t *entity;
|
||||
|
||||
oldVerbose = verbose;
|
||||
|
||||
BeginBSPFile ();
|
||||
|
||||
for ( entity_num=0 ; entity_num< num_entities ; entity_num++ ) {
|
||||
entity = &entities[entity_num];
|
||||
|
||||
if ( !entity->brushes && !entity->patches ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
qprintf ("############### model %i ###############\n", nummodels);
|
||||
if (entity_num == 0)
|
||||
ProcessWorldModel ();
|
||||
else
|
||||
ProcessSubModel ();
|
||||
|
||||
if (!verboseentities)
|
||||
verbose = qfalse; // don't bother printing submodels
|
||||
}
|
||||
|
||||
verbose = oldVerbose;
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
Bspinfo
|
||||
============
|
||||
*/
|
||||
void Bspinfo( int count, char **fileNames ) {
|
||||
int i;
|
||||
char source[1024];
|
||||
int size;
|
||||
FILE *f;
|
||||
|
||||
if ( count < 1 ) {
|
||||
_printf( "No files to dump info for.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
for ( i = 0 ; i < count ; i++ ) {
|
||||
_printf ("---------------------\n");
|
||||
strcpy (source, fileNames[ i ] );
|
||||
DefaultExtension (source, ".bsp");
|
||||
f = fopen (source, "rb");
|
||||
if (f)
|
||||
{
|
||||
size = Q_filelength (f);
|
||||
fclose (f);
|
||||
}
|
||||
else
|
||||
size = 0;
|
||||
_printf ("%s: %i\n", source, size);
|
||||
|
||||
LoadBSPFile (source);
|
||||
PrintBSPFileSizes ();
|
||||
_printf ("---------------------\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
============
|
||||
OnlyEnts
|
||||
============
|
||||
*/
|
||||
void OnlyEnts( void ) {
|
||||
char out[1024];
|
||||
|
||||
sprintf (out, "%s.bsp", source);
|
||||
LoadBSPFile (out);
|
||||
num_entities = 0;
|
||||
|
||||
LoadMapFile (name);
|
||||
SetModelNumbers ();
|
||||
SetLightStyles ();
|
||||
|
||||
UnparseEntities ();
|
||||
|
||||
WriteBSPFile (out);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
============
|
||||
OnlyTextures
|
||||
============
|
||||
*/
|
||||
void OnlyTextures( void ) { // FIXME!!!
|
||||
char out[1024];
|
||||
int i;
|
||||
|
||||
Error( "-onlytextures isn't working now..." );
|
||||
|
||||
sprintf (out, "%s.bsp", source);
|
||||
|
||||
LoadMapFile (name);
|
||||
|
||||
LoadBSPFile (out);
|
||||
|
||||
// replace all the drawsurface shader names
|
||||
for ( i = 0 ; i < numDrawSurfaces ; i++ ) {
|
||||
}
|
||||
|
||||
WriteBSPFile (out);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
============
|
||||
main
|
||||
============
|
||||
*/
|
||||
int LightMain( int argc, char **argv );
|
||||
int VLightMain (int argc, char **argv);
|
||||
int VSoundMain (int argc, char **argv);
|
||||
int VisMain( int argc, char **argv );
|
||||
|
||||
int main (int argc, char **argv) {
|
||||
int i;
|
||||
double start, end;
|
||||
char path[1024];
|
||||
|
||||
_printf ("Q3Map v1.0s (c) 1999 Id Software Inc.\n");
|
||||
|
||||
if ( argc < 2 ) {
|
||||
Error ("usage: q3map [options] mapfile");
|
||||
}
|
||||
|
||||
// check for general program options
|
||||
if (!strcmp(argv[1], "-info")) {
|
||||
Bspinfo( argc - 2, argv + 2 );
|
||||
return 0;
|
||||
}
|
||||
if (!strcmp(argv[1], "-light")) {
|
||||
LightMain( argc - 1, argv + 1 );
|
||||
return 0;
|
||||
}
|
||||
if (!strcmp(argv[1], "-vlight")) {
|
||||
VLightMain( argc - 1, argv + 1 );
|
||||
return 0;
|
||||
}
|
||||
if (!strcmp(argv[1], "-vsound")) {
|
||||
VSoundMain( argc - 1, argv + 1 );
|
||||
return 0;
|
||||
}
|
||||
if (!strcmp(argv[1], "-vis")) {
|
||||
VisMain( argc - 1, argv + 1 );
|
||||
return 0;
|
||||
}
|
||||
|
||||
// do a bsp if nothing else was specified
|
||||
|
||||
_printf ("---- q3map ----\n");
|
||||
|
||||
tempsource[0] = '\0';
|
||||
|
||||
for (i=1 ; i<argc ; i++)
|
||||
{
|
||||
if (!strcmp(argv[i],"-tempname"))
|
||||
{
|
||||
strcpy(tempsource, argv[++i]);
|
||||
}
|
||||
else if (!strcmp(argv[i],"-threads"))
|
||||
{
|
||||
numthreads = atoi (argv[i+1]);
|
||||
i++;
|
||||
}
|
||||
else if (!strcmp(argv[i],"-glview"))
|
||||
{
|
||||
glview = qtrue;
|
||||
}
|
||||
else if (!strcmp(argv[i], "-v"))
|
||||
{
|
||||
_printf ("verbose = true\n");
|
||||
verbose = qtrue;
|
||||
}
|
||||
else if (!strcmp(argv[i], "-draw"))
|
||||
{
|
||||
_printf ("drawflag = true\n");
|
||||
drawflag = qtrue;
|
||||
}
|
||||
else if (!strcmp(argv[i], "-nowater"))
|
||||
{
|
||||
_printf ("nowater = true\n");
|
||||
nowater = qtrue;
|
||||
}
|
||||
else if (!strcmp(argv[i], "-noopt"))
|
||||
{
|
||||
_printf ("noopt = true\n");
|
||||
noopt = qtrue;
|
||||
}
|
||||
else if (!strcmp(argv[i], "-nofill"))
|
||||
{
|
||||
_printf ("nofill = true\n");
|
||||
nofill = qtrue;
|
||||
}
|
||||
else if (!strcmp(argv[i], "-nodetail"))
|
||||
{
|
||||
_printf ("nodetail = true\n");
|
||||
nodetail = qtrue;
|
||||
}
|
||||
else if (!strcmp(argv[i], "-fulldetail"))
|
||||
{
|
||||
_printf ("fulldetail = true\n");
|
||||
fulldetail = qtrue;
|
||||
}
|
||||
else if (!strcmp(argv[i], "-onlyents"))
|
||||
{
|
||||
_printf ("onlyents = true\n");
|
||||
onlyents = qtrue;
|
||||
}
|
||||
else if (!strcmp(argv[i], "-onlytextures"))
|
||||
{
|
||||
_printf ("onlytextures = true\n"); // FIXME: make work again!
|
||||
onlytextures = qtrue;
|
||||
}
|
||||
else if (!strcmp(argv[i], "-micro"))
|
||||
{
|
||||
microvolume = atof(argv[i+1]);
|
||||
_printf ("microvolume = %f\n", microvolume);
|
||||
i++;
|
||||
}
|
||||
else if (!strcmp(argv[i], "-nofog"))
|
||||
{
|
||||
_printf ("nofog = true\n");
|
||||
nofog = qtrue;
|
||||
}
|
||||
else if (!strcmp(argv[i], "-nosubdivide"))
|
||||
{
|
||||
_printf ("nosubdivide = true\n");
|
||||
nosubdivide = qtrue;
|
||||
}
|
||||
else if (!strcmp(argv[i], "-leaktest"))
|
||||
{
|
||||
_printf ("leaktest = true\n");
|
||||
leaktest = qtrue;
|
||||
}
|
||||
else if (!strcmp(argv[i], "-verboseentities"))
|
||||
{
|
||||
_printf ("verboseentities = true\n");
|
||||
verboseentities = qtrue;
|
||||
}
|
||||
else if (!strcmp(argv[i], "-nocurves"))
|
||||
{
|
||||
noCurveBrushes = qtrue;
|
||||
_printf ("no curve brushes\n");
|
||||
}
|
||||
else if (!strcmp(argv[i], "-notjunc"))
|
||||
{
|
||||
notjunc = qtrue;
|
||||
_printf ("no tjunction fixing\n");
|
||||
}
|
||||
else if (!strcmp(argv[i], "-expand"))
|
||||
{
|
||||
testExpand = qtrue;
|
||||
_printf ("Writing expanded.map.\n");
|
||||
}
|
||||
else if (!strcmp(argv[i], "-showseams"))
|
||||
{
|
||||
showseams = qtrue;
|
||||
_printf ("Showing seams on terrain.\n");
|
||||
}
|
||||
else if (!strcmp (argv[i],"-tmpout"))
|
||||
{
|
||||
strcpy (outbase, "/tmp");
|
||||
}
|
||||
else if (!strcmp (argv[i],"-fakemap"))
|
||||
{
|
||||
fakemap = qtrue;
|
||||
_printf( "will generate fakemap.map\n");
|
||||
}
|
||||
else if (!strcmp(argv[i], "-samplesize"))
|
||||
{
|
||||
samplesize = atoi(argv[i+1]);
|
||||
if (samplesize < 1) samplesize = 1;
|
||||
i++;
|
||||
_printf("lightmap sample size is %dx%d units\n", samplesize, samplesize);
|
||||
}
|
||||
else if (argv[i][0] == '-')
|
||||
Error ("Unknown option \"%s\"", argv[i]);
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
if (i != argc - 1)
|
||||
Error ("usage: q3map [options] mapfile");
|
||||
|
||||
start = I_FloatTime ();
|
||||
|
||||
ThreadSetDefault ();
|
||||
//numthreads = 1; // multiple threads aren't helping because of heavy malloc use
|
||||
SetQdirFromPath (argv[i]);
|
||||
|
||||
#ifdef _WIN32
|
||||
InitPakFile(gamedir, NULL);
|
||||
#endif
|
||||
|
||||
strcpy (source, ExpandArg (argv[i]));
|
||||
StripExtension (source);
|
||||
|
||||
// delete portal and line files
|
||||
sprintf (path, "%s.prt", source);
|
||||
remove (path);
|
||||
sprintf (path, "%s.lin", source);
|
||||
remove (path);
|
||||
|
||||
strcpy (name, ExpandArg (argv[i]));
|
||||
if ( strcmp(name + strlen(name) - 4, ".reg" ) ) {
|
||||
// if we are doing a full map, delete the last saved region map
|
||||
sprintf (path, "%s.reg", source);
|
||||
remove (path);
|
||||
|
||||
DefaultExtension (name, ".map"); // might be .reg
|
||||
}
|
||||
|
||||
//
|
||||
// if onlyents, just grab the entites and resave
|
||||
//
|
||||
if ( onlyents ) {
|
||||
OnlyEnts();
|
||||
return 0;
|
||||
}
|
||||
|
||||
//
|
||||
// if onlytextures, just grab the textures and resave
|
||||
//
|
||||
if ( onlytextures ) {
|
||||
OnlyTextures();
|
||||
return 0;
|
||||
}
|
||||
|
||||
//
|
||||
// start from scratch
|
||||
//
|
||||
LoadShaderInfo();
|
||||
|
||||
// load original file from temp spot in case it was renamed by the editor on the way in
|
||||
if (strlen(tempsource) > 0) {
|
||||
LoadMapFile (tempsource);
|
||||
} else {
|
||||
LoadMapFile (name);
|
||||
}
|
||||
|
||||
SetModelNumbers ();
|
||||
SetLightStyles ();
|
||||
|
||||
ProcessModels ();
|
||||
|
||||
EndBSPFile();
|
||||
|
||||
end = I_FloatTime ();
|
||||
_printf ("%5.0f seconds elapsed\n", end-start);
|
||||
|
||||
// remove temp name if appropriate
|
||||
if (strlen(tempsource) > 0) {
|
||||
remove(tempsource);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
379
q3map/facebsp.c
Normal file
379
q3map/facebsp.c
Normal file
|
@ -0,0 +1,379 @@
|
|||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 1999-2005 Id Software, Inc.
|
||||
|
||||
This file is part of Quake III Arena source code.
|
||||
|
||||
Quake III Arena source code 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.
|
||||
|
||||
Quake III Arena source code 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 Foobar; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
#include "qbsp.h"
|
||||
|
||||
|
||||
int c_faceLeafs;
|
||||
|
||||
|
||||
/*
|
||||
================
|
||||
AllocBspFace
|
||||
================
|
||||
*/
|
||||
bspface_t *AllocBspFace( void ) {
|
||||
bspface_t *f;
|
||||
|
||||
f = malloc(sizeof(*f));
|
||||
memset( f, 0, sizeof(*f) );
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
FreeBspFace
|
||||
================
|
||||
*/
|
||||
void FreeBspFace( bspface_t *f ) {
|
||||
if ( f->w ) {
|
||||
FreeWinding( f->w );
|
||||
}
|
||||
free( f );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
================
|
||||
SelectSplitPlaneNum
|
||||
================
|
||||
*/
|
||||
int hintsplit;
|
||||
|
||||
#define BLOCK_SIZE 1024
|
||||
int SelectSplitPlaneNum( node_t *node, bspface_t *list ) {
|
||||
bspface_t *split;
|
||||
bspface_t *check;
|
||||
bspface_t *bestSplit;
|
||||
int splits, facing, front, back;
|
||||
int side;
|
||||
plane_t *plane;
|
||||
int value, bestValue;
|
||||
int i;
|
||||
vec3_t normal;
|
||||
float dist;
|
||||
int planenum;
|
||||
|
||||
hintsplit = qfalse;
|
||||
// if it is crossing a 1k block boundary, force a split
|
||||
for ( i = 0 ; i < 2 ; i++ ) {
|
||||
dist = BLOCK_SIZE * ( floor( node->mins[i] / BLOCK_SIZE ) + 1 );
|
||||
if ( node->maxs[i] > dist ) {
|
||||
VectorClear( normal );
|
||||
normal[i] = 1;
|
||||
planenum = FindFloatPlane( normal, dist );
|
||||
return planenum;
|
||||
}
|
||||
}
|
||||
|
||||
// pick one of the face planes
|
||||
bestValue = -99999;
|
||||
bestSplit = list;
|
||||
|
||||
for ( split = list ; split ; split = split->next ) {
|
||||
split->checked = qfalse;
|
||||
}
|
||||
|
||||
for ( split = list ; split ; split = split->next ) {
|
||||
if ( split->checked ) {
|
||||
continue;
|
||||
}
|
||||
plane = &mapplanes[ split->planenum ];
|
||||
splits = 0;
|
||||
facing = 0;
|
||||
front = 0;
|
||||
back = 0;
|
||||
for ( check = list ; check ; check = check->next ) {
|
||||
if ( check->planenum == split->planenum ) {
|
||||
facing++;
|
||||
check->checked = qtrue; // won't need to test this plane again
|
||||
continue;
|
||||
}
|
||||
side = WindingOnPlaneSide( check->w, plane->normal, plane->dist );
|
||||
if ( side == SIDE_CROSS ) {
|
||||
splits++;
|
||||
} else if ( side == SIDE_FRONT ) {
|
||||
front++;
|
||||
} else if ( side == SIDE_BACK ) {
|
||||
back++;
|
||||
}
|
||||
}
|
||||
value = 5*facing - 5*splits; // - abs(front-back);
|
||||
if ( plane->type < 3 ) {
|
||||
value+=5; // axial is better
|
||||
}
|
||||
value += split->priority; // prioritize hints higher
|
||||
|
||||
if ( value > bestValue ) {
|
||||
bestValue = value;
|
||||
bestSplit = split;
|
||||
}
|
||||
}
|
||||
|
||||
if ( bestValue == -99999 ) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (bestSplit->hint)
|
||||
hintsplit = qtrue;
|
||||
|
||||
return bestSplit->planenum;
|
||||
}
|
||||
|
||||
int CountFaceList( bspface_t *list ) {
|
||||
int c;
|
||||
c = 0;
|
||||
for ( ; list ; list = list->next ) {
|
||||
c++;
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
BuildFaceTree_r
|
||||
================
|
||||
*/
|
||||
void BuildFaceTree_r( node_t *node, bspface_t *list ) {
|
||||
bspface_t *split;
|
||||
bspface_t *next;
|
||||
int side;
|
||||
plane_t *plane;
|
||||
bspface_t *newFace;
|
||||
bspface_t *childLists[2];
|
||||
winding_t *frontWinding, *backWinding;
|
||||
int i;
|
||||
int splitPlaneNum;
|
||||
|
||||
i = CountFaceList( list );
|
||||
|
||||
splitPlaneNum = SelectSplitPlaneNum( node, list );
|
||||
// if we don't have any more faces, this is a node
|
||||
if ( splitPlaneNum == -1 ) {
|
||||
node->planenum = PLANENUM_LEAF;
|
||||
c_faceLeafs++;
|
||||
return;
|
||||
}
|
||||
|
||||
// partition the list
|
||||
node->planenum = splitPlaneNum;
|
||||
node->hint = hintsplit;
|
||||
plane = &mapplanes[ splitPlaneNum ];
|
||||
childLists[0] = NULL;
|
||||
childLists[1] = NULL;
|
||||
for ( split = list ; split ; split = next ) {
|
||||
next = split->next;
|
||||
|
||||
if ( split->planenum == node->planenum ) {
|
||||
FreeBspFace( split );
|
||||
continue;
|
||||
}
|
||||
|
||||
side = WindingOnPlaneSide( split->w, plane->normal, plane->dist );
|
||||
|
||||
if ( side == SIDE_CROSS ) {
|
||||
ClipWindingEpsilon( split->w, plane->normal, plane->dist, CLIP_EPSILON * 2,
|
||||
&frontWinding, &backWinding );
|
||||
if ( frontWinding ) {
|
||||
newFace = AllocBspFace();
|
||||
newFace->w = frontWinding;
|
||||
newFace->next = childLists[0];
|
||||
newFace->planenum = split->planenum;
|
||||
newFace->priority = split->priority;
|
||||
newFace->hint = split->hint;
|
||||
childLists[0] = newFace;
|
||||
}
|
||||
if ( backWinding ) {
|
||||
newFace = AllocBspFace();
|
||||
newFace->w = backWinding;
|
||||
newFace->next = childLists[1];
|
||||
newFace->planenum = split->planenum;
|
||||
newFace->priority = split->priority;
|
||||
newFace->hint = split->hint;
|
||||
childLists[1] = newFace;
|
||||
}
|
||||
FreeBspFace( split );
|
||||
} else if ( side == SIDE_FRONT ) {
|
||||
split->next = childLists[0];
|
||||
childLists[0] = split;
|
||||
} else if ( side == SIDE_BACK ) {
|
||||
split->next = childLists[1];
|
||||
childLists[1] = split;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// recursively process children
|
||||
for ( i = 0 ; i < 2 ; i++ ) {
|
||||
node->children[i] = AllocNode();
|
||||
node->children[i]->parent = node;
|
||||
VectorCopy( node->mins, node->children[i]->mins );
|
||||
VectorCopy( node->maxs, node->children[i]->maxs );
|
||||
}
|
||||
|
||||
for ( i = 0 ; i < 3 ; i++ ) {
|
||||
if ( plane->normal[i] == 1 ) {
|
||||
node->children[0]->mins[i] = plane->dist;
|
||||
node->children[1]->maxs[i] = plane->dist;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for ( i = 0 ; i < 2 ; i++ ) {
|
||||
BuildFaceTree_r ( node->children[i], childLists[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
================
|
||||
FaceBSP
|
||||
|
||||
List will be freed before returning
|
||||
================
|
||||
*/
|
||||
tree_t *FaceBSP( bspface_t *list ) {
|
||||
tree_t *tree;
|
||||
bspface_t *face;
|
||||
int i;
|
||||
int count;
|
||||
|
||||
qprintf( "--- FaceBSP ---\n" );
|
||||
|
||||
tree = AllocTree ();
|
||||
|
||||
count = 0;
|
||||
for ( face = list ; face ; face = face->next ) {
|
||||
count++;
|
||||
for ( i = 0 ; i < face->w->numpoints ; i++ ) {
|
||||
AddPointToBounds( face->w->p[i], tree->mins, tree->maxs);
|
||||
}
|
||||
}
|
||||
qprintf( "%5i faces\n", count );
|
||||
|
||||
tree->headnode = AllocNode();
|
||||
VectorCopy( tree->mins, tree->headnode->mins );
|
||||
VectorCopy( tree->maxs, tree->headnode->maxs );
|
||||
c_faceLeafs = 0;
|
||||
|
||||
BuildFaceTree_r ( tree->headnode, list );
|
||||
|
||||
qprintf( "%5i leafs\n", c_faceLeafs );
|
||||
|
||||
return tree;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=================
|
||||
BspFaceForPortal
|
||||
=================
|
||||
*/
|
||||
bspface_t *BspFaceForPortal( portal_t *p ) {
|
||||
bspface_t *f;
|
||||
|
||||
f = AllocBspFace();
|
||||
f->w = CopyWinding( p->winding );
|
||||
f->planenum = p->onnode->planenum & ~1;
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
=================
|
||||
MakeStructuralBspFaceList
|
||||
=================
|
||||
*/
|
||||
bspface_t *MakeStructuralBspFaceList( bspbrush_t *list ) {
|
||||
bspbrush_t *b;
|
||||
int i;
|
||||
side_t *s;
|
||||
winding_t *w;
|
||||
bspface_t *f, *flist;
|
||||
|
||||
flist = NULL;
|
||||
for ( b = list ; b ; b = b->next ) {
|
||||
if ( b->detail ) {
|
||||
continue;
|
||||
}
|
||||
for ( i = 0 ; i < b->numsides ; i++ ) {
|
||||
s = &b->sides[i];
|
||||
w = s->winding;
|
||||
if ( !w ) {
|
||||
continue;
|
||||
}
|
||||
f = AllocBspFace();
|
||||
f->w = CopyWinding( w );
|
||||
f->planenum = s->planenum & ~1;
|
||||
f->next = flist;
|
||||
if (s->surfaceFlags & SURF_HINT) {
|
||||
//f->priority = HINT_PRIORITY;
|
||||
f->hint = qtrue;
|
||||
}
|
||||
flist = f;
|
||||
}
|
||||
}
|
||||
|
||||
return flist;
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
MakeVisibleBspFaceList
|
||||
=================
|
||||
*/
|
||||
bspface_t *MakeVisibleBspFaceList( bspbrush_t *list ) {
|
||||
bspbrush_t *b;
|
||||
int i;
|
||||
side_t *s;
|
||||
winding_t *w;
|
||||
bspface_t *f, *flist;
|
||||
|
||||
flist = NULL;
|
||||
for ( b = list ; b ; b = b->next ) {
|
||||
if ( b->detail ) {
|
||||
continue;
|
||||
}
|
||||
for ( i = 0 ; i < b->numsides ; i++ ) {
|
||||
s = &b->sides[i];
|
||||
w = s->visibleHull;
|
||||
if ( !w ) {
|
||||
continue;
|
||||
}
|
||||
f = AllocBspFace();
|
||||
f->w = CopyWinding( w );
|
||||
f->planenum = s->planenum & ~1;
|
||||
f->next = flist;
|
||||
if (s->surfaceFlags & SURF_HINT) {
|
||||
//f->priority = HINT_PRIORITY;
|
||||
f->hint = qtrue;
|
||||
}
|
||||
flist = f;
|
||||
}
|
||||
}
|
||||
|
||||
return flist;
|
||||
}
|
||||
|
554
q3map/fog.c
Normal file
554
q3map/fog.c
Normal file
|
@ -0,0 +1,554 @@
|
|||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 1999-2005 Id Software, Inc.
|
||||
|
||||
This file is part of Quake III Arena source code.
|
||||
|
||||
Quake III Arena source code 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.
|
||||
|
||||
Quake III Arena source code 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 Foobar; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
#include "qbsp.h"
|
||||
|
||||
|
||||
int c_fogFragment;
|
||||
int c_fogPatchFragments;
|
||||
|
||||
/*
|
||||
====================
|
||||
DrawSurfToMesh
|
||||
====================
|
||||
*/
|
||||
mesh_t *DrawSurfToMesh( mapDrawSurface_t *ds ) {
|
||||
mesh_t *m;
|
||||
|
||||
m = malloc( sizeof( *m ) );
|
||||
m->width = ds->patchWidth;
|
||||
m->height = ds->patchHeight;
|
||||
m->verts = malloc( sizeof(m->verts[0]) * m->width * m->height );
|
||||
memcpy( m->verts, ds->verts, sizeof(m->verts[0]) * m->width * m->height );
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
====================
|
||||
SplitMeshByPlane
|
||||
====================
|
||||
*/
|
||||
void SplitMeshByPlane( mesh_t *in, vec3_t normal, float dist, mesh_t **front, mesh_t **back ) {
|
||||
int w, h, split;
|
||||
float d[MAX_PATCH_SIZE][MAX_PATCH_SIZE];
|
||||
drawVert_t *dv, *v1, *v2;
|
||||
int c_front, c_back, c_on;
|
||||
mesh_t *f, *b;
|
||||
int i;
|
||||
float frac;
|
||||
int frontAprox, backAprox;
|
||||
|
||||
for ( i = 0 ; i < 2 ; i++ ) {
|
||||
dv = in->verts;
|
||||
c_front = 0;
|
||||
c_back = 0;
|
||||
c_on = 0;
|
||||
for ( h = 0 ; h < in->height ; h++ ) {
|
||||
for ( w = 0 ; w < in->width ; w++, dv++ ) {
|
||||
d[h][w] = DotProduct( dv->xyz, normal ) - dist;
|
||||
if ( d[h][w] > ON_EPSILON ) {
|
||||
c_front++;
|
||||
} else if ( d[h][w] < -ON_EPSILON ) {
|
||||
c_back++;
|
||||
} else {
|
||||
c_on++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*front = NULL;
|
||||
*back = NULL;
|
||||
|
||||
if ( !c_front ) {
|
||||
*back = in;
|
||||
return;
|
||||
}
|
||||
if ( !c_back ) {
|
||||
*front = in;
|
||||
return;
|
||||
}
|
||||
|
||||
// find a split point
|
||||
split = -1;
|
||||
for ( w = 0 ; w < in->width -1 ; w++ ) {
|
||||
if ( ( d[0][w] < 0 ) != ( d[0][w+1] < 0 ) ) {
|
||||
if ( split == -1 ) {
|
||||
split = w;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( split == -1 ) {
|
||||
if ( i == 1 ) {
|
||||
qprintf( "No crossing points in patch\n");
|
||||
*front = in;
|
||||
return;
|
||||
}
|
||||
|
||||
in = TransposeMesh( in );
|
||||
InvertMesh( in );
|
||||
continue;
|
||||
}
|
||||
|
||||
// make sure the split point stays the same for all other rows
|
||||
for ( h = 1 ; h < in->height ; h++ ) {
|
||||
for ( w = 0 ; w < in->width -1 ; w++ ) {
|
||||
if ( ( d[h][w] < 0 ) != ( d[h][w+1] < 0 ) ) {
|
||||
if ( w != split ) {
|
||||
_printf( "multiple crossing points for patch -- can't clip\n");
|
||||
*front = in;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( ( d[h][split] < 0 ) == ( d[h][split+1] < 0 ) ) {
|
||||
_printf( "differing crossing points for patch -- can't clip\n");
|
||||
*front = in;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
// create two new meshes
|
||||
f = malloc( sizeof( *f ) );
|
||||
f->width = split + 2;
|
||||
if ( ! (f->width & 1) ) {
|
||||
f->width++;
|
||||
frontAprox = 1;
|
||||
} else {
|
||||
frontAprox = 0;
|
||||
}
|
||||
if ( f->width > MAX_PATCH_SIZE ) {
|
||||
Error( "MAX_PATCH_SIZE after split");
|
||||
}
|
||||
f->height = in->height;
|
||||
f->verts = malloc( sizeof(f->verts[0]) * f->width * f->height );
|
||||
|
||||
b = malloc( sizeof( *b ) );
|
||||
b->width = in->width - split;
|
||||
if ( ! (b->width & 1) ) {
|
||||
b->width++;
|
||||
backAprox = 1;
|
||||
} else {
|
||||
backAprox = 0;
|
||||
}
|
||||
if ( b->width > MAX_PATCH_SIZE ) {
|
||||
Error( "MAX_PATCH_SIZE after split");
|
||||
}
|
||||
b->height = in->height;
|
||||
b->verts = malloc( sizeof(b->verts[0]) * b->width * b->height );
|
||||
|
||||
if ( d[0][0] > 0 ) {
|
||||
*front = f;
|
||||
*back = b;
|
||||
} else {
|
||||
*front = b;
|
||||
*back = f;
|
||||
}
|
||||
|
||||
// distribute the points
|
||||
for ( w = 0 ; w < in->width ; w++ ) {
|
||||
for ( h = 0 ; h < in->height ; h++ ) {
|
||||
if ( w <= split ) {
|
||||
f->verts[ h * f->width + w ] = in->verts[ h * in->width + w ];
|
||||
} else {
|
||||
b->verts[ h * b->width + w - split + backAprox ] = in->verts[ h * in->width + w ];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// clip the crossing line
|
||||
for ( h = 0 ; h < in->height ; h++ ) {
|
||||
dv = &f->verts[ h * f->width + split + 1 ];
|
||||
v1 = &in->verts[ h * in->width + split ];
|
||||
v2 = &in->verts[ h * in->width + split + 1 ];
|
||||
frac = d[h][split] / ( d[h][split] - d[h][split+1] );
|
||||
for ( i = 0 ; i < 10 ; i++ ) {
|
||||
dv->xyz[i] = v1->xyz[i] + frac * ( v2->xyz[i] - v1->xyz[i] );
|
||||
}
|
||||
dv->xyz[10] = 0;//set all 4 colors to 0
|
||||
if ( frontAprox ) {
|
||||
f->verts[ h * f->width + split + 2 ] = *dv;
|
||||
}
|
||||
b->verts[ h * b->width ] = *dv;
|
||||
if ( backAprox ) {
|
||||
b->verts[ h * b->width + 1 ] = *dv;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
PrintMesh( in );
|
||||
_printf("\n");
|
||||
PrintMesh( f );
|
||||
_printf("\n");
|
||||
PrintMesh( b );
|
||||
_printf("\n");
|
||||
*/
|
||||
|
||||
FreeMesh( in );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
====================
|
||||
ChopPatchByBrush
|
||||
====================
|
||||
*/
|
||||
qboolean ChopPatchByBrush( mapDrawSurface_t *ds, bspbrush_t *b ) {
|
||||
int i, j;
|
||||
side_t *s;
|
||||
plane_t *plane;
|
||||
mesh_t *outside[MAX_BRUSH_SIDES];
|
||||
int numOutside;
|
||||
mesh_t *m, *front, *back;
|
||||
mapDrawSurface_t *newds;
|
||||
|
||||
m = DrawSurfToMesh( ds );
|
||||
numOutside = 0;
|
||||
|
||||
// only split by the top and bottom planes to avoid
|
||||
// some messy patch clipping issues
|
||||
|
||||
for ( i = 4 ; i <= 5 ; i++ ) {
|
||||
s = &b->sides[ i ];
|
||||
plane = &mapplanes[ s->planenum ];
|
||||
|
||||
SplitMeshByPlane( m, plane->normal, plane->dist, &front, &back );
|
||||
|
||||
if ( !back ) {
|
||||
// nothing actually contained inside
|
||||
for ( j = 0 ; j < numOutside ; j++ ) {
|
||||
FreeMesh( outside[j] );
|
||||
}
|
||||
return qfalse;
|
||||
}
|
||||
m = back;
|
||||
|
||||
if ( front ) {
|
||||
if ( numOutside == MAX_BRUSH_SIDES ) {
|
||||
Error( "MAX_BRUSH_SIDES" );
|
||||
}
|
||||
outside[ numOutside ] = front;
|
||||
numOutside++;
|
||||
}
|
||||
}
|
||||
|
||||
// all of outside fragments become seperate drawsurfs
|
||||
c_fogPatchFragments += numOutside;
|
||||
for ( i = 0 ; i < numOutside ; i++ ) {
|
||||
newds = DrawSurfaceForMesh( outside[ i ] );
|
||||
newds->shaderInfo = ds->shaderInfo;
|
||||
FreeMesh( outside[ i ] );
|
||||
}
|
||||
|
||||
// replace ds with m
|
||||
ds->patchWidth = m->width;
|
||||
ds->patchHeight = m->height;
|
||||
ds->numVerts = m->width * m->height;
|
||||
free( ds->verts );
|
||||
ds->verts = malloc( ds->numVerts * sizeof( *ds->verts ) );
|
||||
memcpy( ds->verts, m->verts, ds->numVerts * sizeof( *ds->verts ) );
|
||||
|
||||
FreeMesh( m );
|
||||
|
||||
return qtrue;
|
||||
}
|
||||
|
||||
//===============================================================================
|
||||
|
||||
/*
|
||||
====================
|
||||
WindingFromDrawSurf
|
||||
====================
|
||||
*/
|
||||
winding_t *WindingFromDrawSurf( mapDrawSurface_t *ds ) {
|
||||
winding_t *w;
|
||||
int i;
|
||||
|
||||
w = AllocWinding( ds->numVerts );
|
||||
w->numpoints = ds->numVerts;
|
||||
for ( i = 0 ; i < ds->numVerts ; i++ ) {
|
||||
VectorCopy( ds->verts[i].xyz, w->p[i] );
|
||||
}
|
||||
return w;
|
||||
}
|
||||
|
||||
/*
|
||||
====================
|
||||
ChopFaceByBrush
|
||||
|
||||
There may be a fragment contained in the brush
|
||||
====================
|
||||
*/
|
||||
qboolean ChopFaceByBrush( mapDrawSurface_t *ds, bspbrush_t *b ) {
|
||||
int i, j;
|
||||
side_t *s;
|
||||
plane_t *plane;
|
||||
winding_t *w;
|
||||
winding_t *front, *back;
|
||||
winding_t *outside[MAX_BRUSH_SIDES];
|
||||
int numOutside;
|
||||
mapDrawSurface_t *newds;
|
||||
drawVert_t *dv;
|
||||
shaderInfo_t *si;
|
||||
float mins[2];
|
||||
|
||||
// brush primitive :
|
||||
// axis base
|
||||
vec3_t texX,texY;
|
||||
vec_t x,y;
|
||||
|
||||
w = WindingFromDrawSurf( ds );
|
||||
numOutside = 0;
|
||||
|
||||
for ( i = 0 ; i < b->numsides ; i++ ) {
|
||||
s = &b->sides[ i ];
|
||||
if ( s->backSide ) {
|
||||
continue;
|
||||
}
|
||||
plane = &mapplanes[ s->planenum ];
|
||||
|
||||
// handle coplanar outfacing (don't fog)
|
||||
if ( ds->side->planenum == s->planenum ) {
|
||||
return qfalse;
|
||||
}
|
||||
|
||||
// handle coplanar infacing (keep inside)
|
||||
if ( ( ds->side->planenum ^ 1 ) == s->planenum ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// general case
|
||||
ClipWindingEpsilon( w, plane->normal, plane->dist, ON_EPSILON,
|
||||
&front, &back );
|
||||
FreeWinding( w );
|
||||
if ( !back ) {
|
||||
// nothing actually contained inside
|
||||
for ( j = 0 ; j < numOutside ; j++ ) {
|
||||
FreeWinding( outside[j] );
|
||||
}
|
||||
return qfalse;
|
||||
}
|
||||
if ( front ) {
|
||||
if ( numOutside == MAX_BRUSH_SIDES ) {
|
||||
Error( "MAX_BRUSH_SIDES" );
|
||||
}
|
||||
outside[ numOutside ] = front;
|
||||
numOutside++;
|
||||
}
|
||||
w = back;
|
||||
}
|
||||
|
||||
// all of outside fragments become seperate drawsurfs
|
||||
// linked to the same side
|
||||
c_fogFragment += numOutside;
|
||||
s = ds->side;
|
||||
|
||||
for ( i = 0 ; i < numOutside ; i++ ) {
|
||||
newds = DrawSurfaceForSide( ds->mapBrush, s, outside[i] );
|
||||
FreeWinding( outside[i] );
|
||||
}
|
||||
|
||||
|
||||
// replace ds->verts with the verts for w
|
||||
ds->numVerts = w->numpoints;
|
||||
free( ds->verts );
|
||||
|
||||
ds->verts = malloc( ds->numVerts * sizeof( *ds->verts ) );
|
||||
memset( ds->verts, 0, ds->numVerts * sizeof( *ds->verts ) );
|
||||
|
||||
si = s->shaderInfo;
|
||||
|
||||
mins[0] = 9999;
|
||||
mins[1] = 9999;
|
||||
|
||||
// compute s/t coordinates from brush primitive texture matrix
|
||||
// compute axis base
|
||||
ComputeAxisBase( mapplanes[s->planenum].normal, texX, texY );
|
||||
|
||||
for ( j = 0 ; j < w->numpoints ; j++ ) {
|
||||
dv = ds->verts + j;
|
||||
VectorCopy( w->p[j], dv->xyz );
|
||||
|
||||
if (g_bBrushPrimit==BPRIMIT_OLDBRUSHES)
|
||||
{
|
||||
// calculate texture s/t
|
||||
dv->st[0] = s->vecs[0][3] + DotProduct( s->vecs[0], dv->xyz );
|
||||
dv->st[1] = s->vecs[1][3] + DotProduct( s->vecs[1], dv->xyz );
|
||||
dv->st[0] /= si->width;
|
||||
dv->st[1] /= si->height;
|
||||
}
|
||||
else
|
||||
{
|
||||
// calculate texture s/t from brush primitive texture matrix
|
||||
x = DotProduct( dv->xyz, texX );
|
||||
y = DotProduct( dv->xyz, texY );
|
||||
dv->st[0]=s->texMat[0][0]*x+s->texMat[0][1]*y+s->texMat[0][2];
|
||||
dv->st[1]=s->texMat[1][0]*x+s->texMat[1][1]*y+s->texMat[1][2];
|
||||
}
|
||||
|
||||
if ( dv->st[0] < mins[0] ) {
|
||||
mins[0] = dv->st[0];
|
||||
}
|
||||
if ( dv->st[1] < mins[1] ) {
|
||||
mins[1] = dv->st[1];
|
||||
}
|
||||
|
||||
// copy normal
|
||||
VectorCopy ( mapplanes[s->planenum].normal, dv->normal );
|
||||
}
|
||||
|
||||
// adjust the texture coordinates to be as close to 0 as possible
|
||||
if ( !si->globalTexture ) {
|
||||
mins[0] = floor( mins[0] );
|
||||
mins[1] = floor( mins[1] );
|
||||
for ( i = 0 ; i < w->numpoints ; i++ ) {
|
||||
dv = ds->verts + i;
|
||||
dv->st[0] -= mins[0];
|
||||
dv->st[1] -= mins[1];
|
||||
}
|
||||
}
|
||||
|
||||
return qtrue;
|
||||
}
|
||||
|
||||
//===============================================================================
|
||||
|
||||
|
||||
/*
|
||||
=====================
|
||||
FogDrawSurfs
|
||||
|
||||
Call after the surface list has been pruned,
|
||||
before tjunction fixing
|
||||
before lightmap allocation
|
||||
=====================
|
||||
*/
|
||||
void FogDrawSurfs( void ) {
|
||||
int i, j, k;
|
||||
mapDrawSurface_t *ds;
|
||||
bspbrush_t *b;
|
||||
vec3_t mins, maxs;
|
||||
int c_fogged;
|
||||
int numBaseDrawSurfs;
|
||||
dfog_t *fog;
|
||||
|
||||
qprintf("----- FogDrawsurfs -----\n");
|
||||
|
||||
c_fogged = 0;
|
||||
c_fogFragment = 0;
|
||||
|
||||
// find all fog brushes
|
||||
for ( b = entities[0].brushes ; b ; b = b->next ) {
|
||||
if ( !(b->contents & CONTENTS_FOG) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( numFogs == MAX_MAP_FOGS ) {
|
||||
Error( "MAX_MAP_FOGS" );
|
||||
}
|
||||
fog = &dfogs[numFogs];
|
||||
numFogs++;
|
||||
fog->brushNum = b->outputNumber;
|
||||
|
||||
// find a side with a valid shaderInfo
|
||||
// non-axial fog columns may have bevel planes that need to be skipped
|
||||
for ( i = 0 ; i < b->numsides ; i++ ) {
|
||||
if ( b->sides[i].shaderInfo && (b->sides[i].shaderInfo->contents & CONTENTS_FOG) ) {
|
||||
strcpy( fog->shader, b->sides[i].shaderInfo->shader );
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( i == b->numsides ) {
|
||||
continue; // shouldn't happen
|
||||
}
|
||||
|
||||
fog->visibleSide = -1;
|
||||
|
||||
// clip each surface into this, but don't clip any of
|
||||
// the resulting fragments to the same brush
|
||||
numBaseDrawSurfs = numMapDrawSurfs;
|
||||
for ( i = 0 ; i < numBaseDrawSurfs ; i++ ) {
|
||||
ds = &mapDrawSurfs[i];
|
||||
|
||||
// bound the drawsurf
|
||||
ClearBounds( mins, maxs );
|
||||
for ( j = 0 ; j < ds->numVerts ; j++ ) {
|
||||
AddPointToBounds( ds->verts[j].xyz, mins, maxs );
|
||||
}
|
||||
|
||||
// check against the fog brush
|
||||
for ( k = 0 ; k < 3 ; k++ ) {
|
||||
if ( mins[k] > b->maxs[k] ) {
|
||||
break;
|
||||
}
|
||||
if ( maxs[k] < b->mins[k] ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( k < 3 ) {
|
||||
continue; // bboxes don't intersect
|
||||
}
|
||||
|
||||
if ( ds->mapBrush == b ) {
|
||||
int s;
|
||||
|
||||
s = ds->side - b->sides;
|
||||
if ( s <= 6 ) { // not one of the reversed inside faces
|
||||
// this is a visible fog plane
|
||||
if ( fog->visibleSide != -1 ) {
|
||||
_printf( "WARNING: fog brush %i has multiple visible sides\n", b->brushnum );
|
||||
}
|
||||
fog->visibleSide = s;
|
||||
}
|
||||
}
|
||||
|
||||
if ( ds->miscModel ) {
|
||||
// we could write splitting code for trimodels if we wanted to...
|
||||
c_fogged++;
|
||||
ds->fogNum = numFogs - 1;
|
||||
} else if ( ds->patch ) {
|
||||
if ( ChopPatchByBrush( ds, b ) ) {
|
||||
c_fogged++;
|
||||
ds->fogNum = numFogs - 1;
|
||||
}
|
||||
} else {
|
||||
if ( ChopFaceByBrush( ds, b ) ) {
|
||||
c_fogged++;
|
||||
ds->fogNum = numFogs - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// split the drawsurfs by the fog brushes
|
||||
|
||||
qprintf( "%5i fogs\n", numFogs );
|
||||
qprintf( "%5i fog polygon fragments\n", c_fogFragment );
|
||||
qprintf( "%5i fog patch fragments\n", c_fogPatchFragments );
|
||||
qprintf( "%5i fogged drawsurfs\n", c_fogged );
|
||||
}
|
232
q3map/gldraw.c
Normal file
232
q3map/gldraw.c
Normal file
|
@ -0,0 +1,232 @@
|
|||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 1999-2005 Id Software, Inc.
|
||||
|
||||
This file is part of Quake III Arena source code.
|
||||
|
||||
Quake III Arena source code 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.
|
||||
|
||||
Quake III Arena source code 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 Foobar; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
#include <windows.h>
|
||||
#include <GL/gl.h>
|
||||
#include <GL/glu.h>
|
||||
#include <GL/glaux.h>
|
||||
|
||||
#include "qbsp.h"
|
||||
|
||||
// can't use the glvertex3fv functions, because the vec3_t fields
|
||||
// could be either floats or doubles, depending on DOUBLEVEC_T
|
||||
|
||||
qboolean drawflag;
|
||||
vec3_t draw_mins, draw_maxs;
|
||||
|
||||
|
||||
#define WIN_SIZE 512
|
||||
|
||||
void InitWindow (void)
|
||||
{
|
||||
auxInitDisplayMode (AUX_SINGLE | AUX_RGB);
|
||||
auxInitPosition (0, 0, WIN_SIZE, WIN_SIZE);
|
||||
auxInitWindow ("qcsg");
|
||||
}
|
||||
|
||||
void Draw_ClearWindow (void)
|
||||
{
|
||||
static int init;
|
||||
int w, h, g;
|
||||
vec_t mx, my;
|
||||
|
||||
if (!drawflag)
|
||||
return;
|
||||
|
||||
if (!init)
|
||||
{
|
||||
init = qtrue;
|
||||
InitWindow ();
|
||||
}
|
||||
|
||||
glClearColor (1,0.8,0.8,0);
|
||||
glClear (GL_COLOR_BUFFER_BIT);
|
||||
|
||||
w = (draw_maxs[0] - draw_mins[0]);
|
||||
h = (draw_maxs[1] - draw_mins[1]);
|
||||
|
||||
mx = draw_mins[0] + w/2;
|
||||
my = draw_mins[1] + h/2;
|
||||
|
||||
g = w > h ? w : h;
|
||||
|
||||
glLoadIdentity ();
|
||||
gluPerspective (90, 1, 2, 16384);
|
||||
gluLookAt (mx, my, draw_maxs[2] + g/2, mx , my, draw_maxs[2], 0, 1, 0);
|
||||
|
||||
glColor3f (0,0,0);
|
||||
// glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
|
||||
glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
|
||||
glDisable (GL_DEPTH_TEST);
|
||||
glEnable (GL_BLEND);
|
||||
glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
#if 0
|
||||
glColor4f (1,0,0,0.5);
|
||||
glBegin (GL_POLYGON);
|
||||
|
||||
glVertex3f (0, 500, 0);
|
||||
glVertex3f (0, 900, 0);
|
||||
glVertex3f (0, 900, 100);
|
||||
glVertex3f (0, 500, 100);
|
||||
|
||||
glEnd ();
|
||||
#endif
|
||||
|
||||
glFlush ();
|
||||
|
||||
}
|
||||
|
||||
void Draw_SetRed (void)
|
||||
{
|
||||
if (!drawflag)
|
||||
return;
|
||||
|
||||
glColor3f (1,0,0);
|
||||
}
|
||||
|
||||
void Draw_SetGrey (void)
|
||||
{
|
||||
if (!drawflag)
|
||||
return;
|
||||
|
||||
glColor3f (0.5,0.5,0.5);
|
||||
}
|
||||
|
||||
void Draw_SetBlack (void)
|
||||
{
|
||||
if (!drawflag)
|
||||
return;
|
||||
|
||||
glColor3f (0,0,0);
|
||||
}
|
||||
|
||||
void DrawWinding (winding_t *w)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!drawflag)
|
||||
return;
|
||||
|
||||
glColor4f (0,0,0,0.5);
|
||||
glBegin (GL_LINE_LOOP);
|
||||
for (i=0 ; i<w->numpoints ; i++)
|
||||
glVertex3f (w->p[i][0],w->p[i][1],w->p[i][2] );
|
||||
glEnd ();
|
||||
|
||||
glColor4f (0,1,0,0.3);
|
||||
glBegin (GL_POLYGON);
|
||||
for (i=0 ; i<w->numpoints ; i++)
|
||||
glVertex3f (w->p[i][0],w->p[i][1],w->p[i][2] );
|
||||
glEnd ();
|
||||
|
||||
glFlush ();
|
||||
}
|
||||
|
||||
void DrawAuxWinding (winding_t *w)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!drawflag)
|
||||
return;
|
||||
|
||||
glColor4f (0,0,0,0.5);
|
||||
glBegin (GL_LINE_LOOP);
|
||||
for (i=0 ; i<w->numpoints ; i++)
|
||||
glVertex3f (w->p[i][0],w->p[i][1],w->p[i][2] );
|
||||
glEnd ();
|
||||
|
||||
glColor4f (1,0,0,0.3);
|
||||
glBegin (GL_POLYGON);
|
||||
for (i=0 ; i<w->numpoints ; i++)
|
||||
glVertex3f (w->p[i][0],w->p[i][1],w->p[i][2] );
|
||||
glEnd ();
|
||||
|
||||
glFlush ();
|
||||
}
|
||||
|
||||
//============================================================
|
||||
|
||||
#define GLSERV_PORT 25001
|
||||
|
||||
qboolean wins_init;
|
||||
int draw_socket;
|
||||
|
||||
void GLS_BeginScene (void)
|
||||
{
|
||||
WSADATA winsockdata;
|
||||
WORD wVersionRequested;
|
||||
struct sockaddr_in address;
|
||||
int r;
|
||||
|
||||
if (!wins_init)
|
||||
{
|
||||
wins_init = qtrue;
|
||||
|
||||
wVersionRequested = MAKEWORD(1, 1);
|
||||
|
||||
r = WSAStartup (MAKEWORD(1, 1), &winsockdata);
|
||||
|
||||
if (r)
|
||||
Error ("Winsock initialization failed.");
|
||||
|
||||
}
|
||||
|
||||
// connect a socket to the server
|
||||
|
||||
draw_socket = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||
if (draw_socket == -1)
|
||||
Error ("draw_socket failed");
|
||||
|
||||
address.sin_family = AF_INET;
|
||||
address.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
||||
address.sin_port = GLSERV_PORT;
|
||||
r = connect (draw_socket, (struct sockaddr *)&address, sizeof(address));
|
||||
if (r == -1)
|
||||
{
|
||||
closesocket (draw_socket);
|
||||
draw_socket = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void GLS_Winding (winding_t *w, int code)
|
||||
{
|
||||
byte buf[1024];
|
||||
int i, j;
|
||||
|
||||
if (!draw_socket)
|
||||
return;
|
||||
|
||||
((int *)buf)[0] = w->numpoints;
|
||||
((int *)buf)[1] = code;
|
||||
for (i=0 ; i<w->numpoints ; i++)
|
||||
for (j=0 ; j<3 ; j++)
|
||||
((float *)buf)[2+i*3+j] = w->p[i][j];
|
||||
|
||||
send (draw_socket, buf, w->numpoints*12+8, 0);
|
||||
}
|
||||
|
||||
void GLS_EndScene (void)
|
||||
{
|
||||
closesocket (draw_socket);
|
||||
draw_socket = 0;
|
||||
}
|
148
q3map/glfile.c
Normal file
148
q3map/glfile.c
Normal file
|
@ -0,0 +1,148 @@
|
|||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 1999-2005 Id Software, Inc.
|
||||
|
||||
This file is part of Quake III Arena source code.
|
||||
|
||||
Quake III Arena source code 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.
|
||||
|
||||
Quake III Arena source code 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 Foobar; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
#include "qbsp.h"
|
||||
|
||||
int c_glfaces;
|
||||
|
||||
int PortalVisibleSides (portal_t *p)
|
||||
{
|
||||
int fcon, bcon;
|
||||
|
||||
if (!p->onnode)
|
||||
return 0; // outside
|
||||
|
||||
fcon = p->nodes[0]->opaque;
|
||||
bcon = p->nodes[1]->opaque;
|
||||
|
||||
// same contents never create a face
|
||||
if (fcon == bcon)
|
||||
return 0;
|
||||
|
||||
if (!fcon)
|
||||
return 1;
|
||||
if (!bcon)
|
||||
return 2;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void OutputWinding (winding_t *w, FILE *glview)
|
||||
{
|
||||
static int level = 128;
|
||||
vec_t light;
|
||||
int i;
|
||||
|
||||
fprintf (glview, "%i\n", w->numpoints);
|
||||
level+=28;
|
||||
light = (level&255)/255.0;
|
||||
for (i=0 ; i<w->numpoints ; i++)
|
||||
{
|
||||
fprintf (glview, "%6.3f %6.3f %6.3f %6.3f %6.3f %6.3f\n",
|
||||
w->p[i][0],
|
||||
w->p[i][1],
|
||||
w->p[i][2],
|
||||
light,
|
||||
light,
|
||||
light);
|
||||
}
|
||||
fprintf (glview, "\n");
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
OutputPortal
|
||||
=============
|
||||
*/
|
||||
void OutputPortal (portal_t *p, FILE *glview)
|
||||
{
|
||||
winding_t *w;
|
||||
int sides;
|
||||
|
||||
sides = PortalVisibleSides (p);
|
||||
if (!sides)
|
||||
return;
|
||||
|
||||
c_glfaces++;
|
||||
|
||||
w = p->winding;
|
||||
|
||||
if (sides == 2) // back side
|
||||
w = ReverseWinding (w);
|
||||
|
||||
OutputWinding (w, glview);
|
||||
|
||||
if (sides == 2)
|
||||
FreeWinding(w);
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
WriteGLView_r
|
||||
=============
|
||||
*/
|
||||
void WriteGLView_r (node_t *node, FILE *glview)
|
||||
{
|
||||
portal_t *p, *nextp;
|
||||
|
||||
if (node->planenum != PLANENUM_LEAF)
|
||||
{
|
||||
WriteGLView_r (node->children[0], glview);
|
||||
WriteGLView_r (node->children[1], glview);
|
||||
return;
|
||||
}
|
||||
|
||||
// write all the portals
|
||||
for (p=node->portals ; p ; p=nextp)
|
||||
{
|
||||
if (p->nodes[0] == node)
|
||||
{
|
||||
OutputPortal (p, glview);
|
||||
nextp = p->next[0];
|
||||
}
|
||||
else
|
||||
nextp = p->next[1];
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
WriteGLView
|
||||
=============
|
||||
*/
|
||||
void WriteGLView (tree_t *tree, char *source)
|
||||
{
|
||||
char name[1024];
|
||||
FILE *glview;
|
||||
|
||||
c_glfaces = 0;
|
||||
sprintf (name, "%s%s.gl",outbase, source);
|
||||
_printf ("Writing %s\n", name);
|
||||
|
||||
glview = fopen (name, "w");
|
||||
if (!glview)
|
||||
Error ("Couldn't open %s", name);
|
||||
WriteGLView_r (tree->headnode, glview);
|
||||
fclose (glview);
|
||||
|
||||
_printf ("%5i c_glfaces\n", c_glfaces);
|
||||
}
|
||||
|
100
q3map/leakfile.c
Normal file
100
q3map/leakfile.c
Normal file
|
@ -0,0 +1,100 @@
|
|||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 1999-2005 Id Software, Inc.
|
||||
|
||||
This file is part of Quake III Arena source code.
|
||||
|
||||
Quake III Arena source code 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.
|
||||
|
||||
Quake III Arena source code 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 Foobar; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
#include "qbsp.h"
|
||||
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
LEAF FILE GENERATION
|
||||
|
||||
Save out name.line for qe3 to read
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
=============
|
||||
LeakFile
|
||||
|
||||
Finds the shortest possible chain of portals
|
||||
that leads from the outside leaf to a specifically
|
||||
occupied leaf
|
||||
=============
|
||||
*/
|
||||
void LeakFile (tree_t *tree)
|
||||
{
|
||||
vec3_t mid;
|
||||
FILE *linefile;
|
||||
char filename[1024];
|
||||
node_t *node;
|
||||
int count;
|
||||
|
||||
if (!tree->outside_node.occupied)
|
||||
return;
|
||||
|
||||
qprintf ("--- LeakFile ---\n");
|
||||
|
||||
//
|
||||
// write the points to the file
|
||||
//
|
||||
sprintf (filename, "%s.lin", source);
|
||||
linefile = fopen (filename, "w");
|
||||
if (!linefile)
|
||||
Error ("Couldn't open %s\n", filename);
|
||||
|
||||
count = 0;
|
||||
node = &tree->outside_node;
|
||||
while (node->occupied > 1)
|
||||
{
|
||||
int next;
|
||||
portal_t *p, *nextportal;
|
||||
node_t *nextnode;
|
||||
int s;
|
||||
|
||||
// find the best portal exit
|
||||
next = node->occupied;
|
||||
for (p=node->portals ; p ; p = p->next[!s])
|
||||
{
|
||||
s = (p->nodes[0] == node);
|
||||
if (p->nodes[s]->occupied
|
||||
&& p->nodes[s]->occupied < next)
|
||||
{
|
||||
nextportal = p;
|
||||
nextnode = p->nodes[s];
|
||||
next = nextnode->occupied;
|
||||
}
|
||||
}
|
||||
node = nextnode;
|
||||
WindingCenter (nextportal->winding, mid);
|
||||
fprintf (linefile, "%f %f %f\n", mid[0], mid[1], mid[2]);
|
||||
count++;
|
||||
}
|
||||
// add the occupant center
|
||||
GetVectorForKey (node->occupant, "origin", mid);
|
||||
|
||||
fprintf (linefile, "%f %f %f\n", mid[0], mid[1], mid[2]);
|
||||
qprintf ("%5i point linefile\n", count+1);
|
||||
|
||||
fclose (linefile);
|
||||
}
|
||||
|
2149
q3map/light.c
Normal file
2149
q3map/light.c
Normal file
File diff suppressed because it is too large
Load diff
151
q3map/light.h
Normal file
151
q3map/light.h
Normal file
|
@ -0,0 +1,151 @@
|
|||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 1999-2005 Id Software, Inc.
|
||||
|
||||
This file is part of Quake III Arena source code.
|
||||
|
||||
Quake III Arena source code 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.
|
||||
|
||||
Quake III Arena source code 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 Foobar; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
#include "cmdlib.h"
|
||||
#include "mathlib.h"
|
||||
#include "bspfile.h"
|
||||
#include "polylib.h"
|
||||
#include "imagelib.h"
|
||||
#include "threads.h"
|
||||
#include "scriplib.h"
|
||||
|
||||
#include "shaders.h"
|
||||
#include "mesh.h"
|
||||
|
||||
|
||||
|
||||
typedef enum
|
||||
{
|
||||
emit_point,
|
||||
emit_area,
|
||||
emit_spotlight,
|
||||
emit_sun
|
||||
} emittype_t;
|
||||
|
||||
#define MAX_LIGHT_EDGES 8
|
||||
typedef struct light_s
|
||||
{
|
||||
struct light_s *next;
|
||||
emittype_t type;
|
||||
struct shaderInfo_s *si;
|
||||
|
||||
vec3_t origin;
|
||||
vec3_t normal; // for surfaces, spotlights, and suns
|
||||
float dist; // plane location along normal
|
||||
|
||||
qboolean linearLight;
|
||||
int photons;
|
||||
int style;
|
||||
vec3_t color;
|
||||
float radiusByDist; // for spotlights
|
||||
|
||||
qboolean twosided; // fog lights both sides
|
||||
|
||||
winding_t *w;
|
||||
vec3_t emitColor; // full out-of-gamut value
|
||||
} light_t;
|
||||
|
||||
|
||||
extern float lightscale;
|
||||
extern float ambient;
|
||||
extern float maxlight;
|
||||
extern float direct_scale;
|
||||
extern float entity_scale;
|
||||
|
||||
extern qboolean noSurfaces;
|
||||
|
||||
//===============================================================
|
||||
|
||||
// light_trace.c
|
||||
|
||||
// a facet is a subdivided element of a patch aproximation or model
|
||||
typedef struct cFacet_s {
|
||||
float surface[4];
|
||||
int numBoundaries; // either 3 or 4, anything less is degenerate
|
||||
float boundaries[4][4]; // positive is outside the bounds
|
||||
|
||||
vec3_t points[4]; // needed for area light subdivision
|
||||
|
||||
float textureMatrix[2][4]; // compute texture coordinates at point of impact for translucency
|
||||
} cFacet_t;
|
||||
|
||||
typedef struct {
|
||||
vec3_t mins, maxs;
|
||||
vec3_t origin;
|
||||
float radius;
|
||||
|
||||
qboolean patch;
|
||||
|
||||
int numFacets;
|
||||
cFacet_t *facets;
|
||||
|
||||
shaderInfo_t *shader; // for translucency
|
||||
} surfaceTest_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
vec3_t filter; // starts out 1.0, 1.0, 1.0, may be reduced if
|
||||
// transparent surfaces are crossed
|
||||
|
||||
vec3_t hit; // the impact point of a completely opaque surface
|
||||
float hitFraction; // 0 = at start, 1.0 = at end
|
||||
qboolean passSolid;
|
||||
} trace_t;
|
||||
|
||||
extern surfaceTest_t *surfaceTest[MAX_MAP_DRAW_SURFS];
|
||||
|
||||
void InitTrace( void );
|
||||
|
||||
// traceWork_t is only a parameter to crutch up poor large local allocations on
|
||||
// winNT and macOS. It should be allocated in the worker function, but never
|
||||
// looked at.
|
||||
typedef struct {
|
||||
vec3_t start, end;
|
||||
int numOpenLeafs;
|
||||
int openLeafNumbers[MAX_MAP_LEAFS];
|
||||
trace_t *trace;
|
||||
int patchshadows;
|
||||
} traceWork_t;
|
||||
|
||||
void TraceLine( const vec3_t start, const vec3_t stop, trace_t *trace,
|
||||
qboolean testAll, traceWork_t *tw );
|
||||
qboolean PointInSolid( vec3_t start );
|
||||
|
||||
//===============================================================
|
||||
|
||||
//===============================================================
|
||||
|
||||
|
||||
typedef struct {
|
||||
int textureNum;
|
||||
int x, y, width, height;
|
||||
|
||||
// for patches
|
||||
qboolean patch;
|
||||
mesh_t mesh;
|
||||
|
||||
// for faces
|
||||
vec3_t origin;
|
||||
vec3_t vecs[3];
|
||||
} lightmap_t;
|
||||
|
||||
|
944
q3map/light_trace.c
Normal file
944
q3map/light_trace.c
Normal file
|
@ -0,0 +1,944 @@
|
|||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 1999-2005 Id Software, Inc.
|
||||
|
||||
This file is part of Quake III Arena source code.
|
||||
|
||||
Quake III Arena source code 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.
|
||||
|
||||
Quake III Arena source code 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 Foobar; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
#include "light.h"
|
||||
|
||||
|
||||
|
||||
#define CURVE_FACET_ERROR 8
|
||||
|
||||
int c_totalTrace;
|
||||
int c_cullTrace, c_testTrace;
|
||||
int c_testFacets;
|
||||
|
||||
surfaceTest_t *surfaceTest[MAX_MAP_DRAW_SURFS];
|
||||
|
||||
/*
|
||||
=====================
|
||||
CM_GenerateBoundaryForPoints
|
||||
=====================
|
||||
*/
|
||||
void CM_GenerateBoundaryForPoints( float boundary[4], float plane[4], vec3_t a, vec3_t b ) {
|
||||
vec3_t d1;
|
||||
|
||||
// amke a perpendicular vector to the edge and the surface
|
||||
VectorSubtract( b, a, d1 );
|
||||
CrossProduct( plane, d1, boundary );
|
||||
VectorNormalize( boundary, boundary );
|
||||
boundary[3] = DotProduct( a, boundary );
|
||||
}
|
||||
|
||||
/*
|
||||
=====================
|
||||
TextureMatrixFromPoints
|
||||
=====================
|
||||
*/
|
||||
void TextureMatrixFromPoints( cFacet_t *f, drawVert_t *a, drawVert_t *b, drawVert_t *c ) {
|
||||
int i, j;
|
||||
float t;
|
||||
float m[3][4];
|
||||
float s;
|
||||
|
||||
// This is an incredibly stupid way of solving a three variable equation
|
||||
for ( i = 0 ; i < 2 ; i++ ) {
|
||||
|
||||
m[0][0] = a->xyz[0];
|
||||
m[0][1] = a->xyz[1];
|
||||
m[0][2] = a->xyz[2];
|
||||
m[0][3] = a->st[i];
|
||||
|
||||
m[1][0] = b->xyz[0];
|
||||
m[1][1] = b->xyz[1];
|
||||
m[1][2] = b->xyz[2];
|
||||
m[1][3] = b->st[i];
|
||||
|
||||
m[2][0] = c->xyz[0];
|
||||
m[2][1] = c->xyz[1];
|
||||
m[2][2] = c->xyz[2];
|
||||
m[2][3] = c->st[i];
|
||||
|
||||
if ( fabs(m[1][0]) > fabs(m[0][0]) && fabs(m[1][0]) > fabs(m[2][0]) ) {
|
||||
for ( j = 0 ; j < 4 ; j ++ ) {
|
||||
t = m[0][j];
|
||||
m[0][j] = m[1][j];
|
||||
m[1][j] = t;
|
||||
}
|
||||
} else if ( fabs(m[2][0]) > fabs(m[0][0]) && fabs(m[2][0]) > fabs(m[1][0]) ) {
|
||||
for ( j = 0 ; j < 4 ; j ++ ) {
|
||||
t = m[0][j];
|
||||
m[0][j] = m[2][j];
|
||||
m[2][j] = t;
|
||||
}
|
||||
}
|
||||
|
||||
s = 1.0 / m[0][0];
|
||||
m[0][0] *= s;
|
||||
m[0][1] *= s;
|
||||
m[0][2] *= s;
|
||||
m[0][3] *= s;
|
||||
|
||||
s = m[1][0];
|
||||
m[1][0] -= m[0][0] * s;
|
||||
m[1][1] -= m[0][1] * s;
|
||||
m[1][2] -= m[0][2] * s;
|
||||
m[1][3] -= m[0][3] * s;
|
||||
|
||||
s = m[2][0];
|
||||
m[2][0] -= m[0][0] * s;
|
||||
m[2][1] -= m[0][1] * s;
|
||||
m[2][2] -= m[0][2] * s;
|
||||
m[2][3] -= m[0][3] * s;
|
||||
|
||||
if ( fabs(m[2][1]) > fabs(m[1][1]) ) {
|
||||
for ( j = 0 ; j < 4 ; j ++ ) {
|
||||
t = m[1][j];
|
||||
m[1][j] = m[2][j];
|
||||
m[2][j] = t;
|
||||
}
|
||||
}
|
||||
|
||||
s = 1.0 / m[1][1];
|
||||
m[1][0] *= s;
|
||||
m[1][1] *= s;
|
||||
m[1][2] *= s;
|
||||
m[1][3] *= s;
|
||||
|
||||
s = m[2][1];
|
||||
m[2][0] -= m[1][0] * s;
|
||||
m[2][1] -= m[1][1] * s;
|
||||
m[2][2] -= m[1][2] * s;
|
||||
m[2][3] -= m[1][3] * s;
|
||||
|
||||
s = 1.0 / m[2][2];
|
||||
m[2][0] *= s;
|
||||
m[2][1] *= s;
|
||||
m[2][2] *= s;
|
||||
m[2][3] *= s;
|
||||
|
||||
f->textureMatrix[i][2] = m[2][3];
|
||||
f->textureMatrix[i][1] = m[1][3] - f->textureMatrix[i][2] * m[1][2];
|
||||
f->textureMatrix[i][0] = m[0][3] - f->textureMatrix[i][2] * m[0][2] - f->textureMatrix[i][1] * m[0][1];
|
||||
|
||||
f->textureMatrix[i][3] = 0;
|
||||
/*
|
||||
s = fabs( DotProduct( a->xyz, f->textureMatrix[i] ) - a->st[i] );
|
||||
if ( s > 0.01 ) {
|
||||
Error( "Bad textureMatrix" );
|
||||
}
|
||||
s = fabs( DotProduct( b->xyz, f->textureMatrix[i] ) - b->st[i] );
|
||||
if ( s > 0.01 ) {
|
||||
Error( "Bad textureMatrix" );
|
||||
}
|
||||
s = fabs( DotProduct( c->xyz, f->textureMatrix[i] ) - c->st[i] );
|
||||
if ( s > 0.01 ) {
|
||||
Error( "Bad textureMatrix" );
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
=====================
|
||||
CM_GenerateFacetFor3Points
|
||||
=====================
|
||||
*/
|
||||
qboolean CM_GenerateFacetFor3Points( cFacet_t *f, drawVert_t *a, drawVert_t *b, drawVert_t *c ) {
|
||||
// if we can't generate a valid plane for the points, ignore the facet
|
||||
if ( !PlaneFromPoints( f->surface, a->xyz, b->xyz, c->xyz ) ) {
|
||||
f->numBoundaries = 0;
|
||||
return qfalse;
|
||||
}
|
||||
|
||||
// make boundaries
|
||||
f->numBoundaries = 3;
|
||||
|
||||
CM_GenerateBoundaryForPoints( f->boundaries[0], f->surface, a->xyz, b->xyz );
|
||||
CM_GenerateBoundaryForPoints( f->boundaries[1], f->surface, b->xyz, c->xyz );
|
||||
CM_GenerateBoundaryForPoints( f->boundaries[2], f->surface, c->xyz, a->xyz );
|
||||
|
||||
VectorCopy( a->xyz, f->points[0] );
|
||||
VectorCopy( b->xyz, f->points[1] );
|
||||
VectorCopy( c->xyz, f->points[2] );
|
||||
|
||||
TextureMatrixFromPoints( f, a, b, c );
|
||||
|
||||
return qtrue;
|
||||
}
|
||||
|
||||
/*
|
||||
=====================
|
||||
CM_GenerateFacetFor4Points
|
||||
|
||||
Attempts to use four points as a planar quad
|
||||
=====================
|
||||
*/
|
||||
#define PLANAR_EPSILON 0.1
|
||||
qboolean CM_GenerateFacetFor4Points( cFacet_t *f, drawVert_t *a, drawVert_t *b, drawVert_t *c, drawVert_t *d ) {
|
||||
float dist;
|
||||
int i;
|
||||
vec4_t plane;
|
||||
|
||||
// if we can't generate a valid plane for the points, ignore the facet
|
||||
if ( !PlaneFromPoints( f->surface, a->xyz, b->xyz, c->xyz ) ) {
|
||||
f->numBoundaries = 0;
|
||||
return qfalse;
|
||||
}
|
||||
|
||||
// if the fourth point is also on the plane, we can make a quad facet
|
||||
dist = DotProduct( d->xyz, f->surface ) - f->surface[3];
|
||||
if ( fabs( dist ) > PLANAR_EPSILON ) {
|
||||
f->numBoundaries = 0;
|
||||
return qfalse;
|
||||
}
|
||||
|
||||
// make boundaries
|
||||
f->numBoundaries = 4;
|
||||
|
||||
CM_GenerateBoundaryForPoints( f->boundaries[0], f->surface, a->xyz, b->xyz );
|
||||
CM_GenerateBoundaryForPoints( f->boundaries[1], f->surface, b->xyz, c->xyz );
|
||||
CM_GenerateBoundaryForPoints( f->boundaries[2], f->surface, c->xyz, d->xyz );
|
||||
CM_GenerateBoundaryForPoints( f->boundaries[3], f->surface, d->xyz, a->xyz );
|
||||
|
||||
VectorCopy( a->xyz, f->points[0] );
|
||||
VectorCopy( b->xyz, f->points[1] );
|
||||
VectorCopy( c->xyz, f->points[2] );
|
||||
VectorCopy( d->xyz, f->points[3] );
|
||||
|
||||
for (i = 1; i < 4; i++)
|
||||
{
|
||||
if ( !PlaneFromPoints( plane, f->points[i], f->points[(i+1) % 4], f->points[(i+2) % 4]) ) {
|
||||
f->numBoundaries = 0;
|
||||
return qfalse;
|
||||
}
|
||||
|
||||
if (DotProduct(f->surface, plane) < 0.9) {
|
||||
f->numBoundaries = 0;
|
||||
return qfalse;
|
||||
}
|
||||
}
|
||||
|
||||
TextureMatrixFromPoints( f, a, b, c );
|
||||
|
||||
return qtrue;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
===============
|
||||
SphereFromBounds
|
||||
===============
|
||||
*/
|
||||
void SphereFromBounds( vec3_t mins, vec3_t maxs, vec3_t origin, float *radius ) {
|
||||
vec3_t temp;
|
||||
|
||||
VectorAdd( mins, maxs, origin );
|
||||
VectorScale( origin, 0.5, origin );
|
||||
VectorSubtract( maxs, origin, temp );
|
||||
*radius = VectorLength( temp );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
====================
|
||||
FacetsForTriangleSurface
|
||||
====================
|
||||
*/
|
||||
void FacetsForTriangleSurface( dsurface_t *dsurf, shaderInfo_t *si, surfaceTest_t *test ) {
|
||||
int i;
|
||||
drawVert_t *v1, *v2, *v3, *v4;
|
||||
int count;
|
||||
int i1, i2, i3, i4, i5, i6;
|
||||
|
||||
test->patch = qfalse;
|
||||
test->numFacets = dsurf->numIndexes / 3;
|
||||
test->facets = malloc( sizeof( test->facets[0] ) * test->numFacets );
|
||||
test->shader = si;
|
||||
|
||||
count = 0;
|
||||
for ( i = 0 ; i < test->numFacets ; i++ ) {
|
||||
i1 = drawIndexes[ dsurf->firstIndex + i*3 ];
|
||||
i2 = drawIndexes[ dsurf->firstIndex + i*3 + 1 ];
|
||||
i3 = drawIndexes[ dsurf->firstIndex + i*3 + 2 ];
|
||||
|
||||
v1 = &drawVerts[ dsurf->firstVert + i1 ];
|
||||
v2 = &drawVerts[ dsurf->firstVert + i2 ];
|
||||
v3 = &drawVerts[ dsurf->firstVert + i3 ];
|
||||
|
||||
// try and make a quad out of two triangles
|
||||
if ( i != test->numFacets - 1 ) {
|
||||
i4 = drawIndexes[ dsurf->firstIndex + i*3 + 3 ];
|
||||
i5 = drawIndexes[ dsurf->firstIndex + i*3 + 4 ];
|
||||
i6 = drawIndexes[ dsurf->firstIndex + i*3 + 5 ];
|
||||
if ( i4 == i3 && i5 == i2 ) {
|
||||
v4 = &drawVerts[ dsurf->firstVert + i6 ];
|
||||
if ( CM_GenerateFacetFor4Points( &test->facets[count], v1, v2, v4, v3 ) ) {
|
||||
count++;
|
||||
i++; // skip next tri
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (CM_GenerateFacetFor3Points( &test->facets[count], v1, v2, v3 ))
|
||||
count++;
|
||||
}
|
||||
|
||||
// we may have turned some pairs into quads
|
||||
test->numFacets = count;
|
||||
}
|
||||
|
||||
/*
|
||||
====================
|
||||
FacetsForPatch
|
||||
====================
|
||||
*/
|
||||
void FacetsForPatch( dsurface_t *dsurf, shaderInfo_t *si, surfaceTest_t *test ) {
|
||||
int i, j;
|
||||
drawVert_t *v1, *v2, *v3, *v4;
|
||||
int count;
|
||||
mesh_t srcMesh, *subdivided, *mesh;
|
||||
|
||||
srcMesh.width = dsurf->patchWidth;
|
||||
srcMesh.height = dsurf->patchHeight;
|
||||
srcMesh.verts = &drawVerts[ dsurf->firstVert ];
|
||||
|
||||
//subdivided = SubdivideMesh( mesh, CURVE_FACET_ERROR, 9999 );
|
||||
mesh = SubdivideMesh( srcMesh, 8, 999 );
|
||||
PutMeshOnCurve( *mesh );
|
||||
MakeMeshNormals( *mesh );
|
||||
|
||||
subdivided = RemoveLinearMeshColumnsRows( mesh );
|
||||
FreeMesh(mesh);
|
||||
|
||||
test->patch = qtrue;
|
||||
test->numFacets = ( subdivided->width - 1 ) * ( subdivided->height - 1 ) * 2;
|
||||
test->facets = malloc( sizeof( test->facets[0] ) * test->numFacets );
|
||||
test->shader = si;
|
||||
|
||||
count = 0;
|
||||
for ( i = 0 ; i < subdivided->width - 1 ; i++ ) {
|
||||
for ( j = 0 ; j < subdivided->height - 1 ; j++ ) {
|
||||
|
||||
v1 = subdivided->verts + j * subdivided->width + i;
|
||||
v2 = v1 + 1;
|
||||
v3 = v1 + subdivided->width + 1;
|
||||
v4 = v1 + subdivided->width;
|
||||
|
||||
if ( CM_GenerateFacetFor4Points( &test->facets[count], v1, v4, v3, v2 ) ) {
|
||||
count++;
|
||||
} else {
|
||||
if (CM_GenerateFacetFor3Points( &test->facets[count], v1, v4, v3 ))
|
||||
count++;
|
||||
if (CM_GenerateFacetFor3Points( &test->facets[count], v1, v3, v2 ))
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
test->numFacets = count;
|
||||
FreeMesh(subdivided);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=====================
|
||||
InitSurfacesForTesting
|
||||
|
||||
Builds structures to speed the ray tracing against surfaces
|
||||
=====================
|
||||
*/
|
||||
void InitSurfacesForTesting( void ) {
|
||||
|
||||
int i, j;
|
||||
dsurface_t *dsurf;
|
||||
surfaceTest_t *test;
|
||||
drawVert_t *dvert;
|
||||
shaderInfo_t *si;
|
||||
|
||||
for ( i = 0 ; i < numDrawSurfaces ; i++ ) {
|
||||
dsurf = &drawSurfaces[ i ];
|
||||
if ( !dsurf->numIndexes && !dsurf->patchWidth ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// don't make surfaces for transparent objects
|
||||
// because we want light to pass through them
|
||||
si = ShaderInfoForShader( dshaders[ dsurf->shaderNum].shader );
|
||||
if ( (si->contents & CONTENTS_TRANSLUCENT) && !(si->surfaceFlags & SURF_ALPHASHADOW) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
test = malloc( sizeof( *test ) );
|
||||
surfaceTest[i] = test;
|
||||
ClearBounds( test->mins, test->maxs );
|
||||
|
||||
dvert = &drawVerts[ dsurf->firstVert ];
|
||||
for ( j = 0 ; j < dsurf->numVerts ; j++, dvert++ ) {
|
||||
AddPointToBounds( dvert->xyz, test->mins, test->maxs );
|
||||
}
|
||||
|
||||
SphereFromBounds( test->mins, test->maxs, test->origin, &test->radius );
|
||||
|
||||
if ( dsurf->surfaceType == MST_TRIANGLE_SOUP || dsurf->surfaceType == MST_PLANAR ) {
|
||||
FacetsForTriangleSurface( dsurf, si, test );
|
||||
} else if ( dsurf->surfaceType == MST_PATCH ) {
|
||||
FacetsForPatch( dsurf, si, test );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=====================
|
||||
GenerateBoundaryForPoints
|
||||
=====================
|
||||
*/
|
||||
void GenerateBoundaryForPoints( float boundary[4], float plane[4], vec3_t a, vec3_t b ) {
|
||||
vec3_t d1;
|
||||
|
||||
// amke a perpendicular vector to the edge and the surface
|
||||
VectorSubtract( b, a, d1 );
|
||||
CrossProduct( plane, d1, boundary );
|
||||
VectorNormalize( boundary, boundary );
|
||||
boundary[3] = DotProduct( a, boundary );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=================
|
||||
SetFacetFilter
|
||||
|
||||
Given a point on a facet, determine the color filter
|
||||
for light passing through
|
||||
=================
|
||||
*/
|
||||
void SetFacetFilter( traceWork_t *tr, shaderInfo_t *shader, cFacet_t *facet, vec3_t point ) {
|
||||
float s, t;
|
||||
int is, it;
|
||||
byte *image;
|
||||
int b;
|
||||
|
||||
// most surfaces are completely opaque
|
||||
if ( !(shader->surfaceFlags & SURF_ALPHASHADOW) ) {
|
||||
VectorClear( tr->trace->filter );
|
||||
return;
|
||||
}
|
||||
|
||||
s = DotProduct( point, facet->textureMatrix[0] ) + facet->textureMatrix[0][3];
|
||||
t = DotProduct( point, facet->textureMatrix[1] ) + facet->textureMatrix[1][3];
|
||||
|
||||
if ( !shader->pixels ) {
|
||||
// assume completely solid
|
||||
VectorClear( point );
|
||||
return;
|
||||
}
|
||||
|
||||
s = s - floor( s );
|
||||
t = t - floor( t );
|
||||
|
||||
is = s * shader->width;
|
||||
it = t * shader->height;
|
||||
|
||||
image = shader->pixels + 4 * ( it * shader->width + is );
|
||||
|
||||
// alpha filter
|
||||
b = image[3];
|
||||
|
||||
// alpha test makes this a binary option
|
||||
b = b < 128 ? 0 : 255;
|
||||
|
||||
tr->trace->filter[0] = tr->trace->filter[0] * (255-b) / 255;
|
||||
tr->trace->filter[1] = tr->trace->filter[1] * (255-b) / 255;
|
||||
tr->trace->filter[2] = tr->trace->filter[2] * (255-b) / 255;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
====================
|
||||
TraceAgainstFacet
|
||||
|
||||
Shader is needed for translucent surfaces
|
||||
====================
|
||||
*/
|
||||
void TraceAgainstFacet( traceWork_t *tr, shaderInfo_t *shader, cFacet_t *facet ) {
|
||||
int j;
|
||||
float d1, d2, d, f;
|
||||
vec3_t point;
|
||||
float dist;
|
||||
|
||||
// ignore degenerate facets
|
||||
if ( facet->numBoundaries < 3 ) {
|
||||
return;
|
||||
}
|
||||
|
||||
dist = facet->surface[3];
|
||||
|
||||
// compare the trace endpoints against the facet plane
|
||||
d1 = DotProduct( tr->start, facet->surface ) - dist;
|
||||
if ( d1 > -1 && d1 < 1 ) {
|
||||
return; // don't self intersect
|
||||
}
|
||||
d2 = DotProduct( tr->end, facet->surface ) - dist;
|
||||
if ( d2 > -1 && d2 < 1 ) {
|
||||
return; // don't self intersect
|
||||
}
|
||||
|
||||
// calculate the intersection fraction
|
||||
f = ( d1 - ON_EPSILON ) / ( d1 - d2 );
|
||||
if ( f <= 0 ) {
|
||||
return;
|
||||
}
|
||||
if ( f >= tr->trace->hitFraction ) {
|
||||
return; // we have hit something earlier
|
||||
}
|
||||
|
||||
// calculate the intersection point
|
||||
for ( j = 0 ; j < 3 ; j++ ) {
|
||||
point[j] = tr->start[j] + f * ( tr->end[j] - tr->start[j] );
|
||||
}
|
||||
|
||||
// check the point against the facet boundaries
|
||||
for ( j = 0 ; j < facet->numBoundaries ; j++ ) {
|
||||
// adjust the plane distance apropriately for mins/maxs
|
||||
dist = facet->boundaries[j][3];
|
||||
|
||||
d = DotProduct( point, facet->boundaries[j] );
|
||||
if ( d > dist + ON_EPSILON ) {
|
||||
break; // outside the bounds
|
||||
}
|
||||
}
|
||||
|
||||
if ( j != facet->numBoundaries ) {
|
||||
return; // we are outside the bounds of the facet
|
||||
}
|
||||
|
||||
// we hit this facet
|
||||
|
||||
// if this is a transparent surface, calculate filter value
|
||||
if ( shader->surfaceFlags & SURF_ALPHASHADOW ) {
|
||||
SetFacetFilter( tr, shader, facet, point );
|
||||
} else {
|
||||
// completely opaque
|
||||
VectorClear( tr->trace->filter );
|
||||
tr->trace->hitFraction = f;
|
||||
}
|
||||
|
||||
// VectorCopy( facet->surface, tr->trace->plane.normal );
|
||||
// tr->trace->plane.dist = facet->surface[3];
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
===============================================================
|
||||
|
||||
LINE TRACING
|
||||
|
||||
===============================================================
|
||||
*/
|
||||
|
||||
|
||||
#define TRACE_ON_EPSILON 0.1
|
||||
|
||||
typedef struct tnode_s
|
||||
{
|
||||
int type;
|
||||
vec3_t normal;
|
||||
float dist;
|
||||
int children[2];
|
||||
int planeNum;
|
||||
} tnode_t;
|
||||
|
||||
#define MAX_TNODES (MAX_MAP_NODES*4)
|
||||
tnode_t *tnodes, *tnode_p;
|
||||
|
||||
/*
|
||||
==============
|
||||
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;
|
||||
int leafNum;
|
||||
|
||||
t = tnode_p++;
|
||||
|
||||
node = dnodes + nodenum;
|
||||
plane = dplanes + node->planeNum;
|
||||
|
||||
t->planeNum = node->planeNum;
|
||||
t->type = PlaneTypeForNormal( plane->normal );
|
||||
VectorCopy (plane->normal, t->normal);
|
||||
t->dist = plane->dist;
|
||||
|
||||
for (i=0 ; i<2 ; i++)
|
||||
{
|
||||
if (node->children[i] < 0) {
|
||||
leafNum = -node->children[i] - 1;
|
||||
if ( dleafs[leafNum].cluster == -1 ) {
|
||||
// solid
|
||||
t->children[i] = leafNum | ( 1 << 31 ) | ( 1 << 30 );
|
||||
} else {
|
||||
t->children[i] = leafNum | ( 1 << 31 );
|
||||
}
|
||||
} else {
|
||||
t->children[i] = tnode_p - tnodes;
|
||||
MakeTnode (node->children[i]);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
InitTrace
|
||||
|
||||
Loads the node structure out of a .bsp file to be used for light occlusion
|
||||
=============
|
||||
*/
|
||||
void InitTrace( void ) {
|
||||
// 32 byte align the structs
|
||||
tnodes = malloc( (MAX_TNODES+1) * sizeof(tnode_t));
|
||||
tnodes = (tnode_t *)(((int)tnodes + 31)&~31);
|
||||
tnode_p = tnodes;
|
||||
|
||||
MakeTnode (0);
|
||||
|
||||
InitSurfacesForTesting();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
===================
|
||||
PointInSolid
|
||||
===================
|
||||
*/
|
||||
qboolean PointInSolid_r( vec3_t start, int node ) {
|
||||
tnode_t *tnode;
|
||||
float front;
|
||||
|
||||
while ( !(node & (1<<31) ) ) {
|
||||
tnode = &tnodes[node];
|
||||
switch (tnode->type) {
|
||||
case PLANE_X:
|
||||
front = start[0] - tnode->dist;
|
||||
break;
|
||||
case PLANE_Y:
|
||||
front = start[1] - tnode->dist;
|
||||
break;
|
||||
case PLANE_Z:
|
||||
front = start[2] - tnode->dist;
|
||||
break;
|
||||
default:
|
||||
front = (start[0]*tnode->normal[0] + start[1]*tnode->normal[1] + start[2]*tnode->normal[2]) - tnode->dist;
|
||||
break;
|
||||
}
|
||||
|
||||
if ( front == 0 ) {
|
||||
// exactly on node, must check both sides
|
||||
return (qboolean) ( PointInSolid_r( start, tnode->children[0] )
|
||||
| PointInSolid_r( start, tnode->children[1] ) );
|
||||
}
|
||||
|
||||
if ( front > 0 ) {
|
||||
node = tnode->children[0];
|
||||
} else {
|
||||
node = tnode->children[1];
|
||||
}
|
||||
}
|
||||
|
||||
if ( node & ( 1 << 30 ) ) {
|
||||
return qtrue;
|
||||
}
|
||||
return qfalse;
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
PointInSolid
|
||||
|
||||
=============
|
||||
*/
|
||||
qboolean PointInSolid( vec3_t start ) {
|
||||
return PointInSolid_r( start, 0 );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=============
|
||||
TraceLine_r
|
||||
|
||||
Returns qtrue if something is hit and tracing can stop
|
||||
=============
|
||||
*/
|
||||
int TraceLine_r( int node, const vec3_t start, const vec3_t stop, traceWork_t *tw ) {
|
||||
tnode_t *tnode;
|
||||
float front, back;
|
||||
vec3_t mid;
|
||||
float frac;
|
||||
int side;
|
||||
int r;
|
||||
|
||||
if (node & (1<<31)) {
|
||||
if (node & ( 1 << 30 ) ) {
|
||||
VectorCopy (start, tw->trace->hit);
|
||||
tw->trace->passSolid = qtrue;
|
||||
return qtrue;
|
||||
} else {
|
||||
// save the node off for more exact testing
|
||||
if ( tw->numOpenLeafs == MAX_MAP_LEAFS ) {
|
||||
return qfalse;
|
||||
}
|
||||
tw->openLeafNumbers[ tw->numOpenLeafs ] = node & ~(3 << 30);
|
||||
tw->numOpenLeafs++;
|
||||
return qfalse;
|
||||
}
|
||||
}
|
||||
|
||||
tnode = &tnodes[node];
|
||||
switch (tnode->type) {
|
||||
case PLANE_X:
|
||||
front = start[0] - tnode->dist;
|
||||
back = stop[0] - tnode->dist;
|
||||
break;
|
||||
case PLANE_Y:
|
||||
front = start[1] - tnode->dist;
|
||||
back = stop[1] - tnode->dist;
|
||||
break;
|
||||
case PLANE_Z:
|
||||
front = start[2] - tnode->dist;
|
||||
back = stop[2] - tnode->dist;
|
||||
break;
|
||||
default:
|
||||
front = (start[0]*tnode->normal[0] + start[1]*tnode->normal[1] + start[2]*tnode->normal[2]) - tnode->dist;
|
||||
back = (stop[0]*tnode->normal[0] + stop[1]*tnode->normal[1] + stop[2]*tnode->normal[2]) - tnode->dist;
|
||||
break;
|
||||
}
|
||||
|
||||
if (front >= -TRACE_ON_EPSILON && back >= -TRACE_ON_EPSILON) {
|
||||
return TraceLine_r (tnode->children[0], start, stop, tw);
|
||||
}
|
||||
|
||||
if (front < TRACE_ON_EPSILON && back < TRACE_ON_EPSILON) {
|
||||
return TraceLine_r (tnode->children[1], start, stop, tw);
|
||||
}
|
||||
|
||||
side = front < 0;
|
||||
|
||||
frac = front / (front-back);
|
||||
|
||||
mid[0] = start[0] + (stop[0] - start[0])*frac;
|
||||
mid[1] = start[1] + (stop[1] - start[1])*frac;
|
||||
mid[2] = start[2] + (stop[2] - start[2])*frac;
|
||||
|
||||
r = TraceLine_r (tnode->children[side], start, mid, tw);
|
||||
|
||||
if (r) {
|
||||
return r;
|
||||
}
|
||||
|
||||
// trace->planeNum = tnode->planeNum;
|
||||
return TraceLine_r (tnode->children[!side], mid, stop, tw);
|
||||
}
|
||||
|
||||
//==========================================================================================
|
||||
|
||||
|
||||
/*
|
||||
================
|
||||
SphereCull
|
||||
================
|
||||
*/
|
||||
qboolean SphereCull( vec3_t start, vec3_t stop, vec3_t origin, float radius ) {
|
||||
vec3_t v;
|
||||
float d;
|
||||
vec3_t dir;
|
||||
float len;
|
||||
vec3_t on;
|
||||
|
||||
VectorSubtract( stop, start, dir );
|
||||
len = VectorNormalize( dir, dir );
|
||||
|
||||
VectorSubtract( origin, start, v );
|
||||
d = DotProduct( v, dir );
|
||||
if ( d > len + radius ) {
|
||||
return qtrue; // too far ahead
|
||||
}
|
||||
if ( d < -radius ) {
|
||||
return qtrue; // too far behind
|
||||
}
|
||||
VectorMA( start, d, dir, on );
|
||||
|
||||
VectorSubtract( on, origin, v );
|
||||
|
||||
len = VectorLength( v );
|
||||
|
||||
if ( len > radius ) {
|
||||
return qtrue; // too far to the side
|
||||
}
|
||||
|
||||
return qfalse; // must be traced against
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
TraceAgainstSurface
|
||||
================
|
||||
*/
|
||||
void TraceAgainstSurface( traceWork_t *tw, surfaceTest_t *surf ) {
|
||||
int i;
|
||||
|
||||
// if surfaces are trans
|
||||
if ( SphereCull( tw->start, tw->end, surf->origin, surf->radius ) ) {
|
||||
if ( numthreads == 1 ) {
|
||||
c_cullTrace++;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if ( numthreads == 1 ) {
|
||||
c_testTrace++;
|
||||
c_testFacets += surf->numFacets;
|
||||
}
|
||||
|
||||
/*
|
||||
// MrE: backface culling
|
||||
if (!surf->patch && surf->numFacets) {
|
||||
// if the surface does not cast an alpha shadow
|
||||
if ( !(surf->shader->surfaceFlags & SURF_ALPHASHADOW) ) {
|
||||
vec3_t vec;
|
||||
VectorSubtract(tw->end, tw->start, vec);
|
||||
if (DotProduct(vec, surf->facets->surface) > 0)
|
||||
return;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
// test against each facet
|
||||
for ( i = 0 ; i < surf->numFacets ; i++ ) {
|
||||
TraceAgainstFacet( tw, surf->shader, surf->facets + i );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
TraceLine
|
||||
|
||||
Follow the trace just through the solid leafs first, and only
|
||||
if it passes that, trace against the objects inside the empty leafs
|
||||
Returns qtrue if the trace hit any
|
||||
|
||||
traceWork_t is only a parameter to crutch up poor large local allocations on
|
||||
winNT and macOS. It should be allocated in the worker function, but never
|
||||
looked at.
|
||||
|
||||
leave testAll false if all you care about is if it hit anything at all.
|
||||
if you need to know the exact first point of impact (for a sun trace), set
|
||||
testAll to true
|
||||
=============
|
||||
*/
|
||||
extern qboolean patchshadows;
|
||||
|
||||
void TraceLine( const vec3_t start, const vec3_t stop, trace_t *trace, qboolean testAll, traceWork_t *tw ) {
|
||||
int r;
|
||||
int i, j;
|
||||
dleaf_t *leaf;
|
||||
float oldHitFrac;
|
||||
surfaceTest_t *test;
|
||||
int surfaceNum;
|
||||
byte surfaceTested[MAX_MAP_DRAW_SURFS/8];
|
||||
;
|
||||
|
||||
if ( numthreads == 1 ) {
|
||||
c_totalTrace++;
|
||||
}
|
||||
|
||||
// assume all light gets through, unless the ray crosses
|
||||
// a translucent surface
|
||||
trace->filter[0] = 1.0;
|
||||
trace->filter[1] = 1.0;
|
||||
trace->filter[2] = 1.0;
|
||||
|
||||
VectorCopy( start, tw->start );
|
||||
VectorCopy( stop, tw->end );
|
||||
tw->trace = trace;
|
||||
|
||||
tw->numOpenLeafs = 0;
|
||||
|
||||
trace->passSolid = qfalse;
|
||||
trace->hitFraction = 1.0;
|
||||
|
||||
r = TraceLine_r( 0, start, stop, tw );
|
||||
|
||||
// if we hit a solid leaf, stop without testing the leaf
|
||||
// surfaces. Note that the plane and endpoint might not
|
||||
// be the first solid intersection along the ray.
|
||||
if ( r && !testAll ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( noSurfaces ) {
|
||||
return;
|
||||
}
|
||||
|
||||
memset( surfaceTested, 0, (numDrawSurfaces+7)/8 );
|
||||
oldHitFrac = trace->hitFraction;
|
||||
|
||||
for ( i = 0 ; i < tw->numOpenLeafs ; i++ ) {
|
||||
leaf = &dleafs[ tw->openLeafNumbers[ i ] ];
|
||||
for ( j = 0 ; j < leaf->numLeafSurfaces ; j++ ) {
|
||||
surfaceNum = dleafsurfaces[ leaf->firstLeafSurface + j ];
|
||||
|
||||
// make sure we don't test the same ray against a surface more than once
|
||||
if ( surfaceTested[ surfaceNum>>3 ] & ( 1 << ( surfaceNum & 7) ) ) {
|
||||
continue;
|
||||
}
|
||||
surfaceTested[ surfaceNum>>3 ] |= ( 1 << ( surfaceNum & 7 ) );
|
||||
|
||||
test = surfaceTest[ surfaceNum ];
|
||||
if ( !test ) {
|
||||
continue;
|
||||
}
|
||||
//
|
||||
if ( !tw->patchshadows && test->patch ) {
|
||||
continue;
|
||||
}
|
||||
TraceAgainstSurface( tw, test );
|
||||
}
|
||||
|
||||
// if the trace is now solid, we can't possibly hit anything closer
|
||||
if ( trace->hitFraction < oldHitFrac ) {
|
||||
trace->passSolid = qtrue;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for ( i = 0 ; i < 3 ; i++ ) {
|
||||
trace->hit[i] = start[i] + ( stop[i] - start[i] ) * trace->hitFraction;
|
||||
}
|
||||
}
|
||||
|
395
q3map/lightmaps.c
Normal file
395
q3map/lightmaps.c
Normal file
|
@ -0,0 +1,395 @@
|
|||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 1999-2005 Id Software, Inc.
|
||||
|
||||
This file is part of Quake III Arena source code.
|
||||
|
||||
Quake III Arena source code 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.
|
||||
|
||||
Quake III Arena source code 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 Foobar; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
#include "qbsp.h"
|
||||
|
||||
|
||||
/*
|
||||
|
||||
Lightmap allocation has to be done after all flood filling and
|
||||
visible surface determination.
|
||||
|
||||
*/
|
||||
|
||||
int numSortShaders;
|
||||
mapDrawSurface_t *surfsOnShader[MAX_MAP_SHADERS];
|
||||
|
||||
|
||||
int allocated[LIGHTMAP_WIDTH];
|
||||
|
||||
int numLightmaps = 1;
|
||||
int c_exactLightmap;
|
||||
|
||||
|
||||
void PrepareNewLightmap( void ) {
|
||||
memset( allocated, 0, sizeof( allocated ) );
|
||||
numLightmaps++;
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
AllocLMBlock
|
||||
|
||||
returns a texture number and the position inside it
|
||||
===============
|
||||
*/
|
||||
qboolean AllocLMBlock (int w, int h, int *x, int *y)
|
||||
{
|
||||
int i, j;
|
||||
int best, best2;
|
||||
|
||||
best = LIGHTMAP_HEIGHT;
|
||||
|
||||
for ( i=0 ; i <= LIGHTMAP_WIDTH-w ; i++ ) {
|
||||
best2 = 0;
|
||||
|
||||
for (j=0 ; j<w ; j++) {
|
||||
if (allocated[i+j] >= best) {
|
||||
break;
|
||||
}
|
||||
if (allocated[i+j] > best2) {
|
||||
best2 = allocated[i+j];
|
||||
}
|
||||
}
|
||||
if (j == w) { // this is a valid spot
|
||||
*x = i;
|
||||
*y = best = best2;
|
||||
}
|
||||
}
|
||||
|
||||
if (best + h > LIGHTMAP_HEIGHT) {
|
||||
return qfalse;
|
||||
}
|
||||
|
||||
for (i=0 ; i<w ; i++) {
|
||||
allocated[*x + i] = best + h;
|
||||
}
|
||||
|
||||
return qtrue;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
===================
|
||||
AllocateLightmapForPatch
|
||||
===================
|
||||
*/
|
||||
//#define LIGHTMAP_PATCHSHIFT
|
||||
|
||||
void AllocateLightmapForPatch( mapDrawSurface_t *ds ) {
|
||||
int i, j, k;
|
||||
drawVert_t *verts;
|
||||
int w, h;
|
||||
int x, y;
|
||||
float s, t;
|
||||
mesh_t mesh, *subdividedMesh, *tempMesh, *newmesh;
|
||||
int widthtable[LIGHTMAP_WIDTH], heighttable[LIGHTMAP_HEIGHT], ssize;
|
||||
|
||||
verts = ds->verts;
|
||||
|
||||
mesh.width = ds->patchWidth;
|
||||
mesh.height = ds->patchHeight;
|
||||
mesh.verts = verts;
|
||||
newmesh = SubdivideMesh( mesh, 8, 999 );
|
||||
|
||||
PutMeshOnCurve( *newmesh );
|
||||
tempMesh = RemoveLinearMeshColumnsRows( newmesh );
|
||||
FreeMesh(newmesh);
|
||||
|
||||
ssize = samplesize;
|
||||
if (ds->shaderInfo->lightmapSampleSize)
|
||||
ssize = ds->shaderInfo->lightmapSampleSize;
|
||||
|
||||
#ifdef LIGHTMAP_PATCHSHIFT
|
||||
subdividedMesh = SubdivideMeshQuads( tempMesh, ssize, LIGHTMAP_WIDTH-1, widthtable, heighttable);
|
||||
#else
|
||||
subdividedMesh = SubdivideMeshQuads( tempMesh, ssize, LIGHTMAP_WIDTH, widthtable, heighttable);
|
||||
#endif
|
||||
|
||||
w = subdividedMesh->width;
|
||||
h = subdividedMesh->height;
|
||||
|
||||
#ifdef LIGHTMAP_PATCHSHIFT
|
||||
w++;
|
||||
h++;
|
||||
#endif
|
||||
|
||||
FreeMesh(subdividedMesh);
|
||||
|
||||
// allocate the lightmap
|
||||
c_exactLightmap += w * h;
|
||||
|
||||
if ( !AllocLMBlock( w, h, &x, &y ) ) {
|
||||
PrepareNewLightmap();
|
||||
if ( !AllocLMBlock( w, h, &x, &y ) ) {
|
||||
Error("Entity %i, brush %i: Lightmap allocation failed",
|
||||
ds->mapBrush->entitynum, ds->mapBrush->brushnum );
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef LIGHTMAP_PATCHSHIFT
|
||||
w--;
|
||||
h--;
|
||||
#endif
|
||||
|
||||
// set the lightmap texture coordinates in the drawVerts
|
||||
ds->lightmapNum = numLightmaps - 1;
|
||||
ds->lightmapWidth = w;
|
||||
ds->lightmapHeight = h;
|
||||
ds->lightmapX = x;
|
||||
ds->lightmapY = y;
|
||||
|
||||
for ( i = 0 ; i < ds->patchWidth ; i++ ) {
|
||||
for ( k = 0 ; k < w ; k++ ) {
|
||||
if ( originalWidths[k] >= i ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (k >= w)
|
||||
k = w-1;
|
||||
s = x + k;
|
||||
for ( j = 0 ; j < ds->patchHeight ; j++ ) {
|
||||
for ( k = 0 ; k < h ; k++ ) {
|
||||
if ( originalHeights[k] >= j ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (k >= h)
|
||||
k = h-1;
|
||||
t = y + k;
|
||||
verts[i + j * ds->patchWidth].lightmap[0] = ( s + 0.5 ) / LIGHTMAP_WIDTH;
|
||||
verts[i + j * ds->patchWidth].lightmap[1] = ( t + 0.5 ) / LIGHTMAP_HEIGHT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
===================
|
||||
AllocateLightmapForSurface
|
||||
===================
|
||||
*/
|
||||
//#define LIGHTMAP_BLOCK 16
|
||||
void AllocateLightmapForSurface( mapDrawSurface_t *ds ) {
|
||||
vec3_t mins, maxs, size, exactSize, delta;
|
||||
int i;
|
||||
drawVert_t *verts;
|
||||
int w, h;
|
||||
int x, y, ssize;
|
||||
int axis;
|
||||
vec3_t vecs[2];
|
||||
float s, t;
|
||||
vec3_t origin;
|
||||
plane_t *plane;
|
||||
float d;
|
||||
vec3_t planeNormal;
|
||||
|
||||
if ( ds->patch ) {
|
||||
AllocateLightmapForPatch( ds );
|
||||
return;
|
||||
}
|
||||
|
||||
ssize = samplesize;
|
||||
if (ds->shaderInfo->lightmapSampleSize)
|
||||
ssize = ds->shaderInfo->lightmapSampleSize;
|
||||
|
||||
plane = &mapplanes[ ds->side->planenum ];
|
||||
|
||||
// bound the surface
|
||||
ClearBounds( mins, maxs );
|
||||
verts = ds->verts;
|
||||
for ( i = 0 ; i < ds->numVerts ; i++ ) {
|
||||
AddPointToBounds( verts[i].xyz, mins, maxs );
|
||||
}
|
||||
|
||||
// round to the lightmap resolution
|
||||
for ( i = 0 ; i < 3 ; i++ ) {
|
||||
exactSize[i] = maxs[i] - mins[i];
|
||||
mins[i] = ssize * floor( mins[i] / ssize );
|
||||
maxs[i] = ssize * ceil( maxs[i] / ssize );
|
||||
size[i] = (maxs[i] - mins[i]) / ssize + 1;
|
||||
}
|
||||
|
||||
// the two largest axis will be the lightmap size
|
||||
memset( vecs, 0, sizeof( vecs ) );
|
||||
|
||||
planeNormal[0] = fabs( plane->normal[0] );
|
||||
planeNormal[1] = fabs( plane->normal[1] );
|
||||
planeNormal[2] = fabs( plane->normal[2] );
|
||||
|
||||
if ( planeNormal[0] >= planeNormal[1] && planeNormal[0] >= planeNormal[2] ) {
|
||||
w = size[1];
|
||||
h = size[2];
|
||||
axis = 0;
|
||||
vecs[0][1] = 1.0 / ssize;
|
||||
vecs[1][2] = 1.0 / ssize;
|
||||
} else if ( planeNormal[1] >= planeNormal[0] && planeNormal[1] >= planeNormal[2] ) {
|
||||
w = size[0];
|
||||
h = size[2];
|
||||
axis = 1;
|
||||
vecs[0][0] = 1.0 / ssize;
|
||||
vecs[1][2] = 1.0 / ssize;
|
||||
} else {
|
||||
w = size[0];
|
||||
h = size[1];
|
||||
axis = 2;
|
||||
vecs[0][0] = 1.0 / ssize;
|
||||
vecs[1][1] = 1.0 / ssize;
|
||||
}
|
||||
|
||||
if ( !plane->normal[axis] ) {
|
||||
Error( "Chose a 0 valued axis" );
|
||||
}
|
||||
|
||||
if ( w > LIGHTMAP_WIDTH ) {
|
||||
VectorScale ( vecs[0], (float)LIGHTMAP_WIDTH/w, vecs[0] );
|
||||
w = LIGHTMAP_WIDTH;
|
||||
}
|
||||
|
||||
if ( h > LIGHTMAP_HEIGHT ) {
|
||||
VectorScale ( vecs[1], (float)LIGHTMAP_HEIGHT/h, vecs[1] );
|
||||
h = LIGHTMAP_HEIGHT;
|
||||
}
|
||||
|
||||
c_exactLightmap += w * h;
|
||||
|
||||
if ( !AllocLMBlock( w, h, &x, &y ) ) {
|
||||
PrepareNewLightmap();
|
||||
if ( !AllocLMBlock( w, h, &x, &y ) ) {
|
||||
Error("Entity %i, brush %i: Lightmap allocation failed",
|
||||
ds->mapBrush->entitynum, ds->mapBrush->brushnum );
|
||||
}
|
||||
}
|
||||
|
||||
// set the lightmap texture coordinates in the drawVerts
|
||||
ds->lightmapNum = numLightmaps - 1;
|
||||
ds->lightmapWidth = w;
|
||||
ds->lightmapHeight = h;
|
||||
ds->lightmapX = x;
|
||||
ds->lightmapY = y;
|
||||
|
||||
for ( i = 0 ; i < ds->numVerts ; i++ ) {
|
||||
VectorSubtract( verts[i].xyz, mins, delta );
|
||||
s = DotProduct( delta, vecs[0] ) + x + 0.5;
|
||||
t = DotProduct( delta, vecs[1] ) + y + 0.5;
|
||||
verts[i].lightmap[0] = s / LIGHTMAP_WIDTH;
|
||||
verts[i].lightmap[1] = t / LIGHTMAP_HEIGHT;
|
||||
}
|
||||
|
||||
// calculate the world coordinates of the lightmap samples
|
||||
|
||||
// project mins onto plane to get origin
|
||||
d = DotProduct( mins, plane->normal ) - plane->dist;
|
||||
d /= plane->normal[ axis ];
|
||||
VectorCopy( mins, origin );
|
||||
origin[axis] -= d;
|
||||
|
||||
// project stepped lightmap blocks and subtract to get planevecs
|
||||
for ( i = 0 ; i < 2 ; i++ ) {
|
||||
vec3_t normalized;
|
||||
float len;
|
||||
|
||||
len = VectorNormalize( vecs[i], normalized );
|
||||
VectorScale( normalized, (1.0/len), vecs[i] );
|
||||
d = DotProduct( vecs[i], plane->normal );
|
||||
d /= plane->normal[ axis ];
|
||||
vecs[i][axis] -= d;
|
||||
}
|
||||
|
||||
VectorCopy( origin, ds->lightmapOrigin );
|
||||
VectorCopy( vecs[0], ds->lightmapVecs[0] );
|
||||
VectorCopy( vecs[1], ds->lightmapVecs[1] );
|
||||
VectorCopy( plane->normal, ds->lightmapVecs[2] );
|
||||
}
|
||||
|
||||
/*
|
||||
===================
|
||||
AllocateLightmaps
|
||||
===================
|
||||
*/
|
||||
void AllocateLightmaps( entity_t *e ) {
|
||||
int i, j;
|
||||
mapDrawSurface_t *ds;
|
||||
shaderInfo_t *si;
|
||||
|
||||
qprintf ("--- AllocateLightmaps ---\n");
|
||||
|
||||
|
||||
// sort all surfaces by shader so common shaders will usually
|
||||
// be in the same lightmap
|
||||
numSortShaders = 0;
|
||||
|
||||
for ( i = e->firstDrawSurf ; i < numMapDrawSurfs ; i++ ) {
|
||||
ds = &mapDrawSurfs[i];
|
||||
if ( !ds->numVerts ) {
|
||||
continue; // leftover from a surface subdivision
|
||||
}
|
||||
if ( ds->miscModel ) {
|
||||
continue;
|
||||
}
|
||||
if ( !ds->patch ) {
|
||||
VectorCopy( mapplanes[ds->side->planenum].normal, ds->lightmapVecs[2] );
|
||||
}
|
||||
|
||||
// search for this shader
|
||||
for ( j = 0 ; j < numSortShaders ; j++ ) {
|
||||
if ( ds->shaderInfo == surfsOnShader[j]->shaderInfo ) {
|
||||
ds->nextOnShader = surfsOnShader[j];
|
||||
surfsOnShader[j] = ds;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( j == numSortShaders ) {
|
||||
if ( numSortShaders >= MAX_MAP_SHADERS ) {
|
||||
Error( "MAX_MAP_SHADERS" );
|
||||
}
|
||||
surfsOnShader[j] = ds;
|
||||
numSortShaders++;
|
||||
}
|
||||
}
|
||||
qprintf( "%5i unique shaders\n", numSortShaders );
|
||||
|
||||
// for each shader, allocate lightmaps for each surface
|
||||
|
||||
// numLightmaps = 0;
|
||||
// PrepareNewLightmap();
|
||||
|
||||
for ( i = 0 ; i < numSortShaders ; i++ ) {
|
||||
si = surfsOnShader[i]->shaderInfo;
|
||||
|
||||
for ( ds = surfsOnShader[i] ; ds ; ds = ds->nextOnShader ) {
|
||||
// some surfaces don't need lightmaps allocated for them
|
||||
if ( si->surfaceFlags & SURF_NOLIGHTMAP ) {
|
||||
ds->lightmapNum = -1;
|
||||
} else if ( si->surfaceFlags & SURF_POINTLIGHT ) {
|
||||
ds->lightmapNum = -3;
|
||||
} else {
|
||||
AllocateLightmapForSurface( ds );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
qprintf( "%7i exact lightmap texels\n", c_exactLightmap );
|
||||
qprintf( "%7i block lightmap texels\n", numLightmaps * LIGHTMAP_WIDTH*LIGHTMAP_HEIGHT );
|
||||
}
|
||||
|
||||
|
||||
|
5748
q3map/lightv.c
Normal file
5748
q3map/lightv.c
Normal file
File diff suppressed because it is too large
Load diff
148
q3map/makefile
Normal file
148
q3map/makefile
Normal file
|
@ -0,0 +1,148 @@
|
|||
|
||||
CFLAGS = -c
|
||||
LDFLAGS =
|
||||
ODIR = /q3/q3map
|
||||
|
||||
EXEBASE = q3map
|
||||
EXE = $(ODIR)/$(EXEBASE)
|
||||
all: $(EXE)
|
||||
|
||||
_irix:
|
||||
make "CFLAGS = -c -Ofast=ip27 -OPT:IEEE_arithmetic=3 -I../common -Xcpluscomm " "LDFLAGS = -Ofast=ip27 -OPT:IEEE_arithmetic=3 -g"
|
||||
|
||||
_irixdebug:
|
||||
make "CFLAGS = -c -O2 -g -I../common -Xcpluscomm" "LDFLAGS = -g"
|
||||
|
||||
_irixinst:
|
||||
make "_irix"
|
||||
make "install"
|
||||
|
||||
clean:
|
||||
rm -f $(ODIR)/*.o $(EXE)
|
||||
|
||||
install:
|
||||
cp $(EXE) /quake3_bin
|
||||
chmod 0777 /quake3_bin/$(EXEBASE)
|
||||
|
||||
installtest:
|
||||
cp $(EXE) /quake3_bin/$(EXEBASE)_test
|
||||
chmod 0777 /quake3_bin/$(EXEBASE)_test
|
||||
|
||||
|
||||
FILES = $(ODIR)/fog.o $(ODIR)/brush.o $(ODIR)/tjunction.o $(ODIR)/vis.o $(ODIR)/visflow.o \
|
||||
$(ODIR)/light.o $(ODIR)/lightmaps.o $(ODIR)/bspfile.o \
|
||||
$(ODIR)/cmdlib.o $(ODIR)/patch.o $(ODIR)/mesh.o $(ODIR)/nodraw.o $(ODIR)/glfile.o \
|
||||
$(ODIR)/leakfile.o $(ODIR)/map.o $(ODIR)/mathlib.o $(ODIR)/polylib.o $(ODIR)/aselib.o \
|
||||
$(ODIR)/imagelib.o $(ODIR)/portals.o $(ODIR)/prtfile.o $(ODIR)/bsp.o $(ODIR)/surface.o \
|
||||
$(ODIR)/scriplib.o $(ODIR)/shaders.o $(ODIR)/threads.o $(ODIR)/tree.o \
|
||||
$(ODIR)/writebsp.o $(ODIR)/facebsp.o $(ODIR)/misc_model.o $(ODIR)/light_trace.o
|
||||
|
||||
$(EXE) : $(FILES)
|
||||
cc -o $(EXE) $(LDFLAGS) $(FILES) -lm
|
||||
|
||||
$(ODIR)/surface.o : surface.c
|
||||
cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
|
||||
cc $(CFLAGS) -o $@ /tmp/temp.i
|
||||
$(ODIR)/fog.o : fog.c
|
||||
cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
|
||||
cc $(CFLAGS) -o $@ /tmp/temp.i
|
||||
$(ODIR)/brush.o : brush.c
|
||||
cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
|
||||
cc $(CFLAGS) -o $@ /tmp/temp.i
|
||||
$(ODIR)/tjunction.o : tjunction.c
|
||||
cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
|
||||
cc $(CFLAGS) -o $@ /tmp/temp.i
|
||||
$(ODIR)/lightmaps.o : lightmaps.c
|
||||
cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
|
||||
cc $(CFLAGS) -o $@ /tmp/temp.i
|
||||
$(ODIR)/brushbsp.o : brushbsp.c
|
||||
cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
|
||||
cc $(CFLAGS) -o $@ /tmp/temp.i
|
||||
$(ODIR)/facebsp.o : facebsp.c
|
||||
cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
|
||||
cc $(CFLAGS) -o $@ /tmp/temp.i
|
||||
$(ODIR)/patch.o : patch.c
|
||||
cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
|
||||
cc $(CFLAGS) -o $@ /tmp/temp.i
|
||||
$(ODIR)/mesh.o : mesh.c
|
||||
cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
|
||||
cc $(CFLAGS) -o $@ /tmp/temp.i
|
||||
$(ODIR)/misc_model.o : misc_model.c
|
||||
cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
|
||||
cc $(CFLAGS) -o $@ /tmp/temp.i
|
||||
$(ODIR)/nodraw.o : nodraw.c
|
||||
cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
|
||||
cc $(CFLAGS) -o $@ /tmp/temp.i
|
||||
$(ODIR)/glfile.o : glfile.c
|
||||
cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
|
||||
cc $(CFLAGS) -o $@ /tmp/temp.i
|
||||
$(ODIR)/leakfile.o : leakfile.c
|
||||
cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
|
||||
cc $(CFLAGS) -o $@ /tmp/temp.i
|
||||
$(ODIR)/map.o : map.c
|
||||
cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
|
||||
cc $(CFLAGS) -o $@ /tmp/temp.i
|
||||
$(ODIR)/portals.o : portals.c
|
||||
cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
|
||||
cc $(CFLAGS) -o $@ /tmp/temp.i
|
||||
$(ODIR)/prtfile.o : prtfile.c
|
||||
cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
|
||||
cc $(CFLAGS) -o $@ /tmp/temp.i
|
||||
$(ODIR)/bsp.o : bsp.c
|
||||
cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
|
||||
cc $(CFLAGS) -o $@ /tmp/temp.i
|
||||
$(ODIR)/tree.o : tree.c
|
||||
cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
|
||||
cc $(CFLAGS) -o $@ /tmp/temp.i
|
||||
$(ODIR)/shaders.o : shaders.c
|
||||
cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
|
||||
cc $(CFLAGS) -o $@ /tmp/temp.i
|
||||
$(ODIR)/writebsp.o : writebsp.c
|
||||
cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
|
||||
cc $(CFLAGS) -o $@ /tmp/temp.i
|
||||
$(ODIR)/csg.o : csg.c
|
||||
cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
|
||||
cc $(CFLAGS) -o $@ /tmp/temp.i
|
||||
|
||||
|
||||
$(ODIR)/vis.o : vis.c
|
||||
cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
|
||||
cc $(CFLAGS) -o $@ /tmp/temp.i
|
||||
$(ODIR)/visflow.o : visflow.c
|
||||
cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
|
||||
cc $(CFLAGS) -o $@ /tmp/temp.i
|
||||
|
||||
|
||||
$(ODIR)/light.o : light.c
|
||||
cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
|
||||
cc $(CFLAGS) -o $@ /tmp/temp.i
|
||||
$(ODIR)/light_trace.o : light_trace.c
|
||||
cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
|
||||
cc $(CFLAGS) -o $@ /tmp/temp.i
|
||||
|
||||
$(ODIR)/cmdlib.o : ../common/cmdlib.c
|
||||
cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
|
||||
cc $(CFLAGS) -o $@ /tmp/temp.i
|
||||
$(ODIR)/mathlib.o : ../common/mathlib.c
|
||||
cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
|
||||
cc $(CFLAGS) -o $@ /tmp/temp.i
|
||||
$(ODIR)/polylib.o : ../common/polylib.c
|
||||
cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
|
||||
cc $(CFLAGS) -o $@ /tmp/temp.i
|
||||
$(ODIR)/aselib.o : ../common/aselib.c
|
||||
cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
|
||||
cc $(CFLAGS) -o $@ /tmp/temp.i
|
||||
$(ODIR)/imagelib.o : ../common/imagelib.c
|
||||
cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
|
||||
cc $(CFLAGS) -o $@ /tmp/temp.i
|
||||
$(ODIR)/scriplib.o : ../common/scriplib.c
|
||||
cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
|
||||
cc $(CFLAGS) -o $@ /tmp/temp.i
|
||||
$(ODIR)/threads.o : ../common/threads.c
|
||||
cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
|
||||
cc $(CFLAGS) -o $@ /tmp/temp.i
|
||||
$(ODIR)/bspfile.o : ../common/bspfile.c
|
||||
cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
|
||||
cc $(CFLAGS) -o $@ /tmp/temp.i
|
||||
|
||||
|
1251
q3map/map.c
Normal file
1251
q3map/map.c
Normal file
File diff suppressed because it is too large
Load diff
682
q3map/mesh.c
Normal file
682
q3map/mesh.c
Normal file
|
@ -0,0 +1,682 @@
|
|||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 1999-2005 Id Software, Inc.
|
||||
|
||||
This file is part of Quake III Arena source code.
|
||||
|
||||
Quake III Arena source code 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.
|
||||
|
||||
Quake III Arena source code 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 Foobar; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
#include "qbsp.h"
|
||||
|
||||
|
||||
/*
|
||||
===============================================================
|
||||
|
||||
MESH SUBDIVISION
|
||||
|
||||
===============================================================
|
||||
*/
|
||||
|
||||
|
||||
int originalWidths[MAX_EXPANDED_AXIS];
|
||||
int originalHeights[MAX_EXPANDED_AXIS];
|
||||
|
||||
int neighbors[8][2] = {
|
||||
{0,1}, {1,1}, {1,0}, {1,-1}, {0,-1}, {-1,-1}, {-1,0}, {-1,1}
|
||||
};
|
||||
|
||||
/*
|
||||
============
|
||||
LerpDrawVert
|
||||
============
|
||||
*/
|
||||
void LerpDrawVert( drawVert_t *a, drawVert_t *b, drawVert_t *out ) {
|
||||
out->xyz[0] = 0.5 * (a->xyz[0] + b->xyz[0]);
|
||||
out->xyz[1] = 0.5 * (a->xyz[1] + b->xyz[1]);
|
||||
out->xyz[2] = 0.5 * (a->xyz[2] + b->xyz[2]);
|
||||
|
||||
out->st[0] = 0.5 * (a->st[0] + b->st[0]);
|
||||
out->st[1] = 0.5 * (a->st[1] + b->st[1]);
|
||||
|
||||
out->lightmap[0] = 0.5 * (a->lightmap[0] + b->lightmap[0]);
|
||||
out->lightmap[1] = 0.5 * (a->lightmap[1] + b->lightmap[1]);
|
||||
|
||||
out->color[0] = (a->color[0] + b->color[0]) >> 1;
|
||||
out->color[1] = (a->color[1] + b->color[1]) >> 1;
|
||||
out->color[2] = (a->color[2] + b->color[2]) >> 1;
|
||||
out->color[3] = (a->color[3] + b->color[3]) >> 1;
|
||||
}
|
||||
|
||||
|
||||
void FreeMesh( mesh_t *m ) {
|
||||
free( m->verts );
|
||||
free( m );
|
||||
}
|
||||
|
||||
void PrintMesh( mesh_t *m ) {
|
||||
int i, j;
|
||||
|
||||
for ( i = 0 ; i < m->height ; i++ ) {
|
||||
for ( j = 0 ; j < m->width ; j++ ) {
|
||||
_printf("(%5.2f %5.2f %5.2f) "
|
||||
, m->verts[i*m->width+j].xyz[0]
|
||||
, m->verts[i*m->width+j].xyz[1]
|
||||
, m->verts[i*m->width+j].xyz[2] );
|
||||
}
|
||||
_printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
mesh_t *CopyMesh( mesh_t *mesh ) {
|
||||
mesh_t *out;
|
||||
int size;
|
||||
|
||||
out = malloc( sizeof( *out ) );
|
||||
out->width = mesh->width;
|
||||
out->height = mesh->height;
|
||||
|
||||
size = out->width * out->height * sizeof( *out->verts );
|
||||
out->verts = malloc( size );
|
||||
memcpy( out->verts, mesh->verts, size );
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=================
|
||||
TransposeMesh
|
||||
|
||||
Returns a transposed copy of the mesh, freeing the original
|
||||
=================
|
||||
*/
|
||||
mesh_t *TransposeMesh( mesh_t *in ) {
|
||||
int w, h;
|
||||
mesh_t *out;
|
||||
|
||||
out = malloc( sizeof( *out ) );
|
||||
out->width = in->height;
|
||||
out->height = in->width;
|
||||
out->verts = malloc( out->width * out->height * sizeof( drawVert_t ) );
|
||||
|
||||
for ( h = 0 ; h < in->height ; h++ ) {
|
||||
for ( w = 0 ; w < in->width ; w++ ) {
|
||||
out->verts[ w * in->height + h ] = in->verts[ h * in->width + w ];
|
||||
}
|
||||
}
|
||||
|
||||
FreeMesh( in );
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
void InvertMesh( mesh_t *in ) {
|
||||
int w, h;
|
||||
drawVert_t temp;
|
||||
|
||||
for ( h = 0 ; h < in->height ; h++ ) {
|
||||
for ( w = 0 ; w < in->width / 2 ; w++ ) {
|
||||
temp = in->verts[ h * in->width + w ];
|
||||
in->verts[ h * in->width + w ] = in->verts[ h * in->width + in->width - 1 - w ];
|
||||
in->verts[ h * in->width + in->width - 1 - w ] = temp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
MakeMeshNormals
|
||||
|
||||
=================
|
||||
*/
|
||||
void MakeMeshNormals( mesh_t in ) {
|
||||
int i, j, k, dist;
|
||||
vec3_t normal;
|
||||
vec3_t sum;
|
||||
int count;
|
||||
vec3_t base;
|
||||
vec3_t delta;
|
||||
int x, y;
|
||||
drawVert_t *dv;
|
||||
vec3_t around[8], temp;
|
||||
qboolean good[8];
|
||||
qboolean wrapWidth, wrapHeight;
|
||||
float len;
|
||||
|
||||
wrapWidth = qfalse;
|
||||
for ( i = 0 ; i < in.height ; i++ ) {
|
||||
VectorSubtract( in.verts[i*in.width].xyz,
|
||||
in.verts[i*in.width+in.width-1].xyz, delta );
|
||||
len = VectorLength( delta );
|
||||
if ( len > 1.0 ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( i == in.height ) {
|
||||
wrapWidth = qtrue;
|
||||
}
|
||||
|
||||
wrapHeight = qfalse;
|
||||
for ( i = 0 ; i < in.width ; i++ ) {
|
||||
VectorSubtract( in.verts[i].xyz,
|
||||
in.verts[i + (in.height-1)*in.width].xyz, delta );
|
||||
len = VectorLength( delta );
|
||||
if ( len > 1.0 ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( i == in.width) {
|
||||
wrapHeight = qtrue;
|
||||
}
|
||||
|
||||
|
||||
for ( i = 0 ; i < in.width ; i++ ) {
|
||||
for ( j = 0 ; j < in.height ; j++ ) {
|
||||
count = 0;
|
||||
dv = &in.verts[j*in.width+i];
|
||||
VectorCopy( dv->xyz, base );
|
||||
for ( k = 0 ; k < 8 ; k++ ) {
|
||||
VectorClear( around[k] );
|
||||
good[k] = qfalse;
|
||||
|
||||
for ( dist = 1 ; dist <= 3 ; dist++ ) {
|
||||
x = i + neighbors[k][0] * dist;
|
||||
y = j + neighbors[k][1] * dist;
|
||||
if ( wrapWidth ) {
|
||||
if ( x < 0 ) {
|
||||
x = in.width - 1 + x;
|
||||
} else if ( x >= in.width ) {
|
||||
x = 1 + x - in.width;
|
||||
}
|
||||
}
|
||||
if ( wrapHeight ) {
|
||||
if ( y < 0 ) {
|
||||
y = in.height - 1 + y;
|
||||
} else if ( y >= in.height ) {
|
||||
y = 1 + y - in.height;
|
||||
}
|
||||
}
|
||||
|
||||
if ( x < 0 || x >= in.width || y < 0 || y >= in.height ) {
|
||||
break; // edge of patch
|
||||
}
|
||||
VectorSubtract( in.verts[y*in.width+x].xyz, base, temp );
|
||||
if ( VectorNormalize( temp, temp ) == 0 ) {
|
||||
continue; // degenerate edge, get more dist
|
||||
} else {
|
||||
good[k] = qtrue;
|
||||
VectorCopy( temp, around[k] );
|
||||
break; // good edge
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
VectorClear( sum );
|
||||
for ( k = 0 ; k < 8 ; k++ ) {
|
||||
if ( !good[k] || !good[(k+1)&7] ) {
|
||||
continue; // didn't get two points
|
||||
}
|
||||
CrossProduct( around[(k+1)&7], around[k], normal );
|
||||
if ( VectorNormalize( normal, normal ) == 0 ) {
|
||||
continue;
|
||||
}
|
||||
VectorAdd( normal, sum, sum );
|
||||
count++;
|
||||
}
|
||||
if ( count == 0 ) {
|
||||
//_printf("bad normal\n");
|
||||
count = 1;
|
||||
}
|
||||
VectorNormalize( sum, dv->normal );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
PutMeshOnCurve
|
||||
|
||||
Drops the aproximating points onto the curve
|
||||
=================
|
||||
*/
|
||||
void PutMeshOnCurve( mesh_t in ) {
|
||||
int i, j, l;
|
||||
float prev, next;
|
||||
|
||||
// put all the aproximating points on the curve
|
||||
for ( i = 0 ; i < in.width ; i++ ) {
|
||||
for ( j = 1 ; j < in.height ; j += 2 ) {
|
||||
for ( l = 0 ; l < 3 ; l++ ) {
|
||||
prev = ( in.verts[j*in.width+i].xyz[l] + in.verts[(j+1)*in.width+i].xyz[l] ) * 0.5;
|
||||
next = ( in.verts[j*in.width+i].xyz[l] + in.verts[(j-1)*in.width+i].xyz[l] ) * 0.5;
|
||||
in.verts[j*in.width+i].xyz[l] = ( prev + next ) * 0.5;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for ( j = 0 ; j < in.height ; j++ ) {
|
||||
for ( i = 1 ; i < in.width ; i += 2 ) {
|
||||
for ( l = 0 ; l < 3 ; l++ ) {
|
||||
prev = ( in.verts[j*in.width+i].xyz[l] + in.verts[j*in.width+i+1].xyz[l] ) * 0.5;
|
||||
next = ( in.verts[j*in.width+i].xyz[l] + in.verts[j*in.width+i-1].xyz[l] ) * 0.5;
|
||||
in.verts[j*in.width+i].xyz[l] = ( prev + next ) * 0.5;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=================
|
||||
SubdivideMesh
|
||||
|
||||
=================
|
||||
*/
|
||||
mesh_t *SubdivideMesh( mesh_t in, float maxError, float minLength ) {
|
||||
int i, j, k, l;
|
||||
drawVert_t prev, next, mid;
|
||||
vec3_t prevxyz, nextxyz, midxyz;
|
||||
vec3_t delta;
|
||||
float len;
|
||||
mesh_t out;
|
||||
drawVert_t expand[MAX_EXPANDED_AXIS][MAX_EXPANDED_AXIS];
|
||||
|
||||
out.width = in.width;
|
||||
out.height = in.height;
|
||||
|
||||
for ( i = 0 ; i < in.width ; i++ ) {
|
||||
for ( j = 0 ; j < in.height ; j++ ) {
|
||||
expand[j][i] = in.verts[j*in.width+i];
|
||||
}
|
||||
}
|
||||
|
||||
for ( i = 0 ; i < in.height ; i++ ) {
|
||||
originalHeights[i] = i;
|
||||
}
|
||||
for ( i = 0 ; i < in.width ; i++ ) {
|
||||
originalWidths[i] = i;
|
||||
}
|
||||
|
||||
// horizontal subdivisions
|
||||
for ( j = 0 ; j + 2 < out.width ; j += 2 ) {
|
||||
// check subdivided midpoints against control points
|
||||
for ( i = 0 ; i < out.height ; i++ ) {
|
||||
for ( l = 0 ; l < 3 ; l++ ) {
|
||||
prevxyz[l] = expand[i][j+1].xyz[l] - expand[i][j].xyz[l];
|
||||
nextxyz[l] = expand[i][j+2].xyz[l] - expand[i][j+1].xyz[l];
|
||||
midxyz[l] = (expand[i][j].xyz[l] + expand[i][j+1].xyz[l] * 2
|
||||
+ expand[i][j+2].xyz[l] ) * 0.25;
|
||||
}
|
||||
|
||||
// if the span length is too long, force a subdivision
|
||||
if ( VectorLength( prevxyz ) > minLength
|
||||
|| VectorLength( nextxyz ) > minLength ) {
|
||||
break;
|
||||
}
|
||||
|
||||
// see if this midpoint is off far enough to subdivide
|
||||
VectorSubtract( expand[i][j+1].xyz, midxyz, delta );
|
||||
len = VectorLength( delta );
|
||||
if ( len > maxError ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( out.width + 2 >= MAX_EXPANDED_AXIS ) {
|
||||
break; // can't subdivide any more
|
||||
}
|
||||
|
||||
if ( i == out.height ) {
|
||||
continue; // didn't need subdivision
|
||||
}
|
||||
|
||||
// insert two columns and replace the peak
|
||||
out.width += 2;
|
||||
|
||||
for ( k = out.width - 1 ; k > j + 3 ; k-- ) {
|
||||
originalWidths[k] = originalWidths[k-2];
|
||||
}
|
||||
originalWidths[j+3] = originalWidths[j+1];
|
||||
originalWidths[j+2] = originalWidths[j+1];
|
||||
originalWidths[j+1] = originalWidths[j];
|
||||
|
||||
for ( i = 0 ; i < out.height ; i++ ) {
|
||||
LerpDrawVert( &expand[i][j], &expand[i][j+1], &prev );
|
||||
LerpDrawVert( &expand[i][j+1], &expand[i][j+2], &next );
|
||||
LerpDrawVert( &prev, &next, &mid );
|
||||
|
||||
for ( k = out.width - 1 ; k > j + 3 ; k-- ) {
|
||||
expand[i][k] = expand[i][k-2];
|
||||
}
|
||||
expand[i][j + 1] = prev;
|
||||
expand[i][j + 2] = mid;
|
||||
expand[i][j + 3] = next;
|
||||
}
|
||||
|
||||
// back up and recheck this set again, it may need more subdivision
|
||||
j -= 2;
|
||||
|
||||
}
|
||||
|
||||
// vertical subdivisions
|
||||
for ( j = 0 ; j + 2 < out.height ; j += 2 ) {
|
||||
// check subdivided midpoints against control points
|
||||
for ( i = 0 ; i < out.width ; i++ ) {
|
||||
for ( l = 0 ; l < 3 ; l++ ) {
|
||||
prevxyz[l] = expand[j+1][i].xyz[l] - expand[j][i].xyz[l];
|
||||
nextxyz[l] = expand[j+2][i].xyz[l] - expand[j+1][i].xyz[l];
|
||||
midxyz[l] = (expand[j][i].xyz[l] + expand[j+1][i].xyz[l] * 2
|
||||
+ expand[j+2][i].xyz[l] ) * 0.25;
|
||||
}
|
||||
|
||||
// if the span length is too long, force a subdivision
|
||||
if ( VectorLength( prevxyz ) > minLength
|
||||
|| VectorLength( nextxyz ) > minLength ) {
|
||||
break;
|
||||
}
|
||||
// see if this midpoint is off far enough to subdivide
|
||||
VectorSubtract( expand[j+1][i].xyz, midxyz, delta );
|
||||
len = VectorLength( delta );
|
||||
if ( len > maxError ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( out.height + 2 >= MAX_EXPANDED_AXIS ) {
|
||||
break; // can't subdivide any more
|
||||
}
|
||||
|
||||
if ( i == out.width ) {
|
||||
continue; // didn't need subdivision
|
||||
}
|
||||
|
||||
// insert two columns and replace the peak
|
||||
out.height += 2;
|
||||
|
||||
for ( k = out.height - 1 ; k > j + 3 ; k-- ) {
|
||||
originalHeights[k] = originalHeights[k-2];
|
||||
}
|
||||
originalHeights[j+3] = originalHeights[j+1];
|
||||
originalHeights[j+2] = originalHeights[j+1];
|
||||
originalHeights[j+1] = originalHeights[j];
|
||||
|
||||
for ( i = 0 ; i < out.width ; i++ ) {
|
||||
LerpDrawVert( &expand[j][i], &expand[j+1][i], &prev );
|
||||
LerpDrawVert( &expand[j+1][i], &expand[j+2][i], &next );
|
||||
LerpDrawVert( &prev, &next, &mid );
|
||||
|
||||
for ( k = out.height - 1 ; k > j + 3 ; k-- ) {
|
||||
expand[k][i] = expand[k-2][i];
|
||||
}
|
||||
expand[j+1][i] = prev;
|
||||
expand[j+2][i] = mid;
|
||||
expand[j+3][i] = next;
|
||||
}
|
||||
|
||||
// back up and recheck this set again, it may need more subdivision
|
||||
j -= 2;
|
||||
|
||||
}
|
||||
|
||||
// collapse the verts
|
||||
|
||||
out.verts = &expand[0][0];
|
||||
for ( i = 1 ; i < out.height ; i++ ) {
|
||||
memmove( &out.verts[i*out.width], expand[i], out.width * sizeof(drawVert_t) );
|
||||
}
|
||||
|
||||
return CopyMesh(&out);
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
ProjectPointOntoVector
|
||||
================
|
||||
*/
|
||||
void ProjectPointOntoVector( vec3_t point, vec3_t vStart, vec3_t vEnd, vec3_t vProj )
|
||||
{
|
||||
vec3_t pVec, vec;
|
||||
|
||||
VectorSubtract( point, vStart, pVec );
|
||||
VectorSubtract( vEnd, vStart, vec );
|
||||
VectorNormalize( vec, vec );
|
||||
// project onto the directional vector for this segment
|
||||
VectorMA( vStart, DotProduct( pVec, vec ), vec, vProj );
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
RemoveLinearMeshColumsRows
|
||||
================
|
||||
*/
|
||||
mesh_t *RemoveLinearMeshColumnsRows( mesh_t *in ) {
|
||||
int i, j, k;
|
||||
float len, maxLength;
|
||||
vec3_t proj, dir;
|
||||
mesh_t out;
|
||||
drawVert_t expand[MAX_EXPANDED_AXIS][MAX_EXPANDED_AXIS];
|
||||
|
||||
out.width = in->width;
|
||||
out.height = in->height;
|
||||
|
||||
for ( i = 0 ; i < in->width ; i++ ) {
|
||||
for ( j = 0 ; j < in->height ; j++ ) {
|
||||
expand[j][i] = in->verts[j*in->width+i];
|
||||
}
|
||||
}
|
||||
|
||||
for ( j = 1 ; j < out.width - 1; j++ ) {
|
||||
maxLength = 0;
|
||||
for ( i = 0 ; i < out.height ; i++ ) {
|
||||
ProjectPointOntoVector(expand[i][j].xyz, expand[i][j-1].xyz, expand[i][j+1].xyz, proj);
|
||||
VectorSubtract(expand[i][j].xyz, proj, dir);
|
||||
len = VectorLength(dir);
|
||||
if (len > maxLength) {
|
||||
maxLength = len;
|
||||
}
|
||||
}
|
||||
if (maxLength < 0.1)
|
||||
{
|
||||
out.width--;
|
||||
for ( i = 0 ; i < out.height ; i++ ) {
|
||||
for (k = j; k < out.width; k++) {
|
||||
expand[i][k] = expand[i][k+1];
|
||||
}
|
||||
}
|
||||
for (k = j; k < out.width; k++) {
|
||||
originalWidths[k] = originalWidths[k+1];
|
||||
}
|
||||
j--;
|
||||
}
|
||||
}
|
||||
for ( j = 1 ; j < out.height - 1; j++ ) {
|
||||
maxLength = 0;
|
||||
for ( i = 0 ; i < out.width ; i++ ) {
|
||||
ProjectPointOntoVector(expand[j][i].xyz, expand[j-1][i].xyz, expand[j+1][i].xyz, proj);
|
||||
VectorSubtract(expand[j][i].xyz, proj, dir);
|
||||
len = VectorLength(dir);
|
||||
if (len > maxLength) {
|
||||
maxLength = len;
|
||||
}
|
||||
}
|
||||
if (maxLength < 0.1)
|
||||
{
|
||||
out.height--;
|
||||
for ( i = 0 ; i < out.width ; i++ ) {
|
||||
for (k = j; k < out.height; k++) {
|
||||
expand[k][i] = expand[k+1][i];
|
||||
}
|
||||
}
|
||||
for (k = j; k < out.height; k++) {
|
||||
originalHeights[k] = originalHeights[k+1];
|
||||
}
|
||||
j--;
|
||||
}
|
||||
}
|
||||
// collapse the verts
|
||||
out.verts = &expand[0][0];
|
||||
for ( i = 1 ; i < out.height ; i++ ) {
|
||||
memmove( &out.verts[i*out.width], expand[i], out.width * sizeof(drawVert_t) );
|
||||
}
|
||||
|
||||
return CopyMesh(&out);
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
LerpDrawVertAmount
|
||||
============
|
||||
*/
|
||||
void LerpDrawVertAmount( drawVert_t *a, drawVert_t *b, float amount, drawVert_t *out ) {
|
||||
out->xyz[0] = a->xyz[0] + amount * (b->xyz[0] - a->xyz[0]);
|
||||
out->xyz[1] = a->xyz[1] + amount * (b->xyz[1] - a->xyz[1]);
|
||||
out->xyz[2] = a->xyz[2] + amount * (b->xyz[2] - a->xyz[2]);
|
||||
|
||||
out->st[0] = a->st[0] + amount * (b->st[0] - a->st[0]);
|
||||
out->st[1] = a->st[1] + amount * (b->st[1] - a->st[1]);
|
||||
|
||||
out->lightmap[0] = a->lightmap[0] + amount * (b->lightmap[0] - a->lightmap[0]);
|
||||
out->lightmap[1] = a->lightmap[1] + amount * (b->lightmap[1] - a->lightmap[1]);
|
||||
|
||||
out->color[0] = a->color[0] + amount * (b->color[0] - a->color[0]);
|
||||
out->color[1] = a->color[1] + amount * (b->color[1] - a->color[1]);
|
||||
out->color[2] = a->color[2] + amount * (b->color[2] - a->color[2]);
|
||||
out->color[3] = a->color[3] + amount * (b->color[3] - a->color[3]);
|
||||
|
||||
out->normal[0] = a->normal[0] + amount * (b->normal[0] - a->normal[0]);
|
||||
out->normal[1] = a->normal[1] + amount * (b->normal[1] - a->normal[1]);
|
||||
out->normal[2] = a->normal[2] + amount * (b->normal[2] - a->normal[2]);
|
||||
VectorNormalize(out->normal, out->normal);
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
SubdivideMeshQuads
|
||||
=================
|
||||
*/
|
||||
mesh_t *SubdivideMeshQuads( mesh_t *in, float minLength, int maxsize, int widthtable[], int heighttable[]) {
|
||||
int i, j, k, w, h, maxsubdivisions, subdivisions;
|
||||
vec3_t dir;
|
||||
float length, maxLength, amount;
|
||||
mesh_t out;
|
||||
drawVert_t expand[MAX_EXPANDED_AXIS][MAX_EXPANDED_AXIS];
|
||||
|
||||
out.width = in->width;
|
||||
out.height = in->height;
|
||||
|
||||
for ( i = 0 ; i < in->width ; i++ ) {
|
||||
for ( j = 0 ; j < in->height ; j++ ) {
|
||||
expand[j][i] = in->verts[j*in->width+i];
|
||||
}
|
||||
}
|
||||
|
||||
if (maxsize > MAX_EXPANDED_AXIS)
|
||||
Error("SubdivideMeshQuads: maxsize > MAX_EXPANDED_AXIS");
|
||||
|
||||
// horizontal subdivisions
|
||||
|
||||
maxsubdivisions = (maxsize - in->width) / (in->width - 1);
|
||||
|
||||
for ( w = 0, j = 0 ; w < in->width - 1; w++, j += subdivisions + 1) {
|
||||
maxLength = 0;
|
||||
for ( i = 0 ; i < out.height ; i++ ) {
|
||||
VectorSubtract(expand[i][j+1].xyz, expand[i][j].xyz, dir);
|
||||
length = VectorLength( dir );
|
||||
if (length > maxLength) {
|
||||
maxLength = length;
|
||||
}
|
||||
}
|
||||
|
||||
subdivisions = (int) (maxLength / minLength);
|
||||
if (subdivisions > maxsubdivisions)
|
||||
subdivisions = maxsubdivisions;
|
||||
|
||||
widthtable[w] = subdivisions + 1;
|
||||
if (subdivisions <= 0)
|
||||
continue;
|
||||
|
||||
out.width += subdivisions;
|
||||
|
||||
for ( k = out.width - 1; k >= j + subdivisions; k-- ) {
|
||||
originalWidths[k] = originalWidths[k-subdivisions];
|
||||
}
|
||||
for (k = 1; k <= subdivisions; k++) {
|
||||
originalWidths[j+k] = originalWidths[j];
|
||||
}
|
||||
|
||||
for ( i = 0 ; i < out.height ; i++ ) {
|
||||
for ( k = out.width - 1 ; k > j + subdivisions; k-- ) {
|
||||
expand[i][k] = expand[i][k-subdivisions];
|
||||
}
|
||||
for (k = 1; k <= subdivisions; k++)
|
||||
{
|
||||
amount = (float) k / (subdivisions + 1);
|
||||
LerpDrawVertAmount(&expand[i][j], &expand[i][j+subdivisions+1], amount, &expand[i][j+k]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
maxsubdivisions = (maxsize - in->height) / (in->height - 1);
|
||||
|
||||
for ( h = 0, j = 0 ; h < in->height - 1; h++, j += subdivisions + 1) {
|
||||
maxLength = 0;
|
||||
for ( i = 0 ; i < out.width ; i++ ) {
|
||||
VectorSubtract(expand[j+1][i].xyz, expand[j][i].xyz, dir);
|
||||
length = VectorLength( dir );
|
||||
if (length > maxLength) {
|
||||
maxLength = length;
|
||||
}
|
||||
}
|
||||
|
||||
subdivisions = (int) (maxLength / minLength);
|
||||
if (subdivisions > maxsubdivisions)
|
||||
subdivisions = maxsubdivisions;
|
||||
|
||||
heighttable[h] = subdivisions + 1;
|
||||
if (subdivisions <= 0)
|
||||
continue;
|
||||
|
||||
out.height += subdivisions;
|
||||
|
||||
for ( k = out.height - 1; k >= j + subdivisions; k-- ) {
|
||||
originalHeights[k] = originalHeights[k-subdivisions];
|
||||
}
|
||||
for (k = 1; k <= subdivisions; k++) {
|
||||
originalHeights[j+k] = originalHeights[j];
|
||||
}
|
||||
|
||||
for ( i = 0 ; i < out.width ; i++ ) {
|
||||
for ( k = out.height - 1 ; k > j + subdivisions; k-- ) {
|
||||
expand[k][i] = expand[k-subdivisions][i];
|
||||
}
|
||||
for (k = 1; k <= subdivisions; k++)
|
||||
{
|
||||
amount = (float) k / (subdivisions + 1);
|
||||
LerpDrawVertAmount(&expand[j][i], &expand[j+subdivisions+1][i], amount, &expand[j+k][i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// collapse the verts
|
||||
out.verts = &expand[0][0];
|
||||
for ( i = 1 ; i < out.height ; i++ ) {
|
||||
memmove( &out.verts[i*out.width], expand[i], out.width * sizeof(drawVert_t) );
|
||||
}
|
||||
|
||||
return CopyMesh(&out);
|
||||
}
|
48
q3map/mesh.h
Normal file
48
q3map/mesh.h
Normal file
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 1999-2005 Id Software, Inc.
|
||||
|
||||
This file is part of Quake III Arena source code.
|
||||
|
||||
Quake III Arena source code 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.
|
||||
|
||||
Quake III Arena source code 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 Foobar; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
// mesh.h
|
||||
|
||||
|
||||
typedef struct {
|
||||
int width, height;
|
||||
drawVert_t *verts;
|
||||
} mesh_t;
|
||||
|
||||
#define MAX_EXPANDED_AXIS 128
|
||||
|
||||
extern int originalWidths[MAX_EXPANDED_AXIS];
|
||||
extern int originalHeights[MAX_EXPANDED_AXIS];
|
||||
|
||||
void FreeMesh( mesh_t *m );
|
||||
mesh_t *CopyMesh( mesh_t *mesh );
|
||||
void PrintMesh( mesh_t *m );
|
||||
mesh_t *TransposeMesh( mesh_t *in );
|
||||
void InvertMesh( mesh_t *m );
|
||||
mesh_t *SubdivideMesh( mesh_t in, float maxError, float minLength );
|
||||
mesh_t *SubdivideMeshQuads( mesh_t *in, float minLength, int maxsize, int widthtable[], int heighttable[]);
|
||||
mesh_t *RemoveLinearMeshColumnsRows( mesh_t *in );
|
||||
void MakeMeshNormals( mesh_t in );
|
||||
void PutMeshOnCurve( mesh_t in );
|
||||
|
||||
|
||||
void MakeNormalVectors (vec3_t forward, vec3_t right, vec3_t up);
|
472
q3map/misc_model.c
Normal file
472
q3map/misc_model.c
Normal file
|
@ -0,0 +1,472 @@
|
|||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 1999-2005 Id Software, Inc.
|
||||
|
||||
This file is part of Quake III Arena source code.
|
||||
|
||||
Quake III Arena source code 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.
|
||||
|
||||
Quake III Arena source code 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 Foobar; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
#include "qbsp.h"
|
||||
#include "aselib.h"
|
||||
#ifdef _WIN32
|
||||
#ifdef _TTIMOBUILD
|
||||
#include "pakstuff.h"
|
||||
#else
|
||||
#include "../libs/pakstuff.h"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
typedef struct {
|
||||
char modelName[1024];
|
||||
md3Header_t *header;
|
||||
} loadedModel_t;
|
||||
|
||||
int c_triangleModels;
|
||||
int c_triangleSurfaces;
|
||||
int c_triangleVertexes;
|
||||
int c_triangleIndexes;
|
||||
|
||||
|
||||
#define MAX_LOADED_MODELS 1024
|
||||
loadedModel_t loadedModels[MAX_LOADED_MODELS];
|
||||
int numLoadedModels;
|
||||
|
||||
/*
|
||||
=================
|
||||
R_LoadMD3
|
||||
=================
|
||||
*/
|
||||
#define LL(x) x=LittleLong(x)
|
||||
md3Header_t *R_LoadMD3( const char *mod_name ) {
|
||||
int i, j;
|
||||
md3Header_t *md3;
|
||||
md3Frame_t *frame;
|
||||
md3Surface_t *surf;
|
||||
md3Triangle_t *tri;
|
||||
md3St_t *st;
|
||||
md3XyzNormal_t *xyz;
|
||||
int version;
|
||||
char filename[1024];
|
||||
int len;
|
||||
|
||||
sprintf( filename, "%s%s", gamedir, mod_name );
|
||||
len = TryLoadFile( filename, (void **)&md3 );
|
||||
#ifdef _WIN32
|
||||
if ( len <= 0 ) {
|
||||
len = PakLoadAnyFile(filename, (void **)&md3);
|
||||
}
|
||||
#endif
|
||||
if ( len <= 0 ) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
version = LittleLong (md3->version);
|
||||
if (version != MD3_VERSION) {
|
||||
_printf( "R_LoadMD3: %s has wrong version (%i should be %i)\n",
|
||||
mod_name, version, MD3_VERSION);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
LL(md3->ident);
|
||||
LL(md3->version);
|
||||
LL(md3->numFrames);
|
||||
LL(md3->numTags);
|
||||
LL(md3->numSurfaces);
|
||||
LL(md3->numSkins);
|
||||
LL(md3->ofsFrames);
|
||||
LL(md3->ofsTags);
|
||||
LL(md3->ofsSurfaces);
|
||||
LL(md3->ofsEnd);
|
||||
|
||||
if ( md3->numFrames < 1 ) {
|
||||
_printf( "R_LoadMD3: %s has no frames\n", mod_name );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// we don't need to swap tags in the renderer, they aren't used
|
||||
|
||||
// swap all the frames
|
||||
frame = (md3Frame_t *) ( (byte *)md3 + md3->ofsFrames );
|
||||
for ( i = 0 ; i < md3->numFrames ; i++, frame++) {
|
||||
frame->radius = LittleFloat( frame->radius );
|
||||
for ( j = 0 ; j < 3 ; j++ ) {
|
||||
frame->bounds[0][j] = LittleFloat( frame->bounds[0][j] );
|
||||
frame->bounds[1][j] = LittleFloat( frame->bounds[1][j] );
|
||||
frame->localOrigin[j] = LittleFloat( frame->localOrigin[j] );
|
||||
}
|
||||
}
|
||||
|
||||
// swap all the surfaces
|
||||
surf = (md3Surface_t *) ( (byte *)md3 + md3->ofsSurfaces );
|
||||
for ( i = 0 ; i < md3->numSurfaces ; i++) {
|
||||
|
||||
LL(surf->ident);
|
||||
LL(surf->flags);
|
||||
LL(surf->numFrames);
|
||||
LL(surf->numShaders);
|
||||
LL(surf->numTriangles);
|
||||
LL(surf->ofsTriangles);
|
||||
LL(surf->numVerts);
|
||||
LL(surf->ofsShaders);
|
||||
LL(surf->ofsSt);
|
||||
LL(surf->ofsXyzNormals);
|
||||
LL(surf->ofsEnd);
|
||||
|
||||
if ( surf->numVerts > SHADER_MAX_VERTEXES ) {
|
||||
Error ("R_LoadMD3: %s has more than %i verts on a surface (%i)",
|
||||
mod_name, SHADER_MAX_VERTEXES, surf->numVerts );
|
||||
}
|
||||
if ( surf->numTriangles*3 > SHADER_MAX_INDEXES ) {
|
||||
Error ("R_LoadMD3: %s has more than %i triangles on a surface (%i)",
|
||||
mod_name, SHADER_MAX_INDEXES / 3, surf->numTriangles );
|
||||
}
|
||||
|
||||
// swap all the triangles
|
||||
tri = (md3Triangle_t *) ( (byte *)surf + surf->ofsTriangles );
|
||||
for ( j = 0 ; j < surf->numTriangles ; j++, tri++ ) {
|
||||
LL(tri->indexes[0]);
|
||||
LL(tri->indexes[1]);
|
||||
LL(tri->indexes[2]);
|
||||
}
|
||||
|
||||
// swap all the ST
|
||||
st = (md3St_t *) ( (byte *)surf + surf->ofsSt );
|
||||
for ( j = 0 ; j < surf->numVerts ; j++, st++ ) {
|
||||
st->st[0] = LittleFloat( st->st[0] );
|
||||
st->st[1] = LittleFloat( st->st[1] );
|
||||
}
|
||||
|
||||
// swap all the XyzNormals
|
||||
xyz = (md3XyzNormal_t *) ( (byte *)surf + surf->ofsXyzNormals );
|
||||
for ( j = 0 ; j < surf->numVerts * surf->numFrames ; j++, xyz++ )
|
||||
{
|
||||
xyz->xyz[0] = LittleShort( xyz->xyz[0] );
|
||||
xyz->xyz[1] = LittleShort( xyz->xyz[1] );
|
||||
xyz->xyz[2] = LittleShort( xyz->xyz[2] );
|
||||
|
||||
xyz->normal = LittleShort( xyz->normal );
|
||||
}
|
||||
|
||||
|
||||
// find the next surface
|
||||
surf = (md3Surface_t *)( (byte *)surf + surf->ofsEnd );
|
||||
}
|
||||
|
||||
return md3;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
================
|
||||
LoadModel
|
||||
================
|
||||
*/
|
||||
md3Header_t *LoadModel( const char *modelName ) {
|
||||
int i;
|
||||
loadedModel_t *lm;
|
||||
|
||||
// see if we already have it loaded
|
||||
for ( i = 0, lm = loadedModels ; i < numLoadedModels ; i++, lm++ ) {
|
||||
if ( !strcmp( modelName, lm->modelName ) ) {
|
||||
return lm->header;
|
||||
}
|
||||
}
|
||||
|
||||
// load it
|
||||
if ( numLoadedModels == MAX_LOADED_MODELS ) {
|
||||
Error( "MAX_LOADED_MODELS" );
|
||||
}
|
||||
numLoadedModels++;
|
||||
|
||||
strcpy( lm->modelName, modelName );
|
||||
|
||||
lm->header = R_LoadMD3( modelName );
|
||||
|
||||
return lm->header;
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
InsertMD3Model
|
||||
|
||||
Convert a model entity to raw geometry surfaces and insert it in the tree
|
||||
============
|
||||
*/
|
||||
void InsertMD3Model( const char *modelName, vec3_t origin, float angle, tree_t *tree ) {
|
||||
int i, j;
|
||||
md3Header_t *md3;
|
||||
md3Surface_t *surf;
|
||||
md3Shader_t *shader;
|
||||
md3Triangle_t *tri;
|
||||
md3St_t *st;
|
||||
md3XyzNormal_t *xyz;
|
||||
drawVert_t *outv;
|
||||
float lat, lng;
|
||||
float angleCos, angleSin;
|
||||
mapDrawSurface_t *out;
|
||||
vec3_t temp;
|
||||
|
||||
angle = angle / 180 * Q_PI;
|
||||
angleCos = cos( angle );
|
||||
angleSin = sin( angle );
|
||||
|
||||
// load the model
|
||||
md3 = LoadModel( modelName );
|
||||
if ( !md3 ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// each md3 surface will become a new bsp surface
|
||||
|
||||
c_triangleModels++;
|
||||
c_triangleSurfaces += md3->numSurfaces;
|
||||
|
||||
// expand, translate, and rotate the vertexes
|
||||
// swap all the surfaces
|
||||
surf = (md3Surface_t *) ( (byte *)md3 + md3->ofsSurfaces );
|
||||
for ( i = 0 ; i < md3->numSurfaces ; i++) {
|
||||
// allocate a surface
|
||||
out = AllocDrawSurf();
|
||||
out->miscModel = qtrue;
|
||||
|
||||
shader = (md3Shader_t *) ( (byte *)surf + surf->ofsShaders );
|
||||
|
||||
out->shaderInfo = ShaderInfoForShader( shader->name );
|
||||
|
||||
out->numVerts = surf->numVerts;
|
||||
out->verts = malloc( out->numVerts * sizeof( out->verts[0] ) );
|
||||
|
||||
out->numIndexes = surf->numTriangles * 3;
|
||||
out->indexes = malloc( out->numIndexes * sizeof( out->indexes[0] ) );
|
||||
|
||||
out->lightmapNum = -1;
|
||||
out->fogNum = -1;
|
||||
|
||||
// emit the indexes
|
||||
c_triangleIndexes += surf->numTriangles * 3;
|
||||
tri = (md3Triangle_t *) ( (byte *)surf + surf->ofsTriangles );
|
||||
for ( j = 0 ; j < surf->numTriangles ; j++, tri++ ) {
|
||||
out->indexes[j*3+0] = tri->indexes[0];
|
||||
out->indexes[j*3+1] = tri->indexes[1];
|
||||
out->indexes[j*3+2] = tri->indexes[2];
|
||||
}
|
||||
|
||||
// emit the vertexes
|
||||
st = (md3St_t *) ( (byte *)surf + surf->ofsSt );
|
||||
xyz = (md3XyzNormal_t *) ( (byte *)surf + surf->ofsXyzNormals );
|
||||
|
||||
c_triangleVertexes += surf->numVerts;
|
||||
for ( j = 0 ; j < surf->numVerts ; j++, st++, xyz++ ) {
|
||||
outv = &out->verts[ j ];
|
||||
|
||||
outv->st[0] = st->st[0];
|
||||
outv->st[1] = st->st[1];
|
||||
|
||||
outv->lightmap[0] = 0;
|
||||
outv->lightmap[1] = 0;
|
||||
|
||||
// the colors will be set by the lighting pass
|
||||
outv->color[0] = 255;
|
||||
outv->color[1] = 255;
|
||||
outv->color[2] = 255;
|
||||
outv->color[3] = 255;
|
||||
|
||||
outv->xyz[0] = origin[0] + MD3_XYZ_SCALE * ( xyz->xyz[0] * angleCos - xyz->xyz[1] * angleSin );
|
||||
outv->xyz[1] = origin[1] + MD3_XYZ_SCALE * ( xyz->xyz[0] * angleSin + xyz->xyz[1] * angleCos );
|
||||
outv->xyz[2] = origin[2] + MD3_XYZ_SCALE * ( xyz->xyz[2] );
|
||||
|
||||
// decode the lat/lng normal to a 3 float normal
|
||||
lat = ( xyz->normal >> 8 ) & 0xff;
|
||||
lng = ( xyz->normal & 0xff );
|
||||
lat *= Q_PI/128;
|
||||
lng *= Q_PI/128;
|
||||
|
||||
temp[0] = cos(lat) * sin(lng);
|
||||
temp[1] = sin(lat) * sin(lng);
|
||||
temp[2] = cos(lng);
|
||||
|
||||
// rotate the normal
|
||||
outv->normal[0] = temp[0] * angleCos - temp[1] * angleSin;
|
||||
outv->normal[1] = temp[0] * angleSin + temp[1] * angleCos;
|
||||
outv->normal[2] = temp[2];
|
||||
}
|
||||
|
||||
// find the next surface
|
||||
surf = (md3Surface_t *)( (byte *)surf + surf->ofsEnd );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
|
||||
|
||||
/*
|
||||
============
|
||||
InsertASEModel
|
||||
|
||||
Convert a model entity to raw geometry surfaces and insert it in the tree
|
||||
============
|
||||
*/
|
||||
void InsertASEModel( const char *modelName, vec3_t origin, float angle, tree_t *tree ) {
|
||||
int i, j;
|
||||
drawVert_t *outv;
|
||||
float angleCos, angleSin;
|
||||
mapDrawSurface_t *out;
|
||||
int numSurfaces;
|
||||
const char *name;
|
||||
polyset_t *pset;
|
||||
int numFrames;
|
||||
char filename[1024];
|
||||
|
||||
sprintf( filename, "%s%s", gamedir, modelName );
|
||||
|
||||
angle = angle / 180 * Q_PI;
|
||||
angleCos = cos( angle );
|
||||
angleSin = sin( angle );
|
||||
|
||||
// load the model
|
||||
ASE_Load( filename, qfalse, qfalse );
|
||||
|
||||
// each ase surface will become a new bsp surface
|
||||
numSurfaces = ASE_GetNumSurfaces();
|
||||
|
||||
c_triangleModels++;
|
||||
c_triangleSurfaces += numSurfaces;
|
||||
|
||||
// expand, translate, and rotate the vertexes
|
||||
// swap all the surfaces
|
||||
for ( i = 0 ; i < numSurfaces ; i++) {
|
||||
name = ASE_GetSurfaceName( i );
|
||||
|
||||
pset = ASE_GetSurfaceAnimation( i, &numFrames, -1, -1, -1 );
|
||||
if ( !name || !pset ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// allocate a surface
|
||||
out = AllocDrawSurf();
|
||||
out->miscModel = qtrue;
|
||||
|
||||
out->shaderInfo = ShaderInfoForShader( pset->materialname );
|
||||
|
||||
out->numVerts = 3 * pset->numtriangles;
|
||||
out->verts = malloc( out->numVerts * sizeof( out->verts[0] ) );
|
||||
|
||||
out->numIndexes = 3 * pset->numtriangles;
|
||||
out->indexes = malloc( out->numIndexes * sizeof( out->indexes[0] ) );
|
||||
|
||||
out->lightmapNum = -1;
|
||||
out->fogNum = -1;
|
||||
|
||||
// emit the indexes
|
||||
c_triangleIndexes += out->numIndexes;
|
||||
for ( j = 0 ; j < out->numIndexes ; j++ ) {
|
||||
out->indexes[j] = j;
|
||||
}
|
||||
|
||||
// emit the vertexes
|
||||
c_triangleVertexes += out->numVerts;
|
||||
for ( j = 0 ; j < out->numVerts ; j++ ) {
|
||||
int index;
|
||||
triangle_t *tri;
|
||||
|
||||
index = j % 3;
|
||||
tri = &pset->triangles[ j / 3 ];
|
||||
|
||||
outv = &out->verts[ j ];
|
||||
|
||||
outv->st[0] = tri->texcoords[index][0];
|
||||
outv->st[1] = tri->texcoords[index][1];
|
||||
|
||||
outv->lightmap[0] = 0;
|
||||
outv->lightmap[1] = 0;
|
||||
|
||||
// the colors will be set by the lighting pass
|
||||
outv->color[0] = 255;
|
||||
outv->color[1] = 255;
|
||||
outv->color[2] = 255;
|
||||
outv->color[3] = 255;
|
||||
|
||||
outv->xyz[0] = origin[0] + tri->verts[index][0];
|
||||
outv->xyz[1] = origin[1] + tri->verts[index][1];
|
||||
outv->xyz[2] = origin[2] + tri->verts[index][2];
|
||||
|
||||
// rotate the normal
|
||||
outv->normal[0] = tri->normals[index][0];
|
||||
outv->normal[1] = tri->normals[index][1];
|
||||
outv->normal[2] = tri->normals[index][2];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
//==============================================================================
|
||||
|
||||
|
||||
|
||||
/*
|
||||
=====================
|
||||
AddTriangleModels
|
||||
=====================
|
||||
*/
|
||||
void AddTriangleModels( tree_t *tree ) {
|
||||
int entity_num;
|
||||
entity_t *entity;
|
||||
|
||||
qprintf("----- AddTriangleModels -----\n");
|
||||
|
||||
for ( entity_num=1 ; entity_num< num_entities ; entity_num++ ) {
|
||||
entity = &entities[entity_num];
|
||||
|
||||
// convert misc_models into raw geometry
|
||||
if ( !Q_stricmp( "misc_model", ValueForKey( entity, "classname" ) ) ) {
|
||||
const char *model;
|
||||
vec3_t origin;
|
||||
float angle;
|
||||
|
||||
// get the angle for rotation FIXME: support full matrix positioning
|
||||
angle = FloatForKey( entity, "angle" );
|
||||
|
||||
GetVectorForKey( entity, "origin", origin );
|
||||
|
||||
model = ValueForKey( entity, "model" );
|
||||
if ( !model[0] ) {
|
||||
_printf("WARNING: misc_model at %i %i %i without a model key\n", (int)origin[0],
|
||||
(int)origin[1], (int)origin[2] );
|
||||
continue;
|
||||
}
|
||||
if ( strstr( model, ".md3" ) || strstr( model, ".MD3" ) ) {
|
||||
InsertMD3Model( model, origin, angle, tree );
|
||||
continue;
|
||||
}
|
||||
if ( strstr( model, ".ase" ) || strstr( model, ".ASE" ) ) {
|
||||
InsertASEModel( model, origin, angle, tree );
|
||||
continue;
|
||||
}
|
||||
_printf( "Unknown misc_model type: %s\n", model );
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
qprintf( "%5i triangle models\n", c_triangleModels );
|
||||
qprintf( "%5i triangle surfaces\n", c_triangleSurfaces );
|
||||
qprintf( "%5i triangle vertexes\n", c_triangleVertexes );
|
||||
qprintf( "%5i triangle indexes\n", c_triangleIndexes );
|
||||
}
|
||||
|
47
q3map/nodraw.c
Normal file
47
q3map/nodraw.c
Normal file
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 1999-2005 Id Software, Inc.
|
||||
|
||||
This file is part of Quake III Arena source code.
|
||||
|
||||
Quake III Arena source code 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.
|
||||
|
||||
Quake III Arena source code 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 Foobar; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
#include "qbsp.h"
|
||||
|
||||
vec3_t draw_mins, draw_maxs;
|
||||
qboolean drawflag;
|
||||
|
||||
void Draw_ClearWindow (void)
|
||||
{
|
||||
}
|
||||
|
||||
//============================================================
|
||||
|
||||
#define GLSERV_PORT 25001
|
||||
|
||||
|
||||
void GLS_BeginScene (void)
|
||||
{
|
||||
}
|
||||
|
||||
void GLS_Winding (winding_t *w, int code)
|
||||
{
|
||||
}
|
||||
|
||||
void GLS_EndScene (void)
|
||||
{
|
||||
}
|
286
q3map/patch.c
Normal file
286
q3map/patch.c
Normal file
|
@ -0,0 +1,286 @@
|
|||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 1999-2005 Id Software, Inc.
|
||||
|
||||
This file is part of Quake III Arena source code.
|
||||
|
||||
Quake III Arena source code 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.
|
||||
|
||||
Quake III Arena source code 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 Foobar; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
#include "qbsp.h"
|
||||
|
||||
|
||||
void PrintCtrl( vec3_t ctrl[9] ) {
|
||||
int i, j;
|
||||
|
||||
for ( i = 0 ; i < 3 ; i++ ) {
|
||||
for ( j = 0 ; j < 3 ; j++ ) {
|
||||
_printf("(%5.2f %5.2f %5.2f) ", ctrl[i*3+j][0], ctrl[i*3+j][1], ctrl[i*3+j][2] );
|
||||
}
|
||||
_printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
DrawSurfaceForMesh
|
||||
================
|
||||
*/
|
||||
mapDrawSurface_t *DrawSurfaceForMesh( mesh_t *m ) {
|
||||
mapDrawSurface_t *ds;
|
||||
int i, j;
|
||||
mesh_t *copy;
|
||||
|
||||
// to make valid normals for patches with degenerate edges,
|
||||
// we need to make a copy of the mesh and put the aproximating
|
||||
// points onto the curve
|
||||
copy = CopyMesh( m );
|
||||
PutMeshOnCurve( *copy );
|
||||
MakeMeshNormals( *copy );
|
||||
for ( j = 0 ; j < m->width ; j++ ) {
|
||||
for ( i = 0 ; i < m->height ; i++ ) {
|
||||
VectorCopy( copy->verts[i*m->width+j].normal, m->verts[i*m->width+j].normal );
|
||||
}
|
||||
}
|
||||
FreeMesh( copy );
|
||||
|
||||
ds = AllocDrawSurf();
|
||||
ds->mapBrush = NULL;
|
||||
ds->side = NULL;
|
||||
|
||||
ds->patch = qtrue;
|
||||
ds->patchWidth = m->width;
|
||||
ds->patchHeight = m->height;
|
||||
ds->numVerts = ds->patchWidth * ds->patchHeight;
|
||||
ds->verts = malloc( ds->numVerts * sizeof( *ds->verts ) );
|
||||
memcpy( ds->verts, m->verts, ds->numVerts * sizeof( *ds->verts ) );
|
||||
|
||||
ds->lightmapNum = -1;
|
||||
ds->fogNum = -1;
|
||||
|
||||
return ds;
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
ParsePatch
|
||||
|
||||
Creates a mapDrawSurface_t from the patch text
|
||||
=================
|
||||
*/
|
||||
void ParsePatch( void ) {
|
||||
vec_t info[5];
|
||||
int i, j;
|
||||
parseMesh_t *pm;
|
||||
char texture[MAX_QPATH];
|
||||
char shader[MAX_QPATH];
|
||||
mesh_t m;
|
||||
drawVert_t *verts;
|
||||
epair_t *ep;
|
||||
|
||||
MatchToken( "{" );
|
||||
|
||||
// get texture
|
||||
GetToken (qtrue);
|
||||
strcpy( texture, token );
|
||||
|
||||
// save the shader name for retexturing
|
||||
if ( numMapIndexedShaders == MAX_MAP_BRUSHSIDES ) {
|
||||
Error( "MAX_MAP_BRUSHSIDES" );
|
||||
}
|
||||
strcpy( mapIndexedShaders[numMapIndexedShaders], texture );
|
||||
numMapIndexedShaders++;
|
||||
|
||||
|
||||
Parse1DMatrix( 5, info );
|
||||
m.width = info[0];
|
||||
m.height = info[1];
|
||||
m.verts = verts = malloc( m.width * m.height * sizeof( m.verts[0] ) );
|
||||
|
||||
if ( m.width < 0 || m.width > MAX_PATCH_SIZE
|
||||
|| m.height < 0 || m.height > MAX_PATCH_SIZE ) {
|
||||
Error("ParsePatch: bad size");
|
||||
}
|
||||
|
||||
MatchToken( "(" );
|
||||
for ( j = 0 ; j < m.width ; j++ ) {
|
||||
MatchToken( "(" );
|
||||
for ( i = 0 ; i < m.height ; i++ ) {
|
||||
Parse1DMatrix( 5, verts[i*m.width+j].xyz );
|
||||
}
|
||||
MatchToken( ")" );
|
||||
}
|
||||
MatchToken( ")" );
|
||||
|
||||
// if brush primitives format, we may have some epairs to ignore here
|
||||
GetToken(qtrue);
|
||||
if (g_bBrushPrimit!=BPRIMIT_OLDBRUSHES && strcmp(token,"}"))
|
||||
{
|
||||
// NOTE: we leak that!
|
||||
ep = ParseEpair();
|
||||
}
|
||||
else
|
||||
UnGetToken();
|
||||
|
||||
MatchToken( "}" );
|
||||
MatchToken( "}" );
|
||||
|
||||
if ( noCurveBrushes ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// find default flags and values
|
||||
pm = malloc( sizeof( *pm ) );
|
||||
memset( pm, 0, sizeof( *pm ) );
|
||||
|
||||
sprintf( shader, "textures/%s", texture );
|
||||
pm->shaderInfo = ShaderInfoForShader( shader );
|
||||
pm->mesh = m;
|
||||
|
||||
// link to the entity
|
||||
pm->next = mapent->patches;
|
||||
mapent->patches = pm;
|
||||
}
|
||||
|
||||
|
||||
void GrowGroup_r( int patchNum, int patchCount, const byte *bordering, byte *group ) {
|
||||
int i;
|
||||
const byte *row;
|
||||
|
||||
if ( group[patchNum] ) {
|
||||
return;
|
||||
}
|
||||
group[patchNum] = 1;
|
||||
row = bordering + patchNum * patchCount;
|
||||
for ( i = 0 ; i < patchCount ; i++ ) {
|
||||
if ( row[i] ) {
|
||||
GrowGroup_r( i, patchCount, bordering, group );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=====================
|
||||
PatchMapDrawSurfs
|
||||
|
||||
Any patches that share an edge need to choose their
|
||||
level of detail as a unit, otherwise the edges would
|
||||
pull apart.
|
||||
=====================
|
||||
*/
|
||||
void PatchMapDrawSurfs( entity_t *e ) {
|
||||
parseMesh_t *pm;
|
||||
parseMesh_t *check, *scan;
|
||||
mapDrawSurface_t *ds;
|
||||
int patchCount, groupCount;
|
||||
int i, j, k, l, c1, c2;
|
||||
drawVert_t *v1, *v2;
|
||||
vec3_t bounds[2];
|
||||
byte *bordering;
|
||||
parseMesh_t *meshes[MAX_MAP_DRAW_SURFS];
|
||||
qboolean grouped[MAX_MAP_DRAW_SURFS];
|
||||
byte group[MAX_MAP_DRAW_SURFS];
|
||||
|
||||
qprintf( "----- PatchMapDrawSurfs -----\n" );
|
||||
|
||||
patchCount = 0;
|
||||
for ( pm = e->patches ; pm ; pm = pm->next ) {
|
||||
meshes[patchCount] = pm;
|
||||
patchCount++;
|
||||
}
|
||||
|
||||
if ( !patchCount ) {
|
||||
return;
|
||||
}
|
||||
bordering = malloc( patchCount * patchCount );
|
||||
memset( bordering, 0, patchCount * patchCount );
|
||||
|
||||
// build the bordering matrix
|
||||
for ( k = 0 ; k < patchCount ; k++ ) {
|
||||
bordering[k*patchCount+k] = 1;
|
||||
|
||||
for ( l = k+1 ; l < patchCount ; l++ ) {
|
||||
check = meshes[k];
|
||||
scan = meshes[l];
|
||||
c1 = scan->mesh.width * scan->mesh.height;
|
||||
v1 = scan->mesh.verts;
|
||||
|
||||
for ( i = 0 ; i < c1 ; i++, v1++ ) {
|
||||
c2 = check->mesh.width * check->mesh.height;
|
||||
v2 = check->mesh.verts;
|
||||
for ( j = 0 ; j < c2 ; j++, v2++ ) {
|
||||
if ( fabs( v1->xyz[0] - v2->xyz[0] ) < 1.0
|
||||
&& fabs( v1->xyz[1] - v2->xyz[1] ) < 1.0
|
||||
&& fabs( v1->xyz[2] - v2->xyz[2] ) < 1.0 ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( j != c2 ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( i != c1 ) {
|
||||
// we have a connection
|
||||
bordering[k*patchCount+l] =
|
||||
bordering[l*patchCount+k] = 1;
|
||||
} else {
|
||||
// no connection
|
||||
bordering[k*patchCount+l] =
|
||||
bordering[l*patchCount+k] = 0;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// build groups
|
||||
memset( grouped, 0, sizeof(grouped) );
|
||||
groupCount = 0;
|
||||
for ( i = 0 ; i < patchCount ; i++ ) {
|
||||
if ( !grouped[i] ) {
|
||||
groupCount++;
|
||||
}
|
||||
|
||||
// recursively find all patches that belong in the same group
|
||||
memset( group, 0, patchCount );
|
||||
GrowGroup_r( i, patchCount, bordering, group );
|
||||
|
||||
// bound them
|
||||
ClearBounds( bounds[0], bounds[1] );
|
||||
for ( j = 0 ; j < patchCount ; j++ ) {
|
||||
if ( group[j] ) {
|
||||
grouped[j] = qtrue;
|
||||
scan = meshes[j];
|
||||
c1 = scan->mesh.width * scan->mesh.height;
|
||||
v1 = scan->mesh.verts;
|
||||
for ( k = 0 ; k < c1 ; k++, v1++ ) {
|
||||
AddPointToBounds( v1->xyz, bounds[0], bounds[1] );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// create drawsurf
|
||||
scan = meshes[i];
|
||||
scan->grouped = qtrue;
|
||||
ds = DrawSurfaceForMesh( &scan->mesh );
|
||||
ds->shaderInfo = scan->shaderInfo;
|
||||
VectorCopy( bounds[0], ds->lightmapVecs[0] );
|
||||
VectorCopy( bounds[1], ds->lightmapVecs[1] );
|
||||
}
|
||||
|
||||
qprintf( "%5i patches\n", patchCount );
|
||||
qprintf( "%5i patch LOD groups\n", groupCount );
|
||||
}
|
||||
|
843
q3map/portals.c
Normal file
843
q3map/portals.c
Normal file
|
@ -0,0 +1,843 @@
|
|||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 1999-2005 Id Software, Inc.
|
||||
|
||||
This file is part of Quake III Arena source code.
|
||||
|
||||
Quake III Arena source code 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.
|
||||
|
||||
Quake III Arena source code 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 Foobar; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
#include "qbsp.h"
|
||||
|
||||
|
||||
int c_active_portals;
|
||||
int c_peak_portals;
|
||||
int c_boundary;
|
||||
int c_boundary_sides;
|
||||
|
||||
/*
|
||||
===========
|
||||
AllocPortal
|
||||
===========
|
||||
*/
|
||||
portal_t *AllocPortal (void)
|
||||
{
|
||||
portal_t *p;
|
||||
|
||||
if (numthreads == 1)
|
||||
c_active_portals++;
|
||||
if (c_active_portals > c_peak_portals)
|
||||
c_peak_portals = c_active_portals;
|
||||
|
||||
p = malloc (sizeof(portal_t));
|
||||
memset (p, 0, sizeof(portal_t));
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
void FreePortal (portal_t *p)
|
||||
{
|
||||
if (p->winding)
|
||||
FreeWinding (p->winding);
|
||||
if (numthreads == 1)
|
||||
c_active_portals--;
|
||||
free (p);
|
||||
}
|
||||
|
||||
//==============================================================
|
||||
|
||||
/*
|
||||
=============
|
||||
Portal_Passable
|
||||
|
||||
Returns true if the portal has non-opaque leafs on both sides
|
||||
=============
|
||||
*/
|
||||
qboolean Portal_Passable(portal_t *p) {
|
||||
if (!p->onnode) {
|
||||
return qfalse; // to global outsideleaf
|
||||
}
|
||||
|
||||
if (p->nodes[0]->planenum != PLANENUM_LEAF
|
||||
|| p->nodes[1]->planenum != PLANENUM_LEAF) {
|
||||
Error ("Portal_EntityFlood: not a leaf");
|
||||
}
|
||||
|
||||
if ( !p->nodes[0]->opaque && !p->nodes[1]->opaque ) {
|
||||
return qtrue;
|
||||
}
|
||||
|
||||
return qfalse;
|
||||
}
|
||||
|
||||
|
||||
//=============================================================================
|
||||
|
||||
int c_tinyportals;
|
||||
|
||||
/*
|
||||
=============
|
||||
AddPortalToNodes
|
||||
=============
|
||||
*/
|
||||
void AddPortalToNodes (portal_t *p, node_t *front, node_t *back)
|
||||
{
|
||||
if (p->nodes[0] || p->nodes[1])
|
||||
Error ("AddPortalToNode: allready included");
|
||||
|
||||
p->nodes[0] = front;
|
||||
p->next[0] = front->portals;
|
||||
front->portals = p;
|
||||
|
||||
p->nodes[1] = back;
|
||||
p->next[1] = back->portals;
|
||||
back->portals = p;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=============
|
||||
RemovePortalFromNode
|
||||
=============
|
||||
*/
|
||||
void RemovePortalFromNode (portal_t *portal, node_t *l)
|
||||
{
|
||||
portal_t **pp, *t;
|
||||
|
||||
// remove reference to the current portal
|
||||
pp = &l->portals;
|
||||
while (1)
|
||||
{
|
||||
t = *pp;
|
||||
if (!t)
|
||||
Error ("RemovePortalFromNode: portal not in leaf");
|
||||
|
||||
if ( t == portal )
|
||||
break;
|
||||
|
||||
if (t->nodes[0] == l)
|
||||
pp = &t->next[0];
|
||||
else if (t->nodes[1] == l)
|
||||
pp = &t->next[1];
|
||||
else
|
||||
Error ("RemovePortalFromNode: portal not bounding leaf");
|
||||
}
|
||||
|
||||
if (portal->nodes[0] == l)
|
||||
{
|
||||
*pp = portal->next[0];
|
||||
portal->nodes[0] = NULL;
|
||||
}
|
||||
else if (portal->nodes[1] == l)
|
||||
{
|
||||
*pp = portal->next[1];
|
||||
portal->nodes[1] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
|
||||
void PrintPortal (portal_t *p)
|
||||
{
|
||||
int i;
|
||||
winding_t *w;
|
||||
|
||||
w = p->winding;
|
||||
for (i=0 ; i<w->numpoints ; i++)
|
||||
_printf ("(%5.0f,%5.0f,%5.0f)\n",w->p[i][0]
|
||||
, w->p[i][1], w->p[i][2]);
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
MakeHeadnodePortals
|
||||
|
||||
The created portals will face the global outside_node
|
||||
================
|
||||
*/
|
||||
#define SIDESPACE 8
|
||||
void MakeHeadnodePortals (tree_t *tree)
|
||||
{
|
||||
vec3_t bounds[2];
|
||||
int i, j, n;
|
||||
portal_t *p, *portals[6];
|
||||
plane_t bplanes[6], *pl;
|
||||
node_t *node;
|
||||
|
||||
node = tree->headnode;
|
||||
|
||||
// pad with some space so there will never be null volume leafs
|
||||
for (i=0 ; i<3 ; i++)
|
||||
{
|
||||
bounds[0][i] = tree->mins[i] - SIDESPACE;
|
||||
bounds[1][i] = tree->maxs[i] + SIDESPACE;
|
||||
if ( bounds[0][i] >= bounds[1][i] ) {
|
||||
Error( "Backwards tree volume" );
|
||||
}
|
||||
}
|
||||
|
||||
tree->outside_node.planenum = PLANENUM_LEAF;
|
||||
tree->outside_node.brushlist = NULL;
|
||||
tree->outside_node.portals = NULL;
|
||||
tree->outside_node.opaque = qfalse;
|
||||
|
||||
for (i=0 ; i<3 ; i++)
|
||||
for (j=0 ; j<2 ; j++)
|
||||
{
|
||||
n = j*3 + i;
|
||||
|
||||
p = AllocPortal ();
|
||||
portals[n] = p;
|
||||
|
||||
pl = &bplanes[n];
|
||||
memset (pl, 0, sizeof(*pl));
|
||||
if (j)
|
||||
{
|
||||
pl->normal[i] = -1;
|
||||
pl->dist = -bounds[j][i];
|
||||
}
|
||||
else
|
||||
{
|
||||
pl->normal[i] = 1;
|
||||
pl->dist = bounds[j][i];
|
||||
}
|
||||
p->plane = *pl;
|
||||
p->winding = BaseWindingForPlane (pl->normal, pl->dist);
|
||||
AddPortalToNodes (p, node, &tree->outside_node);
|
||||
}
|
||||
|
||||
// clip the basewindings by all the other planes
|
||||
for (i=0 ; i<6 ; i++)
|
||||
{
|
||||
for (j=0 ; j<6 ; j++)
|
||||
{
|
||||
if (j == i)
|
||||
continue;
|
||||
ChopWindingInPlace (&portals[i]->winding, bplanes[j].normal, bplanes[j].dist, ON_EPSILON);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//===================================================
|
||||
|
||||
|
||||
/*
|
||||
================
|
||||
BaseWindingForNode
|
||||
================
|
||||
*/
|
||||
#define BASE_WINDING_EPSILON 0.001
|
||||
#define SPLIT_WINDING_EPSILON 0.001
|
||||
|
||||
winding_t *BaseWindingForNode (node_t *node)
|
||||
{
|
||||
winding_t *w;
|
||||
node_t *n;
|
||||
plane_t *plane;
|
||||
vec3_t normal;
|
||||
vec_t dist;
|
||||
|
||||
w = BaseWindingForPlane (mapplanes[node->planenum].normal
|
||||
, mapplanes[node->planenum].dist);
|
||||
|
||||
// clip by all the parents
|
||||
for (n=node->parent ; n && w ; )
|
||||
{
|
||||
plane = &mapplanes[n->planenum];
|
||||
|
||||
if (n->children[0] == node)
|
||||
{ // take front
|
||||
ChopWindingInPlace (&w, plane->normal, plane->dist, BASE_WINDING_EPSILON);
|
||||
}
|
||||
else
|
||||
{ // take back
|
||||
VectorSubtract (vec3_origin, plane->normal, normal);
|
||||
dist = -plane->dist;
|
||||
ChopWindingInPlace (&w, normal, dist, BASE_WINDING_EPSILON);
|
||||
}
|
||||
node = n;
|
||||
n = n->parent;
|
||||
}
|
||||
|
||||
return w;
|
||||
}
|
||||
|
||||
//============================================================
|
||||
|
||||
/*
|
||||
==================
|
||||
MakeNodePortal
|
||||
|
||||
create the new portal by taking the full plane winding for the cutting plane
|
||||
and clipping it by all of parents of this node
|
||||
==================
|
||||
*/
|
||||
void MakeNodePortal (node_t *node)
|
||||
{
|
||||
portal_t *new_portal, *p;
|
||||
winding_t *w;
|
||||
vec3_t normal;
|
||||
float dist;
|
||||
int side;
|
||||
|
||||
w = BaseWindingForNode (node);
|
||||
|
||||
// clip the portal by all the other portals in the node
|
||||
for (p = node->portals ; p && w; p = p->next[side])
|
||||
{
|
||||
if (p->nodes[0] == node)
|
||||
{
|
||||
side = 0;
|
||||
VectorCopy (p->plane.normal, normal);
|
||||
dist = p->plane.dist;
|
||||
}
|
||||
else if (p->nodes[1] == node)
|
||||
{
|
||||
side = 1;
|
||||
VectorSubtract (vec3_origin, p->plane.normal, normal);
|
||||
dist = -p->plane.dist;
|
||||
}
|
||||
else
|
||||
Error ("CutNodePortals_r: mislinked portal");
|
||||
|
||||
ChopWindingInPlace (&w, normal, dist, CLIP_EPSILON);
|
||||
}
|
||||
|
||||
if (!w)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (WindingIsTiny (w))
|
||||
{
|
||||
c_tinyportals++;
|
||||
FreeWinding (w);
|
||||
return;
|
||||
}
|
||||
|
||||
new_portal = AllocPortal ();
|
||||
new_portal->plane = mapplanes[node->planenum];
|
||||
new_portal->onnode = node;
|
||||
new_portal->winding = w;
|
||||
new_portal->hint = node->hint;
|
||||
AddPortalToNodes (new_portal, node->children[0], node->children[1]);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
==============
|
||||
SplitNodePortals
|
||||
|
||||
Move or split the portals that bound node so that the node's
|
||||
children have portals instead of node.
|
||||
==============
|
||||
*/
|
||||
void SplitNodePortals (node_t *node)
|
||||
{
|
||||
portal_t *p, *next_portal, *new_portal;
|
||||
node_t *f, *b, *other_node;
|
||||
int side;
|
||||
plane_t *plane;
|
||||
winding_t *frontwinding, *backwinding;
|
||||
|
||||
plane = &mapplanes[node->planenum];
|
||||
f = node->children[0];
|
||||
b = node->children[1];
|
||||
|
||||
for (p = node->portals ; p ; p = next_portal)
|
||||
{
|
||||
if (p->nodes[0] == node)
|
||||
side = 0;
|
||||
else if (p->nodes[1] == node)
|
||||
side = 1;
|
||||
else
|
||||
Error ("SplitNodePortals: mislinked portal");
|
||||
next_portal = p->next[side];
|
||||
|
||||
other_node = p->nodes[!side];
|
||||
RemovePortalFromNode (p, p->nodes[0]);
|
||||
RemovePortalFromNode (p, p->nodes[1]);
|
||||
|
||||
//
|
||||
// cut the portal into two portals, one on each side of the cut plane
|
||||
//
|
||||
ClipWindingEpsilon (p->winding, plane->normal, plane->dist,
|
||||
SPLIT_WINDING_EPSILON, &frontwinding, &backwinding);
|
||||
|
||||
if (frontwinding && WindingIsTiny(frontwinding))
|
||||
{
|
||||
if (!f->tinyportals)
|
||||
VectorCopy(frontwinding->p[0], f->referencepoint);
|
||||
f->tinyportals++;
|
||||
if (!other_node->tinyportals)
|
||||
VectorCopy(frontwinding->p[0], other_node->referencepoint);
|
||||
other_node->tinyportals++;
|
||||
|
||||
FreeWinding (frontwinding);
|
||||
frontwinding = NULL;
|
||||
c_tinyportals++;
|
||||
}
|
||||
|
||||
if (backwinding && WindingIsTiny(backwinding))
|
||||
{
|
||||
if (!b->tinyportals)
|
||||
VectorCopy(backwinding->p[0], b->referencepoint);
|
||||
b->tinyportals++;
|
||||
if (!other_node->tinyportals)
|
||||
VectorCopy(backwinding->p[0], other_node->referencepoint);
|
||||
other_node->tinyportals++;
|
||||
|
||||
FreeWinding (backwinding);
|
||||
backwinding = NULL;
|
||||
c_tinyportals++;
|
||||
}
|
||||
|
||||
if (!frontwinding && !backwinding)
|
||||
{ // tiny windings on both sides
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!frontwinding)
|
||||
{
|
||||
FreeWinding (backwinding);
|
||||
if (side == 0)
|
||||
AddPortalToNodes (p, b, other_node);
|
||||
else
|
||||
AddPortalToNodes (p, other_node, b);
|
||||
continue;
|
||||
}
|
||||
if (!backwinding)
|
||||
{
|
||||
FreeWinding (frontwinding);
|
||||
if (side == 0)
|
||||
AddPortalToNodes (p, f, other_node);
|
||||
else
|
||||
AddPortalToNodes (p, other_node, f);
|
||||
continue;
|
||||
}
|
||||
|
||||
// the winding is split
|
||||
new_portal = AllocPortal ();
|
||||
*new_portal = *p;
|
||||
new_portal->winding = backwinding;
|
||||
FreeWinding (p->winding);
|
||||
p->winding = frontwinding;
|
||||
|
||||
if (side == 0)
|
||||
{
|
||||
AddPortalToNodes (p, f, other_node);
|
||||
AddPortalToNodes (new_portal, b, other_node);
|
||||
}
|
||||
else
|
||||
{
|
||||
AddPortalToNodes (p, other_node, f);
|
||||
AddPortalToNodes (new_portal, other_node, b);
|
||||
}
|
||||
}
|
||||
|
||||
node->portals = NULL;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
================
|
||||
CalcNodeBounds
|
||||
================
|
||||
*/
|
||||
void CalcNodeBounds (node_t *node)
|
||||
{
|
||||
portal_t *p;
|
||||
int s;
|
||||
int i;
|
||||
|
||||
// calc mins/maxs for both leafs and nodes
|
||||
ClearBounds (node->mins, node->maxs);
|
||||
for (p = node->portals ; p ; p = p->next[s])
|
||||
{
|
||||
s = (p->nodes[1] == node);
|
||||
for (i=0 ; i<p->winding->numpoints ; i++)
|
||||
AddPointToBounds (p->winding->p[i], node->mins, node->maxs);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
MakeTreePortals_r
|
||||
==================
|
||||
*/
|
||||
void MakeTreePortals_r (node_t *node)
|
||||
{
|
||||
int i;
|
||||
|
||||
CalcNodeBounds (node);
|
||||
if (node->mins[0] >= node->maxs[0])
|
||||
{
|
||||
_printf ("WARNING: node without a volume\n");
|
||||
_printf("node has %d tiny portals\n", node->tinyportals);
|
||||
_printf("node reference point %1.2f %1.2f %1.2f\n", node->referencepoint[0],
|
||||
node->referencepoint[1],
|
||||
node->referencepoint[2]);
|
||||
}
|
||||
|
||||
for (i=0 ; i<3 ; i++)
|
||||
{
|
||||
if (node->mins[i] < MIN_WORLD_COORD || node->maxs[i] > MAX_WORLD_COORD)
|
||||
{
|
||||
_printf ("WARNING: node with unbounded volume\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (node->planenum == PLANENUM_LEAF)
|
||||
return;
|
||||
|
||||
MakeNodePortal (node);
|
||||
SplitNodePortals (node);
|
||||
|
||||
MakeTreePortals_r (node->children[0]);
|
||||
MakeTreePortals_r (node->children[1]);
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
MakeTreePortals
|
||||
==================
|
||||
*/
|
||||
void MakeTreePortals (tree_t *tree)
|
||||
{
|
||||
qprintf( "----- MakeTreePortals -----\n");
|
||||
MakeHeadnodePortals (tree);
|
||||
MakeTreePortals_r (tree->headnode);
|
||||
qprintf("%6d tiny portals\n", c_tinyportals);
|
||||
}
|
||||
|
||||
/*
|
||||
=========================================================
|
||||
|
||||
FLOOD ENTITIES
|
||||
|
||||
=========================================================
|
||||
*/
|
||||
|
||||
int c_floodedleafs;
|
||||
|
||||
/*
|
||||
=============
|
||||
FloodPortals_r
|
||||
=============
|
||||
*/
|
||||
void FloodPortals_r (node_t *node, int dist) {
|
||||
portal_t *p;
|
||||
int s;
|
||||
|
||||
if ( node->occupied ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( node->opaque ) {
|
||||
return;
|
||||
}
|
||||
|
||||
c_floodedleafs++;
|
||||
node->occupied = dist;
|
||||
|
||||
for (p=node->portals ; p ; p = p->next[s]) {
|
||||
s = (p->nodes[1] == node);
|
||||
FloodPortals_r (p->nodes[!s], dist+1);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
PlaceOccupant
|
||||
=============
|
||||
*/
|
||||
qboolean PlaceOccupant (node_t *headnode, vec3_t origin, entity_t *occupant)
|
||||
{
|
||||
node_t *node;
|
||||
vec_t d;
|
||||
plane_t *plane;
|
||||
|
||||
// find the leaf to start in
|
||||
node = headnode;
|
||||
while (node->planenum != PLANENUM_LEAF)
|
||||
{
|
||||
plane = &mapplanes[node->planenum];
|
||||
d = DotProduct (origin, plane->normal) - plane->dist;
|
||||
if (d >= 0)
|
||||
node = node->children[0];
|
||||
else
|
||||
node = node->children[1];
|
||||
}
|
||||
|
||||
if ( node->opaque )
|
||||
return qfalse;
|
||||
node->occupant = occupant;
|
||||
|
||||
FloodPortals_r (node, 1);
|
||||
|
||||
return qtrue;
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
FloodEntities
|
||||
|
||||
Marks all nodes that can be reached by entites
|
||||
=============
|
||||
*/
|
||||
qboolean FloodEntities( tree_t *tree ) {
|
||||
int i;
|
||||
vec3_t origin;
|
||||
const char *cl;
|
||||
qboolean inside;
|
||||
node_t *headnode;
|
||||
|
||||
headnode = tree->headnode;
|
||||
qprintf ("--- FloodEntities ---\n");
|
||||
inside = qfalse;
|
||||
tree->outside_node.occupied = 0;
|
||||
|
||||
c_floodedleafs = 0;
|
||||
for (i=1 ; i<num_entities ; i++)
|
||||
{
|
||||
GetVectorForKey (&entities[i], "origin", origin);
|
||||
if (VectorCompare(origin, vec3_origin))
|
||||
continue;
|
||||
|
||||
cl = ValueForKey (&entities[i], "classname");
|
||||
|
||||
origin[2] += 1; // so objects on floor are ok
|
||||
|
||||
if (PlaceOccupant (headnode, origin, &entities[i]))
|
||||
inside = qtrue;
|
||||
}
|
||||
|
||||
qprintf("%5i flooded leafs\n", c_floodedleafs );
|
||||
|
||||
if (!inside)
|
||||
{
|
||||
qprintf ("no entities in open -- no filling\n");
|
||||
}
|
||||
else if (tree->outside_node.occupied)
|
||||
{
|
||||
qprintf ("entity reached from outside -- no filling\n");
|
||||
}
|
||||
|
||||
return (qboolean)(inside && !tree->outside_node.occupied);
|
||||
}
|
||||
|
||||
/*
|
||||
=========================================================
|
||||
|
||||
FLOOD AREAS
|
||||
|
||||
=========================================================
|
||||
*/
|
||||
|
||||
int c_areas;
|
||||
|
||||
/*
|
||||
=============
|
||||
FloodAreas_r
|
||||
=============
|
||||
*/
|
||||
void FloodAreas_r (node_t *node)
|
||||
{
|
||||
portal_t *p;
|
||||
int s;
|
||||
bspbrush_t *b;
|
||||
|
||||
if ( node->areaportal ) {
|
||||
//
|
||||
if ( node->area == -1 ) {
|
||||
node->area = c_areas;
|
||||
}
|
||||
// this node is part of an area portal brush
|
||||
b = node->brushlist->original;
|
||||
|
||||
// if the current area has allready touched this
|
||||
// portal, we are done
|
||||
if (b->portalareas[0] == c_areas || b->portalareas[1] == c_areas)
|
||||
return;
|
||||
|
||||
// note the current area as bounding the portal
|
||||
if (b->portalareas[1] != -1)
|
||||
{
|
||||
_printf ("WARNING: areaportal brush %i touches > 2 areas\n", b->brushnum );
|
||||
return;
|
||||
}
|
||||
if (b->portalareas[0] != -1) {
|
||||
b->portalareas[1] = c_areas;
|
||||
} else {
|
||||
b->portalareas[0] = c_areas;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (node->area != -1) {
|
||||
return; // allready got it
|
||||
}
|
||||
if ( node->cluster == -1 ) {
|
||||
return;
|
||||
}
|
||||
|
||||
node->area = c_areas;
|
||||
|
||||
for (p=node->portals ; p ; p = p->next[s])
|
||||
{
|
||||
s = (p->nodes[1] == node);
|
||||
|
||||
if ( !Portal_Passable(p) )
|
||||
continue;
|
||||
|
||||
FloodAreas_r (p->nodes[!s]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=============
|
||||
FindAreas_r
|
||||
|
||||
Just decend the tree, and for each node that hasn't had an
|
||||
area set, flood fill out from there
|
||||
=============
|
||||
*/
|
||||
void FindAreas_r (node_t *node)
|
||||
{
|
||||
if (node->planenum != PLANENUM_LEAF)
|
||||
{
|
||||
FindAreas_r (node->children[0]);
|
||||
FindAreas_r (node->children[1]);
|
||||
return;
|
||||
}
|
||||
|
||||
if (node->opaque)
|
||||
return;
|
||||
|
||||
if (node->areaportal)
|
||||
return;
|
||||
|
||||
if (node->area != -1)
|
||||
return; // allready got it
|
||||
|
||||
FloodAreas_r (node);
|
||||
c_areas++;
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
CheckAreas_r
|
||||
=============
|
||||
*/
|
||||
void CheckAreas_r (node_t *node)
|
||||
{
|
||||
bspbrush_t *b;
|
||||
|
||||
if (node->planenum != PLANENUM_LEAF)
|
||||
{
|
||||
CheckAreas_r (node->children[0]);
|
||||
CheckAreas_r (node->children[1]);
|
||||
return;
|
||||
}
|
||||
|
||||
if (node->opaque)
|
||||
return;
|
||||
|
||||
if (node->cluster != -1)
|
||||
if (node->area == -1)
|
||||
_printf("WARNING: cluster %d has area set to -1\n", node->cluster);
|
||||
if (node->areaportal)
|
||||
{
|
||||
b = node->brushlist->original;
|
||||
|
||||
// check if the areaportal touches two areas
|
||||
if (b->portalareas[0] == -1 || b->portalareas[1] == -1)
|
||||
_printf ("WARNING: areaportal brush %i doesn't touch two areas\n", b->brushnum);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
FloodAreas
|
||||
|
||||
Mark each leaf with an area, bounded by CONTENTS_AREAPORTAL
|
||||
=============
|
||||
*/
|
||||
void FloodAreas (tree_t *tree)
|
||||
{
|
||||
qprintf ("--- FloodAreas ---\n");
|
||||
FindAreas_r( tree->headnode );
|
||||
|
||||
// check for areaportal brushes that don't touch two areas
|
||||
CheckAreas_r( tree->headnode );
|
||||
|
||||
qprintf ("%5i areas\n", c_areas);
|
||||
}
|
||||
|
||||
//======================================================
|
||||
|
||||
int c_outside;
|
||||
int c_inside;
|
||||
int c_solid;
|
||||
|
||||
void FillOutside_r (node_t *node)
|
||||
{
|
||||
if (node->planenum != PLANENUM_LEAF)
|
||||
{
|
||||
FillOutside_r (node->children[0]);
|
||||
FillOutside_r (node->children[1]);
|
||||
return;
|
||||
}
|
||||
|
||||
// anything not reachable by an entity
|
||||
// can be filled away
|
||||
if (!node->occupied) {
|
||||
if ( !node->opaque ) {
|
||||
c_outside++;
|
||||
node->opaque = qtrue;
|
||||
} else {
|
||||
c_solid++;
|
||||
}
|
||||
} else {
|
||||
c_inside++;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
FillOutside
|
||||
|
||||
Fill all nodes that can't be reached by entities
|
||||
=============
|
||||
*/
|
||||
void FillOutside (node_t *headnode)
|
||||
{
|
||||
c_outside = 0;
|
||||
c_inside = 0;
|
||||
c_solid = 0;
|
||||
qprintf ("--- FillOutside ---\n");
|
||||
FillOutside_r (headnode);
|
||||
qprintf ("%5i solid leafs\n", c_solid);
|
||||
qprintf ("%5i leafs filled\n", c_outside);
|
||||
qprintf ("%5i inside leafs\n", c_inside);
|
||||
}
|
||||
|
||||
|
||||
//==============================================================
|
||||
|
272
q3map/prtfile.c
Normal file
272
q3map/prtfile.c
Normal file
|
@ -0,0 +1,272 @@
|
|||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 1999-2005 Id Software, Inc.
|
||||
|
||||
This file is part of Quake III Arena source code.
|
||||
|
||||
Quake III Arena source code 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.
|
||||
|
||||
Quake III Arena source code 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 Foobar; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
#include "qbsp.h"
|
||||
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
PORTAL FILE GENERATION
|
||||
|
||||
Save out name.prt for qvis to read
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
|
||||
#define PORTALFILE "PRT1"
|
||||
|
||||
FILE *pf;
|
||||
int num_visclusters; // clusters the player can be in
|
||||
int num_visportals;
|
||||
int num_solidfaces;
|
||||
|
||||
void WriteFloat (FILE *f, vec_t v)
|
||||
{
|
||||
if ( fabs(v - Q_rint(v)) < 0.001 )
|
||||
fprintf (f,"%i ",(int)Q_rint(v));
|
||||
else
|
||||
fprintf (f,"%f ",v);
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
WritePortalFile_r
|
||||
=================
|
||||
*/
|
||||
void WritePortalFile_r (node_t *node)
|
||||
{
|
||||
int i, s;
|
||||
portal_t *p;
|
||||
winding_t *w;
|
||||
vec3_t normal;
|
||||
vec_t dist;
|
||||
|
||||
// decision node
|
||||
if (node->planenum != PLANENUM_LEAF) {
|
||||
WritePortalFile_r (node->children[0]);
|
||||
WritePortalFile_r (node->children[1]);
|
||||
return;
|
||||
}
|
||||
|
||||
if (node->opaque) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (p = node->portals ; p ; p=p->next[s])
|
||||
{
|
||||
w = p->winding;
|
||||
s = (p->nodes[1] == node);
|
||||
if (w && p->nodes[0] == node)
|
||||
{
|
||||
if (!Portal_Passable(p))
|
||||
continue;
|
||||
// write out to the file
|
||||
|
||||
// sometimes planes get turned around when they are very near
|
||||
// the changeover point between different axis. interpret the
|
||||
// plane the same way vis will, and flip the side orders if needed
|
||||
// FIXME: is this still relevent?
|
||||
WindingPlane (w, normal, &dist);
|
||||
if ( DotProduct (p->plane.normal, normal) < 0.99 )
|
||||
{ // backwards...
|
||||
fprintf (pf,"%i %i %i ",w->numpoints, p->nodes[1]->cluster, p->nodes[0]->cluster);
|
||||
}
|
||||
else
|
||||
fprintf (pf,"%i %i %i ",w->numpoints, p->nodes[0]->cluster, p->nodes[1]->cluster);
|
||||
if (p->hint)
|
||||
fprintf (pf, "1 ");
|
||||
else
|
||||
fprintf (pf, "0 ");
|
||||
for (i=0 ; i<w->numpoints ; i++)
|
||||
{
|
||||
fprintf (pf,"(");
|
||||
WriteFloat (pf, w->p[i][0]);
|
||||
WriteFloat (pf, w->p[i][1]);
|
||||
WriteFloat (pf, w->p[i][2]);
|
||||
fprintf (pf,") ");
|
||||
}
|
||||
fprintf (pf,"\n");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
WriteFaceFile_r
|
||||
=================
|
||||
*/
|
||||
void WriteFaceFile_r (node_t *node)
|
||||
{
|
||||
int i, s;
|
||||
portal_t *p;
|
||||
winding_t *w;
|
||||
|
||||
// decision node
|
||||
if (node->planenum != PLANENUM_LEAF) {
|
||||
WriteFaceFile_r (node->children[0]);
|
||||
WriteFaceFile_r (node->children[1]);
|
||||
return;
|
||||
}
|
||||
|
||||
if (node->opaque) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (p = node->portals ; p ; p=p->next[s])
|
||||
{
|
||||
w = p->winding;
|
||||
s = (p->nodes[1] == node);
|
||||
if (w)
|
||||
{
|
||||
if (Portal_Passable(p))
|
||||
continue;
|
||||
// write out to the file
|
||||
|
||||
if (p->nodes[0] == node)
|
||||
{
|
||||
fprintf (pf,"%i %i ",w->numpoints, p->nodes[0]->cluster);
|
||||
for (i=0 ; i<w->numpoints ; i++)
|
||||
{
|
||||
fprintf (pf,"(");
|
||||
WriteFloat (pf, w->p[i][0]);
|
||||
WriteFloat (pf, w->p[i][1]);
|
||||
WriteFloat (pf, w->p[i][2]);
|
||||
fprintf (pf,") ");
|
||||
}
|
||||
fprintf (pf,"\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf (pf,"%i %i ",w->numpoints, p->nodes[1]->cluster);
|
||||
for (i = w->numpoints-1; i >= 0; i--)
|
||||
{
|
||||
fprintf (pf,"(");
|
||||
WriteFloat (pf, w->p[i][0]);
|
||||
WriteFloat (pf, w->p[i][1]);
|
||||
WriteFloat (pf, w->p[i][2]);
|
||||
fprintf (pf,") ");
|
||||
}
|
||||
fprintf (pf,"\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
NumberLeafs_r
|
||||
================
|
||||
*/
|
||||
void NumberLeafs_r (node_t *node)
|
||||
{
|
||||
portal_t *p;
|
||||
|
||||
if ( node->planenum != PLANENUM_LEAF ) {
|
||||
// decision node
|
||||
node->cluster = -99;
|
||||
NumberLeafs_r (node->children[0]);
|
||||
NumberLeafs_r (node->children[1]);
|
||||
return;
|
||||
}
|
||||
|
||||
node->area = -1;
|
||||
|
||||
if ( node->opaque ) {
|
||||
// solid block, viewpoint never inside
|
||||
node->cluster = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
node->cluster = num_visclusters;
|
||||
num_visclusters++;
|
||||
|
||||
// count the portals
|
||||
for (p = node->portals ; p ; )
|
||||
{
|
||||
if (p->nodes[0] == node) // only write out from first leaf
|
||||
{
|
||||
if (Portal_Passable(p))
|
||||
num_visportals++;
|
||||
else
|
||||
num_solidfaces++;
|
||||
p = p->next[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!Portal_Passable(p))
|
||||
num_solidfaces++;
|
||||
p = p->next[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
================
|
||||
NumberClusters
|
||||
================
|
||||
*/
|
||||
void NumberClusters(tree_t *tree) {
|
||||
num_visclusters = 0;
|
||||
num_visportals = 0;
|
||||
num_solidfaces = 0;
|
||||
|
||||
qprintf ("--- NumberClusters ---\n");
|
||||
|
||||
// set the cluster field in every leaf and count the total number of portals
|
||||
NumberLeafs_r (tree->headnode);
|
||||
|
||||
qprintf ("%5i visclusters\n", num_visclusters);
|
||||
qprintf ("%5i visportals\n", num_visportals);
|
||||
qprintf ("%5i solidfaces\n", num_solidfaces);
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
WritePortalFile
|
||||
================
|
||||
*/
|
||||
void WritePortalFile (tree_t *tree)
|
||||
{
|
||||
char filename[1024];
|
||||
|
||||
qprintf ("--- WritePortalFile ---\n");
|
||||
|
||||
// write the file
|
||||
sprintf (filename, "%s.prt", source);
|
||||
_printf ("writing %s\n", filename);
|
||||
pf = fopen (filename, "w");
|
||||
if (!pf)
|
||||
Error ("Error opening %s", filename);
|
||||
|
||||
fprintf (pf, "%s\n", PORTALFILE);
|
||||
fprintf (pf, "%i\n", num_visclusters);
|
||||
fprintf (pf, "%i\n", num_visportals);
|
||||
fprintf (pf, "%i\n", num_solidfaces);
|
||||
|
||||
WritePortalFile_r(tree->headnode);
|
||||
WriteFaceFile_r(tree->headnode);
|
||||
|
||||
fclose (pf);
|
||||
}
|
||||
|
56
q3map/q3map.sln
Normal file
56
q3map/q3map.sln
Normal file
|
@ -0,0 +1,56 @@
|
|||
Microsoft Visual Studio Solution File, Format Version 8.00
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "jpeg6", "..\libs\jpeg6\jpeg6.vcproj", "{A862AD26-94DD-4618-A814-F6AACA0B2FE3}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pak", "..\libs\pak\pak.vcproj", "{F2ACC9D7-D628-4624-864F-87FE58787625}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "q3map", "q3map.vcproj", "{F1162C55-66E7-4486-B1F3-071CFAA78332}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{F2ACC9D7-D628-4624-864F-87FE58787625} = {F2ACC9D7-D628-4624-864F-87FE58787625}
|
||||
{A862AD26-94DD-4618-A814-F6AACA0B2FE3} = {A862AD26-94DD-4618-A814-F6AACA0B2FE3}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SourceCodeControl) = preSolution
|
||||
SccNumberOfProjects = 3
|
||||
SccProjectUniqueName0 = ..\\libs\\jpeg6\\jpeg6.vcproj
|
||||
SccProjectName0 = \u0022$/source/q3radiant\u0022,\u0020FEFAAAAA
|
||||
SccLocalPath0 = ..\\q3radiant
|
||||
SccProvider0 = MSSCCI:Perforce\u0020SCM
|
||||
SccProjectFilePathRelativizedFromConnection0 = ..\\libs\\jpeg6\\
|
||||
SccProjectUniqueName1 = ..\\libs\\pak\\pak.vcproj
|
||||
SccProjectName1 = \u0022$/source/q3radiant\u0022,\u0020FEFAAAAA
|
||||
SccLocalPath1 = ..\\q3radiant
|
||||
SccProvider1 = MSSCCI:Perforce\u0020SCM
|
||||
SccProjectFilePathRelativizedFromConnection1 = ..\\libs\\pak\\
|
||||
SccProjectUniqueName2 = q3map.vcproj
|
||||
SccProjectName2 = \u0022$/source/q3map\u0022,\u0020PADAAAAA
|
||||
SccLocalPath2 = .
|
||||
SccProvider2 = MSSCCI:Perforce\u0020SCM
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionConfiguration) = preSolution
|
||||
Debug = Debug
|
||||
Release = Release
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfiguration) = postSolution
|
||||
{A862AD26-94DD-4618-A814-F6AACA0B2FE3}.Debug.ActiveCfg = Debug|Win32
|
||||
{A862AD26-94DD-4618-A814-F6AACA0B2FE3}.Debug.Build.0 = Debug|Win32
|
||||
{A862AD26-94DD-4618-A814-F6AACA0B2FE3}.Release.ActiveCfg = Release|Win32
|
||||
{A862AD26-94DD-4618-A814-F6AACA0B2FE3}.Release.Build.0 = Release|Win32
|
||||
{F2ACC9D7-D628-4624-864F-87FE58787625}.Debug.ActiveCfg = Debug|Win32
|
||||
{F2ACC9D7-D628-4624-864F-87FE58787625}.Debug.Build.0 = Debug|Win32
|
||||
{F2ACC9D7-D628-4624-864F-87FE58787625}.Release.ActiveCfg = Release|Win32
|
||||
{F2ACC9D7-D628-4624-864F-87FE58787625}.Release.Build.0 = Release|Win32
|
||||
{F1162C55-66E7-4486-B1F3-071CFAA78332}.Debug.ActiveCfg = Debug|Win32
|
||||
{F1162C55-66E7-4486-B1F3-071CFAA78332}.Debug.Build.0 = Debug|Win32
|
||||
{F1162C55-66E7-4486-B1F3-071CFAA78332}.Release.ActiveCfg = Release|Win32
|
||||
{F1162C55-66E7-4486-B1F3-071CFAA78332}.Release.Build.0 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityAddIns) = postSolution
|
||||
EndGlobalSection
|
||||
EndGlobal
|
1606
q3map/q3map.vcproj
Normal file
1606
q3map/q3map.vcproj
Normal file
File diff suppressed because it is too large
Load diff
455
q3map/qbsp.h
Normal file
455
q3map/qbsp.h
Normal file
|
@ -0,0 +1,455 @@
|
|||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 1999-2005 Id Software, Inc.
|
||||
|
||||
This file is part of Quake III Arena source code.
|
||||
|
||||
Quake III Arena source code 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.
|
||||
|
||||
Quake III Arena source code 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 Foobar; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
#include "cmdlib.h"
|
||||
#include "mathlib.h"
|
||||
#include "scriplib.h"
|
||||
#include "polylib.h"
|
||||
#include "imagelib.h"
|
||||
#include "threads.h"
|
||||
#include "bspfile.h"
|
||||
#include "shaders.h"
|
||||
#include "mesh.h"
|
||||
|
||||
|
||||
#define MAX_PATCH_SIZE 32
|
||||
|
||||
#define CLIP_EPSILON 0.1
|
||||
#define PLANENUM_LEAF -1
|
||||
|
||||
#define HINT_PRIORITY 1000
|
||||
|
||||
typedef struct parseMesh_s {
|
||||
struct parseMesh_s *next;
|
||||
mesh_t mesh;
|
||||
shaderInfo_t *shaderInfo;
|
||||
|
||||
qboolean grouped; // used during shared edge grouping
|
||||
struct parseMesh_s *groupChain;
|
||||
} parseMesh_t;
|
||||
|
||||
typedef struct bspface_s {
|
||||
struct bspface_s *next;
|
||||
int planenum;
|
||||
int priority; // added to value calculation
|
||||
qboolean checked;
|
||||
qboolean hint;
|
||||
winding_t *w;
|
||||
} bspface_t;
|
||||
|
||||
typedef struct plane_s {
|
||||
vec3_t normal;
|
||||
vec_t dist;
|
||||
int type;
|
||||
struct plane_s *hash_chain;
|
||||
} plane_t;
|
||||
|
||||
typedef struct side_s {
|
||||
int planenum;
|
||||
|
||||
float texMat[2][3]; // brush primitive texture matrix
|
||||
// for old brush coordinates mode
|
||||
float vecs[2][4]; // texture coordinate mapping
|
||||
|
||||
winding_t *winding;
|
||||
winding_t *visibleHull; // convex hull of all visible fragments
|
||||
|
||||
struct shaderInfo_s *shaderInfo;
|
||||
|
||||
int contents; // from shaderInfo
|
||||
int surfaceFlags; // from shaderInfo
|
||||
int value; // from shaderInfo
|
||||
|
||||
qboolean visible; // choose visble planes first
|
||||
qboolean bevel; // don't ever use for bsp splitting, and don't bother
|
||||
// making windings for it
|
||||
qboolean backSide; // generated side for a q3map_backShader
|
||||
} side_t;
|
||||
|
||||
|
||||
#define MAX_BRUSH_SIDES 1024
|
||||
|
||||
typedef struct bspbrush_s {
|
||||
struct bspbrush_s *next;
|
||||
|
||||
int entitynum; // editor numbering
|
||||
int brushnum; // editor numbering
|
||||
|
||||
struct shaderInfo_s *contentShader;
|
||||
|
||||
int contents;
|
||||
qboolean detail;
|
||||
qboolean opaque;
|
||||
int outputNumber; // set when the brush is written to the file list
|
||||
|
||||
int portalareas[2];
|
||||
|
||||
struct bspbrush_s *original; // chopped up brushes will reference the originals
|
||||
|
||||
vec3_t mins, maxs;
|
||||
int numsides;
|
||||
side_t sides[6]; // variably sized
|
||||
} bspbrush_t;
|
||||
|
||||
|
||||
|
||||
typedef struct drawsurf_s {
|
||||
shaderInfo_t *shaderInfo;
|
||||
|
||||
bspbrush_t *mapBrush; // not valid for patches
|
||||
side_t *side; // not valid for patches
|
||||
|
||||
struct drawsurf_s *nextOnShader; // when sorting by shader for lightmaps
|
||||
|
||||
int fogNum; // set by FogDrawSurfs
|
||||
|
||||
int lightmapNum; // -1 = no lightmap
|
||||
int lightmapX, lightmapY;
|
||||
int lightmapWidth, lightmapHeight;
|
||||
|
||||
int numVerts;
|
||||
drawVert_t *verts;
|
||||
|
||||
int numIndexes;
|
||||
int *indexes;
|
||||
|
||||
// for faces only
|
||||
int planeNum;
|
||||
|
||||
vec3_t lightmapOrigin; // also used for flares
|
||||
vec3_t lightmapVecs[3]; // also used for flares
|
||||
|
||||
// for patches only
|
||||
qboolean patch;
|
||||
int patchWidth;
|
||||
int patchHeight;
|
||||
|
||||
// for misc_models only
|
||||
qboolean miscModel;
|
||||
|
||||
qboolean flareSurface;
|
||||
} mapDrawSurface_t;
|
||||
|
||||
typedef struct drawSurfRef_s {
|
||||
struct drawSurfRef_s *nextRef;
|
||||
int outputNumber;
|
||||
} drawSurfRef_t;
|
||||
|
||||
typedef struct node_s {
|
||||
// both leafs and nodes
|
||||
int planenum; // -1 = leaf node
|
||||
struct node_s *parent;
|
||||
vec3_t mins, maxs; // valid after portalization
|
||||
bspbrush_t *volume; // one for each leaf/node
|
||||
|
||||
// nodes only
|
||||
side_t *side; // the side that created the node
|
||||
struct node_s *children[2];
|
||||
qboolean hint;
|
||||
int tinyportals;
|
||||
vec3_t referencepoint;
|
||||
|
||||
// leafs only
|
||||
qboolean opaque; // view can never be inside
|
||||
qboolean areaportal;
|
||||
int cluster; // for portalfile writing
|
||||
int area; // for areaportals
|
||||
bspbrush_t *brushlist; // fragments of all brushes in this leaf
|
||||
drawSurfRef_t *drawSurfReferences; // references to patches pushed down
|
||||
|
||||
int occupied; // 1 or greater can reach entity
|
||||
entity_t *occupant; // for leak file testing
|
||||
|
||||
struct portal_s *portals; // also on nodes during construction
|
||||
} node_t;
|
||||
|
||||
typedef struct portal_s {
|
||||
plane_t plane;
|
||||
node_t *onnode; // NULL = outside box
|
||||
node_t *nodes[2]; // [0] = front side of plane
|
||||
struct portal_s *next[2];
|
||||
winding_t *winding;
|
||||
|
||||
qboolean sidefound; // false if ->side hasn't been checked
|
||||
qboolean hint;
|
||||
side_t *side; // NULL = non-visible
|
||||
} portal_t;
|
||||
|
||||
typedef struct {
|
||||
node_t *headnode;
|
||||
node_t outside_node;
|
||||
vec3_t mins, maxs;
|
||||
} tree_t;
|
||||
|
||||
extern int entity_num;
|
||||
|
||||
|
||||
extern qboolean noprune;
|
||||
extern qboolean nodetail;
|
||||
extern qboolean fulldetail;
|
||||
extern qboolean nowater;
|
||||
extern qboolean noCurveBrushes;
|
||||
extern qboolean fakemap;
|
||||
extern qboolean coplanar;
|
||||
extern qboolean nofog;
|
||||
extern qboolean testExpand;
|
||||
extern qboolean showseams;
|
||||
|
||||
extern vec_t microvolume;
|
||||
|
||||
extern char outbase[32];
|
||||
extern char source[1024];
|
||||
|
||||
extern int samplesize; //sample size in units
|
||||
extern int novertexlighting;
|
||||
extern int nogridlighting;
|
||||
|
||||
//=============================================================================
|
||||
|
||||
// brush.c
|
||||
|
||||
int CountBrushList (bspbrush_t *brushes);
|
||||
bspbrush_t *AllocBrush (int numsides);
|
||||
void FreeBrush (bspbrush_t *brushes);
|
||||
void FreeBrushList (bspbrush_t *brushes);
|
||||
bspbrush_t *CopyBrush (bspbrush_t *brush);
|
||||
void DrawBrushList (bspbrush_t *brush);
|
||||
void WriteBrushList (char *name, bspbrush_t *brush, qboolean onlyvis);
|
||||
void PrintBrush (bspbrush_t *brush);
|
||||
qboolean BoundBrush (bspbrush_t *brush);
|
||||
qboolean CreateBrushWindings (bspbrush_t *brush);
|
||||
bspbrush_t *BrushFromBounds (vec3_t mins, vec3_t maxs);
|
||||
vec_t BrushVolume (bspbrush_t *brush);
|
||||
void WriteBspBrushMap (char *name, bspbrush_t *list);
|
||||
|
||||
void FilterDetailBrushesIntoTree( entity_t *e, tree_t *tree );
|
||||
void FilterStructuralBrushesIntoTree( entity_t *e, tree_t *tree );
|
||||
|
||||
//=============================================================================
|
||||
|
||||
// map.c
|
||||
|
||||
extern int entitySourceBrushes;
|
||||
|
||||
// mapplanes[ num^1 ] will always be the mirror or mapplanes[ num ]
|
||||
// nummapplanes will always be even
|
||||
extern plane_t mapplanes[MAX_MAP_PLANES];
|
||||
extern int nummapplanes;
|
||||
|
||||
extern vec3_t map_mins, map_maxs;
|
||||
|
||||
extern char mapIndexedShaders[MAX_MAP_BRUSHSIDES][MAX_QPATH];
|
||||
extern int numMapIndexedShaders;
|
||||
|
||||
extern entity_t *mapent;
|
||||
|
||||
#define MAX_BUILD_SIDES 300
|
||||
extern bspbrush_t *buildBrush;
|
||||
|
||||
|
||||
void LoadMapFile (char *filename);
|
||||
int FindFloatPlane (vec3_t normal, vec_t dist);
|
||||
int PlaneTypeForNormal (vec3_t normal);
|
||||
bspbrush_t *FinishBrush( void );
|
||||
mapDrawSurface_t *AllocDrawSurf( void );
|
||||
mapDrawSurface_t *DrawSurfaceForSide( bspbrush_t *b, side_t *s, winding_t *w );
|
||||
|
||||
//=============================================================================
|
||||
|
||||
//=============================================================================
|
||||
|
||||
// draw.c
|
||||
|
||||
extern vec3_t draw_mins, draw_maxs;
|
||||
extern qboolean drawflag;
|
||||
|
||||
void Draw_ClearWindow (void);
|
||||
void DrawWinding (winding_t *w);
|
||||
|
||||
void GLS_BeginScene (void);
|
||||
void GLS_Winding (winding_t *w, int code);
|
||||
void GLS_EndScene (void);
|
||||
|
||||
//=============================================================================
|
||||
|
||||
// csg
|
||||
|
||||
bspbrush_t *MakeBspBrushList ( bspbrush_t *brushes, vec3_t clipmins, vec3_t clipmaxs);
|
||||
|
||||
//=============================================================================
|
||||
|
||||
// brushbsp
|
||||
|
||||
#define PSIDE_FRONT 1
|
||||
#define PSIDE_BACK 2
|
||||
#define PSIDE_BOTH (PSIDE_FRONT|PSIDE_BACK)
|
||||
#define PSIDE_FACING 4
|
||||
|
||||
int BoxOnPlaneSide (vec3_t mins, vec3_t maxs, plane_t *plane);
|
||||
qboolean WindingIsTiny (winding_t *w);
|
||||
|
||||
void SplitBrush (bspbrush_t *brush, int planenum,
|
||||
bspbrush_t **front, bspbrush_t **back);
|
||||
|
||||
tree_t *AllocTree (void);
|
||||
node_t *AllocNode (void);
|
||||
|
||||
tree_t *BrushBSP (bspbrush_t *brushlist, vec3_t mins, vec3_t maxs);
|
||||
|
||||
//=============================================================================
|
||||
|
||||
// portals.c
|
||||
|
||||
void MakeHeadnodePortals (tree_t *tree);
|
||||
void MakeNodePortal (node_t *node);
|
||||
void SplitNodePortals (node_t *node);
|
||||
|
||||
qboolean Portal_Passable(portal_t *p);
|
||||
|
||||
qboolean FloodEntities (tree_t *tree);
|
||||
void FillOutside (node_t *headnode);
|
||||
void FloodAreas (tree_t *tree);
|
||||
bspface_t *VisibleFaces(entity_t *e, tree_t *tree);
|
||||
void FreePortal (portal_t *p);
|
||||
|
||||
void MakeTreePortals (tree_t *tree);
|
||||
|
||||
//=============================================================================
|
||||
|
||||
// glfile.c
|
||||
|
||||
void OutputWinding( winding_t *w, FILE *glview );
|
||||
void WriteGLView( tree_t *tree, char *source );
|
||||
|
||||
//=============================================================================
|
||||
|
||||
// leakfile.c
|
||||
|
||||
void LeakFile( tree_t *tree );
|
||||
|
||||
//=============================================================================
|
||||
|
||||
// prtfile.c
|
||||
|
||||
void NumberClusters( tree_t *tree );
|
||||
void WritePortalFile( tree_t *tree );
|
||||
|
||||
//=============================================================================
|
||||
|
||||
// writebsp.c
|
||||
|
||||
void SetModelNumbers (void);
|
||||
void SetLightStyles (void);
|
||||
|
||||
int EmitShader( const char *shader );
|
||||
|
||||
void BeginBSPFile (void);
|
||||
void EndBSPFile (void);
|
||||
|
||||
void BeginModel (void);
|
||||
void EndModel( node_t *headnode );
|
||||
|
||||
|
||||
//=============================================================================
|
||||
|
||||
// tree.c
|
||||
|
||||
void FreeTree (tree_t *tree);
|
||||
void FreeTree_r (node_t *node);
|
||||
void PrintTree_r (node_t *node, int depth);
|
||||
void FreeTreePortals_r (node_t *node);
|
||||
|
||||
//=============================================================================
|
||||
|
||||
// patch.c
|
||||
|
||||
extern int numMapPatches;
|
||||
|
||||
mapDrawSurface_t *DrawSurfaceForMesh( mesh_t *m );
|
||||
void ParsePatch( void );
|
||||
mesh_t *SubdivideMesh( mesh_t in, float maxError, float minLength );
|
||||
void PatchMapDrawSurfs( entity_t *e );
|
||||
|
||||
//=============================================================================
|
||||
|
||||
// lightmap.c
|
||||
|
||||
void AllocateLightmaps( entity_t *e );
|
||||
|
||||
//=============================================================================
|
||||
|
||||
// tjunction.c
|
||||
|
||||
void FixTJunctions( entity_t *e );
|
||||
|
||||
|
||||
//=============================================================================
|
||||
|
||||
// fog.c
|
||||
|
||||
void FogDrawSurfs( void );
|
||||
winding_t *WindingFromDrawSurf( mapDrawSurface_t *ds );
|
||||
|
||||
//=============================================================================
|
||||
|
||||
// facebsp.c
|
||||
|
||||
bspface_t *BspFaceForPortal( portal_t *p );
|
||||
bspface_t *MakeStructuralBspFaceList( bspbrush_t *list );
|
||||
bspface_t *MakeVisibleBspFaceList( bspbrush_t *list );
|
||||
tree_t *FaceBSP( bspface_t *list );
|
||||
|
||||
//=============================================================================
|
||||
|
||||
// misc_model.c
|
||||
|
||||
extern int c_triangleModels;
|
||||
extern int c_triangleSurfaces;
|
||||
extern int c_triangleVertexes;
|
||||
extern int c_triangleIndexes;
|
||||
|
||||
void AddTriangleModels( tree_t *tree );
|
||||
|
||||
//=============================================================================
|
||||
|
||||
// surface.c
|
||||
|
||||
extern mapDrawSurface_t mapDrawSurfs[MAX_MAP_DRAW_SURFS];
|
||||
extern int numMapDrawSurfs;
|
||||
|
||||
mapDrawSurface_t *AllocDrawSurf( void );
|
||||
void MergeSides( entity_t *e, tree_t *tree );
|
||||
void SubdivideDrawSurfs( entity_t *e, tree_t *tree );
|
||||
void MakeDrawSurfaces( bspbrush_t *b );
|
||||
void ClipSidesIntoTree( entity_t *e, tree_t *tree );
|
||||
void FilterDrawsurfsIntoTree( entity_t *e, tree_t *tree );
|
||||
|
||||
//==============================================================================
|
||||
|
||||
// brush_primit.c
|
||||
|
||||
#define BPRIMIT_UNDEFINED 0
|
||||
#define BPRIMIT_OLDBRUSHES 1
|
||||
#define BPRIMIT_NEWBRUSHES 2
|
||||
extern int g_bBrushPrimit;
|
||||
|
||||
void ComputeAxisBase( vec3_t normal, vec3_t texX, vec3_t texY);
|
608
q3map/shaders.c
Normal file
608
q3map/shaders.c
Normal file
|
@ -0,0 +1,608 @@
|
|||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 1999-2005 Id Software, Inc.
|
||||
|
||||
This file is part of Quake III Arena source code.
|
||||
|
||||
Quake III Arena source code 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.
|
||||
|
||||
Quake III Arena source code 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 Foobar; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include "cmdlib.h"
|
||||
#include "mathlib.h"
|
||||
#include "imagelib.h"
|
||||
#include "scriplib.h"
|
||||
|
||||
#ifdef _TTIMOBUILD
|
||||
#include "../common/qfiles.h"
|
||||
#include "../common/surfaceflags.h"
|
||||
#else
|
||||
#include "../code/qcommon/qfiles.h"
|
||||
#include "../code/game/surfaceflags.h"
|
||||
#endif
|
||||
|
||||
#include "shaders.h"
|
||||
#ifdef _WIN32
|
||||
|
||||
#ifdef _TTIMOBUILD
|
||||
#include "pakstuff.h"
|
||||
#include "jpeglib.h"
|
||||
#else
|
||||
#include "../libs/pakstuff.h"
|
||||
#include "../libs/jpeglib.h"
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
// 5% backsplash by default
|
||||
#define DEFAULT_BACKSPLASH_FRACTION 0.05
|
||||
#define DEFAULT_BACKSPLASH_DISTANCE 24
|
||||
|
||||
|
||||
#define MAX_SURFACE_INFO 4096
|
||||
|
||||
shaderInfo_t defaultInfo;
|
||||
shaderInfo_t shaderInfo[MAX_SURFACE_INFO];
|
||||
int numShaderInfo;
|
||||
|
||||
|
||||
typedef struct {
|
||||
char *name;
|
||||
int clearSolid, surfaceFlags, contents;
|
||||
} infoParm_t;
|
||||
|
||||
infoParm_t infoParms[] = {
|
||||
// server relevant contents
|
||||
{"water", 1, 0, CONTENTS_WATER },
|
||||
{"slime", 1, 0, CONTENTS_SLIME }, // mildly damaging
|
||||
{"lava", 1, 0, CONTENTS_LAVA }, // very damaging
|
||||
{"playerclip", 1, 0, CONTENTS_PLAYERCLIP },
|
||||
{"monsterclip", 1, 0, CONTENTS_MONSTERCLIP },
|
||||
{"nodrop", 1, 0, CONTENTS_NODROP }, // don't drop items or leave bodies (death fog, lava, etc)
|
||||
{"nonsolid", 1, SURF_NONSOLID, 0}, // clears the solid flag
|
||||
|
||||
// utility relevant attributes
|
||||
{"origin", 1, 0, CONTENTS_ORIGIN }, // center of rotating brushes
|
||||
{"trans", 0, 0, CONTENTS_TRANSLUCENT }, // don't eat contained surfaces
|
||||
{"detail", 0, 0, CONTENTS_DETAIL }, // don't include in structural bsp
|
||||
{"structural", 0, 0, CONTENTS_STRUCTURAL }, // force into structural bsp even if trnas
|
||||
{"areaportal", 1, 0, CONTENTS_AREAPORTAL }, // divides areas
|
||||
{"clusterportal",1, 0, CONTENTS_CLUSTERPORTAL },// for bots
|
||||
{"donotenter", 1, 0, CONTENTS_DONOTENTER }, // for bots
|
||||
{"botclip", 1, 0, CONTENTS_BOTCLIP }, // for bots
|
||||
{"nobotclip", 0, 0, CONTENTS_NOBOTCLIP }, // don't use for bot clipping
|
||||
|
||||
{"fog", 1, 0, CONTENTS_FOG}, // carves surfaces entering
|
||||
{"sky", 0, SURF_SKY, 0 }, // emit light from an environment map
|
||||
{"lightfilter", 0, SURF_LIGHTFILTER, 0 }, // filter light going through it
|
||||
{"alphashadow", 0, SURF_ALPHASHADOW, 0 }, // test light on a per-pixel basis
|
||||
{"hint", 0, SURF_HINT, 0 }, // use as a primary splitter
|
||||
|
||||
// server attributes
|
||||
{"slick", 0, SURF_SLICK, 0 },
|
||||
{"noimpact", 0, SURF_NOIMPACT, 0 }, // don't make impact explosions or marks
|
||||
{"nomarks", 0, SURF_NOMARKS, 0 }, // don't make impact marks, but still explode
|
||||
{"ladder", 0, SURF_LADDER, 0 },
|
||||
{"nodamage", 0, SURF_NODAMAGE, 0 },
|
||||
{"metalsteps", 0, SURF_METALSTEPS,0 },
|
||||
{"flesh", 0, SURF_FLESH, 0 },
|
||||
{"nosteps", 0, SURF_NOSTEPS, 0 },
|
||||
|
||||
// drawsurf attributes
|
||||
{"nodraw", 0, SURF_NODRAW, 0 }, // don't generate a drawsurface (or a lightmap)
|
||||
{"pointlight", 0, SURF_POINTLIGHT, 0 }, // sample lighting at vertexes
|
||||
{"nolightmap", 0, SURF_NOLIGHTMAP,0 }, // don't generate a lightmap
|
||||
{"nodlight", 0, SURF_NODLIGHT, 0 }, // don't ever add dynamic lights
|
||||
{"dust", 0, SURF_DUST, 0} // leave dust trail when walking on this surface
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
===============
|
||||
LoadShaderImage
|
||||
===============
|
||||
*/
|
||||
|
||||
byte* LoadImageFile(char *filename, qboolean *bTGA)
|
||||
{
|
||||
byte *buffer = NULL;
|
||||
int nLen = 0;
|
||||
*bTGA = qtrue;
|
||||
if (FileExists(filename))
|
||||
{
|
||||
LoadFileBlock(filename, &buffer);
|
||||
}
|
||||
#ifdef _WIN32
|
||||
else
|
||||
{
|
||||
PakLoadAnyFile(filename, &buffer);
|
||||
}
|
||||
#endif
|
||||
if ( buffer == NULL)
|
||||
{
|
||||
nLen = strlen(filename);
|
||||
filename[nLen-3] = 'j';
|
||||
filename[nLen-2] = 'p';
|
||||
filename[nLen-1] = 'g';
|
||||
if (FileExists(filename))
|
||||
{
|
||||
LoadFileBlock(filename, &buffer);
|
||||
}
|
||||
#ifdef _WIN32
|
||||
else
|
||||
{
|
||||
PakLoadAnyFile(filename, &buffer);
|
||||
}
|
||||
#endif
|
||||
if ( buffer )
|
||||
{
|
||||
*bTGA = qfalse;
|
||||
}
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
LoadShaderImage
|
||||
===============
|
||||
*/
|
||||
static void LoadShaderImage( shaderInfo_t *si ) {
|
||||
char filename[1024];
|
||||
int i, count;
|
||||
float color[4];
|
||||
byte *buffer;
|
||||
qboolean bTGA = qtrue;
|
||||
|
||||
// look for the lightimage if it is specified
|
||||
if ( si->lightimage[0] ) {
|
||||
sprintf( filename, "%s%s", gamedir, si->lightimage );
|
||||
DefaultExtension( filename, ".tga" );
|
||||
buffer = LoadImageFile(filename, &bTGA);
|
||||
if ( buffer != NULL) {
|
||||
goto loadTga;
|
||||
}
|
||||
}
|
||||
|
||||
// look for the editorimage if it is specified
|
||||
if ( si->editorimage[0] ) {
|
||||
sprintf( filename, "%s%s", gamedir, si->editorimage );
|
||||
DefaultExtension( filename, ".tga" );
|
||||
buffer = LoadImageFile(filename, &bTGA);
|
||||
if ( buffer != NULL) {
|
||||
goto loadTga;
|
||||
}
|
||||
}
|
||||
|
||||
// just try the shader name with a .tga
|
||||
// on unix, we have case sensitivity problems...
|
||||
sprintf( filename, "%s%s.tga", gamedir, si->shader );
|
||||
buffer = LoadImageFile(filename, &bTGA);
|
||||
if ( buffer != NULL) {
|
||||
goto loadTga;
|
||||
}
|
||||
|
||||
sprintf( filename, "%s%s.TGA", gamedir, si->shader );
|
||||
buffer = LoadImageFile(filename, &bTGA);
|
||||
if ( buffer != NULL) {
|
||||
goto loadTga;
|
||||
}
|
||||
|
||||
// couldn't load anything
|
||||
_printf("WARNING: Couldn't find image for shader %s\n", si->shader );
|
||||
|
||||
si->color[0] = 1;
|
||||
si->color[1] = 1;
|
||||
si->color[2] = 1;
|
||||
si->width = 64;
|
||||
si->height = 64;
|
||||
si->pixels = malloc( si->width * si->height * 4 );
|
||||
memset ( si->pixels, 255, si->width * si->height * 4 );
|
||||
return;
|
||||
|
||||
// load the image to get dimensions and color
|
||||
loadTga:
|
||||
if ( bTGA) {
|
||||
LoadTGABuffer( buffer, &si->pixels, &si->width, &si->height );
|
||||
}
|
||||
else {
|
||||
#ifdef _WIN32
|
||||
LoadJPGBuff(buffer, &si->pixels, &si->width, &si->height );
|
||||
#endif
|
||||
}
|
||||
|
||||
free(buffer);
|
||||
|
||||
count = si->width * si->height;
|
||||
|
||||
VectorClear( color );
|
||||
color[ 3 ] = 0;
|
||||
for ( i = 0 ; i < count ; i++ ) {
|
||||
color[0] += si->pixels[ i * 4 + 0 ];
|
||||
color[1] += si->pixels[ i * 4 + 1 ];
|
||||
color[2] += si->pixels[ i * 4 + 2 ];
|
||||
color[3] += si->pixels[ i * 4 + 3 ];
|
||||
}
|
||||
ColorNormalize( color, si->color );
|
||||
VectorScale( color, 1.0/count, si->averageColor );
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
AllocShaderInfo
|
||||
===============
|
||||
*/
|
||||
static shaderInfo_t *AllocShaderInfo( void ) {
|
||||
shaderInfo_t *si;
|
||||
|
||||
if ( numShaderInfo == MAX_SURFACE_INFO ) {
|
||||
Error( "MAX_SURFACE_INFO" );
|
||||
}
|
||||
si = &shaderInfo[ numShaderInfo ];
|
||||
numShaderInfo++;
|
||||
|
||||
// set defaults
|
||||
|
||||
si->contents = CONTENTS_SOLID;
|
||||
|
||||
si->backsplashFraction = DEFAULT_BACKSPLASH_FRACTION;
|
||||
si->backsplashDistance = DEFAULT_BACKSPLASH_DISTANCE;
|
||||
|
||||
si->lightmapSampleSize = 0;
|
||||
si->forceTraceLight = qfalse;
|
||||
si->forceVLight = qfalse;
|
||||
si->patchShadows = qfalse;
|
||||
si->vertexShadows = qfalse;
|
||||
si->noVertexShadows = qfalse;
|
||||
si->forceSunLight = qfalse;
|
||||
si->vertexScale = 1.0;
|
||||
si->notjunc = qfalse;
|
||||
|
||||
return si;
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
ShaderInfoForShader
|
||||
===============
|
||||
*/
|
||||
shaderInfo_t *ShaderInfoForShader( const char *shaderName ) {
|
||||
int i;
|
||||
shaderInfo_t *si;
|
||||
char shader[MAX_QPATH];
|
||||
|
||||
// strip off extension
|
||||
strcpy( shader, shaderName );
|
||||
StripExtension( shader );
|
||||
|
||||
// search for it
|
||||
for ( i = 0 ; i < numShaderInfo ; i++ ) {
|
||||
si = &shaderInfo[ i ];
|
||||
if ( !Q_stricmp( shader, si->shader ) ) {
|
||||
if ( !si->width ) {
|
||||
LoadShaderImage( si );
|
||||
}
|
||||
return si;
|
||||
}
|
||||
}
|
||||
|
||||
si = AllocShaderInfo();
|
||||
strcpy( si->shader, shader );
|
||||
|
||||
LoadShaderImage( si );
|
||||
|
||||
return si;
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
ParseShaderFile
|
||||
===============
|
||||
*/
|
||||
static void ParseShaderFile( const char *filename ) {
|
||||
int i;
|
||||
int numInfoParms = sizeof(infoParms) / sizeof(infoParms[0]);
|
||||
shaderInfo_t *si;
|
||||
|
||||
// qprintf( "shaderFile: %s\n", filename );
|
||||
LoadScriptFile( filename );
|
||||
while ( 1 ) {
|
||||
if ( !GetToken( qtrue ) ) {
|
||||
break;
|
||||
}
|
||||
|
||||
si = AllocShaderInfo();
|
||||
strcpy( si->shader, token );
|
||||
MatchToken( "{" );
|
||||
while ( 1 ) {
|
||||
if ( !GetToken( qtrue ) ) {
|
||||
break;
|
||||
}
|
||||
if ( !strcmp( token, "}" ) ) {
|
||||
break;
|
||||
}
|
||||
|
||||
// skip internal braced sections
|
||||
if ( !strcmp( token, "{" ) ) {
|
||||
si->hasPasses = qtrue;
|
||||
while ( 1 ) {
|
||||
if ( !GetToken( qtrue ) ) {
|
||||
break;
|
||||
}
|
||||
if ( !strcmp( token, "}" ) ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( !Q_stricmp( token, "surfaceparm" ) ) {
|
||||
GetToken( qfalse );
|
||||
for ( i = 0 ; i < numInfoParms ; i++ ) {
|
||||
if ( !Q_stricmp( token, infoParms[i].name ) ) {
|
||||
si->surfaceFlags |= infoParms[i].surfaceFlags;
|
||||
si->contents |= infoParms[i].contents;
|
||||
if ( infoParms[i].clearSolid ) {
|
||||
si->contents &= ~CONTENTS_SOLID;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( i == numInfoParms ) {
|
||||
// we will silently ignore all tokens beginning with qer,
|
||||
// which are QuakeEdRadient parameters
|
||||
if ( Q_strncasecmp( token, "qer", 3 ) ) {
|
||||
_printf( "Unknown surfaceparm: \"%s\"\n", token );
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
// qer_editorimage <image>
|
||||
if ( !Q_stricmp( token, "qer_editorimage" ) ) {
|
||||
GetToken( qfalse );
|
||||
strcpy( si->editorimage, token );
|
||||
DefaultExtension( si->editorimage, ".tga" );
|
||||
continue;
|
||||
}
|
||||
|
||||
// q3map_lightimage <image>
|
||||
if ( !Q_stricmp( token, "q3map_lightimage" ) ) {
|
||||
GetToken( qfalse );
|
||||
strcpy( si->lightimage, token );
|
||||
DefaultExtension( si->lightimage, ".tga" );
|
||||
continue;
|
||||
}
|
||||
|
||||
// q3map_surfacelight <value>
|
||||
if ( !Q_stricmp( token, "q3map_surfacelight" ) ) {
|
||||
GetToken( qfalse );
|
||||
si->value = atoi( token );
|
||||
continue;
|
||||
}
|
||||
|
||||
// q3map_lightsubdivide <value>
|
||||
if ( !Q_stricmp( token, "q3map_lightsubdivide" ) ) {
|
||||
GetToken( qfalse );
|
||||
si->lightSubdivide = atoi( token );
|
||||
continue;
|
||||
}
|
||||
|
||||
// q3map_lightmapsamplesize <value>
|
||||
if ( !Q_stricmp( token, "q3map_lightmapsamplesize" ) ) {
|
||||
GetToken( qfalse );
|
||||
si->lightmapSampleSize = atoi( token );
|
||||
continue;
|
||||
}
|
||||
|
||||
// q3map_tracelight
|
||||
if ( !Q_stricmp( token, "q3map_tracelight" ) ) {
|
||||
si->forceTraceLight = qtrue;
|
||||
continue;
|
||||
}
|
||||
|
||||
// q3map_vlight
|
||||
if ( !Q_stricmp( token, "q3map_vlight" ) ) {
|
||||
si->forceVLight = qtrue;
|
||||
continue;
|
||||
}
|
||||
|
||||
// q3map_patchshadows
|
||||
if ( !Q_stricmp( token, "q3map_patchshadows" ) ) {
|
||||
si->patchShadows = qtrue;
|
||||
continue;
|
||||
}
|
||||
|
||||
// q3map_vertexshadows
|
||||
if ( !Q_stricmp( token, "q3map_vertexshadows" ) ) {
|
||||
si->vertexShadows = qtrue;
|
||||
continue;
|
||||
}
|
||||
|
||||
// q3map_novertexshadows
|
||||
if ( !Q_stricmp( token, "q3map_novertexshadows" ) ) {
|
||||
si->noVertexShadows = qtrue;
|
||||
continue;
|
||||
}
|
||||
|
||||
// q3map_forcesunlight
|
||||
if ( !Q_stricmp( token, "q3map_forcesunlight" ) ) {
|
||||
si->forceSunLight = qtrue;
|
||||
continue;
|
||||
}
|
||||
|
||||
// q3map_vertexscale
|
||||
if ( !Q_stricmp( token, "q3map_vertexscale" ) ) {
|
||||
GetToken( qfalse );
|
||||
si->vertexScale = atof(token);
|
||||
continue;
|
||||
}
|
||||
|
||||
// q3map_notjunc
|
||||
if ( !Q_stricmp( token, "q3map_notjunc" ) ) {
|
||||
si->notjunc = qtrue;
|
||||
continue;
|
||||
}
|
||||
|
||||
// q3map_globaltexture
|
||||
if ( !Q_stricmp( token, "q3map_globaltexture" ) ) {
|
||||
si->globalTexture = qtrue;
|
||||
continue;
|
||||
}
|
||||
|
||||
// q3map_backsplash <percent> <distance>
|
||||
if ( !Q_stricmp( token, "q3map_backsplash" ) ) {
|
||||
GetToken( qfalse );
|
||||
si->backsplashFraction = atof( token ) * 0.01;
|
||||
GetToken( qfalse );
|
||||
si->backsplashDistance = atof( token );
|
||||
continue;
|
||||
}
|
||||
|
||||
// q3map_backshader <shader>
|
||||
if ( !Q_stricmp( token, "q3map_backshader" ) ) {
|
||||
GetToken( qfalse );
|
||||
strcpy( si->backShader, token );
|
||||
continue;
|
||||
}
|
||||
|
||||
// q3map_flare <shader>
|
||||
if ( !Q_stricmp( token, "q3map_flare" ) ) {
|
||||
GetToken( qfalse );
|
||||
strcpy( si->flareShader, token );
|
||||
continue;
|
||||
}
|
||||
|
||||
// light <value>
|
||||
// old style flare specification
|
||||
if ( !Q_stricmp( token, "light" ) ) {
|
||||
GetToken( qfalse );
|
||||
strcpy( si->flareShader, "flareshader" );
|
||||
continue;
|
||||
}
|
||||
|
||||
// q3map_sun <red> <green> <blue> <intensity> <degrees> <elivation>
|
||||
// color will be normalized, so it doesn't matter what range you use
|
||||
// intensity falls off with angle but not distance 100 is a fairly bright sun
|
||||
// degree of 0 = from the east, 90 = north, etc. altitude of 0 = sunrise/set, 90 = noon
|
||||
if ( !Q_stricmp( token, "q3map_sun" ) ) {
|
||||
float a, b;
|
||||
|
||||
GetToken( qfalse );
|
||||
si->sunLight[0] = atof( token );
|
||||
GetToken( qfalse );
|
||||
si->sunLight[1] = atof( token );
|
||||
GetToken( qfalse );
|
||||
si->sunLight[2] = atof( token );
|
||||
|
||||
VectorNormalize( si->sunLight, si->sunLight);
|
||||
|
||||
GetToken( qfalse );
|
||||
a = atof( token );
|
||||
VectorScale( si->sunLight, a, si->sunLight);
|
||||
|
||||
GetToken( qfalse );
|
||||
a = atof( token );
|
||||
a = a / 180 * Q_PI;
|
||||
|
||||
GetToken( qfalse );
|
||||
b = atof( token );
|
||||
b = b / 180 * Q_PI;
|
||||
|
||||
si->sunDirection[0] = cos( a ) * cos( b );
|
||||
si->sunDirection[1] = sin( a ) * cos( b );
|
||||
si->sunDirection[2] = sin( b );
|
||||
|
||||
si->surfaceFlags |= SURF_SKY;
|
||||
continue;
|
||||
}
|
||||
|
||||
// tesssize is used to force liquid surfaces to subdivide
|
||||
if ( !Q_stricmp( token, "tesssize" ) ) {
|
||||
GetToken( qfalse );
|
||||
si->subdivisions = atof( token );
|
||||
continue;
|
||||
}
|
||||
|
||||
// cull none will set twoSided
|
||||
if ( !Q_stricmp( token, "cull" ) ) {
|
||||
GetToken( qfalse );
|
||||
if ( !Q_stricmp( token, "none" ) ) {
|
||||
si->twoSided = qtrue;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
// deformVertexes autosprite[2]
|
||||
// we catch this so autosprited surfaces become point
|
||||
// lights instead of area lights
|
||||
if ( !Q_stricmp( token, "deformVertexes" ) ) {
|
||||
GetToken( qfalse );
|
||||
if ( !Q_strncasecmp( token, "autosprite", 10 ) ) {
|
||||
si->autosprite = qtrue;
|
||||
si->contents = CONTENTS_DETAIL;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
// ignore all other tokens on the line
|
||||
|
||||
while ( TokenAvailable() ) {
|
||||
GetToken( qfalse );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
LoadShaderInfo
|
||||
===============
|
||||
*/
|
||||
#define MAX_SHADER_FILES 64
|
||||
void LoadShaderInfo( void ) {
|
||||
char filename[1024];
|
||||
int i;
|
||||
char *shaderFiles[MAX_SHADER_FILES];
|
||||
int numShaderFiles;
|
||||
|
||||
sprintf( filename, "%sscripts/shaderlist.txt", gamedir );
|
||||
LoadScriptFile( filename );
|
||||
|
||||
numShaderFiles = 0;
|
||||
while ( 1 ) {
|
||||
if ( !GetToken( qtrue ) ) {
|
||||
break;
|
||||
}
|
||||
shaderFiles[numShaderFiles] = malloc(MAX_OS_PATH);
|
||||
strcpy( shaderFiles[ numShaderFiles ], token );
|
||||
numShaderFiles++;
|
||||
}
|
||||
|
||||
for ( i = 0 ; i < numShaderFiles ; i++ ) {
|
||||
sprintf( filename, "%sscripts/%s.shader", gamedir, shaderFiles[i] );
|
||||
ParseShaderFile( filename );
|
||||
free(shaderFiles[i]);
|
||||
}
|
||||
|
||||
qprintf( "%5i shaderInfo\n", numShaderInfo);
|
||||
}
|
||||
|
71
q3map/shaders.h
Normal file
71
q3map/shaders.h
Normal file
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 1999-2005 Id Software, Inc.
|
||||
|
||||
This file is part of Quake III Arena source code.
|
||||
|
||||
Quake III Arena source code 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.
|
||||
|
||||
Quake III Arena source code 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 Foobar; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
|
||||
typedef struct shaderInfo_s {
|
||||
char shader[MAX_QPATH];
|
||||
int surfaceFlags;
|
||||
int contents;
|
||||
int value;
|
||||
|
||||
char backShader[MAX_QPATH]; // for surfaces that generate different front and back passes
|
||||
char flareShader[MAX_QPATH]; // for light flares
|
||||
|
||||
float subdivisions; // from a "tesssize xxx"
|
||||
float backsplashFraction; // floating point value, usually 0.05
|
||||
float backsplashDistance; // default 16
|
||||
float lightSubdivide; // default 120
|
||||
int lightmapSampleSize; // lightmap sample size
|
||||
|
||||
qboolean hasPasses; // false if the shader doesn't define any rendering passes
|
||||
|
||||
qboolean globalTexture; // don't normalize texture repeats
|
||||
|
||||
qboolean twoSided; // cull none
|
||||
qboolean autosprite; // autosprite shaders will become point lights
|
||||
// instead of area lights
|
||||
qboolean lightFilter; // light rays that cross surfaces of this type
|
||||
// should test against the filter image
|
||||
qboolean forceTraceLight; // always use -light for this surface
|
||||
qboolean forceVLight; // always use -vlight for this surface
|
||||
qboolean patchShadows; // have patches casting shadows when using -light for this surface
|
||||
qboolean vertexShadows; // shadows will be casted at this surface even when vertex lit
|
||||
qboolean noVertexShadows; // no shadows will be casted at this surface in vertex lighting
|
||||
qboolean forceSunLight; // force sun light at this surface even tho we might not calculate shadows in vertex lighting
|
||||
qboolean notjunc; // don't use this surface for tjunction fixing
|
||||
float vertexScale; // vertex light scale
|
||||
|
||||
char editorimage[MAX_QPATH]; // use this image to generate texture coordinates
|
||||
char lightimage[MAX_QPATH]; // use this image to generate color / averageColor
|
||||
vec3_t color; // colorNormalized
|
||||
vec3_t averageColor;
|
||||
|
||||
int width, height;
|
||||
byte *pixels;
|
||||
|
||||
vec3_t sunLight;
|
||||
vec3_t sunDirection;
|
||||
} shaderInfo_t;
|
||||
|
||||
void LoadShaderInfo( void );
|
||||
shaderInfo_t *ShaderInfoForShader( const char *shader );
|
||||
|
5742
q3map/soundv.c
Normal file
5742
q3map/soundv.c
Normal file
File diff suppressed because it is too large
Load diff
1158
q3map/surface.c
Normal file
1158
q3map/surface.c
Normal file
File diff suppressed because it is too large
Load diff
1255
q3map/terrain.c
Normal file
1255
q3map/terrain.c
Normal file
File diff suppressed because it is too large
Load diff
551
q3map/tjunction.c
Normal file
551
q3map/tjunction.c
Normal file
|
@ -0,0 +1,551 @@
|
|||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 1999-2005 Id Software, Inc.
|
||||
|
||||
This file is part of Quake III Arena source code.
|
||||
|
||||
Quake III Arena source code 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.
|
||||
|
||||
Quake III Arena source code 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 Foobar; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
#include "qbsp.h"
|
||||
|
||||
typedef struct edgePoint_s {
|
||||
float intercept;
|
||||
vec3_t xyz;
|
||||
struct edgePoint_s *prev, *next;
|
||||
} edgePoint_t;
|
||||
|
||||
typedef struct edgeLine_s {
|
||||
vec3_t normal1;
|
||||
float dist1;
|
||||
|
||||
vec3_t normal2;
|
||||
float dist2;
|
||||
|
||||
vec3_t origin;
|
||||
vec3_t dir;
|
||||
|
||||
edgePoint_t chain; // unused element of doubly linked list
|
||||
} edgeLine_t;
|
||||
|
||||
typedef struct {
|
||||
float length;
|
||||
drawVert_t *dv[2];
|
||||
} originalEdge_t;
|
||||
|
||||
#define MAX_ORIGINAL_EDGES 0x10000
|
||||
originalEdge_t originalEdges[MAX_ORIGINAL_EDGES];
|
||||
int numOriginalEdges;
|
||||
|
||||
|
||||
#define MAX_EDGE_LINES 0x10000
|
||||
edgeLine_t edgeLines[MAX_EDGE_LINES];
|
||||
int numEdgeLines;
|
||||
|
||||
int c_degenerateEdges;
|
||||
int c_addedVerts;
|
||||
int c_totalVerts;
|
||||
|
||||
int c_natural, c_rotate, c_cant;
|
||||
|
||||
// these should be whatever epsilon we actually expect,
|
||||
// plus SNAP_INT_TO_FLOAT
|
||||
#define LINE_POSITION_EPSILON 0.25
|
||||
#define POINT_ON_LINE_EPSILON 0.25
|
||||
|
||||
/*
|
||||
====================
|
||||
InsertPointOnEdge
|
||||
====================
|
||||
*/
|
||||
void InsertPointOnEdge( vec3_t v, edgeLine_t *e ) {
|
||||
vec3_t delta;
|
||||
float d;
|
||||
edgePoint_t *p, *scan;
|
||||
|
||||
VectorSubtract( v, e->origin, delta );
|
||||
d = DotProduct( delta, e->dir );
|
||||
|
||||
p = malloc( sizeof(edgePoint_t) );
|
||||
p->intercept = d;
|
||||
VectorCopy( v, p->xyz );
|
||||
|
||||
if ( e->chain.next == &e->chain ) {
|
||||
e->chain.next = e->chain.prev = p;
|
||||
p->next = p->prev = &e->chain;
|
||||
return;
|
||||
}
|
||||
|
||||
scan = e->chain.next;
|
||||
for ( ; scan != &e->chain ; scan = scan->next ) {
|
||||
d = p->intercept - scan->intercept;
|
||||
if ( d > -LINE_POSITION_EPSILON && d < LINE_POSITION_EPSILON ) {
|
||||
free( p );
|
||||
return; // the point is already set
|
||||
}
|
||||
|
||||
if ( p->intercept < scan->intercept ) {
|
||||
// insert here
|
||||
p->prev = scan->prev;
|
||||
p->next = scan;
|
||||
scan->prev->next = p;
|
||||
scan->prev = p;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// add at the end
|
||||
p->prev = scan->prev;
|
||||
p->next = scan;
|
||||
scan->prev->next = p;
|
||||
scan->prev = p;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
====================
|
||||
AddEdge
|
||||
====================
|
||||
*/
|
||||
int AddEdge( vec3_t v1, vec3_t v2, qboolean createNonAxial ) {
|
||||
int i;
|
||||
edgeLine_t *e;
|
||||
float d;
|
||||
vec3_t dir;
|
||||
|
||||
VectorSubtract( v2, v1, dir );
|
||||
d = VectorNormalize( dir, dir );
|
||||
if ( d < 0.1 ) {
|
||||
// if we added a 0 length vector, it would make degenerate planes
|
||||
c_degenerateEdges++;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ( !createNonAxial ) {
|
||||
if ( fabs( dir[0] + dir[1] + dir[2] ) != 1.0 ) {
|
||||
if ( numOriginalEdges == MAX_ORIGINAL_EDGES ) {
|
||||
Error( "MAX_ORIGINAL_EDGES" );
|
||||
}
|
||||
originalEdges[ numOriginalEdges ].dv[0] = (drawVert_t *)v1;
|
||||
originalEdges[ numOriginalEdges ].dv[1] = (drawVert_t *)v2;
|
||||
originalEdges[ numOriginalEdges ].length = d;
|
||||
numOriginalEdges++;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
for ( i = 0 ; i < numEdgeLines ; i++ ) {
|
||||
e = &edgeLines[i];
|
||||
|
||||
d = DotProduct( v1, e->normal1 ) - e->dist1;
|
||||
if ( d < -POINT_ON_LINE_EPSILON || d > POINT_ON_LINE_EPSILON ) {
|
||||
continue;
|
||||
}
|
||||
d = DotProduct( v1, e->normal2 ) - e->dist2;
|
||||
if ( d < -POINT_ON_LINE_EPSILON || d > POINT_ON_LINE_EPSILON ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
d = DotProduct( v2, e->normal1 ) - e->dist1;
|
||||
if ( d < -POINT_ON_LINE_EPSILON || d > POINT_ON_LINE_EPSILON ) {
|
||||
continue;
|
||||
}
|
||||
d = DotProduct( v2, e->normal2 ) - e->dist2;
|
||||
if ( d < -POINT_ON_LINE_EPSILON || d > POINT_ON_LINE_EPSILON ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// this is the edge
|
||||
InsertPointOnEdge( v1, e );
|
||||
InsertPointOnEdge( v2, e );
|
||||
return i;
|
||||
}
|
||||
|
||||
// create a new edge
|
||||
if ( numEdgeLines >= MAX_EDGE_LINES ) {
|
||||
Error( "MAX_EDGE_LINES" );
|
||||
}
|
||||
|
||||
e = &edgeLines[ numEdgeLines ];
|
||||
numEdgeLines++;
|
||||
|
||||
e->chain.next = e->chain.prev = &e->chain;
|
||||
|
||||
VectorCopy( v1, e->origin );
|
||||
VectorCopy( dir, e->dir );
|
||||
|
||||
MakeNormalVectors( e->dir, e->normal1, e->normal2 );
|
||||
e->dist1 = DotProduct( e->origin, e->normal1 );
|
||||
e->dist2 = DotProduct( e->origin, e->normal2 );
|
||||
|
||||
InsertPointOnEdge( v1, e );
|
||||
InsertPointOnEdge( v2, e );
|
||||
|
||||
return numEdgeLines - 1;
|
||||
}
|
||||
|
||||
/*
|
||||
====================
|
||||
AddSurfaceEdges
|
||||
====================
|
||||
*/
|
||||
void AddSurfaceEdges( mapDrawSurface_t *ds ) {
|
||||
int i;
|
||||
|
||||
for ( i = 0 ; i < ds->numVerts ; i++ ) {
|
||||
// save the edge number in the lightmap field
|
||||
// so we don't need to look it up again
|
||||
ds->verts[i].lightmap[0] =
|
||||
AddEdge( ds->verts[i].xyz, ds->verts[(i+1) % ds->numVerts].xyz, qfalse );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
ColinearEdge
|
||||
================
|
||||
*/
|
||||
qboolean ColinearEdge( vec3_t v1, vec3_t v2, vec3_t v3 ) {
|
||||
vec3_t midpoint, dir, offset, on;
|
||||
float d;
|
||||
|
||||
VectorSubtract( v2, v1, midpoint );
|
||||
VectorSubtract( v3, v1, dir );
|
||||
d = VectorNormalize( dir, dir );
|
||||
if ( d == 0 ) {
|
||||
return qfalse; // degenerate
|
||||
}
|
||||
|
||||
d = DotProduct( midpoint, dir );
|
||||
VectorScale( dir, d, on );
|
||||
VectorSubtract( midpoint, on, offset );
|
||||
d = VectorLength ( offset );
|
||||
|
||||
if ( d < 0.1 ) {
|
||||
return qtrue;
|
||||
}
|
||||
|
||||
return qfalse;
|
||||
}
|
||||
|
||||
/*
|
||||
====================
|
||||
AddPatchEdges
|
||||
|
||||
Add colinear border edges, which will fix some classes of patch to
|
||||
brush tjunctions
|
||||
====================
|
||||
*/
|
||||
void AddPatchEdges( mapDrawSurface_t *ds ) {
|
||||
int i;
|
||||
float *v1, *v2, *v3;
|
||||
|
||||
for ( i = 0 ; i < ds->patchWidth - 2; i+=2 ) {
|
||||
v1 = ds->verts[ i ].xyz;
|
||||
v2 = ds->verts[ i + 1 ].xyz;
|
||||
v3 = ds->verts[ i + 2 ].xyz;
|
||||
|
||||
// if v2 is the midpoint of v1 to v3, add an edge from v1 to v3
|
||||
if ( ColinearEdge( v1, v2, v3 ) ) {
|
||||
AddEdge( v1, v3, qfalse );
|
||||
}
|
||||
|
||||
v1 = ds->verts[ ( ds->patchHeight - 1 ) * ds->patchWidth + i ].xyz;
|
||||
v2 = ds->verts[ ( ds->patchHeight - 1 ) * ds->patchWidth + i + 1 ].xyz;
|
||||
v3 = ds->verts[ ( ds->patchHeight - 1 ) * ds->patchWidth + i + 2 ].xyz;
|
||||
|
||||
// if v2 is on the v1 to v3 line, add an edge from v1 to v3
|
||||
if ( ColinearEdge( v1, v2, v3 ) ) {
|
||||
AddEdge( v1, v3, qfalse );
|
||||
}
|
||||
}
|
||||
|
||||
for ( i = 0 ; i < ds->patchHeight - 2 ; i+=2 ) {
|
||||
v1 = ds->verts[ i * ds->patchWidth ].xyz;
|
||||
v2 = ds->verts[ ( i + 1 ) * ds->patchWidth ].xyz;
|
||||
v3 = ds->verts[ ( i + 2 ) * ds->patchWidth ].xyz;
|
||||
|
||||
// if v2 is the midpoint of v1 to v3, add an edge from v1 to v3
|
||||
if ( ColinearEdge( v1, v2, v3 ) ) {
|
||||
AddEdge( v1, v3, qfalse );
|
||||
}
|
||||
|
||||
v1 = ds->verts[ ( ds->patchWidth - 1 ) + i * ds->patchWidth ].xyz;
|
||||
v2 = ds->verts[ ( ds->patchWidth - 1 ) + ( i + 1 ) * ds->patchWidth ].xyz;
|
||||
v3 = ds->verts[ ( ds->patchWidth - 1 ) + ( i + 2 ) * ds->patchWidth ].xyz;
|
||||
|
||||
// if v2 is the midpoint of v1 to v3, add an edge from v1 to v3
|
||||
if ( ColinearEdge( v1, v2, v3 ) ) {
|
||||
AddEdge( v1, v3, qfalse );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
====================
|
||||
FixSurfaceJunctions
|
||||
====================
|
||||
*/
|
||||
#define MAX_SURFACE_VERTS 256
|
||||
void FixSurfaceJunctions( mapDrawSurface_t *ds ) {
|
||||
int i, j, k;
|
||||
edgeLine_t *e;
|
||||
edgePoint_t *p;
|
||||
int originalVerts;
|
||||
int counts[MAX_SURFACE_VERTS];
|
||||
int originals[MAX_SURFACE_VERTS];
|
||||
int firstVert[MAX_SURFACE_VERTS];
|
||||
drawVert_t verts[MAX_SURFACE_VERTS], *v1, *v2;
|
||||
int numVerts;
|
||||
float start, end, frac;
|
||||
vec3_t delta;
|
||||
|
||||
originalVerts = ds->numVerts;
|
||||
|
||||
numVerts = 0;
|
||||
for ( i = 0 ; i < ds->numVerts ; i++ ) {
|
||||
counts[i] = 0;
|
||||
firstVert[i] = numVerts;
|
||||
|
||||
// copy first vert
|
||||
if ( numVerts == MAX_SURFACE_VERTS ) {
|
||||
Error( "MAX_SURFACE_VERTS" );
|
||||
}
|
||||
verts[numVerts] = ds->verts[i];
|
||||
originals[numVerts] = i;
|
||||
numVerts++;
|
||||
|
||||
// check to see if there are any t junctions before the next vert
|
||||
v1 = &ds->verts[i];
|
||||
v2 = &ds->verts[ (i+1) % ds->numVerts ];
|
||||
|
||||
j = (int)ds->verts[i].lightmap[0];
|
||||
if ( j == -1 ) {
|
||||
continue; // degenerate edge
|
||||
}
|
||||
e = &edgeLines[ j ];
|
||||
|
||||
VectorSubtract( v1->xyz, e->origin, delta );
|
||||
start = DotProduct( delta, e->dir );
|
||||
|
||||
VectorSubtract( v2->xyz, e->origin, delta );
|
||||
end = DotProduct( delta, e->dir );
|
||||
|
||||
|
||||
if ( start < end ) {
|
||||
p = e->chain.next;
|
||||
} else {
|
||||
p = e->chain.prev;
|
||||
}
|
||||
|
||||
for ( ; p != &e->chain ; ) {
|
||||
if ( start < end ) {
|
||||
if ( p->intercept > end - ON_EPSILON ) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if ( p->intercept < end + ON_EPSILON ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
( start < end && p->intercept > start + ON_EPSILON ) ||
|
||||
( start > end && p->intercept < start - ON_EPSILON ) ) {
|
||||
// insert this point
|
||||
if ( numVerts == MAX_SURFACE_VERTS ) {
|
||||
Error( "MAX_SURFACE_VERTS" );
|
||||
}
|
||||
|
||||
// take the exact intercept point
|
||||
VectorCopy( p->xyz, verts[ numVerts ].xyz );
|
||||
|
||||
// copy the normal
|
||||
VectorCopy( v1->normal, verts[ numVerts ].normal );
|
||||
|
||||
// interpolate the texture coordinates
|
||||
frac = ( p->intercept - start ) / ( end - start );
|
||||
for ( j = 0 ; j < 2 ; j++ ) {
|
||||
verts[ numVerts ].st[j] = v1->st[j] +
|
||||
frac * ( v2->st[j] - v1->st[j] );
|
||||
}
|
||||
originals[numVerts] = i;
|
||||
numVerts++;
|
||||
counts[i]++;
|
||||
}
|
||||
|
||||
if ( start < end ) {
|
||||
p = p->next;
|
||||
} else {
|
||||
p = p->prev;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
c_addedVerts += numVerts - ds->numVerts;
|
||||
c_totalVerts += numVerts;
|
||||
|
||||
|
||||
// FIXME: check to see if the entire surface degenerated
|
||||
// after snapping
|
||||
|
||||
// rotate the points so that the initial vertex is between
|
||||
// two non-subdivided edges
|
||||
for ( i = 0 ; i < numVerts ; i++ ) {
|
||||
if ( originals[ (i+1) % numVerts ] == originals[ i ] ) {
|
||||
continue;
|
||||
}
|
||||
j = (i + numVerts - 1 ) % numVerts;
|
||||
k = (i + numVerts - 2 ) % numVerts;
|
||||
if ( originals[ j ] == originals[ k ] ) {
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if ( i == 0 ) {
|
||||
// fine the way it is
|
||||
c_natural++;
|
||||
|
||||
ds->numVerts = numVerts;
|
||||
ds->verts = malloc( numVerts * sizeof( *ds->verts ) );
|
||||
memcpy( ds->verts, verts, numVerts * sizeof( *ds->verts ) );
|
||||
|
||||
return;
|
||||
}
|
||||
if ( i == numVerts ) {
|
||||
// create a vertex in the middle to start the fan
|
||||
c_cant++;
|
||||
|
||||
/*
|
||||
memset ( &verts[numVerts], 0, sizeof( verts[numVerts] ) );
|
||||
for ( i = 0 ; i < numVerts ; i++ ) {
|
||||
for ( j = 0 ; j < 10 ; j++ ) {
|
||||
verts[numVerts].xyz[j] += verts[i].xyz[j];
|
||||
}
|
||||
}
|
||||
for ( j = 0 ; j < 10 ; j++ ) {
|
||||
verts[numVerts].xyz[j] /= numVerts;
|
||||
}
|
||||
|
||||
i = numVerts;
|
||||
numVerts++;
|
||||
*/
|
||||
} else {
|
||||
// just rotate the vertexes
|
||||
c_rotate++;
|
||||
|
||||
}
|
||||
|
||||
ds->numVerts = numVerts;
|
||||
ds->verts = malloc( numVerts * sizeof( *ds->verts ) );
|
||||
|
||||
for ( j = 0 ; j < ds->numVerts ; j++ ) {
|
||||
ds->verts[j] = verts[ ( j + i ) % ds->numVerts ];
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
EdgeCompare
|
||||
================
|
||||
*/
|
||||
int EdgeCompare( const void *elem1, const void *elem2 ) {
|
||||
float d1, d2;
|
||||
|
||||
d1 = ((originalEdge_t *)elem1)->length;
|
||||
d2 = ((originalEdge_t *)elem2)->length;
|
||||
|
||||
if ( d1 < d2 ) {
|
||||
return -1;
|
||||
}
|
||||
if ( d2 > d1 ) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
================
|
||||
FixTJunctions
|
||||
|
||||
Call after the surface list has been pruned, but before lightmap allocation
|
||||
================
|
||||
*/
|
||||
void FixTJunctions( entity_t *ent ) {
|
||||
int i;
|
||||
mapDrawSurface_t *ds;
|
||||
int axialEdgeLines;
|
||||
originalEdge_t *e;
|
||||
|
||||
qprintf("----- FixTJunctions -----\n");
|
||||
|
||||
numEdgeLines = 0;
|
||||
numOriginalEdges = 0;
|
||||
|
||||
// add all the edges
|
||||
// this actually creates axial edges, but it
|
||||
// only creates originalEdge_t structures
|
||||
// for non-axial edges
|
||||
for ( i = ent->firstDrawSurf ; i < numMapDrawSurfs ; i++ ) {
|
||||
ds = &mapDrawSurfs[i];
|
||||
if ( ds->patch ) {
|
||||
AddPatchEdges( ds );
|
||||
} else if ( ds->shaderInfo->autosprite || ds->shaderInfo->notjunc || ds->miscModel ) {
|
||||
// miscModels don't add tjunctions
|
||||
} else {
|
||||
AddSurfaceEdges( ds );
|
||||
}
|
||||
}
|
||||
|
||||
axialEdgeLines = numEdgeLines;
|
||||
|
||||
// sort the non-axial edges by length
|
||||
qsort( originalEdges, numOriginalEdges, sizeof(originalEdges[0]), EdgeCompare );
|
||||
|
||||
// add the non-axial edges, longest first
|
||||
// this gives the most accurate edge description
|
||||
for ( i = 0 ; i < numOriginalEdges ; i++ ) {
|
||||
e = &originalEdges[i];
|
||||
e->dv[0]->lightmap[0] = AddEdge( e->dv[0]->xyz, e->dv[1]->xyz, qtrue );
|
||||
}
|
||||
|
||||
qprintf( "%6i axial edge lines\n", axialEdgeLines );
|
||||
qprintf( "%6i non-axial edge lines\n", numEdgeLines - axialEdgeLines );
|
||||
qprintf( "%6i degenerate edges\n", c_degenerateEdges );
|
||||
|
||||
// insert any needed vertexes
|
||||
for ( i = ent->firstDrawSurf ; i < numMapDrawSurfs ; i++ ) {
|
||||
ds = &mapDrawSurfs[i];
|
||||
if ( ds->patch ) {
|
||||
continue;
|
||||
}
|
||||
if ( ds->shaderInfo->autosprite || ds->shaderInfo->notjunc || ds->miscModel ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
FixSurfaceJunctions( ds );
|
||||
}
|
||||
|
||||
qprintf( "%6i verts added for tjunctions\n", c_addedVerts );
|
||||
qprintf( "%6i total verts\n", c_totalVerts );
|
||||
qprintf( "%6i naturally ordered\n", c_natural );
|
||||
qprintf( "%6i rotated orders\n", c_rotate );
|
||||
qprintf( "%6i can't order\n", c_cant );
|
||||
}
|
146
q3map/tree.c
Normal file
146
q3map/tree.c
Normal file
|
@ -0,0 +1,146 @@
|
|||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 1999-2005 Id Software, Inc.
|
||||
|
||||
This file is part of Quake III Arena source code.
|
||||
|
||||
Quake III Arena source code 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.
|
||||
|
||||
Quake III Arena source code 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 Foobar; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
#include "qbsp.h"
|
||||
|
||||
|
||||
extern int c_nodes;
|
||||
|
||||
void RemovePortalFromNode (portal_t *portal, node_t *l);
|
||||
|
||||
node_t *NodeForPoint (node_t *node, vec3_t origin)
|
||||
{
|
||||
plane_t *plane;
|
||||
vec_t d;
|
||||
|
||||
while (node->planenum != PLANENUM_LEAF)
|
||||
{
|
||||
plane = &mapplanes[node->planenum];
|
||||
d = DotProduct (origin, plane->normal) - plane->dist;
|
||||
if (d >= 0)
|
||||
node = node->children[0];
|
||||
else
|
||||
node = node->children[1];
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
=============
|
||||
FreeTreePortals_r
|
||||
=============
|
||||
*/
|
||||
void FreeTreePortals_r (node_t *node)
|
||||
{
|
||||
portal_t *p, *nextp;
|
||||
int s;
|
||||
|
||||
// free children
|
||||
if (node->planenum != PLANENUM_LEAF)
|
||||
{
|
||||
FreeTreePortals_r (node->children[0]);
|
||||
FreeTreePortals_r (node->children[1]);
|
||||
}
|
||||
|
||||
// free portals
|
||||
for (p=node->portals ; p ; p=nextp)
|
||||
{
|
||||
s = (p->nodes[1] == node);
|
||||
nextp = p->next[s];
|
||||
|
||||
RemovePortalFromNode (p, p->nodes[!s]);
|
||||
FreePortal (p);
|
||||
}
|
||||
node->portals = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
FreeTree_r
|
||||
=============
|
||||
*/
|
||||
void FreeTree_r (node_t *node)
|
||||
{
|
||||
// free children
|
||||
if (node->planenum != PLANENUM_LEAF)
|
||||
{
|
||||
FreeTree_r (node->children[0]);
|
||||
FreeTree_r (node->children[1]);
|
||||
}
|
||||
|
||||
// free bspbrushes
|
||||
FreeBrushList (node->brushlist);
|
||||
|
||||
// free the node
|
||||
if (node->volume)
|
||||
FreeBrush (node->volume);
|
||||
|
||||
if (numthreads == 1)
|
||||
c_nodes--;
|
||||
free (node);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=============
|
||||
FreeTree
|
||||
=============
|
||||
*/
|
||||
void FreeTree (tree_t *tree)
|
||||
{
|
||||
FreeTreePortals_r (tree->headnode);
|
||||
FreeTree_r (tree->headnode);
|
||||
free (tree);
|
||||
}
|
||||
|
||||
//===============================================================
|
||||
|
||||
void PrintTree_r (node_t *node, int depth)
|
||||
{
|
||||
int i;
|
||||
plane_t *plane;
|
||||
bspbrush_t *bb;
|
||||
|
||||
for (i=0 ; i<depth ; i++)
|
||||
_printf (" ");
|
||||
if (node->planenum == PLANENUM_LEAF)
|
||||
{
|
||||
if (!node->brushlist)
|
||||
_printf ("NULL\n");
|
||||
else
|
||||
{
|
||||
for (bb=node->brushlist ; bb ; bb=bb->next)
|
||||
_printf ("%i ", bb->original->brushnum);
|
||||
_printf ("\n");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
plane = &mapplanes[node->planenum];
|
||||
_printf ("#%i (%5.2f %5.2f %5.2f):%5.2f\n", node->planenum,
|
||||
plane->normal[0], plane->normal[1], plane->normal[2],
|
||||
plane->dist);
|
||||
PrintTree_r (node->children[0], depth+1);
|
||||
PrintTree_r (node->children[1], depth+1);
|
||||
}
|
1197
q3map/vis.c
Normal file
1197
q3map/vis.c
Normal file
File diff suppressed because it is too large
Load diff
162
q3map/vis.h
Normal file
162
q3map/vis.h
Normal file
|
@ -0,0 +1,162 @@
|
|||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 1999-2005 Id Software, Inc.
|
||||
|
||||
This file is part of Quake III Arena source code.
|
||||
|
||||
Quake III Arena source code 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.
|
||||
|
||||
Quake III Arena source code 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 Foobar; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
// vis.h
|
||||
|
||||
#include "cmdlib.h"
|
||||
#include "mathlib.h"
|
||||
#include "bspfile.h"
|
||||
|
||||
#define MAX_PORTALS 32768
|
||||
|
||||
#define PORTALFILE "PRT1"
|
||||
|
||||
#define ON_EPSILON 0.1
|
||||
|
||||
//#define MREDEBUG
|
||||
|
||||
// seperator caching helps a bit
|
||||
#define SEPERATORCACHE
|
||||
|
||||
// can't have more seperators than the max number of points on a winding
|
||||
#define MAX_SEPERATORS 64
|
||||
|
||||
typedef struct
|
||||
{
|
||||
vec3_t normal;
|
||||
float dist;
|
||||
} plane_t;
|
||||
|
||||
#define MAX_POINTS_ON_WINDING 64
|
||||
#define MAX_POINTS_ON_FIXED_WINDING 12
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int numpoints;
|
||||
vec3_t points[MAX_POINTS_ON_FIXED_WINDING]; // variable sized
|
||||
} winding_t;
|
||||
|
||||
winding_t *NewWinding (int points);
|
||||
void FreeWinding (winding_t *w);
|
||||
winding_t *CopyWinding (winding_t *w);
|
||||
|
||||
|
||||
typedef struct passage_s
|
||||
{
|
||||
struct passage_s *next;
|
||||
byte cansee[1]; //all portals that can be seen through this passage
|
||||
} passage_t;
|
||||
|
||||
typedef enum {stat_none, stat_working, stat_done} vstatus_t;
|
||||
typedef struct
|
||||
{
|
||||
int num;
|
||||
qboolean hint; // true if this portal was created from a hint splitter
|
||||
qboolean removed;
|
||||
plane_t plane; // normal pointing into neighbor
|
||||
int leaf; // neighbor
|
||||
|
||||
vec3_t origin; // for fast clip testing
|
||||
float radius;
|
||||
|
||||
winding_t *winding;
|
||||
vstatus_t status;
|
||||
byte *portalfront; // [portals], preliminary
|
||||
byte *portalflood; // [portals], intermediate
|
||||
byte *portalvis; // [portals], final
|
||||
|
||||
int nummightsee; // bit count on portalflood for sort
|
||||
passage_t *passages; // there are just as many passages as there
|
||||
// are portals in the leaf this portal leads to
|
||||
} vportal_t;
|
||||
|
||||
#define MAX_PORTALS_ON_LEAF 128
|
||||
typedef struct leaf_s
|
||||
{
|
||||
int numportals;
|
||||
int merged;
|
||||
vportal_t *portals[MAX_PORTALS_ON_LEAF];
|
||||
} leaf_t;
|
||||
|
||||
|
||||
typedef struct pstack_s
|
||||
{
|
||||
byte mightsee[MAX_PORTALS/8]; // bit string
|
||||
struct pstack_s *next;
|
||||
leaf_t *leaf;
|
||||
vportal_t *portal; // portal exiting
|
||||
winding_t *source;
|
||||
winding_t *pass;
|
||||
|
||||
winding_t windings[3]; // source, pass, temp in any order
|
||||
int freewindings[3];
|
||||
|
||||
plane_t portalplane;
|
||||
int depth;
|
||||
#ifdef SEPERATORCACHE
|
||||
plane_t seperators[2][MAX_SEPERATORS];
|
||||
int numseperators[2];
|
||||
#endif
|
||||
} pstack_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
vportal_t *base;
|
||||
int c_chains;
|
||||
pstack_t pstack_head;
|
||||
} threaddata_t;
|
||||
|
||||
|
||||
|
||||
extern int numportals;
|
||||
extern int portalclusters;
|
||||
|
||||
extern vportal_t *portals;
|
||||
extern leaf_t *leafs;
|
||||
|
||||
extern int c_portaltest, c_portalpass, c_portalcheck;
|
||||
extern int c_portalskip, c_leafskip;
|
||||
extern int c_vistest, c_mighttest;
|
||||
extern int c_chains;
|
||||
|
||||
extern byte *vismap, *vismap_p, *vismap_end; // past visfile
|
||||
|
||||
extern int testlevel;
|
||||
|
||||
extern byte *uncompressed;
|
||||
|
||||
extern int leafbytes, leaflongs;
|
||||
extern int portalbytes, portallongs;
|
||||
|
||||
|
||||
void LeafFlow (int leafnum);
|
||||
|
||||
|
||||
void BasePortalVis(int portalnum);
|
||||
void BetterPortalVis(int portalnum);
|
||||
void PortalFlow(int portalnum);
|
||||
void PassagePortalFlow(int portalnum);
|
||||
void CreatePassages(int portalnum);
|
||||
void PassageFlow(int portalnum);
|
||||
|
||||
extern vportal_t *sorted_portals[MAX_MAP_PORTALS*2];
|
||||
|
||||
int CountBits (byte *bits, int numbits);
|
1657
q3map/visflow.c
Normal file
1657
q3map/visflow.c
Normal file
File diff suppressed because it is too large
Load diff
418
q3map/writebsp.c
Normal file
418
q3map/writebsp.c
Normal file
|
@ -0,0 +1,418 @@
|
|||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 1999-2005 Id Software, Inc.
|
||||
|
||||
This file is part of Quake III Arena source code.
|
||||
|
||||
Quake III Arena source code 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.
|
||||
|
||||
Quake III Arena source code 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 Foobar; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
#include "qbsp.h"
|
||||
|
||||
/*
|
||||
============
|
||||
EmitShader
|
||||
============
|
||||
*/
|
||||
int EmitShader( const char *shader ) {
|
||||
int i;
|
||||
shaderInfo_t *si;
|
||||
|
||||
if ( !shader ) {
|
||||
shader = "noshader";
|
||||
}
|
||||
|
||||
for ( i = 0 ; i < numShaders ; i++ ) {
|
||||
if ( !Q_stricmp( shader, dshaders[i].shader ) ) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
if ( i == MAX_MAP_SHADERS ) {
|
||||
Error( "MAX_MAP_SHADERS" );
|
||||
}
|
||||
numShaders++;
|
||||
strcpy( dshaders[i].shader, shader );
|
||||
|
||||
si = ShaderInfoForShader( shader );
|
||||
dshaders[i].surfaceFlags = si->surfaceFlags;
|
||||
dshaders[i].contentFlags = si->contents;
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
============
|
||||
EmitPlanes
|
||||
|
||||
There is no oportunity to discard planes, because all of the original
|
||||
brushes will be saved in the map.
|
||||
============
|
||||
*/
|
||||
void EmitPlanes (void)
|
||||
{
|
||||
int i;
|
||||
dplane_t *dp;
|
||||
plane_t *mp;
|
||||
|
||||
mp = mapplanes;
|
||||
for (i=0 ; i<nummapplanes ; i++, mp++)
|
||||
{
|
||||
dp = &dplanes[numplanes];
|
||||
VectorCopy ( mp->normal, dp->normal);
|
||||
dp->dist = mp->dist;
|
||||
numplanes++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
==================
|
||||
EmitLeaf
|
||||
==================
|
||||
*/
|
||||
void EmitLeaf (node_t *node)
|
||||
{
|
||||
dleaf_t *leaf_p;
|
||||
bspbrush_t *b;
|
||||
drawSurfRef_t *dsr;
|
||||
|
||||
// emit a leaf
|
||||
if (numleafs >= MAX_MAP_LEAFS)
|
||||
Error ("MAX_MAP_LEAFS");
|
||||
|
||||
leaf_p = &dleafs[numleafs];
|
||||
numleafs++;
|
||||
|
||||
leaf_p->cluster = node->cluster;
|
||||
leaf_p->area = node->area;
|
||||
|
||||
//
|
||||
// write bounding box info
|
||||
//
|
||||
VectorCopy (node->mins, leaf_p->mins);
|
||||
VectorCopy (node->maxs, leaf_p->maxs);
|
||||
|
||||
//
|
||||
// write the leafbrushes
|
||||
//
|
||||
leaf_p->firstLeafBrush = numleafbrushes;
|
||||
for ( b = node->brushlist ; b ; b = b->next ) {
|
||||
if ( numleafbrushes >= MAX_MAP_LEAFBRUSHES ) {
|
||||
Error( "MAX_MAP_LEAFBRUSHES" );
|
||||
}
|
||||
dleafbrushes[numleafbrushes] = b->original->outputNumber;
|
||||
numleafbrushes++;
|
||||
}
|
||||
leaf_p->numLeafBrushes = numleafbrushes - leaf_p->firstLeafBrush;
|
||||
|
||||
//
|
||||
// write the surfaces visible in this leaf
|
||||
//
|
||||
if ( node->opaque ) {
|
||||
return; // no leaffaces in solids
|
||||
}
|
||||
|
||||
// add the drawSurfRef_t drawsurfs
|
||||
leaf_p->firstLeafSurface = numleafsurfaces;
|
||||
for ( dsr = node->drawSurfReferences ; dsr ; dsr = dsr->nextRef ) {
|
||||
if ( numleafsurfaces >= MAX_MAP_LEAFFACES)
|
||||
Error ("MAX_MAP_LEAFFACES");
|
||||
dleafsurfaces[numleafsurfaces] = dsr->outputNumber;
|
||||
numleafsurfaces++;
|
||||
}
|
||||
|
||||
|
||||
leaf_p->numLeafSurfaces = numleafsurfaces - leaf_p->firstLeafSurface;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
============
|
||||
EmitDrawNode_r
|
||||
============
|
||||
*/
|
||||
int EmitDrawNode_r (node_t *node)
|
||||
{
|
||||
dnode_t *n;
|
||||
int i;
|
||||
|
||||
if (node->planenum == PLANENUM_LEAF)
|
||||
{
|
||||
EmitLeaf (node);
|
||||
return -numleafs;
|
||||
}
|
||||
|
||||
// emit a node
|
||||
if (numnodes == MAX_MAP_NODES)
|
||||
Error ("MAX_MAP_NODES");
|
||||
n = &dnodes[numnodes];
|
||||
numnodes++;
|
||||
|
||||
VectorCopy (node->mins, n->mins);
|
||||
VectorCopy (node->maxs, n->maxs);
|
||||
|
||||
if (node->planenum & 1)
|
||||
Error ("WriteDrawNodes_r: odd planenum");
|
||||
n->planeNum = node->planenum;
|
||||
|
||||
//
|
||||
// recursively output the other nodes
|
||||
//
|
||||
for (i=0 ; i<2 ; i++)
|
||||
{
|
||||
if (node->children[i]->planenum == PLANENUM_LEAF)
|
||||
{
|
||||
n->children[i] = -(numleafs + 1);
|
||||
EmitLeaf (node->children[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
n->children[i] = numnodes;
|
||||
EmitDrawNode_r (node->children[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return n - dnodes;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
|
||||
|
||||
|
||||
/*
|
||||
============
|
||||
SetModelNumbers
|
||||
============
|
||||
*/
|
||||
void SetModelNumbers (void)
|
||||
{
|
||||
int i;
|
||||
int models;
|
||||
char value[10];
|
||||
|
||||
models = 1;
|
||||
for ( i=1 ; i<num_entities ; i++ ) {
|
||||
if ( entities[i].brushes || entities[i].patches ) {
|
||||
sprintf ( value, "*%i", models );
|
||||
models++;
|
||||
SetKeyValue (&entities[i], "model", value);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
SetLightStyles
|
||||
============
|
||||
*/
|
||||
#define MAX_SWITCHED_LIGHTS 32
|
||||
void SetLightStyles (void)
|
||||
{
|
||||
int stylenum;
|
||||
const char *t;
|
||||
entity_t *e;
|
||||
int i, j;
|
||||
char value[10];
|
||||
char lighttargets[MAX_SWITCHED_LIGHTS][64];
|
||||
|
||||
|
||||
// any light that is controlled (has a targetname)
|
||||
// must have a unique style number generated for it
|
||||
|
||||
stylenum = 0;
|
||||
for (i=1 ; i<num_entities ; i++)
|
||||
{
|
||||
e = &entities[i];
|
||||
|
||||
t = ValueForKey (e, "classname");
|
||||
if (Q_strncasecmp (t, "light", 5))
|
||||
continue;
|
||||
t = ValueForKey (e, "targetname");
|
||||
if (!t[0])
|
||||
continue;
|
||||
|
||||
// find this targetname
|
||||
for (j=0 ; j<stylenum ; j++)
|
||||
if (!strcmp (lighttargets[j], t))
|
||||
break;
|
||||
if (j == stylenum)
|
||||
{
|
||||
if (stylenum == MAX_SWITCHED_LIGHTS)
|
||||
Error ("stylenum == MAX_SWITCHED_LIGHTS");
|
||||
strcpy (lighttargets[j], t);
|
||||
stylenum++;
|
||||
}
|
||||
sprintf (value, "%i", 32 + j);
|
||||
SetKeyValue (e, "style", value);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//===========================================================
|
||||
|
||||
/*
|
||||
==================
|
||||
BeginBSPFile
|
||||
==================
|
||||
*/
|
||||
void BeginBSPFile( void ) {
|
||||
// these values may actually be initialized
|
||||
// if the file existed when loaded, so clear them explicitly
|
||||
nummodels = 0;
|
||||
numnodes = 0;
|
||||
numbrushsides = 0;
|
||||
numleafsurfaces = 0;
|
||||
numleafbrushes = 0;
|
||||
|
||||
// leave leaf 0 as an error, because leafs are referenced as
|
||||
// negative number nodes
|
||||
numleafs = 1;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
============
|
||||
EndBSPFile
|
||||
============
|
||||
*/
|
||||
void EndBSPFile( void ) {
|
||||
char path[1024];
|
||||
|
||||
EmitPlanes ();
|
||||
UnparseEntities ();
|
||||
|
||||
// write the map
|
||||
sprintf (path, "%s.bsp", source);
|
||||
_printf ("Writing %s\n", path);
|
||||
WriteBSPFile (path);
|
||||
}
|
||||
|
||||
|
||||
//===========================================================
|
||||
|
||||
/*
|
||||
============
|
||||
EmitBrushes
|
||||
============
|
||||
*/
|
||||
void EmitBrushes ( bspbrush_t *brushes ) {
|
||||
int j;
|
||||
dbrush_t *db;
|
||||
bspbrush_t *b;
|
||||
dbrushside_t *cp;
|
||||
|
||||
for ( b = brushes ; b ; b = b->next ) {
|
||||
if ( numbrushes == MAX_MAP_BRUSHES ) {
|
||||
Error( "MAX_MAP_BRUSHES" );
|
||||
}
|
||||
b->outputNumber = numbrushes;
|
||||
db = &dbrushes[numbrushes];
|
||||
numbrushes++;
|
||||
|
||||
db->shaderNum = EmitShader( b->contentShader->shader );
|
||||
db->firstSide = numbrushsides;
|
||||
|
||||
// don't emit any generated backSide sides
|
||||
db->numSides = 0;
|
||||
for ( j=0 ; j<b->numsides ; j++ ) {
|
||||
if ( b->sides[j].backSide ) {
|
||||
continue;
|
||||
}
|
||||
if ( numbrushsides == MAX_MAP_BRUSHSIDES ) {
|
||||
Error( "MAX_MAP_BRUSHSIDES ");
|
||||
}
|
||||
cp = &dbrushsides[numbrushsides];
|
||||
db->numSides++;
|
||||
numbrushsides++;
|
||||
cp->planeNum = b->sides[j].planenum;
|
||||
cp->shaderNum = EmitShader( b->sides[j].shaderInfo->shader );
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
==================
|
||||
BeginModel
|
||||
==================
|
||||
*/
|
||||
void BeginModel( void ) {
|
||||
dmodel_t *mod;
|
||||
bspbrush_t *b;
|
||||
entity_t *e;
|
||||
vec3_t mins, maxs;
|
||||
parseMesh_t *p;
|
||||
int i;
|
||||
|
||||
if ( nummodels == MAX_MAP_MODELS ) {
|
||||
Error( "MAX_MAP_MODELS" );
|
||||
}
|
||||
mod = &dmodels[nummodels];
|
||||
|
||||
//
|
||||
// bound the brushes
|
||||
//
|
||||
e = &entities[entity_num];
|
||||
|
||||
ClearBounds (mins, maxs);
|
||||
for ( b = e->brushes ; b ; b = b->next ) {
|
||||
if ( !b->numsides ) {
|
||||
continue; // not a real brush (origin brush, etc)
|
||||
}
|
||||
AddPointToBounds (b->mins, mins, maxs);
|
||||
AddPointToBounds (b->maxs, mins, maxs);
|
||||
}
|
||||
|
||||
for ( p = e->patches ; p ; p = p->next ) {
|
||||
for ( i = 0 ; i < p->mesh.width * p->mesh.height ; i++ ) {
|
||||
AddPointToBounds( p->mesh.verts[i].xyz, mins, maxs );
|
||||
}
|
||||
}
|
||||
|
||||
VectorCopy (mins, mod->mins);
|
||||
VectorCopy (maxs, mod->maxs);
|
||||
|
||||
mod->firstSurface = numDrawSurfaces;
|
||||
mod->firstBrush = numbrushes;
|
||||
|
||||
EmitBrushes( e->brushes );
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
==================
|
||||
EndModel
|
||||
==================
|
||||
*/
|
||||
void EndModel( node_t *headnode ) {
|
||||
dmodel_t *mod;
|
||||
|
||||
qprintf ("--- EndModel ---\n");
|
||||
|
||||
mod = &dmodels[nummodels];
|
||||
EmitDrawNode_r (headnode);
|
||||
mod->numSurfaces = numDrawSurfaces - mod->firstSurface;
|
||||
mod->numBrushes = numbrushes - mod->firstBrush;
|
||||
|
||||
nummodels++;
|
||||
}
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue