mirror of
https://github.com/nzp-team/fteqw.git
synced 2024-11-10 22:51:57 +00:00
Added dpm support, and a mod_md3flags cvar to disable reading flags on md3s.
git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@1292 fc73d0e0-1445-4013-8a0c-d673dee63da5
This commit is contained in:
parent
a8295592a2
commit
2486c48927
1 changed files with 635 additions and 12 deletions
|
@ -1,3 +1,18 @@
|
||||||
|
|
||||||
|
//a note about dedicated servers:
|
||||||
|
//In the server-side gamecode, a couple of q1 extensions require knowing something about models.
|
||||||
|
//So we load models serverside, if required.
|
||||||
|
|
||||||
|
//things we need:
|
||||||
|
//tag/bone names and indexes so we can have reasonable modding with tags. :)
|
||||||
|
//tag/bone positions so we can shoot from the actual gun or other funky stuff
|
||||||
|
//vertex positions so we can trace against the mesh rather than the bbox.
|
||||||
|
|
||||||
|
//we use the gl renderer's model code because it supports more sorts of models than the sw renderer. Sad but true.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#include "quakedef.h"
|
#include "quakedef.h"
|
||||||
#ifdef RGLQUAKE
|
#ifdef RGLQUAKE
|
||||||
#include "glquake.h"
|
#include "glquake.h"
|
||||||
|
@ -8,6 +23,8 @@
|
||||||
|
|
||||||
#if defined(ZYMOTICMODELS) || defined(MD5MODELS)
|
#if defined(ZYMOTICMODELS) || defined(MD5MODELS)
|
||||||
#define SKELETALMODELS
|
#define SKELETALMODELS
|
||||||
|
|
||||||
|
#include "malloc.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define MAX_BONES 256
|
#define MAX_BONES 256
|
||||||
|
@ -71,6 +88,7 @@ vec3_t *tempNormals;
|
||||||
|
|
||||||
extern cvar_t gl_ati_truform;
|
extern cvar_t gl_ati_truform;
|
||||||
extern cvar_t r_vertexdlights;
|
extern cvar_t r_vertexdlights;
|
||||||
|
extern cvar_t mod_md3flags;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
int ofs_indexes;
|
int ofs_indexes;
|
||||||
|
@ -83,8 +101,8 @@ typedef struct {
|
||||||
int ofsskins;
|
int ofsskins;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
qboolean sharesverts; //used with models with two shaders using the same vertex.
|
qboolean sharesverts; //used with models with two shaders using the same vertex - use last mesh's verts
|
||||||
|
qboolean sharesbones; //use last mesh's bones (please, never set this on the first mesh!)
|
||||||
|
|
||||||
int numverts;
|
int numverts;
|
||||||
|
|
||||||
|
@ -192,9 +210,9 @@ void Mod_DoCRC(model_t *mod, char *buffer, int buffersize)
|
||||||
int len;
|
int len;
|
||||||
char st[40];
|
char st[40];
|
||||||
|
|
||||||
CRC_Init(&crc);
|
QCRC_Init(&crc);
|
||||||
for (len = buffersize, p = buffer; len; len--, p++)
|
for (len = buffersize, p = buffer; len; len--, p++)
|
||||||
CRC_ProcessByte(&crc, *p);
|
QCRC_ProcessByte(&crc, *p);
|
||||||
|
|
||||||
sprintf(st, "%d", (int) crc);
|
sprintf(st, "%d", (int) crc);
|
||||||
Info_SetValueForKey (cls.userinfo,
|
Info_SetValueForKey (cls.userinfo,
|
||||||
|
@ -247,7 +265,8 @@ qboolean GLMod_Trace(model_t *model, int forcehullnum, int frame, vec3_t start,
|
||||||
frac = 1;
|
frac = 1;
|
||||||
if (group->isheirachical)
|
if (group->isheirachical)
|
||||||
{
|
{
|
||||||
R_LerpBones(&frac, (float**)posedata, 1, (galiasbone_t*)((char*)mod + mod->ofsbones), mod->numbones, bonepose);
|
if (!mod->sharesbones)
|
||||||
|
R_LerpBones(&frac, (float**)posedata, 1, (galiasbone_t*)((char*)mod + mod->ofsbones), mod->numbones, bonepose);
|
||||||
R_TransformVerticies(bonepose, (galisskeletaltransforms_t*)((char*)mod + mod->ofstransforms), mod->numtransforms, posedata);
|
R_TransformVerticies(bonepose, (galisskeletaltransforms_t*)((char*)mod + mod->ofstransforms), mod->numtransforms, posedata);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -611,7 +630,7 @@ static void R_BuildSkeletalMesh(mesh_t *mesh, float *plerp, float **pose, int po
|
||||||
}
|
}
|
||||||
qglEnd();
|
qglEnd();
|
||||||
|
|
||||||
mesh->numindexes = 0; //don't draw this mesh, as that would obscure the bones. :(
|
// mesh->numindexes = 0; //don't draw this mesh, as that would obscure the bones. :(
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
@ -619,6 +638,7 @@ static void R_BuildSkeletalMesh(mesh_t *mesh, float *plerp, float **pose, int po
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef SERVERONLY
|
#ifndef SERVERONLY
|
||||||
|
//changes vertex lighting values
|
||||||
static void R_GAliasAddDlights(mesh_t *mesh, vec3_t org, vec3_t angles)
|
static void R_GAliasAddDlights(mesh_t *mesh, vec3_t org, vec3_t angles)
|
||||||
{
|
{
|
||||||
int l, v;
|
int l, v;
|
||||||
|
@ -2042,6 +2062,237 @@ void GL_LightMesh (mesh_t *mesh, vec3_t lightpos, vec3_t colours, float radius)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//courtesy of DP
|
||||||
|
void R_BuildBumpVectors(const float *v0, const float *v1, const float *v2, const float *tc0, const float *tc1, const float *tc2, float *svector3f, float *tvector3f, float *normal3f)
|
||||||
|
{
|
||||||
|
float f, tangentcross[3], v10[3], v20[3], tc10[2], tc20[2];
|
||||||
|
// 79 add/sub/negate/multiply (1 cycle), 1 compare (3 cycle?), total cycles not counting load/store/exchange roughly 82 cycles
|
||||||
|
// 6 add, 28 subtract, 39 multiply, 1 compare, 50% chance of 6 negates
|
||||||
|
|
||||||
|
// 6 multiply, 9 subtract
|
||||||
|
VectorSubtract(v1, v0, v10);
|
||||||
|
VectorSubtract(v2, v0, v20);
|
||||||
|
normal3f[0] = v10[1] * v20[2] - v10[2] * v20[1];
|
||||||
|
normal3f[1] = v10[2] * v20[0] - v10[0] * v20[2];
|
||||||
|
normal3f[2] = v10[0] * v20[1] - v10[1] * v20[0];
|
||||||
|
// 12 multiply, 10 subtract
|
||||||
|
tc10[1] = tc1[1] - tc0[1];
|
||||||
|
tc20[1] = tc2[1] - tc0[1];
|
||||||
|
svector3f[0] = tc10[1] * v20[0] - tc20[1] * v10[0];
|
||||||
|
svector3f[1] = tc10[1] * v20[1] - tc20[1] * v10[1];
|
||||||
|
svector3f[2] = tc10[1] * v20[2] - tc20[1] * v10[2];
|
||||||
|
tc10[0] = tc1[0] - tc0[0];
|
||||||
|
tc20[0] = tc2[0] - tc0[0];
|
||||||
|
tvector3f[0] = tc10[0] * v20[0] - tc20[0] * v10[0];
|
||||||
|
tvector3f[1] = tc10[0] * v20[1] - tc20[0] * v10[1];
|
||||||
|
tvector3f[2] = tc10[0] * v20[2] - tc20[0] * v10[2];
|
||||||
|
// 12 multiply, 4 add, 6 subtract
|
||||||
|
f = DotProduct(svector3f, normal3f);
|
||||||
|
svector3f[0] -= f * normal3f[0];
|
||||||
|
svector3f[1] -= f * normal3f[1];
|
||||||
|
svector3f[2] -= f * normal3f[2];
|
||||||
|
f = DotProduct(tvector3f, normal3f);
|
||||||
|
tvector3f[0] -= f * normal3f[0];
|
||||||
|
tvector3f[1] -= f * normal3f[1];
|
||||||
|
tvector3f[2] -= f * normal3f[2];
|
||||||
|
// if texture is mapped the wrong way (counterclockwise), the tangents
|
||||||
|
// have to be flipped, this is detected by calculating a normal from the
|
||||||
|
// two tangents, and seeing if it is opposite the surface normal
|
||||||
|
// 9 multiply, 2 add, 3 subtract, 1 compare, 50% chance of: 6 negates
|
||||||
|
CrossProduct(tvector3f, svector3f, tangentcross);
|
||||||
|
if (DotProduct(tangentcross, normal3f) < 0)
|
||||||
|
{
|
||||||
|
VectorNegate(svector3f, svector3f);
|
||||||
|
VectorNegate(tvector3f, tvector3f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//courtesy of DP
|
||||||
|
void R_AliasGenerateTextureVectors(mesh_t *mesh, float *normal3f, float *svector3f, float *tvector3f)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
float sdir[3], tdir[3], normal[3], *v;
|
||||||
|
int *e;
|
||||||
|
float *vertex3f = (float*)mesh->xyz_array;
|
||||||
|
float *texcoord2f = (float*)mesh->st_array;
|
||||||
|
// clear the vectors
|
||||||
|
// if (svector3f)
|
||||||
|
memset(svector3f, 0, mesh->numvertexes * sizeof(float[3]));
|
||||||
|
// if (tvector3f)
|
||||||
|
memset(tvector3f, 0, mesh->numvertexes * sizeof(float[3]));
|
||||||
|
// if (normal3f)
|
||||||
|
memset(normal3f, 0, mesh->numvertexes * sizeof(float[3]));
|
||||||
|
// process each vertex of each triangle and accumulate the results
|
||||||
|
for (e = mesh->indexes; e < mesh->indexes+mesh->numindexes; e += 3)
|
||||||
|
{
|
||||||
|
R_BuildBumpVectors(vertex3f + e[0] * 3, vertex3f + e[1] * 3, vertex3f + e[2] * 3, texcoord2f + e[0] * 2, texcoord2f + e[1] * 2, texcoord2f + e[2] * 2, sdir, tdir, normal);
|
||||||
|
// if (!areaweighting)
|
||||||
|
// {
|
||||||
|
// VectorNormalize(sdir);
|
||||||
|
// VectorNormalize(tdir);
|
||||||
|
// VectorNormalize(normal);
|
||||||
|
// }
|
||||||
|
// if (svector3f)
|
||||||
|
for (i = 0;i < 3;i++)
|
||||||
|
VectorAdd(svector3f + e[i]*3, sdir, svector3f + e[i]*3);
|
||||||
|
// if (tvector3f)
|
||||||
|
for (i = 0;i < 3;i++)
|
||||||
|
VectorAdd(tvector3f + e[i]*3, tdir, tvector3f + e[i]*3);
|
||||||
|
// if (normal3f)
|
||||||
|
for (i = 0;i < 3;i++)
|
||||||
|
VectorAdd(normal3f + e[i]*3, normal, normal3f + e[i]*3);
|
||||||
|
}
|
||||||
|
// now we could divide the vectors by the number of averaged values on
|
||||||
|
// each vertex... but instead normalize them
|
||||||
|
// 4 assignments, 1 divide, 1 sqrt, 2 adds, 6 multiplies
|
||||||
|
if (svector3f)
|
||||||
|
for (i = 0, v = svector3f;i < mesh->numvertexes;i++, v += 3)
|
||||||
|
VectorNormalize(v);
|
||||||
|
// 4 assignments, 1 divide, 1 sqrt, 2 adds, 6 multiplies
|
||||||
|
if (tvector3f)
|
||||||
|
for (i = 0, v = tvector3f;i < mesh->numvertexes;i++, v += 3)
|
||||||
|
VectorNormalize(v);
|
||||||
|
// 4 assignments, 1 divide, 1 sqrt, 2 adds, 6 multiplies
|
||||||
|
if (normal3f)
|
||||||
|
for (i = 0, v = normal3f;i < mesh->numvertexes;i++, v += 3)
|
||||||
|
VectorNormalize(v);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void R_AliasGenerateVertexLightDirs(mesh_t *mesh, vec3_t lightdir, vec3_t *results, vec3_t *normal3f, vec3_t *svector3f, vec3_t *tvector3f)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
R_AliasGenerateTextureVectors(mesh, (float*)normal3f, (float*)svector3f, (float*)tvector3f);
|
||||||
|
|
||||||
|
for (i = 0; i < mesh->numvertexes; i++)
|
||||||
|
{
|
||||||
|
results[i][0] = -DotProduct(lightdir, tvector3f[i]);
|
||||||
|
results[i][1] = -DotProduct(lightdir, svector3f[i]);
|
||||||
|
results[i][2] = -DotProduct(lightdir, normal3f[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void R_DrawMeshBumpmap(mesh_t *mesh, galiastexnum_t *skin, vec3_t lightdir)
|
||||||
|
{
|
||||||
|
extern int gldepthfunc;
|
||||||
|
static vec3_t *lightdirs;
|
||||||
|
static int maxlightdirs;
|
||||||
|
extern int normalisationCubeMap;
|
||||||
|
|
||||||
|
#ifdef Q3SHADERS
|
||||||
|
R_UnlockArrays();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
//(bumpmap dot cubemap)*texture
|
||||||
|
|
||||||
|
//why no luma?
|
||||||
|
//that's thrown on last.
|
||||||
|
|
||||||
|
//why a cubemap?
|
||||||
|
//we need to pass colours as a normal somehow
|
||||||
|
//we could use the fragment colour for it, however, we then wouldn't be able to colour the light.
|
||||||
|
//so we use a cubemap, which has the added advantage of normalizing the light dir for us.
|
||||||
|
|
||||||
|
//the bumpmap we use is tangent-space (so I'm told)
|
||||||
|
qglDepthFunc(GL_LEQUAL);
|
||||||
|
qglDepthMask(0);
|
||||||
|
if (gldepthmin == 0.5)
|
||||||
|
qglCullFace ( GL_BACK );
|
||||||
|
else
|
||||||
|
qglCullFace ( GL_FRONT );
|
||||||
|
|
||||||
|
qglVertexPointer(3, GL_FLOAT, 0, mesh->xyz_array);
|
||||||
|
qglEnableClientState( GL_VERTEX_ARRAY );
|
||||||
|
|
||||||
|
if (mesh->normals_array && qglNormalPointer) //d3d wrapper doesn't support normals, and this is only really needed for truform
|
||||||
|
{
|
||||||
|
qglNormalPointer(GL_FLOAT, 0, mesh->normals_array);
|
||||||
|
qglEnableClientState( GL_NORMAL_ARRAY );
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mesh->colors_array)
|
||||||
|
{
|
||||||
|
qglColorPointer(4, GL_UNSIGNED_BYTE, 0, mesh->colors_array);
|
||||||
|
qglEnableClientState( GL_COLOR_ARRAY );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
qglDisableClientState( GL_COLOR_ARRAY );
|
||||||
|
|
||||||
|
|
||||||
|
if (maxlightdirs < mesh->numvertexes)
|
||||||
|
{
|
||||||
|
maxlightdirs = mesh->numvertexes;
|
||||||
|
lightdirs = BZ_Malloc(sizeof(vec3_t)*maxlightdirs*4);
|
||||||
|
}
|
||||||
|
|
||||||
|
R_AliasGenerateVertexLightDirs(mesh, lightdir,
|
||||||
|
lightdirs + maxlightdirs*0,
|
||||||
|
lightdirs + maxlightdirs*1,
|
||||||
|
lightdirs + maxlightdirs*2,
|
||||||
|
lightdirs + maxlightdirs*3);
|
||||||
|
|
||||||
|
GL_MBind(mtexid0, skin->bump);
|
||||||
|
GL_TexEnv(GL_REPLACE);
|
||||||
|
qglEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||||
|
qglTexCoordPointer(2, GL_FLOAT, 0, mesh->st_array);
|
||||||
|
qglEnable(GL_TEXTURE_2D);
|
||||||
|
|
||||||
|
GL_SelectTexture(mtexid1);
|
||||||
|
GL_BindType(GL_TEXTURE_CUBE_MAP_ARB, normalisationCubeMap);
|
||||||
|
qglEnable(GL_TEXTURE_CUBE_MAP_ARB);
|
||||||
|
qglTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE);
|
||||||
|
qglTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PREVIOUS_ARB);
|
||||||
|
qglTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_DOT3_RGB_ARB);
|
||||||
|
GL_TexEnv(GL_COMBINE_ARB);
|
||||||
|
|
||||||
|
qglEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||||
|
qglTexCoordPointer(3, GL_FLOAT, 0, lightdirs);
|
||||||
|
|
||||||
|
if (gl_mtexarbable>=3)
|
||||||
|
{
|
||||||
|
GL_MBind(mtexid0+2, skin->base);
|
||||||
|
qglEnable(GL_TEXTURE_2D);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{ //we don't support 3tmus, so draw the bumps, and multiply the rest over the top
|
||||||
|
qglDrawElements(GL_TRIANGLES, mesh->numindexes, GL_UNSIGNED_INT, mesh->indexes);
|
||||||
|
glDisable(GL_TEXTURE_CUBE_MAP_ARB);
|
||||||
|
GL_MBind(mtexid0, skin->base);
|
||||||
|
}
|
||||||
|
GL_TexEnv(GL_MODULATE);
|
||||||
|
qglEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||||
|
qglTexCoordPointer(2, GL_FLOAT, 0, mesh->st_array);
|
||||||
|
|
||||||
|
qglDrawElements(GL_TRIANGLES, mesh->numindexes, GL_UNSIGNED_INT, mesh->indexes);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// GL_SelectTexture(mtexid2);
|
||||||
|
qglDisableClientState( GL_TEXTURE_COORD_ARRAY );
|
||||||
|
qglDisable(GL_TEXTURE_2D);
|
||||||
|
|
||||||
|
GL_SelectTexture(mtexid1);
|
||||||
|
glDisable(GL_TEXTURE_CUBE_MAP_ARB);
|
||||||
|
qglDisableClientState( GL_TEXTURE_COORD_ARRAY );
|
||||||
|
GL_TexEnv(GL_MODULATE);
|
||||||
|
|
||||||
|
GL_SelectTexture(mtexid0);
|
||||||
|
qglEnable(GL_TEXTURE_2D);
|
||||||
|
qglDisableClientState( GL_TEXTURE_COORD_ARRAY );
|
||||||
|
|
||||||
|
qglDisableClientState( GL_VERTEX_ARRAY );
|
||||||
|
qglDisableClientState( GL_COLOR_ARRAY );
|
||||||
|
qglDisableClientState( GL_NORMAL_ARRAY );
|
||||||
|
|
||||||
|
#ifdef Q3SHADERS
|
||||||
|
R_IBrokeTheArrays();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
void R_DrawGAliasModelLighting (entity_t *e, vec3_t lightpos, vec3_t colours, float radius)
|
void R_DrawGAliasModelLighting (entity_t *e, vec3_t lightpos, vec3_t colours, float radius)
|
||||||
{
|
{
|
||||||
#if 1
|
#if 1
|
||||||
|
@ -2101,7 +2352,7 @@ void R_DrawGAliasModelLighting (entity_t *e, vec3_t lightpos, vec3_t colours, fl
|
||||||
GL_TexEnv(GL_REPLACE);
|
GL_TexEnv(GL_REPLACE);
|
||||||
// qglDisable(GL_STENCIL_TEST);
|
// qglDisable(GL_STENCIL_TEST);
|
||||||
qglEnable(GL_BLEND);
|
qglEnable(GL_BLEND);
|
||||||
qglEnable(GL_ALPHA_TEST); //if you used an alpha channel where you shouldn't have, more fool you.
|
qglDisable(GL_ALPHA_TEST); //if you used an alpha channel where you shouldn't have, more fool you.
|
||||||
qglBlendFunc(GL_ONE, GL_ONE);
|
qglBlendFunc(GL_ONE, GL_ONE);
|
||||||
// qglDepthFunc(GL_ALWAYS);
|
// qglDepthFunc(GL_ALWAYS);
|
||||||
for(surfnum=0;inf;surfnum++)
|
for(surfnum=0;inf;surfnum++)
|
||||||
|
@ -2111,8 +2362,15 @@ void R_DrawGAliasModelLighting (entity_t *e, vec3_t lightpos, vec3_t colours, fl
|
||||||
|
|
||||||
tex = GL_ChooseSkin(inf, clmodel->name, surfnum, e);
|
tex = GL_ChooseSkin(inf, clmodel->name, surfnum, e);
|
||||||
|
|
||||||
GL_LightMesh(&mesh, lightdir, colours, radius);
|
if (tex->bump && e->alpha==1)
|
||||||
GL_DrawAliasMesh(&mesh, tex->base);
|
{
|
||||||
|
R_DrawMeshBumpmap(&mesh, tex, lightdir);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
GL_LightMesh(&mesh, lightdir, colours, radius);
|
||||||
|
GL_DrawAliasMesh(&mesh, tex->base);
|
||||||
|
}
|
||||||
|
|
||||||
if (inf->nextsurf)
|
if (inf->nextsurf)
|
||||||
inf = (galiasinfo_t*)((char *)inf + inf->nextsurf);
|
inf = (galiasinfo_t*)((char *)inf + inf->nextsurf);
|
||||||
|
@ -2637,6 +2895,7 @@ static void *Q1_LoadSkins (daliasskintype_t *pskintype, qboolean alpha)
|
||||||
#else
|
#else
|
||||||
static void *Q1_LoadSkins (daliasskintype_t *pskintype, qboolean alpha)
|
static void *Q1_LoadSkins (daliasskintype_t *pskintype, qboolean alpha)
|
||||||
{
|
{
|
||||||
|
extern cvar_t gl_bump;
|
||||||
extern int gl_bumpmappingpossible;
|
extern int gl_bumpmappingpossible;
|
||||||
galiastexnum_t *texnums;
|
galiastexnum_t *texnums;
|
||||||
char skinname[MAX_QPATH];
|
char skinname[MAX_QPATH];
|
||||||
|
@ -2649,6 +2908,7 @@ static void *Q1_LoadSkins (daliasskintype_t *pskintype, qboolean alpha)
|
||||||
|
|
||||||
int texture;
|
int texture;
|
||||||
int fbtexture;
|
int fbtexture;
|
||||||
|
int bumptexture;
|
||||||
|
|
||||||
s = pq1inmodel->skinwidth*pq1inmodel->skinheight;
|
s = pq1inmodel->skinwidth*pq1inmodel->skinheight;
|
||||||
for (i = 0; i < pq1inmodel->numskins; i++)
|
for (i = 0; i < pq1inmodel->numskins; i++)
|
||||||
|
@ -2661,12 +2921,18 @@ static void *Q1_LoadSkins (daliasskintype_t *pskintype, qboolean alpha)
|
||||||
|
|
||||||
//LH's naming scheme ("models" is likly to be ignored)
|
//LH's naming scheme ("models" is likly to be ignored)
|
||||||
fbtexture = 0;
|
fbtexture = 0;
|
||||||
|
bumptexture = 0;
|
||||||
_snprintf(skinname, sizeof(skinname), "%s_%i.", loadmodel->name, i);
|
_snprintf(skinname, sizeof(skinname), "%s_%i.", loadmodel->name, i);
|
||||||
texture = Mod_LoadReplacementTexture(skinname, "models", true, false, true);
|
texture = Mod_LoadReplacementTexture(skinname, "models", true, false, true);
|
||||||
if (texture)
|
if (texture)
|
||||||
{
|
{
|
||||||
_snprintf(skinname, sizeof(skinname), "%s_%i_luma.", loadmodel->name, i);
|
_snprintf(skinname, sizeof(skinname), "%s_%i_luma.", loadmodel->name, i);
|
||||||
fbtexture = Mod_LoadReplacementTexture(skinname, "models", true, false, true);
|
fbtexture = Mod_LoadReplacementTexture(skinname, "models", true, false, true);
|
||||||
|
if (gl_bump.value)
|
||||||
|
{
|
||||||
|
sprintf(skinname, "%s_%i_bump", loadmodel->name, i);
|
||||||
|
bumptexture = Mod_LoadBumpmapTexture(skinname, "models");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -2677,6 +2943,11 @@ static void *Q1_LoadSkins (daliasskintype_t *pskintype, qboolean alpha)
|
||||||
sprintf(skinname, "%s_%i_luma", loadname, i);
|
sprintf(skinname, "%s_%i_luma", loadname, i);
|
||||||
fbtexture = Mod_LoadReplacementTexture(skinname, "models", true, true, true);
|
fbtexture = Mod_LoadReplacementTexture(skinname, "models", true, true, true);
|
||||||
}
|
}
|
||||||
|
if (texture && gl_bump.value)
|
||||||
|
{
|
||||||
|
sprintf(skinname, "%s_%i_bump", loadname, i);
|
||||||
|
bumptexture = Mod_LoadBumpmapTexture(skinname, "models");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//but only preload it if we have no replacement.
|
//but only preload it if we have no replacement.
|
||||||
|
@ -2697,6 +2968,11 @@ static void *Q1_LoadSkins (daliasskintype_t *pskintype, qboolean alpha)
|
||||||
sprintf(skinname, "%s__%i_luma", loadname, i);
|
sprintf(skinname, "%s__%i_luma", loadname, i);
|
||||||
fbtexture = GL_LoadTextureFB(skinname, outskin->skinwidth, outskin->skinheight, saved, true, true);
|
fbtexture = GL_LoadTextureFB(skinname, outskin->skinwidth, outskin->skinheight, saved, true, true);
|
||||||
}
|
}
|
||||||
|
if (gl_bump.value)
|
||||||
|
{
|
||||||
|
sprintf(skinname, "%s__%i_bump", loadname, i);
|
||||||
|
bumptexture = GL_LoadTexture8Bump(skinname, outskin->skinwidth, outskin->skinheight, saved, true, true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
texnums = Hunk_Alloc(sizeof(*texnums));
|
texnums = Hunk_Alloc(sizeof(*texnums));
|
||||||
|
@ -2712,6 +2988,7 @@ static void *Q1_LoadSkins (daliasskintype_t *pskintype, qboolean alpha)
|
||||||
|
|
||||||
texnums->base = texture;
|
texnums->base = texture;
|
||||||
texnums->fullbright = fbtexture;
|
texnums->fullbright = fbtexture;
|
||||||
|
texnums->bump = bumptexture;
|
||||||
|
|
||||||
pskintype = (daliasskintype_t *)((char *)(pskintype+1)+s);
|
pskintype = (daliasskintype_t *)((char *)(pskintype+1)+s);
|
||||||
break;
|
break;
|
||||||
|
@ -3892,7 +4169,10 @@ void GL_LoadQ3Model(model_t *mod, void *buffer)
|
||||||
|
|
||||||
hunkend = Hunk_LowMark ();
|
hunkend = Hunk_LowMark ();
|
||||||
|
|
||||||
mod->flags = LittleLong(header->flags);
|
if (mod_md3flags.value)
|
||||||
|
mod->flags = LittleLong(header->flags);
|
||||||
|
else
|
||||||
|
mod->flags = 0;
|
||||||
if (!mod->flags)
|
if (!mod->flags)
|
||||||
mod->flags = Mod_ReadFlagsFromMD1(mod->name, 0);
|
mod->flags = Mod_ReadFlagsFromMD1(mod->name, 0);
|
||||||
|
|
||||||
|
@ -4205,6 +4485,349 @@ void GLMod_LoadZymoticModel(model_t *mod, void *buffer)
|
||||||
mod->funcs.Trace = GLMod_Trace;
|
mod->funcs.Trace = GLMod_Trace;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////
|
||||||
|
//dpm
|
||||||
|
|
||||||
|
|
||||||
|
// header for the entire file
|
||||||
|
typedef struct dpmheader_s
|
||||||
|
{
|
||||||
|
char id[16]; // "DARKPLACESMODEL\0", length 16
|
||||||
|
unsigned int type; // 2 (hierarchical skeletal pose)
|
||||||
|
unsigned int filesize; // size of entire model file
|
||||||
|
float mins[3], maxs[3], yawradius, allradius; // for clipping uses
|
||||||
|
|
||||||
|
// these offsets are relative to the file
|
||||||
|
unsigned int num_bones;
|
||||||
|
unsigned int num_meshs;
|
||||||
|
unsigned int num_frames;
|
||||||
|
unsigned int ofs_bones; // dpmbone_t bone[num_bones];
|
||||||
|
unsigned int ofs_meshs; // dpmmesh_t mesh[num_meshs];
|
||||||
|
unsigned int ofs_frames; // dpmframe_t frame[num_frames];
|
||||||
|
} dpmheader_t;
|
||||||
|
|
||||||
|
// there may be more than one of these
|
||||||
|
typedef struct dpmmesh_s
|
||||||
|
{
|
||||||
|
// these offsets are relative to the file
|
||||||
|
char shadername[32]; // name of the shader to use
|
||||||
|
unsigned int num_verts;
|
||||||
|
unsigned int num_tris;
|
||||||
|
unsigned int ofs_verts; // dpmvertex_t vert[numvertices]; // see vertex struct
|
||||||
|
unsigned int ofs_texcoords; // float texcoords[numvertices][2];
|
||||||
|
unsigned int ofs_indices; // unsigned int indices[numtris*3]; // designed for glDrawElements (each triangle is 3 unsigned int indices)
|
||||||
|
unsigned int ofs_groupids; // unsigned int groupids[numtris]; // the meaning of these values is entirely up to the gamecode and modeler
|
||||||
|
} dpmmesh_t;
|
||||||
|
|
||||||
|
// if set on a bone, it must be protected from removal
|
||||||
|
#define DPMBONEFLAG_ATTACHMENT 1
|
||||||
|
|
||||||
|
// one per bone
|
||||||
|
typedef struct dpmbone_s
|
||||||
|
{
|
||||||
|
// name examples: upperleftarm leftfinger1 leftfinger2 hand, etc
|
||||||
|
char name[32];
|
||||||
|
// parent bone number
|
||||||
|
signed int parent;
|
||||||
|
// flags for the bone
|
||||||
|
unsigned int flags;
|
||||||
|
} dpmbone_t;
|
||||||
|
|
||||||
|
// a bonepose matrix is intended to be used like this:
|
||||||
|
// (n = output vertex, v = input vertex, m = matrix, f = influence)
|
||||||
|
// n[0] = v[0] * m[0][0] + v[1] * m[0][1] + v[2] * m[0][2] + f * m[0][3];
|
||||||
|
// n[1] = v[0] * m[1][0] + v[1] * m[1][1] + v[2] * m[1][2] + f * m[1][3];
|
||||||
|
// n[2] = v[0] * m[2][0] + v[1] * m[2][1] + v[2] * m[2][2] + f * m[2][3];
|
||||||
|
typedef struct dpmbonepose_s
|
||||||
|
{
|
||||||
|
float matrix[3][4];
|
||||||
|
} dpmbonepose_t;
|
||||||
|
|
||||||
|
// immediately followed by bone positions for the frame
|
||||||
|
typedef struct dpmframe_s
|
||||||
|
{
|
||||||
|
// name examples: idle_1 idle_2 idle_3 shoot_1 shoot_2 shoot_3, etc
|
||||||
|
char name[32];
|
||||||
|
float mins[3], maxs[3], yawradius, allradius;
|
||||||
|
int ofs_bonepositions; // dpmbonepose_t bonepositions[bones];
|
||||||
|
} dpmframe_t;
|
||||||
|
|
||||||
|
// one or more of these per vertex
|
||||||
|
typedef struct dpmbonevert_s
|
||||||
|
{
|
||||||
|
float origin[3]; // vertex location (these blend)
|
||||||
|
float influence; // influence fraction (these must add up to 1)
|
||||||
|
float normal[3]; // surface normal (these blend)
|
||||||
|
unsigned int bonenum; // number of the bone
|
||||||
|
} dpmbonevert_t;
|
||||||
|
|
||||||
|
// variable size, parsed sequentially
|
||||||
|
typedef struct dpmvertex_s
|
||||||
|
{
|
||||||
|
unsigned int numbones;
|
||||||
|
// immediately followed by 1 or more dpmbonevert_t structures
|
||||||
|
} dpmvertex_t;
|
||||||
|
|
||||||
|
void GLMod_LoadDarkPlacesModel(model_t *mod, void *buffer)
|
||||||
|
{
|
||||||
|
#ifndef SERVERONLY
|
||||||
|
galiasskin_t *skin;
|
||||||
|
galiastexnum_t *texnum;
|
||||||
|
int skinfiles;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int i, j, k;
|
||||||
|
int hunkstart, hunkend, hunktotal;
|
||||||
|
|
||||||
|
dpmheader_t *header;
|
||||||
|
galiasinfo_t *root, *m;
|
||||||
|
dpmmesh_t *mesh;
|
||||||
|
dpmvertex_t *vert;
|
||||||
|
dpmbonevert_t *bonevert;
|
||||||
|
|
||||||
|
galisskeletaltransforms_t *transforms;
|
||||||
|
|
||||||
|
galiasbone_t *outbone;
|
||||||
|
dpmbone_t *inbone;
|
||||||
|
|
||||||
|
float *inst, *outst;
|
||||||
|
|
||||||
|
|
||||||
|
float *outposedata;
|
||||||
|
galiaspose_t *outpose;
|
||||||
|
galiasgroup_t *outgroups;
|
||||||
|
float *inposedata;
|
||||||
|
dpmframe_t *inframes;
|
||||||
|
|
||||||
|
unsigned int *index; index_t *outdex; // groan...
|
||||||
|
|
||||||
|
int numtransforms;
|
||||||
|
int numverts;
|
||||||
|
|
||||||
|
|
||||||
|
loadmodel=mod;
|
||||||
|
|
||||||
|
Mod_DoCRC(mod, buffer, com_filesize);
|
||||||
|
|
||||||
|
hunkstart = Hunk_LowMark ();
|
||||||
|
|
||||||
|
header = buffer;
|
||||||
|
|
||||||
|
if (memcmp(header->id, "DARKPLACESMODEL\0", 16))
|
||||||
|
Sys_Error("GLMod_LoadDarkPlacesModel: doesn't appear to be a darkplaces model!\n");
|
||||||
|
|
||||||
|
if (BigLong(header->type) != 2)
|
||||||
|
Sys_Error("GLMod_LoadDarkPlacesModel: only type 2 is supported\n");
|
||||||
|
|
||||||
|
for (i = 0; i < sizeof(dpmheader_t)/4; i++)
|
||||||
|
((int*)header)[i] = BigLong(((int*)header)[i]);
|
||||||
|
|
||||||
|
if (!header->num_bones)
|
||||||
|
Sys_Error("GLMod_LoadDarkPlacesModel: no bones\n");
|
||||||
|
if (!header->num_frames)
|
||||||
|
Sys_Error("GLMod_LoadDarkPlacesModel: no frames\n");
|
||||||
|
if (!header->num_meshs)
|
||||||
|
Sys_Error("GLMod_LoadDarkPlacesModel: no surfaces\n");
|
||||||
|
|
||||||
|
|
||||||
|
VectorCopy(header->mins, mod->mins);
|
||||||
|
VectorCopy(header->maxs, mod->maxs);
|
||||||
|
|
||||||
|
root = Hunk_AllocName(sizeof(galiasinfo_t)*header->num_meshs, loadname);
|
||||||
|
|
||||||
|
mesh = (dpmmesh_t*)((char*)buffer + header->ofs_meshs);
|
||||||
|
for (i = 0; i < header->num_meshs; i++, mesh++)
|
||||||
|
{
|
||||||
|
//work out how much memory we need to allocate
|
||||||
|
|
||||||
|
mesh->num_verts = BigLong(mesh->num_verts);
|
||||||
|
mesh->num_tris = BigLong(mesh->num_tris);
|
||||||
|
mesh->ofs_verts = BigLong(mesh->ofs_verts);
|
||||||
|
mesh->ofs_texcoords = BigLong(mesh->ofs_texcoords);
|
||||||
|
mesh->ofs_indices = BigLong(mesh->ofs_indices);
|
||||||
|
mesh->ofs_groupids = BigLong(mesh->ofs_groupids);
|
||||||
|
|
||||||
|
|
||||||
|
numverts = mesh->num_verts;
|
||||||
|
numtransforms = 0;
|
||||||
|
//count and byteswap the transformations
|
||||||
|
vert = (dpmvertex_t*)((char *)buffer+mesh->ofs_verts);
|
||||||
|
for (j = 0; j < mesh->num_verts; j++)
|
||||||
|
{
|
||||||
|
vert->numbones = BigLong(vert->numbones);
|
||||||
|
numtransforms += vert->numbones;
|
||||||
|
bonevert = (dpmbonevert_t*)(vert+1);
|
||||||
|
vert = (dpmvertex_t*)(bonevert+vert->numbones);
|
||||||
|
}
|
||||||
|
|
||||||
|
m = &root[i];
|
||||||
|
#ifdef SERVERONLY
|
||||||
|
transforms = Hunk_AllocName(numtransforms*sizeof(galisskeletaltransforms_t) + mesh->num_tris*3*sizeof(index_t), loadname);
|
||||||
|
#else
|
||||||
|
outst = Hunk_AllocName(numverts*sizeof(vec2_t) + numtransforms*sizeof(galisskeletaltransforms_t) + mesh->num_tris*3*sizeof(index_t), loadname);
|
||||||
|
m->ofs_st_array = (char*)outst - (char*)m;
|
||||||
|
m->numverts = mesh->num_verts;
|
||||||
|
inst = (float*)((char*)buffer + mesh->ofs_texcoords);
|
||||||
|
for (j = 0; j < numverts; j++, outst+=2, inst+=2)
|
||||||
|
{
|
||||||
|
outst[0] = BigFloat(inst[0]);
|
||||||
|
outst[1] = BigFloat(inst[1]);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//build the transform list.
|
||||||
|
transforms = (galisskeletaltransforms_t*)outst;
|
||||||
|
m->ofstransforms = (char*)transforms - (char*)m;
|
||||||
|
m->numtransforms = numtransforms;
|
||||||
|
vert = (dpmvertex_t*)((char *)buffer+mesh->ofs_verts);
|
||||||
|
for (j = 0; j < mesh->num_verts; j++)
|
||||||
|
{
|
||||||
|
bonevert = (dpmbonevert_t*)(vert+1);
|
||||||
|
for (k = 0; k < vert->numbones; k++, bonevert++, transforms++)
|
||||||
|
{
|
||||||
|
transforms->boneindex = BigLong(bonevert->bonenum);
|
||||||
|
transforms->vertexindex = j;
|
||||||
|
transforms->org[0] = BigFloat(bonevert->origin[0]);
|
||||||
|
transforms->org[1] = BigFloat(bonevert->origin[1]);
|
||||||
|
transforms->org[2] = BigFloat(bonevert->origin[2]);
|
||||||
|
transforms->org[3] = BigFloat(bonevert->influence);
|
||||||
|
//do nothing with the normals. :(
|
||||||
|
}
|
||||||
|
vert = (dpmvertex_t*)bonevert;
|
||||||
|
}
|
||||||
|
|
||||||
|
index = (index_t*)((char*)buffer + mesh->ofs_indices);
|
||||||
|
outdex = (index_t *)transforms;
|
||||||
|
m->ofs_indexes = (char*)outdex - (char*)m;
|
||||||
|
m->numindexes = mesh->num_tris*3;
|
||||||
|
for (j = 0; j < m->numindexes; j++)
|
||||||
|
{
|
||||||
|
*outdex++ = BigLong(*index++);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
outbone = Hunk_Alloc(sizeof(galiasbone_t)*header->num_bones);
|
||||||
|
inbone = (dpmbone_t*)((char*)buffer + header->ofs_bones);
|
||||||
|
for (i = 0; i < header->num_bones; i++)
|
||||||
|
{
|
||||||
|
outbone[i].parent = BigLong(inbone[i].parent);
|
||||||
|
if (outbone[i].parent >= i || outbone[i].parent < -1)
|
||||||
|
Sys_Error("GLMod_LoadDarkPlacesModel: bad bone index in %s\n", mod->name);
|
||||||
|
|
||||||
|
Q_strncpyz(outbone[i].name, inbone[i].name, sizeof(outbone[i].name));
|
||||||
|
//throw away the flags.
|
||||||
|
}
|
||||||
|
|
||||||
|
outgroups = Hunk_Alloc(sizeof(galiasgroup_t)*header->num_frames + sizeof(float)*header->num_frames*header->num_bones*12);
|
||||||
|
outposedata = (float*)(outgroups+header->num_frames);
|
||||||
|
|
||||||
|
inframes = (dpmframe_t*)((char*)buffer + header->ofs_frames);
|
||||||
|
for (i = 0; i < header->num_frames; i++)
|
||||||
|
{
|
||||||
|
inframes[i].ofs_bonepositions = BigLong(inframes[i].ofs_bonepositions);
|
||||||
|
inframes[i].allradius = BigLong(inframes[i].allradius);
|
||||||
|
inframes[i].yawradius = BigLong(inframes[i].yawradius);
|
||||||
|
inframes[i].mins[0] = BigLong(inframes[i].mins[0]);
|
||||||
|
inframes[i].mins[1] = BigLong(inframes[i].mins[1]);
|
||||||
|
inframes[i].mins[2] = BigLong(inframes[i].mins[2]);
|
||||||
|
inframes[i].maxs[0] = BigLong(inframes[i].maxs[0]);
|
||||||
|
inframes[i].maxs[1] = BigLong(inframes[i].maxs[1]);
|
||||||
|
inframes[i].maxs[2] = BigLong(inframes[i].maxs[2]);
|
||||||
|
|
||||||
|
outgroups[i].rate = 10;
|
||||||
|
outgroups[i].numposes = 1;
|
||||||
|
outgroups[i].isheirachical = true;
|
||||||
|
outgroups[i].poseofs = (char*)outposedata - (char*)&outgroups[i];
|
||||||
|
|
||||||
|
inposedata = (float*)((char*)buffer + inframes[i].ofs_bonepositions);
|
||||||
|
for (j = 0; j < header->num_bones*12; j++)
|
||||||
|
*outposedata++ = BigFloat(*inposedata++);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef SERVERONLY
|
||||||
|
skinfiles = GL_BuildSkinFileList(loadmodel->name);
|
||||||
|
if (skinfiles < 1)
|
||||||
|
skinfiles = 1;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
mesh = (dpmmesh_t*)((char*)buffer + header->ofs_meshs);
|
||||||
|
for (i = 0; i < header->num_meshs; i++, mesh++)
|
||||||
|
{
|
||||||
|
m = &root[i];
|
||||||
|
if (i < header->num_meshs-1)
|
||||||
|
m->nextsurf = sizeof(galiasinfo_t);
|
||||||
|
m->sharesbones = true;
|
||||||
|
|
||||||
|
m->ofsbones = (char*)outbone-(char*)m;
|
||||||
|
m->numbones = header->num_bones;
|
||||||
|
|
||||||
|
m->groups = header->num_frames;
|
||||||
|
m->groupofs = (char*)outgroups - (char*)m;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef SERVERONLY
|
||||||
|
m->numskins = 1;
|
||||||
|
#else
|
||||||
|
m->numskins = skinfiles;
|
||||||
|
|
||||||
|
skin = Hunk_Alloc((sizeof(galiasskin_t)+sizeof(galiastexnum_t))*skinfiles);
|
||||||
|
texnum = (galiastexnum_t*)(skin+skinfiles);
|
||||||
|
for (j = 0; j < skinfiles; j++, texnum++)
|
||||||
|
{
|
||||||
|
skin[j].texnums = 1; //non-sequenced skins.
|
||||||
|
skin[j].ofstexnums = (char *)texnum - (char *)&skin[j];
|
||||||
|
|
||||||
|
GL_LoadSkinFile(texnum, mesh->shadername, j, NULL, 0, 0, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
m->ofsskins = (char *)skin - (char *)m;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
root[0].sharesbones = false;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// move the complete, relocatable alias model to the cache
|
||||||
|
//
|
||||||
|
hunkend = Hunk_LowMark ();
|
||||||
|
|
||||||
|
mod->flags = Mod_ReadFlagsFromMD1(mod->name, 0); //file replacement - inherit flags from any defunc mdl files.
|
||||||
|
|
||||||
|
Hunk_Alloc(0);
|
||||||
|
hunktotal = hunkend - hunkstart;
|
||||||
|
|
||||||
|
Cache_Alloc (&mod->cache, hunktotal, loadname);
|
||||||
|
mod->type = mod_alias;
|
||||||
|
if (!mod->cache.data)
|
||||||
|
{
|
||||||
|
Hunk_FreeToLowMark (hunkstart);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
memcpy (mod->cache.data, root, hunktotal);
|
||||||
|
|
||||||
|
Hunk_FreeToLowMark (hunkstart);
|
||||||
|
|
||||||
|
|
||||||
|
mod->funcs.Trace = GLMod_Trace;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#endif //ZYMOTICMODELS
|
#endif //ZYMOTICMODELS
|
||||||
|
|
||||||
#ifdef MD5MODELS
|
#ifdef MD5MODELS
|
||||||
|
@ -4711,7 +5334,7 @@ galiasgroup_t GLMod_ParseMD5Anim(char *buffer, galiasinfo_t *prototype, void**po
|
||||||
{
|
{
|
||||||
if (prototype->numbones != numjoints)
|
if (prototype->numbones != numjoints)
|
||||||
Sys_Error("MD5ANIM: number of bones doesn't match");
|
Sys_Error("MD5ANIM: number of bones doesn't match");
|
||||||
bonelist = (char*)prototype + prototype->ofsbones;
|
bonelist = (galiasbone_t *)((char*)prototype + prototype->ofsbones);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -4967,7 +5590,7 @@ void GLMod_LoadCompositeAnim(model_t *mod, void *buffer)
|
||||||
root->groups = numgroups;
|
root->groups = numgroups;
|
||||||
if (!root->nextsurf)
|
if (!root->nextsurf)
|
||||||
break;
|
break;
|
||||||
root = (char*)root + root->nextsurf;
|
root = (galiasinfo_t*)((char*)root + root->nextsurf);
|
||||||
}
|
}
|
||||||
for (i = 0; i < numgroups; i++)
|
for (i = 0; i < numgroups; i++)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in a new issue