1
0
Fork 0
forked from fte/fteqw
fteqw/engine/client/pr_skelobj.c

1147 lines
31 KiB
C
Raw Normal View History

/*
Copyright (C) 2011 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.
*/
/*
this file deals with qc builtins to apply custom skeletal blending (skeletal objects extension), as well as the logic required to perform realtime ragdoll, if I ever implement that.
*/
#include "quakedef.h"
#if defined(CSQC_DAT) || !defined(CLIENTONLY)
#define RAGDOLL
#include "pr_common.h"
#include "com_mesh.h"
#define MAX_SKEL_OBJECTS 1024
#ifdef RAGDOLL
/*this is the description of the ragdoll, it is how the doll flops around*/
typedef struct doll_s
{
char *name;
model_t *model;
struct doll_s *next;
int numbodies;
int numjoints;
struct
{
int bone;
char *name;
} body[32];
struct
{
int type;
int body[2];
} joint[32];
} doll_t;
enum
{
BF_ACTIVE, /*used to avoid traces if doll is stationary*/
BF_INSOLID
};
typedef struct
{
int ownerent; /*multiple of 12*/
int flags;
float moment[12];
float matrix[12];
} body_t;
#endif
/*this is the skeletal object*/
typedef struct skelobject_s
{
int inuse;
model_t *model;
world_t *world; /*be it ssqc or csqc*/
enum
{
SKOT_RELATIVE,
SKOT_ABSOLUTE
} type;
unsigned int numbones;
float *bonematrix;
#ifdef RAGDOLL
struct skelobject_s *animsource;
unsigned int numbodies;
body_t *body;
doll_t *doll;
#endif
} skelobject_t;
static doll_t *dolllist;
static skelobject_t skelobjects[MAX_SKEL_OBJECTS];
static int numskelobjectsused;
static void bonemat_fromidentity(float *out)
{
out[0] = 1;
out[1] = 0;
out[2] = 0;
out[3] = 0;
out[4] = 0;
out[5] = 1;
out[6] = 0;
out[7] = 0;
out[8] = 0;
out[9] = 0;
out[10] = 1;
out[11] = 0;
}
static void bonemat_fromqcvectors(float *out, const float vx[3], const float vy[3], const float vz[3], const float t[3])
{
out[0] = vx[0];
out[1] = -vy[0];
out[2] = vz[0];
out[3] = t[0];
out[4] = vx[1];
out[5] = -vy[1];
out[6] = vz[1];
out[7] = t[1];
out[8] = vx[2];
out[9] = -vy[2];
out[10] = vz[2];
out[11] = t[2];
}
static void bonemat_fromentity(world_t *w, wedict_t *ed, float *trans)
{
vec3_t d[3], a;
model_t *mod;
mod = w->Get_CModel(w, ed->v->modelindex);
if (!mod || mod->type == mod_alias)
a[0] = -ed->v->angles[0];
else
a[0] = ed->v->angles[0];
a[1] = ed->v->angles[1];
a[2] = ed->v->angles[2];
AngleVectors(a, d[0], d[1], d[2]);
bonemat_fromqcvectors(trans, d[0], d[1], d[2], ed->v->origin);
}
static void bonemat_toqcvectors(const float *in, float vx[3], float vy[3], float vz[3], float t[3])
{
vx[0] = in[0];
vx[1] = in[4];
vx[2] = in[8];
vy[0] = -in[1];
vy[1] = -in[5];
vy[2] = -in[9];
vz[0] = in[2];
vz[1] = in[6];
vz[2] = in[10];
t [0] = in[3];
t [1] = in[7];
t [2] = in[11];
}
static void bonematident_toqcvectors(float vx[3], float vy[3], float vz[3], float t[3])
{
vx[0] = 1;
vx[1] = 0;
vx[2] = 0;
vy[0] = -0;
vy[1] = -1;
vy[2] = -0;
vz[0] = 0;
vz[1] = 0;
vz[2] = 1;
t [0] = 0;
t [1] = 0;
t [2] = 0;
}
static qboolean pendingkill; /*states that there is a skel waiting to be killed*/
#ifdef RAGDOLL
doll_t *rag_loaddoll(model_t *mod, char *fname)
{
doll_t *d;
void *fptr = NULL;
char *file;
int fsize;
int i, j;
for (d = dolllist; d; d = d->next)
{
if (d->model == mod)
if (!strcmp(d->name, fname))
return d;
}
fsize = FS_LoadFile(fname, &fptr);
if (!fptr)
return NULL;
d = malloc(sizeof(*d));
d->next = dolllist;
dolllist = d;
d->name = strdup(fname);
d->model = mod;
d->numbodies = 0;
d->numjoints = 0;
file = fptr;
while(file)
{
file = COM_Parse(file);
if (!strcmp(com_token, "body"))
{
file = COM_Parse(file);
d->body[d->numbodies].name = strdup(com_token);
file = COM_Parse(file);
d->body[d->numbodies].bone = Mod_TagNumForName(d->model, com_token);
d->numbodies++;
}
else if (!strcmp(com_token, "joint"))
{
for (i = 0; i < 2; i++)
{
file = COM_Parse(file);
d->joint[d->numjoints].body[i] = 0;
for (j = 0; j < d->numbodies; j++)
{
if (!strcmp(d->body[j].name, com_token))
{
d->joint[d->numjoints].body[i] = j;
break;
}
}
}
d->numjoints++;
}
}
FS_FreeFile(fptr);
return d;
}
void skel_integrate(progfuncs_t *prinst, skelobject_t *sko, skelobject_t *skelobjsrc, float ft, float mmat[12])
{
#if 0
trace_t t;
vec3_t npos, opos, wnpos, wopos;
vec3_t move;
float wantmat[12];
world_t *w = prinst->parms->user;
body_t *b;
float gravity = 800;
int bone, bno;
int boffs;
galiasbone_t *boneinfo = Mod_GetBoneInfo(sko->model);
if (!boneinfo)
return;
for (bone = 0, bno = 0, b = sko->body; bone < sko->numbones; bone++)
{
boffs = bone*12;
/*if this bone is positioned using a physical body...*/
if (bno < sko->numbodies && b->jointo == boffs)
{
if (skelobjsrc)
{
/*attempt to move to target*/
if (boneinfo[bone].parent >= 0)
{
Matrix3x4_Multiply(skelobjsrc->bonematrix+boffs, sko->bonematrix+12*boneinfo[bone].parent, wantmat);
}
else
{
Vector4Copy(skelobjsrc->bonematrix+boffs+0, wantmat+0);
Vector4Copy(skelobjsrc->bonematrix+boffs+4, wantmat+4);
Vector4Copy(skelobjsrc->bonematrix+boffs+8, wantmat+8);
}
b->vel[0] = (wantmat[3 ] - sko->bonematrix[b->jointo + 3 ])/ft;
b->vel[1] = (wantmat[7 ] - sko->bonematrix[b->jointo + 7 ])/ft;
b->vel[2] = (wantmat[11] - sko->bonematrix[b->jointo + 11])/ft;
}
else
{
/*handle gravity*/
b->vel[2] = b->vel[2] - gravity * ft / 2;
}
VectorScale(b->vel, ft, move);
opos[0] = sko->bonematrix[b->jointo + 3 ];
opos[1] = sko->bonematrix[b->jointo + 7 ];
opos[2] = sko->bonematrix[b->jointo + 11];
npos[0] = opos[0] + move[0];
npos[1] = opos[1] + move[1];
npos[2] = opos[2] + move[2];
Matrix3x4_RM_Transform3(mmat, opos, wopos);
Matrix3x4_RM_Transform3(mmat, npos, wnpos);
t = World_Move(w, wopos, vec3_origin, vec3_origin, wnpos, MOVE_NOMONSTERS, w->edicts);
if (t.startsolid)
t.fraction = 1;
else if (t.fraction < 1)
{
/*clip the velocity*/
float backoff = -DotProduct (b->vel, t.plane.normal) * 1.9; /*teehee, bouncy corpses*/
VectorMA(b->vel, backoff, t.plane.normal, b->vel);
}
if (skelobjsrc)
{
VectorCopy(wantmat+0, sko->bonematrix+b->jointo+0);
VectorCopy(wantmat+4, sko->bonematrix+b->jointo+4);
VectorCopy(wantmat+8, sko->bonematrix+b->jointo+8);
}
sko->bonematrix[b->jointo + 3 ] += move[0] * t.fraction;
sko->bonematrix[b->jointo + 7 ] += move[1] * t.fraction;
sko->bonematrix[b->jointo + 11] += move[2] * t.fraction;
if (!skelobjsrc)
b->vel[2] = b->vel[2] - gravity * ft / 2;
b++;
bno++;
}
else if (skelobjsrc)
{
/*directly copy animation skeleton*/
if (boneinfo[bone].parent >= 0)
{
Matrix3x4_Multiply(skelobjsrc->bonematrix+boffs, sko->bonematrix+12*boneinfo[bone].parent, sko->bonematrix+boffs);
}
else
{
Vector4Copy(skelobjsrc->bonematrix+boffs+0, sko->bonematrix+boffs+0);
Vector4Copy(skelobjsrc->bonematrix+boffs+4, sko->bonematrix+boffs+4);
Vector4Copy(skelobjsrc->bonematrix+boffs+8, sko->bonematrix+boffs+8);
}
}
else
{
/*retain the old relation*/
/*FIXME*/
}
}
#endif
/*debugging*/
#if 0
/*draw points*/
for (bone = 0; p < sko->numbones; bone++)
{
opos[0] = sko->bonematrix[bone*12 + 3 ];
opos[1] = sko->bonematrix[bone*12 + 7 ];
opos[2] = sko->bonematrix[bone*12 + 11];
Matrix3x4_RM_Transform3(mmat, opos, wopos);
P_RunParticleEffectTypeString(wopos, vec3_origin, 1, "ragdolltest");
}
#endif
}
#endif
/*destroys all skeletons*/
void skel_reset(progfuncs_t *prinst)
{
int i;
for (i = 0; i < numskelobjectsused; i++)
{
if (skelobjects[i].world = prinst->parms->user)
{
skelobjects[i].numbones = 0;
skelobjects[i].inuse = false;
skelobjects[i].bonematrix = NULL;
}
}
while (numskelobjectsused && !skelobjects[numskelobjectsused-1].inuse)
numskelobjectsused--;
}
/*deletes any skeletons marked for deletion*/
void skel_dodelete(progfuncs_t *prinst)
{
int skelidx;
if (!pendingkill)
return;
pendingkill = false;
for (skelidx = 0; skelidx < numskelobjectsused; skelidx++)
{
if (skelobjects[skelidx].inuse == 2)
skelobjects[skelidx].inuse = 0;
}
while (numskelobjectsused && !skelobjects[numskelobjectsused-1].inuse)
numskelobjectsused--;
}
skelobject_t *skel_get(progfuncs_t *prinst, int skelidx, int bonecount)
{
if (skelidx == 0)
{
//allocation
if (!bonecount)
return NULL;
for (skelidx = 0; skelidx < numskelobjectsused; skelidx++)
{
if (!skelobjects[skelidx].inuse && skelobjects[skelidx].numbones == bonecount && skelobjects[skelidx].world == prinst->parms->user)
return &skelobjects[skelidx];
}
for (skelidx = 0; skelidx <= MAX_SKEL_OBJECTS; skelidx++)
{
if (!skelobjects[skelidx].inuse &&
(!skelobjects[skelidx].numbones || skelobjects[skelidx].numbones == bonecount) &&
(!skelobjects[skelidx].world || skelobjects[skelidx].world == prinst->parms->user))
{
if (!skelobjects[skelidx].numbones)
{
skelobjects[skelidx].numbones = bonecount;
skelobjects[skelidx].bonematrix = (float*)PR_AddString(prinst, "", sizeof(float)*12*bonecount);
}
skelobjects[skelidx].world = prinst->parms->user;
if (numskelobjectsused == skelidx)
numskelobjectsused = skelidx + 1;
skelobjects[skelidx].model = NULL;
skelobjects[skelidx].inuse = 1;
return &skelobjects[skelidx];
}
}
return NULL;
}
else
{
skelidx--;
if ((unsigned int)skelidx >= numskelobjectsused)
return NULL;
if (skelobjects[skelidx].inuse != 1)
return NULL;
if (bonecount && skelobjects[skelidx].numbones != bonecount)
return NULL;
return &skelobjects[skelidx];
}
}
void skel_lookup(progfuncs_t *prinst, int skelidx, framestate_t *out)
{
skelobject_t *sko = skel_get(prinst, skelidx, 0);
if (sko && sko->inuse)
{
out->boneabs = sko->type;
out->bonecount = sko->numbones;
out->bonestate = sko->bonematrix;
}
}
void QCBUILTIN PF_skel_mmap(progfuncs_t *prinst, struct globalvars_s *pr_globals)
{
int skelidx = G_FLOAT(OFS_PARM0);
skelobject_t *sko = skel_get(prinst, skelidx, 0);
if (!sko || sko->world != prinst->parms->user)
G_INT(OFS_RETURN) = 0;
else
G_INT(OFS_RETURN) = PR_SetString(prinst, (void*)sko->bonematrix);
}
void QCBUILTIN PF_skel_ragedit(progfuncs_t *prinst, struct globalvars_s *pr_globals)
{
#ifdef RAGDOLL
int skelidx = G_FLOAT(OFS_PARM0);
char *skelname = PR_GetStringOfs(prinst, OFS_PARM1);
int parentskel = G_FLOAT(OFS_PARM2);
float *entorg = G_VECTOR(OFS_PARM3);
float *forward = G_VECTOR(OFS_PARM4);
float *right = G_VECTOR(OFS_PARM5);
float *up = G_VECTOR(OFS_PARM6);
skelobject_t *sko, *psko;
doll_t *doll;
int i;
float emat[12];
bonemat_fromqcvectors(emat, forward, right, up, entorg);
G_FLOAT(OFS_RETURN) = 0;
sko = skel_get(prinst, skelidx, 0);
if (!sko)
return;
if (*skelname)
{
doll = rag_loaddoll(sko->model, skelname);
if (!doll)
return;
}
else
{
/*no doll name makes it revert to a normal skeleton*/
sko->doll = NULL;
G_FLOAT(OFS_RETURN) = 1;
return;
}
if (sko->doll != doll)
{
sko->doll = doll;
free(sko->body);
sko->numbodies = doll->numbodies;
sko->body = malloc(sko->numbodies * sizeof(*sko->body));
memset(sko->body, 0, sko->numbodies * sizeof(*sko->body));
// for (i = 0; i < sko->numbodies; i++)
// sko->body[i].jointo = doll->body[i].bone * 12;
}
psko = skel_get(prinst, parentskel, 0);
skel_integrate(prinst, sko, psko, host_frametime, emat);
G_FLOAT(OFS_RETURN) = 1;
#endif
}
//float(float modelindex) skel_create (FTE_CSQC_SKELETONOBJECTS)
void QCBUILTIN PF_skel_create (progfuncs_t *prinst, struct globalvars_s *pr_globals)
{
world_t *w = prinst->parms->user;
int numbones;
skelobject_t *skelobj;
model_t *model;
int midx;
int type;
midx = G_FLOAT(OFS_PARM0);
type = (*prinst->callargc > 1)?G_FLOAT(OFS_PARM1):SKOT_RELATIVE;
//default to failure
G_FLOAT(OFS_RETURN) = 0;
model = w->Get_CModel(w, midx);
if (!model)
return; //no model set, can't get a skeleton
numbones = Mod_GetNumBones(model, type != SKOT_RELATIVE);
if (!numbones)
{
// isabs = true;
// numbones = Mod_GetNumBones(model, isabs);
// if (!numbones)
return; //this isn't a skeletal model.
}
skelobj = skel_get(prinst, 0, numbones);
if (!skelobj)
return; //couldn't get one, ran out of memory or something?
skelobj->model = model;
skelobj->type = type;
G_FLOAT(OFS_RETURN) = (skelobj - skelobjects) + 1;
}
//float(float skel, entity ent, float modelindex, float retainfrac, float firstbone, float lastbone) skel_build (FTE_CSQC_SKELETONOBJECTS)
void QCBUILTIN PF_skel_build(progfuncs_t *prinst, struct globalvars_s *pr_globals)
{
world_t *w = prinst->parms->user;
int skelidx = G_FLOAT(OFS_PARM0);
wedict_t *ent = (wedict_t*)G_EDICT(prinst, OFS_PARM1);
int midx = G_FLOAT(OFS_PARM2);
float retainfrac = G_FLOAT(OFS_PARM3);
int firstbone = G_FLOAT(OFS_PARM4)-1;
int lastbone = G_FLOAT(OFS_PARM5)-1;
float addition = (*prinst->callargc>6)?G_FLOAT(OFS_PARM6):1-retainfrac;
int i, j;
int numbones;
framestate_t fstate;
skelobject_t *skelobj;
model_t *model;
//default to failure
G_FLOAT(OFS_RETURN) = 0;
model = w->Get_CModel(w, midx);
if (!model)
return; //invalid model, can't get a skeleton
w->Get_FrameState(w, ent, &fstate);
//heh... don't copy.
fstate.bonecount = 0;
fstate.bonestate = NULL;
numbones = Mod_GetNumBones(model, false);
if (!numbones)
{
return; //this isn't a skeletal model.
}
skelobj = skel_get(prinst, skelidx, 0);
if (!skelobj)
return; //couldn't get one, ran out of memory or something?
if (lastbone < 0)
lastbone = numbones;
if (lastbone > numbones)
lastbone = numbones;
if (firstbone < 0)
firstbone = 0;
if (retainfrac == 0)
{
/*replace everything*/
if (addition == 1)
Mod_GetBoneRelations(model, firstbone, lastbone, &fstate, skelobj->bonematrix);
else
{
//scale new
float relationsbuf[MAX_BONES*12];
Mod_GetBoneRelations(model, firstbone, lastbone, &fstate, relationsbuf);
for (i = firstbone; i < lastbone; i++)
{
for (j = 0; j < 12; j++)
skelobj->bonematrix[i*12+j] = addition*relationsbuf[i*12+j];
}
}
}
else
{
if (retainfrac != 1)
{
//rescale the existing bones
for (i = firstbone; i < lastbone; i++)
{
for (j = 0; j < 12; j++)
skelobj->bonematrix[i*12+j] *= retainfrac;
}
}
if (addition == 1)
{
//just add
float relationsbuf[MAX_BONES*12];
Mod_GetBoneRelations(model, firstbone, lastbone, &fstate, relationsbuf);
for (i = firstbone; i < lastbone; i++)
{
for (j = 0; j < 12; j++)
skelobj->bonematrix[i*12+j] += relationsbuf[i*12+j];
}
}
else if (addition)
{
//add+scale
float relationsbuf[MAX_BONES*12];
Mod_GetBoneRelations(model, firstbone, lastbone, &fstate, relationsbuf);
for (i = firstbone; i < lastbone; i++)
{
for (j = 0; j < 12; j++)
skelobj->bonematrix[i*12+j] += addition*relationsbuf[i*12+j];
}
}
}
G_FLOAT(OFS_RETURN) = (skelobj - skelobjects) + 1;
}
//float(float skel) skel_get_numbones (FTE_CSQC_SKELETONOBJECTS)
void QCBUILTIN PF_skel_get_numbones (progfuncs_t *prinst, struct globalvars_s *pr_globals)
{
int skelidx = G_FLOAT(OFS_PARM0);
skelobject_t *skelobj;
skelobj = skel_get(prinst, skelidx, 0);
if (!skelobj)
G_FLOAT(OFS_RETURN) = 0;
else
G_FLOAT(OFS_RETURN) = skelobj->numbones;
}
//string(float skel, float bonenum) skel_get_bonename (FTE_CSQC_SKELETONOBJECTS) (returns tempstring)
void QCBUILTIN PF_skel_get_bonename (progfuncs_t *prinst, struct globalvars_s *pr_globals)
{
int skelidx = G_FLOAT(OFS_PARM0);
int boneidx = G_FLOAT(OFS_PARM1);
skelobject_t *skelobj;
skelobj = skel_get(prinst, skelidx, 0);
if (!skelobj)
G_INT(OFS_RETURN) = 0;
else
{
RETURN_TSTRING(Mod_GetBoneName(skelobj->model, boneidx));
}
}
//float(float skel, float bonenum) skel_get_boneparent (FTE_CSQC_SKELETONOBJECTS)
void QCBUILTIN PF_skel_get_boneparent (progfuncs_t *prinst, struct globalvars_s *pr_globals)
{
int skelidx = G_FLOAT(OFS_PARM0);
int boneidx = G_FLOAT(OFS_PARM1);
skelobject_t *skelobj;
skelobj = skel_get(prinst, skelidx, 0);
if (!skelobj)
G_FLOAT(OFS_RETURN) = 0;
else
G_FLOAT(OFS_RETURN) = Mod_GetBoneParent(skelobj->model, boneidx);
}
//float(float skel, string tagname) skel_find_bone (FTE_CSQC_SKELETONOBJECTS)
void QCBUILTIN PF_skel_find_bone (progfuncs_t *prinst, struct globalvars_s *pr_globals)
{
int skelidx = G_FLOAT(OFS_PARM0);
char *bname = PR_GetStringOfs(prinst, OFS_PARM1);
skelobject_t *skelobj;
skelobj = skel_get(prinst, skelidx, 0);
if (!skelobj)
G_FLOAT(OFS_RETURN) = 0;
else
G_FLOAT(OFS_RETURN) = Mod_TagNumForName(skelobj->model, bname);
}
//vector(float skel, float bonenum) skel_get_bonerel (FTE_CSQC_SKELETONOBJECTS) (sets v_forward etc)
void QCBUILTIN PF_skel_get_bonerel (progfuncs_t *prinst, struct globalvars_s *pr_globals)
{
world_t *w = prinst->parms->user;
int skelidx = G_FLOAT(OFS_PARM0);
int boneidx = G_FLOAT(OFS_PARM1)-1;
skelobject_t *skelobj = skel_get(prinst, skelidx, 0);
if (!skelobj || (unsigned int)boneidx >= skelobj->numbones)
bonematident_toqcvectors(w->g.v_forward, w->g.v_right, w->g.v_up, G_VECTOR(OFS_RETURN));
else if (skelobj->type!=SKOT_RELATIVE)
{
float tmp[12];
float invparent[12];
int parent;
/*invert the parent, multiply that against the child, we now know the transform required to go from parent to child. woo.*/
parent = Mod_GetBoneParent(skelobj->model, boneidx+1)-1;
Matrix3x4_Invert(skelobj->bonematrix+12*parent, invparent);
Matrix3x4_Multiply(invparent, skelobj->bonematrix+12*boneidx, tmp);
bonemat_toqcvectors(tmp, w->g.v_forward, w->g.v_right, w->g.v_up, G_VECTOR(OFS_RETURN));
}
else
bonemat_toqcvectors(skelobj->bonematrix+12*boneidx, w->g.v_forward, w->g.v_right, w->g.v_up, G_VECTOR(OFS_RETURN));
}
//vector(float skel, float bonenum) skel_get_boneabs (FTE_CSQC_SKELETONOBJECTS) (sets v_forward etc)
void QCBUILTIN PF_skel_get_boneabs (progfuncs_t *prinst, struct globalvars_s *pr_globals)
{
world_t *w = prinst->parms->user;
int skelidx = G_FLOAT(OFS_PARM0);
int boneidx = G_FLOAT(OFS_PARM1)-1;
float workingm[12], tempmatrix[3][4];
int i;
skelobject_t *skelobj = skel_get(prinst, skelidx, 0);
if (!skelobj || (unsigned int)boneidx >= skelobj->numbones)
bonematident_toqcvectors(w->g.v_forward, w->g.v_right, w->g.v_up, G_VECTOR(OFS_RETURN));
else if (skelobj->type != SKOT_RELATIVE)
{
//can just copy it out
bonemat_toqcvectors(skelobj->bonematrix + boneidx*12, w->g.v_forward, w->g.v_right, w->g.v_up, G_VECTOR(OFS_RETURN));
}
else
{
//we need to work out the abs position
//testme
//set up an identity matrix
for (i = 0;i < 12;i++)
workingm[i] = 0;
workingm[0] = 1;
workingm[5] = 1;
workingm[10] = 1;
while(boneidx >= 0)
{
//copy out the previous working matrix, so we don't stomp on it
memcpy(tempmatrix, workingm, sizeof(tempmatrix));
R_ConcatTransforms((void*)(skelobj->bonematrix + boneidx*12), (void*)tempmatrix, (void*)workingm);
boneidx = Mod_GetBoneParent(skelobj->model, boneidx+1)-1;
}
bonemat_toqcvectors(workingm, w->g.v_forward, w->g.v_right, w->g.v_up, G_VECTOR(OFS_RETURN));
}
}
//void(entity ent, float bonenum, vector org, optional fwd, right, up) skel_set_bone_world (FTE_CSQC_SKELETONOBJECTS2) (reads v_forward etc)
void QCBUILTIN PF_skel_set_bone_world (progfuncs_t *prinst, struct globalvars_s *pr_globals)
{
world_t *w = prinst->parms->user;
wedict_t *ent = G_WEDICT(prinst, OFS_PARM0);
unsigned int boneidx = G_FLOAT(OFS_PARM1)-1;
float *matrix[3];
skelobject_t *skelobj;
float *bone;
float childworld[12], parentinv[12];
/*sort out the parameters*/
if (*prinst->callargc == 4)
{
vec3_t d[3], a;
a[0] = G_VECTOR(OFS_PARM3)[0] * -1; /*mod_alias bug*/
a[1] = G_VECTOR(OFS_PARM3)[1];
a[2] = G_VECTOR(OFS_PARM3)[2];
AngleVectors(a, d[0], d[1], d[2]);
bonemat_fromqcvectors(childworld, d[0], d[1], d[2], G_VECTOR(OFS_PARM2));
}
else
{
if (*prinst->callargc > 5)
{
matrix[0] = G_VECTOR(OFS_PARM3);
matrix[1] = G_VECTOR(OFS_PARM4);
matrix[2] = G_VECTOR(OFS_PARM5);
}
else
{
matrix[0] = w->g.v_forward;
matrix[1] = w->g.v_right;
matrix[2] = w->g.v_up;
}
bonemat_fromqcvectors(childworld, matrix[0], matrix[1], matrix[2], G_VECTOR(OFS_PARM2));
}
/*make sure the skeletal object is correct*/
skelobj = skel_get(prinst, ent->xv->skeletonindex, 0);
if (!skelobj || boneidx >= skelobj->numbones)
return;
/*get the inverse of the parent matrix*/
{
float parentabs[12];
float parentw[12];
float parentent[12];
framestate_t fstate;
w->Get_FrameState(w, ent, &fstate);
if (skelobj->type == SKOT_ABSOLUTE || !Mod_GetTag(skelobj->model, Mod_GetBoneParent(skelobj->model, boneidx+1), &fstate, parentabs))
{
bonemat_fromentity(w, ent, parentw);
}
else
{
bonemat_fromentity(w, ent, parentent);
Matrix3x4_Multiply(parentabs, parentent, parentw);
}
Matrix3x4_Invert(parentw, parentinv);
}
/*calc the result*/
bone = skelobj->bonematrix+12*boneidx;
Matrix3x4_Multiply(childworld, parentinv, bone);
}
//void(float skel, float bonenum, vector org) skel_set_bone (FTE_CSQC_SKELETONOBJECTS) (reads v_forward etc)
void QCBUILTIN PF_skel_set_bone (progfuncs_t *prinst, struct globalvars_s *pr_globals)
{
world_t *w = prinst->parms->user;
int skelidx = G_FLOAT(OFS_PARM0);
unsigned int boneidx = G_FLOAT(OFS_PARM1)-1;
float *matrix[3];
skelobject_t *skelobj;
float *bone;
if (*prinst->callargc > 5)
{
matrix[0] = G_VECTOR(OFS_PARM3);
matrix[1] = G_VECTOR(OFS_PARM4);
matrix[2] = G_VECTOR(OFS_PARM5);
}
else
{
matrix[0] = w->g.v_forward;
matrix[1] = w->g.v_right;
matrix[2] = w->g.v_up;
}
skelobj = skel_get(prinst, skelidx, 0);
if (!skelobj || boneidx >= skelobj->numbones)
return;
bone = skelobj->bonematrix+12*boneidx;
bonemat_fromqcvectors(bone, matrix[0], matrix[1], matrix[2], G_VECTOR(OFS_PARM2));
}
//void(float skel, float bonenum, vector org [, vector fwd, vector right, vector up]) skel_mul_bone (FTE_CSQC_SKELETONOBJECTS) (reads v_forward etc)
void QCBUILTIN PF_skel_mul_bone (progfuncs_t *prinst, struct globalvars_s *pr_globals)
{
world_t *w = prinst->parms->user;
int skelidx = G_FLOAT(OFS_PARM0);
int boneidx = G_FLOAT(OFS_PARM1)-1;
float temp[3][4];
float mult[3][4];
skelobject_t *skelobj;
if (*prinst->callargc > 5)
bonemat_fromqcvectors((float*)mult, G_VECTOR(OFS_PARM3), G_VECTOR(OFS_PARM4), G_VECTOR(OFS_PARM5), G_VECTOR(OFS_PARM2));
else
bonemat_fromqcvectors((float*)mult, w->g.v_forward, w->g.v_right, w->g.v_up, G_VECTOR(OFS_PARM2));
skelobj = skel_get(prinst, skelidx, 0);
if (!skelobj || boneidx >= skelobj->numbones)
return;
//testme
Vector4Copy(skelobj->bonematrix+12*boneidx+0, temp[0]);
Vector4Copy(skelobj->bonematrix+12*boneidx+4, temp[1]);
Vector4Copy(skelobj->bonematrix+12*boneidx+8, temp[2]);
R_ConcatTransforms(mult, temp, (float(*)[4])(skelobj->bonematrix+12*boneidx));
}
//void(float skel, float startbone, float endbone, vector org) skel_mul_bone (FTE_CSQC_SKELETONOBJECTS) (reads v_forward etc)
void QCBUILTIN PF_skel_mul_bones (progfuncs_t *prinst, struct globalvars_s *pr_globals)
{
world_t *w = prinst->parms->user;
int skelidx = G_FLOAT(OFS_PARM0);
unsigned int startbone = G_FLOAT(OFS_PARM1)-1;
unsigned int endbone = G_FLOAT(OFS_PARM2)-1;
float temp[3][4];
float mult[3][4];
skelobject_t *skelobj;
if (*prinst->callargc > 6)
bonemat_fromqcvectors((float*)mult, G_VECTOR(OFS_PARM4), G_VECTOR(OFS_PARM5), G_VECTOR(OFS_PARM6), G_VECTOR(OFS_PARM3));
else
bonemat_fromqcvectors((float*)mult, w->g.v_forward, w->g.v_right, w->g.v_up, G_VECTOR(OFS_PARM3));
skelobj = skel_get(prinst, skelidx, 0);
if (!skelobj)
return;
if (startbone == -1)
startbone = 0;
if (endbone == -1)
endbone = skelobj->numbones;
else if (endbone > skelobj->numbones)
endbone = skelobj->numbones;
while(startbone < endbone)
{
Vector4Copy(skelobj->bonematrix+12*startbone+0, temp[0]);
Vector4Copy(skelobj->bonematrix+12*startbone+4, temp[1]);
Vector4Copy(skelobj->bonematrix+12*startbone+8, temp[2]);
R_ConcatTransforms(mult, temp, (float(*)[4])(skelobj->bonematrix+12*startbone));
startbone++;
}
}
//void(float skeldst, float skelsrc, float startbone, float entbone) skel_copybones (FTE_CSQC_SKELETONOBJECTS)
void QCBUILTIN PF_skel_copybones (progfuncs_t *prinst, struct globalvars_s *pr_globals)
{
int skeldst = G_FLOAT(OFS_PARM0);
int skelsrc = G_FLOAT(OFS_PARM1);
int startbone = G_FLOAT(OFS_PARM2)-1;
int endbone = G_FLOAT(OFS_PARM3)-1;
skelobject_t *skelobjdst;
skelobject_t *skelobjsrc;
skelobjdst = skel_get(prinst, skeldst, 0);
skelobjsrc = skel_get(prinst, skelsrc, 0);
if (!skelobjdst || !skelobjsrc)
return;
if (startbone == -1)
startbone = 0;
if (endbone == -1)
endbone = skelobjdst->numbones;
if (endbone > skelobjdst->numbones)
endbone = skelobjdst->numbones;
if (endbone > skelobjsrc->numbones)
endbone = skelobjsrc->numbones;
if (skelobjsrc->type == skelobjdst->type)
{
while(startbone < endbone)
{
Vector4Copy(skelobjsrc->bonematrix+12*startbone+0, skelobjdst->bonematrix+12*startbone+0);
Vector4Copy(skelobjsrc->bonematrix+12*startbone+4, skelobjdst->bonematrix+12*startbone+4);
Vector4Copy(skelobjsrc->bonematrix+12*startbone+8, skelobjdst->bonematrix+12*startbone+8);
startbone++;
}
}
else if (skelobjsrc->type == SKOT_RELATIVE && skelobjdst->type == SKOT_ABSOLUTE)
{
/*copy from relative to absolute*/
galiasbone_t *boneinfo = Mod_GetBoneInfo(skelobjsrc->model);
if (!boneinfo)
return;
while(startbone < endbone)
{
if (boneinfo[startbone].parent >= 0)
{
Matrix3x4_Multiply(skelobjsrc->bonematrix+12*startbone, skelobjdst->bonematrix+12*boneinfo[startbone].parent, skelobjdst->bonematrix+12*startbone);
}
else
{
Vector4Copy(skelobjsrc->bonematrix+12*startbone+0, skelobjdst->bonematrix+12*startbone+0);
Vector4Copy(skelobjsrc->bonematrix+12*startbone+4, skelobjdst->bonematrix+12*startbone+4);
Vector4Copy(skelobjsrc->bonematrix+12*startbone+8, skelobjdst->bonematrix+12*startbone+8);
}
startbone++;
}
}
}
//void(float skel) skel_delete (FTE_CSQC_SKELETONOBJECTS)
void QCBUILTIN PF_skel_delete (progfuncs_t *prinst, struct globalvars_s *pr_globals)
{
int skelidx = G_FLOAT(OFS_PARM0);
skelobject_t *skelobj;
skelobj = skel_get(prinst, skelidx, 0);
if (skelobj)
{
skelobj->inuse = 2; //2 means don't reuse yet.
skelobj->model = NULL;
pendingkill = true;
}
}
//vector(entity ent, float tag) gettaginfo (DP_MD3_TAGSINFO)
void QCBUILTIN PF_gettaginfo (progfuncs_t *prinst, struct globalvars_s *pr_globals)
{
world_t *w = prinst->parms->user;
wedict_t *ent = G_WEDICT(prinst, OFS_PARM0);
int tagnum = G_FLOAT(OFS_PARM1);
int modelindex = ent->v->modelindex;
model_t *mod = w->Get_CModel(w, modelindex);
float transent[12];
float transforms[12];
float result[12];
framestate_t fstate;
w->Get_FrameState(w, ent, &fstate);
if (!Mod_GetTag(mod, tagnum, &fstate, transforms))
{
bonemat_fromidentity(transforms);
}
if (ent->xv->tag_entity)
{
#ifdef warningmsg
#pragma warningmsg("PF_gettaginfo: This function doesn't honour attachments")
#endif
Con_Printf("bug: PF_gettaginfo doesn't support attachments\n");
}
bonemat_fromentity(w, ent, transent);
R_ConcatTransforms((void*)transent, (void*)transforms, (void*)result);
bonemat_toqcvectors(result, w->g.v_forward, w->g.v_right, w->g.v_up, G_VECTOR(OFS_RETURN));
}
//vector(entity ent, string tagname) gettagindex (DP_MD3_TAGSINFO)
void QCBUILTIN PF_gettagindex (progfuncs_t *prinst, struct globalvars_s *pr_globals)
{
world_t *w = prinst->parms->user;
wedict_t *ent = G_WEDICT(prinst, OFS_PARM0);
char *tagname = PR_GetStringOfs(prinst, OFS_PARM1);
model_t *mod = *tagname?w->Get_CModel(w, ent->v->modelindex):NULL;
if (mod)
G_FLOAT(OFS_RETURN) = Mod_TagNumForName(mod, tagname);
else
G_FLOAT(OFS_RETURN) = 0;
}
const char *Mod_FrameNameForNum(model_t *model, int num);
const char *Mod_SkinNameForNum(model_t *model, int num);
//string(float modidx, float framenum) frametoname
void QCBUILTIN PF_frametoname (progfuncs_t *prinst, struct globalvars_s *pr_globals)
{
world_t *w = prinst->parms->user;
unsigned int modelindex = G_FLOAT(OFS_PARM0);
unsigned int skinnum = G_FLOAT(OFS_PARM1);
model_t *mod = w->Get_CModel(w, modelindex);
const char *n = Mod_FrameNameForNum(mod, skinnum);
if (n)
RETURN_TSTRING(n);
else
G_INT(OFS_RETURN) = 0; //null string (which is also empty in qc)
}
//string(float modidx, float skinnum) skintoname
void QCBUILTIN PF_skintoname (progfuncs_t *prinst, struct globalvars_s *pr_globals)
{
world_t *w = prinst->parms->user;
unsigned int modelindex = G_FLOAT(OFS_PARM0);
unsigned int skinnum = G_FLOAT(OFS_PARM1);
model_t *mod = w->Get_CModel(w, modelindex);
const char *n = Mod_SkinNameForNum(mod, skinnum);
if (n)
RETURN_TSTRING(n);
else
G_INT(OFS_RETURN) = 0; //null string (which is also empty in qc)
}
void QCBUILTIN PF_frameforname (progfuncs_t *prinst, struct globalvars_s *pr_globals)
{
world_t *w = prinst->parms->user;
unsigned int modelindex = G_FLOAT(OFS_PARM0);
char *str = PF_VarString(prinst, 1, pr_globals);
model_t *mod = w->Get_CModel(w, modelindex);
if (mod && Mod_FrameForName)
G_FLOAT(OFS_RETURN) = Mod_FrameForName(mod, str);
else
G_FLOAT(OFS_RETURN) = -1;
}
void QCBUILTIN PF_frameduration (progfuncs_t *prinst, struct globalvars_s *pr_globals)
{
world_t *w = prinst->parms->user;
unsigned int modelindex = G_FLOAT(OFS_PARM0);
unsigned int framenum = G_FLOAT(OFS_PARM1);
model_t *mod = w->Get_CModel(w, modelindex);
if (mod && Mod_GetFrameDuration)
G_FLOAT(OFS_RETURN) = Mod_GetFrameDuration(mod, framenum);
else
G_FLOAT(OFS_RETURN) = 0;
}
void QCBUILTIN PF_skinforname (progfuncs_t *prinst, struct globalvars_s *pr_globals)
{
#ifndef SERVERONLY
world_t *w = prinst->parms->user;
unsigned int modelindex = G_FLOAT(OFS_PARM0);
char *str = PF_VarString(prinst, 1, pr_globals);
model_t *mod = w->Get_CModel(w, modelindex);
if (mod && Mod_SkinForName)
G_FLOAT(OFS_RETURN) = Mod_SkinForName(mod, str);
else
#endif
G_FLOAT(OFS_RETURN) = -1;
}
#endif