DOOM-3-BFG

DOOM 3 BFG Edition
Log | Files | Refs

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 }