From bba7ddd66510c4d74e977979c27c999014071711 Mon Sep 17 00:00:00 2001 From: HarrievG Date: Sun, 13 Nov 2022 01:10:21 +0100 Subject: [PATCH 01/15] [!] Merge error fixes # Conflicts: # neo/idlib/MapFile_gltf.cpp # neo/renderer/Model_gltf.cpp --- neo/d3xp/Camera.cpp | 116 +++++++++++++++++++++++--------------------- 1 file changed, 60 insertions(+), 56 deletions(-) diff --git a/neo/d3xp/Camera.cpp b/neo/d3xp/Camera.cpp index d858700d..63a583d9 100644 --- a/neo/d3xp/Camera.cpp +++ b/neo/d3xp/Camera.cpp @@ -416,66 +416,70 @@ void idCameraAnim::LoadAnim() 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.ExpectTokenString(MD5_VERSION_STRING); + version = parser.ParseInt(); + if (version != MD5_VERSION) { - parser.Error( "Invalid camera cut" ); + parser.Error("Invalid version %d. Should be version %d\n", version, MD5_VERSION); } - } - 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(); + // 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); } } From 31d79dfd362fe32242c8c87bc859507d1c5ec486 Mon Sep 17 00:00:00 2001 From: HarrievG Date: Tue, 15 Nov 2022 20:28:06 +0100 Subject: [PATCH 02/15] [~] idAssert -> assert [+] addOrigin model import option # Conflicts: # neo/renderer/Model_gltf.cpp --- neo/renderer/ModelManager.cpp | 5 + neo/renderer/ModelManager.h | 1 + neo/renderer/Model_gltf.cpp | 231 +++++++++++++++++++++++++++++++--- 3 files changed, 217 insertions(+), 20 deletions(-) diff --git a/neo/renderer/ModelManager.cpp b/neo/renderer/ModelManager.cpp index 3f960e4c..e512f9d3 100644 --- a/neo/renderer/ModelManager.cpp +++ b/neo/renderer/ModelManager.cpp @@ -1137,6 +1137,7 @@ void idImportOptions::Init( const char* commandline, const char* ospath ) ignoreMeshes = false; clearOrigin = false; clearOriginAxis = false; + addOrigin = false; framerate = 24; align = ""; rotate = 0.0f; @@ -1297,6 +1298,10 @@ void idImportOptions::Init( const char* commandline, const char* ospath ) clearOriginAxis = true; } + else if (token == "addorigin") + { + addOrigin = true; + } else if( token == "ignorescale" ) { ignoreScale = true; diff --git a/neo/renderer/ModelManager.h b/neo/renderer/ModelManager.h index e947553f..55df4a62 100644 --- a/neo/renderer/ModelManager.h +++ b/neo/renderer/ModelManager.h @@ -69,6 +69,7 @@ public: bool ignoreMeshes; bool clearOrigin; bool clearOriginAxis; + bool addOrigin; bool ignoreScale; int startframe; int endframe; diff --git a/neo/renderer/Model_gltf.cpp b/neo/renderer/Model_gltf.cpp index 010c90e6..7646ab1a 100644 --- a/neo/renderer/Model_gltf.cpp +++ b/neo/renderer/Model_gltf.cpp @@ -129,6 +129,151 @@ 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& boneList) +{ + idStrList finalList; + idStr nodeName; + idList 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 tmpIdx; + tmpIdx.Append(node->children); + for (int j=0; jNodeList()[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& 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& remapList, const idList& 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 void AddOriginBone(gltfData* data, idList& 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!"); +} //constructs a renderModel from a gltfScene node found in the "models" scene of the given gltfFile. // override with gltf_ModelSceneName @@ -144,7 +289,6 @@ void idRenderModelGLTF::InitFromFile( const char* fileName, const idImportOption int meshID = -1; name = fileName; currentSkin = nullptr; - globalTransform = blenderToDoomTransform; PurgeModel(); @@ -219,9 +363,21 @@ void idRenderModelGLTF::InitFromFile( const char* fileName, const idImportOption bones.Append( currentSkin->joints ); animCount = data->GetAnimationIds( nodes[bones[0]] , animIds ); } + if (options) + { + if (options->keepjoints.Num()) + KeepNodes(data, options->keepjoints, bones); + if (options->addOrigin) + { + AddOriginBone(data, bones, nodes[bones[0]]->parent); + } + if (options->remapjoints.Num()) + RemapNodes(data, options->remapjoints, bones); + } } else { + //Boneless TRS animation. animCount = data->GetAnimationIds( tmpNode , animIds ); bones.Append( meshID ); } @@ -231,8 +387,7 @@ void idRenderModelGLTF::InitFromFile( const char* fileName, const idImportOption hasAnimations = totalAnims > 0; model_state = hasAnimations ? DM_CACHED : DM_STATIC; - - //idMat4 globalTransform = blenderToDoomTransform; + globalTransform = blenderToDoomTransform; if( options ) { @@ -254,7 +409,24 @@ void idRenderModelGLTF::InitFromFile( const char* fileName, const idImportOption 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 ); @@ -383,6 +555,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 +567,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 { @@ -411,14 +581,7 @@ void idRenderModelGLTF::UpdateMd5Joints() gltfNode* node = nodeList[bones[i]]; if( i && node->parent && node->parent != root ) { - if( node->parent->name == ovrBoneName ) - { - md5joints[i].parent = FindMD5Joint( idStr( "origin" ) ); - } - else - { md5joints[i].parent = FindMD5Joint( node->parent->name ); - } } } @@ -484,7 +647,7 @@ void idRenderModelGLTF::DrawJoints( const struct renderEntity_s* ent, const view } } -static bool GatherBoneInfo( gltfData* data, gltfAnimation* gltfAnim, const idList& nodes , idList& bones, idList& jointInfo ) +static bool GatherBoneInfo( gltfData* data, gltfAnimation* gltfAnim, const idList& nodes , idList& bones, idList& jointInfo , const idImportOptions* options ) { //Gather Bones; bool boneLess = false; @@ -492,6 +655,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 ) { @@ -513,13 +677,26 @@ 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); + + } + //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; @@ -637,7 +814,7 @@ idFile_Memory* idRenderModelGLTF::GetAnimBin( const idStr& animName, const ID_TI idList bones; idList jointInfo; - bool boneLess = GatherBoneInfo( data, gltfAnim, nodes, bones, jointInfo ); + bool boneLess = GatherBoneInfo( data, gltfAnim, nodes, bones, jointInfo, options ); idList> animBones; idList componentFrames; @@ -652,8 +829,17 @@ 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) + { + const auto blenderToDoomRotation = idAngles(0.0f, 0.0f, 90).ToMat3(); + float scale = options->scale; + idMat3 scaleMat(scale, 0, 0, 0, scale, 0, 0, 0, scale); + + globalTransform = idMat4(scaleMat * blenderToDoomRotation, vec3_origin); + } gltfNode* root = nullptr; int channelCount = 0; for( auto channel : gltfAnim->channels ) @@ -688,6 +874,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; @@ -748,7 +936,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 ); @@ -975,7 +1163,9 @@ 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++ ) @@ -1148,7 +1338,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 { From 0cf9dac1ef4ca3ce5d8a1db87b0fe0f416cd8e6d Mon Sep 17 00:00:00 2001 From: HarrievG Date: Tue, 15 Nov 2022 20:31:12 +0100 Subject: [PATCH 03/15] A-Style # Conflicts: # neo/d3xp/Camera.cpp # neo/d3xp/Camera.h --- neo/d3xp/Camera.cpp | 185 ++++++++++++++++++++++++-------- neo/d3xp/Camera.h | 2 + neo/renderer/ModelManager.cpp | 6 +- neo/renderer/Model_gltf.cpp | 192 +++++++++++++++++++--------------- 4 files changed, 254 insertions(+), 131 deletions(-) diff --git a/neo/d3xp/Camera.cpp b/neo/d3xp/Camera.cpp index 63a583d9..14676f3f 100644 --- a/neo/d3xp/Camera.cpp +++ b/neo/d3xp/Camera.cpp @@ -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,87 +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 + { + filename.SetFileExtension( MD5_CAMERA_EXT ); + if( !parser.LoadFile( filename ) ) { - parser.Error("Invalid version %d. Should be version %d\n", version, MD5_VERSION); + gameLocal.Error( "Unable to load '%s' on '%s'", filename.c_str(), name.c_str() ); + } + + 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); + parser.ExpectTokenString( "commandline" ); + parser.ReadToken( &token ); // parse num frames - parser.ExpectTokenString("numFrames"); + parser.ExpectTokenString( "numFrames" ); numFrames = parser.ParseInt(); - if (numFrames <= 0) + if( numFrames <= 0 ) { - parser.Error("Invalid number of frames: %d", numFrames); + parser.Error( "Invalid number of frames: %d", numFrames ); } // parse framerate - parser.ExpectTokenString("frameRate"); + parser.ExpectTokenString( "frameRate" ); frameRate = parser.ParseInt(); - if (frameRate <= 0) + if( frameRate <= 0 ) { - parser.Error("Invalid framerate: %d", frameRate); + parser.Error( "Invalid framerate: %d", frameRate ); } // parse num cuts - parser.ExpectTokenString("numCuts"); + parser.ExpectTokenString( "numCuts" ); numCuts = parser.ParseInt(); - if ((numCuts < 0) || (numCuts > numFrames)) + if( ( numCuts < 0 ) || ( numCuts > numFrames ) ) { - parser.Error("Invalid number of camera cuts: %d", numCuts); + 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++) + 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)) + if( ( cameraCuts[i] < 1 ) || ( cameraCuts[i] >= numFrames ) ) { - parser.Error("Invalid camera cut"); + parser.Error( "Invalid camera cut" ); } } - parser.ExpectTokenString("}"); + parser.ExpectTokenString( "}" ); // parse the camera frames - parser.ExpectTokenString("camera"); - parser.ExpectTokenString("{"); - camera.SetNum(numFrames); - for (i = 0; i < numFrames; i++) + 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()); + 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); + idFileLocal file( fileSystem->OpenFileWrite( generatedFileName, "fs_basepath" ) ); + WriteBinaryCamAnim( file ); } } @@ -722,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 ) ) { @@ -735,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 ); @@ -832,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; } /* diff --git a/neo/d3xp/Camera.h b/neo/d3xp/Camera.h index 600da6ec..a9fe1a58 100644 --- a/neo/d3xp/Camera.h +++ b/neo/d3xp/Camera.h @@ -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__ */ diff --git a/neo/renderer/ModelManager.cpp b/neo/renderer/ModelManager.cpp index e512f9d3..7809db1b 100644 --- a/neo/renderer/ModelManager.cpp +++ b/neo/renderer/ModelManager.cpp @@ -371,7 +371,7 @@ idRenderModel* idRenderModelManagerLocal::GetModel( const char* _modelName, bool // determine which subclass of idRenderModel to initialize idRenderModel* model = NULL; - + // HvG: GLTF 2 support if( isGLTF ) { @@ -661,7 +661,7 @@ void idRenderModelManagerLocal::ReloadModels( bool forceAll ) { continue; } - + bool isGLTF = false; idStr filename = model->Name(); idStr extension; @@ -1298,7 +1298,7 @@ void idImportOptions::Init( const char* commandline, const char* ospath ) clearOriginAxis = true; } - else if (token == "addorigin") + else if( token == "addorigin" ) { addOrigin = true; } diff --git a/neo/renderer/Model_gltf.cpp b/neo/renderer/Model_gltf.cpp index 7646ab1a..e57f2c02 100644 --- a/neo/renderer/Model_gltf.cpp +++ b/neo/renderer/Model_gltf.cpp @@ -129,63 +129,71 @@ 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& boneList) +static void KeepNodes( gltfData* data, const idStrList& keepList, idList& boneList ) { idStrList finalList; idStr nodeName; idList nodesToKeep; - for (int i=0 ; i< keepList.Num(); i++) + for( int i = 0 ; i < keepList.Num(); i++ ) { bool parentWildcard = false; bool childWildcard = false; - if (keepList[i] == "*") + 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); + gltfNode* node = data->GetNode( item ); - if ((i < (keepList.Num() -1)) && keepList[i + 1] == "*") + 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) + if( parentWildcard && node->parent ) { gltfNode* parent = node->parent; - while (parent) + while( parent ) { - idx = data->GetNodeIndex(parent); - if (boneList.Find(idx)) - nodesToKeep.Insert(idx); + idx = data->GetNodeIndex( parent ); + if( boneList.Find( idx ) ) + { + nodesToKeep.Insert( idx ); + } } } - if (childWildcard && node->children.Num()) + if( childWildcard && node->children.Num() ) { idList tmpIdx; - tmpIdx.Append(node->children); - for (int j=0; jchildren ); + for( int j = 0; j < tmpIdx.Num(); j++ ) { idx = tmpIdx[j]; - if (boneList.Find(idx)) + if( boneList.Find( idx ) ) { - nodesToKeep.Insert(idx); + nodesToKeep.Insert( idx ); gltfNode* tmpNode = data->NodeList()[idx]; - if (tmpNode) + if( tmpNode ) { - for (int child : tmpNode->children) - tmpIdx.AddUnique(child); + for( int child : tmpNode->children ) + { + tmpIdx.AddUnique( child ); + } } } } @@ -193,76 +201,78 @@ static void KeepNodes(gltfData* data, const idStrList& keepList, idListSetRefreshOnPrint(true); - common->DPrintf("=====\n"); - for (int nodeID : boneList) + common->SetRefreshOnPrint( true ); + common->DPrintf( "=====\n" ); + for( int nodeID : boneList ) { - if (nodesToKeep.Find(nodeID)) + if( nodesToKeep.Find( nodeID ) ) { - common->DPrintf("^7[^2Kept^7]\t\t bone \'%s\'\n", - data->NodeList()[nodeID]->name.c_str()); + 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( "^7[^1Discard^7]\t bone \'%s\'\n ", + data->NodeList()[nodeID]->name.c_str() ); } } - common->DPrintf("=====\n"); - common->SetRefreshOnPrint(false); + common->DPrintf( "=====\n" ); + common->SetRefreshOnPrint( false ); #endif boneList = nodesToKeep; } -static gltfNode* GetBoneNode(gltfData* data, const idList& boneList,const idStr & name ) +static gltfNode* GetBoneNode( gltfData* data, const idList& boneList, const idStr& name ) { auto& nodelist = data->NodeList(); - for (int boneId : boneList) + for( int boneId : boneList ) { gltfNode* boneNode = nodelist[boneId]; - if (boneNode->name == name) + if( boneNode->name == name ) + { return boneNode; + } } return nullptr; } -static void RemapNodes(gltfData* data, const idList& remapList, const idList& boneList) +static void RemapNodes( gltfData* data, const idList& remapList, const idList& 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) + for( const auto& remap : remapList ) { - gltfNode* from = GetBoneNode(data,boneList,remap.from); - gltfNode* to = GetBoneNode(data, boneList, remap.to); - if (!from || !to) + 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->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()); + 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); + to->children.Alloc() = data->GetNodeIndex( from ); } } -static void AddOriginBone(gltfData* data, idList& bones, gltfNode * root) +static void AddOriginBone( gltfData* data, idList& 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); + bones.Insert( newIdx ); newNode->name = "origin"; //patch children - for (int childId : root->children) + for( int childId : root->children ) { newNode->children.Alloc() = childId; gltfNode* childNode = nodeList[childId]; @@ -272,7 +282,7 @@ static void AddOriginBone(gltfData* data, idList& bones, gltfNod root->children.Clear(); root->children.Alloc() = nodeList.Num() - 1; newNode->parent = root; - common->Warning("Added origin bone!"); + common->Warning( "Added origin bone!" ); } //constructs a renderModel from a gltfScene node found in the "models" scene of the given gltfFile. @@ -363,16 +373,20 @@ void idRenderModelGLTF::InitFromFile( const char* fileName, const idImportOption bones.Append( currentSkin->joints ); animCount = data->GetAnimationIds( nodes[bones[0]] , animIds ); } - if (options) + if( options ) { - if (options->keepjoints.Num()) - KeepNodes(data, options->keepjoints, bones); - if (options->addOrigin) + if( options->keepjoints.Num() ) { - AddOriginBone(data, bones, nodes[bones[0]]->parent); - } - if (options->remapjoints.Num()) - RemapNodes(data, options->remapjoints, bones); + KeepNodes( data, options->keepjoints, bones ); + } + if( options->addOrigin ) + { + AddOriginBone( data, bones, nodes[bones[0]]->parent ); + } + if( options->remapjoints.Num() ) + { + RemapNodes( data, options->remapjoints, bones ); + } } } else @@ -409,14 +423,14 @@ void idRenderModelGLTF::InitFromFile( const char* fileName, const idImportOption return; } - if (options) + if( options ) { - if (options->addOrigin) + if( options->addOrigin ) { //patch bone indices; all indices are offset with 1 after adding a bone - for (auto& surf : surfaces) + for( auto& surf : surfaces ) { - for (int i = 0; i < surf.geometry->numVerts; i++) + for( int i = 0; i < surf.geometry->numVerts; i++ ) { idDrawVert& base = surf.geometry->verts[i]; base.color[0] += 1; @@ -567,7 +581,7 @@ void idRenderModelGLTF::UpdateMd5Joints() { if( node->mesh == -1 ) { - common->Warning( "First bone of model \'%s\' is not named \"origin\"!", name.c_str()); + common->Warning( "First bone of model \'%s\' is not named \"origin\"!", name.c_str() ); } } else @@ -581,7 +595,7 @@ void idRenderModelGLTF::UpdateMd5Joints() gltfNode* node = nodeList[bones[i]]; if( i && node->parent && node->parent != root ) { - md5joints[i].parent = FindMD5Joint( node->parent->name ); + md5joints[i].parent = FindMD5Joint( node->parent->name ); } } @@ -677,26 +691,32 @@ static bool GatherBoneInfo( gltfData* data, gltfAnimation* gltfAnim, const idLis bones.Append( targetNode ); } - if (options) + 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->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 ); + } + } - + //create jointInfo jointInfo.SetGranularity( 1 ); jointInfo.SetNum( bones.Num() ); - int idx =0; + int idx = 0; for( auto& joint : jointInfo ) { joint.animBits = 0; joint.firstComponent = -1; - joint.nameIndex = animationLib.JointIndex(nodeList[bones[idx++]]->name); + joint.nameIndex = animationLib.JointIndex( nodeList[bones[idx++]]->name ); } return boneLess; @@ -831,14 +851,14 @@ idFile_Memory* idRenderModelGLTF::GetAnimBin( const idStr& animName, const ID_TI // TODO use idImportOptions to build globalTransform idMat4 globalTransform = blenderToDoomTransform; - if (options) + if( options ) { - const auto blenderToDoomRotation = idAngles(0.0f, 0.0f, 90).ToMat3(); + const auto blenderToDoomRotation = idAngles( 0.0f, 0.0f, 90 ).ToMat3(); float scale = options->scale; - idMat3 scaleMat(scale, 0, 0, 0, scale, 0, 0, 0, scale); + idMat3 scaleMat( scale, 0, 0, 0, scale, 0, 0, 0, scale ); - globalTransform = idMat4(scaleMat * blenderToDoomRotation, vec3_origin); + globalTransform = idMat4( scaleMat * blenderToDoomRotation, vec3_origin ); } gltfNode* root = nullptr; int channelCount = 0; @@ -874,7 +894,7 @@ 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); + assert( newJoint->parentNum >= 0 ); if( newJoint->firstComponent == -1 ) { @@ -1163,8 +1183,10 @@ idFile_Memory* idRenderModelGLTF::GetAnimBin( const idStr& animName, const ID_TI } else { - if (jointInfo[0].animBits) + 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 ) { @@ -1338,7 +1360,7 @@ void idRenderModelGLTF::LoadModel() //check for TRS anim and its artficial root bone if( bones.Num() == 0 && node->mesh != -1 ) { - assert(0); + assert( 0 ); //md5joints[i].name = "origin"; } else From 8c18d58c498e4dc117b6e53fe27c5db3675ed51b Mon Sep 17 00:00:00 2001 From: HarrievG Date: Wed, 16 Nov 2022 20:07:47 +0100 Subject: [PATCH 04/15] [+] Added -reorient option to gltf model import --- neo/renderer/ModelManager.cpp | 18 ++++++++++++++++++ neo/renderer/ModelManager.h | 1 + neo/renderer/Model_gltf.cpp | 25 +++++++++++++++++++------ 3 files changed, 38 insertions(+), 6 deletions(-) diff --git a/neo/renderer/ModelManager.cpp b/neo/renderer/ModelManager.cpp index 7809db1b..a699159a 100644 --- a/neo/renderer/ModelManager.cpp +++ b/neo/renderer/ModelManager.cpp @@ -1148,6 +1148,7 @@ void idImportOptions::Init( const char* commandline, const char* ospath ) xyzPrecision = DEFAULT_ANIM_EPSILON; quatPrecision = DEFAULT_QUAT_EPSILON; cycleStart = -1; + reOrient = ang_zero; src.Clear(); dest.Clear(); @@ -1422,6 +1423,23 @@ 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() ); + token = tokens.NextToken(); + if( token[0] == '-' ) + { + tokens.UnGetToken(); + break; + } + reOrient = idAngles( x, y, z ); + } + } else { MayaError( "Unknown option '%s'", token.c_str() ); diff --git a/neo/renderer/ModelManager.h b/neo/renderer/ModelManager.h index 55df4a62..780fca27 100644 --- a/neo/renderer/ModelManager.h +++ b/neo/renderer/ModelManager.h @@ -87,6 +87,7 @@ public: float rotate; float jointThreshold; int cycleStart; + idAngles reOrient; void Init( const char* commandline, const char* ospath ); diff --git a/neo/renderer/Model_gltf.cpp b/neo/renderer/Model_gltf.cpp index e57f2c02..c8127b4c 100644 --- a/neo/renderer/Model_gltf.cpp +++ b/neo/renderer/Model_gltf.cpp @@ -406,11 +406,17 @@ void idRenderModelGLTF::InitFromFile( const char* fileName, const idImportOption if( options ) { 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( scaleMat * blenderToDoomRotation, vec3_origin ); + globalTransform = idMat4( reOrientationMat * scaleMat, vec3_origin ); } ProcessNode_r( root, mat4_identity, globalTransform, data ); @@ -854,12 +860,19 @@ idFile_Memory* idRenderModelGLTF::GetAnimBin( const idStr& animName, const ID_TI if( options ) { 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( scaleMat * blenderToDoomRotation, vec3_origin ); + globalTransform = idMat4( reOrientationMat * scaleMat, vec3_origin ); } + gltfNode* root = nullptr; int channelCount = 0; for( auto channel : gltfAnim->channels ) @@ -894,7 +907,7 @@ 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 ); + //assert( newJoint->parentNum >= 0 ); if( newJoint->firstComponent == -1 ) { @@ -1030,7 +1043,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; @@ -1042,7 +1055,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 { @@ -1077,7 +1090,7 @@ idFile_Memory* idRenderModelGLTF::GetAnimBin( const idStr& animName, const ID_TI } idList joints; - GetPose( animBones[i], currJoints, blenderToDoomTransform ); + GetPose( animBones[i], currJoints, globalTransform ); for( int b = 0; b < animBones[i].Num(); b++ ) { idJointMat mat = poseMat[b]; From 250c06bfc04ba560b56e0aeffb1de3c516f282e4 Mon Sep 17 00:00:00 2001 From: HarrievG Date: Wed, 16 Nov 2022 22:10:18 +0100 Subject: [PATCH 05/15] [!] Make checkmodelhierachy non-fatal --- neo/d3xp/anim/Anim.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/neo/d3xp/anim/Anim.cpp b/neo/d3xp/anim/Anim.cpp index 4d54dc5d..441a051a 100644 --- a/neo/d3xp/anim/Anim.cpp +++ b/neo/d3xp/anim/Anim.cpp @@ -1138,7 +1138,7 @@ void idMD5Anim::CheckModelHierarchy( const idRenderModel* model ) const int jointNum = jointInfo[ i ].nameIndex; if( modelJoints[ i ].name != animationLib.JointName( jointNum ) ) { - gameLocal.Error( "Model '%s''s joint names don't match anim '%s''s", model->Name(), name.c_str() ); + gameLocal.Warning( "Model '%s''s joint names don't match anim '%s''s", model->Name(), name.c_str() ); } int parent; if( modelJoints[ i ].parent ) @@ -1151,7 +1151,7 @@ void idMD5Anim::CheckModelHierarchy( const idRenderModel* model ) const } if( parent != jointInfo[ i ].parentNum ) { - gameLocal.Error( "Model '%s' has different joint hierarchy than anim '%s'", model->Name(), name.c_str() ); + gameLocal.Warning( "Model '%s' has different joint hierarchy than anim '%s'", model->Name(), name.c_str() ); } } } From e97584642ae42344778342cc63f39b0c88f1f306 Mon Sep 17 00:00:00 2001 From: HarrievG Date: Wed, 16 Nov 2022 22:14:57 +0100 Subject: [PATCH 06/15] [!] Make missing joints on player model non-fatal --- neo/d3xp/Player.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/neo/d3xp/Player.cpp b/neo/d3xp/Player.cpp index 6ec28f6d..cf2d9e82 100644 --- a/neo/d3xp/Player.cpp +++ b/neo/d3xp/Player.cpp @@ -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 From 0829898cb437dfb3df11c2a1604cfe5f306748d5 Mon Sep 17 00:00:00 2001 From: HarrievG Date: Thu, 17 Nov 2022 22:36:12 +0100 Subject: [PATCH 07/15] [!] Fixed loading animated file exclusive models [!] Fixed reloading of models with a commandline (anims: todo) --- neo/renderer/Model_gltf.cpp | 68 ++++++++++++++++++++++++++++--------- neo/renderer/Model_gltf.h | 1 + 2 files changed, 53 insertions(+), 16 deletions(-) diff --git a/neo/renderer/Model_gltf.cpp b/neo/renderer/Model_gltf.cpp index c8127b4c..3e7cbc60 100644 --- a/neo/renderer/Model_gltf.cpp +++ b/neo/renderer/Model_gltf.cpp @@ -45,7 +45,7 @@ idCVar gltf_ModelSceneName( "gltf_ModelSceneName", "models", CVAR_SYSTEM , "Scen 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 ); @@ -299,6 +299,7 @@ void idRenderModelGLTF::InitFromFile( const char* fileName, const idImportOption int meshID = -1; name = fileName; currentSkin = nullptr; + idImportOptions* localOptions = nullptr; PurgeModel(); @@ -307,6 +308,20 @@ void idRenderModelGLTF::InitFromFile( const char* fileName, const idImportOption gltfFileName = idStr( fileName ); model_state = DM_STATIC; + if( options ) + { + commandLine = options->commandLine; + localOptions = const_cast( options ); + } + else + { + if( !commandLine.IsEmpty() ) + { + localOptions = new idImportOptions(); + localOptions->Init( commandLine, fileName ); + } + } + gltfManager::ExtractIdentifier( gltfFileName, meshID, meshName ); GLTF_Parser gltf; gltf.Load( gltfFileName ); @@ -326,13 +341,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 { @@ -347,6 +366,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; } @@ -373,19 +398,19 @@ void idRenderModelGLTF::InitFromFile( const char* fileName, const idImportOption bones.Append( currentSkin->joints ); animCount = data->GetAnimationIds( nodes[bones[0]] , animIds ); } - if( options ) + if( localOptions ) { - if( options->keepjoints.Num() ) + if( localOptions->keepjoints.Num() ) { - KeepNodes( data, options->keepjoints, bones ); + KeepNodes( data, localOptions->keepjoints, bones ); } - if( options->addOrigin ) + if( localOptions->addOrigin ) { AddOriginBone( data, bones, nodes[bones[0]]->parent ); } - if( options->remapjoints.Num() ) + if( localOptions->remapjoints.Num() ) { - RemapNodes( data, options->remapjoints, bones ); + RemapNodes( data, localOptions->remapjoints, bones ); } } } @@ -403,17 +428,17 @@ void idRenderModelGLTF::InitFromFile( const char* fileName, const idImportOption model_state = hasAnimations ? DM_CACHED : DM_STATIC; globalTransform = blenderToDoomTransform; - if( options ) + if( localOptions ) { const auto blenderToDoomRotation = idAngles( 0.0f, 0.0f, 90 ).ToMat3(); idMat3 reOrientationMat = blenderToDoomRotation; - if( options->reOrient != ang_zero ) + if( localOptions->reOrient != ang_zero ) { - reOrientationMat = options->reOrient.ToMat3(); + reOrientationMat = localOptions->reOrient.ToMat3(); } - float scale = options->scale; + float scale = localOptions->scale; idMat3 scaleMat( scale, 0, 0, 0, scale, 0, 0, 0, scale ); globalTransform = idMat4( reOrientationMat * scaleMat, vec3_origin ); @@ -426,6 +451,10 @@ 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; } @@ -455,6 +484,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 ) @@ -478,6 +511,7 @@ bool idRenderModelGLTF::LoadBinaryModel( idFile* file, const ID_TIME_T sourceTim return false; } + file->ReadString( commandLine ); file->ReadBig( model_state ); file->ReadBig( rootID ); @@ -1257,6 +1291,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() ); @@ -1314,6 +1349,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 ) diff --git a/neo/renderer/Model_gltf.h b/neo/renderer/Model_gltf.h index 899013fb..2b3d966b 100644 --- a/neo/renderer/Model_gltf.h +++ b/neo/renderer/Model_gltf.h @@ -74,6 +74,7 @@ private: dynamicModel_t model_state; idStr meshName; idStr gltfFileName; + idStr commandLine; idList md5joints; idList defaultPose; From 34d616b08cfb0eb6935e63fa77a7f67d3501d634 Mon Sep 17 00:00:00 2001 From: HarrievG Date: Wed, 23 Nov 2022 01:55:07 +0100 Subject: [PATCH 08/15] GLTF model import improvements - Added rename node option - Added option for root motion transferal - Fixed re-orient option parsing - Fixed last(?) bug in orientation frame conversion --- neo/renderer/ModelManager.cpp | 46 +++++++++------- neo/renderer/ModelManager.h | 1 + neo/renderer/Model_gltf.cpp | 101 +++++++++++++++++++++++++++++++--- 3 files changed, 120 insertions(+), 28 deletions(-) diff --git a/neo/renderer/ModelManager.cpp b/neo/renderer/ModelManager.cpp index a699159a..8f402408 100644 --- a/neo/renderer/ModelManager.cpp +++ b/neo/renderer/ModelManager.cpp @@ -1130,25 +1130,26 @@ 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; - addOrigin = 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; - reOrient = ang_zero; + 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; src.Clear(); dest.Clear(); @@ -1303,6 +1304,11 @@ void idImportOptions::Init( const char* commandline, const char* ospath ) { addOrigin = true; } + else if( token == "transfermotion" ) + { + token = tokens.NextToken( "Missing value for -transfermotion. Usage: -transfermotion [bonename]" ); + transferRootMotion = token; + } else if( token == "ignorescale" ) { ignoreScale = true; @@ -1431,13 +1437,13 @@ void idImportOptions::Init( const char* commandline, const char* ospath ) 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; } - reOrient = idAngles( x, y, z ); } } else diff --git a/neo/renderer/ModelManager.h b/neo/renderer/ModelManager.h index 780fca27..46c708b2 100644 --- a/neo/renderer/ModelManager.h +++ b/neo/renderer/ModelManager.h @@ -70,6 +70,7 @@ public: bool clearOrigin; bool clearOriginAxis; bool addOrigin; + idStr transferRootMotion; bool ignoreScale; int startframe; int endframe; diff --git a/neo/renderer/Model_gltf.cpp b/neo/renderer/Model_gltf.cpp index 3e7cbc60..0bee086a 100644 --- a/neo/renderer/Model_gltf.cpp +++ b/neo/renderer/Model_gltf.cpp @@ -262,7 +262,7 @@ static void RemapNodes( gltfData* data, const idList& remapList, con } } -static void AddOriginBone( gltfData* data, idList& bones, gltfNode* root ) +static int AddOriginBone( gltfData* data, idList& bones, gltfNode* root, gltfNode* motionTarget ) { //we need to be _very_ careful with modifying the GLTF data since it is not saved or cached!!! auto& nodeList = data->NodeList(); @@ -271,6 +271,17 @@ static void AddOriginBone( gltfData* data, idList& bones, gltfNo bones.Insert( newIdx ); newNode->name = "origin"; + if( motionTarget ) + { + newNode->translation = motionTarget->translation; + newNode->translation.y = 0; + motionTarget->translation.x = 0; + motionTarget->translation.z = 0; + + newNode->rotation = motionTarget->rotation; + motionTarget->rotation = mat3_identity.ToQuat(); + } + //patch children for( int childId : root->children ) { @@ -283,6 +294,27 @@ static void AddOriginBone( gltfData* data, idList& bones, gltfNo root->children.Alloc() = nodeList.Num() - 1; newNode->parent = root; common->Warning( "Added origin bone!" ); + return newIdx; +} + +static void RenameNodes( gltfData* data, const idList& renameList, const idList& 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. @@ -300,7 +332,7 @@ void idRenderModelGLTF::InitFromFile( const char* fileName, const idImportOption name = fileName; currentSkin = nullptr; idImportOptions* localOptions = nullptr; - + PurgeModel(); //FIXME FIXME FIXME @@ -406,12 +438,22 @@ void idRenderModelGLTF::InitFromFile( const char* fileName, const idImportOption } if( localOptions->addOrigin ) { - AddOriginBone( data, bones, nodes[bones[0]]->parent ); + gltfNode* motionTarget = nullptr; + if( !localOptions->transferRootMotion.IsEmpty() ) + { + motionTarget = data->GetNode( localOptions->transferRootMotion ); + } + + AddOriginBone( data, bones, nodes[bones[0]]->parent, motionTarget ); } if( localOptions->remapjoints.Num() ) { RemapNodes( data, localOptions->remapjoints, bones ); } + if( localOptions->renamejoints.Num() ) + { + RenameNodes( data, localOptions->renamejoints, bones ); + } } } else @@ -710,7 +752,6 @@ 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; @@ -739,12 +780,22 @@ static bool GatherBoneInfo( gltfData* data, gltfAnimation* gltfAnim, const idLis } if( options->addOrigin ) { - AddOriginBone( data, bones, data->NodeList()[bones[0]]->parent ); + gltfNode* motionTarget = nullptr; + if( !options->transferRootMotion.IsEmpty() ) + { + motionTarget = data->GetNode( options->transferRootMotion ); + } + + AddOriginBone( data, bones, data->NodeList()[bones[0]]->parent, motionTarget ); } if( options->remapjoints.Num() ) { RemapNodes( data, options->remapjoints, bones ); } + if( options->renamejoints.Num() ) + { + RenameNodes( data, options->renamejoints, bones ); + } } @@ -848,6 +899,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; @@ -888,11 +941,20 @@ 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; @@ -972,6 +1034,20 @@ idFile_Memory* idRenderModelGLTF::GetAnimBin( const idStr& animName, const ID_TI channelCount++; } + + if( options && !options->transferRootMotion.IsEmpty() ) + { + rootMotionCopyTargetId = data->GetNodeIndex( data->GetNode( options->transferRootMotion ) ); + jointAnimInfo_t* newJoint = &( jointInfo[0] ); + newJoint->animBits = ANIM_TX | ANIM_TY | ANIM_TZ | ANIM_QX | ANIM_QY | ANIM_QZ; + numAnimatedComponents += 6; + newJoint->firstComponent = -6; + for( auto& joint : jointInfo ) + { + joint.firstComponent += 6; + } + } + animBones.AssureSize( numFrames ); animBones.SetNum( numFrames ); for( int i = 0; i < numFrames; i++ ) @@ -1048,7 +1124,16 @@ idFile_Memory* idRenderModelGLTF::GetAnimBin( const idStr& animName, const ID_TI idList& values = data->GetAccessorView( 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; } @@ -1119,7 +1204,7 @@ 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; } } From 0e42fbba9347accb93ad7f0db6b143520e504dcd Mon Sep 17 00:00:00 2001 From: HarrievG Date: Thu, 24 Nov 2022 23:26:36 +0100 Subject: [PATCH 09/15] [!] Root motion transfer fixes. --- neo/renderer/Model_gltf.cpp | 37 ++++++++----------------------------- 1 file changed, 8 insertions(+), 29 deletions(-) diff --git a/neo/renderer/Model_gltf.cpp b/neo/renderer/Model_gltf.cpp index 0bee086a..3973684e 100644 --- a/neo/renderer/Model_gltf.cpp +++ b/neo/renderer/Model_gltf.cpp @@ -262,7 +262,7 @@ static void RemapNodes( gltfData* data, const idList& remapList, con } } -static int AddOriginBone( gltfData* data, idList& bones, gltfNode* root, gltfNode* motionTarget ) +static int AddOriginBone( gltfData* data, idList& 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(); @@ -271,16 +271,6 @@ static int AddOriginBone( gltfData* data, idList& bones, gltfNod bones.Insert( newIdx ); newNode->name = "origin"; - if( motionTarget ) - { - newNode->translation = motionTarget->translation; - newNode->translation.y = 0; - motionTarget->translation.x = 0; - motionTarget->translation.z = 0; - - newNode->rotation = motionTarget->rotation; - motionTarget->rotation = mat3_identity.ToQuat(); - } //patch children for( int childId : root->children ) @@ -332,7 +322,7 @@ void idRenderModelGLTF::InitFromFile( const char* fileName, const idImportOption name = fileName; currentSkin = nullptr; idImportOptions* localOptions = nullptr; - + PurgeModel(); //FIXME FIXME FIXME @@ -438,13 +428,8 @@ void idRenderModelGLTF::InitFromFile( const char* fileName, const idImportOption } if( localOptions->addOrigin ) { - gltfNode* motionTarget = nullptr; - if( !localOptions->transferRootMotion.IsEmpty() ) - { - motionTarget = data->GetNode( localOptions->transferRootMotion ); - } - AddOriginBone( data, bones, nodes[bones[0]]->parent, motionTarget ); + AddOriginBone( data, bones, nodes[bones[0]]->parent ); } if( localOptions->remapjoints.Num() ) { @@ -780,13 +765,8 @@ static bool GatherBoneInfo( gltfData* data, gltfAnimation* gltfAnim, const idLis } if( options->addOrigin ) { - gltfNode* motionTarget = nullptr; - if( !options->transferRootMotion.IsEmpty() ) - { - motionTarget = data->GetNode( options->transferRootMotion ); - } - AddOriginBone( data, bones, data->NodeList()[bones[0]]->parent, motionTarget ); + AddOriginBone( data, bones, data->NodeList()[bones[0]]->parent ); } if( options->remapjoints.Num() ) { @@ -1037,14 +1017,13 @@ idFile_Memory* idRenderModelGLTF::GetAnimBin( const idStr& animName, const ID_TI if( options && !options->transferRootMotion.IsEmpty() ) { - rootMotionCopyTargetId = data->GetNodeIndex( data->GetNode( options->transferRootMotion ) ); jointAnimInfo_t* newJoint = &( jointInfo[0] ); - newJoint->animBits = ANIM_TX | ANIM_TY | ANIM_TZ | ANIM_QX | ANIM_QY | ANIM_QZ; - numAnimatedComponents += 6; - newJoint->firstComponent = -6; + newJoint->animBits = ANIM_TX | ANIM_TY | ANIM_TZ; + numAnimatedComponents += 3; + newJoint->firstComponent = -3; for( auto& joint : jointInfo ) { - joint.firstComponent += 6; + joint.firstComponent += 3; } } From 1cd57dce1db86b8567c747d589af01f4e4b7e134 Mon Sep 17 00:00:00 2001 From: HarrievG Date: Tue, 13 Dec 2022 01:44:15 +0100 Subject: [PATCH 10/15] [!] dont crash when trying to load an animation from and GLB does not match last loaded model glb --- neo/renderer/Model_gltf.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/neo/renderer/Model_gltf.cpp b/neo/renderer/Model_gltf.cpp index 3973684e..f3f07124 100644 --- a/neo/renderer/Model_gltf.cpp +++ b/neo/renderer/Model_gltf.cpp @@ -891,6 +891,16 @@ idFile_Memory* idRenderModelGLTF::GetAnimBin( const idStr& animName, const ID_TI auto& accessors = data->AccessorList(); auto& nodes = data->NodeList(); + idStr lastGltfFileName = idStr(lastMeshFromFile->name); + idStr lastName; + gltfManager::ExtractIdentifier(lastGltfFileName, id, lastName); + + if (lastGltfFileName != gltfFileName) + { + common->Warning("Last loaded mesh was not the same file as the current animation"); + return nullptr; + } + gltfNode* nodeRoot = nodes[lastMeshFromFile->rootID]; int boneRootNode = lastMeshFromFile->rootID; if( nodeRoot->skin > -1 ) From e5ec631cafbc6bbcad015a2c4150e973ced637d6 Mon Sep 17 00:00:00 2001 From: HarrievG Date: Wed, 14 Dec 2022 01:59:54 +0100 Subject: [PATCH 11/15] [+] Allowing binding of asset library instances in gltf scenes. --- neo/idlib/MapFile_gltf.cpp | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/neo/idlib/MapFile_gltf.cpp b/neo/idlib/MapFile_gltf.cpp index e6349f9b..9e60dc11 100644 --- a/neo/idlib/MapFile_gltf.cpp +++ b/neo/idlib/MapFile_gltf.cpp @@ -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 ); } } } From 8496f9bf30761bc24e734ae613ab2d0500379d5c Mon Sep 17 00:00:00 2001 From: HarrievG Date: Wed, 14 Dec 2022 21:03:07 +0100 Subject: [PATCH 12/15] [!] Fixes regenerating bMD5Anim from GLTF animation when source is newer than binary --- neo/d3xp/anim/Anim.cpp | 45 +++++++++++++++++++++++++++++------------- 1 file changed, 31 insertions(+), 14 deletions(-) diff --git a/neo/d3xp/anim/Anim.cpp b/neo/d3xp/anim/Anim.cpp index 441a051a..6662ab5d 100644 --- a/neo/d3xp/anim/Anim.cpp +++ b/neo/d3xp/anim/Anim.cpp @@ -208,14 +208,33 @@ 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 ); @@ -1138,20 +1157,18 @@ void idMD5Anim::CheckModelHierarchy( const idRenderModel* model ) const int jointNum = jointInfo[ i ].nameIndex; if( modelJoints[ i ].name != animationLib.JointName( jointNum ) ) { - gameLocal.Warning( "Model '%s''s joint names don't match anim '%s''s", model->Name(), name.c_str() ); + 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 + + if( i > 0 && parent != jointInfo[ i ].parentNum ) { - parent = -1; - } - if( parent != jointInfo[ i ].parentNum ) - { - gameLocal.Warning( "Model '%s' has different joint hierarchy than anim '%s'", model->Name(), name.c_str() ); + gameLocal.Error( "Model '%s' has different joint hierarchy than anim '%s'", model->Name(), name.c_str() ); } } } From 0a48ec16d4426244c6df637db35ad22c1a7d6922 Mon Sep 17 00:00:00 2001 From: HarrievG Date: Wed, 14 Dec 2022 21:07:24 +0100 Subject: [PATCH 13/15] [!] Fixes loading animations from different GLTF files [!] Fixes loading file exclusive skinned models when multiple scenes are present and scene with model is not the default scene. --- neo/idlib/gltfProperties.h | 13 +++++++++ neo/renderer/ModelManager.cpp | 5 ++++ neo/renderer/ModelManager.h | 1 + neo/renderer/Model_gltf.cpp | 53 +++++++++++++++++++++++++++++------ 4 files changed, 63 insertions(+), 9 deletions(-) diff --git a/neo/idlib/gltfProperties.h b/neo/idlib/gltfProperties.h index dc5fb5dc..858253c3 100644 --- a/neo/idlib/gltfProperties.h +++ b/neo/idlib/gltfProperties.h @@ -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 ) diff --git a/neo/renderer/ModelManager.cpp b/neo/renderer/ModelManager.cpp index 8f402408..85ef7bd7 100644 --- a/neo/renderer/ModelManager.cpp +++ b/neo/renderer/ModelManager.cpp @@ -1150,6 +1150,7 @@ void idImportOptions::Init( const char* commandline, const char* ospath ) quatPrecision = DEFAULT_QUAT_EPSILON; cycleStart = -1; reOrient = ang_zero; + armature = ""; src.Clear(); dest.Clear(); @@ -1446,6 +1447,10 @@ void idImportOptions::Init( const char* commandline, const char* ospath ) } } } + else if( token == "armature" ) + { + armature = tokens.NextToken( "Missing skin name for -armature. Usage: -armature [gltfSkin name]" ); + } else { MayaError( "Unknown option '%s'", token.c_str() ); diff --git a/neo/renderer/ModelManager.h b/neo/renderer/ModelManager.h index 46c708b2..07658c7f 100644 --- a/neo/renderer/ModelManager.h +++ b/neo/renderer/ModelManager.h @@ -89,6 +89,7 @@ public: float jointThreshold; int cycleStart; idAngles reOrient; + idStr armature; void Init( const char* commandline, const char* ospath ); diff --git a/neo/renderer/Model_gltf.cpp b/neo/renderer/Model_gltf.cpp index f3f07124..be8f1918 100644 --- a/neo/renderer/Model_gltf.cpp +++ b/neo/renderer/Model_gltf.cpp @@ -890,27 +890,62 @@ 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; - idStr lastGltfFileName = idStr(lastMeshFromFile->name); + idStr lastGltfFileName = idStr( lastMeshFromFile->name ); idStr lastName; - gltfManager::ExtractIdentifier(lastGltfFileName, id, lastName); + gltfManager::ExtractIdentifier( lastGltfFileName, id, lastName ); - if (lastGltfFileName != gltfFileName) + if( options != nullptr && !options->armature.IsEmpty() ) { - common->Warning("Last loaded mesh was not the same file as the current animation"); + 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; + } + + if( rootID == -1 ) + { + common->Warning( "Could not determine root" ); return nullptr; } - gltfNode* nodeRoot = nodes[lastMeshFromFile->rootID]; - int boneRootNode = lastMeshFromFile->rootID; - if( nodeRoot->skin > -1 ) + gltfNode* nodeRoot = nodes[rootID]; + if( rootID == -1 && nodeRoot->skin > -1 ) { - boneRootNode = nodes[data->SkinList()[nodeRoot->skin]->skeleton]->children[0]; + rootID = nodes[data->SkinList()[nodeRoot->skin]->skeleton]->children[0]; } - auto gltfAnim = data->GetAnimation( name, boneRootNode ); + 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; } From defc18ddaa19de64c18eb5b25ac7267b69544b3c Mon Sep 17 00:00:00 2001 From: HarrievG Date: Fri, 16 Dec 2022 21:18:32 +0100 Subject: [PATCH 14/15] [!] Fixed loading animations when the armatures option is not used.. --- neo/renderer/Model_gltf.cpp | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/neo/renderer/Model_gltf.cpp b/neo/renderer/Model_gltf.cpp index be8f1918..c601e21b 100644 --- a/neo/renderer/Model_gltf.cpp +++ b/neo/renderer/Model_gltf.cpp @@ -928,6 +928,17 @@ idFile_Memory* idRenderModelGLTF::GetAnimBin( const idStr& animName, const ID_TI 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]; + } } if( rootID == -1 ) @@ -935,13 +946,6 @@ idFile_Memory* idRenderModelGLTF::GetAnimBin( const idStr& animName, const ID_TI common->Warning( "Could not determine root" ); return nullptr; } - - gltfNode* nodeRoot = nodes[rootID]; - if( rootID == -1 && nodeRoot->skin > -1 ) - { - rootID = nodes[data->SkinList()[nodeRoot->skin]->skeleton]->children[0]; - } - auto gltfAnim = data->GetAnimation( name, rootID ); if( !gltfAnim ) { From 5eaa7801dbb2bbf1b4bc9e8ed7b66626bea183a5 Mon Sep 17 00:00:00 2001 From: Robert Beckebans Date: Thu, 22 Dec 2022 16:46:29 +0100 Subject: [PATCH 15/15] Merge fixes, default models scene remains Scene --- neo/d3xp/anim/Anim.cpp | 6 +++--- neo/renderer/Model_gltf.cpp | 24 +++++++++++++----------- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/neo/d3xp/anim/Anim.cpp b/neo/d3xp/anim/Anim.cpp index 6662ab5d..b882be2b 100644 --- a/neo/d3xp/anim/Anim.cpp +++ b/neo/d3xp/anim/Anim.cpp @@ -208,7 +208,7 @@ bool idMD5Anim::LoadAnim( const char* filename, const idImportOptions* options ) { sourceTimeStamp = fileSystem->GetTimestamp( filename ); } - + idFile* fileptr = nullptr; bool doWrite = false; @@ -217,9 +217,9 @@ bool idMD5Anim::LoadAnim( const char* filename, const idImportOptions* options ) ID_TIME_T binTimeStamp = fileSystem->GetTimestamp( generatedFileName ); if( isGLTF ) { - if (sourceTimeStamp < binTimeStamp) + if( sourceTimeStamp < binTimeStamp ) { - fileptr = fileSystem->OpenFileReadMemory(generatedFileName); + fileptr = fileSystem->OpenFileReadMemory( generatedFileName ); } else { diff --git a/neo/renderer/Model_gltf.cpp b/neo/renderer/Model_gltf.cpp index c601e21b..bc1f277d 100644 --- a/neo/renderer/Model_gltf.cpp +++ b/neo/renderer/Model_gltf.cpp @@ -40,7 +40,7 @@ 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" ); @@ -420,21 +420,24 @@ 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 ); @@ -660,7 +663,7 @@ 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 ) ) ) { md5joints[i].parent = FindMD5Joint( node->parent->name ); } @@ -763,15 +766,17 @@ static bool GatherBoneInfo( gltfData* data, gltfAnimation* gltfAnim, const idLis { 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 ); @@ -930,12 +935,12 @@ idFile_Memory* idRenderModelGLTF::GetAnimBin( const idStr& animName, const ID_TI rootID = lastMeshFromFile->rootID; gltfNode* nodeRoot = nullptr; - if (rootID != -1 ) + if( rootID != -1 ) { nodeRoot = nodes[rootID]; } - if (nodeRoot != nullptr && nodeRoot->skin > -1) + if( nodeRoot != nullptr && nodeRoot->skin > -1 ) { rootID = nodes[data->SkinList()[nodeRoot->skin]->skeleton]->children[0]; } @@ -1396,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 ) { @@ -1474,8 +1479,6 @@ void idRenderModelGLTF::PurgeModel() void idRenderModelGLTF::LoadModel() { - assert( data ); - int num; auto& accessors = data->AccessorList(); auto& nodes = data->NodeList(); @@ -1483,7 +1486,6 @@ void idRenderModelGLTF::LoadModel() if( !fileExclusive ) { meshRoot = data->GetNode( gltf_ModelSceneName.GetString(), meshName ); - assert( meshRoot ); } gltfSkin* skin = nullptr; @@ -1535,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 ); }