fteqw/engine/gl/gl_hlmdl.c

648 lines
22 KiB
C

#include "quakedef.h"
#ifdef HALFLIFEMODELS
#include "glquake.h"
/*
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Half-Life Model Renderer (Experimental) Copyright (C) 2001 James 'Ender' Brown [ender@quakesrc.org] 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. fromquake.h -
render.c - apart from calculations (mostly range checking or value conversion code is a mix of standard Quake 1
meshing, and vertex deforms. The rendering loop uses standard Quake 1 drawing, after SetupBones deforms the vertex.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Also, please note that it won't do all hl models....
Nor will it work 100%
*/
#include "model_hl.h"
void VectorTransform (const vec3_t in1, const float in2[3][4], vec3_t out);
void QuaternionGLMatrix(float x, float y, float z, float w, vec4_t *GLM)
{
GLM[0][0] = 1 - 2 * y * y - 2 * z * z;
GLM[1][0] = 2 * x * y + 2 * w * z;
GLM[2][0] = 2 * x * z - 2 * w * y;
GLM[0][1] = 2 * x * y - 2 * w * z;
GLM[1][1] = 1 - 2 * x * x - 2 * z * z;
GLM[2][1] = 2 * y * z + 2 * w * x;
GLM[0][2] = 2 * x * z + 2 * w * y;
GLM[1][2] = 2 * y * z - 2 * w * x;
GLM[2][2] = 1 - 2 * x * x - 2 * y * y;
}
/*
=======================================================================================================================
QuaternionGLAngle - Convert a GL angle to a quaternion matrix
=======================================================================================================================
*/
void QuaternionGLAngle(const vec3_t angles, vec4_t quaternion)
{
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
float yaw = angles[2] * 0.5;
float pitch = angles[1] * 0.5;
float roll = angles[0] * 0.5;
float siny = sin(yaw);
float cosy = cos(yaw);
float sinp = sin(pitch);
float cosp = cos(pitch);
float sinr = sin(roll);
float cosr = cos(roll);
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
quaternion[0] = sinr * cosp * cosy - cosr * sinp * siny;
quaternion[1] = cosr * sinp * cosy + sinr * cosp * siny;
quaternion[2] = cosr * cosp * siny - sinr * sinp * cosy;
quaternion[3] = cosr * cosp * cosy + sinr * sinp * siny;
}
float transform_matrix[128][3][4]; /* Vertex transformation matrix */
void GL_Draw_HL_AliasFrame(short *order, vec3_t *transformed, float tex_w, float tex_h);
/*
=======================================================================================================================
Mod_LoadHLModel - read in the model's constituent parts
=======================================================================================================================
*/
extern char loadname[];
void Mod_LoadHLModel (model_t *mod, void *buffer)
{
/*~~*/
int i;
hlmodelcache_t *model;
hlmdl_header_t *header;
hlmdl_tex_t *tex;
hlmdl_bone_t *bones;
hlmdl_bonecontroller_t *bonectls;
int start, end, total;
/*~~*/
//checksum the model
if (!strcmp(mod->name, "progs/player.mdl") ||
!strcmp(mod->name, "progs/eyes.mdl")) {
unsigned short crc;
qbyte *p;
int len;
char st[40];
CRC_Init(&crc);
for (len = com_filesize, p = buffer; len; len--, p++)
CRC_ProcessByte(&crc, *p);
sprintf(st, "%d", (int) crc);
Info_SetValueForKey (cls.userinfo,
!strcmp(mod->name, "progs/player.mdl") ? pmodel_name : emodel_name,
st, MAX_INFO_STRING);
if (cls.state >= ca_connected)
{
CL_SendClientCommand("setinfo %s %d",
!strcmp(mod->name, "progs/player.mdl") ? pmodel_name : emodel_name,
(int)crc);
}
}
start = Hunk_LowMark ();
//load the model into hunk
model = Hunk_Alloc(sizeof(hlmodelcache_t));
header = Hunk_Alloc(com_filesize);
memcpy(header, buffer, com_filesize);
if (header->version != 10)
Host_EndGame("Cannot load model %s - unknown version %i\n", mod->name, header->version);
tex = (hlmdl_tex_t *) ((qbyte *) header + header->textures);
bones = (hlmdl_bone_t *) ((qbyte *) header + header->boneindex);
bonectls = (hlmdl_bonecontroller_t *) ((qbyte *) header + header->controllerindex);
/* won't work - doesn't know exact sizes.
header = Hunk_Alloc(sizeof(hlmdl_header_t));
memcpy(header, (hlmdl_header_t *) buffer, sizeof(hlmdl_header_t));
tex = Hunk_Alloc(sizeof(hlmdl_tex_t)*header->numtextures);
memcpy(tex, (hlmdl_tex_t *) buffer, sizeof(hlmdl_tex_t)*header->numtextures);
bones = Hunk_Alloc(sizeof(hlmdl_bone_t)*header->numtextures);
memcpy(bones, (hlmdl_bone_t *) buffer, sizeof(hlmdl_bone_t)*header->numbones);
bonectls = Hunk_Alloc(sizeof(hlmdl_bonecontroller_t)*header->numcontrollers);
memcpy(bonectls, (hlmdl_bonecontroller_t *) buffer, sizeof(hlmdl_bonecontroller_t)*header->numcontrollers);
*/
model->header = (char *)header - (char *)model;
model->textures = (char *)tex - (char *)model;
model->bones = (char *)bones - (char *)model;
model->bonectls = (char *)bonectls - (char *)model;
for(i = 0; i < header->numtextures; i++)
{
tex[i].i = GL_LoadTexture8Pal24("", tex[i].w, tex[i].h, (qbyte *) header + tex[i].i, (qbyte *) header + tex[i].w * tex[i].h + tex[i].i, true, false);
}
//
// move the complete, relocatable alias model to the cache
//
end = Hunk_LowMark ();
total = end - start;
mod->type = mod_halflife;
Cache_Alloc (&mod->cache, total, loadname);
if (!mod->cache.data)
return;
memcpy (mod->cache.data, model, total);
Hunk_FreeToLowMark (start);
}
/*
=======================================================================================================================
HL_CurSequence - return the current sequence
=======================================================================================================================
*/
int HL_CurSequence(hlmodel_t model)
{
return model.sequence;
}
/*
=======================================================================================================================
HL_NewSequence - animation control (just some range checking really)
=======================================================================================================================
*/
int HL_NewSequence(hlmodel_t *model, int _inew)
{
if(_inew < 0)
_inew = model->header->numseq - 1;
else if(_inew >= model->header->numseq)
_inew = 0;
model->sequence = _inew;
model->frame = 0;
{
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
hlmdl_sequencelist_t *pseqdesc;
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
if(_inew == 0)
{
pseqdesc = (hlmdl_sequencelist_t *) ((qbyte *) model->header + model->header->seqindex) + model->sequence;
}
else
{
pseqdesc = (hlmdl_sequencelist_t *) ((qbyte *) model->header + model->header->seqindex) + model->sequence;
}
Sys_Printf("Current Sequence: %s\n", pseqdesc->name);
}
return model->sequence;
}
/*
=======================================================================================================================
HL_SetController - control where the model is facing (upper body usually)
=======================================================================================================================
*/
void HL_SetController(hlmodel_t *model, int num, float value)
{
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
int real, limit;
hlmdl_bonecontroller_t *control = (hlmdl_bonecontroller_t *)
((qbyte *) model->header + model->header->controllerindex);
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
if(num >= model->header->numcontrollers) return;
if(num == 4)
{
limit = 64;
}
else
{
limit = 255;
}
if(control->type & (0x0008 | 0x0010 | 0x0020))
{
if(control->end < control->start) value = -value;
if(control->start + 359.0 >= control->end)
{
if(value > ((control->start + control->end) / 2.0) + 180) value = value - 360;
if(value < ((control->start + control->end) / 2.0) - 180) value = value + 360;
}
else
{
if(value > 360)
value = value - (int) (value / 360.0) * 360.0;
else if(value < 0)
value = value + (int) ((value / -360.0) + 1) * 360.0;
}
}
real = limit * (value - control[num].start) / (control[num].end - control[num].start);
if(real < 0) real = 0;
if(real > limit) real = limit;
model->controller[num] = real;
}
/*
=======================================================================================================================
HL_CalculateBones - calculate bone positions - quaternion+vector in one function
=======================================================================================================================
*/
void HL_CalculateBones
(
int offset,
int frame,
vec4_t adjust,
hlmdl_bone_t *bone,
hlmdl_anim_t *animation,
float *destination
)
{
/*~~~~~~~~~~*/
int i;
vec3_t angle;
/*~~~~~~~~~~*/
/* For each vector */
for(i = 0; i < 3; i++)
{
/*~~~~~~~~~~~~~~~*/
int o = i + offset; /* Take the value offset - allows quaternion & vector in one function */
/*~~~~~~~~~~~~~~~*/
angle[i] = bone->value[o]; /* Take the bone value */
if(animation->offset[o] != 0)
{
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
int tempframe = frame;
hlmdl_animvalue_t *animvalue = (hlmdl_animvalue_t *) ((qbyte *) animation + animation->offset[o]);
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
/* find values including the required frame */
while(animvalue->num.total <= tempframe)
{
tempframe -= animvalue->num.total;
animvalue += animvalue->num.valid + 1;
}
if(animvalue->num.valid > tempframe)
{
if(animvalue->num.valid > (tempframe + 1))
angle[i] += animvalue[tempframe + 1].value * 1; // + 0 * animvalue[tempframe + 2].value * bone->scale[o];
else
angle[i] = animvalue[animvalue->num.valid].value;
angle[i] = bone->value[o] + angle[i] * bone->scale[o];
}
else
{
if(animvalue->num.total <= tempframe + 1)
{
angle[i] +=
(animvalue[animvalue->num.valid].value * 1 +
0 * animvalue[animvalue->num.valid + 2].value) *
bone->scale[o];
}
else
{
angle[i] += animvalue[animvalue->num.valid].value * bone->scale[o];
}
}
}
if(bone->bonecontroller[o] != -1) { /* Add the programmable offset. */
angle[i] += adjust[bone->bonecontroller[o]];
}
}
if(offset < 3)
{
VectorCopy(angle, destination); /* Just a standard vector */
}
else
{
QuaternionGLAngle(angle, destination); /* A quaternion */
}
}
/*
=======================================================================================================================
HL_CalcBoneAdj - Calculate the adjustment values for the programmable controllers
=======================================================================================================================
*/
void HL_CalcBoneAdj(hlmodel_t *model)
{
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
int i;
float value;
hlmdl_bonecontroller_t *control = (hlmdl_bonecontroller_t *)
((qbyte *) model->header + model->header->controllerindex);
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
for(i = 0; i < model->header->numcontrollers; i++)
{
/*~~~~~~~~~~~~~~~~~~~~~*/
int j = control[i].index;
/*~~~~~~~~~~~~~~~~~~~~~*/
if(control[i].type & 0x8000)
{
value = model->controller[j] * (360.0 / 256.0) + control[i].start;
}
else
{
value = model->controller[j] / 255.0;
if(value < 0)
value = 0;
else if(value > 1.0)
value = 1.0;
value = (1.0 - value) * control[i].start + value * control[i].end;
}
/* Rotational controllers need their values converted */
if(control[i].type >= 0x0008 && control[i].type <= 0x0020)
model->adjust[i] = M_PI * value / 180;
else
model->adjust[i] = value;
}
}
/*
=======================================================================================================================
HL_SetupBones - determine where vertex should be using bone movements
=======================================================================================================================
*/
void HL_SetupBones(hlmodel_t *model)
{
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
int i;
float matrix[3][4];
static vec3_t positions[128];
static vec4_t quaternions[128];
hlmdl_sequencelist_t *sequence = (hlmdl_sequencelist_t *) ((qbyte *) model->header + model->header->seqindex) +
model->sequence;
hlmdl_sequencedata_t *sequencedata = (hlmdl_sequencedata_t *)
((qbyte *) model->header + model->header->seqgroups) +
sequence->seqindex;
hlmdl_anim_t *animation = (hlmdl_anim_t *)
((qbyte *) model->header + sequencedata->data + sequence->index);
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
HL_CalcBoneAdj(model); /* Deal with programmable controllers */
if(sequence->motiontype & 0x0001) positions[sequence->motionbone][0] = 0.0;
if(sequence->motiontype & 0x0002) positions[sequence->motionbone][1] = 0.0;
if(sequence->motiontype & 0x0004) positions[sequence->motionbone][2] = 0.0;
/* Sys_Printf("Frame: %i\n", model->frame); */
for(i = 0; i < model->header->numbones; i++)
{
/*
* There are two vector offsets in the structure. The first seems to be the
* positions of the bones, the second the quats of the bone matrix itself. We
* convert it inside the routine - Inconsistant, but hey.. so's the whole model
* format.
*/
HL_CalculateBones(0, model->frame, model->adjust, model->bones + i, animation + i, positions[i]);
HL_CalculateBones(3, model->frame, model->adjust, model->bones + i, animation + i, quaternions[i]);
/* FIXME: Blend the bones and make them cry :) */
QuaternionGLMatrix(quaternions[i][0], quaternions[i][1], quaternions[i][2], quaternions[i][3], matrix);
matrix[0][3] = positions[i][0];
matrix[1][3] = positions[i][1];
matrix[2][3] = positions[i][2];
/* If we have a parent, take the addition. Otherwise just copy the values */
if(model->bones[i].parent>=0)
{
R_ConcatTransforms(transform_matrix[model->bones[i].parent], matrix, transform_matrix[i]);
}
else
{
memcpy(transform_matrix[i], matrix, 12 * sizeof(float));
}
}
}
/*
=======================================================================================================================
R_Draw_HL_AliasModel - main drawing function
=======================================================================================================================
*/
void R_DrawHLModel(entity_t *curent)
{
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
hlmodelcache_t *modelc = Mod_Extradata(curent->model);
hlmodel_t model;
int b, m, v;
short *skins;
hlmdl_sequencelist_t *sequence;
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
//general model
model.header = (hlmdl_header_t *) ((char *)modelc + modelc->header);
model.textures = (hlmdl_tex_t *) ((char *)modelc + modelc->textures);
model.bones = (hlmdl_bone_t *) ((char *)modelc + modelc->bones);
model.bonectls = (hlmdl_bonecontroller_t *) ((char *)modelc + modelc->bonectls);
//specific to entity
model.sequence = curent->frame;
model.frame = 0;
model.frametime = 0;
HL_NewSequence(&model, curent->frame);
skins = (short *) ((qbyte *) model.header + model.header->skins);
sequence = (hlmdl_sequencelist_t *) ((qbyte *) model.header + model.header->seqindex) +
model.sequence;
model.controller[0] = 127;
model.controller[1] = 127;
model.controller[2] = 127;
model.controller[3] = 127;
model.controller[4] = 0;//sin(cl.time)*127+127;
model.frametime += (cl.time - cl.lerpents[curent->keynum].framechange)*sequence->timing;
if (model.frametime>=1)
{
model.frame += (int) model.frametime;
model.frametime -= (int)model.frametime;
}
if (!sequence->numframes)
return;
if(model.frame >= sequence->numframes)
model.frame %= sequence->numframes;
if (sequence->motiontype)
model.frame = sequence->numframes-1;
GL_TexEnv(GL_MODULATE);
if (curent->alpha<1)
{
qglEnable(GL_BLEND);
}
else
{
qglDisable(GL_BLEND);
}
qglBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
// Con_Printf("%s %i\n", sequence->name, sequence->unknown1[0]);
qglPushMatrix();
{
vec3_t difuse, ambient, ldir;
cl.worldmodel->funcs.LightPointValues(curent->origin, difuse, ambient, ldir);
qglColor4f(difuse[0]/255+ambient[0]/255, difuse[1]/255+ambient[1]/255, difuse[2]/255+ambient[2]/255, curent->alpha);
}
R_RotateForEntity (curent);
HL_SetupBones(&model); /* Setup the bones */
/* Manipulate each mesh directly */
for(b = 0; b < model.header->numbodyparts; b++)
{
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
hlmdl_bodypart_t *bodypart = (hlmdl_bodypart_t *) ((qbyte *) model.header + model.header->bodypartindex) +
b;
int bodyindex = (0 / bodypart->base) % bodypart->nummodels;
hlmdl_model_t *amodel = (hlmdl_model_t *) ((qbyte *) model.header + bodypart->modelindex) + bodyindex;
qbyte *bone = ((qbyte *) model.header + amodel->vertinfoindex);
vec3_t *verts = (vec3_t *) ((qbyte *) model.header + amodel->vertindex);
vec3_t transformed[2048];
// vec3_t *norms = (vec3_t *) ((qbyte *) model.header + amodel->unknown3[2]);
// vec3_t transformednorms[2048];
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
for(v = 0; v < amodel->numverts; v++) // Transform per the matrix
{
VectorTransform(verts[v], transform_matrix[bone[v]], transformed[v]);
// glVertex3fv(verts[v]);
// glVertex3f( verts[v][0]+10*verts[v][0],
// verts[v][1]+10*verts[v][1],
// verts[v][2]+10*verts[v][2]);
}
//Need to work out what we have!
//raw data appears to be unit vectors
//transformed gives some points on the skeleton.
//what's also weird is that the meshes use these up!
/* glDisable(GL_TEXTURE_2D);
glBegin(GL_LINES);
for(v = 0; v < amodel->unknown3[0]; v++) // Transform per the matrix
{
VectorTransform(norms[v], transform_matrix[bone[v]], transformednorms[v]);
glVertex3fv(transformednorms[v]);
glVertex3f( transformednorms[v][0]+10*transformednorms[v][0],
transformednorms[v][1]+10*transformednorms[v][1],
transformednorms[v][2]+10*transformednorms[v][2]);
}
glEnd();
glEnable(GL_TEXTURE_2D);
*/
/* Draw each mesh */
for(m = 0; m < amodel->nummesh; m++)
{
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
hlmdl_mesh_t *mesh = (hlmdl_mesh_t *) ((qbyte *) model.header + amodel->meshindex) + m;
float tex_w = 1.0f / model.textures[skins[mesh->skinindex]].w;
float tex_h = 1.0f / model.textures[skins[mesh->skinindex]].h;
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
GL_Bind(model.textures[skins[mesh->skinindex]].i);
GL_Draw_HL_AliasFrame((short *) ((qbyte *) model.header + mesh->index), transformed, tex_w, tex_h);
}
}
qglPopMatrix();
GL_TexEnv(GL_REPLACE);
}
/*
=======================================================================================================================
GL_Draw_HL_AliasFrame - clip and draw all triangles
=======================================================================================================================
*/
void GL_Draw_HL_AliasFrame(short *order, vec3_t *transformed, float tex_w, float tex_h)
{
/*~~~~~~~~~~*/
int count = 0;
/*~~~~~~~~~~*/
// int c_tris=0;
// int c_verts=0;
// int c_chains=0;
for(;;)
{
count = *order++; /* get the vertex count and primitive type */
if(!count) break; /* done */
if(count < 0)
{
count = -count;
qglBegin(GL_TRIANGLE_FAN);
}
else
{
qglBegin(GL_TRIANGLE_STRIP);
}
// c_tris += count-2;
// c_chains++;
do
{
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
float *verts = transformed[order[0]];
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
/* texture coordinates come from the draw list */
qglTexCoord2f(order[2] * tex_w, order[3] * tex_h);
order += 4;
qglVertex3fv(verts);
// c_verts++;
} while(--count);
qglEnd();
}
}
#endif