Online face code generator tool | Tweak facekeys and see how they work

Users who are viewing this thread

Swyter

Grandmaster Knight
M&BWBWF&S
Hey, guys. Guess what? Made a face code generator, like the one for terrain codes, ten years later:


Working with 64-bit numbers in JavaScript is terrible, but it works. I'll polish it a bit, maybe adding support for dropping custom skins.txt files and get the right tags/morph keys back for your race.
🐧


Another thing I have found out is that there's seemingly support for 43-ish face keys, each of them is a 3-bit number that goes from 0 to 7, but only 42 of them do something usable. The 43nd only has one bit and doesn't seem to do anything when you move the slider. You can still add more to the list, but the game won't use them.


It's interesting that, like for combobox overlays, the list elements appear reversed, so the first one (generally Chin Size) appears right at the bottom.

All in all, very cool to have the format documented now.


The funny thing is that I made this because I wanted to take advantage of the new face code operations in the module system to convert stringified numbers from a string register back into an actual number. Now that we know the bit layout; with the bit width and shift of each field, it can be done by exploiting the face code properties to retrieve the individual components, do some magic stuff to take into account the hexadecimal vs. decimal and bit position to reconstruct the number back.


Right now we can turn numbers into strings, but not the other way around. As far as I know.

Another interesting tidbit is that when you export/import a character the game only saves/loads face_key_1 and face_key_2, which is only the first half of the code you see when pressing Ctrl + E in the face editor. Ideally there should also be a face_key_3 and face_key_4, the former stores the face morphs 21 to 42, so that information gets lost. If you wonder about face_key_4, it is always wastefully empty. face_key_2 stores face morphs 0 to 20 (there's a wasted bit).

If they had spent their bit budget well there would be enough space for 64 morph keys in those last three blocks. ¯\_(ツ)_/¯



Face code internal format​

A face code is actually made out of four 64-bit (or 8 byte) numbers (or blocks) written in hexadecimal and concatenated together.

Here named from left to right (0), (1), (2) and (3).

When you export a character from the Character > Statistics page it will only save the left half of it, storing the (0) as face_key_1 and (1) as face_key_2. Meaning that anything after facekey20 (i.e. from facekey21 to facekey42) will be lost. This may or may not matter, depending on the mod.

Keep in mind that the game is very inconsistent in how it calls these codes; sometimes it refers to the whole face code as a «face key», sometimes a «face key» means each of the small face shape tweaks that you can control via in-game sliders, like in module_skins.py, here we use the second meaning. Take a look at the diagram below:

Code:
 face_key_1 = 180000041
 face_key_2 = 36db79b6db6db6fb

                                  <------- start-|
                  <------- start-|               |
  <------- start-|               |               |
0x000000018000004136db79b6db6db6fb7fffff6d77bf36db0000000000000000
  _______180000041
       (0)        36db79b6db6db6fb
                        (1)       7fffff6d77bf36db
                                        (2)       ----------------
                                                         (3)
                                                       (unused)

  Here is block (0) from the example above, we will use the bit shift and number of bits columns
  from the table below to build bitmasks and retrieve each field, using the Python 3 prompt:

  For example, age is in block (0), has 6 bits and a shift of 30 bits,
  using hex((pow(2, num_bits) - 1) << bit_shift) we get:

    >>> hex((pow(2,6)-1) << 30)
    '0xfc0000000'
    >>> hex((pow(2,6)-1))
    '0x3f'

    >>> bin((pow(2,6)-1) << 30) # swy: same thing, but in binary
    '0b111111000000000000000000000000000000'
    >>> bin((pow(2,6)-1)) # swy: what this does is just to generate six ones, six bits
    '0b111111'

  Now that we have the right bitmask we can use the logical AND operation to isolate only the bits
  we are interested in and shift them back to the right (we can also see it as removing the padding)
  to get the actual number with just a simple ((block & bitmask) >> bit_shift):

    >>> hex(0x180000041 & 0xfc0000000) # swy: only leave our six potential bits toggled
    '0x180000000'
    >>> bin(0x180000041 & 0xfc0000000) # swy: same thing, but in binary
    '0b110000000000000000000000000000000'
    >>> bin(0x180000041)               # swy: here's the original, compare against the one above
    '0b110000000000000000000000001000001'

    >>> hex((0x180000041 & 0xfc0000000) >> 30) # swy: we are interested in the left-most «110» bit part you see above
    '0x6'
    >>> bin((0x180000041 & 0xfc0000000) >> 30) # swy: so move the bits 30 positions to the right, same but shown in binary
    '0b110'

    >>> ((0x180000041 & 0xfc0000000) >> 30)
    6

||    Do:  0000000000000000000000000000000110000000000000000000000001000001
 |  Mask:  ----------------------------XXXXXX
 | Final:                              000110

  In our case the age field contains the number six. See more examples below.

  _______180000041
                3f # hair: to get the field ((0x180000041 & 0x3f) >> 0), which results in 1
               fc0 # beard
             3f000 # skin
          3f000000 # hair_color: to get the field ((0x180000041 & 0x3f000000) >> 24), which results in 0
         fc0000000 # age: python code to generate a bitmask: hex((pow(2,6)-1) << 30)

valueblock no.bit shift in keynum bitscomments
hair006Max bits known, adjacent to next left field (beard).
beard066Max bits known, adjacent to next left field (skin).
skin0126 (¿? unsure, maybe more)Check max bits for correctness.
hair_color0246Max bits known, adjacent to next left field (age).
age0306 (¿? unsure, maybe more)Check max bits for correctness.
facekey0013 * 0 (0)3
facekey0113 * 1 (3)3
facekey0213 * 2 (6)3
facekey0313 * 3 (9)3
facekey0413 * 4 (12)3
facekey0513 * 5 (15)3
facekey0613 * 6 (18)3
facekey0713 * 7 (21)3
facekey0813 * 8 (24)3
facekey0913 * 9 (27)3
facekey1013 * 10 (30)3
facekey1113 * 11 (33)3
facekey1213 * 12 (36)3
facekey1313 * 13 (39)3
facekey1413 * 14 (42)3
facekey1513 * 15 (45)3
facekey1613 * 16 (48)3
facekey1713 * 17 (51)3
facekey1813 * 18 (54)3
facekey1913 * 19 (57)3
facekey2013 * 20 (60)3
facekey2123 * 0 (0)3
facekey2223 * 1 (3)3
facekey2323 * 2 (6)3
facekey2423 * 3 (9)3
facekey2523 * 4 (12)3
facekey2623 * 5 (15)3
facekey2723 * 6 (18)3
facekey2823 * 7 (21)3
facekey2923 * 8 (24)3
facekey3023 * 9 (27)3
facekey3123 * 10 (30)3
facekey3223 * 11 (33)3
facekey3323 * 12 (36)3
facekey3423 * 13 (39)3
facekey3523 * 14 (42)3
facekey3623 * 15 (45)3
facekey3723 * 16 (48)3
facekey3823 * 17 (51)3
facekey3923 * 18 (54)3
facekey4023 * 19 (57)3
facekey4123 * 20 (60)3
facekey4223 * 21 (63)1Only one bit left, wonky slider in-game and face does not seem to change;
why not exploit the remaining bit range better? Don't ask me.
 
Last edited:

Swyter

Grandmaster Knight
M&BWBWF&S
Improved and expanded the format and explanation of how to use the information in the table, hopefully it should be more approachable now.
 

Swyter

Grandmaster Knight
M&BWBWF&S
By the way, and a bit off-topic, but I can't be the only one hating the XenForo move and subsequent reorganization of the TW Forums. Previously you could see everything from the main page, The Forge was one click away, now it feels like there's categories upon categories of nested boards to the point one gets lost, and the fact that the modding section is segregated from the main one is also a bit backwards. Normal people won't look here, while previously you'd see a lot of cross-pollination of curious players and visitors.

I generally like what Janus does, and I also know SMF as a forum system was obsolete and hard to maintain. But this is absolutely atrocious, and a great way of killing your healthy community. I am sure that there are other factors at play here, like the recent Discord-for-everything fad, and that it's been a while since the games came out, but I'm pretty sure this one is at the top. It's not just blind nostalgia, it is legitimately worse conceived and implemented.

One example; I have been trying to format the table and every time I type 8) the forum silently turns it into an emoticon. Doesn't seem like there's anything like the [nobbc][/nobbc] tag from the old system, there was also a checkbox to toggle if you wanted automatic smiley conversion, now it does it every time.
 
Thanks very much for this tool. Having used your terrain code generator I noticed that it enabled users to create larger scenes than it was allowed by its Native counterpart (they all would be perfectly compatible with the game). I always wondered whether or not it was possible to do the same thing with HEX face codes as opposed to only HEX terrain codes. Is it though? Having the opportunity to make very deformed faces would be very appreciated!
By the way, and a bit off-topic, but I can't be the only one hating the XenForo move and subsequent reorganization of the TW Forums. Previously you could see everything from the main page, The Forge was one click away, now it feels like there's categories upon categories of nested boards to the point one gets lost, and the fact that the modding section is segregated from the main one is also a bit backwards. Normal people won't look here, while previously you'd see a lot of cross-pollination of curious players and visitors.
After the XenForo transition in 2020 things have not been the same... Now this place is obscured and does not enjoy that much popularity as it used to in the past.
 

Tocan

Sergeant Knight at Arms
Awesome! Thanks for your work Swyter.

After the XenForo transition in 2020 things have not been the same... Now this place is obscured and does not enjoy that much popularity as it used to in the past.
word!
 

Swyter

Grandmaster Knight
M&BWBWF&S
Improved the thingie a bit, now the buttons behave like the ones in the game.

unknown.png

It also supports dropping a custom skins.txt and choosing a race from the list to get the right mesh names. It's ugly, but everything is more or less in place.
 

Swyter

Grandmaster Knight
M&BWBWF&S
Just for completeness, Earendil told us on Discord of a handy tool from 2019 by Marnid called MB Studio that seems so support previewing troops using the OpenBRF library to load assets, it can probably work in tandem with mine: https://github.com/djaessel/MB-Studio

The ModDB pages were seemingly deleted, but maybe there's a way to reupload the precompiled version somewhere. If not, the source code is still up.

I haven't really been up to date with what has been going on here, but I'm slowly catching up. A lot of great utilities and fun advancements since 2017. Props to you, guys. ¯\_(ツ)_/¯
 
Last edited:
Thanks very much for this tool. Having used your terrain code generator I noticed that it enabled users to create larger scenes than it was allowed by its Native counterpart (they all would be perfectly compatible with the game). I always wondered whether or not it was possible to do the same thing with HEX face codes as opposed to only HEX terrain codes. Is it though? Having the opportunity to make very deformed faces would be very appreciated!
Any information if it is possible to do the above?
 

Swyter

Grandmaster Knight
M&BWBWF&S
Any information if it is possible to do the above?
The deformer sliders end up being normalized values in the -1.0 to +1.0 range, I think. So it depends on the actual values one puts in the skins.txt file as a mod developer.

Because my tool lets you set any field to any possible value within the available range you can probably contort faces more than usual, mainly because the in-game sliders have a set of restrictions where if slider X is bigger than A slider Y must be limited to B. You don't have these constraints here.

We have all seen the pygmy heads when the face codes are zeroed out or used in special code troops. Now you have full control about that, careful about choosing skin texture indices outside the normal range. It probably won't crash if the game is clamping the values correctly, but you never know.

Give it a whirl and let me know what you guys think. :party:
 
Last edited:

Swyter

Grandmaster Knight
M&BWBWF&S
By the way, you may have noticed that the in-game deformation sliders seem to have a more nuanced and precise range than mine.

Well, that turns out to be a temporary illusion; if you click on Done and go back to the face editor you will see how they stick to one of the eight available positions, even if they weren't lining up before. Smoke and mirrors.

Three bits, available range from zero to seven. Middle point being three, later remapped (or well, normalized) to negative to positive one.

So 0 is -1.0, 3 is 0.0 and 7 is +1.0, I believe. Not a lot to work with, but I guess nobody has noticed until now. :)
 

Swyter

Grandmaster Knight
M&BWBWF&S
Turns out the hair and beard mesh indices were off by one because I was missing adding an entry for no hair at all. Also fixed some small parser mistakes and improved the styling and layout. Now every time you change the code, the URL will also change, so that you can go back and forth using your browser's history function and it works a bit like a handy undo/redo. You can easily share and save links with specific codes.

If you dropped a custom skins.txt and it is no longer saved in your browser's local storage, falling back to Native again, it's because the face code page flushes older, known-bad parsed versions on purpose and resets the state. Drag and drop the file again and, boom, done.

PS: I was searching previous discussions about this (you know, prior art) and I found these from 2006 and 2015.
 

Eärendil Ardamírë

Subforum Moderator
WBWF&SM&B
Turns out the hair and beard mesh indices were off by one because I was missing adding an entry for no hair at all. Also fixed some small parser mistakes and improved the styling and layout. Now every time you change the code, the URL will also change, so that you can go back and forth using your browser's history function and it works a bit like a handy undo/redo. You can easily share and save links with specific codes.
Fascinating what all is possible, thanks for the work here!
I am sure that I have looked up every tool but forgot again about the second link of yours. It might also be that I just did not want to install another little tool, your html work is more handy at this. I missed the post of the first linked but I find it also difficult to understand and that it is something with Pearl might have deterred others :lol:
 
Top Bottom