Super Easy to Use 3D Engine (Su3De) – Binary File Format Part 4 – Texture Maps

NOTE: There were a few minor errors in a couple of the previous posts.  I just wasn’t consistent in explaining the polygonal mesh with regards to vertices.  I have updated the posts and corrected the errors.  The problems were in the area of describing the vertices per triangle relationship.  So, to recap, there are three vertices per triangle and each vertex is comprised of an x, y and z coordinate.  Each coordinate is comprised of a float which requires four bytes.  So, each triangle of the polygonal mesh requires 36 bytes to describe.

Onward…

OK…finally we get to texture maps in the Su3De binary file format.  Texture maps are fairly straightforward to implement, however, there are lots of details that we have to deal with.  In no real order these details include:

  • Texture map color depth
  • Texture map compression
  • Multi-texturing
  • Texture animation
  • Live video as a texture map
  • Recorded or rendered video as a texture map
  • Texture map dimensions
  • Procedural textures

Something I want to also discuss is “terminology”…it can get muddy sometimes so I want to define the terms the way I want to use them with regards to Su3De.  This might result in a street fight…but that’s OK…I fight dirty.

Street fight over 3D terminology

A “texture map” refers to the coordinates (really just relative offsets into the texture bitmap itself) used to map the texture onto the 3D model’s polygonal mesh.

The term “texture” refers to the actual bitmap bits that are being mapped.

UV coordinates” are the values stored in the texture map (one for each vertex of each triangle and corresponding to each triangle) that are the relative offsets into the texture – normalized as values between 0 and 1.  We use the “U and V” to differentiate from X, Y, and Z that are used for mesh coordinates.

I have written a couple of articles / tech papers that you might find interesting if you are new to 3D and texture mapping.  They can be found here:

// Begin Soapbox

Now…at this point, I am going to climb up on my soap box and say a few things. Some of you have been kind enough to express your opinion with regards to the feature set of Su3De.  I want to remind you of the fact that this is to be a “SUPER EASY TO USE 3D ENGINE”.  This means that, at least in the first versions, there aren’t going to be a lot of powerful fancy features.  Yes, I know many of you are very smart…you have told me so…and that in order to be useful Su3De needs this feature or that.  Let’s get something straight…initially Su3De is being developed to satisfy a specific need that I have for a small feature set in a compact UI environment…nothing more.  I am making it “open source” so that you can freely add the features that you want and hopefully many of these new features will find their way into an official Su3De release in the future.  Yes, I know about shaders.  I know about bump / normal mapping.  I know about object physics, collision detection and reflection mapping and yes I know about open source 3D engines like Irrlicht and Ogre (they are awesome).  I appreciate the comments and PLEASE keep them coming…but bear in mind that the goal is a simple product and the feature set is going to be an extremely small subset of overall 3D functionality to achieve a specific goal.  BTW I REALLY liked the suggestion of converting FreeType text into a 3D object that can be used by Su3De…either statically using some tool -or- doing it at run time “on the fly”.  The “on the fly” method might add weight to Su3De that we don’t have the luxury of, however, this could be a compile option.

//End Soapbox

SU3 File format

Putting the texture map and the textures in the actual 3D model binary format along with the polygonal mesh has been a difficult decision.  Does it really belong there?  What about all the things outlined above – texture animation, live streaming video, recorded rendered video, various formats, etc.?  There are lots of good reasons to make the texture map and textures separate files instead of trying to cram everything into a single binary or a single “entity”.  With all this said, I really want a single file (analogous to a PNG as explained before) that contains the entire 3D object.  The “chunk” architecture of the .SU3 file format easily allows us to reference external files should we choose to implement some “advanced” features at a later time. So the decision is that we will have a texture map and a texture in the SU3 file format along with the mesh data (and other required chunks).

Texture Map

Ok, so remember our friend the cube?  Our cube has six sides and twelve faces because each side is made up of two triangles. This makes for thirty six vertices.  As a reminder the vertices look like this:

[sourcecode language="c"]
#pragma pack(1)
/* structure of floating point xyz coordinates */
typedef struct{
float x;
float y;
float z;
}SU3_VTX3F;

static SU3_VTX3F FlexVerts[] = {
{-10.000000f, 0.000000f, 10.000000f}, //triangle 1
{-10.000000f, 0.000000f, -10.000000f}, //triangle 1
{10.000000f, 0.000000f, -10.000000f}, //triangle 1
{10.000000f, 0.000000f, -10.000000f}, //triangle 2
{10.000000f, 0.000000f, 10.000000f}, //triangle 2
{-10.000000f, 0.000000f, 10.000000f}, //triangle 2
{-10.000000f, 20.000000f, 10.000000f}, //triangle 3
{10.000000f, 20.000000f, 10.000000f}, //triangle 3
{10.000000f, 20.000000f, -10.000000f}, //triangle 3
{10.000000f, 20.000000f, -10.000000f}, //triangle 4
{-10.000000f, 20.000000f, -10.000000f}, //triangle 4
{-10.000000f, 20.000000f, 10.000000f}, //triangle 4
{-10.000000f, 0.000000f, 10.000000f}, //triangle 5
{10.000000f, 0.000000f, 10.000000f}, //triangle 5
{10.000000f, 20.000000f, 10.000000f}, //triangle 5
{10.000000f, 20.000000f, 10.000000f}, //triangle 6
{-10.000000f, 20.000000f, 10.000000f}, //triangle 6
{-10.000000f, 0.000000f, 10.000000f}, //triangle 6
{10.000000f, 0.000000f, 10.000000f}, //triangle 7
{10.000000f, 0.000000f, -10.000000f}, //triangle 7
{10.000000f, 20.000000f, -10.000000f}, //triangle 7
{10.000000f, 20.000000f, -10.000000f}, //triangle 8
{10.000000f, 20.000000f, 10.000000f}, //triangle 8
{10.000000f, 0.000000f, 10.000000f}, //triangle 8
{10.000000f, 0.000000f, -10.000000f}, //triangle 9
{-10.000000f, 0.000000f, -10.000000f}, //triangle 9
{-10.000000f, 20.000000f, -10.000000f}, //triangle 9
{-10.000000f, 20.000000f, -10.000000f}, //triangle 10
{10.000000f, 20.000000f, -10.000000f}, //triangle 10
{10.000000f, 0.000000f, -10.000000f}, //triangle 10
{-10.000000f, 0.000000f, -10.000000f}, //triangle 11
{-10.000000f, 0.000000f, 10.000000f}, //triangle 11
{-10.000000f, 20.000000f, 10.000000f}, //triangle 11
{-10.000000f, 20.000000f, 10.000000f}, //triangle 12
{-10.000000f, 20.000000f, -10.000000f}, //triangle 12
{-10.000000f, 0.000000f, -10.000000f}, //triangle 12
};
#pragma pack()
[/sourcecode]

The wire mesh for the cube looks like the image below:

Cube with 36 vertices

As you can see, and as you know from previous posts, there are three vertices per triangle, so our texture map is also going to have three entries per triangle with each entry representing a UV texture map offset. Below is the code that describes the texture map for the cube.

[sourcecode language="c"]
#pragma pack(1)
static SU3_TEX2F FlexTexMap[36] = {
{0.001200f, 0.665600f}, //triangle 1
{0.001200f, 0.335200f}, //triangle 1
{0.331900f, 0.335200f}, //triangle 1
{0.331900f, 0.335200f}, //triangle 2
{0.331900f, 0.665600f}, //triangle 2
{0.001200f, 0.665600f}, //triangle 2
{0.664100f, 0.333200f}, //triangle 3
{0.333700f, 0.333200f}, //triangle 3
{0.333700f, 0.000800f}, //triangle 3
{0.333700f, 0.000800f}, //triangle 4
{0.664100f, 0.000800f}, //triangle 4
{0.664100f, 0.333200f}, //triangle 4
{0.999300f, 0.333400f}, //triangle 5
{0.665300f, 0.333400f}, //triangle 5
{0.665300f, 0.000800f}, //triangle 5
{0.665300f, 0.000800f}, //triangle 6
{0.999300f, 0.000800f}, //triangle 6
{0.999300f, 0.333400f}, //triangle 6
{1.000000f, 0.666000f}, //triangle 7
{0.665600f, 0.666000f}, //triangle 7
{0.665600f, 0.334200f}, //triangle 7
{0.665600f, 0.334200f}, //triangle 8
{1.000000f, 0.334200f}, //triangle 8
{1.000000f, 0.666000f}, //triangle 8
{0.663000f, 0.665300f}, //triangle 9
{0.334800f, 0.665300f}, //triangle 9
{0.334800f, 0.333800f}, //triangle 9
{0.334800f, 0.333800f}, //triangle 10
{0.663000f, 0.333800f}, //triangle 10
{0.663000f, 0.665300f}, //triangle 10
{0.331900f, 0.332100f}, //triangle 11
{0.000400f, 0.332100f}, //triangle 11
{0.000400f, 0.002000f}, //triangle 11
{0.000400f, 0.002000f}, //triangle 12
{0.331900f, 0.002000f}, //triangle 12
{0.331900f, 0.332100f}, //triangle 12
};
#pragma pack()
[/sourcecode]

The actual texture map for the cube we are describing here looks like the following:


No, there isn’t a problem with the bitmap. The top 1/3 is simply not going to be used. Our cube has six sides, therefore we are going to only use the bottom 2/3rds of our texture map area. The benefit of using a cube is that it is very easy to see each “side”.

With a more complex model, the triangle mapping obviously isn’t so straightforward. Speaking of triangle mapping, here is the same texture with the triangle mapping visually applied. This allows you to clearly see all twelve triangles and how they map to the texture.  OK, so the next thing we have to address is how we are going to add the texture map into the .SU3 binary format.

SU3 – Texture Map Chunk

The texture map chunk is defined simply with the chunk length as the number of bytes required to describe the UV coordinates and the chunk type defined as “MTXM”.  To add the texture map to a SU3 file from part 3′s example, we add the following code:

[sourcecode language="c"]
#define FOURCC_MTXM MAKEFOURCC(‘M’,’T’,’X’,’M’)
#pragma pack(1)
typedef struct{
float u;
float v;
}SU3_TEX2F;
static SU3_TEX2F FlexTexMap[36] = {
{0.001200f, 0.665600f}, //triangle 1
{0.001200f, 0.335200f}, //triangle 1
{0.331900f, 0.335200f}, //triangle 1
{0.331900f, 0.335200f}, //triangle 2
{0.331900f, 0.665600f}, //triangle 2
{0.001200f, 0.665600f}, //triangle 2
{0.664100f, 0.333200f}, //triangle 3
{0.333700f, 0.333200f}, //triangle 3
{0.333700f, 0.000800f}, //triangle 3
{0.333700f, 0.000800f}, //triangle 4
{0.664100f, 0.000800f}, //triangle 4
{0.664100f, 0.333200f}, //triangle 4
{0.999300f, 0.333400f}, //triangle 5
{0.665300f, 0.333400f}, //triangle 5
{0.665300f, 0.000800f}, //triangle 5
{0.665300f, 0.000800f}, //triangle 6
{0.999300f, 0.000800f}, //triangle 6
{0.999300f, 0.333400f}, //triangle 6
{1.000000f, 0.666000f}, //triangle 7
{0.665600f, 0.666000f}, //triangle 7
{0.665600f, 0.334200f}, //triangle 7
{0.665600f, 0.334200f}, //triangle 8
{1.000000f, 0.334200f}, //triangle 8
{1.000000f, 0.666000f}, //triangle 8
{0.663000f, 0.665300f}, //triangle 9
{0.334800f, 0.665300f}, //triangle 9
{0.334800f, 0.333800f}, //triangle 9
{0.334800f, 0.333800f}, //triangle 10
{0.663000f, 0.333800f}, //triangle 10
{0.663000f, 0.665300f}, //triangle 10
{0.331900f, 0.332100f}, //triangle 11
{0.000400f, 0.332100f}, //triangle 11
{0.000400f, 0.002000f}, //triangle 11
{0.000400f, 0.002000f}, //triangle 12
{0.331900f, 0.002000f}, //triangle 12
{0.331900f, 0.332100f}, //triangle 12
};

#pragma pack()

//build and write the texture map chunk
chk.chunkLength = sizeof(FlexTexMap);
chk.chunkType = FOURCC_MTXM;
fwrite(&chk, sizeof(SU3_CHUNK), 1, fp);
fwrite(&FlexTexMap, sizeof(FlexTexMap), 1, fp);
crc = 0×12345678; //bogus placeholder value
fwrite(&crc, sizeof(crc), 1, fp);

[/sourcecode]

After the texture is applied to the polygonal mesh we end up with the following rendered image:

Now…of course we haven’t yet written the actual texture data into the SU3 file, we have only written the texture map.  The texture itself is another complex topic and we will address that in the next post.

Housekeeping

So its time for a little bit of housekeeping for the project.  I have secured the domain name of Su3De.org for the project.  Su3De.org will be the official project page. For the source code we will be using GitHub for this project. The GitHub project site is: https://github.com/ricktewell/Su3De.

There is already quite a bit more code developed for this project then I have written about in the blogs.  It is likely that the GitHub repository will stay far ahead of the blog so if you want to “jump forward” a bit – then GitHub will be the place to go.  For example, the 3D OBJ to SU3 converter is starting to work at this point and I will be adding this source to GitHub soon.

So that’s it for now…see you in Su3De.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>