The Quake III Arena sources as originally released under the GPL license on August 20, 2005.

This commit is contained in:
Travis Bradshaw 2012-01-31 13:41:34 -06:00
commit dbe4ddb103
1409 changed files with 806066 additions and 0 deletions

861
q3map/brush.c Normal file
View file

@ -0,0 +1,861 @@
/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
This file is part of Quake III Arena source code.
Quake III Arena source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
Quake III Arena source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Foobar; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
#include "qbsp.h"
int c_active_brushes;
int c_nodes;
// if a brush just barely pokes onto the other side,
// let it slide by without chopping
#define PLANESIDE_EPSILON 0.001
//0.1
/*
================
CountBrushList
================
*/
int CountBrushList (bspbrush_t *brushes)
{
int c;
c = 0;
for ( ; brushes ; brushes = brushes->next)
c++;
return c;
}
/*
================
AllocBrush
================
*/
bspbrush_t *AllocBrush (int numsides)
{
bspbrush_t *bb;
int c;
c = (int)&(((bspbrush_t *)0)->sides[numsides]);
bb = malloc(c);
memset (bb, 0, c);
if (numthreads == 1)
c_active_brushes++;
return bb;
}
/*
================
FreeBrush
================
*/
void FreeBrush (bspbrush_t *brushes)
{
int i;
for (i=0 ; i<brushes->numsides ; i++)
if (brushes->sides[i].winding)
FreeWinding(brushes->sides[i].winding);
free (brushes);
if (numthreads == 1)
c_active_brushes--;
}
/*
================
FreeBrushList
================
*/
void FreeBrushList (bspbrush_t *brushes)
{
bspbrush_t *next;
for ( ; brushes ; brushes = next)
{
next = brushes->next;
FreeBrush (brushes);
}
}
/*
==================
CopyBrush
Duplicates the brush, the sides, and the windings
==================
*/
bspbrush_t *CopyBrush (bspbrush_t *brush)
{
bspbrush_t *newbrush;
int size;
int i;
size = (int)&(((bspbrush_t *)0)->sides[brush->numsides]);
newbrush = AllocBrush (brush->numsides);
memcpy (newbrush, brush, size);
for (i=0 ; i<brush->numsides ; i++)
{
if (brush->sides[i].winding)
newbrush->sides[i].winding = CopyWinding (brush->sides[i].winding);
}
return newbrush;
}
/*
================
DrawBrushList
================
*/
void DrawBrushList (bspbrush_t *brush)
{
int i;
side_t *s;
GLS_BeginScene ();
for ( ; brush ; brush=brush->next)
{
for (i=0 ; i<brush->numsides ; i++)
{
s = &brush->sides[i];
if (!s->winding)
continue;
GLS_Winding (s->winding, 0);
}
}
GLS_EndScene ();
}
/*
================
WriteBrushList
================
*/
void WriteBrushList (char *name, bspbrush_t *brush, qboolean onlyvis)
{
int i;
side_t *s;
FILE *f;
qprintf ("writing %s\n", name);
f = SafeOpenWrite (name);
for ( ; brush ; brush=brush->next)
{
for (i=0 ; i<brush->numsides ; i++)
{
s = &brush->sides[i];
if (!s->winding)
continue;
if (onlyvis && !s->visible)
continue;
OutputWinding (brush->sides[i].winding, f);
}
}
fclose (f);
}
/*
=============
PrintBrush
=============
*/
void PrintBrush (bspbrush_t *brush)
{
int i;
_printf ("brush: %p\n", brush);
for (i=0;i<brush->numsides ; i++)
{
pw(brush->sides[i].winding);
_printf ("\n");
}
}
/*
==================
BoundBrush
Sets the mins/maxs based on the windings
returns false if the brush doesn't enclose a valid volume
==================
*/
qboolean BoundBrush (bspbrush_t *brush)
{
int i, j;
winding_t *w;
ClearBounds (brush->mins, brush->maxs);
for (i=0 ; i<brush->numsides ; i++)
{
w = brush->sides[i].winding;
if (!w)
continue;
for (j=0 ; j<w->numpoints ; j++)
AddPointToBounds (w->p[j], brush->mins, brush->maxs);
}
for (i=0 ; i<3 ; i++) {
if (brush->mins[i] < MIN_WORLD_COORD || brush->maxs[i] > MAX_WORLD_COORD
|| brush->mins[i] >= brush->maxs[i] ) {
return qfalse;
}
}
return qtrue;
}
/*
==================
CreateBrushWindings
makes basewindigs for sides and mins / maxs for the brush
returns false if the brush doesn't enclose a valid volume
==================
*/
qboolean CreateBrushWindings (bspbrush_t *brush)
{
int i, j;
winding_t *w;
side_t *side;
plane_t *plane;
for ( i = 0; i < brush->numsides; i++ )
{
side = &brush->sides[i];
// don't create a winding for a bevel
if ( side->bevel ) {
continue;
}
plane = &mapplanes[side->planenum];
w = BaseWindingForPlane (plane->normal, plane->dist);
for ( j = 0; j < brush->numsides && w; j++ )
{
if (i == j)
continue;
if ( brush->sides[j].planenum == ( brush->sides[i].planenum ^ 1 ) )
continue; // back side clipaway
if (brush->sides[j].bevel)
continue;
if (brush->sides[j].backSide)
continue;
plane = &mapplanes[brush->sides[j].planenum^1];
ChopWindingInPlace (&w, plane->normal, plane->dist, 0); //CLIP_EPSILON);
}
// free any existing winding
if ( side->winding ) {
FreeWinding( side->winding );
}
side->winding = w;
}
return BoundBrush (brush);
}
/*
==================
BrushFromBounds
Creates a new axial brush
==================
*/
bspbrush_t *BrushFromBounds (vec3_t mins, vec3_t maxs)
{
bspbrush_t *b;
int i;
vec3_t normal;
vec_t dist;
b = AllocBrush (6);
b->numsides = 6;
for (i=0 ; i<3 ; i++)
{
VectorClear (normal);
normal[i] = 1;
dist = maxs[i];
b->sides[i].planenum = FindFloatPlane (normal, dist);
normal[i] = -1;
dist = -mins[i];
b->sides[3+i].planenum = FindFloatPlane (normal, dist);
}
CreateBrushWindings (b);
return b;
}
/*
==================
BrushVolume
==================
*/
vec_t BrushVolume (bspbrush_t *brush)
{
int i;
winding_t *w;
vec3_t corner;
vec_t d, area, volume;
plane_t *plane;
if (!brush)
return 0;
// grab the first valid point as the corner
w = NULL;
for (i=0 ; i<brush->numsides ; i++)
{
w = brush->sides[i].winding;
if (w)
break;
}
if (!w)
return 0;
VectorCopy (w->p[0], corner);
// make tetrahedrons to all other faces
volume = 0;
for ( ; i<brush->numsides ; i++)
{
w = brush->sides[i].winding;
if (!w)
continue;
plane = &mapplanes[brush->sides[i].planenum];
d = -(DotProduct (corner, plane->normal) - plane->dist);
area = WindingArea (w);
volume += d*area;
}
volume /= 3;
return volume;
}
/*
==================
WriteBspBrushMap
==================
*/
void WriteBspBrushMap (char *name, bspbrush_t *list)
{
FILE *f;
side_t *s;
int i;
winding_t *w;
_printf ("writing %s\n", name);
f = fopen (name, "wb");
if (!f)
Error ("Can't write %s\b", name);
fprintf (f, "{\n\"classname\" \"worldspawn\"\n");
for ( ; list ; list=list->next )
{
fprintf (f, "{\n");
for (i=0,s=list->sides ; i<list->numsides ; i++,s++)
{
w = BaseWindingForPlane (mapplanes[s->planenum].normal, mapplanes[s->planenum].dist);
fprintf (f,"( %i %i %i ) ", (int)w->p[0][0], (int)w->p[0][1], (int)w->p[0][2]);
fprintf (f,"( %i %i %i ) ", (int)w->p[1][0], (int)w->p[1][1], (int)w->p[1][2]);
fprintf (f,"( %i %i %i ) ", (int)w->p[2][0], (int)w->p[2][1], (int)w->p[2][2]);
fprintf (f, "notexture 0 0 0 1 1\n" );
FreeWinding (w);
}
fprintf (f, "}\n");
}
fprintf (f, "}\n");
fclose (f);
}
//=====================================================================================
/*
====================
FilterBrushIntoTree_r
====================
*/
int FilterBrushIntoTree_r( bspbrush_t *b, node_t *node ) {
bspbrush_t *front, *back;
int c;
if ( !b ) {
return 0;
}
// add it to the leaf list
if ( node->planenum == PLANENUM_LEAF ) {
b->next = node->brushlist;
node->brushlist = b;
// classify the leaf by the structural brush
if ( !b->detail ) {
if ( b->opaque ) {
node->opaque = qtrue;
node->areaportal = qfalse;
} else if ( b->contents & CONTENTS_AREAPORTAL ) {
if ( !node->opaque ) {
node->areaportal = qtrue;
}
}
}
return 1;
}
// split it by the node plane
SplitBrush ( b, node->planenum, &front, &back );
FreeBrush( b );
c = 0;
c += FilterBrushIntoTree_r( front, node->children[0] );
c += FilterBrushIntoTree_r( back, node->children[1] );
return c;
}
/*
=====================
FilterDetailBrushesIntoTree
Fragment all the detail brushes into the structural leafs
=====================
*/
void FilterDetailBrushesIntoTree( entity_t *e, tree_t *tree ) {
bspbrush_t *b, *newb;
int r;
int c_unique, c_clusters;
int i;
qprintf( "----- FilterDetailBrushesIntoTree -----\n");
c_unique = 0;
c_clusters = 0;
for ( b = e->brushes ; b ; b = b->next ) {
if ( !b->detail ) {
continue;
}
c_unique++;
newb = CopyBrush( b );
r = FilterBrushIntoTree_r( newb, tree->headnode );
c_clusters += r;
// mark all sides as visible so drawsurfs are created
if ( r ) {
for ( i = 0 ; i < b->numsides ; i++ ) {
if ( b->sides[i].winding ) {
b->sides[i].visible = qtrue;
}
}
}
}
qprintf( "%5i detail brushes\n", c_unique );
qprintf( "%5i cluster references\n", c_clusters );
}
/*
=====================
FilterStructuralBrushesIntoTree
Mark the leafs as opaque and areaportals
=====================
*/
void FilterStructuralBrushesIntoTree( entity_t *e, tree_t *tree ) {
bspbrush_t *b, *newb;
int r;
int c_unique, c_clusters;
int i;
qprintf( "----- FilterStructuralBrushesIntoTree -----\n");
c_unique = 0;
c_clusters = 0;
for ( b = e->brushes ; b ; b = b->next ) {
if ( b->detail ) {
continue;
}
c_unique++;
newb = CopyBrush( b );
r = FilterBrushIntoTree_r( newb, tree->headnode );
c_clusters += r;
// mark all sides as visible so drawsurfs are created
if ( r ) {
for ( i = 0 ; i < b->numsides ; i++ ) {
if ( b->sides[i].winding ) {
b->sides[i].visible = qtrue;
}
}
}
}
qprintf( "%5i structural brushes\n", c_unique );
qprintf( "%5i cluster references\n", c_clusters );
}
/*
================
AllocTree
================
*/
tree_t *AllocTree (void)
{
tree_t *tree;
tree = malloc(sizeof(*tree));
memset (tree, 0, sizeof(*tree));
ClearBounds (tree->mins, tree->maxs);
return tree;
}
/*
================
AllocNode
================
*/
node_t *AllocNode (void)
{
node_t *node;
node = malloc(sizeof(*node));
memset (node, 0, sizeof(*node));
return node;
}
/*
================
WindingIsTiny
Returns true if the winding would be crunched out of
existance by the vertex snapping.
================
*/
#define EDGE_LENGTH 0.2
qboolean WindingIsTiny (winding_t *w)
{
/*
if (WindingArea (w) < 1)
return qtrue;
return qfalse;
*/
int i, j;
vec_t len;
vec3_t delta;
int edges;
edges = 0;
for (i=0 ; i<w->numpoints ; i++)
{
j = i == w->numpoints - 1 ? 0 : i+1;
VectorSubtract (w->p[j], w->p[i], delta);
len = VectorLength (delta);
if (len > EDGE_LENGTH)
{
if (++edges == 3)
return qfalse;
}
}
return qtrue;
}
/*
================
WindingIsHuge
Returns true if the winding still has one of the points
from basewinding for plane
================
*/
qboolean WindingIsHuge (winding_t *w)
{
int i, j;
for (i=0 ; i<w->numpoints ; i++)
{
for (j=0 ; j<3 ; j++)
if (w->p[i][j] <= MIN_WORLD_COORD || w->p[i][j] >= MAX_WORLD_COORD)
return qtrue;
}
return qfalse;
}
//============================================================
/*
==================
BrushMostlyOnSide
==================
*/
int BrushMostlyOnSide (bspbrush_t *brush, plane_t *plane)
{
int i, j;
winding_t *w;
vec_t d, max;
int side;
max = 0;
side = PSIDE_FRONT;
for (i=0 ; i<brush->numsides ; i++)
{
w = brush->sides[i].winding;
if (!w)
continue;
for (j=0 ; j<w->numpoints ; j++)
{
d = DotProduct (w->p[j], plane->normal) - plane->dist;
if (d > max)
{
max = d;
side = PSIDE_FRONT;
}
if (-d > max)
{
max = -d;
side = PSIDE_BACK;
}
}
}
return side;
}
/*
================
SplitBrush
Generates two new brushes, leaving the original
unchanged
================
*/
void SplitBrush (bspbrush_t *brush, int planenum,
bspbrush_t **front, bspbrush_t **back)
{
bspbrush_t *b[2];
int i, j;
winding_t *w, *cw[2], *midwinding;
plane_t *plane, *plane2;
side_t *s, *cs;
float d, d_front, d_back;
*front = *back = NULL;
plane = &mapplanes[planenum];
// check all points
d_front = d_back = 0;
for (i=0 ; i<brush->numsides ; i++)
{
w = brush->sides[i].winding;
if (!w)
continue;
for (j=0 ; j<w->numpoints ; j++)
{
d = DotProduct (w->p[j], plane->normal) - plane->dist;
if (d > 0 && d > d_front)
d_front = d;
if (d < 0 && d < d_back)
d_back = d;
}
}
if (d_front < 0.1) // PLANESIDE_EPSILON)
{ // only on back
*back = CopyBrush (brush);
return;
}
if (d_back > -0.1) // PLANESIDE_EPSILON)
{ // only on front
*front = CopyBrush (brush);
return;
}
// create a new winding from the split plane
w = BaseWindingForPlane (plane->normal, plane->dist);
for (i=0 ; i<brush->numsides && w ; i++)
{
if ( brush->sides[i].backSide ) {
continue; // fake back-sided polygons never split
}
plane2 = &mapplanes[brush->sides[i].planenum ^ 1];
ChopWindingInPlace (&w, plane2->normal, plane2->dist, 0); // PLANESIDE_EPSILON);
}
if (!w || WindingIsTiny (w) )
{ // the brush isn't really split
int side;
side = BrushMostlyOnSide (brush, plane);
if (side == PSIDE_FRONT)
*front = CopyBrush (brush);
if (side == PSIDE_BACK)
*back = CopyBrush (brush);
return;
}
if (WindingIsHuge (w))
{
qprintf ("WARNING: huge winding\n");
}
midwinding = w;
// split it for real
for (i=0 ; i<2 ; i++)
{
b[i] = AllocBrush (brush->numsides+1);
memcpy( b[i], brush, sizeof( bspbrush_t ) - sizeof( brush->sides ) );
b[i]->numsides = 0;
b[i]->next = NULL;
b[i]->original = brush->original;
}
// split all the current windings
for (i=0 ; i<brush->numsides ; i++)
{
s = &brush->sides[i];
w = s->winding;
if (!w)
continue;
ClipWindingEpsilon (w, plane->normal, plane->dist,
0 /*PLANESIDE_EPSILON*/, &cw[0], &cw[1]);
for (j=0 ; j<2 ; j++)
{
if (!cw[j])
continue;
/*
if (WindingIsTiny (cw[j]))
{
FreeWinding (cw[j]);
continue;
}
*/
cs = &b[j]->sides[b[j]->numsides];
b[j]->numsides++;
*cs = *s;
cs->winding = cw[j];
}
}
// see if we have valid polygons on both sides
for (i=0 ; i<2 ; i++)
{
BoundBrush (b[i]);
for (j=0 ; j<3 ; j++)
{
if (b[i]->mins[j] < MIN_WORLD_COORD || b[i]->maxs[j] > MAX_WORLD_COORD)
{
qprintf ("bogus brush after clip\n");
break;
}
}
if (b[i]->numsides < 3 || j < 3)
{
FreeBrush (b[i]);
b[i] = NULL;
}
}
if ( !(b[0] && b[1]) )
{
if (!b[0] && !b[1])
qprintf ("split removed brush\n");
else
qprintf ("split not on both sides\n");
if (b[0])
{
FreeBrush (b[0]);
*front = CopyBrush (brush);
}
if (b[1])
{
FreeBrush (b[1]);
*back = CopyBrush (brush);
}
return;
}
// add the midwinding to both sides
for (i=0 ; i<2 ; i++)
{
cs = &b[i]->sides[b[i]->numsides];
b[i]->numsides++;
cs->planenum = planenum^i^1;
cs->shaderInfo = NULL;
if (i==0)
cs->winding = CopyWinding (midwinding);
else
cs->winding = midwinding;
}
{
vec_t v1;
int i;
for (i=0 ; i<2 ; i++)
{
v1 = BrushVolume (b[i]);
if (v1 < 1.0)
{
FreeBrush (b[i]);
b[i] = NULL;
// qprintf ("tiny volume after clip\n");
}
}
}
*front = b[0];
*back = b[1];
}

52
q3map/brush_primit.c Normal file
View file

@ -0,0 +1,52 @@
/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
This file is part of Quake III Arena source code.
Quake III Arena source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
Quake III Arena source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Foobar; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
#include "qbsp.h"
// global flag
int g_bBrushPrimit;
// NOTE : ComputeAxisBase here and in editor code must always BE THE SAME !
// WARNING : special case behaviour of atan2(y,x) <-> atan(y/x) might not be the same everywhere when x == 0
// rotation by (0,RotY,RotZ) assigns X to normal
void ComputeAxisBase(vec3_t normal,vec3_t texX,vec3_t texY)
{
vec_t RotY,RotZ;
// do some cleaning
if (fabs(normal[0])<1e-6)
normal[0]=0.0f;
if (fabs(normal[1])<1e-6)
normal[1]=0.0f;
if (fabs(normal[2])<1e-6)
normal[2]=0.0f;
// compute the two rotations around Y and Z to rotate X to normal
RotY=-atan2(normal[2],sqrt(normal[1]*normal[1]+normal[0]*normal[0]));
RotZ=atan2(normal[1],normal[0]);
// rotate (0,1,0) and (0,0,1) to compute texX and texY
texX[0]=-sin(RotZ);
texX[1]=cos(RotZ);
texX[2]=0;
// the texY vector is along -Z ( T texture coorinates axis )
texY[0]=-sin(RotY)*cos(RotZ);
texY[1]=-sin(RotY)*sin(RotZ);
texY[2]=-cos(RotY);
}

605
q3map/bsp.c Normal file
View file

@ -0,0 +1,605 @@
/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
This file is part of Quake III Arena source code.
Quake III Arena source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
Quake III Arena source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Foobar; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
#include "qbsp.h"
#ifdef _WIN32
#ifdef _TTIMOBUILD
#include "pakstuff.h"
#else
#include "../libs/pakstuff.h"
#endif
extern HWND hwndOut;
#endif
char source[1024];
char tempsource[1024];
char name[1024];
vec_t microvolume = 1.0;
qboolean glview;
qboolean nodetail;
qboolean fulldetail;
qboolean onlyents;
qboolean onlytextures;
qboolean nowater;
qboolean nofill;
qboolean noopt;
qboolean leaktest;
qboolean verboseentities;
qboolean noCurveBrushes;
qboolean fakemap;
qboolean notjunc;
qboolean nomerge;
qboolean nofog;
qboolean nosubdivide;
qboolean testExpand;
qboolean showseams;
char outbase[32];
int entity_num;
/*
============
ProcessWorldModel
============
*/
void ProcessWorldModel( void ) {
entity_t *e;
tree_t *tree;
bspface_t *faces;
qboolean leaked;
BeginModel();
e = &entities[0];
e->firstDrawSurf = 0;//numMapDrawSurfs;
// check for patches with adjacent edges that need to LOD together
PatchMapDrawSurfs( e );
// build an initial bsp tree using all of the sides
// of all of the structural brushes
faces = MakeStructuralBspFaceList ( entities[0].brushes );
tree = FaceBSP( faces );
MakeTreePortals (tree);
FilterStructuralBrushesIntoTree( e, tree );
// see if the bsp is completely enclosed
if ( FloodEntities (tree) ) {
// rebuild a better bsp tree using only the
// sides that are visible from the inside
FillOutside (tree->headnode);
// chop the sides to the convex hull of
// their visible fragments, giving us the smallest
// polygons
ClipSidesIntoTree( e, tree );
faces = MakeVisibleBspFaceList( entities[0].brushes );
FreeTree (tree);
tree = FaceBSP( faces );
MakeTreePortals( tree );
FilterStructuralBrushesIntoTree( e, tree );
leaked = qfalse;
} else {
_printf ("**********************\n");
_printf ("******* leaked *******\n");
_printf ("**********************\n");
LeakFile (tree);
if ( leaktest ) {
_printf ("--- MAP LEAKED, ABORTING LEAKTEST ---\n");
exit (0);
}
leaked = qtrue;
// chop the sides to the convex hull of
// their visible fragments, giving us the smallest
// polygons
ClipSidesIntoTree( e, tree );
}
// save out information for visibility processing
NumberClusters( tree );
if ( !leaked ) {
WritePortalFile( tree );
}
if ( glview ) {
// dump the portals for debugging
WriteGLView( tree, source );
}
FloodAreas (tree);
// add references to the detail brushes
FilterDetailBrushesIntoTree( e, tree );
// create drawsurfs for triangle models
AddTriangleModels( tree );
// drawsurfs that cross fog boundaries will need to
// be split along the bound
if ( !nofog ) {
FogDrawSurfs(); // may fragment drawsurfs
}
// subdivide each drawsurf as required by shader tesselation
if ( !nosubdivide ) {
SubdivideDrawSurfs( e, tree );
}
// merge together all common shaders on the same plane and remove
// all colinear points, so extra tjunctions won't be generated
if ( !nomerge ) {
MergeSides( e, tree ); // !@# testing
}
// add in any vertexes required to fix tjunctions
if ( !notjunc ) {
FixTJunctions( e );
}
// allocate lightmaps for faces and patches
AllocateLightmaps( e );
// add references to the final drawsurfs in the apropriate clusters
FilterDrawsurfsIntoTree( e, tree );
EndModel( tree->headnode );
FreeTree (tree);
}
/*
============
ProcessSubModel
============
*/
void ProcessSubModel( void ) {
entity_t *e;
tree_t *tree;
bspbrush_t *b, *bc;
node_t *node;
BeginModel ();
e = &entities[entity_num];
e->firstDrawSurf = numMapDrawSurfs;
PatchMapDrawSurfs( e );
// just put all the brushes in an empty leaf
// FIXME: patches?
node = AllocNode();
node->planenum = PLANENUM_LEAF;
for ( b = e->brushes ; b ; b = b->next ) {
bc = CopyBrush( b );
bc->next = node->brushlist;
node->brushlist = bc;
}
tree = AllocTree();
tree->headnode = node;
ClipSidesIntoTree( e, tree );
// subdivide each drawsurf as required by shader tesselation or fog
if ( !nosubdivide ) {
SubdivideDrawSurfs( e, tree );
}
// merge together all common shaders on the same plane and remove
// all colinear points, so extra tjunctions won't be generated
if ( !nomerge ) {
MergeSides( e, tree ); // !@# testing
}
// add in any vertexes required to fix tjunctions
if ( !notjunc ) {
FixTJunctions( e );
}
// allocate lightmaps for faces and patches
AllocateLightmaps( e );
// add references to the final drawsurfs in the apropriate clusters
FilterDrawsurfsIntoTree( e, tree );
EndModel ( node );
FreeTree( tree );
}
/*
============
ProcessModels
============
*/
void ProcessModels (void)
{
qboolean oldVerbose;
entity_t *entity;
oldVerbose = verbose;
BeginBSPFile ();
for ( entity_num=0 ; entity_num< num_entities ; entity_num++ ) {
entity = &entities[entity_num];
if ( !entity->brushes && !entity->patches ) {
continue;
}
qprintf ("############### model %i ###############\n", nummodels);
if (entity_num == 0)
ProcessWorldModel ();
else
ProcessSubModel ();
if (!verboseentities)
verbose = qfalse; // don't bother printing submodels
}
verbose = oldVerbose;
}
/*
============
Bspinfo
============
*/
void Bspinfo( int count, char **fileNames ) {
int i;
char source[1024];
int size;
FILE *f;
if ( count < 1 ) {
_printf( "No files to dump info for.\n");
return;
}
for ( i = 0 ; i < count ; i++ ) {
_printf ("---------------------\n");
strcpy (source, fileNames[ i ] );
DefaultExtension (source, ".bsp");
f = fopen (source, "rb");
if (f)
{
size = Q_filelength (f);
fclose (f);
}
else
size = 0;
_printf ("%s: %i\n", source, size);
LoadBSPFile (source);
PrintBSPFileSizes ();
_printf ("---------------------\n");
}
}
/*
============
OnlyEnts
============
*/
void OnlyEnts( void ) {
char out[1024];
sprintf (out, "%s.bsp", source);
LoadBSPFile (out);
num_entities = 0;
LoadMapFile (name);
SetModelNumbers ();
SetLightStyles ();
UnparseEntities ();
WriteBSPFile (out);
}
/*
============
OnlyTextures
============
*/
void OnlyTextures( void ) { // FIXME!!!
char out[1024];
int i;
Error( "-onlytextures isn't working now..." );
sprintf (out, "%s.bsp", source);
LoadMapFile (name);
LoadBSPFile (out);
// replace all the drawsurface shader names
for ( i = 0 ; i < numDrawSurfaces ; i++ ) {
}
WriteBSPFile (out);
}
/*
============
main
============
*/
int LightMain( int argc, char **argv );
int VLightMain (int argc, char **argv);
int VSoundMain (int argc, char **argv);
int VisMain( int argc, char **argv );
int main (int argc, char **argv) {
int i;
double start, end;
char path[1024];
_printf ("Q3Map v1.0s (c) 1999 Id Software Inc.\n");
if ( argc < 2 ) {
Error ("usage: q3map [options] mapfile");
}
// check for general program options
if (!strcmp(argv[1], "-info")) {
Bspinfo( argc - 2, argv + 2 );
return 0;
}
if (!strcmp(argv[1], "-light")) {
LightMain( argc - 1, argv + 1 );
return 0;
}
if (!strcmp(argv[1], "-vlight")) {
VLightMain( argc - 1, argv + 1 );
return 0;
}
if (!strcmp(argv[1], "-vsound")) {
VSoundMain( argc - 1, argv + 1 );
return 0;
}
if (!strcmp(argv[1], "-vis")) {
VisMain( argc - 1, argv + 1 );
return 0;
}
// do a bsp if nothing else was specified
_printf ("---- q3map ----\n");
tempsource[0] = '\0';
for (i=1 ; i<argc ; i++)
{
if (!strcmp(argv[i],"-tempname"))
{
strcpy(tempsource, argv[++i]);
}
else if (!strcmp(argv[i],"-threads"))
{
numthreads = atoi (argv[i+1]);
i++;
}
else if (!strcmp(argv[i],"-glview"))
{
glview = qtrue;
}
else if (!strcmp(argv[i], "-v"))
{
_printf ("verbose = true\n");
verbose = qtrue;
}
else if (!strcmp(argv[i], "-draw"))
{
_printf ("drawflag = true\n");
drawflag = qtrue;
}
else if (!strcmp(argv[i], "-nowater"))
{
_printf ("nowater = true\n");
nowater = qtrue;
}
else if (!strcmp(argv[i], "-noopt"))
{
_printf ("noopt = true\n");
noopt = qtrue;
}
else if (!strcmp(argv[i], "-nofill"))
{
_printf ("nofill = true\n");
nofill = qtrue;
}
else if (!strcmp(argv[i], "-nodetail"))
{
_printf ("nodetail = true\n");
nodetail = qtrue;
}
else if (!strcmp(argv[i], "-fulldetail"))
{
_printf ("fulldetail = true\n");
fulldetail = qtrue;
}
else if (!strcmp(argv[i], "-onlyents"))
{
_printf ("onlyents = true\n");
onlyents = qtrue;
}
else if (!strcmp(argv[i], "-onlytextures"))
{
_printf ("onlytextures = true\n"); // FIXME: make work again!
onlytextures = qtrue;
}
else if (!strcmp(argv[i], "-micro"))
{
microvolume = atof(argv[i+1]);
_printf ("microvolume = %f\n", microvolume);
i++;
}
else if (!strcmp(argv[i], "-nofog"))
{
_printf ("nofog = true\n");
nofog = qtrue;
}
else if (!strcmp(argv[i], "-nosubdivide"))
{
_printf ("nosubdivide = true\n");
nosubdivide = qtrue;
}
else if (!strcmp(argv[i], "-leaktest"))
{
_printf ("leaktest = true\n");
leaktest = qtrue;
}
else if (!strcmp(argv[i], "-verboseentities"))
{
_printf ("verboseentities = true\n");
verboseentities = qtrue;
}
else if (!strcmp(argv[i], "-nocurves"))
{
noCurveBrushes = qtrue;
_printf ("no curve brushes\n");
}
else if (!strcmp(argv[i], "-notjunc"))
{
notjunc = qtrue;
_printf ("no tjunction fixing\n");
}
else if (!strcmp(argv[i], "-expand"))
{
testExpand = qtrue;
_printf ("Writing expanded.map.\n");
}
else if (!strcmp(argv[i], "-showseams"))
{
showseams = qtrue;
_printf ("Showing seams on terrain.\n");
}
else if (!strcmp (argv[i],"-tmpout"))
{
strcpy (outbase, "/tmp");
}
else if (!strcmp (argv[i],"-fakemap"))
{
fakemap = qtrue;
_printf( "will generate fakemap.map\n");
}
else if (!strcmp(argv[i], "-samplesize"))
{
samplesize = atoi(argv[i+1]);
if (samplesize < 1) samplesize = 1;
i++;
_printf("lightmap sample size is %dx%d units\n", samplesize, samplesize);
}
else if (argv[i][0] == '-')
Error ("Unknown option \"%s\"", argv[i]);
else
break;
}
if (i != argc - 1)
Error ("usage: q3map [options] mapfile");
start = I_FloatTime ();
ThreadSetDefault ();
//numthreads = 1; // multiple threads aren't helping because of heavy malloc use
SetQdirFromPath (argv[i]);
#ifdef _WIN32
InitPakFile(gamedir, NULL);
#endif
strcpy (source, ExpandArg (argv[i]));
StripExtension (source);
// delete portal and line files
sprintf (path, "%s.prt", source);
remove (path);
sprintf (path, "%s.lin", source);
remove (path);
strcpy (name, ExpandArg (argv[i]));
if ( strcmp(name + strlen(name) - 4, ".reg" ) ) {
// if we are doing a full map, delete the last saved region map
sprintf (path, "%s.reg", source);
remove (path);
DefaultExtension (name, ".map"); // might be .reg
}
//
// if onlyents, just grab the entites and resave
//
if ( onlyents ) {
OnlyEnts();
return 0;
}
//
// if onlytextures, just grab the textures and resave
//
if ( onlytextures ) {
OnlyTextures();
return 0;
}
//
// start from scratch
//
LoadShaderInfo();
// load original file from temp spot in case it was renamed by the editor on the way in
if (strlen(tempsource) > 0) {
LoadMapFile (tempsource);
} else {
LoadMapFile (name);
}
SetModelNumbers ();
SetLightStyles ();
ProcessModels ();
EndBSPFile();
end = I_FloatTime ();
_printf ("%5.0f seconds elapsed\n", end-start);
// remove temp name if appropriate
if (strlen(tempsource) > 0) {
remove(tempsource);
}
return 0;
}

379
q3map/facebsp.c Normal file
View file

@ -0,0 +1,379 @@
/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
This file is part of Quake III Arena source code.
Quake III Arena source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
Quake III Arena source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Foobar; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
#include "qbsp.h"
int c_faceLeafs;
/*
================
AllocBspFace
================
*/
bspface_t *AllocBspFace( void ) {
bspface_t *f;
f = malloc(sizeof(*f));
memset( f, 0, sizeof(*f) );
return f;
}
/*
================
FreeBspFace
================
*/
void FreeBspFace( bspface_t *f ) {
if ( f->w ) {
FreeWinding( f->w );
}
free( f );
}
/*
================
SelectSplitPlaneNum
================
*/
int hintsplit;
#define BLOCK_SIZE 1024
int SelectSplitPlaneNum( node_t *node, bspface_t *list ) {
bspface_t *split;
bspface_t *check;
bspface_t *bestSplit;
int splits, facing, front, back;
int side;
plane_t *plane;
int value, bestValue;
int i;
vec3_t normal;
float dist;
int planenum;
hintsplit = qfalse;
// if it is crossing a 1k block boundary, force a split
for ( i = 0 ; i < 2 ; i++ ) {
dist = BLOCK_SIZE * ( floor( node->mins[i] / BLOCK_SIZE ) + 1 );
if ( node->maxs[i] > dist ) {
VectorClear( normal );
normal[i] = 1;
planenum = FindFloatPlane( normal, dist );
return planenum;
}
}
// pick one of the face planes
bestValue = -99999;
bestSplit = list;
for ( split = list ; split ; split = split->next ) {
split->checked = qfalse;
}
for ( split = list ; split ; split = split->next ) {
if ( split->checked ) {
continue;
}
plane = &mapplanes[ split->planenum ];
splits = 0;
facing = 0;
front = 0;
back = 0;
for ( check = list ; check ; check = check->next ) {
if ( check->planenum == split->planenum ) {
facing++;
check->checked = qtrue; // won't need to test this plane again
continue;
}
side = WindingOnPlaneSide( check->w, plane->normal, plane->dist );
if ( side == SIDE_CROSS ) {
splits++;
} else if ( side == SIDE_FRONT ) {
front++;
} else if ( side == SIDE_BACK ) {
back++;
}
}
value = 5*facing - 5*splits; // - abs(front-back);
if ( plane->type < 3 ) {
value+=5; // axial is better
}
value += split->priority; // prioritize hints higher
if ( value > bestValue ) {
bestValue = value;
bestSplit = split;
}
}
if ( bestValue == -99999 ) {
return -1;
}
if (bestSplit->hint)
hintsplit = qtrue;
return bestSplit->planenum;
}
int CountFaceList( bspface_t *list ) {
int c;
c = 0;
for ( ; list ; list = list->next ) {
c++;
}
return c;
}
/*
================
BuildFaceTree_r
================
*/
void BuildFaceTree_r( node_t *node, bspface_t *list ) {
bspface_t *split;
bspface_t *next;
int side;
plane_t *plane;
bspface_t *newFace;
bspface_t *childLists[2];
winding_t *frontWinding, *backWinding;
int i;
int splitPlaneNum;
i = CountFaceList( list );
splitPlaneNum = SelectSplitPlaneNum( node, list );
// if we don't have any more faces, this is a node
if ( splitPlaneNum == -1 ) {
node->planenum = PLANENUM_LEAF;
c_faceLeafs++;
return;
}
// partition the list
node->planenum = splitPlaneNum;
node->hint = hintsplit;
plane = &mapplanes[ splitPlaneNum ];
childLists[0] = NULL;
childLists[1] = NULL;
for ( split = list ; split ; split = next ) {
next = split->next;
if ( split->planenum == node->planenum ) {
FreeBspFace( split );
continue;
}
side = WindingOnPlaneSide( split->w, plane->normal, plane->dist );
if ( side == SIDE_CROSS ) {
ClipWindingEpsilon( split->w, plane->normal, plane->dist, CLIP_EPSILON * 2,
&frontWinding, &backWinding );
if ( frontWinding ) {
newFace = AllocBspFace();
newFace->w = frontWinding;
newFace->next = childLists[0];
newFace->planenum = split->planenum;
newFace->priority = split->priority;
newFace->hint = split->hint;
childLists[0] = newFace;
}
if ( backWinding ) {
newFace = AllocBspFace();
newFace->w = backWinding;
newFace->next = childLists[1];
newFace->planenum = split->planenum;
newFace->priority = split->priority;
newFace->hint = split->hint;
childLists[1] = newFace;
}
FreeBspFace( split );
} else if ( side == SIDE_FRONT ) {
split->next = childLists[0];
childLists[0] = split;
} else if ( side == SIDE_BACK ) {
split->next = childLists[1];
childLists[1] = split;
}
}
// recursively process children
for ( i = 0 ; i < 2 ; i++ ) {
node->children[i] = AllocNode();
node->children[i]->parent = node;
VectorCopy( node->mins, node->children[i]->mins );
VectorCopy( node->maxs, node->children[i]->maxs );
}
for ( i = 0 ; i < 3 ; i++ ) {
if ( plane->normal[i] == 1 ) {
node->children[0]->mins[i] = plane->dist;
node->children[1]->maxs[i] = plane->dist;
break;
}
}
for ( i = 0 ; i < 2 ; i++ ) {
BuildFaceTree_r ( node->children[i], childLists[i]);
}
}
/*
================
FaceBSP
List will be freed before returning
================
*/
tree_t *FaceBSP( bspface_t *list ) {
tree_t *tree;
bspface_t *face;
int i;
int count;
qprintf( "--- FaceBSP ---\n" );
tree = AllocTree ();
count = 0;
for ( face = list ; face ; face = face->next ) {
count++;
for ( i = 0 ; i < face->w->numpoints ; i++ ) {
AddPointToBounds( face->w->p[i], tree->mins, tree->maxs);
}
}
qprintf( "%5i faces\n", count );
tree->headnode = AllocNode();
VectorCopy( tree->mins, tree->headnode->mins );
VectorCopy( tree->maxs, tree->headnode->maxs );
c_faceLeafs = 0;
BuildFaceTree_r ( tree->headnode, list );
qprintf( "%5i leafs\n", c_faceLeafs );
return tree;
}
/*
=================
BspFaceForPortal
=================
*/
bspface_t *BspFaceForPortal( portal_t *p ) {
bspface_t *f;
f = AllocBspFace();
f->w = CopyWinding( p->winding );
f->planenum = p->onnode->planenum & ~1;
return f;
}
/*
=================
MakeStructuralBspFaceList
=================
*/
bspface_t *MakeStructuralBspFaceList( bspbrush_t *list ) {
bspbrush_t *b;
int i;
side_t *s;
winding_t *w;
bspface_t *f, *flist;
flist = NULL;
for ( b = list ; b ; b = b->next ) {
if ( b->detail ) {
continue;
}
for ( i = 0 ; i < b->numsides ; i++ ) {
s = &b->sides[i];
w = s->winding;
if ( !w ) {
continue;
}
f = AllocBspFace();
f->w = CopyWinding( w );
f->planenum = s->planenum & ~1;
f->next = flist;
if (s->surfaceFlags & SURF_HINT) {
//f->priority = HINT_PRIORITY;
f->hint = qtrue;
}
flist = f;
}
}
return flist;
}
/*
=================
MakeVisibleBspFaceList
=================
*/
bspface_t *MakeVisibleBspFaceList( bspbrush_t *list ) {
bspbrush_t *b;
int i;
side_t *s;
winding_t *w;
bspface_t *f, *flist;
flist = NULL;
for ( b = list ; b ; b = b->next ) {
if ( b->detail ) {
continue;
}
for ( i = 0 ; i < b->numsides ; i++ ) {
s = &b->sides[i];
w = s->visibleHull;
if ( !w ) {
continue;
}
f = AllocBspFace();
f->w = CopyWinding( w );
f->planenum = s->planenum & ~1;
f->next = flist;
if (s->surfaceFlags & SURF_HINT) {
//f->priority = HINT_PRIORITY;
f->hint = qtrue;
}
flist = f;
}
}
return flist;
}

554
q3map/fog.c Normal file
View file

@ -0,0 +1,554 @@
/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
This file is part of Quake III Arena source code.
Quake III Arena source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
Quake III Arena source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Foobar; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
#include "qbsp.h"
int c_fogFragment;
int c_fogPatchFragments;
/*
====================
DrawSurfToMesh
====================
*/
mesh_t *DrawSurfToMesh( mapDrawSurface_t *ds ) {
mesh_t *m;
m = malloc( sizeof( *m ) );
m->width = ds->patchWidth;
m->height = ds->patchHeight;
m->verts = malloc( sizeof(m->verts[0]) * m->width * m->height );
memcpy( m->verts, ds->verts, sizeof(m->verts[0]) * m->width * m->height );
return m;
}
/*
====================
SplitMeshByPlane
====================
*/
void SplitMeshByPlane( mesh_t *in, vec3_t normal, float dist, mesh_t **front, mesh_t **back ) {
int w, h, split;
float d[MAX_PATCH_SIZE][MAX_PATCH_SIZE];
drawVert_t *dv, *v1, *v2;
int c_front, c_back, c_on;
mesh_t *f, *b;
int i;
float frac;
int frontAprox, backAprox;
for ( i = 0 ; i < 2 ; i++ ) {
dv = in->verts;
c_front = 0;
c_back = 0;
c_on = 0;
for ( h = 0 ; h < in->height ; h++ ) {
for ( w = 0 ; w < in->width ; w++, dv++ ) {
d[h][w] = DotProduct( dv->xyz, normal ) - dist;
if ( d[h][w] > ON_EPSILON ) {
c_front++;
} else if ( d[h][w] < -ON_EPSILON ) {
c_back++;
} else {
c_on++;
}
}
}
*front = NULL;
*back = NULL;
if ( !c_front ) {
*back = in;
return;
}
if ( !c_back ) {
*front = in;
return;
}
// find a split point
split = -1;
for ( w = 0 ; w < in->width -1 ; w++ ) {
if ( ( d[0][w] < 0 ) != ( d[0][w+1] < 0 ) ) {
if ( split == -1 ) {
split = w;
break;
}
}
}
if ( split == -1 ) {
if ( i == 1 ) {
qprintf( "No crossing points in patch\n");
*front = in;
return;
}
in = TransposeMesh( in );
InvertMesh( in );
continue;
}
// make sure the split point stays the same for all other rows
for ( h = 1 ; h < in->height ; h++ ) {
for ( w = 0 ; w < in->width -1 ; w++ ) {
if ( ( d[h][w] < 0 ) != ( d[h][w+1] < 0 ) ) {
if ( w != split ) {
_printf( "multiple crossing points for patch -- can't clip\n");
*front = in;
return;
}
}
}
if ( ( d[h][split] < 0 ) == ( d[h][split+1] < 0 ) ) {
_printf( "differing crossing points for patch -- can't clip\n");
*front = in;
return;
}
}
break;
}
// create two new meshes
f = malloc( sizeof( *f ) );
f->width = split + 2;
if ( ! (f->width & 1) ) {
f->width++;
frontAprox = 1;
} else {
frontAprox = 0;
}
if ( f->width > MAX_PATCH_SIZE ) {
Error( "MAX_PATCH_SIZE after split");
}
f->height = in->height;
f->verts = malloc( sizeof(f->verts[0]) * f->width * f->height );
b = malloc( sizeof( *b ) );
b->width = in->width - split;
if ( ! (b->width & 1) ) {
b->width++;
backAprox = 1;
} else {
backAprox = 0;
}
if ( b->width > MAX_PATCH_SIZE ) {
Error( "MAX_PATCH_SIZE after split");
}
b->height = in->height;
b->verts = malloc( sizeof(b->verts[0]) * b->width * b->height );
if ( d[0][0] > 0 ) {
*front = f;
*back = b;
} else {
*front = b;
*back = f;
}
// distribute the points
for ( w = 0 ; w < in->width ; w++ ) {
for ( h = 0 ; h < in->height ; h++ ) {
if ( w <= split ) {
f->verts[ h * f->width + w ] = in->verts[ h * in->width + w ];
} else {
b->verts[ h * b->width + w - split + backAprox ] = in->verts[ h * in->width + w ];
}
}
}
// clip the crossing line
for ( h = 0 ; h < in->height ; h++ ) {
dv = &f->verts[ h * f->width + split + 1 ];
v1 = &in->verts[ h * in->width + split ];
v2 = &in->verts[ h * in->width + split + 1 ];
frac = d[h][split] / ( d[h][split] - d[h][split+1] );
for ( i = 0 ; i < 10 ; i++ ) {
dv->xyz[i] = v1->xyz[i] + frac * ( v2->xyz[i] - v1->xyz[i] );
}
dv->xyz[10] = 0;//set all 4 colors to 0
if ( frontAprox ) {
f->verts[ h * f->width + split + 2 ] = *dv;
}
b->verts[ h * b->width ] = *dv;
if ( backAprox ) {
b->verts[ h * b->width + 1 ] = *dv;
}
}
/*
PrintMesh( in );
_printf("\n");
PrintMesh( f );
_printf("\n");
PrintMesh( b );
_printf("\n");
*/
FreeMesh( in );
}
/*
====================
ChopPatchByBrush
====================
*/
qboolean ChopPatchByBrush( mapDrawSurface_t *ds, bspbrush_t *b ) {
int i, j;
side_t *s;
plane_t *plane;
mesh_t *outside[MAX_BRUSH_SIDES];
int numOutside;
mesh_t *m, *front, *back;
mapDrawSurface_t *newds;
m = DrawSurfToMesh( ds );
numOutside = 0;
// only split by the top and bottom planes to avoid
// some messy patch clipping issues
for ( i = 4 ; i <= 5 ; i++ ) {
s = &b->sides[ i ];
plane = &mapplanes[ s->planenum ];
SplitMeshByPlane( m, plane->normal, plane->dist, &front, &back );
if ( !back ) {
// nothing actually contained inside
for ( j = 0 ; j < numOutside ; j++ ) {
FreeMesh( outside[j] );
}
return qfalse;
}
m = back;
if ( front ) {
if ( numOutside == MAX_BRUSH_SIDES ) {
Error( "MAX_BRUSH_SIDES" );
}
outside[ numOutside ] = front;
numOutside++;
}
}
// all of outside fragments become seperate drawsurfs
c_fogPatchFragments += numOutside;
for ( i = 0 ; i < numOutside ; i++ ) {
newds = DrawSurfaceForMesh( outside[ i ] );
newds->shaderInfo = ds->shaderInfo;
FreeMesh( outside[ i ] );
}
// replace ds with m
ds->patchWidth = m->width;
ds->patchHeight = m->height;
ds->numVerts = m->width * m->height;
free( ds->verts );
ds->verts = malloc( ds->numVerts * sizeof( *ds->verts ) );
memcpy( ds->verts, m->verts, ds->numVerts * sizeof( *ds->verts ) );
FreeMesh( m );
return qtrue;
}
//===============================================================================
/*
====================
WindingFromDrawSurf
====================
*/
winding_t *WindingFromDrawSurf( mapDrawSurface_t *ds ) {
winding_t *w;
int i;
w = AllocWinding( ds->numVerts );
w->numpoints = ds->numVerts;
for ( i = 0 ; i < ds->numVerts ; i++ ) {
VectorCopy( ds->verts[i].xyz, w->p[i] );
}
return w;
}
/*
====================
ChopFaceByBrush
There may be a fragment contained in the brush
====================
*/
qboolean ChopFaceByBrush( mapDrawSurface_t *ds, bspbrush_t *b ) {
int i, j;
side_t *s;
plane_t *plane;
winding_t *w;
winding_t *front, *back;
winding_t *outside[MAX_BRUSH_SIDES];
int numOutside;
mapDrawSurface_t *newds;
drawVert_t *dv;
shaderInfo_t *si;
float mins[2];
// brush primitive :
// axis base
vec3_t texX,texY;
vec_t x,y;
w = WindingFromDrawSurf( ds );
numOutside = 0;
for ( i = 0 ; i < b->numsides ; i++ ) {
s = &b->sides[ i ];
if ( s->backSide ) {
continue;
}
plane = &mapplanes[ s->planenum ];
// handle coplanar outfacing (don't fog)
if ( ds->side->planenum == s->planenum ) {
return qfalse;
}
// handle coplanar infacing (keep inside)
if ( ( ds->side->planenum ^ 1 ) == s->planenum ) {
continue;
}
// general case
ClipWindingEpsilon( w, plane->normal, plane->dist, ON_EPSILON,
&front, &back );
FreeWinding( w );
if ( !back ) {
// nothing actually contained inside
for ( j = 0 ; j < numOutside ; j++ ) {
FreeWinding( outside[j] );
}
return qfalse;
}
if ( front ) {
if ( numOutside == MAX_BRUSH_SIDES ) {
Error( "MAX_BRUSH_SIDES" );
}
outside[ numOutside ] = front;
numOutside++;
}
w = back;
}
// all of outside fragments become seperate drawsurfs
// linked to the same side
c_fogFragment += numOutside;
s = ds->side;
for ( i = 0 ; i < numOutside ; i++ ) {
newds = DrawSurfaceForSide( ds->mapBrush, s, outside[i] );
FreeWinding( outside[i] );
}
// replace ds->verts with the verts for w
ds->numVerts = w->numpoints;
free( ds->verts );
ds->verts = malloc( ds->numVerts * sizeof( *ds->verts ) );
memset( ds->verts, 0, ds->numVerts * sizeof( *ds->verts ) );
si = s->shaderInfo;
mins[0] = 9999;
mins[1] = 9999;
// compute s/t coordinates from brush primitive texture matrix
// compute axis base
ComputeAxisBase( mapplanes[s->planenum].normal, texX, texY );
for ( j = 0 ; j < w->numpoints ; j++ ) {
dv = ds->verts + j;
VectorCopy( w->p[j], dv->xyz );
if (g_bBrushPrimit==BPRIMIT_OLDBRUSHES)
{
// calculate texture s/t
dv->st[0] = s->vecs[0][3] + DotProduct( s->vecs[0], dv->xyz );
dv->st[1] = s->vecs[1][3] + DotProduct( s->vecs[1], dv->xyz );
dv->st[0] /= si->width;
dv->st[1] /= si->height;
}
else
{
// calculate texture s/t from brush primitive texture matrix
x = DotProduct( dv->xyz, texX );
y = DotProduct( dv->xyz, texY );
dv->st[0]=s->texMat[0][0]*x+s->texMat[0][1]*y+s->texMat[0][2];
dv->st[1]=s->texMat[1][0]*x+s->texMat[1][1]*y+s->texMat[1][2];
}
if ( dv->st[0] < mins[0] ) {
mins[0] = dv->st[0];
}
if ( dv->st[1] < mins[1] ) {
mins[1] = dv->st[1];
}
// copy normal
VectorCopy ( mapplanes[s->planenum].normal, dv->normal );
}
// adjust the texture coordinates to be as close to 0 as possible
if ( !si->globalTexture ) {
mins[0] = floor( mins[0] );
mins[1] = floor( mins[1] );
for ( i = 0 ; i < w->numpoints ; i++ ) {
dv = ds->verts + i;
dv->st[0] -= mins[0];
dv->st[1] -= mins[1];
}
}
return qtrue;
}
//===============================================================================
/*
=====================
FogDrawSurfs
Call after the surface list has been pruned,
before tjunction fixing
before lightmap allocation
=====================
*/
void FogDrawSurfs( void ) {
int i, j, k;
mapDrawSurface_t *ds;
bspbrush_t *b;
vec3_t mins, maxs;
int c_fogged;
int numBaseDrawSurfs;
dfog_t *fog;
qprintf("----- FogDrawsurfs -----\n");
c_fogged = 0;
c_fogFragment = 0;
// find all fog brushes
for ( b = entities[0].brushes ; b ; b = b->next ) {
if ( !(b->contents & CONTENTS_FOG) ) {
continue;
}
if ( numFogs == MAX_MAP_FOGS ) {
Error( "MAX_MAP_FOGS" );
}
fog = &dfogs[numFogs];
numFogs++;
fog->brushNum = b->outputNumber;
// find a side with a valid shaderInfo
// non-axial fog columns may have bevel planes that need to be skipped
for ( i = 0 ; i < b->numsides ; i++ ) {
if ( b->sides[i].shaderInfo && (b->sides[i].shaderInfo->contents & CONTENTS_FOG) ) {
strcpy( fog->shader, b->sides[i].shaderInfo->shader );
break;
}
}
if ( i == b->numsides ) {
continue; // shouldn't happen
}
fog->visibleSide = -1;
// clip each surface into this, but don't clip any of
// the resulting fragments to the same brush
numBaseDrawSurfs = numMapDrawSurfs;
for ( i = 0 ; i < numBaseDrawSurfs ; i++ ) {
ds = &mapDrawSurfs[i];
// bound the drawsurf
ClearBounds( mins, maxs );
for ( j = 0 ; j < ds->numVerts ; j++ ) {
AddPointToBounds( ds->verts[j].xyz, mins, maxs );
}
// check against the fog brush
for ( k = 0 ; k < 3 ; k++ ) {
if ( mins[k] > b->maxs[k] ) {
break;
}
if ( maxs[k] < b->mins[k] ) {
break;
}
}
if ( k < 3 ) {
continue; // bboxes don't intersect
}
if ( ds->mapBrush == b ) {
int s;
s = ds->side - b->sides;
if ( s <= 6 ) { // not one of the reversed inside faces
// this is a visible fog plane
if ( fog->visibleSide != -1 ) {
_printf( "WARNING: fog brush %i has multiple visible sides\n", b->brushnum );
}
fog->visibleSide = s;
}
}
if ( ds->miscModel ) {
// we could write splitting code for trimodels if we wanted to...
c_fogged++;
ds->fogNum = numFogs - 1;
} else if ( ds->patch ) {
if ( ChopPatchByBrush( ds, b ) ) {
c_fogged++;
ds->fogNum = numFogs - 1;
}
} else {
if ( ChopFaceByBrush( ds, b ) ) {
c_fogged++;
ds->fogNum = numFogs - 1;
}
}
}
}
// split the drawsurfs by the fog brushes
qprintf( "%5i fogs\n", numFogs );
qprintf( "%5i fog polygon fragments\n", c_fogFragment );
qprintf( "%5i fog patch fragments\n", c_fogPatchFragments );
qprintf( "%5i fogged drawsurfs\n", c_fogged );
}

232
q3map/gldraw.c Normal file
View file

@ -0,0 +1,232 @@
/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
This file is part of Quake III Arena source code.
Quake III Arena source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
Quake III Arena source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Foobar; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
#include <windows.h>
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glaux.h>
#include "qbsp.h"
// can't use the glvertex3fv functions, because the vec3_t fields
// could be either floats or doubles, depending on DOUBLEVEC_T
qboolean drawflag;
vec3_t draw_mins, draw_maxs;
#define WIN_SIZE 512
void InitWindow (void)
{
auxInitDisplayMode (AUX_SINGLE | AUX_RGB);
auxInitPosition (0, 0, WIN_SIZE, WIN_SIZE);
auxInitWindow ("qcsg");
}
void Draw_ClearWindow (void)
{
static int init;
int w, h, g;
vec_t mx, my;
if (!drawflag)
return;
if (!init)
{
init = qtrue;
InitWindow ();
}
glClearColor (1,0.8,0.8,0);
glClear (GL_COLOR_BUFFER_BIT);
w = (draw_maxs[0] - draw_mins[0]);
h = (draw_maxs[1] - draw_mins[1]);
mx = draw_mins[0] + w/2;
my = draw_mins[1] + h/2;
g = w > h ? w : h;
glLoadIdentity ();
gluPerspective (90, 1, 2, 16384);
gluLookAt (mx, my, draw_maxs[2] + g/2, mx , my, draw_maxs[2], 0, 1, 0);
glColor3f (0,0,0);
// glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
glDisable (GL_DEPTH_TEST);
glEnable (GL_BLEND);
glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
#if 0
glColor4f (1,0,0,0.5);
glBegin (GL_POLYGON);
glVertex3f (0, 500, 0);
glVertex3f (0, 900, 0);
glVertex3f (0, 900, 100);
glVertex3f (0, 500, 100);
glEnd ();
#endif
glFlush ();
}
void Draw_SetRed (void)
{
if (!drawflag)
return;
glColor3f (1,0,0);
}
void Draw_SetGrey (void)
{
if (!drawflag)
return;
glColor3f (0.5,0.5,0.5);
}
void Draw_SetBlack (void)
{
if (!drawflag)
return;
glColor3f (0,0,0);
}
void DrawWinding (winding_t *w)
{
int i;
if (!drawflag)
return;
glColor4f (0,0,0,0.5);
glBegin (GL_LINE_LOOP);
for (i=0 ; i<w->numpoints ; i++)
glVertex3f (w->p[i][0],w->p[i][1],w->p[i][2] );
glEnd ();
glColor4f (0,1,0,0.3);
glBegin (GL_POLYGON);
for (i=0 ; i<w->numpoints ; i++)
glVertex3f (w->p[i][0],w->p[i][1],w->p[i][2] );
glEnd ();
glFlush ();
}
void DrawAuxWinding (winding_t *w)
{
int i;
if (!drawflag)
return;
glColor4f (0,0,0,0.5);
glBegin (GL_LINE_LOOP);
for (i=0 ; i<w->numpoints ; i++)
glVertex3f (w->p[i][0],w->p[i][1],w->p[i][2] );
glEnd ();
glColor4f (1,0,0,0.3);
glBegin (GL_POLYGON);
for (i=0 ; i<w->numpoints ; i++)
glVertex3f (w->p[i][0],w->p[i][1],w->p[i][2] );
glEnd ();
glFlush ();
}
//============================================================
#define GLSERV_PORT 25001
qboolean wins_init;
int draw_socket;
void GLS_BeginScene (void)
{
WSADATA winsockdata;
WORD wVersionRequested;
struct sockaddr_in address;
int r;
if (!wins_init)
{
wins_init = qtrue;
wVersionRequested = MAKEWORD(1, 1);
r = WSAStartup (MAKEWORD(1, 1), &winsockdata);
if (r)
Error ("Winsock initialization failed.");
}
// connect a socket to the server
draw_socket = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP);
if (draw_socket == -1)
Error ("draw_socket failed");
address.sin_family = AF_INET;
address.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
address.sin_port = GLSERV_PORT;
r = connect (draw_socket, (struct sockaddr *)&address, sizeof(address));
if (r == -1)
{
closesocket (draw_socket);
draw_socket = 0;
}
}
void GLS_Winding (winding_t *w, int code)
{
byte buf[1024];
int i, j;
if (!draw_socket)
return;
((int *)buf)[0] = w->numpoints;
((int *)buf)[1] = code;
for (i=0 ; i<w->numpoints ; i++)
for (j=0 ; j<3 ; j++)
((float *)buf)[2+i*3+j] = w->p[i][j];
send (draw_socket, buf, w->numpoints*12+8, 0);
}
void GLS_EndScene (void)
{
closesocket (draw_socket);
draw_socket = 0;
}

148
q3map/glfile.c Normal file
View file

@ -0,0 +1,148 @@
/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
This file is part of Quake III Arena source code.
Quake III Arena source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
Quake III Arena source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Foobar; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
#include "qbsp.h"
int c_glfaces;
int PortalVisibleSides (portal_t *p)
{
int fcon, bcon;
if (!p->onnode)
return 0; // outside
fcon = p->nodes[0]->opaque;
bcon = p->nodes[1]->opaque;
// same contents never create a face
if (fcon == bcon)
return 0;
if (!fcon)
return 1;
if (!bcon)
return 2;
return 0;
}
void OutputWinding (winding_t *w, FILE *glview)
{
static int level = 128;
vec_t light;
int i;
fprintf (glview, "%i\n", w->numpoints);
level+=28;
light = (level&255)/255.0;
for (i=0 ; i<w->numpoints ; i++)
{
fprintf (glview, "%6.3f %6.3f %6.3f %6.3f %6.3f %6.3f\n",
w->p[i][0],
w->p[i][1],
w->p[i][2],
light,
light,
light);
}
fprintf (glview, "\n");
}
/*
=============
OutputPortal
=============
*/
void OutputPortal (portal_t *p, FILE *glview)
{
winding_t *w;
int sides;
sides = PortalVisibleSides (p);
if (!sides)
return;
c_glfaces++;
w = p->winding;
if (sides == 2) // back side
w = ReverseWinding (w);
OutputWinding (w, glview);
if (sides == 2)
FreeWinding(w);
}
/*
=============
WriteGLView_r
=============
*/
void WriteGLView_r (node_t *node, FILE *glview)
{
portal_t *p, *nextp;
if (node->planenum != PLANENUM_LEAF)
{
WriteGLView_r (node->children[0], glview);
WriteGLView_r (node->children[1], glview);
return;
}
// write all the portals
for (p=node->portals ; p ; p=nextp)
{
if (p->nodes[0] == node)
{
OutputPortal (p, glview);
nextp = p->next[0];
}
else
nextp = p->next[1];
}
}
/*
=============
WriteGLView
=============
*/
void WriteGLView (tree_t *tree, char *source)
{
char name[1024];
FILE *glview;
c_glfaces = 0;
sprintf (name, "%s%s.gl",outbase, source);
_printf ("Writing %s\n", name);
glview = fopen (name, "w");
if (!glview)
Error ("Couldn't open %s", name);
WriteGLView_r (tree->headnode, glview);
fclose (glview);
_printf ("%5i c_glfaces\n", c_glfaces);
}

100
q3map/leakfile.c Normal file
View file

@ -0,0 +1,100 @@
/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
This file is part of Quake III Arena source code.
Quake III Arena source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
Quake III Arena source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Foobar; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
#include "qbsp.h"
/*
==============================================================================
LEAF FILE GENERATION
Save out name.line for qe3 to read
==============================================================================
*/
/*
=============
LeakFile
Finds the shortest possible chain of portals
that leads from the outside leaf to a specifically
occupied leaf
=============
*/
void LeakFile (tree_t *tree)
{
vec3_t mid;
FILE *linefile;
char filename[1024];
node_t *node;
int count;
if (!tree->outside_node.occupied)
return;
qprintf ("--- LeakFile ---\n");
//
// write the points to the file
//
sprintf (filename, "%s.lin", source);
linefile = fopen (filename, "w");
if (!linefile)
Error ("Couldn't open %s\n", filename);
count = 0;
node = &tree->outside_node;
while (node->occupied > 1)
{
int next;
portal_t *p, *nextportal;
node_t *nextnode;
int s;
// find the best portal exit
next = node->occupied;
for (p=node->portals ; p ; p = p->next[!s])
{
s = (p->nodes[0] == node);
if (p->nodes[s]->occupied
&& p->nodes[s]->occupied < next)
{
nextportal = p;
nextnode = p->nodes[s];
next = nextnode->occupied;
}
}
node = nextnode;
WindingCenter (nextportal->winding, mid);
fprintf (linefile, "%f %f %f\n", mid[0], mid[1], mid[2]);
count++;
}
// add the occupant center
GetVectorForKey (node->occupant, "origin", mid);
fprintf (linefile, "%f %f %f\n", mid[0], mid[1], mid[2]);
qprintf ("%5i point linefile\n", count+1);
fclose (linefile);
}

2149
q3map/light.c Normal file

File diff suppressed because it is too large Load diff

151
q3map/light.h Normal file
View file

@ -0,0 +1,151 @@
/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
This file is part of Quake III Arena source code.
Quake III Arena source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
Quake III Arena source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Foobar; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
#include "cmdlib.h"
#include "mathlib.h"
#include "bspfile.h"
#include "polylib.h"
#include "imagelib.h"
#include "threads.h"
#include "scriplib.h"
#include "shaders.h"
#include "mesh.h"
typedef enum
{
emit_point,
emit_area,
emit_spotlight,
emit_sun
} emittype_t;
#define MAX_LIGHT_EDGES 8
typedef struct light_s
{
struct light_s *next;
emittype_t type;
struct shaderInfo_s *si;
vec3_t origin;
vec3_t normal; // for surfaces, spotlights, and suns
float dist; // plane location along normal
qboolean linearLight;
int photons;
int style;
vec3_t color;
float radiusByDist; // for spotlights
qboolean twosided; // fog lights both sides
winding_t *w;
vec3_t emitColor; // full out-of-gamut value
} light_t;
extern float lightscale;
extern float ambient;
extern float maxlight;
extern float direct_scale;
extern float entity_scale;
extern qboolean noSurfaces;
//===============================================================
// light_trace.c
// a facet is a subdivided element of a patch aproximation or model
typedef struct cFacet_s {
float surface[4];
int numBoundaries; // either 3 or 4, anything less is degenerate
float boundaries[4][4]; // positive is outside the bounds
vec3_t points[4]; // needed for area light subdivision
float textureMatrix[2][4]; // compute texture coordinates at point of impact for translucency
} cFacet_t;
typedef struct {
vec3_t mins, maxs;
vec3_t origin;
float radius;
qboolean patch;
int numFacets;
cFacet_t *facets;
shaderInfo_t *shader; // for translucency
} surfaceTest_t;
typedef struct {
vec3_t filter; // starts out 1.0, 1.0, 1.0, may be reduced if
// transparent surfaces are crossed
vec3_t hit; // the impact point of a completely opaque surface
float hitFraction; // 0 = at start, 1.0 = at end
qboolean passSolid;
} trace_t;
extern surfaceTest_t *surfaceTest[MAX_MAP_DRAW_SURFS];
void InitTrace( void );
// traceWork_t is only a parameter to crutch up poor large local allocations on
// winNT and macOS. It should be allocated in the worker function, but never
// looked at.
typedef struct {
vec3_t start, end;
int numOpenLeafs;
int openLeafNumbers[MAX_MAP_LEAFS];
trace_t *trace;
int patchshadows;
} traceWork_t;
void TraceLine( const vec3_t start, const vec3_t stop, trace_t *trace,
qboolean testAll, traceWork_t *tw );
qboolean PointInSolid( vec3_t start );
//===============================================================
//===============================================================
typedef struct {
int textureNum;
int x, y, width, height;
// for patches
qboolean patch;
mesh_t mesh;
// for faces
vec3_t origin;
vec3_t vecs[3];
} lightmap_t;

944
q3map/light_trace.c Normal file
View file

@ -0,0 +1,944 @@
/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
This file is part of Quake III Arena source code.
Quake III Arena source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
Quake III Arena source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Foobar; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
#include "light.h"
#define CURVE_FACET_ERROR 8
int c_totalTrace;
int c_cullTrace, c_testTrace;
int c_testFacets;
surfaceTest_t *surfaceTest[MAX_MAP_DRAW_SURFS];
/*
=====================
CM_GenerateBoundaryForPoints
=====================
*/
void CM_GenerateBoundaryForPoints( float boundary[4], float plane[4], vec3_t a, vec3_t b ) {
vec3_t d1;
// amke a perpendicular vector to the edge and the surface
VectorSubtract( b, a, d1 );
CrossProduct( plane, d1, boundary );
VectorNormalize( boundary, boundary );
boundary[3] = DotProduct( a, boundary );
}
/*
=====================
TextureMatrixFromPoints
=====================
*/
void TextureMatrixFromPoints( cFacet_t *f, drawVert_t *a, drawVert_t *b, drawVert_t *c ) {
int i, j;
float t;
float m[3][4];
float s;
// This is an incredibly stupid way of solving a three variable equation
for ( i = 0 ; i < 2 ; i++ ) {
m[0][0] = a->xyz[0];
m[0][1] = a->xyz[1];
m[0][2] = a->xyz[2];
m[0][3] = a->st[i];
m[1][0] = b->xyz[0];
m[1][1] = b->xyz[1];
m[1][2] = b->xyz[2];
m[1][3] = b->st[i];
m[2][0] = c->xyz[0];
m[2][1] = c->xyz[1];
m[2][2] = c->xyz[2];
m[2][3] = c->st[i];
if ( fabs(m[1][0]) > fabs(m[0][0]) && fabs(m[1][0]) > fabs(m[2][0]) ) {
for ( j = 0 ; j < 4 ; j ++ ) {
t = m[0][j];
m[0][j] = m[1][j];
m[1][j] = t;
}
} else if ( fabs(m[2][0]) > fabs(m[0][0]) && fabs(m[2][0]) > fabs(m[1][0]) ) {
for ( j = 0 ; j < 4 ; j ++ ) {
t = m[0][j];
m[0][j] = m[2][j];
m[2][j] = t;
}
}
s = 1.0 / m[0][0];
m[0][0] *= s;
m[0][1] *= s;
m[0][2] *= s;
m[0][3] *= s;
s = m[1][0];
m[1][0] -= m[0][0] * s;
m[1][1] -= m[0][1] * s;
m[1][2] -= m[0][2] * s;
m[1][3] -= m[0][3] * s;
s = m[2][0];
m[2][0] -= m[0][0] * s;
m[2][1] -= m[0][1] * s;
m[2][2] -= m[0][2] * s;
m[2][3] -= m[0][3] * s;
if ( fabs(m[2][1]) > fabs(m[1][1]) ) {
for ( j = 0 ; j < 4 ; j ++ ) {
t = m[1][j];
m[1][j] = m[2][j];
m[2][j] = t;
}
}
s = 1.0 / m[1][1];
m[1][0] *= s;
m[1][1] *= s;
m[1][2] *= s;
m[1][3] *= s;
s = m[2][1];
m[2][0] -= m[1][0] * s;
m[2][1] -= m[1][1] * s;
m[2][2] -= m[1][2] * s;
m[2][3] -= m[1][3] * s;
s = 1.0 / m[2][2];
m[2][0] *= s;
m[2][1] *= s;
m[2][2] *= s;
m[2][3] *= s;
f->textureMatrix[i][2] = m[2][3];
f->textureMatrix[i][1] = m[1][3] - f->textureMatrix[i][2] * m[1][2];
f->textureMatrix[i][0] = m[0][3] - f->textureMatrix[i][2] * m[0][2] - f->textureMatrix[i][1] * m[0][1];
f->textureMatrix[i][3] = 0;
/*
s = fabs( DotProduct( a->xyz, f->textureMatrix[i] ) - a->st[i] );
if ( s > 0.01 ) {
Error( "Bad textureMatrix" );
}
s = fabs( DotProduct( b->xyz, f->textureMatrix[i] ) - b->st[i] );
if ( s > 0.01 ) {
Error( "Bad textureMatrix" );
}
s = fabs( DotProduct( c->xyz, f->textureMatrix[i] ) - c->st[i] );
if ( s > 0.01 ) {
Error( "Bad textureMatrix" );
}
*/
}
}
/*
=====================
CM_GenerateFacetFor3Points
=====================
*/
qboolean CM_GenerateFacetFor3Points( cFacet_t *f, drawVert_t *a, drawVert_t *b, drawVert_t *c ) {
// if we can't generate a valid plane for the points, ignore the facet
if ( !PlaneFromPoints( f->surface, a->xyz, b->xyz, c->xyz ) ) {
f->numBoundaries = 0;
return qfalse;
}
// make boundaries
f->numBoundaries = 3;
CM_GenerateBoundaryForPoints( f->boundaries[0], f->surface, a->xyz, b->xyz );
CM_GenerateBoundaryForPoints( f->boundaries[1], f->surface, b->xyz, c->xyz );
CM_GenerateBoundaryForPoints( f->boundaries[2], f->surface, c->xyz, a->xyz );
VectorCopy( a->xyz, f->points[0] );
VectorCopy( b->xyz, f->points[1] );
VectorCopy( c->xyz, f->points[2] );
TextureMatrixFromPoints( f, a, b, c );
return qtrue;
}
/*
=====================
CM_GenerateFacetFor4Points
Attempts to use four points as a planar quad
=====================
*/
#define PLANAR_EPSILON 0.1
qboolean CM_GenerateFacetFor4Points( cFacet_t *f, drawVert_t *a, drawVert_t *b, drawVert_t *c, drawVert_t *d ) {
float dist;
int i;
vec4_t plane;
// if we can't generate a valid plane for the points, ignore the facet
if ( !PlaneFromPoints( f->surface, a->xyz, b->xyz, c->xyz ) ) {
f->numBoundaries = 0;
return qfalse;
}
// if the fourth point is also on the plane, we can make a quad facet
dist = DotProduct( d->xyz, f->surface ) - f->surface[3];
if ( fabs( dist ) > PLANAR_EPSILON ) {
f->numBoundaries = 0;
return qfalse;
}
// make boundaries
f->numBoundaries = 4;
CM_GenerateBoundaryForPoints( f->boundaries[0], f->surface, a->xyz, b->xyz );
CM_GenerateBoundaryForPoints( f->boundaries[1], f->surface, b->xyz, c->xyz );
CM_GenerateBoundaryForPoints( f->boundaries[2], f->surface, c->xyz, d->xyz );
CM_GenerateBoundaryForPoints( f->boundaries[3], f->surface, d->xyz, a->xyz );
VectorCopy( a->xyz, f->points[0] );
VectorCopy( b->xyz, f->points[1] );
VectorCopy( c->xyz, f->points[2] );
VectorCopy( d->xyz, f->points[3] );
for (i = 1; i < 4; i++)
{
if ( !PlaneFromPoints( plane, f->points[i], f->points[(i+1) % 4], f->points[(i+2) % 4]) ) {
f->numBoundaries = 0;
return qfalse;
}
if (DotProduct(f->surface, plane) < 0.9) {
f->numBoundaries = 0;
return qfalse;
}
}
TextureMatrixFromPoints( f, a, b, c );
return qtrue;
}
/*
===============
SphereFromBounds
===============
*/
void SphereFromBounds( vec3_t mins, vec3_t maxs, vec3_t origin, float *radius ) {
vec3_t temp;
VectorAdd( mins, maxs, origin );
VectorScale( origin, 0.5, origin );
VectorSubtract( maxs, origin, temp );
*radius = VectorLength( temp );
}
/*
====================
FacetsForTriangleSurface
====================
*/
void FacetsForTriangleSurface( dsurface_t *dsurf, shaderInfo_t *si, surfaceTest_t *test ) {
int i;
drawVert_t *v1, *v2, *v3, *v4;
int count;
int i1, i2, i3, i4, i5, i6;
test->patch = qfalse;
test->numFacets = dsurf->numIndexes / 3;
test->facets = malloc( sizeof( test->facets[0] ) * test->numFacets );
test->shader = si;
count = 0;
for ( i = 0 ; i < test->numFacets ; i++ ) {
i1 = drawIndexes[ dsurf->firstIndex + i*3 ];
i2 = drawIndexes[ dsurf->firstIndex + i*3 + 1 ];
i3 = drawIndexes[ dsurf->firstIndex + i*3 + 2 ];
v1 = &drawVerts[ dsurf->firstVert + i1 ];
v2 = &drawVerts[ dsurf->firstVert + i2 ];
v3 = &drawVerts[ dsurf->firstVert + i3 ];
// try and make a quad out of two triangles
if ( i != test->numFacets - 1 ) {
i4 = drawIndexes[ dsurf->firstIndex + i*3 + 3 ];
i5 = drawIndexes[ dsurf->firstIndex + i*3 + 4 ];
i6 = drawIndexes[ dsurf->firstIndex + i*3 + 5 ];
if ( i4 == i3 && i5 == i2 ) {
v4 = &drawVerts[ dsurf->firstVert + i6 ];
if ( CM_GenerateFacetFor4Points( &test->facets[count], v1, v2, v4, v3 ) ) {
count++;
i++; // skip next tri
continue;
}
}
}
if (CM_GenerateFacetFor3Points( &test->facets[count], v1, v2, v3 ))
count++;
}
// we may have turned some pairs into quads
test->numFacets = count;
}
/*
====================
FacetsForPatch
====================
*/
void FacetsForPatch( dsurface_t *dsurf, shaderInfo_t *si, surfaceTest_t *test ) {
int i, j;
drawVert_t *v1, *v2, *v3, *v4;
int count;
mesh_t srcMesh, *subdivided, *mesh;
srcMesh.width = dsurf->patchWidth;
srcMesh.height = dsurf->patchHeight;
srcMesh.verts = &drawVerts[ dsurf->firstVert ];
//subdivided = SubdivideMesh( mesh, CURVE_FACET_ERROR, 9999 );
mesh = SubdivideMesh( srcMesh, 8, 999 );
PutMeshOnCurve( *mesh );
MakeMeshNormals( *mesh );
subdivided = RemoveLinearMeshColumnsRows( mesh );
FreeMesh(mesh);
test->patch = qtrue;
test->numFacets = ( subdivided->width - 1 ) * ( subdivided->height - 1 ) * 2;
test->facets = malloc( sizeof( test->facets[0] ) * test->numFacets );
test->shader = si;
count = 0;
for ( i = 0 ; i < subdivided->width - 1 ; i++ ) {
for ( j = 0 ; j < subdivided->height - 1 ; j++ ) {
v1 = subdivided->verts + j * subdivided->width + i;
v2 = v1 + 1;
v3 = v1 + subdivided->width + 1;
v4 = v1 + subdivided->width;
if ( CM_GenerateFacetFor4Points( &test->facets[count], v1, v4, v3, v2 ) ) {
count++;
} else {
if (CM_GenerateFacetFor3Points( &test->facets[count], v1, v4, v3 ))
count++;
if (CM_GenerateFacetFor3Points( &test->facets[count], v1, v3, v2 ))
count++;
}
}
}
test->numFacets = count;
FreeMesh(subdivided);
}
/*
=====================
InitSurfacesForTesting
Builds structures to speed the ray tracing against surfaces
=====================
*/
void InitSurfacesForTesting( void ) {
int i, j;
dsurface_t *dsurf;
surfaceTest_t *test;
drawVert_t *dvert;
shaderInfo_t *si;
for ( i = 0 ; i < numDrawSurfaces ; i++ ) {
dsurf = &drawSurfaces[ i ];
if ( !dsurf->numIndexes && !dsurf->patchWidth ) {
continue;
}
// don't make surfaces for transparent objects
// because we want light to pass through them
si = ShaderInfoForShader( dshaders[ dsurf->shaderNum].shader );
if ( (si->contents & CONTENTS_TRANSLUCENT) && !(si->surfaceFlags & SURF_ALPHASHADOW) ) {
continue;
}
test = malloc( sizeof( *test ) );
surfaceTest[i] = test;
ClearBounds( test->mins, test->maxs );
dvert = &drawVerts[ dsurf->firstVert ];
for ( j = 0 ; j < dsurf->numVerts ; j++, dvert++ ) {
AddPointToBounds( dvert->xyz, test->mins, test->maxs );
}
SphereFromBounds( test->mins, test->maxs, test->origin, &test->radius );
if ( dsurf->surfaceType == MST_TRIANGLE_SOUP || dsurf->surfaceType == MST_PLANAR ) {
FacetsForTriangleSurface( dsurf, si, test );
} else if ( dsurf->surfaceType == MST_PATCH ) {
FacetsForPatch( dsurf, si, test );
}
}
}
/*
=====================
GenerateBoundaryForPoints
=====================
*/
void GenerateBoundaryForPoints( float boundary[4], float plane[4], vec3_t a, vec3_t b ) {
vec3_t d1;
// amke a perpendicular vector to the edge and the surface
VectorSubtract( b, a, d1 );
CrossProduct( plane, d1, boundary );
VectorNormalize( boundary, boundary );
boundary[3] = DotProduct( a, boundary );
}
/*
=================
SetFacetFilter
Given a point on a facet, determine the color filter
for light passing through
=================
*/
void SetFacetFilter( traceWork_t *tr, shaderInfo_t *shader, cFacet_t *facet, vec3_t point ) {
float s, t;
int is, it;
byte *image;
int b;
// most surfaces are completely opaque
if ( !(shader->surfaceFlags & SURF_ALPHASHADOW) ) {
VectorClear( tr->trace->filter );
return;
}
s = DotProduct( point, facet->textureMatrix[0] ) + facet->textureMatrix[0][3];
t = DotProduct( point, facet->textureMatrix[1] ) + facet->textureMatrix[1][3];
if ( !shader->pixels ) {
// assume completely solid
VectorClear( point );
return;
}
s = s - floor( s );
t = t - floor( t );
is = s * shader->width;
it = t * shader->height;
image = shader->pixels + 4 * ( it * shader->width + is );
// alpha filter
b = image[3];
// alpha test makes this a binary option
b = b < 128 ? 0 : 255;
tr->trace->filter[0] = tr->trace->filter[0] * (255-b) / 255;
tr->trace->filter[1] = tr->trace->filter[1] * (255-b) / 255;
tr->trace->filter[2] = tr->trace->filter[2] * (255-b) / 255;
}
/*
====================
TraceAgainstFacet
Shader is needed for translucent surfaces
====================
*/
void TraceAgainstFacet( traceWork_t *tr, shaderInfo_t *shader, cFacet_t *facet ) {
int j;
float d1, d2, d, f;
vec3_t point;
float dist;
// ignore degenerate facets
if ( facet->numBoundaries < 3 ) {
return;
}
dist = facet->surface[3];
// compare the trace endpoints against the facet plane
d1 = DotProduct( tr->start, facet->surface ) - dist;
if ( d1 > -1 && d1 < 1 ) {
return; // don't self intersect
}
d2 = DotProduct( tr->end, facet->surface ) - dist;
if ( d2 > -1 && d2 < 1 ) {
return; // don't self intersect
}
// calculate the intersection fraction
f = ( d1 - ON_EPSILON ) / ( d1 - d2 );
if ( f <= 0 ) {
return;
}
if ( f >= tr->trace->hitFraction ) {
return; // we have hit something earlier
}
// calculate the intersection point
for ( j = 0 ; j < 3 ; j++ ) {
point[j] = tr->start[j] + f * ( tr->end[j] - tr->start[j] );
}
// check the point against the facet boundaries
for ( j = 0 ; j < facet->numBoundaries ; j++ ) {
// adjust the plane distance apropriately for mins/maxs
dist = facet->boundaries[j][3];
d = DotProduct( point, facet->boundaries[j] );
if ( d > dist + ON_EPSILON ) {
break; // outside the bounds
}
}
if ( j != facet->numBoundaries ) {
return; // we are outside the bounds of the facet
}
// we hit this facet
// if this is a transparent surface, calculate filter value
if ( shader->surfaceFlags & SURF_ALPHASHADOW ) {
SetFacetFilter( tr, shader, facet, point );
} else {
// completely opaque
VectorClear( tr->trace->filter );
tr->trace->hitFraction = f;
}
// VectorCopy( facet->surface, tr->trace->plane.normal );
// tr->trace->plane.dist = facet->surface[3];
}
/*
===============================================================
LINE TRACING
===============================================================
*/
#define TRACE_ON_EPSILON 0.1
typedef struct tnode_s
{
int type;
vec3_t normal;
float dist;
int children[2];
int planeNum;
} tnode_t;
#define MAX_TNODES (MAX_MAP_NODES*4)
tnode_t *tnodes, *tnode_p;
/*
==============
MakeTnode
Converts the disk node structure into the efficient tracing structure
==============
*/
void MakeTnode (int nodenum)
{
tnode_t *t;
dplane_t *plane;
int i;
dnode_t *node;
int leafNum;
t = tnode_p++;
node = dnodes + nodenum;
plane = dplanes + node->planeNum;
t->planeNum = node->planeNum;
t->type = PlaneTypeForNormal( plane->normal );
VectorCopy (plane->normal, t->normal);
t->dist = plane->dist;
for (i=0 ; i<2 ; i++)
{
if (node->children[i] < 0) {
leafNum = -node->children[i] - 1;
if ( dleafs[leafNum].cluster == -1 ) {
// solid
t->children[i] = leafNum | ( 1 << 31 ) | ( 1 << 30 );
} else {
t->children[i] = leafNum | ( 1 << 31 );
}
} else {
t->children[i] = tnode_p - tnodes;
MakeTnode (node->children[i]);
}
}
}
/*
=============
InitTrace
Loads the node structure out of a .bsp file to be used for light occlusion
=============
*/
void InitTrace( void ) {
// 32 byte align the structs
tnodes = malloc( (MAX_TNODES+1) * sizeof(tnode_t));
tnodes = (tnode_t *)(((int)tnodes + 31)&~31);
tnode_p = tnodes;
MakeTnode (0);
InitSurfacesForTesting();
}
/*
===================
PointInSolid
===================
*/
qboolean PointInSolid_r( vec3_t start, int node ) {
tnode_t *tnode;
float front;
while ( !(node & (1<<31) ) ) {
tnode = &tnodes[node];
switch (tnode->type) {
case PLANE_X:
front = start[0] - tnode->dist;
break;
case PLANE_Y:
front = start[1] - tnode->dist;
break;
case PLANE_Z:
front = start[2] - tnode->dist;
break;
default:
front = (start[0]*tnode->normal[0] + start[1]*tnode->normal[1] + start[2]*tnode->normal[2]) - tnode->dist;
break;
}
if ( front == 0 ) {
// exactly on node, must check both sides
return (qboolean) ( PointInSolid_r( start, tnode->children[0] )
| PointInSolid_r( start, tnode->children[1] ) );
}
if ( front > 0 ) {
node = tnode->children[0];
} else {
node = tnode->children[1];
}
}
if ( node & ( 1 << 30 ) ) {
return qtrue;
}
return qfalse;
}
/*
=============
PointInSolid
=============
*/
qboolean PointInSolid( vec3_t start ) {
return PointInSolid_r( start, 0 );
}
/*
=============
TraceLine_r
Returns qtrue if something is hit and tracing can stop
=============
*/
int TraceLine_r( int node, const vec3_t start, const vec3_t stop, traceWork_t *tw ) {
tnode_t *tnode;
float front, back;
vec3_t mid;
float frac;
int side;
int r;
if (node & (1<<31)) {
if (node & ( 1 << 30 ) ) {
VectorCopy (start, tw->trace->hit);
tw->trace->passSolid = qtrue;
return qtrue;
} else {
// save the node off for more exact testing
if ( tw->numOpenLeafs == MAX_MAP_LEAFS ) {
return qfalse;
}
tw->openLeafNumbers[ tw->numOpenLeafs ] = node & ~(3 << 30);
tw->numOpenLeafs++;
return qfalse;
}
}
tnode = &tnodes[node];
switch (tnode->type) {
case PLANE_X:
front = start[0] - tnode->dist;
back = stop[0] - tnode->dist;
break;
case PLANE_Y:
front = start[1] - tnode->dist;
back = stop[1] - tnode->dist;
break;
case PLANE_Z:
front = start[2] - tnode->dist;
back = stop[2] - tnode->dist;
break;
default:
front = (start[0]*tnode->normal[0] + start[1]*tnode->normal[1] + start[2]*tnode->normal[2]) - tnode->dist;
back = (stop[0]*tnode->normal[0] + stop[1]*tnode->normal[1] + stop[2]*tnode->normal[2]) - tnode->dist;
break;
}
if (front >= -TRACE_ON_EPSILON && back >= -TRACE_ON_EPSILON) {
return TraceLine_r (tnode->children[0], start, stop, tw);
}
if (front < TRACE_ON_EPSILON && back < TRACE_ON_EPSILON) {
return TraceLine_r (tnode->children[1], start, stop, tw);
}
side = front < 0;
frac = front / (front-back);
mid[0] = start[0] + (stop[0] - start[0])*frac;
mid[1] = start[1] + (stop[1] - start[1])*frac;
mid[2] = start[2] + (stop[2] - start[2])*frac;
r = TraceLine_r (tnode->children[side], start, mid, tw);
if (r) {
return r;
}
// trace->planeNum = tnode->planeNum;
return TraceLine_r (tnode->children[!side], mid, stop, tw);
}
//==========================================================================================
/*
================
SphereCull
================
*/
qboolean SphereCull( vec3_t start, vec3_t stop, vec3_t origin, float radius ) {
vec3_t v;
float d;
vec3_t dir;
float len;
vec3_t on;
VectorSubtract( stop, start, dir );
len = VectorNormalize( dir, dir );
VectorSubtract( origin, start, v );
d = DotProduct( v, dir );
if ( d > len + radius ) {
return qtrue; // too far ahead
}
if ( d < -radius ) {
return qtrue; // too far behind
}
VectorMA( start, d, dir, on );
VectorSubtract( on, origin, v );
len = VectorLength( v );
if ( len > radius ) {
return qtrue; // too far to the side
}
return qfalse; // must be traced against
}
/*
================
TraceAgainstSurface
================
*/
void TraceAgainstSurface( traceWork_t *tw, surfaceTest_t *surf ) {
int i;
// if surfaces are trans
if ( SphereCull( tw->start, tw->end, surf->origin, surf->radius ) ) {
if ( numthreads == 1 ) {
c_cullTrace++;
}
return;
}
if ( numthreads == 1 ) {
c_testTrace++;
c_testFacets += surf->numFacets;
}
/*
// MrE: backface culling
if (!surf->patch && surf->numFacets) {
// if the surface does not cast an alpha shadow
if ( !(surf->shader->surfaceFlags & SURF_ALPHASHADOW) ) {
vec3_t vec;
VectorSubtract(tw->end, tw->start, vec);
if (DotProduct(vec, surf->facets->surface) > 0)
return;
}
}
*/
// test against each facet
for ( i = 0 ; i < surf->numFacets ; i++ ) {
TraceAgainstFacet( tw, surf->shader, surf->facets + i );
}
}
/*
=============
TraceLine
Follow the trace just through the solid leafs first, and only
if it passes that, trace against the objects inside the empty leafs
Returns qtrue if the trace hit any
traceWork_t is only a parameter to crutch up poor large local allocations on
winNT and macOS. It should be allocated in the worker function, but never
looked at.
leave testAll false if all you care about is if it hit anything at all.
if you need to know the exact first point of impact (for a sun trace), set
testAll to true
=============
*/
extern qboolean patchshadows;
void TraceLine( const vec3_t start, const vec3_t stop, trace_t *trace, qboolean testAll, traceWork_t *tw ) {
int r;
int i, j;
dleaf_t *leaf;
float oldHitFrac;
surfaceTest_t *test;
int surfaceNum;
byte surfaceTested[MAX_MAP_DRAW_SURFS/8];
;
if ( numthreads == 1 ) {
c_totalTrace++;
}
// assume all light gets through, unless the ray crosses
// a translucent surface
trace->filter[0] = 1.0;
trace->filter[1] = 1.0;
trace->filter[2] = 1.0;
VectorCopy( start, tw->start );
VectorCopy( stop, tw->end );
tw->trace = trace;
tw->numOpenLeafs = 0;
trace->passSolid = qfalse;
trace->hitFraction = 1.0;
r = TraceLine_r( 0, start, stop, tw );
// if we hit a solid leaf, stop without testing the leaf
// surfaces. Note that the plane and endpoint might not
// be the first solid intersection along the ray.
if ( r && !testAll ) {
return;
}
if ( noSurfaces ) {
return;
}
memset( surfaceTested, 0, (numDrawSurfaces+7)/8 );
oldHitFrac = trace->hitFraction;
for ( i = 0 ; i < tw->numOpenLeafs ; i++ ) {
leaf = &dleafs[ tw->openLeafNumbers[ i ] ];
for ( j = 0 ; j < leaf->numLeafSurfaces ; j++ ) {
surfaceNum = dleafsurfaces[ leaf->firstLeafSurface + j ];
// make sure we don't test the same ray against a surface more than once
if ( surfaceTested[ surfaceNum>>3 ] & ( 1 << ( surfaceNum & 7) ) ) {
continue;
}
surfaceTested[ surfaceNum>>3 ] |= ( 1 << ( surfaceNum & 7 ) );
test = surfaceTest[ surfaceNum ];
if ( !test ) {
continue;
}
//
if ( !tw->patchshadows && test->patch ) {
continue;
}
TraceAgainstSurface( tw, test );
}
// if the trace is now solid, we can't possibly hit anything closer
if ( trace->hitFraction < oldHitFrac ) {
trace->passSolid = qtrue;
break;
}
}
for ( i = 0 ; i < 3 ; i++ ) {
trace->hit[i] = start[i] + ( stop[i] - start[i] ) * trace->hitFraction;
}
}

395
q3map/lightmaps.c Normal file
View file

@ -0,0 +1,395 @@
/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
This file is part of Quake III Arena source code.
Quake III Arena source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
Quake III Arena source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Foobar; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
#include "qbsp.h"
/*
Lightmap allocation has to be done after all flood filling and
visible surface determination.
*/
int numSortShaders;
mapDrawSurface_t *surfsOnShader[MAX_MAP_SHADERS];
int allocated[LIGHTMAP_WIDTH];
int numLightmaps = 1;
int c_exactLightmap;
void PrepareNewLightmap( void ) {
memset( allocated, 0, sizeof( allocated ) );
numLightmaps++;
}
/*
===============
AllocLMBlock
returns a texture number and the position inside it
===============
*/
qboolean AllocLMBlock (int w, int h, int *x, int *y)
{
int i, j;
int best, best2;
best = LIGHTMAP_HEIGHT;
for ( i=0 ; i <= LIGHTMAP_WIDTH-w ; i++ ) {
best2 = 0;
for (j=0 ; j<w ; j++) {
if (allocated[i+j] >= best) {
break;
}
if (allocated[i+j] > best2) {
best2 = allocated[i+j];
}
}
if (j == w) { // this is a valid spot
*x = i;
*y = best = best2;
}
}
if (best + h > LIGHTMAP_HEIGHT) {
return qfalse;
}
for (i=0 ; i<w ; i++) {
allocated[*x + i] = best + h;
}
return qtrue;
}
/*
===================
AllocateLightmapForPatch
===================
*/
//#define LIGHTMAP_PATCHSHIFT
void AllocateLightmapForPatch( mapDrawSurface_t *ds ) {
int i, j, k;
drawVert_t *verts;
int w, h;
int x, y;
float s, t;
mesh_t mesh, *subdividedMesh, *tempMesh, *newmesh;
int widthtable[LIGHTMAP_WIDTH], heighttable[LIGHTMAP_HEIGHT], ssize;
verts = ds->verts;
mesh.width = ds->patchWidth;
mesh.height = ds->patchHeight;
mesh.verts = verts;
newmesh = SubdivideMesh( mesh, 8, 999 );
PutMeshOnCurve( *newmesh );
tempMesh = RemoveLinearMeshColumnsRows( newmesh );
FreeMesh(newmesh);
ssize = samplesize;
if (ds->shaderInfo->lightmapSampleSize)
ssize = ds->shaderInfo->lightmapSampleSize;
#ifdef LIGHTMAP_PATCHSHIFT
subdividedMesh = SubdivideMeshQuads( tempMesh, ssize, LIGHTMAP_WIDTH-1, widthtable, heighttable);
#else
subdividedMesh = SubdivideMeshQuads( tempMesh, ssize, LIGHTMAP_WIDTH, widthtable, heighttable);
#endif
w = subdividedMesh->width;
h = subdividedMesh->height;
#ifdef LIGHTMAP_PATCHSHIFT
w++;
h++;
#endif
FreeMesh(subdividedMesh);
// allocate the lightmap
c_exactLightmap += w * h;
if ( !AllocLMBlock( w, h, &x, &y ) ) {
PrepareNewLightmap();
if ( !AllocLMBlock( w, h, &x, &y ) ) {
Error("Entity %i, brush %i: Lightmap allocation failed",
ds->mapBrush->entitynum, ds->mapBrush->brushnum );
}
}
#ifdef LIGHTMAP_PATCHSHIFT
w--;
h--;
#endif
// set the lightmap texture coordinates in the drawVerts
ds->lightmapNum = numLightmaps - 1;
ds->lightmapWidth = w;
ds->lightmapHeight = h;
ds->lightmapX = x;
ds->lightmapY = y;
for ( i = 0 ; i < ds->patchWidth ; i++ ) {
for ( k = 0 ; k < w ; k++ ) {
if ( originalWidths[k] >= i ) {
break;
}
}
if (k >= w)
k = w-1;
s = x + k;
for ( j = 0 ; j < ds->patchHeight ; j++ ) {
for ( k = 0 ; k < h ; k++ ) {
if ( originalHeights[k] >= j ) {
break;
}
}
if (k >= h)
k = h-1;
t = y + k;
verts[i + j * ds->patchWidth].lightmap[0] = ( s + 0.5 ) / LIGHTMAP_WIDTH;
verts[i + j * ds->patchWidth].lightmap[1] = ( t + 0.5 ) / LIGHTMAP_HEIGHT;
}
}
}
/*
===================
AllocateLightmapForSurface
===================
*/
//#define LIGHTMAP_BLOCK 16
void AllocateLightmapForSurface( mapDrawSurface_t *ds ) {
vec3_t mins, maxs, size, exactSize, delta;
int i;
drawVert_t *verts;
int w, h;
int x, y, ssize;
int axis;
vec3_t vecs[2];
float s, t;
vec3_t origin;
plane_t *plane;
float d;
vec3_t planeNormal;
if ( ds->patch ) {
AllocateLightmapForPatch( ds );
return;
}
ssize = samplesize;
if (ds->shaderInfo->lightmapSampleSize)
ssize = ds->shaderInfo->lightmapSampleSize;
plane = &mapplanes[ ds->side->planenum ];
// bound the surface
ClearBounds( mins, maxs );
verts = ds->verts;
for ( i = 0 ; i < ds->numVerts ; i++ ) {
AddPointToBounds( verts[i].xyz, mins, maxs );
}
// round to the lightmap resolution
for ( i = 0 ; i < 3 ; i++ ) {
exactSize[i] = maxs[i] - mins[i];
mins[i] = ssize * floor( mins[i] / ssize );
maxs[i] = ssize * ceil( maxs[i] / ssize );
size[i] = (maxs[i] - mins[i]) / ssize + 1;
}
// the two largest axis will be the lightmap size
memset( vecs, 0, sizeof( vecs ) );
planeNormal[0] = fabs( plane->normal[0] );
planeNormal[1] = fabs( plane->normal[1] );
planeNormal[2] = fabs( plane->normal[2] );
if ( planeNormal[0] >= planeNormal[1] && planeNormal[0] >= planeNormal[2] ) {
w = size[1];
h = size[2];
axis = 0;
vecs[0][1] = 1.0 / ssize;
vecs[1][2] = 1.0 / ssize;
} else if ( planeNormal[1] >= planeNormal[0] && planeNormal[1] >= planeNormal[2] ) {
w = size[0];
h = size[2];
axis = 1;
vecs[0][0] = 1.0 / ssize;
vecs[1][2] = 1.0 / ssize;
} else {
w = size[0];
h = size[1];
axis = 2;
vecs[0][0] = 1.0 / ssize;
vecs[1][1] = 1.0 / ssize;
}
if ( !plane->normal[axis] ) {
Error( "Chose a 0 valued axis" );
}
if ( w > LIGHTMAP_WIDTH ) {
VectorScale ( vecs[0], (float)LIGHTMAP_WIDTH/w, vecs[0] );
w = LIGHTMAP_WIDTH;
}
if ( h > LIGHTMAP_HEIGHT ) {
VectorScale ( vecs[1], (float)LIGHTMAP_HEIGHT/h, vecs[1] );
h = LIGHTMAP_HEIGHT;
}
c_exactLightmap += w * h;
if ( !AllocLMBlock( w, h, &x, &y ) ) {
PrepareNewLightmap();
if ( !AllocLMBlock( w, h, &x, &y ) ) {
Error("Entity %i, brush %i: Lightmap allocation failed",
ds->mapBrush->entitynum, ds->mapBrush->brushnum );
}
}
// set the lightmap texture coordinates in the drawVerts
ds->lightmapNum = numLightmaps - 1;
ds->lightmapWidth = w;
ds->lightmapHeight = h;
ds->lightmapX = x;
ds->lightmapY = y;
for ( i = 0 ; i < ds->numVerts ; i++ ) {
VectorSubtract( verts[i].xyz, mins, delta );
s = DotProduct( delta, vecs[0] ) + x + 0.5;
t = DotProduct( delta, vecs[1] ) + y + 0.5;
verts[i].lightmap[0] = s / LIGHTMAP_WIDTH;
verts[i].lightmap[1] = t / LIGHTMAP_HEIGHT;
}
// calculate the world coordinates of the lightmap samples
// project mins onto plane to get origin
d = DotProduct( mins, plane->normal ) - plane->dist;
d /= plane->normal[ axis ];
VectorCopy( mins, origin );
origin[axis] -= d;
// project stepped lightmap blocks and subtract to get planevecs
for ( i = 0 ; i < 2 ; i++ ) {
vec3_t normalized;
float len;
len = VectorNormalize( vecs[i], normalized );
VectorScale( normalized, (1.0/len), vecs[i] );
d = DotProduct( vecs[i], plane->normal );
d /= plane->normal[ axis ];
vecs[i][axis] -= d;
}
VectorCopy( origin, ds->lightmapOrigin );
VectorCopy( vecs[0], ds->lightmapVecs[0] );
VectorCopy( vecs[1], ds->lightmapVecs[1] );
VectorCopy( plane->normal, ds->lightmapVecs[2] );
}
/*
===================
AllocateLightmaps
===================
*/
void AllocateLightmaps( entity_t *e ) {
int i, j;
mapDrawSurface_t *ds;
shaderInfo_t *si;
qprintf ("--- AllocateLightmaps ---\n");
// sort all surfaces by shader so common shaders will usually
// be in the same lightmap
numSortShaders = 0;
for ( i = e->firstDrawSurf ; i < numMapDrawSurfs ; i++ ) {
ds = &mapDrawSurfs[i];
if ( !ds->numVerts ) {
continue; // leftover from a surface subdivision
}
if ( ds->miscModel ) {
continue;
}
if ( !ds->patch ) {
VectorCopy( mapplanes[ds->side->planenum].normal, ds->lightmapVecs[2] );
}
// search for this shader
for ( j = 0 ; j < numSortShaders ; j++ ) {
if ( ds->shaderInfo == surfsOnShader[j]->shaderInfo ) {
ds->nextOnShader = surfsOnShader[j];
surfsOnShader[j] = ds;
break;
}
}
if ( j == numSortShaders ) {
if ( numSortShaders >= MAX_MAP_SHADERS ) {
Error( "MAX_MAP_SHADERS" );
}
surfsOnShader[j] = ds;
numSortShaders++;
}
}
qprintf( "%5i unique shaders\n", numSortShaders );
// for each shader, allocate lightmaps for each surface
// numLightmaps = 0;
// PrepareNewLightmap();
for ( i = 0 ; i < numSortShaders ; i++ ) {
si = surfsOnShader[i]->shaderInfo;
for ( ds = surfsOnShader[i] ; ds ; ds = ds->nextOnShader ) {
// some surfaces don't need lightmaps allocated for them
if ( si->surfaceFlags & SURF_NOLIGHTMAP ) {
ds->lightmapNum = -1;
} else if ( si->surfaceFlags & SURF_POINTLIGHT ) {
ds->lightmapNum = -3;
} else {
AllocateLightmapForSurface( ds );
}
}
}
qprintf( "%7i exact lightmap texels\n", c_exactLightmap );
qprintf( "%7i block lightmap texels\n", numLightmaps * LIGHTMAP_WIDTH*LIGHTMAP_HEIGHT );
}

5748
q3map/lightv.c Normal file

File diff suppressed because it is too large Load diff

148
q3map/makefile Normal file
View file

@ -0,0 +1,148 @@
CFLAGS = -c
LDFLAGS =
ODIR = /q3/q3map
EXEBASE = q3map
EXE = $(ODIR)/$(EXEBASE)
all: $(EXE)
_irix:
make "CFLAGS = -c -Ofast=ip27 -OPT:IEEE_arithmetic=3 -I../common -Xcpluscomm " "LDFLAGS = -Ofast=ip27 -OPT:IEEE_arithmetic=3 -g"
_irixdebug:
make "CFLAGS = -c -O2 -g -I../common -Xcpluscomm" "LDFLAGS = -g"
_irixinst:
make "_irix"
make "install"
clean:
rm -f $(ODIR)/*.o $(EXE)
install:
cp $(EXE) /quake3_bin
chmod 0777 /quake3_bin/$(EXEBASE)
installtest:
cp $(EXE) /quake3_bin/$(EXEBASE)_test
chmod 0777 /quake3_bin/$(EXEBASE)_test
FILES = $(ODIR)/fog.o $(ODIR)/brush.o $(ODIR)/tjunction.o $(ODIR)/vis.o $(ODIR)/visflow.o \
$(ODIR)/light.o $(ODIR)/lightmaps.o $(ODIR)/bspfile.o \
$(ODIR)/cmdlib.o $(ODIR)/patch.o $(ODIR)/mesh.o $(ODIR)/nodraw.o $(ODIR)/glfile.o \
$(ODIR)/leakfile.o $(ODIR)/map.o $(ODIR)/mathlib.o $(ODIR)/polylib.o $(ODIR)/aselib.o \
$(ODIR)/imagelib.o $(ODIR)/portals.o $(ODIR)/prtfile.o $(ODIR)/bsp.o $(ODIR)/surface.o \
$(ODIR)/scriplib.o $(ODIR)/shaders.o $(ODIR)/threads.o $(ODIR)/tree.o \
$(ODIR)/writebsp.o $(ODIR)/facebsp.o $(ODIR)/misc_model.o $(ODIR)/light_trace.o
$(EXE) : $(FILES)
cc -o $(EXE) $(LDFLAGS) $(FILES) -lm
$(ODIR)/surface.o : surface.c
cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
cc $(CFLAGS) -o $@ /tmp/temp.i
$(ODIR)/fog.o : fog.c
cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
cc $(CFLAGS) -o $@ /tmp/temp.i
$(ODIR)/brush.o : brush.c
cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
cc $(CFLAGS) -o $@ /tmp/temp.i
$(ODIR)/tjunction.o : tjunction.c
cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
cc $(CFLAGS) -o $@ /tmp/temp.i
$(ODIR)/lightmaps.o : lightmaps.c
cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
cc $(CFLAGS) -o $@ /tmp/temp.i
$(ODIR)/brushbsp.o : brushbsp.c
cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
cc $(CFLAGS) -o $@ /tmp/temp.i
$(ODIR)/facebsp.o : facebsp.c
cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
cc $(CFLAGS) -o $@ /tmp/temp.i
$(ODIR)/patch.o : patch.c
cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
cc $(CFLAGS) -o $@ /tmp/temp.i
$(ODIR)/mesh.o : mesh.c
cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
cc $(CFLAGS) -o $@ /tmp/temp.i
$(ODIR)/misc_model.o : misc_model.c
cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
cc $(CFLAGS) -o $@ /tmp/temp.i
$(ODIR)/nodraw.o : nodraw.c
cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
cc $(CFLAGS) -o $@ /tmp/temp.i
$(ODIR)/glfile.o : glfile.c
cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
cc $(CFLAGS) -o $@ /tmp/temp.i
$(ODIR)/leakfile.o : leakfile.c
cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
cc $(CFLAGS) -o $@ /tmp/temp.i
$(ODIR)/map.o : map.c
cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
cc $(CFLAGS) -o $@ /tmp/temp.i
$(ODIR)/portals.o : portals.c
cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
cc $(CFLAGS) -o $@ /tmp/temp.i
$(ODIR)/prtfile.o : prtfile.c
cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
cc $(CFLAGS) -o $@ /tmp/temp.i
$(ODIR)/bsp.o : bsp.c
cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
cc $(CFLAGS) -o $@ /tmp/temp.i
$(ODIR)/tree.o : tree.c
cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
cc $(CFLAGS) -o $@ /tmp/temp.i
$(ODIR)/shaders.o : shaders.c
cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
cc $(CFLAGS) -o $@ /tmp/temp.i
$(ODIR)/writebsp.o : writebsp.c
cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
cc $(CFLAGS) -o $@ /tmp/temp.i
$(ODIR)/csg.o : csg.c
cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
cc $(CFLAGS) -o $@ /tmp/temp.i
$(ODIR)/vis.o : vis.c
cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
cc $(CFLAGS) -o $@ /tmp/temp.i
$(ODIR)/visflow.o : visflow.c
cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
cc $(CFLAGS) -o $@ /tmp/temp.i
$(ODIR)/light.o : light.c
cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
cc $(CFLAGS) -o $@ /tmp/temp.i
$(ODIR)/light_trace.o : light_trace.c
cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
cc $(CFLAGS) -o $@ /tmp/temp.i
$(ODIR)/cmdlib.o : ../common/cmdlib.c
cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
cc $(CFLAGS) -o $@ /tmp/temp.i
$(ODIR)/mathlib.o : ../common/mathlib.c
cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
cc $(CFLAGS) -o $@ /tmp/temp.i
$(ODIR)/polylib.o : ../common/polylib.c
cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
cc $(CFLAGS) -o $@ /tmp/temp.i
$(ODIR)/aselib.o : ../common/aselib.c
cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
cc $(CFLAGS) -o $@ /tmp/temp.i
$(ODIR)/imagelib.o : ../common/imagelib.c
cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
cc $(CFLAGS) -o $@ /tmp/temp.i
$(ODIR)/scriplib.o : ../common/scriplib.c
cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
cc $(CFLAGS) -o $@ /tmp/temp.i
$(ODIR)/threads.o : ../common/threads.c
cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
cc $(CFLAGS) -o $@ /tmp/temp.i
$(ODIR)/bspfile.o : ../common/bspfile.c
cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
cc $(CFLAGS) -o $@ /tmp/temp.i

1251
q3map/map.c Normal file

File diff suppressed because it is too large Load diff

682
q3map/mesh.c Normal file
View file

@ -0,0 +1,682 @@
/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
This file is part of Quake III Arena source code.
Quake III Arena source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
Quake III Arena source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Foobar; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
#include "qbsp.h"
/*
===============================================================
MESH SUBDIVISION
===============================================================
*/
int originalWidths[MAX_EXPANDED_AXIS];
int originalHeights[MAX_EXPANDED_AXIS];
int neighbors[8][2] = {
{0,1}, {1,1}, {1,0}, {1,-1}, {0,-1}, {-1,-1}, {-1,0}, {-1,1}
};
/*
============
LerpDrawVert
============
*/
void LerpDrawVert( drawVert_t *a, drawVert_t *b, drawVert_t *out ) {
out->xyz[0] = 0.5 * (a->xyz[0] + b->xyz[0]);
out->xyz[1] = 0.5 * (a->xyz[1] + b->xyz[1]);
out->xyz[2] = 0.5 * (a->xyz[2] + b->xyz[2]);
out->st[0] = 0.5 * (a->st[0] + b->st[0]);
out->st[1] = 0.5 * (a->st[1] + b->st[1]);
out->lightmap[0] = 0.5 * (a->lightmap[0] + b->lightmap[0]);
out->lightmap[1] = 0.5 * (a->lightmap[1] + b->lightmap[1]);
out->color[0] = (a->color[0] + b->color[0]) >> 1;
out->color[1] = (a->color[1] + b->color[1]) >> 1;
out->color[2] = (a->color[2] + b->color[2]) >> 1;
out->color[3] = (a->color[3] + b->color[3]) >> 1;
}
void FreeMesh( mesh_t *m ) {
free( m->verts );
free( m );
}
void PrintMesh( mesh_t *m ) {
int i, j;
for ( i = 0 ; i < m->height ; i++ ) {
for ( j = 0 ; j < m->width ; j++ ) {
_printf("(%5.2f %5.2f %5.2f) "
, m->verts[i*m->width+j].xyz[0]
, m->verts[i*m->width+j].xyz[1]
, m->verts[i*m->width+j].xyz[2] );
}
_printf("\n");
}
}
mesh_t *CopyMesh( mesh_t *mesh ) {
mesh_t *out;
int size;
out = malloc( sizeof( *out ) );
out->width = mesh->width;
out->height = mesh->height;
size = out->width * out->height * sizeof( *out->verts );
out->verts = malloc( size );
memcpy( out->verts, mesh->verts, size );
return out;
}
/*
=================
TransposeMesh
Returns a transposed copy of the mesh, freeing the original
=================
*/
mesh_t *TransposeMesh( mesh_t *in ) {
int w, h;
mesh_t *out;
out = malloc( sizeof( *out ) );
out->width = in->height;
out->height = in->width;
out->verts = malloc( out->width * out->height * sizeof( drawVert_t ) );
for ( h = 0 ; h < in->height ; h++ ) {
for ( w = 0 ; w < in->width ; w++ ) {
out->verts[ w * in->height + h ] = in->verts[ h * in->width + w ];
}
}
FreeMesh( in );
return out;
}
void InvertMesh( mesh_t *in ) {
int w, h;
drawVert_t temp;
for ( h = 0 ; h < in->height ; h++ ) {
for ( w = 0 ; w < in->width / 2 ; w++ ) {
temp = in->verts[ h * in->width + w ];
in->verts[ h * in->width + w ] = in->verts[ h * in->width + in->width - 1 - w ];
in->verts[ h * in->width + in->width - 1 - w ] = temp;
}
}
}
/*
=================
MakeMeshNormals
=================
*/
void MakeMeshNormals( mesh_t in ) {
int i, j, k, dist;
vec3_t normal;
vec3_t sum;
int count;
vec3_t base;
vec3_t delta;
int x, y;
drawVert_t *dv;
vec3_t around[8], temp;
qboolean good[8];
qboolean wrapWidth, wrapHeight;
float len;
wrapWidth = qfalse;
for ( i = 0 ; i < in.height ; i++ ) {
VectorSubtract( in.verts[i*in.width].xyz,
in.verts[i*in.width+in.width-1].xyz, delta );
len = VectorLength( delta );
if ( len > 1.0 ) {
break;
}
}
if ( i == in.height ) {
wrapWidth = qtrue;
}
wrapHeight = qfalse;
for ( i = 0 ; i < in.width ; i++ ) {
VectorSubtract( in.verts[i].xyz,
in.verts[i + (in.height-1)*in.width].xyz, delta );
len = VectorLength( delta );
if ( len > 1.0 ) {
break;
}
}
if ( i == in.width) {
wrapHeight = qtrue;
}
for ( i = 0 ; i < in.width ; i++ ) {
for ( j = 0 ; j < in.height ; j++ ) {
count = 0;
dv = &in.verts[j*in.width+i];
VectorCopy( dv->xyz, base );
for ( k = 0 ; k < 8 ; k++ ) {
VectorClear( around[k] );
good[k] = qfalse;
for ( dist = 1 ; dist <= 3 ; dist++ ) {
x = i + neighbors[k][0] * dist;
y = j + neighbors[k][1] * dist;
if ( wrapWidth ) {
if ( x < 0 ) {
x = in.width - 1 + x;
} else if ( x >= in.width ) {
x = 1 + x - in.width;
}
}
if ( wrapHeight ) {
if ( y < 0 ) {
y = in.height - 1 + y;
} else if ( y >= in.height ) {
y = 1 + y - in.height;
}
}
if ( x < 0 || x >= in.width || y < 0 || y >= in.height ) {
break; // edge of patch
}
VectorSubtract( in.verts[y*in.width+x].xyz, base, temp );
if ( VectorNormalize( temp, temp ) == 0 ) {
continue; // degenerate edge, get more dist
} else {
good[k] = qtrue;
VectorCopy( temp, around[k] );
break; // good edge
}
}
}
VectorClear( sum );
for ( k = 0 ; k < 8 ; k++ ) {
if ( !good[k] || !good[(k+1)&7] ) {
continue; // didn't get two points
}
CrossProduct( around[(k+1)&7], around[k], normal );
if ( VectorNormalize( normal, normal ) == 0 ) {
continue;
}
VectorAdd( normal, sum, sum );
count++;
}
if ( count == 0 ) {
//_printf("bad normal\n");
count = 1;
}
VectorNormalize( sum, dv->normal );
}
}
}
/*
=================
PutMeshOnCurve
Drops the aproximating points onto the curve
=================
*/
void PutMeshOnCurve( mesh_t in ) {
int i, j, l;
float prev, next;
// put all the aproximating points on the curve
for ( i = 0 ; i < in.width ; i++ ) {
for ( j = 1 ; j < in.height ; j += 2 ) {
for ( l = 0 ; l < 3 ; l++ ) {
prev = ( in.verts[j*in.width+i].xyz[l] + in.verts[(j+1)*in.width+i].xyz[l] ) * 0.5;
next = ( in.verts[j*in.width+i].xyz[l] + in.verts[(j-1)*in.width+i].xyz[l] ) * 0.5;
in.verts[j*in.width+i].xyz[l] = ( prev + next ) * 0.5;
}
}
}
for ( j = 0 ; j < in.height ; j++ ) {
for ( i = 1 ; i < in.width ; i += 2 ) {
for ( l = 0 ; l < 3 ; l++ ) {
prev = ( in.verts[j*in.width+i].xyz[l] + in.verts[j*in.width+i+1].xyz[l] ) * 0.5;
next = ( in.verts[j*in.width+i].xyz[l] + in.verts[j*in.width+i-1].xyz[l] ) * 0.5;
in.verts[j*in.width+i].xyz[l] = ( prev + next ) * 0.5;
}
}
}
}
/*
=================
SubdivideMesh
=================
*/
mesh_t *SubdivideMesh( mesh_t in, float maxError, float minLength ) {
int i, j, k, l;
drawVert_t prev, next, mid;
vec3_t prevxyz, nextxyz, midxyz;
vec3_t delta;
float len;
mesh_t out;
drawVert_t expand[MAX_EXPANDED_AXIS][MAX_EXPANDED_AXIS];
out.width = in.width;
out.height = in.height;
for ( i = 0 ; i < in.width ; i++ ) {
for ( j = 0 ; j < in.height ; j++ ) {
expand[j][i] = in.verts[j*in.width+i];
}
}
for ( i = 0 ; i < in.height ; i++ ) {
originalHeights[i] = i;
}
for ( i = 0 ; i < in.width ; i++ ) {
originalWidths[i] = i;
}
// horizontal subdivisions
for ( j = 0 ; j + 2 < out.width ; j += 2 ) {
// check subdivided midpoints against control points
for ( i = 0 ; i < out.height ; i++ ) {
for ( l = 0 ; l < 3 ; l++ ) {
prevxyz[l] = expand[i][j+1].xyz[l] - expand[i][j].xyz[l];
nextxyz[l] = expand[i][j+2].xyz[l] - expand[i][j+1].xyz[l];
midxyz[l] = (expand[i][j].xyz[l] + expand[i][j+1].xyz[l] * 2
+ expand[i][j+2].xyz[l] ) * 0.25;
}
// if the span length is too long, force a subdivision
if ( VectorLength( prevxyz ) > minLength
|| VectorLength( nextxyz ) > minLength ) {
break;
}
// see if this midpoint is off far enough to subdivide
VectorSubtract( expand[i][j+1].xyz, midxyz, delta );
len = VectorLength( delta );
if ( len > maxError ) {
break;
}
}
if ( out.width + 2 >= MAX_EXPANDED_AXIS ) {
break; // can't subdivide any more
}
if ( i == out.height ) {
continue; // didn't need subdivision
}
// insert two columns and replace the peak
out.width += 2;
for ( k = out.width - 1 ; k > j + 3 ; k-- ) {
originalWidths[k] = originalWidths[k-2];
}
originalWidths[j+3] = originalWidths[j+1];
originalWidths[j+2] = originalWidths[j+1];
originalWidths[j+1] = originalWidths[j];
for ( i = 0 ; i < out.height ; i++ ) {
LerpDrawVert( &expand[i][j], &expand[i][j+1], &prev );
LerpDrawVert( &expand[i][j+1], &expand[i][j+2], &next );
LerpDrawVert( &prev, &next, &mid );
for ( k = out.width - 1 ; k > j + 3 ; k-- ) {
expand[i][k] = expand[i][k-2];
}
expand[i][j + 1] = prev;
expand[i][j + 2] = mid;
expand[i][j + 3] = next;
}
// back up and recheck this set again, it may need more subdivision
j -= 2;
}
// vertical subdivisions
for ( j = 0 ; j + 2 < out.height ; j += 2 ) {
// check subdivided midpoints against control points
for ( i = 0 ; i < out.width ; i++ ) {
for ( l = 0 ; l < 3 ; l++ ) {
prevxyz[l] = expand[j+1][i].xyz[l] - expand[j][i].xyz[l];
nextxyz[l] = expand[j+2][i].xyz[l] - expand[j+1][i].xyz[l];
midxyz[l] = (expand[j][i].xyz[l] + expand[j+1][i].xyz[l] * 2
+ expand[j+2][i].xyz[l] ) * 0.25;
}
// if the span length is too long, force a subdivision
if ( VectorLength( prevxyz ) > minLength
|| VectorLength( nextxyz ) > minLength ) {
break;
}
// see if this midpoint is off far enough to subdivide
VectorSubtract( expand[j+1][i].xyz, midxyz, delta );
len = VectorLength( delta );
if ( len > maxError ) {
break;
}
}
if ( out.height + 2 >= MAX_EXPANDED_AXIS ) {
break; // can't subdivide any more
}
if ( i == out.width ) {
continue; // didn't need subdivision
}
// insert two columns and replace the peak
out.height += 2;
for ( k = out.height - 1 ; k > j + 3 ; k-- ) {
originalHeights[k] = originalHeights[k-2];
}
originalHeights[j+3] = originalHeights[j+1];
originalHeights[j+2] = originalHeights[j+1];
originalHeights[j+1] = originalHeights[j];
for ( i = 0 ; i < out.width ; i++ ) {
LerpDrawVert( &expand[j][i], &expand[j+1][i], &prev );
LerpDrawVert( &expand[j+1][i], &expand[j+2][i], &next );
LerpDrawVert( &prev, &next, &mid );
for ( k = out.height - 1 ; k > j + 3 ; k-- ) {
expand[k][i] = expand[k-2][i];
}
expand[j+1][i] = prev;
expand[j+2][i] = mid;
expand[j+3][i] = next;
}
// back up and recheck this set again, it may need more subdivision
j -= 2;
}
// collapse the verts
out.verts = &expand[0][0];
for ( i = 1 ; i < out.height ; i++ ) {
memmove( &out.verts[i*out.width], expand[i], out.width * sizeof(drawVert_t) );
}
return CopyMesh(&out);
}
/*
================
ProjectPointOntoVector
================
*/
void ProjectPointOntoVector( vec3_t point, vec3_t vStart, vec3_t vEnd, vec3_t vProj )
{
vec3_t pVec, vec;
VectorSubtract( point, vStart, pVec );
VectorSubtract( vEnd, vStart, vec );
VectorNormalize( vec, vec );
// project onto the directional vector for this segment
VectorMA( vStart, DotProduct( pVec, vec ), vec, vProj );
}
/*
================
RemoveLinearMeshColumsRows
================
*/
mesh_t *RemoveLinearMeshColumnsRows( mesh_t *in ) {
int i, j, k;
float len, maxLength;
vec3_t proj, dir;
mesh_t out;
drawVert_t expand[MAX_EXPANDED_AXIS][MAX_EXPANDED_AXIS];
out.width = in->width;
out.height = in->height;
for ( i = 0 ; i < in->width ; i++ ) {
for ( j = 0 ; j < in->height ; j++ ) {
expand[j][i] = in->verts[j*in->width+i];
}
}
for ( j = 1 ; j < out.width - 1; j++ ) {
maxLength = 0;
for ( i = 0 ; i < out.height ; i++ ) {
ProjectPointOntoVector(expand[i][j].xyz, expand[i][j-1].xyz, expand[i][j+1].xyz, proj);
VectorSubtract(expand[i][j].xyz, proj, dir);
len = VectorLength(dir);
if (len > maxLength) {
maxLength = len;
}
}
if (maxLength < 0.1)
{
out.width--;
for ( i = 0 ; i < out.height ; i++ ) {
for (k = j; k < out.width; k++) {
expand[i][k] = expand[i][k+1];
}
}
for (k = j; k < out.width; k++) {
originalWidths[k] = originalWidths[k+1];
}
j--;
}
}
for ( j = 1 ; j < out.height - 1; j++ ) {
maxLength = 0;
for ( i = 0 ; i < out.width ; i++ ) {
ProjectPointOntoVector(expand[j][i].xyz, expand[j-1][i].xyz, expand[j+1][i].xyz, proj);
VectorSubtract(expand[j][i].xyz, proj, dir);
len = VectorLength(dir);
if (len > maxLength) {
maxLength = len;
}
}
if (maxLength < 0.1)
{
out.height--;
for ( i = 0 ; i < out.width ; i++ ) {
for (k = j; k < out.height; k++) {
expand[k][i] = expand[k+1][i];
}
}
for (k = j; k < out.height; k++) {
originalHeights[k] = originalHeights[k+1];
}
j--;
}
}
// collapse the verts
out.verts = &expand[0][0];
for ( i = 1 ; i < out.height ; i++ ) {
memmove( &out.verts[i*out.width], expand[i], out.width * sizeof(drawVert_t) );
}
return CopyMesh(&out);
}
/*
============
LerpDrawVertAmount
============
*/
void LerpDrawVertAmount( drawVert_t *a, drawVert_t *b, float amount, drawVert_t *out ) {
out->xyz[0] = a->xyz[0] + amount * (b->xyz[0] - a->xyz[0]);
out->xyz[1] = a->xyz[1] + amount * (b->xyz[1] - a->xyz[1]);
out->xyz[2] = a->xyz[2] + amount * (b->xyz[2] - a->xyz[2]);
out->st[0] = a->st[0] + amount * (b->st[0] - a->st[0]);
out->st[1] = a->st[1] + amount * (b->st[1] - a->st[1]);
out->lightmap[0] = a->lightmap[0] + amount * (b->lightmap[0] - a->lightmap[0]);
out->lightmap[1] = a->lightmap[1] + amount * (b->lightmap[1] - a->lightmap[1]);
out->color[0] = a->color[0] + amount * (b->color[0] - a->color[0]);
out->color[1] = a->color[1] + amount * (b->color[1] - a->color[1]);
out->color[2] = a->color[2] + amount * (b->color[2] - a->color[2]);
out->color[3] = a->color[3] + amount * (b->color[3] - a->color[3]);
out->normal[0] = a->normal[0] + amount * (b->normal[0] - a->normal[0]);
out->normal[1] = a->normal[1] + amount * (b->normal[1] - a->normal[1]);
out->normal[2] = a->normal[2] + amount * (b->normal[2] - a->normal[2]);
VectorNormalize(out->normal, out->normal);
}
/*
=================
SubdivideMeshQuads
=================
*/
mesh_t *SubdivideMeshQuads( mesh_t *in, float minLength, int maxsize, int widthtable[], int heighttable[]) {
int i, j, k, w, h, maxsubdivisions, subdivisions;
vec3_t dir;
float length, maxLength, amount;
mesh_t out;
drawVert_t expand[MAX_EXPANDED_AXIS][MAX_EXPANDED_AXIS];
out.width = in->width;
out.height = in->height;
for ( i = 0 ; i < in->width ; i++ ) {
for ( j = 0 ; j < in->height ; j++ ) {
expand[j][i] = in->verts[j*in->width+i];
}
}
if (maxsize > MAX_EXPANDED_AXIS)
Error("SubdivideMeshQuads: maxsize > MAX_EXPANDED_AXIS");
// horizontal subdivisions
maxsubdivisions = (maxsize - in->width) / (in->width - 1);
for ( w = 0, j = 0 ; w < in->width - 1; w++, j += subdivisions + 1) {
maxLength = 0;
for ( i = 0 ; i < out.height ; i++ ) {
VectorSubtract(expand[i][j+1].xyz, expand[i][j].xyz, dir);
length = VectorLength( dir );
if (length > maxLength) {
maxLength = length;
}
}
subdivisions = (int) (maxLength / minLength);
if (subdivisions > maxsubdivisions)
subdivisions = maxsubdivisions;
widthtable[w] = subdivisions + 1;
if (subdivisions <= 0)
continue;
out.width += subdivisions;
for ( k = out.width - 1; k >= j + subdivisions; k-- ) {
originalWidths[k] = originalWidths[k-subdivisions];
}
for (k = 1; k <= subdivisions; k++) {
originalWidths[j+k] = originalWidths[j];
}
for ( i = 0 ; i < out.height ; i++ ) {
for ( k = out.width - 1 ; k > j + subdivisions; k-- ) {
expand[i][k] = expand[i][k-subdivisions];
}
for (k = 1; k <= subdivisions; k++)
{
amount = (float) k / (subdivisions + 1);
LerpDrawVertAmount(&expand[i][j], &expand[i][j+subdivisions+1], amount, &expand[i][j+k]);
}
}
}
maxsubdivisions = (maxsize - in->height) / (in->height - 1);
for ( h = 0, j = 0 ; h < in->height - 1; h++, j += subdivisions + 1) {
maxLength = 0;
for ( i = 0 ; i < out.width ; i++ ) {
VectorSubtract(expand[j+1][i].xyz, expand[j][i].xyz, dir);
length = VectorLength( dir );
if (length > maxLength) {
maxLength = length;
}
}
subdivisions = (int) (maxLength / minLength);
if (subdivisions > maxsubdivisions)
subdivisions = maxsubdivisions;
heighttable[h] = subdivisions + 1;
if (subdivisions <= 0)
continue;
out.height += subdivisions;
for ( k = out.height - 1; k >= j + subdivisions; k-- ) {
originalHeights[k] = originalHeights[k-subdivisions];
}
for (k = 1; k <= subdivisions; k++) {
originalHeights[j+k] = originalHeights[j];
}
for ( i = 0 ; i < out.width ; i++ ) {
for ( k = out.height - 1 ; k > j + subdivisions; k-- ) {
expand[k][i] = expand[k-subdivisions][i];
}
for (k = 1; k <= subdivisions; k++)
{
amount = (float) k / (subdivisions + 1);
LerpDrawVertAmount(&expand[j][i], &expand[j+subdivisions+1][i], amount, &expand[j+k][i]);
}
}
}
// collapse the verts
out.verts = &expand[0][0];
for ( i = 1 ; i < out.height ; i++ ) {
memmove( &out.verts[i*out.width], expand[i], out.width * sizeof(drawVert_t) );
}
return CopyMesh(&out);
}

48
q3map/mesh.h Normal file
View file

@ -0,0 +1,48 @@
/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
This file is part of Quake III Arena source code.
Quake III Arena source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
Quake III Arena source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Foobar; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
// mesh.h
typedef struct {
int width, height;
drawVert_t *verts;
} mesh_t;
#define MAX_EXPANDED_AXIS 128
extern int originalWidths[MAX_EXPANDED_AXIS];
extern int originalHeights[MAX_EXPANDED_AXIS];
void FreeMesh( mesh_t *m );
mesh_t *CopyMesh( mesh_t *mesh );
void PrintMesh( mesh_t *m );
mesh_t *TransposeMesh( mesh_t *in );
void InvertMesh( mesh_t *m );
mesh_t *SubdivideMesh( mesh_t in, float maxError, float minLength );
mesh_t *SubdivideMeshQuads( mesh_t *in, float minLength, int maxsize, int widthtable[], int heighttable[]);
mesh_t *RemoveLinearMeshColumnsRows( mesh_t *in );
void MakeMeshNormals( mesh_t in );
void PutMeshOnCurve( mesh_t in );
void MakeNormalVectors (vec3_t forward, vec3_t right, vec3_t up);

472
q3map/misc_model.c Normal file
View file

@ -0,0 +1,472 @@
/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
This file is part of Quake III Arena source code.
Quake III Arena source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
Quake III Arena source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Foobar; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
#include "qbsp.h"
#include "aselib.h"
#ifdef _WIN32
#ifdef _TTIMOBUILD
#include "pakstuff.h"
#else
#include "../libs/pakstuff.h"
#endif
#endif
typedef struct {
char modelName[1024];
md3Header_t *header;
} loadedModel_t;
int c_triangleModels;
int c_triangleSurfaces;
int c_triangleVertexes;
int c_triangleIndexes;
#define MAX_LOADED_MODELS 1024
loadedModel_t loadedModels[MAX_LOADED_MODELS];
int numLoadedModels;
/*
=================
R_LoadMD3
=================
*/
#define LL(x) x=LittleLong(x)
md3Header_t *R_LoadMD3( const char *mod_name ) {
int i, j;
md3Header_t *md3;
md3Frame_t *frame;
md3Surface_t *surf;
md3Triangle_t *tri;
md3St_t *st;
md3XyzNormal_t *xyz;
int version;
char filename[1024];
int len;
sprintf( filename, "%s%s", gamedir, mod_name );
len = TryLoadFile( filename, (void **)&md3 );
#ifdef _WIN32
if ( len <= 0 ) {
len = PakLoadAnyFile(filename, (void **)&md3);
}
#endif
if ( len <= 0 ) {
return NULL;
}
version = LittleLong (md3->version);
if (version != MD3_VERSION) {
_printf( "R_LoadMD3: %s has wrong version (%i should be %i)\n",
mod_name, version, MD3_VERSION);
return NULL;
}
LL(md3->ident);
LL(md3->version);
LL(md3->numFrames);
LL(md3->numTags);
LL(md3->numSurfaces);
LL(md3->numSkins);
LL(md3->ofsFrames);
LL(md3->ofsTags);
LL(md3->ofsSurfaces);
LL(md3->ofsEnd);
if ( md3->numFrames < 1 ) {
_printf( "R_LoadMD3: %s has no frames\n", mod_name );
return NULL;
}
// we don't need to swap tags in the renderer, they aren't used
// swap all the frames
frame = (md3Frame_t *) ( (byte *)md3 + md3->ofsFrames );
for ( i = 0 ; i < md3->numFrames ; i++, frame++) {
frame->radius = LittleFloat( frame->radius );
for ( j = 0 ; j < 3 ; j++ ) {
frame->bounds[0][j] = LittleFloat( frame->bounds[0][j] );
frame->bounds[1][j] = LittleFloat( frame->bounds[1][j] );
frame->localOrigin[j] = LittleFloat( frame->localOrigin[j] );
}
}
// swap all the surfaces
surf = (md3Surface_t *) ( (byte *)md3 + md3->ofsSurfaces );
for ( i = 0 ; i < md3->numSurfaces ; i++) {
LL(surf->ident);
LL(surf->flags);
LL(surf->numFrames);
LL(surf->numShaders);
LL(surf->numTriangles);
LL(surf->ofsTriangles);
LL(surf->numVerts);
LL(surf->ofsShaders);
LL(surf->ofsSt);
LL(surf->ofsXyzNormals);
LL(surf->ofsEnd);
if ( surf->numVerts > SHADER_MAX_VERTEXES ) {
Error ("R_LoadMD3: %s has more than %i verts on a surface (%i)",
mod_name, SHADER_MAX_VERTEXES, surf->numVerts );
}
if ( surf->numTriangles*3 > SHADER_MAX_INDEXES ) {
Error ("R_LoadMD3: %s has more than %i triangles on a surface (%i)",
mod_name, SHADER_MAX_INDEXES / 3, surf->numTriangles );
}
// swap all the triangles
tri = (md3Triangle_t *) ( (byte *)surf + surf->ofsTriangles );
for ( j = 0 ; j < surf->numTriangles ; j++, tri++ ) {
LL(tri->indexes[0]);
LL(tri->indexes[1]);
LL(tri->indexes[2]);
}
// swap all the ST
st = (md3St_t *) ( (byte *)surf + surf->ofsSt );
for ( j = 0 ; j < surf->numVerts ; j++, st++ ) {
st->st[0] = LittleFloat( st->st[0] );
st->st[1] = LittleFloat( st->st[1] );
}
// swap all the XyzNormals
xyz = (md3XyzNormal_t *) ( (byte *)surf + surf->ofsXyzNormals );
for ( j = 0 ; j < surf->numVerts * surf->numFrames ; j++, xyz++ )
{
xyz->xyz[0] = LittleShort( xyz->xyz[0] );
xyz->xyz[1] = LittleShort( xyz->xyz[1] );
xyz->xyz[2] = LittleShort( xyz->xyz[2] );
xyz->normal = LittleShort( xyz->normal );
}
// find the next surface
surf = (md3Surface_t *)( (byte *)surf + surf->ofsEnd );
}
return md3;
}
/*
================
LoadModel
================
*/
md3Header_t *LoadModel( const char *modelName ) {
int i;
loadedModel_t *lm;
// see if we already have it loaded
for ( i = 0, lm = loadedModels ; i < numLoadedModels ; i++, lm++ ) {
if ( !strcmp( modelName, lm->modelName ) ) {
return lm->header;
}
}
// load it
if ( numLoadedModels == MAX_LOADED_MODELS ) {
Error( "MAX_LOADED_MODELS" );
}
numLoadedModels++;
strcpy( lm->modelName, modelName );
lm->header = R_LoadMD3( modelName );
return lm->header;
}
/*
============
InsertMD3Model
Convert a model entity to raw geometry surfaces and insert it in the tree
============
*/
void InsertMD3Model( const char *modelName, vec3_t origin, float angle, tree_t *tree ) {
int i, j;
md3Header_t *md3;
md3Surface_t *surf;
md3Shader_t *shader;
md3Triangle_t *tri;
md3St_t *st;
md3XyzNormal_t *xyz;
drawVert_t *outv;
float lat, lng;
float angleCos, angleSin;
mapDrawSurface_t *out;
vec3_t temp;
angle = angle / 180 * Q_PI;
angleCos = cos( angle );
angleSin = sin( angle );
// load the model
md3 = LoadModel( modelName );
if ( !md3 ) {
return;
}
// each md3 surface will become a new bsp surface
c_triangleModels++;
c_triangleSurfaces += md3->numSurfaces;
// expand, translate, and rotate the vertexes
// swap all the surfaces
surf = (md3Surface_t *) ( (byte *)md3 + md3->ofsSurfaces );
for ( i = 0 ; i < md3->numSurfaces ; i++) {
// allocate a surface
out = AllocDrawSurf();
out->miscModel = qtrue;
shader = (md3Shader_t *) ( (byte *)surf + surf->ofsShaders );
out->shaderInfo = ShaderInfoForShader( shader->name );
out->numVerts = surf->numVerts;
out->verts = malloc( out->numVerts * sizeof( out->verts[0] ) );
out->numIndexes = surf->numTriangles * 3;
out->indexes = malloc( out->numIndexes * sizeof( out->indexes[0] ) );
out->lightmapNum = -1;
out->fogNum = -1;
// emit the indexes
c_triangleIndexes += surf->numTriangles * 3;
tri = (md3Triangle_t *) ( (byte *)surf + surf->ofsTriangles );
for ( j = 0 ; j < surf->numTriangles ; j++, tri++ ) {
out->indexes[j*3+0] = tri->indexes[0];
out->indexes[j*3+1] = tri->indexes[1];
out->indexes[j*3+2] = tri->indexes[2];
}
// emit the vertexes
st = (md3St_t *) ( (byte *)surf + surf->ofsSt );
xyz = (md3XyzNormal_t *) ( (byte *)surf + surf->ofsXyzNormals );
c_triangleVertexes += surf->numVerts;
for ( j = 0 ; j < surf->numVerts ; j++, st++, xyz++ ) {
outv = &out->verts[ j ];
outv->st[0] = st->st[0];
outv->st[1] = st->st[1];
outv->lightmap[0] = 0;
outv->lightmap[1] = 0;
// the colors will be set by the lighting pass
outv->color[0] = 255;
outv->color[1] = 255;
outv->color[2] = 255;
outv->color[3] = 255;
outv->xyz[0] = origin[0] + MD3_XYZ_SCALE * ( xyz->xyz[0] * angleCos - xyz->xyz[1] * angleSin );
outv->xyz[1] = origin[1] + MD3_XYZ_SCALE * ( xyz->xyz[0] * angleSin + xyz->xyz[1] * angleCos );
outv->xyz[2] = origin[2] + MD3_XYZ_SCALE * ( xyz->xyz[2] );
// decode the lat/lng normal to a 3 float normal
lat = ( xyz->normal >> 8 ) & 0xff;
lng = ( xyz->normal & 0xff );
lat *= Q_PI/128;
lng *= Q_PI/128;
temp[0] = cos(lat) * sin(lng);
temp[1] = sin(lat) * sin(lng);
temp[2] = cos(lng);
// rotate the normal
outv->normal[0] = temp[0] * angleCos - temp[1] * angleSin;
outv->normal[1] = temp[0] * angleSin + temp[1] * angleCos;
outv->normal[2] = temp[2];
}
// find the next surface
surf = (md3Surface_t *)( (byte *)surf + surf->ofsEnd );
}
}
//==============================================================================
/*
============
InsertASEModel
Convert a model entity to raw geometry surfaces and insert it in the tree
============
*/
void InsertASEModel( const char *modelName, vec3_t origin, float angle, tree_t *tree ) {
int i, j;
drawVert_t *outv;
float angleCos, angleSin;
mapDrawSurface_t *out;
int numSurfaces;
const char *name;
polyset_t *pset;
int numFrames;
char filename[1024];
sprintf( filename, "%s%s", gamedir, modelName );
angle = angle / 180 * Q_PI;
angleCos = cos( angle );
angleSin = sin( angle );
// load the model
ASE_Load( filename, qfalse, qfalse );
// each ase surface will become a new bsp surface
numSurfaces = ASE_GetNumSurfaces();
c_triangleModels++;
c_triangleSurfaces += numSurfaces;
// expand, translate, and rotate the vertexes
// swap all the surfaces
for ( i = 0 ; i < numSurfaces ; i++) {
name = ASE_GetSurfaceName( i );
pset = ASE_GetSurfaceAnimation( i, &numFrames, -1, -1, -1 );
if ( !name || !pset ) {
continue;
}
// allocate a surface
out = AllocDrawSurf();
out->miscModel = qtrue;
out->shaderInfo = ShaderInfoForShader( pset->materialname );
out->numVerts = 3 * pset->numtriangles;
out->verts = malloc( out->numVerts * sizeof( out->verts[0] ) );
out->numIndexes = 3 * pset->numtriangles;
out->indexes = malloc( out->numIndexes * sizeof( out->indexes[0] ) );
out->lightmapNum = -1;
out->fogNum = -1;
// emit the indexes
c_triangleIndexes += out->numIndexes;
for ( j = 0 ; j < out->numIndexes ; j++ ) {
out->indexes[j] = j;
}
// emit the vertexes
c_triangleVertexes += out->numVerts;
for ( j = 0 ; j < out->numVerts ; j++ ) {
int index;
triangle_t *tri;
index = j % 3;
tri = &pset->triangles[ j / 3 ];
outv = &out->verts[ j ];
outv->st[0] = tri->texcoords[index][0];
outv->st[1] = tri->texcoords[index][1];
outv->lightmap[0] = 0;
outv->lightmap[1] = 0;
// the colors will be set by the lighting pass
outv->color[0] = 255;
outv->color[1] = 255;
outv->color[2] = 255;
outv->color[3] = 255;
outv->xyz[0] = origin[0] + tri->verts[index][0];
outv->xyz[1] = origin[1] + tri->verts[index][1];
outv->xyz[2] = origin[2] + tri->verts[index][2];
// rotate the normal
outv->normal[0] = tri->normals[index][0];
outv->normal[1] = tri->normals[index][1];
outv->normal[2] = tri->normals[index][2];
}
}
}
//==============================================================================
/*
=====================
AddTriangleModels
=====================
*/
void AddTriangleModels( tree_t *tree ) {
int entity_num;
entity_t *entity;
qprintf("----- AddTriangleModels -----\n");
for ( entity_num=1 ; entity_num< num_entities ; entity_num++ ) {
entity = &entities[entity_num];
// convert misc_models into raw geometry
if ( !Q_stricmp( "misc_model", ValueForKey( entity, "classname" ) ) ) {
const char *model;
vec3_t origin;
float angle;
// get the angle for rotation FIXME: support full matrix positioning
angle = FloatForKey( entity, "angle" );
GetVectorForKey( entity, "origin", origin );
model = ValueForKey( entity, "model" );
if ( !model[0] ) {
_printf("WARNING: misc_model at %i %i %i without a model key\n", (int)origin[0],
(int)origin[1], (int)origin[2] );
continue;
}
if ( strstr( model, ".md3" ) || strstr( model, ".MD3" ) ) {
InsertMD3Model( model, origin, angle, tree );
continue;
}
if ( strstr( model, ".ase" ) || strstr( model, ".ASE" ) ) {
InsertASEModel( model, origin, angle, tree );
continue;
}
_printf( "Unknown misc_model type: %s\n", model );
continue;
}
}
qprintf( "%5i triangle models\n", c_triangleModels );
qprintf( "%5i triangle surfaces\n", c_triangleSurfaces );
qprintf( "%5i triangle vertexes\n", c_triangleVertexes );
qprintf( "%5i triangle indexes\n", c_triangleIndexes );
}

47
q3map/nodraw.c Normal file
View file

@ -0,0 +1,47 @@
/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
This file is part of Quake III Arena source code.
Quake III Arena source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
Quake III Arena source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Foobar; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
#include "qbsp.h"
vec3_t draw_mins, draw_maxs;
qboolean drawflag;
void Draw_ClearWindow (void)
{
}
//============================================================
#define GLSERV_PORT 25001
void GLS_BeginScene (void)
{
}
void GLS_Winding (winding_t *w, int code)
{
}
void GLS_EndScene (void)
{
}

286
q3map/patch.c Normal file
View file

@ -0,0 +1,286 @@
/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
This file is part of Quake III Arena source code.
Quake III Arena source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
Quake III Arena source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Foobar; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
#include "qbsp.h"
void PrintCtrl( vec3_t ctrl[9] ) {
int i, j;
for ( i = 0 ; i < 3 ; i++ ) {
for ( j = 0 ; j < 3 ; j++ ) {
_printf("(%5.2f %5.2f %5.2f) ", ctrl[i*3+j][0], ctrl[i*3+j][1], ctrl[i*3+j][2] );
}
_printf("\n");
}
}
/*
================
DrawSurfaceForMesh
================
*/
mapDrawSurface_t *DrawSurfaceForMesh( mesh_t *m ) {
mapDrawSurface_t *ds;
int i, j;
mesh_t *copy;
// to make valid normals for patches with degenerate edges,
// we need to make a copy of the mesh and put the aproximating
// points onto the curve
copy = CopyMesh( m );
PutMeshOnCurve( *copy );
MakeMeshNormals( *copy );
for ( j = 0 ; j < m->width ; j++ ) {
for ( i = 0 ; i < m->height ; i++ ) {
VectorCopy( copy->verts[i*m->width+j].normal, m->verts[i*m->width+j].normal );
}
}
FreeMesh( copy );
ds = AllocDrawSurf();
ds->mapBrush = NULL;
ds->side = NULL;
ds->patch = qtrue;
ds->patchWidth = m->width;
ds->patchHeight = m->height;
ds->numVerts = ds->patchWidth * ds->patchHeight;
ds->verts = malloc( ds->numVerts * sizeof( *ds->verts ) );
memcpy( ds->verts, m->verts, ds->numVerts * sizeof( *ds->verts ) );
ds->lightmapNum = -1;
ds->fogNum = -1;
return ds;
}
/*
=================
ParsePatch
Creates a mapDrawSurface_t from the patch text
=================
*/
void ParsePatch( void ) {
vec_t info[5];
int i, j;
parseMesh_t *pm;
char texture[MAX_QPATH];
char shader[MAX_QPATH];
mesh_t m;
drawVert_t *verts;
epair_t *ep;
MatchToken( "{" );
// get texture
GetToken (qtrue);
strcpy( texture, token );
// save the shader name for retexturing
if ( numMapIndexedShaders == MAX_MAP_BRUSHSIDES ) {
Error( "MAX_MAP_BRUSHSIDES" );
}
strcpy( mapIndexedShaders[numMapIndexedShaders], texture );
numMapIndexedShaders++;
Parse1DMatrix( 5, info );
m.width = info[0];
m.height = info[1];
m.verts = verts = malloc( m.width * m.height * sizeof( m.verts[0] ) );
if ( m.width < 0 || m.width > MAX_PATCH_SIZE
|| m.height < 0 || m.height > MAX_PATCH_SIZE ) {
Error("ParsePatch: bad size");
}
MatchToken( "(" );
for ( j = 0 ; j < m.width ; j++ ) {
MatchToken( "(" );
for ( i = 0 ; i < m.height ; i++ ) {
Parse1DMatrix( 5, verts[i*m.width+j].xyz );
}
MatchToken( ")" );
}
MatchToken( ")" );
// if brush primitives format, we may have some epairs to ignore here
GetToken(qtrue);
if (g_bBrushPrimit!=BPRIMIT_OLDBRUSHES && strcmp(token,"}"))
{
// NOTE: we leak that!
ep = ParseEpair();
}
else
UnGetToken();
MatchToken( "}" );
MatchToken( "}" );
if ( noCurveBrushes ) {
return;
}
// find default flags and values
pm = malloc( sizeof( *pm ) );
memset( pm, 0, sizeof( *pm ) );
sprintf( shader, "textures/%s", texture );
pm->shaderInfo = ShaderInfoForShader( shader );
pm->mesh = m;
// link to the entity
pm->next = mapent->patches;
mapent->patches = pm;
}
void GrowGroup_r( int patchNum, int patchCount, const byte *bordering, byte *group ) {
int i;
const byte *row;
if ( group[patchNum] ) {
return;
}
group[patchNum] = 1;
row = bordering + patchNum * patchCount;
for ( i = 0 ; i < patchCount ; i++ ) {
if ( row[i] ) {
GrowGroup_r( i, patchCount, bordering, group );
}
}
}
/*
=====================
PatchMapDrawSurfs
Any patches that share an edge need to choose their
level of detail as a unit, otherwise the edges would
pull apart.
=====================
*/
void PatchMapDrawSurfs( entity_t *e ) {
parseMesh_t *pm;
parseMesh_t *check, *scan;
mapDrawSurface_t *ds;
int patchCount, groupCount;
int i, j, k, l, c1, c2;
drawVert_t *v1, *v2;
vec3_t bounds[2];
byte *bordering;
parseMesh_t *meshes[MAX_MAP_DRAW_SURFS];
qboolean grouped[MAX_MAP_DRAW_SURFS];
byte group[MAX_MAP_DRAW_SURFS];
qprintf( "----- PatchMapDrawSurfs -----\n" );
patchCount = 0;
for ( pm = e->patches ; pm ; pm = pm->next ) {
meshes[patchCount] = pm;
patchCount++;
}
if ( !patchCount ) {
return;
}
bordering = malloc( patchCount * patchCount );
memset( bordering, 0, patchCount * patchCount );
// build the bordering matrix
for ( k = 0 ; k < patchCount ; k++ ) {
bordering[k*patchCount+k] = 1;
for ( l = k+1 ; l < patchCount ; l++ ) {
check = meshes[k];
scan = meshes[l];
c1 = scan->mesh.width * scan->mesh.height;
v1 = scan->mesh.verts;
for ( i = 0 ; i < c1 ; i++, v1++ ) {
c2 = check->mesh.width * check->mesh.height;
v2 = check->mesh.verts;
for ( j = 0 ; j < c2 ; j++, v2++ ) {
if ( fabs( v1->xyz[0] - v2->xyz[0] ) < 1.0
&& fabs( v1->xyz[1] - v2->xyz[1] ) < 1.0
&& fabs( v1->xyz[2] - v2->xyz[2] ) < 1.0 ) {
break;
}
}
if ( j != c2 ) {
break;
}
}
if ( i != c1 ) {
// we have a connection
bordering[k*patchCount+l] =
bordering[l*patchCount+k] = 1;
} else {
// no connection
bordering[k*patchCount+l] =
bordering[l*patchCount+k] = 0;
}
}
}
// build groups
memset( grouped, 0, sizeof(grouped) );
groupCount = 0;
for ( i = 0 ; i < patchCount ; i++ ) {
if ( !grouped[i] ) {
groupCount++;
}
// recursively find all patches that belong in the same group
memset( group, 0, patchCount );
GrowGroup_r( i, patchCount, bordering, group );
// bound them
ClearBounds( bounds[0], bounds[1] );
for ( j = 0 ; j < patchCount ; j++ ) {
if ( group[j] ) {
grouped[j] = qtrue;
scan = meshes[j];
c1 = scan->mesh.width * scan->mesh.height;
v1 = scan->mesh.verts;
for ( k = 0 ; k < c1 ; k++, v1++ ) {
AddPointToBounds( v1->xyz, bounds[0], bounds[1] );
}
}
}
// create drawsurf
scan = meshes[i];
scan->grouped = qtrue;
ds = DrawSurfaceForMesh( &scan->mesh );
ds->shaderInfo = scan->shaderInfo;
VectorCopy( bounds[0], ds->lightmapVecs[0] );
VectorCopy( bounds[1], ds->lightmapVecs[1] );
}
qprintf( "%5i patches\n", patchCount );
qprintf( "%5i patch LOD groups\n", groupCount );
}

843
q3map/portals.c Normal file
View file

@ -0,0 +1,843 @@
/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
This file is part of Quake III Arena source code.
Quake III Arena source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
Quake III Arena source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Foobar; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
#include "qbsp.h"
int c_active_portals;
int c_peak_portals;
int c_boundary;
int c_boundary_sides;
/*
===========
AllocPortal
===========
*/
portal_t *AllocPortal (void)
{
portal_t *p;
if (numthreads == 1)
c_active_portals++;
if (c_active_portals > c_peak_portals)
c_peak_portals = c_active_portals;
p = malloc (sizeof(portal_t));
memset (p, 0, sizeof(portal_t));
return p;
}
void FreePortal (portal_t *p)
{
if (p->winding)
FreeWinding (p->winding);
if (numthreads == 1)
c_active_portals--;
free (p);
}
//==============================================================
/*
=============
Portal_Passable
Returns true if the portal has non-opaque leafs on both sides
=============
*/
qboolean Portal_Passable(portal_t *p) {
if (!p->onnode) {
return qfalse; // to global outsideleaf
}
if (p->nodes[0]->planenum != PLANENUM_LEAF
|| p->nodes[1]->planenum != PLANENUM_LEAF) {
Error ("Portal_EntityFlood: not a leaf");
}
if ( !p->nodes[0]->opaque && !p->nodes[1]->opaque ) {
return qtrue;
}
return qfalse;
}
//=============================================================================
int c_tinyportals;
/*
=============
AddPortalToNodes
=============
*/
void AddPortalToNodes (portal_t *p, node_t *front, node_t *back)
{
if (p->nodes[0] || p->nodes[1])
Error ("AddPortalToNode: allready included");
p->nodes[0] = front;
p->next[0] = front->portals;
front->portals = p;
p->nodes[1] = back;
p->next[1] = back->portals;
back->portals = p;
}
/*
=============
RemovePortalFromNode
=============
*/
void RemovePortalFromNode (portal_t *portal, node_t *l)
{
portal_t **pp, *t;
// remove reference to the current portal
pp = &l->portals;
while (1)
{
t = *pp;
if (!t)
Error ("RemovePortalFromNode: portal not in leaf");
if ( t == portal )
break;
if (t->nodes[0] == l)
pp = &t->next[0];
else if (t->nodes[1] == l)
pp = &t->next[1];
else
Error ("RemovePortalFromNode: portal not bounding leaf");
}
if (portal->nodes[0] == l)
{
*pp = portal->next[0];
portal->nodes[0] = NULL;
}
else if (portal->nodes[1] == l)
{
*pp = portal->next[1];
portal->nodes[1] = NULL;
}
}
//============================================================================
void PrintPortal (portal_t *p)
{
int i;
winding_t *w;
w = p->winding;
for (i=0 ; i<w->numpoints ; i++)
_printf ("(%5.0f,%5.0f,%5.0f)\n",w->p[i][0]
, w->p[i][1], w->p[i][2]);
}
/*
================
MakeHeadnodePortals
The created portals will face the global outside_node
================
*/
#define SIDESPACE 8
void MakeHeadnodePortals (tree_t *tree)
{
vec3_t bounds[2];
int i, j, n;
portal_t *p, *portals[6];
plane_t bplanes[6], *pl;
node_t *node;
node = tree->headnode;
// pad with some space so there will never be null volume leafs
for (i=0 ; i<3 ; i++)
{
bounds[0][i] = tree->mins[i] - SIDESPACE;
bounds[1][i] = tree->maxs[i] + SIDESPACE;
if ( bounds[0][i] >= bounds[1][i] ) {
Error( "Backwards tree volume" );
}
}
tree->outside_node.planenum = PLANENUM_LEAF;
tree->outside_node.brushlist = NULL;
tree->outside_node.portals = NULL;
tree->outside_node.opaque = qfalse;
for (i=0 ; i<3 ; i++)
for (j=0 ; j<2 ; j++)
{
n = j*3 + i;
p = AllocPortal ();
portals[n] = p;
pl = &bplanes[n];
memset (pl, 0, sizeof(*pl));
if (j)
{
pl->normal[i] = -1;
pl->dist = -bounds[j][i];
}
else
{
pl->normal[i] = 1;
pl->dist = bounds[j][i];
}
p->plane = *pl;
p->winding = BaseWindingForPlane (pl->normal, pl->dist);
AddPortalToNodes (p, node, &tree->outside_node);
}
// clip the basewindings by all the other planes
for (i=0 ; i<6 ; i++)
{
for (j=0 ; j<6 ; j++)
{
if (j == i)
continue;
ChopWindingInPlace (&portals[i]->winding, bplanes[j].normal, bplanes[j].dist, ON_EPSILON);
}
}
}
//===================================================
/*
================
BaseWindingForNode
================
*/
#define BASE_WINDING_EPSILON 0.001
#define SPLIT_WINDING_EPSILON 0.001
winding_t *BaseWindingForNode (node_t *node)
{
winding_t *w;
node_t *n;
plane_t *plane;
vec3_t normal;
vec_t dist;
w = BaseWindingForPlane (mapplanes[node->planenum].normal
, mapplanes[node->planenum].dist);
// clip by all the parents
for (n=node->parent ; n && w ; )
{
plane = &mapplanes[n->planenum];
if (n->children[0] == node)
{ // take front
ChopWindingInPlace (&w, plane->normal, plane->dist, BASE_WINDING_EPSILON);
}
else
{ // take back
VectorSubtract (vec3_origin, plane->normal, normal);
dist = -plane->dist;
ChopWindingInPlace (&w, normal, dist, BASE_WINDING_EPSILON);
}
node = n;
n = n->parent;
}
return w;
}
//============================================================
/*
==================
MakeNodePortal
create the new portal by taking the full plane winding for the cutting plane
and clipping it by all of parents of this node
==================
*/
void MakeNodePortal (node_t *node)
{
portal_t *new_portal, *p;
winding_t *w;
vec3_t normal;
float dist;
int side;
w = BaseWindingForNode (node);
// clip the portal by all the other portals in the node
for (p = node->portals ; p && w; p = p->next[side])
{
if (p->nodes[0] == node)
{
side = 0;
VectorCopy (p->plane.normal, normal);
dist = p->plane.dist;
}
else if (p->nodes[1] == node)
{
side = 1;
VectorSubtract (vec3_origin, p->plane.normal, normal);
dist = -p->plane.dist;
}
else
Error ("CutNodePortals_r: mislinked portal");
ChopWindingInPlace (&w, normal, dist, CLIP_EPSILON);
}
if (!w)
{
return;
}
if (WindingIsTiny (w))
{
c_tinyportals++;
FreeWinding (w);
return;
}
new_portal = AllocPortal ();
new_portal->plane = mapplanes[node->planenum];
new_portal->onnode = node;
new_portal->winding = w;
new_portal->hint = node->hint;
AddPortalToNodes (new_portal, node->children[0], node->children[1]);
}
/*
==============
SplitNodePortals
Move or split the portals that bound node so that the node's
children have portals instead of node.
==============
*/
void SplitNodePortals (node_t *node)
{
portal_t *p, *next_portal, *new_portal;
node_t *f, *b, *other_node;
int side;
plane_t *plane;
winding_t *frontwinding, *backwinding;
plane = &mapplanes[node->planenum];
f = node->children[0];
b = node->children[1];
for (p = node->portals ; p ; p = next_portal)
{
if (p->nodes[0] == node)
side = 0;
else if (p->nodes[1] == node)
side = 1;
else
Error ("SplitNodePortals: mislinked portal");
next_portal = p->next[side];
other_node = p->nodes[!side];
RemovePortalFromNode (p, p->nodes[0]);
RemovePortalFromNode (p, p->nodes[1]);
//
// cut the portal into two portals, one on each side of the cut plane
//
ClipWindingEpsilon (p->winding, plane->normal, plane->dist,
SPLIT_WINDING_EPSILON, &frontwinding, &backwinding);
if (frontwinding && WindingIsTiny(frontwinding))
{
if (!f->tinyportals)
VectorCopy(frontwinding->p[0], f->referencepoint);
f->tinyportals++;
if (!other_node->tinyportals)
VectorCopy(frontwinding->p[0], other_node->referencepoint);
other_node->tinyportals++;
FreeWinding (frontwinding);
frontwinding = NULL;
c_tinyportals++;
}
if (backwinding && WindingIsTiny(backwinding))
{
if (!b->tinyportals)
VectorCopy(backwinding->p[0], b->referencepoint);
b->tinyportals++;
if (!other_node->tinyportals)
VectorCopy(backwinding->p[0], other_node->referencepoint);
other_node->tinyportals++;
FreeWinding (backwinding);
backwinding = NULL;
c_tinyportals++;
}
if (!frontwinding && !backwinding)
{ // tiny windings on both sides
continue;
}
if (!frontwinding)
{
FreeWinding (backwinding);
if (side == 0)
AddPortalToNodes (p, b, other_node);
else
AddPortalToNodes (p, other_node, b);
continue;
}
if (!backwinding)
{
FreeWinding (frontwinding);
if (side == 0)
AddPortalToNodes (p, f, other_node);
else
AddPortalToNodes (p, other_node, f);
continue;
}
// the winding is split
new_portal = AllocPortal ();
*new_portal = *p;
new_portal->winding = backwinding;
FreeWinding (p->winding);
p->winding = frontwinding;
if (side == 0)
{
AddPortalToNodes (p, f, other_node);
AddPortalToNodes (new_portal, b, other_node);
}
else
{
AddPortalToNodes (p, other_node, f);
AddPortalToNodes (new_portal, other_node, b);
}
}
node->portals = NULL;
}
/*
================
CalcNodeBounds
================
*/
void CalcNodeBounds (node_t *node)
{
portal_t *p;
int s;
int i;
// calc mins/maxs for both leafs and nodes
ClearBounds (node->mins, node->maxs);
for (p = node->portals ; p ; p = p->next[s])
{
s = (p->nodes[1] == node);
for (i=0 ; i<p->winding->numpoints ; i++)
AddPointToBounds (p->winding->p[i], node->mins, node->maxs);
}
}
/*
==================
MakeTreePortals_r
==================
*/
void MakeTreePortals_r (node_t *node)
{
int i;
CalcNodeBounds (node);
if (node->mins[0] >= node->maxs[0])
{
_printf ("WARNING: node without a volume\n");
_printf("node has %d tiny portals\n", node->tinyportals);
_printf("node reference point %1.2f %1.2f %1.2f\n", node->referencepoint[0],
node->referencepoint[1],
node->referencepoint[2]);
}
for (i=0 ; i<3 ; i++)
{
if (node->mins[i] < MIN_WORLD_COORD || node->maxs[i] > MAX_WORLD_COORD)
{
_printf ("WARNING: node with unbounded volume\n");
break;
}
}
if (node->planenum == PLANENUM_LEAF)
return;
MakeNodePortal (node);
SplitNodePortals (node);
MakeTreePortals_r (node->children[0]);
MakeTreePortals_r (node->children[1]);
}
/*
==================
MakeTreePortals
==================
*/
void MakeTreePortals (tree_t *tree)
{
qprintf( "----- MakeTreePortals -----\n");
MakeHeadnodePortals (tree);
MakeTreePortals_r (tree->headnode);
qprintf("%6d tiny portals\n", c_tinyportals);
}
/*
=========================================================
FLOOD ENTITIES
=========================================================
*/
int c_floodedleafs;
/*
=============
FloodPortals_r
=============
*/
void FloodPortals_r (node_t *node, int dist) {
portal_t *p;
int s;
if ( node->occupied ) {
return;
}
if ( node->opaque ) {
return;
}
c_floodedleafs++;
node->occupied = dist;
for (p=node->portals ; p ; p = p->next[s]) {
s = (p->nodes[1] == node);
FloodPortals_r (p->nodes[!s], dist+1);
}
}
/*
=============
PlaceOccupant
=============
*/
qboolean PlaceOccupant (node_t *headnode, vec3_t origin, entity_t *occupant)
{
node_t *node;
vec_t d;
plane_t *plane;
// find the leaf to start in
node = headnode;
while (node->planenum != PLANENUM_LEAF)
{
plane = &mapplanes[node->planenum];
d = DotProduct (origin, plane->normal) - plane->dist;
if (d >= 0)
node = node->children[0];
else
node = node->children[1];
}
if ( node->opaque )
return qfalse;
node->occupant = occupant;
FloodPortals_r (node, 1);
return qtrue;
}
/*
=============
FloodEntities
Marks all nodes that can be reached by entites
=============
*/
qboolean FloodEntities( tree_t *tree ) {
int i;
vec3_t origin;
const char *cl;
qboolean inside;
node_t *headnode;
headnode = tree->headnode;
qprintf ("--- FloodEntities ---\n");
inside = qfalse;
tree->outside_node.occupied = 0;
c_floodedleafs = 0;
for (i=1 ; i<num_entities ; i++)
{
GetVectorForKey (&entities[i], "origin", origin);
if (VectorCompare(origin, vec3_origin))
continue;
cl = ValueForKey (&entities[i], "classname");
origin[2] += 1; // so objects on floor are ok
if (PlaceOccupant (headnode, origin, &entities[i]))
inside = qtrue;
}
qprintf("%5i flooded leafs\n", c_floodedleafs );
if (!inside)
{
qprintf ("no entities in open -- no filling\n");
}
else if (tree->outside_node.occupied)
{
qprintf ("entity reached from outside -- no filling\n");
}
return (qboolean)(inside && !tree->outside_node.occupied);
}
/*
=========================================================
FLOOD AREAS
=========================================================
*/
int c_areas;
/*
=============
FloodAreas_r
=============
*/
void FloodAreas_r (node_t *node)
{
portal_t *p;
int s;
bspbrush_t *b;
if ( node->areaportal ) {
//
if ( node->area == -1 ) {
node->area = c_areas;
}
// this node is part of an area portal brush
b = node->brushlist->original;
// if the current area has allready touched this
// portal, we are done
if (b->portalareas[0] == c_areas || b->portalareas[1] == c_areas)
return;
// note the current area as bounding the portal
if (b->portalareas[1] != -1)
{
_printf ("WARNING: areaportal brush %i touches > 2 areas\n", b->brushnum );
return;
}
if (b->portalareas[0] != -1) {
b->portalareas[1] = c_areas;
} else {
b->portalareas[0] = c_areas;
}
return;
}
if (node->area != -1) {
return; // allready got it
}
if ( node->cluster == -1 ) {
return;
}
node->area = c_areas;
for (p=node->portals ; p ; p = p->next[s])
{
s = (p->nodes[1] == node);
if ( !Portal_Passable(p) )
continue;
FloodAreas_r (p->nodes[!s]);
}
}
/*
=============
FindAreas_r
Just decend the tree, and for each node that hasn't had an
area set, flood fill out from there
=============
*/
void FindAreas_r (node_t *node)
{
if (node->planenum != PLANENUM_LEAF)
{
FindAreas_r (node->children[0]);
FindAreas_r (node->children[1]);
return;
}
if (node->opaque)
return;
if (node->areaportal)
return;
if (node->area != -1)
return; // allready got it
FloodAreas_r (node);
c_areas++;
}
/*
=============
CheckAreas_r
=============
*/
void CheckAreas_r (node_t *node)
{
bspbrush_t *b;
if (node->planenum != PLANENUM_LEAF)
{
CheckAreas_r (node->children[0]);
CheckAreas_r (node->children[1]);
return;
}
if (node->opaque)
return;
if (node->cluster != -1)
if (node->area == -1)
_printf("WARNING: cluster %d has area set to -1\n", node->cluster);
if (node->areaportal)
{
b = node->brushlist->original;
// check if the areaportal touches two areas
if (b->portalareas[0] == -1 || b->portalareas[1] == -1)
_printf ("WARNING: areaportal brush %i doesn't touch two areas\n", b->brushnum);
}
}
/*
=============
FloodAreas
Mark each leaf with an area, bounded by CONTENTS_AREAPORTAL
=============
*/
void FloodAreas (tree_t *tree)
{
qprintf ("--- FloodAreas ---\n");
FindAreas_r( tree->headnode );
// check for areaportal brushes that don't touch two areas
CheckAreas_r( tree->headnode );
qprintf ("%5i areas\n", c_areas);
}
//======================================================
int c_outside;
int c_inside;
int c_solid;
void FillOutside_r (node_t *node)
{
if (node->planenum != PLANENUM_LEAF)
{
FillOutside_r (node->children[0]);
FillOutside_r (node->children[1]);
return;
}
// anything not reachable by an entity
// can be filled away
if (!node->occupied) {
if ( !node->opaque ) {
c_outside++;
node->opaque = qtrue;
} else {
c_solid++;
}
} else {
c_inside++;
}
}
/*
=============
FillOutside
Fill all nodes that can't be reached by entities
=============
*/
void FillOutside (node_t *headnode)
{
c_outside = 0;
c_inside = 0;
c_solid = 0;
qprintf ("--- FillOutside ---\n");
FillOutside_r (headnode);
qprintf ("%5i solid leafs\n", c_solid);
qprintf ("%5i leafs filled\n", c_outside);
qprintf ("%5i inside leafs\n", c_inside);
}
//==============================================================

272
q3map/prtfile.c Normal file
View file

@ -0,0 +1,272 @@
/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
This file is part of Quake III Arena source code.
Quake III Arena source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
Quake III Arena source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Foobar; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
#include "qbsp.h"
/*
==============================================================================
PORTAL FILE GENERATION
Save out name.prt for qvis to read
==============================================================================
*/
#define PORTALFILE "PRT1"
FILE *pf;
int num_visclusters; // clusters the player can be in
int num_visportals;
int num_solidfaces;
void WriteFloat (FILE *f, vec_t v)
{
if ( fabs(v - Q_rint(v)) < 0.001 )
fprintf (f,"%i ",(int)Q_rint(v));
else
fprintf (f,"%f ",v);
}
/*
=================
WritePortalFile_r
=================
*/
void WritePortalFile_r (node_t *node)
{
int i, s;
portal_t *p;
winding_t *w;
vec3_t normal;
vec_t dist;
// decision node
if (node->planenum != PLANENUM_LEAF) {
WritePortalFile_r (node->children[0]);
WritePortalFile_r (node->children[1]);
return;
}
if (node->opaque) {
return;
}
for (p = node->portals ; p ; p=p->next[s])
{
w = p->winding;
s = (p->nodes[1] == node);
if (w && p->nodes[0] == node)
{
if (!Portal_Passable(p))
continue;
// write out to the file
// sometimes planes get turned around when they are very near
// the changeover point between different axis. interpret the
// plane the same way vis will, and flip the side orders if needed
// FIXME: is this still relevent?
WindingPlane (w, normal, &dist);
if ( DotProduct (p->plane.normal, normal) < 0.99 )
{ // backwards...
fprintf (pf,"%i %i %i ",w->numpoints, p->nodes[1]->cluster, p->nodes[0]->cluster);
}
else
fprintf (pf,"%i %i %i ",w->numpoints, p->nodes[0]->cluster, p->nodes[1]->cluster);
if (p->hint)
fprintf (pf, "1 ");
else
fprintf (pf, "0 ");
for (i=0 ; i<w->numpoints ; i++)
{
fprintf (pf,"(");
WriteFloat (pf, w->p[i][0]);
WriteFloat (pf, w->p[i][1]);
WriteFloat (pf, w->p[i][2]);
fprintf (pf,") ");
}
fprintf (pf,"\n");
}
}
}
/*
=================
WriteFaceFile_r
=================
*/
void WriteFaceFile_r (node_t *node)
{
int i, s;
portal_t *p;
winding_t *w;
// decision node
if (node->planenum != PLANENUM_LEAF) {
WriteFaceFile_r (node->children[0]);
WriteFaceFile_r (node->children[1]);
return;
}
if (node->opaque) {
return;
}
for (p = node->portals ; p ; p=p->next[s])
{
w = p->winding;
s = (p->nodes[1] == node);
if (w)
{
if (Portal_Passable(p))
continue;
// write out to the file
if (p->nodes[0] == node)
{
fprintf (pf,"%i %i ",w->numpoints, p->nodes[0]->cluster);
for (i=0 ; i<w->numpoints ; i++)
{
fprintf (pf,"(");
WriteFloat (pf, w->p[i][0]);
WriteFloat (pf, w->p[i][1]);
WriteFloat (pf, w->p[i][2]);
fprintf (pf,") ");
}
fprintf (pf,"\n");
}
else
{
fprintf (pf,"%i %i ",w->numpoints, p->nodes[1]->cluster);
for (i = w->numpoints-1; i >= 0; i--)
{
fprintf (pf,"(");
WriteFloat (pf, w->p[i][0]);
WriteFloat (pf, w->p[i][1]);
WriteFloat (pf, w->p[i][2]);
fprintf (pf,") ");
}
fprintf (pf,"\n");
}
}
}
}
/*
================
NumberLeafs_r
================
*/
void NumberLeafs_r (node_t *node)
{
portal_t *p;
if ( node->planenum != PLANENUM_LEAF ) {
// decision node
node->cluster = -99;
NumberLeafs_r (node->children[0]);
NumberLeafs_r (node->children[1]);
return;
}
node->area = -1;
if ( node->opaque ) {
// solid block, viewpoint never inside
node->cluster = -1;
return;
}
node->cluster = num_visclusters;
num_visclusters++;
// count the portals
for (p = node->portals ; p ; )
{
if (p->nodes[0] == node) // only write out from first leaf
{
if (Portal_Passable(p))
num_visportals++;
else
num_solidfaces++;
p = p->next[0];
}
else
{
if (!Portal_Passable(p))
num_solidfaces++;
p = p->next[1];
}
}
}
/*
================
NumberClusters
================
*/
void NumberClusters(tree_t *tree) {
num_visclusters = 0;
num_visportals = 0;
num_solidfaces = 0;
qprintf ("--- NumberClusters ---\n");
// set the cluster field in every leaf and count the total number of portals
NumberLeafs_r (tree->headnode);
qprintf ("%5i visclusters\n", num_visclusters);
qprintf ("%5i visportals\n", num_visportals);
qprintf ("%5i solidfaces\n", num_solidfaces);
}
/*
================
WritePortalFile
================
*/
void WritePortalFile (tree_t *tree)
{
char filename[1024];
qprintf ("--- WritePortalFile ---\n");
// write the file
sprintf (filename, "%s.prt", source);
_printf ("writing %s\n", filename);
pf = fopen (filename, "w");
if (!pf)
Error ("Error opening %s", filename);
fprintf (pf, "%s\n", PORTALFILE);
fprintf (pf, "%i\n", num_visclusters);
fprintf (pf, "%i\n", num_visportals);
fprintf (pf, "%i\n", num_solidfaces);
WritePortalFile_r(tree->headnode);
WriteFaceFile_r(tree->headnode);
fclose (pf);
}

56
q3map/q3map.sln Normal file
View file

@ -0,0 +1,56 @@
Microsoft Visual Studio Solution File, Format Version 8.00
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "jpeg6", "..\libs\jpeg6\jpeg6.vcproj", "{A862AD26-94DD-4618-A814-F6AACA0B2FE3}"
ProjectSection(ProjectDependencies) = postProject
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pak", "..\libs\pak\pak.vcproj", "{F2ACC9D7-D628-4624-864F-87FE58787625}"
ProjectSection(ProjectDependencies) = postProject
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "q3map", "q3map.vcproj", "{F1162C55-66E7-4486-B1F3-071CFAA78332}"
ProjectSection(ProjectDependencies) = postProject
{F2ACC9D7-D628-4624-864F-87FE58787625} = {F2ACC9D7-D628-4624-864F-87FE58787625}
{A862AD26-94DD-4618-A814-F6AACA0B2FE3} = {A862AD26-94DD-4618-A814-F6AACA0B2FE3}
EndProjectSection
EndProject
Global
GlobalSection(SourceCodeControl) = preSolution
SccNumberOfProjects = 3
SccProjectUniqueName0 = ..\\libs\\jpeg6\\jpeg6.vcproj
SccProjectName0 = \u0022$/source/q3radiant\u0022,\u0020FEFAAAAA
SccLocalPath0 = ..\\q3radiant
SccProvider0 = MSSCCI:Perforce\u0020SCM
SccProjectFilePathRelativizedFromConnection0 = ..\\libs\\jpeg6\\
SccProjectUniqueName1 = ..\\libs\\pak\\pak.vcproj
SccProjectName1 = \u0022$/source/q3radiant\u0022,\u0020FEFAAAAA
SccLocalPath1 = ..\\q3radiant
SccProvider1 = MSSCCI:Perforce\u0020SCM
SccProjectFilePathRelativizedFromConnection1 = ..\\libs\\pak\\
SccProjectUniqueName2 = q3map.vcproj
SccProjectName2 = \u0022$/source/q3map\u0022,\u0020PADAAAAA
SccLocalPath2 = .
SccProvider2 = MSSCCI:Perforce\u0020SCM
EndGlobalSection
GlobalSection(SolutionConfiguration) = preSolution
Debug = Debug
Release = Release
EndGlobalSection
GlobalSection(ProjectConfiguration) = postSolution
{A862AD26-94DD-4618-A814-F6AACA0B2FE3}.Debug.ActiveCfg = Debug|Win32
{A862AD26-94DD-4618-A814-F6AACA0B2FE3}.Debug.Build.0 = Debug|Win32
{A862AD26-94DD-4618-A814-F6AACA0B2FE3}.Release.ActiveCfg = Release|Win32
{A862AD26-94DD-4618-A814-F6AACA0B2FE3}.Release.Build.0 = Release|Win32
{F2ACC9D7-D628-4624-864F-87FE58787625}.Debug.ActiveCfg = Debug|Win32
{F2ACC9D7-D628-4624-864F-87FE58787625}.Debug.Build.0 = Debug|Win32
{F2ACC9D7-D628-4624-864F-87FE58787625}.Release.ActiveCfg = Release|Win32
{F2ACC9D7-D628-4624-864F-87FE58787625}.Release.Build.0 = Release|Win32
{F1162C55-66E7-4486-B1F3-071CFAA78332}.Debug.ActiveCfg = Debug|Win32
{F1162C55-66E7-4486-B1F3-071CFAA78332}.Debug.Build.0 = Debug|Win32
{F1162C55-66E7-4486-B1F3-071CFAA78332}.Release.ActiveCfg = Release|Win32
{F1162C55-66E7-4486-B1F3-071CFAA78332}.Release.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
EndGlobalSection
GlobalSection(ExtensibilityAddIns) = postSolution
EndGlobalSection
EndGlobal

1606
q3map/q3map.vcproj Normal file

File diff suppressed because it is too large Load diff

455
q3map/qbsp.h Normal file
View file

@ -0,0 +1,455 @@
/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
This file is part of Quake III Arena source code.
Quake III Arena source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
Quake III Arena source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Foobar; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
#include "cmdlib.h"
#include "mathlib.h"
#include "scriplib.h"
#include "polylib.h"
#include "imagelib.h"
#include "threads.h"
#include "bspfile.h"
#include "shaders.h"
#include "mesh.h"
#define MAX_PATCH_SIZE 32
#define CLIP_EPSILON 0.1
#define PLANENUM_LEAF -1
#define HINT_PRIORITY 1000
typedef struct parseMesh_s {
struct parseMesh_s *next;
mesh_t mesh;
shaderInfo_t *shaderInfo;
qboolean grouped; // used during shared edge grouping
struct parseMesh_s *groupChain;
} parseMesh_t;
typedef struct bspface_s {
struct bspface_s *next;
int planenum;
int priority; // added to value calculation
qboolean checked;
qboolean hint;
winding_t *w;
} bspface_t;
typedef struct plane_s {
vec3_t normal;
vec_t dist;
int type;
struct plane_s *hash_chain;
} plane_t;
typedef struct side_s {
int planenum;
float texMat[2][3]; // brush primitive texture matrix
// for old brush coordinates mode
float vecs[2][4]; // texture coordinate mapping
winding_t *winding;
winding_t *visibleHull; // convex hull of all visible fragments
struct shaderInfo_s *shaderInfo;
int contents; // from shaderInfo
int surfaceFlags; // from shaderInfo
int value; // from shaderInfo
qboolean visible; // choose visble planes first
qboolean bevel; // don't ever use for bsp splitting, and don't bother
// making windings for it
qboolean backSide; // generated side for a q3map_backShader
} side_t;
#define MAX_BRUSH_SIDES 1024
typedef struct bspbrush_s {
struct bspbrush_s *next;
int entitynum; // editor numbering
int brushnum; // editor numbering
struct shaderInfo_s *contentShader;
int contents;
qboolean detail;
qboolean opaque;
int outputNumber; // set when the brush is written to the file list
int portalareas[2];
struct bspbrush_s *original; // chopped up brushes will reference the originals
vec3_t mins, maxs;
int numsides;
side_t sides[6]; // variably sized
} bspbrush_t;
typedef struct drawsurf_s {
shaderInfo_t *shaderInfo;
bspbrush_t *mapBrush; // not valid for patches
side_t *side; // not valid for patches
struct drawsurf_s *nextOnShader; // when sorting by shader for lightmaps
int fogNum; // set by FogDrawSurfs
int lightmapNum; // -1 = no lightmap
int lightmapX, lightmapY;
int lightmapWidth, lightmapHeight;
int numVerts;
drawVert_t *verts;
int numIndexes;
int *indexes;
// for faces only
int planeNum;
vec3_t lightmapOrigin; // also used for flares
vec3_t lightmapVecs[3]; // also used for flares
// for patches only
qboolean patch;
int patchWidth;
int patchHeight;
// for misc_models only
qboolean miscModel;
qboolean flareSurface;
} mapDrawSurface_t;
typedef struct drawSurfRef_s {
struct drawSurfRef_s *nextRef;
int outputNumber;
} drawSurfRef_t;
typedef struct node_s {
// both leafs and nodes
int planenum; // -1 = leaf node
struct node_s *parent;
vec3_t mins, maxs; // valid after portalization
bspbrush_t *volume; // one for each leaf/node
// nodes only
side_t *side; // the side that created the node
struct node_s *children[2];
qboolean hint;
int tinyportals;
vec3_t referencepoint;
// leafs only
qboolean opaque; // view can never be inside
qboolean areaportal;
int cluster; // for portalfile writing
int area; // for areaportals
bspbrush_t *brushlist; // fragments of all brushes in this leaf
drawSurfRef_t *drawSurfReferences; // references to patches pushed down
int occupied; // 1 or greater can reach entity
entity_t *occupant; // for leak file testing
struct portal_s *portals; // also on nodes during construction
} node_t;
typedef struct portal_s {
plane_t plane;
node_t *onnode; // NULL = outside box
node_t *nodes[2]; // [0] = front side of plane
struct portal_s *next[2];
winding_t *winding;
qboolean sidefound; // false if ->side hasn't been checked
qboolean hint;
side_t *side; // NULL = non-visible
} portal_t;
typedef struct {
node_t *headnode;
node_t outside_node;
vec3_t mins, maxs;
} tree_t;
extern int entity_num;
extern qboolean noprune;
extern qboolean nodetail;
extern qboolean fulldetail;
extern qboolean nowater;
extern qboolean noCurveBrushes;
extern qboolean fakemap;
extern qboolean coplanar;
extern qboolean nofog;
extern qboolean testExpand;
extern qboolean showseams;
extern vec_t microvolume;
extern char outbase[32];
extern char source[1024];
extern int samplesize; //sample size in units
extern int novertexlighting;
extern int nogridlighting;
//=============================================================================
// brush.c
int CountBrushList (bspbrush_t *brushes);
bspbrush_t *AllocBrush (int numsides);
void FreeBrush (bspbrush_t *brushes);
void FreeBrushList (bspbrush_t *brushes);
bspbrush_t *CopyBrush (bspbrush_t *brush);
void DrawBrushList (bspbrush_t *brush);
void WriteBrushList (char *name, bspbrush_t *brush, qboolean onlyvis);
void PrintBrush (bspbrush_t *brush);
qboolean BoundBrush (bspbrush_t *brush);
qboolean CreateBrushWindings (bspbrush_t *brush);
bspbrush_t *BrushFromBounds (vec3_t mins, vec3_t maxs);
vec_t BrushVolume (bspbrush_t *brush);
void WriteBspBrushMap (char *name, bspbrush_t *list);
void FilterDetailBrushesIntoTree( entity_t *e, tree_t *tree );
void FilterStructuralBrushesIntoTree( entity_t *e, tree_t *tree );
//=============================================================================
// map.c
extern int entitySourceBrushes;
// mapplanes[ num^1 ] will always be the mirror or mapplanes[ num ]
// nummapplanes will always be even
extern plane_t mapplanes[MAX_MAP_PLANES];
extern int nummapplanes;
extern vec3_t map_mins, map_maxs;
extern char mapIndexedShaders[MAX_MAP_BRUSHSIDES][MAX_QPATH];
extern int numMapIndexedShaders;
extern entity_t *mapent;
#define MAX_BUILD_SIDES 300
extern bspbrush_t *buildBrush;
void LoadMapFile (char *filename);
int FindFloatPlane (vec3_t normal, vec_t dist);
int PlaneTypeForNormal (vec3_t normal);
bspbrush_t *FinishBrush( void );
mapDrawSurface_t *AllocDrawSurf( void );
mapDrawSurface_t *DrawSurfaceForSide( bspbrush_t *b, side_t *s, winding_t *w );
//=============================================================================
//=============================================================================
// draw.c
extern vec3_t draw_mins, draw_maxs;
extern qboolean drawflag;
void Draw_ClearWindow (void);
void DrawWinding (winding_t *w);
void GLS_BeginScene (void);
void GLS_Winding (winding_t *w, int code);
void GLS_EndScene (void);
//=============================================================================
// csg
bspbrush_t *MakeBspBrushList ( bspbrush_t *brushes, vec3_t clipmins, vec3_t clipmaxs);
//=============================================================================
// brushbsp
#define PSIDE_FRONT 1
#define PSIDE_BACK 2
#define PSIDE_BOTH (PSIDE_FRONT|PSIDE_BACK)
#define PSIDE_FACING 4
int BoxOnPlaneSide (vec3_t mins, vec3_t maxs, plane_t *plane);
qboolean WindingIsTiny (winding_t *w);
void SplitBrush (bspbrush_t *brush, int planenum,
bspbrush_t **front, bspbrush_t **back);
tree_t *AllocTree (void);
node_t *AllocNode (void);
tree_t *BrushBSP (bspbrush_t *brushlist, vec3_t mins, vec3_t maxs);
//=============================================================================
// portals.c
void MakeHeadnodePortals (tree_t *tree);
void MakeNodePortal (node_t *node);
void SplitNodePortals (node_t *node);
qboolean Portal_Passable(portal_t *p);
qboolean FloodEntities (tree_t *tree);
void FillOutside (node_t *headnode);
void FloodAreas (tree_t *tree);
bspface_t *VisibleFaces(entity_t *e, tree_t *tree);
void FreePortal (portal_t *p);
void MakeTreePortals (tree_t *tree);
//=============================================================================
// glfile.c
void OutputWinding( winding_t *w, FILE *glview );
void WriteGLView( tree_t *tree, char *source );
//=============================================================================
// leakfile.c
void LeakFile( tree_t *tree );
//=============================================================================
// prtfile.c
void NumberClusters( tree_t *tree );
void WritePortalFile( tree_t *tree );
//=============================================================================
// writebsp.c
void SetModelNumbers (void);
void SetLightStyles (void);
int EmitShader( const char *shader );
void BeginBSPFile (void);
void EndBSPFile (void);
void BeginModel (void);
void EndModel( node_t *headnode );
//=============================================================================
// tree.c
void FreeTree (tree_t *tree);
void FreeTree_r (node_t *node);
void PrintTree_r (node_t *node, int depth);
void FreeTreePortals_r (node_t *node);
//=============================================================================
// patch.c
extern int numMapPatches;
mapDrawSurface_t *DrawSurfaceForMesh( mesh_t *m );
void ParsePatch( void );
mesh_t *SubdivideMesh( mesh_t in, float maxError, float minLength );
void PatchMapDrawSurfs( entity_t *e );
//=============================================================================
// lightmap.c
void AllocateLightmaps( entity_t *e );
//=============================================================================
// tjunction.c
void FixTJunctions( entity_t *e );
//=============================================================================
// fog.c
void FogDrawSurfs( void );
winding_t *WindingFromDrawSurf( mapDrawSurface_t *ds );
//=============================================================================
// facebsp.c
bspface_t *BspFaceForPortal( portal_t *p );
bspface_t *MakeStructuralBspFaceList( bspbrush_t *list );
bspface_t *MakeVisibleBspFaceList( bspbrush_t *list );
tree_t *FaceBSP( bspface_t *list );
//=============================================================================
// misc_model.c
extern int c_triangleModels;
extern int c_triangleSurfaces;
extern int c_triangleVertexes;
extern int c_triangleIndexes;
void AddTriangleModels( tree_t *tree );
//=============================================================================
// surface.c
extern mapDrawSurface_t mapDrawSurfs[MAX_MAP_DRAW_SURFS];
extern int numMapDrawSurfs;
mapDrawSurface_t *AllocDrawSurf( void );
void MergeSides( entity_t *e, tree_t *tree );
void SubdivideDrawSurfs( entity_t *e, tree_t *tree );
void MakeDrawSurfaces( bspbrush_t *b );
void ClipSidesIntoTree( entity_t *e, tree_t *tree );
void FilterDrawsurfsIntoTree( entity_t *e, tree_t *tree );
//==============================================================================
// brush_primit.c
#define BPRIMIT_UNDEFINED 0
#define BPRIMIT_OLDBRUSHES 1
#define BPRIMIT_NEWBRUSHES 2
extern int g_bBrushPrimit;
void ComputeAxisBase( vec3_t normal, vec3_t texX, vec3_t texY);

608
q3map/shaders.c Normal file
View file

@ -0,0 +1,608 @@
/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
This file is part of Quake III Arena source code.
Quake III Arena source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
Quake III Arena source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Foobar; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
#include <string.h>
#include <math.h>
#include "cmdlib.h"
#include "mathlib.h"
#include "imagelib.h"
#include "scriplib.h"
#ifdef _TTIMOBUILD
#include "../common/qfiles.h"
#include "../common/surfaceflags.h"
#else
#include "../code/qcommon/qfiles.h"
#include "../code/game/surfaceflags.h"
#endif
#include "shaders.h"
#ifdef _WIN32
#ifdef _TTIMOBUILD
#include "pakstuff.h"
#include "jpeglib.h"
#else
#include "../libs/pakstuff.h"
#include "../libs/jpeglib.h"
#endif
#endif
// 5% backsplash by default
#define DEFAULT_BACKSPLASH_FRACTION 0.05
#define DEFAULT_BACKSPLASH_DISTANCE 24
#define MAX_SURFACE_INFO 4096
shaderInfo_t defaultInfo;
shaderInfo_t shaderInfo[MAX_SURFACE_INFO];
int numShaderInfo;
typedef struct {
char *name;
int clearSolid, surfaceFlags, contents;
} infoParm_t;
infoParm_t infoParms[] = {
// server relevant contents
{"water", 1, 0, CONTENTS_WATER },
{"slime", 1, 0, CONTENTS_SLIME }, // mildly damaging
{"lava", 1, 0, CONTENTS_LAVA }, // very damaging
{"playerclip", 1, 0, CONTENTS_PLAYERCLIP },
{"monsterclip", 1, 0, CONTENTS_MONSTERCLIP },
{"nodrop", 1, 0, CONTENTS_NODROP }, // don't drop items or leave bodies (death fog, lava, etc)
{"nonsolid", 1, SURF_NONSOLID, 0}, // clears the solid flag
// utility relevant attributes
{"origin", 1, 0, CONTENTS_ORIGIN }, // center of rotating brushes
{"trans", 0, 0, CONTENTS_TRANSLUCENT }, // don't eat contained surfaces
{"detail", 0, 0, CONTENTS_DETAIL }, // don't include in structural bsp
{"structural", 0, 0, CONTENTS_STRUCTURAL }, // force into structural bsp even if trnas
{"areaportal", 1, 0, CONTENTS_AREAPORTAL }, // divides areas
{"clusterportal",1, 0, CONTENTS_CLUSTERPORTAL },// for bots
{"donotenter", 1, 0, CONTENTS_DONOTENTER }, // for bots
{"botclip", 1, 0, CONTENTS_BOTCLIP }, // for bots
{"nobotclip", 0, 0, CONTENTS_NOBOTCLIP }, // don't use for bot clipping
{"fog", 1, 0, CONTENTS_FOG}, // carves surfaces entering
{"sky", 0, SURF_SKY, 0 }, // emit light from an environment map
{"lightfilter", 0, SURF_LIGHTFILTER, 0 }, // filter light going through it
{"alphashadow", 0, SURF_ALPHASHADOW, 0 }, // test light on a per-pixel basis
{"hint", 0, SURF_HINT, 0 }, // use as a primary splitter
// server attributes
{"slick", 0, SURF_SLICK, 0 },
{"noimpact", 0, SURF_NOIMPACT, 0 }, // don't make impact explosions or marks
{"nomarks", 0, SURF_NOMARKS, 0 }, // don't make impact marks, but still explode
{"ladder", 0, SURF_LADDER, 0 },
{"nodamage", 0, SURF_NODAMAGE, 0 },
{"metalsteps", 0, SURF_METALSTEPS,0 },
{"flesh", 0, SURF_FLESH, 0 },
{"nosteps", 0, SURF_NOSTEPS, 0 },
// drawsurf attributes
{"nodraw", 0, SURF_NODRAW, 0 }, // don't generate a drawsurface (or a lightmap)
{"pointlight", 0, SURF_POINTLIGHT, 0 }, // sample lighting at vertexes
{"nolightmap", 0, SURF_NOLIGHTMAP,0 }, // don't generate a lightmap
{"nodlight", 0, SURF_NODLIGHT, 0 }, // don't ever add dynamic lights
{"dust", 0, SURF_DUST, 0} // leave dust trail when walking on this surface
};
/*
===============
LoadShaderImage
===============
*/
byte* LoadImageFile(char *filename, qboolean *bTGA)
{
byte *buffer = NULL;
int nLen = 0;
*bTGA = qtrue;
if (FileExists(filename))
{
LoadFileBlock(filename, &buffer);
}
#ifdef _WIN32
else
{
PakLoadAnyFile(filename, &buffer);
}
#endif
if ( buffer == NULL)
{
nLen = strlen(filename);
filename[nLen-3] = 'j';
filename[nLen-2] = 'p';
filename[nLen-1] = 'g';
if (FileExists(filename))
{
LoadFileBlock(filename, &buffer);
}
#ifdef _WIN32
else
{
PakLoadAnyFile(filename, &buffer);
}
#endif
if ( buffer )
{
*bTGA = qfalse;
}
}
return buffer;
}
/*
===============
LoadShaderImage
===============
*/
static void LoadShaderImage( shaderInfo_t *si ) {
char filename[1024];
int i, count;
float color[4];
byte *buffer;
qboolean bTGA = qtrue;
// look for the lightimage if it is specified
if ( si->lightimage[0] ) {
sprintf( filename, "%s%s", gamedir, si->lightimage );
DefaultExtension( filename, ".tga" );
buffer = LoadImageFile(filename, &bTGA);
if ( buffer != NULL) {
goto loadTga;
}
}
// look for the editorimage if it is specified
if ( si->editorimage[0] ) {
sprintf( filename, "%s%s", gamedir, si->editorimage );
DefaultExtension( filename, ".tga" );
buffer = LoadImageFile(filename, &bTGA);
if ( buffer != NULL) {
goto loadTga;
}
}
// just try the shader name with a .tga
// on unix, we have case sensitivity problems...
sprintf( filename, "%s%s.tga", gamedir, si->shader );
buffer = LoadImageFile(filename, &bTGA);
if ( buffer != NULL) {
goto loadTga;
}
sprintf( filename, "%s%s.TGA", gamedir, si->shader );
buffer = LoadImageFile(filename, &bTGA);
if ( buffer != NULL) {
goto loadTga;
}
// couldn't load anything
_printf("WARNING: Couldn't find image for shader %s\n", si->shader );
si->color[0] = 1;
si->color[1] = 1;
si->color[2] = 1;
si->width = 64;
si->height = 64;
si->pixels = malloc( si->width * si->height * 4 );
memset ( si->pixels, 255, si->width * si->height * 4 );
return;
// load the image to get dimensions and color
loadTga:
if ( bTGA) {
LoadTGABuffer( buffer, &si->pixels, &si->width, &si->height );
}
else {
#ifdef _WIN32
LoadJPGBuff(buffer, &si->pixels, &si->width, &si->height );
#endif
}
free(buffer);
count = si->width * si->height;
VectorClear( color );
color[ 3 ] = 0;
for ( i = 0 ; i < count ; i++ ) {
color[0] += si->pixels[ i * 4 + 0 ];
color[1] += si->pixels[ i * 4 + 1 ];
color[2] += si->pixels[ i * 4 + 2 ];
color[3] += si->pixels[ i * 4 + 3 ];
}
ColorNormalize( color, si->color );
VectorScale( color, 1.0/count, si->averageColor );
}
/*
===============
AllocShaderInfo
===============
*/
static shaderInfo_t *AllocShaderInfo( void ) {
shaderInfo_t *si;
if ( numShaderInfo == MAX_SURFACE_INFO ) {
Error( "MAX_SURFACE_INFO" );
}
si = &shaderInfo[ numShaderInfo ];
numShaderInfo++;
// set defaults
si->contents = CONTENTS_SOLID;
si->backsplashFraction = DEFAULT_BACKSPLASH_FRACTION;
si->backsplashDistance = DEFAULT_BACKSPLASH_DISTANCE;
si->lightmapSampleSize = 0;
si->forceTraceLight = qfalse;
si->forceVLight = qfalse;
si->patchShadows = qfalse;
si->vertexShadows = qfalse;
si->noVertexShadows = qfalse;
si->forceSunLight = qfalse;
si->vertexScale = 1.0;
si->notjunc = qfalse;
return si;
}
/*
===============
ShaderInfoForShader
===============
*/
shaderInfo_t *ShaderInfoForShader( const char *shaderName ) {
int i;
shaderInfo_t *si;
char shader[MAX_QPATH];
// strip off extension
strcpy( shader, shaderName );
StripExtension( shader );
// search for it
for ( i = 0 ; i < numShaderInfo ; i++ ) {
si = &shaderInfo[ i ];
if ( !Q_stricmp( shader, si->shader ) ) {
if ( !si->width ) {
LoadShaderImage( si );
}
return si;
}
}
si = AllocShaderInfo();
strcpy( si->shader, shader );
LoadShaderImage( si );
return si;
}
/*
===============
ParseShaderFile
===============
*/
static void ParseShaderFile( const char *filename ) {
int i;
int numInfoParms = sizeof(infoParms) / sizeof(infoParms[0]);
shaderInfo_t *si;
// qprintf( "shaderFile: %s\n", filename );
LoadScriptFile( filename );
while ( 1 ) {
if ( !GetToken( qtrue ) ) {
break;
}
si = AllocShaderInfo();
strcpy( si->shader, token );
MatchToken( "{" );
while ( 1 ) {
if ( !GetToken( qtrue ) ) {
break;
}
if ( !strcmp( token, "}" ) ) {
break;
}
// skip internal braced sections
if ( !strcmp( token, "{" ) ) {
si->hasPasses = qtrue;
while ( 1 ) {
if ( !GetToken( qtrue ) ) {
break;
}
if ( !strcmp( token, "}" ) ) {
break;
}
}
continue;
}
if ( !Q_stricmp( token, "surfaceparm" ) ) {
GetToken( qfalse );
for ( i = 0 ; i < numInfoParms ; i++ ) {
if ( !Q_stricmp( token, infoParms[i].name ) ) {
si->surfaceFlags |= infoParms[i].surfaceFlags;
si->contents |= infoParms[i].contents;
if ( infoParms[i].clearSolid ) {
si->contents &= ~CONTENTS_SOLID;
}
break;
}
}
if ( i == numInfoParms ) {
// we will silently ignore all tokens beginning with qer,
// which are QuakeEdRadient parameters
if ( Q_strncasecmp( token, "qer", 3 ) ) {
_printf( "Unknown surfaceparm: \"%s\"\n", token );
}
}
continue;
}
// qer_editorimage <image>
if ( !Q_stricmp( token, "qer_editorimage" ) ) {
GetToken( qfalse );
strcpy( si->editorimage, token );
DefaultExtension( si->editorimage, ".tga" );
continue;
}
// q3map_lightimage <image>
if ( !Q_stricmp( token, "q3map_lightimage" ) ) {
GetToken( qfalse );
strcpy( si->lightimage, token );
DefaultExtension( si->lightimage, ".tga" );
continue;
}
// q3map_surfacelight <value>
if ( !Q_stricmp( token, "q3map_surfacelight" ) ) {
GetToken( qfalse );
si->value = atoi( token );
continue;
}
// q3map_lightsubdivide <value>
if ( !Q_stricmp( token, "q3map_lightsubdivide" ) ) {
GetToken( qfalse );
si->lightSubdivide = atoi( token );
continue;
}
// q3map_lightmapsamplesize <value>
if ( !Q_stricmp( token, "q3map_lightmapsamplesize" ) ) {
GetToken( qfalse );
si->lightmapSampleSize = atoi( token );
continue;
}
// q3map_tracelight
if ( !Q_stricmp( token, "q3map_tracelight" ) ) {
si->forceTraceLight = qtrue;
continue;
}
// q3map_vlight
if ( !Q_stricmp( token, "q3map_vlight" ) ) {
si->forceVLight = qtrue;
continue;
}
// q3map_patchshadows
if ( !Q_stricmp( token, "q3map_patchshadows" ) ) {
si->patchShadows = qtrue;
continue;
}
// q3map_vertexshadows
if ( !Q_stricmp( token, "q3map_vertexshadows" ) ) {
si->vertexShadows = qtrue;
continue;
}
// q3map_novertexshadows
if ( !Q_stricmp( token, "q3map_novertexshadows" ) ) {
si->noVertexShadows = qtrue;
continue;
}
// q3map_forcesunlight
if ( !Q_stricmp( token, "q3map_forcesunlight" ) ) {
si->forceSunLight = qtrue;
continue;
}
// q3map_vertexscale
if ( !Q_stricmp( token, "q3map_vertexscale" ) ) {
GetToken( qfalse );
si->vertexScale = atof(token);
continue;
}
// q3map_notjunc
if ( !Q_stricmp( token, "q3map_notjunc" ) ) {
si->notjunc = qtrue;
continue;
}
// q3map_globaltexture
if ( !Q_stricmp( token, "q3map_globaltexture" ) ) {
si->globalTexture = qtrue;
continue;
}
// q3map_backsplash <percent> <distance>
if ( !Q_stricmp( token, "q3map_backsplash" ) ) {
GetToken( qfalse );
si->backsplashFraction = atof( token ) * 0.01;
GetToken( qfalse );
si->backsplashDistance = atof( token );
continue;
}
// q3map_backshader <shader>
if ( !Q_stricmp( token, "q3map_backshader" ) ) {
GetToken( qfalse );
strcpy( si->backShader, token );
continue;
}
// q3map_flare <shader>
if ( !Q_stricmp( token, "q3map_flare" ) ) {
GetToken( qfalse );
strcpy( si->flareShader, token );
continue;
}
// light <value>
// old style flare specification
if ( !Q_stricmp( token, "light" ) ) {
GetToken( qfalse );
strcpy( si->flareShader, "flareshader" );
continue;
}
// q3map_sun <red> <green> <blue> <intensity> <degrees> <elivation>
// color will be normalized, so it doesn't matter what range you use
// intensity falls off with angle but not distance 100 is a fairly bright sun
// degree of 0 = from the east, 90 = north, etc. altitude of 0 = sunrise/set, 90 = noon
if ( !Q_stricmp( token, "q3map_sun" ) ) {
float a, b;
GetToken( qfalse );
si->sunLight[0] = atof( token );
GetToken( qfalse );
si->sunLight[1] = atof( token );
GetToken( qfalse );
si->sunLight[2] = atof( token );
VectorNormalize( si->sunLight, si->sunLight);
GetToken( qfalse );
a = atof( token );
VectorScale( si->sunLight, a, si->sunLight);
GetToken( qfalse );
a = atof( token );
a = a / 180 * Q_PI;
GetToken( qfalse );
b = atof( token );
b = b / 180 * Q_PI;
si->sunDirection[0] = cos( a ) * cos( b );
si->sunDirection[1] = sin( a ) * cos( b );
si->sunDirection[2] = sin( b );
si->surfaceFlags |= SURF_SKY;
continue;
}
// tesssize is used to force liquid surfaces to subdivide
if ( !Q_stricmp( token, "tesssize" ) ) {
GetToken( qfalse );
si->subdivisions = atof( token );
continue;
}
// cull none will set twoSided
if ( !Q_stricmp( token, "cull" ) ) {
GetToken( qfalse );
if ( !Q_stricmp( token, "none" ) ) {
si->twoSided = qtrue;
}
continue;
}
// deformVertexes autosprite[2]
// we catch this so autosprited surfaces become point
// lights instead of area lights
if ( !Q_stricmp( token, "deformVertexes" ) ) {
GetToken( qfalse );
if ( !Q_strncasecmp( token, "autosprite", 10 ) ) {
si->autosprite = qtrue;
si->contents = CONTENTS_DETAIL;
}
continue;
}
// ignore all other tokens on the line
while ( TokenAvailable() ) {
GetToken( qfalse );
}
}
}
}
/*
===============
LoadShaderInfo
===============
*/
#define MAX_SHADER_FILES 64
void LoadShaderInfo( void ) {
char filename[1024];
int i;
char *shaderFiles[MAX_SHADER_FILES];
int numShaderFiles;
sprintf( filename, "%sscripts/shaderlist.txt", gamedir );
LoadScriptFile( filename );
numShaderFiles = 0;
while ( 1 ) {
if ( !GetToken( qtrue ) ) {
break;
}
shaderFiles[numShaderFiles] = malloc(MAX_OS_PATH);
strcpy( shaderFiles[ numShaderFiles ], token );
numShaderFiles++;
}
for ( i = 0 ; i < numShaderFiles ; i++ ) {
sprintf( filename, "%sscripts/%s.shader", gamedir, shaderFiles[i] );
ParseShaderFile( filename );
free(shaderFiles[i]);
}
qprintf( "%5i shaderInfo\n", numShaderInfo);
}

71
q3map/shaders.h Normal file
View file

@ -0,0 +1,71 @@
/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
This file is part of Quake III Arena source code.
Quake III Arena source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
Quake III Arena source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Foobar; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
typedef struct shaderInfo_s {
char shader[MAX_QPATH];
int surfaceFlags;
int contents;
int value;
char backShader[MAX_QPATH]; // for surfaces that generate different front and back passes
char flareShader[MAX_QPATH]; // for light flares
float subdivisions; // from a "tesssize xxx"
float backsplashFraction; // floating point value, usually 0.05
float backsplashDistance; // default 16
float lightSubdivide; // default 120
int lightmapSampleSize; // lightmap sample size
qboolean hasPasses; // false if the shader doesn't define any rendering passes
qboolean globalTexture; // don't normalize texture repeats
qboolean twoSided; // cull none
qboolean autosprite; // autosprite shaders will become point lights
// instead of area lights
qboolean lightFilter; // light rays that cross surfaces of this type
// should test against the filter image
qboolean forceTraceLight; // always use -light for this surface
qboolean forceVLight; // always use -vlight for this surface
qboolean patchShadows; // have patches casting shadows when using -light for this surface
qboolean vertexShadows; // shadows will be casted at this surface even when vertex lit
qboolean noVertexShadows; // no shadows will be casted at this surface in vertex lighting
qboolean forceSunLight; // force sun light at this surface even tho we might not calculate shadows in vertex lighting
qboolean notjunc; // don't use this surface for tjunction fixing
float vertexScale; // vertex light scale
char editorimage[MAX_QPATH]; // use this image to generate texture coordinates
char lightimage[MAX_QPATH]; // use this image to generate color / averageColor
vec3_t color; // colorNormalized
vec3_t averageColor;
int width, height;
byte *pixels;
vec3_t sunLight;
vec3_t sunDirection;
} shaderInfo_t;
void LoadShaderInfo( void );
shaderInfo_t *ShaderInfoForShader( const char *shader );

5742
q3map/soundv.c Normal file

File diff suppressed because it is too large Load diff

1158
q3map/surface.c Normal file

File diff suppressed because it is too large Load diff

1255
q3map/terrain.c Normal file

File diff suppressed because it is too large Load diff

551
q3map/tjunction.c Normal file
View file

@ -0,0 +1,551 @@
/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
This file is part of Quake III Arena source code.
Quake III Arena source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
Quake III Arena source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Foobar; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
#include "qbsp.h"
typedef struct edgePoint_s {
float intercept;
vec3_t xyz;
struct edgePoint_s *prev, *next;
} edgePoint_t;
typedef struct edgeLine_s {
vec3_t normal1;
float dist1;
vec3_t normal2;
float dist2;
vec3_t origin;
vec3_t dir;
edgePoint_t chain; // unused element of doubly linked list
} edgeLine_t;
typedef struct {
float length;
drawVert_t *dv[2];
} originalEdge_t;
#define MAX_ORIGINAL_EDGES 0x10000
originalEdge_t originalEdges[MAX_ORIGINAL_EDGES];
int numOriginalEdges;
#define MAX_EDGE_LINES 0x10000
edgeLine_t edgeLines[MAX_EDGE_LINES];
int numEdgeLines;
int c_degenerateEdges;
int c_addedVerts;
int c_totalVerts;
int c_natural, c_rotate, c_cant;
// these should be whatever epsilon we actually expect,
// plus SNAP_INT_TO_FLOAT
#define LINE_POSITION_EPSILON 0.25
#define POINT_ON_LINE_EPSILON 0.25
/*
====================
InsertPointOnEdge
====================
*/
void InsertPointOnEdge( vec3_t v, edgeLine_t *e ) {
vec3_t delta;
float d;
edgePoint_t *p, *scan;
VectorSubtract( v, e->origin, delta );
d = DotProduct( delta, e->dir );
p = malloc( sizeof(edgePoint_t) );
p->intercept = d;
VectorCopy( v, p->xyz );
if ( e->chain.next == &e->chain ) {
e->chain.next = e->chain.prev = p;
p->next = p->prev = &e->chain;
return;
}
scan = e->chain.next;
for ( ; scan != &e->chain ; scan = scan->next ) {
d = p->intercept - scan->intercept;
if ( d > -LINE_POSITION_EPSILON && d < LINE_POSITION_EPSILON ) {
free( p );
return; // the point is already set
}
if ( p->intercept < scan->intercept ) {
// insert here
p->prev = scan->prev;
p->next = scan;
scan->prev->next = p;
scan->prev = p;
return;
}
}
// add at the end
p->prev = scan->prev;
p->next = scan;
scan->prev->next = p;
scan->prev = p;
}
/*
====================
AddEdge
====================
*/
int AddEdge( vec3_t v1, vec3_t v2, qboolean createNonAxial ) {
int i;
edgeLine_t *e;
float d;
vec3_t dir;
VectorSubtract( v2, v1, dir );
d = VectorNormalize( dir, dir );
if ( d < 0.1 ) {
// if we added a 0 length vector, it would make degenerate planes
c_degenerateEdges++;
return -1;
}
if ( !createNonAxial ) {
if ( fabs( dir[0] + dir[1] + dir[2] ) != 1.0 ) {
if ( numOriginalEdges == MAX_ORIGINAL_EDGES ) {
Error( "MAX_ORIGINAL_EDGES" );
}
originalEdges[ numOriginalEdges ].dv[0] = (drawVert_t *)v1;
originalEdges[ numOriginalEdges ].dv[1] = (drawVert_t *)v2;
originalEdges[ numOriginalEdges ].length = d;
numOriginalEdges++;
return -1;
}
}
for ( i = 0 ; i < numEdgeLines ; i++ ) {
e = &edgeLines[i];
d = DotProduct( v1, e->normal1 ) - e->dist1;
if ( d < -POINT_ON_LINE_EPSILON || d > POINT_ON_LINE_EPSILON ) {
continue;
}
d = DotProduct( v1, e->normal2 ) - e->dist2;
if ( d < -POINT_ON_LINE_EPSILON || d > POINT_ON_LINE_EPSILON ) {
continue;
}
d = DotProduct( v2, e->normal1 ) - e->dist1;
if ( d < -POINT_ON_LINE_EPSILON || d > POINT_ON_LINE_EPSILON ) {
continue;
}
d = DotProduct( v2, e->normal2 ) - e->dist2;
if ( d < -POINT_ON_LINE_EPSILON || d > POINT_ON_LINE_EPSILON ) {
continue;
}
// this is the edge
InsertPointOnEdge( v1, e );
InsertPointOnEdge( v2, e );
return i;
}
// create a new edge
if ( numEdgeLines >= MAX_EDGE_LINES ) {
Error( "MAX_EDGE_LINES" );
}
e = &edgeLines[ numEdgeLines ];
numEdgeLines++;
e->chain.next = e->chain.prev = &e->chain;
VectorCopy( v1, e->origin );
VectorCopy( dir, e->dir );
MakeNormalVectors( e->dir, e->normal1, e->normal2 );
e->dist1 = DotProduct( e->origin, e->normal1 );
e->dist2 = DotProduct( e->origin, e->normal2 );
InsertPointOnEdge( v1, e );
InsertPointOnEdge( v2, e );
return numEdgeLines - 1;
}
/*
====================
AddSurfaceEdges
====================
*/
void AddSurfaceEdges( mapDrawSurface_t *ds ) {
int i;
for ( i = 0 ; i < ds->numVerts ; i++ ) {
// save the edge number in the lightmap field
// so we don't need to look it up again
ds->verts[i].lightmap[0] =
AddEdge( ds->verts[i].xyz, ds->verts[(i+1) % ds->numVerts].xyz, qfalse );
}
}
/*
================
ColinearEdge
================
*/
qboolean ColinearEdge( vec3_t v1, vec3_t v2, vec3_t v3 ) {
vec3_t midpoint, dir, offset, on;
float d;
VectorSubtract( v2, v1, midpoint );
VectorSubtract( v3, v1, dir );
d = VectorNormalize( dir, dir );
if ( d == 0 ) {
return qfalse; // degenerate
}
d = DotProduct( midpoint, dir );
VectorScale( dir, d, on );
VectorSubtract( midpoint, on, offset );
d = VectorLength ( offset );
if ( d < 0.1 ) {
return qtrue;
}
return qfalse;
}
/*
====================
AddPatchEdges
Add colinear border edges, which will fix some classes of patch to
brush tjunctions
====================
*/
void AddPatchEdges( mapDrawSurface_t *ds ) {
int i;
float *v1, *v2, *v3;
for ( i = 0 ; i < ds->patchWidth - 2; i+=2 ) {
v1 = ds->verts[ i ].xyz;
v2 = ds->verts[ i + 1 ].xyz;
v3 = ds->verts[ i + 2 ].xyz;
// if v2 is the midpoint of v1 to v3, add an edge from v1 to v3
if ( ColinearEdge( v1, v2, v3 ) ) {
AddEdge( v1, v3, qfalse );
}
v1 = ds->verts[ ( ds->patchHeight - 1 ) * ds->patchWidth + i ].xyz;
v2 = ds->verts[ ( ds->patchHeight - 1 ) * ds->patchWidth + i + 1 ].xyz;
v3 = ds->verts[ ( ds->patchHeight - 1 ) * ds->patchWidth + i + 2 ].xyz;
// if v2 is on the v1 to v3 line, add an edge from v1 to v3
if ( ColinearEdge( v1, v2, v3 ) ) {
AddEdge( v1, v3, qfalse );
}
}
for ( i = 0 ; i < ds->patchHeight - 2 ; i+=2 ) {
v1 = ds->verts[ i * ds->patchWidth ].xyz;
v2 = ds->verts[ ( i + 1 ) * ds->patchWidth ].xyz;
v3 = ds->verts[ ( i + 2 ) * ds->patchWidth ].xyz;
// if v2 is the midpoint of v1 to v3, add an edge from v1 to v3
if ( ColinearEdge( v1, v2, v3 ) ) {
AddEdge( v1, v3, qfalse );
}
v1 = ds->verts[ ( ds->patchWidth - 1 ) + i * ds->patchWidth ].xyz;
v2 = ds->verts[ ( ds->patchWidth - 1 ) + ( i + 1 ) * ds->patchWidth ].xyz;
v3 = ds->verts[ ( ds->patchWidth - 1 ) + ( i + 2 ) * ds->patchWidth ].xyz;
// if v2 is the midpoint of v1 to v3, add an edge from v1 to v3
if ( ColinearEdge( v1, v2, v3 ) ) {
AddEdge( v1, v3, qfalse );
}
}
}
/*
====================
FixSurfaceJunctions
====================
*/
#define MAX_SURFACE_VERTS 256
void FixSurfaceJunctions( mapDrawSurface_t *ds ) {
int i, j, k;
edgeLine_t *e;
edgePoint_t *p;
int originalVerts;
int counts[MAX_SURFACE_VERTS];
int originals[MAX_SURFACE_VERTS];
int firstVert[MAX_SURFACE_VERTS];
drawVert_t verts[MAX_SURFACE_VERTS], *v1, *v2;
int numVerts;
float start, end, frac;
vec3_t delta;
originalVerts = ds->numVerts;
numVerts = 0;
for ( i = 0 ; i < ds->numVerts ; i++ ) {
counts[i] = 0;
firstVert[i] = numVerts;
// copy first vert
if ( numVerts == MAX_SURFACE_VERTS ) {
Error( "MAX_SURFACE_VERTS" );
}
verts[numVerts] = ds->verts[i];
originals[numVerts] = i;
numVerts++;
// check to see if there are any t junctions before the next vert
v1 = &ds->verts[i];
v2 = &ds->verts[ (i+1) % ds->numVerts ];
j = (int)ds->verts[i].lightmap[0];
if ( j == -1 ) {
continue; // degenerate edge
}
e = &edgeLines[ j ];
VectorSubtract( v1->xyz, e->origin, delta );
start = DotProduct( delta, e->dir );
VectorSubtract( v2->xyz, e->origin, delta );
end = DotProduct( delta, e->dir );
if ( start < end ) {
p = e->chain.next;
} else {
p = e->chain.prev;
}
for ( ; p != &e->chain ; ) {
if ( start < end ) {
if ( p->intercept > end - ON_EPSILON ) {
break;
}
} else {
if ( p->intercept < end + ON_EPSILON ) {
break;
}
}
if (
( start < end && p->intercept > start + ON_EPSILON ) ||
( start > end && p->intercept < start - ON_EPSILON ) ) {
// insert this point
if ( numVerts == MAX_SURFACE_VERTS ) {
Error( "MAX_SURFACE_VERTS" );
}
// take the exact intercept point
VectorCopy( p->xyz, verts[ numVerts ].xyz );
// copy the normal
VectorCopy( v1->normal, verts[ numVerts ].normal );
// interpolate the texture coordinates
frac = ( p->intercept - start ) / ( end - start );
for ( j = 0 ; j < 2 ; j++ ) {
verts[ numVerts ].st[j] = v1->st[j] +
frac * ( v2->st[j] - v1->st[j] );
}
originals[numVerts] = i;
numVerts++;
counts[i]++;
}
if ( start < end ) {
p = p->next;
} else {
p = p->prev;
}
}
}
c_addedVerts += numVerts - ds->numVerts;
c_totalVerts += numVerts;
// FIXME: check to see if the entire surface degenerated
// after snapping
// rotate the points so that the initial vertex is between
// two non-subdivided edges
for ( i = 0 ; i < numVerts ; i++ ) {
if ( originals[ (i+1) % numVerts ] == originals[ i ] ) {
continue;
}
j = (i + numVerts - 1 ) % numVerts;
k = (i + numVerts - 2 ) % numVerts;
if ( originals[ j ] == originals[ k ] ) {
continue;
}
break;
}
if ( i == 0 ) {
// fine the way it is
c_natural++;
ds->numVerts = numVerts;
ds->verts = malloc( numVerts * sizeof( *ds->verts ) );
memcpy( ds->verts, verts, numVerts * sizeof( *ds->verts ) );
return;
}
if ( i == numVerts ) {
// create a vertex in the middle to start the fan
c_cant++;
/*
memset ( &verts[numVerts], 0, sizeof( verts[numVerts] ) );
for ( i = 0 ; i < numVerts ; i++ ) {
for ( j = 0 ; j < 10 ; j++ ) {
verts[numVerts].xyz[j] += verts[i].xyz[j];
}
}
for ( j = 0 ; j < 10 ; j++ ) {
verts[numVerts].xyz[j] /= numVerts;
}
i = numVerts;
numVerts++;
*/
} else {
// just rotate the vertexes
c_rotate++;
}
ds->numVerts = numVerts;
ds->verts = malloc( numVerts * sizeof( *ds->verts ) );
for ( j = 0 ; j < ds->numVerts ; j++ ) {
ds->verts[j] = verts[ ( j + i ) % ds->numVerts ];
}
}
/*
================
EdgeCompare
================
*/
int EdgeCompare( const void *elem1, const void *elem2 ) {
float d1, d2;
d1 = ((originalEdge_t *)elem1)->length;
d2 = ((originalEdge_t *)elem2)->length;
if ( d1 < d2 ) {
return -1;
}
if ( d2 > d1 ) {
return 1;
}
return 0;
}
/*
================
FixTJunctions
Call after the surface list has been pruned, but before lightmap allocation
================
*/
void FixTJunctions( entity_t *ent ) {
int i;
mapDrawSurface_t *ds;
int axialEdgeLines;
originalEdge_t *e;
qprintf("----- FixTJunctions -----\n");
numEdgeLines = 0;
numOriginalEdges = 0;
// add all the edges
// this actually creates axial edges, but it
// only creates originalEdge_t structures
// for non-axial edges
for ( i = ent->firstDrawSurf ; i < numMapDrawSurfs ; i++ ) {
ds = &mapDrawSurfs[i];
if ( ds->patch ) {
AddPatchEdges( ds );
} else if ( ds->shaderInfo->autosprite || ds->shaderInfo->notjunc || ds->miscModel ) {
// miscModels don't add tjunctions
} else {
AddSurfaceEdges( ds );
}
}
axialEdgeLines = numEdgeLines;
// sort the non-axial edges by length
qsort( originalEdges, numOriginalEdges, sizeof(originalEdges[0]), EdgeCompare );
// add the non-axial edges, longest first
// this gives the most accurate edge description
for ( i = 0 ; i < numOriginalEdges ; i++ ) {
e = &originalEdges[i];
e->dv[0]->lightmap[0] = AddEdge( e->dv[0]->xyz, e->dv[1]->xyz, qtrue );
}
qprintf( "%6i axial edge lines\n", axialEdgeLines );
qprintf( "%6i non-axial edge lines\n", numEdgeLines - axialEdgeLines );
qprintf( "%6i degenerate edges\n", c_degenerateEdges );
// insert any needed vertexes
for ( i = ent->firstDrawSurf ; i < numMapDrawSurfs ; i++ ) {
ds = &mapDrawSurfs[i];
if ( ds->patch ) {
continue;
}
if ( ds->shaderInfo->autosprite || ds->shaderInfo->notjunc || ds->miscModel ) {
continue;
}
FixSurfaceJunctions( ds );
}
qprintf( "%6i verts added for tjunctions\n", c_addedVerts );
qprintf( "%6i total verts\n", c_totalVerts );
qprintf( "%6i naturally ordered\n", c_natural );
qprintf( "%6i rotated orders\n", c_rotate );
qprintf( "%6i can't order\n", c_cant );
}

146
q3map/tree.c Normal file
View file

@ -0,0 +1,146 @@
/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
This file is part of Quake III Arena source code.
Quake III Arena source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
Quake III Arena source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Foobar; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
#include "qbsp.h"
extern int c_nodes;
void RemovePortalFromNode (portal_t *portal, node_t *l);
node_t *NodeForPoint (node_t *node, vec3_t origin)
{
plane_t *plane;
vec_t d;
while (node->planenum != PLANENUM_LEAF)
{
plane = &mapplanes[node->planenum];
d = DotProduct (origin, plane->normal) - plane->dist;
if (d >= 0)
node = node->children[0];
else
node = node->children[1];
}
return node;
}
/*
=============
FreeTreePortals_r
=============
*/
void FreeTreePortals_r (node_t *node)
{
portal_t *p, *nextp;
int s;
// free children
if (node->planenum != PLANENUM_LEAF)
{
FreeTreePortals_r (node->children[0]);
FreeTreePortals_r (node->children[1]);
}
// free portals
for (p=node->portals ; p ; p=nextp)
{
s = (p->nodes[1] == node);
nextp = p->next[s];
RemovePortalFromNode (p, p->nodes[!s]);
FreePortal (p);
}
node->portals = NULL;
}
/*
=============
FreeTree_r
=============
*/
void FreeTree_r (node_t *node)
{
// free children
if (node->planenum != PLANENUM_LEAF)
{
FreeTree_r (node->children[0]);
FreeTree_r (node->children[1]);
}
// free bspbrushes
FreeBrushList (node->brushlist);
// free the node
if (node->volume)
FreeBrush (node->volume);
if (numthreads == 1)
c_nodes--;
free (node);
}
/*
=============
FreeTree
=============
*/
void FreeTree (tree_t *tree)
{
FreeTreePortals_r (tree->headnode);
FreeTree_r (tree->headnode);
free (tree);
}
//===============================================================
void PrintTree_r (node_t *node, int depth)
{
int i;
plane_t *plane;
bspbrush_t *bb;
for (i=0 ; i<depth ; i++)
_printf (" ");
if (node->planenum == PLANENUM_LEAF)
{
if (!node->brushlist)
_printf ("NULL\n");
else
{
for (bb=node->brushlist ; bb ; bb=bb->next)
_printf ("%i ", bb->original->brushnum);
_printf ("\n");
}
return;
}
plane = &mapplanes[node->planenum];
_printf ("#%i (%5.2f %5.2f %5.2f):%5.2f\n", node->planenum,
plane->normal[0], plane->normal[1], plane->normal[2],
plane->dist);
PrintTree_r (node->children[0], depth+1);
PrintTree_r (node->children[1], depth+1);
}

1197
q3map/vis.c Normal file

File diff suppressed because it is too large Load diff

162
q3map/vis.h Normal file
View file

@ -0,0 +1,162 @@
/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
This file is part of Quake III Arena source code.
Quake III Arena source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
Quake III Arena source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Foobar; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
// vis.h
#include "cmdlib.h"
#include "mathlib.h"
#include "bspfile.h"
#define MAX_PORTALS 32768
#define PORTALFILE "PRT1"
#define ON_EPSILON 0.1
//#define MREDEBUG
// seperator caching helps a bit
#define SEPERATORCACHE
// can't have more seperators than the max number of points on a winding
#define MAX_SEPERATORS 64
typedef struct
{
vec3_t normal;
float dist;
} plane_t;
#define MAX_POINTS_ON_WINDING 64
#define MAX_POINTS_ON_FIXED_WINDING 12
typedef struct
{
int numpoints;
vec3_t points[MAX_POINTS_ON_FIXED_WINDING]; // variable sized
} winding_t;
winding_t *NewWinding (int points);
void FreeWinding (winding_t *w);
winding_t *CopyWinding (winding_t *w);
typedef struct passage_s
{
struct passage_s *next;
byte cansee[1]; //all portals that can be seen through this passage
} passage_t;
typedef enum {stat_none, stat_working, stat_done} vstatus_t;
typedef struct
{
int num;
qboolean hint; // true if this portal was created from a hint splitter
qboolean removed;
plane_t plane; // normal pointing into neighbor
int leaf; // neighbor
vec3_t origin; // for fast clip testing
float radius;
winding_t *winding;
vstatus_t status;
byte *portalfront; // [portals], preliminary
byte *portalflood; // [portals], intermediate
byte *portalvis; // [portals], final
int nummightsee; // bit count on portalflood for sort
passage_t *passages; // there are just as many passages as there
// are portals in the leaf this portal leads to
} vportal_t;
#define MAX_PORTALS_ON_LEAF 128
typedef struct leaf_s
{
int numportals;
int merged;
vportal_t *portals[MAX_PORTALS_ON_LEAF];
} leaf_t;
typedef struct pstack_s
{
byte mightsee[MAX_PORTALS/8]; // bit string
struct pstack_s *next;
leaf_t *leaf;
vportal_t *portal; // portal exiting
winding_t *source;
winding_t *pass;
winding_t windings[3]; // source, pass, temp in any order
int freewindings[3];
plane_t portalplane;
int depth;
#ifdef SEPERATORCACHE
plane_t seperators[2][MAX_SEPERATORS];
int numseperators[2];
#endif
} pstack_t;
typedef struct
{
vportal_t *base;
int c_chains;
pstack_t pstack_head;
} threaddata_t;
extern int numportals;
extern int portalclusters;
extern vportal_t *portals;
extern leaf_t *leafs;
extern int c_portaltest, c_portalpass, c_portalcheck;
extern int c_portalskip, c_leafskip;
extern int c_vistest, c_mighttest;
extern int c_chains;
extern byte *vismap, *vismap_p, *vismap_end; // past visfile
extern int testlevel;
extern byte *uncompressed;
extern int leafbytes, leaflongs;
extern int portalbytes, portallongs;
void LeafFlow (int leafnum);
void BasePortalVis(int portalnum);
void BetterPortalVis(int portalnum);
void PortalFlow(int portalnum);
void PassagePortalFlow(int portalnum);
void CreatePassages(int portalnum);
void PassageFlow(int portalnum);
extern vportal_t *sorted_portals[MAX_MAP_PORTALS*2];
int CountBits (byte *bits, int numbits);

1657
q3map/visflow.c Normal file

File diff suppressed because it is too large Load diff

418
q3map/writebsp.c Normal file
View file

@ -0,0 +1,418 @@
/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
This file is part of Quake III Arena source code.
Quake III Arena source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
Quake III Arena source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Foobar; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
#include "qbsp.h"
/*
============
EmitShader
============
*/
int EmitShader( const char *shader ) {
int i;
shaderInfo_t *si;
if ( !shader ) {
shader = "noshader";
}
for ( i = 0 ; i < numShaders ; i++ ) {
if ( !Q_stricmp( shader, dshaders[i].shader ) ) {
return i;
}
}
if ( i == MAX_MAP_SHADERS ) {
Error( "MAX_MAP_SHADERS" );
}
numShaders++;
strcpy( dshaders[i].shader, shader );
si = ShaderInfoForShader( shader );
dshaders[i].surfaceFlags = si->surfaceFlags;
dshaders[i].contentFlags = si->contents;
return i;
}
/*
============
EmitPlanes
There is no oportunity to discard planes, because all of the original
brushes will be saved in the map.
============
*/
void EmitPlanes (void)
{
int i;
dplane_t *dp;
plane_t *mp;
mp = mapplanes;
for (i=0 ; i<nummapplanes ; i++, mp++)
{
dp = &dplanes[numplanes];
VectorCopy ( mp->normal, dp->normal);
dp->dist = mp->dist;
numplanes++;
}
}
/*
==================
EmitLeaf
==================
*/
void EmitLeaf (node_t *node)
{
dleaf_t *leaf_p;
bspbrush_t *b;
drawSurfRef_t *dsr;
// emit a leaf
if (numleafs >= MAX_MAP_LEAFS)
Error ("MAX_MAP_LEAFS");
leaf_p = &dleafs[numleafs];
numleafs++;
leaf_p->cluster = node->cluster;
leaf_p->area = node->area;
//
// write bounding box info
//
VectorCopy (node->mins, leaf_p->mins);
VectorCopy (node->maxs, leaf_p->maxs);
//
// write the leafbrushes
//
leaf_p->firstLeafBrush = numleafbrushes;
for ( b = node->brushlist ; b ; b = b->next ) {
if ( numleafbrushes >= MAX_MAP_LEAFBRUSHES ) {
Error( "MAX_MAP_LEAFBRUSHES" );
}
dleafbrushes[numleafbrushes] = b->original->outputNumber;
numleafbrushes++;
}
leaf_p->numLeafBrushes = numleafbrushes - leaf_p->firstLeafBrush;
//
// write the surfaces visible in this leaf
//
if ( node->opaque ) {
return; // no leaffaces in solids
}
// add the drawSurfRef_t drawsurfs
leaf_p->firstLeafSurface = numleafsurfaces;
for ( dsr = node->drawSurfReferences ; dsr ; dsr = dsr->nextRef ) {
if ( numleafsurfaces >= MAX_MAP_LEAFFACES)
Error ("MAX_MAP_LEAFFACES");
dleafsurfaces[numleafsurfaces] = dsr->outputNumber;
numleafsurfaces++;
}
leaf_p->numLeafSurfaces = numleafsurfaces - leaf_p->firstLeafSurface;
}
/*
============
EmitDrawNode_r
============
*/
int EmitDrawNode_r (node_t *node)
{
dnode_t *n;
int i;
if (node->planenum == PLANENUM_LEAF)
{
EmitLeaf (node);
return -numleafs;
}
// emit a node
if (numnodes == MAX_MAP_NODES)
Error ("MAX_MAP_NODES");
n = &dnodes[numnodes];
numnodes++;
VectorCopy (node->mins, n->mins);
VectorCopy (node->maxs, n->maxs);
if (node->planenum & 1)
Error ("WriteDrawNodes_r: odd planenum");
n->planeNum = node->planenum;
//
// recursively output the other nodes
//
for (i=0 ; i<2 ; i++)
{
if (node->children[i]->planenum == PLANENUM_LEAF)
{
n->children[i] = -(numleafs + 1);
EmitLeaf (node->children[i]);
}
else
{
n->children[i] = numnodes;
EmitDrawNode_r (node->children[i]);
}
}
return n - dnodes;
}
//=========================================================
/*
============
SetModelNumbers
============
*/
void SetModelNumbers (void)
{
int i;
int models;
char value[10];
models = 1;
for ( i=1 ; i<num_entities ; i++ ) {
if ( entities[i].brushes || entities[i].patches ) {
sprintf ( value, "*%i", models );
models++;
SetKeyValue (&entities[i], "model", value);
}
}
}
/*
============
SetLightStyles
============
*/
#define MAX_SWITCHED_LIGHTS 32
void SetLightStyles (void)
{
int stylenum;
const char *t;
entity_t *e;
int i, j;
char value[10];
char lighttargets[MAX_SWITCHED_LIGHTS][64];
// any light that is controlled (has a targetname)
// must have a unique style number generated for it
stylenum = 0;
for (i=1 ; i<num_entities ; i++)
{
e = &entities[i];
t = ValueForKey (e, "classname");
if (Q_strncasecmp (t, "light", 5))
continue;
t = ValueForKey (e, "targetname");
if (!t[0])
continue;
// find this targetname
for (j=0 ; j<stylenum ; j++)
if (!strcmp (lighttargets[j], t))
break;
if (j == stylenum)
{
if (stylenum == MAX_SWITCHED_LIGHTS)
Error ("stylenum == MAX_SWITCHED_LIGHTS");
strcpy (lighttargets[j], t);
stylenum++;
}
sprintf (value, "%i", 32 + j);
SetKeyValue (e, "style", value);
}
}
//===========================================================
/*
==================
BeginBSPFile
==================
*/
void BeginBSPFile( void ) {
// these values may actually be initialized
// if the file existed when loaded, so clear them explicitly
nummodels = 0;
numnodes = 0;
numbrushsides = 0;
numleafsurfaces = 0;
numleafbrushes = 0;
// leave leaf 0 as an error, because leafs are referenced as
// negative number nodes
numleafs = 1;
}
/*
============
EndBSPFile
============
*/
void EndBSPFile( void ) {
char path[1024];
EmitPlanes ();
UnparseEntities ();
// write the map
sprintf (path, "%s.bsp", source);
_printf ("Writing %s\n", path);
WriteBSPFile (path);
}
//===========================================================
/*
============
EmitBrushes
============
*/
void EmitBrushes ( bspbrush_t *brushes ) {
int j;
dbrush_t *db;
bspbrush_t *b;
dbrushside_t *cp;
for ( b = brushes ; b ; b = b->next ) {
if ( numbrushes == MAX_MAP_BRUSHES ) {
Error( "MAX_MAP_BRUSHES" );
}
b->outputNumber = numbrushes;
db = &dbrushes[numbrushes];
numbrushes++;
db->shaderNum = EmitShader( b->contentShader->shader );
db->firstSide = numbrushsides;
// don't emit any generated backSide sides
db->numSides = 0;
for ( j=0 ; j<b->numsides ; j++ ) {
if ( b->sides[j].backSide ) {
continue;
}
if ( numbrushsides == MAX_MAP_BRUSHSIDES ) {
Error( "MAX_MAP_BRUSHSIDES ");
}
cp = &dbrushsides[numbrushsides];
db->numSides++;
numbrushsides++;
cp->planeNum = b->sides[j].planenum;
cp->shaderNum = EmitShader( b->sides[j].shaderInfo->shader );
}
}
}
/*
==================
BeginModel
==================
*/
void BeginModel( void ) {
dmodel_t *mod;
bspbrush_t *b;
entity_t *e;
vec3_t mins, maxs;
parseMesh_t *p;
int i;
if ( nummodels == MAX_MAP_MODELS ) {
Error( "MAX_MAP_MODELS" );
}
mod = &dmodels[nummodels];
//
// bound the brushes
//
e = &entities[entity_num];
ClearBounds (mins, maxs);
for ( b = e->brushes ; b ; b = b->next ) {
if ( !b->numsides ) {
continue; // not a real brush (origin brush, etc)
}
AddPointToBounds (b->mins, mins, maxs);
AddPointToBounds (b->maxs, mins, maxs);
}
for ( p = e->patches ; p ; p = p->next ) {
for ( i = 0 ; i < p->mesh.width * p->mesh.height ; i++ ) {
AddPointToBounds( p->mesh.verts[i].xyz, mins, maxs );
}
}
VectorCopy (mins, mod->mins);
VectorCopy (maxs, mod->maxs);
mod->firstSurface = numDrawSurfaces;
mod->firstBrush = numbrushes;
EmitBrushes( e->brushes );
}
/*
==================
EndModel
==================
*/
void EndModel( node_t *headnode ) {
dmodel_t *mod;
qprintf ("--- EndModel ---\n");
mod = &dmodels[nummodels];
EmitDrawNode_r (headnode);
mod->numSurfaces = numDrawSurfaces - mod->firstSurface;
mod->numBrushes = numbrushes - mod->firstBrush;
nummodels++;
}