spingle/source/gl_model.c

2865 lines
75 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 "q_defs.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);
qmodel_t *Mod_LoadModel(qmodel_t *mod, bool crash);
cvar_t external_ents = {"external_ents", "1", CVAR_ARCHIVE};
static byte *mod_novis;
static int32_t mod_novis_capacity;
static byte *mod_decompressed;
static int32_t mod_decompressed_capacity;
#define MAX_MOD_KNOWN 2048 /*johnfitz -- was 512 */
qmodel_t mod_known[MAX_MOD_KNOWN];
int32_t 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);
//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)
{
int32_t c;
byte *out;
byte *outend;
int32_t 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 %" PRIi32 " 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;
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)
{
int32_t 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 %" PRIi32 " bytes", mod_novis_capacity);
memset(mod_novis, 0xff, mod_novis_capacity);
}
return mod_novis;
}
/*
===================
Mod_ClearAll
===================
*/
void Mod_ClearAll(void)
{
int32_t 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
}
}
void Mod_ResetAll(void)
{
int32_t 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);
memset(mod, 0, sizeof(qmodel_t));
}
mod_numknown = 0;
}
/*
==================
Mod_FindName
==================
*/
qmodel_t *Mod_FindName(const char *name)
{
int32_t 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, bool crash)
{
byte *buf;
byte stackbuf[1024]; // avoid dirtying the cache heap
int32_t mod_type;
if(!mod->needload)
{
if(mod->type == mod_alias)
{
if(Cache_Check(&mod->cache))
return mod;
}
else
return mod; // not cached at all
}
//
// because the world is so huge, load it one piece at a time
//
if(!crash)
{
}
//
// load the file
//
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"
return NULL;
}
//
// 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 = LittleLong(BytesLong(buf));
switch(mod_type)
{
case IDPOLYHEADER:
Mod_LoadAliasModel(mod, buf);
break;
case IDSPRITEHEADER:
Mod_LoadSpriteModel(mod, buf);
break;
default:
Mod_LoadBrushModel(mod, buf);
break;
}
return mod;
}
/*
==================
Mod_ForName
Loads in a model for the given name
==================
*/
qmodel_t *Mod_ForName(const char *name, bool crash)
{
qmodel_t *mod;
mod = Mod_FindName(name);
return Mod_LoadModel(mod, crash);
}
/*
===============================================================================
BRUSHMODEL LOADING
===============================================================================
*/
byte *mod_base;
/*
=================
Mod_CheckFullbrights -- johnfitz
=================
*/
bool Mod_CheckFullbrights(byte *pixels, int32_t count)
{
int32_t i;
for(i = 0; i < count; i++)
if(*pixels++ > 223)
return true;
return false;
}
/*
=================
Mod_LoadTextures
=================
*/
void Mod_LoadTextures(lump_t *l)
{
int32_t 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];
int32_t nummiptex;
src_offset_t offset;
int32_t mark, fwidth, fheight;
char filename[MAX_OSPATH], filename2[MAX_OSPATH], mapname[MAX_OSPATH];
byte *data;
extern byte *hunk_base;
//johnfitz
//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);
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
// 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));
}
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 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 = Image_LoadImage(filename, &fwidth, &fheight);
if(!data)
{
q_snprintf(filename, sizeof(filename), "textures/#%s", tx->name + 1);
data = Image_LoadImage(filename, &fwidth, &fheight);
}
//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_AllocName(gl_warpimagesize * gl_warpimagesize * 4, "temporary check"); //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;
}
else //regular texture
{
// ericw -- fence textures
int32_t 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 = Image_LoadImage(filename, &fwidth, &fheight);
if(!data)
{
q_snprintf(filename, sizeof(filename), "textures/%s", tx->name);
data = Image_LoadImage(filename, &fwidth, &fheight);
}
//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
Hunk_FreeToLowMark(mark);
q_snprintf(filename2, sizeof(filename2), "%s_glow", filename);
data = Image_LoadImage(filename2, &fwidth, &fheight);
if(!data)
{
q_snprintf(filename2, sizeof(filename2), "%s_luma", filename);
data = Image_LoadImage(filename2, &fwidth, &fheight);
}
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);
}
}
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 %" PRIi32 " 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 %" PRIi32 " 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)
{
int32_t i, mark;
byte *in, *out, *data;
byte d;
char litfilename[MAX_OSPATH];
uint32_t path_id;
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(((int32_t *)data)[1]);
if(i == 1)
{
Con_DPrintf2("%s loaded\n", litfilename);
loadmodel->lightdata = data + 8;
return;
}
else
{
Hunk_FreeToLowMark(mark);
Con_Printf("Unknown .lit file version (%" PRIi32 ")\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 = (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;
int32_t mark;
uint32_t path_id;
if(! external_ents.value)
goto loadembedded;
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;
}
}
loadembedded:
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_LoadVertexes
=================
*/
void Mod_LoadVertexes(lump_t *l)
{
dvertex_t *in;
mvertex_t *out;
int32_t 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, int32_t bsp2)
{
medge_t *out;
int32_t 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] = (uint16_t)LittleShort(in->v[0]);
out->v[1] = (uint16_t)LittleShort(in->v[1]);
}
}
}
/*
=================
Mod_LoadTexinfo
=================
*/
void Mod_LoadTexinfo(lump_t *l)
{
texinfo_t *in;
mtexinfo_t *out;
int32_t i, j, count, miptex;
float len1, len2;
int32_t 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;
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: %" PRIi32 " 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;
int32_t i, j, e;
mvertex_t *v;
mtexinfo_t *tex;
int32_t bmins[2], bmaxs[2];
mins[0] = mins[1] = 999999;
maxs[0] = maxs[1] = -99999;
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;
}
}
for(i = 0 ; i < 2 ; i++)
{
bmins[i] = floor(mins[i] / 16);
bmaxs[i] = ceil(maxs[i] / 16);
s->texturemins[i] = bmins[i] * 16;
s->extents[i] = (bmaxs[i] - bmins[i]) * 16;
if(!(tex->flags & TEX_SPECIAL) && s->extents[i] > 2000) //johnfitz -- was 512 in glq, 256 in winq
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];
int32_t 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 = Hunk_AllocName(sizeof(glpoly_t) + (numverts - 4) * VERTEXSIZE * sizeof(float), __func__);
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)
{
int32_t i, e;
mvertex_t *v;
s->mins[0] = s->mins[1] = s->mins[2] = 9999;
s->maxs[0] = s->maxs[1] = s->maxs[2] = -9999;
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, bool bsp2)
{
dsface_t *ins;
dlface_t *inl;
msurface_t *out;
int32_t i, count, surfnum, lofs;
int32_t planenum, side, texinfon;
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("%" PRIi32 " faces exceeds standard limit of 32767.\n", count);
//johnfitz
loadmodel->surfaces = out;
loadmodel->numsurfaces = count;
for(surfnum = 0 ; surfnum < count ; surfnum++, out++)
{
if(bsp2)
{
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
{
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++;
}
out->flags = 0;
if(side)
out->flags |= SURF_PLANEBACK;
out->plane = loadmodel->planes + planenum;
out->texinfo = loadmodel->texinfo + texinfon;
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 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)
{
int32_t 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("%" PRIi32 " 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 = (uint16_t)LittleShort(in->firstface); //johnfitz -- explicit cast as uint16_t
out->numsurfaces = (uint16_t)LittleShort(in->numfaces); //johnfitz -- explicit cast as uint16_t
for(j = 0 ; j < 2 ; j++)
{
//johnfitz -- hack to handle nodes > 32k, adapted from darkplaces
p = (uint16_t)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 %" PRIi32 " (file has only %" PRIi32 " 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)
{
int32_t 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 uint16_t
out->numsurfaces = LittleLong(in->numfaces); //johnfitz -- explicit cast as uint16_t
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 %" PRIi32 " (file has only %" PRIi32 " 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)
{
int32_t 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 uint16_t
out->numsurfaces = LittleLong(in->numfaces); //johnfitz -- explicit cast as uint16_t
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 %" PRIi32 " (file has only %" PRIi32 " 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, int32_t 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, int32_t filelen)
{
mleaf_t *out;
int32_t 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: %" PRIi32 " 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 + (uint16_t)LittleShort(in->firstmarksurface); //johnfitz -- uint16_t
out->nummarksurfaces = (uint16_t)LittleShort(in->nummarksurfaces); //johnfitz -- uint16_t
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, int32_t filelen)
{
mleaf_t *out;
int32_t 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 -- uint16_t
out->nummarksurfaces = LittleLong(in->nummarksurfaces); //johnfitz -- uint16_t
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, int32_t filelen)
{
mleaf_t *out;
int32_t 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 -- uint16_t
out->nummarksurfaces = LittleLong(in->nummarksurfaces); //johnfitz -- uint16_t
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, int32_t bsp2)
{
void *in = 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);
}
/*
=================
Mod_LoadClipnodes
=================
*/
void Mod_LoadClipnodes(lump_t *l, bool bsp2)
{
dsclipnode_t *ins;
dlclipnode_t *inl;
mclipnode_t *out; //johnfitz -- was dclipnode_t
int32_t 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);
}
out = (mclipnode_t *) Hunk_AllocName(count * sizeof(*out), loadname);
//johnfitz -- warn about exceeding old limits
if(count > 32767 && !bsp2)
Con_DWarning("%" PRIi32 " 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] = (uint16_t)LittleShort(ins->children[0]);
out->children[1] = (uint16_t)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
int32_t 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;
}
}
}
/*
=================
Mod_LoadMarksurfaces
=================
*/
void Mod_LoadMarksurfaces(lump_t *l, int32_t bsp2)
{
int32_t i, j, count;
msurface_t **out;
if(bsp2)
{
uint32_t *in = (uint32_t *)(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
{
int16_t *in = (int16_t *)(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("%" PRIi32 " marksurfaces exceeds standard limit of 32767.\n", count);
//johnfitz
for(i = 0 ; i < count ; i++)
{
j = (uint16_t)LittleShort(in[i]); //johnfitz -- explicit cast as uint16_t
if(j >= loadmodel->numsurfaces)
Sys_Error("Mod_LoadMarksurfaces: bad surface number");
out[i] = loadmodel->surfaces + j;
}
}
}
/*
=================
Mod_LoadSurfedges
=================
*/
void Mod_LoadSurfedges(lump_t *l)
{
int32_t i, count;
int32_t *in, *out;
in = (int32_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 = (int32_t *) 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)
{
int32_t i, j;
mplane_t *out;
dplane_t *in;
int32_t count;
int32_t 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)
{
int32_t 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)
{
dmodel_t *in;
dmodel_t *out;
int32_t i, j, count;
in = (dmodel_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 = (dmodel_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++)
out->headnode[j] = LittleLong(in->headnode[j]);
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("%" PRIi32 " 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, int32_t hull, int32_t 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)
{
int32_t i, j;
int32_t bsp2;
dheader_t *header;
dmodel_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:
Sys_Error("Mod_LoadBrushModel: %s has wrong version number (%" PRIi32 " should be %" PRIi32 ")", mod->name, mod->bspversion, BSPVERSION);
break;
}
// swap all the lumps
mod_base = (byte *)header;
for(i = 0; i < (int32_t)sizeof(dheader_t) / 4; i++)
((int32_t *)header)[i] = LittleLong(((int32_t *)header)[i]);
// 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_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_LoadEntities(&header->lumps[LUMP_ENTITIES]);
Mod_LoadSubmodels(&header->lumps[LUMP_MODELS]);
Mod_MakeHull0();
mod->numframes = 2; // regular and alternate animation
//
// 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];
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, "*%" PRIi32, i + 1);
loadmodel = Mod_FindName(name);
*loadmodel = *mod;
strcpy(loadmodel->name, name);
mod = loadmodel;
}
}
}
/*
==============================================================================
ALIAS MODELS
==============================================================================
*/
aliashdr_t *pheader;
stvert_t stverts[MAXALIASVERTS];
mtriangle_t triangles[MAXALIASTRIS];
// a pose is a single set of vertexes. a frame may be
// an animating sequence of poses
trivertx_t *poseverts[MAXALIASFRAMES];
int32_t posenum;
byte **player_8bit_texels_tbl;
byte *player_8bit_texels;
/*
=================
Mod_LoadAliasFrame
=================
*/
void * Mod_LoadAliasFrame(void * pin, maliasframedesc_t *frame)
{
trivertx_t *pinframe;
int32_t i;
daliasframe_t *pdaliasframe;
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[posenum] = pinframe;
posenum++;
pinframe += pheader->numverts;
return pinframe;
}
/*
=================
Mod_LoadAliasGroup
=================
*/
void *Mod_LoadAliasGroup(void * pin, maliasframedesc_t *frame)
{
daliasgroup_t *pingroup;
int32_t 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 = pin_intervals;
for(i = 0 ; i < numframes ; i++)
{
poseverts[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
{
int16_t 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, int32_t skinwidth, int32_t skinheight)
{
byte fillcolor = *skin; // assume this is the pixel to fill
floodfill_t fifo[FLOODFILL_FIFO_SIZE];
int32_t inpt = 0, outpt = 0;
int32_t filledcolor = -1;
int32_t i;
if(filledcolor == -1)
{
filledcolor = 0;
// attempt to find opaque black
for(i = 0; i < 256; ++i)
if(d_8to24table[i] == (255 << 0)) // 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 %" PRIi32 " to %" PRIi32 "\n", fillcolor, filledcolor );
return;
}
fifo[inpt].x = 0, fifo[inpt].y = 0;
inpt = (inpt + 1) & FLOODFILL_FIFO_MASK;
while(outpt != inpt)
{
int32_t x = fifo[outpt].x, y = fifo[outpt].y;
int32_t 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(int32_t numskins, daliasskintype_t *pskintype)
{
int32_t 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
uint32_t texflags = TEXPREF_PAD;
skin = (byte *)(pskintype + 1);
if(numskins < 1 || numskins > MAX_SKINS)
Sys_Error("Mod_LoadAliasModel: Invalid # of skins: %" PRIi32 "\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%" PRIi32, 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%" PRIi32 "_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%" PRIi32 "_%" PRIi32, 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%" PRIi32 "_%" PRIi32 "_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 pskintype;
}
//=========================================================================
/*
=================
Mod_CalcAliasBounds -- johnfitz -- calculate bounds of alias model for nonrotated, yawrotated, and fullrotated cases
=================
*/
void Mod_CalcAliasBounds(aliashdr_t *a)
{
int32_t 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] = 999999;
loadmodel->maxs[i] = loadmodel->ymaxs[i] = loadmodel->rmaxs[i] = -999999;
radius = yawradius = 0;
}
//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[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;
}
//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 bool
nameInList(const char *list, const char *name)
{
const char *s;
char tmp[MAX_QPATH];
int32_t 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 || mod->type != mod_alias)
return;
mod->flags &= (0xFF | MF_HOLEY); //only preserve first byte, plus MF_HOLEY
// 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;
}
/*
=================
Mod_LoadAliasModel
=================
*/
void Mod_LoadAliasModel(qmodel_t *mod, void *buffer)
{
int32_t i, j;
mdl_t *pinmodel;
stvert_t *pinstverts;
dtriangle_t *pintriangles;
int32_t version, numframes;
int32_t size;
daliasframetype_t *pframetype;
daliasskintype_t *pskintype;
int32_t 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 (%" PRIi32 " should be %" PRIi32 ")",
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)
Sys_Error("model %s has a skin taller than %" PRIi32, mod->name,
MAX_LBM_HEIGHT);
pheader->numverts = LittleLong(pinmodel->numverts);
if(pheader->numverts <= 0)
Sys_Error("model %s has no vertices", mod->name);
if(pheader->numverts > MAXALIASVERTS)
Sys_Error("model %s has too many vertices", mod->name);
pheader->numtris = LittleLong(pinmodel->numtris);
if(pheader->numtris <= 0)
Sys_Error("model %s has no triangles", mod->name);
pheader->numframes = LittleLong(pinmodel->numframes);
numframes = pheader->numframes;
if(numframes < 1)
Sys_Error("Mod_LoadAliasModel: Invalid # of frames: %" PRIi32 "\n", numframes);
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;
mod->type = mod_alias;
Mod_SetExtraFlags(mod); //johnfitz
Mod_CalcAliasBounds(pheader); //johnfitz
//
// build the draw lists
//
GL_MakeAliasModelDisplayLists(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, int32_t framenum)
{
dspriteframe_t *pinframe;
mspriteframe_t *pspriteframe;
int32_t 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;
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%" PRIi32, loadmodel->name, framenum);
offset = (src_offset_t)(pinframe + 1) - (src_offset_t)mod_base; //johnfitz
pspriteframe->gltexture =
TexMgr_LoadImage(loadmodel, name, width, height, SRC_INDEXED,
(byte *)(pinframe + 1), loadmodel->name, offset,
TEXPREF_PAD | TEXPREF_ALPHA | TEXPREF_NOPICMIP); //johnfitz -- TexMgr
return (byte *)pinframe + sizeof(dspriteframe_t) + size;
}
/*
=================
Mod_LoadSpriteGroup
=================
*/
void * Mod_LoadSpriteGroup(void * pin, mspriteframe_t **ppframe, int32_t framenum)
{
dspritegroup_t *pingroup;
mspritegroup_t *pspritegroup;
int32_t i, numframes;
dspriteinterval_t *pin_intervals;
float *poutintervals;
void *ptemp;
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 ; i < numframes ; i++)
{
*poutintervals = LittleFloat(pin_intervals->interval);
if(*poutintervals <= 0.0)
Sys_Error("Mod_LoadSpriteGroup: interval<=0");
poutintervals++;
pin_intervals++;
}
ptemp = pin_intervals;
for(i = 0 ; i < numframes ; i++)
{
ptemp = Mod_LoadSpriteFrame(ptemp, &pspritegroup->frames[i], framenum * 100 + i);
}
return ptemp;
}
/*
=================
Mod_LoadSpriteModel
=================
*/
void Mod_LoadSpriteModel(qmodel_t *mod, void *buffer)
{
int32_t i;
int32_t version;
dsprite_t *pin;
msprite_t *psprite;
int32_t numframes;
int32_t size;
dspriteframetype_t *pframetype;
pin = (dsprite_t *)buffer;
mod_base = (byte *)buffer; //johnfitz
version = LittleLong(pin->version);
if(version != SPRITE_VERSION)
Sys_Error("%s has wrong version number "
"(%" PRIi32 " should be %" PRIi32 ")", mod->name, version, SPRITE_VERSION);
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;
//
// load the frames
//
if(numframes < 1)
Sys_Error("Mod_LoadSpriteModel: Invalid # of frames: %" PRIi32 "\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);
}
else
{
pframetype = (dspriteframetype_t *)
Mod_LoadSpriteGroup(pframetype + 1, &psprite->frames[i].frameptr, i);
}
}
mod->type = mod_sprite;
}
//=============================================================================
/*
================
Mod_Print
================
*/
void Mod_Print(void)
{
int32_t 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("%" PRIi32 " models\n", mod_numknown); //johnfitz -- print the total too
}