mirror of
https://github.com/Shpoike/Quakespasm.git
synced 2024-11-15 09:21:36 +00:00
aa4c9ebc40
git-svn-id: http://svn.code.sf.net/p/quakespasm/code/trunk/quakespasm@676 af15c1b1-3010-417e-b628-4374ebc0bcbd
2420 lines
61 KiB
C
2420 lines
61 KiB
C
/*
|
|
Copyright (C) 1996-2001 Id Software, Inc.
|
|
Copyright (C) 2002-2009 John Fitzgibbons and others
|
|
Copyright (C) 2011 O.Sezer
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU General Public License
|
|
as published by the Free Software Foundation; either version 2
|
|
of the License, or (at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
|
|
See the GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
*/
|
|
// models.c -- model loading and caching
|
|
|
|
// models are the only shared resource between a client and server running
|
|
// on the same machine.
|
|
|
|
#include "quakedef.h"
|
|
|
|
qmodel_t *loadmodel;
|
|
char loadname[32]; // for hunk tags
|
|
|
|
void Mod_LoadSpriteModel (qmodel_t *mod, void *buffer);
|
|
void Mod_LoadBrushModel (qmodel_t *mod, void *buffer);
|
|
void Mod_LoadAliasModel (qmodel_t *mod, void *buffer);
|
|
qmodel_t *Mod_LoadModel (qmodel_t *mod, qboolean crash);
|
|
|
|
cvar_t external_ents = {"external_ents", "1", CVAR_ARCHIVE};
|
|
|
|
byte mod_novis[MAX_MAP_LEAFS/8];
|
|
|
|
#define MAX_MOD_KNOWN 2048 /*johnfitz -- was 512 */
|
|
qmodel_t mod_known[MAX_MOD_KNOWN];
|
|
int mod_numknown;
|
|
|
|
texture_t *r_notexture_mip; //johnfitz -- moved here from r_main.c
|
|
texture_t *r_notexture_mip2; //johnfitz -- used for non-lightmapped surfs with a missing texture
|
|
|
|
/*
|
|
===============
|
|
Mod_Init
|
|
===============
|
|
*/
|
|
void Mod_Init (void)
|
|
{
|
|
Cvar_RegisterVariable (&gl_subdivide_size);
|
|
Cvar_RegisterVariable (&external_ents);
|
|
|
|
memset (mod_novis, 0xff, sizeof(mod_novis));
|
|
|
|
//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)
|
|
{
|
|
static byte decompressed[MAX_MAP_LEAFS/8];
|
|
int c;
|
|
byte *out;
|
|
int row;
|
|
|
|
row = (model->numleafs+7)>>3;
|
|
out = decompressed;
|
|
|
|
#if 0
|
|
memcpy (out, in, row);
|
|
#else
|
|
if (!in)
|
|
{ // no vis info, so make all visible
|
|
while (row)
|
|
{
|
|
*out++ = 0xff;
|
|
row--;
|
|
}
|
|
return decompressed;
|
|
}
|
|
|
|
do
|
|
{
|
|
if (*in)
|
|
{
|
|
*out++ = *in++;
|
|
continue;
|
|
}
|
|
|
|
c = in[1];
|
|
in += 2;
|
|
while (c)
|
|
{
|
|
*out++ = 0;
|
|
c--;
|
|
}
|
|
} while (out - decompressed < row);
|
|
#endif
|
|
|
|
return decompressed;
|
|
}
|
|
|
|
byte *Mod_LeafPVS (mleaf_t *leaf, qmodel_t *model)
|
|
{
|
|
if (leaf == model->leafs)
|
|
return mod_novis;
|
|
return Mod_DecompressVis (leaf->compressed_vis, model);
|
|
}
|
|
|
|
/*
|
|
===================
|
|
Mod_ClearAll
|
|
===================
|
|
*/
|
|
void Mod_ClearAll (void)
|
|
{
|
|
int i;
|
|
qmodel_t *mod;
|
|
|
|
for (i=0 , mod=mod_known ; i<mod_numknown ; i++, mod++)
|
|
if (mod->type != mod_alias)
|
|
{
|
|
mod->needload = true;
|
|
TexMgr_FreeTexturesForOwner (mod); //johnfitz
|
|
}
|
|
}
|
|
|
|
/*
|
|
==================
|
|
Mod_FindName
|
|
|
|
==================
|
|
*/
|
|
qmodel_t *Mod_FindName (const char *name)
|
|
{
|
|
int i;
|
|
qmodel_t *mod;
|
|
|
|
if (!name[0])
|
|
Sys_Error ("Mod_FindName: NULL name"); //johnfitz -- was "Mod_ForName"
|
|
|
|
//
|
|
// search the currently loaded models
|
|
//
|
|
for (i=0 , mod=mod_known ; i<mod_numknown ; i++, mod++)
|
|
if (!strcmp (mod->name, name) )
|
|
break;
|
|
|
|
if (i == mod_numknown)
|
|
{
|
|
if (mod_numknown == MAX_MOD_KNOWN)
|
|
Sys_Error ("mod_numknown == MAX_MOD_KNOWN");
|
|
q_strlcpy (mod->name, name, MAX_QPATH);
|
|
mod->needload = true;
|
|
mod_numknown++;
|
|
}
|
|
|
|
return mod;
|
|
}
|
|
|
|
/*
|
|
==================
|
|
Mod_TouchModel
|
|
|
|
==================
|
|
*/
|
|
void Mod_TouchModel (const char *name)
|
|
{
|
|
qmodel_t *mod;
|
|
|
|
mod = Mod_FindName (name);
|
|
|
|
if (!mod->needload)
|
|
{
|
|
if (mod->type == mod_alias)
|
|
Cache_Check (&mod->cache);
|
|
}
|
|
}
|
|
|
|
/*
|
|
==================
|
|
Mod_LoadModel
|
|
|
|
Loads a model into the cache
|
|
==================
|
|
*/
|
|
qmodel_t *Mod_LoadModel (qmodel_t *mod, qboolean crash)
|
|
{
|
|
byte *buf;
|
|
byte stackbuf[1024]; // avoid dirtying the cache heap
|
|
int mod_type;
|
|
|
|
if (!mod->needload)
|
|
{
|
|
if (mod->type == mod_alias)
|
|
{
|
|
if (Cache_Check (&mod->cache))
|
|
return mod;
|
|
}
|
|
else
|
|
return mod; // not cached at all
|
|
}
|
|
|
|
//
|
|
// 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)
|
|
Sys_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 = (buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24));
|
|
switch (mod_type)
|
|
{
|
|
case IDPOLYHEADER:
|
|
Mod_LoadAliasModel (mod, buf);
|
|
break;
|
|
|
|
case IDSPRITEHEADER:
|
|
Mod_LoadSpriteModel (mod, buf);
|
|
break;
|
|
|
|
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, qboolean crash)
|
|
{
|
|
qmodel_t *mod;
|
|
|
|
mod = Mod_FindName (name);
|
|
|
|
return Mod_LoadModel (mod, crash);
|
|
}
|
|
|
|
|
|
/*
|
|
===============================================================================
|
|
|
|
BRUSHMODEL LOADING
|
|
|
|
===============================================================================
|
|
*/
|
|
|
|
byte *mod_base;
|
|
|
|
/*
|
|
=================
|
|
Mod_CheckFullbrights -- johnfitz
|
|
=================
|
|
*/
|
|
qboolean Mod_CheckFullbrights (byte *pixels, int count)
|
|
{
|
|
int i;
|
|
for (i = 0; i < count; i++)
|
|
if (*pixels++ > 223)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
Mod_LoadTextures
|
|
=================
|
|
*/
|
|
void Mod_LoadTextures (lump_t *l)
|
|
{
|
|
int i, j, pixels, num, maxanim, altmax;
|
|
miptex_t *mt;
|
|
texture_t *tx, *tx2;
|
|
texture_t *anims[10];
|
|
texture_t *altanims[10];
|
|
dmiptexlump_t *m;
|
|
//johnfitz -- more variables
|
|
char texturename[64];
|
|
int nummiptex;
|
|
src_offset_t offset;
|
|
int mark, fwidth, fheight;
|
|
char filename[MAX_OSPATH], filename2[MAX_OSPATH], mapname[MAX_OSPATH];
|
|
byte *data;
|
|
extern byte *hunk_base;
|
|
//johnfitz
|
|
|
|
//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
|
|
memcpy ( tx+1, mt+1, pixels);
|
|
|
|
tx->update_warp = false; //johnfitz
|
|
tx->warpimage = NULL; //johnfitz
|
|
tx->fullbright = NULL; //johnfitz
|
|
|
|
//johnfitz -- lots of changes
|
|
if (!isDedicated) //no texture uploading for dedicated server
|
|
{
|
|
if (!Q_strncasecmp(tx->name,"sky",3)) //sky texture //also note -- was Q_strncmp, changed to match qbsp
|
|
Sky_LoadTexture (tx);
|
|
else if (tx->name[0] == '*') //warping texture
|
|
{
|
|
//external textures -- first look in "textures/mapname/" then look in "textures/"
|
|
mark = Hunk_LowMark();
|
|
COM_StripExtension (loadmodel->name + 5, mapname, sizeof(mapname));
|
|
q_snprintf (filename, sizeof(filename), "textures/%s/#%s", mapname, tx->name+1); //this also replaces the '*' with a '#'
|
|
data = 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_Alloc (gl_warpimagesize*gl_warpimagesize*4); //make sure hunk is big enough so we don't reach an illegal address
|
|
Hunk_FreeToLowMark (mark);
|
|
q_snprintf (texturename, sizeof(texturename), "%s_warp", texturename);
|
|
tx->warpimage = TexMgr_LoadImage (loadmodel, texturename, gl_warpimagesize,
|
|
gl_warpimagesize, SRC_RGBA, hunk_base, "", (src_offset_t)hunk_base, TEXPREF_NOPICMIP | TEXPREF_WARPIMAGE);
|
|
tx->update_warp = true;
|
|
}
|
|
else //regular 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);
|
|
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);
|
|
|
|
//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 );
|
|
}
|
|
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);
|
|
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);
|
|
}
|
|
else
|
|
{
|
|
tx->gltexture = TexMgr_LoadImage (loadmodel, texturename, tx->width, tx->height,
|
|
SRC_INDEXED, (byte *)(tx+1), loadmodel->name, offset, TEXPREF_MIPMAP);
|
|
}
|
|
}
|
|
Hunk_FreeToLowMark (mark);
|
|
}
|
|
}
|
|
//johnfitz
|
|
}
|
|
|
|
//johnfitz -- last 2 slots in array should be filled with dummy textures
|
|
loadmodel->textures[loadmodel->numtextures-2] = r_notexture_mip; //for lightmapped surfs
|
|
loadmodel->textures[loadmodel->numtextures-1] = r_notexture_mip2; //for SURF_DRAWTILED surfs
|
|
|
|
//
|
|
// sequence the animations
|
|
//
|
|
for (i=0 ; i<nummiptex ; i++)
|
|
{
|
|
tx = loadmodel->textures[i];
|
|
if (!tx || tx->name[0] != '+')
|
|
continue;
|
|
if (tx->anim_next)
|
|
continue; // allready sequenced
|
|
|
|
// find the number of frames in the animation
|
|
memset (anims, 0, sizeof(anims));
|
|
memset (altanims, 0, sizeof(altanims));
|
|
|
|
maxanim = tx->name[1];
|
|
altmax = 0;
|
|
if (maxanim >= 'a' && maxanim <= 'z')
|
|
maxanim -= 'a' - 'A';
|
|
if (maxanim >= '0' && maxanim <= '9')
|
|
{
|
|
maxanim -= '0';
|
|
altmax = 0;
|
|
anims[maxanim] = tx;
|
|
maxanim++;
|
|
}
|
|
else if (maxanim >= 'A' && maxanim <= 'J')
|
|
{
|
|
altmax = maxanim - 'A';
|
|
maxanim = 0;
|
|
altanims[altmax] = tx;
|
|
altmax++;
|
|
}
|
|
else
|
|
Sys_Error ("Bad animating texture %s", tx->name);
|
|
|
|
for (j=i+1 ; j<nummiptex ; j++)
|
|
{
|
|
tx2 = loadmodel->textures[j];
|
|
if (!tx2 || tx2->name[0] != '+')
|
|
continue;
|
|
if (strcmp (tx2->name+2, tx->name+2))
|
|
continue;
|
|
|
|
num = tx2->name[1];
|
|
if (num >= 'a' && num <= 'z')
|
|
num -= 'a' - 'A';
|
|
if (num >= '0' && num <= '9')
|
|
{
|
|
num -= '0';
|
|
anims[num] = tx2;
|
|
if (num+1 > maxanim)
|
|
maxanim = num + 1;
|
|
}
|
|
else if (num >= 'A' && num <= 'J')
|
|
{
|
|
num = num - 'A';
|
|
altanims[num] = tx2;
|
|
if (num+1 > altmax)
|
|
altmax = num+1;
|
|
}
|
|
else
|
|
Sys_Error ("Bad animating texture %s", tx->name);
|
|
}
|
|
|
|
#define ANIM_CYCLE 2
|
|
// link them all together
|
|
for (j=0 ; j<maxanim ; j++)
|
|
{
|
|
tx2 = anims[j];
|
|
if (!tx2)
|
|
Sys_Error ("Missing frame %i of %s",j, tx->name);
|
|
tx2->anim_total = maxanim * ANIM_CYCLE;
|
|
tx2->anim_min = j * ANIM_CYCLE;
|
|
tx2->anim_max = (j+1) * ANIM_CYCLE;
|
|
tx2->anim_next = anims[ (j+1)%maxanim ];
|
|
if (altmax)
|
|
tx2->alternate_anims = altanims[0];
|
|
}
|
|
for (j=0 ; j<altmax ; j++)
|
|
{
|
|
tx2 = altanims[j];
|
|
if (!tx2)
|
|
Sys_Error ("Missing frame %i of %s",j, tx->name);
|
|
tx2->anim_total = altmax * ANIM_CYCLE;
|
|
tx2->anim_min = j * ANIM_CYCLE;
|
|
tx2->anim_max = (j+1) * ANIM_CYCLE;
|
|
tx2->anim_next = altanims[ (j+1)%altmax ];
|
|
if (maxanim)
|
|
tx2->alternate_anims = anims[0];
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
=================
|
|
Mod_LoadLighting -- johnfitz -- replaced with lit support code via lordhavoc
|
|
=================
|
|
*/
|
|
void Mod_LoadLighting (lump_t *l)
|
|
{
|
|
int i, mark;
|
|
byte *in, *out, *data;
|
|
byte d;
|
|
char litfilename[MAX_OSPATH];
|
|
unsigned int path_id;
|
|
|
|
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_Printf("ignored %s from a gamedir with lower priority\n", litfilename);
|
|
}
|
|
else
|
|
if (data[0] == 'Q' && data[1] == 'L' && data[2] == 'I' && data[3] == 'T')
|
|
{
|
|
i = LittleLong(((int *)data)[1]);
|
|
if (i == 1)
|
|
{
|
|
Con_DPrintf("%s loaded", litfilename);
|
|
loadmodel->lightdata = data + 8;
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
Hunk_FreeToLowMark(mark);
|
|
Con_Printf("Unknown .lit file version (%d)\n", i);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Hunk_FreeToLowMark(mark);
|
|
Con_Printf("Corrupt .lit file (old version?), ignoring\n");
|
|
}
|
|
}
|
|
// LordHavoc: no .lit found, expand the white lighting data to color
|
|
if (!l->filelen)
|
|
return;
|
|
loadmodel->lightdata = (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)
|
|
{
|
|
if (!l->filelen)
|
|
{
|
|
loadmodel->visdata = NULL;
|
|
return;
|
|
}
|
|
loadmodel->visdata = (byte *) Hunk_AllocName ( l->filelen, loadname);
|
|
memcpy (loadmodel->visdata, mod_base + l->fileofs, l->filelen);
|
|
}
|
|
|
|
|
|
/*
|
|
=================
|
|
Mod_LoadEntities
|
|
=================
|
|
*/
|
|
void Mod_LoadEntities (lump_t *l)
|
|
{
|
|
char entfilename[MAX_QPATH];
|
|
char *ents;
|
|
int mark;
|
|
unsigned int path_id;
|
|
|
|
if (! external_ents.value)
|
|
goto _load_embedded;
|
|
|
|
q_strlcpy(entfilename, loadmodel->name, sizeof(entfilename));
|
|
COM_StripExtension(entfilename, entfilename, sizeof(entfilename));
|
|
q_strlcat(entfilename, ".ent", sizeof(entfilename));
|
|
Con_DPrintf("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_Printf("ignored %s from a gamedir with lower priority\n", entfilename);
|
|
}
|
|
else
|
|
{
|
|
loadmodel->entities = ents;
|
|
Con_DPrintf("Loaded external entity file %s\n", entfilename);
|
|
return;
|
|
}
|
|
}
|
|
|
|
_load_embedded:
|
|
if (!l->filelen)
|
|
{
|
|
loadmodel->entities = NULL;
|
|
return;
|
|
}
|
|
loadmodel->entities = (char *) Hunk_AllocName ( l->filelen, loadname);
|
|
memcpy (loadmodel->entities, mod_base + l->fileofs, l->filelen);
|
|
}
|
|
|
|
|
|
/*
|
|
=================
|
|
Mod_LoadVertexes
|
|
=================
|
|
*/
|
|
void Mod_LoadVertexes (lump_t *l)
|
|
{
|
|
dvertex_t *in;
|
|
mvertex_t *out;
|
|
int i, count;
|
|
|
|
in = (dvertex_t *)(mod_base + l->fileofs);
|
|
if (l->filelen % sizeof(*in))
|
|
Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
|
|
count = l->filelen / sizeof(*in);
|
|
out = (mvertex_t *) Hunk_AllocName ( count*sizeof(*out), loadname);
|
|
|
|
loadmodel->vertexes = out;
|
|
loadmodel->numvertexes = count;
|
|
|
|
for ( i=0 ; i<count ; i++, in++, out++)
|
|
{
|
|
out->position[0] = LittleFloat (in->point[0]);
|
|
out->position[1] = LittleFloat (in->point[1]);
|
|
out->position[2] = LittleFloat (in->point[2]);
|
|
}
|
|
}
|
|
|
|
/*
|
|
=================
|
|
Mod_LoadEdges
|
|
=================
|
|
*/
|
|
void Mod_LoadEdges (lump_t *l)
|
|
{
|
|
dedge_t *in;
|
|
medge_t *out;
|
|
int i, count;
|
|
|
|
in = (dedge_t *)(mod_base + l->fileofs);
|
|
if (l->filelen % sizeof(*in))
|
|
Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
|
|
count = l->filelen / sizeof(*in);
|
|
out = (medge_t *) Hunk_AllocName ( (count + 1) * sizeof(*out), loadname);
|
|
|
|
loadmodel->edges = out;
|
|
loadmodel->numedges = count;
|
|
|
|
for ( i=0 ; i<count ; i++, in++, out++)
|
|
{
|
|
out->v[0] = (unsigned short)LittleShort(in->v[0]);
|
|
out->v[1] = (unsigned short)LittleShort(in->v[1]);
|
|
}
|
|
}
|
|
|
|
/*
|
|
=================
|
|
Mod_LoadTexinfo
|
|
=================
|
|
*/
|
|
void Mod_LoadTexinfo (lump_t *l)
|
|
{
|
|
texinfo_t *in;
|
|
mtexinfo_t *out;
|
|
int i, j, count, miptex;
|
|
float len1, len2;
|
|
int missing = 0; //johnfitz
|
|
|
|
in = (texinfo_t *)(mod_base + l->fileofs);
|
|
if (l->filelen % sizeof(*in))
|
|
Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
|
|
count = l->filelen / sizeof(*in);
|
|
out = (mtexinfo_t *) Hunk_AllocName ( count*sizeof(*out), loadname);
|
|
|
|
loadmodel->texinfo = out;
|
|
loadmodel->numtexinfo = count;
|
|
|
|
for ( i=0 ; i<count ; i++, in++, out++)
|
|
{
|
|
for (j=0 ; j<8 ; j++)
|
|
out->vecs[0][j] = LittleFloat (in->vecs[0][j]);
|
|
len1 = VectorLength (out->vecs[0]);
|
|
len2 = VectorLength (out->vecs[1]);
|
|
len1 = (len1 + len2)/2;
|
|
if (len1 < 0.32)
|
|
out->mipadjust = 4;
|
|
else if (len1 < 0.49)
|
|
out->mipadjust = 3;
|
|
else if (len1 < 0.99)
|
|
out->mipadjust = 2;
|
|
else
|
|
out->mipadjust = 1;
|
|
#if 0
|
|
if (len1 + len2 < 0.001)
|
|
out->mipadjust = 1; // don't crash
|
|
else
|
|
out->mipadjust = 1 / floor( (len1+len2)/2 + 0.1 );
|
|
#endif
|
|
|
|
miptex = LittleLong (in->miptex);
|
|
out->flags = LittleLong (in->flags);
|
|
|
|
//johnfitz -- rewrote this section
|
|
if (miptex >= loadmodel->numtextures-1 || !loadmodel->textures[miptex])
|
|
{
|
|
if (out->flags & TEX_SPECIAL)
|
|
out->texture = loadmodel->textures[loadmodel->numtextures-1];
|
|
else
|
|
out->texture = loadmodel->textures[loadmodel->numtextures-2];
|
|
out->flags |= TEX_MISSING;
|
|
missing++;
|
|
}
|
|
else
|
|
{
|
|
out->texture = loadmodel->textures[miptex];
|
|
}
|
|
//johnfitz
|
|
}
|
|
|
|
//johnfitz: report missing textures
|
|
if (missing && loadmodel->numtextures > 1)
|
|
Con_Printf ("Mod_LoadTexinfo: %d texture(s) missing from BSP file\n", missing);
|
|
//johnfitz
|
|
}
|
|
|
|
/*
|
|
================
|
|
CalcSurfaceExtents
|
|
|
|
Fills in s->texturemins[] and s->extents[]
|
|
================
|
|
*/
|
|
void CalcSurfaceExtents (msurface_t *s)
|
|
{
|
|
float mins[2], maxs[2], val;
|
|
int i,j, e;
|
|
mvertex_t *v;
|
|
mtexinfo_t *tex;
|
|
int bmins[2], bmaxs[2];
|
|
|
|
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++)
|
|
{
|
|
val = v->position[0] * tex->vecs[j][0] +
|
|
v->position[1] * tex->vecs[j][1] +
|
|
v->position[2] * tex->vecs[j][2] +
|
|
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 glquake, 256 in winquake
|
|
Sys_Error ("Bad surface extents");
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
Mod_PolyForUnlitSurface -- johnfitz -- creates polys for unlightmapped surfaces (sky and water)
|
|
|
|
TODO: merge this into BuildSurfaceDisplayList?
|
|
================
|
|
*/
|
|
void Mod_PolyForUnlitSurface (msurface_t *fa)
|
|
{
|
|
vec3_t verts[64];
|
|
int numverts, i, lindex;
|
|
float *vec;
|
|
glpoly_t *poly;
|
|
float texscale;
|
|
|
|
if (fa->flags & (SURF_DRAWTURB | SURF_DRAWSKY))
|
|
texscale = (1.0/128.0); //warp animation repeats every 128
|
|
else
|
|
texscale = (1.0/32.0); //to match r_notexture_mip
|
|
|
|
// convert edges back to a normal polygon
|
|
numverts = 0;
|
|
for (i=0 ; i<fa->numedges ; i++)
|
|
{
|
|
lindex = loadmodel->surfedges[fa->firstedge + i];
|
|
|
|
if (lindex > 0)
|
|
vec = loadmodel->vertexes[loadmodel->edges[lindex].v[0]].position;
|
|
else
|
|
vec = loadmodel->vertexes[loadmodel->edges[-lindex].v[1]].position;
|
|
VectorCopy (vec, verts[numverts]);
|
|
numverts++;
|
|
}
|
|
|
|
//create the poly
|
|
poly = (glpoly_t *) Hunk_Alloc (sizeof(glpoly_t) + (numverts-4) * VERTEXSIZE*sizeof(float));
|
|
poly->next = NULL;
|
|
fa->polys = poly;
|
|
poly->numverts = numverts;
|
|
for (i=0, vec=(float *)verts; i<numverts; i++, vec+= 3)
|
|
{
|
|
VectorCopy (vec, poly->verts[i]);
|
|
poly->verts[i][3] = DotProduct(vec, fa->texinfo->vecs[0]) * texscale;
|
|
poly->verts[i][4] = DotProduct(vec, fa->texinfo->vecs[1]) * texscale;
|
|
}
|
|
}
|
|
|
|
/*
|
|
=================
|
|
Mod_CalcSurfaceBounds -- johnfitz -- calculate bounding box for per-surface frustum culling
|
|
=================
|
|
*/
|
|
void Mod_CalcSurfaceBounds (msurface_t *s)
|
|
{
|
|
int i, e;
|
|
mvertex_t *v;
|
|
|
|
s->mins[0] = s->mins[1] = s->mins[2] = 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)
|
|
{
|
|
dface_t *in;
|
|
msurface_t *out;
|
|
int i, count, surfnum;
|
|
int planenum, side;
|
|
|
|
in = (dface_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 = (msurface_t *) Hunk_AllocName ( count*sizeof(*out), loadname);
|
|
|
|
//johnfitz -- warn mappers about exceeding old limits
|
|
if (count > 32767)
|
|
Con_Warning ("%i faces exceeds standard limit of 32767.\n", count);
|
|
//johnfitz
|
|
|
|
loadmodel->surfaces = out;
|
|
loadmodel->numsurfaces = count;
|
|
|
|
for ( surfnum=0 ; surfnum<count ; surfnum++, in++, out++)
|
|
{
|
|
out->firstedge = LittleLong(in->firstedge);
|
|
out->numedges = LittleShort(in->numedges);
|
|
out->flags = 0;
|
|
|
|
planenum = LittleShort(in->planenum);
|
|
side = LittleShort(in->side);
|
|
if (side)
|
|
out->flags |= SURF_PLANEBACK;
|
|
|
|
out->plane = loadmodel->planes + planenum;
|
|
|
|
out->texinfo = loadmodel->texinfo + LittleShort (in->texinfo);
|
|
|
|
CalcSurfaceExtents (out);
|
|
|
|
Mod_CalcSurfaceBounds (out); //johnfitz -- for per-surface frustum culling
|
|
|
|
// lighting info
|
|
for (i=0 ; i<MAXLIGHTMAPS ; i++)
|
|
out->styles[i] = in->styles[i];
|
|
i = LittleLong(in->lightofs);
|
|
if (i == -1)
|
|
out->samples = NULL;
|
|
else
|
|
out->samples = loadmodel->lightdata + (i * 3); //johnfitz -- lit support via lordhavoc (was "+ i")
|
|
|
|
//johnfitz -- this section rewritten
|
|
if (!Q_strncasecmp(out->texinfo->texture->name,"sky",3)) // sky surface //also note -- was Q_strncmp, changed to match qbsp
|
|
{
|
|
out->flags |= (SURF_DRAWSKY | SURF_DRAWTILED);
|
|
Mod_PolyForUnlitSurface (out); //no more subdivision
|
|
}
|
|
else if (out->texinfo->texture->name[0] == '*') // warp surface
|
|
{
|
|
out->flags |= (SURF_DRAWTURB | SURF_DRAWTILED);
|
|
Mod_PolyForUnlitSurface (out);
|
|
GL_SubdivideSurface (out);
|
|
}
|
|
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 (lump_t *l)
|
|
{
|
|
int i, j, count, p;
|
|
dnode_t *in;
|
|
mnode_t *out;
|
|
|
|
in = (dnode_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_Warning ("%i nodes exceeds standard limit of 32767.\n", count);
|
|
//johnfitz
|
|
|
|
loadmodel->nodes = out;
|
|
loadmodel->numnodes = count;
|
|
|
|
for ( i=0 ; i<count ; i++, in++, out++)
|
|
{
|
|
for (j=0 ; j<3 ; j++)
|
|
{
|
|
out->minmaxs[j] = LittleShort (in->mins[j]);
|
|
out->minmaxs[3+j] = LittleShort (in->maxs[j]);
|
|
}
|
|
|
|
p = LittleLong(in->planenum);
|
|
out->plane = loadmodel->planes + p;
|
|
|
|
out->firstsurface = (unsigned short)LittleShort (in->firstface); //johnfitz -- explicit cast as unsigned short
|
|
out->numsurfaces = (unsigned short)LittleShort (in->numfaces); //johnfitz -- explicit cast as unsigned short
|
|
|
|
for (j=0 ; j<2 ; j++)
|
|
{
|
|
//johnfitz -- hack to handle nodes > 32k, adapted from darkplaces
|
|
p = (unsigned short)LittleShort(in->children[j]);
|
|
if (p < count)
|
|
out->children[j] = loadmodel->nodes + p;
|
|
else
|
|
{
|
|
p = 65535 - p; //note this uses 65535 intentionally, -1 is leaf 0
|
|
if (p < loadmodel->numleafs)
|
|
out->children[j] = (mnode_t *)(loadmodel->leafs + p);
|
|
else
|
|
{
|
|
Con_Printf("Mod_LoadNodes: invalid leaf index %i (file has only %i leafs)\n", p, loadmodel->numleafs);
|
|
out->children[j] = (mnode_t *)(loadmodel->leafs); //map it to the solid leaf
|
|
}
|
|
}
|
|
//johnfitz
|
|
}
|
|
}
|
|
|
|
Mod_SetParent (loadmodel->nodes, NULL); // sets nodes and leafs
|
|
}
|
|
|
|
/*
|
|
=================
|
|
Mod_LoadLeafs
|
|
=================
|
|
*/
|
|
void Mod_LoadLeafs (lump_t *l)
|
|
{
|
|
dleaf_t *in;
|
|
mleaf_t *out;
|
|
int i, j, count, p;
|
|
|
|
in = (dleaf_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 = (mleaf_t *) Hunk_AllocName ( count*sizeof(*out), loadname);
|
|
|
|
//johnfitz
|
|
if (count > 32767)
|
|
Host_Error ("Mod_LoadLeafs: %i leafs exceeds limit of 32767.\n", count);
|
|
//johnfitz
|
|
|
|
loadmodel->leafs = out;
|
|
loadmodel->numleafs = count;
|
|
|
|
for ( i=0 ; i<count ; i++, in++, out++)
|
|
{
|
|
for (j=0 ; j<3 ; j++)
|
|
{
|
|
out->minmaxs[j] = LittleShort (in->mins[j]);
|
|
out->minmaxs[3+j] = LittleShort (in->maxs[j]);
|
|
}
|
|
|
|
p = LittleLong(in->contents);
|
|
out->contents = p;
|
|
|
|
out->firstmarksurface = loadmodel->marksurfaces + (unsigned short)LittleShort(in->firstmarksurface); //johnfitz -- unsigned short
|
|
out->nummarksurfaces = (unsigned short)LittleShort(in->nummarksurfaces); //johnfitz -- unsigned short
|
|
|
|
p = LittleLong(in->visofs);
|
|
if (p == -1)
|
|
out->compressed_vis = NULL;
|
|
else
|
|
out->compressed_vis = loadmodel->visdata + p;
|
|
out->efrags = NULL;
|
|
|
|
for (j=0 ; j<4 ; j++)
|
|
out->ambient_sound_level[j] = in->ambient_level[j];
|
|
|
|
//johnfitz -- removed code to mark surfaces as SURF_UNDERWATER
|
|
}
|
|
}
|
|
|
|
/*
|
|
=================
|
|
Mod_LoadClipnodes
|
|
=================
|
|
*/
|
|
void Mod_LoadClipnodes (lump_t *l)
|
|
{
|
|
dclipnode_t *in;
|
|
mclipnode_t *out; //johnfitz -- was dclipnode_t
|
|
int i, count;
|
|
hull_t *hull;
|
|
|
|
in = (dclipnode_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 = (mclipnode_t *) Hunk_AllocName ( count*sizeof(*out), loadname);
|
|
|
|
//johnfitz -- warn about exceeding old limits
|
|
if (count > 32767)
|
|
Con_Warning ("%i clipnodes exceeds standard limit of 32767.\n", count);
|
|
//johnfitz
|
|
|
|
loadmodel->clipnodes = out;
|
|
loadmodel->numclipnodes = count;
|
|
|
|
hull = &loadmodel->hulls[1];
|
|
hull->clipnodes = out;
|
|
hull->firstclipnode = 0;
|
|
hull->lastclipnode = count-1;
|
|
hull->planes = loadmodel->planes;
|
|
hull->clip_mins[0] = -16;
|
|
hull->clip_mins[1] = -16;
|
|
hull->clip_mins[2] = -24;
|
|
hull->clip_maxs[0] = 16;
|
|
hull->clip_maxs[1] = 16;
|
|
hull->clip_maxs[2] = 32;
|
|
|
|
hull = &loadmodel->hulls[2];
|
|
hull->clipnodes = out;
|
|
hull->firstclipnode = 0;
|
|
hull->lastclipnode = count-1;
|
|
hull->planes = loadmodel->planes;
|
|
hull->clip_mins[0] = -32;
|
|
hull->clip_mins[1] = -32;
|
|
hull->clip_mins[2] = -24;
|
|
hull->clip_maxs[0] = 32;
|
|
hull->clip_maxs[1] = 32;
|
|
hull->clip_maxs[2] = 64;
|
|
|
|
for (i=0 ; i<count ; i++, out++, in++)
|
|
{
|
|
out->planenum = LittleLong(in->planenum);
|
|
|
|
//johnfitz -- bounds check
|
|
if (out->planenum < 0 || out->planenum >= loadmodel->numplanes)
|
|
Host_Error ("Mod_LoadClipnodes: planenum out of bounds");
|
|
//johnfitz
|
|
|
|
//johnfitz -- support clipnodes > 32k
|
|
out->children[0] = (unsigned short)LittleShort(in->children[0]);
|
|
out->children[1] = (unsigned short)LittleShort(in->children[1]);
|
|
if (out->children[0] >= count)
|
|
out->children[0] -= 65536;
|
|
if (out->children[1] >= count)
|
|
out->children[1] -= 65536;
|
|
//johnfitz
|
|
}
|
|
}
|
|
|
|
/*
|
|
=================
|
|
Mod_MakeHull0
|
|
|
|
Duplicate the drawing hull structure as a clipping hull
|
|
=================
|
|
*/
|
|
void Mod_MakeHull0 (void)
|
|
{
|
|
mnode_t *in, *child;
|
|
mclipnode_t *out; //johnfitz -- was dclipnode_t
|
|
int i, j, count;
|
|
hull_t *hull;
|
|
|
|
hull = &loadmodel->hulls[0];
|
|
|
|
in = loadmodel->nodes;
|
|
count = loadmodel->numnodes;
|
|
out = (mclipnode_t *) Hunk_AllocName ( count*sizeof(*out), loadname);
|
|
|
|
hull->clipnodes = out;
|
|
hull->firstclipnode = 0;
|
|
hull->lastclipnode = count-1;
|
|
hull->planes = loadmodel->planes;
|
|
|
|
for (i=0 ; i<count ; i++, out++, in++)
|
|
{
|
|
out->planenum = in->plane - loadmodel->planes;
|
|
for (j=0 ; j<2 ; j++)
|
|
{
|
|
child = in->children[j];
|
|
if (child->contents < 0)
|
|
out->children[j] = child->contents;
|
|
else
|
|
out->children[j] = child - loadmodel->nodes;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
=================
|
|
Mod_LoadMarksurfaces
|
|
=================
|
|
*/
|
|
void Mod_LoadMarksurfaces (lump_t *l)
|
|
{
|
|
int i, j, count;
|
|
short *in;
|
|
msurface_t **out;
|
|
|
|
in = (short *)(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 = (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_Warning ("%i marksurfaces exceeds standard limit of 32767.\n", count);
|
|
//johnfitz
|
|
|
|
for ( i=0 ; i<count ; i++)
|
|
{
|
|
j = (unsigned short)LittleShort(in[i]); //johnfitz -- explicit cast as unsigned short
|
|
if (j >= loadmodel->numsurfaces)
|
|
Sys_Error ("Mod_ParseMarksurfaces: bad surface number");
|
|
out[i] = loadmodel->surfaces + j;
|
|
}
|
|
}
|
|
|
|
/*
|
|
=================
|
|
Mod_LoadSurfedges
|
|
=================
|
|
*/
|
|
void Mod_LoadSurfedges (lump_t *l)
|
|
{
|
|
int i, count;
|
|
int *in, *out;
|
|
|
|
in = (int *)(mod_base + l->fileofs);
|
|
if (l->filelen % sizeof(*in))
|
|
Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
|
|
count = l->filelen / sizeof(*in);
|
|
out = (int *) Hunk_AllocName ( count*sizeof(*out), loadname);
|
|
|
|
loadmodel->surfedges = out;
|
|
loadmodel->numsurfedges = count;
|
|
|
|
for ( i=0 ; i<count ; i++)
|
|
out[i] = LittleLong (in[i]);
|
|
}
|
|
|
|
|
|
/*
|
|
=================
|
|
Mod_LoadPlanes
|
|
=================
|
|
*/
|
|
void Mod_LoadPlanes (lump_t *l)
|
|
{
|
|
int i, j;
|
|
mplane_t *out;
|
|
dplane_t *in;
|
|
int count;
|
|
int bits;
|
|
|
|
in = (dplane_t *)(mod_base + l->fileofs);
|
|
if (l->filelen % sizeof(*in))
|
|
Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
|
|
count = l->filelen / sizeof(*in);
|
|
out = (mplane_t *) Hunk_AllocName ( count*2*sizeof(*out), loadname);
|
|
|
|
loadmodel->planes = out;
|
|
loadmodel->numplanes = count;
|
|
|
|
for ( i=0 ; i<count ; i++, in++, out++)
|
|
{
|
|
bits = 0;
|
|
for (j=0 ; j<3 ; j++)
|
|
{
|
|
out->normal[j] = LittleFloat (in->normal[j]);
|
|
if (out->normal[j] < 0)
|
|
bits |= 1<<j;
|
|
}
|
|
|
|
out->dist = LittleFloat (in->dist);
|
|
out->type = LittleLong (in->type);
|
|
out->signbits = bits;
|
|
}
|
|
}
|
|
|
|
/*
|
|
=================
|
|
RadiusFromBounds
|
|
=================
|
|
*/
|
|
float RadiusFromBounds (vec3_t mins, vec3_t maxs)
|
|
{
|
|
int i;
|
|
vec3_t corner;
|
|
|
|
for (i=0 ; i<3 ; i++)
|
|
{
|
|
corner[i] = fabs(mins[i]) > fabs(maxs[i]) ? fabs(mins[i]) : fabs(maxs[i]);
|
|
}
|
|
|
|
return VectorLength (corner);
|
|
}
|
|
|
|
/*
|
|
=================
|
|
Mod_LoadSubmodels
|
|
=================
|
|
*/
|
|
void Mod_LoadSubmodels (lump_t *l)
|
|
{
|
|
dmodel_t *in;
|
|
dmodel_t *out;
|
|
int 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 > MAX_MAP_LEAFS)
|
|
Sys_Error ("Mod_LoadSubmodels: too many visleafs (%d, max = %d) in %s", out->visleafs, MAX_MAP_LEAFS, loadmodel->name);
|
|
|
|
if (out->visleafs > 8192)
|
|
Con_Warning ("%i visleafs exceeds standard limit of 8192.\n", out->visleafs);
|
|
//johnfitz
|
|
}
|
|
|
|
/*
|
|
=================
|
|
Mod_BoundsFromClipNode -- johnfitz
|
|
|
|
update the model's clipmins and clipmaxs based on each node's plane.
|
|
|
|
This works because of the way brushes are expanded in hull generation.
|
|
Each brush will include all six axial planes, which bound that brush.
|
|
Therefore, the bounding box of the hull can be constructed entirely
|
|
from axial planes found in the clipnodes for that hull.
|
|
=================
|
|
*/
|
|
void Mod_BoundsFromClipNode (qmodel_t *mod, int hull, int nodenum)
|
|
{
|
|
mplane_t *plane;
|
|
mclipnode_t *node;
|
|
|
|
if (nodenum < 0)
|
|
return; //hit a leafnode
|
|
|
|
node = &mod->clipnodes[nodenum];
|
|
plane = mod->hulls[hull].planes + node->planenum;
|
|
switch (plane->type)
|
|
{
|
|
|
|
case PLANE_X:
|
|
if (plane->signbits == 1)
|
|
mod->clipmins[0] = q_min(mod->clipmins[0], -plane->dist - mod->hulls[hull].clip_mins[0]);
|
|
else
|
|
mod->clipmaxs[0] = q_max(mod->clipmaxs[0], plane->dist - mod->hulls[hull].clip_maxs[0]);
|
|
break;
|
|
case PLANE_Y:
|
|
if (plane->signbits == 2)
|
|
mod->clipmins[1] = q_min(mod->clipmins[1], -plane->dist - mod->hulls[hull].clip_mins[1]);
|
|
else
|
|
mod->clipmaxs[1] = q_max(mod->clipmaxs[1], plane->dist - mod->hulls[hull].clip_maxs[1]);
|
|
break;
|
|
case PLANE_Z:
|
|
if (plane->signbits == 4)
|
|
mod->clipmins[2] = q_min(mod->clipmins[2], -plane->dist - mod->hulls[hull].clip_mins[2]);
|
|
else
|
|
mod->clipmaxs[2] = q_max(mod->clipmaxs[2], plane->dist - mod->hulls[hull].clip_maxs[2]);
|
|
break;
|
|
default:
|
|
//skip nonaxial planes; don't need them
|
|
break;
|
|
}
|
|
|
|
Mod_BoundsFromClipNode (mod, hull, node->children[0]);
|
|
Mod_BoundsFromClipNode (mod, hull, node->children[1]);
|
|
}
|
|
|
|
/*
|
|
=================
|
|
Mod_LoadBrushModel
|
|
=================
|
|
*/
|
|
void Mod_LoadBrushModel (qmodel_t *mod, void *buffer)
|
|
{
|
|
int i, j;
|
|
dheader_t *header;
|
|
dmodel_t *bm;
|
|
float radius; //johnfitz
|
|
|
|
loadmodel->type = mod_brush;
|
|
|
|
header = (dheader_t *)buffer;
|
|
|
|
i = LittleLong (header->version);
|
|
if (i != BSPVERSION)
|
|
Sys_Error ("Mod_LoadBrushModel: %s has wrong version number (%i should be %i)", mod->name, i, BSPVERSION);
|
|
|
|
// swap all the lumps
|
|
mod_base = (byte *)header;
|
|
|
|
for (i = 0; i < (int) sizeof(dheader_t) / 4; i++)
|
|
((int *)header)[i] = LittleLong ( ((int *)header)[i]);
|
|
|
|
// load into heap
|
|
|
|
Mod_LoadVertexes (&header->lumps[LUMP_VERTEXES]);
|
|
Mod_LoadEdges (&header->lumps[LUMP_EDGES]);
|
|
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]);
|
|
Mod_LoadMarksurfaces (&header->lumps[LUMP_MARKSURFACES]);
|
|
Mod_LoadVisibility (&header->lumps[LUMP_VISIBILITY]);
|
|
Mod_LoadLeafs (&header->lumps[LUMP_LEAFS]);
|
|
Mod_LoadNodes (&header->lumps[LUMP_NODES]);
|
|
Mod_LoadClipnodes (&header->lumps[LUMP_CLIPNODES]);
|
|
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, "*%i", 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];
|
|
int posenum;
|
|
|
|
byte **player_8bit_texels_tbl;
|
|
byte *player_8bit_texels;
|
|
|
|
/*
|
|
=================
|
|
Mod_LoadAliasFrame
|
|
=================
|
|
*/
|
|
void * Mod_LoadAliasFrame (void * pin, maliasframedesc_t *frame)
|
|
{
|
|
trivertx_t *pinframe;
|
|
int i;
|
|
daliasframe_t *pdaliasframe;
|
|
|
|
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 (void *)pinframe;
|
|
}
|
|
|
|
|
|
/*
|
|
=================
|
|
Mod_LoadAliasGroup
|
|
=================
|
|
*/
|
|
void *Mod_LoadAliasGroup (void * pin, maliasframedesc_t *frame)
|
|
{
|
|
daliasgroup_t *pingroup;
|
|
int i, numframes;
|
|
daliasinterval_t *pin_intervals;
|
|
void *ptemp;
|
|
|
|
pingroup = (daliasgroup_t *)pin;
|
|
|
|
numframes = LittleLong (pingroup->numframes);
|
|
|
|
frame->firstpose = posenum;
|
|
frame->numposes = numframes;
|
|
|
|
for (i=0 ; i<3 ; i++)
|
|
{
|
|
// these are byte values, so we don't have to worry about endianness
|
|
frame->bboxmin.v[i] = pingroup->bboxmin.v[i];
|
|
frame->bboxmax.v[i] = pingroup->bboxmax.v[i];
|
|
}
|
|
|
|
|
|
pin_intervals = (daliasinterval_t *)(pingroup + 1);
|
|
|
|
frame->interval = LittleFloat (pin_intervals->interval);
|
|
|
|
pin_intervals += numframes;
|
|
|
|
ptemp = (void *)pin_intervals;
|
|
|
|
for (i=0 ; i<numframes ; i++)
|
|
{
|
|
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
|
|
{
|
|
short x, y;
|
|
} floodfill_t;
|
|
|
|
// must be a power of 2
|
|
#define FLOODFILL_FIFO_SIZE 0x1000
|
|
#define FLOODFILL_FIFO_MASK (FLOODFILL_FIFO_SIZE - 1)
|
|
|
|
#define FLOODFILL_STEP( off, dx, dy ) \
|
|
do { \
|
|
if (pos[off] == fillcolor) \
|
|
{ \
|
|
pos[off] = 255; \
|
|
fifo[inpt].x = x + (dx), fifo[inpt].y = y + (dy); \
|
|
inpt = (inpt + 1) & FLOODFILL_FIFO_MASK; \
|
|
} \
|
|
else if (pos[off] != 255) fdc = pos[off]; \
|
|
} while (0)
|
|
|
|
void Mod_FloodFillSkin( byte *skin, int skinwidth, int skinheight )
|
|
{
|
|
byte fillcolor = *skin; // assume this is the pixel to fill
|
|
floodfill_t fifo[FLOODFILL_FIFO_SIZE];
|
|
int inpt = 0, outpt = 0;
|
|
int filledcolor = -1;
|
|
int i;
|
|
|
|
if (filledcolor == -1)
|
|
{
|
|
filledcolor = 0;
|
|
// attempt to find opaque black
|
|
for (i = 0; i < 256; ++i)
|
|
if (d_8to24table[i] == (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 %d to %d\n", fillcolor, filledcolor );
|
|
return;
|
|
}
|
|
|
|
fifo[inpt].x = 0, fifo[inpt].y = 0;
|
|
inpt = (inpt + 1) & FLOODFILL_FIFO_MASK;
|
|
|
|
while (outpt != inpt)
|
|
{
|
|
int x = fifo[outpt].x, y = fifo[outpt].y;
|
|
int fdc = filledcolor;
|
|
byte *pos = &skin[x + skinwidth * y];
|
|
|
|
outpt = (outpt + 1) & FLOODFILL_FIFO_MASK;
|
|
|
|
if (x > 0) FLOODFILL_STEP( -1, -1, 0 );
|
|
if (x < skinwidth - 1) FLOODFILL_STEP( 1, 1, 0 );
|
|
if (y > 0) FLOODFILL_STEP( -skinwidth, 0, -1 );
|
|
if (y < skinheight - 1) FLOODFILL_STEP( skinwidth, 0, 1 );
|
|
skin[x + skinwidth * y] = fdc;
|
|
}
|
|
}
|
|
|
|
/*
|
|
===============
|
|
Mod_LoadAllSkins
|
|
===============
|
|
*/
|
|
void *Mod_LoadAllSkins (int numskins, daliasskintype_t *pskintype)
|
|
{
|
|
int i, j, k, size, groupskins;
|
|
char name[32];
|
|
byte *skin, *texels;
|
|
daliasskingroup_t *pinskingroup;
|
|
daliasskininterval_t *pinskinintervals;
|
|
char fbr_mask_name[64]; //johnfitz -- added for fullbright support
|
|
src_offset_t offset; //johnfitz
|
|
|
|
skin = (byte *)(pskintype + 1);
|
|
|
|
if (numskins < 1 || numskins > MAX_SKINS)
|
|
Sys_Error ("Mod_LoadAliasModel: Invalid # of skins: %d\n", numskins);
|
|
|
|
size = pheader->skinwidth * pheader->skinheight;
|
|
|
|
for (i=0 ; i<numskins ; i++)
|
|
{
|
|
if (pskintype->type == ALIAS_SKIN_SINGLE)
|
|
{
|
|
Mod_FloodFillSkin( skin, pheader->skinwidth, pheader->skinheight );
|
|
|
|
// save 8 bit texels for the player model to remap
|
|
texels = (byte *) Hunk_AllocName(size, loadname);
|
|
pheader->texels[i] = texels - (byte *)pheader;
|
|
memcpy (texels, (byte *)(pskintype + 1), size);
|
|
|
|
//johnfitz -- rewritten
|
|
q_snprintf (name, sizeof(name), "%s:frame%i", loadmodel->name, i);
|
|
offset = (src_offset_t)(pskintype+1) - (src_offset_t)mod_base;
|
|
if (Mod_CheckFullbrights ((byte *)(pskintype+1), size))
|
|
{
|
|
pheader->gltextures[i][0] = TexMgr_LoadImage (loadmodel, name, pheader->skinwidth, pheader->skinheight,
|
|
SRC_INDEXED, (byte *)(pskintype+1), loadmodel->name, offset, TEXPREF_PAD | TEXPREF_NOBRIGHT);
|
|
q_snprintf (fbr_mask_name, sizeof(fbr_mask_name), "%s:frame%i_glow", loadmodel->name, i);
|
|
pheader->fbtextures[i][0] = TexMgr_LoadImage (loadmodel, fbr_mask_name, pheader->skinwidth, pheader->skinheight,
|
|
SRC_INDEXED, (byte *)(pskintype+1), loadmodel->name, offset, TEXPREF_PAD | TEXPREF_FULLBRIGHT);
|
|
}
|
|
else
|
|
{
|
|
pheader->gltextures[i][0] = TexMgr_LoadImage (loadmodel, name, pheader->skinwidth, pheader->skinheight,
|
|
SRC_INDEXED, (byte *)(pskintype+1), loadmodel->name, offset, TEXPREF_PAD);
|
|
pheader->fbtextures[i][0] = NULL;
|
|
}
|
|
|
|
pheader->gltextures[i][3] = pheader->gltextures[i][2] = pheader->gltextures[i][1] = pheader->gltextures[i][0];
|
|
pheader->fbtextures[i][3] = pheader->fbtextures[i][2] = pheader->fbtextures[i][1] = pheader->fbtextures[i][0];
|
|
//johnfitz
|
|
|
|
pskintype = (daliasskintype_t *)((byte *)(pskintype+1) + size);
|
|
}
|
|
else
|
|
{
|
|
// animating skin group. yuck.
|
|
pskintype++;
|
|
pinskingroup = (daliasskingroup_t *)pskintype;
|
|
groupskins = LittleLong (pinskingroup->numskins);
|
|
pinskinintervals = (daliasskininterval_t *)(pinskingroup + 1);
|
|
|
|
pskintype = (daliasskintype_t *)(pinskinintervals + groupskins);
|
|
|
|
for (j=0 ; j<groupskins ; j++)
|
|
{
|
|
Mod_FloodFillSkin( skin, pheader->skinwidth, pheader->skinheight );
|
|
if (j == 0) {
|
|
texels = (byte *) Hunk_AllocName(size, loadname);
|
|
pheader->texels[i] = texels - (byte *)pheader;
|
|
memcpy (texels, (byte *)(pskintype), size);
|
|
}
|
|
|
|
//johnfitz -- rewritten
|
|
q_snprintf (name, sizeof(name), "%s:frame%i_%i", loadmodel->name, i,j);
|
|
offset = (src_offset_t)(pskintype) - (src_offset_t)mod_base; //johnfitz
|
|
if (Mod_CheckFullbrights ((byte *)(pskintype), size))
|
|
{
|
|
pheader->gltextures[i][j&3] = TexMgr_LoadImage (loadmodel, name, pheader->skinwidth, pheader->skinheight,
|
|
SRC_INDEXED, (byte *)(pskintype), loadmodel->name, offset, TEXPREF_PAD | TEXPREF_NOBRIGHT);
|
|
q_snprintf (fbr_mask_name, sizeof(fbr_mask_name), "%s:frame%i_%i_glow", loadmodel->name, i,j);
|
|
pheader->fbtextures[i][j&3] = TexMgr_LoadImage (loadmodel, fbr_mask_name, pheader->skinwidth, pheader->skinheight,
|
|
SRC_INDEXED, (byte *)(pskintype), loadmodel->name, offset, TEXPREF_PAD | TEXPREF_FULLBRIGHT);
|
|
}
|
|
else
|
|
{
|
|
pheader->gltextures[i][j&3] = TexMgr_LoadImage (loadmodel, name, pheader->skinwidth, pheader->skinheight,
|
|
SRC_INDEXED, (byte *)(pskintype), loadmodel->name, offset, TEXPREF_PAD);
|
|
pheader->fbtextures[i][j&3] = NULL;
|
|
}
|
|
//johnfitz
|
|
|
|
pskintype = (daliasskintype_t *)((byte *)(pskintype) + size);
|
|
}
|
|
k = j;
|
|
for (/* */; j < 4; j++)
|
|
pheader->gltextures[i][j&3] =
|
|
pheader->gltextures[i][j - k];
|
|
}
|
|
}
|
|
|
|
return (void *)pskintype;
|
|
}
|
|
|
|
//=========================================================================
|
|
|
|
/*
|
|
=================
|
|
Mod_CalcAliasBounds -- johnfitz -- calculate bounds of alias model for nonrotated, yawrotated, and fullrotated cases
|
|
=================
|
|
*/
|
|
void Mod_CalcAliasBounds (aliashdr_t *a)
|
|
{
|
|
int i,j,k;
|
|
float dist, yawradius, radius;
|
|
vec3_t v;
|
|
|
|
//clear out all data
|
|
for (i=0; i<3;i++)
|
|
{
|
|
loadmodel->mins[i] = loadmodel->ymins[i] = loadmodel->rmins[i] = 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];
|
|
}
|
|
|
|
/*
|
|
=================
|
|
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;
|
|
const char *s;
|
|
char tmp[MAX_QPATH];
|
|
int i;
|
|
|
|
if (!mod || !mod->name || mod->type != mod_alias)
|
|
return;
|
|
|
|
mod->flags &= 0xFF; //only preserve first byte
|
|
|
|
// nolerp flag
|
|
s = r_nolerp_list.string;
|
|
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(mod->name, tmp))
|
|
{
|
|
mod->flags |= MOD_NOLERP;
|
|
break;
|
|
}
|
|
//search forwards to the next comma or end of string
|
|
while (*s && *s == ',')
|
|
s++;
|
|
}
|
|
|
|
// noshadow flag (TODO: make this a cvar list)
|
|
if (!strcmp (mod->name, "progs/flame2.mdl") ||
|
|
!strcmp (mod->name, "progs/flame.mdl") ||
|
|
!strcmp (mod->name, "progs/bolt1.mdl") ||
|
|
!strcmp (mod->name, "progs/bolt2.mdl") ||
|
|
!strcmp (mod->name, "progs/bolt3.mdl") ||
|
|
!strcmp (mod->name, "progs/laser.mdl"))
|
|
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)
|
|
{
|
|
int i, j;
|
|
mdl_t *pinmodel;
|
|
stvert_t *pinstverts;
|
|
dtriangle_t *pintriangles;
|
|
int version, numframes;
|
|
int size;
|
|
daliasframetype_t *pframetype;
|
|
daliasskintype_t *pskintype;
|
|
int start, end, total;
|
|
|
|
start = Hunk_LowMark ();
|
|
|
|
pinmodel = (mdl_t *)buffer;
|
|
mod_base = (byte *)buffer; //johnfitz
|
|
|
|
version = LittleLong (pinmodel->version);
|
|
if (version != ALIAS_VERSION)
|
|
Sys_Error ("%s has wrong version number (%i should be %i)",
|
|
mod->name, version, ALIAS_VERSION);
|
|
|
|
//
|
|
// allocate space for a working header, plus all the data except the frames,
|
|
// skin and group info
|
|
//
|
|
size = sizeof(aliashdr_t) +
|
|
(LittleLong (pinmodel->numframes) - 1) * sizeof (pheader->frames[0]);
|
|
pheader = (aliashdr_t *) Hunk_AllocName (size, loadname);
|
|
|
|
mod->flags = LittleLong (pinmodel->flags);
|
|
|
|
//
|
|
// endian-adjust and copy the data, starting with the alias model header
|
|
//
|
|
pheader->boundingradius = LittleFloat (pinmodel->boundingradius);
|
|
pheader->numskins = LittleLong (pinmodel->numskins);
|
|
pheader->skinwidth = LittleLong (pinmodel->skinwidth);
|
|
pheader->skinheight = LittleLong (pinmodel->skinheight);
|
|
|
|
if (pheader->skinheight > MAX_LBM_HEIGHT)
|
|
Sys_Error ("model %s has a skin taller than %d", 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: %d\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, int framenum)
|
|
{
|
|
dspriteframe_t *pinframe;
|
|
mspriteframe_t *pspriteframe;
|
|
int width, height, size, origin[2];
|
|
char name[64];
|
|
src_offset_t offset; //johnfitz
|
|
|
|
pinframe = (dspriteframe_t *)pin;
|
|
|
|
width = LittleLong (pinframe->width);
|
|
height = LittleLong (pinframe->height);
|
|
size = width * height;
|
|
|
|
pspriteframe = (mspriteframe_t *) Hunk_AllocName (sizeof (mspriteframe_t),loadname);
|
|
*ppframe = pspriteframe;
|
|
|
|
pspriteframe->width = width;
|
|
pspriteframe->height = height;
|
|
origin[0] = LittleLong (pinframe->origin[0]);
|
|
origin[1] = LittleLong (pinframe->origin[1]);
|
|
|
|
pspriteframe->up = origin[1];
|
|
pspriteframe->down = origin[1] - height;
|
|
pspriteframe->left = origin[0];
|
|
pspriteframe->right = width + origin[0];
|
|
|
|
//johnfitz -- image might be padded
|
|
pspriteframe->smax = (float)width/(float)TexMgr_PadConditional(width);
|
|
pspriteframe->tmax = (float)height/(float)TexMgr_PadConditional(height);
|
|
//johnfitz
|
|
|
|
q_snprintf (name, sizeof(name), "%s:frame%i", loadmodel->name, framenum);
|
|
offset = (src_offset_t)(pinframe+1) - (src_offset_t)mod_base; //johnfitz
|
|
pspriteframe->gltexture =
|
|
TexMgr_LoadImage (loadmodel, name, width, height, SRC_INDEXED,
|
|
(byte *)(pinframe + 1), loadmodel->name, offset,
|
|
TEXPREF_PAD | TEXPREF_ALPHA | TEXPREF_NOPICMIP); //johnfitz -- TexMgr
|
|
|
|
return (void *)((byte *)pinframe + sizeof (dspriteframe_t) + size);
|
|
}
|
|
|
|
|
|
/*
|
|
=================
|
|
Mod_LoadSpriteGroup
|
|
=================
|
|
*/
|
|
void * Mod_LoadSpriteGroup (void * pin, mspriteframe_t **ppframe, int framenum)
|
|
{
|
|
dspritegroup_t *pingroup;
|
|
mspritegroup_t *pspritegroup;
|
|
int 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 = (void *)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)
|
|
{
|
|
int i;
|
|
int version;
|
|
dsprite_t *pin;
|
|
msprite_t *psprite;
|
|
int numframes;
|
|
int 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 "
|
|
"(%i should be %i)", 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: %d\n", numframes);
|
|
|
|
mod->numframes = numframes;
|
|
|
|
pframetype = (dspriteframetype_t *)(pin + 1);
|
|
|
|
for (i=0 ; i<numframes ; i++)
|
|
{
|
|
spriteframetype_t frametype;
|
|
|
|
frametype = (spriteframetype_t) LittleLong (pframetype->type);
|
|
psprite->frames[i].type = frametype;
|
|
|
|
if (frametype == SPR_SINGLE)
|
|
{
|
|
pframetype = (dspriteframetype_t *)
|
|
Mod_LoadSpriteFrame (pframetype + 1, &psprite->frames[i].frameptr, i);
|
|
}
|
|
else
|
|
{
|
|
pframetype = (dspriteframetype_t *)
|
|
Mod_LoadSpriteGroup (pframetype + 1, &psprite->frames[i].frameptr, i);
|
|
}
|
|
}
|
|
|
|
mod->type = mod_sprite;
|
|
}
|
|
|
|
//=============================================================================
|
|
|
|
/*
|
|
================
|
|
Mod_Print
|
|
================
|
|
*/
|
|
void Mod_Print (void)
|
|
{
|
|
int i;
|
|
qmodel_t *mod;
|
|
|
|
Con_SafePrintf ("Cached models:\n"); //johnfitz -- safeprint instead of print
|
|
for (i=0, mod=mod_known ; i < mod_numknown ; i++, mod++)
|
|
{
|
|
Con_SafePrintf ("%8p : %s\n", mod->cache.data, mod->name); //johnfitz -- safeprint instead of print
|
|
}
|
|
Con_Printf ("%i models\n",mod_numknown); //johnfitz -- print the total too
|
|
}
|
|
|