2 Copyright (C) 1999-2007 id Software, Inc. and contributors.
\r
3 For a list of contributors, see the accompanying CONTRIBUTORS file.
\r
5 This file is part of GtkRadiant.
\r
7 GtkRadiant is free software; you can redistribute it and/or modify
\r
8 it under the terms of the GNU General Public License as published by
\r
9 the Free Software Foundation; either version 2 of the License, or
\r
10 (at your option) any later version.
\r
12 GtkRadiant is distributed in the hope that it will be useful,
\r
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
15 GNU General Public License for more details.
\r
17 You should have received a copy of the GNU General Public License
\r
18 along with GtkRadiant; if not, write to the Free Software
\r
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
\r
21 ----------------------------------------------------------------------------------
\r
23 This code has been altered significantly from its original form, to support
\r
24 several games based on the Quake III Arena engine, in the form of "Q3Map2."
\r
26 ------------------------------------------------------------------------------- */
\r
31 #define SURFACE_META_C
\r
40 #define LIGHTMAP_EXCEEDED -1
\r
41 #define S_EXCEEDED -2
\r
42 #define T_EXCEEDED -3
\r
43 #define ST_EXCEEDED -4
\r
44 #define UNSUITABLE_TRIANGLE -10
\r
45 #define VERTS_EXCEEDED -1000
\r
46 #define INDEXES_EXCEEDED -2000
\r
48 #define GROW_META_VERTS 1024
\r
49 #define GROW_META_TRIANGLES 1024
\r
51 static int numMetaSurfaces, numPatchMetaSurfaces;
\r
53 static int maxMetaVerts = 0;
\r
54 static int numMetaVerts = 0;
\r
55 static int firstSearchMetaVert = 0;
\r
56 static bspDrawVert_t *metaVerts = NULL;
\r
58 static int maxMetaTriangles = 0;
\r
59 static int numMetaTriangles = 0;
\r
60 static metaTriangle_t *metaTriangles = NULL;
\r
66 called before staring a new entity to clear out the triangle list
\r
69 void ClearMetaTriangles( void )
\r
72 numMetaTriangles = 0;
\r
79 finds a matching metavertex in the global list, returning its index
\r
82 static int FindMetaVertex( bspDrawVert_t *src )
\r
85 bspDrawVert_t *v, *temp;
\r
88 /* try to find an existing drawvert */
\r
89 for( i = firstSearchMetaVert, v = &metaVerts[ i ]; i < numMetaVerts; i++, v++ )
\r
91 if( memcmp( src, v, sizeof( bspDrawVert_t ) ) == 0 )
\r
96 if( numMetaVerts >= maxMetaVerts )
\r
98 /* reallocate more room */
\r
99 maxMetaVerts += GROW_META_VERTS;
\r
100 temp = safe_malloc( maxMetaVerts * sizeof( bspDrawVert_t ) );
\r
101 if( metaVerts != NULL )
\r
103 memcpy( temp, metaVerts, numMetaVerts * sizeof( bspDrawVert_t ) );
\r
109 /* add the triangle */
\r
110 memcpy( &metaVerts[ numMetaVerts ], src, sizeof( bspDrawVert_t ) );
\r
113 /* return the count */
\r
114 return (numMetaVerts - 1);
\r
121 adds a new meta triangle, allocating more memory if necessary
\r
124 static int AddMetaTriangle( void )
\r
126 metaTriangle_t *temp;
\r
129 /* enough space? */
\r
130 if( numMetaTriangles >= maxMetaTriangles )
\r
132 /* reallocate more room */
\r
133 maxMetaTriangles += GROW_META_TRIANGLES;
\r
134 temp = safe_malloc( maxMetaTriangles * sizeof( metaTriangle_t ) );
\r
135 if( metaTriangles != NULL )
\r
137 memcpy( temp, metaTriangles, numMetaTriangles * sizeof( metaTriangle_t ) );
\r
138 free( metaTriangles );
\r
140 metaTriangles = temp;
\r
143 /* increment and return */
\r
144 numMetaTriangles++;
\r
145 return numMetaTriangles - 1;
\r
152 finds a matching metatriangle in the global list,
\r
153 otherwise adds it and returns the index to the metatriangle
\r
156 int FindMetaTriangle( metaTriangle_t *src, bspDrawVert_t *a, bspDrawVert_t *b, bspDrawVert_t *c, int planeNum )
\r
163 /* detect degenerate triangles fixme: do something proper here */
\r
164 VectorSubtract( a->xyz, b->xyz, dir );
\r
165 if( VectorLength( dir ) < 0.125f )
\r
167 VectorSubtract( b->xyz, c->xyz, dir );
\r
168 if( VectorLength( dir ) < 0.125f )
\r
170 VectorSubtract( c->xyz, a->xyz, dir );
\r
171 if( VectorLength( dir ) < 0.125f )
\r
175 if( planeNum >= 0 )
\r
177 /* because of precision issues with small triangles, try to use the specified plane */
\r
178 src->planeNum = planeNum;
\r
179 VectorCopy( mapplanes[ planeNum ].normal, src->plane );
\r
180 src->plane[ 3 ] = mapplanes[ planeNum ].dist;
\r
184 /* calculate a plane from the triangle's points (and bail if a plane can't be constructed) */
\r
185 src->planeNum = -1;
\r
186 if( PlaneFromPoints( src->plane, a->xyz, b->xyz, c->xyz ) == qfalse )
\r
190 /* ydnar 2002-10-03: repair any bogus normals (busted ase import kludge) */
\r
191 if( VectorLength( a->normal ) <= 0.0f )
\r
192 VectorCopy( src->plane, a->normal );
\r
193 if( VectorLength( b->normal ) <= 0.0f )
\r
194 VectorCopy( src->plane, b->normal );
\r
195 if( VectorLength( c->normal ) <= 0.0f )
\r
196 VectorCopy( src->plane, c->normal );
\r
198 /* ydnar 2002-10-04: set lightmap axis if not already set */
\r
199 if( !(src->si->compileFlags & C_VERTEXLIT) &&
\r
200 src->lightmapAxis[ 0 ] == 0.0f && src->lightmapAxis[ 1 ] == 0.0f && src->lightmapAxis[ 2 ] == 0.0f )
\r
202 /* the shader can specify an explicit lightmap axis */
\r
203 if( src->si->lightmapAxis[ 0 ] || src->si->lightmapAxis[ 1 ] || src->si->lightmapAxis[ 2 ] )
\r
204 VectorCopy( src->si->lightmapAxis, src->lightmapAxis );
\r
206 /* new axis-finding code */
\r
208 CalcLightmapAxis( src->plane, src->lightmapAxis );
\r
211 /* fill out the src triangle */
\r
212 src->indexes[ 0 ] = FindMetaVertex( a );
\r
213 src->indexes[ 1 ] = FindMetaVertex( b );
\r
214 src->indexes[ 2 ] = FindMetaVertex( c );
\r
216 /* try to find an existing triangle */
\r
217 #ifdef USE_EXHAUSTIVE_SEARCH
\r
220 metaTriangle_t *tri;
\r
223 for( i = 0, tri = metaTriangles; i < numMetaTriangles; i++, tri++ )
\r
225 if( memcmp( src, tri, sizeof( metaTriangle_t ) ) == 0 )
\r
231 /* get a new triangle */
\r
232 triIndex = AddMetaTriangle();
\r
234 /* add the triangle */
\r
235 memcpy( &metaTriangles[ triIndex ], src, sizeof( metaTriangle_t ) );
\r
237 /* return the triangle index */
\r
244 SurfaceToMetaTriangles()
\r
245 converts a classified surface to metatriangles
\r
248 static void SurfaceToMetaTriangles( mapDrawSurface_t *ds )
\r
251 metaTriangle_t src;
\r
252 bspDrawVert_t a, b, c;
\r
255 /* only handle certain types of surfaces */
\r
256 if( ds->type != SURFACE_FACE &&
\r
257 ds->type != SURFACE_META &&
\r
258 ds->type != SURFACE_FORCED_META &&
\r
259 ds->type != SURFACE_DECAL )
\r
262 /* speed at the expense of memory */
\r
263 firstSearchMetaVert = numMetaVerts;
\r
265 /* only handle valid surfaces */
\r
266 if( ds->type != SURFACE_BAD && ds->numVerts >= 3 && ds->numIndexes >= 3 )
\r
268 /* walk the indexes and create triangles */
\r
269 for( i = 0; i < ds->numIndexes; i += 3 )
\r
271 /* sanity check the indexes */
\r
272 if( ds->indexes[ i ] == ds->indexes[ i + 1 ] ||
\r
273 ds->indexes[ i ] == ds->indexes[ i + 2 ] ||
\r
274 ds->indexes[ i + 1 ] == ds->indexes[ i + 2 ] )
\r
276 //% Sys_Printf( "%d! ", ds->numVerts );
\r
280 /* build a metatriangle */
\r
281 src.si = ds->shaderInfo;
\r
282 src.side = (ds->sideRef != NULL ? ds->sideRef->side : NULL);
\r
283 src.entityNum = ds->entityNum;
\r
284 src.surfaceNum = ds->surfaceNum;
\r
285 src.planeNum = ds->planeNum;
\r
286 src.castShadows = ds->castShadows;
\r
287 src.recvShadows = ds->recvShadows;
\r
288 src.fogNum = ds->fogNum;
\r
289 src.sampleSize = ds->sampleSize;
\r
290 VectorCopy( ds->lightmapAxis, src.lightmapAxis );
\r
292 /* copy drawverts */
\r
293 memcpy( &a, &ds->verts[ ds->indexes[ i ] ], sizeof( a ) );
\r
294 memcpy( &b, &ds->verts[ ds->indexes[ i + 1 ] ], sizeof( b ) );
\r
295 memcpy( &c, &ds->verts[ ds->indexes[ i + 2 ] ], sizeof( c ) );
\r
296 FindMetaTriangle( &src, &a, &b, &c, ds->planeNum );
\r
303 /* clear the surface (free verts and indexes, sets it to SURFACE_BAD) */
\r
304 ClearSurface( ds );
\r
310 TriangulatePatchSurface()
\r
311 creates triangles from a patch
\r
314 void TriangulatePatchSurface( mapDrawSurface_t *ds )
\r
316 int iterations, x, y, pw[ 5 ], r;
\r
317 mapDrawSurface_t *dsNew;
\r
318 mesh_t src, *subdivided, *mesh;
\r
321 /* try to early out */
\r
322 if( ds->numVerts == 0 || ds->type != SURFACE_PATCH || patchMeta == qfalse )
\r
325 /* make a mesh from the drawsurf */
\r
326 src.width = ds->patchWidth;
\r
327 src.height = ds->patchHeight;
\r
328 src.verts = ds->verts;
\r
329 //% subdivided = SubdivideMesh( src, 8, 999 );
\r
330 iterations = IterationsForCurve( ds->longestCurve, patchSubdivisions );
\r
331 subdivided = SubdivideMesh2( src, iterations ); //% ds->maxIterations
\r
333 /* fit it to the curve and remove colinear verts on rows/columns */
\r
334 PutMeshOnCurve( *subdivided );
\r
335 mesh = RemoveLinearMeshColumnsRows( subdivided );
\r
336 FreeMesh( subdivided );
\r
337 //% MakeMeshNormals( mesh );
\r
339 /* make a copy of the drawsurface */
\r
340 dsNew = AllocDrawSurface( SURFACE_META );
\r
341 memcpy( dsNew, ds, sizeof( *ds ) );
\r
343 /* if the patch is nonsolid, then discard it */
\r
344 if( !(ds->shaderInfo->compileFlags & C_SOLID) )
\r
345 ClearSurface( ds );
\r
347 /* set new pointer */
\r
350 /* basic transmogrification */
\r
351 ds->type = SURFACE_META;
\r
352 ds->numIndexes = 0;
\r
353 ds->indexes = safe_malloc( mesh->width * mesh->height * 6 * sizeof( int ) );
\r
355 /* copy the verts in */
\r
356 ds->numVerts = (mesh->width * mesh->height);
\r
357 ds->verts = mesh->verts;
\r
359 /* iterate through the mesh quads */
\r
360 for( y = 0; y < (mesh->height - 1); y++ )
\r
362 for( x = 0; x < (mesh->width - 1); x++ )
\r
365 pw[ 0 ] = x + (y * mesh->width);
\r
366 pw[ 1 ] = x + ((y + 1) * mesh->width);
\r
367 pw[ 2 ] = x + 1 + ((y + 1) * mesh->width);
\r
368 pw[ 3 ] = x + 1 + (y * mesh->width);
\r
369 pw[ 4 ] = x + (y * mesh->width); /* same as pw[ 0 ] */
\r
374 /* make first triangle */
\r
375 ds->indexes[ ds->numIndexes++ ] = pw[ r + 0 ];
\r
376 ds->indexes[ ds->numIndexes++ ] = pw[ r + 1 ];
\r
377 ds->indexes[ ds->numIndexes++ ] = pw[ r + 2 ];
\r
379 /* make second triangle */
\r
380 ds->indexes[ ds->numIndexes++ ] = pw[ r + 0 ];
\r
381 ds->indexes[ ds->numIndexes++ ] = pw[ r + 2 ];
\r
382 ds->indexes[ ds->numIndexes++ ] = pw[ r + 3 ];
\r
386 /* free the mesh, but not the verts */
\r
390 numPatchMetaSurfaces++;
\r
393 ClassifySurfaces( 1, ds );
\r
399 FanFaceSurface() - ydnar
\r
400 creates a tri-fan from a brush face winding
\r
401 loosely based on SurfaceAsTriFan()
\r
404 void FanFaceSurface( mapDrawSurface_t *ds )
\r
406 int i, j, k, a, b, c, color[ MAX_LIGHTMAPS ][ 4 ];
\r
407 bspDrawVert_t *verts, *centroid, *dv;
\r
411 /* try to early out */
\r
412 if( !ds->numVerts || (ds->type != SURFACE_FACE && ds->type != SURFACE_DECAL) )
\r
415 /* add a new vertex at the beginning of the surface */
\r
416 verts = safe_malloc( (ds->numVerts + 1) * sizeof( bspDrawVert_t ) );
\r
417 memset( verts, 0, sizeof( bspDrawVert_t ) );
\r
418 memcpy( &verts[ 1 ], ds->verts, ds->numVerts * sizeof( bspDrawVert_t ) );
\r
422 /* add up the drawverts to create a centroid */
\r
423 centroid = &verts[ 0 ];
\r
424 memset( color, 0, 4 * MAX_LIGHTMAPS * sizeof( int ) );
\r
425 for( i = 1, dv = &verts[ 1 ]; i < (ds->numVerts + 1); i++, dv++ )
\r
427 VectorAdd( centroid->xyz, dv->xyz, centroid->xyz );
\r
428 VectorAdd( centroid->normal, dv->normal, centroid->normal );
\r
429 for( j = 0; j < 4; j++ )
\r
431 for( k = 0; k < MAX_LIGHTMAPS; k++ )
\r
432 color[ k ][ j ] += dv->color[ k ][ j ];
\r
435 centroid->st[ j ] += dv->st[ j ];
\r
436 for( k = 0; k < MAX_LIGHTMAPS; k++ )
\r
437 centroid->lightmap[ k ][ j ] += dv->lightmap[ k ][ j ];
\r
442 /* average the centroid */
\r
443 iv = 1.0f / ds->numVerts;
\r
444 VectorScale( centroid->xyz, iv, centroid->xyz );
\r
445 if( VectorNormalize( centroid->normal, centroid->normal ) <= 0 )
\r
446 VectorCopy( verts[ 1 ].normal, centroid->normal );
\r
447 for( j = 0; j < 4; j++ )
\r
449 for( k = 0; k < MAX_LIGHTMAPS; k++ )
\r
451 color[ k ][ j ] /= ds->numVerts;
\r
452 centroid->color[ k ][ j ] = (color[ k ][ j ] < 255.0f ? color[ k ][ j ] : 255);
\r
456 centroid->st[ j ] *= iv;
\r
457 for( k = 0; k < MAX_LIGHTMAPS; k++ )
\r
458 centroid->lightmap[ k ][ j ] *= iv;
\r
462 /* add to vert count */
\r
465 /* fill indexes in triangle fan order */
\r
466 ds->numIndexes = 0;
\r
467 ds->indexes = safe_malloc( ds->numVerts * 3 * sizeof( int ) );
\r
468 for( i = 1; i < ds->numVerts; i++ )
\r
472 c = (i + 1) % ds->numVerts;
\r
474 ds->indexes[ ds->numIndexes++ ] = a;
\r
475 ds->indexes[ ds->numIndexes++ ] = b;
\r
476 ds->indexes[ ds->numIndexes++ ] = c;
\r
483 ClassifySurfaces( 1, ds );
\r
489 StripFaceSurface() - ydnar
\r
490 attempts to create a valid tri-strip w/o degenerate triangles from a brush face winding
\r
491 based on SurfaceAsTriStrip()
\r
494 #define MAX_INDEXES 1024
\r
496 void StripFaceSurface( mapDrawSurface_t *ds )
\r
498 int i, r, least, rotate, numIndexes, ni, a, b, c, indexes[ MAX_INDEXES ];
\r
502 /* try to early out */
\r
503 if( !ds->numVerts || (ds->type != SURFACE_FACE && ds->type != SURFACE_DECAL) )
\r
506 /* is this a simple triangle? */
\r
507 if( ds->numVerts == 3 )
\r
510 VectorSet( indexes, 0, 1, 2 );
\r
514 /* ydnar: find smallest coordinate */
\r
516 if( ds->shaderInfo != NULL && ds->shaderInfo->autosprite == qfalse )
\r
518 for( i = 0; i < ds->numVerts; i++ )
\r
521 v1 = ds->verts[ i ].xyz;
\r
522 v2 = ds->verts[ least ].xyz;
\r
525 if( v1[ 0 ] < v2[ 0 ] ||
\r
526 (v1[ 0 ] == v2[ 0 ] && v1[ 1 ] < v2[ 1 ]) ||
\r
527 (v1[ 0 ] == v2[ 0 ] && v1[ 1 ] == v2[ 1 ] && v1[ 2 ] < v2[ 2 ]) )
\r
532 /* determine the triangle strip order */
\r
533 numIndexes = (ds->numVerts - 2) * 3;
\r
534 if( numIndexes > MAX_INDEXES )
\r
535 Error( "MAX_INDEXES exceeded for surface (%d > %d) (%d verts)", numIndexes, MAX_INDEXES, ds->numVerts );
\r
537 /* try all possible orderings of the points looking for a non-degenerate strip order */
\r
538 for( r = 0; r < ds->numVerts; r++ )
\r
541 rotate = (r + least) % ds->numVerts;
\r
543 /* walk the winding in both directions */
\r
544 for( ni = 0, i = 0; i < ds->numVerts - 2 - i; i++ )
\r
547 a = (ds->numVerts - 1 - i + rotate) % ds->numVerts;
\r
548 b = (i + rotate ) % ds->numVerts;
\r
549 c = (ds->numVerts - 2 - i + rotate) % ds->numVerts;
\r
551 /* test this triangle */
\r
552 if( ds->numVerts > 4 && IsTriangleDegenerate( ds->verts, a, b, c ) )
\r
554 indexes[ ni++ ] = a;
\r
555 indexes[ ni++ ] = b;
\r
556 indexes[ ni++ ] = c;
\r
558 /* handle end case */
\r
559 if( i + 1 != ds->numVerts - 1 - i )
\r
562 a = (ds->numVerts - 2 - i + rotate ) % ds->numVerts;
\r
563 b = (i + rotate ) % ds->numVerts;
\r
564 c = (i + 1 + rotate ) % ds->numVerts;
\r
566 /* test triangle */
\r
567 if( ds->numVerts > 4 && IsTriangleDegenerate( ds->verts, a, b, c ) )
\r
569 indexes[ ni++ ] = a;
\r
570 indexes[ ni++ ] = b;
\r
571 indexes[ ni++ ] = c;
\r
576 if( ni == numIndexes )
\r
580 /* if any triangle in the strip is degenerate, render from a centered fan point instead */
\r
581 if( ni < numIndexes )
\r
583 FanFaceSurface( ds );
\r
588 /* copy strip triangle indexes */
\r
589 ds->numIndexes = numIndexes;
\r
590 ds->indexes = safe_malloc( ds->numIndexes * sizeof( int ) );
\r
591 memcpy( ds->indexes, indexes, ds->numIndexes * sizeof( int ) );
\r
594 numStripSurfaces++;
\r
597 ClassifySurfaces( 1, ds );
\r
603 MakeEntityMetaTriangles()
\r
604 builds meta triangles from brush faces (tristrips and fans)
\r
607 void MakeEntityMetaTriangles( entity_t *e )
\r
609 int i, f, fOld, start;
\r
610 mapDrawSurface_t *ds;
\r
614 Sys_FPrintf( SYS_VRB, "--- MakeEntityMetaTriangles ---\n" );
\r
616 /* init pacifier */
\r
618 start = I_FloatTime();
\r
620 /* walk the list of surfaces in the entity */
\r
621 for( i = e->firstDrawSurf; i < numMapDrawSurfs; i++ )
\r
623 /* print pacifier */
\r
624 f = 10 * (i - e->firstDrawSurf) / (numMapDrawSurfs - e->firstDrawSurf);
\r
628 Sys_FPrintf( SYS_VRB, "%d...", f );
\r
632 ds = &mapDrawSurfs[ i ];
\r
633 if( ds->numVerts <= 0 )
\r
636 /* ignore autosprite surfaces */
\r
637 if( ds->shaderInfo->autosprite )
\r
640 /* meta this surface? */
\r
641 if( meta == qfalse && ds->shaderInfo->forceMeta == qfalse )
\r
644 /* switch on type */
\r
648 case SURFACE_DECAL:
\r
649 StripFaceSurface( ds );
\r
650 SurfaceToMetaTriangles( ds );
\r
653 case SURFACE_PATCH:
\r
654 TriangulatePatchSurface( ds );
\r
657 case SURFACE_TRIANGLES:
\r
660 case SURFACE_FORCED_META:
\r
662 SurfaceToMetaTriangles( ds );
\r
671 if( (numMapDrawSurfs - e->firstDrawSurf) )
\r
672 Sys_FPrintf( SYS_VRB, " (%d)\n", (int) (I_FloatTime() - start) );
\r
674 /* emit some stats */
\r
675 Sys_FPrintf( SYS_VRB, "%9d total meta surfaces\n", numMetaSurfaces );
\r
676 Sys_FPrintf( SYS_VRB, "%9d stripped surfaces\n", numStripSurfaces );
\r
677 Sys_FPrintf( SYS_VRB, "%9d fanned surfaces\n", numFanSurfaces );
\r
678 Sys_FPrintf( SYS_VRB, "%9d patch meta surfaces\n", numPatchMetaSurfaces );
\r
679 Sys_FPrintf( SYS_VRB, "%9d meta verts\n", numMetaVerts );
\r
680 Sys_FPrintf( SYS_VRB, "%9d meta triangles\n", numMetaTriangles );
\r
682 /* tidy things up */
\r
683 TidyEntitySurfaces( e );
\r
689 PointTriangleIntersect()
\r
690 assuming that all points lie in plane, determine if pt
\r
691 is inside the triangle abc
\r
692 code originally (c) 2001 softSurfer (www.softsurfer.com)
\r
695 #define MIN_OUTSIDE_EPSILON -0.01f
\r
696 #define MAX_OUTSIDE_EPSILON 1.01f
\r
698 static qboolean PointTriangleIntersect( vec3_t pt, vec4_t plane, vec3_t a, vec3_t b, vec3_t c, vec3_t bary )
\r
701 float uu, uv, vv, wu, wv, d;
\r
705 VectorSubtract( b, a, u );
\r
706 VectorSubtract( c, a, v );
\r
707 VectorSubtract( pt, a, w );
\r
710 uu = DotProduct( u, u );
\r
711 uv = DotProduct( u, v );
\r
712 vv = DotProduct( v, v );
\r
713 wu = DotProduct( w, u );
\r
714 wv = DotProduct( w, v );
\r
715 d = uv * uv - uu * vv;
\r
717 /* calculate barycentric coordinates */
\r
718 bary[ 1 ] = (uv * wv - vv * wu) / d;
\r
719 if( bary[ 1 ] < MIN_OUTSIDE_EPSILON || bary[ 1 ] > MAX_OUTSIDE_EPSILON )
\r
721 bary[ 2 ] = (uv * wv - uu * wv) / d;
\r
722 if( bary[ 2 ] < MIN_OUTSIDE_EPSILON || bary[ 2 ] > MAX_OUTSIDE_EPSILON )
\r
724 bary[ 0 ] = 1.0f - (bary[ 1 ] + bary[ 2 ]);
\r
726 /* point is in triangle */
\r
734 sets up an edge structure from a plane and 2 points that the edge ab falls lies in
\r
737 typedef struct edge_s
\r
739 vec3_t origin, edge;
\r
740 vec_t length, kingpinLength;
\r
746 void CreateEdge( vec4_t plane, vec3_t a, vec3_t b, edge_t *edge )
\r
748 /* copy edge origin */
\r
749 VectorCopy( a, edge->origin );
\r
751 /* create vector aligned with winding direction of edge */
\r
752 VectorSubtract( b, a, edge->edge );
\r
754 if( fabs( edge->edge[ 0 ] ) > fabs( edge->edge[ 1 ] ) && fabs( edge->edge[ 0 ] ) > fabs( edge->edge[ 2 ] ) )
\r
756 else if( fabs( edge->edge[ 1 ] ) > fabs( edge->edge[ 0 ] ) && fabs( edge->edge[ 1 ] ) > fabs( edge->edge[ 2 ] ) )
\r
760 edge->kingpinLength = edge->edge[ edge->kingpin ];
\r
762 VectorNormalize( edge->edge, edge->edge );
\r
763 edge->edge[ 3 ] = DotProduct( a, edge->edge );
\r
764 edge->length = DotProduct( b, edge->edge ) - edge->edge[ 3 ];
\r
766 /* create perpendicular plane that edge lies in */
\r
767 CrossProduct( plane, edge->edge, edge->plane );
\r
768 edge->plane[ 3 ] = DotProduct( a, edge->plane );
\r
774 FixMetaTJunctions()
\r
775 fixes t-junctions on meta triangles
\r
778 #define TJ_PLANE_EPSILON (1.0f / 8.0f)
\r
779 #define TJ_EDGE_EPSILON (1.0f / 8.0f)
\r
780 #define TJ_POINT_EPSILON (1.0f / 8.0f)
\r
782 void FixMetaTJunctions( void )
\r
784 int i, j, k, f, fOld, start, vertIndex, triIndex, numTJuncs;
\r
785 metaTriangle_t *tri, *newTri;
\r
787 bspDrawVert_t *a, *b, *c, junc;
\r
788 float dist, amount;
\r
794 /* this code is crap; revisit later */
\r
798 Sys_FPrintf( SYS_VRB, "--- FixMetaTJunctions ---\n" );
\r
800 /* init pacifier */
\r
802 start = I_FloatTime();
\r
804 /* walk triangle list */
\r
806 for( i = 0; i < numMetaTriangles; i++ )
\r
809 tri = &metaTriangles[ i ];
\r
811 /* print pacifier */
\r
812 f = 10 * i / numMetaTriangles;
\r
816 Sys_FPrintf( SYS_VRB, "%d...", f );
\r
819 /* attempt to early out */
\r
821 if( (si->compileFlags & C_NODRAW) || si->autosprite || si->notjunc )
\r
824 /* calculate planes */
\r
825 VectorCopy( tri->plane, plane );
\r
826 plane[ 3 ] = tri->plane[ 3 ];
\r
827 CreateEdge( plane, metaVerts[ tri->indexes[ 0 ] ].xyz, metaVerts[ tri->indexes[ 1 ] ].xyz, &edges[ 0 ] );
\r
828 CreateEdge( plane, metaVerts[ tri->indexes[ 1 ] ].xyz, metaVerts[ tri->indexes[ 2 ] ].xyz, &edges[ 1 ] );
\r
829 CreateEdge( plane, metaVerts[ tri->indexes[ 2 ] ].xyz, metaVerts[ tri->indexes[ 0 ] ].xyz, &edges[ 2 ] );
\r
831 /* walk meta vert list */
\r
832 for( j = 0; j < numMetaVerts; j++ )
\r
835 VectorCopy( metaVerts[ j ].xyz, pt );
\r
837 /* debug code: darken verts */
\r
839 VectorSet( metaVerts[ j ].color[ 0 ], 8, 8, 8 );
\r
841 /* determine if point lies in the triangle's plane */
\r
842 dist = DotProduct( pt, plane ) - plane[ 3 ];
\r
843 if( fabs( dist ) > TJ_PLANE_EPSILON )
\r
846 /* skip this point if it already exists in the triangle */
\r
847 for( k = 0; k < 3; k++ )
\r
849 if( fabs( pt[ 0 ] - metaVerts[ tri->indexes[ k ] ].xyz[ 0 ] ) <= TJ_POINT_EPSILON &&
\r
850 fabs( pt[ 1 ] - metaVerts[ tri->indexes[ k ] ].xyz[ 1 ] ) <= TJ_POINT_EPSILON &&
\r
851 fabs( pt[ 2 ] - metaVerts[ tri->indexes[ k ] ].xyz[ 2 ] ) <= TJ_POINT_EPSILON )
\r
858 for( k = 0; k < 3; k++ )
\r
860 /* ignore bogus edges */
\r
861 if( fabs( edges[ k ].kingpinLength ) < TJ_EDGE_EPSILON )
\r
864 /* determine if point lies on the edge */
\r
865 dist = DotProduct( pt, edges[ k ].plane ) - edges[ k ].plane[ 3 ];
\r
866 if( fabs( dist ) > TJ_EDGE_EPSILON )
\r
869 /* determine how far along the edge the point lies */
\r
870 amount = (pt[ edges[ k ].kingpin ] - edges[ k ].origin[ edges[ k ].kingpin ]) / edges[ k ].kingpinLength;
\r
871 if( amount <= 0.0f || amount >= 1.0f )
\r
875 dist = DotProduct( pt, edges[ k ].edge ) - edges[ k ].edge[ 3 ];
\r
876 if( dist <= -0.0f || dist >= edges[ k ].length )
\r
878 amount = dist / edges[ k ].length;
\r
881 /* debug code: brighten this point */
\r
882 //% metaVerts[ j ].color[ 0 ][ 0 ] += 5;
\r
883 //% metaVerts[ j ].color[ 0 ][ 1 ] += 4;
\r
884 VectorSet( metaVerts[ tri->indexes[ k ] ].color[ 0 ], 255, 204, 0 );
\r
885 VectorSet( metaVerts[ tri->indexes[ (k + 1) % 3 ] ].color[ 0 ], 255, 204, 0 );
\r
888 /* the edge opposite the zero-weighted vertex was hit, so use that as an amount */
\r
889 a = &metaVerts[ tri->indexes[ k % 3 ] ];
\r
890 b = &metaVerts[ tri->indexes[ (k + 1) % 3 ] ];
\r
891 c = &metaVerts[ tri->indexes[ (k + 2) % 3 ] ];
\r
893 /* make new vert */
\r
894 LerpDrawVertAmount( a, b, amount, &junc );
\r
895 VectorCopy( pt, junc.xyz );
\r
897 /* compare against existing verts */
\r
898 if( VectorCompare( junc.xyz, a->xyz ) || VectorCompare( junc.xyz, b->xyz ) || VectorCompare( junc.xyz, c->xyz ) )
\r
901 /* see if we can just re-use the existing vert */
\r
902 if( !memcmp( &metaVerts[ j ], &junc, sizeof( junc ) ) )
\r
906 /* find new vertex (note: a and b are invalid pointers after this) */
\r
907 firstSearchMetaVert = numMetaVerts;
\r
908 vertIndex = FindMetaVertex( &junc );
\r
909 if( vertIndex < 0 )
\r
913 /* make new triangle */
\r
914 triIndex = AddMetaTriangle();
\r
918 /* get triangles */
\r
919 tri = &metaTriangles[ i ];
\r
920 newTri = &metaTriangles[ triIndex ];
\r
922 /* copy the triangle */
\r
923 memcpy( newTri, tri, sizeof( *tri ) );
\r
926 tri->indexes[ (k + 1) % 3 ] = vertIndex;
\r
927 newTri->indexes[ k ] = vertIndex;
\r
929 /* recalculate edges */
\r
930 CreateEdge( plane, metaVerts[ tri->indexes[ 0 ] ].xyz, metaVerts[ tri->indexes[ 1 ] ].xyz, &edges[ 0 ] );
\r
931 CreateEdge( plane, metaVerts[ tri->indexes[ 1 ] ].xyz, metaVerts[ tri->indexes[ 2 ] ].xyz, &edges[ 1 ] );
\r
932 CreateEdge( plane, metaVerts[ tri->indexes[ 2 ] ].xyz, metaVerts[ tri->indexes[ 0 ] ].xyz, &edges[ 2 ] );
\r
935 metaVerts[ vertIndex ].color[ 0 ][ 0 ] = 255;
\r
936 metaVerts[ vertIndex ].color[ 0 ][ 1 ] = 204;
\r
937 metaVerts[ vertIndex ].color[ 0 ][ 2 ] = 0;
\r
939 /* add to counter and end processing of this vert */
\r
947 Sys_FPrintf( SYS_VRB, " (%d)\n", (int) (I_FloatTime() - start) );
\r
949 /* emit some stats */
\r
950 Sys_FPrintf( SYS_VRB, "%9d T-junctions added\n", numTJuncs );
\r
956 SmoothMetaTriangles()
\r
957 averages coincident vertex normals in the meta triangles
\r
960 #define MAX_SAMPLES 256
\r
961 #define THETA_EPSILON 0.000001
\r
962 #define EQUAL_NORMAL_EPSILON 0.01
\r
964 void SmoothMetaTriangles( void )
\r
966 int i, j, k, f, fOld, start, cs, numVerts, numVotes, numSmoothed;
\r
967 float shadeAngle, defaultShadeAngle, maxShadeAngle, dot, testAngle;
\r
968 metaTriangle_t *tri;
\r
969 float *shadeAngles;
\r
971 vec3_t average, diff;
\r
972 int indexes[ MAX_SAMPLES ];
\r
973 vec3_t votes[ MAX_SAMPLES ];
\r
977 Sys_FPrintf( SYS_VRB, "--- SmoothMetaTriangles ---\n" );
\r
979 /* allocate shade angle table */
\r
980 shadeAngles = safe_malloc( numMetaVerts * sizeof( float ) );
\r
981 memset( shadeAngles, 0, numMetaVerts * sizeof( float ) );
\r
983 /* allocate smoothed table */
\r
984 cs = (numMetaVerts / 8) + 1;
\r
985 smoothed = safe_malloc( cs );
\r
986 memset( smoothed, 0, cs );
\r
988 /* set default shade angle */
\r
989 defaultShadeAngle = DEG2RAD( npDegrees );
\r
990 maxShadeAngle = 0.0f;
\r
992 /* run through every surface and flag verts belonging to non-lightmapped surfaces
\r
993 and set per-vertex smoothing angle */
\r
994 for( i = 0, tri = &metaTriangles[ i ]; i < numMetaTriangles; i++, tri++ )
\r
996 /* get shader for shade angle */
\r
997 if( tri->si->shadeAngleDegrees > 0.0f )
\r
998 shadeAngle = DEG2RAD( tri->si->shadeAngleDegrees );
\r
1000 shadeAngle = defaultShadeAngle;
\r
1001 if( shadeAngle > maxShadeAngle )
\r
1002 maxShadeAngle = shadeAngle;
\r
1004 /* flag its verts */
\r
1005 for( j = 0; j < 3; j++ )
\r
1007 shadeAngles[ tri->indexes[ j ] ] = shadeAngle;
\r
1008 if( shadeAngle <= 0 )
\r
1009 smoothed[ tri->indexes[ j ] >> 3 ] |= (1 << (tri->indexes[ j ] & 7));
\r
1013 /* bail if no surfaces have a shade angle */
\r
1014 if( maxShadeAngle <= 0 )
\r
1016 Sys_FPrintf( SYS_VRB, "No smoothing angles specified, aborting\n" );
\r
1017 free( shadeAngles );
\r
1022 /* init pacifier */
\r
1024 start = I_FloatTime();
\r
1026 /* go through the list of vertexes */
\r
1028 for( i = 0; i < numMetaVerts; i++ )
\r
1030 /* print pacifier */
\r
1031 f = 10 * i / numMetaVerts;
\r
1035 Sys_FPrintf( SYS_VRB, "%d...", f );
\r
1038 /* already smoothed? */
\r
1039 if( smoothed[ i >> 3 ] & (1 << (i & 7)) )
\r
1043 VectorClear( average );
\r
1047 /* build a table of coincident vertexes */
\r
1048 for( j = i; j < numMetaVerts && numVerts < MAX_SAMPLES; j++ )
\r
1050 /* already smoothed? */
\r
1051 if( smoothed[ j >> 3 ] & (1 << (j & 7)) )
\r
1054 /* test vertexes */
\r
1055 if( VectorCompare( metaVerts[ i ].xyz, metaVerts[ j ].xyz ) == qfalse )
\r
1058 /* use smallest shade angle */
\r
1059 shadeAngle = (shadeAngles[ i ] < shadeAngles[ j ] ? shadeAngles[ i ] : shadeAngles[ j ]);
\r
1061 /* check shade angle */
\r
1062 dot = DotProduct( metaVerts[ i ].normal, metaVerts[ j ].normal );
\r
1065 else if( dot < -1.0 )
\r
1067 testAngle = acos( dot ) + THETA_EPSILON;
\r
1068 if( testAngle >= shadeAngle )
\r
1071 /* add to the list */
\r
1072 indexes[ numVerts++ ] = j;
\r
1075 smoothed[ j >> 3 ] |= (1 << (j & 7));
\r
1077 /* see if this normal has already been voted */
\r
1078 for( k = 0; k < numVotes; k++ )
\r
1080 VectorSubtract( metaVerts[ j ].normal, votes[ k ], diff );
\r
1081 if( fabs( diff[ 0 ] ) < EQUAL_NORMAL_EPSILON &&
\r
1082 fabs( diff[ 1 ] ) < EQUAL_NORMAL_EPSILON &&
\r
1083 fabs( diff[ 2 ] ) < EQUAL_NORMAL_EPSILON )
\r
1087 /* add a new vote? */
\r
1088 if( k == numVotes && numVotes < MAX_SAMPLES )
\r
1090 VectorAdd( average, metaVerts[ j ].normal, average );
\r
1091 VectorCopy( metaVerts[ j ].normal, votes[ numVotes ] );
\r
1096 /* don't average for less than 2 verts */
\r
1097 if( numVerts < 2 )
\r
1100 /* average normal */
\r
1101 if( VectorNormalize( average, average ) > 0 )
\r
1104 for( j = 0; j < numVerts; j++ )
\r
1105 VectorCopy( average, metaVerts[ indexes[ j ] ].normal );
\r
1110 /* free the tables */
\r
1111 free( shadeAngles );
\r
1115 Sys_FPrintf( SYS_VRB, " (%d)\n", (int) (I_FloatTime() - start) );
\r
1117 /* emit some stats */
\r
1118 Sys_FPrintf( SYS_VRB, "%9d smoothed vertexes\n", numSmoothed );
\r
1124 AddMetaVertToSurface()
\r
1125 adds a drawvert to a surface unless an existing vert matching already exists
\r
1126 returns the index of that vert (or < 0 on failure)
\r
1129 int AddMetaVertToSurface( mapDrawSurface_t *ds, bspDrawVert_t *dv1, int *coincident )
\r
1132 bspDrawVert_t *dv2;
\r
1135 /* go through the verts and find a suitable candidate */
\r
1136 for( i = 0; i < ds->numVerts; i++ )
\r
1138 /* get test vert */
\r
1139 dv2 = &ds->verts[ i ];
\r
1141 /* compare xyz and normal */
\r
1142 if( VectorCompare( dv1->xyz, dv2->xyz ) == qfalse )
\r
1144 if( VectorCompare( dv1->normal, dv2->normal ) == qfalse )
\r
1147 /* good enough at this point */
\r
1150 /* compare texture coordinates and color */
\r
1151 if( dv1->st[ 0 ] != dv2->st[ 0 ] || dv1->st[ 1 ] != dv2->st[ 1 ] )
\r
1153 if( dv1->color[ 0 ][ 3 ] != dv2->color[ 0 ][ 3 ] )
\r
1156 /* found a winner */
\r
1161 /* overflow check */
\r
1162 if( ds->numVerts >= maxSurfaceVerts )
\r
1163 return VERTS_EXCEEDED;
\r
1165 /* made it this far, add the vert and return */
\r
1166 dv2 = &ds->verts[ ds->numVerts++ ];
\r
1168 return (ds->numVerts - 1);
\r
1175 AddMetaTriangleToSurface()
\r
1176 attempts to add a metatriangle to a surface
\r
1177 returns the score of the triangle added
\r
1180 #define AXIS_SCORE 100000
\r
1181 #define AXIS_MIN 100000
\r
1182 #define VERT_SCORE 10000
\r
1183 #define SURFACE_SCORE 1000
\r
1184 #define ST_SCORE 50
\r
1185 #define ST_SCORE2 (2 * (ST_SCORE))
\r
1187 #define ADEQUATE_SCORE ((AXIS_MIN) + 1 * (VERT_SCORE))
\r
1188 #define GOOD_SCORE ((AXIS_MIN) + 2 * (VERT_SCORE) + 4 * (ST_SCORE))
\r
1189 #define PERFECT_SCORE ((AXIS_MIN) + + 3 * (VERT_SCORE) + (SURFACE_SCORE) + 4 * (ST_SCORE))
\r
1191 static int AddMetaTriangleToSurface( mapDrawSurface_t *ds, metaTriangle_t *tri, qboolean testAdd )
\r
1193 int i, score, coincident, ai, bi, ci, oldTexRange[ 2 ];
\r
1195 vec3_t mins, maxs;
\r
1196 qboolean inTexRange, es, et;
\r
1197 mapDrawSurface_t old;
\r
1200 /* overflow check */
\r
1201 if( ds->numIndexes >= maxSurfaceIndexes )
\r
1204 /* test the triangle */
\r
1205 if( ds->entityNum != tri->entityNum ) /* ydnar: added 2002-07-06 */
\r
1207 if( ds->castShadows != tri->castShadows || ds->recvShadows != tri->recvShadows )
\r
1209 if( ds->shaderInfo != tri->si || ds->fogNum != tri->fogNum || ds->sampleSize != tri->sampleSize )
\r
1212 if( !(ds->shaderInfo->compileFlags & C_VERTEXLIT) &&
\r
1213 //% VectorCompare( ds->lightmapAxis, tri->lightmapAxis ) == qfalse )
\r
1214 DotProduct( ds->lightmapAxis, tri->plane ) < 0.25f )
\r
1218 /* planar surfaces will only merge with triangles in the same plane */
\r
1219 if( npDegrees == 0.0f && ds->shaderInfo->nonplanar == qfalse && ds->planeNum >= 0 )
\r
1221 if( VectorCompare( mapplanes[ ds->planeNum ].normal, tri->plane ) == qfalse || mapplanes[ ds->planeNum ].dist != tri->plane[ 3 ] )
\r
1223 if( tri->planeNum >= 0 && tri->planeNum != ds->planeNum )
\r
1227 /* set initial score */
\r
1228 score = tri->surfaceNum == ds->surfaceNum ? SURFACE_SCORE : 0;
\r
1230 /* score the the dot product of lightmap axis to plane */
\r
1231 if( (ds->shaderInfo->compileFlags & C_VERTEXLIT) || VectorCompare( ds->lightmapAxis, tri->lightmapAxis ) )
\r
1232 score += AXIS_SCORE;
\r
1234 score += AXIS_SCORE * DotProduct( ds->lightmapAxis, tri->plane );
\r
1236 /* preserve old drawsurface if this fails */
\r
1237 memcpy( &old, ds, sizeof( *ds ) );
\r
1239 /* attempt to add the verts */
\r
1241 ai = AddMetaVertToSurface( ds, &metaVerts[ tri->indexes[ 0 ] ], &coincident );
\r
1242 bi = AddMetaVertToSurface( ds, &metaVerts[ tri->indexes[ 1 ] ], &coincident );
\r
1243 ci = AddMetaVertToSurface( ds, &metaVerts[ tri->indexes[ 2 ] ], &coincident );
\r
1245 /* check vertex underflow */
\r
1246 if( ai < 0 || bi < 0 || ci < 0 )
\r
1248 memcpy( ds, &old, sizeof( *ds ) );
\r
1252 /* score coincident vertex count (2003-02-14: changed so this only matters on planar surfaces) */
\r
1253 score += (coincident * VERT_SCORE);
\r
1255 /* add new vertex bounds to mins/maxs */
\r
1256 VectorCopy( ds->mins, mins );
\r
1257 VectorCopy( ds->maxs, maxs );
\r
1258 AddPointToBounds( metaVerts[ tri->indexes[ 0 ] ].xyz, mins, maxs );
\r
1259 AddPointToBounds( metaVerts[ tri->indexes[ 1 ] ].xyz, mins, maxs );
\r
1260 AddPointToBounds( metaVerts[ tri->indexes[ 2 ] ].xyz, mins, maxs );
\r
1262 /* check lightmap bounds overflow (after at least 1 triangle has been added) */
\r
1263 if( !(ds->shaderInfo->compileFlags & C_VERTEXLIT) &&
\r
1264 ds->numIndexes > 0 && VectorLength( ds->lightmapAxis ) > 0.0f &&
\r
1265 (VectorCompare( ds->mins, mins ) == qfalse || VectorCompare( ds->maxs, maxs ) == qfalse) )
\r
1267 /* set maximum size before lightmap scaling (normally 2032 units) */
\r
1268 lmMax = (ds->sampleSize * (ds->shaderInfo->lmCustomWidth - 1));
\r
1269 for( i = 0; i < 3; i++ )
\r
1271 if( (maxs[ i ] - mins[ i ]) > lmMax )
\r
1273 memcpy( ds, &old, sizeof( *ds ) );
\r
1279 /* check texture range overflow */
\r
1280 oldTexRange[ 0 ] = ds->texRange[ 0 ];
\r
1281 oldTexRange[ 1 ] = ds->texRange[ 1 ];
\r
1282 inTexRange = CalcSurfaceTextureRange( ds );
\r
1284 es = (ds->texRange[ 0 ] > oldTexRange[ 0 ]) ? qtrue : qfalse;
\r
1285 et = (ds->texRange[ 1 ] > oldTexRange[ 1 ]) ? qtrue : qfalse;
\r
1287 if( inTexRange == qfalse && ds->numIndexes > 0 )
\r
1289 memcpy( ds, &old, sizeof( *ds ) );
\r
1290 return UNSUITABLE_TRIANGLE;
\r
1293 /* score texture range */
\r
1294 if( ds->texRange[ 0 ] <= oldTexRange[ 0 ] )
\r
1295 score += ST_SCORE2;
\r
1296 else if( ds->texRange[ 0 ] > oldTexRange[ 0 ] && oldTexRange[ 1 ] > oldTexRange[ 0 ] )
\r
1297 score += ST_SCORE;
\r
1299 if( ds->texRange[ 1 ] <= oldTexRange[ 1 ] )
\r
1300 score += ST_SCORE2;
\r
1301 else if( ds->texRange[ 1 ] > oldTexRange[ 1 ] && oldTexRange[ 0 ] > oldTexRange[ 1 ] )
\r
1302 score += ST_SCORE;
\r
1305 /* go through the indexes and try to find an existing triangle that matches abc */
\r
1306 for( i = 0; i < ds->numIndexes; i += 3 )
\r
1308 /* 2002-03-11 (birthday!): rotate the triangle 3x to find an existing triangle */
\r
1309 if( (ai == ds->indexes[ i ] && bi == ds->indexes[ i + 1 ] && ci == ds->indexes[ i + 2 ]) ||
\r
1310 (bi == ds->indexes[ i ] && ci == ds->indexes[ i + 1 ] && ai == ds->indexes[ i + 2 ]) ||
\r
1311 (ci == ds->indexes[ i ] && ai == ds->indexes[ i + 1 ] && bi == ds->indexes[ i + 2 ]) )
\r
1313 /* triangle already present */
\r
1314 memcpy( ds, &old, sizeof( *ds ) );
\r
1319 /* rotate the triangle 3x to find an inverse triangle (error case) */
\r
1320 if( (ai == ds->indexes[ i ] && bi == ds->indexes[ i + 2 ] && ci == ds->indexes[ i + 1 ]) ||
\r
1321 (bi == ds->indexes[ i ] && ci == ds->indexes[ i + 2 ] && ai == ds->indexes[ i + 1 ]) ||
\r
1322 (ci == ds->indexes[ i ] && ai == ds->indexes[ i + 2 ] && bi == ds->indexes[ i + 1 ]) )
\r
1324 /* warn about it */
\r
1325 Sys_Printf( "WARNING: Flipped triangle: (%6.0f %6.0f %6.0f) (%6.0f %6.0f %6.0f) (%6.0f %6.0f %6.0f)\n",
\r
1326 ds->verts[ ai ].xyz[ 0 ], ds->verts[ ai ].xyz[ 1 ], ds->verts[ ai ].xyz[ 2 ],
\r
1327 ds->verts[ bi ].xyz[ 0 ], ds->verts[ bi ].xyz[ 1 ], ds->verts[ bi ].xyz[ 2 ],
\r
1328 ds->verts[ ci ].xyz[ 0 ], ds->verts[ ci ].xyz[ 1 ], ds->verts[ ci ].xyz[ 2 ] );
\r
1330 /* reverse triangle already present */
\r
1331 memcpy( ds, &old, sizeof( *ds ) );
\r
1337 /* add the triangle indexes */
\r
1338 if( ds->numIndexes < maxSurfaceIndexes )
\r
1339 ds->indexes[ ds->numIndexes++ ] = ai;
\r
1340 if( ds->numIndexes < maxSurfaceIndexes )
\r
1341 ds->indexes[ ds->numIndexes++ ] = bi;
\r
1342 if( ds->numIndexes < maxSurfaceIndexes )
\r
1343 ds->indexes[ ds->numIndexes++ ] = ci;
\r
1345 /* check index overflow */
\r
1346 if( ds->numIndexes >= maxSurfaceIndexes )
\r
1348 memcpy( ds, &old, sizeof( *ds ) );
\r
1352 /* sanity check the indexes */
\r
1353 if( ds->numIndexes >= 3 &&
\r
1354 (ds->indexes[ ds->numIndexes - 3 ] == ds->indexes[ ds->numIndexes - 2 ] ||
\r
1355 ds->indexes[ ds->numIndexes - 3 ] == ds->indexes[ ds->numIndexes - 1 ] ||
\r
1356 ds->indexes[ ds->numIndexes - 2 ] == ds->indexes[ ds->numIndexes - 1 ]) )
\r
1357 Sys_Printf( "DEG:%d! ", ds->numVerts );
\r
1359 /* testing only? */
\r
1361 memcpy( ds, &old, sizeof( *ds ) );
\r
1364 /* copy bounds back to surface */
\r
1365 VectorCopy( mins, ds->mins );
\r
1366 VectorCopy( maxs, ds->maxs );
\r
1368 /* mark triangle as used */
\r
1372 /* add a side reference */
\r
1373 ds->sideRef = AllocSideRef( tri->side, ds->sideRef );
\r
1375 /* return to sender */
\r
1382 MetaTrianglesToSurface()
\r
1383 creates map drawsurface(s) from the list of possibles
\r
1386 static void MetaTrianglesToSurface( int numPossibles, metaTriangle_t *possibles, int *fOld, int *numAdded )
\r
1388 int i, j, f, best, score, bestScore;
\r
1389 metaTriangle_t *seed, *test;
\r
1390 mapDrawSurface_t *ds;
\r
1391 bspDrawVert_t *verts;
\r
1396 /* allocate arrays */
\r
1397 verts = safe_malloc( sizeof( *verts ) * maxSurfaceVerts );
\r
1398 indexes = safe_malloc( sizeof( *indexes ) * maxSurfaceIndexes );
\r
1400 /* walk the list of triangles */
\r
1401 for( i = 0, seed = possibles; i < numPossibles; i++, seed++ )
\r
1403 /* skip this triangle if it has already been merged */
\r
1404 if( seed->si == NULL )
\r
1407 /* -----------------------------------------------------------------
\r
1408 initial drawsurf construction
\r
1409 ----------------------------------------------------------------- */
\r
1411 /* start a new drawsurface */
\r
1412 ds = AllocDrawSurface( SURFACE_META );
\r
1413 ds->entityNum = seed->entityNum;
\r
1414 ds->surfaceNum = seed->surfaceNum;
\r
1415 ds->castShadows = seed->castShadows;
\r
1416 ds->recvShadows = seed->recvShadows;
\r
1418 ds->shaderInfo = seed->si;
\r
1419 ds->planeNum = seed->planeNum;
\r
1420 ds->fogNum = seed->fogNum;
\r
1421 ds->sampleSize = seed->sampleSize;
\r
1422 ds->verts = verts;
\r
1423 ds->indexes = indexes;
\r
1424 VectorCopy( seed->lightmapAxis, ds->lightmapAxis );
\r
1425 ds->sideRef = AllocSideRef( seed->side, NULL );
\r
1427 ClearBounds( ds->mins, ds->maxs );
\r
1429 /* clear verts/indexes */
\r
1430 memset( verts, 0, sizeof( verts ) );
\r
1431 memset( indexes, 0, sizeof( indexes ) );
\r
1433 /* add the first triangle */
\r
1434 if( AddMetaTriangleToSurface( ds, seed, qfalse ) )
\r
1437 /* -----------------------------------------------------------------
\r
1439 ----------------------------------------------------------------- */
\r
1441 /* progressively walk the list until no more triangles can be added */
\r
1445 /* print pacifier */
\r
1446 f = 10 * *numAdded / numMetaTriangles;
\r
1450 Sys_FPrintf( SYS_VRB, "%d...", f );
\r
1453 /* reset best score */
\r
1458 /* walk the list of possible candidates for merging */
\r
1459 for( j = i + 1, test = &possibles[ j ]; j < numPossibles; j++, test++ )
\r
1461 /* score this triangle */
\r
1462 score = AddMetaTriangleToSurface( ds, test, qtrue );
\r
1463 if( score > bestScore )
\r
1466 bestScore = score;
\r
1468 /* if we have a score over a certain threshold, just use it */
\r
1469 if( bestScore >= GOOD_SCORE )
\r
1471 if( AddMetaTriangleToSurface( ds, &possibles[ best ], qfalse ) )
\r
1482 /* add best candidate */
\r
1483 if( best >= 0 && bestScore > ADEQUATE_SCORE )
\r
1485 if( AddMetaTriangleToSurface( ds, &possibles[ best ], qfalse ) )
\r
1493 /* copy the verts and indexes to the new surface */
\r
1494 ds->verts = safe_malloc( ds->numVerts * sizeof( bspDrawVert_t ) );
\r
1495 memcpy( ds->verts, verts, ds->numVerts * sizeof( bspDrawVert_t ) );
\r
1496 ds->indexes = safe_malloc( ds->numIndexes * sizeof( int ) );
\r
1497 memcpy( ds->indexes, indexes, ds->numIndexes * sizeof( int ) );
\r
1499 /* classify the surface */
\r
1500 ClassifySurfaces( 1, ds );
\r
1502 /* add to count */
\r
1503 numMergedSurfaces++;
\r
1514 CompareMetaTriangles()
\r
1515 compare function for qsort()
\r
1518 static int CompareMetaTriangles( const void *a, const void *b )
\r
1521 vec3_t aMins, bMins;
\r
1524 /* shader first */
\r
1525 if( ((metaTriangle_t*) a)->si < ((metaTriangle_t*) b)->si )
\r
1527 else if( ((metaTriangle_t*) a)->si > ((metaTriangle_t*) b)->si )
\r
1531 else if( ((metaTriangle_t*) a)->fogNum < ((metaTriangle_t*) b)->fogNum )
\r
1533 else if( ((metaTriangle_t*) a)->fogNum > ((metaTriangle_t*) b)->fogNum )
\r
1536 /* then position in world */
\r
1540 VectorSet( aMins, 999999, 999999, 999999 );
\r
1541 VectorSet( bMins, 999999, 999999, 999999 );
\r
1542 for( i = 0; i < 3; i++ )
\r
1544 av = ((metaTriangle_t*) a)->indexes[ i ];
\r
1545 bv = ((metaTriangle_t*) b)->indexes[ i ];
\r
1546 for( j = 0; j < 3; j++ )
\r
1548 if( metaVerts[ av ].xyz[ j ] < aMins[ j ] )
\r
1549 aMins[ j ] = metaVerts[ av ].xyz[ j ];
\r
1550 if( metaVerts[ bv ].xyz[ j ] < bMins[ j ] )
\r
1551 bMins[ j ] = metaVerts[ bv ].xyz[ j ];
\r
1556 for( i = 0; i < 3; i++ )
\r
1558 if( aMins[ i ] < bMins[ i ] )
\r
1560 else if( aMins[ i ] > bMins[ i ] )
\r
1565 /* functionally equivalent */
\r
1572 MergeMetaTriangles()
\r
1573 merges meta triangles into drawsurfaces
\r
1576 void MergeMetaTriangles( void )
\r
1578 int i, j, fOld, start, numAdded;
\r
1579 metaTriangle_t *head, *end;
\r
1582 /* only do this if there are meta triangles */
\r
1583 if( numMetaTriangles <= 0 )
\r
1587 Sys_FPrintf( SYS_VRB, "--- MergeMetaTriangles ---\n" );
\r
1589 /* sort the triangles by shader major, fognum minor */
\r
1590 qsort( metaTriangles, numMetaTriangles, sizeof( metaTriangle_t ), CompareMetaTriangles );
\r
1592 /* init pacifier */
\r
1594 start = I_FloatTime();
\r
1598 for( i = 0; i < numMetaTriangles; i = j )
\r
1600 /* get head of list */
\r
1601 head = &metaTriangles[ i ];
\r
1604 for( j = i + 1; j < numMetaTriangles; j++ )
\r
1606 /* get end of list */
\r
1607 end = &metaTriangles[ j ];
\r
1608 if( head->si != end->si || head->fogNum != end->fogNum )
\r
1612 /* try to merge this list of possible merge candidates */
\r
1613 MetaTrianglesToSurface( (j - i), head, &fOld, &numAdded );
\r
1616 /* clear meta triangle list */
\r
1617 ClearMetaTriangles();
\r
1621 Sys_FPrintf( SYS_VRB, " (%d)\n", (int) (I_FloatTime() - start) );
\r
1623 /* emit some stats */
\r
1624 Sys_FPrintf( SYS_VRB, "%9d surfaces merged\n", numMergedSurfaces );
\r
1625 Sys_FPrintf( SYS_VRB, "%9d vertexes merged\n", numMergedVerts );
\r