DOOM-3-BFG

DOOM 3 BFG Edition
Log | Files | Refs

DeclAF.cpp (46763B)


      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 "../idlib/precompiled.h"
     30 #pragma hdrstop
     31 
     32 /*
     33 ===============================================================================
     34 
     35 	idDeclAF
     36 
     37 ===============================================================================
     38 */
     39 
     40 /*
     41 ================
     42 idAFVector::idAFVector
     43 ================
     44 */
     45 idAFVector::idAFVector() {
     46 	type = VEC_COORDS;
     47 	vec.Zero();
     48 	negate = false;
     49 }
     50 
     51 /*
     52 ================
     53 idAFVector::Parse
     54 ================
     55 */
     56 bool idAFVector::Parse( idLexer &src ) {
     57 	idToken token;
     58 
     59 	if ( !src.ReadToken( &token ) ) {
     60 		return false;
     61 	}
     62 
     63 	if ( token == "-" ) {
     64 		negate = true;
     65 		if ( !src.ReadToken( &token ) ) {
     66 			return false;
     67 		}
     68 	}
     69 	else {
     70 		negate = false;
     71 	}
     72 
     73 	if ( token == "(" ) {
     74 		type = idAFVector::VEC_COORDS;
     75 		vec.x = src.ParseFloat();
     76 		src.ExpectTokenString( "," );
     77 		vec.y = src.ParseFloat();
     78 		src.ExpectTokenString( "," );
     79 		vec.z = src.ParseFloat();
     80 		src.ExpectTokenString( ")" );
     81 	}
     82 	else if ( token == "joint" ) {
     83 		type = idAFVector::VEC_JOINT;
     84 		src.ExpectTokenString( "(" );
     85 		src.ReadToken( &token );
     86 		joint1 = token;
     87 		src.ExpectTokenString( ")" );
     88 	}
     89 	else if ( token == "bonecenter" ) {
     90 		type = idAFVector::VEC_BONECENTER;
     91 		src.ExpectTokenString( "(" );
     92 		src.ReadToken( &token );
     93 		joint1 = token;
     94 		src.ExpectTokenString( "," );
     95 		src.ReadToken( &token );
     96 		joint2 = token;
     97 		src.ExpectTokenString( ")" );
     98 	}
     99 	else if ( token == "bonedir" ) {
    100 		type = idAFVector::VEC_BONEDIR;
    101 		src.ExpectTokenString( "(" );
    102 		src.ReadToken( &token );
    103 		joint1 = token;
    104 		src.ExpectTokenString( "," );
    105 		src.ReadToken( &token );
    106 		joint2 = token;
    107 		src.ExpectTokenString( ")" );
    108 	}
    109 	else {
    110 		src.Error( "unknown token %s in vector", token.c_str() );
    111 		return false;
    112 	}
    113 
    114 	return true;
    115 }
    116 
    117 /*
    118 ================
    119 idAFVector::Finish
    120 ================
    121 */
    122 bool idAFVector::Finish( const char *fileName, const getJointTransform_t GetJointTransform, const idJointMat *frame, void *model ) const {
    123 	idMat3 axis;
    124 	idVec3 start, end;
    125 
    126 	switch( type ) {
    127 		case idAFVector::VEC_COORDS: {
    128 			break;
    129 		}
    130 		case idAFVector::VEC_JOINT: {
    131 			if ( !GetJointTransform( model, frame, joint1, vec, axis ) ) {
    132 				common->Warning( "invalid joint %s in joint() in '%s'", joint1.c_str(), fileName );
    133 				vec.Zero();
    134 			}
    135 			break;
    136 		}
    137 		case idAFVector::VEC_BONECENTER: {
    138 			if ( !GetJointTransform( model, frame, joint1, start, axis ) ) {
    139 				common->Warning( "invalid joint %s in bonecenter() in '%s'", joint1.c_str(), fileName );
    140 				start.Zero();
    141 			}
    142 			if ( !GetJointTransform( model, frame, joint2, end, axis ) ) {
    143 				common->Warning( "invalid joint %s in bonecenter() in '%s'", joint2.c_str(), fileName );
    144 				end.Zero();
    145 			}
    146 			vec = ( start + end ) * 0.5f;
    147 			break;
    148 		}
    149 		case idAFVector::VEC_BONEDIR: {
    150 			if ( !GetJointTransform( model, frame, joint1, start, axis ) ) {
    151 				common->Warning( "invalid joint %s in bonedir() in '%s'", joint1.c_str(), fileName );
    152 				start.Zero();
    153 			}
    154 			if ( !GetJointTransform( model, frame, joint2, end, axis ) ) {
    155 				common->Warning( "invalid joint %s in bonedir() in '%s'", joint2.c_str(), fileName );
    156 				end.Zero();
    157 			}
    158 			vec = ( end - start );
    159 			break;
    160 		}
    161 		default: {
    162 			vec.Zero();
    163 			break;
    164 		}
    165 	}
    166 
    167 	if ( negate ) {
    168 		vec = -vec;
    169 	}
    170 
    171 	return true;
    172 }
    173 
    174 /*
    175 ================
    176 idAFVector::Write
    177 ================
    178 */
    179 bool idAFVector::Write( idFile *f ) const {
    180 
    181 	if ( negate ) {
    182 		f->WriteFloatString( "-" );
    183 	}
    184 	switch( type ) {
    185 		case idAFVector::VEC_COORDS: {
    186 			f->WriteFloatString( "( %f, %f, %f )", vec.x, vec.y, vec.z );
    187 			break;
    188 		}
    189 		case idAFVector::VEC_JOINT: {
    190 			f->WriteFloatString( "joint( \"%s\" )", joint1.c_str() );
    191 			break;
    192 		}
    193 		case idAFVector::VEC_BONECENTER: {
    194 			f->WriteFloatString( "bonecenter( \"%s\", \"%s\" )", joint1.c_str(), joint2.c_str() );
    195 			break;
    196 		}
    197 		case idAFVector::VEC_BONEDIR: {
    198 			f->WriteFloatString( "bonedir( \"%s\", \"%s\" )", joint1.c_str(), joint2.c_str() );
    199 			break;
    200 		}
    201 		default: {
    202 			break;
    203 		}
    204 	}
    205 	return true;
    206 }
    207 
    208 /*
    209 ================
    210 idAFVector::ToString
    211 ================
    212 */
    213 const char *idAFVector::ToString( idStr &str, const int precision ) {
    214 
    215 	switch( type ) {
    216 		case idAFVector::VEC_COORDS: {
    217 			char format[128];
    218 			sprintf( format, "( %%.%df, %%.%df, %%.%df )", precision, precision, precision );
    219 			sprintf( str, format, vec.x, vec.y, vec.z );
    220 			break;
    221 		}
    222 		case idAFVector::VEC_JOINT: {
    223 			sprintf( str, "joint( \"%s\" )", joint1.c_str() );
    224 			break;
    225 		}
    226 		case idAFVector::VEC_BONECENTER: {
    227 			sprintf( str, "bonecenter( \"%s\", \"%s\" )", joint1.c_str(), joint2.c_str() );
    228 			break;
    229 		}
    230 		case idAFVector::VEC_BONEDIR: {
    231 			sprintf( str, "bonedir( \"%s\", \"%s\" )", joint1.c_str(), joint2.c_str() );
    232 			break;
    233 		}
    234 		default: {
    235 			break;
    236 		}
    237 	}
    238 	if ( negate ) {
    239 		str = "-" + str;
    240 	}
    241 	return str.c_str();
    242 }
    243 
    244 /*
    245 ================
    246 idDeclAF_Body::SetDefault
    247 ================
    248 */
    249 void idDeclAF_Body::SetDefault( const idDeclAF *file ) {
    250 	name = "noname";
    251 	modelType = TRM_BOX;
    252 	v1.type = idAFVector::VEC_COORDS;
    253 	v1.ToVec3().x = v1.ToVec3().y = v1.ToVec3().z = -10.0f;
    254 	v2.type = idAFVector::VEC_COORDS;
    255 	v2.ToVec3().x = v2.ToVec3().y = v2.ToVec3().z = 10.0f;
    256 	numSides = 3;
    257 	origin.ToVec3().Zero();
    258 	angles.Zero();
    259 	density = 0.2f;
    260 	inertiaScale.Identity();
    261 	linearFriction = file->defaultLinearFriction;
    262 	angularFriction = file->defaultAngularFriction;
    263 	contactFriction = file->defaultContactFriction;
    264 	contents = file->contents;
    265 	clipMask = file->clipMask;
    266 	selfCollision = file->selfCollision;
    267 	frictionDirection.ToVec3().Zero();
    268 	contactMotorDirection.ToVec3().Zero();
    269 	jointName = "origin";
    270 	jointMod = DECLAF_JOINTMOD_AXIS;
    271 	containedJoints = "*origin";
    272 }
    273 
    274 /*
    275 ================
    276 idDeclAF_Constraint::SetDefault
    277 ================
    278 */
    279 void idDeclAF_Constraint::SetDefault( const idDeclAF *file ) {
    280 	name = "noname";
    281 	type = DECLAF_CONSTRAINT_UNIVERSALJOINT;
    282 	if ( file->bodies.Num() ) {
    283 		body1 = file->bodies[0]->name;
    284 	}
    285 	else {
    286 		body1 = "world";
    287 	}
    288 	body2 = "world";
    289 	friction = file->defaultConstraintFriction;
    290 	anchor.ToVec3().Zero();
    291 	anchor2.ToVec3().Zero();
    292 	axis.ToVec3().Set( 1.0f, 0.0f, 0.0f );
    293 	shaft[0].ToVec3().Set( 0.0f, 0.0f, -1.0f );
    294 	shaft[1].ToVec3().Set( 0.0f, 0.0f, 1.0f );
    295 	limit = idDeclAF_Constraint::LIMIT_NONE;
    296 	limitAngles[0] =
    297 	limitAngles[1] =
    298 	limitAngles[2] = 0.0f;
    299 	limitAxis.ToVec3().Set( 0.0f, 0.0f, -1.0f );
    300 }
    301 
    302 /*
    303 ================
    304 idDeclAF::WriteBody
    305 ================
    306 */
    307 bool idDeclAF::WriteBody( idFile *f, const idDeclAF_Body &body ) const {
    308 	idStr str;
    309 
    310 	f->WriteFloatString( "\nbody \"%s\" {\n", body.name.c_str() );
    311 	f->WriteFloatString( "\tjoint \"%s\"\n", body.jointName.c_str() );
    312 	f->WriteFloatString( "\tmod %s\n", JointModToString( body.jointMod ) );
    313 	switch( body.modelType ) {
    314 		case TRM_BOX: {
    315 	        f->WriteFloatString( "\tmodel box( " );
    316 			body.v1.Write( f );
    317 			f->WriteFloatString( ", " );
    318 			body.v2.Write( f );
    319 			f->WriteFloatString( " )\n" );
    320 			break;
    321 		}
    322 		case TRM_OCTAHEDRON: {
    323 	        f->WriteFloatString( "\tmodel octahedron( " );
    324 			body.v1.Write( f );
    325 			f->WriteFloatString( ", " );
    326 			body.v2.Write( f );
    327 			f->WriteFloatString( " )\n" );
    328 			break;
    329 		}
    330 		case TRM_DODECAHEDRON: {
    331 	        f->WriteFloatString( "\tmodel dodecahedron( " );
    332 			body.v1.Write( f );
    333 			f->WriteFloatString( ", " );
    334 			body.v2.Write( f );
    335 			f->WriteFloatString( " )\n" );
    336 			break;
    337 		}
    338 		case TRM_CYLINDER: {
    339 	        f->WriteFloatString( "\tmodel cylinder( " );
    340 			body.v1.Write( f );
    341 			f->WriteFloatString( ", " );
    342 			body.v2.Write( f );
    343 			f->WriteFloatString( ", %d )\n", body.numSides );
    344 			break;
    345 		}	
    346 		case TRM_CONE: {
    347 	        f->WriteFloatString( "\tmodel cone( " );
    348 			body.v1.Write( f );
    349 			f->WriteFloatString( ", " );
    350 			body.v2.Write( f );
    351 			f->WriteFloatString( ", %d )\n", body.numSides );
    352 			break;
    353 		}
    354 		case TRM_BONE: {
    355 	        f->WriteFloatString( "\tmodel bone( " );
    356 			body.v1.Write( f );
    357 			f->WriteFloatString( ", " );
    358 			body.v2.Write( f );
    359 			f->WriteFloatString( ", %f )\n", body.width );
    360 			break;
    361 		}
    362 		default:
    363 			assert( 0 );
    364 			break;
    365 	}
    366 	f->WriteFloatString( "\torigin " );
    367 	body.origin.Write( f );
    368 	f->WriteFloatString( "\n" );
    369 	if ( body.angles != ang_zero ) {
    370 		f->WriteFloatString( "\tangles ( %f, %f, %f )\n", body.angles.pitch, body.angles.yaw, body.angles.roll );
    371 	}
    372 	f->WriteFloatString( "\tdensity %f\n", body.density );
    373 	if ( body.inertiaScale != mat3_identity ) {
    374 		const idMat3 &ic = body.inertiaScale;
    375 		f->WriteFloatString( "\tinertiaScale (%f %f %f %f %f %f %f %f %f)\n",
    376 												ic[0][0], ic[0][1], ic[0][2],
    377 												ic[1][0], ic[1][1], ic[1][2],
    378 												ic[2][0], ic[2][1], ic[2][2] );
    379 	}
    380 	if ( body.linearFriction != -1 ) {
    381 		f->WriteFloatString( "\tfriction %f, %f, %f\n", body.linearFriction, body.angularFriction, body.contactFriction );
    382 	}
    383 	f->WriteFloatString( "\tcontents %s\n", ContentsToString( body.contents, str ) );
    384 	f->WriteFloatString( "\tclipMask %s\n", ContentsToString( body.clipMask, str ) );
    385 	f->WriteFloatString( "\tselfCollision %d\n", body.selfCollision );
    386 	if ( body.frictionDirection.ToVec3() != vec3_origin ) {
    387 		f->WriteFloatString( "\tfrictionDirection " );
    388 		body.frictionDirection.Write( f );
    389 		f->WriteFloatString( "\n" );
    390 	}
    391 	if ( body.contactMotorDirection.ToVec3() != vec3_origin ) {
    392 		f->WriteFloatString( "\tcontactMotorDirection " );
    393 		body.contactMotorDirection.Write( f );
    394 		f->WriteFloatString( "\n" );
    395 	}
    396 	f->WriteFloatString( "\tcontainedJoints \"%s\"\n", body.containedJoints.c_str() );
    397 	f->WriteFloatString( "}\n" );
    398 	return true;
    399 }
    400 
    401 /*
    402 ================
    403 idDeclAF::WriteFixed
    404 ================
    405 */
    406 bool idDeclAF::WriteFixed( idFile *f, const idDeclAF_Constraint &c ) const {
    407 	f->WriteFloatString( "\nfixed \"%s\" {\n", c.name.c_str() );
    408 	f->WriteFloatString( "\tbody1 \"%s\"\n", c.body1.c_str() );
    409 	f->WriteFloatString( "\tbody2 \"%s\"\n", c.body2.c_str() );
    410 	f->WriteFloatString( "}\n" );
    411 	return true;
    412 }
    413 
    414 /*
    415 ================
    416 idDeclAF::WriteBallAndSocketJoint
    417 ================
    418 */
    419 bool idDeclAF::WriteBallAndSocketJoint( idFile *f, const idDeclAF_Constraint &c ) const {
    420 	f->WriteFloatString( "\nballAndSocketJoint \"%s\" {\n", c.name.c_str() );
    421 	f->WriteFloatString( "\tbody1 \"%s\"\n", c.body1.c_str() );
    422 	f->WriteFloatString( "\tbody2 \"%s\"\n", c.body2.c_str() );
    423 	f->WriteFloatString( "\tanchor " );
    424 	c.anchor.Write( f );
    425 	f->WriteFloatString( "\n" );
    426 	f->WriteFloatString( "\tfriction %f\n", c.friction );
    427 	if ( c.limit == idDeclAF_Constraint::LIMIT_CONE ) {
    428 		f->WriteFloatString( "\tconeLimit " );
    429 		c.limitAxis.Write( f );
    430 		f->WriteFloatString( ", %f, ", c.limitAngles[0] );
    431 		c.shaft[0].Write( f );
    432 		f->WriteFloatString( "\n" );
    433 	}
    434 	else if ( c.limit == idDeclAF_Constraint::LIMIT_PYRAMID ) {
    435 		f->WriteFloatString( "\tpyramidLimit " );
    436 		c.limitAxis.Write( f );
    437 		f->WriteFloatString( ", %f, %f, %f, ", c.limitAngles[0], c.limitAngles[1], c.limitAngles[2] );
    438 		c.shaft[0].Write( f );
    439 		f->WriteFloatString( "\n" );
    440 	}
    441 	f->WriteFloatString( "}\n" );
    442 	return true;
    443 }
    444 
    445 /*
    446 ================
    447 idDeclAF::WriteUniversalJoint
    448 ================
    449 */
    450 bool idDeclAF::WriteUniversalJoint( idFile *f, const idDeclAF_Constraint &c ) const {
    451 	f->WriteFloatString( "\nuniversalJoint \"%s\" {\n", c.name.c_str() );
    452 	f->WriteFloatString( "\tbody1 \"%s\"\n", c.body1.c_str() );
    453 	f->WriteFloatString( "\tbody2 \"%s\"\n", c.body2.c_str() );
    454 	f->WriteFloatString( "\tanchor " );
    455 	c.anchor.Write( f );
    456 	f->WriteFloatString( "\n" );
    457 	f->WriteFloatString( "\tshafts " );
    458 	c.shaft[0].Write( f );
    459 	f->WriteFloatString( ", " );
    460 	c.shaft[1].Write( f );
    461 	f->WriteFloatString( "\n" );
    462 	f->WriteFloatString( "\tfriction %f\n", c.friction );
    463 	if ( c.limit == idDeclAF_Constraint::LIMIT_CONE ) {
    464 		f->WriteFloatString( "\tconeLimit " );
    465 		c.limitAxis.Write( f );
    466 		f->WriteFloatString( ", %f\n", c.limitAngles[0] );
    467 	}
    468 	else if ( c.limit == idDeclAF_Constraint::LIMIT_PYRAMID ) {
    469 		f->WriteFloatString( "\tpyramidLimit " );
    470 		c.limitAxis.Write( f );
    471 		f->WriteFloatString( ", %f, %f, %f\n", c.limitAngles[0], c.limitAngles[1], c.limitAngles[2] );
    472 	}
    473 	f->WriteFloatString( "}\n" );
    474 	return true;
    475 }
    476 
    477 /*
    478 ================
    479 idDeclAF::WriteHinge
    480 ================
    481 */
    482 bool idDeclAF::WriteHinge( idFile *f, const idDeclAF_Constraint &c ) const {
    483 	f->WriteFloatString( "\nhinge \"%s\" {\n", c.name.c_str() );
    484 	f->WriteFloatString( "\tbody1 \"%s\"\n", c.body1.c_str() );
    485 	f->WriteFloatString( "\tbody2 \"%s\"\n", c.body2.c_str() );
    486 	f->WriteFloatString( "\tanchor " );
    487 	c.anchor.Write( f );
    488 	f->WriteFloatString( "\n" );
    489 	f->WriteFloatString( "\taxis " );
    490 	c.axis.Write( f );
    491 	f->WriteFloatString( "\n" );
    492 	f->WriteFloatString( "\tfriction %f\n", c.friction );
    493 	if ( c.limit == idDeclAF_Constraint::LIMIT_CONE ) {
    494 		f->WriteFloatString( "\tlimit " );
    495 		f->WriteFloatString( "%f, %f, %f", c.limitAngles[0], c.limitAngles[1], c.limitAngles[2] );
    496 		f->WriteFloatString( "\n" );
    497 	}
    498 	f->WriteFloatString( "}\n" );
    499 	return true;
    500 }
    501 
    502 /*
    503 ================
    504 idDeclAF::WriteSlider
    505 ================
    506 */
    507 bool idDeclAF::WriteSlider( idFile *f, const idDeclAF_Constraint &c ) const {
    508 	f->WriteFloatString( "\nslider \"%s\" {\n", c.name.c_str() );
    509 	f->WriteFloatString( "\tbody1 \"%s\"\n", c.body1.c_str() );
    510 	f->WriteFloatString( "\tbody2 \"%s\"\n", c.body2.c_str() );
    511 	f->WriteFloatString( "\taxis " );
    512 	c.axis.Write( f );
    513 	f->WriteFloatString( "\n" );
    514 	f->WriteFloatString( "\tfriction %f\n", c.friction );
    515 	f->WriteFloatString( "}\n" );
    516 	return true;
    517 }
    518 
    519 /*
    520 ================
    521 idDeclAF::WriteSpring
    522 ================
    523 */
    524 bool idDeclAF::WriteSpring( idFile *f, const idDeclAF_Constraint &c ) const {
    525 	f->WriteFloatString( "\nspring \"%s\" {\n", c.name.c_str() );
    526 	f->WriteFloatString( "\tbody1 \"%s\"\n", c.body1.c_str() );
    527 	f->WriteFloatString( "\tbody2 \"%s\"\n", c.body2.c_str() );
    528 	f->WriteFloatString( "\tanchor1 " );
    529 	c.anchor.Write( f );
    530 	f->WriteFloatString( "\n" );
    531 	f->WriteFloatString( "\tanchor2 " );
    532 	c.anchor2.Write( f );
    533 	f->WriteFloatString( "\n" );
    534 	f->WriteFloatString( "\tfriction %f\n", c.friction );
    535 	f->WriteFloatString( "\tstretch %f\n", c.stretch );
    536 	f->WriteFloatString( "\tcompress %f\n", c.compress );
    537 	f->WriteFloatString( "\tdamping %f\n", c.damping );
    538 	f->WriteFloatString( "\trestLength %f\n", c.restLength );
    539 	f->WriteFloatString( "\tminLength %f\n", c.minLength );
    540 	f->WriteFloatString( "\tmaxLength %f\n", c.maxLength );
    541 	f->WriteFloatString( "}\n" );
    542 	return true;
    543 }
    544 
    545 /*
    546 ================
    547 idDeclAF::WriteConstraint
    548 ================
    549 */
    550 bool idDeclAF::WriteConstraint( idFile *f, const idDeclAF_Constraint &c ) const {
    551 	switch( c.type ) {
    552 		case DECLAF_CONSTRAINT_FIXED:
    553 			return WriteFixed( f, c );
    554 		case DECLAF_CONSTRAINT_BALLANDSOCKETJOINT:
    555 			return WriteBallAndSocketJoint( f, c );
    556 		case DECLAF_CONSTRAINT_UNIVERSALJOINT:
    557 			return WriteUniversalJoint( f, c );
    558 		case DECLAF_CONSTRAINT_HINGE:
    559 			return WriteHinge( f, c );
    560 		case DECLAF_CONSTRAINT_SLIDER:
    561 			return WriteSlider( f, c );
    562 		case DECLAF_CONSTRAINT_SPRING:
    563 			return WriteSpring( f, c );
    564 		default:
    565 			break;
    566 	}
    567 	return false;
    568 }
    569 
    570 /*
    571 ================
    572 idDeclAF::WriteSettings
    573 ================
    574 */
    575 bool idDeclAF::WriteSettings( idFile *f ) const {
    576 	idStr str;
    577 
    578 	f->WriteFloatString( "\nsettings {\n" );
    579 	f->WriteFloatString( "\tmodel \"%s\"\n", model.c_str() );
    580 	f->WriteFloatString( "\tskin \"%s\"\n",	skin.c_str() );
    581 	f->WriteFloatString( "\tfriction %f, %f, %f, %f\n", defaultLinearFriction, defaultAngularFriction, defaultContactFriction, defaultConstraintFriction );
    582 	f->WriteFloatString( "\tsuspendSpeed %f, %f, %f, %f\n", suspendVelocity[0], suspendVelocity[1], suspendAcceleration[0], suspendAcceleration[1] );
    583 	f->WriteFloatString( "\tnoMoveTime %f\n", noMoveTime );
    584 	f->WriteFloatString( "\tnoMoveTranslation %f\n", noMoveTranslation );
    585 	f->WriteFloatString( "\tnoMoveRotation %f\n", noMoveRotation );
    586 	f->WriteFloatString( "\tminMoveTime %f\n", minMoveTime );
    587 	f->WriteFloatString( "\tmaxMoveTime %f\n", maxMoveTime );
    588 	f->WriteFloatString( "\ttotalMass %f\n", totalMass );
    589 	f->WriteFloatString( "\tcontents %s\n", ContentsToString( contents, str ) );
    590 	f->WriteFloatString( "\tclipMask %s\n", ContentsToString( clipMask, str ) );
    591 	f->WriteFloatString( "\tselfCollision %d\n", selfCollision );
    592 	f->WriteFloatString( "}\n" );
    593 	return true;
    594 }
    595 
    596 
    597 /*
    598 ================
    599 idDeclAF::RebuildTextSource
    600 ================
    601 */
    602 bool idDeclAF::RebuildTextSource() {
    603 	int i;
    604 	idFile_Memory f;
    605 
    606 	f.WriteFloatString("\n\n/*\n"
    607 						"\tGenerated by the Articulated Figure Editor.\n"
    608 						"\tDo not edit directly but launch the game and type 'editAFs' on the console.\n"
    609 						"*/\n" );
    610 
    611 	f.WriteFloatString( "\narticulatedFigure %s {\n", GetName() );
    612 
    613 	if ( !WriteSettings( &f ) ) {
    614 		return false;
    615 	}
    616 
    617 	for ( i = 0; i < bodies.Num(); i++ ) {
    618 		if ( !WriteBody( &f, *bodies[i] ) ) {
    619 			return false;
    620 		}
    621 	}
    622 
    623 	for ( i = 0; i < constraints.Num(); i++ ) {
    624 		if ( !WriteConstraint( &f, *constraints[i] ) ) {
    625 			return false;
    626 		}
    627 	}
    628 
    629 	f.WriteFloatString( "\n}" );
    630 
    631 	SetText( f.GetDataPtr() );
    632 
    633 	return true;
    634 }
    635 
    636 /*
    637 ================
    638 idDeclAF::Save
    639 ================
    640 */
    641 bool idDeclAF::Save() {
    642 	RebuildTextSource();
    643 	ReplaceSourceFileText();
    644 	modified = false;
    645 	return true;
    646 }
    647 
    648 /*
    649 ================
    650 idDeclAF::ContentsFromString
    651 ================
    652 */
    653 int idDeclAF::ContentsFromString( const char *str ) {
    654 	int c;
    655 	idToken token;
    656 	idLexer src( str, idStr::Length( str ), "idDeclAF::ContentsFromString" );
    657 
    658 	c = 0;
    659 	while( src.ReadToken( &token ) ) {
    660 		if ( token.Icmp( "none" ) == 0 ) {
    661 			c = 0;
    662 		}
    663 		else if ( token.Icmp( "solid" ) == 0 ) {
    664 			c |= CONTENTS_SOLID;
    665 		}
    666 		else if ( token.Icmp( "body" ) == 0 ) {
    667 			c |= CONTENTS_BODY;
    668 		}
    669 		else if ( token.Icmp( "corpse" ) == 0 ) {
    670 			c |= CONTENTS_CORPSE;
    671 		}
    672 		else if ( token.Icmp( "playerclip" ) == 0 ) {
    673 			c |= CONTENTS_PLAYERCLIP;
    674 		}
    675 		else if ( token.Icmp( "monsterclip" ) == 0 ) {
    676 			c |= CONTENTS_MONSTERCLIP;
    677 		}
    678 		else if ( token == "," ) {
    679 			continue;
    680 		}
    681 		else {
    682 			return c;
    683 		}
    684 	}
    685 	return c;
    686 }
    687 
    688 /*
    689 ================
    690 idDeclAF::ContentsToString
    691 ================
    692 */
    693 const char *idDeclAF::ContentsToString( const int contents, idStr &str ) {
    694 	str = "";
    695 	if ( contents & CONTENTS_SOLID ) {
    696 		if ( str.Length() ) str += ", ";
    697 		str += "solid";
    698 	}
    699 	if ( contents & CONTENTS_BODY ) {
    700 		if ( str.Length() ) str += ", ";
    701 		str += "body";
    702 	}
    703 	if ( contents & CONTENTS_CORPSE ) {
    704 		if ( str.Length() ) str += ", ";
    705 		str += "corpse";
    706 	}
    707 	if ( contents & CONTENTS_PLAYERCLIP ) {
    708 		if ( str.Length() ) str += ", ";
    709 		str += "playerclip";
    710 	}
    711 	if ( contents & CONTENTS_MONSTERCLIP ) {
    712 		if ( str.Length() ) str += ", ";
    713 		str += "monsterclip";
    714 	}
    715 	if ( str[0] == '\0' ) {
    716 		str = "none";
    717 	}
    718 	return str.c_str();
    719 }
    720 
    721 /*
    722 ================
    723 idDeclAF::JointModFromString
    724 ================
    725 */
    726 declAFJointMod_t idDeclAF::JointModFromString( const char *str ) {
    727 	if ( idStr::Icmp( str, "orientation" ) == 0 ) {
    728 		return DECLAF_JOINTMOD_AXIS;
    729 	}
    730 	if ( idStr::Icmp( str, "position" ) == 0 ) {
    731 		return DECLAF_JOINTMOD_ORIGIN;
    732 	}
    733 	if ( idStr::Icmp( str, "both" ) == 0 ) {
    734 		return DECLAF_JOINTMOD_BOTH;
    735 	}
    736 	return DECLAF_JOINTMOD_AXIS;
    737 }
    738 
    739 /*
    740 ================
    741 idDeclAF::JointModToString
    742 ================
    743 */
    744 const char * idDeclAF::JointModToString( declAFJointMod_t jointMod ) {
    745 	switch( jointMod ) {
    746 		case DECLAF_JOINTMOD_AXIS: {
    747 			return "orientation";
    748 		}
    749 		case DECLAF_JOINTMOD_ORIGIN: {
    750 			return "position";
    751 		}
    752 		case DECLAF_JOINTMOD_BOTH: {
    753 			return "both";
    754 		}
    755 	}
    756 	return "orientation";
    757 }
    758 
    759 /*
    760 =================
    761 idDeclAF::Size
    762 =================
    763 */
    764 size_t idDeclAF::Size() const {
    765 	return sizeof( idDeclAF );
    766 }
    767 
    768 /*
    769 ================
    770 idDeclAF::ParseContents
    771 ================
    772 */
    773 bool idDeclAF::ParseContents( idLexer &src, int &c ) const {
    774 	idToken token;
    775 	idStr str;
    776 
    777 	while( src.ReadToken( &token ) ) {
    778 		str += token;
    779 		if ( !src.CheckTokenString( "," ) ) {
    780 			break;
    781 		}
    782 		str += ",";
    783 	}
    784 	c = ContentsFromString( str );
    785 	return true;
    786 }
    787 
    788 /*
    789 ================
    790 idDeclAF::ParseBody
    791 ================
    792 */
    793 bool idDeclAF::ParseBody( idLexer &src ) {
    794 	bool hasJoint = false;
    795 	idToken token;
    796 	idAFVector angles;
    797 	idDeclAF_Body *body = new (TAG_DECL) idDeclAF_Body;
    798 
    799 	bodies.Alloc() = body;
    800 
    801 	body->SetDefault( this );
    802 
    803 	if ( !src.ExpectTokenType( TT_STRING, 0, &token ) ||
    804 			!src.ExpectTokenString( "{" ) ) {
    805 		return false;
    806 	}
    807 
    808 	body->name = token;
    809 	if ( !body->name.Icmp( "origin" ) || !body->name.Icmp( "world" ) ) {
    810 		src.Error( "a body may not be named \"origin\" or \"world\"" );
    811 		return false;
    812 	}
    813 
    814 	while( src.ReadToken( &token ) ) {
    815 
    816 		if ( !token.Icmp( "model" ) ) {
    817 			if ( !src.ExpectTokenType( TT_NAME, 0, &token ) ) {
    818 				return false;
    819 			}
    820 			if ( !token.Icmp( "box" ) ) {
    821 				body->modelType = TRM_BOX;
    822 				if ( !src.ExpectTokenString( "(" ) ||
    823 					!body->v1.Parse( src ) ||
    824 					!src.ExpectTokenString( "," ) ||
    825 					!body->v2.Parse( src ) ||
    826 					!src.ExpectTokenString( ")" ) ) {
    827 					return false;
    828 				}
    829 			} else if ( !token.Icmp( "octahedron" ) ) {
    830 				body->modelType = TRM_OCTAHEDRON;
    831 				if ( !src.ExpectTokenString( "(" ) ||
    832 					!body->v1.Parse( src ) ||
    833 					!src.ExpectTokenString( "," ) ||
    834 					!body->v2.Parse( src ) ||
    835 					!src.ExpectTokenString( ")" ) ) {
    836 					return false;
    837 				}
    838 			} else if ( !token.Icmp( "dodecahedron" ) ) {
    839 				body->modelType = TRM_DODECAHEDRON;
    840 				if ( !src.ExpectTokenString( "(" ) ||
    841 					!body->v1.Parse( src ) ||
    842 					!src.ExpectTokenString( "," ) ||
    843 					!body->v2.Parse( src ) ||
    844 					!src.ExpectTokenString( ")" ) ) {
    845 					return false;
    846 				}
    847 			} else if ( !token.Icmp( "cylinder" ) ) {
    848 				body->modelType = TRM_CYLINDER;
    849 				if ( !src.ExpectTokenString( "(" ) ||
    850 					!body->v1.Parse( src ) ||
    851 					!src.ExpectTokenString( "," ) ||
    852 					!body->v2.Parse( src ) ||
    853 					!src.ExpectTokenString( "," ) ) {
    854 					return false;
    855 				}
    856 				body->numSides = src.ParseInt();
    857 				if ( !src.ExpectTokenString( ")" ) ) {
    858 					return false;
    859 				}
    860 			} else if ( !token.Icmp( "cone" ) ) {
    861 				body->modelType = TRM_CONE;
    862 				if ( !src.ExpectTokenString( "(" ) ||
    863 					!body->v1.Parse( src ) ||
    864 					!src.ExpectTokenString( "," ) ||
    865 					!body->v2.Parse( src ) ||
    866 					!src.ExpectTokenString( "," ) ) {
    867 					return false;
    868 				}
    869 				body->numSides = src.ParseInt();
    870 				if ( !src.ExpectTokenString( ")" ) ) {
    871 					return false;
    872 				}
    873 			} else if ( !token.Icmp( "bone" ) ) {
    874 				body->modelType = TRM_BONE;
    875 				if ( !src.ExpectTokenString( "(" ) ||
    876 					!body->v1.Parse( src ) ||
    877 					!src.ExpectTokenString( "," ) ||
    878 					!body->v2.Parse( src ) ||
    879 					!src.ExpectTokenString( "," ) ) {
    880 					return false;
    881 				}
    882 				body->width = src.ParseFloat();
    883 				if ( !src.ExpectTokenString( ")" ) ) {
    884 					return false;
    885 				}
    886 			} else if ( !token.Icmp( "custom" ) ) {
    887 				src.Error( "custom models not yet implemented" );
    888 				return false;
    889 			} else {
    890 				src.Error( "unkown model type %s", token.c_str() );
    891 				return false;
    892 			}
    893 		} else if ( !token.Icmp( "origin" ) ) {
    894 			if ( !body->origin.Parse( src ) ) {
    895 				return false;
    896 			}
    897 		} else if ( !token.Icmp( "angles" ) ) {
    898 			if ( !angles.Parse( src ) ) {
    899 				return false;
    900 			}
    901 			body->angles = idAngles( angles.ToVec3().x, angles.ToVec3().y, angles.ToVec3().z );
    902 		} else if ( !token.Icmp( "joint" ) ) {
    903 			if ( !src.ExpectTokenType( TT_STRING, 0, &token ) ) {
    904 				return false;
    905 			}
    906 			body->jointName = token;
    907 			hasJoint = true;
    908 		} else if ( !token.Icmp( "mod" ) ) {
    909 			if ( !src.ExpectAnyToken( &token ) ) {
    910 				return false;
    911 			}
    912 			body->jointMod = JointModFromString( token.c_str() );
    913 		} else if ( !token.Icmp( "density" ) ) {
    914 			body->density = src.ParseFloat();
    915 		} else if ( !token.Icmp( "inertiaScale" ) ) {
    916 			src.Parse1DMatrix( 9, body->inertiaScale[0].ToFloatPtr() );
    917 		} else if ( !token.Icmp( "friction" ) ) {
    918 			body->linearFriction = src.ParseFloat();
    919 			src.ExpectTokenString( "," );
    920 			body->angularFriction = src.ParseFloat();
    921 			src.ExpectTokenString( "," );
    922 			body->contactFriction = src.ParseFloat();
    923 		} else if ( !token.Icmp( "contents" ) ) {
    924 			ParseContents( src, body->contents );
    925 		} else if ( !token.Icmp( "clipMask" ) ) {
    926 			ParseContents( src, body->clipMask );
    927 			body->clipMask &= ~CONTENTS_CORPSE;		// never allow collisions against corpses
    928 		} else if ( !token.Icmp( "selfCollision" ) ) {
    929 			body->selfCollision = src.ParseBool();
    930 		} else if ( !token.Icmp( "containedjoints" ) ) {
    931 			if ( !src.ExpectTokenType( TT_STRING, 0, &token ) ) {
    932 				return false;
    933 			}
    934 			body->containedJoints = token;
    935 		} else if ( !token.Icmp( "frictionDirection" ) ) {
    936 			if ( !body->frictionDirection.Parse( src ) ) {
    937 				return false;
    938 			}
    939 		} else if ( !token.Icmp( "contactMotorDirection" ) ) {
    940 			if ( !body->contactMotorDirection.Parse( src ) ) {
    941 				return false;
    942 			}
    943 		} else if ( token == "}" ) {
    944 			break;
    945 		} else {
    946 			src.Error( "unknown token %s in body", token.c_str() );
    947 			return false;
    948 		}
    949 	}
    950 
    951 	if ( body->modelType == TRM_INVALID ) {
    952 		src.Error( "no model set for body" );
    953 		return false;
    954 	}
    955 
    956 	if ( !hasJoint ) {
    957 		src.Error( "no joint set for body" );
    958 		return false;
    959 	}
    960 
    961 	body->clipMask |= CONTENTS_MOVEABLECLIP;
    962 
    963 	return true;
    964 }
    965 
    966 /*
    967 ================
    968 idDeclAF::ParseFixed
    969 ================
    970 */
    971 bool idDeclAF::ParseFixed( idLexer &src ) {
    972 	idToken token;
    973 	idDeclAF_Constraint *constraint = new (TAG_DECL) idDeclAF_Constraint;
    974 
    975 	constraint->SetDefault( this );
    976 	constraints.Alloc() = constraint;
    977 
    978 	if ( !src.ExpectTokenType( TT_STRING, 0, &token ) ||
    979 			!src.ExpectTokenString( "{" ) ) {
    980 		return false;
    981 	}
    982 
    983 	constraint->type = DECLAF_CONSTRAINT_FIXED;
    984 	constraint->name = token;
    985 
    986 	while( src.ReadToken( &token ) ) {
    987 
    988 		if ( !token.Icmp( "body1" ) ) {
    989 			src.ExpectTokenType( TT_STRING, 0, &token );
    990 			constraint->body1 = token;
    991 		} else if ( !token.Icmp( "body2" ) ) {
    992 			src.ExpectTokenType( TT_STRING, 0, &token );
    993 			constraint->body2 = token;
    994 		} else if ( token == "}" ) {
    995 			break;
    996 		} else {
    997 			src.Error( "unknown token %s in ball and socket joint", token.c_str() );
    998 			return false;
    999 		}
   1000 	}
   1001 
   1002 	return true;
   1003 }
   1004 
   1005 /*
   1006 ================
   1007 idDeclAF::ParseBallAndSocketJoint
   1008 ================
   1009 */
   1010 bool idDeclAF::ParseBallAndSocketJoint( idLexer &src ) {
   1011 	idToken token;
   1012 	idDeclAF_Constraint *constraint = new (TAG_DECL) idDeclAF_Constraint;
   1013 
   1014 	constraint->SetDefault( this );
   1015 	constraints.Alloc() = constraint;
   1016 
   1017 	if ( !src.ExpectTokenType( TT_STRING, 0, &token ) ||
   1018 			!src.ExpectTokenString( "{" ) ) {
   1019 		return false;
   1020 	}
   1021 
   1022 	constraint->type = DECLAF_CONSTRAINT_BALLANDSOCKETJOINT;
   1023 	constraint->limit = idDeclAF_Constraint::LIMIT_NONE;
   1024 	constraint->name = token;
   1025 	constraint->friction = 0.5f;
   1026 	constraint->anchor.ToVec3().Zero();
   1027 	constraint->shaft[0].ToVec3().Zero();
   1028 
   1029 	while( src.ReadToken( &token ) ) {
   1030 
   1031 		if ( !token.Icmp( "body1" ) ) {
   1032 			src.ExpectTokenType( TT_STRING, 0, &token );
   1033 			constraint->body1 = token;
   1034 		} else if ( !token.Icmp( "body2" ) ) {
   1035 			src.ExpectTokenType( TT_STRING, 0, &token );
   1036 			constraint->body2 = token;
   1037 		} else if ( !token.Icmp( "anchor" ) ) {
   1038 			if ( !constraint->anchor.Parse( src ) ) {
   1039 				return false;
   1040 			}
   1041 		} else if ( !token.Icmp( "conelimit" ) ) {
   1042 			if ( !constraint->limitAxis.Parse( src ) ||
   1043 				!src.ExpectTokenString( "," ) ) {
   1044 					return false;
   1045 			}
   1046 			constraint->limitAngles[0] = src.ParseFloat();
   1047 			if ( !src.ExpectTokenString( "," ) ||
   1048 				!constraint->shaft[0].Parse( src ) ) {
   1049 				return false;
   1050 			}
   1051 			constraint->limit = idDeclAF_Constraint::LIMIT_CONE;
   1052 		} else if ( !token.Icmp( "pyramidlimit" ) ) {
   1053 			if ( !constraint->limitAxis.Parse( src ) ||
   1054 				!src.ExpectTokenString( "," ) ) {
   1055 					return false;
   1056 			}
   1057 			constraint->limitAngles[0] = src.ParseFloat();
   1058 			if ( !src.ExpectTokenString( "," ) ) {
   1059 				return false;
   1060 			}
   1061 			constraint->limitAngles[1] = src.ParseFloat();
   1062 			if ( !src.ExpectTokenString( "," ) ) {
   1063 				return false;
   1064 			}
   1065 			constraint->limitAngles[2] = src.ParseFloat();
   1066 			if ( !src.ExpectTokenString( "," ) ||
   1067 				!constraint->shaft[0].Parse( src ) ) {
   1068 				return false;
   1069 			}
   1070 			constraint->limit = idDeclAF_Constraint::LIMIT_PYRAMID;
   1071 		} else if ( !token.Icmp( "friction" ) ) {
   1072 			constraint->friction = src.ParseFloat();
   1073 		} else if ( token == "}" ) {
   1074 			break;
   1075 		} else {
   1076 			src.Error( "unknown token %s in ball and socket joint", token.c_str() );
   1077 			return false;
   1078 		}
   1079 	}
   1080 
   1081 	return true;
   1082 }
   1083 
   1084 /*
   1085 ================
   1086 idDeclAF::ParseUniversalJoint
   1087 ================
   1088 */
   1089 bool idDeclAF::ParseUniversalJoint( idLexer &src ) {
   1090 	idToken token;
   1091 	idDeclAF_Constraint *constraint = new (TAG_DECL) idDeclAF_Constraint;
   1092 
   1093 	constraint->SetDefault( this );
   1094 	constraints.Alloc() = constraint;
   1095 
   1096 	if ( !src.ExpectTokenType( TT_STRING, 0, &token ) ||
   1097 			!src.ExpectTokenString( "{" ) ) {
   1098 		return false;
   1099 	}
   1100 
   1101 	constraint->type = DECLAF_CONSTRAINT_UNIVERSALJOINT;
   1102 	constraint->limit = idDeclAF_Constraint::LIMIT_NONE;
   1103 	constraint->name = token;
   1104 	constraint->friction = 0.5f;
   1105 	constraint->anchor.ToVec3().Zero();
   1106 	constraint->shaft[0].ToVec3().Zero();
   1107 	constraint->shaft[1].ToVec3().Zero();
   1108 
   1109 	while( src.ReadToken( &token ) ) {
   1110 
   1111 		if ( !token.Icmp( "body1" ) ) {
   1112 			src.ExpectTokenType( TT_STRING, 0, &token );
   1113 			constraint->body1 = token;
   1114 		} else if ( !token.Icmp( "body2" ) ) {
   1115 			src.ExpectTokenType( TT_STRING, 0, &token );
   1116 			constraint->body2 = token;
   1117 		} else if ( !token.Icmp( "anchor" ) ) {
   1118 			if ( !constraint->anchor.Parse( src ) ) {
   1119 				return false;
   1120 			}
   1121 		} else if ( !token.Icmp( "shafts" ) ) {
   1122 			if ( !constraint->shaft[0].Parse( src ) ||
   1123 					!src.ExpectTokenString( "," ) ||
   1124 					!constraint->shaft[1].Parse( src ) ) {
   1125 				return false;
   1126 			}
   1127 		} else if ( !token.Icmp( "conelimit" ) ) {
   1128 			if ( !constraint->limitAxis.Parse( src ) ||
   1129 				!src.ExpectTokenString( "," ) ) {
   1130 					return false;
   1131 			}
   1132 			constraint->limitAngles[0] = src.ParseFloat();
   1133 			constraint->limit = idDeclAF_Constraint::LIMIT_CONE;
   1134 		} else if ( !token.Icmp( "pyramidlimit" ) ) {
   1135 			if ( !constraint->limitAxis.Parse( src ) ||
   1136 				!src.ExpectTokenString( "," ) ) {
   1137 					return false;
   1138 			}
   1139 			constraint->limitAngles[0] = src.ParseFloat();
   1140 			if ( !src.ExpectTokenString( "," ) ) {
   1141 				return false;
   1142 			}
   1143 			constraint->limitAngles[1] = src.ParseFloat();
   1144 			if ( !src.ExpectTokenString( "," ) ) {
   1145 				return false;
   1146 			}
   1147 			constraint->limitAngles[2] = src.ParseFloat();
   1148 			constraint->limit = idDeclAF_Constraint::LIMIT_PYRAMID;
   1149 		} else if ( !token.Icmp( "friction" ) ) {
   1150 			constraint->friction = src.ParseFloat();
   1151 		} else if ( token == "}" ) {
   1152 			break;
   1153 		} else {
   1154 			src.Error( "unknown token %s in universal joint", token.c_str() );
   1155 			return false;
   1156 		}
   1157 	}
   1158 
   1159 	return true;
   1160 }
   1161 
   1162 /*
   1163 ================
   1164 idDeclAF::ParseHinge
   1165 ================
   1166 */
   1167 bool idDeclAF::ParseHinge( idLexer &src ) {
   1168 	idToken token;
   1169 	idDeclAF_Constraint *constraint = new (TAG_DECL) idDeclAF_Constraint;
   1170 
   1171 	constraint->SetDefault( this );
   1172 	constraints.Alloc() = constraint;
   1173 
   1174 	if ( !src.ExpectTokenType( TT_STRING, 0, &token ) ||
   1175 			!src.ExpectTokenString( "{" ) ) {
   1176 		return false;
   1177 	}
   1178 
   1179 	constraint->type = DECLAF_CONSTRAINT_HINGE;
   1180 	constraint->limit = idDeclAF_Constraint::LIMIT_NONE;
   1181 	constraint->name = token;
   1182 	constraint->friction = 0.5f;
   1183 	constraint->anchor.ToVec3().Zero();
   1184 	constraint->axis.ToVec3().Zero();
   1185 
   1186 	while( src.ReadToken( &token ) ) {
   1187 
   1188 		if ( !token.Icmp( "body1" ) ) {
   1189 			src.ExpectTokenType( TT_STRING, 0, &token );
   1190 			constraint->body1 = token;
   1191 		} else if ( !token.Icmp( "body2" ) ) {
   1192 			src.ExpectTokenType( TT_STRING, 0, &token );
   1193 			constraint->body2 = token;
   1194 		} else if ( !token.Icmp( "anchor" ) ) {
   1195 			if ( !constraint->anchor.Parse( src ) ) {
   1196 				return false;
   1197 			}
   1198 		} else if ( !token.Icmp( "axis" ) ) {
   1199 			if ( !constraint->axis.Parse( src ) ) {
   1200 				return false;
   1201 			}
   1202 		} else if ( !token.Icmp( "limit" ) ) {
   1203 			constraint->limitAngles[0] = src.ParseFloat();
   1204 			if ( !src.ExpectTokenString( "," ) ) {
   1205 				return false;
   1206 			}
   1207 			constraint->limitAngles[1] = src.ParseFloat();
   1208 			if ( !src.ExpectTokenString( "," ) ) {
   1209 				return false;
   1210 			}
   1211 			constraint->limitAngles[2] = src.ParseFloat();
   1212 			constraint->limit = idDeclAF_Constraint::LIMIT_CONE;
   1213 		} else if ( !token.Icmp( "friction" ) ) {
   1214 			constraint->friction = src.ParseFloat();
   1215 		} else if ( token == "}" ) {
   1216 			break;
   1217 		} else {
   1218 			src.Error( "unknown token %s in hinge", token.c_str() );
   1219 			return false;
   1220 		}
   1221 	}
   1222 
   1223 	return true;
   1224 }
   1225 
   1226 /*
   1227 ================
   1228 idDeclAF::ParseSlider
   1229 ================
   1230 */
   1231 bool idDeclAF::ParseSlider( idLexer &src ) {
   1232 	idToken token;
   1233 	idDeclAF_Constraint *constraint = new (TAG_DECL) idDeclAF_Constraint;
   1234 
   1235 	constraint->SetDefault( this );
   1236 	constraints.Alloc() = constraint;
   1237 
   1238 	if ( !src.ExpectTokenType( TT_STRING, 0, &token ) ||
   1239 			!src.ExpectTokenString( "{" ) ) {
   1240 		return false;
   1241 	}
   1242 
   1243 	constraint->type = DECLAF_CONSTRAINT_SLIDER;
   1244 	constraint->limit = idDeclAF_Constraint::LIMIT_NONE;
   1245 	constraint->name = token;
   1246 	constraint->friction = 0.5f;
   1247 
   1248 	while( src.ReadToken( &token ) ) {
   1249 
   1250 		if ( !token.Icmp( "body1" ) ) {
   1251 			src.ExpectTokenType( TT_STRING, 0, &token );
   1252 			constraint->body1 = token;
   1253 		} else if ( !token.Icmp( "body2" ) ) {
   1254 			src.ExpectTokenType( TT_STRING, 0, &token );
   1255 			constraint->body2 = token;
   1256 		} else if ( !token.Icmp( "axis" ) ) {
   1257 			if ( !constraint->axis.Parse( src ) ) {
   1258 				return false;
   1259 			}
   1260 		} else if ( !token.Icmp( "friction" ) ) {
   1261 			constraint->friction = src.ParseFloat();
   1262 		} else if ( token == "}" ) {
   1263 			break;
   1264 		} else {
   1265 			src.Error( "unknown token %s in slider", token.c_str() );
   1266 			return false;
   1267 		}
   1268 	}
   1269 
   1270 	return true;
   1271 }
   1272 
   1273 /*
   1274 ================
   1275 idDeclAF::ParseSpring
   1276 ================
   1277 */
   1278 bool idDeclAF::ParseSpring( idLexer &src ) {
   1279 	idToken token;
   1280 	idDeclAF_Constraint *constraint = new (TAG_DECL) idDeclAF_Constraint;
   1281 
   1282 	constraint->SetDefault( this );
   1283 	constraints.Alloc() = constraint;
   1284 
   1285 	if ( !src.ExpectTokenType( TT_STRING, 0, &token ) ||
   1286 			!src.ExpectTokenString( "{" ) ) {
   1287 		return false;
   1288 	}
   1289 
   1290 	constraint->type = DECLAF_CONSTRAINT_SPRING;
   1291 	constraint->limit = idDeclAF_Constraint::LIMIT_NONE;
   1292 	constraint->name = token;
   1293 	constraint->friction = 0.5f;
   1294 
   1295 	while( src.ReadToken( &token ) ) {
   1296 
   1297 		if ( !token.Icmp( "body1" ) ) {
   1298 			src.ExpectTokenType( TT_STRING, 0, &token );
   1299 			constraint->body1 = token;
   1300 		} else if ( !token.Icmp( "body2" ) ) {
   1301 			src.ExpectTokenType( TT_STRING, 0, &token );
   1302 			constraint->body2 = token;
   1303 		} else if ( !token.Icmp( "anchor1" ) ) {
   1304 			if ( !constraint->anchor.Parse( src ) ) {
   1305 				return false;
   1306 			}
   1307 		} else if ( !token.Icmp( "anchor2" ) ) {
   1308 			if ( !constraint->anchor2.Parse( src ) ) {
   1309 				return false;
   1310 			}
   1311 		} else if ( !token.Icmp( "friction" ) ) {
   1312 			constraint->friction = src.ParseFloat();
   1313 		} else if ( !token.Icmp( "stretch" ) ) {
   1314 			constraint->stretch = src.ParseFloat();
   1315 		} else if ( !token.Icmp( "compress" ) ) {
   1316 			constraint->compress = src.ParseFloat();
   1317 		} else if ( !token.Icmp( "damping" ) ) {
   1318 			constraint->damping = src.ParseFloat();
   1319 		} else if ( !token.Icmp( "restLength" ) ) {
   1320 			constraint->restLength = src.ParseFloat();
   1321 		} else if ( !token.Icmp( "minLength" ) ) {
   1322 			constraint->minLength = src.ParseFloat();
   1323 		} else if ( !token.Icmp( "maxLength" ) ) {
   1324 			constraint->maxLength = src.ParseFloat();
   1325 		} else if ( token == "}" ) {
   1326 			break;
   1327 		} else {
   1328 			src.Error( "unknown token %s in spring", token.c_str() );
   1329 			return false;
   1330 		}
   1331 	}
   1332 
   1333 	return true;
   1334 }
   1335 
   1336 /*
   1337 ================
   1338 idDeclAF::ParseSettings
   1339 ================
   1340 */
   1341 bool idDeclAF::ParseSettings( idLexer &src ) {
   1342 	idToken token;
   1343 
   1344 	if ( !src.ExpectTokenString( "{" ) ) {
   1345 		return false;
   1346 	}
   1347 
   1348 	while( src.ReadToken( &token ) ) {
   1349 
   1350 		if ( !token.Icmp( "mesh" ) ) {
   1351 			if ( !src.ExpectTokenType( TT_STRING, 0, &token ) ) {
   1352 				return false;
   1353 			}
   1354 		} else if ( !token.Icmp( "anim" ) ) {
   1355 			if ( !src.ExpectTokenType( TT_STRING, 0, &token ) ) {
   1356 				return false;
   1357 			}
   1358 		} else if ( !token.Icmp( "model" ) ) {
   1359 			if ( !src.ExpectTokenType( TT_STRING, 0, &token ) ) {
   1360 				return false;
   1361 			}
   1362 			model = token;
   1363 		} else if ( !token.Icmp( "skin" ) ) {
   1364 			if ( !src.ExpectTokenType( TT_STRING, 0, &token ) ) {
   1365 				return false;
   1366 			}
   1367 			skin = token;
   1368 		} else if ( !token.Icmp( "friction" ) ) {
   1369 
   1370 			defaultLinearFriction = src.ParseFloat();
   1371 			if ( !src.ExpectTokenString( "," ) ) {
   1372 				return false;
   1373 			}
   1374 			defaultAngularFriction = src.ParseFloat();
   1375 			if ( !src.ExpectTokenString( "," ) ) {
   1376 				return false;
   1377 			}
   1378 			defaultContactFriction = src.ParseFloat();
   1379 			if ( src.CheckTokenString( "," ) ) {
   1380 				defaultConstraintFriction = src.ParseFloat();
   1381 			}
   1382 		} else if ( !token.Icmp( "totalMass" ) ) {
   1383 			totalMass = src.ParseFloat();
   1384 		} else if ( !token.Icmp( "suspendSpeed" ) ) {
   1385 
   1386 			suspendVelocity[0] = src.ParseFloat();
   1387 			if ( !src.ExpectTokenString( "," ) ) {
   1388 				return false;
   1389 			}
   1390 			suspendVelocity[1] = src.ParseFloat();
   1391 			if ( !src.ExpectTokenString( "," ) ) {
   1392 				return false;
   1393 			}
   1394 			suspendAcceleration[0] = src.ParseFloat();
   1395 			if ( !src.ExpectTokenString( "," ) ) {
   1396 				return false;
   1397 			}
   1398 			suspendAcceleration[1] = src.ParseFloat();
   1399 		} else if ( !token.Icmp( "noMoveTime" ) ) {
   1400 			noMoveTime = src.ParseFloat();
   1401 		} else if ( !token.Icmp( "noMoveTranslation" ) ) {
   1402 			noMoveTranslation = src.ParseFloat();
   1403 		} else if ( !token.Icmp( "noMoveRotation" ) ) {
   1404 			noMoveRotation = src.ParseFloat();
   1405 		} else if ( !token.Icmp( "minMoveTime" ) ) {
   1406 			minMoveTime = src.ParseFloat();
   1407 		} else if ( !token.Icmp( "maxMoveTime" ) ) {
   1408 			maxMoveTime = src.ParseFloat();
   1409 		} else if ( !token.Icmp( "contents" ) ) {
   1410 			ParseContents( src, contents );
   1411 		} else if ( !token.Icmp( "clipMask" ) ) {
   1412 			ParseContents( src, clipMask );
   1413 			clipMask &= ~CONTENTS_CORPSE;	// never allow collisions against corpses
   1414 		} else if ( !token.Icmp( "selfCollision" ) ) {
   1415 			selfCollision = src.ParseBool();
   1416 		} else if ( token == "}" ) {
   1417 			break;
   1418 		} else {
   1419 			src.Error( "unknown token %s in settings", token.c_str() );
   1420 			return false;
   1421 		}
   1422 	}
   1423 
   1424 	return true;
   1425 }
   1426 
   1427 /*
   1428 ================
   1429 idDeclAF::Parse
   1430 ================
   1431 */
   1432 bool idDeclAF::Parse( const char *text, const int textLength, bool allowBinaryVersion ) {
   1433 	int i, j;
   1434 	idLexer src;
   1435 	idToken token;
   1436 
   1437 	src.LoadMemory( text, textLength, GetFileName(), GetLineNum() );
   1438 	src.SetFlags( DECL_LEXER_FLAGS );
   1439 	src.SkipUntilString( "{" );
   1440 
   1441 	while( src.ReadToken( &token ) ) {
   1442 
   1443 		if ( !token.Icmp( "settings" ) ) {
   1444 			if ( !ParseSettings( src ) ) {
   1445 				return false;
   1446 			}
   1447 		} else if ( !token.Icmp( "body" ) ) {
   1448 			if ( !ParseBody( src ) ) {
   1449 				return false;
   1450 			}
   1451 		} else if ( !token.Icmp( "fixed" ) ) {
   1452 			if ( !ParseFixed( src ) ) {
   1453 				return false;
   1454 			}
   1455 		} else if ( !token.Icmp( "ballAndSocketJoint" ) ) {
   1456 			if ( !ParseBallAndSocketJoint( src ) ) {
   1457 				return false;
   1458 			}
   1459 		} else if ( !token.Icmp( "universalJoint" ) ) {
   1460 			if ( !ParseUniversalJoint( src ) ) {
   1461 				return false;
   1462 			}
   1463 		} else if ( !token.Icmp( "hinge" ) ) {
   1464 			if ( !ParseHinge( src ) ) {
   1465 				return false;
   1466 			}
   1467 		} else if ( !token.Icmp( "slider" ) ) {
   1468 			if ( !ParseSlider( src ) ) {
   1469 				return false;
   1470 			}
   1471 		} else if ( !token.Icmp( "spring" ) ) {
   1472 			if ( !ParseSpring( src ) ) {
   1473 				return false;
   1474 			}
   1475 		} else if ( token == "}" ) {
   1476 			break;
   1477 		} else {
   1478 			src.Error( "unknown keyword %s", token.c_str() );
   1479 			return false;
   1480 		}
   1481 	}
   1482 
   1483 	for ( i = 0; i < bodies.Num(); i++ ) {
   1484 		// check for multiple bodies with the same name
   1485 		for ( j = i+1; j < bodies.Num(); j++ ) {
   1486 			if ( bodies[i]->name == bodies[j]->name ) {
   1487 				src.Error( "two bodies with the same name \"%s\"", bodies[i]->name.c_str() );
   1488 			}
   1489 		}
   1490 	}
   1491 
   1492 	for ( i = 0; i < constraints.Num(); i++ ) {
   1493 		// check for multiple constraints with the same name
   1494 		for ( j = i+1; j < constraints.Num(); j++ ) {
   1495 			if ( constraints[i]->name == constraints[j]->name ) {
   1496 				src.Error( "two constraints with the same name \"%s\"", constraints[i]->name.c_str() );
   1497 			}
   1498 		}
   1499 		// check if there are two valid bodies set
   1500 		if ( constraints[i]->body1 == "" ) {
   1501 			src.Error( "no valid body1 specified for constraint '%s'", constraints[i]->name.c_str() );
   1502 		}
   1503 		if ( constraints[i]->body2 == "" ) {
   1504 			src.Error( "no valid body2 specified for constraint '%s'", constraints[i]->name.c_str() );
   1505 		}
   1506 	}
   1507 
   1508 	// make sure the body which modifies the origin comes first
   1509 	for ( i = 0; i < bodies.Num(); i++ ) {
   1510 		if ( bodies[i]->jointName == "origin" ) {
   1511 			if ( i != 0 ) {
   1512 				idDeclAF_Body *b = bodies[0];
   1513 				bodies[0] = bodies[i];
   1514 				bodies[i] = b;
   1515 			}
   1516 			break;
   1517 		}
   1518 	}
   1519 
   1520 	return true;
   1521 }
   1522 
   1523 /*
   1524 ================
   1525 idDeclAF::DefaultDefinition
   1526 ================
   1527 */
   1528 const char *idDeclAF::DefaultDefinition() const {
   1529 	return
   1530 		"{\n"
   1531 	"\t"	"settings {\n"
   1532 	"\t\t"		"model \"\"\n"
   1533 	"\t\t"		"skin \"\"\n"
   1534 	"\t\t"		"friction 0.01, 0.01, 0.8, 0.5\n"
   1535 	"\t\t"		"suspendSpeed 20, 30, 40, 60\n"
   1536 	"\t\t"		"noMoveTime 1\n"
   1537 	"\t\t"		"noMoveTranslation 10\n"
   1538 	"\t\t"		"noMoveRotation 10\n"
   1539 	"\t\t"		"minMoveTime -1\n"
   1540 	"\t\t"		"maxMoveTime -1\n"
   1541 	"\t\t"		"totalMass -1\n"
   1542 	"\t\t"		"contents corpse\n"
   1543 	"\t\t"		"clipMask solid, corpse\n"
   1544 	"\t\t"		"selfCollision 1\n"
   1545 	"\t"	"}\n"
   1546 	"\t"	"body \"body\" {\n"
   1547 	"\t\t"		"joint \"origin\"\n"
   1548 	"\t\t"		"mod orientation\n"
   1549 	"\t\t"		"model box( ( -10, -10, -10 ), ( 10, 10, 10 ) )\n"
   1550 	"\t\t"		"origin ( 0, 0, 0 )\n"
   1551 	"\t\t"		"density 0.2\n"
   1552 	"\t\t"		"friction 0.01, 0.01, 0.8\n"
   1553 	"\t\t"		"contents corpse\n"
   1554 	"\t\t"		"clipMask solid, corpse\n"
   1555 	"\t\t"		"selfCollision 1\n"
   1556 	"\t\t"		"containedJoints \"*origin\"\n"
   1557 	"\t"	"}\n"
   1558 		"}\n";
   1559 }
   1560 
   1561 /*
   1562 ================
   1563 idDeclAF::FreeData
   1564 ================
   1565 */
   1566 void idDeclAF::FreeData() {
   1567 	modified = false;
   1568 	defaultLinearFriction = 0.01f;
   1569 	defaultAngularFriction = 0.01f;
   1570 	defaultContactFriction = 0.8f;
   1571 	defaultConstraintFriction = 0.5f;
   1572 	totalMass = -1;
   1573 	suspendVelocity.Set( 20.0f, 30.0f );
   1574 	suspendAcceleration.Set( 40.0f, 60.0f );
   1575 	noMoveTime = 1.0f;
   1576 	noMoveTranslation = 10.0f;
   1577 	noMoveRotation = 10.0f;
   1578 	minMoveTime = -1.0f;
   1579 	maxMoveTime = -1.0f;
   1580 	selfCollision = true;
   1581 	contents = CONTENTS_CORPSE;
   1582 	clipMask = CONTENTS_SOLID;
   1583 	bodies.DeleteContents( true );
   1584 	constraints.DeleteContents( true );
   1585 }
   1586 
   1587 /*
   1588 ================
   1589 idDeclAF::Finish
   1590 ================
   1591 */
   1592 void idDeclAF::Finish( const getJointTransform_t GetJointTransform, const idJointMat *frame, void *model ) const {
   1593 	int i;
   1594 
   1595 	const char *name = GetName();
   1596 	for ( i = 0; i < bodies.Num(); i++ ) {
   1597 		idDeclAF_Body *body = bodies[i];
   1598 		body->v1.Finish( name, GetJointTransform, frame, model );
   1599 		body->v2.Finish( name, GetJointTransform, frame, model );
   1600 		body->origin.Finish( name, GetJointTransform, frame, model );
   1601 		body->frictionDirection.Finish( name, GetJointTransform, frame, model );
   1602 		body->contactMotorDirection.Finish( name, GetJointTransform, frame, model );
   1603 	}
   1604 	for ( i = 0; i < constraints.Num(); i++ ) {
   1605 		idDeclAF_Constraint *constraint = constraints[i];
   1606 		constraint->anchor.Finish( name, GetJointTransform, frame, model );
   1607 		constraint->anchor2.Finish( name, GetJointTransform, frame, model );
   1608 		constraint->shaft[0].Finish( name, GetJointTransform, frame, model );
   1609 		constraint->shaft[1].Finish( name, GetJointTransform, frame, model );
   1610 		constraint->axis.Finish( name, GetJointTransform, frame, model );
   1611 		constraint->limitAxis.Finish( name, GetJointTransform, frame, model );
   1612 	}
   1613 }
   1614 
   1615 /*
   1616 ================
   1617 idDeclAF::NewBody
   1618 ================
   1619 */
   1620 void idDeclAF::NewBody( const char *name ) {
   1621 	idDeclAF_Body *body;
   1622 
   1623 	body = new (TAG_DECL) idDeclAF_Body();
   1624 	body->SetDefault( this );
   1625 	body->name = name;
   1626 	bodies.Append( body );
   1627 }
   1628 
   1629 /*
   1630 ================
   1631 idDeclAF::RenameBody
   1632 
   1633   rename the body with the given name and rename
   1634   all constraint body references
   1635 ================
   1636 */
   1637 void idDeclAF::RenameBody( const char *oldName, const char *newName ) {
   1638 	int i;
   1639 
   1640 	for ( i = 0; i < bodies.Num(); i++ ) {
   1641 		if ( bodies[i]->name.Icmp( oldName ) == 0 ) {
   1642 			bodies[i]->name = newName;
   1643 			break;
   1644 		}
   1645 	}
   1646 	for ( i = 0; i < constraints.Num(); i++ ) {
   1647 		if ( constraints[i]->body1.Icmp( oldName ) == 0 ) {
   1648 			constraints[i]->body1 = newName;
   1649 		} else if ( constraints[i]->body2.Icmp( oldName ) == 0 ) {
   1650 			constraints[i]->body2 = newName;
   1651 		}
   1652 	}
   1653 }
   1654 
   1655 /*
   1656 ================
   1657 idDeclAF::DeleteBody
   1658 
   1659   delete the body with the given name and delete
   1660   all constraints that reference the body
   1661 ================
   1662 */
   1663 void idDeclAF::DeleteBody( const char *name ) {
   1664 	int i;
   1665 
   1666 	for ( i = 0; i < bodies.Num(); i++ ) {
   1667 		if ( bodies[i]->name.Icmp( name ) == 0 ) {
   1668 			delete bodies[i];
   1669 			bodies.RemoveIndex( i );
   1670 			break;
   1671 		}
   1672 	}
   1673 	for ( i = 0; i < constraints.Num(); i++ ) {
   1674 		if ( constraints[i]->body1.Icmp( name ) == 0 ||
   1675 			constraints[i]->body2.Icmp( name ) == 0 ) {
   1676 			delete constraints[i];
   1677 			constraints.RemoveIndex( i );
   1678 			i--;
   1679 		}
   1680 	}
   1681 }
   1682 
   1683 /*
   1684 ================
   1685 idDeclAF::NewConstraint
   1686 ================
   1687 */
   1688 void idDeclAF::NewConstraint( const char *name ) {
   1689 	idDeclAF_Constraint *constraint;
   1690 
   1691 	constraint = new (TAG_DECL) idDeclAF_Constraint;
   1692 	constraint->SetDefault( this );
   1693 	constraint->name = name;
   1694 	constraints.Append( constraint );
   1695 }
   1696 
   1697 /*
   1698 ================
   1699 idDeclAF::RenameConstraint
   1700 ================
   1701 */
   1702 void idDeclAF::RenameConstraint( const char *oldName, const char *newName ) {
   1703 	int i;
   1704 
   1705 	for ( i = 0; i < constraints.Num(); i++ ) {
   1706 		if ( constraints[i]->name.Icmp( oldName ) == 0 ) {
   1707 			constraints[i]->name = newName;
   1708 			return;
   1709 		}
   1710 	}
   1711 }
   1712 
   1713 /*
   1714 ================
   1715 idDeclAF::DeleteConstraint
   1716 ================
   1717 */
   1718 void idDeclAF::DeleteConstraint( const char *name ) {
   1719 	int i;
   1720 
   1721 	for ( i = 0; i < constraints.Num(); i++ ) {
   1722 		if ( constraints[i]->name.Icmp( name ) == 0 ) {
   1723 			delete constraints[i];
   1724 			constraints.RemoveIndex( i );
   1725 			return;
   1726 		}
   1727 	}
   1728 }
   1729 
   1730 /*
   1731 ================
   1732 idDeclAF::idDeclAF
   1733 ================
   1734 */
   1735 idDeclAF::idDeclAF() {
   1736 	FreeData();
   1737 }
   1738 
   1739 /*
   1740 ================
   1741 idDeclAF::~idDeclAF
   1742 ================
   1743 */
   1744 idDeclAF::~idDeclAF() {
   1745 	bodies.DeleteContents( true );
   1746 	constraints.DeleteContents( true );
   1747 }