UMA in Godot Game Engine Part 1

Right now this is nothing more than a dump of my research into how one could port over the “Unity Multipurpose Avatar” system into the Godot game engine. UMA, in my humble opinion, is a great tool for Indie developers and beginning developers. It would fill a very significant gap in Godot for 3D game developers.

UMA made the awesome decision to open source their software with the MIT License which gives the ability for others to copy\port\modify their code.

Consider this the beginning of a Devlog that will go from the first audacious thought to Proof of Concept. I have no idea how long this will take, not even if it will succeed, and even if the POC is successful if a full port will happen. This is a journey that’s happening in real-time.

Early Conclusions

I started by researching the Code running in Unity. The first thing I’ve noted is that all of the code is written as c# scripts in unity. Not a regular library with “hooks” for Unity. While following some of the code, I found it to be pretty tightly integrated with Unity functions. That is going to make porting more difficult. It also means that it will have to likely be “fork” as well as a port. I don’t see a way of using the UMA code base that I could just add a Godot hook into. That’s a shame because it would’ve meant Godot could have benefitted from future features.

I have also found the code to be very readable. That is in spite of very large script files. That is a rare accomplishment in the gaming industry, so bravo to the developers. I’ve found some minor areas that could use improvements, but nothing critical. I might try and mark those areas during the port for future enhancements

Architecture

For as complex task that UMA is designed to do, it’s architecture is pretty easy to follow. When a scene starts, various pieces of date are fed into the class “DynamicCharacterAvatar.cs” in the folder /Core/Extensions/DynamicCharacterSystem/Scripts. This class outputs an instance of the class “UMAData.cs”. This data contains all of the rendering information for an UMA. On the last step, the UMAData is added to a “dirty”(meaning the rendering of it needs to be updated) list. The list lives in the “UMAGeneratorBase.cs”. UMAGeneratorBase is called each frame and performs several tasks based on priority. If there’s nothing more urgent to do, UMaGeneratorBase adds an UMA on its Update List. The character is then viewable in the scene.

In that short paragraph, I’ve glossed over literally thousands of lines of code and other code files that do important things, but that is the big picture of how the UMA system works.

Other Important Concepts

Just to help me wrap my head around the whole thing, here are some concepts I found that were new to me. If you’re reading this and are experienced with 3D character generation, some of these will probably be familiar concepts.

Loading an UmaRecipe

Loading an UMA Recipe into the DynamicCharacterAvatar class can be done 4 different ways.

  • Pull it from Unity Configuration
  • Give it a file location that has the “Recipe” data
  • Give it a url that has the “Recipe” data
  • Pass the Recipe data object direcly

What is a Recipe

A Recipe contains the data for which Meshes to use for the UMA in “Slots”. Slots represent things like “Torso”, “Feet”, etc… It also contains DNA data(things it will distort to make legs longer, feet shorter, etc) and it also contains a list of wardrobe recipes that may or may not over right the body Mesh data. And finally, it also contains skin texture data, bone structure data, and other additional Recipe items that I’ve not tried yet.

Significant Code

I don’t think it would be useful to try and post the entire execution path(s) that I’ve followed, but I do want to capture the code areas that I believe are most important.

Build and Load Character in DynamicCharacterAvatar.cs
Whether or not it is loaded from Unity Settings, File, or Recipe. Eventually the “BuildCharacter” function gets called. It builds the mesh and skin based on the “Recipe”. The “Recipe” contains information on wardrobe, DNA, etc… It also does some Unity specific function. Eventually, it calls the also significant function “LoadCharacter”.

LoadCharacter appears to gather all of the Slot, Additional Receipe, and wardrobe data. It then puts an order to the overlays for each of the meshes in the 3D slots.

It then calls several functions like “UpdateColors()” and “UpdateNewRace” and others to recreate the UMA Data. Eventually, it also puts itself on the “DirtyList” to be updated

DynamicCharacterAvatar.cs functions that seem significant, but aren’t
Don’t get distracted by “LoadMesh()” it’s only used in the preview window when working within Unity.

The “Update()” function doesn’t appear to do anything other than monitor for Recipe changes. Build\Load Character only seems to prepare itself with the correct information, but not actually rendered or added to the scene.

UMAGeneratorBuiltin.cs Build
The UmaDirtyList works pretty much the way I would expect it to. In the “Update” function of the UMAGeneratorBuiltin.cs, it calls “Work()” unless there’s nothing more pressing to be done. It eventually calls OnDirtyUpdate(). It takes the first item off the list, tries to render it, and if successful removes it from the dirty list and returns. The program does not try to update the rendering of more than one UMA per frame — makes sense to me as its probably a very costly procedure.

HandleDirtyUpdate is what’s called in the update loop and it takes in an UMAData. Oh baby, I feel like I’m really close to finding the code that I want…

The significant things being call in HandleDirtyUpdate are:

PreApply
A section for updating Texture
UpdateUMAMesh
umaData.skeleton.BeginSkeletonUpdate()
UpdateUMABody
UMAReady()

meshCombiner.Preprocess
This is in the class “UMAMeshCombiner.cs”. and is completely empty virtual function. The only class that implements UMAMeshCombiner is UMADefaultMeshCombiner and it doesn’t override that funciton. — hilarious!

PreApply
This calls “PreApplyDNA” in the UMAData class. Not sure of it’s signicance yet.

Section for Textures.
It doesn’t call a separate function, but there is a large section of code that figures out which textures go on what meshes.

umaData.skeleton.BeginSkeletonUpdate()
Updates any bone or skeleton changes of the UMA

UpdateUMAMesh
This calls UpdateUMAMesh in the UMADefaultMeshCombiner class. About 300 lines of code that all get used. I’m assuming this is where the body parts are fused together. If not, very poor naming…

UMAReady()
This calls umaData.Show() and then fires event notices(Update Event, Character Complete). Then does an odd check to see if there are too many bones in the UMA. I think umaData.Show() is the significant piece here.

All “Show()” does is a function in UMAData.cs that gets the list of renderers and makes their enabled property equal “true”. These Renderers are “SkinnedMeshRenderer” which is a built-in class in Unity.

SkinnedMeshRenderer

SkinnedMeshRenderer is Unity’s built in way for a script to create an object on the screen. Presumably, I’ll this will require significant changes when tryint to port to Godot.

Conclusion

That is as far as I’ve gotten. I haven’t tried to move any code yet. Now that I’ve got a basic idea of how UMA works and it’s architecture, I can come up with a strategy of how best to get a POC as quickly as possible.

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Dustin

Dustin

Software Engineer, Writer, and blogger who has a love-hate relationship with the Detroit Lions