Jump to content
  • Advertisement

mrMatrix

Member
  • Content Count

    34
  • Joined

  • Last visited

Community Reputation

330 Neutral

2 Followers

About mrMatrix

Personal Information

  • Interests
    Art
    Programming

Recent Profile Visitors

The recent visitors block is disabled and is not being shown to other users.

  1. mrMatrix

    FBX SDK skinned animation

    Now that I have Assimp working I think Assimp does the latter and my way with the XML does the former. How do I switch between the two? How do I go from each bone knowing which vertices it deforms to each vertex knowing by which bone it is deformed?
  2. I have a stereo 3d application that needs a scene - depth 3d cursor. I found one single post talking about this while googling - from the year 2000 - about having a sphere represent the cursor. This sphere would slide along the objects of the scene according to their depth, getting the depth buffer from at the 2D cursor mouse pos then unprojecting to get position from depth. Then, he said he made that 3d pos be the center of the sphere. This is my function for reconstructing pos from depth in the FRAG shader....I just dont know how to do it in the VERTEX shader so that the sphere actually moves to the location. Any help? I am using a deferred renderer, Maybe his solution works only with forward rendering? vec3 reconstructP(vec2 UV) { vec4 vProjectedPos = vec4(1.f); vProjectedPos.rg = UV * 2.f - 1.f; vProjectedPos.b = texture(gBuf_DS, UV).r * 2.f - 1.f; vec4 vPositionVS = scene.PMinv * vProjectedPos; return vPositionVS.rgb / vPositionVS.a; } vec3 P_VS = reconstructP(v.uv);
  3. mrMatrix

    FBX SDK skinned animation

    I went back to Assimp and its working better than it did for me last year with the official master branch on github (about 1,500 commits later). I guess I should have tried it again sooner.
  4. mrMatrix

    FBX SDK skinned animation

    Yes I've looked at those slides and links but its still hard for me to understand. The math notation is very difficult if not impossible for me to understand. I did very poorly in math when I was in school. I prefer code examples that I can compile to slides (as we probably all do)
  5. mrMatrix

    FBX SDK skinned animation

    FbxNode has no member WorldTransform()....? If you do do a demo, that would be incredibly helpful. I find it really sad that there are no real tutorials on FBX converters at this stage of 3D. Its really holding me back and im sure a ton of other people too. But, dont just stop at the skeleton! Make a simple rectangle move like I have done complete with skinning. ūüėČ. I've been putting in the work, as seen in this thread, but its really really easy to get caught up in the details.
  6. mrMatrix

    FBX SDK skinned animation

    Unfortunately Dirk that code didnt seem to help because it seems to be what I already have in my code. My model is still skewed. Did you notice the edit I put into my last post on page 3 where I said I had fixed the skeletal problem with a switch back to the old code? What could that imply or is it unimportant?
  7. mrMatrix

    FBX SDK skinned animation

    Thank you and take your time . The skeletal problem was """fixed""" by putting both the old code and your 'Read' function back into ProcessJointsAndAnimations() function and switching between them. Note the discrepancies between the two approaches, though. The top is your 'Read' function, the bottom is TLang1991 void FBXtoAbj::ProcessJointsAndAnimations(FbxNode *inNode) { FbxMesh *currMesh = inNode->GetMesh(); uint numDeformers = currMesh->GetDeformerCount(); for (uint i = 0; i < numDeformers; ++i) { FbxSkin *currSkin = reinterpret_cast<FbxSkin *>(currMesh->GetDeformer(i, FbxDeformer::eSkin)); if (!currSkin) continue; uint numClusters = currSkin->GetClusterCount(); for (uint j = 0; j < numClusters; ++j) { FbxCluster *currCluster = currSkin->GetCluster(j); string currJointName = currCluster->GetLink()->GetName(); uint currJointIndex = FindJointIndexUsingName(currJointName); FbxAMatrix transformMatrix, transformLinkMatrix; FbxAMatrix geometryTransform = GetGeometryTransformation(inNode); //geometryTransform.SetIdentity(); currCluster->GetTransformMatrix(transformMatrix); //the xform of the mesh at binding time currCluster->GetTransformLinkMatrix(transformLinkMatrix); // the xform of the cluster(jnt) at binding time from joint space to world space mJoints[currJointIndex].mGlobalBindpose = transformLinkMatrix * transformMatrix * geometryTransform; mJoints[currJointIndex].mGlobalBindposeInverse = transformLinkMatrix.Inverse() * transformMatrix * geometryTransform; //mJoints[currJointIndex].mNode = currCluster->GetLink(); //associate each joint with the ctrl pts it affects uint numIndices = currCluster->GetControlPointIndicesCount(); for (uint k = 0; k < numIndices; ++k) { BlendingIndexWeightPair currBlendingIndexWeightPair; currBlendingIndexWeightPair.mBlendingIndex = currJointIndex; currBlendingIndexWeightPair.mBlendingWeight = currCluster->GetControlPointWeights()[k]; mControlPoints[currCluster->GetControlPointIndices()[k]]->mBlendingInfo.push_back(currBlendingIndexWeightPair); } /* get animation info (for 1 "take") */ //for (uint i = 0; i < mJoints.size(); ++i) { FbxTime::SetGlobalTimeMode(FbxTime::eFrames24); FbxAnimStack *currAnimStack = mScene->GetSrcObject<FbxAnimStack>(0); mScene->SetCurrentAnimationStack(currAnimStack); FbxString Name = currAnimStack->GetNameOnly(); FbxString TakeName = currAnimStack->GetName(); FbxTakeInfo* TakeInfo = mScene->GetTakeInfo(TakeName); FbxTimeSpan LocalTimeSpan = TakeInfo->mLocalTimeSpan; FbxTime Start = LocalTimeSpan.GetStart(); FbxTime Stop = LocalTimeSpan.GetStop(); FbxTime Duration = LocalTimeSpan.GetDuration(); FbxTime::EMode TimeMode = FbxTime::GetGlobalTimeMode(); FbxLongLong FrameCount = Duration.GetFrameCount(TimeMode); double FrameRate = FbxTime::GetFrameRate(TimeMode); for (FbxLongLong f = Start.GetFrameCount(TimeMode); f <= Stop.GetFrameCount(TimeMode); ++f) { FbxTime Time; Time.SetFrame(f, TimeMode); bool useDirk = 0; //bool useDirk = 1; if (useDirk) { for (FbxNode *Node : allJointsOnFBX) { //if (Node->GetName() == mJoints[i].mName) if (Node->GetName() == mJoints[currJointIndex].mName) { FbxAMatrix LocalTransform = Node->EvaluateGlobalTransform(Time); //is there a parent bone? If so, need to apply that parents global inverse transform to this node's transform if (FbxNode *Parent = Node->GetParent()) { FbxNodeAttribute *ParentAttribute = Parent->GetNodeAttribute(); if (ParentAttribute && ParentAttribute->GetAttributeType() == FbxNodeAttribute::eSkeleton) { FbxAMatrix GlobalParentTransform = Parent->EvaluateGlobalTransform(Time); LocalTransform = GlobalParentTransform.Inverse() * LocalTransform; } } mJoints[currJointIndex].keyframes.push_back(LocalTransform); } } } else { FbxAMatrix currentTransformOffset = inNode->EvaluateGlobalTransform(Time) * geometryTransform; FbxAMatrix LocalTransform = currentTransformOffset.Inverse() * currCluster->GetLink()->EvaluateGlobalTransform(Time); mJoints[currJointIndex].keyframes.push_back(LocalTransform); } } } } }
  8. mrMatrix

    FBX SDK skinned animation

    What you have on the last bit of code is CPU skinning, yes? Could you go into that in a bit more detail...when you say std::vector<Vector> I assume you mean vector4 and are doing the std::Fill with the mesh position vec4 elements? Here is some snippets of my code. I'm making progress but putting your Read() function in has made my skeletal debug not function properly. I assume I need to switch and store both local and absolute coordinates? Looking at my code below in that section, how would that be added? From this picture of the skinning on the mesh you can see that it is squished lengthwise and doesnt deform correctly. This is what I have: //get skeleton hierarchy with DFS //get inverse bindpose for joint //joints are associated with control points //get keyframe poses with your function based on readAnimation //generate / write out keyframe poses with your function 'readanimation' to XML //read from XML file //calculate skinning interpolation //send to gbuffer animation shader struct Joint { string mName; int mParentIndex; FbxAMatrix mGlobalBindposeInverse; FbxAMatrix mGlobalBindpose; FbxNode *mNode; }; ProcessSkeletonHierarchy(mScene->GetRootNode()); void FBXtoAbj::ProcessSkeletonHierarchy(FbxNode *inRootNode) { cout << "inRootNode->GetChildCount() = " << inRootNode->GetChildCount() << endl; for (int i = 0; i < inRootNode->GetChildCount(); ++i) { FbxNode *currNode = inRootNode->GetChild(i); ProcessSkeletonHierarchyRecursively(currNode, 0, -1); } } void FBXtoAbj::ProcessSkeletonHierarchyRecursively(FbxNode *inNode, int myIndex, int inParentIndex) { if (inNode->GetNodeAttribute() && inNode->GetNodeAttribute()->GetAttributeType() && inNode->GetNodeAttribute()->GetAttributeType() == FbxNodeAttribute::eSkeleton) { Joint currJoint; currJoint.mParentIndex = inParentIndex; currJoint.mName = inNode->GetName(); mJoints.push_back(currJoint); allJointsOnFBX.push_back(inNode); } for (int i = 0; i < inNode->GetChildCount(); ++i) ProcessSkeletonHierarchyRecursively(inNode->GetChild(i), int(mJoints.size()), myIndex); } //GET INVERSE BINDPOSE FOR JOINT / JOINTS ARE ASSOCIATED WITH CONTROL POINTS ProcessJointsAndAnimations(inNode); ProcessMesh(inNode); void FBXtoAbj::ProcessJointsAndAnimations(FbxNode *inNode) { FbxMesh *currMesh = inNode->GetMesh(); uint numDeformers = currMesh->GetDeformerCount(); for (uint i = 0; i < numDeformers; ++i) { FbxSkin *currSkin = reinterpret_cast<FbxSkin *>(currMesh->GetDeformer(i, FbxDeformer::eSkin)); if (!currSkin) continue; uint numClusters = currSkin->GetClusterCount(); for (uint j = 0; j < numClusters; ++j) { FbxCluster *currCluster = currSkin->GetCluster(j); string currJointName = currCluster->GetLink()->GetName(); uint currJointIndex = FindJointIndexUsingName(currJointName); FbxAMatrix transformMatrix, transformLinkMatrix; FbxAMatrix geometryTransform = GetGeometryTransformation(inNode); //geometryTransform.SetIdentity(); currCluster->GetTransformMatrix(transformMatrix); //the xform of the mesh at binding time currCluster->GetTransformLinkMatrix(transformLinkMatrix); // the xform of the cluster(jnt) at binding time from joint space to world space mJoints[currJointIndex].mGlobalBindpose = transformLinkMatrix * transformMatrix * geometryTransform; mJoints[currJointIndex].mGlobalBindposeInverse = transformLinkMatrix.Inverse() * transformMatrix * geometryTransform; //JOINTS ARE ASSOCIATED WITH CONTROL POINTS uint numIndices = currCluster->GetControlPointIndicesCount(); for (uint k = 0; k < numIndices; ++k) { BlendingIndexWeightPair currBlendingIndexWeightPair; currBlendingIndexWeightPair.mBlendingIndex = currJointIndex; currBlendingIndexWeightPair.mBlendingWeight = currCluster->GetControlPointWeights()[k]; mControlPoints[currCluster->GetControlPointIndices()[k]]->mBlendingInfo.push_back(currBlendingIndexWeightPair); } } } } struct VertexBlendingInfo { uint mBlendingIndex; double mBlendingWeight; bool operator < (const VertexBlendingInfo &rhs) { return (mBlendingWeight > rhs.mBlendingWeight); } }; void FBXtoAbj::ProcessMesh(FbxNode *inNode) { ../ PNTIWVertex temp; temp.mPosition = currCtrlPoint->mPosition; temp.mUV = UV[j][0]; temp.mTangent = tangent[j]; temp.mNormal = normal[j]; for (uint k = 0; k < currCtrlPoint->mBlendingInfo.size(); ++k) { VertexBlendingInfo currBlendingInfo; currBlendingInfo.mBlendingWeight = currCtrlPoint->mBlendingInfo[k].mBlendingWeight; currBlendingInfo.mBlendingIndex = currCtrlPoint->mBlendingInfo[k].mBlendingIndex; temp.mVertexBlendingInfos.push_back(currBlendingInfo); } temp.SortBlendingInfoByWeight(); } struct PNTIWVertex { glm::vec3 mPosition, mNormal, mTangent; glm::vec2 mUV; vector<VertexBlendingInfo> mVertexBlendingInfos; void SortBlendingInfoByWeight() { sort(mVertexBlendingInfos.begin(), mVertexBlendingInfos.end()); } bool operator == (PNTIWVertex &rhs) { bool sameBlendingInfo = 1; //only compare the blending infos when there is any if (!(mVertexBlendingInfos.empty() && rhs.mVertexBlendingInfos.empty())) { //each vert should have 4 index-weight blending info pairs because games typically distribute weights to 4 joints for (uint i = 0; i < 4; ++i) { if (mVertexBlendingInfos[i].mBlendingIndex != rhs.mVertexBlendingInfos[i].mBlendingIndex || abs(mVertexBlendingInfos[i].mBlendingWeight - rhs.mVertexBlendingInfos[i].mBlendingWeight) > .001 ) { sameBlendingInfo = 0; break; } } } bool result0 = (mPosition == rhs.mPosition); bool result1 = (mNormal == rhs.mNormal); bool result2 = (mUV == rhs.mUV); bool result3 = (mTangent == rhs.mTangent); return result0 && result1 && result2 && result3 && sameBlendingInfo; } }; //GENERATE / WRITE OUT KEYFRAME POSES WITH YOUR FUNCTION 'READ' TO XML void FBXtoAbj::WriteAnimationToStream(ostream& inStream) { inStream << fixed << setprecision(3); inStream << "\t\t<animation name='" << mAnimationName << "' length='" << mAnimationLength << "' numJoints='" << mJoints.size() << "'>\n"; for (uint i = 0; i < mJoints.size(); ++i) { string usableParentName = (i == 0) ? "ROOT" : mJoints[mJoints[i].mParentIndex].mName; //inStream << "\t\t\t<track id='" << i << "' name='" << mJoints[i].mName << "'>\n"; inStream << "\t\t\t<track id='" << i << "' name='" << mJoints[i].mName << "' parent='" << usableParentName << "'>\n"; FbxTime::SetGlobalTimeMode(FbxTime::eFrames24); FbxAnimStack *currAnimStack = mScene->GetSrcObject<FbxAnimStack>(0); mScene->SetCurrentAnimationStack(currAnimStack); FbxString Name = currAnimStack->GetNameOnly(); FbxString TakeName = currAnimStack->GetName(); FbxTakeInfo* TakeInfo = mScene->GetTakeInfo(TakeName); FbxTimeSpan LocalTimeSpan = TakeInfo->mLocalTimeSpan; FbxTime Start = LocalTimeSpan.GetStart(); FbxTime Stop = LocalTimeSpan.GetStop(); FbxTime Duration = LocalTimeSpan.GetDuration(); FbxTime::EMode TimeMode = FbxTime::GetGlobalTimeMode(); FbxLongLong FrameCount = Duration.GetFrameCount(TimeMode); double FrameRate = FbxTime::GetFrameRate(TimeMode); for (FbxLongLong f = Start.GetFrameCount(TimeMode); f <= Stop.GetFrameCount(TimeMode); ++f) { FbxTime Time; Time.SetFrame(f, TimeMode); for (FbxNode *Node : allJointsOnFBX) { if (Node->GetName() == mJoints[i].mName) { inStream << "\t\t\t\t <frame num='" << f << "'>\n"; FbxAMatrix LocalTransform = Node->EvaluateGlobalTransform(Time); //is there a parent bone? If so, need to apply that parents global inverse transform to this node's transform if (FbxNode *Parent = Node->GetParent()) { FbxNodeAttribute *ParentAttribute = Parent->GetNodeAttribute(); if (ParentAttribute && ParentAttribute->GetAttributeType() == FbxNodeAttribute::eSkeleton) { FbxAMatrix GlobalParentTransform = Parent->EvaluateGlobalTransform(Time); LocalTransform = GlobalParentTransform.Inverse() * LocalTransform; } FbxVector4 Translation(LocalTransform.GetT()); inStream << "\t\t\t\t\t"; inStream << "<decompT>" << static_cast<float>(Translation.mData[0]) << "," << static_cast<float>(Translation.mData[1]) << "," << static_cast<float>(Translation.mData[2]) << "</decompT>\n"; FbxQuaternion Rotation(LocalTransform.GetQ()); inStream << "\t\t\t\t\t"; inStream << "<decompR>" << static_cast<float>(Rotation.mData[0]) << "," << static_cast<float>(Rotation.mData[1]) << "," << static_cast<float>(Rotation.mData[2]) << "," << static_cast<float>(Rotation.mData[3]) << "</decompR>\n"; } inStream << "\t\t\t\t </frame>\n"; } } } inStream << "\t\t\t </track>\n"; } inStream << "\t\t</animation>\n"; } //read from XML file //calculate skinning interpolation //apply pose to mesh ApplyPose(AnimationTime, myAbj.allAnims[0].trackData[0], glm::mat4(1.f)); // void Object::ApplyPose(float AnimationTime, TrackData track, const glm::mat4 &ParentTransform) { glm::quat RotationQ; CalcInterpolatedRotationGLM_solo(AnimationTime, track, RotationQ); glm::mat4 RotationM = glm::toMat4(RotationQ); glm::vec3 Translation; CalcInterpolatedPositionGLM_solo(AnimationTime, track, Translation); glm::mat4 TranslationM = glm::translate(glm::mat4(1.f), Translation); // // Combine the above transformations glm::mat4 NodeTransform = TranslationM * RotationM; for (int i = 0; i < myAbj.allBindSkeletons[0].skeletonCt; ++i) { if (myAbj.allBindSkeletons[0].skeletonData[i].name == track.name) { string gBonesName; gBonesName.append("gBones["); gBonesName.append(to_string(track.id)); gBonesName.append("]"); NameGBones2 xformNameCombo; xformNameCombo.animatedXform = ParentTransform * NodeTransform * myAbj.allBindSkeletons[0].skeletonData[i].inverseBindpose; //xformNameCombo.animatedXform = glm::mat4(1.f); xformNameCombo.name = gBonesName; xformNameCombo.nameMesh = name->val_s; myAbj.aiGbones2.push_back(xformNameCombo); } } for (auto &i : myAbj.allAnims[0].trackData) { if (i.parent == track.name) { //cout << "found parent / child for : " << track.name << " " << i.name << endl; ApplyPose(AnimationTime, i, ParentTransform * NodeTransform); } } }
  9. mrMatrix

    FBX SDK skinned animation

    I'm just confused about what you're talking about and I've gotten lost. This is the last big road block for my gamedev endevours. Is it possible for you to write a little more code for the CPU skinning side of things to get me going with the debug? I'm sorry to be such a noob at this, but i just cant see the light at the end of the tunnel if you know what I mean. Also, what did you mean by this on page 1? Could you explain this // DON'T get the geometric transform here - it does not propagate! Bake it into the mesh instead! This makes it also easier to read the skinning from the clusters! // Export your mesh and bake the geometric transform into the vertex attributes! Also, your ReadAnimation function (second one) could you clarify that that is the final animation (skinning) matrix? I always read that I need to apply a inverse bindpose of the joint to that, but you dont specify how to get that exactly although I've asked you once before in this thread and I wasnt able to deduce what you meant. So, if yo could clarify that as well, if and how to get the bindpose for the joint to go along with your ReadAnimation() function that would be great.
  10. mrMatrix

    FBX SDK skinned animation

    I saw your code but I used TLang's extended article to get me to where I am with the skeleton working. Now to go back to CPU I dont see the point since my mesh is already heavily simplified. Again, I'm just following his published article. What you propose is to what exactly? Write individual poses onto my XML file and then read them back and then load them onto the GPU in a sequence instead of what I'm doing now which is...traditional gpu skinning? I'm sorry, the FBX terminology is confusing, and according to TLang a " Each cluster is and is not a joint...... You can see a cluster as a joint, but actually, inside each cluster, there is a "link". This "link" is actually the real joint,"
  11. mrMatrix

    FBX SDK skinned animation

    I've perhaps narrowed the problem down to not transposing the output per frame animation matricies, but I've tried combinations of each one without success. How do I know I need to transpose a particular matrix? Do I need to transpose the joint matrix * the parent matrix This is a combination of T * R interpolated matricies multiplied by the parent matrix and then multiplied by the inverse bind pose. So I have animatedxform = some combination of ParentXform * T * R * invbindpose
  12. mrMatrix

    FBX SDK skinned animation

    Using spheres I was able to create and animate a generalized "skeleton" to visualize skeletal data from fbx files. But, now when I go to skin the mesh it moves but it all blows up on me. I've gotten and stored the bone space joint animation matricies per frame and multiplied them by the inverse bind pose of the same joint, but to no avail. My shader code for the gbufferAnim is similar to the ogldev tutorial 38 code for assimp. Does anyone think I'm missing something critical here to narrow it down? layout(location = 0) in vec3 pE; layout(location = 1) in vec2 uvE; layout(location = 2) in vec3 tE; layout(location = 3) in vec3 nE; layout(location = 4) in ivec4 boneIdE; layout(location = 5) in vec4 boneWtE; const int MAX_BONES = 100; uniform mat4 gBones[MAX_BONES]; mat4 BoneTransform = gBones[boneIdE[0]] * boneWtE[0]; BoneTransform += gBones[boneIdE[1]] * boneWtE[1]; BoneTransform += gBones[boneIdE[2]] * boneWtE[2]; BoneTransform += gBones[boneIdE[3]] * boneWtE[3]; gl_Position = MVP * BoneTransform * vec4(pE, 1.f);
  13. mrMatrix

    FBX SDK skinned animation

    In what file format should I save my translated vertex data and skeleton data then in your opinion? This is for my solo development at home as a hobby, not to sell a game at this time but still I would like to know what the "right" format is.
  14. mrMatrix

    FBX SDK skinned animation

    So you mean creating a series of locators or small boxes with GL_LINE / GL_LINE_LOOP and apply the recomposed model matrix to them?
  15. mrMatrix

    FBX SDK skinned animation

    Thank you for your feedback. I checked your results against my previous results from my first foray into FBX and they somewhat match except that my old code is a lot more bloated. I notice you didn't mention anything about a bindpose or comparing against a bindpose to actually get the mesh to move in the viewport which brings me to some questions: I assume I'll be recomposing the vector4 T/R values to get my Model matrix at runtime...looking at my XML file how would I approach doing that when I have so much data? Do I need to compare against a bindpose? I wrote into the XML the decompT/R per joint per frame. In my .abjmesh file I have pos/uv/tan/normal/boneIdx/boneWt information for each vertex. This example mesh rig animation is just half a torso rotating a shoulder up and then to the center like in L. Spiro's example but over a 50 frame duration animated at 24fps. To sync with the game loop I need the delta time per frame like L.Spiro said, but how do I stay in sync with reading from my XML file if things get heavy and bogged down ? Do you have any next steps after your previous example? I made this XML file from reference from the TLang1991 tutorial.
  • Advertisement
√ó

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!