Loading UMA Assets

Dustin

--

This is a devlog to see if I can port the “Universal Multipurpose Avatar”(UMA) addon for Unity over to the Godot Engine. You can find Part 6 here.

I’ve had a lot of disappointments since my last update because I forgot Dustin’s First Law of Programming: “Nothing will ever be easy”. I followed all of the crazy spaghetti code across a dozen source files, many with thousands of lines of code in a single file. And what did I find at the bottom of the plate? Proprietary Unity functions that I don’t have the source for and aren’t even written in .NET which means I can’t easily decompile them.

I should back-up and explain why this sucks so much.

UMA Asset Layout

An UMA has its definition stored across a number of files. At the top, it has “RaceData” which contains only a few pieces of data
-The Basics: Name, height, etc…
-Wardrobe Slots: This defines what type of clothes can go on an UMA
-Recipe: The “Recipe” name that the UMA starts with. This is a pointer to another file.

The two most important things in a Recipe file is the UMA’s DNA(how, and what parts can be modified) and slots(the body parts that make up an UMA).

An UMA slot is where we actually get the visual definition of an UMA. A Slot file, along with bone data, actually lists the vertices in a Mesh to create a leg, foot, head, etc…

All the Obfuscation!

Now, here’s the problem(s) I’ve run into. I’ve mentioned several times before the very tight integration the UMA code has with the Unity Engine. Now that tight integration has seeped into the format of these asset files.

The first problem is that all of the files I described above are in Unity “.asset” files. These asset files are in yaml format. Not a huge deal, I can always get a yaml reader off github. The problem is that many of the assets aren’t stored in text format like Recipe’s are. Some are stored in binary files that are created by the source-absent Unity functions I mentioned in the beginning. And there doesn’t seem to be any official way to decode them that can be used in an open source project.

It appears that all the RaceData definition files are in this binary format, which means that I need to decipher the binary files somehow. Unity has a binary2text tool that I could use to convert them one at a time. The problem with that is that will only work for my PoC. That’s not a solution for the full product. I wouldn’t expect people to run a proprietary, command-line tool on a few dozen files they just purchase before they can use the suit of armor. This will be a problem I solve in the next milestone. I found this tool(with source!) promising.

For this milestone, I just want to render something! So I’m skipping the race data and going straight to the recipe and slots. This is where I hit another obstacle. In the slot assets, even if the asset is stored as text instead of a proprietary binary format, the text data is encoded. Rather than just list out the float values of all the vertices, it stores it in a long hexadecimal stream. With a little googling and experimentation I was able to decipher the format.

Get this:
The first 8 hex characters represent 1 float in a vector. The hex characters have to be broken down into bytes, and then put into a float. But before doing that, we have to transpose a bunch of characters. Because even though the vertices are read left to right. The bytes in the hex word are read right to left. It’s like instead of the sentence “Thor hits Hulk” you get “rohT stih kluH”. But wait… there’s more! Every two characters are transposed so you actually read it as “orTh tshi lkHu”. I’m sure there’s somebody with a Master’s in Computer Science who paid better attention in school than me that understands exactly why that is, but for me…

The Task at Hand

In practicality, what it means is, given a hexadecimal string like so:
09a8d03d7bf7073e
I have to rearrange it to this:
3dd0a809 3e07f77b
And then I can cast each to a float value(The x and y values of the first vertex, in this case).

So to render an entire UMA, I have to:
-1. first run binary to text on the race data file
-2. Read in the text as a yaml.
-3. Use the yaml to find out which Base Recipe
-4. (possibly decode the recipe) and read in the Base Recipe file in Yaml
-5. From the Base Recipe read in the “Recipe String” which is in JSON format
-6. Use the Recipe String to look up each of the slots
-7. Read in each of the Slot files as a yaml
-8.Decipher the vertices hexformat
-9.And then, THEN! I can use the vertice array and render the mesh.

That is what’s going to be in store for me for Milestone 3. For now, I am just going to start with a slot file and make sure I can render it in an ArrayMesh.(Steps 7–9).

Looking even further down the road, I’ll also have to come up with a new way to find the UMA assets. The code depends on Unity keeping some kind of dictionary lookup of all the assets in a project and looks up files that way. So that means at some point I’ll have to write some kind of file-reading discovery feature to find all the different UMA assets.

Conclusion

I am frustrated by how little of the existing UMA code I can reuse. I’m starting to kick myself for wasting a week with porting over the code. Maybe it’ll pay off in a future Milestone, but for now, it seems like 90% of that effort was a waste.

While deciphering all these files, I had a moment where I wondered whether UMAs in Godot was going to be practical. Fortunately, I’ve found some tools that made it a fleeting question, but it felt demoralizing at the time.

Looking forward, assuming Dustin’s First Law doesn’t assert itself again, I think I’m done figuring stuff out for Milestone 2. I just need to code it to prove it works the way I think it does. I am optimistic that my next update will contain a picture of a UMA body part rendered in Godot.

--

--

Dustin

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