Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

How is character clothing done on mid tier+ games?

Discussion in 'General Discussion' started by frosted, Sep 19, 2019.

  1. frosted

    frosted

    Joined:
    Jan 17, 2014
    Posts:
    4,044
    So I've begun working with a freelance artist on a set of RPG style characters with various clothing.

    His preferred approach is to model character body, then separately model character clothing, layered on top of the body mesh. The clothing is, of course, modeled against the same skeletal structure, so animation is easy enough.

    The immediately obvious downside is overdraw. The character mesh's body still exists under the clothing, which adds to overhead.

    He's got some low end AAA experience, and from my work with him so far, he seems to be highly skilled and very capable. The work itself is beautiful.

    My question, is he, being a freelancer, just shifting work to the programming side or is this a standard practice? Do games just 'eat' the overdraw or do they remove the polys under the clothing? Are there easy ways to delete those polys (assuming they're being mapped to the same skeleton, could I delete body verts based on shared weight painting or something)?
     
  2. MadeFromPolygons

    MadeFromPolygons

    Joined:
    Oct 5, 2013
    Posts:
    3,875
    That is a common way to do clothing when you want to do removable/changable clothing. You can ofcourse make some optimisations by splitting the torso up into seperate parts so that you can disable bits that are not imporant (such as the part under a t shirt when the t shirt is active) altho this will just add a lot more work to the rigging side of things.

    Ultimately clothing is difficult and you will likely get a degree of overdraw from it, unless you do some fancy stuff to get around that. The alternative is doing them as textures (baked so that you get a bit of depth) but that will look worse and also add a different kind of overhead instead.

    If the character model is optimised enough it wont cause too many issues having some overdraw, and ofcourse you can never completely eliminate overdraw in most games anyway.

    If you dont need customisable / removable clothing, then he can model the clothes seperately on top and then combine the meshes and remove the polygons underneath the clothes, thatll work fine too.

    But there isnt a single way its done. Some games take the perf drop and optimise around it. Some games do not draw the torso or any bits covered by clothes when clothing is active.

    If you have to eat the perf, try setting the underneath polygons to the simplest shader possible (unlit or vert color) while the clothing is obscuring it (and back to the original shader when removed), but it wont do much to help that perf drop.

    In AAA studios I had the opportunity to look around and sit in on during my sony scholarship, quite a few were using marvelous designer to create clothes with full physics sim, and then bring that back into a DCC app to optimise and adjust, and into engine finally for more adjustments and round and round that cycle.

    Marvelous designer will be overkill for you though, dont bother with it

    EDIT: these 2 articles I always find are good at summarising a reasonable workflow for mid tier graphics clothing in games :

    https://80.lv/articles/game-characters-modeling-clothes-materials/

    https://80.lv/articles/making-clothes-for-video-games/

    Unfortunately most decent looking ones like that will use marvelous designer at some point in the pipeline just like those 2 do



    EDIT 2: Interestingly kotaku have a article on how a lot of AAA games from pre 2016 would actually swap out the mesh or do other tricks to get around problem of taking and putting on clothes, but also gives a bit of an insight into how they generally approach clothing. So definately give that a read!

    https://kotaku.com/why-video-game-characters-almost-never-remove-their-clo-1828182196
     
    Last edited: Sep 19, 2019
    impheris, PutridEx, Ony and 1 other person like this.
  3. frosted

    frosted

    Joined:
    Jan 17, 2014
    Posts:
    4,044
    The clothing is being designed in marvelous. The guy is simply excellent with the tool. He has experience as a clothing specialist in low end AAA, so marvelous was his tool of choice.

    The base body is 4k vert and 7k tri (including head), so I guess at worst I won't be eating too much cost here. Most of the muscle work is baked into the normals.

    Still, I'd like to optimize the work - so in terms of deleting body mesh programatically - what are my options?

    The easiest way I could think of is to just custom mark verts for deletion and save a map of occluded body verts for each piece of clothing, but I don't want to manually manage this.

    There are two core problems:
    1 - performance. I'd like to be able to run on low end PC and starting with really good work as a base would be nice.
    2 - clipping. He's willing to do the work correcting clipping, but it'd be nice to not need to bug him and just guarantee zero clipping.

    If I'm only dealing with a few dozen pieces of clothing - manual management can work - but its less than ideal. Automatic detection and processing would really be ideal. So, how can this be done?
     
    MadeFromPolygons likes this.
  4. MadeFromPolygons

    MadeFromPolygons

    Joined:
    Oct 5, 2013
    Posts:
    3,875
    I honestly have no idea how you would do automatic detection and processing, hopefully someone will chime in. But I think thats going to be a seriously big engineering task so I hope you have a number of developers at hand and lots of the project time to spare making that.

    Otherwise I would say the age old line: Is it performing badly on target hardware? If not, no need to optimise it further.

    If you havent got a lookdev sample build onto a target platform yet, then no point optimising ahead of time and making more work for yourself. Get it in engine, in a sample level of reasonably similar density and fidelity to what your previsualisation is and then test. You can use the unity snaps hd packs for quickly dumping a character into a proper level to test this.

    FYI If he is already doing marvelous designer then I really dont think you need to worry, it has some serious optimisation options both in the program and on export. I think this is a classic case of until its a problem maybe you shouldnt worry too much about it? I doubt it will actually be a problem in the end unless your really trying to go high end, especially if your only worrying about a dozen or so pieces of clothing.
     
    frosted likes this.
  5. BIGTIMEMASTER

    BIGTIMEMASTER

    Joined:
    Jun 1, 2017
    Posts:
    5,181
    You shouldn't need any sophisticated solution to disable rendering of the underlying body, it is standard practice for character modelers to separate this as a gameobject so you can easily disable it when not necessary.

    That's not just for performance, it's for the animations as well. Without an excessive amount of bones and sophisticated rigging, it is impossible to animate clothes over a mesh without bad clipping. Obviously, if there is mesh underneath that cannot be seen the solution is simply to disable it. And the simplest way is to make the body a separate mesh. That is a few clicks in your DCC. No programming necessary. So long as the border vertices share the same position, animations will not show visible seams.

    @GameDevCouple_I , unless I misunderstand or there is something I do not know, the case with Marvelous Designer is the opposite of what you are saying. Theh latest version of MD has some improvements for automatic retopo, but it still puts out non-game ready topology. It's position in the character art pipeline is to produce high quality folds that can be built upon or baked from. But it does not produce game ready models.

    this video shows what process for dealing with MD models is like (also a fantastic workflow for maya users):
    https://lesterbanks.com/2016/11/marvelous-designer-uvs-maya/


    If you want good overview of character rigging for games, check out polycount wiki and also search user mark_dygert post history.
     
  6. MadeFromPolygons

    MadeFromPolygons

    Joined:
    Oct 5, 2013
    Posts:
    3,875
    Nah I sort of didnt explain it properly. You 100% cant put stuff from MD straight into a game as yeah that would be terrible. But on export to DCC programs it has some reasonable optimisation so that what you get inside the DCC is no different to optimising say a CAD model or a sculpted model.

    You will definately have to retopo but I assume the person in question will be doing this as if they are using marvelous designer for games, they 100% must be exporting to a DCC program as next step of pipeline and I cant imagine anyone would do otherwise. Sorry I should have worded post better but I am at work so trying to fast type! :)

    EDIT: haha, that video is actually the one our clothing artist used when first getting to grips with a MD and maya pipeline ! It really is a decent video (so is a lot of lesterbanks stuff actually!)
     
    BIGTIMEMASTER likes this.
  7. frosted

    frosted

    Joined:
    Jan 17, 2014
    Posts:
    4,044
    Is it really that hard to do? I don't need perfection, I need something that doesn't have false positive on occlusion test and identifies a set of vertices to clip.
    Unity_2019-09-19_09-53-18.png

    This is the result of about a half hour of testing out a few simple methods for vert occlusion test. Maybe I'm nuts, but I don't think getting from here to game ready acceptable levels is that far (backfaces are being drawn through here, as far as i can see there are no false positives here).

    This isn't complete, but unless there are serious problems I haven't found I wouldn't expect this to take more than 2-3 days to flesh out.
     
  8. MadeFromPolygons

    MadeFromPolygons

    Joined:
    Oct 5, 2013
    Posts:
    3,875
    Nono I dont think actually doing it is the hard part, but getting that optimised and working alongside all your game logic and full game art (full levels not empty like you shown) is going to add uncessary overhead. Unless your talking about doing this in editor and not realtime? In which case go for it! Thats definately doable

    I assumed you meant on the fly realtime work out based on clothes to stop areas rendering underneath dynamically, which yeah would definately be a big piece of engineering. But if you meant an edit-time tool for within unity, I 100% endose that idea and think it would be a pretty decent thing to make as you can reuse it for future projects with ease. God maybe Ill have a shot at writing one now come to think of it :D
     
  9. frosted

    frosted

    Joined:
    Jan 17, 2014
    Posts:
    4,044
    I was thinking that I could like build ' vertex occlusion map' per clothing in editor, then apply them at runtime. Just write a script to process all the overlaying meshes in batch.

    Applying wouldn't be too hard I think. Whenever I spawn a character and get his clothes, modify the underlying mesh to clip the occluded vertices.
     
  10. AndersMalmgren

    AndersMalmgren

    Joined:
    Aug 31, 2014
    Posts:
    5,358
    Need to remember this thread when I start working on my DayZ VR game
     
    MadeFromPolygons likes this.
  11. MadeFromPolygons

    MadeFromPolygons

    Joined:
    Oct 5, 2013
    Posts:
    3,875
    I think you wont be able to simply apply it once as while the mesh deforms bits will show at the edges and potentially need a recalc, but if you say you dont need perfection then go for it!

    I personally think depending on pose it will need to be recalculated which is the main perf issue I was imagining, but I am also probably overthinking as I tend to think of everything as scalable for the biggest baddest highest budget game which often is well outside scope of what is needed!
     
    frosted likes this.
  12. AndersMalmgren

    AndersMalmgren

    Joined:
    Aug 31, 2014
    Posts:
    5,358
    Even a AAA (Is Bughemia consired AAA?) have problems with these kind of things

     
    impheris, Ryiah and frosted like this.
  13. frosted

    frosted

    Joined:
    Jan 17, 2014
    Posts:
    4,044
    Yeah - I think I'm calling this the 'false positive' problem. I'm thinking that I need something on the 'very conservative' end so it doesn't clip stuff thats visible. That pants example is also nice because the clothing edges are skin tight, flush against the mesh. Something with gaps (like a semi open sleeve) will be harder.
     
    MadeFromPolygons likes this.
  14. MadeFromPolygons

    MadeFromPolygons

    Joined:
    Oct 5, 2013
    Posts:
    3,875
    Wow that is some awful clipping issues!
     
    AndersMalmgren likes this.
  15. BIGTIMEMASTER

    BIGTIMEMASTER

    Joined:
    Jun 1, 2017
    Posts:
    5,181
    That's going to be another issue of doing this programmatically versus standard practice. You are looking for occluded vertices, what happens when vertex is no longer occluded because animation drives it to surface or beyond? No matter what, he gonna have to spend time painting weights for an impossible task. Only way to make is work is if its skin-tight body conforming clothes, or you simply disable the body mesh.
     
    MadeFromPolygons likes this.
  16. frosted

    frosted

    Joined:
    Jan 17, 2014
    Posts:
    4,044
    But who knows what kind of constraints they're working with on this. I can build the vertex clip maps in editor.

    Again, I don't need 100% perfection, I just need - better than nothing without spending tons of time.
     
  17. MadeFromPolygons

    MadeFromPolygons

    Joined:
    Oct 5, 2013
    Posts:
    3,875
    Yeah I guess if you do that then even if it bleeds a bit it wont matter as you are not clipping the bits right at the edges so there is some wiggle room.

    Honestly this is a pretty common problem even for AAAs and I am very interested to follow your progress on tackling the problem! You never know, you may come up with a solution that really is king, and then you can asset store the tech too as a bonus :)

    That said I am pretty sure you wont get much of a perf impact from the clothes so you could always just leave them as water-tight clothes that sit on top of the mesh and deform with it :)
     
  18. AndersMalmgren

    AndersMalmgren

    Joined:
    Aug 31, 2014
    Posts:
    5,358
    I could understand that the hat clip into the mask, Dayz have endless combos of clothing. But the mask that clips the face is a bit wierd.
     
    MadeFromPolygons likes this.
  19. BIGTIMEMASTER

    BIGTIMEMASTER

    Joined:
    Jun 1, 2017
    Posts:
    5,181
    it's probably user made content. Bohemia games are sandboxes for modders, basically. Hundreds of thousands of users. Any number of their fans could have created that, and conforming a face mask to hundreds of in game characters isn't easy task for amateur modder.

    low hanging fruit picking on that
     
    frosted and MadeFromPolygons like this.
  20. MadeFromPolygons

    MadeFromPolygons

    Joined:
    Oct 5, 2013
    Posts:
    3,875
    Wow the mask clipping was so bad I didnt even notice the hat clip!

    But yeah they have such an enormous amount of wearable items its understandible!

    EDIT: even more so if it is user created as @BIGTIMEMASTER implies!
     
  21. frosted

    frosted

    Joined:
    Jan 17, 2014
    Posts:
    4,044
    Nothing I'm doing here is rocket science, at all. If anything I think my goals just might be different from what most imagine.

    Just make a method that trims out some verts. If I spend less than 3 days on it and it clips a few hundred verts with no false positive, then its a win.

    I think most of the time people would approach this looking for much more ambitious goals. I just want to trim some waste and not have to ask a freelancer endless clipping revisions (would rather save revisions for other stuff).

    Another problem is that we're doing body/cloth morphs and him manually testing all the intermediate morph targets could be too much to ask.
     
  22. AndersMalmgren

    AndersMalmgren

    Joined:
    Aug 31, 2014
    Posts:
    5,358
    But this is DayZ standalone, dont think its user created.
     
  23. iamthwee

    iamthwee

    Joined:
    Nov 27, 2015
    Posts:
    2,149
    Good heavens that's horrible! Words cannot even explain . . .

    Why on earth is that guy wearing a cap WITH a balaclava???!
     
  24. AndersMalmgren

    AndersMalmgren

    Joined:
    Aug 31, 2014
    Posts:
    5,358
    Its completely customizeble :)
     
  25. frosted

    frosted

    Joined:
    Jan 17, 2014
    Posts:
    4,044
    Unity_2019-09-19_12-58-27.png Unity_2019-09-19_12-58-42.png Unity_2019-09-19_12-38-06.png Unity_2019-09-19_12-36-51.png

    That's not too bad. I think I could be a little more conservative on the edges, but it looks good in animation so far.

    This is super crude. Just add a mesh collider on the clothes and do a bunch of raycasts to see if the vertex is occluded. But the results seem pretty good.
     
    angrypenguin and Ony like this.
  26. frosted

    frosted

    Joined:
    Jan 17, 2014
    Posts:
    4,044
    Here's the code for anyone interested. This can absolutely be improved and is kinda a mess, not production ready.

    If you want to make it more or less conservative, you basically walk occluded vertexes backward along triangle edges. So like, if an occluded vertex is 1 edge away from a non occluded vertex - that definitely shouldn't be culled. If it's 2-3+ hops then its probably safe to cull (depending on your geometry and camera).

    This code just checks adjacent edges, not multiple hops, but it should give ppl a working basic idea.

    Again, I wouldn't recommend anyone use that code - but from my tests it looks like its going the right way at least.

    Code (csharp):
    1.  
    2. using System;
    3. using System.Collections.Generic;
    4. using UnityEngine;
    5. using UnityEngine.Serialization;
    6.  
    7. namespace FreelancerExample
    8. {
    9.   public class ClipUnderClothes : MonoBehaviour
    10.   {
    11.  
    12.     public Mesh OriginalBody;
    13.     public SkinnedMeshRenderer BodyRenderer;
    14.     public SkinnedMeshRenderer[] Clothing;
    15.     private Mesh m_BodyMesh;
    16.  
    17.     private Mesh m_CulledMesh;
    18.  
    19.     [NonSerialized]
    20.     private List<int> m_Hits = new List<int>();
    21.  
    22.     [NonSerialized]
    23.     private List<int> m_Occluded = new List<int>();
    24.  
    25.     [NonSerialized]
    26.     private List<int> m_OccludedTris = new List<int>();
    27.  
    28.     [EditorButton]
    29.     private void ResetBodyMesh() { BodyRenderer.sharedMesh = m_BodyMesh = Instantiate( OriginalBody ); }
    30.  
    31.     private Dictionary<int, int> BuildIndexMap() {
    32.       var map = new Dictionary<int, int>();
    33.       var occluded = new HashSet<int>( m_Occluded );
    34.       for ( int i = 0, oi = 0; i < m_BodyMesh.vertices.Length; i++ )
    35.       {
    36.         if ( occluded.Contains( i ) ) continue;
    37.         map[i] = oi++;
    38.       }
    39.  
    40.       return map;
    41.     }
    42.     [EditorButton]
    43.     private void ApplyVertexMasking() {
    44.       if ( m_BodyMesh == null )
    45.       {
    46.         Log.Error( "Body mesh not found" );
    47.         return;
    48.       }
    49.  
    50.    
    51.  
    52.       var m = m_BodyMesh;
    53.    
    54.       // Triangles
    55.       var indexMap = BuildIndexMap();
    56.    
    57.       var originalTris = m.triangles;
    58.       var finalTris = new int[originalTris.Length - m_OccludedTris.Count * 3];
    59.  
    60.       for ( int i = 0, triIndex = 0, outputIndex = 0;
    61.             i < originalTris.Length;
    62.             i += 3, triIndex++ )
    63.       {
    64.         if ( m_OccludedTris.Contains( triIndex ) ) continue;
    65.         // these need to be remapped to the current vertices.
    66.         int i0 = originalTris[i + 0];
    67.         int i1 = originalTris[i + 1];
    68.         int i2 = originalTris[i + 2];
    69.  
    70.      
    71.         finalTris[outputIndex++] = indexMap[i0];
    72.         finalTris[outputIndex++] = indexMap[i1];
    73.         finalTris[outputIndex++] = indexMap[i2];
    74.       }
    75.    
    76.       m_CulledMesh = new Mesh();
    77.       m_CulledMesh.vertices = GetFiltered( m_BodyMesh.vertices );
    78.       m_CulledMesh.triangles = finalTris;
    79.       m_CulledMesh.boneWeights = GetFiltered( m.boneWeights );
    80.       m_CulledMesh.uv = GetFiltered( m.uv );
    81.       m_CulledMesh.tangents = GetFiltered( m.tangents );
    82.       m_CulledMesh.normals = GetFiltered(m.normals);
    83.       m_CulledMesh.bindposes = m.bindposes;
    84.       m_CulledMesh.RecalculateTangents();
    85.       BodyRenderer.sharedMesh = m_CulledMesh;
    86.     }
    87.  
    88.     private T[] GetFiltered<T>( T[] original ) {
    89.       T[] filtered = new T[ original.Length - m_Occluded.Count];
    90.       HashSet<int> occluded = new HashSet<int>(m_Occluded);
    91.       for ( int i = 0, fIndex = 0; i < original.Length; i++ )
    92.       {
    93.         if( occluded.Contains( i ) ) continue;
    94.         filtered[fIndex++] = original[i];
    95.       }
    96.  
    97.       return filtered;
    98.     }
    99.  
    100.     private MeshCollider[] GetClothingColliders() {
    101.       var colliders = new MeshCollider[ Clothing.Length ];
    102.       for ( var i = 0; i < Clothing.Length; i++ )
    103.       {
    104.         var m = Clothing[i];
    105.         var collider = m.GetComponent<MeshCollider>();
    106.         if ( collider == null )
    107.         {
    108.           collider = m.gameObject.AddComponent<MeshCollider>();
    109.           collider.sharedMesh = m.sharedMesh;
    110.         }
    111.  
    112.         colliders[i] = collider;
    113.       }
    114.  
    115.       return colliders;
    116.     }
    117.  
    118.     [EditorButton]
    119.     private void CalculateOcclusions() {
    120.       GetBaseOcclusion();
    121.       DoFinalOcclusion();
    122.     }
    123.  
    124.     private void GetBaseOcclusion() {
    125.       m_Hits.Clear();
    126.    
    127.       var colliders = GetClothingColliders();
    128.       var m = BodyRenderer.sharedMesh;
    129.    
    130.       for ( int i = 0; i < m.vertices.Length; i++ )
    131.       {
    132.         var vert = m.vertices[i];
    133.         var normal = m.normals[i];
    134.  
    135.         const float BaseOffset = .1f;
    136.         const float HitTestOffsetMult = 1.5f;
    137.         var off = normal * .1f;
    138.         var p = vert + off;
    139.         RaycastHit hitinfo;
    140.         foreach ( var collider in colliders )
    141.         {
    142.           bool collision = collider.Raycast( new Ray( p, -off ), out hitinfo, BaseOffset * HitTestOffsetMult );
    143.           if ( collision )
    144.           {
    145.             m_Hits.Add( i );
    146.             break;
    147.           }
    148.         }
    149.       }
    150.  
    151.       Log.Debug( $"Hit Count: {m_Hits.Count}" );
    152.     }
    153.  
    154.  
    155.     private void DoFinalOcclusion() {
    156.       var m = BodyRenderer.sharedMesh;
    157.       var t = m.triangles;
    158.    
    159.       HashSet<int> hits = new HashSet<int>( m_Hits );
    160.       HashSet<int> culledHits = new HashSet<int>( m_Hits );
    161.    
    162.       for ( int i = 0, triIndex = 0 ; i < t.Length; i += 3, triIndex ++ )
    163.       {
    164.         if ( !hits.Contains( t[i] )
    165.              || !hits.Contains( t[i + 1] )
    166.              || !hits.Contains( t[i + 2] ) )
    167.         {
    168.           culledHits.Remove( t[i] );
    169.           culledHits.Remove( t[i+1] );
    170.           culledHits.Remove( t[i+2] );
    171.         }
    172.         else
    173.         {
    174.           m_OccludedTris.Add( triIndex );
    175.         }
    176.       }
    177.       m_Occluded = new List<int>(culledHits);
    178.       Log.Debug( "Final Occluded:" + m_Occluded.Count );
    179.     }
    180.  
    181.     private void OnDrawGizmosSelected() { DrawHits(); }
    182.  
    183.     private void DrawHits() {
    184.    
    185.       Gizmos.color = Color.green;
    186.       var m = OriginalBody;
    187.       foreach ( var vertIndex in m_Occluded )
    188.       {
    189.      
    190.         var vert = m.vertices[vertIndex];
    191.         var normal = m.normals[vertIndex];
    192.         Gizmos.DrawLine( vert, vert + normal * .05f );
    193.       }
    194.     }
    195.   }
    196. }
    197.  
     
    angrypenguin, judah4 and Ony like this.
  27. chingwa

    chingwa

    Joined:
    Dec 4, 2009
    Posts:
    3,784
    Alternatively you could apply a binary texture mask to your underlying character that uses the mask value in the Clip() function in order to disappear overlapping parts. You could check the mask in the vertex to save a little processing and then send it to the fragment to perform clipping (or just do it the fragment pass for better accuracy and detail).

    Different clothes items could carry their own underlying mask values, and then this could all be blitted to one master mask for use in the character.
     
  28. frosted

    frosted

    Joined:
    Jan 17, 2014
    Posts:
    4,044
    yeah, that could work also. But if you are generating the clip mask programmatically, why bother? Just clip the verts so you don't send to gpu at all.

    I think clip mask is more an approach to shift work to artist, so they can hand mark the occluded verts (?).
     
  29. chingwa

    chingwa

    Joined:
    Dec 4, 2009
    Posts:
    3,784
    I think it really depends on what exactly you are trying to do... The clip mask has the potential to be more accurate and allows more flexibility in what gets removed and how far. But yeah it does require extra setup in the art pipeline. If you are talking about mixing and matching many different types of items on top of each other you are going to be facing a lot of frustration I think, and probably no single method will work 100%.
     
    angrypenguin and frosted like this.
  30. snacktime

    snacktime

    Joined:
    Apr 15, 2013
    Posts:
    3,356
    Clothing as a separate mesh layered over the body is done because as a tool maker it's the lowest common denominator, it's a one size fits all that's easier to implement. But for what I would say is probably a majority of games, it's a sub par approach to say the least, ranging from that to downright horrible depending on the game. Mesh skinning/animation is one of the most expensive things we do in games, and this lowest common denominator approach is the worst performing.

    UMA has the better mid tier approach. Partition your base model into parts that match clothing. Your clothing is part of the model. So a robe requires a different set of mesh parts then pants. Potentially you might have a couple of different rigs here if you really really care about the difference in animating pants vs robes for example. Most just use a single rig especially for indie games on a budget. And then you just texture the individual parts and at runtime combine it all into a single mesh with a single texture atlas.

    I just got done doing a full implementation of that for our game with LOD and all. Took me under a week. Basically an implementation of the core of what UMA does. Using Mesh Baker to handle the combines as that saved me a good chunk of implementation time.

    My advice big picture is test. Make sure performance is a problem you need to solve. Performance here is a factor of number of verts, number of animating bones, the animation itself, number of skinned mesh renderers, and mechanim vs legacy. All things that are relatively easy to plug in different combinations and get a handle on the performance profile. See what that looks like at the max number of characters you want to have. Then just reason about is it using up more main thread cpu then you have budget for.

    The difference between least to most optimized is fairly significant. And performance differences can come from places you might not expect. Like we started to LOD animations after I realized just how big of an impact the number of keyframes made. I mean I know how skinning works, but it just wasn't something I thought about until I saw the actual difference.
     
    MadeFromPolygons and frosted like this.
  31. frosted

    frosted

    Joined:
    Jan 17, 2014
    Posts:
    4,044
    So, just wanted to paste an update to the process I'm using here.

    2019-09-20_13-13-01.gif

    So I added some additional logic to control how aggressively this will cull faces and allow you to just set some margin for safety.

    There are still some ways this could be better, but this will absolutely work for my needs, and far better than most other methods, including artist building a clip mask - since there's less of a chance of it being wrong imo.

    I'd be interested to see what people with a lot of experience with this kind of thing think.

    Why is this kind of process not widespread? Certainly seems pretty good.
     
    hopeful, angrypenguin and Ony like this.
  32. neoshaman

    neoshaman

    Joined:
    Feb 11, 2011
    Posts:
    6,469
    Uma also use triangle mask on top of separated body part, all have the advantage of being cumulative, so they work very well additively.
     
    kenamis and frosted like this.
  33. Ony

    Ony

    Joined:
    Apr 26, 2009
    Posts:
    1,973
    It looks like it will work really well, for sure! I dig it. :)

    With our games, people can make their own clothing, and a lot of times there is transparency (mesh, lace, straps, sheer, etc.) so the flesh still needs to be showing underneath.

    So, if you are making the clothes ahead of time and know exactly what clothes will be on a character, I think the most efficient approach would be to do this vert removal outside of the engine. Just remove the verts under clothing in the 3D program it's all modeled in, and boom.

    If you're working with uncertain or user-created clothing, especially anything that can be semi-transparent, etc. then the vert removal technique won't necessarily work as well. There are also places where the view can see through to the skin: underneath skirts, through sleeves, etc. so using a real time solution for masking that out would have to be fairly conservative in its approach. Even with a skirt, if you're doing any sort of cloth simulation, you don't really want to remove the verts under it.

    This thread is super interesting to me and I'm watching it closely. We've been doing removable dynamic clothing in our games for about twenty years now, and there's always some new and interesting way to approach it. Thanks for sharing yours. :)
     
  34. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    15,509
    I just kind of assumed that this is how it was done? Maybe not your specific implementation, but definitely a "detect invisible faces, remove them" sort of approach.

    I'm pretty sure Mixamo Fuse works (worked?) this way. When you pick different clothing it applies it to the mesh then analyses the body for hidden parts and removes them.
     
    frosted likes this.
  35. AndersMalmgren

    AndersMalmgren

    Joined:
    Aug 31, 2014
    Posts:
    5,358
    How is performance? What if a few players change clothing at the same time though very little chance of it happening in the same frame. Have you baked the info which faces each cloth hides?

    Edit: a jacket on a t-shirt, do you hide the shirt faces? :)

    Edit: let's say you have shirt, pants, jacket and a backpack. That's 4 skinnable meshes. Now we have 32 players on screen that's 32x4 skins.

    In Dayz even zombies have cloth like this. Must be baked into one skinnable mesh
     
    Last edited: Sep 21, 2019
    Ony likes this.
  36. frosted

    frosted

    Joined:
    Jan 17, 2014
    Posts:
    4,044
    That example, performance is not ready for realtime. But with a little work you could do it, sure.

    Process is basically:
    1 - do a raycast from each vertex against the occluding mesh (so your cost here would depend on the number of verts).
    2 - find the centroid of each face.
    3 - for each face, find the closest non-occluded face (this distance measure is used to control how aggressive the culling is)
    4 - rebuild the mesh excluding whatever faces/verts you want to cull.

    The performance at every level will depend on the number of verts and faces. But if you bake the data you get from the 3rd step (could be culling mask texture, or just raw data - raw data being much faster) applying it and rebuilding the mesh should generally be fast enough for realtime.

    Applying multiple meshes doesn't matter, this is just the union of the culled verts.

    Doing multi-layered could also be possible, but if there are many permutations, this could be more time consuming. This, I imagine would need to be done in build step or something. If you are loading mods, then perhaps there would be a processing step on loading the mod, with data baked into the users installation directory.

    I am hoping to use this to control some limited multi-layering. For example, laying a breastplate on top of cloth on top of skin. I would like to cull the skin and cloth under the breastplate.

    But I also will not have too many combinations. Maybe 30 total combinations.
     
    AndersMalmgren likes this.
  37. AndersMalmgren

    AndersMalmgren

    Joined:
    Aug 31, 2014
    Posts:
    5,358
    But you will not bake intgo singel skinned mesh then?
     
  38. frosted

    frosted

    Joined:
    Jan 17, 2014
    Posts:
    4,044
    Combining mesh itself would not be too hard, but to build atlas and remap uvs might be a real pain in the ass.

    I will probably not bother, I don't think drawcall overhead on characters is that big a deal. I won't have enough characters on screen for it to matter, and apparently SRP has some better options here also.

    Finally, I would like to be able to do stuff like retint skin color, which if the meshes were combined would require a skin mask. The additional cost of skinmask on gpu is probably a bigger cost than cpu drawcall for my needs.

    With separate 'flesh' mesh - I can just apply whatever color remapping to the entire material without worrying about atlas and separate masking.

    I think most people have better CPU than GPU, and almost anyone who has a good GPU will have top shelf CPU, so I tend to prefer GPU optimization over CPU optimization. Although, maybe this is naive?
     
    AndersMalmgren likes this.
  39. frosted

    frosted

    Joined:
    Jan 17, 2014
    Posts:
    4,044
    you could also combine to a single mesh with MeshCombine or whatever, but this will just make submeshes for each material iirc.

    I'm not sure if this really does anything worthwhile if each submesh has a separate material, since at least with legacy, this will still produce multiple passes. So again, this would need atlasing to be worthwhile.

    @snacktime mentioned building an atlasing tool, I imagine this was like a 4 texture cap system? Like 4 1k textures into a 2k texture - then just remap the uvs into the different quads?
     
  40. snacktime

    snacktime

    Joined:
    Apr 15, 2013
    Posts:
    3,356
    I used the mesh baker asset for that, it takes care of packing and uv re mapping and is configurable as to what the max resolution of the atlas can be.
     
  41. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    15,509
    Even if the input is received at exactly the same time, the user experience impact is negligible if you queue them to happen one after the other.

    Depending on what the expensive parts are, as well as optimising it you could also potentially background it and do it over a bunch of frames.
     
    Ryiah likes this.
  42. AndersMalmgren

    AndersMalmgren

    Joined:
    Aug 31, 2014
    Posts:
    5,358
    True, we queue AI logic like that atleast the ones that use the renderer thread. Though the difference is it does not use user input. If you wait more than a few thread you will start to get input lag. edit: though you can prioritize players own cloth change so not a big problem.
     
  43. Teila

    Teila

    Joined:
    Jan 13, 2013
    Posts:
    6,929
    We make clothes for UMA and it works well. @frosted One night thing UMA has is a mesh hider which allows you to paint out polys and attach the mesh hider to the recipe. You might want to look at something like that for your clothing. Also, be aware of hidden polys in clothing as they can cause issues.

    I so no reason something like this could not be implemented for a non-UMA system as well.
     
    Ony, Ryiah and Lurking-Ninja like this.
  44. frosted

    frosted

    Joined:
    Jan 17, 2014
    Posts:
    4,044
    This is just manual vert/face painting to mark which bits are culled? That would be pretty easy to implement I think, although good ui might be time consuming.
     
    Teila likes this.
  45. windylel

    windylel

    Joined:
    Aug 8, 2020
    Posts:
    1
    Could you please share your latest code? Thank you!
     
  46. Reanimate_L

    Reanimate_L

    Joined:
    Oct 10, 2009
    Posts:
    2,785
    @frosted That's looks like a good way to handle it. Might want to split the body into 3 parts ie Head, Upper Body, and Lower Body. That way you can use any cloth combination using prebaked mesh using your tools and maybe store them as a scriptable objects?
     
  47. Hines94

    Hines94

    Joined:
    Feb 15, 2020
    Posts:
    19
  48. Leniaal

    Leniaal

    Joined:
    Nov 7, 2012
    Posts:
    119
    judah4 likes this.
  49. oliviajaymeslin

    oliviajaymeslin

    Joined:
    Nov 23, 2021
    Posts:
    1
    I kind of want to dress my BLM in some BLM sorta armor. I kinda want him in some regular clothes too though idk it’s a tough choose what’s your opinions ?
     
    Last edited: Dec 1, 2021