1 /* -------------------------------------------------------------------------------
3 Copyright (C) 1999-2007 id Software, Inc. and contributors.
4 For a list of contributors, see the accompanying CONTRIBUTORS file.
6 This file is part of GtkRadiant.
8 GtkRadiant is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
13 GtkRadiant is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with GtkRadiant; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22 ----------------------------------------------------------------------------------
24 This code has been altered significantly from its original form, to support
25 several games based on the Quake III Arena engine, in the form of "Q3Map2."
27 ------------------------------------------------------------------------------- */
42 ExpandLongestCurve() - ydnar
43 finds length of quadratic curve specified and determines if length is longer than the supplied max
46 #define APPROX_SUBDIVISION 8
48 static void ExpandLongestCurve( float *longestCurve, vec3_t a, vec3_t b, vec3_t c ){
51 vec3_t ab, bc, ac, pt, last, delta;
55 VectorSubtract( b, a, ab );
56 if ( VectorNormalize( ab, ab ) < 0.125f ) {
59 VectorSubtract( c, b, bc );
60 if ( VectorNormalize( bc, bc ) < 0.125f ) {
63 VectorSubtract( c, a, ac );
64 if ( VectorNormalize( ac, ac ) < 0.125f ) {
68 /* if all 3 vectors are the same direction, then this edge is linear, so we ignore it */
69 if ( DotProduct( ab, bc ) > 0.99f && DotProduct( ab, ac ) > 0.99f ) {
73 /* recalculate vectors */
74 VectorSubtract( b, a, ab );
75 VectorSubtract( c, b, bc );
77 /* determine length */
78 VectorCopy( a, last );
79 for ( i = 0, len = 0.0f, t = 0.0f; i < APPROX_SUBDIVISION; i++, t += ( 1.0f / APPROX_SUBDIVISION ) )
82 delta[ 0 ] = ( ( 1.0f - t ) * ab[ 0 ] ) + ( t * bc[ 0 ] );
83 delta[ 1 ] = ( ( 1.0f - t ) * ab[ 1 ] ) + ( t * bc[ 1 ] );
84 delta[ 2 ] = ( ( 1.0f - t ) * ab[ 2 ] ) + ( t * bc[ 2 ] );
86 /* add to first point and calculate pt-pt delta */
87 VectorAdd( a, delta, pt );
88 VectorSubtract( pt, last, delta );
90 /* add it to length and store last point */
91 len += VectorLength( delta );
92 VectorCopy( pt, last );
96 if ( len > *longestCurve ) {
104 ExpandMaxIterations() - ydnar
105 determines how many iterations a quadratic curve needs to be subdivided with to fit the specified error
108 static void ExpandMaxIterations( int *maxIterations, int maxError, vec3_t a, vec3_t b, vec3_t c ){
110 vec3_t prev, next, mid, delta, delta2;
112 int numPoints, iterations;
113 vec3_t points[ MAX_EXPANDED_AXIS ];
118 VectorCopy( a, points[ 0 ] );
119 VectorCopy( b, points[ 1 ] );
120 VectorCopy( c, points[ 2 ] );
123 for ( i = 0; i + 2 < numPoints; i += 2 )
125 /* check subdivision limit */
126 if ( numPoints + 2 >= MAX_EXPANDED_AXIS ) {
130 /* calculate new curve deltas */
131 for ( j = 0; j < 3; j++ )
133 prev[ j ] = points[ i + 1 ][ j ] - points[ i ][ j ];
134 next[ j ] = points[ i + 2 ][ j ] - points[ i + 1 ][ j ];
135 mid[ j ] = ( points[ i ][ j ] + points[ i + 1 ][ j ] * 2.0f + points[ i + 2 ][ j ] ) * 0.25f;
138 /* see if this midpoint is off far enough to subdivide */
139 VectorSubtract( points[ i + 1 ], mid, delta );
140 len = VectorLength( delta );
141 if ( len < maxError ) {
148 /* create new points */
149 for ( j = 0; j < 3; j++ )
151 prev[ j ] = 0.5f * ( points[ i ][ j ] + points[ i + 1 ][ j ] );
152 next[ j ] = 0.5f * ( points[ i + 1 ][ j ] + points[ i + 2 ][ j ] );
153 mid[ j ] = 0.5f * ( prev[ j ] + next[ j ] );
156 /* push points out */
157 for ( j = numPoints - 1; j > i + 3; j-- )
158 VectorCopy( points[ j - 2 ], points[ j ] );
160 /* insert new points */
161 VectorCopy( prev, points[ i + 1 ] );
162 VectorCopy( mid, points[ i + 2 ] );
163 VectorCopy( next, points[ i + 3 ] );
165 /* back up and recheck this set again, it may need more subdivision */
169 /* put the line on the curve */
170 for ( i = 1; i < numPoints; i += 2 )
172 for ( j = 0; j < 3; j++ )
174 prev[ j ] = 0.5f * ( points[ i ][ j ] + points[ i + 1 ][ j ] );
175 next[ j ] = 0.5f * ( points[ i ][ j ] + points[ i - 1 ][ j ] );
176 points[ i ][ j ] = 0.5f * ( prev[ j ] + next[ j ] );
180 /* eliminate linear sections */
181 for ( i = 0; i + 2 < numPoints; i++ )
184 VectorSubtract( points[ i + 1 ], points[ i ], delta );
185 len = VectorNormalize( delta, delta );
186 VectorSubtract( points[ i + 2 ], points[ i + 1 ], delta2 );
187 len2 = VectorNormalize( delta2, delta2 );
189 /* if either edge is degenerate, then eliminate it */
190 if ( len < 0.0625f || len2 < 0.0625f || DotProduct( delta, delta2 ) >= 1.0f ) {
191 for ( j = i + 1; j + 1 < numPoints; j++ )
192 VectorCopy( points[ j + 1 ], points[ j ] );
198 /* the number of iterations is 2^(points - 1) - 1 */
201 while ( numPoints > 1 )
208 if ( iterations > *maxIterations ) {
209 *maxIterations = iterations;
217 creates a mapDrawSurface_t from the patch text
220 void ParsePatch( qboolean onlyLights ){
224 char texture[ MAX_QPATH ];
225 char shader[ MAX_QPATH ];
227 bspDrawVert_t *verts;
229 vec4_t delta, delta2, delta3;
238 strcpy( texture, token );
240 Parse1DMatrix( 5, info );
243 m.verts = verts = safe_malloc( m.width * m.height * sizeof( m.verts[0] ) );
245 if ( m.width < 0 || m.width > MAX_PATCH_SIZE || m.height < 0 || m.height > MAX_PATCH_SIZE ) {
246 Error( "ParsePatch: bad size" );
250 for ( j = 0; j < m.width ; j++ )
253 for ( i = 0; i < m.height ; i++ )
255 Parse1DMatrix( 5, verts[ i * m.width + j ].xyz );
257 /* ydnar: fix colors */
258 for ( k = 0; k < MAX_LIGHTMAPS; k++ )
260 verts[ i * m.width + j ].color[ k ][ 0 ] = 255;
261 verts[ i * m.width + j ].color[ k ][ 1 ] = 255;
262 verts[ i * m.width + j ].color[ k ][ 2 ] = 255;
263 verts[ i * m.width + j ].color[ k ][ 3 ] = 255;
270 // if brush primitives format, we may have some epairs to ignore here
272 if ( g_bBrushPrimit != BPRIMIT_OLDBRUSHES && strcmp( token,"}" ) ) {
286 if ( noCurveBrushes || onlyLights ) {
291 /* ydnar: delete and warn about degenerate patches */
292 j = ( m.width * m.height );
293 VectorClear( delta );
297 /* find first valid vector */
298 for ( i = 1; i < j && delta[ 3 ] == 0; i++ )
300 VectorSubtract( m.verts[ 0 ].xyz, m.verts[ i ].xyz, delta );
301 delta[ 3 ] = VectorNormalize( delta, delta );
304 /* secondary degenerate test */
305 if ( delta[ 3 ] == 0 ) {
310 /* if all vectors match this or are zero, then this is a degenerate patch */
311 for ( i = 1; i < j && degenerate == qtrue; i++ )
313 VectorSubtract( m.verts[ 0 ].xyz, m.verts[ i ].xyz, delta2 );
314 delta2[ 3 ] = VectorNormalize( delta2, delta2 );
315 if ( delta2[ 3 ] != 0 ) {
316 /* create inverse vector */
317 VectorCopy( delta2, delta3 );
318 delta3[ 3 ] = delta2[ 3 ];
319 VectorInverse( delta3 );
322 if ( VectorCompare( delta, delta2 ) == qfalse && VectorCompare( delta, delta3 ) == qfalse ) {
329 /* warn and select degenerate patch */
331 xml_Select( "degenerate patch", mapEnt->mapEntityNum, entitySourceBrushes, qfalse );
336 /* find longest curve on the mesh */
339 for ( j = 0; j + 2 < m.width; j += 2 )
341 for ( i = 0; i + 2 < m.height; i += 2 )
343 ExpandLongestCurve( &longestCurve, verts[ i * m.width + j ].xyz, verts[ i * m.width + ( j + 1 ) ].xyz, verts[ i * m.width + ( j + 2 ) ].xyz ); /* row */
344 ExpandLongestCurve( &longestCurve, verts[ i * m.width + j ].xyz, verts[ ( i + 1 ) * m.width + j ].xyz, verts[ ( i + 2 ) * m.width + j ].xyz ); /* col */
345 ExpandMaxIterations( &maxIterations, patchSubdivisions, verts[ i * m.width + j ].xyz, verts[ i * m.width + ( j + 1 ) ].xyz, verts[ i * m.width + ( j + 2 ) ].xyz ); /* row */
346 ExpandMaxIterations( &maxIterations, patchSubdivisions, verts[ i * m.width + j ].xyz, verts[ ( i + 1 ) * m.width + j ].xyz, verts[ ( i + 2 ) * m.width + j ].xyz ); /* col */
350 /* allocate patch mesh */
351 pm = safe_malloc0( sizeof( *pm ) );
353 /* ydnar: add entity/brush numbering */
354 pm->entityNum = mapEnt->mapEntityNum;
355 pm->brushNum = entitySourceBrushes;
358 sprintf( shader, "textures/%s", texture );
359 pm->shaderInfo = ShaderInfoForShader( shader );
364 /* set longest curve */
365 pm->longestCurve = longestCurve;
366 pm->maxIterations = maxIterations;
368 /* link to the entity */
369 pm->next = mapEnt->patches;
370 mapEnt->patches = pm;
377 recursively adds patches to a lod group
380 static void GrowGroup_r( parseMesh_t *pm, int patchNum, int patchCount, parseMesh_t **meshes, byte *bordering, byte *group ){
385 /* early out check */
386 if ( group[ patchNum ] ) {
392 group[ patchNum ] = 1;
393 row = bordering + patchNum * patchCount;
396 if ( meshes[ patchNum ]->longestCurve > pm->longestCurve ) {
397 pm->longestCurve = meshes[ patchNum ]->longestCurve;
399 if ( meshes[ patchNum ]->maxIterations > pm->maxIterations ) {
400 pm->maxIterations = meshes[ patchNum ]->maxIterations;
403 /* walk other patches */
404 for ( i = 0; i < patchCount; i++ )
407 GrowGroup_r( pm, i, patchCount, meshes, bordering, group );
415 any patches that share an edge need to choose their
416 level of detail as a unit, otherwise the edges would
420 void PatchMapDrawSurfs( entity_t *e ){
421 int i, j, k, l, c1, c2;
423 parseMesh_t *check, *scan;
424 mapDrawSurface_t *ds;
425 int patchCount, groupCount;
426 bspDrawVert_t *v1, *v2;
430 parseMesh_t *meshes[ MAX_MAP_DRAW_SURFS ];
431 qb_t grouped[ MAX_MAP_DRAW_SURFS ];
432 byte group[ MAX_MAP_DRAW_SURFS ];
436 Sys_FPrintf( SYS_VRB, "--- PatchMapDrawSurfs ---\n" );
439 for ( pm = e->patches ; pm ; pm = pm->next ) {
440 meshes[patchCount] = pm;
447 bordering = safe_malloc0( patchCount * patchCount );
449 // build the bordering matrix
450 for ( k = 0 ; k < patchCount ; k++ ) {
451 bordering[k * patchCount + k] = 1;
453 for ( l = k + 1 ; l < patchCount ; l++ ) {
456 c1 = scan->mesh.width * scan->mesh.height;
457 v1 = scan->mesh.verts;
459 for ( i = 0 ; i < c1 ; i++, v1++ ) {
460 c2 = check->mesh.width * check->mesh.height;
461 v2 = check->mesh.verts;
462 for ( j = 0 ; j < c2 ; j++, v2++ ) {
463 if ( fabs( v1->xyz[0] - v2->xyz[0] ) < 1.0
464 && fabs( v1->xyz[1] - v2->xyz[1] ) < 1.0
465 && fabs( v1->xyz[2] - v2->xyz[2] ) < 1.0 ) {
474 // we have a connection
475 bordering[k * patchCount + l] =
476 bordering[l * patchCount + k] = 1;
480 bordering[k * patchCount + l] =
481 bordering[l * patchCount + k] = 0;
488 memset( grouped, 0, patchCount );
490 for ( i = 0; i < patchCount; i++ )
495 /* start a new group */
496 if ( !grouped[ i ] ) {
500 /* recursively find all patches that belong in the same group */
501 memset( group, 0, patchCount );
502 GrowGroup_r( scan, i, patchCount, meshes, bordering, group );
505 ClearBounds( bounds[ 0 ], bounds[ 1 ] );
506 for ( j = 0; j < patchCount; j++ )
509 grouped[ j ] = qtrue;
511 c1 = check->mesh.width * check->mesh.height;
512 v1 = check->mesh.verts;
513 for ( k = 0; k < c1; k++, v1++ )
514 AddPointToBounds( v1->xyz, bounds[ 0 ], bounds[ 1 ] );
519 //% Sys_Printf( "Longest curve: %f Iterations: %d\n", scan->longestCurve, scan->maxIterations );
521 /* create drawsurf */
522 scan->grouped = qtrue;
523 ds = DrawSurfaceForMesh( e, scan, NULL ); /* ydnar */
524 VectorCopy( bounds[ 0 ], ds->bounds[ 0 ] );
525 VectorCopy( bounds[ 1 ], ds->bounds[ 1 ] );
528 /* emit some statistics */
529 Sys_FPrintf( SYS_VRB, "%9d patches\n", patchCount );
530 Sys_FPrintf( SYS_VRB, "%9d patch LOD groups\n", groupCount );