The GtkRadiant sources as originally released under the GPL license.

This commit is contained in:
Travis Bradshaw
2012-01-31 15:20:35 -06:00
commit 0991a5ce8b
1590 changed files with 431941 additions and 0 deletions

View File

@@ -0,0 +1,12 @@
q3map
*.d
*.o
*.bak
*.BAK
*~
*.ncb
*.plg
*.opt
*.log
Debug
Release

978
tools/quake3/q3map2/brush.c Normal file
View File

@@ -0,0 +1,978 @@
/* -------------------------------------------------------------------------------
Copyright (C) 1999-2006 Id Software, Inc. and contributors.
For a list of contributors, see the accompanying CONTRIBUTORS file.
This file is part of GtkRadiant.
GtkRadiant 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.
GtkRadiant 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 GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
----------------------------------------------------------------------------------
This code has been altered significantly from its original form, to support
several games based on the Quake III Arena engine, in the form of "Q3Map2."
------------------------------------------------------------------------------- */
/* marker */
#define BRUSH_C
/* dependencies */
#include "q3map2.h"
/* -------------------------------------------------------------------------------
functions
------------------------------------------------------------------------------- */
/*
AllocSideRef() - ydnar
allocates and assigns a brush side reference
*/
sideRef_t *AllocSideRef( side_t *side, sideRef_t *next )
{
sideRef_t *sideRef;
/* dummy check */
if( side == NULL )
return next;
/* allocate and return */
sideRef = safe_malloc( sizeof( *sideRef ) );
sideRef->side = side;
sideRef->next = next;
return sideRef;
}
/*
CountBrushList()
counts the number of brushes in a brush linked list
*/
int CountBrushList( brush_t *brushes )
{
int c = 0;
/* count brushes */
for( brushes; brushes != NULL; brushes = brushes->next )
c++;
return c;
}
/*
AllocBrush()
allocates a new brush
*/
brush_t *AllocBrush( int numSides )
{
brush_t *bb;
int c;
/* allocate and clear */
if( numSides <= 0 )
Error( "AllocBrush called with numsides = %d", numSides );
c = (int) &(((brush_t*) 0)->sides[ numSides ]);
bb = safe_malloc( c );
memset( bb, 0, c );
if( numthreads == 1 )
numActiveBrushes++;
/* return it */
return bb;
}
/*
FreeBrush()
frees a single brush and all sides/windings
*/
void FreeBrush( brush_t *b )
{
int i;
/* error check */
if( *((int*) b) == 0xFEFEFEFE )
{
Sys_FPrintf( SYS_VRB, "WARNING: Attempt to free an already freed brush!\n" );
return;
}
/* free brush sides */
for( i = 0; i < b->numsides; i++ )
if( b->sides[i].winding != NULL )
FreeWinding( b->sides[ i ].winding );
/* ydnar: overwrite it */
memset( b, 0xFE, (int) &(((brush_t*) 0)->sides[ b->numsides ]) );
*((int*) b) = 0xFEFEFEFE;
/* free it */
free( b );
if( numthreads == 1 )
numActiveBrushes--;
}
/*
FreeBrushList()
frees a linked list of brushes
*/
void FreeBrushList( brush_t *brushes )
{
brush_t *next;
/* walk brush list */
for( brushes; brushes != NULL; brushes = next )
{
next = brushes->next;
FreeBrush( brushes );
}
}
/*
CopyBrush()
duplicates the brush, sides, and windings
*/
brush_t *CopyBrush( brush_t *brush )
{
brush_t *newBrush;
int size;
int i;
/* copy brush */
size = (int) &(((brush_t*) 0)->sides[ brush->numsides ]);
newBrush = AllocBrush( brush->numsides );
memcpy( newBrush, brush, size );
/* ydnar: nuke linked list */
newBrush->next = NULL;
/* copy sides */
for( i = 0; i < brush->numsides; i++ )
{
if( brush->sides[ i ].winding != NULL )
newBrush->sides[ i ].winding = CopyWinding( brush->sides[ i ].winding );
}
/* return it */
return newBrush;
}
/*
BoundBrush()
sets the mins/maxs based on the windings
returns false if the brush doesn't enclose a valid volume
*/
qboolean BoundBrush( brush_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 == NULL )
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;
}
/*
SnapWeldVector() - ydnar
welds two vec3_t's into a third, taking into account nearest-to-integer
instead of averaging
*/
#define SNAP_EPSILON 0.01
void SnapWeldVector( vec3_t a, vec3_t b, vec3_t out )
{
int i;
vec_t ai, bi, outi;
/* dummy check */
if( a == NULL || b == NULL || out == NULL )
return;
/* do each element */
for( i = 0; i < 3; i++ )
{
/* round to integer */
ai = Q_rint( a[ i ] );
bi = Q_rint( a[ i ] );
/* prefer exact integer */
if( ai == a[ i ] )
out[ i ] = a[ i ];
else if( bi == b[ i ] )
out[ i ] = b[ i ];
/* use nearest */
else if( fabs( ai - a[ i ] ) < fabs( bi < b[ i ] ) )
out[ i ] = a[ i ];
else
out[ i ] = b[ i ];
/* snap */
outi = Q_rint( out[ i ] );
if( fabs( outi - out[ i ] ) <= SNAP_EPSILON )
out[ i ] = outi;
}
}
/*
FixWinding() - ydnar
removes degenerate edges from a winding
returns qtrue if the winding is valid
*/
#define DEGENERATE_EPSILON 0.1
qboolean FixWinding( winding_t *w )
{
qboolean valid = qtrue;
int i, j, k;
vec3_t vec;
float dist;
/* dummy check */
if( !w )
return qfalse;
/* check all verts */
for( i = 0; i < w->numpoints; i++ )
{
/* don't remove points if winding is a triangle */
if( w->numpoints == 3 )
return valid;
/* get second point index */
j = (i + 1) % w->numpoints;
/* degenerate edge? */
VectorSubtract( w->p[ i ], w->p[ j ], vec );
dist = VectorLength( vec );
if( dist < DEGENERATE_EPSILON )
{
valid = qfalse;
//Sys_FPrintf( SYS_VRB, "WARNING: Degenerate winding edge found, fixing...\n" );
/* create an average point (ydnar 2002-01-26: using nearest-integer weld preference) */
SnapWeldVector( w->p[ i ], w->p[ j ], vec );
VectorCopy( vec, w->p[ i ] );
//VectorAdd( w->p[ i ], w->p[ j ], vec );
//VectorScale( vec, 0.5, w->p[ i ] );
/* move the remaining verts */
for( k = i + 2; k < w->numpoints; k++ )
{
VectorCopy( w->p[ k ], w->p[ k - 1 ] );
}
w->numpoints--;
}
}
/* one last check and return */
if( w->numpoints < 3 )
valid = qfalse;
return valid;
}
/*
CreateBrushWindings()
makes basewindigs for sides and mins/maxs for the brush
returns false if the brush doesn't enclose a valid volume
*/
qboolean CreateBrushWindings( brush_t *brush )
{
int i, j;
winding_t *w;
side_t *side;
plane_t *plane;
/* walk the list of brush sides */
for( i = 0; i < brush->numsides; i++ )
{
/* get side and plane */
side = &brush->sides[ i ];
plane = &mapplanes[ side->planenum ];
/* make huge winding */
w = BaseWindingForPlane( plane->normal, plane->dist );
/* walk the list of brush sides */
for( j = 0; j < brush->numsides && w != NULL; 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;
plane = &mapplanes[ brush->sides[ j ].planenum ^ 1 ];
ChopWindingInPlace( &w, plane->normal, plane->dist, 0 ); // CLIP_EPSILON );
/* ydnar: fix broken windings that would generate trifans */
FixWinding( w );
}
/* set side winding */
side->winding = w;
}
/* find brush bounds */
return BoundBrush( brush );
}
/*
==================
BrushFromBounds
Creates a new axial brush
==================
*/
brush_t *BrushFromBounds (vec3_t mins, vec3_t maxs)
{
brush_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, 1, (vec3_t*) &maxs );
normal[i] = -1;
dist = -mins[i];
b->sides[3+i].planenum = FindFloatPlane (normal, dist, 1, (vec3_t*) &mins );
}
CreateBrushWindings (b);
return b;
}
/*
==================
BrushVolume
==================
*/
vec_t BrushVolume (brush_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()
writes a map with the split bsp brushes
*/
void WriteBSPBrushMap( char *name, brush_t *list )
{
FILE *f;
side_t *s;
int i;
winding_t *w;
/* note it */
Sys_Printf( "Writing %s\n", name );
/* open the map file */
f = fopen( name, "wb" );
if( f == NULL )
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()
adds brush reference to any intersecting bsp leafnode
*/
int FilterBrushIntoTree_r( brush_t *b, node_t *node )
{
brush_t *front, *back;
int c;
/* dummy check */
if( b == NULL )
return 0;
/* add it to the leaf list */
if( node->planenum == PLANENUM_LEAF )
{
/* something somewhere is hammering brushlist */
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->compileFlags & C_AREAPORTAL )
{
if( !node->opaque )
node->areaportal = qtrue;
}
}
return 1;
}
/* split it by the node plane */
c = b->numsides;
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 )
{
brush_t *b, *newb;
int r;
int c_unique, c_clusters;
int i;
/* note it */
Sys_FPrintf( SYS_VRB, "--- FilterDetailBrushesIntoTree ---\n" );
/* walk the list of brushes */
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;
}
}
}
/* emit some statistics */
Sys_FPrintf( SYS_VRB, "%9d detail brushes\n", c_unique );
Sys_FPrintf( SYS_VRB, "%9d cluster references\n", c_clusters );
}
/*
=====================
FilterStructuralBrushesIntoTree
Mark the leafs as opaque and areaportals
=====================
*/
void FilterStructuralBrushesIntoTree( entity_t *e, tree_t *tree ) {
brush_t *b, *newb;
int r;
int c_unique, c_clusters;
int i;
Sys_FPrintf (SYS_VRB, "--- 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;
}
}
}
}
/* emit some statistics */
Sys_FPrintf( SYS_VRB, "%9d structural brushes\n", c_unique );
Sys_FPrintf( SYS_VRB, "%9d cluster references\n", c_clusters );
}
/*
================
AllocTree
================
*/
tree_t *AllocTree (void)
{
tree_t *tree;
tree = safe_malloc(sizeof(*tree));
memset (tree, 0, sizeof(*tree));
ClearBounds (tree->mins, tree->maxs);
return tree;
}
/*
================
AllocNode
================
*/
node_t *AllocNode (void)
{
node_t *node;
node = safe_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 (brush_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( brush_t *brush, int planenum, brush_t **front, brush_t **back )
{
brush_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 = NULL;
*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++)
{
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 ) )
Sys_FPrintf( SYS_VRB,"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( brush_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;
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)
{
Sys_FPrintf (SYS_VRB,"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])
Sys_FPrintf (SYS_VRB,"split removed brush\n");
else
Sys_FPrintf (SYS_VRB,"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;
// Sys_FPrintf (SYS_VRB,"tiny volume after clip\n");
}
}
}
*front = b[0];
*back = b[1];
}

View File

@@ -0,0 +1,81 @@
/* -------------------------------------------------------------------------------
Copyright (C) 1999-2006 Id Software, Inc. and contributors.
For a list of contributors, see the accompanying CONTRIBUTORS file.
This file is part of GtkRadiant.
GtkRadiant 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.
GtkRadiant 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 GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
----------------------------------------------------------------------------------
This code has been altered significantly from its original form, to support
several games based on the Quake III Arena engine, in the form of "Q3Map2."
------------------------------------------------------------------------------- */
/* marker */
#define BRUSH_PRIMIT_C
/* dependencies */
#include "q3map2.h"
/* -------------------------------------------------------------------------------
functions
------------------------------------------------------------------------------- */
/*
ComputeAxisBase()
computes the base texture axis for brush primitive texturing
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 );
}

858
tools/quake3/q3map2/bsp.c Normal file
View File

@@ -0,0 +1,858 @@
/* -------------------------------------------------------------------------------
Copyright (C) 1999-2006 Id Software, Inc. and contributors.
For a list of contributors, see the accompanying CONTRIBUTORS file.
This file is part of GtkRadiant.
GtkRadiant 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.
GtkRadiant 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 GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
----------------------------------------------------------------------------------
This code has been altered significantly from its original form, to support
several games based on the Quake III Arena engine, in the form of "Q3Map2."
------------------------------------------------------------------------------- */
/* marker */
#define BSP_C
/* dependencies */
#include "q3map2.h"
/* -------------------------------------------------------------------------------
functions
------------------------------------------------------------------------------- */
/*
SetCloneModelNumbers() - ydnar
sets the model numbers for brush entities
*/
static void SetCloneModelNumbers( void )
{
int i, j;
int models;
char modelValue[ 10 ];
const char *value, *value2, *value3;
/* start with 1 (worldspawn is model 0) */
models = 1;
for( i = 1; i < numEntities; i++ )
{
/* only entities with brushes or patches get a model number */
if( entities[ i ].brushes == NULL && entities[ i ].patches == NULL )
continue;
/* is this a clone? */
value = ValueForKey( &entities[ i ], "_clone" );
if( value[ 0 ] != '\0' )
continue;
/* add the model key */
sprintf( modelValue, "*%d", models );
SetKeyValue( &entities[ i ], "model", modelValue );
/* increment model count */
models++;
}
/* fix up clones */
for( i = 1; i < numEntities; i++ )
{
/* only entities with brushes or patches get a model number */
if( entities[ i ].brushes == NULL && entities[ i ].patches == NULL )
continue;
/* is this a clone? */
value = ValueForKey( &entities[ i ], "_ins" );
if( value[ 0 ] == '\0' )
value = ValueForKey( &entities[ i ], "_instance" );
if( value[ 0 ] == '\0' )
value = ValueForKey( &entities[ i ], "_clone" );
if( value[ 0 ] == '\0' )
continue;
/* find an entity with matching clone name */
for( j = 0; j < numEntities; j++ )
{
/* is this a clone parent? */
value2 = ValueForKey( &entities[ j ], "_clonename" );
if( value2[ 0 ] == '\0' )
continue;
/* do they match? */
if( strcmp( value, value2 ) == 0 )
{
/* get the model num */
value3 = ValueForKey( &entities[ j ], "model" );
if( value3[ 0 ] == '\0' )
{
Sys_Printf( "WARNING: Cloned entity %s referenced entity without model\n", value2 );
continue;
}
models = atoi( &value2[ 1 ] );
/* add the model key */
sprintf( modelValue, "*%d", models );
SetKeyValue( &entities[ i ], "model", modelValue );
/* nuke the brushes/patches for this entity (fixme: leak!) */
entities[ i ].brushes = NULL;
entities[ i ].patches = NULL;
}
}
}
}
/*
FixBrushSides() - ydnar
matches brushsides back to their appropriate drawsurface and shader
*/
static void FixBrushSides( entity_t *e )
{
int i;
mapDrawSurface_t *ds;
sideRef_t *sideRef;
bspBrushSide_t *side;
/* note it */
Sys_FPrintf( SYS_VRB, "--- FixBrushSides ---\n" );
/* walk list of drawsurfaces */
for( i = e->firstDrawSurf; i < numMapDrawSurfs; i++ )
{
/* get surface and try to early out */
ds = &mapDrawSurfs[ i ];
if( ds->outputNum < 0 )
continue;
/* walk sideref list */
for( sideRef = ds->sideRef; sideRef != NULL; sideRef = sideRef->next )
{
/* get bsp brush side */
if( sideRef->side == NULL || sideRef->side->outputNum < 0 )
continue;
side = &bspBrushSides[ sideRef->side->outputNum ];
/* set drawsurface */
side->surfaceNum = ds->outputNum;
//% Sys_FPrintf( SYS_VRB, "DS: %7d Side: %7d ", ds->outputNum, sideRef->side->outputNum );
/* set shader */
if( strcmp( bspShaders[ side->shaderNum ].shader, ds->shaderInfo->shader ) )
{
//% Sys_FPrintf( SYS_VRB, "Remapping %s to %s\n", bspShaders[ side->shaderNum ].shader, ds->shaderInfo->shader );
side->shaderNum = EmitShader( ds->shaderInfo->shader, &ds->shaderInfo->contentFlags, &ds->shaderInfo->surfaceFlags );
}
}
}
}
/*
ProcessWorldModel()
creates a full bsp + surfaces for the worldspawn entity
*/
void ProcessWorldModel( void )
{
int i, s;
entity_t *e;
tree_t *tree;
face_t *faces;
qboolean ignoreLeaks, leaked;
xmlNodePtr polyline, leaknode;
char level[ 2 ], shader[ 1024 ];
const char *value;
/* sets integer blockSize from worldspawn "_blocksize" key if it exists */
value = ValueForKey( &entities[ 0 ], "_blocksize" );
if( value[ 0 ] == '\0' )
value = ValueForKey( &entities[ 0 ], "blocksize" );
if( value[ 0 ] == '\0' )
value = ValueForKey( &entities[ 0 ], "chopsize" ); /* sof2 */
if( value[ 0 ] != '\0' )
{
/* scan 3 numbers */
s = sscanf( value, "%d %d %d", &blockSize[ 0 ], &blockSize[ 1 ], &blockSize[ 2 ] );
/* handle legacy case */
if( s == 1 )
{
blockSize[ 1 ] = blockSize[ 0 ];
blockSize[ 2 ] = blockSize[ 0 ];
}
}
Sys_Printf( "block size = { %d %d %d }\n", blockSize[ 0 ], blockSize[ 1 ], blockSize[ 2 ] );
/* sof2: ignore leaks? */
value = ValueForKey( &entities[ 0 ], "_ignoreleaks" ); /* ydnar */
if( value[ 0 ] == '\0' )
value = ValueForKey( &entities[ 0 ], "ignoreleaks" );
if( value[ 0 ] == '1' )
ignoreLeaks = qtrue;
else
ignoreLeaks = qfalse;
/* begin worldspawn model */
BeginModel();
e = &entities[ 0 ];
e->firstDrawSurf = 0;
/* ydnar: gs mods */
ClearMetaTriangles();
/* 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 ) || ignoreLeaks )
{
/* 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 );
/* build a visible face tree */
faces = MakeVisibleBSPFaceList( entities[ 0 ].brushes );
FreeTree( tree );
tree = FaceBSP( faces );
MakeTreePortals( tree );
FilterStructuralBrushesIntoTree( e, tree );
leaked = qfalse;
/* ydnar: flood again for skybox */
if( skyboxPresent )
FloodEntities( tree );
}
else
{
Sys_FPrintf( SYS_NOXML, "**********************\n" );
Sys_FPrintf( SYS_NOXML, "******* leaked *******\n" );
Sys_FPrintf( SYS_NOXML, "**********************\n" );
polyline = LeakFile( tree );
leaknode = xmlNewNode( NULL, "message" );
xmlNodeSetContent( leaknode, "MAP LEAKED\n" );
xmlAddChild( leaknode, polyline );
level[0] = (int) '0' + SYS_ERR;
level[1] = 0;
xmlSetProp( leaknode, "level", (char*) &level );
xml_SendNode( leaknode );
if( leaktest )
{
Sys_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 );
/* flood from entities */
FloodAreas( tree );
/* create drawsurfs for triangle models */
AddTriangleModels( e );
/* create drawsurfs for surface models */
AddEntitySurfaceModels( e );
/* generate bsp brushes from map brushes */
EmitBrushes( e->brushes, &e->firstBrush, &e->numBrushes );
/* add references to the detail brushes */
FilterDetailBrushesIntoTree( e, tree );
/* drawsurfs that cross fog boundaries will need to be split along the fog boundary */
if( !nofog )
FogDrawSurfaces( e );
/* subdivide each drawsurf as required by shader tesselation */
if( !nosubdivide )
SubdivideFaceSurfaces( e, tree );
/* add in any vertexes required to fix t-junctions */
if( !notjunc )
FixTJunctions( e );
/* ydnar: classify the surfaces */
ClassifyEntitySurfaces( e );
/* ydnar: project decals */
MakeEntityDecals( e );
/* ydnar: meta surfaces */
MakeEntityMetaTriangles( e );
SmoothMetaTriangles();
FixMetaTJunctions();
MergeMetaTriangles();
/* ydnar: debug portals */
if( debugPortals )
MakeDebugPortalSurfs( tree );
/* ydnar: fog hull */
value = ValueForKey( &entities[ 0 ], "_foghull" );
if( value[ 0 ] != '\0' )
{
sprintf( shader, "textures/%s", value );
MakeFogHullSurfs( e, tree, shader );
}
/* ydnar: bug 645: do flares for lights */
for( i = 0; i < numEntities && emitFlares; i++ )
{
entity_t *light, *target;
const char *value, *flareShader;
vec3_t origin, targetOrigin, normal, color;
int lightStyle;
/* get light */
light = &entities[ i ];
value = ValueForKey( light, "classname" );
if( !strcmp( value, "light" ) )
{
/* get flare shader */
flareShader = ValueForKey( light, "_flareshader" );
value = ValueForKey( light, "_flare" );
if( flareShader[ 0 ] != '\0' || value[ 0 ] != '\0' )
{
/* get specifics */
GetVectorForKey( light, "origin", origin );
GetVectorForKey( light, "_color", color );
lightStyle = IntForKey( light, "_style" );
if( lightStyle == 0 )
lightStyle = IntForKey( light, "style" );
/* handle directional spotlights */
value = ValueForKey( light, "target" );
if( value[ 0 ] != '\0' )
{
/* get target light */
target = FindTargetEntity( value );
if( target != NULL )
{
GetVectorForKey( target, "origin", targetOrigin );
VectorSubtract( targetOrigin, origin, normal );
VectorNormalize( normal, normal );
}
}
else
//% VectorClear( normal );
VectorSet( normal, 0, 0, -1 );
/* create the flare surface (note shader defaults automatically) */
DrawSurfaceForFlare( mapEntityNum, origin, normal, color, (char*) flareShader, lightStyle );
}
}
}
/* add references to the final drawsurfs in the apropriate clusters */
FilterDrawsurfsIntoTree( e, tree );
/* match drawsurfaces back to original brushsides (sof2) */
FixBrushSides( e );
/* finish */
EndModel( e, tree->headnode );
FreeTree( tree );
}
/*
ProcessSubModel()
creates bsp + surfaces for other brush models
*/
void ProcessSubModel( void )
{
entity_t *e;
tree_t *tree;
brush_t *b, *bc;
node_t *node;
/* start a brush model */
BeginModel();
e = &entities[ mapEntityNum ];
e->firstDrawSurf = numMapDrawSurfs;
/* ydnar: gs mods */
ClearMetaTriangles();
/* check for patches with adjacent edges that need to lod together */
PatchMapDrawSurfs( e );
/* allocate a tree */
node = AllocNode();
node->planenum = PLANENUM_LEAF;
tree = AllocTree();
tree->headnode = node;
/* add the sides to the tree */
ClipSidesIntoTree( e, tree );
/* ydnar: create drawsurfs for triangle models */
AddTriangleModels( e );
/* create drawsurfs for surface models */
AddEntitySurfaceModels( e );
/* generate bsp brushes from map brushes */
EmitBrushes( e->brushes, &e->firstBrush, &e->numBrushes );
/* just put all the brushes in headnode */
for( b = e->brushes; b; b = b->next )
{
bc = CopyBrush( b );
bc->next = node->brushlist;
node->brushlist = bc;
}
/* subdivide each drawsurf as required by shader tesselation */
if( !nosubdivide )
SubdivideFaceSurfaces( e, tree );
/* add in any vertexes required to fix t-junctions */
if( !notjunc )
FixTJunctions( e );
/* ydnar: classify the surfaces and project lightmaps */
ClassifyEntitySurfaces( e );
/* ydnar: project decals */
MakeEntityDecals( e );
/* ydnar: meta surfaces */
MakeEntityMetaTriangles( e );
SmoothMetaTriangles();
FixMetaTJunctions();
MergeMetaTriangles();
/* add references to the final drawsurfs in the apropriate clusters */
FilterDrawsurfsIntoTree( e, tree );
/* match drawsurfaces back to original brushsides (sof2) */
FixBrushSides( e );
/* finish */
EndModel( e, node );
FreeTree( tree );
}
/*
ProcessModels()
process world + other models into the bsp
*/
void ProcessModels( void )
{
qboolean oldVerbose;
entity_t *entity;
/* preserve -v setting */
oldVerbose = verbose;
/* start a new bsp */
BeginBSPFile();
/* create map fogs */
CreateMapFogs();
/* walk entity list */
for( mapEntityNum = 0; mapEntityNum < numEntities; mapEntityNum++ )
{
/* get entity */
entity = &entities[ mapEntityNum ];
if( entity->brushes == NULL && entity->patches == NULL )
continue;
/* process the model */
Sys_FPrintf( SYS_VRB, "############### model %i ###############\n", numBSPModels );
if( mapEntityNum == 0 )
ProcessWorldModel();
else
ProcessSubModel();
/* potentially turn off the deluge of text */
verbose = verboseEntities;
}
/* restore -v setting */
verbose = oldVerbose;
/* write fogs */
EmitFogs();
}
/*
OnlyEnts()
this is probably broken unless teamed with a radiant version that preserves entity order
*/
void OnlyEnts( void )
{
char out[ 1024 ];
/* note it */
Sys_Printf( "--- OnlyEnts ---\n" );
sprintf( out, "%s.bsp", source );
LoadBSPFile( out );
numEntities = 0;
LoadShaderInfo();
LoadMapFile( name, qfalse );
SetModelNumbers();
SetLightStyles();
numBSPEntities = numEntities;
UnparseEntities();
WriteBSPFile( out );
}
/*
BSPMain() - ydnar
handles creation of a bsp from a map file
*/
int BSPMain( int argc, char **argv )
{
int i;
char path[ 1024 ], tempSource[ 1024 ];
qboolean onlyents = qfalse;
/* note it */
Sys_Printf( "--- BSP ---\n" );
SetDrawSurfacesBuffer();
mapDrawSurfs = safe_malloc( sizeof( mapDrawSurface_t ) * MAX_MAP_DRAW_SURFS );
memset( mapDrawSurfs, 0, sizeof( mapDrawSurface_t ) * MAX_MAP_DRAW_SURFS );
numMapDrawSurfs = 0;
tempSource[ 0 ] = '\0';
/* set standard game flags */
maxSurfaceVerts = game->maxSurfaceVerts;
maxSurfaceIndexes = game->maxSurfaceIndexes;
emitFlares = game->emitFlares;
/* process arguments */
for( i = 1; i < (argc - 1); i++ )
{
if( !strcmp( argv[ i ], "-onlyents" ) )
{
Sys_Printf( "Running entity-only compile\n" );
onlyents = qtrue;
}
else if( !strcmp( argv[ i ], "-tempname" ) )
strcpy( tempSource, argv[ ++i ] );
else if( !strcmp( argv[ i ], "-tmpout" ) )
strcpy( outbase, "/tmp" );
else if( !strcmp( argv[ i ], "-nowater" ) )
{
Sys_Printf( "Disabling water\n" );
nowater = qtrue;
}
else if( !strcmp( argv[ i ], "-nodetail" ) )
{
Sys_Printf( "Ignoring detail brushes\n") ;
nodetail = qtrue;
}
else if( !strcmp( argv[ i ], "-fulldetail" ) )
{
Sys_Printf( "Turning detail brushes into structural brushes\n" );
fulldetail = qtrue;
}
else if( !strcmp( argv[ i ], "-nofog" ) )
{
Sys_Printf( "Fog volumes disabled\n" );
nofog = qtrue;
}
else if( !strcmp( argv[ i ], "-nosubdivide" ) )
{
Sys_Printf( "Disabling brush face subdivision\n" );
nosubdivide = qtrue;
}
else if( !strcmp( argv[ i ], "-leaktest" ) )
{
Sys_Printf( "Leaktest enabled\n" );
leaktest = qtrue;
}
else if( !strcmp( argv[ i ], "-verboseentities" ) )
{
Sys_Printf( "Verbose entities enabled\n" );
verboseEntities = qtrue;
}
else if( !strcmp( argv[ i ], "-nocurves" ) )
{
Sys_Printf( "Ignoring curved surfaces (patches)\n" );
noCurveBrushes = qtrue;
}
else if( !strcmp( argv[ i ], "-notjunc" ) )
{
Sys_Printf( "T-junction fixing disabled\n" );
notjunc = qtrue;
}
else if( !strcmp( argv[ i ], "-fakemap" ) )
{
Sys_Printf( "Generating fakemap.map\n" );
fakemap = qtrue;
}
else if( !strcmp( argv[ i ], "-samplesize" ) )
{
sampleSize = atoi( argv[ i + 1 ] );
if( sampleSize < 1 )
sampleSize = 1;
i++;
Sys_Printf( "Lightmap sample size set to %dx%d units\n", sampleSize, sampleSize );
}
else if( !strcmp( argv[ i ], "-custinfoparms") )
{
Sys_Printf( "Custom info parms enabled\n" );
useCustomInfoParms = qtrue;
}
/* sof2 args */
else if( !strcmp( argv[ i ], "-rename" ) )
{
Sys_Printf( "Appending _bsp suffix to misc_model shaders (SOF2)\n" );
renameModelShaders = qtrue;
}
/* ydnar args */
else if( !strcmp( argv[ i ], "-ne" ) )
{
normalEpsilon = atof( argv[ i + 1 ] );
i++;
Sys_Printf( "Normal epsilon set to %f\n", normalEpsilon );
}
else if( !strcmp( argv[ i ], "-de" ) )
{
distanceEpsilon = atof( argv[ i + 1 ] );
i++;
Sys_Printf( "Distance epsilon set to %f\n", distanceEpsilon );
}
else if( !strcmp( argv[ i ], "-mv" ) )
{
maxLMSurfaceVerts = atoi( argv[ i + 1 ] );
if( maxLMSurfaceVerts < 3 )
maxLMSurfaceVerts = 3;
if( maxLMSurfaceVerts > maxSurfaceVerts )
maxSurfaceVerts = maxLMSurfaceVerts;
i++;
Sys_Printf( "Maximum lightmapped surface vertex count set to %d\n", maxLMSurfaceVerts );
}
else if( !strcmp( argv[ i ], "-mi" ) )
{
maxSurfaceIndexes = atoi( argv[ i + 1 ] );
if( maxSurfaceIndexes < 3 )
maxSurfaceIndexes = 3;
i++;
Sys_Printf( "Maximum per-surface index count set to %d\n", maxSurfaceIndexes );
}
else if( !strcmp( argv[ i ], "-np" ) )
{
npDegrees = atof( argv[ i + 1 ] );
if( npDegrees < 0.0f )
shadeAngleDegrees = 0.0f;
else if( npDegrees > 0.0f )
Sys_Printf( "Forcing nonplanar surfaces with a breaking angle of %f degrees\n", npDegrees );
i++;
}
else if( !strcmp( argv[ i ], "-snap" ) )
{
bevelSnap = atoi( argv[ i + 1 ]);
if( bevelSnap < 0 )
bevelSnap = 0;
i++;
if( bevelSnap > 0 )
Sys_Printf( "Snapping brush bevel planes to %d units\n", bevelSnap );
}
else if( !strcmp( argv[ i ], "-texrange" ) )
{
texRange = atoi( argv[ i + 1 ]);
if( texRange < 0 )
texRange = 0;
i++;
Sys_Printf( "Limiting per-surface texture range to %d texels\n", texRange );
}
else if( !strcmp( argv[ i ], "-nohint" ) )
{
Sys_Printf( "Hint brushes disabled\n" );
noHint = qtrue;
}
else if( !strcmp( argv[ i ], "-flat" ) )
{
Sys_Printf( "Flatshading enabled\n" );
flat = qtrue;
}
else if( !strcmp( argv[ i ], "-meta" ) )
{
Sys_Printf( "Creating meta surfaces from brush faces\n" );
meta = qtrue;
}
else if( !strcmp( argv[ i ], "-patchmeta" ) )
{
Sys_Printf( "Creating meta surfaces from patches\n" );
patchMeta = qtrue;
}
else if( !strcmp( argv[ i ], "-flares" ) )
{
Sys_Printf( "Flare surfaces enabled\n" );
emitFlares = qtrue;
}
else if( !strcmp( argv[ i ], "-noflares" ) )
{
Sys_Printf( "Flare surfaces disabled\n" );
emitFlares = qfalse;
}
else if( !strcmp( argv[ i ], "-skyfix" ) )
{
Sys_Printf( "GL_CLAMP sky fix/hack/workaround enabled\n" );
skyFixHack = qtrue;
}
else if( !strcmp( argv[ i ], "-debugsurfaces" ) )
{
Sys_Printf( "emitting debug surfaces\n" );
debugSurfaces = qtrue;
}
else if( !strcmp( argv[ i ], "-debuginset" ) )
{
Sys_Printf( "Debug surface triangle insetting enabled\n" );
debugInset = qtrue;
}
else if( !strcmp( argv[ i ], "-debugportals" ) )
{
Sys_Printf( "Debug portal surfaces enabled\n" );
debugPortals = qtrue;
}
else if( !strcmp( argv[ i ], "-bsp" ) )
Sys_Printf( "-bsp argument unnecessary\n" );
else
Sys_Printf( "WARNING: Unknown option \"%s\"\n", argv[ i ] );
}
/* fixme: print more useful usage here */
if( i != (argc - 1) )
Error( "usage: q3map [options] mapfile" );
/* copy source name */
strcpy( source, ExpandArg( argv[ i ] ) );
StripExtension( source );
/* ydnar: set default sample size */
SetDefaultSampleSize( sampleSize );
/* delete portal, line and surface files */
sprintf( path, "%s.prt", source );
remove( path );
sprintf( path, "%s.lin", source );
remove( path );
//% sprintf( path, "%s.srf", source ); /* ydnar */
//% remove( path );
/* expand mapname */
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;
}
/* load shaders */
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, qfalse );
else
LoadMapFile( name, qfalse );
/* ydnar: decal setup */
ProcessDecals();
/* ydnar: cloned brush model entities */
SetCloneModelNumbers();
/* process world and submodels */
ProcessModels();
/* set light styles from targetted light entities */
SetLightStyles();
/* finish and write bsp */
EndBSPFile();
/* remove temp map source file if appropriate */
if( strlen( tempSource ) > 0)
remove( tempSource );
/* return to sender */
return 0;
}

View File

@@ -0,0 +1,835 @@
/* -------------------------------------------------------------------------------
Copyright (C) 1999-2006 Id Software, Inc. and contributors.
For a list of contributors, see the accompanying CONTRIBUTORS file.
This file is part of GtkRadiant.
GtkRadiant 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.
GtkRadiant 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 GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
----------------------------------------------------------------------------------
This code has been altered significantly from its original form, to support
several games based on the Quake III Arena engine, in the form of "Q3Map2."
------------------------------------------------------------------------------- */
/* marker */
#define BSPFILE_ABSTRACT_C
/* dependencies */
#include "q3map2.h"
/* -------------------------------------------------------------------------------
this file was copied out of the common directory in order to not break
compatibility with the q3map 1.x tree. it was moved out in order to support
the raven bsp format (RBSP) used in soldier of fortune 2 and jedi knight 2.
since each game has its own set of particular features, the data structures
below no longer directly correspond to the binary format of a particular game.
the translation will be done at bsp load/save time to keep any sort of
special-case code messiness out of the rest of the program.
------------------------------------------------------------------------------- */
/* FIXME: remove the functions below that handle memory management of bsp file chunks */
int numBSPDrawVertsBuffer = 0;
void IncDrawVerts()
{
numBSPDrawVerts++;
if(bspDrawVerts == 0)
{
numBSPDrawVertsBuffer = MAX_MAP_DRAW_VERTS / 37;
bspDrawVerts = safe_malloc_info(sizeof(bspDrawVert_t) * numBSPDrawVertsBuffer, "IncDrawVerts");
}
else if(numBSPDrawVerts > numBSPDrawVertsBuffer)
{
numBSPDrawVertsBuffer *= 3; // multiply by 1.5
numBSPDrawVertsBuffer /= 2;
if(numBSPDrawVertsBuffer > MAX_MAP_DRAW_VERTS)
numBSPDrawVertsBuffer = MAX_MAP_DRAW_VERTS;
bspDrawVerts = realloc(bspDrawVerts, sizeof(bspDrawVert_t) * numBSPDrawVertsBuffer);
if(!bspDrawVerts)
Error( "realloc() failed (IncDrawVerts)");
}
memset(bspDrawVerts + (numBSPDrawVerts - 1), 0, sizeof(bspDrawVert_t));
}
void SetDrawVerts(int n)
{
if(bspDrawVerts != 0)
free(bspDrawVerts);
numBSPDrawVerts = n;
numBSPDrawVertsBuffer = numBSPDrawVerts;
bspDrawVerts = safe_malloc_info(sizeof(bspDrawVert_t) * numBSPDrawVertsBuffer, "IncDrawVerts");
memset(bspDrawVerts, 0, n * sizeof(bspDrawVert_t));
}
int numBSPDrawSurfacesBuffer = 0;
void SetDrawSurfacesBuffer()
{
if(bspDrawSurfaces != 0)
free(bspDrawSurfaces);
numBSPDrawSurfacesBuffer = MAX_MAP_DRAW_SURFS;
bspDrawSurfaces = safe_malloc_info(sizeof(bspDrawSurface_t) * numBSPDrawSurfacesBuffer, "IncDrawSurfaces");
memset(bspDrawSurfaces, 0, MAX_MAP_DRAW_SURFS * sizeof(bspDrawVert_t));
}
void SetDrawSurfaces(int n)
{
if(bspDrawSurfaces != 0)
free(bspDrawSurfaces);
numBSPDrawSurfaces = n;
numBSPDrawSurfacesBuffer = numBSPDrawSurfaces;
bspDrawSurfaces = safe_malloc_info(sizeof(bspDrawSurface_t) * numBSPDrawSurfacesBuffer, "IncDrawSurfaces");
memset(bspDrawSurfaces, 0, n * sizeof(bspDrawVert_t));
}
void BSPFilesCleanup()
{
if(bspDrawVerts != 0)
free(bspDrawVerts);
if(bspDrawSurfaces != 0)
free(bspDrawSurfaces);
if(bspLightBytes != 0)
free(bspLightBytes);
if(bspGridPoints != 0)
free(bspGridPoints);
}
/*
SwapBlock()
if all values are 32 bits, this can be used to swap everything
*/
void SwapBlock( int *block, int size )
{
int i;
/* dummy check */
if( block == NULL )
return;
/* swap */
size >>= 2;
for( i = 0; i < size; i++ )
block[ i ] = LittleLong( block[ i ] );
}
/*
SwapBSPFile()
byte swaps all data in the abstract bsp
*/
void SwapBSPFile( void )
{
int i, j;
/* models */
SwapBlock( (int*) bspModels, numBSPModels * sizeof( bspModels[ 0 ] ) );
/* shaders (don't swap the name) */
for( i = 0; i < numBSPShaders ; i++ )
{
bspShaders[ i ].contentFlags = LittleLong( bspShaders[ i ].contentFlags );
bspShaders[ i ].surfaceFlags = LittleLong( bspShaders[ i ].surfaceFlags );
}
/* planes */
SwapBlock( (int*) bspPlanes, numBSPPlanes * sizeof( bspPlanes[ 0 ] ) );
/* nodes */
SwapBlock( (int*) bspNodes, numBSPNodes * sizeof( bspNodes[ 0 ] ) );
/* leafs */
SwapBlock( (int*) bspLeafs, numBSPLeafs * sizeof( bspLeafs[ 0 ] ) );
/* leaffaces */
SwapBlock( (int*) bspLeafSurfaces, numBSPLeafSurfaces * sizeof( bspLeafSurfaces[ 0 ] ) );
/* leafbrushes */
SwapBlock( (int*) bspLeafBrushes, numBSPLeafBrushes * sizeof( bspLeafBrushes[ 0 ] ) );
// brushes
SwapBlock( (int*) bspBrushes, numBSPBrushes * sizeof( bspBrushes[ 0 ] ) );
// brushsides
SwapBlock( (int*) bspBrushSides, numBSPBrushSides * sizeof( bspBrushSides[ 0 ] ) );
// vis
((int*) &bspVisBytes)[ 0 ] = LittleLong( ((int*) &bspVisBytes)[ 0 ] );
((int*) &bspVisBytes)[ 1 ] = LittleLong( ((int*) &bspVisBytes)[ 1 ] );
/* drawverts (don't swap colors) */
for( i = 0; i < numBSPDrawVerts; i++ )
{
bspDrawVerts[ i ].xyz[ 0 ] = LittleFloat( bspDrawVerts[ i ].xyz[ 0 ] );
bspDrawVerts[ i ].xyz[ 1 ] = LittleFloat( bspDrawVerts[ i ].xyz[ 1 ] );
bspDrawVerts[ i ].xyz[ 2 ] = LittleFloat( bspDrawVerts[ i ].xyz[ 2 ] );
bspDrawVerts[ i ].normal[ 0 ] = LittleFloat( bspDrawVerts[ i ].normal[ 0 ] );
bspDrawVerts[ i ].normal[ 1 ] = LittleFloat( bspDrawVerts[ i ].normal[ 1 ] );
bspDrawVerts[ i ].normal[ 2 ] = LittleFloat( bspDrawVerts[ i ].normal[ 2 ] );
bspDrawVerts[ i ].st[ 0 ] = LittleFloat( bspDrawVerts[ i ].st[ 0 ] );
bspDrawVerts[ i ].st[ 1 ] = LittleFloat( bspDrawVerts[ i ].st[ 1 ] );
for( j = 0; j < MAX_LIGHTMAPS; j++ )
{
bspDrawVerts[ i ].lightmap[ j ][ 0 ] = LittleFloat( bspDrawVerts[ i ].lightmap[ j ][ 0 ] );
bspDrawVerts[ i ].lightmap[ j ][ 1 ] = LittleFloat( bspDrawVerts[ i ].lightmap[ j ][ 1 ] );
}
}
/* drawindexes */
SwapBlock( (int*) bspDrawIndexes, numBSPDrawIndexes * sizeof( bspDrawIndexes[0] ) );
/* drawsurfs */
/* note: rbsp files (and hence q3map2 abstract bsp) have byte lightstyles index arrays, this follows sof2map convention */
SwapBlock( (int*) bspDrawSurfaces, numBSPDrawSurfaces * sizeof( bspDrawSurfaces[ 0 ] ) );
/* fogs */
for( i = 0; i < numBSPFogs; i++ )
{
bspFogs[ i ].brushNum = LittleLong( bspFogs[ i ].brushNum );
bspFogs[ i ].visibleSide = LittleLong( bspFogs[ i ].visibleSide );
}
}
/*
GetLumpElements()
gets the number of elements in a bsp lump
*/
int GetLumpElements( bspHeader_t *header, int lump, int size )
{
/* check for odd size */
if( header->lumps[ lump ].length % size )
{
if( force )
{
Sys_Printf( "WARNING: GetLumpElements: odd lump size (%d) in lump %d\n", header->lumps[ lump ].length, lump );
return 0;
}
else
Error( "GetLumpElements: odd lump size (%d) in lump %d", header->lumps[ lump ].length, lump );
}
/* return element count */
return header->lumps[ lump ].length / size;
}
/*
GetLump()
returns a pointer to the specified lump
*/
void *GetLump( bspHeader_t *header, int lump )
{
return (void*)( (byte*) header + header->lumps[ lump ].offset);
}
/*
CopyLump()
copies a bsp file lump into a destination buffer
*/
int CopyLump( bspHeader_t *header, int lump, void *dest, int size )
{
int length, offset;
/* get lump length and offset */
length = header->lumps[ lump ].length;
offset = header->lumps[ lump ].offset;
/* handle erroneous cases */
if( length == 0 )
return 0;
if( length % size )
{
if( force )
{
Sys_Printf( "WARNING: CopyLump: odd lump size (%d) in lump %d\n", length, lump );
return 0;
}
else
Error( "CopyLump: odd lump size (%d) in lump %d", length, lump );
}
/* copy block of memory and return */
memcpy( dest, (byte*) header + offset, length );
return length / size;
}
/*
AddLump()
adds a lump to an outgoing bsp file
*/
void AddLump( FILE *file, bspHeader_t *header, int lumpNum, const void *data, int length )
{
bspLump_t *lump;
/* add lump to bsp file header */
lump = &header->lumps[ lumpNum ];
lump->offset = LittleLong( ftell( file ) );
lump->length = LittleLong( length );
/* write lump to file */
SafeWrite( file, data, (length + 3) & ~3 );
}
/*
LoadBSPFile()
loads a bsp file into memory
*/
void LoadBSPFile( const char *filename )
{
/* dummy check */
if( game == NULL || game->load == NULL )
Error( "LoadBSPFile: unsupported BSP file format" );
/* load it, then byte swap the in-memory version */
game->load( filename );
SwapBSPFile();
}
/*
WriteBSPFile()
writes a bsp file
*/
void WriteBSPFile( const char *filename )
{
char tempname[ 1024 ];
time_t tm;
/* dummy check */
if( game == NULL || game->write == NULL )
Error( "WriteBSPFile: unsupported BSP file format" );
/* make fake temp name so existing bsp file isn't damaged in case write process fails */
time( &tm );
sprintf( tempname, "%s.%08X", filename, (int) tm );
/* byteswap, write the bsp, then swap back so it can be manipulated further */
SwapBSPFile();
game->write( tempname );
SwapBSPFile();
/* replace existing bsp file */
remove( filename );
rename( tempname, filename );
}
/*
PrintBSPFileSizes()
dumps info about current file
*/
void PrintBSPFileSizes( void )
{
/* parse entities first */
if( numEntities <= 0 )
ParseEntities();
/* note that this is abstracted */
Sys_Printf( "Abstracted BSP file components (*actual sizes may differ)\n" );
/* print various and sundry bits */
Sys_Printf( "%9d models %9d\n",
numBSPModels, (int) (numBSPModels * sizeof( bspModel_t )) );
Sys_Printf( "%9d shaders %9d\n",
numBSPShaders, (int) (numBSPShaders * sizeof( bspShader_t )) );
Sys_Printf( "%9d brushes %9d\n",
numBSPBrushes, (int) (numBSPBrushes * sizeof( bspBrush_t )) );
Sys_Printf( "%9d brushsides %9d *\n",
numBSPBrushSides, (int) (numBSPBrushSides * sizeof( bspBrushSide_t )) );
Sys_Printf( "%9d fogs %9d\n",
numBSPFogs, (int) (numBSPFogs * sizeof( bspFog_t ) ) );
Sys_Printf( "%9d planes %9d\n",
numBSPPlanes, (int) (numBSPPlanes * sizeof( bspPlane_t )) );
Sys_Printf( "%9d entdata %9d\n",
numEntities, bspEntDataSize );
Sys_Printf( "\n");
Sys_Printf( "%9d nodes %9d\n",
numBSPNodes, (int) (numBSPNodes * sizeof( bspNode_t)) );
Sys_Printf( "%9d leafs %9d\n",
numBSPLeafs, (int) (numBSPLeafs * sizeof( bspLeaf_t )) );
Sys_Printf( "%9d leafsurfaces %9d\n",
numBSPLeafSurfaces, (int) (numBSPLeafSurfaces * sizeof( *bspLeafSurfaces )) );
Sys_Printf( "%9d leafbrushes %9d\n",
numBSPLeafBrushes, (int) (numBSPLeafBrushes * sizeof( *bspLeafBrushes )) );
Sys_Printf( "\n");
Sys_Printf( "%9d drawsurfaces %9d *\n",
numBSPDrawSurfaces, (int) (numBSPDrawSurfaces * sizeof( *bspDrawSurfaces )) );
Sys_Printf( "%9d drawverts %9d *\n",
numBSPDrawVerts, (int) (numBSPDrawVerts * sizeof( *bspDrawVerts )) );
Sys_Printf( "%9d drawindexes %9d\n",
numBSPDrawIndexes, (int) (numBSPDrawIndexes * sizeof( *bspDrawIndexes )) );
Sys_Printf( "\n");
Sys_Printf( "%9d lightmaps %9d\n",
numBSPLightBytes / (game->lightmapSize * game->lightmapSize * 3), numBSPLightBytes );
Sys_Printf( "%9d lightgrid %9d *\n",
numBSPGridPoints, (int) (numBSPGridPoints * sizeof( *bspGridPoints )) );
Sys_Printf( " visibility %9d\n",
numBSPVisBytes );
}
/* -------------------------------------------------------------------------------
entity data handling
------------------------------------------------------------------------------- */
/*
StripTrailing()
strips low byte chars off the end of a string
*/
void StripTrailing( char *e )
{
char *s;
s = e + strlen( e ) - 1;
while( s >= e && *s <= 32 )
{
*s = 0;
s--;
}
}
/*
ParseEpair()
parses a single quoted "key" "value" pair into an epair struct
*/
epair_t *ParseEPair( void )
{
epair_t *e;
/* allocate and clear new epair */
e = safe_malloc( sizeof( epair_t ) );
memset( e, 0, sizeof( epair_t ) );
/* handle key */
if( strlen( token ) >= (MAX_KEY - 1) )
Error( "ParseEPair: token too long" );
e->key = copystring( token );
GetToken( qfalse );
/* handle value */
if( strlen( token ) >= MAX_VALUE - 1 )
Error( "ParseEpar: token too long" );
e->value = copystring( token );
/* strip trailing spaces that sometimes get accidentally added in the editor */
StripTrailing( e->key );
StripTrailing( e->value );
/* return it */
return e;
}
/*
ParseEntity()
parses an entity's epairs
*/
qboolean ParseEntity( void )
{
epair_t *e;
/* dummy check */
if( !GetToken( qtrue ) )
return qfalse;
if( strcmp( token, "{" ) )
Error( "ParseEntity: { not found" );
if( numEntities == MAX_MAP_ENTITIES )
Error( "numEntities == MAX_MAP_ENTITIES" );
/* create new entity */
mapEnt = &entities[ numEntities ];
numEntities++;
/* parse */
while( 1 )
{
if( !GetToken( qtrue ) )
Error( "ParseEntity: EOF without closing brace" );
if( !EPAIR_STRCMP( token, "}" ) )
break;
e = ParseEPair();
e->next = mapEnt->epairs;
mapEnt->epairs = e;
}
/* return to sender */
return qtrue;
}
/*
ParseEntities()
parses the bsp entity data string into entities
*/
void ParseEntities( void )
{
numEntities = 0;
ParseFromMemory( bspEntData, bspEntDataSize );
while( ParseEntity() );
/* ydnar: set number of bsp entities in case a map is loaded on top */
numBSPEntities = numEntities;
}
/*
UnparseEntities()
generates the dentdata string from all the entities.
this allows the utilities to add or remove key/value
pairs to the data created by the map editor
*/
void UnparseEntities( void )
{
int i;
char *buf, *end;
epair_t *ep;
char line[ 2048 ];
char key[ 1024 ], value[ 1024 ];
const char *value2;
/* setup */
buf = bspEntData;
end = buf;
*end = 0;
/* run through entity list */
for( i = 0; i < numBSPEntities && i < numEntities; i++ )
{
/* get epair */
ep = entities[ i ].epairs;
if( ep == NULL )
continue; /* ent got removed */
/* ydnar: certain entities get stripped from bsp file */
value2 = ValueForKey( &entities[ i ], "classname" );
if( !Q_stricmp( value2, "misc_model" ) ||
!Q_stricmp( value2, "_decal" ) ||
!Q_stricmp( value2, "_skybox" ) )
continue;
/* add beginning brace */
strcat( end, "{\n" );
end += 2;
/* walk epair list */
for( ep = entities[ i ].epairs; ep != NULL; ep = ep->next )
{
/* copy and clean */
strcpy( key, ep->key );
StripTrailing( key );
strcpy( value, ep->value );
StripTrailing( value );
/* add to buffer */
sprintf( line, "\"%s\" \"%s\"\n", key, value );
strcat( end, line );
end += strlen( line );
}
/* add trailing brace */
strcat( end,"}\n" );
end += 2;
/* check for overflow */
if( end > buf + MAX_MAP_ENTSTRING )
Error( "Entity text too long" );
}
/* set size */
bspEntDataSize = end - buf + 1;
}
/*
PrintEntity()
prints an entity's epairs to the console
*/
void PrintEntity( const entity_t *ent )
{
epair_t *ep;
Sys_Printf( "------- entity %p -------\n", ent );
for( ep = ent->epairs; ep != NULL; ep = ep->next )
Sys_Printf( "%s = %s\n", ep->key, ep->value );
}
/*
SetKeyValue()
sets an epair in an entity
*/
void SetKeyValue( entity_t *ent, const char *key, const char *value )
{
epair_t *ep;
/* check for existing epair */
for( ep = ent->epairs; ep != NULL; ep = ep->next )
{
if( !EPAIR_STRCMP( ep->key, key ) )
{
free( ep->value );
ep->value = copystring( value );
return;
}
}
/* create new epair */
ep = safe_malloc( sizeof( *ep ) );
ep->next = ent->epairs;
ent->epairs = ep;
ep->key = copystring( key );
ep->value = copystring( value );
}
/*
ValueForKey()
gets the value for an entity key
*/
const char *ValueForKey( const entity_t *ent, const char *key )
{
epair_t *ep;
/* dummy check */
if( ent == NULL )
return "";
/* walk epair list */
for( ep = ent->epairs; ep != NULL; ep = ep->next )
{
if( !EPAIR_STRCMP( ep->key, key ) )
return ep->value;
}
/* if no match, return empty string */
return "";
}
/*
IntForKey()
gets the integer point value for an entity key
*/
int IntForKey( const entity_t *ent, const char *key )
{
const char *k;
k = ValueForKey( ent, key );
return atoi( k );
}
/*
FloatForKey()
gets the floating point value for an entity key
*/
vec_t FloatForKey( const entity_t *ent, const char *key )
{
const char *k;
k = ValueForKey( ent, key );
return atof( k );
}
/*
GetVectorForKey()
gets a 3-element vector value for an entity key
*/
void GetVectorForKey( const entity_t *ent, const char *key, vec3_t vec )
{
const char *k;
double v1, v2, v3;
/* get value */
k = ValueForKey( ent, key );
/* scanf into doubles, then assign, so it is vec_t size independent */
v1 = v2 = v3 = 0.0;
sscanf( k, "%lf %lf %lf", &v1, &v2, &v3 );
vec[ 0 ] = v1;
vec[ 1 ] = v2;
vec[ 2 ] = v3;
}
/*
FindTargetEntity()
finds an entity target
*/
entity_t *FindTargetEntity( const char *target )
{
int i;
const char *n;
/* walk entity list */
for( i = 0; i < numEntities; i++ )
{
n = ValueForKey( &entities[ i ], "targetname" );
if ( !strcmp( n, target ) )
return &entities[ i ];
}
/* nada */
return NULL;
}
/*
GetEntityShadowFlags() - ydnar
gets an entity's shadow flags
note: does not set them to defaults if the keys are not found!
*/
void GetEntityShadowFlags( const entity_t *ent, const entity_t *ent2, int *castShadows, int *recvShadows )
{
const char *value;
/* get cast shadows */
if( castShadows != NULL )
{
value = ValueForKey( ent, "_castShadows" );
if( value[ 0 ] == '\0' )
value = ValueForKey( ent, "_cs" );
if( value[ 0 ] == '\0' )
value = ValueForKey( ent2, "_castShadows" );
if( value[ 0 ] == '\0' )
value = ValueForKey( ent2, "_cs" );
if( value[ 0 ] != '\0' )
*castShadows = atoi( value );
}
/* receive */
if( recvShadows != NULL )
{
value = ValueForKey( ent, "_receiveShadows" );
if( value[ 0 ] == '\0' )
value = ValueForKey( ent, "_rs" );
if( value[ 0 ] == '\0' )
value = ValueForKey( ent2, "_receiveShadows" );
if( value[ 0 ] == '\0' )
value = ValueForKey( ent2, "_rs" );
if( value[ 0 ] != '\0' )
*recvShadows = atoi( value );
}
}

View File

@@ -0,0 +1,585 @@
/* -------------------------------------------------------------------------------
Copyright (C) 1999-2006 Id Software, Inc. and contributors.
For a list of contributors, see the accompanying CONTRIBUTORS file.
This file is part of GtkRadiant.
GtkRadiant 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.
GtkRadiant 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 GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
----------------------------------------------------------------------------------
This code has been altered significantly from its original form, to support
several games based on the Quake III Arena engine, in the form of "Q3Map2."
------------------------------------------------------------------------------- */
/* marker */
#define BSPFILE_IBSP_C
/* dependencies */
#include "q3map2.h"
/* -------------------------------------------------------------------------------
this file handles translating the bsp file format used by quake 3, rtcw, and ef
into the abstracted bsp file used by q3map2.
------------------------------------------------------------------------------- */
/* constants */
#define LUMP_ENTITIES 0
#define LUMP_SHADERS 1
#define LUMP_PLANES 2
#define LUMP_NODES 3
#define LUMP_LEAFS 4
#define LUMP_LEAFSURFACES 5
#define LUMP_LEAFBRUSHES 6
#define LUMP_MODELS 7
#define LUMP_BRUSHES 8
#define LUMP_BRUSHSIDES 9
#define LUMP_DRAWVERTS 10
#define LUMP_DRAWINDEXES 11
#define LUMP_FOGS 12
#define LUMP_SURFACES 13
#define LUMP_LIGHTMAPS 14
#define LUMP_LIGHTGRID 15
#define LUMP_VISIBILITY 16
#define HEADER_LUMPS 17
/* types */
typedef struct
{
char ident[ 4 ];
int version;
bspLump_t lumps[ HEADER_LUMPS ];
}
ibspHeader_t;
/* brush sides */
typedef struct
{
int planeNum;
int shaderNum;
}
ibspBrushSide_t;
static void CopyBrushSidesLump( ibspHeader_t *header )
{
int i;
ibspBrushSide_t *in;
bspBrushSide_t *out;
/* get count */
numBSPBrushSides = GetLumpElements( (bspHeader_t*) header, LUMP_BRUSHSIDES, sizeof( *in ) );
/* copy */
in = GetLump( (bspHeader_t*) header, LUMP_BRUSHSIDES );
out = bspBrushSides;
for( i = 0; i < numBSPBrushSides; i++ )
{
out->planeNum = in->planeNum;
out->shaderNum = in->shaderNum;
out->surfaceNum = -1;
in++;
out++;
}
}
static void AddBrushSidesLump( FILE *file, ibspHeader_t *header )
{
int i, size;
bspBrushSide_t *in;
ibspBrushSide_t *buffer, *out;
/* allocate output buffer */
size = numBSPBrushSides * sizeof( *buffer );
buffer = safe_malloc( size );
memset( buffer, 0, size );
/* convert */
in = bspBrushSides;
out = buffer;
for( i = 0; i < numBSPBrushSides; i++ )
{
out->planeNum = in->planeNum;
out->shaderNum = in->shaderNum;
in++;
out++;
}
/* write lump */
AddLump( file, (bspHeader_t*) header, LUMP_BRUSHSIDES, buffer, size );
/* free buffer */
free( buffer );
}
/* drawsurfaces */
typedef struct ibspDrawSurface_s
{
int shaderNum;
int fogNum;
int surfaceType;
int firstVert;
int numVerts;
int firstIndex;
int numIndexes;
int lightmapNum;
int lightmapX, lightmapY;
int lightmapWidth, lightmapHeight;
vec3_t lightmapOrigin;
vec3_t lightmapVecs[ 3 ];
int patchWidth;
int patchHeight;
}
ibspDrawSurface_t;
static void CopyDrawSurfacesLump( ibspHeader_t *header )
{
int i, j;
ibspDrawSurface_t *in;
bspDrawSurface_t *out;
/* get count */
numBSPDrawSurfaces = GetLumpElements( (bspHeader_t*) header, LUMP_SURFACES, sizeof( *in ) );
SetDrawSurfaces( numBSPDrawSurfaces );
/* copy */
in = GetLump( (bspHeader_t*) header, LUMP_SURFACES );
out = bspDrawSurfaces;
for( i = 0; i < numBSPDrawSurfaces; i++ )
{
out->shaderNum = in->shaderNum;
out->fogNum = in->fogNum;
out->surfaceType = in->surfaceType;
out->firstVert = in->firstVert;
out->numVerts = in->numVerts;
out->firstIndex = in->firstIndex;
out->numIndexes = in->numIndexes;
out->lightmapStyles[ 0 ] = LS_NORMAL;
out->vertexStyles[ 0 ] = LS_NORMAL;
out->lightmapNum[ 0 ] = in->lightmapNum;
out->lightmapX[ 0 ] = in->lightmapX;
out->lightmapY[ 0 ] = in->lightmapY;
for( j = 1; j < MAX_LIGHTMAPS; j++ )
{
out->lightmapStyles[ j ] = LS_NONE;
out->vertexStyles[ j ] = LS_NONE;
out->lightmapNum[ j ] = -3;
out->lightmapX[ j ] = 0;
out->lightmapY[ j ] = 0;
}
out->lightmapWidth = in->lightmapWidth;
out->lightmapHeight = in->lightmapHeight;
VectorCopy( in->lightmapOrigin, out->lightmapOrigin );
VectorCopy( in->lightmapVecs[ 0 ], out->lightmapVecs[ 0 ] );
VectorCopy( in->lightmapVecs[ 1 ], out->lightmapVecs[ 1 ] );
VectorCopy( in->lightmapVecs[ 2 ], out->lightmapVecs[ 2 ] );
out->patchWidth = in->patchWidth;
out->patchHeight = in->patchHeight;
in++;
out++;
}
}
static void AddDrawSurfacesLump( FILE *file, ibspHeader_t *header )
{
int i, size;
bspDrawSurface_t *in;
ibspDrawSurface_t *buffer, *out;
/* allocate output buffer */
size = numBSPDrawSurfaces * sizeof( *buffer );
buffer = safe_malloc( size );
memset( buffer, 0, size );
/* convert */
in = bspDrawSurfaces;
out = buffer;
for( i = 0; i < numBSPDrawSurfaces; i++ )
{
out->shaderNum = in->shaderNum;
out->fogNum = in->fogNum;
out->surfaceType = in->surfaceType;
out->firstVert = in->firstVert;
out->numVerts = in->numVerts;
out->firstIndex = in->firstIndex;
out->numIndexes = in->numIndexes;
out->lightmapNum = in->lightmapNum[ 0 ];
out->lightmapX = in->lightmapX[ 0 ];
out->lightmapY = in->lightmapY[ 0 ];
out->lightmapWidth = in->lightmapWidth;
out->lightmapHeight = in->lightmapHeight;
VectorCopy( in->lightmapOrigin, out->lightmapOrigin );
VectorCopy( in->lightmapVecs[ 0 ], out->lightmapVecs[ 0 ] );
VectorCopy( in->lightmapVecs[ 1 ], out->lightmapVecs[ 1 ] );
VectorCopy( in->lightmapVecs[ 2 ], out->lightmapVecs[ 2 ] );
out->patchWidth = in->patchWidth;
out->patchHeight = in->patchHeight;
in++;
out++;
}
/* write lump */
AddLump( file, (bspHeader_t*) header, LUMP_SURFACES, buffer, size );
/* free buffer */
free( buffer );
}
/* drawverts */
typedef struct
{
vec3_t xyz;
float st[ 2 ];
float lightmap[ 2 ];
vec3_t normal;
byte color[ 4 ];
}
ibspDrawVert_t;
static void CopyDrawVertsLump( ibspHeader_t *header )
{
int i;
ibspDrawVert_t *in;
bspDrawVert_t *out;
/* get count */
numBSPDrawVerts = GetLumpElements( (bspHeader_t*) header, LUMP_DRAWVERTS, sizeof( *in ) );
SetDrawVerts( numBSPDrawVerts );
/* copy */
in = GetLump( (bspHeader_t*) header, LUMP_DRAWVERTS );
out = bspDrawVerts;
for( i = 0; i < numBSPDrawVerts; i++ )
{
VectorCopy( in->xyz, out->xyz );
out->st[ 0 ] = in->st[ 0 ];
out->st[ 1 ] = in->st[ 1 ];
out->lightmap[ 0 ][ 0 ] = in->lightmap[ 0 ];
out->lightmap[ 0 ][ 1 ] = in->lightmap[ 1 ];
VectorCopy( in->normal, out->normal );
out->color[ 0 ][ 0 ] = in->color[ 0 ];
out->color[ 0 ][ 1 ] = in->color[ 1 ];
out->color[ 0 ][ 2 ] = in->color[ 2 ];
out->color[ 0 ][ 3 ] = in->color[ 3 ];
in++;
out++;
}
}
static void AddDrawVertsLump( FILE *file, ibspHeader_t *header )
{
int i, size;
bspDrawVert_t *in;
ibspDrawVert_t *buffer, *out;
/* allocate output buffer */
size = numBSPDrawVerts * sizeof( *buffer );
buffer = safe_malloc( size );
memset( buffer, 0, size );
/* convert */
in = bspDrawVerts;
out = buffer;
for( i = 0; i < numBSPDrawVerts; i++ )
{
VectorCopy( in->xyz, out->xyz );
out->st[ 0 ] = in->st[ 0 ];
out->st[ 1 ] = in->st[ 1 ];
out->lightmap[ 0 ] = in->lightmap[ 0 ][ 0 ];
out->lightmap[ 1 ] = in->lightmap[ 0 ][ 1 ];
VectorCopy( in->normal, out->normal );
out->color[ 0 ] = in->color[ 0 ][ 0 ];
out->color[ 1 ] = in->color[ 0 ][ 1 ];
out->color[ 2 ] = in->color[ 0 ][ 2 ];
out->color[ 3 ] = in->color[ 0 ][ 3 ];
in++;
out++;
}
/* write lump */
AddLump( file, (bspHeader_t*) header, LUMP_DRAWVERTS, buffer, size );
/* free buffer */
free( buffer );
}
/* light grid */
typedef struct
{
byte ambient[ 3 ];
byte directed[ 3 ];
byte latLong[ 2 ];
}
ibspGridPoint_t;
static void CopyLightGridLumps( ibspHeader_t *header )
{
int i, j;
ibspGridPoint_t *in;
bspGridPoint_t *out;
/* get count */
numBSPGridPoints = GetLumpElements( (bspHeader_t*) header, LUMP_LIGHTGRID, sizeof( *in ) );
/* allocate buffer */
bspGridPoints = safe_malloc( numBSPGridPoints * sizeof( *bspGridPoints ) );
memset( bspGridPoints, 0, numBSPGridPoints * sizeof( *bspGridPoints ) );
/* copy */
in = GetLump( (bspHeader_t*) header, LUMP_LIGHTGRID );
out = bspGridPoints;
for( i = 0; i < numBSPGridPoints; i++ )
{
for( j = 0; j < MAX_LIGHTMAPS; j++ )
{
VectorCopy( in->ambient, out->ambient[ j ] );
VectorCopy( in->directed, out->directed[ j ] );
out->styles[ j ] = LS_NONE;
}
out->styles[ 0 ] = LS_NORMAL;
out->latLong[ 0 ] = in->latLong[ 0 ];
out->latLong[ 1 ] = in->latLong[ 1 ];
in++;
out++;
}
}
static void AddLightGridLumps( FILE *file, ibspHeader_t *header )
{
int i;
bspGridPoint_t *in;
ibspGridPoint_t *buffer, *out;
/* dummy check */
if( bspGridPoints == NULL )
return;
/* allocate temporary buffer */
buffer = safe_malloc( numBSPGridPoints * sizeof( *out ) );
/* convert */
in = bspGridPoints;
out = buffer;
for( i = 0; i < numBSPGridPoints; i++ )
{
VectorCopy( in->ambient[ 0 ], out->ambient );
VectorCopy( in->directed[ 0 ], out->directed );
out->latLong[ 0 ] = in->latLong[ 0 ];
out->latLong[ 1 ] = in->latLong[ 1 ];
in++;
out++;
}
/* write lumps */
AddLump( file, (bspHeader_t*) header, LUMP_LIGHTGRID, buffer, (numBSPGridPoints * sizeof( *out )) );
/* free buffer (ydnar 2002-10-22: [bug 641] thanks Rap70r! */
free( buffer );
}
/*
LoadIBSPFile()
loads a quake 3 bsp file into memory
*/
void LoadIBSPFile( const char *filename )
{
ibspHeader_t *header;
/* load the file header */
LoadFile( filename, (void**) &header );
/* swap the header (except the first 4 bytes) */
SwapBlock( (int*) ((byte*) header + sizeof( int )), sizeof( *header ) - sizeof( int ) );
/* make sure it matches the format we're trying to load */
if( force == qfalse && *((int*) header->ident) != *((int*) game->bspIdent) )
Error( "%s is not a %s file", filename, game->bspIdent );
if( force == qfalse && header->version != game->bspVersion )
Error( "%s is version %d, not %d", filename, header->version, game->bspVersion );
/* load/convert lumps */
numBSPShaders = CopyLump( (bspHeader_t*) header, LUMP_SHADERS, bspShaders, sizeof( bspShader_t ) );
numBSPModels = CopyLump( (bspHeader_t*) header, LUMP_MODELS, bspModels, sizeof( bspModel_t ) );
numBSPPlanes = CopyLump( (bspHeader_t*) header, LUMP_PLANES, bspPlanes, sizeof( bspPlane_t ) );
numBSPLeafs = CopyLump( (bspHeader_t*) header, LUMP_LEAFS, bspLeafs, sizeof( bspLeaf_t ) );
numBSPNodes = CopyLump( (bspHeader_t*) header, LUMP_NODES, bspNodes, sizeof( bspNode_t ) );
numBSPLeafSurfaces = CopyLump( (bspHeader_t*) header, LUMP_LEAFSURFACES, bspLeafSurfaces, sizeof( bspLeafSurfaces[ 0 ] ) );
numBSPLeafBrushes = CopyLump( (bspHeader_t*) header, LUMP_LEAFBRUSHES, bspLeafBrushes, sizeof( bspLeafBrushes[ 0 ] ) );
numBSPBrushes = CopyLump( (bspHeader_t*) header, LUMP_BRUSHES, bspBrushes, sizeof( bspBrush_t ) );
CopyBrushSidesLump( header );
CopyDrawVertsLump( header );
CopyDrawSurfacesLump( header );
numBSPFogs = CopyLump( (bspHeader_t*) header, LUMP_FOGS, bspFogs, sizeof( bspFog_t ) );
numBSPDrawIndexes = CopyLump( (bspHeader_t*) header, LUMP_DRAWINDEXES, bspDrawIndexes, sizeof( bspDrawIndexes[ 0 ] ) );
numBSPVisBytes = CopyLump( (bspHeader_t*) header, LUMP_VISIBILITY, bspVisBytes, 1 );
numBSPLightBytes = GetLumpElements( (bspHeader_t*) header, LUMP_LIGHTMAPS, 1 );
bspLightBytes = safe_malloc( numBSPLightBytes );
CopyLump( (bspHeader_t*) header, LUMP_LIGHTMAPS, bspLightBytes, 1 );
bspEntDataSize = CopyLump( (bspHeader_t*) header, LUMP_ENTITIES, bspEntData, 1);
CopyLightGridLumps( header );
/* free the file buffer */
free( header );
}
/*
WriteIBSPFile()
writes an id bsp file
*/
void WriteIBSPFile( const char *filename )
{
ibspHeader_t outheader, *header;
FILE *file;
time_t t;
char marker[ 1024 ];
int size;
/* set header */
header = &outheader;
memset( header, 0, sizeof( *header ) );
//% Swapfile();
/* set up header */
*((int*) (bspHeader_t*) header->ident) = *((int*) game->bspIdent);
header->version = LittleLong( game->bspVersion );
/* write initial header */
file = SafeOpenWrite( filename );
SafeWrite( file, (bspHeader_t*) header, sizeof( *header ) ); /* overwritten later */
/* add marker lump */
time( &t );
sprintf( marker, "I LOVE MY Q3MAP2 %s on %s)", Q3MAP_VERSION, asctime( localtime( &t ) ) );
AddLump( file, (bspHeader_t*) header, 0, marker, strlen( marker ) + 1 );
/* add lumps */
AddLump( file, (bspHeader_t*) header, LUMP_SHADERS, bspShaders, numBSPShaders * sizeof( bspShader_t ) );
AddLump( file, (bspHeader_t*) header, LUMP_PLANES, bspPlanes, numBSPPlanes * sizeof( bspPlane_t ) );
AddLump( file, (bspHeader_t*) header, LUMP_LEAFS, bspLeafs, numBSPLeafs * sizeof( bspLeaf_t ) );
AddLump( file, (bspHeader_t*) header, LUMP_NODES, bspNodes, numBSPNodes * sizeof( bspNode_t ) );
AddLump( file, (bspHeader_t*) header, LUMP_BRUSHES, bspBrushes, numBSPBrushes*sizeof( bspBrush_t ) );
AddBrushSidesLump( file, header );
AddLump( file, (bspHeader_t*) header, LUMP_LEAFSURFACES, bspLeafSurfaces, numBSPLeafSurfaces * sizeof( bspLeafSurfaces[ 0 ] ) );
AddLump( file, (bspHeader_t*) header, LUMP_LEAFBRUSHES, bspLeafBrushes, numBSPLeafBrushes * sizeof( bspLeafBrushes[ 0 ] ) );
AddLump( file, (bspHeader_t*) header, LUMP_MODELS, bspModels, numBSPModels * sizeof( bspModel_t ) );
AddDrawVertsLump( file, header );
AddDrawSurfacesLump( file, header );
AddLump( file, (bspHeader_t*) header, LUMP_VISIBILITY, bspVisBytes, numBSPVisBytes );
AddLump( file, (bspHeader_t*) header, LUMP_LIGHTMAPS, bspLightBytes, numBSPLightBytes );
AddLightGridLumps( file, header );
AddLump( file, (bspHeader_t*) header, LUMP_ENTITIES, bspEntData, bspEntDataSize );
AddLump( file, (bspHeader_t*) header, LUMP_FOGS, bspFogs, numBSPFogs * sizeof( bspFog_t ) );
AddLump( file, (bspHeader_t*) header, LUMP_DRAWINDEXES, bspDrawIndexes, numBSPDrawIndexes * sizeof( bspDrawIndexes[ 0 ] ) );
/* emit bsp size */
size = ftell( file );
Sys_Printf( "Wrote %.1f MB (%d bytes)\n", (float) size / (1024 * 1024), size );
/* write the completed header */
fseek( file, 0, SEEK_SET );
SafeWrite( file, header, sizeof( *header ) );
/* close the file */
fclose( file );
}

View File

@@ -0,0 +1,340 @@
/* -------------------------------------------------------------------------------
Copyright (C) 1999-2006 Id Software, Inc. and contributors.
For a list of contributors, see the accompanying CONTRIBUTORS file.
This file is part of GtkRadiant.
GtkRadiant 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.
GtkRadiant 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 GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
----------------------------------------------------------------------------------
This code has been altered significantly from its original form, to support
several games based on the Quake III Arena engine, in the form of "Q3Map2."
------------------------------------------------------------------------------- */
/* marker */
#define BSPFILE_RBSP_C
/* dependencies */
#include "q3map2.h"
/* -------------------------------------------------------------------------------
this file handles translating the bsp file format used by quake 3, rtcw, and ef
into the abstracted bsp file used by q3map2.
------------------------------------------------------------------------------- */
/* constants */
#define LUMP_ENTITIES 0
#define LUMP_SHADERS 1
#define LUMP_PLANES 2
#define LUMP_NODES 3
#define LUMP_LEAFS 4
#define LUMP_LEAFSURFACES 5
#define LUMP_LEAFBRUSHES 6
#define LUMP_MODELS 7
#define LUMP_BRUSHES 8
#define LUMP_BRUSHSIDES 9
#define LUMP_DRAWVERTS 10
#define LUMP_DRAWINDEXES 11
#define LUMP_FOGS 12
#define LUMP_SURFACES 13
#define LUMP_LIGHTMAPS 14
#define LUMP_LIGHTGRID 15
#define LUMP_VISIBILITY 16
#define LUMP_LIGHTARRAY 17
#define HEADER_LUMPS 18
/* types */
typedef struct
{
char ident[ 4 ];
int version;
bspLump_t lumps[ HEADER_LUMPS ];
}
rbspHeader_t;
/* light grid */
#define MAX_MAP_GRID 0xffff
#define MAX_MAP_GRIDARRAY 0x100000
#define LG_EPSILON 4
static void CopyLightGridLumps( rbspHeader_t *header )
{
int i;
unsigned short *inArray;
bspGridPoint_t *in, *out;
/* get count */
numBSPGridPoints = GetLumpElements( (bspHeader_t*) header, LUMP_LIGHTARRAY, sizeof( *inArray ) );
/* allocate buffer */
bspGridPoints = safe_malloc( numBSPGridPoints * sizeof( *bspGridPoints ) );
memset( bspGridPoints, 0, numBSPGridPoints * sizeof( *bspGridPoints ) );
/* copy */
inArray = GetLump( (bspHeader_t*) header, LUMP_LIGHTARRAY );
in = GetLump( (bspHeader_t*) header, LUMP_LIGHTGRID );
out = bspGridPoints;
for( i = 0; i < numBSPGridPoints; i++ )
{
memcpy( out, &in[ *inArray ], sizeof( *in ) );
inArray++;
out++;
}
}
static void AddLightGridLumps( FILE *file, rbspHeader_t *header )
{
int i, j, k, c, d;
int numGridPoints, maxGridPoints;
bspGridPoint_t *gridPoints, *in, *out;
int numGridArray;
unsigned short *gridArray;
qboolean bad;
/* allocate temporary buffers */
maxGridPoints = (numBSPGridPoints < MAX_MAP_GRID) ? numBSPGridPoints : MAX_MAP_GRID;
gridPoints = safe_malloc( maxGridPoints * sizeof( *gridPoints ) );
gridArray = safe_malloc( numBSPGridPoints * sizeof( *gridArray ) );
/* zero out */
numGridPoints = 0;
numGridArray = numBSPGridPoints;
/* for each bsp grid point, find an approximate twin */
Sys_Printf( "Storing lightgrid: %d points\n", numBSPGridPoints );
for( i = 0; i < numGridArray; i++ )
{
/* get points */
in = &bspGridPoints[ i ];
/* walk existing list */
for( j = 0; j < numGridPoints; j++ )
{
/* get point */
out = &gridPoints[ j ];
/* compare styles */
if( *((unsigned int*) in->styles) != *((unsigned int*) out->styles) )
continue;
/* compare direction */
d = abs( in->latLong[ 0 ] - out->latLong[ 0 ] );
if( d < (255 - LG_EPSILON) && d > LG_EPSILON )
continue;
d = abs( in->latLong[ 1 ] - out->latLong[ 1 ] );
if( d < 255 - LG_EPSILON && d > LG_EPSILON )
continue;
/* compare light */
bad = qfalse;
for( k = 0; (k < MAX_LIGHTMAPS && bad == qfalse); k++ )
{
for( c = 0; c < 3; c++ )
{
if( abs( (int) in->ambient[ k ][ c ] - (int) out->ambient[ k ][ c ]) > LG_EPSILON ||
abs( (int) in->directed[ k ][ c ] - (int) out->directed[ k ][ c ]) > LG_EPSILON )
{
bad = qtrue;
break;
}
}
}
/* failure */
if( bad )
continue;
/* this sample is ok */
break;
}
/* set sample index */
gridArray[ i ] = (unsigned short) j;
/* if no sample found, add a new one */
if( j >= numGridPoints && numGridPoints < maxGridPoints )
{
out = &gridPoints[ numGridPoints++ ];
memcpy( out, in, sizeof( *in ) );
}
}
/* swap array */
for( i = 0; i < numGridArray; i++ )
gridArray[ i ] = LittleShort( gridArray[ i ] );
/* write lumps */
AddLump( file, (bspHeader_t*) header, LUMP_LIGHTGRID, gridPoints, (numGridPoints * sizeof( *gridPoints )) );
AddLump( file, (bspHeader_t*) header, LUMP_LIGHTARRAY, gridArray, (numGridArray * sizeof( *gridArray )) );
/* free buffers */
free( gridPoints );
free( gridArray );
}
/*
LoadRBSPFile()
loads a raven bsp file into memory
*/
void LoadRBSPFile( const char *filename )
{
rbspHeader_t *header;
/* load the file header */
LoadFile( filename, (void**) &header );
/* swap the header (except the first 4 bytes) */
SwapBlock( (int*) ((byte*) header + sizeof( int )), sizeof( *header ) - sizeof( int ) );
/* make sure it matches the format we're trying to load */
if( force == qfalse && *((int*) header->ident) != *((int*) game->bspIdent) )
Error( "%s is not a %s file", filename, game->bspIdent );
if( force == qfalse && header->version != game->bspVersion )
Error( "%s is version %d, not %d", filename, header->version, game->bspVersion );
/* load/convert lumps */
numBSPShaders = CopyLump( (bspHeader_t*) header, LUMP_SHADERS, bspShaders, sizeof( bspShader_t ) );
numBSPModels = CopyLump( (bspHeader_t*) header, LUMP_MODELS, bspModels, sizeof( bspModel_t ) );
numBSPPlanes = CopyLump( (bspHeader_t*) header, LUMP_PLANES, bspPlanes, sizeof( bspPlane_t ) );
numBSPLeafs = CopyLump( (bspHeader_t*) header, LUMP_LEAFS, bspLeafs, sizeof( bspLeaf_t ) );
numBSPNodes = CopyLump( (bspHeader_t*) header, LUMP_NODES, bspNodes, sizeof( bspNode_t ) );
numBSPLeafSurfaces = CopyLump( (bspHeader_t*) header, LUMP_LEAFSURFACES, bspLeafSurfaces, sizeof( bspLeafSurfaces[ 0 ] ) );
numBSPLeafBrushes = CopyLump( (bspHeader_t*) header, LUMP_LEAFBRUSHES, bspLeafBrushes, sizeof( bspLeafBrushes[ 0 ] ) );
numBSPBrushes = CopyLump( (bspHeader_t*) header, LUMP_BRUSHES, bspBrushes, sizeof( bspBrush_t ) );
numBSPBrushSides = CopyLump( (bspHeader_t*) header, LUMP_BRUSHSIDES, bspBrushSides, sizeof( bspBrushSide_t ) );
numBSPDrawVerts = GetLumpElements( (bspHeader_t*) header, LUMP_DRAWVERTS, sizeof( bspDrawVerts[ 0 ] ) );
SetDrawVerts( numBSPDrawVerts );
CopyLump( (bspHeader_t*) header, LUMP_DRAWVERTS, bspDrawVerts, sizeof( bspDrawVerts[ 0 ] ) );
numBSPDrawSurfaces = GetLumpElements( (bspHeader_t*) header, LUMP_SURFACES, sizeof( bspDrawSurfaces[ 0 ] ) );
SetDrawSurfaces( numBSPDrawSurfaces );
CopyLump( (bspHeader_t*) header, LUMP_SURFACES, bspDrawSurfaces, sizeof( bspDrawSurfaces[ 0 ] ) );
numBSPFogs = CopyLump( (bspHeader_t*) header, LUMP_FOGS, bspFogs, sizeof( bspFogs[ 0 ] ) );
numBSPDrawIndexes = CopyLump( (bspHeader_t*) header, LUMP_DRAWINDEXES, bspDrawIndexes, sizeof( bspDrawIndexes[ 0 ] ) );
numBSPVisBytes = CopyLump( (bspHeader_t*) header, LUMP_VISIBILITY, bspVisBytes, 1 );
numBSPLightBytes = GetLumpElements( (bspHeader_t*) header, LUMP_LIGHTMAPS, 1 );
bspLightBytes = safe_malloc( numBSPLightBytes );
CopyLump( (bspHeader_t*) header, LUMP_LIGHTMAPS, bspLightBytes, 1 );
bspEntDataSize = CopyLump( (bspHeader_t*) header, LUMP_ENTITIES, bspEntData, 1);
CopyLightGridLumps( header );
/* free the file buffer */
free( header );
}
/*
WriteRBSPFile()
writes a raven bsp file
*/
void WriteRBSPFile( const char *filename )
{
rbspHeader_t outheader, *header;
FILE *file;
time_t t;
char marker[ 1024 ];
int size;
/* set header */
header = &outheader;
memset( header, 0, sizeof( *header ) );
//% Swapfile();
/* set up header */
*((int*) (bspHeader_t*) header->ident) = *((int*) game->bspIdent);
header->version = LittleLong( game->bspVersion );
/* write initial header */
file = SafeOpenWrite( filename );
SafeWrite( file, (bspHeader_t*) header, sizeof( *header ) ); /* overwritten later */
/* add marker lump */
time( &t );
sprintf( marker, "I LOVE MY Q3MAP2 %s on %s)", Q3MAP_VERSION, asctime( localtime( &t ) ) );
AddLump( file, (bspHeader_t*) header, 0, marker, strlen( marker ) + 1 );
/* add lumps */
AddLump( file, (bspHeader_t*) header, LUMP_SHADERS, bspShaders, numBSPShaders * sizeof( bspShader_t ) );
AddLump( file, (bspHeader_t*) header, LUMP_PLANES, bspPlanes, numBSPPlanes * sizeof( bspPlane_t ) );
AddLump( file, (bspHeader_t*) header, LUMP_LEAFS, bspLeafs, numBSPLeafs * sizeof( bspLeaf_t ) );
AddLump( file, (bspHeader_t*) header, LUMP_NODES, bspNodes, numBSPNodes * sizeof( bspNode_t ) );
AddLump( file, (bspHeader_t*) header, LUMP_BRUSHES, bspBrushes, numBSPBrushes*sizeof( bspBrush_t ) );
AddLump( file, (bspHeader_t*) header, LUMP_BRUSHSIDES, bspBrushSides, numBSPBrushSides * sizeof( bspBrushSides[ 0 ] ) );
AddLump( file, (bspHeader_t*) header, LUMP_LEAFSURFACES, bspLeafSurfaces, numBSPLeafSurfaces * sizeof( bspLeafSurfaces[ 0 ] ) );
AddLump( file, (bspHeader_t*) header, LUMP_LEAFBRUSHES, bspLeafBrushes, numBSPLeafBrushes * sizeof( bspLeafBrushes[ 0 ] ) );
AddLump( file, (bspHeader_t*) header, LUMP_MODELS, bspModels, numBSPModels * sizeof( bspModel_t ) );
AddLump( file, (bspHeader_t*) header, LUMP_DRAWVERTS, bspDrawVerts, numBSPDrawVerts * sizeof( bspDrawVerts[ 0 ] ) );
AddLump( file, (bspHeader_t*) header, LUMP_SURFACES, bspDrawSurfaces, numBSPDrawSurfaces * sizeof( bspDrawSurfaces[ 0 ] ) );
AddLump( file, (bspHeader_t*) header, LUMP_VISIBILITY, bspVisBytes, numBSPVisBytes );
AddLump( file, (bspHeader_t*) header, LUMP_LIGHTMAPS, bspLightBytes, numBSPLightBytes );
AddLightGridLumps( file, header );
AddLump( file, (bspHeader_t*) header, LUMP_ENTITIES, bspEntData, bspEntDataSize );
AddLump( file, (bspHeader_t*) header, LUMP_FOGS, bspFogs, numBSPFogs * sizeof( bspFog_t ) );
AddLump( file, (bspHeader_t*) header, LUMP_DRAWINDEXES, bspDrawIndexes, numBSPDrawIndexes * sizeof( bspDrawIndexes[ 0 ] ) );
/* emit bsp size */
size = ftell( file );
Sys_Printf( "Wrote %.1f MB (%d bytes)\n", (float) size / (1024 * 1024), size );
/* write the completed header */
fseek( file, 0, SEEK_SET );
SafeWrite( file, header, sizeof( *header ) );
/* close the file */
fclose( file );
}

View File

@@ -0,0 +1,371 @@
Old Q3Map 1.x and Early Q3Map2 Changelog (Chronological Order)
- FILE IS STATIC. IF YOU MAKE CHANGES, UPDATE CHANGELOG.Q3MAP2 -
Date Version Notes
----------------------------------------------------------------
2001-12-03 1.2 (ydnar) Alpha Initial version (win32)
2001-12-03 1.2 (ydnar 2) Alpha Tolerance expanded
(more brush faces caught)
2001-12-04 1.2 (ydnar 3) Alpha Detail faces inside other
detail brushes now culled,
Small against large detail
faces also culled.
2001-12-04 1.2 (ydnar 4) Alpha djbob found a bug where
coincident caulk faces
were causing textured
faces to be caulked. Fixed.
2001-12-04 1.2 (ydnar 7) Alpha 5 and 6 were internal test
versions. This version
takes into account extra
surface info, so coplanar
clip brushes no longer
cull away textured sides.
2001-12-22 1.2 (ydnar 8) Alpha Optimized light. Lighting
for most maps should now
be measurable in minutes
as opposed to hours.
2001-12-24 1.2 (ydnar 9) Alpha Fixed light. It is still
faster, but to enable
"blinding fast" mode,
you must supply the -fast
switch on the commandline
after -light. Fast mode
should be approximately
2x as fast as build 8.
2001-12-24 1.2 (ydnar 10) Alpha Grid lighting is now
optimized. Not as much
as I would like, but
a distance^2 cull before
traces on EVERY SINGLE
SURFACE LIGHT IN THE MAP
certainly speeds things
the fuck up. -fast not
necessary to see this
optimization. Also added
the -cheap switch to
limit light contributions
to a point when it exceeds
255 in R, G, and B. This
*may* cause artifacts.
Test away...
2001-12-24 1.2 (ydnar 11) Alpha Now using PVS data (vis).
Well constructed and
hinted maps should now
see a bit of a speedup.
Lights in the void are
also now removed as a
byproduct.
2001-12-24 1.2 (ydnar 12) Alpha Fixed bug that caused
vlight to crash.
2001-12-27 1.2 (ydnar 13) Alpha - Fixed broken PVS check.
- Cheap now supresses sun
Sun trace skipped if
sample is "cheapened."
- Experimental -smooth
option for subsampling
shadow edges.
- Experimental radiosity
code. Will probably crash.
- Other minor optimizations.
2001-12-27 1.2 (ydnar 14) Alpha Build 13 always subsampled,
making it slower. Fixed.
2001-12-28 1.2 (ydnar 15) Alpha Bad windings from edge- or
vertex- manipulated brushes
no longer created. Vertex
lighting on func_* with
an origin now works.
Radiosity should be more
stable (but not fully
correct yet). Light
envelopes now properly
calculated for entities
with origins.
2001-12-28 1.2 (ydnar 16) Alpha Un-vised maps will now light.
2001-12-30 1.2 (ydnar 17) Alpha Radiosity. Use q3map_bounce
in shaders to specify
amount of light to reflect.
Use -bounce N after -light
to enable radiosity. Use
-dump to emit radiosity
lights as a prefab.
2001-12-31 1.2 (ydnar 18) Alpha Normalization release. New
features include -fastgrid,
-cheapgrid, and -fastbounce.
Running with -fastgrid and
-cheapgrid will produce
results identical to normal
q3map (with the lightgrid
being a little darker).
Also added q3map_nofast to
shaders to override -fast
switch for a surface light.
2002-01-01 1.2 (ydnar 19) Alpha Fixed an odd vertex lighting
bug (thanks Quakin) that was
causing sun to leak to brush
faces when using r_vertexlight
ingame. Changed a little bit
of the default behavior, so
test with vertex lighting
and with terrain. Minor
shader changes might be
necessary to get some maps to
look as before.
2002-01-01 1.2 (ydnar 20) Alpha Colored alpha shadows. Some
minor optimizations in
shadow tracing. Should be
slightly faster than 19.
2002-01-02 1.2 (ydnar 21) Alpha Set up colored shadows
properly to use
surfaceparm lightfilter.
Shaders must use this parm
to have colored shadows.
Can be used with alphashadow
as well.
2002-01-04 1.2.1-y1 (nightly) This version is all new,
based off the official
GtkRadiant tree, which has
all the previous enhancements.
New features include colored
lightgrid tracing through
lightfilter shaders, and
surfaceparm lightgrid, for
large/space maps with large
volumes. Also fixed are
potential broken brush
winding radiosity crashes.
Maybe.
2002-01-05 1.2.1-y2 (nightly) Merged latest CVS. Removed
bug where ambient was getting
calculated into the radiosity
solution for every pass,
leading to overbright maps
in a hurry. Also removed
the bad PTPFF reporting,
as it only caused problems
with radiosity in a big way.
Sue me.
2002-01-05 1.2.1-y3 (nightly) I really suck. Sample color
now properly cleared to 0
when bouncing.
2002-01-07 1.2.1-y4 (nightly) Particle Studio generated
brush faces should no longer
be culled. I no longer cull
faces that are autosprite.
Added -bouncegrid to have
radiosity add to lightgrid.
2002-01-08 1.2.1-y5 (nightly) Same as y4, but compiled with
full optimizations. Should
be 10-25% faster in all,
including BSP and vis
stages.
2002-01-09 1.2.1-y6 (nightly) Brushfaces with polygonoffset
in their shader will no longer
be faceculled.
2002-01-11 1.2.1-y7 (nightly) Increased stack size for threads
to 4MB on Win32 to (hopefully)
elminate stack overflow
crash with radiosity. Also
made subdivision use the heap
to lessen the stack load. Fixed
bug where q3map_bounce was not
being used in shader parsing.
Redid some of the divide math
to work in 0-255 instead of
0-256.
2002-01-11 1.2.1-y8 (nightly) More Win32 threading
crutches. Eat me, Bill.
2002-01-15 1.2.1-y9 RR2DO2 noticed a stupid bug
in my PVS code. Fixed it,
so the PVS light opts work as
they should. Lighting is
faster. Also got rid of some
redundant square roots from
the raytracing, speeding up
lighting another ~25%.
2002-01-20 1.2.1-y10 Fixed a potential crash bug
with maps with 0 lights. Also
changed how lightmaps are
projected onto patches that
lie in a single plane (bevel
endcaps, floors, etc). Shadows
now work properly on them.
2002-01-22 1.2.1-y11 Fixed a divide-by-zero crash
with maps with no lights or
no tracing. Also added
code to make brush/patch
vertex lighting more closely
resemble lightmap, even on
less-than-perfect maps. And
-light is faster, too...about
25% on q3dm17. 34->25 seconds.
2002-01-24 1.2.1-y12 Completely rewrote the path
argument handling. Should find
the Quake 3 dir and other
dirs properly now. Needs to
be tested on Linux though.
Also made lights linear by
default when run with -game
wolf. This is to match the
Gray Matter q3map and
entity definition.
2002-01-28 1.2.4-y1 Merged from 1.2.4-nightly CVS
sources. Fog sparklies gone.
-nopatchfix so vlight works
properly again. Cleaned up
paths processing some more,
including Linux stuff. Added
_lightmapscale entity key.
Brought -game wolf lighting
to parity with GM tools.
RR2DO2's PCX loading fix. A
bunch of other useful fun shit.
2002-01-29 1.2.4-y2 Fixed a bug in RR2DO2's PCX fix.
Fixed a stupid bug in lightmap
dimension bounds checking (thanks
Laerth).
2002-01-29 1.2.4-y3 Now will detect (and report to
GtkRadiant) all degenerate patches
like those created by capping a
cone.
2002-02-23 2.0.0-a1 thru a3 Rewrote about 30% of the code.
Lots of cool new shit.
2002-02-24 2.0.0-a4 thru a8 Terrain fix (thx Pointy), patches
are no longer circus colored, more
terrain texturing fixes.
2002-02-26 2.0.0-a10 thru a11 Adjacent coplanar surfaces now
will share lightmaps. This prevents
most wierd edge cases with filter
and speeds things up a bit. Patches
too.
2002-02-27 2.0.0-a12 More lightmap fixes for non-planar
surfaces. Bugfixes in allocation/
compression of lightmaps as well.
2002-03-02 2.0.0-a13 Fixed some surface light bugs,
adjusted the occluded-luxel finding
code, and amped the radiosity. Other
fixes to RTCW lighting code (better
angle attenuation on linear lights).
2002-03-04 2.0.0-a14 Vertex light should now be near-
perfect on clean (and mostly on not-
so-clean) maps. Unlit maps will no
longer have tri-fanned brush faces
with random vertex colors. VLight is
now totally gone (reverts to -light).
2002-03-06 2.0.0-a15 Relaxed the planar check, should now
classify all slightly-off plane brush
face metasurfaces as planar. Triangle
checking much more stringent as well.
2002-03-11 2.0.0-a16 Vis crash gone. Lightmap allocation
now sorted by shader to minimize
shader count (and lessen chance for
RTCW shader substitution bug). Hit
big quarter-century also.
2002-03-12 2.0.0-a17 Dammit.
2002-03-12 2.0.0-a18 Hunting phantom lights...
2002-03-16 2.0.0-a19 Fogclip and _celshader. Check the
extras folder...
2002-03-18 2.0.0-b1-rc1 Beta release candidate. Fixed the
stupid phantom light bug finally.
Tricked out the sun tracing a wee
bit as well, should be a little
faster + more accurate. Other little
bits fixed up as well. Thanks to K,
{wf}ShadowSpawn and RR2DO2 for their
help tracking these last bugs down.
2002-03-19 2.0.0-b1-rc2 Increased some maximums, and got
rid of some cruft.
2002-03-22 2.0.0-b1-rc3 Some minor optimizations.
2002-03-30 2.0.0-b1-rc5 Now with fur (see extras/fur.shader).
2002-04-01 2.0.0-b1-rc6 Enhanced with baby seal technology.
2002-05-01 2.0.1 OK, better late than never. Fixed the
alphashadow = 255 = transparent bug.
2002-06-24 2.1.0-b1 Added _foghull functionality. Works
like terrain "_shader" where
you don't need "textures/" prefix.
Also added q3map_normalmap. See
NVIDIA's website for Photoshop filter
to generate normalmaps from grayscale
heightmaps. This makes lightmaps
look bumpmapped. Currently 50% broken.
2002-07-06 2.2.0-b1 Empty epairs now stripped from map,
fixing Wolfenstein crash bug. Func_*
entities are now fogged properly.
Sort of. This will be enhanced later.
Added the .srf file to store all the
extra crap I was hiding in the BSP.
It's an editable text file that
-light uses, so you can change the
samplesize w/o recompiling the map
(just -light'ing it). Changed color
normalization to clamping, because
it looks better. Other stuff got
fixed as well.
2002-07-08 2.2.0-b2 thru b11 Test versions. Thanks jer and jet!
2002-07-09 2.2.0-b12 Larger-than-life lightmaps are now
supported, up to 1024x1024. Add
q3map_lightmapSize H W to a shader
to use. Lightmaps are stored in
maps/{mapname}/_lm_NNN.tga and a
shader script q3map_{mapname}.shader
is generated. Also added
q3map_lightmapGamma N.N. Use a
value of 2.0 to simulate
r_overBrightBits 1 and
r_mapOverBrightBits 2 on external
lightmap images.

View File

@@ -0,0 +1,709 @@
Q3Map2 Version History + Changelog (Reverse Chronological Order)
2.5.16 (2004-10-18)
- New: -fixaas mode to reassociate an AAS file with a changed BSP
- New: -nostyles switch on light phase disabling lightstyles
- Using libmhash cryptographic hashing library instead of md5lib
2.5.15 (2004-07-08)
- New: q3map_rgbGen, q3map_rgbMod or q3map_colorGen, q3map_colorMod
- New: q3map_alphaGen const (like q3map_alphaMod set)
- New: q3map_noVertexLight to suppress overwriting of vertex rgb
- q3map_backShader now works again, can be used instead of
q3map_cloneShader + q3map_invert on the target shader
- q3map_alphaMod volume brushes in func_group entities will now only
affect that entity's surfaces
- q3map_clipModel now works on trans/nonsolid shaders, allowing
simplified clipping surfaces in models
- Fixed bug in alphaMod code where only the first vertex was being
modded (thanks Ratty)
- Vis exits instead of crashing with divide-by-zero when there are
no portals on simple box maps
- All of a surface's lightmaps must be solid or not solid, fixing
ST coord problem with non-Raven games using lightstyles
- Reverted a change in the meta code which was causing lightmap coord
overflows and ugly self-shadowing errors on surface boundaries
- Any use of lightstyles automatically sets -nocollapse to reduce
total shader counts
- Tenebrae lightmap size changed to 512x512
2.5.14 (2004-04-09)
- New: -dirty mode courtesy of RaP7oR (dirtmapping/ambient occlusion)
- New: q3map_skyLight code using more uniform angular distribution about
the sky hemisphere
- New: q3map_alphaMod set N.N
- New: q3map_alphaMod scale N.N
- New: q3map_alphaMod volume - applies all other q3map_alphaMod directives
to each vertex inside a brush textured with this shader, allowing large
faded scrolling fire shaders, waterfalls, marquees, explicit dotProduct
terrain blending control, etc.
- Fixed a bug in alphaMod code where the type of the first alphaMod was
being used for all subsequent alphaMods
- Lowered vertex-lit surface limits to 999 verts, 1000 was breaking Quake 3
- Tightened up solid lightmap tolerances
- Fixed bug in solid lightmap code where lightmap SD coords were being
incorrectly set
2.5.13 (2004-03-03)
- New: -convert -format <game> to convert between different BSP formats.
Example, to convert a Jedi Academy map to Enemy Territory:
-game ja -convert -format et
- New: -game etut support for Urban Terror on Enemy Territory
- New: -analyze mode for reverse engineering a BSP format
- New: -compensate N.N (default 1.0) for descaling lightmap/vertex/grid
data to compensate for ingame overbrighting. 4.0 is a good value for
Quake 3 if used in combination with -gamma 2.2
- New: compensate/gamma per-game setting
- New: -light -cpma argument for "classic" (sic) vertex lighting
- Replaced malloc() with stack allocation in IlluminateRawLightmap for
most small/medium lightmap calculations
- Misc cleanups in q3map2.h
- Support for non-128x128 lightmaps
- The -meta process will now generate surfaces with more than 64
verts that have non-lightmapped shaders
- Extended lightmap size tolerance to 2x for merging meta triangles in
maps with aggressive lightmapscale. Sorry kids!
- Moved surface finish pass (celshading, cloning) to final surface pass.
This should fix a bug involving fog/tesselation/celshading/cloning
- Solid-color lightmaps (within 1/255 in RGB) are detected and replaced
with a single pixel lightmap, saving space
2.5.12 (2004-01-18)
- New: -dark option for dark lightmap/brush seams in -light (Unreal 1-ish)
- New: spawnflags 4 on misc_model entities makes them forcemeta/lightmapped
unless explicitly set as vertex lit in their shader(s). This can be
combined with spawnflags 2 (solid) as spawnflags 6
- New: -gamma N.N switch sets lightmap/vertex gamma value for more
realistic lighting, instead of using high ambient/minlight. Default
is 1.0 (linear ramp), good values are between 1.5 and 2.2
- Changed q3map_lightmapGamma N.N to q3map_lightmapBrightness N.N, to
better match its intended function
- Ported to VS.NET 2003 and GtkRadiant 1.5 (spog_branch)
- Fixed bug in _skybox maps where sunlight was leaking through brushes.
Thanks pjw!
- Fixed bug in ASE loader where models without submodels/submaterials
would not load properly.
- Fixed bug where q3map_tcGen was not being applied to models
- Optimized MergeMetaTriangles a bit
- Added KOR's fix to raytracer
- -bouncegrid no longer affects the lightgrid dir
- Added feature to PicoModel where spaces in material names in 3DS, ASE
and LWO models will mark the end of the shader name, so you can have
"textures/foo/bar 1" and "textures/foo/bar 2" in Lightwave, and both
surfaces will map to "textures/foo/bar"
2.5.11 (2003-12-01)
- New: added support for _skybox entities to generate "portal sky"
surfaces in games w/o native support (Quake 3). _skybox entities have
3 keys: _scale (default 64), and angle/angles (for rotation of the
skybox relative to the map)
- New: added -skyfix switch to BSP phase as a workaround hack for
the black GL_CLAMP border on skybox edges on ATI (and newer nvidia)
video cards. Note: unnecessary in ET or JA
- New: Added _anglescale to light entities for scaling angle attenuation.
Use a small value (< 1.0) to lessen the angle attenuation, and a high
value (> 1.0) for sharper, more faceted lighting
- New: Added _lightmapscale support to misc_model entities
- Custom shaders (external lightmaps, styles) will not be generated
if the find/replace text cannot be found
- Tightened up light culling epsilon from 1.0 to 0.125 to stop certain
surface lights from being incorrectly culled (thanks RasputiN!)
- Fixed bug where small 3 and 4 sided brush faces were getting fanned,
adding triangle/vertex counts
- Moved to Visual Studio .NET, with aggressive optimizations enabled
- Cleaned up missing image warnings
- Parsing images out of shader stages if not found explicit/implicitly
- Loads Enemy Territory implicitMap images if editor/light image not found
2.5.10 (2003-10-22)
- New: Lightwave model support (beta) courtesy of RR2DO2
- New: Heretic 2 FM model support courtesy of Nurail
- Re-enabled vertex cache friendly triangle reordering with fix
- Disabled triangle reordering on certain surfaces, including autosprite
shaders due to visual errors
- Fixed bug in radiosity where sorting of lights by style took forever.
Thanks ReBoOT!
- Fixed bug in sun lighting code where maps too far off the origin would
not be properly it by sun or sky light. Thanks MindLink!
- Entity causing a leak will be printed and selected in Radiant if BSP
monitoring is enabled. Requested by heeen
- Fixed odd bug causing 10x slowdown in lighting in some maps. Should
be back to 2.5.7 performance. Also fixed a couple old bugs related to
autosprite shader (point) lights and backsplash lights not being styled
or setup correctly
2.5.9 (2003-10-12)
- Disabled triangle reordering for now (crashing on some maps)
2.5.8 (2003-10-02)
- New: Added two new sun parameters: angular deviation (width of the sun in
degrees) and sampling count (jitters). This allows for decent approximation
of penumbra "half-shadow" effects from sunlight with 16+ samples. These
parameters are accessible for entity lights (including spots and suns) via
these entity keys: _deviance and _samples. To use in shaders, use the new
q3map_sunExt <r> <g> <b> <brightness> <angle> <elevation> <deviance> <samples>
- New: q3map_lightmapFilterRadius <self> <other> for light-emitting shaders.
Put *after* any q3map_sun directives or else your sun will be filtered. This
is good for eliminating the "stadium lighting" effect from q3map_skyLight.
Also usable as an entity key: _filterradius or _filter
- New: Quake 2 MD2 model support in PicoModel for misc_model entities
(thanks to Nurail!)
- Re-enabled vertex-cache-aware triangle reordering. Will probably have a
negligible effect on rendering performance, but can't hurt
- Added short-circuit to raytracer: any empty nodes (including children) are
ignored on sun traces
- Added BSP file size printout
- Filtering of any kind now disables adaptive supersampling on a per-light,
per-ightmap basis
- Fixed another _minlight <-> styled light interaction bug (thanks pjw!)
2.5.7 (2003-08-31)
- New: Jedi Academy support via -game ja
- New: DDS (DXT1/3/5) texture support for future games
- Re-enabled q3map_surfaceModel support, and the 'oriented' flag works as well
- Re-enabled (fixed, really) large external lightmap support
- Fixed a bug in the model code that would cause a crash if an uninvertable
matrix was created
- Fixed a bug in Mathlib m4x4_t code where the tolerance for a singular matrix
was too low and crapping out on small (scaled down) matrices
- Fixed bug in divide-by-zero on lightmap efficiency calculation
- Added -force switch, allows unsupported BSP formats to (try) to be loaded
2.5.6 (2003-08-15)
- New: can convert BSP files to MAP files via -convert -format map. Note: not
perfect by any means, as certain pieces of data are irretrievably lost, such
as func_group entities (including terrain specifics), brush face texturing
info, and light/misc_model entities with Q3Map2-generated BSPs
2.5.5
- New: -scale N.N mode to scale the BSP
- New: -light -lomem switch to supress trace BSP optimization. This
feature trades lighting performance for decreased memory usage
- New: Added negative light support (note: will not darken below _minlight value)
might screw up radiosity, haven't tested much. Should work with entity
lights, shader lights, sun/sky lights and radiosity. Lightfilter shadows
tint negative lights too, so the end effect is subtraction of the color
- New: Lightstyle support for non-Raven (JK2/SOF2) games, including Quake 3,
RTCW, ET, EF. Only works with lightmapped surfaces, use with care
- Fixed long standing terrain texturing bug, should produce exact desired
results all of the time now. May require fixing alphamaps that were
kludged together to accomodate this bug on existing maps
- Fixed bug (huh, wtf) causing misc_model surfaces to not be fogged
- Fixed bug where fog brushes wouldn't fog surfaces if a global map fog
shader was present
- Fixed bug where -patchmeta surfaces were being ignored for raytracing
- Fixed long-standing bug where twosided surfaces were not correctly
bouncing light. You can now have a foggy glass window that re-emits
bright light with q3map_bounce 3.0 or so
- Fixed really stupid bug in radiosity with bouncing styled lights
- Stripping .map and appending .bsp in -info mode
- Fixed bug where tesselated brush face surfaces were not being fogged
- Twosided surfaces (using cull disable/twosided/none) are now lit twosided
- Added tighter tolerance for alphashadow/lightfilter shadowing to raytracer
which fixed problem with double shadows from tracing near triangle seams
- Brush entities should now be properly fogged. Really.
- Styled lightmaps are no longer affected by _minlight (doh)
2.5.4 (2003-04-01)
- New: q3map_tessSize support for JK2/SOF2
- New: -lightmapsize N argument
- Fixed bug where switched styled lights weren't working correctly in SOF2/JK2
- Fixed bug where external lightmaps with generated shaders were referencing
the wrong lightmap
- Fixed bug causing lightgrid brushes to be ignored
- Added variable sphere around trace sample points to inhibit occluder geometry
2.5.3 (2003-03-06)
- New: SOF2/JK2 light styles now supported
- New: q3map_lightStyle N to set shader lightstyles
- New: Tenebrae 2 support via -game tenebrae
- New: -light -deluxe and -debugdeluxe for Tenebrae "deluxemap" static
lighting algorithm
- Light envelopes now properly clipped to the PVS
- q3map_vertexScale re-enabled (scales vertex lighting per-shader)
- Minor bug in brush bevel code corrected
- Brushes from func_group entities are correctly sorted on insertion
- Fixed a couple misc warnings to print "percent" instead of "%"
- Added -custinfoparms support to -light mode to suppress warnings
- _minlight, _minvertexlight and _mingridlight order independent, and now
allow for values of 0
2.5.2 (2003-02-17)
- Fixed crash bugs with global map fog
- Model loading really only warns once now
2.5.1 (2003-02-17) (Splash Damage internal release)
- Added more Hella-Fast juice to light code. Overall should be 35% faster
- Refactored surface portion of raytracer code for less memory usage
- Changed UVW epsilon in raytracer to catch more edge cases
- Removed bounds check on occluded luxel finding, was causing more problems
than it was solving
- Adaptive antialiasing code now ignores unmapped luxels for better shadow
edges and higher performance
- Brushes in the BSP are now sorted opaque first
- Fixed Really Stupid bug causing MapRawLightmap to take about 4x as long
- Added optimization to make MapRawLightmap 2x as fast
- New non-sucky quadrilateral subdivision of patches for MapRawLightmap
- Patches with < 90 degrees of curvature are now box-lightmapped
- Patch vertex normals now correctly stored, fixing bug in 2.5.0
- Prints warning if map with < 10% detail brushes is detected
2.5.0 (2003-02-14) (Splash Damage internal release)
RAYTRACING AND SHADOW CALCULATION
- New raytracing code. Rewrote the raytracer to maximize efficiency on modern
"caulk-hull" maps. Uses triangle intercept code written by SPoG, based on code
by Tomas Moller and Ben Trumbore (Journal of Graphics Tools, 2(1):21-28, 1997)
and a biased octree leaf subdivision scheme by Y.T.
- Shadows (casting and receiving) are now controllable per-entity
New entity keys: "_castShadows" or "_cs" and "_receiveShadows" or "_rc"
Values: 0 = no shadows, 1 = worldspawn shadows, > 1 explicit shadow group,
negative values imply no worldspawn shadow interation.
*Entities, including model2 and RTCW misc_gamemodels can now cast shadows*
RADIOSITY
- Bumped up default and smallest radiosity patch size. Performance should be
approximately 4x with a small quality tradeoff
- Radiosity patches now trace to centroid of triangle, and not bounds center
- Radiosity and surface lights are now nudged around a bit if in solid
- Radiosity light generation code is now thread-safe
- Radiosity -dump files now .map instead of .pfb
- Poorly worded "q3map_bounce" renamed to "q3map_bounceScale" (old still works)
- New -bounceonly switch to store only bounced light in the BSP (for Tenebrae)
MISC LIGHTING
- Optimized case where light and sample are coplanar
- Forcing nudged luxels to stay within lightmap surfaces' bounds
CURVED SURFACES
- New -subdivisions N argument, works with -patchmeta and -light for setting
patch level-of-detail. Default value is 8, use 4 to simulate default Q3
- All patch tesselation code changed to create x-patterned tesselation for
better lighting
- Storing patch LOD info in the .srf file for better patch light/shadows
FOG
- Reworked fog code to fix bad interation with fog and clipped models
MODELS
- Entities with attached MD3/ASE misc_models now have their bounds correctly set
- Attached misc_models now support q3map_clipModel for solidity
- Missing models will only warn once, rather than spew errors
MISC
- Metasurface merging no longer folds nonplanar triangles into planar surfaces
and vice-versa *
- Fixed Really Stupid Bug where entity numbering was not loaded by lighting code
resulting in lightmaps merging across entity boundaries *
* Might result in slightly larger BSP. For maximum efficiency, ungroup
func_group entities before doing a final compile
TODO
+ Document new shadow stuff
+ Merge adjacent light-casting triangles into convex windings
2.3.38 (2003-02-07)
- New lighting code, return of Smoove-B. Intelligently antialises shadow edges
when you use the new -samples N switch. Get -extra quality in 1/3 the time
- New lightmap filtering code. Now using a proper 0.25/0.5/1.0 filter kernel.
Also operates on individual lightsources, so per-lightsource filter/kernel
settings are now possible
- New -patchmeta fixes, now does stitching and adaptive subdivision.
Thanks Sock!
- Nonsolid patches will no longer be in the BSP when run with -patchmeta
- Misc fog fixes, including q3map_noFog support in maps with global _fog
support (SOF2/JK2)
- Now stripping misc_model entities from the BSP
- Fixed disappearing face bug that's been present since 2.3.36.
Thanks Shadowspawn!
2.3.37 (2003-01-24)
- Building from GtkRadiant CVS trunk
- Added new brush bevel code by MrElusive to fix lingering aas problems (sweet!)
- Added -snap N arg to BSP phase for axial bevel plane snapping to reduce
clipped model plane count (note: will muck with above, use with care)
- Patches in terrain entities should now have proper vertex alpha set
- Fixed bug in fur code where fur was being limited to 2 layers (thanks pazur)
- Reduced vertexlight search area to a reasonable size to keep vertex lighting
times down
2.3.36 (2003-01-15)
- Plane hashing re-enabled (I suck)
- Plane hashing optimized (faster parsing of larger maps)
- Plane finding accuracy code enabled
- New ASE clipping code
+ With above should be 10-50% faster
+ Should generate 33% fewer planes
+ Generates mostly-axial 5-sided polyhedra instead of pyramids,
for tighter 2-sided clipping
- New -light args:
+ -scale N -- scales all lightsources (area, radiosity, point, sky)
+ -sky[scale] N -- scales sky lights (q3map_skylight, q3map_sunlight)
- Changed fur code to scale fur offset based on original vertex alpha
2.3.35 (2003-01-14)
- PicoModel now inverts ASE T coordinate
- BSP to ASE converter now inverts T coordinate
- Disabling 2.3.34 triangle optimization code until I find out why it crashes
- Fixed Conscript-q3map2 to use stack_size ld flags directly on Darwin/OS X
- Added Conscript-q3map2 to q3map2.dsp for easier Win32 edit, *nix target
2.3.34 (2003-01-08)
- Building from merged GtkRadiant 1.2 -> 1.3 merged codebase
- IMPORTANT NEW CHANGE: Light entities are now STRIPPED from the BSP file.
They are re-read in by -light from the MAP file. This has two consequences:
+ It is no longer necessary to re-BSP & re-vis a map in order to change
lighting. You can just change lights in the map file and run -light.
+ Slightly smaller BSP file, due to fewer entities
+ Faster loading time, as the game code doesn't have to deal with them
- Added new -ne (normal epsilon) and -de (distance epsilon) for tuning precision
of plane snapping to correct potential AAS/BSP issues
- Using latest PicoModel, with support for RTCW MDC models
- Surfaces per raw lightmap are now sorted by shader name, which should give
slightly better lightmap efficiency and lower in-game shader counts
- Adjusted model code to use correct m4x4_t code & angles key
- Minor bugfix in patch color gradient calculation code
- Silenced erroneous areaportal warning spew
- q3map_tcGen now works on model surfaces
- Using default radiosity subdivide of 256 again (should make radiosity faster)
- Enabled byte-swapping code so Q3Map2 can be compiled/run on little-endian
architectures (Mac OS X)
2.3.33 (2002-12-08)
- Added new -bouncescale argument for radiosity scaling
- Added -pointscale and -areascale for consistent naming
- Radiosity patch subdivision code enhanced
- Hint portals split the BSP first (higher priority)
- Antiportal and areaportal faces split the BSP last, to minimize errors
- Areaportals work internally like hint and antiportals, so they no longer need
to be full brushes (the other sides can be skip)
- External lightmaps are now named "lm_NNNN.tga" in the maps/mapname dir
- Cleaned up some of -light argument processing
- Planar surfaces w/o lightmaps will no longer be tagged as MST_TRIANGLE_SOUP
(this fixes problems with Particle Studio particles dropping out of view)
2.3.32 (2002-11-30)
- GtkRadiant (1.2.11) integration
- Added epsilon to texture plane choose code to eliminate numerical
inconsistencies on brush faces slanted at 45 degree angles (bug 637)
- Fixed bug in lightmap export after lighting when map contains 0 BSP lightmaps
- Adjusted some light tracing constants to fix certain brush/patch seam shadows
- Tinkered with skylight code again
- Fixed bug where lightgrid would be black if level was compiled with -nogrid
- Fixed -approx code to work in floating-point space, using _minlight
- Fixed bug where vertex light code was using invalid pvs data to create
light list for surface, leading to incorrect vertex lighting
- Fixed related bug in anti-light-leak code that was causing brush faces to go
black (bug 694)
- New: _minlight sets _minvertexlight and (new) _mingridlight automatically
- New: _mingridlight key to set minimum grid lighting
2.3.31 (2002-11-21)
- Stitching the edges of lightmaps on patches that wrap around (cyls and cones)
so the seam is no longer visible
- The -patchmeta switch works better now, the patches are still stored in the
BSP for collision, but are pre-tesselated into nonplanar meta surfaces for
more efficient rendering
- Better, more uniform lightmap sample position finding on patch meshes
- Moved q3map_tcMod and q3map_alphaMod processing to the final phase
- New: q3map_skylight AMOUNT ITERATIONS to replace surfacelight on sky surfaces
for much faster and more uniform sky illumination
2.3.30 (Splash Damage internal release)
- Fixed bug in PicoModel ASE material parsing code
- Fixed a few seam/lightmap precision/projection errors
- Increased MAX_SHADER FILES to 1024 and fixed overrun error when more than that
number of shaders was listed in shaderlist.txt
- Increased a few compiler maximums for larger maps
- New: -np N switch on BSP phase, works like -shadeangle, in that it forces all
planar shaders to be nonplanar with the shading angle specified
- New: -nohint switch on BSP phase, omits hint brushes from compile for testing
- New: -debugaxis switch on light mode. Colors lightmaps based on their lightmap
axis (which direction the lightmap was projected on)
- New: -debugorigin switch on light mode. Colors lightmaps based on the luxel
origin relative to the raw lightmap's bounding box
- New: -debugcluster switch on light mode. Colors lightmaps based on the pvs
cluster the luxel falls into
- New: -convert switch to convert BSP to ASE file (experimental)
- New: q3map_lightmapmergable directive to allow terrain to be mapped onto a
single lightmap page for seamless terrain shadows
2.3.29 (2002-11-03)
- Merged with latest CVS, fixed minor issues with matrix order
- Fixed minor Sys_FPrintf/Sys_Printf substitution typo in Q3Map2
- Expanded debug colors to 12 for debugging surface meshes
- PicoModel: fixed ASE loader to support > 1 texture coordinate per-vertex,
so more models supported correctly, also loading vertex normals
- PicoModel: md3 shader names are now cleaned. Suffixes (such as .tga or .jpg)
are stripped, and \ path separators are changed to /
- New: Add :q3map to the end of any shader name, and it will be interpreted as
the named shader minus :q3map. Example:
textures/shaderlab/concrete:q3map -> textures/shaderlab/concrete
One potential use is the -approx feature to collapse lightmapped surfaces
into vertexlit surfaces, saving lightmap space/memory
- New: q3map_clipModel -- does what you think it does, sort of. This code ix
really experimental, and should *only* be used on large models such as terrain
(not small decorative models). This code will be evolving. Note: the shader's
surfaceparms are inherited by the magic clip brush, so if you have nonsolid
in your model's shader that uses q3map_clipModel, then the brush will also
be nonsolid
2.3.28 (2002-11-01)
- Bug 654 (http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=654):
Fixed problem where brush faces, drawsurfaces, and surfaceparms weren't living
together in perfect harmony (terrain surfaceparms now inherited by brushes)
- Nodraw fog works now, albeit when you're underneath, surfaces above don't get
fogged properly. Could be good for foggy water where you want the above-water
portions to only be occluded by the water surface
- Bug 656 (http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=656):
Number of lightgrid points displayed (byte size is currently out of proportion
due to internal storage format) when Q3Map is called with the -info switch
- Fixed wack surface merging bug where code would attempt to merge triangles not
adjacent to the current set, causing bad lightmap projections on nonplanar
surfaces
- Fixed tiny 1-character bug in 2d lightmap texture allocator where adjacent
luxels were being checked for occlusion rather than the actual source luxel
2.3.27 (2002-10-31) Happy Halloween!
- Fixed minor bug in scriplib bugfix where the last character in a file wasn't
being read.
- Fixed bug where 0-area or bogus triangles were causing crash in MapRawLightmap
if they used a shader with a normalmap (thanks ShadowSpawn)
- Fixed bug where lightmaps were getting hosed levelwide on a prerelease version
of 2.3.27
- Fixed bug where lightmaps were getting knackered on models and certain patches
- Merged latest PicoModel version from seaw0lf, adding support for ASE and WF OBJ
models (preliminary)
- Increased MAX_MAP_PLANES to 0x40000 (~256k)
Known issues:
- Lightmap projection and surface merging on large ASE models sometimes flakes
- Surface to brush surfaceparm propogation doesn't work properly with large
metasurfaces: http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=654
2.3.26 (2002-10-27)
- Now using GtkRadiant's libpng and zlib config (linked as DLLs)
- Fixed bug in script parser where repeat calls to GetToken() were causing
memory corruption
- Fixed SOF2 -rename bug
- When using -game sof2 or -game jk2, the -flares argument is implied
- Added -noflares argument to disable the above behavior
- Added support for flares on entities. Use one of the following keys:
"_flare" "1" -- use default flare (different for each game)
"_flareshader" "path/to/flareshader" -- use a specific flare shader
Note: This only matters in SOF2/JK2 now. Make a light targetted (a spotlight)
to get it to aim the correct direction, otherwise it defaults to pointing
downward. You cannot have omnidirectional flares
- Lightgrid size is automatically increased to accomodate large maps. The
MAX_MAP_LIGHTGRID error will never happen again
2.3.25 (2002-10-22)
- Go Giants!
- Fixed bug where Q3Map would crash on writing the BSP after the light stage.
Thanks to Rap7or (#q3map) and loon8 (Q3W forums) [bug 641]
- Fixed bug where surface lights were not affecting the light grid properly.
Thanks to Shadowspawn and djbob [bug 642]
- NEW: Added -faster support to lightgrid calculations while fixing previous bug
- NEW: Changed it so the BSP file is written to a temp file first, then renamed.
This should prevent BSP file corruption on crashes during writes
2.3.24 (2002-10-20)
- Fixed numerous outstanding bugs and issues.
- Normal interpolation is now improved. It's slightly slower, but more 'correct'
in cases where you have 10 triangles in one plane and 1 triangle in another
meeting up and the 10 triangles were over-affecting the average. Only new
identical normals are averaged now. This change affects phong shading, meta
surfaces, and PicoModel
- PicoModel up to version 0.7.6, BSD license, better 3DS model support
- PicoModel library now fixes broken normals on MD3 and 3DS models
- Bumpmapping code is improved. The correct tangent vectors per-triangle are
now calculated so the bumpmaps are consistent with regards to light direction
- Metasurface merging code optimized. Should be about 100x as fast on complex
maps or maps using models with high triangle counts
- Vertexlight code tweaked a bit
- Triangle/winding orders now more consistent. Tesselated surfaces will have
a uniform triangle ordering (thanks RR2DO2)
- NEW: "vertexDeform move" now parsed and surfaces are merged into the
appropriate BSP leaves they may enter into (thanks to Bart Vrijkorte)
- NEW: shader command: q3map_alphaMod. Currently takes a single form:
q3map_alphaMod dotproduct ( NX NY NZ )
where NX NY NZ are a unit normal (length of 1.0) specifying direction.
An example use would be snow in a shader's 2nd pass, using alphaFunc or
blendFunc:
q3map_alphaMod dotproduct ( 0 0 1 ) // surfaces facing upwards have snow
(idea contributed by RR2DO2)
2.3.23 (2002-10-18)
- In my haste to release the previous version, I neglected to give credit where
it was due. Seaw0lf had as much (probably more) to do with the new model
loading library (PicoModel). Because of his efforts, you can load 3DS models
and use them in misc_model entities.
- PicoModel model library up to version 0.7. Improved 3DS support, more stable.
- Surface models still not reenabled. Soon. :)
- You can now remap a misc_model's shaders like this:
Key "_remapNN" "the/model/shader;the/real/shader"
This works just like TA terrain vertexRemapShader key. You can also supply a
* glob for the source shader if you want all your model's shaders to use the
specified shader:
"_remap" "*;models/mapobjects/tree/bark"
2.3.22 (2002-10-16)
- Moving to sensible Linux-style versioning.
- The misc_model code has been completely rewritten, breaking surface models.
Surface models will reappear in the next release, once the new model API has
stablized.
- New: MD3 and 3D Studio 3DS models now natively supported.
- The misc_model "angles" key now supported. Values: "pitch yaw roll" in keeping
with standard Quake 3 angles order.
- Models scaled with "modelscale_vec" now have proper normal scaling/rotation
(thanks SPOG).
- Models can now be lightmapped.
- Models can now have > 1000 vertexes per surface.
- For best results for above, add the following to models' shaders:
q3map_splotchfix
q3map_nonplanar
- 3DS models' MATERIAL NAMES ARE THE FINAL Q3 SHADER NAMES. YOU HAVE BEEN WARNED.
- Models are generally 13373R. :)
2.3.0-a21 (2002-10-02)
- Fixed a stack of really stupid bugs in the lightgrid code. Should be faster
and more predictable now.
- SOF2/JK2 lightgrid now compiled. This is the first version of Q3Map2 that can
compile full, release-worthy SOF2 and JK2 maps.
- SOF2/JK2 damageshader and damagable brush faces should work correctly now.
2.3.0-a20 (2002-09-26)
- SOF2/JK2 worldspawn "fog" (and "_fog") shader key support for levelwide fog
- SOF2/JK2 light "scale" key to scale light brightness
- SOF2/JK2 -rename function for _bsp and _rmg_bsp shader renaming
2.3.0-a19 (2002-09-24)
- Shaders can now be subclassed (Q3Map relavant portions only, such as
surfaceparms, lighting, texture projection, etc). To subclass an existing
shader, add "q3map_baseshader X" where X is the name of the base shader.
- Preliminary auto-model distribution over surfaces. You can now have things
like grass and tree models automatically distributed across your terrain
or other surfaces. To wit:
q3map_surfacemodel models/mapobjects/tree2/tree2.md3 64 0.001 0.5 4.0 0 360 1
q3map_surfacemodel <path to md3> <density in units> <odds of appearing>
<min scale> <max scale> <min angle> <max angle> <oriented>
The last flag is 1 or 0, and sets whether the model gets fitted to the
orientation of the surface. Not functional yet. See screenshots page for
shots of this in action.
2.3.0-a18 (2002-09-21)
- misc_models can now be attached to any brush model entity. Just target the
brush entity with the misc_model (select model, then entity, hit Ctrl+K)
- q3map_tcMod translate (or shift or offset) <s offset> <t offset>
- q3map_tcMod rotate <degrees> (rotates around origin, not center)
- q3map_tcMod scale <s scale> <t scale>
- Metasurface merging now much, much better. Merges into roughly rectangular or
square areas wherever possible
- q3map_terrain no longer sets Z-axis lightmap projection. It must be set in
the terrain layer shaders if you want previous behavior
- Worlspawn _blocksize key now supports 3 elements for setting X Y and Z splits
independently of each other (use a value of 0 for no splits on that axis)
- Misc bugfixes
2.3.0-a1 through 2.3.0-a17 (2002-07 through 2002-09-20)
- Elite Force support (via -game ef)
- SOF2 and JK2 support (via -game sof2 or -game jk2)
- All new image handling with PNG support
- q3map_lightimage specifies image for radiosity and lighting
- External lightmaps, set using q3map_lightmapsize <width> <height>. Up to
1024 x 1024 supported.
- q3map_lightmapGamma <value> sets the brightness scale of a lightmap
- q3map_lightmapsampleoffset <distance> to fix glitches in lightmapped terrain
- Tons more features and bugfixes. See the forum threads for details/screenshots
- Two new surfaceparms, "antiportal" and "skip," and associated shaders, for
allowing the mapper to more cleanly set up visibility data
- Lightmaps will always have square texels now (no more stretching)
- Vertex light improvements
- Light grid improvements
- q3map_lightrgb support for RTCW

View File

@@ -0,0 +1,375 @@
/* -------------------------------------------------------------------------------
Copyright (C) 1999-2006 Id Software, Inc. and contributors.
For a list of contributors, see the accompanying CONTRIBUTORS file.
This file is part of GtkRadiant.
GtkRadiant 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.
GtkRadiant 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 GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
----------------------------------------------------------------------------------
This code has been altered significantly from its original form, to support
several games based on the Quake III Arena engine, in the form of "Q3Map2."
------------------------------------------------------------------------------- */
/* marker */
#define CONVERT_ASE_C
/* dependencies */
#include "q3map2.h"
/*
ConvertSurface()
converts a bsp drawsurface to an ase chunk
*/
static void ConvertSurface( FILE *f, bspModel_t *model, int modelNum, bspDrawSurface_t *ds, int surfaceNum, vec3_t origin )
{
int i, v, face, a, b, c;
bspDrawVert_t *dv;
vec3_t normal;
char name[ 1024 ];
/* ignore patches for now */
if( ds->surfaceType != MST_PLANAR && ds->surfaceType != MST_TRIANGLE_SOUP )
return;
/* print object header for each dsurf */
sprintf( name, "mat%dmodel%dsurf%d", ds->shaderNum, modelNum, surfaceNum );
fprintf( f, "*GEOMOBJECT\t{\r\n" );
fprintf( f, "\t*NODE_NAME\t\"%s\"\r\n", name );
fprintf( f, "\t*NODE_TM\t{\r\n" );
fprintf( f, "\t\t*NODE_NAME\t\"%s\"\r\n", name );
fprintf( f, "\t\t*INHERIT_POS\t0\t0\t0\r\n" );
fprintf( f, "\t\t*INHERIT_ROT\t0\t0\t0\r\n" );
fprintf( f, "\t\t*INHERIT_SCL\t0\t0\t0\r\n" );
fprintf( f, "\t\t*TM_ROW0\t1.0\t0\t0\r\n" );
fprintf( f, "\t\t*TM_ROW1\t0\t1.0\t0\r\n" );
fprintf( f, "\t\t*TM_ROW2\t0\t0\t1.0\r\n" );
fprintf( f, "\t\t*TM_ROW3\t0\t0\t0\r\n" );
fprintf( f, "\t\t*TM_POS\t%f\t%f\t%f\r\n", origin[ 0 ], origin[ 1 ], origin[ 2 ] );
fprintf( f, "\t}\r\n" );
/* print mesh header */
fprintf( f, "\t*MESH\t{\r\n" );
fprintf( f, "\t\t*TIMEVALUE\t0\r\n" );
fprintf( f, "\t\t*MESH_NUMVERTEX\t%d\r\n", ds->numVerts );
fprintf( f, "\t\t*MESH_NUMFACES\t%d\r\n", ds->numIndexes / 3 );
switch( ds->surfaceType )
{
case MST_PLANAR:
fprintf( f, "\t\t*COMMENT\t\"SURFACETYPE\tMST_PLANAR\"\r\n" );
break;
case MST_TRIANGLE_SOUP:
fprintf( f, "\t\t*COMMENT\t\"SURFACETYPE\tMST_TRIANGLE_SOUP\"\r\n" );
break;
}
/* export vertex xyz */
fprintf( f, "\t\t*MESH_VERTEX_LIST\t{\r\n" );
for( i = 0; i < ds->numVerts; i++ )
{
v = i + ds->firstVert;
dv = &bspDrawVerts[ v ];
fprintf( f, "\t\t\t*MESH_VERTEX\t%d\t%f\t%f\t%f\r\n", i, dv->xyz[ 0 ], dv->xyz[ 1 ], dv->xyz[ 2 ] );
}
fprintf( f, "\t\t}\r\n" );
/* export vertex normals */
fprintf( f, "\t\t*MESH_NORMALS\t{\r\n" );
for( i = 0; i < ds->numIndexes; i += 3 )
{
face = (i / 3);
a = bspDrawIndexes[ i + ds->firstIndex ];
b = bspDrawIndexes[ i + ds->firstIndex + 1 ];
c = bspDrawIndexes[ i + ds->firstIndex + 2 ];
VectorCopy( bspDrawVerts[ a ].normal, normal );
VectorAdd( normal, bspDrawVerts[ b ].normal, normal );
VectorAdd( normal, bspDrawVerts[ c ].normal, normal );
if( VectorNormalize( normal, normal ) )
fprintf( f, "\t\t\t*MESH_FACENORMAL\t%d\t%f\t%f\t%f\r\n", face, normal[ 0 ], normal[ 1 ], normal[ 2 ] );
}
for( i = 0; i < ds->numVerts; i++ )
{
v = i + ds->firstVert;
dv = &bspDrawVerts[ v ];
fprintf( f, "\t\t\t*MESH_VERTEXNORMAL\t%d\t%f\t%f\t%f\r\n", i, dv->normal[ 0 ], dv->normal[ 1 ], dv->normal[ 2 ] );
}
fprintf( f, "\t\t}\r\n" );
/* export faces */
fprintf( f, "\t\t*MESH_FACE_LIST\t{\r\n" );
for( i = 0; i < ds->numIndexes; i += 3 )
{
face = (i / 3);
a = bspDrawIndexes[ i + ds->firstIndex ];
c = bspDrawIndexes[ i + ds->firstIndex + 1 ];
b = bspDrawIndexes[ i + ds->firstIndex + 2 ];
fprintf( f, "\t\t\t*MESH_FACE\t%d\tA:\t%d\tB:\t%d\tC:\t%d\tAB:\t1\tBC:\t1\tCA:\t1\t*MESH_SMOOTHING\t0\t*MESH_MTLID\t0\r\n",
face, a, b, c );
}
fprintf( f, "\t\t}\r\n" );
/* export vertex st */
fprintf( f, "\t\t*MESH_NUMTVERTEX\t%d\r\n", ds->numVerts );
fprintf( f, "\t\t*MESH_TVERTLIST\t{\r\n" );
for( i = 0; i < ds->numVerts; i++ )
{
v = i + ds->firstVert;
dv = &bspDrawVerts[ v ];
fprintf( f, "\t\t\t*MESH_TVERT\t%d\t%f\t%f\t%f\r\n", i, dv->st[ 0 ], (1.0 - dv->st[ 1 ]), 1.0f );
}
fprintf( f, "\t\t}\r\n" );
/* export texture faces */
fprintf( f, "\t\t*MESH_NUMTVFACES\t%d\r\n", ds->numIndexes / 3 );
fprintf( f, "\t\t*MESH_TFACELIST\t{\r\n" );
for( i = 0; i < ds->numIndexes; i += 3 )
{
face = (i / 3);
a = bspDrawIndexes[ i + ds->firstIndex ];
c = bspDrawIndexes[ i + ds->firstIndex + 1 ];
b = bspDrawIndexes[ i + ds->firstIndex + 2 ];
fprintf( f, "\t\t\t*MESH_TFACE\t%d\t%d\t%d\t%d\r\n", face, a, b, c );
}
fprintf( f, "\t\t}\r\n" );
/* print mesh footer */
fprintf( f, "\t}\r\n" );
/* print object footer */
fprintf( f, "\t*PROP_MOTIONBLUR\t0\r\n" );
fprintf( f, "\t*PROP_CASTSHADOW\t1\r\n" );
fprintf( f, "\t*PROP_RECVSHADOW\t1\r\n" );
fprintf( f, "\t*MATERIAL_REF\t%d\r\n", ds->shaderNum );
fprintf( f, "}\r\n" );
}
/*
ConvertModel()
exports a bsp model to an ase chunk
*/
static void ConvertModel( FILE *f, bspModel_t *model, int modelNum, vec3_t origin )
{
int i, s;
bspDrawSurface_t *ds;
/* go through each drawsurf in the model */
for( i = 0; i < model->numBSPSurfaces; i++ )
{
s = i + model->firstBSPSurface;
ds = &bspDrawSurfaces[ s ];
ConvertSurface( f, model, modelNum, ds, s, origin );
}
}
/*
ConvertShader()
exports a bsp shader to an ase chunk
*/
/*
*MATERIAL 0 {
*MATERIAL_NAME "models/test/rock16l"
*MATERIAL_CLASS "Standard"
*MATERIAL_AMBIENT 0.5882 0.5882 0.5882
*MATERIAL_DIFFUSE 0.5882 0.5882 0.5882
*MATERIAL_SPECULAR 0.5882 0.5882 0.5882
*MATERIAL_SHINE 0.0000
*MATERIAL_SHINESTRENGTH 0.0000
*MATERIAL_TRANSPARENCY 0.0000
*MATERIAL_WIRESIZE 1.0000
*MATERIAL_SHADING Phong
*MATERIAL_XP_FALLOFF 0.0000
*MATERIAL_SELFILLUM 0.0000
*MATERIAL_FALLOFF In
*MATERIAL_XP_TYPE Filter
*MAP_DIFFUSE {
*MAP_NAME "Map #2"
*MAP_CLASS "Bitmap"
*MAP_SUBNO 1
*MAP_AMOUNT 1.0000
*BITMAP "models/test/rock16l"
*MAP_TYPE Screen
*UVW_U_OFFSET 0.0000
*UVW_V_OFFSET 0.0000
*UVW_U_TILING 1.0000
*UVW_V_TILING 1.0000
*UVW_ANGLE 0.0000
*UVW_BLUR 1.0000
*UVW_BLUR_OFFSET 0.0000
*UVW_NOUSE_AMT 1.0000
*UVW_NOISE_SIZE 1.0000
*UVW_NOISE_LEVEL 1
*UVW_NOISE_PHASE 0.0000
*BITMAP_FILTER Pyramidal
}
}
*/
static void ConvertShader( FILE *f, bspShader_t *shader, int shaderNum )
{
shaderInfo_t *si;
char *c, filename[ 1024 ];
/* get shader */
si = ShaderInfoForShader( shader->shader );
if( si == NULL )
{
Sys_Printf( "WARNING: NULL shader in BSP\n" );
return;
}
/* set bitmap filename */
if( si->shaderImage->filename[ 0 ] != '*' )
strcpy( filename, si->shaderImage->filename );
else
sprintf( filename, "%s.tga", si->shader );
for( c = filename; *c != '\0'; c++ )
if( *c == '/' )
*c = '\\';
/* print shader info */
fprintf( f, "\t*MATERIAL\t%d\t{\r\n", shaderNum );
fprintf( f, "\t\t*MATERIAL_NAME\t\"%s\"\r\n", shader->shader );
fprintf( f, "\t\t*MATERIAL_CLASS\t\"Standard\"\r\n" );
fprintf( f, "\t\t*MATERIAL_DIFFUSE\t%f\t%f\t%f\r\n", si->color[ 0 ], si->color[ 1 ], si->color[ 2 ] );
fprintf( f, "\t\t*MATERIAL_SHADING Phong\r\n" );
/* print map info */
fprintf( f, "\t\t*MAP_DIFFUSE\t{\r\n" );
fprintf( f, "\t\t\t*MAP_NAME\t\"%s\"\r\n", shader->shader );
fprintf( f, "\t\t\t*MAP_CLASS\t\"Bitmap\"\r\n");
fprintf( f, "\t\t\t*MAP_SUBNO\t1\r\n" );
fprintf( f, "\t\t\t*MAP_AMOUNT\t1.0\r\n" );
fprintf( f, "\t\t\t*MAP_TYPE\tScreen\r\n" );
fprintf( f, "\t\t\t*BITMAP\t\"..\\%s\"\r\n", filename );
fprintf( f, "\t\t\t*BITMAP_FILTER\tPyramidal\r\n" );
fprintf( f, "\t\t}\r\n" );
fprintf( f, "\t}\r\n" );
}
/*
ConvertBSPToASE()
exports an 3d studio ase file from the bsp
*/
int ConvertBSPToASE( char *bspName )
{
int i, modelNum;
FILE *f;
bspShader_t *shader;
bspModel_t *model;
entity_t *e;
vec3_t origin;
const char *key;
char name[ 1024 ], base[ 1024 ];
/* note it */
Sys_Printf( "--- Convert BSP to ASE ---\n" );
/* create the ase filename from the bsp name */
strcpy( name, bspName );
StripExtension( name );
strcat( name, ".ase" );
Sys_Printf( "writing %s\n", name );
ExtractFileBase( bspName, base );
strcat( base, ".bsp" );
/* open it */
f = fopen( name, "wb" );
if( f == NULL )
Error( "Open failed on %s\n", name );
/* print header */
fprintf( f, "*3DSMAX_ASCIIEXPORT\t200\r\n" );
fprintf( f, "*COMMENT\t\"Generated by Q3Map2 (ydnar) -convert -format ase\"\r\n" );
fprintf( f, "*SCENE\t{\r\n" );
fprintf( f, "\t*SCENE_FILENAME\t\"%s\"\r\n", base );
fprintf( f, "\t*SCENE_FIRSTFRAME\t0\r\n" );
fprintf( f, "\t*SCENE_LASTFRAME\t100\r\n" );
fprintf( f, "\t*SCENE_FRAMESPEED\t30\r\n" );
fprintf( f, "\t*SCENE_TICKSPERFRAME\t160\r\n" );
fprintf( f, "\t*SCENE_BACKGROUND_STATIC\t0.0000\t0.0000\t0.0000\r\n" );
fprintf( f, "\t*SCENE_AMBIENT_STATIC\t0.0000\t0.0000\t0.0000\r\n" );
fprintf( f, "}\r\n" );
/* print materials */
fprintf( f, "*MATERIAL_LIST\t{\r\n" );
fprintf( f, "\t*MATERIAL_COUNT\t%d\r\n", numBSPShaders );
for( i = 0; i < numBSPShaders; i++ )
{
shader = &bspShaders[ i ];
ConvertShader( f, shader, i );
}
fprintf( f, "}\r\n" );
/* walk entity list */
for( i = 0; i < numEntities; i++ )
{
/* get entity and model */
e = &entities[ i ];
if( i == 0 )
modelNum = 0;
else
{
key = ValueForKey( e, "model" );
if( key[ 0 ] != '*' )
continue;
modelNum = atoi( key + 1 );
}
model = &bspModels[ modelNum ];
/* get entity origin */
key = ValueForKey( e, "origin" );
if( key[ 0 ] == '\0' )
VectorClear( origin );
else
GetVectorForKey( e, "origin", origin );
/* convert model */
ConvertModel( f, model, modelNum, origin );
}
/* close the file and return */
fclose( f );
/* return to sender */
return 0;
}

View File

@@ -0,0 +1,444 @@
/* -------------------------------------------------------------------------------
Copyright (C) 1999-2006 Id Software, Inc. and contributors.
For a list of contributors, see the accompanying CONTRIBUTORS file.
This file is part of GtkRadiant.
GtkRadiant 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.
GtkRadiant 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 GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
----------------------------------------------------------------------------------
This code has been altered significantly from its original form, to support
several games based on the Quake III Arena engine, in the form of "Q3Map2."
------------------------------------------------------------------------------- */
/* marker */
#define CONVERT_MAP_C
/* dependencies */
#include "q3map2.h"
/*
ConvertBrush()
exports a map brush
*/
#define SNAP_FLOAT_TO_INT 4
#define SNAP_INT_TO_FLOAT (1.0 / SNAP_FLOAT_TO_INT)
static void ConvertBrush( FILE *f, int num, bspBrush_t *brush, vec3_t origin )
{
int i, j;
bspBrushSide_t *side;
side_t *buildSide;
bspShader_t *shader;
char *texture;
bspPlane_t *plane;
vec3_t pts[ 3 ];
/* start brush */
fprintf( f, "\t// brush %d\n", num );
fprintf( f, "\t{\n" );
/* clear out build brush */
for( i = 0; i < buildBrush->numsides; i++ )
{
buildSide = &buildBrush->sides[ i ];
if( buildSide->winding != NULL )
{
FreeWinding( buildSide->winding );
buildSide->winding = NULL;
}
}
buildBrush->numsides = 0;
/* iterate through bsp brush sides */
for( i = 0; i < brush->numSides; i++ )
{
/* get side */
side = &bspBrushSides[ brush->firstSide + i ];
/* get shader */
if( side->shaderNum < 0 || side->shaderNum >= numBSPShaders )
continue;
shader = &bspShaders[ side->shaderNum ];
if( !Q_stricmp( shader->shader, "default" ) || !Q_stricmp( shader->shader, "noshader" ) )
continue;
/* get plane */
plane = &bspPlanes[ side->planeNum ];
/* add build side */
buildSide = &buildBrush->sides[ buildBrush->numsides ];
buildBrush->numsides++;
/* tag it */
buildSide->shaderInfo = ShaderInfoForShader( shader->shader );
buildSide->planenum = side->planeNum;
buildSide->winding = NULL;
}
/* make brush windings */
if( !CreateBrushWindings( buildBrush ) )
return;
/* iterate through build brush sides */
for( i = 0; i < buildBrush->numsides; i++ )
{
/* get build side */
buildSide = &buildBrush->sides[ i ];
/* dummy check */
if( buildSide->shaderInfo == NULL || buildSide->winding == NULL )
continue;
/* get texture name */
if( !Q_strncasecmp( buildSide->shaderInfo->shader, "textures/", 9 ) )
texture = buildSide->shaderInfo->shader + 9;
else
texture = buildSide->shaderInfo->shader;
/* get plane points and offset by origin */
for( j = 0; j < 3; j++ )
{
VectorAdd( buildSide->winding->p[ j ], origin, pts[ j ] );
//% pts[ j ][ 0 ] = SNAP_INT_TO_FLOAT * floor( pts[ j ][ 0 ] * SNAP_FLOAT_TO_INT + 0.5f );
//% pts[ j ][ 1 ] = SNAP_INT_TO_FLOAT * floor( pts[ j ][ 1 ] * SNAP_FLOAT_TO_INT + 0.5f );
//% pts[ j ][ 2 ] = SNAP_INT_TO_FLOAT * floor( pts[ j ][ 2 ] * SNAP_FLOAT_TO_INT + 0.5f );
}
/* print brush side */
/* ( 640 24 -224 ) ( 448 24 -224 ) ( 448 -232 -224 ) common/caulk 0 48 0 0.500000 0.500000 0 0 0 */
fprintf( f, "\t\t( %.3f %.3f %.3f ) ( %.3f %.3f %.3f ) ( %.3f %.3f %.3f ) %s 0 0 0 0.5 0.5 0 0 0\n",
pts[ 0 ][ 0 ], pts[ 0 ][ 1 ], pts[ 0 ][ 2 ],
pts[ 1 ][ 0 ], pts[ 1 ][ 1 ], pts[ 1 ][ 2 ],
pts[ 2 ][ 0 ], pts[ 2 ][ 1 ], pts[ 2 ][ 2 ],
texture );
}
/* end brush */
fprintf( f, "\t}\n\n" );
}
#if 0
/* iterate through the brush sides (ignore the first 6 bevel planes) */
for( i = 0; i < brush->numSides; i++ )
{
/* get side */
side = &bspBrushSides[ brush->firstSide + i ];
/* get shader */
if( side->shaderNum < 0 || side->shaderNum >= numBSPShaders )
continue;
shader = &bspShaders[ side->shaderNum ];
if( !Q_stricmp( shader->shader, "default" ) || !Q_stricmp( shader->shader, "noshader" ) )
continue;
/* get texture name */
if( !Q_strncasecmp( shader->shader, "textures/", 9 ) )
texture = shader->shader + 9;
else
texture = shader->shader;
/* get plane */
plane = &bspPlanes[ side->planeNum ];
/* make plane points */
{
vec3_t vecs[ 2 ];
MakeNormalVectors( plane->normal, vecs[ 0 ], vecs[ 1 ] );
VectorMA( vec3_origin, plane->dist, plane->normal, pts[ 0 ] );
VectorMA( pts[ 0 ], 256.0f, vecs[ 0 ], pts[ 1 ] );
VectorMA( pts[ 0 ], 256.0f, vecs[ 1 ], pts[ 2 ] );
}
/* offset by origin */
for( j = 0; j < 3; j++ )
VectorAdd( pts[ j ], origin, pts[ j ] );
/* print brush side */
/* ( 640 24 -224 ) ( 448 24 -224 ) ( 448 -232 -224 ) common/caulk 0 48 0 0.500000 0.500000 0 0 0 */
fprintf( f, "\t\t( %.3f %.3f %.3f ) ( %.3f %.3f %.3f ) ( %.3f %.3f %.3f ) %s 0 0 0 0.5 0.5 0 0 0\n",
pts[ 0 ][ 0 ], pts[ 0 ][ 1 ], pts[ 0 ][ 2 ],
pts[ 1 ][ 0 ], pts[ 1 ][ 1 ], pts[ 1 ][ 2 ],
pts[ 2 ][ 0 ], pts[ 2 ][ 1 ], pts[ 2 ][ 2 ],
texture );
}
#endif
/*
ConvertPatch()
converts a bsp patch to a map patch
{
patchDef2
{
base_wall/concrete
( 9 3 0 0 0 )
(
( ( 168 168 -192 0 2 ) ( 168 168 -64 0 1 ) ( 168 168 64 0 0 ) ... )
...
)
}
}
*/
static void ConvertPatch( FILE *f, int num, bspDrawSurface_t *ds, vec3_t origin )
{
int x, y;
bspShader_t *shader;
char *texture;
bspDrawVert_t *dv;
vec3_t xyz;
/* only patches */
if( ds->surfaceType != MST_PATCH )
return;
/* get shader */
if( ds->shaderNum < 0 || ds->shaderNum >= numBSPShaders )
return;
shader = &bspShaders[ ds->shaderNum ];
/* get texture name */
if( !Q_strncasecmp( shader->shader, "textures/", 9 ) )
texture = shader->shader + 9;
else
texture = shader->shader;
/* start patch */
fprintf( f, "\t// patch %d\n", num );
fprintf( f, "\t{\n" );
fprintf( f, "\t\tpatchDef2\n" );
fprintf( f, "\t\t{\n" );
fprintf( f, "\t\t\t%s\n", texture );
fprintf( f, "\t\t\t( %d %d 0 0 0 )\n", ds->patchWidth, ds->patchHeight );
fprintf( f, "\t\t\t(\n" );
/* iterate through the verts */
for( x = 0; x < ds->patchWidth; x++ )
{
/* start row */
fprintf( f, "\t\t\t\t(" );
/* iterate through the row */
for( y = 0; y < ds->patchHeight; y++ )
{
/* get vert */
dv = &bspDrawVerts[ ds->firstVert + (y * ds->patchWidth) + x ];
/* offset it */
VectorAdd( origin, dv->xyz, xyz );
/* print vertex */
fprintf( f, " ( %f %f %f %f %f )", xyz[ 0 ], xyz[ 1 ], xyz[ 2 ], dv->st[ 0 ], dv->st[ 1 ] );
}
/* end row */
fprintf( f, " )\n" );
}
/* end patch */
fprintf( f, "\t\t\t)\n" );
fprintf( f, "\t\t}\n" );
fprintf( f, "\t}\n\n" );
}
/*
ConvertModel()
exports a bsp model to a map file
*/
static void ConvertModel( FILE *f, bspModel_t *model, int modelNum, vec3_t origin )
{
int i, num;
bspBrush_t *brush;
bspDrawSurface_t *ds;
/* convert bsp planes to map planes */
nummapplanes = numBSPPlanes;
for( i = 0; i < numBSPPlanes; i++ )
{
VectorCopy( bspPlanes[ i ].normal, mapplanes[ i ].normal );
mapplanes[ i ].dist = bspPlanes[ i ].dist;
mapplanes[ i ].type = PlaneTypeForNormal( mapplanes[ i ].normal );
mapplanes[ i ].hash_chain = NULL;
}
/* allocate a build brush */
buildBrush = AllocBrush( 512 );
buildBrush->entityNum = 0;
buildBrush->original = buildBrush;
/* go through each brush in the model */
for( i = 0; i < model->numBSPBrushes; i++ )
{
num = i + model->firstBSPBrush;
brush = &bspBrushes[ num ];
ConvertBrush( f, num, brush, origin );
}
/* free the build brush */
free( buildBrush );
/* go through each drawsurf in the model */
for( i = 0; i < model->numBSPSurfaces; i++ )
{
num = i + model->firstBSPSurface;
ds = &bspDrawSurfaces[ num ];
/* we only love patches */
if( ds->surfaceType == MST_PATCH )
ConvertPatch( f, num, ds, origin );
}
}
/*
ConvertEPairs()
exports entity key/value pairs to a map file
*/
static void ConvertEPairs( FILE *f, entity_t *e )
{
epair_t *ep;
/* walk epairs */
for( ep = e->epairs; ep != NULL; ep = ep->next )
{
/* ignore empty keys/values */
if( ep->key[ 0 ] == '\0' || ep->value[ 0 ] == '\0' )
continue;
/* ignore model keys with * prefixed values */
if( !Q_stricmp( ep->key, "model" ) && ep->value[ 0 ] == '*' )
continue;
/* emit the epair */
fprintf( f, "\t\"%s\" \"%s\"\n", ep->key, ep->value );
}
}
/*
ConvertBSPToMap()
exports an quake map file from the bsp
*/
int ConvertBSPToMap( char *bspName )
{
int i, modelNum;
FILE *f;
bspModel_t *model;
entity_t *e;
vec3_t origin;
const char *value;
char name[ 1024 ], base[ 1024 ];
/* note it */
Sys_Printf( "--- Convert BSP to MAP ---\n" );
/* create the bsp filename from the bsp name */
strcpy( name, bspName );
StripExtension( name );
strcat( name, "_converted.map" );
Sys_Printf( "writing %s\n", name );
ExtractFileBase( bspName, base );
strcat( base, ".bsp" );
/* open it */
f = fopen( name, "wb" );
if( f == NULL )
Error( "Open failed on %s\n", name );
/* print header */
fprintf( f, "// Generated by Q3Map2 (ydnar) -convert -format map\n" );
/* walk entity list */
for( i = 0; i < numEntities; i++ )
{
/* get entity */
e = &entities[ i ];
/* start entity */
fprintf( f, "// entity %d\n", i );
fprintf( f, "{\n" );
/* export keys */
ConvertEPairs( f, e );
fprintf( f, "\n" );
/* get model num */
if( i == 0 )
modelNum = 0;
else
{
value = ValueForKey( e, "model" );
if( value[ 0 ] == '*' )
modelNum = atoi( value + 1 );
else
modelNum = -1;
}
/* only handle bsp models */
if( modelNum >= 0 )
{
/* get model */
model = &bspModels[ modelNum ];
/* get entity origin */
value = ValueForKey( e, "origin" );
if( value[ 0 ] == '\0' )
VectorClear( origin );
else
GetVectorForKey( e, "origin", origin );
/* convert model */
ConvertModel( f, model, modelNum, origin );
}
/* end entity */
fprintf( f, "}\n\n" );
}
/* close the file and return */
fclose( f );
/* return to sender */
return 0;
}

View File

@@ -0,0 +1,905 @@
/* -------------------------------------------------------------------------------
Copyright (C) 1999-2006 Id Software, Inc. and contributors.
For a list of contributors, see the accompanying CONTRIBUTORS file.
This file is part of GtkRadiant.
GtkRadiant 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.
GtkRadiant 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 GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
----------------------------------------------------------------------------------
This code has been altered significantly from its original form, to support
several games based on the Quake III Arena engine, in the form of "Q3Map2."
------------------------------------------------------------------------------- */
/* marker */
#define DECALS_C
/* dependencies */
#include "q3map2.h"
#define MAX_PROJECTORS 1024
typedef struct decalProjector_s
{
shaderInfo_t *si;
vec3_t mins, maxs;
vec3_t center;
float radius, radius2;
int numPlanes; /* either 5 or 6, for quad or triangle projectors */
vec4_t planes[ 6 ];
vec4_t texMat[ 2 ];
}
decalProjector_t;
static int numProjectors = 0;
static decalProjector_t projectors[ MAX_PROJECTORS ];
static int numDecalSurfaces = 0;
static vec3_t entityOrigin;
/*
DVectorNormalize()
normalizes a vector, returns the length, operates using doubles
*/
typedef double dvec_t;
typedef dvec_t dvec3_t[ 3 ];
dvec_t DVectorNormalize( dvec3_t in, dvec3_t out )
{
dvec_t len, ilen;
len = (dvec_t) sqrt( in[ 0 ] * in[ 0 ] + in[ 1 ] * in[ 1 ] + in[ 2 ] * in[ 2 ] );
if( len == 0.0 )
{
VectorClear( out );
return 0.0;
}
ilen = 1.0 / len;
out[ 0 ] = in[ 0 ] * ilen;
out[ 1 ] = in[ 1 ] * ilen;
out[ 2 ] = in[ 2 ] * ilen;
return len;
}
/*
MakeTextureMatrix()
generates a texture projection matrix for a triangle
returns qfalse if a texture matrix cannot be created
*/
#define Vector2Subtract(a,b,c) ((c)[ 0 ] = (a)[ 0 ] - (b)[ 0 ], (c)[ 1 ] = (a)[ 1 ] - (b)[ 1 ])
static qboolean MakeTextureMatrix( decalProjector_t *dp, vec4_t projection, bspDrawVert_t *a, bspDrawVert_t *b, bspDrawVert_t *c )
{
int i, j;
double bb, s, t, d;
dvec3_t pa, pb, pc;
dvec3_t bary, xyz;
dvec3_t vecs[ 3 ], axis[ 3 ], lengths;
/* project triangle onto plane of projection */
d = DotProduct( a->xyz, projection ) - projection[ 3 ];
VectorMA( a->xyz, -d, projection, pa );
d = DotProduct( b->xyz, projection ) - projection[ 3 ];
VectorMA( b->xyz, -d, projection, pb );
d = DotProduct( c->xyz, projection ) - projection[ 3 ];
VectorMA( c->xyz, -d, projection, pc );
/* two methods */
#if 1
{
/* old code */
/* calculate barycentric basis for the triangle */
bb = (b->st[ 0 ] - a->st[ 0 ]) * (c->st[ 1 ] - a->st[ 1 ]) - (c->st[ 0 ] - a->st[ 0 ]) * (b->st[ 1 ] - a->st[ 1 ]);
if( fabs( bb ) < 0.00000001 )
return qfalse;
/* calculate texture origin */
#if 0
s = 0.0;
t = 0.0;
bary[ 0 ] = ((b->st[ 0 ] - s) * (c->st[ 1 ] - t) - (c->st[ 0 ] - s) * (b->st[ 1 ] - t)) / bb;
bary[ 1 ] = ((c->st[ 0 ] - s) * (a->st[ 1 ] - t) - (a->st[ 0 ] - s) * (c->st[ 1 ] - t)) / bb;
bary[ 2 ] = ((a->st[ 0 ] - s) * (b->st[ 1 ] - t) - (b->st[ 0 ] - s) * (a->st[ 1 ] - t)) / bb;
origin[ 0 ] = bary[ 0 ] * pa[ 0 ] + bary[ 1 ] * pb[ 0 ] + bary[ 2 ] * pc[ 0 ];
origin[ 1 ] = bary[ 0 ] * pa[ 1 ] + bary[ 1 ] * pb[ 1 ] + bary[ 2 ] * pc[ 1 ];
origin[ 2 ] = bary[ 0 ] * pa[ 2 ] + bary[ 1 ] * pb[ 2 ] + bary[ 2 ] * pc[ 2 ];
#endif
/* calculate s vector */
s = a->st[ 0 ] + 1.0;
t = a->st[ 1 ] + 0.0;
bary[ 0 ] = ((b->st[ 0 ] - s) * (c->st[ 1 ] - t) - (c->st[ 0 ] - s) * (b->st[ 1 ] - t)) / bb;
bary[ 1 ] = ((c->st[ 0 ] - s) * (a->st[ 1 ] - t) - (a->st[ 0 ] - s) * (c->st[ 1 ] - t)) / bb;
bary[ 2 ] = ((a->st[ 0 ] - s) * (b->st[ 1 ] - t) - (b->st[ 0 ] - s) * (a->st[ 1 ] - t)) / bb;
xyz[ 0 ] = bary[ 0 ] * pa[ 0 ] + bary[ 1 ] * pb[ 0 ] + bary[ 2 ] * pc[ 0 ];
xyz[ 1 ] = bary[ 0 ] * pa[ 1 ] + bary[ 1 ] * pb[ 1 ] + bary[ 2 ] * pc[ 1 ];
xyz[ 2 ] = bary[ 0 ] * pa[ 2 ] + bary[ 1 ] * pb[ 2 ] + bary[ 2 ] * pc[ 2 ];
//% VectorSubtract( xyz, origin, vecs[ 0 ] );
VectorSubtract( xyz, pa, vecs[ 0 ] );
/* calculate t vector */
s = a->st[ 0 ] + 0.0;
t = a->st[ 1 ] + 1.0;
bary[ 0 ] = ((b->st[ 0 ] - s) * (c->st[ 1 ] - t) - (c->st[ 0 ] - s) * (b->st[ 1 ] - t)) / bb;
bary[ 1 ] = ((c->st[ 0 ] - s) * (a->st[ 1 ] - t) - (a->st[ 0 ] - s) * (c->st[ 1 ] - t)) / bb;
bary[ 2 ] = ((a->st[ 0 ] - s) * (b->st[ 1 ] - t) - (b->st[ 0 ] - s) * (a->st[ 1 ] - t)) / bb;
xyz[ 0 ] = bary[ 0 ] * pa[ 0 ] + bary[ 1 ] * pb[ 0 ] + bary[ 2 ] * pc[ 0 ];
xyz[ 1 ] = bary[ 0 ] * pa[ 1 ] + bary[ 1 ] * pb[ 1 ] + bary[ 2 ] * pc[ 1 ];
xyz[ 2 ] = bary[ 0 ] * pa[ 2 ] + bary[ 1 ] * pb[ 2 ] + bary[ 2 ] * pc[ 2 ];
//% VectorSubtract( xyz, origin, vecs[ 1 ] );
VectorSubtract( xyz, pa, vecs[ 1 ] );
/* calcuate r vector */
VectorScale( projection, -1.0, vecs[ 2 ] );
/* calculate transform axis */
for( i = 0; i < 3; i++ )
lengths[ i ] = DVectorNormalize( vecs[ i ], axis[ i ] );
for( i = 0; i < 2; i++ )
for( j = 0; j < 3; j++ )
dp->texMat[ i ][ j ] = lengths[ i ] > 0.0 ? (axis[ i ][ j ] / lengths[ i ]) : 0.0;
//% dp->texMat[ i ][ j ] = fabs( vecs[ i ][ j ] ) > 0.0 ? (1.0 / vecs[ i ][ j ]) : 0.0;
//% dp->texMat[ i ][ j ] = axis[ i ][ j ] > 0.0 ? (1.0 / axis[ i ][ j ]) : 0.0;
/* calculalate translation component */
dp->texMat[ 0 ][ 3 ] = a->st[ 0 ] - DotProduct( a->xyz, dp->texMat[ 0 ] );
dp->texMat[ 1 ][ 3 ] = a->st[ 1 ] - DotProduct( a->xyz, dp->texMat[ 1 ] );
}
#else
{
int k;
dvec3_t origin, deltas[ 3 ];
double texDeltas[ 3 ][ 2 ];
double delta, texDelta;
/* new code */
/* calculate deltas */
VectorSubtract( pa, pb, deltas[ 0 ] );
VectorSubtract( pa, pc, deltas[ 1 ] );
VectorSubtract( pb, pc, deltas[ 2 ] );
Vector2Subtract( a->st, b->st, texDeltas[ 0 ] );
Vector2Subtract( a->st, c->st, texDeltas[ 1 ] );
Vector2Subtract( b->st, c->st, texDeltas[ 2 ] );
/* walk st */
for( i = 0; i < 2; i++ )
{
/* walk xyz */
for( j = 0; j < 3; j++ )
{
/* clear deltas */
delta = 0.0;
texDelta = 0.0;
/* walk deltas */
for( k = 0; k < 3; k++ )
{
if( fabs( deltas[ k ][ j ] ) > delta &&
fabs( texDeltas[ k ][ i ] ) > texDelta )
{
delta = deltas[ k ][ j ];
texDelta = texDeltas[ k ][ i ];
}
}
/* set texture matrix component */
if( fabs( delta ) > 0.0 )
dp->texMat[ i ][ j ] = texDelta / delta;
else
dp->texMat[ i ][ j ] = 0.0;
}
/* set translation component */
dp->texMat[ i ][ 3 ] = a->st[ i ] - DotProduct( pa, dp->texMat[ i ] );
}
}
#endif
/* debug code */
#if 1
Sys_Printf( "Mat: [ %f %f %f %f ] [ %f %f %f %f ] Theta: %f (%f)\n",
dp->texMat[ 0 ][ 0 ], dp->texMat[ 0 ][ 1 ], dp->texMat[ 0 ][ 2 ], dp->texMat[ 0 ][ 3 ],
dp->texMat[ 1 ][ 0 ], dp->texMat[ 1 ][ 1 ], dp->texMat[ 1 ][ 2 ], dp->texMat[ 1 ][ 3 ],
RAD2DEG( acos( DotProduct( dp->texMat[ 0 ], dp->texMat[ 1 ] ) ) ),
RAD2DEG( acos( DotProduct( axis[ 0 ], axis[ 1 ] ) ) ) );
Sys_Printf( "XYZ: %f %f %f ST: %f %f ST(t): %f %f\n",
a->xyz[ 0 ], a->xyz[ 1 ], a->xyz[ 2 ],
a->st[ 0 ], a->st[ 1 ],
DotProduct( a->xyz, dp->texMat[ 0 ] ) + dp->texMat[ 0 ][ 3 ], DotProduct( a->xyz, dp->texMat[ 1 ] ) + dp->texMat[ 1 ][ 3 ] );
#endif
/* test texture matrix */
s = DotProduct( a->xyz, dp->texMat[ 0 ] ) + dp->texMat[ 0 ][ 3 ];
t = DotProduct( a->xyz, dp->texMat[ 1 ] ) + dp->texMat[ 1 ][ 3 ];
if( fabs( s - a->st[ 0 ] ) > 0.01 || fabs( t - a->st[ 1 ] ) > 0.01 )
{
Sys_Printf( "Bad texture matrix! (A) (%f, %f) != (%f, %f)\n",
s, t, a->st[ 0 ], a->st[ 1 ] );
//% return qfalse;
}
s = DotProduct( b->xyz, dp->texMat[ 0 ] ) + dp->texMat[ 0 ][ 3 ];
t = DotProduct( b->xyz, dp->texMat[ 1 ] ) + dp->texMat[ 1 ][ 3 ];
if( fabs( s - b->st[ 0 ] ) > 0.01 || fabs( t - b->st[ 1 ] ) > 0.01 )
{
Sys_Printf( "Bad texture matrix! (B) (%f, %f) != (%f, %f)\n",
s, t, b->st[ 0 ], b->st[ 1 ] );
//% return qfalse;
}
s = DotProduct( c->xyz, dp->texMat[ 0 ] ) + dp->texMat[ 0 ][ 3 ];
t = DotProduct( c->xyz, dp->texMat[ 1 ] ) + dp->texMat[ 1 ][ 3 ];
if( fabs( s - c->st[ 0 ] ) > 0.01 || fabs( t - c->st[ 1 ] ) > 0.01 )
{
Sys_Printf( "Bad texture matrix! (C) (%f, %f) != (%f, %f)\n",
s, t, c->st[ 0 ], c->st[ 1 ] );
//% return qfalse;
}
/* disco */
return qtrue;
}
/*
TransformDecalProjector()
transforms a decal projector
note: non-normalized axes will screw up the plane transform
*/
static void TransformDecalProjector( decalProjector_t *in, vec3_t axis[ 3 ], vec3_t origin, decalProjector_t *out )
{
int i;
/* copy misc stuff */
out->si = in->si;
out->numPlanes = in->numPlanes;
/* translate bounding box and sphere (note: rotated projector bounding box will be invalid!) */
VectorSubtract( in->mins, origin, out->mins );
VectorSubtract( in->maxs, origin, out->maxs );
VectorSubtract( in->center, origin, out->center );
out->radius = in->radius;
out->radius2 = in->radius2;
/* translate planes */
for( i = 0; i < in->numPlanes; i++ )
{
out->planes[ i ][ 0 ] = DotProduct( in->planes[ i ], axis[ 0 ] );
out->planes[ i ][ 1 ] = DotProduct( in->planes[ i ], axis[ 1 ] );
out->planes[ i ][ 2 ] = DotProduct( in->planes[ i ], axis[ 2 ] );
out->planes[ i ][ 3 ] = in->planes[ i ][ 3 ] - DotProduct( out->planes[ i ], origin );
}
/* translate texture matrix */
for( i = 0; i < 2; i++ )
{
out->texMat[ i ][ 0 ] = DotProduct( in->texMat[ i ], axis[ 0 ] );
out->texMat[ i ][ 1 ] = DotProduct( in->texMat[ i ], axis[ 1 ] );
out->texMat[ i ][ 2 ] = DotProduct( in->texMat[ i ], axis[ 2 ] );
out->texMat[ i ][ 3 ] = in->texMat[ i ][ 3 ] + DotProduct( out->texMat[ i ], origin );
}
}
/*
MakeDecalProjector()
creates a new decal projector from a triangle
*/
static int MakeDecalProjector( shaderInfo_t *si, vec4_t projection, float distance, int numVerts, bspDrawVert_t **dv )
{
int i, j;
decalProjector_t *dp;
vec3_t xyz;
/* dummy check */
if( numVerts != 3 && numVerts != 4 )
return -1;
/* limit check */
if( numProjectors >= MAX_PROJECTORS )
{
Sys_Printf( "WARNING: MAX_PROJECTORS (%d) exceeded, no more decal projectors available.\n", MAX_PROJECTORS );
return -2;
}
/* create a new projector */
dp = &projectors[ numProjectors ];
memset( dp, 0, sizeof( *dp ) );
/* basic setup */
dp->si = si;
dp->numPlanes = numVerts + 2;
/* make texture matrix */
if( !MakeTextureMatrix( dp, projection, dv[ 0 ], dv[ 1 ], dv[ 2 ] ) )
return -1;
/* bound the projector */
ClearBounds( dp->mins, dp->maxs );
for( i = 0; i < numVerts; i++ )
{
AddPointToBounds( dv[ i ]->xyz, dp->mins, dp->maxs );
VectorMA( dv[ i ]->xyz, distance, projection, xyz );
AddPointToBounds( xyz, dp->mins, dp->maxs );
}
/* make bouding sphere */
VectorAdd( dp->mins, dp->maxs, dp->center );
VectorScale( dp->center, 0.5f, dp->center );
VectorSubtract( dp->maxs, dp->center, xyz );
dp->radius = VectorLength( xyz );
dp->radius2 = dp->radius * dp->radius;
/* make the front plane */
if( !PlaneFromPoints( dp->planes[ 0 ], dv[ 0 ]->xyz, dv[ 1 ]->xyz, dv[ 2 ]->xyz ) )
return -1;
/* make the back plane */
VectorSubtract( vec3_origin, dp->planes[ 0 ], dp->planes[ 1 ] );
VectorMA( dv[ 0 ]->xyz, distance, projection, xyz );
dp->planes[ 1 ][ 3 ] = DotProduct( xyz, dp->planes[ 1 ] );
/* make the side planes */
for( i = 0; i < numVerts; i++ )
{
j = (i + 1) % numVerts;
VectorMA( dv[ i ]->xyz, distance, projection, xyz );
if( !PlaneFromPoints( dp->planes[ i + 2 ], dv[ j ]->xyz, dv[ i ]->xyz, xyz ) )
return -1;
}
/* return ok */
numProjectors++;
return numProjectors - 1;
}
/*
ProcessDecals()
finds all decal entities and creates decal projectors
*/
#define PLANAR_EPSILON 0.5f
void ProcessDecals( void )
{
int i, j, x, y, pw[ 5 ], r, iterations;
float distance;
vec4_t projection, plane;
vec3_t origin, target, delta;
entity_t *e, *e2;
parseMesh_t *p;
mesh_t *mesh, *subdivided;
bspDrawVert_t *dv[ 4 ];
const char *value;
/* note it */
Sys_FPrintf( SYS_VRB, "--- ProcessDecals ---\n" );
/* walk entity list */
for( i = 0; i < numEntities; i++ )
{
/* get entity */
e = &entities[ i ];
value = ValueForKey( e, "classname" );
if( Q_stricmp( value, "_decal" ) )
continue;
/* any patches? */
if( e->patches == NULL )
{
Sys_Printf( "WARNING: Decal entity without any patch meshes, ignoring.\n" );
e->epairs = NULL; /* fixme: leak! */
continue;
}
/* find target */
value = ValueForKey( e, "target" );
e2 = FindTargetEntity( value );
/* no target? */
if( e2 == NULL )
{
Sys_Printf( "WARNING: Decal entity without a valid target, ignoring.\n" );
continue;
}
/* walk entity patches */
for( p = e->patches; p != NULL; p = e->patches )
{
/* setup projector */
if( VectorCompare( e->origin, vec3_origin ) )
{
VectorAdd( p->eMins, p->eMaxs, origin );
VectorScale( origin, 0.5f, origin );
}
else
VectorCopy( e->origin, origin );
VectorCopy( e2->origin, target );
VectorSubtract( target, origin, delta );
/* setup projection plane */
distance = VectorNormalize( delta, projection );
projection[ 3 ] = DotProduct( origin, projection );
/* create projectors */
if( distance > 0.125f )
{
/* tesselate the patch */
iterations = IterationsForCurve( p->longestCurve, patchSubdivisions );
subdivided = SubdivideMesh2( p->mesh, iterations );
/* fit it to the curve and remove colinear verts on rows/columns */
PutMeshOnCurve( *subdivided );
mesh = RemoveLinearMeshColumnsRows( subdivided );
FreeMesh( subdivided );
/* offset by projector origin */
for( j = 0; j < (mesh->width * mesh->height); j++ )
VectorAdd( mesh->verts[ j ].xyz, e->origin, mesh->verts[ j ].xyz );
/* iterate through the mesh quads */
for( y = 0; y < (mesh->height - 1); y++ )
{
for( x = 0; x < (mesh->width - 1); x++ )
{
/* set indexes */
pw[ 0 ] = x + (y * mesh->width);
pw[ 1 ] = x + ((y + 1) * mesh->width);
pw[ 2 ] = x + 1 + ((y + 1) * mesh->width);
pw[ 3 ] = x + 1 + (y * mesh->width);
pw[ 4 ] = x + (y * mesh->width); /* same as pw[ 0 ] */
/* set radix */
r = (x + y) & 1;
/* get drawverts */
dv[ 0 ] = &mesh->verts[ pw[ r + 0 ] ];
dv[ 1 ] = &mesh->verts[ pw[ r + 1 ] ];
dv[ 2 ] = &mesh->verts[ pw[ r + 2 ] ];
dv[ 3 ] = &mesh->verts[ pw[ r + 3 ] ];
/* planar? (nuking this optimization as it doesn't work on non-rectangular quads) */
plane[ 0 ] = 0.0f; /* stupid msvc */
if( 0 && PlaneFromPoints( plane, dv[ 0 ]->xyz, dv[ 1 ]->xyz, dv[ 2 ]->xyz ) &&
fabs( DotProduct( dv[ 1 ]->xyz, plane ) - plane[ 3 ] ) <= PLANAR_EPSILON )
{
/* make a quad projector */
MakeDecalProjector( p->shaderInfo, projection, distance, 4, dv );
}
else
{
/* make first triangle */
MakeDecalProjector( p->shaderInfo, projection, distance, 3, dv );
/* make second triangle */
dv[ 1 ] = dv[ 2 ];
dv[ 2 ] = dv[ 3 ];
MakeDecalProjector( p->shaderInfo, projection, distance, 3, dv );
}
}
}
/* clean up */
free( mesh );
}
/* remove patch from entity (fixme: leak!) */
e->patches = p->next;
/* push patch to worldspawn (enable this to debug projectors) */
#if 0
p->next = entities[ 0 ].patches;
entities[ 0 ].patches = p;
#endif
}
}
/* emit some stats */
Sys_FPrintf( SYS_VRB, "%9d decal projectors\n", numProjectors );
}
/*
ProjectDecalOntoWinding()
projects a decal onto a winding
*/
static void ProjectDecalOntoWinding( decalProjector_t *dp, mapDrawSurface_t *ds, winding_t *w )
{
int i, j;
float d, d2, alpha;
winding_t *front, *back;
mapDrawSurface_t *ds2;
bspDrawVert_t *dv;
vec4_t plane;
/* dummy check */
if( w->numpoints < 3 )
{
FreeWinding( w );
return;
}
/* offset by entity origin */
for( i = 0; i < w->numpoints; i++ )
VectorAdd( w->p[ i ], entityOrigin, w->p[ i ] );
/* make a plane from the winding */
if( !PlaneFromPoints( plane, w->p[ 0 ], w->p[ 1 ], w->p[ 2 ] ) )
{
FreeWinding( w );
return;
}
/* backface check */
d = DotProduct( dp->planes[ 0 ], plane );
if( d < -0.0001f )
{
FreeWinding( w );
return;
}
/* walk list of planes */
for( i = 0; i < dp->numPlanes; i++ )
{
/* chop winding by the plane */
ClipWindingEpsilon( w, dp->planes[ i ], dp->planes[ i ][ 3 ], 0.0625f, &front, &back );
FreeWinding( w );
/* lose the front fragment */
if( front != NULL )
FreeWinding( front );
/* if nothing left in back, then bail */
if( back == NULL )
return;
/* reset winding */
w = back;
}
/* nothing left? */
if( w == NULL || w->numpoints < 3 )
return;
/* add to counts */
numDecalSurfaces++;
/* make a new surface */
ds2 = AllocDrawSurface( SURFACE_DECAL );
/* set it up */
ds2->entityNum = ds->entityNum;
ds2->castShadows = ds->castShadows;
ds2->recvShadows = ds->recvShadows;
ds2->shaderInfo = dp->si;
ds2->fogNum = ds->fogNum; /* why was this -1? */
ds2->lightmapScale = ds->lightmapScale;
ds2->numVerts = w->numpoints;
ds2->verts = safe_malloc( ds2->numVerts * sizeof( *ds2->verts ) );
memset( ds2->verts, 0, ds2->numVerts * sizeof( *ds2->verts ) );
/* set vertexes */
for( i = 0; i < ds2->numVerts; i++ )
{
/* get vertex */
dv = &ds2->verts[ i ];
/* set alpha */
d = DotProduct( w->p[ i ], dp->planes[ 0 ] ) - dp->planes[ 0 ][ 3 ];
d2 = DotProduct( w->p[ i ], dp->planes[ 1 ] ) - dp->planes[ 1 ][ 3 ];
alpha = 255.0f * d2 / (d + d2);
if( alpha > 255 )
alpha = 255;
else if( alpha < 0 )
alpha = 0;
/* set misc */
VectorSubtract( w->p[ i ], entityOrigin, dv->xyz );
VectorCopy( plane, dv->normal );
dv->st[ 0 ] = DotProduct( dv->xyz, dp->texMat[ 0 ] ) + dp->texMat[ 0 ][ 3 ];
dv->st[ 1 ] = DotProduct( dv->xyz, dp->texMat[ 1 ] ) + dp->texMat[ 1 ][ 3 ];
/* set color */
for( j = 0; j < MAX_LIGHTMAPS; j++ )
{
dv->color[ j ][ 0 ] = 255;
dv->color[ j ][ 1 ] = 255;
dv->color[ j ][ 2 ] = 255;
dv->color[ j ][ 3 ] = alpha;
}
}
}
/*
ProjectDecalOntoFace()
projects a decal onto a brushface surface
*/
static void ProjectDecalOntoFace( decalProjector_t *dp, mapDrawSurface_t *ds )
{
vec4_t plane;
float d;
winding_t *w;
/* dummy check */
if( ds->sideRef == NULL || ds->sideRef->side == NULL )
return;
/* backface check */
if( ds->planar )
{
VectorCopy( mapplanes[ ds->planeNum ].normal, plane );
plane[ 3 ] = mapplanes[ ds->planeNum ].dist + DotProduct( plane, entityOrigin );
d = DotProduct( dp->planes[ 0 ], plane );
if( d < -0.0001f )
return;
}
/* generate decal */
w = WindingFromDrawSurf( ds );
ProjectDecalOntoWinding( dp, ds, w );
}
/*
ProjectDecalOntoPatch()
projects a decal onto a patch surface
*/
static void ProjectDecalOntoPatch( decalProjector_t *dp, mapDrawSurface_t *ds )
{
int x, y, pw[ 5 ], r, iterations;
vec4_t plane;
float d;
mesh_t src, *mesh, *subdivided;
winding_t *w;
/* backface check */
if( ds->planar )
{
VectorCopy( mapplanes[ ds->planeNum ].normal, plane );
plane[ 3 ] = mapplanes[ ds->planeNum ].dist + DotProduct( plane, entityOrigin );
d = DotProduct( dp->planes[ 0 ], plane );
if( d < -0.0001f )
return;
}
/* tesselate the patch */
src.width = ds->patchWidth;
src.height = ds->patchHeight;
src.verts = ds->verts;
iterations = IterationsForCurve( ds->longestCurve, patchSubdivisions );
subdivided = SubdivideMesh2( src, iterations );
/* fit it to the curve and remove colinear verts on rows/columns */
PutMeshOnCurve( *subdivided );
mesh = RemoveLinearMeshColumnsRows( subdivided );
FreeMesh( subdivided );
/* iterate through the mesh quads */
for( y = 0; y < (mesh->height - 1); y++ )
{
for( x = 0; x < (mesh->width - 1); x++ )
{
/* set indexes */
pw[ 0 ] = x + (y * mesh->width);
pw[ 1 ] = x + ((y + 1) * mesh->width);
pw[ 2 ] = x + 1 + ((y + 1) * mesh->width);
pw[ 3 ] = x + 1 + (y * mesh->width);
pw[ 4 ] = x + (y * mesh->width); /* same as pw[ 0 ] */
/* set radix */
r = (x + y) & 1;
/* generate decal for first triangle */
w = AllocWinding( 3 );
w->numpoints = 3;
VectorCopy( mesh->verts[ pw[ r + 0 ] ].xyz, w->p[ 0 ] );
VectorCopy( mesh->verts[ pw[ r + 1 ] ].xyz, w->p[ 1 ] );
VectorCopy( mesh->verts[ pw[ r + 2 ] ].xyz, w->p[ 2 ] );
ProjectDecalOntoWinding( dp, ds, w );
/* generate decal for second triangle */
w = AllocWinding( 3 );
w->numpoints = 3;
VectorCopy( mesh->verts[ pw[ r + 0 ] ].xyz, w->p[ 0 ] );
VectorCopy( mesh->verts[ pw[ r + 2 ] ].xyz, w->p[ 1 ] );
VectorCopy( mesh->verts[ pw[ r + 3 ] ].xyz, w->p[ 2 ] );
ProjectDecalOntoWinding( dp, ds, w );
}
}
/* clean up */
free( mesh );
}
/*
ProjectDecalOntoTriangles()
projects a decal onto a triangle surface
*/
static void ProjectDecalOntoTriangles( decalProjector_t *dp, mapDrawSurface_t *ds )
{
int i;
vec4_t plane;
float d;
winding_t *w;
/* triangle surfaces without shaders don't get marks by default */
if( ds->type == SURFACE_TRIANGLES && ds->shaderInfo->shaderText == NULL )
return;
/* backface check */
if( ds->planar )
{
VectorCopy( mapplanes[ ds->planeNum ].normal, plane );
plane[ 3 ] = mapplanes[ ds->planeNum ].dist + DotProduct( plane, entityOrigin );
d = DotProduct( dp->planes[ 0 ], plane );
if( d < -0.0001f )
return;
}
/* iterate through triangles */
for( i = 0; i < ds->numIndexes; i += 3 )
{
/* generate decal */
w = AllocWinding( 3 );
w->numpoints = 3;
VectorCopy( ds->verts[ ds->indexes[ i ] ].xyz, w->p[ 0 ] );
VectorCopy( ds->verts[ ds->indexes[ i + 1 ] ].xyz, w->p[ 1 ] );
VectorCopy( ds->verts[ ds->indexes[ i + 2 ] ].xyz, w->p[ 2 ] );
ProjectDecalOntoWinding( dp, ds, w );
}
}
/*
MakeEntityDecals()
projects decals onto world surfaces
*/
void MakeEntityDecals( entity_t *e )
{
int i, j, k, f, fOld, start;
decalProjector_t dp;
mapDrawSurface_t *ds;
vec3_t identityAxis[ 3 ] = { { 1, 0, 0 }, { 0, 1, 0 }, { 0, 0, 1 } };
/* note it */
Sys_FPrintf( SYS_VRB, "--- MakeEntityDecals ---\n" );
/* set entity origin */
VectorCopy( e->origin, entityOrigin );
/* transform projector instead of geometry */
VectorClear( entityOrigin );
/* init pacifier */
fOld = -1;
start = I_FloatTime();
/* walk the list of decal projectors */
for( i = 0; i < numProjectors; i++ )
{
/* print pacifier */
f = 10 * i / numProjectors;
if( f != fOld )
{
fOld = f;
Sys_FPrintf( SYS_VRB, "%d...", f );
}
/* get projector */
TransformDecalProjector( &projectors[ i ], identityAxis, e->origin, &dp );
/* walk the list of surfaces in the entity */
for( j = e->firstDrawSurf; j < numMapDrawSurfs; j++ )
{
/* get surface */
ds = &mapDrawSurfs[ j ];
if( ds->numVerts <= 0 )
continue;
/* ignore autosprite or nomarks */
if( ds->shaderInfo->autosprite || (ds->shaderInfo->compileFlags & C_NOMARKS) )
continue;
/* bounds check */
for( k = 0; k < 3; k++ )
if( ds->mins[ k ] >= (dp.center[ k ] + dp.radius) ||
ds->maxs[ k ] <= (dp.center[ k ] - dp.radius) )
break;
if( k < 3 )
continue;
/* switch on type */
switch( ds->type )
{
case SURFACE_FACE:
ProjectDecalOntoFace( &dp, ds );
break;
case SURFACE_PATCH:
ProjectDecalOntoPatch( &dp, ds );
break;
case SURFACE_TRIANGLES:
case SURFACE_FORCED_META:
case SURFACE_META:
ProjectDecalOntoTriangles( &dp, ds );
break;
default:
break;
}
}
}
/* print time */
Sys_FPrintf( SYS_VRB, " (%d)\n", (int) (I_FloatTime() - start) );
/* emit some stats */
Sys_FPrintf( SYS_VRB, "%9d decal surfaces\n", numDecalSurfaces );
}

View File

@@ -0,0 +1,454 @@
/* -------------------------------------------------------------------------------
Copyright (C) 1999-2006 Id Software, Inc. and contributors.
For a list of contributors, see the accompanying CONTRIBUTORS file.
This file is part of GtkRadiant.
GtkRadiant 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.
GtkRadiant 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 GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
----------------------------------------------------------------------------------
This code has been altered significantly from its original form, to support
several games based on the Quake III Arena engine, in the form of "Q3Map2."
------------------------------------------------------------------------------- */
/* marker */
#define FACEBSP_C
/* dependencies */
#include "q3map2.h"
int c_faceLeafs;
/*
================
AllocBspFace
================
*/
face_t *AllocBspFace( void ) {
face_t *f;
f = safe_malloc(sizeof(*f));
memset( f, 0, sizeof(*f) );
return f;
}
/*
================
FreeBspFace
================
*/
void FreeBspFace( face_t *f ) {
if ( f->w ) {
FreeWinding( f->w );
}
free( f );
}
/*
SelectSplitPlaneNum()
finds the best split plane for this node
*/
static void SelectSplitPlaneNum( node_t *node, face_t *list, int *splitPlaneNum, int *compileFlags )
{
face_t *split;
face_t *check;
face_t *bestSplit;
int splits, facing, front, back;
int side;
plane_t *plane;
int value, bestValue;
int i;
vec3_t normal;
float dist;
int planenum;
/* ydnar: set some defaults */
*splitPlaneNum = -1; /* leaf */
*compileFlags = 0;
/* ydnar 2002-06-24: changed this to split on z-axis as well */
/* ydnar 2002-09-21: changed blocksize to be a vector, so mappers can specify a 3 element value */
/* if it is crossing a block boundary, force a split */
for( i = 0; i < 3; i++ )
{
if( blockSize[ i ] <= 0 )
continue;
dist = blockSize[ i ] * (floor( node->mins[ i ] / blockSize[ i ] ) + 1);
if( node->maxs[ i ] > dist )
{
VectorClear( normal );
normal[ i ] = 1;
planenum = FindFloatPlane( normal, dist, 0, NULL );
*splitPlaneNum = planenum;
return;
}
}
/* 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;
}
}
/* nothing, we have a leaf */
if( bestValue == -99999 )
return;
/* set best split data */
*splitPlaneNum = bestSplit->planenum;
*compileFlags = bestSplit->compileFlags;
}
/*
CountFaceList()
counts bsp faces in the linked list
*/
int CountFaceList( face_t *list )
{
int c;
c = 0;
for( list; list != NULL; list = list->next )
c++;
return c;
}
/*
BuildFaceTree_r()
recursively builds the bsp, splitting on face planes
*/
void BuildFaceTree_r( node_t *node, face_t *list )
{
face_t *split;
face_t *next;
int side;
plane_t *plane;
face_t *newFace;
face_t *childLists[2];
winding_t *frontWinding, *backWinding;
int i;
int splitPlaneNum, compileFlags;
/* count faces left */
i = CountFaceList( list );
/* select the best split plane */
SelectSplitPlaneNum( node, list, &splitPlaneNum, &compileFlags );
/* 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->compileFlags = compileFlags;
plane = &mapplanes[ splitPlaneNum ];
childLists[0] = NULL;
childLists[1] = NULL;
for( split = list; split; split = next )
{
/* set next */
next = split->next;
/* don't split by identical plane */
if( split->planenum == node->planenum )
{
FreeBspFace( split );
continue;
}
/* determine which side the face falls on */
side = WindingOnPlaneSide( split->w, plane->normal, plane->dist );
/* switch on side */
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->compileFlags = split->compileFlags;
childLists[0] = newFace;
}
if( backWinding ) {
newFace = AllocBspFace();
newFace->w = backWinding;
newFace->next = childLists[1];
newFace->planenum = split->planenum;
newFace->priority = split->priority;
newFace->compileFlags = split->compileFlags;
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( face_t *list ) {
tree_t *tree;
face_t *face;
int i;
int count;
Sys_FPrintf (SYS_VRB, "--- FaceBSP ---\n" );
tree = AllocTree ();
count = 0;
for( face = list; face != NULL; face = face->next )
{
count++;
for( i = 0; i < face->w->numpoints; i++ )
{
AddPointToBounds( face->w->p[ i ], tree->mins, tree->maxs );
}
}
Sys_FPrintf( SYS_VRB, "%9d 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 );
Sys_FPrintf( SYS_VRB, "%9d leafs\n", c_faceLeafs );
return tree;
}
/*
MakeStructuralBSPFaceList()
get structural brush faces
*/
face_t *MakeStructuralBSPFaceList( brush_t *list )
{
brush_t *b;
int i;
side_t *s;
winding_t *w;
face_t *f, *flist;
flist = NULL;
for( b = list; b != NULL; b = b->next )
{
if( b->detail )
continue;
for( i = 0; i < b->numsides; i++ )
{
/* get side and winding */
s = &b->sides[ i ];
w = s->winding;
if( w == NULL )
continue;
/* ydnar: skip certain faces */
if( s->compileFlags & C_SKIP )
continue;
/* allocate a face */
f = AllocBspFace();
f->w = CopyWinding( w );
f->planenum = s->planenum & ~1;
f->compileFlags = s->compileFlags; /* ydnar */
/* ydnar: set priority */
f->priority = 0;
if( f->compileFlags & C_HINT )
f->priority += HINT_PRIORITY;
if( f->compileFlags & C_ANTIPORTAL )
f->priority += ANTIPORTAL_PRIORITY;
if( f->compileFlags & C_AREAPORTAL )
f->priority += AREAPORTAL_PRIORITY;
/* get next face */
f->next = flist;
flist = f;
}
}
return flist;
}
/*
MakeVisibleBSPFaceList()
get visible brush faces
*/
face_t *MakeVisibleBSPFaceList( brush_t *list )
{
brush_t *b;
int i;
side_t *s;
winding_t *w;
face_t *f, *flist;
flist = NULL;
for( b = list; b != NULL; b = b->next )
{
if( b->detail )
continue;
for( i = 0; i < b->numsides; i++ )
{
/* get side and winding */
s = &b->sides[ i ];
w = s->visibleHull;
if( w == NULL )
continue;
/* ydnar: skip certain faces */
if( s->compileFlags & C_SKIP )
continue;
/* allocate a face */
f = AllocBspFace();
f->w = CopyWinding( w );
f->planenum = s->planenum & ~1;
f->compileFlags = s->compileFlags; /* ydnar */
/* ydnar: set priority */
f->priority = 0;
if( f->compileFlags & C_HINT )
f->priority += HINT_PRIORITY;
if( f->compileFlags & C_ANTIPORTAL )
f->priority += ANTIPORTAL_PRIORITY;
if( f->compileFlags & C_AREAPORTAL )
f->priority += AREAPORTAL_PRIORITY;
/* get next face */
f->next = flist;
flist = f;
}
}
return flist;
}

803
tools/quake3/q3map2/fog.c Normal file
View File

@@ -0,0 +1,803 @@
/* -------------------------------------------------------------------------------
Copyright (C) 1999-2006 Id Software, Inc. and contributors.
For a list of contributors, see the accompanying CONTRIBUTORS file.
This file is part of GtkRadiant.
GtkRadiant 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.
GtkRadiant 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 GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
----------------------------------------------------------------------------------
This code has been altered significantly from its original form, to support
several games based on the Quake III Arena engine, in the form of "Q3Map2."
------------------------------------------------------------------------------- */
/* marker */
#define FOG_C
/* dependencies */
#include "q3map2.h"
int numFogFragments;
int numFogPatchFragments;
/*
DrawSurfToMesh()
converts a patch drawsurface to a mesh_t
*/
mesh_t *DrawSurfToMesh( mapDrawSurface_t *ds )
{
mesh_t *m;
m = safe_malloc( sizeof( *m ) );
m->width = ds->patchWidth;
m->height = ds->patchHeight;
m->verts = safe_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()
chops a mesh by a plane
*/
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];
bspDrawVert_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 ) {
Sys_FPrintf (SYS_VRB, "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 ) {
Sys_Printf( "multiple crossing points for patch -- can't clip\n");
*front = in;
return;
}
}
}
if ( ( d[h][split] < 0 ) == ( d[h][split+1] < 0 ) ) {
Sys_Printf( "differing crossing points for patch -- can't clip\n");
*front = in;
return;
}
}
break;
}
// create two new meshes
f = safe_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 = safe_malloc( sizeof(f->verts[0]) * f->width * f->height );
b = safe_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 = safe_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] );
/* interpolate */
//% 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
LerpDrawVertAmount( v1, v2, frac, dv );
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 );
Sys_Printf("\n");
PrintMesh( f );
Sys_Printf("\n");
PrintMesh( b );
Sys_Printf("\n");
*/
FreeMesh( in );
}
/*
ChopPatchSurfaceByBrush()
chops a patch up by a fog brush
*/
qboolean ChopPatchSurfaceByBrush( entity_t *e, mapDrawSurface_t *ds, brush_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 */
numFogPatchFragments += numOutside;
for( i = 0; i < numOutside; i++ )
{
/* transpose and invert the chopped patch (fixes potential crash. fixme: why?) */
outside[ i ] = TransposeMesh( outside[ i ] );
InvertMesh( outside[ i ] );
/* ydnar: do this the hacky right way */
newds = AllocDrawSurface( SURFACE_PATCH );
memcpy( newds, ds, sizeof( *ds ) );
newds->patchWidth = outside[ i ]->width;
newds->patchHeight = outside[ i ]->height;
newds->numVerts = outside[ i ]->width * outside[ i ]->height;
newds->verts = safe_malloc( newds->numVerts * sizeof( *newds->verts ) );
memcpy( newds->verts, outside[ i ]->verts, newds->numVerts * sizeof( *newds->verts ) );
/* free the source mesh */
FreeMesh( outside[ i ] );
}
/* only rejigger this patch if it was chopped */
//% Sys_Printf( "Inside: %d x %d\n", m->width, m->height );
if( numOutside > 0 )
{
/* transpose and invert the chopped patch (fixes potential crash. fixme: why?) */
m = TransposeMesh( m );
InvertMesh( m );
/* replace ds with m */
ds->patchWidth = m->width;
ds->patchHeight = m->height;
ds->numVerts = m->width * m->height;
free( ds->verts );
ds->verts = safe_malloc( ds->numVerts * sizeof( *ds->verts ) );
memcpy( ds->verts, m->verts, ds->numVerts * sizeof( *ds->verts ) );
}
/* free the source mesh and return */
FreeMesh( m );
return qtrue;
}
/*
WindingFromDrawSurf()
creates a winding from a surface's verts
*/
winding_t *WindingFromDrawSurf( mapDrawSurface_t *ds )
{
winding_t *w;
int i;
// we use the first point of the surface, maybe something more clever would be useful
// (actually send the whole draw surface would be cool?)
if( ds->numVerts >= MAX_POINTS_ON_WINDING )
{
int max = ds->numVerts;
vec3_t p[256];
if(max > 256)
max = 256;
for ( i = 0 ; i < max ; i++ ) {
VectorCopy( ds->verts[i].xyz, p[i] );
}
xml_Winding( "WindingFromDrawSurf failed: MAX_POINTS_ON_WINDING exceeded", p, max, qtrue );
}
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;
}
/*
ChopFaceSurfaceByBrush()
chops up a face drawsurface by a fog brush, with a potential fragment left inside
*/
qboolean ChopFaceSurfaceByBrush( entity_t *e, mapDrawSurface_t *ds, brush_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;
/* dummy check */
if( ds->sideRef == NULL || ds->sideRef->side == NULL )
return qfalse;
/* initial setup */
w = WindingFromDrawSurf( ds );
numOutside = 0;
/* chop by each brush side */
for( i = 0; i < b->numsides; i++ )
{
/* get brush side and plane */
s = &b->sides[ i ];
plane = &mapplanes[ s->planenum ];
/* handle coplanar outfacing (don't fog) */
if( ds->sideRef->side->planenum == s->planenum )
return qfalse;
/* handle coplanar infacing (keep inside) */
if( (ds->sideRef->side->planenum ^ 1) == s->planenum )
continue;
/* general case */
ClipWindingEpsilon( w, plane->normal, plane->dist, ON_EPSILON, &front, &back );
FreeWinding( w );
if( back == NULL )
{
/* nothing actually contained inside */
for( j = 0; j < numOutside; j++ )
FreeWinding( outside[ j ] );
return qfalse;
}
if( front != NULL )
{
if( numOutside == MAX_BRUSH_SIDES )
Error( "MAX_BRUSH_SIDES" );
outside[ numOutside ] = front;
numOutside++;
}
w = back;
}
/* fixme: celshaded surface fragment errata */
/* all of outside fragments become seperate drawsurfs */
numFogFragments += numOutside;
s = ds->sideRef->side;
for( i = 0; i < numOutside; i++ )
{
newds = DrawSurfaceForSide( e, ds->mapBrush, s, outside[ i ] );
newds->fogNum = ds->fogNum;
FreeWinding( outside[ i ] );
}
/* ydnar: the old code neglected to snap to 0.125 for the fragment
inside the fog brush, leading to sparklies. this new code does
the right thing and uses the original surface's brush side */
/* build a drawsurf for it */
newds = DrawSurfaceForSide( e, ds->mapBrush, s, w );
if( newds == NULL )
return qfalse;
/* copy new to original */
ClearSurface( ds );
memcpy( ds, newds, sizeof( mapDrawSurface_t ) );
/* didn't really add a new drawsurface... :) */
numMapDrawSurfs--;
/* return ok */
return qtrue;
}
/*
FogDrawSurfaces()
call after the surface list has been pruned, before tjunction fixing
*/
void FogDrawSurfaces( entity_t *e )
{
int i, j, k, fogNum;
fog_t *fog;
mapDrawSurface_t *ds;
vec3_t mins, maxs;
int fogged, numFogged;
int numBaseDrawSurfs;
/* note it */
Sys_FPrintf( SYS_VRB, "----- FogDrawSurfs -----\n" );
/* reset counters */
numFogged = 0;
numFogFragments = 0;
/* walk fog list */
for( fogNum = 0; fogNum < numMapFogs; fogNum++ )
{
/* get fog */
fog = &mapFogs[ fogNum ];
/* 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++ )
{
/* get the drawsurface */
ds = &mapDrawSurfs[ i ];
/* no fog? */
if( ds->shaderInfo->noFog )
continue;
/* global fog doesn't have a brush */
if( fog->brush == NULL )
{
/* don't re-fog already fogged surfaces */
if( ds->fogNum >= 0 )
continue;
fogged = 1;
}
else
{
/* find drawsurface bounds */
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 ] > fog->brush->maxs[ k ] )
break;
if( maxs[ k ] < fog->brush->mins[ k ] )
break;
}
/* no intersection? */
if( k < 3 )
continue;
/* ydnar: gs mods: handle the various types of surfaces */
switch( ds->type )
{
/* handle brush faces */
case SURFACE_FACE:
fogged = ChopFaceSurfaceByBrush( e, ds, fog->brush );
break;
/* handle patches */
case SURFACE_PATCH:
fogged = ChopPatchSurfaceByBrush( e, ds, fog->brush );
break;
/* handle triangle surfaces (fixme: split triangle surfaces) */
case SURFACE_TRIANGLES:
case SURFACE_FORCED_META:
case SURFACE_META:
fogged = 1;
break;
/* no fogging */
default:
fogged = 0;
break;
}
}
/* is this surface fogged? */
if( fogged )
{
numFogged += fogged;
ds->fogNum = fogNum;
}
}
}
/* emit some statistics */
Sys_FPrintf( SYS_VRB, "%9d fog polygon fragments\n", numFogFragments );
Sys_FPrintf( SYS_VRB, "%9d fog patch fragments\n", numFogPatchFragments );
Sys_FPrintf( SYS_VRB, "%9d fogged drawsurfs\n", numFogged );
}
/*
FogForPoint() - ydnar
gets the fog number for a point in space
*/
int FogForPoint( vec3_t point, float epsilon )
{
int fogNum, i, j;
float dot;
qboolean inside;
brush_t *brush;
plane_t *plane;
/* start with bogus fog num */
fogNum = defaultFogNum;
/* walk the list of fog volumes */
for( i = 0; i < numMapFogs; i++ )
{
/* sof2: global fog doesn't reference a brush */
if( mapFogs[ i ].brush == NULL )
{
fogNum = i;
continue;
}
/* get fog brush */
brush = mapFogs[ i ].brush;
/* check point against all planes */
inside = qtrue;
for( j = 0; j < brush->numsides && inside; j++ )
{
plane = &mapplanes[ brush->sides[ j ].planenum ]; /* note usage of map planes here */
dot = DotProduct( point, plane->normal );
dot -= plane->dist;
if( dot > epsilon )
inside = qfalse;
}
/* if inside, return the fog num */
if( inside )
{
//% Sys_Printf( "FogForPoint: %f, %f, %f in fog %d\n", point[ 0 ], point[ 1 ], point[ 2 ], i );
return i;
}
}
/* if the point made it this far, it's not inside any fog volumes (or inside global fog) */
return fogNum;
}
/*
FogForBounds() - ydnar
gets the fog number for a bounding box
*/
int FogForBounds( vec3_t mins, vec3_t maxs, float epsilon )
{
int fogNum, i, j;
float highMin, lowMax, volume, bestVolume;
vec3_t fogMins, fogMaxs, overlap;
brush_t *brush;
/* start with bogus fog num */
fogNum = defaultFogNum;
/* init */
bestVolume = 0.0f;
/* walk the list of fog volumes */
for( i = 0; i < numMapFogs; i++ )
{
/* sof2: global fog doesn't reference a brush */
if( mapFogs[ i ].brush == NULL )
{
fogNum = i;
continue;
}
/* get fog brush */
brush = mapFogs[ i ].brush;
/* get bounds */
fogMins[ 0 ] = brush->mins[ 0 ] - epsilon;
fogMins[ 1 ] = brush->mins[ 1 ] - epsilon;
fogMins[ 2 ] = brush->mins[ 2 ] - epsilon;
fogMaxs[ 0 ] = brush->maxs[ 0 ] + epsilon;
fogMaxs[ 1 ] = brush->maxs[ 1 ] + epsilon;
fogMaxs[ 2 ] = brush->maxs[ 2 ] + epsilon;
/* check against bounds */
for( j = 0; j < 3; j++ )
{
if( mins[ j ] > fogMaxs[ j ] || maxs[ j ] < fogMins[ j ] )
break;
highMin = mins[ j ] > fogMins[ j ] ? mins[ j ] : fogMins[ j ];
lowMax = maxs[ j ] < fogMaxs[ j ] ? maxs[ j ] : fogMaxs[ j ];
overlap[ j ] = lowMax - highMin;
if( overlap[ j ] < 1.0f )
overlap[ j ] = 1.0f;
}
/* no overlap */
if( j < 3 )
continue;
/* get volume */
volume = overlap[ 0 ] * overlap[ 1 ] * overlap[ 2 ];
/* test against best volume */
if( volume > bestVolume )
{
bestVolume = volume;
fogNum = i;
}
}
/* if the point made it this far, it's not inside any fog volumes (or inside global fog) */
return fogNum;
}
/*
CreateMapFogs() - ydnar
generates a list of map fogs
*/
void CreateMapFogs( void )
{
int i;
entity_t *entity;
brush_t *brush;
fog_t *fog;
vec3_t invFogDir;
const char *globalFog;
/* skip? */
if( nofog )
return;
/* note it */
Sys_FPrintf( SYS_VRB, "--- CreateMapFogs ---\n" );
/* walk entities */
for( i = 0; i < numEntities; i++ )
{
/* get entity */
entity = &entities[ i ];
/* walk entity brushes */
for( brush = entity->brushes; brush != NULL; brush = brush->next )
{
/* ignore non-fog brushes */
if( brush->contentShader->fogParms == qfalse )
continue;
/* test limit */
if( numMapFogs >= MAX_MAP_FOGS )
Error( "Exceeded MAX_MAP_FOGS (%d)", MAX_MAP_FOGS );
/* set up fog */
fog = &mapFogs[ numMapFogs++ ];
fog->si = brush->contentShader;
fog->brush = brush;
fog->visibleSide = -1;
/* if shader specifies an explicit direction, then find a matching brush side with an opposed normal */
if( VectorLength( fog->si->fogDir ) )
{
/* flip it */
VectorScale( fog->si->fogDir, -1.0f, invFogDir );
/* find the brush side */
for( i = 0; i < brush->numsides; i++ )
{
if( VectorCompare( invFogDir, mapplanes[ brush->sides[ i ].planenum ].normal ) )
{
fog->visibleSide = i;
//% Sys_Printf( "Brush num: %d Side num: %d\n", fog->brushNum, fog->visibleSide );
break;
}
}
}
}
}
/* ydnar: global fog */
globalFog = ValueForKey( &entities[ 0 ], "_fog" );
if( globalFog[ 0 ] == '\0' )
globalFog = ValueForKey( &entities[ 0 ], "fog" );
if( globalFog[ 0 ] != '\0' )
{
/* test limit */
if( numMapFogs >= MAX_MAP_FOGS )
Error( "Exceeded MAX_MAP_FOGS (%d) trying to add global fog", MAX_MAP_FOGS );
/* note it */
Sys_FPrintf( SYS_VRB, "Map has global fog shader %s\n", globalFog );
/* set up fog */
fog = &mapFogs[ numMapFogs++ ];
fog->si = ShaderInfoForShader( globalFog );
if( fog->si == NULL )
Error( "Invalid shader \"%s\" referenced trying to add global fog", globalFog );
fog->brush = NULL;
fog->visibleSide = -1;
/* set as default fog */
defaultFogNum = numMapFogs - 1;
/* mark all worldspawn brushes as fogged */
for( brush = entities[ 0 ].brushes; brush != NULL; brush = brush->next )
ApplySurfaceParm( "fog", &brush->contentFlags, NULL, &brush->compileFlags );
}
/* emit some stats */
Sys_FPrintf( SYS_VRB, "%9d fogs\n", numMapFogs );
}

View File

@@ -0,0 +1,194 @@
/* -------------------------------------------------------------------------------
Copyright (C) 1999-2006 Id Software, Inc. and contributors.
For a list of contributors, see the accompanying CONTRIBUTORS file.
This file is part of GtkRadiant.
GtkRadiant 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.
GtkRadiant 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 GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
----------------------------------------------------------------------------------
This code has been altered significantly from its original form, to support
several games based on the Quake III Arena engine, in the form of "Q3Map2."
------------------------------------------------------------------------------- */
/* marker */
#ifndef GAME_EF_H
#define GAME_EF_H
/* -------------------------------------------------------------------------------
content and surface flags
------------------------------------------------------------------------------- */
/* game flags */
#define E_CONT_SOLID 1 /* an eye is never valid in a solid */
#define E_CONT_LAVA 8
#define E_CONT_SLIME 16
#define E_CONT_WATER 32
#define E_CONT_FOG 64
#define E_CONT_LADDER 128 /* elite force ladder contents */
#define E_CONT_AREAPORTAL 0x8000
#define E_CONT_PLAYERCLIP 0x10000
#define E_CONT_MONSTERCLIP 0x20000
#define E_CONT_SHOTCLIP 0x40000 /* elite force shot clip */
#define E_CONT_ITEM 0x80000
#define E_CONT_CLUSTERPORTAL 0x100000
#define E_CONT_DONOTENTER 0x200000
#define E_CONT_BOTCLIP 0x400000
#define E_CONT_ORIGIN 0x1000000 /* removed before bsping an entity */
#define E_CONT_BODY 0x2000000 /* should never be on a brush, only in game */
#define E_CONT_CORPSE 0x4000000
#define E_CONT_DETAIL 0x8000000 /* brushes not used for the bsp */
#define E_CONT_STRUCTURAL 0x10000000 /* brushes used for the bsp */
#define E_CONT_TRANSLUCENT 0x20000000 /* don't consume surface fragments inside */
#define E_CONT_TRIGGER 0x40000000
#define E_CONT_NODROP 0x80000000 /* don't leave bodies or items (death fog, lava) */
#define E_SURF_NODAMAGE 0x1 /* never give falling damage */
#define E_SURF_SLICK 0x2 /* effects game physics */
#define E_SURF_SKY 0x4 /* lighting from environment map */
#define E_SURF_LADDER 0x8
#define E_SURF_NOIMPACT 0x10 /* don't make missile explosions */
#define E_SURF_NOMARKS 0x20 /* don't leave missile marks */
#define E_SURF_FLESH 0x40 /* make flesh sounds and effects */
#define E_SURF_NODRAW 0x80 /* don't generate a drawsurface at all */
#define E_SURF_HINT 0x100 /* make a primary bsp splitter */
#define E_SURF_SKIP 0x200 /* completely ignore, allowing non-closed brushes */
#define E_SURF_NOLIGHTMAP 0x400 /* surface doesn't need a lightmap */
#define E_SURF_POINTLIGHT 0x800 /* generate lighting info at vertexes */
#define E_SURF_METALSTEPS 0x1000 /* clanking footsteps */
#define E_SURF_NOSTEPS 0x2000 /* no footstep sounds */
#define E_SURF_NONSOLID 0x4000 /* don't collide against curves with this set */
#define E_SURF_LIGHTFILTER 0x8000 /* act as a light filter during q3map -light */
#define E_SURF_ALPHASHADOW 0x10000 /* do per-pixel light shadow casting in q3map */
#define E_SURF_NODLIGHT 0x20000 /* don't dlight even if solid (solid lava, skies) */
#define E_SURF_FORCEFIELD 0x40000 /* elite force forcefield brushes */
/* ydnar flags */
#define E_SURF_VERTEXLIT (E_SURF_POINTLIGHT | E_SURF_NOLIGHTMAP)
/* -------------------------------------------------------------------------------
game_t struct
------------------------------------------------------------------------------- */
{
"ef", /* -game x */
"baseef", /* default base game data dir */
".ef", /* unix home sub-dir */
"elite", /* magic path word */
"scripts", /* shader directory */
64, /* max lightmapped surface verts */
999, /* max surface verts */
6000, /* max surface indexes */
qfalse, /* flares */
"flareshader", /* default flare shader */
qfalse, /* wolf lighting model? */
128, /* lightmap width/height */
1.0f, /* lightmap gamma */
1.0f, /* lightmap compensate */
"IBSP", /* bsp file prefix */
46, /* bsp file version */
qfalse, /* cod-style lump len/ofs order */
LoadIBSPFile, /* bsp load function */
WriteIBSPFile, /* bsp write function */
{
/* name contentFlags contentFlagsClear surfaceFlags surfaceFlagsClear compileFlags compileFlagsClear */
/* default */
{ "default", E_CONT_SOLID, -1, 0, -1, C_SOLID, -1 },
/* ydnar */
{ "lightgrid", 0, 0, 0, 0, C_LIGHTGRID, 0 },
{ "antiportal", 0, 0, 0, 0, C_ANTIPORTAL, 0 },
{ "skip", 0, 0, 0, 0, C_SKIP, 0 },
/* compiler */
{ "origin", E_CONT_ORIGIN, E_CONT_SOLID, 0, 0, C_ORIGIN | C_TRANSLUCENT, C_SOLID },
{ "areaportal", E_CONT_AREAPORTAL, E_CONT_SOLID, 0, 0, C_AREAPORTAL | C_TRANSLUCENT, C_SOLID },
{ "trans", E_CONT_TRANSLUCENT, 0, 0, 0, C_TRANSLUCENT, 0 },
{ "detail", E_CONT_DETAIL, 0, 0, 0, C_DETAIL, 0 },
{ "structural", E_CONT_STRUCTURAL, 0, 0, 0, C_STRUCTURAL, 0 },
{ "hint", 0, 0, E_SURF_HINT, 0, C_HINT, 0 },
{ "nodraw", 0, 0, E_SURF_NODRAW, 0, C_NODRAW, 0 },
{ "alphashadow", 0, 0, E_SURF_ALPHASHADOW, 0, C_ALPHASHADOW | C_TRANSLUCENT, 0 },
{ "lightfilter", 0, 0, E_SURF_LIGHTFILTER, 0, C_LIGHTFILTER | C_TRANSLUCENT, 0 },
{ "nolightmap", 0, 0, E_SURF_VERTEXLIT, 0, C_VERTEXLIT, 0 },
{ "pointlight", 0, 0, E_SURF_VERTEXLIT, 0, C_VERTEXLIT, 0 },
/* game */
{ "nonsolid", 0, E_CONT_SOLID, E_SURF_NONSOLID, 0, 0, C_SOLID },
{ "trigger", E_CONT_TRIGGER, E_CONT_SOLID, 0, 0, C_TRANSLUCENT, C_SOLID },
{ "water", E_CONT_WATER, E_CONT_SOLID, 0, 0, C_LIQUID | C_TRANSLUCENT, C_SOLID },
{ "slime", E_CONT_SLIME, E_CONT_SOLID, 0, 0, C_LIQUID | C_TRANSLUCENT, C_SOLID },
{ "lava", E_CONT_LAVA, E_CONT_SOLID, 0, 0, C_LIQUID | C_TRANSLUCENT, C_SOLID },
{ "playerclip", E_CONT_PLAYERCLIP, E_CONT_SOLID, 0, 0, C_DETAIL | C_TRANSLUCENT, C_SOLID },
{ "monsterclip", E_CONT_MONSTERCLIP, E_CONT_SOLID, 0, 0, C_DETAIL | C_TRANSLUCENT, C_SOLID },
{ "shotclip", E_CONT_SHOTCLIP, E_CONT_SOLID, 0, 0, C_DETAIL | C_TRANSLUCENT, C_SOLID },
{ "nodrop", E_CONT_NODROP, E_CONT_SOLID, 0, 0, C_TRANSLUCENT, C_SOLID },
{ "clusterportal", E_CONT_CLUSTERPORTAL, E_CONT_SOLID, 0, 0, C_TRANSLUCENT, C_SOLID },
{ "donotenter", E_CONT_DONOTENTER, E_CONT_SOLID, 0, 0, C_TRANSLUCENT, C_SOLID },
{ "botclip", E_CONT_BOTCLIP, E_CONT_SOLID, 0, 0, C_TRANSLUCENT, C_SOLID },
{ "fog", E_CONT_FOG, E_CONT_SOLID, 0, 0, C_FOG, C_SOLID },
{ "sky", 0, 0, E_SURF_SKY, 0, C_SKY, 0 },
{ "slick", 0, 0, E_SURF_SLICK, 0, 0, 0 },
{ "noimpact", 0, 0, E_SURF_NOIMPACT, 0, 0, 0 },
{ "nomarks", 0, 0, E_SURF_NOMARKS, 0, C_NOMARKS, 0 },
{ "ladder", E_CONT_LADDER, E_CONT_SOLID, E_SURF_LADDER, 0, C_DETAIL | C_TRANSLUCENT, C_SOLID },
{ "nodamage", 0, 0, E_SURF_NODAMAGE, 0, 0, 0 },
{ "metalsteps", 0, 0, E_SURF_METALSTEPS, 0, 0, 0 },
{ "flesh", 0, 0, E_SURF_FLESH, 0, 0, 0 },
{ "nosteps", 0, 0, E_SURF_NOSTEPS, 0, 0, 0 },
{ "nodlight", 0, 0, E_SURF_NODLIGHT, 0, 0, 0 },
{ "forcefield", 0, 0, E_SURF_FORCEFIELD, 0, 0, 0 },
/* null */
{ NULL, 0, 0, 0, 0, 0, 0 }
}
}
/* end marker */
#endif

View File

@@ -0,0 +1,264 @@
/* -------------------------------------------------------------------------------
Copyright (C) 1999-2006 Id Software, Inc. and contributors.
For a list of contributors, see the accompanying CONTRIBUTORS file.
This file is part of GtkRadiant.
GtkRadiant 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.
GtkRadiant 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 GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
----------------------------------------------------------------------------------
This code has been altered significantly from its original form, to support
several games based on the Quake III Arena engine, in the form of "Q3Map2."
------------------------------------------------------------------------------- */
/* marker */
#ifndef GAME_ETUT_H
#define GAME_ETUT_H
/* -------------------------------------------------------------------------------
content and surface flags
------------------------------------------------------------------------------- */
/* game flags */
#define U_CONT_SOLID 1 /* an eye is never valid in a solid */
#define U_CONT_LAVA 8
#define U_CONT_SLIME 16
#define U_CONT_WATER 32
#define U_CONT_FOG 64
#define U_CONT_AREAPORTAL 0x8000
#define U_CONT_PLAYERCLIP 0x10000
#define U_CONT_MONSTERCLIP 0x20000
#define U_CONT_TELEPORTER 0x40000
#define U_CONT_JUMPPAD 0x80000
#define U_CONT_CLUSTERPORTAL 0x100000
#define U_CONT_DONOTENTER 0x200000
#define U_CONT_BOTCLIP 0x400000
#define U_CONT_ORIGIN 0x1000000 /* removed before bsping an entity */
#define U_CONT_BODY 0x2000000 /* should never be on a brush, only in game */
#define U_CONT_CORPSE 0x4000000
#define U_CONT_DETAIL 0x8000000 /* brushes not used for the bsp */
#define U_CONT_STRUCTURAL 0x10000000 /* brushes used for the bsp */
#define U_CONT_TRANSLUCENT 0x20000000 /* don't consume surface fragments inside */
#define U_CONT_TRIGGER 0x40000000
#define U_CONT_NODROP 0x80000000 /* don't leave bodies or items (death fog, lava) */
#define U_SURF_NODAMAGE 0x1 /* never give falling damage */
#define U_SURF_SLICK 0x2 /* effects game physics */
#define U_SURF_SKY 0x4 /* lighting from environment map */
#define U_SURF_LADDER 0x8
#define U_SURF_NOIMPACT 0x10 /* don't make missile explosions */
#define U_SURF_NOMARKS 0x20 /* don't leave missile marks */
#define U_SURF_FLESH 0x40 /* make flesh sounds and effects */
#define U_SURF_NODRAW 0x80 /* don't generate a drawsurface at all */
#define U_SURF_HINT 0x100 /* make a primary bsp splitter */
#define U_SURF_SKIP 0x200 /* completely ignore, allowing non-closed brushes */
#define U_SURF_NOLIGHTMAP 0x400 /* surface doesn't need a lightmap */
#define U_SURF_POINTLIGHT 0x800 /* generate lighting info at vertexes */
#define U_SURF_METALSTEPS 0x1000 /* clanking footsteps */
#define U_SURF_NOSTEPS 0x2000 /* no footstep sounds */
#define U_SURF_NONSOLID 0x4000 /* don't collide against curves with this set */
#define U_SURF_LIGHTFILTER 0x8000 /* act as a light filter during q3map -light */
#define U_SURF_ALPHASHADOW 0x10000 /* do per-pixel light shadow casting in q3map */
#define U_SURF_NODLIGHT 0x20000 /* don't dlight even if solid (solid lava, skies) */
#define U_SURF_DUST 0x40000 /* leave a dust trail when walking on this surface */
/* ydnar flags */
#define U_SURF_VERTEXLIT (U_SURF_POINTLIGHT | U_SURF_NOLIGHTMAP)
/* materials */
#define U_MAT_MASK 0xFFF00000 /* mask to get the material type */
#define U_MAT_NONE 0x00000000
#define U_MAT_TIN 0x00100000
#define U_MAT_ALUMINUM 0x00200000
#define U_MAT_IRON 0x00300000
#define U_MAT_TITANIUM 0x00400000
#define U_MAT_STEEL 0x00500000
#define U_MAT_BRASS 0x00600000
#define U_MAT_COPPER 0x00700000
#define U_MAT_CEMENT 0x00800000
#define U_MAT_ROCK 0x00900000
#define U_MAT_GRAVEL 0x00A00000
#define U_MAT_PAVEMENT 0x00B00000
#define U_MAT_BRICK 0x00C00000
#define U_MAT_CLAY 0x00D00000
#define U_MAT_GRASS 0x00E00000
#define U_MAT_DIRT 0x00F00000
#define U_MAT_MUD 0x01000000
#define U_MAT_SNOW 0x01100000
#define U_MAT_ICE 0x01200000
#define U_MAT_SAND 0x01300000
#define U_MAT_CERAMICTILE 0x01400000
#define U_MAT_LINOLEUM 0x01500000
#define U_MAT_RUG 0x01600000
#define U_MAT_PLASTER 0x01700000
#define U_MAT_PLASTIC 0x01800000
#define U_MAT_CARDBOARD 0x01900000
#define U_MAT_HARDWOOD 0x01A00000
#define U_MAT_SOFTWOOD 0x01B00000
#define U_MAT_PLANK 0x01C00000
#define U_MAT_GLASS 0x01D00000
#define U_MAT_WATER 0x01E00000
#define U_MAT_STUCCO 0x01F00000
/* -------------------------------------------------------------------------------
game_t struct
------------------------------------------------------------------------------- */
{
"etut", /* -game x */
"etut", /* default base game data dir */
".etwolf", /* unix home sub-dir */
"et", /* magic path word */
"scripts", /* shader directory */
1024, /* max lightmapped surface verts */
1024, /* max surface verts */
6144, /* max surface indexes */
qfalse, /* flares */
"flareshader", /* default flare shader */
qfalse, /* wolf lighting model? */
128, /* lightmap width/height */
2.2f, /* lightmap gamma */
1.0f, /* lightmap compensate */
"IBSP", /* bsp file prefix */
47, /* bsp file version */
qfalse, /* cod-style lump len/ofs order */
LoadIBSPFile, /* bsp load function */
WriteIBSPFile, /* bsp write function */
{
/* name contentFlags contentFlagsClear surfaceFlags surfaceFlagsClear compileFlags compileFlagsClear */
/* default */
{ "default", U_CONT_SOLID, -1, 0, -1, C_SOLID, -1 },
/* ydnar */
{ "lightgrid", 0, 0, 0, 0, C_LIGHTGRID, 0 },
{ "antiportal", 0, 0, 0, 0, C_ANTIPORTAL, 0 },
{ "skip", 0, 0, 0, 0, C_SKIP, 0 },
/* compiler */
{ "origin", U_CONT_ORIGIN, U_CONT_SOLID, 0, 0, C_ORIGIN | C_TRANSLUCENT, C_SOLID },
{ "areaportal", U_CONT_AREAPORTAL, U_CONT_SOLID, 0, 0, C_AREAPORTAL | C_TRANSLUCENT, C_SOLID },
{ "trans", U_CONT_TRANSLUCENT, 0, 0, 0, C_TRANSLUCENT, 0 },
{ "detail", U_CONT_DETAIL, 0, 0, 0, C_DETAIL, 0 },
{ "structural", U_CONT_STRUCTURAL, 0, 0, 0, C_STRUCTURAL, 0 },
{ "hint", 0, 0, U_SURF_HINT, 0, C_HINT, 0 },
{ "nodraw", 0, 0, U_SURF_NODRAW, 0, C_NODRAW, 0 },
{ "alphashadow", 0, 0, U_SURF_ALPHASHADOW, 0, C_ALPHASHADOW | C_TRANSLUCENT, 0 },
{ "lightfilter", 0, 0, U_SURF_LIGHTFILTER, 0, C_LIGHTFILTER | C_TRANSLUCENT, 0 },
{ "nolightmap", 0, 0, U_SURF_VERTEXLIT, 0, C_VERTEXLIT, 0 },
{ "pointlight", 0, 0, U_SURF_VERTEXLIT, 0, C_VERTEXLIT, 0 },
/* game */
{ "nonsolid", 0, U_CONT_SOLID, U_SURF_NONSOLID, 0, 0, C_SOLID },
{ "trigger", U_CONT_TRIGGER, U_CONT_SOLID, 0, 0, C_TRANSLUCENT, C_SOLID },
{ "water", U_CONT_WATER, U_CONT_SOLID, 0, 0, C_LIQUID | C_TRANSLUCENT, C_SOLID },
{ "slime", U_CONT_SLIME, U_CONT_SOLID, 0, 0, C_LIQUID | C_TRANSLUCENT, C_SOLID },
{ "lava", U_CONT_LAVA, U_CONT_SOLID, 0, 0, C_LIQUID | C_TRANSLUCENT, C_SOLID },
{ "playerclip", U_CONT_PLAYERCLIP, U_CONT_SOLID, 0, 0, C_DETAIL | C_TRANSLUCENT, C_SOLID },
{ "monsterclip", U_CONT_MONSTERCLIP, U_CONT_SOLID, 0, 0, C_DETAIL | C_TRANSLUCENT, C_SOLID },
{ "nodrop", U_CONT_NODROP, U_CONT_SOLID, 0, 0, C_TRANSLUCENT, C_SOLID },
{ "clusterportal", U_CONT_CLUSTERPORTAL, U_CONT_SOLID, 0, 0, C_TRANSLUCENT, C_SOLID },
{ "donotenter", U_CONT_DONOTENTER, U_CONT_SOLID, 0, 0, C_TRANSLUCENT, C_SOLID },
{ "botclip", U_CONT_BOTCLIP, U_CONT_SOLID, 0, 0, C_TRANSLUCENT, C_SOLID },
{ "fog", U_CONT_FOG, U_CONT_SOLID, 0, 0, C_FOG, C_SOLID },
{ "sky", 0, 0, U_SURF_SKY, 0, C_SKY, 0 },
{ "slick", 0, 0, U_SURF_SLICK, 0, 0, 0 },
{ "noimpact", 0, 0, U_SURF_NOIMPACT, 0, 0, 0 },
{ "nomarks", 0, 0, U_SURF_NOMARKS, 0, C_NOMARKS, 0 },
{ "ladder", 0, 0, U_SURF_LADDER, 0, 0, 0 },
{ "nodamage", 0, 0, U_SURF_NODAMAGE, 0, 0, 0 },
{ "metalsteps", 0, 0, U_SURF_METALSTEPS, 0, 0, 0 },
{ "flesh", 0, 0, U_SURF_FLESH, 0, 0, 0 },
{ "nosteps", 0, 0, U_SURF_NOSTEPS, 0, 0, 0 },
{ "nodlight", 0, 0, U_SURF_NODLIGHT, 0, 0, 0 },
{ "dust", 0, 0, U_SURF_DUST, 0, 0, 0 },
/* materials */
{ "*mat_none", 0, 0, U_MAT_NONE, U_MAT_MASK, 0, 0 },
{ "*mat_tin", 0, 0, U_MAT_TIN, U_MAT_MASK, 0, 0 },
{ "*mat_aluminum", 0, 0, U_MAT_ALUMINUM, U_MAT_MASK, 0, 0 },
{ "*mat_iron", 0, 0, U_MAT_IRON, U_MAT_MASK, 0, 0 },
{ "*mat_titanium", 0, 0, U_MAT_TITANIUM, U_MAT_MASK, 0, 0 },
{ "*mat_steel", 0, 0, U_MAT_STEEL, U_MAT_MASK, 0, 0 },
{ "*mat_brass", 0, 0, U_MAT_BRASS, U_MAT_MASK, 0, 0 },
{ "*mat_copper", 0, 0, U_MAT_COPPER, U_MAT_MASK, 0, 0 },
{ "*mat_cement", 0, 0, U_MAT_CEMENT, U_MAT_MASK, 0, 0 },
{ "*mat_rock", 0, 0, U_MAT_ROCK, U_MAT_MASK, 0, 0 },
{ "*mat_gravel", 0, 0, U_MAT_GRAVEL, U_MAT_MASK, 0, 0 },
{ "*mat_pavement", 0, 0, U_MAT_PAVEMENT, U_MAT_MASK, 0, 0 },
{ "*mat_brick", 0, 0, U_MAT_BRICK, U_MAT_MASK, 0, 0 },
{ "*mat_clay", 0, 0, U_MAT_CLAY, U_MAT_MASK, 0, 0 },
{ "*mat_grass", 0, 0, U_MAT_GRASS, U_MAT_MASK, 0, 0 },
{ "*mat_dirt", 0, 0, U_MAT_DIRT, U_MAT_MASK, 0, 0 },
{ "*mat_mud", 0, 0, U_MAT_MUD, U_MAT_MASK, 0, 0 },
{ "*mat_snow", 0, 0, U_MAT_SNOW, U_MAT_MASK, 0, 0 },
{ "*mat_ice", 0, 0, U_MAT_ICE, U_MAT_MASK, 0, 0 },
{ "*mat_sand", 0, 0, U_MAT_SAND, U_MAT_MASK, 0, 0 },
{ "*mat_ceramic", 0, 0, U_MAT_CERAMICTILE, U_MAT_MASK, 0, 0 },
{ "*mat_ceramictile", 0, 0, U_MAT_CERAMICTILE, U_MAT_MASK, 0, 0 },
{ "*mat_linoleum", 0, 0, U_MAT_LINOLEUM, U_MAT_MASK, 0, 0 },
{ "*mat_rug", 0, 0, U_MAT_RUG, U_MAT_MASK, 0, 0 },
{ "*mat_plaster", 0, 0, U_MAT_PLASTER, U_MAT_MASK, 0, 0 },
{ "*mat_plastic", 0, 0, U_MAT_PLASTIC, U_MAT_MASK, 0, 0 },
{ "*mat_cardboard", 0, 0, U_MAT_CARDBOARD, U_MAT_MASK, 0, 0 },
{ "*mat_hardwood", 0, 0, U_MAT_HARDWOOD, U_MAT_MASK, 0, 0 },
{ "*mat_softwood", 0, 0, U_MAT_SOFTWOOD, U_MAT_MASK, 0, 0 },
{ "*mat_plank", 0, 0, U_MAT_PLANK, U_MAT_MASK, 0, 0 },
{ "*mat_glass", 0, 0, U_MAT_GLASS, U_MAT_MASK, 0, 0 },
{ "*mat_water", 0, 0, U_MAT_WATER, U_MAT_MASK, 0, 0 },
{ "*mat_stucco", 0, 0, U_MAT_STUCCO, U_MAT_MASK, 0, 0 },
/* null */
{ NULL, 0, 0, 0, 0, 0, 0 }
}
}
/* end marker */
#endif

View File

@@ -0,0 +1,188 @@
/* -------------------------------------------------------------------------------
Copyright (C) 1999-2006 Id Software, Inc. and contributors.
For a list of contributors, see the accompanying CONTRIBUTORS file.
This file is part of GtkRadiant.
GtkRadiant 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.
GtkRadiant 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 GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
----------------------------------------------------------------------------------
This code has been altered significantly from its original form, to support
several games based on the Quake III Arena engine, in the form of "Q3Map2."
------------------------------------------------------------------------------- */
/* marker */
#ifndef GAME_JA_H
#define GAME_JA_H
/* -------------------------------------------------------------------------------
content and surface flags
------------------------------------------------------------------------------- */
/* this file must be included *after* game_sof2.h and game_jk2.h because they share defines! */
#define JA_CONT_INSIDE 0x10000000 /* jedi academy 'inside' */
#define JA_SURF_FORCESIGHT 0x02000000 /* jedi academy 'forcesight' */
/* -------------------------------------------------------------------------------
game_t struct
------------------------------------------------------------------------------- */
{
"ja", /* -game x */
"base", /* default base game data dir */
".ja", /* unix home sub-dir */
"GameData", /* magic path word */
"shaders", /* shader directory */
64, /* max lightmapped surface verts */
999, /* max surface verts */
6000, /* max surface indexes */
qtrue, /* flares */
"gfx/misc/flare", /* default flare shader */
qfalse, /* wolf lighting model? */
128, /* lightmap width/height */
1.0f, /* lightmap gamma */
1.0f, /* lightmap compensate */
"RBSP", /* bsp file prefix */
1, /* bsp file version */
qfalse, /* cod-style lump len/ofs order */
LoadRBSPFile, /* bsp load function */
WriteRBSPFile, /* bsp write function */
{
/* name contentFlags contentFlagsClear surfaceFlags surfaceFlagsClear compileFlags compileFlagsClear */
/* default */
{ "default", S_CONT_SOLID | S_CONT_OPAQUE, -1, 0, -1, C_SOLID, -1 },
/* ydnar */
{ "lightgrid", 0, 0, 0, 0, C_LIGHTGRID, 0 },
{ "antiportal", 0, 0, 0, 0, C_ANTIPORTAL, 0 },
{ "skip", 0, 0, 0, 0, C_SKIP, 0 },
/* compiler */
{ "origin", 0, S_CONT_SOLID, 0, 0, C_ORIGIN | C_TRANSLUCENT, C_SOLID },
{ "areaportal", S_CONT_TRANSLUCENT, S_CONT_SOLID | S_CONT_OPAQUE, 0, 0, C_AREAPORTAL | C_TRANSLUCENT, C_SOLID },
{ "trans", S_CONT_TRANSLUCENT, 0, 0, 0, C_TRANSLUCENT, 0 },
{ "detail", S_CONT_DETAIL, 0, 0, 0, C_DETAIL, 0 },
{ "structural", 0, 0, 0, 0, C_STRUCTURAL, 0 },
{ "hint", 0, 0, 0, 0, C_HINT, 0 },
{ "nodraw", 0, 0, S_SURF_NODRAW, 0, C_NODRAW, 0 },
{ "alphashadow", 0, 0, 0, 0, C_ALPHASHADOW | C_TRANSLUCENT, 0 },
{ "lightfilter", 0, 0, 0, 0, C_LIGHTFILTER | C_TRANSLUCENT, 0 },
{ "nolightmap", 0, 0, 0, 0, C_VERTEXLIT, 0 },
{ "pointlight", 0, 0, 0, 0, C_VERTEXLIT, 0 },
/* game */
{ "nonsolid", 0, S_CONT_SOLID, 0, 0, 0, C_SOLID },
{ "nonopaque", 0, S_CONT_OPAQUE, 0, 0, C_TRANSLUCENT, 0 }, /* setting trans ok? */
{ "trigger", S_CONT_TRIGGER, S_CONT_SOLID | S_CONT_OPAQUE, 0, 0, C_TRANSLUCENT, C_SOLID },
{ "water", S_CONT_WATER, S_CONT_SOLID, 0, 0, C_LIQUID | C_TRANSLUCENT, C_SOLID },
{ "slime", S_CONT_SLIME, S_CONT_SOLID, 0, 0, C_LIQUID | C_TRANSLUCENT, C_SOLID },
{ "lava", S_CONT_LAVA, S_CONT_SOLID, 0, 0, C_LIQUID | C_TRANSLUCENT, C_SOLID },
{ "shotclip", S_CONT_SHOTCLIP, S_CONT_SOLID | S_CONT_OPAQUE, 0, 0, C_DETAIL | C_TRANSLUCENT, C_SOLID }, /* setting trans/detail ok? */
{ "playerclip", S_CONT_PLAYERCLIP, S_CONT_SOLID | S_CONT_OPAQUE, 0, 0, C_DETAIL | C_TRANSLUCENT, C_SOLID },
{ "monsterclip", S_CONT_MONSTERCLIP, S_CONT_SOLID | S_CONT_OPAQUE, 0, 0, C_DETAIL | C_TRANSLUCENT, C_SOLID },
{ "nodrop", S_CONT_NODROP, S_CONT_SOLID | S_CONT_OPAQUE, 0, 0, C_DETAIL | C_TRANSLUCENT, C_SOLID },
{ "terrain", S_CONT_TERRAIN, S_CONT_SOLID | S_CONT_OPAQUE, 0, 0, C_DETAIL | C_TRANSLUCENT, C_SOLID },
{ "ladder", S_CONT_LADDER, S_CONT_SOLID | S_CONT_OPAQUE, 0, 0, C_DETAIL | C_TRANSLUCENT, C_SOLID },
{ "abseil", S_CONT_ABSEIL, S_CONT_SOLID | S_CONT_OPAQUE, 0, 0, C_DETAIL | C_TRANSLUCENT, C_SOLID },
{ "outside", S_CONT_OUTSIDE, S_CONT_SOLID | S_CONT_OPAQUE, 0, 0, C_DETAIL | C_TRANSLUCENT, C_SOLID },
{ "botclip", S_CONT_BOTCLIP, S_CONT_SOLID | S_CONT_OPAQUE, 0, 0, C_DETAIL | C_TRANSLUCENT, C_SOLID },
{ "fog", S_CONT_FOG, S_CONT_SOLID | S_CONT_OPAQUE, 0, 0, C_FOG | C_DETAIL | C_TRANSLUCENT, C_SOLID }, /* nonopaque? */
{ "sky", 0, 0, S_SURF_SKY, 0, C_SKY, 0 },
{ "slick", 0, 0, S_SURF_SLICK, 0, 0, 0 },
{ "noimpact", 0, 0, S_SURF_NOIMPACT, 0, 0, 0 },
{ "nomarks", 0, 0, S_SURF_NOMARKS, 0, C_NOMARKS, 0 },
{ "nodamage", 0, 0, S_SURF_NODAMAGE, 0, 0, 0 },
{ "metalsteps", 0, 0, S_SURF_METALSTEPS, 0, 0, 0 },
{ "nosteps", 0, 0, S_SURF_NOSTEPS, 0, 0, 0 },
{ "nodlight", 0, 0, S_SURF_NODLIGHT, 0, 0, 0 },
{ "nomiscents", 0, 0, S_SURF_NOMISCENTS, 0, 0, 0 },
{ "forcefield", 0, 0, S_SURF_FORCEFIELD, 0, 0, 0 },
/* jedi academy */
{ "inside", JA_CONT_INSIDE, 0, 0, 0, 0, 0 },
{ "forcesight", 0, 0, JA_SURF_FORCESIGHT, 0, 0, 0 },
/* materials */
{ "*mat_none", 0, 0, S_MAT_NONE, S_MAT_MASK, 0, 0 },
{ "*mat_solidwood", 0, 0, S_MAT_SOLIDWOOD, S_MAT_MASK, 0, 0 },
{ "*mat_hollowwood", 0, 0, S_MAT_HOLLOWWOOD, S_MAT_MASK, 0, 0 },
{ "*mat_solidmetal", 0, 0, S_MAT_SOLIDMETAL, S_MAT_MASK, 0, 0 },
{ "*mat_hollowmetal", 0, 0, S_MAT_HOLLOWMETAL, S_MAT_MASK, 0, 0 },
{ "*mat_shortgrass", 0, 0, S_MAT_SHORTGRASS, S_MAT_MASK, 0, 0 },
{ "*mat_longgrass", 0, 0, S_MAT_LONGGRASS, S_MAT_MASK, 0, 0 },
{ "*mat_dirt", 0, 0, S_MAT_DIRT, S_MAT_MASK, 0, 0 },
{ "*mat_sand", 0, 0, S_MAT_SAND, S_MAT_MASK, 0, 0 },
{ "*mat_gravel", 0, 0, S_MAT_GRAVEL, S_MAT_MASK, 0, 0 },
{ "*mat_glass", 0, 0, S_MAT_GLASS, S_MAT_MASK, 0, 0 },
{ "*mat_concrete", 0, 0, S_MAT_CONCRETE, S_MAT_MASK, 0, 0 },
{ "*mat_marble", 0, 0, S_MAT_MARBLE, S_MAT_MASK, 0, 0 },
{ "*mat_water", 0, 0, S_MAT_WATER, S_MAT_MASK, 0, 0 },
{ "*mat_snow", 0, 0, S_MAT_SNOW, S_MAT_MASK, 0, 0 },
{ "*mat_ice", 0, 0, S_MAT_ICE, S_MAT_MASK, 0, 0 },
{ "*mat_flesh", 0, 0, S_MAT_FLESH, S_MAT_MASK, 0, 0 },
{ "*mat_mud", 0, 0, S_MAT_MUD, S_MAT_MASK, 0, 0 },
{ "*mat_bpglass", 0, 0, S_MAT_BPGLASS, S_MAT_MASK, 0, 0 },
{ "*mat_dryleaves", 0, 0, S_MAT_DRYLEAVES, S_MAT_MASK, 0, 0 },
{ "*mat_greenleaves", 0, 0, S_MAT_GREENLEAVES, S_MAT_MASK, 0, 0 },
{ "*mat_fabric", 0, 0, S_MAT_FABRIC, S_MAT_MASK, 0, 0 },
{ "*mat_canvas", 0, 0, S_MAT_CANVAS, S_MAT_MASK, 0, 0 },
{ "*mat_rock", 0, 0, S_MAT_ROCK, S_MAT_MASK, 0, 0 },
{ "*mat_rubber", 0, 0, S_MAT_RUBBER, S_MAT_MASK, 0, 0 },
{ "*mat_plastic", 0, 0, S_MAT_PLASTIC, S_MAT_MASK, 0, 0 },
{ "*mat_tiles", 0, 0, S_MAT_TILES, S_MAT_MASK, 0, 0 },
{ "*mat_carpet", 0, 0, S_MAT_CARPET, S_MAT_MASK, 0, 0 },
{ "*mat_plaster", 0, 0, S_MAT_PLASTER, S_MAT_MASK, 0, 0 },
{ "*mat_shatterglass", 0, 0, S_MAT_SHATTERGLASS, S_MAT_MASK, 0, 0 },
{ "*mat_armor", 0, 0, S_MAT_ARMOR, S_MAT_MASK, 0, 0 },
{ "*mat_computer", 0, 0, S_MAT_COMPUTER, S_MAT_MASK, 0, 0 },
/* null */
{ NULL, 0, 0, 0, 0, 0, 0 }
}
}
/* end marker */
#endif

View File

@@ -0,0 +1,182 @@
/* -------------------------------------------------------------------------------
Copyright (C) 1999-2006 Id Software, Inc. and contributors.
For a list of contributors, see the accompanying CONTRIBUTORS file.
This file is part of GtkRadiant.
GtkRadiant 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.
GtkRadiant 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 GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
----------------------------------------------------------------------------------
This code has been altered significantly from its original form, to support
several games based on the Quake III Arena engine, in the form of "Q3Map2."
------------------------------------------------------------------------------- */
/* marker */
#ifndef GAME_JK2_H
#define GAME_JK2_H
/* -------------------------------------------------------------------------------
content and surface flags
------------------------------------------------------------------------------- */
/* this file must be included *after* game_sof2.h because it shares defines! */
/* -------------------------------------------------------------------------------
game_t struct
------------------------------------------------------------------------------- */
{
"jk2", /* -game x */
"base", /* default base game data dir */
".jk2", /* unix home sub-dir */
"GameData", /* magic path word */
"shaders", /* shader directory */
64, /* max lightmapped surface verts */
999, /* max surface verts */
6000, /* max surface indexes */
qtrue, /* flares */
"gfx/misc/flare", /* default flare shader */
qfalse, /* wolf lighting model? */
128, /* lightmap width/height */
1.0f, /* lightmap gamma */
1.0f, /* lightmap compensate */
"RBSP", /* bsp file prefix */
1, /* bsp file version */
qfalse, /* cod-style lump len/ofs order */
LoadRBSPFile, /* bsp load function */
WriteRBSPFile, /* bsp write function */
{
/* name contentFlags contentFlagsClear surfaceFlags surfaceFlagsClear compileFlags compileFlagsClear */
/* default */
{ "default", S_CONT_SOLID | S_CONT_OPAQUE, -1, 0, -1, C_SOLID, -1 },
/* ydnar */
{ "lightgrid", 0, 0, 0, 0, C_LIGHTGRID, 0 },
{ "antiportal", 0, 0, 0, 0, C_ANTIPORTAL, 0 },
{ "skip", 0, 0, 0, 0, C_SKIP, 0 },
/* compiler */
{ "origin", 0, S_CONT_SOLID, 0, 0, C_ORIGIN | C_TRANSLUCENT, C_SOLID },
{ "areaportal", S_CONT_TRANSLUCENT, S_CONT_SOLID | S_CONT_OPAQUE, 0, 0, C_AREAPORTAL | C_TRANSLUCENT, C_SOLID },
{ "trans", S_CONT_TRANSLUCENT, 0, 0, 0, C_TRANSLUCENT, 0 },
{ "detail", S_CONT_DETAIL, 0, 0, 0, C_DETAIL, 0 },
{ "structural", 0, 0, 0, 0, C_STRUCTURAL, 0 },
{ "hint", 0, 0, 0, 0, C_HINT, 0 },
{ "nodraw", 0, 0, S_SURF_NODRAW, 0, C_NODRAW, 0 },
{ "alphashadow", 0, 0, 0, 0, C_ALPHASHADOW | C_TRANSLUCENT, 0 },
{ "lightfilter", 0, 0, 0, 0, C_LIGHTFILTER | C_TRANSLUCENT, 0 },
{ "nolightmap", 0, 0, 0, 0, C_VERTEXLIT, 0 },
{ "pointlight", 0, 0, 0, 0, C_VERTEXLIT, 0 },
/* game */
{ "nonsolid", 0, S_CONT_SOLID, 0, 0, 0, C_SOLID },
{ "nonopaque", 0, S_CONT_OPAQUE, 0, 0, C_TRANSLUCENT, 0 }, /* setting trans ok? */
{ "trigger", S_CONT_TRIGGER, S_CONT_SOLID | S_CONT_OPAQUE, 0, 0, C_TRANSLUCENT, C_SOLID },
{ "water", S_CONT_WATER, S_CONT_SOLID, 0, 0, C_LIQUID | C_TRANSLUCENT, C_SOLID },
{ "slime", S_CONT_SLIME, S_CONT_SOLID, 0, 0, C_LIQUID | C_TRANSLUCENT, C_SOLID },
{ "lava", S_CONT_LAVA, S_CONT_SOLID, 0, 0, C_LIQUID | C_TRANSLUCENT, C_SOLID },
{ "shotclip", S_CONT_SHOTCLIP, S_CONT_SOLID | S_CONT_OPAQUE, 0, 0, C_DETAIL | C_TRANSLUCENT, C_SOLID }, /* setting trans/detail ok? */
{ "playerclip", S_CONT_PLAYERCLIP, S_CONT_SOLID | S_CONT_OPAQUE, 0, 0, C_DETAIL | C_TRANSLUCENT, C_SOLID },
{ "monsterclip", S_CONT_MONSTERCLIP, S_CONT_SOLID | S_CONT_OPAQUE, 0, 0, C_DETAIL | C_TRANSLUCENT, C_SOLID },
{ "nodrop", S_CONT_NODROP, S_CONT_SOLID | S_CONT_OPAQUE, 0, 0, C_DETAIL | C_TRANSLUCENT, C_SOLID },
{ "terrain", S_CONT_TERRAIN, S_CONT_SOLID | S_CONT_OPAQUE, 0, 0, C_DETAIL | C_TRANSLUCENT, C_SOLID },
{ "ladder", S_CONT_LADDER, S_CONT_SOLID | S_CONT_OPAQUE, 0, 0, C_DETAIL | C_TRANSLUCENT, C_SOLID },
{ "abseil", S_CONT_ABSEIL, S_CONT_SOLID | S_CONT_OPAQUE, 0, 0, C_DETAIL | C_TRANSLUCENT, C_SOLID },
{ "outside", S_CONT_OUTSIDE, S_CONT_SOLID | S_CONT_OPAQUE, 0, 0, C_DETAIL | C_TRANSLUCENT, C_SOLID },
{ "botclip", S_CONT_BOTCLIP, S_CONT_SOLID | S_CONT_OPAQUE, 0, 0, C_DETAIL | C_TRANSLUCENT, C_SOLID },
{ "fog", S_CONT_FOG, S_CONT_SOLID | S_CONT_OPAQUE, 0, 0, C_FOG | C_DETAIL | C_TRANSLUCENT, C_SOLID }, /* nonopaque? */
{ "sky", 0, 0, S_SURF_SKY, 0, C_SKY, 0 },
{ "slick", 0, 0, S_SURF_SLICK, 0, 0, 0 },
{ "noimpact", 0, 0, S_SURF_NOIMPACT, 0, 0, 0 },
{ "nomarks", 0, 0, S_SURF_NOMARKS, 0, C_NOMARKS, 0 },
{ "nodamage", 0, 0, S_SURF_NODAMAGE, 0, 0, 0 },
{ "metalsteps", 0, 0, S_SURF_METALSTEPS, 0, 0, 0 },
{ "nosteps", 0, 0, S_SURF_NOSTEPS, 0, 0, 0 },
{ "nodlight", 0, 0, S_SURF_NODLIGHT, 0, 0, 0 },
{ "nomiscents", 0, 0, S_SURF_NOMISCENTS, 0, 0, 0 },
{ "forcefield", 0, 0, S_SURF_FORCEFIELD, 0, 0, 0 },
/* materials */
{ "*mat_none", 0, 0, S_MAT_NONE, S_MAT_MASK, 0, 0 },
{ "*mat_solidwood", 0, 0, S_MAT_SOLIDWOOD, S_MAT_MASK, 0, 0 },
{ "*mat_hollowwood", 0, 0, S_MAT_HOLLOWWOOD, S_MAT_MASK, 0, 0 },
{ "*mat_solidmetal", 0, 0, S_MAT_SOLIDMETAL, S_MAT_MASK, 0, 0 },
{ "*mat_hollowmetal", 0, 0, S_MAT_HOLLOWMETAL, S_MAT_MASK, 0, 0 },
{ "*mat_shortgrass", 0, 0, S_MAT_SHORTGRASS, S_MAT_MASK, 0, 0 },
{ "*mat_longgrass", 0, 0, S_MAT_LONGGRASS, S_MAT_MASK, 0, 0 },
{ "*mat_dirt", 0, 0, S_MAT_DIRT, S_MAT_MASK, 0, 0 },
{ "*mat_sand", 0, 0, S_MAT_SAND, S_MAT_MASK, 0, 0 },
{ "*mat_gravel", 0, 0, S_MAT_GRAVEL, S_MAT_MASK, 0, 0 },
{ "*mat_glass", 0, 0, S_MAT_GLASS, S_MAT_MASK, 0, 0 },
{ "*mat_concrete", 0, 0, S_MAT_CONCRETE, S_MAT_MASK, 0, 0 },
{ "*mat_marble", 0, 0, S_MAT_MARBLE, S_MAT_MASK, 0, 0 },
{ "*mat_water", 0, 0, S_MAT_WATER, S_MAT_MASK, 0, 0 },
{ "*mat_snow", 0, 0, S_MAT_SNOW, S_MAT_MASK, 0, 0 },
{ "*mat_ice", 0, 0, S_MAT_ICE, S_MAT_MASK, 0, 0 },
{ "*mat_flesh", 0, 0, S_MAT_FLESH, S_MAT_MASK, 0, 0 },
{ "*mat_mud", 0, 0, S_MAT_MUD, S_MAT_MASK, 0, 0 },
{ "*mat_bpglass", 0, 0, S_MAT_BPGLASS, S_MAT_MASK, 0, 0 },
{ "*mat_dryleaves", 0, 0, S_MAT_DRYLEAVES, S_MAT_MASK, 0, 0 },
{ "*mat_greenleaves", 0, 0, S_MAT_GREENLEAVES, S_MAT_MASK, 0, 0 },
{ "*mat_fabric", 0, 0, S_MAT_FABRIC, S_MAT_MASK, 0, 0 },
{ "*mat_canvas", 0, 0, S_MAT_CANVAS, S_MAT_MASK, 0, 0 },
{ "*mat_rock", 0, 0, S_MAT_ROCK, S_MAT_MASK, 0, 0 },
{ "*mat_rubber", 0, 0, S_MAT_RUBBER, S_MAT_MASK, 0, 0 },
{ "*mat_plastic", 0, 0, S_MAT_PLASTIC, S_MAT_MASK, 0, 0 },
{ "*mat_tiles", 0, 0, S_MAT_TILES, S_MAT_MASK, 0, 0 },
{ "*mat_carpet", 0, 0, S_MAT_CARPET, S_MAT_MASK, 0, 0 },
{ "*mat_plaster", 0, 0, S_MAT_PLASTER, S_MAT_MASK, 0, 0 },
{ "*mat_shatterglass", 0, 0, S_MAT_SHATTERGLASS, S_MAT_MASK, 0, 0 },
{ "*mat_armor", 0, 0, S_MAT_ARMOR, S_MAT_MASK, 0, 0 },
{ "*mat_computer", 0, 0, S_MAT_COMPUTER, S_MAT_MASK, 0, 0 },
/* null */
{ NULL, 0, 0, 0, 0, 0, 0 }
}
}
/* end marker */
#endif

View File

@@ -0,0 +1,192 @@
/* -------------------------------------------------------------------------------
Copyright (C) 1999-2006 Id Software, Inc. and contributors.
For a list of contributors, see the accompanying CONTRIBUTORS file.
This file is part of GtkRadiant.
GtkRadiant 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.
GtkRadiant 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 GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
----------------------------------------------------------------------------------
This code has been altered significantly from its original form, to support
several games based on the Quake III Arena engine, in the form of "Q3Map2."
------------------------------------------------------------------------------- */
/* marker */
#ifndef GAME_QUAKE3_H
#define GAME_QUAKE3_H
/* -------------------------------------------------------------------------------
content and surface flags
------------------------------------------------------------------------------- */
/* game flags */
#define Q_CONT_SOLID 1 /* an eye is never valid in a solid */
#define Q_CONT_LAVA 8
#define Q_CONT_SLIME 16
#define Q_CONT_WATER 32
#define Q_CONT_FOG 64
#define Q_CONT_AREAPORTAL 0x8000
#define Q_CONT_PLAYERCLIP 0x10000
#define Q_CONT_MONSTERCLIP 0x20000
#define Q_CONT_TELEPORTER 0x40000
#define Q_CONT_JUMPPAD 0x80000
#define Q_CONT_CLUSTERPORTAL 0x100000
#define Q_CONT_DONOTENTER 0x200000
#define Q_CONT_BOTCLIP 0x400000
#define Q_CONT_ORIGIN 0x1000000 /* removed before bsping an entity */
#define Q_CONT_BODY 0x2000000 /* should never be on a brush, only in game */
#define Q_CONT_CORPSE 0x4000000
#define Q_CONT_DETAIL 0x8000000 /* brushes not used for the bsp */
#define Q_CONT_STRUCTURAL 0x10000000 /* brushes used for the bsp */
#define Q_CONT_TRANSLUCENT 0x20000000 /* don't consume surface fragments inside */
#define Q_CONT_TRIGGER 0x40000000
#define Q_CONT_NODROP 0x80000000 /* don't leave bodies or items (death fog, lava) */
#define Q_SURF_NODAMAGE 0x1 /* never give falling damage */
#define Q_SURF_SLICK 0x2 /* effects game physics */
#define Q_SURF_SKY 0x4 /* lighting from environment map */
#define Q_SURF_LADDER 0x8
#define Q_SURF_NOIMPACT 0x10 /* don't make missile explosions */
#define Q_SURF_NOMARKS 0x20 /* don't leave missile marks */
#define Q_SURF_FLESH 0x40 /* make flesh sounds and effects */
#define Q_SURF_NODRAW 0x80 /* don't generate a drawsurface at all */
#define Q_SURF_HINT 0x100 /* make a primary bsp splitter */
#define Q_SURF_SKIP 0x200 /* completely ignore, allowing non-closed brushes */
#define Q_SURF_NOLIGHTMAP 0x400 /* surface doesn't need a lightmap */
#define Q_SURF_POINTLIGHT 0x800 /* generate lighting info at vertexes */
#define Q_SURF_METALSTEPS 0x1000 /* clanking footsteps */
#define Q_SURF_NOSTEPS 0x2000 /* no footstep sounds */
#define Q_SURF_NONSOLID 0x4000 /* don't collide against curves with this set */
#define Q_SURF_LIGHTFILTER 0x8000 /* act as a light filter during q3map -light */
#define Q_SURF_ALPHASHADOW 0x10000 /* do per-pixel light shadow casting in q3map */
#define Q_SURF_NODLIGHT 0x20000 /* don't dlight even if solid (solid lava, skies) */
#define Q_SURF_DUST 0x40000 /* leave a dust trail when walking on this surface */
/* ydnar flags */
#define Q_SURF_VERTEXLIT (Q_SURF_POINTLIGHT | Q_SURF_NOLIGHTMAP)
/* -------------------------------------------------------------------------------
game_t struct
------------------------------------------------------------------------------- */
{
"quake3", /* -game x */
"baseq3", /* default base game data dir */
".q3a", /* unix home sub-dir */
"quake", /* magic path word */
"scripts", /* shader directory */
64, /* max lightmapped surface verts */
999, /* max surface verts */
6000, /* max surface indexes */
qfalse, /* flares */
"flareshader", /* default flare shader */
qfalse, /* wolf lighting model? */
128, /* lightmap width/height */
1.0f, /* lightmap gamma */
1.0f, /* lightmap compensate */
"IBSP", /* bsp file prefix */
46, /* bsp file version */
qfalse, /* cod-style lump len/ofs order */
LoadIBSPFile, /* bsp load function */
WriteIBSPFile, /* bsp write function */
{
/* name contentFlags contentFlagsClear surfaceFlags surfaceFlagsClear compileFlags compileFlagsClear */
/* default */
{ "default", Q_CONT_SOLID, -1, 0, -1, C_SOLID, -1 },
/* ydnar */
{ "lightgrid", 0, 0, 0, 0, C_LIGHTGRID, 0 },
{ "antiportal", 0, 0, 0, 0, C_ANTIPORTAL, 0 },
{ "skip", 0, 0, 0, 0, C_SKIP, 0 },
/* compiler */
{ "origin", Q_CONT_ORIGIN, Q_CONT_SOLID, 0, 0, C_ORIGIN | C_TRANSLUCENT, C_SOLID },
{ "areaportal", Q_CONT_AREAPORTAL, Q_CONT_SOLID, 0, 0, C_AREAPORTAL | C_TRANSLUCENT, C_SOLID },
{ "trans", Q_CONT_TRANSLUCENT, 0, 0, 0, C_TRANSLUCENT, 0 },
{ "detail", Q_CONT_DETAIL, 0, 0, 0, C_DETAIL, 0 },
{ "structural", Q_CONT_STRUCTURAL, 0, 0, 0, C_STRUCTURAL, 0 },
{ "hint", 0, 0, Q_SURF_HINT, 0, C_HINT, 0 },
{ "nodraw", 0, 0, Q_SURF_NODRAW, 0, C_NODRAW, 0 },
{ "alphashadow", 0, 0, Q_SURF_ALPHASHADOW, 0, C_ALPHASHADOW | C_TRANSLUCENT, 0 },
{ "lightfilter", 0, 0, Q_SURF_LIGHTFILTER, 0, C_LIGHTFILTER | C_TRANSLUCENT, 0 },
{ "nolightmap", 0, 0, Q_SURF_VERTEXLIT, 0, C_VERTEXLIT, 0 },
{ "pointlight", 0, 0, Q_SURF_VERTEXLIT, 0, C_VERTEXLIT, 0 },
/* game */
{ "nonsolid", 0, Q_CONT_SOLID, Q_SURF_NONSOLID, 0, 0, C_SOLID },
{ "trigger", Q_CONT_TRIGGER, Q_CONT_SOLID, 0, 0, C_TRANSLUCENT, C_SOLID },
{ "water", Q_CONT_WATER, Q_CONT_SOLID, 0, 0, C_LIQUID | C_TRANSLUCENT, C_SOLID },
{ "slime", Q_CONT_SLIME, Q_CONT_SOLID, 0, 0, C_LIQUID | C_TRANSLUCENT, C_SOLID },
{ "lava", Q_CONT_LAVA, Q_CONT_SOLID, 0, 0, C_LIQUID | C_TRANSLUCENT, C_SOLID },
{ "playerclip", Q_CONT_PLAYERCLIP, Q_CONT_SOLID, 0, 0, C_DETAIL | C_TRANSLUCENT, C_SOLID },
{ "monsterclip", Q_CONT_MONSTERCLIP, Q_CONT_SOLID, 0, 0, C_DETAIL | C_TRANSLUCENT, C_SOLID },
{ "nodrop", Q_CONT_NODROP, Q_CONT_SOLID, 0, 0, C_TRANSLUCENT, C_SOLID },
{ "clusterportal", Q_CONT_CLUSTERPORTAL, Q_CONT_SOLID, 0, 0, C_TRANSLUCENT, C_SOLID },
{ "donotenter", Q_CONT_DONOTENTER, Q_CONT_SOLID, 0, 0, C_TRANSLUCENT, C_SOLID },
{ "botclip", Q_CONT_BOTCLIP, Q_CONT_SOLID, 0, 0, C_TRANSLUCENT, C_SOLID },
{ "fog", Q_CONT_FOG, Q_CONT_SOLID, 0, 0, C_FOG, C_SOLID },
{ "sky", 0, 0, Q_SURF_SKY, 0, C_SKY, 0 },
{ "slick", 0, 0, Q_SURF_SLICK, 0, 0, 0 },
{ "noimpact", 0, 0, Q_SURF_NOIMPACT, 0, 0, 0 },
{ "nomarks", 0, 0, Q_SURF_NOMARKS, 0, C_NOMARKS, 0 },
{ "ladder", 0, 0, Q_SURF_LADDER, 0, 0, 0 },
{ "nodamage", 0, 0, Q_SURF_NODAMAGE, 0, 0, 0 },
{ "metalsteps", 0, 0, Q_SURF_METALSTEPS, 0, 0, 0 },
{ "flesh", 0, 0, Q_SURF_FLESH, 0, 0, 0 },
{ "nosteps", 0, 0, Q_SURF_NOSTEPS, 0, 0, 0 },
{ "nodlight", 0, 0, Q_SURF_NODLIGHT, 0, 0, 0 },
{ "dust", 0, 0, Q_SURF_DUST, 0, 0, 0 },
/* null */
{ NULL, 0, 0, 0, 0, 0, 0 }
}
}
/* end marker */
#endif

View File

@@ -0,0 +1,257 @@
/* -------------------------------------------------------------------------------
Copyright (C) 1999-2006 Id Software, Inc. and contributors.
For a list of contributors, see the accompanying CONTRIBUTORS file.
This file is part of GtkRadiant.
GtkRadiant 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.
GtkRadiant 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 GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
----------------------------------------------------------------------------------
This code has been altered significantly from its original form, to support
several games based on the Quake III Arena engine, in the form of "Q3Map2."
------------------------------------------------------------------------------- */
/* marker */
#ifndef GAME_SOF2_H
#define GAME_SOF2_H
/* -------------------------------------------------------------------------------
content and surface flags
------------------------------------------------------------------------------- */
/* thanks to the gracious fellows at raven */
#define S_CONT_SOLID 0x00000001 /* Default setting. An eye is never valid in a solid */
#define S_CONT_LAVA 0x00000002
#define S_CONT_WATER 0x00000004
#define S_CONT_FOG 0x00000008
#define S_CONT_PLAYERCLIP 0x00000010
#define S_CONT_MONSTERCLIP 0x00000020
#define S_CONT_BOTCLIP 0x00000040
#define S_CONT_SHOTCLIP 0x00000080
#define S_CONT_BODY 0x00000100 /* should never be on a brush, only in game */
#define S_CONT_CORPSE 0x00000200 /* should never be on a brush, only in game */
#define S_CONT_TRIGGER 0x00000400
#define S_CONT_NODROP 0x00000800 /* don't leave bodies or items (death fog, lava) */
#define S_CONT_TERRAIN 0x00001000 /* volume contains terrain data */
#define S_CONT_LADDER 0x00002000
#define S_CONT_ABSEIL 0x00004000 /* used like ladder to define where an NPC can abseil */
#define S_CONT_OPAQUE 0x00008000 /* defaults to on, when off, solid can be seen through */
#define S_CONT_OUTSIDE 0x00010000 /* volume is considered to be in the outside (i.e. not indoors) */
#define S_CONT_SLIME 0x00020000 /* don't be fooled. it may SAY "slime" but it really means "projectileclip" */
#define S_CONT_LIGHTSABER 0x00040000
#define S_CONT_TELEPORTER 0x00080000
#define S_CONT_ITEM 0x00100000
#define S_CONT_DETAIL 0x08000000 /* brushes not used for the bsp */
#define S_CONT_TRANSLUCENT 0x80000000 /* don't consume surface fragments inside */
#define S_SURF_SKY 0x00002000 /* lighting from environment map */
#define S_SURF_SLICK 0x00004000 /* affects game physics */
#define S_SURF_METALSTEPS 0x00008000 /* chc needs this since we use same tools */
#define S_SURF_FORCEFIELD 0x00010000 /* chc */
#define S_SURF_NODAMAGE 0x00040000 /* never give falling damage */
#define S_SURF_NOIMPACT 0x00080000 /* don't make missile explosions */
#define S_SURF_NOMARKS 0x00100000 /* don't leave missile marks */
#define S_SURF_NODRAW 0x00200000 /* don't generate a drawsurface at all */
#define S_SURF_NOSTEPS 0x00400000 /* no footstep sounds */
#define S_SURF_NODLIGHT 0x00800000 /* don't dlight even if solid (solid lava, skies) */
#define S_SURF_NOMISCENTS 0x01000000 /* no client models allowed on this surface */
#define S_SURF_PATCH 0x80000000 /* mark this face as a patch(editor only) */
/* materials */
#define S_MAT_BITS 5
#define S_MAT_MASK 0x1f /* mask to get the material type */
#define S_MAT_NONE 0 /* for when the artist hasn't set anything up =) */
#define S_MAT_SOLIDWOOD 1 /* freshly cut timber */
#define S_MAT_HOLLOWWOOD 2 /* termite infested creaky wood */
#define S_MAT_SOLIDMETAL 3 /* solid girders */
#define S_MAT_HOLLOWMETAL 4 /* hollow metal machines */
#define S_MAT_SHORTGRASS 5 /* manicured lawn */
#define S_MAT_LONGGRASS 6 /* long jungle grass */
#define S_MAT_DIRT 7 /* hard mud */
#define S_MAT_SAND 8 /* sandy beach */
#define S_MAT_GRAVEL 9 /* lots of small stones */
#define S_MAT_GLASS 10
#define S_MAT_CONCRETE 11 /* hardened concrete pavement */
#define S_MAT_MARBLE 12 /* marble floors */
#define S_MAT_WATER 13 /* light covering of water on a surface */
#define S_MAT_SNOW 14 /* freshly laid snow */
#define S_MAT_ICE 15 /* packed snow/solid ice */
#define S_MAT_FLESH 16 /* hung meat, corpses in the world */
#define S_MAT_MUD 17 /* wet soil */
#define S_MAT_BPGLASS 18 /* bulletproof glass */
#define S_MAT_DRYLEAVES 19 /* dried up leaves on the floor */
#define S_MAT_GREENLEAVES 20 /* fresh leaves still on a tree */
#define S_MAT_FABRIC 21 /* Cotton sheets */
#define S_MAT_CANVAS 22 /* tent material */
#define S_MAT_ROCK 23
#define S_MAT_RUBBER 24 /* hard tire like rubber */
#define S_MAT_PLASTIC 25
#define S_MAT_TILES 26 /* tiled floor */
#define S_MAT_CARPET 27 /* lush carpet */
#define S_MAT_PLASTER 28 /* drywall style plaster */
#define S_MAT_SHATTERGLASS 29 /* glass with the Crisis Zone style shattering */
#define S_MAT_ARMOR 30 /* body armor */
#define S_MAT_COMPUTER 31 /* computers/electronic equipment */
#define S_MAT_LAST 32 /* number of materials */
/* -------------------------------------------------------------------------------
game_t struct
------------------------------------------------------------------------------- */
{
"sof2", /* -game x */
"base", /* default base game data dir */
".sof2", /* unix home sub-dir */
"soldier", /* magic path word */
"shaders", /* shader directory */
64, /* max lightmapped surface verts */
999, /* max surface verts */
6000, /* max surface indexes */
qtrue, /* flares */
"gfx/misc/lens_flare", /* default flare shader */
qfalse, /* wolf lighting model? */
128, /* lightmap width/height */
1.0f, /* lightmap gamma */
1.0f, /* lightmap compensate */
"RBSP", /* bsp file prefix */
1, /* bsp file version */
qfalse, /* cod-style lump len/ofs order */
LoadRBSPFile, /* bsp load function */
WriteRBSPFile, /* bsp write function */
{
/* name contentFlags contentFlagsClear surfaceFlags surfaceFlagsClear compileFlags compileFlagsClear */
/* default */
{ "default", S_CONT_SOLID | S_CONT_OPAQUE, -1, 0, -1, C_SOLID, -1 },
/* ydnar */
{ "lightgrid", 0, 0, 0, 0, C_LIGHTGRID, 0 },
{ "antiportal", 0, 0, 0, 0, C_ANTIPORTAL, 0 },
{ "skip", 0, 0, 0, 0, C_SKIP, 0 },
/* compiler */
{ "origin", 0, S_CONT_SOLID, 0, 0, C_ORIGIN | C_TRANSLUCENT, C_SOLID },
{ "areaportal", S_CONT_TRANSLUCENT, S_CONT_SOLID | S_CONT_OPAQUE, 0, 0, C_AREAPORTAL | C_TRANSLUCENT, C_SOLID },
{ "trans", S_CONT_TRANSLUCENT, 0, 0, 0, C_TRANSLUCENT, 0 },
{ "detail", S_CONT_DETAIL, 0, 0, 0, C_DETAIL, 0 },
{ "structural", 0, 0, 0, 0, C_STRUCTURAL, 0 },
{ "hint", 0, 0, 0, 0, C_HINT, 0 },
{ "nodraw", 0, 0, S_SURF_NODRAW, 0, C_NODRAW, 0 },
{ "alphashadow", 0, 0, 0, 0, C_ALPHASHADOW | C_TRANSLUCENT, 0 },
{ "lightfilter", 0, 0, 0, 0, C_LIGHTFILTER | C_TRANSLUCENT, 0 },
{ "nolightmap", 0, 0, 0, 0, C_VERTEXLIT, 0 },
{ "pointlight", 0, 0, 0, 0, C_VERTEXLIT, 0 },
/* game */
{ "nonsolid", 0, S_CONT_SOLID, 0, 0, 0, C_SOLID },
{ "nonopaque", 0, S_CONT_OPAQUE, 0, 0, C_TRANSLUCENT, 0 }, /* setting trans ok? */
{ "trigger", S_CONT_TRIGGER, S_CONT_SOLID | S_CONT_OPAQUE, 0, 0, C_TRANSLUCENT, C_SOLID },
{ "water", S_CONT_WATER, S_CONT_SOLID, 0, 0, C_LIQUID | C_TRANSLUCENT, C_SOLID },
{ "slime", S_CONT_SLIME, S_CONT_SOLID, 0, 0, C_LIQUID | C_TRANSLUCENT, C_SOLID },
{ "lava", S_CONT_LAVA, S_CONT_SOLID, 0, 0, C_LIQUID | C_TRANSLUCENT, C_SOLID },
{ "shotclip", S_CONT_SHOTCLIP, S_CONT_SOLID | S_CONT_OPAQUE, 0, 0, C_DETAIL | C_TRANSLUCENT, C_SOLID }, /* setting trans/detail ok? */
{ "playerclip", S_CONT_PLAYERCLIP, S_CONT_SOLID | S_CONT_OPAQUE, 0, 0, C_DETAIL | C_TRANSLUCENT, C_SOLID },
{ "monsterclip", S_CONT_MONSTERCLIP, S_CONT_SOLID | S_CONT_OPAQUE, 0, 0, C_DETAIL | C_TRANSLUCENT, C_SOLID },
{ "nodrop", S_CONT_NODROP, S_CONT_SOLID | S_CONT_OPAQUE, 0, 0, C_DETAIL | C_TRANSLUCENT, C_SOLID },
{ "terrain", S_CONT_TERRAIN, S_CONT_SOLID | S_CONT_OPAQUE, 0, 0, C_DETAIL | C_TRANSLUCENT, C_SOLID },
{ "ladder", S_CONT_LADDER, S_CONT_SOLID | S_CONT_OPAQUE, 0, 0, C_DETAIL | C_TRANSLUCENT, C_SOLID },
{ "abseil", S_CONT_ABSEIL, S_CONT_SOLID | S_CONT_OPAQUE, 0, 0, C_DETAIL | C_TRANSLUCENT, C_SOLID },
{ "outside", S_CONT_OUTSIDE, S_CONT_SOLID | S_CONT_OPAQUE, 0, 0, C_DETAIL | C_TRANSLUCENT, C_SOLID },
{ "botclip", S_CONT_BOTCLIP, S_CONT_SOLID | S_CONT_OPAQUE, 0, 0, C_DETAIL | C_TRANSLUCENT, C_SOLID },
{ "fog", S_CONT_FOG, S_CONT_SOLID | S_CONT_OPAQUE, 0, 0, C_FOG | C_DETAIL | C_TRANSLUCENT, C_SOLID }, /* nonopaque? */
{ "sky", 0, 0, S_SURF_SKY, 0, C_SKY, 0 },
{ "slick", 0, 0, S_SURF_SLICK, 0, 0, 0 },
{ "noimpact", 0, 0, S_SURF_NOIMPACT, 0, 0, 0 },
{ "nomarks", 0, 0, S_SURF_NOMARKS, 0, C_NOMARKS, 0 },
{ "nodamage", 0, 0, S_SURF_NODAMAGE, 0, 0, 0 },
{ "metalsteps", 0, 0, S_SURF_METALSTEPS, 0, 0, 0 },
{ "nosteps", 0, 0, S_SURF_NOSTEPS, 0, 0, 0 },
{ "nodlight", 0, 0, S_SURF_NODLIGHT, 0, 0, 0 },
{ "nomiscents", 0, 0, S_SURF_NOMISCENTS, 0, 0, 0 },
{ "forcefield", 0, 0, S_SURF_FORCEFIELD, 0, 0, 0 },
/* materials */
{ "*mat_none", 0, 0, S_MAT_NONE, S_MAT_MASK, 0, 0 },
{ "*mat_solidwood", 0, 0, S_MAT_SOLIDWOOD, S_MAT_MASK, 0, 0 },
{ "*mat_hollowwood", 0, 0, S_MAT_HOLLOWWOOD, S_MAT_MASK, 0, 0 },
{ "*mat_solidmetal", 0, 0, S_MAT_SOLIDMETAL, S_MAT_MASK, 0, 0 },
{ "*mat_hollowmetal", 0, 0, S_MAT_HOLLOWMETAL, S_MAT_MASK, 0, 0 },
{ "*mat_shortgrass", 0, 0, S_MAT_SHORTGRASS, S_MAT_MASK, 0, 0 },
{ "*mat_longgrass", 0, 0, S_MAT_LONGGRASS, S_MAT_MASK, 0, 0 },
{ "*mat_dirt", 0, 0, S_MAT_DIRT, S_MAT_MASK, 0, 0 },
{ "*mat_sand", 0, 0, S_MAT_SAND, S_MAT_MASK, 0, 0 },
{ "*mat_gravel", 0, 0, S_MAT_GRAVEL, S_MAT_MASK, 0, 0 },
{ "*mat_glass", 0, 0, S_MAT_GLASS, S_MAT_MASK, 0, 0 },
{ "*mat_concrete", 0, 0, S_MAT_CONCRETE, S_MAT_MASK, 0, 0 },
{ "*mat_marble", 0, 0, S_MAT_MARBLE, S_MAT_MASK, 0, 0 },
{ "*mat_water", 0, 0, S_MAT_WATER, S_MAT_MASK, 0, 0 },
{ "*mat_snow", 0, 0, S_MAT_SNOW, S_MAT_MASK, 0, 0 },
{ "*mat_ice", 0, 0, S_MAT_ICE, S_MAT_MASK, 0, 0 },
{ "*mat_flesh", 0, 0, S_MAT_FLESH, S_MAT_MASK, 0, 0 },
{ "*mat_mud", 0, 0, S_MAT_MUD, S_MAT_MASK, 0, 0 },
{ "*mat_bpglass", 0, 0, S_MAT_BPGLASS, S_MAT_MASK, 0, 0 },
{ "*mat_dryleaves", 0, 0, S_MAT_DRYLEAVES, S_MAT_MASK, 0, 0 },
{ "*mat_greenleaves", 0, 0, S_MAT_GREENLEAVES, S_MAT_MASK, 0, 0 },
{ "*mat_fabric", 0, 0, S_MAT_FABRIC, S_MAT_MASK, 0, 0 },
{ "*mat_canvas", 0, 0, S_MAT_CANVAS, S_MAT_MASK, 0, 0 },
{ "*mat_rock", 0, 0, S_MAT_ROCK, S_MAT_MASK, 0, 0 },
{ "*mat_rubber", 0, 0, S_MAT_RUBBER, S_MAT_MASK, 0, 0 },
{ "*mat_plastic", 0, 0, S_MAT_PLASTIC, S_MAT_MASK, 0, 0 },
{ "*mat_tiles", 0, 0, S_MAT_TILES, S_MAT_MASK, 0, 0 },
{ "*mat_carpet", 0, 0, S_MAT_CARPET, S_MAT_MASK, 0, 0 },
{ "*mat_plaster", 0, 0, S_MAT_PLASTER, S_MAT_MASK, 0, 0 },
{ "*mat_shatterglass", 0, 0, S_MAT_SHATTERGLASS, S_MAT_MASK, 0, 0 },
{ "*mat_armor", 0, 0, S_MAT_ARMOR, S_MAT_MASK, 0, 0 },
{ "*mat_computer", 0, 0, S_MAT_COMPUTER, S_MAT_MASK, 0, 0 },
/* null */
{ NULL, 0, 0, 0, 0, 0, 0 }
}
}
/* end marker */
#endif

View File

@@ -0,0 +1,34 @@
/*
Copyright (C) 1999-2006 Id Software, Inc. and contributors.
For a list of contributors, see the accompanying CONTRIBUTORS file.
This file is part of GtkRadiant.
GtkRadiant 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.
GtkRadiant 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 GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
/* ydnar: for -game support */
typedef struct game_s
{
char *arg; /* -game matches this */
char *gamePath; /* main game data dir */
char *homeBasePath; /* home sub-dir on unix */
char *magic; /* magic word for figuring out base path */
qboolean wolfLight; /* when true, lights work like wolf q3map */
int bspVersion; /* BSP version to use */
}
game_t;

View File

@@ -0,0 +1,192 @@
/* -------------------------------------------------------------------------------
Copyright (C) 1999-2006 Id Software, Inc. and contributors.
For a list of contributors, see the accompanying CONTRIBUTORS file.
This file is part of GtkRadiant.
GtkRadiant 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.
GtkRadiant 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 GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
----------------------------------------------------------------------------------
This code has been altered significantly from its original form, to support
several games based on the Quake III Arena engine, in the form of "Q3Map2."
------------------------------------------------------------------------------- */
/* marker */
#ifndef GAME_TENEBRAE_H
#define GAME_TENEBRAE_H
/* -------------------------------------------------------------------------------
content and surface flags
------------------------------------------------------------------------------- */
/* game flags */
#define T_CONT_SOLID 1 /* an eye is never valid in a solid */
#define T_CONT_LAVA 8
#define T_CONT_SLIME 16
#define T_CONT_WATER 32
#define T_CONT_FOG 64
#define T_CONT_AREAPORTAL 0x8000
#define T_CONT_PLAYERCLIP 0x10000
#define T_CONT_MONSTERCLIP 0x20000
#define T_CONT_TELEPORTER 0x40000
#define T_CONT_JUMPPAD 0x80000
#define T_CONT_CLUSTERPORTAL 0x100000
#define T_CONT_DONOTENTER 0x200000
#define T_CONT_BOTCLIP 0x400000
#define T_CONT_ORIGIN 0x1000000 /* removed before bsping an entity */
#define T_CONT_BODY 0x2000000 /* should never be on a brush, only in game */
#define T_CONT_CORPSE 0x4000000
#define T_CONT_DETAIL 0x8000000 /* brushes not used for the bsp */
#define T_CONT_STRUCTURAL 0x10000000 /* brushes used for the bsp */
#define T_CONT_TRANSLUCENT 0x20000000 /* don't consume surface fragments inside */
#define T_CONT_TRIGGER 0x40000000
#define T_CONT_NODROP 0x80000000 /* don't leave bodies or items (death fog, lava) */
#define T_SURF_NODAMAGE 0x1 /* never give falling damage */
#define T_SURF_SLICK 0x2 /* effects game physics */
#define T_SURF_SKY 0x4 /* lighting from environment map */
#define T_SURF_LADDER 0x8
#define T_SURF_NOIMPACT 0x10 /* don't make missile explosions */
#define T_SURF_NOMARKS 0x20 /* don't leave missile marks */
#define T_SURF_FLESH 0x40 /* make flesh sounds and effects */
#define T_SURF_NODRAW 0x80 /* don't generate a drawsurface at all */
#define T_SURF_HINT 0x100 /* make a primary bsp splitter */
#define T_SURF_SKIP 0x200 /* completely ignore, allowing non-closed brushes */
#define T_SURF_NOLIGHTMAP 0x400 /* surface doesn't need a lightmap */
#define T_SURF_POINTLIGHT 0x800 /* generate lighting info at vertexes */
#define T_SURF_METALSTEPS 0x1000 /* clanking footsteps */
#define T_SURF_NOSTEPS 0x2000 /* no footstep sounds */
#define T_SURF_NONSOLID 0x4000 /* don't collide against curves with this set */
#define T_SURF_LIGHTFILTER 0x8000 /* act as a light filter during q3map -light */
#define T_SURF_ALPHASHADOW 0x10000 /* do per-pixel light shadow casting in q3map */
#define T_SURF_NODLIGHT 0x20000 /* don't dlight even if solid (solid lava, skies) */
#define T_SURF_DUST 0x40000 /* leave a dust trail when walking on this surface */
/* ydnar flags */
#define T_SURF_VERTEXLIT (T_SURF_POINTLIGHT | T_SURF_NOLIGHTMAP)
/* -------------------------------------------------------------------------------
game_t struct
------------------------------------------------------------------------------- */
{
"tenebrae", /* -game x */
"base", /* default base game data dir */
".tenebrae", /* unix home sub-dir */
"tenebrae", /* magic path word */
"scripts", /* shader directory */
1024, /* max lightmapped surface verts */
1024, /* max surface verts */
6144, /* max surface indexes */
qfalse, /* flares */
"flareshader", /* default flare shader */
qfalse, /* wolf lighting model? */
512, /* lightmap width/height */
2.0f, /* lightmap gamma */
1.0f, /* lightmap compensate */
"IBSP", /* bsp file prefix */
46, /* bsp file version */
qfalse, /* cod-style lump len/ofs order */
LoadIBSPFile, /* bsp load function */
WriteIBSPFile, /* bsp write function */
{
/* name contentFlags contentFlagsClear surfaceFlags surfaceFlagsClear compileFlags compileFlagsClear */
/* default */
{ "default", T_CONT_SOLID, -1, 0, -1, C_SOLID, -1 },
/* ydnar */
{ "lightgrid", 0, 0, 0, 0, C_LIGHTGRID, 0 },
{ "antiportal", 0, 0, 0, 0, C_ANTIPORTAL, 0 },
{ "skip", 0, 0, 0, 0, C_SKIP, 0 },
/* compiler */
{ "origin", T_CONT_ORIGIN, T_CONT_SOLID, 0, 0, C_ORIGIN | C_TRANSLUCENT, C_SOLID },
{ "areaportal", T_CONT_AREAPORTAL, T_CONT_SOLID, 0, 0, C_AREAPORTAL | C_TRANSLUCENT, C_SOLID },
{ "trans", T_CONT_TRANSLUCENT, 0, 0, 0, C_TRANSLUCENT, 0 },
{ "detail", T_CONT_DETAIL, 0, 0, 0, C_DETAIL, 0 },
{ "structural", T_CONT_STRUCTURAL, 0, 0, 0, C_STRUCTURAL, 0 },
{ "hint", 0, 0, T_SURF_HINT, 0, C_HINT, 0 },
{ "nodraw", 0, 0, T_SURF_NODRAW, 0, C_NODRAW, 0 },
{ "alphashadow", 0, 0, T_SURF_ALPHASHADOW, 0, C_ALPHASHADOW | C_TRANSLUCENT, 0 },
{ "lightfilter", 0, 0, T_SURF_LIGHTFILTER, 0, C_LIGHTFILTER | C_TRANSLUCENT, 0 },
{ "nolightmap", 0, 0, T_SURF_VERTEXLIT, 0, C_VERTEXLIT, 0 },
{ "pointlight", 0, 0, T_SURF_VERTEXLIT, 0, C_VERTEXLIT, 0 },
/* game */
{ "nonsolid", 0, T_CONT_SOLID, T_SURF_NONSOLID, 0, 0, C_SOLID },
{ "trigger", T_CONT_TRIGGER, T_CONT_SOLID, 0, 0, C_TRANSLUCENT, C_SOLID },
{ "water", T_CONT_WATER, T_CONT_SOLID, 0, 0, C_LIQUID | C_TRANSLUCENT, C_SOLID },
{ "slime", T_CONT_SLIME, T_CONT_SOLID, 0, 0, C_LIQUID | C_TRANSLUCENT, C_SOLID },
{ "lava", T_CONT_LAVA, T_CONT_SOLID, 0, 0, C_LIQUID | C_TRANSLUCENT, C_SOLID },
{ "playerclip", T_CONT_PLAYERCLIP, T_CONT_SOLID, 0, 0, C_DETAIL | C_TRANSLUCENT, C_SOLID },
{ "monsterclip", T_CONT_MONSTERCLIP, T_CONT_SOLID, 0, 0, C_DETAIL | C_TRANSLUCENT, C_SOLID },
{ "nodrop", T_CONT_NODROP, T_CONT_SOLID, 0, 0, C_TRANSLUCENT, C_SOLID },
{ "clusterportal", T_CONT_CLUSTERPORTAL, T_CONT_SOLID, 0, 0, C_TRANSLUCENT, C_SOLID },
{ "donotenter", T_CONT_DONOTENTER, T_CONT_SOLID, 0, 0, C_TRANSLUCENT, C_SOLID },
{ "botclip", T_CONT_BOTCLIP, T_CONT_SOLID, 0, 0, C_TRANSLUCENT, C_SOLID },
{ "fog", T_CONT_FOG, T_CONT_SOLID, 0, 0, C_FOG, C_SOLID },
{ "sky", 0, 0, T_SURF_SKY, 0, C_SKY, 0 },
{ "slick", 0, 0, T_SURF_SLICK, 0, 0, 0 },
{ "noimpact", 0, 0, T_SURF_NOIMPACT, 0, 0, 0 },
{ "nomarks", 0, 0, T_SURF_NOMARKS, 0, C_NOMARKS, 0 },
{ "ladder", 0, 0, T_SURF_LADDER, 0, 0, 0 },
{ "nodamage", 0, 0, T_SURF_NODAMAGE, 0, 0, 0 },
{ "metalsteps", 0, 0, T_SURF_METALSTEPS, 0, 0, 0 },
{ "flesh", 0, 0, T_SURF_FLESH, 0, 0, 0 },
{ "nosteps", 0, 0, T_SURF_NOSTEPS, 0, 0, 0 },
{ "nodlight", 0, 0, T_SURF_NODLIGHT, 0, 0, 0 },
{ "dust", 0, 0, T_SURF_DUST, 0, 0, 0 },
/* null */
{ NULL, 0, 0, 0, 0, 0, 0 }
}
}
/* end marker */
#endif

View File

@@ -0,0 +1,238 @@
/* -------------------------------------------------------------------------------
Copyright (C) 1999-2006 Id Software, Inc. and contributors.
For a list of contributors, see the accompanying CONTRIBUTORS file.
This file is part of GtkRadiant.
GtkRadiant 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.
GtkRadiant 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 GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
----------------------------------------------------------------------------------
This code has been altered significantly from its original form, to support
several games based on the Quake III Arena engine, in the form of "Q3Map2."
------------------------------------------------------------------------------- */
/* marker */
#ifndef GAME_WOLF_H
#define GAME_WOLF_H
/* -------------------------------------------------------------------------------
content and surface flags
------------------------------------------------------------------------------- */
/* game flags */
#define W_CONT_SOLID 1 /* an eye is never valid in a solid */
#define W_CONT_LAVA 8
#define W_CONT_SLIME 16
#define W_CONT_WATER 32
#define W_CONT_FOG 64
#define W_CONT_MISSILECLIP 0x80 /* wolf ranged missile blocking */
#define W_CONT_ITEM 0x100 /* wolf item contents */
#define W_CONT_AI_NOSIGHT 0x1000 /* wolf ai sight blocking */
#define W_CONT_CLIPSHOT 0x2000 /* wolf shot clip */
#define W_CONT_AREAPORTAL 0x8000
#define W_CONT_PLAYERCLIP 0x10000
#define W_CONT_MONSTERCLIP 0x20000
#define W_CONT_TELEPORTER 0x40000
#define W_CONT_JUMPPAD 0x80000
#define W_CONT_CLUSTERPORTAL 0x100000
#define W_CONT_DONOTENTER 0x200000
#define W_CONT_DONOTENTER_LARGE 0x400000 /* wolf dne */
#define W_CONT_ORIGIN 0x1000000 /* removed before bsping an entity */
#define W_CONT_BODY 0x2000000 /* should never be on a brush, only in game */
#define W_CONT_CORPSE 0x4000000
#define W_CONT_DETAIL 0x8000000 /* brushes not used for the bsp */
#define W_CONT_STRUCTURAL 0x10000000 /* brushes used for the bsp */
#define W_CONT_TRANSLUCENT 0x20000000 /* don't consume surface fragments inside */
#define W_CONT_TRIGGER 0x40000000
#define W_CONT_NODROP 0x80000000 /* don't leave bodies or items (death fog, lava) */
#define W_SURF_NODAMAGE 0x1 /* never give falling damage */
#define W_SURF_SLICK 0x2 /* effects game physics */
#define W_SURF_SKY 0x4 /* lighting from environment map */
#define W_SURF_LADDER 0x8
#define W_SURF_NOIMPACT 0x10 /* don't make missile explosions */
#define W_SURF_NOMARKS 0x20 /* don't leave missile marks */
#define W_SURF_CERAMIC 0x40 /* wolf ceramic material */
#define W_SURF_NODRAW 0x80 /* don't generate a drawsurface at all */
#define W_SURF_HINT 0x100 /* make a primary bsp splitter */
#define W_SURF_SKIP 0x200 /* completely ignore, allowing non-closed brushes */
#define W_SURF_NOLIGHTMAP 0x400 /* surface doesn't need a lightmap */
#define W_SURF_POINTLIGHT 0x800 /* generate lighting info at vertexes */
#define W_SURF_METAL 0x1000 /* wolf metal material */
#define W_SURF_NOSTEPS 0x2000 /* no footstep sounds */
#define W_SURF_NONSOLID 0x4000 /* don't collide against curves with this set */
#define W_SURF_LIGHTFILTER 0x8000 /* act as a light filter during q3map -light */
#define W_SURF_ALPHASHADOW 0x10000 /* do per-pixel light shadow casting in q3map */
#define W_SURF_NODLIGHT 0x20000 /* don't dlight even if solid (solid lava, skies) */
#define W_SURF_WOOD 0x40000 /* wolf wood material */
#define W_SURF_GRASS 0x80000 /* wolf grass material */
#define W_SURF_GRAVEL 0x100000 /* wolf gravel material */
#define W_SURF_GLASS 0x200000 /* wolf glass material */
#define W_SURF_SNOW 0x400000 /* wolf snow material */
#define W_SURF_ROOF 0x800000 /* wolf roof material */
#define W_SURF_RUBBLE 0x1000000 /* wolf rubble material */
#define W_SURF_CARPET 0x2000000 /* wolf carpet material */
#define W_SURF_MONSTERSLICK 0x4000000 /* wolf npc slick surface */
#define W_SURF_MONSLICK_W 0x8000000 /* wolf slide bodies west */
#define W_SURF_MONSLICK_N 0x10000000 /* wolf slide bodies north */
#define W_SURF_MONSLICK_E 0x20000000 /* wolf slide bodies east */
#define W_SURF_MONSLICK_S 0x40000000 /* wolf slide bodies south */
/* ydnar flags */
#define W_SURF_VERTEXLIT (W_SURF_POINTLIGHT | W_SURF_NOLIGHTMAP)
/* -------------------------------------------------------------------------------
game_t struct
------------------------------------------------------------------------------- */
{
"wolf", /* -game x */
"main", /* default base game data dir */
".wolf", /* unix home sub-dir */
"wolf", /* magic path word */
"scripts", /* shader directory */
64, /* max lightmapped surface verts */
999, /* max surface verts */
6000, /* max surface indexes */
qfalse, /* flares */
"flareshader", /* default flare shader */
qtrue, /* wolf lighting model? */
128, /* lightmap width/height */
1.0f, /* lightmap gamma */
1.0f, /* lightmap compensate */
"IBSP", /* bsp file prefix */
47, /* bsp file version */
qfalse, /* cod-style lump len/ofs order */
LoadIBSPFile, /* bsp load function */
WriteIBSPFile, /* bsp write function */
{
/* name contentFlags contentFlagsClear surfaceFlags surfaceFlagsClear compileFlags compileFlagsClear */
/* default */
{ "default", W_CONT_SOLID, -1, 0, -1, C_SOLID, -1 },
/* ydnar */
{ "lightgrid", 0, 0, 0, 0, C_LIGHTGRID, 0 },
{ "antiportal", 0, 0, 0, 0, C_ANTIPORTAL, 0 },
{ "skip", 0, 0, 0, 0, C_SKIP, 0 },
/* compiler */
{ "origin", W_CONT_ORIGIN, W_CONT_SOLID, 0, 0, C_ORIGIN | C_TRANSLUCENT, C_SOLID },
{ "areaportal", W_CONT_AREAPORTAL, W_CONT_SOLID, 0, 0, C_AREAPORTAL | C_TRANSLUCENT, C_SOLID },
{ "trans", W_CONT_TRANSLUCENT, 0, 0, 0, C_TRANSLUCENT, 0 },
{ "detail", W_CONT_DETAIL, 0, 0, 0, C_DETAIL, 0 },
{ "structural", W_CONT_STRUCTURAL, 0, 0, 0, C_STRUCTURAL, 0 },
{ "hint", 0, 0, W_SURF_HINT, 0, C_HINT, 0 },
{ "nodraw", 0, 0, W_SURF_NODRAW, 0, C_NODRAW, 0 },
{ "alphashadow", 0, 0, W_SURF_ALPHASHADOW, 0, C_ALPHASHADOW | C_TRANSLUCENT, 0 },
{ "lightfilter", 0, 0, W_SURF_LIGHTFILTER, 0, C_LIGHTFILTER | C_TRANSLUCENT, 0 },
{ "nolightmap", 0, 0, W_SURF_VERTEXLIT, 0, C_VERTEXLIT, 0 },
{ "pointlight", 0, 0, W_SURF_VERTEXLIT, 0, C_VERTEXLIT, 0 },
/* game */
{ "nonsolid", 0, W_CONT_SOLID, W_SURF_NONSOLID, 0, 0, C_SOLID },
{ "trigger", W_CONT_TRIGGER, W_CONT_SOLID, 0, 0, C_TRANSLUCENT, C_SOLID },
{ "water", W_CONT_WATER, W_CONT_SOLID, 0, 0, C_LIQUID | C_TRANSLUCENT, C_SOLID },
{ "slag", W_CONT_SLIME, W_CONT_SOLID, 0, 0, C_LIQUID | C_TRANSLUCENT, C_SOLID },
{ "lava", W_CONT_LAVA, W_CONT_SOLID, 0, 0, C_LIQUID | C_TRANSLUCENT, C_SOLID },
{ "playerclip", W_CONT_PLAYERCLIP, W_CONT_SOLID, 0, 0, C_DETAIL | C_TRANSLUCENT, C_SOLID },
{ "monsterclip", W_CONT_MONSTERCLIP, W_CONT_SOLID, 0, 0, C_DETAIL | C_TRANSLUCENT, C_SOLID },
{ "clipmissile", W_CONT_MISSILECLIP, W_CONT_SOLID, 0, 0, C_DETAIL | C_TRANSLUCENT, C_SOLID },
{ "clipshot", W_CONT_CLIPSHOT, W_CONT_SOLID, 0, 0, C_DETAIL | C_TRANSLUCENT, C_SOLID },
{ "nodrop", W_CONT_NODROP, W_CONT_SOLID, 0, 0, C_TRANSLUCENT, C_SOLID },
{ "clusterportal", W_CONT_CLUSTERPORTAL, W_CONT_SOLID, 0, 0, C_TRANSLUCENT, C_SOLID },
{ "donotenter", W_CONT_DONOTENTER, W_CONT_SOLID, 0, 0, C_TRANSLUCENT, C_SOLID },
{ "nonotenterlarge",W_CONT_DONOTENTER_LARGE, W_CONT_SOLID, 0, 0, C_TRANSLUCENT, C_SOLID },
{ "fog", W_CONT_FOG, W_CONT_SOLID, 0, 0, C_FOG, C_SOLID },
{ "sky", 0, 0, W_SURF_SKY, 0, C_SKY, 0 },
{ "slick", 0, 0, W_SURF_SLICK, 0, 0, 0 },
{ "noimpact", 0, 0, W_SURF_NOIMPACT, 0, 0, 0 },
{ "nomarks", 0, 0, W_SURF_NOMARKS, 0, C_NOMARKS, 0 },
{ "ladder", 0, 0, W_SURF_LADDER, 0, 0, 0 },
{ "nodamage", 0, 0, W_SURF_NODAMAGE, 0, 0, 0 },
{ "nosteps", 0, 0, W_SURF_NOSTEPS, 0, 0, 0 },
{ "nodlight", 0, 0, W_SURF_NODLIGHT, 0, 0, 0 },
/* materials */
{ "metal", 0, 0, W_SURF_METAL, 0, 0, 0 },
{ "metalsteps", 0, 0, W_SURF_METAL, 0, 0, 0 },
{ "glass", 0, 0, W_SURF_GLASS, 0, 0, 0 },
{ "ceramic", 0, 0, W_SURF_CERAMIC, 0, 0, 0 },
{ "woodsteps", 0, 0, W_SURF_WOOD, 0, 0, 0 },
{ "grasssteps", 0, 0, W_SURF_GRASS, 0, 0, 0 },
{ "gravelsteps", 0, 0, W_SURF_GRAVEL, 0, 0, 0 },
{ "rubble", 0, 0, W_SURF_RUBBLE, 0, 0, 0 },
{ "carpetsteps", 0, 0, W_SURF_CARPET, 0, 0, 0 },
{ "snowsteps", 0, 0, W_SURF_SNOW, 0, 0, 0 },
{ "roofsteps", 0, 0, W_SURF_ROOF, 0, 0, 0 },
/* ai */
{ "ai_nosight", W_CONT_AI_NOSIGHT, W_CONT_SOLID, 0, 0, C_TRANSLUCENT, C_SOLID },
/* ydnar: experimental until bits are confirmed! */
{ "ai_nopass", W_CONT_DONOTENTER, W_CONT_SOLID, 0, 0, C_TRANSLUCENT, C_SOLID },
{ "ai_nopasslarge", W_CONT_DONOTENTER_LARGE, W_CONT_SOLID, 0, 0, C_TRANSLUCENT, C_SOLID },
/* sliding bodies */
{ "monsterslick", 0, 0, W_SURF_MONSTERSLICK, 0, C_TRANSLUCENT, 0 },
{ "monsterslicknorth", 0, 0, W_SURF_MONSLICK_N, 0, C_TRANSLUCENT, 0 },
{ "monsterslickeast", 0, 0, W_SURF_MONSLICK_E, 0, C_TRANSLUCENT, 0 },
{ "monsterslicksouth", 0, 0, W_SURF_MONSLICK_S, 0, C_TRANSLUCENT, 0 },
{ "monsterslickwest", 0, 0, W_SURF_MONSLICK_W, 0, C_TRANSLUCENT, 0 },
/* null */
{ NULL, 0, 0, 0, 0, 0, 0 }
}
}
/* end marker */
#endif

View File

@@ -0,0 +1,177 @@
/* -------------------------------------------------------------------------------
Copyright (C) 1999-2006 Id Software, Inc. and contributors.
For a list of contributors, see the accompanying CONTRIBUTORS file.
This file is part of GtkRadiant.
GtkRadiant 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.
GtkRadiant 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 GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
----------------------------------------------------------------------------------
Support for Wolfenstein: Enemy Territory by ydnar@splashdamage.com
------------------------------------------------------------------------------- */
/* marker */
#ifndef GAME_WOLFET_H
#define GAME_WOLFET_H
/* -------------------------------------------------------------------------------
content and surface flags
------------------------------------------------------------------------------- */
/* this file must be included *after* game_wolf.h because it shares defines! */
#define W_SURF_SPLASH 0x00000040 /* enemy territory water splash surface */
#define W_SURF_LANDMINE 0x80000000 /* enemy territory 'landminable' surface */
/* -------------------------------------------------------------------------------
game_t struct
------------------------------------------------------------------------------- */
{
"et", /* -game x */
"etmain", /* default base game data dir */
".etwolf", /* unix home sub-dir */
"et", /* magic path word */
"scripts", /* shader directory */
1024, /* max lightmapped surface verts */
1024, /* max surface verts */
6144, /* max surface indexes */
qfalse, /* flares */
"flareshader", /* default flare shader */
qtrue, /* wolf lighting model? */
128, /* lightmap width/height */
1.0f, /* lightmap gamma */
1.0f, /* lightmap compensate */
"IBSP", /* bsp file prefix */
47, /* bsp file version */
qfalse, /* cod-style lump len/ofs order */
LoadIBSPFile, /* bsp load function */
WriteIBSPFile, /* bsp write function */
{
/* name contentFlags contentFlagsClear surfaceFlags surfaceFlagsClear compileFlags compileFlagsClear */
/* default */
{ "default", W_CONT_SOLID, -1, 0, -1, C_SOLID, -1 },
/* ydnar */
{ "lightgrid", 0, 0, 0, 0, C_LIGHTGRID, 0 },
{ "antiportal", 0, 0, 0, 0, C_ANTIPORTAL, 0 },
{ "skip", 0, 0, 0, 0, C_SKIP, 0 },
/* compiler */
{ "origin", W_CONT_ORIGIN, W_CONT_SOLID, 0, 0, C_ORIGIN | C_TRANSLUCENT, C_SOLID },
{ "areaportal", W_CONT_AREAPORTAL, W_CONT_SOLID, 0, 0, C_AREAPORTAL | C_TRANSLUCENT, C_SOLID },
{ "trans", W_CONT_TRANSLUCENT, 0, 0, 0, C_TRANSLUCENT, 0 },
{ "detail", W_CONT_DETAIL, 0, 0, 0, C_DETAIL, 0 },
{ "structural", W_CONT_STRUCTURAL, 0, 0, 0, C_STRUCTURAL, 0 },
{ "hint", 0, 0, W_SURF_HINT, 0, C_HINT, 0 },
{ "nodraw", 0, 0, W_SURF_NODRAW, 0, C_NODRAW, 0 },
{ "alphashadow", 0, 0, W_SURF_ALPHASHADOW, 0, C_ALPHASHADOW | C_TRANSLUCENT, 0 },
{ "lightfilter", 0, 0, W_SURF_LIGHTFILTER, 0, C_LIGHTFILTER | C_TRANSLUCENT, 0 },
{ "nolightmap", 0, 0, W_SURF_VERTEXLIT, 0, C_VERTEXLIT, 0 },
{ "pointlight", 0, 0, W_SURF_VERTEXLIT, 0, C_VERTEXLIT, 0 },
/* game */
{ "nonsolid", 0, W_CONT_SOLID, W_SURF_NONSOLID, 0, 0, C_SOLID },
{ "trigger", W_CONT_TRIGGER, W_CONT_SOLID, 0, 0, C_TRANSLUCENT, C_SOLID },
{ "water", W_CONT_WATER, W_CONT_SOLID, 0, 0, C_LIQUID | C_TRANSLUCENT, C_SOLID },
{ "slag", W_CONT_SLIME, W_CONT_SOLID, 0, 0, C_LIQUID | C_TRANSLUCENT, C_SOLID },
{ "lava", W_CONT_LAVA, W_CONT_SOLID, 0, 0, C_LIQUID | C_TRANSLUCENT, C_SOLID },
{ "playerclip", W_CONT_PLAYERCLIP, W_CONT_SOLID, 0, 0, C_DETAIL | C_TRANSLUCENT, C_SOLID },
{ "monsterclip", W_CONT_MONSTERCLIP, W_CONT_SOLID, 0, 0, C_DETAIL | C_TRANSLUCENT, C_SOLID },
{ "clipmissile", W_CONT_MISSILECLIP, W_CONT_SOLID, 0, 0, C_DETAIL | C_TRANSLUCENT, C_SOLID },
{ "clipshot", W_CONT_CLIPSHOT, W_CONT_SOLID, 0, 0, C_DETAIL | C_TRANSLUCENT, C_SOLID },
{ "nodrop", W_CONT_NODROP, W_CONT_SOLID, 0, 0, C_TRANSLUCENT, C_SOLID },
{ "clusterportal", W_CONT_CLUSTERPORTAL, W_CONT_SOLID, 0, 0, C_TRANSLUCENT, C_SOLID },
{ "donotenter", W_CONT_DONOTENTER, W_CONT_SOLID, 0, 0, C_TRANSLUCENT, C_SOLID },
{ "nonotenterlarge",W_CONT_DONOTENTER_LARGE, W_CONT_SOLID, 0, 0, C_TRANSLUCENT, C_SOLID },
{ "fog", W_CONT_FOG, W_CONT_SOLID, 0, 0, C_FOG, C_SOLID },
{ "sky", 0, 0, W_SURF_SKY, 0, C_SKY, 0 },
{ "slick", 0, 0, W_SURF_SLICK, 0, 0, 0 },
{ "noimpact", 0, 0, W_SURF_NOIMPACT, 0, 0, 0 },
{ "nomarks", 0, 0, W_SURF_NOMARKS, 0, C_NOMARKS, 0 },
{ "ladder", 0, 0, W_SURF_LADDER, 0, 0, 0 },
{ "nodamage", 0, 0, W_SURF_NODAMAGE, 0, 0, 0 },
{ "nosteps", 0, 0, W_SURF_NOSTEPS, 0, 0, 0 },
{ "nodlight", 0, 0, W_SURF_NODLIGHT, 0, 0, 0 },
/* wolf et landmine-able surface */
{ "landmine", 0, 0, W_SURF_LANDMINE, 0, 0, 0 },
/* materials */
{ "metal", 0, 0, W_SURF_METAL, 0, 0, 0 },
{ "metalsteps", 0, 0, W_SURF_METAL, 0, 0, 0 },
{ "glass", 0, 0, W_SURF_GLASS, 0, 0, 0 },
{ "splash", 0, 0, W_SURF_SPLASH, 0, 0, 0 },
{ "woodsteps", 0, 0, W_SURF_WOOD, 0, 0, 0 },
{ "grasssteps", 0, 0, W_SURF_GRASS, 0, 0, 0 },
{ "gravelsteps", 0, 0, W_SURF_GRAVEL, 0, 0, 0 },
{ "rubble", 0, 0, W_SURF_RUBBLE, 0, 0, 0 },
{ "carpetsteps", 0, 0, W_SURF_CARPET, 0, 0, 0 },
{ "snowsteps", 0, 0, W_SURF_SNOW, 0, 0, 0 },
{ "roofsteps", 0, 0, W_SURF_ROOF, 0, 0, 0 },
/* ai */
{ "ai_nosight", W_CONT_AI_NOSIGHT, W_CONT_SOLID, 0, 0, C_TRANSLUCENT, C_SOLID },
/* ydnar: experimental until bits are confirmed! */
{ "ai_nopass", W_CONT_DONOTENTER, W_CONT_SOLID, 0, 0, C_TRANSLUCENT, C_SOLID },
{ "ai_nopasslarge", W_CONT_DONOTENTER_LARGE, W_CONT_SOLID, 0, 0, C_TRANSLUCENT, C_SOLID },
/* sliding bodies */
{ "monsterslick", 0, 0, W_SURF_MONSTERSLICK, 0, C_TRANSLUCENT, 0 },
{ "monsterslicknorth", 0, 0, W_SURF_MONSLICK_N, 0, C_TRANSLUCENT, 0 },
{ "monsterslickeast", 0, 0, W_SURF_MONSLICK_E, 0, C_TRANSLUCENT, 0 },
{ "monsterslicksouth", 0, 0, W_SURF_MONSLICK_S, 0, C_TRANSLUCENT, 0 },
{ "monsterslickwest", 0, 0, W_SURF_MONSLICK_W, 0, C_TRANSLUCENT, 0 },
/* null */
{ NULL, 0, 0, 0, 0, 0, 0 }
}
}
/* end marker */
#endif

468
tools/quake3/q3map2/image.c Normal file
View File

@@ -0,0 +1,468 @@
/* -------------------------------------------------------------------------------
Copyright (C) 1999-2006 Id Software, Inc. and contributors.
For a list of contributors, see the accompanying CONTRIBUTORS file.
This file is part of GtkRadiant.
GtkRadiant 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.
GtkRadiant 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 GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
----------------------------------------------------------------------------------
This code has been altered significantly from its original form, to support
several games based on the Quake III Arena engine, in the form of "Q3Map2."
------------------------------------------------------------------------------- */
/* marker */
#define IMAGE_C
/* dependencies */
#include "q3map2.h"
/* -------------------------------------------------------------------------------
this file contains image pool management with reference counting. note: it isn't
reentrant, so only call it from init/shutdown code or wrap calls in a mutex
------------------------------------------------------------------------------- */
/*
LoadDDSBuffer()
loads a dxtc (1, 3, 5) dds buffer into a valid rgba image
*/
static void LoadDDSBuffer( byte *buffer, int size, byte **pixels, int *width, int *height )
{
int w, h;
ddsPF_t pf;
/* dummy check */
if( buffer == NULL || size <= 0 || pixels == NULL || width == NULL || height == NULL )
return;
/* null out */
*pixels = 0;
*width = 0;
*height = 0;
/* get dds info */
if( DDSGetInfo( (ddsBuffer_t*) buffer, &w, &h, &pf ) )
{
Sys_Printf( "WARNING: Invalid DDS texture\n" );
return;
}
/* only certain types of dds textures are supported */
if( pf != DDS_PF_ARGB8888 && pf != DDS_PF_DXT1 && pf != DDS_PF_DXT3 && pf != DDS_PF_DXT5 )
{
Sys_Printf( "WARNING: Only DDS texture formats ARGB8888, DXT1, DXT3, and DXT5 are supported (%d)\n", pf );
return;
}
/* create image pixel buffer */
*width = w;
*height = h;
*pixels = safe_malloc( w * h * 4 );
/* decompress the dds texture */
DDSDecompress( (ddsBuffer_t*) buffer, *pixels );
}
/*
PNGReadData()
callback function for libpng to read from a memory buffer
note: this function is a total hack, as it reads/writes the png struct directly!
*/
typedef struct pngBuffer_s
{
byte *buffer;
int size, offset;
}
pngBuffer_t;
void PNGReadData( png_struct *png, png_byte *buffer, png_size_t size )
{
pngBuffer_t *pb = (pngBuffer_t*) png_get_io_ptr( png );
if( (pb->offset + size) > pb->size )
size = (pb->size - pb->offset);
memcpy( buffer, &pb->buffer[ pb->offset ], size );
pb->offset += size;
//% Sys_Printf( "Copying %d bytes from 0x%08X to 0x%08X (offset: %d of %d)\n", size, &pb->buffer[ pb->offset ], buffer, pb->offset, pb->size );
}
/*
LoadPNGBuffer()
loads a png file buffer into a valid rgba image
*/
static void LoadPNGBuffer( byte *buffer, int size, byte **pixels, int *width, int *height )
{
png_struct *png;
png_info *info, *end;
pngBuffer_t pb;
int i, bitDepth, colorType, channels;
png_uint_32 w, h;
byte **rowPointers;
/* dummy check */
if( buffer == NULL || size <= 0 || pixels == NULL || width == NULL || height == NULL )
return;
/* null out */
*pixels = 0;
*width = 0;
*height = 0;
/* determine if this is a png file */
if( png_sig_cmp( buffer, 0, 8 ) != 0 )
{
Sys_Printf( "WARNING: Invalid PNG file\n" );
return;
}
/* create png structs */
png = png_create_read_struct( PNG_LIBPNG_VER_STRING, NULL, NULL, NULL );
if( png == NULL )
{
Sys_Printf( "WARNING: Unable to create PNG read struct\n" );
return;
}
info = png_create_info_struct( png );
if( info == NULL )
{
Sys_Printf( "WARNING: Unable to create PNG info struct\n" );
png_destroy_read_struct( &png, NULL, NULL );
return;
}
end = png_create_info_struct( png );
if( end == NULL )
{
Sys_Printf( "WARNING: Unable to create PNG end info struct\n" );
png_destroy_read_struct( &png, &info, NULL );
return;
}
/* set read callback */
pb.buffer = buffer;
pb.size = size;
pb.offset = 0;
png_set_read_fn( png, &pb, PNGReadData );
png->io_ptr = &pb; /* hack! */
/* set error longjmp */
if( setjmp( png->jmpbuf ) )
{
Sys_Printf( "WARNING: An error occurred reading PNG image\n" );
png_destroy_read_struct( &png, &info, &end );
return;
}
/* fixme: add proper i/o stuff here */
/* read png info */
png_read_info( png, info );
/* read image header chunk */
png_get_IHDR( png, info,
&w, &h, &bitDepth, &colorType, NULL, NULL, NULL );
/* read number of channels */
channels = png_get_channels( png, info );
/* the following will probably bork on certain types of png images, but hey... */
/* force indexed/gray/trans chunk to rgb */
if( (colorType == PNG_COLOR_TYPE_PALETTE && bitDepth <= 8) ||
(colorType == PNG_COLOR_TYPE_GRAY && bitDepth <= 8) ||
png_get_valid( png, info, PNG_INFO_tRNS ) )
png_set_expand( png );
/* strip 16bpc -> 8bpc */
if( bitDepth == 16 )
png_set_strip_16( png );
/* pad rgb to rgba */
if( bitDepth == 8 && colorType == PNG_COLOR_TYPE_RGB)
png_set_filler( png, 255, PNG_FILLER_AFTER );
/* create image pixel buffer */
*width = w;
*height = h;
*pixels = safe_malloc( w * h * 4 );
/* create row pointers */
rowPointers = safe_malloc( h * sizeof( byte* ) );
for( i = 0; i < h; i++ )
rowPointers[ i ] = *pixels + (i * w * 4);
/* read the png */
png_read_image( png, rowPointers );
/* clean up */
free( rowPointers );
png_destroy_read_struct( &png, &info, &end );
}
/*
ImageInit()
implicitly called by every function to set up image list
*/
static void ImageInit( void )
{
int i;
if( numImages <= 0 )
{
/* clear images (fixme: this could theoretically leak) */
memset( images, 0, sizeof( images ) );
/* generate *bogus image */
images[ 0 ].name = safe_malloc( strlen( DEFAULT_IMAGE ) + 1 );
strcpy( images[ 0 ].name, DEFAULT_IMAGE );
images[ 0 ].filename = safe_malloc( strlen( DEFAULT_IMAGE ) + 1 );
strcpy( images[ 0 ].filename, DEFAULT_IMAGE );
images[ 0 ].width = 64;
images[ 0 ].height = 64;
images[ 0 ].refCount = 1;
images[ 0 ].pixels = safe_malloc( 64 * 64 * 4 );
for( i = 0; i < (64 * 64 * 4); i++ )
images[ 0 ].pixels[ i ] = 255;
}
}
/*
ImageFree()
frees an rgba image
*/
void ImageFree( image_t *image )
{
/* dummy check */
if( image == NULL )
return;
/* decrement refcount */
image->refCount--;
/* free? */
if( image->refCount <= 0 )
{
if( image->name != NULL )
free( image->name );
image->name = NULL;
if( image->filename != NULL )
free( image->filename );
image->filename = NULL;
free( image->pixels );
image->width = 0;
image->height = 0;
numImages--;
}
}
/*
ImageFind()
finds an existing rgba image and returns a pointer to the image_t struct or NULL if not found
*/
image_t *ImageFind( const char *filename )
{
int i;
char name[ 1024 ];
/* init */
ImageInit();
/* dummy check */
if( filename == NULL || filename[ 0 ] == '\0' )
return NULL;
/* strip file extension off name */
strcpy( name, filename );
StripExtension( name );
/* search list */
for( i = 0; i < MAX_IMAGES; i++ )
{
if( images[ i ].name != NULL && !strcmp( name, images[ i ].name ) )
return &images[ i ];
}
/* no matching image found */
return NULL;
}
/*
ImageLoad()
loads an rgba image and returns a pointer to the image_t struct or NULL if not found
*/
image_t *ImageLoad( const char *filename )
{
int i;
image_t *image;
char name[ 1024 ];
int size;
byte *buffer = NULL;
/* init */
ImageInit();
/* dummy check */
if( filename == NULL || filename[ 0 ] == '\0' )
return NULL;
/* strip file extension off name */
strcpy( name, filename );
StripExtension( name );
/* try to find existing image */
image = ImageFind( name );
if( image != NULL )
{
image->refCount++;
return image;
}
/* none found, so find first non-null image */
image = NULL;
for( i = 0; i < MAX_IMAGES; i++ )
{
if( images[ i ].name == NULL )
{
image = &images[ i ];
break;
}
}
/* too many images? */
if( image == NULL )
Error( "MAX_IMAGES (%d) exceeded, there are too many image files referenced by the map.", MAX_IMAGES );
/* set it up */
image->name = safe_malloc( strlen( name ) + 1 );
strcpy( image->name, name );
/* attempt to load tga */
StripExtension( name );
strcat( name, ".tga" );
size = vfsLoadFile( (const char*) name, (void**) &buffer, 0 );
if( size > 0 )
LoadTGABuffer( buffer, buffer + size, &image->pixels, &image->width, &image->height );
else
{
/* attempt to load png */
StripExtension( name );
strcat( name, ".png" );
size = vfsLoadFile( (const char*) name, (void**) &buffer, 0 );
if( size > 0 )
LoadPNGBuffer( buffer, size, &image->pixels, &image->width, &image->height );
else
{
/* attempt to load jpg */
StripExtension( name );
strcat( name, ".jpg" );
size = vfsLoadFile( (const char*) name, (void**) &buffer, 0 );
if( size > 0 )
{
if( LoadJPGBuff( buffer, size, &image->pixels, &image->width, &image->height ) == -1 && image->pixels != NULL )
Sys_Printf( "WARNING: LoadJPGBuff: %s\n", (unsigned char*) image->pixels );
}
else
{
/* attempt to load dds */
StripExtension( name );
strcat( name, ".dds" );
size = vfsLoadFile( (const char*) name, (void**) &buffer, 0 );
if( size > 0 )
{
LoadDDSBuffer( buffer, size, &image->pixels, &image->width, &image->height );
/* debug code */
#if 1
{
ddsPF_t pf;
DDSGetInfo( (ddsBuffer_t*) buffer, NULL, NULL, &pf );
Sys_Printf( "pf = %d\n", pf );
if( image->width > 0 )
{
StripExtension( name );
strcat( name, "_converted.tga" );
WriteTGA( "C:\\games\\quake3\\baseq3\\textures\\rad\\dds_converted.tga", image->pixels, image->width, image->height );
}
}
#endif
}
}
}
}
/* free file buffer */
free( buffer );
/* make sure everything's kosher */
if( size <= 0 || image->width <= 0 || image->height <= 0 || image->pixels == NULL )
{
//% Sys_Printf( "size = %d width = %d height = %d pixels = 0x%08x (%s)\n",
//% size, image->width, image->height, image->pixels, name );
free( image->name );
image->name = NULL;
return NULL;
}
/* set filename */
image->filename = safe_malloc( strlen( name ) + 1 );
strcpy( image->filename, name );
/* set count */
image->refCount = 1;
numImages++;
/* return the image */
return image;
}

View File

@@ -0,0 +1,127 @@
/* -------------------------------------------------------------------------------
Copyright (C) 1999-2006 Id Software, Inc. and contributors.
For a list of contributors, see the accompanying CONTRIBUTORS file.
This file is part of GtkRadiant.
GtkRadiant 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.
GtkRadiant 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 GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
----------------------------------------------------------------------------------
This code has been altered significantly from its original form, to support
several games based on the Quake III Arena engine, in the form of "Q3Map2."
------------------------------------------------------------------------------- */
/* marker */
#define LEAKFILE_C
/* dependencies */
#include "q3map2.h"
/*
==============================================================================
LEAK 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
TTimo: builds a polyline xml node
=============
*/
xmlNodePtr LeakFile (tree_t *tree)
{
vec3_t mid;
FILE *linefile;
char filename[1024];
node_t *node;
int count;
xmlNodePtr xml_node, point;
if (!tree->outside_node.occupied)
return NULL;
Sys_FPrintf (SYS_VRB,"--- 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);
xml_node = xmlNewNode (NULL, "polyline");
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]);
point = xml_NodeForVec(mid);
xmlAddChild(xml_node, point);
count++;
}
// add the occupant center
GetVectorForKey (node->occupant, "origin", mid);
fprintf (linefile, "%f %f %f\n", mid[0], mid[1], mid[2]);
point = xml_NodeForVec(mid);
xmlAddChild(xml_node, point);
Sys_FPrintf( SYS_VRB, "%9d point linefile\n", count+1);
fclose (linefile);
return xml_node;
}

2305
tools/quake3/q3map2/light.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,955 @@
/* -------------------------------------------------------------------------------
Copyright (C) 1999-2006 Id Software, Inc. and contributors.
For a list of contributors, see the accompanying CONTRIBUTORS file.
This file is part of GtkRadiant.
GtkRadiant 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.
GtkRadiant 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 GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
----------------------------------------------------------------------------------
This code has been altered significantly from its original form, to support
several games based on the Quake III Arena engine, in the form of "Q3Map2."
------------------------------------------------------------------------------- */
/* marker */
#define LIGHT_BOUNCE_C
/* dependencies */
#include "q3map2.h"
/* functions */
/*
RadFreeLights()
deletes any existing lights, freeing up memory for the next bounce
*/
void RadFreeLights( void )
{
light_t *light, *next;
/* delete lights */
for( light = lights; light; light = next )
{
next = light->next;
if( light->w != NULL )
FreeWinding( light->w );
free( light );
}
numLights = 0;
lights = NULL;
}
/*
RadClipWindingEpsilon()
clips a rad winding by a plane
based off the regular clip winding code
*/
static void RadClipWindingEpsilon( radWinding_t *in, vec3_t normal, vec_t dist,
vec_t epsilon, radWinding_t *front, radWinding_t *back, clipWork_t *cw )
{
vec_t *dists;
int *sides;
int counts[ 3 ];
vec_t dot; /* ydnar: changed from static b/c of threading */ /* VC 4.2 optimizer bug if not static? */
int i, j, k;
radVert_t *v1, *v2, mid;
int maxPoints;
/* crutch */
dists = cw->dists;
sides = cw->sides;
/* clear counts */
counts[ 0 ] = counts[ 1 ] = counts[ 2 ] = 0;
/* determine sides for each point */
for( i = 0; i < in->numVerts; i++ )
{
dot = DotProduct( in->verts[ i ].xyz, normal );
dot -= dist;
dists[ i ] = dot;
if( dot > epsilon )
sides[ i ] = SIDE_FRONT;
else if( dot < -epsilon )
sides[ i ] = SIDE_BACK;
else
sides[ i ] = SIDE_ON;
counts[ sides[ i ] ]++;
}
sides[ i ] = sides[ 0 ];
dists[ i ] = dists[ 0 ];
/* clear front and back */
front->numVerts = back->numVerts = 0;
/* handle all on one side cases */
if( counts[ 0 ] == 0 )
{
memcpy( back, in, sizeof( radWinding_t ) );
return;
}
if( counts[ 1 ] == 0 )
{
memcpy( front, in, sizeof( radWinding_t ) );
return;
}
/* setup windings */
maxPoints = in->numVerts + 4;
/* do individual verts */
for( i = 0; i < in->numVerts; i++ )
{
/* do simple vertex copies first */
v1 = &in->verts[ i ];
if( sides[ i ] == SIDE_ON )
{
memcpy( &front->verts[ front->numVerts++ ], v1, sizeof( radVert_t ) );
memcpy( &back->verts[ back->numVerts++ ], v1, sizeof( radVert_t ) );
continue;
}
if( sides[ i ] == SIDE_FRONT )
memcpy( &front->verts[ front->numVerts++ ], v1, sizeof( radVert_t ) );
if( sides[ i ] == SIDE_BACK )
memcpy( &back->verts[ back->numVerts++ ], v1, sizeof( radVert_t ) );
if( sides[ i + 1 ] == SIDE_ON || sides[ i + 1 ] == sides[ i ] )
continue;
/* generate a split vertex */
v2 = &in->verts[ (i + 1) % in->numVerts ];
dot = dists[ i ] / (dists[ i ] - dists[ i + 1 ]);
/* average vertex values */
for( j = 0; j < 4; j++ )
{
/* color */
if( j < 4 )
{
for( k = 0; k < MAX_LIGHTMAPS; k++ )
mid.color[ k ][ j ] = v1->color[ k ][ j ] + dot * (v2->color[ k ][ j ] - v1->color[ k ][ j ]);
}
/* xyz, normal */
if( j < 3 )
{
mid.xyz[ j ] = v1->xyz[ j ] + dot * (v2->xyz[ j ] - v1->xyz[ j ]);
mid.normal[ j ] = v1->normal[ j ] + dot * (v2->normal[ j ] - v1->normal[ j ]);
}
/* st, lightmap */
if( j < 2 )
{
mid.st[ j ] = v1->st[ j ] + dot * (v2->st[ j ] - v1->st[ j ]);
for( k = 0; k < MAX_LIGHTMAPS; k++ )
mid.lightmap[ k ][ j ] = v1->lightmap[ k ][ j ] + dot * (v2->lightmap[ k ][ j ] - v1->lightmap[ k ][ j ]);
}
}
/* normalize the averaged normal */
VectorNormalize( mid.normal, mid.normal );
/* copy the midpoint to both windings */
memcpy( &front->verts[ front->numVerts++ ], &mid, sizeof( radVert_t ) );
memcpy( &back->verts[ back->numVerts++ ], &mid, sizeof( radVert_t ) );
}
/* error check */
if( front->numVerts > maxPoints || front->numVerts > maxPoints )
Error( "RadClipWindingEpsilon: points exceeded estimate" );
if( front->numVerts > MAX_POINTS_ON_WINDING || front->numVerts > MAX_POINTS_ON_WINDING )
Error( "RadClipWindingEpsilon: MAX_POINTS_ON_WINDING" );
}
/*
RadSampleImage()
samples a texture image for a given color
returns qfalse if pixels are bad
*/
qboolean RadSampleImage( byte *pixels, int width, int height, float st[ 2 ], float color[ 4 ] )
{
float sto[ 2 ];
int x, y;
/* clear color first */
color[ 0 ] = color[ 1 ] = color[ 2 ] = color[ 3 ] = 255;
/* dummy check */
if( pixels == NULL || width < 1 || height < 1 )
return qfalse;
/* bias st */
sto[ 0 ] = st[ 0 ];
while( sto[ 0 ] < 0.0f )
sto[ 0 ] += 1.0f;
sto[ 1 ] = st[ 1 ];
while( sto[ 1 ] < 0.0f )
sto[ 1 ] += 1.0f;
/* get offsets */
x = ((float) width * sto[ 0 ]) + 0.5f;
x %= width;
y = ((float) height * sto[ 1 ]) + 0.5f;
y %= height;
/* get pixel */
pixels += (y * width * 4) + (x * 4);
VectorCopy( pixels, color );
color[ 3 ] = pixels[ 3 ];
return qtrue;
}
/*
RadSample()
samples a fragment's lightmap or vertex color and returns an
average color and a color gradient for the sample
*/
#define MAX_SAMPLES 150
#define SAMPLE_GRANULARITY 6
static void RadSample( int lightmapNum, bspDrawSurface_t *ds, rawLightmap_t *lm, shaderInfo_t *si, radWinding_t *rw, vec3_t average, vec3_t gradient, int *style )
{
int i, j, k, l, v, x, y, samples;
vec3_t color, mins, maxs;
vec4_t textureColor;
float alpha, alphaI, bf;
vec3_t blend;
float st[ 2 ], lightmap[ 2 ], *radLuxel;
radVert_t *rv[ 3 ];
/* initial setup */
ClearBounds( mins, maxs );
VectorClear( average );
VectorClear( gradient );
alpha = 0;
/* dummy check */
if( rw == NULL || rw->numVerts < 3 )
return;
/* start sampling */
samples = 0;
/* sample vertex colors if no lightmap or this is the initial pass */
if( lm == NULL || lm->radLuxels[ lightmapNum ] == NULL || bouncing == qfalse )
{
for( samples = 0; samples < rw->numVerts; samples++ )
{
/* multiply by texture color */
if( !RadSampleImage( si->lightImage->pixels, si->lightImage->width, si->lightImage->height, rw->verts[ samples ].st, textureColor ) )
{
VectorCopy( si->averageColor, textureColor );
textureColor[ 4 ] = 255.0f;
}
for( i = 0; i < 3; i++ )
color[ i ] = (textureColor[ i ] / 255) * (rw->verts[ samples ].color[ lightmapNum ][ i ] / 255.0f);
AddPointToBounds( color, mins, maxs );
VectorAdd( average, color, average );
/* get alpha */
alpha += (textureColor[ 3 ] / 255.0f) * (rw->verts[ samples ].color[ lightmapNum ][ 3 ] / 255.0f);
}
/* set style */
*style = ds->vertexStyles[ lightmapNum ];
}
/* sample lightmap */
else
{
/* fracture the winding into a fan (including degenerate tris) */
for( v = 1; v < (rw->numVerts - 1) && samples < MAX_SAMPLES; v++ )
{
/* get a triangle */
rv[ 0 ] = &rw->verts[ 0 ];
rv[ 1 ] = &rw->verts[ v ];
rv[ 2 ] = &rw->verts[ v + 1 ];
/* this code is embarassing (really should just rasterize the triangle) */
for( i = 1; i < SAMPLE_GRANULARITY && samples < MAX_SAMPLES; i++ )
{
for( j = 1; j < SAMPLE_GRANULARITY && samples < MAX_SAMPLES; j++ )
{
for( k = 1; k < SAMPLE_GRANULARITY && samples < MAX_SAMPLES; k++ )
{
/* create a blend vector (barycentric coordinates) */
blend[ 0 ] = i;
blend[ 1 ] = j;
blend[ 2 ] = k;
bf = (1.0 / (blend[ 0 ] + blend[ 1 ] + blend[ 2 ]));
VectorScale( blend, bf, blend );
/* create a blended sample */
st[ 0 ] = st[ 1 ] = 0.0f;
lightmap[ 0 ] = lightmap[ 1 ] = 0.0f;
alphaI = 0.0f;
for( l = 0; l < 3; l++ )
{
st[ 0 ] += (rv[ l ]->st[ 0 ] * blend[ l ]);
st[ 1 ] += (rv[ l ]->st[ 1 ] * blend[ l ]);
lightmap[ 0 ] += (rv[ l ]->lightmap[ lightmapNum ][ 0 ] * blend[ l ]);
lightmap[ 1 ] += (rv[ l ]->lightmap[ lightmapNum ][ 1 ] * blend[ l ]);
alphaI += (rv[ l ]->color[ lightmapNum ][ 3 ] * blend[ l ]);
}
/* get lightmap xy coords */
x = lightmap[ 0 ] / (float) superSample;
y = lightmap[ 1 ] / (float) superSample;
if( x < 0 )
x = 0;
else if ( x >= lm->w )
x = lm->w - 1;
if( y < 0 )
y = 0;
else if ( y >= lm->h )
y = lm->h - 1;
/* get radiosity luxel */
radLuxel = RAD_LUXEL( lightmapNum, x, y );
/* ignore unlit/unused luxels */
if( radLuxel[ 0 ] < 0.0f )
continue;
/* inc samples */
samples++;
/* multiply by texture color */
if( !RadSampleImage( si->lightImage->pixels, si->lightImage->width, si->lightImage->height, st, textureColor ) )
{
VectorCopy( si->averageColor, textureColor );
textureColor[ 4 ] = 255;
}
for( i = 0; i < 3; i++ )
color[ i ] = (textureColor[ i ] / 255) * (radLuxel[ i ] / 255);
AddPointToBounds( color, mins, maxs );
VectorAdd( average, color, average );
/* get alpha */
alpha += (textureColor[ 3 ] / 255) * (alphaI / 255);
}
}
}
}
/* set style */
*style = ds->lightmapStyles[ lightmapNum ];
}
/* any samples? */
if( samples <= 0 )
return;
/* average the color */
VectorScale( average, (1.0 / samples), average );
/* create the color gradient */
//% VectorSubtract( maxs, mins, delta );
/* new: color gradient will always be 0-1.0, expressed as the range of light relative to overall light */
//% gradient[ 0 ] = maxs[ 0 ] > 0.0f ? (maxs[ 0 ] - mins[ 0 ]) / maxs[ 0 ] : 0.0f;
//% gradient[ 1 ] = maxs[ 1 ] > 0.0f ? (maxs[ 1 ] - mins[ 1 ]) / maxs[ 1 ] : 0.0f;
//% gradient[ 2 ] = maxs[ 2 ] > 0.0f ? (maxs[ 2 ] - mins[ 2 ]) / maxs[ 2 ] : 0.0f;
/* newer: another contrast function */
for( i = 0; i < 3; i++ )
gradient[ i ] = (maxs[ i ] - mins[ i ]) * maxs[ i ];
}
/*
RadSubdivideDiffuseLight()
subdivides a radiosity winding until it is smaller than subdivide, then generates an area light
*/
#define RADIOSITY_MAX_GRADIENT 0.75f //% 0.25f
#define RADIOSITY_VALUE 500.0f
#define RADIOSITY_MIN 0.0001f
#define RADIOSITY_CLIP_EPSILON 0.125f
static void RadSubdivideDiffuseLight( int lightmapNum, bspDrawSurface_t *ds, rawLightmap_t *lm, shaderInfo_t *si,
float scale, float subdivide, qboolean original, radWinding_t *rw, clipWork_t *cw )
{
int i, style;
float dist, area, value;
vec3_t mins, maxs, normal, d1, d2, cross, color, gradient;
light_t *light, *splash;
winding_t *w;
/* dummy check */
if( rw == NULL || rw->numVerts < 3 )
return;
/* get bounds for winding */
ClearBounds( mins, maxs );
for( i = 0; i < rw->numVerts; i++ )
AddPointToBounds( rw->verts[ i ].xyz, mins, maxs );
/* subdivide if necessary */
for( i = 0; i < 3; i++ )
{
if( maxs[ i ] - mins[ i ] > subdivide )
{
radWinding_t front, back;
/* make axial plane */
VectorClear( normal );
normal[ i ] = 1;
dist = (maxs[ i ] + mins[ i ]) * 0.5f;
/* clip the winding */
RadClipWindingEpsilon( rw, normal, dist, RADIOSITY_CLIP_EPSILON, &front, &back, cw );
/* recurse */
RadSubdivideDiffuseLight( lightmapNum, ds, lm, si, scale, subdivide, qfalse, &front, cw );
RadSubdivideDiffuseLight( lightmapNum, ds, lm, si, scale, subdivide, qfalse, &back, cw );
return;
}
}
/* check area */
area = 0.0f;
for( i = 2; i < rw->numVerts; i++ )
{
VectorSubtract( rw->verts[ i - 1 ].xyz, rw->verts[ 0 ].xyz, d1 );
VectorSubtract( rw->verts[ i ].xyz, rw->verts[ 0 ].xyz, d2 );
CrossProduct( d1, d2, cross );
area += 0.5f * VectorLength( cross );
}
if( area < 1.0f || area > 20000000.0f )
return;
/* more subdivision may be necessary */
if( bouncing )
{
/* get color sample for the surface fragment */
RadSample( lightmapNum, ds, lm, si, rw, color, gradient, &style );
/* if color gradient is too high, subdivide again */
if( subdivide > minDiffuseSubdivide &&
(gradient[ 0 ] > RADIOSITY_MAX_GRADIENT || gradient[ 1 ] > RADIOSITY_MAX_GRADIENT || gradient[ 2 ] > RADIOSITY_MAX_GRADIENT) )
{
RadSubdivideDiffuseLight( lightmapNum, ds, lm, si, scale, (subdivide / 2.0f), qfalse, rw, cw );
return;
}
}
/* create a regular winding and an average normal */
w = AllocWinding( rw->numVerts );
w->numpoints = rw->numVerts;
VectorClear( normal );
for( i = 0; i < rw->numVerts; i++ )
{
VectorCopy( rw->verts[ i ].xyz, w->p[ i ] );
VectorAdd( normal, rw->verts[ i ].normal, normal );
}
VectorScale( normal, (1.0f / rw->numVerts), normal );
if( VectorNormalize( normal, normal ) == 0.0f )
return;
/* early out? */
if( bouncing && VectorLength( color ) < RADIOSITY_MIN )
return;
/* debug code */
//% Sys_Printf( "Size: %d %d %d\n", (int) (maxs[ 0 ] - mins[ 0 ]), (int) (maxs[ 1 ] - mins[ 1 ]), (int) (maxs[ 2 ] - mins[ 2 ]) );
//% Sys_Printf( "Grad: %f %f %f\n", gradient[ 0 ], gradient[ 1 ], gradient[ 2 ] );
/* increment counts */
numDiffuseLights++;
switch( ds->surfaceType )
{
case MST_PLANAR:
numBrushDiffuseLights++;
break;
case MST_TRIANGLE_SOUP:
numTriangleDiffuseLights;
break;
case MST_PATCH:
numPatchDiffuseLights++;
break;
}
/* create a light */
light = safe_malloc( sizeof( *light ) );
memset( light, 0, sizeof( *light ) );
/* attach it */
ThreadLock();
light->next = lights;
lights = light;
ThreadUnlock();
/* initialize the light */
light->flags = LIGHT_AREA_DEFAULT;
light->type = EMIT_AREA;
light->si = si;
light->fade = 1.0f;
light->w = w;
/* set falloff threshold */
light->falloffTolerance = falloffTolerance;
/* bouncing light? */
if( bouncing == qfalse )
{
/* handle first-pass lights in normal q3a style */
value = si->value;
light->photons = value * area * areaScale;
light->add = value * formFactorValueScale * areaScale;
VectorCopy( si->color, light->color );
VectorScale( light->color, light->add, light->emitColor );
light->style = noStyles ? LS_NORMAL : si->lightStyle;
if( light->style < LS_NORMAL || light->style >= LS_NONE )
light->style = LS_NORMAL;
/* set origin */
VectorAdd( mins, maxs, light->origin );
VectorScale( light->origin, 0.5f, light->origin );
/* nudge it off the plane a bit */
VectorCopy( normal, light->normal );
VectorMA( light->origin, 1.0f, light->normal, light->origin );
light->dist = DotProduct( light->origin, normal );
/* optionally create a point splashsplash light for first pass */
if( original && si->backsplashFraction > 0 )
{
/* allocate a new point light */
splash = safe_malloc( sizeof( *splash ) );
memset( splash, 0, sizeof( *splash ) );
splash->next = lights;
lights = splash;
/* set it up */
splash->flags = LIGHT_Q3A_DEFAULT;
splash->type = EMIT_POINT;
splash->photons = light->photons * si->backsplashFraction;
splash->fade = 1.0f;
splash->si = si;
VectorMA( light->origin, si->backsplashDistance, normal, splash->origin );
VectorCopy( si->color, splash->color );
splash->falloffTolerance = falloffTolerance;
splash->style = noStyles ? LS_NORMAL : light->style;
/* add to counts */
numPointLights++;
}
}
else
{
/* handle bounced light (radiosity) a little differently */
value = RADIOSITY_VALUE * si->bounceScale * 0.375f;
light->photons = value * area * bounceScale;
light->add = value * formFactorValueScale * bounceScale;
VectorCopy( color, light->color );
VectorScale( light->color, light->add, light->emitColor );
light->style = noStyles ? LS_NORMAL : style;
if( light->style < LS_NORMAL || light->style >= LS_NONE )
light->style = LS_NORMAL;
/* set origin */
WindingCenter( w, light->origin );
/* nudge it off the plane a bit */
VectorCopy( normal, light->normal );
VectorMA( light->origin, 1.0f, light->normal, light->origin );
light->dist = DotProduct( light->origin, normal );
}
/* emit light from both sides? */
if( si->compileFlags & C_FOG || si->twoSided )
light->flags |= LIGHT_TWOSIDED;
//% Sys_Printf( "\nAL: C: (%6f, %6f, %6f) [%6f] N: (%6f, %6f, %6f) %s\n",
//% light->color[ 0 ], light->color[ 1 ], light->color[ 2 ], light->add,
//% light->normal[ 0 ], light->normal[ 1 ], light->normal[ 2 ],
//% light->si->shader );
}
/*
RadLightForTriangles()
creates unbounced diffuse lights for triangle soup (misc_models, etc)
*/
void RadLightForTriangles( int num, int lightmapNum, rawLightmap_t *lm, shaderInfo_t *si, float scale, float subdivide, clipWork_t *cw )
{
int i, j, k, v;
bspDrawSurface_t *ds;
surfaceInfo_t *info;
float *radVertexLuxel;
radWinding_t rw;
/* get surface */
ds = &bspDrawSurfaces[ num ];
info = &surfaceInfos[ num ];
/* each triangle is a potential emitter */
rw.numVerts = 3;
for( i = 0; i < ds->numIndexes; i += 3 )
{
/* copy each vert */
for( j = 0; j < 3; j++ )
{
/* get vertex index and rad vertex luxel */
v = ds->firstVert + bspDrawIndexes[ ds->firstIndex + i + j ];
/* get most everything */
memcpy( &rw.verts[ j ], &yDrawVerts[ v ], sizeof( bspDrawVert_t ) );
/* fix colors */
for( k = 0; k < MAX_LIGHTMAPS; k++ )
{
radVertexLuxel = RAD_VERTEX_LUXEL( k, ds->firstVert + bspDrawIndexes[ ds->firstIndex + i + j ] );
VectorCopy( radVertexLuxel, rw.verts[ j ].color[ k ] );
rw.verts[ j ].color[ k ][ 3 ] = yDrawVerts[ v ].color[ k ][ 3 ];
}
}
/* subdivide into area lights */
RadSubdivideDiffuseLight( lightmapNum, ds, lm, si, scale, subdivide, qtrue, &rw, cw );
}
}
/*
RadLightForPatch()
creates unbounced diffuse lights for patches
*/
#define PLANAR_EPSILON 0.1f
void RadLightForPatch( int num, int lightmapNum, rawLightmap_t *lm, shaderInfo_t *si, float scale, float subdivide, clipWork_t *cw )
{
int i, x, y, v, t, pw[ 5 ], r;
bspDrawSurface_t *ds;
surfaceInfo_t *info;
bspDrawVert_t *bogus;
bspDrawVert_t *dv[ 4 ];
mesh_t src, *subdivided, *mesh;
float *radVertexLuxel;
float dist;
vec4_t plane;
qboolean planar;
radWinding_t rw;
/* get surface */
ds = &bspDrawSurfaces[ num ];
info = &surfaceInfos[ num ];
/* construct a bogus vert list with color index stuffed into color[ 0 ] */
bogus = safe_malloc( ds->numVerts * sizeof( bspDrawVert_t ) );
memcpy( bogus, &yDrawVerts[ ds->firstVert ], ds->numVerts * sizeof( bspDrawVert_t ) );
for( i = 0; i < ds->numVerts; i++ )
bogus[ i ].color[ 0 ][ 0 ] = i;
/* build a subdivided mesh identical to shadow facets for this patch */
/* this MUST MATCH FacetsForPatch() identically! */
src.width = ds->patchWidth;
src.height = ds->patchHeight;
src.verts = bogus;
//% subdivided = SubdivideMesh( src, 8, 512 );
subdivided = SubdivideMesh2( src, info->patchIterations );
PutMeshOnCurve( *subdivided );
//% MakeMeshNormals( *subdivided );
mesh = RemoveLinearMeshColumnsRows( subdivided );
FreeMesh( subdivided );
free( bogus );
/* FIXME: build interpolation table into color[ 1 ] */
/* fix up color indexes */
for( i = 0; i < (mesh->width * mesh->height); i++ )
{
dv[ 0 ] = &mesh->verts[ i ];
if( dv[ 0 ]->color[ 0 ][ 0 ] >= ds->numVerts )
dv[ 0 ]->color[ 0 ][ 0 ] = ds->numVerts - 1;
}
/* iterate through the mesh quads */
for( y = 0; y < (mesh->height - 1); y++ )
{
for( x = 0; x < (mesh->width - 1); x++ )
{
/* set indexes */
pw[ 0 ] = x + (y * mesh->width);
pw[ 1 ] = x + ((y + 1) * mesh->width);
pw[ 2 ] = x + 1 + ((y + 1) * mesh->width);
pw[ 3 ] = x + 1 + (y * mesh->width);
pw[ 4 ] = x + (y * mesh->width); /* same as pw[ 0 ] */
/* set radix */
r = (x + y) & 1;
/* get drawverts */
dv[ 0 ] = &mesh->verts[ pw[ r + 0 ] ];
dv[ 1 ] = &mesh->verts[ pw[ r + 1 ] ];
dv[ 2 ] = &mesh->verts[ pw[ r + 2 ] ];
dv[ 3 ] = &mesh->verts[ pw[ r + 3 ] ];
/* planar? */
planar = PlaneFromPoints( plane, dv[ 0 ]->xyz, dv[ 1 ]->xyz, dv[ 2 ]->xyz );
if( planar )
{
dist = DotProduct( dv[ 1 ]->xyz, plane ) - plane[ 3 ];
if( fabs( dist ) > PLANAR_EPSILON )
planar = qfalse;
}
/* generate a quad */
if( planar )
{
rw.numVerts = 4;
for( v = 0; v < 4; v++ )
{
/* get most everything */
memcpy( &rw.verts[ v ], dv[ v ], sizeof( bspDrawVert_t ) );
/* fix colors */
for( i = 0; i < MAX_LIGHTMAPS; i++ )
{
radVertexLuxel = RAD_VERTEX_LUXEL( i, ds->firstVert + dv[ v ]->color[ 0 ][ 0 ] );
VectorCopy( radVertexLuxel, rw.verts[ v ].color[ i ] );
rw.verts[ v ].color[ i ][ 3 ] = dv[ v ]->color[ i ][ 3 ];
}
}
/* subdivide into area lights */
RadSubdivideDiffuseLight( lightmapNum, ds, lm, si, scale, subdivide, qtrue, &rw, cw );
}
/* generate 2 tris */
else
{
rw.numVerts = 3;
for( t = 0; t < 2; t++ )
{
for( v = 0; v < 3 + t; v++ )
{
/* get "other" triangle (stupid hacky logic, but whatevah) */
if( v == 1 && t == 1 )
v++;
/* get most everything */
memcpy( &rw.verts[ v ], dv[ v ], sizeof( bspDrawVert_t ) );
/* fix colors */
for( i = 0; i < MAX_LIGHTMAPS; i++ )
{
radVertexLuxel = RAD_VERTEX_LUXEL( i, ds->firstVert + dv[ v ]->color[ 0 ][ 0 ] );
VectorCopy( radVertexLuxel, rw.verts[ v ].color[ i ] );
rw.verts[ v ].color[ i ][ 3 ] = dv[ v ]->color[ i ][ 3 ];
}
}
/* subdivide into area lights */
RadSubdivideDiffuseLight( lightmapNum, ds, lm, si, scale, subdivide, qtrue, &rw, cw );
}
}
}
}
/* free the mesh */
FreeMesh( mesh );
}
/*
RadLight()
creates unbounced diffuse lights for a given surface
*/
void RadLight( int num )
{
int lightmapNum;
float scale, subdivide;
int contentFlags, surfaceFlags, compileFlags;
bspDrawSurface_t *ds;
surfaceInfo_t *info;
rawLightmap_t *lm;
shaderInfo_t *si;
clipWork_t cw;
/* get drawsurface, lightmap, and shader info */
ds = &bspDrawSurfaces[ num ];
info = &surfaceInfos[ num ];
lm = info->lm;
si = info->si;
scale = si->bounceScale;
/* find nodraw bit */
contentFlags = surfaceFlags = compileFlags = 0;
ApplySurfaceParm( "nodraw", &contentFlags, &surfaceFlags, &compileFlags );
/* early outs? */
if( scale <= 0.0f || (si->compileFlags & C_SKY) || si->autosprite ||
(bspShaders[ ds->shaderNum ].contentFlags & contentFlags) || (bspShaders[ ds->shaderNum ].surfaceFlags & surfaceFlags) ||
(si->compileFlags & compileFlags) )
return;
/* determine how much we need to chop up the surface */
if( si->lightSubdivide )
subdivide = si->lightSubdivide;
else
subdivide = diffuseSubdivide;
/* inc counts */
numDiffuseSurfaces++;
/* iterate through styles (this could be more efficient, yes) */
for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
{
/* switch on type */
if( ds->lightmapStyles[ lightmapNum ] != LS_NONE && ds->lightmapStyles[ lightmapNum ] != LS_UNUSED )
{
switch( ds->surfaceType )
{
case MST_PLANAR:
case MST_TRIANGLE_SOUP:
RadLightForTriangles( num, lightmapNum, lm, si, scale, subdivide, &cw );
break;
case MST_PATCH:
RadLightForPatch( num, lightmapNum, lm, si, scale, subdivide, &cw );
break;
default:
break;
}
}
}
}
/*
RadCreateDiffuseLights()
creates lights for unbounced light on surfaces in the bsp
*/
int iterations = 0;
void RadCreateDiffuseLights( void )
{
/* startup */
Sys_FPrintf( SYS_VRB, "--- RadCreateDiffuseLights ---\n" );
numDiffuseSurfaces = 0;
numDiffuseLights = 0;
numBrushDiffuseLights = 0;
numTriangleDiffuseLights = 0;
numPatchDiffuseLights = 0;
numAreaLights = 0;
/* hit every surface (threaded) */
RunThreadsOnIndividual( numBSPDrawSurfaces, qtrue, RadLight );
/* dump the lights generated to a file */
if( dump )
{
char dumpName[ 1024 ], ext[ 64 ];
FILE *file;
light_t *light;
strcpy( dumpName, source );
StripExtension( dumpName );
sprintf( ext, "_bounce_%03d.map", iterations );
strcat( dumpName, ext );
file = fopen( dumpName, "wb" );
Sys_Printf( "Writing %s...\n", dumpName );
if( file )
{
for( light = lights; light; light = light->next )
{
fprintf( file,
"{\n"
"\"classname\" \"light\"\n"
"\"light\" \"%d\"\n"
"\"origin\" \"%.0f %.0f %.0f\"\n"
"\"_color\" \"%.3f %.3f %.3f\"\n"
"}\n",
(int) light->add,
light->origin[ 0 ],
light->origin[ 1 ],
light->origin[ 2 ],
light->color[ 0 ],
light->color[ 1 ],
light->color[ 2 ] );
}
fclose( file );
}
}
/* increment */
iterations++;
/* print counts */
Sys_Printf( "%8d diffuse surfaces\n", numDiffuseSurfaces );
Sys_FPrintf( SYS_VRB, "%8d total diffuse lights\n", numDiffuseLights );
Sys_FPrintf( SYS_VRB, "%8d brush diffuse lights\n", numBrushDiffuseLights );
Sys_FPrintf( SYS_VRB, "%8d patch diffuse lights\n", numPatchDiffuseLights );
Sys_FPrintf( SYS_VRB, "%8d triangle diffuse lights\n", numTriangleDiffuseLights );
}

View File

@@ -0,0 +1,124 @@
/*
Copyright (C) 1999-2006 Id Software, Inc. and contributors.
For a list of contributors, see the accompanying CONTRIBUTORS file.
This file is part of GtkRadiant.
GtkRadiant 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.
GtkRadiant 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 GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#define LIGHT_SHADOWS_C
#include "light.h"
#include "inout.h"
/* -------------------------------------------------------------------------------
ydnar: this code deals with shadow volume bsps
------------------------------------------------------------------------------- */
typedef struct shadowNode_s
{
vec4_t plane;
int children[ 2 ];
}
shadowNode_t;
int numShadowNodes;
shadowNode_t *shadowNodes;
/*
AddShadow()
adds a shadow, returning the index into the shadow list
*/
/*
MakeShadowFromPoints()
creates a shadow volume from 4 points (the first being the light origin)
*/
/*
SetupShadows()
sets up the shadow volumes for all lights in the world
*/
void SetupShadows( void )
{
int i, j, s;
light_t *light;
dleaf_t *leaf;
dsurface_t *ds;
surfaceInfo_t *info;
shaderInfo_t *si;
byte *tested;
/* early out for weird cases where there are no lights */
if( lights == NULL )
return;
/* note it */
Sys_FPrintf( SYS_VRB, "--- SetupShadows ---\n" );
/* allocate a surface test list */
tested = safe_malloc( numDrawSurfaces / 8 + 1 );
/* walk the list of lights */
for( light = lights; light != NULL; light = light->next )
{
/* do some early out testing */
if( light->cluster < 0 )
continue;
/* clear surfacetest list */
memset( tested, 0, numDrawSurfaces / 8 + 1 );
/* walk the bsp leaves */
for( i = 0, leaf = dleafs; i < numleafs; i++, leaf++ )
{
/* in pvs? */
if( ClusterVisible( light->cluster, leaf->cluster ) == qfalse )
continue;
/* walk the surface list for this leaf */
for( j = 0; j < leaf->numLeafSurfaces; j++ )
{
/* don't filter a surface more than once */
s = dleafsurfaces[ leaf->firstLeafSurface + j ];
if( tested[ s >> 3 ] & (1 << (s & 7)) )
continue;
tested[ s >> 3 ] |= (1 << (s & 7));
/* get surface and info */
ds = &drawSurfaces[ s ];
info = &surfaceInfos[ s ];
si = info->si;
/* don't create shadow volumes from translucent surfaces */
if( si->contents & CONTENTS_TRANSLUCENT )
continue;
}
}
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,496 @@
/*
Copyright (C) 1999-2006 Id Software, Inc. and contributors.
For a list of contributors, see the accompanying CONTRIBUTORS file.
This file is part of GtkRadiant.
GtkRadiant 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.
GtkRadiant 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 GtkRadiant; 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 = 0;
int c_planarPatch = 0;
int c_nonplanarLightmap = 0;
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);
/* get sample size */
ssize = ds->sampleSize;
#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;
vec4_t plane;
float d;
/* debug code */
#if 0
if( ds->type == SURF_META && ds->planar == qfalse )
Sys_Printf( "NPMS: %3d vertexes, %s\n", ds->numVerts, ds->shaderInfo->shader );
else if( ds->type == SURF_META && ds->planar == qtrue )
Sys_Printf( "PMS: %3d vertexes, %s\n", ds->numVerts, ds->shaderInfo->shader );
#endif
/* ydnar: handle planar patches */
if( noPatchFix == qtrue || (ds->type == SURF_PATCH && ds->planeNum < 0) )
{
AllocateLightmapForPatch( ds );
return;
}
/* get sample size */
ssize = ds->sampleSize;
/* 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;
}
/* ydnar: lightmap projection axis is already stored */
memset( vecs, 0, sizeof( vecs ) );
/* classify the plane (x y or z major) (ydnar: biased to z axis projection) */
if( ds->lightmapAxis[ 2 ] >= ds->lightmapAxis[ 0 ] && ds->lightmapAxis[ 2 ] >= ds->lightmapAxis[ 1 ] )
{
w = size[ 0 ];
h = size[ 1 ];
axis = 2;
vecs[ 0 ][ 0 ] = 1.0 / ssize;
vecs[ 1 ][ 1 ] = 1.0 / ssize;
}
else if( ds->lightmapAxis[ 0 ] >= ds->lightmapAxis[ 1 ] && ds->lightmapAxis[ 0 ] >= ds->lightmapAxis[ 2 ] )
{
w = size[ 1 ];
h = size[ 2 ];
axis = 0;
vecs[ 0 ][ 1 ] = 1.0 / ssize;
vecs[ 1 ][ 2 ] = 1.0 / ssize;
}
else
{
w = size[ 0 ];
h = size[ 2 ];
axis = 1;
vecs[ 0 ][ 0 ] = 1.0 / ssize;
vecs[ 1 ][ 2 ] = 1.0 / ssize;
}
/* odd check, given projection is now precalculated */
if( ds->lightmapAxis[ axis ] == 0 )
Error( "Chose a 0 valued axis" );
/* clamp to lightmap texture resolution */
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;
}
/* ydnar */
if( ds->planar == qfalse )
c_nonplanarLightmap += w * h;
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 */
/* construct a plane from the first vert and clear bounding box */
/* project mins onto plane to get origin */
VectorCopy( ds->lightmapVecs[ 2 ], plane );
plane[ 3 ] = DotProduct( ds->verts[ 0 ].xyz, plane );
d = DotProduct( mins, plane ) - plane[ 3 ];
d /= plane[ axis ];
//% 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 );
d /= plane[ axis ];
//%d = DotProduct( vecs[i], plane->normal );
//%d /= plane->normal[ axis ];
vecs[i][axis] -= d;
}
/* store lightmap origin and vectors (fixme: make this work right) */
VectorCopy( origin, ds->lightmapOrigin );
//% VectorCopy( plane->normal, ds->lightmapVecs[ 2 ] );
/* ydnar: lightmap vectors 0 and 1 are used for lod bounds, so don't overwrite */
if( ds->type == SURF_PATCH )
c_planarPatch++;
/* store lightmap vectors */
VectorCopy( vecs[ 0 ], ds->lightmapVecs[ 0 ] );
VectorCopy( vecs[ 1 ], ds->lightmapVecs[ 1 ] );
/* ydnar: print some stats */
//Sys_FPrintf( SYS_VRB, "Lightmap block %3d (%3d, %3d) (%3d x %3d) emitted\n", (numLightmaps - 1), x, y, w, h );
}
/*
===================
AllocateLightmaps
===================
*/
void AllocateLightmaps( entity_t *e )
{
int i, j;
mapDrawSurface_t *ds;
shaderInfo_t *si;
/* note it */
Sys_FPrintf( SYS_VRB,"--- AllocateLightmaps ---\n" );
/* sort all surfaces by shader so common shaders will usually be in the same lightmap */
/* ydnar: this is done in two passes, because of an odd bug with lightmapped terrain */
numSortShaders = 0;
for( i = e->firstDrawSurf; i < numMapDrawSurfs; i++ )
{
/* get surface and early out if possible */
ds = &mapDrawSurfs[ i ];
si = ds->shaderInfo;
if( si->surfaceFlags & SURF_VERTEXLIT )
continue;
if( ds->numVerts <= 0 )
continue;
/* ydnar: handle brush faces and patches first */
if( ds->type != SURF_FACE && ds->type != SURF_PATCH )
continue;
/* ydnar: this is unecessary because it should already be set */
//% VectorCopy( ds->plane.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;
}
}
/* new shader */
if( j == numSortShaders )
{
if( numSortShaders >= MAX_MAP_SHADERS )
Error( "MAX_MAP_SHADERS" );
surfsOnShader[ j ] = ds;
ds->nextOnShader = NULL;
numSortShaders++;
}
}
/* second pass, to allocate lightmapped terrain last */
for( i = e->firstDrawSurf; i < numMapDrawSurfs; i++ )
{
/* get surface and early out if possible */
ds = &mapDrawSurfs[ i ];
si = ds->shaderInfo;
if( si->surfaceFlags & SURF_VERTEXLIT )
continue;
if( ds->numVerts <= 0 )
continue;
/* ydnar: this only handles metasurfaces and terrain */
if( ds->type != SURF_TERRAIN && ds->type != SURF_META )
continue;
/* ydnar: a lightmap projection should be pre-stored for anything but excessively curved patches */
if( VectorLength( ds->lightmapAxis ) <= 0 )
continue;
/* search for this shader */
for( j = 0; j < numSortShaders; j++ )
{
if( ds->shaderInfo == surfsOnShader[ j ]->shaderInfo )
{
ds->nextOnShader = surfsOnShader[ j ];
surfsOnShader[ j ] = ds;
break;
}
}
/* new shader */
if( j == numSortShaders )
{
if( numSortShaders >= MAX_MAP_SHADERS )
Error( "MAX_MAP_SHADERS" );
surfsOnShader[ j ] = ds;
ds->nextOnShader = NULL;
numSortShaders++;
}
}
/* tot up shader count */
Sys_FPrintf( SYS_VRB, "%9d unique shaders\n", numSortShaders );
/* for each shader, allocate lightmaps for each surface */
for( i = 0; i < numSortShaders; i++ )
{
si = surfsOnShader[ i ]->shaderInfo;
for( ds = surfsOnShader[ i ]; ds; ds = ds->nextOnShader )
{
/* ydnar: promoting pointlight above nolightmap */
if( si->surfaceFlags & SURF_POINTLIGHT )
ds->lightmapNum = -3;
else if( si->surfaceFlags & SURF_NOLIGHTMAP )
ds->lightmapNum = -1;
else
AllocateLightmapForSurface( ds );
}
}
/* emit some statistics */
Sys_FPrintf( SYS_VRB, "%9d exact lightmap texels\n", c_exactLightmap );
Sys_FPrintf( SYS_VRB, "%9d block lightmap texels\n", numLightmaps * LIGHTMAP_WIDTH * LIGHTMAP_HEIGHT );
Sys_FPrintf( SYS_VRB, "%9d non-planar or terrain lightmap texels\n", c_nonplanarLightmap );
Sys_FPrintf( SYS_VRB, "%9d planar patch lightmaps\n", c_planarPatch );
Sys_FPrintf( SYS_VRB, "%9d lightmap textures, size: %d Kbytes\n", numLightmaps, (numLightmaps * LIGHTMAP_WIDTH * LIGHTMAP_HEIGHT * 3) / 1024 );
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,46 @@
#!/usr/bin/perl -w
use IO::Socket;
use Net::hostent;
my $port = shift || 13131;
my $server = IO::Socket::INET->new(
Proto => 'tcp',
LocalPort => $port,
Listen => SOMAXCONN,
Reuse => 1 )
|| die "can't setup server";
print "[Q3Map2 listener $0 is now active on port $port]\n";
while( $client = $server->accept() )
{
$client->autoflush( 1 );
$hostinfo = gethostbyaddr( $client->peeraddr );
printf "[Connect from %s]\n\n", $hostinfo ? $hostinfo->name : $client->peerhost;
#ask the client for a command
print $client "[server]\$";
my $text = "";
while( <$client> )
{
$text .= $_;
while( $text =~ s|<message[^>]*>([^<]+)</message>|| )
{
my $msg = $1;
# fix xml ents
$msg =~ s|&lt;|<|g;
$msg =~ s|&gt;|>|g;
$msg =~ s|&quot;|"|g;#"
$msg =~ s|&apos;|'|g;#'
print $msg;
}
}
printf "\n[Disconnected: %s]\n\n", $hostinfo ? $hostinfo->name : $client->peerhost;
close $client;
}

745
tools/quake3/q3map2/main.c Normal file
View File

@@ -0,0 +1,745 @@
/* -------------------------------------------------------------------------------
Copyright (C) 1999-2006 Id Software, Inc. and contributors.
For a list of contributors, see the accompanying CONTRIBUTORS file.
This file is part of GtkRadiant.
GtkRadiant 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.
GtkRadiant 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 GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-------------------------------------------------------------------------------
This code has been altered significantly from its original form, to support
several games based on the Quake III Arena engine, in the form of "Q3Map2."
------------------------------------------------------------------------------- */
/* marker */
#define MAIN_C
/* dependencies */
#include "q3map2.h"
/*
Random()
returns a pseudorandom number between 0 and 1
*/
vec_t Random( void )
{
return (vec_t) rand() / RAND_MAX;
}
/*
ExitQ3Map()
cleanup routine
*/
static void ExitQ3Map( void )
{
BSPFilesCleanup();
if( mapDrawSurfs != NULL )
free( mapDrawSurfs );
}
/*
MD4BlockChecksum()
calculates an md4 checksum for a block of data
*/
static int MD4BlockChecksum( void *buffer, int length )
{
MHASH mh;
int digest[ 4 ], checksum;
/* make md4 hash */
mh = mhash_init( MHASH_MD4 );
if( !mh )
Error( "Unable to initialize MD4 hash context" );
mhash( mh, buffer, length );
mhash_deinit( mh, digest );
/* xor the bits and return */
checksum = digest[ 0 ] ^ digest[ 1 ] ^ digest[ 2 ] ^ digest[ 3 ];
return checksum;
}
/*
FixAAS()
resets an aas checksum to match the given BSP
*/
int FixAAS( int argc, char **argv )
{
int length, checksum;
void *buffer;
FILE *file;
char aas[ 1024 ], **ext;
char *exts[] =
{
".aas",
"_b0.aas",
"_b1.aas",
NULL
};
/* arg checking */
if( argc < 2 )
{
Sys_Printf( "Usage: q3map -fixaas [-v] <mapname>\n" );
return 0;
}
/* do some path mangling */
strcpy( source, ExpandArg( argv[ argc - 1 ] ) );
StripExtension( source );
DefaultExtension( source, ".bsp" );
/* note it */
Sys_Printf( "--- FixAAS ---\n" );
/* load the bsp */
Sys_Printf( "Loading %s\n", source );
length = LoadFile( source, &buffer );
/* create bsp checksum */
Sys_Printf( "Creating checksum...\n" );
checksum = LittleLong( MD4BlockChecksum( buffer, length ) );
/* write checksum to aas */
ext = exts;
while( *ext )
{
/* mangle name */
strcpy( aas, source );
StripExtension( aas );
strcat( aas, *ext );
Sys_Printf( "Trying %s\n", aas );
ext++;
/* fix it */
file = fopen( aas, "r+b" );
if( !file )
continue;
if( fwrite( &checksum, 4, 1, file ) != 1 )
Error( "Error writing checksum to %s", aas );
fclose( file );
}
/* return to sender */
return 0;
}
/*
AnalyzeBSP() - ydnar
analyzes a Quake engine BSP file
*/
typedef struct abspHeader_s
{
char ident[ 4 ];
int version;
bspLump_t lumps[ 1 ]; /* unknown size */
}
abspHeader_t;
typedef struct abspLumpTest_s
{
int radix, minCount;
char *name;
}
abspLumpTest_t;
int AnalyzeBSP( int argc, char **argv )
{
abspHeader_t *header;
int size, i, version, offset, length, lumpInt, count;
char ident[ 5 ];
void *lump;
float lumpFloat;
char lumpString[ 1024 ], source[ 1024 ];
qboolean lumpSwap = qfalse;
abspLumpTest_t *lumpTest;
static abspLumpTest_t lumpTests[] =
{
{ sizeof( bspPlane_t ), 6, "IBSP LUMP_PLANES" },
{ sizeof( bspBrush_t ), 1, "IBSP LUMP_BRUSHES" },
{ 8, 6, "IBSP LUMP_BRUSHSIDES" },
{ sizeof( bspBrushSide_t ), 6, "RBSP LUMP_BRUSHSIDES" },
{ sizeof( bspModel_t ), 1, "IBSP LUMP_MODELS" },
{ sizeof( bspNode_t ), 2, "IBSP LUMP_NODES" },
{ sizeof( bspLeaf_t ), 1, "IBSP LUMP_LEAFS" },
{ 104, 3, "IBSP LUMP_DRAWSURFS" },
{ 44, 3, "IBSP LUMP_DRAWVERTS" },
{ 4, 6, "IBSP LUMP_DRAWINDEXES" },
{ 128 * 128 * 3, 1, "IBSP LUMP_LIGHTMAPS" },
{ 256 * 256 * 3, 1, "IBSP LUMP_LIGHTMAPS (256 x 256)" },
{ 512 * 512 * 3, 1, "IBSP LUMP_LIGHTMAPS (512 x 512)" },
{ 0, 0, NULL }
};
/* arg checking */
if( argc < 1 )
{
Sys_Printf( "Usage: q3map -analyze [-lumpswap] [-v] <mapname>\n" );
return 0;
}
/* process arguments */
for( i = 1; i < (argc - 1); i++ )
{
/* -format map|ase|... */
if( !strcmp( argv[ i ], "-lumpswap" ) )
{
Sys_Printf( "Swapped lump structs enabled\n" );
lumpSwap = qtrue;
}
}
/* clean up map name */
strcpy( source, ExpandArg( argv[ i ] ) );
Sys_Printf( "Loading %s\n", source );
/* load the file */
size = LoadFile( source, (void**) &header );
if( size == 0 || header == NULL )
{
Sys_Printf( "Unable to load %s.\n", source );
return -1;
}
/* analyze ident/version */
memcpy( ident, header->ident, 4 );
ident[ 4 ] = '\0';
version = LittleLong( header->version );
Sys_Printf( "Identity: %s\n", ident );
Sys_Printf( "Version: %d\n", version );
Sys_Printf( "---------------------------------------\n" );
/* analyze each lump */
for( i = 0; i < 100; i++ )
{
/* call of duty swapped lump pairs */
if( lumpSwap )
{
offset = LittleLong( header->lumps[ i ].length );
length = LittleLong( header->lumps[ i ].offset );
}
/* standard lump pairs */
else
{
offset = LittleLong( header->lumps[ i ].offset );
length = LittleLong( header->lumps[ i ].length );
}
/* extract data */
lump = (byte*) header + offset;
lumpInt = LittleLong( (int) *((int*) lump) );
lumpFloat = LittleFloat( (float) *((float*) lump) );
memcpy( lumpString, (char*) lump, (length < 1024 ? length : 1024) );
lumpString[ 1024 ] = '\0';
/* print basic lump info */
Sys_Printf( "Lump: %d\n", i );
Sys_Printf( "Offset: %d bytes\n", offset );
Sys_Printf( "Length: %d bytes\n", length );
/* only operate on valid lumps */
if( length > 0 )
{
/* print data in 4 formats */
Sys_Printf( "As hex: %08X\n", lumpInt );
Sys_Printf( "As int: %d\n", lumpInt );
Sys_Printf( "As float: %f\n", lumpFloat );
Sys_Printf( "As string: %s\n", lumpString );
/* guess lump type */
if( lumpString[ 0 ] == '{' && lumpString[ 2 ] == '"' )
Sys_Printf( "Type guess: IBSP LUMP_ENTITIES\n" );
else if( strstr( lumpString, "textures/" ) )
Sys_Printf( "Type guess: IBSP LUMP_SHADERS\n" );
else
{
/* guess based on size/count */
for( lumpTest = lumpTests; lumpTest->radix > 0; lumpTest++ )
{
if( (length % lumpTest->radix) != 0 )
continue;
count = length / lumpTest->radix;
if( count < lumpTest->minCount )
continue;
Sys_Printf( "Type guess: %s (%d x %d)\n", lumpTest->name, count, lumpTest->radix );
}
}
}
Sys_Printf( "---------------------------------------\n" );
/* end of file */
if( offset + length >= size )
break;
}
/* last stats */
Sys_Printf( "Lump count: %d\n", i + 1 );
Sys_Printf( "File size: %d bytes\n", size );
/* return to caller */
return 0;
}
/*
BSPInfo()
emits statistics about the bsp file
*/
int BSPInfo( int count, char **fileNames )
{
int i;
char source[ 1024 ], ext[ 64 ];
int size;
FILE *f;
/* dummy check */
if( count < 1 )
{
Sys_Printf( "No files to dump info for.\n");
return -1;
}
/* enable info mode */
infoMode = qtrue;
/* walk file list */
for( i = 0; i < count; i++ )
{
Sys_Printf( "---------------------------------\n" );
/* mangle filename and get size */
strcpy( source, fileNames[ i ] );
ExtractFileExtension( source, ext );
if( !Q_stricmp( ext, "map" ) )
StripExtension( source );
DefaultExtension( source, ".bsp" );
f = fopen( source, "rb" );
if( f )
{
size = Q_filelength (f);
fclose( f );
}
else
size = 0;
/* load the bsp file and print lump sizes */
Sys_Printf( "%s\n", source );
LoadBSPFile( source );
PrintBSPFileSizes();
/* print sizes */
Sys_Printf( "\n" );
Sys_Printf( " total %9d\n", size );
Sys_Printf( " %9d KB\n", size / 1024 );
Sys_Printf( " %9d MB\n", size / (1024 * 1024) );
Sys_Printf( "---------------------------------\n" );
}
/* return count */
return i;
}
/*
ScaleBSPMain()
amaze and confuse your enemies with wierd scaled maps!
*/
int ScaleBSPMain( int argc, char **argv )
{
int i;
float f, scale;
vec3_t vec;
char str[ 1024 ];
/* arg checking */
if( argc < 2 )
{
Sys_Printf( "Usage: q3map -scale <value> [-v] <mapname>\n" );
return 0;
}
/* get scale */
scale = atof( argv[ argc - 2 ] );
if( scale == 0.0f )
{
Sys_Printf( "Usage: q3map -scale <value> [-v] <mapname>\n" );
Sys_Printf( "Non-zero scale value required.\n" );
return 0;
}
/* do some path mangling */
strcpy( source, ExpandArg( argv[ argc - 1 ] ) );
StripExtension( source );
DefaultExtension( source, ".bsp" );
/* load the bsp */
Sys_Printf( "Loading %s\n", source );
LoadBSPFile( source );
ParseEntities();
/* note it */
Sys_Printf( "--- ScaleBSP ---\n" );
Sys_FPrintf( SYS_VRB, "%9d entities\n", numEntities );
/* scale entity keys */
for( i = 0; i < numBSPEntities && i < numEntities; i++ )
{
/* scale origin */
GetVectorForKey( &entities[ i ], "origin", vec );
if( (vec[ 0 ] + vec[ 1 ] + vec[ 2 ]) )
{
VectorScale( vec, scale, vec );
sprintf( str, "%f %f %f", vec[ 0 ], vec[ 1 ], vec[ 2 ] );
SetKeyValue( &entities[ i ], "origin", str );
}
/* scale door lip */
f = FloatForKey( &entities[ i ], "lip" );
if( f )
{
f *= scale;
sprintf( str, "%f", f );
SetKeyValue( &entities[ i ], "lip", str );
}
}
/* scale models */
for( i = 0; i < numBSPModels; i++ )
{
VectorScale( bspModels[ i ].mins, scale, bspModels[ i ].mins );
VectorScale( bspModels[ i ].maxs, scale, bspModels[ i ].maxs );
}
/* scale nodes */
for( i = 0; i < numBSPNodes; i++ )
{
VectorScale( bspNodes[ i ].mins, scale, bspNodes[ i ].mins );
VectorScale( bspNodes[ i ].maxs, scale, bspNodes[ i ].maxs );
}
/* scale leafs */
for( i = 0; i < numBSPLeafs; i++ )
{
VectorScale( bspLeafs[ i ].mins, scale, bspLeafs[ i ].mins );
VectorScale( bspLeafs[ i ].maxs, scale, bspLeafs[ i ].maxs );
}
/* scale drawverts */
for( i = 0; i < numBSPDrawVerts; i++ )
VectorScale( bspDrawVerts[ i ].xyz, scale, bspDrawVerts[ i ].xyz );
/* scale planes */
for( i = 0; i < numBSPPlanes; i++ )
bspPlanes[ i ].dist *= scale;
/* scale gridsize */
GetVectorForKey( &entities[ 0 ], "gridsize", vec );
if( (vec[ 0 ] + vec[ 1 ] + vec[ 2 ]) == 0.0f )
VectorCopy( gridSize, vec );
VectorScale( vec, scale, vec );
sprintf( str, "%f %f %f", vec[ 0 ], vec[ 1 ], vec[ 2 ] );
SetKeyValue( &entities[ 0 ], "gridsize", str );
/* write the bsp */
UnparseEntities();
StripExtension( source );
DefaultExtension( source, "_s.bsp" );
Sys_Printf( "Writing %s\n", source );
WriteBSPFile( source );
/* return to sender */
return 0;
}
/*
ConvertBSPMain()
main argument processing function for bsp conversion
*/
int ConvertBSPMain( int argc, char **argv )
{
int i;
int (*convertFunc)( char * );
game_t *convertGame;
/* set default */
convertFunc = ConvertBSPToASE;
convertGame = NULL;
/* arg checking */
if( argc < 1 )
{
Sys_Printf( "Usage: q3map -scale <value> [-v] <mapname>\n" );
return 0;
}
/* process arguments */
for( i = 1; i < (argc - 1); i++ )
{
/* -format map|ase|... */
if( !strcmp( argv[ i ], "-format" ) )
{
i++;
if( !Q_stricmp( argv[ i ], "ase" ) )
convertFunc = ConvertBSPToASE;
else if( !Q_stricmp( argv[ i ], "map" ) )
convertFunc = ConvertBSPToMap;
else
{
convertGame = GetGame( argv[ i ] );
if( convertGame == NULL )
Sys_Printf( "Unknown conversion format \"%s\". Defaulting to ASE.\n", argv[ i ] );
}
}
}
/* clean up map name */
strcpy( source, ExpandArg( argv[ i ] ) );
StripExtension( source );
DefaultExtension( source, ".bsp" );
LoadShaderInfo();
Sys_Printf( "Loading %s\n", source );
/* ydnar: load surface file */
//% LoadSurfaceExtraFile( source );
LoadBSPFile( source );
/* parse bsp entities */
ParseEntities();
/* bsp format convert? */
if( convertGame != NULL )
{
/* set global game */
game = convertGame;
/* write bsp */
StripExtension( source );
DefaultExtension( source, "_c.bsp" );
Sys_Printf( "Writing %s\n", source );
WriteBSPFile( source );
/* return to sender */
return 0;
}
/* normal convert */
return convertFunc( source );
}
/*
main()
q3map mojo...
*/
int main( int argc, char **argv )
{
int i, r;
double start, end;
/* we want consistent 'randomness' */
srand( 0 );
/* start timer */
start = I_FloatTime();
/* this was changed to emit version number over the network */
printf( Q3MAP_VERSION "\n" );
/* set exit call */
atexit( ExitQ3Map );
/* read general options first */
for( i = 1; i < argc; i++ )
{
/* -connect */
if( !strcmp( argv[ i ], "-connect" ) )
{
argv[ i ] = NULL;
i++;
Broadcast_Setup( argv[ i ] );
argv[ i ] = NULL;
}
/* verbose */
else if( !strcmp( argv[ i ], "-v" ) )
{
verbose = qtrue;
argv[ i ] = NULL;
}
/* force */
else if( !strcmp( argv[ i ], "-force" ) )
{
force = qtrue;
argv[ i ] = NULL;
}
/* patch subdivisions */
else if( !strcmp( argv[ i ], "-subdivisions" ) )
{
argv[ i ] = NULL;
i++;
patchSubdivisions = atoi( argv[ i ] );
argv[ i ] = NULL;
if( patchSubdivisions <= 0 )
patchSubdivisions = 1;
}
/* threads */
else if( !strcmp( argv[ i ], "-threads" ) )
{
argv[ i ] = NULL;
i++;
numthreads = atoi( argv[ i ] );
argv[ i ] = NULL;
}
}
/* init model library */
PicoInit();
PicoSetMallocFunc( safe_malloc );
PicoSetFreeFunc( free );
PicoSetPrintFunc( PicoPrintFunc );
PicoSetLoadFileFunc( PicoLoadFileFunc );
PicoSetFreeFileFunc( free );
/* set number of threads */
ThreadSetDefault();
/* generate sinusoid jitter table */
for( i = 0; i < MAX_JITTERS; i++ )
{
jitters[ i ] = sin( i * 139.54152147 );
//% Sys_Printf( "Jitter %4d: %f\n", i, jitters[ i ] );
}
/* we print out two versions, q3map's main version (since it evolves a bit out of GtkRadiant)
and we put the GtkRadiant version to make it easy to track with what version of Radiant it was built with */
Sys_Printf( "Q3Map - v1.0r (c) 1999 Id Software Inc.\n" );
Sys_Printf( "Q3Map (ydnar) - v" Q3MAP_VERSION "\n" );
Sys_Printf( "GtkRadiant - v" RADIANT_VERSION " " __DATE__ " " __TIME__ "\n" );
Sys_Printf( "%s\n", Q3MAP_MOTD );
/* ydnar: new path initialization */
InitPaths( &argc, argv );
/* check if we have enough options left to attempt something */
if( argc < 2 )
Error( "Usage: %s [general options] [options] mapfile", argv[ 0 ] );
/* fixaas */
if( !strcmp( argv[ 1 ], "-fixaas" ) )
r = FixAAS( argc - 1, argv + 1 );
/* analyze */
else if( !strcmp( argv[ 1 ], "-analyze" ) )
r = AnalyzeBSP( argc - 1, argv + 1 );
/* info */
else if( !strcmp( argv[ 1 ], "-info" ) )
r = BSPInfo( argc - 2, argv + 2 );
/* vis */
else if( !strcmp( argv[ 1 ], "-vis" ) )
r = VisMain( argc - 1, argv + 1 );
/* light */
else if( !strcmp( argv[ 1 ], "-light" ) )
r = LightMain( argc - 1, argv + 1 );
/* vlight */
else if( !strcmp( argv[ 1 ], "-vlight" ) )
{
Sys_Printf( "WARNING: VLight is no longer supported, defaulting to -light -fast instead\n\n" );
argv[ 1 ] = "-fast"; /* eek a hack */
r = LightMain( argc, argv );
}
/* ydnar: lightmap export */
else if( !strcmp( argv[ 1 ], "-export" ) )
r = ExportLightmapsMain( argc - 1, argv + 1 );
/* ydnar: lightmap import */
else if( !strcmp( argv[ 1 ], "-import" ) )
r = ImportLightmapsMain( argc - 1, argv + 1 );
/* ydnar: bsp scaling */
else if( !strcmp( argv[ 1 ], "-scale" ) )
r = ScaleBSPMain( argc - 1, argv + 1 );
/* ydnar: bsp conversion */
else if( !strcmp( argv[ 1 ], "-convert" ) )
r = ConvertBSPMain( argc - 1, argv + 1 );
/* ydnar: otherwise create a bsp */
else
r = BSPMain( argc, argv );
/* emit time */
end = I_FloatTime();
Sys_Printf( "%9.0f seconds elapsed\n", end - start );
/* shut down connection */
Broadcast_Shutdown();
/* return any error code */
return r;
}

1667
tools/quake3/q3map2/map.c Normal file

File diff suppressed because it is too large Load Diff

826
tools/quake3/q3map2/mesh.c Normal file
View File

@@ -0,0 +1,826 @@
/* -------------------------------------------------------------------------------
Copyright (C) 1999-2006 Id Software, Inc. and contributors.
For a list of contributors, see the accompanying CONTRIBUTORS file.
This file is part of GtkRadiant.
GtkRadiant 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.
GtkRadiant 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 GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
----------------------------------------------------------------------------------
This code has been altered significantly from its original form, to support
several games based on the Quake III Arena engine, in the form of "Q3Map2."
------------------------------------------------------------------------------- */
/* marker */
#define MESH_C
/* dependencies */
#include "q3map2.h"
/*
LerpDrawVert()
returns an 50/50 interpolated vert
*/
void LerpDrawVert( bspDrawVert_t *a, bspDrawVert_t *b, bspDrawVert_t *out )
{
int k;
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 ]);
for( k = 0; k < MAX_LIGHTMAPS; k++ )
{
out->lightmap[ k ][ 0 ] = 0.5f * (a->lightmap[ k ][ 0 ] + b->lightmap[ k ][ 0 ]);
out->lightmap[ k ][ 1 ] = 0.5f * (a->lightmap[ k ][ 1 ] + b->lightmap[ k ][ 1 ]);
out->color[ k ][ 0 ] = (a->color[ k ][ 0 ] + b->color[ k ][ 0 ]) >> 1;
out->color[ k ][ 1 ] = (a->color[ k ][ 1 ] + b->color[ k ][ 1 ]) >> 1;
out->color[ k ][ 2 ] = (a->color[ k ][ 2 ] + b->color[ k ][ 2 ]) >> 1;
out->color[ k ][ 3 ] = (a->color[ k ][ 3 ] + b->color[ k ][ 3 ]) >> 1;
}
/* ydnar: added normal interpolation */
out->normal[ 0 ] = 0.5f * (a->normal[ 0 ] + b->normal[ 0 ]);
out->normal[ 1 ] = 0.5f * (a->normal[ 1 ] + b->normal[ 1 ]);
out->normal[ 2 ] = 0.5f * (a->normal[ 2 ] + b->normal[ 2 ]);
/* if the interpolant created a bogus normal, just copy the normal from a */
if( VectorNormalize( out->normal, out->normal ) == 0 )
VectorCopy( a->normal, out->normal );
}
/*
LerpDrawVertAmount()
returns a biased interpolated vert
*/
void LerpDrawVertAmount( bspDrawVert_t *a, bspDrawVert_t *b, float amount, bspDrawVert_t *out )
{
int k;
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 ]);
for( k = 0; k < MAX_LIGHTMAPS; k++ )
{
out->lightmap[ k ][ 0 ] = a->lightmap[ k ][ 0 ] + amount * (b->lightmap[ k ][ 0 ] - a->lightmap[ k ][ 0 ]);
out->lightmap[ k ][ 1 ] = a->lightmap[ k ][ 1 ] + amount * (b->lightmap[ k ][ 1 ] - a->lightmap[ k ][ 1 ]);
out->color[ k ][ 0 ] = a->color[ k ][ 0 ] + amount * (b->color[ k ][ 0 ] - a->color[ k ][ 0 ]);
out->color[ k ][ 1 ] = a->color[ k ][ 1 ] + amount * (b->color[ k ][ 1 ] - a->color[ k ][ 1 ]);
out->color[ k ][ 2 ] = a->color[ k ][ 2 ] + amount * (b->color[ k ][ 2 ] - a->color[ k ][ 2 ]);
out->color[ k ][ 3 ] = a->color[ k ][ 3 ] + amount * (b->color[ k ][ 3 ] - a->color[ k ][ 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 ]);
/* if the interpolant created a bogus normal, just copy the normal from a */
if( VectorNormalize( out->normal, out->normal ) == 0 )
VectorCopy( a->normal, out->normal );
}
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++ ) {
Sys_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] );
}
Sys_Printf("\n");
}
}
mesh_t *CopyMesh( mesh_t *mesh ) {
mesh_t *out;
int size;
out = safe_malloc( sizeof( *out ) );
out->width = mesh->width;
out->height = mesh->height;
size = out->width * out->height * sizeof( *out->verts );
out->verts = safe_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 = safe_malloc( sizeof( *out ) );
out->width = in->height;
out->height = in->width;
out->verts = safe_malloc( out->width * out->height * sizeof( bspDrawVert_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;
bspDrawVert_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;
bspDrawVert_t *dv;
vec3_t around[8], temp;
qboolean good[8];
qboolean wrapWidth, wrapHeight;
float len;
int neighbors[8][2] =
{
{0,1}, {1,1}, {1,0}, {1,-1}, {0,-1}, {-1,-1}, {-1,0}, {-1,1}
};
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 ) {
//Sys_Printf("bad normal\n");
count = 1;
}
VectorNormalize( sum, dv->normal );
}
}
}
/*
PutMeshOnCurve()
drops the aproximating points onto the curve
ydnar: fixme: make this use LerpDrawVert() rather than this complicated mess
*/
void PutMeshOnCurve( mesh_t in ) {
int i, j, l, m;
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;
/* ydnar: interpolating st coords */
if( l < 2 )
{
prev = ( in.verts[j*in.width+i].st[l] + in.verts[(j+1)*in.width+i].st[l] ) * 0.5;
next = ( in.verts[j*in.width+i].st[l] + in.verts[(j-1)*in.width+i].st[l] ) * 0.5;
in.verts[j*in.width+i].st[l] = ( prev + next ) * 0.5;
for( m = 0; m < MAX_LIGHTMAPS; m++ )
{
prev = ( in.verts[j*in.width+i].lightmap[ m ][l] + in.verts[(j+1)*in.width+i].lightmap[ m ][l] ) * 0.5;
next = ( in.verts[j*in.width+i].lightmap[ m ][l] + in.verts[(j-1)*in.width+i].lightmap[ m ][l] ) * 0.5;
in.verts[j*in.width+i].lightmap[ m ][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;
/* ydnar: interpolating st coords */
if( l < 2 )
{
prev = ( in.verts[j*in.width+i].st[l] + in.verts[j*in.width+i+1].st[l] ) * 0.5;
next = ( in.verts[j*in.width+i].st[l] + in.verts[j*in.width+i-1].st[l] ) * 0.5;
in.verts[j*in.width+i].st[l] = ( prev + next ) * 0.5;
for( m = 0; m < MAX_LIGHTMAPS; m++ )
{
prev = ( in.verts[j*in.width+i].lightmap[ m ][l] + in.verts[j*in.width+i+1].lightmap[ m ][l] ) * 0.5;
next = ( in.verts[j*in.width+i].lightmap[ m ][l] + in.verts[j*in.width+i-1].lightmap[ m ][l] ) * 0.5;
in.verts[j*in.width+i].lightmap[ m ][l] = ( prev + next ) * 0.5;
}
}
}
}
}
}
/*
=================
SubdivideMesh
=================
*/
mesh_t *SubdivideMesh( mesh_t in, float maxError, float minLength )
{
int i, j, k, l;
bspDrawVert_t prev, next, mid;
vec3_t prevxyz, nextxyz, midxyz;
vec3_t delta;
float len;
mesh_t out;
/* ydnar: static for os x */
MAC_STATIC bspDrawVert_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];
}
}
// 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 ( 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 ( 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(bspDrawVert_t) );
}
return CopyMesh(&out);
}
/*
IterationsForCurve() - ydnar
given a curve of a certain length, return the number of subdivision iterations
note: this is affected by subdivision amount
*/
int IterationsForCurve( float len, int subdivisions )
{
int iterations, facets;
/* calculate the number of subdivisions */
for( iterations = 0; iterations < 3; iterations++ )
{
facets = subdivisions * 16 * pow( 2, iterations );
if( facets >= len )
break;
}
/* return to caller */
return iterations;
}
/*
SubdivideMesh2() - ydnar
subdivides each mesh quad a specified number of times
*/
mesh_t *SubdivideMesh2( mesh_t in, int iterations )
{
int i, j, k;
bspDrawVert_t prev, next, mid;
mesh_t out;
/* ydnar: static for os x */
MAC_STATIC bspDrawVert_t expand[ MAX_EXPANDED_AXIS ][ MAX_EXPANDED_AXIS ];
/* initial setup */
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 ];
}
/* keep chopping */
for( iterations; iterations > 0; iterations-- )
{
/* horizontal subdivisions */
for( j = 0; j + 2 < out.width; j += 4 )
{
/* check size limit */
if( out.width + 2 >= MAX_EXPANDED_AXIS )
break;
/* insert two columns and replace the peak */
out.width += 2;
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;
}
}
/* vertical subdivisions */
for ( j = 0; j + 2 < out.height; j += 4 )
{
/* check size limit */
if( out.height + 2 >= MAX_EXPANDED_AXIS )
break;
/* insert two columns and replace the peak */
out.height += 2;
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;
}
}
}
/* 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( bspDrawVert_t ) );
/* return to sender */
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;
/* ydnar: static for os x */
MAC_STATIC bspDrawVert_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];
}
}
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];
}
}
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(bspDrawVert_t) );
}
return CopyMesh(&out);
}
/*
=================
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;
bspDrawVert_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 ( 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 ( 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(bspDrawVert_t) );
}
return CopyMesh(&out);
}

706
tools/quake3/q3map2/model.c Normal file
View File

@@ -0,0 +1,706 @@
/* -------------------------------------------------------------------------------
Copyright (C) 1999-2006 Id Software, Inc. and contributors.
For a list of contributors, see the accompanying CONTRIBUTORS file.
This file is part of GtkRadiant.
GtkRadiant 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.
GtkRadiant 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 GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
----------------------------------------------------------------------------------
This code has been altered significantly from its original form, to support
several games based on the Quake III Arena engine, in the form of "Q3Map2."
------------------------------------------------------------------------------- */
/* marker */
#define MODEL_C
/* dependencies */
#include "q3map2.h"
/*
PicoPrintFunc()
callback for picomodel.lib
*/
void PicoPrintFunc( int level, const char *str )
{
if( str == NULL )
return;
switch( level )
{
case PICO_NORMAL:
Sys_Printf( "%s\n", str );
break;
case PICO_VERBOSE:
Sys_FPrintf( SYS_VRB, "%s\n", str );
break;
case PICO_WARNING:
Sys_Printf( "WARNING: %s\n", str );
break;
case PICO_ERROR:
Sys_Printf( "ERROR: %s\n", str );
break;
case PICO_FATAL:
Error( "ERROR: %s\n", str );
break;
}
}
/*
PicoLoadFileFunc()
callback for picomodel.lib
*/
void PicoLoadFileFunc( char *name, byte **buffer, int *bufSize )
{
*bufSize = vfsLoadFile( (const char*) name, (void**) buffer, 0 );
}
/*
FindModel() - ydnar
finds an existing picoModel and returns a pointer to the picoModel_t struct or NULL if not found
*/
picoModel_t *FindModel( char *name, int frame )
{
int i;
/* init */
if( numPicoModels <= 0 )
memset( picoModels, 0, sizeof( picoModels ) );
/* dummy check */
if( name == NULL || name[ 0 ] == '\0' )
return NULL;
/* search list */
for( i = 0; i < MAX_MODELS; i++ )
{
if( picoModels[ i ] != NULL &&
!strcmp( PicoGetModelName( picoModels[ i ] ), name ) &&
PicoGetModelFrameNum( picoModels[ i ] ) == frame )
return picoModels[ i ];
}
/* no matching picoModel found */
return NULL;
}
/*
LoadModel() - ydnar
loads a picoModel and returns a pointer to the picoModel_t struct or NULL if not found
*/
picoModel_t *LoadModel( char *name, int frame )
{
int i;
picoModel_t *model, **pm;
/* init */
if( numPicoModels <= 0 )
memset( picoModels, 0, sizeof( picoModels ) );
/* dummy check */
if( name == NULL || name[ 0 ] == '\0' )
return NULL;
/* try to find existing picoModel */
model = FindModel( name, frame );
if( model != NULL )
return model;
/* none found, so find first non-null picoModel */
pm = NULL;
for( i = 0; i < MAX_MODELS; i++ )
{
if( picoModels[ i ] == NULL )
{
pm = &picoModels[ i ];
break;
}
}
/* too many picoModels? */
if( pm == NULL )
Error( "MAX_MODELS (%d) exceeded, there are too many model files referenced by the map.", MAX_MODELS );
/* attempt to parse model */
*pm = PicoLoadModel( (char*) name, frame );
/* if loading failed, make a bogus model to silence the rest of the warnings */
if( *pm == NULL )
{
/* allocate a new model */
*pm = PicoNewModel();
if( *pm == NULL )
return NULL;
/* set data */
PicoSetModelName( *pm, name );
PicoSetModelFrameNum( *pm, frame );
}
/* debug code */
#if 0
{
int numSurfaces, numVertexes;
picoSurface_t *ps;
Sys_Printf( "Model %s\n", name );
numSurfaces = PicoGetModelNumSurfaces( *pm );
for( i = 0; i < numSurfaces; i++ )
{
ps = PicoGetModelSurface( *pm, i );
numVertexes = PicoGetSurfaceNumVertexes( ps );
Sys_Printf( "Surface %d has %d vertexes\n", i, numVertexes );
}
}
#endif
/* set count */
if( *pm != NULL )
numPicoModels++;
/* return the picoModel */
return *pm;
}
/*
InsertModel() - ydnar
adds a picomodel into the bsp
*/
void InsertModel( char *name, int frame, m4x4_t transform, remap_t *remap, shaderInfo_t *celShader, int eNum, int castShadows, int recvShadows, int spawnFlags, float lightmapScale )
{
int i, j, k, s, numSurfaces;
m4x4_t identity, nTransform;
picoModel_t *model;
picoShader_t *shader;
picoSurface_t *surface;
shaderInfo_t *si;
mapDrawSurface_t *ds;
bspDrawVert_t *dv;
char *picoShaderName;
char shaderName[ MAX_QPATH ];
picoVec_t *xyz, *normal, *st;
byte *color;
picoIndex_t *indexes;
remap_t *rm, *glob;
/* get model */
model = LoadModel( name, frame );
if( model == NULL )
return;
/* handle null matrix */
if( transform == NULL )
{
m4x4_identity( identity );
transform = identity;
}
/* hack: Stable-1_2 and trunk have differing row/column major matrix order
this transpose is necessary with Stable-1_2
uncomment the following line with old m4x4_t (non 1.3/spog_branch) code */
//% m4x4_transpose( transform );
/* create transform matrix for normals */
memcpy( nTransform, transform, sizeof( m4x4_t ) );
if( m4x4_invert( nTransform ) )
Sys_FPrintf( SYS_VRB, "WARNING: Can't invert model transform matrix, using transpose instead\n" );
m4x4_transpose( nTransform );
/* fix bogus lightmap scale */
if( lightmapScale <= 0.0f )
lightmapScale = 1.0f;
/* each surface on the model will become a new map drawsurface */
numSurfaces = PicoGetModelNumSurfaces( model );
//% Sys_FPrintf( SYS_VRB, "Model %s has %d surfaces\n", name, numSurfaces );
for( s = 0; s < numSurfaces; s++ )
{
/* get surface */
surface = PicoGetModelSurface( model, s );
if( surface == NULL )
continue;
/* only handle triangle surfaces initially (fixme: support patches) */
if( PicoGetSurfaceType( surface ) != PICO_TRIANGLES )
continue;
/* fix the surface's normals */
PicoFixSurfaceNormals( surface );
/* allocate a surface (ydnar: gs mods) */
ds = AllocDrawSurface( SURFACE_TRIANGLES );
ds->entityNum = eNum;
ds->castShadows = castShadows;
ds->recvShadows = recvShadows;
/* get shader name */
shader = PicoGetSurfaceShader( surface );
if( shader == NULL )
picoShaderName = "";
else
picoShaderName = PicoGetShaderName( shader );
/* handle shader remapping */
glob = NULL;
for( rm = remap; rm != NULL; rm = rm->next )
{
if( rm->from[ 0 ] == '*' && rm->from[ 1 ] == '\0' )
glob = rm;
else if( !Q_stricmp( picoShaderName, rm->from ) )
{
Sys_FPrintf( SYS_VRB, "Remapping %s to %s\n", picoShaderName, rm->to );
picoShaderName = rm->to;
glob = NULL;
break;
}
}
if( glob != NULL )
{
Sys_FPrintf( SYS_VRB, "Globbing %s to %s\n", picoShaderName, glob->to );
picoShaderName = glob->to;
}
/* shader renaming for sof2 */
if( renameModelShaders )
{
strcpy( shaderName, picoShaderName );
StripExtension( shaderName );
if( spawnFlags & 1 )
strcat( shaderName, "_RMG_BSP" );
else
strcat( shaderName, "_BSP" );
si = ShaderInfoForShader( shaderName );
}
else
si = ShaderInfoForShader( picoShaderName );
/* set shader */
ds->shaderInfo = si;
/* set lightmap scale */
ds->lightmapScale = lightmapScale;
/* force to meta? */
if( (si != NULL && si->forceMeta) || (spawnFlags & 4) ) /* 3rd bit */
ds->type = SURFACE_FORCED_META;
/* set particulars */
ds->numVerts = PicoGetSurfaceNumVertexes( surface );
ds->verts = safe_malloc( ds->numVerts * sizeof( ds->verts[ 0 ] ) );
memset( ds->verts, 0, ds->numVerts * sizeof( ds->verts[ 0 ] ) );
ds->numIndexes = PicoGetSurfaceNumIndexes( surface );
ds->indexes = safe_malloc( ds->numIndexes * sizeof( ds->indexes[ 0 ] ) );
memset( ds->indexes, 0, ds->numIndexes * sizeof( ds->indexes[ 0 ] ) );
/* copy vertexes */
for( i = 0; i < ds->numVerts; i++ )
{
/* get vertex */
dv = &ds->verts[ i ];
/* xyz and normal */
xyz = PicoGetSurfaceXYZ( surface, i );
VectorCopy( xyz, dv->xyz );
m4x4_transform_point( transform, dv->xyz );
normal = PicoGetSurfaceNormal( surface, i );
VectorCopy( normal, dv->normal );
m4x4_transform_normal( nTransform, dv->normal );
VectorNormalize( dv->normal, dv->normal );
/* ydnar: tek-fu celshading support for flat shaded shit */
if( flat )
{
dv->st[ 0 ] = si->stFlat[ 0 ];
dv->st[ 1 ] = si->stFlat[ 1 ];
}
/* ydnar: gs mods: added support for explicit shader texcoord generation */
else if( si->tcGen )
{
/* project the texture */
dv->st[ 0 ] = DotProduct( si->vecs[ 0 ], dv->xyz );
dv->st[ 1 ] = DotProduct( si->vecs[ 1 ], dv->xyz );
}
/* normal texture coordinates */
else
{
st = PicoGetSurfaceST( surface, 0, i );
dv->st[ 0 ] = st[ 0 ];
dv->st[ 1 ] = st[ 1 ];
}
/* set lightmap/color bits */
color = PicoGetSurfaceColor( surface, 0, i );
for( j = 0; j < MAX_LIGHTMAPS; j++ )
{
dv->lightmap[ j ][ 0 ] = 0.0f;
dv->lightmap[ j ][ 1 ] = 0.0f;
dv->color[ j ][ 0 ] = color[ 0 ];
dv->color[ j ][ 1 ] = color[ 1 ];
dv->color[ j ][ 2 ] = color[ 2 ];
dv->color[ j ][ 3 ] = color[ 3 ];
}
}
/* copy indexes */
indexes = PicoGetSurfaceIndexes( surface, 0 );
for( i = 0; i < ds->numIndexes; i++ )
ds->indexes[ i ] = indexes[ i ];
/* set cel shader */
ds->celShader = celShader;
/* ydnar: giant hack land: generate clipping brushes for model triangles */
if( si->clipModel || (spawnFlags & 2) ) /* 2nd bit */
{
vec3_t points[ 3 ], backs[ 3 ];
vec4_t plane, reverse, pa, pb, pc;
vec3_t nadir;
/* temp hack */
if( !si->clipModel &&
((si->compileFlags & C_TRANSLUCENT) || !(si->compileFlags & C_SOLID)) )
continue;
/* overflow check */
if( (nummapplanes + 64) >= (MAX_MAP_PLANES >> 1) )
continue;
/* walk triangle list */
for( i = 0; i < ds->numIndexes; i += 3 )
{
/* overflow hack */
if( (nummapplanes + 64) >= (MAX_MAP_PLANES >> 1) )
{
Sys_Printf( "WARNING: MAX_MAP_PLANES (%d) hit generating clip brushes for model %s.\n",
MAX_MAP_PLANES, name );
break;
}
/* make points and back points */
for( j = 0; j < 3; j++ )
{
/* get vertex */
dv = &ds->verts[ ds->indexes[ i + j ] ];
/* copy xyz */
VectorCopy( dv->xyz, points[ j ] );
VectorCopy( dv->xyz, backs[ j ] );
/* find nearest axial to normal and push back points opposite */
/* note: this doesn't work as well as simply using the plane of the triangle, below */
for( k = 0; k < 3; k++ )
{
if( fabs( dv->normal[ k ] ) > fabs( dv->normal[ (k + 1) % 3 ] ) &&
fabs( dv->normal[ k ] ) > fabs( dv->normal[ (k + 2) % 3 ] ) )
{
backs[ j ][ k ] += dv->normal[ k ] < 0.0f ? 64.0f : -64.0f;
break;
}
}
}
/* make plane for triangle */
if( PlaneFromPoints( plane, points[ 0 ], points[ 1 ], points[ 2 ] ) )
{
/* regenerate back points */
for( j = 0; j < 3; j++ )
{
/* get vertex */
dv = &ds->verts[ ds->indexes[ i + j ] ];
/* copy xyz */
VectorCopy( dv->xyz, backs[ j ] );
/* find nearest axial to plane normal and push back points opposite */
for( k = 0; k < 3; k++ )
{
if( fabs( plane[ k ] ) > fabs( plane[ (k + 1) % 3 ] ) &&
fabs( plane[ k ] ) > fabs( plane[ (k + 2) % 3 ] ) )
{
backs[ j ][ k ] += plane[ k ] < 0.0f ? 64.0f : -64.0f;
break;
}
}
}
/* make back plane */
VectorScale( plane, -1.0f, reverse );
reverse[ 3 ] = -(plane[ 3 ] - 1);
/* make back pyramid point */
VectorCopy( points[ 0 ], nadir );
VectorAdd( nadir, points[ 1 ], nadir );
VectorAdd( nadir, points[ 2 ], nadir );
VectorScale( nadir, 0.3333333333333f, nadir );
VectorMA( nadir, -2.0f, plane, nadir );
/* make 3 more planes */
//% if( PlaneFromPoints( pa, points[ 2 ], points[ 1 ], nadir ) &&
//% PlaneFromPoints( pb, points[ 1 ], points[ 0 ], nadir ) &&
//% PlaneFromPoints( pc, points[ 0 ], points[ 2 ], nadir ) )
if( PlaneFromPoints( pa, points[ 2 ], points[ 1 ], backs[ 1 ] ) &&
PlaneFromPoints( pb, points[ 1 ], points[ 0 ], backs[ 0 ] ) &&
PlaneFromPoints( pc, points[ 0 ], points[ 2 ], backs[ 2 ] ) )
{
/* build a brush */
buildBrush = AllocBrush( 48 );
buildBrush->entityNum = mapEntityNum;
buildBrush->original = buildBrush;
buildBrush->contentShader = si;
buildBrush->compileFlags = si->compileFlags;
buildBrush->contentFlags = si->contentFlags;
buildBrush->detail = qtrue;
/* set up brush sides */
buildBrush->numsides = 5;
for( j = 0; j < buildBrush->numsides; j++ )
buildBrush->sides[ j ].shaderInfo = si;
buildBrush->sides[ 0 ].planenum = FindFloatPlane( plane, plane[ 3 ], 3, points );
buildBrush->sides[ 1 ].planenum = FindFloatPlane( pa, pa[ 3 ], 1, &points[ 2 ] );
buildBrush->sides[ 2 ].planenum = FindFloatPlane( pb, pb[ 3 ], 1, &points[ 1 ] );
buildBrush->sides[ 3 ].planenum = FindFloatPlane( pc, pc[ 3 ], 1, &points[ 0 ] );
buildBrush->sides[ 4 ].planenum = FindFloatPlane( reverse, reverse[ 3 ], 3, points );
/* add to entity */
if( CreateBrushWindings( buildBrush ) )
{
AddBrushBevels();
//% EmitBrushes( buildBrush, NULL, NULL );
buildBrush->next = entities[ mapEntityNum ].brushes;
entities[ mapEntityNum ].brushes = buildBrush;
entities[ mapEntityNum ].numBrushes++;
}
else
free( buildBrush );
}
}
}
}
}
}
/*
AddTriangleModels()
adds misc_model surfaces to the bsp
*/
void AddTriangleModels( entity_t *e )
{
int num, frame, castShadows, recvShadows, spawnFlags;
entity_t *e2;
const char *targetName;
const char *target, *model, *value;
char shader[ MAX_QPATH ];
shaderInfo_t *celShader;
float temp, baseLightmapScale, lightmapScale;
vec3_t origin, scale, angles;
m4x4_t transform;
epair_t *ep;
remap_t *remap, *remap2;
char *split;
/* note it */
Sys_FPrintf( SYS_VRB, "--- AddTriangleModels ---\n" );
/* get current brush entity targetname */
if( e == entities )
targetName = "";
else
{
targetName = ValueForKey( e, "targetname" );
/* misc_model entities target non-worldspawn brush model entities */
if( targetName[ 0 ] == '\0' )
return;
}
/* get lightmap scale */
baseLightmapScale = FloatForKey( e, "_lightmapscale" );
if( baseLightmapScale <= 0.0f )
baseLightmapScale = 0.0f;
/* walk the entity list */
for( num = 1; num < numEntities; num++ )
{
/* get e2 */
e2 = &entities[ num ];
/* convert misc_models into raw geometry */
if( Q_stricmp( "misc_model", ValueForKey( e2, "classname" ) ) )
continue;
/* ydnar: added support for md3 models on non-worldspawn models */
target = ValueForKey( e2, "target" );
if( strcmp( target, targetName ) )
continue;
/* get model name */
model = ValueForKey( e2, "model" );
if( model[ 0 ] == '\0' )
{
Sys_Printf( "WARNING: misc_model at %i %i %i without a model key\n",
(int) origin[ 0 ], (int) origin[ 1 ], (int) origin[ 2 ] );
continue;
}
/* get model frame */
frame = IntForKey( e2, "_frame" );
/* worldspawn (and func_groups) default to cast/recv shadows in worldspawn group */
if( e == entities )
{
castShadows = WORLDSPAWN_CAST_SHADOWS;
recvShadows = WORLDSPAWN_RECV_SHADOWS;
}
/* other entities don't cast any shadows, but recv worldspawn shadows */
else
{
castShadows = ENTITY_CAST_SHADOWS;
recvShadows = ENTITY_RECV_SHADOWS;
}
/* get explicit shadow flags */
GetEntityShadowFlags( e2, e, &castShadows, &recvShadows );
/* get spawnflags */
spawnFlags = IntForKey( e2, "spawnflags" );
/* get origin */
GetVectorForKey( e2, "origin", origin );
VectorSubtract( origin, e->origin, origin ); /* offset by parent */
/* get scale */
scale[ 0 ] = scale[ 1 ] = scale[ 2 ] = 1.0f;
temp = FloatForKey( e2, "modelscale" );
if( temp != 0.0f )
scale[ 0 ] = scale[ 1 ] = scale[ 2 ] = temp;
value = ValueForKey( e2, "modelscale_vec" );
if( value[ 0 ] != '\0' )
sscanf( value, "%f %f %f", &scale[ 0 ], &scale[ 1 ], &scale[ 2 ] );
/* get "angle" (yaw) or "angles" (pitch yaw roll) */
angles[ 0 ] = angles[ 1 ] = angles[ 2 ] = 0.0f;
angles[ 2 ] = FloatForKey( e2, "angle" );
value = ValueForKey( e2, "angles" );
if( value[ 0 ] != '\0' )
sscanf( value, "%f %f %f", &angles[ 1 ], &angles[ 2 ], &angles[ 0 ] );
/* set transform matrix (thanks spog) */
m4x4_identity( transform );
m4x4_pivoted_transform_by_vec3( transform, origin, angles, eXYZ, scale, vec3_origin );
/* get shader remappings */
remap = NULL;
for( ep = e2->epairs; ep != NULL; ep = ep->next )
{
/* look for keys prefixed with "_remap" */
if( ep->key != NULL && ep->value != NULL &&
ep->key[ 0 ] != '\0' && ep->value[ 0 ] != '\0' &&
!Q_strncasecmp( ep->key, "_remap", 6 ) )
{
/* create new remapping */
remap2 = remap;
remap = safe_malloc( sizeof( *remap ) );
remap->next = remap2;
strcpy( remap->from, ep->value );
/* split the string */
split = strchr( remap->from, ';' );
if( split == NULL )
{
Sys_Printf( "WARNING: Shader _remap key found in misc_model without a ; character\n" );
free( remap );
remap = remap2;
continue;
}
/* store the split */
*split = '\0';
strcpy( remap->to, (split + 1) );
/* note it */
//% Sys_FPrintf( SYS_VRB, "Remapping %s to %s\n", remap->from, remap->to );
}
}
/* ydnar: cel shader support */
value = ValueForKey( e2, "_celshader" );
if( value[ 0 ] == '\0' )
value = ValueForKey( &entities[ 0 ], "_celshader" );
if( value[ 0 ] != '\0' )
{
sprintf( shader, "textures/%s", value );
celShader = ShaderInfoForShader( shader );
}
else
celShader = NULL;
/* get lightmap scale */
lightmapScale = FloatForKey( e2, "_lightmapscale" );
if( lightmapScale <= 0.0f )
lightmapScale = baseLightmapScale;
/* insert the model */
InsertModel( (char*) model, frame, transform, remap, celShader, mapEntityNum, castShadows, recvShadows, spawnFlags, lightmapScale );
/* free shader remappings */
while( remap != NULL )
{
remap2 = remap->next;
free( remap );
remap = remap2;
}
}
}

525
tools/quake3/q3map2/patch.c Normal file
View File

@@ -0,0 +1,525 @@
/* -------------------------------------------------------------------------------
Copyright (C) 1999-2006 Id Software, Inc. and contributors.
For a list of contributors, see the accompanying CONTRIBUTORS file.
This file is part of GtkRadiant.
GtkRadiant 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.
GtkRadiant 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 GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
----------------------------------------------------------------------------------
This code has been altered significantly from its original form, to support
several games based on the Quake III Arena engine, in the form of "Q3Map2."
------------------------------------------------------------------------------- */
/* marker */
#define PATCH_C
/* dependencies */
#include "q3map2.h"
/*
ExpandLongestCurve() - ydnar
finds length of quadratic curve specified and determines if length is longer than the supplied max
*/
#define APPROX_SUBDIVISION 8
static void ExpandLongestCurve( float *longestCurve, vec3_t a, vec3_t b, vec3_t c )
{
int i;
float t, len;
vec3_t ab, bc, ac, pt, last, delta;
/* calc vectors */
VectorSubtract( b, a, ab );
if( VectorNormalize( ab, ab ) < 0.125f )
return;
VectorSubtract( c, b, bc );
if( VectorNormalize( bc, bc ) < 0.125f )
return;
VectorSubtract( c, a, ac );
if( VectorNormalize( ac, ac ) < 0.125f )
return;
/* if all 3 vectors are the same direction, then this edge is linear, so we ignore it */
if( DotProduct( ab, bc ) > 0.99f && DotProduct( ab, ac ) > 0.99f )
return;
/* recalculate vectors */
VectorSubtract( b, a, ab );
VectorSubtract( c, b, bc );
/* determine length */
VectorCopy( a, last );
for( i = 0, len = 0.0f, t = 0.0f; i < APPROX_SUBDIVISION; i++, t += (1.0f / APPROX_SUBDIVISION) )
{
/* calculate delta */
delta[ 0 ] = ((1.0f - t) * ab[ 0 ]) + (t * bc[ 0 ]);
delta[ 1 ] = ((1.0f - t) * ab[ 1 ]) + (t * bc[ 1 ]);
delta[ 2 ] = ((1.0f - t) * ab[ 2 ]) + (t * bc[ 2 ]);
/* add to first point and calculate pt-pt delta */
VectorAdd( a, delta, pt );
VectorSubtract( pt, last, delta );
/* add it to length and store last point */
len += VectorLength( delta );
VectorCopy( pt, last );
}
/* longer? */
if( len > *longestCurve )
*longestCurve = len;
}
/*
ExpandMaxIterations() - ydnar
determines how many iterations a quadratic curve needs to be subdivided with to fit the specified error
*/
static void ExpandMaxIterations( int *maxIterations, int maxError, vec3_t a, vec3_t b, vec3_t c )
{
int i, j;
vec3_t prev, next, mid, delta, delta2;
float len, len2;
int numPoints, iterations;
vec3_t points[ MAX_EXPANDED_AXIS ];
/* initial setup */
numPoints = 3;
VectorCopy( a, points[ 0 ] );
VectorCopy( b, points[ 1 ] );
VectorCopy( c, points[ 2 ] );
/* subdivide */
for( i = 0; i + 2 < numPoints; i += 2 )
{
/* check subdivision limit */
if( numPoints + 2 >= MAX_EXPANDED_AXIS )
break;
/* calculate new curve deltas */
for( j = 0; j < 3; j++ )
{
prev[ j ] = points[ i + 1 ][ j ] - points[ i ][ j ];
next[ j ] = points[ i + 2 ][ j ] - points[ i + 1 ][ j ];
mid[ j ] = (points[ i ][ j ] + points[ i + 1 ][ j ] * 2.0f + points[ i + 2 ][ j ]) * 0.25f;
}
/* see if this midpoint is off far enough to subdivide */
VectorSubtract( points[ i + 1 ], mid, delta );
len = VectorLength( delta );
if( len < maxError )
continue;
/* subdivide */
numPoints += 2;
/* create new points */
for( j = 0; j < 3; j++ )
{
prev[ j ] = 0.5f * (points[ i ][ j ] + points[ i + 1 ][ j ]);
next[ j ] = 0.5f * (points[ i + 1 ][ j ] + points[ i + 2 ][ j ]);
mid[ j ] = 0.5f * (prev[ j ] + next[ j ]);
}
/* push points out */
for( j = numPoints - 1; j > i + 3; j-- )
VectorCopy( points[ j - 2 ], points[ j ] );
/* insert new points */
VectorCopy( prev, points[ i + 1 ] );
VectorCopy( mid, points[ i + 2 ] );
VectorCopy( next, points[ i + 3 ] );
/* back up and recheck this set again, it may need more subdivision */
i -= 2;
}
/* put the line on the curve */
for( i = 1; i < numPoints; i += 2 )
{
for( j = 0; j < 3; j++ )
{
prev[ j ] = 0.5f * (points[ i ][ j ] + points[ i + 1 ][ j ] );
next[ j ] = 0.5f * (points[ i ][ j ] + points[ i - 1 ][ j ] );
points[ i ][ j ] = 0.5f * (prev[ j ] + next[ j ]);
}
}
/* eliminate linear sections */
for( i = 0; i + 2 < numPoints; i++ )
{
/* create vectors */
VectorSubtract( points[ i + 1 ], points[ i ], delta );
len = VectorNormalize( delta, delta );
VectorSubtract( points[ i + 2 ], points[ i + 1 ], delta2 );
len2 = VectorNormalize( delta2, delta2 );
/* if either edge is degenerate, then eliminate it */
if( len < 0.0625f || len2 < 0.0625f || DotProduct( delta, delta2 ) >= 1.0f )
{
for( j = i + 1; j + 1 < numPoints; j++ )
VectorCopy( points[ j + 1 ], points[ j ] );
numPoints--;
continue;
}
}
/* the number of iterations is 2^(points - 1) - 1 */
numPoints >>= 1;
iterations = 0;
while( numPoints > 1 )
{
numPoints >>= 1;
iterations++;
}
/* more? */
if( iterations > *maxIterations )
*maxIterations = iterations;
}
/*
ParsePatch()
creates a mapDrawSurface_t from the patch text
*/
void ParsePatch( qboolean onlyLights )
{
vec_t info[ 5 ];
int i, j, k;
parseMesh_t *pm;
char texture[ MAX_QPATH ];
char shader[ MAX_QPATH ];
mesh_t m;
bspDrawVert_t *verts;
epair_t *ep;
vec4_t delta, delta2, delta3;
qboolean degenerate;
float longestCurve;
int maxIterations;
MatchToken( "{" );
/* get texture */
GetToken( qtrue );
strcpy( texture, token );
Parse1DMatrix( 5, info );
m.width = info[0];
m.height = info[1];
m.verts = verts = safe_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 );
/* ydnar: fix colors */
for( k = 0; k < MAX_LIGHTMAPS; k++ )
{
verts[ i * m.width + j ].color[ k ][ 0 ] = 255;
verts[ i * m.width + j ].color[ k ][ 1 ] = 255;
verts[ i * m.width + j ].color[ k ][ 2 ] = 255;
verts[ i * m.width + j ].color[ k ][ 3 ] = 255;
}
}
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( "}" );
/* short circuit */
if( noCurveBrushes || onlyLights )
return;
/* ydnar: delete and warn about degenerate patches */
j = (m.width * m.height);
VectorClear( delta );
delta[ 3 ] = 0;
degenerate = qtrue;
/* find first valid vector */
for( i = 1; i < j && delta[ 3 ] == 0; i++ )
{
VectorSubtract( m.verts[ 0 ].xyz, m.verts[ i ].xyz, delta );
delta[ 3 ] = VectorNormalize( delta, delta );
}
/* secondary degenerate test */
if( delta[ 3 ] == 0 )
degenerate = qtrue;
else
{
/* if all vectors match this or are zero, then this is a degenerate patch */
for( i = 1; i < j && degenerate == qtrue; i++ )
{
VectorSubtract( m.verts[ 0 ].xyz, m.verts[ i ].xyz, delta2 );
delta2[ 3 ] = VectorNormalize( delta2, delta2 );
if( delta2[ 3 ] != 0 )
{
/* create inverse vector */
VectorCopy( delta2, delta3 );
delta3[ 3 ] = delta2[ 3 ];
VectorInverse( delta3 );
/* compare */
if( VectorCompare( delta, delta2 ) == qfalse && VectorCompare( delta, delta3 ) == qfalse )
degenerate = qfalse;
}
}
}
/* warn and select degenerate patch */
if( degenerate )
{
xml_Select( "degenerate patch", mapEnt->mapEntityNum, entitySourceBrushes, qfalse );
free( m.verts );
return;
}
/* find longest curve on the mesh */
longestCurve = 0.0f;
maxIterations = 0;
for( j = 0; j + 2 < m.width; j += 2 )
{
for( i = 0; i + 2 < m.height; i += 2 )
{
ExpandLongestCurve( &longestCurve, verts[ i * m.width + j ].xyz, verts[ i * m.width + (j + 1) ].xyz, verts[ i * m.width + (j + 2) ].xyz ); /* row */
ExpandLongestCurve( &longestCurve, verts[ i * m.width + j ].xyz, verts[ (i + 1) * m.width + j ].xyz, verts[ (i + 2) * m.width + j ].xyz ); /* col */
ExpandMaxIterations( &maxIterations, patchSubdivisions, verts[ i * m.width + j ].xyz, verts[ i * m.width + (j + 1) ].xyz, verts[ i * m.width + (j + 2) ].xyz ); /* row */
ExpandMaxIterations( &maxIterations, patchSubdivisions, verts[ i * m.width + j ].xyz, verts[ (i + 1) * m.width + j ].xyz, verts[ (i + 2) * m.width + j ].xyz ); /* col */
}
}
/* allocate patch mesh */
pm = safe_malloc( sizeof( *pm ) );
memset( pm, 0, sizeof( *pm ) );
/* ydnar: add entity/brush numbering */
pm->entityNum = mapEnt->mapEntityNum;
pm->brushNum = entitySourceBrushes;
/* set shader */
sprintf( shader, "textures/%s", texture );
pm->shaderInfo = ShaderInfoForShader( shader );
/* set mesh */
pm->mesh = m;
/* set longest curve */
pm->longestCurve = longestCurve;
pm->maxIterations = maxIterations;
/* link to the entity */
pm->next = mapEnt->patches;
mapEnt->patches = pm;
}
/*
GrowGroup_r()
recursively adds patches to a lod group
*/
static void GrowGroup_r( parseMesh_t *pm, int patchNum, int patchCount, parseMesh_t **meshes, byte *bordering, byte *group )
{
int i;
const byte *row;
/* early out check */
if( group[ patchNum ] )
return;
/* set it */
group[ patchNum ] = 1;
row = bordering + patchNum * patchCount;
/* check maximums */
if( meshes[ patchNum ]->longestCurve > pm->longestCurve )
pm->longestCurve = meshes[ patchNum ]->longestCurve;
if( meshes[ patchNum ]->maxIterations > pm->maxIterations )
pm->maxIterations = meshes[ patchNum ]->maxIterations;
/* walk other patches */
for( i = 0; i < patchCount; i++ )
{
if( row[ i ] )
GrowGroup_r( pm, i, patchCount, meshes, 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 )
{
int i, j, k, l, c1, c2;
parseMesh_t *pm;
parseMesh_t *check, *scan;
mapDrawSurface_t *ds;
int patchCount, groupCount;
bspDrawVert_t *v1, *v2;
vec3_t bounds[ 2 ];
byte *bordering;
/* ydnar: mac os x fails with these if not static */
MAC_STATIC parseMesh_t *meshes[ MAX_MAP_DRAW_SURFS ];
MAC_STATIC qb_t grouped[ MAX_MAP_DRAW_SURFS ];
MAC_STATIC byte group[ MAX_MAP_DRAW_SURFS ];
/* note it */
Sys_FPrintf( SYS_VRB, "--- PatchMapDrawSurfs ---\n" );
patchCount = 0;
for ( pm = e->patches ; pm ; pm = pm->next ) {
meshes[patchCount] = pm;
patchCount++;
}
if ( !patchCount ) {
return;
}
bordering = safe_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, patchCount );
groupCount = 0;
for ( i = 0; i < patchCount; i++ )
{
/* get patch */
scan = meshes[ i ];
/* start a new group */
if( !grouped[ i ] )
groupCount++;
/* recursively find all patches that belong in the same group */
memset( group, 0, patchCount );
GrowGroup_r( scan, i, patchCount, meshes, bordering, group );
/* bound them */
ClearBounds( bounds[ 0 ], bounds[ 1 ] );
for( j = 0; j < patchCount; j++ )
{
if ( group[ j ] )
{
grouped[ j ] = qtrue;
check = meshes[ j ];
c1 = check->mesh.width * check->mesh.height;
v1 = check->mesh.verts;
for( k = 0; k < c1; k++, v1++ )
AddPointToBounds( v1->xyz, bounds[ 0 ], bounds[ 1 ] );
}
}
/* debug code */
//% Sys_Printf( "Longest curve: %f Iterations: %d\n", scan->longestCurve, scan->maxIterations );
/* create drawsurf */
scan->grouped = qtrue;
ds = DrawSurfaceForMesh( e, scan, NULL ); /* ydnar */
VectorCopy( bounds[ 0 ], ds->bounds[ 0 ] );
VectorCopy( bounds[ 1 ], ds->bounds[ 1 ] );
}
/* emit some statistics */
Sys_FPrintf( SYS_VRB, "%9d patches\n", patchCount );
Sys_FPrintf( SYS_VRB, "%9d patch LOD groups\n", groupCount );
}

View File

@@ -0,0 +1,462 @@
/* -------------------------------------------------------------------------------
Copyright (C) 1999-2006 Id Software, Inc. and contributors.
For a list of contributors, see the accompanying CONTRIBUTORS file.
This file is part of GtkRadiant.
GtkRadiant 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.
GtkRadiant 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 GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
----------------------------------------------------------------------------------
This code has been altered significantly from its original form, to support
several games based on the Quake III Arena engine, in the form of "Q3Map2."
------------------------------------------------------------------------------- */
/* marker */
#define PATH_INIT_C
/* dependencies */
#include "q3map2.h"
/* path support */
#define MAX_BASE_PATHS 10
#define MAX_GAME_PATHS 10
char *homePath;
char installPath[ MAX_OS_PATH ];
int numBasePaths;
char *basePaths[ MAX_BASE_PATHS ];
int numGamePaths;
char *gamePaths[ MAX_GAME_PATHS ];
/*
some of this code is based off the original q3map port from loki
and finds various paths. moved here from bsp.c for clarity.
*/
/*
PathLokiGetHomeDir()
gets the user's home dir (for ~/.q3a)
*/
char *LokiGetHomeDir( void )
{
#ifndef Q_UNIX
return NULL;
#else
char *home;
uid_t id;
struct passwd *pwd;
/* get the home environment variable */
home = getenv( "HOME" );
if( home == NULL )
{
/* do some more digging */
id = getuid();
setpwent();
while( (pwd = getpwent()) != NULL )
{
if( pwd->pw_uid == id )
{
home = pwd->pw_dir;
break;
}
}
endpwent();
}
/* return it */
return home;
#endif
}
/*
PathLokiInitPaths()
initializes some paths on linux/os x
*/
void LokiInitPaths( char *argv0 )
{
#ifndef Q_UNIX
/* this is kinda crap, but hey */
strcpy( installPath, "../" );
#else
char temp[ MAX_OS_PATH ];
char *home;
char *path;
char *last;
qboolean found;
/* get home dir */
home = LokiGetHomeDir();
if( home == NULL )
home = ".";
/* do some path divining */
strcpy( temp, argv0 );
if( strrchr( temp, '/' ) )
argv0 = strrchr( argv0, '/' ) + 1;
else
{
/* get path environment variable */
path = getenv( "PATH" );
/* minor setup */
last[ 0 ] = path[ 0 ];
last[ 1 ] = '\0';
found = qfalse;
/* go through each : segment of path */
while( last[ 0 ] != '\0' && found == qfalse )
{
/* null out temp */
temp[ 0 ] = '\0';
/* find next chunk */
last = strchr( path, ':' );
if( last == NULL )
last = path + strlen( path );
/* found home dir candidate */
if( *path == '~' )
{
strcpy( temp, home );
path++;
}
/* concatenate */
if( last > (path + 1) )
{
strncat( temp, path, (last - path) );
strcat( temp, "/" );
}
strcat( temp, "./" );
strcat( temp, argv0 );
/* verify the path */
if( access( temp, X_OK ) == 0 )
found++;
path = last + 1;
}
}
/* flake */
if( realpath( temp, installPath ) )
{
/* q3map is in "tools/" */
*(strrchr( installPath, '/' )) = '\0';
*(strrchr( installPath, '/' ) + 1) = '\0';
}
/* set home path */
homePath = home;
#endif
}
/*
CleanPath() - ydnar
cleans a dos path \ -> /
*/
void CleanPath( char *path )
{
while( *path )
{
if( *path == '\\' )
*path = '/';
path++;
}
}
/*
GetGame() - ydnar
gets the game_t based on a -game argument
returns NULL if no match found
*/
game_t *GetGame( char *arg )
{
int i;
/* dummy check */
if( arg == NULL || arg[ 0 ] == '\0' )
return NULL;
/* joke */
if( !Q_stricmp( arg, "quake1" ) ||
!Q_stricmp( arg, "quake2" ) ||
!Q_stricmp( arg, "unreal" ) ||
!Q_stricmp( arg, "ut2k3" ) ||
!Q_stricmp( arg, "dn3d" ) ||
!Q_stricmp( arg, "dnf" ) ||
!Q_stricmp( arg, "hl" ) )
{
Sys_Printf( "April fools, silly rabbit!\n" );
exit( 0 );
}
/* test it */
i = 0;
while( games[ i ].arg != NULL )
{
if( Q_stricmp( arg, games[ i ].arg ) == 0 )
return &games[ i ];
i++;
}
/* no matching game */
return NULL;
}
/*
AddBasePath() - ydnar
adds a base path to the list
*/
void AddBasePath( char *path )
{
/* dummy check */
if( path == NULL || path[ 0 ] == '\0' || numBasePaths >= MAX_BASE_PATHS )
return;
/* add it to the list */
basePaths[ numBasePaths ] = safe_malloc( strlen( path ) + 1 );
strcpy( basePaths[ numBasePaths ], path );
CleanPath( basePaths[ numBasePaths ] );
numBasePaths++;
}
/*
AddHomeBasePath() - ydnar
adds a base path to the beginning of the list, prefixed by ~/
*/
void AddHomeBasePath( char *path )
{
#ifdef Q_UNIX
int i;
char temp[ MAX_OS_PATH ];
/* dummy check */
if( path == NULL || path[ 0 ] == '\0' )
return;
/* make a hole */
for( i = 0; i < (MAX_BASE_PATHS - 1); i++ )
basePaths[ i + 1 ] = basePaths[ i ];
/* concatenate home dir and path */
sprintf( temp, "%s/%s", homePath, path );
/* add it to the list */
basePaths[ 0 ] = safe_malloc( strlen( temp ) + 1 );
strcpy( basePaths[ 0 ], temp );
CleanPath( basePaths[ 0 ] );
numBasePaths++;
#endif
}
/*
AddGamePath() - ydnar
adds a game path to the list
*/
void AddGamePath( char *path )
{
/* dummy check */
if( path == NULL || path[ 0 ] == '\0' || numGamePaths >= MAX_GAME_PATHS )
return;
/* add it to the list */
gamePaths[ numGamePaths ] = safe_malloc( strlen( path ) + 1 );
strcpy( gamePaths[ numGamePaths ], path );
CleanPath( gamePaths[ numGamePaths ] );
numGamePaths++;
}
/*
InitPaths() - ydnar
cleaned up some of the path initialization code from bsp.c
will remove any arguments it uses
*/
void InitPaths( int *argc, char **argv )
{
int i, j, k, len, len2;
char temp[ MAX_OS_PATH ];
/* note it */
Sys_FPrintf( SYS_VRB, "--- InitPaths ---\n" );
/* get the install path for backup */
LokiInitPaths( argv[ 0 ] );
/* set game to default (q3a) */
game = &games[ 0 ];
numBasePaths = 0;
numGamePaths = 0;
/* parse through the arguments and extract those relevant to paths */
for( i = 0; i < *argc; i++ )
{
/* check for null */
if( argv[ i ] == NULL )
continue;
/* -game */
if( strcmp( argv[ i ], "-game" ) == 0 )
{
if( ++i >= *argc )
Error( "Out of arguments: No game specified after %s", argv[ i - 1 ] );
argv[ i - 1 ] = NULL;
game = GetGame( argv[ i ] );
if( game == NULL )
game = &games[ 0 ];
argv[ i ] = NULL;
}
/* -fs_basepath */
else if( strcmp( argv[ i ], "-fs_basepath" ) == 0 )
{
if( ++i >= *argc )
Error( "Out of arguments: No path specified after %s.", argv[ i - 1 ] );
argv[ i - 1 ] = NULL;
AddBasePath( argv[ i ] );
argv[ i ] = NULL;
}
/* -fs_game */
else if( strcmp( argv[ i ], "-fs_game" ) == 0 )
{
if( ++i >= *argc )
Error( "Out of arguments: No path specified after %s.", argv[ i - 1 ] );
argv[ i - 1 ] = NULL;
AddGamePath( argv[ i ] );
argv[ i ] = NULL;
}
}
/* remove processed arguments */
for( i = 0, j = 0, k = 0; i < *argc && j < *argc; i++, j++ )
{
for( j; j < *argc && argv[ j ] == NULL; j++ );
argv[ i ] = argv[ j ];
if( argv[ i ] != NULL )
k++;
}
*argc = k;
/* add standard game path */
AddGamePath( game->gamePath );
/* if there is no base path set, figure it out */
if( numBasePaths == 0 )
{
/* this is another crappy replacement for SetQdirFromPath() */
len2 = strlen( game->magic );
for( i = 0; i < *argc && numBasePaths == 0; i++ )
{
/* extract the arg */
strcpy( temp, argv[ i ] );
CleanPath( temp );
len = strlen( temp );
Sys_FPrintf( SYS_VRB, "Searching for \"%s\" in \"%s\" (%d)...\n", game->magic, temp, i );
/* this is slow, but only done once */
for( j = 0; j < (len - len2); j++ )
{
/* check for the game's magic word */
if( Q_strncasecmp( &temp[ j ], game->magic, len2 ) == 0 )
{
/* now find the next slash and nuke everything after it */
while( temp[ ++j ] != '/' && temp[ j ] != '\0' );
temp[ j ] = '\0';
/* add this as a base path */
AddBasePath( temp );
break;
}
}
}
/* add install path */
if( numBasePaths == 0 )
AddBasePath( installPath );
/* check again */
if( numBasePaths == 0 )
Error( "Failed to find a valid base path." );
}
/* this only affects unix */
AddHomeBasePath( game->homeBasePath );
/* initialize vfs paths */
if( numBasePaths > MAX_BASE_PATHS )
numBasePaths = MAX_BASE_PATHS;
if( numGamePaths > MAX_GAME_PATHS )
numGamePaths = MAX_GAME_PATHS;
/* walk the list of game paths */
for( j = 0; j < numGamePaths; j++ )
{
/* walk the list of base paths */
for( i = 0; i < numBasePaths; i++ )
{
/* create a full path and initialize it */
sprintf( temp, "%s/%s/", basePaths[ i ], gamePaths[ j ] );
vfsInitDirectory( temp );
}
}
/* done */
Sys_Printf( "\n" );
}

View File

@@ -0,0 +1,971 @@
/* -------------------------------------------------------------------------------
Copyright (C) 1999-2006 Id Software, Inc. and contributors.
For a list of contributors, see the accompanying CONTRIBUTORS file.
This file is part of GtkRadiant.
GtkRadiant 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.
GtkRadiant 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 GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
----------------------------------------------------------------------------------
This code has been altered significantly from its original form, to support
several games based on the Quake III Arena engine, in the form of "Q3Map2."
------------------------------------------------------------------------------- */
/* marker */
#define PORTALS_C
/* dependencies */
#include "q3map2.h"
/* ydnar: to fix broken portal windings */
extern qboolean FixWinding( winding_t *w );
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 = safe_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);
}
/*
PortalPassable
returns true if the portal has non-opaque leafs on both sides
*/
qboolean PortalPassable( portal_t *p )
{
/* is this to global outside leaf? */
if( !p->onnode )
return qfalse;
/* this should never happen */
if( p->nodes[ 0 ]->planenum != PLANENUM_LEAF ||
p->nodes[ 1 ]->planenum != PLANENUM_LEAF )
Error( "Portal_EntityFlood: not a leaf" );
/* ydnar: added antiportal to supress portal generation for visibility blocking */
if( p->compileFlags & C_ANTIPORTAL )
return qfalse;
/* both leaves on either side of the portal must be passable */
if( p->nodes[ 0 ]->opaque == qfalse && p->nodes[ 1 ]->opaque == qfalse )
return qtrue;
/* otherwise this isn't a passable portal */
return qfalse;
}
int c_tinyportals;
int c_badportals; /* ydnar */
/*
=============
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++)
Sys_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;
}
/* ydnar: adding this here to fix degenerate windings */
#if 0
if( FixWinding( w ) == qfalse )
{
c_badportals++;
FreeWinding( w );
return;
}
#endif
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->compileFlags = node->compileFlags;
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])
{
Sys_Printf ("WARNING: node without a volume\n");
Sys_Printf("node has %d tiny portals\n", node->tinyportals);
Sys_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)
{
if(node->portals && node->portals->winding)
xml_Winding("WARNING: Node With Unbounded Volume", node->portals->winding->p, node->portals->winding->numpoints, qfalse);
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)
{
Sys_FPrintf (SYS_VRB, "--- MakeTreePortals ---\n");
MakeHeadnodePortals (tree);
MakeTreePortals_r (tree->headnode);
Sys_FPrintf( SYS_VRB, "%9d tiny portals\n", c_tinyportals );
Sys_FPrintf( SYS_VRB, "%9d bad portals\n", c_badportals ); /* ydnar */
}
/*
=========================================================
FLOOD ENTITIES
=========================================================
*/
int c_floodedleafs;
/*
=============
FloodPortals_r
=============
*/
void FloodPortals_r( node_t *node, int dist, qboolean skybox )
{
int s;
portal_t *p;
if( skybox )
node->skybox = skybox;
if( node->occupied || 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, skybox );
}
}
/*
=============
PlaceOccupant
=============
*/
qboolean PlaceOccupant( node_t *headnode, vec3_t origin, entity_t *occupant, qboolean skybox )
{
vec_t d;
node_t *node;
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;
node->skybox = skybox;
FloodPortals_r( node, 1, skybox );
return qtrue;
}
/*
=============
FloodEntities
Marks all nodes that can be reached by entites
=============
*/
qboolean FloodEntities( tree_t *tree )
{
int i, s;
vec3_t origin, offset, scale, angles;
qboolean r, inside, tripped, skybox;
node_t *headnode;
entity_t *e;
const char *value;
headnode = tree->headnode;
Sys_FPrintf( SYS_VRB,"--- FloodEntities ---\n" );
inside = qfalse;
tree->outside_node.occupied = 0;
tripped = qfalse;
c_floodedleafs = 0;
for( i = 1; i < numEntities; i++ )
{
/* get entity */
e = &entities[ i ];
/* get origin */
GetVectorForKey( e, "origin", origin );
if( VectorCompare( origin, vec3_origin ) )
continue;
/* handle skybox entities */
value = ValueForKey( e, "classname" );
if( !Q_stricmp( value, "_skybox" ) )
{
skybox = qtrue;
skyboxPresent = qtrue;
/* invert origin */
VectorScale( origin, -1.0f, offset );
/* get scale */
VectorSet( scale, 64.0f, 64.0f, 64.0f );
value = ValueForKey( e, "_scale" );
if( value[ 0 ] != '\0' )
{
s = sscanf( value, "%f %f %f", &scale[ 0 ], &scale[ 1 ], &scale[ 2 ] );
if( s == 1 )
{
scale[ 1 ] = scale[ 0 ];
scale[ 2 ] = scale[ 0 ];
}
}
/* get "angle" (yaw) or "angles" (pitch yaw roll) */
VectorClear( angles );
angles[ 2 ] = FloatForKey( e, "angle" );
value = ValueForKey( e, "angles" );
if( value[ 0 ] != '\0' )
sscanf( value, "%f %f %f", &angles[ 1 ], &angles[ 2 ], &angles[ 0 ] );
/* set transform matrix (thanks spog) */
m4x4_identity( skyboxTransform );
m4x4_pivoted_transform_by_vec3( skyboxTransform, offset, angles, eXYZ, scale, origin );
}
else
skybox = qfalse;
/* nudge off floor */
origin[ 2 ] += 1;
/* debugging code */
//% if( i == 1 )
//% origin[ 2 ] += 4096;
/* find leaf */
r = PlaceOccupant( headnode, origin, e, skybox );
if( r )
inside = qtrue;
if( (!r || tree->outside_node.occupied) && !tripped )
{
xml_Select( "Entity leaked", e->mapEntityNum, 0, qfalse );
tripped = qtrue;
}
}
Sys_FPrintf( SYS_VRB, "%9d flooded leafs\n", c_floodedleafs );
if( !inside )
Sys_FPrintf( SYS_VRB, "no entities in open -- no filling\n" );
else if( tree->outside_node.occupied )
Sys_FPrintf( SYS_VRB, "entity reached from outside -- no filling\n" );
return (qboolean) (inside && !tree->outside_node.occupied);
}
/*
=========================================================
FLOOD AREAS
=========================================================
*/
int c_areas;
/*
FloodAreas_r()
floods through leaf portals to tag leafs with an area
*/
void FloodAreas_r( node_t *node )
{
int s;
portal_t *p;
brush_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 already 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 )
{
Sys_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;
if( node->cluster == -1 )
return;
node->area = c_areas;
/* ydnar: skybox nodes set the skybox area */
if( node->skybox )
skyboxArea = c_areas;
for( p = node->portals; p; p = p->next[ s ] )
{
s = (p->nodes[1] == node);
/* ydnar: allow areaportal portals to block area flow */
if( p->compileFlags & C_AREAPORTAL )
continue;
if( !PortalPassable( 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 || node->areaportal || node->area != -1 )
return;
FloodAreas_r( node );
c_areas++;
}
/*
=============
CheckAreas_r
=============
*/
void CheckAreas_r (node_t *node)
{
brush_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)
Sys_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)
Sys_Printf ("WARNING: areaportal brush %i doesn't touch two areas\n", b->brushNum);
}
}
/*
FloodSkyboxArea_r() - ydnar
sets all nodes with the skybox area to skybox
*/
void FloodSkyboxArea_r( node_t *node )
{
if( skyboxArea < 0 )
return;
if( node->planenum != PLANENUM_LEAF )
{
FloodSkyboxArea_r( node->children[ 0 ] );
FloodSkyboxArea_r( node->children[ 1 ] );
return;
}
if( node->opaque || node->area != skyboxArea )
return;
node->skybox = qtrue;
}
/*
FloodAreas()
mark each leaf with an area, bounded by C_AREAPORTAL
*/
void FloodAreas( tree_t *tree )
{
Sys_FPrintf( SYS_VRB,"--- FloodAreas ---\n" );
FindAreas_r( tree->headnode );
/* ydnar: flood all skybox nodes */
FloodSkyboxArea_r( tree->headnode );
/* check for areaportal brushes that don't touch two areas */
/* ydnar: fix this rather than just silence the warnings */
//% CheckAreas_r( tree->headnode );
Sys_FPrintf( SYS_VRB, "%9d 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;
Sys_FPrintf( SYS_VRB,"--- FillOutside ---\n" );
FillOutside_r( headnode );
Sys_FPrintf( SYS_VRB,"%9d solid leafs\n", c_solid );
Sys_Printf( "%9d leafs filled\n", c_outside );
Sys_FPrintf( SYS_VRB, "%9d inside leafs\n", c_inside );
}
//==============================================================

View File

@@ -0,0 +1,292 @@
/* -------------------------------------------------------------------------------
Copyright (C) 1999-2006 Id Software, Inc. and contributors.
For a list of contributors, see the accompanying CONTRIBUTORS file.
This file is part of GtkRadiant.
GtkRadiant 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.
GtkRadiant 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 GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
----------------------------------------------------------------------------------
This code has been altered significantly from its original form, to support
several games based on the Quake III Arena engine, in the form of "Q3Map2."
------------------------------------------------------------------------------- */
/* marker */
#define PRTFILE_C
/* dependencies */
#include "q3map2.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 (!PortalPassable(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);
/* ydnar: added this change to make antiportals work */
if( p->compileFlags & C_HINT )
fprintf( pf, "1 " );
else
fprintf( pf, "0 " );
/* write the winding */
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 (PortalPassable(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 (PortalPassable(p))
num_visportals++;
else
num_solidfaces++;
p = p->next[0];
}
else
{
if (!PortalPassable(p))
num_solidfaces++;
p = p->next[1];
}
}
}
/*
================
NumberClusters
================
*/
void NumberClusters(tree_t *tree) {
num_visclusters = 0;
num_visportals = 0;
num_solidfaces = 0;
Sys_FPrintf (SYS_VRB,"--- NumberClusters ---\n");
// set the cluster field in every leaf and count the total number of portals
NumberLeafs_r (tree->headnode);
Sys_FPrintf( SYS_VRB, "%9d visclusters\n", num_visclusters );
Sys_FPrintf( SYS_VRB, "%9d visportals\n", num_visportals );
Sys_FPrintf( SYS_VRB, "%9d solidfaces\n", num_solidfaces );
}
/*
================
WritePortalFile
================
*/
void WritePortalFile (tree_t *tree)
{
char filename[1024];
Sys_FPrintf (SYS_VRB,"--- WritePortalFile ---\n");
// write the file
sprintf (filename, "%s.prt", source);
Sys_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);
}

View File

@@ -0,0 +1,378 @@
# Microsoft Developer Studio Project File - Name="q3map2" - Package Owner=<4>
# Microsoft Developer Studio Generated Build File, Format Version 6.00
# ** DO NOT EDIT **
# TARGTYPE "Win32 (x86) Console Application" 0x0103
CFG=Q3MAP2 - WIN32 RELEASE
!MESSAGE This is not a valid makefile. To build this project using NMAKE,
!MESSAGE use the Export Makefile command and run
!MESSAGE
!MESSAGE NMAKE /f "q3map2.mak".
!MESSAGE
!MESSAGE You can specify a configuration when running NMAKE
!MESSAGE by defining the macro CFG on the command line. For example:
!MESSAGE
!MESSAGE NMAKE /f "q3map2.mak" CFG="Q3MAP2 - WIN32 RELEASE"
!MESSAGE
!MESSAGE Possible choices for configuration are:
!MESSAGE
!MESSAGE "q3map2 - Win32 Release" (based on "Win32 (x86) Console Application")
!MESSAGE "q3map2 - Win32 Debug" (based on "Win32 (x86) Console Application")
!MESSAGE
# Begin Project
# PROP AllowPerConfigDependencies 0
# PROP Scc_ProjName "q3map2"
# PROP Scc_LocalPath ".."
CPP=cl.exe
RSC=rc.exe
!IF "$(CFG)" == "q3map2 - Win32 Release"
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 0
# PROP BASE Output_Dir "Release"
# PROP BASE Intermediate_Dir "Release"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 0
# PROP Output_Dir "Release"
# PROP Intermediate_Dir "Release"
# PROP Ignore_Export_Lib 0
# PROP Target_Dir ""
F90=df.exe
# ADD BASE F90 /include:"Release/"
# ADD F90 /include:"Release/"
# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
# ADD CPP /nologo /MD /W3 /Zi /O2 /I "..\..\..\include" /I "..\common" /I "..\..\..\libs" /I "..\..\..\..\libxml2\include" /I "..\q3map2" /I "..\..\..\..\libpng" /I "..\..\..\..\zlib" /I "..\..\..\..\gtk2-win32\include\glib-2.0" /I "..\..\..\..\gtk2-win32\lib\glib-2.0\include" /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /FR /FD /c
# SUBTRACT CPP /WX /YX
# ADD BASE RSC /l 0x409 /d "NDEBUG"
# ADD RSC /l 0x409 /d "NDEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LINK32=link.exe
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
# ADD LINK32 glib-2.0.lib wsock32.lib jpeg6.lib l_net.lib mathlib.lib md5lib.lib zlib.lib libpng12.lib picomodel.lib ddslib.lib /nologo /stack:0x400000 /subsystem:console /map /machine:I386 /libpath:"..\..\..\libs\ddslib\release" /libpath:"..\..\..\libs\md5lib\release" /libpath:"..\..\..\libs\mathlib\release" /libpath:"..\..\..\libs\pak\release" /libpath:"..\..\..\libs\jpeg6\release" /libpath:"..\..\..\libs\l_net\release" /libpath:"..\..\..\..\libxml2\win32\libxml2\release_so" /libpath:"..\..\..\..\libpng\projects\msvc\libpng___Win32_Release" /libpath:"..\..\..\..\libpng\projects\msvc\zlib___Win32_Release" /libpath:"../../../libs/picomodel/release" /libpath:"..\..\..\..\gtk2-win32\lib\\"
# SUBTRACT LINK32 /pdb:none /debug
# Begin Special Build Tool
SOURCE="$(InputPath)"
PostBuild_Desc=Python post build
PostBuild_Cmds=cd ..\..\.. && run_python.bat win32_install.py release Q3Map2
# End Special Build Tool
!ELSEIF "$(CFG)" == "q3map2 - Win32 Debug"
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 1
# PROP BASE Output_Dir "Debug"
# PROP BASE Intermediate_Dir "Debug"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 1
# PROP Output_Dir "Debug"
# PROP Intermediate_Dir "Debug"
# PROP Ignore_Export_Lib 0
# PROP Target_Dir ""
F90=df.exe
# ADD BASE F90 /include:"Debug/"
# ADD F90 /browser /include:"Debug/"
# ADD BASE CPP /nologo /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
# ADD CPP /nologo /MDd /W3 /Gm /ZI /Od /I "..\..\..\include" /I "..\common" /I "..\..\..\libs" /I "..\..\..\..\libxml2\include" /I "..\..\..\..\libpng" /I "..\..\..\..\zlib" /I "..\..\..\..\gtk2-win32\include\glib-2.0" /I "..\..\..\..\gtk2-win32\lib\glib-2.0\include" /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /FR /FD /c
# SUBTRACT CPP /YX
# ADD BASE RSC /l 0x409 /d "_DEBUG"
# ADD RSC /l 0x409 /d "_DEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LINK32=link.exe
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
# ADD LINK32 glib-2.0.lib wsock32.lib jpeg6.lib l_net.lib mathlib.lib md5lib.lib zlibd.lib libpng12d.lib picomodel.lib ddslib.lib /nologo /stack:0x400000 /subsystem:console /profile /map /debug /machine:I386 /nodefaultlib:"libcd" /libpath:"..\..\..\libs\ddslib\Debug" /libpath:"..\..\..\libs\md5lib\Debug" /libpath:"..\..\..\libs\mathlib\Debug" /libpath:"..\..\..\libs\pak\Debug" /libpath:"..\..\..\libs\jpeg6\Debug" /libpath:"..\..\..\libs\l_net\Debug" /libpath:"..\..\..\..\libxml2\win32\libxml2\debug_so" /libpath:"..\..\..\..\lpng\projects\msvc\win32\libpng___Win32_Debug" /libpath:"..\..\..\libs\jpeg6\release" /libpath:"..\..\..\libs\l_net\release" /libpath:"..\..\..\..\libxml2\win32\libxml2\release_so" /libpath:"..\..\..\..\libpng\projects\msvc\libpng___Win32_Debug" /libpath:"..\..\..\..\libpng\projects\msvc\zlib___Win32_Debug" /libpath:"../../../libs/picomodel/debug" /libpath:"..\..\..\..\gtk2-win32\lib\\"
# Begin Special Build Tool
SOURCE="$(InputPath)"
PostBuild_Desc=Python post build
PostBuild_Cmds=cd ..\..\.. && run_python.bat win32_install.py Q3Map2
# End Special Build Tool
!ENDIF
# Begin Target
# Name "q3map2 - Win32 Release"
# Name "q3map2 - Win32 Debug"
# Begin Group "src"
# PROP Default_Filter "c;cpp;cxx;cc;C"
# Begin Group "common"
# PROP Default_Filter ".c"
# Begin Source File
SOURCE=..\common\cmdlib.c
# End Source File
# Begin Source File
SOURCE=..\common\imagelib.c
# End Source File
# Begin Source File
SOURCE=..\common\inout.c
# End Source File
# Begin Source File
SOURCE=..\common\mutex.c
# End Source File
# Begin Source File
SOURCE=..\common\polylib.c
# End Source File
# Begin Source File
SOURCE=..\common\scriplib.c
# End Source File
# Begin Source File
SOURCE=..\common\threads.c
# End Source File
# Begin Source File
SOURCE=..\common\unzip.c
# End Source File
# Begin Source File
SOURCE=..\common\vfs.c
!IF "$(CFG)" == "q3map2 - Win32 Release"
!ELSEIF "$(CFG)" == "q3map2 - Win32 Debug"
# ADD CPP /I "..\..\..\..\src\glib"
!ENDIF
# End Source File
# End Group
# Begin Group "bsp"
# PROP Default_Filter ".c"
# Begin Source File
SOURCE=.\brush.c
# End Source File
# Begin Source File
SOURCE=.\brush_primit.c
# End Source File
# Begin Source File
SOURCE=.\bsp.c
# End Source File
# Begin Source File
SOURCE=.\decals.c
# End Source File
# Begin Source File
SOURCE=.\facebsp.c
# End Source File
# Begin Source File
SOURCE=.\fog.c
# End Source File
# Begin Source File
SOURCE=.\leakfile.c
# End Source File
# Begin Source File
SOURCE=.\map.c
# End Source File
# Begin Source File
SOURCE=.\patch.c
# End Source File
# Begin Source File
SOURCE=.\portals.c
# End Source File
# Begin Source File
SOURCE=.\prtfile.c
# End Source File
# Begin Source File
SOURCE=.\surface.c
# End Source File
# Begin Source File
SOURCE=.\surface_foliage.c
# End Source File
# Begin Source File
SOURCE=.\surface_fur.c
# End Source File
# Begin Source File
SOURCE=.\surface_meta.c
# End Source File
# Begin Source File
SOURCE=.\tjunction.c
# End Source File
# Begin Source File
SOURCE=.\tree.c
# End Source File
# Begin Source File
SOURCE=.\writebsp.c
# End Source File
# End Group
# Begin Group "light"
# PROP Default_Filter ".c"
# Begin Source File
SOURCE=.\light.c
# End Source File
# Begin Source File
SOURCE=.\light_bounce.c
# End Source File
# Begin Source File
SOURCE=.\light_trace.c
# End Source File
# Begin Source File
SOURCE=.\light_ydnar.c
# End Source File
# Begin Source File
SOURCE=.\lightmaps_ydnar.c
# End Source File
# End Group
# Begin Group "vis"
# PROP Default_Filter ".c"
# Begin Source File
SOURCE=.\vis.c
# End Source File
# Begin Source File
SOURCE=.\visflow.c
# End Source File
# End Group
# Begin Group "convert"
# PROP Default_Filter ".c"
# Begin Source File
SOURCE=.\convert_ase.c
# End Source File
# Begin Source File
SOURCE=.\convert_map.c
# End Source File
# End Group
# Begin Source File
SOURCE=.\bspfile_abstract.c
# End Source File
# Begin Source File
SOURCE=.\bspfile_ibsp.c
# End Source File
# Begin Source File
SOURCE=.\bspfile_rbsp.c
# End Source File
# Begin Source File
SOURCE=.\image.c
# End Source File
# Begin Source File
SOURCE=.\main.c
# End Source File
# Begin Source File
SOURCE=.\mesh.c
# End Source File
# Begin Source File
SOURCE=.\model.c
# End Source File
# Begin Source File
SOURCE=.\path_init.c
# End Source File
# Begin Source File
SOURCE=.\shaders.c
# End Source File
# Begin Source File
SOURCE=.\surface_extra.c
# End Source File
# End Group
# Begin Group "include"
# PROP Default_Filter "h"
# Begin Source File
SOURCE=.\game_ef.h
# End Source File
# Begin Source File
SOURCE=.\game_ja.h
# End Source File
# Begin Source File
SOURCE=.\game_jk2.h
# End Source File
# Begin Source File
SOURCE=.\game_quake3.h
# End Source File
# Begin Source File
SOURCE=.\game_sof2.h
# End Source File
# Begin Source File
SOURCE=.\game_wolf.h
# End Source File
# Begin Source File
SOURCE=.\game_wolfet.h
# End Source File
# Begin Source File
SOURCE=.\q3map2.h
# End Source File
# End Group
# Begin Group "doc"
# PROP Default_Filter "*.txt"
# Begin Source File
SOURCE=.\changelog.q3map2.txt
# End Source File
# End Group
# Begin Group "rc"
# PROP Default_Filter ".rc;.ico"
# Begin Source File
SOURCE=.\q3map2.ico
# End Source File
# Begin Source File
SOURCE=.\q3map2.rc
# End Source File
# End Group
# End Target
# End Project

View File

@@ -0,0 +1,167 @@
Microsoft Developer Studio Workspace File, Format Version 6.00
# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
###############################################################################
Project: "ddslib"=.\ddslib\ddslib.dsp - Package Owner=<4>
Package=<5>
{{{
}}}
Package=<4>
{{{
}}}
###############################################################################
Project: "jpeg6"=..\..\..\libs\jpeg6\jpeg6.dsp - Package Owner=<4>
Package=<5>
{{{
}}}
Package=<4>
{{{
}}}
###############################################################################
Project: "l_net"=..\..\..\libs\l_net\l_net.dsp - Package Owner=<4>
Package=<5>
{{{
}}}
Package=<4>
{{{
}}}
###############################################################################
Project: "libpng"=..\..\..\..\libpng\projects\msvc\libpng.dsp - Package Owner=<4>
Package=<5>
{{{
}}}
Package=<4>
{{{
Begin Project Dependency
Project_Dep_Name zlib
End Project Dependency
}}}
###############################################################################
Project: "libxml2"=..\..\..\..\libxml2\win32\dsp\libxml2.dsp - Package Owner=<4>
Package=<5>
{{{
}}}
Package=<4>
{{{
}}}
###############################################################################
Project: "mathlib"=..\..\..\libs\mathlib\mathlib.dsp - Package Owner=<4>
Package=<5>
{{{
}}}
Package=<4>
{{{
}}}
###############################################################################
Project: "md5lib"=..\..\..\libs\md5lib\md5lib.dsp - Package Owner=<4>
Package=<5>
{{{
}}}
Package=<4>
{{{
}}}
###############################################################################
Project: "picomodel"=..\..\..\libs\picomodel\picomodel.dsp - Package Owner=<4>
Package=<5>
{{{
}}}
Package=<4>
{{{
}}}
###############################################################################
Project: "q3map2"=.\q3map2.dsp - Package Owner=<4>
Package=<5>
{{{
}}}
Package=<4>
{{{
Begin Project Dependency
Project_Dep_Name libxml2
End Project Dependency
Begin Project Dependency
Project_Dep_Name libpng
End Project Dependency
Begin Project Dependency
Project_Dep_Name zlib
End Project Dependency
Begin Project Dependency
Project_Dep_Name picomodel
End Project Dependency
Begin Project Dependency
Project_Dep_Name jpeg6
End Project Dependency
Begin Project Dependency
Project_Dep_Name l_net
End Project Dependency
Begin Project Dependency
Project_Dep_Name mathlib
End Project Dependency
Begin Project Dependency
Project_Dep_Name md5lib
End Project Dependency
Begin Project Dependency
Project_Dep_Name ddslib
End Project Dependency
}}}
###############################################################################
Project: "zlib"=..\..\..\..\libpng\projects\msvc\zlib.dsp - Package Owner=<4>
Package=<5>
{{{
}}}
Package=<4>
{{{
}}}
###############################################################################
Global:
Package=<5>
{{{
}}}
Package=<3>
{{{
}}}
###############################################################################

2318
tools/quake3/q3map2/q3map2.h Normal file

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -0,0 +1 @@
101 ICON DISCARDABLE "q3map2.ico"

View File

@@ -0,0 +1,398 @@
<?xml version="1.0" encoding="Windows-1252"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="7.10"
Name="q3map2"
ProjectGUID="{8ED67991-58A6-44AA-9B3A-3217085EF187}"
RootNamespace="q3map2">
<Platforms>
<Platform
Name="Win32"/>
</Platforms>
<Configurations>
<Configuration
Name="Debug|Win32"
OutputDirectory="Debug"
IntermediateDirectory="Debug"
ConfigurationType="1"
UseOfMFC="0"
ATLMinimizesCRunTimeLibraryUsage="FALSE"
CharacterSet="2">
<Tool
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories="&quot;..\..\..\..\mhash-0.9\lib&quot;;..\..\..\include;..\common;..\..\..\libs;&quot;..\..\..\..\libxml2-2.6\include&quot;;&quot;..\..\..\..\libpng-1.2&quot;;&quot;..\..\..\..\zlib1-1.2\include&quot;;&quot;..\..\..\..\gtk2-2.4\include\glib-2.0&quot;;&quot;..\..\..\..\gtk2-2.4\lib\glib-2.0\include&quot;"
PreprocessorDefinitions="_DEBUG,WIN32,_CONSOLE"
ExceptionHandling="FALSE"
BasicRuntimeChecks="0"
RuntimeLibrary="3"
BufferSecurityCheck="FALSE"
PrecompiledHeaderFile=".\Debug/q3map2.pch"
AssemblerListingLocation=".\Debug/"
ObjectFile=".\Debug/"
ProgramDataBaseFileName=".\Debug/"
BrowseInformation="0"
WarningLevel="3"
SuppressStartupBanner="TRUE"
DebugInformationFormat="4"
CompileAs="0"/>
<Tool
Name="VCCustomBuildTool"/>
<Tool
Name="VCLinkerTool"
AdditionalOptions="/MACHINE:I386"
AdditionalDependencies="glib-2.0.lib wsock32.lib libxml2.lib libpng13.lib libmhash.lib"
OutputFile=".\Debug/q3map2.exe"
LinkIncremental="2"
SuppressStartupBanner="TRUE"
AdditionalLibraryDirectories="&quot;..\..\..\..\mhash-0.9\win32\libmhash\Debug&quot;;&quot;..\..\..\..\libxml2-2.6\win32\lib&quot;;&quot;..\..\..\..\zlib1-1.2\lib&quot;;&quot;..\..\..\..\libpng-1.2\lib&quot;;&quot;..\..\..\..\gtk2-2.4\lib&quot;"
IgnoreDefaultLibraryNames="libcd"
GenerateDebugInformation="TRUE"
ProgramDatabaseFile=".\Debug/q3map2.pdb"
GenerateMapFile="TRUE"
MapFileName=".\Debug/q3map2.map"
SubSystem="1"
StackReserveSize="4194304"/>
<Tool
Name="VCMIDLTool"
TypeLibraryName=".\Debug/q3map2.tlb"/>
<Tool
Name="VCPostBuildEventTool"
Description="Copy to dir..."
CommandLine="copy &quot;$(TargetPath)&quot; &quot;$(SolutionDir)install&quot;"/>
<Tool
Name="VCPreBuildEventTool"/>
<Tool
Name="VCPreLinkEventTool"/>
<Tool
Name="VCResourceCompilerTool"
PreprocessorDefinitions="_DEBUG"
Culture="1033"/>
<Tool
Name="VCWebServiceProxyGeneratorTool"/>
<Tool
Name="VCXMLDataGeneratorTool"/>
<Tool
Name="VCWebDeploymentTool"/>
<Tool
Name="VCManagedWrapperGeneratorTool"/>
<Tool
Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
</Configuration>
<Configuration
Name="Release|Win32"
OutputDirectory="Release"
IntermediateDirectory="Release"
ConfigurationType="1"
UseOfMFC="0"
ATLMinimizesCRunTimeLibraryUsage="FALSE"
CharacterSet="2">
<Tool
Name="VCCLCompilerTool"
Optimization="3"
GlobalOptimizations="TRUE"
InlineFunctionExpansion="2"
EnableIntrinsicFunctions="TRUE"
FavorSizeOrSpeed="1"
OptimizeForProcessor="2"
OptimizeForWindowsApplication="TRUE"
AdditionalIncludeDirectories="&quot;..\..\..\..\mhash-0.9\lib&quot;;..\..\..\include;..\common;..\..\..\libs;&quot;..\..\..\..\libxml2-2.6\include&quot;;&quot;..\..\..\..\libpng-1.2&quot;;&quot;..\..\..\..\zlib1-1.2\include&quot;;&quot;..\..\..\..\gtk2-2.4\include\glib-2.0&quot;;&quot;..\..\..\..\gtk2-2.4\lib\glib-2.0\include&quot;"
PreprocessorDefinitions="NDEBUG,WIN32,_CONSOLE"
StringPooling="TRUE"
ExceptionHandling="FALSE"
BasicRuntimeChecks="0"
RuntimeLibrary="2"
BufferSecurityCheck="FALSE"
EnableFunctionLevelLinking="TRUE"
PrecompiledHeaderFile=".\Release/q3map2.pch"
AssemblerListingLocation=".\Release/"
ObjectFile=".\Release/"
ProgramDataBaseFileName=".\Release/"
BrowseInformation="0"
WarningLevel="3"
SuppressStartupBanner="TRUE"
DebugInformationFormat="3"
CompileAs="0"/>
<Tool
Name="VCCustomBuildTool"/>
<Tool
Name="VCLinkerTool"
AdditionalOptions="/MACHINE:I386"
AdditionalDependencies="glib-2.0.lib wsock32.lib libxml2.lib libpng13.lib libmhash.lib"
OutputFile=".\Release/q3map2.exe"
LinkIncremental="1"
SuppressStartupBanner="TRUE"
AdditionalLibraryDirectories="&quot;..\..\..\..\mhash-0.9\win32\libmhash\Release&quot;;&quot;..\..\..\..\libxml2-2.6\win32\lib&quot;;&quot;..\..\..\..\zlib1-1.2\lib&quot;;&quot;..\..\..\..\libpng-1.2\lib&quot;;&quot;..\..\..\..\gtk2-2.4\lib&quot;"
ProgramDatabaseFile=".\Release/q3map2.pdb"
GenerateMapFile="TRUE"
MapFileName=".\Release/q3map2.map"
SubSystem="1"
StackReserveSize="4194304"/>
<Tool
Name="VCMIDLTool"
TypeLibraryName=".\Release/q3map2.tlb"/>
<Tool
Name="VCPostBuildEventTool"
Description="Copy to dir..."
CommandLine="copy &quot;$(TargetPath)&quot; &quot;$(SolutionDir)install&quot;"/>
<Tool
Name="VCPreBuildEventTool"/>
<Tool
Name="VCPreLinkEventTool"/>
<Tool
Name="VCResourceCompilerTool"
PreprocessorDefinitions="NDEBUG"
Culture="1033"/>
<Tool
Name="VCWebServiceProxyGeneratorTool"/>
<Tool
Name="VCXMLDataGeneratorTool"/>
<Tool
Name="VCWebDeploymentTool"/>
<Tool
Name="VCManagedWrapperGeneratorTool"/>
<Tool
Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
</Configuration>
</Configurations>
<References>
</References>
<Files>
<Filter
Name="src"
Filter="c;cpp;cxx;cc;C">
<File
RelativePath=".\bspfile_abstract.c">
</File>
<File
RelativePath=".\bspfile_ibsp.c">
</File>
<File
RelativePath=".\bspfile_rbsp.c">
</File>
<File
RelativePath=".\image.c">
</File>
<File
RelativePath=".\main.c">
</File>
<File
RelativePath=".\mesh.c">
</File>
<File
RelativePath=".\model.c">
</File>
<File
RelativePath=".\path_init.c">
</File>
<File
RelativePath=".\shaders.c">
</File>
<File
RelativePath=".\surface_extra.c">
</File>
<Filter
Name="common"
Filter=".c">
<File
RelativePath="..\common\cmdlib.c">
</File>
<File
RelativePath="..\common\imagelib.c">
</File>
<File
RelativePath="..\common\inout.c">
</File>
<File
RelativePath="..\common\mutex.c">
</File>
<File
RelativePath="..\common\polylib.c">
</File>
<File
RelativePath="..\common\scriplib.c">
</File>
<File
RelativePath="..\common\threads.c">
</File>
<File
RelativePath="..\common\unzip.c">
</File>
<File
RelativePath="..\common\vfs.c">
</File>
</Filter>
<Filter
Name="bsp"
Filter=".c">
<File
RelativePath=".\brush.c">
</File>
<File
RelativePath=".\brush_primit.c">
</File>
<File
RelativePath=".\bsp.c">
</File>
<File
RelativePath=".\decals.c">
</File>
<File
RelativePath=".\facebsp.c">
</File>
<File
RelativePath=".\fog.c">
</File>
<File
RelativePath=".\leakfile.c">
</File>
<File
RelativePath=".\map.c">
</File>
<File
RelativePath=".\patch.c">
</File>
<File
RelativePath=".\portals.c">
</File>
<File
RelativePath=".\prtfile.c">
</File>
<File
RelativePath=".\surface.c">
</File>
<File
RelativePath=".\surface_foliage.c">
</File>
<File
RelativePath=".\surface_fur.c">
</File>
<File
RelativePath=".\surface_meta.c">
</File>
<File
RelativePath=".\tjunction.c">
</File>
<File
RelativePath=".\tree.c">
</File>
<File
RelativePath=".\writebsp.c">
</File>
</Filter>
<Filter
Name="light"
Filter=".c">
<File
RelativePath=".\light.c">
</File>
<File
RelativePath=".\light_bounce.c">
</File>
<File
RelativePath=".\light_trace.c">
</File>
<File
RelativePath=".\light_ydnar.c">
</File>
<File
RelativePath=".\lightmaps_ydnar.c">
</File>
</Filter>
<Filter
Name="vis"
Filter=".c">
<File
RelativePath=".\vis.c">
</File>
<File
RelativePath=".\visflow.c">
</File>
</Filter>
<Filter
Name="convert"
Filter=".c">
<File
RelativePath=".\convert_ase.c">
</File>
<File
RelativePath=".\convert_map.c">
</File>
</Filter>
</Filter>
<Filter
Name="include"
Filter="h">
<File
RelativePath=".\game_ef.h">
</File>
<File
RelativePath=".\game_etut.h">
</File>
<File
RelativePath=".\game_ja.h">
</File>
<File
RelativePath=".\game_jk2.h">
</File>
<File
RelativePath=".\game_quake3.h">
</File>
<File
RelativePath=".\game_sof2.h">
</File>
<File
RelativePath=".\game_tenebrae.h">
</File>
<File
RelativePath=".\game_wolf.h">
</File>
<File
RelativePath=".\game_wolfet.h">
</File>
<File
RelativePath=".\q3map2.h">
</File>
</Filter>
<Filter
Name="doc"
Filter="*.txt">
<File
RelativePath=".\changelog.q3map2.txt">
</File>
</Filter>
<Filter
Name="rc"
Filter=".rc;.ico">
<File
RelativePath=".\q3map2.ico">
</File>
<File
RelativePath=".\q3map2.rc">
</File>
</Filter>
<File
RelativePath="..\..\..\touch.py">
<FileConfiguration
Name="Debug|Win32">
<Tool
Name="VCCustomBuildTool"
CommandLine="python &quot;$(SolutionDir)touch.py&quot; &quot;$(TargetPath)&quot;
"
AdditionalDependencies="&quot;$(SolutionDir)install\$(TargetFileName)&quot;"
Outputs="&quot;$(TargetPath)&quot;"/>
</FileConfiguration>
<FileConfiguration
Name="Release|Win32">
<Tool
Name="VCCustomBuildTool"
CommandLine="python &quot;$(SolutionDir)touch.py&quot; &quot;$(TargetPath)&quot;
"
AdditionalDependencies="&quot;$(SolutionDir)install\$(TargetFileName)&quot;"
Outputs="&quot;$(TargetPath)&quot;"/>
</FileConfiguration>
</File>
</Files>
<Globals>
</Globals>
</VisualStudioProject>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,445 @@
/* -------------------------------------------------------------------------------
Copyright (C) 1999-2006 Id Software, Inc. and contributors.
For a list of contributors, see the accompanying CONTRIBUTORS file.
This file is part of GtkRadiant.
GtkRadiant 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.
GtkRadiant 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 GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
----------------------------------------------------------------------------------
This code has been altered significantly from its original form, to support
several games based on the Quake III Arena engine, in the form of "Q3Map2."
------------------------------------------------------------------------------- */
/* marker */
#define SURFACE_EXTRA_C
/* dependencies */
#include "q3map2.h"
/* -------------------------------------------------------------------------------
ydnar: srf file module
------------------------------------------------------------------------------- */
typedef struct surfaceExtra_s
{
mapDrawSurface_t *mds;
shaderInfo_t *si;
int parentSurfaceNum;
int entityNum;
int castShadows, recvShadows;
int sampleSize;
float longestCurve;
vec3_t lightmapAxis;
}
surfaceExtra_t;
#define GROW_SURFACE_EXTRAS 1024
int numSurfaceExtras = 0;
int maxSurfaceExtras = 0;
surfaceExtra_t *surfaceExtras;
surfaceExtra_t seDefault = { NULL, NULL, -1, 0, WORLDSPAWN_CAST_SHADOWS, WORLDSPAWN_RECV_SHADOWS, 0, 0, { 0, 0, 0 } };
/*
AllocSurfaceExtra()
allocates a new extra storage
*/
static surfaceExtra_t *AllocSurfaceExtra( void )
{
surfaceExtra_t *se;
/* enough space? */
if( numSurfaceExtras >= maxSurfaceExtras )
{
/* reallocate more room */
maxSurfaceExtras += GROW_SURFACE_EXTRAS;
se = safe_malloc( maxSurfaceExtras * sizeof( surfaceExtra_t ) );
if( surfaceExtras != NULL )
{
memcpy( se, surfaceExtras, numSurfaceExtras * sizeof( surfaceExtra_t ) );
free( surfaceExtras );
}
surfaceExtras = se;
}
/* add another */
se = &surfaceExtras[ numSurfaceExtras ];
numSurfaceExtras++;
memcpy( se, &seDefault, sizeof( surfaceExtra_t ) );
/* return it */
return se;
}
/*
SetDefaultSampleSize()
sets the default lightmap sample size
*/
void SetDefaultSampleSize( int sampleSize )
{
seDefault.sampleSize = sampleSize;
}
/*
SetSurfaceExtra()
stores extra (q3map2) data for the specific numbered drawsurface
*/
void SetSurfaceExtra( mapDrawSurface_t *ds, int num )
{
surfaceExtra_t *se;
/* dummy check */
if( ds == NULL || num < 0 )
return;
/* get a new extra */
se = AllocSurfaceExtra();
/* copy out the relevant bits */
se->mds = ds;
se->si = ds->shaderInfo;
se->parentSurfaceNum = ds->parent != NULL ? ds->parent->outputNum : -1;
se->entityNum = ds->entityNum;
se->castShadows = ds->castShadows;
se->recvShadows = ds->recvShadows;
se->sampleSize = ds->sampleSize;
se->longestCurve = ds->longestCurve;
VectorCopy( ds->lightmapAxis, se->lightmapAxis );
/* debug code */
//% Sys_FPrintf( SYS_VRB, "SetSurfaceExtra(): entityNum = %d\n", ds->entityNum );
}
/*
GetSurfaceExtra*()
getter functions for extra surface data
*/
static surfaceExtra_t *GetSurfaceExtra( int num )
{
if( num < 0 || num >= numSurfaceExtras )
return &seDefault;
return &surfaceExtras[ num ];
}
shaderInfo_t *GetSurfaceExtraShaderInfo( int num )
{
surfaceExtra_t *se = GetSurfaceExtra( num );
return se->si;
}
int GetSurfaceExtraParentSurfaceNum( int num )
{
surfaceExtra_t *se = GetSurfaceExtra( num );
return se->parentSurfaceNum;
}
int GetSurfaceExtraEntityNum( int num )
{
surfaceExtra_t *se = GetSurfaceExtra( num );
return se->entityNum;
}
int GetSurfaceExtraCastShadows( int num )
{
surfaceExtra_t *se = GetSurfaceExtra( num );
return se->castShadows;
}
int GetSurfaceExtraRecvShadows( int num )
{
surfaceExtra_t *se = GetSurfaceExtra( num );
return se->recvShadows;
}
int GetSurfaceExtraSampleSize( int num )
{
surfaceExtra_t *se = GetSurfaceExtra( num );
return se->sampleSize;
}
float GetSurfaceExtraLongestCurve( int num )
{
surfaceExtra_t *se = GetSurfaceExtra( num );
return se->longestCurve;
}
void GetSurfaceExtraLightmapAxis( int num, vec3_t lightmapAxis )
{
surfaceExtra_t *se = GetSurfaceExtra( num );
VectorCopy( se->lightmapAxis, lightmapAxis );
}
/*
WriteSurfaceExtraFile()
writes out a surface info file (<map>.srf)
*/
void WriteSurfaceExtraFile( const char *path )
{
char srfPath[ 1024 ];
FILE *sf;
surfaceExtra_t *se;
int i;
/* dummy check */
if( path == NULL || path[ 0 ] == '\0' )
return;
/* note it */
Sys_Printf( "--- WriteSurfaceExtraFile ---\n" );
/* open the file */
strcpy( srfPath, path );
StripExtension( srfPath );
strcat( srfPath, ".srf" );
Sys_Printf( "Writing %s\n", srfPath );
sf = fopen( srfPath, "w" );
if( sf == NULL )
Error( "Error opening %s for writing", srfPath );
/* lap through the extras list */
for( i = -1; i < numSurfaceExtras; i++ )
{
/* get extra */
se = GetSurfaceExtra( i );
/* default or surface num? */
if( i < 0 )
fprintf( sf, "default" );
else
fprintf( sf, "%d", i );
/* valid map drawsurf? */
if( se->mds == NULL )
fprintf( sf, "\n" );
else
{
fprintf( sf, " // %s V: %d I: %d %s\n",
surfaceTypes[ se->mds->type ],
se->mds->numVerts,
se->mds->numIndexes,
(se->mds->planar ? "planar" : "") );
}
/* open braces */
fprintf( sf, "{\n" );
/* shader */
if( se->si != NULL )
fprintf( sf, "\tshader %s\n", se->si->shader );
/* parent surface number */
if( se->parentSurfaceNum != seDefault.parentSurfaceNum )
fprintf( sf, "\tparent %d\n", se->parentSurfaceNum );
/* entity number */
if( se->entityNum != seDefault.entityNum )
fprintf( sf, "\tentity %d\n", se->entityNum );
/* cast shadows */
if( se->castShadows != seDefault.castShadows || se == &seDefault )
fprintf( sf, "\tcastShadows %d\n", se->castShadows );
/* recv shadows */
if( se->recvShadows != seDefault.recvShadows || se == &seDefault )
fprintf( sf, "\treceiveShadows %d\n", se->recvShadows );
/* lightmap sample size */
if( se->sampleSize != seDefault.sampleSize || se == &seDefault )
fprintf( sf, "\tsampleSize %d\n", se->sampleSize );
/* longest curve */
if( se->longestCurve != seDefault.longestCurve || se == &seDefault )
fprintf( sf, "\tlongestCurve %f\n", se->longestCurve );
/* lightmap axis vector */
if( VectorCompare( se->lightmapAxis, seDefault.lightmapAxis ) == qfalse )
fprintf( sf, "\tlightmapAxis ( %f %f %f )\n", se->lightmapAxis[ 0 ], se->lightmapAxis[ 1 ], se->lightmapAxis[ 2 ] );
/* close braces */
fprintf( sf, "}\n\n" );
}
/* close the file */
fclose( sf );
}
/*
LoadSurfaceExtraFile()
reads a surface info file (<map>.srf)
*/
void LoadSurfaceExtraFile( const char *path )
{
char srfPath[ 1024 ];
surfaceExtra_t *se;
int surfaceNum, size;
byte *buffer;
/* dummy check */
if( path == NULL || path[ 0 ] == '\0' )
return;
/* load the file */
strcpy( srfPath, path );
StripExtension( srfPath );
strcat( srfPath, ".srf" );
Sys_Printf( "Loading %s\n", srfPath );
size = LoadFile( srfPath, (void**) &buffer );
if( size <= 0 )
{
Sys_Printf( "WARNING: Unable to find surface file %s, using defaults.\n", srfPath );
return;
}
/* parse the file */
ParseFromMemory( buffer, size );
/* tokenize it */
while( 1 )
{
/* test for end of file */
if( !GetToken( qtrue ) )
break;
/* default? */
if( !Q_stricmp( token, "default" ) )
se = &seDefault;
/* surface number */
else
{
surfaceNum = atoi( token );
if( surfaceNum < 0 || surfaceNum > MAX_MAP_DRAW_SURFS )
Error( "ReadSurfaceExtraFile(): %s, line %d: bogus surface num %d", srfPath, scriptline, surfaceNum );
while( surfaceNum >= numSurfaceExtras )
se = AllocSurfaceExtra();
se = &surfaceExtras[ surfaceNum ];
}
/* handle { } section */
if( !GetToken( qtrue ) || strcmp( token, "{" ) )
Error( "ReadSurfaceExtraFile(): %s, line %d: { not found", srfPath, scriptline );
while( 1 )
{
if( !GetToken( qtrue ) )
break;
if( !strcmp( token, "}" ) )
break;
/* shader */
if( !Q_stricmp( token, "shader" ) )
{
GetToken( qfalse );
se->si = ShaderInfoForShader( token );
}
/* parent surface number */
else if( !Q_stricmp( token, "parent" ) )
{
GetToken( qfalse );
se->parentSurfaceNum = atoi( token );
}
/* entity number */
else if( !Q_stricmp( token, "entity" ) )
{
GetToken( qfalse );
se->entityNum = atoi( token );
}
/* cast shadows */
else if( !Q_stricmp( token, "castShadows" ) )
{
GetToken( qfalse );
se->castShadows = atoi( token );
}
/* recv shadows */
else if( !Q_stricmp( token, "receiveShadows" ) )
{
GetToken( qfalse );
se->recvShadows = atoi( token );
}
/* lightmap sample size */
else if( !Q_stricmp( token, "sampleSize" ) )
{
GetToken( qfalse );
se->sampleSize = atoi( token );
}
/* longest curve */
else if( !Q_stricmp( token, "longestCurve" ) )
{
GetToken( qfalse );
se->longestCurve = atof( token );
}
/* lightmap axis vector */
else if( !Q_stricmp( token, "lightmapAxis" ) )
Parse1DMatrix( 3, se->lightmapAxis );
/* ignore all other tokens on the line */
while( TokenAvailable() )
GetToken( qfalse );
}
}
/* free the buffer */
free( buffer );
}

View File

@@ -0,0 +1,328 @@
/* -------------------------------------------------------------------------------
Copyright (C) 1999-2006 Id Software, Inc. and contributors.
For a list of contributors, see the accompanying CONTRIBUTORS file.
This file is part of GtkRadiant.
GtkRadiant 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.
GtkRadiant 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 GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
----------------------------------------------------------------------------------
Foliage code for Wolfenstein: Enemy Territory by ydnar@splashdamage.com
------------------------------------------------------------------------------- */
/* marker */
#define SURFACE_FOLIAGE_C
/* dependencies */
#include "q3map2.h"
#define MAX_FOLIAGE_INSTANCES 8192
static int numFoliageInstances;
static foliageInstance_t foliageInstances[ MAX_FOLIAGE_INSTANCES ];
/*
SubdivideFoliageTriangle_r()
recursively subdivides a triangle until the triangle is smaller than
the desired density, then pseudo-randomly sets a point
*/
static void SubdivideFoliageTriangle_r( mapDrawSurface_t *ds, foliage_t *foliage, bspDrawVert_t **tri )
{
bspDrawVert_t mid, *tri2[ 3 ];
int max;
/* limit test */
if( numFoliageInstances >= MAX_FOLIAGE_INSTANCES )
return;
/* plane test */
{
vec4_t plane;
/* make a plane */
if( !PlaneFromPoints( plane, tri[ 0 ]->xyz, tri[ 1 ]->xyz, tri[ 2 ]->xyz ) )
return;
/* if normal is too far off vertical, then don't place an instance */
if( plane[ 2 ] < 0.5f )
return;
}
/* subdivide calc */
{
int i;
float *a, *b, dx, dy, dz, dist, maxDist;
foliageInstance_t *fi;
/* get instance */
fi = &foliageInstances[ numFoliageInstances ];
/* find the longest edge and split it */
max = -1;
maxDist = 0.0f;
VectorClear( fi->xyz );
VectorClear( fi->normal );
for( i = 0; i < 3; i++ )
{
/* get verts */
a = tri[ i ]->xyz;
b = tri[ (i + 1) % 3 ]->xyz;
/* get dists */
dx = a[ 0 ] - b[ 0 ];
dy = a[ 1 ] - b[ 1 ];
dz = a[ 2 ] - b[ 2 ];
dist = (dx * dx) + (dy * dy) + (dz * dz);
/* longer? */
if( dist > maxDist )
{
maxDist = dist;
max = i;
}
/* add to centroid */
VectorAdd( fi->xyz, tri[ i ]->xyz, fi->xyz );
VectorAdd( fi->normal, tri[ i ]->normal, fi->normal );
}
/* is the triangle small enough? */
if( maxDist <= (foliage->density * foliage->density) )
{
float alpha, odds, r;
/* get average alpha */
if( foliage->inverseAlpha == 2 )
alpha = 1.0f;
else
{
alpha = ((float) tri[ 0 ]->color[ 0 ][ 3 ] + (float) tri[ 1 ]->color[ 0 ][ 3 ] + (float) tri[ 2 ]->color[ 0 ][ 3 ]) / 765.0f;
if( foliage->inverseAlpha == 1 )
alpha = 1.0f - alpha;
if( alpha < 0.75f )
return;
}
/* roll the dice */
odds = foliage->odds * alpha;
r = Random();
if( r > odds )
return;
/* scale centroid */
VectorScale( fi->xyz, 0.33333333f, fi->xyz );
if( VectorNormalize( fi->normal, fi->normal ) == 0.0f )
return;
/* add to count and return */
numFoliageInstances++;
return;
}
}
/* split the longest edge and map it */
LerpDrawVert( tri[ max ], tri[ (max + 1) % 3 ], &mid );
/* recurse to first triangle */
VectorCopy( tri, tri2 );
tri2[ max ] = &mid;
SubdivideFoliageTriangle_r( ds, foliage, tri2 );
/* recurse to second triangle */
VectorCopy( tri, tri2 );
tri2[ (max + 1) % 3 ] = &mid;
SubdivideFoliageTriangle_r( ds, foliage, tri2 );
}
/*
GenFoliage()
generates a foliage file for a bsp
*/
void Foliage( mapDrawSurface_t *src )
{
int i, j, k, x, y, pw[ 5 ], r, oldNumMapDrawSurfs;
mapDrawSurface_t *ds;
shaderInfo_t *si;
foliage_t *foliage;
mesh_t srcMesh, *subdivided, *mesh;
bspDrawVert_t *verts, *dv[ 3 ], *fi;
vec3_t scale;
m4x4_t transform;
/* get shader */
si = src->shaderInfo;
if( si == NULL || si->foliage == NULL )
return;
/* do every foliage */
for( foliage = si->foliage; foliage != NULL; foliage = foliage->next )
{
/* zero out */
numFoliageInstances = 0;
/* map the surface onto the lightmap origin/cluster/normal buffers */
switch( src->type )
{
case SURFACE_META:
case SURFACE_FORCED_META:
case SURFACE_TRIANGLES:
/* get verts */
verts = src->verts;
/* map the triangles */
for( i = 0; i < src->numIndexes; i += 3 )
{
dv[ 0 ] = &verts[ src->indexes[ i ] ];
dv[ 1 ] = &verts[ src->indexes[ i + 1 ] ];
dv[ 2 ] = &verts[ src->indexes[ i + 2 ] ];
SubdivideFoliageTriangle_r( src, foliage, dv );
}
break;
case SURFACE_PATCH:
/* make a mesh from the drawsurf */
srcMesh.width = src->patchWidth;
srcMesh.height = src->patchHeight;
srcMesh.verts = src->verts;
subdivided = SubdivideMesh( srcMesh, 8, 512 );
/* fit it to the curve and remove colinear verts on rows/columns */
PutMeshOnCurve( *subdivided );
mesh = RemoveLinearMeshColumnsRows( subdivided );
FreeMesh( subdivided );
/* get verts */
verts = mesh->verts;
/* map the mesh quads */
for( y = 0; y < (mesh->height - 1); y++ )
{
for( x = 0; x < (mesh->width - 1); x++ )
{
/* set indexes */
pw[ 0 ] = x + (y * mesh->width);
pw[ 1 ] = x + ((y + 1) * mesh->width);
pw[ 2 ] = x + 1 + ((y + 1) * mesh->width);
pw[ 3 ] = x + 1 + (y * mesh->width);
pw[ 4 ] = x + (y * mesh->width); /* same as pw[ 0 ] */
/* set radix */
r = (x + y) & 1;
/* get drawverts and map first triangle */
dv[ 0 ] = &verts[ pw[ r + 0 ] ];
dv[ 1 ] = &verts[ pw[ r + 1 ] ];
dv[ 2 ] = &verts[ pw[ r + 2 ] ];
SubdivideFoliageTriangle_r( src, foliage, dv );
/* get drawverts and map second triangle */
dv[ 0 ] = &verts[ pw[ r + 0 ] ];
dv[ 1 ] = &verts[ pw[ r + 2 ] ];
dv[ 2 ] = &verts[ pw[ r + 3 ] ];
SubdivideFoliageTriangle_r( src, foliage, dv );
}
}
/* free the mesh */
FreeMesh( mesh );
break;
default:
break;
}
/* any origins? */
if( numFoliageInstances < 1 )
continue;
/* remember surface count */
oldNumMapDrawSurfs = numMapDrawSurfs;
/* set transform matrix */
VectorSet( scale, foliage->scale, foliage->scale, foliage->scale );
m4x4_scale_for_vec3( transform, scale );
/* add the model to the bsp */
InsertModel( foliage->model, 0, transform, NULL, NULL, src->entityNum, src->castShadows, src->recvShadows, 0, src->lightmapScale );
/* walk each new surface */
for( i = oldNumMapDrawSurfs; i < numMapDrawSurfs; i++ )
{
/* get surface */
ds = &mapDrawSurfs[ i ];
/* set up */
ds->type = SURFACE_FOLIAGE;
ds->numFoliageInstances = numFoliageInstances;
/* a wee hack */
ds->patchWidth = ds->numFoliageInstances;
ds->patchHeight = ds->numVerts;
/* set fog to be same as source surface */
ds->fogNum = src->fogNum;
/* add a drawvert for every instance */
verts = safe_malloc( (ds->numVerts + ds->numFoliageInstances) * sizeof( *verts ) );
memset( verts, 0, (ds->numVerts + ds->numFoliageInstances) * sizeof( *verts ) );
memcpy( verts, ds->verts, ds->numVerts * sizeof( *verts ) );
free( ds->verts );
ds->verts = verts;
/* copy the verts */
for( j = 0; j < ds->numFoliageInstances; j++ )
{
/* get vert (foliage instance) */
fi = &ds->verts[ ds->numVerts + j ];
/* copy xyz and normal */
VectorCopy( foliageInstances[ j ].xyz, fi->xyz );
VectorCopy( foliageInstances[ j ].normal, fi->normal );
/* ydnar: set color */
for( k = 0; k < MAX_LIGHTMAPS; k++ )
{
fi->color[ k ][ 0 ] = 255;
fi->color[ k ][ 1 ] = 255;
fi->color[ k ][ 2 ] = 255;
fi->color[ k ][ 3 ] = 255;
}
}
/* increment */
ds->numVerts += ds->numFoliageInstances;
}
}
}

View File

@@ -0,0 +1,129 @@
/* -------------------------------------------------------------------------------
Copyright (C) 1999-2006 Id Software, Inc. and contributors.
For a list of contributors, see the accompanying CONTRIBUTORS file.
This file is part of GtkRadiant.
GtkRadiant 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.
GtkRadiant 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 GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
----------------------------------------------------------------------------------
This code has been altered significantly from its original form, to support
several games based on the Quake III Arena engine, in the form of "Q3Map2."
------------------------------------------------------------------------------- */
/* marker */
#define SURFACE_FUR_C
/* dependencies */
#include "q3map2.h"
/* -------------------------------------------------------------------------------
ydnar: fur module
------------------------------------------------------------------------------- */
/*
Fur()
runs the fur processing algorithm on a map drawsurface
*/
void Fur( mapDrawSurface_t *ds )
{
int i, j, k, numLayers;
float offset, fade, a;
mapDrawSurface_t *fur;
bspDrawVert_t *dv;
/* dummy check */
if( ds == NULL || ds->fur || ds->shaderInfo->furNumLayers < 1 )
return;
/* get basic info */
numLayers = ds->shaderInfo->furNumLayers;
offset = ds->shaderInfo->furOffset;
fade = ds->shaderInfo->furFade * 255.0f;
/* debug code */
//% Sys_FPrintf( SYS_VRB, "Fur(): layers: %d offset: %f fade: %f %s\n",
//% numLayers, offset, fade, ds->shaderInfo->shader );
/* initial offset */
for( j = 0; j < ds->numVerts; j++ )
{
/* get surface vert */
dv = &ds->verts[ j ];
/* offset is scaled by original vertex alpha */
a = (float) dv->color[ 0 ][ 3 ] / 255.0;
/* offset it */
VectorMA( dv->xyz, (offset * a), dv->normal, dv->xyz );
}
/* wash, rinse, repeat */
for( i = 1; i < numLayers; i++ )
{
/* clone the surface */
fur = CloneSurface( ds, ds->shaderInfo );
if( fur == NULL )
return;
/* set it to fur */
fur->fur = qtrue;
/* walk the verts */
for( j = 0; j < fur->numVerts; j++ )
{
/* get surface vert */
dv = &ds->verts[ j ];
/* offset is scaled by original vertex alpha */
a = (float) dv->color[ 0 ][ 3 ] / 255.0;
/* get fur vert */
dv = &fur->verts[ j ];
/* offset it */
VectorMA( dv->xyz, (offset * a * i), dv->normal, dv->xyz );
/* fade alpha */
for( k = 0; k < MAX_LIGHTMAPS; k++ )
{
a = (float) dv->color[ k ][ 3 ] - fade;
if( a > 255.0f )
dv->color[ k ][ 3 ] = 255;
else if( a < 0 )
dv->color[ k ][ 3 ] = 0;
else
dv->color[ k ][ 3 ] = a;
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,728 @@
/* -------------------------------------------------------------------------------
Copyright (C) 1999-2006 Id Software, Inc. and contributors.
For a list of contributors, see the accompanying CONTRIBUTORS file.
This file is part of GtkRadiant.
GtkRadiant 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.
GtkRadiant 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 GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
----------------------------------------------------------------------------------
This code has been altered significantly from its original form, to support
several games based on the Quake III Arena engine, in the form of "Q3Map2."
------------------------------------------------------------------------------- */
/* marker */
#define TJUNCTION_C
/* dependencies */
#include "q3map2.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;
bspDrawVert_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 = safe_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] = (bspDrawVert_t *)v1;
originalEdges[ numOriginalEdges ].dv[1] = (bspDrawVert_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()
adds a surface's edges
*/
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 ][ 0 ] =
AddEdge( ds->verts[ i ].xyz, ds->verts[ (i + 1) % ds->numVerts ].xyz, qfalse );
}
}
/*
ColinearEdge()
determines if an edge is colinear
*/
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];
bspDrawVert_t verts[MAX_SURFACE_VERTS], *v1, *v2;
int numVerts;
float start, end, frac, c;
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 ][ 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 );
/* 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] );
}
/* copy the normal (FIXME: what about nonplanar surfaces? */
VectorCopy( v1->normal, verts[ numVerts ].normal );
/* ydnar: interpolate the color */
for( k = 0; k < MAX_LIGHTMAPS; k++ )
{
for( j = 0; j < 4; j++ )
{
c = (float) v1->color[ k ][ j ] + frac * ((float) v2->color[ k ][ j ] - (float) v1->color[ k ][ j ]);
verts[ numVerts ].color[ k ][ j ] = (byte) (c < 255.0f ? c : 255);
}
}
/* next... */
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 = safe_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 = safe_malloc( numVerts * sizeof( *ds->verts ) );
for ( j = 0 ; j < ds->numVerts ; j++ ) {
ds->verts[j] = verts[ ( j + i ) % ds->numVerts ];
}
}
/*
FixBrokenSurface() - ydnar
removes nearly coincident verts from a planar winding surface
returns qfalse if the surface is broken
*/
extern void SnapWeldVector( vec3_t a, vec3_t b, vec3_t out );
#define DEGENERATE_EPSILON 0.1
int c_broken = 0;
qboolean FixBrokenSurface( mapDrawSurface_t *ds )
{
qboolean valid = qtrue;
bspDrawVert_t *dv1, *dv2, avg;
int i, j, k;
float dist;
/* dummy check */
if( ds == NULL )
return qfalse;
if( ds->type != SURFACE_FACE )
return qfalse;
/* check all verts */
for( i = 0; i < ds->numVerts; i++ )
{
/* don't remove points if winding is a triangle */
if( ds->numVerts == 3 )
return valid;
/* get verts */
dv1 = &ds->verts[ i ];
dv2 = &ds->verts[ (i + 1) % ds->numVerts ];
/* degenerate edge? */
VectorSubtract( dv1->xyz, dv2->xyz, avg.xyz );
dist = VectorLength( avg.xyz );
if( dist < DEGENERATE_EPSILON )
{
valid = qfalse;
Sys_FPrintf( SYS_VRB, "WARNING: Degenerate T-junction edge found, fixing...\n" );
/* create an average drawvert */
/* ydnar 2002-01-26: added nearest-integer welding preference */
SnapWeldVector( dv1->xyz, dv2->xyz, avg.xyz );
VectorAdd( dv1->normal, dv2->normal, avg.normal );
VectorNormalize( avg.normal, avg.normal );
avg.st[ 0 ] = (dv1->st[ 0 ] + dv2->st[ 0 ]) * 0.5f;
avg.st[ 1 ] = (dv1->st[ 1 ] + dv2->st[ 1 ]) * 0.5f;
/* lightmap st/colors */
for( k = 0; k < MAX_LIGHTMAPS; k++ )
{
avg.lightmap[ k ][ 0 ] = (dv1->lightmap[ k ][ 0 ] + dv2->lightmap[ k ][ 0 ]) * 0.5f;
avg.lightmap[ k ][ 1 ] = (dv1->lightmap[ k ][ 1 ] + dv2->lightmap[ k ][ 1 ]) * 0.5f;
for( j = 0; j < 4; j++ )
avg.color[ k ][ j ] = (int) (dv1->color[ k ][ j ] + dv2->color[ k ][ j ]) >> 1;
}
/* ydnar: der... */
memcpy( dv1, &avg, sizeof( avg ) );
/* move the remaining verts */
for( k = i + 2; k < ds->numVerts; k++ )
{
/* get verts */
dv1 = &ds->verts[ k ];
dv2 = &ds->verts[ k - 1 ];
/* copy */
memcpy( dv2, dv1, sizeof( bspDrawVert_t ) );
}
ds->numVerts--;
}
}
/* one last check and return */
if( ds->numVerts < 3 )
valid = qfalse;
return valid;
}
/*
================
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
*/
void FixTJunctions( entity_t *ent )
{
int i;
mapDrawSurface_t *ds;
shaderInfo_t *si;
int axialEdgeLines;
originalEdge_t *e;
/* meta mode has its own t-junction code (currently not as good as this code) */
//% if( meta )
//% return;
/* note it */
Sys_FPrintf( SYS_VRB, "--- 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++ )
{
/* get surface and early out if possible */
ds = &mapDrawSurfs[ i ];
si = ds->shaderInfo;
if( (si->compileFlags & C_NODRAW) || si->autosprite || si->notjunc || ds->numVerts == 0 )
continue;
/* ydnar: gs mods: handle the various types of surfaces */
switch( ds->type )
{
/* handle brush faces */
case SURFACE_FACE:
AddSurfaceEdges( ds );
break;
/* handle patches */
case SURFACE_PATCH:
AddPatchEdges( ds );
break;
/* fixme: make triangle surfaces t-junction */
default:
break;
}
}
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 ][ 0 ] = AddEdge( e->dv[ 0 ]->xyz, e->dv[ 1 ]->xyz, qtrue );
}
Sys_FPrintf( SYS_VRB, "%9d axial edge lines\n", axialEdgeLines );
Sys_FPrintf( SYS_VRB, "%9d non-axial edge lines\n", numEdgeLines - axialEdgeLines );
Sys_FPrintf( SYS_VRB, "%9d degenerate edges\n", c_degenerateEdges );
// insert any needed vertexes
for( i = ent->firstDrawSurf; i < numMapDrawSurfs ; i++ )
{
/* get surface and early out if possible */
ds = &mapDrawSurfs[ i ];
si = ds->shaderInfo;
if( (si->compileFlags & C_NODRAW) || si->autosprite || si->notjunc || ds->numVerts == 0 || ds->type != SURFACE_FACE )
continue;
/* ydnar: gs mods: handle the various types of surfaces */
switch( ds->type )
{
/* handle brush faces */
case SURFACE_FACE:
FixSurfaceJunctions( ds );
if( FixBrokenSurface( ds ) == qfalse )
{
c_broken++;
ClearSurface( ds );
}
break;
/* fixme: t-junction triangle models and patches */
default:
break;
}
}
/* emit some statistics */
Sys_FPrintf( SYS_VRB, "%9d verts added for T-junctions\n", c_addedVerts );
Sys_FPrintf( SYS_VRB, "%9d total verts\n", c_totalVerts );
Sys_FPrintf( SYS_VRB, "%9d naturally ordered\n", c_natural );
Sys_FPrintf( SYS_VRB, "%9d rotated orders\n", c_rotate );
Sys_FPrintf( SYS_VRB, "%9d can't order\n", c_cant );
Sys_FPrintf( SYS_VRB, "%9d broken (degenerate) surfaces removed\n", c_broken );
}

159
tools/quake3/q3map2/tree.c Normal file
View File

@@ -0,0 +1,159 @@
/* -------------------------------------------------------------------------------
Copyright (C) 1999-2006 Id Software, Inc. and contributors.
For a list of contributors, see the accompanying CONTRIBUTORS file.
This file is part of GtkRadiant.
GtkRadiant 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.
GtkRadiant 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 GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
----------------------------------------------------------------------------------
This code has been altered significantly from its original form, to support
several games based on the Quake III Arena engine, in the form of "Q3Map2."
------------------------------------------------------------------------------- */
/* marker */
#define TREE_C
/* dependencies */
#include "q3map2.h"
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);
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;
brush_t *bb;
for (i=0 ; i<depth ; i++)
Sys_Printf (" ");
if (node->planenum == PLANENUM_LEAF)
{
if (!node->brushlist)
Sys_Printf ("NULL\n");
else
{
for (bb=node->brushlist ; bb ; bb=bb->next)
Sys_Printf ("%d ", bb->original->brushNum);
Sys_Printf ("\n");
}
return;
}
plane = &mapplanes[node->planenum];
Sys_Printf ("#%d (%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);
}

1130
tools/quake3/q3map2/vis.c Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,643 @@
/* -------------------------------------------------------------------------------
Copyright (C) 1999-2006 Id Software, Inc. and contributors.
For a list of contributors, see the accompanying CONTRIBUTORS file.
This file is part of GtkRadiant.
GtkRadiant 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.
GtkRadiant 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 GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
----------------------------------------------------------------------------------
This code has been altered significantly from its original form, to support
several games based on the Quake III Arena engine, in the form of "Q3Map2."
------------------------------------------------------------------------------- */
/* marker */
#define WRITEBSP_C
/* dependencies */
#include "q3map2.h"
/*
EmitShader()
emits a bsp shader entry
*/
int EmitShader( const char *shader, int *contentFlags, int *surfaceFlags )
{
int i;
shaderInfo_t *si;
/* handle special cases */
if( shader == NULL )
shader = "noshader";
/* try to find an existing shader */
for( i = 0; i < numBSPShaders; i++ )
{
/* ydnar: handle custom surface/content flags */
if( surfaceFlags != NULL && bspShaders[ i ].surfaceFlags != *surfaceFlags )
continue;
if( contentFlags != NULL && bspShaders[ i ].contentFlags != *contentFlags )
continue;
/* compare name */
if( !Q_stricmp( shader, bspShaders[ i ].shader ) )
return i;
}
/* get shaderinfo */
si = ShaderInfoForShader( shader );
/* emit a new shader */
if( i == MAX_MAP_SHADERS )
Error( "MAX_MAP_SHADERS" );
numBSPShaders++;
strcpy( bspShaders[ i ].shader, shader );
bspShaders[ i ].surfaceFlags = si->surfaceFlags;
bspShaders[ i ].contentFlags = si->contentFlags;
/* handle custom content/surface flags */
if( surfaceFlags != NULL )
bspShaders[ i ].surfaceFlags = *surfaceFlags;
if( contentFlags != NULL )
bspShaders[ i ].contentFlags = *contentFlags;
/* recursively emit any damage shaders */
if( si->damageShader != NULL && si->damageShader[ 0 ] != '\0' )
{
Sys_FPrintf( SYS_VRB, "Shader %s has damage shader %s\n", si->shader, si->damageShader );
EmitShader( si->damageShader, NULL, NULL );
}
/* return it */
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;
bspPlane_t *bp;
plane_t *mp;
/* walk plane list */
mp = mapplanes;
for( i = 0; i < nummapplanes; i++, mp++ )
{
bp = &bspPlanes[ numBSPPlanes ];
VectorCopy( mp->normal, bp->normal );
bp->dist = mp->dist;
numBSPPlanes++;
}
/* emit some statistics */
Sys_FPrintf( SYS_VRB, "%9d BSP planes\n", numBSPPlanes );
}
/*
EmitLeaf()
emits a leafnode to the bsp file
*/
void EmitLeaf( node_t *node )
{
bspLeaf_t *leaf_p;
brush_t *b;
drawSurfRef_t *dsr;
int i = 0;
/* check limits */
if( numBSPLeafs >= MAX_MAP_LEAFS )
Error( "MAX_MAP_LEAFS" );
leaf_p = &bspLeafs[numBSPLeafs];
numBSPLeafs++;
leaf_p->cluster = node->cluster;
leaf_p->area = node->area;
/* emit bounding box */
VectorCopy( node->mins, leaf_p->mins );
VectorCopy( node->maxs, leaf_p->maxs );
/* emit leaf brushes */
leaf_p->firstBSPLeafBrush = numBSPLeafBrushes;
for( b = node->brushlist; b; b = b->next )
{
/* something is corrupting brushes */
if( (int) b < 256 )
{
Sys_Printf( "WARNING: Node brush list corrupted (0x%08X)\n", b );
break;
}
//% if( b->guard != 0xDEADBEEF )
//% Sys_Printf( "Brush %6d: 0x%08X Guard: 0x%08X Next: 0x%08X Original: 0x%08X Sides: %d\n", b->brushNum, b, b, b->next, b->original, b->numsides );
if( numBSPLeafBrushes >= MAX_MAP_LEAFBRUSHES )
Error( "MAX_MAP_LEAFBRUSHES" );
bspLeafBrushes[ numBSPLeafBrushes ] = b->original->outputNum;
numBSPLeafBrushes++;
}
leaf_p->numBSPLeafBrushes = numBSPLeafBrushes - leaf_p->firstBSPLeafBrush;
/* emit leaf surfaces */
if( node->opaque )
return;
/* add the drawSurfRef_t drawsurfs */
leaf_p->firstBSPLeafSurface = numBSPLeafSurfaces;
for ( dsr = node->drawSurfReferences; dsr; dsr = dsr->nextRef )
{
if( numBSPLeafSurfaces >= MAX_MAP_LEAFFACES )
Error( "MAX_MAP_LEAFFACES" );
bspLeafSurfaces[ numBSPLeafSurfaces ] = dsr->outputNum;
numBSPLeafSurfaces++;
}
leaf_p->numBSPLeafSurfaces = numBSPLeafSurfaces - leaf_p->firstBSPLeafSurface;
}
/*
EmitDrawNode_r()
recursively emit the bsp nodes
*/
int EmitDrawNode_r( node_t *node )
{
bspNode_t *n;
int i;
/* check for leafnode */
if( node->planenum == PLANENUM_LEAF )
{
EmitLeaf( node );
return -numBSPLeafs;
}
/* emit a node */
if( numBSPNodes == MAX_MAP_NODES )
Error( "MAX_MAP_NODES" );
n = &bspNodes[ numBSPNodes ];
numBSPNodes++;
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] = -(numBSPLeafs + 1);
EmitLeaf (node->children[i]);
}
else
{
n->children[i] = numBSPNodes;
EmitDrawNode_r (node->children[i]);
}
}
return n - bspNodes;
}
/*
============
SetModelNumbers
============
*/
void SetModelNumbers (void)
{
int i;
int models;
char value[10];
models = 1;
for ( i=1 ; i<numEntities ; i++ ) {
if ( entities[i].brushes || entities[i].patches ) {
sprintf ( value, "*%i", models );
models++;
SetKeyValue (&entities[i], "model", value);
}
}
}
/*
SetLightStyles()
sets style keys for entity lights
*/
void SetLightStyles( void )
{
int i, j, style, numStyles;
qboolean keepLights;
const char *t;
entity_t *e;
epair_t *ep, *next;
char value[ 10 ];
char lightTargets[ MAX_SWITCHED_LIGHTS ][ 64 ];
int lightStyles[ MAX_SWITCHED_LIGHTS ];
/* ydnar: determine if we keep lights in the bsp */
t = ValueForKey( &entities[ 0 ], "_keepLights" );
keepLights = (t[ 0 ] == '1') ? qtrue : qfalse;
/* any light that is controlled (has a targetname) must have a unique style number generated for it */
numStyles = 0;
for( i = 1; i < numEntities; i++ )
{
e = &entities[ i ];
t = ValueForKey( e, "classname" );
if( Q_strncasecmp( t, "light", 5 ) )
continue;
t = ValueForKey( e, "targetname" );
if( t[ 0 ] == '\0' )
{
/* ydnar: strip the light from the BSP file */
if( keepLights == qfalse )
{
ep = e->epairs;
while( ep != NULL )
{
next = ep->next;
free( ep->key );
free( ep->value );
free( ep );
ep = next;
}
e->epairs = NULL;
numStrippedLights++;
}
/* next light */
continue;
}
/* get existing style */
style = IntForKey( e, "style" );
if( style < LS_NORMAL || style > LS_NONE )
Error( "Invalid lightstyle (%d) on entity %d", style, i );
/* find this targetname */
for( j = 0; j < numStyles; j++ )
if( lightStyles[ j ] == style && !strcmp( lightTargets[ j ], t ) )
break;
/* add a new style */
if( j >= numStyles )
{
if( numStyles == MAX_SWITCHED_LIGHTS )
Error( "MAX_SWITCHED_LIGHTS (%d) exceeded, reduce the number of lights with targetnames", MAX_SWITCHED_LIGHTS );
strcpy( lightTargets[ j ], t );
lightStyles[ j ] = style;
numStyles++;
}
/* set explicit style */
sprintf( value, "%d", 32 + j );
SetKeyValue( e, "style", value );
/* set old style */
if( style != LS_NORMAL )
{
sprintf( value, "%d", style );
SetKeyValue( e, "switch_style", value );
}
}
/* emit some statistics */
Sys_FPrintf( SYS_VRB, "%9d light entities stripped\n", numStrippedLights );
}
/*
BeginBSPFile()
starts a new bsp file
*/
void BeginBSPFile( void )
{
/* these values may actually be initialized if the file existed when loaded, so clear them explicitly */
numBSPModels = 0;
numBSPNodes = 0;
numBSPBrushSides = 0;
numBSPLeafSurfaces = 0;
numBSPLeafBrushes = 0;
/* leave leaf 0 as an error, because leafs are referenced as negative number nodes */
numBSPLeafs = 1;
/* ydnar: gs mods: set the first 6 drawindexes to 0 1 2 2 1 3 for triangles and quads */
numBSPDrawIndexes = 6;
bspDrawIndexes[ 0 ] = 0;
bspDrawIndexes[ 1 ] = 1;
bspDrawIndexes[ 2 ] = 2;
bspDrawIndexes[ 3 ] = 0;
bspDrawIndexes[ 4 ] = 2;
bspDrawIndexes[ 5 ] = 3;
}
/*
EndBSPFile()
finishes a new bsp and writes to disk
*/
void EndBSPFile( void )
{
char path[ 1024 ];
EmitPlanes();
numBSPEntities = numEntities;
UnparseEntities();
/* write the surface extra file */
WriteSurfaceExtraFile( source );
/* write the bsp */
sprintf( path, "%s.bsp", source );
Sys_Printf( "Writing %s\n", path );
WriteBSPFile( path );
}
/*
EmitBrushes()
writes the brush list to the bsp
*/
void EmitBrushes( brush_t *brushes, int *firstBrush, int *numBrushes )
{
int j;
brush_t *b;
bspBrush_t *db;
bspBrushSide_t *cp;
/* set initial brush */
if( firstBrush != NULL )
*firstBrush = numBSPBrushes;
if( numBrushes != NULL )
*numBrushes = 0;
/* walk list of brushes */
for( b = brushes; b != NULL; b = b->next )
{
/* check limits */
if( numBSPBrushes == MAX_MAP_BRUSHES )
Error( "MAX_MAP_BRUSHES (%d)", numBSPBrushes );
/* get bsp brush */
b->outputNum = numBSPBrushes;
db = &bspBrushes[ numBSPBrushes ];
numBSPBrushes++;
if( numBrushes != NULL )
(*numBrushes)++;
db->shaderNum = EmitShader( b->contentShader->shader, &b->contentShader->contentFlags, &b->contentShader->surfaceFlags );
db->firstSide = numBSPBrushSides;
/* walk sides */
db->numSides = 0;
for( j = 0; j < b->numsides; j++ )
{
/* set output number to bogus initially */
b->sides[ j ].outputNum = -1;
/* check count */
if( numBSPBrushSides == MAX_MAP_BRUSHSIDES )
Error( "MAX_MAP_BRUSHSIDES ");
/* emit side */
b->sides[ j ].outputNum = numBSPBrushSides;
cp = &bspBrushSides[ numBSPBrushSides ];
db->numSides++;
numBSPBrushSides++;
cp->planeNum = b->sides[ j ].planenum;
/* emit shader */
if( b->sides[ j ].shaderInfo )
cp->shaderNum = EmitShader( b->sides[ j ].shaderInfo->shader, &b->sides[ j ].shaderInfo->contentFlags, &b->sides[ j ].shaderInfo->surfaceFlags );
else
cp->shaderNum = EmitShader( NULL, NULL, NULL );
}
}
}
/*
EmitFogs() - ydnar
turns map fogs into bsp fogs
*/
void EmitFogs( void )
{
int i, j;
/* setup */
numBSPFogs = numMapFogs;
/* walk list */
for( i = 0; i < numMapFogs; i++ )
{
/* set shader */
strcpy( bspFogs[ i ].shader, mapFogs[ i ].si->shader );
/* global fog doesn't have an associated brush */
if( mapFogs[ i ].brush == NULL )
{
bspFogs[ i ].brushNum = -1;
bspFogs[ i ].visibleSide = -1;
}
else
{
/* set brush */
bspFogs[ i ].brushNum = mapFogs[ i ].brush->outputNum;
/* try to use forced visible side */
if( mapFogs[ i ].visibleSide >= 0 )
{
bspFogs[ i ].visibleSide = mapFogs[ i ].visibleSide;
continue;
}
/* find visible side */
for( j = 0; j < 6; j++ )
{
if( mapFogs[ i ].brush->sides[ j ].visibleHull != NULL )
{
Sys_Printf( "Fog %d has visible side %d\n", i, j );
bspFogs[ i ].visibleSide = j;
break;
}
}
}
}
}
/*
BeginModel()
sets up a new brush model
*/
void BeginModel( void )
{
bspModel_t *mod;
brush_t *b;
entity_t *e;
vec3_t mins, maxs;
vec3_t lgMins, lgMaxs; /* ydnar: lightgrid mins/maxs */
parseMesh_t *p;
int i;
/* test limits */
if( numBSPModels == MAX_MAP_MODELS )
Error( "MAX_MAP_MODELS" );
/* get model and entity */
mod = &bspModels[ numBSPModels ];
e = &entities[ mapEntityNum ];
/* ydnar: lightgrid mins/maxs */
ClearBounds( lgMins, lgMaxs );
/* bound the brushes */
ClearBounds( mins, maxs );
for ( b = e->brushes; b; b = b->next )
{
/* ignore non-real brushes (origin, etc) */
if( b->numsides == 0 )
continue;
AddPointToBounds( b->mins, mins, maxs );
AddPointToBounds( b->maxs, mins, maxs );
/* ydnar: lightgrid bounds */
if( b->compileFlags & C_LIGHTGRID )
{
AddPointToBounds( b->mins, lgMins, lgMaxs );
AddPointToBounds( b->maxs, lgMins, lgMaxs );
}
}
/* bound patches */
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 );
}
/* ydnar: lightgrid mins/maxs */
if( lgMins[ 0 ] < 99999 )
{
/* use lightgrid bounds */
VectorCopy( lgMins, mod->mins );
VectorCopy( lgMaxs, mod->maxs );
}
else
{
/* use brush/patch bounds */
VectorCopy( mins, mod->mins );
VectorCopy( maxs, mod->maxs );
}
/* note size */
Sys_FPrintf( SYS_VRB, "BSP bounds: { %f %f %f } { %f %f %f }\n", mins[ 0 ], mins[ 1 ], mins[ 2 ], maxs[ 0 ], maxs[ 1 ], maxs[ 2 ] );
Sys_FPrintf( SYS_VRB, "Lightgrid bounds: { %f %f %f } { %f %f %f }\n", lgMins[ 0 ], lgMins[ 1 ], lgMins[ 2 ], lgMaxs[ 0 ], lgMaxs[ 1 ], lgMaxs[ 2 ] );
/* set firsts */
mod->firstBSPSurface = numBSPDrawSurfaces;
mod->firstBSPBrush = numBSPBrushes;
}
/*
EndModel()
finish a model's processing
*/
void EndModel( entity_t *e, node_t *headnode )
{
bspModel_t *mod;
/* note it */
Sys_FPrintf( SYS_VRB, "--- EndModel ---\n" );
/* emit the bsp */
mod = &bspModels[ numBSPModels ];
EmitDrawNode_r( headnode );
/* set surfaces and brushes */
mod->numBSPSurfaces = numBSPDrawSurfaces - mod->firstBSPSurface;
mod->firstBSPBrush = e->firstBrush;
mod->numBSPBrushes = e->numBrushes;
/* increment model count */
numBSPModels++;
}