r3480 - in trunk/misc: . gtkradiant gtkradiant/singlepatches

DONOTREPLY at icculus.org DONOTREPLY at icculus.org
Mon Mar 3 06:30:11 EST 2008


Author: div0
Date: 2008-03-03 06:30:10 -0500 (Mon, 03 Mar 2008)
New Revision: 3480

Added:
   trunk/misc/gtkradiant/
   trunk/misc/gtkradiant/gtkradiant-nexuiz-patchset.diff
   trunk/misc/gtkradiant/mergepatches.sh
   trunk/misc/gtkradiant/singlepatches/
   trunk/misc/gtkradiant/singlepatches/both-UTpicomodelase.diff
   trunk/misc/gtkradiant/singlepatches/both-UTpicomodelnormals.diff
   trunk/misc/gtkradiant/singlepatches/both-obj.diff
   trunk/misc/gtkradiant/singlepatches/gtkradiant-modelnormals.diff
   trunk/misc/gtkradiant/singlepatches/gtkradiant-nexuizfixes.diff
   trunk/misc/gtkradiant/singlepatches/q3map2-UTbouncefix.diff
   trunk/misc/gtkradiant/singlepatches/q3map2-UTfloodlight.diff
   trunk/misc/gtkradiant/singlepatches/q3map2-UTlmexposure.diff
   trunk/misc/gtkradiant/singlepatches/q3map2-UTtrianglecheck.diff
   trunk/misc/gtkradiant/singlepatches/q3map2-decomptexcoords.diff
   trunk/misc/gtkradiant/singlepatches/q3map2-snapplane.diff
Log:
add GtkRadiant patch set


Added: trunk/misc/gtkradiant/gtkradiant-nexuiz-patchset.diff
===================================================================
--- trunk/misc/gtkradiant/gtkradiant-nexuiz-patchset.diff	                        (rev 0)
+++ trunk/misc/gtkradiant/gtkradiant-nexuiz-patchset.diff	2008-03-03 11:30:10 UTC (rev 3480)
@@ -0,0 +1,2417 @@
+NOTE: this patch set is autogenerated from the "singlepatches" subdirectory of nexuiz/trunk/misc.
+
+Do not commit changes to THIS!
+
+Always run
+	sh mergepatches.sh > gtkradiant-nexuiz-patchset.diff
+before committing new singlepatches!
+
+
+
+Index: libs/picomodel/pm_ase.c
+===================================================================
+--- libs/picomodel/pm_ase.c	(revision 191)
++++ libs/picomodel/pm_ase.c	(working copy)
+@@ -32,6 +32,7 @@
+ 
+ ----------------------------------------------------------------------------- */
+ 
++void Sys_Printf (const char *format, ...);
+ 
+ /* marker */
+ #define PM_ASE_C
+@@ -253,7 +254,6 @@
+ struct aseVertex_s
+ {
+ 	picoVec3_t xyz;
+-	picoVec3_t normal;
+ 	picoIndex_t id;
+ };
+ 
+@@ -276,6 +276,8 @@
+ 	picoIndex_t smoothingGroup;
+ 	picoIndex_t materialId;
+ 	picoIndex_t subMaterialId;
++   picoVec3_t  facenormal;
++   picoVec3_t  vertexnormal[3];
+ };
+ typedef aseFace_t* aseFacesIter_t;
+ 
+@@ -455,33 +457,157 @@
+ 
+ #endif
+ 
++static int VectorCompareExtn( picoVec3_t n1, picoVec3_t n2, float epsilon )
++{
++	int		i;
++	
++	
++	/* test */
++	for( i= 0; i < 3; i++ )
++		if( fabs( n1[ i ] - n2[ i ]) > epsilon )
++			return -1;
++	return 1;
++}
++
++#define CrossProductTemp(a,b,c)		((c)[0]=(a)[1]*(b)[2]-(a)[2]*(b)[1],(c)[1]=(a)[2]*(b)[0]-(a)[0]*(b)[2],(c)[2]=(a)[0]*(b)[1]-(a)[1]*(b)[0])
++
+ static void _ase_submit_triangles( picoModel_t* model , aseMaterial_t* materials , aseVertex_t* vertices, aseTexCoord_t* texcoords, aseColor_t* colors, aseFace_t* faces, int numFaces )
+ {
+-	aseFacesIter_t i = faces, end = faces + numFaces;
+-	for(; i != end; ++i)
++   
++   picoVec3_t accum;
++   int index;
++   int counter;
++   aseFacesIter_t i = faces, end = faces + numFaces;
++   counter=0;
++
++   //rebuild normals
++   for(i=faces; i != end; ++i)
++   {
++    
++      //&(*i).facenormal
++	   //vec3_t v1, v2;
++	   //VectorSubtract(va, vb, v1);
++	   //VectorSubtract(vc, vb, v2);
++      //CrossProduct(v1, v2, out);
++      
++      picoVec3_t a,b,c;
++      picoVec3_t v1,v2,v3;
++      int j;
++      counter++;
++      for (j=0;j<3;j++)
++      {
++         a[j]    =  vertices[(*i).indices[0]].xyz[j];
++         b[j]    =  vertices[(*i).indices[1]].xyz[j];
++         c[j]    =  vertices[(*i).indices[2]].xyz[j];
++      }
++      for (j=0;j<3;j++)
++      {
++         v1[j]=a[j]-b[j];
++         v2[j]=c[j]-b[j];
++      }
++      
++      CrossProductTemp(v1,v2,v3);
++      _pico_normalize_vec(v3);
++      (*i).facenormal[0]=v3[0];
++      (*i).facenormal[1]=v3[1];
++      (*i).facenormal[2]=v3[2];
++      
++      
++   }
++   
++   
++   //if (counter>0) Sys_Printf( "Rebuilding %d Normals\n", counter * 3 );
++   for(i=faces; i != end; ++i)
+ 	{
+-		/* look up the shader for the material/submaterial pair */
++      /* look up the shader for the material/submaterial pair */
+ 		aseSubMaterial_t* subMtl = _ase_get_submaterial_or_default( materials, (*i).materialId, (*i).subMaterialId );
+-		if( subMtl == NULL )
++
++      if( subMtl == NULL )
+ 		{
+ 			return;
+ 		}
+ 
+ 		{
+ 			picoVec3_t* xyz[3];
++         picoVec3_t *a[3];
+ 			picoVec3_t* normal[3];
+ 			picoVec2_t* st[3];
+ 			picoColor_t* color[3];
+ 			picoIndex_t smooth[3];
+-			int j;
+-			/* we pull the data from the vertex, color and texcoord arrays using the face index data */
+-			for ( j = 0 ; j < 3 ; j ++ )
++
++			int j,z;
++
++         
++   
++         /* we pull the data from the vertex, color and texcoord arrays using the face index data */
++         for ( j = 0 ; j < 3 ; j ++ )
+ 			{
+-				xyz[j]    = &vertices[(*i).indices[j]].xyz;
+-				normal[j] = &vertices[(*i).indices[j]].normal;
++            aseFacesIter_t q = faces;
++            aseFacesIter_t qend = faces + numFaces;
++
++            xyz[j]    = &vertices[(*i).indices[j]].xyz;
++            
++            // Use Face normal
++            normal[j] = &(*i).facenormal;
++             
++     
++            //Oooor we can use the smoothing group
++
++            //Slow method, but testing
++            //Find All faces that use this vertex, average their facenormals.
++            // skip where smoothgroups both equal 0, or don't have any shared bits (x & y)
++            index=(*i).indices[j];
++    
++//            accum[0]=0;
++  //          accum[1]=0;
++    //        accum[2]=0;
++            accum[0]=(*i).facenormal[0];
++            accum[1]=(*i).facenormal[1];
++            accum[2]=(*i).facenormal[2];
++            counter=1;
++           
++            
++            z=0;
++            for(; q != qend; ++q)
++            {
++               z++;
++               if (q==i)
++                  continue;
++               // if  ( (*q).indices[0]==index || (*q).indices[1]==index || (*q).indices[2]==index) 
++                a[0]=  &vertices[(*q).indices[0] ].xyz; 
++                a[1]=  &vertices[(*q).indices[1] ].xyz; 
++                a[2]=  &vertices[(*q).indices[2] ].xyz; 
++               
++               if ( VectorCompareExtn(*a[0],*xyz[j],0.01f)>0 ||
++                    VectorCompareExtn(*a[1],*xyz[j],0.01f)>0 ||
++                    VectorCompareExtn(*a[2],*xyz[j],0.01f)>0
++                  )
++               {
++                  if ( (*i).smoothingGroup==0 && (*q).smoothingGroup ==0 )
++                     continue;
++
++                  if ( (*i).smoothingGroup & (*q).smoothingGroup  )
++                  {
++                     accum[0]+=(*q).facenormal[0];
++                     accum[1]+=(*q).facenormal[1];
++                     accum[2]+=(*q).facenormal[2];
++                     
++                     counter++;
++             
++                  }
++               }
++            } 
++            _pico_normalize_vec(accum); 
++
++            (*i).vertexnormal[j][0]=accum[0];
++            (*i).vertexnormal[j][1]=accum[1];
++            (*i).vertexnormal[j][2]=accum[2];
++            normal[j]=&(*i).vertexnormal[j]; 
++                        
++
+ 				st[j]     = &texcoords[(*i).indices[j + 3]].texcoord;
+-	 	 	
+-				if( colors != NULL && (*i).indices[j + 6] >= 0 )
++	 	 	      
++          	if( colors != NULL && (*i).indices[j + 6] >= 0 )
+ 				{
+ 					color[j] = &colors[(*i).indices[j + 6]].color;
+ 				}
+@@ -490,30 +616,18 @@
+ 					color[j] = &white;
+ 				}
+ 
+-				smooth[j] = (vertices[(*i).indices[j]].id * (1 << 16)) + (*i).smoothingGroup; /* don't merge vertices */
++				smooth[j] = 0;//  (vertices[(*i).indices[j]].id * (1 << 16)) + (*i).smoothingGroup; /* don't merge vertices */
+ 				
+ 			}
+ 
+ 			/* submit the triangle to the model */
+ 			PicoAddTriangleToModel ( model , xyz , normal , 1 , st , 1 , color , subMtl->shader, smooth );
+ 		}
++
+ 	}
+ }
+ 
+-static void shadername_convert(char* shaderName)
+-{
+-  /* unix-style path separators */
+-  char* s = shaderName;
+-  for(; *s != '\0'; ++s)
+-  {
+-    if(*s == '\\')
+-    {
+-      *s = '/';
+-    }
+-  }
+-}
+ 
+-
+ /* _ase_load:
+  *  loads a 3dsmax ase model file.
+ */
+@@ -534,6 +648,9 @@
+ 	int numColorVertices = 0;
+ 	int numColorVertexFaces = 0;
+ 	int vertexId = 0;
++   int currentVertexFace=0;
++   int currentVertexIndex=0;
++   int counter=0;
+ 
+ 	aseMaterial_t* materials = NULL;
+ 
+@@ -610,10 +727,11 @@
+ 		}
+ 		else if (!_pico_stricmp(p->token,"*mesh_numvertex"))
+ 		{
+-			if (!_pico_parse_int( p, &numVertices) )
++  			if (!_pico_parse_int( p, &numVertices) )
+ 				_ase_error_return("Missing MESH_NUMVERTEX value");
+ 
+ 			vertices = _pico_calloc(numVertices, sizeof(aseVertex_t));
++         currentVertexIndex=0;   
+ 		}
+ 		else if (!_pico_stricmp(p->token,"*mesh_numfaces"))
+ 		{
+@@ -621,6 +739,7 @@
+ 				_ase_error_return("Missing MESH_NUMFACES value");
+ 
+ 			faces = _pico_calloc(numFaces, sizeof(aseFace_t));
++
+ 		}
+ 		else if (!_pico_stricmp(p->token,"*mesh_numtvertex"))
+ 		{
+@@ -685,7 +804,20 @@
+ 
+ 			vertices[index].id = vertexId++;
+ 		}
+-		/* model mesh vertex normal */
++		else if (!_pico_stricmp(p->token,"*mesh_facenormal"))
++		{
++		   //Grab the faceindex for the next vertex normals.
++         if( numVertices == 0 )
++				_ase_error_return("Vertex parse error (facenormals)");
++
++         if (!_pico_parse_int( p,&currentVertexFace ))
++				_ase_error_return("Vertex parse error");
++
++ 			if (!_pico_parse_vec( p,faces[currentVertexFace].facenormal ))
++				_ase_error_return("Vertex parse error");
++
++      }
++      /* model mesh vertex normal */
+ 		else if (!_pico_stricmp(p->token,"*mesh_vertexnormal"))
+ 		{
+ 			int			index;
+@@ -696,10 +828,25 @@
+ 			/* get vertex data (orig: index +y -x +z) */
+ 			if (!_pico_parse_int( p,&index ))
+ 				_ase_error_return("Vertex parse error");
+-			if (!_pico_parse_vec( p,vertices[index].normal ))
++
++         //^^ Index is 'wrong' in .ase models.  they reference the same vert index with multiple normals..
++         // I've tried, this is a lost cause.  Use the SG's
++         // 
++			/*
++         
++         if (!_pico_parse_vec( p,vertices[counter].normal ))
+ 				_ase_error_return("Vertex parse error");
++         vertices[counter].faceid=index;
++         counter++;
++         */
+ 		}
+ 		/* model mesh face */
++		else if (!_pico_stricmp(p->token,"*mesh_normals"))
++      {
++      //   counter=0; //part of the above vertex normals fix
++      }
++         
++      /* model mesh face */
+ 		else if (!_pico_stricmp(p->token,"*mesh_face"))
+ 		{
+ 			picoIndex_t indexes[3];
+@@ -736,8 +883,35 @@
+ 				}
+ 				if (!_pico_stricmp (p->token,"*MESH_SMOOTHING" ))
+ 				{
+-					_pico_parse_int ( p , &faces[index].smoothingGroup );
+-				}
++               int total=0;
++               char* point;
++               char* start;
++               _pico_parse(p,0);
++
++               point=p->token;
++               start=point;
++               faces[index].smoothingGroup=0;
++              
++               //Super dodgy comma delimited string parse
++               while (*point<'A') 
++               {
++                  if (*point<=32 || *point==',')
++                  {
++                     total=atoi(start);
++                     if (total!=0)
++                     {
++                        faces[index].smoothingGroup+=1<<total;
++                     }
++                     start=point+1;
++                  }
++                  
++                  point++;
++               }
++               
++               
++			      
++               
++            }
+ 				if (!_pico_stricmp (p->token,"*MESH_MTLID" ))
+ 				{
+ 					_pico_parse_int ( p , &faces[index].subMaterialId );
+@@ -755,19 +929,19 @@
+ 			int			index;
+ 
+ 			if( numVertices == 0 )
+-				_ase_error_return("Texture Vertex parse error");
++				_ase_error_return("Vertex parse error");
+ 
+ 			/* get uv vertex index */
+-			if (!_pico_parse_int( p,&index ) || index >= numTextureVertices)
+-				_ase_error_return("Texture vertex parse error");
++			if (!_pico_parse_int( p,&index ))
++				_ase_error_return("UV vertex parse error");
+ 
+ 			/* get uv vertex s */
+ 			if (!_pico_parse_float( p,&texcoords[index].texcoord[0] ))
+-				_ase_error_return("Texture vertex parse error");
++				_ase_error_return("UV vertex parse error");
+ 
+ 			/* get uv vertex t */
+ 			if (!_pico_parse_float( p,&texcoords[index].texcoord[1] ))
+-				_ase_error_return("Texture vertex parse error");
++				_ase_error_return("UV vertex parse error");
+ 			
+ 			/* ydnar: invert t */
+ 			texcoords[index].texcoord[ 1 ] = 1.0f - texcoords[index].texcoord[ 1 ];
+@@ -831,6 +1005,13 @@
+ 			
+ 			/* leave alpha alone since we don't get any data from the ASE format */
+ 			colors[index].color[3] = 255;
++
++         /* 27 hack, red as alpha */
++         colors[index].color[3]=colors[index].color[0];
++         colors[index].color[0]=255;
++         colors[index].color[1]=255;
++         colors[index].color[2]=255;
++
+ 		}
+ 		/* model color face */
+ 		else if (!_pico_stricmp(p->token,"*mesh_cface"))
+@@ -900,7 +1081,6 @@
+ 				{
+ 					/* set material name */
+ 					_pico_first_token( materialName );
+-          shadername_convert(materialName);
+ 					PicoSetShaderName( shader, materialName);
+ 
+ 					/* set shader's transparency */
+@@ -1085,7 +1265,6 @@
+ 				}
+ 
+ 				/* set material name */
+-        shadername_convert(materialName);
+ 				PicoSetShaderName( shader,materialName );
+ 
+ 				/* set shader's transparency */
+@@ -1115,8 +1294,18 @@
+           char* p = mapname;
+ 
+           /* convert to shader-name format */
+-          shadername_convert(mapname);
+           {
++            /* unix-style path separators */
++            char* s = mapname;
++            for(; *s != '\0'; ++s)
++            {
++              if(*s == '\\')
++              {
++                *s = '/';
++              }
++            }
++          }
++          {
+             /* remove extension */
+             char* last_period = strrchr(p, '.');
+             if(last_period != NULL)
+@@ -1125,14 +1314,32 @@
+             }
+           }
+ 
+-          /* find shader path */
++          /* find game root */
+           for(; *p != '\0'; ++p)
+           {
+-            if(_pico_strnicmp(p, "models/", 7) == 0 || _pico_strnicmp(p, "textures/", 9) == 0)
++            if(_pico_strnicmp(p, "quake", 5) == 0 || _pico_strnicmp(p, "doom", 4) == 0)
+             {
+               break;
+             }
+           }
++          /* root-relative */
++          for(; *p != '\0'; ++p)
++          {
++            if(*p == '/')
++            {
++              ++p;
++              break;
++            }
++          }
++          /* game-relative */
++          for(; *p != '\0'; ++p)
++          {
++            if(*p == '/')
++            {
++              ++p;
++              break;
++            }
++          }
+ 
+           if(*p != '\0')
+           {
+Index: libs/picomodel/picomodel.c
+===================================================================
+--- libs/picomodel/picomodel.c	(revision 191)
++++ libs/picomodel/picomodel.c	(working copy)
+@@ -295,10 +295,7 @@
+ 		model = PicoModuleLoadModel(module, fileName, buffer, bufSize, frameNum);
+ 	}
+ 	
+-  if(model != 0)
+-  {
+-	  _pico_free(buffer);
+-  }
++	_pico_free(buffer);
+ 
+ 	/* return */
+ 	return model;
+@@ -1573,6 +1570,7 @@
+ {
+ 	int		i, j;
+ 	
++//   Sys_Printf(" %f %f %f\n", normal[0] , normal[1] , normal[2] );
+ 	
+ 	/* dummy check */
+ 	if( surface == NULL || surface->numVertexes <= 0 )
+@@ -1861,13 +1859,10 @@
+ typedef picoVec3_t* picoNormalIter_t;
+ typedef picoIndex_t* picoIndexIter_t;
+ 
+-#define THE_CROSSPRODUCTS_OF_ANY_PAIR_OF_EDGES_OF_A_GIVEN_TRIANGLE_ARE_EQUAL 1
+-
+ void _pico_triangles_generate_weighted_normals(picoIndexIter_t first, picoIndexIter_t end, picoVec3_t* xyz, picoVec3_t* normals)
+ {
+ 	for(; first != end; first += 3)
+ 	{
+-#if (THE_CROSSPRODUCTS_OF_ANY_PAIR_OF_EDGES_OF_A_GIVEN_TRIANGLE_ARE_EQUAL)
+ 		picoVec3_t weightedNormal;
+ 		{
+ 			float* a = xyz[*(first + 0)];
+@@ -1878,24 +1873,11 @@
+ 			_pico_subtract_vec( c, a, ca );
+ 			_pico_cross_vec( ca, ba, weightedNormal );
+ 		}
+-#endif
+ 		{
+ 			int j = 0;
+ 			for(; j < 3; ++j)
+ 			{
+ 				float* normal = normals[*(first + j)];
+-#if (!THE_CROSSPRODUCTS_OF_ANY_PAIR_OF_EDGES_OF_A_GIVEN_TRIANGLE_ARE_EQUAL)
+-				picoVec3_t weightedNormal;
+-				{
+-					float* a = xyz[*(first + ((j + 0) % 3))];
+-					float* b = xyz[*(first + ((j + 1) % 3))];
+-					float* c = xyz[*(first + ((j + 2) % 3))];
+-					picoVec3_t ba, ca;
+-					_pico_subtract_vec( b, a, ba );
+-					_pico_subtract_vec( c, a, ca );
+-					_pico_cross_vec( ca, ba, weightedNormal );
+-				}
+-#endif
+ 				_pico_add_vec(weightedNormal, normal, normal);
+ 			}
+ 		}
+@@ -1941,7 +1923,8 @@
+ {
+ 	for(; first != last; ++first, ++generated)
+ 	{
+-		if(!_pico_normal_is_unit_length(*first) || !_pico_normal_within_tolerance(*first, *generated))
++      //27 - fix for badly generated normals thing.
++      //	if(!_pico_normal_is_unit_length(*first) || !_pico_normal_within_tolerance(*first, *generated))
+ 		{
+ 			_pico_copy_vec(*generated, *first);
+ 		}
+@@ -1954,10 +1937,11 @@
+ 
+ 	_pico_normals_zero(normals, normals + surface->numVertexes);
+ 
++   //Just build standard no sg normals for now
+ 	_pico_triangles_generate_weighted_normals(surface->index, surface->index + surface->numIndexes, surface->xyz, normals);
+ 	_pico_vertices_combine_shared_normals(surface->xyz, surface->smoothingGroup, normals, surface->numVertexes);
+ 
+-	_pico_normals_normalize(normals, normals + surface->numVertexes);
++	_pico_normals_normalize(normals, normals + surface->numVertexes); 
+ 
+ 	_pico_normals_assign_generated_normals(surface->normal, surface->normal + surface->numVertexes, normals);
+ 
+@@ -2261,7 +2245,7 @@
+ 		int newVertIndex = PicoGetSurfaceNumIndexes ( workSurface );
+ 
+ 		/* get the index of the vertex that we're going to store at newVertIndex */
+-		vertDataIndex = PicoFindSurfaceVertexNum ( workSurface , *xyz[i] , *normals[i] , numSTs , st[i] , numColors , colors[i], smoothingGroup[i]);
++      vertDataIndex = -1;// PicoFindSurfaceVertexNum ( workSurface , *xyz[i] , *normals[i] , numSTs , st[i] , numColors , colors[i], smoothingGroup[i]);
+ 
+ 		/* the vertex wasn't found, so create a new vertex in the pool from the data we have */
+ 		if ( vertDataIndex == -1 )
+@@ -2290,3 +2274,5 @@
+ 		PicoSetSurfaceIndex ( workSurface , newVertIndex , vertDataIndex );
+ 	}
+ }
++
++
+Index: libs/picomodel/pm_obj.c
+===================================================================
+--- libs/picomodel/pm_obj.c	(revision 193)
++++ libs/picomodel/pm_obj.c	(working copy)
+@@ -265,7 +265,7 @@
+ 		/* get next token in material file */
+ 		if (_pico_parse( p,1 ) == NULL)
+ 			break;
+-#if 0
++#if 1
+ 
+ 		/* skip empty lines */
+ 		if (p->token == NULL || !strlen( p->token ))
+@@ -307,6 +307,7 @@
+ 		else if (!_pico_stricmp(p->token,"map_kd"))
+ 		{
+ 			char *mapName;
++			picoShader_t *shader;
+ 
+ 			/* pointer to current shader must be valid */
+ 			if (curShader == NULL)
+@@ -321,6 +322,10 @@
+ 				_pico_printf( PICO_ERROR,"Missing material map name in MTL, line %d.",p->curLine);
+ 				_obj_mtl_error_return;
+ 			}
++			/* create a new pico shader */
++			shader = PicoNewShader( model );
++			if (shader == NULL)
++				_obj_mtl_error_return;
+ 			/* set shader map name */
+ 			PicoSetShaderMapName( shader,mapName );
+ 		}
+@@ -521,7 +526,7 @@
+ 	PicoSetModelFileName( model,fileName );
+ 
+ 	/* try loading the materials; we don't handle the result */
+-#if 0
++#if 1
+ 	_obj_mtl_load( model );
+ #endif
+ 
+@@ -830,6 +835,41 @@
+ 				curVertex += max;
+ 			}
+ 		}
++		else if (!_pico_stricmp(p->token,"usemtl"))
++		{
++			picoShader_t *shader;
++			char *name;
++
++			/* get material name */
++			name = _pico_parse( p,0 );
++
++			/* validate material name */
++			if (name == NULL || !strlen(name))
++			{
++				_pico_printf( PICO_ERROR,"Missing material name in OBJ, line %d.",p->curLine);
++			}
++			else
++			{
++				shader = PicoFindShader( model, name, 1 );
++				if (shader == NULL)
++				{
++					_pico_printf( PICO_ERROR,"Undefined material name in OBJ, line %d. Making a default shader.",p->curLine);
++
++					/* create a new pico shader */
++					shader = PicoNewShader( model );
++					if (shader != NULL)
++					{
++						PicoSetShaderName( shader,name );
++						PicoSetShaderMapName( shader,name );
++						PicoSetSurfaceShader( curSurface, shader );
++					}
++				}
++				else
++				{
++					PicoSetSurfaceShader( curSurface, shader );
++				}
++			}
++		}
+ 		/* skip unparsed rest of line and continue */
+ 		_pico_parse_skip_rest( p );
+ 	}
+Index: plugins/model/model.cpp
+===================================================================
+--- plugins/model/model.cpp	(revision 193)
++++ plugins/model/model.cpp	(working copy)
+@@ -123,14 +123,27 @@
+     }
+     glVertexPointer(3, GL_FLOAT, sizeof(ArbitraryMeshVertex), &m_vertices.data()->vertex);
+     glDrawElements(GL_TRIANGLES, GLsizei(m_indices.size()), RenderIndexTypeID, m_indices.data());
++
+ #if defined(_DEBUG)
++	GLfloat modelview[16];
++	glGetFloatv(GL_MODELVIEW_MATRIX, modelview); // I know this is slow as hell, but hey - we're in _DEBUG
++	Matrix4 modelview_inv(
++		modelview[0], modelview[1], modelview[2], modelview[3],
++		modelview[4], modelview[5], modelview[6], modelview[7],
++		modelview[8], modelview[9], modelview[10], modelview[11],
++		modelview[12], modelview[13], modelview[14], modelview[15]);
++	matrix4_full_invert(modelview_inv);
++	Matrix4 modelview_inv_transposed = matrix4_transposed(modelview_inv);
++
+     glBegin(GL_LINES);
+ 
+     for(Array<ArbitraryMeshVertex>::const_iterator i = m_vertices.begin(); i != m_vertices.end(); ++i)
+     {
+-      Vector3 normal = vector3_added(vertex3f_to_vector3((*i).vertex), vector3_scaled(normal3f_to_vector3((*i).normal), 8));
++	  Vector3 normal = normal3f_to_vector3((*i).normal);
++	  normal = matrix4_transformed_direction(modelview_inv, vector3_normalised(matrix4_transformed_direction(modelview_inv_transposed, normal))); // do some magic
++      Vector3 normalTransformed = vector3_added(vertex3f_to_vector3((*i).vertex), vector3_scaled(normal, 8));
+       glVertex3fv(vertex3f_to_array((*i).vertex));
+-      glVertex3fv(vector3_to_array(normal));
++      glVertex3fv(vector3_to_array(normalTransformed));
+     }
+     glEnd();
+ #endif
+Index: games/NexuizPack/games/nexuiz.game
+===================================================================
+--- games/NexuizPack/games/nexuiz.game	(revision 26)
++++ games/NexuizPack/games/nexuiz.game	(working copy)
+@@ -17,7 +17,7 @@
+   shaderpath="scripts"
+   archivetypes="pk3"
+   texturetypes="tga jpg png"
+-  modeltypes="md3 mdl md2 ase"
++  modeltypes="md3 mdl md2 ase obj"
+   maptypes="mapq3"
+   shaders="quake3"
+   entityclass="quake3"
+Index: tools/quake3/q3map2/shaders.c
+===================================================================
+--- tools/quake3/q3map2/shaders.c	(revision 191)
++++ tools/quake3/q3map2/shaders.c	(working copy)
+@@ -793,8 +793,14 @@
+ 	}
+ 	
+ 	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 );
++	}
+ }
+ 
+ 
+Index: tools/quake3/q3map2/light_ydnar.c
+===================================================================
+--- tools/quake3/q3map2/light_ydnar.c	(revision 191)
++++ tools/quake3/q3map2/light_ydnar.c	(working copy)
+@@ -1767,6 +1864,8 @@
+ 	float				tests[ 4 ][ 2 ] = { { 0.0f, 0 }, { 1, 0 }, { 0, 1 }, { 1, 1 } };
+ 	trace_t				trace;
+ 	float				stackLightLuxels[ STACK_LL_SIZE ];
++	vec3_t				flood;
++	float				*floodlight;
+ 	
+ 	
+ 	/* bail if this number exceeds the number of raw lightmaps */
+@@ -2223,6 +2332,78 @@
+ 	FreeTraceLights( &trace );
+ 	
+ 	/*	-----------------------------------------------------------------
++		floodlight pass
++		----------------------------------------------------------------- */
++
++	if( floodlighty )
++	{
++		/* 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 cluster */
++					cluster	= SUPER_CLUSTER( x, y );
++					//%	if( *cluster < 0 )
++					//%		continue;
++					
++					/* get particulars */
++					luxel = SUPER_LUXEL( lightmapNum, x, y );
++					floodlight = SUPER_FLOODLIGHT( x, y );
++					
++					flood[0]=floodlightRGB[0]*floodlightIntensity;
++					flood[1]=floodlightRGB[1]*floodlightIntensity;
++					flood[2]=floodlightRGB[2]*floodlightIntensity;
++					             
++					/* scale light value */
++					VectorScale( flood, *floodlight, flood );
++					luxel[0]+=flood[0];
++					luxel[1]+=flood[1];
++					luxel[2]+=flood[2];
++					
++					if (luxel[3]==0) luxel[3]=1;
++				}
++			}
++		}
++	}
++
++	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
+ 		----------------------------------------------------------------- */
+ 	
+@@ -3587,7 +3768,320 @@
+ 	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;
++	
++	/* 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;
++		
++		sscanf( value, "%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;
++		
++		floodlightDistance=v4;
++		floodlightIntensity=v5;
++    
++		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);
++}
++
++//27 - lighttracer style ambient occlusion light hack.
++//Kudos to the dirtmapping author for most of this source.
++void FloodLightRawLightmap( int rawLightmapNum )
++{
++	int					i, x, y, sx, sy, *cluster;
++	float				*origin, *normal, *floodlight, *floodlight2, average, samples;
++	rawLightmap_t		*lm;
++	surfaceInfo_t		*info;
++	trace_t				trace;
++	
++	/* bail if this number exceeds the number of raw lightmaps */
++	if( rawLightmapNum >= numRawLightmaps )
++		return;
++	
++	/* get lightmap */
++	lm = &rawLightmaps[ rawLightmapNum ];
++	
++	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 dirt */
++	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 dirt */
++			*floodlight = FloodLightForSample( &trace );
++		}
++	}
++	
++	/* testing no filtering */
++	return;
++	
++	/* 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;
++		}
++	}
++}
++
++/*
++FloodLightForSample()
++calculates floodlight value for a given sample
++once again, kudos to the dirtmapping coder
++*/
++float FloodLightForSample( trace_t *trace )
++{
++	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 )
++	{
++		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 );
++	}
++
++	/* iterate through ordered vectors */
++	for( i = 0; i < numFloodVectors; i++ )
++	{
++		if (floodlight_lowquality==qtrue)
++        {
++			if (rand()%10 != 0 ) continue;
++		}
++
++		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 )
++		{
++			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;
++}
++
+Index: tools/quake3/q3map2/light.c
+===================================================================
+--- tools/quake3/q3map2/light.c	(revision 191)
++++ tools/quake3/q3map2/light.c	(working copy)
+@@ -1378,6 +1378,56 @@
+ 			break;
+ 	}
+ 	
++	/////// Floodlighting for point //////////////////
++	//do our floodlight ambient occlusion loop, and add a single contribution based on the brightest dir
++	if (floodlighty)
++	{
++		int q;
++		float addSize,f;
++		vec3_t col,dir;
++		col[0]=col[1]=col[2]=floodlightIntensity;
++		dir[0]=dir[1]=0;
++		dir[2]=1;
++      
++		trace.testOcclusion = qtrue;
++		trace.forceSunlight = qfalse;
++		trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
++		trace.testAll = qtrue;     
++      
++		for (q=0;q<2;q++)
++		{
++			if (q==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);
++
++			contributions[ numCon ].color[0]=col[0]*f;
++			contributions[ numCon ].color[1]=col[1]*f;
++			contributions[ numCon ].color[2]=col[2]*f;
++
++			contributions[ numCon ].dir[0]=dir[0];
++			contributions[ numCon ].dir[1]=dir[1];
++			contributions[ numCon ].dir[2]=dir[2];
++
++			contributions[ numCon ].style = 0;
++			numCon++;		
++			/* push average direction around */
++			addSize = VectorLength( col );
++			VectorMA( gp->dir, addSize, dir, gp->dir );
++		}
++	}
++	/////////////////////
++
+ 	/* normalize to get primary light direction */
+ 	VectorNormalize( gp->dir, gp->dir );
+ 	
+@@ -1661,6 +1711,12 @@
+ 		RunThreadsOnIndividual( numRawLightmaps, qtrue, DirtyRawLightmap );
+ 	}
+ 	
++	/* floodlight them up */
++	if( floodlighty )
++	{
++		Sys_Printf( "--- FloodlightRawLightmap ---\n" );
++		RunThreadsOnIndividual( numRawLightmaps, qtrue, FloodLightRawLightmap );
++	}
+ 
+ 	/* ydnar: set up light envelopes */
+ 	SetupEnvelopes( qfalse, fast );
+@@ -1703,6 +1759,7 @@
+ 		/* flag bouncing */
+ 		bouncing = qtrue;
+ 		VectorClear( ambientColor );
++		floodlighty = false;
+ 		
+ 		/* generate diffuse lights */
+ 		RadFreeLights();
+@@ -2191,6 +2256,21 @@
+ 			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" ) )
+@@ -2279,6 +2359,7 @@
+ 	/* ydnar: set up optimization */
+ 	SetupBrushes();
+ 	SetupDirt();
++	SetupFloodLight();
+ 	SetupSurfaceLightmaps();
+ 	
+ 	/* initialize the surface facet tracing */
+Index: tools/quake3/q3map2/lightmaps_ydnar.c
+===================================================================
+--- tools/quake3/q3map2/lightmaps_ydnar.c	(revision 191)
++++ tools/quake3/q3map2/lightmaps_ydnar.c	(working copy)
+@@ -414,6 +414,12 @@
+ 		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 )
+Index: tools/quake3/q3map2/q3map2.h
+===================================================================
+--- tools/quake3/q3map2/q3map2.h	(revision 191)
++++ tools/quake3/q3map2/q3map2.h	(working copy)
+@@ -267,6 +267,7 @@
+ #define SUPER_NORMAL_SIZE		4
+ #define SUPER_DELUXEL_SIZE		3
+ #define BSP_DELUXEL_SIZE		3
++#define SUPER_FLOODLIGHT_SIZE	1
+ 
+ #define VERTEX_LUXEL( s, v )	(vertexLuxels[ s ] + ((v) * VERTEX_LUXEL_SIZE))
+ #define RAD_VERTEX_LUXEL( s, v )(radVertexLuxels[ s ] + ((v) * VERTEX_LUXEL_SIZE))
+@@ -279,6 +280,7 @@
+ #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) )	
+ 
+ 
+ 
+@@ -1392,6 +1395,7 @@
+ 	
+ 	float					*superDeluxels;	/* average light direction */
+ 	float					*bspDeluxels;
++	float					*superFloodLight; 
+ }
+ rawLightmap_t;
+ 
+@@ -1704,6 +1708,10 @@
+ float						DirtForSample( trace_t *trace );
+ void						DirtyRawLightmap( int num );
+ 
++void						SetupFloodLight();
++float						FloodLightForSample( trace_t *trace );
++void						FloodLightRawLightmap( int num );
++
+ void						IlluminateRawLightmap( int num );
+ void						IlluminateVertexes( int num );
+ 
+@@ -2098,6 +2106,13 @@
+ Q_EXTERN float				dirtScale Q_ASSIGN( 1.0f );
+ Q_EXTERN float				dirtGain Q_ASSIGN( 1.0f );
+ 
++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 );
++Q_EXTERN float				floodlightDistance Q_ASSIGN( 1024 );
++
+ Q_EXTERN qboolean			dump Q_ASSIGN( qfalse );
+ Q_EXTERN qboolean			debug Q_ASSIGN( qfalse );
+ Q_EXTERN qboolean			debugUnused Q_ASSIGN( qfalse );
+Index: tools/quake3/q3map2/game_ja.h
+===================================================================
+--- tools/quake3/q3map2/game_ja.h	(revision 191)
++++ tools/quake3/q3map2/game_ja.h	(working copy)
+@@ -67,6 +67,7 @@
+ 	qfalse,				/* wolf lighting model? */
+ 	128,				/* lightmap width/height */
+ 	1.0f,				/* lightmap gamma */
++	1.0f,				/* lightmap exposure */
+ 	1.0f,				/* lightmap compensate */
+ 	"RBSP",				/* bsp file prefix */
+ 	1,					/* bsp file version */
+Index: tools/quake3/q3map2/game_tremulous.h
+===================================================================
+--- tools/quake3/q3map2/game_tremulous.h	(revision 191)
++++ tools/quake3/q3map2/game_tremulous.h	(working copy)
+@@ -70,6 +70,7 @@
+ 	qfalse,				/* wolf lighting model? */
+ 	128,				/* lightmap width/height */
+ 	1.0f,				/* lightmap gamma */
++	1.0f,				/* lightmap exposure */
+ 	1.0f,				/* lightmap compensate */
+ 	"IBSP",				/* bsp file prefix */
+ 	46,					/* bsp file version */
+Index: tools/quake3/q3map2/game_wolfet.h
+===================================================================
+--- tools/quake3/q3map2/game_wolfet.h	(revision 191)
++++ tools/quake3/q3map2/game_wolfet.h	(working copy)
+@@ -66,6 +66,7 @@
+ 	qtrue,				/* wolf lighting model? */
+ 	128,				/* lightmap width/height */
+ 	1.0f,				/* lightmap gamma */
++	1.0f,				/* lightmap exposure */
+ 	1.0f,				/* lightmap compensate */
+ 	"IBSP",				/* bsp file prefix */
+ 	47,					/* bsp file version */
+Index: tools/quake3/q3map2/game_wolf.h
+===================================================================
+--- tools/quake3/q3map2/game_wolf.h	(revision 191)
++++ tools/quake3/q3map2/game_wolf.h	(working copy)
+@@ -129,6 +129,7 @@
+ 	qtrue,				/* wolf lighting model? */
+ 	128,				/* lightmap width/height */
+ 	1.0f,				/* lightmap gamma */
++	1.0f,				/* lightmap exposure */
+ 	1.0f,				/* lightmap compensate */
+ 	"IBSP",				/* bsp file prefix */
+ 	47,					/* bsp file version */
+Index: tools/quake3/q3map2/game_sof2.h
+===================================================================
+--- tools/quake3/q3map2/game_sof2.h	(revision 191)
++++ tools/quake3/q3map2/game_sof2.h	(working copy)
+@@ -139,6 +139,7 @@
+ 	qfalse,					/* wolf lighting model? */
+ 	128,					/* lightmap width/height */
+ 	1.0f,					/* lightmap gamma */
++	1.0f,					/* lightmap exposure */
+ 	1.0f,					/* lightmap compensate */
+ 	"RBSP",					/* bsp file prefix */
+ 	1,						/* bsp file version */
+Index: tools/quake3/q3map2/game_etut.h
+===================================================================
+--- tools/quake3/q3map2/game_etut.h	(revision 191)
++++ tools/quake3/q3map2/game_etut.h	(working copy)
+@@ -148,6 +148,7 @@
+ 	qfalse,				/* wolf lighting model? */
+ 	128,				/* lightmap width/height */
+ 	2.2f,				/* lightmap gamma */
++	1.0f,				/* lightmap exposure */
+ 	1.0f,				/* lightmap compensate */
+ 	"IBSP",				/* bsp file prefix */
+ 	47,					/* bsp file version */
+Index: tools/quake3/q3map2/game_jk2.h
+===================================================================
+--- tools/quake3/q3map2/game_jk2.h	(revision 191)
++++ tools/quake3/q3map2/game_jk2.h	(working copy)
+@@ -64,6 +64,7 @@
+ 	qfalse,				/* wolf lighting model? */
+ 	128,				/* lightmap width/height */
+ 	1.0f,				/* lightmap gamma */
++	1.0f,				/* lightmap exposure */
+ 	1.0f,				/* lightmap compensate */
+ 	"RBSP",				/* bsp file prefix */
+ 	1,					/* bsp file version */
+Index: tools/quake3/q3map2/game_qfusion.h
+===================================================================
+--- tools/quake3/q3map2/game_qfusion.h	(revision 191)
++++ tools/quake3/q3map2/game_qfusion.h	(working copy)
+@@ -115,6 +115,7 @@
+ 	qfalse,				/* wolf lighting model? */
+ 	512,				/* lightmap width/height */
+ 	1.0f,				/* lightmap gamma */
++	1.0f,				/* lightmap exposure */
+ 	1.0f,				/* lightmap compensate */
+ 	"FBSP",				/* bsp file prefix */
+ 	1,					/* bsp file version */
+Index: tools/quake3/q3map2/game_tenebrae.h
+===================================================================
+--- tools/quake3/q3map2/game_tenebrae.h	(revision 191)
++++ tools/quake3/q3map2/game_tenebrae.h	(working copy)
+@@ -112,6 +112,7 @@
+ 	qfalse,				/* wolf lighting model? */
+ 	512,				/* lightmap width/height */
+ 	2.0f,				/* lightmap gamma */
++	1.0f,				/* lightmap exposure */
+ 	1.0f,				/* lightmap compensate */
+ 	"IBSP",				/* bsp file prefix */
+ 	46,					/* bsp file version */
+Index: tools/quake3/q3map2/game_quake3.h
+===================================================================
+--- tools/quake3/q3map2/game_quake3.h	(revision 191)
++++ tools/quake3/q3map2/game_quake3.h	(working copy)
+@@ -112,6 +112,7 @@
+ 	qfalse,				/* wolf lighting model? */
+ 	128,				/* lightmap width/height */
+ 	1.0f,				/* lightmap gamma */
++	1.0f,				/* lightmap exposure */
+ 	1.0f,				/* lightmap compensate */
+ 	"IBSP",				/* bsp file prefix */
+ 	46,					/* bsp file version */
+Index: tools/quake3/q3map2/game_ef.h
+===================================================================
+--- tools/quake3/q3map2/game_ef.h	(revision 191)
++++ tools/quake3/q3map2/game_ef.h	(working copy)
+@@ -113,6 +113,7 @@
+ 	qfalse,				/* wolf lighting model? */
+ 	128,				/* lightmap width/height */
+ 	1.0f,				/* lightmap gamma */
++	1.0f,				/* lightmap exposure */
+ 	1.0f,				/* lightmap compensate */
+ 	"IBSP",				/* bsp file prefix */
+ 	46,					/* bsp file version */
+Index: tools/quake3/q3map2/light_ydnar.c
+===================================================================
+--- tools/quake3/q3map2/light_ydnar.c	(revision 191)
++++ tools/quake3/q3map2/light_ydnar.c	(working copy)
+@@ -49,6 +49,7 @@
+ 	int		i;
+ 	float	max, gamma;
+ 	vec3_t	sample;
++	float 	inv, dif;
+ 	
+ 	
+ 	/* ydnar: scaling necessary for simulating r_overbrightBits on external lightmaps */
+@@ -72,16 +73,51 @@
+ 		/* gamma */
+ 		sample[ i ] = pow( sample[ i ] / 255.0f, gamma ) * 255.0f;
+ 	}
++
++	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;
++		}
++	}
++
+ 	
+-	/* 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 );
+-	
+ 	/* compensate for ingame overbrighting/bitshifting */
+ 	VectorScale( sample, (1.0f / lightmapCompensate), sample );
+ 	
+Index: tools/quake3/q3map2/light.c
+===================================================================
+--- tools/quake3/q3map2/light.c (revision 191)
++++ tools/quake3/q3map2/light.c (working copy)
+@@ -1836,6 +1893,14 @@
+ 			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 ] );
+Index: tools/quake3/q3map2/q3map2.h
+===================================================================
+--- tools/quake3/q3map2/q3map2.h	(revision 191)
++++ tools/quake3/q3map2/q3map2.h	(working copy)
+@@ -543,6 +545,7 @@
+ 	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 */
+ 	char				*bspIdent;						/* 4-letter bsp file prefix */
+ 	int					bspVersion;						/* bsp version to use */
+@@ -2117,6 +2132,7 @@
+ 
+ /* 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 */
+Index: tools/quake3/q3map2/light_ydnar.c
+===================================================================
+--- tools/quake3/q3map2/light_ydnar.c	(revision 191)
++++ tools/quake3/q3map2/light_ydnar.c	(working copy)
+@@ -384,7 +420,7 @@
+ #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 @@
+ 	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 @@
+ 	/* 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)
++	{
++		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,13 @@
+ 	else
+ 		origin[ lm->axisNum ] += lightmapSampleOffset;
+ 	
++	VectorCopy(origin,origintwo);
++	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 +625,14 @@
+ 			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;
+ 		}
+ 	}
+@@ -550,8 +642,8 @@
+ 	{
+ 		VectorMA( dv->xyz, lightmapSampleOffset, dv->normal, nudged );
+ 		pointCluster = ClusterForPointExtFilter( nudged, LUXEL_EPSILON, numClusters, clusters );
+-		if( pointCluster >= 0 )
+-			VectorCopy( nudged, origin );
++		//if( pointCluster >= 0 )
++		//	VectorCopy( nudged, origin );
+ 		luxel[ 1 ] += 1.0f;
+ 	}
+ 	
+@@ -597,7 +689,7 @@
+ 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 +737,7 @@
+ 	
+ 	/* 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 +745,12 @@
+ 	/* 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 +766,7 @@
+ 	int				i;
+ 	vec4_t			plane;
+ 	vec3_t			*stv, *ttv, stvStatic[ 3 ], ttvStatic[ 3 ];
++	vec3_t			worldverts[ 3 ];
+ 	
+ 	
+ 	/* get plane if possible */
+@@ -699,16 +792,20 @@
+ 		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 +827,7 @@
+ 			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 +889,8 @@
+ 	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 +975,10 @@
+ 	}
+ 	
+ 	/* 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 );
+@@ -1173,7 +1270,7 @@
+ 					continue;
+ 				
+ 				/* map the fake vert */
+-				MapSingleLuxel( lm, NULL, &fake, lm->plane, pass, NULL, NULL );
++				MapSingleLuxel( lm, NULL, &fake, lm->plane, pass, NULL, NULL, NULL );
+ 			}
+ 		}
+ 	}
+@@ -1963,22 +2062,32 @@
+ 					deluxel = SUPER_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 ] )
++
++					////////// 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++;
++					}
++					else
++					{
++						/* 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 ] )
++							totalLighted++;
++					}
+ 					
+ 					/* add to light direction map (fixme: use luxel normal as starting point for deluxel?) */
+ 					if( deluxemap )
+Index: tools/quake3/q3map2/convert_map.c
+===================================================================
+--- tools/quake3/q3map2/convert_map.c	(revision 191)
++++ tools/quake3/q3map2/convert_map.c	(working copy)
+@@ -46,6 +46,105 @@
+ #define	SNAP_FLOAT_TO_INT	4
+ #define	SNAP_INT_TO_FLOAT	(1.0 / SNAP_FLOAT_TO_INT)
+ 
++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)
++			{
++				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);
++}
++
+ static void ConvertBrush( FILE *f, int num, bspBrush_t *brush, vec3_t origin )
+ {
+ 	int				i, j;
+@@ -54,12 +153,17 @@
+ 	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" );
++	fprintf( f, "\tbrushDef\n" );
++	fprintf( f, "\t{\n" );
+ 	
+ 	/* clear out build brush */
+ 	for( i = 0; i < buildBrush->numsides; i++ )
+@@ -109,9 +213,88 @@
+ 		/* 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;
++
++		if(vert[0] && vert[1] && vert[2])
++		{
++			int i;
++			vec3_t texX, texY;
++			vec3_t xy1I, xy1J, xy1K;
++			vec2_t stI, stJ, stK;
++			vec_t D, D0, D1, D2;
++
++			ComputeAxisBase(buildPlane->normal, texX, texY);
++
++			VectorSet(xy1I, DotProduct(vert[0]->xyz, texX), DotProduct(vert[0]->xyz, texY), 1);
++			VectorSet(xy1J, DotProduct(vert[1]->xyz, texX), DotProduct(vert[1]->xyz, texY), 1);
++			VectorSet(xy1K, DotProduct(vert[2]->xyz, texX), DotProduct(vert[2]->xyz, texY), 1);
++			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(
++				xy1I[0], xy1I[1], 1,
++				xy1J[0], xy1J[1], 1,
++				xy1K[0], xy1K[1], 1
++			);
++			if(D != 0)
++			{
++				for(i = 0; i < 2; ++i)
++				{
++					D0 = Det3x3(
++						stI[i], xy1I[1], 1,
++						stJ[i], xy1J[1], 1,
++						stK[i], xy1K[1], 1
++					);
++					D1 = Det3x3(
++						xy1I[0], stI[i], 1,
++						xy1J[0], stJ[i], 1,
++						xy1K[0], stK[i], 1
++					);
++					D2 = Det3x3(
++						xy1I[0], xy1I[1], stI[i],
++						xy1J[0], xy1J[1], stJ[i],
++						xy1K[0], xy1K[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], xy1I[0], xy1I[1],
++					vert[1]->xyz[0], vert[1]->xyz[1], vert[1]->xyz[2], xy1J[0], xy1J[1],
++					vert[2]->xyz[0], vert[2]->xyz[1], vert[2]->xyz[2], xy1K[0], xy1K[1]
++					);
++		}
++		else
++			if(strncmp(buildSide->shaderInfo->shader, "textures/common/", 16))
++				fprintf(stderr, "no matching triangle for brushside using %s (hopefully nobody can see this side anyway)\n", buildSide->shaderInfo->shader);
+ 		
+ 		/* get texture name */
+ 		if( !Q_strncasecmp( buildSide->shaderInfo->shader, "textures/", 9 ) )
+@@ -130,14 +313,21 @@
+ 		
+ 		/* 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",
++		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 ],
+-			texture );
++			buildSide->texMat[0][0], buildSide->texMat[0][1], buildSide->texMat[0][2],
++			buildSide->texMat[1][0], buildSide->texMat[1][1], buildSide->texMat[1][2],
++			texture,
++			// DEBUG: valid ? 0 : C_DETAIL
++			0
++			);
++		// TODO write brush primitives format here
+ 	}
+ 	
+ 	/* end brush */
++	fprintf( f, "\t}\n" );
+ 	fprintf( f, "\t}\n\n" );
+ }
+ 
+Index: tools/quake3/q3map2/main.c
+===================================================================
+--- tools/quake3/q3map2/main.c	(revision 191)
++++ tools/quake3/q3map2/main.c	(working copy)
+@@ -541,6 +541,18 @@
+ 					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 );
++ 		}
+ 	}
+ 	
+ 	/* clean up map name */
+Index: tools/quake3/q3map2/model.c
+===================================================================
+--- tools/quake3/q3map2/model.c	(revision 193)
++++ tools/quake3/q3map2/model.c	(working copy)
+@@ -222,6 +222,8 @@
+ 	byte				*color;
+ 	picoIndex_t			*indexes;
+ 	remap_t				*rm, *glob;
++	double				normalEpsilon_save;
++	double				distanceEpsilon_save;
+ 	
+ 	
+ 	/* get model */
+@@ -398,9 +400,8 @@
+ 		/* 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 */
+@@ -437,90 +438,141 @@
+ 					/* 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 ] ) )
++						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;
+ 						}
+ 					}
+ 				}
++
++				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( fabs( plane[ k ] ) > fabs( plane[ (k + 1) % 3 ] ) &&
+-								fabs( plane[ k ] ) > fabs( plane[ (k + 2) % 3 ] ) )
+-							{
+-								backs[ j ][ k ] += plane[ k ] < 0.0f ? 64.0f : -64.0f;
+-								break;
+-							}
+-						}
++
++						// 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 ].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 );
+ 				}
+ 			}
+ 		}
+Index: tools/quake3/q3map2/map.c
+===================================================================
+--- tools/quake3/q3map2/map.c	(revision 193)
++++ tools/quake3/q3map2/map.c	(working copy)
+@@ -184,7 +184,7 @@
+ 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
+@@ -193,7 +193,13 @@
+   SnapPlane reenabled by namespace because of multiple reports of
+   q3map2-crashes which were triggered by this patch.
+ */
++	// div0: ensure the point "center" stays on the plane (actually, this
++	// rotates the plane around the point center).
++	// if center lies on the plane, it is guaranteed to stay on the plane by
++	// this fix.
++	vec_t centerDist = DotProduct(normal, center);
+ 	SnapNormal( normal );
++	*dist += (DotProduct(normal, center) - centerDist);
+ 
+ 	if( fabs( *dist - Q_rint( *dist ) ) < distanceEpsilon )
+ 		*dist = Q_rint( *dist );
+@@ -207,7 +213,7 @@
+ 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 normal, vec_t dist, int numPoints, vec3_t *points ) // NOTE: this has a side effect on the normal. Good or bad?
+ 
+ #ifdef USE_HASHING
+ 
+@@ -215,10 +221,14 @@
+ 	int		i, j, hash, h;
+ 	plane_t	*p;
+ 	vec_t	d;
++	vec3_t centerofweight;
++
++	VectorClear(centerofweight);
++	for(i = 0; i < numPoints; ++i)
++		VectorMA(centerofweight, 1.0 / numPoints, points[i], centerofweight);
+ 	
+-	
+ 	/* hash the plane */
+-	SnapPlane( normal, &dist );
++	SnapPlane( normal, &dist, centerofweight );
+ 	hash = (PLANE_HASHES - 1) & (int) fabs( dist );
+ 	
+ 	/* search the border bins as well */
+@@ -259,7 +269,13 @@
+ 	plane_t	*p;
+ 	
+ 
+-	SnapPlane( normal, &dist );
++	vec3_t centerofweight;
++
++	VectorClear(centerofweight);
++	for(i = 0; i < numPoints; ++i)
++		VectorMA(centerofweight, 1.0 / numPoints, points[i], centerofweight);
++	
++	SnapPlane( normal, &dist, centerofweight );
+ 	for( i = 0, p = mapplanes; i < nummapplanes; i++, p++ )
+ 	{
+ 		if( PlaneEqual( p, normal, dist ) )
+Index: tools/quake3/q3map2/q3map2.h
+===================================================================
+--- tools/quake3/q3map2/q3map2.h	(revision 193)
++++ tools/quake3/q3map2/q3map2.h	(working copy)
+@@ -35,8 +35,8 @@
+ 
+ 
+ /* version */
+-#define Q3MAP_VERSION	"2.5.17"
+-#define Q3MAP_MOTD		"Last one turns the lights off"
++#define Q3MAP_VERSION	"2.5.17-div0-UTpicomodelase-UTpicomodelnormals-obj-UTbouncefix-UTfloodlight-UTlmexposure-UTtrianglecheck-decomptexcoords-snapplane"
++#define Q3MAP_MOTD		"Blackhole Box ate all the light"
+ 
+ 
+ 
+Index: include/version.default
+===================================================================
+--- include/version.default     (revision 193)
++++ include/version.default     (working copy)
+@@ -1 +1 @@
+-1.5.0
++1.5.0-div0-UTpicomodelase-UTpicomodelnormals-obj-modelnormals-nexuizfixes

Added: trunk/misc/gtkradiant/mergepatches.sh
===================================================================
--- trunk/misc/gtkradiant/mergepatches.sh	                        (rev 0)
+++ trunk/misc/gtkradiant/mergepatches.sh	2008-03-03 11:30:10 UTC (rev 3480)
@@ -0,0 +1,60 @@
+#!/bin/sh
+
+# script that generates the combined patch from the source patches
+
+# if this is run as CGI, add a header
+if [ -n "$REQUEST_METHOD" ]; then
+	echo "Content-type: text/plain"
+	echo ""
+fi
+
+cat <<'EOF'
+NOTE: this patch set is autogenerated from the "singlepatches" subdirectory of nexuiz/trunk/misc.
+
+Do not commit changes to THIS!
+
+Always run
+	sh mergepatches.sh > gtkradiant-nexuiz-patchset.diff
+before committing new singlepatches!
+
+
+
+EOF
+
+cd singlepatches
+
+pq=
+pr=
+for X in [!_]*.diff; do
+	cat "$X"
+	Y=${X%.diff}
+	Z=${Y%%-*}
+	Y=${Y#*-}
+	[ "$Z" = "q3map2" ] || pr="$pr-$Y"
+	[ "$Z" = "gtkradiant" ] || pq="$pq-$Y"
+done
+
+cat <<EOF
+Index: tools/quake3/q3map2/q3map2.h
+===================================================================
+--- tools/quake3/q3map2/q3map2.h	(revision 193)
++++ tools/quake3/q3map2/q3map2.h	(working copy)
+@@ -35,8 +35,8 @@
+ 
+ 
+ /* version */
+-#define Q3MAP_VERSION	"2.5.17"
+-#define Q3MAP_MOTD		"Last one turns the lights off"
++#define Q3MAP_VERSION	"2.5.17-div0$pq"
++#define Q3MAP_MOTD		"Blackhole Box ate all the light"
+ 
+ 
+ 
+Index: include/version.default
+===================================================================
+--- include/version.default     (revision 193)
++++ include/version.default     (working copy)
+@@ -1 +1 @@
+-1.5.0
++1.5.0-div0$pr
+EOF


Property changes on: trunk/misc/gtkradiant/mergepatches.sh
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/misc/gtkradiant/singlepatches/both-UTpicomodelase.diff
===================================================================
--- trunk/misc/gtkradiant/singlepatches/both-UTpicomodelase.diff	                        (rev 0)
+++ trunk/misc/gtkradiant/singlepatches/both-UTpicomodelase.diff	2008-03-03 11:30:10 UTC (rev 3480)
@@ -0,0 +1,459 @@
+Index: libs/picomodel/pm_ase.c
+===================================================================
+--- libs/picomodel/pm_ase.c	(revision 191)
++++ libs/picomodel/pm_ase.c	(working copy)
+@@ -32,6 +32,7 @@
+ 
+ ----------------------------------------------------------------------------- */
+ 
++void Sys_Printf (const char *format, ...);
+ 
+ /* marker */
+ #define PM_ASE_C
+@@ -253,7 +254,6 @@
+ struct aseVertex_s
+ {
+ 	picoVec3_t xyz;
+-	picoVec3_t normal;
+ 	picoIndex_t id;
+ };
+ 
+@@ -276,6 +276,8 @@
+ 	picoIndex_t smoothingGroup;
+ 	picoIndex_t materialId;
+ 	picoIndex_t subMaterialId;
++   picoVec3_t  facenormal;
++   picoVec3_t  vertexnormal[3];
+ };
+ typedef aseFace_t* aseFacesIter_t;
+ 
+@@ -455,33 +457,157 @@
+ 
+ #endif
+ 
++static int VectorCompareExtn( picoVec3_t n1, picoVec3_t n2, float epsilon )
++{
++	int		i;
++	
++	
++	/* test */
++	for( i= 0; i < 3; i++ )
++		if( fabs( n1[ i ] - n2[ i ]) > epsilon )
++			return -1;
++	return 1;
++}
++
++#define CrossProductTemp(a,b,c)		((c)[0]=(a)[1]*(b)[2]-(a)[2]*(b)[1],(c)[1]=(a)[2]*(b)[0]-(a)[0]*(b)[2],(c)[2]=(a)[0]*(b)[1]-(a)[1]*(b)[0])
++
+ static void _ase_submit_triangles( picoModel_t* model , aseMaterial_t* materials , aseVertex_t* vertices, aseTexCoord_t* texcoords, aseColor_t* colors, aseFace_t* faces, int numFaces )
+ {
+-	aseFacesIter_t i = faces, end = faces + numFaces;
+-	for(; i != end; ++i)
++   
++   picoVec3_t accum;
++   int index;
++   int counter;
++   aseFacesIter_t i = faces, end = faces + numFaces;
++   counter=0;
++
++   //rebuild normals
++   for(i=faces; i != end; ++i)
++   {
++    
++      //&(*i).facenormal
++	   //vec3_t v1, v2;
++	   //VectorSubtract(va, vb, v1);
++	   //VectorSubtract(vc, vb, v2);
++      //CrossProduct(v1, v2, out);
++      
++      picoVec3_t a,b,c;
++      picoVec3_t v1,v2,v3;
++      int j;
++      counter++;
++      for (j=0;j<3;j++)
++      {
++         a[j]    =  vertices[(*i).indices[0]].xyz[j];
++         b[j]    =  vertices[(*i).indices[1]].xyz[j];
++         c[j]    =  vertices[(*i).indices[2]].xyz[j];
++      }
++      for (j=0;j<3;j++)
++      {
++         v1[j]=a[j]-b[j];
++         v2[j]=c[j]-b[j];
++      }
++      
++      CrossProductTemp(v1,v2,v3);
++      _pico_normalize_vec(v3);
++      (*i).facenormal[0]=v3[0];
++      (*i).facenormal[1]=v3[1];
++      (*i).facenormal[2]=v3[2];
++      
++      
++   }
++   
++   
++   //if (counter>0) Sys_Printf( "Rebuilding %d Normals\n", counter * 3 );
++   for(i=faces; i != end; ++i)
+ 	{
+-		/* look up the shader for the material/submaterial pair */
++      /* look up the shader for the material/submaterial pair */
+ 		aseSubMaterial_t* subMtl = _ase_get_submaterial_or_default( materials, (*i).materialId, (*i).subMaterialId );
+-		if( subMtl == NULL )
++
++      if( subMtl == NULL )
+ 		{
+ 			return;
+ 		}
+ 
+ 		{
+ 			picoVec3_t* xyz[3];
++         picoVec3_t *a[3];
+ 			picoVec3_t* normal[3];
+ 			picoVec2_t* st[3];
+ 			picoColor_t* color[3];
+ 			picoIndex_t smooth[3];
+-			int j;
+-			/* we pull the data from the vertex, color and texcoord arrays using the face index data */
+-			for ( j = 0 ; j < 3 ; j ++ )
++
++			int j,z;
++
++         
++   
++         /* we pull the data from the vertex, color and texcoord arrays using the face index data */
++         for ( j = 0 ; j < 3 ; j ++ )
+ 			{
+-				xyz[j]    = &vertices[(*i).indices[j]].xyz;
+-				normal[j] = &vertices[(*i).indices[j]].normal;
++            aseFacesIter_t q = faces;
++            aseFacesIter_t qend = faces + numFaces;
++
++            xyz[j]    = &vertices[(*i).indices[j]].xyz;
++            
++            // Use Face normal
++            normal[j] = &(*i).facenormal;
++             
++     
++            //Oooor we can use the smoothing group
++
++            //Slow method, but testing
++            //Find All faces that use this vertex, average their facenormals.
++            // skip where smoothgroups both equal 0, or don't have any shared bits (x & y)
++            index=(*i).indices[j];
++    
++//            accum[0]=0;
++  //          accum[1]=0;
++    //        accum[2]=0;
++            accum[0]=(*i).facenormal[0];
++            accum[1]=(*i).facenormal[1];
++            accum[2]=(*i).facenormal[2];
++            counter=1;
++           
++            
++            z=0;
++            for(; q != qend; ++q)
++            {
++               z++;
++               if (q==i)
++                  continue;
++               // if  ( (*q).indices[0]==index || (*q).indices[1]==index || (*q).indices[2]==index) 
++                a[0]=  &vertices[(*q).indices[0] ].xyz; 
++                a[1]=  &vertices[(*q).indices[1] ].xyz; 
++                a[2]=  &vertices[(*q).indices[2] ].xyz; 
++               
++               if ( VectorCompareExtn(*a[0],*xyz[j],0.01f)>0 ||
++                    VectorCompareExtn(*a[1],*xyz[j],0.01f)>0 ||
++                    VectorCompareExtn(*a[2],*xyz[j],0.01f)>0
++                  )
++               {
++                  if ( (*i).smoothingGroup==0 && (*q).smoothingGroup ==0 )
++                     continue;
++
++                  if ( (*i).smoothingGroup & (*q).smoothingGroup  )
++                  {
++                     accum[0]+=(*q).facenormal[0];
++                     accum[1]+=(*q).facenormal[1];
++                     accum[2]+=(*q).facenormal[2];
++                     
++                     counter++;
++             
++                  }
++               }
++            } 
++            _pico_normalize_vec(accum); 
++
++            (*i).vertexnormal[j][0]=accum[0];
++            (*i).vertexnormal[j][1]=accum[1];
++            (*i).vertexnormal[j][2]=accum[2];
++            normal[j]=&(*i).vertexnormal[j]; 
++                        
++
+ 				st[j]     = &texcoords[(*i).indices[j + 3]].texcoord;
+-	 	 	
+-				if( colors != NULL && (*i).indices[j + 6] >= 0 )
++	 	 	      
++          	if( colors != NULL && (*i).indices[j + 6] >= 0 )
+ 				{
+ 					color[j] = &colors[(*i).indices[j + 6]].color;
+ 				}
+@@ -490,30 +616,18 @@
+ 					color[j] = &white;
+ 				}
+ 
+-				smooth[j] = (vertices[(*i).indices[j]].id * (1 << 16)) + (*i).smoothingGroup; /* don't merge vertices */
++				smooth[j] = 0;//  (vertices[(*i).indices[j]].id * (1 << 16)) + (*i).smoothingGroup; /* don't merge vertices */
+ 				
+ 			}
+ 
+ 			/* submit the triangle to the model */
+ 			PicoAddTriangleToModel ( model , xyz , normal , 1 , st , 1 , color , subMtl->shader, smooth );
+ 		}
++
+ 	}
+ }
+ 
+-static void shadername_convert(char* shaderName)
+-{
+-  /* unix-style path separators */
+-  char* s = shaderName;
+-  for(; *s != '\0'; ++s)
+-  {
+-    if(*s == '\\')
+-    {
+-      *s = '/';
+-    }
+-  }
+-}
+ 
+-
+ /* _ase_load:
+  *  loads a 3dsmax ase model file.
+ */
+@@ -534,6 +648,9 @@
+ 	int numColorVertices = 0;
+ 	int numColorVertexFaces = 0;
+ 	int vertexId = 0;
++   int currentVertexFace=0;
++   int currentVertexIndex=0;
++   int counter=0;
+ 
+ 	aseMaterial_t* materials = NULL;
+ 
+@@ -610,10 +727,11 @@
+ 		}
+ 		else if (!_pico_stricmp(p->token,"*mesh_numvertex"))
+ 		{
+-			if (!_pico_parse_int( p, &numVertices) )
++  			if (!_pico_parse_int( p, &numVertices) )
+ 				_ase_error_return("Missing MESH_NUMVERTEX value");
+ 
+ 			vertices = _pico_calloc(numVertices, sizeof(aseVertex_t));
++         currentVertexIndex=0;   
+ 		}
+ 		else if (!_pico_stricmp(p->token,"*mesh_numfaces"))
+ 		{
+@@ -621,6 +739,7 @@
+ 				_ase_error_return("Missing MESH_NUMFACES value");
+ 
+ 			faces = _pico_calloc(numFaces, sizeof(aseFace_t));
++
+ 		}
+ 		else if (!_pico_stricmp(p->token,"*mesh_numtvertex"))
+ 		{
+@@ -685,7 +804,20 @@
+ 
+ 			vertices[index].id = vertexId++;
+ 		}
+-		/* model mesh vertex normal */
++		else if (!_pico_stricmp(p->token,"*mesh_facenormal"))
++		{
++		   //Grab the faceindex for the next vertex normals.
++         if( numVertices == 0 )
++				_ase_error_return("Vertex parse error (facenormals)");
++
++         if (!_pico_parse_int( p,&currentVertexFace ))
++				_ase_error_return("Vertex parse error");
++
++ 			if (!_pico_parse_vec( p,faces[currentVertexFace].facenormal ))
++				_ase_error_return("Vertex parse error");
++
++      }
++      /* model mesh vertex normal */
+ 		else if (!_pico_stricmp(p->token,"*mesh_vertexnormal"))
+ 		{
+ 			int			index;
+@@ -696,10 +828,25 @@
+ 			/* get vertex data (orig: index +y -x +z) */
+ 			if (!_pico_parse_int( p,&index ))
+ 				_ase_error_return("Vertex parse error");
+-			if (!_pico_parse_vec( p,vertices[index].normal ))
++
++         //^^ Index is 'wrong' in .ase models.  they reference the same vert index with multiple normals..
++         // I've tried, this is a lost cause.  Use the SG's
++         // 
++			/*
++         
++         if (!_pico_parse_vec( p,vertices[counter].normal ))
+ 				_ase_error_return("Vertex parse error");
++         vertices[counter].faceid=index;
++         counter++;
++         */
+ 		}
+ 		/* model mesh face */
++		else if (!_pico_stricmp(p->token,"*mesh_normals"))
++      {
++      //   counter=0; //part of the above vertex normals fix
++      }
++         
++      /* model mesh face */
+ 		else if (!_pico_stricmp(p->token,"*mesh_face"))
+ 		{
+ 			picoIndex_t indexes[3];
+@@ -736,8 +883,35 @@
+ 				}
+ 				if (!_pico_stricmp (p->token,"*MESH_SMOOTHING" ))
+ 				{
+-					_pico_parse_int ( p , &faces[index].smoothingGroup );
+-				}
++               int total=0;
++               char* point;
++               char* start;
++               _pico_parse(p,0);
++
++               point=p->token;
++               start=point;
++               faces[index].smoothingGroup=0;
++              
++               //Super dodgy comma delimited string parse
++               while (*point<'A') 
++               {
++                  if (*point<=32 || *point==',')
++                  {
++                     total=atoi(start);
++                     if (total!=0)
++                     {
++                        faces[index].smoothingGroup+=1<<total;
++                     }
++                     start=point+1;
++                  }
++                  
++                  point++;
++               }
++               
++               
++			      
++               
++            }
+ 				if (!_pico_stricmp (p->token,"*MESH_MTLID" ))
+ 				{
+ 					_pico_parse_int ( p , &faces[index].subMaterialId );
+@@ -755,19 +929,19 @@
+ 			int			index;
+ 
+ 			if( numVertices == 0 )
+-				_ase_error_return("Texture Vertex parse error");
++				_ase_error_return("Vertex parse error");
+ 
+ 			/* get uv vertex index */
+-			if (!_pico_parse_int( p,&index ) || index >= numTextureVertices)
+-				_ase_error_return("Texture vertex parse error");
++			if (!_pico_parse_int( p,&index ))
++				_ase_error_return("UV vertex parse error");
+ 
+ 			/* get uv vertex s */
+ 			if (!_pico_parse_float( p,&texcoords[index].texcoord[0] ))
+-				_ase_error_return("Texture vertex parse error");
++				_ase_error_return("UV vertex parse error");
+ 
+ 			/* get uv vertex t */
+ 			if (!_pico_parse_float( p,&texcoords[index].texcoord[1] ))
+-				_ase_error_return("Texture vertex parse error");
++				_ase_error_return("UV vertex parse error");
+ 			
+ 			/* ydnar: invert t */
+ 			texcoords[index].texcoord[ 1 ] = 1.0f - texcoords[index].texcoord[ 1 ];
+@@ -831,6 +1005,13 @@
+ 			
+ 			/* leave alpha alone since we don't get any data from the ASE format */
+ 			colors[index].color[3] = 255;
++
++         /* 27 hack, red as alpha */
++         colors[index].color[3]=colors[index].color[0];
++         colors[index].color[0]=255;
++         colors[index].color[1]=255;
++         colors[index].color[2]=255;
++
+ 		}
+ 		/* model color face */
+ 		else if (!_pico_stricmp(p->token,"*mesh_cface"))
+@@ -900,7 +1081,6 @@
+ 				{
+ 					/* set material name */
+ 					_pico_first_token( materialName );
+-          shadername_convert(materialName);
+ 					PicoSetShaderName( shader, materialName);
+ 
+ 					/* set shader's transparency */
+@@ -1085,7 +1265,6 @@
+ 				}
+ 
+ 				/* set material name */
+-        shadername_convert(materialName);
+ 				PicoSetShaderName( shader,materialName );
+ 
+ 				/* set shader's transparency */
+@@ -1115,8 +1294,18 @@
+           char* p = mapname;
+ 
+           /* convert to shader-name format */
+-          shadername_convert(mapname);
+           {
++            /* unix-style path separators */
++            char* s = mapname;
++            for(; *s != '\0'; ++s)
++            {
++              if(*s == '\\')
++              {
++                *s = '/';
++              }
++            }
++          }
++          {
+             /* remove extension */
+             char* last_period = strrchr(p, '.');
+             if(last_period != NULL)
+@@ -1125,14 +1314,32 @@
+             }
+           }
+ 
+-          /* find shader path */
++          /* find game root */
+           for(; *p != '\0'; ++p)
+           {
+-            if(_pico_strnicmp(p, "models/", 7) == 0 || _pico_strnicmp(p, "textures/", 9) == 0)
++            if(_pico_strnicmp(p, "quake", 5) == 0 || _pico_strnicmp(p, "doom", 4) == 0)
+             {
+               break;
+             }
+           }
++          /* root-relative */
++          for(; *p != '\0'; ++p)
++          {
++            if(*p == '/')
++            {
++              ++p;
++              break;
++            }
++          }
++          /* game-relative */
++          for(; *p != '\0'; ++p)
++          {
++            if(*p == '/')
++            {
++              ++p;
++              break;
++            }
++          }
+ 
+           if(*p != '\0')
+           {

Added: trunk/misc/gtkradiant/singlepatches/both-UTpicomodelnormals.diff
===================================================================
--- trunk/misc/gtkradiant/singlepatches/both-UTpicomodelnormals.diff	                        (rev 0)
+++ trunk/misc/gtkradiant/singlepatches/both-UTpicomodelnormals.diff	2008-03-03 11:30:10 UTC (rev 3480)
@@ -0,0 +1,101 @@
+Index: libs/picomodel/picomodel.c
+===================================================================
+--- libs/picomodel/picomodel.c	(revision 191)
++++ libs/picomodel/picomodel.c	(working copy)
+@@ -295,10 +295,7 @@
+ 		model = PicoModuleLoadModel(module, fileName, buffer, bufSize, frameNum);
+ 	}
+ 	
+-  if(model != 0)
+-  {
+-	  _pico_free(buffer);
+-  }
++	_pico_free(buffer);
+ 
+ 	/* return */
+ 	return model;
+@@ -1573,6 +1570,7 @@
+ {
+ 	int		i, j;
+ 	
++//   Sys_Printf(" %f %f %f\n", normal[0] , normal[1] , normal[2] );
+ 	
+ 	/* dummy check */
+ 	if( surface == NULL || surface->numVertexes <= 0 )
+@@ -1861,13 +1859,10 @@
+ typedef picoVec3_t* picoNormalIter_t;
+ typedef picoIndex_t* picoIndexIter_t;
+ 
+-#define THE_CROSSPRODUCTS_OF_ANY_PAIR_OF_EDGES_OF_A_GIVEN_TRIANGLE_ARE_EQUAL 1
+-
+ void _pico_triangles_generate_weighted_normals(picoIndexIter_t first, picoIndexIter_t end, picoVec3_t* xyz, picoVec3_t* normals)
+ {
+ 	for(; first != end; first += 3)
+ 	{
+-#if (THE_CROSSPRODUCTS_OF_ANY_PAIR_OF_EDGES_OF_A_GIVEN_TRIANGLE_ARE_EQUAL)
+ 		picoVec3_t weightedNormal;
+ 		{
+ 			float* a = xyz[*(first + 0)];
+@@ -1878,24 +1873,11 @@
+ 			_pico_subtract_vec( c, a, ca );
+ 			_pico_cross_vec( ca, ba, weightedNormal );
+ 		}
+-#endif
+ 		{
+ 			int j = 0;
+ 			for(; j < 3; ++j)
+ 			{
+ 				float* normal = normals[*(first + j)];
+-#if (!THE_CROSSPRODUCTS_OF_ANY_PAIR_OF_EDGES_OF_A_GIVEN_TRIANGLE_ARE_EQUAL)
+-				picoVec3_t weightedNormal;
+-				{
+-					float* a = xyz[*(first + ((j + 0) % 3))];
+-					float* b = xyz[*(first + ((j + 1) % 3))];
+-					float* c = xyz[*(first + ((j + 2) % 3))];
+-					picoVec3_t ba, ca;
+-					_pico_subtract_vec( b, a, ba );
+-					_pico_subtract_vec( c, a, ca );
+-					_pico_cross_vec( ca, ba, weightedNormal );
+-				}
+-#endif
+ 				_pico_add_vec(weightedNormal, normal, normal);
+ 			}
+ 		}
+@@ -1941,7 +1923,8 @@
+ {
+ 	for(; first != last; ++first, ++generated)
+ 	{
+-		if(!_pico_normal_is_unit_length(*first) || !_pico_normal_within_tolerance(*first, *generated))
++      //27 - fix for badly generated normals thing.
++      //	if(!_pico_normal_is_unit_length(*first) || !_pico_normal_within_tolerance(*first, *generated))
+ 		{
+ 			_pico_copy_vec(*generated, *first);
+ 		}
+@@ -1954,10 +1937,11 @@
+ 
+ 	_pico_normals_zero(normals, normals + surface->numVertexes);
+ 
++   //Just build standard no sg normals for now
+ 	_pico_triangles_generate_weighted_normals(surface->index, surface->index + surface->numIndexes, surface->xyz, normals);
+ 	_pico_vertices_combine_shared_normals(surface->xyz, surface->smoothingGroup, normals, surface->numVertexes);
+ 
+-	_pico_normals_normalize(normals, normals + surface->numVertexes);
++	_pico_normals_normalize(normals, normals + surface->numVertexes); 
+ 
+ 	_pico_normals_assign_generated_normals(surface->normal, surface->normal + surface->numVertexes, normals);
+ 
+@@ -2261,7 +2245,7 @@
+ 		int newVertIndex = PicoGetSurfaceNumIndexes ( workSurface );
+ 
+ 		/* get the index of the vertex that we're going to store at newVertIndex */
+-		vertDataIndex = PicoFindSurfaceVertexNum ( workSurface , *xyz[i] , *normals[i] , numSTs , st[i] , numColors , colors[i], smoothingGroup[i]);
++      vertDataIndex = -1;// PicoFindSurfaceVertexNum ( workSurface , *xyz[i] , *normals[i] , numSTs , st[i] , numColors , colors[i], smoothingGroup[i]);
+ 
+ 		/* the vertex wasn't found, so create a new vertex in the pool from the data we have */
+ 		if ( vertDataIndex == -1 )
+@@ -2290,3 +2274,5 @@
+ 		PicoSetSurfaceIndex ( workSurface , newVertIndex , vertDataIndex );
+ 	}
+ }
++
++

Added: trunk/misc/gtkradiant/singlepatches/both-obj.diff
===================================================================
--- trunk/misc/gtkradiant/singlepatches/both-obj.diff	                        (rev 0)
+++ trunk/misc/gtkradiant/singlepatches/both-obj.diff	2008-03-03 11:30:10 UTC (rev 3480)
@@ -0,0 +1,83 @@
+Index: libs/picomodel/pm_obj.c
+===================================================================
+--- libs/picomodel/pm_obj.c	(revision 193)
++++ libs/picomodel/pm_obj.c	(working copy)
+@@ -265,7 +265,7 @@
+ 		/* get next token in material file */
+ 		if (_pico_parse( p,1 ) == NULL)
+ 			break;
+-#if 0
++#if 1
+ 
+ 		/* skip empty lines */
+ 		if (p->token == NULL || !strlen( p->token ))
+@@ -307,6 +307,7 @@
+ 		else if (!_pico_stricmp(p->token,"map_kd"))
+ 		{
+ 			char *mapName;
++			picoShader_t *shader;
+ 
+ 			/* pointer to current shader must be valid */
+ 			if (curShader == NULL)
+@@ -321,6 +322,10 @@
+ 				_pico_printf( PICO_ERROR,"Missing material map name in MTL, line %d.",p->curLine);
+ 				_obj_mtl_error_return;
+ 			}
++			/* create a new pico shader */
++			shader = PicoNewShader( model );
++			if (shader == NULL)
++				_obj_mtl_error_return;
+ 			/* set shader map name */
+ 			PicoSetShaderMapName( shader,mapName );
+ 		}
+@@ -521,7 +526,7 @@
+ 	PicoSetModelFileName( model,fileName );
+ 
+ 	/* try loading the materials; we don't handle the result */
+-#if 0
++#if 1
+ 	_obj_mtl_load( model );
+ #endif
+ 
+@@ -830,6 +835,41 @@
+ 				curVertex += max;
+ 			}
+ 		}
++		else if (!_pico_stricmp(p->token,"usemtl"))
++		{
++			picoShader_t *shader;
++			char *name;
++
++			/* get material name */
++			name = _pico_parse( p,0 );
++
++			/* validate material name */
++			if (name == NULL || !strlen(name))
++			{
++				_pico_printf( PICO_ERROR,"Missing material name in OBJ, line %d.",p->curLine);
++			}
++			else
++			{
++				shader = PicoFindShader( model, name, 1 );
++				if (shader == NULL)
++				{
++					_pico_printf( PICO_ERROR,"Undefined material name in OBJ, line %d. Making a default shader.",p->curLine);
++
++					/* create a new pico shader */
++					shader = PicoNewShader( model );
++					if (shader != NULL)
++					{
++						PicoSetShaderName( shader,name );
++						PicoSetShaderMapName( shader,name );
++						PicoSetSurfaceShader( curSurface, shader );
++					}
++				}
++				else
++				{
++					PicoSetSurfaceShader( curSurface, shader );
++				}
++			}
++		}
+ 		/* skip unparsed rest of line and continue */
+ 		_pico_parse_skip_rest( p );
+ 	}

Added: trunk/misc/gtkradiant/singlepatches/gtkradiant-modelnormals.diff
===================================================================
--- trunk/misc/gtkradiant/singlepatches/gtkradiant-modelnormals.diff	                        (rev 0)
+++ trunk/misc/gtkradiant/singlepatches/gtkradiant-modelnormals.diff	2008-03-03 11:30:10 UTC (rev 3480)
@@ -0,0 +1,34 @@
+Index: plugins/model/model.cpp
+===================================================================
+--- plugins/model/model.cpp	(revision 193)
++++ plugins/model/model.cpp	(working copy)
+@@ -123,14 +123,27 @@
+     }
+     glVertexPointer(3, GL_FLOAT, sizeof(ArbitraryMeshVertex), &m_vertices.data()->vertex);
+     glDrawElements(GL_TRIANGLES, GLsizei(m_indices.size()), RenderIndexTypeID, m_indices.data());
++
+ #if defined(_DEBUG)
++	GLfloat modelview[16];
++	glGetFloatv(GL_MODELVIEW_MATRIX, modelview); // I know this is slow as hell, but hey - we're in _DEBUG
++	Matrix4 modelview_inv(
++		modelview[0], modelview[1], modelview[2], modelview[3],
++		modelview[4], modelview[5], modelview[6], modelview[7],
++		modelview[8], modelview[9], modelview[10], modelview[11],
++		modelview[12], modelview[13], modelview[14], modelview[15]);
++	matrix4_full_invert(modelview_inv);
++	Matrix4 modelview_inv_transposed = matrix4_transposed(modelview_inv);
++
+     glBegin(GL_LINES);
+ 
+     for(Array<ArbitraryMeshVertex>::const_iterator i = m_vertices.begin(); i != m_vertices.end(); ++i)
+     {
+-      Vector3 normal = vector3_added(vertex3f_to_vector3((*i).vertex), vector3_scaled(normal3f_to_vector3((*i).normal), 8));
++	  Vector3 normal = normal3f_to_vector3((*i).normal);
++	  normal = matrix4_transformed_direction(modelview_inv, vector3_normalised(matrix4_transformed_direction(modelview_inv_transposed, normal))); // do some magic
++      Vector3 normalTransformed = vector3_added(vertex3f_to_vector3((*i).vertex), vector3_scaled(normal, 8));
+       glVertex3fv(vertex3f_to_array((*i).vertex));
+-      glVertex3fv(vector3_to_array(normal));
++      glVertex3fv(vector3_to_array(normalTransformed));
+     }
+     glEnd();
+ #endif

Added: trunk/misc/gtkradiant/singlepatches/gtkradiant-nexuizfixes.diff
===================================================================
--- trunk/misc/gtkradiant/singlepatches/gtkradiant-nexuizfixes.diff	                        (rev 0)
+++ trunk/misc/gtkradiant/singlepatches/gtkradiant-nexuizfixes.diff	2008-03-03 11:30:10 UTC (rev 3480)
@@ -0,0 +1,13 @@
+Index: games/NexuizPack/games/nexuiz.game
+===================================================================
+--- games/NexuizPack/games/nexuiz.game	(revision 26)
++++ games/NexuizPack/games/nexuiz.game	(working copy)
+@@ -17,7 +17,7 @@
+   shaderpath="scripts"
+   archivetypes="pk3"
+   texturetypes="tga jpg png"
+-  modeltypes="md3 mdl md2 ase"
++  modeltypes="md3 mdl md2 ase obj"
+   maptypes="mapq3"
+   shaders="quake3"
+   entityclass="quake3"

Added: trunk/misc/gtkradiant/singlepatches/q3map2-UTbouncefix.diff
===================================================================
--- trunk/misc/gtkradiant/singlepatches/q3map2-UTbouncefix.diff	                        (rev 0)
+++ trunk/misc/gtkradiant/singlepatches/q3map2-UTbouncefix.diff	2008-03-03 11:30:10 UTC (rev 3480)
@@ -0,0 +1,20 @@
+Index: tools/quake3/q3map2/shaders.c
+===================================================================
+--- tools/quake3/q3map2/shaders.c	(revision 191)
++++ tools/quake3/q3map2/shaders.c	(working copy)
+@@ -793,8 +793,14 @@
+ 	}
+ 	
+ 	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 );
++	}
+ }
+ 
+ 

Added: trunk/misc/gtkradiant/singlepatches/q3map2-UTfloodlight.diff
===================================================================
--- trunk/misc/gtkradiant/singlepatches/q3map2-UTfloodlight.diff	                        (rev 0)
+++ trunk/misc/gtkradiant/singlepatches/q3map2-UTfloodlight.diff	2008-03-03 11:30:10 UTC (rev 3480)
@@ -0,0 +1,595 @@
+Index: tools/quake3/q3map2/light_ydnar.c
+===================================================================
+--- tools/quake3/q3map2/light_ydnar.c	(revision 191)
++++ tools/quake3/q3map2/light_ydnar.c	(working copy)
+@@ -1767,6 +1864,8 @@
+ 	float				tests[ 4 ][ 2 ] = { { 0.0f, 0 }, { 1, 0 }, { 0, 1 }, { 1, 1 } };
+ 	trace_t				trace;
+ 	float				stackLightLuxels[ STACK_LL_SIZE ];
++	vec3_t				flood;
++	float				*floodlight;
+ 	
+ 	
+ 	/* bail if this number exceeds the number of raw lightmaps */
+@@ -2223,6 +2332,78 @@
+ 	FreeTraceLights( &trace );
+ 	
+ 	/*	-----------------------------------------------------------------
++		floodlight pass
++		----------------------------------------------------------------- */
++
++	if( floodlighty )
++	{
++		/* 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 cluster */
++					cluster	= SUPER_CLUSTER( x, y );
++					//%	if( *cluster < 0 )
++					//%		continue;
++					
++					/* get particulars */
++					luxel = SUPER_LUXEL( lightmapNum, x, y );
++					floodlight = SUPER_FLOODLIGHT( x, y );
++					
++					flood[0]=floodlightRGB[0]*floodlightIntensity;
++					flood[1]=floodlightRGB[1]*floodlightIntensity;
++					flood[2]=floodlightRGB[2]*floodlightIntensity;
++					             
++					/* scale light value */
++					VectorScale( flood, *floodlight, flood );
++					luxel[0]+=flood[0];
++					luxel[1]+=flood[1];
++					luxel[2]+=flood[2];
++					
++					if (luxel[3]==0) luxel[3]=1;
++				}
++			}
++		}
++	}
++
++	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
+ 		----------------------------------------------------------------- */
+ 	
+@@ -3587,7 +3768,320 @@
+ 	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;
++	
++	/* 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;
++		
++		sscanf( value, "%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;
++		
++		floodlightDistance=v4;
++		floodlightIntensity=v5;
++    
++		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);
++}
++
++//27 - lighttracer style ambient occlusion light hack.
++//Kudos to the dirtmapping author for most of this source.
++void FloodLightRawLightmap( int rawLightmapNum )
++{
++	int					i, x, y, sx, sy, *cluster;
++	float				*origin, *normal, *floodlight, *floodlight2, average, samples;
++	rawLightmap_t		*lm;
++	surfaceInfo_t		*info;
++	trace_t				trace;
++	
++	/* bail if this number exceeds the number of raw lightmaps */
++	if( rawLightmapNum >= numRawLightmaps )
++		return;
++	
++	/* get lightmap */
++	lm = &rawLightmaps[ rawLightmapNum ];
++	
++	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 dirt */
++	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 dirt */
++			*floodlight = FloodLightForSample( &trace );
++		}
++	}
++	
++	/* testing no filtering */
++	return;
++	
++	/* 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;
++		}
++	}
++}
++
++/*
++FloodLightForSample()
++calculates floodlight value for a given sample
++once again, kudos to the dirtmapping coder
++*/
++float FloodLightForSample( trace_t *trace )
++{
++	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 )
++	{
++		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 );
++	}
++
++	/* iterate through ordered vectors */
++	for( i = 0; i < numFloodVectors; i++ )
++	{
++		if (floodlight_lowquality==qtrue)
++        {
++			if (rand()%10 != 0 ) continue;
++		}
++
++		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 )
++		{
++			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;
++}
++
+Index: tools/quake3/q3map2/light.c
+===================================================================
+--- tools/quake3/q3map2/light.c	(revision 191)
++++ tools/quake3/q3map2/light.c	(working copy)
+@@ -1378,6 +1378,56 @@
+ 			break;
+ 	}
+ 	
++	/////// Floodlighting for point //////////////////
++	//do our floodlight ambient occlusion loop, and add a single contribution based on the brightest dir
++	if (floodlighty)
++	{
++		int q;
++		float addSize,f;
++		vec3_t col,dir;
++		col[0]=col[1]=col[2]=floodlightIntensity;
++		dir[0]=dir[1]=0;
++		dir[2]=1;
++      
++		trace.testOcclusion = qtrue;
++		trace.forceSunlight = qfalse;
++		trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
++		trace.testAll = qtrue;     
++      
++		for (q=0;q<2;q++)
++		{
++			if (q==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);
++
++			contributions[ numCon ].color[0]=col[0]*f;
++			contributions[ numCon ].color[1]=col[1]*f;
++			contributions[ numCon ].color[2]=col[2]*f;
++
++			contributions[ numCon ].dir[0]=dir[0];
++			contributions[ numCon ].dir[1]=dir[1];
++			contributions[ numCon ].dir[2]=dir[2];
++
++			contributions[ numCon ].style = 0;
++			numCon++;		
++			/* push average direction around */
++			addSize = VectorLength( col );
++			VectorMA( gp->dir, addSize, dir, gp->dir );
++		}
++	}
++	/////////////////////
++
+ 	/* normalize to get primary light direction */
+ 	VectorNormalize( gp->dir, gp->dir );
+ 	
+@@ -1661,6 +1711,12 @@
+ 		RunThreadsOnIndividual( numRawLightmaps, qtrue, DirtyRawLightmap );
+ 	}
+ 	
++	/* floodlight them up */
++	if( floodlighty )
++	{
++		Sys_Printf( "--- FloodlightRawLightmap ---\n" );
++		RunThreadsOnIndividual( numRawLightmaps, qtrue, FloodLightRawLightmap );
++	}
+ 
+ 	/* ydnar: set up light envelopes */
+ 	SetupEnvelopes( qfalse, fast );
+@@ -1703,6 +1759,7 @@
+ 		/* flag bouncing */
+ 		bouncing = qtrue;
+ 		VectorClear( ambientColor );
++		floodlighty = false;
+ 		
+ 		/* generate diffuse lights */
+ 		RadFreeLights();
+@@ -2191,6 +2256,21 @@
+ 			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" ) )
+@@ -2279,6 +2359,7 @@
+ 	/* ydnar: set up optimization */
+ 	SetupBrushes();
+ 	SetupDirt();
++	SetupFloodLight();
+ 	SetupSurfaceLightmaps();
+ 	
+ 	/* initialize the surface facet tracing */
+Index: tools/quake3/q3map2/lightmaps_ydnar.c
+===================================================================
+--- tools/quake3/q3map2/lightmaps_ydnar.c	(revision 191)
++++ tools/quake3/q3map2/lightmaps_ydnar.c	(working copy)
+@@ -414,6 +414,12 @@
+ 		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 )
+Index: tools/quake3/q3map2/q3map2.h
+===================================================================
+--- tools/quake3/q3map2/q3map2.h	(revision 191)
++++ tools/quake3/q3map2/q3map2.h	(working copy)
+@@ -267,6 +267,7 @@
+ #define SUPER_NORMAL_SIZE		4
+ #define SUPER_DELUXEL_SIZE		3
+ #define BSP_DELUXEL_SIZE		3
++#define SUPER_FLOODLIGHT_SIZE	1
+ 
+ #define VERTEX_LUXEL( s, v )	(vertexLuxels[ s ] + ((v) * VERTEX_LUXEL_SIZE))
+ #define RAD_VERTEX_LUXEL( s, v )(radVertexLuxels[ s ] + ((v) * VERTEX_LUXEL_SIZE))
+@@ -279,6 +280,7 @@
+ #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) )	
+ 
+ 
+ 
+@@ -1392,6 +1395,7 @@
+ 	
+ 	float					*superDeluxels;	/* average light direction */
+ 	float					*bspDeluxels;
++	float					*superFloodLight; 
+ }
+ rawLightmap_t;
+ 
+@@ -1704,6 +1708,10 @@
+ float						DirtForSample( trace_t *trace );
+ void						DirtyRawLightmap( int num );
+ 
++void						SetupFloodLight();
++float						FloodLightForSample( trace_t *trace );
++void						FloodLightRawLightmap( int num );
++
+ void						IlluminateRawLightmap( int num );
+ void						IlluminateVertexes( int num );
+ 
+@@ -2098,6 +2106,13 @@
+ Q_EXTERN float				dirtScale Q_ASSIGN( 1.0f );
+ Q_EXTERN float				dirtGain Q_ASSIGN( 1.0f );
+ 
++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 );
++Q_EXTERN float				floodlightDistance Q_ASSIGN( 1024 );
++
+ Q_EXTERN qboolean			dump Q_ASSIGN( qfalse );
+ Q_EXTERN qboolean			debug Q_ASSIGN( qfalse );
+ Q_EXTERN qboolean			debugUnused Q_ASSIGN( qfalse );

Added: trunk/misc/gtkradiant/singlepatches/q3map2-UTlmexposure.diff
===================================================================
--- trunk/misc/gtkradiant/singlepatches/q3map2-UTlmexposure.diff	                        (rev 0)
+++ trunk/misc/gtkradiant/singlepatches/q3map2-UTlmexposure.diff	2008-03-03 11:30:10 UTC (rev 3480)
@@ -0,0 +1,244 @@
+Index: tools/quake3/q3map2/game_ja.h
+===================================================================
+--- tools/quake3/q3map2/game_ja.h	(revision 191)
++++ tools/quake3/q3map2/game_ja.h	(working copy)
+@@ -67,6 +67,7 @@
+ 	qfalse,				/* wolf lighting model? */
+ 	128,				/* lightmap width/height */
+ 	1.0f,				/* lightmap gamma */
++	1.0f,				/* lightmap exposure */
+ 	1.0f,				/* lightmap compensate */
+ 	"RBSP",				/* bsp file prefix */
+ 	1,					/* bsp file version */
+Index: tools/quake3/q3map2/game_tremulous.h
+===================================================================
+--- tools/quake3/q3map2/game_tremulous.h	(revision 191)
++++ tools/quake3/q3map2/game_tremulous.h	(working copy)
+@@ -70,6 +70,7 @@
+ 	qfalse,				/* wolf lighting model? */
+ 	128,				/* lightmap width/height */
+ 	1.0f,				/* lightmap gamma */
++	1.0f,				/* lightmap exposure */
+ 	1.0f,				/* lightmap compensate */
+ 	"IBSP",				/* bsp file prefix */
+ 	46,					/* bsp file version */
+Index: tools/quake3/q3map2/game_wolfet.h
+===================================================================
+--- tools/quake3/q3map2/game_wolfet.h	(revision 191)
++++ tools/quake3/q3map2/game_wolfet.h	(working copy)
+@@ -66,6 +66,7 @@
+ 	qtrue,				/* wolf lighting model? */
+ 	128,				/* lightmap width/height */
+ 	1.0f,				/* lightmap gamma */
++	1.0f,				/* lightmap exposure */
+ 	1.0f,				/* lightmap compensate */
+ 	"IBSP",				/* bsp file prefix */
+ 	47,					/* bsp file version */
+Index: tools/quake3/q3map2/game_wolf.h
+===================================================================
+--- tools/quake3/q3map2/game_wolf.h	(revision 191)
++++ tools/quake3/q3map2/game_wolf.h	(working copy)
+@@ -129,6 +129,7 @@
+ 	qtrue,				/* wolf lighting model? */
+ 	128,				/* lightmap width/height */
+ 	1.0f,				/* lightmap gamma */
++	1.0f,				/* lightmap exposure */
+ 	1.0f,				/* lightmap compensate */
+ 	"IBSP",				/* bsp file prefix */
+ 	47,					/* bsp file version */
+Index: tools/quake3/q3map2/game_sof2.h
+===================================================================
+--- tools/quake3/q3map2/game_sof2.h	(revision 191)
++++ tools/quake3/q3map2/game_sof2.h	(working copy)
+@@ -139,6 +139,7 @@
+ 	qfalse,					/* wolf lighting model? */
+ 	128,					/* lightmap width/height */
+ 	1.0f,					/* lightmap gamma */
++	1.0f,					/* lightmap exposure */
+ 	1.0f,					/* lightmap compensate */
+ 	"RBSP",					/* bsp file prefix */
+ 	1,						/* bsp file version */
+Index: tools/quake3/q3map2/game_etut.h
+===================================================================
+--- tools/quake3/q3map2/game_etut.h	(revision 191)
++++ tools/quake3/q3map2/game_etut.h	(working copy)
+@@ -148,6 +148,7 @@
+ 	qfalse,				/* wolf lighting model? */
+ 	128,				/* lightmap width/height */
+ 	2.2f,				/* lightmap gamma */
++	1.0f,				/* lightmap exposure */
+ 	1.0f,				/* lightmap compensate */
+ 	"IBSP",				/* bsp file prefix */
+ 	47,					/* bsp file version */
+Index: tools/quake3/q3map2/game_jk2.h
+===================================================================
+--- tools/quake3/q3map2/game_jk2.h	(revision 191)
++++ tools/quake3/q3map2/game_jk2.h	(working copy)
+@@ -64,6 +64,7 @@
+ 	qfalse,				/* wolf lighting model? */
+ 	128,				/* lightmap width/height */
+ 	1.0f,				/* lightmap gamma */
++	1.0f,				/* lightmap exposure */
+ 	1.0f,				/* lightmap compensate */
+ 	"RBSP",				/* bsp file prefix */
+ 	1,					/* bsp file version */
+Index: tools/quake3/q3map2/game_qfusion.h
+===================================================================
+--- tools/quake3/q3map2/game_qfusion.h	(revision 191)
++++ tools/quake3/q3map2/game_qfusion.h	(working copy)
+@@ -115,6 +115,7 @@
+ 	qfalse,				/* wolf lighting model? */
+ 	512,				/* lightmap width/height */
+ 	1.0f,				/* lightmap gamma */
++	1.0f,				/* lightmap exposure */
+ 	1.0f,				/* lightmap compensate */
+ 	"FBSP",				/* bsp file prefix */
+ 	1,					/* bsp file version */
+Index: tools/quake3/q3map2/game_tenebrae.h
+===================================================================
+--- tools/quake3/q3map2/game_tenebrae.h	(revision 191)
++++ tools/quake3/q3map2/game_tenebrae.h	(working copy)
+@@ -112,6 +112,7 @@
+ 	qfalse,				/* wolf lighting model? */
+ 	512,				/* lightmap width/height */
+ 	2.0f,				/* lightmap gamma */
++	1.0f,				/* lightmap exposure */
+ 	1.0f,				/* lightmap compensate */
+ 	"IBSP",				/* bsp file prefix */
+ 	46,					/* bsp file version */
+Index: tools/quake3/q3map2/game_quake3.h
+===================================================================
+--- tools/quake3/q3map2/game_quake3.h	(revision 191)
++++ tools/quake3/q3map2/game_quake3.h	(working copy)
+@@ -112,6 +112,7 @@
+ 	qfalse,				/* wolf lighting model? */
+ 	128,				/* lightmap width/height */
+ 	1.0f,				/* lightmap gamma */
++	1.0f,				/* lightmap exposure */
+ 	1.0f,				/* lightmap compensate */
+ 	"IBSP",				/* bsp file prefix */
+ 	46,					/* bsp file version */
+Index: tools/quake3/q3map2/game_ef.h
+===================================================================
+--- tools/quake3/q3map2/game_ef.h	(revision 191)
++++ tools/quake3/q3map2/game_ef.h	(working copy)
+@@ -113,6 +113,7 @@
+ 	qfalse,				/* wolf lighting model? */
+ 	128,				/* lightmap width/height */
+ 	1.0f,				/* lightmap gamma */
++	1.0f,				/* lightmap exposure */
+ 	1.0f,				/* lightmap compensate */
+ 	"IBSP",				/* bsp file prefix */
+ 	46,					/* bsp file version */
+Index: tools/quake3/q3map2/light_ydnar.c
+===================================================================
+--- tools/quake3/q3map2/light_ydnar.c	(revision 191)
++++ tools/quake3/q3map2/light_ydnar.c	(working copy)
+@@ -49,6 +49,7 @@
+ 	int		i;
+ 	float	max, gamma;
+ 	vec3_t	sample;
++	float 	inv, dif;
+ 	
+ 	
+ 	/* ydnar: scaling necessary for simulating r_overbrightBits on external lightmaps */
+@@ -72,16 +73,51 @@
+ 		/* gamma */
+ 		sample[ i ] = pow( sample[ i ] / 255.0f, gamma ) * 255.0f;
+ 	}
++
++	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;
++		}
++	}
++
+ 	
+-	/* 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 );
+-	
+ 	/* compensate for ingame overbrighting/bitshifting */
+ 	VectorScale( sample, (1.0f / lightmapCompensate), sample );
+ 	
+Index: tools/quake3/q3map2/light.c
+===================================================================
+--- tools/quake3/q3map2/light.c (revision 191)
++++ tools/quake3/q3map2/light.c (working copy)
+@@ -1836,6 +1893,14 @@
+ 			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 ] );
+Index: tools/quake3/q3map2/q3map2.h
+===================================================================
+--- tools/quake3/q3map2/q3map2.h	(revision 191)
++++ tools/quake3/q3map2/q3map2.h	(working copy)
+@@ -543,6 +545,7 @@
+ 	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 */
+ 	char				*bspIdent;						/* 4-letter bsp file prefix */
+ 	int					bspVersion;						/* bsp version to use */
+@@ -2117,6 +2132,7 @@
+ 
+ /* 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 */

Added: trunk/misc/gtkradiant/singlepatches/q3map2-UTtrianglecheck.diff
===================================================================
--- trunk/misc/gtkradiant/singlepatches/q3map2-UTtrianglecheck.diff	                        (rev 0)
+++ trunk/misc/gtkradiant/singlepatches/q3map2-UTtrianglecheck.diff	2008-03-03 11:30:10 UTC (rev 3480)
@@ -0,0 +1,280 @@
+Index: tools/quake3/q3map2/light_ydnar.c
+===================================================================
+--- tools/quake3/q3map2/light_ydnar.c	(revision 191)
++++ tools/quake3/q3map2/light_ydnar.c	(working copy)
+@@ -384,7 +420,7 @@
+ #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 @@
+ 	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 @@
+ 	/* 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)
++	{
++		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,13 @@
+ 	else
+ 		origin[ lm->axisNum ] += lightmapSampleOffset;
+ 	
++	VectorCopy(origin,origintwo);
++	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 +625,14 @@
+ 			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;
+ 		}
+ 	}
+@@ -550,8 +642,8 @@
+ 	{
+ 		VectorMA( dv->xyz, lightmapSampleOffset, dv->normal, nudged );
+ 		pointCluster = ClusterForPointExtFilter( nudged, LUXEL_EPSILON, numClusters, clusters );
+-		if( pointCluster >= 0 )
+-			VectorCopy( nudged, origin );
++		//if( pointCluster >= 0 )
++		//	VectorCopy( nudged, origin );
+ 		luxel[ 1 ] += 1.0f;
+ 	}
+ 	
+@@ -597,7 +689,7 @@
+ 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 +737,7 @@
+ 	
+ 	/* 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 +745,12 @@
+ 	/* 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 +766,7 @@
+ 	int				i;
+ 	vec4_t			plane;
+ 	vec3_t			*stv, *ttv, stvStatic[ 3 ], ttvStatic[ 3 ];
++	vec3_t			worldverts[ 3 ];
+ 	
+ 	
+ 	/* get plane if possible */
+@@ -699,16 +792,20 @@
+ 		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 +827,7 @@
+ 			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 +889,8 @@
+ 	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 +975,10 @@
+ 	}
+ 	
+ 	/* 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 );
+@@ -1173,7 +1270,7 @@
+ 					continue;
+ 				
+ 				/* map the fake vert */
+-				MapSingleLuxel( lm, NULL, &fake, lm->plane, pass, NULL, NULL );
++				MapSingleLuxel( lm, NULL, &fake, lm->plane, pass, NULL, NULL, NULL );
+ 			}
+ 		}
+ 	}
+@@ -1963,22 +2062,32 @@
+ 					deluxel = SUPER_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 ] )
++
++					////////// 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++;
++					}
++					else
++					{
++						/* 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 ] )
++							totalLighted++;
++					}
+ 					
+ 					/* add to light direction map (fixme: use luxel normal as starting point for deluxel?) */
+ 					if( deluxemap )

Added: trunk/misc/gtkradiant/singlepatches/q3map2-decomptexcoords.diff
===================================================================
--- trunk/misc/gtkradiant/singlepatches/q3map2-decomptexcoords.diff	                        (rev 0)
+++ trunk/misc/gtkradiant/singlepatches/q3map2-decomptexcoords.diff	2008-03-03 11:30:10 UTC (rev 3480)
@@ -0,0 +1,264 @@
+Index: tools/quake3/q3map2/convert_map.c
+===================================================================
+--- tools/quake3/q3map2/convert_map.c	(revision 191)
++++ tools/quake3/q3map2/convert_map.c	(working copy)
+@@ -46,6 +46,105 @@
+ #define	SNAP_FLOAT_TO_INT	4
+ #define	SNAP_INT_TO_FLOAT	(1.0 / SNAP_FLOAT_TO_INT)
+ 
++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)
++			{
++				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);
++}
++
+ static void ConvertBrush( FILE *f, int num, bspBrush_t *brush, vec3_t origin )
+ {
+ 	int				i, j;
+@@ -54,12 +153,17 @@
+ 	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" );
++	fprintf( f, "\tbrushDef\n" );
++	fprintf( f, "\t{\n" );
+ 	
+ 	/* clear out build brush */
+ 	for( i = 0; i < buildBrush->numsides; i++ )
+@@ -109,9 +213,88 @@
+ 		/* 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;
++
++		if(vert[0] && vert[1] && vert[2])
++		{
++			int i;
++			vec3_t texX, texY;
++			vec3_t xy1I, xy1J, xy1K;
++			vec2_t stI, stJ, stK;
++			vec_t D, D0, D1, D2;
++
++			ComputeAxisBase(buildPlane->normal, texX, texY);
++
++			VectorSet(xy1I, DotProduct(vert[0]->xyz, texX), DotProduct(vert[0]->xyz, texY), 1);
++			VectorSet(xy1J, DotProduct(vert[1]->xyz, texX), DotProduct(vert[1]->xyz, texY), 1);
++			VectorSet(xy1K, DotProduct(vert[2]->xyz, texX), DotProduct(vert[2]->xyz, texY), 1);
++			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(
++				xy1I[0], xy1I[1], 1,
++				xy1J[0], xy1J[1], 1,
++				xy1K[0], xy1K[1], 1
++			);
++			if(D != 0)
++			{
++				for(i = 0; i < 2; ++i)
++				{
++					D0 = Det3x3(
++						stI[i], xy1I[1], 1,
++						stJ[i], xy1J[1], 1,
++						stK[i], xy1K[1], 1
++					);
++					D1 = Det3x3(
++						xy1I[0], stI[i], 1,
++						xy1J[0], stJ[i], 1,
++						xy1K[0], stK[i], 1
++					);
++					D2 = Det3x3(
++						xy1I[0], xy1I[1], stI[i],
++						xy1J[0], xy1J[1], stJ[i],
++						xy1K[0], xy1K[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], xy1I[0], xy1I[1],
++					vert[1]->xyz[0], vert[1]->xyz[1], vert[1]->xyz[2], xy1J[0], xy1J[1],
++					vert[2]->xyz[0], vert[2]->xyz[1], vert[2]->xyz[2], xy1K[0], xy1K[1]
++					);
++		}
++		else
++			if(strncmp(buildSide->shaderInfo->shader, "textures/common/", 16))
++				fprintf(stderr, "no matching triangle for brushside using %s (hopefully nobody can see this side anyway)\n", buildSide->shaderInfo->shader);
+ 		
+ 		/* get texture name */
+ 		if( !Q_strncasecmp( buildSide->shaderInfo->shader, "textures/", 9 ) )
+@@ -130,14 +313,21 @@
+ 		
+ 		/* 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",
++		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 ],
+-			texture );
++			buildSide->texMat[0][0], buildSide->texMat[0][1], buildSide->texMat[0][2],
++			buildSide->texMat[1][0], buildSide->texMat[1][1], buildSide->texMat[1][2],
++			texture,
++			// DEBUG: valid ? 0 : C_DETAIL
++			0
++			);
++		// TODO write brush primitives format here
+ 	}
+ 	
+ 	/* end brush */
++	fprintf( f, "\t}\n" );
+ 	fprintf( f, "\t}\n\n" );
+ }
+ 
+Index: tools/quake3/q3map2/main.c
+===================================================================
+--- tools/quake3/q3map2/main.c	(revision 191)
++++ tools/quake3/q3map2/main.c	(working copy)
+@@ -541,6 +541,18 @@
+ 					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 );
++ 		}
+ 	}
+ 	
+ 	/* clean up map name */

Added: trunk/misc/gtkradiant/singlepatches/q3map2-snapplane.diff
===================================================================
--- trunk/misc/gtkradiant/singlepatches/q3map2-snapplane.diff	                        (rev 0)
+++ trunk/misc/gtkradiant/singlepatches/q3map2-snapplane.diff	2008-03-03 11:30:10 UTC (rev 3480)
@@ -0,0 +1,292 @@
+Index: tools/quake3/q3map2/model.c
+===================================================================
+--- tools/quake3/q3map2/model.c	(revision 193)
++++ tools/quake3/q3map2/model.c	(working copy)
+@@ -222,6 +222,8 @@
+ 	byte				*color;
+ 	picoIndex_t			*indexes;
+ 	remap_t				*rm, *glob;
++	double				normalEpsilon_save;
++	double				distanceEpsilon_save;
+ 	
+ 	
+ 	/* get model */
+@@ -398,9 +400,8 @@
+ 		/* 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 */
+@@ -437,90 +438,141 @@
+ 					/* 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 ] ) )
++						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;
+ 						}
+ 					}
+ 				}
++
++				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( fabs( plane[ k ] ) > fabs( plane[ (k + 1) % 3 ] ) &&
+-								fabs( plane[ k ] ) > fabs( plane[ (k + 2) % 3 ] ) )
+-							{
+-								backs[ j ][ k ] += plane[ k ] < 0.0f ? 64.0f : -64.0f;
+-								break;
+-							}
+-						}
++
++						// 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 ].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 );
+ 				}
+ 			}
+ 		}
+Index: tools/quake3/q3map2/map.c
+===================================================================
+--- tools/quake3/q3map2/map.c	(revision 193)
++++ tools/quake3/q3map2/map.c	(working copy)
+@@ -184,7 +184,7 @@
+ 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
+@@ -193,7 +193,13 @@
+   SnapPlane reenabled by namespace because of multiple reports of
+   q3map2-crashes which were triggered by this patch.
+ */
++	// div0: ensure the point "center" stays on the plane (actually, this
++	// rotates the plane around the point center).
++	// if center lies on the plane, it is guaranteed to stay on the plane by
++	// this fix.
++	vec_t centerDist = DotProduct(normal, center);
+ 	SnapNormal( normal );
++	*dist += (DotProduct(normal, center) - centerDist);
+ 
+ 	if( fabs( *dist - Q_rint( *dist ) ) < distanceEpsilon )
+ 		*dist = Q_rint( *dist );
+@@ -207,7 +213,7 @@
+ 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 normal, vec_t dist, int numPoints, vec3_t *points ) // NOTE: this has a side effect on the normal. Good or bad?
+ 
+ #ifdef USE_HASHING
+ 
+@@ -215,10 +221,14 @@
+ 	int		i, j, hash, h;
+ 	plane_t	*p;
+ 	vec_t	d;
++	vec3_t centerofweight;
++
++	VectorClear(centerofweight);
++	for(i = 0; i < numPoints; ++i)
++		VectorMA(centerofweight, 1.0 / numPoints, points[i], centerofweight);
+ 	
+-	
+ 	/* hash the plane */
+-	SnapPlane( normal, &dist );
++	SnapPlane( normal, &dist, centerofweight );
+ 	hash = (PLANE_HASHES - 1) & (int) fabs( dist );
+ 	
+ 	/* search the border bins as well */
+@@ -259,7 +269,13 @@
+ 	plane_t	*p;
+ 	
+ 
+-	SnapPlane( normal, &dist );
++	vec3_t centerofweight;
++
++	VectorClear(centerofweight);
++	for(i = 0; i < numPoints; ++i)
++		VectorMA(centerofweight, 1.0 / numPoints, points[i], centerofweight);
++	
++	SnapPlane( normal, &dist, centerofweight );
+ 	for( i = 0, p = mapplanes; i < nummapplanes; i++, p++ )
+ 	{
+ 		if( PlaneEqual( p, normal, dist ) )




More information about the nexuiz-commits mailing list