Mutable Groom with MetaHuman

Hello,

I am using Mutable with MetaHuman characters and Grooms.

It seems that MetaHuman Grooms are set up with 7 LODs, whereas MetaHuman heads depending on Export preset can be 4 LODs.

This difference seems to crash Mutable, as the LODs do not match.

The docs https://github.com/anticto/Mutable-Documentation/wiki/Grooms-(v5.5) do state that: “Mutable generated Skeletal Mesh must have the same number of LODs as the Source Skeletal Mesh in the Binding Asset.”

So I assume this is expected.

Is there an easy way to use MetaHuman grooms in Mutable without having to create copies to reduce the LODs?

In BPs this seems to be handled by the LODSync node, but Mutable does not seem to have anything like this, is that correct?

Is there a recommended workflow I could follow?

Thanks,

Claudia

Hi Claudia, sorry for the delay following up. I wanted to look through the code and work through the process of setting up some MH grooms with Mutable before following up. When I did that, in both 5.7 and 5.8, I didn’t end up running into a crash, as long as the binding asset had the optimized head mesh specified as the Source Skeletal Mesh.

In your setup, did you change the binding asset to reference your optimized head as the source mesh? I noticed by default when you assemble the MH character, that property isn’t set. If that doesn’t help, would you be able to provide the callstack of the crash you’re running into? And it’d also be helpful to see a screenshot of how you’re applying the grooms in the Mutable CO graph if you can share that.

Thanks,

Euan

Hello Euan,

Thanks for getting back to me :smiley:

That’s unfortunate, I just tested the same setup in a vanilla UE5.7 and vanilla UE5.8 with a default MetaHuman and could also not reproduce the Crash.

Unfortunately in my real Project it’s a reproducible issue, but I am not able to share it here. I created completely new COs to test it and its still happening, but I assume its something I will need to investigate more on our end first then.

The error I’m seeing in the crash log is:

Assertion failed: (Index >= 0) & (Index < ArrayNum) [File:Z:\UEVFS\Root\Engine\Source\Runtime\Core\Public\Containers\Array.h] [Line: 1095] 
Array index out of bounds: 7 into an array of size 7

And I also see

DBG-MODULE<00007FFD126A0000 003fe000 "KERNELBASE.dll" "KERNELBASE.pdb" 0 {d218ba2f-1c9b-5e12-cc7223574b1b6698} 1 3392326996>
DBG-MODULE<00007FFC0B690000 01ded000 "UnrealEditor-Core.dll" "UnrealEditor-Core.pdb" 0 {35d36dfd-f660-466f-a4c31f2fde7b3a73} 1 1785874590>
DBG-MODULE<00007FFC4EC40000 00435000 "UnrealEditor-RenderCore.dll" "UnrealEditor-RenderCore.pdb" 0 {8b019998-d942-4717-bd6eb5c066c39863} 1 1785874588>
DBG-MODULE<00007FFBBC2D0000 01e42000 "UnrealEditor-Renderer.dll" "UnrealEditor-Renderer.pdb" 0 {906f5afb-dc03-492f-ae9b35b61e411154} 1 1785874715>
DBG-MODULE<00007FFBB2D50000 003ed000 "UnrealEditor-HairStrandsCore.dll" "UnrealEditor-HairStrandsCore.pdb" 0 {28386ee0-14ea-4295-bafe1bf70dd05a00} 1 1785874932>

So I assume its related to something with the Strand Hair, which I do remove in my reduced LOD hair versions.

I will investigate a bit more on my end.

Its already great to know that this _should_ work then. Thank you so much!

Claudia

Hi Claudia,

I wouldn’t say for certain that this should work, it’s likely to depend on the exact setup, and the operations that you’re performing on the mesh. It may just be that my repro setup was too simple to run into the same issue that you’re hitting.

Can you give me some more info on your setup:

  • What’s the Source Skeletal Mesh in the binding asset (if any), and how many LODs does it have
  • What’s the Target Skeletal Mesh in the binding asset, and how many LODs does it have
  • What’s the Reference Skeletal Mesh in your CO, and how many LODs does it have
  • How many LODs is your CO generating
  • What kind of modifications are you performing on the head mesh, are you doing anything that would potentially change the topology of the mesh?

In terms of the callstack, it looks like the pdb files weren’t found so there’s no symbols included which makes it hard to see exactly what’s going wrong within the hair strands module. Would you be able to provide a callstack from a build that does include the symbols?

Thanks,

Euan

Hey Euan,

I did succeed in creating a simple CO setup in my real project that did not crash, using only a MetaHuman head with the skin mesh section. But as soon as I added the tongue mesh section it crashed again. I have not quite figured out why and will look into getting a better callstack for you tomorrow and see if I can share that.

To answer your questions:

  • > What’s the Source Skeletal Mesh in the binding asset (if any), and how many LODs does it have

Pure MetaHuman Setup: None (seems to be the default)

  • > What’s the Target Skeletal Mesh in the binding asset, and how many LODs does it have

Pure MetaHuman Setup: MetaHuman SKM - 4 LODs

  • > What’s the Reference Skeletal Mesh in your CO, and how many LODs does it have

Same MetaHuman SKM - 4 LODs

  • > How many LODs is your CO generating

4 LODs

  • > What kind of modifications are you performing on the head mesh, are you doing anything that would potentially change the topology of the mesh?

None. It crashes on a simple CO with no mesh modification nodes.

In my vanilla UE project I was not able to reproduce the crash, but with the same setup I see LOD issues when adding the MH hair to the Mutable Groom node. It seems like the hair sometimes disappears and sometimes gets deformed. I attached a video and my test project.

Is that expected behavior for MetaHuman Grooms with Mutable?

Thanks,

Claudia

HairLODissue.mp4(1.3 MB)

Nevermind, it only allowed one attachment. Adding the test project

MHMutable.zip(565 MB)

Thanks for attaching the repro project; that’s helped track down what’s going wrong here. The reason we’re crashing is that Mutable is changing the number of sections on the output mesh, which the groom component can’t cope with, since it needs to be identical to the target mesh in the binding asset. There are a couple of different things at play here. The first is that we also ran into the same crash in development of 5.8 and have made a fix for. The CL with the change is 52376990, but it’s just a one-liner in AddHairStrandInitMeshSamplesPass if you’re able to make source modifications:

Previously:

SectionDatas.Init(FSectionData(), TotalSectionCount);New:

SectionDatas.Init(FSectionData(), FMath::Max(uint32(MeshData.Sections.Num()), TotalSectionCount));That will at least prevent the crash, but the groom will still be invalid as the sections on the generated mesh don’t match the binding asset. The reason for that is this property in the customizable object:

There’s some information about that property in the docs here. It causes the lower LODs to be generated automatically, resulting in different section information. To prevent that, you can switch it to manual, and then wire up all the section data from the input mesh. That should give all the same sections on the output, which should mean the groom binding is now valid.

Having said that, there’s a final issue that I then ran into. As you mentioned originally, MH uses the LODSync component to keep the groom LODs synchronized with the mesh LODs, and it turns out that MH characters only use LOD1, 3, 5 and 7. There is some code that strips the geometry from the groom’s other LODs during assembly to make those assets more efficient. Since Mutable doesn’t have anything similar to the LODSync component, you’ll see the groom LODs that don’t contain any geometry being used as the LOD changes in game, so the groom will disappear.

I’m going to talk with the MH team about this because it feels like the better approach here would be to consolidate the optimized groom so that the LODs are contiguous, instead of only using 1, 3, 5 and 7. That would mean you would be able to use the optimized groom with your Mutable setup. But in the short to medium term, the only option here is to make a code change to remove the optimization step and leave the original groom intact.

The code in question is here in ProcessSkeletalMeshes in MetaHumanGroomEditorPipeline.cpp:

				// Need to optimize the Groom before downsizing to have final Groom data and avoid creating textures that will not be used later on
				if (GroomQuality != EMetaHumanQualityLevel::Cinematic)
				{
					OptimizeGroom(NewGroom, GroomQuality);  // REMOVE THIS LINE
					DownsizeGroomTextures(NewGroom, TargetPlatform, OuterForGeneratedObjects, GroomBuiltData);
				}
 
				// Need to call PostEditChangeProperty for the hair cards after any changes
				FPropertyChangedEvent HairGroupCardsChanged(UGroomAsset::StaticClass()->FindPropertyByName(UGroomAsset::GetHairGroupsCardsMemberName()));
				NewGroom->PostEditChangeProperty(HairGroupCardsChanged);

Hi Euan,

This is great information, thank you so much!

We tried this out the code snippets and I set the Skeletal Mesh Component to Manual LODs only.

The first fix seems to work and its not crashing anymore, which is great!

The second fix however causes the hair to not end up on the MetaHumans head but be all over his eyes and neck etc. This is what we see with that:

Setting it to use “Only manually created LODs” does not seem to change anything.

Do you have any further suggestions of what we can try?

I also attached the callstack with symbols now.

Thanks!

Claudia

callstack.txt(5.12 KB)

Hi Claudia, good to hear that fixed the crash at least. In terms of the groom not being applied correctly, did you hook up all of the section pins from the input Skeletal Mesh node to the Skeletal Mesh Component node? This is what my graph looked like in the end:

The other thing you can do to confirm this is look at the section information in the preview viewport within the CO editor. You can display the section data via the drop-down that I have highlighted in the screenshot. If you compare what you see there with the sections in the target skeletal mesh for the groom asset, the number of sections needs to be the same.

Thanks,

Euan

Hey Euan,

I re-created my test setup again, just to make sure I get the proper sections and did not forget anything.

This is what the different LODs look like for us.

LOD0 - 9 Sections

LOD1 - 7 SectionsLOD2 - 4 Sections

LOD3 - 4 Sections

That matches my Mutable setup.

But we noticed that even within the same LOD, when zooming in/out the hair changes. So it does not seem to only change between LODs?

Is there anything else I can test for this?

Thanks!

Claudia

Hi Claudia, it’s hard to say what could be going wrong here without being able to look at the assets directly. Is there any chance you could share a cut-down version of the project? I’ve also attached my working version of the repro project that you sent over previously in case you want to compare that to see if there’s anything obvious that’s different.

> But we noticed that even within the same LOD, when zooming in/out the hair changes. So it does not seem to only change between LODs?

This sounds like the groom LODs vs the mesh LODs which is part of what I was talking about previously when I mentioned that the Metahuman Creator groom optimizations would need to be disabled. Since Mutable just passes through the groom components, there are no modifications to the LODs on that asset. And all that MH Creator does when optimizing the MH is to strip out the geometry from the groom LODs, it doesn’t actually remove them. So you’ll still see 8 LODs on the groom, which will change independently of the generated mesh’s LODs.

Thanks,

Euan

MHMutable_08_17.zip(721 MB)

Hi Euan,

Thanks for your response and the updated sample project!

> Is there any chance you could share a cut-down version of the project?

I don’t think so, but I will check with our team to see what we might be able to do instead.

> I mentioned that the Metahuman Creator groom optimizations would need to be disabled

How do I do that? I checked MetaHuman Creator and did not find any option for that.

Thanks,

Claudia

No problem, hopefully that’s useful. Let me know if you find anything - it may just be that the repro was too simple and I’m missing something. I’d double-check the groom binding asset to make sure the relationships between the source, target, and mutable output meshes are the same as in the repro project.

> I don’t think so, but I will check with our team to see what we might be able to do instead.

We can also convert this thread to private if that helps with sharing anything further.

> How do I do that? I checked MetaHuman Creator and did not find any option for that.

That’s something that would require a source modification, since all grooms have every other LOD stripped of geometry by default. It’s the code that I mentioned previously in ProcessSkeletalMeshes in MetaHumanGroomEditorPipeline.cpp:

				// Need to optimize the Groom before downsizing to have final Groom data and avoid creating textures that will not be used later on
				if (GroomQuality != EMetaHumanQualityLevel::Cinematic)
				{
					OptimizeGroom(NewGroom, GroomQuality);  // REMOVE THIS LINE
					DownsizeGroomTextures(NewGroom, TargetPlatform, OuterForGeneratedObjects, GroomBuiltData);
				}
 
				// Need to call PostEditChangeProperty for the hair cards after any changes
				FPropertyChangedEvent HairGroupCardsChanged(UGroomAsset::StaticClass()->FindPropertyByName(UGroomAsset::GetHairGroupsCardsMemberName()));
				NewGroom->PostEditChangeProperty(HairGroupCardsChanged);

Thanks,

Euan

Hey Euan.

I just wanted to ping this again to not have it automatically close. We did not get around to testing anything more yet to maybe determine what is different between our project and vanilla UE yet. I will get back to you when we could investigate a bit more. Thanks for all your help!

Hey Euan,

We finally were able to have a look at this and were able to update the MetaHuman grooms to not cull any LODs. That seems to fix the issue! Thanks for the advice!

We found that the hair groom does not get culled when using the Cine mode, but does when using the UE Optimized mode.

Is there an easy way to configure new templates for assembling the MetaHumans? Something where we could define how the groom should be configured?

Thanks!

Claudia

Hi Claudia,

That’s good to hear that you managed to get things working in the end. Odd that I didn’t run into the same problem before I stripped out the optimization, I just saw the intermediate LOD grooms as missing.

When you say a template, are you thinking along the lines of something data-driven where you have an asset that you can specify things like which LODs get stripped? My understanding is that the original intention for the assembly pipelines used by Creator was for them to be customizable, but I’m not sure how far we’ve gotten with that (I’d need to double-check). Having said that, even if they can be customized, nothing is exposed to the editor currently as far as I’m aware.

Thanks,

Euan

Hi Euan,

Oh so is the UE Optimized mode not what is stripping the LODs?

> data-driven where you have an asset that you can specify things like which LODs get stripped?

Yes exactly. I would love to configure if LODs get stripped, or which type (strand, card, mesh) each LOD is meant to have. It’d be great if that was configurable somewhere to avoid having to modify the code or adding some post process action after MH assemble.

Thanks,

Claudia

Hi Claudia,

> Oh so is the UE Optimized mode not what is stripping the LODs?

Sorry, no your original thinking was correct - it is the MH optimization code that is stripping the LODs out of the groom. I was just surprised that the stripped LODs resulted in different issues for me vs what you were seeing. I just saw them missing entirely when those LODs should have been active, whereas you got the odd rendering artifacting.

> Yes exactly. I would love to configure if LODs get stripped, or which type (strand, card, mesh) each LOD is meant to have. It’d be great if that was configurable somewhere to avoid having to modify the code or adding some post process action after MH assemble.

In theory the pipeline blueprints that are used by MH creator would be the place for this kind of thing to be exposed. But at the moment it’s very limited. For instance, the lods of both the face mesh and the grooms are determined by this property in BP_DefaultLegacyPipeline_High, which lives in the MetaHuman Creator plugin (this pipeline is used by Creator when building a cine asset, there’s a different pipeline for optimized characters):

There’s also a groom pipeline that is used by the main pipeline. You can access this by selecting the groom in Creator’s Hair & Clothing section, then hit Accessory Properties, and you’ll be taken to an asset that shows the values for the groom pipeline:

But you’re very limited on what you can actually set here. Changing the LODTransition property doesn’t actually change the type of each LOD; that’s still determined by the original groom asset. I know that internally we’ve discussed making these pipelines more open and extensible as part of the MH Crowds work, with the composition system there. But in theory, at the moment, you would likely need to write a custom pipeline class that exposed more functionality and used it during the build process.

Let me know if you’ve got any more questions on this or if you think we’re good to close out the thread.

Thanks,

Euan

Hi Euan,

> I just saw them missing entirely when those LODs should have been active, whereas you got the odd rendering artifacting.

Oh same, I only saw the artifacts after updating the code with your suggestion in a previous post. In UE5.8 it seems that the crash issue is automatically solved and whether the hair displays or not seems to depend on if its set up in the groom asset or not.

Thank you for pointing me at the MH pipeline configuration! I’ll have a closer look into that!

I think this ticket can be resolved, I now know how to use MH hair with Mutable.

Thanks again!

Claudia