Home Technical Talk

Autodesk 3ds Max Maxscript: Convert Object Position To Proper Bytes For Hex

polycounter lvl 6
Offline / Send Message
MrQuetch polycounter lvl 6
Hello. I'm deciphering the model structure for an old video game so I can see how my models look in the engine's hardware.

The structure uses 6 bytes for a single vertex. Each vertex has 2 bytes for each axis. The vertices are as follows: the first byte takes a value of 255 to make a whole number of the second unit, while the second unit represents the whole number unit. The good news is that the vertex positions start from the origin: 0x00 0x00. The downside is that the vertex points only move to the right on the X axis and down on the Z axis; the Y and Z axes are flipped for the game engine. So: 0x00 0xFF is basically the origin subtracting 1 whole unit. The weirdest part is that the whole number units wrap around. This means that once a whole number unit is at 0x80, it will wrap from the right or bottom edge to the left or top edge. The decimal unit (the first byte) doesn't wrap around since it is too small to reach the edges of the grid that the whole number unit (the second byte) can reach.

For example: instead of having a position of 2.385 (axis doesn't matter as all vertices work the same on each one), I would like that to be a value of: 0x82 0x02 for the two bytes. Notice that the 2 is the whole number unit, and the decimal (.385) is the 0x82. I hope all of this make sense.

I have searched on Google for floating and fixed point precision for 3ds Max with no luck. I'm hoping someone can help me get the position values properly converted to hex. Anything is helpful, as well as appreciated.

Replies

  • sprunghunt
    Options
    Offline / Send Message
    sprunghunt polycounter
    Use
    ceil
    and
    floor
    in MAXScript to get the whole numbers and decimal point component respectively. Then encode those using
    intAsHex


  • MrQuetch
    Options
    Offline / Send Message
    MrQuetch polycounter lvl 6
    Use
    ceil
    and
    floor
    in MAXScript to get the whole numbers and decimal point component respectively. Then encode those using
    intAsHex


    Thank you, sprunghunt.

    I have been using these, and they've been very helpful... However, I noticed that my values are much bigger than 0xFF - which is the full amount of a byte in a hex editor. Is there a way that I can lower the values instead of having them overshoot? Right now, the bytes are written in 8-bits: 0xAABBCCDD.
  • sprunghunt
    Options
    Offline / Send Message
    sprunghunt polycounter
    MrQuetch said:
    Use
    ceil
    and
    floor
    in MAXScript to get the whole numbers and decimal point component respectively. Then encode those using
    intAsHex


    Thank you, sprunghunt.

    I have been using these, and they've been very helpful... However, I noticed that my values are much bigger than 0xFF - which is the full amount of a byte in a hex editor. Is there a way that I can lower the values instead of having them overshoot? Right now, the bytes are written in 8-bits: 0xAABBCCDD.
    The first thing that I thought of is just to use Python instead of Maxscript. in python you can just use
    round( myNumber, 3)
    to round something off to 3 decimal places. This way you can keep you float values from being tool large to fit in a byte

    http://docs.autodesk.com/3DSMAX/16/ENU/3ds-Max-Python-API-Documentation/index.html
  • RN
    Options
    Offline / Send Message
    RN sublime tool
    Hi, some observations:

    - How do you know that the format is supposed to be what you think it is, is there any documentation? (It sounds like the half-float format)

    - "Right now, the bytes are written in 8-bits: 0xAABBCCDD." <---- That's a 4-byte value, like a 32 bit integer or float. If your coordinates use two bytes of storage, then format is this: 0xAABB.
    Each byte (AA, BB) is represented by 8 bits, but you don't usually think in bits when writing them. Something like the value 128, for example, you can write in the decimal ("128") or hexadecimal ("0x80") representations.

    - Your "385" (from your "2.385") is bigger than 255 and impossible to represent with a single byte. So you cannot break "2.385" down into two bytes, one for "2" and the other for "385" or something like that. The half-float format uses a different way, with sign, exponent and significand bits (read more), if it is what that game uses.
  • MrQuetch
    Options
    Offline / Send Message
    MrQuetch polycounter lvl 6
    MrQuetch said:
    Use
    ceil
    and
    floor
    in MAXScript to get the whole numbers and decimal point component respectively. Then encode those using
    intAsHex


    Thank you, sprunghunt.

    I have been using these, and they've been very helpful... However, I noticed that my values are much bigger than 0xFF - which is the full amount of a byte in a hex editor. Is there a way that I can lower the values instead of having them overshoot? Right now, the bytes are written in 8-bits: 0xAABBCCDD.
    The first thing that I thought of is just to use Python instead of Maxscript. in python you can just use
    round( myNumber, 3)
    to round something off to 3 decimal places. This way you can keep you float values from being tool large to fit in a byte

    http://docs.autodesk.com/3DSMAX/16/ENU/3ds-Max-Python-API-Documentation/index.html
    Hi again, sprunghunt.

    I was unaware that I could use Python in Maxscript. But, I just realized that I have a function for rounding a number to a specific decimal point, so I will use that first. If it doesn't work, then I will look in to the Python documentation. Thanks again for your help.
  • MrQuetch
    Options
    Offline / Send Message
    MrQuetch polycounter lvl 6
    RN said:
    Hi, some observations:

    - How do you know that the format is supposed to be what you think it is, is there any documentation? (It sounds like the half-float format)

    - "Right now, the bytes are written in 8-bits: 0xAABBCCDD." <---- That's a 4-byte value, like a 32 bit integer or float. If your coordinates use two bytes of storage, then format is this: 0xAABB.
    Each byte (AA, BB) is represented by 8 bits, but you don't usually think in bits when writing them. Something like the value 128, for example, you can write in the decimal ("128") or hexadecimal ("0x80") representations.

    - Your "385" (from your "2.385") is bigger than 255 and impossible to represent with a single byte. So you cannot break "2.385" down into two bytes, one for "2" and the other for "385" or something like that. The half-float format uses a different way, with sign, exponent and significand bits (read more), if it is what that game uses.
    Hi, RN.

    1.) There is some documentation, but it's loosely based and partially reverse engineered by other people; I had to manually edit bytes on my own to see what exactly the data does. The extra work doesn't matter in my opinion because it's been a good learning experience. I will look at the "half-float format".

    2.) Oops, I meant 32-bit. When I see 8 bytes next to each other I think of 8 bits; I know I shouldn't confuse my bytes with bits. Right, so the format is just two bytes of storage. I'm aware I can write the hexadecimal representations, but thanks for reminding me.

    3.) I just noticed that I failed to add further information, and it lead to this assumption. I'm figuring out a way to convert my position into hex. I've decided that for the whole number byte is just the whole number of my vertex position, and the decimal number byte is just a normalized value between 0 and 1 (by taking the float portion of the vertex and subtracting it by the integer portion). I've been making calculations for the float portion and have found that I can use the number 0.00392156862 * 255 = 0.999999998 - which is an incredibly small number for getting a roughly "precise" number. With the number (0.00392...) I can use a for-loop and check the decimal number to see what that number lies between, and then set the hex number accordingly. I will also look into the sign, exponent, and significand material; I read a little bit about it already, and it's vert interesting.

    Thank you for your help and suggestions; it means a lot to me.

  • RN
    Options
    Offline / Send Message
    RN sublime tool
    I see what you mean, you have a byte for the whole part of the number and a byte for the fractional part of the number.
    You have 256 steps in that fractional byte, so each step could represent that 1.0 / 255 = 0.0039215... increment of the fractional part, like you mentioned.

    In order to fit those two bytes into a two-byte int, you can use the bit shift operations (shift left, in your case). Something like:
    (AA shl 2) or BB

    (bitwise shift-left 0xAA for two digits, so it's 0xAA00, and then do a bitwise OR with 0xBB to get 0xAABB)
  • MrQuetch
    Options
    Offline / Send Message
    MrQuetch polycounter lvl 6
    I managed to solve my problem with the vertex coordinates. I searched "Significand" on Google and found my answer on Wikipedia. In the end, I ended up using: "vert.x*10^2", and the same for y and z axes. This way, the vertex position was able to fill up the two bytes for one axis, and similarly for the others. Now, my problem is getting the normals to work properly. It appears that the correspondence is similar to vertex colors and texture coordinates - where they don't match the model nicely.

    For my normals I'm using: "$.modifiers["Edit Normals"].getNormal i" to get them correctly. Unfortunately, the data still doesn't convert as it should. I know that like the vertices, it will be the exact same for the normals where I will use: "norm.x*10^2" to get the same bytes in memory.
  • MrQuetch
    Options
    Offline / Send Message
    MrQuetch polycounter lvl 6
    Edit: I have managed to get the normals exporting fine for the most part. However, the length of the normal is different for every vertex (that is something I'm not changing in Max). If I could find out a way to get the normals to have the same length no matter where they are, the work here would be done.
  • MrQuetch
    Options
    Offline / Send Message
    MrQuetch polycounter lvl 6
    Another Edit: For those who were wondering, I have solved my problem with the vertex normals. Apparently, they are supposed to be offset from the vertex coordinates themselves. So, I came up with a couple extra Maxscripts to aid me, but this will work for now. Basically, after I create my model, I use a shell modifier on it, and then I separate the shell from the main mesh, and then I create normals (dummy objects) that have the same indexes as the vertices in the mesh. But, these "normals" get transferred onto the shell instead of the main mesh. Once I export my data to binary hex, the "normals" positions are saved instead of the real normals of the model. I don't know why it won't work with the ordinary normals, but perhaps it will have to be that way for this old bizarre model format. Thank you everyone for your help.
  • RN
    Options
    Offline / Send Message
    RN sublime tool
    That's odd.
    So it's not really a normal, it's more like "the mesh vertex offset by its normal", and the game probably derives the actual vertex normals from the vector 'offsetVertex.xyz - meshVertex.xyz'.

    In order to use your original mesh normals, try exporting this vertex normal data as 'meshVertex.xyz + meshNormal.xyz' (vertex position plus its normal vector, same as offseting it like a shell).
  • MrQuetch
    Options
    Offline / Send Message
    MrQuetch polycounter lvl 6
    RN said:
    That's odd.
    So it's not really a normal, it's more like "the mesh vertex offset by its normal", and the game probably derives the actual vertex normals from the vector 'offsetVertex.xyz - meshVertex.xyz'.

    In order to use your original mesh normals, try exporting this vertex normal data as 'meshVertex.xyz + meshNormal.xyz' (vertex position plus its normal vector, same as offseting it like a shell).
    Hi again, RN.

    Yeah... That threw me off for awhile; gave me some good headaches. It really is strange, because I have come across no other models that use normals in this manner. Thank you for the extra idea about 'meshVertex.xys + meshNormal.xyz'. I appreciate the extra ideas that you have, and that you keep coming back to check-up.
Sign In or Register to comment.