halflife-photomode/utils/studiomdl/studiomdl.cpp

3555 lines
71 KiB
C++

/***
*
* Copyright (c) 1996-2002, Valve LLC. All rights reserved.
*
* This product contains software technology licensed from Id
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
* All Rights Reserved.
*
****/
//
// studiomdl.c: generates a studio .mdl file from a .qc script
// models/<scriptname>.mdl.
//
#pragma warning(disable : 4244)
#pragma warning(disable : 4237)
#pragma warning(disable : 4305)
#include <algorithm>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <math.h>
#include "steam/steamtypes.h"
#include "cmdlib.h"
#include "lbmlib.h"
#include "scriplib.h"
#include "mathlib.h"
#define Vector vec3_t
#define EXTERN
#include "../../engine/studio.h"
#include "studiomdl.h"
#include "../../dlls/activity.h"
#include "../../dlls/activitymap.h"
static int force_powerof2_textures = 0;
void clip_rotations(vec3_t rot);
#define strcpyn(a, b) strncpy(a, b, sizeof(a))
/*
=================
=================
*/
int k_memtotal;
void* kalloc(int num, int size)
{
// printf( "calloc( %d, %d )\n", num, size );
// printf( "%d ", num * size );
k_memtotal += num * size;
return calloc(num, size);
}
void kmemset(void* ptr, int value, int size)
{
// printf( "kmemset( %x, %d, %d )\n", ptr, value, size );
memset(ptr, value, size);
return;
}
/*
=================
=================
*/
void ClearModel(void)
{
}
void ExtractMotion()
{
int i, j, k;
int q;
// extract linear motion
for (i = 0; i < numseq; i++)
{
if (sequence[i].numframes > 1)
{
// assume 0 for now.
int type;
vec3_t* ppos;
vec3_t motion = {0, 0, 0};
type = sequence[i].motiontype;
ppos = sequence[i].panim[0]->pos[0];
k = sequence[i].numframes - 1;
if (type & STUDIO_LX)
motion[0] = ppos[k][0] - ppos[0][0];
if (type & STUDIO_LY)
motion[1] = ppos[k][1] - ppos[0][1];
if (type & STUDIO_LZ)
motion[2] = ppos[k][2] - ppos[0][2];
// printf("%f %f %f\n", motion[0], motion[1], motion[2] );
for (j = 0; j < sequence[i].numframes; j++)
{
vec3_t adj;
for (k = 0; k < sequence[i].panim[0]->numbones; k++)
{
if (sequence[i].panim[0]->node[k].parent == -1)
{
ppos = sequence[i].panim[0]->pos[k];
VectorScale(motion, j * 1.0 / (sequence[i].numframes - 1), adj);
// printf(" %f %f %f\n", adj[0], adj[1], adj[2] );
for (q = 0; q < sequence[i].numblends; q++)
{
VectorSubtract(sequence[i].panim[q]->pos[k][j], adj, sequence[i].panim[q]->pos[k][j]);
}
}
}
}
VectorCopy(motion, sequence[i].linearmovement);
}
else
{
VectorSubtract(sequence[i].linearmovement, sequence[i].linearmovement, sequence[i].linearmovement);
}
}
// extract unused motion
for (i = 0; i < numseq; i++)
{
int type;
type = sequence[i].motiontype;
for (k = 0; k < sequence[i].panim[0]->numbones; k++)
{
if (sequence[i].panim[0]->node[k].parent == -1)
{
for (q = 0; q < sequence[i].numblends; q++)
{
float motion[6];
motion[0] = sequence[i].panim[q]->pos[k][0][0];
motion[1] = sequence[i].panim[q]->pos[k][0][1];
motion[2] = sequence[i].panim[q]->pos[k][0][2];
motion[3] = sequence[i].panim[q]->rot[k][0][0];
motion[4] = sequence[i].panim[q]->rot[k][0][1];
motion[5] = sequence[i].panim[q]->rot[k][0][2];
for (j = 0; j < sequence[i].numframes; j++)
{
/*
if (type & STUDIO_X)
sequence[i].panim[q]->pos[k][j][0] = motion[0];
if (type & STUDIO_Y)
sequence[i].panim[q]->pos[k][j][1] = motion[1];
if (type & STUDIO_Z)
sequence[i].panim[q]->pos[k][j][2] = motion[2];
*/
if (type & STUDIO_XR)
sequence[i].panim[q]->rot[k][j][0] = motion[3];
if (type & STUDIO_YR)
sequence[i].panim[q]->rot[k][j][1] = motion[4];
if (type & STUDIO_ZR)
sequence[i].panim[q]->rot[k][j][2] = motion[5];
}
}
}
}
}
// extract auto motion
for (i = 0; i < numseq; i++)
{
// assume 0 for now.
int type;
vec3_t* ppos;
vec3_t* prot;
vec3_t motion = {0, 0, 0};
vec3_t angles = {0, 0, 0};
type = sequence[i].motiontype;
// printf("%f %f %f\n", motion[0], motion[1], motion[2] );
for (j = 0; j < sequence[i].numframes; j++)
{
ppos = sequence[i].panim[0]->pos[0];
prot = sequence[i].panim[0]->rot[0];
if (type & STUDIO_AX)
motion[0] = ppos[j][0] - ppos[0][0];
if (type & STUDIO_AY)
motion[1] = ppos[j][1] - ppos[0][1];
if (type & STUDIO_AZ)
motion[2] = ppos[j][2] - ppos[0][2];
if (type & STUDIO_AXR)
angles[0] = prot[j][0] - prot[0][0];
if (type & STUDIO_AYR)
angles[1] = prot[j][1] - prot[0][1];
if (type & STUDIO_AZR)
angles[2] = prot[j][2] - prot[0][2];
VectorCopy(motion, sequence[i].automovepos[j]);
VectorCopy(angles, sequence[i].automoveangle[j]);
for (k = 0; k < sequence[i].panim[0]->numbones; k++)
{
if (sequence[i].panim[0]->node[k].parent == -1)
{
// printf(" %f %f %f\n", adj[0], adj[1], adj[2] );
for (q = 0; q < sequence[i].numblends; q++)
{
// VectorSubtract( sequence[i].panim[q]->pos[k][j], motion, sequence[i].panim[q]->pos[k][j] );
// VectorSubtract( sequence[i].panim[q]->rot[k][j], angles, sequence[i].panim[q]->pos[k][j] );
}
}
}
}
}
}
void OptimizeAnimations(void)
{
int i, j;
int n, m;
int type;
int q;
int iError = 0;
// optimize animations
for (i = 0; i < numseq; i++)
{
sequence[i].numframes = sequence[i].panim[0]->endframe - sequence[i].panim[0]->startframe + 1;
// force looping animations to be looping
if (sequence[i].flags & STUDIO_LOOPING)
{
for (j = 0; j < sequence[i].panim[0]->numbones; j++)
{
for (q = 0; q < sequence[i].numblends; q++)
{
vec3_t* ppos = sequence[i].panim[q]->pos[j];
vec3_t* prot = sequence[i].panim[q]->rot[j];
n = 0; // sequence[i].panim[q]->startframe;
m = sequence[i].numframes - 1; // sequence[i].panim[q]->endframe;
type = sequence[i].motiontype;
if (!(type & STUDIO_LX))
ppos[m][0] = ppos[n][0];
if (!(type & STUDIO_LY))
ppos[m][1] = ppos[n][1];
if (!(type & STUDIO_LZ))
ppos[m][2] = ppos[n][2];
prot[m][0] = prot[n][0];
prot[m][1] = prot[n][1];
prot[m][2] = prot[n][2];
}
}
}
for (j = 0; j < sequence[i].numevents; j++)
{
if (sequence[i].event[j].frame < sequence[i].panim[0]->startframe)
{
printf("sequence %s has event (%d) before first frame (%d)\n", sequence[i].name, sequence[i].event[j].frame, sequence[i].panim[0]->startframe);
sequence[i].event[j].frame = sequence[i].panim[0]->startframe;
iError++;
}
if (sequence[i].event[j].frame > sequence[i].panim[0]->endframe)
{
printf("sequence %s has event (%d) after last frame (%d)\n", sequence[i].name, sequence[i].event[j].frame, sequence[i].panim[0]->endframe);
sequence[i].event[j].frame = sequence[i].panim[0]->endframe;
iError++;
}
}
sequence[i].frameoffset = sequence[i].panim[0]->startframe;
// printf("\n");
}
/*
if (iError)
exit(1);
*/
}
int findNode(char* name)
{
int k;
for (k = 0; k < numbones; k++)
{
if (strcmp(bonetable[k].name, name) == 0)
{
return k;
}
}
return -1;
}
void MatrixCopy(float in[3][4], float out[3][4])
{
int i, j;
for (i = 0; i < 3; i++)
{
for (j = 0; j < 4; j++)
{
out[i][j] = in[i][j];
}
}
}
void MakeTransitions()
{
int i, j, k;
int iHit;
// add in direct node transitions
for (i = 0; i < numseq; i++)
{
if (sequence[i].entrynode != sequence[i].exitnode)
{
xnode[sequence[i].entrynode - 1][sequence[i].exitnode - 1] = sequence[i].exitnode;
if (sequence[i].nodeflags)
{
xnode[sequence[i].exitnode - 1][sequence[i].entrynode - 1] = sequence[i].entrynode;
}
}
if (sequence[i].entrynode > numxnodes)
numxnodes = sequence[i].entrynode;
}
// add multi-stage transitions
do
{
iHit = 0;
for (i = 1; i <= numxnodes; i++)
{
for (j = 1; j <= numxnodes; j++)
{
// if I can't go there directly
if (i != j && xnode[i - 1][j - 1] == 0)
{
for (k = 1; k < numxnodes; k++)
{
// but I found someone who knows how that I can get to
if (xnode[k - 1][j - 1] > 0 && xnode[i - 1][k - 1] > 0)
{
// then go to them
xnode[i - 1][j - 1] = -xnode[i - 1][k - 1];
iHit = 1;
break;
}
}
}
}
}
// reset previous pass so the links can be used in the next pass
for (i = 1; i <= numxnodes; i++)
{
for (j = 1; j <= numxnodes; j++)
{
xnode[i - 1][j - 1] = abs(xnode[i - 1][j - 1]);
}
}
} while (iHit);
}
void SimplifyModel(void)
{
int i, j, k;
int n, m, q;
vec3_t* defaultpos[MAXSTUDIOSRCBONES];
vec3_t* defaultrot[MAXSTUDIOSRCBONES];
int iError = 0;
OptimizeAnimations();
ExtractMotion();
MakeTransitions();
// find used bones
for (i = 0; i < nummodels; i++)
{
for (k = 0; k < MAXSTUDIOSRCBONES; k++)
{
model[i]->boneref[k] = 0;
}
for (j = 0; j < model[i]->numverts; j++)
{
model[i]->boneref[model[i]->vert[j].bone] = 1;
}
for (k = 0; k < MAXSTUDIOSRCBONES; k++)
{
// tag parent bones as used if child has been used
if (model[i]->boneref[k])
{
n = model[i]->node[k].parent;
while (n != -1 && !model[i]->boneref[n])
{
model[i]->boneref[n] = 1;
n = model[i]->node[n].parent;
}
}
}
}
// rename model bones if needed
for (i = 0; i < nummodels; i++)
{
for (j = 0; j < model[i]->numbones; j++)
{
for (k = 0; k < numrenamedbones; k++)
{
if (!strcmp(model[i]->node[j].name, renamedbone[k].from))
{
strcpy(model[i]->node[j].name, renamedbone[k].to);
break;
}
}
}
}
// union of all used bones
numbones = 0;
for (i = 0; i < nummodels; i++)
{
for (k = 0; k < MAXSTUDIOSRCBONES; k++)
{
model[i]->boneimap[k] = -1;
}
for (j = 0; j < model[i]->numbones; j++)
{
if (model[i]->boneref[j])
{
k = findNode(model[i]->node[j].name);
if (k == -1)
{
// create new bone
// printf("%d : %s\n", numbones, model[i]->node[j].name );
k = numbones;
strcpyn(bonetable[k].name, model[i]->node[j].name);
if ((n = model[i]->node[j].parent) != -1)
bonetable[k].parent = findNode(model[i]->node[n].name);
else
bonetable[k].parent = -1;
bonetable[k].bonecontroller = 0;
bonetable[k].flags = 0;
// set defaults
defaultpos[k] = reinterpret_cast<vec3_t*>(kalloc(MAXSTUDIOANIMATIONS, sizeof(vec3_t)));
defaultrot[k] = reinterpret_cast<vec3_t*>(kalloc(MAXSTUDIOANIMATIONS, sizeof(vec3_t)));
for (n = 0; n < MAXSTUDIOANIMATIONS; n++)
{
VectorCopy(model[i]->skeleton[j].pos, defaultpos[k][n]);
VectorCopy(model[i]->skeleton[j].rot, defaultrot[k][n]);
}
VectorCopy(model[i]->skeleton[j].pos, bonetable[k].pos);
VectorCopy(model[i]->skeleton[j].rot, bonetable[k].rot);
numbones++;
}
else
{
// double check parent assignments
n = model[i]->node[j].parent;
if (n != -1)
n = findNode(model[i]->node[n].name);
m = bonetable[k].parent;
if (n != m)
{
printf("illegal parent bone replacement in model \"%s\"\n\t\"%s\" has \"%s\", previously was \"%s\"\n",
model[i]->name,
model[i]->node[j].name,
(n != -1) ? bonetable[n].name : "ROOT",
(m != -1) ? bonetable[m].name : "ROOT");
iError++;
}
}
model[i]->bonemap[j] = k;
model[i]->boneimap[k] = j;
}
}
}
if (iError && !(ignore_warnings))
{
exit(1);
}
if (numbones >= MAXSTUDIOBONES)
{
Error("Too many bones used in model, used %d, max %d\n", numbones, MAXSTUDIOBONES);
}
// rename sequence bones if needed
for (i = 0; i < numseq; i++)
{
for (j = 0; j < sequence[i].panim[0]->numbones; j++)
{
for (k = 0; k < numrenamedbones; k++)
{
if (!strcmp(sequence[i].panim[0]->node[j].name, renamedbone[k].from))
{
strcpy(sequence[i].panim[0]->node[j].name, renamedbone[k].to);
break;
}
}
}
}
// map each sequences bone list to master list
for (i = 0; i < numseq; i++)
{
for (k = 0; k < MAXSTUDIOSRCBONES; k++)
{
sequence[i].panim[0]->boneimap[k] = -1;
}
for (j = 0; j < sequence[i].panim[0]->numbones; j++)
{
k = findNode(sequence[i].panim[0]->node[j].name);
if (k == -1)
{
// printf("unknown bone \"%s\" in sequence \"%s\"\n", sequence[i].panim[0]->node[j].name, sequence[i].name );
sequence[i].panim[0]->bonemap[j] = -1;
}
else
{
char* szAnim = "ROOT";
char* szNode = "ROOT";
// whoa, check parent connections!
if (sequence[i].panim[0]->node[j].parent != -1)
szAnim = sequence[i].panim[0]->node[sequence[i].panim[0]->node[j].parent].name;
if (bonetable[k].parent != -1)
szNode = bonetable[bonetable[k].parent].name;
if (strcmp(szAnim, szNode))
{
printf("illegal parent bone replacement in sequence \"%s\"\n\t\"%s\" has \"%s\", reference has \"%s\"\n",
sequence[i].name,
sequence[i].panim[0]->node[j].name,
szAnim,
szNode);
iError++;
}
sequence[i].panim[0]->bonemap[j] = k;
sequence[i].panim[0]->boneimap[k] = j;
// VectorCopy( sequence[i].panim[0]->pos[j][0].org, bonetable[k].pos );
// VectorCopy( sequence[i].panim[0]->rot[j][0].org, bonetable[k].rot );
}
}
}
if (iError && !(ignore_warnings))
{
exit(1);
}
// link bonecontrollers
for (i = 0; i < numbonecontrollers; i++)
{
for (j = 0; j < numbones; j++)
{
if (stricmp(bonecontroller[i].name, bonetable[j].name) == 0)
break;
}
if (j >= numbones)
{
Error("unknown bonecontroller link '%s'\n", bonecontroller[i].name);
}
bonecontroller[i].bone = j;
}
// link attachments
for (i = 0; i < numattachments; i++)
{
for (j = 0; j < numbones; j++)
{
if (stricmp(attachment[i].bonename, bonetable[j].name) == 0)
break;
}
if (j >= numbones)
{
Error("unknown attachment link '%s'\n", attachment[i].bonename);
}
attachment[i].bone = j;
}
// relink model
for (i = 0; i < nummodels; i++)
{
for (j = 0; j < model[i]->numverts; j++)
{
model[i]->vert[j].bone = model[i]->bonemap[model[i]->vert[j].bone];
}
for (j = 0; j < model[i]->numnorms; j++)
{
model[i]->normal[j].bone = model[i]->bonemap[model[i]->normal[j].bone];
}
}
// set hitgroups
for (k = 0; k < numbones; k++)
{
bonetable[k].group = -9999;
}
for (j = 0; j < numhitgroups; j++)
{
for (k = 0; k < numbones; k++)
{
if (strcmpi(bonetable[k].name, hitgroup[j].name) == 0)
{
bonetable[k].group = hitgroup[j].group;
break;
}
}
if (k >= numbones)
Error("cannot find bone %s for hitgroup %d\n", hitgroup[j].name, hitgroup[j].group);
}
for (k = 0; k < numbones; k++)
{
if (bonetable[k].group == -9999)
{
if (bonetable[k].parent != -1)
bonetable[k].group = bonetable[bonetable[k].parent].group;
else
bonetable[k].group = 0;
}
}
if (numhitboxes == 0)
{
// find intersection box volume for each bone
for (k = 0; k < numbones; k++)
{
for (j = 0; j < 3; j++)
{
bonetable[k].bmin[j] = 0.0;
bonetable[k].bmax[j] = 0.0;
}
}
// try all the connect vertices
for (i = 0; i < nummodels; i++)
{
vec3_t p;
for (j = 0; j < model[i]->numverts; j++)
{
VectorCopy(model[i]->vert[j].org, p);
k = model[i]->vert[j].bone;
if (p[0] < bonetable[k].bmin[0])
bonetable[k].bmin[0] = p[0];
if (p[1] < bonetable[k].bmin[1])
bonetable[k].bmin[1] = p[1];
if (p[2] < bonetable[k].bmin[2])
bonetable[k].bmin[2] = p[2];
if (p[0] > bonetable[k].bmax[0])
bonetable[k].bmax[0] = p[0];
if (p[1] > bonetable[k].bmax[1])
bonetable[k].bmax[1] = p[1];
if (p[2] > bonetable[k].bmax[2])
bonetable[k].bmax[2] = p[2];
}
}
// add in all your children as well
for (k = 0; k < numbones; k++)
{
if ((j = bonetable[k].parent) != -1)
{
if (bonetable[k].pos[0] < bonetable[j].bmin[0])
bonetable[j].bmin[0] = bonetable[k].pos[0];
if (bonetable[k].pos[1] < bonetable[j].bmin[1])
bonetable[j].bmin[1] = bonetable[k].pos[1];
if (bonetable[k].pos[2] < bonetable[j].bmin[2])
bonetable[j].bmin[2] = bonetable[k].pos[2];
if (bonetable[k].pos[0] > bonetable[j].bmax[0])
bonetable[j].bmax[0] = bonetable[k].pos[0];
if (bonetable[k].pos[1] > bonetable[j].bmax[1])
bonetable[j].bmax[1] = bonetable[k].pos[1];
if (bonetable[k].pos[2] > bonetable[j].bmax[2])
bonetable[j].bmax[2] = bonetable[k].pos[2];
}
}
for (k = 0; k < numbones; k++)
{
if (bonetable[k].bmin[0] < bonetable[k].bmax[0] - 1 && bonetable[k].bmin[1] < bonetable[k].bmax[1] - 1 && bonetable[k].bmin[2] < bonetable[k].bmax[2] - 1)
{
hitbox[numhitboxes].bone = k;
hitbox[numhitboxes].group = bonetable[k].group;
VectorCopy(bonetable[k].bmin, hitbox[numhitboxes].bmin);
VectorCopy(bonetable[k].bmax, hitbox[numhitboxes].bmax);
if (dump_hboxes)
{
printf("$hbox %d \"%s\" %.2f %.2f %.2f %.2f %.2f %.2f\n",
hitbox[numhitboxes].group,
bonetable[hitbox[numhitboxes].bone].name,
hitbox[numhitboxes].bmin[0], hitbox[numhitboxes].bmin[1], hitbox[numhitboxes].bmin[2],
hitbox[numhitboxes].bmax[0], hitbox[numhitboxes].bmax[1], hitbox[numhitboxes].bmax[2]);
}
numhitboxes++;
}
}
}
else
{
for (j = 0; j < numhitboxes; j++)
{
for (k = 0; k < numbones; k++)
{
if (strcmpi(bonetable[k].name, hitbox[j].name) == 0)
{
hitbox[j].bone = k;
break;
}
}
if (k >= numbones)
Error("cannot find bone %s for bbox\n", hitbox[j].name);
}
}
// relink animations
for (i = 0; i < numseq; i++)
{
vec3_t* origpos[MAXSTUDIOSRCBONES];
vec3_t* origrot[MAXSTUDIOSRCBONES];
for (q = 0; q < sequence[i].numblends; q++)
{
// save pointers to original animations
for (j = 0; j < sequence[i].panim[q]->numbones; j++)
{
origpos[j] = sequence[i].panim[q]->pos[j];
origrot[j] = sequence[i].panim[q]->rot[j];
}
for (j = 0; j < numbones; j++)
{
if ((k = sequence[i].panim[0]->boneimap[j]) >= 0)
{
// link to original animations
sequence[i].panim[q]->pos[j] = origpos[k];
sequence[i].panim[q]->rot[j] = origrot[k];
}
else
{
// link to dummy animations
sequence[i].panim[q]->pos[j] = defaultpos[j];
sequence[i].panim[q]->rot[j] = defaultrot[j];
}
}
}
// printf("%s %f\n", sequence[i].name, sequence[i].panim[0]->pos[3][0][0] );
}
// find scales for all bones
for (j = 0; j < numbones; j++)
{
for (k = 0; k < 6; k++)
{
float minv, maxv, scale;
if (k < 3)
{
minv = -128.0;
maxv = 128.0;
}
else
{
minv = -Q_PI / 8.0;
maxv = Q_PI / 8.0;
}
for (i = 0; i < numseq; i++)
{
for (q = 0; q < sequence[i].numblends; q++)
{
for (n = 0; n < sequence[i].numframes; n++)
{
float v = 0;
switch (k)
{
case 0:
case 1:
case 2:
v = (sequence[i].panim[q]->pos[j][n][k] - bonetable[j].pos[k]);
break;
case 3:
case 4:
case 5:
v = (sequence[i].panim[q]->rot[j][n][k - 3] - bonetable[j].rot[k - 3]);
if (v >= Q_PI)
v -= Q_PI * 2;
if (v < -Q_PI)
v += Q_PI * 2;
break;
}
if (v < minv)
minv = v;
if (v > maxv)
maxv = v;
}
}
}
if (minv < maxv)
{
if (-minv > maxv)
{
scale = minv / -32768.0;
}
else
{
scale = maxv / 32767;
}
}
else
{
scale = 1.0 / 32.0;
}
switch (k)
{
case 0:
case 1:
case 2:
bonetable[j].posscale[k] = scale;
break;
case 3:
case 4:
case 5:
bonetable[j].rotscale[k - 3] = scale;
break;
}
// printf("%.0f ", 1.0 / scale );
}
// printf("\n" );
}
// find bounding box for each sequence
for (i = 0; i < numseq; i++)
{
vec3_t bmin, bmax;
// find intersection box volume for each bone
for (j = 0; j < 3; j++)
{
bmin[j] = 9999.0;
bmax[j] = -9999.0;
}
for (q = 0; q < sequence[i].numblends; q++)
{
for (n = 0; n < sequence[i].numframes; n++)
{
float bonetransform[MAXSTUDIOBONES][3][4]; // bone transformation matrix
float bonematrix[3][4]; // local transformation matrix
vec3_t pos;
for (j = 0; j < numbones; j++)
{
vec3_t angle;
// convert to degrees
angle[0] = sequence[i].panim[q]->rot[j][n][0] * (180.0 / Q_PI);
angle[1] = sequence[i].panim[q]->rot[j][n][1] * (180.0 / Q_PI);
angle[2] = sequence[i].panim[q]->rot[j][n][2] * (180.0 / Q_PI);
AngleMatrix(angle, bonematrix);
bonematrix[0][3] = sequence[i].panim[q]->pos[j][n][0];
bonematrix[1][3] = sequence[i].panim[q]->pos[j][n][1];
bonematrix[2][3] = sequence[i].panim[q]->pos[j][n][2];
if (bonetable[j].parent == -1)
{
MatrixCopy(bonematrix, bonetransform[j]);
}
else
{
R_ConcatTransforms(bonetransform[bonetable[j].parent], bonematrix, bonetransform[j]);
}
}
for (k = 0; k < nummodels; k++)
{
for (j = 0; j < model[k]->numverts; j++)
{
VectorTransform(model[k]->vert[j].org, bonetransform[model[k]->vert[j].bone], pos);
if (pos[0] < bmin[0])
bmin[0] = pos[0];
if (pos[1] < bmin[1])
bmin[1] = pos[1];
if (pos[2] < bmin[2])
bmin[2] = pos[2];
if (pos[0] > bmax[0])
bmax[0] = pos[0];
if (pos[1] > bmax[1])
bmax[1] = pos[1];
if (pos[2] > bmax[2])
bmax[2] = pos[2];
}
}
}
}
VectorCopy(bmin, sequence[i].bmin);
VectorCopy(bmax, sequence[i].bmax);
/*
printf("%s : %.0f %.0f %.0f %.0f %.0f %.0f\n",
sequence[i].name, bmin[0], bmax[0], bmin[1], bmax[1], bmin[2], bmax[2] );
*/
// printf("%s %.2f\n", sequence[i].name, sequence[i].panim[0]->pos[9][0][0] / bonetable[9].pos[0] );
}
// reduce animations
{
int total = 0;
int changes = 0;
int p;
for (i = 0; i < numseq; i++)
{
for (q = 0; q < sequence[i].numblends; q++)
{
for (j = 0; j < numbones; j++)
{
for (k = 0; k < 6; k++)
{
mstudioanimvalue_t *pcount, *pvalue;
float v;
short value[MAXSTUDIOANIMATIONS];
mstudioanimvalue_t data[MAXSTUDIOANIMATIONS];
for (n = 0; n < sequence[i].numframes; n++)
{
switch (k)
{
case 0:
case 1:
case 2:
value[n] = (sequence[i].panim[q]->pos[j][n][k] - bonetable[j].pos[k]) / bonetable[j].posscale[k];
break;
case 3:
case 4:
case 5:
v = (sequence[i].panim[q]->rot[j][n][k - 3] - bonetable[j].rot[k - 3]);
if (v >= Q_PI)
v -= Q_PI * 2;
if (v < -Q_PI)
v += Q_PI * 2;
value[n] = v / bonetable[j].rotscale[k - 3];
break;
}
}
if (n == 0)
Error("no animation frames: \"%s\"\n", sequence[i].name);
sequence[i].panim[q]->numanim[j][k] = 0;
memset(data, 0, sizeof(data));
pcount = data;
pvalue = pcount + 1;
pcount->num.valid = 1;
pcount->num.total = 1;
pvalue->value = value[0];
pvalue++;
for (m = 1, p = 0; m < n; m++)
{
if (abs(value[p] - value[m]) > 1600)
{
changes++;
p = m;
}
}
// this compression algorithm needs work
for (m = 1; m < n; m++)
{
if (pcount->num.total == 255)
{
// too many, force a new entry
pcount = pvalue;
pvalue = pcount + 1;
pcount->num.valid++;
pvalue->value = value[m];
pvalue++;
}
// insert value if they're not equal,
// or if we're not on a run and the run is less than 3 units
else if ((value[m] != value[m - 1]) || ((pcount->num.total == pcount->num.valid) && ((m < n - 1) && value[m] != value[m + 1])))
{
total++;
if (pcount->num.total != pcount->num.valid)
{
//if (j == 0) printf("%d:%d ", pcount->num.valid, pcount->num.total );
pcount = pvalue;
pvalue = pcount + 1;
}
pcount->num.valid++;
pvalue->value = value[m];
pvalue++;
}
pcount->num.total++;
}
//if (j == 0) printf("%d:%d\n", pcount->num.valid, pcount->num.total );
sequence[i].panim[q]->numanim[j][k] = pvalue - data;
if (sequence[i].panim[q]->numanim[j][k] == 2 && value[0] == 0)
{
sequence[i].panim[q]->numanim[j][k] = 0;
}
else
{
sequence[i].panim[q]->anim[j][k] = reinterpret_cast<mstudioanimvalue_t*>(kalloc(pvalue - data, sizeof(mstudioanimvalue_t)));
memmove(sequence[i].panim[q]->anim[j][k], data, (pvalue - data) * sizeof(mstudioanimvalue_t));
}
// printf("%d(%d) ", sequence[i].panim[q]->numanim[j][k], n );
}
// printf("\n");
}
}
}
// printf("total %.0f changes %.0f\n", total, changes );
}
// auto groups
if (numseqgroups == 1 && maxseqgroupsize < 1024 * 1024)
{
int current = 0;
numseqgroups = 2;
for (i = 0; i < numseq; i++)
{
int accum = 0;
if (sequence[i].activity == 0)
{
for (q = 0; q < sequence[i].numblends; q++)
{
for (j = 0; j < numbones; j++)
{
for (k = 0; k < 6; k++)
{
accum += sequence[i].panim[q]->numanim[j][k] * sizeof(mstudioanimvalue_t);
}
}
}
accum += sequence[i].numblends * numbones * sizeof(mstudioanim_t);
if (current && current + accum > maxseqgroupsize)
{
numseqgroups++;
current = accum;
}
else
{
current += accum;
}
// printf("%d %d %d\n", numseqgroups, current, accum );
sequence[i].seqgroup = numseqgroups - 1;
}
else
{
sequence[i].seqgroup = 0;
}
}
}
}
/*
=================
=================
*/
int lookupControl(char* string)
{
if (stricmp(string, "X") == 0)
return STUDIO_X;
if (stricmp(string, "Y") == 0)
return STUDIO_Y;
if (stricmp(string, "Z") == 0)
return STUDIO_Z;
if (stricmp(string, "XR") == 0)
return STUDIO_XR;
if (stricmp(string, "YR") == 0)
return STUDIO_YR;
if (stricmp(string, "ZR") == 0)
return STUDIO_ZR;
if (stricmp(string, "LX") == 0)
return STUDIO_LX;
if (stricmp(string, "LY") == 0)
return STUDIO_LY;
if (stricmp(string, "LZ") == 0)
return STUDIO_LZ;
if (stricmp(string, "AX") == 0)
return STUDIO_AX;
if (stricmp(string, "AY") == 0)
return STUDIO_AY;
if (stricmp(string, "AZ") == 0)
return STUDIO_AZ;
if (stricmp(string, "AXR") == 0)
return STUDIO_AXR;
if (stricmp(string, "AYR") == 0)
return STUDIO_AYR;
if (stricmp(string, "AZR") == 0)
return STUDIO_AZR;
return -1;
}
// search case-insensitive for string2 in string
char* stristr(const char* string, const char* string2)
{
int c, len;
c = tolower(*string2);
len = strlen(string2);
while (string)
{
for (; *string && tolower(*string) != c; string++)
;
if (*string)
{
if (strnicmp(string, string2, len) == 0)
{
break;
}
string++;
}
else
{
return NULL;
}
}
return (char*)string;
}
/*
=================
=================
*/
int lookup_texture(char* texturename)
{
int i;
for (i = 0; i < numtextures; i++)
{
if (stricmp(texture[i].name, texturename) == 0)
{
return i;
}
}
strcpyn(texture[i].name, texturename);
if (stristr(texturename, "chrome") != NULL)
{
texture[i].flags = STUDIO_NF_FLATSHADE | STUDIO_NF_CHROME;
}
else
{
texture[i].flags = 0;
}
numtextures++;
return i;
}
s_mesh_t* lookup_mesh(s_model_t* pmodel, char* texturename)
{
int i, j;
j = lookup_texture(texturename);
for (i = 0; i < pmodel->nummesh; i++)
{
if (pmodel->pmesh[i]->skinref == j)
{
return pmodel->pmesh[i];
}
}
if (i >= MAXSTUDIOMESHES)
{
Error("too many textures in model: \"%s\"\n", pmodel->name);
}
pmodel->nummesh = i + 1;
pmodel->pmesh[i] = reinterpret_cast<s_mesh_t*>(kalloc(1, sizeof(s_mesh_t)));
pmodel->pmesh[i]->skinref = j;
return pmodel->pmesh[i];
}
s_trianglevert_t* lookup_triangle(s_mesh_t* pmesh, int index)
{
if (index >= pmesh->alloctris)
{
int start = pmesh->alloctris;
pmesh->alloctris = index + 256;
if (pmesh->triangle)
{
pmesh->triangle = reinterpret_cast<s_trianglevert_t(*)[3]>(realloc(pmesh->triangle, pmesh->alloctris * sizeof(*pmesh->triangle)));
kmemset(&pmesh->triangle[start], 0, (pmesh->alloctris - start) * sizeof(*pmesh->triangle));
}
else
{
pmesh->triangle = reinterpret_cast<s_trianglevert_t(*)[3]>(kalloc(pmesh->alloctris, sizeof(*pmesh->triangle)));
}
}
return pmesh->triangle[index];
}
int lookup_normal(s_model_t* pmodel, s_normal_t* pnormal)
{
int i;
for (i = 0; i < pmodel->numnorms; i++)
{
// if (VectorCompare( pmodel->normal[i].org, pnormal->org )
if (DotProduct(pmodel->normal[i].org, pnormal->org) > normal_blend && pmodel->normal[i].bone == pnormal->bone && pmodel->normal[i].skinref == pnormal->skinref)
{
return i;
}
}
if (i >= MAXSTUDIOVERTS)
{
Error("too many normals in model: \"%s\"\n", pmodel->name);
}
VectorCopy(pnormal->org, pmodel->normal[i].org);
pmodel->normal[i].bone = pnormal->bone;
pmodel->normal[i].skinref = pnormal->skinref;
pmodel->numnorms = i + 1;
return i;
}
int lookup_vertex(s_model_t* pmodel, s_vertex_t* pv)
{
int i;
// assume 2 digits of accuracy
pv->org[0] = (int)(pv->org[0] * 100) / 100.0;
pv->org[1] = (int)(pv->org[1] * 100) / 100.0;
pv->org[2] = (int)(pv->org[2] * 100) / 100.0;
for (i = 0; i < pmodel->numverts; i++)
{
if (VectorCompare(pmodel->vert[i].org, pv->org) && pmodel->vert[i].bone == pv->bone)
{
return i;
}
}
if (i >= MAXSTUDIOVERTS)
{
Error("too many vertices in model: \"%s\"\n", pmodel->name);
}
VectorCopy(pv->org, pmodel->vert[i].org);
pmodel->vert[i].bone = pv->bone;
pmodel->numverts = i + 1;
return i;
}
void adjust_vertex(float* org)
{
org[0] = (org[0] - adjust[0]);
org[1] = (org[1] - adjust[1]);
org[2] = (org[2] - adjust[2]);
}
void scale_vertex(float* org)
{
org[0] = org[0] * scale_up;
org[1] = org[1] * scale_up;
org[2] = org[2] * scale_up;
}
/*
============
SetSkinValues
Called for the base frame
============
*/
void TextureCoordRanges(s_mesh_t* pmesh, s_texture_t* ptexture)
{
int i, j;
if (ptexture->flags & STUDIO_NF_CHROME)
{
ptexture->skintop = 0;
ptexture->skinleft = 0;
ptexture->skinwidth = (ptexture->srcwidth + 3) & ~3;
ptexture->skinheight = ptexture->srcheight;
for (i = 0; i < pmesh->numtris; i++)
{
for (j = 0; j < 3; j++)
{
pmesh->triangle[i][j].s = 0;
pmesh->triangle[i][j].t = 0;
}
ptexture->max_s = 63;
ptexture->min_s = 0;
ptexture->max_t = 63;
ptexture->min_t = 0;
}
return;
}
// clip texture coords.
for (i = 0; i < pmesh->numtris; i++)
{
if (pmesh->triangle[i][0].u > 100.0 || pmesh->triangle[i][1].u > 100.0 || pmesh->triangle[i][2].u > 100.0)
{
// printf("%d : %f %f %f\n", i, pmesh->triangle[i][0].u, pmesh->triangle[i][1].u, pmesh->triangle[i][2].u );
}
if (pmesh->triangle[i][0].v > 100.0 || pmesh->triangle[i][1].v > 100.0 || pmesh->triangle[i][2].v > 100.0)
{
// printf("%d : %f %f %f\n", i, pmesh->triangle[i][0].v, pmesh->triangle[i][1].v, pmesh->triangle[i][2].v );
}
}
for (i = 0; i < pmesh->numtris; i++)
{
for (j = 0; j < 3; j++)
{
if (pmesh->triangle[i][j].u > 2.0)
pmesh->triangle[i][j].u = 2.0;
if (pmesh->triangle[i][j].u < -1.0)
pmesh->triangle[i][j].u = -1.0;
if (pmesh->triangle[i][j].v > 2.0)
pmesh->triangle[i][j].v = 2.0;
if (pmesh->triangle[i][j].v < -1.0)
pmesh->triangle[i][j].v = -1.0;
}
}
// pack texture coords
if (!clip_texcoords)
{
int k, n;
do
{
float min_u = 10;
float max_u = -10;
float k_max_u = max_u, n_min_u = min_u;
k = -1;
n = -1;
for (i = 0; i < pmesh->numtris; i++)
{
float local_min, local_max;
local_min = std::min(pmesh->triangle[i][0].u, std::min(pmesh->triangle[i][1].u, pmesh->triangle[i][2].u));
local_max = std::max(pmesh->triangle[i][0].u, std::max(pmesh->triangle[i][1].u, pmesh->triangle[i][2].u));
if (local_min < min_u)
{
min_u = local_min;
k = i;
k_max_u = local_max;
}
if (local_max > max_u)
{
max_u = local_max;
n = i;
n_min_u = local_min;
}
}
if (k_max_u + 1.0 < max_u)
{
//printf("%d %f %f\n", k, k_max_u, max_u );
for (j = 0; j < 3; j++)
pmesh->triangle[k][j].u += 1.0;
}
else if (n_min_u - 1.0 > min_u)
{
//printf("%d %f %f\n", n, n_min_u, min_u );
for (j = 0; j < 3; j++)
pmesh->triangle[n][j].u -= 1.0;
}
else
{
break;
}
} while (1);
do
{
float min_v = 10;
float max_v = -10;
float k_max_v = max_v, n_min_v = min_v;
k = -1;
n = -1;
for (i = 0; i < pmesh->numtris; i++)
{
float local_min, local_max;
local_min = std::min(pmesh->triangle[i][0].v, std::min(pmesh->triangle[i][1].v, pmesh->triangle[i][2].v));
local_max = std::max(pmesh->triangle[i][0].v, std::max(pmesh->triangle[i][1].v, pmesh->triangle[i][2].v));
if (local_min < min_v)
{
min_v = local_min;
k = i;
k_max_v = local_max;
}
if (local_max > max_v)
{
max_v = local_max;
n = i;
n_min_v = local_min;
}
}
if (k_max_v + 1.0 < max_v)
{
//printf("%d %f %f\n", k, k_max_v, max_v );
for (j = 0; j < 3; j++)
pmesh->triangle[k][j].v += 1.0;
}
else if (n_min_v - 1.0 > min_v)
{
//printf("%d %f %f\n", n, n_min_v, min_v );
for (j = 0; j < 3; j++)
pmesh->triangle[n][j].v -= 1.0;
}
else
{
break;
}
} while (1);
}
else
{
for (i = 0; i < pmesh->numtris; i++)
{
for (j = 0; j < 3; j++)
{
if (pmesh->triangle[i][j].u < 0)
pmesh->triangle[i][j].u = 0;
if (pmesh->triangle[i][j].u > 1)
pmesh->triangle[i][j].u = 1;
if (pmesh->triangle[i][j].v < 0)
pmesh->triangle[i][j].v = 0;
if (pmesh->triangle[i][j].v > 1)
pmesh->triangle[i][j].v = 1;
}
}
}
// convert to pixel coordinates
for (i = 0; i < pmesh->numtris; i++)
{
for (j = 0; j < 3; j++)
{
// FIXME losing texture coord resultion!
pmesh->triangle[i][j].s = pmesh->triangle[i][j].u * (ptexture->srcwidth - 1);
pmesh->triangle[i][j].t = pmesh->triangle[i][j].v * (ptexture->srcheight - 1);
}
}
// find the range
if (!clip_texcoords)
{
for (i = 0; i < pmesh->numtris; i++)
{
for (j = 0; j < 3; j++)
{
ptexture->max_s = std::max(static_cast<float>(pmesh->triangle[i][j].s), ptexture->max_s);
ptexture->min_s = std::min(static_cast<float>(pmesh->triangle[i][j].s), ptexture->min_s);
ptexture->max_t = std::max(static_cast<float>(pmesh->triangle[i][j].t), ptexture->max_t);
ptexture->min_t = std::min(static_cast<float>(pmesh->triangle[i][j].t), ptexture->min_t);
}
}
}
else
{
ptexture->max_s = ptexture->srcwidth - 1;
ptexture->min_s = 0;
ptexture->max_t = ptexture->srcheight - 1;
ptexture->min_t = 0;
}
//printf("%d %d : ", ptexture->srcwidth, ptexture->srcheight );
//printf("%.0f %.0f %.0f %.0f\n", ptexture->min_s, ptexture->max_s, ptexture->min_t, ptexture->max_t );
}
void ResetTextureCoordRanges(s_mesh_t* pmesh, s_texture_t* ptexture)
{
int i, j;
// adjust top, left edge
for (i = 0; i < pmesh->numtris; i++)
{
for (j = 0; j < 3; j++)
{
pmesh->triangle[i][j].s -= ptexture->min_s;
// quake wants t inverted
pmesh->triangle[i][j].t = (ptexture->max_t - ptexture->min_t) - (pmesh->triangle[i][j].t - ptexture->min_t);
}
}
}
/*
===============
Grab_Skin
===============
*/
void Grab_BMP(char* filename, s_texture_t* ptexture)
{
int result;
if (result = LoadBMP(filename, &ptexture->ppicture, (byte**)&ptexture->ppal); result != 0)
{
Error("error %d reading BMP image \"%s\"\n", result, filename);
}
ptexture->srcwidth = bmhd.w;
ptexture->srcheight = bmhd.h;
}
#define MIN_DIMENSION 8
#define MAX_DIMENSION 512
int GetBestPowerOf2(int value)
{
int i;
int power = MIN_DIMENSION;
for (i = 0; i < 32; i++)
{
if ((1 << i) < MIN_DIMENSION)
continue;
if ((1 << i) > MAX_DIMENSION)
continue;
power = (1 << i);
if (power >= value)
{
break;
}
}
return power;
}
int GetSkinWidth(int rawsize)
{
if (!force_powerof2_textures)
{
return (int)(rawsize + 3) & ~3;
}
return GetBestPowerOf2(rawsize);
}
int GetSkinHeight(int rawsize)
{
if (!force_powerof2_textures)
{
return (rawsize);
}
return GetBestPowerOf2(rawsize);
}
void ResizeTexture(s_texture_t* ptexture)
{
int i, j, s, t;
byte* pdest;
int srcadjwidth;
// make the width a multiple of 4; some hardware requires this, and it ensures
// dword alignment for each scan
ptexture->skintop = ptexture->min_t;
ptexture->skinleft = ptexture->min_s;
ptexture->skinwidth = GetSkinWidth(ptexture->max_s - ptexture->min_s + 1);
ptexture->skinheight = GetSkinHeight(ptexture->max_t - ptexture->min_t + 1);
ptexture->size = ptexture->skinwidth * ptexture->skinheight + 256 * 3;
printf("BMP %s [%d %d] (%.0f%%) %6d bytes\n", ptexture->name, ptexture->skinwidth, ptexture->skinheight,
((ptexture->skinwidth * ptexture->skinheight) / (float)(ptexture->srcwidth * ptexture->srcheight)) * 100.0,
ptexture->size);
if (ptexture->size > 640 * 480)
{
printf("%.0f %.0f %.0f %.0f\n", ptexture->min_s, ptexture->max_s, ptexture->min_t, ptexture->max_t);
Error("texture too large\n");
}
pdest = reinterpret_cast<byte*>(malloc(ptexture->size));
ptexture->pdata = pdest;
// data is saved as a multiple of 4
srcadjwidth = (ptexture->srcwidth + 3) & ~3;
// move the picture data to the model area, replicating missing data, deleting unused data.
for (i = 0, t = ptexture->srcheight - ptexture->skinheight - ptexture->skintop + 10 * ptexture->srcheight; i < ptexture->skinheight; i++, t++)
{
while (t >= ptexture->srcheight)
t -= ptexture->srcheight;
while (t < 0)
t += ptexture->srcheight;
for (j = 0, s = ptexture->skinleft + 10 * ptexture->srcwidth; j < ptexture->skinwidth; j++, s++)
{
while (s >= ptexture->srcwidth)
s -= ptexture->srcwidth;
*(pdest++) = *(ptexture->ppicture + s + t * srcadjwidth);
}
}
// TODO: process the texture and flag it if fullbright or transparent are used.
// TODO: only save as many palette entries as are actually used.
if (gamma != 1.8)
{
// gamma correct the monster textures to a gamma of 1.8
float g;
byte* psrc = (byte*)ptexture->ppal;
g = gamma / 1.8;
for (i = 0; i < 768; i++)
{
pdest[i] = pow(psrc[i] / 255.0, g) * 255;
}
}
else
{
memcpy(pdest, ptexture->ppal, 256 * sizeof(rgb_t));
}
free(ptexture->ppicture);
free(ptexture->ppal);
}
void Grab_Skin(s_texture_t* ptexture)
{
char file1[1024];
int time1 = 0;
sprintf(file1, "%s/%s", cdpartial, ptexture->name);
ExpandPathAndArchive(file1);
if (cdtextureset > 0)
{
int i;
for (i = 0; i < cdtextureset; i++)
{
sprintf(file1, "%s/%s", cdtexture[i], ptexture->name);
time1 = FileTime(file1);
if (time1 != -1)
break;
}
if (time1 == -1)
Error("%s not found", file1);
}
else
{
sprintf(file1, "%s/%s", cddir, ptexture->name);
}
if (stricmp(".bmp", &file1[strlen(file1) - 4]) == 0)
{
Grab_BMP(file1, ptexture);
}
else
{
Error("unknown graphics type: \"%s\"\n", file1);
}
}
void SetSkinValues()
{
int i, j;
int index;
for (i = 0; i < numtextures; i++)
{
Grab_Skin(&texture[i]);
texture[i].max_s = -9999999;
texture[i].min_s = 9999999;
texture[i].max_t = -9999999;
texture[i].min_t = 9999999;
}
for (i = 0; i < nummodels; i++)
{
for (j = 0; j < model[i]->nummesh; j++)
{
TextureCoordRanges(model[i]->pmesh[j], &texture[model[i]->pmesh[j]->skinref]);
}
}
for (i = 0; i < numtextures; i++)
{
if (texture[i].max_s < texture[i].min_s)
{
// must be a replacement texture
if (texture[i].flags & STUDIO_NF_CHROME)
{
texture[i].max_s = 63;
texture[i].min_s = 0;
texture[i].max_t = 63;
texture[i].min_t = 0;
}
else
{
texture[i].max_s = texture[texture[i].parent].max_s;
texture[i].min_s = texture[texture[i].parent].min_s;
texture[i].max_t = texture[texture[i].parent].max_t;
texture[i].min_t = texture[texture[i].parent].min_t;
}
}
ResizeTexture(&texture[i]);
}
for (i = 0; i < nummodels; i++)
{
for (j = 0; j < model[i]->nummesh; j++)
{
ResetTextureCoordRanges(model[i]->pmesh[j], &texture[model[i]->pmesh[j]->skinref]);
}
}
// build texture groups
for (i = 0; i < MAXSTUDIOSKINS; i++)
{
for (j = 0; j < MAXSTUDIOSKINS; j++)
{
skinref[i][j] = j;
}
}
index = 0;
for (i = 0; i < numtexturelayers[0]; i++)
{
for (j = 0; j < numtexturereps[0]; j++)
{
skinref[i][texturegroup[0][0][j]] = texturegroup[0][i][j];
}
}
if (i != 0)
{
numskinfamilies = i;
}
else
{
numskinfamilies = 1;
numskinref = numtextures;
}
// printf ("width: %i height: %i\n",width, height);
/*
printf ("adjusted width: %i height: %i top : %i left: %i\n",
pmesh->skinwidth, pmesh->skinheight, pmesh->skintop, pmesh->skinleft );
*/
}
/*
=================
=================
*/
char filename[1024];
FILE* input;
char line[1024];
int linecount;
void Build_Reference(s_model_t* pmodel)
{
int i, parent;
float angle[3];
for (i = 0; i < pmodel->numbones; i++)
{
float m[3][4];
vec3_t p;
// convert to degrees
angle[0] = pmodel->skeleton[i].rot[0] * (180.0 / Q_PI);
angle[1] = pmodel->skeleton[i].rot[1] * (180.0 / Q_PI);
angle[2] = pmodel->skeleton[i].rot[2] * (180.0 / Q_PI);
parent = pmodel->node[i].parent;
if (parent == -1)
{
// scale the done pos.
// calc rotational matrices
AngleMatrix(angle, bonefixup[i].m);
AngleIMatrix(angle, bonefixup[i].im);
VectorCopy(pmodel->skeleton[i].pos, bonefixup[i].worldorg);
}
else
{
// calc compound rotational matrices
// FIXME : Hey, it's orthogical so inv(A) == transpose(A)
AngleMatrix(angle, m);
R_ConcatTransforms(bonefixup[parent].m, m, bonefixup[i].m);
AngleIMatrix(angle, m);
R_ConcatTransforms(m, bonefixup[parent].im, bonefixup[i].im);
// calc true world coord.
VectorTransform(pmodel->skeleton[i].pos, bonefixup[parent].m, p);
VectorAdd(p, bonefixup[parent].worldorg, bonefixup[i].worldorg);
}
// printf("%3d %f %f %f\n", i, bonefixup[i].worldorg[0], bonefixup[i].worldorg[1], bonefixup[i].worldorg[2] );
/*
AngleMatrix( angle, m );
printf("%8.4f %8.4f %8.4f\n", m[0][0], m[1][0], m[2][0] );
printf("%8.4f %8.4f %8.4f\n", m[0][1], m[1][1], m[2][1] );
printf("%8.4f %8.4f %8.4f\n", m[0][2], m[1][2], m[2][2] );
*/
}
}
void Grab_Triangles(s_model_t* pmodel)
{
int i, j;
int tcount = 0;
int ncount = 0;
vec3_t vmin, vmax;
vmin[0] = vmin[1] = vmin[2] = 99999;
vmax[0] = vmax[1] = vmax[2] = -99999;
Build_Reference(pmodel);
//
// load the base triangles
//
while (1)
{
if (fgets(line, sizeof(line), input) != NULL)
{
s_mesh_t* pmesh;
char texturename[64];
s_trianglevert_t* ptriv;
int bone;
vec3_t vert[3];
vec3_t norm[3];
linecount++;
// check for end
if (strcmp("end\n", line) == 0)
return;
// strip off trailing smag
strcpy(texturename, line);
for (i = strlen(texturename) - 1; i >= 0 && !isgraph(texturename[i]); i--)
;
texturename[i + 1] = '\0';
// funky texture overrides
for (i = 0; i < numrep; i++)
{
if (sourcetexture[i][0] == '\0')
{
strcpy(texturename, defaulttexture[i]);
break;
}
if (stricmp(texturename, sourcetexture[i]) == 0)
{
strcpy(texturename, defaulttexture[i]);
break;
}
}
if (texturename[0] == '\0')
{
// weird model problem, skip them
fgets(line, sizeof(line), input);
fgets(line, sizeof(line), input);
fgets(line, sizeof(line), input);
linecount += 3;
continue;
}
pmesh = lookup_mesh(pmodel, texturename);
for (j = 0; j < 3; j++)
{
if (flip_triangles)
// quake wants them in the reverse order
ptriv = lookup_triangle(pmesh, pmesh->numtris) + 2 - j;
else
ptriv = lookup_triangle(pmesh, pmesh->numtris) + j;
if (fgets(line, sizeof(line), input) != NULL)
{
s_vertex_t p;
vec3_t tmp;
s_normal_t normal;
linecount++;
if (sscanf(line, "%d %f %f %f %f %f %f %f %f",
&bone,
&p.org[0], &p.org[1], &p.org[2],
&normal.org[0], &normal.org[1], &normal.org[2],
&ptriv->u, &ptriv->v) == 9)
{
if (bone < 0 || bone >= pmodel->numbones)
{
fprintf(stderr, "bogus bone index\n");
fprintf(stderr, "%d %s :\n%s", linecount, filename, line);
exit(1);
}
/*
if (ptriv->u > 2.0)
{
printf("%d %f\n", linecount, ptriv->u );
}
*/
VectorCopy(p.org, vert[j]);
VectorCopy(normal.org, norm[j]);
p.bone = bone;
normal.bone = bone;
normal.skinref = pmesh->skinref;
if (p.org[2] < vmin[2])
vmin[2] = p.org[2];
adjust_vertex(p.org);
scale_vertex(p.org);
// move vertex position to object space.
VectorSubtract(p.org, bonefixup[p.bone].worldorg, tmp);
VectorTransform(tmp, bonefixup[p.bone].im, p.org);
// move normal to object space.
VectorCopy(normal.org, tmp);
VectorTransform(tmp, bonefixup[p.bone].im, normal.org);
VectorNormalize(normal.org);
ptriv->normindex = lookup_normal(pmodel, &normal);
ptriv->vertindex = lookup_vertex(pmodel, &p);
// tag bone as being used
// pmodel->bone[bone].ref = 1;
}
else
{
Error("%s: error on line %d: %s", filename, linecount, line);
}
}
}
if (tag_reversed || tag_normals)
{
// check triangle direction
if (DotProduct(norm[0], norm[1]) < 0.0 || DotProduct(norm[1], norm[2]) < 0.0 || DotProduct(norm[2], norm[0]) < 0.0)
{
ncount++;
if (tag_normals)
{
// steal the triangle and make it white
s_trianglevert_t* ptriv2;
pmesh = lookup_mesh(pmodel, "..\\white.bmp");
ptriv2 = lookup_triangle(pmesh, pmesh->numtris);
ptriv2[0] = ptriv[0];
ptriv2[1] = ptriv[1];
ptriv2[2] = ptriv[2];
}
}
else
{
vec3_t a1, a2, sn;
float x, y, z;
VectorSubtract(vert[1], vert[0], a1);
VectorSubtract(vert[2], vert[0], a2);
CrossProduct(a1, a2, sn);
VectorNormalize(sn);
x = DotProduct(sn, norm[0]);
y = DotProduct(sn, norm[1]);
z = DotProduct(sn, norm[2]);
if (x < 0.0 || y < 0.0 || z < 0.0)
{
if (tag_reversed)
{
// steal the triangle and make it white
s_trianglevert_t* ptriv2;
printf("triangle reversed (%f %f %f)\n",
DotProduct(norm[0], norm[1]),
DotProduct(norm[1], norm[2]),
DotProduct(norm[2], norm[0]));
pmesh = lookup_mesh(pmodel, "..\\white.bmp");
ptriv2 = lookup_triangle(pmesh, pmesh->numtris);
ptriv2[0] = ptriv[0];
ptriv2[1] = ptriv[1];
ptriv2[2] = ptriv[2];
}
}
}
}
pmodel->trimesh[tcount] = pmesh;
pmodel->trimap[tcount] = pmesh->numtris++;
tcount++;
}
else
{
break;
}
}
if (ncount)
printf("%d triangles with misdirected normals\n", ncount);
if (vmin[2] != 0.0)
{
printf("lowest vector at %f\n", vmin[2]);
}
}
void Grab_Skeleton(s_node_t* pnodes, s_bone_t* pbones)
{
float x, y, z, xr, yr, zr;
char cmd[1024];
int index;
while (fgets(line, sizeof(line), input) != NULL)
{
linecount++;
if (sscanf(line, "%d %f %f %f %f %f %f", &index, &x, &y, &z, &xr, &yr, &zr) == 7)
{
pbones[index].pos[0] = x;
pbones[index].pos[1] = y;
pbones[index].pos[2] = z;
scale_vertex(pbones[index].pos);
if (pnodes[index].mirrored)
VectorScale(pbones[index].pos, -1.0, pbones[index].pos);
pbones[index].rot[0] = xr;
pbones[index].rot[1] = yr;
pbones[index].rot[2] = zr;
clip_rotations(pbones[index].rot);
}
else if (sscanf(line, "%s %d", cmd, &index))
{
if (strcmp(cmd, "time") == 0)
{
// pbones = pnode->bones[index] = kalloc(1, sizeof( s_bones_t ));
}
else if (strcmp(cmd, "end") == 0)
{
return;
}
}
}
}
int Grab_Nodes(s_node_t* pnodes)
{
int index;
char name[1024];
int parent;
int numnodebones = 0;
int i;
while (fgets(line, sizeof(line), input) != NULL)
{
linecount++;
if (sscanf(line, "%d \"%[^\"]\" %d", &index, name, &parent) == 3)
{
// check for duplicated bones
/*
if (strlen(pnodes[index].name) != 0)
{
Error( "bone \"%s\" exists more than once\n", name );
}
*/
strcpyn(pnodes[index].name, name);
pnodes[index].parent = parent;
numnodebones = index;
// check for mirrored bones;
for (i = 0; i < nummirrored; i++)
{
if (strcmp(name, mirrored[i]) == 0)
pnodes[index].mirrored = 1;
}
if ((!pnodes[index].mirrored) && parent != -1)
{
pnodes[index].mirrored = pnodes[pnodes[index].parent].mirrored;
}
}
else
{
return numnodebones + 1;
}
}
Error("Unexpected EOF at line %d\n", linecount);
}
void Grab_Studio(s_model_t* pmodel)
{
int time1;
char cmd[1024];
int option;
sprintf(filename, "%s/%s.smd", cddir, pmodel->name);
time1 = FileTime(filename);
if (time1 == -1)
Error("%s doesn't exist", filename);
printf("grabbing %s\n", filename);
if ((input = fopen(filename, "r")) == 0)
{
Error("reader: could not open file '%s'\n", filename);
}
linecount = 0;
while (fgets(line, sizeof(line), input) != NULL)
{
linecount++;
sscanf(line, "%s %d", cmd, &option);
if (strcmp(cmd, "version") == 0)
{
if (option != 1)
{
Error("bad version\n");
}
}
else if (strcmp(cmd, "nodes") == 0)
{
pmodel->numbones = Grab_Nodes(pmodel->node);
}
else if (strcmp(cmd, "skeleton") == 0)
{
Grab_Skeleton(pmodel->node, pmodel->skeleton);
}
else if (strcmp(cmd, "triangles") == 0)
{
Grab_Triangles(pmodel);
}
else
{
printf("unknown studio command\n");
}
}
fclose(input);
}
void clip_rotations(vec3_t rot)
{
int j;
// clip everything to : -Q_PI <= x < Q_PI
for (j = 0; j < 3; j++)
{
while (rot[j] >= Q_PI)
rot[j] -= Q_PI * 2;
while (rot[j] < -Q_PI)
rot[j] += Q_PI * 2;
}
}
/*
=================
Cmd_Eyeposition
=================
*/
void Cmd_Eyeposition(void)
{
// rotate points into frame of reference so model points down the positive x
// axis
GetToken(false);
eyeposition[1] = atof(token);
GetToken(false);
eyeposition[0] = -atof(token);
GetToken(false);
eyeposition[2] = atof(token);
}
/*
=================
Cmd_Flags
=================
*/
void Cmd_Flags(void)
{
GetToken(false);
gflags = atoi(token);
}
/*
=================
Cmd_Modelname
=================
*/
void Cmd_Modelname(void)
{
GetToken(false);
strcpyn(outname, token);
}
/*
===============
===============
*/
void Option_Studio()
{
if (!GetToken(false))
return;
model[nummodels] = reinterpret_cast<s_model_t*>(kalloc(1, sizeof(s_model_t)));
bodypart[numbodyparts].pmodel[bodypart[numbodyparts].nummodels] = model[nummodels];
strcpyn(model[nummodels]->name, token);
flip_triangles = 1;
scale_up = default_scale;
while (TokenAvailable())
{
GetToken(false);
if (stricmp("reverse", token) == 0)
{
flip_triangles = 0;
}
else if (stricmp("scale", token) == 0)
{
GetToken(false);
scale_up = atof(token);
}
}
Grab_Studio(model[nummodels]);
bodypart[numbodyparts].nummodels++;
nummodels++;
scale_up = default_scale;
}
int Option_Blank()
{
model[nummodels] = reinterpret_cast<s_model_t*>(kalloc(1, sizeof(s_model_t)));
bodypart[numbodyparts].pmodel[bodypart[numbodyparts].nummodels] = model[nummodels];
strcpyn(model[nummodels]->name, "blank");
bodypart[numbodyparts].nummodels++;
nummodels++;
return 0;
}
void Cmd_Bodygroup()
{
int is_started = 0;
if (!GetToken(false))
return;
if (numbodyparts == 0)
{
bodypart[numbodyparts].base = 1;
}
else
{
bodypart[numbodyparts].base = bodypart[numbodyparts - 1].base * bodypart[numbodyparts - 1].nummodels;
}
strcpyn(bodypart[numbodyparts].name, token);
do
{
GetToken(true);
if (endofscript)
return;
else if (token[0] == '{')
is_started = 1;
else if (token[0] == '}')
break;
else if (stricmp("studio", token) == 0)
Option_Studio();
else if (stricmp("blank", token) == 0)
Option_Blank();
} while (1);
numbodyparts++;
return;
}
void Cmd_Body()
{
if (!GetToken(false))
return;
if (numbodyparts == 0)
{
bodypart[numbodyparts].base = 1;
}
else
{
bodypart[numbodyparts].base = bodypart[numbodyparts - 1].base * bodypart[numbodyparts - 1].nummodels;
}
strcpyn(bodypart[numbodyparts].name, token);
Option_Studio();
numbodyparts++;
}
/*
===============
===============
*/
void Grab_Animation(s_animation_t* panim)
{
vec3_t pos;
vec3_t rot;
char cmd[1024];
int index;
int t = -99999999;
float cz, sz;
int start = 99999;
int end = 0;
for (index = 0; index < panim->numbones; index++)
{
panim->pos[index] = reinterpret_cast<vec3_t*>(kalloc(MAXSTUDIOANIMATIONS, sizeof(vec3_t)));
panim->rot[index] = reinterpret_cast<vec3_t*>(kalloc(MAXSTUDIOANIMATIONS, sizeof(vec3_t)));
}
cz = cos(zrotation);
sz = sin(zrotation);
while (fgets(line, sizeof(line), input) != NULL)
{
linecount++;
if (sscanf(line, "%d %f %f %f %f %f %f", &index, &pos[0], &pos[1], &pos[2], &rot[0], &rot[1], &rot[2]) == 7)
{
if (t >= panim->startframe && t <= panim->endframe)
{
if (panim->node[index].parent == -1)
{
adjust_vertex(pos);
panim->pos[index][t][0] = cz * pos[0] - sz * pos[1];
panim->pos[index][t][1] = sz * pos[0] + cz * pos[1];
panim->pos[index][t][2] = pos[2];
// rotate model
rot[2] += zrotation;
}
else
{
VectorCopy(pos, panim->pos[index][t]);
}
if (t > end)
end = t;
if (t < start)
start = t;
if (panim->node[index].mirrored)
VectorScale(panim->pos[index][t], -1.0, panim->pos[index][t]);
scale_vertex(panim->pos[index][t]);
clip_rotations(rot);
VectorCopy(rot, panim->rot[index][t]);
}
}
else if (sscanf(line, "%s %d", cmd, &index))
{
if (strcmp(cmd, "time") == 0)
{
t = index;
}
else if (strcmp(cmd, "end") == 0)
{
panim->startframe = start;
panim->endframe = end;
return;
}
else
{
Error("Error(%d) : %s", linecount, line);
}
}
else
{
Error("Error(%d) : %s", linecount, line);
}
}
Error("unexpected EOF: %s\n", panim->name);
}
void Shift_Animation(s_animation_t* panim)
{
int j;
int size;
size = (panim->endframe - panim->startframe + 1) * sizeof(vec3_t);
// shift
for (j = 0; j < panim->numbones; j++)
{
vec3_t* ppos;
vec3_t* prot;
k_memtotal -= MAXSTUDIOANIMATIONS * sizeof(vec3_t) * 2;
k_memtotal += size * 2;
ppos = reinterpret_cast<vec3_t*>(kalloc(1, size));
prot = reinterpret_cast<vec3_t*>(kalloc(1, size));
memmove(ppos, &panim->pos[j][panim->startframe], size);
memmove(prot, &panim->rot[j][panim->startframe], size);
free(panim->pos[j]);
free(panim->rot[j]);
panim->pos[j] = ppos;
panim->rot[j] = prot;
}
}
void Option_Animation(char* name, s_animation_t* panim)
{
int time1;
char cmd[1024];
int option;
strcpyn(panim->name, name);
sprintf(filename, "%s/%s.smd", cddir, panim->name);
time1 = FileTime(filename);
if (time1 == -1)
Error("%s doesn't exist", filename);
printf("grabbing %s\n", filename);
if ((input = fopen(filename, "r")) == 0)
{
fprintf(stderr, "reader: could not open file '%s'\n", filename);
Error(0);
}
linecount = 0;
while (fgets(line, sizeof(line), input) != NULL)
{
linecount++;
sscanf(line, "%s %d", cmd, &option);
if (strcmp(cmd, "version") == 0)
{
if (option != 1)
{
Error("bad version\n");
}
}
else if (strcmp(cmd, "nodes") == 0)
{
panim->numbones = Grab_Nodes(panim->node);
}
else if (strcmp(cmd, "skeleton") == 0)
{
Grab_Animation(panim);
Shift_Animation(panim);
}
else
{
printf("unknown studio command : %s\n", cmd);
while (fgets(line, sizeof(line), input) != NULL)
{
linecount++;
if (strncmp(line, "end", 3) == 0)
break;
}
}
}
fclose(input);
}
int Option_Deform(s_sequence_t* /*psequence*/)
{
return 0;
}
/*
===============
===============
*/
int Option_Motion(s_sequence_t* psequence)
{
while (TokenAvailable())
{
GetToken(false);
psequence->motiontype |= lookupControl(token);
}
return 0;
}
int Option_Event(s_sequence_t* psequence)
{
int event;
if (psequence->numevents + 1 >= MAXSTUDIOEVENTS)
{
printf("too many events\n");
exit(0);
}
GetToken(false);
event = atoi(token);
psequence->event[psequence->numevents].event = event;
GetToken(false);
psequence->event[psequence->numevents].frame = atoi(token);
psequence->numevents++;
// option token
if (TokenAvailable())
{
GetToken(false);
if (token[0] == '}') // opps, hit the end
return 1;
// found an option
strcpy(psequence->event[psequence->numevents - 1].options, token);
}
return 0;
}
int Option_Fps(s_sequence_t* psequence)
{
GetToken(false);
psequence->fps = atof(token);
return 0;
}
int Option_AddPivot(s_sequence_t* psequence)
{
if (psequence->numpivots + 1 >= MAXSTUDIOPIVOTS)
{
printf("too many pivot points\n");
exit(0);
}
GetToken(false);
psequence->pivot[psequence->numpivots].index = atoi(token);
GetToken(false);
psequence->pivot[psequence->numpivots].start = atoi(token);
GetToken(false);
psequence->pivot[psequence->numpivots].end = atoi(token);
psequence->numpivots++;
return 0;
}
/*
=================
Option_Origin
=================
*/
void Cmd_Origin(void)
{
GetToken(false);
defaultadjust[0] = atof(token);
GetToken(false);
defaultadjust[1] = atof(token);
GetToken(false);
defaultadjust[2] = atof(token);
if (TokenAvailable())
{
GetToken(false);
defaultzrotation = (atof(token) + 90) * (Q_PI / 180.0);
}
}
void Option_Origin(void)
{
GetToken(false);
adjust[0] = atof(token);
GetToken(false);
adjust[1] = atof(token);
GetToken(false);
adjust[2] = atof(token);
}
void Option_Rotate(void)
{
GetToken(false);
zrotation = (atof(token) + 90) * (Q_PI / 180.0);
}
/*
=================
=================
*/
void Cmd_ScaleUp(void)
{
GetToken(false);
default_scale = scale_up = atof(token);
}
void Option_ScaleUp(void)
{
GetToken(false);
scale_up = atof(token);
}
/*
=================
=================
*/
int Cmd_SequenceGroup()
{
GetToken(false);
strcpyn(sequencegroup[numseqgroups].label, token);
numseqgroups++;
return 0;
}
int Cmd_SequenceGroupSize()
{
GetToken(false);
maxseqgroupsize = 1024 * atoi(token);
return 0;
}
int lookupActivity(char* szActivity)
{
int i;
for (i = 0; activity_map[i].name; i++)
{
if (stricmp(szActivity, activity_map[i].name) == 0)
return activity_map[i].type;
}
// match ACT_#
if (strnicmp(szActivity, "ACT_", 4) == 0)
{
return atoi(&szActivity[4]);
}
return 0;
}
int Cmd_Sequence()
{
int depth = 0;
char smdfilename[MAXSTUDIOGROUPS][1024];
int i;
int numblends = 0;
int start = 0;
int end = MAXSTUDIOANIMATIONS - 1;
if (!GetToken(false))
return 0;
strcpyn(sequence[numseq].name, token);
VectorCopy(defaultadjust, adjust);
scale_up = default_scale;
zrotation = defaultzrotation;
sequence[numseq].fps = 30.0;
sequence[numseq].seqgroup = numseqgroups - 1;
sequence[numseq].blendstart[0] = 0.0;
sequence[numseq].blendend[0] = 1.0;
while (1)
{
if (depth > 0)
{
if (!GetToken(true))
{
break;
}
}
else
{
if (!TokenAvailable())
{
break;
}
else
{
GetToken(false);
}
}
if (endofscript)
{
if (depth != 0)
{
printf("missing }\n");
exit(1);
}
return 1;
}
if (stricmp("{", token) == 0)
{
depth++;
}
else if (stricmp("}", token) == 0)
{
depth--;
}
else if (stricmp("deform", token) == 0)
{
Option_Deform(&sequence[numseq]);
}
else if (stricmp("event", token) == 0)
{
depth -= Option_Event(&sequence[numseq]);
}
else if (stricmp("pivot", token) == 0)
{
Option_AddPivot(&sequence[numseq]);
}
else if (stricmp("fps", token) == 0)
{
Option_Fps(&sequence[numseq]);
}
else if (stricmp("origin", token) == 0)
{
Option_Origin();
}
else if (stricmp("rotate", token) == 0)
{
Option_Rotate();
}
else if (stricmp("scale", token) == 0)
{
Option_ScaleUp();
}
else if (strnicmp("loop", token, 4) == 0)
{
sequence[numseq].flags |= STUDIO_LOOPING;
}
else if (strnicmp("frame", token, 5) == 0)
{
GetToken(false);
start = atoi(token);
GetToken(false);
end = atoi(token);
}
else if (strnicmp("blend", token, 5) == 0)
{
GetToken(false);
sequence[numseq].blendtype[0] = lookupControl(token);
GetToken(false);
sequence[numseq].blendstart[0] = atof(token);
GetToken(false);
sequence[numseq].blendend[0] = atof(token);
}
else if (strnicmp("node", token, 4) == 0)
{
GetToken(false);
sequence[numseq].entrynode = sequence[numseq].exitnode = atoi(token);
}
else if (strnicmp("transition", token, 4) == 0)
{
GetToken(false);
sequence[numseq].entrynode = atoi(token);
GetToken(false);
sequence[numseq].exitnode = atoi(token);
}
else if (strnicmp("rtransition", token, 4) == 0)
{
GetToken(false);
sequence[numseq].entrynode = atoi(token);
GetToken(false);
sequence[numseq].exitnode = atoi(token);
sequence[numseq].nodeflags |= 1;
}
else if (lookupControl(token) != -1)
{
sequence[numseq].motiontype |= lookupControl(token);
}
else if (stricmp("animation", token) == 0)
{
GetToken(false);
strcpyn(smdfilename[numblends++], token);
}
else if ((i = lookupActivity(token)) != 0)
{
sequence[numseq].activity = i;
GetToken(false);
sequence[numseq].actweight = atoi(token);
}
else
{
strcpyn(smdfilename[numblends++], token);
}
if (depth < 0)
{
printf("missing {\n");
exit(1);
}
};
if (numblends == 0)
{
printf("no animations found\n");
exit(1);
}
for (i = 0; i < numblends; i++)
{
panimation[numani] = reinterpret_cast<s_animation_t*>(kalloc(1, sizeof(s_animation_t)));
sequence[numseq].panim[i] = panimation[numani];
sequence[numseq].panim[i]->startframe = start;
sequence[numseq].panim[i]->endframe = end;
sequence[numseq].panim[i]->flags = 0;
Option_Animation(smdfilename[i], panimation[numani]);
numani++;
}
sequence[numseq].numblends = numblends;
numseq++;
return 0;
}
/*
=================
=================
*/
int Cmd_Root(void)
{
if (GetToken(false))
{
strcpyn(pivotname[0], token);
return 0;
}
return 1;
}
int Cmd_Pivot(void)
{
if (GetToken(false))
{
int index = atoi(token);
if (GetToken(false))
{
strcpyn(pivotname[index], token);
return 0;
}
}
return 1;
}
int Cmd_Controller(void)
{
if (GetToken(false))
{
if (!strcmpi("mouth", token))
{
bonecontroller[numbonecontrollers].index = 4;
}
else
{
bonecontroller[numbonecontrollers].index = atoi(token);
}
if (GetToken(false))
{
strcpyn(bonecontroller[numbonecontrollers].name, token);
GetToken(false);
if ((bonecontroller[numbonecontrollers].type = lookupControl(token)) == -1)
{
printf("unknown bonecontroller type '%s'\n", token);
return 0;
}
GetToken(false);
bonecontroller[numbonecontrollers].start = atof(token);
GetToken(false);
bonecontroller[numbonecontrollers].end = atof(token);
if (bonecontroller[numbonecontrollers].type & (STUDIO_XR | STUDIO_YR | STUDIO_ZR))
{
if (((int)(bonecontroller[numbonecontrollers].start + 360) % 360) == ((int)(bonecontroller[numbonecontrollers].end + 360) % 360))
{
bonecontroller[numbonecontrollers].type |= STUDIO_RLOOP;
}
}
numbonecontrollers++;
}
}
return 1;
}
/*
=================
=================
*/
void Cmd_BBox(void)
{
GetToken(false);
bbox[0][0] = atof(token);
GetToken(false);
bbox[0][1] = atof(token);
GetToken(false);
bbox[0][2] = atof(token);
GetToken(false);
bbox[1][0] = atof(token);
GetToken(false);
bbox[1][1] = atof(token);
GetToken(false);
bbox[1][2] = atof(token);
}
/*
=================
=================
*/
void Cmd_CBox(void)
{
GetToken(false);
cbox[0][0] = atof(token);
GetToken(false);
cbox[0][1] = atof(token);
GetToken(false);
cbox[0][2] = atof(token);
GetToken(false);
cbox[1][0] = atof(token);
GetToken(false);
cbox[1][1] = atof(token);
GetToken(false);
cbox[1][2] = atof(token);
}
/*
=================
=================
*/
void Cmd_Mirror(void)
{
GetToken(false);
strcpyn(mirrored[nummirrored++], token);
}
/*
=================
=================
*/
void Cmd_Gamma(void)
{
GetToken(false);
gamma = atof(token);
}
/*
=================
=================
*/
int Cmd_TextureGroup()
{
int i;
int depth = 0;
int index = 0;
int group = 0;
if (numtextures == 0)
Error("texturegroups must follow model loading\n");
if (!GetToken(false))
return 0;
if (numskinref == 0)
numskinref = numtextures;
while (1)
{
if (!GetToken(true))
{
break;
}
if (endofscript)
{
if (depth != 0)
{
Error("missing }\n");
}
return 1;
}
if (token[0] == '{')
{
depth++;
}
else if (token[0] == '}')
{
depth--;
if (depth == 0)
break;
group++;
index = 0;
}
else if (depth == 2)
{
i = lookup_texture(token);
texturegroup[numtexturegroups][group][index] = i;
if (group != 0)
texture[i].parent = texturegroup[numtexturegroups][0][index];
index++;
numtexturereps[numtexturegroups] = index;
numtexturelayers[numtexturegroups] = group + 1;
}
}
numtexturegroups++;
return 0;
}
/*
=================
=================
*/
int Cmd_Hitgroup()
{
GetToken(false);
hitgroup[numhitgroups].group = atoi(token);
GetToken(false);
strcpyn(hitgroup[numhitgroups].name, token);
numhitgroups++;
return 0;
}
int Cmd_Hitbox()
{
GetToken(false);
hitbox[numhitboxes].group = atoi(token);
GetToken(false);
strcpyn(hitbox[numhitboxes].name, token);
GetToken(false);
hitbox[numhitboxes].bmin[0] = atof(token);
GetToken(false);
hitbox[numhitboxes].bmin[1] = atof(token);
GetToken(false);
hitbox[numhitboxes].bmin[2] = atof(token);
GetToken(false);
hitbox[numhitboxes].bmax[0] = atof(token);
GetToken(false);
hitbox[numhitboxes].bmax[1] = atof(token);
GetToken(false);
hitbox[numhitboxes].bmax[2] = atof(token);
numhitboxes++;
return 0;
}
/*
=================
=================
*/
int Cmd_Attachment()
{
// index
GetToken(false);
attachment[numattachments].index = atoi(token);
// bone name
GetToken(false);
strcpyn(attachment[numattachments].bonename, token);
// position
GetToken(false);
attachment[numattachments].org[0] = atof(token);
GetToken(false);
attachment[numattachments].org[1] = atof(token);
GetToken(false);
attachment[numattachments].org[2] = atof(token);
if (TokenAvailable())
GetToken(false);
if (TokenAvailable())
GetToken(false);
numattachments++;
return 0;
}
/*
=================
=================
*/
void Cmd_Renamebone()
{
// from
GetToken(false);
strcpy(renamedbone[numrenamedbones].from, token);
// to
GetToken(false);
strcpy(renamedbone[numrenamedbones].to, token);
numrenamedbones++;
}
/*add transparent texture support to models
===================
Cmd_SetTextureRendermode
//paramaters:
// "texturename" "rendermode" renderamt
// acceptable strings for rendermode are:
// "alpha"
// "additive"
// "masked"
===================
*/
void Cmd_SetTextureRendermode(void)
{
int iTextureIndex;
if (!TokenAvailable())
{
printf("*********ERROR!!!*************");
printf("\nmissing texturename after $texrendermode\n");
exit(1);
}
GetToken(false);
iTextureIndex = lookup_texture(token);
if (!TokenAvailable())
{
printf("\n*********ERROR!!!*************\n");
printf("\nmissing rendermode at $texrendermode\n");
exit(1);
}
GetToken(false);
if (!strcmp(token, "additive"))
{
texture[iTextureIndex].flags |= STUDIO_NF_ADDITIVE;
return;
}
else if (!strcmp(token, "masked"))
{
texture[iTextureIndex].flags |= STUDIO_NF_MASKED;
return;
}
else
{
printf("\n*********ERROR!!!*************\n");
printf("\ninvalid rendermode at $texrendermode, choices are :\nadditive\nmasked\n");
exit(1);
}
}
/*
===============
ParseScript
===============
*/
void ParseScript(void)
{
while (1)
{
do
{ // look for a line starting with a $ command
GetToken(true);
if (endofscript)
return;
if (token[0] == '$')
break;
while (TokenAvailable())
GetToken(false);
} while (1);
if (!strcmp(token, "$modelname"))
{
Cmd_Modelname();
}
else if (!strcmp(token, "$cd"))
{
if (cdset)
Error("Two $cd in one model");
cdset = true;
GetToken(false);
strcpy(cdpartial, token);
strcpy(cddir, ExpandPath(token));
}
else if (!strcmp(token, "$cdtexture"))
{
while (TokenAvailable())
{
GetToken(false);
strcpy(cdtexture[cdtextureset], ExpandPath(token));
cdtextureset++;
}
}
else if (!strcmp(token, "$scale"))
{
Cmd_ScaleUp();
}
else if (!strcmp(token, "$root"))
{
Cmd_Root();
}
else if (!strcmp(token, "$pivot"))
{
Cmd_Pivot();
}
else if (!strcmp(token, "$controller"))
{
Cmd_Controller();
}
else if (!strcmp(token, "$body"))
{
Cmd_Body();
}
else if (!strcmp(token, "$bodygroup"))
{
Cmd_Bodygroup();
}
else if (!strcmp(token, "$sequence"))
{
Cmd_Sequence();
}
else if (!strcmp(token, "$sequencegroup"))
{
Cmd_SequenceGroup();
}
else if (!strcmp(token, "$sequencegroupsize"))
{
Cmd_SequenceGroupSize();
}
else if (!strcmp(token, "$eyeposition"))
{
Cmd_Eyeposition();
}
else if (!strcmp(token, "$origin"))
{
Cmd_Origin();
}
else if (!strcmp(token, "$bbox"))
{
Cmd_BBox();
}
else if (!strcmp(token, "$cbox"))
{
Cmd_CBox();
}
else if (!strcmp(token, "$mirrorbone"))
{
Cmd_Mirror();
}
else if (!strcmp(token, "$gamma"))
{
Cmd_Gamma();
}
else if (!strcmp(token, "$flags"))
{
Cmd_Flags();
}
else if (!strcmp(token, "$texturegroup"))
{
Cmd_TextureGroup();
}
else if (!strcmp(token, "$hgroup"))
{
Cmd_Hitgroup();
}
else if (!strcmp(token, "$hbox"))
{
Cmd_Hitbox();
}
else if (!strcmp(token, "$attachment"))
{
Cmd_Attachment();
}
else if (!strcmp(token, "$externaltextures"))
{
split_textures = 1;
}
else if (!strcmp(token, "$cliptotextures"))
{
clip_texcoords = 0;
}
else if (!strcmp(token, "$renamebone"))
{
Cmd_Renamebone();
}
else if (!strcmp(token, "$texrendermode"))
{
Cmd_SetTextureRendermode();
}
else
{
Error("bad command %s\n", token);
}
}
}
/*
==============
main
==============
*/
int main(int argc, char** argv)
{
int i;
char path[1024];
default_scale = 1.0;
defaultzrotation = Q_PI / 2;
numrep = 0;
tag_reversed = 0;
tag_normals = 0;
flip_triangles = 1;
maxseqgroupsize = 1024 * 1024;
normal_blend = cos(2.0 * (Q_PI / 180.0));
gamma = 1.8;
if (argc == 1)
Error("usage: studiomdl [-t texture] -r(tag reversed) -n(tag bad normals) -f(flip all triangles) [-a normal_blend_angle] -h(dump hboxes) -i(ignore warnings) -p(force power of 2 textures) [-g max_sequencegroup_size(K)] file.qc");
for (i = 1; i < argc - 1; i++)
{
if (argv[i][0] == '-')
{
switch (argv[i][1])
{
case 't':
i++;
strcpy(defaulttexture[numrep], argv[i]);
if (i < argc - 2 && argv[i + 1][0] != '-')
{
i++;
strcpy(sourcetexture[numrep], argv[i]);
printf("Replaceing %s with %s\n", sourcetexture[numrep], defaulttexture[numrep]);
}
printf("Using default texture: %s\n", defaulttexture[numrep]);
numrep++;
break;
case 'r':
tag_reversed = 1;
break;
case 'n':
tag_normals = 1;
break;
case 'f':
flip_triangles = 0;
break;
case 'a':
i++;
normal_blend = cos(atof(argv[i]) * (Q_PI / 180.0));
break;
case 'h':
dump_hboxes = 1;
break;
case 'g':
i++;
maxseqgroupsize = 1024 * atoi(argv[i]);
break;
case 'p':
case '2':
force_powerof2_textures = 1;
break;
case 'i':
ignore_warnings = 1;
break;
}
}
}
strcpy(sequencegroup[numseqgroups].label, "default");
numseqgroups = 1;
//
// load the script
//
strcpy(path, argv[i]);
DefaultExtension(path, ".qc");
// SetQdirFromPath ();
LoadScriptFile(path);
//
// parse it
//
ClearModel();
strcpy(outname, argv[i]);
ParseScript();
SetSkinValues();
SimplifyModel();
WriteFile();
return 0;
}