2865 lines
75 KiB
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
|
|
}
|
|
|