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,¤tVertexFace ))
++ _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 ] = ∣
+- 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 ] = ∣
+- 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,¤tVertexFace ))
++ _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 ] = ∣
+- 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 ] = ∣
+- 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