2000-06-10 03:36:28 +00:00
|
|
|
// Halflife BSP Loading
|
|
|
|
// Hack #1:
|
|
|
|
// Use this LoadLighting instead of the existing one in gl_model.c
|
|
|
|
// Hack #2:
|
|
|
|
// Use this LoadTexture instead of the existing one in gl_model.c
|
|
|
|
// Hack #?:
|
|
|
|
// Add int lhcsum; to gltexture_t in gl_draw.c
|
|
|
|
// Hack #4:
|
|
|
|
// save bspversion in 'int bspversion;'. Allow 30.
|
|
|
|
// Hack #5:
|
|
|
|
// New hl_wad.c
|
|
|
|
// Hack #6:
|
|
|
|
// In gl_model.h, texture_s, add "int transparent;"
|
|
|
|
// Hack #7:
|
|
|
|
// LoadEntities:
|
|
|
|
// if (bspversion > 29) // hlbsp
|
|
|
|
// CL_ParseEntityLump(loadmodel->entities);
|
|
|
|
//
|
|
|
|
// Hack #8:
|
|
|
|
// Top of GL_RenderBrushPoly (gl_rsurf.c - and add the function :)
|
|
|
|
// if (fa->texinfo->texture->transparent) {
|
|
|
|
// R_RenderBrushPolyTransparent(fa);
|
|
|
|
// return;
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// Hack #9: (mod_loadfaces)
|
|
|
|
// if (i == -1)
|
|
|
|
// out->samples = NULL;
|
|
|
|
// else if (bspversion > 29)
|
|
|
|
// out->samples = loadmodel->lightdata + i;
|
|
|
|
// else
|
|
|
|
// out->samples = loadmodel->lightdata + (i * 3);
|
|
|
|
//
|
|
|
|
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
# include <config.h>
|
|
|
|
#endif
|
|
|
|
#include <math.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <stdio.h>
|
2000-06-10 21:40:31 +00:00
|
|
|
#include <stdlib.h>
|
2000-06-10 03:36:28 +00:00
|
|
|
|
|
|
|
#include "bothdefs.h" // needed by: common.h, net.h, client.h
|
|
|
|
#include "qendian.h"
|
|
|
|
#include "msg.h"
|
|
|
|
#include "bspfile.h" // needed by: glquake.h
|
|
|
|
#include "vid.h"
|
|
|
|
#include "sys.h"
|
|
|
|
#include "zone.h" // needed by: client.h, gl_model.h
|
|
|
|
#include "mathlib.h" // needed by: protocol.h, render.h, client.h,
|
|
|
|
// modelgen.h, glmodel.h
|
|
|
|
#include "wad.h"
|
|
|
|
#include "draw.h"
|
|
|
|
#include "cvar.h"
|
|
|
|
#include "crc.h"
|
|
|
|
#include "net.h" // needed by: client.h
|
|
|
|
#include "protocol.h" // needed by: client.h
|
|
|
|
#include "cmd.h"
|
|
|
|
#include "sbar.h"
|
|
|
|
#include "render.h" // needed by: client.h, gl_model.h, glquake.h
|
|
|
|
#include "client.h" // need cls in this file
|
|
|
|
#include "model.h" // needed by: glquake.h
|
|
|
|
#include "console.h"
|
|
|
|
#include "glquake.h"
|
|
|
|
#include "quakefs.h"
|
|
|
|
#include "checksum.h"
|
2000-06-10 21:40:31 +00:00
|
|
|
#include "hl.h"
|
2000-06-10 03:36:28 +00:00
|
|
|
|
|
|
|
|
|
|
|
int image_width;
|
|
|
|
int image_height;
|
|
|
|
|
|
|
|
// From gl_draw.c
|
|
|
|
typedef struct
|
|
|
|
{
|
|
|
|
int texnum;
|
|
|
|
char identifier[64];
|
|
|
|
int width, height;
|
|
|
|
qboolean mipmap;
|
|
|
|
int lhcsum;
|
|
|
|
} gltexture_t;
|
|
|
|
|
|
|
|
extern int bspversion;
|
|
|
|
extern model_t *loadmodel;
|
|
|
|
extern char loadname[32];
|
|
|
|
extern byte *mod_base;
|
|
|
|
extern gltexture_t gltextures[];
|
|
|
|
extern int numgltextures;
|
|
|
|
|
|
|
|
int HL_LoadTexture (char *identifier, int width, int height, byte *data, qboolean mipmap, qboolean alpha);
|
|
|
|
|
|
|
|
typedef struct miptex_hls
|
|
|
|
{
|
|
|
|
char name[16];
|
|
|
|
unsigned width, height;
|
|
|
|
char junk[16];
|
|
|
|
} miptex_hl;
|
|
|
|
|
|
|
|
void HL_Mod_LoadLighting (lump_t *l)
|
|
|
|
{
|
|
|
|
if (!l->filelen)
|
|
|
|
{
|
|
|
|
loadmodel->lightdata = NULL;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (bspversion > 29) { // Load coloured lighting data
|
|
|
|
loadmodel->lightdata = Hunk_AllocName ( l->filelen, loadname);
|
|
|
|
memcpy (loadmodel->lightdata, mod_base + l->fileofs, l->filelen);
|
|
|
|
} else { // Expand white lighting data
|
|
|
|
byte *in, *out, d;
|
|
|
|
int i;
|
|
|
|
Con_DPrintf("Converting lightmap data\n");
|
|
|
|
loadmodel->lightdata = Hunk_AllocName (l->filelen*3, loadname);
|
|
|
|
in = loadmodel->lightdata + l->filelen*2; // place the file at the end, so it will not be overwritten until the very last write
|
|
|
|
out = loadmodel->lightdata;
|
|
|
|
memcpy (in, mod_base + l->fileofs, l->filelen);
|
|
|
|
for (i = 0;i < l->filelen;i++) {
|
|
|
|
d = *in++;
|
|
|
|
*out++ = d;
|
|
|
|
*out++ = d;
|
|
|
|
*out++ = d;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
byte* loadimagepixels (char* filename, qboolean complain, int matchwidth, int matchheight)
|
|
|
|
{
|
|
|
|
FILE *f;
|
|
|
|
char basename[128], name[128];
|
|
|
|
byte *image_rgba;
|
|
|
|
COM_StripExtension(filename, basename); // strip the extension to allow TGA skins on Q2 models despite the .pcx in the skin name
|
|
|
|
sprintf (name, "%s.tga", basename);
|
|
|
|
COM_FOpenFile (name, &f);
|
|
|
|
if (f)
|
2000-06-10 21:40:31 +00:00
|
|
|
return LoadTGA (f);
|
|
|
|
//return LoadTGA (f, matchwidth, matchheight);
|
2000-06-10 03:36:28 +00:00
|
|
|
sprintf (name, "%s.pcx", basename);
|
|
|
|
COM_FOpenFile (name, &f);
|
|
|
|
if (f)
|
2000-06-10 21:40:31 +00:00
|
|
|
return LoadPCX (f);
|
|
|
|
//return LoadPCX (f, matchwidth, matchheight);
|
|
|
|
if ((image_rgba = W_GetTexture(basename, matchwidth, matchheight)))
|
2000-06-10 03:36:28 +00:00
|
|
|
return image_rgba;
|
|
|
|
if (complain)
|
|
|
|
Con_Printf ("Couldn't load %s.tga or .pcx\n", filename);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
void HL_Mod_LoadTextures (lump_t *l)
|
|
|
|
{
|
2000-06-10 21:40:31 +00:00
|
|
|
int i, j, num, max, altmax, freeimage, transparent, bytesperpixel;
|
2000-06-10 03:36:28 +00:00
|
|
|
miptex_t *mt;
|
|
|
|
texture_t *tx, *tx2;
|
|
|
|
texture_t *anims[10];
|
|
|
|
texture_t *altanims[10];
|
|
|
|
dmiptexlump_t *m;
|
|
|
|
byte *data;
|
|
|
|
char imagename[32];
|
|
|
|
|
|
|
|
if (!l->filelen)
|
|
|
|
{
|
|
|
|
loadmodel->textures = NULL;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
m = (dmiptexlump_t *)(mod_base + l->fileofs);
|
|
|
|
m->nummiptex = LittleLong (m->nummiptex);
|
|
|
|
loadmodel->numtextures = m->nummiptex;
|
|
|
|
loadmodel->textures = Hunk_AllocName (m->nummiptex * sizeof(*loadmodel->textures) , loadname);
|
|
|
|
|
|
|
|
for (i=0 ; i<m->nummiptex ; i++)
|
|
|
|
{
|
|
|
|
m->dataofs[i] = LittleLong(m->dataofs[i]);
|
|
|
|
if (m->dataofs[i] == -1)
|
|
|
|
continue;
|
|
|
|
mt = (miptex_t *)((byte *)m + m->dataofs[i]);
|
|
|
|
mt->width = LittleLong (mt->width);
|
|
|
|
mt->height = LittleLong (mt->height);
|
|
|
|
for (j=0 ; j<MIPLEVELS ; j++)
|
|
|
|
mt->offsets[j] = LittleLong (mt->offsets[j]);
|
|
|
|
|
|
|
|
if ( (mt->width & 15) || (mt->height & 15) )
|
|
|
|
Sys_Error ("Texture %s is not 16 aligned", mt->name);
|
|
|
|
|
|
|
|
// LordHavoc: rewriting the map texture loader for GLQuake
|
|
|
|
tx = Hunk_AllocName (sizeof(texture_t), loadname );
|
|
|
|
loadmodel->textures[i] = tx;
|
|
|
|
|
|
|
|
memcpy (tx->name, mt->name, sizeof(tx->name));
|
|
|
|
tx->width = mt->width;
|
|
|
|
tx->height = mt->height;
|
|
|
|
for (j=0 ; j<MIPLEVELS ; j++)
|
|
|
|
tx->offsets[j] = 0;
|
|
|
|
|
|
|
|
strcpy(imagename, "textures/");
|
|
|
|
strcat(imagename, mt->name);
|
|
|
|
|
2000-06-10 21:40:31 +00:00
|
|
|
freeimage = true;
|
|
|
|
transparent = false;
|
2000-06-10 03:36:28 +00:00
|
|
|
bytesperpixel = 4;
|
2000-06-10 21:40:31 +00:00
|
|
|
data = loadimagepixels(imagename, false, tx->width, tx->height);
|
2000-06-10 03:36:28 +00:00
|
|
|
if (!data) // no external texture found
|
|
|
|
{
|
|
|
|
strcpy(imagename, mt->name);
|
2000-06-10 21:40:31 +00:00
|
|
|
data = loadimagepixels(imagename, false, tx->width, tx->height);
|
2000-06-10 03:36:28 +00:00
|
|
|
if (!data) // no external texture found
|
|
|
|
{
|
2000-06-10 21:40:31 +00:00
|
|
|
freeimage = false;
|
2000-06-10 03:36:28 +00:00
|
|
|
bytesperpixel = 1;
|
|
|
|
if (mt->offsets[0]) // texture included
|
|
|
|
data = (byte *)((int) mt + mt->offsets[0]);
|
|
|
|
else // no texture, and no external replacement texture was found
|
|
|
|
{
|
|
|
|
tx->width = tx->height = 16;
|
|
|
|
data = (byte *)((int) r_notexture_mip + r_notexture_mip->offsets[0]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
for (j = 0;j < image_width*image_height;j++)
|
|
|
|
if (data[j*4+3] < 255)
|
|
|
|
{
|
2000-06-10 21:40:31 +00:00
|
|
|
transparent = true;
|
2000-06-10 03:36:28 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
for (j = 0;j < image_width*image_height;j++)
|
|
|
|
if (data[j*4+3] < 255)
|
|
|
|
{
|
2000-06-10 21:40:31 +00:00
|
|
|
transparent = true;
|
2000-06-10 03:36:28 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!strncmp(mt->name,"sky",3))
|
|
|
|
{
|
2000-06-10 21:40:31 +00:00
|
|
|
tx->transparent = false;
|
|
|
|
R_InitSky_32 (data);
|
2000-06-10 03:36:28 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
tx->transparent = transparent;
|
|
|
|
texture_mode = GL_LINEAR_MIPMAP_NEAREST; //_LINEAR;
|
|
|
|
if (bytesperpixel == 1)
|
|
|
|
tx->gl_texturenum = GL_LoadTexture (tx->name, tx->width, tx->height, data, true, transparent);
|
|
|
|
else
|
|
|
|
tx->gl_texturenum = HL_LoadTexture (tx->name, tx->width, tx->height, data, true, transparent);
|
|
|
|
texture_mode = GL_LINEAR;
|
|
|
|
}
|
|
|
|
if (freeimage)
|
|
|
|
free(data);
|
|
|
|
}
|
|
|
|
//
|
|
|
|
// sequence the animations
|
|
|
|
//
|
|
|
|
for (i=0 ; i<m->nummiptex ; i++)
|
|
|
|
{
|
|
|
|
tx = loadmodel->textures[i];
|
|
|
|
if (!tx || tx->name[0] != '+')
|
|
|
|
continue;
|
|
|
|
if (tx->anim_next)
|
|
|
|
continue; // allready sequenced
|
|
|
|
|
|
|
|
// find the number of frames in the animation
|
|
|
|
memset (anims, 0, sizeof(anims));
|
|
|
|
memset (altanims, 0, sizeof(altanims));
|
|
|
|
|
|
|
|
max = tx->name[1];
|
|
|
|
altmax = 0;
|
|
|
|
if (max >= 'a' && max <= 'z')
|
|
|
|
max -= 'a' - 'A';
|
|
|
|
if (max >= '0' && max <= '9')
|
|
|
|
{
|
|
|
|
max -= '0';
|
|
|
|
altmax = 0;
|
|
|
|
anims[max] = tx;
|
|
|
|
max++;
|
|
|
|
}
|
|
|
|
else if (max >= 'A' && max <= 'J')
|
|
|
|
{
|
|
|
|
altmax = max - 'A';
|
|
|
|
max = 0;
|
|
|
|
altanims[altmax] = tx;
|
|
|
|
altmax++;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
Sys_Error ("Bad animating texture %s", tx->name);
|
|
|
|
|
|
|
|
for (j=i+1 ; j<m->nummiptex ; j++)
|
|
|
|
{
|
|
|
|
tx2 = loadmodel->textures[j];
|
|
|
|
if (!tx2 || tx2->name[0] != '+')
|
|
|
|
continue;
|
|
|
|
if (strcmp (tx2->name+2, tx->name+2))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
num = tx2->name[1];
|
|
|
|
if (num >= 'a' && num <= 'z')
|
|
|
|
num -= 'a' - 'A';
|
|
|
|
if (num >= '0' && num <= '9')
|
|
|
|
{
|
|
|
|
num -= '0';
|
|
|
|
anims[num] = tx2;
|
|
|
|
if (num+1 > max)
|
|
|
|
max = num + 1;
|
|
|
|
}
|
|
|
|
else if (num >= 'A' && num <= 'J')
|
|
|
|
{
|
|
|
|
num = num - 'A';
|
|
|
|
altanims[num] = tx2;
|
|
|
|
if (num+1 > altmax)
|
|
|
|
altmax = num+1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
Sys_Error ("Bad animating texture %s", tx->name);
|
|
|
|
}
|
|
|
|
|
|
|
|
#define ANIM_CYCLE 2
|
|
|
|
// link them all together
|
|
|
|
for (j=0 ; j<max ; j++)
|
|
|
|
{
|
|
|
|
tx2 = anims[j];
|
|
|
|
if (!tx2)
|
|
|
|
Sys_Error ("Missing frame %i of %s",j, tx->name);
|
|
|
|
tx2->anim_total = max * ANIM_CYCLE;
|
|
|
|
tx2->anim_min = j * ANIM_CYCLE;
|
|
|
|
tx2->anim_max = (j+1) * ANIM_CYCLE;
|
|
|
|
tx2->anim_next = anims[ (j+1)%max ];
|
|
|
|
if (altmax)
|
|
|
|
tx2->alternate_anims = altanims[0];
|
|
|
|
}
|
|
|
|
for (j=0 ; j<altmax ; j++)
|
|
|
|
{
|
|
|
|
tx2 = altanims[j];
|
|
|
|
if (!tx2)
|
|
|
|
Sys_Error ("Missing frame %i of %s",j, tx->name);
|
|
|
|
tx2->anim_total = altmax * ANIM_CYCLE;
|
|
|
|
tx2->anim_min = j * ANIM_CYCLE;
|
|
|
|
tx2->anim_max = (j+1) * ANIM_CYCLE;
|
|
|
|
tx2->anim_next = altanims[ (j+1)%altmax ];
|
|
|
|
if (max)
|
|
|
|
tx2->alternate_anims = anims[0];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void CL_ParseEntityLump(char *entdata)
|
|
|
|
{
|
|
|
|
char *data;
|
|
|
|
char key[128], value[1024];
|
|
|
|
char wadname[128];
|
|
|
|
int i, j, k;
|
|
|
|
|
|
|
|
data = entdata;
|
|
|
|
if (!data)
|
|
|
|
return;
|
|
|
|
|
|
|
|
data = COM_Parse(data);
|
|
|
|
|
|
|
|
if (!data)
|
|
|
|
return; // valid exit
|
|
|
|
|
|
|
|
if (com_token[0] != '{')
|
|
|
|
return; // error
|
|
|
|
|
|
|
|
|
|
|
|
while (1)
|
|
|
|
{
|
|
|
|
data = COM_Parse(data);
|
|
|
|
if (!data)
|
|
|
|
return; // error
|
|
|
|
if (com_token[0] == '}')
|
|
|
|
return; // since we're just parsing the first ent (worldspawn), exit
|
|
|
|
strcpy(key, com_token);
|
|
|
|
while (key[strlen(key)-1] == ' ') // remove trailing spaces
|
|
|
|
key[strlen(key)-1] = 0;
|
|
|
|
data = COM_Parse(data);
|
|
|
|
if (!data)
|
|
|
|
return; // error
|
|
|
|
strcpy(value, com_token);
|
|
|
|
// if (!strcmp("sky", key))
|
|
|
|
// strcpy(skyname, value);
|
|
|
|
// else if (!strcmp("skyboxsize", key))
|
|
|
|
// {
|
|
|
|
// r_skyboxsize.value = atof(value);
|
|
|
|
// if (r_skyboxsize.value < 64)
|
|
|
|
// r_skyboxsize.value = 64;
|
|
|
|
// }
|
|
|
|
|
|
|
|
if (!strcmp("wad", key)) // for HalfLife maps
|
|
|
|
{
|
|
|
|
j = 0;
|
|
|
|
for (i = 0;i < 128;i++)
|
|
|
|
if (value[i] != ';' && value[i] != '\\' && value[i] != '/' && value[i] != ':')
|
|
|
|
break;
|
|
|
|
if (value[i])
|
|
|
|
{
|
|
|
|
for (;i < 128;i++)
|
|
|
|
{
|
|
|
|
// ignore path - the \\ check is for HalfLife... stupid windoze 'programmers'...
|
|
|
|
if (value[i] == '\\' || value[i] == '/' || value[i] == ':')
|
|
|
|
j = i+1;
|
|
|
|
else if (value[i] == ';' || value[i] == 0)
|
|
|
|
{
|
|
|
|
k = value[i];
|
|
|
|
value[i] = 0;
|
|
|
|
strcpy(wadname, "textures/");
|
|
|
|
Con_DPrintf("Wad: %s\n", &value[j]);
|
|
|
|
strcat(wadname, &value[j]);
|
2000-06-10 21:40:31 +00:00
|
|
|
W_LoadTextureWadFile (wadname, false);
|
2000-06-10 03:36:28 +00:00
|
|
|
j = i+1;
|
|
|
|
if (!k)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
================
|
|
|
|
GL_LoadTexture
|
|
|
|
================
|
|
|
|
*/
|
|
|
|
int lhcsumtable2[256];
|
|
|
|
int HL_LoadTexture (char *identifier, int width, int height, byte *data, qboolean mipmap, qboolean alpha)
|
|
|
|
{
|
2000-06-10 21:40:31 +00:00
|
|
|
int i, s, lhcsum;
|
2000-06-10 03:36:28 +00:00
|
|
|
gltexture_t *glt;
|
|
|
|
|
|
|
|
// LordHavoc: do a checksum to confirm the data really is the same as previous
|
|
|
|
// occurances. well this isn't exactly a checksum, it's better than that but
|
|
|
|
// not following any standards.
|
|
|
|
lhcsum = 0;
|
|
|
|
s = width*height*4;
|
|
|
|
for (i = 0;i < 256;i++) lhcsumtable2[i] = i + 1;
|
|
|
|
for (i = 0;i < s;i++) lhcsum += (lhcsumtable2[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))
|
|
|
|
{
|
|
|
|
// LordHavoc: everyone hates cache mismatchs, so I fixed it
|
|
|
|
if (lhcsum != glt->lhcsum || width != glt->width || height != glt->height)
|
|
|
|
{
|
|
|
|
Con_DPrintf("GL_LoadTexture: cache mismatch, replacing old texture\n");
|
|
|
|
goto HL_LoadTexture_setup; // drop out with glt pointing to the texture to replace
|
|
|
|
}
|
|
|
|
return glt->texnum;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
glt = &gltextures[numgltextures];
|
|
|
|
numgltextures++;
|
|
|
|
|
|
|
|
strcpy (glt->identifier, identifier);
|
|
|
|
glt->texnum = texture_extension_number;
|
|
|
|
texture_extension_number++;
|
|
|
|
// LordHavoc: label to drop out of the loop into the setup code
|
|
|
|
HL_LoadTexture_setup:
|
|
|
|
glt->lhcsum = lhcsum; // LordHavoc: used to verify textures are identical
|
|
|
|
glt->width = width;
|
|
|
|
glt->height = height;
|
|
|
|
glt->mipmap = mipmap;
|
|
|
|
|
|
|
|
GL_Bind(glt->texnum);
|
2000-06-10 21:40:31 +00:00
|
|
|
GL_Upload32 ((unsigned *) data, width, height, mipmap, true);
|
2000-06-10 03:36:28 +00:00
|
|
|
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
|
|
|
|
|
|
|
|
return glt->texnum;
|
|
|
|
}
|
|
|
|
|