41b0d993f2
support RLE+luminance+alpha tga files. support half-float tga files. recognise hdr astc images. added appropriate fallbacks for astc support. load mip-less .astc files (mostly just for debugging stuff). allow packages to warn about required engine/gpu features. catch when stdin flags get changed to blocking by external libraries, to avoid fatal stalls. basic support for .mdx files (kingpin models) sort packages loaded via wildcards, by datetime then name, to avoid random ordering from certain filesystems. git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@5531 fc73d0e0-1445-4013-8a0c-d673dee63da5
660 lines
17 KiB
C
660 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;
|
|
struct iqmanim *oanim;
|
|
struct iqmpose2 *opose;
|
|
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;
|
|
byte_vec4_t *oboneweight;
|
|
unsigned short *oposedata;
|
|
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;
|
|
else if (m->numanimations && m->ofsanimations->numposes)
|
|
ivert = m->ofsanimations->poseofs->ofsverts;
|
|
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;
|
|
}
|
|
else if (m->numanimations && m->ofsanimations->numposes)
|
|
{
|
|
inorm = m->ofsanimations->poseofs->ofsnormals;
|
|
isdir = m->ofsanimations->poseofs->ofssvector;
|
|
itdir = m->ofsanimations->poseofs->ofstvector;
|
|
}
|
|
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
|
|
}
|