mirror of
https://github.com/nzp-team/fteqw.git
synced 2024-11-22 20:11:44 +00:00
bfa5cef4d3
git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@5620 fc73d0e0-1445-4013-8a0c-d673dee63da5
668 lines
17 KiB
C
668 lines
17 KiB
C
#ifndef GLQUAKE
|
|
#define GLQUAKE //this is shit.
|
|
#endif
|
|
#include "quakedef.h"
|
|
#include "../plugin.h"
|
|
#include "com_mesh.h"
|
|
|
|
#ifdef SKELETALMODELS
|
|
|
|
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
|
|
}
|
|
#endif
|