435 lines
11 KiB
C++
435 lines
11 KiB
C++
/***
|
|
*
|
|
* Copyright (c) 1996-2002, 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"
|
|
#undef DotProduct
|
|
#include "../../dlls/vector.h"
|
|
#include "../../public/steam/steamtypes.h" // defines int32, required by studio.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 outwidth, 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 (outwidth = 1; outwidth < ptexture->width; outwidth <<= 1)
|
|
;
|
|
|
|
if (outwidth > 256)
|
|
outwidth = 256;
|
|
|
|
for (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)
|
|
{
|
|
int i;
|
|
mstudiobonecontroller_t* pbonecontroller = (mstudiobonecontroller_t*)((byte*)m_pstudiohdr + m_pstudiohdr->bonecontrollerindex);
|
|
|
|
// find first controller that matches the index
|
|
for (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;
|
|
}
|