aboutsummaryrefslogtreecommitdiffstats
path: root/q3radiant/terrain.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'q3radiant/terrain.cpp')
-rwxr-xr-xq3radiant/terrain.cpp1735
1 files changed, 1735 insertions, 0 deletions
diff --git a/q3radiant/terrain.cpp b/q3radiant/terrain.cpp
new file mode 100755
index 0000000..284d30b
--- /dev/null
+++ b/q3radiant/terrain.cpp
@@ -0,0 +1,1735 @@
+/*
+===========================================================================
+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
+===========================================================================
+*/
+/*
+
+Todo:
+
+immediate:
+Texture placement
+New map format
+q3map
+
+
+later:
+Smoothing brush
+Stitching terrains together
+Cross terrain selection
+
+
+
+
+
+Terrain_ApplyMatrix
+UpdateTerrainInspector
+
+*/
+
+#include "stdafx.h"
+#include "qe3.h"
+#include "DialogInfo.h"
+#include "assert.h"
+
+//random in the range [0, 1]
+#define random() ((rand () & 0x7fff) / ((float)0x7fff))
+
+//random in the range [-1, 1]
+#define crandom() (2.0 * (random() - 0.5))
+
+typedef struct {
+ int index;
+ vec3_t xyz;
+ vec4_t rgba;
+ vec2_t tc;
+} terravert_t;
+
+/*
+==============
+Terrain_SetEpair
+sets an epair for the given patch
+==============
+*/
+void Terrain_SetEpair( terrainMesh_t *p, const char *pKey, const char *pValue ) {
+ if ( g_qeglobals.m_bBrushPrimitMode ) {
+ SetKeyValue( p->epairs, pKey, pValue );
+ }
+}
+
+/*
+=================
+Terrain_GetKeyValue
+=================
+*/
+const char *Terrain_GetKeyValue( terrainMesh_t *p, const char *pKey ) {
+ if ( g_qeglobals.m_bBrushPrimitMode ) {
+ return ValueForKey( p->epairs, pKey );
+ }
+ return "";
+}
+
+/*
+==================
+Terrain_MemorySize
+==================
+*/
+int Terrain_MemorySize( terrainMesh_t *p ) {
+ return _msize( p );
+}
+
+void Terrain_GetVert( terrainMesh_t *pm, int x, int y, float s, float t, terravert_t *v, qtexture_t *texture = NULL ) {
+ terrainVert_t *cell;
+
+ v->index = x + y * pm->width;
+
+ cell = &pm->heightmap[ v->index ];
+
+ v->xyz[ 0 ] = pm->origin[ 0 ] + x * pm->scale_x;
+ v->xyz[ 1 ] = pm->origin[ 1 ] + y * pm->scale_y;
+ v->xyz[ 2 ] = pm->origin[ 2 ] + cell->height;
+
+ VectorCopy( cell->rgba, v->rgba );
+
+ if ( !texture || ( texture == cell->tri.texture ) ) {
+ v->rgba[ 3 ] = 1.0f;
+ } else {
+ v->rgba[ 3 ] = 0.0f;
+ }
+
+ v->tc[ 0 ] = s;
+ v->tc[ 1 ] = t;
+}
+
+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 ) {
+ if ( ( x + y ) & 1 ) {
+ // first tri
+ Terrain_GetVert( pm, x, y, 1.0f, 1.0f, a0, texture );
+ Terrain_GetVert( pm, x, y + 1, 1.0f, 0.0f, a1, texture );
+ Terrain_GetVert( pm, x + 1, y + 1, 0.0f, 0.0f, a2, texture );
+
+ // second tri
+ *b0 = *a2;
+ Terrain_GetVert( pm, x + 1, y, 0.0f, 1.0f, b1, texture );
+ *b2 = *a0;
+ } else {
+ // first tri
+ Terrain_GetVert( pm, x, y, 1.0f, 1.0f, a0, texture );
+ Terrain_GetVert( pm, x, y + 1, 1.0f, 0.0f, a1, texture );
+ Terrain_GetVert( pm, x + 1, y, 0.0f, 1.0f, a2, texture );
+
+ // second tri
+ *b0 = *a2;
+ *b1 = *a1;
+ Terrain_GetVert( pm, x + 1, y + 1, 0.0f, 0.0f, b2, texture );
+ }
+}
+
+void Terrain_GetTriangle( terrainMesh_t *pm, int index, terravert_t *a0, terravert_t *a1, terravert_t *a2 ) {
+ int x;
+ int y;
+ int which;
+
+ which = index & 1;
+ index >>= 1;
+ y = index / pm->width;
+ x = index % pm->width;
+
+ if ( ( x + y ) & 1 ) {
+ if ( !which ) {
+ // first tri
+ Terrain_GetVert( pm, x, y, 1.0f, 1.0f, a0 );
+ Terrain_GetVert( pm, x, y + 1, 1.0f, 0.0f, a1 );
+ Terrain_GetVert( pm, x + 1, y + 1, 0.0f, 0.0f, a2 );
+ } else {
+ Terrain_GetVert( pm, x + 1, y + 1, 0.0f, 0.0f, a0 );
+ Terrain_GetVert( pm, x + 1, y, 0.0f, 1.0f, a1 );
+ Terrain_GetVert( pm, x, y, 1.0f, 1.0f, a2 );
+ }
+ } else {
+ if ( !which ) {
+ // first tri
+ Terrain_GetVert( pm, x, y, 1.0f, 1.0f, a0 );
+ Terrain_GetVert( pm, x, y + 1, 1.0f, 0.0f, a1 );
+ Terrain_GetVert( pm, x + 1, y, 0.0f, 1.0f, a2 );
+ } else {
+ Terrain_GetVert( pm, x + 1, y, 0.0f, 1.0f, a0 );
+ Terrain_GetVert( pm, x, y + 1, 1.0f, 0.0f, a1 );
+ Terrain_GetVert( pm, x + 1, y + 1, 0.0f, 0.0f, a2 );
+ }
+ }
+}
+
+void Terrain_Delete( terrainMesh_t *p ) {
+ if ( p->pSymbiot ) {
+ p->pSymbiot->pTerrain = NULL;
+ p->pSymbiot->terrainBrush = false;
+ }
+
+ free( p );
+
+ p = NULL;
+
+ UpdateTerrainInspector();
+}
+
+void Terrain_AddTexture( terrainMesh_t *pm, qtexture_t *texture ) {
+ int i;
+
+ if ( !texture ) {
+ return;
+ }
+
+ for( i = 0; i < pm->numtextures; i++ ) {
+ if ( pm->textures[ i ] == texture ) {
+ return;
+ }
+ }
+
+ if ( pm->numtextures >= MAX_TERRAIN_TEXTURES ) {
+ Warning( "Too many textures on terrain" );
+ return;
+ }
+
+ pm->textures[ pm->numtextures++ ] = texture;
+}
+
+void Terrain_RemoveTexture( terrainMesh_t *p, qtexture_t *texture ) {
+ int i;
+
+ for( i = 0; i < p->numtextures; i++ ) {
+ if ( p->textures[ i ] == texture ) {
+ break;
+ }
+ }
+
+ if ( i < p->numtextures ) {
+ // shift all textures down to remove the texture from the list
+ p->numtextures--;
+ for( ; i < p->numtextures; i++ ) {
+ p->textures[ i ] = p->textures[ i + 1 ];
+ }
+ }
+}
+
+terrainMesh_t *MakeNewTerrain( int width, int height, qtexture_t *texture ) {
+ int h;
+ int w;
+ terrainMesh_t *pm;
+ size_t size;
+ size_t heightmapsize;
+ terrainVert_t *vert;
+ int index;
+
+ heightmapsize = sizeof( terrainVert_t ) * width * height;
+ size = sizeof( terrainMesh_t ) + heightmapsize;
+
+ pm = reinterpret_cast< terrainMesh_t * >( qmalloc( size ) );
+
+ memset( pm, 0x00, size );
+
+ pm->numtextures = 0;
+ pm->width = width;
+ pm->height = height;
+ pm->heightmap = reinterpret_cast< terrainVert_t * >( pm + 1 );
+
+ if ( texture ) {
+ Terrain_AddTexture( pm, texture );
+ }
+
+ index = 0;
+ vert = pm->heightmap;
+ for( h = 0; h < pm->height; h++ ) {
+ for( w = 0; w < pm->width; w++, vert++ ) {
+ vert->tri.index = index++;
+ vert->tri.texture = texture;
+ if ( texture ) {
+ vert->tri.texdef.SetName( texture->name );
+ }
+
+ vert->height = 0;
+
+ VectorClear( vert->normal );
+ VectorSet( vert->rgba, 1.0f, 1.0f, 1.0f );
+ vert->rgba[ 3 ] = 1.0f;
+ }
+ }
+
+ return pm;
+}
+
+brush_t *AddBrushForTerrain( terrainMesh_t *pm, bool bLinkToWorld ) {
+ int j;
+ vec3_t vMin;
+ vec3_t vMax;
+ brush_t *b;
+ face_t *f;
+
+ // calculate the face normals
+ Terrain_CalcNormals( pm );
+
+ // find the farthest points in x,y,z
+ Terrain_CalcBounds( pm, vMin, vMax );
+
+ for( j = 0; j < 3; j++ ) {
+ if ( vMin[ j ] == vMax[ j ] ) {
+ vMin[ j ] -= 4;
+ vMax[ j ] += 4;
+ }
+ }
+
+ b = Brush_Create( vMin, vMax, &pm->heightmap->tri.texdef );
+
+ for( f = b->brush_faces; f != NULL; f = f->next ) {
+ // copy the texdef to the brush faces texdef
+ f->texdef = pm->heightmap->tri.texdef;
+ }
+
+ // FIXME: this entire type of linkage needs to be fixed
+ b->pTerrain = pm;
+ b->terrainBrush = true;
+ pm->pSymbiot = b;
+ pm->bSelected = false;
+ pm->bDirty = true;
+ pm->nListID = -1;
+
+ if ( bLinkToWorld ) {
+ Brush_AddToList( b, &active_brushes );
+ Entity_LinkBrush( world_entity, b );
+ Brush_Build( b, true );
+ }
+
+ return b;
+}
+
+terrainMesh_t *Terrain_Duplicate( terrainMesh_t *pFrom ) {
+ terrainMesh_t *p;
+ int w;
+ int h;
+ int index;
+
+ p = MakeNewTerrain( pFrom->width, pFrom->height );
+
+ VectorCopy( pFrom->origin, p->origin );
+ VectorCopy( pFrom->mins, p->mins );
+ VectorCopy( pFrom->maxs, p->maxs );
+
+ p->scale_x = pFrom->scale_x;
+ p->scale_y = pFrom->scale_y;
+ p->pSymbiot = pFrom->pSymbiot;
+
+ for( index = 0; index < pFrom->numtextures; index++ ) {
+ Terrain_AddTexture( p, pFrom->textures[ index ] );
+ }
+
+ index = 0;
+ for( h = 0; h < p->height; h++ ) {
+ for( w = 0; w < p->width; w++, index++ ) {
+ p->heightmap[ index ] = pFrom->heightmap[ index ];
+ }
+ }
+
+ p->bSelected = false;
+ p->bDirty = true;
+ p->nListID = -1;
+
+ AddBrushForTerrain( p );
+
+ return p;
+}
+
+void Terrain_BrushToMesh( void ) {
+ brush_t *b;
+ terrainMesh_t *p;
+
+ if ( !QE_SingleBrush() ) {
+ return;
+ }
+
+ b = selected_brushes.next;
+
+ if ( g_qeglobals.d_terrainWidth < 1 ) {
+ g_qeglobals.d_terrainWidth = 1;
+ }
+
+ if ( g_qeglobals.d_terrainHeight < 1 ) {
+ g_qeglobals.d_terrainHeight = 1;
+ }
+
+ p = MakeNewTerrain( g_qeglobals.d_terrainWidth + 1, g_qeglobals.d_terrainHeight + 1, b->brush_faces->d_texture );
+ p->scale_x = ( b->maxs[ 0 ] - b->mins[ 0 ] ) / float( p->width - 1 );
+ p->scale_y = ( b->maxs[ 1 ] - b->mins[ 1 ] ) / float( p->height - 1 );
+
+ VectorCopy( b->mins, p->origin );
+
+ b = AddBrushForTerrain( p );
+ Select_Delete();
+ Select_Brush( b );
+}
+
+terrainFace_t *Terrain_ParseFace( terrainFace_t *f ) {
+ // read the texturename
+ GetToken( false );
+ f->texdef.SetName( token );
+
+ // Load the texture, and set the face to that texture's defaults
+ f->texture = Texture_ForName( f->texdef.Name() );
+
+ // read the texturedef
+ GetToken( false );
+ f->texdef.shift[ 0 ] = ( float )atoi( token );
+ GetToken( false );
+ f->texdef.shift[ 1 ] = ( float )atoi( token );
+ GetToken( false );
+ f->texdef.rotate = atof( token );
+ GetToken( false );
+ f->texdef.scale[ 0 ] = atof( token );
+ GetToken( false );
+ f->texdef.scale[ 1 ] = atof( token );
+
+ // the flags and value field aren't necessarily present
+ //f->texture = Texture_ForName( f->texdef.Name() );
+ f->texdef.flags = f->texture->flags;
+ f->texdef.value = f->texture->value;
+ f->texdef.contents = f->texture->contents;
+
+ if ( TokenAvailable () ) {
+ GetToken (false);
+ f->texdef.contents = atoi(token);
+ GetToken (false);
+ f->texdef.flags = atoi(token);
+ GetToken (false);
+ f->texdef.value = atoi(token);
+ }
+
+ return f;
+}
+
+brush_t *Terrain_Parse( void ) {
+ terrainMesh_t *pm;
+ terrainVert_t *vert;
+ int w;
+ int h;
+
+ GetToken( true );
+ if ( strcmp( token, "{" ) ) {
+ return NULL;
+ }
+
+ // get width
+ GetToken( false );
+ w = atoi( token );
+
+ // get height
+ GetToken( false );
+ h = atoi( token );
+
+ pm = MakeNewTerrain( w, h );
+
+ // get scale_x
+ GetToken( false );
+ pm->scale_x = atoi( token );
+
+ // get scale_y
+ GetToken( false );
+ pm->scale_y = atoi( token );
+
+ // get origin
+ GetToken( true );
+ pm->origin[ 0 ] = atoi( token );
+ GetToken( false );
+ pm->origin[ 1 ] = atoi( token );
+ GetToken( false );
+ pm->origin[ 2 ] = atoi( token );
+
+ // get the height map
+ vert = pm->heightmap;
+ for( h = 0; h < pm->height; h++ ) {
+ for( w = 0; w < pm->width; w++, vert++ ) {
+ GetToken( true );
+ vert->height = atoi( token );
+
+ if ( !Terrain_ParseFace( &vert->tri ) ) {
+ Terrain_Delete( pm );
+ return NULL;
+ }
+
+ Terrain_AddTexture( pm, vert->tri.texture );
+ }
+ }
+
+ GetToken( true );
+ if ( strcmp( token, "}" ) ) {
+ Terrain_Delete( pm );
+ return NULL;
+ }
+
+ return AddBrushForTerrain( pm, false );
+}
+
+CString Terrain_SurfaceString( terrainFace_t *face ) {
+ char temp[ 1024 ];
+ CString text;
+ const char *pname;
+
+ pname = face->texdef.Name();
+ if ( pname[ 0 ] == 0 ) {
+ pname = "unnamed";
+ }
+
+ sprintf( temp, "%s %i %i %.2f ", pname, ( int )face->texdef.shift[ 0 ], ( int )face->texdef.shift[ 1 ], face->texdef.rotate );
+ text += temp;
+
+ if ( face->texdef.scale[ 0 ] == ( int )face->texdef.scale[ 0 ] ) {
+ sprintf( temp, "%i ", ( int )face->texdef.scale[ 0 ] );
+ } else {
+ sprintf( temp, "%f ", ( float )face->texdef.scale[ 0 ] );
+ }
+ text += temp;
+
+ if ( face->texdef.scale[ 1 ] == (int)face->texdef.scale[ 1 ] ) {
+ sprintf( temp, "%i", ( int )face->texdef.scale[ 1 ] );
+ } else {
+ sprintf( temp, "%f", ( float )face->texdef.scale[ 1 ] );
+ }
+ text += temp;
+
+ // only output flags and value if not default
+ sprintf( temp, " %i %i %i ", face->texdef.contents, face->texdef.flags, face->texdef.value );
+ text += temp;
+
+ return text;
+}
+
+void Terrain_Write( terrainMesh_t *p, CMemFile *file ) {
+ int w;
+ int h;
+ terrainVert_t *vert;
+
+ MemFile_fprintf( file, " {\n terrainDef\n {\n" );
+ MemFile_fprintf( file, " %d %d %f %f\n", p->width, p->height, p->scale_x, p->scale_y );
+ MemFile_fprintf( file, " %f %f %f\n", p->origin[ 0 ], p->origin[ 1 ], p->origin[ 2 ] );
+
+ vert = p->heightmap;
+ for( h = 0; h < p->height; h++ ) {
+ for( w = 0; w < p->width; w++, vert++ ) {
+ MemFile_fprintf( file, " %f %s\n", vert->height, ( const char * )Terrain_SurfaceString( &vert->tri ) );
+ }
+ }
+
+ MemFile_fprintf( file, " }\n }\n" );
+}
+
+void Terrain_Write( terrainMesh_t *p, FILE *file ) {
+ int w;
+ int h;
+ terrainVert_t *vert;
+
+ fprintf( file, " {\n terrainDef\n {\n" );
+ fprintf( file, " %d %d %f %f\n", p->width, p->height, p->scale_x, p->scale_y );
+ fprintf( file, " %f %f %f\n", p->origin[ 0 ], p->origin[ 1 ], p->origin[ 2 ] );
+
+ vert = p->heightmap;
+ for( h = 0; h < p->height; h++ ) {
+ for( w = 0; w < p->width; w++, vert++ ) {
+ fprintf( file, " %f %s\n", vert->height, ( const char * )Terrain_SurfaceString( &vert->tri ) );
+ }
+ }
+
+ fprintf( file, " }\n }\n" );
+}
+
+void Terrain_Select( terrainMesh_t *p ) {
+ p->bSelected = true;
+}
+
+void Terrain_Deselect( terrainMesh_t *p ) {
+ p->bSelected = false;
+}
+
+void Terrain_Move( terrainMesh_t *pm, const vec3_t vMove, bool bRebuild ) {
+ pm->bDirty = true;
+
+ VectorAdd( pm->origin, vMove, pm->origin );
+
+ if ( bRebuild ) {
+ vec3_t vMin;
+ vec3_t vMax;
+
+ Terrain_CalcBounds( pm, vMin, vMax );
+ }
+
+ UpdateTerrainInspector();
+}
+
+void UpdateTerrainInspector( void ) {
+ // not written yet
+}
+
+void Terrain_CalcBounds( terrainMesh_t *p, vec3_t &vMin, vec3_t &vMax ) {
+ int w;
+ int h;
+ float f;
+ terrainVert_t *vert;
+
+ vMin[ 0 ] = p->origin[ 0 ];
+ vMin[ 1 ] = p->origin[ 1 ];
+ vMin[ 2 ] = MAX_WORLD_COORD;
+
+ vMax[ 0 ] = p->origin[ 0 ] + ( p->width - 1 ) * p->scale_x;
+ vMax[ 1 ] = p->origin[ 1 ] + ( p->height - 1 ) * p->scale_y;
+ vMax[ 2 ] = MIN_WORLD_COORD;
+
+ p->bDirty = true;
+ vert = p->heightmap;
+ for( h = 0; h < p->height; h++ ) {
+ for( w = 0; w < p->width; w++, vert++ ) {
+ f = p->origin[ 2 ] + vert->height;
+ if ( f < vMin[ 2 ] ) {
+ vMin[ 2 ] = f;
+ }
+
+ if ( f > vMax[ 2 ] ) {
+ vMax[ 2 ] = f;
+ }
+ }
+ }
+}
+
+void CalcTriNormal( const vec3_t a, const vec3_t b, const vec3_t c, vec3_t o ) {
+ vec3_t a1;
+ vec3_t b1;
+
+ VectorSubtract( b, a, a1 );
+ VectorNormalize( a1 );
+
+ VectorSubtract( c, a, b1 );
+ VectorNormalize( b1 );
+
+ CrossProduct( a1, b1, o );
+ VectorNormalize( o );
+}
+
+inline void Terrain_CalcVertPos( terrainMesh_t *p, int x, int y, vec3_t vert ) {
+ int index;
+
+ index = x + y * p->width;
+ vert[ 0 ] = p->origin[ 0 ] + x * p->scale_x;
+ vert[ 1 ] = p->origin[ 1 ] + y * p->scale_y;
+ vert[ 2 ] = p->origin[ 2 ] + p->heightmap[ index ].height;
+
+ VectorCopy( vert, p->heightmap[ index ].xyz );
+}
+
+void Terrain_CalcNormals( terrainMesh_t *p ) {
+ int x;
+ int y;
+ int width;
+ int num;
+ terrainVert_t *vert;
+ vec3_t norm;
+ terravert_t a0;
+ terravert_t a1;
+ terravert_t a2;
+ terravert_t b0;
+ terravert_t b1;
+ terravert_t b2;
+
+ p->bDirty = true;
+
+ num = p->height * p->width;
+ vert = p->heightmap;
+ //for( x = 0; x < num; x++, vert++ ) {
+ for( y = 0; y < p->height - 1; y++ ) {
+ for( x = 0; x < p->width - 1; x++, vert++ ) {
+ VectorClear( vert->normal );
+ Terrain_CalcVertPos( p, x, y, norm );
+ }
+ }
+
+ width = p->width;
+ vert = p->heightmap;
+
+ for( y = 0; y < p->height - 1; y++ ) {
+ for( x = 0; x < width - 1; x++ ) {
+ Terrain_GetTriangles( p, x, y, &a0, &a1, &a2, &b0, &b1, &b2, NULL );
+
+ CalcTriNormal( a0.xyz, a2.xyz, a1.xyz, norm );
+
+ VectorAdd( vert[ a0.index ].normal, norm, vert[ a0.index ].normal );
+ VectorAdd( vert[ a1.index ].normal, norm, vert[ a1.index ].normal );
+ VectorAdd( vert[ a2.index ].normal, norm, vert[ a2.index ].normal );
+
+ CalcTriNormal( b0.xyz, b2.xyz, b1.xyz, norm );
+
+ VectorAdd( vert[ b0.index ].normal, norm, vert[ b0.index ].normal );
+ VectorAdd( vert[ b1.index ].normal, norm, vert[ b1.index ].normal );
+ VectorAdd( vert[ b2.index ].normal, norm, vert[ b2.index ].normal );
+ }
+ }
+
+ for( x = 0; x < num; x++, vert++ ) {
+ VectorNormalize( vert->normal );
+ //FIXME
+ vert->normal[ 2 ] += 0.5;
+ VectorNormalize( vert->normal );
+ assert( vert->normal[ 2 ] > 0 );
+ VectorSet( vert->rgba, vert->normal[ 2 ], vert->normal[ 2 ], vert->normal[ 2 ] );
+ vert->rgba[ 3 ] = 1.0f;
+ }
+}
+
+void Terrain_FindReplaceTexture( terrainMesh_t *p, const char *pFind, const char *pReplace, bool bForce ) {
+ int w;
+ int h;
+ terrainVert_t *vert;
+ qtexture_t *texture;
+
+ texture = Texture_ForName( pReplace );
+
+ vert = p->heightmap;
+ for( h = 0; h < p->height; h++ ) {
+ for( w = 0; w < p->width; w++, vert++ ) {
+ if ( bForce || strcmpi( vert->tri.texture->name, pFind ) == 0 ) {
+ vert->tri.texture = texture;
+ vert->tri.texdef.SetName( texture->name );
+ }
+ }
+ }
+
+ if ( bForce ) {
+ p->numtextures = 0;
+ Terrain_AddTexture( p, Texture_ForName( pReplace ) );
+ } else {
+ Terrain_RemoveTexture( p, Texture_ForName( pFind ) );
+ Terrain_AddTexture( p, texture );
+ }
+}
+
+bool Terrain_HasTexture( terrainMesh_t *p, const char *name ) {
+ int w;
+ int h;
+ terrainVert_t *vert;
+
+ vert = p->heightmap;
+ for( h = 0; h < p->height; h++ ) {
+ for( w = 0; w < p->width; w++, vert++ ) {
+ if ( strcmpi( vert->tri.texture->name, name ) == 0 ) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+void Terrain_ReplaceQTexture( terrainMesh_t *p, qtexture_t *pOld, qtexture_t *pNew ) {
+ int w;
+ int h;
+ terrainVert_t *vert;
+
+ vert = p->heightmap;
+ for( h = 0; h < p->height; h++ ) {
+ for( w = 0; w < p->width; w++, vert++ ) {
+ if ( vert->tri.texture == pOld ) {
+ vert->tri.texture = pNew;
+ vert->tri.texdef.SetName( pNew->name );
+ }
+ }
+ }
+
+ Terrain_RemoveTexture( p, pOld );
+ Terrain_AddTexture( p, pNew );
+}
+
+void Terrain_SetTexture( terrainMesh_t *p, texdef_t *tex_def ) {
+ int w;
+ int h;
+ qtexture_t *newtex;
+ terrainVert_t *vert;
+
+ p->bDirty = 1;
+
+ newtex = Texture_ForName( tex_def->name );
+
+ p->numtextures = 0;
+ Terrain_AddTexture( p, newtex );
+
+ vert = p->heightmap;
+ for( h = 0; h < p->height; h++ ) {
+ for( w = 0; w < p->width; w++, vert++ ) {
+ vert->tri.texture = newtex;
+ vert->tri.texdef.SetName( newtex->name );
+ }
+ }
+
+ UpdateTerrainInspector();
+}
+
+void Terrain_Scale( terrainMesh_t *p, const vec3_t vOrigin, const vec3_t vAmt, bool bRebuild ) {
+ int w;
+ int h;
+ vec3_t pos;
+ terrainVert_t *vert;
+ vec3_t vMin;
+ vec3_t vMax;
+
+ vert = p->heightmap;
+ for( h = 0; h < p->height; h++ ) {
+ pos[ 1 ] = p->origin[ 1 ] + h * p->scale_y;
+ for( w = 0; w < p->width; w++, vert++ ) {
+ pos[ 0 ] = p->origin[ 0 ] + w * p->scale_x;
+ pos[ 2 ] = vert->height;
+
+ if ( ( g_qeglobals.d_select_mode == sel_terrainpoint ) && ( Terrain_PointInMoveList( vert ) == -1 ) ) {
+ continue;
+ }
+
+ vert->height -= vOrigin[ 2 ] - p->origin[ 2 ];
+ vert->height *= vAmt[ 2 ];
+ vert->height += vOrigin[ 2 ] - p->origin[ 2 ];
+ }
+ }
+
+ if ( g_qeglobals.d_select_mode != sel_terrainpoint ) {
+ p->scale_x *= vAmt[ 0 ];
+ p->scale_y *= vAmt[ 1 ];
+
+ p->origin[ 0 ] -= vOrigin[ 0 ];
+ p->origin[ 0 ] *= vAmt[ 0 ];
+ p->origin[ 0 ] += vOrigin[ 0 ];
+
+ p->origin[ 1 ] -= vOrigin[ 1 ];
+ p->origin[ 1 ] *= vAmt[ 1 ];
+ p->origin[ 1 ] += vOrigin[ 1 ];
+ }
+
+ if ( bRebuild ) {
+ Terrain_CalcBounds( p, vMin, vMax );
+ Terrain_CalcNormals( p );
+ Brush_RebuildBrush( p->pSymbiot, vMin, vMax );
+ }
+
+ UpdateTerrainInspector();
+}
+
+bool Terrain_DragScale( terrainMesh_t *p, vec3_t vAmt, vec3_t vMove ) {
+ vec3_t vMin;
+ vec3_t vMax;
+ vec3_t vScale;
+ vec3_t vTemp;
+ vec3_t vMid;
+ int i;
+
+ Terrain_CalcBounds( p, vMin, vMax );
+
+ VectorSubtract( vMax, vMin, vTemp );
+
+ // if we are scaling in the same dimension the terrain has no depth
+ for( i = 0; i < 3; i++ ) {
+ if ( ( vTemp[ i ] == 0 ) && ( vMove[ i ] != 0 ) ) {
+ return false;
+ }
+ }
+
+ for( i = 0; i < 3; i++ ) {
+ vMid[ i ] = ( vMin[ i ] + vMax[ i ] ) / 2;
+ }
+
+ for( i = 0; i < 3; i++ ) {
+ if ( vAmt[ i ] != 0 ) {
+ vScale[i] = 1.0 + vAmt[i] / vTemp[i];
+ } else {
+ vScale[i] = 1.0;
+ }
+ }
+
+ Terrain_Scale( p, vMid, vScale, false );
+ VectorSubtract( vMax, vMin, vTemp );
+ Terrain_CalcBounds( p, vMin, vMax );
+ VectorSubtract( vMax, vMin, vMid );
+ VectorSubtract( vMid, vTemp, vTemp );
+ VectorScale( vTemp, 0.5f, vTemp );
+
+ // abs of both should always be equal
+ if ( !VectorCompare( vMove, vAmt ) ) {
+ for( i = 0; i < 3; i++ ) {
+ if ( vMove[ i ] != vAmt[ i ] ) {
+ vTemp[ i ] = -vTemp[ i ];
+ }
+ }
+ }
+
+ Terrain_CalcNormals( p );
+ Terrain_Move( p, vTemp );
+
+ return true;
+}
+
+void Terrain_ApplyMatrix( terrainMesh_t *p, const vec3_t vOrigin, const vec3_t vMatrix[ 3 ], bool bSnap ) {
+}
+
+void Terrain_DrawFace( brush_t *brush, terrainFace_t *terraface ) {
+ terrainMesh_t *pm;
+ terravert_t a0;
+ terravert_t a1;
+ terravert_t a2;
+
+ pm = brush->pTerrain;
+
+ Terrain_GetTriangle( pm, terraface->index, &a0, &a1, &a2 );
+
+ qglBindTexture( GL_TEXTURE_2D, terraface->texture->texture_number );
+ qglBegin( GL_TRIANGLES );
+
+ // first tri
+ qglColor4fv( a0.rgba );
+ qglTexCoord2fv( a0.tc );
+ qglVertex3fv( a0.xyz );
+
+ qglColor4fv( a1.rgba );
+ qglTexCoord2fv( a1.tc );
+ qglVertex3fv( a1.xyz );
+
+ qglColor4fv( a2.rgba );
+ qglTexCoord2fv( a2.tc );
+ qglVertex3fv( a2.xyz );
+
+ qglEnd ();
+}
+
+void DrawTerrain( terrainMesh_t *pm, bool bPoints, bool bShade ) {
+ int i;
+ int w;
+ int h;
+ int x;
+ int y;
+ //int n;
+ //float x1;
+ //float y1;
+ float scale_x;
+ float scale_y;
+ //vec3_t pSelectedPoints[ MAX_TERRA_POINTS ];
+ //int nIndex;
+ terravert_t a0;
+ terravert_t a1;
+ terravert_t a2;
+ terravert_t b0;
+ terravert_t b1;
+ terravert_t b2;
+ terrainVert_t *vert;
+ qtexture_t *texture;
+
+ h = pm->height - 1;
+ w = pm->width - 1;
+
+ scale_x = pm->scale_x;
+ scale_y = pm->scale_y;
+
+ qglShadeModel (GL_SMOOTH);
+
+ if ( bShade ) {
+ for( i = 0; i < pm->numtextures; i++ ) {
+ texture = pm->textures[ i ];
+
+ qglBindTexture( GL_TEXTURE_2D, texture->texture_number );
+
+ vert = pm->heightmap;
+ for( y = 0; y < h; y++ ) {
+ qglBegin( GL_TRIANGLES );
+
+ for( x = 0; x < w; x++, vert++ ) {
+ Terrain_GetTriangles( pm, x, y, &a0, &a1, &a2, &b0, &b1, &b2, texture );
+
+ // first tri
+ if ( a0.rgba[ 3 ] || a1.rgba[ 3 ] || a2.rgba[ 3 ] ) {
+ qglColor4fv( a0.rgba );
+ qglTexCoord2fv( a0.tc );
+ qglVertex3fv( a0.xyz );
+
+ qglColor4fv( a1.rgba );
+ qglTexCoord2fv( a1.tc );
+ qglVertex3fv( a1.xyz );
+
+ qglColor4fv( a2.rgba );
+ qglTexCoord2fv( a2.tc );
+ qglVertex3fv( a2.xyz );
+ }
+
+ // second tri
+ if ( b0.rgba[ 3 ] || b1.rgba[ 3 ] || b2.rgba[ 3 ] ) {
+ qglColor4fv( b0.rgba );
+ qglTexCoord2fv( b0.tc );
+ qglVertex3fv( b0.xyz );
+
+ qglColor4fv( b1.rgba );
+ qglTexCoord2fv( b1.tc );
+ qglVertex3fv( b1.xyz );
+
+ qglColor4fv( b2.rgba );
+ qglTexCoord2fv( b2.tc );
+ qglVertex3fv( b2.xyz );
+ }
+ }
+
+ qglEnd ();
+ }
+ }
+ } else {
+ for( i = 0; i < pm->numtextures; i++ ) {
+ texture = pm->textures[ i ];
+
+ qglBindTexture( GL_TEXTURE_2D, texture->texture_number );
+
+ vert = pm->heightmap;
+ for( y = 0; y < h; y++ ) {
+ qglBegin( GL_TRIANGLES );
+
+ for( x = 0; x < w; x++, vert++ ) {
+ Terrain_GetTriangles( pm, x, y, &a0, &a1, &a2, &b0, &b1, &b2, texture );
+
+ // first tri
+ if ( a0.rgba[ 3 ] || a1.rgba[ 3 ] || a2.rgba[ 3 ] ) {
+ qglColor4fv( a0.rgba );
+ qglTexCoord2fv( a0.tc );
+ qglVertex3fv( a0.xyz );
+
+ qglColor4fv( a1.rgba );
+ qglTexCoord2fv( a1.tc );
+ qglVertex3fv( a1.xyz );
+
+ qglColor4fv( a2.rgba );
+ qglTexCoord2fv( a2.tc );
+ qglVertex3fv( a2.xyz );
+ }
+
+ // second tri
+ if ( b0.rgba[ 3 ] || b1.rgba[ 3 ] || b2.rgba[ 3 ] ) {
+ qglColor4fv( b0.rgba );
+ qglTexCoord2fv( b0.tc );
+ qglVertex3fv( b0.xyz );
+
+ qglColor4fv( b1.rgba );
+ qglTexCoord2fv( b1.tc );
+ qglVertex3fv( b1.xyz );
+
+ qglColor4fv( b2.rgba );
+ qglTexCoord2fv( b2.tc );
+ qglVertex3fv( b2.xyz );
+ }
+ }
+ qglEnd ();
+ }
+ }
+ }
+
+ qglPushAttrib( GL_CURRENT_BIT );
+
+ bool bDisabledLighting = qglIsEnabled( GL_LIGHTING );
+ if ( bDisabledLighting ) {
+ qglDisable( GL_LIGHTING );
+ }
+
+#if 0
+ terrainVert_t *currentrow;
+ terrainVert_t *nextrow;
+ float x2;
+ float y2;
+
+ // Draw normals
+ qglDisable( GL_TEXTURE_2D );
+ qglDisable( GL_BLEND );
+ qglColor3f( 1, 1, 1 );
+ qglBegin( GL_LINES );
+
+ y2 = pm->origin[ 1 ];
+ nextrow = pm->heightmap;
+ for( y = 0; y < h; y++ ) {
+ y1 = y2;
+ y2 += scale_y;
+
+ x2 = pm->origin[ 0 ];
+ currentrow = nextrow;
+ nextrow = currentrow + pm->width;
+ for( x = 0; x < w; x++ ) {
+ x1 = x2;
+ x2 += scale_x;
+
+ // normals
+ qglVertex3f( x1, y1, pm->origin[ 2 ] + currentrow[ x ].height );
+ 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 );
+
+ qglVertex3f( x2, y1, pm->origin[ 2 ] + currentrow[ x + 1 ].height );
+ 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 );
+
+ qglVertex3f( x1, y2, pm->origin[ 2 ] + nextrow[ x ].height );
+ 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 );
+
+ qglVertex3f( x2, y2, pm->origin[ 2 ] + nextrow[ x + 1 ].height );
+ 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 );
+ }
+ }
+
+ qglEnd ();
+ qglEnable( GL_TEXTURE_2D );
+#endif
+
+#if 0
+ if ( bPoints && ( g_qeglobals.d_select_mode == sel_terrainpoint || g_qeglobals.d_select_mode == sel_area ) ) {
+ qglPointSize( 6 );
+ qglDisable( GL_TEXTURE_2D );
+ qglDisable( GL_BLEND );
+
+ qglBegin( GL_POINTS );
+
+ nIndex = 0;
+
+ qglColor4f( 1, 0, 1, 1 );
+
+ y1 = pm->origin[ 1 ];
+ for ( y = 0; y < pm->height; y++, y1 += pm->scale_y ) {
+ x1 = pm->origin[ 0 ];
+ for( x = 0; x < pm->width; x++, x1 += pm->scale_x ) {
+ // FIXME: need to not do loop lookups inside here
+ n = Terrain_PointInMoveList( &pm->heightmap[ x + y * pm->width ] );
+ if ( n >= 0 ) {
+ VectorSet( pSelectedPoints[ nIndex ], x1, y1, pm->heightmap[ x + y * pm->width ].height + pm->origin[ 2 ] );
+ nIndex++;
+ } else {
+ qglVertex3f( x1, y1, pm->origin[ 2 ] + pm->heightmap[ x + y * pm->width ].height );
+ }
+ }
+ }
+
+ qglEnd();
+
+ qglEnable( GL_TEXTURE_2D );
+
+ if ( nIndex > 0 ) {
+ qglBegin( GL_POINTS );
+ qglColor4f( 0, 0, 1, 1 );
+ while( nIndex-- > 0 ) {
+ qglVertex3fv( pSelectedPoints[ nIndex ] );
+ }
+
+ qglEnd();
+ }
+ }
+#endif
+
+ if ( g_qeglobals.d_numterrapoints && ( ( g_qeglobals.d_select_mode == sel_terrainpoint ) || ( g_qeglobals.d_select_mode == sel_terraintexture ) ) ) {
+#if 0
+ qglPointSize( 6 );
+ qglDisable( GL_TEXTURE_2D );
+ qglDisable( GL_BLEND );
+
+ qglBegin( GL_POINTS );
+
+ qglColor4f( 1, 0, 1, 1 );
+
+ for( i = 0; i < g_qeglobals.d_numterrapoints; i++ ) {
+ qglVertex3fv( g_qeglobals.d_terrapoints[ i ]->xyz );
+ }
+
+ qglEnd();
+
+ qglEnable( GL_TEXTURE_2D );
+#endif
+
+ brush_t *pb;
+ terrainMesh_t *pm;
+
+ pm = NULL;
+ for( pb = active_brushes .next; pb != &active_brushes; pb = pb->next ) {
+ if ( pb->terrainBrush ) {
+ pm = pb->pTerrain;
+ break;
+ }
+ }
+
+ if ( pm ) {
+ qglDisable( GL_TEXTURE_2D );
+ qglBegin( GL_TRIANGLES );
+ qglEnable( GL_BLEND );
+
+ qglColor4f( 0.25, 0.5, 1, 0.35 );
+
+ for( i = 0; i < g_qeglobals.d_numterrapoints; i++ ) {
+ terravert_t a0;
+ terravert_t a1;
+ terravert_t a2;
+
+ qglColor4f( 0.25, 0.5, 1, g_qeglobals.d_terrapoints[ i ]->scale * 0.75 + 0.25 );
+ Terrain_GetTriangle( pm, g_qeglobals.d_terrapoints[ i ]->tri.index * 2, &a0, &a1, &a2 );
+
+ qglVertex3fv( a0.xyz );
+ qglVertex3fv( a1.xyz );
+ qglVertex3fv( a2.xyz );
+
+ Terrain_GetTriangle( pm, g_qeglobals.d_terrapoints[ i ]->tri.index * 2 + 1, &a0, &a1, &a2 );
+
+ qglVertex3fv( a0.xyz );
+ qglVertex3fv( a1.xyz );
+ qglVertex3fv( a2.xyz );
+ }
+ qglEnd();
+
+ qglDisable( GL_BLEND );
+ qglEnable( GL_TEXTURE_2D );
+ }
+ }
+}
+
+void Terrain_DrawCam( terrainMesh_t *pm ) {
+ qglColor3f( 1,1,1 );
+ qglPushAttrib( GL_ALL_ATTRIB_BITS );
+
+ if ( g_bPatchWireFrame ) {
+ if( pm->bSelected ) {
+ qglLineWidth( 2 );
+ } else {
+ qglLineWidth( 1 );
+ }
+
+ qglDisable( GL_CULL_FACE );
+ qglPolygonMode( GL_FRONT_AND_BACK, GL_LINE );
+ qglDisable( GL_TEXTURE_2D );
+
+ if ( g_PrefsDlg.m_bGLLighting ) {
+ qglDisable( GL_LIGHTING );
+ }
+
+ DrawTerrain( pm, pm->bSelected, true );
+
+ if ( g_PrefsDlg.m_bGLLighting ) {
+ qglEnable( GL_LIGHTING );
+ }
+
+ qglEnable( GL_CULL_FACE );
+ qglLineWidth( 1 );
+ } else {
+ qglEnable( GL_CULL_FACE );
+ qglCullFace( GL_FRONT );
+
+ // draw the textured polys
+ DrawTerrain( pm, pm->bSelected, true );
+
+ // if selected, draw the red tint on the polys
+ if( pm->bSelected ) { // && ( g_qeglobals.d_savedinfo.include & INCLUDE_CAMERATINT ) ) {
+ qglColor4f( 1.0, 0.0, 0.0, 0.3 );
+ qglEnable( GL_BLEND );
+ qglPolygonMode( GL_FRONT_AND_BACK, GL_FILL );
+ qglBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
+
+ DrawTerrain( pm, pm->bSelected );
+
+ qglColor3f( 1, 1, 1 );
+ }
+
+ // draw the backside poly outlines
+ qglCullFace( GL_BACK );
+ qglPolygonMode( GL_FRONT_AND_BACK, GL_LINE );
+ qglDisable( GL_BLEND );
+ DrawTerrain( pm, pm->bSelected, true );
+ }
+
+ qglPopAttrib();
+}
+
+void Terrain_DrawXY( terrainMesh_t *pm, entity_t *owner ) {
+ qglPolygonMode( GL_FRONT_AND_BACK, GL_LINE );
+
+ if ( pm->bSelected ) {
+ qglColor3fv( g_qeglobals.d_savedinfo.colors[ COLOR_SELBRUSHES ] );
+ } else if ( owner != world_entity && _stricmp( owner->eclass->name, "func_group" ) ) {
+ qglColor3fv( owner->eclass->color );
+ } else {
+ //FIXME
+ qglColor3fv( g_qeglobals.d_savedinfo.colors[ COLOR_BRUSHES ] );
+ }
+
+ qglLineWidth( 1 );
+
+ DrawTerrain( pm, pm->bSelected );
+
+ qglPolygonMode( GL_FRONT_AND_BACK, GL_FILL );
+}
+
+bool OnlyTerrainSelected( void ) {
+ brush_t *pb;
+
+ //if ( numselfaces || selected_brushes.next == &selected_brushes )
+ if ( selected_brushes.next == &selected_brushes ) {
+ return false;
+ }
+
+ for( pb = selected_brushes.next; pb != &selected_brushes; pb = pb->next ) {
+ if ( !pb->terrainBrush ) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool AnyTerrainSelected( void ) {
+ brush_t *pb;
+
+ //if ( numselfaces || selected_brushes.next == &selected_brushes )
+ if ( selected_brushes.next == &selected_brushes ) {
+ return false;
+ }
+
+ for( pb = selected_brushes.next; pb != &selected_brushes; pb = pb->next ) {
+ if ( pb->terrainBrush ) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+terrainMesh_t *SingleTerrainSelected( void ) {
+ if ( selected_brushes.next->terrainBrush ) {
+ return selected_brushes.next->pTerrain;
+ }
+
+ return NULL;
+}
+
+void Terrain_Edit( void ) {
+ //brush_t *pb;
+ //terrainMesh_t *p;
+ //int i;
+ //int j;
+
+// g_qeglobals.d_numpoints = 0;
+ g_qeglobals.d_numterrapoints = 0;
+#if 0
+ for( pb = selected_brushes.next; pb != &selected_brushes ; pb = pb->next ) {
+ if ( pb->terrainBrush ) {
+ p = pb->pTerrain;
+
+ if ( ( g_qeglobals.d_numpoints + p->width * p->height ) > MAX_POINTS ) {
+ Warning( "Too many points on terrain\n" );
+ continue;
+ }
+ for( i = 0; i < p->width; i++ ) {
+ for( j = 0; j < p->height; j++ ) {
+ Terrain_CalcVertPos( p, i, j, g_qeglobals.d_points[ g_qeglobals.d_numpoints ] );
+ g_qeglobals.d_numpoints++;
+ }
+ }
+ }
+ }
+#endif
+
+ g_qeglobals.d_select_mode = sel_terrainpoint;
+}
+
+void Terrain_SelectPointByRay( vec3_t org, vec3_t dir, int buttons ) {
+ float bestd;
+ terrainFace_t *face;
+ terrainFace_t *bestface;
+ brush_t *pb;
+ float dist;
+ vec3_t vec;
+
+ // find the point closest to the ray
+ bestface = NULL;
+ bestd = WORLD_SIZE * 4;
+ for( pb = selected_brushes.next; pb != &selected_brushes; pb = pb->next ) {
+ if ( pb->terrainBrush ) {
+ face = Terrain_Ray( org, dir, pb, &dist );
+ if ( face && ( dist < bestd ) ) {
+ bestface = face;
+ bestd = dist;
+ }
+ }
+ }
+
+ for( pb = active_brushes .next; pb != &active_brushes; pb = pb->next ) {
+ if ( pb->terrainBrush ) {
+ face = Terrain_Ray( org, dir, pb, &dist );
+ if ( face && ( dist < bestd ) ) {
+ bestface = face;
+ bestd = dist;
+ }
+ }
+ }
+
+ if ( !bestface ) {
+ return;
+ }
+
+ VectorMA( org, bestd, dir, vec );
+ Terrain_AddMovePoint( vec, buttons & MK_CONTROL, buttons & MK_SHIFT, buttons );
+}
+
+void Terrain_AddMovePoint( vec3_t v, bool bMulti, bool bFull, int buttons ) {
+ brush_t *pb;
+ terrainMesh_t *p;
+ terrainVert_t *vert;
+ int x;
+ int y;
+ int x1, y1;
+ float dx, dy;
+ float dist;
+ float pd;
+
+ if ( !g_bSameView && !bMulti && !bFull ) {
+ g_bSameView = true;
+ return;
+ }
+
+ g_qeglobals.d_numterrapoints = 0;
+ for( pb = active_brushes .next; pb != &active_brushes; pb = pb->next ) {
+ //for( pb = selected_brushes.next; pb != &selected_brushes; pb = pb->next ) {
+ if ( pb->terrainBrush ) {
+ p = pb->pTerrain;
+
+ x = ( v[ 0 ] - p->origin[ 0 ] ) / p->scale_x;
+ y = ( v[ 1 ] - p->origin[ 1 ] ) / p->scale_x;
+ if ( ( x < 0 ) || ( x >= p->width ) || ( y < 0 ) || ( y >= p->height ) ) {
+ continue;
+ }
+
+ vert = p->heightmap;
+ for( y1 = 0; y1 < p->height; y1++ ) {
+ for( x1 = 0; x1 < p->width; x1++, vert++ ) {
+
+ if ( g_qeglobals.d_terrainBrush == TERRAIN_BRUSH_CIRCLE ) {
+ dx = x1 - x;
+ dy = y1 - y;
+ dist = sqrt( dx * dx + dy * dy );
+ } else {
+ dx = abs( x1 - x );
+ dy = abs( y1 - y );
+ if ( dx > dy ) {
+ dist = dx;
+ } else {
+ dist = dy;
+ }
+ }
+
+ pd = dist * 2.0f / g_qeglobals.d_terrainBrushSize;
+ if ( fabs( pd ) <= 1.0f ) {
+ Terrain_AddPoint( p, vert );
+
+ if ( ( buttons & MK_LBUTTON ) && ( g_qeglobals.d_select_mode == sel_terraintexture ) ) {
+ vert->tri.texture = Texture_ForName( g_qeglobals.d_texturewin.texdef.name );
+ vert->tri.texdef.SetName( vert->tri.texture->name );
+ Terrain_AddTexture( p, vert->tri.texture );
+ continue;
+ }
+
+ if ( g_qeglobals.d_terrainFalloff == TERRAIN_FALLOFF_CURVED ) {
+ if ( g_qeglobals.d_terrainBrush == TERRAIN_BRUSH_CIRCLE ) {
+ vert->scale = ( 0.5f + cos( pd * M_PI ) * 0.5f );
+ } else {
+ 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;
+ }
+ } else {
+ vert->scale = 1.0f - pd;
+ }
+
+ switch( g_qeglobals.d_terrainNoiseType ) {
+ case NOISE_PLUS :
+ vert->scale *= crandom();
+ break;
+
+ case NOISE_PLUSMINUS :
+ vert->scale *= random();
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+void Terrain_UpdateSelected( vec3_t vMove ) {
+ int i;
+ brush_t *pb;
+ terrainMesh_t *p;
+ vec3_t vMin;
+ vec3_t vMax;
+
+ if ( g_qeglobals.d_select_mode == sel_terrainpoint ) {
+ for( i = 0; i < g_qeglobals.d_numterrapoints; i++ ) {
+ g_qeglobals.d_terrapoints[ i ]->height += vMove[ 2 ] * g_qeglobals.d_terrapoints[ i ]->scale;
+ }
+ }
+
+ for( pb = active_brushes .next; pb != &active_brushes; pb = pb->next ) {
+// for( pb = selected_brushes.next; pb != &selected_brushes; pb = pb->next ) {
+ if ( pb->terrainBrush ) {
+ p = pb->pTerrain;
+
+ Terrain_CalcBounds( p, vMin, vMax );
+ Terrain_CalcNormals( p );
+ Brush_RebuildBrush( p->pSymbiot, vMin, vMax );
+ }
+ }
+}
+
+int Terrain_PointInMoveList( terrainVert_t *pf ) {
+ int i;
+
+ for( i = 0; i < g_qeglobals.d_numterrapoints; i++ ) {
+ if ( pf == g_qeglobals.d_terrapoints[ i ] ) {
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+void Terrain_RemovePointFromMoveList( terrainVert_t *v ) {
+ int n;
+ int i;
+
+ while( ( n = Terrain_PointInMoveList( v ) ) >= 0 ) {
+ for( i = n; i < g_qeglobals.d_numterrapoints - 1; i++ ) {
+ g_qeglobals.d_terrapoints[ i ] = g_qeglobals.d_terrapoints[ i + 1 ];
+ }
+
+ g_qeglobals.d_numterrapoints--;
+ }
+}
+
+void Terrain_AddPoint( terrainMesh_t *p, terrainVert_t *v ) {
+ if ( g_qeglobals.d_numterrapoints < MAX_TERRA_POINTS ) {
+ g_qeglobals.d_terrapoints[ g_qeglobals.d_numterrapoints++ ] = v;
+ }
+}
+
+void Terrain_SelectAreaPoints( void ) {
+ brush_t *pb;
+ terrainMesh_t *p;
+ int x;
+ int y;
+ vec3_t vec;
+
+ g_qeglobals.d_numterrapoints = 0;
+ g_nPatchClickedView = -1;
+
+ for( pb = selected_brushes.next; pb != &selected_brushes; pb = pb->next ) {
+ if ( pb->terrainBrush ) {
+ p = pb->pTerrain;
+ for( x = 0; x < p->width; x++ ) {
+ for( y = 0; y < p->height; y++ ) {
+ Terrain_CalcVertPos( p, x, y, vec );
+ if ( within( vec, g_qeglobals.d_vAreaTL, g_qeglobals.d_vAreaBR ) ) {
+ if ( g_qeglobals.d_numterrapoints < MAX_TERRA_POINTS ) {
+ g_qeglobals.d_terrapoints[ g_qeglobals.d_numterrapoints++ ] = &p->heightmap[ x + y * p->width ];
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+#define EPSILON 0.0001
+
+bool RayTriangleIntersect( vec3_t orig, vec3_t dir, vec3_t vert1, vec3_t vert2, vec3_t vert3, float *t ) {
+ float u;
+ float v;
+ vec3_t edge1;
+ vec3_t edge2;
+ vec3_t tvec;
+ vec3_t pvec;
+ vec3_t qvec;
+ float det;
+
+ VectorSubtract( vert2, vert1, edge1 );
+ VectorSubtract( vert3, vert1, edge2 );
+
+ // begin calculating determinant - also used to calculate U parameter
+ CrossProduct( dir, edge2, pvec );
+
+ // if determinant is near zero, ray lies in plane of triangle
+ det = DotProduct( edge1, pvec );
+ if ( det < EPSILON ) {
+ return false;
+ }
+
+ // calculate distance from vert1 to ray origin
+ VectorSubtract( orig, vert1, tvec );
+
+ // calculate U parameter and test bounds
+ u = DotProduct( tvec, pvec );
+ if ( ( u < 0.0f ) || ( u > det ) ) {
+ return false;
+ }
+
+ // prepare to test V parameter
+ CrossProduct( tvec, edge1, qvec );
+
+ // calculate V parameter and test bounds
+ v = DotProduct( dir, qvec );
+ if ( ( v < 0.0f ) || ( u + v > det ) ) {
+ return false;
+ }
+
+ // calculate t, scale parameters, ray intersects triangle
+ *t = DotProduct( edge2, qvec ) / det;
+
+ return true;
+}
+
+/*
+==============
+Terrain_Ray
+
+Itersects a ray with a terrain
+Returns the face hit and the distance along the ray the intersection occured at
+Returns NULL and 0 if not hit at all
+==============
+*/
+terrainFace_t *Terrain_Ray( vec3_t origin, vec3_t dir, brush_t *b, float *dist ) {
+ terrainMesh_t *pm;
+ int h;
+ int w;
+ int x;
+ int y;
+ float best_t;
+ float t;
+ terravert_t a0;
+ terravert_t a1;
+ terravert_t a2;
+ terravert_t b0;
+ terravert_t b1;
+ terravert_t b2;
+ terrainVert_t *vert;
+ terrainFace_t *best;
+
+ best = NULL;
+ best_t = WORLD_SIZE * 2;
+
+ pm = b->pTerrain;
+ h = pm->height - 1;
+ w = pm->width - 1;
+
+ vert = pm->heightmap;
+ for( y = 0; y < h; y++, vert++ ) {
+ for( x = 0; x < w; x++, vert++ ) {
+ Terrain_GetTriangles( pm, x, y, &a0, &a1, &a2, &b0, &b1, &b2, NULL );
+
+ t = WORLD_SIZE * 2;
+ if ( RayTriangleIntersect( origin, dir, a2.xyz, a1.xyz, a0.xyz, &t ) ) {
+ if ( ( t >= 0 ) && ( t < best_t ) ) {
+ best = &vert->tri;
+ best_t = t;
+ }
+ }
+
+ t = WORLD_SIZE * 2;
+ if ( RayTriangleIntersect( origin, dir, b2.xyz, b1.xyz, b0.xyz, &t ) ) {
+ if ( ( t >= 0 ) && ( t < best_t ) ) {
+ best = &vert->tri;
+ best_t = t;
+ }
+ }
+ }
+ }
+
+ if ( !best ) {
+ *dist = 0;
+ return NULL;
+ }
+
+ *dist = best_t;
+
+ return best;
+}
+
+/*
+============
+Select_TerrainFace
+
+Select the face
+============
+*/
+void Select_TerrainFace ( brush_t * brush, terrainFace_t *terraface ) {
+#if 0
+ UnSelect_Brush( brush );
+
+ if( numselfaces < MAX_SEL_FACES ) {
+ selfaces[numselfaces].face = NULL;
+ selfaces[numselfaces].brush = brush;
+ selfaces[numselfaces].terraface = terraface;
+ numselfaces++;
+ }
+#endif
+}
+
+void Select_TerrainFacesFromBrush( brush_t *brush ) {
+ terrainMesh_t *pm;
+ int h;
+ int w;
+ int x;
+ int y;
+
+ pm = brush->pTerrain;
+ h = pm->height - 1;
+ w = pm->width - 1;
+
+ for( y = 0; y < h; y++ ) {
+ for( x = 0; x < w; x++ ) {
+ Select_TerrainFace( brush, &brush->pTerrain->heightmap[ x + y * pm->width ].tri );
+ }
+ }
+}
+
+void SetTerrainTexdef( brush_t *brush, terrainFace_t *face, texdef_t *texdef ) {
+ int oldFlags;
+ int oldContents;
+
+ oldFlags = face->texdef.flags;
+ oldContents = face->texdef.contents;
+
+ face->texdef = *texdef;
+
+ face->texdef.flags = ( face->texdef.flags & ~SURF_KEEP ) | ( oldFlags & SURF_KEEP );
+ face->texdef.contents = ( face->texdef.contents & ~CONTENTS_KEEP ) | ( oldContents & CONTENTS_KEEP );
+
+ face->texture = Texture_ForName( texdef->name );
+
+ //Terrain_AddTexture( face->texture );
+}
+
+void RotateTerrainFaceTexture( terrainFace_t *vert, int nAxis, float fDeg ) {
+}
+
+void TerrainFace_FitTexture( terrainFace_t *vert ) {
+}
+
+void Terrain_Init( void ) {
+ g_qeglobals.d_terrainWidth = 64;
+ g_qeglobals.d_terrainHeight = 64;
+ g_qeglobals.d_terrainBrushSize = 12;
+ g_qeglobals.d_terrainNoiseType = NOISE_NONE;
+ //g_qeglobals.d_terrainFalloff = TERRAIN_FALLOFF_LINEAR;
+ g_qeglobals.d_terrainFalloff = TERRAIN_FALLOFF_CURVED;
+ g_qeglobals.d_terrainBrush = TERRAIN_BRUSH_CIRCLE;
+ //g_qeglobals.d_terrainBrush = TERRAIN_BRUSH_SQUARE;
+}