/* Copyright (C) 1996-1997 Id Software, Inc. 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. */ #include "quakedef.h" static cvar_t r_mipskins = {CVAR_SAVE, "r_mipskins", "0"}; void Mod_AliasInit (void) { Cvar_RegisterVariable(&r_mipskins); } static void Mod_Alias_SERAddEntity(void) { R_Clip_AddBox(currentrenderentity->mins, currentrenderentity->maxs, R_Entity_Callback, currentrenderentity, NULL); } // LordHavoc: proper bounding box considerations static float aliasbboxmin[3], aliasbboxmax[3], modelyawradius, modelradius; static float vertst[MAXALIASVERTS][2]; static int vertusage[MAXALIASVERTS]; static int vertonseam[MAXALIASVERTS]; static int vertremap[MAXALIASVERTS]; static int temptris[MAXALIASTRIS][3]; static void Mod_ConvertAliasVerts (int inverts, vec3_t scale, vec3_t translate, trivertx_t *v, trivertx_t *out) { int i, j, invalidnormals = 0; float dist; vec3_t temp; for (i = 0;i < inverts;i++) { if (vertremap[i] < 0 && vertremap[i+inverts] < 0) // only used vertices need apply... continue; temp[0] = v[i].v[0] * scale[0] + translate[0]; temp[1] = v[i].v[1] * scale[1] + translate[1]; temp[2] = v[i].v[2] * scale[2] + translate[2]; // update bounding box if (temp[0] < aliasbboxmin[0]) aliasbboxmin[0] = temp[0]; if (temp[1] < aliasbboxmin[1]) aliasbboxmin[1] = temp[1]; if (temp[2] < aliasbboxmin[2]) aliasbboxmin[2] = temp[2]; if (temp[0] > aliasbboxmax[0]) aliasbboxmax[0] = temp[0]; if (temp[1] > aliasbboxmax[1]) aliasbboxmax[1] = temp[1]; if (temp[2] > aliasbboxmax[2]) aliasbboxmax[2] = temp[2]; dist = temp[0]*temp[0]+temp[1]*temp[1]; if (modelyawradius < dist) modelyawradius = dist; dist += temp[2]*temp[2]; if (modelradius < dist) modelradius = dist; j = vertremap[i]; // not onseam if (j >= 0) { VectorCopy(v[i].v, out[j].v); out[j].lightnormalindex = v[i].lightnormalindex; if (out[j].lightnormalindex >= NUMVERTEXNORMALS) { invalidnormals++; out[j].lightnormalindex = 0; } } j = vertremap[i+inverts]; // onseam if (j >= 0) { VectorCopy(v[i].v, out[j].v); out[j].lightnormalindex = v[i].lightnormalindex; if (out[j].lightnormalindex >= NUMVERTEXNORMALS) { invalidnormals++; out[j].lightnormalindex = 0; } } } if (invalidnormals) Con_Printf("Mod_ConvertAliasVerts: \"%s\", %i invalid normal indices found\n", loadmodel->name, invalidnormals); } static void Mod_MDL_LoadFrames (long datapointer, int inverts, int outverts, vec3_t scale, vec3_t translate) { daliasframetype_t *pframetype; daliasframe_t *pinframe; daliasgroup_t *group; daliasinterval_t *intervals; int i, f, pose, groupframes; float interval; animscene_t *scene; pose = 0; scene = loadmodel->animscenes; for (f = 0;f < loadmodel->numframes;f++) { pframetype = (daliasframetype_t *)datapointer; datapointer += sizeof(daliasframetype_t); if (LittleLong (pframetype->type) == ALIAS_SINGLE) { // a single frame is still treated as a group interval = 0.1f; groupframes = 1; } else { // read group header group = (daliasgroup_t *)datapointer; datapointer += sizeof(daliasgroup_t); groupframes = LittleLong (group->numframes); // intervals (time per frame) intervals = (daliasinterval_t *)datapointer; datapointer += sizeof(daliasinterval_t) * groupframes; interval = LittleFloat (intervals->interval); // FIXME: support variable framerate groups if (interval < 0.01f) Host_Error("Mod_LoadAliasGroup: invalid interval"); } // get scene name from first frame pinframe = (daliasframe_t *)datapointer; strcpy(scene->name, pinframe->name); scene->firstframe = pose; scene->framecount = groupframes; scene->framerate = 1.0f / interval; scene->loop = true; scene++; // read frames for (i = 0;i < groupframes;i++) { pinframe = (daliasframe_t *)datapointer; datapointer += sizeof(daliasframe_t); // convert to MD2 frame headers strcpy(loadmodel->mdlmd2data_frames[pose].name, pinframe->name); VectorCopy(scale, loadmodel->mdlmd2data_frames[pose].scale); VectorCopy(translate, loadmodel->mdlmd2data_frames[pose].translate); Mod_ConvertAliasVerts(inverts, scale, translate, (trivertx_t *)datapointer, loadmodel->mdlmd2data_pose + pose * outverts); datapointer += sizeof(trivertx_t) * inverts; pose++; } } } static rtexture_t *GL_SkinSplitShirt(byte *in, byte *out, int width, int height, int bits, char *name, int precache) { int i, pixels, passed; byte pixeltest[16]; for (i = 0;i < 16;i++) pixeltest[i] = (bits & (1 << i)) != 0; pixels = width*height; passed = 0; while(pixels--) { if (pixeltest[*in >> 4] && *in != 0 && *in != 255) { passed++; // turn to white while copying if (*in >= 128 && *in < 224) // backwards ranges *out = (*in & 15) ^ 15; else *out = *in & 15; } else *out = 0; in++; out++; } if (passed) return R_LoadTexture (loadmodel->texturepool, name, width, height, out - width*height, TEXTYPE_QPALETTE, (r_mipskins.integer ? TEXF_MIPMAP : 0) | (precache ? TEXF_PRECACHE : 0)); else return NULL; } static rtexture_t *GL_SkinSplit(byte *in, byte *out, int width, int height, int bits, char *name, int precache) { int i, pixels, passed; byte pixeltest[16]; for (i = 0;i < 16;i++) pixeltest[i] = (bits & (1 << i)) != 0; pixels = width*height; passed = 0; while(pixels--) { if (pixeltest[*in >> 4] && *in != 0 && *in != 255) { passed++; *out = *in; } else *out = 0; in++; out++; } if (passed) return R_LoadTexture (loadmodel->texturepool, name, width, height, out - width*height, TEXTYPE_QPALETTE, (r_mipskins.integer ? TEXF_MIPMAP : 0) | (precache ? TEXF_PRECACHE : 0)); else return NULL; } static void Mod_LoadSkin (char *basename, byte *skindata, byte *skintemp, int width, int height, skinframe_t *skinframe) { skinframe->base = loadtextureimagewithmask(loadmodel->texturepool, va("%s_normal", basename), 0, 0, false, r_mipskins.integer, true); skinframe->fog = image_masktex; skinframe->pants = NULL; skinframe->shirt = NULL; skinframe->glow = loadtextureimage(loadmodel->texturepool, va("%s_glow" , basename), 0, 0, false, r_mipskins.integer, true); skinframe->merged = NULL; if (skinframe->base) { skinframe->pants = loadtextureimage(loadmodel->texturepool, va("%s_pants" , basename), 0, 0, false, r_mipskins.integer, true); skinframe->shirt = loadtextureimage(loadmodel->texturepool, va("%s_shirt" , basename), 0, 0, false, r_mipskins.integer, true); } else { skinframe->base = loadtextureimagewithmask(loadmodel->texturepool, basename, 0, 0, false, true, true); skinframe->fog = image_masktex; if (!skinframe->base) { skinframe->pants = GL_SkinSplitShirt(skindata, skintemp, width, height, 0x0040, va("%s_pants", basename), false); // pants skinframe->shirt = GL_SkinSplitShirt(skindata, skintemp, width, height, 0x0002, va("%s_shirt", basename), false); // shirt skinframe->glow = GL_SkinSplit (skindata, skintemp, width, height, 0xC000, va("%s_glow", basename), true); // glow if (skinframe->pants || skinframe->shirt) { skinframe->base = GL_SkinSplit (skindata, skintemp, width, height, 0x3FBD, va("%s_normal", basename), false); // normal (no special colors) skinframe->merged = GL_SkinSplit (skindata, skintemp, width, height, 0x3FFF, va("%s_body", basename), true); // body (normal + pants + shirt, but not glow) } else skinframe->base = GL_SkinSplit (skindata, skintemp, width, height, 0x3FFF, va("%s_base", basename), true); // no special colors // quake model skins don't have alpha skinframe->fog = NULL; } } } #define BOUNDI(VALUE,MIN,MAX) if (VALUE < MIN || VALUE >= MAX) Host_Error("model %s has an invalid ##VALUE (%d exceeds %d - %d)\n", loadmodel->name, VALUE, MIN, MAX); #define BOUNDF(VALUE,MIN,MAX) if (VALUE < MIN || VALUE >= MAX) Host_Error("model %s has an invalid ##VALUE (%f exceeds %f - %f)\n", loadmodel->name, VALUE, MIN, MAX); void Mod_LoadAliasModel (model_t *mod, void *buffer) { int i, j, version, numverts, totalposes, totalskins, skinwidth, skinheight, totalverts, groupframes, groupskins; mdl_t *pinmodel; stvert_t *pinstverts; dtriangle_t *pintriangles; daliasskintype_t *pinskintype; daliasskingroup_t *pinskingroup; daliasskininterval_t *pinskinintervals; daliasframetype_t *pinframetype; daliasgroup_t *pinframegroup; float scales, scalet, scale[3], translate[3], interval; long datapointer, startframes, startskins; char name[MAX_QPATH]; byte *skintemp = NULL; modelyawradius = 0; modelradius = 0; datapointer = (long) buffer; pinmodel = (mdl_t *)datapointer; datapointer += sizeof(mdl_t); version = LittleLong (pinmodel->version); if (version != ALIAS_VERSION) Host_Error ("%s has wrong version number (%i should be %i)", loadmodel->name, version, ALIAS_VERSION); loadmodel->type = mod_alias; loadmodel->aliastype = ALIASTYPE_MDL; loadmodel->numskins = LittleLong(pinmodel->numskins); BOUNDI(loadmodel->numskins,0,256); skinwidth = LittleLong (pinmodel->skinwidth); BOUNDI(skinwidth,0,4096); skinheight = LittleLong (pinmodel->skinheight); BOUNDI(skinheight,0,4096); loadmodel->numverts = numverts = LittleLong(pinmodel->numverts); BOUNDI(loadmodel->numverts,0,MAXALIASVERTS); loadmodel->numtris = LittleLong(pinmodel->numtris); BOUNDI(loadmodel->numtris,0,MAXALIASTRIS); loadmodel->numframes = LittleLong(pinmodel->numframes); BOUNDI(loadmodel->numframes,0,65536); loadmodel->synctype = LittleLong (pinmodel->synctype); BOUNDI(loadmodel->synctype,0,2); loadmodel->flags = LittleLong (pinmodel->flags); for (i = 0;i < 3;i++) { scale[i] = LittleFloat (pinmodel->scale[i]); translate[i] = LittleFloat (pinmodel->scale_origin[i]); } startskins = datapointer; totalskins = 0; for (i = 0;i < loadmodel->numskins;i++) { pinskintype = (daliasskintype_t *)datapointer; datapointer += sizeof(daliasskintype_t); if (LittleLong(pinskintype->type) == ALIAS_SKIN_SINGLE) groupskins = 1; else { pinskingroup = (daliasskingroup_t *)datapointer; datapointer += sizeof(daliasskingroup_t); groupskins = LittleLong(pinskingroup->numskins); datapointer += sizeof(daliasskininterval_t) * groupskins; } for (j = 0;j < groupskins;j++) { datapointer += skinwidth * skinheight; totalskins++; } } pinstverts = (stvert_t *)datapointer; datapointer += sizeof(stvert_t) * numverts; pintriangles = (dtriangle_t *)datapointer; datapointer += sizeof(dtriangle_t) * loadmodel->numtris; startframes = datapointer; totalposes = 0; for (i = 0;i < loadmodel->numframes;i++) { pinframetype = (daliasframetype_t *)datapointer; datapointer += sizeof(daliasframetype_t); if (LittleLong (pinframetype->type) == ALIAS_SINGLE) groupframes = 1; else { pinframegroup = (daliasgroup_t *)datapointer; datapointer += sizeof(daliasgroup_t); groupframes = LittleLong(pinframegroup->numframes); datapointer += sizeof(daliasinterval_t) * groupframes; } for (j = 0;j < groupframes;j++) { datapointer += sizeof(daliasframe_t); datapointer += sizeof(trivertx_t) * numverts; totalposes++; } } // load the skins skintemp = Mem_Alloc(tempmempool, skinwidth * skinheight); loadmodel->skinscenes = Mem_Alloc(loadmodel->mempool, loadmodel->numskins * sizeof(animscene_t)); loadmodel->skinframes = Mem_Alloc(loadmodel->mempool, totalskins * sizeof(skinframe_t)); totalskins = 0; datapointer = startskins; for (i = 0;i < loadmodel->numskins;i++) { pinskintype = (daliasskintype_t *)datapointer; datapointer += sizeof(daliasskintype_t); if (pinskintype->type == ALIAS_SKIN_SINGLE) { groupskins = 1; interval = 0.1f; } else { pinskingroup = (daliasskingroup_t *)datapointer; datapointer += sizeof(daliasskingroup_t); groupskins = LittleLong (pinskingroup->numskins); pinskinintervals = (daliasskininterval_t *)datapointer; datapointer += sizeof(daliasskininterval_t) * groupskins; interval = LittleFloat(pinskinintervals[0].interval); if (interval < 0.01f) Host_Error("Mod_LoadAliasModel: invalid interval\n"); } sprintf(loadmodel->skinscenes[i].name, "skin %i", i); loadmodel->skinscenes[i].firstframe = totalskins; loadmodel->skinscenes[i].framecount = groupskins; loadmodel->skinscenes[i].framerate = 1.0f / interval; loadmodel->skinscenes[i].loop = true; for (j = 0;j < groupskins;j++) { if (groupskins > 1) sprintf (name, "%s_%i_%i", loadmodel->name, i, j); else sprintf (name, "%s_%i", loadmodel->name, i); Mod_LoadSkin(name, (byte *)datapointer, skintemp, skinwidth, skinheight, loadmodel->skinframes + totalskins); datapointer += skinwidth * skinheight; totalskins++; } } Mem_Free(skintemp); // store texture coordinates into temporary array, they will be stored after usage is determined (triangle data) scales = 1.0 / skinwidth; scalet = 1.0 / skinheight; for (i = 0;i < numverts;i++) { vertonseam[i] = LittleLong(pinstverts[i].onseam); vertst[i][0] = LittleLong(pinstverts[i].s) * scales; vertst[i][1] = LittleLong(pinstverts[i].t) * scalet; vertst[i+numverts][0] = vertst[i][0] + 0.5; vertst[i+numverts][1] = vertst[i][1]; vertusage[i] = 0; vertusage[i+numverts] = 0; } // load triangle data loadmodel->mdldata_indices = Mem_Alloc(loadmodel->mempool, sizeof(int[3]) * loadmodel->numtris); // count the vertices used for (i = 0;i < numverts*2;i++) vertusage[i] = 0; for (i = 0;i < loadmodel->numtris;i++) { temptris[i][0] = LittleLong(pintriangles[i].vertindex[0]); temptris[i][1] = LittleLong(pintriangles[i].vertindex[1]); temptris[i][2] = LittleLong(pintriangles[i].vertindex[2]); if (!LittleLong(pintriangles[i].facesfront)) // backface { if (vertonseam[temptris[i][0]]) temptris[i][0] += numverts; if (vertonseam[temptris[i][1]]) temptris[i][1] += numverts; if (vertonseam[temptris[i][2]]) temptris[i][2] += numverts; } vertusage[temptris[i][0]]++; vertusage[temptris[i][1]]++; vertusage[temptris[i][2]]++; } // build remapping table and compact array totalverts = 0; for (i = 0;i < numverts*2;i++) { if (vertusage[i]) { vertremap[i] = totalverts; vertst[totalverts][0] = vertst[i][0]; vertst[totalverts][1] = vertst[i][1]; totalverts++; } else vertremap[i] = -1; // not used at all } loadmodel->numverts = totalverts; // remap the triangle references for (i = 0;i < loadmodel->numtris;i++) { loadmodel->mdldata_indices[i*3+0] = vertremap[temptris[i][0]]; loadmodel->mdldata_indices[i*3+1] = vertremap[temptris[i][1]]; loadmodel->mdldata_indices[i*3+2] = vertremap[temptris[i][2]]; } // store the texture coordinates loadmodel->mdldata_texcoords = Mem_Alloc(loadmodel->mempool, sizeof(float[2]) * totalverts); for (i = 0;i < totalverts;i++) { loadmodel->mdldata_texcoords[i*2+0] = vertst[i][0]; loadmodel->mdldata_texcoords[i*2+1] = vertst[i][1]; } // load the frames loadmodel->animscenes = Mem_Alloc(loadmodel->mempool, sizeof(animscene_t) * loadmodel->numframes); loadmodel->mdlmd2data_frames = Mem_Alloc(loadmodel->mempool, sizeof(md2frame_t) * totalposes); loadmodel->mdlmd2data_pose = Mem_Alloc(loadmodel->mempool, sizeof(trivertx_t) * totalposes * totalverts); // LordHavoc: doing proper bbox for model aliasbboxmin[0] = aliasbboxmin[1] = aliasbboxmin[2] = 1000000000; aliasbboxmax[0] = aliasbboxmax[1] = aliasbboxmax[2] = -1000000000; Mod_MDL_LoadFrames (startframes, numverts, totalverts, scale, translate); // LordHavoc: fixed model bbox - was //FIXME: do this right //loadmodel->mins[0] = loadmodel->mins[1] = loadmodel->mins[2] = -16; //loadmodel->maxs[0] = loadmodel->maxs[1] = loadmodel->maxs[2] = 16; modelyawradius = sqrt(modelyawradius); modelradius = sqrt(modelradius); // loadmodel->modelradius = modelradius; for (j = 0;j < 3;j++) { loadmodel->normalmins[j] = aliasbboxmin[j]; loadmodel->normalmaxs[j] = aliasbboxmax[j]; loadmodel->rotatedmins[j] = -modelradius; loadmodel->rotatedmaxs[j] = modelradius; } loadmodel->yawmins[0] = loadmodel->yawmins[1] = -(loadmodel->yawmaxs[0] = loadmodel->yawmaxs[1] = modelyawradius); loadmodel->yawmins[2] = loadmodel->normalmins[2]; loadmodel->yawmaxs[2] = loadmodel->normalmaxs[2]; loadmodel->SERAddEntity = Mod_Alias_SERAddEntity; loadmodel->Draw = R_DrawAliasModel; loadmodel->DrawSky = NULL; loadmodel->DrawShadow = NULL; } static void Mod_MD2_ConvertVerts (int numverts, vec3_t scale, vec3_t translate, trivertx_t *v, trivertx_t *out) { int i, invalidnormals = 0; float dist; vec3_t temp; for (i = 0;i < numverts;i++) { VectorCopy(v[i].v, out[i].v); temp[0] = v[i].v[0] * scale[0] + translate[0]; temp[1] = v[i].v[1] * scale[1] + translate[1]; temp[2] = v[i].v[2] * scale[2] + translate[2]; // update bounding box if (temp[0] < aliasbboxmin[0]) aliasbboxmin[0] = temp[0]; if (temp[1] < aliasbboxmin[1]) aliasbboxmin[1] = temp[1]; if (temp[2] < aliasbboxmin[2]) aliasbboxmin[2] = temp[2]; if (temp[0] > aliasbboxmax[0]) aliasbboxmax[0] = temp[0]; if (temp[1] > aliasbboxmax[1]) aliasbboxmax[1] = temp[1]; if (temp[2] > aliasbboxmax[2]) aliasbboxmax[2] = temp[2]; dist = temp[0]*temp[0]+temp[1]*temp[1]; if (modelyawradius < dist) modelyawradius = dist; dist += temp[2]*temp[2]; if (modelradius < dist) modelradius = dist; out[i].lightnormalindex = v[i].lightnormalindex; if (out[i].lightnormalindex >= NUMVERTEXNORMALS) { invalidnormals++; out[i].lightnormalindex = 0; } } if (invalidnormals) Con_Printf("Mod_MD2_ConvertVerts: \"%s\", %i invalid normal indices found\n", loadmodel->name, invalidnormals); } void Mod_MD2_ReadHeader(md2_t *in, int *numglcmds) { int version, end; version = LittleLong (in->version); if (version != MD2ALIAS_VERSION) Host_Error ("%s has wrong version number (%i should be %i)", loadmodel->name, version, MD2ALIAS_VERSION); loadmodel->type = mod_alias; loadmodel->aliastype = ALIASTYPE_MD2; loadmodel->SERAddEntity = Mod_Alias_SERAddEntity; loadmodel->Draw = R_DrawAliasModel; loadmodel->DrawSky = NULL; loadmodel->DrawShadow = NULL; if (LittleLong(in->num_tris < 1) || LittleLong(in->num_tris) > MD2MAX_TRIANGLES) Host_Error ("%s has invalid number of triangles: %i", loadmodel->name, LittleLong(in->num_tris)); if (LittleLong(in->num_xyz < 1) || LittleLong(in->num_xyz) > MD2MAX_VERTS) Host_Error ("%s has invalid number of vertices: %i", loadmodel->name, LittleLong(in->num_xyz)); if (LittleLong(in->num_frames < 1) || LittleLong(in->num_frames) > MD2MAX_FRAMES) Host_Error ("%s has invalid number of frames: %i", loadmodel->name, LittleLong(in->num_frames)); if (LittleLong(in->num_skins < 0) || LittleLong(in->num_skins) > MAX_SKINS) Host_Error ("%s has invalid number of skins: %i", loadmodel->name, LittleLong(in->num_skins)); end = LittleLong(in->ofs_end); if (LittleLong(in->num_skins) >= 1 && (LittleLong(in->ofs_skins <= 0) || LittleLong(in->ofs_skins) >= end)) Host_Error ("%s is not a valid model", loadmodel->name); if (LittleLong(in->ofs_st <= 0) || LittleLong(in->ofs_st) >= end) Host_Error ("%s is not a valid model", loadmodel->name); if (LittleLong(in->ofs_tris <= 0) || LittleLong(in->ofs_tris) >= end) Host_Error ("%s is not a valid model", loadmodel->name); if (LittleLong(in->ofs_frames <= 0) || LittleLong(in->ofs_frames) >= end) Host_Error ("%s is not a valid model", loadmodel->name); if (LittleLong(in->ofs_glcmds <= 0) || LittleLong(in->ofs_glcmds) >= end) Host_Error ("%s is not a valid model", loadmodel->name); loadmodel->numskins = LittleLong(in->num_skins); loadmodel->numverts = LittleLong(in->num_xyz); // loadmodel->md2num_st = LittleLong(in->num_st); loadmodel->numtris = LittleLong(in->num_tris); loadmodel->numframes = LittleLong(in->num_frames); *numglcmds = LittleLong(in->num_glcmds); loadmodel->flags = 0; // there are no MD2 flags loadmodel->synctype = ST_RAND; } void Mod_MD2_LoadSkins(char *in) { int i; // load the skins if (loadmodel->numskins) { loadmodel->skinscenes = Mem_Alloc(loadmodel->mempool, sizeof(animscene_t) * loadmodel->numskins); loadmodel->skinframes = Mem_Alloc(loadmodel->mempool, sizeof(skinframe_t) * loadmodel->numskins); for (i = 0;i < loadmodel->numskins;i++) { loadmodel->skinscenes[i].firstframe = i; loadmodel->skinscenes[i].framecount = 1; loadmodel->skinscenes[i].loop = true; loadmodel->skinscenes[i].framerate = 10; loadmodel->skinframes[i].base = loadtextureimagewithmask (loadmodel->texturepool, in, 0, 0, true, r_mipskins.integer, true); loadmodel->skinframes[i].fog = image_masktex; loadmodel->skinframes[i].pants = NULL; loadmodel->skinframes[i].shirt = NULL; loadmodel->skinframes[i].glow = NULL; loadmodel->skinframes[i].merged = NULL; in += MD2MAX_SKINNAME; } } } /* void Mod_MD2_LoadTriangles(md2triangle_t *in) { int i, j; loadmodel->md2data_tris = Mem_Alloc(loadmodel->mempool, loadmodel->numtris * sizeof(md2triangle_t)); // swap the triangle list for (i = 0;i < loadmodel->numtris;i++) { for (j = 0;j < 3;j++) { loadmodel->md2data_tris[i].index_xyz[j] = LittleShort (in[i].index_xyz[j]); loadmodel->md2data_tris[i].index_st[j] = LittleShort (in[i].index_st[j]); if (loadmodel->md2data_tris[i].index_xyz[j] >= loadmodel->md2num_xyz) Host_Error ("%s has invalid vertex indices", loadmodel->name); if (loadmodel->md2data_tris[i].index_st[j] >= loadmodel->md2num_st) Host_Error ("%s has invalid vertex indices", loadmodel->name); } } } */ void Mod_MD2_LoadFrames(void *start) { int i, j; long datapointer; md2frame_t *pinframe; // LordHavoc: doing proper bbox for model aliasbboxmin[0] = aliasbboxmin[1] = aliasbboxmin[2] = 1000000000; aliasbboxmax[0] = aliasbboxmax[1] = aliasbboxmax[2] = -1000000000; modelyawradius = 0; modelradius = 0; datapointer = (long) start; // load the frames loadmodel->animscenes = Mem_Alloc(loadmodel->mempool, loadmodel->numframes * sizeof(animscene_t)); loadmodel->mdlmd2data_frames = Mem_Alloc(loadmodel->mempool, loadmodel->numframes * sizeof(md2frame_t)); loadmodel->mdlmd2data_pose = Mem_Alloc(loadmodel->mempool, loadmodel->numverts * loadmodel->numframes * sizeof(trivertx_t)); for (i = 0;i < loadmodel->numframes;i++) { pinframe = (md2frame_t *)datapointer; datapointer += sizeof(md2frame_t); strcpy(loadmodel->mdlmd2data_frames[i].name, pinframe->name); for (j = 0;j < 3;j++) { loadmodel->mdlmd2data_frames[i].scale[j] = LittleFloat(pinframe->scale[j]); loadmodel->mdlmd2data_frames[i].translate[j] = LittleFloat(pinframe->translate[j]); } Mod_MD2_ConvertVerts (loadmodel->numverts, loadmodel->mdlmd2data_frames[i].scale, loadmodel->mdlmd2data_frames[i].translate, (void *)datapointer, &loadmodel->mdlmd2data_pose[i * loadmodel->numverts]); datapointer += loadmodel->numverts * sizeof(trivertx_t); strcpy(loadmodel->animscenes[i].name, loadmodel->mdlmd2data_frames[i].name); loadmodel->animscenes[i].firstframe = i; loadmodel->animscenes[i].framecount = 1; loadmodel->animscenes[i].framerate = 10; loadmodel->animscenes[i].loop = true; } // LordHavoc: model bbox modelyawradius = sqrt(modelyawradius); modelradius = sqrt(modelradius); // loadmodel->modelradius = modelradius; for (j = 0;j < 3;j++) { loadmodel->normalmins[j] = aliasbboxmin[j]; loadmodel->normalmaxs[j] = aliasbboxmax[j]; loadmodel->rotatedmins[j] = -modelradius; loadmodel->rotatedmaxs[j] = modelradius; } loadmodel->yawmins[0] = loadmodel->yawmins[1] = -(loadmodel->yawmaxs[0] = loadmodel->yawmaxs[1] = modelyawradius); loadmodel->yawmins[2] = loadmodel->normalmins[2]; loadmodel->yawmaxs[2] = loadmodel->normalmaxs[2]; } void Mod_MD2_LoadGLCmds(int *in, int numglcmds) { int i; // load the draw list loadmodel->md2data_glcmds = Mem_Alloc(loadmodel->mempool, numglcmds * sizeof(int)); for (i = 0;i < numglcmds;i++) loadmodel->md2data_glcmds[i] = LittleLong(in[i]); } void Mod_LoadQ2AliasModel (model_t *mod, void *buffer) { md2_t *pinmodel; int numglcmds; long base; pinmodel = buffer; base = (long) buffer; Mod_MD2_ReadHeader(pinmodel, &numglcmds); Mod_MD2_LoadSkins((void*)(base + LittleLong(pinmodel->ofs_skins))); // Mod_MD2_LoadTriangles((void*)(base + LittleLong(pinmodel->ofs_tris))); Mod_MD2_LoadFrames((void*)(base + LittleLong(pinmodel->ofs_frames))); Mod_MD2_LoadGLCmds((void*)(base + LittleLong(pinmodel->ofs_glcmds)), numglcmds); } static void zymswapintblock(int *m, int size) { size /= 4; while(size--) { *m = BigLong(*m); m++; } } void Mod_LoadZymoticModel(model_t *mod, void *buffer) { int i, pbase, *bonecount; rtexture_t **texture; char *shadername; zymtype1header_t *pinmodel, *pheader; zymscene_t *scene; zymbone_t *bone; float corner[2], modelradius; pinmodel = (void *)buffer; if (memcmp(pinmodel->id, "ZYMOTICMODEL", 12)) Host_Error ("Mod_LoadZymoticModel: %s is not a zymotic model\n"); if (BigLong(pinmodel->type) != 1) Host_Error ("Mod_LoadZymoticModel: only type 1 (skeletal pose) models are currently supported\n"); loadmodel->type = mod_alias; loadmodel->aliastype = ALIASTYPE_ZYM; loadmodel->zymdata_header = pheader = Mem_Alloc(loadmodel->mempool, BigLong(pinmodel->filesize)); pbase = (int) pheader; memcpy(pheader, pinmodel, BigLong(pinmodel->filesize)); // byteswap header #define SWAPLONG(var) var = BigLong(var) #define SWAPFLOAT(var) var = BigFloat(var) #define SWAPVEC(var) SWAPFLOAT(var[0]);SWAPFLOAT(var[1]);SWAPFLOAT(var[2]) SWAPLONG(pheader->type); SWAPLONG(pheader->filesize); SWAPVEC(pheader->mins); SWAPVEC(pheader->maxs); SWAPFLOAT(pheader->radius); SWAPLONG(pheader->numverts); SWAPLONG(pheader->numtris); SWAPLONG(pheader->numshaders); SWAPLONG(pheader->numbones); SWAPLONG(pheader->numscenes); #define SWAPLUMPINFO(var) SWAPLONG(pheader->lump_##var.start);SWAPLONG(pheader->lump_##var.length) SWAPLUMPINFO(scenes); SWAPLUMPINFO(poses); SWAPLUMPINFO(bones); SWAPLUMPINFO(vertbonecounts); SWAPLUMPINFO(verts); SWAPLUMPINFO(texcoords); SWAPLUMPINFO(render); SWAPLUMPINFO(shaders); SWAPLUMPINFO(trizone); loadmodel->flags = 0; // there are no flags loadmodel->numframes = pheader->numscenes; loadmodel->synctype = ST_SYNC; loadmodel->numtris = pheader->numtris; // FIXME: add skin support and texturing and shaders and... loadmodel->skinscenes = Mem_Alloc(loadmodel->mempool, sizeof(animscene_t) + sizeof(skinframe_t)); loadmodel->skinscenes[0].firstframe = 0; loadmodel->skinscenes[0].framecount = 1; loadmodel->skinscenes[0].loop = true; loadmodel->skinscenes[0].framerate = 10; loadmodel->skinframes = (void *)(loadmodel->skinscenes + 1); loadmodel->skinframes->base = NULL; loadmodel->skinframes->fog = NULL; loadmodel->skinframes->pants = NULL; loadmodel->skinframes->shirt = NULL; loadmodel->skinframes->glow = NULL; loadmodel->skinframes->merged = NULL; loadmodel->numskins = 1; // go through the lumps, swapping things // zymlump_t lump_scenes; // zymscene_t scene[numscenes]; // name and other information for each scene (see zymscene struct) scene = (void *) (pheader->lump_scenes.start + pbase); loadmodel->animscenes = Mem_Alloc(loadmodel->mempool, sizeof(animscene_t) * loadmodel->numframes); for (i = 0;i < pheader->numscenes;i++) { SWAPVEC(scene->mins); SWAPVEC(scene->maxs); SWAPFLOAT(scene->radius); SWAPFLOAT(scene->framerate); SWAPLONG(scene->flags); SWAPLONG(scene->start); SWAPLONG(scene->length); memcpy(loadmodel->animscenes[i].name, scene->name, 32); loadmodel->animscenes[i].firstframe = scene->start; loadmodel->animscenes[i].framecount = scene->length; loadmodel->animscenes[i].framerate = scene->framerate; loadmodel->animscenes[i].loop = (scene->flags & ZYMSCENEFLAG_NOLOOP) == 0; scene++; } // zymlump_t lump_poses; // float pose[numposes][numbones][3][4]; // animation data zymswapintblock((void *) (pheader->lump_poses.start + pbase), pheader->lump_poses.length); // zymlump_t lump_bones; // zymbone_t bone[numbones]; bone = (void *) (pheader->lump_bones.start + pbase); for (i = 0;i < pheader->numbones;i++) { SWAPLONG(bone[i].flags); SWAPLONG(bone[i].parent); if (bone[i].parent >= i) Host_Error("Mod_LoadZymoticModel: bone[i].parent >= i\n"); } // zymlump_t lump_vertbonecounts; // int vertbonecounts[numvertices]; // how many bones influence each vertex (separate mainly to make this compress better) zymswapintblock((void *) (pheader->lump_vertbonecounts.start + pbase), pheader->lump_vertbonecounts.length); bonecount = (void *) (pheader->lump_vertbonecounts.start + pbase); for (i = 0;i < pheader->numbones;i++) if (bonecount[i] < 1) Host_Error("Mod_LoadZymoticModel: bone vertex count < 1\n"); // zymlump_t lump_verts; // zymvertex_t vert[numvertices]; // see vertex struct zymswapintblock((void *) (pheader->lump_verts.start + pbase), pheader->lump_verts.length); // zymlump_t lump_texcoords; // float texcoords[numvertices][2]; zymswapintblock((void *) (pheader->lump_texcoords.start + pbase), pheader->lump_texcoords.length); // zymlump_t lump_render; // int renderlist[rendersize]; // sorted by shader with run lengths (int count), shaders are sequentially used, each run can be used with glDrawElements (each triangle is 3 int indices) zymswapintblock((void *) (pheader->lump_render.start + pbase), pheader->lump_render.length); // zymlump_t lump_shaders; // char shadername[numshaders][32]; // shaders used on this model shadername = (void *) (pheader->lump_shaders.start + pbase); texture = (void *) shadername; for (i = 0;i < pheader->numshaders;i++) { rtexture_t *rt; rt = loadtextureimage(loadmodel->texturepool, shadername, 0, 0, true, r_mipskins.integer, true); shadername += 32; *texture++ = rt; // reuse shader name list for texture pointers } // zymlump_t lump_trizone; // byte trizone[numtris]; // see trizone explanation zymswapintblock((void *) (pheader->lump_trizone.start + pbase), pheader->lump_trizone.length); // model bbox modelradius = pheader->radius; // loadmodel->modelradius = pheader->radius; for (i = 0;i < 3;i++) { loadmodel->normalmins[i] = pheader->mins[i]; loadmodel->normalmaxs[i] = pheader->maxs[i]; loadmodel->rotatedmins[i] = -modelradius; loadmodel->rotatedmaxs[i] = modelradius; } corner[0] = max(fabs(loadmodel->normalmins[0]), fabs(loadmodel->normalmaxs[0])); corner[1] = max(fabs(loadmodel->normalmins[1]), fabs(loadmodel->normalmaxs[1])); loadmodel->yawmaxs[0] = loadmodel->yawmaxs[1] = sqrt(corner[0]*corner[0]+corner[1]*corner[1]); if (loadmodel->yawmaxs[0] > modelradius) loadmodel->yawmaxs[0] = loadmodel->yawmaxs[1] = modelradius; loadmodel->yawmins[0] = loadmodel->yawmins[1] = -loadmodel->yawmaxs[0]; loadmodel->yawmins[2] = loadmodel->normalmins[2]; loadmodel->yawmaxs[2] = loadmodel->normalmaxs[2]; loadmodel->SERAddEntity = Mod_Alias_SERAddEntity; loadmodel->Draw = R_DrawAliasModel; loadmodel->DrawSky = NULL; loadmodel->DrawShadow = NULL; }