Source

quake3 / q3map / patch.c

/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.

This file is part of Quake III Arena source code.

Quake III Arena source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.

Quake III Arena source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with Foobar; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
===========================================================================
*/
#include "qbsp.h"


void PrintCtrl( vec3_t ctrl[9] ) {
	int		i, j;

	for ( i = 0 ; i < 3 ; i++ ) {
		for ( j = 0 ; j < 3 ; j++ ) {
			_printf("(%5.2f %5.2f %5.2f) ", ctrl[i*3+j][0], ctrl[i*3+j][1], ctrl[i*3+j][2] );
		}
		_printf("\n");
	}
}

/*
================
DrawSurfaceForMesh
================
*/
mapDrawSurface_t	*DrawSurfaceForMesh( mesh_t *m ) {
	mapDrawSurface_t	*ds;
	int				i, j;
	mesh_t			*copy;

	// to make valid normals for patches with degenerate edges,
	// we need to make a copy of the mesh and put the aproximating
	// points onto the curve
	copy = CopyMesh( m );
	PutMeshOnCurve( *copy );
	MakeMeshNormals( *copy );
	for ( j = 0 ; j < m->width ; j++ ) {
		for ( i = 0 ; i < m->height ; i++ ) {
			VectorCopy( copy->verts[i*m->width+j].normal, m->verts[i*m->width+j].normal );
		}
	}
	FreeMesh( copy );

	ds = AllocDrawSurf();
	ds->mapBrush = NULL;
	ds->side = NULL;

	ds->patch = qtrue;
	ds->patchWidth = m->width;
	ds->patchHeight = m->height;
	ds->numVerts = ds->patchWidth * ds->patchHeight;
	ds->verts = malloc( ds->numVerts * sizeof( *ds->verts ) );
	memcpy( ds->verts, m->verts, ds->numVerts * sizeof( *ds->verts ) );

	ds->lightmapNum = -1;
	ds->fogNum = -1;

	return ds;
}

/*
=================
ParsePatch

Creates a mapDrawSurface_t from the patch text
=================
*/
void ParsePatch( void ) {
	vec_t		info[5];
	int			i, j;
	parseMesh_t	*pm;
	char		texture[MAX_QPATH];
	char		shader[MAX_QPATH];
	mesh_t		m;
	drawVert_t	*verts;
  epair_t *ep;

	MatchToken( "{" );

	// get texture
	GetToken (qtrue);
	strcpy( texture, token );

	// save the shader name for retexturing
	if ( numMapIndexedShaders == MAX_MAP_BRUSHSIDES ) {
		Error( "MAX_MAP_BRUSHSIDES" );
	}
	strcpy( mapIndexedShaders[numMapIndexedShaders], texture );
	numMapIndexedShaders++;


	Parse1DMatrix( 5, info );
	m.width = info[0];
	m.height = info[1];
	m.verts = verts = malloc( m.width * m.height * sizeof( m.verts[0] ) );

	if ( m.width < 0 || m.width > MAX_PATCH_SIZE
		|| m.height < 0 || m.height > MAX_PATCH_SIZE ) {
		Error("ParsePatch: bad size");
	}

	MatchToken( "(" );
	for ( j = 0 ; j < m.width ; j++ ) {
		MatchToken( "(" );
		for ( i = 0 ; i < m.height ; i++ ) {
			Parse1DMatrix( 5, verts[i*m.width+j].xyz );
		}
		MatchToken( ")" );
	}
	MatchToken( ")" );

  // if brush primitives format, we may have some epairs to ignore here
  GetToken(qtrue);
  if (g_bBrushPrimit!=BPRIMIT_OLDBRUSHES && strcmp(token,"}"))
  {
    // NOTE: we leak that!
    ep = ParseEpair();
  }
  else
    UnGetToken();

	MatchToken( "}" );
	MatchToken( "}" );

	if ( noCurveBrushes ) {
		return;
	}

	// find default flags and values
	pm = malloc( sizeof( *pm ) );
	memset( pm, 0, sizeof( *pm ) );

	sprintf( shader, "textures/%s", texture );
	pm->shaderInfo = ShaderInfoForShader( shader ); 
	pm->mesh = m;

	// link to the entity
	pm->next = mapent->patches;
	mapent->patches = pm;
}


void GrowGroup_r( int patchNum, int patchCount, const byte *bordering, byte *group ) {
	int		i;
	const byte *row;

	if ( group[patchNum] ) {
		return;
	}
	group[patchNum] = 1;
	row = bordering + patchNum * patchCount;
	for ( i = 0 ; i < patchCount ; i++ ) {
		if ( row[i] ) {
			GrowGroup_r( i, patchCount, bordering, group );
		}
	}
}


/*
=====================
PatchMapDrawSurfs

Any patches that share an edge need to choose their
level of detail as a unit, otherwise the edges would
pull apart.
=====================
*/
void PatchMapDrawSurfs( entity_t *e ) {
	parseMesh_t			*pm;
	parseMesh_t			*check, *scan;
	mapDrawSurface_t	*ds;
	int					patchCount, groupCount;
	int					i, j, k, l, c1, c2;
	drawVert_t			*v1, *v2;
	vec3_t				bounds[2];
	byte				*bordering;
	parseMesh_t			*meshes[MAX_MAP_DRAW_SURFS];
	qboolean			grouped[MAX_MAP_DRAW_SURFS];
	byte				group[MAX_MAP_DRAW_SURFS];

	qprintf( "----- PatchMapDrawSurfs -----\n" );

	patchCount = 0;
	for ( pm = e->patches ; pm ; pm = pm->next  ) {
		meshes[patchCount] = pm;
		patchCount++;
	}

	if ( !patchCount ) {
		return;
	}
	bordering = malloc( patchCount * patchCount );
	memset( bordering, 0, patchCount * patchCount );

	// build the bordering matrix
	for ( k = 0 ; k < patchCount ; k++ ) {
		bordering[k*patchCount+k] = 1;

		for ( l = k+1 ; l < patchCount ; l++ ) {
			check = meshes[k];
			scan = meshes[l];
			c1 = scan->mesh.width * scan->mesh.height;
			v1 = scan->mesh.verts;

			for ( i = 0 ; i < c1 ; i++, v1++ ) {
				c2 = check->mesh.width * check->mesh.height;
				v2 = check->mesh.verts;
				for ( j = 0 ; j < c2 ; j++, v2++ ) {
					if ( fabs( v1->xyz[0] - v2->xyz[0] ) < 1.0
						&& fabs( v1->xyz[1] - v2->xyz[1] ) < 1.0
						&& fabs( v1->xyz[2] - v2->xyz[2] ) < 1.0 ) {
						break;
					}
				}
				if ( j != c2 ) {
					break;
				}
			}
			if ( i != c1 ) {
				// we have a connection
				bordering[k*patchCount+l] =
				bordering[l*patchCount+k] = 1;
			} else {
				// no connection
				bordering[k*patchCount+l] =
				bordering[l*patchCount+k] = 0;
			}

		}
	}

	// build groups
	memset( grouped, 0, sizeof(grouped) );
	groupCount = 0;
	for ( i = 0 ; i < patchCount ; i++ ) {
		if ( !grouped[i] ) {
			groupCount++;
		}

		// recursively find all patches that belong in the same group
		memset( group, 0, patchCount );
		GrowGroup_r( i, patchCount, bordering, group );

		// bound them
		ClearBounds( bounds[0], bounds[1] );
		for ( j = 0 ; j < patchCount ; j++ ) {
			if ( group[j] ) {
				grouped[j] = qtrue;
				scan = meshes[j];
				c1 = scan->mesh.width * scan->mesh.height;
				v1 = scan->mesh.verts;
				for ( k = 0 ; k < c1 ; k++, v1++ ) {
					AddPointToBounds( v1->xyz, bounds[0], bounds[1] );
				}
			}
		}

		// create drawsurf
		scan = meshes[i];
		scan->grouped = qtrue;
		ds = DrawSurfaceForMesh( &scan->mesh );
		ds->shaderInfo = scan->shaderInfo;
		VectorCopy( bounds[0], ds->lightmapVecs[0] );
		VectorCopy( bounds[1], ds->lightmapVecs[1] );
	}

	qprintf( "%5i patches\n", patchCount );
	qprintf( "%5i patch LOD groups\n", groupCount );
}