Jump to content
  • Advertisement
dragon-kurve

OpenGL Mesh is distorted(half-ish) after applying skinning

Recommended Posts

I'm trying to implement skeletal animation using FBX SDK and OpenGL(with glm).

 

First of all, let me show results.

original.thumb.png.bc93a0e82b300f39546cb6ffd49da386.png

Above is original mesh, without animation.

skinned.thumb.png.6e9bb4587dc1933f81b0fcf7fb217e04.png

And this is the result.

 

My method to generate matrices is:

// joint is ordered by id, root id is 0
for (u8 i = 0; i < jointCount; ++i) {
	auto track = mCurrAnimation->track(i); // track is a list of key frames, which contains translation(vec3) and rotation(quat)

	auto& currKey = track->keys[mCurrKeyIndex]; // get current key frame
	auto& nextKey = track->keys[mNextKeyIndex]; // get next key frame
  
	auto& joint = mSkeleton->joints[i]; // get joint by id

	auto t = glm::mix(currKey.t, nextKey.t, alpha); // lerp translation
	auto q = glm::slerp(currKey.q, nextKey.q, alpha); // slerp rotation

	auto skinTransform = glm::translate(glm::mat4{1.0f}, t);
	skinTransform = skinTransform * glm::mat4_cast(q);
		
	glm::mat4 parentSkinTransform{1.0};
	if (joint.parentId > 0) { // if not root
		parentSkinTransform = mJointMatrices[joint.parentId];
	}

	mJointMatrices[i] = parentSkinTransform * skinTransform; // to parent space
	mSkinMatrices[i] = mJointMatrices[i] * joint.invBindPose; // inverse bind pose
}

I'm really struggling with this problem. As you can see, also left side of mesh is slightly distorted.

 

For more detail, these are how I got inverse bind pose and key frames from FBX.

// get joint inverse bind pose
if (const auto attrib = node->GetNodeAttribute();
	attrib) {
	const auto attribType = attrib->GetAttributeType();
	if (attribType == FbxNodeAttribute::eSkeleton) {
		CvtJoint cvtJoint{node->GetName(), id, parentId};
		cvtJoint.invBindPose = makeMat4(node->EvaluateGlobalTransform().Inverse());
		mCvtJointList.emplace_back(cvtJoint);
	}
}
// get key frames
for (auto frameIndex = startCount; frameIndex < stopCount; ++frameIndex) {
	FbxTime time{};
	time.SetFrame(frameIndex, timeMode);
	cvtAnimKey.time = static_cast<f32>(time.GetSecondDouble()); // get time of frame

	auto transform = node->EvaluateGlobalTransform(time); // get global transform at time
	if (const auto parent = node->GetParent(); // if node has parent
		parent) {
		auto parentTransform = parent->EvaluateGlobalTransform(time); // get parent global transform at time
		transform = parentTransform.Inverse() * transform; // to local space
	}
	cvtAnimKey.t = makeVec3(transform.GetT()); // FbxVector4 -> glm::vec3
	cvtAnimKey.q = makeQuat(transform.GetQ()); // FbxQuaternion -> glm::quat

	cvtAnimTrack.keys.emplace_back(cvtAnimKey); // add key to track
}

 

Any suggestion or advice is really appreciated...

Share this post


Link to post
Share on other sites
Advertisement

UPDATE:

 

I found that my method to get vertex skin weight/joint index was completely wrong.

// joint index - vertex weight pair
// Which is compelety wrong.
std::vector<std::vector<std::pair<glm::u8, glm::f32>>> skinData{};
skinData.resize(mesh->GetControlPointsCount());

const auto deformerCount = mesh->GetDeformerCount(FbxDeformer::eSkin);
for (int deformerIndex = 0; deformerIndex < deformerCount; ++deformerIndex) {
	auto skin = FbxCast<FbxSkin>(mesh->GetDeformer(deformerIndex, FbxDeformer::eSkin));

	const auto clusterCount = skin->GetClusterCount();
	for (int clusterIndex = 0; clusterIndex < clusterCount; ++clusterIndex) {
		const auto cluster = skin->GetCluster(clusterIndex);
		const auto link = cluster->GetLink();
		const auto linkName = link->GetName();

		const auto it = std::find_if(mCvtSkeleton.mJointList.begin(), mCvtSkeleton.mJointList.end(), [linkName](const auto& cvtJoint) {
			return cvtJoint.name == linkName;
		});
		if (it == mCvtJointList.end()) {
			continue;
		
		// transformation of joint
		FbxAMatrix transformLinkMatrix{};
		cluster->GetTransformLinkMatrix(transformLinkMatrix);
		// transformation of mesh
		FbxAMatrix transformMatrix{};
		cluster->GetTransformMatrix(transformMatrix)
		it->invBindPose = makeMat4(transformLinkMatrix.Inverse() * transformMatrix);

		const auto skinIndicesCount = cluster->GetControlPointIndicesCount();
		const auto skinIndices = cluster->GetControlPointIndices();
		const auto skinWeights = cluster->GetControlPointWeights();
		for (int skinIndex = 0; skinIndex < skinIndicesCount; ++skinIndex) {
            	// This is the wrong part. I should use control point index instead of joint id.
			skinData[skinIndices[skinIndex]].emplace_back(it->id, makeF32(skinWeights[skinIndex]));
		}
	}
}

Then later, when I call

FbxMesh::GetPolygonVertex(int pPolygonIndex, int pPositionInPolygon ) const -> int

that returns control point index.

 

That means I need someting like this

struct CvtSkinInfo
{
	glm::u8 mJointId;
	glm::u16 mCpIndex;
	glm::f32 mCpWeight;
};

to correctly get joint id and vertex weight of a control point.

 

I'll fix this and share again.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

  • Advertisement
×

Important Information

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

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!