0

I've been reverse engineering a MineCraft application in C, built with OpenGL. If you are unaware, the game involves the generation of cubes, UV Maps textures to the cubes, and then they are placed in the world that you (generally) add to, and remove to build and destroy (and a whole lot more).

The game is fully functional. It works. Everything renders. All functionality is okay. But I am having a LOT of trouble (for days), reconstructing the UV Mapping on a cube from a texture atlas. The problem specifically is that I do not understand how this system is mapping the textures onto the cube with UV Mapping. I've reverse engineered, reconstructed, and manually calculated the function spoken of in this post, and yet from all the resources I've read, I'm falling short. If anyone would be gracious enough to help me see what I've understood incorrectly, and / or how this function is doing what it is doing, I'd be overjoyed. This is the final piece, the rest of the application I understand inside and out. It's now just this.

One function is being utilized to create the vertices buffer data, the normal data, and the texture data to push down the GL Pipeline after the cubes are constructed within it.

The vertices data in the function I am reverse engineering is self-explanatory, as it is simply using a cartesian coordinate system. The normal data, also simple. But the UV Data, I've been spinning on this for three days. ONE reason I am jammed up is all of the tutorials for UV Mapping either involve a single square texture used on all 6 sides of the square, or an unraveled cube texture. This Minecraft does not use either of these. It uses a 3x1 rectangle map. Bottom, middle, top. The middle is used as all the side (left, right, front, back), and the top and bottom are standalone.

The other reason is when I've done the math (and I have, over and over again to check my calculations), I cannot come up with the same answer the program does. And the program is running fine, it works. So I know I'm missing something.

I've drawn the UV coordinates on my whiteboard, researched / read UV Mapping from many sources (I'll cite below), and I just cannot figure out how this formula works. The main reason is the non-existent documentation and relatively ambiguous naming conventions the person used to create these functions. But nevertheless, I'm so close to finishing everything. This is the last piece I need to understand in order for the whole puzzle to snap into place.

From my understanding as to how this works, with with UV Texture Mapping, the UV or ST coordinates map the same as Cartesian, except that they are to be from [0..1]. But from what I see as the algorithm in this application (that works), I must be missing something.

The way I was looking at it, or at least what I had read in multiple places, is that regardless of where your vertices are, wherever you want to render them, wherever you want the bottom left of the texture to be, you place it where you would consider your bottom left vertices to be. Top right of the texture, top right vertices. And so on. And when I did the math on some of these, 4 / 6 of the equations panned out when I substituted the proper [0..1] coordinates in place of the <x,y,z> vectors.

FYI, the texture atlas has 16 "rows", or textures, from left to right. But each cube consists of 3 texture squares. There are no "left", "right", or "back", as the "front" texture is used for each of those sides. Meaning, left = front = back = left... Ultimately, TOP, MIDDLE x 4, BOTTOM. Each texture block is 0.0625 wide, 0.0625 tall. That's 16 in total.

This is the actual texture atlas used. 16 textures, and in the 0..1 normalized coordinates, they are each 0.0625 coordinates each.

  • float *vector is an array of floats that will be used as the vertices for the shader.
  • float *normal is the array of normal data to be used for the shader.
  • float *texture is the UV data, which likewise is later added to the shader.
  • int left, int right, int top, int bottom, int front, int back are all booleans. They specify which faces to render. If all are 1, then all vertex data for that cube will be rendered. If all of those were 0, then none would be rendered.
  • float x, float y, float z are the coordinates of the block to be rendered.
  • int n is the offset of the coordinates supplied next, to build the cube (the width and height)
  • int w is the texture to be used. (from 1 - 16)

When the function begins, it subtracts 1 from w to start the texture coordinates at 0, instead of 1. It then runs through, setting the coordinates for each point, going to the next memory space and assigning it the following coordinates. It does that over and over, for the *vector_data which points to *vector, and *normal_data which points to *normal. This is all intuitive.

But coming to *texture_data which points to "*texture", is where it all goes to hell and a hand-basket for me.

I used values (0, 0, 0) as the initial (x, y, z) values, w = 1, n = 0.5, and all faces, int left, int right, int top, int bottom, int front, int back = 1, 1, 1, 1, 1, 1, to try to dissect it, as could not find clear-cut information in the resources I cited below, not at least which could help me resolve this, that I could understand.

Just to let you know I've tried everything I think I can, I'll also give you the data that I ran through this application, that didn't match up with my equations.

THE PROBLEMATIC FUNCTION.

make_cube(float *vector, float *normal, float *texture, int left, int right, int top, int bottom, int front, int back,
          float x, float y, float z, float n, int w) {
    float *vector_data = vector;
    float *normal_data = normal;
    float *texture_data = texture;
    float s = 1.0f / 16; // 0.0625f
    float a = 0;
    float b = 1.0f / 16; // 0.0625
//    float b = s; // 0.0625
    float du, dv;
    float ou, ov;
    w--;
    ou = (w % 16) * s; // w % 16 * 0.0625 width offset... the position of the texture times the width percentage of textures texture_pos * (1 / 16)
    ov = (w / 16 * 3) * s; // height offset

    // 108 data points, or 36 (3D) vector_data for the cube
    // 108 data points, or 36 (3D) normal_data for the cube
    // 72 data points, or  36 (2D) texture_data for the cube
    if (left) {
        du = ou; dv = ov + s;
        *(vector_data++) = x - n; *(vector_data++) = y - n; *(vector_data++) = z - n;
        *(vector_data++) = x - n; *(vector_data++) = y + n; *(vector_data++) = z + n;
        *(vector_data++) = x - n; *(vector_data++) = y + n; *(vector_data++) = z - n;
        *(vector_data++) = x - n; *(vector_data++) = y - n; *(vector_data++) = z - n;
        *(vector_data++) = x - n; *(vector_data++) = y - n; *(vector_data++) = z + n;
        *(vector_data++) = x - n; *(vector_data++) = y + n; *(vector_data++) = z + n;
        *(normal_data++) = -1; *(normal_data++) = 0; *(normal_data++) = 0;
        *(normal_data++) = -1; *(normal_data++) = 0; *(normal_data++) = 0;
        *(normal_data++) = -1; *(normal_data++) = 0; *(normal_data++) = 0;
        *(normal_data++) = -1; *(normal_data++) = 0; *(normal_data++) = 0;
        *(normal_data++) = -1; *(normal_data++) = 0; *(normal_data++) = 0;
        *(normal_data++) = -1; *(normal_data++) = 0; *(normal_data++) = 0;

        // hint: z appears to be interchanged with y in pattern
        //   and x's pattern doesn't matter.
        *(texture_data++) = a + du; *(texture_data++) = a + dv;
        *(texture_data++) = b + du; *(texture_data++) = b + dv;
        *(texture_data++) = a + du; *(texture_data++) = b + dv;
        *(texture_data++) = a + du; *(texture_data++) = a + dv;
        *(texture_data++) = b + du; *(texture_data++) = a + dv;
        *(texture_data++) = b + du; *(texture_data++) = b + dv;
    }
    if (right) {
        du = ou; dv = ov + s;
        *(vector_data++) = x + n; *(vector_data++) = y - n; *(vector_data++) = z - n;
        *(vector_data++) = x + n; *(vector_data++) = y + n; *(vector_data++) = z + n;
        *(vector_data++) = x + n; *(vector_data++) = y - n; *(vector_data++) = z + n;
        *(vector_data++) = x + n; *(vector_data++) = y - n; *(vector_data++) = z - n;
        *(vector_data++) = x + n; *(vector_data++) = y + n; *(vector_data++) = z - n;
        *(vector_data++) = x + n; *(vector_data++) = y + n; *(vector_data++) = z + n;
        *(normal_data++) = 1; *(normal_data++) = 0; *(normal_data++) = 0;
        *(normal_data++) = 1; *(normal_data++) = 0; *(normal_data++) = 0;
        *(normal_data++) = 1; *(normal_data++) = 0; *(normal_data++) = 0;
        *(normal_data++) = 1; *(normal_data++) = 0; *(normal_data++) = 0;
        *(normal_data++) = 1; *(normal_data++) = 0; *(normal_data++) = 0;
        *(normal_data++) = 1; *(normal_data++) = 0; *(normal_data++) = 0;

        // hint: z appears to be interchanged with y in pattern
        //   and x's pattern doesn't matter.
        *(texture_data++) = b + du; *(texture_data++) = a + dv;
        *(texture_data++) = a + du; *(texture_data++) = b + dv;
        *(texture_data++) = a + du; *(texture_data++) = a + dv;
        *(texture_data++) = b + du; *(texture_data++) = a + dv;
        *(texture_data++) = b + du; *(texture_data++) = b + dv;
        *(texture_data++) = a + du; *(texture_data++) = b + dv;
    }
    if (top) {
        du = ou; dv = ov + s + s;
        *(vector_data++) = x - n; *(vector_data++) = y + n; *(vector_data++) = z - n;
        *(vector_data++) = x - n; *(vector_data++) = y + n; *(vector_data++) = z + n;
        *(vector_data++) = x + n; *(vector_data++) = y + n; *(vector_data++) = z + n;
        *(vector_data++) = x - n; *(vector_data++) = y + n; *(vector_data++) = z - n;
        *(vector_data++) = x + n; *(vector_data++) = y + n; *(vector_data++) = z + n;
        *(vector_data++) = x + n; *(vector_data++) = y + n; *(vector_data++) = z - n;
        *(normal_data++) = 0; *(normal_data++) = 1; *(normal_data++) = 0;
        *(normal_data++) = 0; *(normal_data++) = 1; *(normal_data++) = 0;
        *(normal_data++) = 0; *(normal_data++) = 1; *(normal_data++) = 0;
        *(normal_data++) = 0; *(normal_data++) = 1; *(normal_data++) = 0;
        *(normal_data++) = 0; *(normal_data++) = 1; *(normal_data++) = 0;
        *(normal_data++) = 0; *(normal_data++) = 1; *(normal_data++) = 0;

        // hint: the pattern seems to be with x and z, (not y) because it remains the same
        *(texture_data++) = a + du; *(texture_data++) = b + dv;
        *(texture_data++) = a + du; *(texture_data++) = a + dv;
        *(texture_data++) = b + du; *(texture_data++) = a + dv;
        *(texture_data++) = a + du; *(texture_data++) = b + dv;
        *(texture_data++) = b + du; *(texture_data++) = a + dv;
        *(texture_data++) = b + du; *(texture_data++) = b + dv;
    }
    if (bottom) {
        du = ou; dv = ov + 0;
        *(vector_data++) = x - n; *(vector_data++) = y - n; *(vector_data++) = z - n;
        *(vector_data++) = x + n; *(vector_data++) = y - n; *(vector_data++) = z - n;
        *(vector_data++) = x + n; *(vector_data++) = y - n; *(vector_data++) = z + n;
        *(vector_data++) = x - n; *(vector_data++) = y - n; *(vector_data++) = z - n;
        *(vector_data++) = x + n; *(vector_data++) = y - n; *(vector_data++) = z + n;
        *(vector_data++) = x - n; *(vector_data++) = y - n; *(vector_data++) = z + n;
        *(normal_data++) = 0; *(normal_data++) = -1; *(normal_data++) = 0;
        *(normal_data++) = 0; *(normal_data++) = -1; *(normal_data++) = 0;
        *(normal_data++) = 0; *(normal_data++) = -1; *(normal_data++) = 0;
        *(normal_data++) = 0; *(normal_data++) = -1; *(normal_data++) = 0;
        *(normal_data++) = 0; *(normal_data++) = -1; *(normal_data++) = 0;
        *(normal_data++) = 0; *(normal_data++) = -1; *(normal_data++) = 0;

        // hint: the pattern seems to fit with x, z and negate y
        *(texture_data++) = a + du; *(texture_data++) = a + dv;
        *(texture_data++) = b + du; *(texture_data++) = a + dv;
        *(texture_data++) = b + du; *(texture_data++) = b + dv;
        *(texture_data++) = a + du; *(texture_data++) = a + dv;
        *(texture_data++) = b + du; *(texture_data++) = b + dv;
        *(texture_data++) = a + du; *(texture_data++) = b + dv;
    }
    if (front) {
        du = ou; dv = ov + s;
        *(vector_data++) = x - n; *(vector_data++) = y - n; *(vector_data++) = z + n;
        *(vector_data++) = x + n; *(vector_data++) = y - n; *(vector_data++) = z + n;
        *(vector_data++) = x + n; *(vector_data++) = y + n; *(vector_data++) = z + n;
        *(vector_data++) = x - n; *(vector_data++) = y - n; *(vector_data++) = z + n;
        *(vector_data++) = x + n; *(vector_data++) = y + n; *(vector_data++) = z + n;
        *(vector_data++) = x - n; *(vector_data++) = y + n; *(vector_data++) = z + n;
        *(normal_data++) = 0; *(normal_data++) = 0; *(normal_data++) = -1;
        *(normal_data++) = 0; *(normal_data++) = 0; *(normal_data++) = -1;
        *(normal_data++) = 0; *(normal_data++) = 0; *(normal_data++) = -1;
        *(normal_data++) = 0; *(normal_data++) = 0; *(normal_data++) = -1;
        *(normal_data++) = 0; *(normal_data++) = 0; *(normal_data++) = -1;
        *(normal_data++) = 0; *(normal_data++) = 0; *(normal_data++) = -1;

        // hint: u seems to link with x, and v with y (but the opposite)
        *(texture_data++) = b + du; *(texture_data++) = a + dv;
        *(texture_data++) = a + du; *(texture_data++) = a + dv;
        *(texture_data++) = a + du; *(texture_data++) = b + dv;
        *(texture_data++) = b + du; *(texture_data++) = a + dv;
        *(texture_data++) = a + du; *(texture_data++) = b + dv;
        *(texture_data++) = b + du; *(texture_data++) = b + dv;
    }
    if (back) {
        du = ou; dv = ov + s;
        *(vector_data++) = x - n; *(vector_data++) = y - n; *(vector_data++) = z - n;
        *(vector_data++) = x + n; *(vector_data++) = y + n; *(vector_data++) = z - n;
        *(vector_data++) = x + n; *(vector_data++) = y - n; *(vector_data++) = z - n;
        *(vector_data++) = x - n; *(vector_data++) = y - n; *(vector_data++) = z - n;
        *(vector_data++) = x - n; *(vector_data++) = y + n; *(vector_data++) = z - n;
        *(vector_data++) = x + n; *(vector_data++) = y + n; *(vector_data++) = z - n;
        *(normal_data++) = 0; *(normal_data++) = 0; *(normal_data++) = 1;
        *(normal_data++) = 0; *(normal_data++) = 0; *(normal_data++) = 1;
        *(normal_data++) = 0; *(normal_data++) = 0; *(normal_data++) = 1;
        *(normal_data++) = 0; *(normal_data++) = 0; *(normal_data++) = 1;
        *(normal_data++) = 0; *(normal_data++) = 0; *(normal_data++) = 1;
        *(normal_data++) = 0; *(normal_data++) = 0; *(normal_data++) = 1;

        // hint: the texture u seems to link with x, and v with y
        *(texture_data++) = a + du; *(texture_data++) = a + dv;
        *(texture_data++) = b + du; *(texture_data++) = b + dv;
        *(texture_data++) = b + du; *(texture_data++) = a + dv;
        *(texture_data++) = a + du; *(texture_data++) = a + dv;
        *(texture_data++) = a + du; *(texture_data++) = b + dv;
        *(texture_data++) = b + du; *(texture_data++) = b + dv;


        *(texture_data++) = b + du; *(texture_data++) = a + dv;
        *(texture_data++) = a + du; *(texture_data++) = a + dv;
        *(texture_data++) = a + du; *(texture_data++) = b + dv;
        *(texture_data++) = b + du; *(texture_data++) = a + dv;
        *(texture_data++) = a + du; *(texture_data++) = b + dv;
        *(texture_data++) = b + du; *(texture_data++) = b + dv;
    }
}

When I got the results for the left, from the program itself, this is what I got for the vertices:

x = 0.500000, y = 0.500000, z = 0.500000
x = 0.500000, y = 1.500000, z = 1.500000
x = 0.500000, y = 1.500000, z = 0.500000
x = 0.500000, y = 0.500000, z = 0.500000
x = 0.500000, y = 0.500000, z = 1.500000
x = 0.500000, y = 1.500000, z = 1.500000

And the texture data... (remember that because the left cube, is the second one up (v = 0.0625) and the x starting position is u = 0), so the origin in UV is 0, 0.0625.

x = 0.000000, y = 0.062500
x = 0.062500, y = 0.125000
x = 0.000000, y = 0.125000
x = 0.000000, y = 0.062500
x = 0.062500, y = 0.062500
x = 0.062500, y = 0.125000

But my calculations by hand, when I applied the coordinates to the UV Map Positioning, I actually got... The way I did this was every time I got a

x = 0.000000, y = 0.062500
x = 0.062500, y = 0.125000
x = 0.062500, y = 0.062500
x = 0.000000, y = 0.062500
x = 0.000000, y = 0.125000
x = 0.062500, y = 0.125000

I don't think it's by accident that I got the ALMOST correct values. The cubes also do not have identically rendered textures. Some of them are reversed, to make the texture seem organically connecting one to another. Here are some screenshots of the 4 sides of the cube when rotating around it, counter-clockwise.

Front Front Left Left Back Back Right Right

Does anyone have any idea how this works? Can someone point me in the direction? I feel like I've been looking at this so long I can't see the forest through the trees. I need to understand how these UV Coordinates are mapped to each of these specific sides of this cube.

If anyone else is interested, here is the rest of the output of the vertices and texture data (respectively in the same block of code).

Right

x = 1.500000, y = 0.500000, z = 0.500000
x = 1.500000, y = 1.500000, z = 1.500000
x = 1.500000, y = 0.500000, z = 1.500000
x = 1.500000, y = 0.500000, z = 0.500000
x = 1.500000, y = 1.500000, z = 0.500000
x = 1.500000, y = 1.500000, z = 1.500000

x = 0.062500, y = 0.062500
x = 0.000000, y = 0.125000
x = 0.000000, y = 0.062500
x = 0.062500, y = 0.062500
x = 0.062500, y = 0.125000
x = 0.000000, y = 0.125000

Top

x = 0.500000, y = 1.500000, z = 0.500000
x = 0.500000, y = 1.500000, z = 1.500000
x = 1.500000, y = 1.500000, z = 1.500000
x = 0.500000, y = 1.500000, z = 0.500000
x = 1.500000, y = 1.500000, z = 1.500000
x = 1.500000, y = 1.500000, z = 0.500000


x = 0.000000, y = 0.187500
x = 0.000000, y = 0.125000
x = 0.062500, y = 0.125000
x = 0.000000, y = 0.187500
x = 0.062500, y = 0.125000
x = 0.062500, y = 0.187500

Bottom

x = 0.500000, y = 0.500000, z = 0.500000
x = 1.500000, y = 0.500000, z = 0.500000
x = 1.500000, y = 0.500000, z = 1.500000
x = 0.500000, y = 0.500000, z = 0.500000
x = 1.500000, y = 0.500000, z = 1.500000
x = 0.500000, y = 0.500000, z = 1.500000


x = 0.000000, y = 0.000000
x = 0.062500, y = 0.000000
x = 0.062500, y = 0.062500
x = 0.000000, y = 0.000000
x = 0.062500, y = 0.062500
x = 0.000000, y = 0.062500

Front

x = 0.500000, y = 0.500000, z = 1.500000
x = 1.500000, y = 0.500000, z = 1.500000
x = 1.500000, y = 1.500000, z = 1.500000
x = 0.500000, y = 0.500000, z = 1.500000
x = 1.500000, y = 1.500000, z = 1.500000
x = 0.500000, y = 1.500000, z = 1.500000


x = 0.062500, y = 0.062500
x = 0.000000, y = 0.062500
x = 0.000000, y = 0.125000
x = 0.062500, y = 0.062500
x = 0.000000, y = 0.125000
x = 0.062500, y = 0.125000

Back

x = 0.500000, y = 0.500000, z = 0.500000
x = 1.500000, y = 1.500000, z = 0.500000
x = 1.500000, y = 0.500000, z = 0.500000
x = 0.500000, y = 0.500000, z = 0.500000
x = 0.500000, y = 1.500000, z = 0.500000
x = 1.500000, y = 1.500000, z = 0.500000


x = 0.000000, y = 0.062500
x = 0.062500, y = 0.125000
x = 0.062500, y = 0.062500
x = 0.000000, y = 0.062500
x = 0.000000, y = 0.125000
x = 0.062500, y = 0.125000

I'd be so grateful if anyone could figure this out. Simply to put my mind at peace. I've been called a Jack-Rustle Terrier -- unable to let go.

I'd love to see the simple thing I'm missing! If you've gone this far, THANK YOU!

Here are SOME of the references I've used to try to figure this out.

http://www.opengl-tutorial.org/beginners-tutorials/tutorial-5-a-textured-cube/ https://gamedev.stackexchange.com/questions/152991/how-can-i-calculate-normals-using-a-vertex-and-index-buffer https://gamedev.stackexchange.com/questions/172352/finding-texture-coordinates-for-plane https://gamedev.net/forums/topic/626790-uv-mapping-where-do-the-u-v-coordinates-correspond-to-on-a-square-texture/4951976/ https://github.com/Hopson97/MineCraft-One-Week-Challenge/blob/master/Source/Texture/TextureAtlas.cpp https://en.wikipedia.org/wiki/UV_mapping https://gamedev.stackexchange.com/questions/25057/help-a-2d-image-onto-a-3d-cube OpenGL 4 - UV Coordinates for Triangle Strip Cube https://conceptartempire.com/uv-mapping-unwrapping/ Beginning OpenGL Game Programming by Dave Astle page 151 - 158 (Texture Mapping)

luminol
  • 407
  • 4
  • 15
  • see [Texturing a cube with different images using OpenGL](https://stackoverflow.com/a/65276638/2521214) – Spektre Jun 20 '21 at 07:07
  • 1
    I really fail to see the issue. WIth the data for the left side as an example, it is easy to see that your coordinates are just flipped w.r.t. what the original code does. However, the "correct" orientation is just an arbitrary design-time decison, it just has to match the actual data in the texture. Just draw the texture space mappings on paper. If you want to emulate the effects of the original code, you also have to emulate the correct orientation, but I somehow fail to see the issue here. You already know the original algorithm, and the output it produces makes total sense. – derhass Jun 20 '21 at 14:40
  • So basically, are you saying that the person just chose an arbitrary orientation, and I've just confused myself in thinking that I am wrong because I looked at it from a different perspective? I've obviously been looking at this for too long. – luminol Jun 20 '21 at 17:32

0 Answers0