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 }