From e56f696c9c96a9ba7105d4367b1cc0e9a62139f3 Mon Sep 17 00:00:00 2001 From: HarrievG Date: Sun, 31 Jul 2022 12:13:07 +0200 Subject: [PATCH] - Skinned / animated GLTF models --- neo/CMakeLists.txt | 3 + neo/idStuff.natvis | 63 ++++ neo/idlib/MapFile_gltf.cpp | 7 +- neo/idlib/gltfParser.cpp | 2 +- neo/idlib/gltfProperties.h | 111 ++++-- neo/idlib/math/Math.h | 17 + neo/idlib/math/VectorI.h | 254 +++++++++++++ neo/renderer/Model_gltf.cpp | 728 +++++++++++++++++++++--------------- neo/renderer/Model_gltf.h | 8 +- 9 files changed, 848 insertions(+), 345 deletions(-) create mode 100644 neo/idStuff.natvis diff --git a/neo/CMakeLists.txt b/neo/CMakeLists.txt index 33cd0bd2..46483660 100644 --- a/neo/CMakeLists.txt +++ b/neo/CMakeLists.txt @@ -484,6 +484,8 @@ endif (RAPIDJSON_FOUND) add_subdirectory(idlib) +file(GLOB NATVIS_SOURCES .natvis) + file(GLOB AAS_INCLUDES aas/*.h) file(GLOB AAS_SOURCES aas/*.cpp) @@ -1389,6 +1391,7 @@ set(RBDOOM3_INCLUDES ) set(RBDOOM3_SOURCES + ${NATVIS_SOURCES} ${AAS_SOURCES} ${CM_SOURCES} ${FRAMEWORK_SOURCES} diff --git a/neo/idStuff.natvis b/neo/idStuff.natvis new file mode 100644 index 00000000..5b4ab57d --- /dev/null +++ b/neo/idStuff.natvis @@ -0,0 +1,63 @@ + + + + + + + + {{Size={num} Capacity={size}}} + + num + size + + num + list + + + + + + {{{x,g},{y,g},{z,g},{w,g}}} + + + + {{{x},{y}}} + + + + {data,s} + + + + {name} + + sprite + edittext + font + text + imageSize + name + + + + + + value.object + value.function + value.string + value.f + value + value.b + value.i + value.i + + + + + [{name}]{value} + + + + [{name}] + + \ No newline at end of file diff --git a/neo/idlib/MapFile_gltf.cpp b/neo/idlib/MapFile_gltf.cpp index 46d48157..93910a94 100644 --- a/neo/idlib/MapFile_gltf.cpp +++ b/neo/idlib/MapFile_gltf.cpp @@ -218,17 +218,14 @@ MapPolygonMesh* MapPolygonMesh::ConvertFromMeshGltf( const gltfMesh_Primitive* p bin.Seek( attrBv->byteStride - ( attrib->elementSize * attrAcc->typeSize ), FS_SEEK_CUR ); } - mesh->verts[i].color2[0] = vec.x; - mesh->verts[i].color2[1] = vec.y; - mesh->verts[i].color2[2] = vec.z; - mesh->verts[i].color2[3] = vec.w; + mesh->verts[i].SetColor2( PackColor( vec ) ); } break; } case gltfMesh_Primitive_Attribute::Type::Indices: { - idVec4 vec; + idVec4i vec; for( int i = 0; i < attrAcc->count; i++ ) { bin.Read( ( void* )( &vec.x ), attrAcc->typeSize ); diff --git a/neo/idlib/gltfParser.cpp b/neo/idlib/gltfParser.cpp index 41f582c8..0bc5ef6f 100644 --- a/neo/idlib/gltfParser.cpp +++ b/neo/idlib/gltfParser.cpp @@ -2318,7 +2318,7 @@ CONSOLE_COMMAND_COMPILE( LoadGLTF, "Loads an .gltf or .glb file", idCmdSystem::A // not dots allowed in [%s]! -// [filename].[%i|%s].[gltf/glb] +// [filename].[%i|%s].[gltf|glb] bool gltfManager::ExtractIdentifier( idStr& filename, int& id, idStr& name ) { idStr extension; diff --git a/neo/idlib/gltfProperties.h b/neo/idlib/gltfProperties.h index fa01d141..ca2c67c2 100644 --- a/neo/idlib/gltfProperties.h +++ b/neo/idlib/gltfProperties.h @@ -134,6 +134,11 @@ public: gltfNode() : camera( -1 ), skin( -1 ), matrix( mat4_zero ), mesh( -1 ), rotation( 0.f, 0.f, 0.f, 1.f ), scale( 1.f, 1.f, 1.f ), translation( vec3_zero ), parent( nullptr ), dirty( true ) { } + //Only checks name! + bool operator == ( const gltfNode& rhs ) + { + return name == rhs.name; + } int camera; idList children; int skin; @@ -866,23 +871,6 @@ public: return nullptr; } - gltfNode* GetNode( gltfScene* scene, idStr name ) - { - assert( scene ); - assert( name[0] ); - - auto& nodeList = scene->nodes; - for( auto& nodeId : nodeList ) - { - if( nodes[nodeId]->name == name ) - { - return nodes[nodeId]; - } - } - - return nullptr; - } - gltfNode* GetNode( idStr sceneName, int id, idStr* name = nullptr ) { int sceneId = GetSceneId( sceneName ); @@ -913,7 +901,7 @@ public: return nullptr; } - gltfNode* GetNode( idStr sceneName, idStr name , int* id = nullptr ) + gltfNode* GetNode( idStr sceneName, idStr name , int* id = nullptr , bool caseSensitive = false ) { int sceneId = GetSceneId( sceneName ); if( sceneId < 0 || sceneId > scenes.Num() ) @@ -927,9 +915,9 @@ public: assert( name[0] ); auto& nodeList = scene->nodes; - for( auto& nodeId : nodeList ) + for( auto nodeId : nodeList ) { - if( nodes[nodeId]->name.Icmp( name ) == 0 ) + if( caseSensitive ? nodes[nodeId]->name.Cmp( name ) : nodes[nodeId]->name.Icmp( name ) == 0 ) { if( id != nullptr ) { @@ -946,10 +934,10 @@ public: int GetNodeIndex( gltfNode* node ) { int index = -1; - for( auto* node : nodes ) + for( auto& it : nodes ) { index++; - if( node == node ) + if( it == node ) { return index; } @@ -984,19 +972,81 @@ public: return nullptr; } - int GetSceneId( idStr sceneName ) const + int GetSceneId( idStr sceneName , gltfScene* result = nullptr ) const { for( int i = 0; i < scenes.Num(); i++ ) { if( scenes[i]->name == sceneName ) { + if( result != nullptr ) + { + result = scenes[i]; + } + return i; } } return -1; } - idList GetChannelIds( gltfAnimation* anim , gltfNode* node ) + void GetAllMeshes( gltfNode* node, idList& meshIds ) + { + if( node->mesh != -1 ) + { + meshIds.Append( GetNodeIndex( node ) ); + } + + for( auto child : node->children ) + { + GetAllMeshes( nodes[child], meshIds ); + } + } + + gltfSkin* GetSkin( int boneNodeId ) + { + for( auto skin : skins ) + { + if( skin->joints.Find( boneNodeId ) ) + { + return skin; + } + } + + return nullptr; + } + + gltfSkin* GetSkin( gltfAnimation* anim ) + { + auto animTargets = GetAnimTargets( anim ); + + if( !animTargets.Num() ) + { + return nullptr; + } + + for( int nodeID : animTargets ) + { + gltfSkin* foundSkin = GetSkin( nodeID ); + if( foundSkin != nullptr ) + { + return foundSkin; + } + } + + return nullptr; + } + + idList GetAnimTargets( gltfAnimation* anim ) const + { + idList result; + for( auto channel : anim->channels ) + { + result.AddUnique( channel->target.node ); + } + return result; + } + + idList GetChannelIds( gltfAnimation* anim , gltfNode* node ) const { idList result; int channelIdx = 0; @@ -1012,9 +1062,9 @@ public: return result; } - idList GetAnimationIds( gltfNode* node ) + int GetAnimationIds( gltfNode* node , idList& result ) { - idList result; + int animIdx = 0; for( auto anim : animations ) { @@ -1022,13 +1072,16 @@ public: { if( channel->target.node >= 0 && nodes[channel->target.node] == node ) { - result.Append( animIdx ); - break; + result.AddUnique( animIdx ); } } animIdx++; } - return result; + for( int nodeId : node->children ) + { + GetAnimationIds( nodes[nodeId], result ); + } + return result.Num(); } idMat4 GetViewMatrix( int camId ) const diff --git a/neo/idlib/math/Math.h b/neo/idlib/math/Math.h index 2031ac91..50e0b7cc 100644 --- a/neo/idlib/math/Math.h +++ b/neo/idlib/math/Math.h @@ -360,6 +360,8 @@ public: static void Init(); + static float RSqrt( float x ); // reciprocal square root, returns huge number when x == 0.0 + static float InvSqrt( float x ); // inverse square root with 32 bits precision, returns huge number when x == 0.0 static float InvSqrt16( float x ); // inverse square root with 16 bits precision, returns huge number when x == 0.0 @@ -502,6 +504,21 @@ ID_INLINE byte CLAMP_BYTE( int x ) return ( ( x ) < 0 ? ( 0 ) : ( ( x ) > 255 ? 255 : ( byte )( x ) ) ); } + +ID_INLINE float idMath::RSqrt( float x ) +{ + int i; + float y, r; + + y = x * 0.5f; + i = *reinterpret_cast< int* >( &x ); + i = 0x5f3759df - ( i >> 1 ); + r = *reinterpret_cast< float* >( &i ); + r = r * ( 1.5f - r * r * y ); + return r; +} + + /* ======================== idMath::InvSqrt diff --git a/neo/idlib/math/VectorI.h b/neo/idlib/math/VectorI.h index 05f938dc..1b4b9a89 100644 --- a/neo/idlib/math/VectorI.h +++ b/neo/idlib/math/VectorI.h @@ -280,4 +280,258 @@ public: } }; + +//=============================================================== +// +// idVec4i - 4D vector +// +//=============================================================== + +class idVec4i +{ +public: + uint8 x; + uint8 y; + uint8 z; + uint8 w; + + idVec4i( void ); + explicit idVec4i( const uint8 x ) + { + Set( x, x, x, x ); + } + explicit idVec4i( const uint8 x, const uint8 y, const uint8 z, const uint8 w ); + + void Set( const uint8 x, const uint8 y, const uint8 z, const uint8 w ); + void Zero( void ); + + int operator[]( const int index ) const; + uint8& operator[]( const int index ); + idVec4i operator-( ) const; + uint8 operator*( const idVec4i& a ) const; + idVec4i operator*( const uint8 a ) const; + idVec4i operator/( const uint8 a ) const; + idVec4i operator+( const idVec4i& a ) const; + idVec4i operator-( const idVec4i& a ) const; + idVec4i& operator+=( const idVec4i& a ); + idVec4i& operator-=( const idVec4i& a ); + idVec4i& operator/=( const idVec4i& a ); + idVec4i& operator/=( const uint8 a ); + idVec4i& operator*=( const uint8 a ); + + friend idVec4i operator*( const uint8 a, const idVec4i b ); + + idVec4i Multiply( const idVec4i& a ) const; + bool Compare( const idVec4i& a ) const; // exact compare, no epsilon + + bool operator==( const idVec4i& a ) const; // exact compare, no epsilon + bool operator!=( const idVec4i& a ) const; // exact compare, no epsilon + + float Length( void ) const; + float LengthSqr( void ) const; + float Normalize( void ); // returns length + float NormalizeFast( void ); // returns length + + int GetDimension( void ) const; + + const uint8* ToIntPtr( void ) const; + uint8* ToIntPtr( void ); + const char* ToString( int precision = 2 ) const; + + void Lerp( const idVec4i& v1, const idVec4i& v2, const float l ); +}; + + +ID_INLINE idVec4i::idVec4i( void ) { } + +ID_INLINE idVec4i::idVec4i( const uint8 x, const uint8 y, const uint8 z, const uint8 w ) +{ + this->x = x; + this->y = y; + this->z = z; + this->w = w; +} + +ID_INLINE void idVec4i::Set( const uint8 x, const uint8 y, const uint8 z, const uint8 w ) +{ + this->x = x; + this->y = y; + this->z = z; + this->w = w; +} + +ID_INLINE void idVec4i::Zero( void ) +{ + x = y = z = w = 0.0f; +} + +ID_INLINE int idVec4i::operator[]( int index ) const +{ + return ( &x )[index]; +} + +ID_INLINE uint8& idVec4i::operator[]( int index ) +{ + return ( &x )[index]; +} + +ID_INLINE idVec4i idVec4i::operator-( ) const +{ + return idVec4i( -x, -y, -z, -w ); +} + +ID_INLINE idVec4i idVec4i::operator-( const idVec4i& a ) const +{ + return idVec4i( x - a.x, y - a.y, z - a.z, w - a.w ); +} + +ID_INLINE uint8 idVec4i::operator*( const idVec4i& a ) const +{ + return x * a.x + y * a.y + z * a.z + w * a.w; +} + +ID_INLINE idVec4i idVec4i::operator*( const uint8 a ) const +{ + return idVec4i( x * a, y * a, z * a, w * a ); +} + +ID_INLINE idVec4i idVec4i::operator/( const uint8 a ) const +{ + float inva = 1.0f / a; + return idVec4i( x * inva, y * inva, z * inva, w * inva ); +} + +ID_INLINE idVec4i operator*( const int a, const idVec4i b ) +{ + return idVec4i( b.x * a, b.y * a, b.z * a, b.w * a ); +} + +ID_INLINE idVec4i idVec4i::operator+( const idVec4i& a ) const +{ + return idVec4i( x + a.x, y + a.y, z + a.z, w + a.w ); +} + +ID_INLINE idVec4i& idVec4i::operator+=( const idVec4i& a ) +{ + x += a.x; + y += a.y; + z += a.z; + w += a.w; + + return *this; +} + +ID_INLINE idVec4i& idVec4i::operator/=( const idVec4i& a ) +{ + x /= a.x; + y /= a.y; + z /= a.z; + w /= a.w; + + return *this; +} + +ID_INLINE idVec4i& idVec4i::operator/=( const uint8 a ) +{ + float inva = 1.0f / a; + x *= inva; + y *= inva; + z *= inva; + w *= inva; + + return *this; +} + +ID_INLINE idVec4i& idVec4i::operator-=( const idVec4i& a ) +{ + x -= a.x; + y -= a.y; + z -= a.z; + w -= a.w; + + return *this; +} + +ID_INLINE idVec4i& idVec4i::operator*=( const uint8 a ) +{ + x *= a; + y *= a; + z *= a; + w *= a; + + return *this; +} + +ID_INLINE idVec4i idVec4i::Multiply( const idVec4i& a ) const +{ + return idVec4i( x * a.x, y * a.y, z * a.z, w * a.w ); +} + +ID_INLINE bool idVec4i::Compare( const idVec4i& a ) const +{ + return ( ( x == a.x ) && ( y == a.y ) && ( z == a.z ) && w == a.w ); +} + +ID_INLINE bool idVec4i::operator==( const idVec4i& a ) const +{ + return Compare( a ); +} + +ID_INLINE bool idVec4i::operator!=( const idVec4i& a ) const +{ + return !Compare( a ); +} + +ID_INLINE float idVec4i::Length( void ) const +{ + return ( float ) idMath::Sqrt( x * x + y * y + z * z + w * w ); +} + +ID_INLINE float idVec4i::LengthSqr( void ) const +{ + return ( x * x + y * y + z * z + w * w ); +} + +ID_INLINE float idVec4i::Normalize( void ) +{ + float sqrLength, invLength; + + sqrLength = x * x + y * y + z * z + w * w; + invLength = idMath::InvSqrt( sqrLength ); + x *= invLength; + y *= invLength; + z *= invLength; + w *= invLength; + return invLength * sqrLength; +} + +ID_INLINE float idVec4i::NormalizeFast( void ) +{ + float sqrLength, invLength; + + sqrLength = x * x + y * y + z * z + w * w; + invLength = idMath::RSqrt( sqrLength ); + x *= invLength; + y *= invLength; + z *= invLength; + w *= invLength; + return invLength * sqrLength; +} + +ID_INLINE int idVec4i::GetDimension( void ) const +{ + return 4; +} + +ID_INLINE const uint8* idVec4i::ToIntPtr( void ) const +{ + return &x; +} + +ID_INLINE uint8* idVec4i::ToIntPtr( void ) +{ + return &x; +} + + #endif diff --git a/neo/renderer/Model_gltf.cpp b/neo/renderer/Model_gltf.cpp index 7041edd9..6fd2c450 100644 --- a/neo/renderer/Model_gltf.cpp +++ b/neo/renderer/Model_gltf.cpp @@ -48,6 +48,8 @@ idCVar gltf_AnimSampleRate( "gltf_AnimSampleRate", "24", CVAR_SYSTEM | CVAR_INTE static const byte GLMB_VERSION = 100; static const unsigned int GLMB_MAGIC = ( 'M' << 24 ) | ( 'L' << 16 ) | ( 'G' << 8 ) | GLMB_VERSION; static const char* GLTF_SnapshotName = "_GLTF_Snapshot_"; +static const idAngles axisTransformAngels = idAngles( 0.0f, 0.0f, 90 ); +static const idMat4 axisTransform( axisTransformAngels.ToMat3( ), vec3_origin ); bool idRenderModelStatic::ConvertGltfMeshToModelsurfaces( const gltfMesh* mesh ) @@ -76,7 +78,7 @@ void idRenderModelGLTF::ProcessNode( gltfNode* modelNode, idMat4 trans, gltfData } else { - newTrans = modelNode->matrix; + newTrans = mat4_identity; } for( auto prim : targetMesh->primitives ) @@ -151,10 +153,12 @@ void idRenderModelGLTF::InitFromFile( const char* fileName ) rootID = -1; int meshID = -1; name = fileName; + currentSkin = nullptr; + + PurgeModel( ); //FIXME FIXME FIXME - maxJointVertDist = 60; - idStr meshName; + maxJointVertDist = 10; idStr gltfFileName = idStr( fileName ); model_state = DM_STATIC; @@ -178,21 +182,21 @@ void idRenderModelGLTF::InitFromFile( const char* fileName ) bounds.Clear(); int sceneId = data->DefaultScene(); - assert( sceneId >= 0 ); + + auto scene = data->SceneList()[sceneId]; + assert( scene ); + + auto nodes = data->NodeList(); + assert( nodes.Num() ); + + //determine root node if( !meshName[0] ) { - //this needs to be fixed to correctly support multiple meshes. - // atm this will only correctlty with static models. - // we could do gltfMeshes a la md5 - // or re-use this class - auto& nodeList = data->NodeList(); - for( auto& nodeID : data->SceneList()[sceneId]->nodes ) - { - gltfNode* modelNode = nodeList[nodeID]; - assert( modelNode ); - ProcessNode( modelNode, mat4_identity, data ); - } + root = new gltfNode(); + root->name = scene->name; + root->children.Append( scene->nodes ); + rootID = -1; fileExclusive = true; } else @@ -201,37 +205,61 @@ void idRenderModelGLTF::InitFromFile( const char* fileName ) if( modelNode ) { root = modelNode; - ProcessNode( modelNode, mat4_identity, data ); } - } - if( surfaces.Num( ) <= 0 ) + if( !root ) + { + common->Warning( "Couldn't find model: '%s'", name.c_str( ) ); + MakeDefaultModel( ); + return; + } + + //get allmeshes in hierachy, starting at root. + MeshNodeIds.Clear(); + data->GetAllMeshes( root, MeshNodeIds ); + + //find all animations and bones + bones.Clear( ); + int totalAnims = 0; + for( int meshID : MeshNodeIds ) + { + gltfNode* tmpNode = nodes[meshID]; + int animCount = data->GetAnimationIds( tmpNode , animIds ); + + //check if this model has a skeleton/bones + //if not but it has an anim, create a bone from the target mesh-node as origin. + if( tmpNode->skin >= 0 ) + { + currentSkin = data->SkinList( )[tmpNode->skin]; + assert( currentSkin ); + if( currentSkin->joints.Num() ) + { + bones.Append( currentSkin->joints ); + animCount = data->GetAnimationIds( nodes[bones[0]] , animIds ); + } + } + else if( animCount ) + { + bones.Append( meshID ); + } + + totalAnims += animCount; + } + + hasAnimations = totalAnims > 0; + model_state = hasAnimations ? DM_CACHED : DM_STATIC; + + ProcessNode( root, mat4_identity, data ); + + if( surfaces.Num( ) <= 0 || surfaces.Num( ) != MeshNodeIds.Num( ) ) { common->Warning( "Couldn't load model: '%s'", name.c_str( ) ); MakeDefaultModel( ); return; } - //find all animations - animIds = data->GetAnimationIds( root ); - - hasAnimations = animIds.Num() > 0; - model_state = hasAnimations ? DM_CONTINUOUS : DM_STATIC; - - //check if this model has a skeleton - if( root->skin >= 0 ) - { - - gltfSkin* skin = data->SkinList( )[root->skin]; - bones = skin->joints; - } - else if( animIds.Num( ) ) - { - bones.Clear( ); - bones.Append( rootID ); - } - + // derive mikktspace tangents from normals FinishSurfaces( true ); @@ -281,7 +309,19 @@ bool idRenderModelGLTF::LoadBinaryModel( idFile* file, const ID_TIME_T sourceTim } data = gltfParser->currentAsset; - root = data->GetNode( gltf_ModelSceneName.GetString(), rootID ); + if( rootID != -1 ) + { + root = data->GetNode( gltf_ModelSceneName.GetString(), rootID ); + } + else + { + root = new gltfNode(); + root->name = gltf_ModelSceneName.GetString(); + gltfScene scene; + data->GetSceneId( root->name, &scene ); + root->children.Append( scene.nodes ); + } + assert( root ); int animCnt; @@ -321,13 +361,13 @@ bool idRenderModelGLTF::LoadBinaryModel( idFile* file, const ID_TIME_T sourceTim bones.SetNum( boneCnt ); } else - - + { if( root->skin == -1 && hasAnimations && !bones.Num() ) { bones.Clear( ); bones.Append( rootID ); } + } file->ReadBig( tempNum ); defaultPose.SetNum( tempNum ); @@ -350,9 +390,6 @@ bool idRenderModelGLTF::LoadBinaryModel( idFile* file, const ID_TIME_T sourceTim model_state = hasAnimations ? DM_CONTINUOUS : DM_STATIC; - //HVG_TODO: replace bonedata with md5jointData - //UpdateMd5Joints(); - return true; } @@ -361,6 +398,7 @@ void idRenderModelGLTF::UpdateMd5Joints( ) md5joints.Clear(); md5joints.Resize( bones.Num() ); md5joints.SetNum( bones.Num() ); + idStr ovrBoneName; auto& nodeList = data->NodeList(); for( int i = 0 ; i < bones.Num(); i++ ) @@ -368,8 +406,14 @@ void idRenderModelGLTF::UpdateMd5Joints( ) gltfNode* node = nodeList[bones[i]]; //check for TRS anim and its artificial root bone - if( bones.Num() == 1 && node->mesh != -1 ) + if( i == 0 && node->name != "origin" ) { + if( node->mesh == -1 ) + { + common->Warning( "First bone of model is not named \"origin\", name forced!" ); + } + + ovrBoneName = node->name; md5joints[i].name = "origin"; } else @@ -395,9 +439,30 @@ void idRenderModelGLTF::UpdateMd5Joints( ) for( int i = 0 ; i < bones.Num(); i++ ) { gltfNode* node = nodeList[bones[i]]; - if( node->parent ) + if( i && node->parent && node->parent != root ) { - md5joints[i].parent = findMd5Joint( node->parent->name ); + if( node->parent->name == ovrBoneName ) + { + md5joints[i].parent = findMd5Joint( idStr( "origin" ) ); + } + else + { + md5joints[i].parent = findMd5Joint( node->parent->name ); + } + } + } + + if( bones.Num( ) == 1 ) + { + //patch bone indices + for( auto& surf : surfaces ) + { + for( int i = 0; i < surf.geometry->numVerts; i++ ) + { + idDrawVert& base = surf.geometry->verts[i]; + base.SetColor( PackColor( ( vec4_zero ) ) ); + base.SetColor2( PackColor( ( vec4_one / 4 ) ) ); + } } } } @@ -449,48 +514,34 @@ void idRenderModelGLTF::DrawJoints( const struct renderEntity_s* ent, const view } } -bool gatherBoneInfo( gltfAnimation* gltfAnim, const idList& nodes , idList& bones, idList& jointInfo ) +bool gatherBoneInfo( gltfData* data, gltfAnimation* gltfAnim, const idList& nodes , idList& bones, idList& jointInfo ) { //Gather Bones; bool boneLess = false; - int boneLessTarget = -1; - for( auto channel : gltfAnim->channels ) - { - auto* target = nodes[channel->target.node]; - if( target->mesh != 0 ) - { + int targetNode = -1; - if( boneLessTarget == -1 || boneLessTarget != channel->target.node ) - { - assert( !boneLess ); - } + auto skin = data->GetSkin( gltfAnim ); + auto targets = data->GetAnimTargets( gltfAnim ); - boneLess = true; - boneLessTarget = channel->target.node; - } - else - { - bones.Append( channel->target.node ); - } - } - - if( !bones.Num( ) ) + if( targets.Num( ) == 1 ) { - assert( boneLess ); - } - else - { - assert( bones.Num( ) && !boneLess ); + boneLess = true; + targetNode = targets[0]; } //we cant be sure channels are sorted by bone? if( !boneLess ) { - assert( 0 ); // implement me + if( skin == nullptr ) + { + skin = data->GetSkin( targets[0] ); + } + assert( skin ); + bones.Append( skin->joints ); } else { - bones.Append( boneLessTarget ); + bones.Append( targetNode ); } //create jointInfo @@ -505,14 +556,87 @@ bool gatherBoneInfo( gltfAnimation* gltfAnim, const idList& nodes , i return boneLess; } + +idList GetPose( idList& bones, idJointMat* poseMat ) +{ + idList ret; + ret.AssureSize( bones.Num( ) ); + + for( int i = 0; i < bones.Num( ); i++ ) + { + auto* node = &bones[i]; + + idMat4 trans = mat4_identity; + gltfData::ResolveNodeMatrix( node, &trans, &bones[0] ); + + if( node->parent == nullptr ) + { + node->matrix *= axisTransform; + } + + idJointQuat& pose = ret[i]; + pose.q = ( trans.ToMat3( ).Transpose().ToQuat( ) ); + pose.t = idVec3( trans[0][3], trans[1][3], trans[2][3] ); + pose.w = pose.q.CalcW( ); + } + + for( int i = 0; i < bones.Num( ); i++ ) + { + const gltfNode* joint = &bones[i]; + idJointQuat* pose = &ret[i]; + poseMat[i].SetRotation( pose->q.ToMat3( ) ); + poseMat[i].SetTranslation( pose->t ); + if( joint->parent ) + { + int parentNum = bones.FindIndex( *joint->parent ); + pose->q = ( poseMat[i].ToMat3( ) * poseMat[parentNum].ToMat3( ).Transpose( ) ).ToQuat( ); + pose->t = ( poseMat[i].ToVec3( ) - poseMat[parentNum].ToVec3( ) ) * poseMat[parentNum].ToMat3( ).Transpose( ); + } + } + + return ret; +} +int copyBones( gltfData* data, const idList& bones, idList& out ) +{ + out.Clear(); + + auto nodes = data->NodeList(); + for( auto jointId : bones ) + { + auto* newNode = &out.Alloc(); + *newNode = *nodes[jointId]; + } + + //patch parents + for( auto& bone : out ) + { + bool found = false; + for( int i = 0; i < out.Num( ); i++ ) + { + if( bone.parent && bone.parent->name == out[i].name ) + { + bone.parent = &out[i]; + found = true; + break; + } + } + + if( !found ) + { + bone.parent = nullptr; + } + } + //patch childs! + // -> skipping because not used. + return out.Num(); +} + idFile_Memory* idRenderModelGLTF::GetAnimBin( idStr animName , const ID_TIME_T sourceTimeStamp ) { - ///keep in sync with game. + ///keep in sync with game! static const byte B_ANIM_MD5_VERSION = 101; static const unsigned int B_ANIM_MD5_MAGIC = ( 'B' << 24 ) | ( 'M' << 16 ) | ( 'D' << 8 ) | B_ANIM_MD5_VERSION; - idMat4 axisTransform( idAngles( 0.0f, 0.0f, 90.0f ).ToMat3( ), vec3_origin ); - int id; idStr gltfFileName = idStr( animName ); idStr name; @@ -544,10 +668,10 @@ idFile_Memory* idRenderModelGLTF::GetAnimBin( idStr animName , const ID_TIME_T idList bones; idList jointInfo; - bool boneLess = gatherBoneInfo( gltfAnim, nodes, bones, jointInfo ); + bool boneLess = gatherBoneInfo( data, gltfAnim, nodes, bones, jointInfo ); - idList frameCounts; + idList> animBones; idList componentFrames; idList baseFrame; @@ -557,21 +681,22 @@ idFile_Memory* idRenderModelGLTF::GetAnimBin( idStr animName , const ID_TIME_T int numJoints = bones.Num(); int numAnimatedComponents = 0; + gameLocal.Printf( "Generating MD5Anim for GLTF anim %s from scene %s\n", name.c_str(), gltf_ModelSceneName.GetString() ); + gltfNode* root = nullptr; int channelCount = 0; for( auto channel : gltfAnim->channels ) { - auto& sampler = *gltfAnim->samplers[channel->sampler]; + auto* sampler = gltfAnim->samplers[channel->sampler]; - auto* input = accessors[sampler.input]; - auto* output = accessors[sampler.output]; + auto* input = accessors[sampler->input]; + auto* output = accessors[sampler->output]; auto* target = nodes[channel->target.node]; jointAnimInfo_t* newJoint = &( jointInfo[ bones.FindIndex( channel->target.node ) ] ); idList& timeStamps = data->GetAccessorView( input ); root = target; int frames = timeStamps.Num(); - frameCounts.Append( frames ); if( numFrames != 0 && numFrames != frames ) { @@ -612,10 +737,140 @@ idFile_Memory* idRenderModelGLTF::GetAnimBin( idStr animName , const ID_TIME_T channelCount++; } + animBones.AssureSize( numFrames ); + animBones.SetNum( numFrames ); + for( int i = 0; i < numFrames; i++ ) + { + int totalCopied = copyBones( data, bones, animBones[i] ); + assert( totalCopied ); + } + gameLocal.Printf( "Total bones %i \n", bones.Num() ); + + //we can calculate frame rate by: + // max_timestamp_value / totalFrames + // but keeping it fixed for now. frameRate = gltf_AnimSampleRate.GetInteger(); INT animLength = ( ( numFrames - 1 ) * 1000 + frameRate - 1 ) / frameRate; + for( int i = 0; i < jointInfo.Num( ); i++ ) + { + jointAnimInfo_t& j = jointInfo[i]; + idStr jointName = animationLib.JointName( j.nameIndex ); + if( i == 0 && ( jointName != "origin" ) ) + { + gameLocal.Warning( "Renaming bone 0 from %s to %s \n", jointName.c_str(), "origin" ); + jointName = "origin"; + } + } + + baseFrame.SetGranularity( 1 ); + baseFrame.SetNum( bones.Num( ) ); + + gltfSkin* skin = data->GetSkin( gltfAnim );; + gltfAccessor* acc = nullptr; + if( skin != nullptr ) + { + acc = data->AccessorList( )[skin->inverseBindMatrices]; + } + else + { + skin = new gltfSkin; + skin->joints.AssureSize( 1, data->GetNodeIndex( root ) ); + idMat4 trans = mat4_identity; + data->ResolveNodeMatrix( root, &trans ); + acc = new gltfAccessor( ); + acc->matView = new idList( 1 ); + acc->matView->AssureSize( 1, trans.Inverse( ).Transpose( ) ); + } + + idJointMat* poseMat = ( idJointMat* ) _alloca16( bones.Num( ) * sizeof( poseMat[0] ) ); + baseFrame = GetPose( animBones[0], poseMat ); + + componentFrames.SetGranularity( 1 ); + componentFrames.SetNum( ( ( numAnimatedComponents * numFrames ) ) + 1 ); + int componentFrameIndex = 0; + for( int i = 0; i < numFrames; i++ ) + { + for( auto channel : gltfAnim->channels ) + { + auto sampler = gltfAnim->samplers[channel->sampler]; + + auto* input = accessors[sampler->input]; + auto* output = accessors[sampler->output]; + auto* target = nodes[channel->target.node]; + idList& timeStamps = data->GetAccessorView( input ); + int boneIndex = bones.FindIndex( channel->target.node ); + + switch( channel->target.TRS ) + { + default: + break; + case gltfAnimation_Channel_Target::none: + break; + case gltfAnimation_Channel_Target::rotation: + { + idList& values = data->GetAccessorView( output ); + if( values.Num( ) > i ) + { + animBones[i][boneIndex].rotation = *values[i]; + } + } + break; + case gltfAnimation_Channel_Target::translation: + { + idList& values = data->GetAccessorView( output ); + if( values.Num( ) > i ) + { + animBones[i][boneIndex].translation = *values[i]; + } + } + break; + case gltfAnimation_Channel_Target::scale: + idList& values = data->GetAccessorView( output ); + //animBones[boneIndex].scale = *values[i] ; + break; + } + } + for( int b = 0; b < bones.Num( ); b++ ) + { + auto* node = &animBones[i][b]; + jointAnimInfo_t* joint = &( jointInfo[b] ); + + idQuat q = node->rotation; + idVec3 t = node->translation; + + if( joint->animBits & ( ANIM_TX | ANIM_TY | ANIM_TZ ) ) + { + componentFrames[componentFrameIndex++] = t.x; + componentFrames[componentFrameIndex++] = t.y; + componentFrames[componentFrameIndex++] = t.z; + } + if( joint->animBits & ( ANIM_QX | ANIM_QY | ANIM_QZ ) ) + { + + if( node->parent == nullptr ) + { + q = axisTransformAngels.ToQuat( ) * animBones[i][b].rotation; + } + else + { + q = -animBones[i][b].rotation; + } + + componentFrames[componentFrameIndex++] = q.x; + componentFrames[componentFrameIndex++] = q.y; + componentFrames[componentFrameIndex++] = q.z; + } + } + } + + assert( componentFrames.Num( ) == ( componentFrameIndex + 1 ) ); + + + ////////////////////////////////////////////////////////////////////////// + /// Start writing //////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////// idFile_Memory* file = new idFile_Memory(); file->WriteBig( B_ANIM_MD5_MAGIC ); file->WriteBig( sourceTimeStamp ); @@ -627,18 +882,27 @@ idFile_Memory* idRenderModelGLTF::GetAnimBin( idStr animName , const ID_TIME_T file->WriteBig( numAnimatedComponents ); bounds.SetGranularity( 1 ); + bounds.AssureSize( numFrames ); bounds.SetNum( numFrames ); + + idList boundBones; + int totalCopied = copyBones( data, bones, boundBones ); + assert( totalCopied ); + for( int i = 0 ; i < numFrames; i++ ) { - for( int boneId : bones ) + bounds[i].Clear(); + for( int b = 0; b < animBones[i].Num(); b++ ) { - gltfNode* boneNode = nodes[boneId]; - idMat4 trans = mat4_identity; - gltfData::ResolveNodeMatrix( boneNode, &trans ); - trans *= axisTransform; + auto* node = &animBones[i][b]; - bounds[i].AddPoint( idVec3( trans[0][3], trans[1][3], trans[2][3] ) ); + idMat4 trans = mat4_identity; + gltfData::ResolveNodeMatrix( node, &trans, &animBones[i][0] ); + bounds[i].AddPoint( idVec3( trans[0][3], trans[1][3], trans[2][3] ) * axisTransform ); } + //the only way to do this right, is or to calculate the maxVertBoneDist on runtime , + //or pass the mesh entry for the current model decl + bounds[i].ExpandSelf( 10 ); } file->WriteBig( bounds.Num( ) ); @@ -655,26 +919,14 @@ idFile_Memory* idRenderModelGLTF::GetAnimBin( idStr animName , const ID_TIME_T { jointAnimInfo_t& j = jointInfo[i]; idStr jointName = animationLib.JointName( j.nameIndex ); + file->WriteString( jointName ); file->WriteBig( j.parentNum ); file->WriteBig( j.animBits ); file->WriteBig( j.firstComponent ); } - baseFrame.SetGranularity( 1 ); - baseFrame.SetNum( bones.Num() ); - - - for( int i = 0 ; i < bones.Num(); i++ ) - { - gltfNode* boneNode = nodes[bones[i]]; - - baseFrame[i].q = boneNode->rotation * idAngles( 0.0f, 0.0f, 90.0f ).ToMat3( ).Transpose().ToQuat( ); - baseFrame[i].t = boneNode->translation * axisTransform; - baseFrame[i].w = baseFrame[i].q.CalcW(); - } - - //raw node / skeleton + //base frame file->WriteBig( baseFrame.Num( ) ); for( int i = 0; i < baseFrame.Num( ); i++ ) { @@ -686,103 +938,6 @@ idFile_Memory* idRenderModelGLTF::GetAnimBin( idStr animName , const ID_TIME_T file->WriteVec3( j.t ); } - componentFrames.SetGranularity( 1 ); - componentFrames.SetNum( numAnimatedComponents * numFrames * numJoints + 1 ); - componentFrames[numAnimatedComponents * numFrames * numJoints ] = 0.0f; - int componentFrameIndex = 0; - - for( int i = 0 ; i < numFrames; i++ ) - { - - bool hasTranslation = false; - bool hasRotation = false; - bool transDone = false; - bool rotDone = false; - //add weight + scale - - while( !transDone && !rotDone ) - { - idVec3 t = vec3_zero; - idQuat q; - q.w = 1.0f; - - for( auto channel : gltfAnim->channels ) - { - auto& sampler = *gltfAnim->samplers[channel->sampler]; - - auto* input = accessors[sampler.input]; - auto* output = accessors[sampler.output]; - auto* target = nodes[channel->target.node]; - idList& timeStamps = data->GetAccessorView( input ); - jointAnimInfo_t* joint = &( jointInfo[ bones.FindIndex( channel->target.node ) ] ); - hasTranslation |= ( joint->animBits & ANIM_TX || joint->animBits & ANIM_TY || joint->animBits & ANIM_TZ ); - hasRotation |= ( joint->animBits & ANIM_QX || joint->animBits & ANIM_QY || joint->animBits & ANIM_QZ ); - - int targetID = nodes.FindIndex( target ); - - switch( channel->target.TRS ) - { - default: - break; - case gltfAnimation_Channel_Target::none: - break; - case gltfAnimation_Channel_Target::rotation: - { - idList& values = data->GetAccessorView( output ); - if( values.Num() > i ) - { - q = *values[i]; - } - rotDone = true; - } - break; - case gltfAnimation_Channel_Target::translation: - { - idList& values = data->GetAccessorView( output ); - if( values.Num() > i ) - { - t = *values[i]; - } - transDone = true; - } - break; - case gltfAnimation_Channel_Target::scale: // this is not supported by engine, but it should be for gltf - break; - } - - if( !transDone ) - { - transDone = !hasTranslation; - } - if( !rotDone ) - { - rotDone = !hasRotation; - } - } - - if( rotDone && transDone ) - { - if( hasTranslation ) - { - t *= axisTransform; - componentFrames[componentFrameIndex++] = t.x; - componentFrames[componentFrameIndex++] = t.y; - componentFrames[componentFrameIndex++] = t.z; - } - if( hasRotation ) - { - q *= idAngles( 0.0f, 0.0f, 90.0f ).ToMat3( ).Transpose().ToQuat( ); - idVec3 rot = q.ToRotation().GetVec(); - componentFrames[componentFrameIndex++] = q.x; - componentFrames[componentFrameIndex++] = q.y; - componentFrames[componentFrameIndex++] = q.z; - } - break; - } - } - } - - assert( componentFrames.Num() == ( componentFrameIndex + 1 ) ); //per joint timestamp values, T R file->WriteBig( componentFrames.Num( ) - 1 ); for( int i = 0; i < componentFrames.Num( ); i++ ) @@ -850,7 +1005,7 @@ void idRenderModelGLTF::WriteBinaryModel( idFile* file, ID_TIME_T* _timeStamp /* { idRenderModelStatic::WriteBinaryModel( file ); - if( file == NULL || root == nullptr ) + if( file == NULL ) { return; } @@ -906,83 +1061,33 @@ void idRenderModelGLTF::PurgeModel() purged = true; md5joints.Clear( ); defaultPose.Clear( ); - meshes.Clear( ); -} + invertedDefaultPose.Clear(); -idList getSkinningMatrix( gltfData* data, gltfSkin* skin, gltfAccessor* acc ) -{ - idMat4 world = mat4_identity; - idList values = data->GetAccessorViewMat( acc ); + animIds.Clear(); + bones.Clear(); + MeshNodeIds.Clear(); - auto& nodeList = data->NodeList( ); - int count = 0; - - if( skin->skeleton == -1 ) + //if no root id was set, it is a generated one. + if( rootID == -1 && root ) { - skin->skeleton = skin->joints[0]; + delete root; } - - for( int joint : skin->joints ) - { - world = mat4_identity; - auto* node = nodeList[joint]; - idMat4* bindMat = &values[count]; - bindMat->TransposeSelf( ); - data->ResolveNodeMatrix( node, &world ); - *bindMat = world * *bindMat; - bindMat->TransposeSelf( ); - count++; - } - return values; } void idRenderModelGLTF::LoadModel() { - int meshID; - idStr meshName; - idStr gltfFileName = idStr( name ); - - gltfManager::ExtractIdentifier( gltfFileName, meshID, meshName ); - - if( gltfParser->currentFile.Length( ) ) - { - if( gltfParser->currentAsset && gltfParser->currentFile != gltfFileName ) - { - common->FatalError( "multiple GLTF file loading not supported" ); - } - } - else - { - if( !gltfParser->Load( gltfFileName ) ) - { - MakeDefaultModel( ); - return; - } - } - - timeStamp = fileSystem->GetTimestamp( gltfFileName ); - int num; - int parentNum; - - if( !purged ) - { - PurgeModel( ); - } - - purged = false; gltfData* data = gltfParser->currentAsset; auto& accessors = data->AccessorList( ); auto& nodes = data->NodeList( ); gltfNode* meshRoot = data->GetNode( gltf_ModelSceneName.GetString(), meshName ); - auto animList = data->GetAnimationIds( meshRoot ); gltfSkin* skin = nullptr; gltfAccessor* acc = nullptr; - if( meshRoot->skin != -1 ) + if( currentSkin != nullptr ) { - skin = data->SkinList( )[meshRoot->skin]; + skin = currentSkin; acc = data->AccessorList( )[skin->inverseBindMatrices]; } else @@ -996,12 +1101,9 @@ void idRenderModelGLTF::LoadModel() acc->matView->AssureSize( 1, trans.Inverse() ); } - auto basepose = getSkinningMatrix( data, skin, acc ); - - if( meshRoot->skin == -1 ) + if( skin && skin->skeleton == -1 ) { - delete skin; - delete acc; + skin->skeleton = skin->joints[0]; } num = bones.Num(); @@ -1015,7 +1117,7 @@ void idRenderModelGLTF::LoadModel() gltfNode* node = nodes[bones[i]]; //check for TRS anim and its artficial root bone - if( bones.Num( ) == 1 && node->mesh != -1 ) + if( bones.Num( ) == 0 && node->mesh != -1 ) { md5joints[i].name = "origin"; } @@ -1039,38 +1141,28 @@ void idRenderModelGLTF::LoadModel() return &staticJoint; }; - idAngles axisTransformAngels = idAngles( 0.0f, 0.0f, 90.0f ); - idMat4 axisTransform( axisTransformAngels.ToMat3( ), vec3_origin ); for( int i = 0; i < bones.Num( ); i++ ) { - gltfNode* node = nodes[bones[i]]; - if( node->parent ) + auto* node = nodes[bones[i]]; + + if( i && node->parent && node->parent != root ) { md5joints[i].parent = findMd5Joint( node->parent->name ); } - idJointQuat& pose = defaultPose[i]; - pose.q = node->rotation; - pose.t = node->translation; } - idJointMat* poseMat = ( idJointMat* ) _alloca16( md5joints.Num( ) * sizeof( poseMat[0] ) ); - for( int i = 0; i < md5joints.Num( ); i++ ) + idJointMat* poseMat = ( idJointMat* ) _alloca16( bones.Num( ) * sizeof( poseMat[0] ) ); + idList animBones; + int totalCopied = copyBones( data, bones, animBones ); + defaultPose = GetPose( animBones, poseMat ); + + if( !currentSkin ) { - idMD5Joint* joint = &md5joints[i]; - idJointQuat* pose = &defaultPose[i]; - - poseMat[i].SetRotation( pose->q.ToMat3() * axisTransformAngels.ToMat3( ).Transpose() ); - poseMat[i].SetTranslation( pose->t * axisTransform ); - if( joint->parent ) - { - parentNum = joint->parent - md5joints.Ptr( ); - pose->q = ( poseMat[i].ToMat3( ) * poseMat[parentNum].ToMat3( ).Transpose( ) ).ToQuat( ); - pose->t = ( poseMat[i].ToVec3( ) - poseMat[parentNum].ToVec3( ) ) * poseMat[parentNum].ToMat3( ).Transpose( ); - } + delete skin; + delete acc; } - //----------------------------------------- // create the inverse of the base pose joints to support tech6 style deformation // of base pose vertexes, normals, and tangents. @@ -1087,14 +1179,16 @@ void idRenderModelGLTF::LoadModel() SIMD_INIT_LAST_JOINT( invertedDefaultPose.Ptr( ), md5joints.Num( ) ); - //deformInfo = R_BuildDeformInfo( texCoords.Num( ), basePose, tris.Num( ), tris.Ptr( ), + //auto deformInfo = R_BuildDeformInfo( texCoords.Num( ), basePose, tris.Num( ), tris.Ptr( ), // shader->UseUnsmoothedTangents( ) ); - model_state = hasAnimations ? DM_CONTINUOUS : DM_STATIC; + model_state = hasAnimations ? DM_CACHED : DM_STATIC; // set the timestamp for reloadmodels fileSystem->ReadFile( name, NULL, &timeStamp ); + purged = false; + common->UpdateLevelLoadPacifier( ); } @@ -1108,18 +1202,20 @@ void idRenderModelGLTF::TouchData() void idRenderModelGLTF::Print() const { - common->Warning( "The method or operation is not implemented." ); + idRenderModelStatic::Print(); + // TODO } void idRenderModelGLTF::List() const { - common->Warning( "The method or operation is not implemented." ); + idRenderModelStatic::List(); + // TODO } int idRenderModelGLTF::Memory() const { - common->Warning( "The method or operation is not implemented." ); - return -1; + return idRenderModelStatic::Memory(); + // TODO } dynamicModel_t idRenderModelGLTF::IsDynamicModel() const @@ -1129,17 +1225,30 @@ dynamicModel_t idRenderModelGLTF::IsDynamicModel() const void TransformVertsAndTangents_GLTF( idDrawVert* targetVerts, const int numVerts, const idDrawVert* baseVerts, const idJointMat* joints ) { - idJointMat trans = joints[0]; - //add skinning for( int i = 0; i < numVerts; i++ ) { const idDrawVert& base = baseVerts[i]; - targetVerts[i].xyz = trans * idVec4( base.xyz.x, base.xyz.y, base.xyz.z, 1.0f ); - targetVerts[i].SetNormal( trans * baseVerts[i].GetNormal( ) ); - float t3 = base.tangent[3]; - targetVerts[i].SetTangent( trans * base.GetTangent( ) ); - targetVerts[i].tangent[3] = t3; + const idJointMat& j0 = joints[base.color[0]]; + const idJointMat& j1 = joints[base.color[1]]; + const idJointMat& j2 = joints[base.color[2]]; + const idJointMat& j3 = joints[base.color[3]]; + + const float w0 = base.color2[0] * ( 1.0f / 255.0f ); + const float w1 = base.color2[1] * ( 1.0f / 255.0f ); + const float w2 = base.color2[2] * ( 1.0f / 255.0f ); + const float w3 = base.color2[3] * ( 1.0f / 255.0f ); + + idJointMat accum; + idJointMat::Mul( accum, j0, w0 ); + idJointMat::Mad( accum, j1, w1 ); + idJointMat::Mad( accum, j2, w2 ); + idJointMat::Mad( accum, j3, w3 ); + + targetVerts[i].xyz = accum * idVec4( base.xyz.x, base.xyz.y, base.xyz.z, 1.0f ); + targetVerts[i].SetNormal( accum * base.GetNormal( ) ); + targetVerts[i].SetTangent( accum * base.GetTangent( ) ); + targetVerts[i].tangent[3] = base.tangent[3]; } } @@ -1182,11 +1291,11 @@ void idRenderModelGLTF::UpdateSurface( const struct renderEntity_s* ent, const i if( r_useGPUSkinning.GetBool( ) && glConfig.gpuSkinningAvailable ) { - if( tri->verts != NULL && tri->verts != sourceSurf.geometry->verts ) + if( tri->verts != NULL && tri->verts != verts ) { R_FreeStaticTriSurfVerts( tri ); } - tri->verts = sourceSurf.geometry->verts; + tri->verts = verts; tri->ambientCache = sourceSurf.geometry->ambientCache; tri->shadowCache = sourceSurf.geometry->shadowCache; tri->referencedVerts = true; @@ -1203,11 +1312,14 @@ void idRenderModelGLTF::UpdateSurface( const struct renderEntity_s* ent, const i } TransformVertsAndTangents_GLTF( tri->verts, numVerts, verts, entJointsInverted ); - tri->tangentsCalculated = true; } + tri->tangentsCalculated = true; + //fix this. + //only vs root bone is checked. + //now, this is not the biggest issue, there is something wrong with the bounds for the gltfmodel in general. + // it did work for non skinned models, it broke somewhere down the line. #if defined(USE_INTRINSICS_SSE) - __m128 minX = vector_float_posInfinity; __m128 minY = vector_float_posInfinity; __m128 minZ = vector_float_posInfinity; @@ -1534,5 +1646,11 @@ int idRenderModelGLTF::NearestJoint( int surfaceNum, int a, int b, int c ) const idBounds idRenderModelGLTF::Bounds( const struct renderEntity_s* ent ) const { - return bounds; + if( ent == NULL ) + { + // this is the bounds for the reference pose + return bounds; + } + + return ent->bounds; } diff --git a/neo/renderer/Model_gltf.h b/neo/renderer/Model_gltf.h index 16dabd6f..b1025eb2 100644 --- a/neo/renderer/Model_gltf.h +++ b/neo/renderer/Model_gltf.h @@ -68,16 +68,14 @@ private: float maxJointVertDist; // maximum distance a vertex is separated from a joint idList animIds; idList bones; + idList MeshNodeIds; dynamicModel_t model_state; - idList SkeletonNodes; + idStr meshName; idList md5joints; idList defaultPose; idList invertedDefaultPose; - idList meshes; - deformInfo_t* deformInfo; - - MapPolygonMesh* mesh; + gltfSkin* currentSkin; private: void DrawJoints( const struct renderEntity_s* ent, const viewDef_t* view ); }; \ No newline at end of file