Author Tomas "Tomaz" Jakobsson E-mail tompsson@hotmail Page www.quakemods.net/tomaz Oki... This tutorial will show how easy it is to get almost the whole engine to use PCX or TGA pictures to add some more colors to the game. The reason i have both TGA and PCX is cos TGA can have 32-bit. But they also can be kinda big in filesize. The disadvantage with PCX is that it can only have 8-bit (I think) but it has its own palette and has a smaller filesize than TGA. This code has been tested with a clean source base. So i cant guarantee it will work if u have an altered engine but i see no reason why it shouldnt. Most of this code is originally done by LordHavoc and altered by me Tomaz. So if it works u can thank LordHavoc... If it doesnt u can blame me ;)
Well on to the coding. // Tomaz || TGA Begin int bytesperpixel; int lhcsum; // Tomaz || TGA End
GL_LoadTexture with: // Tomaz || TGA Begin int lhcsumtable[256]; int GL_LoadTexture (char *identifier, int width, int height, byte *data, qboolean mipmap, qboolean alpha, int bytesperpixel) { qboolean noalpha; int i, p, s, lhcsum; gltexture_t *glt; // occurances. well this isn't exactly a checksum, it's better than that but // not following any standards. lhcsum = 0; s = width*height*bytesperpixel; for (i = 0;i < 256;i++) lhcsumtable[i] = i + 1; for (i = 0;i < s;i++) lhcsum += (lhcsumtable[data[i] & 255]++); // see if the texture is allready present if (identifier[0]) { for (i=0, glt=gltextures ; i < numgltextures ; i++, glt++) { if (!strcmp (identifier, glt->identifier)) { if (lhcsum != glt->lhcsum || width != glt->width || height != glt->height) { Con_DPrintf("GL_LoadTexture: cache mismatch\n"); goto GL_LoadTexture_setup; } return glt->texnum; } } } // whoever at id or threewave must've been half asleep... glt = &gltextures[numgltextures]; numgltextures++; strcpy (glt->identifier, identifier); glt->texnum = texture_extension_number; texture_extension_number++; GL_LoadTexture_setup: glt->lhcsum = lhcsum; glt->width = width; glt->height = height; glt->mipmap = mipmap; glt->bytesperpixel = bytesperpixel; if (!isDedicated) { GL_Bind(glt->texnum); if (bytesperpixel == 1) GL_Upload8 (data, width, height, mipmap, alpha); else if (bytesperpixel == 4) GL_Upload32 ((void *)data, width, height, mipmap, true); else Sys_Error("GL_LoadTexture: unknown bytesperpixel\n"); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); } return glt->texnum; } // Tomaz || TGA End In glquake.h replace: int GL_LoadTexture (char *identifier, int width, int height, byte *data, qboolean mipmap, qboolean alpha); int GL_LoadTexture (char *identifier, int width, int height, byte *data, qboolean mipmap, qboolean alpha, int bytesperpixel);
Then do a search on GL_LoadTexture and add ,1 at the end of all of em except the ones that has ", int bytesperpixel"
at the end. So then it should be like char_texture = GL_LoadTexture ("charset", 128, 128, draw_chars, false, true); char_texture = GL_LoadTexture ("charset", 128, 128, draw_chars, false, true ,1); At the bottom of gl_draw.c add:
// Tomaz || TGA Begin int image_width; int image_height; /* =========== PCX Loading =========== */ typedef struct { char manufacturer; char version; char encoding; char bits_per_pixel; unsigned short xmin,ymin,xmax,ymax; unsigned short hres,vres; unsigned char palette[48]; char reserved; char color_planes; unsigned short bytes_per_line; unsigned short palette_type; char filler[58]; unsigned data; // unbounded } pcx_t; /* ======== LoadPCX ======== */ byte* LoadPCX (FILE *f, int matchwidth, int matchheight) { pcx_t *pcx, pcxbuf; byte palette[768]; byte *pix, *image_rgba; int x, y; int dataByte, runLength; int count; // // parse the PCX file // fread (&pcxbuf, 1, sizeof(pcxbuf), f); pcx = &pcxbuf; if (pcx->manufacturer != 0x0a || pcx->version != 5 || pcx->encoding != 1 || pcx->bits_per_pixel != 8 || pcx->xmax > 320 || pcx->ymax > 256) { Con_Printf ("Bad pcx file\n"); return NULL; } if (matchwidth && (pcx->xmax+1) != matchwidth) return NULL; if (matchheight && (pcx->ymax+1) != matchheight) return NULL; // seek to palette fseek (f, -768, SEEK_END); fread (palette, 1, 768, f); fseek (f, sizeof(pcxbuf) - 4, SEEK_SET); count = (pcx->xmax+1) * (pcx->ymax+1); image_rgba = malloc( count * 4); for (y=0 ; y<=pcx->ymax ; y++) { pix = image_rgba + 4*y*(pcx->xmax+1); for (x=0 ; x<=pcx->xmax ; ) { dataByte = fgetc(f); if((dataByte & 0xC0) == 0xC0) { runLength = dataByte & 0x3F; dataByte = fgetc(f); } else runLength = 1; while(runLength-- > 0) { pix[0] = palette[dataByte*3]; pix[1] = palette[dataByte*3+1]; pix[2] = palette[dataByte*3+2]; pix[3] = 255; pix += 4; x++; } } } fclose(f); image_width = pcx->xmax+1; image_height = pcx->ymax+1; return image_rgba; } /* ============= TARGA LOADING ============= */ typedef struct _TargaHeader { unsigned char id_length, colormap_type, image_type; unsigned short colormap_index, colormap_length; unsigned char colormap_size; unsigned short x_origin, y_origin, width, height; unsigned char pixel_size, attributes; } TargaHeader; TargaHeader targa_header; int fgetLittleShort (FILE *f) { byte b1, b2; b1 = fgetc(f); b2 = fgetc(f); return (short)(b1 + b2*256); } int fgetLittleLong (FILE *f) { byte b1, b2, b3, b4; b1 = fgetc(f); b2 = fgetc(f); b3 = fgetc(f); b4 = fgetc(f); return b1 + (b2<<8) + (b3<<16) + (b4<<24); } /* ======== LoadTGA ======== */ byte* LoadTGA (FILE *fin, int matchwidth, int matchheight) { int columns, rows, numPixels; byte *pixbuf; int row, column; byte *image_rgba; targa_header.id_length = fgetc(fin); targa_header.colormap_type = fgetc(fin); targa_header.image_type = fgetc(fin); targa_header.colormap_index = fgetLittleShort(fin); targa_header.colormap_length = fgetLittleShort(fin); targa_header.colormap_size = fgetc(fin); targa_header.x_origin = fgetLittleShort(fin); targa_header.y_origin = fgetLittleShort(fin); targa_header.width = fgetLittleShort(fin); targa_header.height = fgetLittleShort(fin); if (matchwidth && targa_header.width != matchwidth) return NULL; if (matchheight && targa_header.height != matchheight) return NULL; targa_header.pixel_size = fgetc(fin); targa_header.attributes = fgetc(fin); if (targa_header.image_type!=2 && targa_header.image_type!=10) Host_Error ("LoadTGA: Only type 2 and 10 targa RGB images supported\n"); if (targa_header.colormap_type !=0 || (targa_header.pixel_size!=32 && targa_header.pixel_size!=24)) Host_Error ("LoadTGA: Only 32 or 24 bit images supported (no colormaps)\n"); columns = targa_header.width; rows = targa_header.height; numPixels = columns * rows; image_rgba = malloc (numPixels*4); if (targa_header.id_length != 0) fseek(fin, targa_header.id_length, SEEK_CUR); // skip TARGA image comment if (targa_header.image_type==2) { // Uncompressed, RGB images for(row=rows-1; row>=0; row--) { pixbuf = image_rgba + row*columns*4; for(column=0; column Thats it with the TGA Loading functions... Now we only have to make all different functions to use it... First of is Sprites. Go to Mod_LoadSpriteFrame in gl_model.c change char name[64]; char name[64], sprite[64], sprite2[64]; sprintf (name, "%s_%i", loadmodel->name, framenum); pspriteframe->gl_texturenum = GL_LoadTexture (name, width, height, (byte *)(pinframe + 1), true, true, 1); COM_StripExtension(loadmodel->name, sprite); sprintf (sprite2, "%s_%i", sprite, framenum); pspriteframe->gl_texturenum = loadtextureimage (sprite2, 0, 0, false, true); if (pspriteframe->gl_texturenum == 0)// did not find a matching TGA... { sprintf (name, "%s_%i", loadmodel->name, framenum); pspriteframe->gl_texturenum = GL_LoadTexture (name, width, height, (byte *)(pinframe + 1), true, true, 1); } glDisable(GL_ALPHA_TEST); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_BLEND); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); and add glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); glDisable(GL_BLEND); glDisable(GL_ALPHA_TEST); Oki now im gonna explain what we did and hopefully you will be able to use it yourself later on. First of the "name" is the path to the image we are looking for. So what we do is that instead of using GL_LoadTexture we use loadtextureimage. If it cant find any TGA it looks for a PCX and if it cant find that either it uses the original LMP. It searches for the images in the same folder as the sprite is. For sprites we also need to know how many frames the sprite is using. But for the explosion its 6. So the TGA's will be "s_explod_0.tga" and all the way up to "s_explod_5.tga". I hope this is farely understandable. If not then let me know.
// Tomaz || TGA Begin char texname[64]; // Tomaz || TGA End And then go a bit further down to: tx->gl_texturenum = GL_LoadTexture (mt->name, tx->width, tx->height, (byte *)(tx+1), true, false ,1); sprintf (texname, "textures/%s", mt->name); tx->gl_texturenum = loadtextureimage (texname, 0, 0, false, true); if (tx->gl_texturenum == 0)// did not find a matching TGA... { tx->gl_texturenum = GL_LoadTexture (mt->name, tx->width, tx->height, (byte *)(tx+1), true, false ,1); } So sprite images goes in the same folder as the sprite and map textures goes in quake/id1/textures or quake/whatever you have named your mod/textures If anything is unclear then let me know... Another tutorial on how to change more things in quake to use TGA or PCX will come in a near future.
THATS IT...
change char name[32]; char name[64], model[64], model2[64]; replace sprintf (name, "%s_%i", loadmodel->name, i); pheader->gl_texturenum[i][0] = pheader->gl_texturenum[i][1] = pheader->gl_texturenum[i][2] = pheader->gl_texturenum[i][3] = GL_LoadTexture (name, pheader->skinwidth, pheader->skinheight, (byte *)(pskintype + 1), true, false, 1); COM_StripExtension(loadmodel->name, model); sprintf (model2, "%s_%i", model, i); pheader->gl_texturenum[i][0] = pheader->gl_texturenum[i][1] = pheader->gl_texturenum[i][2] = pheader->gl_texturenum[i][3] = loadtextureimage (model2, 0, 0, false, true); if (pheader->gl_texturenum[i][0] == 0)// did not find a matching TGA... { sprintf (name, "%s_%i", loadmodel->name, i); pheader->gl_texturenum[i][0] = pheader->gl_texturenum[i][1] = pheader->gl_texturenum[i][2] = pheader->gl_texturenum[i][3] = GL_LoadTexture (name, pheader->skinwidth, pheader->skinheight, (byte *)(pskintype + 1), true, false,1); } add glDisable(GL_ALPHA_TEST); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_BLEND); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); and add glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); glDisable(GL_BLEND); glDisable(GL_ALPHA_TEST); This uses the same way of finding the image as the sprite does. It searches for the image in the same folder as the model is. The first skin is always _0. So if you want to do a new skin on the quad. Then it will be like quaddama_0.tga. If the model has more skins then it just have to be a bigger number. Now we gonna do it so we can use an PCX or TGA for console background and for conchars. Go to Draw_Init in gl_draw.c replace char_texture = GL_LoadTexture ("charset", 128, 128, draw_chars, false, true); char_texture = loadtextureimage ("charset", 0, 0, false, false); if (char_texture == 0)// did not find a matching TGA... char_texture = GL_LoadTexture ("charset", 128, 128, draw_chars, false, true, 1); gl->texnum = GL_LoadTexture ("conback", conback->width, conback->height, ncdata, false, false); gl->texnum = loadtextureimage ("conback", 0, 0, false, false); if (gl->texnum == 0)// did not find a matching TGA... gl->texnum = GL_LoadTexture ("conback", conback->width, conback->height, ncdata, false, false, 1); Hopefully this will work now... If it for some reason doesnt work then just contact me and im sure we can sort it out. |