• Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
  • Asset Store
  • Get Unity

UNITY ACCOUNT

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account
  • Blog
  • Forums
  • Answers
  • Evangelists
  • User Groups
  • Beta Program
  • Advisory Panel

Navigation

  • Home
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
    • Blog
    • Forums
    • Answers
    • Evangelists
    • User Groups
    • Beta Program
    • Advisory Panel

Unity account

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account

Language

  • Chinese
  • Spanish
  • Japanese
  • Korean
  • Portuguese
  • Ask a question
  • Spaces
    • Default
    • Help Room
    • META
    • Moderators
    • Topics
    • Questions
    • Users
    • Badges
  • Home /
avatar image
Question by Keiro5 · Sep 10, 2019 at 09:14 PM · mesh3driggingskinnedmeshrendererskinning

How to calculate nearest bones, weights and bindposes programmatically?

Hey guys,

I'm trying to implement mesh rigging algorithm inside the Unity.

For testing, I'm using Ethan's mesh. The algorithm looks something like:

  1. For each bone, find all related bones (parents and children)

  2. For each vertex in mesh, find nearest bone in hierarchy

  3. Then, find nearest four (or less) related bones

  4. Calculate weights inversely proportional to the distance from vertex

  5. Calculate bindposes

This is how mesh looks like with 1 bone quality set in SkinnedMeshRenderer: alt text And this is with 4 bone quality: alt text

I was working on it for a quite while and don't know how to fix this problem.

Here's the code I am using:

 public class RiggingHelper : MonoBehaviour
 {
     private GameObject rootBoneObject;
     private Transform[] bones;
     private Mesh mesh;
     private float boneDistance;
     private int boneIndex;
     private int vertexCount;
     private BoneWeight[] boneWeights;
     private Matrix4x4[] bindposes;
     private int[] boneIndicies;
     private Vector3[] vertices;
     private Vector3[] bonesPositions;
 
     private bool finished = false;
     private bool calcWeightsFinished = false;
 
     private Vector3 weightBonesVertex;
     private Vector3[] weightBonesPositions;
     private float[] weightBonesWeights;
     private int[] weightBonesIndices;
 
     private List<Vector3>[] relatedBonesPositions;
     private List<int>[] relatedBonesIndices;
 
     public void SkinMesh(SkinnedMeshRenderer meshRenderer, GameObject skeleton)
     {
         rootBoneObject = skeleton;
         List<Transform> bonesList = new List<Transform>();
         foreach (var bone in skeleton.GetComponentsInChildren<Transform>())
         {
             if (bone.parent != null)
                 bonesList.Add(bone);
         }
         bones = bonesList.ToArray();
 
         mesh = meshRenderer.sharedMesh;
         vertexCount = mesh.vertexCount;
         meshRenderer.bones = bones;
         boneWeights = new BoneWeight[vertexCount];
         boneIndicies = new int[vertexCount];
         vertices = mesh.vertices;
         bonesPositions = new Vector3[bones.Length];
 
         for (int i = 0; i < bones.Length; i++)
         {
             bonesPositions[i] = bones[i].position;
         }
 
         StartCoroutine(SkinMeshCoroutine());
     }
 
     private IEnumerator SkinMeshCoroutine()
     {
         ThreadPool.QueueUserWorkItem(FindNearestBones);
         while (!finished)
             yield return null;
 
         FindRelatedBones();
 
         finished = false;
         ThreadPool.QueueUserWorkItem(CalcAdditionalWeights);
         while (!finished)
             yield return null;
 
         // Create bindposes
         bindposes = new Matrix4x4[bones.Length];
         for (int i = 0; i < bindposes.Length; i++)
         {
             if (bones[i].parent != null)
                 bindposes[i] = bones[i].worldToLocalMatrix * bones[i].parent.localToWorldMatrix;
             else
                 bindposes[i] = bones[i].worldToLocalMatrix;
         }
 
         mesh.boneWeights = boneWeights;
         mesh.bindposes = bindposes;
     }
 
     private void FindNearestBones(object token)
     {
         // For each of the vertices, find nearest bone
         for (int i = 0; i < vertexCount; i++)
         {
             Vector3 vertex = vertices[i];
             boneIndex = 0;
             boneDistance = Vector3.Distance(bonesPositions[0], vertex);
             for (int j = 1; j < bones.Length; j++)
             {
                 float newDistance = Vector3.Distance(bonesPositions[j], vertex);
                 if (newDistance < boneDistance)
                 {
                     boneIndex = j;
                     boneDistance = newDistance;
                 }
             }
             boneIndicies[i] = boneIndex;
         }
         finished = true;
     }
 
     private void FindRelatedBones()
     {
         relatedBonesPositions = new List<Vector3>[bones.Length];
         relatedBonesIndices = new List<int>[bones.Length];
 
         // Find all related bones, including children and parents
         for (int i = 0; i < bones.Length; i++)
         {
             Transform startBone = bones[i];
             relatedBonesPositions[i] = new List<Vector3>();
             relatedBonesIndices[i] = new List<int>();
 
             relatedBonesPositions[i].Add(startBone.position);
             relatedBonesIndices[i].Add(i);
 
             foreach (var child in startBone.GetComponentsInChildren<Transform>())
             {
                 if (child.gameObject.GetInstanceID() != startBone.gameObject.GetInstanceID())
                 {
                     relatedBonesPositions[i].Add(child.position);
                     for (int j = 0; j < bones.Length; j++)
                     {
                         if (bones[j].gameObject.GetInstanceID() == child.gameObject.GetInstanceID())
                         {
                             relatedBonesIndices[i].Add(j);
                             break;
                         }
                     }
                 }
             }
             foreach (var parent in startBone.GetComponentsInParent<Transform>())
             {
                 if (parent.gameObject.GetInstanceID() != startBone.gameObject.GetInstanceID() &&
                     parent.gameObject.GetInstanceID() != startBone.parent.gameObject.GetInstanceID())
                 {
                     for (int j = 0; j < bones.Length; j++)
                     {
                         if (bones[j].gameObject.GetInstanceID() == parent.gameObject.GetInstanceID())
                         {
                             relatedBonesPositions[i].Add(parent.position);
                             relatedBonesIndices[i].Add(j);
                             break;
                         }
                     }
                 }
             }
         }
     }
 
     private void CalcAdditionalWeights(object token)
     {
         for (int i = 0; i < boneIndicies.Length; i++)
         {
             weightBonesVertex = vertices[i];
             weightBonesPositions = relatedBonesPositions[boneIndicies[i]].ToArray();
             weightBonesIndices = relatedBonesIndices[boneIndicies[i]].ToArray();
             calcWeightsFinished = false;
             GetBonesWithWages();
 
             // Assign bone indicies and weights
             for (int j = 0; j < weightBonesWeights.Length; j++)
             {
                 switch (j)
                 {
                     case 0:
                         boneWeights[i].boneIndex0 = weightBonesIndices[j];
                         boneWeights[i].weight0 = weightBonesWeights[j];
                         break;
                     case 1:
                         boneWeights[i].boneIndex1 = weightBonesIndices[j];
                         boneWeights[i].weight1 = weightBonesWeights[j];
                         break;
                     case 2:
                         boneWeights[i].boneIndex2 = weightBonesIndices[j];
                         boneWeights[i].weight2 = weightBonesWeights[j];
                         break;
                     case 3:
                         boneWeights[i].boneIndex3 = weightBonesIndices[j];
                         boneWeights[i].weight3 = weightBonesWeights[j];
                         break;
                     default:
                         break;
                 }
             }
         }
         finished = true;
     }
 
     private void GetBonesWithWages()
     {
         float[] distances = new float[weightBonesPositions.Length];
 
         for (int i = 0; i < distances.Length; i++)
             distances[i] = Vector3.Distance(weightBonesVertex, weightBonesPositions[i]);
 
         // Sort distances ascending
         for (int j = distances.Length - 1; j > 0; j--)
         {
             for (int i = 0; i < j; i++)
             {
                 if (distances[i] > distances[i + 1])
                 {
                     // Sort distance, position and index
                     float tmpDist = distances[i];
                     distances[i] = distances[i + 1];
                     distances[i + 1] = tmpDist;
 
                     Vector3 tmpPos = weightBonesPositions[i];
                     weightBonesPositions[i] = weightBonesPositions[i + 1];
                     weightBonesPositions[i + 1] = tmpPos;
 
                     int tmpIndex = weightBonesIndices[i];
                     weightBonesIndices[i] = weightBonesIndices[i + 1];
                     weightBonesIndices[i + 1] = tmpIndex;
                 }
             }
         }
 
         // Take up to 4 distances, calc their weights and find corresponding indices
         float distanceSum = 0f;
         int index = 0;
 
         for (int i = 0; i < 4; i++)
         {
             if (i < distances.Length)
             {
                 distanceSum += distances[i];
                 index++;
             }
             else
                 break;
         }
 
         weightBonesWeights = new float[index];
         for (int i = 0; i < 4; i++)
         {
             if (i < distances.Length)
             {
                 weightBonesWeights[i] = distances[i] / distanceSum;
             }
             else
                 break;
         }
         Array.Reverse(weightBonesWeights);
 
         calcWeightsFinished = true;
     }
 }

 


2019-09-10-20h28-46.png (255.5 kB)
2019-09-10-20h29-43.png (246.4 kB)
Comment

People who like this

0 Show 0
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users

1 Reply

· Add your reply
  • Sort: 
avatar image

Answer by mo0stafa · Oct 12, 2022 at 02:52 PM

Hi did you fix it

Comment

People who like this

0 Show 0 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users

Your answer

Hint: You can notify a user about this post by typing @username

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Welcome to Unity Answers

If you’re new to Unity Answers, please check our User Guide to help you navigate through our website and refer to our FAQ for more information.

Before posting, make sure to check out our Knowledge Base for commonly asked Unity questions.

Check our Moderator Guidelines if you’re a new moderator and want to work together in an effort to improve Unity Answers and support our users.

Follow this Question

Answers Answers and Comments

148 People are following this question.

avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image

Related Questions

Sorting Layers and 3D meshes 1 Answer

BuildAvatar from new rigged mesh 0 Answers

Viewing a 3d mesh in VR 0 Answers

How to increase the polygon count? 0 Answers

Putting Together Multiple Rigged Models 1 Answer


Enterprise
Social Q&A

Social
Subscribe on YouTube social-youtube Follow on LinkedIn social-linkedin Follow on Twitter social-twitter Follow on Facebook social-facebook Follow on Instagram social-instagram

Footer

  • Purchase
    • Products
    • Subscription
    • Asset Store
    • Unity Gear
    • Resellers
  • Education
    • Students
    • Educators
    • Certification
    • Learn
    • Center of Excellence
  • Download
    • Unity
    • Beta Program
  • Unity Labs
    • Labs
    • Publications
  • Resources
    • Learn platform
    • Community
    • Documentation
    • Unity QA
    • FAQ
    • Services Status
    • Connect
  • About Unity
    • About Us
    • Blog
    • Events
    • Careers
    • Contact
    • Press
    • Partners
    • Affiliates
    • Security
Copyright © 2020 Unity Technologies
  • Legal
  • Privacy Policy
  • Cookies
  • Do Not Sell My Personal Information
  • Cookies Settings
"Unity", Unity logos, and other Unity trademarks are trademarks or registered trademarks of Unity Technologies or its affiliates in the U.S. and elsewhere (more info here). Other names or brands are trademarks of their respective owners.
  • Anonymous
  • Sign in
  • Create
  • Ask a question
  • Spaces
  • Default
  • Help Room
  • META
  • Moderators
  • Explore
  • Topics
  • Questions
  • Users
  • Badges