Quake-III-Arena

Quake III Arena GPL Source Release
Log | Files | Refs

terrain.cpp (40790B)


      1 /*
      2 ===========================================================================
      3 Copyright (C) 1999-2005 Id Software, Inc.
      4 
      5 This file is part of Quake III Arena source code.
      6 
      7 Quake III Arena source code is free software; you can redistribute it
      8 and/or modify it under the terms of the GNU General Public License as
      9 published by the Free Software Foundation; either version 2 of the License,
     10 or (at your option) any later version.
     11 
     12 Quake III Arena source code is distributed in the hope that it will be
     13 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
     14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     15 GNU General Public License for more details.
     16 
     17 You should have received a copy of the GNU General Public License
     18 along with Foobar; if not, write to the Free Software
     19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
     20 ===========================================================================
     21 */
     22 /*
     23 
     24 Todo:
     25 
     26 immediate:
     27 Texture placement
     28 New map format
     29 q3map
     30 
     31 
     32 later:
     33 Smoothing brush
     34 Stitching terrains together
     35 Cross terrain selection
     36 
     37 
     38 
     39 
     40 
     41 Terrain_ApplyMatrix
     42 UpdateTerrainInspector
     43 
     44 */
     45 
     46 #include "stdafx.h"
     47 #include "qe3.h"
     48 #include "DialogInfo.h"
     49 #include "assert.h"
     50 
     51 //random in the range [0, 1]
     52 #define random()			((rand () & 0x7fff) / ((float)0x7fff))
     53 
     54 //random in the range [-1, 1]
     55 #define crandom()			(2.0 * (random() - 0.5))
     56 
     57 typedef struct {
     58 	int		index;
     59 	vec3_t	xyz;
     60 	vec4_t	rgba;
     61 	vec2_t	tc;
     62 } terravert_t;
     63 
     64 /*
     65 ==============
     66 Terrain_SetEpair
     67 sets an epair for the given patch
     68 ==============
     69 */
     70 void Terrain_SetEpair( terrainMesh_t *p, const char *pKey, const char *pValue ) {
     71 	if ( g_qeglobals.m_bBrushPrimitMode ) {
     72 		SetKeyValue( p->epairs, pKey, pValue );
     73 	}
     74 }
     75 
     76 /* 
     77 =================
     78 Terrain_GetKeyValue
     79 =================
     80 */
     81 const char *Terrain_GetKeyValue( terrainMesh_t *p, const char *pKey ) {
     82 	if ( g_qeglobals.m_bBrushPrimitMode ) {
     83 		return ValueForKey( p->epairs, pKey );
     84 	}
     85 	return "";
     86 }
     87 
     88 /*
     89 ==================
     90 Terrain_MemorySize
     91 ==================
     92 */
     93 int Terrain_MemorySize( terrainMesh_t *p ) {
     94 	return _msize( p );
     95 }
     96 
     97 void Terrain_GetVert( terrainMesh_t *pm, int x, int y, float s, float t, terravert_t *v, qtexture_t *texture = NULL ) {
     98 	terrainVert_t *cell;
     99 
    100 	v->index = x + y * pm->width;
    101 
    102 	cell = &pm->heightmap[ v->index ];
    103 
    104 	v->xyz[ 0 ] = pm->origin[ 0 ] + x * pm->scale_x;
    105 	v->xyz[ 1 ] = pm->origin[ 1 ] + y * pm->scale_y;
    106 	v->xyz[ 2 ] = pm->origin[ 2 ] + cell->height;
    107 
    108 	VectorCopy( cell->rgba, v->rgba );
    109 
    110 	if ( !texture || ( texture == cell->tri.texture ) ) {
    111 		v->rgba[ 3 ] = 1.0f;
    112 	} else {
    113 		v->rgba[ 3 ] = 0.0f;
    114 	}
    115 
    116 	v->tc[ 0 ] = s;
    117 	v->tc[ 1 ] = t;
    118 }
    119 
    120 void Terrain_GetTriangles( terrainMesh_t *pm, int x, int y, terravert_t *a0, terravert_t *a1, terravert_t *a2, terravert_t *b0, terravert_t *b1, terravert_t *b2, qtexture_t *texture ) {
    121 	if ( ( x + y ) & 1 ) {
    122 		// first tri
    123 		Terrain_GetVert( pm, x, y, 1.0f, 1.0f, a0, texture );
    124 		Terrain_GetVert( pm, x, y + 1, 1.0f, 0.0f, a1, texture );
    125 		Terrain_GetVert( pm, x + 1, y + 1, 0.0f, 0.0f, a2, texture );
    126 
    127 		// second tri
    128 		*b0 = *a2;
    129 		Terrain_GetVert( pm, x + 1, y, 0.0f, 1.0f, b1, texture );
    130 		*b2 = *a0;
    131 	} else {
    132 		// first tri
    133 		Terrain_GetVert( pm, x, y, 1.0f, 1.0f, a0, texture );
    134 		Terrain_GetVert( pm, x, y + 1, 1.0f, 0.0f, a1, texture );
    135 		Terrain_GetVert( pm, x + 1, y, 0.0f, 1.0f, a2, texture );
    136 
    137 		// second tri
    138 		*b0 = *a2;
    139 		*b1 = *a1;
    140 		Terrain_GetVert( pm, x + 1, y + 1, 0.0f, 0.0f, b2, texture );
    141 	}
    142 }
    143 
    144 void Terrain_GetTriangle( terrainMesh_t *pm, int index, terravert_t *a0, terravert_t *a1, terravert_t *a2 ) {
    145 	int x;
    146 	int y;
    147 	int which;
    148 
    149 	which = index & 1;
    150 	index >>= 1;
    151 	y = index / pm->width;
    152 	x = index % pm->width;
    153 
    154 	if ( ( x + y ) & 1 ) {
    155 		if ( !which ) {
    156 			// first tri
    157 			Terrain_GetVert( pm, x, y, 1.0f, 1.0f, a0 );
    158 			Terrain_GetVert( pm, x, y + 1, 1.0f, 0.0f, a1 );
    159 			Terrain_GetVert( pm, x + 1, y + 1, 0.0f, 0.0f, a2 );
    160 		} else {
    161 			Terrain_GetVert( pm, x + 1, y + 1, 0.0f, 0.0f, a0 );
    162 			Terrain_GetVert( pm, x + 1, y, 0.0f, 1.0f, a1 );
    163 			Terrain_GetVert( pm, x, y, 1.0f, 1.0f, a2 );
    164 		}
    165 	} else {
    166 		if ( !which ) {
    167 			// first tri
    168 			Terrain_GetVert( pm, x, y, 1.0f, 1.0f, a0 );
    169 			Terrain_GetVert( pm, x, y + 1, 1.0f, 0.0f, a1 );
    170 			Terrain_GetVert( pm, x + 1, y, 0.0f, 1.0f, a2 );
    171 		} else {
    172 			Terrain_GetVert( pm, x + 1, y, 0.0f, 1.0f, a0 );
    173 			Terrain_GetVert( pm, x, y + 1, 1.0f, 0.0f, a1 );
    174 			Terrain_GetVert( pm, x + 1, y + 1, 0.0f, 0.0f, a2 );
    175 		}
    176 	}
    177 }
    178 
    179 void Terrain_Delete( terrainMesh_t *p ) {
    180 	if ( p->pSymbiot ) {
    181 		p->pSymbiot->pTerrain = NULL;
    182 		p->pSymbiot->terrainBrush = false;
    183 	}
    184 
    185 	free( p );
    186 
    187 	p = NULL;
    188    
    189 	UpdateTerrainInspector();
    190 }
    191 
    192 void Terrain_AddTexture( terrainMesh_t *pm, qtexture_t *texture ) {
    193 	int i;
    194 
    195 	if ( !texture ) {
    196 		return;
    197 	}
    198 
    199 	for( i = 0; i < pm->numtextures; i++ ) {
    200 		if ( pm->textures[ i ] == texture ) {
    201 			return;
    202 		}
    203 	}
    204 
    205 	if ( pm->numtextures >= MAX_TERRAIN_TEXTURES ) {
    206 		Warning( "Too many textures on terrain" );
    207 		return;
    208 	}
    209 
    210 	pm->textures[ pm->numtextures++ ] = texture;
    211 }
    212 
    213 void Terrain_RemoveTexture( terrainMesh_t *p, qtexture_t *texture ) {
    214 	int i;
    215 
    216 	for( i = 0; i < p->numtextures; i++ ) {
    217 		if ( p->textures[ i ] == texture ) {
    218 			break;
    219 		}
    220 	}
    221 
    222 	if ( i < p->numtextures ) {
    223 		// shift all textures down to remove the texture from the list
    224 		p->numtextures--;
    225 		for( ; i < p->numtextures; i++ ) {
    226 			p->textures[ i ] = p->textures[ i + 1 ];
    227 		}
    228 	}
    229 }
    230 
    231 terrainMesh_t *MakeNewTerrain( int width, int height, qtexture_t *texture ) {
    232 	int				h;
    233 	int				w;
    234 	terrainMesh_t	*pm;
    235 	size_t			size;
    236 	size_t			heightmapsize;
    237 	terrainVert_t	*vert;
    238 	int				index;
    239 
    240 	heightmapsize  = sizeof( terrainVert_t ) * width * height;
    241 	size           = sizeof( terrainMesh_t ) + heightmapsize;
    242    
    243 	pm = reinterpret_cast< terrainMesh_t * >( qmalloc( size ) );
    244    
    245 	memset( pm, 0x00, size );
    246 
    247 	pm->numtextures = 0;
    248 	pm->width      = width;
    249 	pm->height     = height;
    250 	pm->heightmap  = reinterpret_cast< terrainVert_t * >( pm + 1 );
    251 
    252 	if ( texture ) {
    253 		Terrain_AddTexture( pm, texture );
    254 	}
    255 
    256 	index = 0;
    257 	vert = pm->heightmap;
    258 	for( h = 0; h < pm->height; h++ ) {
    259 		for( w = 0; w < pm->width; w++, vert++ ) {
    260 			vert->tri.index = index++;
    261 			vert->tri.texture = texture;
    262 			if ( texture ) {
    263 				vert->tri.texdef.SetName( texture->name );
    264 			}
    265 
    266 			vert->height = 0;
    267 
    268 			VectorClear( vert->normal );
    269 			VectorSet( vert->rgba, 1.0f, 1.0f, 1.0f );
    270 			vert->rgba[ 3 ] = 1.0f;
    271 		}
    272 	}
    273    
    274 	return pm;
    275 }
    276 
    277 brush_t *AddBrushForTerrain( terrainMesh_t *pm, bool bLinkToWorld ) {
    278 	int		j;
    279 	vec3_t	vMin;
    280 	vec3_t	vMax;
    281 	brush_t	*b;
    282 	face_t	*f;
    283 
    284 	// calculate the face normals
    285 	Terrain_CalcNormals( pm );
    286    
    287 	// find the farthest points in x,y,z
    288 	Terrain_CalcBounds( pm, vMin, vMax );
    289 
    290 	for( j = 0; j < 3; j++ ) {
    291 		if ( vMin[ j ] == vMax[ j ] ) {
    292 			vMin[ j ] -= 4;
    293 			vMax[ j ] += 4;
    294 		}
    295 	}
    296 
    297 	b = Brush_Create( vMin, vMax, &pm->heightmap->tri.texdef );
    298 
    299 	for( f = b->brush_faces; f != NULL; f = f->next ) {
    300 		// copy the texdef to the brush faces texdef
    301 		f->texdef = pm->heightmap->tri.texdef;
    302 	}
    303 
    304 	// FIXME: this entire type of linkage needs to be fixed
    305 	b->pTerrain		= pm;
    306 	b->terrainBrush = true;
    307 	pm->pSymbiot    = b;
    308 	pm->bSelected   = false;
    309 	pm->bDirty      = true;
    310 	pm->nListID		= -1;
    311 
    312 	if ( bLinkToWorld ) {
    313 		Brush_AddToList( b, &active_brushes );
    314 		Entity_LinkBrush( world_entity, b );
    315 		Brush_Build( b, true );
    316 	}
    317   
    318 	return b;
    319 }
    320 
    321 terrainMesh_t *Terrain_Duplicate( terrainMesh_t *pFrom ) {
    322 	terrainMesh_t *p;
    323 	int w;
    324 	int h;
    325 	int index;
    326 
    327 	p = MakeNewTerrain( pFrom->width, pFrom->height );
    328    
    329 	VectorCopy( pFrom->origin, p->origin );
    330 	VectorCopy( pFrom->mins, p->mins );
    331 	VectorCopy( pFrom->maxs, p->maxs );
    332 
    333 	p->scale_x  = pFrom->scale_x;
    334 	p->scale_y  = pFrom->scale_y;
    335 	p->pSymbiot = pFrom->pSymbiot;
    336 
    337 	for( index = 0; index < pFrom->numtextures; index++ ) {
    338 		Terrain_AddTexture( p, pFrom->textures[ index ] );
    339 	}
    340 
    341 	index = 0;
    342 	for( h = 0; h < p->height; h++ ) {
    343 		for( w = 0; w < p->width; w++, index++ ) {
    344 			p->heightmap[ index ] = pFrom->heightmap[ index ];
    345 		}
    346 	}
    347    
    348 	p->bSelected   = false;
    349 	p->bDirty      = true;
    350 	p->nListID     = -1;
    351 
    352 	AddBrushForTerrain( p );
    353 
    354 	return p;
    355 }
    356 
    357 void Terrain_BrushToMesh( void ) {
    358 	brush_t			*b;
    359 	terrainMesh_t	*p;
    360 
    361 	if ( !QE_SingleBrush() ) {
    362 		return;
    363 	}
    364 
    365 	b = selected_brushes.next;
    366 
    367 	if ( g_qeglobals.d_terrainWidth	< 1 ) {
    368 		g_qeglobals.d_terrainWidth = 1;
    369 	}
    370 
    371 	if ( g_qeglobals.d_terrainHeight < 1 ) {
    372 		g_qeglobals.d_terrainHeight = 1;
    373 	}
    374 
    375 	p = MakeNewTerrain( g_qeglobals.d_terrainWidth + 1, g_qeglobals.d_terrainHeight + 1, b->brush_faces->d_texture );
    376 	p->scale_x = ( b->maxs[ 0 ] - b->mins[ 0 ] ) / float( p->width - 1 );
    377 	p->scale_y = ( b->maxs[ 1 ] - b->mins[ 1 ] ) / float( p->height - 1 );
    378 
    379 	VectorCopy( b->mins, p->origin );
    380 
    381 	b = AddBrushForTerrain( p );
    382 	Select_Delete();
    383 	Select_Brush( b );
    384 }
    385 
    386 terrainFace_t *Terrain_ParseFace( terrainFace_t *f ) {
    387 	// read the texturename
    388 	GetToken( false );
    389 	f->texdef.SetName( token );
    390 
    391 	// Load the texture, and set the face to that texture's defaults
    392 	f->texture = Texture_ForName( f->texdef.Name() );
    393 
    394 	// read the texturedef
    395 	GetToken( false );
    396 	f->texdef.shift[ 0 ] = ( float )atoi( token );
    397 	GetToken( false );
    398 	f->texdef.shift[ 1 ] = ( float )atoi( token );
    399 	GetToken( false );
    400 	f->texdef.rotate = atof( token );
    401 	GetToken( false );
    402 	f->texdef.scale[ 0 ] = atof( token );
    403 	GetToken( false );
    404 	f->texdef.scale[ 1 ] = atof( token );
    405 
    406 	// the flags and value field aren't necessarily present
    407 	//f->texture = Texture_ForName( f->texdef.Name() );
    408 	f->texdef.flags = f->texture->flags;
    409 	f->texdef.value = f->texture->value;
    410 	f->texdef.contents = f->texture->contents;
    411 	
    412 	if ( TokenAvailable () ) {
    413 		GetToken (false);
    414 		f->texdef.contents = atoi(token);
    415 		GetToken (false);
    416 		f->texdef.flags = atoi(token);
    417 		GetToken (false);
    418 		f->texdef.value = atoi(token);
    419 	}
    420 
    421 	return f;
    422 }
    423 
    424 brush_t *Terrain_Parse( void ) {
    425 	terrainMesh_t *pm;
    426 	terrainVert_t *vert;
    427 	int w;
    428 	int h;
    429 
    430 	GetToken( true );
    431     if ( strcmp( token, "{" ) ) {
    432 		return NULL;
    433 	}
    434 
    435 	// get width
    436 	GetToken( false );
    437 	w = atoi( token );
    438 
    439 	// get height
    440 	GetToken( false );
    441 	h = atoi( token );
    442 
    443 	pm = MakeNewTerrain( w, h );
    444 
    445 	// get scale_x
    446 	GetToken( false );
    447 	pm->scale_x = atoi( token );
    448 
    449 	// get scale_y
    450 	GetToken( false );
    451 	pm->scale_y = atoi( token );
    452 
    453 	// get origin
    454 	GetToken( true );
    455 	pm->origin[ 0 ] = atoi( token );
    456 	GetToken( false );
    457 	pm->origin[ 1 ] = atoi( token );
    458 	GetToken( false );
    459 	pm->origin[ 2 ] = atoi( token );
    460 
    461 	// get the height map
    462 	vert = pm->heightmap;
    463 	for( h = 0; h < pm->height; h++ ) {
    464 		for( w = 0; w < pm->width; w++, vert++ ) {
    465 			GetToken( true );
    466 			vert->height = atoi( token );
    467 
    468 			if ( !Terrain_ParseFace( &vert->tri ) ) {
    469 				Terrain_Delete( pm );
    470 				return NULL;
    471 			}
    472 
    473 			Terrain_AddTexture( pm, vert->tri.texture );
    474 		}
    475 	}
    476 
    477 	GetToken( true );
    478 	if ( strcmp( token, "}" ) ) {
    479 		Terrain_Delete( pm );
    480 		return NULL;
    481 	}
    482 
    483 	return AddBrushForTerrain( pm, false );
    484 }
    485 
    486 CString Terrain_SurfaceString( terrainFace_t *face ) {
    487 	char		temp[ 1024 ];
    488 	CString		text;
    489 	const char	*pname;
    490 
    491 	pname = face->texdef.Name();
    492 	if ( pname[ 0 ] == 0 ) {
    493 		pname = "unnamed";
    494 	}
    495 
    496 	sprintf( temp, "%s %i %i %.2f ", pname, ( int )face->texdef.shift[ 0 ], ( int )face->texdef.shift[ 1 ], face->texdef.rotate );
    497 	text += temp;
    498 
    499 	if ( face->texdef.scale[ 0 ] == ( int )face->texdef.scale[ 0 ] ) {
    500 		sprintf( temp, "%i ", ( int )face->texdef.scale[ 0 ] );
    501 	} else {
    502 		sprintf( temp, "%f ", ( float )face->texdef.scale[ 0 ] );
    503 	}
    504 	text += temp;
    505 
    506 	if ( face->texdef.scale[ 1 ] == (int)face->texdef.scale[ 1 ] ) {
    507 		sprintf( temp, "%i", ( int )face->texdef.scale[ 1 ] );
    508 	} else {
    509 		sprintf( temp, "%f", ( float )face->texdef.scale[ 1 ] );
    510 	}
    511 	text += temp;
    512 
    513 	// only output flags and value if not default
    514 	sprintf( temp, " %i %i %i ", face->texdef.contents, face->texdef.flags, face->texdef.value );
    515 	text += temp;
    516    
    517 	return text;
    518 }
    519 
    520 void Terrain_Write( terrainMesh_t *p, CMemFile *file ) {
    521 	int w;
    522 	int h;
    523 	terrainVert_t *vert;
    524 
    525 	MemFile_fprintf( file, " {\n  terrainDef\n  {\n" );
    526 	MemFile_fprintf( file, "   %d %d %f %f\n", p->width, p->height, p->scale_x, p->scale_y );
    527 	MemFile_fprintf( file, "   %f %f %f\n", p->origin[ 0 ], p->origin[ 1 ], p->origin[ 2 ] );
    528 
    529 	vert = p->heightmap;
    530 	for( h = 0; h < p->height; h++ ) {
    531 		for( w = 0; w < p->width; w++, vert++ ) {
    532 			MemFile_fprintf( file, "   %f %s\n", vert->height, ( const char * )Terrain_SurfaceString( &vert->tri ) );
    533 		}
    534 	}
    535    
    536 	MemFile_fprintf( file, "  }\n }\n" );
    537 }
    538 
    539 void Terrain_Write( terrainMesh_t *p, FILE *file ) {
    540 	int w;
    541 	int h;
    542 	terrainVert_t *vert;
    543 
    544 	fprintf( file, " {\n  terrainDef\n  {\n" );
    545 	fprintf( file, "   %d %d %f %f\n", p->width, p->height, p->scale_x, p->scale_y );
    546 	fprintf( file, "   %f %f %f\n", p->origin[ 0 ], p->origin[ 1 ], p->origin[ 2 ] );
    547 
    548 	vert = p->heightmap;
    549 	for( h = 0; h < p->height; h++ ) {
    550 		for( w = 0; w < p->width; w++, vert++ ) {
    551 			fprintf( file, "   %f %s\n", vert->height, ( const char * )Terrain_SurfaceString( &vert->tri ) );
    552 		}
    553 	}
    554    
    555 	fprintf( file, "  }\n }\n" );
    556 }
    557 
    558 void Terrain_Select( terrainMesh_t *p ) {
    559 	p->bSelected = true;
    560 }
    561 
    562 void Terrain_Deselect( terrainMesh_t *p ) {
    563 	p->bSelected = false;
    564 }
    565 
    566 void Terrain_Move( terrainMesh_t *pm, const vec3_t vMove, bool bRebuild ) {
    567 	pm->bDirty = true;
    568 
    569 	VectorAdd( pm->origin, vMove, pm->origin );
    570 
    571 	if ( bRebuild ) {
    572 		vec3_t vMin; 
    573 		vec3_t vMax;
    574 
    575 		Terrain_CalcBounds( pm, vMin, vMax );
    576 	}
    577   
    578 	UpdateTerrainInspector();
    579 }
    580 
    581 void UpdateTerrainInspector( void ) {
    582 	// not written yet
    583 }
    584 
    585 void Terrain_CalcBounds( terrainMesh_t *p, vec3_t &vMin, vec3_t &vMax ) {
    586 	int w;
    587 	int h;
    588 	float f;
    589 	terrainVert_t *vert;
    590 
    591 	vMin[ 0 ] = p->origin[ 0 ];
    592 	vMin[ 1 ] = p->origin[ 1 ];
    593 	vMin[ 2 ] = MAX_WORLD_COORD;
    594 
    595 	vMax[ 0 ] = p->origin[ 0 ] + ( p->width - 1 ) * p->scale_x;
    596 	vMax[ 1 ] = p->origin[ 1 ] + ( p->height - 1 ) * p->scale_y;
    597 	vMax[ 2 ] = MIN_WORLD_COORD;
    598 
    599 	p->bDirty = true;
    600 	vert = p->heightmap;
    601 	for( h = 0; h < p->height; h++ ) {
    602 		for( w = 0; w < p->width; w++, vert++ ) {
    603 			f = p->origin[ 2 ] + vert->height;
    604 			if ( f < vMin[ 2 ] ) {
    605 				vMin[ 2 ] = f;
    606 			}
    607 
    608 			if ( f > vMax[ 2 ] ) {
    609 				vMax[ 2 ] = f;
    610 			}
    611 		}
    612 	}
    613 }
    614 
    615 void CalcTriNormal( const vec3_t a, const vec3_t b, const vec3_t c, vec3_t o ) {
    616 	vec3_t a1;
    617 	vec3_t b1;
    618 
    619 	VectorSubtract( b, a, a1 );
    620 	VectorNormalize( a1 );
    621 
    622 	VectorSubtract( c, a, b1 );
    623 	VectorNormalize( b1 );
    624 
    625 	CrossProduct( a1, b1, o );
    626 	VectorNormalize( o );
    627 }
    628 
    629 inline void Terrain_CalcVertPos( terrainMesh_t *p, int x, int y, vec3_t vert ) {
    630 	int index;
    631 
    632 	index = x + y * p->width;
    633 	vert[ 0 ] = p->origin[ 0 ] + x * p->scale_x;
    634 	vert[ 1 ] = p->origin[ 1 ] + y * p->scale_y;
    635 	vert[ 2 ] = p->origin[ 2 ] + p->heightmap[ index ].height;
    636 	
    637 	VectorCopy( vert, p->heightmap[ index ].xyz );
    638 }
    639    
    640 void Terrain_CalcNormals( terrainMesh_t *p ) {
    641 	int				x;
    642 	int				y;
    643 	int				width;
    644 	int				num;
    645 	terrainVert_t	*vert;
    646 	vec3_t			norm;
    647 	terravert_t		a0;
    648 	terravert_t		a1;
    649 	terravert_t		a2;
    650 	terravert_t		b0;
    651 	terravert_t		b1;
    652 	terravert_t		b2;
    653 
    654 	p->bDirty = true;
    655 
    656 	num = p->height * p->width;
    657 	vert = p->heightmap;
    658 	//for( x = 0; x < num; x++, vert++ ) {
    659 	for( y = 0; y < p->height - 1; y++ ) {
    660 		for( x = 0; x < p->width - 1; x++, vert++ ) {
    661 			VectorClear( vert->normal );
    662 			Terrain_CalcVertPos( p, x, y, norm );
    663 		}
    664 	}
    665 
    666 	width = p->width;
    667 	vert = p->heightmap;
    668    
    669 	for( y = 0; y < p->height - 1; y++ ) {
    670 		for( x = 0; x < width - 1; x++ ) {
    671 			Terrain_GetTriangles( p, x, y, &a0, &a1, &a2, &b0, &b1, &b2, NULL );
    672 
    673 			CalcTriNormal( a0.xyz, a2.xyz, a1.xyz, norm );
    674 
    675 			VectorAdd( vert[ a0.index ].normal, norm, vert[ a0.index ].normal );
    676 			VectorAdd( vert[ a1.index ].normal, norm, vert[ a1.index ].normal );
    677 			VectorAdd( vert[ a2.index ].normal, norm, vert[ a2.index ].normal );
    678 
    679 			CalcTriNormal( b0.xyz, b2.xyz, b1.xyz, norm );
    680 
    681 			VectorAdd( vert[ b0.index ].normal, norm, vert[ b0.index ].normal );
    682 			VectorAdd( vert[ b1.index ].normal, norm, vert[ b1.index ].normal );
    683 			VectorAdd( vert[ b2.index ].normal, norm, vert[ b2.index ].normal );
    684 		}
    685 	}
    686    
    687 	for( x = 0; x < num; x++, vert++ ) {
    688 		VectorNormalize( vert->normal );
    689 		//FIXME
    690 		vert->normal[ 2 ] += 0.5;
    691 		VectorNormalize( vert->normal );
    692 		assert( vert->normal[ 2 ] > 0 );
    693 		VectorSet( vert->rgba, vert->normal[ 2 ], vert->normal[ 2 ], vert->normal[ 2 ] );
    694 		vert->rgba[ 3 ] = 1.0f;
    695 	}
    696 }
    697 
    698 void Terrain_FindReplaceTexture( terrainMesh_t *p, const char *pFind, const char *pReplace, bool bForce ) {
    699 	int				w;
    700 	int				h;
    701 	terrainVert_t	*vert;
    702 	qtexture_t		*texture;
    703 
    704 	texture = Texture_ForName( pReplace );
    705 
    706 	vert = p->heightmap;
    707 	for( h = 0; h < p->height; h++ ) {
    708 		for( w = 0; w < p->width; w++, vert++ ) {
    709 			if ( bForce || strcmpi( vert->tri.texture->name, pFind ) == 0 ) {
    710 				vert->tri.texture = texture;
    711 				vert->tri.texdef.SetName( texture->name );
    712 			}
    713 		}
    714 	}
    715 
    716 	if ( bForce ) {
    717 		p->numtextures = 0;
    718 		Terrain_AddTexture( p, Texture_ForName( pReplace ) );
    719 	} else {
    720 		Terrain_RemoveTexture( p, Texture_ForName( pFind ) );
    721 		Terrain_AddTexture( p, texture );
    722 	}
    723 }
    724 
    725 bool Terrain_HasTexture( terrainMesh_t *p, const char *name ) {
    726 	int w;
    727 	int h;
    728 	terrainVert_t *vert;
    729 
    730 	vert = p->heightmap;
    731 	for( h = 0; h < p->height; h++ ) {
    732 		for( w = 0; w < p->width; w++, vert++ ) {
    733 			if ( strcmpi( vert->tri.texture->name, name ) == 0 ) {
    734 				return true;
    735 			}
    736 		}
    737 	}
    738 
    739 	return false;
    740 }
    741 
    742 void Terrain_ReplaceQTexture( terrainMesh_t *p, qtexture_t *pOld, qtexture_t *pNew ) {
    743 	int w;
    744 	int h;
    745 	terrainVert_t *vert;
    746 
    747 	vert = p->heightmap;
    748 	for( h = 0; h < p->height; h++ ) {
    749 		for( w = 0; w < p->width; w++, vert++ ) {
    750 			if ( vert->tri.texture == pOld ) {
    751 				vert->tri.texture = pNew;
    752 				vert->tri.texdef.SetName( pNew->name );
    753 			}
    754 		}
    755 	}
    756 
    757 	Terrain_RemoveTexture( p, pOld );
    758 	Terrain_AddTexture( p, pNew );
    759 }
    760 
    761 void Terrain_SetTexture( terrainMesh_t *p, texdef_t *tex_def ) {
    762 	int w;
    763 	int h;
    764 	qtexture_t *newtex;
    765 	terrainVert_t *vert;
    766 
    767 	p->bDirty = 1;
    768   
    769 	newtex = Texture_ForName( tex_def->name );
    770 
    771 	p->numtextures = 0;
    772 	Terrain_AddTexture( p, newtex );
    773 
    774 	vert = p->heightmap;
    775 	for( h = 0; h < p->height; h++ ) {
    776 		for( w = 0; w < p->width; w++, vert++ ) {
    777 			vert->tri.texture = newtex;
    778 			vert->tri.texdef.SetName( newtex->name );
    779 		}
    780 	}
    781    
    782 	UpdateTerrainInspector();
    783 }
    784 
    785 void Terrain_Scale( terrainMesh_t *p, const vec3_t vOrigin, const vec3_t vAmt, bool bRebuild ) {
    786 	int    w;
    787 	int    h;
    788 	vec3_t pos;
    789 	terrainVert_t *vert;
    790 	vec3_t vMin;
    791 	vec3_t vMax;
    792 
    793 	vert = p->heightmap;
    794 	for( h = 0; h < p->height; h++ ) {
    795 		pos[ 1 ] = p->origin[ 1 ] + h * p->scale_y;
    796 		for( w = 0; w < p->width; w++, vert++ ) {
    797 			pos[ 0 ] = p->origin[ 0 ] + w * p->scale_x;
    798 			pos[ 2 ] = vert->height;
    799 
    800 			if ( ( g_qeglobals.d_select_mode == sel_terrainpoint ) && ( Terrain_PointInMoveList( vert ) == -1 ) ) {
    801 				continue;
    802 			}
    803 
    804 			vert->height -= vOrigin[ 2 ] - p->origin[ 2 ];
    805 			vert->height *= vAmt[ 2 ];
    806 			vert->height += vOrigin[ 2 ] - p->origin[ 2 ];
    807 		}
    808 	}
    809 
    810 	if ( g_qeglobals.d_select_mode != sel_terrainpoint ) {
    811 		p->scale_x *= vAmt[ 0 ];
    812 		p->scale_y *= vAmt[ 1 ];
    813       
    814 		p->origin[ 0 ] -= vOrigin[ 0 ];
    815 		p->origin[ 0 ] *= vAmt[ 0 ];
    816 		p->origin[ 0 ] += vOrigin[ 0 ];
    817 
    818 		p->origin[ 1 ] -= vOrigin[ 1 ];
    819 		p->origin[ 1 ] *= vAmt[ 1 ];
    820 		p->origin[ 1 ] += vOrigin[ 1 ];
    821 	}
    822 
    823 	if ( bRebuild ) {
    824 		Terrain_CalcBounds( p, vMin, vMax );
    825 		Terrain_CalcNormals( p );
    826 		Brush_RebuildBrush( p->pSymbiot, vMin, vMax );
    827 	}
    828 
    829 	UpdateTerrainInspector();
    830 }
    831 
    832 bool Terrain_DragScale( terrainMesh_t *p, vec3_t vAmt, vec3_t vMove ) {
    833 	vec3_t	vMin;
    834 	vec3_t	vMax;
    835 	vec3_t	vScale;
    836 	vec3_t	vTemp;
    837 	vec3_t	vMid;
    838 	int		i;
    839 
    840 	Terrain_CalcBounds( p, vMin, vMax );
    841 
    842 	VectorSubtract( vMax, vMin, vTemp );
    843 
    844 	// if we are scaling in the same dimension the terrain has no depth
    845 	for( i = 0; i < 3; i++ ) {
    846 		if ( ( vTemp[ i ] == 0 ) && ( vMove[ i ] != 0 ) ) {
    847 			return false;
    848 		}
    849 	}
    850   
    851 	for( i = 0; i < 3; i++ ) {
    852 		vMid[ i ] = ( vMin[ i ] + vMax[ i ] ) / 2;
    853 	}
    854 
    855 	for( i = 0; i < 3; i++ ) {
    856 		if ( vAmt[ i ] != 0 ) {
    857 			vScale[i] = 1.0 + vAmt[i] / vTemp[i];
    858 		} else {
    859 			vScale[i] = 1.0;
    860 		}
    861 	}
    862 
    863 	Terrain_Scale( p, vMid, vScale, false );
    864 	VectorSubtract( vMax, vMin, vTemp );
    865 	Terrain_CalcBounds( p, vMin, vMax );
    866   	VectorSubtract( vMax, vMin, vMid );
    867 	VectorSubtract( vMid, vTemp, vTemp );
    868 	VectorScale( vTemp, 0.5f, vTemp );
    869 
    870 	// abs of both should always be equal
    871 	if ( !VectorCompare( vMove, vAmt ) ) {
    872 		for( i = 0; i < 3; i++ ) {
    873 			if ( vMove[ i ] != vAmt[ i ] ) {
    874 				vTemp[ i ] = -vTemp[ i ];
    875 			}
    876 		}
    877 	}
    878 
    879 	Terrain_CalcNormals( p );
    880 	Terrain_Move( p, vTemp );
    881 
    882 	return true;
    883 }
    884 
    885 void Terrain_ApplyMatrix( terrainMesh_t *p, const vec3_t vOrigin, const vec3_t vMatrix[ 3 ], bool bSnap ) {
    886 }
    887 
    888 void Terrain_DrawFace( brush_t *brush, terrainFace_t *terraface ) {
    889 	terrainMesh_t	*pm;
    890 	terravert_t		a0;
    891 	terravert_t		a1;
    892 	terravert_t		a2;
    893 
    894 	pm = brush->pTerrain;
    895    
    896 	Terrain_GetTriangle( pm, terraface->index, &a0, &a1, &a2 );
    897 
    898 	qglBindTexture( GL_TEXTURE_2D, terraface->texture->texture_number );
    899 	qglBegin( GL_TRIANGLES );
    900 
    901 	// first tri
    902 	qglColor4fv( a0.rgba );
    903 	qglTexCoord2fv( a0.tc );
    904 	qglVertex3fv( a0.xyz );
    905 
    906 	qglColor4fv( a1.rgba );
    907 	qglTexCoord2fv( a1.tc );
    908 	qglVertex3fv( a1.xyz );
    909 
    910 	qglColor4fv( a2.rgba );
    911 	qglTexCoord2fv( a2.tc );
    912 	qglVertex3fv( a2.xyz );
    913 
    914 	qglEnd ();
    915 }
    916 
    917 void DrawTerrain( terrainMesh_t *pm, bool bPoints, bool bShade ) {
    918 	int				i;
    919 	int				w;
    920 	int				h;
    921 	int				x;
    922 	int				y;
    923 	//int			n;
    924 	//float			x1;
    925 	//float			y1;
    926 	float			scale_x;
    927 	float			scale_y;
    928 	//vec3_t		pSelectedPoints[ MAX_TERRA_POINTS ];
    929 	//int			nIndex;
    930 	terravert_t		a0;
    931 	terravert_t		a1;
    932 	terravert_t		a2;
    933 	terravert_t		b0;
    934 	terravert_t		b1;
    935 	terravert_t		b2;
    936 	terrainVert_t	*vert;
    937 	qtexture_t		*texture;
    938 
    939 	h = pm->height - 1;
    940 	w = pm->width - 1;
    941    
    942 	scale_x = pm->scale_x;
    943 	scale_y = pm->scale_y;
    944 
    945 	qglShadeModel (GL_SMOOTH);
    946 
    947 	if ( bShade ) {
    948 		for( i = 0; i < pm->numtextures; i++ ) {
    949 			texture = pm->textures[ i ];
    950 
    951 			qglBindTexture( GL_TEXTURE_2D, texture->texture_number );
    952 
    953 			vert = pm->heightmap;
    954 			for( y = 0; y < h; y++ ) {
    955 				qglBegin( GL_TRIANGLES );
    956 
    957 				for( x = 0; x < w; x++, vert++ ) {
    958 					Terrain_GetTriangles( pm, x, y, &a0, &a1, &a2, &b0, &b1, &b2, texture );
    959 
    960 					// first tri
    961 					if ( a0.rgba[ 3 ] || a1.rgba[ 3 ] || a2.rgba[ 3 ] ) {
    962 						qglColor4fv( a0.rgba );
    963 						qglTexCoord2fv( a0.tc );
    964 						qglVertex3fv( a0.xyz );
    965 
    966 						qglColor4fv( a1.rgba );
    967 						qglTexCoord2fv( a1.tc );
    968 						qglVertex3fv( a1.xyz );
    969 
    970 						qglColor4fv( a2.rgba );
    971 						qglTexCoord2fv( a2.tc );
    972 						qglVertex3fv( a2.xyz );
    973 					}
    974 
    975 					// second tri
    976 					if ( b0.rgba[ 3 ] || b1.rgba[ 3 ] || b2.rgba[ 3 ] ) {
    977 						qglColor4fv( b0.rgba );
    978 						qglTexCoord2fv( b0.tc );
    979 						qglVertex3fv( b0.xyz );
    980 
    981 						qglColor4fv( b1.rgba );
    982 						qglTexCoord2fv( b1.tc );
    983 						qglVertex3fv( b1.xyz );
    984 
    985 						qglColor4fv( b2.rgba );
    986 						qglTexCoord2fv( b2.tc );
    987 						qglVertex3fv( b2.xyz );
    988 					}
    989 				}
    990 
    991 			qglEnd ();
    992 			}
    993 		}
    994 	} else {
    995 		for( i = 0; i < pm->numtextures; i++ ) {
    996 			texture = pm->textures[ i ];
    997 
    998 			qglBindTexture( GL_TEXTURE_2D, texture->texture_number );
    999 
   1000 			vert = pm->heightmap;
   1001 			for( y = 0; y < h; y++ ) {
   1002 				qglBegin( GL_TRIANGLES );
   1003 
   1004 				for( x = 0; x < w; x++, vert++ ) {
   1005 					Terrain_GetTriangles( pm, x, y, &a0, &a1, &a2, &b0, &b1, &b2, texture );
   1006 
   1007 					// first tri
   1008 					if ( a0.rgba[ 3 ] || a1.rgba[ 3 ] || a2.rgba[ 3 ] ) {
   1009 						qglColor4fv( a0.rgba );
   1010 						qglTexCoord2fv( a0.tc );
   1011 						qglVertex3fv( a0.xyz );
   1012 
   1013 						qglColor4fv( a1.rgba );
   1014 						qglTexCoord2fv( a1.tc );
   1015 						qglVertex3fv( a1.xyz );
   1016 
   1017 						qglColor4fv( a2.rgba );
   1018 						qglTexCoord2fv( a2.tc );
   1019 						qglVertex3fv( a2.xyz );
   1020 					}
   1021 
   1022 					// second tri
   1023 					if ( b0.rgba[ 3 ] || b1.rgba[ 3 ] || b2.rgba[ 3 ] ) {
   1024 						qglColor4fv( b0.rgba );
   1025 						qglTexCoord2fv( b0.tc );
   1026 						qglVertex3fv( b0.xyz );
   1027 
   1028 						qglColor4fv( b1.rgba );
   1029 						qglTexCoord2fv( b1.tc );
   1030 						qglVertex3fv( b1.xyz );
   1031 
   1032 						qglColor4fv( b2.rgba );
   1033 						qglTexCoord2fv( b2.tc );
   1034 						qglVertex3fv( b2.xyz );
   1035 					}
   1036 				}
   1037 				qglEnd ();
   1038 			}
   1039 		}
   1040 	}
   1041 
   1042 	qglPushAttrib( GL_CURRENT_BIT );
   1043 
   1044 	bool bDisabledLighting = qglIsEnabled( GL_LIGHTING );
   1045 	if ( bDisabledLighting ) {
   1046 		qglDisable( GL_LIGHTING );
   1047 	}
   1048 
   1049 #if 0
   1050 	terrainVert_t	*currentrow;
   1051 	terrainVert_t	*nextrow;
   1052 	float			x2;
   1053 	float			y2;
   1054 
   1055 	// Draw normals
   1056 	qglDisable( GL_TEXTURE_2D );
   1057 	qglDisable( GL_BLEND );
   1058 	qglColor3f( 1, 1, 1 );
   1059 	qglBegin( GL_LINES );
   1060 
   1061 	y2 = pm->origin[ 1 ];
   1062 	nextrow = pm->heightmap;
   1063 	for( y = 0; y < h; y++ ) {
   1064 		y1 = y2;
   1065 		y2 += scale_y;
   1066 
   1067 		x2 = pm->origin[ 0 ];
   1068 		currentrow = nextrow;
   1069 		nextrow = currentrow + pm->width;
   1070 		for( x = 0; x < w; x++ ) {
   1071 			x1 = x2;
   1072 			x2 += scale_x;
   1073 
   1074 			// normals
   1075 			qglVertex3f( x1, y1, pm->origin[ 2 ] + currentrow[ x ].height );
   1076 			qglVertex3f( x1 + currentrow[ x ].normal[ 0 ] * 16.0f, y1 + currentrow[ x ].normal[ 1 ] * 16.0f, pm->origin[ 2 ] + currentrow[ x ].height + currentrow[ x ].normal[ 2 ] * 16.0f );
   1077 
   1078 			qglVertex3f( x2, y1, pm->origin[ 2 ] + currentrow[ x + 1 ].height );
   1079 			qglVertex3f( x2 + currentrow[ x + 1 ].normal[ 0 ] * 16.0f, y1 + currentrow[ x + 1 ].normal[ 1 ] * 16.0f, pm->origin[ 2 ] + currentrow[ x + 1 ].height + currentrow[ x + 1 ].normal[ 2 ] * 16.0f );
   1080 
   1081 			qglVertex3f( x1, y2, pm->origin[ 2 ] + nextrow[ x ].height );
   1082 			qglVertex3f( x1 + nextrow[ x ].normal[ 0 ] * 16.0f, y2 + nextrow[ x ].normal[ 1 ] * 16.0f, pm->origin[ 2 ] + nextrow[ x ].height + nextrow[ x ].normal[ 2 ] * 16.0f );
   1083 
   1084 			qglVertex3f( x2, y2, pm->origin[ 2 ] + nextrow[ x + 1 ].height );
   1085 			qglVertex3f( x2 + nextrow[ x + 1 ].normal[ 0 ] * 16.0f, y2 + nextrow[ x + 1 ].normal[ 1 ] * 16.0f, pm->origin[ 2 ] + nextrow[ x + 1 ].height + nextrow[ x + 1 ].normal[ 2 ] * 16.0f );
   1086 		}
   1087 	}
   1088 
   1089 	qglEnd ();
   1090 	qglEnable( GL_TEXTURE_2D );
   1091 #endif
   1092 
   1093 #if 0
   1094 	if ( bPoints && ( g_qeglobals.d_select_mode == sel_terrainpoint || g_qeglobals.d_select_mode == sel_area ) ) {
   1095 		qglPointSize( 6 );
   1096 		qglDisable( GL_TEXTURE_2D );
   1097 		qglDisable( GL_BLEND );
   1098 
   1099 		qglBegin( GL_POINTS );
   1100 
   1101 		nIndex = 0;
   1102 
   1103 		qglColor4f( 1, 0, 1, 1 );
   1104 
   1105 		y1 = pm->origin[ 1 ];
   1106 		for ( y = 0; y < pm->height; y++, y1 += pm->scale_y ) {
   1107 			x1 = pm->origin[ 0 ];
   1108 			for( x = 0; x < pm->width; x++, x1 += pm->scale_x ) {
   1109 				// FIXME: need to not do loop lookups inside here
   1110 				n = Terrain_PointInMoveList( &pm->heightmap[ x + y * pm->width ] );
   1111 				if ( n >= 0 ) {
   1112 					VectorSet( pSelectedPoints[ nIndex ], x1, y1, pm->heightmap[ x + y * pm->width ].height + pm->origin[ 2 ] );
   1113 					nIndex++;
   1114 				} else {
   1115 					qglVertex3f( x1, y1, pm->origin[ 2 ] + pm->heightmap[ x + y * pm->width ].height );
   1116 				}
   1117 			}
   1118 		}
   1119 
   1120 		qglEnd();
   1121 		
   1122 		qglEnable( GL_TEXTURE_2D );
   1123 
   1124 		if ( nIndex > 0 ) {
   1125 			qglBegin( GL_POINTS );
   1126 			qglColor4f( 0, 0, 1, 1 );
   1127 			while( nIndex-- > 0 ) {
   1128 				qglVertex3fv( pSelectedPoints[ nIndex ] );
   1129 			}
   1130 		
   1131 			qglEnd();
   1132 		}
   1133 	}
   1134 #endif
   1135 
   1136 	if ( g_qeglobals.d_numterrapoints && ( ( g_qeglobals.d_select_mode == sel_terrainpoint ) || ( g_qeglobals.d_select_mode == sel_terraintexture ) ) ) {
   1137 #if 0 
   1138 		qglPointSize( 6 );
   1139 		qglDisable( GL_TEXTURE_2D );
   1140 		qglDisable( GL_BLEND );
   1141 
   1142 		qglBegin( GL_POINTS );
   1143 
   1144 		qglColor4f( 1, 0, 1, 1 );
   1145 
   1146 		for( i = 0; i < g_qeglobals.d_numterrapoints; i++ ) {
   1147 			qglVertex3fv( g_qeglobals.d_terrapoints[ i ]->xyz );
   1148 		}
   1149 
   1150 		qglEnd();
   1151 			
   1152 		qglEnable( GL_TEXTURE_2D );
   1153 #endif
   1154 
   1155 		brush_t			*pb;
   1156 		terrainMesh_t	*pm;
   1157 
   1158 		pm = NULL;
   1159 		for( pb = active_brushes .next; pb != &active_brushes; pb = pb->next ) {
   1160 			if ( pb->terrainBrush ) {
   1161 				pm = pb->pTerrain;
   1162 				break;
   1163 			}
   1164 		}
   1165 
   1166 		if ( pm ) {
   1167 			qglDisable( GL_TEXTURE_2D );
   1168 			qglBegin( GL_TRIANGLES );
   1169 			qglEnable( GL_BLEND );
   1170 
   1171 			qglColor4f( 0.25, 0.5, 1, 0.35 );
   1172 
   1173 			for( i = 0; i < g_qeglobals.d_numterrapoints; i++ ) {
   1174 				terravert_t		a0;
   1175 				terravert_t		a1;
   1176 				terravert_t		a2;
   1177 
   1178 				qglColor4f( 0.25, 0.5, 1, g_qeglobals.d_terrapoints[ i ]->scale * 0.75 + 0.25 );
   1179 				Terrain_GetTriangle( pm, g_qeglobals.d_terrapoints[ i ]->tri.index * 2, &a0, &a1, &a2 );
   1180 
   1181 				qglVertex3fv( a0.xyz );
   1182 				qglVertex3fv( a1.xyz );
   1183 				qglVertex3fv( a2.xyz );
   1184 
   1185 				Terrain_GetTriangle( pm, g_qeglobals.d_terrapoints[ i ]->tri.index * 2 + 1, &a0, &a1, &a2 );
   1186 
   1187 				qglVertex3fv( a0.xyz );
   1188 				qglVertex3fv( a1.xyz );
   1189 				qglVertex3fv( a2.xyz );
   1190 			}
   1191 			qglEnd();
   1192 			
   1193 			qglDisable( GL_BLEND );
   1194 			qglEnable( GL_TEXTURE_2D );
   1195 		}
   1196 	}
   1197 }
   1198 
   1199 void Terrain_DrawCam( terrainMesh_t *pm ) {
   1200 	qglColor3f( 1,1,1 );
   1201 	qglPushAttrib( GL_ALL_ATTRIB_BITS );
   1202 
   1203 	if ( g_bPatchWireFrame ) {
   1204 		if( pm->bSelected ) {
   1205 			qglLineWidth( 2 );
   1206 		} else {
   1207 			qglLineWidth( 1 );
   1208 		}
   1209 
   1210 		qglDisable( GL_CULL_FACE );
   1211 		qglPolygonMode( GL_FRONT_AND_BACK, GL_LINE );
   1212 		qglDisable( GL_TEXTURE_2D );
   1213 
   1214 		if ( g_PrefsDlg.m_bGLLighting ) {
   1215 			qglDisable( GL_LIGHTING );
   1216 		}
   1217 
   1218 		DrawTerrain( pm, pm->bSelected, true );
   1219 
   1220 		if ( g_PrefsDlg.m_bGLLighting ) {
   1221 			qglEnable( GL_LIGHTING );
   1222 		}
   1223 
   1224 		qglEnable( GL_CULL_FACE );
   1225 		qglLineWidth( 1 );
   1226 	} else {
   1227 		qglEnable( GL_CULL_FACE );
   1228 		qglCullFace( GL_FRONT );
   1229 
   1230 		// draw the textured polys
   1231 		DrawTerrain( pm, pm->bSelected, true );
   1232 
   1233 		// if selected, draw the red tint on the polys
   1234 		if( pm->bSelected ) { // && ( g_qeglobals.d_savedinfo.include & INCLUDE_CAMERATINT ) ) {
   1235 			qglColor4f( 1.0, 0.0, 0.0, 0.3 );
   1236 			qglEnable( GL_BLEND );
   1237 			qglPolygonMode( GL_FRONT_AND_BACK, GL_FILL );
   1238 			qglBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
   1239 
   1240 			DrawTerrain( pm, pm->bSelected );
   1241 
   1242 			qglColor3f( 1, 1, 1 );
   1243 		}
   1244 
   1245 		// draw the backside poly outlines
   1246 		qglCullFace( GL_BACK );
   1247 		qglPolygonMode( GL_FRONT_AND_BACK, GL_LINE );
   1248 		qglDisable( GL_BLEND );
   1249 		DrawTerrain( pm, pm->bSelected, true );
   1250 	}
   1251 
   1252 	qglPopAttrib();
   1253 }
   1254 
   1255 void Terrain_DrawXY( terrainMesh_t *pm, entity_t *owner ) {
   1256 	qglPolygonMode( GL_FRONT_AND_BACK, GL_LINE );
   1257    
   1258 	if ( pm->bSelected ) {
   1259 		qglColor3fv( g_qeglobals.d_savedinfo.colors[ COLOR_SELBRUSHES ] );
   1260 	} else if ( owner != world_entity && _stricmp( owner->eclass->name, "func_group" ) ) {
   1261 		qglColor3fv( owner->eclass->color );
   1262 	} else {
   1263 		//FIXME
   1264 		qglColor3fv( g_qeglobals.d_savedinfo.colors[ COLOR_BRUSHES ] );
   1265 	}
   1266 	
   1267 	qglLineWidth( 1 );
   1268 
   1269 	DrawTerrain( pm, pm->bSelected );
   1270 
   1271 	qglPolygonMode( GL_FRONT_AND_BACK, GL_FILL );
   1272 }
   1273 
   1274 bool OnlyTerrainSelected( void ) {
   1275 	brush_t *pb;
   1276 
   1277 	//if ( numselfaces || selected_brushes.next == &selected_brushes )
   1278 	if ( selected_brushes.next == &selected_brushes ) {
   1279 		return false;
   1280 	}
   1281 
   1282 	for( pb = selected_brushes.next; pb != &selected_brushes; pb = pb->next ) {
   1283 		if ( !pb->terrainBrush ) {
   1284 			return false;
   1285 		}
   1286 	}
   1287    
   1288 	return true;
   1289 }
   1290 
   1291 bool AnyTerrainSelected( void ) {
   1292 	brush_t *pb;
   1293 
   1294 	//if ( numselfaces || selected_brushes.next == &selected_brushes )
   1295 	if ( selected_brushes.next == &selected_brushes ) {
   1296 		return false;
   1297 	}
   1298 
   1299 	for( pb = selected_brushes.next; pb != &selected_brushes; pb = pb->next ) {
   1300 		if ( pb->terrainBrush ) {
   1301 			return true;
   1302 		}
   1303 	}
   1304   
   1305 	return false;
   1306 }
   1307 
   1308 terrainMesh_t *SingleTerrainSelected( void ) {
   1309 	if ( selected_brushes.next->terrainBrush ) {
   1310 		return selected_brushes.next->pTerrain;
   1311 	}
   1312 
   1313 	return NULL;
   1314 }
   1315 
   1316 void Terrain_Edit( void ) {
   1317 	//brush_t *pb;
   1318 	//terrainMesh_t *p;
   1319 	//int i;
   1320 	//int j;
   1321 
   1322 //	g_qeglobals.d_numpoints = 0;
   1323 	g_qeglobals.d_numterrapoints = 0;
   1324 #if 0	
   1325 	for( pb = selected_brushes.next; pb != &selected_brushes ; pb = pb->next ) {
   1326 		if ( pb->terrainBrush ) {
   1327 			p = pb->pTerrain;
   1328 
   1329 			if ( ( g_qeglobals.d_numpoints + p->width * p->height ) > MAX_POINTS ) {
   1330 				Warning( "Too many points on terrain\n" );
   1331 				continue;
   1332 			}
   1333 			for( i = 0; i < p->width; i++ ) {
   1334 				for( j = 0; j < p->height; j++ ) {
   1335 					Terrain_CalcVertPos( p, i, j, g_qeglobals.d_points[ g_qeglobals.d_numpoints ] );
   1336 					g_qeglobals.d_numpoints++;
   1337 				}
   1338 			}
   1339 		}
   1340 	}
   1341 #endif
   1342    
   1343 	g_qeglobals.d_select_mode = sel_terrainpoint;
   1344 }
   1345 
   1346 void Terrain_SelectPointByRay( vec3_t org, vec3_t dir, int buttons ) {
   1347 	float			bestd;
   1348 	terrainFace_t	*face;
   1349 	terrainFace_t	*bestface;
   1350 	brush_t			*pb;
   1351 	float			dist;
   1352 	vec3_t			vec;
   1353 
   1354 	// find the point closest to the ray
   1355 	bestface = NULL;
   1356 	bestd = WORLD_SIZE * 4;
   1357 	for( pb = selected_brushes.next; pb != &selected_brushes; pb = pb->next ) {
   1358 		if ( pb->terrainBrush ) {
   1359 			face = Terrain_Ray( org, dir, pb, &dist );
   1360 			if ( face && ( dist < bestd ) ) {
   1361 				bestface = face;
   1362 				bestd = dist;
   1363 			}
   1364 		}
   1365 	}
   1366 
   1367 	for( pb = active_brushes .next; pb != &active_brushes; pb = pb->next ) {
   1368 		if ( pb->terrainBrush ) {
   1369 			face = Terrain_Ray( org, dir, pb, &dist );
   1370 			if ( face && ( dist < bestd ) ) {
   1371 				bestface = face;
   1372 				bestd = dist;
   1373 			}
   1374 		}
   1375 	}
   1376 
   1377 	if ( !bestface ) {
   1378 		return;
   1379 	}
   1380 	
   1381 	VectorMA( org, bestd, dir, vec );
   1382 	Terrain_AddMovePoint( vec, buttons & MK_CONTROL, buttons & MK_SHIFT, buttons );
   1383 }
   1384 
   1385 void Terrain_AddMovePoint( vec3_t v, bool bMulti, bool bFull, int buttons  ) {
   1386 	brush_t			*pb;
   1387 	terrainMesh_t	*p;
   1388 	terrainVert_t	*vert;
   1389 	int				x;
   1390 	int				y;
   1391 	int				x1, y1;
   1392 	float			dx, dy;
   1393 	float			dist;
   1394 	float			pd;
   1395 
   1396 	if ( !g_bSameView && !bMulti && !bFull ) {
   1397 		g_bSameView = true;
   1398 		return;
   1399 	}
   1400 	
   1401 	g_qeglobals.d_numterrapoints = 0;
   1402 	for( pb = active_brushes .next; pb != &active_brushes; pb = pb->next ) {
   1403 	//for( pb = selected_brushes.next; pb != &selected_brushes; pb = pb->next ) {
   1404 		if ( pb->terrainBrush ) {
   1405 			p = pb->pTerrain;
   1406 	    
   1407 			x = ( v[ 0 ] - p->origin[ 0 ] ) / p->scale_x;
   1408 			y = ( v[ 1 ] - p->origin[ 1 ] ) / p->scale_x;
   1409 			if ( ( x < 0 ) || ( x >= p->width ) || ( y < 0 ) || ( y >= p->height ) ) {
   1410 				continue;
   1411 			}
   1412 
   1413 			vert = p->heightmap;
   1414 			for( y1 = 0; y1 < p->height; y1++ ) {
   1415 				for( x1 = 0; x1 < p->width; x1++, vert++ ) {
   1416 
   1417 					if ( g_qeglobals.d_terrainBrush == TERRAIN_BRUSH_CIRCLE ) {
   1418 						dx = x1 - x;
   1419 						dy = y1 - y;
   1420 						dist = sqrt( dx * dx + dy * dy );
   1421 					} else {
   1422 						dx = abs( x1 - x );
   1423 						dy = abs( y1 - y );
   1424 						if ( dx > dy ) {
   1425 							dist = dx;
   1426 						} else {
   1427 							dist = dy;
   1428 						}
   1429 					}
   1430 
   1431 					pd = dist * 2.0f / g_qeglobals.d_terrainBrushSize;
   1432 					if ( fabs( pd ) <= 1.0f ) {
   1433 						Terrain_AddPoint( p, vert );
   1434 
   1435 						if ( ( buttons & MK_LBUTTON ) && ( g_qeglobals.d_select_mode == sel_terraintexture ) ) {
   1436 							vert->tri.texture = Texture_ForName( g_qeglobals.d_texturewin.texdef.name );
   1437 							vert->tri.texdef.SetName( vert->tri.texture->name );
   1438 							Terrain_AddTexture( p, vert->tri.texture );
   1439 							continue;
   1440 						}
   1441 
   1442 						if ( g_qeglobals.d_terrainFalloff == TERRAIN_FALLOFF_CURVED ) {
   1443 							if ( g_qeglobals.d_terrainBrush == TERRAIN_BRUSH_CIRCLE ) {
   1444 								vert->scale = ( 0.5f + cos( pd * M_PI ) * 0.5f );
   1445 							} else {
   1446 								vert->scale = ( 0.5f + cos( dx/ g_qeglobals.d_terrainBrushSize * M_PI ) * 0.5f ) * ( 0.5f + cos( dy/ g_qeglobals.d_terrainBrushSize * M_PI ) * 0.5f ) - 0.25;
   1447 							}
   1448 						} else {
   1449 							vert->scale = 1.0f - pd;
   1450 						}
   1451 
   1452 						switch( g_qeglobals.d_terrainNoiseType ) {
   1453 						case NOISE_PLUS :
   1454 							vert->scale *= crandom();
   1455 							break;
   1456 
   1457 						case NOISE_PLUSMINUS :
   1458 							vert->scale *= random();
   1459 							break;
   1460 						}
   1461 					}
   1462 				}
   1463 			}
   1464 		}
   1465 	}
   1466 }
   1467 
   1468 void Terrain_UpdateSelected( vec3_t vMove ) {
   1469 	int				i;
   1470 	brush_t			*pb;
   1471 	terrainMesh_t	*p;
   1472 	vec3_t			vMin;
   1473 	vec3_t			vMax;
   1474 
   1475 	if ( g_qeglobals.d_select_mode == sel_terrainpoint ) {
   1476 		for( i = 0; i < g_qeglobals.d_numterrapoints; i++ ) {
   1477 			g_qeglobals.d_terrapoints[ i ]->height += vMove[ 2 ] * g_qeglobals.d_terrapoints[ i ]->scale;
   1478 		}
   1479 	}
   1480 
   1481 	for( pb = active_brushes .next; pb != &active_brushes; pb = pb->next ) {
   1482 //	for( pb = selected_brushes.next; pb != &selected_brushes; pb = pb->next ) {
   1483 		if ( pb->terrainBrush ) {
   1484 			p = pb->pTerrain;
   1485 
   1486 			Terrain_CalcBounds( p, vMin, vMax );
   1487 			Terrain_CalcNormals( p );
   1488 			Brush_RebuildBrush( p->pSymbiot, vMin, vMax );
   1489 		}
   1490 	}
   1491 }
   1492 
   1493 int Terrain_PointInMoveList( terrainVert_t *pf ) {
   1494 	int i;
   1495 
   1496 	for( i = 0; i < g_qeglobals.d_numterrapoints; i++ ) {
   1497 		if ( pf == g_qeglobals.d_terrapoints[ i ] ) {
   1498 			return i;
   1499 		}
   1500 	}
   1501 
   1502 	return -1;
   1503 }
   1504 
   1505 void Terrain_RemovePointFromMoveList( terrainVert_t *v ) {
   1506 	int n;
   1507 	int i;
   1508    
   1509 	while( ( n = Terrain_PointInMoveList( v ) ) >= 0 ) {
   1510 		for( i = n; i < g_qeglobals.d_numterrapoints - 1; i++ ) {
   1511 			g_qeglobals.d_terrapoints[ i ] = g_qeglobals.d_terrapoints[ i + 1 ];
   1512 		}
   1513     
   1514 		g_qeglobals.d_numterrapoints--;
   1515 	}
   1516 }
   1517 
   1518 void Terrain_AddPoint( terrainMesh_t *p, terrainVert_t *v ) {
   1519 	if ( g_qeglobals.d_numterrapoints < MAX_TERRA_POINTS ) {
   1520 		g_qeglobals.d_terrapoints[ g_qeglobals.d_numterrapoints++ ] = v;
   1521 	}
   1522 }
   1523 
   1524 void Terrain_SelectAreaPoints( void ) {
   1525 	brush_t			*pb;
   1526 	terrainMesh_t	*p;
   1527 	int				x;
   1528 	int				y;
   1529 	vec3_t			vec;
   1530 
   1531 	g_qeglobals.d_numterrapoints = 0;
   1532 	g_nPatchClickedView = -1;
   1533 
   1534 	for( pb = selected_brushes.next; pb != &selected_brushes; pb = pb->next ) {
   1535 		if ( pb->terrainBrush ) {
   1536 			p = pb->pTerrain;
   1537 			for( x = 0; x < p->width; x++ ) {
   1538 				for( y = 0; y < p->height; y++ ) {
   1539 					Terrain_CalcVertPos( p, x, y, vec );
   1540 					if ( within( vec, g_qeglobals.d_vAreaTL, g_qeglobals.d_vAreaBR ) ) {
   1541 						if ( g_qeglobals.d_numterrapoints < MAX_TERRA_POINTS ) {
   1542 							g_qeglobals.d_terrapoints[ g_qeglobals.d_numterrapoints++ ] = &p->heightmap[ x + y * p->width ];
   1543 						}
   1544 					}
   1545 				}
   1546 			}
   1547 		}
   1548 	}
   1549 }
   1550 
   1551 #define EPSILON 0.0001
   1552 
   1553 bool RayTriangleIntersect( vec3_t orig, vec3_t dir, vec3_t vert1, vec3_t vert2, vec3_t vert3, float *t ) {
   1554 	float   u;
   1555 	float   v;
   1556 	vec3_t  edge1;
   1557 	vec3_t  edge2;
   1558 	vec3_t  tvec;
   1559 	vec3_t	pvec;
   1560 	vec3_t	qvec;
   1561 	float	det;
   1562 
   1563 	VectorSubtract( vert2, vert1, edge1 );
   1564 	VectorSubtract( vert3, vert1, edge2 );
   1565 
   1566 	// begin calculating determinant - also used to calculate U parameter
   1567 	CrossProduct( dir, edge2, pvec );
   1568 
   1569 	// if determinant is near zero, ray lies in plane of triangle
   1570 	det = DotProduct( edge1, pvec );
   1571 	if ( det < EPSILON ) {
   1572 		return false;
   1573 	}
   1574 
   1575 	// calculate distance from vert1 to ray origin
   1576 	VectorSubtract( orig, vert1, tvec );
   1577 
   1578 	// calculate U parameter and test bounds
   1579 	u = DotProduct( tvec, pvec );
   1580 	if ( ( u < 0.0f ) || ( u > det ) ) {
   1581 		return false;
   1582 	}
   1583 
   1584 	// prepare to test V parameter
   1585 	CrossProduct( tvec, edge1, qvec );
   1586 
   1587 	// calculate V parameter and test bounds
   1588 	v = DotProduct( dir, qvec );
   1589 	if ( ( v < 0.0f ) || ( u + v > det ) ) {
   1590 		return false;
   1591 	}
   1592 
   1593 	// calculate t, scale parameters, ray intersects triangle
   1594 	*t = DotProduct( edge2, qvec ) / det;
   1595 
   1596 	return true;
   1597 }
   1598 
   1599 /*
   1600 ==============
   1601 Terrain_Ray
   1602 
   1603 Itersects a ray with a terrain
   1604 Returns the face hit and the distance along the ray the intersection occured at
   1605 Returns NULL and 0 if not hit at all
   1606 ==============
   1607 */
   1608 terrainFace_t *Terrain_Ray( vec3_t origin, vec3_t dir, brush_t *b, float *dist ) {
   1609 	terrainMesh_t	*pm;
   1610 	int				h;
   1611 	int				w;
   1612 	int				x;
   1613 	int				y;
   1614 	float			best_t;
   1615 	float			t;
   1616 	terravert_t		a0;
   1617 	terravert_t		a1;
   1618 	terravert_t		a2;
   1619 	terravert_t		b0;
   1620 	terravert_t		b1;
   1621 	terravert_t		b2;
   1622 	terrainVert_t	*vert;
   1623 	terrainFace_t	*best;
   1624 
   1625 	best = NULL;
   1626 	best_t = WORLD_SIZE * 2;
   1627 
   1628 	pm = b->pTerrain;
   1629 	h = pm->height - 1;
   1630 	w = pm->width - 1;
   1631    
   1632 	vert = pm->heightmap;
   1633 	for( y = 0; y < h; y++, vert++ ) {
   1634 		for( x = 0; x < w; x++, vert++ ) {
   1635 			Terrain_GetTriangles( pm, x, y, &a0, &a1, &a2, &b0, &b1, &b2, NULL );
   1636 
   1637 			t = WORLD_SIZE * 2;
   1638 			if ( RayTriangleIntersect( origin, dir, a2.xyz, a1.xyz, a0.xyz, &t ) ) {
   1639 				if ( ( t >= 0 ) && ( t < best_t ) ) {
   1640 					best = &vert->tri;
   1641 					best_t = t;
   1642 				}
   1643 			}
   1644 
   1645 			t = WORLD_SIZE * 2;
   1646 			if ( RayTriangleIntersect( origin, dir, b2.xyz, b1.xyz, b0.xyz, &t ) ) {
   1647 				if ( ( t >= 0 ) && ( t < best_t ) ) {
   1648 					best = &vert->tri;
   1649 					best_t = t;
   1650 				}
   1651 			}
   1652 		}
   1653 	}
   1654 
   1655 	if ( !best ) {
   1656 		*dist = 0;
   1657 		return NULL;
   1658 	}
   1659 
   1660 	*dist = best_t;
   1661 
   1662 	return best;
   1663 }
   1664 
   1665 /*
   1666 ============
   1667 Select_TerrainFace
   1668 
   1669 Select the face
   1670 ============
   1671 */
   1672 void Select_TerrainFace ( brush_t * brush, terrainFace_t *terraface ) {
   1673 #if 0
   1674 	UnSelect_Brush( brush );
   1675 
   1676 	if( numselfaces < MAX_SEL_FACES ) {
   1677 		selfaces[numselfaces].face = NULL;
   1678 		selfaces[numselfaces].brush = brush;
   1679 		selfaces[numselfaces].terraface = terraface;
   1680 		numselfaces++;
   1681 	}
   1682 #endif
   1683 }
   1684 
   1685 void Select_TerrainFacesFromBrush( brush_t *brush ) {
   1686 	terrainMesh_t	*pm;
   1687 	int				h;
   1688 	int				w;
   1689 	int				x;
   1690 	int				y;
   1691 
   1692 	pm = brush->pTerrain;
   1693 	h = pm->height - 1;
   1694 	w = pm->width - 1;
   1695    
   1696 	for( y = 0; y < h; y++ ) {
   1697 		for( x = 0; x < w; x++ ) {
   1698 			Select_TerrainFace( brush, &brush->pTerrain->heightmap[ x + y * pm->width ].tri );
   1699 		}
   1700 	}
   1701 }
   1702 
   1703 void SetTerrainTexdef( brush_t *brush, terrainFace_t *face, texdef_t *texdef ) {
   1704 	int	oldFlags;
   1705 	int	oldContents;
   1706 
   1707 	oldFlags = face->texdef.flags;
   1708 	oldContents = face->texdef.contents;
   1709 
   1710 	face->texdef = *texdef;
   1711 
   1712 	face->texdef.flags = ( face->texdef.flags & ~SURF_KEEP ) | ( oldFlags & SURF_KEEP );
   1713 	face->texdef.contents = ( face->texdef.contents & ~CONTENTS_KEEP ) | ( oldContents & CONTENTS_KEEP );
   1714    
   1715 	face->texture = Texture_ForName( texdef->name );
   1716 
   1717 	//Terrain_AddTexture( face->texture );
   1718 }
   1719 
   1720 void RotateTerrainFaceTexture( terrainFace_t *vert, int nAxis, float fDeg ) {
   1721 }
   1722 
   1723 void TerrainFace_FitTexture( terrainFace_t *vert ) {
   1724 }
   1725 
   1726 void Terrain_Init( void ) {
   1727 	g_qeglobals.d_terrainWidth		= 64;
   1728 	g_qeglobals.d_terrainHeight		= 64;
   1729 	g_qeglobals.d_terrainBrushSize	= 12;
   1730 	g_qeglobals.d_terrainNoiseType	= NOISE_NONE;
   1731 	//g_qeglobals.d_terrainFalloff	= TERRAIN_FALLOFF_LINEAR;
   1732 	g_qeglobals.d_terrainFalloff	= TERRAIN_FALLOFF_CURVED;
   1733 	g_qeglobals.d_terrainBrush		= TERRAIN_BRUSH_CIRCLE;
   1734 	//g_qeglobals.d_terrainBrush		= TERRAIN_BRUSH_SQUARE;
   1735 }