Search Unity

  1. How can our website serve you better? Give us your feedback. Take our survey and let us know.
    Dismiss Notice

"stitch multiple body parts into one character"

Discussion in 'Scripting' started by masterprompt, Jan 6, 2009.

  1. Durins-Bane

    Durins-Bane

    Joined:
    Sep 21, 2012
    Posts:
    175
    You guys seem to know what you are talking about :D
    Could someone help me with this? Im no modeller and barely a programmer but trying to learn
    http://forum.unity3d.com/threads/whats-the-best-way-to-instantiate-armor-onto-a-character.413660/

    Stuck trying to spawn armor onto my char? any ideas? How would I get this working on my game?

    Would really like to learn how to model my own characters setup the rigs but have no idea where to begin, just got 3DS max to make my own weapons/armor etc but have no idea how to attach them with the proper rotations :( this is the only thing I am stuck with for a long long time can make pretty much anything else apart from this!
     
  2. Max_Bol

    Max_Bol

    Joined:
    May 12, 2014
    Posts:
    135
    The Stitcher script really work well and the little amount of problem I had with it weren't that much of an issue to fix. Thanks a lot!

    I would like to share one small change to its script that might seems tiny, but does a lot when considering how Unity manage its lighting.

    Code (CSharp):
    1.     private GameObject AddChild (GameObject source, Transform parent)
    2.     {
    3.         var target = new GameObject (source.name);
    4.         target.transform.parent = parent;
    5.         target.transform.localPosition = source.transform.localPosition;
    6.         target.transform.localRotation = source.transform.localRotation;
    7.         target.transform.localScale = source.transform.localScale;
    8.         target.layer = source.layer;
    9.         return target;
    10.     }
    Otherwise, the generated stitched parts will always be stuck in the Default Layer and, if you're building a game with a complex lighting system that makes uses of layers, this allow you to controls which layer the skinned mesh will be part of.

    In my case, Most of the levels have their own static lightmap and all "real-time" lights only affects up to 3 layers in the 20's layers I have set. The characters have 2 layers where the top parent have it either to "Local Player" or "Remote Player" (which is used by how I implemented the network system as my game can involve up to 4 actors (AI or players) either local or remote) and one called AnimatedElements which is pretty much all the skinned meshes or not-static meshes in the scene such as, in this case, armors and clothes. With this small addition to the Stitcher.cs script, you just have to set the layer of the prefab to the desired one (or change it dynamically if needed before stitching it) and it will be transfered to the new stitched child.
     
    hopeful and theANMATOR2b like this.
  3. malkere

    malkere

    Joined:
    Dec 6, 2013
    Posts:
    1,184
    has anyone gotten this to work with MCS characters?
    When I open the fbx in blender the character is warped in object mode, but fine in edit mode... Trying to just re-export into Unity shows the warped version in the animation preview. The stiching of course doesn't work at all after that...
    Hmmmmmmmmm

    Even trying to just stich the base LOD0 MCSMaleLite onto anything else, it just disappears ? Do the bones have to use the same naming convention? Do the rig's have to use the same avatar?
     
    Last edited: Jan 22, 2017
  4. BowesAcademy

    BowesAcademy

    Joined:
    Feb 18, 2014
    Posts:
    133
    Hey guys,

    When I first found this thread two years ago, I really wanted to find a tutorial that would help me integrate Masterprompt's awesome script into an inventory system. Thanks to GameGrind's inventory series and Masterprompt's example scene, I've managed to do it myself. I think it will benefit beginner to early-intermediate programmers struggling with this issue.



    Look forward to any feedback :)
     
    sunwangshu, KIMSEONHO, dmonin and 3 others like this.
  5. malkere

    malkere

    Joined:
    Dec 6, 2013
    Posts:
    1,184
    Looks pretty comprehensive! Thanks for putting this together. I will watch it this weekend.
     
    BowesAcademy likes this.
  6. deab

    deab

    Joined:
    Aug 11, 2013
    Posts:
    85
    I really want to maintain the ability to change a characters rig and bone weights without having to update every piece of armour, so I'm trying a slightly different approach - auto bone weights for armour based on nearest weighted vertex.

    I have the main character mesh which is fully rigged/weighted. I'm then create new armour objects in the same Blender file. These are placed in the correct position I want them to display (origins are all at zero). The objects are parented to the same rig as the character mesh.

    Import the FBX in to Blender as normal, and warnings are shown about the lack of weights on the armour objects.

    Using Unity's OnPostprocessModel() I'm locating the base character mesh, them iterating over the armour meshes. For each mesh, find the closets vertex in the weighted mesh and copy the bone weights.

    Initial is very promising!

    GIF showing initial test

    Couple of things to be aware of:
    • Base character mesh needs enough vertices to sample, so I made need to added extra vertices that aren't used on the base model as helpers for the armour.
    • Currently this is simplistic, but could be enhanced to lerp between two vertices if no local vertex found.
    • Script takes a little while to run on import (long loop for searching for nearest vertex - this could no doubt be optimised). I'm not too worried here, I'll add an option to my pipeline to disable this process when iterating or testing animations.
    • At run time you will need to copy the prefab, then remove parts you don't want (eg all helms except the one you want).
    Here's the first pass at the bone weight code:

    Code (CSharp):
    1.  
    2.     // call from OnPostprocessModel() in an editor script
    3.     private void Options()
    4.     {
    5.         SkinnedMeshRenderer baseSkin = null;
    6.         List<SkinnedMeshRenderer> options = new List<SkinnedMeshRenderer>();
    7.  
    8.         SkinnedMeshRenderer[] skins = _gameObject.GetComponentsInChildren<SkinnedMeshRenderer>();
    9.         foreach (SkinnedMeshRenderer skin in skins)
    10.         {
    11.             // name of base mesh that is fully weighted
    12.             if (skin.name == "_CHAR_base")
    13.             {
    14.                 baseSkin = skin;
    15.                 continue;
    16.             }
    17.             // optional armour meshes that need weights
    18.             if (skin.name.Contains("_OPT_"))
    19.                 options.Add(skin);
    20.         }
    21.  
    22.         if (baseSkin == null)
    23.         {
    24.             Debug.Log("NO base found.");
    25.             return;
    26.         }
    27.         Debug.Log("   found options: " + options.Count);
    28.  
    29.         // tolerance level, enable break out of loop as soon as cloest enough vertex found
    30.         const float tolerance = 0.001f;
    31.         // iterate found armour optional meshes
    32.         foreach (SkinnedMeshRenderer option in options)
    33.         {
    34.             // copy weight array so can make changed
    35.             var weights = option.sharedMesh.boneWeights;
    36.             for (int o = 0; o < option.sharedMesh.vertices.Length; o++)
    37.             {
    38.                 Vector3 optVector3 = option.sharedMesh.vertices[o];
    39.  
    40.                 // find closet
    41.                 float magnitude = 100;
    42.                 int index = 0;
    43.                 // iterate base skin vertices to find closest
    44.                 for (int v = 0; v < baseSkin.sharedMesh.vertices.Length; v++)
    45.                 {
    46.                     Vector3 basevertex = baseSkin.sharedMesh.vertices[v];
    47.                     float thisMag = (optVector3 - basevertex).sqrMagnitude;
    48.                     if (thisMag < magnitude)
    49.                     {
    50.                         magnitude = thisMag;
    51.                         index = v;
    52.                     }
    53.                     if (magnitude <= tolerance)
    54.                         break;
    55.                 }
    56.                 // copy closest vertex weight
    57.                 weights[o] = baseSkin.sharedMesh.boneWeights[index];
    58.             }
    59.             // write updated weights array back to option mesh
    60.             option.sharedMesh.boneWeights = weights;
    61.         }
    62.     }
    63.  
     
  7. malkere

    malkere

    Joined:
    Dec 6, 2013
    Posts:
    1,184
    That approach makes a lot of sense to me. Vertex location in the t-pose might as well use the bone and weight data of whatever is closest to it, no prior rigging necessary... I'm currently working with UMA as of a couple days ago and it is basically taking care of a lot of that behind the scene if anyone is looking for an alternative.
     
    hopeful likes this.
  8. BowesAcademy

    BowesAcademy

    Joined:
    Feb 18, 2014
    Posts:
    133
    So I made that tutorial video above and it's getting some traction. While I was making it, I kept running into this problem:

    ArgumentException: An element with the same key already exists in the dictionary.

    It would direct me to line 65 and 67 in the Stitcher script. I managed to dodge it and I thought I had some functional code, but I just took the code into a new project and tried to set it up with a different model and the error popped up again.

    Can someone please help me understand what that error means and how I can fix it? Here is Masterprompt's Stitcher script, my edited Equipment script, and the ItemData script which uses the Equipment script methods: https://www.mediafire.com/?68c0vb84c7rc9cp

    I'd post the whole project but I'm waiting to hear back from the author of my inventory script to see if that's ok.
     
  9. BowesAcademy

    BowesAcademy

    Joined:
    Feb 18, 2014
    Posts:
    133
    Ok, I seem to have fixed it by adding this to the Catalog method in the Stitcher script:

    Code (CSharp):
    1.     #region Catalog
    2.         private void Catalog (Transform transform)
    3.         {
    4.             if(ContainsKey(transform.name))
    5.             {
    6.                 Remove(transform.name);
    7.                 Add(transform.name, transform);
    8.             }
    9.             else
    10.                 Add(transform.name, transform);
    11.             foreach (Transform child in transform)
    12.                 Catalog (child);
    13.         }
    14.         #endregion
    This is not a permanent fix though as I don't know why the key was there in the first place. Any advice would be appreciated :)
     
  10. allcoder

    allcoder

    Joined:
    Jun 4, 2015
    Posts:
    13
    EsriDeschaine likes this.
  11. LorandVarga

    LorandVarga

    Joined:
    Apr 5, 2014
    Posts:
    2
    Hello everyone.

    I've integrated the Stitcher in my RPG project a while ago, and it worked perfectly when I equipped multiple armor parts one by one.

    However recently I started working on a save/load system, and the most recent thing I implemented was to save and load the equipment loadout of my character, and I ran into the following issue:

    If I stitch multiple parts, at once, only the first is visible, the others are not. But if I add a coroutine to stich one part per frame, it works perfectly.

    First I thought I did something wrong in my project, but I managed to replicate the issue in the https://github.com/masterprompt/ModelStitching project as well.

    Here's a better example of what happens:
    First of all I created prefabs for the helmet and chestplace assets from the github project and added them to the clothing list in the Tester class.

    #1. Using this code in the OnGUI method:
    Code (CSharp):
    1. if (GUI.Button(new Rect(0, offset, buttonWidth, buttonHeight), "All"))
    2. {
    3.     Wear(clothing[1]);   // red pants
    4.     Wear(clothing[2]);   // chestplate
    5.     Wear(clothing[3]);   // helmet
    6. }
    All of the prefabs are instantiated and correctly attached to the character, however only clothing[1] is visible


    One thing I noticed is that the Bounds on the SkinnedMeshRenderer attached to the amour components which are not visible are all 0,0,0. I tried adjusting these manually, but it had no effect



    I have been trying to figure this out since this morning, but that was about 12 hours ago..


    #2. The only solution that I accidentally found (don't exactly how I came up with this idea, maybe I was too desperate :D) was to try and split the code up in a coroutine, which attaches the clothing parts one on each frame. To my amazement, this actually worked, but I honestly don't like this solution.

    This is the code I used (everything else, such as the prefabs, are exactly the same)
    Code (CSharp):
    1. public void OnGUI()
    2. {
    3.     ...
    4.    
    5.     offset += buttonHeight;
    6.     if (GUI.Button(new Rect(0, offset, buttonWidth, buttonHeight), "All (coroutine)"))
    7.     {
    8.         StartCoroutine(WearAll());
    9.     }
    10. }
    11.  
    12. private IEnumerator WearAll()
    13. {
    14.     for(int i = 1; i <= 3; i++)
    15.     {
    16.         Wear(clothing[i]);
    17.  
    18.         yield return null;
    19.         //    yield return new WaitForEndOfFrame();    // <- this also works
    20.     }
    21. }
    The result:


    I cannot for the life of me figure out why the coroutine option #2 works, but option #1 doesn't. If someone could shed some light one this and suggest another solution, I'd be extremely grateful :D
     
  12. Corva-Nocta

    Corva-Nocta

    Joined:
    Feb 7, 2013
    Posts:
    582
    First off, thank you so much Masterprompt! Your script is the answer that I have been looking for, and I'm honestly surprised that Unity hasn't made this easier or at least put out something to make it function. Anyway, I decided I should post the most recent edition of the code for people who are just now coming into it. There's a lot of different code out there. I did not change any code myself, I just want to compile it here for others.

    I took this code directly from Hempcrete (GameDevStudent) but this is the base code for the Stitching. So if you are looking to have this process done, copy all of this code into a script by itself (no additional code necessary for this part)
    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections.Generic;
    4.  
    5. public class Stitcher
    6. {
    7.     /// <summary>
    8.     /// Stitch clothing onto an avatar.  Both clothing and avatar must be instantiated however clothing may be destroyed after.
    9.     /// </summary>
    10.     /// <param name="sourceClothing"></param>
    11.     /// <param name="targetAvatar"></param>
    12.     /// <returns>Newly created clothing on avatar</returns>
    13.     public GameObject Stitch(GameObject sourceClothing, GameObject targetAvatar)
    14.     {
    15.         var boneCatalog = new TransformCatalog(targetAvatar.transform);
    16.         var skinnedMeshRenderers = sourceClothing.GetComponentsInChildren<SkinnedMeshRenderer>();
    17.         var targetClothing = AddChild(sourceClothing, targetAvatar.transform);
    18.  
    19.  
    20.         foreach (var sourceRenderer in skinnedMeshRenderers)
    21.         {
    22.             var targetRenderer = AddSkinnedMeshRenderer(sourceRenderer, targetClothing);
    23.             targetRenderer.bones = TranslateTransforms(sourceRenderer.bones, boneCatalog);
    24.         }
    25.         return targetClothing;
    26.     }
    27.  
    28.     private GameObject AddChild(GameObject source, Transform parent)
    29.     {
    30.         source.transform.parent = parent;
    31.  
    32.         foreach (Transform child in source.transform)
    33.         {
    34.             Object.Destroy(child.gameObject);
    35.         }
    36.  
    37.         return source;
    38.     }
    39.  
    40.     private SkinnedMeshRenderer AddSkinnedMeshRenderer(SkinnedMeshRenderer source, GameObject parent)
    41.     {
    42.         GameObject meshObject = new GameObject(source.name);
    43.         meshObject.transform.parent = parent.transform;
    44.  
    45.         var target = meshObject.AddComponent<SkinnedMeshRenderer>();
    46.         target.sharedMesh = source.sharedMesh;
    47.         target.materials = source.materials;
    48.         return target;
    49.     }
    50.  
    51.     private Transform[] TranslateTransforms(Transform[] sources, TransformCatalog transformCatalog)
    52.     {
    53.         var targets = new Transform[sources.Length];
    54.         for (var index = 0; index < sources.Length; index++)
    55.             targets[index] = DictionaryExtensions.Find(transformCatalog, sources[index].name);
    56.         return targets;
    57.     }
    58.  
    59.     #region TransformCatalog
    60.     private class TransformCatalog : Dictionary<string, Transform>
    61.     {
    62.         #region Constructors
    63.         public TransformCatalog(Transform transform)
    64.         {
    65.             Catalog(transform);
    66.         }
    67.         #endregion
    68.  
    69.         #region Catalog
    70.         private void Catalog(Transform transform)
    71.         {
    72.             if (ContainsKey(transform.name))
    73.             {
    74.                 Remove(transform.name);
    75.                 Add(transform.name, transform);
    76.             }
    77.             else
    78.                 Add(transform.name, transform);
    79.             foreach (Transform child in transform)
    80.                 Catalog(child);
    81.         }
    82.         #endregion
    83.     }
    84.     #endregion
    85.  
    86.  
    87.     #region DictionaryExtensions
    88.     private class DictionaryExtensions
    89.     {
    90.         public static TValue Find<TKey, TValue>(Dictionary<TKey, TValue> source, TKey key)
    91.         {
    92.             TValue value;
    93.             source.TryGetValue(key, out value);
    94.             return value;
    95.         }
    96.     }
    97.     #endregion
    98.  
    99. }
    Alright that script will be doing all the work for us. The best part? This script doesn't even have to be attached to anything! Just copy and leave it in your scripts folder.

    Now for the important part, this is the code you will add to your script that is controlling what equipment objects are being spawned.
    Code (csharp):
    1.  
    2. public GameObject avatar; //Drag human model to this slot, the whole model + Armature
    3. private GameObject wornClothing;
    4.  
    5. private Stitcher stitcher;
    6.  
    7. public void Awake()
    8. {
    9.     stitcher = new Stitcher();
    10. }
    11.  
    12. public void SomeFunctionThatAddsEquipment()
    13. {
    14.     Wear(//put the gameobject you want spawned here);
    15. }
    16.  
    17. public void SomeFunctionThatRemovesEquipment()
    18. {
    19.     RemoveWorn();
    20. }
    21.  
    22. private void RemoveWorn()
    23.     {
    24.         if (wornClothing == null)
    25.             return;
    26.         Destroy(wornClothing);
    27.     }
    28.  
    29.     private void Wear(GameObject clothing)
    30.     {
    31.         if (clothing == null)
    32.             return;
    33.         clothing = Instantiate(clothing);
    34.         wornClothing = stitcher.Stitch(clothing, avatar);
    35.     }
    And that's it! That is all you need! remarkably simple really, but works quite well. From here you can multiply the parts of the script you need to do what you want. Anyway I hope this helps anyone who is just coming into this!
     
    DrunkenMastah likes this.
  13. Corva-Nocta

    Corva-Nocta

    Joined:
    Feb 7, 2013
    Posts:
    582

    Has anyone encountered this problem with stitching? It is only happening with the head and chest areas. I have no idea at all why this is happening.



    ---SOLVED---

    If anyone encounters this problem, here is what is most likely happening (or at least what was happening for me)

    After setting everything up you need to make sure you are dragging your player model to the Avatar variable, not your entire player. So if you have an empty gameobject where you hold everything for your player as children, things like the models, UI, and other elements, you need to make sure you grab the child model of the empty game object, not the empty game object itself. Hope that helps anyone that runs into the same issue!
     
    Last edited: Apr 24, 2018
  14. Chris110

    Chris110

    Joined:
    Feb 25, 2014
    Posts:
    3
    Does anyone have an updated version of this script?

    My issue is that it doesn't seem to be copying the bones to the skinned mesh renderers? - i can't for the life of me solve why.

    Edit: Omg, I've solved it.. This is the best thread ever, I've been running into this issue for days now and it's finally sorted. Thanks everyone!
     
    Last edited: May 31, 2018
    malkere likes this.
  15. NeatWolf

    NeatWolf

    Joined:
    Sep 27, 2013
    Posts:
    909
    Hi there!

    Does the Stitcher work even when "ÖptimiseBones" is enabled?

    I can't access the Bones array in that way, but I indeed need to only expose the bones I need in the hierarchy.
    Is there any other way to access the bones/transforms from an optimised SkinnedMeshRenderer?
     
  16. masterprompt

    masterprompt

    Joined:
    Jan 6, 2009
    Posts:
    115
    whoa, this thread is still alive :)
    I know I am the only one on the repository. I'd gladly give anyone else access to it to approve pull requests in my absence!
     
  17. stonstad

    stonstad

    Joined:
    Jan 19, 2018
    Posts:
    249
    Using the GitHub repo w/ Unity 2019.1* I am not able to get any of the models to load and Blender is installed. Can someone confirm the GitHub sample assets and scripts work as-is?

    Update: I couldn't get the GH repository files to work due to missing or unimported assets. However, I was able to get the unity package with FBX files on the first page of this thread to work.
     
    Last edited: Jul 31, 2019
  18. HyruleDefender

    HyruleDefender

    Joined:
    Apr 4, 2017
    Posts:
    7
    For those who return here as I have seeking another opportunity at simple copy and paste code, I have good news.

    @jorger12 provided a script that did work for applying 1 item, but it did not apply more than 1 and had several errors.

    I have updated and commented out the errors. My Spanish is not fluent enough to translate, but here's the script that worked for me on unity's 2019.1.4

    *EDIT - this script works by attaching it to your character model, and using the in-editor drop-down list to add/remove clothes you wish to attach.

    Perhaps it works other ways, but for me, I skinned my clothes to the bones separately (in the 3d program) and dragged them into Unity when I was ready to use them (so didn't have to have them all exported at the same time)

    Then I marked them as humanoid (even though it was just a model of a t-shirt).

    and the script did the rest.

    Code (CSharp):
    1. // Basado en proyecto de 'masterprompt' <Joined:Jan 6, 2009>
    2. // -http://forum.unity3d.com/threads/stitch-multiple-body-parts-into-one-character.16485/
    3. // Modificado para uso de multiples items animados en personaje
    4. // con eliminacion de memoria de recursos que no son necesarios
    5.  
    6.  
    7. using UnityEngine;
    8. using System.Collections;
    9. using System.Collections.Generic;
    10.  
    11.  
    12. public class AddCloth : MonoBehaviour {
    13.  
    14.     public List <GameObject> ClothItems=null;            // lista de vestimentas
    15.  
    16.  
    17.     private GameObject[] cpClothItem=null;                // copia de vestimanta
    18.     private SkinnedMeshRenderer [] skClothItem=null;    // copia skin vestimentas
    19.     private Transform[] skBone=null;                    // copia auxiliar para añadir a lista
    20.  
    21.  
    22.     void Start () {
    23.         if (ClothItems!=null) {        // Lista posee items, añadirlos al personaje
    24.             AddItemCloth (ClothItems.Count);
    25.             }
    26.     }
    27.  
    28.     private void AddItemCloth (int Contador) {
    29.  
    30.         cpClothItem=new GameObject [Contador];    // preparar lista objetos
    31.         skClothItem=new SkinnedMeshRenderer [Contador];    // prepara lista skinned
    32.  
    33.         // añadir los items al personaje
    34.         // prepara copia lista esqueletos
    35.         for (int ci=0; ci<Contador; ci++) {
    36.             // cargar esqueleto prenda
    37.             SkinnedMeshRenderer[] skCloth=ClothItems [ci].GetComponentsInChildren <SkinnedMeshRenderer>();
    38.             foreach (SkinnedMeshRenderer SMR in skCloth) {
    39.                 // duplicar objeto
    40.                 cpClothItem [ci]=new GameObject (SMR.gameObject.name);
    41.                 // emparentar con personaje
    42.                 cpClothItem [ci].transform.parent=transform;
    43.                 skClothItem [ci]=cpClothItem [ci].AddComponent (typeof (SkinnedMeshRenderer)) as SkinnedMeshRenderer;
    44.                 // hacer copia del esqueleto
    45.                 skBone=new Transform [SMR.bones.Length];
    46.                 for (int ii=0; ii<SMR.bones.Length; ii++)
    47.                     skBone [ii]=FindChildByName (SMR.bones [ii].name,transform);
    48.                 // Nuevo objeto reemplaza al modelo original, pero con esqueleto
    49.                 // reasignado + animacion + materiales. Este objeto no debe eliminarse.
    50.                 skClothItem [ci].bones=skBone;
    51.                 skClothItem [ci].sharedMesh=SMR.sharedMesh;
    52.                 skClothItem [ci].sharedMaterial=SMR.sharedMaterial;
    53.                 // Liberar memoria de objetos en deshuso
    54.                 //Destroy(SMR);     ************************ this is not permitted
    55.                 //for (int ii=0; ii<skCloth.Length; ii++)
    56.                     //Object.Destroy (skCloth [ii]);
    57.             }
    58.             //for (int ii=0; ii<Contador; ii++)
    59.                 //Destroy (ClothItems [ii]);  
    60.             skBone=null;
    61.         }
    62.     }
    63.  
    64.  
    65.     private Transform FindChildByName (string ThisName, Transform ThisGObj)    {  
    66.         Transform ReturnObj;
    67.      
    68.         if (ThisGObj.name==ThisName)  
    69.             return ThisGObj.transform;
    70.         foreach (Transform child in ThisGObj)    {  
    71.             ReturnObj=FindChildByName (ThisName,child);
    72.             if (ReturnObj!=null)  return ReturnObj;  
    73.         }
    74.         return null;  
    75.     }
    76.  
    77. }
    78.  
     
    drcfrx and ikazrima like this.
  19. Sangemdoko

    Sangemdoko

    Joined:
    Dec 15, 2013
    Posts:
    139
    Hi,

    This morning I've been trying to attach equipment to a character, doing some research I came across this forum.
    I tried to simplify the solution above hopefully, it helps some of you.

    The line that really matters is the one that copies the bone hierarchy from the main skinned mesh to the equipment skinned mesh.

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections.Generic;
    3.  
    4. /// <summary>
    5. /// Attach this script to the character
    6. /// </summary>
    7. public class SkinnedMeshStitcher : MonoBehaviour {
    8.    
    9.     [Tooltip("Main skinned mesh such as head or body, etc...")]
    10.     public SkinnedMeshRenderer m_MainSkinnedMeshRenderer;
    11.     [Tooltip("Skinned Mesh Equipment Prefabs, only the skinned mesh object does not require the rig")]
    12.     public List <GameObject> m_SkinnedMeshEquipmentPrefabs;
    13.  
    14.     private void Start () {
    15.          if (m_SkinnedMeshEquipmentPrefabs == null) { return; }
    16.  
    17.          for (int i=0; i<m_SkinnedMeshEquipmentPrefabs.Count; i++) {
    18.              AddEquipmentToRig(m_SkinnedMeshEquipmentPrefabs[i]);
    19.          }
    20.     }
    21.  
    22.     /// <summary>
    23.     /// Instantiate the equipment prefab and skin it on the same rig as the main skin mesh renderer
    24.     /// </summary>
    25.     /// <param name="equipmentPrefab"></param>
    26.     public void AddEquipmentToRig(GameObject equipmentPrefab)
    27.     {
    28.          var equipmentInstance = Instantiate(equipmentPrefab, transform);
    29.          SkinnedMeshRenderer[] equipmentSkinnedMeshRenderers = equipmentInstance.GetComponentsInChildren<SkinnedMeshRenderer>();
    30.        
    31.          for (var i = 0; i < equipmentSkinnedMeshRenderers.Length; i++) {
    32.              SkinnedMeshRenderer equipmentSkinnedMeshRenderer = equipmentSkinnedMeshRenderers[i];
    33.  
    34.              equipmentSkinnedMeshRenderer.rootBone = m_MainSkinnedMeshRenderer.rootBone;
    35.              equipmentSkinnedMeshRenderer.bones = m_MainSkinnedMeshRenderer.bones;
    36.          }
    37.     }
    38. }
    Note that this script does not fuse skinnedMesh or Materials into one. If you have a few animated characters using this you shouldn't have any performance problems, but if you have hundreds I would recommend using UMA.

    Note that the AddEquipmentToRig function is public so that you can add equipment to your character at runtime. You may need to add a dictionary using int,enum or string as key and gameobject as value to keep track of the equipment so that they can be removed.
     
  20. BayernMaik

    BayernMaik

    Joined:
    Oct 15, 2019
    Posts:
    18
    Hi, quite new to Unity,

    My previous approach was to parent the equipment bones to the characters bones to make the equipment move along the characters rig. This works quite ok except few clipping issues but multiple bones parented to the characters bones made the characters rig structure quite confusing and i guess too many equipmentrigs may have impact on performance...
    So i was searching for different solutions and found this thread. I think i understand what it does since it seems to be the same "concept" however it is a much more structured and probably performance friendlier solution.

    Currently i have this issue with deformed meshes after assigning the characters skinned mesh renderers bones array to the equipments skinned mesh renderers bone array. Appearantly this is caused by mixed up bone arrays in equipments skinned mesh renderers. I tried to sort the bones array according to the characters rig by name and the mesh did change its shape, but it still doesnt look anything near it should look like. Are there any other components except the bones array i have to sort to fix the mesh? Or am i completely wrong ?

    Any help appreciated
     
  21. ikazrima

    ikazrima

    Joined:
    Feb 11, 2014
    Posts:
    312
    The standard way is just like what you did, have an attachment bone where necessary. Performance wise should be negligible. But of course you can plan and clean up a bit on how you structure your rig.

    If you're sorting the array by name you need to make sure that the array count matches exactly between the two armatures. You can't have a character armature with 100 bones and an equipment armature with 20 bones.

    This thread is more for combining skinned meshes into one mesh, and stitching the seams between those meshes together so that the normals / lighting appears correctly. (something akin to Blender's combine mesh, weld vertices and recalculating normals).
     
    BayernMaik likes this.
  22. Nivbot

    Nivbot

    Joined:
    Mar 21, 2015
    Posts:
    65
    The best part is you told us the solution. I love when people do that
     
  23. Ragee

    Ragee

    Joined:
    Nov 24, 2012
    Posts:
    15
    Last edited: Jun 28, 2020
  24. Zakrich

    Zakrich

    Joined:
    Sep 18, 2018
    Posts:
    3
    I wonder if it's possible for some parts to have their extra bones.

    Say, I have a base body and a tailcoat. While the tailcoat has an identical basic skeleton structure with the body, it also has some unique, additional bones (parented to the spine bone) to animate the tail with physics.

    With the method in this post, only the base bones work properly, and the tail can't be animated. Is there any workaround?
     
  25. Reanimate_L

    Reanimate_L

    Joined:
    Oct 10, 2009
    Posts:
    2,511
    for that kind of configuration usually additional bone will be included in the base rig
     
  26. Zakrich

    Zakrich

    Joined:
    Sep 18, 2018
    Posts:
    3
    Yeah that could work in common cases. What I'm thinking about is a character customization system with very different interchangeable items. For example, the body could either wear a tailcoat (with physically animated tail) or a dress (with physically animated skirt). To have all those extra bones in the base rig would be inefficient I suppose?

    Instead of replacing the clothing's bones, I wonder if I should make them copy the transform of corresponding bones from the base rig, and leave the extra bones as they are. Do you think this is a viable solution?
     
  27. Reanimate_L

    Reanimate_L

    Joined:
    Oct 10, 2009
    Posts:
    2,511
    Honestly i'm not quite sure if the bone data would remapped properly, unless the new mesh are not even rigged to the base rig. But you are free to try, i'm interested to know how it goes though
     
  28. Akant0r

    Akant0r

    Joined:
    Aug 22, 2013
    Posts:
    2
  29. FaffyWaffles

    FaffyWaffles

    Joined:
    Apr 10, 2020
    Posts:
    17
    Geez, you could make a whole Unity Asset with this. Pity I have to learn JavaScript now lol
     
  30. hopeful

    hopeful

    Joined:
    Nov 20, 2013
    Posts:
    5,371
    Take a look at UMA, which is free and much like this.
     
unityunity