3D Texture Mapping

Sixth part of The 3D Coding Blackhole tutorial series

Home at http://3Dblackhole.base.org

NOTE: Some part of functions and some members of structures are not
explained here, but we'll discuss them in other tutorials of the series.
They're usually displayed in totality for the sake of similarity between
the different tutorials and with the complete 3D engine.
---------------------------------------------------------------------------

   * Overview
   * The Magic Numbers
   * Perspective Correct Texture Mapping

Overview

The first things you must think about when doing texture mapping, is having
an array of textures and initializing 3D texture coordinates. The textures
will be stored in:

#define MAXTEXTURES 16
bitmap_t Textures[MAXTEXTURES];

We will allocate and load them from PCX files. I chosed to make all of them
64x64. We will use the texture coordinates of the polygon_t structure:

vertex_t P,M,N;

We will initialize them in a function that we will called after creating
the polygons. P is the origin of the texture, M the horizontal end of the
texture, and N the vertical end.

void TEX_Setup(Polygon_t * Polygon, Object_t *Object)
{
   Polygon->P.Local=P3D(Vert(1).Local.x,
                        Vert(1).Local.y,
                        Vert(1).Local.z);
   Polygon->M.Local=P3D(Vert(0).Local.x,
                        Vert(0).Local.y,
                        Vert(0).Local.z);
   Polygon->N.Local=P3D(Vert(2).Local.x,
                        Vert(2).Local.y,
                        Vert(2).Local.z);
}

We need to transform them like any other vertices of the object, so we will
create a world transforming and an align transforming function right now:

void TR_Object(Object_t *Object, float matrix[4][4])
{
   int v,p;
   for(v=0; vVertexCount; v++)
      VEC_MultMatrix(&Object->Vertex[v].Local,matrix,&Object->Vertex[v].World);
   for(p=0; pPolygonCount; p++)
   {
      VEC_MultMatrix(&Object->Polygon[p].P.Local,matrix,&Object->Polygon[p].P.World);
      VEC_MultMatrix(&Object->Polygon[p].M.Local,matrix,&Object->Polygon[p].M.World);
      VEC_MultMatrix(&Object->Polygon[p].N.Local,matrix,&Object->Polygon[p].N.World);
   }
}
void TR_AlignObject(Object_t *Object, float matrix[4][4])
{
   int v,p;
   for(v=0; vVertexCount; v++)
      VEC_MultMatrix(&Object->Vertex[v].World,matrix,&Object->Vertex[v].Aligned);
   for(p=0; pPolygonCount; p++)
   {
      VEC_MultMatrix(&Object->Polygon[p].P.World,matrix,&Object->Polygon[p].P.Aligned);
      VEC_MultMatrix(&Object->Polygon[p].M.World,matrix,&Object->Polygon[p].M.Aligned);
      VEC_MultMatrix(&Object->Polygon[p].N.World,matrix,&Object->Polygon[p].N.Aligned);
   }
}

The Magic Numbers

Now that we have the transformed texture coordinates, our objective is to
find what is the horizontal and the vertical coordinates of the pixel in
the texture bitmap that should be drawn on the screen. These texture
coordinates will be called u and v. And the equations that will give us
these coordinates are:
                          u = a * TEXTURE_SIZE / c
and
                          v = b * TEXTURE_SIZE / c
Now these magic coordinates have equations for themselves:
                            a = Ox + Vx j + Hx i
                            b = Oy + Vy j + Hy i
                            c = Oz + Vz j + Hz i
These O, H, V numbers are the magic numbers. They are computed from the
texture coordinates in the following way:
                              Ox = NxPy - NyPx
                              Hx = NyPz - NzPy
                              Vx = NzPx - NxPz
                              Oy = MxPy - MyPx
                              Hy = MyPz - MzPy
                              Vy = MzPx - MxPz
                              Oz = MyNx - MxNy
                              Hz = MzNy - MyNz
                              Vz = MxNz - MzNx

I don't really know why it works, but it does! It seems to be some strange
cross product... Anyway, now that you have the equations, let's implement
the code!

Perspective Correct Texture Mapping

The computation of the O, H, V numbers requires some modifications, so we
will add the following to ENG3D_SetPlane:

   //Used to fix errors that happen when the numbers get too big
   #define FIX_FACTOR 1/640

   //Initialize texture vectors
   P=Polygon->P.Aligned;
   M=VEC_Difference(Polygon->M.Aligned,Polygon->P.Aligned);
   N=VEC_Difference(Polygon->N.Aligned,Polygon->P.Aligned);

   P.x*=Focal_Distance;
   P.y*=Focal_Distance;

   M.x*=Focal_Distance;
   M.y*=Focal_Distance;

   N.x*=Focal_Distance;
   N.y*=Focal_Distance;

And here is the implementation of VEC_Difference (Not extremely
complex...):

_3D VEC_Difference(_3D Vector1, _3D Vector2)
{
    return
P3D(Vector1.x-Vector2.x,Vector1.y-Vector2.y,Vector1.z-Vector2.z);
}

Then we can compute them.

_3D O, H, V;

In ENG3D_SetPlane:

   H.x=(N.y*P.z-N.z*P.y)*FIX_FACTOR;
   V.x=(N.z*P.x-N.x*P.z)*FIX_FACTOR;
   O.x=(N.x*P.y-N.y*P.x)*FIX_FACTOR;

   H.z=(M.z*N.y-M.y*N.z)*FIX_FACTOR;
   V.z=(M.x*N.z-M.z*N.x)*FIX_FACTOR;
   O.z=(M.y*N.x-M.x*N.y)*FIX_FACTOR;

   H.y=(M.y*P.z-M.z*P.y)*FIX_FACTOR;
   V.y=(M.z*P.x-M.x*P.z)*FIX_FACTOR;
   O.y=(M.x*P.y-M.y*P.x)*FIX_FACTOR;

Now it's time to change our VID_HLine for a TEX_HLine so that it uses
texture mapping (the tough part). We must at first initialize our magic
coordinates:

   a=-((long)O.x+((long)V.x*(long)j)+((long)H.x*(long)i))*64L;
   b= ((long)O.y+((long)V.y*(long)j)+((long)H.y*(long)i))*64L;
   c= ((long)O.z+((long)V.z*(long)j)+((long)H.z*(long)i));
   long Hx,Hy,Hz;
   int u,v;
   BYTE color=0;
   BYTE *mapping=Textures[texture].Picture;

Then we multiply H.x and H.y by 64 so we don't have to do it for every
pixel. We use long variables instead of floats too:

   Hx=H.x*-64;
   Hy=H.y*64;
   Hz=H.z;

Then for each pixel, change the last parameter for texture and instead of
plotting the old one do:

         if(c)
         {
            u=a/c;
            v=b/c;
            color=mapping[((v&63)<<6)+(u&63)];
            if(color)
            {
               zbuffer[offset]=z;
               SCREENBUFFER[offset]=LightTable[light][color];
            }
         }
         a+=Hx;
         b+=Hy;
         c+=Hz;

And we've got ourselves a texture mapper!
---------------------------------------------------------------------------
E-mail me at jerstlouis@videotron.ca
Back to Jerome St-Louis's Homepage
Back to The 3D Coding BlackHole

                                                  Last Updated: 08-24-1997

Discuss this article in the forums


Date this article was posted to GameDev.net: 7/16/1999
(Note that this date does not necessarily correspond to the date the article was written)

See Also:
Texture Mapping

© 1999-2011 Gamedev.net. All rights reserved. Terms of Use Privacy Policy
Comments? Questions? Feedback? Click here!