From: Rudolf Polzer <divverent@alientrap.org>
Date: Wed, 23 Feb 2011 10:12:03 +0000 (+0100)
Subject: use q3map2 from NetRadiant (VOLUNTEERS: split this commit into functional parts!)
X-Git-Url: https://git.rm.cloudns.org/?a=commitdiff_plain;h=f6b5ecda0bc947aa19472738121158e9797326a5;p=xonotic%2Fnetradiant.git

use q3map2 from NetRadiant (VOLUNTEERS: split this commit into functional parts!)
---

diff --git a/tools/quake3/q3map2/brush.c b/tools/quake3/q3map2/brush.c
index 1093be3b..b7c50f4c 100644
--- a/tools/quake3/q3map2/brush.c
+++ b/tools/quake3/q3map2/brush.c
@@ -321,8 +321,6 @@ void SnapWeldVectorAccu(vec3_accu_t a, vec3_accu_t b, vec3_accu_t out)
 	}
 }
 
-
-
 /*
 FixWinding() - ydnar
 removes degenerate edges from a winding
diff --git a/tools/quake3/q3map2/bsp.c b/tools/quake3/q3map2/bsp.c
index 3a16b0c2..79019792 100644
--- a/tools/quake3/q3map2/bsp.c
+++ b/tools/quake3/q3map2/bsp.c
@@ -44,7 +44,6 @@ functions
 
 ------------------------------------------------------------------------------- */
 
-
 /*
 ProcessAdvertisements()
 copies advertisement info into the BSP structures
@@ -268,7 +267,6 @@ void ProcessWorldModel( void )
 	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' )
@@ -342,12 +340,12 @@ void ProcessWorldModel( void )
 		Sys_FPrintf( SYS_NOXML, "******* leaked *******\n" );
 		Sys_FPrintf( SYS_NOXML, "**********************\n" );
 		polyline = LeakFile( tree );
-		leaknode = xmlNewNode( NULL, "message" );
-		xmlNodeSetContent( leaknode, "MAP LEAKED\n" );
+		leaknode = xmlNewNode( NULL, (xmlChar*)"message" );
+		xmlNodeSetContent( leaknode, (xmlChar*)"MAP LEAKED\n" );
 		xmlAddChild( leaknode, polyline );
 		level[0] = (int) '0' + SYS_ERR;
 		level[1] = 0;
-		xmlSetProp( leaknode, "level", (char*) &level );
+		xmlSetProp( leaknode, (xmlChar*)"level", (xmlChar*) &level );
 		xml_SendNode( leaknode );
 		if( leaktest )
 		{
@@ -460,7 +458,7 @@ void ProcessWorldModel( void )
 					VectorSet( normal, 0, 0, -1 );
 				
 				/* create the flare surface (note shader defaults automatically) */
-				DrawSurfaceForFlare( mapEntityNum, origin, normal, color, (char*) flareShader, lightStyle );
+				DrawSurfaceForFlare( mapEntityNum, origin, normal, color, flareShader, lightStyle );
 			}
 		}
 	}
@@ -605,6 +603,9 @@ void ProcessModels( void )
 	
 	/* write fogs */
 	EmitFogs();
+
+	/* vortex: emit meta stats */
+	EmitMetaStats();
 }
 
 
@@ -618,18 +619,34 @@ void OnlyEnts( void )
 {
 	char out[ 1024 ];
 
+	char save_cmdline[1024], save_version[1024];
+	const char *p;
 	
 	/* note it */
 	Sys_Printf( "--- OnlyEnts ---\n" );
 	
 	sprintf( out, "%s.bsp", source );
 	LoadBSPFile( out );
+
+	ParseEntities();
+	p = ValueForKey(&entities[0], "_q3map2_cmdline");
+	strncpy(save_cmdline, p, sizeof(save_cmdline));
+	save_cmdline[sizeof(save_cmdline)-1] = 0;
+	p = ValueForKey(&entities[0], "_q3map2_version");
+	strncpy(save_version, p, sizeof(save_version));
+	save_version[sizeof(save_version)-1] = 0;
+
 	numEntities = 0;
 
 	LoadShaderInfo();
-	LoadMapFile( name, qfalse );
+	LoadMapFile( name, qfalse, qfalse );
 	SetModelNumbers();
 	SetLightStyles();
+
+	if(*save_cmdline)
+		SetKeyValue(&entities[0], "_q3map2_cmdline", save_cmdline);
+	if(*save_version)
+		SetKeyValue(&entities[0], "_q3map2_version", save_version);
 	
 	numBSPEntities = numEntities;
 	UnparseEntities();
@@ -660,6 +677,7 @@ int BSPMain( int argc, char **argv )
 	numMapDrawSurfs = 0;
 	
 	tempSource[ 0 ] = '\0';
+	globalCelShader[0] = 0;
 	
 	/* set standard game flags */
 	maxSurfaceVerts = game->maxSurfaceVerts;
@@ -736,6 +754,14 @@ int BSPMain( int argc, char **argv )
  			i++;
 			Sys_Printf( "Lightmap sample size set to %dx%d units\n", sampleSize, sampleSize );
  		}
+		else if( !strcmp( argv[ i ], "-minsamplesize" ) )
+		{
+			minSampleSize = atoi( argv[ i + 1 ] );
+			if( minSampleSize < 1 )
+				minSampleSize = 1;
+			i++;
+			Sys_Printf( "Minimum lightmap sample size set to %dx%d units\n", minSampleSize, minSampleSize );
+		}
 		else if( !strcmp( argv[ i ],  "-custinfoparms") )
 		{
 			Sys_Printf( "Custom info parms enabled\n" );
@@ -816,6 +842,15 @@ int BSPMain( int argc, char **argv )
 			Sys_Printf( "Flatshading enabled\n" );
 			flat = qtrue;
 		}
+		else if( !strcmp( argv[ i ], "-celshader" ) )
+		{
+			++i;
+			if(argv[i][0])
+				sprintf( globalCelShader, "textures/%s", argv[ i ] );
+			else
+				*globalCelShader = 0;
+			Sys_Printf( "Global cel shader set to \"%s\"\n", globalCelShader );
+		}
 		else if( !strcmp( argv[ i ], "-meta" ) )
 		{
 			Sys_Printf( "Creating meta surfaces from brush faces\n" );
@@ -856,10 +891,27 @@ int BSPMain( int argc, char **argv )
 			Sys_Printf( "Debug portal surfaces enabled\n" );
 			debugPortals = qtrue;
 		}
+		else if( !strcmp( argv[ i ], "-altsplit" ) )
+		{
+			Sys_Printf( "Alternate BSP splitting (by 27) enabled\n" );
+			bspAlternateSplitWeights = qtrue;
+		}
+		else if( !strcmp( argv[ i ], "-deep" ) )
+		{
+			Sys_Printf( "Deep BSP tree generation enabled\n" );
+			deepBSP = qtrue;
+		}
+		else if( !strcmp( argv[ i ], "-maxarea" ) )
+		{
+			Sys_Printf( "Max Area face surface generation enabled\n" );
+			maxAreaFaceSurface = 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 */
@@ -903,9 +955,12 @@ int BSPMain( int argc, char **argv )
 	
 	/* 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 );
+		LoadMapFile( tempSource, qfalse, qfalse );
 	else
-		LoadMapFile( name, qfalse );
+		LoadMapFile( name, qfalse, qfalse );
+	
+	/* div0: inject command line parameters */
+	InjectCommandLine(argv, 1, argc - 1);
 	
 	/* ydnar: decal setup */
 	ProcessDecals();
@@ -923,7 +978,7 @@ int BSPMain( int argc, char **argv )
 	ProcessAdvertisements();
 
 	/* finish and write bsp */
-	EndBSPFile();
+	EndBSPFile(qtrue);
 	
 	/* remove temp map source file if appropriate */
 	if( strlen( tempSource ) > 0)
diff --git a/tools/quake3/q3map2/bspfile_abstract.c b/tools/quake3/q3map2/bspfile_abstract.c
index 31b6f171..ef78160f 100644
--- a/tools/quake3/q3map2/bspfile_abstract.c
+++ b/tools/quake3/q3map2/bspfile_abstract.c
@@ -64,7 +64,7 @@ void IncDrawVerts()
 
 	if(bspDrawVerts == 0)
 	{
-		numBSPDrawVertsBuffer = MAX_MAP_DRAW_VERTS / 37;
+		numBSPDrawVertsBuffer = 1024;
 		
 		bspDrawVerts = safe_malloc_info(sizeof(bspDrawVert_t) * numBSPDrawVertsBuffer, "IncDrawVerts");
 
@@ -74,9 +74,6 @@ void IncDrawVerts()
 		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)
@@ -332,6 +329,13 @@ int CopyLump( bspHeader_t *header, int lump, void *dest, int size )
 	return length / size;
 }
 
+int CopyLump_Allocate( bspHeader_t *header, int lump, void **dest, int size, int *allocationVariable )
+{
+	/* get lump length and offset */
+	*allocationVariable = header->lumps[ lump ].length / size;
+	*dest = realloc(*dest, size * *allocationVariable);
+	return CopyLump(header, lump, *dest, size);
+}
 
 
 /*
@@ -581,7 +585,44 @@ void ParseEntities( void )
 	numBSPEntities = numEntities;
 }
 
+/*
+ * must be called before UnparseEntities
+ */
+void InjectCommandLine(char **argv, int beginArgs, int endArgs)
+{
+	const char *previousCommandLine;
+	char newCommandLine[1024];
+	const char *inpos;
+	char *outpos = newCommandLine;
+	char *sentinel = newCommandLine + sizeof(newCommandLine) - 1;
+	int i;
+
+	previousCommandLine = ValueForKey(&entities[0], "_q3map2_cmdline");
+	if(previousCommandLine && *previousCommandLine)
+	{
+		inpos = previousCommandLine;
+		while(outpos != sentinel && *inpos)
+			*outpos++ = *inpos++;
+		if(outpos != sentinel)
+			*outpos++ = ';';
+		if(outpos != sentinel)
+			*outpos++ = ' ';
+	}
 
+	for(i = beginArgs; i < endArgs; ++i)
+	{
+		if(outpos != sentinel && i != beginArgs)
+			*outpos++ = ' ';
+		inpos = argv[i];
+		while(outpos != sentinel && *inpos)
+			if(*inpos != '\\' && *inpos != '"' && *inpos != ';' && (unsigned char) *inpos >= ' ')
+				*outpos++ = *inpos++;
+	}
+
+	*outpos = 0;
+	SetKeyValue(&entities[0], "_q3map2_cmdline", newCommandLine);
+	SetKeyValue(&entities[0], "_q3map2_version", Q3MAP_VERSION);
+}
 
 /*
 UnparseEntities()
@@ -601,13 +642,22 @@ void UnparseEntities( void )
 	
 	
 	/* setup */
+	AUTOEXPAND_BY_REALLOC(bspEntData, 0, allocatedBSPEntData, 1024);
 	buf = bspEntData;
 	end = buf;
 	*end = 0;
+
 	
 	/* run through entity list */
 	for( i = 0; i < numBSPEntities && i < numEntities; i++ )
 	{
+		{
+			int sz = end - buf;
+			AUTOEXPAND_BY_REALLOC(bspEntData, sz + 65536, allocatedBSPEntData, 1024);
+			buf = bspEntData;
+			end = buf + sz;
+		}
+
 		/* get epair */
 		ep = entities[ i ].epairs;
 		if( ep == NULL )
@@ -644,7 +694,7 @@ void UnparseEntities( void )
 		end += 2;
 		
 		/* check for overflow */
-		if( end > buf + MAX_MAP_ENTSTRING )
+		if( end > buf + allocatedBSPEntData )
 			Error( "Entity text too long" );
 	}
 	
@@ -701,7 +751,25 @@ void SetKeyValue( entity_t *ent, const char *key, const char *value )
 	ep->value = copystring( value );
 }
 
+/*
+KeyExists()
+returns true if entity has this key
+*/
+
+qboolean KeyExists( const entity_t *ent, const char *key )
+{
+	epair_t	*ep;
+	
+	/* walk epair list */
+	for( ep = ent->epairs; ep != NULL; ep = ep->next )
+	{
+		if( !EPAIR_STRCMP( ep->key, key ) )
+			return qtrue;
+	}
 
+	/* no match */
+	return qfalse;
+}
 
 /*
 ValueForKey()
@@ -821,7 +889,6 @@ void GetEntityShadowFlags( const entity_t *ent, const entity_t *ent2, int *castS
 {
 	const char	*value;
 	
-	
 	/* get cast shadows */
 	if( castShadows != NULL )
 	{
@@ -849,5 +916,19 @@ void GetEntityShadowFlags( const entity_t *ent, const entity_t *ent2, int *castS
 		if( value[ 0 ] != '\0' )
 			*recvShadows = atoi( value );
 	}
+
+	/* vortex: game-specific default eneity keys */
+	value = ValueForKey( ent, "classname" );
+	if (!Q_stricmp( game->magic, "dq" ) || !Q_stricmp( game->magic, "prophecy" ) )
+	{
+		/* vortex: deluxe quake default shadow flags */
+		if (!Q_stricmp( value, "func_wall" ) )
+		{
+			if( recvShadows != NULL )
+				*recvShadows = 1;
+			if( castShadows != NULL )
+				*castShadows = 1;
+		}
+	}
 }
 
diff --git a/tools/quake3/q3map2/bspfile_ibsp.c b/tools/quake3/q3map2/bspfile_ibsp.c
index 26fc56dc..77ffd704 100644
--- a/tools/quake3/q3map2/bspfile_ibsp.c
+++ b/tools/quake3/q3map2/bspfile_ibsp.c
@@ -101,14 +101,14 @@ static void CopyBrushSidesLump( ibspHeader_t *header )
 	
 	/* copy */
 	in = GetLump( (bspHeader_t*) header, LUMP_BRUSHSIDES );
-	out = bspBrushSides;
 	for( i = 0; i < numBSPBrushSides; i++ )
 	{
+		AUTOEXPAND_BY_REALLOC(bspBrushSides, i, allocatedBSPBrushSides, 1024);
+		out = &bspBrushSides[i];
 		out->planeNum = in->planeNum;
 		out->shaderNum = in->shaderNum;
 		out->surfaceNum = -1;
 		in++;
-		out++;
 	}
 }
 
@@ -477,21 +477,21 @@ void LoadIBSPFile( const char *filename )
 		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 ) );
+	numBSPShaders = CopyLump_Allocate( (bspHeader_t*) header, LUMP_SHADERS, (void **) &bspShaders, sizeof( bspShader_t ), &allocatedBSPShaders );
 	
-	numBSPModels = CopyLump( (bspHeader_t*) header, LUMP_MODELS, bspModels, sizeof( bspModel_t ) );
+	numBSPModels = CopyLump_Allocate( (bspHeader_t*) header, LUMP_MODELS, (void **) &bspModels, sizeof( bspModel_t ), &allocatedBSPModels );
 	
-	numBSPPlanes = CopyLump( (bspHeader_t*) header, LUMP_PLANES, bspPlanes, sizeof( bspPlane_t ) );
+	numBSPPlanes = CopyLump_Allocate( (bspHeader_t*) header, LUMP_PLANES, (void **) &bspPlanes, sizeof( bspPlane_t ), &allocatedBSPPlanes );
 	
-	numBSPLeafs = CopyLump( (bspHeader_t*) header, LUMP_LEAFS, bspLeafs, sizeof( bspLeaf_t ) );
+	numBSPLeafs = CopyLump( (bspHeader_t*) header, LUMP_LEAFS, bspLeafs, sizeof( bspLeaf_t ) ); // TODO fix overflow
 	
-	numBSPNodes = CopyLump( (bspHeader_t*) header, LUMP_NODES, bspNodes, sizeof( bspNode_t ) );
+	numBSPNodes = CopyLump_Allocate( (bspHeader_t*) header, LUMP_NODES, (void **) &bspNodes, sizeof( bspNode_t ), &allocatedBSPNodes );
 	
-	numBSPLeafSurfaces = CopyLump( (bspHeader_t*) header, LUMP_LEAFSURFACES, bspLeafSurfaces, sizeof( bspLeafSurfaces[ 0 ] ) );
+	numBSPLeafSurfaces = CopyLump_Allocate( (bspHeader_t*) header, LUMP_LEAFSURFACES, (void **) &bspLeafSurfaces, sizeof( bspLeafSurfaces[ 0 ] ), &allocatedBSPLeafSurfaces );
 	
-	numBSPLeafBrushes = CopyLump( (bspHeader_t*) header, LUMP_LEAFBRUSHES, bspLeafBrushes, sizeof( bspLeafBrushes[ 0 ] ) );
+	numBSPLeafBrushes = CopyLump_Allocate( (bspHeader_t*) header, LUMP_LEAFBRUSHES, (void **) &bspLeafBrushes, sizeof( bspLeafBrushes[ 0 ] ), &allocatedBSPLeafBrushes );
 	
-	numBSPBrushes = CopyLump( (bspHeader_t*) header, LUMP_BRUSHES, bspBrushes, sizeof( bspBrush_t ) );
+	numBSPBrushes = CopyLump_Allocate( (bspHeader_t*) header, LUMP_BRUSHES, (void **) &bspBrushes, sizeof( bspBrush_t ), &allocatedBSPLeafBrushes );
 	
 	CopyBrushSidesLump( header );
 
@@ -499,17 +499,17 @@ void LoadIBSPFile( const char *filename )
 	
 	CopyDrawSurfacesLump( header );
 	
-	numBSPFogs = CopyLump( (bspHeader_t*) header, LUMP_FOGS, bspFogs, sizeof( bspFog_t ) );
+	numBSPFogs = CopyLump( (bspHeader_t*) header, LUMP_FOGS, bspFogs, sizeof( bspFog_t ) ); // TODO fix overflow
 	
 	numBSPDrawIndexes = CopyLump( (bspHeader_t*) header, LUMP_DRAWINDEXES, bspDrawIndexes, sizeof( bspDrawIndexes[ 0 ] ) );
 	
-	numBSPVisBytes = CopyLump( (bspHeader_t*) header, LUMP_VISIBILITY, bspVisBytes, 1 );
+	numBSPVisBytes = CopyLump( (bspHeader_t*) header, LUMP_VISIBILITY, bspVisBytes, 1 ); // TODO fix overflow
 	
-	numBSPLightBytes = GetLumpElements( (bspHeader_t*) header, LUMP_LIGHTMAPS, 1 );
+	numBSPLightBytes = GetLumpElements( (bspHeader_t*) header, LUMP_LIGHTMAPS, 1 ); // TODO change to CopyLump_Allocate
 		bspLightBytes = safe_malloc( numBSPLightBytes );
 		CopyLump( (bspHeader_t*) header, LUMP_LIGHTMAPS, bspLightBytes, 1 );
 	
-	bspEntDataSize = CopyLump( (bspHeader_t*) header, LUMP_ENTITIES, bspEntData, 1);
+	bspEntDataSize = CopyLump_Allocate( (bspHeader_t*) header, LUMP_ENTITIES, (void **) &bspEntData, 1, &allocatedBSPEntData);
 	
 	CopyLightGridLumps( header );
 
diff --git a/tools/quake3/q3map2/bspfile_rbsp.c b/tools/quake3/q3map2/bspfile_rbsp.c
index 74d77ae9..79f177ea 100644
--- a/tools/quake3/q3map2/bspfile_rbsp.c
+++ b/tools/quake3/q3map2/bspfile_rbsp.c
@@ -229,23 +229,23 @@ void LoadRBSPFile( const char *filename )
 		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 ) );
+	numBSPShaders = CopyLump_Allocate( (bspHeader_t*) header, LUMP_SHADERS, (void **) &bspShaders, sizeof( bspShader_t ), &allocatedBSPShaders );
 	
-	numBSPModels = CopyLump( (bspHeader_t*) header, LUMP_MODELS, bspModels, sizeof( bspModel_t ) );
+	numBSPModels = CopyLump_Allocate( (bspHeader_t*) header, LUMP_MODELS, (void **) &bspModels, sizeof( bspModel_t ), &allocatedBSPModels );
 	
-	numBSPPlanes = CopyLump( (bspHeader_t*) header, LUMP_PLANES, bspPlanes, sizeof( bspPlane_t ) );
+	numBSPPlanes = CopyLump_Allocate( (bspHeader_t*) header, LUMP_PLANES, (void **) &bspPlanes, sizeof( bspPlane_t ), &allocatedBSPPlanes );
 	
 	numBSPLeafs = CopyLump( (bspHeader_t*) header, LUMP_LEAFS, bspLeafs, sizeof( bspLeaf_t ) );
 	
-	numBSPNodes = CopyLump( (bspHeader_t*) header, LUMP_NODES, bspNodes, sizeof( bspNode_t ) );
+	numBSPNodes = CopyLump_Allocate( (bspHeader_t*) header, LUMP_NODES, (void **) &bspNodes, sizeof( bspNode_t ), &allocatedBSPNodes );
 	
-	numBSPLeafSurfaces = CopyLump( (bspHeader_t*) header, LUMP_LEAFSURFACES, bspLeafSurfaces, sizeof( bspLeafSurfaces[ 0 ] ) );
+	numBSPLeafSurfaces = CopyLump_Allocate( (bspHeader_t*) header, LUMP_LEAFSURFACES, (void **) &bspLeafSurfaces, sizeof( bspLeafSurfaces[ 0 ] ), &allocatedBSPLeafSurfaces );
 	
-	numBSPLeafBrushes = CopyLump( (bspHeader_t*) header, LUMP_LEAFBRUSHES, bspLeafBrushes, sizeof( bspLeafBrushes[ 0 ] ) );
+	numBSPLeafBrushes = CopyLump_Allocate( (bspHeader_t*) header, LUMP_LEAFBRUSHES, (void **) &bspLeafBrushes, sizeof( bspLeafBrushes[ 0 ] ), &allocatedBSPLeafBrushes );
 	
-	numBSPBrushes = CopyLump( (bspHeader_t*) header, LUMP_BRUSHES, bspBrushes, sizeof( bspBrush_t ) );
+	numBSPBrushes = CopyLump_Allocate( (bspHeader_t*) header, LUMP_BRUSHES, (void **) &bspBrushes, sizeof( bspBrush_t ), &allocatedBSPLeafBrushes );
 	
-	numBSPBrushSides = CopyLump( (bspHeader_t*) header, LUMP_BRUSHSIDES, bspBrushSides, sizeof( bspBrushSide_t ) );
+	numBSPBrushSides = CopyLump_Allocate( (bspHeader_t*) header, LUMP_BRUSHSIDES, (void **) &bspBrushSides, sizeof( bspBrushSide_t ), &allocatedBSPBrushSides );
 	
 	numBSPDrawVerts = GetLumpElements( (bspHeader_t*) header, LUMP_DRAWVERTS, sizeof( bspDrawVerts[ 0 ] ) );
 		SetDrawVerts( numBSPDrawVerts );
@@ -265,7 +265,7 @@ void LoadRBSPFile( const char *filename )
 		bspLightBytes = safe_malloc( numBSPLightBytes );
 		CopyLump( (bspHeader_t*) header, LUMP_LIGHTMAPS, bspLightBytes, 1 );
 	
-	bspEntDataSize = CopyLump( (bspHeader_t*) header, LUMP_ENTITIES, bspEntData, 1);
+	bspEntDataSize = CopyLump_Allocate( (bspHeader_t*) header, LUMP_ENTITIES, (void **) &bspEntData, 1, &allocatedBSPEntData);
 	
 	CopyLightGridLumps( header );
 	
diff --git a/tools/quake3/q3map2/convert_ase.c b/tools/quake3/q3map2/convert_ase.c
index 2be25a43..56262311 100644
--- a/tools/quake3/q3map2/convert_ase.c
+++ b/tools/quake3/q3map2/convert_ase.c
@@ -43,6 +43,7 @@ ConvertSurface()
 converts a bsp drawsurface to an ase chunk
 */
 
+int numLightmapsASE = 0;
 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;
@@ -162,7 +163,15 @@ static void ConvertSurface( FILE *f, bspModel_t *model, int modelNum, bspDrawSur
 	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 );
+	if(lightmapsAsTexcoord)
+	{
+		if(ds->lightmapNum[0] >= 0 && ds->lightmapNum[0] + deluxemap < numLightmapsASE)
+			fprintf( f, "\t*MATERIAL_REF\t%d\r\n", ds->lightmapNum[0] + deluxemap );
+		else
+			Sys_Printf( "WARNING: lightmap %d out of range, not exporting\n", ds->lightmapNum[0] + deluxemap );
+	}
+	else
+		fprintf( f, "\t*MATERIAL_REF\t%d\r\n", ds->shaderNum );
 	fprintf( f, "}\r\n" );
 }
 
@@ -271,12 +280,40 @@ static void ConvertShader( FILE *f, bspShader_t *shader, int shaderNum )
 	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 );
+	if(shadersAsBitmap)
+		fprintf( f, "\t\t\t*BITMAP\t\"%s\"\r\n", shader->shader );
+	else
+		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" );
 }
+static void ConvertLightmap( FILE *f, const char *base, int lightmapNum )
+{
+	/* print shader info */
+	fprintf( f, "\t*MATERIAL\t%d\t{\r\n", lightmapNum );
+	fprintf( f, "\t\t*MATERIAL_NAME\t\"lm_%04d\"\r\n", lightmapNum );
+	fprintf( f, "\t\t*MATERIAL_CLASS\t\"Standard\"\r\n" );
+	fprintf( f, "\t\t*MATERIAL_DIFFUSE\t1\t1\t1\r\n");
+	fprintf( f, "\t\t*MATERIAL_SHADING Phong\r\n" );
+	
+	/* print map info */
+	if(lightmapNum >= 0)
+	{
+		fprintf( f, "\t\t*MAP_DIFFUSE\t{\r\n" );
+		fprintf( f, "\t\t\t*MAP_NAME\t\"lm_%04d\"\r\n", lightmapNum );
+		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\\lm_%04d.tga\"\r\n", base, lightmapNum );
+		fprintf( f, "\t\t\t*BITMAP_FILTER\tPyramidal\r\n" );
+		fprintf( f, "\t\t}\r\n" );
+	}
+	
+	fprintf( f, "\t}\r\n" );
+}
 
 
 
@@ -294,20 +331,21 @@ int ConvertBSPToASE( char *bspName )
 	entity_t		*e;
 	vec3_t			origin;
 	const char		*key;
-	char			name[ 1024 ], base[ 1024 ];
+	char			name[ 1024 ], base[ 1024 ], dirname[ 1024 ];
 	
 	
 	/* note it */
 	Sys_Printf( "--- Convert BSP to ASE ---\n" );
 
 	/* create the ase filename from the bsp name */
+	strcpy( dirname, bspName );
+	StripExtension( dirname );
 	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" );
@@ -318,7 +356,7 @@ int ConvertBSPToASE( char *bspName )
 	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_FILENAME\t\"%s.bsp\"\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" );
@@ -329,11 +367,35 @@ int ConvertBSPToASE( char *bspName )
 	
 	/* 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++ )
+	if(lightmapsAsTexcoord)
 	{
-		shader = &bspShaders[ i ];
-		ConvertShader( f, shader, i );
+		int lightmapCount;
+		for( lightmapCount = 0; lightmapCount < numBSPLightmaps; lightmapCount++ )
+			;
+		for( ; ; lightmapCount++ )
+		{
+			char buf[1024];
+			FILE *tmp;
+			snprintf(buf, sizeof(buf), "%s/lm_%04d.tga", dirname, lightmapCount);
+			buf[sizeof(buf) - 1] = 0;
+			tmp = fopen(buf, "rb");
+			if(!tmp)
+				break;
+			fclose(tmp);
+		}
+		fprintf( f, "\t*MATERIAL_COUNT\t%d\r\n", lightmapCount );
+		for( i = 0; i < lightmapCount; i++ )
+			ConvertLightmap( f, base, i );
+		numLightmapsASE = lightmapCount;
+	}
+	else
+	{
+		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" );
 	
diff --git a/tools/quake3/q3map2/convert_map.c b/tools/quake3/q3map2/convert_map.c
index 1fabbc63..0f37665d 100644
--- a/tools/quake3/q3map2/convert_map.c
+++ b/tools/quake3/q3map2/convert_map.c
@@ -46,7 +46,169 @@ 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 )
+typedef vec_t vec2_t[2];
+
+static vec_t Det3x3(vec_t a00, vec_t a01, vec_t a02,
+                    vec_t a10, vec_t a11, vec_t a12,
+                    vec_t a20, vec_t a21, vec_t a22)
+{
+	return
+		a00 * (a11 * a22 - a12 * a21)
+	-	a01 * (a10 * a22 - a12 * a20)
+	+	a02 * (a10 * a21 - a11 * a20);
+}
+
+void GetBestSurfaceTriangleMatchForBrushside(side_t *buildSide, bspDrawVert_t *bestVert[3])
+{
+	bspDrawSurface_t *s;
+	int i;
+	int t;
+	vec_t best = 0;
+	vec_t thisarea;
+	vec3_t normdiff;
+	vec3_t v1v0, v2v0, norm;
+	bspDrawVert_t *vert[3];
+	winding_t *polygon;
+	plane_t *buildPlane = &mapplanes[buildSide->planenum];
+	int matches = 0;
+
+	// first, start out with NULLs
+	bestVert[0] = bestVert[1] = bestVert[2] = NULL;
+
+	// brute force through all surfaces
+	for(s = bspDrawSurfaces; s != bspDrawSurfaces + numBSPDrawSurfaces; ++s)
+	{
+		if(s->surfaceType != MST_PLANAR && s->surfaceType != MST_TRIANGLE_SOUP)
+			continue;
+		if(strcmp(buildSide->shaderInfo->shader, bspShaders[s->shaderNum].shader))
+			continue;
+		for(t = 0; t + 3 <= s->numIndexes; t += 3)
+		{
+			vert[0] = &bspDrawVerts[s->firstVert + bspDrawIndexes[s->firstIndex + t + 0]];
+			vert[1] = &bspDrawVerts[s->firstVert + bspDrawIndexes[s->firstIndex + t + 1]];
+			vert[2] = &bspDrawVerts[s->firstVert + bspDrawIndexes[s->firstIndex + t + 2]];
+			if(s->surfaceType == MST_PLANAR && VectorCompare(vert[0]->normal, vert[1]->normal) && VectorCompare(vert[1]->normal, vert[2]->normal))
+			{
+				VectorSubtract(vert[0]->normal, buildPlane->normal, normdiff); if(VectorLength(normdiff) >= normalEpsilon) continue;
+				VectorSubtract(vert[1]->normal, buildPlane->normal, normdiff); if(VectorLength(normdiff) >= normalEpsilon) continue;
+				VectorSubtract(vert[2]->normal, buildPlane->normal, normdiff); if(VectorLength(normdiff) >= normalEpsilon) continue;
+			}
+			else
+			{
+				// this is more prone to roundoff errors, but with embedded
+				// models, there is no better way
+				VectorSubtract(vert[1]->xyz, vert[0]->xyz, v1v0);
+				VectorSubtract(vert[2]->xyz, vert[0]->xyz, v2v0);
+				CrossProduct(v2v0, v1v0, norm);
+				VectorNormalize(norm, norm);
+				VectorSubtract(norm, buildPlane->normal, normdiff); if(VectorLength(normdiff) >= normalEpsilon) continue;
+			}
+			if(abs(DotProduct(vert[0]->xyz, buildPlane->normal) - buildPlane->dist) >= distanceEpsilon) continue;
+			if(abs(DotProduct(vert[1]->xyz, buildPlane->normal) - buildPlane->dist) >= distanceEpsilon) continue;
+			if(abs(DotProduct(vert[2]->xyz, buildPlane->normal) - buildPlane->dist) >= distanceEpsilon) continue;
+			// Okay. Correct surface type, correct shader, correct plane. Let's start with the business...
+			polygon = CopyWinding(buildSide->winding);
+			for(i = 0; i < 3; ++i)
+			{
+				// 0: 1, 2
+				// 1: 2, 0
+				// 2; 0, 1
+				vec3_t *v1 = &vert[(i+1)%3]->xyz;
+				vec3_t *v2 = &vert[(i+2)%3]->xyz;
+				vec3_t triNormal;
+				vec_t triDist;
+				vec3_t sideDirection;
+				// we now need to generate triNormal and triDist so that they represent the plane spanned by normal and (v2 - v1).
+				VectorSubtract(*v2, *v1, sideDirection);
+				CrossProduct(sideDirection, buildPlane->normal, triNormal);
+				triDist = DotProduct(*v1, triNormal);
+				ChopWindingInPlace(&polygon, triNormal, triDist, distanceEpsilon);
+				if(!polygon)
+					goto exwinding;
+			}
+			thisarea = WindingArea(polygon);
+			if(thisarea > 0)
+				++matches;
+			if(thisarea > best)
+			{
+				best = thisarea;
+				bestVert[0] = vert[0];
+				bestVert[1] = vert[1];
+				bestVert[2] = vert[2];
+			}
+			FreeWinding(polygon);
+exwinding:
+			;
+		}
+	}
+	//if(strncmp(buildSide->shaderInfo->shader, "textures/common/", 16))
+	//	fprintf(stderr, "brushside with %s: %d matches (%f area)\n", buildSide->shaderInfo->shader, matches, best);
+}
+
+#define FRAC(x) ((x) - floor(x))
+static void ConvertOriginBrush( FILE *f, int num, vec3_t origin, qboolean brushPrimitives )
+{
+	int originSize = 256;
+
+	char pattern[6][7][3] = {
+		{ "+++", "+-+", "-++", "-  ", " + ", " - ", "-  " },
+		{ "+++", "-++", "++-", "-  ", "  +", "+  ", "  +" },
+		{ "+++", "++-", "+-+", " - ", "  +", " - ", "  +" },
+		{ "---", "+--", "-+-", "-  ", " + ", " - ", "+  " },
+		{ "---", "--+", "+--", "-  ", "  +", "-  ", "  +" },
+		{ "---", "-+-", "--+", " - ", "  +", " + ", "  +" }
+	};
+	int i;
+#define S(a,b,c) (pattern[a][b][c] == '+' ? +1 : pattern[a][b][c] == '-' ? -1 : 0)
+
+	/* start brush */
+	fprintf( f, "\t// brush %d\n", num );
+	fprintf( f, "\t{\n" );
+	if(brushPrimitives)
+	{
+		fprintf( f, "\tbrushDef\n" );
+		fprintf( f, "\t{\n" );
+	}
+	/* print brush side */
+	/* ( 640 24 -224 ) ( 448 24 -224 ) ( 448 -232 -224 ) common/caulk 0 48 0 0.500000 0.500000 0 0 0 */
+
+	for(i = 0; i < 6; ++i)
+	{
+		if(brushPrimitives)
+		{
+			fprintf( f, "\t\t( %.3f %.3f %.3f ) ( %.3f %.3f %.3f ) ( %.3f %.3f %.3f ) ( ( %.8f %.8f %.8f ) ( %.8f %.8f %.8f ) ) %s %d 0 0\n",
+					origin[0] + 8 * S(i,0,0), origin[1] + 8 * S(i,0,1), origin[2] + 8 * S(i,0,2),
+					origin[0] + 8 * S(i,1,0), origin[1] + 8 * S(i,1,1), origin[2] + 8 * S(i,1,2),
+					origin[0] + 8 * S(i,2,0), origin[1] + 8 * S(i,2,1), origin[2] + 8 * S(i,2,2),
+					1.0f/16.0f, 0.0f, FRAC((S(i,5,0) * origin[0] + S(i,5,1) * origin[1] + S(i,5,2) * origin[2]) / 16.0 + 0.5),
+					0.0f, 1.0f/16.0f, FRAC((S(i,6,0) * origin[0] + S(i,6,1) * origin[1] + S(i,6,2) * origin[2]) / 16.0 + 0.5),
+					"common/origin",
+					0
+				   );
+		}
+		else
+		{
+			fprintf( f, "\t\t( %.3f %.3f %.3f ) ( %.3f %.3f %.3f ) ( %.3f %.3f %.3f ) %s %.8f %.8f %.8f %.8f %.8f %d 0 0\n",
+					origin[0] + 8 * S(i,0,0), origin[1] + 8 * S(i,0,1), origin[2] + 8 * S(i,0,2),
+					origin[0] + 8 * S(i,1,0), origin[1] + 8 * S(i,1,1), origin[2] + 8 * S(i,1,2),
+					origin[0] + 8 * S(i,2,0), origin[1] + 8 * S(i,2,1), origin[2] + 8 * S(i,2,2),
+					"common/origin",
+					FRAC((S(i,3,0) * origin[0] + S(i,3,1) * origin[1] + S(i,3,2) * origin[2]) / 16.0 + 0.5) * originSize,
+					FRAC((S(i,4,0) * origin[0] + S(i,4,1) * origin[1] + S(i,4,2) * origin[2]) / 16.0 + 0.5) * originSize,
+					0.0f, 16.0 / originSize, 16.0 / originSize,
+					0
+				   );
+		}
+	}
+#undef S
+	
+	/* end brush */
+	if(brushPrimitives)
+		fprintf( f, "\t}\n" );
+	fprintf( f, "\t}\n\n" );
+}
+
+static void ConvertBrush( FILE *f, int num, bspBrush_t *brush, vec3_t origin, qboolean brushPrimitives )
 {
 	int				i, j;
 	bspBrushSide_t	*side;
@@ -54,12 +216,20 @@ static void ConvertBrush( FILE *f, int num, bspBrush_t *brush, vec3_t origin )
 	bspShader_t		*shader;
 	char			*texture;
 	bspPlane_t		*plane;
+	plane_t         *buildPlane;
 	vec3_t			pts[ 3 ];
+	bspDrawVert_t	*vert[3];
+	int valid;
 	
 	
 	/* start brush */
 	fprintf( f, "\t// brush %d\n", num );
 	fprintf( f, "\t{\n" );
+	if(brushPrimitives)
+	{
+		fprintf( f, "\tbrushDef\n" );
+		fprintf( f, "\t{\n" );
+	}
 	
 	/* clear out build brush */
 	for( i = 0; i < buildBrush->numsides; i++ )
@@ -83,8 +253,8 @@ static void ConvertBrush( FILE *f, int num, bspBrush_t *brush, vec3_t origin )
 		if( side->shaderNum < 0 || side->shaderNum >= numBSPShaders )
 			continue;
 		shader = &bspShaders[ side->shaderNum ];
-		if( !Q_stricmp( shader->shader, "default" ) || !Q_stricmp( shader->shader, "noshader" ) )
-			continue;
+		//if( !Q_stricmp( shader->shader, "default" ) || !Q_stricmp( shader->shader, "noshader" ) )
+		//	continue;
 		
 		/* get plane */
 		plane = &bspPlanes[ side->planeNum ];
@@ -101,7 +271,10 @@ static void ConvertBrush( FILE *f, int num, bspBrush_t *brush, vec3_t origin )
 	
 	/* make brush windings */
 	if( !CreateBrushWindings( buildBrush ) )
+	{
+		Sys_Printf( "CreateBrushWindings failed\n" );
 		return;
+	}
 	
 	/* iterate through build brush sides */
 	for( i = 0; i < buildBrush->numsides; i++ )
@@ -109,10 +282,26 @@ static void ConvertBrush( FILE *f, int num, bspBrush_t *brush, vec3_t origin )
 		/* get build side */
 		buildSide = &buildBrush->sides[ i ];
 		
+		/* get plane */
+		buildPlane = &mapplanes[ buildSide->planenum ];
+
 		/* dummy check */
 		if( buildSide->shaderInfo == NULL || buildSide->winding == NULL )
 			continue;
-		
+
+		// st-texcoords -> texMat block
+		// start out with dummy
+		VectorSet(buildSide->texMat[0], 1/32.0, 0, 0);
+		VectorSet(buildSide->texMat[1], 0, 1/32.0, 0);
+
+		// find surface for this side (by brute force)
+		// surface format:
+		//   - meshverts point in pairs of three into verts
+		//   - (triangles)
+		//   - find the triangle that has most in common with our side
+		GetBestSurfaceTriangleMatchForBrushside(buildSide, vert);
+		valid = 0;
+
 		/* get texture name */
 		if( !Q_strncasecmp( buildSide->shaderInfo->shader, "textures/", 9 ) )
 			texture = buildSide->shaderInfo->shader + 9;
@@ -127,19 +316,228 @@ static void ConvertBrush( FILE *f, int num, bspBrush_t *brush, vec3_t origin )
 			//%	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 );
+
+		if(vert[0] && vert[1] && vert[2])
+		{
+			if(brushPrimitives)
+			{
+				int i;
+				vec3_t texX, texY;
+				vec2_t xyI, xyJ, xyK;
+				vec2_t stI, stJ, stK;
+				vec_t D, D0, D1, D2;
+
+				ComputeAxisBase(buildPlane->normal, texX, texY);
+
+				xyI[0] = DotProduct(vert[0]->xyz, texX);
+				xyI[1] = DotProduct(vert[0]->xyz, texY);
+				xyJ[0] = DotProduct(vert[1]->xyz, texX);
+				xyJ[1] = DotProduct(vert[1]->xyz, texY);
+				xyK[0] = DotProduct(vert[2]->xyz, texX);
+				xyK[1] = DotProduct(vert[2]->xyz, texY);
+				stI[0] = vert[0]->st[0]; stI[1] = vert[0]->st[1];
+				stJ[0] = vert[1]->st[0]; stJ[1] = vert[1]->st[1];
+				stK[0] = vert[2]->st[0]; stK[1] = vert[2]->st[1];
+
+				//   - solve linear equations:
+				//     - (x, y) := xyz . (texX, texY)
+				//     - st[i] = texMat[i][0]*x + texMat[i][1]*y + texMat[i][2]
+				//       (for three vertices)
+				D = Det3x3(
+					xyI[0], xyI[1], 1,
+					xyJ[0], xyJ[1], 1,
+					xyK[0], xyK[1], 1
+				);
+				if(D != 0)
+				{
+					for(i = 0; i < 2; ++i)
+					{
+						D0 = Det3x3(
+							stI[i], xyI[1], 1,
+							stJ[i], xyJ[1], 1,
+							stK[i], xyK[1], 1
+						);
+						D1 = Det3x3(
+							xyI[0], stI[i], 1,
+							xyJ[0], stJ[i], 1,
+							xyK[0], stK[i], 1
+						);
+						D2 = Det3x3(
+							xyI[0], xyI[1], stI[i],
+							xyJ[0], xyJ[1], stJ[i],
+							xyK[0], xyK[1], stK[i]
+						);
+						VectorSet(buildSide->texMat[i], D0 / D, D1 / D, D2 / D);
+					}
+					valid = 1;
+				}
+				else
+					fprintf(stderr, "degenerate triangle found when solving texMat equations for\n(%f %f %f) (%f %f %f) (%f %f %f)\n( %f %f %f )\n( %f %f %f ) -> ( %f %f )\n( %f %f %f ) -> ( %f %f )\n( %f %f %f ) -> ( %f %f )\n",
+						buildPlane->normal[0], buildPlane->normal[1], buildPlane->normal[2],
+						vert[0]->normal[0], vert[0]->normal[1], vert[0]->normal[2],
+						texX[0], texX[1], texX[2], texY[0], texY[1], texY[2],
+						vert[0]->xyz[0], vert[0]->xyz[1], vert[0]->xyz[2], xyI[0], xyI[1],
+						vert[1]->xyz[0], vert[1]->xyz[1], vert[1]->xyz[2], xyJ[0], xyJ[1],
+						vert[2]->xyz[0], vert[2]->xyz[1], vert[2]->xyz[2], xyK[0], xyK[1]
+						);
+
+				/* 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 ) ( ( %.8f %.8f %.8f ) ( %.8f %.8f %.8f ) ) %s %d 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 ],
+						buildSide->texMat[0][0], buildSide->texMat[0][1], FRAC(buildSide->texMat[0][2]),
+						buildSide->texMat[1][0], buildSide->texMat[1][1], FRAC(buildSide->texMat[1][2]),
+						texture,
+						// DEBUG: valid ? 0 : C_DETAIL
+						0
+					   );
+			}
+			else
+			{
+				// invert QuakeTextureVecs
+				int i;
+				vec3_t vecs[2];
+				int sv, tv;
+				vec2_t stI, stJ, stK;
+				vec3_t sts[2];
+				vec2_t shift, scale;
+				vec_t rotate;
+				vec_t D, D0, D1, D2;
+
+				TextureAxisFromPlane(buildPlane, vecs[0], vecs[1]);
+				if (vecs[0][0])
+					sv = 0;
+				else if (vecs[0][1])
+					sv = 1;
+				else
+					sv = 2;
+				if (vecs[1][0])
+					tv = 0;
+				else if (vecs[1][1])
+					tv = 1;
+				else
+					tv = 2;
+
+				stI[0] = vert[0]->st[0] * buildSide->shaderInfo->shaderWidth; stI[1] = vert[0]->st[1] * buildSide->shaderInfo->shaderHeight;
+				stJ[0] = vert[1]->st[0] * buildSide->shaderInfo->shaderWidth; stJ[1] = vert[1]->st[1] * buildSide->shaderInfo->shaderHeight;
+				stK[0] = vert[2]->st[0] * buildSide->shaderInfo->shaderWidth; stK[1] = vert[2]->st[1] * buildSide->shaderInfo->shaderHeight;
+
+				D = Det3x3(
+					vert[0]->xyz[sv], vert[0]->xyz[tv], 1,
+					vert[1]->xyz[sv], vert[1]->xyz[tv], 1,
+					vert[2]->xyz[sv], vert[2]->xyz[tv], 1
+				);
+				if(D != 0)
+				{
+					for(i = 0; i < 2; ++i)
+					{
+						D0 = Det3x3(
+							stI[i], vert[0]->xyz[tv], 1,
+							stJ[i], vert[1]->xyz[tv], 1,
+							stK[i], vert[2]->xyz[tv], 1
+						);
+						D1 = Det3x3(
+							vert[0]->xyz[sv], stI[i], 1,
+							vert[1]->xyz[sv], stJ[i], 1,
+							vert[2]->xyz[sv], stK[i], 1
+						);
+						D2 = Det3x3(
+							vert[0]->xyz[sv], vert[0]->xyz[tv], stI[i],
+							vert[1]->xyz[sv], vert[1]->xyz[tv], stJ[i],
+							vert[2]->xyz[sv], vert[2]->xyz[tv], stK[i]
+						);
+						VectorSet(sts[i], D0 / D, D1 / D, D2 / D);
+					}
+					valid = 1;
+				}
+				else
+					fprintf(stderr, "degenerate triangle found when solving texDef equations\n"); // FIXME add stuff here
+
+				// now we must solve:
+					//	// now we must invert:
+					//	ang = rotate / 180 * Q_PI;
+					//	sinv = sin(ang);
+					//	cosv = cos(ang);
+					//	ns = cosv * vecs[0][sv];
+					//	nt = sinv * vecs[0][sv];
+					//	vecsrotscaled[0][sv] = ns / scale[0];
+					//	vecsrotscaled[0][tv] = nt / scale[0];
+					//	ns = -sinv * vecs[1][tv];
+					//	nt =  cosv * vecs[1][tv];
+					//	vecsrotscaled[1][sv] = ns / scale[1];
+					//	vecsrotscaled[1][tv] = nt / scale[1];
+				scale[0] = 1.0/sqrt(sts[0][0] * sts[0][0] + sts[0][1] * sts[0][1]);
+				scale[1] = 1.0/sqrt(sts[1][0] * sts[1][0] + sts[1][1] * sts[1][1]);
+				rotate = atan2(sts[0][1] * vecs[0][sv] - sts[1][0] * vecs[1][tv], sts[0][0] * vecs[0][sv] + sts[1][1] * vecs[1][tv]) * (180.0f / Q_PI);
+				shift[0] = buildSide->shaderInfo->shaderWidth * FRAC(sts[0][2] / buildSide->shaderInfo->shaderWidth);
+				shift[1] = buildSide->shaderInfo->shaderHeight * FRAC(sts[1][2] / buildSide->shaderInfo->shaderHeight);
+
+				/* 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 %.8f %.8f %.8f %.8f %.8f %d 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,
+						shift[0], shift[1], rotate, scale[0], scale[1],
+						// DEBUG: valid ? 0 : C_DETAIL
+						0
+					   );
+			}
+		}
+		else
+		{
+			vec3_t	vecs[ 2 ];
+			if(strncmp(buildSide->shaderInfo->shader, "textures/common/", 16))
+			if(strcmp(buildSide->shaderInfo->shader, "noshader"))
+			if(strcmp(buildSide->shaderInfo->shader, "default"))
+			{
+				fprintf(stderr, "no matching triangle for brushside using %s (hopefully nobody can see this side anyway)\n", buildSide->shaderInfo->shader);
+				texture = "common/WTF";
+			}
+
+			MakeNormalVectors( buildPlane->normal, vecs[ 0 ], vecs[ 1 ] );
+			VectorMA( vec3_origin, buildPlane->dist, buildPlane->normal, pts[ 0 ] );
+			VectorMA( pts[ 0 ], 256.0f, vecs[ 0 ], pts[ 1 ] );
+			VectorMA( pts[ 0 ], 256.0f, vecs[ 1 ], pts[ 2 ] );
+			if(brushPrimitives)
+			{
+				fprintf( f, "\t\t( %.3f %.3f %.3f ) ( %.3f %.3f %.3f ) ( %.3f %.3f %.3f ) ( ( %.8f %.8f %.8f ) ( %.8f %.8f %.8f ) ) %s %d 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 ],
+						1.0f/16.0f, 0.0f, 0.0f,
+						0.0f, 1.0f/16.0f, 0.0f,
+						texture,
+						// DEBUG: valid ? 0 : C_DETAIL
+						0
+					   );
+			}
+			else
+			{
+				fprintf( f, "\t\t( %.3f %.3f %.3f ) ( %.3f %.3f %.3f ) ( %.3f %.3f %.3f ) %s %.8f %.8f %.8f %.8f %.8f %d 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,
+						0.0f, 0.0f, 0.0f, 0.25f, 0.25f,
+						// DEBUG: valid ? 0 : C_DETAIL
+						0
+					   );
+			}
+		}
 	}
 	
 	/* end brush */
+	if(brushPrimitives)
+	{
+		fprintf( f, "\t}\n" );
+	}
 	fprintf( f, "\t}\n\n" );
 }
+#undef FRAC
 
 #if 0
 	/* iterate through the brush sides (ignore the first 6 bevel planes) */
@@ -278,7 +676,7 @@ ConvertModel()
 exports a bsp model to a map file
 */
 
-static void ConvertModel( FILE *f, bspModel_t *model, int modelNum, vec3_t origin )
+static void ConvertModel( FILE *f, bspModel_t *model, int modelNum, vec3_t origin, qboolean brushPrimitives )
 {
 	int					i, num;
 	bspBrush_t			*brush;
@@ -287,25 +685,29 @@ static void ConvertModel( FILE *f, bspModel_t *model, int modelNum, vec3_t origi
 	
 	/* convert bsp planes to map planes */
 	nummapplanes = numBSPPlanes;
+	AUTOEXPAND_BY_REALLOC(mapplanes, nummapplanes, allocatedmapplanes, 1024);
 	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;
+		mapplanes[ i ].hash_chain = 0;
 	}
 	
 	/* allocate a build brush */
 	buildBrush = AllocBrush( 512 );
 	buildBrush->entityNum = 0;
 	buildBrush->original = buildBrush;
+
+	if(origin[0] != 0 || origin[1] != 0 || origin[2] != 0)
+		ConvertOriginBrush(f, -1, origin, brushPrimitives);
 	
 	/* 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 );
+		ConvertBrush( f, num, brush, origin, brushPrimitives );
 	}
 	
 	/* free the build brush */
@@ -330,7 +732,7 @@ ConvertEPairs()
 exports entity key/value pairs to a map file
 */
 
-static void ConvertEPairs( FILE *f, entity_t *e )
+static void ConvertEPairs( FILE *f, entity_t *e, qboolean skip_origin )
 {
 	epair_t	*ep;
 	
@@ -346,6 +748,10 @@ static void ConvertEPairs( FILE *f, entity_t *e )
 		if( !Q_stricmp( ep->key, "model" ) && ep->value[ 0 ] == '*' )
 			continue;
 		
+		/* ignore origin keys if skip_origin is set */
+		if( skip_origin && !Q_stricmp( ep->key, "origin" ) )
+			continue;
+		
 		/* emit the epair */
 		fprintf( f, "\t\"%s\" \"%s\"\n", ep->key, ep->value );
 	}
@@ -358,7 +764,7 @@ ConvertBSPToMap()
 exports an quake map file from the bsp
 */
 
-int ConvertBSPToMap( char *bspName )
+int ConvertBSPToMap_Ext( char *bspName, qboolean brushPrimitives )
 {
 	int				i, modelNum;
 	FILE			*f;
@@ -399,10 +805,6 @@ int ConvertBSPToMap( char *bspName )
 		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;
@@ -415,6 +817,10 @@ int ConvertBSPToMap( char *bspName )
 				modelNum = -1;
 		}
 		
+		/* export keys */
+		ConvertEPairs( f, e, modelNum >= 0 );
+		fprintf( f, "\n" );
+		
 		/* only handle bsp models */
 		if( modelNum >= 0 )
 		{
@@ -429,7 +835,7 @@ int ConvertBSPToMap( char *bspName )
 				GetVectorForKey( e, "origin", origin );
 			
 			/* convert model */
-			ConvertModel( f, model, modelNum, origin );
+			ConvertModel( f, model, modelNum, origin, brushPrimitives );
 		}
 		
 		/* end entity */
@@ -442,3 +848,13 @@ int ConvertBSPToMap( char *bspName )
 	/* return to sender */
 	return 0;
 }
+
+int ConvertBSPToMap( char *bspName )
+{
+	return ConvertBSPToMap_Ext(bspName, qfalse);
+}
+
+int ConvertBSPToMap_BP( char *bspName )
+{
+	return ConvertBSPToMap_Ext(bspName, qtrue);
+}
diff --git a/tools/quake3/q3map2/convert_obj.c b/tools/quake3/q3map2/convert_obj.c
new file mode 100644
index 00000000..08e75e11
--- /dev/null
+++ b/tools/quake3/q3map2/convert_obj.c
@@ -0,0 +1,328 @@
+/* -------------------------------------------------------------------------------
+
+Copyright (C) 1999-2007 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 obj chunk
+*/
+
+int firstLightmap = 0;
+int lastLightmap = -1;
+static void ConvertLightmapToMTL( FILE *f, const char *base, int lightmapNum );
+
+int objVertexCount = 0;
+int objLastShaderNum = -1;
+static void ConvertSurfaceToOBJ( 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;
+	
+	/* ignore patches for now */
+	if( ds->surfaceType != MST_PLANAR && ds->surfaceType != MST_TRIANGLE_SOUP )
+		return;
+
+	fprintf(f, "g mat%dmodel%dsurf%d\r\n", ds->shaderNum, modelNum, surfaceNum);
+	switch( ds->surfaceType )
+	{
+		case MST_PLANAR:
+			fprintf( f, "# SURFACETYPE MST_PLANAR\r\n" );
+			break;
+		case MST_TRIANGLE_SOUP:
+			fprintf( f, "# SURFACETYPE MST_TRIANGLE_SOUP\r\n" );
+			break;
+	}
+
+	/* export shader */
+	if(lightmapsAsTexcoord)
+	{
+		if(objLastShaderNum != ds->lightmapNum[0])
+		{
+			fprintf(f, "usemtl lm_%04d\r\n", ds->lightmapNum[0] + deluxemap);
+			objLastShaderNum = ds->lightmapNum[0] + deluxemap;
+		}
+		if(ds->lightmapNum[0] + deluxemap < firstLightmap)
+		{
+			Sys_Printf( "WARNING: lightmap %d out of range (exporting anyway)\n", ds->lightmapNum[0] + deluxemap );
+			firstLightmap = ds->lightmapNum[0] + deluxemap;
+		}
+		if(ds->lightmapNum[0] > lastLightmap)
+		{
+			Sys_Printf( "WARNING: lightmap %d out of range (exporting anyway)\n", ds->lightmapNum[0] + deluxemap );
+			lastLightmap = ds->lightmapNum[0] + deluxemap;
+		}
+	}
+	else
+	{
+		if(objLastShaderNum != ds->shaderNum)
+		{
+			fprintf(f, "usemtl %s\r\n", bspShaders[ds->shaderNum].shader);
+			objLastShaderNum = ds->shaderNum;
+		}
+	}
+	
+	/* export vertex */
+	for( i = 0; i < ds->numVerts; i++ )
+	{
+		v = i + ds->firstVert;
+		dv = &bspDrawVerts[ v ];
+		fprintf(f, "# vertex %d\r\n", i + objVertexCount + 1);
+		fprintf(f, "v %f %f %f\r\n", dv->xyz[ 0 ], dv->xyz[ 1 ], dv->xyz[ 2 ]);
+		fprintf(f, "vn %f %f %f\r\n", dv->normal[ 0 ], dv->normal[ 1 ], dv->normal[ 2 ]);
+		if(lightmapsAsTexcoord)
+			fprintf(f, "vt %f %f\r\n", dv->lightmap[0][0], 1.0 - dv->lightmap[0][1]);
+		else
+			fprintf(f, "vt %f %f\r\n", dv->st[ 0 ], 1.0 - dv->st[ 1 ]);
+	}
+
+	/* export faces */
+	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, "f %d/%d/%d %d/%d/%d %d/%d/%d\r\n",
+			a + objVertexCount + 1, a + objVertexCount + 1, a + objVertexCount + 1, 
+			b + objVertexCount + 1, b + objVertexCount + 1, b + objVertexCount + 1, 
+			c + objVertexCount + 1, c + objVertexCount + 1, c + objVertexCount + 1
+		);
+	}
+
+	objVertexCount += ds->numVerts;
+}
+
+
+
+/*
+ConvertModel()
+exports a bsp model to an ase chunk
+*/
+
+static void ConvertModelToOBJ( 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 ];
+		ConvertSurfaceToOBJ( f, model, modelNum, ds, s, origin );
+	}
+}
+
+
+
+/*
+ConvertShader()
+exports a bsp shader to an ase chunk
+*/
+
+static void ConvertShaderToMTL( 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 );
+
+	/* blender hates this, so let's not do it
+	for( c = filename; *c != '\0'; c++ )
+		if( *c == '/' )
+			*c = '\\';
+	*/
+	
+	/* print shader info */
+	fprintf( f, "newmtl %s\r\n", shader->shader );
+	fprintf( f, "Kd %f %f %f\r\n", si->color[ 0 ], si->color[ 1 ], si->color[ 2 ] );
+	if(shadersAsBitmap)
+		fprintf( f, "map_Kd %s\r\n", shader->shader );
+	else
+	/* blender hates this, so let's not do it
+		fprintf( f, "map_Kd ..\\%s\r\n", filename );
+	*/
+		fprintf( f, "map_Kd ../%s\r\n", filename );
+}
+
+static void ConvertLightmapToMTL( FILE *f, const char *base, int lightmapNum )
+{
+	/* print shader info */
+	fprintf( f, "newmtl lm_%04d\r\n", lightmapNum );
+	if(lightmapNum >= 0)
+	/* blender hates this, so let's not do it
+		fprintf( f, "map_Kd %s\\lm_%04d.tga\r\n", base, lightmapNum );
+	*/
+		fprintf( f, "map_Kd %s/lm_%04d.tga\r\n", base, lightmapNum );
+}
+
+
+
+/*
+ConvertBSPToASE()
+exports an 3d studio ase file from the bsp
+*/
+
+int ConvertBSPToOBJ( char *bspName )
+{
+	int				i, modelNum;
+	FILE			*f, *fmtl;
+	bspShader_t		*shader;
+	bspModel_t		*model;
+	entity_t		*e;
+	vec3_t			origin;
+	const char		*key;
+	char			name[ 1024 ], base[ 1024 ], mtlname[ 1024 ], dirname[ 1024 ];
+	
+	
+	/* note it */
+	Sys_Printf( "--- Convert BSP to OBJ ---\n" );
+
+	/* create the ase filename from the bsp name */
+	strcpy( dirname, bspName );
+	StripExtension( dirname );
+	strcpy( name, bspName );
+	StripExtension( name );
+	strcat( name, ".obj" );
+	Sys_Printf( "writing %s\n", name );
+	strcpy( mtlname, bspName );
+	StripExtension( mtlname );
+	strcat( mtlname, ".mtl" );
+	Sys_Printf( "writing %s\n", mtlname );
+	
+	ExtractFileBase( bspName, base );
+	
+	/* open it */
+	f = fopen( name, "wb" );
+	if( f == NULL )
+		Error( "Open failed on %s\n", name );
+	fmtl = fopen( mtlname, "wb" );
+	if( fmtl == NULL )
+		Error( "Open failed on %s\n", mtlname );
+	
+	/* print header */
+	fprintf( f, "o %s\r\n", base );
+	fprintf( f, "# Generated by Q3Map2 (ydnar) -convert -format obj\r\n" );
+	fprintf( f, "mtllib %s.mtl\r\n", base );
+
+	fprintf( fmtl, "# Generated by Q3Map2 (ydnar) -convert -format obj\r\n" );
+	if(lightmapsAsTexcoord)
+	{
+		int lightmapCount;
+		for( lightmapCount = 0; lightmapCount < numBSPLightmaps; lightmapCount++ )
+			;
+		for( ; ; lightmapCount++ )
+		{
+			char buf[1024];
+			FILE *tmp;
+			snprintf(buf, sizeof(buf), "%s/lm_%04d.tga", dirname, lightmapCount);
+			buf[sizeof(buf) - 1] = 0;
+			tmp = fopen(buf, "rb");
+			if(!tmp)
+				break;
+			fclose(tmp);
+		}
+		lastLightmap = lightmapCount - 1;
+	}
+	else
+	{
+		for( i = 0; i < numBSPShaders; i++ )
+		{
+			shader = &bspShaders[ i ];
+			ConvertShaderToMTL( fmtl, shader, i );
+		}
+	}
+	
+	/* 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 */
+		ConvertModelToOBJ( f, model, modelNum, origin );
+	}
+	
+	if(lightmapsAsTexcoord)
+	{
+		for( i = firstLightmap; i <= lastLightmap; i++ )
+			ConvertLightmapToMTL( fmtl, base, i );
+	}
+
+	/* close the file and return */
+	fclose( f );
+	fclose( fmtl );
+	
+	/* return to sender */
+	return 0;
+}
+
+
+
diff --git a/tools/quake3/q3map2/decals.c b/tools/quake3/q3map2/decals.c
index f4de7cea..a130334f 100644
--- a/tools/quake3/q3map2/decals.c
+++ b/tools/quake3/q3map2/decals.c
@@ -626,6 +626,7 @@ static void ProjectDecalOntoWinding( decalProjector_t *dp, mapDrawSurface_t *ds,
 	ds2->shaderInfo = dp->si;
 	ds2->fogNum = ds->fogNum;	/* why was this -1? */
 	ds2->lightmapScale = ds->lightmapScale;
+	ds2->shadeAngleDegrees = ds->shadeAngleDegrees;
 	ds2->numVerts = w->numpoints;
 	ds2->verts = safe_malloc( ds2->numVerts * sizeof( *ds2->verts ) );
 	memset( ds2->verts, 0, ds2->numVerts * sizeof( *ds2->verts ) );
diff --git a/tools/quake3/q3map2/facebsp.c b/tools/quake3/q3map2/facebsp.c
index 270eefe2..9291f564 100644
--- a/tools/quake3/q3map2/facebsp.c
+++ b/tools/quake3/q3map2/facebsp.c
@@ -89,7 +89,10 @@ static void SelectSplitPlaneNum( node_t *node, face_t *list, int *splitPlaneNum,
 	vec3_t		normal;
 	float		dist;
 	int			planenum;
-	
+	float       sizeBias;
+
+	//int frontC,backC,splitsC,facingC;
+
 	
 	/* ydnar: set some defaults */
 	*splitPlaneNum = -1; /* leaf */
@@ -118,14 +121,16 @@ static void SelectSplitPlaneNum( node_t *node, face_t *list, int *splitPlaneNum,
 	bestValue = -99999;
 	bestSplit = list;
 	
-	for( split = list; split; split = split->next )
-		split->checked = qfalse;
+
+	// div0: this check causes detail/structural mixes
+	//for( split = list; split; split = split->next )
+	//	split->checked = qfalse;
 	
 	for( split = list; split; split = split->next )
 	{
-		if ( split->checked )
-			continue;
-		
+		//if ( split->checked )
+		//	continue;
+
 		plane = &mapplanes[ split->planenum ];
 		splits = 0;
 		facing = 0;
@@ -134,7 +139,7 @@ static void SelectSplitPlaneNum( node_t *node, face_t *list, int *splitPlaneNum,
 		for ( check = list ; check ; check = check->next ) {
 			if ( check->planenum == split->planenum ) {
 				facing++;
-				check->checked = qtrue;	// won't need to test this plane again
+				//check->checked = qtrue;	// won't need to test this plane again
 				continue;
 			}
 			side = WindingOnPlaneSide( check->w, plane->normal, plane->dist );
@@ -146,15 +151,38 @@ static void SelectSplitPlaneNum( node_t *node, face_t *list, int *splitPlaneNum,
 				back++;
 			}
 		}
-		value =  5*facing - 5*splits; // - abs(front-back);
-		if ( plane->type < 3 ) {
-			value+=5;		// axial is better
+
+		if(bspAlternateSplitWeights)
+		{
+			// from 27
+
+			//Bigger is better
+			sizeBias=WindingArea(split->w);
+
+			//Base score = 20000 perfectly balanced 
+			value = 20000-(abs(front-back));
+			value -= plane->counter;// If we've already used this plane sometime in the past try not to use it again 
+			value -= facing ;       // if we're going to have alot of other surfs use this plane, we want to get it in quickly.
+			value -= splits*5;        //more splits = bad
+			value +=  sizeBias*10; //We want a huge score bias based on plane size
+		}
+		else
+		{
+			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;
+			//frontC=front;
+			//backC=back;
+			//splitsC=splits;
+			//facingC=facing;
 		}
 	}
 	
@@ -162,9 +190,22 @@ static void SelectSplitPlaneNum( node_t *node, face_t *list, int *splitPlaneNum,
 	if( bestValue == -99999 )
 		return;
 	
+	//Sys_FPrintf (SYS_VRB, "F: %d B:%d S:%d FA:%ds\n",frontC,backC,splitsC,facingC );
+
 	/* set best split data */
 	*splitPlaneNum = bestSplit->planenum;
 	*compileFlags = bestSplit->compileFlags;
+
+#if 0
+	if(bestSplit->compileFlags & C_DETAIL)
+		for( split = list; split; split = split->next )
+			if(!(split->compileFlags & C_DETAIL))
+				Sys_FPrintf(SYS_ERR, "DON'T DO SUCH SPLITS (1)\n");
+	if((node->compileFlags & C_DETAIL) && !(bestSplit->compileFlags & C_DETAIL))
+		Sys_FPrintf(SYS_ERR, "DON'T DO SUCH SPLITS (2)\n");
+#endif
+
+   if (*splitPlaneNum>-1) mapplanes[ *splitPlaneNum ].counter++;
 }
 
 
@@ -203,6 +244,7 @@ void BuildFaceTree_r( node_t *node, face_t *list )
 	winding_t	*frontWinding, *backWinding;
 	int			i;
 	int			splitPlaneNum, compileFlags;
+	qboolean isstruct = qfalse;
 	
 	
 	/* count faces left */
@@ -215,6 +257,7 @@ void BuildFaceTree_r( node_t *node, face_t *list )
 	if ( splitPlaneNum == -1 )
 	{
 		node->planenum = PLANENUM_LEAF;
+		node->has_structural_children = qfalse;
 		c_faceLeafs++;
 		return;
 	}
@@ -222,9 +265,11 @@ void BuildFaceTree_r( node_t *node, face_t *list )
 	/* partition the list */
 	node->planenum = splitPlaneNum;
 	node->compileFlags = compileFlags;
+	node->has_structural_children = !(compileFlags & C_DETAIL) && !node->opaque;
 	plane = &mapplanes[ splitPlaneNum ];
 	childLists[0] = NULL;
 	childLists[1] = NULL;
+
 	for( split = list; split; split = next )
 	{
 		/* set next */
@@ -236,6 +281,9 @@ void BuildFaceTree_r( node_t *node, face_t *list )
 			FreeBspFace( split );
 			continue;
 		}
+
+		if(!(split->compileFlags & C_DETAIL))
+			isstruct = 1;
 		
 		/* determine which side the face falls on */
 		side = WindingOnPlaneSide( split->w, plane->normal, plane->dist );
@@ -290,9 +338,22 @@ void BuildFaceTree_r( node_t *node, face_t *list )
 		}
 	}
 
+#if 0
+	if((node->compileFlags & C_DETAIL) && isstruct)
+		Sys_FPrintf(SYS_ERR, "I am detail, my child is structural, this is a wtf1\n", node->has_structural_children);
+#endif
+
 	for ( i = 0 ; i < 2 ; i++ ) {
 		BuildFaceTree_r ( node->children[i], childLists[i]);
+		node->has_structural_children |= node->children[i]->has_structural_children;
 	}
+
+#if 0
+	if((node->compileFlags & C_DETAIL) && !(node->children[0]->compileFlags & C_DETAIL) && node->children[0]->planenum != PLANENUM_LEAF)
+		Sys_FPrintf(SYS_ERR, "I am detail, my child is structural\n", node->has_structural_children);
+	if((node->compileFlags & C_DETAIL) && isstruct)
+		Sys_FPrintf(SYS_ERR, "I am detail, my child is structural, this is a wtf2\n", node->has_structural_children);
+#endif
 }
 
 
@@ -324,6 +385,11 @@ tree_t *FaceBSP( face_t *list ) {
 	}
 	Sys_FPrintf( SYS_VRB, "%9d faces\n", count );
 
+   for( i = 0; i < nummapplanes; i++)
+   {
+      mapplanes[ i ].counter=0;
+   }
+
 	tree->headnode = AllocNode();
 	VectorCopy( tree->mins, tree->headnode->mins );
 	VectorCopy( tree->maxs, tree->headnode->maxs );
@@ -355,7 +421,7 @@ face_t *MakeStructuralBSPFaceList( brush_t *list )
 	flist = NULL;
 	for( b = list; b != NULL; b = b->next )
 	{
-		if( b->detail )
+		if( !deepBSP && b->detail )
 			continue;
 		
 		for( i = 0; i < b->numsides; i++ )
@@ -375,6 +441,8 @@ face_t *MakeStructuralBSPFaceList( brush_t *list )
 			f->w = CopyWinding( w );
 			f->planenum = s->planenum & ~1;
 			f->compileFlags = s->compileFlags;	/* ydnar */
+			if(b->detail)
+				f->compileFlags |= C_DETAIL;
 			
 			/* ydnar: set priority */
 			f->priority = 0;
@@ -384,6 +452,8 @@ face_t *MakeStructuralBSPFaceList( brush_t *list )
 				f->priority += ANTIPORTAL_PRIORITY;
 			if( f->compileFlags & C_AREAPORTAL )
 				f->priority += AREAPORTAL_PRIORITY;
+			if( f->compileFlags & C_DETAIL )
+				f->priority += DETAIL_PRIORITY;
 			
 			/* get next face */
 			f->next = flist;
@@ -413,7 +483,7 @@ face_t *MakeVisibleBSPFaceList( brush_t *list )
 	flist = NULL;
 	for( b = list; b != NULL; b = b->next )
 	{
-		if( b->detail )
+		if( !deepBSP && b->detail )
 			continue;
 		
 		for( i = 0; i < b->numsides; i++ )
@@ -433,6 +503,8 @@ face_t *MakeVisibleBSPFaceList( brush_t *list )
 			f->w = CopyWinding( w );
 			f->planenum = s->planenum & ~1;
 			f->compileFlags = s->compileFlags;	/* ydnar */
+			if(b->detail)
+				f->compileFlags |= C_DETAIL;
 			
 			/* ydnar: set priority */
 			f->priority = 0;
@@ -442,6 +514,8 @@ face_t *MakeVisibleBSPFaceList( brush_t *list )
 				f->priority += ANTIPORTAL_PRIORITY;
 			if( f->compileFlags & C_AREAPORTAL )
 				f->priority += AREAPORTAL_PRIORITY;
+			if( f->compileFlags & C_DETAIL )
+				f->priority += DETAIL_PRIORITY;
 			
 			/* get next face */
 			f->next = flist;
diff --git a/tools/quake3/q3map2/game__null.h b/tools/quake3/q3map2/game__null.h
new file mode 100644
index 00000000..c2f24849
--- /dev/null
+++ b/tools/quake3/q3map2/game__null.h
@@ -0,0 +1,98 @@
+/* -------------------------------------------------------------------------------
+
+Copyright (C) 1999-2007 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__NULL_H
+#define GAME__NULL_H
+
+
+
+/* -------------------------------------------------------------------------------
+
+content and surface flags
+are in game_quake3.h
+
+------------------------------------------------------------------------------- */
+
+
+
+/* -------------------------------------------------------------------------------
+
+game_t struct
+
+------------------------------------------------------------------------------- */
+
+{
+	NULL,			/* -game x */
+	NULL,			/* default base game data dir */
+	NULL,			/* unix home sub-dir */
+	NULL,			/* magic path word */
+	NULL,			/* shader directory */
+	0,			/* max lightmapped surface verts */
+	0,			/* max surface verts */
+	0,			/* max surface indexes */
+	qfalse,			/* flares */
+	NULL,			/* default flare shader */
+	qfalse,			/* wolf lighting model? */
+	0,			/* lightmap width/height */
+	0,			/* lightmap gamma */
+	0,			/* lightmap exposure */
+	0,			/* lightmap compensate */
+	0,			/* lightgrid scale */
+	0,			/* lightgrid ambient scale */
+	qfalse,			/* light angle attenuation uses half-lambert curve */
+	qfalse,			/* disable shader lightstyles hack */
+	qfalse,			/* keep light entities on bsp */
+	0,			/* default patchMeta subdivisions tolerance */
+	qfalse,			/* patch casting enabled */
+	qfalse,			/* compile deluxemaps */
+	0,			/* deluxemaps default mode */
+	0,			/* minimap size */
+	0,			/* minimap sharpener */
+	0,			/* minimap border */
+	qfalse,			/* minimap keep aspect */
+	MINIMAP_MODE_GRAY,	/* minimap mode */
+	NULL,			/* minimap name format */
+	NULL,			/* bsp file prefix */
+	0,			/* bsp file version */
+	qfalse,			/* cod-style lump len/ofs order */
+	NULL,			/* bsp load function */
+	NULL,			/* bsp write function */
+
+	{
+		{ NULL, 0, 0, 0, 0, 0, 0 }
+	}
+}
+
+
+
+/* end marker */
+#endif
+
diff --git a/tools/quake3/q3map2/game_darkplaces.h b/tools/quake3/q3map2/game_darkplaces.h
new file mode 100644
index 00000000..e91fdb76
--- /dev/null
+++ b/tools/quake3/q3map2/game_darkplaces.h
@@ -0,0 +1,150 @@
+/* -------------------------------------------------------------------------------
+
+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_DARKPLACES_H
+#define GAME_DARKPLACES_H
+
+/* content and surface flags get form quake3 */
+
+/* -------------------------------------------------------------------------------
+
+game_t struct
+
+------------------------------------------------------------------------------- */
+
+{
+	"darkplaces",		/* -game x */
+	"id1",				/* default base game data dir */
+	".darkplaces ",		/* unix home sub-dir */
+	"darkplaces",		/* magic path word */
+	"scripts",			/* shader directory */
+	999,				/* 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 */
+	200.0f,				/* lightmap exposure */
+	1.0f,				/* lightmap compensate */
+	0.3f,				/* lightgrid scale */
+	0.6f,				/* lightgrid ambient scale */
+	qfalse,				/* light angle attenuation uses half-lambert curve */
+	qtrue,				/* disable shader lightstyles hack */
+	qtrue,				/* keep light entities on bsp */
+	4,					/* default patchMeta subdivisions tolerance */
+	qfalse,				/* patch casting enabled */
+	qfalse,				/* compile deluxemaps */
+	0,					/* deluxemaps default mode */
+	512,                /* minimap size */
+	1.0f,               /* minimap sharpener */
+	0.0f,               /* minimap border */
+	qtrue,              /* minimap keep aspect */
+	MINIMAP_MODE_GRAY,  /* minimap mode */
+	"%s.tga",           /* minimap name format */
+	"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
+
diff --git a/tools/quake3/q3map2/game_dq.h b/tools/quake3/q3map2/game_dq.h
new file mode 100644
index 00000000..97d2d6d5
--- /dev/null
+++ b/tools/quake3/q3map2/game_dq.h
@@ -0,0 +1,150 @@
+/* -------------------------------------------------------------------------------
+
+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_DQ_H
+#define GAME_DQ_H
+
+/* content and surface flags get form quake3 */
+
+/* -------------------------------------------------------------------------------
+
+game_t struct
+
+------------------------------------------------------------------------------- */
+
+{
+	"dq",				/* -game x */
+	"basedq",			/* default base game data dir */
+	".dq",				/* unix home sub-dir */
+	"dq",				/* 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.2f,				/* lightmap gamma */
+	200.0f,				/* lightmap exposure */
+	1.0f,				/* lightmap compensate */
+	0.3f,				/* lightgrid scale */
+	0.6f,				/* lightgrid ambient scale */
+	qfalse,				/* light angle attenuation uses half-lambert curve */
+	qtrue,				/* disable shader lightstyles hack */
+	qtrue,				/* keep light entities on bsp */
+	4,					/* default patchMeta subdivisions tolerance */
+	qtrue,				/* patch casting enabled */
+	qtrue,				/* compile deluxemaps */
+	1,					/* deluxemaps default mode */
+	512,                /* minimap size */
+	1.0f,               /* minimap sharpener */
+	0.0f,               /* minimap border */
+	qtrue,              /* minimap keep aspect */
+	MINIMAP_MODE_GRAY,  /* minimap mode */
+	"%s.tga",           /* minimap name format */
+	"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
+
diff --git a/tools/quake3/q3map2/game_ef.h b/tools/quake3/q3map2/game_ef.h
index f8d21d1a..84ea664c 100644
--- a/tools/quake3/q3map2/game_ef.h
+++ b/tools/quake3/q3map2/game_ef.h
@@ -113,7 +113,23 @@ game_t struct
 	qfalse,				/* wolf lighting model? */
 	128,				/* lightmap width/height */
 	1.0f,				/* lightmap gamma */
+	1.0f,				/* lightmap exposure */
 	1.0f,				/* lightmap compensate */
+	1.0f,				/* lightgrid scale */
+	1.0f,				/* lightgrid ambient scale */
+	qfalse,				/* light angle attenuation uses half-lambert curve */
+	qfalse,				/* disable shader lightstyles hack */
+	qfalse,				/* keep light entities on bsp */
+	8,					/* default patchMeta subdivisions tolerance */
+	qfalse,				/* patch casting enabled */
+	qfalse,				/* compile deluxemaps */
+	0,					/* deluxemaps default mode */
+	512,                /* minimap size */
+	1.0f,               /* minimap sharpener */
+	0.0f,               /* minimap border */
+	qtrue,              /* minimap keep aspect */
+	MINIMAP_MODE_GRAY,  /* minimap mode */
+	"%s.tga",           /* minimap name format */
 	"IBSP",				/* bsp file prefix */
 	46,					/* bsp file version */
 	qfalse,				/* cod-style lump len/ofs order */
diff --git a/tools/quake3/q3map2/game_etut.h b/tools/quake3/q3map2/game_etut.h
index 9823ff96..8add5bbe 100644
--- a/tools/quake3/q3map2/game_etut.h
+++ b/tools/quake3/q3map2/game_etut.h
@@ -148,7 +148,23 @@ game_t struct
 	qfalse,				/* wolf lighting model? */
 	128,				/* lightmap width/height */
 	2.2f,				/* lightmap gamma */
+	1.0f,				/* lightmap exposure */
 	1.0f,				/* lightmap compensate */
+	1.0f,				/* lightgrid scale */
+	1.0f,				/* lightgrid ambient scale */
+	qfalse,				/* light angle attenuation uses half-lambert curve */
+	qfalse,				/* disable shader lightstyles hack */
+	qfalse,				/* keep light entities on bsp */
+	8,					/* default patchMeta subdivisions tolerance */
+	qfalse,				/* patch casting enabled */
+	qfalse,				/* compile deluxemaps */
+	0,					/* deluxemaps default mode */
+	512,                /* minimap size */
+	1.0f,               /* minimap sharpener */
+	0.0f,               /* minimap border */
+	qtrue,              /* minimap keep aspect */
+	MINIMAP_MODE_GRAY,  /* minimap mode */
+	"%s.tga",           /* minimap name format */
 	"IBSP",				/* bsp file prefix */
 	47,					/* bsp file version */
 	qfalse,				/* cod-style lump len/ofs order */
diff --git a/tools/quake3/q3map2/game_ja.h b/tools/quake3/q3map2/game_ja.h
index 79007ed6..3d25129e 100644
--- a/tools/quake3/q3map2/game_ja.h
+++ b/tools/quake3/q3map2/game_ja.h
@@ -67,7 +67,23 @@ game_t struct
 	qfalse,				/* wolf lighting model? */
 	128,				/* lightmap width/height */
 	1.0f,				/* lightmap gamma */
+	1.0f,				/* lightmap exposure */
 	1.0f,				/* lightmap compensate */
+	1.0f,				/* lightgrid scale */
+	1.0f,				/* lightgrid ambient scale */
+	qfalse,				/* light angle attenuation uses half-lambert curve */
+	qfalse,				/* disable shader lightstyles hack */
+	qfalse,				/* keep light entities on bsp */
+	8,					/* default patchMeta subdivisions tolerance */
+	qfalse,				/* patch casting enabled */
+	qfalse,				/* compile deluxemaps */
+	0,					/* deluxemaps default mode */
+	512,                /* minimap size */
+	1.0f,               /* minimap sharpener */
+	0.0f,               /* minimap border */
+	qtrue,              /* minimap keep aspect */
+	MINIMAP_MODE_GRAY,  /* minimap mode */
+	"%s.tga",           /* minimap name format */
 	"RBSP",				/* bsp file prefix */
 	1,					/* bsp file version */
 	qfalse,				/* cod-style lump len/ofs order */
diff --git a/tools/quake3/q3map2/game_jk2.h b/tools/quake3/q3map2/game_jk2.h
index a8dc01a0..056baf01 100644
--- a/tools/quake3/q3map2/game_jk2.h
+++ b/tools/quake3/q3map2/game_jk2.h
@@ -64,7 +64,23 @@ game_t struct
 	qfalse,				/* wolf lighting model? */
 	128,				/* lightmap width/height */
 	1.0f,				/* lightmap gamma */
+	1.0f,				/* lightmap exposure */
 	1.0f,				/* lightmap compensate */
+	1.0f,				/* lightgrid scale */
+	1.0f,				/* lightgrid ambient scale */
+	qfalse,				/* light angle attenuation uses half-lambert curve */
+	qfalse,				/* disable shader lightstyles hack */
+	qfalse,				/* keep light entities on bsp */
+	8,					/* default patchMeta subdivisions tolerance */
+	qfalse,				/* patch casting enabled */
+	qfalse,				/* compile deluxemaps */
+	0,					/* deluxemaps default mode */
+	512,                /* minimap size */
+	1.0f,               /* minimap sharpener */
+	0.0f,               /* minimap border */
+	qtrue,              /* minimap keep aspect */
+	MINIMAP_MODE_GRAY,  /* minimap mode */
+	"%s.tga",           /* minimap name format */
 	"RBSP",				/* bsp file prefix */
 	1,					/* bsp file version */
 	qfalse,				/* cod-style lump len/ofs order */
diff --git a/tools/quake3/q3map2/game_nexuiz.h b/tools/quake3/q3map2/game_nexuiz.h
index 0d23766d..4d496c33 100644
--- a/tools/quake3/q3map2/game_nexuiz.h
+++ b/tools/quake3/q3map2/game_nexuiz.h
@@ -63,7 +63,23 @@ game_t struct
 	qfalse,				/* wolf lighting model? */
 	128,				/* lightmap width/height */
 	1.0f,				/* lightmap gamma */
+	1.0f,				/* lightmap exposure */
 	1.0f,				/* lightmap compensate */
+	1.0f,				/* lightgrid scale */
+	1.0f,				/* lightgrid ambient scale */
+	qfalse,				/* light angle attenuation uses half-lambert curve */
+	qtrue,				/* disable shader lightstyles hack */
+	qtrue,				/* keep light entities on bsp */
+	8,					/* default patchMeta subdivisions tolerance */
+	qfalse,				/* patch casting enabled */
+	qfalse,				/* compile deluxemaps */
+	0,					/* deluxemaps default mode */
+	512,                /* minimap size */
+	1.0f,               /* minimap sharpener */
+	1.0f/66.0f,         /* minimap border */
+	qtrue,              /* minimap keep aspect */
+	MINIMAP_MODE_GRAY,  /* minimap mode */
+	"../gfx/%s_mini.tga", /* minimap name format */
 	"IBSP",				/* bsp file prefix */
 	46,					/* bsp file version */
 	qfalse,				/* cod-style lump len/ofs order */
@@ -130,6 +146,7 @@ game_t struct
 		{ "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 }
 	}
diff --git a/tools/quake3/q3map2/game_prophecy.h b/tools/quake3/q3map2/game_prophecy.h
new file mode 100644
index 00000000..60e299d3
--- /dev/null
+++ b/tools/quake3/q3map2/game_prophecy.h
@@ -0,0 +1,150 @@
+/* -------------------------------------------------------------------------------
+
+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_PROPHECY_H
+#define GAME_PROPHECY_H
+
+/* content and surface flags get form quake3 */
+
+/* -------------------------------------------------------------------------------
+
+game_t struct
+
+------------------------------------------------------------------------------- */
+
+{
+	"prophecy",			/* -game x */
+	"base",				/* default base game data dir */
+	".prophecy",		/* unix home sub-dir */
+	"prophecy",			/* 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 */
+	200.0f,				/* lightmap exposure */
+	1.0f,				/* lightmap compensate */
+	0.4f,				/* lightgrid scale */
+	0.6f,				/* lightgrid ambient scale */
+	qfalse,				/* light angle attenuation uses half-lambert curve */
+	qtrue,				/* disable shader lightstyles hack */
+	qtrue,				/* keep light entities on bsp */
+	4,					/* default patchMeta subdivisions tolerance */
+	qtrue,				/* patch casting enabled */
+	qtrue,				/* compile deluxemaps */
+	0,					/* deluxemaps default mode */
+	512,                /* minimap size */
+	1.0f,               /* minimap sharpener */
+	0.0f,               /* minimap border */
+	qtrue,              /* minimap keep aspect */
+	MINIMAP_MODE_GRAY,  /* minimap mode */
+	"%s.tga",           /* minimap name format */
+	"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
+
diff --git a/tools/quake3/q3map2/game_qfusion.h b/tools/quake3/q3map2/game_qfusion.h
index 43c27fc5..7645ad0a 100644
--- a/tools/quake3/q3map2/game_qfusion.h
+++ b/tools/quake3/q3map2/game_qfusion.h
@@ -115,7 +115,23 @@ game_t struct
 	qfalse,				/* wolf lighting model? */
 	512,				/* lightmap width/height */
 	1.0f,				/* lightmap gamma */
+	1.0f,				/* lightmap exposure */
 	1.0f,				/* lightmap compensate */
+	1.0f,				/* lightgrid scale */
+	1.0f,				/* lightgrid ambient scale */
+	qtrue,				/* light angle attenuation uses half-lambert curve */
+	qtrue,				/* disable shader lightstyles hack */
+	qtrue,				/* keep light entities on bsp */
+	8,					/* default patchMeta subdivisions tolerance */
+	qtrue,				/* patch casting enabled */
+	qtrue,				/* compile deluxemaps */
+	0,					/* deluxemaps default mode */
+	256,                /* minimap size */
+	1.0f,               /* minimap sharpener */
+	0.0f,               /* minimap border */
+	qtrue,              /* minimap keep aspect */
+	MINIMAP_MODE_WHITE, /* minimap mode */
+	"../minimaps/%s.tga", /* minimap name format */
 	"FBSP",				/* bsp file prefix */
 	1,					/* bsp file version */
 	qfalse,				/* cod-style lump len/ofs order */
diff --git a/tools/quake3/q3map2/game_quake3.h b/tools/quake3/q3map2/game_quake3.h
index be1f3397..98d4c4b8 100644
--- a/tools/quake3/q3map2/game_quake3.h
+++ b/tools/quake3/q3map2/game_quake3.h
@@ -112,7 +112,23 @@ game_t struct
 	qfalse,				/* wolf lighting model? */
 	128,				/* lightmap width/height */
 	1.0f,				/* lightmap gamma */
+	1.0f,				/* lightmap exposure */
 	1.0f,				/* lightmap compensate */
+	1.0f,				/* lightgrid scale */
+	1.0f,				/* lightgrid ambient scale */
+	qfalse,				/* light angle attenuation uses half-lambert curve */
+	qfalse,				/* disable shader lightstyles hack */
+	qfalse,				/* keep light entities on bsp */
+	8,					/* default patchMeta subdivisions tolerance */
+	qfalse,				/* patch casting enabled */
+	qfalse,				/* compile deluxemaps */
+	0,					/* deluxemaps default mode */
+	512,                /* minimap size */
+	1.0f,               /* minimap sharpener */
+	0.0f,               /* minimap border */
+	qtrue,              /* minimap keep aspect */
+	MINIMAP_MODE_GRAY,  /* minimap mode */
+	"%s.tga",           /* minimap name format */
 	"IBSP",				/* bsp file prefix */
 	46,					/* bsp file version */
 	qfalse,				/* cod-style lump len/ofs order */
diff --git a/tools/quake3/q3map2/game_quakelive.h b/tools/quake3/q3map2/game_quakelive.h
index 18fd90f3..1a509272 100644
--- a/tools/quake3/q3map2/game_quakelive.h
+++ b/tools/quake3/q3map2/game_quakelive.h
@@ -52,9 +52,9 @@ game_t struct
 
 {
 	"quakelive",		/* -game x */
-	"baseq3",			/* default base game data dir */
-	".q3a",				/* unix home sub-dir */
-	"quake",			/* magic path word */
+	"baseq3",			/* default base game data dir (FIXME what does quake live really use?) */
+	".q3a",				/* unix home sub-dir (FIXME what does quake live really use?) */
+	"quake",			/* magic path word (FIXME where does quake live install to?) */
 	"scripts",			/* shader directory */
 	64,					/* max lightmapped surface verts */
 	999,				/* max surface verts */
@@ -64,7 +64,23 @@ game_t struct
 	qfalse,				/* wolf lighting model? */
 	128,				/* lightmap width/height */
 	1.0f,				/* lightmap gamma */
+	1.0f,				/* lightmap exposure */
 	1.0f,				/* lightmap compensate */
+	1.0f,				/* lightgrid scale */
+	1.0f,				/* lightgrid ambient scale */
+	qfalse,				/* light angle attenuation uses half-lambert curve */
+	qfalse,				/* disable shader lightstyles hack */
+	qfalse,				/* keep light entities on bsp */
+	8,					/* default patchMeta subdivisions tolerance */
+	qfalse,				/* patch casting enabled */
+	qfalse,				/* compile deluxemaps */
+	0,					/* deluxemaps default mode */
+	512,                /* minimap size */
+	1.0f,               /* minimap sharpener */
+	0.0f,               /* minimap border */
+	qtrue,              /* minimap keep aspect */
+	MINIMAP_MODE_GRAY,  /* minimap mode */
+	"%s.tga",           /* minimap name format */
 	"IBSP",				/* bsp file prefix */
 	47,					/* bsp file version */
 	qfalse,				/* cod-style lump len/ofs order */
diff --git a/tools/quake3/q3map2/game_reaction.h b/tools/quake3/q3map2/game_reaction.h
index 22e3e8ef..c81e7245 100644
--- a/tools/quake3/q3map2/game_reaction.h
+++ b/tools/quake3/q3map2/game_reaction.h
@@ -84,7 +84,23 @@ game_t struct
 	qfalse,				/* wolf lighting model? */
 	128,				/* lightmap width/height */
 	1.0f,				/* lightmap gamma */
+	1.0f,				/* lightmap exposure */
 	1.0f,				/* lightmap compensate */
+	1.0f,				/* lightgrid scale */
+	1.0f,				/* lightgrid ambient scale */
+	qfalse,				/* light angle attenuation uses half-lambert curve */
+	qfalse,				/* disable shader lightstyles hack */
+	qfalse,				/* keep light entities on bsp */
+	8,					/* default patchMeta subdivisions tolerance */
+	qfalse,				/* patch casting enabled */
+	qfalse,				/* compile deluxemaps */
+	0,					/* deluxemaps default mode */
+	512,                /* minimap size */
+	1.0f,               /* minimap sharpener */
+	0.0f,               /* minimap border */
+	qtrue,              /* minimap keep aspect */
+	MINIMAP_MODE_GRAY,  /* minimap mode */
+	"%s.tga",           /* minimap name format */
 	"IBSP",				/* bsp file prefix */
 	46,					/* bsp file version */
 	qfalse,				/* cod-style lump len/ofs order */
diff --git a/tools/quake3/q3map2/game_sof2.h b/tools/quake3/q3map2/game_sof2.h
index c9760392..931ec69c 100644
--- a/tools/quake3/q3map2/game_sof2.h
+++ b/tools/quake3/q3map2/game_sof2.h
@@ -139,7 +139,23 @@ game_t struct
 	qfalse,					/* wolf lighting model? */
 	128,					/* lightmap width/height */
 	1.0f,					/* lightmap gamma */
+	1.0f,					/* lightmap exposure */
 	1.0f,					/* lightmap compensate */
+	1.0f,				/* lightgrid scale */
+	1.0f,				/* lightgrid ambient scale */
+	qfalse,				/* light angle attenuation uses half-lambert curve */
+	qfalse,				/* disable shader lightstyles hack */
+	qfalse,				/* keep light entities on bsp */
+	8,					/* default patchMeta subdivisions tolerance */
+	qfalse,				/* patch casting enabled */
+	qfalse,				/* compile deluxemaps */
+	0,					/* deluxemaps default mode */
+	512,                /* minimap size */
+	1.0f,               /* minimap sharpener */
+	0.0f,               /* minimap border */
+	qtrue,              /* minimap keep aspect */
+	MINIMAP_MODE_GRAY,  /* minimap mode */
+	"%s.tga",           /* minimap name format */
 	"RBSP",					/* bsp file prefix */
 	1,						/* bsp file version */
 	qfalse,					/* cod-style lump len/ofs order */
diff --git a/tools/quake3/q3map2/game_tenebrae.h b/tools/quake3/q3map2/game_tenebrae.h
index 355c9263..ab312158 100644
--- a/tools/quake3/q3map2/game_tenebrae.h
+++ b/tools/quake3/q3map2/game_tenebrae.h
@@ -112,7 +112,23 @@ game_t struct
 	qfalse,				/* wolf lighting model? */
 	512,				/* lightmap width/height */
 	2.0f,				/* lightmap gamma */
+	1.0f,				/* lightmap exposure */
 	1.0f,				/* lightmap compensate */
+	1.0f,				/* lightgrid scale */
+	1.0f,				/* lightgrid ambient scale */
+	qfalse,				/* light angle attenuation uses half-lambert curve */
+	qtrue,				/* disable shader lightstyles hack */
+	qfalse,				/* keep light entities on bsp */
+	8,					/* default patchMeta subdivisions tolerance */
+	qfalse,				/* patch casting enabled */
+	qtrue,				/* compile deluxemaps */
+	0,					/* deluxemaps default mode */
+	512,                /* minimap size */
+	1.0f,               /* minimap sharpener */
+	0.0f,               /* minimap border */
+	qtrue,              /* minimap keep aspect */
+	MINIMAP_MODE_GRAY,  /* minimap mode */
+	"%s.tga",           /* minimap name format */
 	"IBSP",				/* bsp file prefix */
 	46,					/* bsp file version */
 	qfalse,				/* cod-style lump len/ofs order */
diff --git a/tools/quake3/q3map2/game_tremulous.h b/tools/quake3/q3map2/game_tremulous.h
index 9864cb9c..5d422629 100644
--- a/tools/quake3/q3map2/game_tremulous.h
+++ b/tools/quake3/q3map2/game_tremulous.h
@@ -70,7 +70,23 @@ game_t struct
 	qfalse,				/* wolf lighting model? */
 	128,				/* lightmap width/height */
 	1.0f,				/* lightmap gamma */
+	1.0f,				/* lightmap exposure */
 	1.0f,				/* lightmap compensate */
+	1.0f,				/* lightgrid scale */
+	1.0f,				/* lightgrid ambient scale */
+	qfalse,				/* light angle attenuation uses half-lambert curve */
+	qfalse,				/* disable shader lightstyles hack */
+	qfalse,				/* keep light entities on bsp */
+	8,					/* default patchMeta subdivisions tolerance */
+	qfalse,				/* patch casting enabled */
+	qfalse,				/* compile deluxemaps */
+	0,					/* deluxemaps default mode */
+	512,                /* minimap size */
+	1.0f,               /* minimap sharpener */
+	0.0f,               /* minimap border */
+	qtrue,              /* minimap keep aspect */
+	MINIMAP_MODE_GRAY,  /* minimap mode */
+	"%s.tga",           /* minimap name format */
 	"IBSP",				/* bsp file prefix */
 	46,					/* bsp file version */
 	qfalse,				/* cod-style lump len/ofs order */
diff --git a/tools/quake3/q3map2/game_wolf.h b/tools/quake3/q3map2/game_wolf.h
index 3200d162..157bfe3a 100644
--- a/tools/quake3/q3map2/game_wolf.h
+++ b/tools/quake3/q3map2/game_wolf.h
@@ -129,7 +129,23 @@ game_t struct
 	qtrue,				/* wolf lighting model? */
 	128,				/* lightmap width/height */
 	1.0f,				/* lightmap gamma */
+	1.0f,				/* lightmap exposure */
 	1.0f,				/* lightmap compensate */
+	1.0f,				/* lightgrid scale */
+	1.0f,				/* lightgrid ambient scale */
+	qfalse,				/* light angle attenuation uses half-lambert curve */
+	qfalse,				/* disable shader lightstyles hack */
+	qfalse,				/* keep light entities on bsp */
+	8,					/* default patchMeta subdivisions tolerance */
+	qfalse,				/* patch casting enabled */
+	qfalse,				/* compile deluxemaps */
+	0,					/* deluxemaps default mode */
+	512,                /* minimap size */
+	1.0f,               /* minimap sharpener */
+	0.0f,               /* minimap border */
+	qtrue,              /* minimap keep aspect */
+	MINIMAP_MODE_GRAY,  /* minimap mode */
+	"%s.tga",           /* minimap name format */
 	"IBSP",				/* bsp file prefix */
 	47,					/* bsp file version */
 	qfalse,				/* cod-style lump len/ofs order */
diff --git a/tools/quake3/q3map2/game_wolfet.h b/tools/quake3/q3map2/game_wolfet.h
index a6ca181f..3495f0dc 100644
--- a/tools/quake3/q3map2/game_wolfet.h
+++ b/tools/quake3/q3map2/game_wolfet.h
@@ -66,7 +66,23 @@ game_t struct
 	qtrue,				/* wolf lighting model? */
 	128,				/* lightmap width/height */
 	1.0f,				/* lightmap gamma */
+	1.0f,				/* lightmap exposure */
 	1.0f,				/* lightmap compensate */
+	1.0f,				/* lightgrid scale */
+	1.0f,				/* lightgrid ambient scale */
+	qfalse,				/* light angle attenuation uses half-lambert curve */
+	qfalse,				/* disable shader lightstyles hack */
+	qfalse,				/* keep light entities on bsp */
+	8,					/* default patchMeta subdivisions tolerance */
+	qfalse,				/* patch casting enabled */
+	qfalse,				/* compile deluxemaps */
+	0,					/* deluxemaps default mode */
+	512,                /* minimap size */
+	1.0f,               /* minimap sharpener */
+	0.0f,               /* minimap border */
+	qtrue,              /* minimap keep aspect */
+	MINIMAP_MODE_GRAY,  /* minimap mode */
+	"%s.tga",           /* minimap name format */
 	"IBSP",				/* bsp file prefix */
 	47,					/* bsp file version */
 	qfalse,				/* cod-style lump len/ofs order */
diff --git a/tools/quake3/q3map2/game_xonotic.h b/tools/quake3/q3map2/game_xonotic.h
new file mode 100644
index 00000000..568c263e
--- /dev/null
+++ b/tools/quake3/q3map2/game_xonotic.h
@@ -0,0 +1,155 @@
+/* -------------------------------------------------------------------------------
+
+Copyright (C) 1999-2007 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_XONOTIC_H
+#define GAME_XONOTIC_H
+
+
+
+/* -------------------------------------------------------------------------------
+
+content and surface flags
+are in game_quake3.h
+
+------------------------------------------------------------------------------- */
+
+
+
+/* -------------------------------------------------------------------------------
+
+game_t struct
+
+------------------------------------------------------------------------------- */
+
+{
+	"xonotic", /* -game x */
+	"data", /* default base game data dir */
+	".xonotic", /* unix home sub-dir */
+	"xonotic", /* magic path word */
+	"scripts", /* shader directory */
+	1048575, /* max lightmapped surface verts */
+	1048575, /* max surface verts */
+	1048575, /* max surface indexes */
+	qfalse, /* flares */
+	"flareshader", /* default flare shader */
+	qfalse, /* wolf lighting model? */
+	128, /* lightmap width/height */
+	1.0f, /* lightmap gamma */
+	1.0f, /* lightmap exposure */
+	1.0f, /* lightmap compensate */
+	1.0f, /* lightgrid scale */
+	1.0f, /* lightgrid ambient scale */
+	qfalse, /* light angle attenuation uses half-lambert curve */
+	qtrue, /* disable shader lightstyles hack */
+	qtrue, /* keep light entities on bsp */
+	4, /* default patchMeta subdivisions tolerance */
+	qtrue, /* patch casting enabled */
+	qtrue, /* compile deluxemaps */
+	0, /* deluxemaps default mode */
+	512, /* minimap size */
+	1.0f, /* minimap sharpener */
+	1.0f/66.0f, /* minimap border */
+	qtrue, /* minimap keep aspect */
+	MINIMAP_MODE_GRAY, /* minimap mode */
+	"../gfx/%s_mini.tga", /* minimap name format */
+	"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
+
diff --git a/tools/quake3/q3map2/image.c b/tools/quake3/q3map2/image.c
index 0ddcaf86..0289e77c 100644
--- a/tools/quake3/q3map2/image.c
+++ b/tools/quake3/q3map2/image.c
@@ -99,7 +99,7 @@ note: this function is a total hack, as it reads/writes the png struct directly!
 typedef struct pngBuffer_s
 {
 	byte	*buffer;
-	int		size, offset;
+	png_size_t		size, offset;
 }
 pngBuffer_t;
 
@@ -127,8 +127,8 @@ static void LoadPNGBuffer( byte *buffer, int size, byte **pixels, int *width, in
 	png_struct	*png;
 	png_info	*info, *end;
 	pngBuffer_t	pb;
-	int			i, bitDepth, colorType, channels;
-	png_uint_32	w, h;
+	int			bitDepth, colorType, channels;
+	png_uint_32	w, h, i;
 	byte		**rowPointers;
 	
 	
diff --git a/tools/quake3/q3map2/leakfile.c b/tools/quake3/q3map2/leakfile.c
index 51fda300..fc5a3191 100644
--- a/tools/quake3/q3map2/leakfile.c
+++ b/tools/quake3/q3map2/leakfile.c
@@ -81,15 +81,15 @@ xmlNodePtr LeakFile (tree_t *tree)
 	if (!linefile)
 		Error ("Couldn't open %s\n", filename);
 
-  xml_node = xmlNewNode (NULL, "polyline");
+  xml_node = xmlNewNode (NULL, (xmlChar*)"polyline");
 
 	count = 0;
 	node = &tree->outside_node;
 	while (node->occupied > 1)
 	{
 		int			next;
-		portal_t	*p, *nextportal;
-		node_t		*nextnode;
+		portal_t	*p, *nextportal = NULL;
+		node_t		*nextnode = NULL;
 		int			s;
 
 		// find the best portal exit
diff --git a/tools/quake3/q3map2/light.c b/tools/quake3/q3map2/light.c
index 8520edd7..9323b8c8 100644
--- a/tools/quake3/q3map2/light.c
+++ b/tools/quake3/q3map2/light.c
@@ -313,7 +313,15 @@ void CreateEntityLights( void )
 			flags |= LIGHT_GRID;
 			flags &= ~LIGHT_SURFACES;
 		}
-		
+
+		/* vortex: unnormalized? */
+		if (spawnflags & 32)
+			flags |= LIGHT_UNNORMALIZED;
+
+		/* vortex: distance atten? */
+		if (spawnflags & 64)
+			flags |= LIGHT_ATTEN_DISTANCE;
+
 		/* store the flags */
 		light->flags = flags;
 		
@@ -385,14 +393,21 @@ void CreateEntityLights( void )
 		if( _color && _color[ 0 ] )
 		{
 			sscanf( _color, "%f %f %f", &light->color[ 0 ], &light->color[ 1 ], &light->color[ 2 ] );
-			ColorNormalize( light->color, light->color );
+			if (!(light->flags & LIGHT_UNNORMALIZED))
+			{
+				ColorNormalize( light->color, light->color );
+			}
 		}
 		else
 			light->color[ 0 ] = light->color[ 1 ] = light->color[ 2 ] = 1.0f;
+
+		light->extraDist = FloatForKey( e, "_extradist" );
+		if(light->extraDist == 0.0f)
+			light->extraDist = extraDist;
 		
 		intensity = intensity * pointScale;
 		light->photons = intensity;
-		
+
 		light->type = EMIT_POINT;
 		
 		/* set falloff threshold */
@@ -734,13 +749,21 @@ int LightContributionToSample( trace_t *trace )
 	float			angle;
 	float			add;
 	float			dist;
-	
+	float			addDeluxe = 0.0f, addDeluxeBounceScale = 0.25f;
+	qboolean		angledDeluxe = qtrue;
+	float			colorBrightness;
+	qboolean		doAddDeluxe = qtrue;
 	
 	/* get light */
 	light = trace->light;
 	
 	/* clear color */
+	trace->forceSubsampling = 0.0f; /* to make sure */
 	VectorClear( trace->color );
+	VectorClear( trace->colorNoShadow );
+	VectorClear( trace->directionContribution );
+
+	colorBrightness = RGBTOGRAY( light->color ) * ( 1.0f/255.0f );
 	
 	/* ydnar: early out */
 	if( !(light->flags & LIGHT_SURFACES) || light->envelope <= 0.0f )
@@ -766,7 +789,6 @@ int LightContributionToSample( trace_t *trace )
 		float		d;
 		vec3_t		pushedOrigin;
 		
-		
 		/* project sample point into light plane */
 		d = DotProduct( trace->origin, light->normal ) - light->dist;
 		if( d < 3.0f )
@@ -781,7 +803,7 @@ int LightContributionToSample( trace_t *trace )
 		}
 		
 		/* nudge the point so that it is clearly forward of the light */
-		/* so that surfaces meeting a light emiter don't get black edges */
+		/* so that surfaces meeting a light emitter don't get black edges */
 		if( d > -8.0f && d < 8.0f )
 			VectorMA( trace->origin, (8.0f - d), light->normal, pushedOrigin );				
 		else
@@ -800,8 +822,13 @@ int LightContributionToSample( trace_t *trace )
 			angle = DotProduct( trace->normal, trace->direction );
 			
 			/* twosided lighting */
-			if( trace->twoSided )
-				angle = fabs( angle );
+			if( trace->twoSided && angle < 0 )
+			{
+				angle = -angle;
+
+				/* no deluxemap contribution from "other side" light */
+				doAddDeluxe = qfalse;
+			}
 			
 			/* attenuate */
 			angle *= -DotProduct( light->normal, trace->direction );
@@ -809,8 +836,27 @@ int LightContributionToSample( trace_t *trace )
 				return 0;
 			else if( angle < 0.0f &&
 				(trace->twoSided || (light->flags & LIGHT_TWOSIDED)) )
+			{
 				angle = -angle;
+
+				/* no deluxemap contribution from "other side" light */
+				doAddDeluxe = qfalse;
+			}
+
+			/* clamp the distance to prevent super hot spots */
+			dist = sqrt(dist * dist + light->extraDist * light->extraDist);
+			if( dist < 16.0f )
+				dist = 16.0f;
+
 			add = light->photons / (dist * dist) * angle;
+
+			if( deluxemap )
+			{
+				if( angledDeluxe )
+					addDeluxe = light->photons / (dist * dist) * angle;
+				else
+					addDeluxe = light->photons / (dist * dist);
+			}
 		}
 		else
 		{
@@ -830,13 +876,26 @@ int LightContributionToSample( trace_t *trace )
 					dist = SetupTrace( trace );
 					if( dist >= light->envelope )
 						return 0;
+
+					/* no deluxemap contribution from "other side" light */
+					doAddDeluxe = qfalse;
 				}
 				else
 					return 0;
 			}
+
+			/* also don't deluxe if the direction is on the wrong side */
+			if(DotProduct(trace->normal, trace->direction) < 0)
+			{
+				/* no deluxemap contribution from "other side" light */
+				doAddDeluxe = qfalse;
+			}
 			
 			/* ydnar: moved to here */
 			add = factor * light->add;
+
+			if( deluxemap )
+				addDeluxe = add;
 		}
 	}
 	
@@ -848,13 +907,45 @@ int LightContributionToSample( trace_t *trace )
 		dist = SetupTrace( trace );
 		if( dist >= light->envelope )
 			return 0;
-		
+
 		/* clamp the distance to prevent super hot spots */
+		dist = sqrt(dist * dist + light->extraDist * light->extraDist);
 		if( dist < 16.0f )
 			dist = 16.0f;
-		
+
 		/* angle attenuation */
-		angle = (light->flags & LIGHT_ATTEN_ANGLE) ? DotProduct( trace->normal, trace->direction ) : 1.0f;
+		if( light->flags & LIGHT_ATTEN_ANGLE )
+		{
+			/* standard Lambert attenuation */
+			float dot = DotProduct( trace->normal, trace->direction ); 
+
+			/* twosided lighting */
+			if( trace->twoSided && dot < 0 )
+			{
+				dot = -dot;
+
+				/* no deluxemap contribution from "other side" light */
+				doAddDeluxe = qfalse;
+			}
+
+			/* jal: optional half Lambert attenuation (http://developer.valvesoftware.com/wiki/Half_Lambert) */
+			if( lightAngleHL )
+			{
+				if( dot > 0.001f ) // skip coplanar
+				{
+					if( dot > 1.0f ) dot = 1.0f;
+					dot = ( dot * 0.5f ) + 0.5f;
+					dot *= dot;
+				}
+				else
+					dot = 0;
+			}
+
+			angle = dot;
+		}
+		else
+			angle = 1.0f;
+
 		if( light->angleScale != 0.0f )
 		{
 			angle /= light->angleScale;
@@ -862,27 +953,48 @@ int LightContributionToSample( trace_t *trace )
 				angle = 1.0f;
 		}
 		
-		/* twosided lighting */
-		if( trace->twoSided )
-			angle = fabs( angle );
-		
 		/* attenuate */
 		if( light->flags & LIGHT_ATTEN_LINEAR )
 		{
 			add = angle * light->photons * linearScale - (dist * light->fade);
 			if( add < 0.0f )
 				add = 0.0f;
+
+			if( deluxemap )
+			{
+				if( angledDeluxe )
+					addDeluxe = angle * light->photons * linearScale - (dist * light->fade);
+				else
+					addDeluxe = light->photons * linearScale - (dist * light->fade);
+
+				if( addDeluxe < 0.0f )
+					addDeluxe = 0.0f;
+			}
 		}
 		else
-			add = light->photons / (dist * dist) * angle;
+		{
+			add = (light->photons / (dist * dist)) * angle;
+			if( add < 0.0f )
+				add = 0.0f;
+
+			if( deluxemap )
+			{
+				if( angledDeluxe )
+					addDeluxe = (light->photons / (dist * dist)) * angle;
+				else
+					addDeluxe = (light->photons / (dist * dist));
+			}
+
+			if( addDeluxe < 0.0f )
+				addDeluxe = 0.0f;
+		}
 		
 		/* handle spotlights */
 		if( light->type == EMIT_SPOT )
 		{
 			float	distByNormal, radiusAtDist, sampleRadius;
 			vec3_t	pointAtDist, distToSample;
-			
-			
+	
 			/* do cone calculation */
 			distByNormal = -DotProduct( trace->displacement, light->normal );
 			if( distByNormal < 0.0f )
@@ -898,7 +1010,16 @@ int LightContributionToSample( trace_t *trace )
 			
 			/* attenuate */
 			if( sampleRadius > (radiusAtDist - 32.0f) )
+			{
 				add *= ((radiusAtDist - sampleRadius) / 32.0f);
+				if( add < 0.0f )
+					add = 0.0f;
+
+				addDeluxe *= ((radiusAtDist - sampleRadius) / 32.0f);
+
+				if( addDeluxe < 0.0f )
+					addDeluxe = 0.0f;
+			}
 		}
 	}
 	
@@ -908,20 +1029,70 @@ int LightContributionToSample( trace_t *trace )
 		/* get origin and direction */
 		VectorAdd( trace->origin, light->origin, trace->end );
 		dist = SetupTrace( trace );
-		
+
 		/* angle attenuation */
-		angle = (light->flags & LIGHT_ATTEN_ANGLE)
-			? DotProduct( trace->normal, trace->direction )
-			: 1.0f;
-		
-		/* twosided lighting */
-		if( trace->twoSided )
-			angle = fabs( angle );
+		if( light->flags & LIGHT_ATTEN_ANGLE )
+		{
+			/* standard Lambert attenuation */
+			float dot = DotProduct( trace->normal, trace->direction ); 
+
+			/* twosided lighting */
+			if( trace->twoSided && dot < 0 )
+			{
+				dot = -dot;
+
+				/* no deluxemap contribution from "other side" light */
+				doAddDeluxe = qfalse;
+			}
+
+			/* jal: optional half Lambert attenuation (http://developer.valvesoftware.com/wiki/Half_Lambert) */
+			if( lightAngleHL )
+			{
+				if( dot > 0.001f ) // skip coplanar
+				{
+					if( dot > 1.0f ) dot = 1.0f;
+					dot = ( dot * 0.5f ) + 0.5f;
+					dot *= dot;
+				}
+				else
+					dot = 0;
+			}
+			
+			angle = dot;
+		}
+		else
+			angle = 1.0f;
 		
 		/* attenuate */
 		add = light->photons * angle;
+
+		if( deluxemap )
+		{
+			if( angledDeluxe )
+				addDeluxe = light->photons * angle;
+			else
+				addDeluxe = light->photons;
+
+			if( addDeluxe < 0.0f )
+				addDeluxe = 0.0f;
+		}
+
 		if( add <= 0.0f )
 			return 0;
+
+		/* VorteX: set noShadow color */
+		VectorScale(light->color, add, trace->colorNoShadow);
+
+		addDeluxe *= colorBrightness;
+
+		if( bouncing )
+		{
+			addDeluxe *= addDeluxeBounceScale;
+			if( addDeluxe < 0.00390625f )
+				addDeluxe = 0.00390625f;
+		}
+
+		VectorScale( trace->direction, addDeluxe, trace->directionContribution );
 		
 		/* setup trace */
 		trace->testAll = qtrue;
@@ -932,9 +1103,12 @@ int LightContributionToSample( trace_t *trace )
 		{
 			/* trace */
 			TraceLine( trace );
+			trace->forceSubsampling *= add;
 			if( !(trace->compileFlags & C_SKY) || trace->opaque )
 			{
 				VectorClear( trace->color );
+				VectorClear( trace->directionContribution );
+
 				return -1;
 			}
 		}
@@ -942,10 +1116,43 @@ int LightContributionToSample( trace_t *trace )
 		/* return to sender */
 		return 1;
 	}
+	else
+		Error("Light of undefined type!");
+
+	/* VorteX: set noShadow color */
+	VectorScale(light->color, add, trace->colorNoShadow);
 	
 	/* ydnar: changed to a variable number */
 	if( add <= 0.0f || (add <= light->falloffTolerance && (light->flags & LIGHT_FAST_ACTUAL)) )
 		return 0;
+
+	addDeluxe *= colorBrightness;
+
+	/* hack land: scale down the radiosity contribution to light directionality.
+	Deluxemaps fusion many light directions into one. In a rtl process all lights
+	would contribute individually to the bump map, so several light sources together
+	would make it more directional (example: a yellow and red lights received from
+	opposing sides would light one side in red and the other in blue, adding
+	the effect of 2 directions applied. In the deluxemapping case, this 2 lights would
+	neutralize each other making it look like having no direction.
+	Same thing happens with radiosity. In deluxemapping case the radiosity contribution
+	is modifying the direction applied from directional lights, making it go closer and closer
+	to the surface normal the bigger is the amount of radiosity received.
+	So, for preserving the directional lights contributions, we scale down the radiosity
+	contribution. It's a hack, but there's a reason behind it */
+	if( bouncing )
+	{
+		addDeluxe *= addDeluxeBounceScale;
+		/* better NOT increase it beyond the original value
+		if( addDeluxe < 0.00390625f )
+			addDeluxe = 0.00390625f;
+		*/
+	}
+
+	if(doAddDeluxe)
+	{
+		VectorScale( trace->direction, addDeluxe, trace->directionContribution );
+	}
 	
 	/* setup trace */
 	trace->testAll = qfalse;
@@ -953,9 +1160,12 @@ int LightContributionToSample( trace_t *trace )
 	
 	/* raytrace */
 	TraceLine( trace );
+	trace->forceSubsampling *= add;
 	if( trace->passSolid || trace->opaque )
 	{
 		VectorClear( trace->color );
+		VectorClear( trace->directionContribution );
+
 		return -1;
 	}
 	
@@ -1099,9 +1309,10 @@ int LightContributionToPoint( trace_t *trace )
 	if( light->type == EMIT_AREA && faster )
 	{
 		/* clamp the distance to prevent super hot spots */
+		dist = sqrt(dist * dist + light->extraDist * light->extraDist);
 		if( dist < 16.0f )
 			dist = 16.0f;
-		
+
 		/* attenuate */
 		add = light->photons / (dist * dist);
 	}
@@ -1145,6 +1356,7 @@ int LightContributionToPoint( trace_t *trace )
 	else if( light->type == EMIT_POINT || light->type == EMIT_SPOT )
 	{
 		/* clamp the distance to prevent super hot spots */
+		dist = sqrt(dist * dist + light->extraDist * light->extraDist);
 		if( dist < 16.0f )
 			dist = 16.0f;
 		
@@ -1244,27 +1456,27 @@ grid samples are for quickly determining the lighting
 of dynamically placed entities in the world
 */
 
-#define	MAX_CONTRIBUTIONS	1024
+#define	MAX_CONTRIBUTIONS	32768
 
 typedef struct
 {
 	vec3_t		dir;
 	vec3_t		color;
+	vec3_t		ambient;
 	int			style;
 }
 contribution_t;
 
 void TraceGrid( int num )
 {
-	int						i, j, x, y, z, mod, step, numCon, numStyles;
-	float					d;
-	vec3_t					baseOrigin, cheapColor, color;
+	int						i, j, x, y, z, mod, numCon, numStyles;
+	float					d, step;
+	vec3_t					baseOrigin, cheapColor, color, thisdir;
 	rawGridPoint_t			*gp;
 	bspGridPoint_t			*bgp;
 	contribution_t			contributions[ MAX_CONTRIBUTIONS ];
 	trace_t					trace;
 	
-	
 	/* get grid points */
 	gp = &rawGridPoints[ num ];
 	bgp = &bspGridPoints[ num ];
@@ -1295,38 +1507,21 @@ void TraceGrid( int num )
 	{
 		/* try to nudge the origin around to find a valid point */
 		VectorCopy( trace.origin, baseOrigin );
-		for( step = 9; step <= 18; step += 9 )
+		for( step = 0; (step += 0.005) <= 1.0; )
 		{
-			for( i = 0; i < 8; i++ )
-			{
-				VectorCopy( baseOrigin, trace.origin );
-				if( i & 1 )
-					trace.origin[ 0 ] += step;
-				else
-					trace.origin[ 0 ] -= step;
-				
-				if( i & 2 )
-					trace.origin[ 1 ] += step;
-				else
-					trace.origin[ 1 ] -= step;
+			VectorCopy( baseOrigin, trace.origin );
+			trace.origin[ 0 ] += step * (Random() - 0.5) * gridSize[0];
+			trace.origin[ 1 ] += step * (Random() - 0.5) * gridSize[1];
+			trace.origin[ 2 ] += step * (Random() - 0.5) * gridSize[2];
 				
-				if( i & 4 )
-					trace.origin[ 2 ] += step;
-				else
-					trace.origin[ 2 ] -= step;
-				
-				/* ydnar: changed to find cluster num */
-				trace.cluster = ClusterForPointExt( trace.origin, VERTEX_EPSILON );
-				if( trace.cluster >= 0 )
-					break;
-			}
-			
-			if( i != 8 )
+			/* ydnar: changed to find cluster num */
+			trace.cluster = ClusterForPointExt( trace.origin, VERTEX_EPSILON );
+			if( trace.cluster >= 0 )
 				break;
 		}
 		
 		/* can't find a valid point at all */
-		if( step > 18 )
+		if( step > 1.0 )
 			return;
 	}
 	
@@ -1361,6 +1556,7 @@ void TraceGrid( int num )
 		/* add a contribution */
 		VectorCopy( trace.color, contributions[ numCon ].color );
 		VectorCopy( trace.direction, contributions[ numCon ].dir );
+		VectorClear( contributions[ numCon ].ambient );
 		contributions[ numCon ].style = trace.light->style;
 		numCon++;
 		
@@ -1378,16 +1574,74 @@ void TraceGrid( int num )
 			break;
 	}
 	
+	/////// Floodlighting for point //////////////////
+	//do our floodlight ambient occlusion loop, and add a single contribution based on the brightest dir
+	if( floodlighty )
+	{
+		int k;
+		float addSize, f;
+		vec3_t dir = { 0, 0, 1 };
+		float ambientFrac = 0.25f;
+
+		trace.testOcclusion = qtrue;
+		trace.forceSunlight = qfalse;
+		trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
+		trace.testAll = qtrue;
+
+		for( k = 0; k < 2; k++ )
+		{
+			if( k == 0 ) // upper hemisphere
+			{
+				trace.normal[0] = 0;
+				trace.normal[1] = 0;
+				trace.normal[2] = 1;
+			}
+			else //lower hemisphere
+			{
+				trace.normal[0] = 0;
+				trace.normal[1] = 0;
+				trace.normal[2] = -1;
+			}
+
+			f = FloodLightForSample( &trace, floodlightDistance, floodlight_lowquality );
+
+			/* add a fraction as pure ambient, half as top-down direction */
+			contributions[ numCon ].color[0]= floodlightRGB[0] * floodlightIntensity * f * ( 1.0f - ambientFrac );
+			contributions[ numCon ].color[1]= floodlightRGB[1] * floodlightIntensity * f * ( 1.0f - ambientFrac );
+			contributions[ numCon ].color[2]= floodlightRGB[2] * floodlightIntensity * f * ( 1.0f - ambientFrac );
+
+			contributions[ numCon ].ambient[0]= floodlightRGB[0] * floodlightIntensity * f * ambientFrac;
+			contributions[ numCon ].ambient[1]= floodlightRGB[1] * floodlightIntensity * f * ambientFrac;
+			contributions[ numCon ].ambient[2]= floodlightRGB[2] * floodlightIntensity * f * ambientFrac;
+
+			contributions[ numCon ].dir[0] = dir[0];
+			contributions[ numCon ].dir[1] = dir[1];
+			contributions[ numCon ].dir[2] = dir[2];
+
+			contributions[ numCon ].style = 0;
+
+			/* push average direction around */
+			addSize = VectorLength( contributions[ numCon ].color );
+			VectorMA( gp->dir, addSize, dir, gp->dir );
+
+			numCon++;
+		}
+	}
+	/////////////////////
+
 	/* normalize to get primary light direction */
-	VectorNormalize( gp->dir, gp->dir );
+	VectorNormalize( gp->dir, thisdir );
 	
 	/* now that we have identified the primary light direction,
 	   go back and separate all the light into directed and ambient */
+
 	numStyles = 1;
 	for( i = 0; i < numCon; i++ )
 	{
 		/* get relative directed strength */
-		d = DotProduct( contributions[ i ].dir, gp->dir );
+		d = DotProduct( contributions[ i ].dir, thisdir );
+		/* we map 1 to gridDirectionality, and 0 to gridAmbientDirectionality */
+		d = gridAmbientDirectionality + d * (gridDirectionality - gridAmbientDirectionality);
 		if( d < 0.0f )
 			d = 0.0f;
 		
@@ -1420,25 +1674,47 @@ void TraceGrid( int num )
 		
 		/* ambient light will be at 1/4 the value of directed light */
 		/* (ydnar: nuke this in favor of more dramatic lighting?) */
+		/* (PM: how about actually making it work? d=1 when it got here for single lights/sun :P */
+//		d = 0.25f;
+		/* (Hobbes: always setting it to .25 is hardly any better) */
 		d = 0.25f * (1.0f - d);
 		VectorMA( gp->ambient[ j ], d, contributions[ i ].color, gp->ambient[ j ] );
+
+		VectorAdd( gp->ambient[ j ], contributions[ i ].ambient, gp->ambient[ j ] );
+
+/*
+ * div0:
+ * the total light average = ambient value + 0.25 * sum of all directional values
+ * we can also get the total light average as 0.25 * the sum of all contributions
+ *
+ * 0.25 * sum(contribution_i) == ambient + 0.25 * sum(d_i contribution_i)
+ *
+ * THIS YIELDS:
+ * ambient == 0.25 * sum((1 - d_i) contribution_i)
+ *
+ * So, 0.25f * (1.0f - d) IS RIGHT. If you want to tune it, tune d BEFORE.
+ */
 	}
 	
 	
 	/* store off sample */
 	for( i = 0; i < MAX_LIGHTMAPS; i++ )
 	{
+#if 0
 		/* do some fudging to keep the ambient from being too low (2003-07-05: 0.25 -> 0.125) */
 		if( !bouncing )
 			VectorMA( gp->ambient[ i ], 0.125f, gp->directed[ i ], gp->ambient[ i ] );
+#endif
 		
 		/* set minimum light and copy off to bytes */
 		VectorCopy( gp->ambient[ i ], color );
 		for( j = 0; j < 3; j++ )
 			if( color[ j ] < minGridLight[ j ] )
 				color[ j ] = minGridLight[ j ];
-		ColorToBytes( color, bgp->ambient[ i ], 1.0f );
-		ColorToBytes( gp->directed[ i ], bgp->directed[ i ], 1.0f );
+
+		/* vortex: apply gridscale and gridambientscale here */
+		ColorToBytes( color, bgp->ambient[ i ], gridScale*gridAmbientScale );
+		ColorToBytes( gp->directed[ i ], bgp->directed[ i ], gridScale );
 	}
 	
 	/* debug code */
@@ -1451,8 +1727,7 @@ void TraceGrid( int num )
 	#endif
 	
 	/* store direction */
-	if( !bouncing )
-		NormalToLatLong( gp->dir, bgp->latLong );
+	NormalToLatLong( thisdir, bgp->latLong );
 }
 
 
@@ -1631,7 +1906,9 @@ void LightWorld( void )
 		SetupEnvelopes( qtrue, fastgrid );
 		
 		Sys_Printf( "--- TraceGrid ---\n" );
+		inGrid = qtrue;
 		RunThreadsOnIndividual( numRawGridPoints, qtrue, TraceGrid );
+		inGrid = qfalse;
 		Sys_Printf( "%d x %d x %d = %d grid\n",
 			gridBounds[ 0 ], gridBounds[ 1 ], gridBounds[ 2 ], numBSPGridPoints );
 		
@@ -1654,13 +1931,11 @@ void LightWorld( void )
 	if( dirty )
 	{
 		Sys_Printf( "--- DirtyRawLightmap ---\n" );
-
-
-
-
 		RunThreadsOnIndividual( numRawLightmaps, qtrue, DirtyRawLightmap );
 	}
 	
+	/* floodlight pass */
+	FloodlightRawLightmaps();
 
 	/* ydnar: set up light envelopes */
 	SetupEnvelopes( qfalse, fast );
@@ -1694,6 +1969,7 @@ void LightWorld( void )
 	{
 		/* store off the bsp between bounces */
 		StoreSurfaceLightmaps();
+		UnparseEntities();
 		Sys_Printf( "Writing %s\n", source );
 		WriteBSPFile( source );
 		
@@ -1703,6 +1979,7 @@ void LightWorld( void )
 		/* flag bouncing */
 		bouncing = qtrue;
 		VectorClear( ambientColor );
+		floodlighty = qfalse;
 		
 		/* generate diffuse lights */
 		RadFreeLights();
@@ -1723,7 +2000,9 @@ void LightWorld( void )
 			gridBoundsCulled = 0;
 			
 			Sys_Printf( "--- BounceGrid ---\n" );
+			inGrid = qtrue;
 			RunThreadsOnIndividual( numRawGridPoints, qtrue, TraceGrid );
+			inGrid = qfalse;
 			Sys_FPrintf( SYS_VRB, "%9d grid points envelope culled\n", gridEnvelopeCulled );
 			Sys_FPrintf( SYS_VRB, "%9d grid points bounds culled\n", gridBoundsCulled );
 		}
@@ -1770,16 +2049,74 @@ int LightMain( int argc, char **argv )
 	float		f;
 	char		mapSource[ 1024 ];
 	const char	*value;
+	int lightmapMergeSize = 0;
+	qboolean	lightSamplesInsist = qfalse;
 	
 	
 	/* note it */
 	Sys_Printf( "--- Light ---\n" );
-	
+	Sys_Printf( "--- ProcessGameSpecific ---\n" );
+
 	/* set standard game flags */
 	wolfLight = game->wolfLight;
+	if (wolfLight == qtrue)
+		Sys_Printf( " lightning model: wolf\n" );
+	else
+		Sys_Printf( " lightning model: quake3\n" );
+
 	lmCustomSize = game->lightmapSize;
+	Sys_Printf( " lightmap size: %d x %d pixels\n", lmCustomSize, lmCustomSize );
+
 	lightmapGamma = game->lightmapGamma;
+	Sys_Printf( " lightning gamma: %f\n", lightmapGamma );
+
 	lightmapCompensate = game->lightmapCompensate;
+	Sys_Printf( " lightning compensation: %f\n", lightmapCompensate );
+
+	lightmapExposure = game->lightmapExposure;
+	Sys_Printf( " lightning exposure: %f\n", lightmapExposure );
+
+	gridScale = game->gridScale;
+	Sys_Printf( " lightgrid scale: %f\n", gridScale );
+
+	gridAmbientScale = game->gridAmbientScale;
+	Sys_Printf( " lightgrid ambient scale: %f\n", gridAmbientScale );
+
+	lightAngleHL = game->lightAngleHL;
+	if( lightAngleHL )
+		Sys_Printf( " half lambert light angle attenuation enabled \n" );
+
+	noStyles = game->noStyles;
+	if (noStyles == qtrue)
+		Sys_Printf( " shader lightstyles hack: disabled\n" );
+	else
+		Sys_Printf( " shader lightstyles hack: enabled\n" );
+
+	keepLights = game->keepLights;
+	if (keepLights == qtrue)
+		Sys_Printf( " keep lights: enabled\n" );
+	else
+		Sys_Printf( " keep lights: disabled\n" );
+
+	patchShadows = game->patchShadows;
+	if (patchShadows == qtrue)
+		Sys_Printf( " patch shadows: enabled\n" );
+	else
+		Sys_Printf( " patch shadows: disabled\n" );
+
+	deluxemap = game->deluxeMap;
+	deluxemode = game->deluxeMode;
+ 	if (deluxemap == qtrue)
+	{
+		if (deluxemode)
+			Sys_Printf( " deluxemapping: enabled with tangentspace deluxemaps\n" );
+		else
+			Sys_Printf( " deluxemapping: enabled with modelspace deluxemaps\n" );
+	}
+	else
+		Sys_Printf( " deluxemapping: disabled\n" );
+
+	Sys_Printf( "--- ProcessCommandLine ---\n" );
 	
 	/* process commandline arguments */
 	for( i = 1; i < (argc - 1); i++ )
@@ -1827,6 +2164,42 @@ int LightMain( int argc, char **argv )
 			Sys_Printf( "All light scaled by %f\n", f );
 			i++;
 		}
+
+		else if( !strcmp( argv[ i ], "-gridscale" ) )
+		{
+			f = atof( argv[ i + 1 ] );
+			Sys_Printf( "Grid lightning scaled by %f\n", f );
+			gridScale *= f;
+			i++;
+		}
+
+		else if( !strcmp( argv[ i ], "-gridambientscale" ) )
+		{
+			f = atof( argv[ i + 1 ] );
+			Sys_Printf( "Grid ambient lightning scaled by %f\n", f );
+			gridAmbientScale *= f;
+			i++;
+		}
+
+		else if( !strcmp( argv[ i ], "-griddirectionality" ) )
+		{
+			f = atof( argv[ i + 1 ] );
+			if(f > 1) f = 1;
+			if(f < gridAmbientDirectionality) gridAmbientDirectionality = f;
+			Sys_Printf( "Grid directionality is %f\n", f );
+			gridDirectionality = f;
+			i++;
+		}
+
+		else if( !strcmp( argv[ i ], "-gridambientdirectionality" ) )
+		{
+			f = atof( argv[ i + 1 ] );
+			if(f < -1) f = -1;
+			if(f > gridDirectionality) gridDirectionality = f;
+			Sys_Printf( "Grid ambient directionality is %f\n", f );
+			gridAmbientDirectionality = f;
+			i++;
+		}
 		
 		else if( !strcmp( argv[ i ], "-gamma" ) )
 		{
@@ -1836,6 +2209,14 @@ int LightMain( int argc, char **argv )
 			i++;
 		}
 		
+		else if( !strcmp( argv[ i ], "-exposure" ) )
+		{
+			f = atof( argv[ i + 1 ] );
+			lightmapExposure = f;
+			Sys_Printf( "Lighting exposure set to %f\n", lightmapExposure );
+			i++;
+		}
+
 		else if( !strcmp( argv[ i ], "-compensate" ) )
 		{
 			f = atof( argv[ i + 1 ] );
@@ -1867,8 +2248,18 @@ int LightMain( int argc, char **argv )
 			i++;
 		}
 		
+		else if( !strcmp( argv[ i ], "-randomsamples" ) )
+		{
+			lightRandomSamples = qtrue;
+			Sys_Printf( "Random sampling enabled\n", lightRandomSamples );
+		}
+		
 		else if( !strcmp( argv[ i ], "-samples" ) )
 		{
+			if(*argv[i+1] == '+')
+				lightSamplesInsist = qtrue;
+			else
+				lightSamplesInsist = qfalse;
 			lightSamples = atoi( argv[ i + 1 ] );
 			if( lightSamples < 1 )
 				lightSamples = 1;
@@ -1877,6 +2268,18 @@ int LightMain( int argc, char **argv )
 			i++;
 		}
 		
+		else if( !strcmp( argv[ i ], "-samplessearchboxsize" ) )
+		{
+			lightSamplesSearchBoxSize = atoi( argv[ i + 1 ] );
+			if( lightSamplesSearchBoxSize <= 0 )
+				lightSamplesSearchBoxSize = 1;
+			if( lightSamplesSearchBoxSize > 4 )
+				lightSamplesSearchBoxSize = 4; /* more makes no sense */
+			else if( lightSamplesSearchBoxSize != 1 )
+				Sys_Printf( "Adaptive supersampling uses %f times the normal search box size\n", lightSamplesSearchBoxSize );
+			i++;
+		}
+
 		else if( !strcmp( argv[ i ], "-filter" ) )
 		{
 			filter = qtrue;
@@ -1889,12 +2292,6 @@ int LightMain( int argc, char **argv )
 			Sys_Printf( "Dark lightmap seams enabled\n" );
 		}
 		
-
-
-
-
-
-
 		else if( !strcmp( argv[ i ], "-shadeangle" ) )
 		{
 			shadeAngleDegrees = atof( argv[ i + 1 ] );
@@ -1927,13 +2324,28 @@ int LightMain( int argc, char **argv )
 				Sys_Printf( "Approximating lightmaps within a byte tolerance of %d\n", approximateTolerance );
 			i++;
 		}
-		
 		else if( !strcmp( argv[ i ], "-deluxe" ) || !strcmp( argv[ i ], "-deluxemap" ) )
 		{
 			deluxemap = qtrue;
 			Sys_Printf( "Generating deluxemaps for average light direction\n" );
 		}
-		
+		else if( !strcmp( argv[ i ], "-deluxemode" ))
+		{
+			deluxemode = atoi( argv[ i + 1 ] );
+			if (deluxemode == 0 || deluxemode > 1 || deluxemode < 0)
+			{
+				Sys_Printf( "Generating modelspace deluxemaps\n" );
+				deluxemode = 0;
+			}
+			else 
+				Sys_Printf( "Generating tangentspace deluxemaps\n" );
+			i++;
+		}
+		else if( !strcmp( argv[ i ], "-nodeluxe" ) || !strcmp( argv[ i ], "-nodeluxemap" ) )
+		{
+			deluxemap = qfalse;
+			Sys_Printf( "Disabling generating of deluxemaps for average light direction\n" );
+		}
 		else if( !strcmp( argv[ i ], "-external" ) )
 		{
 			externalLightmaps = qtrue;
@@ -1961,6 +2373,23 @@ int LightMain( int argc, char **argv )
 			}
 		}
 		
+		else if( !strcmp( argv[ i ], "-rawlightmapsizelimit" ) )
+		{
+			lmLimitSize = atoi( argv[ i + 1 ] );
+			
+			i++;
+			Sys_Printf( "Raw lightmap size limit set to %d x %d pixels\n", lmLimitSize, lmLimitSize );
+		}
+		
+		else if( !strcmp( argv[ i ], "-lightmapdir" ) )
+		{
+			lmCustomDir = argv[i + 1];
+			i++;
+			Sys_Printf( "Lightmap directory set to %s\n", lmCustomDir );
+			externalLightmaps = qtrue;
+			Sys_Printf( "Storing all lightmaps externally\n" );
+		}
+		
 		/* ydnar: add this to suppress warnings */
 		else if( !strcmp( argv[ i ],  "-custinfoparms") )
 		{
@@ -1981,6 +2410,15 @@ int LightMain( int argc, char **argv )
 			wolfLight = qfalse;
 			Sys_Printf( "Enabling Quake 3 lighting model (nonlinear default)\n" );
 		}
+
+		else if( !strcmp( argv[ i ], "-extradist" ) )
+		{
+			extraDist = atof( argv[ i + 1 ] );
+			if( extraDist < 0 )
+				extraDist = 0;
+			i++;
+			Sys_Printf( "Default extra radius set to %f units\n", extraDist );
+		}
 		
 		else if( !strcmp( argv[ i ], "-sunonly" ) )
 		{
@@ -1999,6 +2437,26 @@ int LightMain( int argc, char **argv )
 			noCollapse = qtrue;
 			Sys_Printf( "Identical lightmap collapsing disabled\n" );
 		}
+
+		else if( !strcmp( argv[ i ], "-nolightmapsearch" ) )
+		{
+			lightmapSearchBlockSize = 1;
+			Sys_Printf( "No lightmap searching - all lightmaps will be sequential\n" );
+		}
+		
+		else if( !strcmp( argv[ i ], "-lightmapsearchpower" ) )
+		{
+			lightmapMergeSize = (game->lightmapSize << atoi(argv[i+1]));
+			++i;
+			Sys_Printf( "Restricted lightmap searching enabled - optimize for lightmap merge power %d (size %d)\n", atoi(argv[i]), lightmapMergeSize );
+		}
+		
+		else if( !strcmp( argv[ i ], "-lightmapsearchblocksize" ) )
+		{
+			lightmapSearchBlockSize = atoi(argv[i+1]);
+			++i;
+			Sys_Printf( "Restricted lightmap searching enabled - block size set to %d\n", lightmapSearchBlockSize );
+		}
 		
 		else if( !strcmp( argv[ i ], "-shade" ) )
 		{
@@ -2151,6 +2609,20 @@ int LightMain( int argc, char **argv )
 			i++;
 			Sys_Printf( "Default lightmap sample size set to %dx%d units\n", sampleSize, sampleSize );
 		}
+		else if( !strcmp( argv[ i ], "-minsamplesize" ) )
+		{
+			minSampleSize = atoi( argv[ i + 1 ] );
+			if( minSampleSize < 1 )
+				minSampleSize = 1;
+			i++;
+			Sys_Printf( "Minimum lightmap sample size set to %dx%d units\n", minSampleSize, minSampleSize );
+		}
+		else if( !strcmp( argv[ i ],  "-samplescale" ) )
+ 		{
+			sampleScale = atoi( argv[ i + 1 ] );
+ 			i++;
+			Sys_Printf( "Lightmaps sample scale set to %d\n", sampleScale);
+ 		}
 		else if( !strcmp( argv[ i ], "-novertex" ) )
 		{
 			noVertexLighting = qtrue;
@@ -2181,16 +2653,52 @@ int LightMain( int argc, char **argv )
 			loMem = qtrue;
 			Sys_Printf( "Enabling low-memory (potentially slower) lighting mode\n" );
 		}
+		else if( !strcmp( argv[ i ], "-lightanglehl" ) )
+		{
+			if( ( atoi( argv[ i + 1 ] ) != 0 ) != lightAngleHL )
+			{
+				lightAngleHL = ( atoi( argv[ i + 1 ] ) != 0 );
+				if( lightAngleHL )
+					Sys_Printf( "Enabling half lambert light angle attenuation\n" );
+				else
+					Sys_Printf( "Disabling half lambert light angle attenuation\n" );
+			}
+		}
 		else if( !strcmp( argv[ i ], "-nostyle" ) || !strcmp( argv[ i ], "-nostyles" ) )
 		{
 			noStyles = qtrue;
 			Sys_Printf( "Disabling lightstyles\n" );
 		}
+		else if( !strcmp( argv[ i ], "-style" ) || !strcmp( argv[ i ], "-styles" ) )
+		{
+			noStyles = qfalse;
+			Sys_Printf( "Enabling lightstyles\n" );
+		}
+		else if( !strcmp( argv[ i ], "-keeplights" ))
+		{
+			keepLights = qtrue;
+			Sys_Printf( "Leaving light entities on map after compile\n" );
+		}
 		else if( !strcmp( argv[ i ], "-cpma" ) )
 		{
 			cpmaHack = qtrue;
 			Sys_Printf( "Enabling Challenge Pro Mode Asstacular Vertex Lighting Mode (tm)\n" );
 		}
+		else if( !strcmp( argv[ i ], "-floodlight" ) )
+		{
+			floodlighty = qtrue;
+			Sys_Printf( "FloodLighting enabled\n" );
+		}
+		else if( !strcmp( argv[ i ], "-debugnormals" ) )
+		{
+			debugnormals = qtrue;
+			Sys_Printf( "DebugNormals enabled\n" );
+		}
+		else if( !strcmp( argv[ i ], "-lowquality" ) )
+		{
+			floodlight_lowquality = qtrue;
+			Sys_Printf( "Low Quality FloodLighting enabled\n" );
+		}
 		
 		/* r7: dirtmapping */
 		else if( !strcmp( argv[ i ], "-dirty" ) )
@@ -2212,6 +2720,7 @@ int LightMain( int argc, char **argv )
 				Sys_Printf( "Enabling randomized dirtmapping\n" );
 			else
 				Sys_Printf( "Enabling ordered dir mapping\n" );
+			i++;
 		}
 		else if( !strcmp( argv[ i ], "-dirtdepth" ) )
 		{
@@ -2219,6 +2728,7 @@ int LightMain( int argc, char **argv )
 			if( dirtDepth <= 0.0f )
 				dirtDepth = 128.0f;
 			Sys_Printf( "Dirtmapping depth set to %.1f\n", dirtDepth );
+			i++;
 		}
 		else if( !strcmp( argv[ i ], "-dirtscale" ) )
 		{
@@ -2226,6 +2736,7 @@ int LightMain( int argc, char **argv )
 			if( dirtScale <= 0.0f )
 				dirtScale = 1.0f;
 			Sys_Printf( "Dirtmapping scale set to %.1f\n", dirtScale );
+			i++;
 		}
 		else if( !strcmp( argv[ i ], "-dirtgain" ) )
 		{
@@ -2233,13 +2744,70 @@ int LightMain( int argc, char **argv )
 			if( dirtGain <= 0.0f )
 				dirtGain = 1.0f;
 			Sys_Printf( "Dirtmapping gain set to %.1f\n", dirtGain );
+			i++;
+		}
+		else if( !strcmp( argv[ i ], "-trianglecheck" ) )
+		{
+			lightmapTriangleCheck = qtrue;
+		}
+		else if( !strcmp( argv[ i ], "-extravisnudge" ) )
+		{
+			lightmapExtraVisClusterNudge = qtrue;
+		}
+		else if( !strcmp( argv[ i ], "-fill" ) )
+		{
+			lightmapFill = qtrue;
+			Sys_Printf( "Filling lightmap colors from surrounding pixels to improve JPEG compression\n" );
 		}
-		
 		/* unhandled args */
 		else
+		{
 			Sys_Printf( "WARNING: Unknown argument \"%s\"\n", argv[ i ] );
+		}
 
 	}
+
+	/* fix up samples count */
+	if(lightRandomSamples)
+	{
+		if(!lightSamplesInsist)
+		{
+			/* approximately match -samples in quality */
+			switch(lightSamples)
+			{
+				/* somewhat okay */
+				case 1:
+				case 2:
+					lightSamples = 16;
+					Sys_Printf( "Adaptive supersampling preset enabled with %d random sample(s) per lightmap texel\n", lightSamples );
+					break;
+
+				/* good */
+				case 3:
+					lightSamples = 64;
+					Sys_Printf( "Adaptive supersampling preset enabled with %d random sample(s) per lightmap texel\n", lightSamples );
+					break;
+
+				/* perfect */
+				case 4:
+					lightSamples = 256;
+					Sys_Printf( "Adaptive supersampling preset enabled with %d random sample(s) per lightmap texel\n", lightSamples );
+					break;
+
+				default: break;
+			}
+		}
+	}
+
+	/* fix up lightmap search power */
+	if(lightmapMergeSize)
+	{
+		lightmapSearchBlockSize = (lightmapMergeSize / lmCustomSize) * (lightmapMergeSize / lmCustomSize);
+		if(lightmapSearchBlockSize < 1)
+			lightmapSearchBlockSize = 1;
+
+		Sys_Printf( "Restricted lightmap searching enabled - block size adjusted to %d\n", lightmapSearchBlockSize );
+	}
 	
 	/* clean up map name */
 	strcpy( source, ExpandArg( argv[ i ] ) );
@@ -2267,11 +2835,14 @@ int LightMain( int argc, char **argv )
 	
 	/* parse bsp entities */
 	ParseEntities();
+
+	/* inject command line parameters */
+	InjectCommandLine(argv, 0, argc - 1);
 	
 	/* load map file */
 	value = ValueForKey( &entities[ 0 ], "_keepLights" );
 	if( value[ 0 ] != '1' )
-		LoadMapFile( mapSource, qtrue );
+		LoadMapFile( mapSource, qtrue, qfalse );
 	
 	/* set the entity/model origins and init yDrawVerts */
 	SetEntityOrigins();
@@ -2279,6 +2850,7 @@ int LightMain( int argc, char **argv )
 	/* ydnar: set up optimization */
 	SetupBrushes();
 	SetupDirt();
+	SetupFloodLight();
 	SetupSurfaceLightmaps();
 	
 	/* initialize the surface facet tracing */
diff --git a/tools/quake3/q3map2/light_bounce.c b/tools/quake3/q3map2/light_bounce.c
index dba5b131..7db00613 100644
--- a/tools/quake3/q3map2/light_bounce.c
+++ b/tools/quake3/q3map2/light_bounce.c
@@ -414,7 +414,7 @@ subdivides a radiosity winding until it is smaller than subdivide, then generate
 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;
+	int				i, style = 0;
 	float			dist, area, value;
 	vec3_t			mins, maxs, normal, d1, d2, cross, color, gradient;
 	light_t			*light, *splash;
@@ -539,7 +539,7 @@ static void RadSubdivideDiffuseLight( int lightmapNum, bspDrawSurface_t *ds, raw
 	light->falloffTolerance = falloffTolerance;
 	
 	/* bouncing light? */
-	if( bouncing == qfalse )
+	if( !bouncing )
 	{
 		/* handle first-pass lights in normal q3a style */
 		value = si->value;
@@ -836,6 +836,9 @@ void RadLight( int num )
 	/* find nodraw bit */
 	contentFlags = surfaceFlags = compileFlags = 0;
 	ApplySurfaceParm( "nodraw", &contentFlags, &surfaceFlags, &compileFlags );
+
+	// jal : avoid bouncing on trans surfaces
+	ApplySurfaceParm( "trans", &contentFlags, &surfaceFlags, &compileFlags );
 	
 	/* early outs? */
 	if( scale <= 0.0f || (si->compileFlags & C_SKY) || si->autosprite ||
diff --git a/tools/quake3/q3map2/light_trace.c b/tools/quake3/q3map2/light_trace.c
index 2a4e7e46..8e79244e 100644
--- a/tools/quake3/q3map2/light_trace.c
+++ b/tools/quake3/q3map2/light_trace.c
@@ -56,7 +56,7 @@ several games based on the Quake III Arena engine, in the form of "Q3Map2."
 #define GROW_TRACE_NODES		16384		//%	16384
 #define GROW_NODE_ITEMS			16			//%	256
 
-#define MAX_TW_VERTS			12
+#define MAX_TW_VERTS			24 // vortex: increased from 12 to 24 for ability co compile some insane maps with large curve count
 
 #define	TRACE_ON_EPSILON		0.1f
 
@@ -73,7 +73,7 @@ traceVert_t;
 typedef struct traceInfo_s
 {
 	shaderInfo_t				*si;
-	int							surfaceNum, castShadows;
+	int							surfaceNum, castShadows, skipGrid;
 }
 traceInfo_t;
 
@@ -144,7 +144,8 @@ static int AddTraceInfo( traceInfo_t *ti )
 	{
 		if( traceInfos[ num ].si == ti->si &&
 			traceInfos[ num ].surfaceNum == ti->surfaceNum &&
-			traceInfos[ num ].castShadows == ti->castShadows )
+			traceInfos[ num ].castShadows == ti->castShadows &&
+			traceInfos[ num ].skipGrid == ti->skipGrid )
 			return num;
 	}
 	
@@ -201,6 +202,9 @@ static int AllocTraceNode( void )
 	memset( &traceNodes[ numTraceNodes ], 0, sizeof( traceNode_t ) );
 	traceNodes[ numTraceNodes ].type = TRACE_LEAF;
 	ClearBounds( traceNodes[ numTraceNodes ].mins, traceNodes[ numTraceNodes ].maxs );
+
+	/* Sys_Printf("alloc node %d\n", numTraceNodes); */
+
 	numTraceNodes++;
 	
 	/* return the count */
@@ -362,7 +366,7 @@ recursively create the initial trace node structure from the bsp tree
 
 static int SetupTraceNodes_r( int bspNodeNum )
 {
-	int				i, nodeNum, bspLeafNum;
+	int				i, nodeNum, bspLeafNum, newNode;
 	bspPlane_t		*plane;
 	bspNode_t 		*bspNode;
 	
@@ -388,15 +392,26 @@ static int SetupTraceNodes_r( int bspNodeNum )
 			bspLeafNum = -bspNode->children[ i ] - 1;
 			
 			/* new code */
-			traceNodes[ nodeNum ].children[ i ] = AllocTraceNode();
+			newNode = AllocTraceNode();
+			traceNodes[ nodeNum ].children[ i ] = newNode;
+			/* have to do this separately, as gcc first executes LHS, then RHS, and if a realloc took place, this fails */
+
 			if( bspLeafs[ bspLeafNum ].cluster == -1 )
 				traceNodes[ traceNodes[ nodeNum ].children[ i ] ].type = TRACE_LEAF_SOLID;
 		}
 		
 		/* normal node */
 		else
-			traceNodes[ nodeNum ].children[ i ] = SetupTraceNodes_r( bspNode->children[ i ] );
+		{
+			newNode = SetupTraceNodes_r( bspNode->children[ i ] );
+			traceNodes[ nodeNum ].children[ i ] = newNode;
+		}
+
+		if(traceNodes[ nodeNum ].children[ i ] == 0)
+			Error( "Invalid tracenode allocated" );
 	}
+
+	/* Sys_Printf("node %d children: %d %d\n", nodeNum, traceNodes[ nodeNum ].children[0], traceNodes[ nodeNum ].children[1]); */
 	
 	/* return node number */
 	return nodeNum;
@@ -506,13 +521,10 @@ void ClipTraceWinding( traceWinding_t *tw, vec4_t plane, traceWinding_t *front,
 					mid.xyz[ k ] = -plane[ 3 ];
 				else
 					mid.xyz[ k ] = a->xyz[ k ] + frac * (b->xyz[ k ] - a->xyz[ k ]);
-				
-				/* set texture coordinates */
-				if( k > 1 )
-					continue;
-				mid.st[ 0 ] = a->st[ 0 ] + frac * (b->st[ 0 ] - a->st[ 0 ]);
-				mid.st[ 1 ] = a->st[ 1 ] + frac * (b->st[ 1 ] - a->st[ 1 ]);
 			}
+			/* set texture coordinates */
+			mid.st[ 0 ] = a->st[ 0 ] + frac * (b->st[ 0 ] - a->st[ 0 ]);
+			mid.st[ 1 ] = a->st[ 1 ] + frac * (b->st[ 1 ] - a->st[ 1 ]);
 			
 			/* copy midpoint to front and back polygons */
 			front->v[ front->numVerts++ ] = mid;
@@ -523,39 +535,6 @@ void ClipTraceWinding( traceWinding_t *tw, vec4_t plane, traceWinding_t *front,
 
 
 
-/*
-FilterPointToTraceNodes_r() - ydnar
-debugging tool
-*/
-
-static int FilterPointToTraceNodes_r( vec3_t pt, int nodeNum )
-{
-	float			dot;
-	traceNode_t		*node;
-	
-	
-	if( nodeNum < 0 || nodeNum >= numTraceNodes )
-		return -1;
-	
-	node = &traceNodes[ nodeNum ];
-	
-	if( node->type >= 0 )
-	{
-		dot = DotProduct( pt, node->plane ) - node->plane[ 3 ];
-		if( dot > -0.001f )
-			FilterPointToTraceNodes_r( pt, node->children[ 0 ] );
-		if( dot < 0.001f )
-			FilterPointToTraceNodes_r( pt, node->children[ 1 ] );
-		return -1;
-	}
-	
-	Sys_Printf( "%d ", nodeNum );
-	
-	return nodeNum;
-}
-
-
-
 /*
 FilterTraceWindingIntoNodes_r() - ydnar
 filters a trace winding into the raytracing tree
@@ -960,6 +939,7 @@ static void PopulateWithBSPModel( bspModel_t *model, m4x4_t transform )
 		ti.si = info->si;
 		ti.castShadows = info->castShadows;
 		ti.surfaceNum = model->firstBSPBrush + i;
+		ti.skipGrid = (ds->surfaceType == MST_PATCH);
 		
 		/* choose which node (normal or skybox) */
 		if( info->parentSurfaceNum >= 0 )
@@ -1129,6 +1109,7 @@ static void PopulateWithPicoModel( int castShadows, picoModel_t *model, m4x4_t t
 		/* setup trace info */
 		ti.castShadows = castShadows;
 		ti.surfaceNum = -1;
+		ti.skipGrid = qtrue; // also ignore picomodels when skipping patches
 		
 		/* setup trace winding */
 		memset( &tw, 0, sizeof( tw ) );
@@ -1240,7 +1221,7 @@ static void PopulateTraceNodes( void )
 			/* external model */
 			default:
 				frame = IntForKey( e, "_frame" );
-				model = LoadModel( (char*) value, frame );
+				model = LoadModel( value, frame );
 				if( model == NULL )
 					continue;
 				PopulateWithPicoModel( castShadows, model, transform );
@@ -1268,7 +1249,7 @@ static void PopulateTraceNodes( void )
 			/* external model */
 			default:
 				frame = IntForKey( e, "_frame2" );
-				model = LoadModel( (char*) value, frame );
+				model = LoadModel( value, frame );
 				if( model == NULL )
 					continue;
 				PopulateWithPicoModel( castShadows, model, transform );
@@ -1412,7 +1393,7 @@ qboolean TraceTriangle( traceInfo_t *ti, traceTriangle_t *tt, trace_t *trace )
 		if( ti->castShadows != 1 )
 			return qfalse;
 	}
-	
+
 	/* receive shadows from same group and worldspawn group */
 	else if( trace->recvShadows > 1 )
 	{
@@ -1428,6 +1409,13 @@ qboolean TraceTriangle( traceInfo_t *ti, traceTriangle_t *tt, trace_t *trace )
 			return qfalse;
 	}
 	
+	/* skip patches when doing the grid (FIXME this is an ugly hack) */
+	if( inGrid )
+	{
+		if (ti->skipGrid)
+			return qfalse;
+	}
+	
 	/* begin calculating determinant - also used to calculate u parameter */
 	CrossProduct( trace->direction, tt->edge2, pvec );
 	
@@ -1487,6 +1475,9 @@ qboolean TraceTriangle( traceInfo_t *ti, traceTriangle_t *tt, trace_t *trace )
 		trace->opaque = qtrue;
 		return qtrue;
 	}
+
+	/* force subsampling because the lighting is texture dependent */
+	trace->forceSubsampling = 1.0;
 	
 	/* try to avoid double shadows near triangle seams */
 	if( u < -ASLF_EPSILON || u > (1.0f + ASLF_EPSILON) ||
@@ -1529,6 +1520,7 @@ qboolean TraceTriangle( traceInfo_t *ti, traceTriangle_t *tt, trace_t *trace )
 	/* check filter for opaque */
 	if( trace->color[ 0 ] <= 0.001f && trace->color[ 1 ] <= 0.001f && trace->color[ 2 ] <= 0.001f )
 	{
+		VectorClear( trace->color );
 		VectorMA( trace->origin, depth, trace->direction, trace->hit );
 		trace->opaque = qtrue;
 		return qtrue;
diff --git a/tools/quake3/q3map2/light_ydnar.c b/tools/quake3/q3map2/light_ydnar.c
index 44d60d8a..f60e6645 100644
--- a/tools/quake3/q3map2/light_ydnar.c
+++ b/tools/quake3/q3map2/light_ydnar.c
@@ -49,6 +49,7 @@ void ColorToBytes( const float *color, byte *colorBytes, float scale )
 	int		i;
 	float	max, gamma;
 	vec3_t	sample;
+	float 	inv, dif;
 	
 	
 	/* ydnar: scaling necessary for simulating r_overbrightBits on external lightmaps */
@@ -72,15 +73,50 @@ void ColorToBytes( const float *color, byte *colorBytes, float scale )
 		/* gamma */
 		sample[ i ] = pow( sample[ i ] / 255.0f, gamma ) * 255.0f;
 	}
-	
-	/* clamp with color normalization */
-	max = sample[ 0 ];
-	if( sample[ 1 ] > max )
-		max = sample[ 1 ];
-	if( sample[ 2 ] > max )
-		max = sample[ 2 ];
-	if( max > 255.0f )
-		VectorScale( sample, (255.0f / max), sample );
+
+	if (lightmapExposure == 1)
+	{
+		/* clamp with color normalization */
+		max = sample[ 0 ];
+		if( sample[ 1 ] > max )
+			max = sample[ 1 ];
+		if( sample[ 2 ] > max )
+			max = sample[ 2 ];
+		if( max > 255.0f )
+			VectorScale( sample, (255.0f / max), sample );
+	}
+	else
+	{
+		if (lightmapExposure==0)
+		{
+			lightmapExposure=1.0f;
+		}
+		inv=1.f/lightmapExposure;
+		//Exposure
+
+		max = sample[ 0 ];
+		if( sample[ 1 ] > max )
+			max = sample[ 1 ];
+		if( sample[ 2 ] > max )
+			max = sample[ 2 ];
+
+		dif = (1-  exp(-max * inv) )  *  255;
+
+		if (max >0)
+		{
+			dif = dif / max;
+		}
+		else
+		{
+			dif = 0;
+		}
+
+		for (i=0;i<3;i++)
+		{
+			sample[i]*=dif;
+		}
+	}
+
 	
 	/* compensate for ingame overbrighting/bitshifting */
 	VectorScale( sample, (1.0f / lightmapCompensate), sample );
@@ -384,7 +420,7 @@ maps a luxel for triangle bv at
 #define NUDGE			0.5f
 #define BOGUS_NUDGE		-99999.0f
 
-static int MapSingleLuxel( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv, vec4_t plane, float pass, vec3_t stv[ 3 ], vec3_t ttv[ 3 ] )
+static int MapSingleLuxel( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv, vec4_t plane, float pass, vec3_t stv[ 3 ], vec3_t ttv[ 3 ], vec3_t worldverts[ 3 ] )
 {
 	int				i, x, y, numClusters, *clusters, pointCluster, *cluster;
 	float			*luxel, *origin, *normal, d, lightmapSampleOffset;
@@ -392,6 +428,12 @@ static int MapSingleLuxel( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t
 	vec3_t			pNormal;
 	vec3_t			vecs[ 3 ];
 	vec3_t			nudged;
+	vec3_t			cverts[ 3 ];
+	vec3_t			temp;
+	vec4_t			sideplane, hostplane;
+	vec3_t			origintwo;
+	int				j, next;
+	float			e;
 	float			*nudge;
 	static float	nudges[][ 2 ] =
 					{
@@ -485,6 +527,51 @@ static int MapSingleLuxel( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t
 	/* non axial lightmap projection (explicit xyz) */
 	else
 		VectorCopy( dv->xyz, origin );
+
+	//////////////////////
+	//27's test to make sure samples stay within the triangle boundaries
+	//1) Test the sample origin to see if it lays on the wrong side of any edge (x/y)
+	//2) if it does, nudge it onto the correct side.
+
+	if (worldverts!=NULL && lightmapTriangleCheck)
+	{
+		for (j=0;j<3;j++)
+		{
+			VectorCopy(worldverts[j],cverts[j]);
+		}
+		PlaneFromPoints(hostplane,cverts[0],cverts[1],cverts[2]);
+
+		for (j=0;j<3;j++)
+		{
+			for (i=0;i<3;i++)
+			{
+				//build plane using 2 edges and a normal
+				next=(i+1)%3;
+
+				VectorCopy(cverts[next],temp);
+				VectorAdd(temp,hostplane,temp);
+				PlaneFromPoints(sideplane,cverts[i],cverts[ next ], temp);
+
+				//planetest sample point
+				e=DotProduct(origin,sideplane);
+				e=e-sideplane[3];
+				if (e>0)
+				{
+					//we're bad.
+					//VectorClear(origin);
+					//Move the sample point back inside triangle bounds
+					origin[0]-=sideplane[0]*(e+1);
+					origin[1]-=sideplane[1]*(e+1);
+					origin[2]-=sideplane[2]*(e+1);
+#ifdef DEBUG_27_1
+					VectorClear(origin);
+#endif
+				}
+			}
+		}
+	}
+
+	////////////////////////
 	
 	/* planar surfaces have precalculated lightmap vectors for nudging */
 	if( lm->plane != NULL )
@@ -516,8 +603,16 @@ static int MapSingleLuxel( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t
 	else
 		origin[ lm->axisNum ] += lightmapSampleOffset;
 	
+	VectorCopy(origin,origintwo);
+	if(lightmapExtraVisClusterNudge)
+	{
+		origintwo[0]+=vecs[2][0];
+		origintwo[1]+=vecs[2][1];
+		origintwo[2]+=vecs[2][2];
+	}
+
 	/* get cluster */
-	pointCluster = ClusterForPointExtFilter( origin, LUXEL_EPSILON, numClusters, clusters );
+	pointCluster = ClusterForPointExtFilter( origintwo, LUXEL_EPSILON, numClusters, clusters );
 	
 	/* another retarded hack, storing nudge count in luxel[ 1 ] */
 	luxel[ 1 ] = 0.0f;	
@@ -533,14 +628,14 @@ static int MapSingleLuxel( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t
 			for( i = 0; i < 3; i++ )
 			{
 				/* set nudged point*/
-				nudged[ i ] = origin[ i ] + (nudge[ 0 ] * vecs[ 0 ][ i ]) + (nudge[ 1 ] * vecs[ 1 ][ i ]);
+				nudged[ i ] = origintwo[ i ] + (nudge[ 0 ] * vecs[ 0 ][ i ]) + (nudge[ 1 ] * vecs[ 1 ][ i ]);
 			}
 			nudge += 2;
 			
 			/* get pvs cluster */
 			pointCluster = ClusterForPointExtFilter( nudged, LUXEL_EPSILON, numClusters, clusters ); //% + 0.625 );
-			if( pointCluster >= 0 )	
-				VectorCopy( nudged, origin );
+			if( pointCluster >= 0 )
+   				VectorCopy( nudged, origin );
 			luxel[ 1 ] += 1.0f;
 		}
 	}
@@ -597,7 +692,7 @@ recursively subdivides a triangle until its edges are shorter
 than the distance between two luxels (thanks jc :)
 */
 
-static void MapTriangle_r( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv[ 3 ], vec4_t plane, vec3_t stv[ 3 ], vec3_t ttv[ 3 ] )
+static void MapTriangle_r( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv[ 3 ], vec4_t plane, vec3_t stv[ 3 ], vec3_t ttv[ 3 ], vec3_t worldverts[ 3 ] )
 {
 	bspDrawVert_t	mid, *dv2[ 3 ];
 	int				max;
@@ -645,7 +740,7 @@ static void MapTriangle_r( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t
 	
 	/* split the longest edge and map it */
 	LerpDrawVert( dv[ max ], dv[ (max + 1) % 3 ], &mid );
-	MapSingleLuxel( lm, info, &mid, plane, 1, stv, ttv );
+	MapSingleLuxel( lm, info, &mid, plane, 1, stv, ttv, worldverts );
 	
 	/* push the point up a little bit to account for fp creep (fixme: revisit this) */
 	//%	VectorMA( mid.xyz, 2.0f, mid.normal, mid.xyz );
@@ -653,12 +748,12 @@ static void MapTriangle_r( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t
 	/* recurse to first triangle */
 	VectorCopy( dv, dv2 );
 	dv2[ max ] = &mid;
-	MapTriangle_r( lm, info, dv2, plane, stv, ttv );
+	MapTriangle_r( lm, info, dv2, plane, stv, ttv, worldverts );
 	
 	/* recurse to second triangle */
 	VectorCopy( dv, dv2 );
 	dv2[ (max + 1) % 3 ] = &mid;
-	MapTriangle_r( lm, info, dv2, plane, stv, ttv );
+	MapTriangle_r( lm, info, dv2, plane, stv, ttv, worldverts );
 }
 
 
@@ -674,6 +769,7 @@ static qboolean MapTriangle( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert
 	int				i;
 	vec4_t			plane;
 	vec3_t			*stv, *ttv, stvStatic[ 3 ], ttvStatic[ 3 ];
+	vec3_t			worldverts[ 3 ];
 	
 	
 	/* get plane if possible */
@@ -699,16 +795,20 @@ static qboolean MapTriangle( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert
 		ttv = NULL;
 	}
 	
+	VectorCopy( dv[ 0 ]->xyz, worldverts[ 0 ] );
+	VectorCopy( dv[ 1 ]->xyz, worldverts[ 1 ] );
+	VectorCopy( dv[ 2 ]->xyz, worldverts[ 2 ] );
+
 	/* map the vertexes */
-	MapSingleLuxel( lm, info, dv[ 0 ], plane, 1, stv, ttv );
-	MapSingleLuxel( lm, info, dv[ 1 ], plane, 1, stv, ttv );
-	MapSingleLuxel( lm, info, dv[ 2 ], plane, 1, stv, ttv );
+	MapSingleLuxel( lm, info, dv[ 0 ], plane, 1, stv, ttv, worldverts );
+	MapSingleLuxel( lm, info, dv[ 1 ], plane, 1, stv, ttv, worldverts );
+	MapSingleLuxel( lm, info, dv[ 2 ], plane, 1, stv, ttv, worldverts );
 	
 	/* 2002-11-20: prefer axial triangle edges */
 	if( mapNonAxial )
 	{
 		/* subdivide the triangle */
-		MapTriangle_r( lm, info, dv, plane, stv, ttv );
+		MapTriangle_r( lm, info, dv, plane, stv, ttv, worldverts );
 		return qtrue;
 	}
 	
@@ -730,7 +830,7 @@ static qboolean MapTriangle( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert
 			dv2[ 2 ] = dv[ (i + 1) % 3 ];
 			
 			/* map the degenerate triangle */
-			MapTriangle_r( lm, info, dv2, plane, stv, ttv );
+			MapTriangle_r( lm, info, dv2, plane, stv, ttv, worldverts );
 		}
 	}
 	
@@ -792,8 +892,8 @@ static void MapQuad_r( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv
 	LerpDrawVert( dv[ max + 2 ], dv[ (max + 3) % 4 ], &mid[ 1 ] );
 	
 	/* map the vertexes */
-	MapSingleLuxel( lm, info, &mid[ 0 ], plane, 1, stv, ttv );
-	MapSingleLuxel( lm, info, &mid[ 1 ], plane, 1, stv, ttv );
+	MapSingleLuxel( lm, info, &mid[ 0 ], plane, 1, stv, ttv, NULL );
+	MapSingleLuxel( lm, info, &mid[ 1 ], plane, 1, stv, ttv, NULL );
 	
 	/* 0 and 2 */
 	if( max == 0 )
@@ -878,10 +978,10 @@ static qboolean MapQuad( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *
 	}
 	
 	/* map the vertexes */
-	MapSingleLuxel( lm, info, dv[ 0 ], plane, 1, stv, ttv );
-	MapSingleLuxel( lm, info, dv[ 1 ], plane, 1, stv, ttv );
-	MapSingleLuxel( lm, info, dv[ 2 ], plane, 1, stv, ttv );
-	MapSingleLuxel( lm, info, dv[ 3 ], plane, 1, stv, ttv );
+	MapSingleLuxel( lm, info, dv[ 0 ], plane, 1, stv, ttv, NULL );
+	MapSingleLuxel( lm, info, dv[ 1 ], plane, 1, stv, ttv, NULL );
+	MapSingleLuxel( lm, info, dv[ 2 ], plane, 1, stv, ttv, NULL );
+	MapSingleLuxel( lm, info, dv[ 3 ], plane, 1, stv, ttv, NULL );
 	
 	/* subdivide the quad */
 	MapQuad_r( lm, info, dv, plane, stv, ttv );
@@ -1054,13 +1154,18 @@ void MapRawLightmap( int rawLightmapNum )
 						if( MapQuad( lm, info, dv ) )
 							continue;
 						
-						/* get drawverts and map first triangle */
-						MapTriangle( lm, info, dv, mapNonAxial );
-						
-						/* get drawverts and map second triangle */
-						dv[ 1 ] = &verts[ pw[ r + 2 ] ];
-						dv[ 2 ] = &verts[ pw[ r + 3 ] ];
-						MapTriangle( lm, info, dv, mapNonAxial );
+						for( mapNonAxial = 0; mapNonAxial < 2; mapNonAxial++ )
+						{
+							/* get drawverts and map first triangle */
+							dv[ 1 ] = &verts[ pw[ r + 1 ] ];
+							dv[ 2 ] = &verts[ pw[ r + 2 ] ];
+							MapTriangle( lm, info, dv, mapNonAxial );
+							
+							/* get drawverts and map second triangle */
+							dv[ 1 ] = &verts[ pw[ r + 2 ] ];
+							dv[ 2 ] = &verts[ pw[ r + 3 ] ];
+							MapTriangle( lm, info, dv, mapNonAxial );
+						}
 					}
 				}
 				
@@ -1173,7 +1278,7 @@ void MapRawLightmap( int rawLightmapNum )
 					continue;
 				
 				/* map the fake vert */
-				MapSingleLuxel( lm, NULL, &fake, lm->plane, pass, NULL, NULL );
+				MapSingleLuxel( lm, NULL, &fake, lm->plane, pass, NULL, NULL, NULL );
 			}
 		}
 	}
@@ -1325,7 +1430,7 @@ float DirtForSample( trace_t *trace )
 	VectorCopy( trace->normal, normal );
 	
 	/* check if the normal is aligned to the world-up */
-	if( normal[ 0 ] == 0.0f && normal[ 1 ] == 0.0f )
+	if( normal[ 0 ] == 0.0f && normal[ 1 ] == 0.0f && ( normal[ 2 ] == 1.0f || normal[ 2 ] == -1.0f ) )
 	{
 		if( normal[ 2 ] == 1.0f )		
 		{
@@ -1371,7 +1476,7 @@ float DirtForSample( trace_t *trace )
 			
 			/* trace */
 			TraceLine( trace );
-			if( trace->opaque )
+			if( trace->opaque && !(trace->compileFlags & C_SKY) )
 			{
 				VectorSubtract( trace->hit, trace->origin, displacement );
 				gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
@@ -1446,7 +1551,8 @@ void DirtyRawLightmap( int rawLightmapNum )
 	rawLightmap_t		*lm;
 	surfaceInfo_t		*info;
 	trace_t				trace;
-	
+	qboolean			noDirty;
+
 	
 	/* bail if this number exceeds the number of raw lightmaps */
 	if( rawLightmapNum >= numRawLightmaps )
@@ -1461,7 +1567,7 @@ void DirtyRawLightmap( int rawLightmapNum )
 	trace.recvShadows = lm->recvShadows;
 	trace.numSurfaces = lm->numLightSurfaces;
 	trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];
-	trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
+	trace.inhibitRadius = 0.0f;
 	trace.testAll = qfalse;
 	
 	/* twosided lighting (may or may not be a good idea for lightmapped stuff) */
@@ -1478,6 +1584,20 @@ void DirtyRawLightmap( int rawLightmapNum )
 			break;
 		}
 	}
+
+	noDirty = qfalse;
+	for( i = 0; i < trace.numSurfaces; i++ )
+	{
+		/* get surface */
+		info = &surfaceInfos[ trace.surfaces[ i ] ];
+
+		/* check twosidedness */
+		if( info->si->noDirty )
+		{
+			noDirty = qtrue;
+			break;
+		}
+	}
 	
 	/* gather dirt */
 	for( y = 0; y < lm->sh; y++ )
@@ -1496,6 +1616,13 @@ void DirtyRawLightmap( int rawLightmapNum )
 			/* only look at mapped luxels */
 			if( *cluster < 0 )
 				continue;
+
+			/* don't apply dirty on this surface */
+			if( noDirty )
+			{
+				*dirt = 1.0f;
+				continue;
+			}
 			
 			/* copy to trace */
 			trace.cluster = *cluster;
@@ -1592,7 +1719,9 @@ static qboolean SubmapRawLuxel( rawLightmap_t *lm, int x, int y, float bx, float
 		//%	normal2 = SUPER_NORMAL( x, y );
 	}
 	else
-		Sys_Printf( "WARNING: Spurious lightmap S vector\n" );
+	{
+		Error( "Spurious lightmap S vector\n" );
+	}
 	
 	VectorSubtract( origin2, origin, originVecs[ 0 ] );
 	//%	VectorSubtract( normal2, normal, normalVecs[ 0 ] );
@@ -1651,14 +1780,15 @@ SubsampleRawLuxel_r()
 recursively subsamples a luxel until its color gradient is low enough or subsampling limit is reached
 */
 
-static void SubsampleRawLuxel_r( rawLightmap_t *lm, trace_t *trace, vec3_t sampleOrigin, int x, int y, float bias, float *lightLuxel )
+static void SubsampleRawLuxel_r( rawLightmap_t *lm, trace_t *trace, vec3_t sampleOrigin, int x, int y, float bias, float *lightLuxel, float *lightDeluxel )
 {
 	int			b, samples, mapped, lighted;
 	int			cluster[ 4 ];
 	vec4_t		luxel[ 4 ];
+	vec3_t		deluxel[ 3 ];
 	vec3_t		origin[ 4 ], normal[ 4 ];
 	float		biasDirs[ 4 ][ 2 ] = { { -1.0f, -1.0f }, { 1.0f, -1.0f }, { -1.0f, 1.0f }, { 1.0f, 1.0f } };
-	vec3_t		color, total;
+	vec3_t		color, direction, total;
 	
 	
 	/* limit check */
@@ -1695,9 +1825,20 @@ static void SubsampleRawLuxel_r( rawLightmap_t *lm, trace_t *trace, vec3_t sampl
 		/* sample light */
 
 		LightContributionToSample( trace );
+		if(trace->forceSubsampling > 1.0f)
+		{
+			/* alphashadow: we subsample as deep as we can */
+			++lighted;
+			++mapped;
+			++mapped;
+		}
 		
 		/* add to totals (fixme: make contrast function) */
 		VectorCopy( trace->color, luxel[ b ] );
+		if(lightDeluxel)
+		{
+			VectorCopy( trace->directionContribution, deluxel[ b ] );
+		}
 		VectorAdd( total, trace->color, total );
 		if( (luxel[ b ][ 0 ] + luxel[ b ][ 1 ] + luxel[ b ][ 2 ]) > 0.0f )
 			lighted++;
@@ -1712,7 +1853,7 @@ static void SubsampleRawLuxel_r( rawLightmap_t *lm, trace_t *trace, vec3_t sampl
 		{
 			if( cluster[ b ] < 0 )
 				continue;
-			SubsampleRawLuxel_r( lm, trace, origin[ b ], x, y, (bias * 0.25f), luxel[ b ] );
+			SubsampleRawLuxel_r( lm, trace, origin[ b ], x, y, (bias * 0.5f), luxel[ b ], lightDeluxel ? deluxel[ b ] : NULL );
 		}
 	}
 	
@@ -1720,12 +1861,20 @@ static void SubsampleRawLuxel_r( rawLightmap_t *lm, trace_t *trace, vec3_t sampl
 	//%	VectorClear( color );
 	//%	samples = 0;
 	VectorCopy( lightLuxel, color );
+	if(lightDeluxel)
+	{
+		VectorCopy( lightDeluxel, direction );
+	}
 	samples = 1;
 	for( b = 0; b < 4; b++ )
 	{
 		if( cluster[ b ] < 0 )
 			continue;
 		VectorAdd( color, luxel[ b ], color );
+		if(lightDeluxel)
+		{
+			VectorAdd( direction, deluxel[ b ], direction );
+		}
 		samples++;
 	}
 	
@@ -1736,10 +1885,85 @@ static void SubsampleRawLuxel_r( rawLightmap_t *lm, trace_t *trace, vec3_t sampl
 		color[ 0 ] /= samples;
 		color[ 1 ] /= samples;
 		color[ 2 ] /= samples;
-		
+
 		/* add to color */
 		VectorCopy( color, lightLuxel );
 		lightLuxel[ 3 ] += 1.0f;
+
+		if(lightDeluxel)
+		{
+			direction[ 0 ] /= samples;
+			direction[ 1 ] /= samples;
+			direction[ 2 ] /= samples;
+			VectorCopy( direction, lightDeluxel );
+		}
+	}
+}
+
+/* A mostly Gaussian-like bounded random distribution (sigma is expected standard deviation) */
+static void GaussLikeRandom(float sigma, float *x, float *y)
+{
+	float r;
+	r = Random() * 2 * Q_PI;
+	*x = sigma * 2.73861278752581783822 * cos(r);
+	*y = sigma * 2.73861278752581783822 * sin(r);
+	r = Random();
+	r = 1 - sqrt(r);
+	r = 1 - sqrt(r);
+	*x *= r;
+	*y *= r;
+}
+static void RandomSubsampleRawLuxel( rawLightmap_t *lm, trace_t *trace, vec3_t sampleOrigin, int x, int y, float bias, float *lightLuxel, float *lightDeluxel )
+{
+	int			b, mapped;
+	int			cluster;
+	vec3_t		origin, normal;
+	vec3_t		total, totaldirection;
+	float		dx, dy;
+	
+	VectorClear( total );
+	VectorClear( totaldirection );
+	mapped = 0;
+	for(b = 0; b < lightSamples; ++b)
+	{
+		/* set origin */
+		VectorCopy( sampleOrigin, origin );
+		GaussLikeRandom(bias, &dx, &dy);
+
+		/* calculate position */
+		if( !SubmapRawLuxel( lm, x, y, dx, dy, &cluster, origin, normal ) )
+		{
+			cluster = -1;
+			continue;
+		}
+		mapped++;
+
+		trace->cluster = cluster;
+		VectorCopy( origin, trace->origin );
+		VectorCopy( normal, trace->normal );
+
+		LightContributionToSample( trace );
+		VectorAdd( total, trace->color, total );
+		if(lightDeluxel)
+		{
+			VectorAdd( totaldirection, trace->directionContribution, totaldirection );
+		}
+	}
+
+	/* add to luxel */
+	if( mapped > 0 )
+	{
+		/* average */
+		lightLuxel[ 0 ] = total[ 0 ] / mapped;
+		lightLuxel[ 1 ] = total[ 1 ] / mapped;
+		lightLuxel[ 2 ] = total[ 2 ] / mapped;
+
+		if(lightDeluxel)
+		{
+			lightDeluxel[ 0 ] = totaldirection[ 0 ] / mapped;
+			lightDeluxel[ 1 ] = totaldirection[ 1 ] / mapped;
+			lightDeluxel[ 2 ] = totaldirection[ 2 ] / mapped;
+		}
 	}
 }
 
@@ -1752,18 +1976,21 @@ illuminates the luxels
 
 #define STACK_LL_SIZE			(SUPER_LUXEL_SIZE * 64 * 64)
 #define LIGHT_LUXEL( x, y )		(lightLuxels + ((((y) * lm->sw) + (x)) * SUPER_LUXEL_SIZE))
+#define LIGHT_DELUXEL( x, y )		(lightDeluxels + ((((y) * lm->sw) + (x)) * SUPER_DELUXEL_SIZE))
 
 void IlluminateRawLightmap( int rawLightmapNum )
 {
-	int					i, t, x, y, sx, sy, size, llSize, luxelFilterRadius, lightmapNum;
+	int					i, t, x, y, sx, sy, size, luxelFilterRadius, lightmapNum;
 	int					*cluster, *cluster2, mapped, lighted, totalLighted;
+	size_t					llSize, ldSize;
 	rawLightmap_t		*lm;
 	surfaceInfo_t		*info;
 	qboolean			filterColor, filterDir;
 	float				brightness;
 	float				*origin, *normal, *dirt, *luxel, *luxel2, *deluxel, *deluxel2;
-	float				*lightLuxels, *lightLuxel, samples, filterRadius, weight;
-	vec3_t				color, averageColor, averageDir, total, temp, temp2;
+	unsigned char			*flag;
+	float				*lightLuxels, *lightDeluxels, *lightLuxel, *lightDeluxel, samples, filterRadius, weight;
+	vec3_t				color, direction, averageColor, averageDir, total, temp, temp2;
 	float				tests[ 4 ][ 2 ] = { { 0.0f, 0 }, { 1, 0 }, { 0, 1 }, { 1, 1 } };
 	trace_t				trace;
 	float				stackLightLuxels[ STACK_LL_SIZE ];
@@ -1877,10 +2104,15 @@ void IlluminateRawLightmap( int rawLightmapNum )
 	{
 		/* allocate temporary per-light luxel storage */
 		llSize = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
+		ldSize = lm->sw * lm->sh * SUPER_DELUXEL_SIZE * sizeof( float );
 		if( llSize <= (STACK_LL_SIZE * sizeof( float )) )
 			lightLuxels = stackLightLuxels;
 		else
 			lightLuxels = safe_malloc( llSize );
+		if(deluxemap)
+			lightDeluxels = safe_malloc( ldSize );
+		else
+			lightDeluxels = NULL;
 		
 		/* clear luxels */
 		//%	memset( lm->superLuxels[ 0 ], 0, llSize );
@@ -1905,7 +2137,15 @@ void IlluminateRawLightmap( int rawLightmapNum )
 				{
 					VectorCopy( ambientColor, luxel );
 					if( deluxemap )
-						VectorScale( normal, 0.00390625f, deluxel );
+					{
+						brightness = RGBTOGRAY( ambientColor ) * ( 1.0f/255.0f );
+
+						// use AT LEAST this amount of contribution from ambient for the deluxemap, fixes points that receive ZERO light
+						if(brightness < 0.00390625f)
+							brightness = 0.00390625f;
+
+						VectorScale( normal, brightness, deluxel );
+					}
 					luxel[ 3 ] = 1.0f;
 				}
 			}
@@ -1946,8 +2186,31 @@ void IlluminateRawLightmap( int rawLightmapNum )
 			
 			/* setup */
 			memset( lightLuxels, 0, llSize );
+			if(deluxemap)
+				memset( lightDeluxels, 0, ldSize );
 			totalLighted = 0;
 			
+			/* determine filter radius */
+			filterRadius = lm->filterRadius > trace.light->filterRadius
+				? lm->filterRadius
+				: trace.light->filterRadius;
+			if( filterRadius < 0.0f )
+				filterRadius = 0.0f;
+			
+			/* set luxel filter radius */
+			luxelFilterRadius = superSample * filterRadius / lm->sampleSize;
+			if( luxelFilterRadius == 0 && (filterRadius > 0.0f || filter) )
+				luxelFilterRadius = 1;
+
+			/* allocate sampling flags storage */
+			if((lightSamples > 1 || lightRandomSamples) && luxelFilterRadius == 0)
+			{
+				size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( unsigned char );
+				if(lm->superFlags == NULL)
+					lm->superFlags = safe_malloc( size );
+				memset( (void *) lm->superFlags, 0, size );
+			}
+
 			/* initial pass, one sample per luxel */
 			for( y = 0; y < lm->sh; y++ )
 			{
@@ -1960,34 +2223,49 @@ void IlluminateRawLightmap( int rawLightmapNum )
 					
 					/* get particulars */
 					lightLuxel = LIGHT_LUXEL( x, y );
-					deluxel = SUPER_DELUXEL( x, y );
+					lightDeluxel = LIGHT_DELUXEL( x, y );
 					origin = SUPER_ORIGIN( x, y );
 					normal = SUPER_NORMAL( x, y );
-					
-					/* set contribution count */
-					lightLuxel[ 3 ] = 1.0f;
-					
-					/* setup trace */
-					trace.cluster = *cluster;
-					VectorCopy( origin, trace.origin );
-					VectorCopy( normal, trace.normal );
-					
-					/* get light for this sample */
-					LightContributionToSample( &trace );
-					VectorCopy( trace.color, lightLuxel );
-					
-					/* add to count */
-					if( trace.color[ 0 ] || trace.color[ 1 ] || trace.color[ 2 ] )
+					flag = SUPER_FLAG( x, y );
+
+#if 0
+					////////// 27's temp hack for testing edge clipping ////
+					if( origin[0]==0 && origin[1]==0 && origin[2]==0 )
+					{
+						lightLuxel[ 1 ] = 255;
+						lightLuxel[ 3 ] = 1.0f;
 						totalLighted++;
-					
-					/* add to light direction map (fixme: use luxel normal as starting point for deluxel?) */
-					if( deluxemap )
+					}
+					else
+#endif
 					{
-						/* color to grayscale (photoshop rgb weighting) */
-						brightness = trace.color[ 0 ] * 0.3f + trace.color[ 1 ] * 0.59f + trace.color[ 2 ] * 0.11f;
-						brightness *= (1.0 / 255.0);
-						VectorScale( trace.direction, brightness, trace.direction );
-						VectorAdd( deluxel, trace.direction, deluxel );
+						/* set contribution count */
+						lightLuxel[ 3 ] = 1.0f;
+
+						/* setup trace */
+						trace.cluster = *cluster;
+						VectorCopy( origin, trace.origin );
+						VectorCopy( normal, trace.normal );
+
+						/* get light for this sample */
+						LightContributionToSample( &trace );
+						VectorCopy( trace.color, lightLuxel );
+
+						/* add the contribution to the deluxemap */
+						if( deluxemap )
+						{
+							VectorCopy( trace.directionContribution, lightDeluxel );
+						}
+
+						/* check for evilness */
+						if(trace.forceSubsampling > 1.0f && (lightSamples > 1 || lightRandomSamples) && luxelFilterRadius == 0)
+						{
+							totalLighted++;
+							*flag |= FLAG_FORCE_SUBSAMPLING; /* force */
+						}
+						/* add to count */
+						else if( trace.color[ 0 ] || trace.color[ 1 ] || trace.color[ 2 ] )
+							totalLighted++;
 					}
 				}
 			}
@@ -1996,21 +2274,9 @@ void IlluminateRawLightmap( int rawLightmapNum )
 			if( totalLighted == 0 )
 				continue;
 			
-			/* determine filter radius */
-			filterRadius = lm->filterRadius > trace.light->filterRadius
-				? lm->filterRadius
-				: trace.light->filterRadius;
-			if( filterRadius < 0.0f )
-				filterRadius = 0.0f;
-			
-			/* set luxel filter radius */
-			luxelFilterRadius = superSample * filterRadius / lm->sampleSize;
-			if( luxelFilterRadius == 0 && (filterRadius > 0.0f || filter) )
-				luxelFilterRadius = 1;
-			
 			/* secondary pass, adaptive supersampling (fixme: use a contrast function to determine if subsampling is necessary) */
 			/* 2003-09-27: changed it so filtering disamples supersampling, as it would waste time */
-			if( lightSamples > 1 && luxelFilterRadius == 0 )
+			if( (lightSamples > 1 || lightRandomSamples) && luxelFilterRadius == 0 )
 			{
 				/* walk luxels */
 				for( y = 0; y < (lm->sh - 1); y++ )
@@ -2036,6 +2302,14 @@ void IlluminateRawLightmap( int rawLightmapNum )
 							mapped++;
 							
 							/* get luxel */
+							flag = SUPER_FLAG( sx, sy );
+							if(*flag & FLAG_FORCE_SUBSAMPLING)
+							{
+								/* force a lighted/mapped discrepancy so we subsample */
+								++lighted;
+								++mapped;
+								++mapped;
+							}
 							lightLuxel = LIGHT_LUXEL( sx, sy );
 							VectorAdd( total, lightLuxel, total );
 							if( (lightLuxel[ 0 ] + lightLuxel[ 1 ] + lightLuxel[ 2 ]) > 0.0f )
@@ -2059,7 +2333,11 @@ void IlluminateRawLightmap( int rawLightmapNum )
 								cluster = SUPER_CLUSTER( sx, sy );
 								if( *cluster < 0 )
 									continue;
+								flag = SUPER_FLAG( sx, sy );
+								if(*flag & FLAG_ALREADY_SUBSAMPLED) // already subsampled
+									continue;
 								lightLuxel = LIGHT_LUXEL( sx, sy );
+								lightDeluxel = LIGHT_DELUXEL( sx, sy );
 								origin = SUPER_ORIGIN( sx, sy );
 								
 								/* only subsample shadowed luxels */
@@ -2067,7 +2345,12 @@ void IlluminateRawLightmap( int rawLightmapNum )
 								//%		continue;
 								
 								/* subsample it */
-								SubsampleRawLuxel_r( lm, &trace, origin, sx, sy, 0.25f, lightLuxel );
+								if(lightRandomSamples)
+									RandomSubsampleRawLuxel( lm, &trace, origin, sx, sy, 0.5f * lightSamplesSearchBoxSize, lightLuxel, deluxemap ? lightDeluxel : NULL );
+								else
+									SubsampleRawLuxel_r( lm, &trace, origin, sx, sy, 0.25f * lightSamplesSearchBoxSize, lightLuxel, deluxemap ? lightDeluxel : NULL );
+
+								*flag |= FLAG_ALREADY_SUBSAMPLED;
 								
 								/* debug code to colorize subsampled areas to yellow */
 								//%	luxel = SUPER_LUXEL( lightmapNum, sx, sy );
@@ -2109,7 +2392,7 @@ void IlluminateRawLightmap( int rawLightmapNum )
 				lm->superLuxels[ lightmapNum ] = safe_malloc( size );
 				memset( lm->superLuxels[ lightmapNum ], 0, size );
 			}
-			
+
 			/* set style */
 			if( lightmapNum > 0 )
 			{
@@ -2133,6 +2416,7 @@ void IlluminateRawLightmap( int rawLightmapNum )
 					{
 						/* setup */
 						VectorClear( averageColor );
+						VectorClear( averageDir );
 						samples = 0.0f;
 						
 						/* cheaper distance-based filtering */
@@ -2151,6 +2435,7 @@ void IlluminateRawLightmap( int rawLightmapNum )
 								if( *cluster < 0 )
 									continue;
 								lightLuxel = LIGHT_LUXEL( sx, sy );
+								lightDeluxel = LIGHT_DELUXEL( sx, sy );
 								
 								/* create weight */
 								weight = (abs( sx - x ) == luxelFilterRadius ? 0.5f : 1.0f);
@@ -2159,6 +2444,11 @@ void IlluminateRawLightmap( int rawLightmapNum )
 								/* scale luxel by filter weight */
 								VectorScale( lightLuxel, weight, color );
 								VectorAdd( averageColor, color, averageColor );
+								if(deluxemap)
+								{
+									VectorScale( lightDeluxel, weight, direction );
+									VectorAdd( averageDir, direction, averageDir );
+								}
 								samples += weight;
 							}
 						}
@@ -2186,6 +2476,15 @@ void IlluminateRawLightmap( int rawLightmapNum )
 							luxel[ 1 ] += averageColor[ 1 ] / samples;
 							luxel[ 2 ] += averageColor[ 2 ] / samples;
 						}
+						
+						if(deluxemap)
+						{
+							/* scale into luxel */
+							deluxel = SUPER_DELUXEL( x, y );
+							deluxel[ 0 ] += averageDir[ 0 ] / samples;
+							deluxel[ 1 ] += averageDir[ 1 ] / samples;
+							deluxel[ 2 ] += averageDir[ 2 ] / samples;
+						}
 					}
 					
 					/* single sample */
@@ -2193,7 +2492,9 @@ void IlluminateRawLightmap( int rawLightmapNum )
 					{
 						/* get particulars */
 						lightLuxel = LIGHT_LUXEL( x, y );
+						lightDeluxel = LIGHT_DELUXEL( x, y );
 						luxel = SUPER_LUXEL( lightmapNum, x, y );
+						deluxel = SUPER_DELUXEL( x, y );
 						
 						/* handle negative light */
 						if( trace.light->flags & LIGHT_NEGATIVE )
@@ -2209,6 +2510,11 @@ void IlluminateRawLightmap( int rawLightmapNum )
 						/* handle normal light */
 						else
 							VectorAdd( luxel, lightLuxel, luxel );
+
+						if(deluxemap)
+						{
+							VectorAdd( deluxel, lightDeluxel, deluxel );
+						}
 					}
 				}
 			}
@@ -2217,11 +2523,47 @@ void IlluminateRawLightmap( int rawLightmapNum )
 		/* free temporary luxels */
 		if( lightLuxels != stackLightLuxels )
 			free( lightLuxels );
+		
+		if(deluxemap)
+			free( lightDeluxels );
 	}
 	
 	/* free light list */
 	FreeTraceLights( &trace );
 	
+	/* floodlight pass */
+	if( floodlighty )
+		FloodlightIlluminateLightmap(lm);
+
+	if (debugnormals)
+	{
+		for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
+		{
+			/* early out */
+			if( lm->superLuxels[ lightmapNum ] == NULL )
+				continue;
+			
+			for( y = 0; y < lm->sh; y++ )
+			{
+				for( x = 0; x < lm->sw; x++ )
+				{
+					/* get cluster */
+					cluster	= SUPER_CLUSTER( x, y );
+					//%	if( *cluster < 0 )
+					//%		continue;
+					
+					/* get particulars */
+					luxel = SUPER_LUXEL( lightmapNum, x, y );
+					normal = SUPER_NORMAL (  x, y );
+               
+					luxel[0]=(normal[0]*127)+127;
+					luxel[1]=(normal[1]*127)+127;
+					luxel[2]=(normal[2]*127)+127;
+				}
+			}
+		}
+	}
+	
 	/*	-----------------------------------------------------------------
 		dirt pass
 		----------------------------------------------------------------- */
@@ -2242,7 +2584,7 @@ void IlluminateRawLightmap( int rawLightmapNum )
 				{
 					/* get cluster */
 					cluster	= SUPER_CLUSTER( x, y );
-					//%	if( *cluster < 0 )
+					//%	if( *cluster < 0 ) // TODO why not do this check? These pixels should be zero anyway
 					//%		continue;
 					
 					/* get particulars */
@@ -2288,6 +2630,7 @@ void IlluminateRawLightmap( int rawLightmapNum )
 				if( *cluster < 0 ||
 					(lm->splotchFix && (luxel[ 0 ] <= ambientColor[ 0 ] || luxel[ 1 ] <= ambientColor[ 1 ] || luxel[ 2 ] <= ambientColor[ 2 ])) )
 					filterColor = qtrue;
+
 				if( deluxemap && lightmapNum == 0 && (*cluster < 0 || filter) )
 					filterDir = qtrue;
 				
@@ -2355,6 +2698,44 @@ void IlluminateRawLightmap( int rawLightmapNum )
 			}
 		}
 	}
+
+
+#if 0
+	// audit pass
+	for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
+	{
+		/* early out */
+		if( lm->superLuxels[ lightmapNum ] == NULL )
+			continue;
+		for( y = 0; y < lm->sh; y++ )
+			for( x = 0; x < lm->sw; x++ )
+			{
+				/* get cluster */
+				cluster	= SUPER_CLUSTER( x, y );
+				luxel = SUPER_LUXEL( lightmapNum, x, y );
+				deluxel = SUPER_DELUXEL( x, y );
+				if(!luxel || !deluxel || !cluster)
+				{
+					Sys_FPrintf(SYS_VRB, "WARNING: I got NULL'd.\n");
+					continue;
+				}
+				else if(*cluster < 0)
+				{
+					// unmapped pixel
+					// should have neither deluxemap nor lightmap
+					if(deluxel[3])
+						Sys_FPrintf(SYS_VRB, "WARNING: I have written deluxe to an unmapped luxel. Sorry.\n");
+				}
+				else
+				{
+					// mapped pixel
+					// should have both deluxemap and lightmap
+					if(deluxel[3])
+						Sys_FPrintf(SYS_VRB, "WARNING: I forgot to write deluxe to a mapped luxel. Sorry.\n");
+				}
+			}
+	}
+#endif
 }
 
 
@@ -2377,6 +2758,8 @@ void IlluminateVertexes( int num )
 	rawLightmap_t		*lm;
 	bspDrawVert_t		*verts;
 	trace_t				trace;
+	float				floodLightAmount;
+	vec3_t				floodColor;
 	
 	
 	/* get surface, info, and raw lightmap */
@@ -2454,11 +2837,20 @@ void IlluminateVertexes( int num )
 					VectorCopy( verts[ i ].normal, trace.normal );
 					
 					/* r7 dirt */
-					if( dirty )
+					if( dirty && !bouncing )
 						dirt = DirtForSample( &trace );
 					else
 						dirt = 1.0f;
 
+					/* jal: floodlight */
+					floodLightAmount = 0.0f;
+					VectorClear( floodColor );
+					if( floodlighty && !bouncing )
+					{
+						floodLightAmount = floodlightIntensity * FloodLightForSample( &trace, floodlightDistance, floodlight_lowquality );
+						VectorScale( floodlightRGB, floodLightAmount, floodColor );
+					}
+
 					/* trace */
 					LightingAtSample( &trace, ds->vertexStyles, colors );
 					
@@ -2467,6 +2859,9 @@ void IlluminateVertexes( int num )
 					{
 						/* r7 dirt */
 						VectorScale( colors[ lightmapNum ], dirt, colors[ lightmapNum ] );
+
+						/* jal: floodlight */
+						VectorAdd( colors[ lightmapNum ], floodColor, colors[ lightmapNum ] ); 
 						
 						/* store */
 						radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
@@ -2482,16 +2877,16 @@ void IlluminateVertexes( int num )
 					radVertLuxel[ 2 ] <= ambientColor[ 2 ] )
 				{
 					/* nudge the sample point around a bit */
-					for( x = 0; x < 4; x++ )
+					for( x = 0; x < 5; x++ )
 					{
 						/* two's complement 0, 1, -1, 2, -2, etc */
 						x1 = ((x >> 1) ^ (x & 1 ? -1 : 0)) + (x & 1);
 						
-						for( y = 0; y < 4; y++ )
+						for( y = 0; y < 5; y++ )
 						{
 							y1 = ((y >> 1) ^ (y & 1 ? -1 : 0)) + (y & 1);
 							
-							for( z = 0; z < 4; z++ )
+							for( z = 0; z < 5; z++ )
 							{
 								z1 = ((z >> 1) ^ (z & 1 ? -1 : 0)) + (z & 1);
 								
@@ -2504,6 +2899,21 @@ void IlluminateVertexes( int num )
 								trace.cluster = ClusterForPointExtFilter( origin, VERTEX_EPSILON, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ] );
 								if( trace.cluster < 0 )
 									continue;
+
+								/* r7 dirt */
+								if( dirty && !bouncing )
+									dirt = DirtForSample( &trace );
+								else
+									dirt = 1.0f;
+
+								/* jal: floodlight */
+								floodLightAmount = 0.0f;
+								VectorClear( floodColor );
+								if( floodlighty && !bouncing )
+								{
+									floodLightAmount = floodlightIntensity * FloodLightForSample( &trace, floodlightDistance, floodlight_lowquality );
+									VectorScale( floodlightRGB, floodLightAmount, floodColor );
+								}
 															
 								/* trace */
 								LightingAtSample( &trace, ds->vertexStyles, colors );
@@ -2513,6 +2923,9 @@ void IlluminateVertexes( int num )
 								{
 									/* r7 dirt */
 									VectorScale( colors[ lightmapNum ], dirt, colors[ lightmapNum ] );
+
+									/* jal: floodlight */
+									VectorAdd( colors[ lightmapNum ], floodColor, colors[ lightmapNum ] ); 
 									
 									/* store */
 									radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
@@ -3112,7 +3525,7 @@ void SetupEnvelopes( qboolean forGrid, qboolean fastFlag )
 	int			i, x, y, z, x1, y1, z1;
 	light_t		*light, *light2, **owner;
 	bspLeaf_t	*leaf;
-	vec3_t		origin, dir, mins, maxs, nullVector = { 0, 0, 0 };
+	vec3_t		origin, dir, mins, maxs;
 	float		radius, intensity;
 	light_t		*buckets[ 256 ];
 	
@@ -3225,6 +3638,7 @@ void SetupEnvelopes( qboolean forGrid, qboolean fastFlag )
 					/* check for fast mode */
 					if( !(light->flags & LIGHT_FAST) && !(light->flags & LIGHT_FAST_TEMP) )
 						light->envelope = MAX_WORLD_COORD * 8.0f;
+					intensity = light->photons; /* hopefully not used */
 				}
 				else
 				{
@@ -3587,7 +4001,434 @@ void CreateTraceLightsForSurface( int num, trace_t *trace )
 	CreateTraceLightsForBounds( mins, maxs, normal, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ], LIGHT_SURFACES, trace );
 }
 
+/////////////////////////////////////////////////////////////
+
+#define FLOODLIGHT_CONE_ANGLE			88	/* degrees */
+#define FLOODLIGHT_NUM_ANGLE_STEPS		16
+#define FLOODLIGHT_NUM_ELEVATION_STEPS	4
+#define FLOODLIGHT_NUM_VECTORS			(FLOODLIGHT_NUM_ANGLE_STEPS * FLOODLIGHT_NUM_ELEVATION_STEPS)
+
+static vec3_t	floodVectors[ FLOODLIGHT_NUM_VECTORS ];
+static int		numFloodVectors = 0;
+
+void SetupFloodLight( void )
+{
+	int		i, j;
+	float	angle, elevation, angleStep, elevationStep;
+	const char	*value;
+	double v1,v2,v3,v4,v5,v6;
+
+	/* note it */
+	Sys_FPrintf( SYS_VRB, "--- SetupFloodLight ---\n" );
+
+	/* calculate angular steps */
+	angleStep = DEG2RAD( 360.0f / FLOODLIGHT_NUM_ANGLE_STEPS );
+	elevationStep = DEG2RAD( FLOODLIGHT_CONE_ANGLE / FLOODLIGHT_NUM_ELEVATION_STEPS );
+
+	/* iterate angle */
+	angle = 0.0f;
+	for( i = 0, angle = 0.0f; i < FLOODLIGHT_NUM_ANGLE_STEPS; i++, angle += angleStep )
+	{
+		/* iterate elevation */
+		for( j = 0, elevation = elevationStep * 0.5f; j < FLOODLIGHT_NUM_ELEVATION_STEPS; j++, elevation += elevationStep )
+		{
+			floodVectors[ numFloodVectors ][ 0 ] = sin( elevation ) * cos( angle );
+			floodVectors[ numFloodVectors ][ 1 ] = sin( elevation ) * sin( angle );
+			floodVectors[ numFloodVectors ][ 2 ] = cos( elevation );
+			numFloodVectors++;
+		}
+	}
+
+	/* emit some statistics */
+	Sys_FPrintf( SYS_VRB, "%9d numFloodVectors\n", numFloodVectors );
+
+      /* floodlight */
+	value = ValueForKey( &entities[ 0 ], "_floodlight" );
+
+	if( value[ 0 ] != '\0' )
+	{
+		v1=v2=v3=0;
+		v4=floodlightDistance;
+		v5=floodlightIntensity;
+		v6=floodlightDirectionScale;
+
+		sscanf( value, "%lf %lf %lf %lf %lf %lf", &v1, &v2, &v3, &v4, &v5);
+
+		floodlightRGB[0]=v1;
+		floodlightRGB[1]=v2;
+		floodlightRGB[2]=v3;
+
+		if (VectorLength(floodlightRGB)==0)
+		{
+			VectorSet(floodlightRGB,240,240,255);
+		}
+
+		if (v4<1) v4=1024;
+		if (v5<1) v5=128;
+		if (v6<0) v6=1;
+
+		floodlightDistance=v4;
+		floodlightIntensity=v5;
+		floodlightDirectionScale=v6;
+
+		floodlighty = qtrue;
+		Sys_Printf( "FloodLighting enabled via worldspawn _floodlight key.\n" );
+	}
+	else
+	{
+		VectorSet(floodlightRGB,240,240,255);
+		//floodlighty = qtrue;
+		//Sys_Printf( "FloodLighting enabled via worldspawn _floodlight key.\n" );
+	}
+	VectorNormalize(floodlightRGB,floodlightRGB);
+}
+
+/*
+FloodLightForSample()
+calculates floodlight value for a given sample
+once again, kudos to the dirtmapping coder
+*/
+
+float FloodLightForSample( trace_t *trace , float floodLightDistance, qboolean floodLightLowQuality)
+{
+	int		i;
+	float 	d;
+	float 	contribution;
+	int 	sub = 0;
+	float	gatherLight, outLight;
+	vec3_t	normal, worldUp, myUp, myRt, direction, displacement;
+	float 	dd;
+	int 	vecs = 0;
+ 
+	gatherLight=0;
+	/* dummy check */
+	//if( !dirty )
+	//	return 1.0f;
+	if( trace == NULL || trace->cluster < 0 )
+		return 0.0f;
+	
+
+	/* setup */
+	dd = floodLightDistance;
+	VectorCopy( trace->normal, normal );
+	
+	/* check if the normal is aligned to the world-up */
+	if( normal[ 0 ] == 0.0f && normal[ 1 ] == 0.0f && ( normal[ 2 ] == 1.0f || normal[ 2 ] == -1.0f ) )
+	{
+		if( normal[ 2 ] == 1.0f )		
+		{
+			VectorSet( myRt, 1.0f, 0.0f, 0.0f );
+			VectorSet( myUp, 0.0f, 1.0f, 0.0f );
+		}
+		else if( normal[ 2 ] == -1.0f )
+		{
+			VectorSet( myRt, -1.0f, 0.0f, 0.0f );
+			VectorSet( myUp,  0.0f, 1.0f, 0.0f );
+		}
+	}
+	else
+	{
+		VectorSet( worldUp, 0.0f, 0.0f, 1.0f );
+		CrossProduct( normal, worldUp, myRt );
+		VectorNormalize( myRt, myRt );
+		CrossProduct( myRt, normal, myUp );
+		VectorNormalize( myUp, myUp );
+	}
+
+	/* vortex: optimise floodLightLowQuality a bit */
+	if ( floodLightLowQuality == qtrue )
+    {
+		/* iterate through ordered vectors */
+		for( i = 0; i < numFloodVectors; i++ )
+			if (rand()%10 != 0 ) continue;
+	}
+	else
+	{
+		/* iterate through ordered vectors */
+		for( i = 0; i < numFloodVectors; i++ )
+		{
+			vecs++;
+	         
+			/* transform vector into tangent space */
+			direction[ 0 ] = myRt[ 0 ] * floodVectors[ i ][ 0 ] + myUp[ 0 ] * floodVectors[ i ][ 1 ] + normal[ 0 ] * floodVectors[ i ][ 2 ];
+			direction[ 1 ] = myRt[ 1 ] * floodVectors[ i ][ 0 ] + myUp[ 1 ] * floodVectors[ i ][ 1 ] + normal[ 1 ] * floodVectors[ i ][ 2 ];
+			direction[ 2 ] = myRt[ 2 ] * floodVectors[ i ][ 0 ] + myUp[ 2 ] * floodVectors[ i ][ 1 ] + normal[ 2 ] * floodVectors[ i ][ 2 ];
+
+			/* set endpoint */
+			VectorMA( trace->origin, dd, direction, trace->end );
 
+			//VectorMA( trace->origin, 1, direction, trace->origin );
+				
+			SetupTrace( trace );
+			/* trace */
+	  		TraceLine( trace );
+			contribution=1;
 
+			if ( trace->compileFlags & C_SKY || trace->compileFlags & C_TRANSLUCENT )
+			{
+				contribution=1.0f;
+			}
+			else if ( trace->opaque )
+			{
+				VectorSubtract( trace->hit, trace->origin, displacement );
+				d=VectorLength( displacement );
+
+				// d=trace->distance;            
+				//if (d>256) gatherDirt+=1;
+				contribution=d/dd;
+				if (contribution>1) contribution=1.0f; 
+	             
+				//gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
+			}
+	         
+			gatherLight+=contribution;
+		}
+	}
+   
+	/* early out */
+	if( gatherLight <= 0.0f )
+		return 0.0f;
+   	
+	sub=vecs;
 
+	if (sub<1) sub=1;
+	gatherLight/=(sub);
 
+	outLight=gatherLight;
+	if( outLight > 1.0f )
+		outLight = 1.0f;
+	
+	/* return to sender */
+	return outLight;
+}
+
+/*
+FloodLightRawLightmap
+lighttracer style ambient occlusion light hack.
+Kudos to the dirtmapping author for most of this source.
+VorteX: modified to floodlight up custom surfaces (q3map_floodLight)
+VorteX: fixed problems with deluxemapping
+*/
+
+// floodlight pass on a lightmap
+void FloodLightRawLightmapPass( rawLightmap_t *lm , vec3_t lmFloodLightRGB, float lmFloodLightIntensity, float lmFloodLightDistance, qboolean lmFloodLightLowQuality, float floodlightDirectionScale)
+{
+	int					i, x, y, *cluster;
+	float				*origin, *normal, *floodlight, floodLightAmount;
+	surfaceInfo_t		*info;
+	trace_t				trace;
+	// int sx, sy;
+	// float samples, average, *floodlight2;
+	
+	memset(&trace,0,sizeof(trace_t));
+
+	/* setup trace */
+	trace.testOcclusion = qtrue;
+	trace.forceSunlight = qfalse;
+	trace.twoSided = qtrue;
+	trace.recvShadows = lm->recvShadows;
+	trace.numSurfaces = lm->numLightSurfaces;
+	trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];
+	trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
+	trace.testAll = qfalse;
+	trace.distance = 1024;
+	
+	/* twosided lighting (may or may not be a good idea for lightmapped stuff) */
+	//trace.twoSided = qfalse;
+	for( i = 0; i < trace.numSurfaces; i++ )
+	{
+		/* get surface */
+		info = &surfaceInfos[ trace.surfaces[ i ] ];
+		
+		/* check twosidedness */
+		if( info->si->twoSided )
+		{
+			trace.twoSided = qtrue;
+			break;
+		}
+	}
+	
+	/* gather floodlight */
+	for( y = 0; y < lm->sh; y++ )
+	{
+		for( x = 0; x < lm->sw; x++ )
+		{
+			/* get luxel */
+			cluster = SUPER_CLUSTER( x, y );
+			origin = SUPER_ORIGIN( x, y );
+			normal = SUPER_NORMAL( x, y );
+			floodlight = SUPER_FLOODLIGHT( x, y );
+			
+			/* set default dirt */
+			*floodlight = 0.0f;
+			
+			/* only look at mapped luxels */
+			if( *cluster < 0 )
+				continue;
+			
+			/* copy to trace */
+			trace.cluster = *cluster;
+			VectorCopy( origin, trace.origin );
+			VectorCopy( normal, trace.normal );
+   
+			/* get floodlight */
+			floodLightAmount = FloodLightForSample( &trace , lmFloodLightDistance, lmFloodLightLowQuality)*lmFloodLightIntensity;
+			
+			/* add floodlight */
+			floodlight[0] += lmFloodLightRGB[0]*floodLightAmount;
+			floodlight[1] += lmFloodLightRGB[1]*floodLightAmount;
+			floodlight[2] += lmFloodLightRGB[2]*floodLightAmount;
+			floodlight[3] += floodlightDirectionScale;
+		}
+	}
+	
+	/* testing no filtering */
+	return;
+
+#if 0
+	
+	/* filter "dirt" */
+	for( y = 0; y < lm->sh; y++ )
+	{
+		for( x = 0; x < lm->sw; x++ )
+		{
+			/* get luxel */
+			cluster = SUPER_CLUSTER( x, y );
+			floodlight = SUPER_FLOODLIGHT(x, y );
+			
+			/* filter dirt by adjacency to unmapped luxels */
+			average = *floodlight;
+			samples = 1.0f;
+			for( sy = (y - 1); sy <= (y + 1); sy++ )
+			{
+				if( sy < 0 || sy >= lm->sh )
+					continue;
+				
+				for( sx = (x - 1); sx <= (x + 1); sx++ )
+				{
+					if( sx < 0 || sx >= lm->sw || (sx == x && sy == y) )
+						continue;
+					
+					/* get neighboring luxel */
+					cluster = SUPER_CLUSTER( sx, sy );
+					floodlight2 = SUPER_FLOODLIGHT( sx, sy );
+					if( *cluster < 0 || *floodlight2 <= 0.0f )
+						continue;
+					
+					/* add it */
+					average += *floodlight2;
+					samples += 1.0f;
+				}
+				
+				/* bail */
+				if( samples <= 0.0f )
+					break;
+			}
+			
+			/* bail */
+			if( samples <= 0.0f )
+				continue;
+			
+			/* scale dirt */
+			*floodlight = average / samples;
+		}
+	}
+#endif
+}
+
+void FloodLightRawLightmap( int rawLightmapNum )
+{
+	rawLightmap_t		*lm;
+
+	/* bail if this number exceeds the number of raw lightmaps */
+	if( rawLightmapNum >= numRawLightmaps )
+		return;
+	/* get lightmap */
+	lm = &rawLightmaps[ rawLightmapNum ];
+
+	/* global pass */
+	if (floodlighty && floodlightIntensity)
+		FloodLightRawLightmapPass(lm, floodlightRGB, floodlightIntensity, floodlightDistance, floodlight_lowquality, floodlightDirectionScale);
+
+	/* custom pass */
+	if (lm->floodlightIntensity)
+	{
+		FloodLightRawLightmapPass(lm, lm->floodlightRGB, lm->floodlightIntensity, lm->floodlightDistance, qfalse, lm->floodlightDirectionScale);
+		numSurfacesFloodlighten += 1;
+	}
+}
+
+void FloodlightRawLightmaps()
+{
+	Sys_Printf( "--- FloodlightRawLightmap ---\n" );
+	numSurfacesFloodlighten = 0;
+	RunThreadsOnIndividual( numRawLightmaps, qtrue, FloodLightRawLightmap );
+	Sys_Printf( "%9d custom lightmaps floodlighted\n", numSurfacesFloodlighten );
+}
+
+/*
+FloodLightIlluminate()
+illuminate floodlight into lightmap luxels
+*/
+
+void FloodlightIlluminateLightmap( rawLightmap_t *lm )
+{
+	float				*luxel, *floodlight, *deluxel, *normal;
+	int					*cluster;
+	float				brightness;
+	int					x, y, lightmapNum;
+
+	/* walk lightmaps */
+	for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
+	{
+		/* early out */
+		if( lm->superLuxels[ lightmapNum ] == NULL )
+			continue;
+
+		/* apply floodlight to each luxel */
+		for( y = 0; y < lm->sh; y++ )
+		{
+			for( x = 0; x < lm->sw; x++ )
+			{
+				/* get floodlight */
+				floodlight = SUPER_FLOODLIGHT( x, y );
+				if (!floodlight[0] && !floodlight[1] && !floodlight[2])
+					continue;
+						
+				/* get cluster */
+				cluster	= SUPER_CLUSTER( x, y );
+
+				/* only process mapped luxels */
+				if( *cluster < 0 )
+					continue;
+
+				/* get particulars */
+				luxel = SUPER_LUXEL( lightmapNum, x, y );
+				deluxel = SUPER_DELUXEL( x, y );
+
+				/* add to lightmap */
+				luxel[0]+=floodlight[0];
+				luxel[1]+=floodlight[1];
+				luxel[2]+=floodlight[2];
+
+				if (luxel[3]==0) luxel[3]=1;
+
+				/* add to deluxemap */
+				if (deluxemap && floodlight[3] > 0)
+				{
+					vec3_t				lightvector;
+
+					normal = SUPER_NORMAL( x, y );
+					brightness = RGBTOGRAY( floodlight ) * ( 1.0f/255.0f ) * floodlight[3];
+
+					// use AT LEAST this amount of contribution from ambient for the deluxemap, fixes points that receive ZERO light
+					if(brightness < 0.00390625f)
+						brightness = 0.00390625f;
+
+					VectorScale( normal, brightness, lightvector );
+					VectorAdd( deluxel, lightvector, deluxel );
+				}
+			}
+		}
+	}
+}
diff --git a/tools/quake3/q3map2/lightmaps.c b/tools/quake3/q3map2/lightmaps.c
index 62977ddf..13f5c533 100644
--- a/tools/quake3/q3map2/lightmaps.c
+++ b/tools/quake3/q3map2/lightmaps.c
@@ -30,7 +30,8 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */
 
 int					numSortShaders;
-mapDrawSurface_t	*surfsOnShader[ MAX_MAP_SHADERS ];
+mapDrawSurface_t	**surfsOnShader;
+int allocatedSurfsOnShader;
 
 
 int		allocated[ LIGHTMAP_WIDTH ];
@@ -96,94 +97,6 @@ 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;
-		}
-	}
-}
 
 
 /*
@@ -194,176 +107,6 @@ 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 );
-}
 
 
 /*
@@ -371,126 +114,6 @@ void AllocateLightmapForSurface( mapDrawSurface_t *ds )
 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 );
-}
 
 
 
diff --git a/tools/quake3/q3map2/lightmaps_ydnar.c b/tools/quake3/q3map2/lightmaps_ydnar.c
index 8ae0d75a..080f26ec 100644
--- a/tools/quake3/q3map2/lightmaps_ydnar.c
+++ b/tools/quake3/q3map2/lightmaps_ydnar.c
@@ -291,8 +291,8 @@ static int CompareLightSurface( const void *a, const void *b )
 	
 	
 	/* get shaders */
-	asi = surfaceInfos[ *((int*) a) ].si;
-	bsi = surfaceInfos[ *((int*) b) ].si;
+	asi = surfaceInfos[ *((const int*) a) ].si;
+	bsi = surfaceInfos[ *((const int*) b) ].si;
 	
 	/* dummy check */
 	if( asi == NULL )
@@ -414,6 +414,12 @@ void FinishRawLightmap( rawLightmap_t *lm )
 		lm->superNormals = safe_malloc( size );
 	memset( lm->superNormals, 0, size );
 	
+ 	/* allocate floodlight map storage */
+	size = lm->sw * lm->sh * SUPER_FLOODLIGHT_SIZE * sizeof( float );
+	if( lm->superFloodLight == NULL )
+		lm->superFloodLight = safe_malloc( size );
+	memset( lm->superFloodLight, 0, size );
+
 	/* allocate cluster map storage */
 	size = lm->sw * lm->sh * sizeof( int );
 	if( lm->superClusters == NULL )
@@ -672,12 +678,25 @@ qboolean AddSurfaceToRawLightmap( int num, rawLightmap_t *lm )
  		size[ i ] = (maxs[ i ] - mins[ i ]) / sampleSize + 1.0f;
 		
 		/* hack (god this sucks) */
-		if( size[ i ] > lm->customWidth || size[ i ] > lm->customHeight )
+		if( size[ i ] > lm->customWidth || size[ i ] > lm->customHeight  || (lmLimitSize && size[i] > lmLimitSize))
 		{
 			i = -1;
 			sampleSize += 1.0f;
 		}
 	}
+
+	if(sampleSize != lm->sampleSize && lmLimitSize == 0)
+	{
+		Sys_FPrintf(SYS_VRB,"WARNING: surface at (%6.0f %6.0f %6.0f) (%6.0f %6.0f %6.0f) too large for desired samplesize/lightmapsize/lightmapscale combination, increased samplesize from %d to %d\n",
+			info->mins[0],
+			info->mins[1],
+			info->mins[2],
+			info->maxs[0],
+			info->maxs[1],
+			info->maxs[2],
+			lm->sampleSize,
+			(int) sampleSize);
+	}
 	
 	/* set actual sample size */
 	lm->actualSampleSize = sampleSize;
@@ -842,13 +861,13 @@ static int CompareSurfaceInfo( const void *a, const void *b )
 	
 
 	/* get surface info */
-	aInfo = &surfaceInfos[ *((int*) a) ];
-	bInfo = &surfaceInfos[ *((int*) b) ];
+	aInfo = &surfaceInfos[ *((const int*) a) ];
+	bInfo = &surfaceInfos[ *((const int*) b) ];
 	
 	/* model first */
-	if( aInfo->model < bInfo->model )
+	if( aInfo->modelindex < bInfo->modelindex )
 		return 1;
-	else if( aInfo->model > bInfo->model )
+	else if( aInfo->modelindex > bInfo->modelindex )
 		return -1;
 	
 	/* then lightmap status */
@@ -856,6 +875,13 @@ static int CompareSurfaceInfo( const void *a, const void *b )
 		return 1;
 	else if( aInfo->hasLightmap > bInfo->hasLightmap )
 		return -1;
+
+   /* 27: then shader! */
+   if (aInfo->si < bInfo->si)
+   	return 1;
+   else if (aInfo->si > bInfo->si)
+      return -1;
+	
 	
 	/* then lightmap sample size */
 	if( aInfo->sampleSize < bInfo->sampleSize )
@@ -978,7 +1004,7 @@ void SetupSurfaceLightmaps( void )
 				VectorClear( entityOrigin );
 			
 			/* basic setup */
-			info->model = model;
+			info->modelindex = i;
 			info->lm = NULL;
 			info->plane = NULL;
 			info->firstSurfaceCluster = numSurfaceClusters;
@@ -1092,12 +1118,20 @@ void SetupSurfaceLightmaps( void )
 		lm->splotchFix = info->si->splotchFix;
 		lm->firstLightSurface = numLightSurfaces;
 		lm->numLightSurfaces = 0;
-		lm->sampleSize = info->sampleSize;
-		lm->actualSampleSize = info->sampleSize;
+		/* vortex: multiply lightmap sample size by -samplescale */
+		if (sampleScale > 0)
+			lm->sampleSize = info->sampleSize*sampleScale;
+		else
+			lm->sampleSize = info->sampleSize;
+		lm->actualSampleSize = lm->sampleSize;
 		lm->entityNum = info->entityNum;
 		lm->recvShadows = info->recvShadows;
 		lm->brightness = info->si->lmBrightness;
 		lm->filterRadius = info->si->lmFilterRadius;
+		VectorCopy(info->si->floodlightRGB, lm->floodlightRGB);
+		lm->floodlightDistance = info->si->floodlightDistance;
+		lm->floodlightIntensity = info->si->floodlightIntensity;
+		lm->floodlightDirectionScale = info->si->floodlightDirectionScale;
 		VectorCopy( info->axis, lm->axis );
 		lm->plane = info->plane;	
 		VectorCopy( info->mins, lm->mins );
@@ -1884,9 +1918,10 @@ FindOutLightmaps()
 for a given surface lightmap, find output lightmap pages and positions for it
 */
 
+#define LIGHTMAP_RESERVE_COUNT 1
 static void FindOutLightmaps( rawLightmap_t *lm )
 {
-	int					i, j, lightmapNum, xMax, yMax, x, y, sx, sy, ox, oy, offset, temp;
+	int					i, j, k, lightmapNum, xMax, yMax, x, y, sx, sy, ox, oy, offset;
 	outLightmap_t		*olm;
 	surfaceInfo_t		*info;
 	float				*luxel, *deluxel;
@@ -1981,7 +2016,11 @@ static void FindOutLightmaps( rawLightmap_t *lm )
 			y = 0;
 			
 			/* walk the list of lightmap pages */
-			for( i = 0; i < numOutLightmaps; i++ )
+			if(lightmapSearchBlockSize <= 0 || numOutLightmaps < LIGHTMAP_RESERVE_COUNT)
+				i = 0;
+			else
+				i = ((numOutLightmaps - LIGHTMAP_RESERVE_COUNT) / lightmapSearchBlockSize) * lightmapSearchBlockSize;
+			for( ; i < numOutLightmaps; i++ )
 			{
 				/* get the output lightmap */
 				olm = &outLightmaps[ i ];
@@ -2035,22 +2074,22 @@ static void FindOutLightmaps( rawLightmap_t *lm )
 		/* no match? */
 		if( ok == qfalse )
 		{
-			/* allocate two new output lightmaps */
-			numOutLightmaps += 2;
+			/* allocate LIGHTMAP_RESERVE_COUNT new output lightmaps */
+			numOutLightmaps += LIGHTMAP_RESERVE_COUNT;
 			olm = safe_malloc( numOutLightmaps * sizeof( outLightmap_t ) );
-			if( outLightmaps != NULL && numOutLightmaps > 2 )
+			if( outLightmaps != NULL && numOutLightmaps > LIGHTMAP_RESERVE_COUNT )
 			{
-				memcpy( olm, outLightmaps, (numOutLightmaps - 2) * sizeof( outLightmap_t ) );
+				memcpy( olm, outLightmaps, (numOutLightmaps - LIGHTMAP_RESERVE_COUNT) * sizeof( outLightmap_t ) );
 				free( outLightmaps );
 			}
 			outLightmaps = olm;
 			
 			/* initialize both out lightmaps */
-			SetupOutLightmap( lm, &outLightmaps[ numOutLightmaps - 2 ] );
-			SetupOutLightmap( lm, &outLightmaps[ numOutLightmaps - 1 ] );
+			for(k = numOutLightmaps - LIGHTMAP_RESERVE_COUNT; k < numOutLightmaps; ++k)
+				SetupOutLightmap( lm, &outLightmaps[ k ] );
 			
 			/* set out lightmap */
-			i = numOutLightmaps - 2;
+			i = numOutLightmaps - LIGHTMAP_RESERVE_COUNT;
 			olm = &outLightmaps[ i ];
 			
 			/* set stamp xy origin to the first surface lightmap */
@@ -2154,21 +2193,12 @@ static void FindOutLightmaps( rawLightmap_t *lm )
 				if( deluxemap )
 				{
 					/* normalize average light direction */
-					if( VectorNormalize( deluxel, direction ) )
-					{
-						/* encode [-1,1] in [0,255] */
-						pixel = olm->bspDirBytes + (((oy * olm->customWidth) + ox) * 3);
-						for( i = 0; i < 3; i++ )
-						{
-							temp = (direction[ i ] + 1.0f) * 127.5f;
-							if( temp < 0 )
-								pixel[ i ] = 0;
-							else if( temp > 255 )
-								pixel[ i ] = 255;
-							else
-								pixel[ i ] = temp;
-						}
-					}
+					pixel = olm->bspDirBytes + (((oy * olm->customWidth) + ox) * 3);
+					VectorScale( deluxel, 1000.0f, direction );
+					VectorNormalize( direction, direction );
+					VectorScale( direction, 127.5f, direction );
+					for( i = 0; i < 3; i++ )
+						pixel[ i ] = (byte)( 127.5f + direction[ i ] );
 				}
 			}
 		}
@@ -2190,8 +2220,8 @@ static int CompareRawLightmap( const void *a, const void *b )
 	
 	
 	/* get lightmaps */
-	alm = &rawLightmaps[ *((int*) a) ];
-	blm = &rawLightmaps[ *((int*) b) ];
+	alm = &rawLightmaps[ *((const int*) a) ];
+	blm = &rawLightmaps[ *((const int*) b) ];
 	
 	/* get min number of surfaces */
 	min = (alm->numLightSurfaces < blm->numLightSurfaces ? alm->numLightSurfaces : blm->numLightSurfaces);
@@ -2225,7 +2255,112 @@ static int CompareRawLightmap( const void *a, const void *b )
 	return 0;
 }
 
+void FillOutLightmap(outLightmap_t *olm)
+{
+	int x, y;
+	int ofs;
+	vec3_t dir_sum, light_sum;
+	int cnt, filled;
+	byte *lightBitsNew = NULL;
+	byte *lightBytesNew = NULL;
+	byte *dirBytesNew = NULL;
+
+	lightBitsNew = safe_malloc((olm->customWidth * olm->customHeight + 8) / 8);
+	lightBytesNew = safe_malloc(olm->customWidth * olm->customHeight * 3);
+	if(deluxemap)
+		dirBytesNew = safe_malloc(olm->customWidth * olm->customHeight * 3);
+
+	/*
+	memset(olm->lightBits, 0, (olm->customWidth * olm->customHeight + 8) / 8);
+		olm->lightBits[0] |= 1;
+		olm->lightBits[(10 * olm->customWidth + 30) >> 3] |= 1 << ((10 * olm->customWidth + 30) & 7);
+	memset(olm->bspLightBytes, 0, olm->customWidth * olm->customHeight * 3);
+		olm->bspLightBytes[0] = 255;
+		olm->bspLightBytes[(10 * olm->customWidth + 30) * 3 + 2] = 255;
+	*/
+
+	memcpy(lightBitsNew, olm->lightBits, (olm->customWidth * olm->customHeight + 8) / 8);
+	memcpy(lightBytesNew, olm->bspLightBytes, olm->customWidth * olm->customHeight * 3);
+	if(deluxemap)
+		memcpy(dirBytesNew, olm->bspDirBytes, olm->customWidth * olm->customHeight * 3);
+
+	for(;;)
+	{
+		filled = 0;
+		for(y = 0; y < olm->customHeight; ++y)
+		{
+			for(x = 0; x < olm->customWidth; ++x)
+			{
+				ofs = y * olm->customWidth + x;
+				if(olm->lightBits[ofs >> 3] & (1 << (ofs & 7))) /* already filled */
+					continue;
+				cnt = 0;
+				VectorClear(dir_sum);
+				VectorClear(light_sum);
+
+				/* try all four neighbors */
+				ofs = ((y + olm->customHeight - 1) % olm->customHeight) * olm->customWidth + x;
+				if(olm->lightBits[ofs >> 3] & (1 << (ofs & 7))) /* already filled */
+				{
+					++cnt;
+					VectorAdd(light_sum, olm->bspLightBytes + ofs * 3, light_sum);
+					if(deluxemap)
+						VectorAdd(dir_sum, olm->bspDirBytes + ofs * 3, dir_sum);
+				}
+
+				ofs = ((y + 1) % olm->customHeight) * olm->customWidth + x;
+				if(olm->lightBits[ofs >> 3] & (1 << (ofs & 7))) /* already filled */
+				{
+					++cnt;
+					VectorAdd(light_sum, olm->bspLightBytes + ofs * 3, light_sum);
+					if(deluxemap)
+						VectorAdd(dir_sum, olm->bspDirBytes + ofs * 3, dir_sum);
+				}
+
+				ofs = y * olm->customWidth + (x + olm->customWidth - 1) % olm->customWidth;
+				if(olm->lightBits[ofs >> 3] & (1 << (ofs & 7))) /* already filled */
+				{
+					++cnt;
+					VectorAdd(light_sum, olm->bspLightBytes + ofs * 3, light_sum);
+					if(deluxemap)
+						VectorAdd(dir_sum, olm->bspDirBytes + ofs * 3, dir_sum);
+				}
+
+				ofs = y * olm->customWidth + (x + 1) % olm->customWidth;
+				if(olm->lightBits[ofs >> 3] & (1 << (ofs & 7))) /* already filled */
+				{
+					++cnt;
+					VectorAdd(light_sum, olm->bspLightBytes + ofs * 3, light_sum);
+					if(deluxemap)
+						VectorAdd(dir_sum, olm->bspDirBytes + ofs * 3, dir_sum);
+				}
+
+				if(cnt)
+				{
+					++filled;
+					ofs = y * olm->customWidth + x;
+					lightBitsNew[ofs >> 3] |= (1 << (ofs & 7));
+					VectorScale(light_sum, 1.0/cnt, lightBytesNew + ofs * 3);
+					if(deluxemap)
+						VectorScale(dir_sum, 1.0/cnt, dirBytesNew + ofs * 3);
+				}
+			}
+		}
+
+		if(!filled)
+			break;
+
+		memcpy(olm->lightBits, lightBitsNew, (olm->customWidth * olm->customHeight + 8) / 8);
+		memcpy(olm->bspLightBytes, lightBytesNew, olm->customWidth * olm->customHeight * 3);
+		if(deluxemap)
+			memcpy(olm->bspDirBytes, dirBytesNew, olm->customWidth * olm->customHeight * 3);
+	}
 
+	free(lightBitsNew);
+	free(lightBytesNew);
+	if(deluxemap)
+		free(dirBytesNew);
+}
 
 /*
 StoreSurfaceLightmaps()
@@ -2251,16 +2386,23 @@ void StoreSurfaceLightmaps( void )
 	char				dirname[ 1024 ], filename[ 1024 ];
 	shaderInfo_t		*csi;
 	char				lightmapName[ 128 ];
-	char				*rgbGenValues[ 256 ];
-	char				*alphaGenValues[ 256 ];
+	const char				*rgbGenValues[ 256 ];
+	const char				*alphaGenValues[ 256 ];
 	
 	
 	/* note it */
 	Sys_Printf( "--- StoreSurfaceLightmaps ---\n");
 	
 	/* setup */
-	strcpy( dirname, source );
-	StripExtension( dirname );
+	if(lmCustomDir)
+	{
+		strcpy( dirname, lmCustomDir );
+	}
+	else
+	{
+		strcpy( dirname, source );
+		StripExtension( dirname );
+	}
 	memset( rgbGenValues, 0, sizeof( rgbGenValues ) );
 	memset( alphaGenValues, 0, sizeof( alphaGenValues ) );
 	
@@ -2269,7 +2411,7 @@ void StoreSurfaceLightmaps( void )
 	   ----------------------------------------------------------------- */
 	
 	/* note it */
-	Sys_FPrintf( SYS_VRB, "Subsampling..." );
+	Sys_Printf( "Subsampling..." );
 	
 	/* walk the list of raw lightmaps */
 	numUsed = 0;
@@ -2605,6 +2747,135 @@ void StoreSurfaceLightmaps( void )
 		}
 	}
 	
+	/* -----------------------------------------------------------------
+	   convert modelspace deluxemaps to tangentspace
+	   ----------------------------------------------------------------- */
+	/* note it */
+	if( !bouncing )
+	{
+		if( deluxemap && deluxemode == 1)
+		{
+			vec3_t	worldUp, myNormal, myTangent, myBinormal;
+			float dist;
+
+			Sys_Printf( "converting..." );
+
+			for( i = 0; i < numRawLightmaps; i++ )
+			{
+				/* get lightmap */
+				lm = &rawLightmaps[ i ];
+
+				/* walk lightmap samples */
+				for( y = 0; y < lm->sh; y++ )
+				{
+					for( x = 0; x < lm->sw; x++ )
+					{
+						/* get normal and deluxel */
+						normal = SUPER_NORMAL(x, y);
+						cluster = SUPER_CLUSTER(x, y);
+						bspDeluxel = BSP_DELUXEL( x, y );
+						deluxel = SUPER_DELUXEL( x, y ); 
+
+						/* get normal */
+						VectorSet( myNormal, normal[0], normal[1], normal[2] );
+		
+						/* get tangent vectors */
+						if( myNormal[ 0 ] == 0.0f && myNormal[ 1 ] == 0.0f )
+						{
+							if( myNormal[ 2 ] == 1.0f )		
+							{
+								VectorSet( myTangent, 1.0f, 0.0f, 0.0f );
+								VectorSet( myBinormal, 0.0f, 1.0f, 0.0f );
+							}
+							else if( myNormal[ 2 ] == -1.0f )
+							{
+								VectorSet( myTangent, -1.0f, 0.0f, 0.0f );
+								VectorSet( myBinormal,  0.0f, 1.0f, 0.0f );
+							}
+						}
+						else
+						{
+							VectorSet( worldUp, 0.0f, 0.0f, 1.0f );
+							CrossProduct( myNormal, worldUp, myTangent );
+							VectorNormalize( myTangent, myTangent );
+							CrossProduct( myTangent, myNormal, myBinormal );
+							VectorNormalize( myBinormal, myBinormal );
+						}
+
+						/* project onto plane */
+						dist = -DotProduct(myTangent, myNormal); 
+						VectorMA(myTangent, dist, myNormal, myTangent);
+						dist = -DotProduct(myBinormal, myNormal); 
+						VectorMA(myBinormal, dist, myNormal, myBinormal);
+
+						/* renormalize */
+						VectorNormalize( myTangent, myTangent );
+						VectorNormalize( myBinormal, myBinormal );
+
+						/* convert modelspace deluxel to tangentspace */
+						dirSample[0] = bspDeluxel[0];
+						dirSample[1] = bspDeluxel[1];
+						dirSample[2] = bspDeluxel[2];
+						VectorNormalize(dirSample, dirSample);
+
+						/* fix tangents to world matrix */
+						if (myNormal[0] > 0 || myNormal[1] < 0 || myNormal[2] < 0)
+							VectorNegate(myTangent, myTangent);
+
+						/* build tangentspace vectors */
+						bspDeluxel[0] = DotProduct(dirSample, myTangent);
+						bspDeluxel[1] = DotProduct(dirSample, myBinormal);
+						bspDeluxel[2] = DotProduct(dirSample, myNormal);
+					}
+				}
+			}
+		}
+	}
+
+	/* -----------------------------------------------------------------
+	   blend lightmaps
+	   ----------------------------------------------------------------- */
+
+#ifdef sdfsdfwq312323
+	/* note it */
+	Sys_Printf( "blending..." );
+
+	for( i = 0; i < numRawLightmaps; i++ )
+	{
+		vec3_t	myColor;
+		float myBrightness;
+
+		/* get lightmap */
+		lm = &rawLightmaps[ i ];
+
+		/* walk individual lightmaps */
+		for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
+		{
+			/* early outs */
+			if( lm->superLuxels[ lightmapNum ] == NULL )
+				continue;
+
+			/* walk lightmap samples */
+			for( y = 0; y < lm->sh; y++ )
+			{
+				for( x = 0; x < lm->sw; x++ )
+				{
+					/* get luxel */
+					bspLuxel = BSP_LUXEL( lightmapNum, x, y );
+
+					/* get color */
+					VectorNormalize(bspLuxel, myColor);
+					myBrightness = VectorLength(bspLuxel);
+					myBrightness *= (1 / 127.0f);
+					myBrightness = myBrightness*myBrightness;
+					myBrightness *= 127.0f;
+					VectorScale(myColor, myBrightness, bspLuxel);
+				}
+			}
+		}
+	}
+#endif
+
 	/* -----------------------------------------------------------------
 	   collapse non-unique lightmaps
 	   ----------------------------------------------------------------- */
@@ -2612,7 +2883,7 @@ void StoreSurfaceLightmaps( void )
 	if( noCollapse == qfalse && deluxemap == qfalse )
 	{
 		/* note it */
-		Sys_FPrintf( SYS_VRB, "collapsing..." );
+		Sys_Printf( "collapsing..." );
 		
 		/* set all twin refs to null */
 		for( i = 0; i < numRawLightmaps; i++ )
@@ -2680,7 +2951,7 @@ void StoreSurfaceLightmaps( void )
 	   ----------------------------------------------------------------- */
 	
 	/* note it */
-	Sys_FPrintf( SYS_VRB, "sorting..." );
+	Sys_Printf( "sorting..." );
 	
 	/* allocate a new sorted list */
 	if( sortLightmaps == NULL )
@@ -2696,7 +2967,7 @@ void StoreSurfaceLightmaps( void )
 	   ----------------------------------------------------------------- */
 	
 	/* note it */
-	Sys_FPrintf( SYS_VRB, "allocating..." );
+	Sys_Printf( "allocating..." );
 	
 	/* kill all existing output lightmaps */
 	if( outLightmaps != NULL )
@@ -2749,7 +3020,7 @@ void StoreSurfaceLightmaps( void )
 	   ----------------------------------------------------------------- */
 	
 	/* note it */
-	Sys_FPrintf( SYS_VRB, "storing..." );
+	Sys_Printf( "storing..." );
 	
 	/* count the bsp lightmaps and allocate space */
 	if( bspLightBytes != NULL )
@@ -2771,6 +3042,10 @@ void StoreSurfaceLightmaps( void )
 	{
 		/* get output lightmap */
 		olm = &outLightmaps[ i ];
+
+		/* fill output lightmap */
+		if(lightmapFill)
+			FillOutLightmap(olm);
 		
 		/* is this a valid bsp lightmap? */
 		if( olm->lightmapNum >= 0 && !externalLightmaps )
@@ -2817,7 +3092,7 @@ void StoreSurfaceLightmaps( void )
 	}
 	
 	if( numExtLightmaps > 0 )
-		Sys_FPrintf( SYS_VRB, "\n" );
+		Sys_Printf( "\n" );
 	
 	/* delete unused external lightmaps */
 	for( i = numExtLightmaps; i; i++ )
@@ -2836,7 +3111,7 @@ void StoreSurfaceLightmaps( void )
 	   ----------------------------------------------------------------- */
 	
 	/* note it */
-	Sys_FPrintf( SYS_VRB, "projecting..." );
+	Sys_Printf( "projecting..." );
 	
 	/* walk the list of surfaces */
 	for( i = 0; i < numBSPDrawSurfaces; i++ )
@@ -3004,7 +3279,7 @@ void StoreSurfaceLightmaps( void )
 				if( rgbGenValues[ style ] == NULL )
 				{
 					sprintf( key, "_style%drgbgen", style );
-					rgbGenValues[ style ] = (char*) ValueForKey( &entities[ 0 ], key );
+					rgbGenValues[ style ] = ValueForKey( &entities[ 0 ], key );
 					if( rgbGenValues[ style ][ 0 ] == '\0' )
 						rgbGenValues[ style ] = "wave noise 0.5 1 0 5.37";
 				}
@@ -3018,7 +3293,7 @@ void StoreSurfaceLightmaps( void )
 				if( alphaGenValues[ style ] == NULL )
 				{
 					sprintf( key, "_style%dalphagen", style );
-					alphaGenValues[ style ] = (char*) ValueForKey( &entities[ 0 ], key );
+					alphaGenValues[ style ] = ValueForKey( &entities[ 0 ], key );
 				}
 				if( alphaGenValues[ style ][ 0 ] != '\0' )
 					sprintf( alphaGen, "\t\talphaGen %s // style %d\n", alphaGenValues[ style ], style );
@@ -3108,7 +3383,7 @@ void StoreSurfaceLightmaps( void )
 	}
 	
 	/* finish */
-	Sys_FPrintf( SYS_VRB, "done.\n" );
+	Sys_Printf( "done.\n" );
 	
 	/* calc num stored */
 	numStored = numBSPLightBytes / 3;
diff --git a/tools/quake3/q3map2/main.c b/tools/quake3/q3map2/main.c
index 86d4d498..074a4a43 100644
--- a/tools/quake3/q3map2/main.c
+++ b/tools/quake3/q3map2/main.c
@@ -1,4 +1,4 @@
-/* -------------------------------------------------------------------------------
+/* -------------------------------------------------------------------------------;
 
 Copyright (C) 1999-2007 id Software, Inc. and contributors.
 For a list of contributors, see the accompanying CONTRIBUTORS file.
@@ -36,6 +36,8 @@ several games based on the Quake III Arena engine, in the form of "Q3Map2."
 /* dependencies */
 #include "q3map2.h"
 
+
+
 /*
 Random()
 returns a pseudorandom number between 0 and 1
@@ -60,17 +62,752 @@ static void ExitQ3Map( void )
 		free( mapDrawSurfs );
 }
 
-static int MD4BlockChecksum( void * buffer, int length ) {
-	unsigned char digest[16];
-	int checksum;
-
-	md4_get_digest( buffer, length, digest );
-	/* I suppose it has to be done that way for legacy reasons? */
-	checksum = digest[0] & ( digest[1] << 8 ) & ( digest[2] << 16 ) & ( digest[3] << 24 );
-	checksum ^= digest[4] & ( digest[5] << 8 ) & ( digest[6] << 16 ) & ( digest[7] << 24 );
-	checksum ^= digest[8] & ( digest[9] << 8 ) & ( digest[10] << 16 ) & ( digest[11] << 24 );
-	checksum ^= digest[12] & ( digest[13] << 8 ) & ( digest[14] << 16 ) & ( digest[15] << 24 );
-	return checksum;
+
+
+/* minimap stuff */
+
+typedef struct minimap_s
+{
+	bspModel_t *model;
+	int width;
+	int height;
+	int samples;
+	float *sample_offsets;
+	float sharpen_boxmult;
+	float sharpen_centermult;
+	float boost;
+	float *data1f;
+	float *sharpendata1f;
+	vec3_t mins, size;
+}
+minimap_t;
+static minimap_t minimap;
+
+qboolean BrushIntersectionWithLine(bspBrush_t *brush, vec3_t start, vec3_t dir, float *t_in, float *t_out)
+{
+	int i;
+	qboolean in = qfalse, out = qfalse;
+	bspBrushSide_t *sides = &bspBrushSides[brush->firstSide];
+
+	for(i = 0; i < brush->numSides; ++i)
+	{
+		bspPlane_t *p = &bspPlanes[sides[i].planeNum];
+		float sn = DotProduct(start, p->normal);
+		float dn = DotProduct(dir, p->normal);
+		if(dn == 0)
+		{
+			if(sn > p->dist)
+				return qfalse; // outside!
+		}
+		else
+		{
+			float t = (p->dist - sn) / dn;
+			if(dn < 0)
+			{
+				if(!in || t > *t_in)
+				{
+					*t_in = t;
+					in = qtrue;
+					// as t_in can only increase, and t_out can only decrease, early out
+					if(out && *t_in >= *t_out)
+						return qfalse;
+				}
+			}
+			else
+			{
+				if(!out || t < *t_out)
+				{
+					*t_out = t;
+					out = qtrue;
+					// as t_in can only increase, and t_out can only decrease, early out
+					if(in && *t_in >= *t_out)
+						return qfalse;
+				}
+			}
+		}
+	}
+	return in && out;
+}
+
+static float MiniMapSample(float x, float y)
+{
+	vec3_t org, dir;
+	int i, bi;
+	float t0, t1;
+	float samp;
+	bspBrush_t *b;
+	bspBrushSide_t *s;
+	int cnt;
+
+	org[0] = x;
+	org[1] = y;
+	org[2] = 0;
+	dir[0] = 0;
+	dir[1] = 0;
+	dir[2] = 1;
+
+	cnt = 0;
+	samp = 0;
+	for(i = 0; i < minimap.model->numBSPBrushes; ++i)
+	{
+		bi = minimap.model->firstBSPBrush + i;
+		if(opaqueBrushes[bi >> 3] & (1 << (bi & 7)))
+		{
+			b = &bspBrushes[bi];
+
+			// sort out mins/maxs of the brush
+			s = &bspBrushSides[b->firstSide];
+			if(x < -bspPlanes[s[0].planeNum].dist)
+				continue;
+			if(x > +bspPlanes[s[1].planeNum].dist)
+				continue;
+			if(y < -bspPlanes[s[2].planeNum].dist)
+				continue;
+			if(y > +bspPlanes[s[3].planeNum].dist)
+				continue;
+
+			if(BrushIntersectionWithLine(b, org, dir, &t0, &t1))
+			{
+				samp += t1 - t0;
+				++cnt;
+			}
+		}
+	}
+
+	return samp;
+}
+
+void RandomVector2f(float v[2])
+{
+	do
+	{
+		v[0] = 2 * Random() - 1;
+		v[1] = 2 * Random() - 1;
+	}
+	while(v[0] * v[0] + v[1] * v[1] > 1);
+}
+
+static void MiniMapRandomlySupersampled(int y)
+{
+	int x, i;
+	float *p = &minimap.data1f[y * minimap.width];
+	float ymin = minimap.mins[1] + minimap.size[1] * (y / (float) minimap.height);
+	float dx   =                   minimap.size[0]      / (float) minimap.width;
+	float dy   =                   minimap.size[1]      / (float) minimap.height;
+	float uv[2];
+	float thisval;
+
+	for(x = 0; x < minimap.width; ++x)
+	{
+		float xmin = minimap.mins[0] + minimap.size[0] * (x / (float) minimap.width);
+		float val = 0;
+
+		for(i = 0; i < minimap.samples; ++i)
+		{
+			RandomVector2f(uv);
+			thisval = MiniMapSample(
+				xmin + (uv[0] + 0.5) * dx, /* exaggerated random pattern for better results */
+				ymin + (uv[1] + 0.5) * dy  /* exaggerated random pattern for better results */
+			);
+			val += thisval;
+		}
+		val /= minimap.samples * minimap.size[2];
+		*p++ = val;
+	}
+}
+
+static void MiniMapSupersampled(int y)
+{
+	int x, i;
+	float *p = &minimap.data1f[y * minimap.width];
+	float ymin = minimap.mins[1] + minimap.size[1] * (y / (float) minimap.height);
+	float dx   =                   minimap.size[0]      / (float) minimap.width;
+	float dy   =                   minimap.size[1]      / (float) minimap.height;
+
+	for(x = 0; x < minimap.width; ++x)
+	{
+		float xmin = minimap.mins[0] + minimap.size[0] * (x / (float) minimap.width);
+		float val = 0;
+
+		for(i = 0; i < minimap.samples; ++i)
+		{
+			float thisval = MiniMapSample(
+				xmin + minimap.sample_offsets[2*i+0] * dx,
+				ymin + minimap.sample_offsets[2*i+1] * dy
+			);
+			val += thisval;
+		}
+		val /= minimap.samples * minimap.size[2];
+		*p++ = val;
+	}
+}
+
+static void MiniMapNoSupersampling(int y)
+{
+	int x;
+	float *p = &minimap.data1f[y * minimap.width];
+	float ymin = minimap.mins[1] + minimap.size[1] * ((y + 0.5) / (float) minimap.height);
+
+	for(x = 0; x < minimap.width; ++x)
+	{
+		float xmin = minimap.mins[0] + minimap.size[0] * ((x + 0.5) / (float) minimap.width);
+		*p++ = MiniMapSample(xmin, ymin) / minimap.size[2];
+	}
+}
+
+static void MiniMapSharpen(int y)
+{
+	int x;
+	qboolean up = (y > 0);
+	qboolean down = (y < minimap.height - 1);
+	float *p = &minimap.data1f[y * minimap.width];
+	float *q = &minimap.sharpendata1f[y * minimap.width];
+
+	for(x = 0; x < minimap.width; ++x)
+	{
+		qboolean left = (x > 0);
+		qboolean right = (x < minimap.width - 1);
+		float val = p[0] * minimap.sharpen_centermult;
+
+		if(left && up)
+			val += p[-1 -minimap.width] * minimap.sharpen_boxmult;
+		if(left && down)
+			val += p[-1 +minimap.width] * minimap.sharpen_boxmult;
+		if(right && up)
+			val += p[+1 -minimap.width] * minimap.sharpen_boxmult;
+		if(right && down)
+			val += p[+1 +minimap.width] * minimap.sharpen_boxmult;
+			
+		if(left)
+			val += p[-1] * minimap.sharpen_boxmult;
+		if(right)
+			val += p[+1] * minimap.sharpen_boxmult;
+		if(up)
+			val += p[-minimap.width] * minimap.sharpen_boxmult;
+		if(down)
+			val += p[+minimap.width] * minimap.sharpen_boxmult;
+
+		++p;
+		*q++ = val;
+	}
+}
+
+static void MiniMapContrastBoost(int y)
+{
+	int x;
+	float *q = &minimap.data1f[y * minimap.width];
+	for(x = 0; x < minimap.width; ++x)
+	{
+		*q = *q * minimap.boost / ((minimap.boost - 1) * *q + 1);
+		++q;
+	}
+}
+
+void MiniMapMakeMinsMaxs(vec3_t mins_in, vec3_t maxs_in, float border, qboolean keepaspect)
+{
+	vec3_t mins, maxs, extend;
+	VectorCopy(mins_in, mins);
+	VectorCopy(maxs_in, maxs);
+
+	// line compatible to nexuiz mapinfo
+	Sys_Printf("size %f %f %f %f %f %f\n", mins[0], mins[1], mins[2], maxs[0], maxs[1], maxs[2]);
+
+	if(keepaspect)
+	{
+		VectorSubtract(maxs, mins, extend);
+		if(extend[1] > extend[0])
+		{
+			mins[0] -= (extend[1] - extend[0]) * 0.5;
+			maxs[0] += (extend[1] - extend[0]) * 0.5;
+		}
+		else
+		{
+			mins[1] -= (extend[0] - extend[1]) * 0.5;
+			maxs[1] += (extend[0] - extend[1]) * 0.5;
+		}
+	}
+
+	/* border: amount of black area around the image */
+	/* input: border, 1-2*border, border but we need border/(1-2*border) */
+
+	VectorSubtract(maxs, mins, extend);
+	VectorScale(extend, border / (1 - 2 * border), extend);
+
+	VectorSubtract(mins, extend, mins);
+	VectorAdd(maxs, extend, maxs);
+
+	VectorCopy(mins, minimap.mins);
+	VectorSubtract(maxs, mins, minimap.size);
+
+	// line compatible to nexuiz mapinfo
+	Sys_Printf("size_texcoords %f %f %f %f %f %f\n", mins[0], mins[1], mins[2], maxs[0], maxs[1], maxs[2]);
+}
+
+/*
+MiniMapSetupBrushes()
+determines solid non-sky brushes in the world
+*/
+
+void MiniMapSetupBrushes( void )
+{
+	int				i, b, compileFlags;
+	bspBrush_t		*brush;
+	bspShader_t		*shader;
+	shaderInfo_t	*si;
+	
+	
+	/* note it */
+	Sys_FPrintf( SYS_VRB, "--- MiniMapSetupBrushes ---\n" );
+	
+	/* allocate */
+	if( opaqueBrushes == NULL )
+		opaqueBrushes = safe_malloc( numBSPBrushes / 8 + 1 );
+	
+	/* clear */
+	memset( opaqueBrushes, 0, numBSPBrushes / 8 + 1 );
+	numOpaqueBrushes = 0;
+	
+	/* walk the list of worldspawn brushes */
+	for( i = 0; i < minimap.model->numBSPBrushes; i++ )
+	{
+		/* get brush */
+		b = minimap.model->firstBSPBrush + i;
+		brush = &bspBrushes[ b ];
+		
+#if 0
+		/* check all sides */
+		compileFlags = 0;
+		for( j = 0; j < brush->numSides; j++ )
+		{
+			/* do bsp shader calculations */
+			side = &bspBrushSides[ brush->firstSide + j ];
+			shader = &bspShaders[ side->shaderNum ];
+			
+			/* get shader info */
+			si = ShaderInfoForShader( shader->shader );
+			if( si == NULL )
+				continue;
+			
+			/* or together compile flags */
+			compileFlags |= si->compileFlags;
+		}
+#else
+		shader = &bspShaders[ brush->shaderNum ];
+		si = ShaderInfoForShader( shader->shader );
+		if( si == NULL )
+			compileFlags = 0;
+		else
+			compileFlags = si->compileFlags;
+#endif
+		
+		/* determine if this brush is solid */
+		if( (compileFlags & (C_SOLID | C_SKY)) == C_SOLID )
+		{
+			opaqueBrushes[ b >> 3 ] |= (1 << (b & 7));
+			numOpaqueBrushes++;
+			maxOpaqueBrush = i;
+		}
+	}
+	
+	/* emit some statistics */
+	Sys_FPrintf( SYS_VRB, "%9d solid brushes\n", numOpaqueBrushes );
+}
+
+qboolean MiniMapEvaluateSampleOffsets(int *bestj, int *bestk, float *bestval)
+{
+	float val, dx, dy;
+	int j, k;
+
+	*bestj = *bestk = -1;
+	*bestval = 3; /* max possible val is 2 */
+
+	for(j = 0; j < minimap.samples; ++j)
+		for(k = j + 1; k < minimap.samples; ++k)
+		{
+			dx = minimap.sample_offsets[2*j+0] - minimap.sample_offsets[2*k+0];
+			dy = minimap.sample_offsets[2*j+1] - minimap.sample_offsets[2*k+1];
+			if(dx > +0.5) dx -= 1;
+			if(dx < -0.5) dx += 1;
+			if(dy > +0.5) dy -= 1;
+			if(dy < -0.5) dy += 1;
+			val = dx * dx + dy * dy;
+			if(val < *bestval)
+			{
+				*bestj = j;
+				*bestk = k;
+				*bestval = val;
+			}
+		}
+	
+	return *bestval < 3;
+}
+
+void MiniMapMakeSampleOffsets()
+{
+	int i, j, k, jj, kk;
+	float val, valj, valk, sx, sy, rx, ry;
+
+	Sys_Printf( "Generating good sample offsets (this may take a while)...\n" );
+
+	/* start with entirely random samples */
+	for(i = 0; i < minimap.samples; ++i)
+	{
+		minimap.sample_offsets[2*i+0] = Random();
+		minimap.sample_offsets[2*i+1] = Random();
+	}
+
+	for(i = 0; i < 1000; ++i)
+	{
+		if(MiniMapEvaluateSampleOffsets(&j, &k, &val))
+		{
+			sx = minimap.sample_offsets[2*j+0];
+			sy = minimap.sample_offsets[2*j+1];
+			minimap.sample_offsets[2*j+0] = rx = Random();
+			minimap.sample_offsets[2*j+1] = ry = Random();
+			if(!MiniMapEvaluateSampleOffsets(&jj, &kk, &valj))
+				valj = -1;
+			minimap.sample_offsets[2*j+0] = sx;
+			minimap.sample_offsets[2*j+1] = sy;
+
+			sx = minimap.sample_offsets[2*k+0];
+			sy = minimap.sample_offsets[2*k+1];
+			minimap.sample_offsets[2*k+0] = rx;
+			minimap.sample_offsets[2*k+1] = ry;
+			if(!MiniMapEvaluateSampleOffsets(&jj, &kk, &valk))
+				valk = -1;
+			minimap.sample_offsets[2*k+0] = sx;
+			minimap.sample_offsets[2*k+1] = sy;
+
+			if(valj > valk)
+			{
+				if(valj > val)
+				{
+					/* valj is the greatest */
+					minimap.sample_offsets[2*j+0] = rx;
+					minimap.sample_offsets[2*j+1] = ry;
+					i = -1;
+				}
+				else
+				{
+					/* valj is the greater and it is useless - forget it */
+				}
+			}
+			else
+			{
+				if(valk > val)
+				{
+					/* valk is the greatest */
+					minimap.sample_offsets[2*k+0] = rx;
+					minimap.sample_offsets[2*k+1] = ry;
+					i = -1;
+				}
+				else
+				{
+					/* valk is the greater and it is useless - forget it */
+				}
+			}
+		}
+		else
+			break;
+	}
+}
+
+void MergeRelativePath(char *out, const char *absolute, const char *relative)
+{
+	const char *endpos = absolute + strlen(absolute);
+	while(endpos != absolute && (endpos[-1] == '/' || endpos[-1] == '\\'))
+		--endpos;
+	while(relative[0] == '.' && relative[1] == '.' && (relative[2] == '/' || relative[2] == '\\'))
+	{
+		relative += 3;
+		while(endpos != absolute)
+		{
+			--endpos;
+			if(*endpos == '/' || *endpos == '\\')
+				break;
+		}
+		while(endpos != absolute && (endpos[-1] == '/' || endpos[-1] == '\\'))
+			--endpos;
+	}
+	memcpy(out, absolute, endpos - absolute);
+	out[endpos - absolute] = '/';
+	strcpy(out + (endpos - absolute + 1), relative);
+}
+
+int MiniMapBSPMain( int argc, char **argv )
+{
+	char minimapFilename[1024];
+	char basename[1024];
+	char path[1024];
+	char relativeMinimapFilename[1024];
+	float minimapSharpen;
+	float border;
+	byte *data4b, *p;
+	float *q;
+	int x, y;
+	int i;
+	miniMapMode_t mode;
+	vec3_t mins, maxs;
+	qboolean keepaspect;
+
+	/* arg checking */
+	if( argc < 2 )
+	{
+		Sys_Printf( "Usage: q3map [-v] -minimap [-size n] [-sharpen f] [-samples n | -random n] [-o filename.tga] [-minmax Xmin Ymin Zmin Xmax Ymax Zmax] <mapname>\n" );
+		return 0;
+	}
+
+	/* load the BSP first */
+	strcpy( source, ExpandArg( argv[ argc - 1 ] ) );
+	StripExtension( source );
+	DefaultExtension( source, ".bsp" );
+	Sys_Printf( "Loading %s\n", source );
+	BeginMapShaderFile( source );
+	LoadShaderInfo();
+	LoadBSPFile( source );
+
+	minimap.model = &bspModels[0];
+	VectorCopy(minimap.model->mins, mins);
+	VectorCopy(minimap.model->maxs, maxs);
+
+	*minimapFilename = 0;
+	minimapSharpen = game->miniMapSharpen;
+	minimap.width = minimap.height = game->miniMapSize;
+	border = game->miniMapBorder;
+	keepaspect = game->miniMapKeepAspect;
+	mode = game->miniMapMode;
+
+	minimap.samples = 1;
+	minimap.sample_offsets = NULL;
+	minimap.boost = 1.0;
+
+	/* process arguments */
+	for( i = 1; i < (argc - 1); i++ )
+	{
+		if( !strcmp( argv[ i ],  "-size" ) )
+ 		{
+			minimap.width = minimap.height = atoi(argv[i + 1]);
+			i++;
+			Sys_Printf( "Image size set to %i\n", minimap.width );
+ 		}
+		else if( !strcmp( argv[ i ],  "-sharpen" ) )
+ 		{
+			minimapSharpen = atof(argv[i + 1]);
+			i++;
+			Sys_Printf( "Sharpening coefficient set to %f\n", minimapSharpen );
+ 		}
+		else if( !strcmp( argv[ i ],  "-samples" ) )
+ 		{
+			minimap.samples = atoi(argv[i + 1]);
+			i++;
+			Sys_Printf( "Samples set to %i\n", minimap.samples );
+			if(minimap.sample_offsets)
+				free(minimap.sample_offsets);
+			minimap.sample_offsets = malloc(2 * sizeof(*minimap.sample_offsets) * minimap.samples);
+			MiniMapMakeSampleOffsets();
+ 		}
+		else if( !strcmp( argv[ i ],  "-random" ) )
+ 		{
+			minimap.samples = atoi(argv[i + 1]);
+			i++;
+			Sys_Printf( "Random samples set to %i\n", minimap.samples );
+			if(minimap.sample_offsets)
+				free(minimap.sample_offsets);
+			minimap.sample_offsets = NULL;
+ 		}
+		else if( !strcmp( argv[ i ],  "-border" ) )
+ 		{
+			border = atof(argv[i + 1]);
+			i++;
+			Sys_Printf( "Border set to %f\n", border );
+ 		}
+		else if( !strcmp( argv[ i ],  "-keepaspect" ) )
+ 		{
+			keepaspect = qtrue;
+			Sys_Printf( "Keeping aspect ratio by letterboxing\n", border );
+ 		}
+		else if( !strcmp( argv[ i ],  "-nokeepaspect" ) )
+ 		{
+			keepaspect = qfalse;
+			Sys_Printf( "Not keeping aspect ratio\n", border );
+ 		}
+		else if( !strcmp( argv[ i ],  "-o" ) )
+ 		{
+			strcpy(minimapFilename, argv[i + 1]);
+			i++;
+			Sys_Printf( "Output file name set to %s\n", minimapFilename );
+ 		}
+		else if( !strcmp( argv[ i ],  "-minmax" ) && i < (argc - 7) )
+ 		{
+			mins[0] = atof(argv[i + 1]);
+			mins[1] = atof(argv[i + 2]);
+			mins[2] = atof(argv[i + 3]);
+			maxs[0] = atof(argv[i + 4]);
+			maxs[1] = atof(argv[i + 5]);
+			maxs[2] = atof(argv[i + 6]);
+			i += 6;
+			Sys_Printf( "Map mins/maxs overridden\n" );
+ 		}
+		else if( !strcmp( argv[ i ],  "-gray" ) )
+ 		{
+			mode = MINIMAP_MODE_GRAY;
+			Sys_Printf( "Writing as white-on-black image\n" );
+ 		}
+		else if( !strcmp( argv[ i ],  "-black" ) )
+ 		{
+			mode = MINIMAP_MODE_BLACK;
+			Sys_Printf( "Writing as black alpha image\n" );
+ 		}
+		else if( !strcmp( argv[ i ],  "-white" ) )
+ 		{
+			mode = MINIMAP_MODE_WHITE;
+			Sys_Printf( "Writing as white alpha image\n" );
+ 		}
+		else if( !strcmp( argv[ i ],  "-boost" ) )
+ 		{
+			minimap.boost = atof(argv[i + 1]);
+			i++;
+			Sys_Printf( "Contrast boost set to %f\n", minimap.boost );
+ 		}
+	}
+
+	MiniMapMakeMinsMaxs(mins, maxs, border, keepaspect);
+
+	if(!*minimapFilename)
+	{
+		ExtractFileBase(source, basename);
+		ExtractFilePath(source, path);
+		sprintf(relativeMinimapFilename, game->miniMapNameFormat, basename);
+		MergeRelativePath(minimapFilename, path, relativeMinimapFilename);
+		Sys_Printf("Output file name automatically set to %s\n", minimapFilename);
+	}
+	ExtractFilePath(minimapFilename, path);
+	Q_mkdir(path);
+
+	if(minimapSharpen >= 0)
+	{
+		minimap.sharpen_centermult = 8 * minimapSharpen + 1;
+		minimap.sharpen_boxmult    =    -minimapSharpen;
+	}
+
+	minimap.data1f = safe_malloc(minimap.width * minimap.height * sizeof(*minimap.data1f));
+	data4b = safe_malloc(minimap.width * minimap.height * 4);
+	if(minimapSharpen >= 0)
+		minimap.sharpendata1f = safe_malloc(minimap.width * minimap.height * sizeof(*minimap.data1f));
+
+	MiniMapSetupBrushes();
+
+	if(minimap.samples <= 1)
+	{
+		Sys_Printf( "\n--- MiniMapNoSupersampling (%d) ---\n", minimap.height );
+		RunThreadsOnIndividual(minimap.height, qtrue, MiniMapNoSupersampling);
+	}
+	else
+	{
+		if(minimap.sample_offsets)
+		{
+			Sys_Printf( "\n--- MiniMapSupersampled (%d) ---\n", minimap.height );
+			RunThreadsOnIndividual(minimap.height, qtrue, MiniMapSupersampled);
+		}
+		else
+		{
+			Sys_Printf( "\n--- MiniMapRandomlySupersampled (%d) ---\n", minimap.height );
+			RunThreadsOnIndividual(minimap.height, qtrue, MiniMapRandomlySupersampled);
+		}
+	}
+
+	if(minimap.boost != 1.0)
+	{
+		Sys_Printf( "\n--- MiniMapContrastBoost (%d) ---\n", minimap.height );
+		RunThreadsOnIndividual(minimap.height, qtrue, MiniMapContrastBoost);
+	}
+
+	if(minimap.sharpendata1f)
+	{
+		Sys_Printf( "\n--- MiniMapSharpen (%d) ---\n", minimap.height );
+		RunThreadsOnIndividual(minimap.height, qtrue, MiniMapSharpen);
+		q = minimap.sharpendata1f;
+	}
+	else
+	{
+		q = minimap.data1f;
+	}
+
+	Sys_Printf( "\nConverting...");
+
+	switch(mode)
+	{
+		case MINIMAP_MODE_GRAY:
+			p = data4b;
+			for(y = 0; y < minimap.height; ++y)
+				for(x = 0; x < minimap.width; ++x)
+				{
+					byte b;
+					float v = *q++;
+					if(v < 0) v = 0;
+					if(v > 255.0/256.0) v = 255.0/256.0;
+					b = v * 256;
+					*p++ = b;
+				}
+			Sys_Printf( " writing to %s...", minimapFilename );
+			WriteTGAGray(minimapFilename, data4b, minimap.width, minimap.height);
+			break;
+		case MINIMAP_MODE_BLACK:
+			p = data4b;
+			for(y = 0; y < minimap.height; ++y)
+				for(x = 0; x < minimap.width; ++x)
+				{
+					byte b;
+					float v = *q++;
+					if(v < 0) v = 0;
+					if(v > 255.0/256.0) v = 255.0/256.0;
+					b = v * 256;
+					*p++ = 0;
+					*p++ = 0;
+					*p++ = 0;
+					*p++ = b;
+				}
+			Sys_Printf( " writing to %s...", minimapFilename );
+			WriteTGA(minimapFilename, data4b, minimap.width, minimap.height);
+			break;
+		case MINIMAP_MODE_WHITE:
+			p = data4b;
+			for(y = 0; y < minimap.height; ++y)
+				for(x = 0; x < minimap.width; ++x)
+				{
+					byte b;
+					float v = *q++;
+					if(v < 0) v = 0;
+					if(v > 255.0/256.0) v = 255.0/256.0;
+					b = v * 256;
+					*p++ = 255;
+					*p++ = 255;
+					*p++ = 255;
+					*p++ = b;
+				}
+			Sys_Printf( " writing to %s...", minimapFilename );
+			WriteTGA(minimapFilename, data4b, minimap.width, minimap.height);
+			break;
+	}
+
+	Sys_Printf( " done.\n" );
+
+	/* return to sender */
+	return 0;
+}
+
+
+
+
+
+/*
+MD4BlockChecksum()
+calculates an md4 checksum for a block of data
+*/
+
+static int MD4BlockChecksum( void *buffer, int length )
+{
+	return Com_BlockChecksum(buffer, length);
 }
 
 /*
@@ -252,8 +989,8 @@ int AnalyzeBSP( int argc, char **argv )
 		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';
+		memcpy( lumpString, (char*) lump, (length < sizeof(lumpString) ? length : sizeof(lumpString)-1) );
+		lumpString[ sizeof(lumpString)-1 ] = '\0';
 		
 		/* print basic lump info */
 		Sys_Printf( "Lump:          %d\n", i );
@@ -368,6 +1105,74 @@ int BSPInfo( int count, char **fileNames )
 }
 
 
+static void ExtrapolateTexcoords(const float *axyz, const float *ast, const float *bxyz, const float *bst, const float *cxyz, const float *cst, const float *axyz_new, float *ast_out, const float *bxyz_new, float *bst_out, const float *cxyz_new, float *cst_out)
+{
+	vec4_t scoeffs, tcoeffs;
+	float md;
+	m4x4_t solvematrix;
+
+	vec3_t norm;
+	vec3_t dab, dac;
+	VectorSubtract(bxyz, axyz, dab);
+	VectorSubtract(cxyz, axyz, dac);
+	CrossProduct(dab, dac, norm);
+	
+	// assume:
+	//   s = f(x, y, z)
+	//   s(v + norm) = s(v) when n ortho xyz
+	
+	// s(v) = DotProduct(v, scoeffs) + scoeffs[3]
+
+	// solve:
+	//   scoeffs * (axyz, 1) == ast[0]
+	//   scoeffs * (bxyz, 1) == bst[0]
+	//   scoeffs * (cxyz, 1) == cst[0]
+	//   scoeffs * (norm, 0) == 0
+	// scoeffs * [axyz, 1 | bxyz, 1 | cxyz, 1 | norm, 0] = [ast[0], bst[0], cst[0], 0]
+	solvematrix[0] = axyz[0];
+	solvematrix[4] = axyz[1];
+	solvematrix[8] = axyz[2];
+	solvematrix[12] = 1;
+	solvematrix[1] = bxyz[0];
+	solvematrix[5] = bxyz[1];
+	solvematrix[9] = bxyz[2];
+	solvematrix[13] = 1;
+	solvematrix[2] = cxyz[0];
+	solvematrix[6] = cxyz[1];
+	solvematrix[10] = cxyz[2];
+	solvematrix[14] = 1;
+	solvematrix[3] = norm[0];
+	solvematrix[7] = norm[1];
+	solvematrix[11] = norm[2];
+	solvematrix[15] = 0;
+
+	md = m4_det(solvematrix);
+	if(md*md < 1e-10)
+	{
+		Sys_Printf("Cannot invert some matrix, some texcoords aren't extrapolated!");
+		return;
+	}
+
+	m4x4_invert(solvematrix);
+
+	scoeffs[0] = ast[0];
+	scoeffs[1] = bst[0];
+	scoeffs[2] = cst[0];
+	scoeffs[3] = 0;
+	m4x4_transform_vec4(solvematrix, scoeffs);
+	tcoeffs[0] = ast[1];
+	tcoeffs[1] = bst[1];
+	tcoeffs[2] = cst[1];
+	tcoeffs[3] = 0;
+	m4x4_transform_vec4(solvematrix, tcoeffs);
+
+	ast_out[0] = scoeffs[0] * axyz_new[0] + scoeffs[1] * axyz_new[1] + scoeffs[2] * axyz_new[2] + scoeffs[3];
+	ast_out[1] = tcoeffs[0] * axyz_new[0] + tcoeffs[1] * axyz_new[1] + tcoeffs[2] * axyz_new[2] + tcoeffs[3];
+	bst_out[0] = scoeffs[0] * bxyz_new[0] + scoeffs[1] * bxyz_new[1] + scoeffs[2] * bxyz_new[2] + scoeffs[3];
+	bst_out[1] = tcoeffs[0] * bxyz_new[0] + tcoeffs[1] * bxyz_new[1] + tcoeffs[2] * bxyz_new[2] + tcoeffs[3];
+	cst_out[0] = scoeffs[0] * cxyz_new[0] + scoeffs[1] * cxyz_new[1] + scoeffs[2] * cxyz_new[2] + scoeffs[3];
+	cst_out[1] = tcoeffs[0] * cxyz_new[0] + tcoeffs[1] * cxyz_new[1] + tcoeffs[2] * cxyz_new[2] + tcoeffs[3];
+}
 
 /*
 ScaleBSPMain()
@@ -376,24 +1181,53 @@ amaze and confuse your enemies with wierd scaled maps!
 
 int ScaleBSPMain( int argc, char **argv )
 {
-	int			i;
-	float		f, scale;
+	int			i, j;
+	float		f, a;
+	vec3_t scale;
 	vec3_t		vec;
 	char		str[ 1024 ];
+	int uniform, axis;
+	qboolean texscale;
+	float *old_xyzst = NULL;
+	float spawn_ref = 0;
 	
 	
 	/* arg checking */
-	if( argc < 2 )
+	if( argc < 3 )
 	{
-		Sys_Printf( "Usage: q3map -scale <value> [-v] <mapname>\n" );
+		Sys_Printf( "Usage: q3map [-v] -scale [-tex] [-spawn_ref <value>] <value> <mapname>\n" );
 		return 0;
 	}
 	
+	texscale = qfalse;
+	for(i = 1; i < argc-2; ++i)
+	{
+		if(!strcmp(argv[i], "-tex"))
+		{
+			texscale = qtrue;
+		}
+		else if(!strcmp(argv[i], "-spawn_ref"))
+		{
+			spawn_ref = atof(argv[i+1]);
+			++i;
+		}
+		else
+			break;
+	}
+	
 	/* get scale */
-	scale = atof( argv[ argc - 2 ] );
-	if( scale == 0.0f )
+	// if(argc-2 >= i) // always true
+		scale[2] = scale[1] = scale[0] = atof( argv[ argc - 2 ] );
+	if(argc-3 >= i)
+		scale[1] = scale[0] = atof( argv[ argc - 3 ] );
+	if(argc-4 >= i)
+		scale[0] = atof( argv[ argc - 4 ] );
+
+	uniform = ((scale[0] == scale[1]) && (scale[1] == scale[2]));
+
+	if( scale[0] == 0.0f || scale[1] == 0.0f || scale[2] == 0.0f )
 	{
-		Sys_Printf( "Usage: q3map -scale <value> [-v] <mapname>\n" );
+		Sys_Printf( "Usage: q3map [-v] -scale [-tex] [-spawn_ref <value>] <value> <mapname>\n" );
 		Sys_Printf( "Non-zero scale value required.\n" );
 		return 0;
 	}
@@ -417,59 +1251,172 @@ int ScaleBSPMain( int argc, char **argv )
 	{
 		/* scale origin */
 		GetVectorForKey( &entities[ i ], "origin", vec );
-		if( (vec[ 0 ] + vec[ 1 ] + vec[ 2 ]) )
+		if( (vec[ 0 ] || vec[ 1 ] || vec[ 2 ]) )
 		{
-			VectorScale( vec, scale, vec );
+			if(!strncmp(ValueForKey(&entities[i], "classname"), "info_player_", 12))
+				vec[2] += spawn_ref;
+			vec[0] *= scale[0];
+			vec[1] *= scale[1];
+			vec[2] *= scale[2];
+			if(!strncmp(ValueForKey(&entities[i], "classname"), "info_player_", 12))
+				vec[2] -= spawn_ref;
 			sprintf( str, "%f %f %f", vec[ 0 ], vec[ 1 ], vec[ 2 ] );
 			SetKeyValue( &entities[ i ], "origin", str );
 		}
+
+		a = FloatForKey( &entities[ i ], "angle" );
+		if(a == -1 || a == -2) // z scale
+			axis = 2;
+		else if(fabs(sin(DEG2RAD(a))) < 0.707)
+			axis = 0;
+		else
+			axis = 1;
 		
 		/* scale door lip */
 		f = FloatForKey( &entities[ i ], "lip" );
 		if( f )
 		{
-			f *= scale;
+			f *= scale[axis];
 			sprintf( str, "%f", f );
 			SetKeyValue( &entities[ i ], "lip", str );
 		}
+		
+		/* scale plat height */
+		f = FloatForKey( &entities[ i ], "height" );
+		if( f )
+		{
+			f *= scale[2];
+			sprintf( str, "%f", f );
+			SetKeyValue( &entities[ i ], "height", str );
+		}
+
+		// TODO maybe allow a definition file for entities to specify which values are scaled how?
 	}
 	
 	/* scale models */
 	for( i = 0; i < numBSPModels; i++ )
 	{
-		VectorScale( bspModels[ i ].mins, scale, bspModels[ i ].mins );
-		VectorScale( bspModels[ i ].maxs, scale, bspModels[ i ].maxs );
+		bspModels[ i ].mins[0] *= scale[0];
+		bspModels[ i ].mins[1] *= scale[1];
+		bspModels[ i ].mins[2] *= scale[2];
+		bspModels[ i ].maxs[0] *= scale[0];
+		bspModels[ i ].maxs[1] *= scale[1];
+		bspModels[ i ].maxs[2] *= scale[2];
 	}
 	
 	/* scale nodes */
 	for( i = 0; i < numBSPNodes; i++ )
 	{
-		VectorScale( bspNodes[ i ].mins, scale, bspNodes[ i ].mins );
-		VectorScale( bspNodes[ i ].maxs, scale, bspNodes[ i ].maxs );
+		bspNodes[ i ].mins[0] *= scale[0];
+		bspNodes[ i ].mins[1] *= scale[1];
+		bspNodes[ i ].mins[2] *= scale[2];
+		bspNodes[ i ].maxs[0] *= scale[0];
+		bspNodes[ i ].maxs[1] *= scale[1];
+		bspNodes[ i ].maxs[2] *= scale[2];
 	}
 	
 	/* scale leafs */
 	for( i = 0; i < numBSPLeafs; i++ )
 	{
-		VectorScale( bspLeafs[ i ].mins, scale, bspLeafs[ i ].mins );
-		VectorScale( bspLeafs[ i ].maxs, scale, bspLeafs[ i ].maxs );
+		bspLeafs[ i ].mins[0] *= scale[0];
+		bspLeafs[ i ].mins[1] *= scale[1];
+		bspLeafs[ i ].mins[2] *= scale[2];
+		bspLeafs[ i ].maxs[0] *= scale[0];
+		bspLeafs[ i ].maxs[1] *= scale[1];
+		bspLeafs[ i ].maxs[2] *= scale[2];
 	}
 	
+	if(texscale)
+	{
+		Sys_Printf("Using texture unlocking (and probably breaking texture alignment a lot)\n");
+		old_xyzst = safe_malloc(sizeof(*old_xyzst) * numBSPDrawVerts * 5);
+		for(i = 0; i < numBSPDrawVerts; i++)
+		{
+			old_xyzst[5*i+0] = bspDrawVerts[i].xyz[0];
+			old_xyzst[5*i+1] = bspDrawVerts[i].xyz[1];
+			old_xyzst[5*i+2] = bspDrawVerts[i].xyz[2];
+			old_xyzst[5*i+3] = bspDrawVerts[i].st[0];
+			old_xyzst[5*i+4] = bspDrawVerts[i].st[1];
+		}
+	}
+
 	/* scale drawverts */
 	for( i = 0; i < numBSPDrawVerts; i++ )
-		VectorScale( bspDrawVerts[ i ].xyz, scale, bspDrawVerts[ i ].xyz );
+	{
+		bspDrawVerts[i].xyz[0] *= scale[0];
+		bspDrawVerts[i].xyz[1] *= scale[1];
+		bspDrawVerts[i].xyz[2] *= scale[2];
+		bspDrawVerts[i].normal[0] /= scale[0];
+		bspDrawVerts[i].normal[1] /= scale[1];
+		bspDrawVerts[i].normal[2] /= scale[2];
+		VectorNormalize(bspDrawVerts[i].normal, bspDrawVerts[i].normal);
+	}
+
+	if(texscale)
+	{
+		for(i = 0; i < numBSPDrawSurfaces; i++)
+		{
+			switch(bspDrawSurfaces[i].surfaceType)
+			{
+				case SURFACE_FACE:
+				case SURFACE_META:
+					if(bspDrawSurfaces[i].numIndexes % 3)
+						Error("Not a triangulation!");
+					for(j = bspDrawSurfaces[i].firstIndex; j < bspDrawSurfaces[i].firstIndex + bspDrawSurfaces[i].numIndexes; j += 3)
+					{
+						int ia = bspDrawIndexes[j] + bspDrawSurfaces[i].firstVert, ib = bspDrawIndexes[j+1] + bspDrawSurfaces[i].firstVert, ic = bspDrawIndexes[j+2] + bspDrawSurfaces[i].firstVert;
+						bspDrawVert_t *a = &bspDrawVerts[ia], *b = &bspDrawVerts[ib], *c = &bspDrawVerts[ic];
+						float *oa = &old_xyzst[ia*5], *ob = &old_xyzst[ib*5], *oc = &old_xyzst[ic*5];
+						// extrapolate:
+						//   a->xyz -> oa
+						//   b->xyz -> ob
+						//   c->xyz -> oc
+						ExtrapolateTexcoords(
+							&oa[0], &oa[3],
+							&ob[0], &ob[3],
+							&oc[0], &oc[3],
+							a->xyz, a->st,
+							b->xyz, b->st,
+							c->xyz, c->st);
+					}
+					break;
+			}
+		}
+	}
 	
 	/* scale planes */
-	for( i = 0; i < numBSPPlanes; i++ )
-		bspPlanes[ i ].dist *= scale;
+	if(uniform)
+	{
+		for( i = 0; i < numBSPPlanes; i++ )
+		{
+			bspPlanes[ i ].dist *= scale[0];
+		}
+	}
+	else
+	{
+		for( i = 0; i < numBSPPlanes; i++ )
+		{
+			bspPlanes[ i ].normal[0] /= scale[0];
+			bspPlanes[ i ].normal[1] /= scale[1];
+			bspPlanes[ i ].normal[2] /= scale[2];
+			f = 1/VectorLength(bspPlanes[i].normal);
+			VectorScale(bspPlanes[i].normal, f, bspPlanes[i].normal);
+			bspPlanes[ i ].dist *= f;
+		}
+	}
 	
 	/* scale gridsize */
 	GetVectorForKey( &entities[ 0 ], "gridsize", vec );
 	if( (vec[ 0 ] + vec[ 1 ] + vec[ 2 ]) == 0.0f )
 		VectorCopy( gridSize, vec );
-	VectorScale( vec, scale, vec );
+	vec[0] *= scale[0];
+	vec[1] *= scale[1];
+	vec[2] *= scale[2];
 	sprintf( str, "%f %f %f", vec[ 0 ], vec[ 1 ], vec[ 2 ] );
 	SetKeyValue( &entities[ 0 ], "gridsize", str );
+
+	/* inject command line parameters */
+	InjectCommandLine(argv, 0, argc - 1);
 	
 	/* write the bsp */
 	UnparseEntities();
@@ -483,6 +1430,101 @@ int ScaleBSPMain( int argc, char **argv )
 }
 
 
+/*
+PseudoCompileBSP()
+a stripped down ProcessModels
+*/
+void PseudoCompileBSP(qboolean need_tree)
+{
+	int models;
+	char modelValue[10];
+	entity_t *entity;
+	face_t *faces;
+	tree_t *tree;
+	node_t *node;
+	brush_t *brush;
+	side_t *side;
+	int i;
+
+        SetDrawSurfacesBuffer();
+	mapDrawSurfs = safe_malloc( sizeof( mapDrawSurface_t ) * MAX_MAP_DRAW_SURFS );
+	memset( mapDrawSurfs, 0, sizeof( mapDrawSurface_t ) * MAX_MAP_DRAW_SURFS );
+	numMapDrawSurfs = 0;
+
+	BeginBSPFile();
+	models = 1;
+	for( mapEntityNum = 0; mapEntityNum < numEntities; mapEntityNum++ )
+	{
+		/* get entity */
+		entity = &entities[ mapEntityNum ];
+		if( entity->brushes == NULL && entity->patches == NULL )
+			continue;
+
+		if(mapEntityNum != 0)
+		{
+			sprintf( modelValue, "*%d", models++);
+			SetKeyValue(entity, "model", modelValue);
+		}
+		
+		/* process the model */
+		Sys_FPrintf( SYS_VRB, "############### model %i ###############\n", numBSPModels );
+		BeginModel();
+
+		entity->firstDrawSurf = numMapDrawSurfs;
+
+		ClearMetaTriangles();
+		PatchMapDrawSurfs(entity);
+
+		if(mapEntityNum == 0 && need_tree)
+		{
+			faces = MakeStructuralBSPFaceList(entities[0].brushes);
+			tree = FaceBSP(faces);
+			node = tree->headnode;
+		}
+		else
+		{
+			node = AllocNode();
+			node->planenum = PLANENUM_LEAF;
+			tree = AllocTree();
+			tree->headnode = node;
+		}
+
+		/* a minimized ClipSidesIntoTree */
+		for( brush = entity->brushes; brush; brush = brush->next )
+		{
+			/* walk the brush sides */
+			for( i = 0; i < brush->numsides; i++ )
+			{
+				/* get side */
+				side = &brush->sides[ i ];
+				if( side->winding == NULL )
+					continue;
+				/* shader? */
+				if( side->shaderInfo == NULL )
+					continue;
+				/* save this winding as a visible surface */
+				DrawSurfaceForSide(entity, brush, side, side->winding);
+			}
+		}
+
+		if(meta)
+		{
+			ClassifyEntitySurfaces(entity);
+			MakeEntityDecals(entity);
+			MakeEntityMetaTriangles(entity);
+			SmoothMetaTriangles();
+			MergeMetaTriangles();
+		}
+		FilterDrawsurfsIntoTree(entity, tree);
+
+		FilterStructuralBrushesIntoTree(entity, tree);
+		FilterDetailBrushesIntoTree(entity, tree);
+
+		EmitBrushes(entity->brushes, &entity->firstBrush, &entity->numBrushes );
+		EndModel(entity, node);
+	}
+	EndBSPFile(qfalse);
+}
 
 /*
 ConvertBSPMain()
@@ -494,16 +1536,21 @@ int ConvertBSPMain( int argc, char **argv )
 	int		i;
 	int		(*convertFunc)( char * );
 	game_t	*convertGame;
+	char		ext[1024];
+	qboolean	map_allowed, force_bsp, force_map;
 	
 	
 	/* set default */
 	convertFunc = ConvertBSPToASE;
 	convertGame = NULL;
+	map_allowed = qfalse;
+	force_bsp = qfalse;
+	force_map = qfalse;
 	
 	/* arg checking */
 	if( argc < 1 )
 	{
-		Sys_Printf( "Usage: q3map -scale <value> [-v] <mapname>\n" );
+		Sys_Printf( "Usage: q3map -convert -format <ase|obj|map_bp|map> [-shadesasbitmap|-lightmapsastexcoord|-deluxemapsastexcoord] [-readbsp|-readmap [-meta|-patchmeta]] <mapname>\n" );
 		return 0;
 	}
 	
@@ -515,34 +1562,94 @@ int ConvertBSPMain( int argc, char **argv )
  		{
 			i++;
 			if( !Q_stricmp( argv[ i ], "ase" ) )
+			{
 				convertFunc = ConvertBSPToASE;
+				map_allowed = qfalse;
+			}
+			else if( !Q_stricmp( argv[ i ], "obj" ) )
+			{
+				convertFunc = ConvertBSPToOBJ;
+				map_allowed = qfalse;
+			}
+			else if( !Q_stricmp( argv[ i ], "map_bp" ) )
+			{
+				convertFunc = ConvertBSPToMap_BP;
+				map_allowed = qtrue;
+			}
 			else if( !Q_stricmp( argv[ i ], "map" ) )
+			{
 				convertFunc = ConvertBSPToMap;
+				map_allowed = qtrue;
+			}
 			else
 			{
 				convertGame = GetGame( argv[ i ] );
+				map_allowed = qfalse;
 				if( convertGame == NULL )
 					Sys_Printf( "Unknown conversion format \"%s\". Defaulting to ASE.\n", argv[ i ] );
 			}
  		}
+		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 ],  "-shadersasbitmap" ) )
+			shadersAsBitmap = qtrue;
+		else if( !strcmp( argv[ i ],  "-lightmapsastexcoord" ) )
+			lightmapsAsTexcoord = qtrue;
+		else if( !strcmp( argv[ i ],  "-deluxemapsastexcoord" ) )
+		{
+			lightmapsAsTexcoord = qtrue;
+			deluxemap = qtrue;
+		}
+		else if( !strcmp( argv[ i ],  "-readbsp" ) )
+			force_bsp = qtrue;
+		else if( !strcmp( argv[ i ],  "-readmap" ) )
+			force_map = qtrue;
+		else if( !strcmp( argv[ i ],  "-meta" ) )
+			meta = qtrue;
+		else if( !strcmp( argv[ i ],  "-patchmeta" ) )
+		{
+			meta = qtrue;
+			patchMeta = qtrue;
+		}
 	}
-	
-	/* 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();
+	/* clean up map name */
+	strcpy(source, ExpandArg(argv[i]));
+	ExtractFileExtension(source, ext);
+
+	if(!map_allowed && !force_map)
+		force_bsp = qtrue;
+
+	if(force_map || (!force_bsp && !Q_stricmp(ext, "map") && map_allowed))
+	{
+		if(!map_allowed)
+			Sys_Printf("WARNING: the requested conversion should not be done from .map files. Compile a .bsp first.\n");
+		StripExtension(source);
+		DefaultExtension(source, ".map");
+		Sys_Printf("Loading %s\n", source);
+		LoadMapFile(source, qfalse, convertGame == NULL);
+		PseudoCompileBSP(convertGame != NULL);
+	}
+	else
+	{
+		StripExtension(source);
+		DefaultExtension(source, ".bsp");
+		Sys_Printf("Loading %s\n", source);
+		LoadBSPFile(source);
+		ParseEntities();
+	}
 	
 	/* bsp format convert? */
 	if( convertGame != NULL )
@@ -588,7 +1695,7 @@ int main( int argc, char **argv )
 	
 	/* set exit call */
 	atexit( ExitQ3Map );
-	
+
 	/* read general options first */
 	for( i = 1; i < argc; i++ )
 	{
@@ -604,8 +1711,11 @@ int main( int argc, char **argv )
 		/* verbose */
 		else if( !strcmp( argv[ i ], "-v" ) )
 		{
-			verbose = qtrue;
-			argv[ i ] = NULL;
+			if(!verbose)
+			{
+				verbose = qtrue;
+				argv[ i ] = NULL;
+			}
 		}
 		
 		/* force */
@@ -635,7 +1745,7 @@ int main( int argc, char **argv )
 			argv[ i ] = NULL;
 		}
 	}
-	
+
 	/* init model library */
 	PicoInit();
 	PicoSetMallocFunc( safe_malloc );
@@ -659,11 +1769,15 @@ int main( int argc, char **argv )
 	
 	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( "NetRadiant    - v" RADIANT_VERSION " " __DATE__ " " __TIME__ "\n" );
 	Sys_Printf( "%s\n", Q3MAP_MOTD );
 	
 	/* ydnar: new path initialization */
 	InitPaths( &argc, argv );
+
+	/* set game options */
+	if (!patchSubdivisions)
+		patchSubdivisions = game->patchSubdivisions;
 	
 	/* check if we have enough options left to attempt something */
 	if( argc < 2 )
@@ -713,6 +1827,10 @@ int main( int argc, char **argv )
 	else if( !strcmp( argv[ 1 ], "-convert" ) )
 		r = ConvertBSPMain( argc - 1, argv + 1 );
 	
+	/* div0: minimap */
+	else if( !strcmp( argv[ 1 ], "-minimap" ) )
+		r = MiniMapBSPMain(argc - 1, argv + 1);
+
 	/* ydnar: otherwise create a bsp */
 	else
 		r = BSPMain( argc, argv );
diff --git a/tools/quake3/q3map2/map.c b/tools/quake3/q3map2/map.c
index 4ec5a381..95bb3d1f 100644
--- a/tools/quake3/q3map2/map.c
+++ b/tools/quake3/q3map2/map.c
@@ -44,7 +44,7 @@ several games based on the Quake III Arena engine, in the form of "Q3Map2."
 #define	USE_HASHING
 #define	PLANE_HASHES	8192
 
-plane_t					*planehash[ PLANE_HASHES ];
+int						planehash[ PLANE_HASHES ];
 
 int						c_boxbevels;
 int						c_edgebevels;
@@ -97,7 +97,7 @@ void AddPlaneToHash( plane_t *p )
 	hash = (PLANE_HASHES - 1) & (int) fabs( p->dist );
 
 	p->hash_chain = planehash[hash];
-	planehash[hash] = p;
+	planehash[hash] = p - mapplanes + 1;
 }
 
 /*
@@ -116,8 +116,7 @@ int CreateNewFloatPlane (vec3_t normal, vec_t dist)
 	}
 
 	// create a new plane
-	if (nummapplanes+2 > MAX_MAP_PLANES)
-		Error ("MAX_MAP_PLANES");
+	AUTOEXPAND_BY_REALLOC(mapplanes, nummapplanes+1, allocatedmapplanes, 1024);
 
 	p = &mapplanes[nummapplanes];
 	VectorCopy (normal, p->normal);
@@ -248,7 +247,7 @@ SnapPlane()
 snaps a plane to normal/distance epsilons
 */
 
-void SnapPlane( vec3_t normal, vec_t *dist )
+void SnapPlane( vec3_t normal, vec_t *dist, vec3_t center )
 {
 // SnapPlane disabled by LordHavoc because it often messes up collision
 // brushes made from triangles of embedded models, and it has little effect
@@ -322,22 +321,25 @@ void SnapPlaneImproved(vec3_t normal, vec_t *dist, int numPoints, const vec3_t *
 }
 
 
+
 /*
 FindFloatPlane()
 ydnar: changed to allow a number of test points to be supplied that
 must be within an epsilon distance of the plane
 */
 
-int FindFloatPlane( vec3_t normal, vec_t dist, int numPoints, vec3_t *points )
+int FindFloatPlane( vec3_t innormal, vec_t dist, int numPoints, vec3_t *points ) // NOTE: this has a side effect on the normal. Good or bad?
 
 #ifdef USE_HASHING
 
 {
 	int		i, j, hash, h;
+	int pidx;
 	plane_t	*p;
 	vec_t	d;
-	
-	
+	vec3_t normal;
+
+	VectorCopy(innormal, normal);
 #if Q3MAP2_EXPERIMENTAL_SNAP_PLANE_FIX
 	SnapPlaneImproved(normal, &dist, numPoints, (const vec3_t *) points);
 #else
@@ -350,8 +352,10 @@ int FindFloatPlane( vec3_t normal, vec_t dist, int numPoints, vec3_t *points )
 	for( i = -1; i <= 1; i++ )
 	{
 		h = (hash + i) & (PLANE_HASHES - 1);
-		for( p = planehash[ h ]; p != NULL; p = p->hash_chain )
+		for( pidx = planehash[ h ] - 1; pidx != -1; pidx = mapplanes[pidx].hash_chain - 1 )
 		{
+			p = &mapplanes[pidx];
+
 			/* do standard plane compare */
 			if( !PlaneEqual( p, normal, dist ) )
 				continue;
@@ -367,7 +371,7 @@ int FindFloatPlane( vec3_t normal, vec_t dist, int numPoints, vec3_t *points )
 				// very small when world coordinates extend to 2^16.  Making the
 				// dot product here in 64 bit land will not really help the situation
 				// because the error will already be carried in dist.
-				d = DotProduct( points[ j ], normal ) - dist;
+				d = DotProduct( points[ j ], p->normal ) - p->dist;
 				d = fabs(d);
 				if (d != 0.0 && d >= distanceEpsilon)
 					break; // Point is too far from plane.
@@ -388,15 +392,32 @@ int FindFloatPlane( vec3_t normal, vec_t dist, int numPoints, vec3_t *points )
 {
 	int		i;
 	plane_t	*p;
+	vec3_t normal;
 	
+	VectorCopy(innormal, normal);
 #if Q3MAP2_EXPERIMENTAL_SNAP_PLANE_FIX
 	SnapPlaneImproved(normal, &dist, numPoints, (const vec3_t *) points);
 #else
-	SnapPlane( normal, &dist );
+ 	SnapPlane( normal, &dist );
 #endif
 	for( i = 0, p = mapplanes; i < nummapplanes; i++, p++ )
 	{
-		if( PlaneEqual( p, normal, dist ) )
+		if( !PlaneEqual( p, normal, dist ) )
+			continue;
+
+		/* ydnar: uncomment the following line for old-style plane finding */
+		//%	return i;
+			
+		/* ydnar: test supplied points against this plane */
+		for( j = 0; j < numPoints; j++ )
+		{
+			d = DotProduct( points[ j ], p->normal ) - p->dist;
+			if( fabs( d ) > distanceEpsilon )
+				break;
+		}
+		
+		/* found a matching plane */
+		if( j >= numPoints )
 			return i;
 		// TODO: Note that the non-USE_HASHING code does not compute epsilons
 		// for the provided points.  It should do that.  I think this code
@@ -487,6 +508,9 @@ void SetBrushContents( brush_t *b )
 			continue;
 		if( s->contentFlags != contentFlags || s->compileFlags != compileFlags )
 			mixed = qtrue;
+
+		contentFlags |= s->contentFlags;
+		compileFlags |= s->compileFlags;
 	}
 	
 	/* ydnar: getting rid of this stupid warning */
@@ -739,7 +763,23 @@ produces a final brush based on the buildBrush->sides array
 and links it to the current entity
 */
 
-brush_t *FinishBrush( void )
+static void MergeOrigin(entity_t *ent, vec3_t origin)
+{
+	vec3_t adjustment;
+	char string[128];
+
+	/* we have not parsed the brush completely yet... */
+	GetVectorForKey( ent, "origin", ent->origin );
+
+	VectorMA(origin, -1, ent->originbrush_origin, adjustment);
+	VectorAdd(adjustment, ent->origin, ent->origin);
+	VectorCopy(origin, ent->originbrush_origin);
+
+	sprintf(string, "%f %f %f", ent->origin[0], ent->origin[1], ent->origin[2]);
+	SetKeyValue(ent, "origin", string);
+}
+
+brush_t *FinishBrush( qboolean noCollapseGroups )
 {
 	brush_t		*b;
 	
@@ -752,9 +792,11 @@ brush_t *FinishBrush( void )
 	   after the entire entity is parsed, the planenums and texinfos will be adjusted for the origin brush */
 	if( buildBrush->compileFlags & C_ORIGIN )
 	{
-		char	string[ 32 ];
 		vec3_t	origin;
 
+		Sys_Printf( "Entity %i, Brush %i: origin brush detected\n", 
+				mapEnt->mapEntityNum, entitySourceBrushes );
+
 		if( numEntities == 1 )
 		{
 			Sys_Printf( "Entity %i, Brush %i: origin brushes not allowed in world\n", 
@@ -765,10 +807,7 @@ brush_t *FinishBrush( void )
 		VectorAdd (buildBrush->mins, buildBrush->maxs, origin);
 		VectorScale (origin, 0.5, origin);
 
-		sprintf( string, "%i %i %i", (int) origin[ 0 ], (int) origin[ 1 ], (int) origin[ 2 ] );
-		SetKeyValue( &entities[ numEntities - 1 ], "origin", string);
-
-		VectorCopy( origin, entities[ numEntities - 1 ].origin);
+		MergeOrigin(&entities[ numEntities - 1 ], origin);
 
 		/* don't keep this brush */
 		return NULL;
@@ -785,7 +824,8 @@ brush_t *FinishBrush( void )
 	}
 	
 	/* add bevel planes */
-	AddBrushBevels();
+	if(!noCollapseGroups)
+		AddBrushBevels();
 	
 	/* keep it */
 	b = CopyBrush( buildBrush );
@@ -957,7 +997,7 @@ static void ParseRawBrush( qboolean onlyLights )
 	int				planenum;
 	shaderInfo_t	*si;
 	vec_t			shift[ 2 ];
-	vec_t			rotate;
+	vec_t			rotate = 0;
 	vec_t			scale[ 2 ];
 	char			name[ MAX_QPATH ];
 	char			shader[ MAX_QPATH ];
@@ -1154,7 +1194,7 @@ ParseBrush()
 parses a brush out of a map file and sets it up
 */
 
-static void ParseBrush( qboolean onlyLights )
+static void ParseBrush( qboolean onlyLights, qboolean noCollapseGroups )
 {
 	brush_t	*b;
 	
@@ -1201,7 +1241,7 @@ static void ParseBrush( qboolean onlyLights )
 	}
 	
 	/* finish the brush */
-	b = FinishBrush();
+	b = FinishBrush(noCollapseGroups);
 }
 
 
@@ -1213,11 +1253,16 @@ adds them to the world's brush list
 (used by func_group)
 */
 
+void AdjustBrushesForOrigin( entity_t *ent );
 void MoveBrushesToWorld( entity_t *ent )
 {
 	brush_t		*b, *next;
 	parseMesh_t	*pm;
 
+	/* we need to undo the common/origin adjustment, and instead shift them by the entity key origin */
+	VectorScale(ent->origin, -1, ent->originbrush_origin);
+	AdjustBrushesForOrigin(ent);
+	VectorClear(ent->originbrush_origin);
 	
 	/* move brushes */
 	for( b = ent->brushes; b != NULL; b = next )
@@ -1280,7 +1325,6 @@ void AdjustBrushesForOrigin( entity_t *ent )
 	brush_t		*b;
 	parseMesh_t	*p;
 	
-	
 	/* walk brush list */
 	for( b = ent->brushes; b != NULL; b = b->next )
 	{
@@ -1291,7 +1335,7 @@ void AdjustBrushesForOrigin( entity_t *ent )
 			s = &b->sides[ i ];
 			
 			/* offset side plane */
-			newdist = mapplanes[ s->planenum ].dist - DotProduct( mapplanes[ s->planenum ].normal, ent->origin );
+			newdist = mapplanes[ s->planenum ].dist - DotProduct( mapplanes[ s->planenum ].normal, ent->originbrush_origin );
 			
 			/* find a new plane */
 			s->planenum = FindFloatPlane( mapplanes[ s->planenum ].normal, newdist, 0, NULL );
@@ -1305,7 +1349,7 @@ void AdjustBrushesForOrigin( entity_t *ent )
 	for( p = ent->patches; p != NULL; p = p->next )
 	{
 		for( i = 0; i < (p->mesh.width * p->mesh.height); i++ )
-			VectorSubtract( p->mesh.verts[ i ].xyz, ent->origin, p->mesh.verts[ i ].xyz );
+			VectorSubtract( p->mesh.verts[ i ].xyz, ent->originbrush_origin, p->mesh.verts[ i ].xyz );
 	}
 }
 
@@ -1534,11 +1578,12 @@ ParseMapEntity()
 parses a single entity out of a map file
 */
 
-static qboolean ParseMapEntity( qboolean onlyLights )
+static qboolean ParseMapEntity( qboolean onlyLights, qboolean noCollapseGroups )
 {
 	epair_t			*ep;
 	const char		*classname, *value;
-	float			lightmapScale;
+	float			lightmapScale, shadeAngle;
+	int				lightmapSampleSize;
 	char			shader[ MAX_QPATH ];
 	shaderInfo_t	*celShader = NULL;
 	brush_t			*brush;
@@ -1612,7 +1657,7 @@ static qboolean ParseMapEntity( qboolean onlyLights )
 				g_bBrushPrimit = BPRIMIT_NEWBRUSHES;
 				
 				/* parse brush primitive */
-				ParseBrush( onlyLights );
+				ParseBrush( onlyLights, noCollapseGroups );
 			}
 			else
 			{
@@ -1622,7 +1667,7 @@ static qboolean ParseMapEntity( qboolean onlyLights )
 				
 				/* parse old brush format */
 				UnGetToken();
-				ParseBrush( onlyLights );
+				ParseBrush( onlyLights, noCollapseGroups );
 			}
 			entitySourceBrushes++;
 		}
@@ -1675,19 +1720,24 @@ static qboolean ParseMapEntity( qboolean onlyLights )
 	/* get explicit shadow flags */
 	GetEntityShadowFlags( mapEnt, NULL, &castShadows, &recvShadows );
 	
+	/* vortex: added _ls key (short name of lightmapscale) */
 	/* ydnar: get lightmap scaling value for this entity */
+	lightmapScale = 0.0f;
 	if( strcmp( "", ValueForKey( mapEnt, "lightmapscale" ) ) ||
-		strcmp( "", ValueForKey( mapEnt, "_lightmapscale" ) ) )
+		strcmp( "", ValueForKey( mapEnt, "_lightmapscale" ) ) || 
+		strcmp( "", ValueForKey( mapEnt, "_ls" ) ) )
 	{
 		/* get lightmap scale from entity */
 		lightmapScale = FloatForKey( mapEnt, "lightmapscale" );
 		if( lightmapScale <= 0.0f )
 			lightmapScale = FloatForKey( mapEnt, "_lightmapscale" );
+		if( lightmapScale <= 0.0f )
+			lightmapScale = FloatForKey( mapEnt, "_ls" );
+		if( lightmapScale < 0.0f )
+			lightmapScale = 0.0f;
 		if( lightmapScale > 0.0f )
 			Sys_Printf( "Entity %d (%s) has lightmap scale of %.4f\n", mapEnt->mapEntityNum, classname, lightmapScale );
 	}
-	else
-		lightmapScale = 0.0f;
 	
 	/* ydnar: get cel shader :) for this entity */
 	value = ValueForKey( mapEnt, "_celshader" );
@@ -1695,12 +1745,50 @@ static qboolean ParseMapEntity( qboolean onlyLights )
 		value = ValueForKey( &entities[ 0 ], "_celshader" );
 	if( value[ 0 ] != '\0' )
 	{
-		sprintf( shader, "textures/%s", value );
-		celShader = ShaderInfoForShader( shader );
-		Sys_Printf( "Entity %d (%s) has cel shader %s\n", mapEnt->mapEntityNum, classname, celShader->shader );
+		if(strcmp(value, "none"))
+		{
+			sprintf( shader, "textures/%s", value );
+			celShader = ShaderInfoForShader( shader );
+			Sys_Printf( "Entity %d (%s) has cel shader %s\n", mapEnt->mapEntityNum, classname, celShader->shader );
+		}
+		else
+		{
+			celShader = NULL;
+		}
 	}
 	else
-		celShader = NULL;
+		celShader = (*globalCelShader ? ShaderInfoForShader(globalCelShader) : NULL);
+
+	/* jal : entity based _shadeangle */
+	shadeAngle = 0.0f;
+	if ( strcmp( "", ValueForKey( mapEnt, "_shadeangle" ) ) )
+		shadeAngle = FloatForKey( mapEnt, "_shadeangle" );
+	/* vortex' aliases */
+	else if ( strcmp( "", ValueForKey( mapEnt, "_smoothnormals" ) ) )
+		shadeAngle = FloatForKey( mapEnt, "_smoothnormals" );
+	else if ( strcmp( "", ValueForKey( mapEnt, "_sn" ) ) )
+		shadeAngle = FloatForKey( mapEnt, "_sn" );
+	else if ( strcmp( "", ValueForKey( mapEnt, "_smooth" ) ) )
+		shadeAngle = FloatForKey( mapEnt, "_smooth" );
+	
+	if( shadeAngle < 0.0f )
+		shadeAngle = 0.0f;
+
+	if( shadeAngle > 0.0f )
+		Sys_Printf( "Entity %d (%s) has shading angle of %.4f\n", mapEnt->mapEntityNum, classname, shadeAngle );
+	
+	/* jal : entity based _samplesize */
+	lightmapSampleSize = 0;
+	if ( strcmp( "", ValueForKey( mapEnt, "_lightmapsamplesize" ) ) )
+		lightmapSampleSize = IntForKey( mapEnt, "_lightmapsamplesize" );
+	else if ( strcmp( "", ValueForKey( mapEnt, "_samplesize" ) ) )
+		lightmapSampleSize = IntForKey( mapEnt, "_samplesize" );
+	
+	if( lightmapSampleSize < 0 )
+		lightmapSampleSize = 0;
+
+	if( lightmapSampleSize > 0 )
+		Sys_Printf( "Entity %d (%s) has lightmap sample size of %d\n", mapEnt->mapEntityNum, classname, lightmapSampleSize );
 	
 	/* attach stuff to everything in the entity */
 	for( brush = mapEnt->brushes; brush != NULL; brush = brush->next )
@@ -1708,8 +1796,10 @@ static qboolean ParseMapEntity( qboolean onlyLights )
 		brush->entityNum = mapEnt->mapEntityNum;
 		brush->castShadows = castShadows;
 		brush->recvShadows = recvShadows;
+		brush->lightmapSampleSize = lightmapSampleSize;
 		brush->lightmapScale = lightmapScale;
 		brush->celShader = celShader;
+		brush->shadeAngleDegrees = shadeAngle;
 	}
 	
 	for( patch = mapEnt->patches; patch != NULL; patch = patch->next )
@@ -1717,6 +1807,7 @@ static qboolean ParseMapEntity( qboolean onlyLights )
 		patch->entityNum = mapEnt->mapEntityNum;
 		patch->castShadows = castShadows;
 		patch->recvShadows = recvShadows;
+		patch->lightmapSampleSize = lightmapSampleSize;
 		patch->lightmapScale = lightmapScale;
 		patch->celShader = celShader;
 	}
@@ -1729,18 +1820,18 @@ static qboolean ParseMapEntity( qboolean onlyLights )
 	
 	/* get entity origin and adjust brushes */
 	GetVectorForKey( mapEnt, "origin", mapEnt->origin );
-	if( mapEnt->origin[ 0 ] || mapEnt->origin[ 1 ] || mapEnt->origin[ 2 ] )
+	if( mapEnt->originbrush_origin[ 0 ] || mapEnt->originbrush_origin[ 1 ] || mapEnt->originbrush_origin[ 2 ] )
 		AdjustBrushesForOrigin( mapEnt );
 
 	/* group_info entities are just for editor grouping (fixme: leak!) */
-	if( !Q_stricmp( "group_info", classname ) )
+	if( !noCollapseGroups && !Q_stricmp( "group_info", classname ) )
 	{
 		numEntities--;
 		return qtrue;
 	}
 	
 	/* group entities are just for editor convenience, toss all brushes into worldspawn */
-	if( funcGroup )
+	if( !noCollapseGroups && funcGroup )
 	{
 		MoveBrushesToWorld( mapEnt );
 		numEntities--;
@@ -1758,11 +1849,11 @@ LoadMapFile()
 loads a map file into a list of entities
 */
 
-void LoadMapFile( char *filename, qboolean onlyLights )
+void LoadMapFile( char *filename, qboolean onlyLights, qboolean noCollapseGroups )
 {		
 	FILE		*file;
 	brush_t		*b;
-	int			oldNumEntities, numMapBrushes;
+	int			oldNumEntities = 0, numMapBrushes;
 	
 	
 	/* note it */
@@ -1791,7 +1882,7 @@ void LoadMapFile( char *filename, qboolean onlyLights )
 	buildBrush = AllocBrush( MAX_BUILD_SIDES );
 	
 	/* parse the map file */
-	while( ParseMapEntity( onlyLights ) );
+	while( ParseMapEntity( onlyLights, noCollapseGroups ) );
 	
 	/* light loading */
 	if( onlyLights )
diff --git a/tools/quake3/q3map2/model.c b/tools/quake3/q3map2/model.c
index ef0a0489..a56134c8 100644
--- a/tools/quake3/q3map2/model.c
+++ b/tools/quake3/q3map2/model.c
@@ -78,9 +78,9 @@ PicoLoadFileFunc()
 callback for picomodel.lib
 */
 
-void PicoLoadFileFunc( char *name, byte **buffer, int *bufSize )
+void PicoLoadFileFunc( const char *name, byte **buffer, int *bufSize )
 {
-	*bufSize = vfsLoadFile( (const char*) name, (void**) buffer, 0 );
+	*bufSize = vfsLoadFile( name, (void**) buffer, 0 );
 }
 
 
@@ -90,7 +90,7 @@ 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 )
+picoModel_t *FindModel( const char *name, int frame )
 {
 	int			i;
 	
@@ -123,7 +123,7 @@ 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 )
+picoModel_t *LoadModel( const char *name, int frame )
 {
 	int				i;
 	picoModel_t		*model, **pm;
@@ -158,7 +158,7 @@ picoModel_t *LoadModel( char *name, int frame )
 		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 );
+	*pm = PicoLoadModel( name, frame );
 	
 	/* if loading failed, make a bogus model to silence the rest of the warnings */
 	if( *pm == NULL )
@@ -206,7 +206,7 @@ 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 )
+void InsertModel( const char *name, int skin, int frame, m4x4_t transform, remap_t *remap, shaderInfo_t *celShader, int eNum, int castShadows, int recvShadows, int spawnFlags, float lightmapScale, int lightmapSampleSize, float shadeAngle )
 {
 	int					i, j, k, s, numSurfaces;
 	m4x4_t				identity, nTransform;
@@ -222,12 +222,76 @@ void InsertModel( char *name, int frame, m4x4_t transform, remap_t *remap, shade
 	byte				*color;
 	picoIndex_t			*indexes;
 	remap_t				*rm, *glob;
+	skinfile_t			*sf, *sf2;
+	double				normalEpsilon_save;
+	double				distanceEpsilon_save;
+	char				skinfilename[ MAX_QPATH ];
+	char				*skinfilecontent;
+	int					skinfilesize;
+	char				*skinfileptr, *skinfilenextptr;
 	
 	
 	/* get model */
 	model = LoadModel( name, frame );
 	if( model == NULL )
 		return;
+
+	/* load skin file */
+	snprintf(skinfilename, sizeof(skinfilename), "%s_%d.skin", name, skin);
+	skinfilename[sizeof(skinfilename)-1] = 0;
+	skinfilesize = vfsLoadFile(skinfilename, (void**) &skinfilecontent, 0);
+	if(skinfilesize < 0 && skin != 0)
+	{
+		/* fallback to skin 0 if invalid */
+		snprintf(skinfilename, sizeof(skinfilename), "%s_0.skin", name);
+		skinfilename[sizeof(skinfilename)-1] = 0;
+		skinfilesize = vfsLoadFile(skinfilename, (void**) &skinfilecontent, 0);
+		if(skinfilesize >= 0)
+			Sys_Printf( "Skin %d of %s does not exist, using 0 instead\n", skin, name );
+	}
+	sf = NULL;
+	if(skinfilesize >= 0)
+	{
+		Sys_Printf( "Using skin %d of %s\n", skin, name );
+		int pos;
+		for(skinfileptr = skinfilecontent; *skinfileptr; skinfileptr = skinfilenextptr)
+		{
+			// for fscanf
+			char format[64];
+
+			skinfilenextptr = strchr(skinfileptr, '\r');
+			if(skinfilenextptr)
+			{
+				*skinfilenextptr++ = 0;
+			}
+			else
+			{
+				skinfilenextptr = strchr(skinfileptr, '\n');
+				if(skinfilenextptr)
+					*skinfilenextptr++ = 0;
+				else
+					skinfilenextptr = skinfileptr + strlen(skinfileptr);
+			}
+
+			/* create new item */
+			sf2 = sf;
+			sf = safe_malloc( sizeof( *sf ) );
+			sf->next = sf2;
+
+			sprintf(format, "replace %%%ds %%%ds", (int)sizeof(sf->name)-1, (int)sizeof(sf->to)-1);
+			if(sscanf(skinfileptr, format, sf->name, sf->to) == 2)
+				continue;
+			sprintf(format, " %%%d[^, 	] ,%%%ds", (int)sizeof(sf->name)-1, (int)sizeof(sf->to)-1);
+			if((pos = sscanf(skinfileptr, format, sf->name, sf->to)) == 2)
+				continue;
+
+			/* invalid input line -> discard sf struct */
+			Sys_Printf( "Discarding skin directive in %s: %s\n", skinfilename, skinfileptr );
+			free(sf);
+			sf = sf2;
+		}
+		free(skinfilecontent);
+	}
 	
 	/* handle null matrix */
 	if( transform == NULL )
@@ -250,6 +314,10 @@ void InsertModel( char *name, int frame, m4x4_t transform, remap_t *remap, shade
 	/* fix bogus lightmap scale */
 	if( lightmapScale <= 0.0f )
 		lightmapScale = 1.0f;
+
+	/* fix bogus shade angle */
+	if( shadeAngle <= 0.0f )
+		shadeAngle = 0.0f;
 	
 	/* each surface on the model will become a new map drawsurface */
 	numSurfaces = PicoGetModelNumSurfaces( model );
@@ -265,9 +333,6 @@ void InsertModel( char *name, int frame, m4x4_t transform, remap_t *remap, shade
 		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;
@@ -280,7 +345,27 @@ void InsertModel( char *name, int frame, m4x4_t transform, remap_t *remap, shade
 			picoShaderName = "";
 		else
 			picoShaderName = PicoGetShaderName( shader );
-		
+
+		/* handle .skin file */
+		if(sf)
+		{
+			picoShaderName = NULL;
+			for(sf2 = sf; sf2 != NULL; sf2 = sf2->next)
+			{
+				if( !Q_stricmp( surface->name, sf2->name ) )
+				{
+					Sys_FPrintf( SYS_VRB, "Skin file: mapping %s to %s\n", surface->name, sf2->to );
+					picoShaderName = sf2->to;
+					break;
+				}
+			}
+			if(!picoShaderName)
+			{
+				Sys_FPrintf( SYS_VRB, "Skin file: not mapping %s\n", surface->name );
+				continue;
+			}
+		}
+
 		/* handle shader remapping */
 		glob = NULL;
 		for( rm = remap; rm != NULL; rm = rm->next )
@@ -318,13 +403,26 @@ void InsertModel( char *name, int frame, m4x4_t transform, remap_t *remap, shade
 		
 		/* 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;
+
+		/* fix the surface's normals (jal: conditioned by shader info) */
+		if( !(spawnFlags & 64) && ( shadeAngle == 0.0f || ds->type != SURFACE_FORCED_META ) )
+			PicoFixSurfaceNormals( surface );
+
+		/* set sample size */
+		if( lightmapSampleSize > 0.0f )
+			ds->sampleSize = lightmapSampleSize;
+		
+		/* set lightmap scale */
+		if( lightmapScale > 0.0f )
+			ds->lightmapScale = lightmapScale;
+
+		/* set shading angle */
+		if( shadeAngle > 0.0f )
+			ds->shadeAngleDegrees = shadeAngle;
 		
 		/* set particulars */
 		ds->numVerts = PicoGetSurfaceNumVertexes( surface );
@@ -380,10 +478,20 @@ void InsertModel( char *name, int frame, m4x4_t transform, remap_t *remap, shade
 			{
 				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 ];
+				if(spawnFlags & 32) // spawnflag 32: model color -> alpha hack
+				{
+					dv->color[ j ][ 0 ] = 255.0f;
+					dv->color[ j ][ 1 ] = 255.0f;
+					dv->color[ j ][ 2 ] = 255.0f;
+					dv->color[ j ][ 3 ] = RGBTOGRAY( color );
+				}
+				else
+				{
+					dv->color[ j ][ 0 ] = color[ 0 ];
+					dv->color[ j ][ 1 ] = color[ 1 ];
+					dv->color[ j ][ 2 ] = color[ 2 ];
+					dv->color[ j ][ 3 ] = color[ 3 ];
+				}
 			}
 		}
 		
@@ -398,30 +506,19 @@ void InsertModel( char *name, int frame, m4x4_t transform, remap_t *remap, shade
 		/* ydnar: giant hack land: generate clipping brushes for model triangles */
 		if( si->clipModel || (spawnFlags & 2) )	/* 2nd bit */
 		{
-			vec3_t		points[ 3 ], backs[ 3 ];
+			vec3_t		points[ 4 ], 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) )
+			if( !si->clipModel && !(si->compileFlags & C_SOLID) )
 				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;
-				}
+				AUTOEXPAND_BY_REALLOC(mapplanes, (nummapplanes+64) << 1, allocatedmapplanes, 1024);
 				
 				/* make points and back points */
 				for( j = 0; j < 3; j++ )
@@ -431,107 +528,135 @@ void InsertModel( char *name, int frame, m4x4_t transform, remap_t *remap, shade
 					
 					/* copy xyz */
 					VectorCopy( dv->xyz, points[ j ] );
-#if ! Q3MAP2_EXPERIMENTAL_MODEL_CLIPPING_FIX
-					// The code below is totally unneeded regardless of Q3MAP2_EXPERIMENTAL_MODEL_CLIPPING_FIX.
-					// backs is reinitialized further below.  However, the extra code does not hurt anything.
-					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;
-						}
-					}
-#endif
 				}
+
+				VectorCopy( points[0], points[3] ); // for cyclic usage
 				
 				/* make plane for triangle */
+				// div0: add some extra spawnflags:
+				//   0: snap normals to axial planes for extrusion
+				//   8: extrude with the original normals
+				//  16: extrude only with up/down normals (ideal for terrain)
+				//  24: extrude by distance zero (may need engine changes)
 				if( PlaneFromPoints( plane, points[ 0 ], points[ 1 ], points[ 2 ] ) )
 				{
+					vec3_t bestNormal;
+					float backPlaneDistance = 2;
+
+					if(spawnFlags & 8) // use a DOWN normal
+					{
+						if(spawnFlags & 16)
+						{
+							// 24: normal as is, and zero width (broken)
+							VectorCopy(plane, bestNormal);
+						}
+						else
+						{
+							// 8: normal as is
+							VectorCopy(plane, bestNormal);
+						}
+					}
+					else
+					{
+						if(spawnFlags & 16)
+						{
+							// 16: UP/DOWN normal
+							VectorSet(bestNormal, 0, 0, (plane[2] >= 0 ? 1 : -1));
+						}
+						else
+						{
+							// 0: axial normal
+							if(fabs(plane[0]) > fabs(plane[1])) // x>y
+								if(fabs(plane[1]) > fabs(plane[2])) // x>y, y>z
+									VectorSet(bestNormal, (plane[0] >= 0 ? 1 : -1), 0, 0);
+								else // x>y, z>=y
+									if(fabs(plane[0]) > fabs(plane[2])) // x>z, z>=y
+										VectorSet(bestNormal, (plane[0] >= 0 ? 1 : -1), 0, 0);
+									else // z>=x, x>y
+										VectorSet(bestNormal, 0, 0, (plane[2] >= 0 ? 1 : -1));
+							else // y>=x
+								if(fabs(plane[1]) > fabs(plane[2])) // y>z, y>=x
+									VectorSet(bestNormal, 0, (plane[1] >= 0 ? 1 : -1), 0);
+								else // z>=y, y>=x
+									VectorSet(bestNormal, 0, 0, (plane[2] >= 0 ? 1 : -1));
+						}
+					}
+
+					/* build a brush */
+					buildBrush = AllocBrush( 48 );
+					buildBrush->entityNum = mapEntityNum;
+					buildBrush->original = buildBrush;
+					buildBrush->contentShader = si;
+					buildBrush->compileFlags = si->compileFlags;
+					buildBrush->contentFlags = si->contentFlags;
+					normalEpsilon_save = normalEpsilon;
+					distanceEpsilon_save = distanceEpsilon;
+					if(si->compileFlags & C_STRUCTURAL) // allow forced structural brushes here
+					{
+						buildBrush->detail = qfalse;
+
+						// only allow EXACT matches when snapping for these (this is mostly for caulk brushes inside a model)
+						if(normalEpsilon > 0)
+							normalEpsilon = 0;
+						if(distanceEpsilon > 0)
+							distanceEpsilon = 0;
+					}
+					else
+						buildBrush->detail = qtrue;
+
 					/* 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 Q3MAP2_EXPERIMENTAL_MODEL_CLIPPING_FIX
-							if( fabs( plane[ k ] ) >= fabs( plane[ (k + 1) % 3 ] ) &&
-								fabs( plane[ k ] ) >= fabs( plane[ (k + 2) % 3 ] ) )
-#else
-							// This code is broken for 45 degree angles where there
-							// is no clear winner.
-							if( fabs( plane[ k ] ) > fabs( plane[ (k + 1) % 3 ] ) &&
-								fabs( plane[ k ] ) > fabs( plane[ (k + 2) % 3 ] ) )
-#endif
-							{
-								backs[ j ][ k ] += plane[ k ] < 0.0f ? 64.0f : -64.0f;
-								break;
-							}
-						}
+
+						// shift by some units
+						VectorMA(dv->xyz, -64.0f, bestNormal, backs[j]); // 64 prevents roundoff errors a bit
 					}
-					
+
 					/* 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 ) )
+					reverse[ 3 ] = -plane[ 3 ];
+					if((spawnFlags & 24) != 24)
+						reverse[3] += DotProduct(bestNormal, plane) * backPlaneDistance;
+					// that's at least sqrt(1/3) backPlaneDistance, unless in DOWN mode; in DOWN mode, we are screwed anyway if we encounter a plane that's perpendicular to the xy plane)
+
 					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 ] ) )
+							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 ].shaderInfo = si;
+						for( j = 1; j < buildBrush->numsides; j++ )
+							buildBrush->sides[ j ].shaderInfo = NULL; // don't emit these faces as draw surfaces, should make smaller BSPs; hope this works
+
 						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 );
+						buildBrush->sides[ 1 ].planenum = FindFloatPlane( pa, pa[ 3 ], 2, &points[ 1 ] ); // pa contains points[1] and points[2]
+						buildBrush->sides[ 2 ].planenum = FindFloatPlane( pb, pb[ 3 ], 2, &points[ 0 ] ); // pb contains points[0] and points[1]
+						buildBrush->sides[ 3 ].planenum = FindFloatPlane( pc, pc[ 3 ], 2, &points[ 2 ] ); // pc contains points[2] and points[0] (copied to points[3]
+						buildBrush->sides[ 4 ].planenum = FindFloatPlane( reverse, reverse[ 3 ], 3, backs );
+					}
+					else
+					{
+						free(buildBrush);
+						continue;
 					}
+
+					normalEpsilon = normalEpsilon_save;
+					distanceEpsilon = distanceEpsilon_save;
+
+					/* 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 );
 				}
 			}
 		}
@@ -547,13 +672,15 @@ adds misc_model surfaces to the bsp
 
 void AddTriangleModels( entity_t *e )
 {
-	int				num, frame, castShadows, recvShadows, spawnFlags;
+	int				num, frame, skin, 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;
+	float			shadeAngle;
+	int				lightmapSampleSize;
 	vec3_t			origin, scale, angles;
 	m4x4_t			transform;
 	epair_t			*ep;
@@ -577,9 +704,23 @@ void AddTriangleModels( entity_t *e )
 	}
 	
 	/* get lightmap scale */
-	baseLightmapScale = FloatForKey( e, "_lightmapscale" );
-	if( baseLightmapScale <= 0.0f )
-		baseLightmapScale = 0.0f;
+	/* vortex: added _ls key (short name of lightmapscale) */
+	baseLightmapScale = 0.0f;
+	if( strcmp( "", ValueForKey( e, "lightmapscale" ) ) ||
+		strcmp( "", ValueForKey( e, "_lightmapscale" ) ) || 
+		strcmp( "", ValueForKey( e, "_ls" ) ) )
+	{
+		baseLightmapScale = FloatForKey( e, "lightmapscale" );
+		if( baseLightmapScale <= 0.0f )
+			baseLightmapScale = FloatForKey( e, "_lightmapscale" );
+		if( baseLightmapScale <= 0.0f )
+			baseLightmapScale = FloatForKey( e, "_ls" );
+		if( baseLightmapScale < 0.0f )
+			baseLightmapScale = 0.0f;
+		if( baseLightmapScale > 0.0f )
+			Sys_Printf( "World Entity has lightmap scale of %.4f\n", baseLightmapScale );
+	}
+	
 	
 	/* walk the entity list */
 	for( num = 1; num < numEntities; num++ )
@@ -696,15 +837,61 @@ void AddTriangleModels( entity_t *e )
 			celShader = ShaderInfoForShader( shader );
 		}
 		else
-			celShader = NULL;
-		
+			celShader = *globalCelShader ? ShaderInfoForShader(globalCelShader) : NULL;
+
+		/* jal : entity based _samplesize */
+		lightmapSampleSize = 0;
+		if ( strcmp( "", ValueForKey( e2, "_lightmapsamplesize" ) ) )
+			lightmapSampleSize = IntForKey( e2, "_lightmapsamplesize" );
+		else if ( strcmp( "", ValueForKey( e2, "_samplesize" ) ) )
+			lightmapSampleSize = IntForKey( e2, "_samplesize" );
+
+		if( lightmapSampleSize < 0 )
+			lightmapSampleSize = 0;
+
+		if( lightmapSampleSize > 0.0f )
+			Sys_Printf( "misc_model has lightmap sample size of %.d\n", lightmapSampleSize );
+
 		/* get lightmap scale */
-		lightmapScale = FloatForKey( e2, "_lightmapscale" );
-		if( lightmapScale <= 0.0f )
-			lightmapScale = baseLightmapScale;
-		
+		/* vortex: added _ls key (short name of lightmapscale) */
+		lightmapScale = 0.0f;
+		if( strcmp( "", ValueForKey( e2, "lightmapscale" ) ) ||
+			strcmp( "", ValueForKey( e2, "_lightmapscale" ) ) || 
+			strcmp( "", ValueForKey( e2, "_ls" ) ) )
+		{
+			lightmapScale = FloatForKey( e2, "lightmapscale" );
+			if( lightmapScale <= 0.0f )
+				lightmapScale = FloatForKey( e2, "_lightmapscale" );
+			if( lightmapScale <= 0.0f )
+				lightmapScale = FloatForKey( e2, "_ls" );
+			if( lightmapScale < 0.0f )
+				lightmapScale = 0.0f;
+			if( lightmapScale > 0.0f )
+				Sys_Printf( "misc_model has lightmap scale of %.4f\n", lightmapScale );
+		}
+
+		/* jal : entity based _shadeangle */
+		shadeAngle = 0.0f;
+		if ( strcmp( "", ValueForKey( e2, "_shadeangle" ) ) )
+			shadeAngle = FloatForKey( e2, "_shadeangle" );
+		/* vortex' aliases */
+		else if ( strcmp( "", ValueForKey( e2, "_smoothnormals" ) ) )
+			shadeAngle = FloatForKey( e2, "_smoothnormals" );
+		else if ( strcmp( "", ValueForKey( e2, "_sn" ) ) )
+			shadeAngle = FloatForKey( e2, "_sn" );
+		else if ( strcmp( "", ValueForKey( e2, "_smooth" ) ) )
+			shadeAngle = FloatForKey( e2, "_smooth" );
+
+		if( shadeAngle < 0.0f )
+			shadeAngle = 0.0f;
+
+		if( shadeAngle > 0.0f )
+			Sys_Printf( "misc_model has shading angle of %.4f\n", shadeAngle );
+
+		skin = IntForKey(e2, "skin");
+
 		/* insert the model */
-		InsertModel( (char*) model, frame, transform, remap, celShader, mapEntityNum, castShadows, recvShadows, spawnFlags, lightmapScale );
+		InsertModel( model, skin, frame, transform, remap, celShader, mapEntityNum, castShadows, recvShadows, spawnFlags, lightmapScale, lightmapSampleSize, shadeAngle );
 		
 		/* free shader remappings */
 		while( remap != NULL )
diff --git a/tools/quake3/q3map2/patch.c b/tools/quake3/q3map2/patch.c
index 58b8ea20..9cd707e2 100644
--- a/tools/quake3/q3map2/patch.c
+++ b/tools/quake3/q3map2/patch.c
@@ -227,7 +227,6 @@ void ParsePatch( qboolean onlyLights )
 	float			longestCurve;
 	int				maxIterations;
 	
-	
 	MatchToken( "{" );
 	
 	/* get texture */
diff --git a/tools/quake3/q3map2/path_init.c b/tools/quake3/q3map2/path_init.c
index 927ba76a..83383e22 100644
--- a/tools/quake3/q3map2/path_init.c
+++ b/tools/quake3/q3map2/path_init.c
@@ -49,7 +49,7 @@ int						numBasePaths;
 char					*basePaths[ MAX_BASE_PATHS ];
 int						numGamePaths;
 char					*gamePaths[ MAX_GAME_PATHS ];
-
+char					*homeBasePath = NULL;
 
 
 /*
@@ -109,6 +109,7 @@ void LokiInitPaths( char *argv0 )
 		strcpy( installPath, "../" );
 	#else
 		char		temp[ MAX_OS_PATH ];
+		char		last0[ 2 ];
 		char		*home;
 		char		*path;
 		char		*last;
@@ -122,7 +123,7 @@ void LokiInitPaths( char *argv0 )
 		
 		/* do some path divining */
 		strcpy( temp, argv0 );
-		if( strrchr( temp, '/' ) )
+		if( strrchr( argv0, '/' ) )
 			argv0 = strrchr( argv0, '/' ) + 1;
 		else
 		{
@@ -130,6 +131,7 @@ void LokiInitPaths( char *argv0 )
 			path = getenv( "PATH" );
 			
 			/* minor setup */
+			last = last0;
 			last[ 0 ] = path[ 0 ];
 			last[ 1 ] = '\0';
 			found = qfalse;
@@ -280,7 +282,7 @@ void AddHomeBasePath( char *path )
 			return;
 
 		/* make a hole */
-		for( i = 0; i < (MAX_BASE_PATHS - 1); i++ )
+		for( i = (MAX_BASE_PATHS - 2); i >= 0; i-- )
 			basePaths[ i + 1 ] = basePaths[ i ];
 		
 		/* concatenate home dir and path */
@@ -374,6 +376,21 @@ void InitPaths( int *argc, char **argv )
 			argv[ i ] = NULL;
 		}
 
+		/* -fs_forbiddenpath */
+		else if( strcmp( argv[ i ], "-fs_forbiddenpath" ) == 0 )
+		{
+			if( ++i >= *argc )
+				Error( "Out of arguments: No path specified after %s.", argv[ i - 1 ] );
+			argv[ i - 1 ] = NULL;
+			if(g_numForbiddenDirs < VFS_MAXDIRS)
+			{
+				strncpy(g_strForbiddenDirs[g_numForbiddenDirs], argv[i], PATH_MAX);
+				g_strForbiddenDirs[g_numForbiddenDirs][PATH_MAX] = 0;
+				++g_numForbiddenDirs;
+			}
+			argv[ i ] = NULL;
+		}
+
 		/* -fs_basepath */
 		else if( strcmp( argv[ i ], "-fs_basepath" ) == 0 )
 		{
@@ -393,6 +410,16 @@ void InitPaths( int *argc, char **argv )
 			AddGamePath( argv[ i ] );
 			argv[ i ] = NULL;
 		}
+		
+		/* -fs_nohomebase */
+		else if( strcmp( argv[ i ], "-fs_homebase" ) == 0 )
+		{
+			if( ++i >= *argc )
+				Error( "Out of arguments: No path specified after %s.", argv[ i - 1 ] );
+			argv[ i - 1 ] = NULL;
+			homeBasePath = argv[i];
+			argv[ i ] = NULL;
+		}
 	}
 	
 	/* remove processed arguments */
@@ -448,7 +475,10 @@ void InitPaths( int *argc, char **argv )
 	}
 	
 	/* this only affects unix */
-	AddHomeBasePath( game->homeBasePath );
+	if(homeBasePath)
+		AddHomeBasePath( homeBasePath );
+	else
+		AddHomeBasePath( game->homeBasePath );
 	
 	/* initialize vfs paths */
 	if( numBasePaths > MAX_BASE_PATHS )
diff --git a/tools/quake3/q3map2/portals.c b/tools/quake3/q3map2/portals.c
index 02209d54..2efc1db4 100644
--- a/tools/quake3/q3map2/portals.c
+++ b/tools/quake3/q3map2/portals.c
@@ -668,9 +668,15 @@ qboolean FloodEntities( tree_t *tree )
 		
 		/* get origin */
 		GetVectorForKey( e, "origin", origin );
+
+		/* as a special case, allow origin-less entities */
 		if( VectorCompare( origin, vec3_origin ) ) 
 			continue;
 		
+		/* also allow bmodel entities outside, as they could be on a moving path that will go into the map */
+		if( e->brushes != NULL || e->patches != NULL )
+			continue;
+
 		/* handle skybox entities */
 		value = ValueForKey( e, "classname" );
 		if( !Q_stricmp( value, "_skybox" ) )
@@ -719,7 +725,11 @@ qboolean FloodEntities( tree_t *tree )
 		r = PlaceOccupant( headnode, origin, e, skybox );
 		if( r )
 			inside = qtrue;
-		if( (!r || tree->outside_node.occupied) && !tripped )
+		if( !r )
+		{
+			Sys_Printf( "Entity %i, Brush %i: Entity in solid\n", e->mapEntityNum, 0);
+		}
+		else if( tree->outside_node.occupied && !tripped )
 		{
 			xml_Select( "Entity leaked", e->mapEntityNum, 0, qfalse );
 			tripped = qtrue;
diff --git a/tools/quake3/q3map2/prtfile.c b/tools/quake3/q3map2/prtfile.c
index 6f36f758..2f10c7c0 100644
--- a/tools/quake3/q3map2/prtfile.c
+++ b/tools/quake3/q3map2/prtfile.c
@@ -63,6 +63,38 @@ void WriteFloat (FILE *f, vec_t v)
 		fprintf (f,"%f ",v);
 }
 
+void CountVisportals_r(node_t *node)
+{
+	int			s;	
+	portal_t	*p;
+	winding_t	*w;
+
+	// decision node
+	if (node->planenum != PLANENUM_LEAF) {
+		CountVisportals_r (node->children[0]);
+		CountVisportals_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;
+			if(p->nodes[0]->cluster == p->nodes[1]->cluster)
+				continue;
+			++num_visportals;
+		}
+	}
+}
+
 /*
 =================
 WritePortalFile_r
@@ -95,6 +127,9 @@ void WritePortalFile_r (node_t *node)
 		{
 			if (!PortalPassable(p))
 				continue;
+			if(p->nodes[0]->cluster == p->nodes[1]->cluster)
+				continue;
+			--num_visportals;
 			// write out to the file
 			
 			// sometimes planes get turned around when they are very near
@@ -102,6 +137,7 @@ void WritePortalFile_r (node_t *node)
 			// 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);
@@ -130,6 +166,40 @@ void WritePortalFile_r (node_t *node)
 
 }
 
+void CountSolidFaces_r (node_t *node)
+{
+	int			s;	
+	portal_t	*p;
+	winding_t	*w;
+
+	// decision node
+	if (node->planenum != PLANENUM_LEAF) {
+		CountSolidFaces_r (node->children[0]);
+		CountSolidFaces_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;
+			if(p->nodes[0]->cluster == p->nodes[1]->cluster)
+				continue;
+			// write out to the file
+
+			++num_solidfaces;
+		}
+	}
+}
+
 /*
 =================
 WriteFaceFile_r
@@ -160,6 +230,8 @@ void WriteFaceFile_r (node_t *node)
 		{
 			if (PortalPassable(p))
 				continue;
+			if(p->nodes[0]->cluster == p->nodes[1]->cluster)
+				continue;
 			// write out to the file
 
 			if (p->nodes[0] == node)
@@ -197,15 +269,31 @@ void WriteFaceFile_r (node_t *node)
 NumberLeafs_r
 ================
 */
-void NumberLeafs_r (node_t *node)
+void NumberLeafs_r (node_t *node, int c)
 {
+#if 0
 	portal_t	*p;
-
+#endif
 	if ( node->planenum != PLANENUM_LEAF ) {
 		// decision node
 		node->cluster = -99;
-		NumberLeafs_r (node->children[0]);
-		NumberLeafs_r (node->children[1]);
+
+		if(node->has_structural_children)
+		{
+#if 0
+			if(c >= 0)
+				Sys_FPrintf (SYS_ERR,"THIS CANNOT HAPPEN\n");
+#endif
+			NumberLeafs_r (node->children[0], c);
+			NumberLeafs_r (node->children[1], c);
+		}
+		else
+		{
+			if(c < 0)
+				c = num_visclusters++;
+			NumberLeafs_r (node->children[0], c);
+			NumberLeafs_r (node->children[1], c);
+		}
 		return;
 	}
 	
@@ -217,9 +305,12 @@ void NumberLeafs_r (node_t *node)
 		return;
 	}
 
-	node->cluster = num_visclusters;
-	num_visclusters++;
+	if(c < 0)
+		c = num_visclusters++;
+	
+	node->cluster = c;
 
+#if 0
 	// count the portals
 	for (p = node->portals ; p ; )
 	{
@@ -238,6 +329,7 @@ void NumberLeafs_r (node_t *node)
 			p = p->next[1];		
 		}
 	}
+#endif
 }
 
 
@@ -254,7 +346,9 @@ void NumberClusters(tree_t *tree) {
 	Sys_FPrintf (SYS_VRB,"--- NumberClusters ---\n");
 	
 	// set the cluster field in every leaf and count the total number of portals
-	NumberLeafs_r (tree->headnode);
+	NumberLeafs_r (tree->headnode, -1);
+	CountVisportals_r (tree->headnode);
+	CountSolidFaces_r (tree->headnode);
 
 	Sys_FPrintf( SYS_VRB, "%9d visclusters\n", num_visclusters );
 	Sys_FPrintf( SYS_VRB, "%9d visportals\n", num_visportals );
diff --git a/tools/quake3/q3map2/q3map2.h b/tools/quake3/q3map2/q3map2.h
index 78b2f7c1..e912d15b 100644
--- a/tools/quake3/q3map2/q3map2.h
+++ b/tools/quake3/q3map2/q3map2.h
@@ -35,8 +35,11 @@ several games based on the Quake III Arena engine, in the form of "Q3Map2."
 
 
 /* version */
-#define Q3MAP_VERSION	"2.5.17"
-#define Q3MAP_MOTD		"Last one turns the lights off"
+#ifndef Q3MAP_VERSION
+#error no Q3MAP_VERSION defined
+#endif
+#define Q3MAP_MOTD		"Your map saw the pretty lights from q3map2's BFG"
+
 
 
 
@@ -68,7 +71,6 @@ dependencies
 #include "cmdlib.h"
 #include "mathlib.h"
 #include "md5lib.h"
-#include "md4lib.h"
 #include "ddslib.h"
 
 #include "picomodel.h"
@@ -80,7 +82,8 @@ dependencies
 #include "inout.h"
 #include "vfs.h"
 #include "png.h"
-
+#include "md4.h"
+#include "radiant_jpeglib.h"
 #include <stdlib.h>
 
 
@@ -124,7 +127,6 @@ constants
 #define Q3MAP2_EXPERIMENTAL_HIGH_PRECISION_MATH_FIXES	1
 #define Q3MAP2_EXPERIMENTAL_SNAP_NORMAL_FIX		1
 #define Q3MAP2_EXPERIMENTAL_SNAP_PLANE_FIX		1
-#define Q3MAP2_EXPERIMENTAL_MODEL_CLIPPING_FIX		1
 
 /* general */
 #define MAX_QPATH				64
@@ -189,7 +191,7 @@ constants
 /* bsp */
 #define	MAX_PATCH_SIZE			32
 #define	MAX_BRUSH_SIDES			1024
-#define MAX_BUILD_SIDES			300
+#define MAX_BUILD_SIDES			1024
 
 #define	MAX_EXPANDED_AXIS		128
 
@@ -200,6 +202,7 @@ constants
 #define	HINT_PRIORITY			1000		/* ydnar: force hint splits first and antiportal/areaportal splits last */
 #define ANTIPORTAL_PRIORITY		-1000
 #define AREAPORTAL_PRIORITY		-1000
+#define DETAIL_PRIORITY		-3000
 
 #define	PSIDE_FRONT				1
 #define	PSIDE_BACK				2
@@ -218,10 +221,10 @@ constants
 
 #define	PORTALFILE				"PRT1"
 
-#define	MAX_PORTALS				32768
+#define	MAX_PORTALS				0x20000 /* same as MAX_MAP_PORTALS */
 #define MAX_SEPERATORS			MAX_POINTS_ON_WINDING
 #define	MAX_POINTS_ON_FIXED_WINDING	24	/* ydnar: increased this from 12 at the expense of more memory */
-#define	MAX_PORTALS_ON_LEAF		128
+#define	MAX_PORTALS_ON_LEAF		1024
 
 
 /* light */
@@ -241,6 +244,7 @@ constants
 #define LIGHT_FAST_TEMP			512
 #define LIGHT_FAST_ACTUAL		(LIGHT_FAST | LIGHT_FAST_TEMP)
 #define LIGHT_NEGATIVE			1024
+#define LIGHT_UNNORMALIZED		2048	/* vortex: do not normalize _color */
 
 #define LIGHT_SUN_DEFAULT		(LIGHT_ATTEN_ANGLE | LIGHT_GRID | LIGHT_SURFACES)
 #define LIGHT_AREA_DEFAULT		(LIGHT_ATTEN_ANGLE | LIGHT_ATTEN_DISTANCE | LIGHT_GRID | LIGHT_SURFACES)	/* q3a and wolf are the same */
@@ -255,6 +259,7 @@ constants
 #define GRID_EPSILON			0.0f
 
 #define DEFAULT_LIGHTMAP_SAMPLE_SIZE	16
+#define DEFAULT_LIGHTMAP_MIN_SAMPLE_SIZE	0
 #define DEFAULT_LIGHTMAP_SAMPLE_OFFSET	1.0f
 #define DEFAULT_SUBDIVIDE_THRESHOLD		1.0f
 
@@ -269,22 +274,28 @@ constants
 #define BSP_LUXEL_SIZE			3
 #define RAD_LUXEL_SIZE			3
 #define SUPER_LUXEL_SIZE		4
+#define SUPER_FLAG_SIZE			4
+#define FLAG_FORCE_SUBSAMPLING 1
+#define FLAG_ALREADY_SUBSAMPLED 2
 #define SUPER_ORIGIN_SIZE		3
 #define SUPER_NORMAL_SIZE		4
 #define SUPER_DELUXEL_SIZE		3
 #define BSP_DELUXEL_SIZE		3
+#define SUPER_FLOODLIGHT_SIZE	4
 
 #define VERTEX_LUXEL( s, v )	(vertexLuxels[ s ] + ((v) * VERTEX_LUXEL_SIZE))
 #define RAD_VERTEX_LUXEL( s, v )(radVertexLuxels[ s ] + ((v) * VERTEX_LUXEL_SIZE))
 #define BSP_LUXEL( s, x, y )	(lm->bspLuxels[ s ] + ((((y) * lm->w) + (x)) * BSP_LUXEL_SIZE))
 #define RAD_LUXEL( s, x, y )	(lm->radLuxels[ s ] + ((((y) * lm->w) + (x)) * RAD_LUXEL_SIZE))
 #define SUPER_LUXEL( s, x, y )	(lm->superLuxels[ s ] + ((((y) * lm->sw) + (x)) * SUPER_LUXEL_SIZE))
+#define SUPER_FLAG( x, y )	(lm->superFlags + ((((y) * lm->sw) + (x)) * SUPER_FLAG_SIZE))
 #define SUPER_DELUXEL( x, y )	(lm->superDeluxels + ((((y) * lm->sw) + (x)) * SUPER_DELUXEL_SIZE))
 #define BSP_DELUXEL( x, y )		(lm->bspDeluxels + ((((y) * lm->w) + (x)) * BSP_DELUXEL_SIZE))
 #define SUPER_CLUSTER( x, y )	(lm->superClusters + (((y) * lm->sw) + (x)))
 #define SUPER_ORIGIN( x, y )	(lm->superOrigins + ((((y) * lm->sw) + (x)) * SUPER_ORIGIN_SIZE))
 #define SUPER_NORMAL( x, y )	(lm->superNormals + ((((y) * lm->sw) + (x)) * SUPER_NORMAL_SIZE))
 #define SUPER_DIRT( x, y )		(lm->superNormals + ((((y) * lm->sw) + (x)) * SUPER_NORMAL_SIZE) + 3)	/* stash dirtyness in normal[ 3 ] */
+#define SUPER_FLOODLIGHT( x, y )	(lm->superFloodLight + ((((y) * lm->sw) + (x)) * SUPER_FLOODLIGHT_SIZE) )
 
 
 
@@ -306,27 +317,19 @@ abstracted bsp file
 #define MAX_LIGHTMAP_SHADERS	256
 
 /* ok to increase these at the expense of more memory */
-#define	MAX_MAP_MODELS			0x400
-#define	MAX_MAP_BRUSHES			0x10000
 #define	MAX_MAP_ENTITIES		0x1000		//%	0x800	/* ydnar */
 #define	MAX_MAP_ENTSTRING		0x80000		//%	0x40000	/* ydnar */
-#define	MAX_MAP_SHADERS			0x800		//%	0x400	/* ydnar */
 
 #define	MAX_MAP_AREAS			0x100		/* MAX_MAP_AREA_BYTES in q_shared must match! */
 #define	MAX_MAP_FOGS			30			//& 0x100	/* RBSP (32 - world fog - goggles) */
-#define	MAX_MAP_PLANES			0x100000	//%	0x20000	/* ydnar for md */
-#define	MAX_MAP_NODES			0x20000
-#define	MAX_MAP_BRUSHSIDES		0x100000	//%	0x20000	/* ydnar */
 #define	MAX_MAP_LEAFS			0x20000
-#define	MAX_MAP_LEAFFACES		0x100000	//%	0x20000	/* ydnar */
-#define	MAX_MAP_LEAFBRUSHES		0x40000
 #define	MAX_MAP_PORTALS			0x20000
 #define	MAX_MAP_LIGHTING		0x800000
 #define	MAX_MAP_LIGHTGRID		0x100000	//%	0x800000 /* ydnar: set to points, not bytes */
-#define	MAX_MAP_VISIBILITY		0x200000
+#define MAX_MAP_VISCLUSTERS     0x4000 // <= MAX_MAP_LEAFS
+#define	MAX_MAP_VISIBILITY		(VIS_HEADER_SIZE + MAX_MAP_VISCLUSTERS * (((MAX_MAP_VISCLUSTERS + 63) & ~63) >> 3))
 
 #define	MAX_MAP_DRAW_SURFS		0x20000
-#define	MAX_MAP_DRAW_VERTS		0x100000
 #define	MAX_MAP_DRAW_INDEXES	0x80000
 
 #define MAX_MAP_ADVERTISEMENTS	30
@@ -525,7 +528,7 @@ general types
 ------------------------------------------------------------------------------- */
 
 /* ydnar: for smaller structs */
-typedef char	qb_t;
+typedef unsigned char	qb_t;
 
 
 /* ydnar: for q3map_tcMod */
@@ -542,6 +545,13 @@ typedef struct surfaceParm_s
 }
 surfaceParm_t;
 
+typedef enum
+{
+	MINIMAP_MODE_GRAY,
+	MINIMAP_MODE_BLACK,
+	MINIMAP_MODE_WHITE
+}
+miniMapMode_t;
 
 typedef struct game_s
 {
@@ -558,7 +568,23 @@ typedef struct game_s
 	qboolean			wolfLight;						/* when true, lights work like wolf q3map  */
 	int					lightmapSize;					/* bsp lightmap width/height */
 	float				lightmapGamma;					/* default lightmap gamma */
+	float				lightmapExposure;				/* default lightmap exposure */
 	float				lightmapCompensate;				/* default lightmap compensate value */
+	float				gridScale;						/* vortex: default lightgrid scale (affects both directional and ambient spectres) */
+	float				gridAmbientScale;				/* vortex: default lightgrid ambient spectre scale */
+	qboolean			lightAngleHL;					/* jal: use half-lambert curve for light angle attenuation */
+	qboolean			noStyles;						/* use lightstyles hack or not */
+	qboolean			keepLights;						/* keep light entities on bsp */
+	int					patchSubdivisions;				/* default patch subdivisions tolerance */
+	qboolean			patchShadows;					/* patch casting enabled */
+	qboolean			deluxeMap;						/* compile deluxemaps */
+	int					deluxeMode;						/* deluxemap mode (0 - modelspace, 1 - tangentspace with renormalization, 2 - tangentspace without renormalization) */
+	int                 miniMapSize;                    /* minimap size */
+	float               miniMapSharpen;                 /* minimap sharpening coefficient */
+	float               miniMapBorder;                  /* minimap border amount */
+	qboolean            miniMapKeepAspect;              /* minimap keep aspect ratio by letterboxing */
+	miniMapMode_t       miniMapMode;                    /* minimap mode */
+	char                *miniMapNameFormat;             /* minimap name format */
 	char				*bspIdent;						/* 4-letter bsp file prefix */
 	int					bspVersion;						/* bsp version to use */
 	qboolean			lumpSwap;						/* cod-style len/ofs order */
@@ -625,6 +651,14 @@ typedef struct remap_s
 }
 remap_t;
 
+typedef struct skinfile_s
+{
+	struct skinfile_s	*next;
+	char				name[ 1024 ];
+	char				to[ MAX_QPATH ];
+}
+skinfile_t;
+
 
 /* wingdi.h hack, it's the same: 0 */
 #undef CM_NONE
@@ -639,8 +673,12 @@ typedef enum
 	CM_ALPHA_SCALE,
 	CM_COLOR_DOT_PRODUCT,
 	CM_ALPHA_DOT_PRODUCT,
+	CM_COLOR_DOT_PRODUCT_SCALE,
+	CM_ALPHA_DOT_PRODUCT_SCALE,
 	CM_COLOR_DOT_PRODUCT_2,
-	CM_ALPHA_DOT_PRODUCT_2
+	CM_ALPHA_DOT_PRODUCT_2,
+	CM_COLOR_DOT_PRODUCT_2_SCALE,
+	CM_ALPHA_DOT_PRODUCT_2_SCALE
 }
 colorModType_t;
 
@@ -677,6 +715,7 @@ typedef struct shaderInfo_s
 	char				*backShader;					/* for surfaces that generate different front and back passes */
 	char				*cloneShader;					/* ydnar: for cloning of a surface */
 	char				*remapShader;					/* ydnar: remap a shader in final stage */
+	char				*deprecateShader;				/* vortex: shader is deprecated and replaced by this on use */
 
 	surfaceModel_t		*surfaceModel;					/* ydnar: for distribution of models */
 	foliage_t			*foliage;						/* ydnar/splash damage: wolf et foliage */
@@ -728,7 +767,8 @@ typedef struct shaderInfo_s
 	qb_t				noFog;							/* ydnar: supress fogging */
 	qb_t				clipModel;						/* ydnar: solid model hack */
 	qb_t				noVertexLight;					/* ydnar: leave vertex color alone */
-	
+	qb_t				noDirty;						/* jal: do not apply the dirty pass to this surface */
+
 	byte				styleMarker;					/* ydnar: light styles hack */
 	
 	float				vertexScale;					/* vertex light scale */
@@ -752,7 +792,13 @@ typedef struct shaderInfo_s
 	
 	vec3_t				color;							/* normalized color */
 	vec3_t				averageColor;
-	byte				lightStyle;
+	byte				lightStyle;					
+
+	/* vortex: per-surface floodlight */
+	float				floodlightDirectionScale;
+	vec3_t				floodlightRGB; 
+	float				floodlightIntensity;
+	float				floodlightDistance;
 	
 	qb_t				lmMergable;						/* ydnar */
 	int					lmCustomWidth, lmCustomHeight;	/* ydnar */
@@ -783,7 +829,7 @@ typedef struct face_s
 	struct face_s		*next;
 	int					planenum;
 	int					priority;
-	qboolean			checked;
+	//qboolean			checked;
 	int					compileFlags;
 	winding_t			*w;
 }
@@ -795,7 +841,8 @@ typedef struct plane_s
 	vec3_t				normal;
 	vec_t				dist;
 	int					type;
-	struct plane_s		*hash_chain;
+	int                 counter;
+	int					hash_chain;
 }
 plane_t;
 
@@ -862,7 +909,9 @@ typedef struct brush_s
 	shaderInfo_t		*celShader;			/* :) */
 	
 	/* ydnar: gs mods */
+	int					lightmapSampleSize; /* jal : entity based _lightmapsamplesize */
 	float				lightmapScale;
+	float				shadeAngleDegrees; /* jal : entity based _shadeangle */
 	vec3_t				eMins, eMaxs;
 	indexMap_t			*im;
 
@@ -913,6 +962,7 @@ typedef struct parseMesh_s
 	shaderInfo_t		*celShader;				/* :) */
 	
 	/* ydnar: gs mods */
+	int					lightmapSampleSize;		/* jal : entity based _lightmapsamplesize */
 	float				lightmapScale;
 	vec3_t				eMins, eMaxs;
 	indexMap_t			*im;
@@ -1010,6 +1060,9 @@ typedef struct mapDrawSurface_s
 	
 	/* ydnar: per-surface (per-entity, actually) lightmap sample size scaling */
 	float				lightmapScale;
+
+	/* jal: per-surface (per-entity, actually) shadeangle */
+	float				shadeAngleDegrees;
 	
 	/* ydnar: surface classification */
 	vec3_t				mins, maxs;
@@ -1028,7 +1081,7 @@ typedef struct mapDrawSurface_s
 	int					maxIterations;
 	int					patchWidth, patchHeight;
 	vec3_t				bounds[ 2 ];
-	
+
 	/* ydnar/sd: for foliage */
 	int					numFoliageInstances;
 	
@@ -1053,6 +1106,7 @@ typedef struct metaTriangle_s
 	shaderInfo_t		*si;
 	side_t				*side;
 	int					entityNum, surfaceNum, planeNum, fogNum, sampleSize, castShadows, recvShadows;
+	float				shadeAngleDegrees;
 	vec4_t				plane;
 	vec3_t				lightmapAxis;
 	int					indexes[ 3 ];
@@ -1076,6 +1130,7 @@ typedef struct
 	int					mapEntityNum, firstDrawSurf;
 	int					firstBrush, numBrushes;		/* only valid during BSP compile */
 	epair_t				*epairs;
+	vec3_t				originbrush_origin;
 }
 entity_t;
 
@@ -1109,6 +1164,8 @@ typedef struct node_s
 	entity_t			*occupant;		/* for leak file testing */
 
 	struct portal_s		*portals;		/* also on nodes during construction */
+
+	qboolean            has_structural_children;
 }
 node_t;
 
@@ -1266,6 +1323,7 @@ typedef struct light_s
 	float				radiusByDist;	/* for spotlights */
 	float				fade;			/* ydnar: from wolf, for linear lights */
 	float				angleScale;		/* ydnar: stolen from vlight for K */
+	float				extraDist;		/* "extra dimension" distance of the light, to kill hot spots */
 
 	float				add;			/* ydnar: used for area lights */
 	float				envelope;		/* ydnar: units until falloff < tolerance */
@@ -1311,12 +1369,15 @@ typedef struct
 	
 	/* input and output */
 	vec3_t				color;			/* starts out at full color, may be reduced if transparent surfaces are crossed */
-	
+	vec3_t				colorNoShadow;	/* result color with no shadow casting */
+	vec3_t				directionContribution; /* result contribution to the deluxe map */
+
 	/* output */
 	vec3_t				hit;
 	int					compileFlags;	/* for determining surface compile flags traced through */
 	qboolean			passSolid;
 	qboolean			opaque;
+	vec_t			forceSubsampling; /* needs subsampling (alphashadow), value = max color contribution possible from it */
 	
 	/* working data */
 	int					numTestNodes;
@@ -1382,6 +1443,13 @@ typedef struct rawLightmap_s
 	int						numLightClusters, *lightClusters;
 	
 	int						sampleSize, actualSampleSize, axisNum;
+
+	/* vortex: per-surface floodlight */
+	float					floodlightDirectionScale;
+	vec3_t					floodlightRGB; 
+	float					floodlightIntensity;
+	float					floodlightDistance;
+
 	int						entityNum;
 	int						recvShadows;
 	vec3_t					mins, maxs, axis, origin, *vecs;
@@ -1401,12 +1469,14 @@ typedef struct rawLightmap_s
 	float					*bspLuxels[ MAX_LIGHTMAPS ];
 	float					*radLuxels[ MAX_LIGHTMAPS ];
 	float					*superLuxels[ MAX_LIGHTMAPS ];
+	unsigned char				*superFlags;
 	float					*superOrigins;
 	float					*superNormals;
 	int						*superClusters;
 	
 	float					*superDeluxels;	/* average light direction */
 	float					*bspDeluxels;
+	float					*superFloodLight;
 }
 rawLightmap_t;
 
@@ -1423,7 +1493,7 @@ rawGridPoint_t;
 
 typedef struct surfaceInfo_s
 {
-	bspModel_t			*model;
+	int					modelindex;
 	shaderInfo_t		*si;
 	rawLightmap_t		*lm;
 	int					parentSurfaceNum, childSurfaceNum;
@@ -1436,8 +1506,6 @@ typedef struct surfaceInfo_s
 }
 surfaceInfo_t;
 
-
-
 /* -------------------------------------------------------------------------------
 
 prototypes
@@ -1462,11 +1530,15 @@ int							BSPMain( int argc, char **argv );
 
 /* convert_map.c */
 int							ConvertBSPToMap( char *bspName );
+int							ConvertBSPToMap_BP( char *bspName );
 
 
 /* convert_ase.c */
 int							ConvertBSPToASE( char *bspName );
 
+/* convert_obj.c */
+int							ConvertBSPToOBJ( char *bspName );
+
 
 /* brush.c */
 sideRef_t					*AllocSideRef( side_t *side, sideRef_t *next );
@@ -1513,11 +1585,11 @@ void						MakeNormalVectors( vec3_t forward, vec3_t right, vec3_t up );
 
 
 /* map.c */
-void 						LoadMapFile( char *filename, qboolean onlyLights );
+void 						LoadMapFile( char *filename, qboolean onlyLights, qboolean noCollapseGroups );
 int							FindFloatPlane( vec3_t normal, vec_t dist, int numPoints, vec3_t *points );
 int							PlaneTypeForNormal( vec3_t normal );
 void						AddBrushBevels( void );
-brush_t						*FinishBrush( void );
+brush_t						*FinishBrush(qboolean noCollapseGroups);
 
 
 /* portals.c */
@@ -1552,7 +1624,7 @@ void						SetLightStyles( void );
 int							EmitShader( const char *shader, int *contentFlags, int *surfaceFlags );
 
 void						BeginBSPFile( void );
-void						EndBSPFile( void );
+void						EndBSPFile( qboolean do_write );
 void						EmitBrushes( brush_t *brushes, int *firstBrush, int *numBrushes );
 void						EmitFogs( void );
 
@@ -1571,6 +1643,7 @@ void						FreeTreePortals_r( node_t *node );
 void						ParsePatch( qboolean onlyLights );
 mesh_t						*SubdivideMesh( mesh_t in, float maxError, float minLength );
 void						PatchMapDrawSurfs( entity_t *e );
+void						TriangulatePatchSurface( entity_t *e , mapDrawSurface_t *ds );
 
 
 /* tjunction.c */
@@ -1593,10 +1666,10 @@ tree_t						*FaceBSP( face_t *list );
 
 /* model.c */
 void						PicoPrintFunc( int level, const char *str );
-void						PicoLoadFileFunc( char *name, byte **buffer, int *bufSize );
-picoModel_t					*FindModel( char *name, int frame );
-picoModel_t					*LoadModel( char *name, int frame );
-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 );
+void						PicoLoadFileFunc( const char *name, byte **buffer, int *bufSize );
+picoModel_t					*FindModel( const char *name, int frame );
+picoModel_t					*LoadModel( const char *name, int frame );
+void						InsertModel( const char *name, int skin, int frame, m4x4_t transform, remap_t *remap, shaderInfo_t *celShader, int eNum, int castShadows, int recvShadows, int spawnFlags, float lightmapScale, int lightmapSampleSize, float shadeAngle );
 void						AddTriangleModels( entity_t *e );
 
 
@@ -1604,6 +1677,7 @@ void						AddTriangleModels( entity_t *e );
 mapDrawSurface_t			*AllocDrawSurface( surfaceType_t type );
 void						FinishSurface( mapDrawSurface_t *ds );
 void						StripFaceSurface( mapDrawSurface_t *ds );
+void						MaxAreaFaceSurface( mapDrawSurface_t *ds );
 qboolean					CalcSurfaceTextureRange( mapDrawSurface_t *ds );
 qboolean					CalcLightmapAxis( vec3_t normal, vec3_t axis );
 void						ClassifySurfaces( int numSurfs, mapDrawSurface_t *ds );
@@ -1616,7 +1690,7 @@ void						ClearSurface( mapDrawSurface_t *ds );
 void						AddEntitySurfaceModels( entity_t *e );
 mapDrawSurface_t			*DrawSurfaceForSide( entity_t *e, brush_t *b, side_t *s, winding_t *w );
 mapDrawSurface_t			*DrawSurfaceForMesh( entity_t *e, parseMesh_t *p, mesh_t *mesh );
-mapDrawSurface_t			*DrawSurfaceForFlare( int entNum, vec3_t origin, vec3_t normal, vec3_t color, char *flareShader, int lightStyle );
+mapDrawSurface_t			*DrawSurfaceForFlare( int entNum, vec3_t origin, vec3_t normal, vec3_t color, const char *flareShader, int lightStyle );
 mapDrawSurface_t			*DrawSurfaceForShader( char *shader );
 void						ClipSidesIntoTree( entity_t *e, tree_t *tree );
 void						MakeDebugPortalSurfs( tree_t *tree );
@@ -1625,6 +1699,8 @@ void						SubdivideFaceSurfaces( entity_t *e, tree_t *tree );
 void						AddEntitySurfaceModels( entity_t *e );
 int							AddSurfaceModels( mapDrawSurface_t *ds );
 void						FilterDrawsurfsIntoTree( entity_t *e, tree_t *tree );
+void						EmitPatchSurface( entity_t *e, mapDrawSurface_t *ds );
+void						EmitTriangleSurface( mapDrawSurface_t *ds );
 
 
 /* surface_fur.c */
@@ -1642,6 +1718,7 @@ void						MakeEntityMetaTriangles( entity_t *e );
 void						FixMetaTJunctions( void );
 void						SmoothMetaTriangles( void );
 void						MergeMetaTriangles( void );
+void						EmitMetaStats(); // vortex: print meta statistics even in no-verbose mode
 
 
 /* surface_extra.c */
@@ -1655,6 +1732,7 @@ int							GetSurfaceExtraEntityNum( int num );
 int							GetSurfaceExtraCastShadows( int num );
 int							GetSurfaceExtraRecvShadows( int num );
 int							GetSurfaceExtraSampleSize( int num );
+int							GetSurfaceExtraMinSampleSize( int num );
 float						GetSurfaceExtraLongestCurve( int num );
 void						GetSurfaceExtraLightmapAxis( int num, vec3_t lightmapAxis );
 
@@ -1666,6 +1744,8 @@ void						LoadSurfaceExtraFile( const char *path );
 void						ProcessDecals( void );
 void						MakeEntityDecals( entity_t *e );
 
+/* map.c */
+void						TextureAxisFromPlane( plane_t *pln, vec3_t xv, vec3_t yv );
 
 /* brush_primit.c */
 void						ComputeAxisBase( vec3_t normal, vec3_t texX, vec3_t texY);
@@ -1719,6 +1799,12 @@ void						SetupDirt();
 float						DirtForSample( trace_t *trace );
 void						DirtyRawLightmap( int num );
 
+void						SetupFloodLight();
+void						FloodlightRawLightmaps();
+void						FloodlightIlluminateLightmap( rawLightmap_t *lm );
+float						FloodLightForSample( trace_t *trace , float floodLightDistance, qboolean floodLightLowQuality);
+void						FloodLightRawLightmap( int num );
+
 void						IlluminateRawLightmap( int num );
 void						IlluminateVertexes( int num );
 
@@ -1787,6 +1873,7 @@ void						SwapBlock( int *block, int size );
 int							GetLumpElements( bspHeader_t *header, int lump, int size );
 void						*GetLump( bspHeader_t *header, int lump );
 int							CopyLump( bspHeader_t *header, int lump, void *dest, int size );
+int							CopyLump_Allocate( bspHeader_t *header, int lump, void **dest, int size, int *allocationVariable );
 void						AddLump( FILE *file, bspHeader_t *header, int lumpNum, const void *data, int length );
 
 void						LoadBSPFile( const char *filename );
@@ -1798,12 +1885,15 @@ void						ParseEntities( void );
 void						UnparseEntities( void );
 void						PrintEntity( const entity_t *ent );
 void						SetKeyValue( entity_t *ent, const char *key, const char *value );
+qboolean					KeyExists( const entity_t *ent, const char *key ); /* VorteX: check if key exists */
 const char					*ValueForKey( const entity_t *ent, const char *key );
 int							IntForKey( const entity_t *ent, const char *key );
 vec_t						FloatForKey( const entity_t *ent, const char *key );
 void						GetVectorForKey( const entity_t *ent, const char *key, vec3_t vec );
 entity_t					*FindTargetEntity( const char *target );
 void						GetEntityShadowFlags( const entity_t *ent, const entity_t *ent2, int *castShadows, int *recvShadows );
+void InjectCommandLine(char **argv, int beginArgs, int endArgs);
+		
 
 
 /* bspfile_ibsp.c */
@@ -1844,6 +1934,8 @@ Q_EXTERN game_t				games[]
 								,
 								#include "game_nexuiz.h"/* most be after game_quake3.h as they share defines! */
 								,
+								#include "game_xonotic.h"/* most be after game_quake3.h as they share defines! */
+								,
 								#include "game_tremulous.h" /*LinuxManMikeC: must be after game_quake3.h, depends on #define's set in it */
 								,
 								#include "game_tenebrae.h"
@@ -1866,7 +1958,13 @@ Q_EXTERN game_t				games[]
 								,
 								#include "game_reaction.h" /* must be after game_quake3.h */
 								,
-								{ NULL }	/* null game */
+								#include "game_darkplaces.h"	/* vortex: darkplaces q1 engine */
+								,
+								#include "game_dq.h"	/* vortex: deluxe quake game ( darkplaces q1 engine) */
+								,
+								#include "game_prophecy.h"	/* vortex: prophecy game ( darkplaces q1 engine) */
+								,
+								#include "game__null.h"	/* null game (must be last item) */
 							};
 #endif
 Q_EXTERN game_t				*game Q_ASSIGN( &games[ 0 ] );
@@ -1914,6 +2012,9 @@ Q_EXTERN qboolean			nofog Q_ASSIGN( qfalse );
 Q_EXTERN qboolean			noHint Q_ASSIGN( qfalse );				/* ydnar */
 Q_EXTERN qboolean			renameModelShaders Q_ASSIGN( qfalse );	/* ydnar */
 Q_EXTERN qboolean			skyFixHack Q_ASSIGN( qfalse );			/* ydnar */
+Q_EXTERN qboolean			bspAlternateSplitWeights Q_ASSIGN( qfalse );			/* 27 */
+Q_EXTERN qboolean			deepBSP Q_ASSIGN( qfalse );			/* div0 */
+Q_EXTERN qboolean			maxAreaFaceSurface Q_ASSIGN( qfalse );			/* divVerent */
 
 Q_EXTERN int				patchSubdivisions Q_ASSIGN( 8 );		/* ydnar: -patchmeta subdivisions */
 
@@ -1930,6 +2031,9 @@ Q_EXTERN qboolean			emitFlares Q_ASSIGN( qfalse );
 Q_EXTERN qboolean			debugSurfaces Q_ASSIGN( qfalse );
 Q_EXTERN qboolean			debugInset Q_ASSIGN( qfalse );
 Q_EXTERN qboolean			debugPortals Q_ASSIGN( qfalse );
+Q_EXTERN qboolean           lightmapTriangleCheck Q_ASSIGN(qfalse);
+Q_EXTERN qboolean           lightmapExtraVisClusterNudge Q_ASSIGN(qfalse);
+Q_EXTERN qboolean           lightmapFill Q_ASSIGN(qfalse);
 
 #if Q3MAP2_EXPERIMENTAL_SNAP_NORMAL_FIX
 // Increasing the normalEpsilon to compensate for new logic in SnapNormal(), where
@@ -1972,13 +2076,16 @@ Q_EXTERN char				source[ 1024 ];
 Q_EXTERN char				outbase[ 32 ];
 
 Q_EXTERN int				sampleSize;						/* lightmap sample size in units */
+Q_EXTERN int				minSampleSize;                  /* minimum sample size to use at all */
+Q_EXTERN int				sampleScale;					/* vortex: lightmap sample scale (ie quality)*/
 
 Q_EXTERN int				mapEntityNum Q_ASSIGN( 0 );
 
 Q_EXTERN int				entitySourceBrushes;
 
-Q_EXTERN plane_t			mapplanes[ MAX_MAP_PLANES ];	/* mapplanes[ num ^ 1 ] will always be the mirror or mapplanes[ num ] */
-Q_EXTERN int				nummapplanes;					/* nummapplanes will always be even */
+Q_EXTERN plane_t			*mapplanes Q_ASSIGN(NULL);	/* mapplanes[ num ^ 1 ] will always be the mirror or mapplanes[ num ] */
+Q_EXTERN int				nummapplanes Q_ASSIGN(0);		/* nummapplanes will always be even */
+Q_EXTERN int				allocatedmapplanes Q_ASSIGN(0);
 Q_EXTERN int				numMapPatches;
 Q_EXTERN vec3_t				mapMins, mapMaxs;
 
@@ -2001,6 +2108,7 @@ Q_EXTERN int				numMapDrawSurfs;
 Q_EXTERN int				numSurfacesByType[ NUM_SURFACE_TYPES ];
 Q_EXTERN int				numClearedSurfaces;
 Q_EXTERN int				numStripSurfaces;
+Q_EXTERN int				numMaxAreaSurfaces;
 Q_EXTERN int				numFanSurfaces;
 Q_EXTERN int				numMergedSurfaces;
 Q_EXTERN int				numMergedVerts;
@@ -2047,13 +2155,12 @@ Q_EXTERN qboolean			fastvis;
 Q_EXTERN qboolean			noPassageVis;
 Q_EXTERN qboolean			passageVisOnly;
 Q_EXTERN qboolean			mergevis;
+Q_EXTERN qboolean			mergevisportals;
 Q_EXTERN qboolean			nosort;
 Q_EXTERN qboolean			saveprt;
 Q_EXTERN qboolean			hint;	/* ydnar */
 Q_EXTERN char				inbase[ MAX_QPATH ];
-
-/* other bits */
-Q_EXTERN int				totalvis;
+Q_EXTERN char				globalCelShader[ MAX_QPATH ];
 
 Q_EXTERN float				farPlaneDist;	/* rr2do2, rf, mre, ydnar all contributed to this one... */
 
@@ -2094,10 +2201,13 @@ light global variables
 
 /* commandline arguments */
 Q_EXTERN qboolean			wolfLight Q_ASSIGN( qfalse );
+Q_EXTERN float				extraDist Q_ASSIGN( 0.0f );
 Q_EXTERN qboolean			loMem Q_ASSIGN( qfalse );
 Q_EXTERN qboolean			noStyles Q_ASSIGN( qfalse );
+Q_EXTERN qboolean			keepLights Q_ASSIGN( qfalse );
 
 Q_EXTERN int				sampleSize Q_ASSIGN( DEFAULT_LIGHTMAP_SAMPLE_SIZE );
+Q_EXTERN int				minSampleSize Q_ASSIGN( DEFAULT_LIGHTMAP_MIN_SAMPLE_SIZE );
 Q_EXTERN qboolean			noVertexLighting Q_ASSIGN( qfalse );
 Q_EXTERN qboolean			noGridLighting Q_ASSIGN( qfalse );
 
@@ -2108,6 +2218,7 @@ Q_EXTERN qboolean			cpmaHack Q_ASSIGN( qfalse );
 
 Q_EXTERN qboolean			deluxemap Q_ASSIGN( qfalse );
 Q_EXTERN qboolean			debugDeluxemap Q_ASSIGN( qfalse );
+Q_EXTERN int				deluxemode Q_ASSIGN( 0 );	/* deluxemap format (0 - modelspace, 1 - tangentspace with renormalization, 2 - tangentspace without renormalization) */
 
 Q_EXTERN qboolean			fast Q_ASSIGN( qfalse );
 Q_EXTERN qboolean			faster Q_ASSIGN( qfalse );
@@ -2125,14 +2236,19 @@ Q_EXTERN qboolean			shade Q_ASSIGN( qfalse );
 Q_EXTERN float				shadeAngleDegrees Q_ASSIGN( 0.0f );
 Q_EXTERN int				superSample Q_ASSIGN( 0 );
 Q_EXTERN int				lightSamples Q_ASSIGN( 1 );
+Q_EXTERN qboolean			lightRandomSamples Q_ASSIGN( qfalse );
+Q_EXTERN int				lightSamplesSearchBoxSize Q_ASSIGN( 1 );
 Q_EXTERN qboolean			filter Q_ASSIGN( qfalse );
 Q_EXTERN qboolean			dark Q_ASSIGN( qfalse );
 Q_EXTERN qboolean			sunOnly Q_ASSIGN( qfalse );
 Q_EXTERN int				approximateTolerance Q_ASSIGN( 0 );
 Q_EXTERN qboolean			noCollapse Q_ASSIGN( qfalse );
+Q_EXTERN int				lightmapSearchBlockSize Q_ASSIGN( 0 );
 Q_EXTERN qboolean			exportLightmaps Q_ASSIGN( qfalse );
 Q_EXTERN qboolean			externalLightmaps Q_ASSIGN( qfalse );
 Q_EXTERN int				lmCustomSize Q_ASSIGN( LIGHTMAP_WIDTH );
+Q_EXTERN char *				lmCustomDir Q_ASSIGN( NULL );
+Q_EXTERN int				lmLimitSize Q_ASSIGN( 0 );
 
 Q_EXTERN qboolean			dirty Q_ASSIGN( qfalse );
 Q_EXTERN qboolean			dirtDebug Q_ASSIGN( qfalse );
@@ -2141,6 +2257,15 @@ Q_EXTERN float				dirtDepth Q_ASSIGN( 128.0f );
 Q_EXTERN float				dirtScale Q_ASSIGN( 1.0f );
 Q_EXTERN float				dirtGain Q_ASSIGN( 1.0f );
 
+/* 27: floodlighting */
+Q_EXTERN qboolean					debugnormals Q_ASSIGN( qfalse );
+Q_EXTERN qboolean					floodlighty Q_ASSIGN( qfalse );
+Q_EXTERN qboolean					floodlight_lowquality Q_ASSIGN( qfalse );
+Q_EXTERN vec3_t						floodlightRGB;
+Q_EXTERN float						floodlightIntensity Q_ASSIGN( 512.0f );
+Q_EXTERN float						floodlightDistance Q_ASSIGN( 1024.0f );
+Q_EXTERN float						floodlightDirectionScale Q_ASSIGN( 1.0f );
+
 Q_EXTERN qboolean			dump Q_ASSIGN( qfalse );
 Q_EXTERN qboolean			debug Q_ASSIGN( qfalse );
 Q_EXTERN qboolean			debugUnused Q_ASSIGN( qfalse );
@@ -2158,8 +2283,19 @@ Q_EXTERN float				areaScale Q_ASSIGN( 0.25f );
 Q_EXTERN float				skyScale Q_ASSIGN( 1.0f );
 Q_EXTERN float				bounceScale Q_ASSIGN( 0.25f );
 
+/* jal: alternative angle attenuation curve */
+Q_EXTERN qboolean			lightAngleHL Q_ASSIGN( qfalse );
+ 
+/* vortex: gridscale and gridambientscale */
+Q_EXTERN float				gridScale Q_ASSIGN( 1.0f );
+Q_EXTERN float				gridAmbientScale Q_ASSIGN( 1.0f );
+Q_EXTERN float				gridDirectionality Q_ASSIGN( 1.0f );
+Q_EXTERN float				gridAmbientDirectionality Q_ASSIGN( 0.0f );
+Q_EXTERN qboolean			inGrid Q_ASSIGN(0);
+
 /* ydnar: lightmap gamma/compensation */
 Q_EXTERN float				lightmapGamma Q_ASSIGN( 1.0f );
+Q_EXTERN float				lightmapExposure Q_ASSIGN( 1.0f );
 Q_EXTERN float				lightmapCompensate Q_ASSIGN( 1.0f );
 
 /* ydnar: for runtime tweaking of falloff tolerance */
@@ -2168,6 +2304,10 @@ Q_EXTERN qboolean			exactPointToPolygon Q_ASSIGN( qtrue );
 Q_EXTERN float				formFactorValueScale Q_ASSIGN( 3.0f );
 Q_EXTERN float				linearScale Q_ASSIGN( 1.0f / 8000.0f );
 
+// for .ase conversion
+Q_EXTERN qboolean			shadersAsBitmap Q_ASSIGN( qfalse );
+Q_EXTERN qboolean			lightmapsAsTexcoord Q_ASSIGN( qfalse );
+
 Q_EXTERN light_t			*lights;
 Q_EXTERN int				numPointLights;
 Q_EXTERN int				numSpotLights;
@@ -2264,6 +2404,9 @@ Q_EXTERN int				numBSPLightmaps Q_ASSIGN( 0 );
 Q_EXTERN int				numExtLightmaps Q_ASSIGN( 0 );
 Q_EXTERN outLightmap_t		*outLightmaps Q_ASSIGN( NULL );
 
+/* vortex: per surface floodlight statictics */
+Q_EXTERN int				numSurfacesFloodlighten Q_ASSIGN( 0 );
+
 /* grid points */
 Q_EXTERN int				numRawGridPoints Q_ASSIGN( 0 );
 Q_EXTERN rawGridPoint_t		*rawGridPoints Q_ASSIGN( NULL );
@@ -2306,34 +2449,43 @@ Q_EXTERN int				numBSPEntities Q_ASSIGN( 0 );
 Q_EXTERN entity_t			entities[ MAX_MAP_ENTITIES ];
 
 Q_EXTERN int				numBSPModels Q_ASSIGN( 0 );
-Q_EXTERN bspModel_t			bspModels[ MAX_MAP_MODELS ];
+Q_EXTERN int				allocatedBSPModels Q_ASSIGN( 0 );
+Q_EXTERN bspModel_t*		bspModels Q_ASSIGN(NULL);
 
 Q_EXTERN int				numBSPShaders Q_ASSIGN( 0 );
-Q_EXTERN bspShader_t		bspShaders[ MAX_MAP_MODELS ];
+Q_EXTERN int				allocatedBSPShaders Q_ASSIGN( 0 );
+Q_EXTERN bspShader_t*		bspShaders Q_ASSIGN(0);
 
 Q_EXTERN int				bspEntDataSize Q_ASSIGN( 0 );
-Q_EXTERN char				bspEntData[ MAX_MAP_ENTSTRING ];
+Q_EXTERN int				allocatedBSPEntData Q_ASSIGN( 0 );
+Q_EXTERN char				*bspEntData Q_ASSIGN(0);
 
 Q_EXTERN int				numBSPLeafs Q_ASSIGN( 0 );
 Q_EXTERN bspLeaf_t			bspLeafs[ MAX_MAP_LEAFS ];
 
 Q_EXTERN int				numBSPPlanes Q_ASSIGN( 0 );
-Q_EXTERN bspPlane_t			bspPlanes[ MAX_MAP_PLANES ];
+Q_EXTERN int				allocatedBSPPlanes Q_ASSIGN(0);
+Q_EXTERN bspPlane_t			*bspPlanes;
 
 Q_EXTERN int				numBSPNodes Q_ASSIGN( 0 );
-Q_EXTERN bspNode_t			bspNodes[ MAX_MAP_NODES ];
+Q_EXTERN int				allocatedBSPNodes Q_ASSIGN( 0 );
+Q_EXTERN bspNode_t*			bspNodes Q_ASSIGN(NULL);
 
 Q_EXTERN int				numBSPLeafSurfaces Q_ASSIGN( 0 );
-Q_EXTERN int				bspLeafSurfaces[ MAX_MAP_LEAFFACES ];
+Q_EXTERN int				allocatedBSPLeafSurfaces Q_ASSIGN( 0 );
+Q_EXTERN int*				bspLeafSurfaces Q_ASSIGN(NULL);
 
 Q_EXTERN int				numBSPLeafBrushes Q_ASSIGN( 0 );
-Q_EXTERN int				bspLeafBrushes[ MAX_MAP_LEAFBRUSHES ];
+Q_EXTERN int				allocatedBSPLeafBrushes Q_ASSIGN( 0 );
+Q_EXTERN int*				bspLeafBrushes Q_ASSIGN(NULL);
 
 Q_EXTERN int				numBSPBrushes Q_ASSIGN( 0 );
-Q_EXTERN bspBrush_t			bspBrushes[ MAX_MAP_BRUSHES ];
+Q_EXTERN int				allocatedBSPBrushes Q_ASSIGN( 0 );
+Q_EXTERN bspBrush_t*		bspBrushes Q_ASSIGN(NULL);
 
 Q_EXTERN int				numBSPBrushSides Q_ASSIGN( 0 );
-Q_EXTERN bspBrushSide_t		bspBrushSides[ MAX_MAP_BRUSHSIDES ];
+Q_EXTERN int				allocatedBSPBrushSides Q_ASSIGN( 0 );
+Q_EXTERN bspBrushSide_t*	bspBrushSides Q_ASSIGN(NULL);
 
 Q_EXTERN int				numBSPLightBytes Q_ASSIGN( 0 );
 Q_EXTERN byte				*bspLightBytes Q_ASSIGN( NULL );
@@ -2362,6 +2514,27 @@ Q_EXTERN bspFog_t			bspFogs[ MAX_MAP_FOGS ];
 Q_EXTERN int				numBSPAds Q_ASSIGN( 0 );
 Q_EXTERN bspAdvertisement_t	bspAds[ MAX_MAP_ADVERTISEMENTS ];
 
+#define AUTOEXPAND_BY_REALLOC(ptr, reqitem, allocated, def) \
+	do \
+	{ \
+		if(reqitem >= allocated) \
+		{ \
+			if(allocated == 0) \
+				allocated = def; \
+			while(reqitem >= allocated && allocated) \
+				allocated *= 2; \
+			if(!allocated || allocated > 2147483647 / (int)sizeof(*ptr)) \
+			{ \
+				Error(#ptr " over 2 GB"); \
+			} \
+			ptr = realloc(ptr, sizeof(*ptr) * allocated); \
+			if(!ptr) \
+				Error(#ptr " out of memory"); \
+		} \
+	} \
+	while(0)
+
+#define AUTOEXPAND_BY_REALLOC_BSP(suffix, def) AUTOEXPAND_BY_REALLOC(bsp##suffix, numBSP##suffix, allocatedBSP##suffix, def)
 
 /* end marker */
 #endif
diff --git a/tools/quake3/q3map2/q3map2.rc b/tools/quake3/q3map2/q3map2.rc
index 78cf7b3a..bc120375 100644
--- a/tools/quake3/q3map2/q3map2.rc
+++ b/tools/quake3/q3map2/q3map2.rc
@@ -1 +1 @@
-101                     ICON    DISCARDABLE     "q3map2.ico"
+101                     ICON    DISCARDABLE     "q3map2.ico"
diff --git a/tools/quake3/q3map2/shaders.c b/tools/quake3/q3map2/shaders.c
index 321a527b..3bf6cbe1 100644
--- a/tools/quake3/q3map2/shaders.c
+++ b/tools/quake3/q3map2/shaders.c
@@ -98,21 +98,47 @@ void ColorMod( colorMod_t *cm, int numVerts, bspDrawVert_t *drawVerts )
 					VectorSet( mult, c, c, c );
 					break;
 				
+				case CM_COLOR_DOT_PRODUCT_SCALE:
+					c = DotProduct( dv->normal, cm2->data );
+					c = (c - cm2->data[3]) / (cm2->data[4] - cm2->data[3]);
+					VectorSet( mult, c, c, c );
+					break;
+				
 				case CM_ALPHA_DOT_PRODUCT:
 					mult[ 3 ] = DotProduct( dv->normal, cm2->data );
 					break;
 				
+				case CM_ALPHA_DOT_PRODUCT_SCALE:
+					c = DotProduct( dv->normal, cm2->data );
+					c = (c - cm2->data[3]) / (cm2->data[4] - cm2->data[3]);
+					mult[ 3 ] = c;
+					break;
+				
 				case CM_COLOR_DOT_PRODUCT_2:
 					c = DotProduct( dv->normal, cm2->data );
 					c *= c;
 					VectorSet( mult, c, c, c );
 					break;
 				
+				case CM_COLOR_DOT_PRODUCT_2_SCALE:
+					c = DotProduct( dv->normal, cm2->data );
+					c *= c;
+					c = (c - cm2->data[3]) / (cm2->data[4] - cm2->data[3]);
+					VectorSet( mult, c, c, c );
+					break;
+				
 				case CM_ALPHA_DOT_PRODUCT_2:
 					mult[ 3 ] = DotProduct( dv->normal, cm2->data );
 					mult[ 3 ] *= mult[ 3 ];
 					break;
 				
+				case CM_ALPHA_DOT_PRODUCT_2_SCALE:
+					c = DotProduct( dv->normal, cm2->data );
+					c *= c;
+					c = (c - cm2->data[3]) / (cm2->data[4] - cm2->data[3]);
+					mult[ 3 ] = c;
+					break;
+				
 				default:
 					break;
 			}
@@ -408,7 +434,6 @@ shaderInfo_t *CustomShader( shaderInfo_t *si, char *find, char *replace )
 	char			shader[ MAX_QPATH ];
 	char			*s;
 	int				loc;
-	md5_state_t		mh;
 	byte			digest[ 16 ];
 	char			*srcShaderText, temp[ 8192 ], shaderText[ 8192 ];	/* ydnar: fixme (make this bigger?) */
 	
@@ -529,10 +554,8 @@ shaderInfo_t *CustomShader( shaderInfo_t *si, char *find, char *replace )
 		strcat( shaderText, &srcShaderText[ loc + strlen( find ) ] );
 	}
 	
-	/* make md5 hash of the shader text */
-	md5_init( &mh );
-	md5_append( &mh, shaderText, strlen( shaderText ) );
-	md5_finish( &mh, digest );
+	/* make md4 hash of the shader text */
+	Com_BlockFullChecksum(shaderText, strlen(shaderText), digest);
 	
 	/* mangle hash into a shader name */
 	sprintf( shader, "%s/%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X", mapName,
@@ -568,7 +591,6 @@ adds a vertexremapshader key/value pair to worldspawn
 
 void EmitVertexRemapShader( char *from, char *to )
 {
-	md5_state_t		mh;
 	byte			digest[ 16 ];
 	char			key[ 64 ], value[ 256 ];
 	
@@ -581,10 +603,8 @@ void EmitVertexRemapShader( char *from, char *to )
 	/* build value */
 	sprintf( value, "%s;%s", from, to );
 	
-	/* make md5 hash */
-	md5_init( &mh );
-	md5_append( &mh, value, strlen( value ) );
-	md5_finish( &mh, digest );
+	/* make md4 hash */
+	Com_BlockFullChecksum(value, strlen(value), digest);
 
 	/* make key (this is annoying, as vertexremapshader is precisely 17 characters,
 	   which is one too long, so we leave off the last byte of the md5 digest) */
@@ -790,8 +810,14 @@ static void LoadShaderImages( shaderInfo_t *si )
 	}
 	
 	if( VectorLength( si->color ) <= 0.0f )
+	{
 		ColorNormalize( color, si->color );
-	VectorScale( color, (1.0f / count), si->averageColor );
+		VectorScale( color, (1.0f / count), si->averageColor );
+	}
+	else
+	{
+		VectorCopy( si->color, si->averageColor );
+	}
 }
 
 
@@ -801,13 +827,15 @@ ShaderInfoForShader()
 finds a shaderinfo for a named shader
 */
 
+#define MAX_SHADER_DEPRECATION_DEPTH 16
+
 shaderInfo_t *ShaderInfoForShader( const char *shaderName )
 {
 	int				i;
+	int				deprecationDepth;
 	shaderInfo_t	*si;
 	char			shader[ MAX_QPATH ];
-	
-	
+
 	/* dummy check */
 	if( shaderName == NULL || shaderName[ 0 ] == '\0' )
 	{
@@ -820,11 +848,27 @@ shaderInfo_t *ShaderInfoForShader( const char *shaderName )
 	StripExtension( shader );
 	
 	/* search for it */
+	deprecationDepth = 0;
 	for( i = 0; i < numShaderInfo; i++ )
 	{
 		si = &shaderInfo[ i ];
 		if( !Q_stricmp( shader, si->shader ) )
 		{
+			/* check if shader is deprecated */
+			if (deprecationDepth < MAX_SHADER_DEPRECATION_DEPTH && si->deprecateShader && si->deprecateShader[ 0 ] )
+			{
+				/* override name */
+				strcpy( shader, si->deprecateShader );
+				StripExtension( shader );
+				/* increase deprecation depth */
+				deprecationDepth++;
+				if (deprecationDepth == MAX_SHADER_DEPRECATION_DEPTH)
+					Sys_Printf("WARNING: Max deprecation depth of %i is reached on shader '%s'\n", MAX_SHADER_DEPRECATION_DEPTH, shader);
+				/* search again from beginning */
+				i = -1;
+				continue;
+			}
+
 			/* load image if necessary */
 			if( si->finished == qfalse )
 			{
@@ -1337,7 +1381,6 @@ static void ParseShaderFile( const char *filename )
 				{
 					surfaceModel_t	*model;
 					
-					
 					/* allocate new model and attach it */
 					model = safe_malloc( sizeof( *model ) );
 					memset( model, 0, sizeof( *model ) );
@@ -1462,6 +1505,30 @@ static void ParseShaderFile( const char *filename )
 					GetTokenAppend( shaderText, qfalse );
 					si->backsplashDistance = atof( token );
 				}
+
+				/* q3map_floodLight <r> <g> <b> <diste> <intensity> <light_direction_power> */
+				else if( !Q_stricmp( token, "q3map_floodLight" ) )
+				{
+					/* get color */
+					GetTokenAppend( shaderText, qfalse );
+					si->floodlightRGB[ 0 ] = atof( token );
+					GetTokenAppend( shaderText, qfalse );
+					si->floodlightRGB[ 1 ] = atof( token );
+					GetTokenAppend( shaderText, qfalse );
+					si->floodlightRGB[ 2 ] = atof( token );
+					GetTokenAppend( shaderText, qfalse );
+					si->floodlightDistance = atof( token ); 
+					GetTokenAppend( shaderText, qfalse );
+					si->floodlightIntensity = atof( token ); 
+					GetTokenAppend( shaderText, qfalse );
+					si->floodlightDirectionScale = atof( token ); 
+				}
+
+				/* jal: q3map_nodirty : skip dirty */
+				else if( !Q_stricmp( token, "q3map_nodirty" ) )
+				{
+					si->noDirty = qtrue;
+				}
 				
 				/* q3map_lightmapSampleSize <value> */
 				else if( !Q_stricmp( token, "q3map_lightmapSampleSize" ) )
@@ -1470,7 +1537,7 @@ static void ParseShaderFile( const char *filename )
 					si->lightmapSampleSize = atoi( token );
 				}
 				
-				/* q3map_lightmapSampleSffset <value> */
+				/* q3map_lightmapSampleOffset <value> */
 				else if( !Q_stricmp( token, "q3map_lightmapSampleOffset" ) )
 				{
 					GetTokenAppend( shaderText, qfalse );
@@ -1587,6 +1654,18 @@ static void ParseShaderFile( const char *filename )
 						strcpy( si->remapShader, token );
 					}
 				}
+
+				/* q3map_deprecateShader <shader> */
+				else if( !Q_stricmp( token, "q3map_deprecateShader" ) )
+				{
+					GetTokenAppend( shaderText, qfalse );
+					if( token[ 0 ] != '\0' )
+					{
+
+						si->deprecateShader = safe_malloc( strlen( token ) + 1 );
+						strcpy( si->deprecateShader, token );
+					}
+				}
 				
 				/* ydnar: q3map_offset <value> */
 				else if( !Q_stricmp( token, "q3map_offset" ) )
@@ -1595,7 +1674,7 @@ static void ParseShaderFile( const char *filename )
 					si->offset = atof( token );
 				}
 				
-				/* ydnar: q3map_textureSize <width> <height> (substitute for q3map_lightimage derivation for terrain) */
+				/* ydnar: q3map_fur <numlayers> <offset> <fade> */
 				else if( !Q_stricmp( token, "q3map_fur" ) )
 				{
 					GetTokenAppend( shaderText, qfalse );
@@ -1746,6 +1825,13 @@ static void ParseShaderFile( const char *filename )
 						Parse1DMatrixAppend( shaderText, 3, cm->data );
 					}
 					
+					/* dotProductScale ( X Y Z MIN MAX ) */
+					else if( !Q_stricmp( token, "dotProductScale" ) )
+					{
+						cm->type = CM_COLOR_DOT_PRODUCT_SCALE + alpha;
+						Parse1DMatrixAppend( shaderText, 5, cm->data );
+					}
+					
 					/* dotProduct2 ( X Y Z ) */
 					else if( !Q_stricmp( token, "dotProduct2" ) )
 					{
@@ -1753,6 +1839,13 @@ static void ParseShaderFile( const char *filename )
 						Parse1DMatrixAppend( shaderText, 3, cm->data );
 					}
 					
+					/* dotProduct2scale ( X Y Z MIN MAX ) */
+					else if( !Q_stricmp( token, "dotProduct2scale" ) )
+					{
+						cm->type = CM_COLOR_DOT_PRODUCT_2_SCALE + alpha;
+						Parse1DMatrixAppend( shaderText, 5, cm->data );
+					}
+					
 					/* volume */
 					else if( !Q_stricmp( token, "volume" ) )
 					{
@@ -1893,12 +1986,14 @@ static void ParseShaderFile( const char *filename )
 					si->styleMarker = 2;
 				
 				/* ydnar: default to searching for q3map_<surfaceparm> */
-				else
+#if 0
+ 				else
 				{
-					//%	Sys_FPrintf( SYS_VRB, "Attempting to match %s with a known surfaceparm\n", token );
+					Sys_FPrintf( SYS_VRB, "Attempting to match %s with a known surfaceparm\n", token );
 					if( ApplySurfaceParm( &token[ 6 ], &si->contentFlags, &si->surfaceFlags, &si->compileFlags ) == qfalse )
-						;//%	Sys_Printf( "WARNING: Unknown q3map_* directive \"%s\"\n", token );
+						Sys_Printf( "WARNING: Unknown q3map_* directive \"%s\"\n", token );
 				}
+#endif
 			}
 			
 			
diff --git a/tools/quake3/q3map2/surface.c b/tools/quake3/q3map2/surface.c
index 5415ae10..ca4f5fa6 100644
--- a/tools/quake3/q3map2/surface.c
+++ b/tools/quake3/q3map2/surface.c
@@ -181,6 +181,7 @@ mapDrawSurface_t *MakeCelSurface( mapDrawSurface_t *src, shaderInfo_t *si )
 	/* do some fixups for celshading */
 	ds->planar = qfalse;
 	ds->planeNum = -1;
+	ds->celShader = NULL; /* don't cel shade cels :P */
 	
 	/* return the surface */
 	return ds;
@@ -290,7 +291,7 @@ deletes all empty or bad surfaces from the surface list
 void TidyEntitySurfaces( entity_t *e )
 {
 	int					i, j, deleted;
-	mapDrawSurface_t	*out, *in;
+	mapDrawSurface_t	*out, *in = NULL;
 	
 	
 	/* note it */
@@ -630,19 +631,26 @@ void ClassifySurfaces( int numSurfs, mapDrawSurface_t *ds )
 			//% 	Sys_Printf( "Failed to map axis %d onto patch\n", bestAxis );
 		}
 		
-		/* get lightmap sample size */
-		if( ds->sampleSize <= 0 )
+		/* calculate lightmap sample size */
+		if( ds->shaderInfo->lightmapSampleSize > 0 ) /* shader value overrides every other */
+			ds->sampleSize = ds->shaderInfo->lightmapSampleSize;
+		else if( ds->sampleSize <= 0 ) /* may contain the entity asigned value */
+			ds->sampleSize = sampleSize; /* otherwise use global default */
+
+		if( ds->lightmapScale > 0.0f ) /* apply surface lightmap scaling factor */
 		{
- 			ds->sampleSize = sampleSize;
- 			if( ds->shaderInfo->lightmapSampleSize )
- 				ds->sampleSize = ds->shaderInfo->lightmapSampleSize;
-			if( ds->lightmapScale > 0 )
-				ds->sampleSize *= ds->lightmapScale;
-			if( ds->sampleSize <= 0 )
-				ds->sampleSize = 1;
-			else if( ds->sampleSize > 16384 )	/* powers of 2 are preferred */
-				ds->sampleSize = 16384;
+			ds->sampleSize = ds->lightmapScale * (float)ds->sampleSize;
+			ds->lightmapScale = 0; /* applied */
 		}
+
+		if( ds->sampleSize < minSampleSize )
+			ds->sampleSize = minSampleSize;
+
+		if( ds->sampleSize < 1 )
+			ds->sampleSize = 1;
+
+		if( ds->sampleSize > 16384 ) /* powers of 2 are preferred */
+			ds->sampleSize = 16384;
 	}
 }
 
@@ -911,6 +919,7 @@ mapDrawSurface_t *DrawSurfaceForSide( entity_t *e, brush_t *b, side_t *s, windin
 	ds->mapBrush = b;
 	ds->sideRef = AllocSideRef( s, NULL );
 	ds->fogNum = -1;
+	ds->sampleSize = b->lightmapSampleSize;
 	ds->lightmapScale = b->lightmapScale;
 	ds->numVerts = w->numpoints;
 	ds->verts = safe_malloc( ds->numVerts * sizeof( *ds->verts ) );
@@ -986,6 +995,10 @@ mapDrawSurface_t *DrawSurfaceForSide( entity_t *e, brush_t *b, side_t *s, windin
 	
 	/* set cel shader */
 	ds->celShader = b->celShader;
+
+	/* set shade angle */
+	if( b->shadeAngleDegrees > 0.0f )
+		ds->shadeAngleDegrees = b->shadeAngleDegrees;
 	
 	/* ydnar: gs mods: moved st biasing elsewhere */
 	return ds;
@@ -1094,6 +1107,7 @@ mapDrawSurface_t *DrawSurfaceForMesh( entity_t *e, parseMesh_t *p, mesh_t *mesh
 	
 	ds->shaderInfo = si;
 	ds->mapMesh = p;
+	ds->sampleSize = p->lightmapSampleSize;
 	ds->lightmapScale = p->lightmapScale;	/* ydnar */
 	ds->patchWidth = mesh->width;
 	ds->patchHeight = mesh->height;
@@ -1193,7 +1207,7 @@ DrawSurfaceForFlare() - ydnar
 creates a flare draw surface
 */
 
-mapDrawSurface_t *DrawSurfaceForFlare( int entNum, vec3_t origin, vec3_t normal, vec3_t color, char *flareShader, int lightStyle )
+mapDrawSurface_t *DrawSurfaceForFlare( int entNum, vec3_t origin, vec3_t normal, vec3_t color, const char *flareShader, int lightStyle )
 {
 	mapDrawSurface_t	*ds;
 	
@@ -1951,6 +1965,51 @@ int FilterPointIntoTree_r( vec3_t point, mapDrawSurface_t *ds, node_t *node )
 	return AddReferenceToLeaf( ds, node );
 }
 
+/*
+FilterPointConvexHullIntoTree_r() - ydnar
+filters the convex hull of multiple points from a surface into the tree
+*/
+
+int FilterPointConvexHullIntoTree_r( vec3_t **points, int npoints, mapDrawSurface_t *ds, node_t *node )
+{
+	float			d, dmin, dmax;
+	plane_t			*plane;
+	int				refs = 0;
+	int				i;
+
+	if(!points)
+		return 0;
+	
+	/* is this a decision node? */
+	if( node->planenum != PLANENUM_LEAF )
+	{
+		/* classify the point in relation to the plane */
+		plane = &mapplanes[ node->planenum ];
+
+		dmin = dmax = DotProduct( *(points[0]), plane->normal ) - plane->dist;
+		for(i = 1; i < npoints; ++i)
+		{
+			d = DotProduct( *(points[i]), plane->normal ) - plane->dist;
+			if(d > dmax)
+				dmax = d;
+			if(d < dmin)
+				dmin = d;
+		}
+		
+		/* filter by this plane */
+		refs = 0;
+		if( dmax >= -ON_EPSILON )
+			refs += FilterPointConvexHullIntoTree_r( points, npoints, ds, node->children[ 0 ] );
+		if( dmin <= ON_EPSILON )
+			refs += FilterPointConvexHullIntoTree_r( points, npoints, ds, node->children[ 1 ] );
+		
+		/* return */
+		return refs;
+	}
+	
+	/* add a reference */
+	return AddReferenceToLeaf( ds, node );
+}
 
 
 /*
@@ -1978,14 +2037,25 @@ int FilterWindingIntoTree_r( winding_t *w, mapDrawSurface_t *ds, node_t *node )
 	{
 		/* 'fatten' the winding by the shader mins/maxs (parsed from vertexDeform move) */
 		/* note this winding is completely invalid (concave, nonplanar, etc) */
-		fat = AllocWinding( w->numpoints * 3 );
-		fat->numpoints = w->numpoints * 3;
+		fat = AllocWinding( w->numpoints * 3 + 3 );
+		fat->numpoints = w->numpoints * 3 + 3;
 		for( i = 0; i < w->numpoints; i++ )
 		{
 			VectorCopy( w->p[ i ], fat->p[ i ] );
-			VectorAdd( w->p[ i ], si->mins, fat->p[ i * 2 ] );
-			VectorAdd( w->p[ i ], si->maxs, fat->p[ i * 3 ] );
+			VectorAdd( w->p[ i ], si->mins, fat->p[ i + (w->numpoints+1) ] );
+			VectorAdd( w->p[ i ], si->maxs, fat->p[ i + (w->numpoints+1) * 2 ] );
 		}
+		VectorCopy( w->p[ 0 ], fat->p[ i ] );
+		VectorAdd( w->p[ 0 ], si->mins, fat->p[ i + w->numpoints ] );
+		VectorAdd( w->p[ 0 ], si->maxs, fat->p[ i + w->numpoints * 2 ] );
+
+		/*
+		 * note: this winding is STILL not suitable for ClipWindingEpsilon, and
+		 * also does not really fulfill the intention as it only contains
+		 * origin, +mins, +maxs, but thanks to the "closing" points I just
+		 * added to the three sub-windings, the fattening at least doesn't make
+		 * it worse
+		 */
 		
 		FreeWinding( w );
 		w = fat;
@@ -2077,48 +2147,24 @@ subdivides a patch into an approximate curve and filters it into the tree
 
 static int FilterPatchIntoTree( mapDrawSurface_t *ds, tree_t *tree )
 {
-	int					i, x, y, refs;
-	mesh_t				src, *mesh;
-	winding_t			*w;
+	int					x, y, refs;
 	
-	
-	/* subdivide the surface */
-	src.width = ds->patchWidth;
-	src.height = ds->patchHeight;
-	src.verts = ds->verts;
-	mesh = SubdivideMesh( src, FILTER_SUBDIVISION, 32 );
-	
-	
-	/* filter each quad into the tree (fixme: use new patch x-triangulation code?) */
-	refs = 0;
-	for( y = 0; y < (mesh->height - 1); y++ )
-	{
-		for( x = 0; x < (mesh->width - 1); x++ )
+	for(y = 0; y + 2 < ds->patchHeight; y += 2)
+		for(x = 0; x + 2 < ds->patchWidth; x += 2)
 		{
-			/* triangle 1 */
-			w = AllocWinding( 3 );
-			w->numpoints = 3;
-			VectorCopy( mesh->verts[ y * mesh->width + x ].xyz, w->p[ 0 ] );
-			VectorCopy( mesh->verts[ y * mesh->width + x + 1 ].xyz, w->p[ 1 ] );
-			VectorCopy( mesh->verts[ (y + 1) * mesh->width + x ].xyz, w->p[ 2 ] );
-			refs += FilterWindingIntoTree_r( w, ds, tree->headnode );
-			
-			/* triangle 2 */
-			w = AllocWinding( 3 );
-			w->numpoints = 3;
-			VectorCopy( mesh->verts[ y * mesh->width + x + 1 ].xyz, w->p[ 0 ] );
-			VectorCopy( mesh->verts[ (y + 1 ) * mesh->width + x + 1 ].xyz, w->p[ 1 ] );
-			VectorCopy( mesh->verts[ (y + 1 ) * mesh->width + x ].xyz, w->p[ 2 ] );
-			refs += FilterWindingIntoTree_r( w, ds, tree->headnode );
+			vec3_t *points[9];
+			points[0] = &ds->verts[(y+0) * ds->patchWidth + (x+0)].xyz;
+			points[1] = &ds->verts[(y+0) * ds->patchWidth + (x+1)].xyz;
+			points[2] = &ds->verts[(y+0) * ds->patchWidth + (x+2)].xyz;
+			points[3] = &ds->verts[(y+1) * ds->patchWidth + (x+0)].xyz;
+			points[4] = &ds->verts[(y+1) * ds->patchWidth + (x+1)].xyz;
+			points[5] = &ds->verts[(y+1) * ds->patchWidth + (x+2)].xyz;
+			points[6] = &ds->verts[(y+2) * ds->patchWidth + (x+0)].xyz;
+			points[7] = &ds->verts[(y+2) * ds->patchWidth + (x+1)].xyz;
+			points[8] = &ds->verts[(y+2) * ds->patchWidth + (x+2)].xyz;
+			refs += FilterPointConvexHullIntoTree_r(points, 9, ds, tree->headnode);
 		}
-	}
-	
-	/* use point filtering as well */
-	for( i = 0; i < (mesh->width * mesh->height); i++ )
-		refs += FilterPointIntoTree_r( mesh->verts[ i ].xyz, ds, tree->headnode );
-	
-	/* free the subdivided mesh and return */
-	FreeMesh( mesh );
+
 	return refs;
 }
 
@@ -2248,8 +2294,6 @@ void EmitDrawVerts( mapDrawSurface_t *ds, bspDrawSurface_t *out )
 	for( i = 0; i < ds->numVerts; i++ )
 	{
 		/* allocate a new vert */
-		if( numBSPDrawVerts == MAX_MAP_DRAW_VERTS )
-			Error( "MAX_MAP_DRAW_VERTS" );
 		IncDrawVerts();
 		dv = &bspDrawVerts[ numBSPDrawVerts - 1 ];
 		
@@ -2445,25 +2489,27 @@ void EmitFlareSurface( mapDrawSurface_t *ds )
 	numSurfacesByType[ ds->type ]++;
 }
 
-
-
 /*
 EmitPatchSurface()
 emits a bsp patch drawsurface
 */
 
-void EmitPatchSurface( mapDrawSurface_t *ds )
+void EmitPatchSurface( entity_t *e, mapDrawSurface_t *ds )
 {
 	int					i, j;
 	bspDrawSurface_t	*out;
 	int					surfaceFlags, contentFlags;
-	
+	int					forcePatchMeta;
+
+	/* vortex: _patchMeta support */
+	forcePatchMeta = IntForKey(e, "_patchMeta" );
+	if (!forcePatchMeta)
+		forcePatchMeta = IntForKey(e, "patchMeta" );
 	
 	/* invert the surface if necessary */
 	if( ds->backSide || ds->shaderInfo->invert )
 	{
 		bspDrawVert_t	*dv1, *dv2, temp;
-		
 
 		/* walk the verts, flip the normal */
 		for( i = 0; i < ds->numVerts; i++ )
@@ -2485,7 +2531,7 @@ void EmitPatchSurface( mapDrawSurface_t *ds )
 		/* invert facing */
 		VectorScale( ds->lightmapVecs[ 2 ], -1.0f, ds->lightmapVecs[ 2 ] );
 	}
-	
+
 	/* allocate a new surface */
 	if( numBSPDrawSurfaces == MAX_MAP_DRAW_SURFS )
 		Error( "MAX_MAP_DRAW_SURFS" );
@@ -2493,12 +2539,12 @@ void EmitPatchSurface( mapDrawSurface_t *ds )
 	ds->outputNum = numBSPDrawSurfaces;
 	numBSPDrawSurfaces++;
 	memset( out, 0, sizeof( *out ) );
-	
+
 	/* set it up */
 	out->surfaceType = MST_PATCH;
 	if( debugSurfaces )
 		out->shaderNum = EmitShader( "debugsurfaces", NULL, NULL );
-	else if( patchMeta )
+	else if( patchMeta || forcePatchMeta )
 	{
 		/* patch meta requires that we have nodraw patches for collision */
 		surfaceFlags = ds->shaderInfo->surfaceFlags;
@@ -2548,8 +2594,6 @@ void EmitPatchSurface( mapDrawSurface_t *ds )
 	numSurfacesByType[ ds->type ]++;
 }
 
-
-
 /*
 OptimizeTriangleSurface() - ydnar
 optimizes the vertex/index data in a triangle surface
@@ -2673,12 +2717,11 @@ EmitTriangleSurface()
 creates a bsp drawsurface from arbitrary triangle surfaces
 */
 
-static void EmitTriangleSurface( mapDrawSurface_t *ds )
+void EmitTriangleSurface( mapDrawSurface_t *ds )
 {
 	int						i, temp;
 	bspDrawSurface_t		*out;
-	
-	
+
 	/* invert the surface if necessary */
 	if( ds->backSide || ds->shaderInfo->invert )
 	{
@@ -2689,15 +2732,15 @@ static void EmitTriangleSurface( mapDrawSurface_t *ds )
 			ds->indexes[ i ] = ds->indexes[ i + 1 ];
 			ds->indexes[ i + 1 ] = temp;
 		}
-		
+			
 		/* walk the verts, flip the normal */
 		for( i = 0; i < ds->numVerts; i++ )
 			VectorScale( ds->verts[ i ].normal, -1.0f, ds->verts[ i ].normal );
-		
+			
 		/* invert facing */
 		VectorScale( ds->lightmapVecs[ 2 ], -1.0f, ds->lightmapVecs[ 2 ] );
 	}
-	
+		
 	/* allocate a new surface */
 	if( numBSPDrawSurfaces == MAX_MAP_DRAW_SURFS )
 		Error( "MAX_MAP_DRAW_SURFS" );
@@ -2804,15 +2847,17 @@ EmitFaceSurface()
 emits a bsp planar winding (brush face) drawsurface
 */
 
-static void EmitFaceSurface( mapDrawSurface_t *ds )
+static void EmitFaceSurface(mapDrawSurface_t *ds )
 {
 	/* strip/fan finding was moved elsewhere */
-	StripFaceSurface( ds );
-	EmitTriangleSurface( ds );
+	if(maxAreaFaceSurface)
+		MaxAreaFaceSurface( ds );
+	else
+		StripFaceSurface( ds );
+	EmitTriangleSurface(ds);
 }
 
 
-
 /*
 MakeDebugPortalSurfs_r() - ydnar
 generates drawsurfaces for passable portals in the bsp
@@ -3128,7 +3173,7 @@ int AddSurfaceModelsToTriangle_r( mapDrawSurface_t *ds, surfaceModel_t *model, b
 			}
 			
 			/* insert the model */
-			InsertModel( (char *) model->model, 0, transform, NULL, ds->celShader, ds->entityNum, ds->castShadows, ds->recvShadows, 0, ds->lightmapScale );
+			InsertModel( (char *) model->model, 0, 0, transform, NULL, ds->celShader, ds->entityNum, ds->castShadows, ds->recvShadows, 0, ds->lightmapScale, 0, 0 );
 			
 			/* return to sender */
 			return 1;
@@ -3406,6 +3451,7 @@ void FilterDrawsurfsIntoTree( entity_t *e, tree_t *tree )
 	vec3_t				origin, mins, maxs;
 	int					refs;
 	int					numSurfs, numRefs, numSkyboxSurfaces;
+	qboolean	sb;
 	
 	
 	/* note it */
@@ -3424,15 +3470,18 @@ void FilterDrawsurfsIntoTree( entity_t *e, tree_t *tree )
 		
 		/* get shader */
 		si = ds->shaderInfo;
-		
+
 		/* ydnar: skybox surfaces are special */
 		if( ds->skybox )
 		{
 			refs = AddReferenceToTree_r( ds, tree->headnode, qtrue );
 			ds->skybox = qfalse;
+			sb = qtrue;
 		}
 		else
 		{
+			sb = qfalse;
+
 			/* refs initially zero */
 			refs = 0;
 			
@@ -3502,7 +3551,7 @@ void FilterDrawsurfsIntoTree( entity_t *e, tree_t *tree )
 				if( refs == 0 )
 					refs = FilterPatchIntoTree( ds, tree );
 				if( refs > 0 )
-					EmitPatchSurface( ds );
+					EmitPatchSurface( e, ds );
 				break;
 			
 			/* handle triangle surfaces */
@@ -3552,6 +3601,11 @@ void FilterDrawsurfsIntoTree( entity_t *e, tree_t *tree )
 				refs = 0;
 				break;
 		}
+
+		/* maybe surface got marked as skybox again */
+		/* if we keep that flag, it will get scaled up AGAIN */
+		if(sb)
+			ds->skybox = qfalse;
 		
 		/* tot up the references */
 		if( refs > 0 )
@@ -3590,6 +3644,7 @@ void FilterDrawsurfsIntoTree( entity_t *e, tree_t *tree )
 	Sys_FPrintf( SYS_VRB, "%9d (%d) emitted drawsurfs\n", numSurfs, numBSPDrawSurfaces );
 	Sys_FPrintf( SYS_VRB, "%9d stripped face surfaces\n", numStripSurfaces );
 	Sys_FPrintf( SYS_VRB, "%9d fanned face surfaces\n", numFanSurfaces );
+	Sys_FPrintf( SYS_VRB, "%9d maxarea'd face surfaces\n", numMaxAreaSurfaces );
 	Sys_FPrintf( SYS_VRB, "%9d surface models generated\n", numSurfaceModels );
 	Sys_FPrintf( SYS_VRB, "%9d skybox surfaces generated\n", numSkyboxSurfaces );
 	for( i = 0; i < NUM_SURFACE_TYPES; i++ )
diff --git a/tools/quake3/q3map2/surface_extra.c b/tools/quake3/q3map2/surface_extra.c
index 440fd946..5cadc1d9 100644
--- a/tools/quake3/q3map2/surface_extra.c
+++ b/tools/quake3/q3map2/surface_extra.c
@@ -346,7 +346,7 @@ void LoadSurfaceExtraFile( const char *path )
 	}
 	
 	/* parse the file */
-	ParseFromMemory( buffer, size );
+	ParseFromMemory( (char *) buffer, size );
 	
 	/* tokenize it */
 	while( 1 )
diff --git a/tools/quake3/q3map2/surface_foliage.c b/tools/quake3/q3map2/surface_foliage.c
index e9a8ec71..cc5452ae 100644
--- a/tools/quake3/q3map2/surface_foliage.c
+++ b/tools/quake3/q3map2/surface_foliage.c
@@ -275,7 +275,7 @@ void Foliage( mapDrawSurface_t *src )
 		m4x4_scale_for_vec3( transform, scale );
 		
 		/* add the model to the bsp */
-		InsertModel( foliage->model, 0, transform, NULL, NULL, src->entityNum, src->castShadows, src->recvShadows, 0, src->lightmapScale );
+		InsertModel( foliage->model, 0, 0, transform, NULL, NULL, src->entityNum, src->castShadows, src->recvShadows, 0, src->lightmapScale, 0, 0 );
 		
 		/* walk each new surface */
 		for( i = oldNumMapDrawSurfs; i < numMapDrawSurfs; i++ )
diff --git a/tools/quake3/q3map2/surface_meta.c b/tools/quake3/q3map2/surface_meta.c
index 9191ac14..0e300dde 100644
--- a/tools/quake3/q3map2/surface_meta.c
+++ b/tools/quake3/q3map2/surface_meta.c
@@ -84,7 +84,7 @@ static int FindMetaVertex( bspDrawVert_t *src )
 {
 	int			i;
 	bspDrawVert_t	*v, *temp;
-	
+
 	
 	/* try to find an existing drawvert */
 	for( i = firstSearchMetaVert, v = &metaVerts[ i ]; i < numMetaVerts; i++, v++ )
@@ -288,6 +288,7 @@ static void SurfaceToMetaTriangles( mapDrawSurface_t *ds )
 			src.recvShadows = ds->recvShadows;
 			src.fogNum = ds->fogNum;
 			src.sampleSize = ds->sampleSize;
+			src.shadeAngleDegrees = ds->shadeAngleDegrees;
 			VectorCopy( ds->lightmapAxis, src.lightmapAxis );
 			
 			/* copy drawverts */
@@ -312,23 +313,41 @@ TriangulatePatchSurface()
 creates triangles from a patch
 */
 
-void TriangulatePatchSurface( mapDrawSurface_t *ds )
+void TriangulatePatchSurface( entity_t *e , mapDrawSurface_t *ds )
 {
 	int					iterations, x, y, pw[ 5 ], r;
 	mapDrawSurface_t	*dsNew;
 	mesh_t				src, *subdivided, *mesh;
-	
-	
+	int					forcePatchMeta;
+	int					patchQuality;
+	int					patchSubdivision;
+
+	/* vortex: _patchMeta, _patchQuality, _patchSubdivide support */
+	forcePatchMeta = IntForKey(e, "_patchMeta" );
+	if (!forcePatchMeta)
+		forcePatchMeta = IntForKey(e, "patchMeta" );
+	patchQuality = IntForKey(e, "_patchQuality" );
+	if (!patchQuality)
+		patchQuality = IntForKey(e, "patchQuality" );
+	if (!patchQuality)
+		patchQuality = 1.0;
+	patchSubdivision = IntForKey(e, "_patchSubdivide" );
+	if (!patchSubdivision)
+		patchSubdivision = IntForKey(e, "patchSubdivide" );
+
 	/* try to early out */
-	if( ds->numVerts == 0 || ds->type != SURFACE_PATCH || patchMeta == qfalse )
+	if(ds->numVerts == 0 || ds->type != SURFACE_PATCH || ( patchMeta == qfalse && !forcePatchMeta) )
 		return;
-	
 	/* make a mesh from the drawsurf */ 
 	src.width = ds->patchWidth;
 	src.height = ds->patchHeight;
 	src.verts = ds->verts;
 	//%	subdivided = SubdivideMesh( src, 8, 999 );
-	iterations = IterationsForCurve( ds->longestCurve, patchSubdivisions );
+	if (patchSubdivision)
+		iterations = IterationsForCurve( ds->longestCurve, patchSubdivision );
+	else
+		iterations = IterationsForCurve( ds->longestCurve, patchSubdivisions / patchQuality );
+
 	subdivided = SubdivideMesh2( src, iterations );	//%	ds->maxIterations
 	
 	/* fit it to the curve and remove colinear verts on rows/columns */
@@ -394,6 +413,222 @@ void TriangulatePatchSurface( mapDrawSurface_t *ds )
 	ClassifySurfaces( 1, ds );
 }
 
+#define TINY_AREA 1.0f
+#define MAXAREA_MAXTRIES 8
+int MaxAreaIndexes(bspDrawVert_t *vert, int cnt, int *indexes)
+{
+	int r, s, t, bestR = 0, bestS = 1, bestT = 2;
+	int i, j, try;
+	double A, bestA = -1, V, bestV = -1;
+	vec3_t ab, ac, bc, cross;
+	bspDrawVert_t *buf;
+	double shiftWidth;
+
+	if(cnt < 3)
+		return 0;
+
+	/* calculate total area */
+	A = 0;
+	for(i = 1; i+1 < cnt; ++i)
+	{
+		VectorSubtract(vert[i].xyz, vert[0].xyz, ab);
+		VectorSubtract(vert[i+1].xyz, vert[0].xyz, ac);
+		CrossProduct(ab, ac, cross);
+		A += VectorLength(cross);
+	}
+	V = 0;
+	for(i = 0; i < cnt; ++i)
+	{
+		VectorSubtract(vert[(i+1)%cnt].xyz, vert[i].xyz, ab);
+		V += VectorLength(ab);
+	}
+
+	/* calculate shift width from the area sensibly, assuming the polygon
+	 * fits about 25% of the screen in both dimensions
+	 * we assume 1280x1024
+	 * 1 pixel is then about sqrt(A) / (0.25 * screenwidth)
+	 * 8 pixels are then about sqrt(A) /  (0.25 * 1280) * 8
+	 * 8 pixels are then about sqrt(A) * 0.025
+	 * */
+	shiftWidth = sqrt(A) * 0.0125;
+	/*     3->1 6->2 12->3 ... */
+	if(A - ceil(log(cnt/1.5) / log(2)) * V * shiftWidth * 2 < 0)
+	{
+		/* printf("Small triangle detected (area %f, circumference %f), adjusting shiftWidth from %f to ", A, V, shiftWidth); */
+		shiftWidth = A / (ceil(log(cnt/1.5) / log(2)) * V * 2);
+		/* printf("%f\n", shiftWidth); */
+	}
+
+	/* find the triangle with highest area */
+	for(r = 0; r+2 < cnt; ++r)
+	for(s = r+1; s+1 < cnt; ++s)
+	for(t = s+1; t < cnt; ++t)
+	{
+		VectorSubtract(vert[s].xyz, vert[r].xyz, ab);
+		VectorSubtract(vert[t].xyz, vert[r].xyz, ac);
+		VectorSubtract(vert[t].xyz, vert[s].xyz, bc);
+		CrossProduct(ab, ac, cross);
+		A = VectorLength(cross);
+
+		V = A - (VectorLength(ab) - VectorLength(ac) - VectorLength(bc)) * shiftWidth;
+		/* value = A - circumference * shiftWidth, i.e. we back out by shiftWidth units from each side, to prevent too acute triangles */
+		/* this kind of simulates "number of shiftWidth*shiftWidth fragments in the triangle not touched by an edge" */
+
+		if(bestA < 0 || V > bestV)
+		{
+			bestA = A;
+			bestV = V;
+			bestR = r;
+			bestS = s;
+			bestT = t;
+		}
+	}
+
+	/*
+	if(bestV < 0)
+		printf("value was REALLY bad\n");
+	*/
+
+	for(try = 0; try < MAXAREA_MAXTRIES; ++try)
+	{
+		if(try)
+		{
+			bestR = rand() % cnt;
+			bestS = rand() % cnt;
+			bestT = rand() % cnt;
+			if(bestR == bestS || bestR == bestT || bestS == bestT)
+				continue;
+			// bubblesort inline
+			// abc acb bac bca cab cba
+			if(bestR > bestS)
+			{
+				j = bestR;
+				bestR = bestS;
+				bestS = j;
+			}
+			// abc acb abc bca acb bca
+			if(bestS > bestT)
+			{
+				j = bestS;
+				bestS = bestT;
+				bestT = j;
+			}
+			// abc abc abc bac abc bac
+			if(bestR > bestS)
+			{
+				j = bestR;
+				bestR = bestS;
+				bestS = j;
+			}
+			// abc abc abc abc abc abc
+
+			VectorSubtract(vert[bestS].xyz, vert[bestR].xyz, ab);
+			VectorSubtract(vert[bestT].xyz, vert[bestR].xyz, ac);
+			CrossProduct(ab, ac, cross);
+			bestA = VectorLength(cross);
+		}
+
+		if(bestA < TINY_AREA)
+			/* the biggest triangle is degenerate - then every other is too, and the other algorithms wouldn't generate anything useful either */
+			continue;
+
+		i = 0;
+		indexes[i++] = bestR;
+		indexes[i++] = bestS;
+		indexes[i++] = bestT;
+			/* uses 3 */
+
+		/* identify the other fragments */
+
+		/* full polygon without triangle (bestR,bestS,bestT) = three new polygons:
+		 * 1. bestR..bestS
+		 * 2. bestS..bestT
+		 * 3. bestT..bestR
+		 */
+
+		j = MaxAreaIndexes(vert + bestR, bestS - bestR + 1, indexes + i);
+		if(j < 0)
+			continue;
+		j += i;
+		for(; i < j; ++i)
+			indexes[i] += bestR;
+			/* uses 3*(bestS-bestR+1)-6 */
+		j = MaxAreaIndexes(vert + bestS, bestT - bestS + 1, indexes + i);
+		if(j < 0)
+			continue;
+		j += i;
+		for(; i < j; ++i)
+			indexes[i] += bestS;
+			/* uses 3*(bestT-bestS+1)-6 */
+
+		/* can'bestT recurse this one directly... therefore, buffering */
+		if(cnt + bestR - bestT + 1 >= 3)
+		{
+			buf = safe_malloc(sizeof(*vert) * (cnt + bestR - bestT + 1));
+			memcpy(buf, vert + bestT, sizeof(*vert) * (cnt - bestT));
+			memcpy(buf + (cnt - bestT), vert, sizeof(*vert) * (bestR + 1));
+			j = MaxAreaIndexes(buf, cnt + bestR - bestT + 1, indexes + i);
+			if(j < 0)
+			{
+				free(buf);
+				continue;
+			}
+			j += i;
+			for(; i < j; ++i)
+				indexes[i] = (indexes[i] + bestT) % cnt;
+				/* uses 3*(cnt+bestR-bestT+1)-6 */
+			free(buf);
+		}
+
+		/* together 3 + 3*(cnt+3) - 18 = 3*cnt-6 q.e.d. */
+		return i;
+	}
+
+	return -1;
+}
+
+/*
+MaxAreaFaceSurface() - divVerent
+creates a triangle list using max area indexes
+*/
+
+void MaxAreaFaceSurface(mapDrawSurface_t *ds)
+{
+	int n;
+	/* try to early out  */
+	if( !ds->numVerts || (ds->type != SURFACE_FACE && ds->type != SURFACE_DECAL) )
+		return;
+
+	/* is this a simple triangle? */
+	if( ds->numVerts == 3 )
+	{
+		ds->numIndexes = 3;
+		ds->indexes = safe_malloc( ds->numIndexes * sizeof( int ) );
+		VectorSet( ds->indexes, 0, 1, 2 );
+		numMaxAreaSurfaces++;
+		return;
+	}
+
+	/* do it! */
+	ds->numIndexes = 3 * ds->numVerts - 6;
+	ds->indexes = safe_malloc( ds->numIndexes * sizeof( int ) );
+	n = MaxAreaIndexes(ds->verts, ds->numVerts, ds->indexes);
+	if(n < 0)
+	{
+		/* whatever we do, it's degenerate */
+		free(ds->indexes);
+		ds->numIndexes = 0;
+		StripFaceSurface(ds);
+		return;
+	}
+	ds->numIndexes = n;
+
+	/* add to count */
+	numMaxAreaSurfaces++;
+
+	/* classify it */
+	ClassifySurfaces( 1, ds );
+}
 
 
 /*
@@ -536,6 +771,7 @@ void StripFaceSurface( mapDrawSurface_t *ds )
 			Error( "MAX_INDEXES exceeded for surface (%d > %d) (%d verts)", numIndexes, MAX_INDEXES, ds->numVerts );
 		
 		/* try all possible orderings of the points looking for a non-degenerate strip order */
+		ni = 0;
 		for( r = 0; r < ds->numVerts; r++ )
 		{
 			/* set rotation */
@@ -597,8 +833,24 @@ void StripFaceSurface( mapDrawSurface_t *ds )
 	/* classify it */
 	ClassifySurfaces( 1, ds );
 }
+ 
+ 
+/*
+EmitMetaStatictics
+vortex: prints meta statistics in general output
+*/
 
-
+void EmitMetaStats()
+{
+	Sys_Printf( "--- EmitMetaStats ---\n" );
+	Sys_Printf( "%9d total meta surfaces\n", numMetaSurfaces );
+	Sys_Printf( "%9d stripped surfaces\n", numStripSurfaces );
+	Sys_Printf( "%9d fanned surfaces\n", numFanSurfaces );
+	Sys_Printf( "%9d maxarea'd surfaces\n", numMaxAreaSurfaces );
+	Sys_Printf( "%9d patch meta surfaces\n", numPatchMetaSurfaces );
+	Sys_Printf( "%9d meta verts\n", numMetaVerts );
+	Sys_Printf( "%9d meta triangles\n", numMetaTriangles );
+}
 
 /*
 MakeEntityMetaTriangles()
@@ -647,17 +899,20 @@ void MakeEntityMetaTriangles( entity_t *e )
 		{
 			case SURFACE_FACE:
 			case SURFACE_DECAL:
-				StripFaceSurface( ds );
+				if(maxAreaFaceSurface)
+					MaxAreaFaceSurface( ds );
+				else
+					StripFaceSurface( ds );
 				SurfaceToMetaTriangles( ds );
 				break;
 			
 			case SURFACE_PATCH:
-				TriangulatePatchSurface( ds );
+				TriangulatePatchSurface(e, ds );
 				break;
 			
 			case SURFACE_TRIANGLES:
 				break;
-			
+		
 			case SURFACE_FORCED_META:
 			case SURFACE_META:
 				SurfaceToMetaTriangles( ds );
@@ -676,6 +931,7 @@ void MakeEntityMetaTriangles( entity_t *e )
 	Sys_FPrintf( SYS_VRB, "%9d total meta surfaces\n", numMetaSurfaces );
 	Sys_FPrintf( SYS_VRB, "%9d stripped surfaces\n", numStripSurfaces );
 	Sys_FPrintf( SYS_VRB, "%9d fanned surfaces\n", numFanSurfaces );
+	Sys_FPrintf( SYS_VRB, "%9d maxarea'd surfaces\n", numMaxAreaSurfaces );
 	Sys_FPrintf( SYS_VRB, "%9d patch meta surfaces\n", numPatchMetaSurfaces );
 	Sys_FPrintf( SYS_VRB, "%9d meta verts\n", numMetaVerts );
 	Sys_FPrintf( SYS_VRB, "%9d meta triangles\n", numMetaTriangles );
@@ -686,50 +942,6 @@ void MakeEntityMetaTriangles( entity_t *e )
 
 
 
-/*
-PointTriangleIntersect()
-assuming that all points lie in plane, determine if pt
-is inside the triangle abc
-code originally (c) 2001 softSurfer (www.softsurfer.com)
-*/
-
-#define MIN_OUTSIDE_EPSILON		-0.01f
-#define MAX_OUTSIDE_EPSILON		1.01f
-
-static qboolean PointTriangleIntersect( vec3_t pt, vec4_t plane, vec3_t a, vec3_t b, vec3_t c, vec3_t bary )
-{
-	vec3_t	u, v, w;
-	float	uu, uv, vv, wu, wv, d;
-	
-	
-	/* make vectors */
-	VectorSubtract( b, a, u );
-	VectorSubtract( c, a, v );
-	VectorSubtract( pt, a, w );
-	
-	/* more setup */
-	uu = DotProduct( u, u );
-	uv = DotProduct( u, v );
-	vv = DotProduct( v, v );
-	wu = DotProduct( w, u );
-	wv = DotProduct( w, v );
-	d = uv * uv - uu * vv;
-	
-	/* calculate barycentric coordinates */
-	bary[ 1 ] = (uv * wv - vv * wu) / d;
-	if( bary[ 1 ] < MIN_OUTSIDE_EPSILON || bary[ 1 ] > MAX_OUTSIDE_EPSILON )
-		return qfalse;
-	bary[ 2 ] = (uv * wv - uu * wv) / d;
-	if( bary[ 2 ] < MIN_OUTSIDE_EPSILON || bary[ 2 ] > MAX_OUTSIDE_EPSILON )
-		return qfalse;
-	bary[ 0 ] = 1.0f - (bary[ 1 ] + bary[ 2 ]);
-	
-	/* point is in triangle */
-	return qtrue;
-}
-
-
-
 /*
 CreateEdge()
 sets up an edge structure from a plane and 2 points that the edge ab falls lies in
@@ -835,10 +1047,6 @@ void FixMetaTJunctions( void )
 			/* get vert */
 			VectorCopy( metaVerts[ j ].xyz, pt );
 
-			/* debug code: darken verts */
-			if( i == 0 )
-				VectorSet( metaVerts[ j ].color[ 0 ], 8, 8, 8 );
-			
 			/* determine if point lies in the triangle's plane */
 			dist = DotProduct( pt, plane ) - plane[ 3 ];
 			if( fabs( dist ) > TJ_PLANE_EPSILON )
@@ -879,13 +1087,6 @@ void FixMetaTJunctions( void )
 				amount = dist / edges[ k ].length;
 				#endif
 				
-				/* debug code: brighten this point */
-				//%	metaVerts[ j ].color[ 0 ][ 0 ] += 5;
-				//%	metaVerts[ j ].color[ 0 ][ 1 ] += 4;
-				VectorSet( metaVerts[ tri->indexes[ k ] ].color[ 0 ], 255, 204, 0 );
-				VectorSet( metaVerts[ tri->indexes[ (k + 1) % 3 ] ].color[ 0 ], 255, 204, 0 );
-				
-
 				/* the edge opposite the zero-weighted vertex was hit, so use that as an amount */
 				a = &metaVerts[ tri->indexes[ k % 3 ] ];
 				b = &metaVerts[ tri->indexes[ (k + 1) % 3 ] ];
@@ -973,7 +1174,6 @@ void SmoothMetaTriangles( void )
 	int				indexes[ MAX_SAMPLES ];
 	vec3_t			votes[ MAX_SAMPLES ];
 	
-	
 	/* note it */
 	Sys_FPrintf( SYS_VRB, "--- SmoothMetaTriangles ---\n" );
 	
@@ -994,11 +1194,18 @@ void SmoothMetaTriangles( void )
 	   and set per-vertex smoothing angle */
 	for( i = 0, tri = &metaTriangles[ i ]; i < numMetaTriangles; i++, tri++ )
 	{
-		/* get shader for shade angle */
+		shadeAngle = defaultShadeAngle;
+
+		/* get shade angle from shader */
 		if( tri->si->shadeAngleDegrees > 0.0f )
 			shadeAngle = DEG2RAD( tri->si->shadeAngleDegrees );
-		else
+		/* get shade angle from entity */
+		else if( tri->shadeAngleDegrees > 0.0f )
+			shadeAngle = DEG2RAD( tri->shadeAngleDegrees );
+		
+		if( shadeAngle <= 0.0f ) 
 			shadeAngle = defaultShadeAngle;
+
 		if( shadeAngle > maxShadeAngle )
 			maxShadeAngle = shadeAngle;
 		
@@ -1186,8 +1393,9 @@ returns the score of the triangle added
 #define ST_SCORE2			(2 * (ST_SCORE))
 
 #define ADEQUATE_SCORE		((AXIS_MIN) + 1 * (VERT_SCORE))
-#define GOOD_SCORE			((AXIS_MIN) + 2 * (VERT_SCORE) + 4 * (ST_SCORE))
-#define PERFECT_SCORE		((AXIS_MIN) + + 3 * (VERT_SCORE) + (SURFACE_SCORE) + 4 * (ST_SCORE))
+#define GOOD_SCORE			((AXIS_MIN) + 2 * (VERT_SCORE)                   + 4 * (ST_SCORE))
+#define PERFECT_SCORE		((AXIS_MIN) + 3 * (VERT_SCORE) + (SURFACE_SCORE) + 4 * (ST_SCORE))
+//#define MAX_BBOX_DISTANCE   16
 
 static int AddMetaTriangleToSurface( mapDrawSurface_t *ds, metaTriangle_t *tri, qboolean testAdd )
 {
@@ -1224,6 +1432,32 @@ static int AddMetaTriangleToSurface( mapDrawSurface_t *ds, metaTriangle_t *tri,
 		if( tri->planeNum >= 0 && tri->planeNum != ds->planeNum )
 			return 0;
 	}
+
+#if MAX_BBOX_DISTANCE > 0
+	VectorCopy( ds->mins, mins );
+	VectorCopy( ds->maxs, maxs );
+	mins[0] -= MAX_BBOX_DISTANCE;
+	mins[1] -= MAX_BBOX_DISTANCE;
+	mins[2] -= MAX_BBOX_DISTANCE;
+	maxs[0] += MAX_BBOX_DISTANCE;
+	maxs[1] += MAX_BBOX_DISTANCE;
+	maxs[2] += MAX_BBOX_DISTANCE;
+#define CHECK_1D(mins, v, maxs) ((mins) <= (v) && (v) <= (maxs))
+#define CHECK_3D(mins, v, maxs) (CHECK_1D((mins)[0], (v)[0], (maxs)[0]) && CHECK_1D((mins)[1], (v)[1], (maxs)[1]) && CHECK_1D((mins)[2], (v)[2], (maxs)[2]))
+	VectorCopy(metaVerts[ tri->indexes[ 0 ] ].xyz, p);
+	if(!CHECK_3D(mins, p, maxs))
+	{
+		VectorCopy(metaVerts[ tri->indexes[ 1 ] ].xyz, p);
+		if(!CHECK_3D(mins, p, maxs))
+		{
+			VectorCopy(metaVerts[ tri->indexes[ 2 ] ].xyz, p);
+			if(!CHECK_3D(mins, p, maxs))
+				return 0;
+		}
+	}
+#undef CHECK_3D
+#undef CHECK_1D
+#endif
 	
 	/* set initial score */
 	score = tri->surfaceNum == ds->surfaceNum ? SURFACE_SCORE : 0;
@@ -1422,6 +1656,7 @@ static void MetaTrianglesToSurface( int numPossibles, metaTriangle_t *possibles,
 		ds->planeNum = seed->planeNum;
 		ds->fogNum = seed->fogNum;
 		ds->sampleSize = seed->sampleSize;
+		ds->shadeAngleDegrees = seed->shadeAngleDegrees;
 		ds->verts = verts;
 		ds->indexes = indexes;
 		VectorCopy( seed->lightmapAxis, ds->lightmapAxis );
@@ -1529,37 +1764,37 @@ static int CompareMetaTriangles( const void *a, const void *b )
 	
 	
 	/* shader first */
-	if( ((metaTriangle_t*) a)->si < ((metaTriangle_t*) b)->si )
+	if( ((const metaTriangle_t*) a)->si < ((const metaTriangle_t*) b)->si )
 		return 1;
-	else if( ((metaTriangle_t*) a)->si > ((metaTriangle_t*) b)->si )
+	else if( ((const metaTriangle_t*) a)->si > ((const metaTriangle_t*) b)->si )
 		return -1;
 	
 	/* then fog */
-	else if( ((metaTriangle_t*) a)->fogNum < ((metaTriangle_t*) b)->fogNum )
+	else if( ((const metaTriangle_t*) a)->fogNum < ((const metaTriangle_t*) b)->fogNum )
 		return 1;
-	else if( ((metaTriangle_t*) a)->fogNum > ((metaTriangle_t*) b)->fogNum )
+	else if( ((const metaTriangle_t*) a)->fogNum > ((const metaTriangle_t*) b)->fogNum )
 		return -1;
 	
 	/* then plane */
 	#if 0
-		else if( npDegrees == 0.0f && ((metaTriangle_t*) a)->si->nonplanar == qfalse &&
-			((metaTriangle_t*) a)->planeNum >= 0 && ((metaTriangle_t*) a)->planeNum >= 0 )
+		else if( npDegrees == 0.0f && ((const metaTriangle_t*) a)->si->nonplanar == qfalse &&
+			((const metaTriangle_t*) a)->planeNum >= 0 && ((const metaTriangle_t*) a)->planeNum >= 0 )
 		{
-			if( ((metaTriangle_t*) a)->plane[ 3 ] < ((metaTriangle_t*) b)->plane[ 3 ] )
+			if( ((const metaTriangle_t*) a)->plane[ 3 ] < ((const metaTriangle_t*) b)->plane[ 3 ] )
 				return 1;
-			else if( ((metaTriangle_t*) a)->plane[ 3 ] > ((metaTriangle_t*) b)->plane[ 3 ] )
+			else if( ((const metaTriangle_t*) a)->plane[ 3 ] > ((const metaTriangle_t*) b)->plane[ 3 ] )
 				return -1;
-			else if( ((metaTriangle_t*) a)->plane[ 0 ] < ((metaTriangle_t*) b)->plane[ 0 ] )
+			else if( ((const metaTriangle_t*) a)->plane[ 0 ] < ((const metaTriangle_t*) b)->plane[ 0 ] )
 				return 1;
-			else if( ((metaTriangle_t*) a)->plane[ 0 ] > ((metaTriangle_t*) b)->plane[ 0 ] )
+			else if( ((const metaTriangle_t*) a)->plane[ 0 ] > ((const metaTriangle_t*) b)->plane[ 0 ] )
 				return -1;
-			else if( ((metaTriangle_t*) a)->plane[ 1 ] < ((metaTriangle_t*) b)->plane[ 1 ] )
+			else if( ((const metaTriangle_t*) a)->plane[ 1 ] < ((const metaTriangle_t*) b)->plane[ 1 ] )
 				return 1;
-			else if( ((metaTriangle_t*) a)->plane[ 1 ] > ((metaTriangle_t*) b)->plane[ 1 ] )
+			else if( ((const metaTriangle_t*) a)->plane[ 1 ] > ((const metaTriangle_t*) b)->plane[ 1 ] )
 				return -1;
-			else if( ((metaTriangle_t*) a)->plane[ 2 ] < ((metaTriangle_t*) b)->plane[ 2 ] )
+			else if( ((const metaTriangle_t*) a)->plane[ 2 ] < ((const metaTriangle_t*) b)->plane[ 2 ] )
 				return 1;
-			else if( ((metaTriangle_t*) a)->plane[ 2 ] > ((metaTriangle_t*) b)->plane[ 2 ] )
+			else if( ((const metaTriangle_t*) a)->plane[ 2 ] > ((const metaTriangle_t*) b)->plane[ 2 ] )
 				return -1;
 		}
 	#endif
@@ -1571,8 +1806,8 @@ static int CompareMetaTriangles( const void *a, const void *b )
 	VectorSet( bMins, 999999, 999999, 999999 );
 	for( i = 0; i < 3; i++ )
 	{
-		av = ((metaTriangle_t*) a)->indexes[ i ];
-		bv = ((metaTriangle_t*) b)->indexes[ i ];
+		av = ((const metaTriangle_t*) a)->indexes[ i ];
+		bv = ((const metaTriangle_t*) b)->indexes[ i ];
 		for( j = 0; j < 3; j++ )
 		{
 			if( metaVerts[ av ].xyz[ j ] < aMins[ j ] )
diff --git a/tools/quake3/q3map2/tjunction.c b/tools/quake3/q3map2/tjunction.c
index d728c185..cde7ece4 100644
--- a/tools/quake3/q3map2/tjunction.c
+++ b/tools/quake3/q3map2/tjunction.c
@@ -55,7 +55,7 @@ typedef struct edgeLine_s {
 	vec3_t		origin;
 	vec3_t		dir;
 
-	edgePoint_t	chain;		// unused element of doubly linked list
+	edgePoint_t	*chain;		// unused element of doubly linked list
 } edgeLine_t;
 
 typedef struct {
@@ -63,14 +63,14 @@ typedef struct {
 	bspDrawVert_t	*dv[2];
 } originalEdge_t;
 
-#define	MAX_ORIGINAL_EDGES	0x20000
-originalEdge_t	originalEdges[MAX_ORIGINAL_EDGES];
+originalEdge_t	*originalEdges = NULL;
 int				numOriginalEdges;
+int				allocatedOriginalEdges = 0;
 
 
-#define	MAX_EDGE_LINES		0x10000
-edgeLine_t		edgeLines[MAX_EDGE_LINES];
+edgeLine_t		*edgeLines = NULL;
 int				numEdgeLines;
+int				allocatedEdgeLines = 0;
 
 int				c_degenerateEdges;
 int				c_addedVerts;
@@ -100,14 +100,14 @@ void InsertPointOnEdge( vec3_t v, edgeLine_t *e ) {
 	p->intercept = d;
 	VectorCopy( v, p->xyz );
 
-	if ( e->chain.next == &e->chain ) {
-		e->chain.next = e->chain.prev = p;
-		p->next = p->prev = &e->chain;
+	if ( e->chain->next == e->chain ) {
+		e->chain->next = e->chain->prev = p;
+		p->next = p->prev = e->chain;
 		return;
 	}
 
-	scan = e->chain.next;
-	for ( ; scan != &e->chain ; scan = scan->next ) {
+	scan = e->chain->next;
+	for ( ; scan != e->chain ; scan = scan->next ) {
 		d = p->intercept - scan->intercept;
 		if ( d > -LINE_POSITION_EPSILON && d < LINE_POSITION_EPSILON ) {
 			free( p );
@@ -153,9 +153,7 @@ int AddEdge( vec3_t v1, vec3_t v2, qboolean createNonAxial ) {
 
 	if ( !createNonAxial ) {
 		if ( fabs( dir[0] + dir[1] + dir[2] ) != 1.0 ) {
-			if ( numOriginalEdges == MAX_ORIGINAL_EDGES ) {
-				Error( "MAX_ORIGINAL_EDGES" );
-			}
+			AUTOEXPAND_BY_REALLOC(originalEdges, numOriginalEdges, allocatedOriginalEdges, 1024);
 			originalEdges[ numOriginalEdges ].dv[0] = (bspDrawVert_t *)v1;
 			originalEdges[ numOriginalEdges ].dv[1] = (bspDrawVert_t *)v2;
 			originalEdges[ numOriginalEdges ].length = d;
@@ -192,14 +190,13 @@ int AddEdge( vec3_t v1, vec3_t v2, qboolean createNonAxial ) {
 	}
 
 	// create a new edge
-	if ( numEdgeLines >= MAX_EDGE_LINES ) {
-		Error( "MAX_EDGE_LINES" );
-	}
+	AUTOEXPAND_BY_REALLOC(edgeLines, numEdgeLines, allocatedEdgeLines, 1024);
 
 	e = &edgeLines[ numEdgeLines ];
 	numEdgeLines++;
 
-	e->chain.next = e->chain.prev = &e->chain;
+	e->chain = safe_malloc( sizeof(edgePoint_t) );
+	e->chain->next = e->chain->prev = e->chain;
 
 	VectorCopy( v1, e->origin );
 	VectorCopy( dir, e->dir );
@@ -377,12 +374,12 @@ void FixSurfaceJunctions( mapDrawSurface_t *ds ) {
 
 
 		if ( start < end ) {
-			p = e->chain.next;
+			p = e->chain->next;
 		} else {
-			p = e->chain.prev;
+			p = e->chain->prev;
 		}
 
-		for (  ; p != &e->chain ;  ) {
+		for (  ; p != e->chain ;  ) {
 			if ( start < end ) {
 				if ( p->intercept > end - ON_EPSILON ) {
 					break;
@@ -519,7 +516,6 @@ int		c_broken = 0;
 
 qboolean FixBrokenSurface( mapDrawSurface_t *ds )
 {
-	qboolean	valid = qtrue;
 	bspDrawVert_t	*dv1, *dv2, avg;
 	int			i, j, k;
 	float		dist;
@@ -534,10 +530,6 @@ qboolean FixBrokenSurface( mapDrawSurface_t *ds )
 	/* check all verts */
 	for( i = 0; i < ds->numVerts; i++ )
 	{
-		/* don't remove points if winding is a triangle */
-		if( ds->numVerts == 3 )
-			return valid;
-		
 		/* get verts */
 		dv1 = &ds->verts[ i ];
 		dv2 = &ds->verts[ (i + 1) % ds->numVerts ];
@@ -547,7 +539,6 @@ qboolean FixBrokenSurface( mapDrawSurface_t *ds )
 		dist = VectorLength( avg.xyz );
 		if( dist < DEGENERATE_EPSILON )
 		{
-			valid = qfalse;
 			Sys_FPrintf( SYS_VRB, "WARNING: Degenerate T-junction edge found, fixing...\n" );
 
 			/* create an average drawvert */
@@ -581,13 +572,16 @@ qboolean FixBrokenSurface( mapDrawSurface_t *ds )
 				memcpy( dv2, dv1, sizeof( bspDrawVert_t ) );
 			}
 			ds->numVerts--;
+			
+			/* after welding, we have to consider the same vertex again, as it now has a new neighbor dv2 */
+			--i;
+
+			/* should ds->numVerts have become 0, then i is now -1. In the next iteration, the loop will abort. */
 		}
 	}
 	
 	/* one last check and return */
-	if( ds->numVerts < 3 )
-		valid = qfalse;
-	return valid;
+	return ds->numVerts >= 3;
 }
 
 
@@ -606,8 +600,8 @@ EdgeCompare
 int EdgeCompare( const void *elem1, const void *elem2 ) {
 	float	d1, d2;
 
-	d1 = ((originalEdge_t *)elem1)->length;
-	d2 = ((originalEdge_t *)elem2)->length;
+	d1 = ((const originalEdge_t *)elem1)->length;
+	d2 = ((const originalEdge_t *)elem2)->length;
 
 	if ( d1 < d2 ) {
 		return -1;
@@ -632,7 +626,7 @@ void FixTJunctions( entity_t *ent )
 	shaderInfo_t		*si;
 	int					axialEdgeLines;
 	originalEdge_t		*e;
-	
+	bspDrawVert_t	*dv;
 	
 	/* meta mode has its own t-junction code (currently not as good as this code) */
 	//%	if( meta )
@@ -683,7 +677,8 @@ void FixTJunctions( entity_t *ent )
 	// this gives the most accurate edge description
 	for ( i = 0 ; i < numOriginalEdges ; i++ ) {
 		e = &originalEdges[i];
-		e->dv[ 0 ]->lightmap[ 0 ][ 0 ] = AddEdge( e->dv[ 0 ]->xyz, e->dv[ 1 ]->xyz, qtrue );
+		dv = e->dv[0]; // e might change during AddEdge
+		dv->lightmap[ 0 ][ 0 ] = AddEdge( e->dv[ 0 ]->xyz, e->dv[ 1 ]->xyz, qtrue );
 	}
 
 	Sys_FPrintf( SYS_VRB, "%9d axial edge lines\n", axialEdgeLines );
diff --git a/tools/quake3/q3map2/vis.c b/tools/quake3/q3map2/vis.c
index 4ee843a0..b9291922 100644
--- a/tools/quake3/q3map2/vis.c
+++ b/tools/quake3/q3map2/vis.c
@@ -102,9 +102,9 @@ the earlier information.
 */
 int PComp (const void *a, const void *b)
 {
-	if ( (*(vportal_t **)a)->nummightsee == (*(vportal_t **)b)->nummightsee)
+	if ( (*(const vportal_t *const *)a)->nummightsee == (*(const vportal_t *const *)b)->nummightsee)
 		return 0;
-	if ( (*(vportal_t **)a)->nummightsee < (*(vportal_t **)b)->nummightsee)
+	if ( (*(const vportal_t *const *)a)->nummightsee < (*(const vportal_t *const *)b)->nummightsee)
 		return -1;
 	return 1;
 }
@@ -167,6 +167,7 @@ ClusterMerge
 Merges the portal visibility for a leaf
 ===============
 */
+static int clustersizehistogram[MAX_MAP_LEAFS] = {0};
 void ClusterMerge (int leafnum)
 {
 	leaf_t		*leaf;
@@ -212,9 +213,8 @@ void ClusterMerge (int leafnum)
 
 	numvis++;		// count the leaf itself
 
-	totalvis += numvis;
-
-	Sys_FPrintf (SYS_VRB,"cluster %4i : %4i visible\n", leafnum, numvis);
+	//Sys_FPrintf (SYS_VRB,"cluster %4i : %4i visible\n", leafnum, numvis);
+	++clustersizehistogram[numvis];
 
 	memcpy (bspVisBytes + VIS_HEADER_SIZE + leafnum*leafbytes, uncompressed, leafbytes);
 }
@@ -310,8 +310,9 @@ CalcVis
 */
 void CalcVis (void)
 {
-	int			i;
+	int			i, minvis, maxvis;
 	const char	*value;
+	double mu, sigma, totalvis, totalvis2;
 	
 	
 	/* ydnar: rr2do2's farplane code */
@@ -357,9 +358,34 @@ void CalcVis (void)
 	Sys_Printf("creating leaf vis...\n");
 	for (i=0 ; i<portalclusters ; i++)
 		ClusterMerge (i);
-
-  Sys_Printf( "Total visible clusters: %i\n", totalvis );
-  Sys_Printf( "Average clusters visible: %i\n", totalvis / portalclusters );
+	
+	totalvis = 0;
+	totalvis2 = 0;
+	minvis = -1;
+	maxvis = -1;
+	for(i = 0; i < MAX_MAP_LEAFS; ++i)
+		if(clustersizehistogram[i])
+		{
+			if(debugCluster)
+				Sys_FPrintf(SYS_VRB, "%4i clusters have exactly %4i visible clusters\n", clustersizehistogram[i], i);
+			/* cast is to prevent integer overflow */
+			totalvis  += ((double) i)                * ((double) clustersizehistogram[i]);
+			totalvis2 += ((double) i) * ((double) i) * ((double) clustersizehistogram[i]);
+
+			if(minvis < 0)
+				minvis = i;
+			maxvis = i;
+		}
+	
+	mu = totalvis / portalclusters;
+	sigma = sqrt(totalvis2 / portalclusters - mu * mu);
+	
+  Sys_Printf( "Total clusters: %i\n", portalclusters );
+  Sys_Printf( "Total visible clusters: %.0f\n", totalvis );
+  Sys_Printf( "Average clusters visible: %.2f (%.3f%%/total)\n", mu, mu / portalclusters * 100.0);
+  Sys_Printf( "  Standard deviation: %.2f (%.3f%%/total, %.3f%%/avg)\n", sigma, sigma / portalclusters * 100.0, sigma / mu * 100.0);
+  Sys_Printf( "  Minimum: %i (%.3f%%/total, %.3f%%/avg)\n", minvis, minvis / (double) portalclusters * 100.0, minvis / mu * 100.0);
+  Sys_Printf( "  Maximum: %i (%.3f%%/total, %.3f%%/avg)\n", maxvis, maxvis / (double) portalclusters * 100.0, maxvis / mu * 100.0);
 }
 
 /*
@@ -871,6 +897,9 @@ void LoadPortals (char *name)
 	Sys_Printf ("%6i portalclusters\n", portalclusters);
 	Sys_Printf ("%6i numportals\n", numportals);
 	Sys_Printf ("%6i numfaces\n", numfaces);
+
+	if(numportals > MAX_PORTALS)
+		Error("MAX_PORTALS");
 	
 	// these counts should take advantage of 64 bit systems automatically
 	leafbytes = ((portalclusters+63)&~63)>>3;
@@ -903,8 +932,8 @@ void LoadPortals (char *name)
 			Error ("LoadPortals: reading portal %i", i);
 		if (numpoints > MAX_POINTS_ON_WINDING)
 			Error ("LoadPortals: portal %i has too many points", i);
-		if ( (unsigned)leafnums[0] > portalclusters
-		|| (unsigned)leafnums[1] > portalclusters)
+		if (leafnums[0] > portalclusters
+		|| leafnums[1] > portalclusters)
 			Error ("LoadPortals: reading portal %i", i);
 		if (fscanf (f, "%i ", &hint) != 1)
 			Error ("LoadPortals: reading hint state");
@@ -925,7 +954,10 @@ void LoadPortals (char *name)
 			for (k=0 ; k<3 ; k++)
 				w->points[j][k] = v[k];
 		}
-		fscanf (f, "\n");
+		if(fscanf (f, "\n") != 0)
+		{
+			// silence gcc warning
+		}
 		
 		// calc plane
 		PlaneFromWinding (w, &plane);
@@ -996,7 +1028,10 @@ void LoadPortals (char *name)
 			for (k=0 ; k<3 ; k++)
 				w->points[j][k] = v[k];
 		}
-		fscanf (f, "\n");
+		if(fscanf (f, "\n") != 0)
+		{
+			// silence gcc warning
+		}
 		
 		// calc plane
 		PlaneFromWinding (w, &plane);
@@ -1046,6 +1081,9 @@ int VisMain (int argc, char **argv)
 		} else if (!strcmp(argv[i], "-merge")) {
 			Sys_Printf ("merge = true\n");
 			mergevis = qtrue;
+		} else if (!strcmp(argv[i], "-mergeportals")) {
+			Sys_Printf ("mergeportals = true\n");
+			mergevisportals = qtrue;
 		} else if (!strcmp(argv[i], "-nopassage")) {
 			Sys_Printf ("nopassage = true\n");
 			noPassageVis = qtrue;
@@ -1058,6 +1096,9 @@ int VisMain (int argc, char **argv)
 		} else if (!strcmp (argv[i],"-saveprt")) {
 			Sys_Printf ("saveprt = true\n");
 			saveprt = qtrue;
+		} else if( !strcmp( argv[ i ], "-v" ) ) {
+			debugCluster = qtrue;
+			Sys_Printf( "Extra verbous mode enabled\n" );
 		} else if (!strcmp (argv[i],"-tmpin")) {
 			strcpy (inbase, "/tmp");
 		} else if (!strcmp (argv[i],"-tmpout")) {
@@ -1074,7 +1115,9 @@ int VisMain (int argc, char **argv)
 		}
 		
 		else
+		{
 			Sys_Printf( "WARNING: Unknown option \"%s\"\n", argv[ i ] );
+		}
 	}
 
 	if( i != argc - 1 )
@@ -1105,11 +1148,15 @@ int VisMain (int argc, char **argv)
 	/* ydnar: for getting far plane */
 	ParseEntities();
 	
+	/* inject command line parameters */
+	InjectCommandLine(argv, 0, argc - 1);
+	UnparseEntities();
+	
 	if( mergevis )
-	{
 		MergeLeaves();
+
+	if( mergevis || mergevisportals )
 		MergeLeafPortals();
-	}
 	
 	CountActivePortals();
 	/* WritePortals( "maps/hints.prs" );*/
diff --git a/tools/quake3/q3map2/writebsp.c b/tools/quake3/q3map2/writebsp.c
index 4cdd3a99..2e6e67a0 100644
--- a/tools/quake3/q3map2/writebsp.c
+++ b/tools/quake3/q3map2/writebsp.c
@@ -66,13 +66,15 @@ int	EmitShader( const char *shader, int *contentFlags, int *surfaceFlags )
 		if( !Q_stricmp( shader, bspShaders[ i ].shader ) )
 			return i;
 	}
+
+	// i == numBSPShaders
 	
 	/* get shaderinfo */
 	si = ShaderInfoForShader( shader );
 	
 	/* emit a new shader */
-	if( i == MAX_MAP_SHADERS )
-		Error( "MAX_MAP_SHADERS" );
+	AUTOEXPAND_BY_REALLOC_BSP(Shaders, 1024);
+
 	numBSPShaders++;
 	strcpy( bspShaders[ i ].shader, shader );
 	bspShaders[ i ].surfaceFlags = si->surfaceFlags;
@@ -114,6 +116,7 @@ void EmitPlanes( void )
 	mp = mapplanes;
 	for( i = 0; i < nummapplanes; i++, mp++ )
 	{
+		AUTOEXPAND_BY_REALLOC_BSP(Planes, 1024);
 		bp = &bspPlanes[ numBSPPlanes ];
 		VectorCopy( mp->normal, bp->normal );
 		bp->dist = mp->dist;
@@ -165,8 +168,7 @@ void EmitLeaf( node_t *node )
 		//%	if( b->guard != 0xDEADBEEF )
 		//%		Sys_Printf( "Brush %6d: 0x%08X Guard: 0x%08X Next: 0x%08X Original: 0x%08X Sides: %d\n", b->brushNum, b, b, b->next, b->original, b->numsides );
 		
-		if( numBSPLeafBrushes >= MAX_MAP_LEAFBRUSHES )
-			Error( "MAX_MAP_LEAFBRUSHES" );
+		AUTOEXPAND_BY_REALLOC_BSP(LeafBrushes, 1024);
 		bspLeafBrushes[ numBSPLeafBrushes ] = b->original->outputNum;
 		numBSPLeafBrushes++;
 	}
@@ -181,8 +183,7 @@ void EmitLeaf( node_t *node )
 	leaf_p->firstBSPLeafSurface = numBSPLeafSurfaces;
 	for ( dsr = node->drawSurfReferences; dsr; dsr = dsr->nextRef )
 	{
-		if( numBSPLeafSurfaces >= MAX_MAP_LEAFFACES )
-			Error( "MAX_MAP_LEAFFACES" );
+		AUTOEXPAND_BY_REALLOC_BSP(LeafSurfaces, 1024);
 		bspLeafSurfaces[ numBSPLeafSurfaces ] = dsr->outputNum;
 		numBSPLeafSurfaces++;			
 	}
@@ -199,7 +200,7 @@ recursively emit the bsp nodes
 int EmitDrawNode_r( node_t *node )
 {
 	bspNode_t	*n;
-	int			i;
+	int			i, n0;
 	
 	
 	/* check for leafnode */
@@ -210,9 +211,9 @@ int EmitDrawNode_r( node_t *node )
 	}
 	
 	/* emit a node */
-	if( numBSPNodes == MAX_MAP_NODES )
-		Error( "MAX_MAP_NODES" );
-	n = &bspNodes[ numBSPNodes ];
+	AUTOEXPAND_BY_REALLOC_BSP(Nodes, 1024);
+	n0 = numBSPNodes;
+	n = &bspNodes[ n0 ];
 	numBSPNodes++;
 	
 	VectorCopy (node->mins, n->mins);
@@ -236,6 +237,8 @@ int EmitDrawNode_r( node_t *node )
 		{
 			n->children[i] = numBSPNodes;	
 			EmitDrawNode_r (node->children[i]);
+			// n may have become invalid here, so...
+			n = &bspNodes[ n0 ];
 		}
 	}
 
@@ -277,18 +280,19 @@ sets style keys for entity lights
 void SetLightStyles( void )
 {
 	int			i, j, style, numStyles;
-	qboolean	keepLights;
 	const char	*t;
 	entity_t	*e;
 	epair_t		*ep, *next;
 	char		value[ 10 ];
 	char		lightTargets[ MAX_SWITCHED_LIGHTS ][ 64 ];
 	int			lightStyles[ MAX_SWITCHED_LIGHTS ];
-	
-	
+
 	/* ydnar: determine if we keep lights in the bsp */
-	t = ValueForKey( &entities[ 0 ], "_keepLights" );
-	keepLights = (t[ 0 ] == '1') ? qtrue : qfalse;
+	if (KeyExists(&entities[ 0 ], "_keepLights") == qtrue)
+	{
+		t = ValueForKey( &entities[ 0 ], "_keepLights" );
+		keepLights = (t[ 0 ] == '1') ? qtrue : qfalse;
+	}
 	
 	/* any light that is controlled (has a targetname) must have a unique style number generated for it */
 	numStyles = 0;
@@ -395,7 +399,7 @@ EndBSPFile()
 finishes a new bsp and writes to disk
 */
 
-void EndBSPFile( void )
+void EndBSPFile(qboolean do_write)
 {
 	char	path[ 1024 ];
 	
@@ -407,13 +411,16 @@ void EndBSPFile( void )
 	numBSPEntities = numEntities;
 	UnparseEntities();
 	
-	/* write the surface extra file */
-	WriteSurfaceExtraFile( source );
-	
-	/* write the bsp */
-	sprintf( path, "%s.bsp", source );
-	Sys_Printf( "Writing %s\n", path );
-	WriteBSPFile( path );
+	if(do_write)
+	{
+		/* write the surface extra file */
+		WriteSurfaceExtraFile( source );
+
+		/* write the bsp */
+		sprintf( path, "%s.bsp", source );
+		Sys_Printf( "Writing %s\n", path );
+		WriteBSPFile( path );
+	}
 }
 
 
@@ -441,8 +448,7 @@ void EmitBrushes( brush_t *brushes, int *firstBrush, int *numBrushes )
 	for( b = brushes; b != NULL; b = b->next )
 	{
 		/* check limits */
-		if( numBSPBrushes == MAX_MAP_BRUSHES )
-			Error( "MAX_MAP_BRUSHES (%d)", numBSPBrushes );
+		AUTOEXPAND_BY_REALLOC_BSP(Brushes, 1024);
 		
 		/* get bsp brush */
 		b->outputNum = numBSPBrushes;
@@ -462,8 +468,7 @@ void EmitBrushes( brush_t *brushes, int *firstBrush, int *numBrushes )
 			b->sides[ j ].outputNum = -1;
 			
 			/* check count */
-			if( numBSPBrushSides == MAX_MAP_BRUSHSIDES )
-				Error( "MAX_MAP_BRUSHSIDES ");
+			AUTOEXPAND_BY_REALLOC_BSP(BrushSides, 1024);
 			
 			/* emit side */
 			b->sides[ j ].outputNum = numBSPBrushSides;
@@ -553,8 +558,7 @@ void BeginModel( void )
 	
 	
 	/* test limits */
-	if( numBSPModels == MAX_MAP_MODELS )
-		Error( "MAX_MAP_MODELS" );
+	AUTOEXPAND_BY_REALLOC_BSP(Models, 256);
 	
 	/* get model and entity */
 	mod = &bspModels[ numBSPModels ];