2
0
mirror of https://github.com/FWGS/xash3d-fwgs synced 2024-11-26 20:00:53 +01:00
xash3d-fwgs/engine/common/mod_alias.c

242 lines
6.9 KiB
C

/*
mod_alias.c - alias model loading
Copyright (C) 2010 Uncle Mike
Copyright (C) 2024 Alibek Omarov
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 3 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.
*/
#include "common.h"
#include "alias.h"
#if !XASH_DEDICATED
#include "ref_common.h"
#endif // XASH_DEDICATED
#include "mod_local.h"
#include "xash3d_mathlib.h"
static int g_posenum = 0;
static const trivertex_t *g_poseverts[MAXALIASFRAMES];
static const void *Mod_LoadAliasFrame( const daliasframe_t *pdaliasframe, maliasframedesc_t *frame, const aliashdr_t *aliashdr )
{
const trivertex_t *pinframe;
int i;
Q_strncpy( frame->name, pdaliasframe->name, sizeof( frame->name ));
frame->firstpose = g_posenum;
frame->numposes = 1;
for( i = 0; i < 3; i++ )
{
frame->bboxmin.v[i] = pdaliasframe->bboxmin.v[i];
frame->bboxmax.v[i] = pdaliasframe->bboxmax.v[i];
}
pinframe = (const trivertex_t *)(pdaliasframe + 1);
g_poseverts[g_posenum] = pinframe;
g_posenum++;
pinframe += aliashdr->numverts;
return (void *)pinframe;
}
static const void *Mod_LoadAliasGroup( const daliasgroup_t *pingroup, maliasframedesc_t *frame, const aliashdr_t *aliashdr )
{
const daliasinterval_t *pin_intervals;
const void *ptemp;
int i, numframes;
frame->firstpose = g_posenum;
frame->numposes = numframes = pingroup->numframes;
for( i = 0; i < 3; i++ )
{
frame->bboxmin.v[i] = pingroup->bboxmin.v[i];
frame->bboxmax.v[i] = pingroup->bboxmax.v[i];
}
pin_intervals = (const daliasinterval_t *)(pingroup + 1);
// all the intervals are always equal 0.1 so we don't care about them
frame->interval = pin_intervals->interval;
pin_intervals += numframes;
ptemp = (void *)pin_intervals;
for( i = 0; i < numframes; i++ )
{
g_poseverts[g_posenum] = (const trivertex_t *)((const daliasframe_t *)ptemp + 1);
ptemp = g_poseverts[g_posenum] + aliashdr->numverts;
g_posenum++;
}
return ptemp;
}
static void Mod_CalcAliasBounds( model_t *mod, const aliashdr_t *aliashdr )
{
int i, j, k;
float radius;
float dist;
vec3_t v;
ClearBounds( mod->mins, mod->maxs );
radius = 0.0f;
// process verts
for( i = 0; i < aliashdr->numposes; i++ )
{
for( j = 0; j < aliashdr->numverts; j++ )
{
for( k = 0; k < 3; k++ )
v[k] = g_poseverts[i][j].v[k] * aliashdr->scale[k] + aliashdr->scale_origin[k];
AddPointToBounds( v, mod->mins, mod->maxs );
dist = DotProduct( v, v );
if( radius < dist )
radius = dist;
}
}
mod->radius = sqrt( radius );
}
static const void *Mod_LoadAllSkins( model_t *mod, int numskins, const daliasskintype_t *pskintype, const aliashdr_t *aliashdr )
{
int i, size;
if(( numskins < 1 ) || ( numskins > MAX_SKINS ))
Host_Error( "%s: Invalid # of skins: %d\n", __func__, numskins );
size = aliashdr->skinwidth * aliashdr->skinheight;
// just skipping textures, renderer will take care of them later
for( i = 0; i < numskins; i++ )
{
if( pskintype->type == ALIAS_SKIN_SINGLE )
{
const byte *ptexture = (const byte *)&pskintype[1];
pskintype = (const daliasskintype_t *)( ptexture + size );
}
else
{
const daliasskingroup_t *pinskingroup = (const daliasskingroup_t *)&pskintype[1];
const daliasskininterval_t *pinskinintervals = (const daliasskininterval_t *)&pinskingroup[1];
const byte *ptexture = (const byte *)&pinskinintervals[pinskingroup->numskins];
pskintype = (const daliasskintype_t *)( ptexture + size );
}
}
return pskintype;
}
/*
====================
Mod_LoadAliasModel
load alias model
====================
*/
void Mod_LoadAliasModel( model_t *mod, const void *buffer, qboolean *loaded )
{
const daliasframetype_t *pframetype;
const daliasskintype_t *pskintype;
const dtriangle_t *pintriangles;
const daliashdr_t *pinmodel;
const stvert_t *pinstverts;
aliashdr_t *m_pAliasHeader;
size_t size;
char poolname[MAX_VA_STRING];
int i;
if( loaded ) *loaded = false;
pinmodel = (const daliashdr_t *)buffer;
i = pinmodel->version;
if( i != ALIAS_VERSION )
{
Con_DPrintf( S_ERROR "%s has wrong version number (%i should be %i)\n", mod->name, i, ALIAS_VERSION );
return;
}
if( pinmodel->numverts <= 0 || pinmodel->numtris <= 0 || pinmodel->numframes <= 0 )
return; // how is it possible to make that?
Q_snprintf( poolname, sizeof( poolname ), "^2%s^7", mod->name );
mod->mempool = Mem_AllocPool( poolname );
size = sizeof( aliashdr_t ) + (pinmodel->numframes - 1) * sizeof( maliasframedesc_t );
mod->cache.data = m_pAliasHeader = Mem_Calloc( mod->mempool, size );
// endian-adjust and copy the data, starting with the alias model header
m_pAliasHeader->numverts = pinmodel->numverts;
if( m_pAliasHeader->numverts > MAXALIASVERTS )
{
Con_DPrintf( S_ERROR "model %s has too many vertices\n", mod->name );
return;
}
mod->flags = pinmodel->flags; // share effects flags
m_pAliasHeader->boundingradius = pinmodel->boundingradius;
m_pAliasHeader->numskins = pinmodel->numskins;
m_pAliasHeader->skinwidth = pinmodel->skinwidth;
m_pAliasHeader->skinheight = pinmodel->skinheight;
m_pAliasHeader->numtris = pinmodel->numtris;
mod->numframes = m_pAliasHeader->numframes = pinmodel->numframes;
m_pAliasHeader->size = pinmodel->size;
for( i = 0; i < 3; i++ )
{
m_pAliasHeader->scale[i] = pinmodel->scale[i];
m_pAliasHeader->scale_origin[i] = pinmodel->scale_origin[i];
m_pAliasHeader->eyeposition[i] = pinmodel->eyeposition[i];
}
// load the skins
pskintype = (const daliasskintype_t *)&pinmodel[1];
pskintype = Mod_LoadAllSkins( mod, m_pAliasHeader->numskins, pskintype, m_pAliasHeader );
// will be done at renderer side...
// load base s and t vertices
pinstverts = (const stvert_t *)pskintype;
// will be done at renderer side...
// load triangle lists
pintriangles = (const dtriangle_t *)&pinstverts[m_pAliasHeader->numverts];
// will be done at renderer side
// load the frames
pframetype = (const daliasframetype_t *)&pintriangles[m_pAliasHeader->numtris];
m_pAliasHeader->pposeverts = g_poseverts; // store the pointer to be accessed by renderer
g_posenum = 0;
for( i = 0; i < m_pAliasHeader->numframes; i++ )
{
aliasframetype_t frametype = pframetype->type;
if( frametype == ALIAS_SINGLE )
pframetype = (const daliasframetype_t *)Mod_LoadAliasFrame((const daliasframe_t *)&pframetype[1], &m_pAliasHeader->frames[i], m_pAliasHeader );
else pframetype = (const daliasframetype_t *)Mod_LoadAliasGroup(( const daliasgroup_t *)&pframetype[1], &m_pAliasHeader->frames[i], m_pAliasHeader );
}
m_pAliasHeader->numposes = g_posenum;
Mod_CalcAliasBounds( mod, m_pAliasHeader );
mod->type = mod_alias;
if( loaded ) *loaded = true; // done
}