SWF_ShapeParser.cpp (27781B)
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 #pragma hdrstop 29 #include "../idlib/precompiled.h" 30 #include "float.h" 31 32 #pragma warning( disable: 4189 ) // local variable is initialized but not referenced 33 34 /* 35 ======================== 36 idSWFShapeParser::ParseShape 37 ======================== 38 */ 39 void idSWFShapeParser::Parse( idSWFBitStream & bitstream, idSWFShape & shape, int recordType ) { 40 extendedCount = ( recordType > 1 ); 41 lineStyle2 = ( recordType == 4 ); 42 rgba = ( recordType >= 3 ); 43 morph = false; 44 45 bitstream.ReadRect( shape.startBounds ); 46 shape.endBounds = shape.startBounds; 47 48 if ( recordType == 4 ) { 49 swfRect_t edgeBounds; 50 bitstream.ReadRect( edgeBounds ); 51 bitstream.ReadU8(); // flags (that we ignore) 52 } 53 54 ReadFillStyle( bitstream ); 55 ParseShapes( bitstream, NULL, false ); 56 TriangulateSoup( shape ); 57 58 shape.lineDraws.SetNum( lineDraws.Num() ); 59 for ( int i = 0; i < lineDraws.Num(); i++ ) { 60 idSWFShapeDrawLine & ld = shape.lineDraws[i]; 61 swfSPDrawLine_t & spld = lineDraws[i]; 62 ld.style = spld.style; 63 ld.indices.SetNum( spld.edges.Num() * 3 ); 64 ld.indices.SetNum( 0 ); 65 for ( int e = 0; e < spld.edges.Num(); e++ ) { 66 int v0 = ld.startVerts.AddUnique( verts[ spld.edges[e].start.v0 ] ); 67 ld.indices.Append( v0 ); 68 ld.indices.Append( v0 ); 69 70 // Rather then tesselating curves at run time, we do them once here by inserting a vert every 10 units 71 // It may not wind up being 10 actual pixels when rendered because the shape may have scaling applied to it 72 if ( spld.edges[e].start.cp != 0xFFFF ) { 73 assert( spld.edges[e].end.cp != 0xFFFF ); 74 float length1 = ( verts[ spld.edges[e].start.v0 ] - verts[ spld.edges[e].start.v1 ] ).Length(); 75 float length2 = ( verts[ spld.edges[e].end.v0 ] - verts[ spld.edges[e].end.v1 ] ).Length(); 76 int numPoints = 1 + idMath::Ftoi( Max( length1, length2 ) / 10.0f ); 77 for ( int ti = 0; ti < numPoints; ti++ ) { 78 float t0 = ( ti + 1 ) / ( (float) numPoints + 1.0f ); 79 float t1 = ( 1.0f - t0 ); 80 float c1 = t1 * t1; 81 float c2 = t0 * t1 * 2.0f; 82 float c3 = t0 * t0; 83 84 idVec2 p1 = c1 * verts[ spld.edges[e].start.v0 ]; 85 p1 += c2 * verts[ spld.edges[e].start.cp ]; 86 p1 += c3 * verts[ spld.edges[e].start.v1 ]; 87 88 int v1 = ld.startVerts.AddUnique( p1 ); 89 ld.indices.Append( v1 ); 90 ld.indices.Append( v1 ); 91 ld.indices.Append( v1 ); 92 } 93 } 94 ld.indices.Append( ld.startVerts.AddUnique( verts[ spld.edges[e].start.v1 ] ) ); 95 } 96 } 97 } 98 99 /* 100 ======================== 101 idSWFShapeParser::ParseMorph 102 ======================== 103 */ 104 void idSWFShapeParser::ParseMorph( idSWFBitStream & bitstream, idSWFShape & shape ) { 105 extendedCount = true; 106 lineStyle2 = false; 107 rgba = true; 108 morph = true; 109 110 bitstream.ReadRect( shape.startBounds ); 111 bitstream.ReadRect( shape.endBounds ); 112 113 uint32 offset = bitstream.ReadU32(); 114 115 // offset is the byte offset from the current read position to the 'endShape' record 116 // we read the entire block into 'bitstream1' which moves the read pointer of 'bitstream' 117 // to the start of the 'endShape' record 118 119 idSWFBitStream bitstream1; 120 bitstream1.Load( (byte *)bitstream.ReadData( offset ), offset, false ); 121 122 ReadFillStyle( bitstream1 ); 123 ParseShapes( bitstream1, &bitstream, true ); 124 TriangulateSoup( shape ); 125 } 126 127 /* 128 ======================== 129 idSWFShapeParser::ParseFont 130 ======================== 131 */ 132 void idSWFShapeParser::ParseFont( idSWFBitStream & bitstream, idSWFFontGlyph & shape ) { 133 extendedCount = false; 134 lineStyle2 = false; 135 rgba = false; 136 morph = false; 137 138 fillDraws.SetNum( 1 ); 139 140 ParseShapes( bitstream, NULL, true ); 141 TriangulateSoup( shape ); 142 } 143 144 /* 145 ======================== 146 idSWFShapeParser::ParseShapes 147 ======================== 148 */ 149 void idSWFShapeParser::ParseShapes( idSWFBitStream & bitstream1, idSWFBitStream * bitstream2, bool swap ) { 150 int32 pen1X = 0; 151 int32 pen1Y = 0; 152 int32 pen2X = 0; 153 int32 pen2Y = 0; 154 155 uint8 fillStyle0 = 0; 156 uint8 fillStyle1 = 0; 157 uint8 lineStyle = 0; 158 159 uint16 baseFillStyle = 0; 160 uint16 baseLineStyle = 0; 161 162 uint8 numBits = bitstream1.ReadU8(); 163 uint8 numFillBits1 = numBits >> 4; 164 uint8 numLineBits1 = numBits & 0xF; 165 166 uint8 numFillBits2 = 0; 167 uint8 numLineBits2 = 0; 168 169 if ( bitstream2 ) { 170 numBits = bitstream2->ReadU8(); 171 numFillBits2 = numBits >> 4; 172 numLineBits2 = numBits & 0xF; 173 } 174 175 while ( true ) { 176 if ( !bitstream1.ReadBool() ) { 177 bool stateNewStyles = bitstream1.ReadBool(); 178 bool stateLineStyle = bitstream1.ReadBool(); 179 bool stateFillStyle1 = bitstream1.ReadBool(); 180 bool stateFillStyle0 = bitstream1.ReadBool(); 181 bool stateMoveTo = bitstream1.ReadBool(); 182 if ( ( stateNewStyles || stateLineStyle || stateFillStyle1 || stateFillStyle0 || stateMoveTo ) == false ) { 183 // end record 184 if ( bitstream2 ) { 185 uint8 flags = bitstream2->ReadU( 6 ); 186 if ( flags != 0 ) { 187 idLib::Warning( "idSWFShapeParser: morph stream 1 ends before 2" ); 188 break; 189 } 190 } 191 break; 192 } 193 if ( stateMoveTo ) { 194 uint8 moveBits = bitstream1.ReadU( 5 ); 195 pen1X = bitstream1.ReadS( moveBits ); 196 pen1Y = bitstream1.ReadS( moveBits ); 197 } 198 if ( stateFillStyle0 ) { 199 fillStyle0 = bitstream1.ReadU( numFillBits1 ); 200 } 201 if ( stateFillStyle1 ) { 202 fillStyle1 = bitstream1.ReadU( numFillBits1 ); 203 } 204 if ( stateLineStyle ) { 205 lineStyle = bitstream1.ReadU( numLineBits1 ); 206 } 207 if ( stateNewStyles ) { 208 baseFillStyle = fillDraws.Num(); 209 baseLineStyle = lineDraws.Num(); 210 ReadFillStyle( bitstream1 ); 211 numBits = bitstream1.ReadU8(); 212 numFillBits1 = numBits >> 4; 213 numLineBits1 = numBits & 0xF; 214 } 215 if ( bitstream2 ) { 216 bool isEdge = bitstream2->ReadBool(); 217 if ( isEdge ) { 218 idLib::Warning( "idSWFShapeParser: morph stream 1 defines style change, but stream 2 does not" ); 219 break; 220 } 221 bool stateNewStyles = bitstream2->ReadBool(); 222 bool stateLineStyle = bitstream2->ReadBool(); 223 bool stateFillStyle1 = bitstream2->ReadBool(); 224 bool stateFillStyle0 = bitstream2->ReadBool(); 225 bool stateMoveTo = bitstream2->ReadBool(); 226 if ( stateMoveTo ) { 227 uint8 moveBits = bitstream2->ReadU( 5 ); 228 pen2X = bitstream2->ReadS( moveBits ); 229 pen2Y = bitstream2->ReadS( moveBits ); 230 } 231 if ( stateFillStyle0 ) { 232 if ( bitstream2->ReadU( numFillBits2 ) != fillStyle0 ) { 233 idLib::Warning( "idSWFShapeParser: morph stream 2 defined a different fillStyle0 from stream 1" ); 234 break; 235 } 236 } 237 if ( stateFillStyle1 ) { 238 if ( bitstream2->ReadU( numFillBits2 ) != fillStyle1 ) { 239 idLib::Warning( "idSWFShapeParser: morph stream 2 defined a different fillStyle1 from stream 1" ); 240 break; 241 } 242 } 243 if ( stateLineStyle ) { 244 if ( bitstream2->ReadU( numLineBits2 ) != lineStyle ) { 245 idLib::Warning( "idSWFShapeParser: morph stream 2 defined a different lineStyle from stream 1" ); 246 break; 247 } 248 } 249 if ( stateNewStyles ) { 250 idLib::Warning( "idSWFShapeParser: morph stream 2 defines new styles" ); 251 break; 252 } 253 } 254 } else { 255 swfSPMorphEdge_t morphEdge; 256 257 ParseEdge( bitstream1, pen1X, pen1Y, morphEdge.start ); 258 if ( bitstream2 ) { 259 bool isEdge = bitstream2->ReadBool(); 260 if ( !isEdge ) { 261 idLib::Warning( "idSWFShapeParser: morph stream 1 defines an edge, but stream 2 does not" ); 262 break; 263 } 264 ParseEdge( *bitstream2, pen2X, pen2Y, morphEdge.end ); 265 } else { 266 morphEdge.end = morphEdge.start; 267 } 268 269 // one edge may be a straight edge, and the other may be a curve 270 // in this case, we turn the straight edge into a curve by adding 271 // a control point in the middle of the line 272 if ( morphEdge.start.cp != 0xFFFF ) { 273 if ( morphEdge.end.cp == 0xFFFF ) { 274 const idVec2 & v0 = verts[ morphEdge.end.v0 ]; 275 const idVec2 & v1 = verts[ morphEdge.end.v1 ]; 276 morphEdge.end.cp = verts.AddUnique( ( v0 + v1 ) * 0.5f ); 277 } 278 } else { 279 if ( morphEdge.end.cp != 0xFFFF ) { 280 const idVec2 & v0 = verts[ morphEdge.start.v0 ]; 281 const idVec2 & v1 = verts[ morphEdge.start.v1 ]; 282 morphEdge.start.cp = verts.AddUnique( ( v0 + v1 ) * 0.5f ); 283 } 284 } 285 286 if ( lineStyle != 0 ) { 287 lineDraws[ baseLineStyle + lineStyle - 1 ].edges.Append( morphEdge ); 288 } 289 if ( swap ) { 290 SwapValues( morphEdge.start.v0, morphEdge.start.v1 ); 291 SwapValues( morphEdge.end.v0, morphEdge.end.v1 ); 292 } 293 if ( fillStyle1 != 0 ) { 294 fillDraws[ baseFillStyle + fillStyle1 - 1 ].edges.Append( morphEdge ); 295 } 296 if ( fillStyle0 != 0 ) { 297 // for fill style 0, we need to reverse the winding 298 swfSPMorphEdge_t swapped = morphEdge; 299 SwapValues( swapped.start.v0, swapped.start.v1 ); 300 SwapValues( swapped.end.v0, swapped.end.v1 ); 301 fillDraws[ baseFillStyle + fillStyle0 - 1 ].edges.Append( swapped ); 302 } 303 } 304 } 305 } 306 307 /* 308 ======================== 309 idSWFShapeParser::ParseEdge 310 ======================== 311 */ 312 void idSWFShapeParser::ParseEdge( idSWFBitStream & bitstream, int32 & penX, int32 & penY, swfSPEdge_t & edge ) { 313 bool straight = bitstream.ReadBool(); 314 uint8 numBits = bitstream.ReadU( 4 ) + 2; 315 316 edge.v0 = verts.AddUnique( idVec2( SWFTWIP( penX ), SWFTWIP( penY ) ) ); 317 318 if ( straight ) { 319 edge.cp = 0xFFFF; 320 if ( bitstream.ReadBool() ) { 321 penX += bitstream.ReadS( numBits ); 322 penY += bitstream.ReadS( numBits ); 323 } else { 324 if ( bitstream.ReadBool() ) { 325 penY += bitstream.ReadS( numBits ); 326 } else { 327 penX += bitstream.ReadS( numBits ); 328 } 329 } 330 } else { 331 penX += bitstream.ReadS( numBits ); 332 penY += bitstream.ReadS( numBits ); 333 edge.cp = verts.AddUnique( idVec2( SWFTWIP( penX ), SWFTWIP( penY ) ) ); 334 penX += bitstream.ReadS( numBits ); 335 penY += bitstream.ReadS( numBits ); 336 } 337 338 edge.v1 = verts.AddUnique( idVec2( SWFTWIP( penX ), SWFTWIP( penY ) ) ); 339 } 340 341 /* 342 ======================== 343 idSWFShapeParser::MakeLoops 344 ======================== 345 */ 346 void idSWFShapeParser::MakeLoops() { 347 348 // At this point, each fill style has an edge soup associated with it 349 // We want to turn this soup into loops of connected verts 350 351 for ( int i = 0; i < fillDraws.Num(); i++ ) { 352 swfSPDrawFill_t & fill = fillDraws[i]; 353 354 // first remove degenerate edges 355 for ( int e = 0; e < fill.edges.Num(); e++ ) { 356 if ( ( fill.edges[e].start.v0 == fill.edges[e].start.v1 ) || ( fill.edges[e].end.v0 == fill.edges[e].end.v1 ) ) { 357 fill.edges.RemoveIndexFast( e ); 358 e--; 359 } 360 } 361 362 idList< int > unusedEdges; 363 unusedEdges.SetNum( fill.edges.Num() ); 364 for ( int e = 0; e < fill.edges.Num(); e++ ) { 365 unusedEdges[e] = e; 366 } 367 while ( unusedEdges.Num() > 0 ) { 368 swfSPLineLoop_t & loop = fill.loops.Alloc(); 369 loop.hole = false; 370 371 int e1 = unusedEdges[ unusedEdges.Num() - 1 ]; 372 unusedEdges.SetNum( unusedEdges.Num() - 1 ); 373 374 while ( true ) { 375 loop.vindex1.Append( fill.edges[e1].start.v0 ); 376 loop.vindex2.Append( fill.edges[e1].end.v0 ); 377 378 // Rather then tesselating curves at run time, we do them once here by inserting a vert every 10 units 379 // It may not wind up being 10 actual pixels when rendered because the shape may have scaling applied to it 380 if ( fill.edges[e1].start.cp != 0xFFFF ) { 381 assert( fill.edges[e1].end.cp != 0xFFFF ); 382 float length1 = ( verts[ fill.edges[e1].start.v0 ] - verts[ fill.edges[e1].start.v1 ] ).Length(); 383 float length2 = ( verts[ fill.edges[e1].end.v0 ] - verts[ fill.edges[e1].end.v1 ] ).Length(); 384 int numPoints = 1 + idMath::Ftoi( Max( length1, length2 ) / 10.0f ); 385 for ( int ti = 0; ti < numPoints; ti++ ) { 386 float t0 = ( ti + 1 ) / ( (float) numPoints + 1.0f ); 387 float t1 = ( 1.0f - t0 ); 388 float c1 = t1 * t1; 389 float c2 = t0 * t1 * 2.0f; 390 float c3 = t0 * t0; 391 392 idVec2 p1 = c1 * verts[ fill.edges[e1].start.v0 ]; 393 p1 += c2 * verts[ fill.edges[e1].start.cp ]; 394 p1 += c3 * verts[ fill.edges[e1].start.v1 ]; 395 396 idVec2 p2 = c1 * verts[ fill.edges[e1].end.v0 ]; 397 p2 += c2 * verts[ fill.edges[e1].end.cp ]; 398 p2 += c3 * verts[ fill.edges[e1].end.v1 ]; 399 400 loop.vindex1.Append( verts.AddUnique( p1 ) ); 401 loop.vindex2.Append( verts.AddUnique( p2 ) ); 402 } 403 } 404 405 const swfSPEdge_t & edge1 = fill.edges[e1].start; 406 407 float bestNormal = FLT_MAX; 408 int beste = -1; 409 for ( int e = 0; e < unusedEdges.Num(); e++ ) { 410 int e2 = unusedEdges[e]; 411 const swfSPEdge_t & edge2 = fill.edges[e2].start; 412 if ( edge1.v1 != edge2.v0 ) { 413 continue; 414 } 415 416 assert( edge1.v0 != edge2.v0 ); 417 assert( edge1.v1 != edge2.v1 ); 418 419 const idVec2 & v1 = verts[ edge1.v0 ]; 420 const idVec2 & v2 = verts[ edge1.v1 ]; // == edge2.v0 421 const idVec2 & v3 = verts[ edge2.v1 ]; 422 idVec2 a = v1 - v2; 423 idVec2 b = v3 - v2; 424 425 float normal = ( a.x * b.y - a.y * b.x ); 426 if ( normal < bestNormal ) { 427 bestNormal = normal; 428 beste = e; 429 } else { 430 assert( beste != -1 ); 431 } 432 } 433 if ( beste < 0 ) { 434 // no more edges connect to this one 435 break; 436 } 437 e1 = unusedEdges[beste]; 438 unusedEdges.RemoveIndexFast( beste ); 439 } 440 if ( loop.vindex1.Num() < 3 ) { 441 idLib::Warning( "idSWFShapeParser: loop with < 3 verts" ); 442 fill.loops.SetNum( fill.loops.Num() - 1 ); 443 continue; 444 } 445 // Use the left most vert to determine if it's a hole or not 446 float leftMostX = FLT_MAX; 447 int leftMostIndex = 0; 448 for ( int j = 0; j < loop.vindex1.Num(); j++ ) { 449 idVec2 & v = verts[ loop.vindex1[j] ]; 450 if ( v.x < leftMostX ) { 451 leftMostIndex = j; 452 leftMostX = v.x; 453 } 454 } 455 const idVec2 & v1 = verts[ loop.vindex1[(loop.vindex1.Num() + leftMostIndex - 1) % loop.vindex1.Num()] ]; 456 const idVec2 & v2 = verts[ loop.vindex1[leftMostIndex] ]; 457 const idVec2 & v3 = verts[ loop.vindex1[(leftMostIndex+1) % loop.vindex1.Num()] ]; 458 idVec2 a = v1 - v2; 459 idVec2 b = v3 - v2; 460 float normal = ( a.x * b.y - a.y * b.x ); 461 loop.hole = ( normal > 0.0f ); 462 } 463 464 // now we have a series of loops, which define either shapes or holes 465 // we want to merge the holes into the shapes by inserting edges 466 // this assumes shapes are either completely contained or not 467 // we start merging holes starting on the right so nested holes work 468 while ( true ) { 469 int hole = -1; 470 int holeVert = -1; 471 float rightMostX = -1e10f; 472 for ( int j = 0; j < fill.loops.Num(); j++ ) { 473 swfSPLineLoop_t & loop = fill.loops[j]; 474 if ( !loop.hole ) { 475 continue; 476 } 477 for ( int v = 0; v < loop.vindex1.Num(); v++ ) { 478 if ( verts[ loop.vindex1[v] ].x > rightMostX ) { 479 hole = j; 480 holeVert = v; 481 rightMostX = verts[ loop.vindex1[v] ].x; 482 } 483 } 484 } 485 if ( hole == -1 ) { 486 break; 487 } 488 swfSPLineLoop_t & loopHole = fill.loops[ hole ]; 489 const idVec2 & holePoint = verts[ loopHole.vindex1[ holeVert ] ]; 490 491 int shape = -1; 492 for ( int j = 0; j < fill.loops.Num(); j++ ) { 493 swfSPLineLoop_t & loop = fill.loops[j]; 494 if ( loop.hole ) { 495 continue; 496 } 497 bool inside = false; 498 for ( int k = 0; k < loop.vindex1.Num(); k++ ) { 499 const idVec2 & v1 = verts[ loop.vindex1[k] ]; 500 const idVec2 & v2 = verts[ loop.vindex1[(k + 1) % loop.vindex1.Num()] ]; 501 if ( v1.x < holePoint.x && v2.x < holePoint.x ) { 502 continue; // both on the left of the holePoint 503 } 504 if ( ( v1.y < holePoint.y ) == ( v2.y < holePoint.y ) ) { 505 continue; // both on the same side of the horizon 506 } 507 assert( v1 != holePoint ); 508 assert( v2 != holePoint ); 509 inside = !inside; 510 } 511 if ( inside ) { 512 shape = j; 513 break; 514 } 515 } 516 if ( shape == -1 ) { 517 idLib::Warning( "idSWFShapeParser: Hole not in a shape" ); 518 fill.loops.RemoveIndexFast( hole ); 519 continue; 520 } 521 swfSPLineLoop_t & loopShape = fill.loops[ shape ]; 522 523 // now that we have a hole and the shape it's inside, merge the two together 524 525 // find the nearest vert that's on the right side of holePoint 526 float bestDist = 1e10f; 527 int shapeVert = -1; 528 for ( int j = 0; j < loopShape.vindex1.Num(); j++ ) { 529 const idVec2 & v1 = verts[ loopShape.vindex1[j] ]; 530 if ( v1.x < holePoint.x ) { 531 continue; // on the left of the holePoint 532 } 533 float dist = ( v1 - holePoint ).Length(); 534 if ( dist < bestDist ) { 535 shapeVert = j; 536 bestDist = dist; 537 } 538 } 539 540 // connect holeVert to shapeVert 541 idList< uint16 > vindex; 542 vindex.SetNum( loopShape.vindex1.Num() + loopHole.vindex1.Num() + 1 ); 543 vindex.SetNum( 0 ); 544 for ( int j = 0; j <= shapeVert; j++ ) { 545 vindex.Append( loopShape.vindex1[j] ); 546 } 547 for ( int j = holeVert; j < loopHole.vindex1.Num(); j++ ) { 548 vindex.Append( loopHole.vindex1[j] ); 549 } 550 for ( int j = 0; j <= holeVert; j++ ) { 551 vindex.Append( loopHole.vindex1[j] ); 552 } 553 for ( int j = shapeVert; j < loopShape.vindex1.Num(); j++ ) { 554 vindex.Append( loopShape.vindex1[j] ); 555 } 556 loopShape.vindex1 = vindex; 557 558 vindex.Clear(); 559 for ( int j = 0; j <= shapeVert; j++ ) { 560 vindex.Append( loopShape.vindex2[j] ); 561 } 562 for ( int j = holeVert; j < loopHole.vindex2.Num(); j++ ) { 563 vindex.Append( loopHole.vindex2[j] ); 564 } 565 for ( int j = 0; j <= holeVert; j++ ) { 566 vindex.Append( loopHole.vindex2[j] ); 567 } 568 for ( int j = shapeVert; j < loopShape.vindex2.Num(); j++ ) { 569 vindex.Append( loopShape.vindex2[j] ); 570 } 571 loopShape.vindex2 = vindex; 572 573 fill.loops.RemoveIndexFast( hole ); 574 } 575 } 576 } 577 578 /* 579 ======================== 580 idSWFShapeParser::TriangulateSoup 581 ======================== 582 */ 583 void idSWFShapeParser::TriangulateSoup( idSWFShape & shape ) { 584 585 MakeLoops(); 586 587 // Now turn the (potentially) concave line loops into triangles by using ear clipping 588 589 shape.fillDraws.SetNum( fillDraws.Num() ); 590 for ( int i = 0; i < fillDraws.Num(); i++ ) { 591 swfSPDrawFill_t & spDrawFill = fillDraws[i]; 592 idSWFShapeDrawFill & drawFill = shape.fillDraws[i]; 593 594 swfFillStyle_t & style = spDrawFill.style; 595 drawFill.style = spDrawFill.style; 596 597 for ( int j = 0; j < spDrawFill.loops.Num(); j++ ) { 598 swfSPLineLoop_t & loop = spDrawFill.loops[j]; 599 int numVerts = loop.vindex1.Num(); 600 for ( int k = 0; k < numVerts - 2; k++ ) { 601 int v1 = FindEarVert( loop ); 602 if ( v1 == -1 ) { 603 idLib::Warning( "idSWFShapeParser: could not find an ear vert" ); 604 break; 605 } 606 int num = loop.vindex1.Num(); 607 int v2 = ( v1 + 1 ) % num; 608 int v3 = ( v1 + 2 ) % num; 609 610 AddUniqueVert( drawFill, verts[ loop.vindex1[ v1 ] ], verts[ loop.vindex2[ v1 ] ] ); 611 AddUniqueVert( drawFill, verts[ loop.vindex1[ v2 ] ], verts[ loop.vindex2[ v2 ] ] ); 612 AddUniqueVert( drawFill, verts[ loop.vindex1[ v3 ] ], verts[ loop.vindex2[ v3 ] ] ); 613 614 loop.vindex1.RemoveIndex( v2 ); 615 loop.vindex2.RemoveIndex( v2 ); 616 } 617 } 618 } 619 } 620 621 /* 622 ======================== 623 idSWFShapeParser::TriangulateSoup 624 ======================== 625 */ 626 void idSWFShapeParser::TriangulateSoup( idSWFFontGlyph & shape ) { 627 628 MakeLoops(); 629 630 // Now turn the (potentially) concave line loops into triangles by using ear clipping 631 632 assert( fillDraws.Num() == 1 ); 633 swfSPDrawFill_t & spDrawFill = fillDraws[0]; 634 for ( int j = 0; j < spDrawFill.loops.Num(); j++ ) { 635 swfSPLineLoop_t & loop = spDrawFill.loops[j]; 636 int numVerts = loop.vindex1.Num(); 637 for ( int k = 0; k < numVerts - 2; k++ ) { 638 int v1 = FindEarVert( loop ); 639 if ( v1 == -1 ) { 640 idLib::Warning( "idSWFShapeParser: could not find an ear vert" ); 641 break; 642 } 643 int num = loop.vindex1.Num(); 644 int v2 = ( v1 + 1 ) % num; 645 int v3 = ( v1 + 2 ) % num; 646 647 shape.indices.Append( shape.verts.AddUnique( verts[ loop.vindex1[ v1 ] ] ) ); 648 shape.indices.Append( shape.verts.AddUnique( verts[ loop.vindex1[ v2 ] ] ) ); 649 shape.indices.Append( shape.verts.AddUnique( verts[ loop.vindex1[ v3 ] ] ) ); 650 651 loop.vindex1.RemoveIndex( v2 ); 652 loop.vindex2.RemoveIndex( v2 ); 653 } 654 } 655 } 656 657 struct earVert_t { 658 int i1; 659 int i2; 660 int i3; 661 float cross; 662 }; 663 class idSort_Ears : public idSort_Quick< earVert_t, idSort_Ears > { 664 public: 665 int Compare( const earVert_t & a, const earVert_t & b ) const { 666 if ( a.cross < b.cross ) { 667 return -1; 668 } else if ( a.cross > b.cross ) { 669 return 1; 670 } 671 return 0; 672 } 673 }; 674 675 /* 676 ======================== 677 idSWFShapeParser::FindEarVert 678 ======================== 679 */ 680 int idSWFShapeParser::FindEarVert( const swfSPLineLoop_t & loop ) { 681 assert( loop.vindex1.Num() == loop.vindex2.Num() ); 682 int num = loop.vindex1.Num(); 683 684 idList<earVert_t> ears; 685 ears.SetNum( num ); 686 687 for ( int i1 = 0; i1 < num; i1++ ) { 688 int i2 = ( i1 + 1 ) % num; 689 int i3 = ( i1 + 2 ) % num; 690 const idVec2 & v1s = verts[ loop.vindex1[ i1 ] ]; 691 const idVec2 & v2s = verts[ loop.vindex1[ i2 ] ]; 692 const idVec2 & v3s = verts[ loop.vindex1[ i3 ] ]; 693 694 idVec2 a = v1s - v2s; 695 idVec2 b = v2s - v3s; 696 697 ears[i1].cross = a.x * b.y - a.y * b.x; 698 ears[i1].i1 = i1; 699 ears[i1].i2 = i2; 700 ears[i1].i3 = i3; 701 } 702 ears.SortWithTemplate( idSort_Ears() ); 703 704 for ( int i = 0; i < ears.Num(); i++ ) { 705 if ( ears[i].cross < 0.0f ) { 706 continue; 707 } 708 int i1 = ears[i].i1; 709 int i2 = ears[i].i2; 710 int i3 = ears[i].i3; 711 712 const idVec2 & v1s = verts[ loop.vindex1[ i1 ] ]; 713 const idVec2 & v2s = verts[ loop.vindex1[ i2 ] ]; 714 const idVec2 & v3s = verts[ loop.vindex1[ i3 ] ]; 715 716 const idVec2 & v1e = verts[ loop.vindex2[ i1 ] ]; 717 const idVec2 & v2e = verts[ loop.vindex2[ i2 ] ]; 718 const idVec2 & v3e = verts[ loop.vindex2[ i3 ] ]; 719 720 idMat3 edgeEquations1; 721 edgeEquations1[0].Set( v1s.x, v1s.y, 1.0f ); 722 edgeEquations1[1].Set( v2s.x, v2s.y, 1.0f ); 723 edgeEquations1[2].Set( v3s.x, v3s.y, 1.0f ); 724 725 idMat3 edgeEquations2; 726 edgeEquations2[0].Set( v1e.x, v1e.y, 1.0f ); 727 edgeEquations2[1].Set( v2e.x, v2e.y, 1.0f ); 728 edgeEquations2[2].Set( v3e.x, v3e.y, 1.0f ); 729 730 edgeEquations1.InverseSelf(); 731 edgeEquations2.InverseSelf(); 732 733 bool isEar = true; 734 for ( int j = 0; j < num; j++ ) { 735 if ( j == i1 || j == i2 || j == i3 ) { 736 continue; 737 } 738 739 idVec3 p1; 740 p1.ToVec2() = verts[ loop.vindex1[j] ]; 741 p1.z = 1.0f; 742 743 idVec3 signs1 = p1 * edgeEquations1; 744 745 bool b1x = signs1.x > 0; 746 bool b1y = signs1.y > 0; 747 bool b1z = signs1.z > 0; 748 if ( b1x == b1y && b1x == b1z ) { 749 // point inside 750 isEar = false; 751 break; 752 } 753 754 idVec3 p2; 755 p2.ToVec2() = verts[ loop.vindex2[j] ]; 756 p2.z = 1.0f; 757 758 idVec3 signs2 = p2 * edgeEquations2; 759 760 bool b2x = signs2.x > 0; 761 bool b2y = signs2.y > 0; 762 bool b2z = signs2.z > 0; 763 if ( b2x == b2y && b2x == b2z ) { 764 // point inside 765 isEar = false; 766 break; 767 } 768 } 769 if ( isEar ) { 770 return i1; 771 } 772 } 773 return -1; 774 } 775 776 /* 777 ======================== 778 idSWFShapeParser::AddUniqueVert 779 ======================== 780 */ 781 void idSWFShapeParser::AddUniqueVert( idSWFShapeDrawFill & drawFill, const idVec2 & start, const idVec2 & end ) { 782 if ( morph ) { 783 for ( int i = 0; i < drawFill.startVerts.Num(); i++ ) { 784 if ( drawFill.startVerts[i] == start && drawFill.endVerts[i] == end ) { 785 drawFill.indices.Append( i ); 786 return; 787 } 788 } 789 int index1 = drawFill.startVerts.Append( start ); 790 int index2 = drawFill.endVerts.Append( end ); 791 assert( index1 == index2 ); 792 793 drawFill.indices.Append( index1 ); 794 } else { 795 drawFill.indices.Append( drawFill.startVerts.AddUnique( start ) ); 796 } 797 } 798 799 /* 800 ======================== 801 idSWFShapeParser::ReadFillStyle 802 ======================== 803 */ 804 void idSWFShapeParser::ReadFillStyle( idSWFBitStream & bitstream ) { 805 uint16 fillStyleCount = bitstream.ReadU8(); 806 if ( extendedCount && fillStyleCount == 0xFF ) { 807 fillStyleCount = bitstream.ReadU16(); 808 } 809 810 for ( int i = 0; i < fillStyleCount; i++ ) { 811 uint8 fillStyleType = bitstream.ReadU8(); 812 813 swfFillStyle_t & fillStyle = fillDraws.Alloc().style; 814 fillStyle.type = fillStyleType >> 4; 815 fillStyle.subType = fillStyleType & 0xF; 816 817 if ( fillStyle.type == 0 ) { 818 if ( morph ) { 819 bitstream.ReadColorRGBA( fillStyle.startColor ); 820 bitstream.ReadColorRGBA( fillStyle.endColor ); 821 } else { 822 if ( rgba ) { 823 bitstream.ReadColorRGBA( fillStyle.startColor ); 824 } else { 825 bitstream.ReadColorRGB( fillStyle.startColor ); 826 } 827 fillStyle.endColor = fillStyle.startColor; 828 } 829 } else if ( fillStyle.type == 1 ) { 830 bitstream.ReadMatrix( fillStyle.startMatrix ); 831 if ( morph ) { 832 bitstream.ReadMatrix( fillStyle.endMatrix ); 833 bitstream.ReadMorphGradient( fillStyle.gradient ); 834 } else { 835 fillStyle.endMatrix = fillStyle.startMatrix; 836 bitstream.ReadGradient( fillStyle.gradient, rgba ); 837 } 838 if ( fillStyle.subType == 3 ) { 839 assert( morph == false ); // focal gradients aren't allowed in morph shapes 840 fillStyle.focalPoint = bitstream.ReadFixed8(); 841 } 842 } else if ( fillStyle.type == 4 ) { 843 fillStyle.bitmapID = bitstream.ReadU16(); 844 bitstream.ReadMatrix( fillStyle.startMatrix ); 845 if ( morph ) { 846 bitstream.ReadMatrix( fillStyle.endMatrix ); 847 } else { 848 fillStyle.endMatrix = fillStyle.startMatrix; 849 } 850 } 851 } 852 853 uint16 lineStyleCount = bitstream.ReadU8(); 854 if ( extendedCount && lineStyleCount == 0xFF ) { 855 lineStyleCount = bitstream.ReadU16(); 856 } 857 858 lineDraws.SetNum( lineDraws.Num() + lineStyleCount ); 859 lineDraws.SetNum( 0 ); 860 for ( int i = 0; i < lineStyleCount; i++ ) { 861 swfLineStyle_t & lineStyle = lineDraws.Alloc().style; 862 lineStyle.startWidth = bitstream.ReadU16(); 863 if ( lineStyle2 ) { 864 lineStyle.endWidth = lineStyle.startWidth; 865 866 uint8 startCapStyle = bitstream.ReadU( 2 ); 867 uint8 joinStyle = bitstream.ReadU( 2 ); 868 bool hasFillFlag = bitstream.ReadBool(); 869 bool noHScaleFlag = bitstream.ReadBool(); 870 bool noVScaleFlag = bitstream.ReadBool(); 871 bool pixelHintingFlag = bitstream.ReadBool(); 872 uint8 reserved = bitstream.ReadU( 5 ); 873 bool noClose = bitstream.ReadBool(); 874 uint8 endCapStyle = bitstream.ReadU( 2 ); 875 if ( joinStyle == 2 ) { 876 uint16 miterLimitFactor = bitstream.ReadU16(); 877 } 878 if ( hasFillFlag ) { 879 // FIXME: read fill style 880 idLib::Warning( "idSWFShapeParser: Ignoring hasFillFlag" ); 881 } else { 882 bitstream.ReadColorRGBA( lineStyle.startColor ); 883 lineStyle.endColor = lineStyle.startColor; 884 } 885 } else { 886 if ( morph ) { 887 lineStyle.endWidth = bitstream.ReadU16(); 888 } else { 889 lineStyle.endWidth = lineStyle.startWidth; 890 } 891 if ( rgba ) { 892 bitstream.ReadColorRGBA( lineStyle.startColor ); 893 } else { 894 bitstream.ReadColorRGB( lineStyle.startColor ); 895 } 896 if ( morph ) { 897 bitstream.ReadColorRGBA( lineStyle.endColor ); 898 } else { 899 lineStyle.endColor = lineStyle.startColor; 900 } 901 } 902 } 903 }