6e3f69f504
fte particle scripts are disabled (classic works). I'll fix these in the new year. Redid framestate stuff again. Slightly better now, but this is the bulk of the changes here. Reworked the renderqueue to provide batches of items instead of individual items. This cleans up the particle rendering code significantly, and is a step towards multiple concurrent particle systems. fte's scripted particles are broken as I'm trying to find a way to rework them to batch types together, rather than having to restart each batch after each particle when you have two particles in a trail. I'll fix it some time. Reworked some alias model code regarding skeletal models. Added some conceptual skeletal bone control builtins available to csqc. Currently it can query the bone names and save off animation states, but can't animate - its just not complete. Added more info to glsl custom shaders. Updated surface sorting on halflife maps to properly cope with alphaed entities, rather than just texture-based blends (q2-style). git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@3095 fc73d0e0-1445-4013-8a0c-d673dee63da5
3922 lines
92 KiB
C
3922 lines
92 KiB
C
/*
|
|
Copyright (C) 1996-1997 Id Software, Inc.
|
|
|
|
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"
|
|
#include "r_local.h"
|
|
|
|
#include "com_mesh.h"
|
|
|
|
model_t *loadmodel;
|
|
char loadname[32]; // for hunk tags
|
|
|
|
qboolean SWMod_LoadSpriteModel (model_t *mod, void *buffer);
|
|
qboolean SWMod_LoadSprite2Model (model_t *mod, void *buffer);
|
|
qboolean SWMod_LoadBrushModel (model_t *mod, void *buffer);
|
|
qboolean Mod_LoadQ2BrushModel (model_t *mod, void *buffer);
|
|
qboolean SWMod_LoadAliasModel (model_t *mod, void *buffer);
|
|
qboolean SWMod_LoadAlias2Model (model_t *mod, void *buffer);
|
|
qboolean SWMod_LoadAlias3Model (model_t *mod, void *buffer);
|
|
model_t *SWMod_LoadModel (model_t *mod, qboolean crash);
|
|
|
|
int Mod_ReadFlagsFromMD1(char *name, int md3version);
|
|
|
|
qbyte mod_novis[MAX_MAP_LEAFS/8];
|
|
|
|
#define MAX_MOD_KNOWN 1024
|
|
model_t mod_known[MAX_MOD_KNOWN];
|
|
int mod_numknown;
|
|
|
|
#ifdef SERVERONLY
|
|
#define SWMod_FindName Mod_FindName
|
|
#define SWMod_ForName Mod_ForName
|
|
#define SWMod_LeafPVS Mod_LeafPVS
|
|
#define SWMod_PointInLeaf Mod_PointInLeaf
|
|
#define SWMod_ClearAll Mod_ClearAll
|
|
#define SWMod_Init Mod_Init
|
|
#endif
|
|
|
|
/*
|
|
===============
|
|
Mod_Init
|
|
===============
|
|
*/
|
|
void SWMod_Init (void)
|
|
{
|
|
mod_numknown = 0;
|
|
memset (mod_novis, 0xff, sizeof(mod_novis));
|
|
|
|
Cmd_RemoveCommand("mod_texturelist");
|
|
}
|
|
|
|
void SWMod_Think(void)
|
|
{
|
|
}
|
|
|
|
/*
|
|
===============
|
|
Mod_Init
|
|
|
|
Caches the data if needed
|
|
===============
|
|
*/
|
|
void *SWMod_Extradata (model_t *mod)
|
|
{
|
|
void *r;
|
|
|
|
r = Cache_Check (&mod->cache);
|
|
if (r)
|
|
return r;
|
|
|
|
SWMod_LoadModel (mod, true);
|
|
|
|
if (!mod->cache.data)
|
|
Sys_Error ("Mod_Extradata: caching failed");
|
|
return mod->cache.data;
|
|
}
|
|
|
|
/*
|
|
===============
|
|
Mod_PointInLeaf
|
|
===============
|
|
*/
|
|
static int SWMod_LeafForPoint (model_t *model, vec3_t p)
|
|
{
|
|
mnode_t *node;
|
|
float d;
|
|
mplane_t *plane;
|
|
|
|
#ifdef Q2BSPS
|
|
if (model->fromgame == fg_quake2 || model->fromgame == fg_quake3)
|
|
{
|
|
return CM_PointLeafnum(cl.worldmodel, p);
|
|
}
|
|
#endif
|
|
|
|
if (!model || !model->nodes)
|
|
Sys_Error ("Mod_PointInLeaf: bad model");
|
|
|
|
node = model->nodes;
|
|
while (1)
|
|
{
|
|
if (node->contents < 0)
|
|
return (mleaf_t *)node - model->leafs;
|
|
plane = node->plane;
|
|
d = DotProduct (p,plane->normal) - plane->dist;
|
|
if (d > 0)
|
|
node = node->children[0];
|
|
else
|
|
node = node->children[1];
|
|
}
|
|
|
|
return 0; // never reached
|
|
}
|
|
|
|
mleaf_t *SWMod_PointInLeaf (model_t *model, vec3_t p)
|
|
{
|
|
return model->leafs + SWMod_LeafForPoint(model, p);
|
|
}
|
|
|
|
|
|
/*
|
|
===================
|
|
Mod_DecompressVis
|
|
===================
|
|
*/
|
|
static qbyte *SWMod_DecompressVis (model_t *model, qbyte *in, qbyte *decompressed)
|
|
{
|
|
int c;
|
|
qbyte *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;
|
|
}
|
|
|
|
qbyte *SWMod_LeafPVS (model_t *model, mleaf_t *leaf, qbyte *buffer)
|
|
{
|
|
static qbyte decompressed[MAX_MAP_LEAFS/8];
|
|
|
|
if (leaf == model->leafs)
|
|
return mod_novis;
|
|
|
|
if (!buffer)
|
|
buffer = decompressed;
|
|
return SWMod_DecompressVis (model, leaf->compressed_vis, buffer);
|
|
}
|
|
|
|
/*
|
|
===================
|
|
Mod_ClearAll
|
|
===================
|
|
*/
|
|
void SWMod_ClearAll (void)
|
|
{
|
|
int i;
|
|
model_t *mod;
|
|
|
|
for (i=0 , mod=mod_known ; i<mod_numknown ; i++, mod++)
|
|
if (mod->type != mod_alias)
|
|
mod->needload = true;
|
|
}
|
|
|
|
/*
|
|
==================
|
|
Mod_FindName
|
|
|
|
==================
|
|
*/
|
|
model_t *SWMod_FindName (char *name)
|
|
{
|
|
int i;
|
|
model_t *mod;
|
|
|
|
// if (!name[0]) //this is allowed to happen for q2 cinematics. :(
|
|
// Sys_Error ("Mod_ForName: NULL name");
|
|
|
|
//
|
|
// 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");
|
|
memset(mod, 0, sizeof(model_t)); //clear the old model as the renderers use the same globals
|
|
strcpy (mod->name, name);
|
|
mod->needload = true;
|
|
mod_numknown++;
|
|
mod->particleeffect = -1;
|
|
}
|
|
|
|
return mod;
|
|
}
|
|
|
|
/*
|
|
==================
|
|
Mod_TouchModel
|
|
|
|
==================
|
|
*/
|
|
void SWMod_TouchModel (char *name)
|
|
{
|
|
model_t *mod;
|
|
|
|
mod = SWMod_FindName (name);
|
|
|
|
if (!mod->needload)
|
|
{
|
|
if (mod->type == mod_alias)
|
|
Cache_Check (&mod->cache);
|
|
}
|
|
}
|
|
|
|
/*
|
|
==================
|
|
Mod_LoadModel
|
|
|
|
Loads a model into the cache
|
|
==================
|
|
*/
|
|
model_t *SWMod_LoadModel (model_t *mod, qboolean crash)
|
|
{
|
|
extern cvar_t r_replacemodels;
|
|
|
|
void *d;
|
|
unsigned *buf = NULL;
|
|
qbyte stackbuf[1024]; // avoid dirtying the cache heap
|
|
char mdlbase[MAX_QPATH];
|
|
qboolean lastload = false;
|
|
char *replstr;
|
|
// qboolean doomsprite = false;
|
|
|
|
char *ext;
|
|
|
|
if (!mod->needload)
|
|
{
|
|
if (mod->type == mod_alias)
|
|
{
|
|
d = Cache_Check (&mod->cache);
|
|
if (d)
|
|
return mod;
|
|
}
|
|
else
|
|
return mod; // not cached at all
|
|
}
|
|
#ifdef Q2BSPS
|
|
if (!*mod->name)
|
|
{
|
|
loadmodel = mod;
|
|
if (!Mod_LoadQ2BrushModel(mod, NULL))
|
|
goto couldntload;
|
|
mod->needload = false;
|
|
P_DefaultTrail(mod);
|
|
return mod;
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// because the world is so huge, load it one piece at a time
|
|
//
|
|
// set necessary engine flags for loading purposes
|
|
if (!strcmp(mod->name, "progs/player.mdl"))
|
|
{
|
|
mod->engineflags |= MDLF_PLAYER | MDLF_DOCRC;
|
|
}
|
|
else if (!strcmp(mod->name, "progs/flame.mdl") ||
|
|
!strcmp(mod->name, "progs/flame2.mdl"))
|
|
mod->engineflags |= MDLF_FLAME;
|
|
else if (!strcmp(mod->name, "progs/bolt.mdl") ||
|
|
!strcmp(mod->name, "progs/bolt2.mdl") ||
|
|
!strcmp(mod->name, "progs/bolt3.mdl") ||
|
|
!strcmp(mod->name, "progs/beam.mdl") ||
|
|
!strcmp(mod->name, "models/stsunsf2.mdl") ||
|
|
!strcmp(mod->name, "models/stsunsf1.mdl") ||
|
|
!strcmp(mod->name, "models/stice.mdl"))
|
|
mod->engineflags |= MDLF_BOLT;
|
|
else if (!strcmp(mod->name, "progs/backpack.mdl"))
|
|
mod->engineflags |= MDLF_NOTREPLACEMENTS;
|
|
else if (!strcmp(mod->name, "progs/eyes.mdl"))
|
|
mod->engineflags |= MDLF_NOTREPLACEMENTS|MDLF_DOCRC;
|
|
|
|
// call the apropriate loader
|
|
mod->needload = false;
|
|
|
|
// get string used for replacement tokens
|
|
ext = COM_FileExtension(mod->name);
|
|
if (!Q_strcasecmp(ext, "spr") || !Q_strcasecmp(ext, "sp2"))
|
|
replstr = ""; // sprite
|
|
else if (!Q_strcasecmp(ext, "dsp")) // doom sprite
|
|
{
|
|
replstr = "";
|
|
// doomsprite = true;
|
|
}
|
|
else // assume models
|
|
replstr = r_replacemodels.string;
|
|
|
|
// gl_load24bit 0 disables all replacements
|
|
// if (!gl_load24bit.value)
|
|
// replstr = "";
|
|
|
|
COM_StripExtension(mod->name, mdlbase, sizeof(mdlbase));
|
|
|
|
while (replstr)
|
|
{
|
|
replstr = COM_ParseStringSet(replstr);
|
|
if (replstr)
|
|
buf = (unsigned *)COM_LoadStackFile (va("%s.%s", mdlbase, com_token), stackbuf, sizeof(stackbuf));
|
|
else
|
|
{
|
|
buf = (unsigned *)COM_LoadStackFile (mod->name, stackbuf, sizeof(stackbuf));
|
|
if (!buf)
|
|
{
|
|
#ifdef DOOMWADS
|
|
if (doomsprite) // special case needed for doom sprites
|
|
{
|
|
mod->needload = false;
|
|
GLMod_LoadDoomSprite(mod);
|
|
P_DefaultTrail(mod);
|
|
return mod;
|
|
}
|
|
#endif
|
|
break; // failed to load unreplaced file and nothing left
|
|
}
|
|
}
|
|
if (!buf)
|
|
continue;
|
|
|
|
//
|
|
// allocate a new model
|
|
//
|
|
COM_FileBase (mod->name, loadname, sizeof(loadname));
|
|
|
|
loadmodel = mod;
|
|
//
|
|
// fill it in
|
|
//
|
|
Mod_DoCRC(mod, (char*)buf, com_filesize);
|
|
|
|
switch (LittleLong(*(unsigned *)buf))
|
|
{
|
|
#ifndef SERVERONLY
|
|
case IDPOLYHEADER:
|
|
if (!SWMod_LoadAliasModel (mod, buf))
|
|
continue;
|
|
break;
|
|
|
|
case MD2IDALIASHEADER:
|
|
if (!SWMod_LoadAlias2Model (mod, buf))
|
|
continue;
|
|
break;
|
|
|
|
case MD3_IDENT:
|
|
if (!SWMod_LoadAlias3Model (mod, buf))
|
|
continue;
|
|
break;
|
|
|
|
case IDSPRITEHEADER:
|
|
if (!SWMod_LoadSpriteModel (mod, buf))
|
|
continue;
|
|
break;
|
|
|
|
case IDSPRITE2HEADER:
|
|
if (!SWMod_LoadSprite2Model (mod, buf))
|
|
continue;
|
|
break;
|
|
#endif
|
|
#ifdef Q2BSPS
|
|
case IDBSPHEADER: //looks like id switched to have proper ids
|
|
if (!Mod_LoadQ2BrushModel (mod, buf))
|
|
continue;
|
|
break;
|
|
#endif
|
|
|
|
case BSPVERSIONHL:
|
|
case BSPVERSION: //hmm.
|
|
case BSPVERSIONPREREL:
|
|
if (!SWMod_LoadBrushModel (mod, buf))
|
|
continue;
|
|
break;
|
|
|
|
default: //some telejano mods can do this
|
|
Con_Printf(CON_WARNING "Unrecognized format %i\n", LittleLong(*(unsigned *)buf));
|
|
continue;
|
|
}
|
|
|
|
P_DefaultTrail(mod);
|
|
|
|
#ifndef SERVERONLY
|
|
if (cl.model_precache[1]) //not the world.
|
|
Validation_IncludeFile(mod->name, (char *)buf, com_filesize);
|
|
#endif
|
|
|
|
return mod;
|
|
}
|
|
|
|
couldntload:
|
|
if (crash)
|
|
Host_EndGame ("Mod_NumForName: %s not found or couldn't load", mod->name);
|
|
|
|
Con_Printf(CON_ERROR "Unable to load or replace %s\n", mod->name);
|
|
mod->type = mod_dummy;
|
|
mod->mins[0] = -16;
|
|
mod->mins[1] = -16;
|
|
mod->mins[2] = -16;
|
|
mod->maxs[0] = 16;
|
|
mod->maxs[1] = 16;
|
|
mod->maxs[2] = 16;
|
|
mod->needload = true;
|
|
mod->engineflags = 0;
|
|
P_DefaultTrail(mod);
|
|
return mod;
|
|
}
|
|
|
|
/*
|
|
==================
|
|
Mod_ForName
|
|
|
|
Loads in a model for the given name
|
|
==================
|
|
*/
|
|
model_t *SWMod_ForName (char *name, qboolean crash)
|
|
{
|
|
model_t *mod;
|
|
|
|
mod = SWMod_FindName (name);
|
|
|
|
return SWMod_LoadModel (mod, crash);
|
|
}
|
|
|
|
|
|
/*
|
|
===============================================================================
|
|
|
|
BRUSHMODEL LOADING
|
|
|
|
===============================================================================
|
|
*/
|
|
|
|
qbyte *mod_base;
|
|
|
|
|
|
/*
|
|
=================
|
|
Mod_LoadTextures
|
|
=================
|
|
*/
|
|
#ifndef SERVERONLY
|
|
qboolean SWMod_LoadTextures (lump_t *l)
|
|
{
|
|
int i, j, pixels, num, max, altmax;
|
|
miptex_t *mt;
|
|
texture_t *tx, *tx2;
|
|
texture_t *anims[10];
|
|
texture_t *altanims[10];
|
|
dmiptexlump_t *m;
|
|
|
|
if (!l->filelen)
|
|
{
|
|
loadmodel->textures = NULL;
|
|
return true;
|
|
}
|
|
m = (dmiptexlump_t *)(mod_base + l->fileofs);
|
|
|
|
m->nummiptex = LittleLong (m->nummiptex);
|
|
|
|
loadmodel->numtextures = m->nummiptex;
|
|
loadmodel->textures = Hunk_AllocName (m->nummiptex * sizeof(*loadmodel->textures) , loadname);
|
|
|
|
for (i=0 ; i<m->nummiptex ; i++)
|
|
{
|
|
m->dataofs[i] = LittleLong(m->dataofs[i]);
|
|
if (m->dataofs[i] == -1)
|
|
continue;
|
|
mt = (miptex_t *)((qbyte *)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) )
|
|
{
|
|
Con_Printf (CON_ERROR "Texture %s is not 16 aligned\n", mt->name);
|
|
return false;
|
|
}
|
|
|
|
if (!mt->offsets[0]) //external hl texture.
|
|
{
|
|
int pb;
|
|
if (r_usinglits) //allocate enough mem
|
|
pb = 4;
|
|
else
|
|
pb = 1;
|
|
|
|
pixels = mt->width*pb*mt->height/64*85;
|
|
|
|
tx = Hunk_AllocName (sizeof(texture_t) +pixels, mt->name ); //allocate enough to cover it.
|
|
tx->pixbytes = pb;
|
|
loadmodel->textures[i] = tx;
|
|
|
|
memcpy (tx->name, mt->name, sizeof(tx->name));
|
|
tx->width = mt->width;
|
|
tx->height = mt->height;
|
|
for (j=0 ; j<MIPLEVELS ; j++)
|
|
tx->offsets[j] = 0;//mt->offsets[j] + sizeof(texture_t) - sizeof(miptex_t);
|
|
// the pixels immediately follow the structures
|
|
memset ( tx+1, 7, pixels); //set it all to 7 for no particular reason.
|
|
|
|
|
|
R_AddBulleten(tx);
|
|
continue;
|
|
}
|
|
else if (loadmodel->fromgame == fg_halflife) //internal hl texture
|
|
{
|
|
qboolean alphaed;
|
|
int pb;
|
|
if (r_usinglits) //allocate enough mem
|
|
pb = 4;
|
|
else
|
|
pb = 1;
|
|
|
|
pixels = (mt->width*pb*mt->height)*85/64;
|
|
|
|
tx = Hunk_AllocName (sizeof(texture_t) +pixels, loadname );
|
|
tx->pixbytes = pb;
|
|
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(miptex_t))*4 + sizeof(texture_t);
|
|
// the pixels immediately follow the structures
|
|
if (pb == 4)
|
|
{
|
|
memcpy ( tx+1, W_ConvertWAD3Texture(mt, &mt->width, &mt->height, &alphaed), pixels);
|
|
}
|
|
else
|
|
{ //need to convert it down
|
|
int k;
|
|
qbyte *in;
|
|
qbyte *out;
|
|
out = (qbyte *)(tx+1);
|
|
in = W_ConvertWAD3Texture(mt, &mt->width, &mt->height, &alphaed);
|
|
tx->offsets[0] = (char *)out - (char *)tx;
|
|
for (j = 0; j < mt->width*mt->height; j++, in+=4)
|
|
{
|
|
if (in[3] == 0)
|
|
*out++ = 255;
|
|
else
|
|
*out++ = GetPaletteNoFB(in[0], in[1], in[2]);
|
|
}
|
|
|
|
in = out-mt->width*mt->height; //shrink mips.
|
|
|
|
tx->offsets[1] = (char *)out - (char *)tx;
|
|
for (j = 0; j < tx->height; j+=2) //we could convert mip[1], but shrinking is probably faster.
|
|
for (k = 0; k < tx->width; k+=2)
|
|
*out++ = in[k + tx->width*j];
|
|
|
|
tx->offsets[2] = (char *)out - (char *)tx;
|
|
for (j = 0; j < tx->height; j+=4)
|
|
for (k = 0; k < tx->width; k+=4)
|
|
*out++ = in[k + tx->width*j];
|
|
|
|
tx->offsets[3] = (char *)out - (char *)tx;
|
|
for (j = 0; j < tx->height; j+=8)
|
|
for (k = 0; k < tx->width; k+=8)
|
|
*out++ = in[k + tx->width*j];
|
|
}
|
|
|
|
|
|
// if (!Q_strncmp(mt->name,"sky",3))
|
|
// R_InitSky (tx);
|
|
#ifdef PEXT_BULLETENS
|
|
// else
|
|
R_AddBulleten(tx);
|
|
#endif
|
|
continue;
|
|
}
|
|
//internal 8bit texture
|
|
pixels = mt->width*mt->height/64*85;
|
|
|
|
tx = Hunk_AllocName (sizeof(texture_t) +pixels, loadname );
|
|
tx->pixbytes = 1;
|
|
loadmodel->textures[i] = tx;
|
|
|
|
tx->parttype = P_ParticleTypeForName(va("tex_%s", tx->name));
|
|
|
|
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);
|
|
|
|
if (!Q_strncmp(mt->name,"sky",3))
|
|
SWR_InitSky (tx);
|
|
#ifdef PEXT_BULLETENS
|
|
else R_AddBulleten(tx);
|
|
#endif
|
|
}
|
|
|
|
//
|
|
// sequence the animations
|
|
//
|
|
for (i=0 ; i<m->nummiptex ; i++)
|
|
{
|
|
tx = loadmodel->textures[i];
|
|
if (!tx || tx->name[0] != '+')
|
|
continue;
|
|
if (tx->anim_next)
|
|
continue; // already sequenced
|
|
|
|
// find the number of frames in the animation
|
|
memset (anims, 0, sizeof(anims));
|
|
memset (altanims, 0, sizeof(altanims));
|
|
|
|
max = tx->name[1];
|
|
altmax = 0;
|
|
if (max >= 'a' && max <= 'z')
|
|
max -= 'a' - 'A';
|
|
if (max >= '0' && max <= '9')
|
|
{
|
|
max -= '0';
|
|
altmax = 0;
|
|
anims[max] = tx;
|
|
max++;
|
|
}
|
|
else if (max >= 'A' && max <= 'J')
|
|
{
|
|
altmax = max - 'A';
|
|
max = 0;
|
|
altanims[altmax] = tx;
|
|
altmax++;
|
|
}
|
|
else
|
|
{
|
|
Con_Printf (CON_ERROR "Bad animating texture %s\n", tx->name);
|
|
return false;
|
|
}
|
|
|
|
for (j=i+1 ; j<m->nummiptex ; j++)
|
|
{
|
|
tx2 = loadmodel->textures[j];
|
|
if (!tx2 || tx2->name[0] != '+')
|
|
continue;
|
|
if (strcmp (tx2->name+2, tx->name+2))
|
|
continue;
|
|
|
|
num = tx2->name[1];
|
|
if (num >= 'a' && num <= 'z')
|
|
num -= 'a' - 'A';
|
|
if (num >= '0' && num <= '9')
|
|
{
|
|
num -= '0';
|
|
anims[num] = tx2;
|
|
if (num+1 > max)
|
|
max = num + 1;
|
|
}
|
|
else if (num >= 'A' && num <= 'J')
|
|
{
|
|
num = num - 'A';
|
|
altanims[num] = tx2;
|
|
if (num+1 > altmax)
|
|
altmax = num+1;
|
|
}
|
|
else
|
|
{
|
|
Con_Printf (CON_ERROR "Bad animating texture %s\n", tx->name);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
#define ANIM_CYCLE 2
|
|
// link them all together
|
|
for (j=0 ; j<max ; j++)
|
|
{
|
|
tx2 = anims[j];
|
|
if (!tx2)
|
|
{
|
|
Con_Printf (CON_ERROR "Missing frame %i of %s\n",j, tx->name);
|
|
return false;
|
|
}
|
|
tx2->anim_total = max * ANIM_CYCLE;
|
|
tx2->anim_min = j * ANIM_CYCLE;
|
|
tx2->anim_max = (j+1) * ANIM_CYCLE;
|
|
tx2->anim_next = anims[ (j+1)%max ];
|
|
if (altmax)
|
|
tx2->alternate_anims = altanims[0];
|
|
}
|
|
for (j=0 ; j<altmax ; j++)
|
|
{
|
|
tx2 = altanims[j];
|
|
if (!tx2)
|
|
{
|
|
Con_Printf (CON_ERROR "Missing frame %i of %s\n",j, tx->name);
|
|
return false;
|
|
}
|
|
tx2->anim_total = altmax * ANIM_CYCLE;
|
|
tx2->anim_min = j * ANIM_CYCLE;
|
|
tx2->anim_max = (j+1) * ANIM_CYCLE;
|
|
tx2->anim_next = altanims[ (j+1)%altmax ];
|
|
if (max)
|
|
tx2->alternate_anims = anims[0];
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void SWMod_NowLoadExternal(void)
|
|
{
|
|
int i, j, m, x, y;
|
|
texture_t *tx;
|
|
qbyte *in;
|
|
qbyte *out;
|
|
qboolean alphaed;
|
|
unsigned int *out32;
|
|
int t;
|
|
for (t=0 ; t<loadmodel->numtextures ; t++)
|
|
{
|
|
tx = loadmodel->textures[t];
|
|
if (tx && !tx->offsets[0])
|
|
{
|
|
in = W_GetTexture(tx->name, &(tx->width), &(tx->height), &alphaed);
|
|
i=0;
|
|
tx->offsets[0] = sizeof(texture_t);
|
|
tx->offsets[1] = tx->offsets[0] + tx->width*tx->pixbytes*tx->height;
|
|
tx->offsets[2] = tx->offsets[1] + (tx->width*tx->pixbytes*tx->height)/4;
|
|
tx->offsets[3] = tx->offsets[2] + (tx->width*tx->pixbytes*tx->height)/16;
|
|
|
|
if (!in)
|
|
{
|
|
if (tx->pixbytes == 4)
|
|
{
|
|
// out32 = (unsigned int *)((qbyte *)tx+tx->offsets[0]);
|
|
// memset(out32, 255, tx->width*tx->pixbytes*tx->height*85/64);
|
|
for (m=0 ; m<4 ; m++)
|
|
{
|
|
out32 = (int *)((qbyte *)tx + tx->offsets[m]);
|
|
for (y=0 ; y< (tx->height>>m) ; y++)
|
|
for (x=0 ; x< (tx->width>>m) ; x++)
|
|
{
|
|
if ((y/2+x/2)&1)
|
|
*out32++ = 0;
|
|
else if (y/2&1&&x/2&1)
|
|
*out32++ = 0x00007700;
|
|
else
|
|
*out32++ = 0x00000077;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// out = (qbyte *)tx+tx->offsets[0];
|
|
// memset(out32, 15, tx->width*tx->height*85/64); //usually white, anyway...
|
|
for (m=0 ; m<4 ; m++)
|
|
{
|
|
out = (qbyte *)tx + tx->offsets[m];
|
|
for (y=0 ; y< (tx->height>>m) ; y++)
|
|
for (x=0 ; x< (tx->width>>m) ; x++)
|
|
{
|
|
if ((y+x)&1)
|
|
*out++ = 0;
|
|
else
|
|
*out++ = 0xff;
|
|
}
|
|
}
|
|
}
|
|
continue; //bother.
|
|
}
|
|
|
|
if (tx->pixbytes == 4)
|
|
{
|
|
out32 = (unsigned int *)((qbyte *)tx+tx->offsets[0]);
|
|
memcpy(out32, in, tx->width*tx->pixbytes*tx->height*85/64);
|
|
}
|
|
else
|
|
{
|
|
out = (qbyte *)tx+tx->offsets[0];
|
|
for (i=0; i < tx->width*tx->height; i++) //downgrade colour
|
|
{
|
|
if (in[3] == 0)
|
|
*out++ = 255;
|
|
else
|
|
*out++ = GetPaletteNoFB(in[0], in[1], in[2]);
|
|
in += 4;
|
|
}
|
|
in = (qbyte *)tx+tx->offsets[0]; //shrink mips.
|
|
|
|
for (j = 0; j < tx->height; j+=2) //we could convert mip[1], but shrinking is probably faster.
|
|
for (i = 0; i < tx->width; i+=2)
|
|
*out++ = in[i + tx->width*j];
|
|
|
|
for (j = 0; j < tx->height; j+=4)
|
|
for (i = 0; i < tx->width; i+=4)
|
|
*out++ = in[i + tx->width*j];
|
|
|
|
for (j = 0; j < tx->height; j+=8)
|
|
for (i = 0; i < tx->width; i+=8)
|
|
*out++ = in[i + tx->width*j];
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
/*
|
|
=================
|
|
Mod_LoadLighting
|
|
=================
|
|
*/
|
|
void SWMod_LoadLighting (lump_t *l)
|
|
{
|
|
extern cvar_t r_loadlits;
|
|
if (!l->filelen)
|
|
{
|
|
loadmodel->lightdata = NULL;
|
|
return;
|
|
}
|
|
if (r_pixbytes == 4) //using 24 bit lighting
|
|
{
|
|
r_usinglits = true;
|
|
if (r_loadlits.value && r_usinglits)
|
|
{
|
|
char litname[MAX_QPATH];
|
|
qbyte *litdata = NULL;
|
|
if (!litdata)
|
|
{
|
|
strcpy(litname, loadmodel->name);
|
|
COM_StripExtension(loadmodel->name, litname, sizeof(litname));
|
|
COM_DefaultExtension(litname, ".lit", sizeof(litname));
|
|
litdata = COM_LoadHunkFile(litname);
|
|
}
|
|
if (!litdata)
|
|
{
|
|
strcpy(litname, "lits/");
|
|
COM_StripExtension(COM_SkipPath(loadmodel->name), litname+5, sizeof(litname)-5);
|
|
strcat(litname, ".lit");
|
|
|
|
litdata = COM_LoadHunkFile(litname);
|
|
}
|
|
if (litdata)
|
|
{
|
|
if (l->filelen != (com_filesize-8)/3)
|
|
Con_Printf("lit \"%s\" doesn't match level. Ignored.\n", litname);
|
|
else if (litdata[0] == 'Q' && litdata[1] == 'L' && litdata[2] == 'I' && litdata[3] == 'T')
|
|
{
|
|
if (LittleLong(*(int *)&litdata[4]) == 1)
|
|
{
|
|
float prop;
|
|
int i;
|
|
qbyte *normal;
|
|
// qbyte max;
|
|
loadmodel->lightdata = litdata+8;
|
|
|
|
//now some cheat protection.
|
|
normal = mod_base + l->fileofs;
|
|
litdata = loadmodel->lightdata;
|
|
|
|
for (i = 0; i < l->filelen; i++) //force it to the same intensity. (or less, depending on how you see it...)
|
|
{
|
|
#define m(a, b, c) (a>(b>c?b:c)?a:(b>c?b:c))
|
|
prop = *normal / (float)m(litdata[0], litdata[1], litdata[2]);
|
|
litdata[0] *= prop;
|
|
litdata[1] *= prop;
|
|
litdata[2] *= prop;
|
|
|
|
normal++;
|
|
litdata+=3;
|
|
}
|
|
//end anti-cheat
|
|
return;
|
|
}
|
|
else
|
|
Con_Printf("\"%s\" isn't a standard version 1 lit\n", litname);
|
|
}
|
|
else
|
|
Con_Printf("lit \"%s\" isn't a lit\n", litname);
|
|
}
|
|
}
|
|
if (loadmodel->fromgame == fg_halflife || loadmodel->fromgame == fg_quake2 || loadmodel->fromgame == fg_quake3) //half-life levels use 24 bit anyway.
|
|
{
|
|
loadmodel->lightdata = Hunk_AllocName ( l->filelen, loadname);
|
|
memcpy (loadmodel->lightdata, mod_base + l->fileofs, l->filelen);
|
|
}
|
|
else if (!r_usinglits)
|
|
{
|
|
loadmodel->lightdata = Hunk_AllocName ( l->filelen, loadname);
|
|
memcpy (loadmodel->lightdata, mod_base + l->fileofs, l->filelen);
|
|
}
|
|
else
|
|
{ //expand to 24 bit
|
|
int i;
|
|
qbyte *dest, *src = mod_base + l->fileofs;
|
|
loadmodel->lightdata = Hunk_AllocName ( l->filelen*3, loadname);
|
|
dest = loadmodel->lightdata;
|
|
for (i = 0; i<l->filelen; i++)
|
|
{
|
|
dest[0] = *src;
|
|
dest[1] = *src;
|
|
dest[2] = *src;
|
|
|
|
src++;
|
|
dest+=3;
|
|
}
|
|
}
|
|
|
|
if (r_lightmap_saturation.value != 1.0f)
|
|
SaturateR8G8B8(loadmodel->lightdata, l->filelen, r_lightmap_saturation.value);
|
|
}
|
|
else
|
|
{
|
|
r_usinglits = false;
|
|
if (loadmodel->fromgame == fg_halflife || loadmodel->fromgame == fg_quake2 || loadmodel->fromgame == fg_quake3)
|
|
{
|
|
int i;
|
|
qbyte *out;
|
|
qbyte *in;
|
|
out = loadmodel->lightdata = Hunk_AllocName ( l->filelen/3, loadname);
|
|
in = mod_base + l->fileofs; //24 bit to luminance.
|
|
for (i = 0; i < l->filelen; i+=3)
|
|
*out++ = ((int)in[i] + (int)in[i] + (int)in[i])/3;
|
|
|
|
}
|
|
else
|
|
{//standard Quake
|
|
loadmodel->lightdata = Hunk_AllocName ( l->filelen, loadname);
|
|
memcpy (loadmodel->lightdata, mod_base + l->fileofs, l->filelen);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
=================
|
|
Mod_LoadVisibility
|
|
=================
|
|
*/
|
|
void SWMod_LoadVisibility (lump_t *l)
|
|
{
|
|
if (!l->filelen)
|
|
{
|
|
loadmodel->visdata = NULL;
|
|
return;
|
|
}
|
|
loadmodel->visdata = Hunk_AllocName ( l->filelen, loadname);
|
|
memcpy (loadmodel->visdata, mod_base + l->fileofs, l->filelen);
|
|
}
|
|
|
|
|
|
/*
|
|
=================
|
|
Mod_LoadEntities
|
|
=================
|
|
*/
|
|
void SWMod_LoadEntities (lump_t *l)
|
|
{
|
|
if (!l->filelen)
|
|
{
|
|
loadmodel->entities = NULL;
|
|
return;
|
|
}
|
|
loadmodel->entities = Hunk_AllocName ( l->filelen + 1, loadname);
|
|
memcpy (loadmodel->entities, mod_base + l->fileofs, l->filelen);
|
|
loadmodel->entities[l->filelen] = 0;
|
|
}
|
|
|
|
|
|
/*
|
|
=================
|
|
Mod_LoadVertexes
|
|
=================
|
|
*/
|
|
qboolean SWMod_LoadVertexes (lump_t *l)
|
|
{
|
|
dvertex_t *in;
|
|
mvertex_t *out;
|
|
int i, count;
|
|
|
|
in = (void *)(mod_base + l->fileofs);
|
|
if (l->filelen % sizeof(*in))
|
|
{
|
|
Con_Printf (CON_ERROR "MOD_LoadBmodel: funny lump size in %s\n",loadmodel->name);
|
|
return false;
|
|
}
|
|
count = l->filelen / sizeof(*in);
|
|
out = Hunk_AllocName ( (count+8)*sizeof(*out), loadname); //spare for skybox
|
|
|
|
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]);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static qboolean hexen2map;
|
|
/*
|
|
=================
|
|
Mod_LoadSubmodels
|
|
=================
|
|
*/
|
|
qboolean SWMod_LoadSubmodels (lump_t *l)
|
|
{
|
|
dq1model_t *inq;
|
|
dh2model_t *inh;
|
|
mmodel_t *out;
|
|
int i, j, count;
|
|
|
|
//this is crazy!
|
|
|
|
inq = (void *)(mod_base + l->fileofs);
|
|
inh = (void *)(mod_base + l->fileofs);
|
|
if (!inq->numfaces)
|
|
{
|
|
hexen2map = true;
|
|
if (l->filelen % sizeof(*inh))
|
|
{
|
|
Con_Printf (CON_ERROR "MOD_LoadBmodel: funny lump size in %s\n",loadmodel->name);
|
|
return false;
|
|
}
|
|
count = l->filelen / sizeof(*inh);
|
|
out = Hunk_AllocName ( count*sizeof(*out), loadname);
|
|
|
|
loadmodel->submodels = out;
|
|
loadmodel->numsubmodels = count;
|
|
|
|
for ( i=0 ; i<count ; i++, inh++, out++)
|
|
{
|
|
for (j=0 ; j<3 ; j++)
|
|
{ // spread the mins / maxs by a pixel
|
|
out->mins[j] = LittleFloat (inh->mins[j]) - 1;
|
|
out->maxs[j] = LittleFloat (inh->maxs[j]) + 1;
|
|
out->origin[j] = LittleFloat (inh->origin[j]);
|
|
}
|
|
for (j=0 ; j<MAX_MAP_HULLSDH2 ; j++)
|
|
{
|
|
out->headnode[j] = LittleLong (inh->headnode[j]);
|
|
}
|
|
for ( ; j<MAX_MAP_HULLSM ; j++)
|
|
out->headnode[j] = 0;
|
|
for (j=0 ; j<MAX_MAP_HULLSDH2 ; j++)
|
|
out->hullavailable[j] = true;
|
|
for ( ; j<MAX_MAP_HULLSM ; j++)
|
|
out->hullavailable[j] = false;
|
|
out->visleafs = LittleLong (inh->visleafs);
|
|
out->firstface = LittleLong (inh->firstface);
|
|
out->numfaces = LittleLong (inh->numfaces);
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
hexen2map = false;
|
|
if (l->filelen % sizeof(*inq))
|
|
{
|
|
Con_Printf (CON_ERROR "MOD_LoadBmodel: funny lump size in %s\n",loadmodel->name);
|
|
return false;
|
|
}
|
|
count = l->filelen / sizeof(*inq);
|
|
out = Hunk_AllocName ( count*sizeof(*out), loadname);
|
|
|
|
loadmodel->submodels = out;
|
|
loadmodel->numsubmodels = count;
|
|
|
|
for ( i=0 ; i<count ; i++, inq++, out++)
|
|
{
|
|
for (j=0 ; j<3 ; j++)
|
|
{ // spread the mins / maxs by a pixel
|
|
out->mins[j] = LittleFloat (inq->mins[j]) - 1;
|
|
out->maxs[j] = LittleFloat (inq->maxs[j]) + 1;
|
|
out->origin[j] = LittleFloat (inq->origin[j]);
|
|
}
|
|
for (j=0 ; j<MAX_MAP_HULLSDQ1 ; j++)
|
|
{
|
|
out->headnode[j] = LittleLong (inq->headnode[j]);
|
|
}
|
|
for ( ; j<MAX_MAP_HULLSM ; j++)
|
|
out->headnode[j] = 0;
|
|
for (j=0 ; j<3 ; j++)
|
|
out->hullavailable[j] = true;
|
|
for ( ; j<MAX_MAP_HULLSM ; j++)
|
|
out->hullavailable[j] = false;
|
|
out->visleafs = LittleLong (inq->visleafs);
|
|
out->firstface = LittleLong (inq->firstface);
|
|
out->numfaces = LittleLong (inq->numfaces);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
Mod_LoadEdges
|
|
=================
|
|
*/
|
|
qboolean SWMod_LoadEdges (lump_t *l)
|
|
{
|
|
dedge_t *in;
|
|
medge_t *out;
|
|
int i, count;
|
|
|
|
in = (void *)(mod_base + l->fileofs);
|
|
if (l->filelen % sizeof(*in))
|
|
{
|
|
Con_Printf (CON_ERROR "MOD_LoadBmodel: funny lump size in %s\n",loadmodel->name);
|
|
return false;
|
|
}
|
|
count = l->filelen / sizeof(*in);
|
|
out = Hunk_AllocName ( (count + 1 + 12) * sizeof(*out), loadname); //spare for skybox
|
|
|
|
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]);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
Mod_LoadTexinfo
|
|
=================
|
|
*/
|
|
#ifndef SERVERONLY
|
|
qboolean SWMod_LoadTexinfo (lump_t *l)
|
|
{
|
|
texinfo_t *in;
|
|
mtexinfo_t *out;
|
|
int i, j, count;
|
|
int miptex;
|
|
float len1, len2;
|
|
|
|
in = (void *)(mod_base + l->fileofs);
|
|
if (l->filelen % sizeof(*in))
|
|
{
|
|
Con_Printf (CON_ERROR "MOD_LoadBmodel: funny lump size in %s\n",loadmodel->name);
|
|
return false;
|
|
}
|
|
count = l->filelen / sizeof(*in);
|
|
out = 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 = Length (out->vecs[0]);
|
|
len2 = Length (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);
|
|
|
|
if (!loadmodel->textures)
|
|
{
|
|
out->texture = r_notexture_mip; // checkerboard texture
|
|
out->flags = 0;
|
|
}
|
|
else
|
|
{
|
|
if (miptex >= loadmodel->numtextures)
|
|
Sys_Error ("miptex >= loadmodel->numtextures");
|
|
out->texture = loadmodel->textures[miptex];
|
|
if (!out->texture)
|
|
{
|
|
out->texture = r_notexture_mip; // texture not found
|
|
out->flags = 0;
|
|
}
|
|
else if (!strncmp(out->texture->name, "sky", 3))
|
|
{
|
|
out->flags |= SURF_SKY;
|
|
}
|
|
else if (!strncmp(out->texture->name, "glass", 5)) //halflife levels
|
|
{
|
|
out->flags |= SURF_TRANS66;
|
|
}
|
|
else if (*out->texture->name == '~') //halflife levels
|
|
{
|
|
out->flags |= SURF_ALPHATEST;
|
|
}
|
|
else if (*out->texture->name == '!') //halflife levels
|
|
{
|
|
out->flags |= SURF_WARP | SURF_TRANS33;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
#endif
|
|
/*
|
|
================
|
|
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 (s->extents[i] < 16)
|
|
s->extents[i] = 16; // take at least one cache block
|
|
|
|
// if ( !(tex->flags & TEX_SPECIAL) && s->extents[i] > 512)
|
|
// Sys_Error ("Bad surface extents");
|
|
}
|
|
}
|
|
*/
|
|
|
|
/*
|
|
=================
|
|
Mod_LoadFaces
|
|
=================
|
|
*/
|
|
#ifndef SERVERONLY
|
|
qboolean SWMod_LoadFaces (lump_t *l)
|
|
{
|
|
dface_t *in;
|
|
msurface_t *out;
|
|
int i, count, surfnum;
|
|
int planenum, side;
|
|
|
|
in = (void *)(mod_base + l->fileofs);
|
|
if (l->filelen % sizeof(*in))
|
|
{
|
|
Con_Printf (CON_ERROR "MOD_LoadBmodel: funny lump size in %s\n",loadmodel->name);
|
|
return false;
|
|
}
|
|
count = l->filelen / sizeof(*in);
|
|
out = Hunk_AllocName ( (count+6)*sizeof(*out), loadname); //spare for skybox
|
|
|
|
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);
|
|
|
|
if (!out->texinfo->texture)
|
|
out->texinfo->texture = r_notexture_mip;
|
|
|
|
CalcSurfaceExtents (out);
|
|
|
|
// 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 if (r_usinglits)
|
|
{
|
|
if (loadmodel->fromgame == fg_halflife)
|
|
out->samples = loadmodel->lightdata + i;
|
|
else
|
|
out->samples = loadmodel->lightdata + i*3;
|
|
}
|
|
else
|
|
{
|
|
if (loadmodel->fromgame == fg_halflife)
|
|
out->samples = loadmodel->lightdata + i/3;
|
|
else
|
|
out->samples = loadmodel->lightdata + i;
|
|
}
|
|
|
|
// set the drawing flags flag
|
|
|
|
if (!Q_strncmp(out->texinfo->texture->name,"sky",3)) // sky
|
|
{
|
|
if (loadmodel->fromgame == fg_halflife)
|
|
out->flags |= SURF_DRAWBACKGROUND;
|
|
else
|
|
out->flags |= (SURF_DRAWSKY | SURF_DRAWTILED);
|
|
continue;
|
|
}
|
|
|
|
if (!Q_strncmp(out->texinfo->texture->name,"*",1)) // turbulent
|
|
{
|
|
out->flags |= (SURF_DRAWTURB | SURF_DRAWTILED);
|
|
for (i=0 ; i<2 ; i++)
|
|
{
|
|
out->extents[i] = 16384;
|
|
out->texturemins[i] = -8192;
|
|
}
|
|
continue;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
=================
|
|
Mod_SetParent
|
|
=================
|
|
*/
|
|
void SWMod_SetParent (mnode_t *node, mnode_t *parent)
|
|
{
|
|
node->parent = parent;
|
|
if (node->contents < 0)
|
|
return;
|
|
SWMod_SetParent (node->children[0], node);
|
|
SWMod_SetParent (node->children[1], node);
|
|
}
|
|
|
|
/*
|
|
=================
|
|
Mod_LoadNodes
|
|
=================
|
|
*/
|
|
qboolean SWMod_LoadNodes (lump_t *l)
|
|
{
|
|
int i, j, count, p;
|
|
dnode_t *in;
|
|
mnode_t *out;
|
|
|
|
in = (void *)(mod_base + l->fileofs);
|
|
if (l->filelen % sizeof(*in))
|
|
{
|
|
Con_Printf (CON_ERROR "MOD_LoadBmodel: funny lump size in %s\n",loadmodel->name);
|
|
return false;
|
|
}
|
|
count = l->filelen / sizeof(*in);
|
|
out = 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 = LittleShort (in->firstface);
|
|
out->numsurfaces = LittleShort (in->numfaces);
|
|
|
|
for (j=0 ; j<2 ; j++)
|
|
{
|
|
p = LittleShort (in->children[j]);
|
|
if (p >= 0)
|
|
out->children[j] = loadmodel->nodes + p;
|
|
else
|
|
out->children[j] = (mnode_t *)(loadmodel->leafs + (-1 - p));
|
|
}
|
|
}
|
|
|
|
SWMod_SetParent (loadmodel->nodes, NULL); // sets nodes and leafs
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
Mod_LoadLeafs
|
|
=================
|
|
*/
|
|
qboolean SWMod_LoadLeafs (lump_t *l)
|
|
{
|
|
dleaf_t *in;
|
|
mleaf_t *out;
|
|
int i, j, count, p;
|
|
|
|
in = (void *)(mod_base + l->fileofs);
|
|
if (l->filelen % sizeof(*in))
|
|
{
|
|
Con_Printf (CON_ERROR "MOD_LoadBmodel: funny lump size in %s\n",loadmodel->name);
|
|
return false;
|
|
}
|
|
count = l->filelen / sizeof(*in);
|
|
out = 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 +
|
|
(unsigned short)LittleShort(in->firstmarksurface);
|
|
out->nummarksurfaces = (unsigned short)LittleShort(in->nummarksurfaces);
|
|
|
|
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];
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//these are used to boost other info sizes
|
|
int numsuplementryplanes;
|
|
int numsuplementryclipnodes;
|
|
void *suplementryclipnodes;
|
|
void *suplementryplanes;
|
|
void *crouchhullfile;
|
|
|
|
qbyte *COM_LoadMallocFile (char *path);
|
|
void SWMod_LoadCrouchHull(void)
|
|
{
|
|
int i;
|
|
int numsm;
|
|
char crouchhullname[MAX_QPATH];
|
|
int *data;
|
|
|
|
// dclipnode_t *cn;
|
|
|
|
numsuplementryplanes = numsuplementryclipnodes = 0;
|
|
|
|
//find a name for a ccn and try to load it.
|
|
strcpy(crouchhullname, loadmodel->name);
|
|
COM_StripExtension(loadmodel->name, crouchhullname, sizeof(crouchhullname));
|
|
COM_DefaultExtension(crouchhullname, ".crh", sizeof(crouchhullname)); //crouch hull
|
|
|
|
crouchhullfile = COM_LoadMallocFile(crouchhullname); //or otherwise temporary storage. load on hunk if you want, but that would be a waste.
|
|
if (!crouchhullfile)
|
|
return;
|
|
|
|
if (LittleLong(((int *)crouchhullfile)[0]) != ('Q') + ('C'<<8) + ('C'<<16) + ('N'<<24)) //make sure it's the right version
|
|
return;
|
|
|
|
if (LittleLong(((int *)crouchhullfile)[1]) != 0) //make sure it's the right version
|
|
return;
|
|
|
|
numsm = LittleLong(((int *)crouchhullfile)[2]);
|
|
if (numsm != loadmodel->numsubmodels) //not compatible
|
|
return;
|
|
|
|
numsuplementryplanes = LittleLong(((int *)crouchhullfile)[3]);
|
|
numsuplementryclipnodes = LittleLong(((int *)crouchhullfile)[4]);
|
|
|
|
data = &((int *)crouchhullfile)[5];
|
|
|
|
for (i = 0; i < numsm; i++) //load headnode references
|
|
{
|
|
loadmodel->submodels[i].headnode[3] = LittleLong(*data)+1;
|
|
data++;
|
|
}
|
|
|
|
suplementryplanes = data;
|
|
suplementryclipnodes = (qbyte*)data + sizeof(dplane_t)*numsuplementryplanes;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
Mod_LoadClipnodes
|
|
=================
|
|
*/
|
|
qboolean SWMod_LoadClipnodes (lump_t *l)
|
|
{
|
|
dclipnode_t *in, *out;
|
|
int i, count;
|
|
hull_t *hull;
|
|
|
|
in = (void *)(mod_base + l->fileofs);
|
|
if (l->filelen % sizeof(*in))
|
|
{
|
|
Con_Printf (CON_ERROR "MOD_LoadBmodel: funny lump size in %s\n", loadmodel->name);
|
|
return false;
|
|
}
|
|
count = l->filelen / sizeof(*in);
|
|
out = Hunk_AllocName ( (count+numsuplementryclipnodes)*sizeof(*out), loadname);//space for both
|
|
|
|
loadmodel->clipnodes = out;
|
|
loadmodel->numclipnodes = count+numsuplementryclipnodes;
|
|
|
|
if (hexen2map)
|
|
{ //hexen2.
|
|
hexen2map=false;
|
|
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->available = true;
|
|
|
|
hull = &loadmodel->hulls[2];
|
|
hull->clipnodes = out;
|
|
hull->firstclipnode = 0;
|
|
hull->lastclipnode = count-1;
|
|
hull->planes = loadmodel->planes;
|
|
hull->clip_mins[0] = -24;
|
|
hull->clip_mins[1] = -24;
|
|
hull->clip_mins[2] = -20;
|
|
hull->clip_maxs[0] = 24;
|
|
hull->clip_maxs[1] = 24;
|
|
hull->clip_maxs[2] = 20;
|
|
hull->available = true;
|
|
|
|
hull = &loadmodel->hulls[3];
|
|
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] = -12;
|
|
hull->clip_maxs[0] = 16;
|
|
hull->clip_maxs[1] = 16;
|
|
hull->clip_maxs[2] = 16;
|
|
hull->available = true;
|
|
|
|
hull = &loadmodel->hulls[4];
|
|
hull->clipnodes = out;
|
|
hull->firstclipnode = 0;
|
|
hull->lastclipnode = count-1;
|
|
hull->planes = loadmodel->planes;
|
|
hull->clip_mins[0] = -40;
|
|
hull->clip_mins[1] = -40;
|
|
hull->clip_mins[2] = -42;
|
|
hull->clip_maxs[0] = 40;
|
|
hull->clip_maxs[1] = 40;
|
|
hull->clip_maxs[2] = 42;
|
|
hull->available = true;
|
|
|
|
hull = &loadmodel->hulls[5];
|
|
hull->clipnodes = out;
|
|
hull->firstclipnode = 0;
|
|
hull->lastclipnode = count-1;
|
|
hull->planes = loadmodel->planes;
|
|
hull->clip_mins[0] = -48;
|
|
hull->clip_mins[1] = -48;
|
|
hull->clip_mins[2] = -50;
|
|
hull->clip_maxs[0] = 48;
|
|
hull->clip_maxs[1] = 48;
|
|
hull->clip_maxs[2] = 50;
|
|
hull->available = true;
|
|
}
|
|
else if (loadmodel->fromgame == fg_halflife)
|
|
{
|
|
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] = -32; //I dont know what is correct here, but we'll just copy mvdsv for now
|
|
hull->clip_maxs[0] = 16;
|
|
hull->clip_maxs[1] = 16;
|
|
hull->clip_maxs[2] = 32;
|
|
hull->available = true;
|
|
|
|
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] = -32;
|
|
hull->clip_maxs[0] = 32;
|
|
hull->clip_maxs[1] = 32;
|
|
hull->clip_maxs[2] = 32;
|
|
hull->available = true;
|
|
|
|
hull = &loadmodel->hulls[3];
|
|
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] = -18;
|
|
hull->clip_maxs[0] = 16;
|
|
hull->clip_maxs[1] = 16;
|
|
hull->clip_maxs[2] = 18;
|
|
hull->available = true;
|
|
}
|
|
else
|
|
{
|
|
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->available = true;
|
|
|
|
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;
|
|
hull->available = true;
|
|
|
|
hull = &loadmodel->hulls[3];
|
|
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] = -6;
|
|
hull->clip_maxs[0] = 16;
|
|
hull->clip_maxs[1] = 16;
|
|
hull->clip_maxs[2] = 30;
|
|
hull->available = false;
|
|
}
|
|
|
|
for (i=0 ; i<count ; i++, out++, in++)
|
|
{
|
|
out->planenum = LittleLong(in->planenum);
|
|
out->children[0] = LittleShort(in->children[0]);
|
|
out->children[1] = LittleShort(in->children[1]);
|
|
}
|
|
|
|
if (numsuplementryclipnodes) //now load the crouch ones.
|
|
{
|
|
in = suplementryclipnodes;
|
|
hull->planes = suplementryplanes;
|
|
hull->clipnodes = out-1;
|
|
hull->lastclipnode = numsuplementryclipnodes;
|
|
hull->available = true;
|
|
|
|
for (i=0 ; i<numsuplementryclipnodes ; i++, out++, in++)
|
|
{
|
|
out->planenum = LittleLong(in->planenum);
|
|
out->children[0] = LittleShort(in->children[0]);
|
|
out->children[0] += out->children[0]>=0?1:0;
|
|
out->children[1] = LittleShort(in->children[1]);
|
|
out->children[1] += out->children[1]>=0?1:0;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
Mod_MakeHull0
|
|
|
|
Deplicate the drawing hull structure as a clipping hull
|
|
=================
|
|
*/
|
|
void SWMod_MakeHull0 (void)
|
|
{
|
|
mnode_t *in, *child;
|
|
dclipnode_t *out;
|
|
int i, j, count;
|
|
hull_t *hull;
|
|
|
|
hull = &loadmodel->hulls[0];
|
|
|
|
in = loadmodel->nodes;
|
|
count = loadmodel->numnodes;
|
|
out = 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
|
|
=================
|
|
*/
|
|
qboolean SWMod_LoadMarksurfaces (lump_t *l)
|
|
{
|
|
int i, j, count;
|
|
short *in;
|
|
msurface_t **out;
|
|
|
|
in = (void *)(mod_base + l->fileofs);
|
|
if (l->filelen % sizeof(*in))
|
|
{
|
|
Con_Printf (CON_ERROR "MOD_LoadBmodel: funny lump size in %s\n",loadmodel->name);
|
|
return false;
|
|
}
|
|
count = l->filelen / sizeof(*in);
|
|
out = Hunk_AllocName ( count*sizeof(*out), loadname);
|
|
|
|
loadmodel->marksurfaces = out;
|
|
loadmodel->nummarksurfaces = count;
|
|
|
|
for ( i=0 ; i<count ; i++)
|
|
{
|
|
j = (unsigned short)LittleShort(in[i]);
|
|
if (j >= loadmodel->numsurfaces)
|
|
{
|
|
Con_Printf (CON_ERROR "Mod_ParseMarksurfaces: bad surface number\n");
|
|
return false;
|
|
}
|
|
out[i] = loadmodel->surfaces + j;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
Mod_LoadSurfedges
|
|
=================
|
|
*/
|
|
qboolean SWMod_LoadSurfedges (lump_t *l)
|
|
{
|
|
int i, count;
|
|
int *in, *out;
|
|
|
|
in = (void *)(mod_base + l->fileofs);
|
|
if (l->filelen % sizeof(*in))
|
|
{
|
|
Con_Printf ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
|
|
return false;
|
|
}
|
|
count = l->filelen / sizeof(*in);
|
|
out = Hunk_AllocName ( (count+24)*sizeof(*out), loadname); //spare for skybox
|
|
|
|
loadmodel->surfedges = out;
|
|
loadmodel->numsurfedges = count;
|
|
|
|
for ( i=0 ; i<count ; i++)
|
|
out[i] = LittleLong (in[i]);
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
Mod_LoadPlanes
|
|
=================
|
|
*/
|
|
qboolean SWMod_LoadPlanes (lump_t *l)
|
|
{
|
|
int i, j;
|
|
mplane_t *out;
|
|
dplane_t *in;
|
|
int count;
|
|
int bits;
|
|
|
|
in = (void *)(mod_base + l->fileofs);
|
|
if (l->filelen % sizeof(*in))
|
|
{
|
|
Con_Printf (CON_ERROR "MOD_LoadBmodel: funny lump size in %s\n",loadmodel->name);
|
|
return false;
|
|
}
|
|
count = l->filelen / sizeof(*in);
|
|
out = Hunk_AllocName ( (count+numsuplementryplanes)*2*sizeof(*out), loadname);
|
|
|
|
loadmodel->planes = out;
|
|
loadmodel->numplanes = count+numsuplementryplanes;
|
|
|
|
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;
|
|
}
|
|
|
|
if (numsuplementryplanes)
|
|
{
|
|
in = suplementryplanes;
|
|
suplementryplanes = out;
|
|
for ( i=0 ; i<numsuplementryplanes ; 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;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
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 Length (corner);
|
|
}
|
|
*/
|
|
|
|
|
|
void Q1BSP_MarkLights (dlight_t *light, int bit, mnode_t *node);
|
|
void SWQ1BSP_LightPointValues(model_t *mod, vec3_t point, vec3_t res_diffuse, vec3_t res_ambient, vec3_t res_dir);
|
|
|
|
void SWR_Q1BSP_StainNode (mnode_t *node, float *parms);
|
|
/*
|
|
=================
|
|
Mod_LoadBrushModel
|
|
=================
|
|
*/
|
|
qboolean SWMod_LoadBrushModel (model_t *mod, void *buffer)
|
|
{
|
|
int i, j;
|
|
dheader_t *header;
|
|
mmodel_t *bm;
|
|
int start;
|
|
qboolean noerrors;
|
|
|
|
start = Hunk_LowMark();
|
|
|
|
loadmodel->type = mod_brush;
|
|
|
|
header = (dheader_t *)buffer;
|
|
|
|
i = LittleLong (header->version);
|
|
|
|
if (i == BSPVERSION || i == BSPVERSIONPREREL)
|
|
loadmodel->fromgame = fg_quake;
|
|
else if (i == BSPVERSIONHL)
|
|
loadmodel->fromgame = fg_halflife;
|
|
else
|
|
{
|
|
Con_Printf (CON_ERROR "Mod_LoadBrushModel: %s has wrong version number (%i should be %i)", mod->name, i, BSPVERSION);
|
|
return false;
|
|
}
|
|
// 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 = (qbyte *)header;
|
|
|
|
for (i=0 ; i<sizeof(dheader_t)/4 ; i++)
|
|
((int *)header)[i] = LittleLong ( ((int *)header)[i]);
|
|
|
|
mod->checksum = 0;
|
|
mod->checksum2 = 0;
|
|
|
|
// checksum all of the map, except for entities
|
|
for (i = 0; i < HEADER_LUMPS; i++)
|
|
{
|
|
if ((unsigned)header->lumps[i].fileofs + (unsigned)header->lumps[i].filelen > com_filesize)
|
|
{
|
|
Con_Printf (CON_ERROR "Mod_LoadBrushModel: %s appears truncated\n", mod->name);
|
|
return false;
|
|
}
|
|
if (i == LUMP_ENTITIES)
|
|
continue;
|
|
mod->checksum ^= Com_BlockChecksum(mod_base + header->lumps[i].fileofs,
|
|
header->lumps[i].filelen);
|
|
|
|
if (i == LUMP_VISIBILITY || i == LUMP_LEAFS || i == LUMP_NODES)
|
|
continue;
|
|
mod->checksum2 ^= Com_BlockChecksum(mod_base + header->lumps[i].fileofs,
|
|
header->lumps[i].filelen);
|
|
}
|
|
|
|
noerrors = true;
|
|
crouchhullfile = NULL;
|
|
|
|
// load into heap
|
|
#ifndef CLIENTONLY
|
|
if (!isDedicated)
|
|
#endif
|
|
{
|
|
noerrors = noerrors && SWMod_LoadVertexes (&header->lumps[LUMP_VERTEXES]);
|
|
noerrors = noerrors && SWMod_LoadEdges (&header->lumps[LUMP_EDGES]);
|
|
noerrors = noerrors && SWMod_LoadSurfedges (&header->lumps[LUMP_SURFEDGES]);
|
|
if (noerrors)
|
|
SWMod_LoadLighting (&header->lumps[LUMP_LIGHTING]); //DMW, made lighting load first. (so we know if lighting is rgb or luminance)
|
|
noerrors = noerrors && SWMod_LoadTextures (&header->lumps[LUMP_TEXTURES]);
|
|
}
|
|
|
|
noerrors = noerrors && SWMod_LoadSubmodels (&header->lumps[LUMP_MODELS]); //needs to come before we set the headnodes[3]
|
|
if (noerrors)
|
|
SWMod_LoadCrouchHull ();
|
|
noerrors = noerrors && SWMod_LoadPlanes (&header->lumps[LUMP_PLANES]);
|
|
|
|
#ifndef CLIENTONLY
|
|
if (!isDedicated)
|
|
#endif
|
|
{
|
|
noerrors = noerrors && SWMod_LoadTexinfo (&header->lumps[LUMP_TEXINFO]);
|
|
noerrors = noerrors && SWMod_LoadFaces (&header->lumps[LUMP_FACES]);
|
|
noerrors = noerrors && SWMod_LoadMarksurfaces (&header->lumps[LUMP_MARKSURFACES]);
|
|
}
|
|
|
|
if (noerrors)
|
|
SWMod_LoadVisibility (&header->lumps[LUMP_VISIBILITY]);
|
|
noerrors = noerrors && SWMod_LoadLeafs (&header->lumps[LUMP_LEAFS]);
|
|
noerrors = noerrors && SWMod_LoadNodes (&header->lumps[LUMP_NODES]);
|
|
noerrors = noerrors && SWMod_LoadClipnodes (&header->lumps[LUMP_CLIPNODES]);
|
|
if (noerrors)
|
|
SWMod_LoadEntities (&header->lumps[LUMP_ENTITIES]);
|
|
|
|
if (crouchhullfile)
|
|
{
|
|
BZ_Free(crouchhullfile);
|
|
crouchhullfile=NULL;
|
|
}
|
|
|
|
if (!noerrors)
|
|
{
|
|
Hunk_FreeToLowMark(start);
|
|
return false;
|
|
}
|
|
|
|
Q1BSP_SetModelFuncs(mod);
|
|
mod->funcs.LightPointValues = SWQ1BSP_LightPointValues;
|
|
mod->funcs.StainNode = SWR_Q1BSP_StainNode;
|
|
mod->funcs.MarkLights = Q1BSP_MarkLights;
|
|
|
|
|
|
//We ONLY do this for the world model
|
|
#ifndef SERVERONLY
|
|
#ifndef CLIENTONLY
|
|
if (sv.state) //if the server is running
|
|
{
|
|
if (!strcmp(loadmodel->name, va("maps/%s.bsp", sv.name)))
|
|
Mod_ParseInfoFromEntityLump(mod_base + header->lumps[LUMP_ENTITIES].fileofs);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
if (!cl.model_precache[1]) //not copied across yet
|
|
Mod_ParseInfoFromEntityLump(mod_base + header->lumps[LUMP_ENTITIES].fileofs);
|
|
}
|
|
#endif
|
|
|
|
SWMod_MakeHull0 ();
|
|
|
|
mod->numframes = 2; // regular and alternate animation
|
|
|
|
//
|
|
// set up the submodels (FIXME: this is confusing)
|
|
//
|
|
for (i=0 ; i<mod->numsubmodels ; i++)
|
|
{
|
|
bm = &mod->submodels[i];
|
|
|
|
mod->hulls[0].firstclipnode = bm->headnode[0];
|
|
Q1BSP_SetHullFuncs(&mod->hulls[0]);
|
|
for (j=1 ; j<MAX_MAP_HULLSM ; j++)
|
|
{
|
|
mod->hulls[j].firstclipnode = bm->headnode[j];
|
|
mod->hulls[j].lastclipnode = mod->numclipnodes-1;
|
|
Q1BSP_SetHullFuncs(&mod->hulls[j]);
|
|
}
|
|
|
|
mod->firstmodelsurface = bm->firstface;
|
|
mod->nummodelsurfaces = bm->numfaces;
|
|
mod->radius = RadiusFromBounds (mod->mins, mod->maxs);
|
|
|
|
VectorCopy (bm->maxs, mod->maxs);
|
|
VectorCopy (bm->mins, mod->mins);
|
|
|
|
mod->numleafs = bm->visleafs;
|
|
|
|
if (i < mod->numsubmodels-1)
|
|
{ // duplicate the basic information
|
|
char name[10];
|
|
|
|
sprintf (name, "*%i", i+1);
|
|
loadmodel = SWMod_FindName (name);
|
|
*loadmodel = *mod;
|
|
strcpy (loadmodel->name, name);
|
|
mod = loadmodel;
|
|
|
|
P_DefaultTrail(mod);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
#ifndef SERVERONLY
|
|
/*
|
|
==============================================================================
|
|
|
|
ALIAS MODELS
|
|
|
|
==============================================================================
|
|
*/
|
|
|
|
void * SWMod_LoadAliasQTestFrame (void * pin, int *pframeindex, int numv,
|
|
dtrivertx_t *pbboxmin, dtrivertx_t *pbboxmax, aliashdr_t *pheader, char *name)
|
|
{
|
|
dtrivertx_t *pframe, *pinframe;
|
|
int i, j;
|
|
qtestaliasframe_t *pdaliasframe;
|
|
|
|
pdaliasframe = (qtestaliasframe_t *)pin;
|
|
|
|
name[0] = '\0';
|
|
|
|
for (i=0 ; i<3 ; i++)
|
|
{
|
|
// these are byte values, so we don't have to worry about
|
|
// endianness
|
|
pbboxmin->v[i] = pdaliasframe->bboxmin.v[i];
|
|
pbboxmax->v[i] = pdaliasframe->bboxmax.v[i];
|
|
}
|
|
|
|
pinframe = (dtrivertx_t *)(pdaliasframe + 1);
|
|
pframe = Hunk_AllocName (numv * sizeof(*pframe), loadname);
|
|
|
|
*pframeindex = (qbyte *)pframe - (qbyte *)pheader;
|
|
|
|
for (j=0 ; j<numv ; j++)
|
|
{
|
|
int k;
|
|
|
|
// these are all byte values, so no need to deal with endianness
|
|
pframe[j].lightnormalindex = pinframe[j].lightnormalindex;
|
|
|
|
for (k=0 ; k<3 ; k++)
|
|
{
|
|
pframe[j].v[k] = pinframe[j].v[k];
|
|
}
|
|
}
|
|
|
|
pinframe += numv;
|
|
|
|
return (void *)pinframe;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
Mod_LoadAliasFrame
|
|
=================
|
|
*/
|
|
void * SWMod_LoadAliasFrame (void * pin, int *pframeindex, int numv,
|
|
dtrivertx_t *pbboxmin, dtrivertx_t *pbboxmax, aliashdr_t *pheader, char *name)
|
|
{
|
|
dtrivertx_t *pframe, *pinframe;
|
|
int i, j;
|
|
daliasframe_t *pdaliasframe;
|
|
|
|
pdaliasframe = (daliasframe_t *)pin;
|
|
|
|
strcpy (name, pdaliasframe->name);
|
|
|
|
for (i=0 ; i<3 ; i++)
|
|
{
|
|
// these are byte values, so we don't have to worry about
|
|
// endianness
|
|
pbboxmin->v[i] = pdaliasframe->bboxmin.v[i];
|
|
pbboxmax->v[i] = pdaliasframe->bboxmax.v[i];
|
|
}
|
|
|
|
pinframe = (dtrivertx_t *)(pdaliasframe + 1);
|
|
pframe = Hunk_AllocName (numv * sizeof(*pframe), loadname);
|
|
|
|
*pframeindex = (qbyte *)pframe - (qbyte *)pheader;
|
|
|
|
for (j=0 ; j<numv ; j++)
|
|
{
|
|
int k;
|
|
|
|
// these are all byte values, so no need to deal with endianness
|
|
pframe[j].lightnormalindex = pinframe[j].lightnormalindex;
|
|
|
|
for (k=0 ; k<3 ; k++)
|
|
{
|
|
pframe[j].v[k] = pinframe[j].v[k];
|
|
}
|
|
}
|
|
|
|
pinframe += numv;
|
|
|
|
return (void *)pinframe;
|
|
}
|
|
|
|
|
|
/*
|
|
=================
|
|
Mod_LoadAliasGroup
|
|
=================
|
|
*/
|
|
void * SWMod_LoadAliasGroup (void * pin, int *pframeindex, int numv,
|
|
dtrivertx_t *pbboxmin, dtrivertx_t *pbboxmax, aliashdr_t *pheader, char *name)
|
|
{
|
|
daliasgroup_t *pingroup;
|
|
maliasgroup_t *paliasgroup;
|
|
int i, numframes;
|
|
daliasinterval_t *pin_intervals;
|
|
float *poutintervals;
|
|
void *ptemp;
|
|
|
|
pingroup = (daliasgroup_t *)pin;
|
|
|
|
numframes = LittleLong (pingroup->numframes);
|
|
|
|
paliasgroup = Hunk_AllocName (sizeof (maliasgroup_t) +
|
|
(numframes - 1) * sizeof (paliasgroup->frames[0]), loadname);
|
|
|
|
paliasgroup->numframes = numframes;
|
|
|
|
for (i=0 ; i<3 ; i++)
|
|
{
|
|
// these are byte values, so we don't have to worry about endianness
|
|
pbboxmin->v[i] = pingroup->bboxmin.v[i];
|
|
pbboxmax->v[i] = pingroup->bboxmax.v[i];
|
|
}
|
|
|
|
*pframeindex = (qbyte *)paliasgroup - (qbyte *)pheader;
|
|
|
|
pin_intervals = (daliasinterval_t *)(pingroup + 1);
|
|
|
|
poutintervals = Hunk_AllocName (numframes * sizeof (float), loadname);
|
|
|
|
paliasgroup->intervals = (qbyte *)poutintervals - (qbyte *)pheader;
|
|
|
|
for (i=0 ; i<numframes ; i++)
|
|
{
|
|
*poutintervals = LittleFloat (pin_intervals->interval);
|
|
if (*poutintervals <= 0.0)
|
|
return NULL;
|
|
|
|
poutintervals++;
|
|
pin_intervals++;
|
|
}
|
|
|
|
ptemp = (void *)pin_intervals;
|
|
|
|
for (i=0 ; i<numframes ; i++)
|
|
{
|
|
ptemp = SWMod_LoadAliasFrame (ptemp,
|
|
&paliasgroup->frames[i].frame,
|
|
numv,
|
|
&paliasgroup->frames[i].bboxmin,
|
|
&paliasgroup->frames[i].bboxmax,
|
|
pheader, name);
|
|
if (ptemp == NULL)
|
|
return NULL;
|
|
}
|
|
|
|
return ptemp;
|
|
}
|
|
|
|
|
|
/*
|
|
=================
|
|
Mod_LoadAliasSkin
|
|
=================
|
|
*/
|
|
void * SWMod_LoadAliasSkin (void * pin, int *pskinindex, int skinsize,
|
|
aliashdr_t *pheader)
|
|
{
|
|
int i;
|
|
qbyte *pskin, *pinskin;
|
|
// unsigned short *pusskin;
|
|
unsigned int *p32skin;
|
|
|
|
pskin = Hunk_AllocName (skinsize * r_pixbytes, loadname);
|
|
pinskin = (qbyte *)pin;
|
|
*pskinindex = (qbyte *)pskin - (qbyte *)pheader;
|
|
|
|
if (r_pixbytes == 1 || r_pixbytes == 2)
|
|
{
|
|
Q_memcpy (pskin, pinskin, skinsize);
|
|
}
|
|
else if (r_pixbytes == 4)
|
|
{
|
|
extern qbyte *host_basepal;
|
|
p32skin = (unsigned int *)pskin;
|
|
|
|
for (i=0 ; i<skinsize ; i++)
|
|
p32skin[i] = (255<<24) | (host_basepal[pinskin[i]*3+0]<<16) | (host_basepal[pinskin[i]*3+1]<<8) | (host_basepal[pinskin[i]*3+2]<<0);//d_8to32table[pinskin[i]];
|
|
}
|
|
else
|
|
{
|
|
Sys_Error ("Mod_LoadAliasSkin: driver set invalid r_pixbytes: %d\n",
|
|
r_pixbytes);
|
|
}
|
|
|
|
pinskin += skinsize;
|
|
|
|
return ((void *)pinskin);
|
|
}
|
|
|
|
void * SWMod_LoadAlias2Skin (void * pin, int *pskinindex, int skinsize,
|
|
aliashdr_t *pheader)
|
|
{
|
|
return ((void *)pin);
|
|
}
|
|
|
|
/*
|
|
=================
|
|
Mod_LoadAliasSkinGroup
|
|
=================
|
|
*/
|
|
void * SWMod_LoadAliasSkinGroup (void * pin, int *pskinindex, int skinsize,
|
|
aliashdr_t *pheader)
|
|
{
|
|
daliasskingroup_t *pinskingroup;
|
|
maliasskingroup_t *paliasskingroup;
|
|
int i, numskins;
|
|
daliasskininterval_t *pinskinintervals;
|
|
float *poutskinintervals;
|
|
void *ptemp;
|
|
|
|
pinskingroup = (daliasskingroup_t *)pin;
|
|
|
|
numskins = LittleLong (pinskingroup->numskins);
|
|
|
|
paliasskingroup = Hunk_AllocName (sizeof (maliasskingroup_t) +
|
|
(numskins - 1) * sizeof (paliasskingroup->skindescs[0]),
|
|
loadname);
|
|
|
|
paliasskingroup->numskins = numskins;
|
|
|
|
*pskinindex = (qbyte *)paliasskingroup - (qbyte *)pheader;
|
|
|
|
pinskinintervals = (daliasskininterval_t *)(pinskingroup + 1);
|
|
|
|
poutskinintervals = Hunk_AllocName (numskins * sizeof (float),loadname);
|
|
|
|
paliasskingroup->intervals = (qbyte *)poutskinintervals - (qbyte *)pheader;
|
|
|
|
for (i=0 ; i<numskins ; i++)
|
|
{
|
|
*poutskinintervals = LittleFloat (pinskinintervals->interval);
|
|
if (*poutskinintervals <= 0)
|
|
Sys_Error ("Mod_LoadAliasSkinGroup: interval<=0");
|
|
|
|
poutskinintervals++;
|
|
pinskinintervals++;
|
|
}
|
|
|
|
ptemp = (void *)pinskinintervals;
|
|
|
|
for (i=0 ; i<numskins ; i++)
|
|
{
|
|
ptemp = SWMod_LoadAliasSkin (ptemp,
|
|
&paliasskingroup->skindescs[i].skin, skinsize, pheader);
|
|
}
|
|
|
|
return ptemp;
|
|
}
|
|
|
|
|
|
/*
|
|
=================
|
|
Mod_LoadAliasModel
|
|
=================
|
|
*/
|
|
qboolean SWMod_LoadAliasModel (model_t *mod, void *buffer)
|
|
{
|
|
int i;
|
|
mmdl_t *pmodel;
|
|
dmdl_t *pinmodel;
|
|
mstvert_t *pstverts;
|
|
dstvert_t *pinstverts;
|
|
aliashdr_t *pheader;
|
|
mtriangle_t *ptri;
|
|
dtriangle_t *pintriangles;
|
|
int version, numframes, numskins;
|
|
int size;
|
|
daliasframetype_t *pframetype;
|
|
daliasskintype_t *pskintype;
|
|
maliasskindesc_t *pskindesc;
|
|
int skinsize;
|
|
int start, end, total;
|
|
qboolean qtest = false;
|
|
|
|
start = Hunk_LowMark ();
|
|
|
|
pinmodel = (dmdl_t *)buffer;
|
|
|
|
version = LittleLong (pinmodel->version);
|
|
if (version == QTESTALIAS_VERSION)
|
|
qtest = true;
|
|
else if (version != ALIAS_VERSION)
|
|
{
|
|
Con_Printf (CON_ERROR "%s has wrong version number (%i should be %i)\n",
|
|
mod->name, version, ALIAS_VERSION);
|
|
return false;
|
|
}
|
|
|
|
//
|
|
// 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]) +
|
|
sizeof (mmdl_t) +
|
|
LittleLong (pinmodel->numverts)*2 * sizeof (mstvert_t) +
|
|
LittleLong (pinmodel->numtris) * sizeof (mtriangle_t);
|
|
|
|
pheader = Hunk_AllocName (size, loadname);
|
|
pmodel = (mmdl_t *) ((qbyte *)&pheader[1] +
|
|
(LittleLong (pinmodel->numframes) - 1) *
|
|
sizeof (pheader->frames[0]));
|
|
|
|
// mod->cache.data = pheader;
|
|
if (qtest)
|
|
mod->flags = 0;
|
|
else
|
|
mod->flags = LittleLong (pinmodel->flags);
|
|
|
|
//
|
|
// endian-adjust and copy the data, starting with the alias model header
|
|
//
|
|
pmodel->boundingradius = LittleFloat (pinmodel->boundingradius);
|
|
pmodel->numskins = LittleLong (pinmodel->numskins);
|
|
pmodel->skinwidth = LittleLong (pinmodel->skinwidth);
|
|
pmodel->skinheight = LittleLong (pinmodel->skinheight);
|
|
|
|
if (pmodel->skinheight > MAX_LBM_HEIGHT)
|
|
{
|
|
// TODO: at least downsize the skin
|
|
Con_Printf (CON_ERROR "model %s has a skin taller than %d\n", mod->name,
|
|
MAX_LBM_HEIGHT);
|
|
Hunk_FreeToLowMark(start);
|
|
return false;
|
|
}
|
|
|
|
pmodel->numstverts = pmodel->numverts = LittleLong (pinmodel->numverts);
|
|
|
|
if (pmodel->numverts <= 0)
|
|
{
|
|
Con_Printf (CON_ERROR "model %s has no vertices\n", mod->name);
|
|
Hunk_FreeToLowMark(start);
|
|
return false;
|
|
}
|
|
|
|
if (pmodel->numverts > MAXALIASVERTS)
|
|
{
|
|
Con_Printf (CON_ERROR "model %s has too many vertices\n", mod->name);
|
|
Hunk_FreeToLowMark(start);
|
|
return false;
|
|
}
|
|
|
|
pmodel->numtris = LittleLong (pinmodel->numtris);
|
|
|
|
if (pmodel->numtris <= 0)
|
|
{
|
|
Con_Printf (CON_ERROR "model %s has no triangles\n", mod->name);
|
|
Hunk_FreeToLowMark(start);
|
|
return false;
|
|
}
|
|
|
|
pmodel->numframes = LittleLong (pinmodel->numframes);
|
|
if (qtest)
|
|
pmodel->size = 1.0 * ALIAS_BASE_SIZE_RATIO;
|
|
else
|
|
pmodel->size = LittleFloat (pinmodel->size) * ALIAS_BASE_SIZE_RATIO;
|
|
mod->synctype = LittleLong (pinmodel->synctype);
|
|
mod->numframes = pmodel->numframes;
|
|
|
|
for (i=0 ; i<3 ; i++)
|
|
{
|
|
pmodel->scale[i] = LittleFloat (pinmodel->scale[i]);
|
|
pmodel->scale_origin[i] = LittleFloat (pinmodel->scale_origin[i]);
|
|
pmodel->eyeposition[i] = LittleFloat (pinmodel->eyeposition[i]);
|
|
}
|
|
|
|
numskins = pmodel->numskins;
|
|
numframes = pmodel->numframes;
|
|
|
|
if (pmodel->skinwidth & 0x03)
|
|
{
|
|
Con_Printf (CON_ERROR "Mod_LoadAliasModel: \"%s\" skinwidth not multiple of 4\n", loadmodel->name);
|
|
Hunk_FreeToLowMark(start);
|
|
return false;
|
|
}
|
|
|
|
pheader->model = (qbyte *)pmodel - (qbyte *)pheader;
|
|
|
|
//
|
|
// load the skins
|
|
//
|
|
skinsize = pmodel->skinheight * pmodel->skinwidth;
|
|
|
|
if (numskins < 1)
|
|
{
|
|
Con_Printf (CON_ERROR "Mod_LoadAliasModel: %s, invalid # of skins: %d\n", loadmodel->name, numskins);
|
|
Hunk_FreeToLowMark(start);
|
|
return false;
|
|
}
|
|
|
|
if (qtest)
|
|
pskintype = (daliasskintype_t *)((char *)&pinmodel[1] - sizeof(int)*2);
|
|
else
|
|
pskintype = (daliasskintype_t *)&pinmodel[1];
|
|
|
|
pskindesc = Hunk_AllocName (numskins * sizeof (maliasskindesc_t),
|
|
loadname);
|
|
|
|
pheader->skindesc = (qbyte *)pskindesc - (qbyte *)pheader;
|
|
|
|
for (i=0 ; i<numskins ; i++)
|
|
{
|
|
aliasskintype_t skintype;
|
|
|
|
skintype = LittleLong (pskintype->type);
|
|
pskindesc[i].type = skintype;
|
|
|
|
if (skintype == ALIAS_SKIN_SINGLE)
|
|
{
|
|
pskintype = (daliasskintype_t *)
|
|
SWMod_LoadAliasSkin (pskintype + 1,
|
|
&pskindesc[i].skin,
|
|
skinsize, pheader);
|
|
}
|
|
else
|
|
{
|
|
pskintype = (daliasskintype_t *)
|
|
SWMod_LoadAliasSkinGroup (pskintype + 1,
|
|
&pskindesc[i].skin,
|
|
skinsize, pheader);
|
|
}
|
|
}
|
|
|
|
//
|
|
// set base s and t vertices
|
|
//
|
|
pstverts = (mstvert_t *)&pmodel[1];
|
|
pinstverts = (dstvert_t *)pskintype;
|
|
|
|
pheader->stverts = (qbyte *)pstverts - (qbyte *)pheader;
|
|
|
|
for (i=0 ; i<pmodel->numverts ; i++) //fixme: really, we only need to duplicate the onseem ones.
|
|
{
|
|
// put s and t in 16.16 format
|
|
pstverts[i].s = LittleLong (pinstverts[i].s) << 16;
|
|
pstverts[i].t = LittleLong (pinstverts[i].t) << 16;
|
|
|
|
if (LittleLong (pinstverts[i].onseam))
|
|
pstverts[i+pmodel->numverts].s = pstverts[i].s + ((pmodel->skinwidth>>1) << 16);
|
|
else
|
|
pstverts[i+pmodel->numverts].s = pstverts[i].s; //FIXME: prevent duplication.
|
|
pstverts[i+pmodel->numverts].t = pstverts[i].t;
|
|
}
|
|
|
|
pmodel->numstverts = pmodel->numverts*2;
|
|
|
|
//
|
|
// set up the triangles
|
|
//
|
|
ptri = (mtriangle_t *)&pstverts[pmodel->numstverts];
|
|
pintriangles = (dtriangle_t *)&pinstverts[pmodel->numverts];
|
|
|
|
pheader->triangles = (qbyte *)ptri - (qbyte *)pheader;
|
|
|
|
for (i=0 ; i<pmodel->numtris ; i++)
|
|
{
|
|
int j;
|
|
|
|
for (j=0 ; j<3 ; j++)
|
|
{
|
|
ptri[i].xyz_index[j] =
|
|
LittleLong (pintriangles[i].vertindex[j]);
|
|
if (LittleLong (pintriangles[i].facesfront))
|
|
ptri[i].st_index[j] = ptri[i].xyz_index[j];
|
|
else
|
|
ptri[i].st_index[j] = ptri[i].xyz_index[j]+pmodel->numverts;
|
|
}
|
|
}
|
|
|
|
//
|
|
// load the frames
|
|
//
|
|
if (numframes < 1)
|
|
{
|
|
Con_Printf (CON_ERROR "Mod_LoadAliasModel: %s, invalid # of frames: %d\n", mod->name, numframes);
|
|
Hunk_FreeToLowMark(start);
|
|
return false;
|
|
}
|
|
|
|
pframetype = (daliasframetype_t *)&pintriangles[pmodel->numtris];
|
|
|
|
for (i=0 ; i<numframes ; i++)
|
|
{
|
|
aliasframetype_t frametype;
|
|
|
|
frametype = LittleLong (pframetype->type);
|
|
pheader->frames[i].type = frametype;
|
|
VectorCopy(pmodel->scale_origin, pheader->frames[i].scale_origin);
|
|
VectorCopy(pmodel->scale, pheader->frames[i].scale);
|
|
|
|
if (qtest)
|
|
{
|
|
pframetype = (daliasframetype_t *)
|
|
SWMod_LoadAliasQTestFrame (pframetype + 1,
|
|
&pheader->frames[i].frame,
|
|
pmodel->numverts,
|
|
&pheader->frames[i].bboxmin,
|
|
&pheader->frames[i].bboxmax,
|
|
pheader, pheader->frames[i].name);
|
|
}
|
|
else if (frametype == ALIAS_SINGLE)
|
|
{
|
|
pframetype = (daliasframetype_t *)
|
|
SWMod_LoadAliasFrame (pframetype + 1,
|
|
&pheader->frames[i].frame,
|
|
pmodel->numverts,
|
|
&pheader->frames[i].bboxmin,
|
|
&pheader->frames[i].bboxmax,
|
|
pheader, pheader->frames[i].name);
|
|
}
|
|
else
|
|
{
|
|
pframetype = (daliasframetype_t *)
|
|
SWMod_LoadAliasGroup (pframetype + 1,
|
|
&pheader->frames[i].frame,
|
|
pmodel->numverts,
|
|
&pheader->frames[i].bboxmin,
|
|
&pheader->frames[i].bboxmax,
|
|
pheader, pheader->frames[i].name);
|
|
}
|
|
|
|
if (pframetype == NULL)
|
|
{
|
|
Con_Printf (CON_ERROR "SWMod_LoadAliasModel: %s, couldn't load frame data\n", mod->name);
|
|
Hunk_FreeToLowMark(start);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
mod->type = mod_alias;
|
|
|
|
// FIXME: do this right
|
|
|
|
/*
|
|
mod->mins[0] = mod->mins[1] = mod->mins[2] = -16;
|
|
mod->maxs[0] = mod->maxs[1] = mod->maxs[2] = 16;
|
|
*/
|
|
VectorCopy (pmodel->scale_origin, mod->mins);
|
|
VectorMA (mod->mins, 255, pmodel->scale, mod->maxs);
|
|
|
|
//
|
|
// move the complete, relocatable alias model to the cache
|
|
//
|
|
end = Hunk_LowMark ();
|
|
total = end - start;
|
|
|
|
Hunk_Check();
|
|
|
|
Cache_Alloc (&mod->cache, total, loadname);
|
|
if (!mod->cache.data)
|
|
return false;
|
|
memcpy (mod->cache.data, pheader, total);
|
|
|
|
Hunk_FreeToLowMark (start);
|
|
return true;
|
|
}
|
|
|
|
typedef struct
|
|
{
|
|
float scale[3]; // multiply qbyte verts by this
|
|
float translate[3]; // then add this
|
|
char name[16]; // frame name from grabbing
|
|
dtrivertx_t verts[1]; // variable sized
|
|
} dmd2aliasframe_t;
|
|
|
|
qboolean SWMod_LoadAlias2Model (model_t *mod, void *buffer)
|
|
{
|
|
int i, j;
|
|
mmdl_t *pmodel;
|
|
md2_t *pinmodel;
|
|
mstvert_t *pstverts;
|
|
dmd2stvert_t *pinstverts;
|
|
aliashdr_t *pheader;
|
|
mtriangle_t *ptri;
|
|
dmd2triangle_t *pintriangles;
|
|
int version, numframes, numskins;
|
|
int size;
|
|
dmd2aliasframe_t *pinframe;
|
|
maliasframedesc_t *poutframe;
|
|
maliasskindesc_t *pskindesc;
|
|
int skinsize;
|
|
int start, end, total;
|
|
dtrivertx_t *frameverts;
|
|
|
|
vec3_t mins, maxs;
|
|
|
|
start = Hunk_LowMark ();
|
|
|
|
pinmodel = (md2_t *)buffer;
|
|
|
|
version = LittleLong (pinmodel->version);
|
|
if (version != MD2ALIAS_VERSION)
|
|
{
|
|
Con_Printf (CON_ERROR "%s has wrong version number (%i should be %i)\n",
|
|
mod->name, version, MD2ALIAS_VERSION);
|
|
return false;
|
|
}
|
|
|
|
//
|
|
// allocate space for a working header, plus all the data except the frames,
|
|
// skin and group info
|
|
//
|
|
size = sizeof (aliashdr_t) + (LittleLong (pinmodel->num_frames) - 1) *
|
|
sizeof (pheader->frames[0]) +
|
|
sizeof (mmdl_t) +
|
|
LittleLong (pinmodel->num_st) * sizeof (mstvert_t) +
|
|
LittleLong (pinmodel->num_tris) * sizeof (mtriangle_t);
|
|
|
|
pheader = Hunk_AllocName (size, loadname);
|
|
pmodel = (mmdl_t *) ((qbyte *)&pheader[1] +
|
|
(LittleLong (pinmodel->num_frames) - 1) *
|
|
sizeof (pheader->frames[0]));
|
|
|
|
mod->flags = 0;//LittleLong (pinmodel->flags);
|
|
|
|
//
|
|
// endian-adjust and copy the data, starting with the alias model header
|
|
//
|
|
pmodel->boundingradius = 100;//LittleFloat (pinmodel->boundingradius);
|
|
pmodel->numskins = LittleLong (pinmodel->num_skins);
|
|
pmodel->skinwidth = LittleLong (pinmodel->skinwidth);
|
|
pmodel->skinheight = LittleLong (pinmodel->skinheight);
|
|
|
|
if (pmodel->skinheight > MAX_LBM_HEIGHT)
|
|
{
|
|
Con_Printf (CON_ERROR "model %s has a skin taller than %d\n", mod->name,
|
|
MAX_LBM_HEIGHT);
|
|
Hunk_FreeToLowMark(start);
|
|
return false;
|
|
}
|
|
|
|
pmodel->numverts = LittleLong (pinmodel->num_xyz);
|
|
pmodel->numstverts = LittleLong (pinmodel->num_st);
|
|
|
|
if (pmodel->numverts <= 0)
|
|
{
|
|
Con_Printf (CON_ERROR "model %s has no vertices\n", mod->name);
|
|
Hunk_FreeToLowMark(start);
|
|
return false;
|
|
}
|
|
|
|
if (pmodel->numverts > MAXALIASVERTS)
|
|
{
|
|
Con_Printf (CON_ERROR "model %s has too many vertices\n", mod->name);
|
|
Hunk_FreeToLowMark(start);
|
|
return false;
|
|
}
|
|
|
|
pmodel->numtris = LittleLong (pinmodel->num_tris);
|
|
|
|
if (pmodel->numtris <= 0)
|
|
{
|
|
Con_Printf (CON_ERROR "model %s has no triangles\n", mod->name);
|
|
Hunk_FreeToLowMark(start);
|
|
return false;
|
|
}
|
|
|
|
pmodel->numframes = LittleLong (pinmodel->num_frames);
|
|
pmodel->size = 1000;//LittleFloat (pinmodel->size) * ALIAS_BASE_SIZE_RATIO;
|
|
mod->synctype = 1;//LittleLong (pinmodel->synctype);
|
|
mod->numframes = pmodel->numframes;
|
|
|
|
for (i=0 ; i<3 ; i++)
|
|
{
|
|
pmodel->scale[i] = 0;
|
|
pmodel->scale_origin[i] = 0;
|
|
pmodel->eyeposition[i] = 0;
|
|
}
|
|
|
|
numskins = pmodel->numskins;
|
|
numframes = pmodel->numframes;
|
|
|
|
if (pmodel->skinwidth & 0x03)
|
|
{
|
|
Con_Printf (CON_ERROR "Mod_LoadAliasModel: %s, skinwidth not multiple of 4\n", mod->name);
|
|
Hunk_FreeToLowMark(start);
|
|
return false;
|
|
}
|
|
|
|
pheader->model = (qbyte *)pmodel - (qbyte *)pheader;
|
|
|
|
//
|
|
// set base s and t vertices
|
|
//
|
|
pstverts = (mstvert_t *)&pmodel[1];
|
|
pinstverts = (dmd2stvert_t *)((qbyte *)pinmodel + LittleLong(pinmodel->ofs_st));
|
|
|
|
pheader->stverts = (qbyte *)pstverts - (qbyte *)pheader;
|
|
|
|
for (i=0 ; i<pmodel->numstverts ; i++)
|
|
{
|
|
// put s and t in 16.16 format
|
|
pstverts[i].s = LittleShort (pinstverts[i].s)<<16;
|
|
pstverts[i].t = LittleShort (pinstverts[i].t)<<16;
|
|
}
|
|
|
|
//
|
|
// set up the triangles
|
|
//
|
|
ptri = (mtriangle_t *)&pstverts[pmodel->numstverts];
|
|
pintriangles = (dmd2triangle_t *)((qbyte *)pinmodel + LittleLong(pinmodel->ofs_tris));
|
|
|
|
pheader->triangles = (qbyte *)ptri - (qbyte *)pheader;
|
|
|
|
for (i=0 ; i<pmodel->numtris ; i++)
|
|
{
|
|
int j;
|
|
|
|
for (j=0 ; j<3 ; j++)
|
|
{
|
|
ptri[i].xyz_index[j] = LittleShort (pintriangles[i].xyz_index[j]);
|
|
ptri[i].st_index[j] = LittleShort (pintriangles[i].st_index[j]);
|
|
}
|
|
}
|
|
|
|
//
|
|
// load the frames
|
|
//
|
|
if (numframes < 1)
|
|
{
|
|
Con_Printf (CON_ERROR "Mod_LoadAliasModel: %s, Invalid # of frames: %d\n", mod->name, numframes);
|
|
Hunk_FreeToLowMark(start);
|
|
return false;
|
|
}
|
|
|
|
for (i=0 ; i<numframes ; i++)
|
|
{
|
|
pinframe = (dmd2aliasframe_t *) ((qbyte *)pinmodel
|
|
+ LittleLong(pinmodel->ofs_frames) + i * LittleLong(pinmodel->framesize));
|
|
poutframe = &pheader->frames[i];
|
|
|
|
memcpy (poutframe->name, pinframe->name, sizeof(poutframe->name));
|
|
|
|
for (j=0 ; j<3 ; j++)
|
|
{
|
|
poutframe->scale[j] = LittleFloat (pinframe->scale[j]);
|
|
poutframe->scale_origin[j] = LittleFloat (pinframe->translate[j]);
|
|
}
|
|
VectorCopy (poutframe->scale_origin, mins); //work out precise size.
|
|
VectorMA (mins, 255, poutframe->scale, maxs);
|
|
poutframe->bboxmin.v[0] = poutframe->bboxmin.v[1] = poutframe->bboxmin.v[2] = 0;
|
|
poutframe->bboxmax.v[0] = poutframe->bboxmax.v[1] = poutframe->bboxmax.v[2] = 255;
|
|
|
|
if (i == 0)
|
|
{
|
|
VectorCopy (mins, mod->mins); //first frame - nothing to compare against.
|
|
VectorCopy (maxs, mod->maxs);
|
|
}
|
|
else
|
|
{
|
|
for (j = 0; j < 3; j++)
|
|
{
|
|
if (mod->mins[j] > mins[j]) //and make sure that the biggest ends up as the model size.
|
|
mod->mins[j] = mins[j];
|
|
if (mod->maxs[j] < maxs[j])
|
|
mod->maxs[j] = maxs[j];
|
|
}
|
|
}
|
|
|
|
// verts are all 8 bit, so no swapping needed
|
|
frameverts = Hunk_AllocName(pmodel->numverts*sizeof(dtrivertx_t), loadname);
|
|
poutframe->frame = (qbyte *)frameverts - (qbyte *)pheader;
|
|
for (j = 0; j < pmodel->numverts; j++)
|
|
{
|
|
frameverts[j].lightnormalindex = pinframe->verts[j].lightnormalindex;
|
|
frameverts[j].v[0] = pinframe->verts[j].v[0];
|
|
frameverts[j].v[1] = pinframe->verts[j].v[1];
|
|
frameverts[j].v[2] = pinframe->verts[j].v[2];
|
|
}
|
|
|
|
}
|
|
|
|
VectorCopy (mod->mins, pmodel->scale_origin); //work out global scale
|
|
pmodel->scale[0] = (mod->maxs[0] - mod->mins[0])/255;
|
|
pmodel->scale[1] = (mod->maxs[1] - mod->mins[1])/255;
|
|
pmodel->scale[2] = (mod->maxs[2] - mod->mins[2])/255;
|
|
|
|
{
|
|
int width;
|
|
int height;
|
|
int j;
|
|
qbyte *buffer;
|
|
qbyte *texture;
|
|
char *skinnames;
|
|
qbyte *skin;
|
|
pmodel->numskins = 0;
|
|
skinnames = Hunk_AllocName(numskins*MAX_SKINNAME, loadname);
|
|
memcpy(skinnames, (qbyte *)pinmodel + LittleLong(pinmodel->ofs_skins), numskins*MAX_SKINNAME);
|
|
|
|
skinsize = pmodel->skinheight * pmodel->skinwidth;
|
|
pskindesc = Hunk_AllocName (numskins * sizeof (maliasskindesc_t),
|
|
loadname);
|
|
pheader->skindesc = (qbyte *)pskindesc - (qbyte *)pheader;
|
|
|
|
for (i=0 ; i<numskins ; i++, skinnames+=MAX_SKINNAME)
|
|
{
|
|
buffer = COM_LoadTempFile(skinnames);
|
|
if (!buffer)
|
|
{
|
|
Con_Printf(CON_WARNING "Skin %s not found\n", skinnames);
|
|
continue;
|
|
}
|
|
texture = ReadPCXFile(buffer, com_filesize, &width, &height);
|
|
// BZ_Free(buffer);
|
|
if (!texture)
|
|
{
|
|
Con_Printf(CON_WARNING "Skin %s not a pcx\n", skinnames);
|
|
continue;
|
|
}
|
|
if (width != pmodel->skinwidth || height != pmodel->skinheight) //FIXME: scale
|
|
{
|
|
BZ_Free(texture);
|
|
Con_Printf(CON_WARNING "Skin %s not same size as model specifies it should be\n", skinnames);
|
|
continue;
|
|
}
|
|
|
|
skin = Hunk_AllocName(skinsize*r_pixbytes, loadname);
|
|
if (r_pixbytes == 4)
|
|
{
|
|
for (j = 0; j < skinsize*4; j+=4)
|
|
{
|
|
skin[j+0] = texture[j+2];
|
|
skin[j+1] = texture[j+1];
|
|
skin[j+2] = texture[j+0];
|
|
skin[j+3] = texture[j+3];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (j = 0; j < skinsize; j++) //you know when you've been palettized.
|
|
{
|
|
skin[j+0] = GetPaletteNoFB(texture[j*4+0], texture[j*4+1], texture[j*4+2]);
|
|
}
|
|
}
|
|
|
|
BZ_Free(texture);
|
|
|
|
pskindesc[pmodel->numskins].type = ALIAS_SKIN_SINGLE;
|
|
pskindesc[pmodel->numskins].skin = (qbyte *)skin - (qbyte *)pheader;
|
|
pmodel->numskins++;
|
|
}
|
|
}
|
|
|
|
mod->type = mod_alias;
|
|
|
|
//
|
|
// 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 false;
|
|
memcpy (mod->cache.data, pheader, total);
|
|
|
|
Hunk_FreeToLowMark (start);
|
|
return true;
|
|
}
|
|
|
|
|
|
//structures from Tenebrae
|
|
typedef struct {
|
|
int ident;
|
|
int version;
|
|
|
|
char name[MAX_QPATH];
|
|
|
|
int flags; //Does anyone know what these are?
|
|
|
|
int numFrames;
|
|
int numTags;
|
|
int numSurfaces;
|
|
|
|
int numSkins;
|
|
|
|
int ofsFrames;
|
|
int ofsTags;
|
|
int ofsSurfaces;
|
|
int ofsEnd;
|
|
} md3Header_t;
|
|
|
|
//then has header->numFrames of these at header->ofs_Frames
|
|
typedef struct md3Frame_s {
|
|
vec3_t bounds[2];
|
|
vec3_t localOrigin;
|
|
float radius;
|
|
char name[16];
|
|
} md3Frame_t;
|
|
|
|
//there are header->numSurfaces of these at header->ofsSurfaces, following from ofsEnd
|
|
typedef struct {
|
|
int ident; //
|
|
|
|
char name[MAX_QPATH]; // polyset name
|
|
|
|
int flags;
|
|
int numFrames; // all surfaces in a model should have the same
|
|
|
|
int numShaders; // all surfaces in a model should have the same
|
|
int numVerts;
|
|
|
|
int numTriangles;
|
|
int ofsTriangles;
|
|
|
|
int ofsShaders; // offset from start of md3Surface_t
|
|
int ofsSt; // texture coords are common for all frames
|
|
int ofsXyzNormals; // numVerts * numFrames
|
|
|
|
int ofsEnd; // next surface follows
|
|
} md3Surface_t;
|
|
|
|
//at surf+surf->ofsXyzNormals
|
|
typedef struct {
|
|
short xyz[3];
|
|
unsigned short normal;
|
|
} md3XyzNormal_t;
|
|
|
|
//surf->numTriangles at surf+surf->ofsTriangles
|
|
typedef struct {
|
|
int indexes[3];
|
|
} md3Triangle_t;
|
|
|
|
//surf->numVerts at surf+surf->ofsSt
|
|
typedef struct {
|
|
float s;
|
|
float t;
|
|
} md3St_t;
|
|
|
|
typedef struct {
|
|
char name[MAX_QPATH];
|
|
int shaderIndex;
|
|
} md3Shader_t;
|
|
//End of Tenebrae 'assistance'
|
|
|
|
typedef struct {
|
|
char name[MAX_QPATH];
|
|
vec3_t org;
|
|
float ang[3][3];
|
|
} md3tag_t;
|
|
|
|
qbyte *LoadTextureFile(char *texturename)
|
|
{
|
|
qbyte *tex;
|
|
if ((tex = COM_LoadMallocFile(texturename)))
|
|
return tex;
|
|
if ((tex = COM_LoadMallocFile(va("textures/%s.tga", texturename))))
|
|
return tex;
|
|
if ((tex = COM_LoadMallocFile(va("textures/%s.jpg", texturename))))
|
|
return tex;
|
|
if ((tex = COM_LoadMallocFile(va("%s.tga", texturename))))
|
|
return tex;
|
|
if ((tex = COM_LoadMallocFile(va("%s.jpg", texturename))))
|
|
return tex;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
qboolean SWMod_LoadAlias3Model (model_t *mod, void *buffer)
|
|
{
|
|
int i, j;
|
|
mmdl_t *pmodel;
|
|
aliashdr_t *pheader;
|
|
|
|
md3Header_t *pinmodel;
|
|
mstvert_t *pstverts;
|
|
md3St_t *pinstverts;
|
|
mtriangle_t *ptri;
|
|
md3Triangle_t *pintriangles;
|
|
int version, numframes, numskins;
|
|
int size;
|
|
md3Frame_t *pinframe;
|
|
maliasframedesc_t *poutframe;
|
|
maliasskindesc_t *pskindesc;
|
|
int skinsize;
|
|
int start, end, total;
|
|
dtrivertx_t *frameverts;
|
|
md3XyzNormal_t *pinverts;
|
|
|
|
md3Surface_t *surface;
|
|
|
|
vec3_t mins, maxs;
|
|
|
|
if (loadmodel->engineflags & MDLF_DOCRC)
|
|
{
|
|
unsigned short crc;
|
|
qbyte *p;
|
|
int len;
|
|
char st[40];
|
|
|
|
QCRC_Init(&crc);
|
|
for (len = com_filesize, p = buffer; len; len--, p++)
|
|
QCRC_ProcessByte(&crc, *p);
|
|
|
|
sprintf(st, "%d", (int) crc);
|
|
Info_SetValueForKey (cls.userinfo,
|
|
(loadmodel->engineflags & MDLF_PLAYER) ? pmodel_name : emodel_name,
|
|
st, MAX_INFO_STRING);
|
|
|
|
if (cls.state >= ca_connected)
|
|
{
|
|
CL_SendClientCommand(true, "setinfo %s %d",
|
|
(loadmodel->engineflags & MDLF_PLAYER) ? pmodel_name : emodel_name,
|
|
(int)crc);
|
|
}
|
|
}
|
|
|
|
start = Hunk_LowMark ();
|
|
|
|
pinmodel = (md3Header_t *)buffer;
|
|
|
|
version = LittleLong (pinmodel->version);
|
|
// if (version != MD3ALIAS_VERSION)
|
|
// Sys_Error ("%s has wrong version number (%i should be %i)",
|
|
// mod->name, version, MD3ALIAS_VERSION);
|
|
|
|
surface = (md3Surface_t*)((qbyte *)pinmodel + pinmodel->ofsSurfaces);
|
|
|
|
//
|
|
// 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]) +
|
|
sizeof (mmdl_t) +
|
|
LittleLong (surface->numVerts) * sizeof (mstvert_t) +
|
|
LittleLong (surface->numTriangles) * sizeof (mtriangle_t);
|
|
|
|
pheader = Hunk_AllocName (size, loadname);
|
|
pmodel = (mmdl_t *) ((qbyte *)&pheader[1] +
|
|
(LittleLong (surface->numFrames) - 1) *
|
|
sizeof (pheader->frames[0]));
|
|
|
|
mod->flags = 0;//LittleLong (pinmodel->flags);
|
|
|
|
//
|
|
// endian-adjust and copy the data, starting with the alias model header
|
|
//
|
|
pmodel->boundingradius = 100;//LittleFloat (pinmodel->boundingradius);
|
|
pmodel->numskins = LittleLong (surface->numShaders);
|
|
// pmodel->skinwidth = LittleLong (pinmodel->skinwidth); //fill in later.
|
|
// pmodel->skinheight = LittleLong (pinmodel->skinheight);
|
|
|
|
if (pmodel->skinheight > MAX_LBM_HEIGHT)
|
|
{
|
|
Con_Printf (CON_ERROR "model %s has a skin taller than %d\n", mod->name,
|
|
MAX_LBM_HEIGHT);
|
|
Hunk_FreeToLowMark(start);
|
|
return false;
|
|
}
|
|
|
|
pmodel->numverts = LittleLong (surface->numVerts);
|
|
pmodel->numstverts = LittleLong (surface->numVerts);
|
|
|
|
if (surface->numVerts <= 0)
|
|
{
|
|
Con_Printf (CON_ERROR "model %s has no vertices\n", mod->name);
|
|
Hunk_FreeToLowMark(start);
|
|
return false;
|
|
}
|
|
|
|
if (pmodel->numverts > MAXALIASVERTS)
|
|
{
|
|
Con_Printf (CON_ERROR "model %s has too many vertices\n", mod->name);
|
|
Hunk_FreeToLowMark(start);
|
|
return false;
|
|
}
|
|
|
|
pmodel->numtris = LittleLong (surface->numTriangles);
|
|
|
|
if (pmodel->numtris <= 0)
|
|
{
|
|
Con_Printf (CON_ERROR "model %s has no triangles\n", mod->name);
|
|
Hunk_FreeToLowMark(start);
|
|
return false;
|
|
}
|
|
|
|
pmodel->numframes = LittleLong (surface->numFrames);
|
|
pmodel->size = 1000;//LittleFloat (pinmodel->size) * ALIAS_BASE_SIZE_RATIO;
|
|
mod->synctype = 1;//LittleLong (pinmodel->synctype);
|
|
mod->numframes = pmodel->numframes;
|
|
|
|
for (i=0 ; i<3 ; i++)
|
|
{
|
|
pmodel->scale[i] = 0;
|
|
pmodel->scale_origin[i] = 0;
|
|
pmodel->eyeposition[i] = 0;
|
|
}
|
|
|
|
numskins = pmodel->numskins;
|
|
numframes = pmodel->numframes;
|
|
|
|
|
|
pheader->model = (qbyte *)pmodel - (qbyte *)pheader;
|
|
|
|
{
|
|
int width;
|
|
int height;
|
|
int j;
|
|
qbyte *buffer;
|
|
qbyte *texture;
|
|
md3Shader_t *pinskin;
|
|
qbyte *skin;
|
|
pmodel->numskins = 0;
|
|
|
|
pinskin = (md3Shader_t *)((qbyte *)surface + surface->ofsShaders);
|
|
|
|
skinsize = pmodel->skinheight * pmodel->skinwidth;
|
|
pskindesc = Hunk_AllocName (numskins * sizeof (maliasskindesc_t),
|
|
loadname);
|
|
pheader->skindesc = (qbyte *)pskindesc - (qbyte *)pheader;
|
|
|
|
for (i=0 ; i<numskins ; i++, pinskin++)
|
|
{
|
|
buffer = LoadTextureFile(pinskin->name);
|
|
if (!buffer)
|
|
{
|
|
char altname[256];
|
|
strcpy(altname, mod->name); //backup
|
|
strcpy(COM_SkipPath(altname), COM_SkipPath(pinskin->name));
|
|
buffer = LoadTextureFile(altname);
|
|
}
|
|
|
|
if (!buffer)
|
|
{
|
|
Con_Printf(CON_WARNING "Skin %s not found\n", pinskin->name);
|
|
continue;
|
|
}
|
|
texture = ReadTargaFile(buffer, com_filesize, &width, &height, false);
|
|
#ifdef AVAIL_JPEGLIB
|
|
if (!texture)
|
|
texture = ReadJPEGFile(buffer, com_filesize, &width, &height);
|
|
#endif
|
|
#ifdef AVAIL_PNGLIB
|
|
if (!texture)
|
|
texture = ReadPNGFile(buffer, com_filesize, &width, &height, pinskin->name);
|
|
#endif
|
|
if (!texture)
|
|
texture = ReadPCXFile(buffer, com_filesize, &width, &height);
|
|
BZ_Free(buffer);
|
|
if (!texture)
|
|
{
|
|
Con_Printf(CON_WARNING "Skin %s filetype not recognised\n", pinskin->name);
|
|
continue;
|
|
}
|
|
if (!pmodel->numskins) //this is the first skin.
|
|
{
|
|
pmodel->skinwidth = width;
|
|
pmodel->skinheight = height;
|
|
}
|
|
skinsize = width*height;
|
|
if (width != pmodel->skinwidth || height != pmodel->skinheight) //FIXME: scale
|
|
{
|
|
BZ_Free(texture);
|
|
Con_Printf(CON_WARNING "Skin %s not same size as model specifies it should be\n", pinskin->name);
|
|
continue;
|
|
}
|
|
|
|
skin = Hunk_AllocName(skinsize*r_pixbytes, loadname);
|
|
if (r_pixbytes == 4)
|
|
{
|
|
for (j = 0; j < skinsize*4; j+=4)
|
|
{
|
|
skin[j+0] = texture[j+2];
|
|
skin[j+1] = texture[j+1];
|
|
skin[j+2] = texture[j+0];
|
|
skin[j+3] = texture[j+3];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (j = 0; j < skinsize; j++) //you know when you've been palettized.
|
|
{
|
|
skin[j+0] = GetPaletteNoFB(texture[j*4+0], texture[j*4+1], texture[j*4+2]);
|
|
}
|
|
}
|
|
|
|
BZ_Free(texture);
|
|
|
|
pskindesc[pmodel->numskins].type = ALIAS_SKIN_SINGLE;
|
|
pskindesc[pmodel->numskins].skin = (qbyte *)skin - (qbyte *)pheader;
|
|
pmodel->numskins++;
|
|
}
|
|
}
|
|
|
|
if (!pmodel->numskins)
|
|
Con_Printf(CON_WARNING "model %s has no skins\n", loadmodel->name);
|
|
|
|
if (pmodel->skinwidth & 0x03)
|
|
{
|
|
Con_Printf (CON_ERROR "Mod_LoadAliasModel: %s, skinwidth not multiple of 4\n", mod->name);
|
|
Hunk_FreeToLowMark(start);
|
|
return false;
|
|
}
|
|
|
|
//
|
|
// set base s and t vertices
|
|
//
|
|
pstverts = (mstvert_t *)&pmodel[1];
|
|
pinstverts = (md3St_t *)((qbyte *)surface + LittleLong(surface->ofsSt));
|
|
|
|
pheader->stverts = (qbyte *)pstverts - (qbyte *)pheader;
|
|
|
|
for (i=0 ; i<pmodel->numstverts ; i++)
|
|
{
|
|
// put s and t in 16.16 format
|
|
pstverts[i].s = (int)(LittleFloat (pinstverts[i].s)*pmodel->skinwidth)<<16;
|
|
pstverts[i].t = (int)(LittleFloat (pinstverts[i].t)*pmodel->skinheight)<<16;
|
|
}
|
|
|
|
//
|
|
// set up the triangles
|
|
//
|
|
ptri = (mtriangle_t *)&pstverts[pmodel->numstverts];
|
|
pintriangles = (md3Triangle_t *)((qbyte *)surface + LittleLong(surface->ofsTriangles));
|
|
|
|
pheader->triangles = (qbyte *)ptri - (qbyte *)pheader;
|
|
|
|
for (i=0 ; i<pmodel->numtris ; i++)
|
|
{
|
|
int j;
|
|
|
|
for (j=0 ; j<3 ; j++)
|
|
{
|
|
ptri[i].st_index[j] = ptri[i].xyz_index[j] = LittleLong (pintriangles[i].indexes[j]);
|
|
}
|
|
}
|
|
|
|
//
|
|
// load the frames
|
|
//
|
|
if (numframes < 1)
|
|
{
|
|
Con_Printf (CON_ERROR "Mod_LoadAliasModel: %s, Invalid # of frames: %d\n", mod->name, numframes);
|
|
Hunk_FreeToLowMark(start);
|
|
return false;
|
|
}
|
|
|
|
pinverts = (md3XyzNormal_t *)((qbyte *)surface + surface->ofsXyzNormals);
|
|
for (i=0 ; i<numframes ; i++)
|
|
{
|
|
pinframe = (md3Frame_t *) ((qbyte *)pinmodel
|
|
+ LittleLong(pinmodel->ofsFrames) + i * sizeof(md3Frame_t));
|
|
poutframe = &pheader->frames[i];
|
|
|
|
memcpy (poutframe->name, pinframe->name, sizeof(poutframe->name));
|
|
|
|
for (j=0 ; j<3 ; j++)
|
|
{
|
|
poutframe->scale_origin[j] = LittleFloat (pinframe->bounds[0][j]);
|
|
poutframe->scale[j] = (LittleFloat (pinframe->bounds[1][j])-poutframe->scale_origin[j])/255;
|
|
}
|
|
VectorCopy (poutframe->scale_origin, mins); //work out precise size.
|
|
VectorMA (mins, 255, poutframe->scale, maxs);
|
|
poutframe->bboxmin.v[0] = poutframe->bboxmin.v[1] = poutframe->bboxmin.v[2] = 0;
|
|
poutframe->bboxmax.v[0] = poutframe->bboxmax.v[1] = poutframe->bboxmax.v[2] = 255;
|
|
|
|
if (i == 0)
|
|
{
|
|
VectorCopy (mins, mod->mins); //first frame - nothing to compare against.
|
|
VectorCopy (maxs, mod->maxs);
|
|
}
|
|
else
|
|
{
|
|
for (j = 0; j < 3; j++)
|
|
{
|
|
if (mod->mins[j] > mins[j]) //and make sure that the biggest ends up as the model size.
|
|
mod->mins[j] = mins[j];
|
|
if (mod->maxs[j] < maxs[j])
|
|
mod->maxs[j] = maxs[j];
|
|
}
|
|
}
|
|
|
|
// verts are all 8 bit, so no swapping needed
|
|
frameverts = Hunk_AllocName(pmodel->numverts*sizeof(dtrivertx_t), loadname);
|
|
poutframe->frame = (qbyte *)frameverts - (qbyte *)pheader;
|
|
for (j = 0; j < pmodel->numverts; j++)
|
|
{
|
|
frameverts[j].lightnormalindex = LittleShort(pinverts->normal)/256;
|
|
|
|
//scale down to 0 - 255 within the scaleorigin and scaleorigin+scale*255
|
|
// i/64 = scaleorigin+scale*o
|
|
// i/64-scaleorigin = scale*o
|
|
// i/64-scaleorigin/scale = 0
|
|
frameverts[j].v[0] = ((double)LittleShort(pinverts[j].xyz[0])/64.0-poutframe->scale_origin[0])/(poutframe->scale[0]);
|
|
frameverts[j].v[1] = ((double)LittleShort(pinverts[j].xyz[1])/64.0-poutframe->scale_origin[1])/(poutframe->scale[1]);
|
|
frameverts[j].v[2] = ((double)LittleShort(pinverts[j].xyz[2])/64.0-poutframe->scale_origin[2])/(poutframe->scale[2]);
|
|
}
|
|
pinverts += pmodel->numverts;
|
|
|
|
}
|
|
|
|
VectorCopy (mod->mins, pmodel->scale_origin); //work out global scale
|
|
pmodel->scale[0] = (mod->maxs[0] - mod->mins[0])/255;
|
|
pmodel->scale[1] = (mod->maxs[1] - mod->mins[1])/255;
|
|
pmodel->scale[2] = (mod->maxs[2] - mod->mins[2])/255;
|
|
|
|
mod->type = mod_alias;
|
|
|
|
//
|
|
// 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 false;
|
|
memcpy (mod->cache.data, pheader, total);
|
|
|
|
Hunk_FreeToLowMark (start);
|
|
#ifdef RGLQUAKE
|
|
mod->flags = Mod_ReadFlagsFromMD1(mod->name, 0);
|
|
#endif
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
//=============================================================================
|
|
|
|
/*
|
|
=================
|
|
Mod_LoadSpriteFrame
|
|
=================
|
|
*/
|
|
void * SWMod_LoadSpriteFrame (void * pin, mspriteframe_t **ppframe, int version)
|
|
{
|
|
dspriteframe_t *pinframe;
|
|
mspriteframe_t *pspriteframe;
|
|
int i, width, height, size, origin[2];
|
|
|
|
pinframe = (dspriteframe_t *)pin;
|
|
|
|
width = LittleLong (pinframe->width);
|
|
height = LittleLong (pinframe->height);
|
|
size = width * height;
|
|
|
|
pspriteframe = Hunk_AllocName (sizeof (mspriteframe_t) + size*r_pixbytes,
|
|
loadname);
|
|
|
|
Q_memset (pspriteframe, 0, sizeof (mspriteframe_t) + size);
|
|
*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];
|
|
|
|
if (r_pixbytes == 1)
|
|
{
|
|
qbyte *ppixout, *ppixin;
|
|
|
|
if (version == SPRITE32_VERSION)
|
|
{ //downgrade quality
|
|
|
|
ppixin = (unsigned char *)(pinframe + 1);
|
|
ppixout = (unsigned char *)&pspriteframe->pixels[0];
|
|
|
|
for (i=0 ; i<size ; i++)
|
|
{
|
|
if (ppixin[i*4+3] < 128)
|
|
ppixout[i] = 255; //transparent.
|
|
else
|
|
ppixout[i] = GetPaletteNoFB(ppixin[i*4], ppixin[i*4+1], ppixin[i*4+2]);
|
|
}
|
|
size *= 4;
|
|
}
|
|
else
|
|
Q_memcpy (&pspriteframe->pixels[0], (qbyte *)(pinframe + 1), size);
|
|
}
|
|
else if (r_pixbytes == 2)
|
|
{
|
|
qbyte *ppixin;
|
|
unsigned short *p16out;
|
|
if (version == SPRITE32_VERSION)
|
|
{ //downgrade quality
|
|
|
|
ppixin = (unsigned char *)(pinframe + 1);
|
|
p16out = (unsigned short *)&pspriteframe->pixels[0];
|
|
|
|
for (i=0 ; i<size ; i++)
|
|
{
|
|
if (ppixin[i*4+3] < 128)
|
|
p16out[i] = 0xffff; //transparent.
|
|
else
|
|
p16out[i] = ((ppixin[i*4]*32/255)<<10) + ((ppixin[i*4+1]*32/255)<<5) + ((ppixin[i*4+2]*32/255)<<0);
|
|
}
|
|
size *= 4;
|
|
}
|
|
else
|
|
{
|
|
ppixin = (unsigned char *)(pinframe + 1);
|
|
p16out = (unsigned short *)&pspriteframe->pixels[0];
|
|
for (i=0 ; i<size ; i++)
|
|
{
|
|
if (ppixin[i] == 255)
|
|
p16out[i] = 0xffff; //transparent.
|
|
else
|
|
p16out[i] = d_8to16table[ppixin[i]];
|
|
}
|
|
}
|
|
}
|
|
else if (r_pixbytes == 4)
|
|
{
|
|
unsigned int *p32out;
|
|
if (version == SPRITE32_VERSION)
|
|
{ //copy accross
|
|
unsigned int *p32in;
|
|
p32in = (unsigned int *)(pinframe + 1);
|
|
p32out = (unsigned int *)&pspriteframe->pixels[0];
|
|
|
|
for (i=0 ; i<size ; i++)
|
|
p32out[i] = p32in[i];
|
|
|
|
size *= 4;
|
|
}
|
|
else
|
|
{ //upgrade
|
|
qbyte *ppixin;
|
|
ppixin = (qbyte *)(pinframe + 1);
|
|
p32out = (unsigned int *)&pspriteframe->pixels[0];
|
|
|
|
for (i=0 ; i<size ; i++)
|
|
p32out[i] = d_8to32table[ppixin[i]];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Sys_Error ("Mod_LoadSpriteFrame: driver set invalid r_pixbytes: %d\n",
|
|
r_pixbytes);
|
|
}
|
|
|
|
return (void *)((qbyte *)pinframe + sizeof (dspriteframe_t) + size);
|
|
}
|
|
|
|
|
|
/*
|
|
=================
|
|
Mod_LoadSpriteGroup
|
|
=================
|
|
*/
|
|
void * SWMod_LoadSpriteGroup (void * pin, mspriteframe_t **ppframe, int version)
|
|
{
|
|
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 = 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 = Hunk_AllocName (numframes * sizeof (float), loadname);
|
|
|
|
pspritegroup->intervals = poutintervals;
|
|
|
|
for (i=0 ; i<numframes ; i++)
|
|
{
|
|
*poutintervals = LittleFloat (pin_intervals->interval);
|
|
if (*poutintervals <= 0.0)
|
|
{
|
|
Con_Printf (CON_ERROR "Mod_LoadSpriteGroup: interval<=0\n");
|
|
return NULL;
|
|
}
|
|
|
|
poutintervals++;
|
|
pin_intervals++;
|
|
}
|
|
|
|
ptemp = (void *)pin_intervals;
|
|
|
|
for (i=0 ; i<numframes ; i++)
|
|
{
|
|
ptemp = SWMod_LoadSpriteFrame (ptemp, &pspritegroup->frames[i], version);
|
|
}
|
|
|
|
return ptemp;
|
|
}
|
|
|
|
|
|
/*
|
|
=================
|
|
Mod_LoadSpriteModel
|
|
=================
|
|
*/
|
|
qboolean SWMod_LoadSpriteModel (model_t *mod, void *buffer)
|
|
{
|
|
int i;
|
|
int version;
|
|
dsprite_t *pin;
|
|
msprite_t *psprite;
|
|
int numframes;
|
|
int size;
|
|
dspriteframetype_t *pframetype;
|
|
int hunkstart;
|
|
|
|
hunkstart = Hunk_LowMark();
|
|
|
|
pin = (dsprite_t *)buffer;
|
|
|
|
version = LittleLong (pin->version);
|
|
if (version != SPRITE32_VERSION)
|
|
if (version != SPRITE_VERSION)
|
|
{
|
|
Con_Printf (CON_ERROR "%s has wrong version number "
|
|
"(%i should be %i)\n", mod->name, version, SPRITE_VERSION);
|
|
return false;
|
|
}
|
|
|
|
numframes = LittleLong (pin->numframes);
|
|
|
|
size = sizeof (msprite_t) + (numframes - 1) * sizeof (psprite->frames);
|
|
|
|
psprite = 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 = 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)
|
|
{
|
|
Con_Printf (CON_ERROR "Mod_LoadSpriteModel: Invalid # of frames: %d\n", numframes);
|
|
return false;
|
|
}
|
|
|
|
mod->numframes = numframes;
|
|
|
|
pframetype = (dspriteframetype_t *)(pin + 1);
|
|
|
|
for (i=0 ; i<numframes ; i++)
|
|
{
|
|
spriteframetype_t frametype;
|
|
|
|
frametype = LittleLong (pframetype->type);
|
|
psprite->frames[i].type = frametype;
|
|
|
|
if (frametype == SPR_SINGLE)
|
|
{
|
|
pframetype = (dspriteframetype_t *)
|
|
SWMod_LoadSpriteFrame (pframetype + 1,
|
|
&psprite->frames[i].frameptr, version);
|
|
}
|
|
else
|
|
{
|
|
pframetype = (dspriteframetype_t *)
|
|
SWMod_LoadSpriteGroup (pframetype + 1,
|
|
&psprite->frames[i].frameptr, version);
|
|
}
|
|
if (pframetype == NULL)
|
|
{
|
|
Hunk_FreeToLowMark(hunkstart);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
mod->type = mod_sprite;
|
|
return true;
|
|
}
|
|
|
|
qboolean SWMod_LoadSprite2Model (model_t *mod, void *buffer)
|
|
{
|
|
int i, j;
|
|
int version;
|
|
dmd2sprite_t *pin;
|
|
msprite_t *psprite;
|
|
int numframes;
|
|
int size;
|
|
dmd2sprframe_t *pframetype;
|
|
mspriteframe_t *frame;
|
|
float origin[2];
|
|
int width;
|
|
int height;
|
|
qbyte *framefile;
|
|
qbyte *framedata;
|
|
int hunkstart;
|
|
|
|
hunkstart = Hunk_LowMark();
|
|
|
|
pin = (dmd2sprite_t *)buffer;
|
|
|
|
version = LittleLong (pin->version);
|
|
if (version != SPRITE2_VERSION)
|
|
{
|
|
Con_Printf ("%s has wrong version number "
|
|
"(%i should be %i)\n", mod->name, version, SPRITE2_VERSION);
|
|
return false;
|
|
}
|
|
|
|
numframes = LittleLong (pin->numframes);
|
|
|
|
size = sizeof (msprite_t) + (numframes - 1) * sizeof (psprite->frames);
|
|
|
|
psprite = Hunk_AllocName (size, loadname);
|
|
|
|
mod->cache.data = psprite;
|
|
|
|
psprite->type = SPR_VP_PARALLEL;
|
|
psprite->maxwidth = 1;
|
|
psprite->maxheight = 1;
|
|
psprite->beamlength = 1;
|
|
mod->synctype = 0;
|
|
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)
|
|
{
|
|
Con_Printf (CON_ERROR "Mod_LoadSpriteModel: Invalid # of frames: %d\n", numframes);
|
|
Hunk_FreeToLowMark(hunkstart);
|
|
return false;
|
|
}
|
|
|
|
mod->numframes = 0;
|
|
|
|
pframetype = pin->frames;
|
|
for (i=0 ; i<numframes ; i++)
|
|
{
|
|
psprite->frames[mod->numframes].type = SPR_SINGLE;
|
|
|
|
width = LittleLong(pframetype->width);
|
|
height = LittleLong(pframetype->height);
|
|
|
|
framefile = COM_LoadMallocFile(pframetype->name);
|
|
if (!framefile)
|
|
{
|
|
Con_Printf("Couldn't open sprite frame %s\n", pframetype->name);
|
|
continue; //skip this frame - is this a bad idea?
|
|
}
|
|
framedata = ReadPCXFile(framefile, com_filesize, &width, &height);
|
|
BZ_Free(framefile);
|
|
if (!framedata)
|
|
{
|
|
Con_Printf("Sprite frame %s is not a pcx\n", pframetype->name);
|
|
continue; //skip this frame - is this a bad idea?
|
|
}
|
|
|
|
frame = psprite->frames[mod->numframes].frameptr = Hunk_AllocName(sizeof(mspriteframe_t)+width*r_pixbytes*height, loadname);
|
|
|
|
frame->width = width;
|
|
frame->height = height;
|
|
origin[0] = LittleLong (pframetype->origin_x);
|
|
origin[1] = LittleLong (pframetype->origin_y);
|
|
|
|
frame->up = -origin[1];
|
|
frame->down = frame->height - origin[1];
|
|
frame->left = -origin[0];
|
|
frame->right = frame->width - origin[0];
|
|
|
|
if (r_pixbytes == 4)
|
|
{
|
|
for (j = 0; j < width*height; j++)
|
|
{
|
|
frame->pixels[j*4+0] = framedata[j*4+2];
|
|
frame->pixels[j*4+1] = framedata[j*4+1];
|
|
frame->pixels[j*4+2] = framedata[j*4+0];
|
|
frame->pixels[j*4+3] = framedata[j*4+3];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (j = 0; j < width*height; j++)
|
|
{
|
|
if (!framedata[j*4+3]) //make sure
|
|
frame->pixels[j] = 255;
|
|
else
|
|
frame->pixels[j] = GetPaletteNoFB(framedata[j*4+0], framedata[j*4+1], framedata[j*4+2]);
|
|
}
|
|
}
|
|
BZ_Free(framedata);
|
|
|
|
mod->numframes++;
|
|
}
|
|
|
|
mod->type = mod_sprite;
|
|
return true;
|
|
}
|
|
|
|
//=============================================================================
|
|
#endif
|
|
/*
|
|
================
|
|
Mod_Print
|
|
================
|
|
*/
|
|
void SWMod_Print (void)
|
|
{
|
|
int i;
|
|
model_t *mod;
|
|
|
|
Con_Printf ("Cached models:\n");
|
|
for (i=0, mod=mod_known ; i < mod_numknown ; i++, mod++)
|
|
{
|
|
Con_Printf ("%8p : %s\n",mod->cache.data, mod->name);
|
|
}
|
|
}
|
|
|
|
|