halflife-thewastes-sdk/utils/mdlviewer/studio_utils.cpp
2023-09-06 03:25:39 +03:00

425 lines
11 KiB
C++

/***
*
* Copyright (c) 1996-2001, Valve LLC. All rights reserved.
*
* This product contains software technology licensed from Id
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
* All Rights Reserved.
*
****/
// updates:
// 1-4-99 fixed file texture load and file read bug
// 2-8-99 fixed demand loaded sequence bug (thanks to Frans 'Otis' Bouma)
////////////////////////////////////////////////////////////////////////
#include <stdio.h>
#include <windows.h>
#include <gl\gl.h>
#include <gl\glu.h>
#include "mathlib.h"
#include "..\..\engine\studio.h"
#include "mdlviewer.h"
#pragma warning( disable : 4244 ) // double to float
////////////////////////////////////////////////////////////////////////
static int g_texnum = 1;
void StudioModel::UploadTexture(mstudiotexture_t *ptexture, byte *data, byte *pal)
{
// unsigned *in, int inwidth, int inheight, unsigned *out, int outwidth, int outheight;
int i, j;
int row1[256], row2[256], col1[256], col2[256];
byte *pix1, *pix2, *pix3, *pix4;
byte *tex, *out;
// convert texture to power of 2
for (int outwidth = 1; outwidth < ptexture->width; outwidth <<= 1)
;
if (outwidth > 256)
outwidth = 256;
for (int outheight = 1; outheight < ptexture->height; outheight <<= 1)
;
if (outheight > 256)
outheight = 256;
tex = out = (byte *)malloc( outwidth * outheight * 4);
for (i = 0; i < outwidth; i++)
{
col1[i] = (i + 0.25) * (ptexture->width / (float)outwidth);
col2[i] = (i + 0.75) * (ptexture->width / (float)outwidth);
}
for (i = 0; i < outheight; i++)
{
row1[i] = (int)((i + 0.25) * (ptexture->height / (float)outheight)) * ptexture->width;
row2[i] = (int)((i + 0.75) * (ptexture->height / (float)outheight)) * ptexture->width;
}
// scale down and convert to 32bit RGB
for (i=0 ; i<outheight ; i++)
{
for (j=0 ; j<outwidth ; j++, out += 4)
{
pix1 = &pal[data[row1[i] + col1[j]] * 3];
pix2 = &pal[data[row1[i] + col2[j]] * 3];
pix3 = &pal[data[row2[i] + col1[j]] * 3];
pix4 = &pal[data[row2[i] + col2[j]] * 3];
out[0] = (pix1[0] + pix2[0] + pix3[0] + pix4[0])>>2;
out[1] = (pix1[1] + pix2[1] + pix3[1] + pix4[1])>>2;
out[2] = (pix1[2] + pix2[2] + pix3[2] + pix4[2])>>2;
out[3] = 0xFF;
}
}
glBindTexture( GL_TEXTURE_2D, g_texnum );
glTexImage2D( GL_TEXTURE_2D, 0, 3/*??*/, outwidth, outheight, 0, GL_RGBA, GL_UNSIGNED_BYTE, tex );
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// ptexture->width = outwidth;
// ptexture->height = outheight;
ptexture->index = g_texnum;
g_texnum++;
free( tex );
}
studiohdr_t *StudioModel::LoadModel( char *modelname )
{
FILE *fp;
long size;
void *buffer;
// load the model
if( (fp = fopen( modelname, "rb" )) == NULL)
{
printf("unable to open %s\n", modelname );
exit(1);
}
fseek( fp, 0, SEEK_END );
size = ftell( fp );
fseek( fp, 0, SEEK_SET );
buffer = malloc( size );
fread( buffer, size, 1, fp );
int i;
byte *pin;
studiohdr_t *phdr;
mstudiotexture_t *ptexture;
pin = (byte *)buffer;
phdr = (studiohdr_t *)pin;
ptexture = (mstudiotexture_t *)(pin + phdr->textureindex);
if (phdr->textureindex != 0)
{
for (i = 0; i < phdr->numtextures; i++)
{
// strcpy( name, mod->name );
// strcpy( name, ptexture[i].name );
UploadTexture( &ptexture[i], pin + ptexture[i].index, pin + ptexture[i].width * ptexture[i].height + ptexture[i].index );
}
}
// UNDONE: free texture memory
fclose( fp );
return (studiohdr_t *)buffer;
}
studioseqhdr_t *StudioModel::LoadDemandSequences( char *modelname )
{
FILE *fp;
long size;
void *buffer;
// load the model
if( (fp = fopen( modelname, "rb" )) == NULL)
{
printf("unable to open %s\n", modelname );
exit(1);
}
fseek( fp, 0, SEEK_END );
size = ftell( fp );
fseek( fp, 0, SEEK_SET );
buffer = malloc( size );
fread( buffer, size, 1, fp );
fclose( fp );
return (studioseqhdr_t *)buffer;
}
void StudioModel::Init( char *modelname )
{
m_pstudiohdr = LoadModel( modelname );
// preload textures
if (m_pstudiohdr->numtextures == 0)
{
char texturename[256];
strcpy( texturename, modelname );
strcpy( &texturename[strlen(texturename) - 4], "T.mdl" );
m_ptexturehdr = LoadModel( texturename );
}
else
{
m_ptexturehdr = m_pstudiohdr;
}
// preload animations
if (m_pstudiohdr->numseqgroups > 1)
{
for (int i = 1; i < m_pstudiohdr->numseqgroups; i++)
{
char seqgroupname[256];
strcpy( seqgroupname, modelname );
sprintf( &seqgroupname[strlen(seqgroupname) - 4], "%02d.mdl", i );
m_panimhdr[i] = LoadDemandSequences( seqgroupname );
}
}
}
////////////////////////////////////////////////////////////////////////
int StudioModel::GetSequence( )
{
return m_sequence;
}
int StudioModel::SetSequence( int iSequence )
{
if (iSequence > m_pstudiohdr->numseq)
iSequence = 0;
if (iSequence < 0)
iSequence = m_pstudiohdr->numseq-1;
m_sequence = iSequence;
m_frame = 0;
return m_sequence;
}
void StudioModel::ExtractBbox( float *mins, float *maxs )
{
mstudioseqdesc_t *pseqdesc;
pseqdesc = (mstudioseqdesc_t *)((byte *)m_pstudiohdr + m_pstudiohdr->seqindex);
mins[0] = pseqdesc[ m_sequence ].bbmin[0];
mins[1] = pseqdesc[ m_sequence ].bbmin[1];
mins[2] = pseqdesc[ m_sequence ].bbmin[2];
maxs[0] = pseqdesc[ m_sequence ].bbmax[0];
maxs[1] = pseqdesc[ m_sequence ].bbmax[1];
maxs[2] = pseqdesc[ m_sequence ].bbmax[2];
}
void StudioModel::GetSequenceInfo( float *pflFrameRate, float *pflGroundSpeed )
{
mstudioseqdesc_t *pseqdesc;
pseqdesc = (mstudioseqdesc_t *)((byte *)m_pstudiohdr + m_pstudiohdr->seqindex) + (int)m_sequence;
if (pseqdesc->numframes > 1)
{
*pflFrameRate = 256 * pseqdesc->fps / (pseqdesc->numframes - 1);
*pflGroundSpeed = sqrt( pseqdesc->linearmovement[0]*pseqdesc->linearmovement[0]+ pseqdesc->linearmovement[1]*pseqdesc->linearmovement[1]+ pseqdesc->linearmovement[2]*pseqdesc->linearmovement[2] );
*pflGroundSpeed = *pflGroundSpeed * pseqdesc->fps / (pseqdesc->numframes - 1);
}
else
{
*pflFrameRate = 256.0;
*pflGroundSpeed = 0.0;
}
}
float StudioModel::SetController( int iController, float flValue )
{
mstudiobonecontroller_t *pbonecontroller = (mstudiobonecontroller_t *)((byte *)m_pstudiohdr + m_pstudiohdr->bonecontrollerindex);
// find first controller that matches the index
for (int i = 0; i < m_pstudiohdr->numbonecontrollers; i++, pbonecontroller++)
{
if (pbonecontroller->index == iController)
break;
}
if (i >= m_pstudiohdr->numbonecontrollers)
return flValue;
// wrap 0..360 if it's a rotational controller
if (pbonecontroller->type & (STUDIO_XR | STUDIO_YR | STUDIO_ZR))
{
// ugly hack, invert value if end < start
if (pbonecontroller->end < pbonecontroller->start)
flValue = -flValue;
// does the controller not wrap?
if (pbonecontroller->start + 359.0 >= pbonecontroller->end)
{
if (flValue > ((pbonecontroller->start + pbonecontroller->end) / 2.0) + 180)
flValue = flValue - 360;
if (flValue < ((pbonecontroller->start + pbonecontroller->end) / 2.0) - 180)
flValue = flValue + 360;
}
else
{
if (flValue > 360)
flValue = flValue - (int)(flValue / 360.0) * 360.0;
else if (flValue < 0)
flValue = flValue + (int)((flValue / -360.0) + 1) * 360.0;
}
}
int setting = 255 * (flValue - pbonecontroller->start) / (pbonecontroller->end - pbonecontroller->start);
if (setting < 0) setting = 0;
if (setting > 255) setting = 255;
m_controller[iController] = setting;
return setting * (1.0 / 255.0) * (pbonecontroller->end - pbonecontroller->start) + pbonecontroller->start;
}
float StudioModel::SetMouth( float flValue )
{
mstudiobonecontroller_t *pbonecontroller = (mstudiobonecontroller_t *)((byte *)m_pstudiohdr + m_pstudiohdr->bonecontrollerindex);
// find first controller that matches the mouth
for (int i = 0; i < m_pstudiohdr->numbonecontrollers; i++, pbonecontroller++)
{
if (pbonecontroller->index == 4)
break;
}
// wrap 0..360 if it's a rotational controller
if (pbonecontroller->type & (STUDIO_XR | STUDIO_YR | STUDIO_ZR))
{
// ugly hack, invert value if end < start
if (pbonecontroller->end < pbonecontroller->start)
flValue = -flValue;
// does the controller not wrap?
if (pbonecontroller->start + 359.0 >= pbonecontroller->end)
{
if (flValue > ((pbonecontroller->start + pbonecontroller->end) / 2.0) + 180)
flValue = flValue - 360;
if (flValue < ((pbonecontroller->start + pbonecontroller->end) / 2.0) - 180)
flValue = flValue + 360;
}
else
{
if (flValue > 360)
flValue = flValue - (int)(flValue / 360.0) * 360.0;
else if (flValue < 0)
flValue = flValue + (int)((flValue / -360.0) + 1) * 360.0;
}
}
int setting = 64 * (flValue - pbonecontroller->start) / (pbonecontroller->end - pbonecontroller->start);
if (setting < 0) setting = 0;
if (setting > 64) setting = 64;
m_mouth = setting;
return setting * (1.0 / 64.0) * (pbonecontroller->end - pbonecontroller->start) + pbonecontroller->start;
}
float StudioModel::SetBlending( int iBlender, float flValue )
{
mstudioseqdesc_t *pseqdesc;
pseqdesc = (mstudioseqdesc_t *)((byte *)m_pstudiohdr + m_pstudiohdr->seqindex) + (int)m_sequence;
if (pseqdesc->blendtype[iBlender] == 0)
return flValue;
if (pseqdesc->blendtype[iBlender] & (STUDIO_XR | STUDIO_YR | STUDIO_ZR))
{
// ugly hack, invert value if end < start
if (pseqdesc->blendend[iBlender] < pseqdesc->blendstart[iBlender])
flValue = -flValue;
// does the controller not wrap?
if (pseqdesc->blendstart[iBlender] + 359.0 >= pseqdesc->blendend[iBlender])
{
if (flValue > ((pseqdesc->blendstart[iBlender] + pseqdesc->blendend[iBlender]) / 2.0) + 180)
flValue = flValue - 360;
if (flValue < ((pseqdesc->blendstart[iBlender] + pseqdesc->blendend[iBlender]) / 2.0) - 180)
flValue = flValue + 360;
}
}
int setting = 255 * (flValue - pseqdesc->blendstart[iBlender]) / (pseqdesc->blendend[iBlender] - pseqdesc->blendstart[iBlender]);
if (setting < 0) setting = 0;
if (setting > 255) setting = 255;
m_blending[iBlender] = setting;
return setting * (1.0 / 255.0) * (pseqdesc->blendend[iBlender] - pseqdesc->blendstart[iBlender]) + pseqdesc->blendstart[iBlender];
}
int StudioModel::SetBodygroup( int iGroup, int iValue )
{
if (iGroup > m_pstudiohdr->numbodyparts)
return -1;
mstudiobodyparts_t *pbodypart = (mstudiobodyparts_t *)((byte *)m_pstudiohdr + m_pstudiohdr->bodypartindex) + iGroup;
int iCurrent = (m_bodynum / pbodypart->base) % pbodypart->nummodels;
if (iValue >= pbodypart->nummodels)
return iCurrent;
m_bodynum = (m_bodynum - (iCurrent * pbodypart->base) + (iValue * pbodypart->base));
return iValue;
}
int StudioModel::SetSkin( int iValue )
{
if (iValue < m_pstudiohdr->numskinfamilies)
{
return m_skinnum;
}
m_skinnum = iValue;
return iValue;
}