fteqw/plugins/models/exportiqm.c
Spoike 131a6be4bc Fix ignore command.
Add cl_lerp_driftbias and cl_lerp_driftfrac cvars, to tweak drifting. changed defaults to try to reduce clamping.
Implement ladders with nq player physics.
Fix submodel contents with nq player physics.
Implemented drawrotpic_dp for compat (incompatible with fte's earlier implementation)
Added con_textfont cvar to set fonts without uglifying menuqc/csqc drawstrings that don't specify explicit fonts.
Enemycolor and teamcolor are now true cvars, which means they now work with seta.
Move the homedir from CSIDL_PERSONAL to CSIDL_LOCAL_APPDATA, because microsoft apparently scrape all CSIDL_PERSONAL data for upload to their servers, because they don't understand 'personal'. Will still use the old homedir if it exists.
Pack signon data without wasting so much, primarily to allow abusive mods to spew larger individual signon messages (hurrah for packet fragmentation).


git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@5546 fc73d0e0-1445-4013-8a0c-d673dee63da5
2019-09-17 19:49:39 +00:00

664 lines
17 KiB
C

#ifndef GLQUAKE
#define GLQUAKE //this is shit.
#endif
#include "quakedef.h"
#include "../plugin.h"
#include "com_mesh.h"
extern plugmodfuncs_t *modfuncs;
extern plugfsfuncs_t *filefuncs;
#define IQM_MAGIC "INTERQUAKEMODEL"
#define IQM_VERSION2 2
struct iqmheader
{
char magic[16];
unsigned int version;
unsigned int filesize;
unsigned int flags;
unsigned int num_text, ofs_text;
unsigned int num_meshes, ofs_meshes;
unsigned int num_vertexarrays, num_vertexes, ofs_vertexarrays;
unsigned int num_triangles, ofs_triangles, ofs_adjacency;
unsigned int num_joints, ofs_joints;
unsigned int num_poses, ofs_poses;
unsigned int num_anims, ofs_anims;
unsigned int num_frames, num_framechannels, ofs_frames, ofs_bounds;
unsigned int num_comment, ofs_comment;
unsigned int num_extensions, ofs_extensions;
};
struct iqmmesh
{
unsigned int name;
unsigned int material;
unsigned int first_vertex, num_vertexes;
unsigned int first_triangle, num_triangles;
};
enum
{
IQM_POSITION = 0,
IQM_TEXCOORD = 1,
IQM_NORMAL = 2,
IQM_TANGENT = 3,
IQM_BLENDINDEXES = 4,
IQM_BLENDWEIGHTS = 5,
IQM_COLOR = 6,
IQM_CUSTOM = 0x10
};
enum
{
IQM_BYTE = 0,
IQM_UBYTE = 1,
IQM_SHORT = 2,
IQM_USHORT = 3,
IQM_INT = 4,
IQM_UINT = 5,
IQM_HALF = 6,
IQM_FLOAT = 7,
IQM_DOUBLE = 8,
};
struct iqmtriangle
{
unsigned int vertex[3];
};
struct iqmjoint2
{
unsigned int name;
int parent;
float translate[3], rotate[4], scale[3];
};
struct iqmpose2
{
int parent;
unsigned int mask;
float channeloffset[10];
float channelscale[10];
};
struct iqmanim
{
unsigned int name;
unsigned int first_frame, num_frames;
float framerate;
unsigned int flags;
};
enum
{
IQM_LOOP = 1<<0
};
struct iqmvertexarray
{
unsigned int type;
unsigned int flags;
unsigned int format;
unsigned int size;
unsigned int offset;
};
struct iqmbounds
{
float bbmin[3], bbmax[3];
float xyradius, radius;
};
struct iqmextension
{
unsigned int name;
unsigned int num_data, ofs_data;
unsigned int ofs_extensions; // pointer to next extension. wtf is up with this? how is this not redundant due to ofs_data?
};
struct iqmext_fte_mesh
{
unsigned int contents; //default CONTENTS_BODY
unsigned int surfaceflags; //propagates to trace_surfaceflags
unsigned int surfaceid; //the body reported to qc via trace_surface
unsigned int geomset;
unsigned int geomid;
float mindist;
float maxdist;
};
struct iqmext_fte_event
{
unsigned int anim;
float timestamp;
unsigned int evcode;
unsigned int evdata_str; //stringtable
};
static 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 void Bone_To_PosQuat4(const float *matrix, float *pos, float *quat4, float *scale)
{ //I originally ripped this function out of DP. tweaked slightly.
//http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm
float origininvscale = 1;
float origin[3];
float quat[4];
float quatscale;
float trace = matrix[0*4+0] + matrix[1*4+1] + matrix[2*4+2];
origin[0] = matrix[0*4+0];
origin[1] = matrix[1*4+0];
origin[2] = matrix[2*4+0];
scale [0] = sqrt(DotProduct(origin,origin));
origin[0] = matrix[0*4+1];
origin[1] = matrix[1*4+1];
origin[2] = matrix[2*4+1];
scale [1] = sqrt(DotProduct(origin,origin));
origin[1] = matrix[0*4+2];
origin[1] = matrix[1*4+2];
origin[2] = matrix[2*4+2];
scale [2] = sqrt(DotProduct(origin,origin));
origin[0] = matrix[0*4+3];
origin[1] = matrix[1*4+3];
origin[2] = matrix[2*4+3];
if(trace > 0)
{
float r = sqrt(1.0f + trace), inv = 0.5f / r;
quat[0] = (matrix[2*4+1] - matrix[1*4+2]) * inv;
quat[1] = (matrix[0*4+2] - matrix[2*4+0]) * inv;
quat[2] = (matrix[1*4+0] - matrix[0*4+1]) * inv;
quat[3] = 0.5f * r;
}
else if(matrix[0*4+0] > matrix[1*4+1] && matrix[0*4+0] > matrix[2*4+2])
{
float r = sqrt(1.0f + matrix[0*4+0] - matrix[1*4+1] - matrix[2*4+2]), inv = 0.5f / r;
quat[0] = 0.5f * r;
quat[1] = (matrix[1*4+0] + matrix[0*4+1]) * inv;
quat[2] = (matrix[0*4+2] + matrix[2*4+0]) * inv;
quat[3] = (matrix[2*4+1] - matrix[1*4+2]) * inv;
}
else if(matrix[1*4+1] > matrix[2*4+2])
{
float r = sqrt(1.0f + matrix[1*4+1] - matrix[0*4+0] - matrix[2*4+2]), inv = 0.5f / r;
quat[0] = (matrix[1*4+0] + matrix[0*4+1]) * inv;
quat[1] = 0.5f * r;
quat[2] = (matrix[2*4+1] + matrix[1*4+2]) * inv;
quat[3] = (matrix[0*4+2] - matrix[2*4+0]) * inv;
}
else
{
float r = sqrt(1.0f + matrix[2*4+2] - matrix[0*4+0] - matrix[1*4+1]), inv = 0.5f / r;
quat[0] = (matrix[0*4+2] + matrix[2*4+0]) * inv;
quat[1] = (matrix[2*4+1] + matrix[1*4+2]) * inv;
quat[2] = 0.5f * r;
quat[3] = (matrix[1*4+0] - matrix[0*4+1]) * inv;
}
// normalize quaternion so that it is unit length
quatscale = quat[0]*quat[0]+quat[1]*quat[1]+quat[2]*quat[2]+quat[3]*quat[3];
if (quatscale)
quatscale = (quat[3] >= 0 ? -1.0f : 1.0f) / sqrt(quatscale);
// use a negative scale on the quat because the above function produces a
// positive quat[3] and canonical quaternions have negative quat[3]
VectorScale(origin, origininvscale, pos);
Vector4Scale(quat, quatscale, quat4);
}
void Mod_ExportIQM(char *fname, int flags, galiasinfo_t *mesh)
{
int i, j, k;
vfsfile_t *f;
galiasinfo_t *m;
qbyte *data = NULL;
char *otext;
struct iqmvertexarray *ovarr;
struct iqmtriangle *otri;
struct iqmmesh *omesh;
struct iqmjoint2 *ojoint = NULL;
struct iqmanim *oanim = NULL;
struct iqmpose2 *opose = NULL;
struct
{
float min[10], max[10], scale[10];
int flags;
} *poseinfo = NULL, *pi; //per bone
struct
{ //pos3, quat4, scale3
float posquatscale[10]; //raw values, used to calibrate ranges
} *posedata = NULL, *pd; //per bone*joint
avec4_t *ivert;
vec2_t *ist;
vec3_t *overt;
vec3_t *onorm = NULL;
vec4_t *otang = NULL;
vec2_t *ost;
bone_vec4_t *oboneidx = NULL;
byte_vec4_t *oboneweight = NULL;
unsigned short *oposedata = NULL;
struct iqmheader hdr = {IQM_MAGIC, IQM_VERSION2}, *oh;
hdr.flags = flags;
hdr.num_vertexarrays = 4;
hdr.num_triangles = 0;
hdr.ofs_adjacency = 0; //noone actually uses this...
hdr.num_poses = 0;
// hdr.ofs_poses = 0;
hdr.num_anims = 0;
// hdr.ofs_anims = 0;
hdr.num_frames = 0;
hdr.num_framechannels = 0;
// hdr.ofs_frames = 0;
// hdr.ofs_bounds = 0;
hdr.num_comment = 0;
// hdr.ofs_comment = 0;
hdr.num_extensions = 0;
// hdr.ofs_extensions = 0;
hdr.num_joints = mesh->numbones;
if (hdr.num_joints)
{
float *matrix;
hdr.num_vertexarrays+= 2;
hdr.num_anims = mesh->numanimations;
for (i = 0; i < hdr.num_anims; i++)
{
hdr.num_text += strlen(mesh->ofsanimations[i].name)+1;
hdr.num_frames += mesh->ofsanimations[i].numposes;
}
if (hdr.num_frames)
{
poseinfo = malloc(sizeof(*poseinfo)*hdr.num_joints);
hdr.num_poses = hdr.num_joints;
posedata = malloc(sizeof(*posedata)*hdr.num_joints*hdr.num_poses);
//pull out the raw data and convert to the quats that we need
for (i = 0, pd = posedata; i < hdr.num_anims; i++)
for (j = 0, matrix = mesh->ofsanimations[i].boneofs; j < mesh->ofsanimations[i].numposes; j++)
for (k = 0; k < hdr.num_joints; k++)
{
Bone_To_PosQuat4(matrix, &pd->posquatscale[0], &pd->posquatscale[3], &pd->posquatscale[7]);
pd++;
matrix+=12;
}
//now figure out each poseinfo's min+max
for (i = 0, pi = poseinfo; i < hdr.num_joints; i++, pi++)
for (j = 0, pd = posedata+i; j < hdr.num_poses; j++, pd+=hdr.num_joints)
for (k = 0; k < 10; k++)
{
if (!i || pd->posquatscale[k] < pi->min[k])
pi->min[k] = pd->posquatscale[k];
if (!i || pi[i].max[k] < pd->posquatscale[k])
pi->max[k] = pd->posquatscale[k];
}
//figure out the offset+range+flags
for (i = 0, pi = poseinfo; i < hdr.num_joints; i++, pi++)
for (k = 0; k < 10; k++)
{
pi->scale[k] = pi->max[k]-pi->min[k];
if (pi->scale[k] < 1e-10f)
; //total range is tiny and won't make any real difference, drop this channel for a small saving.
else
{
pi->scale[k] /= 0xffffu; //compensate for the datatype's max
pi->flags |= 1u<<k;
hdr.num_framechannels++;
}
}
hdr.num_framechannels *= hdr.num_frames; //there'll be one for each pose*channel*frame
}
}
hdr.num_text += hdr.num_joints*32; //gah
//count needed data
for (m = mesh; m; m = m->nextsurf)
{
//can't handle the surface if its verts are weird.
if (m->shares_verts && m->shares_verts != hdr.num_meshes)
continue;
//can only handle one set of bones.
if (m->shares_bones != 0)
continue;
//and must have the same number of bones.
if (hdr.num_joints != m->numbones)
continue;
hdr.num_text += strlen(m->surfacename)+1;
if (m->ofsskins && m->ofsskins->frame)
hdr.num_text += strlen(m->ofsskins->frame->shadername)+1;
hdr.num_triangles += m->numindexes/3;
hdr.num_vertexes += m->numverts;
hdr.num_meshes++;
}
//allocate our output buffer
#define ALLOCSPACE hdr.filesize = 0; \
ALLOC(oh, sizeof(*oh)); \
ALLOC(otext, sizeof(*otext)*hdr.num_text); \
ALLOC(ovarr, sizeof(*ovarr)*hdr.num_vertexarrays); \
ALLOC(otri, sizeof(*otri)*hdr.num_triangles); \
ALLOC(overt, sizeof(*overt)*hdr.num_vertexes); \
ALLOC(ost, sizeof(*ost)*hdr.num_vertexes); \
if (mesh->ofs_skel_norm) {ALLOC(onorm, sizeof(*onorm)*hdr.num_vertexes);} \
if (mesh->ofs_skel_svect && mesh->ofs_skel_tvect) {ALLOC(otang, sizeof(*otang)*hdr.num_vertexes);} \
if (hdr.num_joints) {ALLOC(oboneweight, sizeof(*oboneweight)*hdr.num_vertexes);} \
if (hdr.num_joints) {ALLOC(oboneidx, sizeof(*oboneidx)*hdr.num_vertexes);} \
if (hdr.num_joints) {ALLOC(ojoint, sizeof(*ojoint)*hdr.num_joints);} \
if (hdr.num_anims) {ALLOC(oanim, sizeof(*oanim)*hdr.num_anims);} \
if (hdr.num_poses) {ALLOC(opose, sizeof(*opose)*hdr.num_poses);} \
if (hdr.num_framechannels) {ALLOC(oposedata, sizeof(*oposedata)*hdr.num_framechannels);} \
ALLOC(omesh, sizeof(*omesh)*hdr.num_meshes);
#define ALLOC(p,s) p=(void*)(data+hdr.filesize);hdr.filesize+=s;
ALLOCSPACE; //figure out how much space we need
data = malloc(hdr.filesize);
memset(data, 0xFE, hdr.filesize);
ALLOCSPACE; //and assign everything to the right offsets.
#undef ALLOC
#undef ALLOCSPACE
//copy over the preliminary header
*oh = hdr;
#define hdr hdr
if (omesh) oh->ofs_meshes = (qbyte*)omesh-data;
if (otext) oh->ofs_text = (qbyte*)otext-data;
if (ovarr) oh->ofs_vertexarrays = (qbyte*)ovarr-data;
if (otri) oh->ofs_triangles = (qbyte*)otri-data;
if (ojoint) oh->ofs_joints = (qbyte*)ojoint-data;
if (opose) oh->ofs_poses = (qbyte*)opose-data;
if (oposedata) oh->ofs_frames = (qbyte*)oposedata-data;
if (oanim) oh->ofs_anims = (qbyte*)oanim-data;
//set up vertex array data. we might add some padding here, in case the extra data isn't availble.
memset(ovarr, 0, sizeof(*ovarr)*oh->num_vertexarrays);
oh->num_vertexarrays=0;
ovarr->type = IQM_POSITION;
ovarr->flags = 0;
ovarr->format = IQM_FLOAT;
ovarr->size = 3;
ovarr->offset = (qbyte*)overt - data;
ovarr++;
oh->num_vertexarrays++;
ovarr->type = IQM_TEXCOORD;
ovarr->flags = 0;
ovarr->format = IQM_FLOAT;
ovarr->size = 2;
ovarr->offset = (qbyte*)ost - data;
ovarr++;
oh->num_vertexarrays++;
if (onorm)
{
ovarr->type = IQM_NORMAL;
ovarr->flags = 0;
ovarr->format = IQM_FLOAT;
ovarr->size = 3;
ovarr->offset = (qbyte*)onorm - data;
ovarr++;
oh->num_vertexarrays++;
}
if (otang)
{
ovarr->type = IQM_TANGENT;
ovarr->flags = 0;
ovarr->format = IQM_FLOAT;
ovarr->size = 4;
ovarr->offset = (qbyte*)otang - data;
ovarr++;
oh->num_vertexarrays++;
}
if (oboneidx)
{
ovarr->type = IQM_BLENDINDEXES;
ovarr->flags = 0;
ovarr->format = (MAX_BONES>65536)?IQM_UINT:((MAX_BONES>256)?IQM_USHORT:IQM_UBYTE);
ovarr->size = 4;
ovarr->offset = (qbyte*)oboneidx - data;
ovarr++;
oh->num_vertexarrays++;
}
if (oboneweight)
{
ovarr->type = IQM_BLENDWEIGHTS;
ovarr->flags = 0;
ovarr->format = IQM_BYTE;
ovarr->size = 4;
ovarr->offset = (qbyte*)oboneweight - data;
ovarr++;
oh->num_vertexarrays++;
}
/*if (orgba)
{
ovarr->type = IQM_COLOR;
ovarr->flags = 0;
ovarr->format = IQM_FLOAT;
ovarr->size = 4;
ovarr->offset = (qbyte*)orgba - data;
ovarr++;
oh->num_vertexarrays++;
}*/
if (ojoint)
{
for (i = 0; i < hdr.num_joints; i++)
{
ojoint[i].parent = mesh->ofsbones[i].parent;
ojoint[i].name = (qbyte*)otext-(data+oh->ofs_text);
strcpy(otext, mesh->ofsbones[i].name);
otext += strlen(otext)+1;
Bone_To_PosQuat4(mesh->ofsbones[i].inverse, ojoint[i].translate, ojoint[i].rotate, ojoint[i].scale);
}
}
if (opose)
{
int c;
for (i = 0, pi=poseinfo; i < hdr.num_joints; i++, pi++)
{
opose[i].parent = mesh->ofsbones[i].parent;
opose[i].mask = pi->flags;
for (k = 0; k < 10; k++)
{
opose[i].channeloffset[k] = pi->min[k];
opose[i].channelscale[k] = pi->scale[k];
}
for (j = 0, pd = posedata+i; j < hdr.num_frames; j++, pd+=hdr.num_joints)
{
for (k = 0; k < 10; k++)
{
if (opose[i].mask & (1<<k))
{
c = (pd->posquatscale[k]-pi->min[k])/pi->scale[k];
c = bound(0, c, 0xffff); //clamp it just in case (floats can be annoying)
*oposedata++ = c;
}
}
}
}
}
if (oposedata)
{
for (i = 0; i < hdr.num_joints; i++)
{
opose[i].parent = mesh->ofsbones[i].parent;
opose[i].mask = poseinfo[i].flags;
for (k = 0; k < 10; k++)
{
opose[i].channeloffset[k] = poseinfo[i].min[k];
opose[i].channelscale[k] = poseinfo[i].scale[k];
}
}
}
hdr.num_frames = 0;
for (i = 0; i < hdr.num_anims; i++, oanim++)
{
oanim->first_frame = hdr.num_frames;
oanim->num_frames = mesh->ofsanimations[i].numposes;
oanim->framerate = mesh->ofsanimations[i].rate;
oanim->flags = mesh->ofsanimations[i].loop?IQM_LOOP:0;
oanim->name = (qbyte*)otext-(data+oh->ofs_text);
strcpy(otext, mesh->ofsanimations[i].name);
otext += strlen(otext)+1;
hdr.num_frames += mesh->ofsanimations[i].numposes;
}
oh->num_anims = i;
oh->num_frames = hdr.num_frames;
//count needed data
hdr.num_triangles = 0;
hdr.num_vertexes = 0;
for (m = mesh; m; m = m->nextsurf)
{
//can't handle the surface if its verts are weird.
if (m->shares_verts && m->shares_verts != hdr.num_meshes)
continue;
//can only handle one set of bones.
if (m->shares_bones != 0)
continue;
if (hdr.num_joints != m->numbones)
continue;
omesh->name = (qbyte*)otext-(data+oh->ofs_text);
strcpy(otext, m->surfacename);
otext += strlen(otext)+1;
omesh->material = (qbyte*)otext-(data+oh->ofs_text);
if (m->ofsskins && m->ofsskins->frame)
strcpy(otext, m->ofsskins->frame->shadername);
else
strcpy(otext, "");
otext += strlen(otext)+1;
omesh->first_vertex = hdr.num_vertexes;
omesh->num_vertexes = m->numverts;
omesh->first_triangle = hdr.num_triangles;
omesh->num_triangles = m->numindexes/3;
if (m->ofs_skel_xyz)
ivert = m->ofs_skel_xyz;
#ifdef NONSKELETALMODELS
else if (m->numanimations && m->ofsanimations->numposes)
ivert = m->ofsanimations->poseofs->ofsverts;
#endif
else
ivert = NULL;
if (ivert)
{
for (i = 0; i < omesh->num_vertexes; i++)
VectorCopy (ivert[i], overt[i]);
}
else
{
for (i = 0; i < omesh->num_vertexes; i++)
VectorClear (overt[i]);
}
overt += i;
if (oboneidx)
{
bone_vec4_t *iidx = m->ofs_skel_idx;
vec4_t *iweight = m->ofs_skel_weight;
for (i = 0; i < omesh->num_vertexes; i++)
{
Vector4Copy(iidx[i], oboneidx[i]);
Vector4Scale(iweight[i], 255, oboneweight[i]);
}
oboneidx += i;
oboneweight += i;
}
if (ost)
{
ist = m->ofs_st_array;
for (i = 0; i < omesh->num_vertexes; i++)
Vector2Copy(ist[i], ost[i]);
ost += i;
}
if (onorm)
{
vec3_t *inorm, *isdir, *itdir, t;
if (m->ofs_skel_norm)
{
inorm = m->ofs_skel_norm;
isdir = m->ofs_skel_svect;
itdir = m->ofs_skel_tvect;
}
#ifdef NONSKELETALMODELS
else if (m->numanimations && m->ofsanimations->numposes)
{
inorm = m->ofsanimations->poseofs->ofsnormals;
isdir = m->ofsanimations->poseofs->ofssvector;
itdir = m->ofsanimations->poseofs->ofstvector;
}
#endif
else
{
inorm = NULL;
isdir = NULL;
itdir = NULL;
}
if (otang)
{
if (inorm && isdir && itdir)
{
for (i = 0; i < omesh->num_vertexes; i++)
{
VectorCopy (isdir[i], otang[i]);
CrossProduct_(isdir[i], inorm[i], t);
otang[i][3] = DotProduct(itdir[i], t)<0; //fourth part is simply a flag that says which direction the bitangent is in, should otherwise be a nice crossproduct result.
}
}
else
{
for (i = 0; i < omesh->num_vertexes; i++)
{
VectorClear (otang[i]);
otang[i][3] = 0;
}
}
otang += i;
}
if (inorm)
{
for (i = 0; i < omesh->num_vertexes; i++)
VectorCopy (ivert[i], onorm[i]);
}
else
{
for (i = 0; i < omesh->num_vertexes; i++)
VectorClear (onorm[i]);
}
otang += i;
onorm += i;
}
for (i = 0; i < omesh->num_triangles; i++)
{
otri[i].vertex[0] = m->ofs_indexes[i*3+0]+hdr.num_vertexes;
otri[i].vertex[1] = m->ofs_indexes[i*3+1]+hdr.num_vertexes;
otri[i].vertex[2] = m->ofs_indexes[i*3+2]+hdr.num_vertexes;
}
otri += i;
hdr.num_vertexes += omesh->num_vertexes;
hdr.num_triangles += omesh->num_triangles;
}
//and write it out
f = filefuncs->OpenVFS(fname, "wb", FS_GAMEONLY);
if (f)
{
VFS_WRITE(f, oh, oh->filesize);
VFS_CLOSE(f);
}
free(data);
free(poseinfo);
#undef hdr
}