MapFile.cpp (24367B)
1 /* 2 =========================================================================== 3 4 Doom 3 BFG Edition GPL Source Code 5 Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. 6 7 This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code"). 8 9 Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify 10 it under the terms of the GNU General Public License as published by 11 the Free Software Foundation, either version 3 of the License, or 12 (at your option) any later version. 13 14 Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful, 15 but WITHOUT ANY WARRANTY; without even the implied warranty of 16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 GNU General Public License for more details. 18 19 You should have received a copy of the GNU General Public License 20 along with Doom 3 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>. 21 22 In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below. 23 24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. 25 26 =========================================================================== 27 */ 28 29 #include "precompiled.h" 30 #pragma hdrstop 31 32 33 /* 34 =============== 35 FloatCRC 36 =============== 37 */ 38 ID_INLINE unsigned int FloatCRC( float f ) { 39 return *(unsigned int *)&f; 40 } 41 42 /* 43 =============== 44 StringCRC 45 =============== 46 */ 47 ID_INLINE unsigned int StringCRC( const char *str ) { 48 unsigned int i, crc; 49 const unsigned char *ptr; 50 51 crc = 0; 52 ptr = reinterpret_cast<const unsigned char*>(str); 53 for ( i = 0; str[i]; i++ ) { 54 crc ^= str[i] << (i & 3); 55 } 56 return crc; 57 } 58 59 /* 60 ================= 61 ComputeAxisBase 62 63 WARNING : special case behaviour of atan2(y,x) <-> atan(y/x) might not be the same everywhere when x == 0 64 rotation by (0,RotY,RotZ) assigns X to normal 65 ================= 66 */ 67 static void ComputeAxisBase( const idVec3 &normal, idVec3 &texS, idVec3 &texT ) { 68 float RotY, RotZ; 69 idVec3 n; 70 71 // do some cleaning 72 n[0] = ( idMath::Fabs( normal[0] ) < 1e-6f ) ? 0.0f : normal[0]; 73 n[1] = ( idMath::Fabs( normal[1] ) < 1e-6f ) ? 0.0f : normal[1]; 74 n[2] = ( idMath::Fabs( normal[2] ) < 1e-6f ) ? 0.0f : normal[2]; 75 76 RotY = -atan2( n[2], idMath::Sqrt( n[1] * n[1] + n[0] * n[0]) ); 77 RotZ = atan2( n[1], n[0] ); 78 // rotate (0,1,0) and (0,0,1) to compute texS and texT 79 texS[0] = -sin(RotZ); 80 texS[1] = cos(RotZ); 81 texS[2] = 0; 82 // the texT vector is along -Z ( T texture coorinates axis ) 83 texT[0] = -sin(RotY) * cos(RotZ); 84 texT[1] = -sin(RotY) * sin(RotZ); 85 texT[2] = -cos(RotY); 86 } 87 88 /* 89 ================= 90 idMapBrushSide::GetTextureVectors 91 ================= 92 */ 93 void idMapBrushSide::GetTextureVectors( idVec4 v[2] ) const { 94 int i; 95 idVec3 texX, texY; 96 97 ComputeAxisBase( plane.Normal(), texX, texY ); 98 for ( i = 0; i < 2; i++ ) { 99 v[i][0] = texX[0] * texMat[i][0] + texY[0] * texMat[i][1]; 100 v[i][1] = texX[1] * texMat[i][0] + texY[1] * texMat[i][1]; 101 v[i][2] = texX[2] * texMat[i][0] + texY[2] * texMat[i][1]; 102 v[i][3] = texMat[i][2] + ( origin * v[i].ToVec3() ); 103 } 104 } 105 106 /* 107 ================= 108 idMapPatch::Parse 109 ================= 110 */ 111 idMapPatch *idMapPatch::Parse( idLexer &src, const idVec3 &origin, bool patchDef3, float version ) { 112 float info[7]; 113 idDrawVert *vert; 114 idToken token; 115 int i, j; 116 117 if ( !src.ExpectTokenString( "{" ) ) { 118 return NULL; 119 } 120 121 // read the material (we had an implicit 'textures/' in the old format...) 122 if ( !src.ReadToken( &token ) ) { 123 src.Error( "idMapPatch::Parse: unexpected EOF" ); 124 return NULL; 125 } 126 127 // Parse it 128 if (patchDef3) { 129 if ( !src.Parse1DMatrix( 7, info ) ) { 130 src.Error( "idMapPatch::Parse: unable to Parse patchDef3 info" ); 131 return NULL; 132 } 133 } else { 134 if ( !src.Parse1DMatrix( 5, info ) ) { 135 src.Error( "idMapPatch::Parse: unable to parse patchDef2 info" ); 136 return NULL; 137 } 138 } 139 140 idMapPatch *patch = new (TAG_IDLIB) idMapPatch( info[0], info[1] ); 141 142 patch->SetSize( info[0], info[1] ); 143 if ( version < 2.0f ) { 144 patch->SetMaterial( "textures/" + token ); 145 } else { 146 patch->SetMaterial( token ); 147 } 148 149 if ( patchDef3 ) { 150 patch->SetHorzSubdivisions( info[2] ); 151 patch->SetVertSubdivisions( info[3] ); 152 patch->SetExplicitlySubdivided( true ); 153 } 154 155 if ( patch->GetWidth() < 0 || patch->GetHeight() < 0 ) { 156 src.Error( "idMapPatch::Parse: bad size" ); 157 delete patch; 158 return NULL; 159 } 160 161 // these were written out in the wrong order, IMHO 162 if ( !src.ExpectTokenString( "(" ) ) { 163 src.Error( "idMapPatch::Parse: bad patch vertex data" ); 164 delete patch; 165 return NULL; 166 } 167 168 169 for ( j = 0; j < patch->GetWidth(); j++ ) { 170 if ( !src.ExpectTokenString( "(" ) ) { 171 src.Error( "idMapPatch::Parse: bad vertex row data" ); 172 delete patch; 173 return NULL; 174 } 175 for ( i = 0; i < patch->GetHeight(); i++ ) { 176 float v[5]; 177 178 if ( !src.Parse1DMatrix( 5, v ) ) { 179 src.Error( "idMapPatch::Parse: bad vertex column data" ); 180 delete patch; 181 return NULL; 182 } 183 184 vert = &((*patch)[i * patch->GetWidth() + j]); 185 vert->xyz[0] = v[0] - origin[0]; 186 vert->xyz[1] = v[1] - origin[1]; 187 vert->xyz[2] = v[2] - origin[2]; 188 vert->SetTexCoord( v[3], v[4] ); 189 } 190 if ( !src.ExpectTokenString( ")" ) ) { 191 delete patch; 192 src.Error( "idMapPatch::Parse: unable to parse patch control points" ); 193 return NULL; 194 } 195 } 196 197 if ( !src.ExpectTokenString( ")" ) ) { 198 src.Error( "idMapPatch::Parse: unable to parse patch control points, no closure" ); 199 delete patch; 200 return NULL; 201 } 202 203 // read any key/value pairs 204 while( src.ReadToken( &token ) ) { 205 if ( token == "}" ) { 206 src.ExpectTokenString( "}" ); 207 break; 208 } 209 if ( token.type == TT_STRING ) { 210 idStr key = token; 211 src.ExpectTokenType( TT_STRING, 0, &token ); 212 patch->epairs.Set( key, token ); 213 } 214 } 215 216 return patch; 217 } 218 219 /* 220 ============ 221 idMapPatch::Write 222 ============ 223 */ 224 bool idMapPatch::Write( idFile *fp, int primitiveNum, const idVec3 &origin ) const { 225 int i, j; 226 const idDrawVert *v; 227 228 if ( GetExplicitlySubdivided() ) { 229 fp->WriteFloatString( "// primitive %d\n{\n patchDef3\n {\n", primitiveNum ); 230 fp->WriteFloatString( " \"%s\"\n ( %d %d %d %d 0 0 0 )\n", GetMaterial(), GetWidth(), GetHeight(), GetHorzSubdivisions(), GetVertSubdivisions()); 231 } else { 232 fp->WriteFloatString( "// primitive %d\n{\n patchDef2\n {\n", primitiveNum ); 233 fp->WriteFloatString( " \"%s\"\n ( %d %d 0 0 0 )\n", GetMaterial(), GetWidth(), GetHeight()); 234 } 235 236 fp->WriteFloatString( " (\n" ); 237 idVec2 st; 238 for ( i = 0; i < GetWidth(); i++ ) { 239 fp->WriteFloatString( " ( " ); 240 for ( j = 0; j < GetHeight(); j++ ) { 241 v = &verts[ j * GetWidth() + i ]; 242 st = v->GetTexCoord(); 243 fp->WriteFloatString( " ( %f %f %f %f %f )", v->xyz[0] + origin[0], 244 v->xyz[1] + origin[1], v->xyz[2] + origin[2], st[0], st[1] ); 245 } 246 fp->WriteFloatString( " )\n" ); 247 } 248 fp->WriteFloatString( " )\n }\n}\n" ); 249 250 return true; 251 } 252 253 /* 254 =============== 255 idMapPatch::GetGeometryCRC 256 =============== 257 */ 258 unsigned int idMapPatch::GetGeometryCRC() const { 259 int i, j; 260 unsigned int crc; 261 262 crc = GetHorzSubdivisions() ^ GetVertSubdivisions(); 263 for ( i = 0; i < GetWidth(); i++ ) { 264 for ( j = 0; j < GetHeight(); j++ ) { 265 crc ^= FloatCRC( verts[j * GetWidth() + i].xyz.x ); 266 crc ^= FloatCRC( verts[j * GetWidth() + i].xyz.y ); 267 crc ^= FloatCRC( verts[j * GetWidth() + i].xyz.z ); 268 } 269 } 270 271 crc ^= StringCRC( GetMaterial() ); 272 273 return crc; 274 } 275 276 /* 277 ================= 278 idMapBrush::Parse 279 ================= 280 */ 281 idMapBrush *idMapBrush::Parse( idLexer &src, const idVec3 &origin, bool newFormat, float version ) { 282 int i; 283 idVec3 planepts[3]; 284 idToken token; 285 idList<idMapBrushSide*> sides; 286 idMapBrushSide *side; 287 idDict epairs; 288 289 if ( !src.ExpectTokenString( "{" ) ) { 290 return NULL; 291 } 292 293 do { 294 if ( !src.ReadToken( &token ) ) { 295 src.Error( "idMapBrush::Parse: unexpected EOF" ); 296 sides.DeleteContents( true ); 297 return NULL; 298 } 299 if ( token == "}" ) { 300 break; 301 } 302 303 // here we may have to jump over brush epairs ( only used in editor ) 304 do { 305 // if token is a brace 306 if ( token == "(" ) { 307 break; 308 } 309 // the token should be a key string for a key/value pair 310 if ( token.type != TT_STRING ) { 311 src.Error( "idMapBrush::Parse: unexpected %s, expected ( or epair key string", token.c_str() ); 312 sides.DeleteContents( true ); 313 return NULL; 314 } 315 316 idStr key = token; 317 318 if ( !src.ReadTokenOnLine( &token ) || token.type != TT_STRING ) { 319 src.Error( "idMapBrush::Parse: expected epair value string not found" ); 320 sides.DeleteContents( true ); 321 return NULL; 322 } 323 324 epairs.Set( key, token ); 325 326 // try to read the next key 327 if ( !src.ReadToken( &token ) ) { 328 src.Error( "idMapBrush::Parse: unexpected EOF" ); 329 sides.DeleteContents( true ); 330 return NULL; 331 } 332 } while (1); 333 334 src.UnreadToken( &token ); 335 336 side = new (TAG_IDLIB) idMapBrushSide(); 337 sides.Append(side); 338 339 if ( newFormat ) { 340 if ( !src.Parse1DMatrix( 4, side->plane.ToFloatPtr() ) ) { 341 src.Error( "idMapBrush::Parse: unable to read brush side plane definition" ); 342 sides.DeleteContents( true ); 343 return NULL; 344 } 345 } else { 346 // read the three point plane definition 347 if (!src.Parse1DMatrix( 3, planepts[0].ToFloatPtr() ) || 348 !src.Parse1DMatrix( 3, planepts[1].ToFloatPtr() ) || 349 !src.Parse1DMatrix( 3, planepts[2].ToFloatPtr() ) ) { 350 src.Error( "idMapBrush::Parse: unable to read brush side plane definition" ); 351 sides.DeleteContents( true ); 352 return NULL; 353 } 354 355 planepts[0] -= origin; 356 planepts[1] -= origin; 357 planepts[2] -= origin; 358 359 side->plane.FromPoints( planepts[0], planepts[1], planepts[2] ); 360 } 361 362 // read the texture matrix 363 // this is odd, because the texmat is 2D relative to default planar texture axis 364 if ( !src.Parse2DMatrix( 2, 3, side->texMat[0].ToFloatPtr() ) ) { 365 src.Error( "idMapBrush::Parse: unable to read brush side texture matrix" ); 366 sides.DeleteContents( true ); 367 return NULL; 368 } 369 side->origin = origin; 370 371 // read the material 372 if ( !src.ReadTokenOnLine( &token ) ) { 373 src.Error( "idMapBrush::Parse: unable to read brush side material" ); 374 sides.DeleteContents( true ); 375 return NULL; 376 } 377 378 // we had an implicit 'textures/' in the old format... 379 if ( version < 2.0f ) { 380 side->material = "textures/" + token; 381 } else { 382 side->material = token; 383 } 384 385 // Q2 allowed override of default flags and values, but we don't any more 386 if ( src.ReadTokenOnLine( &token ) ) { 387 if ( src.ReadTokenOnLine( &token ) ) { 388 if ( src.ReadTokenOnLine( &token ) ) { 389 } 390 } 391 } 392 } while( 1 ); 393 394 if ( !src.ExpectTokenString( "}" ) ) { 395 sides.DeleteContents( true ); 396 return NULL; 397 } 398 399 idMapBrush *brush = new (TAG_IDLIB) idMapBrush(); 400 for ( i = 0; i < sides.Num(); i++ ) { 401 brush->AddSide( sides[i] ); 402 } 403 404 brush->epairs = epairs; 405 406 return brush; 407 } 408 409 /* 410 ================= 411 idMapBrush::ParseQ3 412 ================= 413 */ 414 idMapBrush *idMapBrush::ParseQ3( idLexer &src, const idVec3 &origin ) { 415 int i, shift[2], rotate; 416 float scale[2]; 417 idVec3 planepts[3]; 418 idToken token; 419 idList<idMapBrushSide*> sides; 420 idMapBrushSide *side; 421 idDict epairs; 422 423 do { 424 if ( src.CheckTokenString( "}" ) ) { 425 break; 426 } 427 428 side = new (TAG_IDLIB) idMapBrushSide(); 429 sides.Append( side ); 430 431 // read the three point plane definition 432 if (!src.Parse1DMatrix( 3, planepts[0].ToFloatPtr() ) || 433 !src.Parse1DMatrix( 3, planepts[1].ToFloatPtr() ) || 434 !src.Parse1DMatrix( 3, planepts[2].ToFloatPtr() ) ) { 435 src.Error( "idMapBrush::ParseQ3: unable to read brush side plane definition" ); 436 sides.DeleteContents( true ); 437 return NULL; 438 } 439 440 planepts[0] -= origin; 441 planepts[1] -= origin; 442 planepts[2] -= origin; 443 444 side->plane.FromPoints( planepts[0], planepts[1], planepts[2] ); 445 446 // read the material 447 if ( !src.ReadTokenOnLine( &token ) ) { 448 src.Error( "idMapBrush::ParseQ3: unable to read brush side material" ); 449 sides.DeleteContents( true ); 450 return NULL; 451 } 452 453 // we have an implicit 'textures/' in the old format 454 side->material = "textures/" + token; 455 456 // read the texture shift, rotate and scale 457 shift[0] = src.ParseInt(); 458 shift[1] = src.ParseInt(); 459 rotate = src.ParseInt(); 460 scale[0] = src.ParseFloat(); 461 scale[1] = src.ParseFloat(); 462 side->texMat[0] = idVec3( 0.03125f, 0.0f, 0.0f ); 463 side->texMat[1] = idVec3( 0.0f, 0.03125f, 0.0f ); 464 side->origin = origin; 465 466 // Q2 allowed override of default flags and values, but we don't any more 467 if ( src.ReadTokenOnLine( &token ) ) { 468 if ( src.ReadTokenOnLine( &token ) ) { 469 if ( src.ReadTokenOnLine( &token ) ) { 470 } 471 } 472 } 473 } while( 1 ); 474 475 idMapBrush *brush = new (TAG_IDLIB) idMapBrush(); 476 for ( i = 0; i < sides.Num(); i++ ) { 477 brush->AddSide( sides[i] ); 478 } 479 480 brush->epairs = epairs; 481 482 return brush; 483 } 484 485 /* 486 ============ 487 idMapBrush::Write 488 ============ 489 */ 490 bool idMapBrush::Write( idFile *fp, int primitiveNum, const idVec3 &origin ) const { 491 int i; 492 idMapBrushSide *side; 493 494 fp->WriteFloatString( "// primitive %d\n{\n brushDef3\n {\n", primitiveNum ); 495 496 // write brush epairs 497 for ( i = 0; i < epairs.GetNumKeyVals(); i++) { 498 fp->WriteFloatString( " \"%s\" \"%s\"\n", epairs.GetKeyVal(i)->GetKey().c_str(), epairs.GetKeyVal(i)->GetValue().c_str()); 499 } 500 501 // write brush sides 502 for ( i = 0; i < GetNumSides(); i++ ) { 503 side = GetSide( i ); 504 fp->WriteFloatString( " ( %f %f %f %f ) ", side->plane[0], side->plane[1], side->plane[2], side->plane[3] ); 505 fp->WriteFloatString( "( ( %f %f %f ) ( %f %f %f ) ) \"%s\" 0 0 0\n", 506 side->texMat[0][0], side->texMat[0][1], side->texMat[0][2], 507 side->texMat[1][0], side->texMat[1][1], side->texMat[1][2], 508 side->material.c_str() ); 509 } 510 511 fp->WriteFloatString( " }\n}\n" ); 512 513 return true; 514 } 515 516 /* 517 =============== 518 idMapBrush::GetGeometryCRC 519 =============== 520 */ 521 unsigned int idMapBrush::GetGeometryCRC() const { 522 int i, j; 523 idMapBrushSide *mapSide; 524 unsigned int crc; 525 526 crc = 0; 527 for ( i = 0; i < GetNumSides(); i++ ) { 528 mapSide = GetSide(i); 529 for ( j = 0; j < 4; j++ ) { 530 crc ^= FloatCRC( mapSide->GetPlane()[j] ); 531 } 532 crc ^= StringCRC( mapSide->GetMaterial() ); 533 } 534 535 return crc; 536 } 537 538 /* 539 ================ 540 idMapEntity::Parse 541 ================ 542 */ 543 idMapEntity *idMapEntity::Parse( idLexer &src, bool worldSpawn, float version ) { 544 idToken token; 545 idMapEntity *mapEnt; 546 idMapPatch *mapPatch; 547 idMapBrush *mapBrush; 548 bool worldent; 549 idVec3 origin; 550 double v1, v2, v3; 551 552 if ( !src.ReadToken(&token) ) { 553 return NULL; 554 } 555 556 if ( token != "{" ) { 557 src.Error( "idMapEntity::Parse: { not found, found %s", token.c_str() ); 558 return NULL; 559 } 560 561 mapEnt = new (TAG_IDLIB) idMapEntity(); 562 563 if ( worldSpawn ) { 564 mapEnt->primitives.Resize( 1024, 256 ); 565 } 566 567 origin.Zero(); 568 worldent = false; 569 do { 570 if ( !src.ReadToken(&token) ) { 571 src.Error( "idMapEntity::Parse: EOF without closing brace" ); 572 return NULL; 573 } 574 if ( token == "}" ) { 575 break; 576 } 577 578 if ( token == "{" ) { 579 // parse a brush or patch 580 if ( !src.ReadToken( &token ) ) { 581 src.Error( "idMapEntity::Parse: unexpected EOF" ); 582 return NULL; 583 } 584 585 if ( worldent ) { 586 origin.Zero(); 587 } 588 589 // if is it a brush: brush, brushDef, brushDef2, brushDef3 590 if ( token.Icmpn( "brush", 5 ) == 0 ) { 591 mapBrush = idMapBrush::Parse( src, origin, ( !token.Icmp( "brushDef2" ) || !token.Icmp( "brushDef3" ) ), version ); 592 if ( !mapBrush ) { 593 return NULL; 594 } 595 mapEnt->AddPrimitive( mapBrush ); 596 } 597 // if is it a patch: patchDef2, patchDef3 598 else if ( token.Icmpn( "patch", 5 ) == 0 ) { 599 mapPatch = idMapPatch::Parse( src, origin, !token.Icmp( "patchDef3" ), version ); 600 if ( !mapPatch ) { 601 return NULL; 602 } 603 mapEnt->AddPrimitive( mapPatch ); 604 } 605 // assume it's a brush in Q3 or older style 606 else { 607 src.UnreadToken( &token ); 608 mapBrush = idMapBrush::ParseQ3( src, origin ); 609 if ( !mapBrush ) { 610 return NULL; 611 } 612 mapEnt->AddPrimitive( mapBrush ); 613 } 614 } else { 615 idStr key, value; 616 617 // parse a key / value pair 618 key = token; 619 src.ReadTokenOnLine( &token ); 620 value = token; 621 622 // strip trailing spaces that sometimes get accidentally 623 // added in the editor 624 value.StripTrailingWhitespace(); 625 key.StripTrailingWhitespace(); 626 627 mapEnt->epairs.Set( key, value ); 628 629 if ( !idStr::Icmp( key, "origin" ) ) { 630 // scanf into doubles, then assign, so it is idVec size independent 631 v1 = v2 = v3 = 0; 632 sscanf( value, "%lf %lf %lf", &v1, &v2, &v3 ); 633 origin.x = v1; 634 origin.y = v2; 635 origin.z = v3; 636 } 637 else if ( !idStr::Icmp( key, "classname" ) && !idStr::Icmp( value, "worldspawn" ) ) { 638 worldent = true; 639 } 640 } 641 } while( 1 ); 642 643 return mapEnt; 644 } 645 646 /* 647 ============ 648 idMapEntity::Write 649 ============ 650 */ 651 bool idMapEntity::Write( idFile *fp, int entityNum ) const { 652 int i; 653 idMapPrimitive *mapPrim; 654 idVec3 origin; 655 656 fp->WriteFloatString( "// entity %d\n{\n", entityNum ); 657 658 // write entity epairs 659 for ( i = 0; i < epairs.GetNumKeyVals(); i++) { 660 fp->WriteFloatString( "\"%s\" \"%s\"\n", epairs.GetKeyVal(i)->GetKey().c_str(), epairs.GetKeyVal(i)->GetValue().c_str()); 661 } 662 663 epairs.GetVector( "origin", "0 0 0", origin ); 664 665 // write pritimives 666 for ( i = 0; i < GetNumPrimitives(); i++ ) { 667 mapPrim = GetPrimitive( i ); 668 669 switch( mapPrim->GetType() ) { 670 case idMapPrimitive::TYPE_BRUSH: 671 static_cast<idMapBrush*>(mapPrim)->Write( fp, i, origin ); 672 break; 673 case idMapPrimitive::TYPE_PATCH: 674 static_cast<idMapPatch*>(mapPrim)->Write( fp, i, origin ); 675 break; 676 } 677 } 678 679 fp->WriteFloatString( "}\n" ); 680 681 return true; 682 } 683 684 /* 685 =============== 686 idMapEntity::RemovePrimitiveData 687 =============== 688 */ 689 void idMapEntity::RemovePrimitiveData() { 690 primitives.DeleteContents(true); 691 } 692 693 /* 694 =============== 695 idMapEntity::GetGeometryCRC 696 =============== 697 */ 698 unsigned int idMapEntity::GetGeometryCRC() const { 699 int i; 700 unsigned int crc; 701 idMapPrimitive *mapPrim; 702 703 crc = 0; 704 for ( i = 0; i < GetNumPrimitives(); i++ ) { 705 mapPrim = GetPrimitive( i ); 706 707 switch( mapPrim->GetType() ) { 708 case idMapPrimitive::TYPE_BRUSH: 709 crc ^= static_cast<idMapBrush*>(mapPrim)->GetGeometryCRC(); 710 break; 711 case idMapPrimitive::TYPE_PATCH: 712 crc ^= static_cast<idMapPatch*>(mapPrim)->GetGeometryCRC(); 713 break; 714 } 715 } 716 717 return crc; 718 } 719 720 /* 721 =============== 722 idMapFile::Parse 723 =============== 724 */ 725 bool idMapFile::Parse( const char *filename, bool ignoreRegion, bool osPath ) { 726 // no string concatenation for epairs and allow path names for materials 727 idLexer src( LEXFL_NOSTRINGCONCAT | LEXFL_NOSTRINGESCAPECHARS | LEXFL_ALLOWPATHNAMES ); 728 idToken token; 729 idStr fullName; 730 idMapEntity *mapEnt; 731 int i, j, k; 732 733 name = filename; 734 name.StripFileExtension(); 735 fullName = name; 736 hasPrimitiveData = false; 737 738 if ( !ignoreRegion ) { 739 // try loading a .reg file first 740 fullName.SetFileExtension( "reg" ); 741 src.LoadFile( fullName, osPath ); 742 } 743 744 if ( !src.IsLoaded() ) { 745 // now try a .map file 746 fullName.SetFileExtension( "map" ); 747 src.LoadFile( fullName, osPath ); 748 if ( !src.IsLoaded() ) { 749 // didn't get anything at all 750 return false; 751 } 752 } 753 754 version = OLD_MAP_VERSION; 755 fileTime = src.GetFileTime(); 756 entities.DeleteContents( true ); 757 758 if ( src.CheckTokenString( "Version" ) ) { 759 src.ReadTokenOnLine( &token ); 760 version = token.GetFloatValue(); 761 } 762 763 while( 1 ) { 764 mapEnt = idMapEntity::Parse( src, ( entities.Num() == 0 ), version ); 765 if ( !mapEnt ) { 766 break; 767 } 768 entities.Append( mapEnt ); 769 } 770 771 SetGeometryCRC(); 772 773 // if the map has a worldspawn 774 if ( entities.Num() ) { 775 776 // "removeEntities" "classname" can be set in the worldspawn to remove all entities with the given classname 777 const idKeyValue *removeEntities = entities[0]->epairs.MatchPrefix( "removeEntities", NULL ); 778 while ( removeEntities ) { 779 RemoveEntities( removeEntities->GetValue() ); 780 removeEntities = entities[0]->epairs.MatchPrefix( "removeEntities", removeEntities ); 781 } 782 783 // "overrideMaterial" "material" can be set in the worldspawn to reset all materials 784 idStr material; 785 if ( entities[0]->epairs.GetString( "overrideMaterial", "", material ) ) { 786 for ( i = 0; i < entities.Num(); i++ ) { 787 mapEnt = entities[i]; 788 for ( j = 0; j < mapEnt->GetNumPrimitives(); j++ ) { 789 idMapPrimitive *mapPrimitive = mapEnt->GetPrimitive( j ); 790 switch( mapPrimitive->GetType() ) { 791 case idMapPrimitive::TYPE_BRUSH: { 792 idMapBrush *mapBrush = static_cast<idMapBrush *>(mapPrimitive); 793 for ( k = 0; k < mapBrush->GetNumSides(); k++ ) { 794 mapBrush->GetSide( k )->SetMaterial( material ); 795 } 796 break; 797 } 798 case idMapPrimitive::TYPE_PATCH: { 799 static_cast<idMapPatch *>(mapPrimitive)->SetMaterial( material ); 800 break; 801 } 802 } 803 } 804 } 805 } 806 807 // force all entities to have a name key/value pair 808 if ( entities[0]->epairs.GetBool( "forceEntityNames" ) ) { 809 for ( i = 1; i < entities.Num(); i++ ) { 810 mapEnt = entities[i]; 811 if ( !mapEnt->epairs.FindKey( "name" ) ) { 812 mapEnt->epairs.Set( "name", va( "%s%d", mapEnt->epairs.GetString( "classname", "forcedName" ), i ) ); 813 } 814 } 815 } 816 817 // move the primitives of any func_group entities to the worldspawn 818 if ( entities[0]->epairs.GetBool( "moveFuncGroups" ) ) { 819 for ( i = 1; i < entities.Num(); i++ ) { 820 mapEnt = entities[i]; 821 if ( idStr::Icmp( mapEnt->epairs.GetString( "classname" ), "func_group" ) == 0 ) { 822 entities[0]->primitives.Append( mapEnt->primitives ); 823 mapEnt->primitives.Clear(); 824 } 825 } 826 } 827 } 828 829 hasPrimitiveData = true; 830 return true; 831 } 832 833 /* 834 ============ 835 idMapFile::Write 836 ============ 837 */ 838 bool idMapFile::Write( const char *fileName, const char *ext, bool fromBasePath ) { 839 int i; 840 idStr qpath; 841 idFile *fp; 842 843 qpath = fileName; 844 qpath.SetFileExtension( ext ); 845 846 idLib::common->Printf( "writing %s...\n", qpath.c_str() ); 847 848 if ( fromBasePath ) { 849 fp = idLib::fileSystem->OpenFileWrite( qpath, "fs_basepath" ); 850 } 851 else { 852 fp = idLib::fileSystem->OpenExplicitFileWrite( qpath ); 853 } 854 855 if ( !fp ) { 856 idLib::common->Warning( "Couldn't open %s\n", qpath.c_str() ); 857 return false; 858 } 859 860 fp->WriteFloatString( "Version %f\n", (float) CURRENT_MAP_VERSION ); 861 862 for ( i = 0; i < entities.Num(); i++ ) { 863 entities[i]->Write( fp, i ); 864 } 865 866 idLib::fileSystem->CloseFile( fp ); 867 868 return true; 869 } 870 871 /* 872 =============== 873 idMapFile::SetGeometryCRC 874 =============== 875 */ 876 void idMapFile::SetGeometryCRC() { 877 int i; 878 879 geometryCRC = 0; 880 for ( i = 0; i < entities.Num(); i++ ) { 881 geometryCRC ^= entities[i]->GetGeometryCRC(); 882 } 883 } 884 885 /* 886 =============== 887 idMapFile::AddEntity 888 =============== 889 */ 890 int idMapFile::AddEntity( idMapEntity *mapEnt ) { 891 int ret = entities.Append( mapEnt ); 892 return ret; 893 } 894 895 /* 896 =============== 897 idMapFile::FindEntity 898 =============== 899 */ 900 idMapEntity *idMapFile::FindEntity( const char *name ) { 901 for ( int i = 0; i < entities.Num(); i++ ) { 902 idMapEntity *ent = entities[i]; 903 if ( idStr::Icmp( ent->epairs.GetString( "name" ), name ) == 0 ) { 904 return ent; 905 } 906 } 907 return NULL; 908 } 909 910 /* 911 =============== 912 idMapFile::RemoveEntity 913 =============== 914 */ 915 void idMapFile::RemoveEntity( idMapEntity *mapEnt ) { 916 entities.Remove( mapEnt ); 917 delete mapEnt; 918 } 919 920 /* 921 =============== 922 idMapFile::RemoveEntity 923 =============== 924 */ 925 void idMapFile::RemoveEntities( const char *classname ) { 926 for ( int i = 0; i < entities.Num(); i++ ) { 927 idMapEntity *ent = entities[i]; 928 if ( idStr::Icmp( ent->epairs.GetString( "classname" ), classname ) == 0 ) { 929 delete entities[i]; 930 entities.RemoveIndex( i ); 931 i--; 932 } 933 } 934 } 935 936 /* 937 =============== 938 idMapFile::RemoveAllEntities 939 =============== 940 */ 941 void idMapFile::RemoveAllEntities() { 942 entities.DeleteContents( true ); 943 hasPrimitiveData = false; 944 } 945 946 /* 947 =============== 948 idMapFile::RemovePrimitiveData 949 =============== 950 */ 951 void idMapFile::RemovePrimitiveData() { 952 for ( int i = 0; i < entities.Num(); i++ ) { 953 idMapEntity *ent = entities[i]; 954 ent->RemovePrimitiveData(); 955 } 956 hasPrimitiveData = false; 957 } 958 959 /* 960 =============== 961 idMapFile::NeedsReload 962 =============== 963 */ 964 bool idMapFile::NeedsReload() { 965 if ( name.Length() ) { 966 ID_TIME_T time = FILE_NOT_FOUND_TIMESTAMP; 967 if ( idLib::fileSystem->ReadFile( name, NULL, &time ) > 0 ) { 968 return ( time > fileTime ); 969 } 970 } 971 return true; 972 }