M32 Texture Support & Compile Tools

 

Copyright info

All code in this tutorial is protected by the [GPL License].
All text in this tutorial is protected by the [FDL License].

 

Tutorial by: |nc

Mailto: mmilley@yossman.net

If you're wondering what the .m32 texture format is, its basically the .wal format upgraded to 32bit with a few extras.

Before we start, this does not add FULL .m32 support. It only adds support to the OpenGL renderer, qbsp3, and qrad3. The engine itself will still try to download the .wal for a texture even if the GL renderer already loaded a .m32 for it, so the allow_download cvar should be set to "0" unless you want to see a bunch of texture download failures. Don't bother looking for detail or damage textures, because this tutorial doesn't go into that. Also, the qrad tool Raven uses must have been compiled with different light falloff values because the sample map from their SDK (the first level of SOF) was WAY too bright when lit with Quake2's qrad3. If you can't find the Quake2 tools source, its at "ftp://ftp.idsoftware.com/idstuff/quake2/source/old/q2source_12_11.zip". This code works with the 3.19 and 3.21 source.

Quake2:


Open the Quake2 source and set ref_gl as the active project. Open the "Header Files" folder in the File View and open qfiles.h

qfiles.h:

After:

 
                   
} miptex_t;
 
     

Add:

 
 
// |nc - .M32 Support Begin:
/*
==============================================================================
.M32 texture file format
==============================================================================
*/
 
typedef struct miptex32_s
{
          int                version;
          char               name[128];
          char               altname[128];                         // texture substitution
          char               animname[128];                        // next frame in animation chain
          char               damagename[128];             // image that should be shown when damaged
          unsigned  width[16], height[16];
          unsigned  offsets[16];
          int                flags;
          int                contents;
          int                value;
          float              scale_x, scale_y;
          int                mip_scale;
                             
          // detail texturing info
          char               dt_name[128];                // detailed texture name
          float              dt_scale_x, dt_scale_y;
          float              dt_u, dt_v;
          float              dt_alpha;
          int                dt_src_blend_mode, dt_dst_blend_mode;
          int                flags2;
          float              damage_health;
                             
          int                unused[18];                                     // future expansion to maintain compatibility with h2
} miptex32_t;
// |nc - .M32 Support End
 
     

gl_model.c:

In function Mod_LoadTexinfo():

Replace:

 
 
if (!out->image)
{
          ri.Con_Printf (PRINT_ALL, "Couldn't load %s\n", name);
          out->image = r_notexture;
}
 
     

With:

 
 
// |nc - .M32 Support Begin:
//                 if (!out->image)
//                 {
//                           ri.Con_Printf (PRINT_ALL, "Couldn't load %s\n", name);
//                           out->image = r_notexture;
//                 }
                   if (!out->image || out->image == r_notexture)
                   {
                             Com_sprintf (name, sizeof(name), "textures/%s.m32", in->texture);
                             out->image = GL_FindImage (name, it_wall);
                                                                   
                             if (!out->image)
                             {
                                      ri.Con_Printf (PRINT_ALL, "Couldn't load %s\n", name);
                                      out->image = r_notexture;
                             }
                   }
                   // |nc - .M32 Support End
 
     

gl_image.c:

After function GL_LoadWal():

Add:

 
                             
// |nc - .M32 Support Begin:
/*
================
GL_LoadWal32
================
*/
image_t *GL_LoadWal32 (char *name)
{
          miptex32_t         *mt;
          int                          width, height, ofs;
          image_t            *image;
                             
          ri.FS_LoadFile (name, (void **)&mt);
          if (!mt)
          {
                   ri.Con_Printf (PRINT_ALL, "GL_FindImage: can't load %s\n", name);
                   return r_notexture;
          }
                                      
          width = LittleLong (mt->width[0]);
          height = LittleLong (mt->height[0]);
          ofs = LittleLong (mt->offsets[0]);
                                      
          image = GL_LoadPic (name, (byte *)mt + ofs, width, height, it_wall, 32);
          ri.FS_FreeFile ((void *)mt);
                                      
          return image;
}
// |nc - .M32 Support End
 
     

In Function GL_FindImage():

After:

 
                   
else if (!strcmp(name+len-4, ".tga"))
{
          LoadTGA (name, &pic, &width, &height);                                             
          if (!pic)
                   return NULL; // ri.Sys_Error (ERR_DROP, "GL_FindImage: can't load %s", name);
                   image = GL_LoadPic (name, pic, width, height, type, 32);
          }
 
     

Add:

 
 
// |nc - .M32 Support Begin:
          else if (!strcmp(name+len-4, ".m32"))
          {
                   image = GL_LoadWal32 (name);
          }
// |nc - .M32 Support End
 
     


QBSP3:

qfiles.h (in the "External Dependencies" folder):

After:

 
                   
          } miptex_t;
 
     

Add:

 
                             
/*
==============================================================================
                             
.M32 texture file format
                             
==============================================================================
*/
                             
typedef struct miptex32_s
{
          int                version;
          char               name[128];
          char               altname[128];                         // texture substitution
          char               animname[128];                        // next frame in animation chain
          char               damagename[128];             // image that should be shown when damaged
          unsigned  width[16], height[16];
          unsigned  offsets[16];
          int                flags;
          int                contents;
          int                value;
          float              scale_x, scale_y;
          int                mip_scale;
                             
          // detail texturing info
          char               dt_name[128];                // detailed texture name
          float              dt_scale_x, dt_scale_y;
          float              dt_u, dt_v;
          float              dt_alpha;
          int                dt_src_blend_mode, dt_dst_blend_mode;
          int                flags2;
          float              damage_health;
                             
          int                unused[18];                                     // future expansion to maintain compatibility with h2
} miptex32_t;
 
     

textures.c:

In function FindMiptex():

After:

 
 
          miptex_t  *mt;
 
     

Add:

 
 
          // |nc - .M32 Support Begin:
          miptex32_t         *mt32;
          // |nc - .M32 Support End
 
     

After:

 
 
          if (TryLoadFile (path, (void **)&mt) != -1)
          {
                   textureref[i].value = LittleLong (mt->value);
                   textureref[i].flags = LittleLong (mt->flags);
                   textureref[i].contents = LittleLong (mt->contents);
                   strcpy (textureref[i].animname, mt->animname);
                   free (mt);
          }
 
     

Add:

 
                             
          // |nc - .M32 Support Begin:
          else
          {
                   sprintf (path, "%stextures/%s.m32", gamedir, name);
                   if (TryLoadFile (path, (void **)&mt32) != -1)
                   {
                             textureref[i].value = LittleLong (mt32->value);
                             textureref[i].flags = LittleLong (mt32->flags);
                             textureref[i].contents = LittleLong (mt32->contents);
                             strcpy (textureref[i].animname, mt32->animname);
                             free (mt32);
                   }
          }
          // |nc - .M32 Support End
 
     


QRAD3:

patches.c:

Replace the entire CalcTextureReflectivity() funcion with:

 
 
// |nc - .M32 Support Begin
void CalcTextureReflectivity (void)
{
          int                i;
          int                j, k, texels;
          int                color[3];
          int                texel;
          byte               *palette;
          char               path[1024];
          float              r, scale;
          miptex_t  *mt;
          miptex32_t         *mt32;
          int                offset;
          byte               *temp;
                             
          sprintf (path, "%spics/colormap.pcx", gamedir);
                             
          // get the game palette
          Load256Image (path, NULL, &palette, NULL, NULL);
                   
          // allways set index 0 even if no textures
          texture_reflectivity[0][0] = 0.5;
          texture_reflectivity[0][1] = 0.5;
          texture_reflectivity[0][2] = 0.5;
                             
          for (i=0 ; i < numtexinfo ; i++)
          {
                   // see if an earlier texinfo allready got the value
                   for (j=0 ; j < i ; j++)
                   {
                             if (!strcmp (texinfo[i].texture, texinfo[j].texture))
                             {
                                      VectorCopy (texture_reflectivity[j], texture_reflectivity[i]);
                                      break;
                             }
                   }
 
                   if (j != i)
                             continue;
                             
                   // load the wal file
                   sprintf (path, "%stextures/%s.wal", gamedir, texinfo[i].texture);
                   if (TryLoadFile (path, (void **)&mt) != -1)
                   {
                             texels = LittleLong(mt->width)*LittleLong(mt->height);
                             offset = LittleLong(mt->offsets[0]);
                             temp = (byte *)mt;                    
                             color[0] = color[1] = color[2] = 0;
                   
                             for (j=0 ; j<texels ; j++)
                             {
                                      texel = temp[offset + j];
                                      for (k=0 ; k < 3 ; k++)
                                                color[k] += palette[texel*3+k];
                             }
                   }
                   else
                   {
                             sprintf (path, "%stextures/%s.m32", gamedir, texinfo[i].texture);
                             if (TryLoadFile (path, (void **)&mt32) != -1)
                             {
                                      texels = LittleLong(mt32->width[0])*LittleLong(mt32->height[0]);
                                      offset = LittleLong(mt32->offsets[0]);
                                      temp = (byte *)mt32;                                  
                                      color[0] = color[1] = color[2] = 0;
                                                                   
                                      for (j=0 ; j < texels ; j++)
                                      {
                                                texel = temp[offset + j];
                                                for (k=0 ; k<3 ; k++)
                                                         color[k] += texel*4+k;
                                      }
                             }
                             else
                             {
                                      printf ("Couldn't load %s\n", path);
                                      texture_reflectivity[i][0] = 0.5;
                                      texture_reflectivity[i][1] = 0.5;
                                      texture_reflectivity[i][2] = 0.5;
                                      continue;
                             }
                   }
                             
                   for (j=0 ; j<3 ; j++)
                   {
                             r = color[j]/texels/255.0;
                             texture_reflectivity[i][j] = r;
                   }
                   // scale the reflectivity up, because the textures are
                   // so dim
                   scale = ColorNormalize (texture_reflectivity[i],
                   texture_reflectivity[i]);
                   if (scale < 0.5)
                   {
                             scale *= 2;
                             VectorScale (texture_reflectivity[i], scale, texture_reflectivity[i]);
                   }
                   #if 0
                   texture_reflectivity[i][0] = 0.5;
                   texture_reflectivity[i][1] = 0.5;
                   texture_reflectivity[i][2] = 0.5;
                   #endif
          }
}
// |nc - .M32 Support End
 
     

Now you can compile maps with .m32 textures and Quake2 will load them if theres no .wals with the same name.

 

 

Tutorial Originally found at: