The GtkRadiant sources as originally released under the GPL license.

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

View File

@@ -0,0 +1,965 @@
/*
Copyright (C) 1999-2006 Id Software, Inc. and contributors.
For a list of contributors, see the accompanying CONTRIBUTORS file.
This file is part of GtkRadiant.
GtkRadiant is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
GtkRadiant is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "aselib.h"
#include "inout.h"
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#define MAX_ASE_MATERIALS 32
#define MAX_ASE_OBJECTS 64
#define MAX_ASE_ANIMATIONS 32
#define MAX_ASE_ANIMATION_FRAMES 512
#define VERBOSE( x ) { if ( ase.verbose ) { Sys_Printf x ; } }
typedef struct
{
float x, y, z;
float nx, ny, nz;
float s, t;
} aseVertex_t;
typedef struct
{
float s, t;
} aseTVertex_t;
typedef int aseFace_t[3];
typedef struct
{
int numFaces;
int numVertexes;
int numTVertexes;
int timeValue;
aseVertex_t *vertexes;
aseTVertex_t *tvertexes;
aseFace_t *faces, *tfaces;
int currentFace, currentVertex;
} aseMesh_t;
typedef struct
{
int numFrames;
aseMesh_t frames[MAX_ASE_ANIMATION_FRAMES];
int currentFrame;
} aseMeshAnimation_t;
typedef struct
{
char name[128];
} aseMaterial_t;
/*
** contains the animate sequence of a single surface
** using a single material
*/
typedef struct
{
char name[128];
int materialRef;
int numAnimations;
aseMeshAnimation_t anim;
} aseGeomObject_t;
typedef struct
{
int numMaterials;
aseMaterial_t materials[MAX_ASE_MATERIALS];
aseGeomObject_t objects[MAX_ASE_OBJECTS];
char *buffer;
char *curpos;
int len;
int currentObject;
qboolean verbose;
qboolean grabAnims;
} ase_t;
static char s_token[1024];
static ase_t ase;
static char gl_filename[1024];
static void ASE_Process( void );
static void ASE_FreeGeomObject( int ndx );
#if defined (__linux__) || defined (__APPLE__)
static char* strlwr (char* string)
{
char *cp;
for (cp = string; *cp; ++cp)
{
if ('A' <= *cp && *cp <= 'Z')
*cp += 'a' - 'A';
}
return string;
}
#endif
/*
** ASE_Load
*/
void ASE_Load( const char *filename, qboolean verbose, qboolean grabAnims )
{
FILE *fp = fopen( filename, "rb" );
if ( !fp )
Error( "File not found '%s'", filename );
memset( &ase, 0, sizeof( ase ) );
ase.verbose = verbose;
ase.grabAnims = grabAnims;
ase.len = Q_filelength( fp );
ase.curpos = ase.buffer = safe_malloc( ase.len );
Sys_Printf( "Processing '%s'\n", filename );
if ( fread( ase.buffer, ase.len, 1, fp ) != 1 )
{
fclose( fp );
Error( "fread() != -1 for '%s'", filename );
}
fclose( fp );
strcpy(gl_filename, filename);
ASE_Process();
}
/*
** ASE_Free
*/
void ASE_Free( void )
{
int i;
for ( i = 0; i < ase.currentObject; i++ )
{
ASE_FreeGeomObject( i );
}
}
/*
** ASE_GetNumSurfaces
*/
int ASE_GetNumSurfaces( void )
{
return ase.currentObject;
}
/*
** ASE_GetSurfaceName
*/
const char *ASE_GetSurfaceName( int which )
{
aseGeomObject_t *pObject = &ase.objects[which];
if ( !pObject->anim.numFrames )
return 0;
return pObject->name;
}
/*
** ASE_GetSurfaceAnimation
**
** Returns an animation (sequence of polysets)
*/
polyset_t *ASE_GetSurfaceAnimation( int which, int *pNumFrames, int skipFrameStart, int skipFrameEnd, int maxFrames )
{
aseGeomObject_t *pObject = &ase.objects[which];
polyset_t *psets;
int numFramesInAnimation;
int numFramesToKeep;
int i, f;
if ( !pObject->anim.numFrames )
return 0;
if ( pObject->anim.numFrames > maxFrames && maxFrames != -1 )
{
numFramesInAnimation = maxFrames;
}
else
{
numFramesInAnimation = pObject->anim.numFrames;
if ( maxFrames != -1 )
Sys_Printf( "WARNING: ASE_GetSurfaceAnimation maxFrames > numFramesInAnimation\n" );
}
if ( skipFrameEnd != -1 )
numFramesToKeep = numFramesInAnimation - ( skipFrameEnd - skipFrameStart + 1 );
else
numFramesToKeep = numFramesInAnimation;
*pNumFrames = numFramesToKeep;
psets = calloc( sizeof( polyset_t ) * numFramesToKeep, 1 );
for ( f = 0, i = 0; i < numFramesInAnimation; i++ )
{
int t;
aseMesh_t *pMesh = &pObject->anim.frames[i];
if ( skipFrameStart != -1 )
{
if ( i >= skipFrameStart && i <= skipFrameEnd )
continue;
}
strcpy( psets[f].name, pObject->name );
strcpy( psets[f].materialname, ase.materials[pObject->materialRef].name );
psets[f].triangles = calloc( sizeof( triangle_t ) * pObject->anim.frames[i].numFaces, 1 );
psets[f].numtriangles = pObject->anim.frames[i].numFaces;
for ( t = 0; t < pObject->anim.frames[i].numFaces; t++ )
{
int k;
for ( k = 0; k < 3; k++ )
{
psets[f].triangles[t].verts[k][0] = pMesh->vertexes[pMesh->faces[t][k]].x;
psets[f].triangles[t].verts[k][1] = pMesh->vertexes[pMesh->faces[t][k]].y;
psets[f].triangles[t].verts[k][2] = pMesh->vertexes[pMesh->faces[t][k]].z;
if ( pMesh->tvertexes && pMesh->tfaces )
{
psets[f].triangles[t].texcoords[k][0] = pMesh->tvertexes[pMesh->tfaces[t][k]].s;
psets[f].triangles[t].texcoords[k][1] = pMesh->tvertexes[pMesh->tfaces[t][k]].t;
}
}
}
f++;
}
return psets;
}
static void ASE_FreeGeomObject( int ndx )
{
aseGeomObject_t *pObject;
int i;
pObject = &ase.objects[ndx];
for ( i = 0; i < pObject->anim.numFrames; i++ )
{
if ( pObject->anim.frames[i].vertexes )
{
free( pObject->anim.frames[i].vertexes );
}
if ( pObject->anim.frames[i].tvertexes )
{
free( pObject->anim.frames[i].tvertexes );
}
if ( pObject->anim.frames[i].faces )
{
free( pObject->anim.frames[i].faces );
}
if ( pObject->anim.frames[i].tfaces )
{
free( pObject->anim.frames[i].tfaces );
}
}
memset( pObject, 0, sizeof( *pObject ) );
}
static aseMesh_t *ASE_GetCurrentMesh( void )
{
aseGeomObject_t *pObject;
if ( ase.currentObject >= MAX_ASE_OBJECTS )
{
Error( "Too many GEOMOBJECTs" );
return 0; // never called
}
pObject = &ase.objects[ase.currentObject];
if ( pObject->anim.currentFrame >= MAX_ASE_ANIMATION_FRAMES )
{
Error( "Too many MESHes" );
return 0;
}
return &pObject->anim.frames[pObject->anim.currentFrame];
}
static int CharIsTokenDelimiter( int ch )
{
if ( ch <= 32 )
return 1;
return 0;
}
static int ASE_GetToken( qboolean restOfLine )
{
int i = 0;
if ( ase.buffer == 0 )
return 0;
if ( ( ase.curpos - ase.buffer ) == ase.len )
return 0;
// skip over crap
while ( ( ( ase.curpos - ase.buffer ) < ase.len ) &&
( *ase.curpos <= 32 ) )
{
ase.curpos++;
}
while ( ( ase.curpos - ase.buffer ) < ase.len )
{
s_token[i] = *ase.curpos;
ase.curpos++;
i++;
if ( ( CharIsTokenDelimiter( s_token[i-1] ) && !restOfLine ) ||
( ( s_token[i-1] == '\n' ) || ( s_token[i-1] == '\r' ) ) )
{
s_token[i-1] = 0;
break;
}
}
s_token[i] = 0;
return 1;
}
static void ASE_ParseBracedBlock( void (*parser)( const char *token ) )
{
int indent = 0;
while ( ASE_GetToken( qfalse ) )
{
if ( !strcmp( s_token, "{" ) )
{
indent++;
}
else if ( !strcmp( s_token, "}" ) )
{
--indent;
if ( indent == 0 )
break;
else if ( indent < 0 )
Error( "Unexpected '}'" );
}
else
{
if ( parser )
parser( s_token );
}
}
}
static void ASE_SkipEnclosingBraces( void )
{
int indent = 0;
while ( ASE_GetToken( qfalse ) )
{
if ( !strcmp( s_token, "{" ) )
{
indent++;
}
else if ( !strcmp( s_token, "}" ) )
{
indent--;
if ( indent == 0 )
break;
else if ( indent < 0 )
Error( "Unexpected '}'" );
}
}
}
static void ASE_SkipRestOfLine( void )
{
ASE_GetToken( qtrue );
}
static void ASE_KeyMAP_DIFFUSE( const char *token )
{
char fullpath[1024], bitmap[1024], modeldir[1024];
char filename[1024];
int i = 0, count;
strcpy(filename, gl_filename);
if ( !strcmp( token, "*BITMAP" ) )
{
ASE_GetToken( qfalse );
// the purpose of this whole chunk of code below is to extract the relative path
// from a full path in the ASE
strcpy( bitmap, s_token + 1 );
if ( strchr( bitmap, '"' ) )
*strchr( bitmap, '"' ) = 0;
/* convert backslash to slash */
while ( bitmap[i] )
{
if ( bitmap[i] == '\\' )
bitmap[i] = '/';
i++;
}
/* remove filename from path */
for( i=strlen(filename); i>0; i--)
{
if(filename[i] == '/')
{
filename[i] = '\0';
break;
}
}
/* replaces a relative path with a full path */
if(bitmap[0] == '.' && bitmap[1] == '.' && bitmap[2] == '/')
{
while(bitmap[0] == '.' && bitmap[1] == '.' && bitmap[2] == '/')
{
/* remove last item from path */
for( i=strlen(filename); i>0; i--)
{
if(filename[i] == '/')
{
filename[i] = '\0';
break;
}
}
strcpy(bitmap, &bitmap[3]);
}
strcat(filename, "/");
strcat(filename, bitmap);
strcpy(bitmap, filename);
}
if ( strstr( bitmap, gamedir ) )
{
strcpy( ase.materials[ase.numMaterials].name, strstr( bitmap, gamedir ) + strlen( gamedir ) );
Sys_Printf("material name: \'%s\'\n", strstr( bitmap, gamedir ) + strlen( gamedir ) );
}
else
{
sprintf( ase.materials[ase.numMaterials].name, "(not converted: '%s')", bitmap );
Sys_Printf( "WARNING: illegal material name '%s'\n", bitmap );
}
}
else
{
}
}
static void ASE_KeyMATERIAL( const char *token )
{
if ( !strcmp( token, "*MAP_DIFFUSE" ) )
{
ASE_ParseBracedBlock( ASE_KeyMAP_DIFFUSE );
}
else
{
}
}
static void ASE_KeyMATERIAL_LIST( const char *token )
{
if ( !strcmp( token, "*MATERIAL_COUNT" ) )
{
ASE_GetToken( qfalse );
VERBOSE( ( "..num materials: %s\n", s_token ) );
if ( atoi( s_token ) > MAX_ASE_MATERIALS )
{
Error( "Too many materials!" );
}
ase.numMaterials = 0;
}
else if ( !strcmp( token, "*MATERIAL" ) )
{
VERBOSE( ( "..material %d ", ase.numMaterials ) );
ASE_ParseBracedBlock( ASE_KeyMATERIAL );
ase.numMaterials++;
}
}
static void ASE_KeyMESH_VERTEX_LIST( const char *token )
{
aseMesh_t *pMesh = ASE_GetCurrentMesh();
if ( !strcmp( token, "*MESH_VERTEX" ) )
{
ASE_GetToken( qfalse ); // skip number
ASE_GetToken( qfalse );
pMesh->vertexes[pMesh->currentVertex].y = atof( s_token );
ASE_GetToken( qfalse );
pMesh->vertexes[pMesh->currentVertex].x = -atof( s_token );
ASE_GetToken( qfalse );
pMesh->vertexes[pMesh->currentVertex].z = atof( s_token );
pMesh->currentVertex++;
if ( pMesh->currentVertex > pMesh->numVertexes )
{
Error( "pMesh->currentVertex >= pMesh->numVertexes" );
}
}
else
{
Error( "Unknown token '%s' while parsing MESH_VERTEX_LIST", token );
}
}
static void ASE_KeyMESH_FACE_LIST( const char *token )
{
aseMesh_t *pMesh = ASE_GetCurrentMesh();
if ( !strcmp( token, "*MESH_FACE" ) )
{
ASE_GetToken( qfalse ); // skip face number
ASE_GetToken( qfalse ); // skip label
ASE_GetToken( qfalse ); // first vertex
pMesh->faces[pMesh->currentFace][0] = atoi( s_token );
ASE_GetToken( qfalse ); // skip label
ASE_GetToken( qfalse ); // second vertex
pMesh->faces[pMesh->currentFace][2] = atoi( s_token );
ASE_GetToken( qfalse ); // skip label
ASE_GetToken( qfalse ); // third vertex
pMesh->faces[pMesh->currentFace][1] = atoi( s_token );
ASE_GetToken( qtrue );
/*
if ( ( p = strstr( s_token, "*MESH_MTLID" ) ) != 0 )
{
p += strlen( "*MESH_MTLID" ) + 1;
mtlID = atoi( p );
}
else
{
Error( "No *MESH_MTLID found for face!" );
}
*/
pMesh->currentFace++;
}
else
{
Error( "Unknown token '%s' while parsing MESH_FACE_LIST", token );
}
}
static void ASE_KeyTFACE_LIST( const char *token )
{
aseMesh_t *pMesh = ASE_GetCurrentMesh();
if ( !strcmp( token, "*MESH_TFACE" ) )
{
int a, b, c;
ASE_GetToken( qfalse );
ASE_GetToken( qfalse );
a = atoi( s_token );
ASE_GetToken( qfalse );
c = atoi( s_token );
ASE_GetToken( qfalse );
b = atoi( s_token );
pMesh->tfaces[pMesh->currentFace][0] = a;
pMesh->tfaces[pMesh->currentFace][1] = b;
pMesh->tfaces[pMesh->currentFace][2] = c;
pMesh->currentFace++;
}
else
{
Error( "Unknown token '%s' in MESH_TFACE", token );
}
}
static void ASE_KeyMESH_TVERTLIST( const char *token )
{
aseMesh_t *pMesh = ASE_GetCurrentMesh();
if ( !strcmp( token, "*MESH_TVERT" ) )
{
char u[80], v[80], w[80];
ASE_GetToken( qfalse );
ASE_GetToken( qfalse );
strcpy( u, s_token );
ASE_GetToken( qfalse );
strcpy( v, s_token );
ASE_GetToken( qfalse );
strcpy( w, s_token );
pMesh->tvertexes[pMesh->currentVertex].s = atof( u );
pMesh->tvertexes[pMesh->currentVertex].t = 1.0f - atof( v );
pMesh->currentVertex++;
if ( pMesh->currentVertex > pMesh->numTVertexes )
{
Error( "pMesh->currentVertex > pMesh->numTVertexes" );
}
}
else
{
Error( "Unknown token '%s' while parsing MESH_TVERTLIST" );
}
}
static void ASE_KeyMESH( const char *token )
{
aseMesh_t *pMesh = ASE_GetCurrentMesh();
if ( !strcmp( token, "*TIMEVALUE" ) )
{
ASE_GetToken( qfalse );
pMesh->timeValue = atoi( s_token );
VERBOSE( ( ".....timevalue: %d\n", pMesh->timeValue ) );
}
else if ( !strcmp( token, "*MESH_NUMVERTEX" ) )
{
ASE_GetToken( qfalse );
pMesh->numVertexes = atoi( s_token );
VERBOSE( ( ".....TIMEVALUE: %d\n", pMesh->timeValue ) );
VERBOSE( ( ".....num vertexes: %d\n", pMesh->numVertexes ) );
}
else if ( !strcmp( token, "*MESH_NUMFACES" ) )
{
ASE_GetToken( qfalse );
pMesh->numFaces = atoi( s_token );
VERBOSE( ( ".....num faces: %d\n", pMesh->numFaces ) );
}
else if ( !strcmp( token, "*MESH_NUMTVFACES" ) )
{
ASE_GetToken( qfalse );
if ( atoi( s_token ) != pMesh->numFaces )
{
Error( "MESH_NUMTVFACES != MESH_NUMFACES" );
}
}
else if ( !strcmp( token, "*MESH_NUMTVERTEX" ) )
{
ASE_GetToken( qfalse );
pMesh->numTVertexes = atoi( s_token );
VERBOSE( ( ".....num tvertexes: %d\n", pMesh->numTVertexes ) );
}
else if ( !strcmp( token, "*MESH_VERTEX_LIST" ) )
{
pMesh->vertexes = calloc( sizeof( aseVertex_t ) * pMesh->numVertexes, 1 );
pMesh->currentVertex = 0;
VERBOSE( ( ".....parsing MESH_VERTEX_LIST\n" ) );
ASE_ParseBracedBlock( ASE_KeyMESH_VERTEX_LIST );
}
else if ( !strcmp( token, "*MESH_TVERTLIST" ) )
{
pMesh->currentVertex = 0;
pMesh->tvertexes = calloc( sizeof( aseTVertex_t ) * pMesh->numTVertexes, 1 );
VERBOSE( ( ".....parsing MESH_TVERTLIST\n" ) );
ASE_ParseBracedBlock( ASE_KeyMESH_TVERTLIST );
}
else if ( !strcmp( token, "*MESH_FACE_LIST" ) )
{
pMesh->faces = calloc( sizeof( aseFace_t ) * pMesh->numFaces, 1 );
pMesh->currentFace = 0;
VERBOSE( ( ".....parsing MESH_FACE_LIST\n" ) );
ASE_ParseBracedBlock( ASE_KeyMESH_FACE_LIST );
}
else if ( !strcmp( token, "*MESH_TFACELIST" ) )
{
pMesh->tfaces = calloc( sizeof( aseFace_t ) * pMesh->numFaces, 1 );
pMesh->currentFace = 0;
VERBOSE( ( ".....parsing MESH_TFACE_LIST\n" ) );
ASE_ParseBracedBlock( ASE_KeyTFACE_LIST );
}
else if ( !strcmp( token, "*MESH_NORMALS" ) )
{
ASE_ParseBracedBlock( 0 );
}
}
static void ASE_KeyMESH_ANIMATION( const char *token )
{
aseMesh_t *pMesh = ASE_GetCurrentMesh();
// loads a single animation frame
if ( !strcmp( token, "*MESH" ) )
{
VERBOSE( ( "...found MESH\n" ) );
assert( pMesh->faces == 0 );
assert( pMesh->vertexes == 0 );
assert( pMesh->tvertexes == 0 );
memset( pMesh, 0, sizeof( *pMesh ) );
ASE_ParseBracedBlock( ASE_KeyMESH );
if ( ++ase.objects[ase.currentObject].anim.currentFrame == MAX_ASE_ANIMATION_FRAMES )
{
Error( "Too many animation frames" );
}
}
else
{
Error( "Unknown token '%s' while parsing MESH_ANIMATION", token );
}
}
static void ASE_KeyGEOMOBJECT( const char *token )
{
if ( !strcmp( token, "*NODE_NAME" ) )
{
char *name = ase.objects[ase.currentObject].name;
ASE_GetToken( qtrue );
VERBOSE( ( " %s\n", s_token ) );
strcpy( ase.objects[ase.currentObject].name, s_token + 1 );
if ( strchr( ase.objects[ase.currentObject].name, '"' ) )
*strchr( ase.objects[ase.currentObject].name, '"' ) = 0;
if ( strstr( name, "tag" ) == name )
{
while ( strchr( name, '_' ) != strrchr( name, '_' ) )
{
*strrchr( name, '_' ) = 0;
}
while ( strrchr( name, ' ' ) )
{
*strrchr( name, ' ' ) = 0;
}
}
}
else if ( !strcmp( token, "*NODE_PARENT" ) )
{
ASE_SkipRestOfLine();
}
// ignore unused data blocks
else if ( !strcmp( token, "*NODE_TM" ) ||
!strcmp( token, "*TM_ANIMATION" ) )
{
ASE_ParseBracedBlock( 0 );
}
// ignore regular meshes that aren't part of animation
else if ( !strcmp( token, "*MESH" ) && !ase.grabAnims )
{
/*
if ( strstr( ase.objects[ase.currentObject].name, "tag_" ) == ase.objects[ase.currentObject].name )
{
s_forceStaticMesh = true;
ASE_ParseBracedBlock( ASE_KeyMESH );
s_forceStaticMesh = false;
}
*/
ASE_ParseBracedBlock( ASE_KeyMESH );
if ( ++ase.objects[ase.currentObject].anim.currentFrame == MAX_ASE_ANIMATION_FRAMES )
{
Error( "Too many animation frames" );
}
ase.objects[ase.currentObject].anim.numFrames = ase.objects[ase.currentObject].anim.currentFrame;
ase.objects[ase.currentObject].numAnimations++;
/*
// ignore meshes that aren't part of animations if this object isn't a
// a tag
else
{
ASE_ParseBracedBlock( 0 );
}
*/
}
// according to spec these are obsolete
else if ( !strcmp( token, "*MATERIAL_REF" ) )
{
ASE_GetToken( qfalse );
ase.objects[ase.currentObject].materialRef = atoi( s_token );
}
// loads a sequence of animation frames
else if ( !strcmp( token, "*MESH_ANIMATION" ) )
{
if ( ase.grabAnims )
{
VERBOSE( ( "..found MESH_ANIMATION\n" ) );
if ( ase.objects[ase.currentObject].numAnimations )
{
Error( "Multiple MESH_ANIMATIONS within a single GEOM_OBJECT" );
}
ASE_ParseBracedBlock( ASE_KeyMESH_ANIMATION );
ase.objects[ase.currentObject].anim.numFrames = ase.objects[ase.currentObject].anim.currentFrame;
ase.objects[ase.currentObject].numAnimations++;
}
else
{
ASE_SkipEnclosingBraces();
}
}
// skip unused info
else if ( !strcmp( token, "*PROP_MOTIONBLUR" ) ||
!strcmp( token, "*PROP_CASTSHADOW" ) ||
!strcmp( token, "*PROP_RECVSHADOW" ) )
{
ASE_SkipRestOfLine();
}
}
static void ConcatenateObjects( aseGeomObject_t *pObjA, aseGeomObject_t *pObjB )
{
}
static void CollapseObjects( void )
{
int i;
int numObjects = ase.currentObject;
for ( i = 0; i < numObjects; i++ )
{
int j;
// skip tags
if ( strstr( ase.objects[i].name, "tag" ) == ase.objects[i].name )
{
continue;
}
if ( !ase.objects[i].numAnimations )
{
continue;
}
for ( j = i + 1; j < numObjects; j++ )
{
if ( strstr( ase.objects[j].name, "tag" ) == ase.objects[j].name )
{
continue;
}
if ( ase.objects[i].materialRef == ase.objects[j].materialRef )
{
if ( ase.objects[j].numAnimations )
{
ConcatenateObjects( &ase.objects[i], &ase.objects[j] );
}
}
}
}
}
/*
** ASE_Process
*/
static void ASE_Process( void )
{
while ( ASE_GetToken( qfalse ) )
{
if ( !strcmp( s_token, "*3DSMAX_ASCIIEXPORT" ) ||
!strcmp( s_token, "*COMMENT" ) )
{
ASE_SkipRestOfLine();
}
else if ( !strcmp( s_token, "*SCENE" ) )
ASE_SkipEnclosingBraces();
else if ( !strcmp( s_token, "*MATERIAL_LIST" ) )
{
VERBOSE( ("MATERIAL_LIST\n") );
ASE_ParseBracedBlock( ASE_KeyMATERIAL_LIST );
}
else if ( !strcmp( s_token, "*GEOMOBJECT" ) )
{
VERBOSE( ("GEOMOBJECT" ) );
ASE_ParseBracedBlock( ASE_KeyGEOMOBJECT );
if ( strstr( ase.objects[ase.currentObject].name, "Bip" ) ||
strstr( ase.objects[ase.currentObject].name, "ignore_" ) )
{
ASE_FreeGeomObject( ase.currentObject );
VERBOSE( ( "(discarding BIP/ignore object)\n" ) );
}
else if ( ( strstr( ase.objects[ase.currentObject].name, "h_" ) != ase.objects[ase.currentObject].name ) &&
( strstr( ase.objects[ase.currentObject].name, "l_" ) != ase.objects[ase.currentObject].name ) &&
( strstr( ase.objects[ase.currentObject].name, "u_" ) != ase.objects[ase.currentObject].name ) &&
( strstr( ase.objects[ase.currentObject].name, "tag" ) != ase.objects[ase.currentObject].name ) &&
ase.grabAnims )
{
VERBOSE( ( "(ignoring improperly labeled object '%s')\n", ase.objects[ase.currentObject].name ) );
ASE_FreeGeomObject( ase.currentObject );
}
else
{
if ( ++ase.currentObject == MAX_ASE_OBJECTS )
{
Error( "Too many GEOMOBJECTs" );
}
}
}
else if ( s_token[0] )
{
Sys_Printf( "Unknown token '%s'\n", s_token );
}
}
if ( !ase.currentObject )
Error( "No animation data!" );
CollapseObjects();
}

View File

@@ -0,0 +1,31 @@
/*
Copyright (C) 1999-2006 Id Software, Inc. and contributors.
For a list of contributors, see the accompanying CONTRIBUTORS file.
This file is part of GtkRadiant.
GtkRadiant is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
GtkRadiant is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../common/cmdlib.h"
#include "mathlib.h"
#include "polyset.h"
void ASE_Load( const char *filename, qboolean verbose, qboolean meshanims );
int ASE_GetNumSurfaces( void );
polyset_t *ASE_GetSurfaceAnimation( int ndx, int *numFrames, int skipFrameStart, int skipFrameEnd, int maxFrames );
const char *ASE_GetSurfaceName( int ndx );
void ASE_Free( void );

View File

@@ -0,0 +1,706 @@
/*
Copyright (C) 1999-2006 Id Software, Inc. and contributors.
For a list of contributors, see the accompanying CONTRIBUTORS file.
This file is part of GtkRadiant.
GtkRadiant is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
GtkRadiant is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "cmdlib.h"
#include "mathlib.h"
#include "inout.h"
#include "bspfile.h"
#include "scriplib.h"
void GetLeafNums (void);
//=============================================================================
int bsp_version = Q3_BSP_VERSION;
int nummodels;
dmodel_t dmodels[MAX_MAP_MODELS];
int numShaders;
dshader_t dshaders[MAX_MAP_SHADERS];
int entdatasize;
char dentdata[MAX_MAP_ENTSTRING];
int numleafs;
dleaf_t dleafs[MAX_MAP_LEAFS];
int numplanes;
dplane_t dplanes[MAX_MAP_PLANES];
int numnodes;
dnode_t dnodes[MAX_MAP_NODES];
int numleafsurfaces;
int dleafsurfaces[MAX_MAP_LEAFFACES];
int numleafbrushes;
int dleafbrushes[MAX_MAP_LEAFBRUSHES];
int numbrushes;
dbrush_t dbrushes[MAX_MAP_BRUSHES];
int numbrushsides;
dbrushside_t dbrushsides[MAX_MAP_BRUSHSIDES];
int numLightBytes;
byte *lightBytes;
int numGridPoints;
byte *gridData;
int numVisBytes;
byte visBytes[MAX_MAP_VISIBILITY];
int numDrawVerts = 0;
int numDrawVertsBuffer = 0;
drawVert_t *drawVerts = NULL;
int numDrawIndexes;
int drawIndexes[MAX_MAP_DRAW_INDEXES];
int numDrawSurfaces;
int numDrawSurfacesBuffer = 0;
dsurface_t *drawSurfaces = NULL;
int numFogs;
dfog_t dfogs[MAX_MAP_FOGS];
void SetLightBytes(int n)
{
if(lightBytes != 0)
free(lightBytes);
numLightBytes = n;
if(n == 0)
return;
lightBytes = safe_malloc_info(numLightBytes, "SetLightBytes");
memset(lightBytes, 0, numLightBytes);
}
void SetGridPoints(int n)
{
if(gridData != 0)
free(gridData);
numGridPoints = n;
if(n == 0)
return;
gridData = safe_malloc_info(numGridPoints * 8, "SetGridPoints");
memset(gridData, 0, numGridPoints * 8);
}
void IncDrawVerts()
{
numDrawVerts++;
if(drawVerts == 0)
{
numDrawVertsBuffer = MAX_MAP_DRAW_VERTS / 37;
drawVerts = safe_malloc_info(sizeof(drawVert_t) * numDrawVertsBuffer, "IncDrawVerts");
}
else if(numDrawVerts > numDrawVertsBuffer)
{
numDrawVertsBuffer *= 3; // multiply by 1.5
numDrawVertsBuffer /= 2;
if(numDrawVertsBuffer > MAX_MAP_DRAW_VERTS)
numDrawVertsBuffer = MAX_MAP_DRAW_VERTS;
drawVerts = realloc(drawVerts, sizeof(drawVert_t) * numDrawVertsBuffer);
if(!drawVerts)
Error( "realloc() failed (IncDrawVerts)");
}
memset(drawVerts + (numDrawVerts - 1), 0, sizeof(drawVert_t));
}
void SetDrawVerts(int n)
{
if(drawVerts != 0)
free(drawVerts);
numDrawVerts = n;
numDrawVertsBuffer = numDrawVerts;
drawVerts = safe_malloc_info(sizeof(drawVert_t) * numDrawVertsBuffer, "IncDrawVerts");
memset(drawVerts, 0, n * sizeof(drawVert_t));
}
void SetDrawSurfacesBuffer()
{
if(drawSurfaces != 0)
free(drawSurfaces);
numDrawSurfacesBuffer = MAX_MAP_DRAW_SURFS;
drawSurfaces = safe_malloc_info(sizeof(dsurface_t) * numDrawSurfacesBuffer, "IncDrawSurfaces");
memset(drawSurfaces, 0, MAX_MAP_DRAW_SURFS * sizeof(drawVert_t));
}
void SetDrawSurfaces(int n)
{
if(drawSurfaces != 0)
free(drawSurfaces);
numDrawSurfaces = n;
numDrawSurfacesBuffer = numDrawSurfaces;
drawSurfaces = safe_malloc_info(sizeof(dsurface_t) * numDrawSurfacesBuffer, "IncDrawSurfaces");
memset(drawSurfaces, 0, n * sizeof(drawVert_t));
}
void BspFilesCleanup()
{
if(drawVerts != 0)
free(drawVerts);
if(drawSurfaces != 0)
free(drawSurfaces);
if(lightBytes != 0)
free(lightBytes);
if(gridData != 0)
free(gridData);
}
//=============================================================================
/*
=============
SwapBlock
If all values are 32 bits, this can be used to swap everything
=============
*/
void SwapBlock( int *block, int sizeOfBlock ) {
int i;
sizeOfBlock >>= 2;
for ( i = 0 ; i < sizeOfBlock ; i++ ) {
block[i] = LittleLong( block[i] );
}
}
/*
=============
SwapBSPFile
Byte swaps all data in a bsp file.
=============
*/
void SwapBSPFile( void ) {
int i;
// models
SwapBlock( (int *)dmodels, nummodels * sizeof( dmodels[0] ) );
// shaders (don't swap the name)
for ( i = 0 ; i < numShaders ; i++ ) {
dshaders[i].contentFlags = LittleLong( dshaders[i].contentFlags );
dshaders[i].surfaceFlags = LittleLong( dshaders[i].surfaceFlags );
}
// planes
SwapBlock( (int *)dplanes, numplanes * sizeof( dplanes[0] ) );
// nodes
SwapBlock( (int *)dnodes, numnodes * sizeof( dnodes[0] ) );
// leafs
SwapBlock( (int *)dleafs, numleafs * sizeof( dleafs[0] ) );
// leaffaces
SwapBlock( (int *)dleafsurfaces, numleafsurfaces * sizeof( dleafsurfaces[0] ) );
// leafbrushes
SwapBlock( (int *)dleafbrushes, numleafbrushes * sizeof( dleafbrushes[0] ) );
// brushes
SwapBlock( (int *)dbrushes, numbrushes * sizeof( dbrushes[0] ) );
// brushsides
SwapBlock( (int *)dbrushsides, numbrushsides * sizeof( dbrushsides[0] ) );
// vis
((int *)&visBytes)[0] = LittleLong( ((int *)&visBytes)[0] );
((int *)&visBytes)[1] = LittleLong( ((int *)&visBytes)[1] );
// drawverts (don't swap colors )
for ( i = 0 ; i < numDrawVerts ; i++ ) {
drawVerts[i].lightmap[0] = LittleFloat( drawVerts[i].lightmap[0] );
drawVerts[i].lightmap[1] = LittleFloat( drawVerts[i].lightmap[1] );
drawVerts[i].st[0] = LittleFloat( drawVerts[i].st[0] );
drawVerts[i].st[1] = LittleFloat( drawVerts[i].st[1] );
drawVerts[i].xyz[0] = LittleFloat( drawVerts[i].xyz[0] );
drawVerts[i].xyz[1] = LittleFloat( drawVerts[i].xyz[1] );
drawVerts[i].xyz[2] = LittleFloat( drawVerts[i].xyz[2] );
drawVerts[i].normal[0] = LittleFloat( drawVerts[i].normal[0] );
drawVerts[i].normal[1] = LittleFloat( drawVerts[i].normal[1] );
drawVerts[i].normal[2] = LittleFloat( drawVerts[i].normal[2] );
}
// drawindexes
SwapBlock( (int *)drawIndexes, numDrawIndexes * sizeof( drawIndexes[0] ) );
// drawsurfs
SwapBlock( (int *)drawSurfaces, numDrawSurfaces * sizeof( drawSurfaces[0] ) );
// fogs
for ( i = 0 ; i < numFogs ; i++ ) {
dfogs[i].brushNum = LittleLong( dfogs[i].brushNum );
dfogs[i].visibleSide = LittleLong( dfogs[i].visibleSide );
}
}
/*
=============
GetLumpElements
=============
*/
int GetLumpElements( dheader_t *header, int lump, int size ) {
int length, ofs;
length = header->lumps[lump].filelen;
ofs = header->lumps[lump].fileofs;
if ( length % size ) {
Error ("LoadBSPFile: odd lump size");
}
return length / size;
}
/*
=============
CopyLump
=============
*/
int CopyLump( dheader_t *header, int lump, void *dest, int size ) {
int length, ofs;
length = header->lumps[lump].filelen;
ofs = header->lumps[lump].fileofs;
if(length == 0)
return 0;
if ( length % size ) {
Error ("LoadBSPFile: odd lump size");
}
memcpy( dest, (byte *)header + ofs, length );
return length / size;
}
/*
=============
LoadBSPFile
=============
*/
void LoadBSPFile( const char *filename ) {
dheader_t *header;
// load the file header
LoadFile (filename, (void **)&header);
// swap the header
SwapBlock( (int *)header, sizeof(*header) );
if ( header->ident != BSP_IDENT ) {
Error( "%s is not a IBSP file", filename );
}
if ( header->version != bsp_version ) {
Error( "%s is version %i, not %i", filename, header->version, bsp_version );
}
numShaders = CopyLump( header, LUMP_SHADERS, dshaders, sizeof(dshader_t) );
nummodels = CopyLump( header, LUMP_MODELS, dmodels, sizeof(dmodel_t) );
numplanes = CopyLump( header, LUMP_PLANES, dplanes, sizeof(dplane_t) );
numleafs = CopyLump( header, LUMP_LEAFS, dleafs, sizeof(dleaf_t) );
numnodes = CopyLump( header, LUMP_NODES, dnodes, sizeof(dnode_t) );
numleafsurfaces = CopyLump( header, LUMP_LEAFSURFACES, dleafsurfaces, sizeof(dleafsurfaces[0]) );
numleafbrushes = CopyLump( header, LUMP_LEAFBRUSHES, dleafbrushes, sizeof(dleafbrushes[0]) );
numbrushes = CopyLump( header, LUMP_BRUSHES, dbrushes, sizeof(dbrush_t) );
numbrushsides = CopyLump( header, LUMP_BRUSHSIDES, dbrushsides, sizeof(dbrushside_t) );
numDrawVerts = GetLumpElements( header, LUMP_DRAWVERTS, sizeof(drawVert_t) );
SetDrawVerts(numDrawVerts);
CopyLump( header, LUMP_DRAWVERTS, drawVerts, sizeof(drawVert_t) );
numDrawSurfaces = GetLumpElements( header, LUMP_SURFACES, sizeof(dsurface_t) );
SetDrawSurfaces(numDrawSurfaces);
numDrawSurfaces = CopyLump( header, LUMP_SURFACES, drawSurfaces, sizeof(dsurface_t) );
numFogs = CopyLump( header, LUMP_FOGS, dfogs, sizeof(dfog_t) );
numDrawIndexes = CopyLump( header, LUMP_DRAWINDEXES, drawIndexes, sizeof(drawIndexes[0]) );
numVisBytes = CopyLump( header, LUMP_VISIBILITY, visBytes, 1 );
numLightBytes = GetLumpElements( header, LUMP_LIGHTMAPS, 1 );
SetLightBytes(numLightBytes);
CopyLump( header, LUMP_LIGHTMAPS, lightBytes, 1 );
entdatasize = CopyLump( header, LUMP_ENTITIES, dentdata, 1);
numGridPoints = GetLumpElements( header, LUMP_LIGHTGRID, 8 );
SetGridPoints(numGridPoints);
CopyLump( header, LUMP_LIGHTGRID, gridData, 8 );
free( header ); // everything has been copied out
// swap everything
SwapBSPFile();
}
//============================================================================
/*
=============
AddLump
=============
*/
void AddLump( FILE *bspfile, dheader_t *header, int lumpnum, const void *data, int len ) {
lump_t *lump;
lump = &header->lumps[lumpnum];
lump->fileofs = LittleLong( ftell(bspfile) );
lump->filelen = LittleLong( len );
SafeWrite( bspfile, data, (len+3)&~3 );
}
/*
=============
WriteBSPFile
Swaps the bsp file in place, so it should not be referenced again
=============
*/
void WriteBSPFile( const char *filename ) {
dheader_t outheader, *header;
FILE *bspfile;
header = &outheader;
memset( header, 0, sizeof(dheader_t) );
SwapBSPFile();
header->ident = LittleLong( BSP_IDENT );
header->version = LittleLong( bsp_version );
bspfile = SafeOpenWrite( filename );
SafeWrite( bspfile, header, sizeof(dheader_t) ); // overwritten later
AddLump( bspfile, header, LUMP_SHADERS, dshaders, numShaders*sizeof(dshader_t) );
AddLump( bspfile, header, LUMP_PLANES, dplanes, numplanes*sizeof(dplane_t) );
AddLump( bspfile, header, LUMP_LEAFS, dleafs, numleafs*sizeof(dleaf_t) );
AddLump( bspfile, header, LUMP_NODES, dnodes, numnodes*sizeof(dnode_t) );
AddLump( bspfile, header, LUMP_BRUSHES, dbrushes, numbrushes*sizeof(dbrush_t) );
AddLump( bspfile, header, LUMP_BRUSHSIDES, dbrushsides, numbrushsides*sizeof(dbrushside_t) );
AddLump( bspfile, header, LUMP_LEAFSURFACES, dleafsurfaces, numleafsurfaces*sizeof(dleafsurfaces[0]) );
AddLump( bspfile, header, LUMP_LEAFBRUSHES, dleafbrushes, numleafbrushes*sizeof(dleafbrushes[0]) );
AddLump( bspfile, header, LUMP_MODELS, dmodels, nummodels*sizeof(dmodel_t) );
AddLump( bspfile, header, LUMP_DRAWVERTS, drawVerts, numDrawVerts*sizeof(drawVert_t) );
AddLump( bspfile, header, LUMP_SURFACES, drawSurfaces, numDrawSurfaces*sizeof(dsurface_t) );
AddLump( bspfile, header, LUMP_VISIBILITY, visBytes, numVisBytes );
AddLump( bspfile, header, LUMP_LIGHTMAPS, lightBytes, numLightBytes );
AddLump( bspfile, header, LUMP_LIGHTGRID, gridData, 8 * numGridPoints );
AddLump( bspfile, header, LUMP_ENTITIES, dentdata, entdatasize );
AddLump( bspfile, header, LUMP_FOGS, dfogs, numFogs * sizeof(dfog_t) );
AddLump( bspfile, header, LUMP_DRAWINDEXES, drawIndexes, numDrawIndexes * sizeof(drawIndexes[0]) );
fseek (bspfile, 0, SEEK_SET);
SafeWrite (bspfile, header, sizeof(dheader_t));
fclose (bspfile);
}
//============================================================================
/*
=============
PrintBSPFileSizes
Dumps info about current file
=============
*/
void PrintBSPFileSizes( void ) {
if ( !num_entities ) {
ParseEntities();
}
Sys_Printf ("%6i models %7i\n"
,nummodels, (int)(nummodels*sizeof(dmodel_t)));
Sys_Printf ("%6i shaders %7i\n"
,numShaders, (int)(numShaders*sizeof(dshader_t)));
Sys_Printf ("%6i brushes %7i\n"
,numbrushes, (int)(numbrushes*sizeof(dbrush_t)));
Sys_Printf ("%6i brushsides %7i\n"
,numbrushsides, (int)(numbrushsides*sizeof(dbrushside_t)));
Sys_Printf ("%6i fogs %7i\n"
,numFogs, (int)(numFogs*sizeof(dfog_t)));
Sys_Printf ("%6i planes %7i\n"
,numplanes, (int)(numplanes*sizeof(dplane_t)));
Sys_Printf ("%6i entdata %7i\n", num_entities, entdatasize);
Sys_Printf ("\n");
Sys_Printf ("%6i nodes %7i\n"
,numnodes, (int)(numnodes*sizeof(dnode_t)));
Sys_Printf ("%6i leafs %7i\n"
,numleafs, (int)(numleafs*sizeof(dleaf_t)));
Sys_Printf ("%6i leafsurfaces %7i\n"
,numleafsurfaces, (int)(numleafsurfaces*sizeof(dleafsurfaces[0])));
Sys_Printf ("%6i leafbrushes %7i\n"
,numleafbrushes, (int)(numleafbrushes*sizeof(dleafbrushes[0])));
Sys_Printf ("%6i drawverts %7i\n"
,numDrawVerts, (int)(numDrawVerts*sizeof(drawVerts[0])));
Sys_Printf ("%6i drawindexes %7i\n"
,numDrawIndexes, (int)(numDrawIndexes*sizeof(drawIndexes[0])));
Sys_Printf ("%6i drawsurfaces %7i\n"
,numDrawSurfaces, (int)(numDrawSurfaces*sizeof(drawSurfaces[0])));
Sys_Printf ("%6i lightmaps %7i\n"
,numLightBytes / (LIGHTMAP_WIDTH*LIGHTMAP_HEIGHT*3), numLightBytes );
Sys_Printf (" visibility %7i\n"
, numVisBytes );
}
//============================================
int num_entities;
entity_t entities[MAX_MAP_ENTITIES];
void StripTrailing( char *e ) {
char *s;
s = e + strlen(e)-1;
while (s >= e && *s <= 32)
{
*s = 0;
s--;
}
}
/*
=================
ParseEpair
=================
*/
epair_t *ParseEpair( void ) {
epair_t *e;
e = safe_malloc( sizeof(epair_t) );
memset( e, 0, sizeof(epair_t) );
if ( strlen(token) >= MAX_KEY-1 ) {
Error ("ParseEpar: token too long");
}
e->key = copystring( token );
GetToken( qfalse );
if ( strlen(token) >= MAX_VALUE-1 ) {
Error ("ParseEpar: token too long");
}
e->value = copystring( token );
// strip trailing spaces that sometimes get accidentally
// added in the editor
StripTrailing( e->key );
StripTrailing( e->value );
return e;
}
/*
================
ParseEntity
================
*/
qboolean ParseEntity( void ) {
epair_t *e;
entity_t *mapent;
if ( !GetToken (qtrue) ) {
return qfalse;
}
if ( strcmp (token, "{") ) {
Error ("ParseEntity: { not found");
}
if ( num_entities == MAX_MAP_ENTITIES ) {
Error ("num_entities == MAX_MAP_ENTITIES");
}
mapent = &entities[num_entities];
num_entities++;
do {
if ( !GetToken (qtrue) ) {
Error ("ParseEntity: EOF without closing brace");
}
if ( !strcmp (token, "}") ) {
break;
}
e = ParseEpair ();
e->next = mapent->epairs;
mapent->epairs = e;
} while (1);
return qtrue;
}
/*
================
ParseEntities
Parses the dentdata string into entities
================
*/
void ParseEntities( void ) {
num_entities = 0;
ParseFromMemory( dentdata, entdatasize );
while ( ParseEntity () ) {
}
}
/*
================
UnparseEntities
Generates the dentdata string from all the entities
This allows the utilities to add or remove key/value pairs
to the data created by the map editor.
================
*/
void UnparseEntities( void ) {
char *buf, *end;
epair_t *ep;
char line[2048];
int i;
char key[1024], value[1024];
buf = dentdata;
end = buf;
*end = 0;
for (i=0 ; i<num_entities ; i++) {
ep = entities[i].epairs;
if ( !ep ) {
continue; // ent got removed
}
strcat (end,"{\n");
end += 2;
for ( ep = entities[i].epairs ; ep ; ep=ep->next ) {
strcpy (key, ep->key);
StripTrailing (key);
strcpy (value, ep->value);
StripTrailing (value);
sprintf (line, "\"%s\" \"%s\"\n", key, value);
strcat (end, line);
end += strlen(line);
}
strcat (end,"}\n");
end += 2;
if (end > buf + MAX_MAP_ENTSTRING) {
Error ("Entity text too long");
}
}
entdatasize = end - buf + 1;
}
void PrintEntity( const entity_t *ent ) {
epair_t *ep;
Sys_Printf ("------- entity %p -------\n", ent);
for (ep=ent->epairs ; ep ; ep=ep->next) {
Sys_Printf( "%s = %s\n", ep->key, ep->value );
}
}
void SetKeyValue( entity_t *ent, const char *key, const char *value ) {
epair_t *ep;
for ( ep=ent->epairs ; ep ; ep=ep->next ) {
if ( !strcmp (ep->key, key) ) {
free (ep->value);
ep->value = copystring(value);
return;
}
}
ep = safe_malloc (sizeof(*ep));
ep->next = ent->epairs;
ent->epairs = ep;
ep->key = copystring(key);
ep->value = copystring(value);
}
const char *ValueForKey( const entity_t *ent, const char *key ) {
epair_t *ep;
for (ep=ent->epairs ; ep ; ep=ep->next) {
if (!strcmp (ep->key, key) ) {
return ep->value;
}
}
return "";
}
vec_t FloatForKey( const entity_t *ent, const char *key ) {
const char *k;
k = ValueForKey( ent, key );
return atof(k);
}
void GetVectorForKey( const entity_t *ent, const char *key, vec3_t vec ) {
const char *k;
double v1, v2, v3;
k = ValueForKey (ent, key);
// scanf into doubles, then assign, so it is vec_t size independent
v1 = v2 = v3 = 0;
sscanf (k, "%lf %lf %lf", &v1, &v2, &v3);
vec[0] = v1;
vec[1] = v2;
vec[2] = v3;
}

View File

@@ -0,0 +1,121 @@
/*
Copyright (C) 1999-2006 Id Software, Inc. and contributors.
For a list of contributors, see the accompanying CONTRIBUTORS file.
This file is part of GtkRadiant.
GtkRadiant is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
GtkRadiant is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "qfiles.h"
#include "surfaceflags.h"
extern int bsp_version;
extern int nummodels;
extern dmodel_t dmodels[MAX_MAP_MODELS];
extern int numShaders;
extern dshader_t dshaders[MAX_MAP_MODELS];
extern int entdatasize;
extern char dentdata[MAX_MAP_ENTSTRING];
extern int numleafs;
extern dleaf_t dleafs[MAX_MAP_LEAFS];
extern int numplanes;
extern dplane_t dplanes[MAX_MAP_PLANES];
extern int numnodes;
extern dnode_t dnodes[MAX_MAP_NODES];
extern int numleafsurfaces;
extern int dleafsurfaces[MAX_MAP_LEAFFACES];
extern int numleafbrushes;
extern int dleafbrushes[MAX_MAP_LEAFBRUSHES];
extern int numbrushes;
extern dbrush_t dbrushes[MAX_MAP_BRUSHES];
extern int numbrushsides;
extern dbrushside_t dbrushsides[MAX_MAP_BRUSHSIDES];
void SetLightBytes(int n);
extern int numLightBytes;
extern byte *lightBytes;
void SetGridPoints(int n);
extern int numGridPoints;
extern byte *gridData;
extern int numVisBytes;
extern byte visBytes[MAX_MAP_VISIBILITY];
void SetDrawVerts(int n);
void IncDrawVerts();
extern int numDrawVerts;
extern drawVert_t *drawVerts;
extern int numDrawIndexes;
extern int drawIndexes[MAX_MAP_DRAW_INDEXES];
void SetDrawSurfaces(int n);
void SetDrawSurfacesBuffer();
extern int numDrawSurfaces;
extern dsurface_t *drawSurfaces;
extern int numFogs;
extern dfog_t dfogs[MAX_MAP_FOGS];
void LoadBSPFile( const char *filename );
void WriteBSPFile( const char *filename );
void PrintBSPFileSizes( void );
//===============
typedef struct epair_s {
struct epair_s *next;
char *key;
char *value;
} epair_t;
typedef struct {
vec3_t origin;
struct bspbrush_s *brushes;
struct parseMesh_s *patches;
int firstDrawSurf;
epair_t *epairs;
} entity_t;
extern int num_entities;
extern entity_t entities[MAX_MAP_ENTITIES];
void ParseEntities( void );
void UnparseEntities( void );
void SetKeyValue( entity_t *ent, const char *key, const char *value );
const char *ValueForKey( const entity_t *ent, const char *key );
// will return "" if not present
vec_t FloatForKey( const entity_t *ent, const char *key );
void GetVectorForKey( const entity_t *ent, const char *key, vec3_t vec );
epair_t *ParseEpair( void );
void PrintEntity( const entity_t *ent );

1153
tools/quake3/common/cmdlib.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,160 @@
/*
Copyright (C) 1999-2006 Id Software, Inc. and contributors.
For a list of contributors, see the accompanying CONTRIBUTORS file.
This file is part of GtkRadiant.
GtkRadiant is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
GtkRadiant is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
// cmdlib.h
#ifndef __CMDLIB__
#define __CMDLIB__
#include "bytebool.h"
#ifdef _WIN32
#pragma warning(disable : 4244) // MIPS
#pragma warning(disable : 4136) // X86
#pragma warning(disable : 4051) // ALPHA
#pragma warning(disable : 4018) // signed/unsigned mismatch
#pragma warning(disable : 4305) // truncate from double to float
#pragma check_stack(off)
#endif
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <ctype.h>
#include <time.h>
#include <stdarg.h>
#ifdef _WIN32
#pragma intrinsic( memset, memcpy )
#endif
#define MAX_OS_PATH 1024
#define MEM_BLOCKSIZE 4096
// the dec offsetof macro doesnt work very well...
#define myoffsetof(type,identifier) ((size_t)&((type *)0)->identifier)
#define SAFE_MALLOC
#ifdef SAFE_MALLOC
void *safe_malloc( size_t size );
void *safe_malloc_info( size_t size, char* info );
#else
#define safe_malloc(a) malloc(a)
#endif /* SAFE_MALLOC */
// set these before calling CheckParm
extern int myargc;
extern char **myargv;
char *strlower (char *in);
int Q_strncasecmp( const char *s1, const char *s2, int n );
int Q_stricmp( const char *s1, const char *s2 );
void Q_getwd( char *out );
int Q_filelength (FILE *f);
int FileTime( const char *path );
void Q_mkdir( const char *path );
extern char qdir[1024];
extern char gamedir[1024];
extern char writedir[1024];
extern char *moddirparam;
void SetQdirFromPath( const char *path);
char *ExpandArg( const char *path ); // from cmd line
char *ExpandPath( const char *path ); // from scripts
char *ExpandGamePath (const char *path);
char *ExpandPathAndArchive( const char *path );
void ExpandWildcards( int *argc, char ***argv );
double I_FloatTime( void );
void Error( const char *error, ... );
int CheckParm( const char *check );
FILE *SafeOpenWrite( const char *filename );
FILE *SafeOpenRead( const char *filename );
void SafeRead (FILE *f, void *buffer, int count);
void SafeWrite (FILE *f, const void *buffer, int count);
int LoadFile( const char *filename, void **bufferptr );
int LoadFileBlock( const char *filename, void **bufferptr );
int TryLoadFile( const char *filename, void **bufferptr );
void SaveFile( const char *filename, const void *buffer, int count );
qboolean FileExists( const char *filename );
void DefaultExtension( char *path, const char *extension );
void DefaultPath( char *path, const char *basepath );
void StripFilename( char *path );
void StripExtension( char *path );
void ExtractFilePath( const char *path, char *dest );
void ExtractFileBase( const char *path, char *dest );
void ExtractFileExtension( const char *path, char *dest );
int ParseNum (const char *str);
short BigShort (short l);
short LittleShort (short l);
int BigLong (int l);
int LittleLong (int l);
float BigFloat (float l);
float LittleFloat (float l);
char *COM_Parse (char *data);
extern char com_token[1024];
extern qboolean com_eof;
char *copystring(const char *s);
void CRC_Init(unsigned short *crcvalue);
void CRC_ProcessByte(unsigned short *crcvalue, byte data);
unsigned short CRC_Value(unsigned short crcvalue);
void CreatePath( const char *path );
void QCopyFile( const char *from, const char *to );
extern qboolean archive;
extern char archivedir[1024];
// sleep for the given amount of milliseconds
void Sys_Sleep(int n);
// for compression routines
typedef struct
{
void *data;
int count, width, height;
} cblock_t;
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,44 @@
/*
Copyright (C) 1999-2006 Id Software, Inc. and contributors.
For a list of contributors, see the accompanying CONTRIBUTORS file.
This file is part of GtkRadiant.
GtkRadiant is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
GtkRadiant is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
// piclib.h
void LoadLBM (const char *filename, byte **picture, byte **palette);
void WriteLBMfile (const char *filename, byte *data, int width, int height
, byte *palette);
void LoadPCX (const char *filename, byte **picture, byte **palette, int *width, int *height);
void WritePCXfile (const char *filename, byte *data, int width, int height
, byte *palette);
// loads / saves either lbm or pcx, depending on extension
void Load256Image (const char *name, byte **pixels, byte **palette,
int *width, int *height);
void Save256Image (const char *name, byte *pixels, byte *palette,
int width, int height);
void LoadTGA (const char *filename, byte **pixels, int *width, int *height);
void LoadTGABuffer ( const byte *buffer, const byte* enddata, byte **pic, int *width, int *height);
void WriteTGA (const char *filename, byte *data, int width, int height);
void Load32BitImage (const char *name, unsigned **pixels, int *width, int *height);

375
tools/quake3/common/inout.c Normal file
View File

@@ -0,0 +1,375 @@
/*
Copyright (C) 1999-2006 Id Software, Inc. and contributors.
For a list of contributors, see the accompanying CONTRIBUTORS file.
This file is part of GtkRadiant.
GtkRadiant is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
GtkRadiant is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
//-----------------------------------------------------------------------------
//
//
// DESCRIPTION:
// deal with in/out tasks, for either stdin/stdout or network/XML stream
//
#include "cmdlib.h"
#include "mathlib.h"
#include "polylib.h"
#include "inout.h"
#include <sys/types.h>
#include <sys/stat.h>
#ifdef WIN32
#include <direct.h>
#include <windows.h>
#endif
// network broadcasting
#include "l_net/l_net.h"
#include "libxml/tree.h"
// utf8 conversion
#include <glib/gconvert.h>
#include <glib/gmem.h>
#ifdef WIN32
HWND hwndOut = NULL;
qboolean lookedForServer = qfalse;
UINT wm_BroadcastCommand = -1;
#endif
socket_t *brdcst_socket;
netmessage_t msg;
qboolean verbose = qfalse;
// our main document
// is streamed through the network to Radiant
// possibly written to disk at the end of the run
//++timo FIXME: need to be global, required when creating nodes?
xmlDocPtr doc;
xmlNodePtr tree;
// some useful stuff
xmlNodePtr xml_NodeForVec( vec3_t v )
{
xmlNodePtr ret;
char buf[1024];
sprintf (buf, "%f %f %f", v[0], v[1], v[2]);
ret = xmlNewNode (NULL, "point");
xmlNodeSetContent (ret, buf);
return ret;
}
// send a node down the stream, add it to the document
void xml_SendNode (xmlNodePtr node)
{
xmlBufferPtr xml_buf;
char xmlbuf[MAX_NETMESSAGE]; // we have to copy content from the xmlBufferPtr into an aux buffer .. that sucks ..
// this index loops through the node buffer
int pos = 0;
int size;
xmlAddChild( doc->children, node );
if (brdcst_socket)
{
xml_buf = xmlBufferCreate();
xmlNodeDump( xml_buf, doc, node, 0, 0 );
// the XML node might be too big to fit in a single network message
// l_net library defines an upper limit of MAX_NETMESSAGE
// there are some size check errors, so we use MAX_NETMESSAGE-10 to be safe
// if the size of the buffer exceeds MAX_NETMESSAGE-10 we'll send in several network messages
while (pos < xml_buf->use)
{
// what size are we gonna send now?
(xml_buf->use - pos < MAX_NETMESSAGE - 10) ? (size = xml_buf->use - pos) : (size = MAX_NETMESSAGE - 10);
//++timo just a debug thing
if (size == MAX_NETMESSAGE - 10)
Sys_FPrintf (SYS_NOXML, "Got to split the buffer\n");
memcpy( xmlbuf, xml_buf->content+pos, size);
xmlbuf[size] = '\0';
NMSG_Clear( &msg );
NMSG_WriteString (&msg, xmlbuf );
Net_Send(brdcst_socket, &msg );
// now that the thing is sent prepare to loop again
pos += size;
}
#if 0
// NOTE: the NMSG_WriteString is limited to MAX_NETMESSAGE
// we will need to split into chunks
// (we could also go lower level, in the end it's using send and receiv which are not size limited)
//++timo FIXME: MAX_NETMESSAGE is not exactly the max size we can stick in the message
// there's some tweaking to do in l_net for that .. so let's give us a margin for now
//++timo we need to handle the case of a buffer too big to fit in a single message
// try without checks for now
if (xml_buf->use > MAX_NETMESSAGE-10 )
{
// if we send that we are probably gonna break the stream at the other end..
// and Error will call right there
//Error( "MAX_NETMESSAGE exceeded for XML feedback stream in FPrintf (%d)\n", xml_buf->use);
Sys_FPrintf (SYS_NOXML, "MAX_NETMESSAGE exceeded for XML feedback stream in FPrintf (%d)\n", xml_buf->use);
xml_buf->content[xml_buf->use]='\0'; //++timo this corrupts the buffer but we don't care it's for printing
Sys_FPrintf (SYS_NOXML, xml_buf->content);
}
size = xml_buf->use;
memcpy( xmlbuf, xml_buf->content, size );
xmlbuf[size] = '\0';
NMSG_Clear( &msg );
NMSG_WriteString (&msg, xmlbuf );
Net_Send(brdcst_socket, &msg );
#endif
xmlBufferFree( xml_buf );
}
}
void xml_Select (char *msg, int entitynum, int brushnum, qboolean bError)
{
xmlNodePtr node, select;
char buf[1024];
char level[2];
// now build a proper "select" XML node
sprintf (buf, "Entity %i, Brush %i: %s", entitynum, brushnum, msg);
node = xmlNewNode (NULL, "select");
xmlNodeSetContent (node, buf);
level[0] = (int)'0' + (bError ? SYS_ERR : SYS_WRN) ;
level[1] = 0;
xmlSetProp (node, "level", (char *)&level);
// a 'select' information
sprintf (buf, "%i %i", entitynum, brushnum);
select = xmlNewNode (NULL, "brush");
xmlNodeSetContent (select, buf);
xmlAddChild (node, select);
xml_SendNode (node);
sprintf (buf, "Entity %i, Brush %i: %s", entitynum, brushnum, msg);
if (bError)
Error(buf);
else
Sys_FPrintf (SYS_NOXML, "%s\n", buf);
}
void xml_Point (char *msg, vec3_t pt)
{
xmlNodePtr node, point;
char buf[1024];
char level[2];
node = xmlNewNode (NULL, "pointmsg");
xmlNodeSetContent (node, msg);
level[0] = (int)'0' + SYS_ERR;
level[1] = 0;
xmlSetProp (node, "level", (char *)&level);
// a 'point' node
sprintf (buf, "%g %g %g", pt[0], pt[1], pt[2]);
point = xmlNewNode (NULL, "point");
xmlNodeSetContent (point, buf);
xmlAddChild (node, point);
xml_SendNode (node);
sprintf (buf, "%s (%g %g %g)", msg, pt[0], pt[1], pt[2]);
Error (buf);
}
#define WINDING_BUFSIZE 2048
void xml_Winding (char *msg, vec3_t p[], int numpoints, qboolean die)
{
xmlNodePtr node, winding;
char buf[WINDING_BUFSIZE];
char smlbuf[128];
char level[2];
int i;
node = xmlNewNode (NULL, "windingmsg");
xmlNodeSetContent (node, msg);
level[0] = (int)'0' + SYS_ERR;
level[1] = 0;
xmlSetProp (node, "level", (char *)&level);
// a 'winding' node
sprintf( buf, "%i ", numpoints);
for(i = 0; i < numpoints; i++)
{
sprintf (smlbuf, "(%g %g %g)", p[i][0], p[i][1], p[i][2]);
// don't overflow
if (strlen(buf)+strlen(smlbuf)>WINDING_BUFSIZE)
break;
strcat( buf, smlbuf);
}
winding = xmlNewNode (NULL, "winding");
xmlNodeSetContent (winding, buf);
xmlAddChild (node, winding);
xml_SendNode (node);
if(die)
Error (msg);
else
{
Sys_Printf(msg);
Sys_Printf("\n");
}
}
// in include
#include "stream_version.h"
void Broadcast_Setup( const char *dest )
{
address_t address;
char sMsg[1024];
Net_Setup();
Net_StringToAddress((char *)dest, &address);
brdcst_socket = Net_Connect(&address, 0);
if (brdcst_socket)
{
// send in a header
sprintf (sMsg, "<?xml version=\"1.0\"?><q3map_feedback version=\"" Q3MAP_STREAM_VERSION "\">");
NMSG_Clear( &msg );
NMSG_WriteString(&msg, sMsg );
Net_Send(brdcst_socket, &msg );
}
}
void Broadcast_Shutdown()
{
if (brdcst_socket)
{
Sys_Printf("Disconnecting\n");
Net_Disconnect(brdcst_socket);
brdcst_socket = NULL;
}
}
// all output ends up through here
void FPrintf (int flag, char *buf)
{
xmlNodePtr node;
static qboolean bGotXML = qfalse;
char level[2];
printf(buf);
// the following part is XML stuff only.. but maybe we don't want that message to go down the XML pipe?
if (flag == SYS_NOXML)
return;
// ouput an XML file of the run
// use the DOM interface to build a tree
/*
<message level='flag'>
message string
.. various nodes to describe corresponding geometry ..
</message>
*/
if (!bGotXML)
{
// initialize
doc = xmlNewDoc("1.0");
doc->children = xmlNewDocRawNode(doc, NULL, "q3map_feedback", NULL);
bGotXML = qtrue;
}
node = xmlNewNode (NULL, "message");
{
gchar* utf8 = g_locale_to_utf8(buf, -1, NULL, NULL, NULL);
xmlNodeSetContent(node, utf8);
g_free(utf8);
}
level[0] = (int)'0' + flag;
level[1] = 0;
xmlSetProp (node, "level", (char *)&level );
xml_SendNode (node);
}
#ifdef DBG_XML
void DumpXML()
{
xmlSaveFile( "XMLDump.xml", doc );
}
#endif
void Sys_FPrintf (int flag, const char *format, ...)
{
char out_buffer[4096];
va_list argptr;
if ((flag == SYS_VRB) && (verbose == qfalse))
return;
va_start (argptr, format);
vsprintf (out_buffer, format, argptr);
va_end (argptr);
FPrintf (flag, out_buffer);
}
void Sys_Printf (const char *format, ...)
{
char out_buffer[4096];
va_list argptr;
va_start (argptr, format);
vsprintf (out_buffer, format, argptr);
va_end (argptr);
FPrintf (SYS_STD, out_buffer);
}
/*
=================
Error
For abnormal program terminations
=================
*/
void Error( const char *error, ...)
{
char out_buffer[4096];
char tmp[4096];
va_list argptr;
va_start (argptr,error);
vsprintf (tmp, error, argptr);
va_end (argptr);
sprintf( out_buffer, "************ ERROR ************\n%s\n", tmp );
FPrintf( SYS_ERR, out_buffer );
#ifdef DBG_XML
DumpXML();
#endif
//++timo HACK ALERT .. if we shut down too fast the xml stream won't reach the listener.
// a clean solution is to send a sync request node in the stream and wait for an answer before exiting
Sys_Sleep( 1000 );
Broadcast_Shutdown();
exit (1);
}

View File

@@ -0,0 +1,62 @@
/*
Copyright (C) 1999-2006 Id Software, Inc. and contributors.
For a list of contributors, see the accompanying CONTRIBUTORS file.
This file is part of GtkRadiant.
GtkRadiant is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
GtkRadiant is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef __INOUT__
#define __INOUT__
// inout is the only stuff relying on xml, include the headers there
#include "libxml/tree.h"
#include "mathlib.h"
// some useful xml routines
xmlNodePtr xml_NodeForVec( vec3_t v );
void xml_SendNode (xmlNodePtr node);
// print a message in q3map output and send the corresponding select information down the xml stream
// bError: do we end with an error on this one or do we go ahead?
void xml_Select (char *msg, int entitynum, int brushnum, qboolean bError);
// end q3map with an error message and send a point information in the xml stream
// note: we might want to add a boolean to use this as a warning or an error thing..
void xml_Winding (char *msg, vec3_t p[], int numpoints, qboolean die);
void xml_Point (char *msg, vec3_t pt);
extern qboolean bNetworkBroadcast;
void Broadcast_Setup( const char *dest );
void Broadcast_Shutdown();
#define SYS_VRB 0 // verbose support (on/off)
#define SYS_STD 1 // standard print level
#define SYS_WRN 2 // warnings
#define SYS_ERR 3 // error
#define SYS_NOXML 4 // don't send that down the XML stream
extern qboolean verbose;
void Sys_Printf (const char *text, ...);
void Sys_FPrintf (int flag, const char *text, ...);
#ifdef _DEBUG
#define DBG_XML 1
#endif
#ifdef DBG_XML
void DumpXML();
#endif
#endif

View File

@@ -0,0 +1,301 @@
/*
Copyright (C) 1999-2006 Id Software, Inc. and contributors.
For a list of contributors, see the accompanying CONTRIBUTORS file.
This file is part of GtkRadiant.
GtkRadiant is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
GtkRadiant is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
//
// l3dslib.c: library for loading triangles from an Alias triangle file
//
#include <stdio.h>
#include "cmdlib.h"
#include "mathlib.h"
#include "trilib.h"
#include "l3dslib.h"
#define MAIN3DS 0x4D4D
#define EDIT3DS 0x3D3D // this is the start of the editor config
#define EDIT_OBJECT 0x4000
#define OBJ_TRIMESH 0x4100
#define TRI_VERTEXL 0x4110
#define TRI_FACEL1 0x4120
#define MAXVERTS 2000
#define MAXTRIANGLES 750
typedef struct {
int v[4];
} tri;
float fverts[MAXVERTS][3];
tri tris[MAXTRIANGLES];
int bytesread, level, numtris, totaltris;
int vertsfound, trisfound;
triangle_t *ptri;
// Alias stores triangles as 3 explicit vertices in .tri files, so even though we
// start out with a vertex pool and vertex indices for triangles, we have to convert
// to raw, explicit triangles
void StoreAliasTriangles (void)
{
int i, j, k;
if ((totaltris + numtris) > MAXTRIANGLES)
Error ("Error: Too many triangles");
for (i=0; i<numtris ; i++)
{
for (j=0 ; j<3 ; j++)
{
for (k=0 ; k<3 ; k++)
{
ptri[i+totaltris].verts[j][k] = fverts[tris[i].v[j]][k];
}
}
}
totaltris += numtris;
numtris = 0;
vertsfound = 0;
trisfound = 0;
}
int ParseVertexL (FILE *input)
{
int i, j, startbytesread, numverts;
unsigned short tshort;
if (vertsfound)
Error ("Error: Multiple vertex chunks");
vertsfound = 1;
startbytesread = bytesread;
if (feof(input))
Error ("Error: unexpected end of file");
fread(&tshort, sizeof(tshort), 1, input);
bytesread += sizeof(tshort);
numverts = (int)tshort;
if (numverts > MAXVERTS)
Error ("Error: Too many vertices");
for (i=0 ; i<numverts ; i++)
{
for (j=0 ; j<3 ; j++)
{
if (feof(input))
Error ("Error: unexpected end of file");
fread(&fverts[i][j], sizeof(float), 1, input);
bytesread += sizeof(float);
}
}
if (vertsfound && trisfound)
StoreAliasTriangles ();
return bytesread - startbytesread;
}
int ParseFaceL1 (FILE *input)
{
int i, j, startbytesread;
unsigned short tshort;
if (trisfound)
Error ("Error: Multiple face chunks");
trisfound = 1;
startbytesread = bytesread;
if (feof(input))
Error ("Error: unexpected end of file");
fread(&tshort, sizeof(tshort), 1, input);
bytesread += sizeof(tshort);
numtris = (int)tshort;
if (numtris > MAXTRIANGLES)
Error ("Error: Too many triangles");
for (i=0 ; i<numtris ; i++)
{
for (j=0 ; j<4 ; j++)
{
if (feof(input))
Error ("Error: unexpected end of file");
fread(&tshort, sizeof(tshort), 1, input);
bytesread += sizeof(tshort);
tris[i].v[j] = (int)tshort;
}
}
if (vertsfound && trisfound)
StoreAliasTriangles ();
return bytesread - startbytesread;
}
int ParseChunk (FILE *input)
{
#define BLOCK_SIZE 4096
char temp[BLOCK_SIZE];
unsigned short type;
int i, length, w, t, retval;
level++;
retval = 0;
// chunk type
if (feof(input))
Error ("Error: unexpected end of file");
fread(&type, sizeof(type), 1, input);
bytesread += sizeof(type);
// chunk length
if (feof(input))
Error ("Error: unexpected end of file");
fread (&length, sizeof(length), 1, input);
bytesread += sizeof(length);
w = length - 6;
// process chunk if we care about it, otherwise skip it
switch (type)
{
case TRI_VERTEXL:
w -= ParseVertexL (input);
goto ParseSubchunk;
case TRI_FACEL1:
w -= ParseFaceL1 (input);
goto ParseSubchunk;
case EDIT_OBJECT:
// read the name
i = 0;
do
{
if (feof(input))
Error ("Error: unexpected end of file");
fread (&temp[i], 1, 1, input);
i++;
w--;
bytesread++;
} while (temp[i-1]);
case MAIN3DS:
case OBJ_TRIMESH:
case EDIT3DS:
// parse through subchunks
ParseSubchunk:
while (w > 0)
{
w -= ParseChunk (input);
}
retval = length;
goto Done;
default:
// skip other chunks
while (w > 0)
{
t = w;
if (t > BLOCK_SIZE)
t = BLOCK_SIZE;
if (feof(input))
Error ("Error: unexpected end of file");
fread (&temp, t, 1, input);
bytesread += t;
w -= t;
}
retval = length;
goto Done;
}
Done:
level--;
return retval;
}
void Load3DSTriangleList (char *filename, triangle_t **pptri, int *numtriangles)
{
FILE *input;
short int tshort;
bytesread = 0;
level = 0;
numtris = 0;
totaltris = 0;
vertsfound = 0;
trisfound = 0;
if ((input = fopen(filename, "rb")) == 0) {
fprintf(stderr,"reader: could not open file '%s'\n", filename);
exit(0);
}
fread(&tshort, sizeof(tshort), 1, input);
// should only be MAIN3DS, but some files seem to start with EDIT3DS, with
// no MAIN3DS
if ((tshort != MAIN3DS) && (tshort != EDIT3DS)) {
fprintf(stderr,"File is not a 3DS file.\n");
exit(0);
}
// back to top of file so we can parse the first chunk descriptor
fseek(input, 0, SEEK_SET);
ptri = safe_malloc (MAXTRIANGLES * sizeof(triangle_t));
*pptri = ptri;
// parse through looking for the relevant chunk tree (MAIN3DS | EDIT3DS | EDIT_OBJECT |
// OBJ_TRIMESH | {TRI_VERTEXL, TRI_FACEL1}) and skipping other chunks
ParseChunk (input);
if (vertsfound || trisfound)
Error ("Incomplete triangle set");
*numtriangles = totaltris;
fclose (input);
}

View File

@@ -0,0 +1,26 @@
/*
Copyright (C) 1999-2006 Id Software, Inc. and contributors.
For a list of contributors, see the accompanying CONTRIBUTORS file.
This file is part of GtkRadiant.
GtkRadiant is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
GtkRadiant is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
//
// l3dslib.h: header file for loading triangles from a 3DS triangle file
//
void Load3DSTriangleList (char *filename, triangle_t **pptri, int *numtriangles);

298
tools/quake3/common/md4.c Normal file
View File

@@ -0,0 +1,298 @@
/*
Copyright (C) 1999-2006 Id Software, Inc. and contributors.
For a list of contributors, see the accompanying CONTRIBUTORS file.
This file is part of GtkRadiant.
GtkRadiant is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
GtkRadiant is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
/* GLOBAL.H - RSAREF types and constants */
#include <string.h>
/* POINTER defines a generic pointer type */
typedef unsigned char *POINTER;
/* UINT2 defines a two byte word */
typedef unsigned short int UINT2;
/* UINT4 defines a four byte word */
typedef unsigned long int UINT4;
/* MD4.H - header file for MD4C.C */
/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991.
All rights reserved.
License to copy and use this software is granted provided that it is identified as the “RSA Data Security, Inc. MD4 Message-Digest Algorithm” in all material mentioning or referencing this software or this function.
License is also granted to make and use derivative works provided that such works are identified as “derived from the RSA Data Security, Inc. MD4 Message-Digest Algorithm” in all material mentioning or referencing the derived work.
RSA Data Security, Inc. makes no representations concerning either the merchantability of this software or the suitability of this software for any particular purpose. It is provided “as is” without express or implied warranty of any kind.
These notices must be retained in any copies of any part of this documentation and/or software. */
/* MD4 context. */
typedef struct {
UINT4 state[4]; /* state (ABCD) */
UINT4 count[2]; /* number of bits, modulo 2^64 (lsb first) */
unsigned char buffer[64]; /* input buffer */
} MD4_CTX;
void MD4Init (MD4_CTX *);
void MD4Update (MD4_CTX *, unsigned char *, unsigned int);
void MD4Final (unsigned char [16], MD4_CTX *);
/* MD4C.C - RSA Data Security, Inc., MD4 message-digest algorithm */
/* Copyright (C) 1990-2, RSA Data Security, Inc. All rights reserved.
License to copy and use this software is granted provided that it is identified as the
RSA Data Security, Inc. MD4 Message-Digest Algorithm
in all material mentioning or referencing this software or this function.
License is also granted to make and use derivative works provided that such works are identified as
derived from the RSA Data Security, Inc. MD4 Message-Digest Algorithm
in all material mentioning or referencing the derived work.
RSA Data Security, Inc. makes no representations concerning either the merchantability of this software or the suitability of this software for any particular purpose. It is provided
as is without express or implied warranty of any kind.
These notices must be retained in any copies of any part of this documentation and/or software. */
/* Constants for MD4Transform routine. */
#define S11 3
#define S12 7
#define S13 11
#define S14 19
#define S21 3
#define S22 5
#define S23 9
#define S24 13
#define S31 3
#define S32 9
#define S33 11
#define S34 15
static void MD4Transform (UINT4 [4], unsigned char [64]);
static void Encode (unsigned char *, UINT4 *, unsigned int);
static void Decode (UINT4 *, unsigned char *, unsigned int);
static void MD4_memcpy (POINTER, POINTER, unsigned int);
static void MD4_memset (POINTER, int, unsigned int);
static unsigned char PADDING[64] = {
0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
/* F, G and H are basic MD4 functions. */
#define F(x, y, z) (((x) & (y)) | ((~x) & (z)))
#define G(x, y, z) (((x) & (y)) | ((x) & (z)) | ((y) & (z)))
#define H(x, y, z) ((x) ^ (y) ^ (z))
/* ROTATE_LEFT rotates x left n bits. */
#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n))))
/* FF, GG and HH are transformations for rounds 1, 2 and 3 */
/* Rotation is separate from addition to prevent recomputation */
#define FF(a, b, c, d, x, s) {(a) += F ((b), (c), (d)) + (x); (a) = ROTATE_LEFT ((a), (s));}
#define GG(a, b, c, d, x, s) {(a) += G ((b), (c), (d)) + (x) + (UINT4)0x5a827999; (a) = ROTATE_LEFT ((a), (s));}
#define HH(a, b, c, d, x, s) {(a) += H ((b), (c), (d)) + (x) + (UINT4)0x6ed9eba1; (a) = \
ROTATE_LEFT ((a), (s)); }
/* MD4 initialization. Begins an MD4 operation, writing a new context. */
void MD4Init (MD4_CTX *context)
{
context->count[0] = context->count[1] = 0;
/* Load magic initialization constants.*/
context->state[0] = 0x67452301;
context->state[1] = 0xefcdab89;
context->state[2] = 0x98badcfe;
context->state[3] = 0x10325476;
}
/* MD4 block update operation. Continues an MD4 message-digest operation, processing another message block, and updating the context. */
void MD4Update (MD4_CTX *context, unsigned char *input, unsigned int inputLen)
{
unsigned int i, index, partLen;
/* Compute number of bytes mod 64 */
index = (unsigned int)((context->count[0] >> 3) & 0x3F);
/* Update number of bits */
if ((context->count[0] += ((UINT4)inputLen << 3))< ((UINT4)inputLen << 3))
context->count[1]++;
context->count[1] += ((UINT4)inputLen >> 29);
partLen = 64 - index;
/* Transform as many times as possible.*/
if (inputLen >= partLen)
{
memcpy((POINTER)&context->buffer[index], (POINTER)input, partLen);
MD4Transform (context->state, context->buffer);
for (i = partLen; i + 63 < inputLen; i += 64)
MD4Transform (context->state, &input[i]);
index = 0;
}
else
i = 0;
/* Buffer remaining input */
memcpy ((POINTER)&context->buffer[index], (POINTER)&input[i], inputLen-i);
}
/* MD4 finalization. Ends an MD4 message-digest operation, writing the the message digest and zeroizing the context. */
void MD4Final (unsigned char digest[16], MD4_CTX *context)
{
unsigned char bits[8];
unsigned int index, padLen;
/* Save number of bits */
Encode (bits, context->count, 8);
/* Pad out to 56 mod 64.*/
index = (unsigned int)((context->count[0] >> 3) & 0x3f);
padLen = (index < 56) ? (56 - index) : (120 - index);
MD4Update (context, PADDING, padLen);
/* Append length (before padding) */
MD4Update (context, bits, 8);
/* Store state in digest */
Encode (digest, context->state, 16);
/* Zeroize sensitive information.*/
memset ((POINTER)context, 0, sizeof (*context));
}
/* MD4 basic transformation. Transforms state based on block. */
static void MD4Transform (UINT4 state[4], unsigned char block[64])
{
UINT4 a = state[0], b = state[1], c = state[2], d = state[3], x[16];
Decode (x, block, 64);
/* Round 1 */
FF (a, b, c, d, x[ 0], S11); /* 1 */
FF (d, a, b, c, x[ 1], S12); /* 2 */
FF (c, d, a, b, x[ 2], S13); /* 3 */
FF (b, c, d, a, x[ 3], S14); /* 4 */
FF (a, b, c, d, x[ 4], S11); /* 5 */
FF (d, a, b, c, x[ 5], S12); /* 6 */
FF (c, d, a, b, x[ 6], S13); /* 7 */
FF (b, c, d, a, x[ 7], S14); /* 8 */
FF (a, b, c, d, x[ 8], S11); /* 9 */
FF (d, a, b, c, x[ 9], S12); /* 10 */
FF (c, d, a, b, x[10], S13); /* 11 */
FF (b, c, d, a, x[11], S14); /* 12 */
FF (a, b, c, d, x[12], S11); /* 13 */
FF (d, a, b, c, x[13], S12); /* 14 */
FF (c, d, a, b, x[14], S13); /* 15 */
FF (b, c, d, a, x[15], S14); /* 16 */
/* Round 2 */
GG (a, b, c, d, x[ 0], S21); /* 17 */
GG (d, a, b, c, x[ 4], S22); /* 18 */
GG (c, d, a, b, x[ 8], S23); /* 19 */
GG (b, c, d, a, x[12], S24); /* 20 */
GG (a, b, c, d, x[ 1], S21); /* 21 */
GG (d, a, b, c, x[ 5], S22); /* 22 */
GG (c, d, a, b, x[ 9], S23); /* 23 */
GG (b, c, d, a, x[13], S24); /* 24 */
GG (a, b, c, d, x[ 2], S21); /* 25 */
GG (d, a, b, c, x[ 6], S22); /* 26 */
GG (c, d, a, b, x[10], S23); /* 27 */
GG (b, c, d, a, x[14], S24); /* 28 */
GG (a, b, c, d, x[ 3], S21); /* 29 */
GG (d, a, b, c, x[ 7], S22); /* 30 */
GG (c, d, a, b, x[11], S23); /* 31 */
GG (b, c, d, a, x[15], S24); /* 32 */
/* Round 3 */
HH (a, b, c, d, x[ 0], S31); /* 33 */
HH (d, a, b, c, x[ 8], S32); /* 34 */
HH (c, d, a, b, x[ 4], S33); /* 35 */
HH (b, c, d, a, x[12], S34); /* 36 */
HH (a, b, c, d, x[ 2], S31); /* 37 */
HH (d, a, b, c, x[10], S32); /* 38 */
HH (c, d, a, b, x[ 6], S33); /* 39 */
HH (b, c, d, a, x[14], S34); /* 40 */
HH (a, b, c, d, x[ 1], S31); /* 41 */
HH (d, a, b, c, x[ 9], S32); /* 42 */
HH (c, d, a, b, x[ 5], S33); /* 43 */
HH (b, c, d, a, x[13], S34); /* 44 */
HH (a, b, c, d, x[ 3], S31); /* 45 */
HH (d, a, b, c, x[11], S32); /* 46 */
HH (c, d, a, b, x[ 7], S33); /* 47 */
HH (b, c, d, a, x[15], S34); /* 48 */
state[0] += a;
state[1] += b;
state[2] += c;
state[3] += d;
/* Zeroize sensitive information.*/
memset ((POINTER)x, 0, sizeof (x));
}
/* Encodes input (UINT4) into output (unsigned char). Assumes len is a multiple of 4. */
static void Encode (unsigned char *output, UINT4 *input, unsigned int len)
{
unsigned int i, j;
for (i = 0, j = 0; j < len; i++, j += 4) {
output[j] = (unsigned char)(input[i] & 0xff);
output[j+1] = (unsigned char)((input[i] >> 8) & 0xff);
output[j+2] = (unsigned char)((input[i] >> 16) & 0xff);
output[j+3] = (unsigned char)((input[i] >> 24) & 0xff);
}
}
/* Decodes input (unsigned char) into output (UINT4). Assumes len is a multiple of 4. */
static void Decode (UINT4 *output, unsigned char *input, unsigned int len)
{
unsigned int i, j;
for (i = 0, j = 0; j < len; i++, j += 4)
output[i] = ((UINT4)input[j]) | (((UINT4)input[j+1]) << 8) | (((UINT4)input[j+2]) << 16) | (((UINT4)input[j+3]) << 24);
}
//===================================================================
unsigned Com_BlockChecksum (void *buffer, int length)
{
int digest[4];
unsigned val;
MD4_CTX ctx;
MD4Init (&ctx);
MD4Update (&ctx, (unsigned char *)buffer, length);
MD4Final ( (unsigned char *)digest, &ctx);
val = digest[0] ^ digest[1] ^ digest[2] ^ digest[3];
return val;
}

197
tools/quake3/common/mutex.c Normal file
View File

@@ -0,0 +1,197 @@
/*
Copyright (C) 1999-2006 Id Software, Inc. and contributors.
For a list of contributors, see the accompanying CONTRIBUTORS file.
This file is part of GtkRadiant.
GtkRadiant is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
GtkRadiant is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "cmdlib.h"
#include "qthreads.h"
#include "mutex.h"
/*
===================================================================
WIN32
===================================================================
*/
#ifdef WIN32
#define USED
#include <windows.h>
void MutexLock (mutex_t *m)
{
CRITICAL_SECTION *crit;
if (!m)
return;
crit = (CRITICAL_SECTION *) m;
EnterCriticalSection (crit);
}
void MutexUnlock (mutex_t *m)
{
CRITICAL_SECTION *crit;
if (!m)
return;
crit = (CRITICAL_SECTION *) m;
LeaveCriticalSection (crit);
}
mutex_t *MutexAlloc(void)
{
CRITICAL_SECTION *crit;
if (numthreads == 1)
return NULL;
crit = (CRITICAL_SECTION *) safe_malloc(sizeof(CRITICAL_SECTION));
InitializeCriticalSection (crit);
return (void *) crit;
}
#endif
/*
===================================================================
OSF1
===================================================================
*/
#ifdef __osf__
#define USED
#include <pthread.h>
void MutexLock (mutex_t *m)
{
pthread_mutex_t *my_mutex;
if (!m)
return;
my_mutex = (pthread_mutex_t *) m;
pthread_mutex_lock (my_mutex);
}
void MutexUnlock (mutex_t *m)
{
pthread_mutex_t *my_mutex;
if (!m)
return;
my_mutex = (pthread_mutex_t *) m;
pthread_mutex_unlock (my_mutex);
}
mutex_t *MutexAlloc(void)
{
pthread_mutex_t *my_mutex;
pthread_mutexattr_t mattrib;
if (numthreads == 1)
return NULL;
my_mutex = safe_malloc (sizeof(*my_mutex));
if (pthread_mutexattr_create (&mattrib) == -1)
Error ("pthread_mutex_attr_create failed");
if (pthread_mutexattr_setkind_np (&mattrib, MUTEX_FAST_NP) == -1)
Error ("pthread_mutexattr_setkind_np failed");
if (pthread_mutex_init (my_mutex, mattrib) == -1)
Error ("pthread_mutex_init failed");
return (void *) my_mutex;
}
#endif
/*
===================================================================
IRIX
===================================================================
*/
#ifdef _MIPS_ISA
#define USED
#include <task.h>
#include <abi_mutex.h>
#include <sys/types.h>
#include <sys/prctl.h>
void MutexLock (mutex_t *m)
{
abilock_t *lck;
if (!m)
return;
lck = (abilock_t *) m;
spin_lock (lck);
}
void MutexUnlock (mutex_t *m)
{
abilock_t *lck;
if (!m)
return;
lck = (abilock_t *) m;
release_lock (lck);
}
mutex_t *MutexAlloc(void)
{
abilock_t *lck;
if (numthreads == 1)
return NULL;
lck = (abilock_t *) safe_malloc(sizeof(abilock_t));
init_lock (lck);
return (void *) lck;
}
#endif
/*
=======================================================================
SINGLE THREAD
=======================================================================
*/
#ifndef USED
void MutexLock (mutex_t *m)
{
}
void MutexUnlock (mutex_t *m)
{
}
mutex_t *MutexAlloc(void)
{
return NULL;
}
#endif

View File

@@ -0,0 +1,28 @@
/*
Copyright (C) 1999-2006 Id Software, Inc. and contributors.
For a list of contributors, see the accompanying CONTRIBUTORS file.
This file is part of GtkRadiant.
GtkRadiant is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
GtkRadiant is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
typedef void *mutex_t;
void MutexLock (mutex_t *m);
void MutexUnlock (mutex_t *m);
mutex_t *MutexAlloc(void);

View File

@@ -0,0 +1,745 @@
/*
Copyright (C) 1999-2006 Id Software, Inc. and contributors.
For a list of contributors, see the accompanying CONTRIBUTORS file.
This file is part of GtkRadiant.
GtkRadiant is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
GtkRadiant is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "cmdlib.h"
#include "mathlib.h"
#include "inout.h"
#include "polylib.h"
#include "qfiles.h"
extern int numthreads;
// counters are only bumped when running single threaded,
// because they are an awefull coherence problem
int c_active_windings;
int c_peak_windings;
int c_winding_allocs;
int c_winding_points;
#define BOGUS_RANGE WORLD_SIZE
void pw(winding_t *w)
{
int i;
for (i=0 ; i<w->numpoints ; i++)
Sys_Printf ("(%5.1f, %5.1f, %5.1f)\n",w->p[i][0], w->p[i][1],w->p[i][2]);
}
/*
=============
AllocWinding
=============
*/
winding_t *AllocWinding (int points)
{
winding_t *w;
int s;
if (points >= MAX_POINTS_ON_WINDING)
Error ("AllocWinding failed: MAX_POINTS_ON_WINDING exceeded");
if (numthreads == 1)
{
c_winding_allocs++;
c_winding_points += points;
c_active_windings++;
if (c_active_windings > c_peak_windings)
c_peak_windings = c_active_windings;
}
s = sizeof(vec_t)*3*points + sizeof(int);
w = safe_malloc (s);
memset (w, 0, s);
return w;
}
void FreeWinding (winding_t *w)
{
if (*(unsigned *)w == 0xdeaddead)
Error ("FreeWinding: freed a freed winding");
*(unsigned *)w = 0xdeaddead;
if (numthreads == 1)
c_active_windings--;
free (w);
}
/*
============
RemoveColinearPoints
============
*/
int c_removed;
void RemoveColinearPoints (winding_t *w)
{
int i, j, k;
vec3_t v1, v2;
int nump;
vec3_t p[MAX_POINTS_ON_WINDING];
nump = 0;
for (i=0 ; i<w->numpoints ; i++)
{
j = (i+1)%w->numpoints;
k = (i+w->numpoints-1)%w->numpoints;
VectorSubtract (w->p[j], w->p[i], v1);
VectorSubtract (w->p[i], w->p[k], v2);
VectorNormalize(v1,v1);
VectorNormalize(v2,v2);
if (DotProduct(v1, v2) < 0.999)
{
VectorCopy (w->p[i], p[nump]);
nump++;
}
}
if (nump == w->numpoints)
return;
if (numthreads == 1)
c_removed += w->numpoints - nump;
w->numpoints = nump;
memcpy (w->p, p, nump*sizeof(p[0]));
}
/*
============
WindingPlane
============
*/
void WindingPlane (winding_t *w, vec3_t normal, vec_t *dist)
{
vec3_t v1, v2;
VectorSubtract (w->p[1], w->p[0], v1);
VectorSubtract (w->p[2], w->p[0], v2);
CrossProduct (v2, v1, normal);
VectorNormalize (normal, normal);
*dist = DotProduct (w->p[0], normal);
}
/*
=============
WindingArea
=============
*/
vec_t WindingArea (winding_t *w)
{
int i;
vec3_t d1, d2, cross;
vec_t total;
total = 0;
for (i=2 ; i<w->numpoints ; i++)
{
VectorSubtract (w->p[i-1], w->p[0], d1);
VectorSubtract (w->p[i], w->p[0], d2);
CrossProduct (d1, d2, cross);
total += 0.5 * VectorLength ( cross );
}
return total;
}
void WindingBounds (winding_t *w, vec3_t mins, vec3_t maxs)
{
vec_t v;
int i,j;
mins[0] = mins[1] = mins[2] = 99999;
maxs[0] = maxs[1] = maxs[2] = -99999;
for (i=0 ; i<w->numpoints ; i++)
{
for (j=0 ; j<3 ; j++)
{
v = w->p[i][j];
if (v < mins[j])
mins[j] = v;
if (v > maxs[j])
maxs[j] = v;
}
}
}
/*
=============
WindingCenter
=============
*/
void WindingCenter (winding_t *w, vec3_t center)
{
int i;
float scale;
VectorCopy (vec3_origin, center);
for (i=0 ; i<w->numpoints ; i++)
VectorAdd (w->p[i], center, center);
scale = 1.0/w->numpoints;
VectorScale (center, scale, center);
}
/*
=================
BaseWindingForPlane
=================
*/
winding_t *BaseWindingForPlane (vec3_t normal, vec_t dist)
{
int i, x;
vec_t max, v;
vec3_t org, vright, vup;
winding_t *w;
// find the major axis
max = -BOGUS_RANGE;
x = -1;
for (i=0 ; i<3; i++)
{
v = fabs(normal[i]);
if (v > max)
{
x = i;
max = v;
}
}
if (x==-1)
Error ("BaseWindingForPlane: no axis found");
VectorCopy (vec3_origin, vup);
switch (x)
{
case 0:
case 1:
vup[2] = 1;
break;
case 2:
vup[0] = 1;
break;
}
v = DotProduct (vup, normal);
VectorMA (vup, -v, normal, vup);
VectorNormalize (vup, vup);
VectorScale (normal, dist, org);
CrossProduct (vup, normal, vright);
VectorScale (vup, MAX_WORLD_COORD, vup);
VectorScale (vright, MAX_WORLD_COORD, vright);
// project a really big axis aligned box onto the plane
w = AllocWinding (4);
VectorSubtract (org, vright, w->p[0]);
VectorAdd (w->p[0], vup, w->p[0]);
VectorAdd (org, vright, w->p[1]);
VectorAdd (w->p[1], vup, w->p[1]);
VectorAdd (org, vright, w->p[2]);
VectorSubtract (w->p[2], vup, w->p[2]);
VectorSubtract (org, vright, w->p[3]);
VectorSubtract (w->p[3], vup, w->p[3]);
w->numpoints = 4;
return w;
}
/*
==================
CopyWinding
==================
*/
winding_t *CopyWinding (winding_t *w)
{
int size;
winding_t *c;
c = AllocWinding (w->numpoints);
size = (int)((winding_t *)0)->p[w->numpoints];
memcpy (c, w, size);
return c;
}
/*
==================
ReverseWinding
==================
*/
winding_t *ReverseWinding (winding_t *w)
{
int i;
winding_t *c;
c = AllocWinding (w->numpoints);
for (i=0 ; i<w->numpoints ; i++)
{
VectorCopy (w->p[w->numpoints-1-i], c->p[i]);
}
c->numpoints = w->numpoints;
return c;
}
/*
=============
ClipWindingEpsilon
=============
*/
void ClipWindingEpsilon (winding_t *in, vec3_t normal, vec_t dist,
vec_t epsilon, winding_t **front, winding_t **back)
{
vec_t dists[MAX_POINTS_ON_WINDING+4];
int sides[MAX_POINTS_ON_WINDING+4];
int counts[3];
static vec_t dot; // VC 4.2 optimizer bug if not static
int i, j;
vec_t *p1, *p2;
vec3_t mid;
winding_t *f, *b;
int maxpts;
counts[0] = counts[1] = counts[2] = 0;
// determine sides for each point
for (i=0 ; i<in->numpoints ; i++)
{
dot = DotProduct (in->p[i], normal);
dot -= dist;
dists[i] = dot;
if (dot > epsilon)
sides[i] = SIDE_FRONT;
else if (dot < -epsilon)
sides[i] = SIDE_BACK;
else
{
sides[i] = SIDE_ON;
}
counts[sides[i]]++;
}
sides[i] = sides[0];
dists[i] = dists[0];
*front = *back = NULL;
if (!counts[0])
{
*back = CopyWinding (in);
return;
}
if (!counts[1])
{
*front = CopyWinding (in);
return;
}
maxpts = in->numpoints+4; // cant use counts[0]+2 because
// of fp grouping errors
*front = f = AllocWinding (maxpts);
*back = b = AllocWinding (maxpts);
for (i=0 ; i<in->numpoints ; i++)
{
p1 = in->p[i];
if (sides[i] == SIDE_ON)
{
VectorCopy (p1, f->p[f->numpoints]);
f->numpoints++;
VectorCopy (p1, b->p[b->numpoints]);
b->numpoints++;
continue;
}
if (sides[i] == SIDE_FRONT)
{
VectorCopy (p1, f->p[f->numpoints]);
f->numpoints++;
}
if (sides[i] == SIDE_BACK)
{
VectorCopy (p1, b->p[b->numpoints]);
b->numpoints++;
}
if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
continue;
// generate a split point
p2 = in->p[(i+1)%in->numpoints];
dot = dists[i] / (dists[i]-dists[i+1]);
for (j=0 ; j<3 ; j++)
{ // avoid round off error when possible
if (normal[j] == 1)
mid[j] = dist;
else if (normal[j] == -1)
mid[j] = -dist;
else
mid[j] = p1[j] + dot*(p2[j]-p1[j]);
}
VectorCopy (mid, f->p[f->numpoints]);
f->numpoints++;
VectorCopy (mid, b->p[b->numpoints]);
b->numpoints++;
}
if (f->numpoints > maxpts || b->numpoints > maxpts)
Error ("ClipWinding: points exceeded estimate");
if (f->numpoints > MAX_POINTS_ON_WINDING || b->numpoints > MAX_POINTS_ON_WINDING)
Error ("ClipWinding: MAX_POINTS_ON_WINDING");
}
/*
=============
ChopWindingInPlace
=============
*/
void ChopWindingInPlace (winding_t **inout, vec3_t normal, vec_t dist, vec_t epsilon)
{
winding_t *in;
vec_t dists[MAX_POINTS_ON_WINDING+4];
int sides[MAX_POINTS_ON_WINDING+4];
int counts[3];
static vec_t dot; // VC 4.2 optimizer bug if not static
int i, j;
vec_t *p1, *p2;
vec3_t mid;
winding_t *f;
int maxpts;
in = *inout;
counts[0] = counts[1] = counts[2] = 0;
// determine sides for each point
for (i=0 ; i<in->numpoints ; i++)
{
dot = DotProduct (in->p[i], normal);
dot -= dist;
dists[i] = dot;
if (dot > epsilon)
sides[i] = SIDE_FRONT;
else if (dot < -epsilon)
sides[i] = SIDE_BACK;
else
{
sides[i] = SIDE_ON;
}
counts[sides[i]]++;
}
sides[i] = sides[0];
dists[i] = dists[0];
if (!counts[0])
{
FreeWinding (in);
*inout = NULL;
return;
}
if (!counts[1])
return; // inout stays the same
maxpts = in->numpoints+4; // cant use counts[0]+2 because
// of fp grouping errors
f = AllocWinding (maxpts);
for (i=0 ; i<in->numpoints ; i++)
{
p1 = in->p[i];
if (sides[i] == SIDE_ON)
{
VectorCopy (p1, f->p[f->numpoints]);
f->numpoints++;
continue;
}
if (sides[i] == SIDE_FRONT)
{
VectorCopy (p1, f->p[f->numpoints]);
f->numpoints++;
}
if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
continue;
// generate a split point
p2 = in->p[(i+1)%in->numpoints];
dot = dists[i] / (dists[i]-dists[i+1]);
for (j=0 ; j<3 ; j++)
{ // avoid round off error when possible
if (normal[j] == 1)
mid[j] = dist;
else if (normal[j] == -1)
mid[j] = -dist;
else
mid[j] = p1[j] + dot*(p2[j]-p1[j]);
}
VectorCopy (mid, f->p[f->numpoints]);
f->numpoints++;
}
if (f->numpoints > maxpts)
Error ("ClipWinding: points exceeded estimate");
if (f->numpoints > MAX_POINTS_ON_WINDING)
Error ("ClipWinding: MAX_POINTS_ON_WINDING");
FreeWinding (in);
*inout = f;
}
/*
=================
ChopWinding
Returns the fragment of in that is on the front side
of the cliping plane. The original is freed.
=================
*/
winding_t *ChopWinding (winding_t *in, vec3_t normal, vec_t dist)
{
winding_t *f, *b;
ClipWindingEpsilon (in, normal, dist, ON_EPSILON, &f, &b);
FreeWinding (in);
if (b)
FreeWinding (b);
return f;
}
/*
=================
CheckWinding
=================
*/
void CheckWinding (winding_t *w)
{
int i, j;
vec_t *p1, *p2;
vec_t d, edgedist;
vec3_t dir, edgenormal, facenormal;
vec_t area;
vec_t facedist;
if (w->numpoints < 3)
Error ("CheckWinding: %i points",w->numpoints);
area = WindingArea(w);
if (area < 1)
Error ("CheckWinding: %f area", area);
WindingPlane (w, facenormal, &facedist);
for (i=0 ; i<w->numpoints ; i++)
{
p1 = w->p[i];
for (j=0 ; j<3 ; j++)
if (p1[j] > MAX_WORLD_COORD || p1[j] < MIN_WORLD_COORD)
Error ("CheckFace: MAX_WORLD_COORD exceeded: %f",p1[j]);
j = i+1 == w->numpoints ? 0 : i+1;
// check the point is on the face plane
d = DotProduct (p1, facenormal) - facedist;
if (d < -ON_EPSILON || d > ON_EPSILON)
Error ("CheckWinding: point off plane");
// check the edge isnt degenerate
p2 = w->p[j];
VectorSubtract (p2, p1, dir);
if (VectorLength (dir) < ON_EPSILON)
Error ("CheckWinding: degenerate edge");
CrossProduct (facenormal, dir, edgenormal);
VectorNormalize (edgenormal, edgenormal);
edgedist = DotProduct (p1, edgenormal);
edgedist += ON_EPSILON;
// all other points must be on front side
for (j=0 ; j<w->numpoints ; j++)
{
if (j == i)
continue;
d = DotProduct (w->p[j], edgenormal);
if (d > edgedist)
Error ("CheckWinding: non-convex");
}
}
}
/*
============
WindingOnPlaneSide
============
*/
int WindingOnPlaneSide (winding_t *w, vec3_t normal, vec_t dist)
{
qboolean front, back;
int i;
vec_t d;
front = qfalse;
back = qfalse;
for (i=0 ; i<w->numpoints ; i++)
{
d = DotProduct (w->p[i], normal) - dist;
if (d < -ON_EPSILON)
{
if (front)
return SIDE_CROSS;
back = qtrue;
continue;
}
if (d > ON_EPSILON)
{
if (back)
return SIDE_CROSS;
front = qtrue;
continue;
}
}
if (back)
return SIDE_BACK;
if (front)
return SIDE_FRONT;
return SIDE_ON;
}
/*
=================
AddWindingToConvexHull
Both w and *hull are on the same plane
=================
*/
#define MAX_HULL_POINTS 128
void AddWindingToConvexHull( winding_t *w, winding_t **hull, vec3_t normal ) {
int i, j, k;
float *p, *copy;
vec3_t dir;
float d;
int numHullPoints, numNew;
vec3_t hullPoints[MAX_HULL_POINTS];
vec3_t newHullPoints[MAX_HULL_POINTS];
vec3_t hullDirs[MAX_HULL_POINTS];
qboolean hullSide[MAX_HULL_POINTS];
qboolean outside;
if ( !*hull ) {
*hull = CopyWinding( w );
return;
}
numHullPoints = (*hull)->numpoints;
memcpy( hullPoints, (*hull)->p, numHullPoints * sizeof(vec3_t) );
for ( i = 0 ; i < w->numpoints ; i++ ) {
p = w->p[i];
// calculate hull side vectors
for ( j = 0 ; j < numHullPoints ; j++ ) {
k = ( j + 1 ) % numHullPoints;
VectorSubtract( hullPoints[k], hullPoints[j], dir );
VectorNormalize( dir, dir );
CrossProduct( normal, dir, hullDirs[j] );
}
outside = qfalse;
for ( j = 0 ; j < numHullPoints ; j++ ) {
VectorSubtract( p, hullPoints[j], dir );
d = DotProduct( dir, hullDirs[j] );
if ( d >= ON_EPSILON ) {
outside = qtrue;
}
if ( d >= -ON_EPSILON ) {
hullSide[j] = qtrue;
} else {
hullSide[j] = qfalse;
}
}
// if the point is effectively inside, do nothing
if ( !outside ) {
continue;
}
// find the back side to front side transition
for ( j = 0 ; j < numHullPoints ; j++ ) {
if ( !hullSide[ j % numHullPoints ] && hullSide[ (j + 1) % numHullPoints ] ) {
break;
}
}
if ( j == numHullPoints ) {
continue;
}
// insert the point here
VectorCopy( p, newHullPoints[0] );
numNew = 1;
// copy over all points that aren't double fronts
j = (j+1)%numHullPoints;
for ( k = 0 ; k < numHullPoints ; k++ ) {
if ( hullSide[ (j+k) % numHullPoints ] && hullSide[ (j+k+1) % numHullPoints ] ) {
continue;
}
copy = hullPoints[ (j+k+1) % numHullPoints ];
VectorCopy( copy, newHullPoints[numNew] );
numNew++;
}
numHullPoints = numNew;
memcpy( hullPoints, newHullPoints, numHullPoints * sizeof(vec3_t) );
}
FreeWinding( *hull );
w = AllocWinding( numHullPoints );
w->numpoints = numHullPoints;
*hull = w;
memcpy( w->p, hullPoints, numHullPoints * sizeof(vec3_t) );
}

View File

@@ -0,0 +1,57 @@
/*
Copyright (C) 1999-2006 Id Software, Inc. and contributors.
For a list of contributors, see the accompanying CONTRIBUTORS file.
This file is part of GtkRadiant.
GtkRadiant is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
GtkRadiant is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
typedef struct
{
int numpoints;
vec3_t p[4]; // variable sized
} winding_t;
#define MAX_POINTS_ON_WINDING 64
// you can define on_epsilon in the makefile as tighter
#ifndef ON_EPSILON
#define ON_EPSILON 0.1
#endif
winding_t *AllocWinding (int points);
vec_t WindingArea (winding_t *w);
void WindingCenter (winding_t *w, vec3_t center);
void ClipWindingEpsilon (winding_t *in, vec3_t normal, vec_t dist,
vec_t epsilon, winding_t **front, winding_t **back);
winding_t *ChopWinding (winding_t *in, vec3_t normal, vec_t dist);
winding_t *CopyWinding (winding_t *w);
winding_t *ReverseWinding (winding_t *w);
winding_t *BaseWindingForPlane (vec3_t normal, vec_t dist);
void CheckWinding (winding_t *w);
void WindingPlane (winding_t *w, vec3_t normal, vec_t *dist);
void RemoveColinearPoints (winding_t *w);
int WindingOnPlaneSide (winding_t *w, vec3_t normal, vec_t dist);
void FreeWinding (winding_t *w);
void WindingBounds (winding_t *w, vec3_t mins, vec3_t maxs);
void AddWindingToConvexHull( winding_t *w, winding_t **hull, vec3_t normal );
void ChopWindingInPlace (winding_t **w, vec3_t normal, vec_t dist, vec_t epsilon);
// frees the original if clipped
void pw(winding_t *w);

View File

@@ -0,0 +1,51 @@
/*
Copyright (C) 1999-2006 Id Software, Inc. and contributors.
For a list of contributors, see the accompanying CONTRIBUTORS file.
This file is part of GtkRadiant.
GtkRadiant is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
GtkRadiant is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef __POLYSET_H__
#define __POLYSET_H__
#define POLYSET_MAXTRIANGLES 4096
#define POLYSET_MAXPOLYSETS 64
typedef float st_t[2];
typedef float rgb_t[3];
typedef struct {
vec3_t verts[3];
vec3_t normals[3];
st_t texcoords[3];
} triangle_t;
typedef struct
{
char name[100];
char materialname[100];
triangle_t *triangles;
int numtriangles;
} polyset_t;
polyset_t *Polyset_LoadSets( const char *file, int *numpolysets, int maxTrisPerSet );
polyset_t *Polyset_CollapseSets( polyset_t *psets, int numpolysets );
polyset_t *Polyset_SplitSets( polyset_t *psets, int numpolysets, int *pNumNewPolysets, int maxTris );
void Polyset_SnapSets( polyset_t *psets, int numpolysets );
void Polyset_ComputeNormals( polyset_t *psets, int numpolysets );
#endif

View File

@@ -0,0 +1,489 @@
/*
Copyright (C) 1999-2006 Id Software, Inc. and contributors.
For a list of contributors, see the accompanying CONTRIBUTORS file.
This file is part of GtkRadiant.
GtkRadiant is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
GtkRadiant is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef __QFILES_H__
#define __QFILES_H__
//
// qfiles.h: quake file formats
// This file must be identical in the quake and utils directories
//
// surface geometry should not exceed these limits
#define SHADER_MAX_VERTEXES 1000
#define SHADER_MAX_INDEXES (6*SHADER_MAX_VERTEXES)
// the maximum size of game reletive pathnames
#define MAX_QPATH 64
/*
========================================================================
QVM files
========================================================================
*/
#define VM_MAGIC 0x12721444
typedef struct {
int vmMagic;
int instructionCount;
int codeOffset;
int codeLength;
int dataOffset;
int dataLength;
int litLength; // ( dataLength - litLength ) should be byteswapped on load
int bssLength; // zero filled memory appended to datalength
} vmHeader_t;
/*
========================================================================
PCX files are used for 8 bit images
========================================================================
*/
typedef struct {
char manufacturer;
char version;
char encoding;
char bits_per_pixel;
unsigned short xmin,ymin,xmax,ymax;
unsigned short hres,vres;
unsigned char palette[48];
char reserved;
char color_planes;
unsigned short bytes_per_line;
unsigned short palette_type;
char filler[58];
unsigned char data; // unbounded
} pcx_t;
/*
========================================================================
TGA files are used for 24/32 bit images
========================================================================
*/
typedef struct _TargaHeader {
unsigned char id_length, colormap_type, image_type;
unsigned short colormap_index, colormap_length;
unsigned char colormap_size;
unsigned short x_origin, y_origin, width, height;
unsigned char pixel_size, attributes;
} TargaHeader;
/*
========================================================================
.MD3 triangle model file format
========================================================================
*/
#define MD3_IDENT (('3'<<24)+('P'<<16)+('D'<<8)+'I')
#define MD3_VERSION 15
// limits
#define MD3_MAX_LODS 4
#define MD3_MAX_TRIANGLES 8192 // per surface
#define MD3_MAX_VERTS 4096 // per surface
#define MD3_MAX_SHADERS 256 // per surface
#define MD3_MAX_FRAMES 1024 // per model
#define MD3_MAX_SURFACES 32 // per model
#define MD3_MAX_TAGS 16 // per frame
// vertex scales
#define MD3_XYZ_SCALE (1.0/64)
typedef struct md3Frame_s {
vec3_t bounds[2];
vec3_t localOrigin;
float radius;
char name[16];
} md3Frame_t;
typedef struct md3Tag_s {
char name[MAX_QPATH]; // tag name
vec3_t origin;
vec3_t axis[3];
} md3Tag_t;
/*
** md3Surface_t
**
** CHUNK SIZE
** header sizeof( md3Surface_t )
** shaders sizeof( md3Shader_t ) * numShaders
** triangles[0] sizeof( md3Triangle_t ) * numTriangles
** st sizeof( md3St_t ) * numVerts
** XyzNormals sizeof( md3XyzNormal_t ) * numVerts * numFrames
*/
typedef struct {
int ident; //
char name[MAX_QPATH]; // polyset name
int flags;
int numFrames; // all surfaces in a model should have the same
int numShaders; // all surfaces in a model should have the same
int numVerts;
int numTriangles;
int ofsTriangles;
int ofsShaders; // offset from start of md3Surface_t
int ofsSt; // texture coords are common for all frames
int ofsXyzNormals; // numVerts * numFrames
int ofsEnd; // next surface follows
} md3Surface_t;
typedef struct {
char name[MAX_QPATH];
int shaderIndex; // for in-game use
} md3Shader_t;
typedef struct {
int indexes[3];
} md3Triangle_t;
typedef struct {
float st[2];
} md3St_t;
typedef struct {
short xyz[3];
short normal;
} md3XyzNormal_t;
typedef struct {
int ident;
int version;
char name[MAX_QPATH]; // model name
int flags;
int numFrames;
int numTags;
int numSurfaces;
int numSkins;
int ofsFrames; // offset for first frame
int ofsTags; // numFrames * numTags
int ofsSurfaces; // first surface, others follow
int ofsEnd; // end of file
} md3Header_t;
/*
==============================================================================
MD4 file format
==============================================================================
*/
#define MD4_IDENT (('4'<<24)+('P'<<16)+('D'<<8)+'I')
#define MD4_VERSION 1
#define MD4_MAX_BONES 128
typedef struct {
int boneIndex; // these are indexes into the boneReferences,
float boneWeight; // not the global per-frame bone list
} md4Weight_t;
typedef struct {
vec3_t vertex;
vec3_t normal;
float texCoords[2];
int numWeights;
md4Weight_t weights[1]; // variable sized
} md4Vertex_t;
typedef struct {
int indexes[3];
} md4Triangle_t;
typedef struct {
int ident;
char name[MAX_QPATH]; // polyset name
char shader[MAX_QPATH];
int shaderIndex; // for in-game use
int ofsHeader; // this will be a negative number
int numVerts;
int ofsVerts;
int numTriangles;
int ofsTriangles;
// Bone references are a set of ints representing all the bones
// present in any vertex weights for this surface. This is
// needed because a model may have surfaces that need to be
// drawn at different sort times, and we don't want to have
// to re-interpolate all the bones for each surface.
int numBoneReferences;
int ofsBoneReferences;
int ofsEnd; // next surface follows
} md4Surface_t;
typedef struct {
float matrix[3][4];
} md4Bone_t;
typedef struct {
vec3_t bounds[2]; // bounds of all surfaces of all LOD's for this frame
vec3_t localOrigin; // midpoint of bounds, used for sphere cull
float radius; // dist from localOrigin to corner
char name[16];
md4Bone_t bones[1]; // [numBones]
} md4Frame_t;
typedef struct {
int numSurfaces;
int ofsSurfaces; // first surface, others follow
int ofsEnd; // next lod follows
} md4LOD_t;
typedef struct {
int ident;
int version;
char name[MAX_QPATH]; // model name
// frames and bones are shared by all levels of detail
int numFrames;
int numBones;
int ofsFrames; // md4Frame_t[numFrames]
// each level of detail has completely separate sets of surfaces
int numLODs;
int ofsLODs;
int ofsEnd; // end of file
} md4Header_t;
/*
==============================================================================
.BSP file format
==============================================================================
*/
#define BSP_IDENT (('P'<<24)+('S'<<16)+('B'<<8)+'I')
// little-endian "IBSP"
//#define BSP_VERSION 46
#define Q3_BSP_VERSION 46
#define WOLF_BSP_VERSION 47
// there shouldn't be any problem with increasing these values at the
// expense of more memory allocation in the utilities
#define MAX_MAP_MODELS 0x400
#define MAX_MAP_BRUSHES 0x8000
#define MAX_MAP_ENTITIES 0x800
#define MAX_MAP_ENTSTRING 0x40000
#define MAX_MAP_SHADERS 0x400
#define MAX_MAP_AREAS 0x100 // MAX_MAP_AREA_BYTES in q_shared must match!
#define MAX_MAP_FOGS 0x100
#define MAX_MAP_PLANES 0x20000
#define MAX_MAP_NODES 0x20000
#define MAX_MAP_BRUSHSIDES 0x40000 //% 0x20000 /* ydnar */
#define MAX_MAP_LEAFS 0x20000
#define MAX_MAP_LEAFFACES 0x20000
#define MAX_MAP_LEAFBRUSHES 0x40000
#define MAX_MAP_PORTALS 0x20000
#define MAX_MAP_LIGHTING 0x800000
#define MAX_MAP_LIGHTGRID 0x800000
#define MAX_MAP_VISIBILITY 0x200000
#define MAX_MAP_DRAW_SURFS 0x20000
#define MAX_MAP_DRAW_VERTS 0x80000
#define MAX_MAP_DRAW_INDEXES 0x80000
// key / value pair sizes in the entities lump
#define MAX_KEY 32
#define MAX_VALUE 1024
// the editor uses these predefined yaw angles to orient entities up or down
#define ANGLE_UP -1
#define ANGLE_DOWN -2
#define LIGHTMAP_WIDTH 128
#define LIGHTMAP_HEIGHT 128
#define MIN_WORLD_COORD (-65536)
#define MAX_WORLD_COORD (65536)
#define WORLD_SIZE (MAX_WORLD_COORD - MIN_WORLD_COORD)
//=============================================================================
typedef struct {
int fileofs, filelen;
} lump_t;
#define LUMP_ENTITIES 0
#define LUMP_SHADERS 1
#define LUMP_PLANES 2
#define LUMP_NODES 3
#define LUMP_LEAFS 4
#define LUMP_LEAFSURFACES 5
#define LUMP_LEAFBRUSHES 6
#define LUMP_MODELS 7
#define LUMP_BRUSHES 8
#define LUMP_BRUSHSIDES 9
#define LUMP_DRAWVERTS 10
#define LUMP_DRAWINDEXES 11
#define LUMP_FOGS 12
#define LUMP_SURFACES 13
#define LUMP_LIGHTMAPS 14
#define LUMP_LIGHTGRID 15
#define LUMP_VISIBILITY 16
#define HEADER_LUMPS 17
typedef struct {
int ident;
int version;
lump_t lumps[HEADER_LUMPS];
} dheader_t;
typedef struct {
float mins[3], maxs[3];
int firstSurface, numSurfaces;
int firstBrush, numBrushes;
} dmodel_t;
typedef struct {
char shader[MAX_QPATH];
int surfaceFlags;
int contentFlags;
} dshader_t;
// planes x^1 is allways the opposite of plane x
typedef struct {
float normal[3];
float dist;
} dplane_t;
typedef struct {
int planeNum;
int children[2]; // negative numbers are -(leafs+1), not nodes
int mins[3]; // for frustom culling
int maxs[3];
} dnode_t;
typedef struct {
int cluster; // -1 = opaque cluster (do I still store these?)
int area;
int mins[3]; // for frustum culling
int maxs[3];
int firstLeafSurface;
int numLeafSurfaces;
int firstLeafBrush;
int numLeafBrushes;
} dleaf_t;
typedef struct {
int planeNum; // positive plane side faces out of the leaf
int shaderNum;
} dbrushside_t;
typedef struct {
int firstSide;
int numSides;
int shaderNum; // the shader that determines the contents flags
} dbrush_t;
typedef struct {
char shader[MAX_QPATH];
int brushNum;
int visibleSide; // the brush side that ray tests need to clip against (-1 == none)
} dfog_t;
typedef struct {
vec3_t xyz;
float st[2];
float lightmap[2];
vec3_t normal;
byte color[4];
} drawVert_t;
typedef enum {
MST_BAD,
MST_PLANAR,
MST_PATCH,
MST_TRIANGLE_SOUP,
MST_FLARE
} mapSurfaceType_t;
typedef struct {
int shaderNum;
int fogNum;
int surfaceType;
int firstVert;
int numVerts;
int firstIndex;
int numIndexes;
int lightmapNum;
int lightmapX, lightmapY;
int lightmapWidth, lightmapHeight;
vec3_t lightmapOrigin;
vec3_t lightmapVecs[3]; // for patches, [0] and [1] are lodbounds
int patchWidth;
int patchHeight;
} dsurface_t;
#endif

View File

@@ -0,0 +1,31 @@
/*
Copyright (C) 1999-2006 Id Software, Inc. and contributors.
For a list of contributors, see the accompanying CONTRIBUTORS file.
This file is part of GtkRadiant.
GtkRadiant is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
GtkRadiant is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
extern int numthreads;
void ThreadSetDefault (void);
int GetThreadWork (void);
void RunThreadsOnIndividual (int workcnt, qboolean showpacifier, void(*func)(int));
void RunThreadsOn (int workcnt, qboolean showpacifier, void(*func)(int));
void ThreadLock (void);
void ThreadUnlock (void);

View File

@@ -0,0 +1,409 @@
/*
Copyright (C) 1999-2006 Id Software, Inc. and contributors.
For a list of contributors, see the accompanying CONTRIBUTORS file.
This file is part of GtkRadiant.
GtkRadiant is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
GtkRadiant is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
// scriplib.c
#include "cmdlib.h"
#include "mathlib.h"
#include "inout.h"
#include "scriplib.h"
#include "vfs.h"
/*
=============================================================================
PARSING STUFF
=============================================================================
*/
typedef struct
{
char filename[1024];
char *buffer,*script_p,*end_p;
int line;
} script_t;
#define MAX_INCLUDES 8
script_t scriptstack[MAX_INCLUDES];
script_t *script;
int scriptline;
char token[MAXTOKEN];
qboolean endofscript;
qboolean tokenready; // only qtrue if UnGetToken was just called
/*
==============
AddScriptToStack
==============
*/
void AddScriptToStack (const char *filename, int index)
{
int size;
script++;
if (script == &scriptstack[MAX_INCLUDES])
Error ("script file exceeded MAX_INCLUDES");
strcpy (script->filename, ExpandPath (filename));
size = vfsLoadFile (script->filename, (void **)&script->buffer, index);
if (size == -1)
Sys_Printf ("Script file %s was not found\n", script->filename);
else
{
if (index > 0)
Sys_Printf ("entering %s (%d)\n", script->filename, index+1);
else
Sys_Printf ("entering %s\n", script->filename);
}
script->line = 1;
script->script_p = script->buffer;
script->end_p = script->buffer + size;
}
/*
==============
LoadScriptFile
==============
*/
void LoadScriptFile (const char *filename, int index)
{
script = scriptstack;
AddScriptToStack (filename, index);
endofscript = qfalse;
tokenready = qfalse;
}
/*
==============
ParseFromMemory
==============
*/
void ParseFromMemory (char *buffer, int size)
{
script = scriptstack;
script++;
if (script == &scriptstack[MAX_INCLUDES])
Error ("script file exceeded MAX_INCLUDES");
strcpy (script->filename, "memory buffer" );
script->buffer = buffer;
script->line = 1;
script->script_p = script->buffer;
script->end_p = script->buffer + size;
endofscript = qfalse;
tokenready = qfalse;
}
/*
==============
UnGetToken
Signals that the current token was not used, and should be reported
for the next GetToken. Note that
GetToken (qtrue);
UnGetToken ();
GetToken (qfalse);
could cross a line boundary.
==============
*/
void UnGetToken (void)
{
tokenready = qtrue;
}
qboolean EndOfScript (qboolean crossline)
{
if (!crossline)
Error ("Line %i is incomplete\n",scriptline);
if (!strcmp (script->filename, "memory buffer"))
{
endofscript = qtrue;
return qfalse;
}
if( script->buffer == NULL )
Sys_Printf( "WARNING: Attempt to free already freed script buffer\n" );
else
free( script->buffer );
script->buffer = NULL;
if (script == scriptstack+1)
{
endofscript = qtrue;
return qfalse;
}
script--;
scriptline = script->line;
Sys_Printf ("returning to %s\n", script->filename);
return GetToken (crossline);
}
/*
==============
GetToken
==============
*/
qboolean GetToken (qboolean crossline)
{
char *token_p;
/* ydnar: dummy testing */
if( script == NULL || script->buffer == NULL )
return qfalse;
if (tokenready) // is a token already waiting?
{
tokenready = qfalse;
return qtrue;
}
if ((script->script_p >= script->end_p) || (script->script_p == NULL))
return EndOfScript (crossline);
//
// skip space
//
skipspace:
while (*script->script_p <= 32)
{
if (script->script_p >= script->end_p)
return EndOfScript (crossline);
if (*script->script_p++ == '\n')
{
if (!crossline)
Error ("Line %i is incomplete\n",scriptline);
script->line++;
scriptline = script->line;
}
}
if (script->script_p >= script->end_p)
return EndOfScript (crossline);
// ; # // comments
if (*script->script_p == ';' || *script->script_p == '#'
|| ( script->script_p[0] == '/' && script->script_p[1] == '/') )
{
if (!crossline)
Error ("Line %i is incomplete\n",scriptline);
while (*script->script_p++ != '\n')
if (script->script_p >= script->end_p)
return EndOfScript (crossline);
script->line++;
scriptline = script->line;
goto skipspace;
}
// /* */ comments
if (script->script_p[0] == '/' && script->script_p[1] == '*')
{
if (!crossline)
Error ("Line %i is incomplete\n",scriptline);
script->script_p+=2;
while (script->script_p[0] != '*' && script->script_p[1] != '/')
{
if ( *script->script_p == '\n' )
{
script->line++;
scriptline = script->line;
}
script->script_p++;
if (script->script_p >= script->end_p)
return EndOfScript (crossline);
}
script->script_p += 2;
goto skipspace;
}
//
// copy token
//
token_p = token;
if (*script->script_p == '"')
{
// quoted token
script->script_p++;
while (*script->script_p != '"')
{
*token_p++ = *script->script_p++;
if (script->script_p == script->end_p)
break;
if (token_p == &token[MAXTOKEN])
Error ("Token too large on line %i\n",scriptline);
}
script->script_p++;
}
else // regular token
while ( *script->script_p > 32 && *script->script_p != ';')
{
*token_p++ = *script->script_p++;
if (script->script_p == script->end_p)
break;
if (token_p == &token[MAXTOKEN])
Error ("Token too large on line %i\n",scriptline);
}
*token_p = 0;
if (!strcmp (token, "$include"))
{
GetToken (qfalse);
AddScriptToStack (token, 0);
return GetToken (crossline);
}
return qtrue;
}
/*
==============
TokenAvailable
Returns qtrue if there is another token on the line
==============
*/
qboolean TokenAvailable (void) {
int oldLine, oldScriptLine;
qboolean r;
/* save */
oldLine = scriptline;
oldScriptLine = script->line;
/* test */
r = GetToken( qtrue );
if ( !r ) {
return qfalse;
}
UnGetToken();
if ( oldLine == scriptline ) {
return qtrue;
}
/* restore */
//% scriptline = oldLine;
//% script->line = oldScriptLine;
return qfalse;
}
//=====================================================================
void MatchToken( char *match ) {
GetToken( qtrue );
if ( strcmp( token, match ) ) {
Error( "MatchToken( \"%s\" ) failed at line %i in file %s", match, scriptline, script->filename);
}
}
void Parse1DMatrix (int x, vec_t *m) {
int i;
MatchToken( "(" );
for (i = 0 ; i < x ; i++) {
GetToken( qfalse );
m[i] = atof(token);
}
MatchToken( ")" );
}
void Parse2DMatrix (int y, int x, vec_t *m) {
int i;
MatchToken( "(" );
for (i = 0 ; i < y ; i++) {
Parse1DMatrix (x, m + i * x);
}
MatchToken( ")" );
}
void Parse3DMatrix (int z, int y, int x, vec_t *m) {
int i;
MatchToken( "(" );
for (i = 0 ; i < z ; i++) {
Parse2DMatrix (y, x, m + i * x*y);
}
MatchToken( ")" );
}
void Write1DMatrix (FILE *f, int x, vec_t *m) {
int i;
fprintf (f, "( ");
for (i = 0 ; i < x ; i++) {
if (m[i] == (int)m[i] ) {
fprintf (f, "%i ", (int)m[i]);
} else {
fprintf (f, "%f ", m[i]);
}
}
fprintf (f, ")");
}
void Write2DMatrix (FILE *f, int y, int x, vec_t *m) {
int i;
fprintf (f, "( ");
for (i = 0 ; i < y ; i++) {
Write1DMatrix (f, x, m + i*x);
fprintf (f, " ");
}
fprintf (f, ")\n");
}
void Write3DMatrix (FILE *f, int z, int y, int x, vec_t *m) {
int i;
fprintf (f, "(\n");
for (i = 0 ; i < z ; i++) {
Write2DMatrix (f, y, x, m + i*(x*y) );
}
fprintf (f, ")\n");
}

View File

@@ -0,0 +1,55 @@
/*
Copyright (C) 1999-2006 Id Software, Inc. and contributors.
For a list of contributors, see the accompanying CONTRIBUTORS file.
This file is part of GtkRadiant.
GtkRadiant is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
GtkRadiant is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
// scriplib.h
#ifndef __CMDLIB__
#include "../common/cmdlib.h"
#endif
#ifndef __MATHLIB__
#include "mathlib.h"
#endif
#define MAXTOKEN 1024
extern char token[MAXTOKEN];
extern char *scriptbuffer,*script_p,*scriptend_p;
extern int grabbed;
extern int scriptline;
extern qboolean endofscript;
void LoadScriptFile (const char *filename, int index);
void ParseFromMemory (char *buffer, int size);
qboolean GetToken (qboolean crossline);
void UnGetToken (void);
qboolean TokenAvailable (void);
void MatchToken( char *match );
void Parse1DMatrix (int x, vec_t *m);
void Parse2DMatrix (int y, int x, vec_t *m);
void Parse3DMatrix (int z, int y, int x, vec_t *m);
void Write1DMatrix (FILE *f, int x, vec_t *m);
void Write2DMatrix (FILE *f, int y, int x, vec_t *m);
void Write3DMatrix (FILE *f, int z, int y, int x, vec_t *m);

View File

@@ -0,0 +1,114 @@
/*
Copyright (C) 1999-2006 Id Software, Inc. and contributors.
For a list of contributors, see the accompanying CONTRIBUTORS file.
This file is part of GtkRadiant.
GtkRadiant is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
GtkRadiant is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
// Copyright (C) 1999-2000 Id Software, Inc.
//
// This file must be identical in the quake and utils directories
// contents flags are seperate bits
// a given brush can contribute multiple content bits
// these definitions also need to be in q_shared.h!
#define CONTENTS_SOLID 1 // an eye is never valid in a solid
#define CONTENTS_LAVA 8
#define CONTENTS_SLIME 16
#define CONTENTS_WATER 32
#define CONTENTS_FOG 64
#define CONTENTS_AREAPORTAL 0x8000
#define CONTENTS_PLAYERCLIP 0x10000
#define CONTENTS_MONSTERCLIP 0x20000
//bot specific contents types
#define CONTENTS_TELEPORTER 0x40000
#define CONTENTS_JUMPPAD 0x80000
#define CONTENTS_CLUSTERPORTAL 0x100000
#define CONTENTS_DONOTENTER 0x200000
#define CONTENTS_BOTCLIP 0x400000
#define CONTENTS_ORIGIN 0x1000000 // removed before bsping an entity
#define CONTENTS_BODY 0x2000000 // should never be on a brush, only in game
#define CONTENTS_CORPSE 0x4000000
#define CONTENTS_DETAIL 0x8000000 // brushes not used for the bsp
#define CONTENTS_STRUCTURAL 0x10000000 // brushes used for the bsp
#define CONTENTS_TRANSLUCENT 0x20000000 // don't consume surface fragments inside
#define CONTENTS_TRIGGER 0x40000000
#define CONTENTS_NODROP 0x80000000 // don't leave bodies or items (death fog, lava)
#define SURF_NODAMAGE 0x1 // never give falling damage
#define SURF_SLICK 0x2 // effects game physics
#define SURF_SKY 0x4 // lighting from environment map
#define SURF_LADDER 0x8
#define SURF_NOIMPACT 0x10 // don't make missile explosions
#define SURF_NOMARKS 0x20 // don't leave missile marks
#define SURF_FLESH 0x40 // make flesh sounds and effects
#define SURF_NODRAW 0x80 // don't generate a drawsurface at all
#define SURF_HINT 0x100 // make a primary bsp splitter
#define SURF_SKIP 0x200 // completely ignore, allowing non-closed brushes
#define SURF_NOLIGHTMAP 0x400 // surface doesn't need a lightmap
#define SURF_POINTLIGHT 0x800 // generate lighting info at vertexes
#define SURF_METALSTEPS 0x1000 // clanking footsteps
#define SURF_NOSTEPS 0x2000 // no footstep sounds
#define SURF_NONSOLID 0x4000 // don't collide against curves with this set
#define SURF_LIGHTFILTER 0x8000 // act as a light filter during q3map -light
#define SURF_ALPHASHADOW 0x10000 // do per-pixel light shadow casting in q3map
#define SURF_NODLIGHT 0x20000 // don't dlight even if solid (solid lava, skies)
#define SURF_DUST 0x40000 // leave a dust trail when walking on this surface
/* ydnar flags */
#define CONTENTS_OPAQUE 0x02
#define CONTENTS_LIGHTGRID 0x04
#define SURF_VERTEXLIT (SURF_POINTLIGHT | SURF_NOLIGHTMAP)
/* wolfenstein flags (collisions with valid q3a flags are noted) */
#define CONTENTS_MISSILECLIP 0x80
#define CONTENTS_ITEM 0x100
#define CONTENTS_AI_NOSIGHT 0x1000
#define CONTENTS_CLIPSHOT 0x2000
#define CONTENTS_DONOTENTER_LARGE 0x400000 /* CONTENTS_BOTCLIP */
#define SURF_CERAMIC 0x40 /* SURF_FLESH */
#define SURF_METAL 0x1000 /* SURF_METALSTEPS */
#define SURF_WOOD 0x40000 /* SURF_DUST */
#define SURF_GRASS 0x80000
#define SURF_GRAVEL 0x100000
#define SURF_GLASS 0x200000
#define SURF_SNOW 0x400000
#define SURF_ROOF 0x800000
#define SURF_RUBBLE 0x1000000
#define SURF_CARPET 0x2000000
#define SURF_MONSTERSLICK 0x4000000
#define SURF_MONSLICK_W 0x8000000
#define SURF_MONSLICK_N 0x10000000
#define SURF_MONSLICK_E 0x20000000
#define SURF_MONSLICK_S 0x40000000

View File

@@ -0,0 +1,620 @@
/*
Copyright (C) 1999-2006 Id Software, Inc. and contributors.
For a list of contributors, see the accompanying CONTRIBUTORS file.
This file is part of GtkRadiant.
GtkRadiant is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
GtkRadiant is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef WIN32
// The below define is necessary to use
// pthreads extensions like pthread_mutexattr_settype
#define _GNU_SOURCE
#include <pthread.h>
#endif
#include "cmdlib.h"
#include "mathlib.h"
#include "inout.h"
#include "qthreads.h"
#define MAX_THREADS 64
int dispatch;
int workcount;
int oldf;
qboolean pacifier;
qboolean threaded;
/*
=============
GetThreadWork
=============
*/
int GetThreadWork (void)
{
int r;
int f;
ThreadLock ();
if (dispatch == workcount)
{
ThreadUnlock ();
return -1;
}
f = 10*dispatch / workcount;
if (f != oldf)
{
oldf = f;
if (pacifier)
{
Sys_Printf ("%i...", f);
fflush( stdout ); /* ydnar */
}
}
r = dispatch;
dispatch++;
ThreadUnlock ();
return r;
}
void (*workfunction) (int);
void ThreadWorkerFunction (int threadnum)
{
int work;
while (1)
{
work = GetThreadWork ();
if (work == -1)
break;
//Sys_Printf ("thread %i, work %i\n", threadnum, work);
workfunction(work);
}
}
void RunThreadsOnIndividual (int workcnt, qboolean showpacifier, void(*func)(int))
{
if (numthreads == -1)
ThreadSetDefault ();
workfunction = func;
RunThreadsOn (workcnt, showpacifier, ThreadWorkerFunction);
}
/*
===================================================================
WIN32
===================================================================
*/
#ifdef WIN32
#define USED
#include <windows.h>
int numthreads = -1;
CRITICAL_SECTION crit;
static int enter;
void ThreadSetDefault (void)
{
SYSTEM_INFO info;
if (numthreads == -1) // not set manually
{
GetSystemInfo (&info);
numthreads = info.dwNumberOfProcessors;
if (numthreads < 1 || numthreads > 32)
numthreads = 1;
}
Sys_Printf ("%i threads\n", numthreads);
}
void ThreadLock (void)
{
if (!threaded)
return;
EnterCriticalSection (&crit);
if (enter)
Error ("Recursive ThreadLock\n");
enter = 1;
}
void ThreadUnlock (void)
{
if (!threaded)
return;
if (!enter)
Error ("ThreadUnlock without lock\n");
enter = 0;
LeaveCriticalSection (&crit);
}
/*
=============
RunThreadsOn
=============
*/
void RunThreadsOn (int workcnt, qboolean showpacifier, void(*func)(int))
{
int threadid[MAX_THREADS];
HANDLE threadhandle[MAX_THREADS];
int i;
int start, end;
start = I_FloatTime ();
dispatch = 0;
workcount = workcnt;
oldf = -1;
pacifier = showpacifier;
threaded = qtrue;
//
// run threads in parallel
//
InitializeCriticalSection (&crit);
if (numthreads == 1)
{ // use same thread
func (0);
}
else
{
for (i=0 ; i<numthreads ; i++)
{
threadhandle[i] = CreateThread(
NULL, // LPSECURITY_ATTRIBUTES lpsa,
//0, // DWORD cbStack,
/* ydnar: cranking stack size to eliminate radiosity crash with 1MB stack on win32 */
(4096 * 1024),
(LPTHREAD_START_ROUTINE)func, // LPTHREAD_START_ROUTINE lpStartAddr,
(LPVOID)i, // LPVOID lpvThreadParm,
0, // DWORD fdwCreate,
&threadid[i]);
}
for (i=0 ; i<numthreads ; i++)
WaitForSingleObject (threadhandle[i], INFINITE);
}
DeleteCriticalSection (&crit);
threaded = qfalse;
end = I_FloatTime ();
if (pacifier)
Sys_Printf (" (%i)\n", end-start);
}
#endif
/*
===================================================================
OSF1
===================================================================
*/
#ifdef __osf__
#define USED
int numthreads = 4;
void ThreadSetDefault (void)
{
if (numthreads == -1) // not set manually
{
numthreads = 4;
}
}
#include <pthread.h>
pthread_mutex_t *my_mutex;
void ThreadLock (void)
{
if (my_mutex)
pthread_mutex_lock (my_mutex);
}
void ThreadUnlock (void)
{
if (my_mutex)
pthread_mutex_unlock (my_mutex);
}
/*
=============
RunThreadsOn
=============
*/
void RunThreadsOn (int workcnt, qboolean showpacifier, void(*func)(int))
{
int i;
pthread_t work_threads[MAX_THREADS];
pthread_addr_t status;
pthread_attr_t attrib;
pthread_mutexattr_t mattrib;
int start, end;
start = I_FloatTime ();
dispatch = 0;
workcount = workcnt;
oldf = -1;
pacifier = showpacifier;
threaded = qtrue;
if (pacifier)
setbuf (stdout, NULL);
if (!my_mutex)
{
my_mutex = safe_malloc (sizeof(*my_mutex));
if (pthread_mutexattr_create (&mattrib) == -1)
Error ("pthread_mutex_attr_create failed");
if (pthread_mutexattr_setkind_np (&mattrib, MUTEX_FAST_NP) == -1)
Error ("pthread_mutexattr_setkind_np failed");
if (pthread_mutex_init (my_mutex, mattrib) == -1)
Error ("pthread_mutex_init failed");
}
if (pthread_attr_create (&attrib) == -1)
Error ("pthread_attr_create failed");
if (pthread_attr_setstacksize (&attrib, 0x100000) == -1)
Error ("pthread_attr_setstacksize failed");
for (i=0 ; i<numthreads ; i++)
{
if (pthread_create(&work_threads[i], attrib
, (pthread_startroutine_t)func, (pthread_addr_t)i) == -1)
Error ("pthread_create failed");
}
for (i=0 ; i<numthreads ; i++)
{
if (pthread_join (work_threads[i], &status) == -1)
Error ("pthread_join failed");
}
threaded = qfalse;
end = I_FloatTime ();
if (pacifier)
Sys_Printf (" (%i)\n", end-start);
}
#endif
/*
===================================================================
IRIX
===================================================================
*/
#ifdef _MIPS_ISA
#define USED
#include <task.h>
#include <abi_mutex.h>
#include <sys/types.h>
#include <sys/prctl.h>
int numthreads = -1;
abilock_t lck;
void ThreadSetDefault (void)
{
if (numthreads == -1)
numthreads = prctl(PR_MAXPPROCS);
Sys_Printf ("%i threads\n", numthreads);
usconfig (CONF_INITUSERS, numthreads);
}
void ThreadLock (void)
{
spin_lock (&lck);
}
void ThreadUnlock (void)
{
release_lock (&lck);
}
/*
=============
RunThreadsOn
=============
*/
void RunThreadsOn (int workcnt, qboolean showpacifier, void(*func)(int))
{
int i;
int pid[MAX_THREADS];
int start, end;
start = I_FloatTime ();
dispatch = 0;
workcount = workcnt;
oldf = -1;
pacifier = showpacifier;
threaded = qtrue;
if (pacifier)
setbuf (stdout, NULL);
init_lock (&lck);
for (i=0 ; i<numthreads-1 ; i++)
{
pid[i] = sprocsp ( (void (*)(void *, size_t))func, PR_SALL, (void *)i
, NULL, 0x200000); // 2 meg stacks
if (pid[i] == -1)
{
perror ("sproc");
Error ("sproc failed");
}
}
func(i);
for (i=0 ; i<numthreads-1 ; i++)
wait (NULL);
threaded = qfalse;
end = I_FloatTime ();
if (pacifier)
Sys_Printf (" (%i)\n", end-start);
}
#endif
/*
=======================================================================
Linux pthreads
=======================================================================
*/
#ifdef __linux__
#define USED
int numthreads = 4;
void ThreadSetDefault (void)
{
if (numthreads == -1) // not set manually
{
/* default to one thread, only multi-thread when specifically told to */
numthreads = 1;
}
if(numthreads > 1)
Sys_Printf("threads: %d\n", numthreads);
}
#include <pthread.h>
typedef struct pt_mutex_s
{
pthread_t *owner;
pthread_mutex_t a_mutex;
pthread_cond_t cond;
unsigned int lock;
} pt_mutex_t;
pt_mutex_t global_lock;
void ThreadLock(void)
{
pt_mutex_t *pt_mutex = &global_lock;
if(!threaded)
return;
pthread_mutex_lock(&pt_mutex->a_mutex);
if(pthread_equal(pthread_self(), (pthread_t)&pt_mutex->owner))
pt_mutex->lock++;
else
{
if((!pt_mutex->owner) && (pt_mutex->lock == 0))
{
pt_mutex->owner = (pthread_t *)pthread_self();
pt_mutex->lock = 1;
}
else
{
while(1)
{
pthread_cond_wait(&pt_mutex->cond, &pt_mutex->a_mutex);
if((!pt_mutex->owner) && (pt_mutex->lock == 0))
{
pt_mutex->owner = (pthread_t *)pthread_self();
pt_mutex->lock = 1;
break;
}
}
}
}
pthread_mutex_unlock(&pt_mutex->a_mutex);
}
void ThreadUnlock(void)
{
pt_mutex_t *pt_mutex = &global_lock;
if(!threaded)
return;
pthread_mutex_lock(&pt_mutex->a_mutex);
pt_mutex->lock--;
if(pt_mutex->lock == 0)
{
pt_mutex->owner = NULL;
pthread_cond_signal(&pt_mutex->cond);
}
pthread_mutex_unlock(&pt_mutex->a_mutex);
}
void recursive_mutex_init(pthread_mutexattr_t attribs)
{
pt_mutex_t *pt_mutex = &global_lock;
pt_mutex->owner = NULL;
if(pthread_mutex_init(&pt_mutex->a_mutex, &attribs) != 0)
Error("pthread_mutex_init failed\n");
if(pthread_cond_init(&pt_mutex->cond, NULL) != 0)
Error("pthread_cond_init failed\n");
pt_mutex->lock = 0;
}
/*
=============
RunThreadsOn
=============
*/
void RunThreadsOn (int workcnt, qboolean showpacifier, void(*func)(int))
{
pthread_mutexattr_t mattrib;
pthread_t work_threads[MAX_THREADS];
int start, end;
int i=0, status=0;
start = I_FloatTime ();
pacifier = showpacifier;
dispatch = 0;
oldf = -1;
workcount = workcnt;
if(numthreads == 1)
func(0);
else
{
threaded = qtrue;
if(pacifier)
setbuf(stdout, NULL);
if(pthread_mutexattr_init(&mattrib) != 0)
Error("pthread_mutexattr_init failed");
#if __GLIBC_MINOR__ == 1
if (pthread_mutexattr_settype(&mattrib, PTHREAD_MUTEX_FAST_NP) != 0)
#else
if (pthread_mutexattr_settype(&mattrib, PTHREAD_MUTEX_ADAPTIVE_NP) != 0)
#endif
Error ("pthread_mutexattr_settype failed");
recursive_mutex_init(mattrib);
for (i=0 ; i<numthreads ; i++)
{
/* Default pthread attributes: joinable & non-realtime scheduling */
if(pthread_create(&work_threads[i], NULL, (void*)func, (void*)i) != 0)
Error("pthread_create failed");
}
for (i=0 ; i<numthreads ; i++)
{
if(pthread_join(work_threads[i], (void **)&status) != 0)
Error("pthread_join failed");
}
pthread_mutexattr_destroy(&mattrib);
threaded = qfalse;
}
end = I_FloatTime ();
if (pacifier)
Sys_Printf (" (%i)\n", end-start);
}
#endif // ifdef __linux__
/*
=======================================================================
SINGLE THREAD
=======================================================================
*/
#ifndef USED
int numthreads = 1;
void ThreadSetDefault (void)
{
numthreads = 1;
}
void ThreadLock (void)
{
}
void ThreadUnlock (void)
{
}
/*
=============
RunThreadsOn
=============
*/
void RunThreadsOn (int workcnt, qboolean showpacifier, void(*func)(int))
{
int i;
int start, end;
dispatch = 0;
workcount = workcnt;
oldf = -1;
pacifier = showpacifier;
start = I_FloatTime ();
func(0);
end = I_FloatTime ();
if (pacifier)
Sys_Printf (" (%i)\n", end-start);
}
#endif

View File

@@ -0,0 +1,235 @@
/*
Copyright (C) 1999-2006 Id Software, Inc. and contributors.
For a list of contributors, see the accompanying CONTRIBUTORS file.
This file is part of GtkRadiant.
GtkRadiant is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
GtkRadiant is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
//
// trilib.c: library for loading triangles from an Alias triangle file
//
#include <stdio.h>
#include "cmdlib.h"
#include "mathlib.h"
#include "polyset.h"
#include "trilib.h"
// on disk representation of a face
#define FLOAT_START 99999.0
#define FLOAT_END -FLOAT_START
#define MAGIC 123322
//#define NOISY 1
#if defined (__linux__) || defined (__APPLE__)
#define strlwr strlower
#endif
typedef struct {
float v[3];
} vector;
typedef struct
{
vector n; /* normal */
vector p; /* point */
vector c; /* color */
float u; /* u */
float v; /* v */
} aliaspoint_t;
typedef struct {
aliaspoint_t pt[3];
} tf_triangle;
static void ByteSwapTri (tf_triangle *tri)
{
int i;
for (i=0 ; i<sizeof(tf_triangle)/4 ; i++)
{
((int *)tri)[i] = BigLong (((int *)tri)[i]);
}
}
static void ReadPolysetGeometry( triangle_t *tripool, FILE *input, int count, triangle_t *ptri )
{
tf_triangle tri;
int i;
for (i = 0; i < count; ++i) {
int j;
fread( &tri, sizeof(tf_triangle), 1, input );
ByteSwapTri (&tri);
for (j=0 ; j<3 ; j++)
{
int k;
for (k=0 ; k<3 ; k++)
{
ptri->verts[j][k] = tri.pt[j].p.v[k];
ptri->normals[j][k] = tri.pt[j].n.v[k];
// ptri->colors[j][k] = tri.pt[j].c.v[k];
}
ptri->texcoords[j][0] = tri.pt[j].u;
ptri->texcoords[j][1] = tri.pt[j].v;
}
ptri++;
if ((ptri - tripool ) >= POLYSET_MAXTRIANGLES)
Error ("Error: too many triangles; increase POLYSET_MAXTRIANGLES\n");
}
}
void TRI_LoadPolysets( const char *filename, polyset_t **ppPSET, int *numpsets )
{
FILE *input;
float start;
char name[256], tex[256];
int i, count, magic, pset = 0;
triangle_t *ptri;
polyset_t *pPSET;
int iLevel;
int exitpattern;
float t;
t = -FLOAT_START;
*((unsigned char *)&exitpattern + 0) = *((unsigned char *)&t + 3);
*((unsigned char *)&exitpattern + 1) = *((unsigned char *)&t + 2);
*((unsigned char *)&exitpattern + 2) = *((unsigned char *)&t + 1);
*((unsigned char *)&exitpattern + 3) = *((unsigned char *)&t + 0);
if ((input = fopen(filename, "rb")) == 0)
Error ("reader: could not open file '%s'", filename);
iLevel = 0;
fread(&magic, sizeof(int), 1, input);
if (BigLong(magic) != MAGIC)
Error ("%s is not a Alias object separated triangle file, magic number is wrong.", filename);
pPSET = calloc( 1, POLYSET_MAXPOLYSETS * sizeof( polyset_t ) );
ptri = calloc( 1, POLYSET_MAXTRIANGLES * sizeof( triangle_t ) );
*ppPSET = pPSET;
while (feof(input) == 0) {
if (fread(&start, sizeof(float), 1, input) < 1)
break;
*(int *)&start = BigLong(*(int *)&start);
if (*(int *)&start != exitpattern)
{
if (start == FLOAT_START) {
/* Start of an object or group of objects. */
i = -1;
do {
/* There are probably better ways to read a string from */
/* a file, but this does allow you to do error checking */
/* (which I'm not doing) on a per character basis. */
++i;
fread( &(name[i]), sizeof( char ), 1, input);
} while( name[i] != '\0' );
if ( i != 0 )
strncpy( pPSET[pset].name, name, sizeof( pPSET[pset].name ) - 1 );
else
strcpy( pPSET[pset].name , "(unnamed)" );
strlwr( pPSET[pset].name );
// indent();
// fprintf(stdout,"OBJECT START: %s\n",name);
fread( &count, sizeof(int), 1, input);
count = BigLong(count);
++iLevel;
if (count != 0) {
// indent();
// fprintf(stdout,"NUMBER OF TRIANGLES: %d\n",count);
i = -1;
do {
++i;
fread( &(tex[i]), sizeof( char ), 1, input);
} while( tex[i] != '\0' );
/*
if ( i != 0 )
strncpy( pPSET[pset].texname, tex, sizeof( pPSET[pset].texname ) - 1 );
else
strcpy( pPSET[pset].texname, "(unnamed)" );
strlwr( pPSET[pset].texname );
*/
// indent();
// fprintf(stdout," Object texture name: '%s'\n",tex);
}
/* Else (count == 0) this is the start of a group, and */
/* no texture name is present. */
}
else if (start == FLOAT_END) {
/* End of an object or group. Yes, the name should be */
/* obvious from context, but it is in here just to be */
/* safe and to provide a little extra information for */
/* those who do not wish to write a recursive reader. */
/* Mea culpa. */
--iLevel;
i = -1;
do {
++i;
fread( &(name[i]), sizeof( char ), 1, input);
} while( name[i] != '\0' );
if ( i != 0 )
strncpy( pPSET[pset].name, name, sizeof( pPSET[pset].name ) - 1 );
else
strcpy( pPSET[pset].name , "(unnamed)" );
strlwr( pPSET[pset].name );
// indent();
// fprintf(stdout,"OBJECT END: %s\n",name);
continue;
}
}
//
// read the triangles
//
if ( count > 0 )
{
pPSET[pset].triangles = ptri;
ReadPolysetGeometry( pPSET[0].triangles, input, count, ptri );
ptri += count;
pPSET[pset].numtriangles = count;
if ( ++pset >= POLYSET_MAXPOLYSETS )
{
Error ("Error: too many polysets; increase POLYSET_MAXPOLYSETS\n");
}
}
}
*numpsets = pset;
fclose (input);
}

View File

@@ -0,0 +1,26 @@
/*
Copyright (C) 1999-2006 Id Software, Inc. and contributors.
For a list of contributors, see the accompanying CONTRIBUTORS file.
This file is part of GtkRadiant.
GtkRadiant is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
GtkRadiant is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
//
// trilib.h: header file for loading triangles from an Alias triangle file
//
void TRI_LoadPolysets( const char *filename, polyset_t **ppPSET, int *numpsets );

4596
tools/quake3/common/unzip.c Normal file

File diff suppressed because it is too large Load Diff

321
tools/quake3/common/unzip.h Normal file
View File

@@ -0,0 +1,321 @@
/*
Copyright (C) 1999-2006 Id Software, Inc. and contributors.
For a list of contributors, see the accompanying CONTRIBUTORS file.
This file is part of GtkRadiant.
GtkRadiant is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
GtkRadiant is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#if defined(STRICTUNZIP) || defined(STRICTZIPUNZIP)
/* like the STRICT of WIN32, we define a pointer that cannot be converted
from (void*) without cast */
typedef struct TagunzFile__ { int unused; } unzFile__;
typedef unzFile__ *unzFile;
#else
typedef void* unzFile;
#endif
/* tm_unz contain date/time info */
typedef struct tm_unz_s
{
unsigned int tm_sec; /* seconds after the minute - [0,59] */
unsigned int tm_min; /* minutes after the hour - [0,59] */
unsigned int tm_hour; /* hours since midnight - [0,23] */
unsigned int tm_mday; /* day of the month - [1,31] */
unsigned int tm_mon; /* months since January - [0,11] */
unsigned int tm_year; /* years - [1980..2044] */
} tm_unz;
/* unz_global_info structure contain global data about the ZIPfile
These data comes from the end of central dir */
typedef struct unz_global_info_s
{
unsigned long number_entry; /* total number of entries in the central dir on this disk */
unsigned long size_comment; /* size of the global comment of the zipfile */
} unz_global_info;
/* unz_file_info contain information about a file in the zipfile */
typedef struct unz_file_info_s
{
unsigned long version; /* version made by 2 unsigned chars */
unsigned long version_needed; /* version needed to extract 2 unsigned chars */
unsigned long flag; /* general purpose bit flag 2 unsigned chars */
unsigned long compression_method; /* compression method 2 unsigned chars */
unsigned long dosDate; /* last mod file date in Dos fmt 4 unsigned chars */
unsigned long crc; /* crc-32 4 unsigned chars */
unsigned long compressed_size; /* compressed size 4 unsigned chars */
unsigned long uncompressed_size; /* uncompressed size 4 unsigned chars */
unsigned long size_filename; /* filename length 2 unsigned chars */
unsigned long size_file_extra; /* extra field length 2 unsigned chars */
unsigned long size_file_comment; /* file comment length 2 unsigned chars */
unsigned long disk_num_start; /* disk number start 2 unsigned chars */
unsigned long internal_fa; /* internal file attributes 2 unsigned chars */
unsigned long external_fa; /* external file attributes 4 unsigned chars */
tm_unz tmu_date;
} unz_file_info;
/* unz_file_info_interntal contain internal info about a file in zipfile*/
typedef struct unz_file_info_internal_s
{
unsigned long offset_curfile;/* relative offset of static header 4 unsigned chars */
} unz_file_info_internal;
typedef void* (*alloc_func) (void* opaque, unsigned int items, unsigned int size);
typedef void (*free_func) (void* opaque, void* address);
struct internal_state;
typedef struct z_stream_s {
unsigned char *next_in; /* next input unsigned char */
unsigned int avail_in; /* number of unsigned chars available at next_in */
unsigned long total_in; /* total nb of input unsigned chars read so */
unsigned char *next_out; /* next output unsigned char should be put there */
unsigned int avail_out; /* remaining free space at next_out */
unsigned long total_out; /* total nb of unsigned chars output so */
char *msg; /* last error message, NULL if no error */
struct internal_state *state; /* not visible by applications */
alloc_func zalloc; /* used to allocate the internal state */
free_func zfree; /* used to free the internal state */
unsigned char* opaque; /* private data object passed to zalloc and zfree */
int data_type; /* best guess about the data type: ascii or binary */
unsigned long adler; /* adler32 value of the uncompressed data */
unsigned long reserved; /* reserved for future use */
} z_stream;
typedef z_stream *z_streamp;
/* file_in_zip_read_info_s contain internal information about a file in zipfile,
when reading and decompress it */
typedef struct
{
char *read_buffer; /* internal buffer for compressed data */
z_stream stream; /* zLib stream structure for inflate */
unsigned long pos_in_zipfile; /* position in unsigned char on the zipfile, for fseek*/
unsigned long stream_initialised; /* flag set if stream structure is initialised*/
unsigned long offset_local_extrafield;/* offset of the static extra field */
unsigned int size_local_extrafield;/* size of the static extra field */
unsigned long pos_local_extrafield; /* position in the static extra field in read*/
unsigned long crc32; /* crc32 of all data uncompressed */
unsigned long crc32_wait; /* crc32 we must obtain after decompress all */
unsigned long rest_read_compressed; /* number of unsigned char to be decompressed */
unsigned long rest_read_uncompressed;/*number of unsigned char to be obtained after decomp*/
FILE* file; /* io structore of the zipfile */
unsigned long compression_method; /* compression method (0==store) */
unsigned long byte_before_the_zipfile;/* unsigned char before the zipfile, (>0 for sfx)*/
} file_in_zip_read_info_s;
/* unz_s contain internal information about the zipfile
*/
typedef struct
{
FILE* file; /* io structore of the zipfile */
unz_global_info gi; /* public global information */
unsigned long byte_before_the_zipfile;/* unsigned char before the zipfile, (>0 for sfx)*/
unsigned long num_file; /* number of the current file in the zipfile*/
unsigned long pos_in_central_dir; /* pos of the current file in the central dir*/
unsigned long current_file_ok; /* flag about the usability of the current file*/
unsigned long central_pos; /* position of the beginning of the central dir*/
unsigned long size_central_dir; /* size of the central directory */
unsigned long offset_central_dir; /* offset of start of central directory with
respect to the starting disk number */
unz_file_info cur_file_info; /* public info about the current file in zip*/
unz_file_info_internal cur_file_info_internal; /* private info about it*/
file_in_zip_read_info_s* pfile_in_zip_read; /* structure about the current
file if we are decompressing it */
} unz_s;
#define UNZ_OK (0)
#define UNZ_END_OF_LIST_OF_FILE (-100)
#define UNZ_ERRNO (Z_ERRNO)
#define UNZ_EOF (0)
#define UNZ_PARAMERROR (-102)
#define UNZ_BADZIPFILE (-103)
#define UNZ_INTERNALERROR (-104)
#define UNZ_CRCERROR (-105)
#define UNZ_CASESENSITIVE 1
#define UNZ_NOTCASESENSITIVE 2
#define UNZ_OSDEFAULTCASE 0
extern int unzStringFileNameCompare (const char* fileName1, const char* fileName2, int iCaseSensitivity);
/*
Compare two filename (fileName1,fileName2).
If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp)
If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi
or strcasecmp)
If iCaseSenisivity = 0, case sensitivity is defaut of your operating system
(like 1 on Unix, 2 on Windows)
*/
extern unzFile unzOpen (const char *path);
extern unzFile unzReOpen (const char* path, unzFile file);
/*
Open a Zip file. path contain the full pathname (by example,
on a Windows NT computer "c:\\zlib\\zlib111.zip" or on an Unix computer
"zlib/zlib111.zip".
If the zipfile cannot be opened (file don't exist or in not valid), the
return value is NULL.
Else, the return value is a unzFile Handle, usable with other function
of this unzip package.
*/
extern int unzClose (unzFile file);
/*
Close a ZipFile opened with unzipOpen.
If there is files inside the .Zip opened with unzOpenCurrentFile (see later),
these files MUST be closed with unzipCloseCurrentFile before call unzipClose.
return UNZ_OK if there is no problem. */
extern int unzGetGlobalInfo (unzFile file, unz_global_info *pglobal_info);
/*
Write info about the ZipFile in the *pglobal_info structure.
No preparation of the structure is needed
return UNZ_OK if there is no problem. */
extern int unzGetGlobalComment (unzFile file, char *szComment, unsigned long uSizeBuf);
/*
Get the global comment string of the ZipFile, in the szComment buffer.
uSizeBuf is the size of the szComment buffer.
return the number of unsigned char copied or an error code <0
*/
/***************************************************************************/
/* Unzip package allow you browse the directory of the zipfile */
extern int unzGoToFirstFile (unzFile file);
/*
Set the current file of the zipfile to the first file.
return UNZ_OK if there is no problem
*/
extern int unzGoToNextFile (unzFile file);
/*
Set the current file of the zipfile to the next file.
return UNZ_OK if there is no problem
return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest.
*/
extern int unzLocateFile (unzFile file, const char *szFileName, int iCaseSensitivity);
/*
Try locate the file szFileName in the zipfile.
For the iCaseSensitivity signification, see unzStringFileNameCompare
return value :
UNZ_OK if the file is found. It becomes the current file.
UNZ_END_OF_LIST_OF_FILE if the file is not found
*/
extern int unzGetCurrentFileInfo (unzFile file, unz_file_info *pfile_info, char *szFileName, unsigned long fileNameBufferSize, void *extraField, unsigned long extraFieldBufferSize, char *szComment, unsigned long commentBufferSize);
/*
Get Info about the current file
if pfile_info!=NULL, the *pfile_info structure will contain somes info about
the current file
if szFileName!=NULL, the filemane string will be copied in szFileName
(fileNameBufferSize is the size of the buffer)
if extraField!=NULL, the extra field information will be copied in extraField
(extraFieldBufferSize is the size of the buffer).
This is the Central-header version of the extra field
if szComment!=NULL, the comment string of the file will be copied in szComment
(commentBufferSize is the size of the buffer)
*/
/***************************************************************************/
/* for reading the content of the current zipfile, you can open it, read data
from it, and close it (you can close it before reading all the file)
*/
extern int unzOpenCurrentFile (unzFile file);
/*
Open for reading data the current file in the zipfile.
If there is no error, the return value is UNZ_OK.
*/
extern int unzCloseCurrentFile (unzFile file);
/*
Close the file in zip opened with unzOpenCurrentFile
Return UNZ_CRCERROR if all the file was read but the CRC is not good
*/
extern int unzReadCurrentFile (unzFile file, void* buf, unsigned len);
/*
Read unsigned chars from the current file (opened by unzOpenCurrentFile)
buf contain buffer where data must be copied
len the size of buf.
return the number of unsigned char copied if somes unsigned chars are copied
return 0 if the end of file was reached
return <0 with error code if there is an error
(UNZ_ERRNO for IO error, or zLib error for uncompress error)
*/
extern long unztell(unzFile file);
/*
Give the current position in uncompressed data
*/
extern int unzeof (unzFile file);
/*
return 1 if the end of file was reached, 0 elsewhere
*/
extern int unzGetLocalExtrafield (unzFile file, void* buf, unsigned len);
/*
Read extra field from the current file (opened by unzOpenCurrentFile)
This is the local-header version of the extra field (sometimes, there is
more info in the local-header version than in the central-header)
if buf==NULL, it return the size of the local extra field
if buf!=NULL, len is the size of the buffer, the extra header is copied in
buf.
the return value is the number of unsigned chars copied in buf, or (if <0)
the error code
*/

365
tools/quake3/common/vfs.c Normal file
View File

@@ -0,0 +1,365 @@
/*
Copyright (c) 2001, Loki software, inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this list
of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.
Neither the name of Loki software nor the names of its contributors may be used
to endorse or promote products derived from this software without specific prior
written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
DIRECT,INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
//
// Rules:
//
// - Directories should be searched in the following order: ~/.q3a/baseq3,
// install dir (/usr/local/games/quake3/baseq3) and cd_path (/mnt/cdrom/baseq3).
//
// - Pak files are searched first inside the directories.
// - Case insensitive.
// - Unix-style slashes (/) (windows is backwards .. everyone knows that)
//
// Leonardo Zide (leo@lokigames.com)
//
#include <stdio.h>
#if defined (__linux__) || defined (__APPLE__)
#include <dirent.h>
#include <unistd.h>
#else
#include <wtypes.h>
#include <io.h>
#define R_OK 04
#define S_ISDIR(mode) (mode & _S_IFDIR)
#define PATH_MAX 260
#endif
#include <string.h>
#include <stdlib.h>
#include <sys/stat.h>
#include "cmdlib.h"
#include "mathlib.h"
#include <glib.h>
#include "inout.h"
#include "vfs.h"
#include "unzip.h"
typedef struct
{
char* name;
unz_s zipinfo;
unzFile zipfile;
guint32 size;
} VFS_PAKFILE;
// =============================================================================
// Global variables
static GSList* g_unzFiles;
static GSList* g_pakFiles;
static char g_strDirs[VFS_MAXDIRS][PATH_MAX];
static int g_numDirs;
static gboolean g_bUsePak = TRUE;
// =============================================================================
// Static functions
static void vfsAddSlash (char *str)
{
int n = strlen (str);
if (n > 0)
{
if (str[n-1] != '\\' && str[n-1] != '/')
strcat (str, "/");
}
}
static void vfsFixDOSName (char *src)
{
if (src == NULL)
return;
while (*src)
{
if (*src == '\\')
*src = '/';
src++;
}
}
//!\todo Define globally or use heap-allocated string.
#define NAME_MAX 255
static void vfsInitPakFile (const char *filename)
{
unz_global_info gi;
unzFile uf;
guint32 i;
int err;
uf = unzOpen (filename);
if (uf == NULL)
return;
g_unzFiles = g_slist_append (g_unzFiles, uf);
err = unzGetGlobalInfo (uf,&gi);
if (err != UNZ_OK)
return;
unzGoToFirstFile(uf);
for (i = 0; i < gi.number_entry; i++)
{
char filename_inzip[NAME_MAX];
unz_file_info file_info;
VFS_PAKFILE* file;
err = unzGetCurrentFileInfo (uf, &file_info, filename_inzip, sizeof(filename_inzip), NULL, 0, NULL, 0);
if (err != UNZ_OK)
break;
file = (VFS_PAKFILE*)safe_malloc (sizeof (VFS_PAKFILE));
g_pakFiles = g_slist_append (g_pakFiles, file);
vfsFixDOSName (filename_inzip);
g_strdown (filename_inzip);
file->name = strdup (filename_inzip);
file->size = file_info.uncompressed_size;
file->zipfile = uf;
memcpy (&file->zipinfo, uf, sizeof (unz_s));
if ((i+1) < gi.number_entry)
{
err = unzGoToNextFile(uf);
if (err!=UNZ_OK)
break;
}
}
}
// =============================================================================
// Global functions
// reads all pak files from a dir
void vfsInitDirectory (const char *path)
{
char filename[PATH_MAX];
char *dirlist;
GDir *dir;
if (g_numDirs == (VFS_MAXDIRS-1))
return;
Sys_Printf ("VFS Init: %s\n", path);
strcpy (g_strDirs[g_numDirs], path);
vfsFixDOSName (g_strDirs[g_numDirs]);
vfsAddSlash (g_strDirs[g_numDirs]);
g_numDirs++;
if (g_bUsePak)
{
dir = g_dir_open (path, 0, NULL);
if (dir != NULL)
{
while (1)
{
const char* name = g_dir_read_name(dir);
if(name == NULL)
break;
dirlist = g_strdup(name);
{
char *ext = strrchr (dirlist, '.');
if ((ext == NULL) || (Q_stricmp (ext, ".pk3") != 0))
continue;
}
sprintf (filename, "%s/%s", path, dirlist);
vfsInitPakFile (filename);
g_free(dirlist);
}
g_dir_close (dir);
}
}
}
// frees all memory that we allocated
void vfsShutdown ()
{
while (g_unzFiles)
{
unzClose ((unzFile)g_unzFiles->data);
g_unzFiles = g_slist_remove (g_unzFiles, g_unzFiles->data);
}
while (g_pakFiles)
{
VFS_PAKFILE* file = (VFS_PAKFILE*)g_pakFiles->data;
free (file->name);
free (file);
g_pakFiles = g_slist_remove (g_pakFiles, file);
}
}
// return the number of files that match
int vfsGetFileCount (const char *filename)
{
int i, count = 0;
char fixed[NAME_MAX], tmp[NAME_MAX];
GSList *lst;
strcpy (fixed, filename);
vfsFixDOSName (fixed);
g_strdown (fixed);
for (lst = g_pakFiles; lst != NULL; lst = g_slist_next (lst))
{
VFS_PAKFILE* file = (VFS_PAKFILE*)lst->data;
if (strcmp (file->name, fixed) == 0)
count++;
}
for (i = 0; i < g_numDirs; i++)
{
strcpy (tmp, g_strDirs[i]);
strcat (tmp, fixed);
if (access (tmp, R_OK) == 0)
count++;
}
return count;
}
// NOTE: when loading a file, you have to allocate one extra byte and set it to \0
int vfsLoadFile (const char *filename, void **bufferptr, int index)
{
int i, count = 0;
char tmp[NAME_MAX], fixed[NAME_MAX];
GSList *lst;
// filename is a full path
if (index == -1)
{
long len;
FILE *f;
f = fopen (filename, "rb");
if (f == NULL)
return -1;
fseek (f, 0, SEEK_END);
len = ftell (f);
rewind (f);
*bufferptr = safe_malloc (len+1);
if (*bufferptr == NULL)
return -1;
fread (*bufferptr, 1, len, f);
fclose (f);
// we need to end the buffer with a 0
((char*) (*bufferptr))[len] = 0;
return len;
}
*bufferptr = NULL;
strcpy (fixed, filename);
vfsFixDOSName (fixed);
g_strdown (fixed);
for (i = 0; i < g_numDirs; i++)
{
strcpy (tmp, g_strDirs[i]);
strcat (tmp, filename);
if (access (tmp, R_OK) == 0)
{
if (count == index)
{
long len;
FILE *f;
f = fopen (tmp, "rb");
if (f == NULL)
return -1;
fseek (f, 0, SEEK_END);
len = ftell (f);
rewind (f);
*bufferptr = safe_malloc (len+1);
if (*bufferptr == NULL)
return -1;
fread (*bufferptr, 1, len, f);
fclose (f);
// we need to end the buffer with a 0
((char*) (*bufferptr))[len] = 0;
return len;
}
count++;
}
}
for (lst = g_pakFiles; lst != NULL; lst = g_slist_next (lst))
{
VFS_PAKFILE* file = (VFS_PAKFILE*)lst->data;
if (strcmp (file->name, fixed) != 0)
continue;
if (count == index)
{
memcpy (file->zipfile, &file->zipinfo, sizeof (unz_s));
if (unzOpenCurrentFile (file->zipfile) != UNZ_OK)
return -1;
*bufferptr = safe_malloc (file->size+1);
// we need to end the buffer with a 0
((char*) (*bufferptr))[file->size] = 0;
i = unzReadCurrentFile (file->zipfile , *bufferptr, file->size);
unzCloseCurrentFile (file->zipfile);
if (i < 0)
return -1;
else
return file->size;
}
count++;
}
return -1;
}

41
tools/quake3/common/vfs.h Normal file
View File

@@ -0,0 +1,41 @@
/*
Copyright (c) 2001, Loki software, inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this list
of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.
Neither the name of Loki software nor the names of its contributors may be used
to endorse or promote products derived from this software without specific prior
written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
DIRECT,INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef _VFS_H_
#define _VFS_H_
#define VFS_MAXDIRS 8
void vfsInitDirectory (const char *path);
void vfsShutdown ();
int vfsGetFileCount (const char *filename);
int vfsLoadFile (const char *filename, void **buffer, int index);
#endif // _VFS_H_

View File

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

View File

@@ -0,0 +1,2 @@
*.dsp -m 'COPY' -k 'b'
*.dsw -m 'COPY' -k 'b'

View File

@@ -0,0 +1,651 @@
/*
Copyright (C) 1999-2006 Id Software, Inc. and contributors.
For a list of contributors, see the accompanying CONTRIBUTORS file.
This file is part of GtkRadiant.
GtkRadiant is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
GtkRadiant is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <assert.h>
#include "q3data.h"
static void Load3DS( const char *filename, _3DS_t *p3DS, qboolean verbose );
static qboolean s_verbose;
#define MAX_MATERIALS 100
#define MAX_NAMED_OBJECTS 100
#define MAX_MESH_MATERIAL_GROUPS 100
#define MAX_TRI_OBJECTS 512
static char s_buffer[1000000];
static int ReadString( FILE *fp, char *buffer )
{
int i = 0;
int bytesRead = 0;
do
{
fread( &buffer[i], 1, sizeof( char ), fp );
bytesRead++;
} while ( buffer[i++] != 0 );
buffer[i] = 0;
return bytesRead;
}
static int ReadChunkAndLength( FILE *fp, short *chunk, long *len )
{
if ( fread( chunk, sizeof( short ), 1, fp ) != 1 )
return 0;
if ( fread( len, sizeof( long ), 1, fp ) != 1 )
Error( "Unexpected EOF found" );
return 1;
}
static void LoadMapName( FILE *fp, char *buffer, int thisChunkLen )
{
unsigned short chunkID;
long chunkLen;
long bytesRead = 0;
while ( ReadChunkAndLength( fp, &chunkID, &chunkLen ) )
{
switch ( chunkID )
{
case _3DS_CHUNK_MAT_MAPNAME:
fread( buffer, chunkLen - 6, 1, fp );
break;
default:
fread( s_buffer, chunkLen - 6, 1, fp );
break;
}
bytesRead += chunkLen;
if ( bytesRead >= thisChunkLen )
return;
}
}
static void LoadMaterialList( FILE *fp, long thisChunkLen, _3DSMaterial_t *pMat )
{
long chunkLen;
unsigned short chunkID;
long bytesRead = 0;
_3DSMaterial_t mat;
char curdir[1024];
char buffer[2048];
memset( &mat, 0, sizeof( mat ) );
if ( s_verbose )
printf( " >>> MATERIAL LIST\n" );
while ( ReadChunkAndLength( fp, &chunkID, &chunkLen ) )
{
switch ( chunkID )
{
case _3DS_CHUNK_MAT_NAME:
fread( mat.name, chunkLen - 6, 1, fp );
if ( s_verbose )
printf( " found mat name '%s'\n", mat.name );
break;
case _3DS_CHUNK_TEXMAP:
LoadMapName( fp, mat.texture, chunkLen - 6 );
if ( s_verbose )
printf( " found texture '%s'\n", mat.texture );
break;
case _3DS_CHUNK_SPECMAP:
LoadMapName( fp, mat.specular, chunkLen - 6 );
if ( s_verbose )
printf( " found specular map '%s'\n", mat.specular );
break;
case _3DS_CHUNK_OPACMAP:
LoadMapName( fp, mat.opacity, chunkLen - 6 );
if ( s_verbose )
printf( " found opacity map '%s'\n", mat.opacity );
break;
case _3DS_CHUNK_REFLMAP:
LoadMapName( fp, mat.reflection, chunkLen - 6 );
if ( s_verbose )
printf( " found reflection map '%s'\n", mat.reflection );
break;
case _3DS_CHUNK_BUMPMAP:
LoadMapName( fp, mat.bump, chunkLen - 6 );
if ( s_verbose )
printf( " found bump map '%s'\n", mat.bump );
break;
default:
fread( s_buffer, chunkLen - 6, 1, fp );
break;
}
bytesRead += chunkLen;
if ( bytesRead >= thisChunkLen )
break;
}
Q_getwd( curdir );
if ( mat.texture[0] )
{
sprintf( buffer, "%s%s", curdir, mat.texture );
if ( strstr( buffer, gamedir + 1 ) )
strcpy( mat.texture, strstr( buffer, gamedir + 1 ) + strlen( gamedir ) - 1 );
else
strcpy( mat.texture, buffer );
}
if ( mat.specular[0] )
{
sprintf( buffer, "%s%s", curdir, mat.specular );
if ( strstr( buffer, gamedir + 1 ) )
strcpy( mat.specular, strstr( buffer, gamedir + 1 ) + strlen( gamedir ) - 1 );
else
strcpy( mat.specular, buffer );
}
if ( mat.bump[0] )
{
sprintf( buffer, "%s%s", curdir, mat.bump );
if ( strstr( buffer, gamedir + 1 ) )
strcpy( mat.bump, strstr( buffer, gamedir + 1 ) + strlen( gamedir ) - 1 );
else
strcpy( mat.bump, buffer );
}
if ( mat.reflection[0] )
{
sprintf( buffer, "%s%s", curdir, mat.reflection );
if ( strstr( buffer, gamedir + 1 ) )
strcpy( mat.reflection, strstr( buffer, gamedir + 1 ) + strlen( gamedir ) - 1 );
else
strcpy( mat.reflection, buffer );
}
if ( mat.opacity[0] )
{
sprintf( buffer, "%s%s", curdir, mat.opacity );
if ( strstr( buffer, gamedir + 1 ) )
strcpy( mat.opacity, strstr( buffer, gamedir + 1 ) + strlen( gamedir ) - 1 );
else
strcpy( mat.opacity, buffer );
}
*pMat = mat;
}
static void LoadMeshMaterialGroup( FILE *fp, long thisChunkLen, _3DSMeshMaterialGroup_t *pMMG )
{
_3DSMeshMaterialGroup_t mmg;
memset( &mmg, 0, sizeof( mmg ) );
ReadString( fp, mmg.name );
fread( &mmg.numFaces, sizeof( mmg.numFaces ), 1, fp );
mmg.pFaces = malloc( sizeof( mmg.pFaces[0] ) * mmg.numFaces );
fread( mmg.pFaces, sizeof( mmg.pFaces[0] ), mmg.numFaces, fp );
if ( s_verbose )
{
printf( " >>> MESH MATERIAL GROUP '%s' (%d faces)\n", mmg.name, mmg.numFaces );
{
int i;
for ( i = 0; i < mmg.numFaces; i++ )
{
printf( " %d\n", mmg.pFaces[i] );
}
}
}
*pMMG = mmg;
}
static void LoadNamedTriObject( FILE *fp, long thisChunkLen, _3DSTriObject_t *pTO )
{
long chunkLen;
unsigned short chunkID;
int i = 0;
long bytesRead = 0;
_3DSTriObject_t triObj;
_3DSMeshMaterialGroup_t meshMaterialGroups[MAX_MESH_MATERIAL_GROUPS];
int numMeshMaterialGroups = 0;
memset( &triObj, 0, sizeof( triObj ) );
if ( s_verbose )
printf( " >>> NAMED TRI OBJECT\n" );
while ( ReadChunkAndLength( fp, &chunkID, &chunkLen ) )
{
switch ( chunkID )
{
case _3DS_CHUNK_MSH_MAT_GROUP:
LoadMeshMaterialGroup( fp, chunkLen - 6, &meshMaterialGroups[numMeshMaterialGroups] );
bytesRead += chunkLen;
numMeshMaterialGroups++;
break;
case _3DS_CHUNK_FACE_ARRAY:
fread( &triObj.numFaces, sizeof( triObj.numFaces ), 1, fp );
assert( triObj.pFaces == 0 );
triObj.pFaces = malloc( sizeof( triObj.pFaces[0] ) * triObj.numFaces );
fread( triObj.pFaces, sizeof( triObj.pFaces[0] ), triObj.numFaces, fp );
bytesRead += sizeof( triObj.numFaces ) + triObj.numFaces * sizeof( triObj.pFaces[0] ) + 6;
if ( s_verbose )
{
printf( " found face array with %d faces\n", triObj.numFaces );
for ( i = 0; i < triObj.numFaces; i++ )
{
printf( " %d: %d,%d,%d\n", i, triObj.pFaces[i].a, triObj.pFaces[i].b, triObj.pFaces[i].c );
}
}
break;
case _3DS_CHUNK_POINT_ARRAY:
fread( &triObj.numPoints, sizeof( triObj.numPoints ), 1, fp );
triObj.pPoints = malloc( sizeof( triObj.pPoints[0] ) * triObj.numPoints );
fread( triObj.pPoints, sizeof( triObj.pPoints[0] ), triObj.numPoints, fp );
bytesRead += sizeof( triObj.numPoints ) + triObj.numPoints * sizeof( triObj.pPoints[0] ) + 6;
// flip points around into our coordinate system
for ( i = 0; i < triObj.numPoints; i++ )
{
float x, y, z;
x = triObj.pPoints[i].x;
y = triObj.pPoints[i].y;
z = triObj.pPoints[i].z;
triObj.pPoints[i].x = -y;
triObj.pPoints[i].y = x;
triObj.pPoints[i].z = z;
}
if ( s_verbose )
{
printf( " found point array with %d points\n", triObj.numPoints );
for ( i = 0; i < triObj.numPoints; i++ )
{
printf( " %d: %f,%f,%f\n", i, triObj.pPoints[i].x, triObj.pPoints[i].y, triObj.pPoints[i].z );
}
}
break;
case _3DS_CHUNK_TEX_VERTS:
fread( &triObj.numTexVerts, sizeof( triObj.numTexVerts ), 1, fp );
triObj.pTexVerts = malloc( sizeof( triObj.pTexVerts[0] ) * triObj.numTexVerts );
fread( triObj.pTexVerts, sizeof( triObj.pTexVerts[0] ), triObj.numTexVerts, fp );
bytesRead += sizeof( triObj.numTexVerts ) + sizeof( triObj.pTexVerts[0] ) * triObj.numTexVerts + 6;
if ( s_verbose )
{
printf( " found tex vert array with %d tex verts\n", triObj.numTexVerts );
for ( i = 0; i < triObj.numTexVerts; i++ )
{
printf( " %d: %f,%f\n", i, triObj.pTexVerts[i].s, triObj.pTexVerts[i].t );
}
}
break;
default:
fread( s_buffer, chunkLen - 6, 1, fp );
bytesRead += chunkLen;
break;
}
if ( bytesRead >= thisChunkLen )
break;
}
*pTO = triObj;
if ( numMeshMaterialGroups == 0 )
{
numMeshMaterialGroups = 1;
strcpy( meshMaterialGroups[0].name, "(null)" );
if ( pTO->numTexVerts ) {
printf( "Warning: assigning (null) skin to tri object\n" );
}
}
else
{
assert( pTO->numFaces == meshMaterialGroups[0].numFaces );
}
pTO->pMeshMaterialGroups = malloc( sizeof( _3DSMeshMaterialGroup_t ) * numMeshMaterialGroups );
memcpy( pTO->pMeshMaterialGroups, meshMaterialGroups, numMeshMaterialGroups * sizeof( meshMaterialGroups[0] ) );
pTO->numMeshMaterialGroups = numMeshMaterialGroups;
//
// sanity checks
//
assert( numMeshMaterialGroups <= 1 );
}
static void LoadNamedObject( FILE *fp, long thisChunkLen, _3DSNamedObject_t *pNO )
{
long chunkLen;
unsigned short chunkID;
int i = 0;
long bytesRead = 0;
char name[100];
_3DSTriObject_t triObj[MAX_TRI_OBJECTS];
int numTriObjects = 0;
memset( triObj, 0, sizeof( triObj ) );
bytesRead += ReadString( fp, name );
if ( s_verbose )
printf( " >>> NAMED OBJECT '%s'\n", name );
while ( ReadChunkAndLength( fp, &chunkID, &chunkLen ) )
{
switch ( chunkID )
{
case _3DS_CHUNK_NAMED_TRI_OBJECT:
LoadNamedTriObject( fp, chunkLen - 6, &triObj[numTriObjects] );
numTriObjects++;
break;
default:
fread( s_buffer, chunkLen - 6, 1, fp );
break;
}
bytesRead += chunkLen;
if ( bytesRead >= thisChunkLen )
break;
}
strcpy( pNO->name, name );
pNO->pTriObjects = malloc( sizeof( _3DSTriObject_t ) * numTriObjects );
memcpy( pNO->pTriObjects, triObj, sizeof( triObj[0] ) * numTriObjects );
pNO->numTriObjects = numTriObjects;
assert( numTriObjects <= 1 );
}
static void LoadEditChunk( FILE *fp, long thisChunkLen, _3DSEditChunk_t *pEC )
{
unsigned short chunkID;
long chunkLen;
long bytesRead = 0;
_3DSEditChunk_t editChunk;
_3DSMaterial_t mat[MAX_MATERIALS];
_3DSNamedObject_t namedObjects[MAX_NAMED_OBJECTS];
int numMaterials = 0, numNamedObjects = 0;
memset( &editChunk, 0, sizeof( editChunk ) );
if ( s_verbose )
printf( ">>> EDIT CHUNK\n" );
while ( ReadChunkAndLength( fp, &chunkID, &chunkLen ) )
{
switch ( chunkID )
{
case _3DS_CHUNK_MAT_LIST:
LoadMaterialList( fp, chunkLen - 6, &mat[numMaterials] );
numMaterials++;
break;
case _3DS_CHUNK_NAMED_OBJECT:
LoadNamedObject( fp, chunkLen - 6, &namedObjects[numNamedObjects] );
if ( namedObjects[numNamedObjects].numTriObjects != 0 )
++numNamedObjects;
break;
case _3DS_CHUNK_MESH_VERSION:
default:
fread( s_buffer, chunkLen - 6, 1, fp );
break;
}
bytesRead += chunkLen;
if ( bytesRead >= thisChunkLen )
break;
}
if ( numMaterials == 0 )
{
numMaterials = 1;
strcpy( mat[0].name, "(null)" );
printf( "Warning: no material definitions found\n" );
}
pEC->numNamedObjects = numNamedObjects;
pEC->pMaterials = malloc( sizeof( _3DSMaterial_t ) * numMaterials );
pEC->pNamedObjects = malloc( sizeof( _3DSNamedObject_t ) * numNamedObjects );
memcpy( pEC->pMaterials, mat, numMaterials * sizeof( mat[0] ) );
memcpy( pEC->pNamedObjects, namedObjects, numNamedObjects * sizeof( namedObjects[0] ) );
}
static void Load3DS( const char *filename, _3DS_t *p3DS, qboolean verbose )
{
FILE *fp;
unsigned short chunkID;
long chunkLen;
_3DSEditChunk_t editChunk;
s_verbose = verbose;
if ( ( fp = fopen( filename, "rb" ) ) == 0 )
Error( "Unable to open '%s'", filename );
// read magic number
if ( ( fread( &chunkID, sizeof( short ), 1, fp ) != 1 ) ||
( LittleShort( chunkID ) != _3DS_CHUNK_MAGIC ) )
{
Error( "Missing or incorrect magic number in '%s'", filename );
}
if ( fread( &chunkLen, sizeof( chunkLen ), 1, fp ) != 1 )
Error( "Unexpected EOF encountered in '%s'", filename );
// version number
if ( !ReadChunkAndLength( fp, &chunkID, &chunkLen ) )
Error( "Missing version number in '%s'", filename );
if ( fread( s_buffer, chunkLen - 6, 1, fp ) != 1 )
Error( "Unexpected EOF encountered in '%s'", filename );
while ( ReadChunkAndLength( fp, &chunkID, &chunkLen ) )
{
switch ( chunkID )
{
case _3DS_CHUNK_EDIT:
LoadEditChunk( fp, chunkLen - 6, &editChunk );
break;
case _3DS_CHUNK_KEYFRAME_DATA:
fread( s_buffer, chunkLen - 6, 1, fp );
break;
default:
fread( s_buffer, chunkLen - 6, 1, fp );
break;
}
}
fclose( fp );
p3DS->editChunk = editChunk;
}
static void ComputeNormals( _3DSTriObject_t *pTO, triangle_t *pTris )
{
vec3_t faceNormals[POLYSET_MAXTRIANGLES];
vec3_t vertexNormals[POLYSET_MAXTRIANGLES*3];
vec3_t side0, side1, facenormal;
int f, v;
memset( faceNormals, 0, sizeof( faceNormals ) );
memset( vertexNormals, 0, sizeof( vertexNormals ) );
//
// compute face normals
//
for ( f = 0; f < pTO->numFaces; f++ )
{
VectorSubtract( pTris[f].verts[0], pTris[f].verts[1], side0 );
VectorSubtract( pTris[f].verts[2], pTris[f].verts[1], side1 );
CrossProduct( side0, side1, facenormal );
VectorNormalize( facenormal, faceNormals[f] );
}
//
// sum vertex normals
//
for ( v = 0; v < pTO->numPoints; v++ )
{
for ( f = 0; f < pTO->numFaces; f++ )
{
if ( ( pTO->pFaces[f].a == v ) ||
( pTO->pFaces[f].b == v ) ||
( pTO->pFaces[f].c == v ) )
{
vertexNormals[v][0] += faceNormals[f][0];
vertexNormals[v][1] += faceNormals[f][1];
vertexNormals[v][2] += faceNormals[f][2];
}
}
VectorNormalize( vertexNormals[v], vertexNormals[v] );
}
//
// copy vertex normals into triangles
//
for ( f = 0; f < pTO->numFaces; f++ )
{
int i0 = pTO->pFaces[f].c;
int i1 = pTO->pFaces[f].b;
int i2 = pTO->pFaces[f].a;
VectorCopy( vertexNormals[i0], pTris[f].normals[0] );
VectorCopy( vertexNormals[i1], pTris[f].normals[1] );
VectorCopy( vertexNormals[i2], pTris[f].normals[2] );
}
}
/*
** void _3DS_LoadPolysets
*/
void _3DS_LoadPolysets( const char *filename, polyset_t **ppPSET, int *numpsets, qboolean verbose )
{
_3DS_t _3ds;
int numPolysets;
polyset_t *pPSET;
triangle_t *ptri, *triangles;
int i;
// load the 3DS
memset( &_3ds, 0, sizeof( _3ds ) );
Load3DS( filename, &_3ds, verbose );
// compute information
numPolysets = _3ds.editChunk.numNamedObjects;
// allocate memory
pPSET = calloc( 1, numPolysets * sizeof( polyset_t ) );
triangles = ptri = calloc( 1, POLYSET_MAXTRIANGLES * sizeof( triangle_t ) );
// copy the data over
for ( i = 0; i < numPolysets; i++ )
{
char matnamebuf[1024];
int j;
triangle_t *tri;
_3DSTriObject_t *pTO = &_3ds.editChunk.pNamedObjects[i].pTriObjects[0];
pPSET[i].triangles = ptri;
pPSET[i].numtriangles = pTO->numFaces;
strcpy( pPSET[i].name, _3ds.editChunk.pNamedObjects[i].name );
strcpy( matnamebuf, filename );
if ( strrchr( matnamebuf, '/' ) )
*( strrchr( matnamebuf, '/' ) + 1 )= 0;
strcat( matnamebuf, pTO->pMeshMaterialGroups[0].name );
if ( strstr( matnamebuf, gamedir ) )
strcpy( pPSET[i].materialname, strstr( matnamebuf, gamedir ) + strlen( gamedir ) );
else
strcpy( pPSET[i].materialname, pTO->pMeshMaterialGroups[0].name );
assert( pPSET[i].numtriangles < POLYSET_MAXTRIANGLES );
for ( tri = ptri, j = 0; j < pPSET[i].numtriangles; j++ )
{
int i0 = pTO->pFaces[j].c;
int i1 = pTO->pFaces[j].b;
int i2 = pTO->pFaces[j].a;
tri->verts[0][0] = pTO->pPoints[i0].x;
tri->verts[0][1] = pTO->pPoints[i0].y;
tri->verts[0][2] = pTO->pPoints[i0].z;
tri->verts[1][0] = pTO->pPoints[i1].x;
tri->verts[1][1] = pTO->pPoints[i1].y;
tri->verts[1][2] = pTO->pPoints[i1].z;
tri->verts[2][0] = pTO->pPoints[i2].x;
tri->verts[2][1] = pTO->pPoints[i2].y;
tri->verts[2][2] = pTO->pPoints[i2].z;
/*
for ( k = 0; k < 3; k++ )
{
tri->colors[0][k] = 1;
tri->colors[1][k] = 1;
tri->colors[2][k] = 1;
}
*/
if ( pTO->pTexVerts )
{
tri->texcoords[0][0] = pTO->pTexVerts[i0].s;
tri->texcoords[0][1] = 1.0f - pTO->pTexVerts[i0].t;
tri->texcoords[1][0] = pTO->pTexVerts[i1].s;
tri->texcoords[1][1] = 1.0f - pTO->pTexVerts[i1].t;
tri->texcoords[2][0] = pTO->pTexVerts[i2].s;
tri->texcoords[2][1] = 1.0f - pTO->pTexVerts[i2].t;
}
tri++;
}
ptri += pPSET[i].numtriangles;
assert( ptri - triangles < POLYSET_MAXTRIANGLES );
}
// compute normal data
#if 0
for ( i = 0; i < numPolysets; i++ )
{
// unique vertices based solely on vertex position
ComputeNormals( &_3ds.editChunk.pNamedObjects[i].pTriObjects[0],
pPSET[i].triangles );
}
#endif
free( _3ds.editChunk.pMaterials );
free( _3ds.editChunk.pNamedObjects );
*ppPSET = pPSET;
*numpsets = numPolysets;
}

View File

@@ -0,0 +1,139 @@
/*
Copyright (C) 1999-2006 Id Software, Inc. and contributors.
For a list of contributors, see the accompanying CONTRIBUTORS file.
This file is part of GtkRadiant.
GtkRadiant is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
GtkRadiant is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
typedef struct
{
float x, y, z;
} _3DSPoint_t;
typedef struct
{
short a, b, c;
short flags;
} _3DSFace_t;
typedef struct
{
float s, t;
} _3DSTexVert_t;
typedef struct
{
char name[100];
short numFaces;
short *pFaces;
} _3DSMeshMaterialGroup_t;
typedef struct
{
char name[80];
char texture[100];
char specular[100];
char reflection[100];
char bump[100];
char opacity[100];
} _3DSMaterial_t;
typedef struct
{
short numFaces, numPoints, numTexVerts;
int numMeshMaterialGroups;
_3DSPoint_t *pPoints;
_3DSFace_t *pFaces;
_3DSTexVert_t *pTexVerts;
_3DSMeshMaterialGroup_t *pMeshMaterialGroups;
} _3DSTriObject_t;
typedef struct
{
char name[100];
int numTriObjects;
_3DSTriObject_t *pTriObjects;
} _3DSNamedObject_t;
typedef struct
{
int numNamedObjects;
int numMaterials;
_3DSNamedObject_t *pNamedObjects;
_3DSMaterial_t *pMaterials;
} _3DSEditChunk_t;
typedef struct
{
_3DSEditChunk_t editChunk;
} _3DS_t;
#define _3DS_CHUNK_NULL 0x0000
#define _3DS_CHUNK_UNKNOWN0 0x0001
#define _3DS_CHUNK_M3D_VERSION 0x0002
#define _3DS_CHUNK_M3D_KFVERSION 0x0005
#define _3DS_CHUNK_COLOR_F 0x0010
#define _3DS_CHUNK_COLOR_24 0x0011
#define _3DS_CHUNK_LIN_COLOR_24 0x0012
#define _3DS_CHUNK_LIN_COLOR_F 0x0013
#define _3DS_CHUNK_INT_PERCENTAGE 0x0030
#define _3DS_CHUNK_FLOAT_PERCENT 0x0031
#define _3DS_CHUNK_MASTER_SCALE 0x0100
#define _3DS_CHUNK_CHUNK_TYPE 0x0995
#define _3DS_CHUNK_CHUNK_UNIQUE 0x0996
#define _3DS_CHUNK_NOT_CHUNK 0x0997
#define _3DS_CHUNK_CONTAINER 0x0998
#define _3DS_CHUNK_IS_CHUNK 0x0999
#define _3DS_CHUNK_C_SXP_SELFI_MASKDATA 0x0c3c
#define _3DS_CHUNK_BITMAP 0x1100
#define _3DS_CHUNK_USE_BITMAP 0x1101
#define _3DS_CHUNK_SOLID_BGND 0x1200
#define _3DS_CHUNK_USE_SOLID_BGND 0x1201
#define _3DS_CHUNK_EDIT 0x3d3d
#define _3DS_CHUNK_MESH_VERSION 0x3d3e
#define _3DS_CHUNK_NAMED_OBJECT 0x4000
#define _3DS_CHUNK_NAMED_TRI_OBJECT 0x4100
#define _3DS_CHUNK_POINT_ARRAY 0x4110
#define _3DS_CHUNK_POINT_FLAG_ARRAY 0x4111
#define _3DS_CHUNK_FACE_ARRAY 0x4120
#define _3DS_CHUNK_MSH_MAT_GROUP 0x4130
#define _3DS_CHUNK_TEX_VERTS 0x4140
#define _3DS_CHUNK_SMOOTH_GROUP 0x4150
#define _3DS_CHUNK_MESH_MATRIX 0x4160
#define _3DS_CHUNK_MAGIC 0x4d4d
#define _3DS_CHUNK_MAT_NAME 0xa000
#define _3DS_CHUNK_TEXMAP 0xa200
#define _3DS_CHUNK_SPECMAP 0xa204
#define _3DS_CHUNK_OPACMAP 0xa210
#define _3DS_CHUNK_REFLMAP 0xa220
#define _3DS_CHUNK_BUMPMAP 0xa230
#define _3DS_CHUNK_MAT_MAPNAME 0xa300
#define _3DS_CHUNK_MAT_LIST 0xafff
#define _3DS_CHUNK_KEYFRAME_DATA 0xb000
void _3DS_LoadPolysets( const char *filename, polyset_t **ppPSET, int *numpsets, qboolean verbose );

View File

@@ -0,0 +1,771 @@
/*
Copyright (C) 1999-2006 Id Software, Inc. and contributors.
For a list of contributors, see the accompanying CONTRIBUTORS file.
This file is part of GtkRadiant.
GtkRadiant is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
GtkRadiant is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "q3data.h"
#if 0
/*
==================
MTF
==================
*/
cblock_t MTF (cblock_t in)
{
int i, j, b, code;
byte *out_p;
int index[256];
cblock_t out;
out_p = out.data = malloc(in.count + 4);
// write count
*out_p++ = in.count&255;
*out_p++ = (in.count>>8)&255;
*out_p++ = (in.count>>16)&255;
*out_p++ = (in.count>>24)&255;
for (i=0 ; i<256 ; i++)
index[i] = i;
for (i=0 ; i<in.count ; i++)
{
b = in.data[i];
code = index[b];
*out_p++ = code;
// shuffle b indexes to 0
for (j=0 ; j<256 ; j++)
if (index[j] < code)
index[j]++;
index[b] = 0;
}
out.count = out_p - out.data;
return out;
}
//==========================================================================
int bwt_size;
byte *bwt_data;
int bwtCompare (const void *elem1, const void *elem2)
{
int i;
int i1, i2;
int b1, b2;
i1 = *(int *)elem1;
i2 = *(int *)elem2;
for (i=0 ; i<bwt_size ; i++)
{
b1 = bwt_data[i1];
b2 = bwt_data[i2];
if (b1 < b2)
return -1;
if (b1 > b2)
return 1;
if (++i1 == bwt_size)
i1 = 0;
if (++i2 == bwt_size)
i2 = 0;
}
return 0;
}
/*
==================
BWT
==================
*/
cblock_t BWT (cblock_t in)
{
int *sorted;
int i;
byte *out_p;
cblock_t out;
bwt_size = in.count;
bwt_data = in.data;
sorted = malloc(in.count*sizeof(*sorted));
for (i=0 ; i<in.count ; i++)
sorted[i] = i;
qsort (sorted, in.count, sizeof(*sorted), bwtCompare);
out_p = out.data = malloc(in.count + 8);
// write count
*out_p++ = in.count&255;
*out_p++ = (in.count>>8)&255;
*out_p++ = (in.count>>16)&255;
*out_p++ = (in.count>>24)&255;
// write head index
for (i=0 ; i<in.count ; i++)
if (sorted[i] == 0)
break;
*out_p++ = i&255;
*out_p++ = (i>>8)&255;
*out_p++ = (i>>16)&255;
*out_p++ = (i>>24)&255;
// write the L column
for (i=0 ; i<in.count ; i++)
*out_p++ = in.data[(sorted[i]+in.count-1)%in.count];
free (sorted);
out.count = out_p - out.data;
return out;
}
//==========================================================================
typedef struct hnode_s
{
int count;
qboolean used;
int children[2];
} hnode_t;
int numhnodes;
hnode_t hnodes[512];
unsigned charbits[256];
int charbitscount[256];
int SmallestNode (void)
{
int i;
int best, bestnode;
best = 99999999;
bestnode = -1;
for (i=0 ; i<numhnodes ; i++)
{
if (hnodes[i].used)
continue;
if (!hnodes[i].count)
continue;
if (hnodes[i].count < best)
{
best = hnodes[i].count;
bestnode = i;
}
}
if (bestnode == -1)
return -1;
hnodes[bestnode].used = true;
return bestnode;
}
void BuildChars (int nodenum, unsigned bits, int bitcount)
{
hnode_t *node;
if (nodenum < 256)
{
if (bitcount > 32)
Error ("bitcount > 32");
charbits[nodenum] = bits;
charbitscount[nodenum] = bitcount;
return;
}
node = &hnodes[nodenum];
bits <<= 1;
BuildChars (node->children[0], bits, bitcount+1);
bits |= 1;
BuildChars (node->children[1], bits, bitcount+1);
}
/*
==================
Huffman
==================
*/
cblock_t Huffman (cblock_t in)
{
int i;
hnode_t *node;
int outbits, c;
unsigned bits;
byte *out_p;
cblock_t out;
int max, maxchar;
// count
memset (hnodes, 0, sizeof(hnodes));
for (i=0 ; i<in.count ; i++)
hnodes[in.data[i]].count++;
// normalize counts
max = 0;
maxchar = 0;
for (i=0 ; i<256 ; i++)
{
if (hnodes[i].count > max)
{
max = hnodes[i].count;
maxchar = i;
}
}
if (max == 0)
Error ("Huffman: max == 0");
for (i=0 ; i<256 ; i++)
{
hnodes[i].count = (hnodes[i].count*255+max-1) / max;
}
// build the nodes
numhnodes = 256;
while (numhnodes != 511)
{
node = &hnodes[numhnodes];
// pick two lowest counts
node->children[0] = SmallestNode ();
if (node->children[0] == -1)
break; // no more
node->children[1] = SmallestNode ();
if (node->children[1] == -1)
{
if (node->children[0] != numhnodes-1)
Error ("Bad smallestnode");
break;
}
node->count = hnodes[node->children[0]].count +
hnodes[node->children[1]].count;
numhnodes++;
}
BuildChars (numhnodes-1, 0, 0);
out_p = out.data = malloc(in.count*2 + 1024);
memset (out_p, 0, in.count*2+1024);
// write count
*out_p++ = in.count&255;
*out_p++ = (in.count>>8)&255;
*out_p++ = (in.count>>16)&255;
*out_p++ = (in.count>>24)&255;
// save out the 256 normalized counts so the tree can be recreated
for (i=0 ; i<256 ; i++)
*out_p++ = hnodes[i].count;
// write bits
outbits = 0;
for (i=0 ; i<in.count ; i++)
{
c = charbitscount[in.data[i]];
bits = charbits[in.data[i]];
while (c)
{
c--;
if (bits & (1<<c))
out_p[outbits>>3] |= 1<<(outbits&7);
outbits++;
}
}
out_p += (outbits+7)>>3;
out.count = out_p - out.data;
return out;
}
//==========================================================================
/*
==================
RLE
==================
*/
#define RLE_CODE 0xe8
#define RLE_TRIPPLE 0xe9
int rle_counts[256];
int rle_bytes[256];
cblock_t RLE (cblock_t in)
{
int i;
byte *out_p;
int val;
int repeat;
cblock_t out;
out_p = out.data = malloc (in.count*2);
// write count
*out_p++ = in.count&255;
*out_p++ = (in.count>>8)&255;
*out_p++ = (in.count>>16)&255;
*out_p++ = (in.count>>24)&255;
for (i=0 ; i<in.count ; )
{
val = in.data[i];
rle_bytes[val]++;
repeat = 1;
i++;
while (i<in.count && repeat < 255 && in.data[i] == val)
{
repeat++;
i++;
}
if (repeat < 256)
rle_counts[repeat]++;
if (repeat > 3 || val == RLE_CODE)
{
*out_p++ = RLE_CODE;
*out_p++ = val;
*out_p++ = repeat;
}
else
{
while (repeat--)
*out_p++ = val;
}
}
out.count = out_p - out.data;
return out;
}
//==========================================================================
unsigned lzss_head[256];
unsigned lzss_next[0x20000];
/*
==================
LZSS
==================
*/
#define BACK_WINDOW 0x10000
#define BACK_BITS 16
#define FRONT_WINDOW 16
#define FRONT_BITS 4
cblock_t LZSS (cblock_t in)
{
int i;
byte *out_p;
cblock_t out;
int val;
int j, start, max;
int bestlength, beststart;
int outbits;
if (in.count >= sizeof(lzss_next)/4)
Error ("LZSS: too big");
memset (lzss_head, -1, sizeof(lzss_head));
out_p = out.data = malloc (in.count*2);
memset (out.data, 0, in.count*2);
// write count
*out_p++ = in.count&255;
*out_p++ = (in.count>>8)&255;
*out_p++ = (in.count>>16)&255;
*out_p++ = (in.count>>24)&255;
outbits = 0;
for (i=0 ; i<in.count ; )
{
val = in.data[i];
#if 1
// chained search
bestlength = 0;
beststart = 0;
max = FRONT_WINDOW;
if (i + max > in.count)
max = in.count - i;
start = lzss_head[val];
while (start != -1 && start >= i-BACK_WINDOW)
{
// count match length
for (j=0 ; j<max ; j++)
if (in.data[start+j] != in.data[i+j])
break;
if (j > bestlength)
{
bestlength = j;
beststart = start;
}
start = lzss_next[start];
}
#else
// slow simple search
// search for a match
max = FRONT_WINDOW;
if (i + max > in.count)
max = in.count - i;
start = i - BACK_WINDOW;
if (start < 0)
start = 0;
bestlength = 0;
beststart = 0;
for ( ; start < i ; start++)
{
if (in.data[start] != val)
continue;
// count match length
for (j=0 ; j<max ; j++)
if (in.data[start+j] != in.data[i+j])
break;
if (j > bestlength)
{
bestlength = j;
beststart = start;
}
}
#endif
beststart = BACK_WINDOW - (i-beststart);
if (bestlength < 3)
{ // output a single char
bestlength = 1;
out_p[outbits>>3] |= 1<<(outbits&7); // set bit to mark char
outbits++;
for (j=0 ; j<8 ; j++, outbits++)
if (val & (1<<j) )
out_p[outbits>>3] |= 1<<(outbits&7);
}
else
{ // output a phrase
outbits++; // leave a 0 bit to mark phrase
for (j=0 ; j<BACK_BITS ; j++, outbits++)
if (beststart & (1<<j) )
out_p[outbits>>3] |= 1<<(outbits&7);
for (j=0 ; j<FRONT_BITS ; j++, outbits++)
if (bestlength & (1<<j) )
out_p[outbits>>3] |= 1<<(outbits&7);
}
while (bestlength--)
{
val = in.data[i];
lzss_next[i] = lzss_head[val];
lzss_head[val] = i;
i++;
}
}
out_p += (outbits+7)>>3;
out.count = out_p - out.data;
return out;
}
//==========================================================================
#define MIN_REPT 15
#define MAX_REPT 0
#define HUF_TOKENS (256+MAX_REPT)
unsigned charbits1[256][HUF_TOKENS];
int charbitscount1[256][HUF_TOKENS];
hnode_t hnodes1[256][HUF_TOKENS*2];
int numhnodes1[256];
int order0counts[256];
/*
==================
SmallestNode1
==================
*/
int SmallestNode1 (hnode_t *hnodes, int numhnodes)
{
int i;
int best, bestnode;
best = 99999999;
bestnode = -1;
for (i=0 ; i<numhnodes ; i++)
{
if (hnodes[i].used)
continue;
if (!hnodes[i].count)
continue;
if (hnodes[i].count < best)
{
best = hnodes[i].count;
bestnode = i;
}
}
if (bestnode == -1)
return -1;
hnodes[bestnode].used = true;
return bestnode;
}
/*
==================
BuildChars1
==================
*/
void BuildChars1 (int prev, int nodenum, unsigned bits, int bitcount)
{
hnode_t *node;
if (nodenum < HUF_TOKENS)
{
if (bitcount > 32)
Error ("bitcount > 32");
charbits1[prev][nodenum] = bits;
charbitscount1[prev][nodenum] = bitcount;
return;
}
node = &hnodes1[prev][nodenum];
bits <<= 1;
BuildChars1 (prev, node->children[0], bits, bitcount+1);
bits |= 1;
BuildChars1 (prev, node->children[1], bits, bitcount+1);
}
/*
==================
BuildTree1
==================
*/
void BuildTree1 (int prev)
{
hnode_t *node, *nodebase;
int numhnodes;
// build the nodes
numhnodes = HUF_TOKENS;
nodebase = hnodes1[prev];
while (1)
{
node = &nodebase[numhnodes];
// pick two lowest counts
node->children[0] = SmallestNode1 (nodebase, numhnodes);
if (node->children[0] == -1)
break; // no more
node->children[1] = SmallestNode1 (nodebase, numhnodes);
if (node->children[1] == -1)
break;
node->count = nodebase[node->children[0]].count +
nodebase[node->children[1]].count;
numhnodes++;
}
numhnodes1[prev] = numhnodes-1;
BuildChars1 (prev, numhnodes-1, 0, 0);
}
/*
==================
Huffman1_Count
==================
*/
void Huffman1_Count (cblock_t in)
{
int i;
int prev;
int v;
int rept;
prev = 0;
for (i=0 ; i<in.count ; i++)
{
v = in.data[i];
order0counts[v]++;
hnodes1[prev][v].count++;
prev = v;
#if 1
for (rept=1 ; i+rept < in.count && rept < MAX_REPT ; rept++)
if (in.data[i+rept] != v)
break;
if (rept > MIN_REPT)
{
hnodes1[prev][255+rept].count++;
i += rept-1;
}
#endif
}
}
/*
==================
Huffman1_Build
==================
*/
byte scaled[256][HUF_TOKENS];
void Huffman1_Build (FILE *f)
{
int i, j, v;
int max;
int total;
for (i=0 ; i<256 ; i++)
{
// normalize and save the counts
max = 0;
for (j=0 ; j<HUF_TOKENS ; j++)
{
if (hnodes1[i][j].count > max)
max = hnodes1[i][j].count;
}
if (max == 0)
max = 1;
total = 0;
for (j=0 ; j<HUF_TOKENS ; j++)
{ // easy to overflow 32 bits here!
v = (hnodes1[i][j].count*(double)255+max-1)/max;
if (v > 255)
Error ("v > 255");
scaled[i][j] = hnodes1[i][j].count = v;
if (v)
total++;
}
if (total == 1)
{ // must have two tokens
if (!scaled[i][0])
scaled[i][0] = hnodes1[i][0].count = 1;
else
scaled[i][1] = hnodes1[i][1].count = 1;
}
BuildTree1 (i);
}
#if 0
// count up the total bits
total = 0;
for (i=0 ; i<256 ; i++)
for (j=0 ; j<256 ; j++)
total += charbitscount1[i][j] * hnodes1[i][j].count;
total = (total+7)/8;
printf ("%i bytes huffman1 compressed\n", total);
#endif
fwrite (scaled, 1, sizeof(scaled), f);
}
/*
==================
Huffman1
Order 1 compression with pre-built table
==================
*/
cblock_t Huffman1 (cblock_t in)
{
int i;
int outbits, c;
unsigned bits;
byte *out_p;
cblock_t out;
int prev;
int v;
int rept;
out_p = out.data = malloc(in.count*2 + 1024);
memset (out_p, 0, in.count*2+1024);
// write count
*out_p++ = in.count&255;
*out_p++ = (in.count>>8)&255;
*out_p++ = (in.count>>16)&255;
*out_p++ = (in.count>>24)&255;
// write bits
outbits = 0;
prev = 0;
for (i=0 ; i<in.count ; i++)
{
v = in.data[i];
c = charbitscount1[prev][v];
bits = charbits1[prev][v];
if (!c)
Error ("!bits");
while (c)
{
c--;
if (bits & (1<<c))
out_p[outbits>>3] |= 1<<(outbits&7);
outbits++;
}
prev = v;
#if 1
// check for repeat encodes
for (rept=1 ; i+rept < in.count && rept < MAX_REPT ; rept++)
if (in.data[i+rept] != v)
break;
if (rept > MIN_REPT)
{
c = charbitscount1[prev][255+rept];
bits = charbits1[prev][255+rept];
if (!c)
Error ("!bits");
while (c)
{
c--;
if (bits & (1<<c))
out_p[outbits>>3] |= 1<<(outbits&7);
outbits++;
}
i += rept-1;
}
#endif
}
out_p += (outbits+7)>>3;
out.count = out_p - out.data;
return out;
}
#endif

View File

@@ -0,0 +1,486 @@
/*
Copyright (C) 1999-2006 Id Software, Inc. and contributors.
For a list of contributors, see the accompanying CONTRIBUTORS file.
This file is part of GtkRadiant.
GtkRadiant is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
GtkRadiant is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "q3data.h"
byte *byteimage, *lbmpalette;
int byteimagewidth, byteimageheight;
char mip_prefix[1024]; // directory to dump the textures in
qboolean colormap_issued;
byte colormap_palette[768];
/*
==============
Cmd_Grab
$grab filename x y width height
==============
*/
void Cmd_Grab (void)
{
int xl,yl,w,h,y;
byte *cropped;
char savename[1024];
char dest[1024];
GetToken (qfalse);
if (token[0] == '/' || token[0] == '\\')
sprintf (savename, "%s%s.pcx", writedir, token+1);
else
sprintf (savename, "%spics/%s.pcx", writedir, token);
if (g_release)
{
if (token[0] == '/' || token[0] == '\\')
sprintf (dest, "%s.pcx", token+1);
else
sprintf (dest, "pics/%s.pcx", token);
ReleaseFile (dest);
return;
}
GetToken (qfalse);
xl = atoi (token);
GetToken (qfalse);
yl = atoi (token);
GetToken (qfalse);
w = atoi (token);
GetToken (qfalse);
h = atoi (token);
if (xl<0 || yl<0 || w<0 || h<0 || xl+w>byteimagewidth || yl+h>byteimageheight)
Error ("GrabPic: Bad size: %i, %i, %i, %i",xl,yl,w,h);
// crop it to the proper size
cropped = malloc (w*h);
for (y=0 ; y<h ; y++)
{
memcpy (cropped+y*w, byteimage+(y+yl)*byteimagewidth+xl, w);
}
// save off the new image
printf ("saving %s\n", savename);
CreatePath (savename);
WritePCXfile (savename, cropped, w, h, lbmpalette);
free (cropped);
}
/*
==============
Cmd_Raw
$grab filename x y width height
==============
*/
void Cmd_Raw (void)
{
int xl,yl,w,h,y;
byte *cropped;
char savename[1024];
char dest[1024];
GetToken (qfalse);
sprintf (savename, "%s%s.lmp", writedir, token);
if (g_release)
{
sprintf (dest, "%s.lmp", token);
ReleaseFile (dest);
return;
}
GetToken (qfalse);
xl = atoi (token);
GetToken (qfalse);
yl = atoi (token);
GetToken (qfalse);
w = atoi (token);
GetToken (qfalse);
h = atoi (token);
if (xl<0 || yl<0 || w<0 || h<0 || xl+w>byteimagewidth || yl+h>byteimageheight)
Error ("GrabPic: Bad size: %i, %i, %i, %i",xl,yl,w,h);
// crop it to the proper size
cropped = malloc (w*h);
for (y=0 ; y<h ; y++)
{
memcpy (cropped+y*w, byteimage+(y+yl)*byteimagewidth+xl, w);
}
// save off the new image
printf ("saving %s\n", savename);
CreatePath (savename);
SaveFile (savename, cropped, w*h);
free (cropped);
}
/*
=============================================================================
COLORMAP GRABBING
=============================================================================
*/
/*
===============
BestColor
===============
*/
byte BestColor (int r, int g, int b, int start, int stop)
{
int i;
int dr, dg, db;
int bestdistortion, distortion;
int bestcolor;
byte *pal;
//
// let any color go to 0 as a last resort
//
bestdistortion = 256*256*4;
bestcolor = 0;
pal = colormap_palette + start*3;
for (i=start ; i<= stop ; i++)
{
dr = r - (int)pal[0];
dg = g - (int)pal[1];
db = b - (int)pal[2];
pal += 3;
distortion = dr*dr + dg*dg + db*db;
if (distortion < bestdistortion)
{
if (!distortion)
return i; // perfect match
bestdistortion = distortion;
bestcolor = i;
}
}
return bestcolor;
}
/*
==============
Cmd_Colormap
$colormap filename
the brightes colormap is first in the table (FIXME: reverse this now?)
64 rows of 256 : lightmaps
256 rows of 256 : translucency table
==============
*/
void Cmd_Colormap (void)
{
int levels, brights;
int l, c;
float frac, red, green, blue;
float range;
byte *cropped, *lump_p;
char savename[1024];
char dest[1024];
colormap_issued = qtrue;
if (!g_release)
memcpy (colormap_palette, lbmpalette, 768);
if (!TokenAvailable ())
{ // just setting colormap_issued
return;
}
GetToken (qfalse);
sprintf (savename, "%spics/%s.pcx", writedir, token);
if (g_release)
{
sprintf (dest, "pics/%s.pcx", token);
ReleaseFile (dest);
return;
}
range = 2;
levels = 64;
brights = 1; // ignore 255 (transparent)
cropped = malloc((levels+256)*256);
lump_p = cropped;
// shaded levels
for (l=0;l<levels;l++)
{
frac = range - range*(float)l/(levels-1);
for (c=0 ; c<256-brights ; c++)
{
red = lbmpalette[c*3];
green = lbmpalette[c*3+1];
blue = lbmpalette[c*3+2];
red = (int)(red*frac+0.5);
green = (int)(green*frac+0.5);
blue = (int)(blue*frac+0.5);
//
// note: 254 instead of 255 because 255 is the transparent color, and we
// don't want anything remapping to that
// don't use color 0, because NT can't remap that (or 255)
//
*lump_p++ = BestColor(red,green,blue, 1, 254);
}
// fullbrights allways stay the same
for ( ; c<256 ; c++)
*lump_p++ = c;
}
// 66% transparancy table
for (l=0;l<255;l++)
{
for (c=0 ; c<255 ; c++)
{
red = lbmpalette[c*3]*0.33 + lbmpalette[l*3]*0.66;
green = lbmpalette[c*3+1]*0.33 + lbmpalette[l*3+1]*0.66;
blue = lbmpalette[c*3+2]*0.33 + lbmpalette[l*3+2]*0.66;
*lump_p++ = BestColor(red,green,blue, 1, 254);
}
*lump_p++ = 255;
}
for (c=0 ; c<256 ; c++)
*lump_p++ = 255;
// save off the new image
printf ("saving %s\n", savename);
CreatePath (savename);
WritePCXfile (savename, cropped, 256, levels+256, lbmpalette);
free (cropped);
}
/*
=============================================================================
MIPTEX GRABBING
=============================================================================
*/
byte pixdata[256];
int d_red, d_green, d_blue;
byte palmap[32][32][32];
qboolean palmap_built;
/*
=============
FindColor
=============
*/
int FindColor (int r, int g, int b)
{
int bestcolor;
if (r > 255)
r = 255;
if (r < 0)
r = 0;
if (g > 255)
g = 255;
if (g < 0)
g = 0;
if (b > 255)
b = 255;
if (b < 0)
b = 0;
#ifndef TABLECOLORS
bestcolor = BestColor (r, g, b, 0, 254);
#else
bestcolor = palmap[r>>3][g>>3][b>>3];
#endif
return bestcolor;
}
void BuildPalmap (void)
{
#ifdef TABLECOLORS
int r, g, b;
int bestcolor;
if (palmap_built)
return;
palmap_built = qtrue;
for (r=4 ; r<256 ; r+=8)
{
for (g=4 ; g<256 ; g+=8)
{
for (b=4 ; b<256 ; b+=8)
{
bestcolor = BestColor (r, g, b, 1, 254);
palmap[r>>3][g>>3][b>>3] = bestcolor;
}
}
}
#endif
if (!colormap_issued)
Error ("You must issue a $colormap command first");
}
/*
=============
AveragePixels
=============
*/
byte AveragePixels (int count)
{
int r,g,b;
int i;
int vis;
int pix;
int bestcolor;
byte *pal;
int fullbright;
vis = 0;
r = g = b = 0;
fullbright = 0;
for (i=0 ; i<count ; i++)
{
pix = pixdata[i];
r += lbmpalette[pix*3];
g += lbmpalette[pix*3+1];
b += lbmpalette[pix*3+2];
vis++;
}
r /= vis;
g /= vis;
b /= vis;
// error diffusion
r += d_red;
g += d_green;
b += d_blue;
//
// find the best color
//
bestcolor = FindColor (r, g, b);
// error diffusion
pal = colormap_palette + bestcolor*3;
d_red = r - (int)pal[0];
d_green = g - (int)pal[1];
d_blue = b - (int)pal[2];
return bestcolor;
}
/*
=============================================================================
ENVIRONMENT MAP GRABBING
Creates six pcx files from tga files without any palette edge seams
also copies the tga files for GL rendering.
=============================================================================
*/
// 3dstudio environment map suffixes
char *suf[6] = {"rt", "ft", "lf", "bk", "up", "dn"};
/*
=================
Cmd_Environment
=================
*/
void Cmd_Environment (void)
{
char name[1024];
int i, x, y;
byte image[256*256];
byte *tga;
GetToken (qfalse);
if (g_release)
{
for (i=0 ; i<6 ; i++)
{
sprintf (name, "env/%s%s.pcx", token, suf[i]);
ReleaseFile (name);
sprintf (name, "env/%s%s.tga", token, suf[i]);
ReleaseFile (name);
}
return;
}
// get the palette
BuildPalmap ();
sprintf (name, "%senv/", gamedir);
CreatePath (name);
// convert the images
for (i=0 ; i<6 ; i++)
{
sprintf (name, "%senv/%s%s.tga", gamedir, token, suf[i]);
printf ("loading %s...\n", name);
LoadTGA (name, &tga, NULL, NULL);
for (y=0 ; y<256 ; y++)
{
for (x=0 ; x<256 ; x++)
{
image[y*256+x] = FindColor (tga[(y*256+x)*4+0],tga[(y*256+x)*4+1],tga[(y*256+x)*4+2]);
}
}
free (tga);
sprintf (name, "%senv/%s%s.pcx", writedir, token, suf[i]);
if (FileTime (name) != -1)
printf ("%s already exists, not overwriting.\n", name);
else
WritePCXfile (name, image, 256, 256, colormap_palette);
}
}

View File

@@ -0,0 +1,214 @@
/*
Copyright (C) 1999-2006 Id Software, Inc. and contributors.
For a list of contributors, see the accompanying CONTRIBUTORS file.
This file is part of GtkRadiant.
GtkRadiant is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
GtkRadiant is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <assert.h>
#ifdef WIN32
#include <io.h>
#endif
#include "md3lib.h"
#if defined (__linux__) || defined (__APPLE__)
#define filelength Q_filelength
#endif
/*
** MD3_ComputeTagFromTri
*/
void MD3_ComputeTagFromTri( md3Tag_t *pTag, const float pTri[3][3] )
{
float len[3];
vec3_t axes[3], sides[3];
int longestSide, shortestSide, hypotSide;
int origin;
int j;
float d;
memset( axes, 0, sizeof( axes ) );
memset( sides, 0, sizeof( sides ) );
//
// compute sides
//
for ( j = 0; j < 3; j++ )
{
sides[j][0] = pTri[(j+1)%3][0] - pTri[j][0];
sides[j][1] = pTri[(j+1)%3][1] - pTri[j][1];
sides[j][2] = pTri[(j+1)%3][2] - pTri[j][2];
len[j] = ( float ) sqrt( DotProduct( sides[j], sides[j] ) );
}
#if 0
if ( len[0] > len[1] && len[0] > len[2] )
{
longestSide = 0; shortestSide = 1; origin = 2;
}
else if ( len[1] > len[0] && len[1] > len[2] )
{
longestSide = 1; shortestSide = 2; origin = 0;
}
else if ( len[2] > len[0] && len[2] > len[1] )
{
longestSide = 2; shortestSide = 0; origin = 1;
}
else
{
Error( "invalid tag triangle, must be a right triangle with unequal length sides" );
}
#endif
if ( len[0] > len[1] && len[0] > len[2] ) {
hypotSide = 0;
origin = 2;
} else if ( len[1] > len[0] && len[1] > len[2] ) {
hypotSide = 1;
origin = 0;
} else if ( len[2] > len[0] && len[2] > len[1] ) {
hypotSide = 2;
origin = 1;
}
len[hypotSide] = -1;
if ( len[0] > len[1] && len[0] > len[2] ) {
longestSide = 0;
} else if ( len[1] > len[0] && len[1] > len[2] ) {
longestSide = 1;
} else if ( len[2] > len[0] && len[2] > len[1] ) {
longestSide = 2;
}
len[longestSide] = -1;
if ( len[0] > len[1] && len[0] > len[2] ) {
shortestSide = 0;
} else if ( len[1] > len[0] && len[1] > len[2] ) {
shortestSide = 1;
} else if ( len[2] > len[0] && len[2] > len[1] ) {
shortestSide = 2;
}
len[shortestSide] = -1;
// VectorNormalize( sides[shortestSide], axes[0] );
// VectorNormalize( sides[longestSide], axes[1] );
VectorNormalize( sides[longestSide], axes[0] );
VectorNormalize( sides[shortestSide], axes[1] );
// project shortest side so that it is exactly 90 degrees to the longer side
d = DotProduct( axes[0], axes[1] );
VectorMA( axes[0], -d, axes[1], axes[0] );
VectorNormalize( axes[0], axes[0] );
CrossProduct( sides[longestSide], sides[shortestSide], axes[2] );
VectorNormalize( axes[2], axes[2] );
pTag->origin[0] = pTri[origin][0];
pTag->origin[1] = pTri[origin][1];
pTag->origin[2] = pTri[origin][2];
VectorCopy( axes[0], pTag->axis[0] );
VectorCopy( axes[1], pTag->axis[1] );
VectorCopy( axes[2], pTag->axis[2] );
}
/*
==============
MD3_Dump
==============
*/
void MD3_Dump( const char *filename )
{
md3Header_t header;
md3Tag_t *pTag;
md3Surface_t *pSurface;
FILE *fp;
void *_buffer;
void *buffer;
long fileSize;
int i;
if ( ( fp = fopen( filename, "rb" ) ) == 0 )
{
Error( "Unable to open '%s'\n", filename );
}
fileSize = filelength( fileno( fp ) );
_buffer = malloc( filelength( fileno( fp ) ) );
fread( _buffer, fileSize, 1, fp );
fclose( fp );
buffer = ( char * ) _buffer;
header = *( md3Header_t * ) _buffer;
if ( header.ident != MD3_IDENT )
{
Error( "Incorrect ident for '%s'\n", filename );
}
printf( "Contents of '%s'\n", filename );
printf( " version: %d\n", header.version );
printf( " name: %s\n", header.name );
printf( " num frames: %d\n", header.numFrames );
printf( " num tags: %d\n", header.numTags );
printf( " num surfaces: %d\n", header.numSurfaces );
printf( " num skins: %d\n", header.numSkins );
printf( " file size: %d\n", fileSize );
printf( "--- TAGS ---\n" );
pTag = ( md3Tag_t * ) ( ( ( char * ) buffer ) + header.ofsTags );
for ( i = 0; i < header.numTags; i++, pTag++ )
{
printf( " tag %d ('%s')\n", i, pTag->name );
printf( " origin: %f,%f,%f\n", pTag->origin[0], pTag->origin[1], pTag->origin[2] );
printf( " vf: %f,%f,%f\n", pTag->axis[0][0], pTag->axis[0][1], pTag->axis[0][2] );
printf( " vr: %f,%f,%f\n", pTag->axis[1][0], pTag->axis[1][1], pTag->axis[1][2] );
printf( " vu: %f,%f,%f\n", pTag->axis[2][0], pTag->axis[2][1], pTag->axis[2][2] );
}
printf( "--- SURFACES ---\n" );
pSurface = ( md3Surface_t * ) ( ( ( char * ) buffer ) + header.ofsSurfaces );
for ( i = 0; i < header.numSurfaces; i++ )
{
int j;
md3Shader_t *pShader = ( md3Shader_t * ) ( ( ( char * ) pSurface ) + pSurface->ofsShaders );
printf( "\n surface %d ('%s')\n", i, pSurface->name );
printf( " num frames: %d\n", pSurface->numFrames );
printf( " num shaders: %d\n", pSurface->numShaders );
printf( " num tris: %d\n", pSurface->numTriangles );
printf( " num verts: %d\n", pSurface->numVerts );
if ( pSurface->numShaders > 0 )
{
printf( " --- SHADERS ---\n" );
for ( j = 0; j < pSurface->numShaders; j++, pShader++ )
{
printf( " shader %d ('%s')\n", j, pShader->name );
}
}
pSurface = ( md3Surface_t * ) ( ( ( char * ) pSurface ) + pSurface->ofsEnd );
}
free( _buffer );
}

View File

@@ -0,0 +1,28 @@
/*
Copyright (C) 1999-2006 Id Software, Inc. and contributors.
For a list of contributors, see the accompanying CONTRIBUTORS file.
This file is part of GtkRadiant.
GtkRadiant is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
GtkRadiant is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <stdio.h>
#include "../common/cmdlib.h"
#include "mathlib.h"
#include "../common/qfiles.h"
void MD3_Dump( const char *filename );
void MD3_ComputeTagFromTri( md3Tag_t *pTag, const float tri[3][3] );

2155
tools/quake3/q3data/models.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,151 @@
/*
Copyright (C) 1999-2006 Id Software, Inc. and contributors.
For a list of contributors, see the accompanying CONTRIBUTORS file.
This file is part of GtkRadiant.
GtkRadiant is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
GtkRadiant is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#if 0
/*
** ReindexTriangle
**
** Given a triangle_t, find which indices match into the associated
** surface's base triangles.
*/
static void ReindexTriangle( int surfno, triangle_t *pTri, int indices[3] )
{
int t, i;
md3SurfaceData_t *pSurfData = &g_data.surfData[surfno];
int matches[3][3];
int numMatches = 0;
indices[0] = -1;
indices[1] = -1;
indices[2] = -1;
for ( i = 0; i < 3; i++ )
{
numMatches = 0;
matches[i][0] = -1;
matches[i][1] = -1;
matches[i][2] = -1;
for ( t = 0; t < pSurfData->header.numVerts; t++ )
{
if ( !VectorCompare( pTri->verts[i], pSurfData->baseVertexes[t].xyz ) )
continue;
/*
if ( !VectorCompare( pTri->normals[i], pSurfData->baseVertexes[t].normal ) )
continue;
if ( pTri->texcoords[i][0] != pSurfData->baseVertexes[t].st[0] )
continue;
if ( pTri->texcoords[i][1] != pSurfData->baseVertexes[t].st[1] )
continue;
*/
matches[i][numMatches++] = t;
}
if ( indices[i] == -1 )
{
// Error( "Could not ReindexTriangle, vertex not found" );
}
}
#if 0
for ( t = 0; t < psets[i].numtriangles; t++ )
{
int b;
bTri = &g_data.surfData[i].baseTriangles[t];
for (j=0 ; j<3 ; j++)
{
bVert = &bTri->v[j];
// get the xyz index
for ( k = 0; k < g_data.surfData[i].header.numVerts; k++ )
{
if ( ( g_data.surfData[i].baseVertexes[k].st[0] == bVert->st[0] ) &&
( g_data.surfData[i].baseVertexes[k].st[1] == bVert->st[1] ) &&
( VectorCompare (bVert->xyz, g_data.surfData[i].baseVertexes[k].xyz) ) &&
( VectorCompare (bVert->normal, g_data.surfData[i].baseVertexes[k].normal) ) )
{
break; // this vertex is already in the base vertex list
}
}
if (k == g_data.surfData[i].header.numVerts) { // new index
g_data.surfData[i].baseVertexes[g_data.surfData[i].header.numVerts] = *bVert;
g_data.surfData[i].header.numVerts++;
}
bVert->index = k;
}
}
#endif
}
const char *FindFrameFile (const char *frame)
{
int time1;
char file1[1024];
static char retname[1024];
char base[32];
char suffix[32];
const char *s;
if (strstr (frame, "."))
return frame; // allready in dot format
// split 'run1' into 'run' and '1'
s = frame + strlen(frame)-1;
while (s != frame && *s >= '0' && *s <= '9')
s--;
strcpy (suffix, s+1);
strcpy (base, frame);
base[s-frame+1] = 0;
// check for 'run1.tri'
sprintf (file1, "%s/%s%s.tri", g_cddir, base, suffix);
time1 = FileTime (file1);
if (time1 != -1)
{
sprintf (retname, "%s%s.tri", base, suffix);
return retname;
}
// check for 'run.1'
sprintf (file1, "%s/%s.%s",g_cddir, base, suffix);
time1 = FileTime (file1);
if (time1 != -1)
{
sprintf (retname, "%s.%s", base, suffix);
return retname;
}
Error ("frame %s could not be found",frame);
return NULL;
}
#endif

View File

@@ -0,0 +1,345 @@
/*
Copyright (C) 1999-2006 Id Software, Inc. and contributors.
For a list of contributors, see the accompanying CONTRIBUTORS file.
This file is part of GtkRadiant.
GtkRadiant is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
GtkRadiant is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "p3dlib.h"
#ifdef WIN32
#include <io.h>
#endif
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define MAX_POLYSETS 64
#if defined (__linux__) || defined (__APPLE__)
#define _strcmpi Q_stricmp
#define filelength Q_filelength
#define strlwr strlower
#endif
typedef struct
{
long len;
int numPairs;
char polysetNames[MAX_POLYSETS][256];
char shaders[MAX_POLYSETS][256];
char *buffer, *curpos;
} p3d_t;
static p3d_t p3d;
static int P3DProcess();
static int P3DGetToken( int restOfLine );
static char s_token[1024];
static int s_curpair;
/*
** P3DLoad
**
*/
int P3DLoad( const char *filename )
{
FILE *fp = fopen( filename, "rb" );
if ( !fp )
return 0;
memset( &p3d, 0, sizeof( p3d ) );
p3d.len = filelength( fileno( fp ) );
p3d.curpos = p3d.buffer = malloc( p3d.len );
if ( fread( p3d.buffer, p3d.len, 1, fp ) != 1 )
{
fclose( fp );
return 0;
}
fclose( fp );
return P3DProcess();
}
/*
** P3DClose
**
*/
void P3DClose()
{
if ( p3d.buffer )
{
free( p3d.buffer );
p3d.buffer = 0;
}
}
int CharIsTokenDelimiter( int ch )
{
if ( ch <= 32 )
return 1;
return 0;
}
int P3DSkipToToken( const char *name )
{
while ( P3DGetToken( 0 ) )
{
if ( !_strcmpi( s_token, name ) )
return 1;
}
return 0;
}
/*
** P3DGetToken
**
*/
int P3DGetToken( int restOfLine )
{
int i = 0;
if ( p3d.buffer == 0 )
return 0;
if ( ( p3d.curpos - p3d.buffer ) == p3d.len )
return 0;
// skip over crap
while ( ( ( p3d.curpos - p3d.buffer ) < p3d.len ) &&
( *p3d.curpos <= 32 ) )
{
p3d.curpos++;
}
while ( ( p3d.curpos - p3d.buffer ) < p3d.len )
{
s_token[i] = *p3d.curpos;
p3d.curpos++;
i++;
if ( ( CharIsTokenDelimiter( s_token[i-1] ) && !restOfLine ) ||
( ( s_token[i-1] == '\n' ) ) )
{
s_token[i-1] = 0;
break;
}
}
s_token[i] = 0;
return 1;
}
int P3DGetNextPair( char **psetName, char **associatedShader )
{
if ( s_curpair < p3d.numPairs )
{
*psetName = p3d.polysetNames[s_curpair];
*associatedShader = p3d.shaders[s_curpair];
s_curpair++;
return 1;
}
return 0;
}
int P3DSkipToTokenInBlock( const char *name )
{
int iLevel = 0;
while ( P3DGetToken( 0 ) )
{
if ( !_strcmpi( s_token, "}" ) )
iLevel--;
else if ( !_strcmpi( s_token, "{" ) )
iLevel++;
if ( !_strcmpi( s_token, name ) )
return 1;
if ( iLevel == 0 )
{
return 0;
}
}
return 0;
}
/*
** P3DProcess
**
** Nothing fancy here.
*/
int P3DProcess()
{
s_curpair = 0;
// first token should be a string
P3DGetToken( 1 ); // Voodoo Ascii File
// skip to the first Obj declaration
while ( P3DGetToken( 0 ) )
{
if ( !_strcmpi( s_token, "Obj" ) )
{
int j = 0, k = 0;
if ( P3DSkipToToken( "Text" ) )
{
if ( P3DSkipToTokenInBlock( "TMap" ) )
{
char *p;
if ( !P3DSkipToToken( "Path" ) )
return 0;
if ( !P3DGetToken( 1 ) )
return 0;
while ( s_token[j] != 0 )
{
if ( s_token[j] == '\\' )
{
j++;
p3d.shaders[p3d.numPairs][k] = '/';
}
else
{
p3d.shaders[p3d.numPairs][k] = s_token[j];
}
j++;
k++;
}
p3d.shaders[p3d.numPairs][k] = 0;
//
// strip off any explicit extensions
//
if ( ( p = strrchr( p3d.shaders[p3d.numPairs], '/' ) ) != 0 )
{
while ( *p )
{
if ( *p == '.' )
{
*p = 0;
break;
}
p++;
}
}
//
// skip to the end of the Object and grab its name
//
if ( !P3DSkipToToken( "Name" ) )
return 0;
if ( P3DGetToken( 0 ) )
{
// strip off leading 'Obj_' if it exists
if ( strstr( s_token, "Obj_" ) == s_token )
strcpy( p3d.polysetNames[p3d.numPairs], s_token + strlen( "Obj_" ) );
else
strcpy( p3d.polysetNames[p3d.numPairs], s_token );
// strip off trailing unused color information
// if ( strrchr( p3d.polysetNames[p3d.numPairs], '_' ) != 0 )
// *strrchr( p3d.polysetNames[p3d.numPairs], '_' ) = 0;
p3d.numPairs++;
}
else
{
return 0;
}
}
}
}
}
s_curpair = 0;
return 1;
}
#if 0
void SkinFromP3D( const char *file )
{
char filename[1024];
char *psetName, *associatedShader;
/*
** a P3D file contains a list of polysets, each with a list of associated
** texture names that constitute it's
**
** Thus:
**
** P3D file -> skin
** polyset -> polyset
** texture -> texture.SHADER becomes polyset's shader
*/
sprintf( filename, "%s/%s", g_cddir, file );
if ( !P3DLoad( filename ) )
Error( "unable to load '%s'", filename );
while ( P3DGetNextPair( &psetName, &associatedShader ) )
{
int i;
// find the polyset in the object that this particular pset/shader pair
// corresponds to and append the shader to it
for ( i = 0; i < g_data.model.numSurfaces; i++ )
{
if ( !_strcmpi( g_data.surfData[i].header.name, psetName) )
{
char *p;
if ( strstr( associatedShader, gamedir + 1 ) )
{
p = strstr( associatedShader, gamedir + 1 ) + strlen( gamedir ) - 1;
}
else
{
p = associatedShader;
}
strcpy( g_data.surfData[i].shaders[g_data.surfData[i].header.numShaders].name, p );
g_data.surfData[i].header.numShaders++;
}
}
}
P3DClose();
}
#endif

View File

@@ -0,0 +1,29 @@
/*
Copyright (C) 1999-2006 Id Software, Inc. and contributors.
For a list of contributors, see the accompanying CONTRIBUTORS file.
This file is part of GtkRadiant.
GtkRadiant is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
GtkRadiant is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#define P3D_GET_CROSSLINE 1
#define P3D_GET_RESTOFLINE 2
int P3DLoad( const char *filename );
void P3DClose();
int P3DGetNextPair( char **name, char **associatedShader );

View File

@@ -0,0 +1,273 @@
/*
Copyright (C) 1999-2006 Id Software, Inc. and contributors.
For a list of contributors, see the accompanying CONTRIBUTORS file.
This file is part of GtkRadiant.
GtkRadiant is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
GtkRadiant is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <assert.h>
#include "q3data.h"
polyset_t *Polyset_SplitSets( polyset_t *psets, int numpolysets, int *pNumNewPolysets, int maxTris )
{
int p, np, op;
int numNewPolysets = 0;
int numSplitPolysets = 0;
polyset_t *newpsets;
int sumTriangles = 0;
for ( p = 0; p < numpolysets; p++ )
{
numNewPolysets += psets[p].numtriangles / maxTris + 1;
}
if ( numNewPolysets == numpolysets )
return psets;
printf( "Warning: creating %d polysets from input of %d polysets\n", numNewPolysets, numpolysets );
newpsets = calloc( sizeof( polyset_t ) * numNewPolysets, 1 );
for ( np = 0, op = 0; op < numpolysets; op++ )
{
numSplitPolysets = ( psets[op].numtriangles / ( maxTris + 1 ) ) + 1;
if ( numSplitPolysets == 1 )
{
memcpy( &newpsets[np], &psets[op], sizeof( polyset_t ) );
np++;
}
else
{
sumTriangles = 0;
// split this pset into multiple smaller psets
for ( p = 0; p < numSplitPolysets; p++, np++ )
{
memcpy( &newpsets[np], &psets[op], sizeof( polyset_t ) );
newpsets[np].triangles = psets[op].triangles + sumTriangles;
if ( sumTriangles + maxTris > psets[op].numtriangles )
newpsets[np].numtriangles = psets[op].numtriangles - sumTriangles;
else
newpsets[np].numtriangles = maxTris;
sumTriangles += newpsets[np].numtriangles;
}
}
}
*pNumNewPolysets = numNewPolysets;
return newpsets;
}
polyset_t *Polyset_LoadSets( const char *file, int *numpolysets, int maxTrisPerSet )
{
polyset_t *psets;
polyset_t *finalpsets;
//
// load the frame
//
if ( strstr( file, ".3DS" ) || strstr( file, ".3ds" ) )
_3DS_LoadPolysets( file, &psets, numpolysets, g_verbose );
else
Error( "TRI files no longer supported" );
// TRI_LoadPolysets( file, &psets, numpolysets );
/*
//
// scale polysets
//
for ( i = 0; i < psets; i++ )
{
int j;
for ( j = 0; j < psets[i].numtriangles; j++ )
{
}
}
*/
//
// split polysets if necessary
//
finalpsets = Polyset_SplitSets( psets, *numpolysets, numpolysets, maxTrisPerSet );
return finalpsets;
}
polyset_t *Polyset_CollapseSets( polyset_t *psets, int numpolysets )
{
int p;
int sumtriangles = 0;
polyset_t *oldpsets = psets;
//
// no tag checking because this is an $oldbase and thus shouldn't have any
// tags
//
for ( p = 0; p < numpolysets; p++ )
{
sumtriangles += oldpsets[p].numtriangles;
}
psets = calloc( 1, sizeof( polyset_t ) );
psets[0].numtriangles = sumtriangles;
psets[0].triangles = malloc( MD3_MAX_TRIANGLES * sizeof( triangle_t ) );
// each call to "LoadPolysets" only allocates a single large chunk of
// triangle memory that is utilized by all the polysets loaded by
// that one call
memcpy( psets[0].triangles, oldpsets[0].triangles, sizeof( triangle_t ) * sumtriangles );
free( oldpsets[0].triangles );
free( oldpsets );
return psets;
}
static float SnapFloat( float x )
{
int ix;
x *= 1.0f / MD3_XYZ_SCALE;
ix = ( int ) x;
x = ( float ) ix;
x *= MD3_XYZ_SCALE;
return x;
}
void Polyset_SnapSets( polyset_t *psets, int numpolysets )
{
int p;
for ( p = 0; p < numpolysets; p++ )
{
int t;
for ( t = 0; t < psets[p].numtriangles; t++ )
{
int v;
for ( v = 0; v < 3; v++ )
{
psets[p].triangles[t].verts[v][0] = SnapFloat( psets[p].triangles[t].verts[v][0] );
psets[p].triangles[t].verts[v][1] = SnapFloat( psets[p].triangles[t].verts[v][1] );
psets[p].triangles[t].verts[v][2] = SnapFloat( psets[p].triangles[t].verts[v][2] );
}
}
}
}
void Polyset_ComputeNormals( polyset_t *psets, int numpolysets )
{
int p;
int i, t;
int vertexIndex[MD3_MAX_TRIANGLES][3];
vec3_t verts[MD3_MAX_VERTS];
vec3_t normals[MD3_MAX_VERTS];
vec3_t faceNormals[MD3_MAX_TRIANGLES];
//
// iterate through polysets
//
for ( p = 0; p < numpolysets; p++ )
{
int numUniqueVertices = 0;
assert( psets[p].numtriangles < MD3_MAX_TRIANGLES );
memset( vertexIndex, 0xff, sizeof( vertexIndex ) );
memset( verts, 0, sizeof( verts ) );
memset( normals, 0, sizeof( normals ) );
//
// unique vertices
//
for ( t = 0; t < psets[p].numtriangles; t++ )
{
int j;
for ( j = 0; j < 3; j++ )
{
for ( i = 0; i < numUniqueVertices; i++ )
{
if ( VectorCompare( psets[p].triangles[t].verts[j], verts[i] ) )
{
break;
}
}
if ( i == numUniqueVertices )
{
vertexIndex[t][j] = numUniqueVertices;
VectorCopy( (psets[p].triangles[t].verts[j]), (verts[numUniqueVertices]) );
numUniqueVertices++;
}
else
{
vertexIndex[t][j] = i;
}
}
}
//
// compute face normals
//
for ( t = 0; t < psets[p].numtriangles; t++ )
{
vec3_t side0, side1, facenormal;
VectorSubtract( psets[p].triangles[t].verts[0], psets[p].triangles[t].verts[1], side0 );
VectorSubtract( psets[p].triangles[t].verts[2], psets[p].triangles[t].verts[1], side1);
CrossProduct( side0, side1, facenormal );
VectorNormalize( facenormal, faceNormals[t] );
}
//
// sum normals and copy them back
//
for ( i = 0; i < numUniqueVertices; i++ )
{
for ( t = 0; t < psets[p].numtriangles; t++ )
{
if ( vertexIndex[t][0] == i ||
vertexIndex[t][1] == i ||
vertexIndex[t][2] == i )
{
normals[i][0] += faceNormals[t][0];
normals[i][1] += faceNormals[t][1];
normals[i][2] += faceNormals[t][2];
}
}
VectorNormalize( normals[i], normals[i] );
}
for ( t = 0; t < psets[p].numtriangles; t++ )
{
VectorCopy( normals[vertexIndex[t][0]], psets[p].triangles[t].normals[0] );
VectorCopy( normals[vertexIndex[t][1]], psets[p].triangles[t].normals[1] );
VectorCopy( normals[vertexIndex[t][2]], psets[p].triangles[t].normals[2] );
}
}
}

View File

@@ -0,0 +1,666 @@
/*
Copyright (C) 1999-2006 Id Software, Inc. and contributors.
For a list of contributors, see the accompanying CONTRIBUTORS file.
This file is part of GtkRadiant.
GtkRadiant is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
GtkRadiant is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifdef WIN32
#include <io.h>
#endif
#include "q3data.h"
#include "md3lib.h"
#include "vfs.h"
qboolean g_verbose;
qboolean g_stripify = qtrue;
qboolean g_release; // don't grab, copy output data to new tree
char g_releasedir[1024]; // c:\quake2\baseq2, etc
qboolean g_archive; // don't grab, copy source data to new tree
char g_only[256]; // if set, only grab this cd
qboolean g_skipmodel; // set true when a cd is not g_only
// bogus externs for some TA hacks (common/ using them against q3map)
char *moddir = NULL;
// some old defined that was in cmdlib lost during merge
char writedir[1024];
#if defined (__linux__) || defined (__APPLE__)
#define strlwr strlower
#endif
/*
=======================================================
PAK FILES
=======================================================
*/
unsigned Com_BlockChecksum (void *buffer, int length);
typedef struct
{
char name[56];
int filepos, filelen;
} packfile_t;
typedef struct
{
char id[4];
int dirofs;
int dirlen;
} packheader_t;
packfile_t pfiles[16384];
FILE *pakfile;
packfile_t *pf;
packheader_t pakheader;
/*
==============
ReleaseFile
Filename should be gamedir reletive.
Either copies the file to the release dir, or adds it to
the pak file.
==============
*/
void ReleaseFile (char *filename)
{
char source[1024];
char dest[1024];
if (!g_release)
return;
sprintf (source, "%s%s", gamedir, filename);
sprintf (dest, "%s/%s", g_releasedir, filename);
printf ("copying to %s\n", dest);
QCopyFile (source, dest);
return;
}
typedef struct
{
// shader
// opaque
// opaque 2
// blend
// blend 2
char names[5][1024];
int num;
} ShaderFiles_t;
ShaderFiles_t s_shaderFiles;
void FindShaderFiles( char *filename )
{
char buffer[1024];
char stripped[1024];
char linebuffer[1024];
int len, i;
char *buf;
char *diffuseExtensions[] =
{
".TGA",
".WAL",
".PCX",
0
};
char *otherExtensions[] =
{
".specular.TGA",
".blend.TGA",
".alpha.TGA",
0
};
s_shaderFiles.num = 0;
strcpy( stripped, filename );
if ( strrchr( stripped, '.' ) )
*strrchr( stripped, '.' ) = 0;
strcat( stripped, ".shader" );
if ( FileExists( stripped ) )
{
char *p;
char mapa[512], mapb[512];
strcpy( s_shaderFiles.names[s_shaderFiles.num], stripped );
s_shaderFiles.num++;
// load and parse
len = LoadFile( stripped, (void **)&buf);
p = buf;
while ( p - buf < len )
{
i = 0;
// skip spaces
while ( *p == ' ' || *p == '\n' || *p == '\t' )
p++;
// grab rest of the line
while ( *p != 0 && *p != '\n' )
{
linebuffer[i] = *p;
i++;
p++;
}
if ( *p == '\n' )
p++;
linebuffer[i] = 0;
strlwr( linebuffer );
// see if the line specifies an opaque map or blendmap
if ( strstr( linebuffer, "opaquemap" ) == linebuffer ||
strstr( linebuffer, "blendmap" ) == linebuffer )
{
int j;
i = 0;
mapa[0] = mapb[0] = 0;
// skip past the keyword
while ( linebuffer[i] != ' ' && linebuffer[i] != '\t' && linebuffer[i] )
i++;
// skip past spaces
while ( ( linebuffer[i] == ' ' || linebuffer[i] == '\t' ) && linebuffer[i] )
i++;
// grab first map name
j = 0;
while ( linebuffer[i] != ' ' && linebuffer[i] != '\t' && linebuffer[i] )
{
mapa[j] = linebuffer[i];
j++;
i++;
}
mapa[j] = 0;
// skip past spaces
while ( ( linebuffer[i] == ' ' || linebuffer[i] == '\t' ) && linebuffer[i] )
i++;
// grab second map name
j = 0;
while ( linebuffer[i] != ' ' && linebuffer[i] != '\t' && linebuffer[i] )
{
mapb[j] = linebuffer[i];
j++;
i++;
}
mapb[j] = 0;
// store map names
if ( mapa[0] != 0 && mapa[0] != '-' )
{
sprintf( s_shaderFiles.names[s_shaderFiles.num], "%s%s", gamedir, mapa );
s_shaderFiles.num++;
}
if ( mapb[0] != 0 && mapb[0] != '-' && mapb[0] != '^' && mapb[0] != '*' )
{
sprintf( s_shaderFiles.names[s_shaderFiles.num], "%s%s", gamedir, mapb );
s_shaderFiles.num++;
}
}
}
}
else
{
if ( strrchr( stripped, '.' ) )
*strrchr( stripped, '.' ) = 0;
// look for diffuse maps
for ( i = 0; i < 3; i++ )
{
strcpy( buffer, stripped );
strcat( buffer, diffuseExtensions[i] );
if ( FileExists( buffer ) )
{
strcpy( s_shaderFiles.names[s_shaderFiles.num], buffer );
s_shaderFiles.num++;
break;
}
}
for ( i = 0; i < 3; i++ )
{
strcpy( buffer, stripped );
strcat( buffer, otherExtensions[i] );
if ( FileExists( buffer ) )
{
strcpy( s_shaderFiles.names[s_shaderFiles.num], buffer );
s_shaderFiles.num++;
}
}
}
}
/*
==============
ReleaseShader
Copies all needed files for a shader to the release directory
==============
*/
void ReleaseShader( char *filename )
{
char fullpath[1024];
char dest[1024];
char stripped[1024];
int i;
sprintf( fullpath, "%s%s", gamedir, filename );
FindShaderFiles( fullpath );
for ( i = 0; i < s_shaderFiles.num; i++ )
{
strcpy( stripped, s_shaderFiles.names[i] );
if ( strstr( stripped, gamedir ) )
{
memmove( stripped, stripped+ strlen( gamedir ), strlen( stripped ) );
}
sprintf( dest, "%s/%s", g_releasedir, stripped );
printf ("copying to %s\n", dest );
QCopyFile( s_shaderFiles.names[i], dest );
}
}
/*
===============
Cmd_File
This is only used to cause a file to be copied during a release
build (default.cfg, maps, etc)
===============
*/
void Cmd_File (void)
{
GetToken (qfalse);
ReleaseFile (token);
}
/*
===============
PackDirectory_r
===============
*/
#ifdef _WIN32
#include "io.h"
void PackDirectory_r (char *dir)
{
struct _finddata_t fileinfo;
int handle;
char dirstring[1024];
char filename[1024];
sprintf (dirstring, "%s%s/*.*", gamedir, dir);
handle = _findfirst (dirstring, &fileinfo);
if (handle == -1)
return;
do
{
sprintf (filename, "%s/%s", dir, fileinfo.name);
if (fileinfo.attrib & _A_SUBDIR)
{ // directory
if (fileinfo.name[0] != '.') // don't pak . and ..
PackDirectory_r (filename);
continue;
}
// copy or pack the file
ReleaseFile (filename);
} while (_findnext( handle, &fileinfo ) != -1);
_findclose (handle);
}
#else
#include <sys/types.h>
#ifndef WIN32
#include <sys/dir.h>
#else
#include <sys/dirent.h>
#endif
void PackDirectory_r (char *dir)
{
#ifdef NeXT
struct direct **namelist, *ent;
#else
struct dirent **namelist, *ent;
#endif
int count;
struct stat st;
int i;
int len;
char fullname[1024];
char dirstring[1024];
char *name;
sprintf (dirstring, "%s%s", gamedir, dir);
count = scandir(dirstring, &namelist, NULL, NULL);
for (i=0 ; i<count ; i++)
{
ent = namelist[i];
name = ent->d_name;
if (name[0] == '.')
continue;
sprintf (fullname, "%s/%s", dir, name);
sprintf (dirstring, "%s%s/%s", gamedir, dir, name);
if (stat (dirstring, &st) == -1)
Error ("fstating %s", pf->name);
if (st.st_mode & S_IFDIR)
{ // directory
PackDirectory_r (fullname);
continue;
}
// copy or pack the file
ReleaseFile (fullname);
}
}
#endif
/*
===============
Cmd_Dir
This is only used to cause a directory to be copied during a
release build (sounds, etc)
===============
*/
void Cmd_Dir (void)
{
GetToken (qfalse);
PackDirectory_r (token);
}
//========================================================================
#define MAX_RTEX 16384
int numrtex;
char rtex[MAX_RTEX][64];
void ReleaseTexture (char *name)
{
int i;
char path[1024];
for (i=0 ; i<numrtex ; i++)
if (!Q_stricmp(name, rtex[i]))
return;
if (numrtex == MAX_RTEX)
Error ("numrtex == MAX_RTEX");
strcpy (rtex[i], name);
numrtex++;
sprintf (path, "textures/%s.wal", name);
ReleaseFile (path);
}
/*
===============
Cmd_Maps
Only relevent for release and pak files.
Releases the .bsp files for the maps, and scans all of the files to
build a list of all textures used, which are then released.
===============
*/
void Cmd_Maps (void)
{
char map[1024];
while (TokenAvailable ())
{
GetToken (qfalse);
sprintf (map, "maps/%s.bsp", token);
ReleaseFile (map);
if (!g_release)
continue;
// get all the texture references
sprintf (map, "%smaps/%s.bsp", gamedir, token);
LoadBSPFile( map );
}
}
//==============================================================
/*
===============
ParseScript
===============
*/
void ParseScript (void)
{
while (1)
{
do
{ // look for a line starting with a $ command
GetToken (qtrue);
if (endofscript)
return;
if (token[0] == '$')
break;
while (TokenAvailable())
GetToken (qfalse);
} while (1);
//
// model commands
//
if (!strcmp (token, "$modelname"))
Cmd_Modelname ();
else if (!strcmp (token, "$base"))
Cmd_Base ();
else if ( !strcmp( token, "$exit" ) )
break;
else if ( !strcmp( token, "$3dsconvert" ) )
Cmd_3DSConvert();
else if (!strcmp (token, "$spritebase"))
Cmd_SpriteBase ();
else if (!strcmp (token, "$cd"))
Cmd_Cd ();
else if (!strcmp (token, "$origin"))
Cmd_Origin ();
else if (!strcmp (token, "$scale"))
Cmd_ScaleUp ();
else if (!strcmp (token, "$frame"))
Cmd_Frame ();
else if (!strcmp (token, "$skin" ))
Cmd_Skin();
else if (!strcmp (token, "$spriteshader"))
Cmd_SpriteShader();
else if (!strcmp( token, "$aseconvert" ))
Cmd_ASEConvert( qfalse );
else if (!strcmp( token, "$aseanimconvert" ) )
Cmd_ASEConvert( qtrue );
//
// image commands
//
else if (!strcmp (token, "$grab"))
Cmd_Grab ();
else if (!strcmp (token, "$raw"))
Cmd_Raw ();
else if (!strcmp (token, "$colormap"))
Cmd_Colormap ();
else if (!strcmp (token, "$environment"))
Cmd_Environment ();
//
// video
//
else if (!strcmp (token, "$video"))
Cmd_Video ();
//
// misc
//
else if (!strcmp (token, "$file"))
Cmd_File ();
else if (!strcmp (token, "$dir"))
Cmd_Dir ();
else if (!strcmp (token, "$maps"))
Cmd_Maps ();
else
Error ("bad command %s\n", token);
}
}
//=======================================================
#include "version.h"
/*
==============
main
==============
*/
int main (int argc, char **argv)
{
static int i; // VC4.2 compiler bug if auto...
char path[1024];
// using GtkRadiant's versioning next to Id's versioning
printf ("Q3Data - (c) 1999 Id Software Inc.\n");
printf ("GtkRadiant - v" RADIANT_VERSION " " __DATE__ "\n");
ExpandWildcards (&argc, &argv);
for (i=1 ; i<argc ; i++)
{
if (!strcmp(argv[i], "-archive"))
{
archive = qtrue;
strcpy (archivedir, argv[i+1]);
printf ("Archiving source to: %s\n", archivedir);
i++;
}
else if (!strcmp(argv[i], "-release"))
{
g_release = qtrue;
strcpy (g_releasedir, argv[i+1]);
printf ("Copy output to: %s\n", g_releasedir);
i++;
}
else if ( !strcmp( argv[i], "-nostrips" ) )
{
g_stripify = qfalse;
printf( "Not optimizing for strips\n" );
}
else if ( !strcmp( argv[i], "-writedir" ) )
{
strcpy( writedir, argv[i+1] );
printf( "Write output to: %s\n", writedir );
i++;
}
else if ( !strcmp( argv[i], "-verbose" ) )
{
g_verbose = qtrue;
}
else if ( !strcmp( argv[i], "-dump" ) )
{
printf( "Dumping contents of: '%s'\n", argv[i+1] );
if ( strstr( argv[i+1], ".md3" ) )
{
MD3_Dump( argv[i+1] );
}
else
{
Error( "Do not know how to dump the contents of '%s'\n", argv[i+1] );
}
i++;
}
else if ( !strcmp( argv[i], "-3dsconvert" ) )
{
// NOTE TTimo this is broken, tried on a sample .3ds
// what happens .. it calls the Convert3DStoMD3,
// which calls the scriptlib function in non initialized state .. and crashes
printf( "Converting %s.3DS to %s.MD3\n", argv[i+1], argv[i+1] );
SetQdirFromPath( argv[i+1] );
vfsInitDirectory( gamedir );
Convert3DStoMD3( argv[i+1] );
i++;
}
else if (!strcmp(argv[i], "-only"))
{
strcpy (g_only, argv[i+1]);
printf ("Only grabbing %s\n", g_only);
i++;
}
else if (!strcmp(argv[i], "-gamedir"))
{
strcpy(gamedir, argv[i+1]);
i++;
}
else if (argv[i][0] == '-')
Error ("Unknown option \"%s\"", argv[i]);
else
break;
}
if (i == argc)
Error ("usage: q3data [-archive <directory>] [-dump <file.md3>] [-release <directory>] [-only <model>] [-3dsconvert <file.3ds>] [-verbose] [file.qdt]");
for ( ; i<argc ; i++)
{
printf ("--------------- %s ---------------\n", argv[i]);
// load the script
strcpy (path, argv[i]);
DefaultExtension (path, ".qdt");
if(!gamedir[0])
SetQdirFromPath (path);
// NOTE TTimo
// q3data went through a partial conversion to use the vfs
// it was never actually tested before 1.1.1
// the code is still mostly using direct file access calls
vfsInitDirectory( gamedir );
LoadScriptFile (ExpandArg(path), -1);
//
// parse it
//
ParseScript ();
// write out the last model
FinishModel ( TYPE_UNKNOWN );
}
return 0;
}

View File

@@ -0,0 +1,200 @@
# Microsoft Developer Studio Project File - Name="q3data" - Package Owner=<4>
# Microsoft Developer Studio Generated Build File, Format Version 6.00
# ** DO NOT EDIT **
# TARGTYPE "Win32 (x86) Console Application" 0x0103
CFG=q3data - Win32 Debug
!MESSAGE This is not a valid makefile. To build this project using NMAKE,
!MESSAGE use the Export Makefile command and run
!MESSAGE
!MESSAGE NMAKE /f "q3data.mak".
!MESSAGE
!MESSAGE You can specify a configuration when running NMAKE
!MESSAGE by defining the macro CFG on the command line. For example:
!MESSAGE
!MESSAGE NMAKE /f "q3data.mak" CFG="q3data - Win32 Debug"
!MESSAGE
!MESSAGE Possible choices for configuration are:
!MESSAGE
!MESSAGE "q3data - Win32 Release" (based on "Win32 (x86) Console Application")
!MESSAGE "q3data - Win32 Debug" (based on "Win32 (x86) Console Application")
!MESSAGE
# Begin Project
# PROP AllowPerConfigDependencies 0
# PROP Scc_ProjName "q3data"
# PROP Scc_LocalPath ".."
CPP=cl.exe
RSC=rc.exe
!IF "$(CFG)" == "q3data - Win32 Release"
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 0
# PROP BASE Output_Dir "Release"
# PROP BASE Intermediate_Dir "Release"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 0
# PROP Output_Dir "Release"
# PROP Intermediate_Dir "Release"
# PROP Ignore_Export_Lib 0
# PROP Target_Dir ""
F90=df.exe
# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
# ADD CPP /nologo /MD /W3 /GX /O2 /I "..\common" /I "..\..\..\..\libxml2\include" /I "..\..\..\libs" /I "..\..\..\..\gtk2-win32\include\glib-2.0" /I "..\..\..\..\gtk2-win32\lib\glib-2.0\include" /I "..\..\..\include" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /FD /c
# SUBTRACT CPP /YX
# ADD BASE RSC /l 0x409 /d "NDEBUG"
# ADD RSC /l 0x409 /d "NDEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LINK32=link.exe
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
# ADD LINK32 glib-2.0.lib l_net.lib wsock32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /stack:0xf42400 /subsystem:console /debug /machine:I386 /libpath:"..\..\..\libs\pak\release" /libpath:"..\..\..\libs\l_net\release" /libpath:"..\..\..\..\gtk2-win32\lib\\"
# SUBTRACT LINK32 /pdb:none
!ELSEIF "$(CFG)" == "q3data - Win32 Debug"
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 1
# PROP BASE Output_Dir "Debug"
# PROP BASE Intermediate_Dir "Debug"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 1
# PROP Output_Dir "Debug"
# PROP Intermediate_Dir "Debug"
# PROP Ignore_Export_Lib 0
# PROP Target_Dir ""
F90=df.exe
# ADD BASE CPP /nologo /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
# ADD CPP /nologo /MDd /W3 /Gm /ZI /Od /I "..\common" /I "..\..\..\..\libxml2\include" /I "..\..\..\libs" /I "..\..\..\..\gtk2-win32\include\glib-2.0" /I "..\..\..\..\gtk2-win32\lib\glib-2.0\include" /I "..\..\..\include" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FR /FD /c
# SUBTRACT CPP /YX
# ADD BASE RSC /l 0x409 /d "_DEBUG"
# ADD RSC /l 0x409 /d "_DEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LINK32=link.exe
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
# ADD LINK32 glib-2.0.lib l_net.lib wsock32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /stack:0xf42400 /subsystem:console /debug /machine:I386 /pdbtype:sept /libpath:"..\..\..\libs\l_net\Debug" /libpath:"..\..\..\libs\pak\Debug" /libpath:"..\..\..\..\gtk2-win32\lib\\"
# SUBTRACT LINK32 /pdb:none
!ENDIF
# Begin Target
# Name "q3data - Win32 Release"
# Name "q3data - Win32 Debug"
# Begin Source File
SOURCE=.\3dslib.c
# End Source File
# Begin Source File
SOURCE=.\3dslib.h
# End Source File
# Begin Source File
SOURCE=..\common\aselib.c
# End Source File
# Begin Source File
SOURCE=..\common\aselib.h
# End Source File
# Begin Source File
SOURCE=..\common\bspfile.c
# End Source File
# Begin Source File
SOURCE=..\common\cmdlib.c
# End Source File
# Begin Source File
SOURCE=.\compress.c
# End Source File
# Begin Source File
SOURCE=..\common\imagelib.c
# End Source File
# Begin Source File
SOURCE=.\images.c
# End Source File
# Begin Source File
SOURCE=..\common\inout.c
# End Source File
# Begin Source File
SOURCE=.\md3lib.c
# End Source File
# Begin Source File
SOURCE=.\md3lib.h
# End Source File
# Begin Source File
SOURCE=..\common\md4.c
# End Source File
# Begin Source File
SOURCE=.\models.c
# End Source File
# Begin Source File
SOURCE=.\p3dlib.c
# End Source File
# Begin Source File
SOURCE=.\p3dlib.h
# End Source File
# Begin Source File
SOURCE=.\polyset.c
# End Source File
# Begin Source File
SOURCE=..\common\polyset.h
# End Source File
# Begin Source File
SOURCE=.\q3data.c
# End Source File
# Begin Source File
SOURCE=.\q3data.h
# End Source File
# Begin Source File
SOURCE=..\common\scriplib.c
# End Source File
# Begin Source File
SOURCE=.\stripper.c
# End Source File
# Begin Source File
SOURCE=..\common\trilib.c
# End Source File
# Begin Source File
SOURCE=..\common\unzip.c
# End Source File
# Begin Source File
SOURCE=..\common\unzip.h
# End Source File
# Begin Source File
SOURCE=..\common\vfs.c
# End Source File
# Begin Source File
SOURCE=.\video.c
# End Source File
# End Target
# End Project

View File

@@ -0,0 +1,99 @@
/*
Copyright (C) 1999-2006 Id Software, Inc. and contributors.
For a list of contributors, see the accompanying CONTRIBUTORS file.
This file is part of GtkRadiant.
GtkRadiant is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
GtkRadiant is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
// q3data.h
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <sys/stat.h>
#include "../common/cmdlib.h"
#include "scriplib.h"
#include "mathlib.h"
#include "polyset.h"
#include "trilib.h"
#include "imagelib.h"
#include "qthreads.h"
#include "l3dslib.h"
#include "bspfile.h"
#include "p3dlib.h"
#include "3dslib.h"
#include "aselib.h"
#include "md3lib.h"
void Cmd_ASEConvert( qboolean grabAnims );
void Cmd_3DSConvert( void );
void Cmd_Modelname (void);
void Cmd_SpriteBase (void);
void Cmd_Base (void);
void Cmd_Cd (void);
void Cmd_Origin (void);
void Cmd_ScaleUp (void);
void Cmd_Frame (void);
void Cmd_Modelname (void);
void Cmd_SpriteShader(void);
void Cmd_Skin(void);
void Cmd_Skinsize (void);
void FinishModel (int type);
void Cmd_Grab (void);
void Cmd_Raw (void);
void Cmd_Mip (void);
void Cmd_Environment (void);
void Cmd_Colormap (void);
void Cmd_File (void);
void Cmd_Dir (void);
void Cmd_StartWad (void);
void Cmd_EndWad (void);
void Cmd_Mippal (void);
void Cmd_Mipdir (void);
void Cmd_Video (void);
void ReleaseFile (char *filename);
void ReleaseShader( char *filename );
void Convert3DStoMD3( const char *filename );
void OrderMesh( int input[][3], int output[][3], int numTris );
extern byte *byteimage, *lbmpalette;
extern int byteimagewidth, byteimageheight;
extern qboolean g_release; // don't grab, copy output data to new tree
extern char g_releasedir[1024]; // c:\quake2\baseq2, etc
extern qboolean g_archive; // don't grab, copy source data to new tree
extern qboolean do3ds;
extern char g_only[256]; // if set, only grab this cd
extern qboolean g_skipmodel; // set true when a cd is not g_only
extern qboolean g_verbose;
extern char *trifileext;
#define TYPE_ITEM 0
#define TYPE_PLAYER 1
#define TYPE_WEAPON 2
#define TYPE_HAND 3
#define TYPE_UNKNOWN 4

View File

@@ -0,0 +1,207 @@
<?xml version="1.0" encoding = "Windows-1252"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="7.00"
Name="q3data">
<Platforms>
<Platform
Name="Win32"/>
</Platforms>
<Configurations>
<Configuration
Name="Debug|Win32"
OutputDirectory=".\Debug"
IntermediateDirectory=".\Debug"
ConfigurationType="1"
UseOfMFC="0"
ATLMinimizesCRunTimeLibraryUsage="FALSE"
CharacterSet="2">
<Tool
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories="..\common,..\..\..\..\libxml2\include,..\..\..\libs,..\..\..\..\gtk2-win32\include\glib-2.0,..\..\..\..\gtk2-win32\lib\glib-2.0\include,..\..\..\include"
PreprocessorDefinitions="WIN32,_DEBUG,_CONSOLE"
RuntimeLibrary="3"
PrecompiledHeaderFile=".\Debug/q3data.pch"
AssemblerListingLocation=".\Debug/"
ObjectFile=".\Debug/"
ProgramDataBaseFileName=".\Debug/"
BrowseInformation="1"
WarningLevel="3"
SuppressStartupBanner="TRUE"
DebugInformationFormat="4"
CompileAs="0"/>
<Tool
Name="VCCustomBuildTool"/>
<Tool
Name="VCLinkerTool"
AdditionalOptions="/MACHINE:I386"
AdditionalDependencies="glib-2.0.lib wsock32.lib libxml2.lib"
OutputFile=".\Debug/q3data.exe"
LinkIncremental="2"
SuppressStartupBanner="TRUE"
AdditionalLibraryDirectories="&quot;..\..\..\..\libxml2\win32\binaries-debug&quot;;..\..\..\libs\l_net\Debug;..\..\..\libs\pak\Debug;&quot;..\..\..\..\gtk2-win32\lib\&quot;"
GenerateDebugInformation="TRUE"
ProgramDatabaseFile=".\Debug/q3data.pdb"
SubSystem="1"
StackReserveSize="16000000"/>
<Tool
Name="VCMIDLTool"
TypeLibraryName=".\Debug/q3data.tlb"/>
<Tool
Name="VCPostBuildEventTool"/>
<Tool
Name="VCPreBuildEventTool"/>
<Tool
Name="VCPreLinkEventTool"/>
<Tool
Name="VCResourceCompilerTool"
PreprocessorDefinitions="_DEBUG"
Culture="1033"/>
<Tool
Name="VCWebServiceProxyGeneratorTool"/>
<Tool
Name="VCWebDeploymentTool"/>
</Configuration>
<Configuration
Name="Release|Win32"
OutputDirectory=".\Release"
IntermediateDirectory=".\Release"
ConfigurationType="1"
UseOfMFC="0"
ATLMinimizesCRunTimeLibraryUsage="FALSE"
CharacterSet="2">
<Tool
Name="VCCLCompilerTool"
InlineFunctionExpansion="1"
AdditionalIncludeDirectories="..\common,..\..\..\..\libxml2\include,..\..\..\libs,..\..\..\..\gtk2-win32\include\glib-2.0,..\..\..\..\gtk2-win32\lib\glib-2.0\include,..\..\..\include"
PreprocessorDefinitions="WIN32,NDEBUG,_CONSOLE"
StringPooling="TRUE"
RuntimeLibrary="2"
EnableFunctionLevelLinking="TRUE"
PrecompiledHeaderFile=".\Release/q3data.pch"
AssemblerListingLocation=".\Release/"
ObjectFile=".\Release/"
ProgramDataBaseFileName=".\Release/"
WarningLevel="3"
SuppressStartupBanner="TRUE"
CompileAs="0"/>
<Tool
Name="VCCustomBuildTool"/>
<Tool
Name="VCLinkerTool"
AdditionalOptions="/MACHINE:I386"
AdditionalDependencies="glib-2.0.lib wsock32.lib libxml2.lib"
OutputFile=".\Release/q3data.exe"
LinkIncremental="1"
SuppressStartupBanner="TRUE"
AdditionalLibraryDirectories="..\..\..\libs\pak\release;..\..\..\libs\l_net\release;&quot;..\..\..\..\libxml2\win32\binaries-release&quot;;&quot;..\..\..\..\gtk2-win32\lib\&quot;"
GenerateDebugInformation="TRUE"
ProgramDatabaseFile=".\Release/q3data.pdb"
SubSystem="1"
StackReserveSize="16000000"/>
<Tool
Name="VCMIDLTool"
TypeLibraryName=".\Release/q3data.tlb"/>
<Tool
Name="VCPostBuildEventTool"/>
<Tool
Name="VCPreBuildEventTool"/>
<Tool
Name="VCPreLinkEventTool"/>
<Tool
Name="VCResourceCompilerTool"
PreprocessorDefinitions="NDEBUG"
Culture="1033"/>
<Tool
Name="VCWebServiceProxyGeneratorTool"/>
<Tool
Name="VCWebDeploymentTool"/>
</Configuration>
</Configurations>
<Files>
<File
RelativePath=".\3dslib.c">
</File>
<File
RelativePath=".\3dslib.h">
</File>
<File
RelativePath="..\common\aselib.c">
</File>
<File
RelativePath="..\common\aselib.h">
</File>
<File
RelativePath="..\common\bspfile.c">
</File>
<File
RelativePath="..\common\cmdlib.c">
</File>
<File
RelativePath=".\compress.c">
</File>
<File
RelativePath="..\common\imagelib.c">
</File>
<File
RelativePath=".\images.c">
</File>
<File
RelativePath="..\common\inout.c">
</File>
<File
RelativePath=".\md3lib.c">
</File>
<File
RelativePath=".\md3lib.h">
</File>
<File
RelativePath="..\common\md4.c">
</File>
<File
RelativePath=".\models.c">
</File>
<File
RelativePath=".\p3dlib.c">
</File>
<File
RelativePath=".\p3dlib.h">
</File>
<File
RelativePath=".\polyset.c">
</File>
<File
RelativePath="..\common\polyset.h">
</File>
<File
RelativePath=".\q3data.c">
</File>
<File
RelativePath=".\q3data.h">
</File>
<File
RelativePath="..\common\scriplib.c">
</File>
<File
RelativePath=".\stripper.c">
</File>
<File
RelativePath="..\common\trilib.c">
</File>
<File
RelativePath="..\common\unzip.c">
</File>
<File
RelativePath="..\common\unzip.h">
</File>
<File
RelativePath="..\common\vfs.c">
</File>
<File
RelativePath=".\video.c">
</File>
</Files>
<Globals>
</Globals>
</VisualStudioProject>

View File

@@ -0,0 +1,303 @@
/*
Copyright (C) 1999-2006 Id Software, Inc. and contributors.
For a list of contributors, see the accompanying CONTRIBUTORS file.
This file is part of GtkRadiant.
GtkRadiant is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
GtkRadiant is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
static int s_used[8192]; // same as MD3_MAX_TRIANGLES
/*
** FindNextTriangleInStrip
**
** Given a surface and triangle this tries to find the next triangle
** in the strip that would continue the strip. The next triangle in
** the strip should have the same winding as this triangle.
*/
static int FindNextTriangleInStripOrFan( int mesh[][3], int tri, int orientation, int numTris, int odd )
{
int t;
int sum = 0;
int currentTri[3];
int side;
int a, b, c;
int refa, refb;
currentTri[0] = mesh[tri][(0+orientation)%3];
currentTri[1] = mesh[tri][(1+orientation)%3];
currentTri[2] = mesh[tri][(2+orientation)%3];
if ( odd )
{
refa = currentTri[1];
refb = currentTri[2];
}
else
{
refa = currentTri[2];
refb = currentTri[0];
}
// go through all triangles and look for sides that match
// this triangle's
for ( t = 0; t < numTris; t++ )
{
// don't check against self or against previously used triangles
if ( t == tri )
continue;
if ( s_used[t] )
continue;
// check all three sides of the candidate triangle
for ( side = 0; side < 3; side++ )
{
// check only the second (abutting) side
if ( ( refa == mesh[t][(side+1)%3] ) &&
( refb == mesh[t][side] ) )
{
a = mesh[t][0];
b = mesh[t][1];
c = mesh[t][2];
// rotate the candidate triangle to align it properly in the strip
if ( side == 1 )
{
mesh[t][0] = b;
mesh[t][1] = c;
mesh[t][2] = a;
}
else if ( side == 2 )
{
mesh[t][0] = c;
mesh[t][1] = a;
mesh[t][2] = b;
}
return t;
}
/*
else
{
Error( "fans not implemented yet" );
// check only the third (abutting) side
if ( ( currentTri[2] == pSurf->baseTriangles[t].v[side].index ) &&
( currentTri[0] == pSurf->baseTriangles[t].v[(side+1)%3].index ) )
{
return t;
}
}
*/
}
}
return -1;
}
/*
** StripLength
*/
static int StripLength( int mesh[][3], int strip[][3], int tri, int orientation, int numInputTris, int fillNo )
{
int stripIndex = 0;
int next;
int odd = 1;
strip[stripIndex][0] = mesh[tri][(0+orientation)%3];
strip[stripIndex][1] = mesh[tri][(1+orientation)%3];
strip[stripIndex][2] = mesh[tri][(2+orientation)%3];
s_used[tri] = fillNo;
stripIndex++;
next = tri;
while ( ( next = FindNextTriangleInStripOrFan( mesh, next, orientation, numInputTris, odd ) ) != -1 )
{
s_used[next] = fillNo;
odd = !odd;
strip[stripIndex][0] = mesh[next][0];
strip[stripIndex][1] = mesh[next][1];
strip[stripIndex][2] = mesh[next][2];
stripIndex++;
// all iterations after first need to be with an unrotated reference triangle
orientation = 0;
}
return stripIndex;
}
/*
** BuildOptimizedList
**
** Attempts to build the longest strip/fan possible. Does not adhere
** to pure strip or fan, will intermix between the two so long as some
** type of connectivity can be maintained.
*/
#define MAX_ORIENTATIONS 3
#define MAX_MATCHED_SIDES 4
#define MAX_SEED_TRIANGLES 16
static int BuildOptimizedList( int mesh[][3], int strip[][3], int numInputTris )
{
int t;
int stripLen = 0;
int startTri = -1;
int bestTri = -1, bestLength = 0, bestOrientation = -1;
int matchedSides = 0;
int orientation = 0;
int seedTriangles[MAX_MATCHED_SIDES][MAX_SEED_TRIANGLES];
int seedLengths[MAX_ORIENTATIONS][MAX_MATCHED_SIDES][MAX_SEED_TRIANGLES];
int numSeeds[MAX_MATCHED_SIDES] = { 0, 0, 0 };
int i;
// build a ranked list of candidate seed triangles based on
// number of offshoot strips. Precedence goes to orphans,
// then corners, then edges, and interiors.
memset( seedTriangles, 0xff, sizeof( seedTriangles ) );
memset( seedLengths, 0xff, sizeof( seedLengths ) );
for ( i = 0; i < MAX_MATCHED_SIDES; i++ )
{
// find the triangle with lowest number of child strips
for ( t = 0; t < numInputTris; t++ )
{
int orientation;
int n;
if ( s_used[t] )
continue;
// try the candidate triangle in three different orientations
matchedSides = 0;
for ( orientation = 0; orientation < 3; orientation++ )
{
if ( ( n = FindNextTriangleInStripOrFan( mesh, t, orientation, numInputTris, 1 ) ) != -1 )
{
matchedSides++;
}
}
if ( matchedSides == i )
{
seedTriangles[i][numSeeds[i]] = t;
numSeeds[i]++;
if ( numSeeds[i] == MAX_SEED_TRIANGLES )
break;
}
}
}
// we have a list of potential seed triangles, so we now go through each
// potential candidate and look to see which produces the longest strip
// and select our startTri based on this
for ( i = 0; i < MAX_MATCHED_SIDES; i++ )
{
int j;
for ( j = 0; j < numSeeds[i]; j++ )
{
for ( orientation = 0; orientation < 3; orientation++ )
{
int k;
seedLengths[orientation][i][j] = StripLength( mesh, strip, seedTriangles[i][j], orientation, numInputTris, 2 );
if ( seedLengths[orientation][i][j] > bestLength )
{
bestTri = seedTriangles[i][j];
bestLength = seedLengths[orientation][i][j];
bestOrientation = orientation;
}
for ( k = 0; k < numInputTris; k++ )
{
if ( s_used[k] == 2 )
s_used[k] = 0;
}
}
}
if ( bestTri != -1 )
{
break;
}
}
// build the strip for real
if ( bestTri != -1 )
{
stripLen = StripLength( mesh, strip, bestTri, bestOrientation, numInputTris, 1 );
}
return stripLen;
}
/*
** OrderMesh
**
** Given an input mesh and an output mesh, this routine will reorder
** the triangles within the mesh into strips/fans.
*/
void OrderMesh( int input[][3], int output[][3], int numTris )
{
int i;
int sumStrippedTriangles = 0;
int strippedTriangles;
int totalStrips = 0;
int strip[8192][3]; // could dump directly into 'output', but
// this helps with debugging
memset( s_used, 0, sizeof( s_used ) );
#if 0
FILE *fp = fopen( "strip.txt", "wt" );
for ( i = 0; i < numTris; i++ )
{
fprintf( fp, "%4d: %3d %3d %3d\n", i, input[i][0], input[i][1], input[i][2] );
}
fclose( fp );
#endif
// while there are still triangles that are not part of a strip
while ( sumStrippedTriangles < numTris )
{
// build a strip
strippedTriangles = BuildOptimizedList( input, strip, numTris );
for ( i = 0; i < strippedTriangles; i++ )
{
output[sumStrippedTriangles+i][0] = strip[i][0];
output[sumStrippedTriangles+i][1] = strip[i][1];
output[sumStrippedTriangles+i][2] = strip[i][2];
}
sumStrippedTriangles += strippedTriangles;
totalStrips++;
}
printf( "Triangles on surface: %d\n", sumStrippedTriangles );
printf( "Total strips from surface: %d\n", totalStrips );
printf( "Average strip length: %f\n", ( float ) sumStrippedTriangles / totalStrips );
}

1153
tools/quake3/q3data/video.c Normal file

File diff suppressed because it is too large Load Diff

View File

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

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

@@ -0,0 +1,978 @@
/* -------------------------------------------------------------------------------
Copyright (C) 1999-2006 Id Software, Inc. and contributors.
For a list of contributors, see the accompanying CONTRIBUTORS file.
This file is part of GtkRadiant.
GtkRadiant is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
GtkRadiant is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
----------------------------------------------------------------------------------
This code has been altered significantly from its original form, to support
several games based on the Quake III Arena engine, in the form of "Q3Map2."
------------------------------------------------------------------------------- */
/* marker */
#define BRUSH_C
/* dependencies */
#include "q3map2.h"
/* -------------------------------------------------------------------------------
functions
------------------------------------------------------------------------------- */
/*
AllocSideRef() - ydnar
allocates and assigns a brush side reference
*/
sideRef_t *AllocSideRef( side_t *side, sideRef_t *next )
{
sideRef_t *sideRef;
/* dummy check */
if( side == NULL )
return next;
/* allocate and return */
sideRef = safe_malloc( sizeof( *sideRef ) );
sideRef->side = side;
sideRef->next = next;
return sideRef;
}
/*
CountBrushList()
counts the number of brushes in a brush linked list
*/
int CountBrushList( brush_t *brushes )
{
int c = 0;
/* count brushes */
for( brushes; brushes != NULL; brushes = brushes->next )
c++;
return c;
}
/*
AllocBrush()
allocates a new brush
*/
brush_t *AllocBrush( int numSides )
{
brush_t *bb;
int c;
/* allocate and clear */
if( numSides <= 0 )
Error( "AllocBrush called with numsides = %d", numSides );
c = (int) &(((brush_t*) 0)->sides[ numSides ]);
bb = safe_malloc( c );
memset( bb, 0, c );
if( numthreads == 1 )
numActiveBrushes++;
/* return it */
return bb;
}
/*
FreeBrush()
frees a single brush and all sides/windings
*/
void FreeBrush( brush_t *b )
{
int i;
/* error check */
if( *((int*) b) == 0xFEFEFEFE )
{
Sys_FPrintf( SYS_VRB, "WARNING: Attempt to free an already freed brush!\n" );
return;
}
/* free brush sides */
for( i = 0; i < b->numsides; i++ )
if( b->sides[i].winding != NULL )
FreeWinding( b->sides[ i ].winding );
/* ydnar: overwrite it */
memset( b, 0xFE, (int) &(((brush_t*) 0)->sides[ b->numsides ]) );
*((int*) b) = 0xFEFEFEFE;
/* free it */
free( b );
if( numthreads == 1 )
numActiveBrushes--;
}
/*
FreeBrushList()
frees a linked list of brushes
*/
void FreeBrushList( brush_t *brushes )
{
brush_t *next;
/* walk brush list */
for( brushes; brushes != NULL; brushes = next )
{
next = brushes->next;
FreeBrush( brushes );
}
}
/*
CopyBrush()
duplicates the brush, sides, and windings
*/
brush_t *CopyBrush( brush_t *brush )
{
brush_t *newBrush;
int size;
int i;
/* copy brush */
size = (int) &(((brush_t*) 0)->sides[ brush->numsides ]);
newBrush = AllocBrush( brush->numsides );
memcpy( newBrush, brush, size );
/* ydnar: nuke linked list */
newBrush->next = NULL;
/* copy sides */
for( i = 0; i < brush->numsides; i++ )
{
if( brush->sides[ i ].winding != NULL )
newBrush->sides[ i ].winding = CopyWinding( brush->sides[ i ].winding );
}
/* return it */
return newBrush;
}
/*
BoundBrush()
sets the mins/maxs based on the windings
returns false if the brush doesn't enclose a valid volume
*/
qboolean BoundBrush( brush_t *brush )
{
int i, j;
winding_t *w;
ClearBounds( brush->mins, brush->maxs );
for( i = 0; i < brush->numsides; i++ )
{
w = brush->sides[ i ].winding;
if( w == NULL )
continue;
for( j = 0; j < w->numpoints; j++ )
AddPointToBounds( w->p[ j ], brush->mins, brush->maxs );
}
for( i = 0; i < 3; i++ )
{
if( brush->mins[ i ] < MIN_WORLD_COORD || brush->maxs[ i ] > MAX_WORLD_COORD || brush->mins[i] >= brush->maxs[ i ] )
return qfalse;
}
return qtrue;
}
/*
SnapWeldVector() - ydnar
welds two vec3_t's into a third, taking into account nearest-to-integer
instead of averaging
*/
#define SNAP_EPSILON 0.01
void SnapWeldVector( vec3_t a, vec3_t b, vec3_t out )
{
int i;
vec_t ai, bi, outi;
/* dummy check */
if( a == NULL || b == NULL || out == NULL )
return;
/* do each element */
for( i = 0; i < 3; i++ )
{
/* round to integer */
ai = Q_rint( a[ i ] );
bi = Q_rint( a[ i ] );
/* prefer exact integer */
if( ai == a[ i ] )
out[ i ] = a[ i ];
else if( bi == b[ i ] )
out[ i ] = b[ i ];
/* use nearest */
else if( fabs( ai - a[ i ] ) < fabs( bi < b[ i ] ) )
out[ i ] = a[ i ];
else
out[ i ] = b[ i ];
/* snap */
outi = Q_rint( out[ i ] );
if( fabs( outi - out[ i ] ) <= SNAP_EPSILON )
out[ i ] = outi;
}
}
/*
FixWinding() - ydnar
removes degenerate edges from a winding
returns qtrue if the winding is valid
*/
#define DEGENERATE_EPSILON 0.1
qboolean FixWinding( winding_t *w )
{
qboolean valid = qtrue;
int i, j, k;
vec3_t vec;
float dist;
/* dummy check */
if( !w )
return qfalse;
/* check all verts */
for( i = 0; i < w->numpoints; i++ )
{
/* don't remove points if winding is a triangle */
if( w->numpoints == 3 )
return valid;
/* get second point index */
j = (i + 1) % w->numpoints;
/* degenerate edge? */
VectorSubtract( w->p[ i ], w->p[ j ], vec );
dist = VectorLength( vec );
if( dist < DEGENERATE_EPSILON )
{
valid = qfalse;
//Sys_FPrintf( SYS_VRB, "WARNING: Degenerate winding edge found, fixing...\n" );
/* create an average point (ydnar 2002-01-26: using nearest-integer weld preference) */
SnapWeldVector( w->p[ i ], w->p[ j ], vec );
VectorCopy( vec, w->p[ i ] );
//VectorAdd( w->p[ i ], w->p[ j ], vec );
//VectorScale( vec, 0.5, w->p[ i ] );
/* move the remaining verts */
for( k = i + 2; k < w->numpoints; k++ )
{
VectorCopy( w->p[ k ], w->p[ k - 1 ] );
}
w->numpoints--;
}
}
/* one last check and return */
if( w->numpoints < 3 )
valid = qfalse;
return valid;
}
/*
CreateBrushWindings()
makes basewindigs for sides and mins/maxs for the brush
returns false if the brush doesn't enclose a valid volume
*/
qboolean CreateBrushWindings( brush_t *brush )
{
int i, j;
winding_t *w;
side_t *side;
plane_t *plane;
/* walk the list of brush sides */
for( i = 0; i < brush->numsides; i++ )
{
/* get side and plane */
side = &brush->sides[ i ];
plane = &mapplanes[ side->planenum ];
/* make huge winding */
w = BaseWindingForPlane( plane->normal, plane->dist );
/* walk the list of brush sides */
for( j = 0; j < brush->numsides && w != NULL; j++ )
{
if( i == j )
continue;
if( brush->sides[ j ].planenum == (brush->sides[ i ].planenum ^ 1) )
continue; /* back side clipaway */
if( brush->sides[ j ].bevel )
continue;
plane = &mapplanes[ brush->sides[ j ].planenum ^ 1 ];
ChopWindingInPlace( &w, plane->normal, plane->dist, 0 ); // CLIP_EPSILON );
/* ydnar: fix broken windings that would generate trifans */
FixWinding( w );
}
/* set side winding */
side->winding = w;
}
/* find brush bounds */
return BoundBrush( brush );
}
/*
==================
BrushFromBounds
Creates a new axial brush
==================
*/
brush_t *BrushFromBounds (vec3_t mins, vec3_t maxs)
{
brush_t *b;
int i;
vec3_t normal;
vec_t dist;
b = AllocBrush (6);
b->numsides = 6;
for (i=0 ; i<3 ; i++)
{
VectorClear (normal);
normal[i] = 1;
dist = maxs[i];
b->sides[i].planenum = FindFloatPlane (normal, dist, 1, (vec3_t*) &maxs );
normal[i] = -1;
dist = -mins[i];
b->sides[3+i].planenum = FindFloatPlane (normal, dist, 1, (vec3_t*) &mins );
}
CreateBrushWindings (b);
return b;
}
/*
==================
BrushVolume
==================
*/
vec_t BrushVolume (brush_t *brush)
{
int i;
winding_t *w;
vec3_t corner;
vec_t d, area, volume;
plane_t *plane;
if (!brush)
return 0;
// grab the first valid point as the corner
w = NULL;
for (i=0 ; i<brush->numsides ; i++)
{
w = brush->sides[i].winding;
if (w)
break;
}
if (!w)
return 0;
VectorCopy (w->p[0], corner);
// make tetrahedrons to all other faces
volume = 0;
for ( ; i<brush->numsides ; i++)
{
w = brush->sides[i].winding;
if (!w)
continue;
plane = &mapplanes[brush->sides[i].planenum];
d = -(DotProduct (corner, plane->normal) - plane->dist);
area = WindingArea (w);
volume += d*area;
}
volume /= 3;
return volume;
}
/*
WriteBSPBrushMap()
writes a map with the split bsp brushes
*/
void WriteBSPBrushMap( char *name, brush_t *list )
{
FILE *f;
side_t *s;
int i;
winding_t *w;
/* note it */
Sys_Printf( "Writing %s\n", name );
/* open the map file */
f = fopen( name, "wb" );
if( f == NULL )
Error( "Can't write %s\b", name );
fprintf (f, "{\n\"classname\" \"worldspawn\"\n");
for ( ; list ; list=list->next )
{
fprintf (f, "{\n");
for (i=0,s=list->sides ; i<list->numsides ; i++,s++)
{
w = BaseWindingForPlane (mapplanes[s->planenum].normal, mapplanes[s->planenum].dist);
fprintf (f,"( %i %i %i ) ", (int)w->p[0][0], (int)w->p[0][1], (int)w->p[0][2]);
fprintf (f,"( %i %i %i ) ", (int)w->p[1][0], (int)w->p[1][1], (int)w->p[1][2]);
fprintf (f,"( %i %i %i ) ", (int)w->p[2][0], (int)w->p[2][1], (int)w->p[2][2]);
fprintf (f, "notexture 0 0 0 1 1\n" );
FreeWinding (w);
}
fprintf (f, "}\n");
}
fprintf (f, "}\n");
fclose (f);
}
/*
FilterBrushIntoTree_r()
adds brush reference to any intersecting bsp leafnode
*/
int FilterBrushIntoTree_r( brush_t *b, node_t *node )
{
brush_t *front, *back;
int c;
/* dummy check */
if( b == NULL )
return 0;
/* add it to the leaf list */
if( node->planenum == PLANENUM_LEAF )
{
/* something somewhere is hammering brushlist */
b->next = node->brushlist;
node->brushlist = b;
/* classify the leaf by the structural brush */
if( !b->detail )
{
if( b->opaque )
{
node->opaque = qtrue;
node->areaportal = qfalse;
}
else if( b->compileFlags & C_AREAPORTAL )
{
if( !node->opaque )
node->areaportal = qtrue;
}
}
return 1;
}
/* split it by the node plane */
c = b->numsides;
SplitBrush( b, node->planenum, &front, &back );
FreeBrush( b );
c = 0;
c += FilterBrushIntoTree_r( front, node->children[ 0 ] );
c += FilterBrushIntoTree_r( back, node->children[ 1 ] );
return c;
}
/*
FilterDetailBrushesIntoTree
fragment all the detail brushes into the structural leafs
*/
void FilterDetailBrushesIntoTree( entity_t *e, tree_t *tree )
{
brush_t *b, *newb;
int r;
int c_unique, c_clusters;
int i;
/* note it */
Sys_FPrintf( SYS_VRB, "--- FilterDetailBrushesIntoTree ---\n" );
/* walk the list of brushes */
c_unique = 0;
c_clusters = 0;
for( b = e->brushes; b; b = b->next )
{
if( !b->detail )
continue;
c_unique++;
newb = CopyBrush( b );
r = FilterBrushIntoTree_r( newb, tree->headnode );
c_clusters += r;
/* mark all sides as visible so drawsurfs are created */
if( r )
{
for( i = 0; i < b->numsides; i++ )
{
if( b->sides[ i ].winding )
b->sides[ i ].visible = qtrue;
}
}
}
/* emit some statistics */
Sys_FPrintf( SYS_VRB, "%9d detail brushes\n", c_unique );
Sys_FPrintf( SYS_VRB, "%9d cluster references\n", c_clusters );
}
/*
=====================
FilterStructuralBrushesIntoTree
Mark the leafs as opaque and areaportals
=====================
*/
void FilterStructuralBrushesIntoTree( entity_t *e, tree_t *tree ) {
brush_t *b, *newb;
int r;
int c_unique, c_clusters;
int i;
Sys_FPrintf (SYS_VRB, "--- FilterStructuralBrushesIntoTree ---\n");
c_unique = 0;
c_clusters = 0;
for ( b = e->brushes ; b ; b = b->next ) {
if ( b->detail ) {
continue;
}
c_unique++;
newb = CopyBrush( b );
r = FilterBrushIntoTree_r( newb, tree->headnode );
c_clusters += r;
// mark all sides as visible so drawsurfs are created
if ( r ) {
for ( i = 0 ; i < b->numsides ; i++ ) {
if ( b->sides[i].winding ) {
b->sides[i].visible = qtrue;
}
}
}
}
/* emit some statistics */
Sys_FPrintf( SYS_VRB, "%9d structural brushes\n", c_unique );
Sys_FPrintf( SYS_VRB, "%9d cluster references\n", c_clusters );
}
/*
================
AllocTree
================
*/
tree_t *AllocTree (void)
{
tree_t *tree;
tree = safe_malloc(sizeof(*tree));
memset (tree, 0, sizeof(*tree));
ClearBounds (tree->mins, tree->maxs);
return tree;
}
/*
================
AllocNode
================
*/
node_t *AllocNode (void)
{
node_t *node;
node = safe_malloc(sizeof(*node));
memset (node, 0, sizeof(*node));
return node;
}
/*
================
WindingIsTiny
Returns true if the winding would be crunched out of
existance by the vertex snapping.
================
*/
#define EDGE_LENGTH 0.2
qboolean WindingIsTiny (winding_t *w)
{
/*
if (WindingArea (w) < 1)
return qtrue;
return qfalse;
*/
int i, j;
vec_t len;
vec3_t delta;
int edges;
edges = 0;
for (i=0 ; i<w->numpoints ; i++)
{
j = i == w->numpoints - 1 ? 0 : i+1;
VectorSubtract (w->p[j], w->p[i], delta);
len = VectorLength (delta);
if (len > EDGE_LENGTH)
{
if (++edges == 3)
return qfalse;
}
}
return qtrue;
}
/*
================
WindingIsHuge
Returns true if the winding still has one of the points
from basewinding for plane
================
*/
qboolean WindingIsHuge (winding_t *w)
{
int i, j;
for (i=0 ; i<w->numpoints ; i++)
{
for (j=0 ; j<3 ; j++)
if (w->p[i][j] <= MIN_WORLD_COORD || w->p[i][j] >= MAX_WORLD_COORD)
return qtrue;
}
return qfalse;
}
//============================================================
/*
==================
BrushMostlyOnSide
==================
*/
int BrushMostlyOnSide (brush_t *brush, plane_t *plane)
{
int i, j;
winding_t *w;
vec_t d, max;
int side;
max = 0;
side = PSIDE_FRONT;
for (i=0 ; i<brush->numsides ; i++)
{
w = brush->sides[i].winding;
if (!w)
continue;
for (j=0 ; j<w->numpoints ; j++)
{
d = DotProduct (w->p[j], plane->normal) - plane->dist;
if (d > max)
{
max = d;
side = PSIDE_FRONT;
}
if (-d > max)
{
max = -d;
side = PSIDE_BACK;
}
}
}
return side;
}
/*
SplitBrush()
generates two new brushes, leaving the original unchanged
*/
void SplitBrush( brush_t *brush, int planenum, brush_t **front, brush_t **back )
{
brush_t *b[2];
int i, j;
winding_t *w, *cw[2], *midwinding;
plane_t *plane, *plane2;
side_t *s, *cs;
float d, d_front, d_back;
*front = NULL;
*back = NULL;
plane = &mapplanes[planenum];
// check all points
d_front = d_back = 0;
for (i=0 ; i<brush->numsides ; i++)
{
w = brush->sides[i].winding;
if (!w)
continue;
for (j=0 ; j<w->numpoints ; j++)
{
d = DotProduct (w->p[j], plane->normal) - plane->dist;
if (d > 0 && d > d_front)
d_front = d;
if (d < 0 && d < d_back)
d_back = d;
}
}
if (d_front < 0.1) // PLANESIDE_EPSILON)
{ // only on back
*back = CopyBrush( brush );
return;
}
if (d_back > -0.1) // PLANESIDE_EPSILON)
{ // only on front
*front = CopyBrush( brush );
return;
}
// create a new winding from the split plane
w = BaseWindingForPlane (plane->normal, plane->dist);
for (i=0 ; i<brush->numsides && w ; i++)
{
plane2 = &mapplanes[brush->sides[i].planenum ^ 1];
ChopWindingInPlace (&w, plane2->normal, plane2->dist, 0); // PLANESIDE_EPSILON);
}
if (!w || WindingIsTiny (w) )
{ // the brush isn't really split
int side;
side = BrushMostlyOnSide (brush, plane);
if (side == PSIDE_FRONT)
*front = CopyBrush (brush);
if (side == PSIDE_BACK)
*back = CopyBrush (brush);
return;
}
if( WindingIsHuge( w ) )
Sys_FPrintf( SYS_VRB,"WARNING: huge winding\n" );
midwinding = w;
// split it for real
for (i=0 ; i<2 ; i++)
{
b[i] = AllocBrush (brush->numsides+1);
memcpy( b[i], brush, sizeof( brush_t ) - sizeof( brush->sides ) );
b[i]->numsides = 0;
b[i]->next = NULL;
b[i]->original = brush->original;
}
// split all the current windings
for (i=0 ; i<brush->numsides ; i++)
{
s = &brush->sides[i];
w = s->winding;
if (!w)
continue;
ClipWindingEpsilon (w, plane->normal, plane->dist,
0 /*PLANESIDE_EPSILON*/, &cw[0], &cw[1]);
for (j=0 ; j<2 ; j++)
{
if (!cw[j])
continue;
cs = &b[j]->sides[b[j]->numsides];
b[j]->numsides++;
*cs = *s;
cs->winding = cw[j];
}
}
// see if we have valid polygons on both sides
for (i=0 ; i<2 ; i++)
{
BoundBrush (b[i]);
for (j=0 ; j<3 ; j++)
{
if (b[i]->mins[j] < MIN_WORLD_COORD || b[i]->maxs[j] > MAX_WORLD_COORD)
{
Sys_FPrintf (SYS_VRB,"bogus brush after clip\n");
break;
}
}
if (b[i]->numsides < 3 || j < 3)
{
FreeBrush (b[i]);
b[i] = NULL;
}
}
if ( !(b[0] && b[1]) )
{
if (!b[0] && !b[1])
Sys_FPrintf (SYS_VRB,"split removed brush\n");
else
Sys_FPrintf (SYS_VRB,"split not on both sides\n");
if (b[0])
{
FreeBrush (b[0]);
*front = CopyBrush (brush);
}
if (b[1])
{
FreeBrush (b[1]);
*back = CopyBrush (brush);
}
return;
}
// add the midwinding to both sides
for (i=0 ; i<2 ; i++)
{
cs = &b[i]->sides[b[i]->numsides];
b[i]->numsides++;
cs->planenum = planenum^i^1;
cs->shaderInfo = NULL;
if (i==0)
cs->winding = CopyWinding (midwinding);
else
cs->winding = midwinding;
}
{
vec_t v1;
int i;
for (i=0 ; i<2 ; i++)
{
v1 = BrushVolume (b[i]);
if (v1 < 1.0)
{
FreeBrush (b[i]);
b[i] = NULL;
// Sys_FPrintf (SYS_VRB,"tiny volume after clip\n");
}
}
}
*front = b[0];
*back = b[1];
}

View File

@@ -0,0 +1,81 @@
/* -------------------------------------------------------------------------------
Copyright (C) 1999-2006 Id Software, Inc. and contributors.
For a list of contributors, see the accompanying CONTRIBUTORS file.
This file is part of GtkRadiant.
GtkRadiant is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
GtkRadiant is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
----------------------------------------------------------------------------------
This code has been altered significantly from its original form, to support
several games based on the Quake III Arena engine, in the form of "Q3Map2."
------------------------------------------------------------------------------- */
/* marker */
#define BRUSH_PRIMIT_C
/* dependencies */
#include "q3map2.h"
/* -------------------------------------------------------------------------------
functions
------------------------------------------------------------------------------- */
/*
ComputeAxisBase()
computes the base texture axis for brush primitive texturing
note: ComputeAxisBase here and in editor code must always BE THE SAME!
warning: special case behaviour of atan2( y, x ) <-> atan( y / x ) might not be the same everywhere when x == 0
rotation by (0,RotY,RotZ) assigns X to normal
*/
void ComputeAxisBase( vec3_t normal, vec3_t texX, vec3_t texY )
{
vec_t RotY, RotZ;
/* do some cleaning */
if( fabs( normal[ 0 ] ) < 1e-6 )
normal[ 0 ]= 0.0f;
if( fabs( normal[ 1 ] ) < 1e-6 )
normal[ 1 ]=0.0f;
if( fabs( normal[ 2 ] ) < 1e-6 )
normal[ 2 ] = 0.0f;
/* compute the two rotations around y and z to rotate x to normal */
RotY = -atan2( normal[ 2 ], sqrt( normal[ 1 ] * normal[ 1 ] + normal[ 0 ] * normal[ 0 ]) );
RotZ = atan2( normal[ 1 ], normal[ 0 ] );
/* rotate (0,1,0) and (0,0,1) to compute texX and texY */
texX[ 0 ] = -sin( RotZ );
texX[ 1 ] = cos( RotZ );
texX[ 2 ] = 0;
/* the texY vector is along -z (t texture coorinates axis) */
texY[ 0 ] = -sin( RotY ) * cos( RotZ );
texY[ 1 ] = -sin( RotY ) * sin( RotZ );
texY[ 2 ] = -cos( RotY );
}

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,454 @@
/* -------------------------------------------------------------------------------
Copyright (C) 1999-2006 Id Software, Inc. and contributors.
For a list of contributors, see the accompanying CONTRIBUTORS file.
This file is part of GtkRadiant.
GtkRadiant is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
GtkRadiant is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
----------------------------------------------------------------------------------
This code has been altered significantly from its original form, to support
several games based on the Quake III Arena engine, in the form of "Q3Map2."
------------------------------------------------------------------------------- */
/* marker */
#define FACEBSP_C
/* dependencies */
#include "q3map2.h"
int c_faceLeafs;
/*
================
AllocBspFace
================
*/
face_t *AllocBspFace( void ) {
face_t *f;
f = safe_malloc(sizeof(*f));
memset( f, 0, sizeof(*f) );
return f;
}
/*
================
FreeBspFace
================
*/
void FreeBspFace( face_t *f ) {
if ( f->w ) {
FreeWinding( f->w );
}
free( f );
}
/*
SelectSplitPlaneNum()
finds the best split plane for this node
*/
static void SelectSplitPlaneNum( node_t *node, face_t *list, int *splitPlaneNum, int *compileFlags )
{
face_t *split;
face_t *check;
face_t *bestSplit;
int splits, facing, front, back;
int side;
plane_t *plane;
int value, bestValue;
int i;
vec3_t normal;
float dist;
int planenum;
/* ydnar: set some defaults */
*splitPlaneNum = -1; /* leaf */
*compileFlags = 0;
/* ydnar 2002-06-24: changed this to split on z-axis as well */
/* ydnar 2002-09-21: changed blocksize to be a vector, so mappers can specify a 3 element value */
/* if it is crossing a block boundary, force a split */
for( i = 0; i < 3; i++ )
{
if( blockSize[ i ] <= 0 )
continue;
dist = blockSize[ i ] * (floor( node->mins[ i ] / blockSize[ i ] ) + 1);
if( node->maxs[ i ] > dist )
{
VectorClear( normal );
normal[ i ] = 1;
planenum = FindFloatPlane( normal, dist, 0, NULL );
*splitPlaneNum = planenum;
return;
}
}
/* pick one of the face planes */
bestValue = -99999;
bestSplit = list;
for( split = list; split; split = split->next )
split->checked = qfalse;
for( split = list; split; split = split->next )
{
if ( split->checked )
continue;
plane = &mapplanes[ split->planenum ];
splits = 0;
facing = 0;
front = 0;
back = 0;
for ( check = list ; check ; check = check->next ) {
if ( check->planenum == split->planenum ) {
facing++;
check->checked = qtrue; // won't need to test this plane again
continue;
}
side = WindingOnPlaneSide( check->w, plane->normal, plane->dist );
if ( side == SIDE_CROSS ) {
splits++;
} else if ( side == SIDE_FRONT ) {
front++;
} else if ( side == SIDE_BACK ) {
back++;
}
}
value = 5*facing - 5*splits; // - abs(front-back);
if ( plane->type < 3 ) {
value+=5; // axial is better
}
value += split->priority; // prioritize hints higher
if ( value > bestValue ) {
bestValue = value;
bestSplit = split;
}
}
/* nothing, we have a leaf */
if( bestValue == -99999 )
return;
/* set best split data */
*splitPlaneNum = bestSplit->planenum;
*compileFlags = bestSplit->compileFlags;
}
/*
CountFaceList()
counts bsp faces in the linked list
*/
int CountFaceList( face_t *list )
{
int c;
c = 0;
for( list; list != NULL; list = list->next )
c++;
return c;
}
/*
BuildFaceTree_r()
recursively builds the bsp, splitting on face planes
*/
void BuildFaceTree_r( node_t *node, face_t *list )
{
face_t *split;
face_t *next;
int side;
plane_t *plane;
face_t *newFace;
face_t *childLists[2];
winding_t *frontWinding, *backWinding;
int i;
int splitPlaneNum, compileFlags;
/* count faces left */
i = CountFaceList( list );
/* select the best split plane */
SelectSplitPlaneNum( node, list, &splitPlaneNum, &compileFlags );
/* if we don't have any more faces, this is a node */
if ( splitPlaneNum == -1 )
{
node->planenum = PLANENUM_LEAF;
c_faceLeafs++;
return;
}
/* partition the list */
node->planenum = splitPlaneNum;
node->compileFlags = compileFlags;
plane = &mapplanes[ splitPlaneNum ];
childLists[0] = NULL;
childLists[1] = NULL;
for( split = list; split; split = next )
{
/* set next */
next = split->next;
/* don't split by identical plane */
if( split->planenum == node->planenum )
{
FreeBspFace( split );
continue;
}
/* determine which side the face falls on */
side = WindingOnPlaneSide( split->w, plane->normal, plane->dist );
/* switch on side */
if( side == SIDE_CROSS )
{
ClipWindingEpsilon( split->w, plane->normal, plane->dist, CLIP_EPSILON * 2,
&frontWinding, &backWinding );
if( frontWinding ) {
newFace = AllocBspFace();
newFace->w = frontWinding;
newFace->next = childLists[0];
newFace->planenum = split->planenum;
newFace->priority = split->priority;
newFace->compileFlags = split->compileFlags;
childLists[0] = newFace;
}
if( backWinding ) {
newFace = AllocBspFace();
newFace->w = backWinding;
newFace->next = childLists[1];
newFace->planenum = split->planenum;
newFace->priority = split->priority;
newFace->compileFlags = split->compileFlags;
childLists[1] = newFace;
}
FreeBspFace( split );
} else if ( side == SIDE_FRONT ) {
split->next = childLists[0];
childLists[0] = split;
} else if ( side == SIDE_BACK ) {
split->next = childLists[1];
childLists[1] = split;
}
}
// recursively process children
for ( i = 0 ; i < 2 ; i++ ) {
node->children[i] = AllocNode();
node->children[i]->parent = node;
VectorCopy( node->mins, node->children[i]->mins );
VectorCopy( node->maxs, node->children[i]->maxs );
}
for ( i = 0 ; i < 3 ; i++ ) {
if ( plane->normal[i] == 1 ) {
node->children[0]->mins[i] = plane->dist;
node->children[1]->maxs[i] = plane->dist;
break;
}
}
for ( i = 0 ; i < 2 ; i++ ) {
BuildFaceTree_r ( node->children[i], childLists[i]);
}
}
/*
================
FaceBSP
List will be freed before returning
================
*/
tree_t *FaceBSP( face_t *list ) {
tree_t *tree;
face_t *face;
int i;
int count;
Sys_FPrintf (SYS_VRB, "--- FaceBSP ---\n" );
tree = AllocTree ();
count = 0;
for( face = list; face != NULL; face = face->next )
{
count++;
for( i = 0; i < face->w->numpoints; i++ )
{
AddPointToBounds( face->w->p[ i ], tree->mins, tree->maxs );
}
}
Sys_FPrintf( SYS_VRB, "%9d faces\n", count );
tree->headnode = AllocNode();
VectorCopy( tree->mins, tree->headnode->mins );
VectorCopy( tree->maxs, tree->headnode->maxs );
c_faceLeafs = 0;
BuildFaceTree_r ( tree->headnode, list );
Sys_FPrintf( SYS_VRB, "%9d leafs\n", c_faceLeafs );
return tree;
}
/*
MakeStructuralBSPFaceList()
get structural brush faces
*/
face_t *MakeStructuralBSPFaceList( brush_t *list )
{
brush_t *b;
int i;
side_t *s;
winding_t *w;
face_t *f, *flist;
flist = NULL;
for( b = list; b != NULL; b = b->next )
{
if( b->detail )
continue;
for( i = 0; i < b->numsides; i++ )
{
/* get side and winding */
s = &b->sides[ i ];
w = s->winding;
if( w == NULL )
continue;
/* ydnar: skip certain faces */
if( s->compileFlags & C_SKIP )
continue;
/* allocate a face */
f = AllocBspFace();
f->w = CopyWinding( w );
f->planenum = s->planenum & ~1;
f->compileFlags = s->compileFlags; /* ydnar */
/* ydnar: set priority */
f->priority = 0;
if( f->compileFlags & C_HINT )
f->priority += HINT_PRIORITY;
if( f->compileFlags & C_ANTIPORTAL )
f->priority += ANTIPORTAL_PRIORITY;
if( f->compileFlags & C_AREAPORTAL )
f->priority += AREAPORTAL_PRIORITY;
/* get next face */
f->next = flist;
flist = f;
}
}
return flist;
}
/*
MakeVisibleBSPFaceList()
get visible brush faces
*/
face_t *MakeVisibleBSPFaceList( brush_t *list )
{
brush_t *b;
int i;
side_t *s;
winding_t *w;
face_t *f, *flist;
flist = NULL;
for( b = list; b != NULL; b = b->next )
{
if( b->detail )
continue;
for( i = 0; i < b->numsides; i++ )
{
/* get side and winding */
s = &b->sides[ i ];
w = s->visibleHull;
if( w == NULL )
continue;
/* ydnar: skip certain faces */
if( s->compileFlags & C_SKIP )
continue;
/* allocate a face */
f = AllocBspFace();
f->w = CopyWinding( w );
f->planenum = s->planenum & ~1;
f->compileFlags = s->compileFlags; /* ydnar */
/* ydnar: set priority */
f->priority = 0;
if( f->compileFlags & C_HINT )
f->priority += HINT_PRIORITY;
if( f->compileFlags & C_ANTIPORTAL )
f->priority += ANTIPORTAL_PRIORITY;
if( f->compileFlags & C_AREAPORTAL )
f->priority += AREAPORTAL_PRIORITY;
/* get next face */
f->next = flist;
flist = f;
}
}
return flist;
}

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,34 @@
/*
Copyright (C) 1999-2006 Id Software, Inc. and contributors.
For a list of contributors, see the accompanying CONTRIBUTORS file.
This file is part of GtkRadiant.
GtkRadiant is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
GtkRadiant is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
/* ydnar: for -game support */
typedef struct game_s
{
char *arg; /* -game matches this */
char *gamePath; /* main game data dir */
char *homeBasePath; /* home sub-dir on unix */
char *magic; /* magic word for figuring out base path */
qboolean wolfLight; /* when true, lights work like wolf q3map */
int bspVersion; /* BSP version to use */
}
game_t;

View File

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

View File

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

View File

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

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

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

View File

@@ -0,0 +1,127 @@
/* -------------------------------------------------------------------------------
Copyright (C) 1999-2006 Id Software, Inc. and contributors.
For a list of contributors, see the accompanying CONTRIBUTORS file.
This file is part of GtkRadiant.
GtkRadiant is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
GtkRadiant is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
----------------------------------------------------------------------------------
This code has been altered significantly from its original form, to support
several games based on the Quake III Arena engine, in the form of "Q3Map2."
------------------------------------------------------------------------------- */
/* marker */
#define LEAKFILE_C
/* dependencies */
#include "q3map2.h"
/*
==============================================================================
LEAK FILE GENERATION
Save out name.line for qe3 to read
==============================================================================
*/
/*
=============
LeakFile
Finds the shortest possible chain of portals
that leads from the outside leaf to a specifically
occupied leaf
TTimo: builds a polyline xml node
=============
*/
xmlNodePtr LeakFile (tree_t *tree)
{
vec3_t mid;
FILE *linefile;
char filename[1024];
node_t *node;
int count;
xmlNodePtr xml_node, point;
if (!tree->outside_node.occupied)
return NULL;
Sys_FPrintf (SYS_VRB,"--- LeakFile ---\n");
//
// write the points to the file
//
sprintf (filename, "%s.lin", source);
linefile = fopen (filename, "w");
if (!linefile)
Error ("Couldn't open %s\n", filename);
xml_node = xmlNewNode (NULL, "polyline");
count = 0;
node = &tree->outside_node;
while (node->occupied > 1)
{
int next;
portal_t *p, *nextportal;
node_t *nextnode;
int s;
// find the best portal exit
next = node->occupied;
for (p=node->portals ; p ; p = p->next[!s])
{
s = (p->nodes[0] == node);
if (p->nodes[s]->occupied
&& p->nodes[s]->occupied < next)
{
nextportal = p;
nextnode = p->nodes[s];
next = nextnode->occupied;
}
}
node = nextnode;
WindingCenter (nextportal->winding, mid);
fprintf (linefile, "%f %f %f\n", mid[0], mid[1], mid[2]);
point = xml_NodeForVec(mid);
xmlAddChild(xml_node, point);
count++;
}
// add the occupant center
GetVectorForKey (node->occupant, "origin", mid);
fprintf (linefile, "%f %f %f\n", mid[0], mid[1], mid[2]);
point = xml_NodeForVec(mid);
xmlAddChild(xml_node, point);
Sys_FPrintf( SYS_VRB, "%9d point linefile\n", count+1);
fclose (linefile);
return xml_node;
}

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

File diff suppressed because it is too large Load Diff

View File

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

View File

@@ -0,0 +1,124 @@
/*
Copyright (C) 1999-2006 Id Software, Inc. and contributors.
For a list of contributors, see the accompanying CONTRIBUTORS file.
This file is part of GtkRadiant.
GtkRadiant is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
GtkRadiant is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#define LIGHT_SHADOWS_C
#include "light.h"
#include "inout.h"
/* -------------------------------------------------------------------------------
ydnar: this code deals with shadow volume bsps
------------------------------------------------------------------------------- */
typedef struct shadowNode_s
{
vec4_t plane;
int children[ 2 ];
}
shadowNode_t;
int numShadowNodes;
shadowNode_t *shadowNodes;
/*
AddShadow()
adds a shadow, returning the index into the shadow list
*/
/*
MakeShadowFromPoints()
creates a shadow volume from 4 points (the first being the light origin)
*/
/*
SetupShadows()
sets up the shadow volumes for all lights in the world
*/
void SetupShadows( void )
{
int i, j, s;
light_t *light;
dleaf_t *leaf;
dsurface_t *ds;
surfaceInfo_t *info;
shaderInfo_t *si;
byte *tested;
/* early out for weird cases where there are no lights */
if( lights == NULL )
return;
/* note it */
Sys_FPrintf( SYS_VRB, "--- SetupShadows ---\n" );
/* allocate a surface test list */
tested = safe_malloc( numDrawSurfaces / 8 + 1 );
/* walk the list of lights */
for( light = lights; light != NULL; light = light->next )
{
/* do some early out testing */
if( light->cluster < 0 )
continue;
/* clear surfacetest list */
memset( tested, 0, numDrawSurfaces / 8 + 1 );
/* walk the bsp leaves */
for( i = 0, leaf = dleafs; i < numleafs; i++, leaf++ )
{
/* in pvs? */
if( ClusterVisible( light->cluster, leaf->cluster ) == qfalse )
continue;
/* walk the surface list for this leaf */
for( j = 0; j < leaf->numLeafSurfaces; j++ )
{
/* don't filter a surface more than once */
s = dleafsurfaces[ leaf->firstLeafSurface + j ];
if( tested[ s >> 3 ] & (1 << (s & 7)) )
continue;
tested[ s >> 3 ] |= (1 << (s & 7));
/* get surface and info */
ds = &drawSurfaces[ s ];
info = &surfaceInfos[ s ];
si = info->si;
/* don't create shadow volumes from translucent surfaces */
if( si->contents & CONTENTS_TRANSLUCENT )
continue;
}
}
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,496 @@
/*
Copyright (C) 1999-2006 Id Software, Inc. and contributors.
For a list of contributors, see the accompanying CONTRIBUTORS file.
This file is part of GtkRadiant.
GtkRadiant is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
GtkRadiant is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "qbsp.h"
/*
Lightmap allocation has to be done after all flood filling and
visible surface determination.
*/
int numSortShaders;
mapDrawSurface_t *surfsOnShader[ MAX_MAP_SHADERS ];
int allocated[ LIGHTMAP_WIDTH ];
int numLightmaps = 1;
int c_exactLightmap = 0;
int c_planarPatch = 0;
int c_nonplanarLightmap = 0;
void PrepareNewLightmap( void ) {
memset( allocated, 0, sizeof( allocated ) );
numLightmaps++;
}
/*
===============
AllocLMBlock
returns a texture number and the position inside it
===============
*/
qboolean AllocLMBlock (int w, int h, int *x, int *y)
{
int i, j;
int best, best2;
best = LIGHTMAP_HEIGHT;
for ( i=0 ; i <= LIGHTMAP_WIDTH-w ; i++ ) {
best2 = 0;
for (j=0 ; j<w ; j++) {
if (allocated[i+j] >= best) {
break;
}
if (allocated[i+j] > best2) {
best2 = allocated[i+j];
}
}
if (j == w) { // this is a valid spot
*x = i;
*y = best = best2;
}
}
if (best + h > LIGHTMAP_HEIGHT) {
return qfalse;
}
for (i=0 ; i<w ; i++) {
allocated[*x + i] = best + h;
}
return qtrue;
}
/*
===================
AllocateLightmapForPatch
===================
*/
//#define LIGHTMAP_PATCHSHIFT
void AllocateLightmapForPatch( mapDrawSurface_t *ds )
{
int i, j, k;
drawVert_t *verts;
int w, h;
int x, y;
float s, t;
mesh_t mesh, *subdividedMesh, *tempMesh, *newmesh;
int widthtable[LIGHTMAP_WIDTH], heighttable[LIGHTMAP_HEIGHT], ssize;
verts = ds->verts;
mesh.width = ds->patchWidth;
mesh.height = ds->patchHeight;
mesh.verts = verts;
newmesh = SubdivideMesh( mesh, 8, 999 );
PutMeshOnCurve( *newmesh );
tempMesh = RemoveLinearMeshColumnsRows( newmesh );
FreeMesh(newmesh);
/* get sample size */
ssize = ds->sampleSize;
#ifdef LIGHTMAP_PATCHSHIFT
subdividedMesh = SubdivideMeshQuads( tempMesh, ssize, LIGHTMAP_WIDTH-1, widthtable, heighttable );
#else
subdividedMesh = SubdivideMeshQuads( tempMesh, ssize, LIGHTMAP_WIDTH, widthtable, heighttable );
#endif
w = subdividedMesh->width;
h = subdividedMesh->height;
#ifdef LIGHTMAP_PATCHSHIFT
w++;
h++;
#endif
FreeMesh(subdividedMesh);
// allocate the lightmap
c_exactLightmap += w * h;
if ( !AllocLMBlock( w, h, &x, &y ) ) {
PrepareNewLightmap();
if ( !AllocLMBlock( w, h, &x, &y ) )
{
Error("Entity %i, brush %i: Lightmap allocation failed",
ds->mapBrush->entitynum, ds->mapBrush->brushnum );
}
}
#ifdef LIGHTMAP_PATCHSHIFT
w--;
h--;
#endif
// set the lightmap texture coordinates in the drawVerts
ds->lightmapNum = numLightmaps - 1;
ds->lightmapWidth = w;
ds->lightmapHeight = h;
ds->lightmapX = x;
ds->lightmapY = y;
for ( i = 0 ; i < ds->patchWidth ; i++ ) {
for ( k = 0 ; k < w ; k++ ) {
if ( originalWidths[k] >= i ) {
break;
}
}
if (k >= w)
k = w-1;
s = x + k;
for ( j = 0 ; j < ds->patchHeight ; j++ ) {
for ( k = 0 ; k < h ; k++ ) {
if ( originalHeights[k] >= j ) {
break;
}
}
if (k >= h)
k = h-1;
t = y + k;
verts[i + j * ds->patchWidth].lightmap[0] = ( s + 0.5 ) / LIGHTMAP_WIDTH;
verts[i + j * ds->patchWidth].lightmap[1] = ( t + 0.5 ) / LIGHTMAP_HEIGHT;
}
}
}
/*
===================
AllocateLightmapForSurface
===================
*/
//#define LIGHTMAP_BLOCK 16
void AllocateLightmapForSurface( mapDrawSurface_t *ds )
{
vec3_t mins, maxs, size, exactSize, delta;
int i;
drawVert_t *verts;
int w, h;
int x, y, ssize;
int axis;
vec3_t vecs[ 2 ];
float s, t;
vec3_t origin;
vec4_t plane;
float d;
/* debug code */
#if 0
if( ds->type == SURF_META && ds->planar == qfalse )
Sys_Printf( "NPMS: %3d vertexes, %s\n", ds->numVerts, ds->shaderInfo->shader );
else if( ds->type == SURF_META && ds->planar == qtrue )
Sys_Printf( "PMS: %3d vertexes, %s\n", ds->numVerts, ds->shaderInfo->shader );
#endif
/* ydnar: handle planar patches */
if( noPatchFix == qtrue || (ds->type == SURF_PATCH && ds->planeNum < 0) )
{
AllocateLightmapForPatch( ds );
return;
}
/* get sample size */
ssize = ds->sampleSize;
/* bound the surface */
ClearBounds( mins, maxs );
verts = ds->verts;
for ( i = 0 ; i < ds->numVerts ; i++ )
AddPointToBounds( verts[i].xyz, mins, maxs );
/* round to the lightmap resolution */
for( i = 0; i < 3; i++ )
{
exactSize[i] = maxs[i] - mins[i];
mins[i] = ssize * floor( mins[i] / ssize );
maxs[i] = ssize * ceil( maxs[i] / ssize );
size[i] = (maxs[i] - mins[i]) / ssize + 1;
}
/* ydnar: lightmap projection axis is already stored */
memset( vecs, 0, sizeof( vecs ) );
/* classify the plane (x y or z major) (ydnar: biased to z axis projection) */
if( ds->lightmapAxis[ 2 ] >= ds->lightmapAxis[ 0 ] && ds->lightmapAxis[ 2 ] >= ds->lightmapAxis[ 1 ] )
{
w = size[ 0 ];
h = size[ 1 ];
axis = 2;
vecs[ 0 ][ 0 ] = 1.0 / ssize;
vecs[ 1 ][ 1 ] = 1.0 / ssize;
}
else if( ds->lightmapAxis[ 0 ] >= ds->lightmapAxis[ 1 ] && ds->lightmapAxis[ 0 ] >= ds->lightmapAxis[ 2 ] )
{
w = size[ 1 ];
h = size[ 2 ];
axis = 0;
vecs[ 0 ][ 1 ] = 1.0 / ssize;
vecs[ 1 ][ 2 ] = 1.0 / ssize;
}
else
{
w = size[ 0 ];
h = size[ 2 ];
axis = 1;
vecs[ 0 ][ 0 ] = 1.0 / ssize;
vecs[ 1 ][ 2 ] = 1.0 / ssize;
}
/* odd check, given projection is now precalculated */
if( ds->lightmapAxis[ axis ] == 0 )
Error( "Chose a 0 valued axis" );
/* clamp to lightmap texture resolution */
if( w > LIGHTMAP_WIDTH )
{
VectorScale ( vecs[0], (float) LIGHTMAP_WIDTH / w, vecs[0] );
w = LIGHTMAP_WIDTH;
}
if( h > LIGHTMAP_HEIGHT )
{
VectorScale ( vecs[1], (float) LIGHTMAP_HEIGHT / h, vecs[1] );
h = LIGHTMAP_HEIGHT;
}
/* ydnar */
if( ds->planar == qfalse )
c_nonplanarLightmap += w * h;
c_exactLightmap += w * h;
if( !AllocLMBlock( w, h, &x, &y ) )
{
PrepareNewLightmap();
if ( !AllocLMBlock( w, h, &x, &y ) )
{
Error( "Entity %i, brush %i: Lightmap allocation failed",
ds->mapBrush->entitynum, ds->mapBrush->brushnum );
}
}
/* set the lightmap texture coordinates in the drawVerts */
ds->lightmapNum = numLightmaps - 1;
ds->lightmapWidth = w;
ds->lightmapHeight = h;
ds->lightmapX = x;
ds->lightmapY = y;
for ( i = 0 ; i < ds->numVerts ; i++ )
{
VectorSubtract( verts[i].xyz, mins, delta );
s = DotProduct( delta, vecs[0] ) + x + 0.5;
t = DotProduct( delta, vecs[1] ) + y + 0.5;
verts[i].lightmap[0] = s / LIGHTMAP_WIDTH;
verts[i].lightmap[1] = t / LIGHTMAP_HEIGHT;
}
/* calculate the world coordinates of the lightmap samples */
/* construct a plane from the first vert and clear bounding box */
/* project mins onto plane to get origin */
VectorCopy( ds->lightmapVecs[ 2 ], plane );
plane[ 3 ] = DotProduct( ds->verts[ 0 ].xyz, plane );
d = DotProduct( mins, plane ) - plane[ 3 ];
d /= plane[ axis ];
//% d = DotProduct( mins, plane->normal ) - plane->dist;
//% d /= plane->normal[ axis ];
VectorCopy( mins, origin );
origin[ axis ] -= d;
/* project stepped lightmap blocks and subtract to get planevecs */
for( i = 0; i < 2; i++ )
{
vec3_t normalized;
float len;
len = VectorNormalize( vecs[i], normalized );
VectorScale( normalized, (1.0/len), vecs[i] );
d = DotProduct( vecs[i], plane );
d /= plane[ axis ];
//%d = DotProduct( vecs[i], plane->normal );
//%d /= plane->normal[ axis ];
vecs[i][axis] -= d;
}
/* store lightmap origin and vectors (fixme: make this work right) */
VectorCopy( origin, ds->lightmapOrigin );
//% VectorCopy( plane->normal, ds->lightmapVecs[ 2 ] );
/* ydnar: lightmap vectors 0 and 1 are used for lod bounds, so don't overwrite */
if( ds->type == SURF_PATCH )
c_planarPatch++;
/* store lightmap vectors */
VectorCopy( vecs[ 0 ], ds->lightmapVecs[ 0 ] );
VectorCopy( vecs[ 1 ], ds->lightmapVecs[ 1 ] );
/* ydnar: print some stats */
//Sys_FPrintf( SYS_VRB, "Lightmap block %3d (%3d, %3d) (%3d x %3d) emitted\n", (numLightmaps - 1), x, y, w, h );
}
/*
===================
AllocateLightmaps
===================
*/
void AllocateLightmaps( entity_t *e )
{
int i, j;
mapDrawSurface_t *ds;
shaderInfo_t *si;
/* note it */
Sys_FPrintf( SYS_VRB,"--- AllocateLightmaps ---\n" );
/* sort all surfaces by shader so common shaders will usually be in the same lightmap */
/* ydnar: this is done in two passes, because of an odd bug with lightmapped terrain */
numSortShaders = 0;
for( i = e->firstDrawSurf; i < numMapDrawSurfs; i++ )
{
/* get surface and early out if possible */
ds = &mapDrawSurfs[ i ];
si = ds->shaderInfo;
if( si->surfaceFlags & SURF_VERTEXLIT )
continue;
if( ds->numVerts <= 0 )
continue;
/* ydnar: handle brush faces and patches first */
if( ds->type != SURF_FACE && ds->type != SURF_PATCH )
continue;
/* ydnar: this is unecessary because it should already be set */
//% VectorCopy( ds->plane.normal, ds->lightmapVecs[ 2 ] );
/* search for this shader */
for( j = 0 ; j < numSortShaders; j++ )
{
if( ds->shaderInfo == surfsOnShader[ j ]->shaderInfo )
{
ds->nextOnShader = surfsOnShader[ j ];
surfsOnShader[ j ] = ds;
break;
}
}
/* new shader */
if( j == numSortShaders )
{
if( numSortShaders >= MAX_MAP_SHADERS )
Error( "MAX_MAP_SHADERS" );
surfsOnShader[ j ] = ds;
ds->nextOnShader = NULL;
numSortShaders++;
}
}
/* second pass, to allocate lightmapped terrain last */
for( i = e->firstDrawSurf; i < numMapDrawSurfs; i++ )
{
/* get surface and early out if possible */
ds = &mapDrawSurfs[ i ];
si = ds->shaderInfo;
if( si->surfaceFlags & SURF_VERTEXLIT )
continue;
if( ds->numVerts <= 0 )
continue;
/* ydnar: this only handles metasurfaces and terrain */
if( ds->type != SURF_TERRAIN && ds->type != SURF_META )
continue;
/* ydnar: a lightmap projection should be pre-stored for anything but excessively curved patches */
if( VectorLength( ds->lightmapAxis ) <= 0 )
continue;
/* search for this shader */
for( j = 0; j < numSortShaders; j++ )
{
if( ds->shaderInfo == surfsOnShader[ j ]->shaderInfo )
{
ds->nextOnShader = surfsOnShader[ j ];
surfsOnShader[ j ] = ds;
break;
}
}
/* new shader */
if( j == numSortShaders )
{
if( numSortShaders >= MAX_MAP_SHADERS )
Error( "MAX_MAP_SHADERS" );
surfsOnShader[ j ] = ds;
ds->nextOnShader = NULL;
numSortShaders++;
}
}
/* tot up shader count */
Sys_FPrintf( SYS_VRB, "%9d unique shaders\n", numSortShaders );
/* for each shader, allocate lightmaps for each surface */
for( i = 0; i < numSortShaders; i++ )
{
si = surfsOnShader[ i ]->shaderInfo;
for( ds = surfsOnShader[ i ]; ds; ds = ds->nextOnShader )
{
/* ydnar: promoting pointlight above nolightmap */
if( si->surfaceFlags & SURF_POINTLIGHT )
ds->lightmapNum = -3;
else if( si->surfaceFlags & SURF_NOLIGHTMAP )
ds->lightmapNum = -1;
else
AllocateLightmapForSurface( ds );
}
}
/* emit some statistics */
Sys_FPrintf( SYS_VRB, "%9d exact lightmap texels\n", c_exactLightmap );
Sys_FPrintf( SYS_VRB, "%9d block lightmap texels\n", numLightmaps * LIGHTMAP_WIDTH * LIGHTMAP_HEIGHT );
Sys_FPrintf( SYS_VRB, "%9d non-planar or terrain lightmap texels\n", c_nonplanarLightmap );
Sys_FPrintf( SYS_VRB, "%9d planar patch lightmaps\n", c_planarPatch );
Sys_FPrintf( SYS_VRB, "%9d lightmap textures, size: %d Kbytes\n", numLightmaps, (numLightmaps * LIGHTMAP_WIDTH * LIGHTMAP_HEIGHT * 3) / 1024 );
}

File diff suppressed because it is too large Load Diff

View File

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

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

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

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

File diff suppressed because it is too large Load Diff

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

@@ -0,0 +1,826 @@
/* -------------------------------------------------------------------------------
Copyright (C) 1999-2006 Id Software, Inc. and contributors.
For a list of contributors, see the accompanying CONTRIBUTORS file.
This file is part of GtkRadiant.
GtkRadiant is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
GtkRadiant is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
----------------------------------------------------------------------------------
This code has been altered significantly from its original form, to support
several games based on the Quake III Arena engine, in the form of "Q3Map2."
------------------------------------------------------------------------------- */
/* marker */
#define MESH_C
/* dependencies */
#include "q3map2.h"
/*
LerpDrawVert()
returns an 50/50 interpolated vert
*/
void LerpDrawVert( bspDrawVert_t *a, bspDrawVert_t *b, bspDrawVert_t *out )
{
int k;
out->xyz[ 0 ] = 0.5 * (a->xyz[ 0 ] + b->xyz[ 0 ]);
out->xyz[ 1 ] = 0.5 * (a->xyz[ 1 ] + b->xyz[ 1 ]);
out->xyz[ 2 ] = 0.5 * (a->xyz[ 2 ] + b->xyz[ 2 ]);
out->st[ 0 ] = 0.5 * (a->st[ 0 ] + b->st[ 0 ]);
out->st[ 1 ] = 0.5 * (a->st[ 1 ] + b->st[ 1 ]);
for( k = 0; k < MAX_LIGHTMAPS; k++ )
{
out->lightmap[ k ][ 0 ] = 0.5f * (a->lightmap[ k ][ 0 ] + b->lightmap[ k ][ 0 ]);
out->lightmap[ k ][ 1 ] = 0.5f * (a->lightmap[ k ][ 1 ] + b->lightmap[ k ][ 1 ]);
out->color[ k ][ 0 ] = (a->color[ k ][ 0 ] + b->color[ k ][ 0 ]) >> 1;
out->color[ k ][ 1 ] = (a->color[ k ][ 1 ] + b->color[ k ][ 1 ]) >> 1;
out->color[ k ][ 2 ] = (a->color[ k ][ 2 ] + b->color[ k ][ 2 ]) >> 1;
out->color[ k ][ 3 ] = (a->color[ k ][ 3 ] + b->color[ k ][ 3 ]) >> 1;
}
/* ydnar: added normal interpolation */
out->normal[ 0 ] = 0.5f * (a->normal[ 0 ] + b->normal[ 0 ]);
out->normal[ 1 ] = 0.5f * (a->normal[ 1 ] + b->normal[ 1 ]);
out->normal[ 2 ] = 0.5f * (a->normal[ 2 ] + b->normal[ 2 ]);
/* if the interpolant created a bogus normal, just copy the normal from a */
if( VectorNormalize( out->normal, out->normal ) == 0 )
VectorCopy( a->normal, out->normal );
}
/*
LerpDrawVertAmount()
returns a biased interpolated vert
*/
void LerpDrawVertAmount( bspDrawVert_t *a, bspDrawVert_t *b, float amount, bspDrawVert_t *out )
{
int k;
out->xyz[ 0 ] = a->xyz[ 0 ] + amount * (b->xyz[ 0 ] - a->xyz[ 0 ]);
out->xyz[ 1 ] = a->xyz[ 1 ] + amount * (b->xyz[ 1 ] - a->xyz[ 1 ]);
out->xyz[ 2 ] = a->xyz[ 2 ] + amount * (b->xyz[ 2 ] - a->xyz[ 2 ]);
out->st[ 0 ] = a->st[ 0 ] + amount * (b->st[ 0 ] - a->st[ 0 ]);
out->st[ 1 ] = a->st[ 1 ] + amount * (b->st[ 1 ] - a->st[ 1 ]);
for( k = 0; k < MAX_LIGHTMAPS; k++ )
{
out->lightmap[ k ][ 0 ] = a->lightmap[ k ][ 0 ] + amount * (b->lightmap[ k ][ 0 ] - a->lightmap[ k ][ 0 ]);
out->lightmap[ k ][ 1 ] = a->lightmap[ k ][ 1 ] + amount * (b->lightmap[ k ][ 1 ] - a->lightmap[ k ][ 1 ]);
out->color[ k ][ 0 ] = a->color[ k ][ 0 ] + amount * (b->color[ k ][ 0 ] - a->color[ k ][ 0 ]);
out->color[ k ][ 1 ] = a->color[ k ][ 1 ] + amount * (b->color[ k ][ 1 ] - a->color[ k ][ 1 ]);
out->color[ k ][ 2 ] = a->color[ k ][ 2 ] + amount * (b->color[ k ][ 2 ] - a->color[ k ][ 2 ]);
out->color[ k ][ 3 ] = a->color[ k ][ 3 ] + amount * (b->color[ k ][ 3 ] - a->color[ k ][ 3 ]);
}
out->normal[ 0 ] = a->normal[ 0 ] + amount * (b->normal[ 0 ] - a->normal[ 0 ]);
out->normal[ 1 ] = a->normal[ 1 ] + amount * (b->normal[ 1 ] - a->normal[ 1 ]);
out->normal[ 2 ] = a->normal[ 2 ] + amount * (b->normal[ 2 ] - a->normal[ 2 ]);
/* if the interpolant created a bogus normal, just copy the normal from a */
if( VectorNormalize( out->normal, out->normal ) == 0 )
VectorCopy( a->normal, out->normal );
}
void FreeMesh( mesh_t *m ) {
free( m->verts );
free( m );
}
void PrintMesh( mesh_t *m ) {
int i, j;
for ( i = 0 ; i < m->height ; i++ ) {
for ( j = 0 ; j < m->width ; j++ ) {
Sys_Printf("(%5.2f %5.2f %5.2f) "
, m->verts[i*m->width+j].xyz[0]
, m->verts[i*m->width+j].xyz[1]
, m->verts[i*m->width+j].xyz[2] );
}
Sys_Printf("\n");
}
}
mesh_t *CopyMesh( mesh_t *mesh ) {
mesh_t *out;
int size;
out = safe_malloc( sizeof( *out ) );
out->width = mesh->width;
out->height = mesh->height;
size = out->width * out->height * sizeof( *out->verts );
out->verts = safe_malloc( size );
memcpy( out->verts, mesh->verts, size );
return out;
}
/*
TransposeMesh()
returns a transposed copy of the mesh, freeing the original
*/
mesh_t *TransposeMesh( mesh_t *in ) {
int w, h;
mesh_t *out;
out = safe_malloc( sizeof( *out ) );
out->width = in->height;
out->height = in->width;
out->verts = safe_malloc( out->width * out->height * sizeof( bspDrawVert_t ) );
for ( h = 0 ; h < in->height ; h++ ) {
for ( w = 0 ; w < in->width ; w++ ) {
out->verts[ w * in->height + h ] = in->verts[ h * in->width + w ];
}
}
FreeMesh( in );
return out;
}
void InvertMesh( mesh_t *in ) {
int w, h;
bspDrawVert_t temp;
for ( h = 0 ; h < in->height ; h++ ) {
for ( w = 0 ; w < in->width / 2 ; w++ ) {
temp = in->verts[ h * in->width + w ];
in->verts[ h * in->width + w ] = in->verts[ h * in->width + in->width - 1 - w ];
in->verts[ h * in->width + in->width - 1 - w ] = temp;
}
}
}
/*
=================
MakeMeshNormals
=================
*/
void MakeMeshNormals( mesh_t in )
{
int i, j, k, dist;
vec3_t normal;
vec3_t sum;
int count;
vec3_t base;
vec3_t delta;
int x, y;
bspDrawVert_t *dv;
vec3_t around[8], temp;
qboolean good[8];
qboolean wrapWidth, wrapHeight;
float len;
int neighbors[8][2] =
{
{0,1}, {1,1}, {1,0}, {1,-1}, {0,-1}, {-1,-1}, {-1,0}, {-1,1}
};
wrapWidth = qfalse;
for ( i = 0 ; i < in.height ; i++ ) {
VectorSubtract( in.verts[i*in.width].xyz,
in.verts[i*in.width+in.width-1].xyz, delta );
len = VectorLength( delta );
if ( len > 1.0 ) {
break;
}
}
if ( i == in.height ) {
wrapWidth = qtrue;
}
wrapHeight = qfalse;
for ( i = 0 ; i < in.width ; i++ ) {
VectorSubtract( in.verts[i].xyz,
in.verts[i + (in.height-1)*in.width].xyz, delta );
len = VectorLength( delta );
if ( len > 1.0 ) {
break;
}
}
if ( i == in.width) {
wrapHeight = qtrue;
}
for ( i = 0 ; i < in.width ; i++ ) {
for ( j = 0 ; j < in.height ; j++ ) {
count = 0;
dv = &in.verts[j*in.width+i];
VectorCopy( dv->xyz, base );
for ( k = 0 ; k < 8 ; k++ ) {
VectorClear( around[k] );
good[k] = qfalse;
for ( dist = 1 ; dist <= 3 ; dist++ ) {
x = i + neighbors[k][0] * dist;
y = j + neighbors[k][1] * dist;
if ( wrapWidth ) {
if ( x < 0 ) {
x = in.width - 1 + x;
} else if ( x >= in.width ) {
x = 1 + x - in.width;
}
}
if ( wrapHeight ) {
if ( y < 0 ) {
y = in.height - 1 + y;
} else if ( y >= in.height ) {
y = 1 + y - in.height;
}
}
if ( x < 0 || x >= in.width || y < 0 || y >= in.height ) {
break; // edge of patch
}
VectorSubtract( in.verts[y*in.width+x].xyz, base, temp );
if ( VectorNormalize( temp, temp ) == 0 ) {
continue; // degenerate edge, get more dist
} else {
good[k] = qtrue;
VectorCopy( temp, around[k] );
break; // good edge
}
}
}
VectorClear( sum );
for ( k = 0 ; k < 8 ; k++ ) {
if ( !good[k] || !good[(k+1)&7] ) {
continue; // didn't get two points
}
CrossProduct( around[(k+1)&7], around[k], normal );
if ( VectorNormalize( normal, normal ) == 0 ) {
continue;
}
VectorAdd( normal, sum, sum );
count++;
}
if ( count == 0 ) {
//Sys_Printf("bad normal\n");
count = 1;
}
VectorNormalize( sum, dv->normal );
}
}
}
/*
PutMeshOnCurve()
drops the aproximating points onto the curve
ydnar: fixme: make this use LerpDrawVert() rather than this complicated mess
*/
void PutMeshOnCurve( mesh_t in ) {
int i, j, l, m;
float prev, next;
// put all the aproximating points on the curve
for ( i = 0 ; i < in.width ; i++ ) {
for ( j = 1 ; j < in.height ; j += 2 ) {
for ( l = 0 ; l < 3 ; l++ ) {
prev = ( in.verts[j*in.width+i].xyz[l] + in.verts[(j+1)*in.width+i].xyz[l] ) * 0.5;
next = ( in.verts[j*in.width+i].xyz[l] + in.verts[(j-1)*in.width+i].xyz[l] ) * 0.5;
in.verts[j*in.width+i].xyz[l] = ( prev + next ) * 0.5;
/* ydnar: interpolating st coords */
if( l < 2 )
{
prev = ( in.verts[j*in.width+i].st[l] + in.verts[(j+1)*in.width+i].st[l] ) * 0.5;
next = ( in.verts[j*in.width+i].st[l] + in.verts[(j-1)*in.width+i].st[l] ) * 0.5;
in.verts[j*in.width+i].st[l] = ( prev + next ) * 0.5;
for( m = 0; m < MAX_LIGHTMAPS; m++ )
{
prev = ( in.verts[j*in.width+i].lightmap[ m ][l] + in.verts[(j+1)*in.width+i].lightmap[ m ][l] ) * 0.5;
next = ( in.verts[j*in.width+i].lightmap[ m ][l] + in.verts[(j-1)*in.width+i].lightmap[ m ][l] ) * 0.5;
in.verts[j*in.width+i].lightmap[ m ][l] = ( prev + next ) * 0.5;
}
}
}
}
}
for ( j = 0 ; j < in.height ; j++ ) {
for ( i = 1 ; i < in.width ; i += 2 ) {
for ( l = 0 ; l < 3 ; l++ ) {
prev = ( in.verts[j*in.width+i].xyz[l] + in.verts[j*in.width+i+1].xyz[l] ) * 0.5;
next = ( in.verts[j*in.width+i].xyz[l] + in.verts[j*in.width+i-1].xyz[l] ) * 0.5;
in.verts[j*in.width+i].xyz[l] = ( prev + next ) * 0.5;
/* ydnar: interpolating st coords */
if( l < 2 )
{
prev = ( in.verts[j*in.width+i].st[l] + in.verts[j*in.width+i+1].st[l] ) * 0.5;
next = ( in.verts[j*in.width+i].st[l] + in.verts[j*in.width+i-1].st[l] ) * 0.5;
in.verts[j*in.width+i].st[l] = ( prev + next ) * 0.5;
for( m = 0; m < MAX_LIGHTMAPS; m++ )
{
prev = ( in.verts[j*in.width+i].lightmap[ m ][l] + in.verts[j*in.width+i+1].lightmap[ m ][l] ) * 0.5;
next = ( in.verts[j*in.width+i].lightmap[ m ][l] + in.verts[j*in.width+i-1].lightmap[ m ][l] ) * 0.5;
in.verts[j*in.width+i].lightmap[ m ][l] = ( prev + next ) * 0.5;
}
}
}
}
}
}
/*
=================
SubdivideMesh
=================
*/
mesh_t *SubdivideMesh( mesh_t in, float maxError, float minLength )
{
int i, j, k, l;
bspDrawVert_t prev, next, mid;
vec3_t prevxyz, nextxyz, midxyz;
vec3_t delta;
float len;
mesh_t out;
/* ydnar: static for os x */
MAC_STATIC bspDrawVert_t expand[MAX_EXPANDED_AXIS][MAX_EXPANDED_AXIS];
out.width = in.width;
out.height = in.height;
for ( i = 0 ; i < in.width ; i++ ) {
for ( j = 0 ; j < in.height ; j++ ) {
expand[j][i] = in.verts[j*in.width+i];
}
}
// horizontal subdivisions
for ( j = 0 ; j + 2 < out.width ; j += 2 ) {
// check subdivided midpoints against control points
for ( i = 0 ; i < out.height ; i++ ) {
for ( l = 0 ; l < 3 ; l++ ) {
prevxyz[l] = expand[i][j+1].xyz[l] - expand[i][j].xyz[l];
nextxyz[l] = expand[i][j+2].xyz[l] - expand[i][j+1].xyz[l];
midxyz[l] = (expand[i][j].xyz[l] + expand[i][j+1].xyz[l] * 2
+ expand[i][j+2].xyz[l] ) * 0.25;
}
// if the span length is too long, force a subdivision
if ( VectorLength( prevxyz ) > minLength
|| VectorLength( nextxyz ) > minLength ) {
break;
}
// see if this midpoint is off far enough to subdivide
VectorSubtract( expand[i][j+1].xyz, midxyz, delta );
len = VectorLength( delta );
if ( len > maxError ) {
break;
}
}
if ( out.width + 2 >= MAX_EXPANDED_AXIS ) {
break; // can't subdivide any more
}
if ( i == out.height ) {
continue; // didn't need subdivision
}
// insert two columns and replace the peak
out.width += 2;
for ( i = 0 ; i < out.height ; i++ ) {
LerpDrawVert( &expand[i][j], &expand[i][j+1], &prev );
LerpDrawVert( &expand[i][j+1], &expand[i][j+2], &next );
LerpDrawVert( &prev, &next, &mid );
for ( k = out.width - 1 ; k > j + 3 ; k-- ) {
expand[i][k] = expand[i][k-2];
}
expand[i][j + 1] = prev;
expand[i][j + 2] = mid;
expand[i][j + 3] = next;
}
// back up and recheck this set again, it may need more subdivision
j -= 2;
}
// vertical subdivisions
for ( j = 0 ; j + 2 < out.height ; j += 2 ) {
// check subdivided midpoints against control points
for ( i = 0 ; i < out.width ; i++ ) {
for ( l = 0 ; l < 3 ; l++ ) {
prevxyz[l] = expand[j+1][i].xyz[l] - expand[j][i].xyz[l];
nextxyz[l] = expand[j+2][i].xyz[l] - expand[j+1][i].xyz[l];
midxyz[l] = (expand[j][i].xyz[l] + expand[j+1][i].xyz[l] * 2
+ expand[j+2][i].xyz[l] ) * 0.25;
}
// if the span length is too long, force a subdivision
if ( VectorLength( prevxyz ) > minLength
|| VectorLength( nextxyz ) > minLength ) {
break;
}
// see if this midpoint is off far enough to subdivide
VectorSubtract( expand[j+1][i].xyz, midxyz, delta );
len = VectorLength( delta );
if ( len > maxError ) {
break;
}
}
if ( out.height + 2 >= MAX_EXPANDED_AXIS ) {
break; // can't subdivide any more
}
if ( i == out.width ) {
continue; // didn't need subdivision
}
// insert two columns and replace the peak
out.height += 2;
for ( i = 0 ; i < out.width ; i++ ) {
LerpDrawVert( &expand[j][i], &expand[j+1][i], &prev );
LerpDrawVert( &expand[j+1][i], &expand[j+2][i], &next );
LerpDrawVert( &prev, &next, &mid );
for ( k = out.height - 1 ; k > j + 3 ; k-- ) {
expand[k][i] = expand[k-2][i];
}
expand[j+1][i] = prev;
expand[j+2][i] = mid;
expand[j+3][i] = next;
}
// back up and recheck this set again, it may need more subdivision
j -= 2;
}
// collapse the verts
out.verts = &expand[0][0];
for ( i = 1 ; i < out.height ; i++ ) {
memmove( &out.verts[i*out.width], expand[i], out.width * sizeof(bspDrawVert_t) );
}
return CopyMesh(&out);
}
/*
IterationsForCurve() - ydnar
given a curve of a certain length, return the number of subdivision iterations
note: this is affected by subdivision amount
*/
int IterationsForCurve( float len, int subdivisions )
{
int iterations, facets;
/* calculate the number of subdivisions */
for( iterations = 0; iterations < 3; iterations++ )
{
facets = subdivisions * 16 * pow( 2, iterations );
if( facets >= len )
break;
}
/* return to caller */
return iterations;
}
/*
SubdivideMesh2() - ydnar
subdivides each mesh quad a specified number of times
*/
mesh_t *SubdivideMesh2( mesh_t in, int iterations )
{
int i, j, k;
bspDrawVert_t prev, next, mid;
mesh_t out;
/* ydnar: static for os x */
MAC_STATIC bspDrawVert_t expand[ MAX_EXPANDED_AXIS ][ MAX_EXPANDED_AXIS ];
/* initial setup */
out.width = in.width;
out.height = in.height;
for( i = 0; i < in.width; i++ )
{
for( j = 0; j < in.height; j++ )
expand[ j ][ i ] = in.verts[ j * in.width + i ];
}
/* keep chopping */
for( iterations; iterations > 0; iterations-- )
{
/* horizontal subdivisions */
for( j = 0; j + 2 < out.width; j += 4 )
{
/* check size limit */
if( out.width + 2 >= MAX_EXPANDED_AXIS )
break;
/* insert two columns and replace the peak */
out.width += 2;
for( i = 0; i < out.height; i++ )
{
LerpDrawVert( &expand[ i ][ j ], &expand[ i ][ j + 1 ], &prev );
LerpDrawVert( &expand[ i ][ j + 1 ], &expand[ i ][ j + 2 ], &next );
LerpDrawVert( &prev, &next, &mid );
for ( k = out.width - 1 ; k > j + 3; k-- )
expand [ i ][ k ] = expand[ i ][ k - 2 ];
expand[ i ][ j + 1 ] = prev;
expand[ i ][ j + 2 ] = mid;
expand[ i ][ j + 3 ] = next;
}
}
/* vertical subdivisions */
for ( j = 0; j + 2 < out.height; j += 4 )
{
/* check size limit */
if( out.height + 2 >= MAX_EXPANDED_AXIS )
break;
/* insert two columns and replace the peak */
out.height += 2;
for( i = 0; i < out.width; i++ )
{
LerpDrawVert( &expand[ j ][ i ], &expand[ j + 1 ][ i ], &prev );
LerpDrawVert( &expand[ j + 1 ][ i ], &expand[ j + 2 ][ i ], &next );
LerpDrawVert( &prev, &next, &mid );
for( k = out.height - 1; k > j + 3; k-- )
expand[ k ][ i ] = expand[ k - 2 ][ i ];
expand[ j + 1 ][ i ] = prev;
expand[ j + 2 ][ i ] = mid;
expand[ j + 3 ][ i ] = next;
}
}
}
/* collapse the verts */
out.verts = &expand[ 0 ][ 0 ];
for( i = 1; i < out.height; i++ )
memmove( &out.verts[ i * out.width ], expand[ i ], out.width * sizeof( bspDrawVert_t ) );
/* return to sender */
return CopyMesh( &out );
}
/*
================
ProjectPointOntoVector
================
*/
void ProjectPointOntoVector( vec3_t point, vec3_t vStart, vec3_t vEnd, vec3_t vProj )
{
vec3_t pVec, vec;
VectorSubtract( point, vStart, pVec );
VectorSubtract( vEnd, vStart, vec );
VectorNormalize( vec, vec );
// project onto the directional vector for this segment
VectorMA( vStart, DotProduct( pVec, vec ), vec, vProj );
}
/*
================
RemoveLinearMeshColumsRows
================
*/
mesh_t *RemoveLinearMeshColumnsRows( mesh_t *in ) {
int i, j, k;
float len, maxLength;
vec3_t proj, dir;
mesh_t out;
/* ydnar: static for os x */
MAC_STATIC bspDrawVert_t expand[MAX_EXPANDED_AXIS][MAX_EXPANDED_AXIS];
out.width = in->width;
out.height = in->height;
for ( i = 0 ; i < in->width ; i++ ) {
for ( j = 0 ; j < in->height ; j++ ) {
expand[j][i] = in->verts[j*in->width+i];
}
}
for ( j = 1 ; j < out.width - 1; j++ ) {
maxLength = 0;
for ( i = 0 ; i < out.height ; i++ ) {
ProjectPointOntoVector(expand[i][j].xyz, expand[i][j-1].xyz, expand[i][j+1].xyz, proj);
VectorSubtract(expand[i][j].xyz, proj, dir);
len = VectorLength(dir);
if (len > maxLength) {
maxLength = len;
}
}
if (maxLength < 0.1)
{
out.width--;
for ( i = 0 ; i < out.height ; i++ ) {
for (k = j; k < out.width; k++) {
expand[i][k] = expand[i][k+1];
}
}
j--;
}
}
for ( j = 1 ; j < out.height - 1; j++ ) {
maxLength = 0;
for ( i = 0 ; i < out.width ; i++ ) {
ProjectPointOntoVector(expand[j][i].xyz, expand[j-1][i].xyz, expand[j+1][i].xyz, proj);
VectorSubtract(expand[j][i].xyz, proj, dir);
len = VectorLength(dir);
if (len > maxLength) {
maxLength = len;
}
}
if (maxLength < 0.1)
{
out.height--;
for ( i = 0 ; i < out.width ; i++ ) {
for (k = j; k < out.height; k++) {
expand[k][i] = expand[k+1][i];
}
}
j--;
}
}
// collapse the verts
out.verts = &expand[0][0];
for ( i = 1 ; i < out.height ; i++ ) {
memmove( &out.verts[i*out.width], expand[i], out.width * sizeof(bspDrawVert_t) );
}
return CopyMesh(&out);
}
/*
=================
SubdivideMeshQuads
=================
*/
mesh_t *SubdivideMeshQuads( mesh_t *in, float minLength, int maxsize, int *widthtable, int *heighttable )
{
int i, j, k, w, h, maxsubdivisions, subdivisions;
vec3_t dir;
float length, maxLength, amount;
mesh_t out;
bspDrawVert_t expand[MAX_EXPANDED_AXIS][MAX_EXPANDED_AXIS];
out.width = in->width;
out.height = in->height;
for ( i = 0 ; i < in->width ; i++ ) {
for ( j = 0 ; j < in->height ; j++ ) {
expand[j][i] = in->verts[j*in->width+i];
}
}
if (maxsize > MAX_EXPANDED_AXIS)
Error("SubdivideMeshQuads: maxsize > MAX_EXPANDED_AXIS");
// horizontal subdivisions
maxsubdivisions = (maxsize - in->width) / (in->width - 1);
for ( w = 0, j = 0 ; w < in->width - 1; w++, j += subdivisions + 1) {
maxLength = 0;
for ( i = 0 ; i < out.height ; i++ ) {
VectorSubtract(expand[i][j+1].xyz, expand[i][j].xyz, dir);
length = VectorLength( dir );
if (length > maxLength) {
maxLength = length;
}
}
subdivisions = (int) (maxLength / minLength);
if (subdivisions > maxsubdivisions)
subdivisions = maxsubdivisions;
widthtable[w] = subdivisions + 1;
if (subdivisions <= 0)
continue;
out.width += subdivisions;
for ( i = 0 ; i < out.height ; i++ ) {
for ( k = out.width - 1 ; k > j + subdivisions; k-- ) {
expand[i][k] = expand[i][k-subdivisions];
}
for (k = 1; k <= subdivisions; k++)
{
amount = (float) k / (subdivisions + 1);
LerpDrawVertAmount(&expand[i][j], &expand[i][j+subdivisions+1], amount, &expand[i][j+k]);
}
}
}
maxsubdivisions = (maxsize - in->height) / (in->height - 1);
for ( h = 0, j = 0 ; h < in->height - 1; h++, j += subdivisions + 1) {
maxLength = 0;
for ( i = 0 ; i < out.width ; i++ ) {
VectorSubtract(expand[j+1][i].xyz, expand[j][i].xyz, dir);
length = VectorLength( dir );
if (length > maxLength) {
maxLength = length;
}
}
subdivisions = (int) (maxLength / minLength);
if (subdivisions > maxsubdivisions)
subdivisions = maxsubdivisions;
heighttable[h] = subdivisions + 1;
if (subdivisions <= 0)
continue;
out.height += subdivisions;
for ( i = 0 ; i < out.width ; i++ ) {
for ( k = out.height - 1 ; k > j + subdivisions; k-- ) {
expand[k][i] = expand[k-subdivisions][i];
}
for (k = 1; k <= subdivisions; k++)
{
amount = (float) k / (subdivisions + 1);
LerpDrawVertAmount(&expand[j][i], &expand[j+subdivisions+1][i], amount, &expand[j+k][i]);
}
}
}
// collapse the verts
out.verts = &expand[0][0];
for ( i = 1 ; i < out.height ; i++ ) {
memmove( &out.verts[i*out.width], expand[i], out.width * sizeof(bspDrawVert_t) );
}
return CopyMesh(&out);
}

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

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

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

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

View File

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

View File

@@ -0,0 +1,971 @@
/* -------------------------------------------------------------------------------
Copyright (C) 1999-2006 Id Software, Inc. and contributors.
For a list of contributors, see the accompanying CONTRIBUTORS file.
This file is part of GtkRadiant.
GtkRadiant is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
GtkRadiant is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
----------------------------------------------------------------------------------
This code has been altered significantly from its original form, to support
several games based on the Quake III Arena engine, in the form of "Q3Map2."
------------------------------------------------------------------------------- */
/* marker */
#define PORTALS_C
/* dependencies */
#include "q3map2.h"
/* ydnar: to fix broken portal windings */
extern qboolean FixWinding( winding_t *w );
int c_active_portals;
int c_peak_portals;
int c_boundary;
int c_boundary_sides;
/*
===========
AllocPortal
===========
*/
portal_t *AllocPortal (void)
{
portal_t *p;
if (numthreads == 1)
c_active_portals++;
if (c_active_portals > c_peak_portals)
c_peak_portals = c_active_portals;
p = safe_malloc (sizeof(portal_t));
memset (p, 0, sizeof(portal_t));
return p;
}
void FreePortal (portal_t *p)
{
if (p->winding)
FreeWinding (p->winding);
if (numthreads == 1)
c_active_portals--;
free (p);
}
/*
PortalPassable
returns true if the portal has non-opaque leafs on both sides
*/
qboolean PortalPassable( portal_t *p )
{
/* is this to global outside leaf? */
if( !p->onnode )
return qfalse;
/* this should never happen */
if( p->nodes[ 0 ]->planenum != PLANENUM_LEAF ||
p->nodes[ 1 ]->planenum != PLANENUM_LEAF )
Error( "Portal_EntityFlood: not a leaf" );
/* ydnar: added antiportal to supress portal generation for visibility blocking */
if( p->compileFlags & C_ANTIPORTAL )
return qfalse;
/* both leaves on either side of the portal must be passable */
if( p->nodes[ 0 ]->opaque == qfalse && p->nodes[ 1 ]->opaque == qfalse )
return qtrue;
/* otherwise this isn't a passable portal */
return qfalse;
}
int c_tinyportals;
int c_badportals; /* ydnar */
/*
=============
AddPortalToNodes
=============
*/
void AddPortalToNodes (portal_t *p, node_t *front, node_t *back)
{
if (p->nodes[0] || p->nodes[1])
Error ("AddPortalToNode: allready included");
p->nodes[0] = front;
p->next[0] = front->portals;
front->portals = p;
p->nodes[1] = back;
p->next[1] = back->portals;
back->portals = p;
}
/*
=============
RemovePortalFromNode
=============
*/
void RemovePortalFromNode (portal_t *portal, node_t *l)
{
portal_t **pp, *t;
// remove reference to the current portal
pp = &l->portals;
while (1)
{
t = *pp;
if (!t)
Error ("RemovePortalFromNode: portal not in leaf");
if ( t == portal )
break;
if (t->nodes[0] == l)
pp = &t->next[0];
else if (t->nodes[1] == l)
pp = &t->next[1];
else
Error ("RemovePortalFromNode: portal not bounding leaf");
}
if (portal->nodes[0] == l)
{
*pp = portal->next[0];
portal->nodes[0] = NULL;
}
else if (portal->nodes[1] == l)
{
*pp = portal->next[1];
portal->nodes[1] = NULL;
}
}
//============================================================================
void PrintPortal (portal_t *p)
{
int i;
winding_t *w;
w = p->winding;
for (i=0 ; i<w->numpoints ; i++)
Sys_Printf ("(%5.0f,%5.0f,%5.0f)\n",w->p[i][0]
, w->p[i][1], w->p[i][2]);
}
/*
================
MakeHeadnodePortals
The created portals will face the global outside_node
================
*/
#define SIDESPACE 8
void MakeHeadnodePortals (tree_t *tree)
{
vec3_t bounds[2];
int i, j, n;
portal_t *p, *portals[6];
plane_t bplanes[6], *pl;
node_t *node;
node = tree->headnode;
// pad with some space so there will never be null volume leafs
for (i=0 ; i<3 ; i++)
{
bounds[0][i] = tree->mins[i] - SIDESPACE;
bounds[1][i] = tree->maxs[i] + SIDESPACE;
if ( bounds[0][i] >= bounds[1][i] ) {
Error( "Backwards tree volume" );
}
}
tree->outside_node.planenum = PLANENUM_LEAF;
tree->outside_node.brushlist = NULL;
tree->outside_node.portals = NULL;
tree->outside_node.opaque = qfalse;
for (i=0 ; i<3 ; i++)
for (j=0 ; j<2 ; j++)
{
n = j*3 + i;
p = AllocPortal ();
portals[n] = p;
pl = &bplanes[n];
memset (pl, 0, sizeof(*pl));
if (j)
{
pl->normal[i] = -1;
pl->dist = -bounds[j][i];
}
else
{
pl->normal[i] = 1;
pl->dist = bounds[j][i];
}
p->plane = *pl;
p->winding = BaseWindingForPlane (pl->normal, pl->dist);
AddPortalToNodes (p, node, &tree->outside_node);
}
// clip the basewindings by all the other planes
for (i=0 ; i<6 ; i++)
{
for (j=0 ; j<6 ; j++)
{
if (j == i)
continue;
ChopWindingInPlace (&portals[i]->winding, bplanes[j].normal, bplanes[j].dist, ON_EPSILON);
}
}
}
//===================================================
/*
================
BaseWindingForNode
================
*/
#define BASE_WINDING_EPSILON 0.001
#define SPLIT_WINDING_EPSILON 0.001
winding_t *BaseWindingForNode (node_t *node)
{
winding_t *w;
node_t *n;
plane_t *plane;
vec3_t normal;
vec_t dist;
w = BaseWindingForPlane (mapplanes[node->planenum].normal
, mapplanes[node->planenum].dist);
// clip by all the parents
for (n=node->parent ; n && w ; )
{
plane = &mapplanes[n->planenum];
if (n->children[0] == node)
{ // take front
ChopWindingInPlace (&w, plane->normal, plane->dist, BASE_WINDING_EPSILON);
}
else
{ // take back
VectorSubtract (vec3_origin, plane->normal, normal);
dist = -plane->dist;
ChopWindingInPlace (&w, normal, dist, BASE_WINDING_EPSILON);
}
node = n;
n = n->parent;
}
return w;
}
//============================================================
/*
==================
MakeNodePortal
create the new portal by taking the full plane winding for the cutting plane
and clipping it by all of parents of this node
==================
*/
void MakeNodePortal (node_t *node)
{
portal_t *new_portal, *p;
winding_t *w;
vec3_t normal;
float dist;
int side;
w = BaseWindingForNode (node);
// clip the portal by all the other portals in the node
for (p = node->portals ; p && w; p = p->next[side])
{
if (p->nodes[0] == node)
{
side = 0;
VectorCopy (p->plane.normal, normal);
dist = p->plane.dist;
}
else if (p->nodes[1] == node)
{
side = 1;
VectorSubtract (vec3_origin, p->plane.normal, normal);
dist = -p->plane.dist;
}
else
Error ("CutNodePortals_r: mislinked portal");
ChopWindingInPlace (&w, normal, dist, CLIP_EPSILON);
}
if (!w)
{
return;
}
/* ydnar: adding this here to fix degenerate windings */
#if 0
if( FixWinding( w ) == qfalse )
{
c_badportals++;
FreeWinding( w );
return;
}
#endif
if (WindingIsTiny (w))
{
c_tinyportals++;
FreeWinding (w);
return;
}
new_portal = AllocPortal ();
new_portal->plane = mapplanes[node->planenum];
new_portal->onnode = node;
new_portal->winding = w;
new_portal->compileFlags = node->compileFlags;
AddPortalToNodes (new_portal, node->children[0], node->children[1]);
}
/*
==============
SplitNodePortals
Move or split the portals that bound node so that the node's
children have portals instead of node.
==============
*/
void SplitNodePortals (node_t *node)
{
portal_t *p, *next_portal, *new_portal;
node_t *f, *b, *other_node;
int side;
plane_t *plane;
winding_t *frontwinding, *backwinding;
plane = &mapplanes[node->planenum];
f = node->children[0];
b = node->children[1];
for (p = node->portals ; p ; p = next_portal)
{
if (p->nodes[0] == node)
side = 0;
else if (p->nodes[1] == node)
side = 1;
else
Error ("SplitNodePortals: mislinked portal");
next_portal = p->next[side];
other_node = p->nodes[!side];
RemovePortalFromNode (p, p->nodes[0]);
RemovePortalFromNode (p, p->nodes[1]);
//
// cut the portal into two portals, one on each side of the cut plane
//
ClipWindingEpsilon (p->winding, plane->normal, plane->dist,
SPLIT_WINDING_EPSILON, &frontwinding, &backwinding);
if (frontwinding && WindingIsTiny(frontwinding))
{
if (!f->tinyportals)
VectorCopy(frontwinding->p[0], f->referencepoint);
f->tinyportals++;
if (!other_node->tinyportals)
VectorCopy(frontwinding->p[0], other_node->referencepoint);
other_node->tinyportals++;
FreeWinding (frontwinding);
frontwinding = NULL;
c_tinyportals++;
}
if (backwinding && WindingIsTiny(backwinding))
{
if (!b->tinyportals)
VectorCopy(backwinding->p[0], b->referencepoint);
b->tinyportals++;
if (!other_node->tinyportals)
VectorCopy(backwinding->p[0], other_node->referencepoint);
other_node->tinyportals++;
FreeWinding (backwinding);
backwinding = NULL;
c_tinyportals++;
}
if (!frontwinding && !backwinding)
{ // tiny windings on both sides
continue;
}
if (!frontwinding)
{
FreeWinding (backwinding);
if (side == 0)
AddPortalToNodes (p, b, other_node);
else
AddPortalToNodes (p, other_node, b);
continue;
}
if (!backwinding)
{
FreeWinding (frontwinding);
if (side == 0)
AddPortalToNodes (p, f, other_node);
else
AddPortalToNodes (p, other_node, f);
continue;
}
// the winding is split
new_portal = AllocPortal ();
*new_portal = *p;
new_portal->winding = backwinding;
FreeWinding (p->winding);
p->winding = frontwinding;
if (side == 0)
{
AddPortalToNodes (p, f, other_node);
AddPortalToNodes (new_portal, b, other_node);
}
else
{
AddPortalToNodes (p, other_node, f);
AddPortalToNodes (new_portal, other_node, b);
}
}
node->portals = NULL;
}
/*
================
CalcNodeBounds
================
*/
void CalcNodeBounds (node_t *node)
{
portal_t *p;
int s;
int i;
// calc mins/maxs for both leafs and nodes
ClearBounds (node->mins, node->maxs);
for (p = node->portals ; p ; p = p->next[s])
{
s = (p->nodes[1] == node);
for (i=0 ; i<p->winding->numpoints ; i++)
AddPointToBounds (p->winding->p[i], node->mins, node->maxs);
}
}
/*
==================
MakeTreePortals_r
==================
*/
void MakeTreePortals_r (node_t *node)
{
int i;
CalcNodeBounds (node);
if (node->mins[0] >= node->maxs[0])
{
Sys_Printf ("WARNING: node without a volume\n");
Sys_Printf("node has %d tiny portals\n", node->tinyportals);
Sys_Printf("node reference point %1.2f %1.2f %1.2f\n", node->referencepoint[0],
node->referencepoint[1],
node->referencepoint[2]);
}
for (i=0 ; i<3 ; i++)
{
if (node->mins[i] < MIN_WORLD_COORD || node->maxs[i] > MAX_WORLD_COORD)
{
if(node->portals && node->portals->winding)
xml_Winding("WARNING: Node With Unbounded Volume", node->portals->winding->p, node->portals->winding->numpoints, qfalse);
break;
}
}
if (node->planenum == PLANENUM_LEAF)
return;
MakeNodePortal (node);
SplitNodePortals (node);
MakeTreePortals_r (node->children[0]);
MakeTreePortals_r (node->children[1]);
}
/*
==================
MakeTreePortals
==================
*/
void MakeTreePortals (tree_t *tree)
{
Sys_FPrintf (SYS_VRB, "--- MakeTreePortals ---\n");
MakeHeadnodePortals (tree);
MakeTreePortals_r (tree->headnode);
Sys_FPrintf( SYS_VRB, "%9d tiny portals\n", c_tinyportals );
Sys_FPrintf( SYS_VRB, "%9d bad portals\n", c_badportals ); /* ydnar */
}
/*
=========================================================
FLOOD ENTITIES
=========================================================
*/
int c_floodedleafs;
/*
=============
FloodPortals_r
=============
*/
void FloodPortals_r( node_t *node, int dist, qboolean skybox )
{
int s;
portal_t *p;
if( skybox )
node->skybox = skybox;
if( node->occupied || node->opaque )
return;
c_floodedleafs++;
node->occupied = dist;
for( p = node->portals; p; p = p->next[ s ] )
{
s = (p->nodes[ 1 ] == node);
FloodPortals_r( p->nodes[ !s ], dist + 1, skybox );
}
}
/*
=============
PlaceOccupant
=============
*/
qboolean PlaceOccupant( node_t *headnode, vec3_t origin, entity_t *occupant, qboolean skybox )
{
vec_t d;
node_t *node;
plane_t *plane;
// find the leaf to start in
node = headnode;
while( node->planenum != PLANENUM_LEAF )
{
plane = &mapplanes[ node->planenum ];
d = DotProduct( origin, plane->normal ) - plane->dist;
if( d >= 0 )
node = node->children[ 0 ];
else
node = node->children[ 1 ];
}
if( node->opaque )
return qfalse;
node->occupant = occupant;
node->skybox = skybox;
FloodPortals_r( node, 1, skybox );
return qtrue;
}
/*
=============
FloodEntities
Marks all nodes that can be reached by entites
=============
*/
qboolean FloodEntities( tree_t *tree )
{
int i, s;
vec3_t origin, offset, scale, angles;
qboolean r, inside, tripped, skybox;
node_t *headnode;
entity_t *e;
const char *value;
headnode = tree->headnode;
Sys_FPrintf( SYS_VRB,"--- FloodEntities ---\n" );
inside = qfalse;
tree->outside_node.occupied = 0;
tripped = qfalse;
c_floodedleafs = 0;
for( i = 1; i < numEntities; i++ )
{
/* get entity */
e = &entities[ i ];
/* get origin */
GetVectorForKey( e, "origin", origin );
if( VectorCompare( origin, vec3_origin ) )
continue;
/* handle skybox entities */
value = ValueForKey( e, "classname" );
if( !Q_stricmp( value, "_skybox" ) )
{
skybox = qtrue;
skyboxPresent = qtrue;
/* invert origin */
VectorScale( origin, -1.0f, offset );
/* get scale */
VectorSet( scale, 64.0f, 64.0f, 64.0f );
value = ValueForKey( e, "_scale" );
if( value[ 0 ] != '\0' )
{
s = sscanf( value, "%f %f %f", &scale[ 0 ], &scale[ 1 ], &scale[ 2 ] );
if( s == 1 )
{
scale[ 1 ] = scale[ 0 ];
scale[ 2 ] = scale[ 0 ];
}
}
/* get "angle" (yaw) or "angles" (pitch yaw roll) */
VectorClear( angles );
angles[ 2 ] = FloatForKey( e, "angle" );
value = ValueForKey( e, "angles" );
if( value[ 0 ] != '\0' )
sscanf( value, "%f %f %f", &angles[ 1 ], &angles[ 2 ], &angles[ 0 ] );
/* set transform matrix (thanks spog) */
m4x4_identity( skyboxTransform );
m4x4_pivoted_transform_by_vec3( skyboxTransform, offset, angles, eXYZ, scale, origin );
}
else
skybox = qfalse;
/* nudge off floor */
origin[ 2 ] += 1;
/* debugging code */
//% if( i == 1 )
//% origin[ 2 ] += 4096;
/* find leaf */
r = PlaceOccupant( headnode, origin, e, skybox );
if( r )
inside = qtrue;
if( (!r || tree->outside_node.occupied) && !tripped )
{
xml_Select( "Entity leaked", e->mapEntityNum, 0, qfalse );
tripped = qtrue;
}
}
Sys_FPrintf( SYS_VRB, "%9d flooded leafs\n", c_floodedleafs );
if( !inside )
Sys_FPrintf( SYS_VRB, "no entities in open -- no filling\n" );
else if( tree->outside_node.occupied )
Sys_FPrintf( SYS_VRB, "entity reached from outside -- no filling\n" );
return (qboolean) (inside && !tree->outside_node.occupied);
}
/*
=========================================================
FLOOD AREAS
=========================================================
*/
int c_areas;
/*
FloodAreas_r()
floods through leaf portals to tag leafs with an area
*/
void FloodAreas_r( node_t *node )
{
int s;
portal_t *p;
brush_t *b;
if( node->areaportal )
{
if( node->area == -1 )
node->area = c_areas;
/* this node is part of an area portal brush */
b = node->brushlist->original;
/* if the current area has already touched this portal, we are done */
if( b->portalareas[ 0 ] == c_areas || b->portalareas[ 1 ] == c_areas )
return;
// note the current area as bounding the portal
if( b->portalareas[ 1 ] != -1 )
{
Sys_Printf( "WARNING: areaportal brush %i touches > 2 areas\n", b->brushNum );
return;
}
if( b->portalareas[ 0 ] != -1 )
b->portalareas[ 1 ] = c_areas;
else
b->portalareas[ 0 ] = c_areas;
return;
}
if( node->area != -1 )
return;
if( node->cluster == -1 )
return;
node->area = c_areas;
/* ydnar: skybox nodes set the skybox area */
if( node->skybox )
skyboxArea = c_areas;
for( p = node->portals; p; p = p->next[ s ] )
{
s = (p->nodes[1] == node);
/* ydnar: allow areaportal portals to block area flow */
if( p->compileFlags & C_AREAPORTAL )
continue;
if( !PortalPassable( p ) )
continue;
FloodAreas_r( p->nodes[ !s ] );
}
}
/*
=============
FindAreas_r
Just decend the tree, and for each node that hasn't had an
area set, flood fill out from there
=============
*/
void FindAreas_r( node_t *node )
{
if( node->planenum != PLANENUM_LEAF )
{
FindAreas_r( node->children[ 0 ] );
FindAreas_r( node->children[ 1 ] );
return;
}
if( node->opaque || node->areaportal || node->area != -1 )
return;
FloodAreas_r( node );
c_areas++;
}
/*
=============
CheckAreas_r
=============
*/
void CheckAreas_r (node_t *node)
{
brush_t *b;
if (node->planenum != PLANENUM_LEAF)
{
CheckAreas_r (node->children[0]);
CheckAreas_r (node->children[1]);
return;
}
if (node->opaque)
return;
if (node->cluster != -1)
if (node->area == -1)
Sys_Printf("WARNING: cluster %d has area set to -1\n", node->cluster);
if (node->areaportal)
{
b = node->brushlist->original;
// check if the areaportal touches two areas
if (b->portalareas[0] == -1 || b->portalareas[1] == -1)
Sys_Printf ("WARNING: areaportal brush %i doesn't touch two areas\n", b->brushNum);
}
}
/*
FloodSkyboxArea_r() - ydnar
sets all nodes with the skybox area to skybox
*/
void FloodSkyboxArea_r( node_t *node )
{
if( skyboxArea < 0 )
return;
if( node->planenum != PLANENUM_LEAF )
{
FloodSkyboxArea_r( node->children[ 0 ] );
FloodSkyboxArea_r( node->children[ 1 ] );
return;
}
if( node->opaque || node->area != skyboxArea )
return;
node->skybox = qtrue;
}
/*
FloodAreas()
mark each leaf with an area, bounded by C_AREAPORTAL
*/
void FloodAreas( tree_t *tree )
{
Sys_FPrintf( SYS_VRB,"--- FloodAreas ---\n" );
FindAreas_r( tree->headnode );
/* ydnar: flood all skybox nodes */
FloodSkyboxArea_r( tree->headnode );
/* check for areaportal brushes that don't touch two areas */
/* ydnar: fix this rather than just silence the warnings */
//% CheckAreas_r( tree->headnode );
Sys_FPrintf( SYS_VRB, "%9d areas\n", c_areas );
}
//======================================================
int c_outside;
int c_inside;
int c_solid;
void FillOutside_r (node_t *node)
{
if (node->planenum != PLANENUM_LEAF)
{
FillOutside_r (node->children[0]);
FillOutside_r (node->children[1]);
return;
}
// anything not reachable by an entity
// can be filled away
if (!node->occupied) {
if ( !node->opaque ) {
c_outside++;
node->opaque = qtrue;
} else {
c_solid++;
}
} else {
c_inside++;
}
}
/*
=============
FillOutside
Fill all nodes that can't be reached by entities
=============
*/
void FillOutside (node_t *headnode)
{
c_outside = 0;
c_inside = 0;
c_solid = 0;
Sys_FPrintf( SYS_VRB,"--- FillOutside ---\n" );
FillOutside_r( headnode );
Sys_FPrintf( SYS_VRB,"%9d solid leafs\n", c_solid );
Sys_Printf( "%9d leafs filled\n", c_outside );
Sys_FPrintf( SYS_VRB, "%9d inside leafs\n", c_inside );
}
//==============================================================

View File

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

View File

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

View File

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

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

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

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

View File

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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

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

Some files were not shown because too many files have changed in this diff Show More