diff --git a/CMakeLists.txt b/CMakeLists.txt index 8c1d1caa..4ab5c702 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1214,6 +1214,7 @@ IF(FTE_PLUG_HL2) plugins/hl2/hl2.c plugins/hl2/fs_vpk.c plugins/hl2/img_vtf.c + plugins/hl2/mod_hl2.c ) SET_TARGET_PROPERTIES(plug_hl2 PROPERTIES COMPILE_DEFINITIONS "FTEPLUGIN;MULTITHREAD;${FTE_LIB_DEFINES}") TARGET_LINK_LIBRARIES(plug_hl2 ${SYS_LIBS} ${ZLIB_LIBRARIES}) diff --git a/plugins/Makefile b/plugins/Makefile index b44d6b42..a82be5df 100644 --- a/plugins/Makefile +++ b/plugins/Makefile @@ -415,7 +415,7 @@ $(PLUG_PREFIX)openssl$(PLUG_NATIVE_EXT): net_ssl_openssl.c plugin.c ##################### #for compat with half-life 2's file formats -$(PLUG_PREFIX)hl2$(PLUG_NATIVE_EXT): hl2/fs_vpk.c hl2/img_vtf.c hl2/hl2.c plugin.c +$(PLUG_PREFIX)hl2$(PLUG_NATIVE_EXT): hl2/fs_vpk.c hl2/img_vtf.c hl2/mod_hl2.c hl2/hl2.c plugin.c $(CC) $(BASE_CFLAGS) $(CFLAGS) -DFTEPLUGIN -DMULTITHREAD -o $@ -shared $(PLUG_CFLAGS) $^ $(PLUG_DEFFILE) $(PLUG_LDFLAGS) #NATIVE_PLUGINS+=hl2 diff --git a/plugins/hl2/hl2.c b/plugins/hl2/hl2.c index 9624e2b9..010a326f 100644 --- a/plugins/hl2/hl2.c +++ b/plugins/hl2/hl2.c @@ -1,13 +1,14 @@ #include "../plugin.h" qboolean VPK_Init(void); qboolean VTF_Init(void); +qboolean MDL_Init(void); qboolean Plug_Init(void) { if (!VPK_Init()) return false; - if (!VTF_Init()) - return false; + VTF_Init(); + MDL_Init(); return true; } diff --git a/plugins/hl2/mod_hl2.c b/plugins/hl2/mod_hl2.c new file mode 100644 index 00000000..4dd1923a --- /dev/null +++ b/plugins/hl2/mod_hl2.c @@ -0,0 +1,611 @@ +#include "../plugin.h" +#include "../engine/common/com_mesh.h" + + +/* + Half-Life 2 / Source models store much of their data in other files. + I'm only going to try loading simple static models, so we don't need much data from the .mdl itself. + + FIXME: multiple meshes are still buggy. + FIXME: materials are not loaded properly. + FIXME: no lod stuff. +*/ + +static plugfsfuncs_t *filefuncs; +static plugmodfuncs_t *modfuncs; + +//Utility functions. silly plugins. +float Length(const vec3_t v) {return sqrt(DotProduct(v,v));} +float RadiusFromBounds (const vec3_t mins, const vec3_t maxs) +{ + int i; + vec3_t corner; + + for (i=0 ; i<3 ; i++) + { + corner[i] = fabs(mins[i]) > fabs(maxs[i]) ? fabs(mins[i]) : fabs(maxs[i]); + } + + return Length (corner); +} + +//blargh +typedef struct +{ + unsigned int magic; + unsigned int version; + unsigned int revisionid; + char name[64]; + unsigned int filesize; + + vec3_t _80; + vec3_t _92; + vec3_t mins; + vec3_t maxs; + vec3_t _128; + vec3_t _140; + unsigned int _152; + unsigned int _156; + unsigned int _160; + unsigned int _164; + unsigned int _168; + unsigned int _172; + unsigned int _176; + unsigned int _180; + unsigned int _184; + unsigned int _188; + unsigned int _192; + unsigned int _196; + unsigned int _200; + unsigned int tex_count; + unsigned int tex_ofs; + unsigned int texpath_count; + unsigned int texpath_ofs; + unsigned int texbind_count; //N slots per skin + unsigned int skin_count; + unsigned int texbind_offset;//provides skin|slot->texture mappings + unsigned int body_count; + unsigned int body_ofs; + //other stuff? +} hl2mdlheader_t; +typedef struct +{ + unsigned int name_ofs; + unsigned int surf_count; + unsigned int base; + unsigned int surf_ofs; +} hl2mdlbody_t; +typedef struct +{ + char name[64]; + unsigned int type; + unsigned int radius; + unsigned int mesh_count; + unsigned int mesh_ofs; + unsigned int vertex_count; + unsigned int _84; + unsigned int _88; + unsigned int _92; + unsigned int _96; + unsigned int _100; + unsigned int _104; + unsigned int _108; + unsigned int _112; + unsigned int _116; + unsigned int _120; + unsigned int _124; + unsigned int _128; + unsigned int _132; + unsigned int _136; + unsigned int _140; + unsigned int _144; +} hl2mdlsurf_t; +typedef struct +{ + unsigned int mat_idx; + unsigned int model_ofs; + unsigned int vert_count; + unsigned int vert_first; + unsigned int _16; + unsigned int _20; + unsigned int _24; + unsigned int _28; + unsigned int _32; + unsigned int _36; + unsigned int _40; + unsigned int _44; + unsigned int _48; + unsigned int _52; + unsigned int _56; + unsigned int _60; + unsigned int _64; + unsigned int _68; + unsigned int _72; + unsigned int _76; + unsigned int _80; + unsigned int _84; + unsigned int _88; + unsigned int _92; + unsigned int _96; + unsigned int _100; + unsigned int _104; + unsigned int _108; + unsigned int _112; +} hl2mdlmesh_t; +typedef struct +{ + unsigned int nameofs; + unsigned int _4; + unsigned int _8; + unsigned int _12; + unsigned int _16; + unsigned int _20; + unsigned int _24; + unsigned int _28; + unsigned int _32; + unsigned int _36; + unsigned int _40; + unsigned int _44; + unsigned int _48; + unsigned int _52; + unsigned int _56; + unsigned int _60; +} hl2mdltexture_t; +typedef struct +{ + unsigned int nameofs; +} hl2mdltexturepath_t; +#pragma pack(push,1) //urgh wtf is this bullshit +typedef struct +{ + unsigned int numskins; + unsigned int offsetskin; +} hl2vtxskins_t; +typedef struct +{ + unsigned short foo; + //no padding + unsigned int offsetskinname; +} hl2vtxskin_t; +typedef struct +{ + unsigned int version; + unsigned int vertcachesize; + unsigned short bonesperstrip; + unsigned short bonespertri; + unsigned int bonespervert; + unsigned int revisionid; + unsigned int lod_count; + unsigned int texreplacements_offset; + unsigned int body_count; + unsigned int body_ofs; +} hl2vtxheader_t; +typedef struct +{ + unsigned int surf_count; + unsigned int surf_ofs; +} hl2vtxbody_t; +typedef struct +{ + unsigned int lod_count; + unsigned int lod_ofs; +} hl2vtxsurf_t; +typedef struct +{ + unsigned int mesh_count; + unsigned int mesh_ofs; + float dist; +} hl2vtxlod_t; +typedef struct +{ + unsigned int stripg_count; + unsigned int stripg_ofs; + unsigned char flags; + //no padding (3 bytes) +} hl2vtxmesh_t; +typedef struct +{ + unsigned int vert_count; + unsigned int vert_ofs; + unsigned int idx_count; + unsigned int idx_ofs; + unsigned int strip_count; + unsigned int strip_ofs; + unsigned char flags; + //no padding (3 bytes) +} hl2vtxstripg_t; +typedef struct +{ + unsigned int idx_num; + unsigned int idx_ofs; + unsigned int vert_count; + unsigned int vert_ofs; + unsigned short bone_count; + unsigned char flags; + //no padding (1 byte) + unsigned int bonestate_count; + unsigned int bonestate_ofs; +} hl2vtxstrip_t; +typedef struct +{ + qbyte bone[3]; + qbyte bone_count; + unsigned short vert; + qbyte boneID[3]; + //no padding +} hl2vtxvert_t; +typedef struct +{ + unsigned int magic; + unsigned int version; + unsigned int revisionid; + unsigned int lod_count; + unsigned int lodverts_count[8]; + unsigned int fixups_count; + unsigned int fixups_offset; + unsigned int verts_offset; + unsigned int tangents_offset; +} hl2vvdheader_t; +typedef struct +{ + unsigned int lod; + unsigned int sourcevert; + unsigned int numverts; +} hl2vvdfixup_t; +typedef struct +{ + float weight[3]; + qbyte bone[3]; + qbyte numbones; + vec3_t xyz; + vec3_t norm; + vec2_t st; +} hl2vvdvert_t; +#pragma pack(pop) +/*seriously, how many structs do you need?*/ +typedef struct +{ + model_t *mod; + + unsigned numverts; + vec2_t *ofs_st_array; + vecV_t *ofs_skel_xyz; + vec3_t *ofs_skel_norm; + vec3_t *ofs_skel_svect; + vec3_t *ofs_skel_tvect; +// byte_vec4_t *ofs_skel_idx; +// vec4_t *ofs_skel_weight; + struct + { + unsigned int numfixups; + index_t *fixup; + } lod[1]; //must remain at 1 (instead of 8) until fixups are handled. +} hl2parsecontext_t; + +static index_t *Mod_HL2_LoadIndexes(hl2parsecontext_t *ctx, unsigned int *idxcount, const hl2vtxmesh_t *vmesh, unsigned int lod, index_t firstindex) +{ + size_t numidx = 0, g; + const hl2vtxstripg_t *vg; + index_t *idx, *ret = NULL; + + vg = (const void*)((const qbyte*)vmesh+vmesh->stripg_ofs); + for (g = 0; g < vmesh->stripg_count; g++, vg++) + { + if (vg->idx_count%3) + { + *idxcount = 0; + return NULL; + } + numidx += vg->idx_count; + } + + ret = idx = plugfuncs->GMalloc(&ctx->mod->memgroup, sizeof(*idx)*numidx); + + vg = (const void*)((const qbyte*)vmesh+vmesh->stripg_ofs); + for (g = 0; g < vmesh->stripg_count; g++, vg++) + { + const unsigned short *in = (const void*)((const qbyte*)vg+vg->idx_ofs); + const unsigned short *e = in+vg->idx_count; + const hl2vtxvert_t *v = (const void*)((const qbyte*)vg+vg->vert_ofs); + if (ctx->lod[lod].numfixups) + { + index_t *fixup = ctx->lod[lod].fixup; + for(;;) + { + if (in == e) + break; + *idx++ = fixup[v[*in++].vert+firstindex]; + } + } + else + { + for(;;) + { + if (in == e) + break; + *idx++ = v[*in++].vert+firstindex; + } + } + } + *idxcount = idx-ret; + return ret; +} +static qboolean Mod_HL2_LoadVTX(hl2parsecontext_t *ctx, const void *buffer, size_t fsize, unsigned int rev, const hl2mdlheader_t *mdl) +{ //horribly overcomplicated way to express this stuff. + size_t totalsurfs = 0, b, s, l, m, t; + const hl2vtxheader_t *header = buffer; + const hl2vtxbody_t *vbody; + const hl2vtxsurf_t *vsurf; + const hl2vtxlod_t *vlod; + const hl2vtxmesh_t *vmesh; +// const hl2vtxskins_t *vskins; +// const hl2vtxskin_t *vskin; + + const hl2mdlbody_t *mbody = (const hl2mdlbody_t*)((const qbyte*)mdl + mdl->body_ofs); + const hl2mdltexture_t *mtex = (const hl2mdltexture_t*)((const qbyte*)mdl + mdl->tex_ofs); + const unsigned short *skinbind; + + galiasinfo_t *surf=NULL; + galiasskin_t *skin; + skinframe_t *skinframe; + size_t firstvert = 0; + + if (fsize < sizeof(*header) || header->version != 7 || header->revisionid != rev || header->body_count == 0) + return false; + + vbody = (const void*)((const qbyte*)header + header->body_ofs); + for (b = 0; b < header->body_count; b++, vbody++) + { + vsurf = (const void*)((const qbyte*)vbody + vbody->surf_ofs); + for (s = 0; s < vbody->surf_count; s++, vsurf++) + { + vlod = (const void*)((const qbyte*)vsurf + vsurf->lod_ofs); + for (l = 0; l < min(vsurf->lod_count, countof(ctx->lod)); l++, vlod++) + totalsurfs += vlod->mesh_count; + } + } + + if (!totalsurfs) + return false; + + ctx->mod->meshinfo = surf = plugfuncs->GMalloc(&ctx->mod->memgroup, sizeof(*surf)*totalsurfs); + + t = mdl->skin_count*mdl->texbind_count; + skinbind = (const unsigned short*)((const qbyte*)mdl+mdl->texbind_offset); + skin = plugfuncs->GMalloc(&ctx->mod->memgroup, sizeof(*skin)*t + sizeof(*skinframe)*t); + skinframe = (skinframe_t*)(skin+t); + for (s = 0; s < mdl->skin_count; s++) + for (t = 0; t < mdl->texbind_count; t++) + { + galiasskin_t *ns = &skin[s + t*mdl->skin_count]; + Q_snprintfz(ns->name, sizeof(ns->name), "skin%u %u", (unsigned)s, (unsigned)t); + + m = *skinbind++; + if (mdl->texpath_count) + { + const hl2mdltexturepath_t *mpath = (const hl2mdltexturepath_t*)((const qbyte*)mdl + mdl->texpath_ofs); + Q_strlcpy(skinframe->shadername, (const char*)mdl+mpath->nameofs, sizeof(skinframe->shadername)); + } + else + { + modfuncs->StripExtension((const char*)ctx->mod->name, skinframe->shadername, sizeof(skinframe->shadername)); + Q_strlcat(skinframe->shadername, "/", sizeof(skinframe->shadername)); + } + Q_strlcat(skinframe->shadername, (const char*)&mtex[m]+mtex[m].nameofs, sizeof(skinframe->shadername)); + Q_strlcat(skinframe->shadername, ".vmt", sizeof(skinframe->shadername)); + + ns->numframes = 1; //no skingroups... not that kind anyway. + ns->skinspeed = 10; + ns->frame = skinframe; + skinframe++; + } + + vbody = (const void*)((const qbyte*)header + header->body_ofs); + for (b = 0; b < header->body_count; b++, vbody++, mbody++) + { + const hl2mdlsurf_t *msurf = (const hl2mdlsurf_t*)((const qbyte*)mbody + mbody->surf_ofs); + vsurf = (const void*)((const qbyte*)vbody + vbody->surf_ofs); + for (s = 0; s < vbody->surf_count; s++, vsurf++, msurf++) + { + vlod = (const void*)((const qbyte*)vsurf + vsurf->lod_ofs); +// vskins = (const hl2vtxskins_t*)((const qbyte*)header + header->texreplacements_offset); + for (l = 0, t = 0; l < min(vsurf->lod_count, countof(ctx->lod)); l++, vlod++/*, vskins++*/) + { + const hl2mdlmesh_t *mmesh = (const hl2mdlmesh_t*)((const qbyte*)msurf + msurf->mesh_ofs); + vmesh = (const void*)((const qbyte*)vlod + vlod->mesh_ofs); + for (m = 0; m < vlod->mesh_count; m++, vmesh++, mmesh++) + { + Q_snprintfz(surf->surfacename, sizeof(surf->surfacename), "%s:%s:l%u:m%u", (const char*)mbody+mbody->name_ofs, msurf->name, (unsigned)l, (unsigned)m); + + /*animation info*/ + surf->shares_bones = 0; + surf->numbones = 0; + surf->ofsbones = NULL; //no support for animations... + surf->numanimations = 0; + surf->ofsanimations = NULL; //we have no animation data + + #ifndef SERVERONLY + /*skin data*/ + surf->numskins = mdl->skin_count; + surf->ofsskins = skin+mmesh->mat_idx*mdl->skin_count; + + /*vertdata*/ + surf->ofs_rgbaf = NULL; + surf->ofs_rgbaub = NULL; + surf->ofs_st_array = ctx->ofs_st_array; + #endif + surf->shares_verts = 0; + surf->numverts = ctx->numverts; + surf->ofs_skel_xyz = ctx->ofs_skel_xyz; + surf->ofs_skel_norm = ctx->ofs_skel_norm; + surf->ofs_skel_svect = ctx->ofs_skel_svect; + surf->ofs_skel_tvect = ctx->ofs_skel_tvect; + //surf->ofs_skel_idx = ctx->ofs_skel_idx; + //surf->ofs_skel_weight = ctx->ofs_skel_weight; + + /*index data*/ + if (mmesh->vert_first+mmesh->vert_count > surf->numverts) + surf->ofs_indexes = NULL, surf->numindexes = 0; //erk? + else + surf->ofs_indexes = Mod_HL2_LoadIndexes(ctx, &surf->numindexes, vmesh, l, firstvert+mmesh->vert_first); + + /*misc data*/ + surf->geomset = 0; + surf->geomid = 0; + surf->contents = FTECONTENTS_BODY; + surf->csurface.flags = 0; + surf->surfaceid = 0; + surf->mindist = 0; + surf->maxdist = 0; + + if (surf != ctx->mod->meshinfo) + surf[-1].nextsurf = surf; + surf->nextsurf = NULL; + surf++; + } + } + firstvert += msurf->vertex_count; + } + } + + if (surf == ctx->mod->meshinfo) + return false; + + return true; +} +void CrossProduct (const vec3_t v1, const vec3_t v2, vec3_t cross) +{ + cross[0] = v1[1]*v2[2] - v1[2]*v2[1]; + cross[1] = v1[2]*v2[0] - v1[0]*v2[2]; + cross[2] = v1[0]*v2[1] - v1[1]*v2[0]; +} +static qboolean Mod_HL2_LoadVVD(hl2parsecontext_t *ctx, const void *buffer, size_t fsize, unsigned int rev) +{ + const hl2vvdheader_t *header = buffer; + size_t lod; + const hl2vvdvert_t *in; + const vec4_t *it; + if (fsize < sizeof(*header) || header->magic != (('I'<<0)|('D'<<8)|('S'<<16)|('V'<<24)) || header->version != 4 || header->revisionid != rev || header->lodverts_count[0] == 0) + return false; + + { + size_t v, numverts = ctx->numverts = header->lodverts_count[0]; + vec2_t *st = ctx->ofs_st_array = plugfuncs->GMalloc(&ctx->mod->memgroup, sizeof(*st)*numverts); + vecV_t *xyz = ctx->ofs_skel_xyz = plugfuncs->GMalloc(&ctx->mod->memgroup, sizeof(*xyz)*numverts); + vec3_t *norm = ctx->ofs_skel_norm = plugfuncs->GMalloc(&ctx->mod->memgroup, sizeof(*norm)*numverts); + vec3_t *sdir = ctx->ofs_skel_svect = plugfuncs->GMalloc(&ctx->mod->memgroup, sizeof(*sdir)*numverts); + vec3_t *tdir = ctx->ofs_skel_tvect = plugfuncs->GMalloc(&ctx->mod->memgroup, sizeof(*tdir)*numverts); +// byte_vec4_t *bone = ctx->lod[lod].ofs_skel_idx = plugfuncs->GMalloc(&ctx->mod->memgroup, sizeof(*bone)*numverts); +// vec4_t *weight = ctx->lod[lod].ofs_skel_weight = plugfuncs->GMalloc(&ctx->mod->memgroup, sizeof(*weight)*numverts); + + in = (const void*)((const char*)buffer+header->verts_offset); + it = (const void*)((const char*)buffer+header->tangents_offset); + for(v = 0; v < numverts; v++, in++, it++) + { + Vector2Copy(in->st, st[v]); + VectorCopy(in->xyz, xyz[v]); + VectorCopy(in->norm, norm[v]); + +// VectorCopy(in->bone, bone[v]); +// bone[v][3] = bone[v][2]; //make sure its valid, and in cache. +// VectorCopy(in->weight, weight[v]); +// weight[v][3] = 0; //missing influences cannot influence. + + //tangents are compacted, and for some reason in a different part of the file. + VectorCopy((*it), sdir[v]); + CrossProduct(in->norm, (*it), tdir[v]); + VectorScale(tdir[v], (*it)[3], tdir[v]); + } + } + + if (header->fixups_count) + { + size_t fixups = header->fixups_count, f, v; + const hl2vvdfixup_t *fixup = (const hl2vvdfixup_t*)((const qbyte*)header+header->fixups_offset); + for (lod = 0; lod < countof(ctx->lod) && lod < header->lod_count; lod++) + { + size_t numverts; + for (numverts=0, f = 0; f < fixups; f++) + { + if (fixup[f].lod >= lod) + numverts += fixup[f].numverts; + } + if (numverts != header->lodverts_count[lod]) + continue; + ctx->lod[lod].numfixups = numverts; + ctx->lod[lod].fixup = plugfuncs->GMalloc(&ctx->mod->memgroup, sizeof(index_t)*numverts); + for (numverts=0, f = 0; f < fixups; f++) + { + if (fixup[f].lod >= lod) + for (v = 0; v < fixup[f].numverts; v++) + ctx->lod[lod].fixup[numverts++] = fixup[f].sourcevert+v; + } + } + } + return true; +} +qboolean QDECL Mod_LoadHL2Model (model_t *mod, void *buffer, size_t fsize) +{ + hl2parsecontext_t ctx = {mod}; + const hl2mdlheader_t *header = buffer; + void *vtx = NULL, *vvd = NULL; + size_t vtxsize = 0, vvdsize = 0; + char base[MAX_QPATH]; + qboolean result; + size_t i; + const char *vtxpostfixes[] = { + ".dx90.vtx", + ".dx80.vtx", + ".vtx", + ".sw.vtx" + }; + const char *vvdpostfixes[] = { + ".vvd" + }; + + for (i = 0; !vtx && i < countof(vtxpostfixes); i++) + { + modfuncs->StripExtension(mod->name, base, sizeof(base)); + Q_strncatz(base, vtxpostfixes[i], sizeof(base)); + vtx = filefuncs->LoadFile(base, &vtxsize); + } + for (i = 0; !vvd && i < countof(vvdpostfixes); i++) + { + modfuncs->StripExtension(mod->name, base, sizeof(base)); + Q_strncatz(base, vvdpostfixes[i], sizeof(base)); + vvd = filefuncs->LoadFile(base, &vvdsize); + } + + result = Mod_HL2_LoadVVD(&ctx, vvd, vvdsize, header->revisionid); + result &= Mod_HL2_LoadVTX(&ctx, vtx, vtxsize, header->revisionid, header); + plugfuncs->Free(vvd); + plugfuncs->Free(vtx); + + VectorCopy(header->mins, mod->mins); + VectorCopy(header->maxs, mod->maxs); + + mod->type = mod_alias; + mod->radius = RadiusFromBounds(mod->mins, mod->maxs); + modfuncs->BIH_BuildAlias(mod, mod->meshinfo); + return result; +} + +qboolean MDL_Init(void) +{ + filefuncs = plugfuncs->GetEngineInterface(plugfsfuncs_name, sizeof(*filefuncs)); + modfuncs = plugfuncs->GetEngineInterface(plugmodfuncs_name, sizeof(*modfuncs)); + if (modfuncs && modfuncs->version < MODPLUGFUNCS_VERSION) + modfuncs = NULL; + + if (modfuncs && filefuncs) + { + modfuncs->RegisterModelFormatMagic("Source model (v44)", "IDST\x2c\0\0\0",8, Mod_LoadHL2Model); + modfuncs->RegisterModelFormatMagic("Source model (v45)", "IDST\x2d\0\0\0",8, Mod_LoadHL2Model); + modfuncs->RegisterModelFormatMagic("Source model (v46)", "IDST\x2e\0\0\0",8, Mod_LoadHL2Model); + modfuncs->RegisterModelFormatMagic("Source model (v47)", "IDST\x2f\0\0\0",8, Mod_LoadHL2Model); + modfuncs->RegisterModelFormatMagic("Source model (v48)", "IDST\x30\0\0\0",8, Mod_LoadHL2Model); + modfuncs->RegisterModelFormatMagic("Source model (v49)", "IDST\x31\0\0\0",8, Mod_LoadHL2Model); + return true; + } + return false; +}