1 NOTE: this patch set is autogenerated from the "singlepatches" subdirectory of nexuiz/trunk/misc.
3 Do not commit changes to THIS!
6 sh mergepatches.sh > gtkradiant-nexuiz-patchset.diff
7 before committing new singlepatches!
11 Index: libs/picomodel/pm_ase.c
12 ===================================================================
13 --- libs/picomodel/pm_ase.c (revision 191)
14 +++ libs/picomodel/pm_ase.c (working copy)
17 ----------------------------------------------------------------------------- */
19 +void Sys_Printf (const char *format, ...);
32 picoIndex_t smoothingGroup;
33 picoIndex_t materialId;
34 picoIndex_t subMaterialId;
35 + picoVec3_t facenormal;
36 + picoVec3_t vertexnormal[3];
38 typedef aseFace_t* aseFacesIter_t;
40 @@ -455,33 +457,157 @@
44 +static int VectorCompareExtn( picoVec3_t n1, picoVec3_t n2, float epsilon )
50 + for( i= 0; i < 3; i++ )
51 + if( fabs( n1[ i ] - n2[ i ]) > epsilon )
56 +#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])
58 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 )
60 - aseFacesIter_t i = faces, end = faces + numFaces;
61 - for(; i != end; ++i)
66 + aseFacesIter_t i = faces, end = faces + numFaces;
70 + for(i=faces; i != end; ++i)
75 + //VectorSubtract(va, vb, v1);
76 + //VectorSubtract(vc, vb, v2);
77 + //CrossProduct(v1, v2, out);
80 + picoVec3_t v1,v2,v3;
85 + a[j] = vertices[(*i).indices[0]].xyz[j];
86 + b[j] = vertices[(*i).indices[1]].xyz[j];
87 + c[j] = vertices[(*i).indices[2]].xyz[j];
95 + CrossProductTemp(v1,v2,v3);
96 + _pico_normalize_vec(v3);
97 + (*i).facenormal[0]=v3[0];
98 + (*i).facenormal[1]=v3[1];
99 + (*i).facenormal[2]=v3[2];
105 + //if (counter>0) Sys_Printf( "Rebuilding %d Normals\n", counter * 3 );
106 + for(i=faces; i != end; ++i)
108 - /* look up the shader for the material/submaterial pair */
109 + /* look up the shader for the material/submaterial pair */
110 aseSubMaterial_t* subMtl = _ase_get_submaterial_or_default( materials, (*i).materialId, (*i).subMaterialId );
111 - if( subMtl == NULL )
113 + if( subMtl == NULL )
121 picoVec3_t* normal[3];
123 picoColor_t* color[3];
124 picoIndex_t smooth[3];
126 - /* we pull the data from the vertex, color and texcoord arrays using the face index data */
127 - for ( j = 0 ; j < 3 ; j ++ )
133 + /* we pull the data from the vertex, color and texcoord arrays using the face index data */
134 + for ( j = 0 ; j < 3 ; j ++ )
136 - xyz[j] = &vertices[(*i).indices[j]].xyz;
137 - normal[j] = &vertices[(*i).indices[j]].normal;
138 + aseFacesIter_t q = faces;
139 + aseFacesIter_t qend = faces + numFaces;
141 + xyz[j] = &vertices[(*i).indices[j]].xyz;
144 + normal[j] = &(*i).facenormal;
147 + //Oooor we can use the smoothing group
149 + //Slow method, but testing
150 + //Find All faces that use this vertex, average their facenormals.
151 + // skip where smoothgroups both equal 0, or don't have any shared bits (x & y)
152 + index=(*i).indices[j];
157 + accum[0]=(*i).facenormal[0];
158 + accum[1]=(*i).facenormal[1];
159 + accum[2]=(*i).facenormal[2];
164 + for(; q != qend; ++q)
169 + // if ( (*q).indices[0]==index || (*q).indices[1]==index || (*q).indices[2]==index)
170 + a[0]= &vertices[(*q).indices[0] ].xyz;
171 + a[1]= &vertices[(*q).indices[1] ].xyz;
172 + a[2]= &vertices[(*q).indices[2] ].xyz;
174 + if ( VectorCompareExtn(*a[0],*xyz[j],0.01f)>0 ||
175 + VectorCompareExtn(*a[1],*xyz[j],0.01f)>0 ||
176 + VectorCompareExtn(*a[2],*xyz[j],0.01f)>0
179 + if ( (*i).smoothingGroup==0 && (*q).smoothingGroup ==0 )
182 + if ( (*i).smoothingGroup & (*q).smoothingGroup )
184 + accum[0]+=(*q).facenormal[0];
185 + accum[1]+=(*q).facenormal[1];
186 + accum[2]+=(*q).facenormal[2];
193 + _pico_normalize_vec(accum);
195 + (*i).vertexnormal[j][0]=accum[0];
196 + (*i).vertexnormal[j][1]=accum[1];
197 + (*i).vertexnormal[j][2]=accum[2];
198 + normal[j]=&(*i).vertexnormal[j];
201 st[j] = &texcoords[(*i).indices[j + 3]].texcoord;
203 - if( colors != NULL && (*i).indices[j + 6] >= 0 )
205 + if( colors != NULL && (*i).indices[j + 6] >= 0 )
207 color[j] = &colors[(*i).indices[j + 6]].color;
209 @@ -490,30 +616,18 @@
213 - smooth[j] = (vertices[(*i).indices[j]].id * (1 << 16)) + (*i).smoothingGroup; /* don't merge vertices */
214 + smooth[j] = 0;// (vertices[(*i).indices[j]].id * (1 << 16)) + (*i).smoothingGroup; /* don't merge vertices */
218 /* submit the triangle to the model */
219 PicoAddTriangleToModel ( model , xyz , normal , 1 , st , 1 , color , subMtl->shader, smooth );
225 -static void shadername_convert(char* shaderName)
227 - /* unix-style path separators */
228 - char* s = shaderName;
229 - for(; *s != '\0'; ++s)
240 * loads a 3dsmax ase model file.
243 int numColorVertices = 0;
244 int numColorVertexFaces = 0;
246 + int currentVertexFace=0;
247 + int currentVertexIndex=0;
250 aseMaterial_t* materials = NULL;
252 @@ -610,10 +727,11 @@
254 else if (!_pico_stricmp(p->token,"*mesh_numvertex"))
256 - if (!_pico_parse_int( p, &numVertices) )
257 + if (!_pico_parse_int( p, &numVertices) )
258 _ase_error_return("Missing MESH_NUMVERTEX value");
260 vertices = _pico_calloc(numVertices, sizeof(aseVertex_t));
261 + currentVertexIndex=0;
263 else if (!_pico_stricmp(p->token,"*mesh_numfaces"))
266 _ase_error_return("Missing MESH_NUMFACES value");
268 faces = _pico_calloc(numFaces, sizeof(aseFace_t));
271 else if (!_pico_stricmp(p->token,"*mesh_numtvertex"))
275 vertices[index].id = vertexId++;
277 - /* model mesh vertex normal */
278 + else if (!_pico_stricmp(p->token,"*mesh_facenormal"))
280 + //Grab the faceindex for the next vertex normals.
281 + if( numVertices == 0 )
282 + _ase_error_return("Vertex parse error (facenormals)");
284 + if (!_pico_parse_int( p,¤tVertexFace ))
285 + _ase_error_return("Vertex parse error");
287 + if (!_pico_parse_vec( p,faces[currentVertexFace].facenormal ))
288 + _ase_error_return("Vertex parse error");
291 + /* model mesh vertex normal */
292 else if (!_pico_stricmp(p->token,"*mesh_vertexnormal"))
295 @@ -696,10 +828,25 @@
296 /* get vertex data (orig: index +y -x +z) */
297 if (!_pico_parse_int( p,&index ))
298 _ase_error_return("Vertex parse error");
299 - if (!_pico_parse_vec( p,vertices[index].normal ))
301 + //^^ Index is 'wrong' in .ase models. they reference the same vert index with multiple normals..
302 + // I've tried, this is a lost cause. Use the SG's
306 + if (!_pico_parse_vec( p,vertices[counter].normal ))
307 _ase_error_return("Vertex parse error");
308 + vertices[counter].faceid=index;
312 /* model mesh face */
313 + else if (!_pico_stricmp(p->token,"*mesh_normals"))
315 + // counter=0; //part of the above vertex normals fix
318 + /* model mesh face */
319 else if (!_pico_stricmp(p->token,"*mesh_face"))
321 picoIndex_t indexes[3];
324 if (!_pico_stricmp (p->token,"*MESH_SMOOTHING" ))
326 - _pico_parse_int ( p , &faces[index].smoothingGroup );
335 + faces[index].smoothingGroup=0;
337 + //Super dodgy comma delimited string parse
340 + if (*point<=32 || *point==',')
345 + faces[index].smoothingGroup+=1<<total;
357 if (!_pico_stricmp (p->token,"*MESH_MTLID" ))
359 _pico_parse_int ( p , &faces[index].subMaterialId );
360 @@ -755,19 +929,19 @@
363 if( numVertices == 0 )
364 - _ase_error_return("Texture Vertex parse error");
365 + _ase_error_return("Vertex parse error");
367 /* get uv vertex index */
368 - if (!_pico_parse_int( p,&index ) || index >= numTextureVertices)
369 - _ase_error_return("Texture vertex parse error");
370 + if (!_pico_parse_int( p,&index ))
371 + _ase_error_return("UV vertex parse error");
373 /* get uv vertex s */
374 if (!_pico_parse_float( p,&texcoords[index].texcoord[0] ))
375 - _ase_error_return("Texture vertex parse error");
376 + _ase_error_return("UV vertex parse error");
378 /* get uv vertex t */
379 if (!_pico_parse_float( p,&texcoords[index].texcoord[1] ))
380 - _ase_error_return("Texture vertex parse error");
381 + _ase_error_return("UV vertex parse error");
383 /* ydnar: invert t */
384 texcoords[index].texcoord[ 1 ] = 1.0f - texcoords[index].texcoord[ 1 ];
385 @@ -831,6 +1005,13 @@
387 /* leave alpha alone since we don't get any data from the ASE format */
388 colors[index].color[3] = 255;
390 + /* 27 hack, red as alpha */
391 + colors[index].color[3]=colors[index].color[0];
392 + colors[index].color[0]=255;
393 + colors[index].color[1]=255;
394 + colors[index].color[2]=255;
397 /* model color face */
398 else if (!_pico_stricmp(p->token,"*mesh_cface"))
401 /* set material name */
402 _pico_first_token( materialName );
403 - shadername_convert(materialName);
404 PicoSetShaderName( shader, materialName);
406 /* set shader's transparency */
407 @@ -1085,7 +1265,6 @@
410 /* set material name */
411 - shadername_convert(materialName);
412 PicoSetShaderName( shader,materialName );
414 /* set shader's transparency */
415 @@ -1115,8 +1294,18 @@
418 /* convert to shader-name format */
419 - shadername_convert(mapname);
421 + /* unix-style path separators */
423 + for(; *s != '\0'; ++s)
432 /* remove extension */
433 char* last_period = strrchr(p, '.');
434 if(last_period != NULL)
435 @@ -1125,14 +1314,32 @@
439 - /* find shader path */
440 + /* find game root */
441 for(; *p != '\0'; ++p)
443 - if(_pico_strnicmp(p, "models/", 7) == 0 || _pico_strnicmp(p, "textures/", 9) == 0)
444 + if(_pico_strnicmp(p, "quake", 5) == 0 || _pico_strnicmp(p, "doom", 4) == 0)
449 + /* root-relative */
450 + for(; *p != '\0'; ++p)
458 + /* game-relative */
459 + for(; *p != '\0'; ++p)
470 Index: libs/picomodel/picomodel.c
471 ===================================================================
472 --- libs/picomodel/picomodel.c (revision 191)
473 +++ libs/picomodel/picomodel.c (working copy)
475 model = PicoModuleLoadModel(module, fileName, buffer, bufSize, frameNum);
480 - _pico_free(buffer);
482 + _pico_free(buffer);
486 @@ -1573,6 +1570,7 @@
490 +// Sys_Printf(" %f %f %f\n", normal[0] , normal[1] , normal[2] );
493 if( surface == NULL || surface->numVertexes <= 0 )
494 @@ -1861,13 +1859,10 @@
495 typedef picoVec3_t* picoNormalIter_t;
496 typedef picoIndex_t* picoIndexIter_t;
498 -#define THE_CROSSPRODUCTS_OF_ANY_PAIR_OF_EDGES_OF_A_GIVEN_TRIANGLE_ARE_EQUAL 1
500 void _pico_triangles_generate_weighted_normals(picoIndexIter_t first, picoIndexIter_t end, picoVec3_t* xyz, picoVec3_t* normals)
502 for(; first != end; first += 3)
504 -#if (THE_CROSSPRODUCTS_OF_ANY_PAIR_OF_EDGES_OF_A_GIVEN_TRIANGLE_ARE_EQUAL)
505 picoVec3_t weightedNormal;
507 float* a = xyz[*(first + 0)];
508 @@ -1878,24 +1873,11 @@
509 _pico_subtract_vec( c, a, ca );
510 _pico_cross_vec( ca, ba, weightedNormal );
517 float* normal = normals[*(first + j)];
518 -#if (!THE_CROSSPRODUCTS_OF_ANY_PAIR_OF_EDGES_OF_A_GIVEN_TRIANGLE_ARE_EQUAL)
519 - picoVec3_t weightedNormal;
521 - float* a = xyz[*(first + ((j + 0) % 3))];
522 - float* b = xyz[*(first + ((j + 1) % 3))];
523 - float* c = xyz[*(first + ((j + 2) % 3))];
525 - _pico_subtract_vec( b, a, ba );
526 - _pico_subtract_vec( c, a, ca );
527 - _pico_cross_vec( ca, ba, weightedNormal );
530 _pico_add_vec(weightedNormal, normal, normal);
533 @@ -1941,7 +1923,8 @@
535 for(; first != last; ++first, ++generated)
537 - if(!_pico_normal_is_unit_length(*first) || !_pico_normal_within_tolerance(*first, *generated))
538 + //27 - fix for badly generated normals thing.
539 + // if(!_pico_normal_is_unit_length(*first) || !_pico_normal_within_tolerance(*first, *generated))
541 _pico_copy_vec(*generated, *first);
543 @@ -1954,10 +1937,11 @@
545 _pico_normals_zero(normals, normals + surface->numVertexes);
547 + //Just build standard no sg normals for now
548 _pico_triangles_generate_weighted_normals(surface->index, surface->index + surface->numIndexes, surface->xyz, normals);
549 _pico_vertices_combine_shared_normals(surface->xyz, surface->smoothingGroup, normals, surface->numVertexes);
551 - _pico_normals_normalize(normals, normals + surface->numVertexes);
552 + _pico_normals_normalize(normals, normals + surface->numVertexes);
554 _pico_normals_assign_generated_normals(surface->normal, surface->normal + surface->numVertexes, normals);
556 @@ -2261,7 +2245,7 @@
557 int newVertIndex = PicoGetSurfaceNumIndexes ( workSurface );
559 /* get the index of the vertex that we're going to store at newVertIndex */
560 - vertDataIndex = PicoFindSurfaceVertexNum ( workSurface , *xyz[i] , *normals[i] , numSTs , st[i] , numColors , colors[i], smoothingGroup[i]);
561 + vertDataIndex = -1;// PicoFindSurfaceVertexNum ( workSurface , *xyz[i] , *normals[i] , numSTs , st[i] , numColors , colors[i], smoothingGroup[i]);
563 /* the vertex wasn't found, so create a new vertex in the pool from the data we have */
564 if ( vertDataIndex == -1 )
565 @@ -2290,3 +2274,5 @@
566 PicoSetSurfaceIndex ( workSurface , newVertIndex , vertDataIndex );
571 Index: libs/picomodel/pm_obj.c
572 ===================================================================
573 --- libs/picomodel/pm_obj.c (revision 193)
574 +++ libs/picomodel/pm_obj.c (working copy)
576 /* get next token in material file */
577 if (_pico_parse( p,1 ) == NULL)
582 /* skip empty lines */
583 if (p->token == NULL || !strlen( p->token ))
585 else if (!_pico_stricmp(p->token,"map_kd"))
588 + picoShader_t *shader;
590 /* pointer to current shader must be valid */
591 if (curShader == NULL)
593 _pico_printf( PICO_ERROR,"Missing material map name in MTL, line %d.",p->curLine);
594 _obj_mtl_error_return;
596 + /* create a new pico shader */
597 + shader = PicoNewShader( model );
598 + if (shader == NULL)
599 + _obj_mtl_error_return;
600 /* set shader map name */
601 PicoSetShaderMapName( shader,mapName );
604 PicoSetModelFileName( model,fileName );
606 /* try loading the materials; we don't handle the result */
609 _obj_mtl_load( model );
616 + else if (!_pico_stricmp(p->token,"usemtl"))
618 + picoShader_t *shader;
621 + /* get material name */
622 + name = _pico_parse( p,0 );
624 + /* validate material name */
625 + if (name == NULL || !strlen(name))
627 + _pico_printf( PICO_ERROR,"Missing material name in OBJ, line %d.",p->curLine);
631 + shader = PicoFindShader( model, name, 1 );
632 + if (shader == NULL)
634 + _pico_printf( PICO_ERROR,"Undefined material name in OBJ, line %d. Making a default shader.",p->curLine);
636 + /* create a new pico shader */
637 + shader = PicoNewShader( model );
638 + if (shader != NULL)
640 + PicoSetShaderName( shader,name );
641 + PicoSetShaderMapName( shader,name );
642 + PicoSetSurfaceShader( curSurface, shader );
647 + PicoSetSurfaceShader( curSurface, shader );
651 /* skip unparsed rest of line and continue */
652 _pico_parse_skip_rest( p );
654 Index: plugins/model/model.cpp
655 ===================================================================
656 --- plugins/model/model.cpp (revision 193)
657 +++ plugins/model/model.cpp (working copy)
658 @@ -123,14 +123,27 @@
660 glVertexPointer(3, GL_FLOAT, sizeof(ArbitraryMeshVertex), &m_vertices.data()->vertex);
661 glDrawElements(GL_TRIANGLES, GLsizei(m_indices.size()), RenderIndexTypeID, m_indices.data());
664 + GLfloat modelview[16];
665 + glGetFloatv(GL_MODELVIEW_MATRIX, modelview); // I know this is slow as hell, but hey - we're in _DEBUG
666 + Matrix4 modelview_inv(
667 + modelview[0], modelview[1], modelview[2], modelview[3],
668 + modelview[4], modelview[5], modelview[6], modelview[7],
669 + modelview[8], modelview[9], modelview[10], modelview[11],
670 + modelview[12], modelview[13], modelview[14], modelview[15]);
671 + matrix4_full_invert(modelview_inv);
672 + Matrix4 modelview_inv_transposed = matrix4_transposed(modelview_inv);
676 for(Array<ArbitraryMeshVertex>::const_iterator i = m_vertices.begin(); i != m_vertices.end(); ++i)
678 - Vector3 normal = vector3_added(vertex3f_to_vector3((*i).vertex), vector3_scaled(normal3f_to_vector3((*i).normal), 8));
679 + Vector3 normal = normal3f_to_vector3((*i).normal);
680 + normal = matrix4_transformed_direction(modelview_inv, vector3_normalised(matrix4_transformed_direction(modelview_inv_transposed, normal))); // do some magic
681 + Vector3 normalTransformed = vector3_added(vertex3f_to_vector3((*i).vertex), vector3_scaled(normal, 8));
682 glVertex3fv(vertex3f_to_array((*i).vertex));
683 - glVertex3fv(vector3_to_array(normal));
684 + glVertex3fv(vector3_to_array(normalTransformed));
688 Index: games/NexuizPack/games/nexuiz.game
689 ===================================================================
690 --- games/NexuizPack/games/nexuiz.game (revision 26)
691 +++ games/NexuizPack/games/nexuiz.game (working copy)
695 texturetypes="tga jpg png"
696 - modeltypes="md3 mdl md2 ase"
697 + modeltypes="md3 mdl md2 ase obj"
701 Index: tools/quake3/q3map2/shaders.c
702 ===================================================================
703 --- tools/quake3/q3map2/shaders.c (revision 191)
704 +++ tools/quake3/q3map2/shaders.c (working copy)
708 if( VectorLength( si->color ) <= 0.0f )
710 ColorNormalize( color, si->color );
711 - VectorScale( color, (1.0f / count), si->averageColor );
712 + VectorScale( color, (1.0f / count), si->averageColor );
716 + VectorCopy( si->color, si->averageColor );
721 Index: tools/quake3/q3map2/light_ydnar.c
722 ===================================================================
723 --- tools/quake3/q3map2/light_ydnar.c (revision 191)
724 +++ tools/quake3/q3map2/light_ydnar.c (working copy)
725 @@ -1767,6 +1864,8 @@
726 float tests[ 4 ][ 2 ] = { { 0.0f, 0 }, { 1, 0 }, { 0, 1 }, { 1, 1 } };
728 float stackLightLuxels[ STACK_LL_SIZE ];
733 /* bail if this number exceeds the number of raw lightmaps */
734 @@ -2223,6 +2332,78 @@
735 FreeTraceLights( &trace );
737 /* -----------------------------------------------------------------
739 + ----------------------------------------------------------------- */
743 + /* walk lightmaps */
744 + for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
747 + if( lm->superLuxels[ lightmapNum ] == NULL )
750 + /* apply floodlight to each luxel */
751 + for( y = 0; y < lm->sh; y++ )
753 + for( x = 0; x < lm->sw; x++ )
756 + cluster = SUPER_CLUSTER( x, y );
757 + //% if( *cluster < 0 )
760 + /* get particulars */
761 + luxel = SUPER_LUXEL( lightmapNum, x, y );
762 + floodlight = SUPER_FLOODLIGHT( x, y );
764 + flood[0]=floodlightRGB[0]*floodlightIntensity;
765 + flood[1]=floodlightRGB[1]*floodlightIntensity;
766 + flood[2]=floodlightRGB[2]*floodlightIntensity;
768 + /* scale light value */
769 + VectorScale( flood, *floodlight, flood );
770 + luxel[0]+=flood[0];
771 + luxel[1]+=flood[1];
772 + luxel[2]+=flood[2];
774 + if (luxel[3]==0) luxel[3]=1;
782 + for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
785 + if( lm->superLuxels[ lightmapNum ] == NULL )
788 + for( y = 0; y < lm->sh; y++ )
790 + for( x = 0; x < lm->sw; x++ )
793 + cluster = SUPER_CLUSTER( x, y );
794 + //% if( *cluster < 0 )
797 + /* get particulars */
798 + luxel = SUPER_LUXEL( lightmapNum, x, y );
799 + normal = SUPER_NORMAL ( x, y );
801 + luxel[0]=(normal[0]*127)+127;
802 + luxel[1]=(normal[1]*127)+127;
803 + luxel[2]=(normal[2]*127)+127;
809 + /* -----------------------------------------------------------------
811 ----------------------------------------------------------------- */
813 @@ -3587,7 +3768,320 @@
814 CreateTraceLightsForBounds( mins, maxs, normal, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ], LIGHT_SURFACES, trace );
817 +/////////////////////////////////////////////////////////////
819 +#define FLOODLIGHT_CONE_ANGLE 88 /* degrees */
820 +#define FLOODLIGHT_NUM_ANGLE_STEPS 16
821 +#define FLOODLIGHT_NUM_ELEVATION_STEPS 4
822 +#define FLOODLIGHT_NUM_VECTORS (FLOODLIGHT_NUM_ANGLE_STEPS * FLOODLIGHT_NUM_ELEVATION_STEPS)
824 +static vec3_t floodVectors[ FLOODLIGHT_NUM_VECTORS ];
825 +static int numFloodVectors = 0;
827 +void SetupFloodLight( void )
830 + float angle, elevation, angleStep, elevationStep;
832 + double v1,v2,v3,v4,v5;
835 + Sys_FPrintf( SYS_VRB, "--- SetupFloodLight ---\n" );
837 + /* calculate angular steps */
838 + angleStep = DEG2RAD( 360.0f / FLOODLIGHT_NUM_ANGLE_STEPS );
839 + elevationStep = DEG2RAD( FLOODLIGHT_CONE_ANGLE / FLOODLIGHT_NUM_ELEVATION_STEPS );
841 + /* iterate angle */
843 + for( i = 0, angle = 0.0f; i < FLOODLIGHT_NUM_ANGLE_STEPS; i++, angle += angleStep )
845 + /* iterate elevation */
846 + for( j = 0, elevation = elevationStep * 0.5f; j < FLOODLIGHT_NUM_ELEVATION_STEPS; j++, elevation += elevationStep )
848 + floodVectors[ numFloodVectors ][ 0 ] = sin( elevation ) * cos( angle );
849 + floodVectors[ numFloodVectors ][ 1 ] = sin( elevation ) * sin( angle );
850 + floodVectors[ numFloodVectors ][ 2 ] = cos( elevation );
855 + /* emit some statistics */
856 + Sys_FPrintf( SYS_VRB, "%9d numFloodVectors\n", numFloodVectors );
859 + value = ValueForKey( &entities[ 0 ], "_floodlight" );
861 + if( value[ 0 ] != '\0' )
864 + v4=floodlightDistance;
865 + v5=floodlightIntensity;
867 + sscanf( value, "%lf %lf %lf %lf %lf", &v1, &v2, &v3, &v4, &v5);
869 + floodlightRGB[0]=v1;
870 + floodlightRGB[1]=v2;
871 + floodlightRGB[2]=v3;
873 + if (VectorLength(floodlightRGB)==0)
875 + VectorSet(floodlightRGB,240,240,255);
881 + floodlightDistance=v4;
882 + floodlightIntensity=v5;
884 + floodlighty = qtrue;
885 + Sys_Printf( "FloodLighting enabled via worldspawn _floodlight key.\n" );
889 + VectorSet(floodlightRGB,240,240,255);
890 + //floodlighty = qtrue;
891 + //Sys_Printf( "FloodLighting enabled via worldspawn _floodlight key.\n" );
893 + VectorNormalize(floodlightRGB,floodlightRGB);
896 +//27 - lighttracer style ambient occlusion light hack.
897 +//Kudos to the dirtmapping author for most of this source.
898 +void FloodLightRawLightmap( int rawLightmapNum )
900 + int i, x, y, sx, sy, *cluster;
901 + float *origin, *normal, *floodlight, *floodlight2, average, samples;
903 + surfaceInfo_t *info;
906 + /* bail if this number exceeds the number of raw lightmaps */
907 + if( rawLightmapNum >= numRawLightmaps )
911 + lm = &rawLightmaps[ rawLightmapNum ];
913 + memset(&trace,0,sizeof(trace_t));
915 + trace.testOcclusion = qtrue;
916 + trace.forceSunlight = qfalse;
917 + trace.twoSided = qtrue;
918 + trace.recvShadows = lm->recvShadows;
919 + trace.numSurfaces = lm->numLightSurfaces;
920 + trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];
921 + trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
922 + trace.testAll = qfalse;
923 + trace.distance = 1024;
925 + /* twosided lighting (may or may not be a good idea for lightmapped stuff) */
926 + //trace.twoSided = qfalse;
927 + for( i = 0; i < trace.numSurfaces; i++ )
930 + info = &surfaceInfos[ trace.surfaces[ i ] ];
932 + /* check twosidedness */
933 + if( info->si->twoSided )
935 + trace.twoSided = qtrue;
941 + for( y = 0; y < lm->sh; y++ )
943 + for( x = 0; x < lm->sw; x++ )
946 + cluster = SUPER_CLUSTER( x, y );
947 + origin = SUPER_ORIGIN( x, y );
948 + normal = SUPER_NORMAL( x, y );
949 + floodlight = SUPER_FLOODLIGHT( x, y );
951 + /* set default dirt */
952 + *floodlight = 0.0f;
954 + /* only look at mapped luxels */
958 + /* copy to trace */
959 + trace.cluster = *cluster;
960 + VectorCopy( origin, trace.origin );
961 + VectorCopy( normal, trace.normal );
966 + *floodlight = FloodLightForSample( &trace );
970 + /* testing no filtering */
973 + /* filter "dirt" */
974 + for( y = 0; y < lm->sh; y++ )
976 + for( x = 0; x < lm->sw; x++ )
979 + cluster = SUPER_CLUSTER( x, y );
980 + floodlight = SUPER_FLOODLIGHT( x, y );
982 + /* filter dirt by adjacency to unmapped luxels */
983 + average = *floodlight;
985 + for( sy = (y - 1); sy <= (y + 1); sy++ )
987 + if( sy < 0 || sy >= lm->sh )
990 + for( sx = (x - 1); sx <= (x + 1); sx++ )
992 + if( sx < 0 || sx >= lm->sw || (sx == x && sy == y) )
995 + /* get neighboring luxel */
996 + cluster = SUPER_CLUSTER( sx, sy );
997 + floodlight2 = SUPER_FLOODLIGHT( sx, sy );
998 + if( *cluster < 0 || *floodlight2 <= 0.0f )
1002 + average += *floodlight2;
1007 + if( samples <= 0.0f )
1012 + if( samples <= 0.0f )
1016 + *floodlight = average / samples;
1022 +FloodLightForSample()
1023 +calculates floodlight value for a given sample
1024 +once again, kudos to the dirtmapping coder
1026 +float FloodLightForSample( trace_t *trace )
1030 + float contribution;
1032 + float gatherLight, outLight;
1033 + vec3_t normal, worldUp, myUp, myRt, direction, displacement;
1041 + if( trace == NULL || trace->cluster < 0 )
1046 + dd = floodlightDistance;
1047 + VectorCopy( trace->normal, normal );
1049 + /* check if the normal is aligned to the world-up */
1050 + if( normal[ 0 ] == 0.0f && normal[ 1 ] == 0.0f )
1052 + if( normal[ 2 ] == 1.0f )
1054 + VectorSet( myRt, 1.0f, 0.0f, 0.0f );
1055 + VectorSet( myUp, 0.0f, 1.0f, 0.0f );
1057 + else if( normal[ 2 ] == -1.0f )
1059 + VectorSet( myRt, -1.0f, 0.0f, 0.0f );
1060 + VectorSet( myUp, 0.0f, 1.0f, 0.0f );
1065 + VectorSet( worldUp, 0.0f, 0.0f, 1.0f );
1066 + CrossProduct( normal, worldUp, myRt );
1067 + VectorNormalize( myRt, myRt );
1068 + CrossProduct( myRt, normal, myUp );
1069 + VectorNormalize( myUp, myUp );
1072 + /* iterate through ordered vectors */
1073 + for( i = 0; i < numFloodVectors; i++ )
1075 + if (floodlight_lowquality==qtrue)
1077 + if (rand()%10 != 0 ) continue;
1082 + /* transform vector into tangent space */
1083 + direction[ 0 ] = myRt[ 0 ] * floodVectors[ i ][ 0 ] + myUp[ 0 ] * floodVectors[ i ][ 1 ] + normal[ 0 ] * floodVectors[ i ][ 2 ];
1084 + direction[ 1 ] = myRt[ 1 ] * floodVectors[ i ][ 0 ] + myUp[ 1 ] * floodVectors[ i ][ 1 ] + normal[ 1 ] * floodVectors[ i ][ 2 ];
1085 + direction[ 2 ] = myRt[ 2 ] * floodVectors[ i ][ 0 ] + myUp[ 2 ] * floodVectors[ i ][ 1 ] + normal[ 2 ] * floodVectors[ i ][ 2 ];
1087 + /* set endpoint */
1088 + VectorMA( trace->origin, dd, direction, trace->end );
1090 + //VectorMA( trace->origin, 1, direction, trace->origin );
1092 + SetupTrace( trace );
1094 + TraceLine( trace );
1097 + if (trace->compileFlags & C_SKY )
1099 + contribution=1.0f;
1101 + else if ( trace->opaque )
1103 + VectorSubtract( trace->hit, trace->origin, displacement );
1104 + d=VectorLength( displacement );
1106 + // d=trace->distance;
1107 + //if (d>256) gatherDirt+=1;
1108 + contribution=d/dd;
1109 + if (contribution>1) contribution=1.0f;
1111 + //gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
1114 + gatherLight+=contribution;
1118 + if( gatherLight <= 0.0f )
1124 + gatherLight/=(sub);
1126 + outLight=gatherLight;
1127 + if( outLight > 1.0f )
1130 + /* return to sender */
1134 Index: tools/quake3/q3map2/light.c
1135 ===================================================================
1136 --- tools/quake3/q3map2/light.c (revision 191)
1137 +++ tools/quake3/q3map2/light.c (working copy)
1138 @@ -1378,6 +1378,56 @@
1142 + /////// Floodlighting for point //////////////////
1143 + //do our floodlight ambient occlusion loop, and add a single contribution based on the brightest dir
1149 + col[0]=col[1]=col[2]=floodlightIntensity;
1153 + trace.testOcclusion = qtrue;
1154 + trace.forceSunlight = qfalse;
1155 + trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
1156 + trace.testAll = qtrue;
1160 + if (q==0) //upper hemisphere
1162 + trace.normal[0]=0;
1163 + trace.normal[1]=0;
1164 + trace.normal[2]=1;
1166 + else //lower hemisphere
1168 + trace.normal[0]=0;
1169 + trace.normal[1]=0;
1170 + trace.normal[2]=-1;
1173 + f = FloodLightForSample(&trace);
1175 + contributions[ numCon ].color[0]=col[0]*f;
1176 + contributions[ numCon ].color[1]=col[1]*f;
1177 + contributions[ numCon ].color[2]=col[2]*f;
1179 + contributions[ numCon ].dir[0]=dir[0];
1180 + contributions[ numCon ].dir[1]=dir[1];
1181 + contributions[ numCon ].dir[2]=dir[2];
1183 + contributions[ numCon ].style = 0;
1185 + /* push average direction around */
1186 + addSize = VectorLength( col );
1187 + VectorMA( gp->dir, addSize, dir, gp->dir );
1190 + /////////////////////
1192 /* normalize to get primary light direction */
1193 VectorNormalize( gp->dir, gp->dir );
1195 @@ -1661,6 +1711,12 @@
1196 RunThreadsOnIndividual( numRawLightmaps, qtrue, DirtyRawLightmap );
1199 + /* floodlight them up */
1202 + Sys_Printf( "--- FloodlightRawLightmap ---\n" );
1203 + RunThreadsOnIndividual( numRawLightmaps, qtrue, FloodLightRawLightmap );
1206 /* ydnar: set up light envelopes */
1207 SetupEnvelopes( qfalse, fast );
1208 @@ -1703,6 +1759,7 @@
1211 VectorClear( ambientColor );
1212 + floodlighty = false;
1214 /* generate diffuse lights */
1216 @@ -2191,6 +2256,21 @@
1218 Sys_Printf( "Enabling Challenge Pro Mode Asstacular Vertex Lighting Mode (tm)\n" );
1220 + else if( !strcmp( argv[ i ], "-floodlight" ) )
1222 + floodlighty = qtrue;
1223 + Sys_Printf( "FloodLighting enabled\n" );
1225 + else if( !strcmp( argv[ i ], "-debugnormals" ) )
1227 + debugnormals = qtrue;
1228 + Sys_Printf( "DebugNormals enabled\n" );
1230 + else if( !strcmp( argv[ i ], "-lowquality" ) )
1232 + floodlight_lowquality = qtrue;
1233 + Sys_Printf( "Low Quality FloodLighting enabled\n" );
1236 /* r7: dirtmapping */
1237 else if( !strcmp( argv[ i ], "-dirty" ) )
1238 @@ -2279,6 +2359,7 @@
1239 /* ydnar: set up optimization */
1242 + SetupFloodLight();
1243 SetupSurfaceLightmaps();
1245 /* initialize the surface facet tracing */
1246 Index: tools/quake3/q3map2/lightmaps_ydnar.c
1247 ===================================================================
1248 --- tools/quake3/q3map2/lightmaps_ydnar.c (revision 191)
1249 +++ tools/quake3/q3map2/lightmaps_ydnar.c (working copy)
1250 @@ -414,6 +414,12 @@
1251 lm->superNormals = safe_malloc( size );
1252 memset( lm->superNormals, 0, size );
1254 + /* allocate floodlight map storage */
1255 + size = lm->sw * lm->sh * SUPER_FLOODLIGHT_SIZE * sizeof( float );
1256 + if( lm->superFloodLight == NULL )
1257 + lm->superFloodLight = safe_malloc( size );
1258 + memset( lm->superFloodLight, 0, size );
1260 /* allocate cluster map storage */
1261 size = lm->sw * lm->sh * sizeof( int );
1262 if( lm->superClusters == NULL )
1263 Index: tools/quake3/q3map2/q3map2.h
1264 ===================================================================
1265 --- tools/quake3/q3map2/q3map2.h (revision 191)
1266 +++ tools/quake3/q3map2/q3map2.h (working copy)
1268 #define SUPER_NORMAL_SIZE 4
1269 #define SUPER_DELUXEL_SIZE 3
1270 #define BSP_DELUXEL_SIZE 3
1271 +#define SUPER_FLOODLIGHT_SIZE 1
1273 #define VERTEX_LUXEL( s, v ) (vertexLuxels[ s ] + ((v) * VERTEX_LUXEL_SIZE))
1274 #define RAD_VERTEX_LUXEL( s, v )(radVertexLuxels[ s ] + ((v) * VERTEX_LUXEL_SIZE))
1276 #define SUPER_ORIGIN( x, y ) (lm->superOrigins + ((((y) * lm->sw) + (x)) * SUPER_ORIGIN_SIZE))
1277 #define SUPER_NORMAL( x, y ) (lm->superNormals + ((((y) * lm->sw) + (x)) * SUPER_NORMAL_SIZE))
1278 #define SUPER_DIRT( x, y ) (lm->superNormals + ((((y) * lm->sw) + (x)) * SUPER_NORMAL_SIZE) + 3) /* stash dirtyness in normal[ 3 ] */
1279 +#define SUPER_FLOODLIGHT( x, y ) (lm->superFloodLight + ((((y) * lm->sw) + (x)) * SUPER_FLOODLIGHT_SIZE) )
1283 @@ -1392,6 +1395,7 @@
1285 float *superDeluxels; /* average light direction */
1287 + float *superFloodLight;
1291 @@ -1704,6 +1708,10 @@
1292 float DirtForSample( trace_t *trace );
1293 void DirtyRawLightmap( int num );
1295 +void SetupFloodLight();
1296 +float FloodLightForSample( trace_t *trace );
1297 +void FloodLightRawLightmap( int num );
1299 void IlluminateRawLightmap( int num );
1300 void IlluminateVertexes( int num );
1302 @@ -2098,6 +2106,13 @@
1303 Q_EXTERN float dirtScale Q_ASSIGN( 1.0f );
1304 Q_EXTERN float dirtGain Q_ASSIGN( 1.0f );
1306 +Q_EXTERN qboolean debugnormals Q_ASSIGN( qfalse );
1307 +Q_EXTERN qboolean floodlighty Q_ASSIGN( qfalse );
1308 +Q_EXTERN qboolean floodlight_lowquality Q_ASSIGN( qfalse );
1309 +Q_EXTERN vec3_t floodlightRGB;
1310 +Q_EXTERN float floodlightIntensity Q_ASSIGN( 512 );
1311 +Q_EXTERN float floodlightDistance Q_ASSIGN( 1024 );
1313 Q_EXTERN qboolean dump Q_ASSIGN( qfalse );
1314 Q_EXTERN qboolean debug Q_ASSIGN( qfalse );
1315 Q_EXTERN qboolean debugUnused Q_ASSIGN( qfalse );
1316 Index: tools/quake3/q3map2/game_ja.h
1317 ===================================================================
1318 --- tools/quake3/q3map2/game_ja.h (revision 191)
1319 +++ tools/quake3/q3map2/game_ja.h (working copy)
1321 qfalse, /* wolf lighting model? */
1322 128, /* lightmap width/height */
1323 1.0f, /* lightmap gamma */
1324 + 1.0f, /* lightmap exposure */
1325 1.0f, /* lightmap compensate */
1326 "RBSP", /* bsp file prefix */
1327 1, /* bsp file version */
1328 Index: tools/quake3/q3map2/game_tremulous.h
1329 ===================================================================
1330 --- tools/quake3/q3map2/game_tremulous.h (revision 191)
1331 +++ tools/quake3/q3map2/game_tremulous.h (working copy)
1333 qfalse, /* wolf lighting model? */
1334 128, /* lightmap width/height */
1335 1.0f, /* lightmap gamma */
1336 + 1.0f, /* lightmap exposure */
1337 1.0f, /* lightmap compensate */
1338 "IBSP", /* bsp file prefix */
1339 46, /* bsp file version */
1340 Index: tools/quake3/q3map2/game_wolfet.h
1341 ===================================================================
1342 --- tools/quake3/q3map2/game_wolfet.h (revision 191)
1343 +++ tools/quake3/q3map2/game_wolfet.h (working copy)
1345 qtrue, /* wolf lighting model? */
1346 128, /* lightmap width/height */
1347 1.0f, /* lightmap gamma */
1348 + 1.0f, /* lightmap exposure */
1349 1.0f, /* lightmap compensate */
1350 "IBSP", /* bsp file prefix */
1351 47, /* bsp file version */
1352 Index: tools/quake3/q3map2/game_wolf.h
1353 ===================================================================
1354 --- tools/quake3/q3map2/game_wolf.h (revision 191)
1355 +++ tools/quake3/q3map2/game_wolf.h (working copy)
1357 qtrue, /* wolf lighting model? */
1358 128, /* lightmap width/height */
1359 1.0f, /* lightmap gamma */
1360 + 1.0f, /* lightmap exposure */
1361 1.0f, /* lightmap compensate */
1362 "IBSP", /* bsp file prefix */
1363 47, /* bsp file version */
1364 Index: tools/quake3/q3map2/game_sof2.h
1365 ===================================================================
1366 --- tools/quake3/q3map2/game_sof2.h (revision 191)
1367 +++ tools/quake3/q3map2/game_sof2.h (working copy)
1369 qfalse, /* wolf lighting model? */
1370 128, /* lightmap width/height */
1371 1.0f, /* lightmap gamma */
1372 + 1.0f, /* lightmap exposure */
1373 1.0f, /* lightmap compensate */
1374 "RBSP", /* bsp file prefix */
1375 1, /* bsp file version */
1376 Index: tools/quake3/q3map2/game_etut.h
1377 ===================================================================
1378 --- tools/quake3/q3map2/game_etut.h (revision 191)
1379 +++ tools/quake3/q3map2/game_etut.h (working copy)
1381 qfalse, /* wolf lighting model? */
1382 128, /* lightmap width/height */
1383 2.2f, /* lightmap gamma */
1384 + 1.0f, /* lightmap exposure */
1385 1.0f, /* lightmap compensate */
1386 "IBSP", /* bsp file prefix */
1387 47, /* bsp file version */
1388 Index: tools/quake3/q3map2/game_jk2.h
1389 ===================================================================
1390 --- tools/quake3/q3map2/game_jk2.h (revision 191)
1391 +++ tools/quake3/q3map2/game_jk2.h (working copy)
1393 qfalse, /* wolf lighting model? */
1394 128, /* lightmap width/height */
1395 1.0f, /* lightmap gamma */
1396 + 1.0f, /* lightmap exposure */
1397 1.0f, /* lightmap compensate */
1398 "RBSP", /* bsp file prefix */
1399 1, /* bsp file version */
1400 Index: tools/quake3/q3map2/game_qfusion.h
1401 ===================================================================
1402 --- tools/quake3/q3map2/game_qfusion.h (revision 191)
1403 +++ tools/quake3/q3map2/game_qfusion.h (working copy)
1405 qfalse, /* wolf lighting model? */
1406 512, /* lightmap width/height */
1407 1.0f, /* lightmap gamma */
1408 + 1.0f, /* lightmap exposure */
1409 1.0f, /* lightmap compensate */
1410 "FBSP", /* bsp file prefix */
1411 1, /* bsp file version */
1412 Index: tools/quake3/q3map2/game_tenebrae.h
1413 ===================================================================
1414 --- tools/quake3/q3map2/game_tenebrae.h (revision 191)
1415 +++ tools/quake3/q3map2/game_tenebrae.h (working copy)
1417 qfalse, /* wolf lighting model? */
1418 512, /* lightmap width/height */
1419 2.0f, /* lightmap gamma */
1420 + 1.0f, /* lightmap exposure */
1421 1.0f, /* lightmap compensate */
1422 "IBSP", /* bsp file prefix */
1423 46, /* bsp file version */
1424 Index: tools/quake3/q3map2/game_quake3.h
1425 ===================================================================
1426 --- tools/quake3/q3map2/game_quake3.h (revision 191)
1427 +++ tools/quake3/q3map2/game_quake3.h (working copy)
1429 qfalse, /* wolf lighting model? */
1430 128, /* lightmap width/height */
1431 1.0f, /* lightmap gamma */
1432 + 1.0f, /* lightmap exposure */
1433 1.0f, /* lightmap compensate */
1434 "IBSP", /* bsp file prefix */
1435 46, /* bsp file version */
1436 Index: tools/quake3/q3map2/game_ef.h
1437 ===================================================================
1438 --- tools/quake3/q3map2/game_ef.h (revision 191)
1439 +++ tools/quake3/q3map2/game_ef.h (working copy)
1441 qfalse, /* wolf lighting model? */
1442 128, /* lightmap width/height */
1443 1.0f, /* lightmap gamma */
1444 + 1.0f, /* lightmap exposure */
1445 1.0f, /* lightmap compensate */
1446 "IBSP", /* bsp file prefix */
1447 46, /* bsp file version */
1448 Index: tools/quake3/q3map2/light_ydnar.c
1449 ===================================================================
1450 --- tools/quake3/q3map2/light_ydnar.c (revision 191)
1451 +++ tools/quake3/q3map2/light_ydnar.c (working copy)
1459 /* ydnar: scaling necessary for simulating r_overbrightBits on external lightmaps */
1462 sample[ i ] = pow( sample[ i ] / 255.0f, gamma ) * 255.0f;
1465 + if (lightmapExposure == 1)
1467 + /* clamp with color normalization */
1468 + max = sample[ 0 ];
1469 + if( sample[ 1 ] > max )
1470 + max = sample[ 1 ];
1471 + if( sample[ 2 ] > max )
1472 + max = sample[ 2 ];
1473 + if( max > 255.0f )
1474 + VectorScale( sample, (255.0f / max), sample );
1478 + if (lightmapExposure==0)
1480 + lightmapExposure=1.0f;
1482 + inv=1.f/lightmapExposure;
1485 + max = sample[ 0 ];
1486 + if( sample[ 1 ] > max )
1487 + max = sample[ 1 ];
1488 + if( sample[ 2 ] > max )
1489 + max = sample[ 2 ];
1491 + dif = (1- exp(-max * inv) ) * 255;
1509 - /* clamp with color normalization */
1510 - max = sample[ 0 ];
1511 - if( sample[ 1 ] > max )
1512 - max = sample[ 1 ];
1513 - if( sample[ 2 ] > max )
1514 - max = sample[ 2 ];
1515 - if( max > 255.0f )
1516 - VectorScale( sample, (255.0f / max), sample );
1518 /* compensate for ingame overbrighting/bitshifting */
1519 VectorScale( sample, (1.0f / lightmapCompensate), sample );
1521 Index: tools/quake3/q3map2/light.c
1522 ===================================================================
1523 --- tools/quake3/q3map2/light.c (revision 191)
1524 +++ tools/quake3/q3map2/light.c (working copy)
1525 @@ -1836,6 +1893,14 @@
1529 + else if( !strcmp( argv[ i ], "-exposure" ) )
1531 + f = atof( argv[ i + 1 ] );
1532 + lightmapExposure = f;
1533 + Sys_Printf( "Lighting exposure set to %f\n", lightmapExposure );
1537 else if( !strcmp( argv[ i ], "-compensate" ) )
1539 f = atof( argv[ i + 1 ] );
1540 Index: tools/quake3/q3map2/q3map2.h
1541 ===================================================================
1542 --- tools/quake3/q3map2/q3map2.h (revision 191)
1543 +++ tools/quake3/q3map2/q3map2.h (working copy)
1545 qboolean wolfLight; /* when true, lights work like wolf q3map */
1546 int lightmapSize; /* bsp lightmap width/height */
1547 float lightmapGamma; /* default lightmap gamma */
1548 + float lightmapExposure; /* default lightmap exposure */
1549 float lightmapCompensate; /* default lightmap compensate value */
1550 char *bspIdent; /* 4-letter bsp file prefix */
1551 int bspVersion; /* bsp version to use */
1552 @@ -2117,6 +2132,7 @@
1554 /* ydnar: lightmap gamma/compensation */
1555 Q_EXTERN float lightmapGamma Q_ASSIGN( 1.0f );
1556 +Q_EXTERN float lightmapExposure Q_ASSIGN( 1.0f );
1557 Q_EXTERN float lightmapCompensate Q_ASSIGN( 1.0f );
1559 /* ydnar: for runtime tweaking of falloff tolerance */
1560 Index: tools/quake3/q3map2/light_ydnar.c
1561 ===================================================================
1562 --- tools/quake3/q3map2/light_ydnar.c (revision 191)
1563 +++ tools/quake3/q3map2/light_ydnar.c (working copy)
1566 #define BOGUS_NUDGE -99999.0f
1568 -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 ] )
1569 +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 ] )
1571 int i, x, y, numClusters, *clusters, pointCluster, *cluster;
1572 float *luxel, *origin, *normal, d, lightmapSampleOffset;
1573 @@ -392,6 +428,12 @@
1577 + vec3_t cverts[ 3 ];
1579 + vec4_t sideplane, hostplane;
1584 static float nudges[][ 2 ] =
1586 @@ -485,6 +527,51 @@
1587 /* non axial lightmap projection (explicit xyz) */
1589 VectorCopy( dv->xyz, origin );
1591 + //////////////////////
1592 + //27's test to make sure samples stay within the triangle boundaries
1593 + //1) Test the sample origin to see if it lays on the wrong side of any edge (x/y)
1594 + //2) if it does, nudge it onto the correct side.
1596 + if (worldverts!=NULL)
1600 + VectorCopy(worldverts[j],cverts[j]);
1602 + PlaneFromPoints(hostplane,cverts[0],cverts[1],cverts[2]);
1608 + //build plane using 2 edges and a normal
1611 + VectorCopy(cverts[next],temp);
1612 + VectorAdd(temp,hostplane,temp);
1613 + PlaneFromPoints(sideplane,cverts[i],cverts[ next ], temp);
1615 + //planetest sample point
1616 + e=DotProduct(origin,sideplane);
1621 + //VectorClear(origin);
1622 + //Move the sample point back inside triangle bounds
1623 + origin[0]-=sideplane[0]*(e+1);
1624 + origin[1]-=sideplane[1]*(e+1);
1625 + origin[2]-=sideplane[2]*(e+1);
1627 + VectorClear(origin);
1634 + ////////////////////////
1636 /* planar surfaces have precalculated lightmap vectors for nudging */
1637 if( lm->plane != NULL )
1638 @@ -516,8 +603,13 @@
1640 origin[ lm->axisNum ] += lightmapSampleOffset;
1642 + VectorCopy(origin,origintwo);
1643 + origintwo[0]+=vecs[2][0];
1644 + origintwo[1]+=vecs[2][1];
1645 + origintwo[2]+=vecs[2][2];
1648 - pointCluster = ClusterForPointExtFilter( origin, LUXEL_EPSILON, numClusters, clusters );
1649 + pointCluster = ClusterForPointExtFilter( origintwo, LUXEL_EPSILON, numClusters, clusters );
1651 /* another retarded hack, storing nudge count in luxel[ 1 ] */
1653 @@ -533,14 +625,14 @@
1654 for( i = 0; i < 3; i++ )
1656 /* set nudged point*/
1657 - nudged[ i ] = origin[ i ] + (nudge[ 0 ] * vecs[ 0 ][ i ]) + (nudge[ 1 ] * vecs[ 1 ][ i ]);
1658 + nudged[ i ] = origintwo[ i ] + (nudge[ 0 ] * vecs[ 0 ][ i ]) + (nudge[ 1 ] * vecs[ 1 ][ i ]);
1662 /* get pvs cluster */
1663 pointCluster = ClusterForPointExtFilter( nudged, LUXEL_EPSILON, numClusters, clusters ); //% + 0.625 );
1664 - if( pointCluster >= 0 )
1665 - VectorCopy( nudged, origin );
1666 + //if( pointCluster >= 0 )
1667 + // VectorCopy( nudged, origin );
1673 VectorMA( dv->xyz, lightmapSampleOffset, dv->normal, nudged );
1674 pointCluster = ClusterForPointExtFilter( nudged, LUXEL_EPSILON, numClusters, clusters );
1675 - if( pointCluster >= 0 )
1676 - VectorCopy( nudged, origin );
1677 + //if( pointCluster >= 0 )
1678 + // VectorCopy( nudged, origin );
1683 than the distance between two luxels (thanks jc :)
1686 -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 ] )
1687 +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 ] )
1689 bspDrawVert_t mid, *dv2[ 3 ];
1693 /* split the longest edge and map it */
1694 LerpDrawVert( dv[ max ], dv[ (max + 1) % 3 ], &mid );
1695 - MapSingleLuxel( lm, info, &mid, plane, 1, stv, ttv );
1696 + MapSingleLuxel( lm, info, &mid, plane, 1, stv, ttv, worldverts );
1698 /* push the point up a little bit to account for fp creep (fixme: revisit this) */
1699 //% VectorMA( mid.xyz, 2.0f, mid.normal, mid.xyz );
1700 @@ -653,12 +745,12 @@
1701 /* recurse to first triangle */
1702 VectorCopy( dv, dv2 );
1704 - MapTriangle_r( lm, info, dv2, plane, stv, ttv );
1705 + MapTriangle_r( lm, info, dv2, plane, stv, ttv, worldverts );
1707 /* recurse to second triangle */
1708 VectorCopy( dv, dv2 );
1709 dv2[ (max + 1) % 3 ] = ∣
1710 - MapTriangle_r( lm, info, dv2, plane, stv, ttv );
1711 + MapTriangle_r( lm, info, dv2, plane, stv, ttv, worldverts );
1718 vec3_t *stv, *ttv, stvStatic[ 3 ], ttvStatic[ 3 ];
1719 + vec3_t worldverts[ 3 ];
1722 /* get plane if possible */
1723 @@ -699,16 +792,20 @@
1727 + VectorCopy( dv[ 0 ]->xyz, worldverts[ 0 ] );
1728 + VectorCopy( dv[ 1 ]->xyz, worldverts[ 1 ] );
1729 + VectorCopy( dv[ 2 ]->xyz, worldverts[ 2 ] );
1731 /* map the vertexes */
1732 - MapSingleLuxel( lm, info, dv[ 0 ], plane, 1, stv, ttv );
1733 - MapSingleLuxel( lm, info, dv[ 1 ], plane, 1, stv, ttv );
1734 - MapSingleLuxel( lm, info, dv[ 2 ], plane, 1, stv, ttv );
1735 + MapSingleLuxel( lm, info, dv[ 0 ], plane, 1, stv, ttv, worldverts );
1736 + MapSingleLuxel( lm, info, dv[ 1 ], plane, 1, stv, ttv, worldverts );
1737 + MapSingleLuxel( lm, info, dv[ 2 ], plane, 1, stv, ttv, worldverts );
1739 /* 2002-11-20: prefer axial triangle edges */
1742 /* subdivide the triangle */
1743 - MapTriangle_r( lm, info, dv, plane, stv, ttv );
1744 + MapTriangle_r( lm, info, dv, plane, stv, ttv, worldverts );
1749 dv2[ 2 ] = dv[ (i + 1) % 3 ];
1751 /* map the degenerate triangle */
1752 - MapTriangle_r( lm, info, dv2, plane, stv, ttv );
1753 + MapTriangle_r( lm, info, dv2, plane, stv, ttv, worldverts );
1758 LerpDrawVert( dv[ max + 2 ], dv[ (max + 3) % 4 ], &mid[ 1 ] );
1760 /* map the vertexes */
1761 - MapSingleLuxel( lm, info, &mid[ 0 ], plane, 1, stv, ttv );
1762 - MapSingleLuxel( lm, info, &mid[ 1 ], plane, 1, stv, ttv );
1763 + MapSingleLuxel( lm, info, &mid[ 0 ], plane, 1, stv, ttv, NULL );
1764 + MapSingleLuxel( lm, info, &mid[ 1 ], plane, 1, stv, ttv, NULL );
1768 @@ -878,10 +975,10 @@
1771 /* map the vertexes */
1772 - MapSingleLuxel( lm, info, dv[ 0 ], plane, 1, stv, ttv );
1773 - MapSingleLuxel( lm, info, dv[ 1 ], plane, 1, stv, ttv );
1774 - MapSingleLuxel( lm, info, dv[ 2 ], plane, 1, stv, ttv );
1775 - MapSingleLuxel( lm, info, dv[ 3 ], plane, 1, stv, ttv );
1776 + MapSingleLuxel( lm, info, dv[ 0 ], plane, 1, stv, ttv, NULL );
1777 + MapSingleLuxel( lm, info, dv[ 1 ], plane, 1, stv, ttv, NULL );
1778 + MapSingleLuxel( lm, info, dv[ 2 ], plane, 1, stv, ttv, NULL );
1779 + MapSingleLuxel( lm, info, dv[ 3 ], plane, 1, stv, ttv, NULL );
1781 /* subdivide the quad */
1782 MapQuad_r( lm, info, dv, plane, stv, ttv );
1783 @@ -1173,7 +1270,7 @@
1786 /* map the fake vert */
1787 - MapSingleLuxel( lm, NULL, &fake, lm->plane, pass, NULL, NULL );
1788 + MapSingleLuxel( lm, NULL, &fake, lm->plane, pass, NULL, NULL, NULL );
1792 @@ -1963,22 +2062,32 @@
1793 deluxel = SUPER_DELUXEL( x, y );
1794 origin = SUPER_ORIGIN( x, y );
1795 normal = SUPER_NORMAL( x, y );
1797 - /* set contribution count */
1798 - lightLuxel[ 3 ] = 1.0f;
1801 - trace.cluster = *cluster;
1802 - VectorCopy( origin, trace.origin );
1803 - VectorCopy( normal, trace.normal );
1805 - /* get light for this sample */
1806 - LightContributionToSample( &trace );
1807 - VectorCopy( trace.color, lightLuxel );
1809 - /* add to count */
1810 - if( trace.color[ 0 ] || trace.color[ 1 ] || trace.color[ 2 ] )
1812 + ////////// 27's temp hack for testing edge clipping ////
1813 + if( origin[0]==0 && origin[1]==0 && origin[2]==0 )
1815 + lightLuxel[ 1 ] = 255;
1816 + lightLuxel[ 3 ] = 1.0f;
1821 + /* set contribution count */
1822 + lightLuxel[ 3 ] = 1.0f;
1825 + trace.cluster = *cluster;
1826 + VectorCopy( origin, trace.origin );
1827 + VectorCopy( normal, trace.normal );
1829 + /* get light for this sample */
1830 + LightContributionToSample( &trace );
1831 + VectorCopy( trace.color, lightLuxel );
1833 + /* add to count */
1834 + if( trace.color[ 0 ] || trace.color[ 1 ] || trace.color[ 2 ] )
1838 /* add to light direction map (fixme: use luxel normal as starting point for deluxel?) */
1840 Index: tools/quake3/q3map2/convert_map.c
1841 ===================================================================
1842 --- tools/quake3/q3map2/convert_map.c (revision 191)
1843 +++ tools/quake3/q3map2/convert_map.c (working copy)
1845 #define SNAP_FLOAT_TO_INT 4
1846 #define SNAP_INT_TO_FLOAT (1.0 / SNAP_FLOAT_TO_INT)
1848 +typedef vec_t vec2_t[2];
1850 +static vec_t Det3x3(vec_t a00, vec_t a01, vec_t a02,
1851 + vec_t a10, vec_t a11, vec_t a12,
1852 + vec_t a20, vec_t a21, vec_t a22)
1855 + a00 * (a11 * a22 - a12 * a21)
1856 + - a01 * (a10 * a22 - a12 * a20)
1857 + + a02 * (a10 * a21 - a11 * a20);
1860 +void GetBestSurfaceTriangleMatchForBrushside(side_t *buildSide, bspDrawVert_t *bestVert[3])
1862 + bspDrawSurface_t *s;
1868 + vec3_t v1v0, v2v0, norm;
1869 + bspDrawVert_t *vert[3];
1870 + winding_t *polygon;
1871 + plane_t *buildPlane = &mapplanes[buildSide->planenum];
1874 + // first, start out with NULLs
1875 + bestVert[0] = bestVert[1] = bestVert[2] = NULL;
1877 + // brute force through all surfaces
1878 + for(s = bspDrawSurfaces; s != bspDrawSurfaces + numBSPDrawSurfaces; ++s)
1880 + if(s->surfaceType != MST_PLANAR && s->surfaceType != MST_TRIANGLE_SOUP)
1882 + if(strcmp(buildSide->shaderInfo->shader, bspShaders[s->shaderNum].shader))
1884 + for(t = 0; t + 3 <= s->numIndexes; t += 3)
1886 + vert[0] = &bspDrawVerts[s->firstVert + bspDrawIndexes[s->firstIndex + t + 0]];
1887 + vert[1] = &bspDrawVerts[s->firstVert + bspDrawIndexes[s->firstIndex + t + 1]];
1888 + vert[2] = &bspDrawVerts[s->firstVert + bspDrawIndexes[s->firstIndex + t + 2]];
1889 + if(s->surfaceType == MST_PLANAR)
1891 + VectorSubtract(vert[0]->normal, buildPlane->normal, normdiff); if(VectorLength(normdiff) >= normalEpsilon) continue;
1892 + VectorSubtract(vert[1]->normal, buildPlane->normal, normdiff); if(VectorLength(normdiff) >= normalEpsilon) continue;
1893 + VectorSubtract(vert[2]->normal, buildPlane->normal, normdiff); if(VectorLength(normdiff) >= normalEpsilon) continue;
1897 + // this is more prone to roundoff errors, but with embedded
1898 + // models, there is no better way
1899 + VectorSubtract(vert[1]->xyz, vert[0]->xyz, v1v0);
1900 + VectorSubtract(vert[2]->xyz, vert[0]->xyz, v2v0);
1901 + CrossProduct(v2v0, v1v0, norm);
1902 + VectorNormalize(norm, norm);
1903 + VectorSubtract(norm, buildPlane->normal, normdiff); if(VectorLength(normdiff) >= normalEpsilon) continue;
1905 + if(abs(DotProduct(vert[0]->xyz, buildPlane->normal) - buildPlane->dist) >= distanceEpsilon) continue;
1906 + if(abs(DotProduct(vert[1]->xyz, buildPlane->normal) - buildPlane->dist) >= distanceEpsilon) continue;
1907 + if(abs(DotProduct(vert[2]->xyz, buildPlane->normal) - buildPlane->dist) >= distanceEpsilon) continue;
1908 + // Okay. Correct surface type, correct shader, correct plane. Let's start with the business...
1909 + polygon = CopyWinding(buildSide->winding);
1910 + for(i = 0; i < 3; ++i)
1915 + vec3_t *v1 = &vert[(i+1)%3]->xyz;
1916 + vec3_t *v2 = &vert[(i+2)%3]->xyz;
1919 + vec3_t sideDirection;
1920 + // we now need to generate triNormal and triDist so that they represent the plane spanned by normal and (v2 - v1).
1921 + VectorSubtract(*v2, *v1, sideDirection);
1922 + CrossProduct(sideDirection, buildPlane->normal, triNormal);
1923 + triDist = DotProduct(*v1, triNormal);
1924 + ChopWindingInPlace(&polygon, triNormal, triDist, distanceEpsilon);
1928 + thisarea = WindingArea(polygon);
1931 + if(thisarea > best)
1934 + bestVert[0] = vert[0];
1935 + bestVert[1] = vert[1];
1936 + bestVert[2] = vert[2];
1938 + FreeWinding(polygon);
1943 + //if(strncmp(buildSide->shaderInfo->shader, "textures/common/", 16))
1944 + // fprintf(stderr, "brushside with %s: %d matches (%f area)\n", buildSide->shaderInfo->shader, matches, best);
1947 static void ConvertBrush( FILE *f, int num, bspBrush_t *brush, vec3_t origin )
1950 @@ -54,12 +153,17 @@
1951 bspShader_t *shader;
1954 + plane_t *buildPlane;
1956 + bspDrawVert_t *vert[3];
1961 fprintf( f, "\t// brush %d\n", num );
1962 fprintf( f, "\t{\n" );
1963 + fprintf( f, "\tbrushDef\n" );
1964 + fprintf( f, "\t{\n" );
1966 /* clear out build brush */
1967 for( i = 0; i < buildBrush->numsides; i++ )
1968 @@ -109,9 +213,88 @@
1969 /* get build side */
1970 buildSide = &buildBrush->sides[ i ];
1973 + buildPlane = &mapplanes[ buildSide->planenum ];
1976 if( buildSide->shaderInfo == NULL || buildSide->winding == NULL )
1979 + // st-texcoords -> texMat block
1980 + // start out with dummy
1981 + VectorSet(buildSide->texMat[0], 1/32.0, 0, 0);
1982 + VectorSet(buildSide->texMat[1], 0, 1/32.0, 0);
1984 + // find surface for this side (by brute force)
1985 + // surface format:
1986 + // - meshverts point in pairs of three into verts
1988 + // - find the triangle that has most in common with our side
1989 + GetBestSurfaceTriangleMatchForBrushside(buildSide, vert);
1992 + if(vert[0] && vert[1] && vert[2])
1995 + vec3_t texX, texY;
1996 + vec3_t xy1I, xy1J, xy1K;
1997 + vec2_t stI, stJ, stK;
1998 + vec_t D, D0, D1, D2;
2000 + ComputeAxisBase(buildPlane->normal, texX, texY);
2002 + VectorSet(xy1I, DotProduct(vert[0]->xyz, texX), DotProduct(vert[0]->xyz, texY), 1);
2003 + VectorSet(xy1J, DotProduct(vert[1]->xyz, texX), DotProduct(vert[1]->xyz, texY), 1);
2004 + VectorSet(xy1K, DotProduct(vert[2]->xyz, texX), DotProduct(vert[2]->xyz, texY), 1);
2005 + stI[0] = vert[0]->st[0]; stI[1] = vert[0]->st[1];
2006 + stJ[0] = vert[1]->st[0]; stJ[1] = vert[1]->st[1];
2007 + stK[0] = vert[2]->st[0]; stK[1] = vert[2]->st[1];
2009 + // - solve linear equations:
2010 + // - (x, y) := xyz . (texX, texY)
2011 + // - st[i] = texMat[i][0]*x + texMat[i][1]*y + texMat[i][2]
2012 + // (for three vertices)
2014 + xy1I[0], xy1I[1], 1,
2015 + xy1J[0], xy1J[1], 1,
2016 + xy1K[0], xy1K[1], 1
2020 + for(i = 0; i < 2; ++i)
2023 + stI[i], xy1I[1], 1,
2024 + stJ[i], xy1J[1], 1,
2025 + stK[i], xy1K[1], 1
2028 + xy1I[0], stI[i], 1,
2029 + xy1J[0], stJ[i], 1,
2030 + xy1K[0], stK[i], 1
2033 + xy1I[0], xy1I[1], stI[i],
2034 + xy1J[0], xy1J[1], stJ[i],
2035 + xy1K[0], xy1K[1], stK[i]
2037 + VectorSet(buildSide->texMat[i], D0 / D, D1 / D, D2 / D);
2042 + 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",
2043 + buildPlane->normal[0], buildPlane->normal[1], buildPlane->normal[2],
2044 + vert[0]->normal[0], vert[0]->normal[1], vert[0]->normal[2],
2045 + texX[0], texX[1], texX[2], texY[0], texY[1], texY[2],
2046 + vert[0]->xyz[0], vert[0]->xyz[1], vert[0]->xyz[2], xy1I[0], xy1I[1],
2047 + vert[1]->xyz[0], vert[1]->xyz[1], vert[1]->xyz[2], xy1J[0], xy1J[1],
2048 + vert[2]->xyz[0], vert[2]->xyz[1], vert[2]->xyz[2], xy1K[0], xy1K[1]
2052 + if(strncmp(buildSide->shaderInfo->shader, "textures/common/", 16))
2053 + fprintf(stderr, "no matching triangle for brushside using %s (hopefully nobody can see this side anyway)\n", buildSide->shaderInfo->shader);
2055 /* get texture name */
2056 if( !Q_strncasecmp( buildSide->shaderInfo->shader, "textures/", 9 ) )
2057 @@ -130,14 +313,21 @@
2059 /* print brush side */
2060 /* ( 640 24 -224 ) ( 448 24 -224 ) ( 448 -232 -224 ) common/caulk 0 48 0 0.500000 0.500000 0 0 0 */
2061 - 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",
2062 + fprintf( f, "\t\t( %.3f %.3f %.3f ) ( %.3f %.3f %.3f ) ( %.3f %.3f %.3f ) ( ( %.8f %.8f %.8f ) ( %.8f %.8f %.8f ) ) %s %d 0 0\n",
2063 pts[ 0 ][ 0 ], pts[ 0 ][ 1 ], pts[ 0 ][ 2 ],
2064 pts[ 1 ][ 0 ], pts[ 1 ][ 1 ], pts[ 1 ][ 2 ],
2065 pts[ 2 ][ 0 ], pts[ 2 ][ 1 ], pts[ 2 ][ 2 ],
2067 + buildSide->texMat[0][0], buildSide->texMat[0][1], buildSide->texMat[0][2],
2068 + buildSide->texMat[1][0], buildSide->texMat[1][1], buildSide->texMat[1][2],
2070 + // DEBUG: valid ? 0 : C_DETAIL
2073 + // TODO write brush primitives format here
2077 + fprintf( f, "\t}\n" );
2078 fprintf( f, "\t}\n\n" );
2081 Index: tools/quake3/q3map2/main.c
2082 ===================================================================
2083 --- tools/quake3/q3map2/main.c (revision 191)
2084 +++ tools/quake3/q3map2/main.c (working copy)
2085 @@ -541,6 +541,18 @@
2086 Sys_Printf( "Unknown conversion format \"%s\". Defaulting to ASE.\n", argv[ i ] );
2089 + else if( !strcmp( argv[ i ], "-ne" ) )
2091 + normalEpsilon = atof( argv[ i + 1 ] );
2093 + Sys_Printf( "Normal epsilon set to %f\n", normalEpsilon );
2095 + else if( !strcmp( argv[ i ], "-de" ) )
2097 + distanceEpsilon = atof( argv[ i + 1 ] );
2099 + Sys_Printf( "Distance epsilon set to %f\n", distanceEpsilon );
2103 /* clean up map name */
2104 Index: tools/quake3/q3map2/model.c
2105 ===================================================================
2106 --- tools/quake3/q3map2/model.c (revision 193)
2107 +++ tools/quake3/q3map2/model.c (working copy)
2110 picoIndex_t *indexes;
2112 + double normalEpsilon_save;
2113 + double distanceEpsilon_save;
2118 /* ydnar: giant hack land: generate clipping brushes for model triangles */
2119 if( si->clipModel || (spawnFlags & 2) ) /* 2nd bit */
2121 - vec3_t points[ 3 ], backs[ 3 ];
2122 + vec3_t points[ 4 ], backs[ 3 ];
2123 vec4_t plane, reverse, pa, pb, pc;
2128 @@ -437,90 +438,141 @@
2129 /* note: this doesn't work as well as simply using the plane of the triangle, below */
2130 for( k = 0; k < 3; k++ )
2132 - if( fabs( dv->normal[ k ] ) > fabs( dv->normal[ (k + 1) % 3 ] ) &&
2133 - fabs( dv->normal[ k ] ) > fabs( dv->normal[ (k + 2) % 3 ] ) )
2134 + if( fabs( dv->normal[ k ] ) >= fabs( dv->normal[ (k + 1) % 3 ] ) &&
2135 + fabs( dv->normal[ k ] ) >= fabs( dv->normal[ (k + 2) % 3 ] ) )
2137 backs[ j ][ k ] += dv->normal[ k ] < 0.0f ? 64.0f : -64.0f;
2143 + VectorCopy( points[0], points[3] ); // for cyclic usage
2145 /* make plane for triangle */
2146 + // div0: add some extra spawnflags:
2147 + // 0: snap normals to axial planes for extrusion
2148 + // 8: extrude with the original normals
2149 + // 16: extrude only with up/down normals (ideal for terrain)
2150 + // 24: extrude by distance zero (may need engine changes)
2151 if( PlaneFromPoints( plane, points[ 0 ], points[ 1 ], points[ 2 ] ) )
2153 + vec3_t bestNormal;
2154 + float backPlaneDistance = 2;
2156 + if(spawnFlags & 8) // use a DOWN normal
2158 + if(spawnFlags & 16)
2160 + // 24: normal as is, and zero width (broken)
2161 + VectorCopy(plane, bestNormal);
2165 + // 8: normal as is
2166 + VectorCopy(plane, bestNormal);
2171 + if(spawnFlags & 16)
2173 + // 16: UP/DOWN normal
2174 + VectorSet(bestNormal, 0, 0, (plane[2] >= 0 ? 1 : -1));
2178 + // 0: axial normal
2179 + if(fabs(plane[0]) > fabs(plane[1])) // x>y
2180 + if(fabs(plane[1]) > fabs(plane[2])) // x>y, y>z
2181 + VectorSet(bestNormal, (plane[0] >= 0 ? 1 : -1), 0, 0);
2183 + if(fabs(plane[0]) > fabs(plane[2])) // x>z, z>=y
2184 + VectorSet(bestNormal, (plane[0] >= 0 ? 1 : -1), 0, 0);
2186 + VectorSet(bestNormal, 0, 0, (plane[2] >= 0 ? 1 : -1));
2188 + if(fabs(plane[1]) > fabs(plane[2])) // y>z, y>=x
2189 + VectorSet(bestNormal, 0, (plane[1] >= 0 ? 1 : -1), 0);
2190 + else // z>=y, y>=x
2191 + VectorSet(bestNormal, 0, 0, (plane[2] >= 0 ? 1 : -1));
2195 + /* build a brush */
2196 + buildBrush = AllocBrush( 48 );
2197 + buildBrush->entityNum = mapEntityNum;
2198 + buildBrush->original = buildBrush;
2199 + buildBrush->contentShader = si;
2200 + buildBrush->compileFlags = si->compileFlags;
2201 + buildBrush->contentFlags = si->contentFlags;
2202 + normalEpsilon_save = normalEpsilon;
2203 + distanceEpsilon_save = distanceEpsilon;
2204 + if(si->compileFlags & C_STRUCTURAL) // allow forced structural brushes here
2206 + buildBrush->detail = qfalse;
2208 + // only allow EXACT matches when snapping for these (this is mostly for caulk brushes inside a model)
2209 + if(normalEpsilon > 0)
2210 + normalEpsilon = 0;
2211 + if(distanceEpsilon > 0)
2212 + distanceEpsilon = 0;
2215 + buildBrush->detail = qtrue;
2217 /* regenerate back points */
2218 for( j = 0; j < 3; j++ )
2221 dv = &ds->verts[ ds->indexes[ i + j ] ];
2224 - VectorCopy( dv->xyz, backs[ j ] );
2226 - /* find nearest axial to plane normal and push back points opposite */
2227 - for( k = 0; k < 3; k++ )
2229 - if( fabs( plane[ k ] ) > fabs( plane[ (k + 1) % 3 ] ) &&
2230 - fabs( plane[ k ] ) > fabs( plane[ (k + 2) % 3 ] ) )
2232 - backs[ j ][ k ] += plane[ k ] < 0.0f ? 64.0f : -64.0f;
2237 + // shift by some units
2238 + VectorMA(dv->xyz, -64.0f, bestNormal, backs[j]); // 64 prevents roundoff errors a bit
2242 /* make back plane */
2243 VectorScale( plane, -1.0f, reverse );
2244 - reverse[ 3 ] = -(plane[ 3 ] - 1);
2246 - /* make back pyramid point */
2247 - VectorCopy( points[ 0 ], nadir );
2248 - VectorAdd( nadir, points[ 1 ], nadir );
2249 - VectorAdd( nadir, points[ 2 ], nadir );
2250 - VectorScale( nadir, 0.3333333333333f, nadir );
2251 - VectorMA( nadir, -2.0f, plane, nadir );
2253 - /* make 3 more planes */
2254 - //% if( PlaneFromPoints( pa, points[ 2 ], points[ 1 ], nadir ) &&
2255 - //% PlaneFromPoints( pb, points[ 1 ], points[ 0 ], nadir ) &&
2256 - //% PlaneFromPoints( pc, points[ 0 ], points[ 2 ], nadir ) )
2257 + reverse[ 3 ] = -plane[ 3 ];
2258 + if((spawnFlags & 24) != 24)
2259 + reverse[3] += DotProduct(bestNormal, plane) * backPlaneDistance;
2260 + // 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)
2262 if( PlaneFromPoints( pa, points[ 2 ], points[ 1 ], backs[ 1 ] ) &&
2263 - PlaneFromPoints( pb, points[ 1 ], points[ 0 ], backs[ 0 ] ) &&
2264 - PlaneFromPoints( pc, points[ 0 ], points[ 2 ], backs[ 2 ] ) )
2265 + PlaneFromPoints( pb, points[ 1 ], points[ 0 ], backs[ 0 ] ) &&
2266 + PlaneFromPoints( pc, points[ 0 ], points[ 2 ], backs[ 2 ] ) )
2268 - /* build a brush */
2269 - buildBrush = AllocBrush( 48 );
2271 - buildBrush->entityNum = mapEntityNum;
2272 - buildBrush->original = buildBrush;
2273 - buildBrush->contentShader = si;
2274 - buildBrush->compileFlags = si->compileFlags;
2275 - buildBrush->contentFlags = si->contentFlags;
2276 - buildBrush->detail = qtrue;
2278 /* set up brush sides */
2279 buildBrush->numsides = 5;
2280 for( j = 0; j < buildBrush->numsides; j++ )
2281 buildBrush->sides[ j ].shaderInfo = si;
2283 buildBrush->sides[ 0 ].planenum = FindFloatPlane( plane, plane[ 3 ], 3, points );
2284 - buildBrush->sides[ 1 ].planenum = FindFloatPlane( pa, pa[ 3 ], 1, &points[ 2 ] );
2285 - buildBrush->sides[ 2 ].planenum = FindFloatPlane( pb, pb[ 3 ], 1, &points[ 1 ] );
2286 - buildBrush->sides[ 3 ].planenum = FindFloatPlane( pc, pc[ 3 ], 1, &points[ 0 ] );
2287 - buildBrush->sides[ 4 ].planenum = FindFloatPlane( reverse, reverse[ 3 ], 3, points );
2289 - /* add to entity */
2290 - if( CreateBrushWindings( buildBrush ) )
2293 - //% EmitBrushes( buildBrush, NULL, NULL );
2294 - buildBrush->next = entities[ mapEntityNum ].brushes;
2295 - entities[ mapEntityNum ].brushes = buildBrush;
2296 - entities[ mapEntityNum ].numBrushes++;
2299 - free( buildBrush );
2300 + buildBrush->sides[ 1 ].planenum = FindFloatPlane( pa, pa[ 3 ], 2, &points[ 1 ] ); // pa contains points[1] and points[2]
2301 + buildBrush->sides[ 2 ].planenum = FindFloatPlane( pb, pb[ 3 ], 2, &points[ 0 ] ); // pb contains points[0] and points[1]
2302 + buildBrush->sides[ 3 ].planenum = FindFloatPlane( pc, pc[ 3 ], 2, &points[ 2 ] ); // pc contains points[2] and points[0] (copied to points[3]
2303 + buildBrush->sides[ 4 ].planenum = FindFloatPlane( reverse, reverse[ 3 ], 3, backs );
2311 + normalEpsilon = normalEpsilon_save;
2312 + distanceEpsilon = distanceEpsilon_save;
2314 + /* add to entity */
2315 + if( CreateBrushWindings( buildBrush ) )
2318 + //% EmitBrushes( buildBrush, NULL, NULL );
2319 + buildBrush->next = entities[ mapEntityNum ].brushes;
2320 + entities[ mapEntityNum ].brushes = buildBrush;
2321 + entities[ mapEntityNum ].numBrushes++;
2324 + free( buildBrush );
2328 Index: tools/quake3/q3map2/map.c
2329 ===================================================================
2330 --- tools/quake3/q3map2/map.c (revision 193)
2331 +++ tools/quake3/q3map2/map.c (working copy)
2333 snaps a plane to normal/distance epsilons
2336 -void SnapPlane( vec3_t normal, vec_t *dist )
2337 +void SnapPlane( vec3_t normal, vec_t *dist, vec3_t center )
2339 // SnapPlane disabled by LordHavoc because it often messes up collision
2340 // brushes made from triangles of embedded models, and it has little effect
2341 @@ -193,7 +193,13 @@
2342 SnapPlane reenabled by namespace because of multiple reports of
2343 q3map2-crashes which were triggered by this patch.
2345 + // div0: ensure the point "center" stays on the plane (actually, this
2346 + // rotates the plane around the point center).
2347 + // if center lies on the plane, it is guaranteed to stay on the plane by
2349 + vec_t centerDist = DotProduct(normal, center);
2350 SnapNormal( normal );
2351 + *dist += (DotProduct(normal, center) - centerDist);
2353 if( fabs( *dist - Q_rint( *dist ) ) < distanceEpsilon )
2354 *dist = Q_rint( *dist );
2356 must be within an epsilon distance of the plane
2359 -int FindFloatPlane( vec3_t normal, vec_t dist, int numPoints, vec3_t *points )
2360 +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?
2364 @@ -215,10 +221,14 @@
2368 + vec3_t centerofweight;
2370 + VectorClear(centerofweight);
2371 + for(i = 0; i < numPoints; ++i)
2372 + VectorMA(centerofweight, 1.0 / numPoints, points[i], centerofweight);
2375 /* hash the plane */
2376 - SnapPlane( normal, &dist );
2377 + SnapPlane( normal, &dist, centerofweight );
2378 hash = (PLANE_HASHES - 1) & (int) fabs( dist );
2380 /* search the border bins as well */
2381 @@ -259,7 +269,13 @@
2385 - SnapPlane( normal, &dist );
2386 + vec3_t centerofweight;
2388 + VectorClear(centerofweight);
2389 + for(i = 0; i < numPoints; ++i)
2390 + VectorMA(centerofweight, 1.0 / numPoints, points[i], centerofweight);
2392 + SnapPlane( normal, &dist, centerofweight );
2393 for( i = 0, p = mapplanes; i < nummapplanes; i++, p++ )
2395 if( PlaneEqual( p, normal, dist ) )
2396 Index: tools/quake3/q3map2/q3map2.h
2397 ===================================================================
2398 --- tools/quake3/q3map2/q3map2.h (revision 193)
2399 +++ tools/quake3/q3map2/q3map2.h (working copy)
2404 -#define Q3MAP_VERSION "2.5.17"
2405 -#define Q3MAP_MOTD "Last one turns the lights off"
2406 +#define Q3MAP_VERSION "2.5.17-div0-UTpicomodelase-UTpicomodelnormals-obj-UTbouncefix-UTfloodlight-UTlmexposure-UTtrianglecheck-decomptexcoords-snapplane"
2407 +#define Q3MAP_MOTD "Blackhole Box ate all the light"
2411 Index: include/version.default
2412 ===================================================================
2413 --- include/version.default (revision 193)
2414 +++ include/version.default (working copy)
2417 +1.5.0-div0-UTpicomodelase-UTpicomodelnormals-obj-modelnormals-nexuizfixes