617 lines
13 KiB
C
617 lines
13 KiB
C
/*
|
|
Copyright (C) 1997-2001 Id Software, Inc.
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU General Public License
|
|
as published by the Free Software Foundation; either version 2
|
|
of the License, or (at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
|
|
See the GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
*/
|
|
|
|
#include "r_local.h"
|
|
|
|
|
|
#define MAX_RIMAGES 1024
|
|
image_t r_images[MAX_RIMAGES];
|
|
int numr_images;
|
|
|
|
|
|
/*
|
|
===============
|
|
R_ImageList_f
|
|
===============
|
|
*/
|
|
void R_ImageList_f (void)
|
|
{
|
|
int i;
|
|
image_t *image;
|
|
int texels;
|
|
|
|
ri.Con_Printf (PRINT_ALL, "------------------\n");
|
|
texels = 0;
|
|
|
|
for (i=0, image=r_images ; i<numr_images ; i++, image++)
|
|
{
|
|
if (image->registration_sequence <= 0)
|
|
continue;
|
|
texels += image->width*image->height;
|
|
switch (image->type)
|
|
{
|
|
case it_skin:
|
|
ri.Con_Printf (PRINT_ALL, "M");
|
|
break;
|
|
case it_sprite:
|
|
ri.Con_Printf (PRINT_ALL, "S");
|
|
break;
|
|
case it_wall:
|
|
ri.Con_Printf (PRINT_ALL, "W");
|
|
break;
|
|
case it_pic:
|
|
ri.Con_Printf (PRINT_ALL, "P");
|
|
break;
|
|
default:
|
|
ri.Con_Printf (PRINT_ALL, " ");
|
|
break;
|
|
}
|
|
|
|
ri.Con_Printf (PRINT_ALL, " %3i %3i : %s\n",
|
|
image->width, image->height, image->name);
|
|
}
|
|
ri.Con_Printf (PRINT_ALL, "Total texel count: %i\n", texels);
|
|
}
|
|
|
|
|
|
/*
|
|
=================================================================
|
|
|
|
PCX LOADING
|
|
|
|
=================================================================
|
|
*/
|
|
|
|
/*
|
|
==============
|
|
LoadPCX
|
|
==============
|
|
*/
|
|
void LoadPCX (char *filename, byte **pic, byte **palette, int *width, int *height)
|
|
{
|
|
byte *raw;
|
|
pcx_t *pcx;
|
|
int x, y;
|
|
int len;
|
|
int dataByte, runLength;
|
|
byte *out, *pix;
|
|
|
|
*pic = NULL;
|
|
|
|
//
|
|
// load the file
|
|
//
|
|
len = ri.FS_LoadFile (filename, (void **)&raw);
|
|
if (!raw)
|
|
{
|
|
ri.Con_Printf (PRINT_DEVELOPER, "Bad pcx file %s\n", filename);
|
|
return;
|
|
}
|
|
|
|
//
|
|
// parse the PCX file
|
|
//
|
|
pcx = (pcx_t *)raw;
|
|
|
|
pcx->xmin = LittleShort(pcx->xmin);
|
|
pcx->ymin = LittleShort(pcx->ymin);
|
|
pcx->xmax = LittleShort(pcx->xmax);
|
|
pcx->ymax = LittleShort(pcx->ymax);
|
|
pcx->hres = LittleShort(pcx->hres);
|
|
pcx->vres = LittleShort(pcx->vres);
|
|
pcx->bytes_per_line = LittleShort(pcx->bytes_per_line);
|
|
pcx->palette_type = LittleShort(pcx->palette_type);
|
|
|
|
raw = &pcx->data;
|
|
|
|
if (pcx->manufacturer != 0x0a
|
|
|| pcx->version != 5
|
|
|| pcx->encoding != 1
|
|
|| pcx->bits_per_pixel != 8
|
|
|| pcx->xmax >= 640
|
|
|| pcx->ymax >= 480)
|
|
{
|
|
ri.Con_Printf (PRINT_ALL, "Bad pcx file %s\n", filename);
|
|
return;
|
|
}
|
|
|
|
out = malloc ( (pcx->ymax+1) * (pcx->xmax+1) );
|
|
|
|
*pic = out;
|
|
|
|
pix = out;
|
|
|
|
if (palette)
|
|
{
|
|
*palette = malloc(768);
|
|
memcpy (*palette, (byte *)pcx + len - 768, 768);
|
|
}
|
|
|
|
if (width)
|
|
*width = pcx->xmax+1;
|
|
if (height)
|
|
*height = pcx->ymax+1;
|
|
|
|
for (y=0 ; y<=pcx->ymax ; y++, pix += pcx->xmax+1)
|
|
{
|
|
for (x=0 ; x<=pcx->xmax ; )
|
|
{
|
|
dataByte = *raw++;
|
|
|
|
if((dataByte & 0xC0) == 0xC0)
|
|
{
|
|
runLength = dataByte & 0x3F;
|
|
dataByte = *raw++;
|
|
}
|
|
else
|
|
runLength = 1;
|
|
|
|
while(runLength-- > 0)
|
|
pix[x++] = dataByte;
|
|
}
|
|
|
|
}
|
|
|
|
if ( raw - (byte *)pcx > len)
|
|
{
|
|
ri.Con_Printf (PRINT_DEVELOPER, "PCX file %s was malformed", filename);
|
|
free (*pic);
|
|
*pic = NULL;
|
|
}
|
|
|
|
ri.FS_FreeFile (pcx);
|
|
}
|
|
|
|
/*
|
|
=========================================================
|
|
|
|
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;
|
|
|
|
|
|
/*
|
|
=============
|
|
LoadTGA
|
|
=============
|
|
*/
|
|
void LoadTGA (char *name, byte **pic, int *width, int *height)
|
|
{
|
|
int columns, rows, numPixels;
|
|
byte *pixbuf;
|
|
int row, column;
|
|
byte *buf_p;
|
|
byte *buffer;
|
|
int length;
|
|
TargaHeader targa_header;
|
|
byte *targa_rgba;
|
|
|
|
*pic = NULL;
|
|
|
|
//
|
|
// load the file
|
|
//
|
|
length = ri.FS_LoadFile (name, (void **)&buffer);
|
|
if (!buffer)
|
|
{
|
|
ri.Con_Printf (PRINT_DEVELOPER, "Bad tga file %s\n", name);
|
|
return;
|
|
}
|
|
|
|
buf_p = buffer;
|
|
|
|
targa_header.id_length = *buf_p++;
|
|
targa_header.colormap_type = *buf_p++;
|
|
targa_header.image_type = *buf_p++;
|
|
|
|
targa_header.colormap_index = LittleShort ( *((short *)buf_p) );
|
|
buf_p+=2;
|
|
targa_header.colormap_length = LittleShort ( *((short *)buf_p) );
|
|
buf_p+=2;
|
|
targa_header.colormap_size = *buf_p++;
|
|
targa_header.x_origin = LittleShort ( *((short *)buf_p) );
|
|
buf_p+=2;
|
|
targa_header.y_origin = LittleShort ( *((short *)buf_p) );
|
|
buf_p+=2;
|
|
targa_header.width = LittleShort ( *((short *)buf_p) );
|
|
buf_p+=2;
|
|
targa_header.height = LittleShort ( *((short *)buf_p) );
|
|
buf_p+=2;
|
|
targa_header.pixel_size = *buf_p++;
|
|
targa_header.attributes = *buf_p++;
|
|
|
|
if (targa_header.image_type!=2
|
|
&& targa_header.image_type!=10)
|
|
ri.Sys_Error (ERR_DROP, "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))
|
|
ri.Sys_Error (ERR_DROP, "LoadTGA: Only 32 or 24 bit images supported (no colormaps)\n");
|
|
|
|
columns = targa_header.width;
|
|
rows = targa_header.height;
|
|
numPixels = columns * rows;
|
|
|
|
if (width)
|
|
*width = columns;
|
|
if (height)
|
|
*height = rows;
|
|
|
|
targa_rgba = malloc (numPixels*4);
|
|
*pic = targa_rgba;
|
|
|
|
if (targa_header.id_length != 0)
|
|
buf_p += targa_header.id_length; // skip TARGA image comment
|
|
|
|
if (targa_header.image_type==2) { // Uncompressed, RGB images
|
|
for(row=rows-1; row>=0; row--) {
|
|
pixbuf = targa_rgba + row*columns*4;
|
|
for(column=0; column<columns; column++) {
|
|
unsigned char red,green,blue,alphabyte;
|
|
switch (targa_header.pixel_size) {
|
|
case 24:
|
|
|
|
blue = *buf_p++;
|
|
green = *buf_p++;
|
|
red = *buf_p++;
|
|
*pixbuf++ = red;
|
|
*pixbuf++ = green;
|
|
*pixbuf++ = blue;
|
|
*pixbuf++ = 255;
|
|
break;
|
|
case 32:
|
|
blue = *buf_p++;
|
|
green = *buf_p++;
|
|
red = *buf_p++;
|
|
alphabyte = *buf_p++;
|
|
*pixbuf++ = red;
|
|
*pixbuf++ = green;
|
|
*pixbuf++ = blue;
|
|
*pixbuf++ = alphabyte;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (targa_header.image_type==10) { // Runlength encoded RGB images
|
|
unsigned char red,green,blue,alphabyte,packetHeader,packetSize,j;
|
|
for(row=rows-1; row>=0; row--) {
|
|
pixbuf = targa_rgba + row*columns*4;
|
|
for(column=0; column<columns; ) {
|
|
packetHeader= *buf_p++;
|
|
packetSize = 1 + (packetHeader & 0x7f);
|
|
if (packetHeader & 0x80) { // run-length packet
|
|
switch (targa_header.pixel_size) {
|
|
case 24:
|
|
blue = *buf_p++;
|
|
green = *buf_p++;
|
|
red = *buf_p++;
|
|
alphabyte = 255;
|
|
break;
|
|
case 32:
|
|
blue = *buf_p++;
|
|
green = *buf_p++;
|
|
red = *buf_p++;
|
|
alphabyte = *buf_p++;
|
|
break;
|
|
}
|
|
|
|
for(j=0;j<packetSize;j++) {
|
|
*pixbuf++=red;
|
|
*pixbuf++=green;
|
|
*pixbuf++=blue;
|
|
*pixbuf++=alphabyte;
|
|
column++;
|
|
if (column==columns) { // run spans across rows
|
|
column=0;
|
|
if (row>0)
|
|
row--;
|
|
else
|
|
goto breakOut;
|
|
pixbuf = targa_rgba + row*columns*4;
|
|
}
|
|
}
|
|
}
|
|
else { // non run-length packet
|
|
for(j=0;j<packetSize;j++) {
|
|
switch (targa_header.pixel_size) {
|
|
case 24:
|
|
blue = *buf_p++;
|
|
green = *buf_p++;
|
|
red = *buf_p++;
|
|
*pixbuf++ = red;
|
|
*pixbuf++ = green;
|
|
*pixbuf++ = blue;
|
|
*pixbuf++ = 255;
|
|
break;
|
|
case 32:
|
|
blue = *buf_p++;
|
|
green = *buf_p++;
|
|
red = *buf_p++;
|
|
alphabyte = *buf_p++;
|
|
*pixbuf++ = red;
|
|
*pixbuf++ = green;
|
|
*pixbuf++ = blue;
|
|
*pixbuf++ = alphabyte;
|
|
break;
|
|
}
|
|
column++;
|
|
if (column==columns) { // pixel packet run spans across rows
|
|
column=0;
|
|
if (row>0)
|
|
row--;
|
|
else
|
|
goto breakOut;
|
|
pixbuf = targa_rgba + row*columns*4;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
breakOut:;
|
|
}
|
|
}
|
|
|
|
ri.FS_FreeFile (buffer);
|
|
}
|
|
|
|
|
|
//=======================================================
|
|
|
|
image_t *R_FindFreeImage (void)
|
|
{
|
|
image_t *image;
|
|
int i;
|
|
|
|
// find a free image_t
|
|
for (i=0, image=r_images ; i<numr_images ; i++,image++)
|
|
{
|
|
if (!image->registration_sequence)
|
|
break;
|
|
}
|
|
if (i == numr_images)
|
|
{
|
|
if (numr_images == MAX_RIMAGES)
|
|
ri.Sys_Error (ERR_DROP, "MAX_RIMAGES");
|
|
numr_images++;
|
|
}
|
|
image = &r_images[i];
|
|
|
|
return image;
|
|
}
|
|
|
|
/*
|
|
================
|
|
GL_LoadPic
|
|
|
|
================
|
|
*/
|
|
image_t *GL_LoadPic (char *name, byte *pic, int width, int height, imagetype_t type)
|
|
{
|
|
image_t *image;
|
|
int i, c, b;
|
|
|
|
image = R_FindFreeImage ();
|
|
if (strlen(name) >= sizeof(image->name))
|
|
ri.Sys_Error (ERR_DROP, "Draw_LoadPic: \"%s\" is too long", name);
|
|
strcpy (image->name, name);
|
|
image->registration_sequence = registration_sequence;
|
|
|
|
image->width = width;
|
|
image->height = height;
|
|
image->type = type;
|
|
|
|
c = width*height;
|
|
image->pixels[0] = malloc (c);
|
|
image->transparent = false;
|
|
for (i=0 ; i<c ; i++)
|
|
{
|
|
b = pic[i];
|
|
if (b == 255)
|
|
image->transparent = true;
|
|
image->pixels[0][i] = b;
|
|
}
|
|
|
|
return image;
|
|
}
|
|
|
|
/*
|
|
================
|
|
R_LoadWal
|
|
================
|
|
*/
|
|
image_t *R_LoadWal (char *name)
|
|
{
|
|
miptex_t *mt;
|
|
int ofs;
|
|
image_t *image;
|
|
int size;
|
|
|
|
ri.FS_LoadFile (name, (void **)&mt);
|
|
if (!mt)
|
|
{
|
|
ri.Con_Printf (PRINT_ALL, "R_LoadWal: can't load %s\n", name);
|
|
return r_notexture_mip;
|
|
}
|
|
|
|
image = R_FindFreeImage ();
|
|
strcpy (image->name, name);
|
|
image->width = LittleLong (mt->width);
|
|
image->height = LittleLong (mt->height);
|
|
image->type = it_wall;
|
|
image->registration_sequence = registration_sequence;
|
|
|
|
size = image->width*image->height * (256+64+16+4)/256;
|
|
image->pixels[0] = malloc (size);
|
|
image->pixels[1] = image->pixels[0] + image->width*image->height;
|
|
image->pixels[2] = image->pixels[1] + image->width*image->height/4;
|
|
image->pixels[3] = image->pixels[2] + image->width*image->height/16;
|
|
|
|
ofs = LittleLong (mt->offsets[0]);
|
|
memcpy ( image->pixels[0], (byte *)mt + ofs, size);
|
|
|
|
ri.FS_FreeFile ((void *)mt);
|
|
|
|
return image;
|
|
}
|
|
|
|
|
|
/*
|
|
===============
|
|
R_FindImage
|
|
|
|
Finds or loads the given image
|
|
===============
|
|
*/
|
|
image_t *R_FindImage (char *name, imagetype_t type)
|
|
{
|
|
image_t *image;
|
|
int i, len;
|
|
byte *pic, *palette;
|
|
int width, height;
|
|
|
|
if (!name)
|
|
return NULL; // ri.Sys_Error (ERR_DROP, "R_FindImage: NULL name");
|
|
len = strlen(name);
|
|
if (len<5)
|
|
return NULL; // ri.Sys_Error (ERR_DROP, "R_FindImage: bad name: %s", name);
|
|
|
|
// look for it
|
|
for (i=0, image=r_images ; i<numr_images ; i++,image++)
|
|
{
|
|
if (!strcmp(name, image->name))
|
|
{
|
|
image->registration_sequence = registration_sequence;
|
|
return image;
|
|
}
|
|
}
|
|
|
|
//
|
|
// load the pic from disk
|
|
//
|
|
pic = NULL;
|
|
palette = NULL;
|
|
if (!strcmp(name+len-4, ".pcx"))
|
|
{
|
|
LoadPCX (name, &pic, &palette, &width, &height);
|
|
if (!pic)
|
|
return NULL; // ri.Sys_Error (ERR_DROP, "R_FindImage: can't load %s", name);
|
|
image = GL_LoadPic (name, pic, width, height, type);
|
|
}
|
|
else if (!strcmp(name+len-4, ".wal"))
|
|
{
|
|
image = R_LoadWal (name);
|
|
}
|
|
else if (!strcmp(name+len-4, ".tga"))
|
|
return NULL; // ri.Sys_Error (ERR_DROP, "R_FindImage: can't load %s in software renderer", name);
|
|
else
|
|
return NULL; // ri.Sys_Error (ERR_DROP, "R_FindImage: bad extension on: %s", name);
|
|
|
|
if (pic)
|
|
free(pic);
|
|
if (palette)
|
|
free(palette);
|
|
|
|
return image;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
===============
|
|
R_RegisterSkin
|
|
===============
|
|
*/
|
|
struct image_s *R_RegisterSkin (char *name)
|
|
{
|
|
return R_FindImage (name, it_skin);
|
|
}
|
|
|
|
|
|
/*
|
|
================
|
|
R_FreeUnusedImages
|
|
|
|
Any image that was not touched on this registration sequence
|
|
will be freed.
|
|
================
|
|
*/
|
|
void R_FreeUnusedImages (void)
|
|
{
|
|
int i;
|
|
image_t *image;
|
|
|
|
for (i=0, image=r_images ; i<numr_images ; i++, image++)
|
|
{
|
|
if (image->registration_sequence == registration_sequence)
|
|
{
|
|
Com_PageInMemory ((byte *)image->pixels[0], image->width*image->height);
|
|
continue; // used this sequence
|
|
}
|
|
if (!image->registration_sequence)
|
|
continue; // free texture
|
|
if (image->type == it_pic)
|
|
continue; // don't free pics
|
|
// free it
|
|
free (image->pixels[0]); // the other mip levels just follow
|
|
memset (image, 0, sizeof(*image));
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
===============
|
|
R_InitImages
|
|
===============
|
|
*/
|
|
void R_InitImages (void)
|
|
{
|
|
registration_sequence = 1;
|
|
}
|
|
|
|
/*
|
|
===============
|
|
R_ShutdownImages
|
|
===============
|
|
*/
|
|
void R_ShutdownImages (void)
|
|
{
|
|
int i;
|
|
image_t *image;
|
|
|
|
for (i=0, image=r_images ; i<numr_images ; i++, image++)
|
|
{
|
|
if (!image->registration_sequence)
|
|
continue; // free texture
|
|
// free it
|
|
free (image->pixels[0]); // the other mip levels just follow
|
|
memset (image, 0, sizeof(*image));
|
|
}
|
|
}
|
|
|