mirror of
https://github.com/dhewm/dhewm3.git
synced 2024-11-30 16:11:11 +00:00
736ec20d4d
Don't include the lazy precompiled.h everywhere, only what's required for the compilation unit. platform.h needs to be included instead to provide all essential defines and types. All includes use the relative path to the neo or the game specific root. Move all idlib related includes from idlib/Lib.h to precompiled.h. precompiled.h still exists for the MFC stuff in tools/. Add some missing header guards.
1751 lines
46 KiB
C++
1751 lines
46 KiB
C++
/*
|
|
===========================================================================
|
|
|
|
Doom 3 GPL Source Code
|
|
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
|
|
|
|
This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code").
|
|
|
|
Doom 3 Source Code is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
Doom 3 Source Code is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
In addition, the Doom 3 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 Source Code. If not, please request a copy in writing from id Software at the address below.
|
|
|
|
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.
|
|
|
|
===========================================================================
|
|
*/
|
|
|
|
#include "sys/platform.h"
|
|
#include "idlib/geometry/JointTransform.h"
|
|
#include "idlib/geometry/TraceModel.h"
|
|
#include "framework/Common.h"
|
|
#include "framework/File.h"
|
|
#include "renderer/Material.h"
|
|
|
|
#include "framework/DeclAF.h"
|
|
|
|
/*
|
|
===============================================================================
|
|
|
|
idDeclAF
|
|
|
|
===============================================================================
|
|
*/
|
|
|
|
/*
|
|
================
|
|
idAFVector::idAFVector
|
|
================
|
|
*/
|
|
idAFVector::idAFVector( void ) {
|
|
type = VEC_COORDS;
|
|
vec.Zero();
|
|
negate = false;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idAFVector::Parse
|
|
================
|
|
*/
|
|
bool idAFVector::Parse( idLexer &src ) {
|
|
idToken token;
|
|
|
|
if ( !src.ReadToken( &token ) ) {
|
|
return false;
|
|
}
|
|
|
|
if ( token == "-" ) {
|
|
negate = true;
|
|
if ( !src.ReadToken( &token ) ) {
|
|
return false;
|
|
}
|
|
}
|
|
else {
|
|
negate = false;
|
|
}
|
|
|
|
if ( token == "(" ) {
|
|
type = idAFVector::VEC_COORDS;
|
|
vec.x = src.ParseFloat();
|
|
src.ExpectTokenString( "," );
|
|
vec.y = src.ParseFloat();
|
|
src.ExpectTokenString( "," );
|
|
vec.z = src.ParseFloat();
|
|
src.ExpectTokenString( ")" );
|
|
}
|
|
else if ( token == "joint" ) {
|
|
type = idAFVector::VEC_JOINT;
|
|
src.ExpectTokenString( "(" );
|
|
src.ReadToken( &token );
|
|
joint1 = token;
|
|
src.ExpectTokenString( ")" );
|
|
}
|
|
else if ( token == "bonecenter" ) {
|
|
type = idAFVector::VEC_BONECENTER;
|
|
src.ExpectTokenString( "(" );
|
|
src.ReadToken( &token );
|
|
joint1 = token;
|
|
src.ExpectTokenString( "," );
|
|
src.ReadToken( &token );
|
|
joint2 = token;
|
|
src.ExpectTokenString( ")" );
|
|
}
|
|
else if ( token == "bonedir" ) {
|
|
type = idAFVector::VEC_BONEDIR;
|
|
src.ExpectTokenString( "(" );
|
|
src.ReadToken( &token );
|
|
joint1 = token;
|
|
src.ExpectTokenString( "," );
|
|
src.ReadToken( &token );
|
|
joint2 = token;
|
|
src.ExpectTokenString( ")" );
|
|
}
|
|
else {
|
|
src.Error( "unknown token %s in vector", token.c_str() );
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idAFVector::Finish
|
|
================
|
|
*/
|
|
bool idAFVector::Finish( const char *fileName, const getJointTransform_t GetJointTransform, const idJointMat *frame, void *model ) const {
|
|
idMat3 axis;
|
|
idVec3 start, end;
|
|
|
|
switch( type ) {
|
|
case idAFVector::VEC_COORDS: {
|
|
break;
|
|
}
|
|
case idAFVector::VEC_JOINT: {
|
|
if ( !GetJointTransform( model, frame, joint1, vec, axis ) ) {
|
|
common->Warning( "invalid joint %s in joint() in '%s'", joint1.c_str(), fileName );
|
|
vec.Zero();
|
|
}
|
|
break;
|
|
}
|
|
case idAFVector::VEC_BONECENTER: {
|
|
if ( !GetJointTransform( model, frame, joint1, start, axis ) ) {
|
|
common->Warning( "invalid joint %s in bonecenter() in '%s'", joint1.c_str(), fileName );
|
|
start.Zero();
|
|
}
|
|
if ( !GetJointTransform( model, frame, joint2, end, axis ) ) {
|
|
common->Warning( "invalid joint %s in bonecenter() in '%s'", joint2.c_str(), fileName );
|
|
end.Zero();
|
|
}
|
|
vec = ( start + end ) * 0.5f;
|
|
break;
|
|
}
|
|
case idAFVector::VEC_BONEDIR: {
|
|
if ( !GetJointTransform( model, frame, joint1, start, axis ) ) {
|
|
common->Warning( "invalid joint %s in bonedir() in '%s'", joint1.c_str(), fileName );
|
|
start.Zero();
|
|
}
|
|
if ( !GetJointTransform( model, frame, joint2, end, axis ) ) {
|
|
common->Warning( "invalid joint %s in bonedir() in '%s'", joint2.c_str(), fileName );
|
|
end.Zero();
|
|
}
|
|
vec = ( end - start );
|
|
break;
|
|
}
|
|
default: {
|
|
vec.Zero();
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( negate ) {
|
|
vec = -vec;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idAFVector::Write
|
|
================
|
|
*/
|
|
bool idAFVector::Write( idFile *f ) const {
|
|
|
|
if ( negate ) {
|
|
f->WriteFloatString( "-" );
|
|
}
|
|
switch( type ) {
|
|
case idAFVector::VEC_COORDS: {
|
|
f->WriteFloatString( "( %f, %f, %f )", vec.x, vec.y, vec.z );
|
|
break;
|
|
}
|
|
case idAFVector::VEC_JOINT: {
|
|
f->WriteFloatString( "joint( \"%s\" )", joint1.c_str() );
|
|
break;
|
|
}
|
|
case idAFVector::VEC_BONECENTER: {
|
|
f->WriteFloatString( "bonecenter( \"%s\", \"%s\" )", joint1.c_str(), joint2.c_str() );
|
|
break;
|
|
}
|
|
case idAFVector::VEC_BONEDIR: {
|
|
f->WriteFloatString( "bonedir( \"%s\", \"%s\" )", joint1.c_str(), joint2.c_str() );
|
|
break;
|
|
}
|
|
default: {
|
|
break;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idAFVector::ToString
|
|
================
|
|
*/
|
|
const char *idAFVector::ToString( idStr &str, const int precision ) {
|
|
|
|
switch( type ) {
|
|
case idAFVector::VEC_COORDS: {
|
|
char format[128];
|
|
sprintf( format, "( %%.%df, %%.%df, %%.%df )", precision, precision, precision );
|
|
sprintf( str, format, vec.x, vec.y, vec.z );
|
|
break;
|
|
}
|
|
case idAFVector::VEC_JOINT: {
|
|
sprintf( str, "joint( \"%s\" )", joint1.c_str() );
|
|
break;
|
|
}
|
|
case idAFVector::VEC_BONECENTER: {
|
|
sprintf( str, "bonecenter( \"%s\", \"%s\" )", joint1.c_str(), joint2.c_str() );
|
|
break;
|
|
}
|
|
case idAFVector::VEC_BONEDIR: {
|
|
sprintf( str, "bonedir( \"%s\", \"%s\" )", joint1.c_str(), joint2.c_str() );
|
|
break;
|
|
}
|
|
default: {
|
|
break;
|
|
}
|
|
}
|
|
if ( negate ) {
|
|
str = "-" + str;
|
|
}
|
|
return str.c_str();
|
|
}
|
|
|
|
/*
|
|
================
|
|
idDeclAF_Body::SetDefault
|
|
================
|
|
*/
|
|
void idDeclAF_Body::SetDefault( const idDeclAF *file ) {
|
|
name = "noname";
|
|
modelType = TRM_BOX;
|
|
v1.type = idAFVector::VEC_COORDS;
|
|
v1.ToVec3().x = v1.ToVec3().y = v1.ToVec3().z = -10.0f;
|
|
v2.type = idAFVector::VEC_COORDS;
|
|
v2.ToVec3().x = v2.ToVec3().y = v2.ToVec3().z = 10.0f;
|
|
numSides = 3;
|
|
origin.ToVec3().Zero();
|
|
angles.Zero();
|
|
density = 0.2f;
|
|
inertiaScale.Identity();
|
|
linearFriction = file->defaultLinearFriction;
|
|
angularFriction = file->defaultAngularFriction;
|
|
contactFriction = file->defaultContactFriction;
|
|
contents = file->contents;
|
|
clipMask = file->clipMask;
|
|
selfCollision = file->selfCollision;
|
|
frictionDirection.ToVec3().Zero();
|
|
contactMotorDirection.ToVec3().Zero();
|
|
jointName = "origin";
|
|
jointMod = DECLAF_JOINTMOD_AXIS;
|
|
containedJoints = "*origin";
|
|
}
|
|
|
|
/*
|
|
================
|
|
idDeclAF_Constraint::SetDefault
|
|
================
|
|
*/
|
|
void idDeclAF_Constraint::SetDefault( const idDeclAF *file ) {
|
|
name = "noname";
|
|
type = DECLAF_CONSTRAINT_UNIVERSALJOINT;
|
|
if ( file->bodies.Num() ) {
|
|
body1 = file->bodies[0]->name;
|
|
}
|
|
else {
|
|
body1 = "world";
|
|
}
|
|
body2 = "world";
|
|
friction = file->defaultConstraintFriction;
|
|
anchor.ToVec3().Zero();
|
|
anchor2.ToVec3().Zero();
|
|
axis.ToVec3().Set( 1.0f, 0.0f, 0.0f );
|
|
shaft[0].ToVec3().Set( 0.0f, 0.0f, -1.0f );
|
|
shaft[1].ToVec3().Set( 0.0f, 0.0f, 1.0f );
|
|
limit = idDeclAF_Constraint::LIMIT_NONE;
|
|
limitAngles[0] =
|
|
limitAngles[1] =
|
|
limitAngles[2] = 0.0f;
|
|
limitAxis.ToVec3().Set( 0.0f, 0.0f, -1.0f );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idDeclAF::WriteBody
|
|
================
|
|
*/
|
|
bool idDeclAF::WriteBody( idFile *f, const idDeclAF_Body &body ) const {
|
|
idStr str;
|
|
|
|
f->WriteFloatString( "\nbody \"%s\" {\n", body.name.c_str() );
|
|
f->WriteFloatString( "\tjoint \"%s\"\n", body.jointName.c_str() );
|
|
f->WriteFloatString( "\tmod %s\n", JointModToString( body.jointMod ) );
|
|
switch( body.modelType ) {
|
|
case TRM_BOX: {
|
|
f->WriteFloatString( "\tmodel box( " );
|
|
body.v1.Write( f );
|
|
f->WriteFloatString( ", " );
|
|
body.v2.Write( f );
|
|
f->WriteFloatString( " )\n" );
|
|
break;
|
|
}
|
|
case TRM_OCTAHEDRON: {
|
|
f->WriteFloatString( "\tmodel octahedron( " );
|
|
body.v1.Write( f );
|
|
f->WriteFloatString( ", " );
|
|
body.v2.Write( f );
|
|
f->WriteFloatString( " )\n" );
|
|
break;
|
|
}
|
|
case TRM_DODECAHEDRON: {
|
|
f->WriteFloatString( "\tmodel dodecahedron( " );
|
|
body.v1.Write( f );
|
|
f->WriteFloatString( ", " );
|
|
body.v2.Write( f );
|
|
f->WriteFloatString( " )\n" );
|
|
break;
|
|
}
|
|
case TRM_CYLINDER: {
|
|
f->WriteFloatString( "\tmodel cylinder( " );
|
|
body.v1.Write( f );
|
|
f->WriteFloatString( ", " );
|
|
body.v2.Write( f );
|
|
f->WriteFloatString( ", %d )\n", body.numSides );
|
|
break;
|
|
}
|
|
case TRM_CONE: {
|
|
f->WriteFloatString( "\tmodel cone( " );
|
|
body.v1.Write( f );
|
|
f->WriteFloatString( ", " );
|
|
body.v2.Write( f );
|
|
f->WriteFloatString( ", %d )\n", body.numSides );
|
|
break;
|
|
}
|
|
case TRM_BONE: {
|
|
f->WriteFloatString( "\tmodel bone( " );
|
|
body.v1.Write( f );
|
|
f->WriteFloatString( ", " );
|
|
body.v2.Write( f );
|
|
f->WriteFloatString( ", %f )\n", body.width );
|
|
break;
|
|
}
|
|
default:
|
|
assert( 0 );
|
|
break;
|
|
}
|
|
f->WriteFloatString( "\torigin " );
|
|
body.origin.Write( f );
|
|
f->WriteFloatString( "\n" );
|
|
if ( body.angles != ang_zero ) {
|
|
f->WriteFloatString( "\tangles ( %f, %f, %f )\n", body.angles.pitch, body.angles.yaw, body.angles.roll );
|
|
}
|
|
f->WriteFloatString( "\tdensity %f\n", body.density );
|
|
if ( body.inertiaScale != mat3_identity ) {
|
|
const idMat3 &ic = body.inertiaScale;
|
|
f->WriteFloatString( "\tinertiaScale (%f %f %f %f %f %f %f %f %f)\n",
|
|
ic[0][0], ic[0][1], ic[0][2],
|
|
ic[1][0], ic[1][1], ic[1][2],
|
|
ic[2][0], ic[2][1], ic[2][2] );
|
|
}
|
|
if ( body.linearFriction != -1 ) {
|
|
f->WriteFloatString( "\tfriction %f, %f, %f\n", body.linearFriction, body.angularFriction, body.contactFriction );
|
|
}
|
|
f->WriteFloatString( "\tcontents %s\n", ContentsToString( body.contents, str ) );
|
|
f->WriteFloatString( "\tclipMask %s\n", ContentsToString( body.clipMask, str ) );
|
|
f->WriteFloatString( "\tselfCollision %d\n", body.selfCollision );
|
|
if ( body.frictionDirection.ToVec3() != vec3_origin ) {
|
|
f->WriteFloatString( "\tfrictionDirection " );
|
|
body.frictionDirection.Write( f );
|
|
f->WriteFloatString( "\n" );
|
|
}
|
|
if ( body.contactMotorDirection.ToVec3() != vec3_origin ) {
|
|
f->WriteFloatString( "\tcontactMotorDirection " );
|
|
body.contactMotorDirection.Write( f );
|
|
f->WriteFloatString( "\n" );
|
|
}
|
|
f->WriteFloatString( "\tcontainedJoints \"%s\"\n", body.containedJoints.c_str() );
|
|
f->WriteFloatString( "}\n" );
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idDeclAF::WriteFixed
|
|
================
|
|
*/
|
|
bool idDeclAF::WriteFixed( idFile *f, const idDeclAF_Constraint &c ) const {
|
|
f->WriteFloatString( "\nfixed \"%s\" {\n", c.name.c_str() );
|
|
f->WriteFloatString( "\tbody1 \"%s\"\n", c.body1.c_str() );
|
|
f->WriteFloatString( "\tbody2 \"%s\"\n", c.body2.c_str() );
|
|
f->WriteFloatString( "}\n" );
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idDeclAF::WriteBallAndSocketJoint
|
|
================
|
|
*/
|
|
bool idDeclAF::WriteBallAndSocketJoint( idFile *f, const idDeclAF_Constraint &c ) const {
|
|
f->WriteFloatString( "\nballAndSocketJoint \"%s\" {\n", c.name.c_str() );
|
|
f->WriteFloatString( "\tbody1 \"%s\"\n", c.body1.c_str() );
|
|
f->WriteFloatString( "\tbody2 \"%s\"\n", c.body2.c_str() );
|
|
f->WriteFloatString( "\tanchor " );
|
|
c.anchor.Write( f );
|
|
f->WriteFloatString( "\n" );
|
|
f->WriteFloatString( "\tfriction %f\n", c.friction );
|
|
if ( c.limit == idDeclAF_Constraint::LIMIT_CONE ) {
|
|
f->WriteFloatString( "\tconeLimit " );
|
|
c.limitAxis.Write( f );
|
|
f->WriteFloatString( ", %f, ", c.limitAngles[0] );
|
|
c.shaft[0].Write( f );
|
|
f->WriteFloatString( "\n" );
|
|
}
|
|
else if ( c.limit == idDeclAF_Constraint::LIMIT_PYRAMID ) {
|
|
f->WriteFloatString( "\tpyramidLimit " );
|
|
c.limitAxis.Write( f );
|
|
f->WriteFloatString( ", %f, %f, %f, ", c.limitAngles[0], c.limitAngles[1], c.limitAngles[2] );
|
|
c.shaft[0].Write( f );
|
|
f->WriteFloatString( "\n" );
|
|
}
|
|
f->WriteFloatString( "}\n" );
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idDeclAF::WriteUniversalJoint
|
|
================
|
|
*/
|
|
bool idDeclAF::WriteUniversalJoint( idFile *f, const idDeclAF_Constraint &c ) const {
|
|
f->WriteFloatString( "\nuniversalJoint \"%s\" {\n", c.name.c_str() );
|
|
f->WriteFloatString( "\tbody1 \"%s\"\n", c.body1.c_str() );
|
|
f->WriteFloatString( "\tbody2 \"%s\"\n", c.body2.c_str() );
|
|
f->WriteFloatString( "\tanchor " );
|
|
c.anchor.Write( f );
|
|
f->WriteFloatString( "\n" );
|
|
f->WriteFloatString( "\tshafts " );
|
|
c.shaft[0].Write( f );
|
|
f->WriteFloatString( ", " );
|
|
c.shaft[1].Write( f );
|
|
f->WriteFloatString( "\n" );
|
|
f->WriteFloatString( "\tfriction %f\n", c.friction );
|
|
if ( c.limit == idDeclAF_Constraint::LIMIT_CONE ) {
|
|
f->WriteFloatString( "\tconeLimit " );
|
|
c.limitAxis.Write( f );
|
|
f->WriteFloatString( ", %f\n", c.limitAngles[0] );
|
|
}
|
|
else if ( c.limit == idDeclAF_Constraint::LIMIT_PYRAMID ) {
|
|
f->WriteFloatString( "\tpyramidLimit " );
|
|
c.limitAxis.Write( f );
|
|
f->WriteFloatString( ", %f, %f, %f\n", c.limitAngles[0], c.limitAngles[1], c.limitAngles[2] );
|
|
}
|
|
f->WriteFloatString( "}\n" );
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idDeclAF::WriteHinge
|
|
================
|
|
*/
|
|
bool idDeclAF::WriteHinge( idFile *f, const idDeclAF_Constraint &c ) const {
|
|
f->WriteFloatString( "\nhinge \"%s\" {\n", c.name.c_str() );
|
|
f->WriteFloatString( "\tbody1 \"%s\"\n", c.body1.c_str() );
|
|
f->WriteFloatString( "\tbody2 \"%s\"\n", c.body2.c_str() );
|
|
f->WriteFloatString( "\tanchor " );
|
|
c.anchor.Write( f );
|
|
f->WriteFloatString( "\n" );
|
|
f->WriteFloatString( "\taxis " );
|
|
c.axis.Write( f );
|
|
f->WriteFloatString( "\n" );
|
|
f->WriteFloatString( "\tfriction %f\n", c.friction );
|
|
if ( c.limit == idDeclAF_Constraint::LIMIT_CONE ) {
|
|
f->WriteFloatString( "\tlimit " );
|
|
f->WriteFloatString( "%f, %f, %f", c.limitAngles[0], c.limitAngles[1], c.limitAngles[2] );
|
|
f->WriteFloatString( "\n" );
|
|
}
|
|
f->WriteFloatString( "}\n" );
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idDeclAF::WriteSlider
|
|
================
|
|
*/
|
|
bool idDeclAF::WriteSlider( idFile *f, const idDeclAF_Constraint &c ) const {
|
|
f->WriteFloatString( "\nslider \"%s\" {\n", c.name.c_str() );
|
|
f->WriteFloatString( "\tbody1 \"%s\"\n", c.body1.c_str() );
|
|
f->WriteFloatString( "\tbody2 \"%s\"\n", c.body2.c_str() );
|
|
f->WriteFloatString( "\taxis " );
|
|
c.axis.Write( f );
|
|
f->WriteFloatString( "\n" );
|
|
f->WriteFloatString( "\tfriction %f\n", c.friction );
|
|
f->WriteFloatString( "}\n" );
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idDeclAF::WriteSpring
|
|
================
|
|
*/
|
|
bool idDeclAF::WriteSpring( idFile *f, const idDeclAF_Constraint &c ) const {
|
|
f->WriteFloatString( "\nspring \"%s\" {\n", c.name.c_str() );
|
|
f->WriteFloatString( "\tbody1 \"%s\"\n", c.body1.c_str() );
|
|
f->WriteFloatString( "\tbody2 \"%s\"\n", c.body2.c_str() );
|
|
f->WriteFloatString( "\tanchor1 " );
|
|
c.anchor.Write( f );
|
|
f->WriteFloatString( "\n" );
|
|
f->WriteFloatString( "\tanchor2 " );
|
|
c.anchor2.Write( f );
|
|
f->WriteFloatString( "\n" );
|
|
f->WriteFloatString( "\tfriction %f\n", c.friction );
|
|
f->WriteFloatString( "\tstretch %f\n", c.stretch );
|
|
f->WriteFloatString( "\tcompress %f\n", c.compress );
|
|
f->WriteFloatString( "\tdamping %f\n", c.damping );
|
|
f->WriteFloatString( "\trestLength %f\n", c.restLength );
|
|
f->WriteFloatString( "\tminLength %f\n", c.minLength );
|
|
f->WriteFloatString( "\tmaxLength %f\n", c.maxLength );
|
|
f->WriteFloatString( "}\n" );
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idDeclAF::WriteConstraint
|
|
================
|
|
*/
|
|
bool idDeclAF::WriteConstraint( idFile *f, const idDeclAF_Constraint &c ) const {
|
|
switch( c.type ) {
|
|
case DECLAF_CONSTRAINT_FIXED:
|
|
return WriteFixed( f, c );
|
|
case DECLAF_CONSTRAINT_BALLANDSOCKETJOINT:
|
|
return WriteBallAndSocketJoint( f, c );
|
|
case DECLAF_CONSTRAINT_UNIVERSALJOINT:
|
|
return WriteUniversalJoint( f, c );
|
|
case DECLAF_CONSTRAINT_HINGE:
|
|
return WriteHinge( f, c );
|
|
case DECLAF_CONSTRAINT_SLIDER:
|
|
return WriteSlider( f, c );
|
|
case DECLAF_CONSTRAINT_SPRING:
|
|
return WriteSpring( f, c );
|
|
default:
|
|
break;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idDeclAF::WriteSettings
|
|
================
|
|
*/
|
|
bool idDeclAF::WriteSettings( idFile *f ) const {
|
|
idStr str;
|
|
|
|
f->WriteFloatString( "\nsettings {\n" );
|
|
f->WriteFloatString( "\tmodel \"%s\"\n", model.c_str() );
|
|
f->WriteFloatString( "\tskin \"%s\"\n", skin.c_str() );
|
|
f->WriteFloatString( "\tfriction %f, %f, %f, %f\n", defaultLinearFriction, defaultAngularFriction, defaultContactFriction, defaultConstraintFriction );
|
|
f->WriteFloatString( "\tsuspendSpeed %f, %f, %f, %f\n", suspendVelocity[0], suspendVelocity[1], suspendAcceleration[0], suspendAcceleration[1] );
|
|
f->WriteFloatString( "\tnoMoveTime %f\n", noMoveTime );
|
|
f->WriteFloatString( "\tnoMoveTranslation %f\n", noMoveTranslation );
|
|
f->WriteFloatString( "\tnoMoveRotation %f\n", noMoveRotation );
|
|
f->WriteFloatString( "\tminMoveTime %f\n", minMoveTime );
|
|
f->WriteFloatString( "\tmaxMoveTime %f\n", maxMoveTime );
|
|
f->WriteFloatString( "\ttotalMass %f\n", totalMass );
|
|
f->WriteFloatString( "\tcontents %s\n", ContentsToString( contents, str ) );
|
|
f->WriteFloatString( "\tclipMask %s\n", ContentsToString( clipMask, str ) );
|
|
f->WriteFloatString( "\tselfCollision %d\n", selfCollision );
|
|
f->WriteFloatString( "}\n" );
|
|
return true;
|
|
}
|
|
|
|
|
|
/*
|
|
================
|
|
idDeclAF::RebuildTextSource
|
|
================
|
|
*/
|
|
bool idDeclAF::RebuildTextSource( void ) {
|
|
int i;
|
|
idFile_Memory f;
|
|
|
|
f.WriteFloatString("\n\n/*\n"
|
|
"\tGenerated by the Articulated Figure Editor.\n"
|
|
"\tDo not edit directly but launch the game and type 'editAFs' on the console.\n"
|
|
"*/\n" );
|
|
|
|
f.WriteFloatString( "\narticulatedFigure %s {\n", GetName() );
|
|
|
|
if ( !WriteSettings( &f ) ) {
|
|
return false;
|
|
}
|
|
|
|
for ( i = 0; i < bodies.Num(); i++ ) {
|
|
if ( !WriteBody( &f, *bodies[i] ) ) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
for ( i = 0; i < constraints.Num(); i++ ) {
|
|
if ( !WriteConstraint( &f, *constraints[i] ) ) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
f.WriteFloatString( "\n}" );
|
|
|
|
SetText( f.GetDataPtr() );
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idDeclAF::Save
|
|
================
|
|
*/
|
|
bool idDeclAF::Save( void ) {
|
|
RebuildTextSource();
|
|
ReplaceSourceFileText();
|
|
modified = false;
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idDeclAF::ContentsFromString
|
|
================
|
|
*/
|
|
int idDeclAF::ContentsFromString( const char *str ) {
|
|
int c;
|
|
idToken token;
|
|
idLexer src( str, idStr::Length( str ), "idDeclAF::ContentsFromString" );
|
|
|
|
c = 0;
|
|
while( src.ReadToken( &token ) ) {
|
|
if ( token.Icmp( "none" ) == 0 ) {
|
|
c = 0;
|
|
}
|
|
else if ( token.Icmp( "solid" ) == 0 ) {
|
|
c |= CONTENTS_SOLID;
|
|
}
|
|
else if ( token.Icmp( "body" ) == 0 ) {
|
|
c |= CONTENTS_BODY;
|
|
}
|
|
else if ( token.Icmp( "corpse" ) == 0 ) {
|
|
c |= CONTENTS_CORPSE;
|
|
}
|
|
else if ( token.Icmp( "playerclip" ) == 0 ) {
|
|
c |= CONTENTS_PLAYERCLIP;
|
|
}
|
|
else if ( token.Icmp( "monsterclip" ) == 0 ) {
|
|
c |= CONTENTS_MONSTERCLIP;
|
|
}
|
|
else if ( token == "," ) {
|
|
continue;
|
|
}
|
|
else {
|
|
return c;
|
|
}
|
|
}
|
|
return c;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idDeclAF::ContentsToString
|
|
================
|
|
*/
|
|
const char *idDeclAF::ContentsToString( const int contents, idStr &str ) {
|
|
str = "";
|
|
if ( contents & CONTENTS_SOLID ) {
|
|
if ( str.Length() ) str += ", ";
|
|
str += "solid";
|
|
}
|
|
if ( contents & CONTENTS_BODY ) {
|
|
if ( str.Length() ) str += ", ";
|
|
str += "body";
|
|
}
|
|
if ( contents & CONTENTS_CORPSE ) {
|
|
if ( str.Length() ) str += ", ";
|
|
str += "corpse";
|
|
}
|
|
if ( contents & CONTENTS_PLAYERCLIP ) {
|
|
if ( str.Length() ) str += ", ";
|
|
str += "playerclip";
|
|
}
|
|
if ( contents & CONTENTS_MONSTERCLIP ) {
|
|
if ( str.Length() ) str += ", ";
|
|
str += "monsterclip";
|
|
}
|
|
if ( str[0] == '\0' ) {
|
|
str = "none";
|
|
}
|
|
return str.c_str();
|
|
}
|
|
|
|
/*
|
|
================
|
|
idDeclAF::JointModFromString
|
|
================
|
|
*/
|
|
declAFJointMod_t idDeclAF::JointModFromString( const char *str ) {
|
|
if ( idStr::Icmp( str, "orientation" ) == 0 ) {
|
|
return DECLAF_JOINTMOD_AXIS;
|
|
}
|
|
if ( idStr::Icmp( str, "position" ) == 0 ) {
|
|
return DECLAF_JOINTMOD_ORIGIN;
|
|
}
|
|
if ( idStr::Icmp( str, "both" ) == 0 ) {
|
|
return DECLAF_JOINTMOD_BOTH;
|
|
}
|
|
return DECLAF_JOINTMOD_AXIS;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idDeclAF::JointModToString
|
|
================
|
|
*/
|
|
const char * idDeclAF::JointModToString( declAFJointMod_t jointMod ) {
|
|
switch( jointMod ) {
|
|
case DECLAF_JOINTMOD_AXIS: {
|
|
return "orientation";
|
|
}
|
|
case DECLAF_JOINTMOD_ORIGIN: {
|
|
return "position";
|
|
}
|
|
case DECLAF_JOINTMOD_BOTH: {
|
|
return "both";
|
|
}
|
|
}
|
|
return "orientation";
|
|
}
|
|
|
|
/*
|
|
=================
|
|
idDeclAF::Size
|
|
=================
|
|
*/
|
|
size_t idDeclAF::Size( void ) const {
|
|
return sizeof( idDeclAF );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idDeclAF::ParseContents
|
|
================
|
|
*/
|
|
bool idDeclAF::ParseContents( idLexer &src, int &c ) const {
|
|
idToken token;
|
|
idStr str;
|
|
|
|
while( src.ReadToken( &token ) ) {
|
|
str += token;
|
|
if ( !src.CheckTokenString( "," ) ) {
|
|
break;
|
|
}
|
|
str += ",";
|
|
}
|
|
c = ContentsFromString( str );
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idDeclAF::ParseBody
|
|
================
|
|
*/
|
|
bool idDeclAF::ParseBody( idLexer &src ) {
|
|
bool hasJoint = false;
|
|
idToken token;
|
|
idAFVector angles;
|
|
idDeclAF_Body *body = new idDeclAF_Body;
|
|
|
|
bodies.Alloc() = body;
|
|
|
|
body->SetDefault( this );
|
|
|
|
if ( !src.ExpectTokenType( TT_STRING, 0, &token ) ||
|
|
!src.ExpectTokenString( "{" ) ) {
|
|
return false;
|
|
}
|
|
|
|
body->name = token;
|
|
if ( !body->name.Icmp( "origin" ) || !body->name.Icmp( "world" ) ) {
|
|
src.Error( "a body may not be named \"origin\" or \"world\"" );
|
|
return false;
|
|
}
|
|
|
|
while( src.ReadToken( &token ) ) {
|
|
|
|
if ( !token.Icmp( "model" ) ) {
|
|
if ( !src.ExpectTokenType( TT_NAME, 0, &token ) ) {
|
|
return false;
|
|
}
|
|
if ( !token.Icmp( "box" ) ) {
|
|
body->modelType = TRM_BOX;
|
|
if ( !src.ExpectTokenString( "(" ) ||
|
|
!body->v1.Parse( src ) ||
|
|
!src.ExpectTokenString( "," ) ||
|
|
!body->v2.Parse( src ) ||
|
|
!src.ExpectTokenString( ")" ) ) {
|
|
return false;
|
|
}
|
|
} else if ( !token.Icmp( "octahedron" ) ) {
|
|
body->modelType = TRM_OCTAHEDRON;
|
|
if ( !src.ExpectTokenString( "(" ) ||
|
|
!body->v1.Parse( src ) ||
|
|
!src.ExpectTokenString( "," ) ||
|
|
!body->v2.Parse( src ) ||
|
|
!src.ExpectTokenString( ")" ) ) {
|
|
return false;
|
|
}
|
|
} else if ( !token.Icmp( "dodecahedron" ) ) {
|
|
body->modelType = TRM_DODECAHEDRON;
|
|
if ( !src.ExpectTokenString( "(" ) ||
|
|
!body->v1.Parse( src ) ||
|
|
!src.ExpectTokenString( "," ) ||
|
|
!body->v2.Parse( src ) ||
|
|
!src.ExpectTokenString( ")" ) ) {
|
|
return false;
|
|
}
|
|
} else if ( !token.Icmp( "cylinder" ) ) {
|
|
body->modelType = TRM_CYLINDER;
|
|
if ( !src.ExpectTokenString( "(" ) ||
|
|
!body->v1.Parse( src ) ||
|
|
!src.ExpectTokenString( "," ) ||
|
|
!body->v2.Parse( src ) ||
|
|
!src.ExpectTokenString( "," ) ) {
|
|
return false;
|
|
}
|
|
body->numSides = src.ParseInt();
|
|
if ( !src.ExpectTokenString( ")" ) ) {
|
|
return false;
|
|
}
|
|
} else if ( !token.Icmp( "cone" ) ) {
|
|
body->modelType = TRM_CONE;
|
|
if ( !src.ExpectTokenString( "(" ) ||
|
|
!body->v1.Parse( src ) ||
|
|
!src.ExpectTokenString( "," ) ||
|
|
!body->v2.Parse( src ) ||
|
|
!src.ExpectTokenString( "," ) ) {
|
|
return false;
|
|
}
|
|
body->numSides = src.ParseInt();
|
|
if ( !src.ExpectTokenString( ")" ) ) {
|
|
return false;
|
|
}
|
|
} else if ( !token.Icmp( "bone" ) ) {
|
|
body->modelType = TRM_BONE;
|
|
if ( !src.ExpectTokenString( "(" ) ||
|
|
!body->v1.Parse( src ) ||
|
|
!src.ExpectTokenString( "," ) ||
|
|
!body->v2.Parse( src ) ||
|
|
!src.ExpectTokenString( "," ) ) {
|
|
return false;
|
|
}
|
|
body->width = src.ParseFloat();
|
|
if ( !src.ExpectTokenString( ")" ) ) {
|
|
return false;
|
|
}
|
|
} else if ( !token.Icmp( "custom" ) ) {
|
|
src.Error( "custom models not yet implemented" );
|
|
return false;
|
|
} else {
|
|
src.Error( "unkown model type %s", token.c_str() );
|
|
return false;
|
|
}
|
|
} else if ( !token.Icmp( "origin" ) ) {
|
|
if ( !body->origin.Parse( src ) ) {
|
|
return false;
|
|
}
|
|
} else if ( !token.Icmp( "angles" ) ) {
|
|
if ( !angles.Parse( src ) ) {
|
|
return false;
|
|
}
|
|
body->angles = idAngles( angles.ToVec3().x, angles.ToVec3().y, angles.ToVec3().z );
|
|
} else if ( !token.Icmp( "joint" ) ) {
|
|
if ( !src.ExpectTokenType( TT_STRING, 0, &token ) ) {
|
|
return false;
|
|
}
|
|
body->jointName = token;
|
|
hasJoint = true;
|
|
} else if ( !token.Icmp( "mod" ) ) {
|
|
if ( !src.ExpectAnyToken( &token ) ) {
|
|
return false;
|
|
}
|
|
body->jointMod = JointModFromString( token.c_str() );
|
|
} else if ( !token.Icmp( "density" ) ) {
|
|
body->density = src.ParseFloat();
|
|
} else if ( !token.Icmp( "inertiaScale" ) ) {
|
|
src.Parse1DMatrix( 9, body->inertiaScale[0].ToFloatPtr() );
|
|
} else if ( !token.Icmp( "friction" ) ) {
|
|
body->linearFriction = src.ParseFloat();
|
|
src.ExpectTokenString( "," );
|
|
body->angularFriction = src.ParseFloat();
|
|
src.ExpectTokenString( "," );
|
|
body->contactFriction = src.ParseFloat();
|
|
} else if ( !token.Icmp( "contents" ) ) {
|
|
ParseContents( src, body->contents );
|
|
} else if ( !token.Icmp( "clipMask" ) ) {
|
|
ParseContents( src, body->clipMask );
|
|
} else if ( !token.Icmp( "selfCollision" ) ) {
|
|
body->selfCollision = src.ParseBool();
|
|
} else if ( !token.Icmp( "containedjoints" ) ) {
|
|
if ( !src.ExpectTokenType( TT_STRING, 0, &token ) ) {
|
|
return false;
|
|
}
|
|
body->containedJoints = token;
|
|
} else if ( !token.Icmp( "frictionDirection" ) ) {
|
|
if ( !body->frictionDirection.Parse( src ) ) {
|
|
return false;
|
|
}
|
|
} else if ( !token.Icmp( "contactMotorDirection" ) ) {
|
|
if ( !body->contactMotorDirection.Parse( src ) ) {
|
|
return false;
|
|
}
|
|
} else if ( token == "}" ) {
|
|
break;
|
|
} else {
|
|
src.Error( "unknown token %s in body", token.c_str() );
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if ( body->modelType == TRM_INVALID ) {
|
|
src.Error( "no model set for body" );
|
|
return false;
|
|
}
|
|
|
|
if ( !hasJoint ) {
|
|
src.Error( "no joint set for body" );
|
|
return false;
|
|
}
|
|
|
|
body->clipMask |= CONTENTS_MOVEABLECLIP;
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idDeclAF::ParseFixed
|
|
================
|
|
*/
|
|
bool idDeclAF::ParseFixed( idLexer &src ) {
|
|
idToken token;
|
|
idDeclAF_Constraint *constraint = new idDeclAF_Constraint;
|
|
|
|
constraint->SetDefault( this );
|
|
constraints.Alloc() = constraint;
|
|
|
|
if ( !src.ExpectTokenType( TT_STRING, 0, &token ) ||
|
|
!src.ExpectTokenString( "{" ) ) {
|
|
return false;
|
|
}
|
|
|
|
constraint->type = DECLAF_CONSTRAINT_FIXED;
|
|
constraint->name = token;
|
|
|
|
while( src.ReadToken( &token ) ) {
|
|
|
|
if ( !token.Icmp( "body1" ) ) {
|
|
src.ExpectTokenType( TT_STRING, 0, &token );
|
|
constraint->body1 = token;
|
|
} else if ( !token.Icmp( "body2" ) ) {
|
|
src.ExpectTokenType( TT_STRING, 0, &token );
|
|
constraint->body2 = token;
|
|
} else if ( token == "}" ) {
|
|
break;
|
|
} else {
|
|
src.Error( "unknown token %s in ball and socket joint", token.c_str() );
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idDeclAF::ParseBallAndSocketJoint
|
|
================
|
|
*/
|
|
bool idDeclAF::ParseBallAndSocketJoint( idLexer &src ) {
|
|
idToken token;
|
|
idDeclAF_Constraint *constraint = new idDeclAF_Constraint;
|
|
|
|
constraint->SetDefault( this );
|
|
constraints.Alloc() = constraint;
|
|
|
|
if ( !src.ExpectTokenType( TT_STRING, 0, &token ) ||
|
|
!src.ExpectTokenString( "{" ) ) {
|
|
return false;
|
|
}
|
|
|
|
constraint->type = DECLAF_CONSTRAINT_BALLANDSOCKETJOINT;
|
|
constraint->limit = idDeclAF_Constraint::LIMIT_NONE;
|
|
constraint->name = token;
|
|
constraint->friction = 0.5f;
|
|
constraint->anchor.ToVec3().Zero();
|
|
constraint->shaft[0].ToVec3().Zero();
|
|
|
|
while( src.ReadToken( &token ) ) {
|
|
|
|
if ( !token.Icmp( "body1" ) ) {
|
|
src.ExpectTokenType( TT_STRING, 0, &token );
|
|
constraint->body1 = token;
|
|
} else if ( !token.Icmp( "body2" ) ) {
|
|
src.ExpectTokenType( TT_STRING, 0, &token );
|
|
constraint->body2 = token;
|
|
} else if ( !token.Icmp( "anchor" ) ) {
|
|
if ( !constraint->anchor.Parse( src ) ) {
|
|
return false;
|
|
}
|
|
} else if ( !token.Icmp( "conelimit" ) ) {
|
|
if ( !constraint->limitAxis.Parse( src ) ||
|
|
!src.ExpectTokenString( "," ) ) {
|
|
return false;
|
|
}
|
|
constraint->limitAngles[0] = src.ParseFloat();
|
|
if ( !src.ExpectTokenString( "," ) ||
|
|
!constraint->shaft[0].Parse( src ) ) {
|
|
return false;
|
|
}
|
|
constraint->limit = idDeclAF_Constraint::LIMIT_CONE;
|
|
} else if ( !token.Icmp( "pyramidlimit" ) ) {
|
|
if ( !constraint->limitAxis.Parse( src ) ||
|
|
!src.ExpectTokenString( "," ) ) {
|
|
return false;
|
|
}
|
|
constraint->limitAngles[0] = src.ParseFloat();
|
|
if ( !src.ExpectTokenString( "," ) ) {
|
|
return false;
|
|
}
|
|
constraint->limitAngles[1] = src.ParseFloat();
|
|
if ( !src.ExpectTokenString( "," ) ) {
|
|
return false;
|
|
}
|
|
constraint->limitAngles[2] = src.ParseFloat();
|
|
if ( !src.ExpectTokenString( "," ) ||
|
|
!constraint->shaft[0].Parse( src ) ) {
|
|
return false;
|
|
}
|
|
constraint->limit = idDeclAF_Constraint::LIMIT_PYRAMID;
|
|
} else if ( !token.Icmp( "friction" ) ) {
|
|
constraint->friction = src.ParseFloat();
|
|
} else if ( token == "}" ) {
|
|
break;
|
|
} else {
|
|
src.Error( "unknown token %s in ball and socket joint", token.c_str() );
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idDeclAF::ParseUniversalJoint
|
|
================
|
|
*/
|
|
bool idDeclAF::ParseUniversalJoint( idLexer &src ) {
|
|
idToken token;
|
|
idDeclAF_Constraint *constraint = new idDeclAF_Constraint;
|
|
|
|
constraint->SetDefault( this );
|
|
constraints.Alloc() = constraint;
|
|
|
|
if ( !src.ExpectTokenType( TT_STRING, 0, &token ) ||
|
|
!src.ExpectTokenString( "{" ) ) {
|
|
return false;
|
|
}
|
|
|
|
constraint->type = DECLAF_CONSTRAINT_UNIVERSALJOINT;
|
|
constraint->limit = idDeclAF_Constraint::LIMIT_NONE;
|
|
constraint->name = token;
|
|
constraint->friction = 0.5f;
|
|
constraint->anchor.ToVec3().Zero();
|
|
constraint->shaft[0].ToVec3().Zero();
|
|
constraint->shaft[1].ToVec3().Zero();
|
|
|
|
while( src.ReadToken( &token ) ) {
|
|
|
|
if ( !token.Icmp( "body1" ) ) {
|
|
src.ExpectTokenType( TT_STRING, 0, &token );
|
|
constraint->body1 = token;
|
|
} else if ( !token.Icmp( "body2" ) ) {
|
|
src.ExpectTokenType( TT_STRING, 0, &token );
|
|
constraint->body2 = token;
|
|
} else if ( !token.Icmp( "anchor" ) ) {
|
|
if ( !constraint->anchor.Parse( src ) ) {
|
|
return false;
|
|
}
|
|
} else if ( !token.Icmp( "shafts" ) ) {
|
|
if ( !constraint->shaft[0].Parse( src ) ||
|
|
!src.ExpectTokenString( "," ) ||
|
|
!constraint->shaft[1].Parse( src ) ) {
|
|
return false;
|
|
}
|
|
} else if ( !token.Icmp( "conelimit" ) ) {
|
|
if ( !constraint->limitAxis.Parse( src ) ||
|
|
!src.ExpectTokenString( "," ) ) {
|
|
return false;
|
|
}
|
|
constraint->limitAngles[0] = src.ParseFloat();
|
|
constraint->limit = idDeclAF_Constraint::LIMIT_CONE;
|
|
} else if ( !token.Icmp( "pyramidlimit" ) ) {
|
|
if ( !constraint->limitAxis.Parse( src ) ||
|
|
!src.ExpectTokenString( "," ) ) {
|
|
return false;
|
|
}
|
|
constraint->limitAngles[0] = src.ParseFloat();
|
|
if ( !src.ExpectTokenString( "," ) ) {
|
|
return false;
|
|
}
|
|
constraint->limitAngles[1] = src.ParseFloat();
|
|
if ( !src.ExpectTokenString( "," ) ) {
|
|
return false;
|
|
}
|
|
constraint->limitAngles[2] = src.ParseFloat();
|
|
constraint->limit = idDeclAF_Constraint::LIMIT_PYRAMID;
|
|
} else if ( !token.Icmp( "friction" ) ) {
|
|
constraint->friction = src.ParseFloat();
|
|
} else if ( token == "}" ) {
|
|
break;
|
|
} else {
|
|
src.Error( "unknown token %s in universal joint", token.c_str() );
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idDeclAF::ParseHinge
|
|
================
|
|
*/
|
|
bool idDeclAF::ParseHinge( idLexer &src ) {
|
|
idToken token;
|
|
idDeclAF_Constraint *constraint = new idDeclAF_Constraint;
|
|
|
|
constraint->SetDefault( this );
|
|
constraints.Alloc() = constraint;
|
|
|
|
if ( !src.ExpectTokenType( TT_STRING, 0, &token ) ||
|
|
!src.ExpectTokenString( "{" ) ) {
|
|
return false;
|
|
}
|
|
|
|
constraint->type = DECLAF_CONSTRAINT_HINGE;
|
|
constraint->limit = idDeclAF_Constraint::LIMIT_NONE;
|
|
constraint->name = token;
|
|
constraint->friction = 0.5f;
|
|
constraint->anchor.ToVec3().Zero();
|
|
constraint->axis.ToVec3().Zero();
|
|
|
|
while( src.ReadToken( &token ) ) {
|
|
|
|
if ( !token.Icmp( "body1" ) ) {
|
|
src.ExpectTokenType( TT_STRING, 0, &token );
|
|
constraint->body1 = token;
|
|
} else if ( !token.Icmp( "body2" ) ) {
|
|
src.ExpectTokenType( TT_STRING, 0, &token );
|
|
constraint->body2 = token;
|
|
} else if ( !token.Icmp( "anchor" ) ) {
|
|
if ( !constraint->anchor.Parse( src ) ) {
|
|
return false;
|
|
}
|
|
} else if ( !token.Icmp( "axis" ) ) {
|
|
if ( !constraint->axis.Parse( src ) ) {
|
|
return false;
|
|
}
|
|
} else if ( !token.Icmp( "limit" ) ) {
|
|
constraint->limitAngles[0] = src.ParseFloat();
|
|
if ( !src.ExpectTokenString( "," ) ) {
|
|
return false;
|
|
}
|
|
constraint->limitAngles[1] = src.ParseFloat();
|
|
if ( !src.ExpectTokenString( "," ) ) {
|
|
return false;
|
|
}
|
|
constraint->limitAngles[2] = src.ParseFloat();
|
|
constraint->limit = idDeclAF_Constraint::LIMIT_CONE;
|
|
} else if ( !token.Icmp( "friction" ) ) {
|
|
constraint->friction = src.ParseFloat();
|
|
} else if ( token == "}" ) {
|
|
break;
|
|
} else {
|
|
src.Error( "unknown token %s in hinge", token.c_str() );
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idDeclAF::ParseSlider
|
|
================
|
|
*/
|
|
bool idDeclAF::ParseSlider( idLexer &src ) {
|
|
idToken token;
|
|
idDeclAF_Constraint *constraint = new idDeclAF_Constraint;
|
|
|
|
constraint->SetDefault( this );
|
|
constraints.Alloc() = constraint;
|
|
|
|
if ( !src.ExpectTokenType( TT_STRING, 0, &token ) ||
|
|
!src.ExpectTokenString( "{" ) ) {
|
|
return false;
|
|
}
|
|
|
|
constraint->type = DECLAF_CONSTRAINT_SLIDER;
|
|
constraint->limit = idDeclAF_Constraint::LIMIT_NONE;
|
|
constraint->name = token;
|
|
constraint->friction = 0.5f;
|
|
|
|
while( src.ReadToken( &token ) ) {
|
|
|
|
if ( !token.Icmp( "body1" ) ) {
|
|
src.ExpectTokenType( TT_STRING, 0, &token );
|
|
constraint->body1 = token;
|
|
} else if ( !token.Icmp( "body2" ) ) {
|
|
src.ExpectTokenType( TT_STRING, 0, &token );
|
|
constraint->body2 = token;
|
|
} else if ( !token.Icmp( "axis" ) ) {
|
|
if ( !constraint->axis.Parse( src ) ) {
|
|
return false;
|
|
}
|
|
} else if ( !token.Icmp( "friction" ) ) {
|
|
constraint->friction = src.ParseFloat();
|
|
} else if ( token == "}" ) {
|
|
break;
|
|
} else {
|
|
src.Error( "unknown token %s in slider", token.c_str() );
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idDeclAF::ParseSpring
|
|
================
|
|
*/
|
|
bool idDeclAF::ParseSpring( idLexer &src ) {
|
|
idToken token;
|
|
idDeclAF_Constraint *constraint = new idDeclAF_Constraint;
|
|
|
|
constraint->SetDefault( this );
|
|
constraints.Alloc() = constraint;
|
|
|
|
if ( !src.ExpectTokenType( TT_STRING, 0, &token ) ||
|
|
!src.ExpectTokenString( "{" ) ) {
|
|
return false;
|
|
}
|
|
|
|
constraint->type = DECLAF_CONSTRAINT_SPRING;
|
|
constraint->limit = idDeclAF_Constraint::LIMIT_NONE;
|
|
constraint->name = token;
|
|
constraint->friction = 0.5f;
|
|
|
|
while( src.ReadToken( &token ) ) {
|
|
|
|
if ( !token.Icmp( "body1" ) ) {
|
|
src.ExpectTokenType( TT_STRING, 0, &token );
|
|
constraint->body1 = token;
|
|
} else if ( !token.Icmp( "body2" ) ) {
|
|
src.ExpectTokenType( TT_STRING, 0, &token );
|
|
constraint->body2 = token;
|
|
} else if ( !token.Icmp( "anchor1" ) ) {
|
|
if ( !constraint->anchor.Parse( src ) ) {
|
|
return false;
|
|
}
|
|
} else if ( !token.Icmp( "anchor2" ) ) {
|
|
if ( !constraint->anchor2.Parse( src ) ) {
|
|
return false;
|
|
}
|
|
} else if ( !token.Icmp( "friction" ) ) {
|
|
constraint->friction = src.ParseFloat();
|
|
} else if ( !token.Icmp( "stretch" ) ) {
|
|
constraint->stretch = src.ParseFloat();
|
|
} else if ( !token.Icmp( "compress" ) ) {
|
|
constraint->compress = src.ParseFloat();
|
|
} else if ( !token.Icmp( "damping" ) ) {
|
|
constraint->damping = src.ParseFloat();
|
|
} else if ( !token.Icmp( "restLength" ) ) {
|
|
constraint->restLength = src.ParseFloat();
|
|
} else if ( !token.Icmp( "minLength" ) ) {
|
|
constraint->minLength = src.ParseFloat();
|
|
} else if ( !token.Icmp( "maxLength" ) ) {
|
|
constraint->maxLength = src.ParseFloat();
|
|
} else if ( token == "}" ) {
|
|
break;
|
|
} else {
|
|
src.Error( "unknown token %s in spring", token.c_str() );
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idDeclAF::ParseSettings
|
|
================
|
|
*/
|
|
bool idDeclAF::ParseSettings( idLexer &src ) {
|
|
idToken token;
|
|
|
|
if ( !src.ExpectTokenString( "{" ) ) {
|
|
return false;
|
|
}
|
|
|
|
while( src.ReadToken( &token ) ) {
|
|
|
|
if ( !token.Icmp( "mesh" ) ) {
|
|
if ( !src.ExpectTokenType( TT_STRING, 0, &token ) ) {
|
|
return false;
|
|
}
|
|
} else if ( !token.Icmp( "anim" ) ) {
|
|
if ( !src.ExpectTokenType( TT_STRING, 0, &token ) ) {
|
|
return false;
|
|
}
|
|
} else if ( !token.Icmp( "model" ) ) {
|
|
if ( !src.ExpectTokenType( TT_STRING, 0, &token ) ) {
|
|
return false;
|
|
}
|
|
model = token;
|
|
} else if ( !token.Icmp( "skin" ) ) {
|
|
if ( !src.ExpectTokenType( TT_STRING, 0, &token ) ) {
|
|
return false;
|
|
}
|
|
skin = token;
|
|
} else if ( !token.Icmp( "friction" ) ) {
|
|
|
|
defaultLinearFriction = src.ParseFloat();
|
|
if ( !src.ExpectTokenString( "," ) ) {
|
|
return false;
|
|
}
|
|
defaultAngularFriction = src.ParseFloat();
|
|
if ( !src.ExpectTokenString( "," ) ) {
|
|
return false;
|
|
}
|
|
defaultContactFriction = src.ParseFloat();
|
|
if ( src.CheckTokenString( "," ) ) {
|
|
defaultConstraintFriction = src.ParseFloat();
|
|
}
|
|
} else if ( !token.Icmp( "totalMass" ) ) {
|
|
totalMass = src.ParseFloat();
|
|
} else if ( !token.Icmp( "suspendSpeed" ) ) {
|
|
|
|
suspendVelocity[0] = src.ParseFloat();
|
|
if ( !src.ExpectTokenString( "," ) ) {
|
|
return false;
|
|
}
|
|
suspendVelocity[1] = src.ParseFloat();
|
|
if ( !src.ExpectTokenString( "," ) ) {
|
|
return false;
|
|
}
|
|
suspendAcceleration[0] = src.ParseFloat();
|
|
if ( !src.ExpectTokenString( "," ) ) {
|
|
return false;
|
|
}
|
|
suspendAcceleration[1] = src.ParseFloat();
|
|
} else if ( !token.Icmp( "noMoveTime" ) ) {
|
|
noMoveTime = src.ParseFloat();
|
|
} else if ( !token.Icmp( "noMoveTranslation" ) ) {
|
|
noMoveTranslation = src.ParseFloat();
|
|
} else if ( !token.Icmp( "noMoveRotation" ) ) {
|
|
noMoveRotation = src.ParseFloat();
|
|
} else if ( !token.Icmp( "minMoveTime" ) ) {
|
|
minMoveTime = src.ParseFloat();
|
|
} else if ( !token.Icmp( "maxMoveTime" ) ) {
|
|
maxMoveTime = src.ParseFloat();
|
|
} else if ( !token.Icmp( "contents" ) ) {
|
|
ParseContents( src, contents );
|
|
} else if ( !token.Icmp( "clipMask" ) ) {
|
|
ParseContents( src, clipMask );
|
|
} else if ( !token.Icmp( "selfCollision" ) ) {
|
|
selfCollision = src.ParseBool();
|
|
} else if ( token == "}" ) {
|
|
break;
|
|
} else {
|
|
src.Error( "unknown token %s in settings", token.c_str() );
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idDeclAF::Parse
|
|
================
|
|
*/
|
|
bool idDeclAF::Parse( const char *text, const int textLength ) {
|
|
int i, j;
|
|
idLexer src;
|
|
idToken token;
|
|
|
|
src.LoadMemory( text, textLength, GetFileName(), GetLineNum() );
|
|
src.SetFlags( DECL_LEXER_FLAGS );
|
|
src.SkipUntilString( "{" );
|
|
|
|
while( src.ReadToken( &token ) ) {
|
|
|
|
if ( !token.Icmp( "settings" ) ) {
|
|
if ( !ParseSettings( src ) ) {
|
|
return false;
|
|
}
|
|
} else if ( !token.Icmp( "body" ) ) {
|
|
if ( !ParseBody( src ) ) {
|
|
return false;
|
|
}
|
|
} else if ( !token.Icmp( "fixed" ) ) {
|
|
if ( !ParseFixed( src ) ) {
|
|
return false;
|
|
}
|
|
} else if ( !token.Icmp( "ballAndSocketJoint" ) ) {
|
|
if ( !ParseBallAndSocketJoint( src ) ) {
|
|
return false;
|
|
}
|
|
} else if ( !token.Icmp( "universalJoint" ) ) {
|
|
if ( !ParseUniversalJoint( src ) ) {
|
|
return false;
|
|
}
|
|
} else if ( !token.Icmp( "hinge" ) ) {
|
|
if ( !ParseHinge( src ) ) {
|
|
return false;
|
|
}
|
|
} else if ( !token.Icmp( "slider" ) ) {
|
|
if ( !ParseSlider( src ) ) {
|
|
return false;
|
|
}
|
|
} else if ( !token.Icmp( "spring" ) ) {
|
|
if ( !ParseSpring( src ) ) {
|
|
return false;
|
|
}
|
|
} else if ( token == "}" ) {
|
|
break;
|
|
} else {
|
|
src.Error( "unknown keyword %s", token.c_str() );
|
|
return false;
|
|
}
|
|
}
|
|
|
|
for ( i = 0; i < bodies.Num(); i++ ) {
|
|
// check for multiple bodies with the same name
|
|
for ( j = i+1; j < bodies.Num(); j++ ) {
|
|
if ( bodies[i]->name == bodies[j]->name ) {
|
|
src.Error( "two bodies with the same name \"%s\"", bodies[i]->name.c_str() );
|
|
}
|
|
}
|
|
}
|
|
|
|
for ( i = 0; i < constraints.Num(); i++ ) {
|
|
// check for multiple constraints with the same name
|
|
for ( j = i+1; j < constraints.Num(); j++ ) {
|
|
if ( constraints[i]->name == constraints[j]->name ) {
|
|
src.Error( "two constraints with the same name \"%s\"", constraints[i]->name.c_str() );
|
|
}
|
|
}
|
|
// check if there are two valid bodies set
|
|
if ( constraints[i]->body1 == "" ) {
|
|
src.Error( "no valid body1 specified for constraint '%s'", constraints[i]->name.c_str() );
|
|
}
|
|
if ( constraints[i]->body2 == "" ) {
|
|
src.Error( "no valid body2 specified for constraint '%s'", constraints[i]->name.c_str() );
|
|
}
|
|
}
|
|
|
|
// make sure the body which modifies the origin comes first
|
|
for ( i = 0; i < bodies.Num(); i++ ) {
|
|
if ( bodies[i]->jointName == "origin" ) {
|
|
if ( i != 0 ) {
|
|
idDeclAF_Body *b = bodies[0];
|
|
bodies[0] = bodies[i];
|
|
bodies[i] = b;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idDeclAF::DefaultDefinition
|
|
================
|
|
*/
|
|
const char *idDeclAF::DefaultDefinition( void ) const {
|
|
return
|
|
"{\n"
|
|
"\t" "settings {\n"
|
|
"\t\t" "model \"\"\n"
|
|
"\t\t" "skin \"\"\n"
|
|
"\t\t" "friction 0.01, 0.01, 0.8, 0.5\n"
|
|
"\t\t" "suspendSpeed 20, 30, 40, 60\n"
|
|
"\t\t" "noMoveTime 1\n"
|
|
"\t\t" "noMoveTranslation 10\n"
|
|
"\t\t" "noMoveRotation 10\n"
|
|
"\t\t" "minMoveTime -1\n"
|
|
"\t\t" "maxMoveTime -1\n"
|
|
"\t\t" "totalMass -1\n"
|
|
"\t\t" "contents corpse\n"
|
|
"\t\t" "clipMask solid, corpse\n"
|
|
"\t\t" "selfCollision 1\n"
|
|
"\t" "}\n"
|
|
"\t" "body \"body\" {\n"
|
|
"\t\t" "joint \"origin\"\n"
|
|
"\t\t" "mod orientation\n"
|
|
"\t\t" "model box( ( -10, -10, -10 ), ( 10, 10, 10 ) )\n"
|
|
"\t\t" "origin ( 0, 0, 0 )\n"
|
|
"\t\t" "density 0.2\n"
|
|
"\t\t" "friction 0.01, 0.01, 0.8\n"
|
|
"\t\t" "contents corpse\n"
|
|
"\t\t" "clipMask solid, corpse\n"
|
|
"\t\t" "selfCollision 1\n"
|
|
"\t\t" "containedJoints \"*origin\"\n"
|
|
"\t" "}\n"
|
|
"}\n";
|
|
}
|
|
|
|
/*
|
|
================
|
|
idDeclAF::FreeData
|
|
================
|
|
*/
|
|
void idDeclAF::FreeData( void ) {
|
|
modified = false;
|
|
defaultLinearFriction = 0.01f;
|
|
defaultAngularFriction = 0.01f;
|
|
defaultContactFriction = 0.8f;
|
|
defaultConstraintFriction = 0.5f;
|
|
totalMass = -1;
|
|
suspendVelocity.Set( 20.0f, 30.0f );
|
|
suspendAcceleration.Set( 40.0f, 60.0f );
|
|
noMoveTime = 1.0f;
|
|
noMoveTranslation = 10.0f;
|
|
noMoveRotation = 10.0f;
|
|
minMoveTime = -1.0f;
|
|
maxMoveTime = -1.0f;
|
|
selfCollision = true;
|
|
contents = CONTENTS_CORPSE;
|
|
clipMask = CONTENTS_SOLID | CONTENTS_CORPSE;
|
|
bodies.DeleteContents( true );
|
|
constraints.DeleteContents( true );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idDeclAF::Finish
|
|
================
|
|
*/
|
|
void idDeclAF::Finish( const getJointTransform_t GetJointTransform, const idJointMat *frame, void *model ) const {
|
|
int i;
|
|
|
|
const char *name = GetName();
|
|
for ( i = 0; i < bodies.Num(); i++ ) {
|
|
idDeclAF_Body *body = bodies[i];
|
|
body->v1.Finish( name, GetJointTransform, frame, model );
|
|
body->v2.Finish( name, GetJointTransform, frame, model );
|
|
body->origin.Finish( name, GetJointTransform, frame, model );
|
|
body->frictionDirection.Finish( name, GetJointTransform, frame, model );
|
|
body->contactMotorDirection.Finish( name, GetJointTransform, frame, model );
|
|
}
|
|
for ( i = 0; i < constraints.Num(); i++ ) {
|
|
idDeclAF_Constraint *constraint = constraints[i];
|
|
constraint->anchor.Finish( name, GetJointTransform, frame, model );
|
|
constraint->anchor2.Finish( name, GetJointTransform, frame, model );
|
|
constraint->shaft[0].Finish( name, GetJointTransform, frame, model );
|
|
constraint->shaft[1].Finish( name, GetJointTransform, frame, model );
|
|
constraint->axis.Finish( name, GetJointTransform, frame, model );
|
|
constraint->limitAxis.Finish( name, GetJointTransform, frame, model );
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idDeclAF::NewBody
|
|
================
|
|
*/
|
|
void idDeclAF::NewBody( const char *name ) {
|
|
idDeclAF_Body *body;
|
|
|
|
body = new idDeclAF_Body();
|
|
body->SetDefault( this );
|
|
body->name = name;
|
|
bodies.Append( body );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idDeclAF::RenameBody
|
|
|
|
rename the body with the given name and rename
|
|
all constraint body references
|
|
================
|
|
*/
|
|
void idDeclAF::RenameBody( const char *oldName, const char *newName ) {
|
|
int i;
|
|
|
|
for ( i = 0; i < bodies.Num(); i++ ) {
|
|
if ( bodies[i]->name.Icmp( oldName ) == 0 ) {
|
|
bodies[i]->name = newName;
|
|
break;
|
|
}
|
|
}
|
|
for ( i = 0; i < constraints.Num(); i++ ) {
|
|
if ( constraints[i]->body1.Icmp( oldName ) == 0 ) {
|
|
constraints[i]->body1 = newName;
|
|
} else if ( constraints[i]->body2.Icmp( oldName ) == 0 ) {
|
|
constraints[i]->body2 = newName;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idDeclAF::DeleteBody
|
|
|
|
delete the body with the given name and delete
|
|
all constraints that reference the body
|
|
================
|
|
*/
|
|
void idDeclAF::DeleteBody( const char *name ) {
|
|
int i;
|
|
|
|
for ( i = 0; i < bodies.Num(); i++ ) {
|
|
if ( bodies[i]->name.Icmp( name ) == 0 ) {
|
|
delete bodies[i];
|
|
bodies.RemoveIndex( i );
|
|
break;
|
|
}
|
|
}
|
|
for ( i = 0; i < constraints.Num(); i++ ) {
|
|
if ( constraints[i]->body1.Icmp( name ) == 0 ||
|
|
constraints[i]->body2.Icmp( name ) == 0 ) {
|
|
delete constraints[i];
|
|
constraints.RemoveIndex( i );
|
|
i--;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idDeclAF::NewConstraint
|
|
================
|
|
*/
|
|
void idDeclAF::NewConstraint( const char *name ) {
|
|
idDeclAF_Constraint *constraint;
|
|
|
|
constraint = new idDeclAF_Constraint;
|
|
constraint->SetDefault( this );
|
|
constraint->name = name;
|
|
constraints.Append( constraint );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idDeclAF::RenameConstraint
|
|
================
|
|
*/
|
|
void idDeclAF::RenameConstraint( const char *oldName, const char *newName ) {
|
|
int i;
|
|
|
|
for ( i = 0; i < constraints.Num(); i++ ) {
|
|
if ( constraints[i]->name.Icmp( oldName ) == 0 ) {
|
|
constraints[i]->name = newName;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idDeclAF::DeleteConstraint
|
|
================
|
|
*/
|
|
void idDeclAF::DeleteConstraint( const char *name ) {
|
|
int i;
|
|
|
|
for ( i = 0; i < constraints.Num(); i++ ) {
|
|
if ( constraints[i]->name.Icmp( name ) == 0 ) {
|
|
delete constraints[i];
|
|
constraints.RemoveIndex( i );
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idDeclAF::idDeclAF
|
|
================
|
|
*/
|
|
idDeclAF::idDeclAF( void ) {
|
|
FreeData();
|
|
}
|
|
|
|
/*
|
|
================
|
|
idDeclAF::~idDeclAF
|
|
================
|
|
*/
|
|
idDeclAF::~idDeclAF( void ) {
|
|
bodies.DeleteContents( true );
|
|
constraints.DeleteContents( true );
|
|
}
|