mirror of
https://github.com/Shpoike/Quakespasm.git
synced 2024-11-10 15:31:39 +00:00
68ae3bfee7
Converts to rgb8 for now, so still only uses ldr lighting.
3425 lines
91 KiB
C
3425 lines
91 KiB
C
/*
|
|
Copyright (C) 1996-2001 Id Software, Inc.
|
|
Copyright (C) 2002-2009 John Fitzgibbons and others
|
|
Copyright (C) 2010-2014 QuakeSpasm developers
|
|
|
|
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.
|
|
|
|
*/
|
|
// models.c -- model loading and caching
|
|
|
|
// models are the only shared resource between a client and server running
|
|
// on the same machine.
|
|
|
|
#include "quakedef.h"
|
|
|
|
qmodel_t *loadmodel;
|
|
char loadname[32]; // for hunk tags
|
|
|
|
void Mod_LoadSpriteModel (qmodel_t *mod, void *buffer);
|
|
void Mod_LoadBrushModel (qmodel_t *mod, void *buffer);
|
|
void Mod_LoadAliasModel (qmodel_t *mod, void *buffer);
|
|
void Mod_LoadMD3Model (qmodel_t *mod, void *buffer);
|
|
qmodel_t *Mod_LoadModel (qmodel_t *mod, qboolean crash);
|
|
|
|
cvar_t external_ents = {"external_ents", "1", CVAR_ARCHIVE};
|
|
cvar_t gl_load24bit = {"gl_load24bit", "1", CVAR_ARCHIVE};
|
|
cvar_t mod_ignorelmscale = {"mod_ignorelmscale", "0"};
|
|
|
|
static byte *mod_novis;
|
|
static int mod_novis_capacity;
|
|
|
|
static byte *mod_decompressed;
|
|
static int mod_decompressed_capacity;
|
|
|
|
#define MAX_MOD_KNOWN 8192 /*spike -- new value, was 2048 in qs, 512 in vanilla. Needs to be big for big maps with many many inline models. */
|
|
qmodel_t mod_known[MAX_MOD_KNOWN];
|
|
int mod_numknown;
|
|
|
|
texture_t *r_notexture_mip; //johnfitz -- moved here from r_main.c
|
|
texture_t *r_notexture_mip2; //johnfitz -- used for non-lightmapped surfs with a missing texture
|
|
|
|
/*
|
|
===============
|
|
Mod_Init
|
|
===============
|
|
*/
|
|
void Mod_Init (void)
|
|
{
|
|
Cvar_RegisterVariable (&gl_subdivide_size);
|
|
Cvar_RegisterVariable (&external_ents);
|
|
Cvar_RegisterVariable (&gl_load24bit);
|
|
Cvar_RegisterVariable (&mod_ignorelmscale);
|
|
|
|
//johnfitz -- create notexture miptex
|
|
r_notexture_mip = (texture_t *) Hunk_AllocName (sizeof(texture_t), "r_notexture_mip");
|
|
strcpy (r_notexture_mip->name, "notexture");
|
|
r_notexture_mip->height = r_notexture_mip->width = 32;
|
|
|
|
r_notexture_mip2 = (texture_t *) Hunk_AllocName (sizeof(texture_t), "r_notexture_mip2");
|
|
strcpy (r_notexture_mip2->name, "notexture2");
|
|
r_notexture_mip2->height = r_notexture_mip2->width = 32;
|
|
//johnfitz
|
|
}
|
|
|
|
/*
|
|
===============
|
|
Mod_Extradata
|
|
|
|
Caches the data if needed
|
|
===============
|
|
*/
|
|
void *Mod_Extradata (qmodel_t *mod)
|
|
{
|
|
void *r;
|
|
|
|
r = Cache_Check (&mod->cache);
|
|
if (r)
|
|
return r;
|
|
|
|
Mod_LoadModel (mod, true);
|
|
|
|
if (!mod->cache.data)
|
|
Sys_Error ("Mod_Extradata: caching failed");
|
|
return mod->cache.data;
|
|
}
|
|
|
|
/*
|
|
===============
|
|
Mod_PointInLeaf
|
|
===============
|
|
*/
|
|
mleaf_t *Mod_PointInLeaf (vec3_t p, qmodel_t *model)
|
|
{
|
|
mnode_t *node;
|
|
float d;
|
|
mplane_t *plane;
|
|
|
|
if (!model || !model->nodes)
|
|
Sys_Error ("Mod_PointInLeaf: bad model");
|
|
|
|
node = model->nodes;
|
|
while (1)
|
|
{
|
|
if (node->contents < 0)
|
|
return (mleaf_t *)node;
|
|
plane = node->plane;
|
|
d = DotProduct (p,plane->normal) - plane->dist;
|
|
if (d > 0)
|
|
node = node->children[0];
|
|
else
|
|
node = node->children[1];
|
|
}
|
|
|
|
return NULL; // never reached
|
|
}
|
|
|
|
|
|
/*
|
|
===================
|
|
Mod_DecompressVis
|
|
===================
|
|
*/
|
|
byte *Mod_DecompressVis (byte *in, qmodel_t *model)
|
|
{
|
|
int c;
|
|
byte *out;
|
|
byte *outend;
|
|
int row;
|
|
|
|
row = (model->numleafs+7)>>3;
|
|
if (mod_decompressed == NULL || row > mod_decompressed_capacity)
|
|
{
|
|
mod_decompressed_capacity = row;
|
|
mod_decompressed = (byte *) realloc (mod_decompressed, mod_decompressed_capacity);
|
|
if (!mod_decompressed)
|
|
Sys_Error ("Mod_DecompressVis: realloc() failed on %d bytes", mod_decompressed_capacity);
|
|
}
|
|
out = mod_decompressed;
|
|
outend = mod_decompressed + row;
|
|
|
|
if (!in)
|
|
{ // no vis info, so make all visible
|
|
while (row)
|
|
{
|
|
*out++ = 0xff;
|
|
row--;
|
|
}
|
|
return mod_decompressed;
|
|
}
|
|
|
|
do
|
|
{
|
|
if (*in)
|
|
{
|
|
*out++ = *in++;
|
|
continue;
|
|
}
|
|
|
|
c = in[1];
|
|
in += 2;
|
|
if (c > row - (out - mod_decompressed))
|
|
c = row - (out - mod_decompressed); //now that we're dynamically allocating pvs buffers, we have to be more careful to avoid heap overflows with buggy maps.
|
|
while (c)
|
|
{
|
|
if (out == outend)
|
|
{
|
|
if(!model->viswarn) {
|
|
model->viswarn = true;
|
|
Con_Warning("Mod_DecompressVis: output overrun on model \"%s\"\n", model->name);
|
|
}
|
|
return mod_decompressed;
|
|
}
|
|
*out++ = 0;
|
|
c--;
|
|
}
|
|
} while (out - mod_decompressed < row);
|
|
|
|
return mod_decompressed;
|
|
}
|
|
|
|
byte *Mod_LeafPVS (mleaf_t *leaf, qmodel_t *model)
|
|
{
|
|
if (leaf == model->leafs)
|
|
return Mod_NoVisPVS (model);
|
|
return Mod_DecompressVis (leaf->compressed_vis, model);
|
|
}
|
|
|
|
byte *Mod_NoVisPVS (qmodel_t *model)
|
|
{
|
|
int pvsbytes;
|
|
|
|
pvsbytes = (model->numleafs+7)>>3;
|
|
if (mod_novis == NULL || pvsbytes > mod_novis_capacity)
|
|
{
|
|
mod_novis_capacity = pvsbytes;
|
|
mod_novis = (byte *) realloc (mod_novis, mod_novis_capacity);
|
|
if (!mod_novis)
|
|
Sys_Error ("Mod_NoVisPVS: realloc() failed on %d bytes", mod_novis_capacity);
|
|
|
|
memset(mod_novis, 0xff, mod_novis_capacity);
|
|
}
|
|
return mod_novis;
|
|
}
|
|
|
|
/*
|
|
===================
|
|
Mod_ClearAll
|
|
===================
|
|
*/
|
|
void Mod_ClearAll (void)
|
|
{
|
|
int i;
|
|
qmodel_t *mod;
|
|
|
|
for (i=0 , mod=mod_known ; i<mod_numknown ; i++, mod++)
|
|
if (mod->type != mod_alias)
|
|
{
|
|
mod->needload = true;
|
|
TexMgr_FreeTexturesForOwner (mod); //johnfitz
|
|
PScript_ClearSurfaceParticles(mod);
|
|
}
|
|
}
|
|
|
|
void Mod_ResetAll (void)
|
|
{
|
|
int i;
|
|
qmodel_t *mod;
|
|
|
|
//ericw -- free alias model VBOs
|
|
GLMesh_DeleteVertexBuffers ();
|
|
|
|
for (i=0 , mod=mod_known ; i<mod_numknown ; i++, mod++)
|
|
{
|
|
if (!mod->needload) //otherwise Mod_ClearAll() did it already
|
|
{
|
|
TexMgr_FreeTexturesForOwner (mod);
|
|
PScript_ClearSurfaceParticles(mod);
|
|
}
|
|
memset(mod, 0, sizeof(qmodel_t));
|
|
}
|
|
mod_numknown = 0;
|
|
}
|
|
|
|
/*
|
|
==================
|
|
Mod_FindName
|
|
|
|
==================
|
|
*/
|
|
qmodel_t *Mod_FindName (const char *name)
|
|
{
|
|
int i;
|
|
qmodel_t *mod;
|
|
|
|
if (!name[0])
|
|
Sys_Error ("Mod_FindName: NULL name"); //johnfitz -- was "Mod_ForName"
|
|
|
|
//
|
|
// search the currently loaded models
|
|
//
|
|
for (i=0 , mod=mod_known ; i<mod_numknown ; i++, mod++)
|
|
if (!strcmp (mod->name, name) )
|
|
break;
|
|
|
|
if (i == mod_numknown)
|
|
{
|
|
if (mod_numknown == MAX_MOD_KNOWN)
|
|
Sys_Error ("mod_numknown == MAX_MOD_KNOWN");
|
|
q_strlcpy (mod->name, name, MAX_QPATH);
|
|
mod->needload = true;
|
|
mod_numknown++;
|
|
}
|
|
|
|
return mod;
|
|
}
|
|
|
|
/*
|
|
==================
|
|
Mod_TouchModel
|
|
|
|
==================
|
|
*/
|
|
void Mod_TouchModel (const char *name)
|
|
{
|
|
qmodel_t *mod;
|
|
|
|
mod = Mod_FindName (name);
|
|
|
|
if (!mod->needload)
|
|
{
|
|
if (mod->type == mod_alias)
|
|
Cache_Check (&mod->cache);
|
|
}
|
|
}
|
|
|
|
/*
|
|
==================
|
|
Mod_LoadModel
|
|
|
|
Loads a model into the cache
|
|
==================
|
|
*/
|
|
qmodel_t *Mod_LoadModel (qmodel_t *mod, qboolean crash)
|
|
{
|
|
byte *buf;
|
|
byte stackbuf[1024]; // avoid dirtying the cache heap
|
|
int mod_type;
|
|
|
|
if (!mod->needload)
|
|
{
|
|
if (mod->type == mod_alias)
|
|
{
|
|
if (Cache_Check (&mod->cache))
|
|
return mod;
|
|
}
|
|
else
|
|
return mod; // not cached at all
|
|
}
|
|
|
|
//
|
|
// load the file
|
|
//
|
|
if (*mod->name == '*')
|
|
buf = NULL;
|
|
else
|
|
buf = COM_LoadStackFile (mod->name, stackbuf, sizeof(stackbuf), & mod->path_id);
|
|
if (!buf)
|
|
{
|
|
if (crash)
|
|
Host_Error ("Mod_LoadModel: %s not found", mod->name); //johnfitz -- was "Mod_NumForName"
|
|
else if (mod->name[0] == '*' && (mod->name[1] < '0' || mod->name[1] > '9'))
|
|
; //*foo doesn't warn, unless its *NUM. inline models. gah.
|
|
else
|
|
Con_Warning("Mod_LoadModel: %s not found\n", mod->name);
|
|
|
|
//avoid crashes
|
|
mod->needload = false;
|
|
mod->type = mod_ext_invalid;
|
|
mod->flags = 0;
|
|
|
|
Mod_SetExtraFlags (mod); //johnfitz. spike -- moved this to be generic, because most of the flags are anyway.
|
|
return mod;
|
|
}
|
|
|
|
//
|
|
// allocate a new model
|
|
//
|
|
COM_FileBase (mod->name, loadname, sizeof(loadname));
|
|
|
|
loadmodel = mod;
|
|
|
|
//
|
|
// fill it in
|
|
//
|
|
|
|
// call the apropriate loader
|
|
mod->needload = false;
|
|
|
|
mod_type = (buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24));
|
|
switch (mod_type)
|
|
{
|
|
case IDPOLYHEADER:
|
|
Mod_LoadAliasModel (mod, buf);
|
|
break;
|
|
|
|
case IDSPRITEHEADER:
|
|
Mod_LoadSpriteModel (mod, buf);
|
|
break;
|
|
|
|
//Spike -- md3 support
|
|
case (('I'<<0)+('D'<<8)+('P'<<16)+('3'<<24)): //md3
|
|
Mod_LoadMD3Model(mod, buf);
|
|
break;
|
|
|
|
//Spike -- added checks for a few other model types.
|
|
//this is useful because of the number of models with renamed extensions.
|
|
//that and its hard to test the extension stuff when this was crashing.
|
|
case (('R'<<0)+('A'<<8)+('P'<<16)+('O'<<24)): //h2mp
|
|
Con_Warning("%s is a hexen2-missionpack model (unsupported)\n", mod->name);
|
|
mod->type = mod_ext_invalid;
|
|
break;
|
|
case (('I'<<0)+('D'<<8)+('P'<<16)+('2'<<24)): //md2
|
|
Con_Warning("%s is an md2 (unsupported)\n", mod->name);
|
|
mod->type = mod_ext_invalid;
|
|
break;
|
|
case (('I'<<0)+('N'<<8)+('T'<<16)+('E'<<24)): //iqm
|
|
Con_Warning("%s is an iqm (unsupported)\n", mod->name);
|
|
mod->type = mod_ext_invalid;
|
|
break;
|
|
case (('D'<<0)+('A'<<8)+('R'<<16)+('K'<<24)): //dpm
|
|
Con_Warning("%s is an dpm (unsupported)\n", mod->name);
|
|
mod->type = mod_ext_invalid;
|
|
break;
|
|
case (('A'<<0)+('C'<<8)+('T'<<16)+('R'<<24)): //psk
|
|
Con_Warning("%s is a psk (unsupported)\n", mod->name);
|
|
mod->type = mod_ext_invalid;
|
|
break;
|
|
case (('I'<<0)+('B'<<8)+('S'<<16)+('P'<<24)): //q2/q3bsp
|
|
Con_Warning("%s is a q2/q3bsp (unsupported)\n", mod->name);
|
|
mod->type = mod_ext_invalid;
|
|
break;
|
|
|
|
default:
|
|
Mod_LoadBrushModel (mod, buf);
|
|
break;
|
|
}
|
|
|
|
if (crash && mod->type == mod_ext_invalid)
|
|
{ //any of those formats for a world map will be screwed up.
|
|
Sys_Error ("Mod_LoadModel: couldn't load %s", mod->name); //johnfitz -- was "Mod_NumForName"
|
|
return NULL;
|
|
}
|
|
|
|
Mod_SetExtraFlags (mod); //johnfitz. spike -- moved this to be generic, because most of the flags are anyway.
|
|
|
|
return mod;
|
|
}
|
|
|
|
/*
|
|
==================
|
|
Mod_ForName
|
|
|
|
Loads in a model for the given name
|
|
==================
|
|
*/
|
|
qmodel_t *Mod_ForName (const char *name, qboolean crash)
|
|
{
|
|
qmodel_t *mod;
|
|
|
|
mod = Mod_FindName (name);
|
|
|
|
return Mod_LoadModel (mod, crash);
|
|
}
|
|
|
|
|
|
/*
|
|
===============================================================================
|
|
|
|
BRUSHMODEL LOADING
|
|
|
|
===============================================================================
|
|
*/
|
|
|
|
static byte *mod_base;
|
|
|
|
|
|
typedef struct {
|
|
char lumpname[24]; // up to 23 chars, zero-padded
|
|
int fileofs; // from file start
|
|
int filelen;
|
|
} bspx_lump_t;
|
|
typedef struct {
|
|
char id[4]; // 'BSPX'
|
|
int numlumps;
|
|
bspx_lump_t lumps[1];
|
|
} bspx_header_t;
|
|
static char *bspxbase;
|
|
static bspx_header_t *bspxheader;
|
|
//supported lumps:
|
|
//RGBLIGHTING (.lit)
|
|
//LMSHIFT (.lit2)
|
|
//LMOFFSET (LMSHIFT helper)
|
|
//LMSTYLE (LMSHIFT helper)
|
|
|
|
//unsupported lumps ('documented' elsewhere):
|
|
//BRUSHLIST (because hulls suck)
|
|
//LIGHTINGDIR (.lux)
|
|
//LIGHTING_E5BGR9 (hdr lighting)
|
|
//VERTEXNORMALS (smooth shading with dlights/rtlights)
|
|
static void *Q1BSPX_FindLump(char *lumpname, int *lumpsize)
|
|
{
|
|
int i;
|
|
*lumpsize = 0;
|
|
if (!bspxheader)
|
|
return NULL;
|
|
|
|
for (i = 0; i < bspxheader->numlumps; i++)
|
|
{
|
|
if (!strncmp(bspxheader->lumps[i].lumpname, lumpname, 24))
|
|
{
|
|
*lumpsize = bspxheader->lumps[i].filelen;
|
|
return bspxbase + bspxheader->lumps[i].fileofs;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
static void Q1BSPX_Setup(qmodel_t *mod, char *filebase, unsigned int filelen, lump_t *lumps, int numlumps)
|
|
{
|
|
int i;
|
|
int offs = 0;
|
|
bspx_header_t *h;
|
|
qboolean misaligned = false;
|
|
|
|
bspxbase = filebase;
|
|
bspxheader = NULL;
|
|
|
|
for (i = 0; i < numlumps; i++, lumps++)
|
|
{
|
|
if ((lumps->fileofs & 3) && i != LUMP_ENTITIES)
|
|
misaligned = true;
|
|
if (offs < lumps->fileofs + lumps->filelen)
|
|
offs = lumps->fileofs + lumps->filelen;
|
|
}
|
|
if (misaligned)
|
|
Con_DWarning("%s contains misaligned lumps\n", mod->name);
|
|
offs = (offs + 3) & ~3;
|
|
if (offs + sizeof(*bspxheader) > filelen)
|
|
return; /*no space for it*/
|
|
h = (bspx_header_t*)(filebase + offs);
|
|
|
|
i = LittleLong(h->numlumps);
|
|
/*verify the header*/
|
|
if (strncmp(h->id, "BSPX", 4) ||
|
|
i < 0 ||
|
|
offs + sizeof(*h) + sizeof(h->lumps[0])*(i-1) > filelen)
|
|
return;
|
|
h->numlumps = i;
|
|
while(i-->0)
|
|
{
|
|
h->lumps[i].fileofs = LittleLong(h->lumps[i].fileofs);
|
|
h->lumps[i].filelen = LittleLong(h->lumps[i].filelen);
|
|
if (h->lumps[i].fileofs & 3)
|
|
Con_DWarning("%s contains misaligned bspx limp %s\n", mod->name, h->lumps[i].lumpname);
|
|
if ((unsigned int)h->lumps[i].fileofs + (unsigned int)h->lumps[i].filelen > filelen)
|
|
return;
|
|
}
|
|
|
|
bspxheader = h;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
Mod_CheckFullbrights -- johnfitz
|
|
=================
|
|
*/
|
|
qboolean Mod_CheckFullbrights (byte *pixels, int count)
|
|
{
|
|
int i;
|
|
for (i = 0; i < count; i++)
|
|
if (*pixels++ > 223)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
Mod_LoadTextures
|
|
=================
|
|
*/
|
|
void Mod_LoadTextures (lump_t *l)
|
|
{
|
|
int i, j, pixels, num, maxanim, altmax;
|
|
miptex_t *mt;
|
|
texture_t *tx, *tx2;
|
|
texture_t *anims[10];
|
|
texture_t *altanims[10];
|
|
dmiptexlump_t *m;
|
|
//johnfitz -- more variables
|
|
char texturename[64];
|
|
int nummiptex;
|
|
src_offset_t offset;
|
|
int mark, fwidth, fheight;
|
|
char filename[MAX_OSPATH], filename2[MAX_OSPATH], mapname[MAX_OSPATH];
|
|
byte *data;
|
|
extern byte *hunk_base;
|
|
//johnfitz
|
|
qboolean malloced; //spike
|
|
|
|
//johnfitz -- don't return early if no textures; still need to create dummy texture
|
|
if (!l->filelen)
|
|
{
|
|
Con_Printf ("Mod_LoadTextures: no textures in bsp file\n");
|
|
nummiptex = 0;
|
|
m = NULL; // avoid bogus compiler warning
|
|
}
|
|
else
|
|
{
|
|
m = (dmiptexlump_t *)(mod_base + l->fileofs);
|
|
m->nummiptex = LittleLong (m->nummiptex);
|
|
nummiptex = m->nummiptex;
|
|
}
|
|
//johnfitz
|
|
|
|
loadmodel->numtextures = nummiptex + 2; //johnfitz -- need 2 dummy texture chains for missing textures
|
|
loadmodel->textures = (texture_t **) Hunk_AllocName (loadmodel->numtextures * sizeof(*loadmodel->textures) , loadname);
|
|
|
|
for (i=0 ; i<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);
|
|
// spike -- quakespasm doesn't use the submips anyway
|
|
pixels = mt->width*mt->height;
|
|
//pixels = mt->width*mt->height/64*85;
|
|
|
|
tx = (texture_t *) Hunk_AllocName (sizeof(texture_t) +pixels, 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] = mt->offsets[j] + sizeof(texture_t) - sizeof(miptex_t);
|
|
// the pixels immediately follow the structures
|
|
|
|
if (!LittleLong (mt->offsets[0]))
|
|
{ // spike -- support tyrlight-ericw's -notex argument to avoid gpl violations from embedded textures
|
|
size_t x,y;
|
|
for(y=0;y<tx->width;y++)
|
|
for(x=0;x<tx->width;x++)
|
|
((byte*)(tx+1))[y*tx->width+x] = (((x>>2)^(y>>2))&1)?6:2;
|
|
}
|
|
else
|
|
{
|
|
// ericw -- check for pixels extending past the end of the lump.
|
|
// appears in the wild; e.g. jam2_tronyn.bsp (func_mapjam2),
|
|
// kellbase1.bsp (quoth), and can lead to a segfault if we read past
|
|
// the end of the .bsp file buffer
|
|
if (((byte*)(mt+1) + pixels) > (mod_base + l->fileofs + l->filelen))
|
|
{
|
|
Con_DPrintf("Texture %s extends past end of lump\n", mt->name);
|
|
pixels = q_max(0, (mod_base + l->fileofs + l->filelen) - (byte*)(mt+1));
|
|
}
|
|
|
|
//spike -- this is actually a pointless waste of memory.
|
|
//its not like this data will actually be used beyond this function in any gl renderer.
|
|
//this makes copying it pointless
|
|
//which in turn makes the pointer-to-array-of-pointers-to-texture a bit silly.
|
|
memcpy ( tx+1, mt+1, pixels);
|
|
}
|
|
|
|
tx->update_warp = false; //johnfitz
|
|
tx->warpimage = NULL; //johnfitz
|
|
tx->fullbright = NULL; //johnfitz
|
|
|
|
//johnfitz -- lots of changes
|
|
if (!isDedicated) //no texture uploading for dedicated server
|
|
{
|
|
if (!q_strncasecmp(tx->name,"sky",3)) //sky texture //also note -- was Q_strncmp, changed to match qbsp
|
|
Sky_LoadTexture (tx);
|
|
else if (tx->name[0] == '*') //warping texture
|
|
{
|
|
//external textures -- first look in "textures/mapname/" then look in "textures/"
|
|
mark = Hunk_LowMark();
|
|
COM_StripExtension (loadmodel->name + 5, mapname, sizeof(mapname));
|
|
q_snprintf (filename, sizeof(filename), "textures/%s/#%s", mapname, tx->name+1); //this also replaces the '*' with a '#'
|
|
data = !gl_load24bit.value?NULL:Image_LoadImage (filename, &fwidth, &fheight, &malloced);
|
|
if (!data)
|
|
{
|
|
q_snprintf (filename, sizeof(filename), "textures/#%s", tx->name+1);
|
|
data = !gl_load24bit.value?NULL:Image_LoadImage (filename, &fwidth, &fheight, &malloced);
|
|
}
|
|
|
|
//now load whatever we found
|
|
if (data) //load external image
|
|
{
|
|
q_strlcpy (texturename, filename, sizeof(texturename));
|
|
tx->gltexture = TexMgr_LoadImage (loadmodel, texturename, fwidth, fheight,
|
|
SRC_RGBA, data, filename, 0, TEXPREF_NONE);
|
|
}
|
|
else //use the texture from the bsp file
|
|
{
|
|
q_snprintf (texturename, sizeof(texturename), "%s:%s", loadmodel->name, tx->name);
|
|
offset = (src_offset_t)(mt+1) - (src_offset_t)mod_base;
|
|
tx->gltexture = TexMgr_LoadImage (loadmodel, texturename, tx->width, tx->height,
|
|
SRC_INDEXED, (byte *)(tx+1), loadmodel->name, offset, TEXPREF_NONE);
|
|
}
|
|
|
|
//now create the warpimage, using dummy data from the hunk to create the initial image
|
|
Hunk_Alloc (gl_warpimagesize*gl_warpimagesize*4); //make sure hunk is big enough so we don't reach an illegal address
|
|
Hunk_FreeToLowMark (mark);
|
|
q_snprintf (texturename, sizeof(texturename), "%s_warp", texturename);
|
|
tx->warpimage = TexMgr_LoadImage (loadmodel, texturename, gl_warpimagesize,
|
|
gl_warpimagesize, SRC_RGBA, hunk_base, "", (src_offset_t)hunk_base, TEXPREF_NOPICMIP | TEXPREF_WARPIMAGE);
|
|
tx->update_warp = true;
|
|
if (malloced)
|
|
free(data);
|
|
}
|
|
else //regular texture
|
|
{
|
|
// ericw -- fence textures
|
|
int extraflags;
|
|
|
|
extraflags = 0;
|
|
if (tx->name[0] == '{')
|
|
extraflags |= TEXPREF_ALPHA;
|
|
// ericw
|
|
|
|
//external textures -- first look in "textures/mapname/" then look in "textures/"
|
|
mark = Hunk_LowMark ();
|
|
COM_StripExtension (loadmodel->name + 5, mapname, sizeof(mapname));
|
|
q_snprintf (filename, sizeof(filename), "textures/%s/%s", mapname, tx->name);
|
|
data = !gl_load24bit.value?NULL:Image_LoadImage (filename, &fwidth, &fheight, &malloced);
|
|
if (!data)
|
|
{
|
|
q_snprintf (filename, sizeof(filename), "textures/%s", tx->name);
|
|
data = !gl_load24bit.value?NULL:Image_LoadImage (filename, &fwidth, &fheight, &malloced);
|
|
}
|
|
|
|
//now load whatever we found
|
|
if (data) //load external image
|
|
{
|
|
tx->gltexture = TexMgr_LoadImage (loadmodel, filename, fwidth, fheight,
|
|
SRC_RGBA, data, filename, 0, TEXPREF_MIPMAP | extraflags );
|
|
|
|
//now try to load glow/luma image from the same place
|
|
if (malloced)
|
|
free(data);
|
|
Hunk_FreeToLowMark (mark);
|
|
q_snprintf (filename2, sizeof(filename2), "%s_glow", filename);
|
|
data = !gl_load24bit.value?NULL:Image_LoadImage (filename2, &fwidth, &fheight, &malloced);
|
|
if (!data)
|
|
{
|
|
q_snprintf (filename2, sizeof(filename2), "%s_luma", filename);
|
|
data = !gl_load24bit.value?NULL:Image_LoadImage (filename2, &fwidth, &fheight, &malloced);
|
|
}
|
|
|
|
if (data)
|
|
tx->fullbright = TexMgr_LoadImage (loadmodel, filename2, fwidth, fheight,
|
|
SRC_RGBA, data, filename, 0, TEXPREF_MIPMAP | extraflags );
|
|
}
|
|
else //use the texture from the bsp file
|
|
{
|
|
q_snprintf (texturename, sizeof(texturename), "%s:%s", loadmodel->name, tx->name);
|
|
offset = (src_offset_t)(mt+1) - (src_offset_t)mod_base;
|
|
if (Mod_CheckFullbrights ((byte *)(tx+1), pixels))
|
|
{
|
|
tx->gltexture = TexMgr_LoadImage (loadmodel, texturename, tx->width, tx->height,
|
|
SRC_INDEXED, (byte *)(tx+1), loadmodel->name, offset, TEXPREF_MIPMAP | TEXPREF_NOBRIGHT | extraflags);
|
|
q_snprintf (texturename, sizeof(texturename), "%s:%s_glow", loadmodel->name, tx->name);
|
|
tx->fullbright = TexMgr_LoadImage (loadmodel, texturename, tx->width, tx->height,
|
|
SRC_INDEXED, (byte *)(tx+1), loadmodel->name, offset, TEXPREF_MIPMAP | TEXPREF_FULLBRIGHT | extraflags);
|
|
}
|
|
else
|
|
{
|
|
tx->gltexture = TexMgr_LoadImage (loadmodel, texturename, tx->width, tx->height,
|
|
SRC_INDEXED, (byte *)(tx+1), loadmodel->name, offset, TEXPREF_MIPMAP | extraflags);
|
|
}
|
|
}
|
|
if (malloced)
|
|
free(data);
|
|
Hunk_FreeToLowMark (mark);
|
|
}
|
|
}
|
|
//johnfitz
|
|
}
|
|
|
|
//johnfitz -- last 2 slots in array should be filled with dummy textures
|
|
loadmodel->textures[loadmodel->numtextures-2] = r_notexture_mip; //for lightmapped surfs
|
|
loadmodel->textures[loadmodel->numtextures-1] = r_notexture_mip2; //for SURF_DRAWTILED surfs
|
|
|
|
//
|
|
// sequence the animations
|
|
//
|
|
for (i=0 ; i<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));
|
|
|
|
maxanim = tx->name[1];
|
|
altmax = 0;
|
|
if (maxanim >= 'a' && maxanim <= 'z')
|
|
maxanim -= 'a' - 'A';
|
|
if (maxanim >= '0' && maxanim <= '9')
|
|
{
|
|
maxanim -= '0';
|
|
altmax = 0;
|
|
anims[maxanim] = tx;
|
|
maxanim++;
|
|
}
|
|
else if (maxanim >= 'A' && maxanim <= 'J')
|
|
{
|
|
altmax = maxanim - 'A';
|
|
maxanim = 0;
|
|
altanims[altmax] = tx;
|
|
altmax++;
|
|
}
|
|
else
|
|
Sys_Error ("Bad animating texture %s", tx->name);
|
|
|
|
for (j=i+1 ; j<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 > maxanim)
|
|
maxanim = 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<maxanim ; j++)
|
|
{
|
|
tx2 = anims[j];
|
|
if (!tx2)
|
|
Sys_Error ("Missing frame %i of %s",j, tx->name);
|
|
tx2->anim_total = maxanim * ANIM_CYCLE;
|
|
tx2->anim_min = j * ANIM_CYCLE;
|
|
tx2->anim_max = (j+1) * ANIM_CYCLE;
|
|
tx2->anim_next = anims[ (j+1)%maxanim ];
|
|
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 (maxanim)
|
|
tx2->alternate_anims = anims[0];
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
=================
|
|
Mod_LoadLighting -- johnfitz -- replaced with lit support code via lordhavoc
|
|
=================
|
|
*/
|
|
void Mod_LoadLighting (lump_t *l)
|
|
{
|
|
int i, mark;
|
|
byte *in, *out, *data;
|
|
byte d;
|
|
char litfilename[MAX_OSPATH];
|
|
unsigned int path_id;
|
|
int bspxsize;
|
|
|
|
loadmodel->lightdata = NULL;
|
|
// LordHavoc: check for a .lit file
|
|
q_strlcpy(litfilename, loadmodel->name, sizeof(litfilename));
|
|
COM_StripExtension(litfilename, litfilename, sizeof(litfilename));
|
|
q_strlcat(litfilename, ".lit", sizeof(litfilename));
|
|
mark = Hunk_LowMark();
|
|
data = (byte*) COM_LoadHunkFile (litfilename, &path_id);
|
|
if (data)
|
|
{
|
|
// use lit file only from the same gamedir as the map
|
|
// itself or from a searchpath with higher priority.
|
|
if (path_id < loadmodel->path_id)
|
|
{
|
|
Hunk_FreeToLowMark(mark);
|
|
Con_DPrintf("ignored %s from a gamedir with lower priority\n", litfilename);
|
|
}
|
|
else
|
|
if (data[0] == 'Q' && data[1] == 'L' && data[2] == 'I' && data[3] == 'T')
|
|
{
|
|
i = LittleLong(((int *)data)[1]);
|
|
if (i == 1)
|
|
{
|
|
if (8+l->filelen*3 == com_filesize)
|
|
{
|
|
Con_DPrintf2("%s loaded\n", litfilename);
|
|
loadmodel->lightdata = data + 8;
|
|
return;
|
|
}
|
|
Hunk_FreeToLowMark(mark);
|
|
Con_Printf("Outdated .lit file (%s should be %u bytes, not %u)\n", litfilename, 8+l->filelen*3, com_filesize);
|
|
}
|
|
else
|
|
{
|
|
Hunk_FreeToLowMark(mark);
|
|
Con_Printf("Unknown .lit file version (%d)\n", i);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Hunk_FreeToLowMark(mark);
|
|
Con_Printf("Corrupt .lit file (old version?), ignoring\n");
|
|
}
|
|
}
|
|
// LordHavoc: no .lit found, expand the white lighting data to color
|
|
if (!l->filelen)
|
|
return;
|
|
loadmodel->lightdata = Q1BSPX_FindLump("RGBLIGHTING", &bspxsize);
|
|
if (loadmodel->lightdata && bspxsize == l->filelen*3)
|
|
Con_DPrintf("bspx lighting loaded\n");
|
|
else
|
|
{
|
|
in = Q1BSPX_FindLump("LIGHTING_E5BGR9", &bspxsize);
|
|
if (in && bspxsize == l->filelen*4)
|
|
{ //we don't really support hdr lighting, but we downgrade it to ldr whenever there's no rgb data.
|
|
//FIXME: don't convert this stuff here. upload the data to the gpu with GL_EXT_shared_exponent (core in gl3)
|
|
loadmodel->lightdata = (byte *) Hunk_AllocName ( l->filelen*3, litfilename);
|
|
out = loadmodel->lightdata;
|
|
Con_DPrintf("bspx hdr->ldr lighting loaded\n");
|
|
for (i = 0;i < l->filelen;i++, in+=4)
|
|
{
|
|
static const float rgb9e5tab[32] = { //multipliers for the 9-bit mantissa, according to the biased mantissa
|
|
//aka: pow(2, biasedexponent - bias-bits) where bias is 15 and bits is 9
|
|
1.0/(1<<24), 1.0/(1<<23), 1.0/(1<<22), 1.0/(1<<21), 1.0/(1<<20), 1.0/(1<<19), 1.0/(1<<18), 1.0/(1<<17),
|
|
1.0/(1<<16), 1.0/(1<<15), 1.0/(1<<14), 1.0/(1<<13), 1.0/(1<<12), 1.0/(1<<11), 1.0/(1<<10), 1.0/(1<<9),
|
|
1.0/(1<<8), 1.0/(1<<7), 1.0/(1<<6), 1.0/(1<<5), 1.0/(1<<4), 1.0/(1<<3), 1.0/(1<<2), 1.0/(1<<1),
|
|
1.0, 1.0*(1<<1), 1.0*(1<<2), 1.0*(1<<3), 1.0*(1<<4), 1.0*(1<<5), 1.0*(1<<6), 1.0*(1<<7),
|
|
};
|
|
unsigned int e5bgr9 = *(unsigned int*)in;
|
|
float e = rgb9e5tab[e5bgr9>>27] * (1<<7); //we're converting to a scale that holds overbrights, so 1->128, its 2->255ish
|
|
*out++ = q_min(255, e*((e5bgr9>> 0)&0x1ff)); //red
|
|
*out++ = q_min(255, e*((e5bgr9>> 9)&0x1ff)); //green
|
|
*out++ = q_min(255, e*((e5bgr9>>18)&0x1ff)); //blue
|
|
}
|
|
}
|
|
else
|
|
{
|
|
loadmodel->lightdata = (byte *) Hunk_AllocName ( l->filelen*3, litfilename);
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
=================
|
|
Mod_LoadVisibility
|
|
=================
|
|
*/
|
|
void Mod_LoadVisibility (lump_t *l)
|
|
{
|
|
loadmodel->viswarn = false;
|
|
if (!l->filelen)
|
|
{
|
|
loadmodel->visdata = NULL;
|
|
return;
|
|
}
|
|
loadmodel->visdata = (byte *) Hunk_AllocName ( l->filelen, loadname);
|
|
memcpy (loadmodel->visdata, mod_base + l->fileofs, l->filelen);
|
|
}
|
|
|
|
|
|
/*
|
|
=================
|
|
Mod_LoadEntities
|
|
=================
|
|
*/
|
|
void Mod_LoadEntities (lump_t *l)
|
|
{
|
|
char entfilename[MAX_QPATH];
|
|
char *ents;
|
|
int mark;
|
|
unsigned int path_id;
|
|
|
|
if (! external_ents.value)
|
|
goto _load_embedded;
|
|
|
|
q_strlcpy(entfilename, loadmodel->name, sizeof(entfilename));
|
|
COM_StripExtension(entfilename, entfilename, sizeof(entfilename));
|
|
q_strlcat(entfilename, ".ent", sizeof(entfilename));
|
|
Con_DPrintf2("trying to load %s\n", entfilename);
|
|
mark = Hunk_LowMark();
|
|
ents = (char *) COM_LoadHunkFile (entfilename, &path_id);
|
|
if (ents)
|
|
{
|
|
// use ent file only from the same gamedir as the map
|
|
// itself or from a searchpath with higher priority.
|
|
if (path_id < loadmodel->path_id)
|
|
{
|
|
Hunk_FreeToLowMark(mark);
|
|
Con_DPrintf("ignored %s from a gamedir with lower priority\n", entfilename);
|
|
}
|
|
else
|
|
{
|
|
loadmodel->entities = ents;
|
|
Con_DPrintf("Loaded external entity file %s\n", entfilename);
|
|
return;
|
|
}
|
|
}
|
|
|
|
_load_embedded:
|
|
if (!l->filelen)
|
|
{
|
|
loadmodel->entities = NULL;
|
|
return;
|
|
}
|
|
loadmodel->entities = (char *) Hunk_AllocName ( l->filelen, loadname);
|
|
memcpy (loadmodel->entities, mod_base + l->fileofs, l->filelen);
|
|
}
|
|
|
|
|
|
/*
|
|
=================
|
|
Mod_ParseWorldspawnKey
|
|
=================
|
|
(Blame Spike)
|
|
This just quickly scans the worldspawn entity for a single key. Returning both _prefixed and non prefixed keys.
|
|
(wantkey argument should not have a _prefix.)
|
|
*/
|
|
const char *Mod_ParseWorldspawnKey(qmodel_t *mod, const char *wantkey, char *buffer, size_t sizeofbuffer)
|
|
{
|
|
char foundkey[128];
|
|
const char *data = COM_Parse(mod->entities);
|
|
|
|
if (data && com_token[0] == '{')
|
|
{
|
|
while (1)
|
|
{
|
|
data = COM_Parse(data);
|
|
if (!data)
|
|
break; // error
|
|
if (com_token[0] == '}')
|
|
break; // end of worldspawn
|
|
if (com_token[0] == '_')
|
|
strcpy(foundkey, com_token + 1);
|
|
else
|
|
strcpy(foundkey, com_token);
|
|
data = COM_Parse(data);
|
|
if (!data)
|
|
break; // error
|
|
if (!strcmp(wantkey, foundkey))
|
|
{
|
|
q_strlcpy(buffer, com_token, sizeofbuffer);
|
|
return buffer;
|
|
}
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/*
|
|
=================
|
|
Mod_LoadVertexes
|
|
=================
|
|
*/
|
|
void Mod_LoadVertexes (lump_t *l)
|
|
{
|
|
dvertex_t *in;
|
|
mvertex_t *out;
|
|
int i, count;
|
|
|
|
in = (dvertex_t *)(mod_base + l->fileofs);
|
|
if (l->filelen % sizeof(*in))
|
|
Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
|
|
count = l->filelen / sizeof(*in);
|
|
out = (mvertex_t *) Hunk_AllocName ( count*sizeof(*out), loadname);
|
|
|
|
loadmodel->vertexes = out;
|
|
loadmodel->numvertexes = count;
|
|
|
|
for (i=0 ; i<count ; i++, in++, out++)
|
|
{
|
|
out->position[0] = LittleFloat (in->point[0]);
|
|
out->position[1] = LittleFloat (in->point[1]);
|
|
out->position[2] = LittleFloat (in->point[2]);
|
|
}
|
|
}
|
|
|
|
/*
|
|
=================
|
|
Mod_LoadEdges
|
|
=================
|
|
*/
|
|
void Mod_LoadEdges (lump_t *l, int bsp2)
|
|
{
|
|
medge_t *out;
|
|
int i, count;
|
|
|
|
if (bsp2)
|
|
{
|
|
dledge_t *in = (dledge_t *)(mod_base + l->fileofs);
|
|
|
|
if (l->filelen % sizeof(*in))
|
|
Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
|
|
|
|
count = l->filelen / sizeof(*in);
|
|
out = (medge_t *) Hunk_AllocName ( (count + 1) * sizeof(*out), loadname);
|
|
|
|
loadmodel->edges = out;
|
|
loadmodel->numedges = count;
|
|
|
|
for (i=0 ; i<count ; i++, in++, out++)
|
|
{
|
|
out->v[0] = LittleLong(in->v[0]);
|
|
out->v[1] = LittleLong(in->v[1]);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dsedge_t *in = (dsedge_t *)(mod_base + l->fileofs);
|
|
|
|
if (l->filelen % sizeof(*in))
|
|
Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
|
|
|
|
count = l->filelen / sizeof(*in);
|
|
out = (medge_t *) Hunk_AllocName ( (count + 1) * sizeof(*out), loadname);
|
|
|
|
loadmodel->edges = out;
|
|
loadmodel->numedges = count;
|
|
|
|
for (i=0 ; i<count ; i++, in++, out++)
|
|
{
|
|
out->v[0] = (unsigned short)LittleShort(in->v[0]);
|
|
out->v[1] = (unsigned short)LittleShort(in->v[1]);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
=================
|
|
Mod_LoadTexinfo
|
|
=================
|
|
*/
|
|
void Mod_LoadTexinfo (lump_t *l)
|
|
{
|
|
texinfo_t *in;
|
|
mtexinfo_t *out;
|
|
int i, j, count, miptex;
|
|
float len1, len2;
|
|
int missing = 0; //johnfitz
|
|
|
|
in = (texinfo_t *)(mod_base + l->fileofs);
|
|
if (l->filelen % sizeof(*in))
|
|
Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
|
|
count = l->filelen / sizeof(*in);
|
|
out = (mtexinfo_t *) Hunk_AllocName ( count*sizeof(*out), loadname);
|
|
|
|
loadmodel->texinfo = out;
|
|
loadmodel->numtexinfo = count;
|
|
|
|
for (i=0 ; i<count ; i++, in++, out++)
|
|
{
|
|
for (j=0 ; j<4 ; j++)
|
|
{
|
|
out->vecs[0][j] = LittleFloat (in->vecs[0][j]);
|
|
out->vecs[1][j] = LittleFloat (in->vecs[1][j]);
|
|
}
|
|
len1 = VectorLength (out->vecs[0]);
|
|
len2 = VectorLength (out->vecs[1]);
|
|
len1 = (len1 + len2)/2;
|
|
if (len1 < 0.32)
|
|
out->mipadjust = 4;
|
|
else if (len1 < 0.49)
|
|
out->mipadjust = 3;
|
|
else if (len1 < 0.99)
|
|
out->mipadjust = 2;
|
|
else
|
|
out->mipadjust = 1;
|
|
#if 0
|
|
if (len1 + len2 < 0.001)
|
|
out->mipadjust = 1; // don't crash
|
|
else
|
|
out->mipadjust = 1 / floor((len1+len2)/2 + 0.1);
|
|
#endif
|
|
|
|
miptex = LittleLong (in->miptex);
|
|
out->flags = LittleLong (in->flags);
|
|
|
|
//johnfitz -- rewrote this section
|
|
if (miptex >= loadmodel->numtextures-1 || !loadmodel->textures[miptex])
|
|
{
|
|
if (out->flags & TEX_SPECIAL)
|
|
out->texture = loadmodel->textures[loadmodel->numtextures-1];
|
|
else
|
|
out->texture = loadmodel->textures[loadmodel->numtextures-2];
|
|
out->flags |= TEX_MISSING;
|
|
missing++;
|
|
}
|
|
else
|
|
{
|
|
out->texture = loadmodel->textures[miptex];
|
|
}
|
|
//johnfitz
|
|
}
|
|
|
|
//johnfitz: report missing textures
|
|
if (missing && loadmodel->numtextures > 1)
|
|
Con_Printf ("Mod_LoadTexinfo: %d texture(s) missing from BSP file\n", missing);
|
|
//johnfitz
|
|
}
|
|
|
|
/*
|
|
================
|
|
CalcSurfaceExtents
|
|
|
|
Fills in s->texturemins[] and s->extents[]
|
|
================
|
|
*/
|
|
void CalcSurfaceExtents (msurface_t *s)
|
|
{
|
|
float mins[2], maxs[2], val;
|
|
int i,j, e;
|
|
mvertex_t *v;
|
|
mtexinfo_t *tex;
|
|
int bmins[2], bmaxs[2];
|
|
int maxextent, lmscale;
|
|
|
|
mins[0] = mins[1] = FLT_MAX;
|
|
maxs[0] = maxs[1] = -FLT_MAX;
|
|
|
|
tex = s->texinfo;
|
|
|
|
for (i=0 ; i<s->numedges ; i++)
|
|
{
|
|
e = loadmodel->surfedges[s->firstedge+i];
|
|
if (e >= 0)
|
|
v = &loadmodel->vertexes[loadmodel->edges[e].v[0]];
|
|
else
|
|
v = &loadmodel->vertexes[loadmodel->edges[-e].v[1]];
|
|
|
|
for (j=0 ; j<2 ; j++)
|
|
{
|
|
/* The following calculation is sensitive to floating-point
|
|
* precision. It needs to produce the same result that the
|
|
* light compiler does, because R_BuildLightMap uses surf->
|
|
* extents to know the width/height of a surface's lightmap,
|
|
* and incorrect rounding here manifests itself as patches
|
|
* of "corrupted" looking lightmaps.
|
|
* Most light compilers are win32 executables, so they use
|
|
* x87 floating point. This means the multiplies and adds
|
|
* are done at 80-bit precision, and the result is rounded
|
|
* down to 32-bits and stored in val.
|
|
* Adding the casts to double seems to be good enough to fix
|
|
* lighting glitches when Quakespasm is compiled as x86_64
|
|
* and using SSE2 floating-point. A potential trouble spot
|
|
* is the hallway at the beginning of mfxsp17. -- ericw
|
|
*/
|
|
val = ((double)v->position[0] * (double)tex->vecs[j][0]) +
|
|
((double)v->position[1] * (double)tex->vecs[j][1]) +
|
|
((double)v->position[2] * (double)tex->vecs[j][2]) +
|
|
(double)tex->vecs[j][3];
|
|
|
|
if (val < mins[j])
|
|
mins[j] = val;
|
|
if (val > maxs[j])
|
|
maxs[j] = val;
|
|
}
|
|
}
|
|
|
|
lmscale = 1<<s->lmshift;
|
|
maxextent = q_max(LMBLOCK_WIDTH,LMBLOCK_HEIGHT)*lmscale;
|
|
|
|
for (i=0 ; i<2 ; i++)
|
|
{
|
|
bmins[i] = floor(mins[i]/lmscale);
|
|
bmaxs[i] = ceil(maxs[i]/lmscale);
|
|
|
|
s->texturemins[i] = bmins[i] * lmscale;
|
|
s->extents[i] = (bmaxs[i] - bmins[i]) * lmscale;
|
|
|
|
if ( !(tex->flags & TEX_SPECIAL) && s->extents[i] > maxextent) //johnfitz -- was 512 in glquake, 256 in winquake
|
|
{
|
|
s->extents[i] = 1;
|
|
// Sys_Error ("Bad surface extents");
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
Mod_PolyForUnlitSurface -- johnfitz -- creates polys for unlightmapped surfaces (sky and water)
|
|
|
|
TODO: merge this into BuildSurfaceDisplayList?
|
|
================
|
|
*/
|
|
void Mod_PolyForUnlitSurface (msurface_t *fa)
|
|
{
|
|
vec3_t verts[64];
|
|
int numverts, i, lindex;
|
|
float *vec;
|
|
glpoly_t *poly;
|
|
float texscale;
|
|
|
|
if (fa->flags & (SURF_DRAWTURB | SURF_DRAWSKY))
|
|
texscale = (1.0/128.0); //warp animation repeats every 128
|
|
else
|
|
texscale = (1.0/32.0); //to match r_notexture_mip
|
|
|
|
// convert edges back to a normal polygon
|
|
numverts = 0;
|
|
for (i=0 ; i<fa->numedges ; i++)
|
|
{
|
|
lindex = loadmodel->surfedges[fa->firstedge + i];
|
|
|
|
if (lindex > 0)
|
|
vec = loadmodel->vertexes[loadmodel->edges[lindex].v[0]].position;
|
|
else
|
|
vec = loadmodel->vertexes[loadmodel->edges[-lindex].v[1]].position;
|
|
VectorCopy (vec, verts[numverts]);
|
|
numverts++;
|
|
}
|
|
|
|
//create the poly
|
|
poly = (glpoly_t *) Hunk_Alloc (sizeof(glpoly_t) + (numverts-4) * VERTEXSIZE*sizeof(float));
|
|
poly->next = NULL;
|
|
fa->polys = poly;
|
|
poly->numverts = numverts;
|
|
for (i=0, vec=(float *)verts; i<numverts; i++, vec+= 3)
|
|
{
|
|
VectorCopy (vec, poly->verts[i]);
|
|
poly->verts[i][3] = DotProduct(vec, fa->texinfo->vecs[0]) * texscale;
|
|
poly->verts[i][4] = DotProduct(vec, fa->texinfo->vecs[1]) * texscale;
|
|
}
|
|
}
|
|
|
|
/*
|
|
=================
|
|
Mod_CalcSurfaceBounds -- johnfitz -- calculate bounding box for per-surface frustum culling
|
|
=================
|
|
*/
|
|
void Mod_CalcSurfaceBounds (msurface_t *s)
|
|
{
|
|
int i, e;
|
|
mvertex_t *v;
|
|
|
|
s->mins[0] = s->mins[1] = s->mins[2] = FLT_MAX;
|
|
s->maxs[0] = s->maxs[1] = s->maxs[2] = -FLT_MAX;
|
|
|
|
for (i=0 ; i<s->numedges ; i++)
|
|
{
|
|
e = loadmodel->surfedges[s->firstedge+i];
|
|
if (e >= 0)
|
|
v = &loadmodel->vertexes[loadmodel->edges[e].v[0]];
|
|
else
|
|
v = &loadmodel->vertexes[loadmodel->edges[-e].v[1]];
|
|
|
|
if (s->mins[0] > v->position[0])
|
|
s->mins[0] = v->position[0];
|
|
if (s->mins[1] > v->position[1])
|
|
s->mins[1] = v->position[1];
|
|
if (s->mins[2] > v->position[2])
|
|
s->mins[2] = v->position[2];
|
|
|
|
if (s->maxs[0] < v->position[0])
|
|
s->maxs[0] = v->position[0];
|
|
if (s->maxs[1] < v->position[1])
|
|
s->maxs[1] = v->position[1];
|
|
if (s->maxs[2] < v->position[2])
|
|
s->maxs[2] = v->position[2];
|
|
}
|
|
}
|
|
|
|
/*
|
|
=================
|
|
Mod_LoadFaces
|
|
=================
|
|
*/
|
|
void Mod_LoadFaces (lump_t *l, qboolean bsp2)
|
|
{
|
|
dsface_t *ins;
|
|
dlface_t *inl;
|
|
msurface_t *out;
|
|
int i, count, surfnum, lofs, shift;
|
|
int planenum, side, texinfon;
|
|
|
|
unsigned char *lmshift = NULL, defaultshift = 4;
|
|
unsigned int *lmoffset = NULL;
|
|
unsigned char *lmstyle = NULL;
|
|
int lumpsize;
|
|
char scalebuf[16];
|
|
|
|
if (bsp2)
|
|
{
|
|
ins = NULL;
|
|
inl = (dlface_t *)(mod_base + l->fileofs);
|
|
if (l->filelen % sizeof(*inl))
|
|
Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
|
|
count = l->filelen / sizeof(*inl);
|
|
}
|
|
else
|
|
{
|
|
ins = (dsface_t *)(mod_base + l->fileofs);
|
|
inl = NULL;
|
|
if (l->filelen % sizeof(*ins))
|
|
Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
|
|
count = l->filelen / sizeof(*ins);
|
|
}
|
|
out = (msurface_t *)Hunk_AllocName ( count*sizeof(*out), loadname);
|
|
|
|
//johnfitz -- warn mappers about exceeding old limits
|
|
if (count > 32767 && !bsp2)
|
|
Con_DWarning ("%i faces exceeds standard limit of 32767.\n", count);
|
|
//johnfitz
|
|
|
|
if (!mod_ignorelmscale.value)
|
|
{
|
|
lmshift = Q1BSPX_FindLump("LMSHIFT", &lumpsize);
|
|
if (lumpsize != sizeof(*lmshift)*count)
|
|
lmshift = NULL;
|
|
lmoffset = Q1BSPX_FindLump("LMOFFSET", &lumpsize);
|
|
if (lumpsize != sizeof(*lmoffset)*count)
|
|
lmoffset = NULL;
|
|
lmstyle = Q1BSPX_FindLump("LMSTYLE", &lumpsize);
|
|
if (lumpsize != sizeof(*lmstyle)*MAXLIGHTMAPS*count)
|
|
lmstyle = NULL;
|
|
|
|
if (Mod_ParseWorldspawnKey(loadmodel, "lightmap_scale", scalebuf, sizeof(scalebuf)))
|
|
{
|
|
i = atoi(scalebuf);
|
|
for (defaultshift = 0; (1<<defaultshift) < i && defaultshift < 254; defaultshift++)
|
|
break;
|
|
}
|
|
}
|
|
|
|
loadmodel->surfaces = out;
|
|
loadmodel->numsurfaces = count;
|
|
|
|
for (surfnum=0 ; surfnum<count ; surfnum++, out++)
|
|
{
|
|
if (bsp2)
|
|
{ //32bit datatypes
|
|
out->firstedge = LittleLong(inl->firstedge);
|
|
out->numedges = LittleLong(inl->numedges);
|
|
planenum = LittleLong(inl->planenum);
|
|
side = LittleLong(inl->side);
|
|
texinfon = LittleLong (inl->texinfo);
|
|
for (i=0 ; i<MAXLIGHTMAPS ; i++)
|
|
out->styles[i] = inl->styles[i];
|
|
lofs = LittleLong(inl->lightofs);
|
|
inl++;
|
|
}
|
|
else
|
|
{ //16bit datatypes
|
|
out->firstedge = LittleLong(ins->firstedge);
|
|
out->numedges = LittleShort(ins->numedges);
|
|
planenum = LittleShort(ins->planenum);
|
|
side = LittleShort(ins->side);
|
|
texinfon = LittleShort (ins->texinfo);
|
|
for (i=0 ; i<MAXLIGHTMAPS ; i++)
|
|
out->styles[i] = ins->styles[i];
|
|
lofs = LittleLong(ins->lightofs);
|
|
ins++;
|
|
}
|
|
shift = defaultshift;
|
|
//bspx overrides (for lmscale)
|
|
if (lmstyle)
|
|
shift = lmshift[surfnum];
|
|
if (lmoffset)
|
|
lofs = LittleLong(lmoffset[surfnum]);
|
|
if (lmstyle)
|
|
for (i=0 ; i<MAXLIGHTMAPS ; i++)
|
|
out->styles[i] = lmstyle[surfnum*MAXLIGHTMAPS+i];
|
|
|
|
out->flags = 0;
|
|
|
|
if (side)
|
|
out->flags |= SURF_PLANEBACK;
|
|
|
|
out->plane = loadmodel->planes + planenum;
|
|
out->texinfo = loadmodel->texinfo + texinfon;
|
|
out->lmshift = shift;
|
|
|
|
CalcSurfaceExtents (out);
|
|
|
|
Mod_CalcSurfaceBounds (out); //johnfitz -- for per-surface frustum culling
|
|
|
|
// lighting info
|
|
if (lofs == -1)
|
|
out->samples = NULL;
|
|
else
|
|
out->samples = loadmodel->lightdata + (lofs * 3); //johnfitz -- lit support via lordhavoc (was "+ i")
|
|
|
|
//johnfitz -- this section rewritten
|
|
if (!q_strncasecmp(out->texinfo->texture->name,"sky",3)) // sky surface //also note -- was Q_strncmp, changed to match qbsp
|
|
{
|
|
out->flags |= (SURF_DRAWSKY | SURF_DRAWTILED);
|
|
Mod_PolyForUnlitSurface (out); //no more subdivision
|
|
}
|
|
else if (out->texinfo->texture->name[0] == '*') // warp surface
|
|
{
|
|
out->flags |= (SURF_DRAWTURB | SURF_DRAWTILED);
|
|
|
|
// detect special liquid types
|
|
if (!strncmp (out->texinfo->texture->name, "*lava", 5))
|
|
out->flags |= SURF_DRAWLAVA;
|
|
else if (!strncmp (out->texinfo->texture->name, "*slime", 6))
|
|
out->flags |= SURF_DRAWSLIME;
|
|
else if (!strncmp (out->texinfo->texture->name, "*tele", 5))
|
|
out->flags |= SURF_DRAWTELE;
|
|
else out->flags |= SURF_DRAWWATER;
|
|
|
|
Mod_PolyForUnlitSurface (out);
|
|
GL_SubdivideSurface (out);
|
|
}
|
|
else if (out->texinfo->texture->name[0] == '{') // ericw -- fence textures
|
|
{
|
|
out->flags |= SURF_DRAWFENCE;
|
|
}
|
|
else if (out->texinfo->flags & TEX_MISSING) // texture is missing from bsp
|
|
{
|
|
if (out->samples) //lightmapped
|
|
{
|
|
out->flags |= SURF_NOTEXTURE;
|
|
}
|
|
else // not lightmapped
|
|
{
|
|
out->flags |= (SURF_NOTEXTURE | SURF_DRAWTILED);
|
|
Mod_PolyForUnlitSurface (out);
|
|
}
|
|
}
|
|
//johnfitz
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
=================
|
|
Mod_SetParent
|
|
=================
|
|
*/
|
|
void Mod_SetParent (mnode_t *node, mnode_t *parent)
|
|
{
|
|
node->parent = parent;
|
|
if (node->contents < 0)
|
|
return;
|
|
Mod_SetParent (node->children[0], node);
|
|
Mod_SetParent (node->children[1], node);
|
|
}
|
|
|
|
/*
|
|
=================
|
|
Mod_LoadNodes
|
|
=================
|
|
*/
|
|
void Mod_LoadNodes_S (lump_t *l)
|
|
{
|
|
int i, j, count, p;
|
|
dsnode_t *in;
|
|
mnode_t *out;
|
|
|
|
in = (dsnode_t *)(mod_base + l->fileofs);
|
|
if (l->filelen % sizeof(*in))
|
|
Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
|
|
count = l->filelen / sizeof(*in);
|
|
out = (mnode_t *) Hunk_AllocName ( count*sizeof(*out), loadname);
|
|
|
|
//johnfitz -- warn mappers about exceeding old limits
|
|
if (count > 32767)
|
|
Con_DWarning ("%i nodes exceeds standard limit of 32767.\n", count);
|
|
//johnfitz
|
|
|
|
loadmodel->nodes = out;
|
|
loadmodel->numnodes = count;
|
|
|
|
for (i=0 ; i<count ; i++, in++, out++)
|
|
{
|
|
for (j=0 ; j<3 ; j++)
|
|
{
|
|
out->minmaxs[j] = LittleShort (in->mins[j]);
|
|
out->minmaxs[3+j] = LittleShort (in->maxs[j]);
|
|
}
|
|
|
|
p = LittleLong(in->planenum);
|
|
out->plane = loadmodel->planes + p;
|
|
|
|
out->firstsurface = (unsigned short)LittleShort (in->firstface); //johnfitz -- explicit cast as unsigned short
|
|
out->numsurfaces = (unsigned short)LittleShort (in->numfaces); //johnfitz -- explicit cast as unsigned short
|
|
|
|
for (j=0 ; j<2 ; j++)
|
|
{
|
|
//johnfitz -- hack to handle nodes > 32k, adapted from darkplaces
|
|
p = (unsigned short)LittleShort(in->children[j]);
|
|
if (p < count)
|
|
out->children[j] = loadmodel->nodes + p;
|
|
else
|
|
{
|
|
p = 65535 - p; //note this uses 65535 intentionally, -1 is leaf 0
|
|
if (p < loadmodel->numleafs)
|
|
out->children[j] = (mnode_t *)(loadmodel->leafs + p);
|
|
else
|
|
{
|
|
Con_Printf("Mod_LoadNodes: invalid leaf index %i (file has only %i leafs)\n", p, loadmodel->numleafs);
|
|
out->children[j] = (mnode_t *)(loadmodel->leafs); //map it to the solid leaf
|
|
}
|
|
}
|
|
//johnfitz
|
|
}
|
|
}
|
|
}
|
|
|
|
void Mod_LoadNodes_L1 (lump_t *l)
|
|
{
|
|
int i, j, count, p;
|
|
dl1node_t *in;
|
|
mnode_t *out;
|
|
|
|
in = (dl1node_t *)(mod_base + l->fileofs);
|
|
if (l->filelen % sizeof(*in))
|
|
Sys_Error ("Mod_LoadNodes: funny lump size in %s",loadmodel->name);
|
|
|
|
count = l->filelen / sizeof(*in);
|
|
out = (mnode_t *)Hunk_AllocName ( count*sizeof(*out), loadname);
|
|
|
|
loadmodel->nodes = out;
|
|
loadmodel->numnodes = count;
|
|
|
|
for (i=0 ; i<count ; i++, in++, out++)
|
|
{
|
|
for (j=0 ; j<3 ; j++)
|
|
{
|
|
out->minmaxs[j] = LittleShort (in->mins[j]);
|
|
out->minmaxs[3+j] = LittleShort (in->maxs[j]);
|
|
}
|
|
|
|
p = LittleLong(in->planenum);
|
|
out->plane = loadmodel->planes + p;
|
|
|
|
out->firstsurface = LittleLong (in->firstface); //johnfitz -- explicit cast as unsigned short
|
|
out->numsurfaces = LittleLong (in->numfaces); //johnfitz -- explicit cast as unsigned short
|
|
|
|
for (j=0 ; j<2 ; j++)
|
|
{
|
|
//johnfitz -- hack to handle nodes > 32k, adapted from darkplaces
|
|
p = LittleLong(in->children[j]);
|
|
if (p >= 0 && p < count)
|
|
out->children[j] = loadmodel->nodes + p;
|
|
else
|
|
{
|
|
p = 0xffffffff - p; //note this uses 65535 intentionally, -1 is leaf 0
|
|
if (p >= 0 && p < loadmodel->numleafs)
|
|
out->children[j] = (mnode_t *)(loadmodel->leafs + p);
|
|
else
|
|
{
|
|
Con_Printf("Mod_LoadNodes: invalid leaf index %i (file has only %i leafs)\n", p, loadmodel->numleafs);
|
|
out->children[j] = (mnode_t *)(loadmodel->leafs); //map it to the solid leaf
|
|
}
|
|
}
|
|
//johnfitz
|
|
}
|
|
}
|
|
}
|
|
|
|
void Mod_LoadNodes_L2 (lump_t *l)
|
|
{
|
|
int i, j, count, p;
|
|
dl2node_t *in;
|
|
mnode_t *out;
|
|
|
|
in = (dl2node_t *)(mod_base + l->fileofs);
|
|
if (l->filelen % sizeof(*in))
|
|
Sys_Error ("Mod_LoadNodes: funny lump size in %s",loadmodel->name);
|
|
|
|
count = l->filelen / sizeof(*in);
|
|
out = (mnode_t *)Hunk_AllocName ( count*sizeof(*out), loadname);
|
|
|
|
loadmodel->nodes = out;
|
|
loadmodel->numnodes = count;
|
|
|
|
for (i=0 ; i<count ; i++, in++, out++)
|
|
{
|
|
for (j=0 ; j<3 ; j++)
|
|
{
|
|
out->minmaxs[j] = LittleFloat (in->mins[j]);
|
|
out->minmaxs[3+j] = LittleFloat (in->maxs[j]);
|
|
}
|
|
|
|
p = LittleLong(in->planenum);
|
|
out->plane = loadmodel->planes + p;
|
|
|
|
out->firstsurface = LittleLong (in->firstface); //johnfitz -- explicit cast as unsigned short
|
|
out->numsurfaces = LittleLong (in->numfaces); //johnfitz -- explicit cast as unsigned short
|
|
|
|
for (j=0 ; j<2 ; j++)
|
|
{
|
|
//johnfitz -- hack to handle nodes > 32k, adapted from darkplaces
|
|
p = LittleLong(in->children[j]);
|
|
if (p > 0 && p < count)
|
|
out->children[j] = loadmodel->nodes + p;
|
|
else
|
|
{
|
|
p = 0xffffffff - p; //note this uses 65535 intentionally, -1 is leaf 0
|
|
if (p >= 0 && p < loadmodel->numleafs)
|
|
out->children[j] = (mnode_t *)(loadmodel->leafs + p);
|
|
else
|
|
{
|
|
Con_Printf("Mod_LoadNodes: invalid leaf index %i (file has only %i leafs)\n", p, loadmodel->numleafs);
|
|
out->children[j] = (mnode_t *)(loadmodel->leafs); //map it to the solid leaf
|
|
}
|
|
}
|
|
//johnfitz
|
|
}
|
|
}
|
|
}
|
|
|
|
void Mod_LoadNodes (lump_t *l, int bsp2)
|
|
{
|
|
if (bsp2 == 2)
|
|
Mod_LoadNodes_L2(l);
|
|
else if (bsp2)
|
|
Mod_LoadNodes_L1(l);
|
|
else
|
|
Mod_LoadNodes_S(l);
|
|
|
|
// Mod_SetParent (loadmodel->nodes, NULL); // sets nodes and leafs
|
|
}
|
|
|
|
void Mod_ProcessLeafs_S (dsleaf_t *in, int filelen)
|
|
{
|
|
mleaf_t *out;
|
|
int i, j, count, p;
|
|
|
|
if (filelen % sizeof(*in))
|
|
Sys_Error ("Mod_ProcessLeafs: funny lump size in %s", loadmodel->name);
|
|
count = filelen / sizeof(*in);
|
|
out = (mleaf_t *) Hunk_AllocName ( count*sizeof(*out), loadname);
|
|
|
|
//johnfitz
|
|
if (count > 32767)
|
|
Host_Error ("Mod_LoadLeafs: %i leafs exceeds limit of 32767.\n", count);
|
|
//johnfitz
|
|
|
|
loadmodel->leafs = out;
|
|
loadmodel->numleafs = count;
|
|
|
|
for (i=0 ; i<count ; i++, in++, out++)
|
|
{
|
|
for (j=0 ; j<3 ; j++)
|
|
{
|
|
out->minmaxs[j] = LittleShort (in->mins[j]);
|
|
out->minmaxs[3+j] = LittleShort (in->maxs[j]);
|
|
}
|
|
|
|
p = LittleLong(in->contents);
|
|
out->contents = p;
|
|
|
|
out->firstmarksurface = loadmodel->marksurfaces + (unsigned short)LittleShort(in->firstmarksurface); //johnfitz -- unsigned short
|
|
out->nummarksurfaces = (unsigned short)LittleShort(in->nummarksurfaces); //johnfitz -- unsigned short
|
|
|
|
p = LittleLong(in->visofs);
|
|
if (p == -1)
|
|
out->compressed_vis = NULL;
|
|
else
|
|
out->compressed_vis = loadmodel->visdata + p;
|
|
out->efrags = NULL;
|
|
|
|
for (j=0 ; j<4 ; j++)
|
|
out->ambient_sound_level[j] = in->ambient_level[j];
|
|
|
|
//johnfitz -- removed code to mark surfaces as SURF_UNDERWATER
|
|
}
|
|
}
|
|
|
|
void Mod_ProcessLeafs_L1 (dl1leaf_t *in, int filelen)
|
|
{
|
|
mleaf_t *out;
|
|
int i, j, count, p;
|
|
|
|
if (filelen % sizeof(*in))
|
|
Sys_Error ("Mod_ProcessLeafs: funny lump size in %s", loadmodel->name);
|
|
|
|
count = filelen / sizeof(*in);
|
|
|
|
out = (mleaf_t *) Hunk_AllocName (count * sizeof(*out), loadname);
|
|
|
|
loadmodel->leafs = out;
|
|
loadmodel->numleafs = count;
|
|
|
|
for (i=0 ; i<count ; i++, in++, out++)
|
|
{
|
|
for (j=0 ; j<3 ; j++)
|
|
{
|
|
out->minmaxs[j] = LittleShort (in->mins[j]);
|
|
out->minmaxs[3+j] = LittleShort (in->maxs[j]);
|
|
}
|
|
|
|
p = LittleLong(in->contents);
|
|
out->contents = p;
|
|
|
|
out->firstmarksurface = loadmodel->marksurfaces + LittleLong(in->firstmarksurface); //johnfitz -- unsigned short
|
|
out->nummarksurfaces = LittleLong(in->nummarksurfaces); //johnfitz -- unsigned short
|
|
|
|
p = LittleLong(in->visofs);
|
|
if (p == -1)
|
|
out->compressed_vis = NULL;
|
|
else
|
|
out->compressed_vis = loadmodel->visdata + p;
|
|
out->efrags = NULL;
|
|
|
|
for (j=0 ; j<4 ; j++)
|
|
out->ambient_sound_level[j] = in->ambient_level[j];
|
|
|
|
//johnfitz -- removed code to mark surfaces as SURF_UNDERWATER
|
|
}
|
|
}
|
|
|
|
void Mod_ProcessLeafs_L2 (dl2leaf_t *in, int filelen)
|
|
{
|
|
mleaf_t *out;
|
|
int i, j, count, p;
|
|
|
|
if (filelen % sizeof(*in))
|
|
Sys_Error ("Mod_ProcessLeafs: funny lump size in %s", loadmodel->name);
|
|
|
|
count = filelen / sizeof(*in);
|
|
|
|
out = (mleaf_t *) Hunk_AllocName (count * sizeof(*out), loadname);
|
|
|
|
loadmodel->leafs = out;
|
|
loadmodel->numleafs = count;
|
|
|
|
for (i=0 ; i<count ; i++, in++, out++)
|
|
{
|
|
for (j=0 ; j<3 ; j++)
|
|
{
|
|
out->minmaxs[j] = LittleFloat (in->mins[j]);
|
|
out->minmaxs[3+j] = LittleFloat (in->maxs[j]);
|
|
}
|
|
|
|
p = LittleLong(in->contents);
|
|
out->contents = p;
|
|
|
|
out->firstmarksurface = loadmodel->marksurfaces + LittleLong(in->firstmarksurface); //johnfitz -- unsigned short
|
|
out->nummarksurfaces = LittleLong(in->nummarksurfaces); //johnfitz -- unsigned short
|
|
|
|
p = LittleLong(in->visofs);
|
|
if (p == -1)
|
|
out->compressed_vis = NULL;
|
|
else
|
|
out->compressed_vis = loadmodel->visdata + p;
|
|
out->efrags = NULL;
|
|
|
|
for (j=0 ; j<4 ; j++)
|
|
out->ambient_sound_level[j] = in->ambient_level[j];
|
|
|
|
//johnfitz -- removed code to mark surfaces as SURF_UNDERWATER
|
|
}
|
|
}
|
|
|
|
/*
|
|
=================
|
|
Mod_LoadLeafs
|
|
=================
|
|
*/
|
|
void Mod_LoadLeafs (lump_t *l, int bsp2)
|
|
{
|
|
void *in = (void *)(mod_base + l->fileofs);
|
|
|
|
if (bsp2 == 2)
|
|
Mod_ProcessLeafs_L2 ((dl2leaf_t *)in, l->filelen);
|
|
else if (bsp2)
|
|
Mod_ProcessLeafs_L1 ((dl1leaf_t *)in, l->filelen);
|
|
else
|
|
Mod_ProcessLeafs_S ((dsleaf_t *) in, l->filelen);
|
|
}
|
|
|
|
void Mod_CheckWaterVis(void)
|
|
{
|
|
mleaf_t *leaf, *other;
|
|
int i, j, k;
|
|
int numclusters = loadmodel->submodels[0].visleafs;
|
|
int contentfound = 0;
|
|
int contenttransparent = 0;
|
|
int contenttype;
|
|
|
|
if (r_novis.value)
|
|
{ //all can be
|
|
loadmodel->contentstransparent = (SURF_DRAWWATER|SURF_DRAWTELE|SURF_DRAWSLIME|SURF_DRAWLAVA);
|
|
return;
|
|
}
|
|
|
|
//pvs is 1-based. leaf 0 sees all (the solid leaf).
|
|
//leaf 0 has no pvs, and does not appear in other leafs either, so watch out for the biases.
|
|
for (i=0,leaf=loadmodel->leafs+1 ; i<numclusters ; i++, leaf++)
|
|
{
|
|
byte *vis;
|
|
if (leaf->contents == CONTENTS_WATER)
|
|
{
|
|
if ((contenttransparent & (SURF_DRAWWATER|SURF_DRAWTELE))==(SURF_DRAWWATER|SURF_DRAWTELE))
|
|
continue;
|
|
//this check is somewhat risky, but we should be able to get away with it.
|
|
for (contenttype = 0, i = 0; i < leaf->nummarksurfaces; i++)
|
|
if (leaf->firstmarksurface[i]->flags & (SURF_DRAWWATER|SURF_DRAWTELE))
|
|
{
|
|
contenttype = leaf->firstmarksurface[i]->flags & (SURF_DRAWWATER|SURF_DRAWTELE);
|
|
break;
|
|
}
|
|
//its possible that this leaf has absolutely no surfaces in it, turb or otherwise.
|
|
if (contenttype == 0)
|
|
continue;
|
|
}
|
|
else if (leaf->contents == CONTENTS_SLIME)
|
|
contenttype = SURF_DRAWSLIME;
|
|
else if (leaf->contents == CONTENTS_LAVA)
|
|
contenttype = SURF_DRAWLAVA;
|
|
//fixme: tele
|
|
else
|
|
continue;
|
|
if (contenttransparent & contenttype)
|
|
{
|
|
nextleaf:
|
|
continue; //found one of this type already
|
|
}
|
|
contentfound |= contenttype;
|
|
vis = Mod_DecompressVis(leaf->compressed_vis, loadmodel);
|
|
for (j = 0; j < (numclusters+7)/8; j++)
|
|
{
|
|
if (vis[j])
|
|
{
|
|
for (k = 0; k < 8; k++)
|
|
{
|
|
if (vis[j] & (1u<<k))
|
|
{
|
|
other = &loadmodel->leafs[(j<<3)+k+1];
|
|
if (leaf->contents != other->contents)
|
|
{
|
|
// Con_Printf("%p:%i sees %p:%i\n", leaf, leaf->contents, other, other->contents);
|
|
contenttransparent |= contenttype;
|
|
goto nextleaf;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!contenttransparent)
|
|
Con_DPrintf("%s is not watervised\n", loadmodel->name);
|
|
else
|
|
{
|
|
Con_DPrintf("%s is vised for transparent", loadmodel->name);
|
|
if (contenttransparent & SURF_DRAWWATER)
|
|
Con_DPrintf(" water");
|
|
if (contenttransparent & SURF_DRAWTELE)
|
|
Con_DPrintf(" tele");
|
|
if (contenttransparent & SURF_DRAWLAVA)
|
|
Con_DPrintf(" lava");
|
|
if (contenttransparent & SURF_DRAWSLIME)
|
|
Con_DPrintf(" slime");
|
|
Con_DPrintf("\n");
|
|
}
|
|
//any types that we didn't find are assumed to be transparent.
|
|
//this allows submodels to work okay (eg: ad uses func_illusionary teleporters for some reason).
|
|
loadmodel->contentstransparent = contenttransparent | (~contentfound & (SURF_DRAWWATER|SURF_DRAWTELE|SURF_DRAWSLIME|SURF_DRAWLAVA));
|
|
}
|
|
|
|
/*
|
|
=================
|
|
Mod_LoadClipnodes
|
|
=================
|
|
*/
|
|
void Mod_LoadClipnodes (lump_t *l, qboolean bsp2)
|
|
{
|
|
dsclipnode_t *ins;
|
|
dlclipnode_t *inl;
|
|
|
|
mclipnode_t *out; //johnfitz -- was dclipnode_t
|
|
int i, count;
|
|
hull_t *hull;
|
|
|
|
if (bsp2)
|
|
{
|
|
ins = NULL;
|
|
inl = (dlclipnode_t *)(mod_base + l->fileofs);
|
|
if (l->filelen % sizeof(*inl))
|
|
Sys_Error ("Mod_LoadClipnodes: funny lump size in %s",loadmodel->name);
|
|
|
|
count = l->filelen / sizeof(*inl);
|
|
}
|
|
else
|
|
{
|
|
ins = (dsclipnode_t *)(mod_base + l->fileofs);
|
|
inl = NULL;
|
|
if (l->filelen % sizeof(*ins))
|
|
Sys_Error ("Mod_LoadClipnodes: funny lump size in %s",loadmodel->name);
|
|
|
|
count = l->filelen / sizeof(*ins);
|
|
}
|
|
if (count)
|
|
out = (mclipnode_t *) Hunk_AllocName ( count*sizeof(*out), loadname);
|
|
else
|
|
out = NULL; //will use rnodes.
|
|
|
|
//johnfitz -- warn about exceeding old limits
|
|
if (count > 32767 && !bsp2)
|
|
Con_DWarning ("%i clipnodes exceeds standard limit of 32767.\n", count);
|
|
//johnfitz
|
|
|
|
loadmodel->clipnodes = out;
|
|
loadmodel->numclipnodes = count;
|
|
|
|
hull = &loadmodel->hulls[1];
|
|
hull->clipnodes = out;
|
|
hull->firstclipnode = 0;
|
|
hull->lastclipnode = count-1;
|
|
hull->planes = loadmodel->planes;
|
|
hull->clip_mins[0] = -16;
|
|
hull->clip_mins[1] = -16;
|
|
hull->clip_mins[2] = -24;
|
|
hull->clip_maxs[0] = 16;
|
|
hull->clip_maxs[1] = 16;
|
|
hull->clip_maxs[2] = 32;
|
|
|
|
hull = &loadmodel->hulls[2];
|
|
hull->clipnodes = out;
|
|
hull->firstclipnode = 0;
|
|
hull->lastclipnode = count-1;
|
|
hull->planes = loadmodel->planes;
|
|
hull->clip_mins[0] = -32;
|
|
hull->clip_mins[1] = -32;
|
|
hull->clip_mins[2] = -24;
|
|
hull->clip_maxs[0] = 32;
|
|
hull->clip_maxs[1] = 32;
|
|
hull->clip_maxs[2] = 64;
|
|
|
|
if (bsp2)
|
|
{
|
|
for (i=0 ; i<count ; i++, out++, inl++)
|
|
{
|
|
out->planenum = LittleLong(inl->planenum);
|
|
|
|
//johnfitz -- bounds check
|
|
if (out->planenum < 0 || out->planenum >= loadmodel->numplanes)
|
|
Host_Error ("Mod_LoadClipnodes: planenum out of bounds");
|
|
//johnfitz
|
|
|
|
out->children[0] = LittleLong(inl->children[0]);
|
|
out->children[1] = LittleLong(inl->children[1]);
|
|
//Spike: FIXME: bounds check
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (i=0 ; i<count ; i++, out++, ins++)
|
|
{
|
|
out->planenum = LittleLong(ins->planenum);
|
|
|
|
//johnfitz -- bounds check
|
|
if (out->planenum < 0 || out->planenum >= loadmodel->numplanes)
|
|
Host_Error ("Mod_LoadClipnodes: planenum out of bounds");
|
|
//johnfitz
|
|
|
|
//johnfitz -- support clipnodes > 32k
|
|
out->children[0] = (unsigned short)LittleShort(ins->children[0]);
|
|
out->children[1] = (unsigned short)LittleShort(ins->children[1]);
|
|
|
|
if (out->children[0] >= count)
|
|
out->children[0] -= 65536;
|
|
if (out->children[1] >= count)
|
|
out->children[1] -= 65536;
|
|
//johnfitz
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
=================
|
|
Mod_MakeHull0
|
|
|
|
Duplicate the drawing hull structure as a clipping hull
|
|
=================
|
|
*/
|
|
void Mod_MakeHull0 (void)
|
|
{
|
|
mnode_t *in, *child;
|
|
mclipnode_t *out; //johnfitz -- was dclipnode_t
|
|
int i, j, count;
|
|
hull_t *hull;
|
|
|
|
hull = &loadmodel->hulls[0];
|
|
|
|
in = loadmodel->nodes;
|
|
count = loadmodel->numnodes;
|
|
out = (mclipnode_t *) Hunk_AllocName ( count*sizeof(*out), loadname);
|
|
|
|
hull->clipnodes = out;
|
|
hull->firstclipnode = 0;
|
|
hull->lastclipnode = count-1;
|
|
hull->planes = loadmodel->planes;
|
|
|
|
for (i=0 ; i<count ; i++, out++, in++)
|
|
{
|
|
out->planenum = in->plane - loadmodel->planes;
|
|
for (j=0 ; j<2 ; j++)
|
|
{
|
|
child = in->children[j];
|
|
if (child->contents < 0)
|
|
out->children[j] = child->contents;
|
|
else
|
|
out->children[j] = child - loadmodel->nodes;
|
|
}
|
|
}
|
|
|
|
//if qbsp was run with -noclip, make sure the extra hulls use the rnodes instead of the missing clipnodes
|
|
//this won't 'fix' it, but it will stop it from crashing if it was just quickly built for debugging or whatever.
|
|
if (!loadmodel->hulls[1].clipnodes)
|
|
{ //hulls will be point-sized.
|
|
//bias that point so that its mid,mid,bottom instead of at the absmin or origin. this will retain view offsets.
|
|
loadmodel->hulls[1].clip_maxs[2] -= loadmodel->hulls[1].clip_mins[2];
|
|
loadmodel->hulls[1].clip_mins[2] = 0;
|
|
loadmodel->hulls[1].clipnodes = hull->clipnodes;
|
|
loadmodel->hulls[1].firstclipnode = hull->firstclipnode;
|
|
loadmodel->hulls[1].lastclipnode = hull->lastclipnode;
|
|
}
|
|
if (!loadmodel->hulls[2].clipnodes)
|
|
{
|
|
loadmodel->hulls[2].clip_maxs[2] -= loadmodel->hulls[2].clip_mins[2];
|
|
loadmodel->hulls[2].clip_mins[2] = 0;
|
|
loadmodel->hulls[2].clipnodes = loadmodel->hulls[1].clipnodes;
|
|
loadmodel->hulls[2].firstclipnode = loadmodel->hulls[1].firstclipnode;
|
|
loadmodel->hulls[2].lastclipnode = loadmodel->hulls[1].lastclipnode;
|
|
}
|
|
}
|
|
|
|
/*
|
|
=================
|
|
Mod_LoadMarksurfaces
|
|
=================
|
|
*/
|
|
void Mod_LoadMarksurfaces (lump_t *l, int bsp2)
|
|
{
|
|
int i, j, count;
|
|
msurface_t **out;
|
|
if (bsp2)
|
|
{
|
|
unsigned int *in = (unsigned int *)(mod_base + l->fileofs);
|
|
|
|
if (l->filelen % sizeof(*in))
|
|
Host_Error ("Mod_LoadMarksurfaces: funny lump size in %s",loadmodel->name);
|
|
|
|
count = l->filelen / sizeof(*in);
|
|
out = (msurface_t **)Hunk_AllocName ( count*sizeof(*out), loadname);
|
|
|
|
loadmodel->marksurfaces = out;
|
|
loadmodel->nummarksurfaces = count;
|
|
|
|
for (i=0 ; i<count ; i++)
|
|
{
|
|
j = LittleLong(in[i]);
|
|
if (j >= loadmodel->numsurfaces)
|
|
Host_Error ("Mod_LoadMarksurfaces: bad surface number");
|
|
out[i] = loadmodel->surfaces + j;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
short *in = (short *)(mod_base + l->fileofs);
|
|
|
|
if (l->filelen % sizeof(*in))
|
|
Host_Error ("Mod_LoadMarksurfaces: funny lump size in %s",loadmodel->name);
|
|
|
|
count = l->filelen / sizeof(*in);
|
|
out = (msurface_t **)Hunk_AllocName ( count*sizeof(*out), loadname);
|
|
|
|
loadmodel->marksurfaces = out;
|
|
loadmodel->nummarksurfaces = count;
|
|
|
|
//johnfitz -- warn mappers about exceeding old limits
|
|
if (count > 32767)
|
|
Con_DWarning ("%i marksurfaces exceeds standard limit of 32767.\n", count);
|
|
//johnfitz
|
|
|
|
for (i=0 ; i<count ; i++)
|
|
{
|
|
j = (unsigned short)LittleShort(in[i]); //johnfitz -- explicit cast as unsigned short
|
|
if (j >= loadmodel->numsurfaces)
|
|
Sys_Error ("Mod_LoadMarksurfaces: bad surface number");
|
|
out[i] = loadmodel->surfaces + j;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
=================
|
|
Mod_LoadSurfedges
|
|
=================
|
|
*/
|
|
void Mod_LoadSurfedges (lump_t *l)
|
|
{
|
|
int i, count;
|
|
int *in, *out;
|
|
|
|
in = (int *)(mod_base + l->fileofs);
|
|
if (l->filelen % sizeof(*in))
|
|
Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
|
|
count = l->filelen / sizeof(*in);
|
|
out = (int *) Hunk_AllocName ( count*sizeof(*out), loadname);
|
|
|
|
loadmodel->surfedges = out;
|
|
loadmodel->numsurfedges = count;
|
|
|
|
for (i=0 ; i<count ; i++)
|
|
out[i] = LittleLong (in[i]);
|
|
}
|
|
|
|
|
|
/*
|
|
=================
|
|
Mod_LoadPlanes
|
|
=================
|
|
*/
|
|
void Mod_LoadPlanes (lump_t *l)
|
|
{
|
|
int i, j;
|
|
mplane_t *out;
|
|
dplane_t *in;
|
|
int count;
|
|
int bits;
|
|
|
|
in = (dplane_t *)(mod_base + l->fileofs);
|
|
if (l->filelen % sizeof(*in))
|
|
Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
|
|
count = l->filelen / sizeof(*in);
|
|
out = (mplane_t *) Hunk_AllocName ( count*2*sizeof(*out), loadname);
|
|
|
|
loadmodel->planes = out;
|
|
loadmodel->numplanes = count;
|
|
|
|
for (i=0 ; i<count ; i++, in++, out++)
|
|
{
|
|
bits = 0;
|
|
for (j=0 ; j<3 ; j++)
|
|
{
|
|
out->normal[j] = LittleFloat (in->normal[j]);
|
|
if (out->normal[j] < 0)
|
|
bits |= 1<<j;
|
|
}
|
|
|
|
out->dist = LittleFloat (in->dist);
|
|
out->type = LittleLong (in->type);
|
|
out->signbits = bits;
|
|
}
|
|
}
|
|
|
|
/*
|
|
=================
|
|
RadiusFromBounds
|
|
=================
|
|
*/
|
|
float RadiusFromBounds (vec3_t mins, vec3_t maxs)
|
|
{
|
|
int i;
|
|
vec3_t corner;
|
|
|
|
for (i=0 ; i<3 ; i++)
|
|
{
|
|
corner[i] = fabs(mins[i]) > fabs(maxs[i]) ? fabs(mins[i]) : fabs(maxs[i]);
|
|
}
|
|
|
|
return VectorLength (corner);
|
|
}
|
|
|
|
/*
|
|
=================
|
|
Mod_LoadSubmodels
|
|
=================
|
|
*/
|
|
void Mod_LoadSubmodels (lump_t *l)
|
|
{
|
|
mmodel_t *out;
|
|
size_t i, j, count;
|
|
|
|
//detect whether this is a hexen2 8-hull map or a quake 4-hull map
|
|
dmodelq1_t *inq1 = (dmodelq1_t *)(mod_base + l->fileofs);
|
|
dmodelh2_t *inh2 = (dmodelh2_t *)(mod_base + l->fileofs);
|
|
//the numfaces is a bit of a hack. hexen2 only actually uses 6 of its 8 hulls and we depend upon this.
|
|
//this means that the 7th and 8th are null. q1.numfaces of the world equates to h2.hull[6], so should have a value for q1, and be 0 for hexen2.
|
|
//this should work even for maps that have enough submodels to realign the size.
|
|
//note that even if the map loads, you're on your own regarding the palette (hurrah for retexturing projects?).
|
|
//fixme: we don't fix up the clipnodes yet, the player is fine, shamblers/ogres/fiends/vores will have issues.
|
|
//unfortunately c doesn't do templates, which would make all this code a bit less copypastay
|
|
if ((size_t)l->filelen >= sizeof(*inh2) && !(l->filelen % sizeof(*inh2)) && !inq1->numfaces && inq1[1].firstface)
|
|
{
|
|
dmodelh2_t *in = inh2;
|
|
if (l->filelen % sizeof(*in))
|
|
Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
|
|
count = l->filelen / sizeof(*in);
|
|
out = (mmodel_t *) Hunk_AllocName ( count*sizeof(*out), loadname);
|
|
|
|
loadmodel->submodels = out;
|
|
loadmodel->numsubmodels = count;
|
|
|
|
for (i=0 ; i<count ; i++, in++, out++)
|
|
{
|
|
for (j=0 ; j<3 ; j++)
|
|
{ // spread the mins / maxs by a pixel
|
|
out->mins[j] = LittleFloat (in->mins[j]) - 1;
|
|
out->maxs[j] = LittleFloat (in->maxs[j]) + 1;
|
|
out->origin[j] = LittleFloat (in->origin[j]);
|
|
}
|
|
for (j=0 ; j<MAX_MAP_HULLS && j<sizeof(in->headnode)/sizeof(in->headnode[0]) ; j++)
|
|
out->headnode[j] = LittleLong (in->headnode[j]);
|
|
for (; j<MAX_MAP_HULLS ; j++)
|
|
out->headnode[j] = 0;
|
|
out->visleafs = LittleLong (in->visleafs);
|
|
out->firstface = LittleLong (in->firstface);
|
|
out->numfaces = LittleLong (in->numfaces);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dmodelq1_t *in = inq1;
|
|
if (l->filelen % sizeof(*in))
|
|
Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
|
|
count = l->filelen / sizeof(*in);
|
|
out = (mmodel_t *) Hunk_AllocName ( count*sizeof(*out), loadname);
|
|
|
|
loadmodel->submodels = out;
|
|
loadmodel->numsubmodels = count;
|
|
|
|
for (i=0 ; i<count ; i++, in++, out++)
|
|
{
|
|
for (j=0 ; j<3 ; j++)
|
|
{ // spread the mins / maxs by a pixel
|
|
out->mins[j] = LittleFloat (in->mins[j]) - 1;
|
|
out->maxs[j] = LittleFloat (in->maxs[j]) + 1;
|
|
out->origin[j] = LittleFloat (in->origin[j]);
|
|
}
|
|
for (j=0 ; j<MAX_MAP_HULLS && j<sizeof(in->headnode)/sizeof(in->headnode[0]) ; j++)
|
|
out->headnode[j] = LittleLong (in->headnode[j]);
|
|
for (; j<MAX_MAP_HULLS ; j++)
|
|
out->headnode[j] = 0;
|
|
out->visleafs = LittleLong (in->visleafs);
|
|
out->firstface = LittleLong (in->firstface);
|
|
out->numfaces = LittleLong (in->numfaces);
|
|
}
|
|
}
|
|
|
|
// johnfitz -- check world visleafs -- adapted from bjp
|
|
out = loadmodel->submodels;
|
|
|
|
if (out->visleafs > 8192)
|
|
Con_DWarning ("%i visleafs exceeds standard limit of 8192.\n", out->visleafs);
|
|
//johnfitz
|
|
}
|
|
|
|
/*
|
|
=================
|
|
Mod_BoundsFromClipNode -- johnfitz
|
|
|
|
update the model's clipmins and clipmaxs based on each node's plane.
|
|
|
|
This works because of the way brushes are expanded in hull generation.
|
|
Each brush will include all six axial planes, which bound that brush.
|
|
Therefore, the bounding box of the hull can be constructed entirely
|
|
from axial planes found in the clipnodes for that hull.
|
|
=================
|
|
*/
|
|
void Mod_BoundsFromClipNode (qmodel_t *mod, int hull, int nodenum)
|
|
{
|
|
mplane_t *plane;
|
|
mclipnode_t *node;
|
|
|
|
if (nodenum < 0)
|
|
return; //hit a leafnode
|
|
|
|
node = &mod->clipnodes[nodenum];
|
|
plane = mod->hulls[hull].planes + node->planenum;
|
|
switch (plane->type)
|
|
{
|
|
|
|
case PLANE_X:
|
|
if (plane->signbits == 1)
|
|
mod->clipmins[0] = q_min(mod->clipmins[0], -plane->dist - mod->hulls[hull].clip_mins[0]);
|
|
else
|
|
mod->clipmaxs[0] = q_max(mod->clipmaxs[0], plane->dist - mod->hulls[hull].clip_maxs[0]);
|
|
break;
|
|
case PLANE_Y:
|
|
if (plane->signbits == 2)
|
|
mod->clipmins[1] = q_min(mod->clipmins[1], -plane->dist - mod->hulls[hull].clip_mins[1]);
|
|
else
|
|
mod->clipmaxs[1] = q_max(mod->clipmaxs[1], plane->dist - mod->hulls[hull].clip_maxs[1]);
|
|
break;
|
|
case PLANE_Z:
|
|
if (plane->signbits == 4)
|
|
mod->clipmins[2] = q_min(mod->clipmins[2], -plane->dist - mod->hulls[hull].clip_mins[2]);
|
|
else
|
|
mod->clipmaxs[2] = q_max(mod->clipmaxs[2], plane->dist - mod->hulls[hull].clip_maxs[2]);
|
|
break;
|
|
default:
|
|
//skip nonaxial planes; don't need them
|
|
break;
|
|
}
|
|
|
|
Mod_BoundsFromClipNode (mod, hull, node->children[0]);
|
|
Mod_BoundsFromClipNode (mod, hull, node->children[1]);
|
|
}
|
|
|
|
/*
|
|
=================
|
|
Mod_LoadBrushModel
|
|
=================
|
|
*/
|
|
void Mod_LoadBrushModel (qmodel_t *mod, void *buffer)
|
|
{
|
|
int i, j;
|
|
int bsp2;
|
|
dheader_t *header;
|
|
mmodel_t *bm;
|
|
float radius; //johnfitz
|
|
|
|
loadmodel->type = mod_brush;
|
|
|
|
header = (dheader_t *)buffer;
|
|
|
|
mod->bspversion = LittleLong (header->version);
|
|
|
|
switch(mod->bspversion)
|
|
{
|
|
case BSPVERSION:
|
|
bsp2 = false;
|
|
break;
|
|
case BSP2VERSION_2PSB:
|
|
bsp2 = 1; //first iteration
|
|
break;
|
|
case BSP2VERSION_BSP2:
|
|
bsp2 = 2; //sanitised revision
|
|
break;
|
|
default:
|
|
loadmodel->type = mod_ext_invalid;
|
|
Con_Warning ("Mod_LoadBrushModel: %s has wrong version number (%i should be %i)\n", mod->name, mod->bspversion, BSPVERSION);
|
|
return;
|
|
}
|
|
|
|
// swap all the lumps
|
|
mod_base = (byte *)header;
|
|
|
|
for (i = 0; i < (int) sizeof(dheader_t) / 4; i++)
|
|
((int *)header)[i] = LittleLong ( ((int *)header)[i]);
|
|
|
|
Q1BSPX_Setup(mod, buffer, com_filesize, header->lumps, HEADER_LUMPS);
|
|
|
|
// load into heap
|
|
|
|
Mod_LoadVertexes (&header->lumps[LUMP_VERTEXES]);
|
|
Mod_LoadEdges (&header->lumps[LUMP_EDGES], bsp2);
|
|
Mod_LoadSurfedges (&header->lumps[LUMP_SURFEDGES]);
|
|
Mod_LoadTextures (&header->lumps[LUMP_TEXTURES]);
|
|
Mod_LoadLighting (&header->lumps[LUMP_LIGHTING]);
|
|
Mod_LoadPlanes (&header->lumps[LUMP_PLANES]);
|
|
Mod_LoadTexinfo (&header->lumps[LUMP_TEXINFO]);
|
|
Mod_LoadEntities (&header->lumps[LUMP_ENTITIES]); //Spike: moved this earlier, so that we can parse worldspawn keys earlier.
|
|
Mod_LoadFaces (&header->lumps[LUMP_FACES], bsp2);
|
|
Mod_LoadMarksurfaces (&header->lumps[LUMP_MARKSURFACES], bsp2);
|
|
Mod_LoadVisibility (&header->lumps[LUMP_VISIBILITY]);
|
|
Mod_LoadLeafs (&header->lumps[LUMP_LEAFS], bsp2);
|
|
Mod_LoadNodes (&header->lumps[LUMP_NODES], bsp2);
|
|
Mod_LoadClipnodes (&header->lumps[LUMP_CLIPNODES], bsp2);
|
|
Mod_LoadSubmodels (&header->lumps[LUMP_MODELS]);
|
|
|
|
Mod_MakeHull0 ();
|
|
|
|
mod->numframes = 2; // regular and alternate animation
|
|
|
|
Mod_CheckWaterVis();
|
|
|
|
//
|
|
// set up the submodels (FIXME: this is confusing)
|
|
//
|
|
|
|
// johnfitz -- okay, so that i stop getting confused every time i look at this loop, here's how it works:
|
|
// we're looping through the submodels starting at 0. Submodel 0 is the main model, so we don't have to
|
|
// worry about clobbering data the first time through, since it's the same data. At the end of the loop,
|
|
// we create a new copy of the data to use the next time through.
|
|
for (i=0 ; i<mod->numsubmodels ; i++)
|
|
{
|
|
bm = &mod->submodels[i];
|
|
|
|
mod->hulls[0].firstclipnode = bm->headnode[0];
|
|
for (j=1 ; j<MAX_MAP_HULLS ; j++)
|
|
{
|
|
mod->hulls[j].firstclipnode = bm->headnode[j];
|
|
if (mod->hulls[j].clipnodes == mod->hulls[0].clipnodes)
|
|
mod->hulls[j].lastclipnode = mod->hulls[0].lastclipnode;
|
|
else
|
|
mod->hulls[j].lastclipnode = mod->numclipnodes-1;
|
|
}
|
|
|
|
mod->firstmodelsurface = bm->firstface;
|
|
mod->nummodelsurfaces = bm->numfaces;
|
|
|
|
VectorCopy (bm->maxs, mod->maxs);
|
|
VectorCopy (bm->mins, mod->mins);
|
|
|
|
//johnfitz -- calculate rotate bounds and yaw bounds
|
|
radius = RadiusFromBounds (mod->mins, mod->maxs);
|
|
mod->rmaxs[0] = mod->rmaxs[1] = mod->rmaxs[2] = mod->ymaxs[0] = mod->ymaxs[1] = mod->ymaxs[2] = radius;
|
|
mod->rmins[0] = mod->rmins[1] = mod->rmins[2] = mod->ymins[0] = mod->ymins[1] = mod->ymins[2] = -radius;
|
|
//johnfitz
|
|
|
|
//johnfitz -- correct physics cullboxes so that outlying clip brushes on doors and stuff are handled right
|
|
if (i > 0 || strcmp(mod->name, sv.modelname) != 0) //skip submodel 0 of sv.worldmodel, which is the actual world
|
|
{
|
|
// start with the hull0 bounds
|
|
VectorCopy (mod->maxs, mod->clipmaxs);
|
|
VectorCopy (mod->mins, mod->clipmins);
|
|
|
|
// process hull1 (we don't need to process hull2 becuase there's
|
|
// no such thing as a brush that appears in hull2 but not hull1)
|
|
//Mod_BoundsFromClipNode (mod, 1, mod->hulls[1].firstclipnode); // (disabled for now becuase it fucks up on rotating models)
|
|
}
|
|
//johnfitz
|
|
|
|
mod->numleafs = bm->visleafs;
|
|
|
|
if (i < mod->numsubmodels-1)
|
|
{ // duplicate the basic information
|
|
char name[10];
|
|
|
|
sprintf (name, "*%i", i+1);
|
|
loadmodel = Mod_FindName (name);
|
|
*loadmodel = *mod;
|
|
strcpy (loadmodel->name, name);
|
|
mod = loadmodel;
|
|
|
|
Mod_SetExtraFlags(mod);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
==============================================================================
|
|
|
|
ALIAS MODELS
|
|
|
|
==============================================================================
|
|
*/
|
|
|
|
aliashdr_t *pheader;
|
|
|
|
stvert_t stverts[MAXALIASVERTS];
|
|
mtriangle_t *triangles;
|
|
int max_triangles;
|
|
|
|
// a pose is a single set of vertexes. a frame may be
|
|
// an animating sequence of poses
|
|
trivertx_t *poseverts_mdl[MAXALIASFRAMES];
|
|
int posenum;
|
|
|
|
byte **player_8bit_texels_tbl;
|
|
byte *player_8bit_texels;
|
|
|
|
/*
|
|
=================
|
|
Mod_LoadAliasFrame
|
|
=================
|
|
*/
|
|
void * Mod_LoadAliasFrame (void * pin, maliasframedesc_t *frame)
|
|
{
|
|
trivertx_t *pinframe;
|
|
int i;
|
|
daliasframe_t *pdaliasframe;
|
|
|
|
if (posenum >= MAXALIASFRAMES)
|
|
Sys_Error ("posenum >= MAXALIASFRAMES");
|
|
|
|
pdaliasframe = (daliasframe_t *)pin;
|
|
|
|
strcpy (frame->name, pdaliasframe->name);
|
|
frame->firstpose = posenum;
|
|
frame->numposes = 1;
|
|
|
|
for (i=0 ; i<3 ; i++)
|
|
{
|
|
// these are byte values, so we don't have to worry about
|
|
// endianness
|
|
frame->bboxmin.v[i] = pdaliasframe->bboxmin.v[i];
|
|
frame->bboxmax.v[i] = pdaliasframe->bboxmax.v[i];
|
|
}
|
|
|
|
|
|
pinframe = (trivertx_t *)(pdaliasframe + 1);
|
|
|
|
poseverts_mdl[posenum] = pinframe;
|
|
posenum++;
|
|
|
|
pinframe += pheader->numverts;
|
|
|
|
return (void *)pinframe;
|
|
}
|
|
|
|
|
|
/*
|
|
=================
|
|
Mod_LoadAliasGroup
|
|
=================
|
|
*/
|
|
void *Mod_LoadAliasGroup (void * pin, maliasframedesc_t *frame)
|
|
{
|
|
daliasgroup_t *pingroup;
|
|
int i, numframes;
|
|
daliasinterval_t *pin_intervals;
|
|
void *ptemp;
|
|
|
|
pingroup = (daliasgroup_t *)pin;
|
|
|
|
numframes = LittleLong (pingroup->numframes);
|
|
|
|
frame->firstpose = posenum;
|
|
frame->numposes = numframes;
|
|
|
|
for (i=0 ; i<3 ; i++)
|
|
{
|
|
// these are byte values, so we don't have to worry about endianness
|
|
frame->bboxmin.v[i] = pingroup->bboxmin.v[i];
|
|
frame->bboxmax.v[i] = pingroup->bboxmax.v[i];
|
|
}
|
|
|
|
|
|
pin_intervals = (daliasinterval_t *)(pingroup + 1);
|
|
|
|
frame->interval = LittleFloat (pin_intervals->interval);
|
|
|
|
pin_intervals += numframes;
|
|
|
|
ptemp = (void *)pin_intervals;
|
|
|
|
for (i=0 ; i<numframes ; i++)
|
|
{
|
|
if (posenum >= MAXALIASFRAMES) Sys_Error ("posenum >= MAXALIASFRAMES");
|
|
|
|
poseverts_mdl[posenum] = (trivertx_t *)((daliasframe_t *)ptemp + 1);
|
|
posenum++;
|
|
|
|
ptemp = (trivertx_t *)((daliasframe_t *)ptemp + 1) + pheader->numverts;
|
|
}
|
|
|
|
return ptemp;
|
|
}
|
|
|
|
//=========================================================
|
|
|
|
|
|
/*
|
|
=================
|
|
Mod_FloodFillSkin
|
|
|
|
Fill background pixels so mipmapping doesn't have haloes - Ed
|
|
=================
|
|
*/
|
|
|
|
typedef struct
|
|
{
|
|
short x, y;
|
|
} floodfill_t;
|
|
|
|
// must be a power of 2
|
|
#define FLOODFILL_FIFO_SIZE 0x1000
|
|
#define FLOODFILL_FIFO_MASK (FLOODFILL_FIFO_SIZE - 1)
|
|
|
|
#define FLOODFILL_STEP( off, dx, dy ) \
|
|
do { \
|
|
if (pos[off] == fillcolor) \
|
|
{ \
|
|
pos[off] = 255; \
|
|
fifo[inpt].x = x + (dx), fifo[inpt].y = y + (dy); \
|
|
inpt = (inpt + 1) & FLOODFILL_FIFO_MASK; \
|
|
} \
|
|
else if (pos[off] != 255) fdc = pos[off]; \
|
|
} while (0)
|
|
|
|
void Mod_FloodFillSkin( byte *skin, int skinwidth, int skinheight )
|
|
{
|
|
byte fillcolor = *skin; // assume this is the pixel to fill
|
|
floodfill_t fifo[FLOODFILL_FIFO_SIZE];
|
|
int inpt = 0, outpt = 0;
|
|
int filledcolor = -1;
|
|
int i;
|
|
|
|
if (filledcolor == -1)
|
|
{
|
|
filledcolor = 0;
|
|
// attempt to find opaque black
|
|
for (i = 0; i < 256; ++i)
|
|
if (d_8to24table[i] == (unsigned int)LittleLong(255 << 24)) // alpha 1.0
|
|
{
|
|
filledcolor = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// can't fill to filled color or to transparent color (used as visited marker)
|
|
if ((fillcolor == filledcolor) || (fillcolor == 255))
|
|
{
|
|
//printf( "not filling skin from %d to %d\n", fillcolor, filledcolor );
|
|
return;
|
|
}
|
|
|
|
fifo[inpt].x = 0, fifo[inpt].y = 0;
|
|
inpt = (inpt + 1) & FLOODFILL_FIFO_MASK;
|
|
|
|
while (outpt != inpt)
|
|
{
|
|
int x = fifo[outpt].x, y = fifo[outpt].y;
|
|
int fdc = filledcolor;
|
|
byte *pos = &skin[x + skinwidth * y];
|
|
|
|
outpt = (outpt + 1) & FLOODFILL_FIFO_MASK;
|
|
|
|
if (x > 0) FLOODFILL_STEP( -1, -1, 0 );
|
|
if (x < skinwidth - 1) FLOODFILL_STEP( 1, 1, 0 );
|
|
if (y > 0) FLOODFILL_STEP( -skinwidth, 0, -1 );
|
|
if (y < skinheight - 1) FLOODFILL_STEP( skinwidth, 0, 1 );
|
|
skin[x + skinwidth * y] = fdc;
|
|
}
|
|
}
|
|
|
|
/*
|
|
===============
|
|
Mod_LoadAllSkins
|
|
===============
|
|
*/
|
|
void *Mod_LoadAllSkins (int numskins, daliasskintype_t *pskintype)
|
|
{
|
|
int i, j, k, size, groupskins;
|
|
char name[MAX_QPATH];
|
|
byte *skin, *texels;
|
|
daliasskingroup_t *pinskingroup;
|
|
daliasskininterval_t *pinskinintervals;
|
|
char fbr_mask_name[MAX_QPATH]; //johnfitz -- added for fullbright support
|
|
src_offset_t offset; //johnfitz
|
|
unsigned int texflags = TEXPREF_PAD;
|
|
|
|
skin = (byte *)(pskintype + 1);
|
|
|
|
if (numskins < 1 || numskins > MAX_SKINS)
|
|
Sys_Error ("Mod_LoadAliasModel: Invalid # of skins: %d\n", numskins);
|
|
|
|
size = pheader->skinwidth * pheader->skinheight;
|
|
|
|
if (loadmodel->flags & MF_HOLEY)
|
|
texflags |= TEXPREF_ALPHA;
|
|
|
|
for (i=0 ; i<numskins ; i++)
|
|
{
|
|
if (pskintype->type == ALIAS_SKIN_SINGLE)
|
|
{
|
|
Mod_FloodFillSkin( skin, pheader->skinwidth, pheader->skinheight );
|
|
|
|
// save 8 bit texels for the player model to remap
|
|
texels = (byte *) Hunk_AllocName(size, loadname);
|
|
pheader->texels[i] = texels - (byte *)pheader;
|
|
memcpy (texels, (byte *)(pskintype + 1), size);
|
|
|
|
//johnfitz -- rewritten
|
|
q_snprintf (name, sizeof(name), "%s:frame%i", loadmodel->name, i);
|
|
offset = (src_offset_t)(pskintype+1) - (src_offset_t)mod_base;
|
|
if (Mod_CheckFullbrights ((byte *)(pskintype+1), size))
|
|
{
|
|
pheader->gltextures[i][0] = TexMgr_LoadImage (loadmodel, name, pheader->skinwidth, pheader->skinheight,
|
|
SRC_INDEXED, (byte *)(pskintype+1), loadmodel->name, offset, texflags | TEXPREF_NOBRIGHT);
|
|
q_snprintf (fbr_mask_name, sizeof(fbr_mask_name), "%s:frame%i_glow", loadmodel->name, i);
|
|
pheader->fbtextures[i][0] = TexMgr_LoadImage (loadmodel, fbr_mask_name, pheader->skinwidth, pheader->skinheight,
|
|
SRC_INDEXED, (byte *)(pskintype+1), loadmodel->name, offset, texflags | TEXPREF_FULLBRIGHT);
|
|
}
|
|
else
|
|
{
|
|
pheader->gltextures[i][0] = TexMgr_LoadImage (loadmodel, name, pheader->skinwidth, pheader->skinheight,
|
|
SRC_INDEXED, (byte *)(pskintype+1), loadmodel->name, offset, texflags);
|
|
pheader->fbtextures[i][0] = NULL;
|
|
}
|
|
|
|
pheader->gltextures[i][3] = pheader->gltextures[i][2] = pheader->gltextures[i][1] = pheader->gltextures[i][0];
|
|
pheader->fbtextures[i][3] = pheader->fbtextures[i][2] = pheader->fbtextures[i][1] = pheader->fbtextures[i][0];
|
|
//johnfitz
|
|
|
|
pskintype = (daliasskintype_t *)((byte *)(pskintype+1) + size);
|
|
}
|
|
else
|
|
{
|
|
// animating skin group. yuck.
|
|
pskintype++;
|
|
pinskingroup = (daliasskingroup_t *)pskintype;
|
|
groupskins = LittleLong (pinskingroup->numskins);
|
|
pinskinintervals = (daliasskininterval_t *)(pinskingroup + 1);
|
|
|
|
pskintype = (daliasskintype_t *)(pinskinintervals + groupskins);
|
|
|
|
for (j=0 ; j<groupskins ; j++)
|
|
{
|
|
Mod_FloodFillSkin( skin, pheader->skinwidth, pheader->skinheight );
|
|
if (j == 0) {
|
|
texels = (byte *) Hunk_AllocName(size, loadname);
|
|
pheader->texels[i] = texels - (byte *)pheader;
|
|
memcpy (texels, (byte *)(pskintype), size);
|
|
}
|
|
|
|
//johnfitz -- rewritten
|
|
q_snprintf (name, sizeof(name), "%s:frame%i_%i", loadmodel->name, i,j);
|
|
offset = (src_offset_t)(pskintype) - (src_offset_t)mod_base; //johnfitz
|
|
if (Mod_CheckFullbrights ((byte *)(pskintype), size))
|
|
{
|
|
pheader->gltextures[i][j&3] = TexMgr_LoadImage (loadmodel, name, pheader->skinwidth, pheader->skinheight,
|
|
SRC_INDEXED, (byte *)(pskintype), loadmodel->name, offset, texflags | TEXPREF_NOBRIGHT);
|
|
q_snprintf (fbr_mask_name, sizeof(fbr_mask_name), "%s:frame%i_%i_glow", loadmodel->name, i,j);
|
|
pheader->fbtextures[i][j&3] = TexMgr_LoadImage (loadmodel, fbr_mask_name, pheader->skinwidth, pheader->skinheight,
|
|
SRC_INDEXED, (byte *)(pskintype), loadmodel->name, offset, texflags | TEXPREF_FULLBRIGHT);
|
|
}
|
|
else
|
|
{
|
|
pheader->gltextures[i][j&3] = TexMgr_LoadImage (loadmodel, name, pheader->skinwidth, pheader->skinheight,
|
|
SRC_INDEXED, (byte *)(pskintype), loadmodel->name, offset, texflags);
|
|
pheader->fbtextures[i][j&3] = NULL;
|
|
}
|
|
//johnfitz
|
|
|
|
pskintype = (daliasskintype_t *)((byte *)(pskintype) + size);
|
|
}
|
|
k = j;
|
|
for (/**/; j < 4; j++)
|
|
pheader->gltextures[i][j&3] = pheader->gltextures[i][j - k];
|
|
}
|
|
}
|
|
|
|
return (void *)pskintype;
|
|
}
|
|
|
|
//=========================================================================
|
|
|
|
/*
|
|
=================
|
|
Mod_CalcAliasBounds -- johnfitz -- calculate bounds of alias model for nonrotated, yawrotated, and fullrotated cases
|
|
=================
|
|
*/
|
|
void Mod_CalcAliasBounds (aliashdr_t *a)
|
|
{
|
|
int i,j,k;
|
|
float dist, yawradius, radius;
|
|
vec3_t v;
|
|
|
|
//clear out all data
|
|
for (i=0; i<3;i++)
|
|
{
|
|
loadmodel->mins[i] = loadmodel->ymins[i] = loadmodel->rmins[i] = FLT_MAX;
|
|
loadmodel->maxs[i] = loadmodel->ymaxs[i] = loadmodel->rmaxs[i] = -FLT_MAX;
|
|
}
|
|
radius = yawradius = 0;
|
|
|
|
for (;;)
|
|
{
|
|
if (a->numposes && a->numverts)
|
|
{
|
|
if (a->posevertssize == 1)
|
|
{
|
|
//process verts
|
|
for (i=0 ; i<a->numposes; i++)
|
|
for (j=0; j<a->numverts; j++)
|
|
{
|
|
for (k=0; k<3;k++)
|
|
v[k] = poseverts_mdl[i][j].v[k] * pheader->scale[k] + pheader->scale_origin[k];
|
|
|
|
for (k=0; k<3;k++)
|
|
{
|
|
loadmodel->mins[k] = q_min(loadmodel->mins[k], v[k]);
|
|
loadmodel->maxs[k] = q_max(loadmodel->maxs[k], v[k]);
|
|
}
|
|
dist = v[0] * v[0] + v[1] * v[1];
|
|
if (yawradius < dist)
|
|
yawradius = dist;
|
|
dist += v[2] * v[2];
|
|
if (radius < dist)
|
|
radius = dist;
|
|
}
|
|
}
|
|
else if (a->posevertssize == 2)
|
|
{
|
|
//process verts
|
|
for (i=0 ; i<a->numposes; i++)
|
|
{
|
|
md3XyzNormal_t *pv = (md3XyzNormal_t *)((byte*)a+a->vertexes) + i*a->numverts;
|
|
for (j=0; j<a->numverts; j++)
|
|
{
|
|
for (k=0; k<3;k++)
|
|
v[k] = pv[j].xyz[k] * 1/64.0;
|
|
|
|
for (k=0; k<3;k++)
|
|
{
|
|
loadmodel->mins[k] = q_min(loadmodel->mins[k], v[k]);
|
|
loadmodel->maxs[k] = q_max(loadmodel->maxs[k], v[k]);
|
|
}
|
|
dist = v[0] * v[0] + v[1] * v[1];
|
|
if (yawradius < dist)
|
|
yawradius = dist;
|
|
dist += v[2] * v[2];
|
|
if (radius < dist)
|
|
radius = dist;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!a->nextsurface)
|
|
break;
|
|
a = (aliashdr_t*)((byte*)a + a->nextsurface);
|
|
}
|
|
|
|
//dodgy model that lacks any frames or verts
|
|
for (i=0; i<3;i++)
|
|
{
|
|
if (loadmodel->mins[i] > loadmodel->maxs[i])
|
|
{ //set sizes to 0 if its invalid.
|
|
loadmodel->mins[i] = 0;
|
|
loadmodel->maxs[i] = 0;
|
|
}
|
|
}
|
|
|
|
//rbounds will be used when entity has nonzero pitch or roll
|
|
radius = sqrt(radius);
|
|
loadmodel->rmins[0] = loadmodel->rmins[1] = loadmodel->rmins[2] = -radius;
|
|
loadmodel->rmaxs[0] = loadmodel->rmaxs[1] = loadmodel->rmaxs[2] = radius;
|
|
|
|
//ybounds will be used when entity has nonzero yaw
|
|
yawradius = sqrt(yawradius);
|
|
loadmodel->ymins[0] = loadmodel->ymins[1] = -yawradius;
|
|
loadmodel->ymaxs[0] = loadmodel->ymaxs[1] = yawradius;
|
|
loadmodel->ymins[2] = loadmodel->mins[2];
|
|
loadmodel->ymaxs[2] = loadmodel->maxs[2];
|
|
}
|
|
|
|
static qboolean
|
|
nameInList(const char *list, const char *name)
|
|
{
|
|
const char *s;
|
|
char tmp[MAX_QPATH];
|
|
int i;
|
|
|
|
s = list;
|
|
|
|
while (*s)
|
|
{
|
|
// make a copy until the next comma or end of string
|
|
i = 0;
|
|
while (*s && *s != ',')
|
|
{
|
|
if (i < MAX_QPATH - 1)
|
|
tmp[i++] = *s;
|
|
s++;
|
|
}
|
|
tmp[i] = '\0';
|
|
//compare it to the model name
|
|
if (!strcmp(name, tmp))
|
|
{
|
|
return true;
|
|
}
|
|
//search forwards to the next comma or end of string
|
|
while (*s && *s == ',')
|
|
s++;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
Mod_SetExtraFlags -- johnfitz -- set up extra flags that aren't in the mdl
|
|
=================
|
|
*/
|
|
void Mod_SetExtraFlags (qmodel_t *mod)
|
|
{
|
|
extern cvar_t r_nolerp_list, r_noshadow_list;
|
|
|
|
if (!mod)
|
|
return;
|
|
|
|
mod->flags &= (0xFF | MF_HOLEY); //only preserve first byte, plus MF_HOLEY
|
|
|
|
if (mod->type == mod_alias)
|
|
{
|
|
// nolerp flag
|
|
if (nameInList(r_nolerp_list.string, mod->name))
|
|
mod->flags |= MOD_NOLERP;
|
|
|
|
// noshadow flag
|
|
if (nameInList(r_noshadow_list.string, mod->name))
|
|
mod->flags |= MOD_NOSHADOW;
|
|
|
|
// fullbright hack (TODO: make this a cvar list)
|
|
if (!strcmp (mod->name, "progs/flame2.mdl") ||
|
|
!strcmp (mod->name, "progs/flame.mdl") ||
|
|
!strcmp (mod->name, "progs/boss.mdl"))
|
|
mod->flags |= MOD_FBRIGHTHACK;
|
|
}
|
|
|
|
#ifdef PSET_SCRIPT
|
|
PScript_UpdateModelEffects(mod);
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
=================
|
|
Mod_LoadAliasModel
|
|
=================
|
|
*/
|
|
void Mod_LoadAliasModel (qmodel_t *mod, void *buffer)
|
|
{
|
|
int i, j;
|
|
mdl_t *pinmodel;
|
|
stvert_t *pinstverts;
|
|
dtriangle_t *pintriangles;
|
|
int version, numframes;
|
|
int size;
|
|
daliasframetype_t *pframetype;
|
|
daliasskintype_t *pskintype;
|
|
int start, end, total;
|
|
|
|
start = Hunk_LowMark ();
|
|
|
|
pinmodel = (mdl_t *)buffer;
|
|
mod_base = (byte *)buffer; //johnfitz
|
|
|
|
version = LittleLong (pinmodel->version);
|
|
if (version != ALIAS_VERSION)
|
|
Sys_Error ("%s has wrong version number (%i should be %i)",
|
|
mod->name, version, ALIAS_VERSION);
|
|
|
|
//
|
|
// allocate space for a working header, plus all the data except the frames,
|
|
// skin and group info
|
|
//
|
|
size = sizeof(aliashdr_t) +
|
|
(LittleLong (pinmodel->numframes) - 1) * sizeof (pheader->frames[0]);
|
|
pheader = (aliashdr_t *) Hunk_AllocName (size, loadname);
|
|
|
|
mod->flags = LittleLong (pinmodel->flags);
|
|
|
|
//
|
|
// endian-adjust and copy the data, starting with the alias model header
|
|
//
|
|
pheader->boundingradius = LittleFloat (pinmodel->boundingradius);
|
|
pheader->numskins = LittleLong (pinmodel->numskins);
|
|
pheader->skinwidth = LittleLong (pinmodel->skinwidth);
|
|
pheader->skinheight = LittleLong (pinmodel->skinheight);
|
|
|
|
if (pheader->skinheight > MAX_LBM_HEIGHT)
|
|
Con_DWarning ("model %s has a skin taller than %d\n", mod->name,
|
|
MAX_LBM_HEIGHT); //Spike -- this was always a bogus error in gl renderers. its width*height that really matters.
|
|
|
|
pheader->numverts = LittleLong (pinmodel->numverts);
|
|
|
|
if (pheader->numverts <= 0)
|
|
Sys_Error ("model %s has no vertices", mod->name);
|
|
if (pheader->numverts > MAXALIASVERTS)
|
|
{ //Spike -- made this more tollerant. its still an error of course.
|
|
Con_Warning("model %s has too many vertices (%i > %i)\n", mod->name, pheader->numverts, MAXALIASVERTS);
|
|
mod->type = mod_ext_invalid;
|
|
return;
|
|
}
|
|
if (pheader->numverts > VANILLA_MAXALIASVERTS)
|
|
Con_DWarning("model %s exceeds standard vertex limit (%i > %i)\n", mod->name, pheader->numverts, VANILLA_MAXALIASVERTS);
|
|
|
|
pheader->numtris = LittleLong (pinmodel->numtris);
|
|
|
|
if (pheader->numtris <= 0)
|
|
Sys_Error ("model %s has no triangles", mod->name);
|
|
if (pheader->numtris > max_triangles)
|
|
{
|
|
mtriangle_t *n = malloc(sizeof(*triangles) * pheader->numtris);
|
|
if (n)
|
|
{
|
|
free(triangles);
|
|
triangles = n;
|
|
max_triangles = pheader->numtris;
|
|
}
|
|
else
|
|
{
|
|
max_triangles = 0;
|
|
//Spike -- added this check, because I'm segfaulting out.
|
|
Con_Warning("model %s has too many triangles (%i)\n", mod->name, pheader->numtris);
|
|
mod->type = mod_ext_invalid;
|
|
return;
|
|
}
|
|
}
|
|
|
|
pheader->numframes = LittleLong (pinmodel->numframes);
|
|
numframes = pheader->numframes;
|
|
if (numframes < 1)
|
|
Sys_Error ("Mod_LoadAliasModel: Invalid # of frames: %d\n", numframes);
|
|
if (numframes > MAXALIASFRAMES)
|
|
{
|
|
numframes = MAXALIASFRAMES;
|
|
Con_Warning("model %s has too many frames (%i > %i)\n", mod->name, numframes, MAXALIASFRAMES);
|
|
}
|
|
|
|
pheader->size = LittleFloat (pinmodel->size) * ALIAS_BASE_SIZE_RATIO;
|
|
mod->synctype = (synctype_t) LittleLong (pinmodel->synctype);
|
|
mod->numframes = pheader->numframes;
|
|
|
|
for (i=0 ; i<3 ; i++)
|
|
{
|
|
pheader->scale[i] = LittleFloat (pinmodel->scale[i]);
|
|
pheader->scale_origin[i] = LittleFloat (pinmodel->scale_origin[i]);
|
|
pheader->eyeposition[i] = LittleFloat (pinmodel->eyeposition[i]);
|
|
}
|
|
|
|
|
|
//
|
|
// load the skins
|
|
//
|
|
pskintype = (daliasskintype_t *)&pinmodel[1];
|
|
pskintype = (daliasskintype_t *) Mod_LoadAllSkins (pheader->numskins, pskintype);
|
|
|
|
//
|
|
// load base s and t vertices
|
|
//
|
|
pinstverts = (stvert_t *)pskintype;
|
|
|
|
for (i=0 ; i<pheader->numverts ; i++)
|
|
{
|
|
stverts[i].onseam = LittleLong (pinstverts[i].onseam);
|
|
stverts[i].s = LittleLong (pinstverts[i].s);
|
|
stverts[i].t = LittleLong (pinstverts[i].t);
|
|
}
|
|
|
|
//
|
|
// load triangle lists
|
|
//
|
|
pintriangles = (dtriangle_t *)&pinstverts[pheader->numverts];
|
|
|
|
for (i=0 ; i<pheader->numtris ; i++)
|
|
{
|
|
triangles[i].facesfront = LittleLong (pintriangles[i].facesfront);
|
|
|
|
for (j=0 ; j<3 ; j++)
|
|
{
|
|
triangles[i].vertindex[j] =
|
|
LittleLong (pintriangles[i].vertindex[j]);
|
|
}
|
|
}
|
|
|
|
//
|
|
// load the frames
|
|
//
|
|
posenum = 0;
|
|
pframetype = (daliasframetype_t *)&pintriangles[pheader->numtris];
|
|
|
|
for (i=0 ; i<numframes ; i++)
|
|
{
|
|
aliasframetype_t frametype;
|
|
frametype = (aliasframetype_t) LittleLong (pframetype->type);
|
|
if (frametype == ALIAS_SINGLE)
|
|
pframetype = (daliasframetype_t *) Mod_LoadAliasFrame (pframetype + 1, &pheader->frames[i]);
|
|
else
|
|
pframetype = (daliasframetype_t *) Mod_LoadAliasGroup (pframetype + 1, &pheader->frames[i]);
|
|
}
|
|
|
|
pheader->numposes = posenum;
|
|
pheader->posevertssize = 1;
|
|
|
|
mod->type = mod_alias;
|
|
|
|
Mod_CalcAliasBounds (pheader); //johnfitz
|
|
|
|
//Spike: for setmodel compat with vanilla
|
|
mod->clipmins[0] = mod->clipmins[1] = mod->clipmins[2] = -16;
|
|
mod->clipmaxs[0] = mod->clipmaxs[1] = mod->clipmaxs[2] = 16;
|
|
|
|
//
|
|
// build the draw lists
|
|
//
|
|
GL_MakeAliasModelDisplayLists (mod, pheader);
|
|
GLMesh_LoadVertexBuffer (mod, pheader);
|
|
|
|
//
|
|
// move the complete, relocatable alias model to the cache
|
|
//
|
|
end = Hunk_LowMark ();
|
|
total = end - start;
|
|
|
|
Cache_Alloc (&mod->cache, total, loadname);
|
|
if (!mod->cache.data)
|
|
return;
|
|
memcpy (mod->cache.data, pheader, total);
|
|
|
|
Hunk_FreeToLowMark (start);
|
|
}
|
|
|
|
//=============================================================================
|
|
|
|
/*
|
|
=================
|
|
Mod_LoadSpriteFrame
|
|
=================
|
|
*/
|
|
void * Mod_LoadSpriteFrame (void * pin, mspriteframe_t **ppframe, int framenum, enum srcformat fmt)
|
|
{
|
|
dspriteframe_t *pinframe;
|
|
mspriteframe_t *pspriteframe;
|
|
int width, height, size, origin[2];
|
|
char name[64];
|
|
src_offset_t offset; //johnfitz
|
|
|
|
pinframe = (dspriteframe_t *)pin;
|
|
|
|
width = LittleLong (pinframe->width);
|
|
height = LittleLong (pinframe->height);
|
|
size = width * height;
|
|
if (fmt == SRC_RGBA)
|
|
size *= 4;
|
|
|
|
pspriteframe = (mspriteframe_t *) Hunk_AllocName (sizeof (mspriteframe_t),loadname);
|
|
*ppframe = pspriteframe;
|
|
|
|
pspriteframe->width = width;
|
|
pspriteframe->height = height;
|
|
origin[0] = LittleLong (pinframe->origin[0]);
|
|
origin[1] = LittleLong (pinframe->origin[1]);
|
|
|
|
pspriteframe->up = origin[1];
|
|
pspriteframe->down = origin[1] - height;
|
|
pspriteframe->left = origin[0];
|
|
pspriteframe->right = width + origin[0];
|
|
|
|
//johnfitz -- image might be padded
|
|
pspriteframe->smax = (float)width/(float)TexMgr_PadConditional(width);
|
|
pspriteframe->tmax = (float)height/(float)TexMgr_PadConditional(height);
|
|
//johnfitz
|
|
|
|
q_snprintf (name, sizeof(name), "%s:frame%i", loadmodel->name, framenum);
|
|
offset = (src_offset_t)(pinframe+1) - (src_offset_t)mod_base; //johnfitz
|
|
pspriteframe->gltexture =
|
|
TexMgr_LoadImage (loadmodel, name, width, height, fmt,
|
|
(byte *)(pinframe + 1), loadmodel->name, offset,
|
|
TEXPREF_PAD | TEXPREF_ALPHA | TEXPREF_NOPICMIP); //johnfitz -- TexMgr
|
|
|
|
return (void *)((byte *)pinframe + sizeof (dspriteframe_t) + size);
|
|
}
|
|
|
|
|
|
/*
|
|
=================
|
|
Mod_LoadSpriteGroup
|
|
=================
|
|
*/
|
|
void * Mod_LoadSpriteGroup (void * pin, mspriteframe_t **ppframe, int framenum, enum srcformat fmt)
|
|
{
|
|
dspritegroup_t *pingroup;
|
|
mspritegroup_t *pspritegroup;
|
|
int i, numframes;
|
|
dspriteinterval_t *pin_intervals;
|
|
float *poutintervals;
|
|
void *ptemp;
|
|
float prevtime;
|
|
|
|
pingroup = (dspritegroup_t *)pin;
|
|
|
|
numframes = LittleLong (pingroup->numframes);
|
|
|
|
pspritegroup = (mspritegroup_t *) Hunk_AllocName (sizeof (mspritegroup_t) +
|
|
(numframes - 1) * sizeof (pspritegroup->frames[0]), loadname);
|
|
|
|
pspritegroup->numframes = numframes;
|
|
|
|
*ppframe = (mspriteframe_t *)pspritegroup;
|
|
|
|
pin_intervals = (dspriteinterval_t *)(pingroup + 1);
|
|
|
|
poutintervals = (float *) Hunk_AllocName (numframes * sizeof (float), loadname);
|
|
|
|
pspritegroup->intervals = poutintervals;
|
|
|
|
for (i=0,prevtime=0 ; i<numframes ; i++)
|
|
{
|
|
*poutintervals = LittleFloat (pin_intervals->interval);
|
|
if (*poutintervals <= 0.0)
|
|
Sys_Error ("Mod_LoadSpriteGroup: interval<=0");
|
|
//Spike -- we need to accumulate the previous time too, so we get actual timestamps, otherwise spritegroups won't animate (vanilla bug).
|
|
prevtime = *poutintervals = prevtime+*poutintervals;
|
|
|
|
poutintervals++;
|
|
pin_intervals++;
|
|
}
|
|
|
|
ptemp = (void *)pin_intervals;
|
|
|
|
for (i=0 ; i<numframes ; i++)
|
|
{
|
|
ptemp = Mod_LoadSpriteFrame (ptemp, &pspritegroup->frames[i], framenum * 100 + i, fmt);
|
|
}
|
|
|
|
return ptemp;
|
|
}
|
|
|
|
|
|
/*
|
|
=================
|
|
Mod_LoadSpriteModel
|
|
=================
|
|
*/
|
|
void Mod_LoadSpriteModel (qmodel_t *mod, void *buffer)
|
|
{
|
|
int i;
|
|
int version;
|
|
dsprite_t *pin;
|
|
msprite_t *psprite;
|
|
int numframes;
|
|
int size;
|
|
dspriteframetype_t *pframetype;
|
|
enum srcformat fmt = SRC_INDEXED;
|
|
|
|
pin = (dsprite_t *)buffer;
|
|
mod_base = (byte *)buffer; //johnfitz
|
|
|
|
version = LittleLong (pin->version);
|
|
if (version == 32)
|
|
fmt = SRC_RGBA; //Spike -- spr32 is identical to regular sprites, but uses rgba instead of indexed values. should probably also blend these sprites instead of alphatest, but meh.
|
|
else if (version != SPRITE_VERSION)
|
|
{
|
|
//Spike -- made this more tolerant. its still an error, it just won't crash us out
|
|
Con_Printf( "%s has wrong version number "
|
|
"(%i should be %i)\n", mod->name, version, SPRITE_VERSION);
|
|
mod->type = mod_ext_invalid;
|
|
return;
|
|
}
|
|
|
|
numframes = LittleLong (pin->numframes);
|
|
|
|
size = sizeof (msprite_t) + (numframes - 1) * sizeof (psprite->frames);
|
|
|
|
psprite = (msprite_t *) Hunk_AllocName (size, loadname);
|
|
|
|
mod->cache.data = psprite;
|
|
|
|
psprite->type = LittleLong (pin->type);
|
|
psprite->maxwidth = LittleLong (pin->width);
|
|
psprite->maxheight = LittleLong (pin->height);
|
|
psprite->beamlength = LittleFloat (pin->beamlength);
|
|
mod->synctype = (synctype_t) LittleLong (pin->synctype);
|
|
psprite->numframes = numframes;
|
|
|
|
mod->mins[0] = mod->mins[1] = -psprite->maxwidth/2;
|
|
mod->maxs[0] = mod->maxs[1] = psprite->maxwidth/2;
|
|
mod->mins[2] = -psprite->maxheight/2;
|
|
mod->maxs[2] = psprite->maxheight/2;
|
|
VectorCopy(mod->mins, mod->clipmins);
|
|
VectorCopy(mod->maxs, mod->clipmaxs);
|
|
|
|
//
|
|
// load the frames
|
|
//
|
|
if (numframes < 1)
|
|
Sys_Error ("Mod_LoadSpriteModel: Invalid # of frames: %d\n", numframes);
|
|
|
|
mod->numframes = numframes;
|
|
|
|
pframetype = (dspriteframetype_t *)(pin + 1);
|
|
|
|
for (i=0 ; i<numframes ; i++)
|
|
{
|
|
spriteframetype_t frametype;
|
|
|
|
frametype = (spriteframetype_t) LittleLong (pframetype->type);
|
|
psprite->frames[i].type = frametype;
|
|
|
|
if (frametype == SPR_SINGLE)
|
|
{
|
|
pframetype = (dspriteframetype_t *)
|
|
Mod_LoadSpriteFrame (pframetype + 1, &psprite->frames[i].frameptr, i, fmt);
|
|
}
|
|
else
|
|
{
|
|
pframetype = (dspriteframetype_t *)
|
|
Mod_LoadSpriteGroup (pframetype + 1, &psprite->frames[i].frameptr, i, fmt);
|
|
}
|
|
}
|
|
|
|
mod->type = mod_sprite;
|
|
}
|
|
|
|
//=============================================================================
|
|
|
|
/*
|
|
================
|
|
Mod_Print
|
|
================
|
|
*/
|
|
void Mod_Print (void)
|
|
{
|
|
int i;
|
|
qmodel_t *mod;
|
|
|
|
Con_SafePrintf ("Cached models:\n"); //johnfitz -- safeprint instead of print
|
|
for (i=0, mod=mod_known ; i < mod_numknown ; i++, mod++)
|
|
{
|
|
Con_SafePrintf ("%8p : %s\n", mod->cache.data, mod->name); //johnfitz -- safeprint instead of print
|
|
}
|
|
Con_Printf ("%i models\n",mod_numknown); //johnfitz -- print the total too
|
|
}
|
|
|