mirror of
https://github.com/id-Software/DOOM-3-BFG.git
synced 2025-03-14 06:34:10 +00:00
Merge branch 'master' into 635-nvrhi5
This commit is contained in:
commit
21947e8f08
10 changed files with 724 additions and 152 deletions
|
@ -34,6 +34,9 @@ If you have questions concerning this license or the applicable additional terms
|
|||
#include "gltfParser.h"
|
||||
|
||||
|
||||
static const byte BCANIM_VERSION = 100;
|
||||
static const unsigned int BCANIM_MAGIC = ( 'A' << 24 ) | ( 'C' << 16 ) | ( 'B' << 8 ) | BCANIM_VERSION;
|
||||
#define CAMERA_ANIM_BINARYFILE_EXT "bcanim"
|
||||
static const idMat4 blenderToDoomTransform( idAngles( 0.0f, 0.0f, 90 ).ToMat3(), vec3_origin );
|
||||
/*
|
||||
===============================================================================
|
||||
|
@ -399,83 +402,117 @@ void idCameraAnim::LoadAnim()
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
// check for generated file
|
||||
idStrStatic< MAX_OSPATH > generatedFileName = key;
|
||||
generatedFileName.Insert( "generated/", 0 );
|
||||
generatedFileName.SetFileExtension( CAMERA_ANIM_BINARYFILE_EXT );
|
||||
|
||||
ID_TIME_T currentTimeStamp = FILE_NOT_FOUND_TIMESTAMP;
|
||||
if( isGLTF )
|
||||
{
|
||||
gltfLoadAnim( gltfFileName, animName );
|
||||
currentTimeStamp = fileSystem->GetTimestamp( gltfFileName );
|
||||
}
|
||||
else
|
||||
{
|
||||
filename.SetFileExtension( MD5_CAMERA_EXT );
|
||||
if( !parser.LoadFile( filename ) )
|
||||
currentTimeStamp = fileSystem->GetTimestamp( key );
|
||||
}
|
||||
|
||||
|
||||
// if we are reloading the same map, check the timestamp
|
||||
// and try to skip all the work
|
||||
ID_TIME_T generatedTimeStamp = fileSystem->GetTimestamp( generatedFileName );
|
||||
ID_TIME_T sourceTimeStamp = currentTimeStamp;
|
||||
|
||||
|
||||
if( ( generatedTimeStamp != FILE_NOT_FOUND_TIMESTAMP ) && ( sourceTimeStamp != 0 ) && ( sourceTimeStamp != generatedTimeStamp ) )
|
||||
{
|
||||
idFileLocal file( fileSystem->OpenFileReadMemory( generatedFileName ) );
|
||||
LoadBinaryCamAnim( file, currentTimeStamp );
|
||||
}
|
||||
else
|
||||
{
|
||||
if( isGLTF )
|
||||
{
|
||||
gameLocal.Error( "Unable to load '%s' on '%s'", filename.c_str(), name.c_str() );
|
||||
gltfLoadAnim( gltfFileName, animName );
|
||||
}
|
||||
|
||||
cameraCuts.Clear();
|
||||
cameraCuts.SetGranularity( 1 );
|
||||
camera.Clear();
|
||||
camera.SetGranularity( 1 );
|
||||
|
||||
parser.ExpectTokenString( MD5_VERSION_STRING );
|
||||
version = parser.ParseInt();
|
||||
if( version != MD5_VERSION )
|
||||
else
|
||||
{
|
||||
parser.Error( "Invalid version %d. Should be version %d\n", version, MD5_VERSION );
|
||||
}
|
||||
|
||||
// skip the commandline
|
||||
parser.ExpectTokenString( "commandline" );
|
||||
parser.ReadToken( &token );
|
||||
|
||||
// parse num frames
|
||||
parser.ExpectTokenString( "numFrames" );
|
||||
numFrames = parser.ParseInt();
|
||||
if( numFrames <= 0 )
|
||||
{
|
||||
parser.Error( "Invalid number of frames: %d", numFrames );
|
||||
}
|
||||
|
||||
// parse framerate
|
||||
parser.ExpectTokenString( "frameRate" );
|
||||
frameRate = parser.ParseInt();
|
||||
if( frameRate <= 0 )
|
||||
{
|
||||
parser.Error( "Invalid framerate: %d", frameRate );
|
||||
}
|
||||
|
||||
// parse num cuts
|
||||
parser.ExpectTokenString( "numCuts" );
|
||||
numCuts = parser.ParseInt();
|
||||
if( ( numCuts < 0 ) || ( numCuts > numFrames ) )
|
||||
{
|
||||
parser.Error( "Invalid number of camera cuts: %d", numCuts );
|
||||
}
|
||||
|
||||
// parse the camera cuts
|
||||
parser.ExpectTokenString( "cuts" );
|
||||
parser.ExpectTokenString( "{" );
|
||||
cameraCuts.SetNum( numCuts );
|
||||
for( i = 0; i < numCuts; i++ )
|
||||
{
|
||||
cameraCuts[i] = parser.ParseInt();
|
||||
if( ( cameraCuts[i] < 1 ) || ( cameraCuts[i] >= numFrames ) )
|
||||
filename.SetFileExtension( MD5_CAMERA_EXT );
|
||||
if( !parser.LoadFile( filename ) )
|
||||
{
|
||||
parser.Error( "Invalid camera cut" );
|
||||
gameLocal.Error( "Unable to load '%s' on '%s'", filename.c_str(), name.c_str() );
|
||||
}
|
||||
}
|
||||
parser.ExpectTokenString( "}" );
|
||||
|
||||
// parse the camera frames
|
||||
parser.ExpectTokenString( "camera" );
|
||||
parser.ExpectTokenString( "{" );
|
||||
camera.SetNum( numFrames );
|
||||
for( i = 0; i < numFrames; i++ )
|
||||
{
|
||||
parser.Parse1DMatrix( 3, camera[i].t.ToFloatPtr() );
|
||||
parser.Parse1DMatrix( 3, camera[i].q.ToFloatPtr() );
|
||||
camera[i].fov = parser.ParseFloat();
|
||||
cameraCuts.Clear();
|
||||
cameraCuts.SetGranularity( 1 );
|
||||
camera.Clear();
|
||||
camera.SetGranularity( 1 );
|
||||
|
||||
parser.ExpectTokenString( MD5_VERSION_STRING );
|
||||
version = parser.ParseInt();
|
||||
if( version != MD5_VERSION )
|
||||
{
|
||||
parser.Error( "Invalid version %d. Should be version %d\n", version, MD5_VERSION );
|
||||
}
|
||||
|
||||
// skip the commandline
|
||||
parser.ExpectTokenString( "commandline" );
|
||||
parser.ReadToken( &token );
|
||||
|
||||
// parse num frames
|
||||
parser.ExpectTokenString( "numFrames" );
|
||||
numFrames = parser.ParseInt();
|
||||
if( numFrames <= 0 )
|
||||
{
|
||||
parser.Error( "Invalid number of frames: %d", numFrames );
|
||||
}
|
||||
|
||||
// parse framerate
|
||||
parser.ExpectTokenString( "frameRate" );
|
||||
frameRate = parser.ParseInt();
|
||||
if( frameRate <= 0 )
|
||||
{
|
||||
parser.Error( "Invalid framerate: %d", frameRate );
|
||||
}
|
||||
|
||||
// parse num cuts
|
||||
parser.ExpectTokenString( "numCuts" );
|
||||
numCuts = parser.ParseInt();
|
||||
if( ( numCuts < 0 ) || ( numCuts > numFrames ) )
|
||||
{
|
||||
parser.Error( "Invalid number of camera cuts: %d", numCuts );
|
||||
}
|
||||
|
||||
// parse the camera cuts
|
||||
parser.ExpectTokenString( "cuts" );
|
||||
parser.ExpectTokenString( "{" );
|
||||
cameraCuts.SetNum( numCuts );
|
||||
for( i = 0; i < numCuts; i++ )
|
||||
{
|
||||
cameraCuts[i] = parser.ParseInt();
|
||||
if( ( cameraCuts[i] < 1 ) || ( cameraCuts[i] >= numFrames ) )
|
||||
{
|
||||
parser.Error( "Invalid camera cut" );
|
||||
}
|
||||
}
|
||||
parser.ExpectTokenString( "}" );
|
||||
|
||||
// parse the camera frames
|
||||
parser.ExpectTokenString( "camera" );
|
||||
parser.ExpectTokenString( "{" );
|
||||
camera.SetNum( numFrames );
|
||||
for( i = 0; i < numFrames; i++ )
|
||||
{
|
||||
parser.Parse1DMatrix( 3, camera[i].t.ToFloatPtr() );
|
||||
parser.Parse1DMatrix( 3, camera[i].q.ToFloatPtr() );
|
||||
camera[i].fov = parser.ParseFloat();
|
||||
}
|
||||
parser.ExpectTokenString( "}" );
|
||||
}
|
||||
parser.ExpectTokenString( "}" );
|
||||
|
||||
idFileLocal file( fileSystem->OpenFileWrite( generatedFileName, "fs_basepath" ) );
|
||||
WriteBinaryCamAnim( file );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -718,7 +755,7 @@ void idCameraAnim::Event_Activate( idEntity* _activator )
|
|||
|
||||
void idCameraAnim::gltfLoadAnim( idStr gltfFileName, idStr animName )
|
||||
{
|
||||
//we dont wat to load the gltb all the time. write custom binary format !
|
||||
// we dont want to load the gltb all the time. write custom binary format !
|
||||
GLTF_Parser gltf;
|
||||
if( gltf.Load( gltfFileName ) )
|
||||
{
|
||||
|
@ -731,7 +768,11 @@ void idCameraAnim::gltfLoadAnim( idStr gltfFileName, idStr animName )
|
|||
assert( cameraNode );
|
||||
|
||||
gltfAnimation* anim = data->GetAnimation( animName, data->GetNodeIndex( cameraNode ) );
|
||||
assert( anim );
|
||||
|
||||
if( anim == nullptr )
|
||||
{
|
||||
gameLocal.Error( "Missing 'anim.%s' on '%s'", animName, gltfFileName.c_str() );
|
||||
}
|
||||
|
||||
cameraCuts.Clear();
|
||||
cameraCuts.SetGranularity( 1 );
|
||||
|
@ -828,9 +869,71 @@ void idCameraAnim::gltfLoadAnim( idStr gltfFileName, idStr animName )
|
|||
{
|
||||
gameLocal.Error( "Missing 'anim' key on '%s'", name.c_str() );
|
||||
}
|
||||
}
|
||||
|
||||
void idCameraAnim::WriteBinaryCamAnim( idFile* file, ID_TIME_T* _timeStamp /*= NULL*/ )
|
||||
{
|
||||
if( file != NULL )
|
||||
{
|
||||
file->WriteBig( BCANIM_MAGIC );
|
||||
file->WriteInt( frameRate );
|
||||
file->WriteInt( cameraCuts.Num() );
|
||||
|
||||
for( auto cut : cameraCuts )
|
||||
{
|
||||
file->WriteInt( cut );
|
||||
}
|
||||
|
||||
file->WriteInt( camera.Num() );
|
||||
|
||||
for( auto cam : camera )
|
||||
{
|
||||
file->WriteBig( cam.fov );
|
||||
file->WriteBigArray( cam.q.ToFloatPtr(), 3 );
|
||||
file->WriteVec3( cam.t );
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool idCameraAnim::LoadBinaryCamAnim( idFile* file, const ID_TIME_T sourceTimeStamp )
|
||||
{
|
||||
if( file != NULL )
|
||||
{
|
||||
unsigned int magic = 0;
|
||||
file->ReadBig( magic );
|
||||
if( magic != BCANIM_MAGIC )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
file->ReadInt( frameRate );
|
||||
|
||||
int count = 0;
|
||||
|
||||
file->ReadInt( count );
|
||||
|
||||
for( int i = 0; i < count; i++ )
|
||||
{
|
||||
file->ReadInt( cameraCuts.Alloc() );
|
||||
}
|
||||
|
||||
count = 0;
|
||||
|
||||
file->ReadInt( count );
|
||||
int i = 0;
|
||||
for( i = 0; i < count; i++ )
|
||||
{
|
||||
cameraFrame_t& cam = camera.Alloc();
|
||||
file->ReadBig( cam.fov );
|
||||
file->ReadBigArray( cam.q.ToFloatPtr(), 3 );
|
||||
file->ReadVec3( cam.t );
|
||||
}
|
||||
|
||||
assert( i == count );
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -133,6 +133,8 @@ private:
|
|||
void Event_Activate( idEntity* activator );
|
||||
|
||||
void gltfLoadAnim( idStr gltfFileName, idStr animName );
|
||||
void WriteBinaryCamAnim( idFile* file, ID_TIME_T* _timeStamp = NULL );
|
||||
bool LoadBinaryCamAnim( idFile* file, const ID_TIME_T sourceTimeStamp );
|
||||
};
|
||||
|
||||
#endif /* !__GAME_CAMERA_H__ */
|
||||
|
|
|
@ -1919,21 +1919,21 @@ void idPlayer::Init()
|
|||
hipJoint = animator.GetJointHandle( value );
|
||||
if( hipJoint == INVALID_JOINT )
|
||||
{
|
||||
gameLocal.Error( "Joint '%s' not found for 'bone_hips' on '%s'", value, name.c_str() );
|
||||
gameLocal.Warning( "Joint '%s' not found for 'bone_hips' on '%s'", value, name.c_str() );
|
||||
}
|
||||
|
||||
value = spawnArgs.GetString( "bone_chest", "" );
|
||||
chestJoint = animator.GetJointHandle( value );
|
||||
if( chestJoint == INVALID_JOINT )
|
||||
{
|
||||
gameLocal.Error( "Joint '%s' not found for 'bone_chest' on '%s'", value, name.c_str() );
|
||||
gameLocal.Warning( "Joint '%s' not found for 'bone_chest' on '%s'", value, name.c_str() );
|
||||
}
|
||||
|
||||
value = spawnArgs.GetString( "bone_head", "" );
|
||||
headJoint = animator.GetJointHandle( value );
|
||||
if( headJoint == INVALID_JOINT )
|
||||
{
|
||||
gameLocal.Error( "Joint '%s' not found for 'bone_head' on '%s'", value, name.c_str() );
|
||||
gameLocal.Warning( "Joint '%s' not found for 'bone_head' on '%s'", value, name.c_str() );
|
||||
}
|
||||
|
||||
// initialize the script variables
|
||||
|
|
|
@ -209,13 +209,32 @@ bool idMD5Anim::LoadAnim( const char* filename, const idImportOptions* options )
|
|||
sourceTimeStamp = fileSystem->GetTimestamp( filename );
|
||||
}
|
||||
|
||||
// glTF2 animations will be saved straight to .bMD5anim files instead of the old ASCII format
|
||||
idFile* fileptr = fileSystem->OpenFileReadMemory( generatedFileName );
|
||||
idFile* fileptr = nullptr;
|
||||
bool doWrite = false;
|
||||
if( fileptr == nullptr && isGLTF )
|
||||
|
||||
// glTF2 animations will be saved straight to .bMD5anim files instead of the old ASCII format,
|
||||
// so to correctly regenerate newly exported animations, we need to check here.
|
||||
ID_TIME_T binTimeStamp = fileSystem->GetTimestamp( generatedFileName );
|
||||
if( isGLTF )
|
||||
{
|
||||
fileptr = idRenderModelGLTF::GetAnimBin( filenameStr, sourceTimeStamp, options );
|
||||
doWrite = fileptr != nullptr;
|
||||
if( sourceTimeStamp < binTimeStamp )
|
||||
{
|
||||
fileptr = fileSystem->OpenFileReadMemory( generatedFileName );
|
||||
}
|
||||
else
|
||||
{
|
||||
idLib::Printf( "Regenerating %s\n", generatedFileName.c_str() );
|
||||
}
|
||||
|
||||
if( fileptr == nullptr )
|
||||
{
|
||||
fileptr = idRenderModelGLTF::GetAnimBin( filenameStr, sourceTimeStamp, options );
|
||||
doWrite = fileptr != nullptr;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fileptr = fileSystem->OpenFileReadMemory( generatedFileName );
|
||||
}
|
||||
|
||||
idFileLocal file( fileptr );
|
||||
|
@ -1140,16 +1159,14 @@ void idMD5Anim::CheckModelHierarchy( const idRenderModel* model ) const
|
|||
{
|
||||
gameLocal.Error( "Model '%s''s joint names don't match anim '%s''s", model->Name(), name.c_str() );
|
||||
}
|
||||
int parent;
|
||||
|
||||
int parent = -1;
|
||||
if( modelJoints[ i ].parent )
|
||||
{
|
||||
parent = modelJoints[ i ].parent - modelJoints;
|
||||
}
|
||||
else
|
||||
{
|
||||
parent = -1;
|
||||
}
|
||||
if( parent != jointInfo[ i ].parentNum )
|
||||
|
||||
if( i > 0 && parent != jointInfo[ i ].parentNum )
|
||||
{
|
||||
gameLocal.Error( "Model '%s' has different joint hierarchy than anim '%s'", model->Name(), name.c_str() );
|
||||
}
|
||||
|
|
|
@ -560,7 +560,7 @@ void ResolveEntity( gltfData* data, idMapEntity* newEntity, gltfNode* node )
|
|||
#endif
|
||||
}
|
||||
|
||||
int FindEntities( gltfData* data, idMapEntity::EntityListRef entities, gltfNode* node )
|
||||
int FindEntities( gltfData* data, idMapEntity::EntityListRef entities, gltfNode* node , idDict epairs )
|
||||
{
|
||||
int entityCount = 0;
|
||||
|
||||
|
@ -574,16 +574,28 @@ int FindEntities( gltfData* data, idMapEntity::EntityListRef entities, gltfNode*
|
|||
// skip everything that is not an entity
|
||||
if( !classnameStr.IsEmpty() )
|
||||
{
|
||||
idMapEntity* newEntity = new( TAG_IDLIB_GLTF ) idMapEntity();
|
||||
auto* newEntity = new( TAG_IDLIB_GLTF ) idMapEntity();
|
||||
entities.Append( newEntity );
|
||||
newEntity->epairs.Copy( epairs );
|
||||
epairs.Clear();
|
||||
ResolveEntity( data, newEntity, node );
|
||||
entityCount++;
|
||||
}
|
||||
else
|
||||
{
|
||||
idStr bindTarget = node->extras.strPairs.GetString( "bind" );
|
||||
|
||||
if( !bindTarget.IsEmpty() )
|
||||
{
|
||||
epairs.Set( "bind", bindTarget );
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
for( auto& child : node->children )
|
||||
{
|
||||
entityCount += FindEntities( data, entities, data->NodeList()[child] );
|
||||
entityCount += FindEntities( data, entities, data->NodeList()[child], epairs );
|
||||
}
|
||||
|
||||
return entityCount;
|
||||
|
@ -621,7 +633,7 @@ int idMapEntity::GetEntities( gltfData* data, EntityListRef entities, int sceneI
|
|||
{
|
||||
idStr classnameStr = node->extras.strPairs.GetString( "classname" );
|
||||
bool skipInline = !node->extras.strPairs.GetBool( "inline", true );
|
||||
|
||||
idDict epairs;
|
||||
// skip everything that is not an entity
|
||||
if( !classnameStr.IsEmpty() )
|
||||
{
|
||||
|
@ -640,10 +652,19 @@ int idMapEntity::GetEntities( gltfData* data, EntityListRef entities, int sceneI
|
|||
|
||||
entityCount++;
|
||||
}
|
||||
else
|
||||
{
|
||||
idStr bindTarget = node->extras.strPairs.GetString( "bind" );
|
||||
|
||||
if( !bindTarget.IsEmpty() )
|
||||
{
|
||||
epairs.Copy( node->extras.strPairs );
|
||||
}
|
||||
}
|
||||
// add entities from all subnodes
|
||||
for( auto& child : node->children )
|
||||
{
|
||||
entityCount += FindEntities( data, entities, data->NodeList()[child] );
|
||||
entityCount += FindEntities( data, entities, data->NodeList()[child] , epairs );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1051,6 +1051,19 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
gltfSkin* GetSkin( idStr name )
|
||||
{
|
||||
for( auto skin : skins )
|
||||
{
|
||||
if( skin->name == name )
|
||||
{
|
||||
return skin;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
gltfSkin* GetSkin( int boneNodeId )
|
||||
{
|
||||
for( auto skin : skins )
|
||||
|
|
|
@ -1163,23 +1163,27 @@ void idImportOptions::Init( const char* commandline, const char* ospath )
|
|||
idStr destDir;
|
||||
|
||||
//Reset( commandline );
|
||||
scale = 1.0f;
|
||||
//type = WRITE_MESH;
|
||||
startframe = -1;
|
||||
endframe = -1;
|
||||
ignoreMeshes = false;
|
||||
clearOrigin = false;
|
||||
clearOriginAxis = false;
|
||||
framerate = 24;
|
||||
align = "";
|
||||
rotate = 0.0f;
|
||||
commandLine = commandline;
|
||||
prefix = "";
|
||||
jointThreshold = 0.05f;
|
||||
ignoreScale = false;
|
||||
xyzPrecision = DEFAULT_ANIM_EPSILON;
|
||||
quatPrecision = DEFAULT_QUAT_EPSILON;
|
||||
cycleStart = -1;
|
||||
scale = 1.0f;
|
||||
//type = WRITE_MESH;
|
||||
startframe = -1;
|
||||
endframe = -1;
|
||||
ignoreMeshes = false;
|
||||
clearOrigin = false;
|
||||
clearOriginAxis = false;
|
||||
addOrigin = false;
|
||||
transferRootMotion = "";
|
||||
framerate = 24;
|
||||
align = "";
|
||||
rotate = 0.0f;
|
||||
commandLine = commandline;
|
||||
prefix = "";
|
||||
jointThreshold = 0.05f;
|
||||
ignoreScale = false;
|
||||
xyzPrecision = DEFAULT_ANIM_EPSILON;
|
||||
quatPrecision = DEFAULT_QUAT_EPSILON;
|
||||
cycleStart = -1;
|
||||
reOrient = ang_zero;
|
||||
armature = "";
|
||||
|
||||
src.Clear();
|
||||
dest.Clear();
|
||||
|
@ -1330,6 +1334,15 @@ void idImportOptions::Init( const char* commandline, const char* ospath )
|
|||
clearOriginAxis = true;
|
||||
|
||||
}
|
||||
else if( token == "addorigin" )
|
||||
{
|
||||
addOrigin = true;
|
||||
}
|
||||
else if( token == "transfermotion" )
|
||||
{
|
||||
token = tokens.NextToken( "Missing value for -transfermotion. Usage: -transfermotion [bonename]" );
|
||||
transferRootMotion = token;
|
||||
}
|
||||
else if( token == "ignorescale" )
|
||||
{
|
||||
ignoreScale = true;
|
||||
|
@ -1450,6 +1463,27 @@ void idImportOptions::Init( const char* commandline, const char* ospath )
|
|||
keepjoints.AddUnique( token );
|
||||
}
|
||||
}
|
||||
else if( token == "reorient" )
|
||||
{
|
||||
while( tokens.TokenAvailable() )
|
||||
{
|
||||
idAngles angle;
|
||||
float x = atof( tokens.NextToken() );
|
||||
float y = atof( tokens.NextToken() );
|
||||
float z = atof( tokens.NextToken() );
|
||||
reOrient = idAngles( x, y, z );
|
||||
token = tokens.NextToken();
|
||||
if( token[0] == '-' )
|
||||
{
|
||||
tokens.UnGetToken();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if( token == "armature" )
|
||||
{
|
||||
armature = tokens.NextToken( "Missing skin name for -armature. Usage: -armature [gltfSkin name]" );
|
||||
}
|
||||
else
|
||||
{
|
||||
MayaError( "Unknown option '%s'", token.c_str() );
|
||||
|
|
|
@ -69,6 +69,8 @@ public:
|
|||
bool ignoreMeshes;
|
||||
bool clearOrigin;
|
||||
bool clearOriginAxis;
|
||||
bool addOrigin;
|
||||
idStr transferRootMotion;
|
||||
bool ignoreScale;
|
||||
int startframe;
|
||||
int endframe;
|
||||
|
@ -86,6 +88,8 @@ public:
|
|||
float rotate;
|
||||
float jointThreshold;
|
||||
int cycleStart;
|
||||
idAngles reOrient;
|
||||
idStr armature;
|
||||
|
||||
void Init( const char* commandline, const char* ospath );
|
||||
|
||||
|
|
|
@ -40,12 +40,12 @@ If you have questions concerning this license or the applicable additional terms
|
|||
#include "d3xp/Game_local.h"
|
||||
|
||||
idCVar gltf_ForceBspMeshTexture( "gltf_ForceBspMeshTexture", "0", CVAR_SYSTEM | CVAR_BOOL, "all world geometry has the same forced texture" );
|
||||
idCVar gltf_ModelSceneName( "gltf_ModelSceneName", "models", CVAR_SYSTEM , "Scene to use when loading specific models" );
|
||||
idCVar gltf_ModelSceneName( "gltf_ModelSceneName", "Scene", CVAR_SYSTEM , "Scene to use when loading specific models" );
|
||||
|
||||
idCVar gltf_AnimSampleRate( "gltf_AnimSampleRate", "24", CVAR_SYSTEM | CVAR_INTEGER , "The frame rate of the converted md5anim" );
|
||||
|
||||
|
||||
static const byte GLMB_VERSION = 101;
|
||||
static const byte GLMB_VERSION = 102;
|
||||
static const unsigned int GLMB_MAGIC = ( 'M' << 24 ) | ( 'L' << 16 ) | ( 'G' << 8 ) | GLMB_VERSION;
|
||||
static const char* GLTF_SnapshotName = "_GLTF_Snapshot_";
|
||||
static const idMat4 blenderToDoomTransform( idAngles( 0.0f, 0.0f, 90 ).ToMat3(), vec3_origin );
|
||||
|
@ -129,6 +129,183 @@ void idRenderModelGLTF::ProcessNode_r( gltfNode* modelNode, const idMat4& parent
|
|||
ProcessNode_r( nodeList[child], nodeToWorldTransform, globalTransform, data );
|
||||
}
|
||||
}
|
||||
static void KeepNodes( gltfData* data, const idStrList& keepList, idList<int, TAG_MODEL>& boneList )
|
||||
{
|
||||
idStrList finalList;
|
||||
idStr nodeName;
|
||||
idList<int> nodesToKeep;
|
||||
|
||||
for( int i = 0 ; i < keepList.Num(); i++ )
|
||||
{
|
||||
bool parentWildcard = false;
|
||||
bool childWildcard = false;
|
||||
if( keepList[i] == "*" )
|
||||
{
|
||||
parentWildcard = true;
|
||||
i++;
|
||||
}
|
||||
const idStr item = keepList[i];
|
||||
gltfNode* node = data->GetNode( item );
|
||||
|
||||
if( node == nullptr )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
int idx = data->GetNodeIndex( node );
|
||||
if( boneList.Find( idx ) )
|
||||
{
|
||||
nodesToKeep.Insert( idx );
|
||||
}
|
||||
|
||||
if( ( i < ( keepList.Num() - 1 ) ) && keepList[i + 1] == "*" )
|
||||
{
|
||||
childWildcard = true;
|
||||
i++;
|
||||
}
|
||||
|
||||
if( parentWildcard && node->parent )
|
||||
{
|
||||
gltfNode* parent = node->parent;
|
||||
while( parent )
|
||||
{
|
||||
idx = data->GetNodeIndex( parent );
|
||||
if( boneList.Find( idx ) )
|
||||
{
|
||||
nodesToKeep.Insert( idx );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( childWildcard && node->children.Num() )
|
||||
{
|
||||
idList<int> tmpIdx;
|
||||
tmpIdx.Append( node->children );
|
||||
for( int j = 0; j < tmpIdx.Num(); j++ )
|
||||
{
|
||||
idx = tmpIdx[j];
|
||||
if( boneList.Find( idx ) )
|
||||
{
|
||||
nodesToKeep.Insert( idx );
|
||||
gltfNode* tmpNode = data->NodeList()[idx];
|
||||
if( tmpNode )
|
||||
{
|
||||
for( int child : tmpNode->children )
|
||||
{
|
||||
tmpIdx.AddUnique( child );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
common->SetRefreshOnPrint( true );
|
||||
common->DPrintf( "=====\n" );
|
||||
for( int nodeID : boneList )
|
||||
{
|
||||
if( nodesToKeep.Find( nodeID ) )
|
||||
{
|
||||
common->DPrintf( "^7[^2Kept^7]\t\t bone \'%s\'\n",
|
||||
data->NodeList()[nodeID]->name.c_str() );
|
||||
}
|
||||
else
|
||||
{
|
||||
common->DPrintf( "^7[^1Discard^7]\t bone \'%s\'\n ",
|
||||
data->NodeList()[nodeID]->name.c_str() );
|
||||
}
|
||||
|
||||
}
|
||||
common->DPrintf( "=====\n" );
|
||||
common->SetRefreshOnPrint( false );
|
||||
#endif
|
||||
|
||||
boneList = nodesToKeep;
|
||||
}
|
||||
|
||||
static gltfNode* GetBoneNode( gltfData* data, const idList<int, TAG_MODEL>& boneList, const idStr& name )
|
||||
{
|
||||
auto& nodelist = data->NodeList();
|
||||
for( int boneId : boneList )
|
||||
{
|
||||
gltfNode* boneNode = nodelist[boneId];
|
||||
if( boneNode->name == name )
|
||||
{
|
||||
return boneNode;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static void RemapNodes( gltfData* data, const idList<idNamePair>& remapList, const idList<int, TAG_MODEL>& boneList )
|
||||
{
|
||||
//we need to be _very_ careful with modifying the GLTF data since it is not saved or cached!!!
|
||||
auto& nodeList = data->NodeList();
|
||||
for( const auto& remap : remapList )
|
||||
{
|
||||
gltfNode* from = GetBoneNode( data, boneList, remap.from );
|
||||
gltfNode* to = GetBoneNode( data, boneList, remap.to );
|
||||
if( !from || !to )
|
||||
{
|
||||
common->Error( "Invalid remap name pair \'%s\'[\"%s\"] : \'%s\'[\"%s\"]",
|
||||
remap.from.c_str(), from ? from->name.c_str() : "Not Found",
|
||||
remap.to.c_str(), to ? to->name.c_str() : "Not Found" );
|
||||
}
|
||||
|
||||
common->Warning( "Remapping.. setting \'%s\' as parent of \'%s\' ",
|
||||
remap.to.c_str(), remap.from.c_str() );
|
||||
|
||||
|
||||
from->parent = to;
|
||||
to->children.Alloc() = data->GetNodeIndex( from );
|
||||
}
|
||||
}
|
||||
|
||||
static int AddOriginBone( gltfData* data, idList<int, TAG_MODEL>& bones, gltfNode* root )
|
||||
{
|
||||
//we need to be _very_ careful with modifying the GLTF data since it is not saved or cached!!!
|
||||
auto& nodeList = data->NodeList();
|
||||
gltfNode* newNode = data->Node();
|
||||
int newIdx = nodeList.Num() - 1;
|
||||
bones.Insert( newIdx );
|
||||
newNode->name = "origin";
|
||||
|
||||
|
||||
//patch children
|
||||
for( int childId : root->children )
|
||||
{
|
||||
newNode->children.Alloc() = childId;
|
||||
gltfNode* childNode = nodeList[childId];
|
||||
childNode->parent = newNode;
|
||||
}
|
||||
|
||||
root->children.Clear();
|
||||
root->children.Alloc() = nodeList.Num() - 1;
|
||||
newNode->parent = root;
|
||||
common->Warning( "Added origin bone!" );
|
||||
return newIdx;
|
||||
}
|
||||
|
||||
static void RenameNodes( gltfData* data, const idList<idNamePair>& renameList, const idList<int, TAG_MODEL>& boneList )
|
||||
{
|
||||
//we need to be _very_ careful with modifying the GLTF data since it is not saved or cached!!!
|
||||
auto& nodeList = data->NodeList();
|
||||
for( const auto& rename : renameList )
|
||||
{
|
||||
gltfNode* from = GetBoneNode( data, boneList, rename.from );
|
||||
if( !from )
|
||||
{
|
||||
common->Error( "Invalid rename name pair from \'%s\'[\"%s\"] ",
|
||||
rename.from.c_str(), from ? from->name.c_str() : "Not Found" );
|
||||
}
|
||||
|
||||
common->Warning( "Renaming.. \'%s\' to \'%s\' ",
|
||||
rename.from.c_str(), rename.to.c_str() );
|
||||
|
||||
from->name = rename.to;
|
||||
}
|
||||
}
|
||||
|
||||
//constructs a renderModel from a gltfScene node found in the "models" scene of the given gltfFile.
|
||||
// override with gltf_ModelSceneName
|
||||
|
@ -144,7 +321,7 @@ void idRenderModelGLTF::InitFromFile( const char* fileName, const idImportOption
|
|||
int meshID = -1;
|
||||
name = fileName;
|
||||
currentSkin = nullptr;
|
||||
globalTransform = blenderToDoomTransform;
|
||||
idImportOptions* localOptions = nullptr;
|
||||
|
||||
PurgeModel();
|
||||
|
||||
|
@ -153,6 +330,20 @@ void idRenderModelGLTF::InitFromFile( const char* fileName, const idImportOption
|
|||
gltfFileName = idStr( fileName );
|
||||
model_state = DM_STATIC;
|
||||
|
||||
if( options )
|
||||
{
|
||||
commandLine = options->commandLine;
|
||||
localOptions = const_cast<idImportOptions*>( options );
|
||||
}
|
||||
else
|
||||
{
|
||||
if( !commandLine.IsEmpty() )
|
||||
{
|
||||
localOptions = new idImportOptions();
|
||||
localOptions->Init( commandLine, fileName );
|
||||
}
|
||||
}
|
||||
|
||||
gltfManager::ExtractIdentifier( gltfFileName, meshID, meshName );
|
||||
GLTF_Parser gltf;
|
||||
gltf.Load( gltfFileName );
|
||||
|
@ -172,13 +363,17 @@ void idRenderModelGLTF::InitFromFile( const char* fileName, const idImportOption
|
|||
assert( nodes.Num() );
|
||||
|
||||
//determine root node
|
||||
if( !meshName[0] )
|
||||
if( !meshName[0] && data->MeshList().Num() )
|
||||
{
|
||||
root = new gltfNode();
|
||||
root->name = scene->name;
|
||||
root->children.Append( scene->nodes );
|
||||
rootID = -1;
|
||||
gltfMesh* firstMesh = data->MeshList()[0];
|
||||
|
||||
fileExclusive = true;
|
||||
root = data->GetNode( scene, firstMesh, &rootID );
|
||||
if( root )
|
||||
{
|
||||
rootID = data->GetNodeIndex( root );
|
||||
meshName = root->name;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -193,6 +388,12 @@ void idRenderModelGLTF::InitFromFile( const char* fileName, const idImportOption
|
|||
{
|
||||
common->Warning( "Couldn't find model: '%s'", name.c_str() );
|
||||
MakeDefaultModel();
|
||||
|
||||
if( localOptions && !options )
|
||||
{
|
||||
delete localOptions;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -219,9 +420,33 @@ void idRenderModelGLTF::InitFromFile( const char* fileName, const idImportOption
|
|||
bones.Append( currentSkin->joints );
|
||||
animCount = data->GetAnimationIds( nodes[bones[0]] , animIds );
|
||||
}
|
||||
|
||||
if( localOptions )
|
||||
{
|
||||
if( localOptions->keepjoints.Num() )
|
||||
{
|
||||
KeepNodes( data, localOptions->keepjoints, bones );
|
||||
}
|
||||
|
||||
if( localOptions->addOrigin )
|
||||
{
|
||||
AddOriginBone( data, bones, nodes[bones[0]]->parent );
|
||||
}
|
||||
|
||||
if( localOptions->remapjoints.Num() )
|
||||
{
|
||||
RemapNodes( data, localOptions->remapjoints, bones );
|
||||
}
|
||||
|
||||
if( localOptions->renamejoints.Num() )
|
||||
{
|
||||
RenameNodes( data, localOptions->renamejoints, bones );
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//Boneless TRS animation.
|
||||
animCount = data->GetAnimationIds( tmpNode , animIds );
|
||||
bones.Append( meshID );
|
||||
}
|
||||
|
@ -231,17 +456,22 @@ void idRenderModelGLTF::InitFromFile( const char* fileName, const idImportOption
|
|||
|
||||
hasAnimations = totalAnims > 0;
|
||||
model_state = hasAnimations ? DM_CACHED : DM_STATIC;
|
||||
globalTransform = blenderToDoomTransform;
|
||||
|
||||
//idMat4 globalTransform = blenderToDoomTransform;
|
||||
|
||||
if( options )
|
||||
if( localOptions )
|
||||
{
|
||||
const auto blenderToDoomRotation = idAngles( 0.0f, 0.0f, 90 ).ToMat3();
|
||||
idMat3 reOrientationMat = blenderToDoomRotation;
|
||||
|
||||
float scale = options->scale;
|
||||
if( localOptions->reOrient != ang_zero )
|
||||
{
|
||||
reOrientationMat = localOptions->reOrient.ToMat3();
|
||||
}
|
||||
|
||||
float scale = localOptions->scale;
|
||||
idMat3 scaleMat( scale, 0, 0, 0, scale, 0, 0, 0, scale );
|
||||
|
||||
globalTransform = idMat4( scaleMat * blenderToDoomRotation, vec3_origin );
|
||||
globalTransform = idMat4( reOrientationMat * scaleMat, vec3_origin );
|
||||
}
|
||||
|
||||
ProcessNode_r( root, mat4_identity, globalTransform, data );
|
||||
|
@ -251,10 +481,31 @@ void idRenderModelGLTF::InitFromFile( const char* fileName, const idImportOption
|
|||
common->Warning( "Couldn't load model: '%s'", name.c_str() );
|
||||
MakeDefaultModel();
|
||||
data = nullptr;
|
||||
if( localOptions && !options )
|
||||
{
|
||||
delete localOptions;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if( options )
|
||||
{
|
||||
if( options->addOrigin )
|
||||
{
|
||||
//patch bone indices; all indices are offset with 1 after adding a bone
|
||||
for( auto& surf : surfaces )
|
||||
{
|
||||
for( int i = 0; i < surf.geometry->numVerts; i++ )
|
||||
{
|
||||
idDrawVert& base = surf.geometry->verts[i];
|
||||
base.color[0] += 1;
|
||||
base.color[1] += 1;
|
||||
base.color[2] += 1;
|
||||
base.color[3] += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// derive mikktspace tangents from normals
|
||||
FinishSurfaces( true );
|
||||
|
||||
|
@ -263,6 +514,10 @@ void idRenderModelGLTF::InitFromFile( const char* fileName, const idImportOption
|
|||
|
||||
// it is now available for use
|
||||
lastMeshFromFile = this;
|
||||
if( localOptions && !options )
|
||||
{
|
||||
delete localOptions;
|
||||
}
|
||||
}
|
||||
|
||||
bool idRenderModelGLTF::LoadBinaryModel( idFile* file, const ID_TIME_T sourceTimeStamp )
|
||||
|
@ -286,6 +541,7 @@ bool idRenderModelGLTF::LoadBinaryModel( idFile* file, const ID_TIME_T sourceTim
|
|||
return false;
|
||||
}
|
||||
|
||||
file->ReadString( commandLine );
|
||||
file->ReadBig( model_state );
|
||||
file->ReadBig( rootID );
|
||||
|
||||
|
@ -383,6 +639,7 @@ void idRenderModelGLTF::UpdateMd5Joints()
|
|||
md5joints.Resize( bones.Num() );
|
||||
md5joints.SetNum( bones.Num() );
|
||||
idStr ovrBoneName;
|
||||
int overrideIdx = -1;
|
||||
auto& nodeList = data->NodeList();
|
||||
|
||||
for( int i = 0 ; i < bones.Num(); i++ )
|
||||
|
@ -394,11 +651,8 @@ void idRenderModelGLTF::UpdateMd5Joints()
|
|||
{
|
||||
if( node->mesh == -1 )
|
||||
{
|
||||
common->Warning( "First bone of model is not named \"origin\", name forced!" );
|
||||
common->Warning( "First bone of model \'%s\' is not named \"origin\"!", name.c_str() );
|
||||
}
|
||||
|
||||
ovrBoneName = node->name;
|
||||
md5joints[i].name = "origin";
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -409,16 +663,9 @@ void idRenderModelGLTF::UpdateMd5Joints()
|
|||
for( int i = 0 ; i < bones.Num(); i++ )
|
||||
{
|
||||
gltfNode* node = nodeList[bones[i]];
|
||||
if( i && node->parent && node->parent != root )
|
||||
if( i && node->parent && node->parent != root && ( currentSkin && ( node->parent->name != currentSkin->name ) ) )
|
||||
{
|
||||
if( node->parent->name == ovrBoneName )
|
||||
{
|
||||
md5joints[i].parent = FindMD5Joint( idStr( "origin" ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
md5joints[i].parent = FindMD5Joint( node->parent->name );
|
||||
}
|
||||
md5joints[i].parent = FindMD5Joint( node->parent->name );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -484,7 +731,7 @@ void idRenderModelGLTF::DrawJoints( const struct renderEntity_s* ent, const view
|
|||
}
|
||||
}
|
||||
|
||||
static bool GatherBoneInfo( gltfData* data, gltfAnimation* gltfAnim, const idList<gltfNode*>& nodes , idList<int, TAG_MODEL>& bones, idList<jointAnimInfo_t, TAG_MD5_ANIM>& jointInfo )
|
||||
static bool GatherBoneInfo( gltfData* data, gltfAnimation* gltfAnim, const idList<gltfNode*>& nodes , idList<int, TAG_MODEL>& bones, idList<jointAnimInfo_t, TAG_MD5_ANIM>& jointInfo , const idImportOptions* options )
|
||||
{
|
||||
//Gather Bones;
|
||||
bool boneLess = false;
|
||||
|
@ -492,7 +739,7 @@ static bool GatherBoneInfo( gltfData* data, gltfAnimation* gltfAnim, const idLis
|
|||
|
||||
auto skin = data->GetSkin( gltfAnim );
|
||||
auto targets = data->GetAnimTargets( gltfAnim );
|
||||
|
||||
auto& nodeList = data->NodeList();
|
||||
if( skin == nullptr )
|
||||
{
|
||||
boneLess = true;
|
||||
|
@ -513,13 +760,39 @@ static bool GatherBoneInfo( gltfData* data, gltfAnimation* gltfAnim, const idLis
|
|||
bones.Append( targetNode );
|
||||
}
|
||||
|
||||
if( options )
|
||||
{
|
||||
if( options->keepjoints.Num() )
|
||||
{
|
||||
KeepNodes( data, options->keepjoints, bones );
|
||||
}
|
||||
|
||||
if( options->addOrigin )
|
||||
{
|
||||
AddOriginBone( data, bones, data->NodeList()[bones[0]]->parent );
|
||||
}
|
||||
|
||||
if( options->remapjoints.Num() )
|
||||
{
|
||||
RemapNodes( data, options->remapjoints, bones );
|
||||
}
|
||||
|
||||
if( options->renamejoints.Num() )
|
||||
{
|
||||
RenameNodes( data, options->renamejoints, bones );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//create jointInfo
|
||||
jointInfo.SetGranularity( 1 );
|
||||
jointInfo.SetNum( bones.Num() );
|
||||
int idx = 0;
|
||||
for( auto& joint : jointInfo )
|
||||
{
|
||||
joint.animBits = 0;
|
||||
joint.firstComponent = -1;
|
||||
joint.nameIndex = animationLib.JointIndex( nodeList[bones[idx++]]->name );
|
||||
}
|
||||
|
||||
return boneLess;
|
||||
|
@ -611,6 +884,8 @@ idFile_Memory* idRenderModelGLTF::GetAnimBin( const idStr& animName, const ID_TI
|
|||
|
||||
// convert animName to original glTF2 filename and load it
|
||||
GLTF_Parser gltf;
|
||||
int rootMotionCopyTargetId = -1;
|
||||
|
||||
int id;
|
||||
idStr gltfFileName = idStr( animName );
|
||||
idStr name;
|
||||
|
@ -620,24 +895,73 @@ idFile_Memory* idRenderModelGLTF::GetAnimBin( const idStr& animName, const ID_TI
|
|||
gltfData* data = gltf.currentAsset;
|
||||
auto& accessors = data->AccessorList();
|
||||
auto& nodes = data->NodeList();
|
||||
int rootID = -1;
|
||||
|
||||
gltfNode* nodeRoot = nodes[lastMeshFromFile->rootID];
|
||||
int boneRootNode = lastMeshFromFile->rootID;
|
||||
if( nodeRoot->skin > -1 )
|
||||
idStr lastGltfFileName = idStr( lastMeshFromFile->name );
|
||||
idStr lastName;
|
||||
gltfManager::ExtractIdentifier( lastGltfFileName, id, lastName );
|
||||
|
||||
if( options != nullptr && !options->armature.IsEmpty() )
|
||||
{
|
||||
boneRootNode = nodes[data->SkinList()[nodeRoot->skin]->skeleton]->children[0];
|
||||
gameLocal.Printf( "Looking for armature %s\n", options->armature.c_str() );
|
||||
gltfSkin* skin = data->GetSkin( options->armature );
|
||||
if( skin && ( skin->skeleton > -1 && skin->skeleton < nodes.Num() ) )
|
||||
{
|
||||
rootID = nodes[skin->skeleton]->children[0];
|
||||
}
|
||||
|
||||
}
|
||||
else if( lastMeshFromFile == nullptr || lastGltfFileName != gltfFileName )
|
||||
{
|
||||
//treat the gltf file as one that only has 1 scene and 1 model, aka a fileExclusive model and
|
||||
//try to use node from first model in first scene in gltf file
|
||||
common->Warning( "Loading %s as if it was a gltf with only 1 mesh, 1 scene and 1 armature", gltfFileName.c_str() );
|
||||
if( data->MeshList().Num() < 1 || data->SceneList().Num() < 1 )
|
||||
{
|
||||
common->Warning( "Could not determine root" );
|
||||
return nullptr;
|
||||
}
|
||||
gltfMesh* firstMesh = data->MeshList()[0];
|
||||
gltfScene* scenePtr = data->SceneList()[0];
|
||||
|
||||
gltfNode* root = data->GetNode( scenePtr, firstMesh, &rootID );
|
||||
if( root )
|
||||
{
|
||||
rootID = data->GetNodeIndex( root );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
rootID = lastMeshFromFile->rootID;
|
||||
gltfNode* nodeRoot = nullptr;
|
||||
|
||||
if( rootID != -1 )
|
||||
{
|
||||
nodeRoot = nodes[rootID];
|
||||
}
|
||||
|
||||
if( nodeRoot != nullptr && nodeRoot->skin > -1 )
|
||||
{
|
||||
rootID = nodes[data->SkinList()[nodeRoot->skin]->skeleton]->children[0];
|
||||
}
|
||||
}
|
||||
|
||||
auto gltfAnim = data->GetAnimation( name, boneRootNode );
|
||||
if( rootID == -1 )
|
||||
{
|
||||
common->Warning( "Could not determine root" );
|
||||
return nullptr;
|
||||
}
|
||||
auto gltfAnim = data->GetAnimation( name, rootID );
|
||||
if( !gltfAnim )
|
||||
{
|
||||
common->Warning( "Could not find action %s in %s !", name.c_str(), gltfFileName.c_str() );
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
idList<int, TAG_MODEL> bones;
|
||||
idList<jointAnimInfo_t, TAG_MD5_ANIM> jointInfo;
|
||||
|
||||
bool boneLess = GatherBoneInfo( data, gltfAnim, nodes, bones, jointInfo );
|
||||
bool boneLess = GatherBoneInfo( data, gltfAnim, nodes, bones, jointInfo, options );
|
||||
|
||||
idList<idList<gltfNode>> animBones;
|
||||
idList<float, TAG_MD5_ANIM> componentFrames;
|
||||
|
@ -651,8 +975,33 @@ idFile_Memory* idRenderModelGLTF::GetAnimBin( const idStr& animName, const ID_TI
|
|||
|
||||
gameLocal.Printf( "Generating MD5Anim for GLTF anim %s from scene %s\n", name.c_str(), gltf_ModelSceneName.GetString() );
|
||||
|
||||
// TODO use idImportOptions to build globalTransform
|
||||
idMat4 globalTransform = blenderToDoomTransform;
|
||||
|
||||
if( options )
|
||||
{
|
||||
if( !options->transferRootMotion.IsEmpty() )
|
||||
{
|
||||
gltfNode* target = data->GetNode( options->transferRootMotion );
|
||||
if( !target )
|
||||
{
|
||||
common->Warning( "Target bone to copy root motion from is not found" );
|
||||
}
|
||||
|
||||
rootMotionCopyTargetId = data->GetNodeIndex( target );
|
||||
}
|
||||
const auto blenderToDoomRotation = idAngles( 0.0f, 0.0f, 90 ).ToMat3();
|
||||
idMat3 reOrientationMat = blenderToDoomRotation;
|
||||
|
||||
if( options->reOrient != ang_zero )
|
||||
{
|
||||
reOrientationMat = options->reOrient.ToMat3();
|
||||
}
|
||||
|
||||
float scale = options->scale;
|
||||
idMat3 scaleMat( scale, 0, 0, 0, scale, 0, 0, 0, scale );
|
||||
|
||||
globalTransform = idMat4( reOrientationMat * scaleMat, vec3_origin );
|
||||
}
|
||||
|
||||
gltfNode* root = nullptr;
|
||||
int channelCount = 0;
|
||||
|
@ -688,6 +1037,8 @@ idFile_Memory* idRenderModelGLTF::GetAnimBin( const idStr& animName, const ID_TI
|
|||
newJoint->nameIndex = animationLib.JointIndex( boneLess ? "origin" : target->name );
|
||||
newJoint->parentNum = bones.FindIndex( parentIndex );
|
||||
|
||||
//assert( newJoint->parentNum >= 0 );
|
||||
|
||||
if( newJoint->firstComponent == -1 )
|
||||
{
|
||||
newJoint->firstComponent = numAnimatedComponents;
|
||||
|
@ -717,6 +1068,19 @@ idFile_Memory* idRenderModelGLTF::GetAnimBin( const idStr& animName, const ID_TI
|
|||
|
||||
channelCount++;
|
||||
}
|
||||
|
||||
if( options && !options->transferRootMotion.IsEmpty() )
|
||||
{
|
||||
jointAnimInfo_t* newJoint = &( jointInfo[0] );
|
||||
newJoint->animBits = ANIM_TX | ANIM_TY | ANIM_TZ;
|
||||
numAnimatedComponents += 3;
|
||||
newJoint->firstComponent = -3;
|
||||
for( auto& joint : jointInfo )
|
||||
{
|
||||
joint.firstComponent += 3;
|
||||
}
|
||||
}
|
||||
|
||||
animBones.AssureSize( numFrames );
|
||||
animBones.SetNum( numFrames );
|
||||
for( int i = 0; i < numFrames; i++ )
|
||||
|
@ -748,7 +1112,7 @@ idFile_Memory* idRenderModelGLTF::GetAnimBin( const idStr& animName, const ID_TI
|
|||
baseFrame.SetNum( bones.Num() );
|
||||
|
||||
idJointMat* poseMat = ( idJointMat* ) _alloca16( bones.Num() * sizeof( poseMat[0] ) );
|
||||
baseFrame = GetPose( animBones[0], poseMat, blenderToDoomTransform );
|
||||
baseFrame = GetPose( animBones[0], poseMat, globalTransform );
|
||||
|
||||
componentFrames.SetGranularity( 1 );
|
||||
componentFrames.SetNum( ( ( numAnimatedComponents * numFrames ) ) + 1 );
|
||||
|
@ -793,7 +1157,16 @@ idFile_Memory* idRenderModelGLTF::GetAnimBin( const idStr& animName, const ID_TI
|
|||
idList<idVec3*>& values = data->GetAccessorView<idVec3>( output );
|
||||
if( values.Num() > i )
|
||||
{
|
||||
animBones[i][boneIndex].translation = *values[i];
|
||||
if( channel->target.node == rootMotionCopyTargetId )
|
||||
{
|
||||
animBones[i][boneIndex].translation.y = values[i]->y;
|
||||
animBones[i][0].translation = *values[i];
|
||||
animBones[i][0].translation.y = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
animBones[i][boneIndex].translation = *values[i];
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -822,7 +1195,7 @@ idFile_Memory* idRenderModelGLTF::GetAnimBin( const idStr& animName, const ID_TI
|
|||
{
|
||||
if( node->parent == nullptr )
|
||||
{
|
||||
t = blenderToDoomTransform * t;
|
||||
t = globalTransform * t;
|
||||
}
|
||||
|
||||
componentFrames[componentFrameIndex++] = t.x;
|
||||
|
@ -834,7 +1207,7 @@ idFile_Memory* idRenderModelGLTF::GetAnimBin( const idStr& animName, const ID_TI
|
|||
{
|
||||
if( node->parent == nullptr )
|
||||
{
|
||||
q = blenderToDoomTransform.ToMat3().ToQuat() * animBones[i][b].rotation;
|
||||
q = globalTransform.ToMat3().ToQuat() * animBones[i][b].rotation;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -864,12 +1237,12 @@ idFile_Memory* idRenderModelGLTF::GetAnimBin( const idStr& animName, const ID_TI
|
|||
{
|
||||
if( animBones[i][b].parent == nullptr )
|
||||
{
|
||||
animBones[i][b].translation = blenderToDoomTransform * animBones[i][b].translation;
|
||||
animBones[i][b].translation = globalTransform * animBones[i][b].translation;
|
||||
}
|
||||
}
|
||||
|
||||
idList<idJointMat> joints;
|
||||
GetPose( animBones[i], currJoints, blenderToDoomTransform );
|
||||
GetPose( animBones[i], currJoints, globalTransform );
|
||||
for( int b = 0; b < animBones[i].Num(); b++ )
|
||||
{
|
||||
idJointMat mat = poseMat[b];
|
||||
|
@ -975,7 +1348,11 @@ idFile_Memory* idRenderModelGLTF::GetAnimBin( const idStr& animName, const ID_TI
|
|||
}
|
||||
else
|
||||
{
|
||||
componentPtr = &componentFrames[jointInfo[0].firstComponent];
|
||||
if( jointInfo[0].animBits )
|
||||
{
|
||||
componentPtr = &componentFrames[jointInfo[0].firstComponent];
|
||||
}
|
||||
//of there is root movement on a different bone , for example when adding a root bone, this wil fail.
|
||||
if( jointInfo[0].animBits & ANIM_TX )
|
||||
{
|
||||
for( int i = 0; i < numFrames; i++ )
|
||||
|
@ -1024,7 +1401,7 @@ idFile_Memory* idRenderModelGLTF::GetAnimBin( const idStr& animName, const ID_TI
|
|||
|
||||
void idRenderModelGLTF::WriteBinaryModel( idFile* file, ID_TIME_T* _timeStamp /*= NULL */ ) const
|
||||
{
|
||||
idRenderModelStatic::WriteBinaryModel( file );
|
||||
idRenderModelStatic::WriteBinaryModel( file , _timeStamp );
|
||||
|
||||
if( file == NULL )
|
||||
{
|
||||
|
@ -1032,6 +1409,7 @@ void idRenderModelGLTF::WriteBinaryModel( idFile* file, ID_TIME_T* _timeStamp /*
|
|||
}
|
||||
|
||||
file->WriteBig( GLMB_MAGIC );
|
||||
file->WriteString( commandLine );
|
||||
file->WriteBig( model_state );
|
||||
file->WriteBig( rootID );
|
||||
file->WriteString( file->GetName() );
|
||||
|
@ -1089,6 +1467,7 @@ void idRenderModelGLTF::PurgeModel()
|
|||
bones.Clear();
|
||||
MeshNodeIds.Clear();
|
||||
gltfFileName.Clear();
|
||||
meshName.Clear();
|
||||
|
||||
//if no root id was set, it is a generated one.
|
||||
if( rootID == -1 && root )
|
||||
|
@ -1100,8 +1479,6 @@ void idRenderModelGLTF::PurgeModel()
|
|||
|
||||
void idRenderModelGLTF::LoadModel()
|
||||
{
|
||||
assert( data );
|
||||
|
||||
int num;
|
||||
auto& accessors = data->AccessorList();
|
||||
auto& nodes = data->NodeList();
|
||||
|
@ -1109,7 +1486,6 @@ void idRenderModelGLTF::LoadModel()
|
|||
if( !fileExclusive )
|
||||
{
|
||||
meshRoot = data->GetNode( gltf_ModelSceneName.GetString(), meshName );
|
||||
assert( meshRoot );
|
||||
}
|
||||
|
||||
gltfSkin* skin = nullptr;
|
||||
|
@ -1148,7 +1524,8 @@ void idRenderModelGLTF::LoadModel()
|
|||
//check for TRS anim and its artficial root bone
|
||||
if( bones.Num() == 0 && node->mesh != -1 )
|
||||
{
|
||||
md5joints[i].name = "origin";
|
||||
assert( 0 );
|
||||
//md5joints[i].name = "origin";
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1160,7 +1537,7 @@ void idRenderModelGLTF::LoadModel()
|
|||
{
|
||||
auto* node = nodes[bones[i]];
|
||||
|
||||
if( i && node->parent && node->parent != root )
|
||||
if( i && node->parent && node->parent != root && ( currentSkin && ( node->parent->name != currentSkin->name ) ) )
|
||||
{
|
||||
md5joints[i].parent = FindMD5Joint( node->parent->name );
|
||||
}
|
||||
|
|
|
@ -74,6 +74,7 @@ private:
|
|||
dynamicModel_t model_state;
|
||||
idStr meshName;
|
||||
idStr gltfFileName;
|
||||
idStr commandLine;
|
||||
|
||||
idList<idMD5Joint, TAG_MODEL> md5joints;
|
||||
idList<idJointQuat, TAG_MODEL> defaultPose;
|
||||
|
|
Loading…
Reference in a new issue