#include #include #include #include #ifndef M_PI #define M_PI 3.14159265358979323846 // matches value in gcc v2 math.h #endif #if _MSC_VER #pragma warning (disable : 4244) #endif #define MAX_NAME 64 #define MAX_FRAMES 65536 #define MAX_TRIS 65536 #define MAX_VERTS (MAX_TRIS * 3) #define MAX_BONES 256 #define MAX_SHADERS 256 #define MAX_FILESIZE 16777216 #define MAX_ATTACHMENTS MAX_BONES // model format type #define SKMFORMAT_HEADER "SKM1" #define SKMFORMAT_VERSION 2 // hierarchical skeletal pose // model format related flags #define SKMBONEFLAG_ATTACH 1 #define MAX_PATH 1024 #define POINT_EPSILON 0.1 #define NORMAL_EPSILON 0.01 #define TEXCOORD_EPSILON 0.1 char outputdir_name[MAX_PATH]; char output_name[MAX_PATH]; char header_name[MAX_PATH]; char model_name[MAX_PATH]; char pose_name[MAX_PATH]; char scene_name[MAX_PATH]; char model_name_uppercase[MAX_PATH]; char scene_name_uppercase[MAX_PATH]; FILE *headerfile = NULL; double modelorigin[3]; double modelorigin[3] = {0.0, 0.0, 0.0}, modelscale = 1.0; double modelrotate_pitch = 0.0, modelrotate_yaw = 0.0, modelrotate_roll = 0.0; int cleanbones; void stringtouppercase(char *in, char *out) { // cleanup name while (*in) { *out = *in++; // force lowercase if (*out >= 'a' && *out <= 'z') *out += 'A' - 'a'; out++; } *out++ = 0; } void cleancopyname(char *out, char *in, int size) { char *end = out + size - 1; // cleanup name while (out < end) { *out = *in++; if (!*out) break; // force lowercase if (*out >= 'A' && *out <= 'Z') *out += 'a' - 'A'; // convert backslash to slash if (*out == '\\') *out = '/'; out++; } end++; while (out < end) *out++ = 0; // pad with nulls } void chopextension(char *text) { char *temp; if (!*text) return; temp = text; while (*temp) { if (*temp == '\\') *temp = '/'; temp++; } temp = text + strlen(text) - 1; while (temp >= text) { if (*temp == '.') // found an extension { // clear extension *temp++ = 0; while (*temp) *temp++ = 0; break; } if (*temp == '/') // no extension but hit path break; temp--; } } void *readfile(char *filename, int *filesize) { FILE *file; void *mem; unsigned long size; if (!filename[0]) { printf ("readfile: tried to open empty filename\n"); return NULL; } if (!(file = fopen (filename, "rb"))) return NULL; fseek (file, 0, SEEK_END); if (!(size = ftell(file))) { fclose (file); return NULL; } if (!(mem = malloc (size + 1))) { fclose (file); return NULL; } ((unsigned char *)mem)[size] = 0; // 0 byte added on the end fseek (file, 0, SEEK_SET); if (fread (mem, 1, size, file) < size) { fclose (file); free (mem); return NULL; } fclose (file); if (filesize) // can be passed NULL... *filesize = size; return mem; } void writefile(char *filename, void *buffer, int size) { int size1; FILE *file; file = fopen (filename, "wb"); if (!file) { printf ("unable to open file \"%s\" for writing\n", filename); return; } size1 = fwrite (buffer, 1, size, file); fclose (file); if (size1 < size) { printf ("unable to write file \"%s\"\n", filename); return; } } char *scriptbytes, *scriptend; int scriptsize; unsigned char *tokenpos; int getline(unsigned char *line) { unsigned char *out = line; if (!line) return 0; while (*tokenpos == '\r' || *tokenpos == '\n') tokenpos++; if (*tokenpos == 0) { *out++ = 0; return 0; } while (*tokenpos && *tokenpos != '\r' && *tokenpos != '\n') *out++ = *tokenpos++; *out++ = 0; return out - line; } //JAL:HL2SMD[start] #define MAX_TOKEN_CHARS 1024 char com_token[MAX_TOKEN_CHARS]; char *COM_ParseExt(char **data_p/*, int nl*/) { int c; int len; char *data; //int newlines = 0; data = *data_p; len = 0; com_token[0] = 0; if (!data) { *data_p = NULL; return ""; } // skip whitespace skipwhite: while ( (c = *data) <= ' ') { if (c == 0) { *data_p = NULL; return ""; } //if (c == '\n') // newlines = 1; data++; } //if ( newlines && !nl ) { // *data_p = data; // return com_token; //} // skip // comments if (c == '/' && data[1] == '/') { data += 2; while (*data && *data != '\n') data++; goto skipwhite; } // skip /* */ comments if (c == '/' && data[1] == '*') { data += 2; while (1) { if (!*data) break; if (*data != '*' || *(data+1) != '/') data++; else { data += 2; break; } } goto skipwhite; } // handle quoted strings specially if (c == '\"') { data++; while (1) { c = *data++; if (c=='\"' || !c) { if (len == MAX_TOKEN_CHARS) { //Com_Printf ("Token exceeded %i chars, discarded.\n", MAX_TOKEN_CHARS); len = 0; } com_token[len] = 0; *data_p = data; return com_token; } if (len < MAX_TOKEN_CHARS) { com_token[len] = c; len++; } } } // parse a regular word do { if (len < MAX_TOKEN_CHARS) { com_token[len] = c; len++; } data++; c = *data; } while (c>32); if (len == MAX_TOKEN_CHARS) { //Com_Printf ("Token exceeded %i chars, discarded.\n", MAX_TOKEN_CHARS); len = 0; } com_token[len] = 0; *data_p = data; return com_token; } //JAL:HL2SMD[end] typedef struct bonepose_s { double q[4]; double origin[3]; } bonepose_t; typedef struct bone_s { char name[MAX_NAME]; int parent; // parent of this bone int flags; int users; // used to determine if a bone is used to avoid saving out unnecessary bones int defined; } bone_t; typedef struct frame_s { int defined; char name[MAX_NAME]; double mins[3], maxs[3], yawradius, allradius; // clipping int numbones; bonepose_t *bones; } frame_t; //JAL:HL2SMD[start] #define MAX_INFLUENCES 16 typedef struct tripoint_s { int shadernum; //int bonenum; double texcoord[2]; //double origin[3]; //double normal[3]; int numinfluences; double influenceorigin[MAX_INFLUENCES][3]; double influencenormal[MAX_INFLUENCES][3]; int influencebone[MAX_INFLUENCES]; float influenceweight[MAX_INFLUENCES]; } tripoint; //JAL:HL2SMD[end] typedef struct triangle_s { int shadernum; int v[3]; } triangle; typedef struct attachment_s { char name[MAX_NAME]; char parentname[MAX_NAME]; bonepose_t matrix; } attachment; int numattachments = 0; attachment attachments[MAX_ATTACHMENTS]; int numframes = 0; frame_t frames[MAX_FRAMES]; int numbones = 0; bone_t bones[MAX_BONES]; // master bone list int numshaders = 0; char shaders[MAX_SHADERS][32]; int numtriangles = 0; triangle triangles[MAX_TRIS]; int numverts = 0; tripoint vertices[MAX_VERTS]; // these are used while processing things bonepose_t bonematrix[MAX_BONES]; char *modelfile; int vertremap[MAX_VERTS]; double quat_normalize(double q[4]) { double length; length = q[0] * q[0] + q[1] * q[1] + q[2] * q[2] + q[3] * q[3]; if (length != 0) { double ilength = 1.0 / sqrt (length); q[0] *= ilength; q[1] *= ilength; q[2] *= ilength; q[3] *= ilength; } return length; } void quat_multiply(double q1[4], double q2[4], double out[4]) { out[3] = q1[3]*q2[3] - q1[0]*q2[0] - q1[1]*q2[1] - q1[2]*q2[2]; out[0] = q1[3]*q2[0] + q1[0]*q2[3] + q1[1]*q2[2] - q1[2]*q2[1]; out[1] = q1[3]*q2[1] + q1[1]*q2[3] + q1[2]*q2[0] - q1[0]*q2[2]; out[2] = q1[3]*q2[2] + q1[2]*q2[3] + q1[0]*q2[1] - q1[1]*q2[0]; } void quat_transform(double q[4], double v[3], double out[3]) { double wx, wy, wz, xx, yy, yz, xy, xz, zz, x2, y2, z2; x2 = q[0] + q[0]; y2 = q[1] + q[1]; z2 = q[2] + q[2]; xx = q[0] * x2; xy = q[0] * y2; xz = q[0] * z2; yy = q[1] * y2; yz = q[1] * z2; zz = q[2] * z2; wx = q[3] * x2; wy = q[3] * y2; wz = q[3] * z2; out[0] = (1.0f - yy - zz) * v[0] + (xy - wz) * v[1] + (xz + wy) * v[2]; out[1] = (xy + wz) * v[0] + (1.0f - xx - zz) * v[1] + (yz - wx) * v[2]; out[2] = (xz - wy) * v[0] + (yz + wx) * v[1] + (1.0f - xx - yy) * v[2]; } void matrix_to_quat( double m[3][3], double q[4] ) { double tr, s; tr = m[0][0] + m[1][1] + m[2][2]; if( tr > 0.00001 ) { s = (double)sqrt( tr + 1.0f ); q[3] = (double)(s * 0.5); s = (double)(0.5 / s); q[0] = (m[2][1] - m[1][2]) * s; q[1] = (m[0][2] - m[2][0]) * s; q[2] = (m[1][0] - m[0][1]) * s; } else { int i, j, k; i = 0; if (m[1][1] > m[0][0]) i = 1; if (m[2][2] > m[i][i]) i = 2; j = (i + 1) % 3; k = (i + 2) % 3; s = (double)sqrt( m[i][i] - (m[j][j] + m[k][k]) + 1.0 ); q[i] = (double)(s * 0.5); if( s != 0.0 ) s = (double)(0.5 / s); q[j] = (m[j][i] + m[i][j]) * s; q[k] = (m[k][i] + m[i][k]) * s; q[3] = (m[k][j] - m[j][k]) * s; } quat_normalize( q ); } void matrix_copy( double m1[3][3], double m2[3][3] ) { int i, j; for( i = 0; i < 3; i++ ) for( j = 0; j < 3; j++ ) m2[i][j] = m1[i][j]; } void matrix_multiply( double m1[3][3], double m2[3][3], double out[3][3] ) { out[0][0] = m1[0][0]*m2[0][0] + m1[0][1]*m2[1][0] + m1[0][2]*m2[2][0]; out[0][1] = m1[0][0]*m2[0][1] + m1[0][1]*m2[1][1] + m1[0][2]*m2[2][1]; out[0][2] = m1[0][0]*m2[0][2] + m1[0][1]*m2[1][2] + m1[0][2]*m2[2][2]; out[1][0] = m1[1][0]*m2[0][0] + m1[1][1]*m2[1][0] + m1[1][2]*m2[2][0]; out[1][1] = m1[1][0]*m2[0][1] + m1[1][1]*m2[1][1] + m1[1][2]*m2[2][1]; out[1][2] = m1[1][0]*m2[0][2] + m1[1][1]*m2[1][2] + m1[1][2]*m2[2][2]; out[2][0] = m1[2][0]*m2[0][0] + m1[2][1]*m2[1][0] + m1[2][2]*m2[2][0]; out[2][1] = m1[2][0]*m2[0][1] + m1[2][1]*m2[1][1] + m1[2][2]*m2[2][1]; out[2][2] = m1[2][0]*m2[0][2] + m1[2][1]*m2[1][2] + m1[2][2]*m2[2][2]; } #define DEG2RAD( a ) ( a * M_PI ) / 180.0F void matrix_rotate( double m[3][3], double angle, double x, double y, double z ) { double t[3][3], b[3][3]; double c = cos( DEG2RAD(angle) ); double s = sin( DEG2RAD(angle) ); double mc = 1 - c, t1, t2; t[0][0] = (x * x * mc) + c; t[1][1] = (y * y * mc) + c; t[2][2] = (z * z * mc) + c; t1 = y * x * mc; t2 = z * s; t[0][1] = t1 + t2; t[1][0] = t1 - t2; t1 = x * z * mc; t2 = y * s; t[0][2] = t1 - t2; t[2][0] = t1 + t2; t1 = y * z * mc; t2 = x * s; t[1][2] = t1 + t2; t[2][1] = t1 - t2; matrix_copy( m, b ); matrix_multiply( b, t, m ); } double wrapangles(double f) { while (f < M_PI) f += M_PI * 2; while (f >= M_PI) f -= M_PI * 2; return f; } bonepose_t computebonematrix(double x, double y, double z, double a, double b, double c) { bonepose_t out; double sr, sp, sy, cr, cp, cy; double m[3][3], tr, s; sy = sin (c); cy = cos (c); sp = sin (b); cp = cos (b); sr = sin (a); cr = cos (a); m[0][0] = cp*cy; m[1][0] = cp*sy; m[2][0] = -sp; m[0][1] = sr*sp*cy+cr*-sy; m[1][1] = sr*sp*sy+cr*cy; m[2][1] = sr*cp; m[0][2] = (cr*sp*cy+-sr*-sy); m[1][2] = (cr*sp*sy+-sr*cy); m[2][2] = cr*cp; tr = m[0][0] + m[1][1] + m[2][2]; if( tr > 0.00000001 ) { s = sqrt( tr + 1.0 ); out.q[3] = s * 0.5; s = 0.5 / s; out.q[0] = (m[2][1] - m[1][2]) * s; out.q[1] = (m[0][2] - m[2][0]) * s; out.q[2] = (m[1][0] - m[0][1]) * s; } else { int i, j, k; i = 0; if (m[1][1] > m[0][0]) i = 1; if (m[2][2] > m[i][i]) i = 2; j = (i + 1) % 3; k = (i + 2) % 3; s = sqrt( m[i][i] - (m[j][j] + m[k][k]) + 1.0 ); out.q[i] = s * 0.5; if( s != 0.0 ) s = 0.5 / s; out.q[j] = (m[j][i] + m[i][j]) * s; out.q[k] = (m[k][i] + m[i][k]) * s; out.q[3] = (m[k][j] - m[j][k]) * s; } quat_normalize( out.q ); out.origin[0] = x; out.origin[1] = y; out.origin[2] = z; return out; } bonepose_t concattransform(bonepose_t in1, bonepose_t in2) { bonepose_t out; quat_multiply( in1.q, in2.q, out.q ); quat_transform( in1.q, in2.origin, out.origin ); out.origin[0] += in1.origin[0]; out.origin[1] += in1.origin[1]; out.origin[2] += in1.origin[2]; return out; } void transform(double in[3], bonepose_t b, double out[3]) { quat_transform( b.q, in, out ); out[0] += b.origin[0]; out[1] += b.origin[1]; out[2] += b.origin[2]; } void inversetransform(double in[3], bonepose_t b, double out[3]) { double temp[3]; double btemp[4]; temp[0] = in[0] - b.origin[0]; temp[1] = in[1] - b.origin[1]; temp[2] = in[2] - b.origin[2]; btemp[0] = -b.q[0]; btemp[1] = -b.q[1]; btemp[2] = -b.q[2]; btemp[3] = b.q[3]; quat_transform( btemp, temp, out ); } void inverserotate(double in[3], bonepose_t b, double out[3]) { double btemp[4]; btemp[0] = -b.q[0]; btemp[1] = -b.q[1]; btemp[2] = -b.q[2]; btemp[3] = b.q[3]; quat_transform( btemp, in, out ); } int parsenodes(void) { int num, parent; unsigned char line[1024], name[1024]; char *tok, *string;//JAL:HL2SMD memset(bones, 0, sizeof(bones)); numbones = 0; while (getline(line)) { if (!strcmp(line, "end")) break; //JAL:HL2SMD[start] //parse this line read by tokens string = line; //get bone number tok = COM_ParseExt(&string); if( !tok ) { printf("error in nodes, expecting bone number in line:%s\n", line); return 0; } num = atoi( tok ); //get bone name tok = COM_ParseExt(&string); if( !tok ) { printf("error in nodes, expecting bone name in line:%s\n", line); return 0; } cleancopyname(name, tok, MAX_NAME);//printf( "bone name: %s\n", name ); //get parent number tok = COM_ParseExt(&string); if( !tok ) { printf("error in nodes, expecting parent number in line:%s\n", line); return 0; } parent = atoi( tok ); //JAL:HL2SMD[end] if (num < 0 || num >= MAX_BONES) { printf("invalid bone number %i\n", num); return 0; } if (parent >= num) { printf("bone's parent >= bone's number\n"); return 0; } if (parent < -1) { printf("bone's parent < -1\n"); return 0; } if (parent >= 0 && !bones[parent].defined) { printf("bone's parent bone has not been defined\n"); return 0; } memcpy(bones[num].name, name, MAX_NAME); bones[num].defined = 1; bones[num].parent = parent; if (num >= numbones) numbones = num + 1; } return 1; } int parseskeleton(void) { unsigned char line[1024], temp[1024]; int i, frame, num; double x, y, z, a, b, c; int baseframe; char *tok, *string;//JAL:HL2SMD baseframe = numframes; frame = baseframe; while (getline (line)) { //JAL:HL2SMD[start] if (!strcmp(line, "end")) break; //parse this line read by tokens string = line; //get opening line token tok = COM_ParseExt(&string); if (!tok) { printf("error in parseskeleton, script line:%s\n", line); return 0; } if (!strcmp(tok, "time")) { //get the time value tok = COM_ParseExt(&string); if (!tok) { printf("error in parseskeleton, expecting time value in line:%s\n", line); return 0; } i = atoi( tok ); if (i < 0) { printf("invalid time %i\n", i); return 0; } frame = baseframe + i; if (frame >= MAX_FRAMES) { printf("only %i frames supported currently\n", MAX_FRAMES); return 0; } if (frames[frame].defined) { printf("warning: duplicate frame\n"); free(frames[frame].bones); } sprintf(temp, "%s_%i", scene_name, i); if (strlen(temp) > 31) { printf("error: frame name \"%s\" is longer than 31 characters\n", temp); return 0; } cleancopyname(frames[frame].name, temp, MAX_NAME); frames[frame].numbones = numbones + numattachments + 1; frames[frame].bones = malloc(frames[frame].numbones * sizeof(bonepose_t)); memset(frames[frame].bones, 0, frames[frame].numbones * sizeof(bonepose_t)); frames[frame].bones[frames[frame].numbones - 1].q[3] = 1; frames[frame].defined = 1; if (numframes < frame + 1) numframes = frame + 1; } else { //the token was bone number num = atoi( tok ); if (num < 0 || num >= numbones) { printf("error: invalid bone number: %i\n", num); return 0; } if (!bones[num].defined) { printf("error: bone %i not defined\n", num); return 0; } //get x, y, z tokens tok = COM_ParseExt(&string); if (!tok || tok[0] <= ' ') { printf("error in parseskeleton, expecting 'x' value in line:%s\n", line); return 0; } x = atof( tok ); tok = COM_ParseExt(&string); if (!tok || tok[0] <= ' ') { printf("error in parseskeleton, expecting 'y' value in line:%s\n", line); return 0; } y = atof( tok ); tok = COM_ParseExt(&string); if (!tok || tok[0] <= ' ') { printf("error in parseskeleton, expecting 'z' value in line:%s\n", line); return 0; } z = atof( tok ); //get a, b, c tokens tok = COM_ParseExt(&string); if (!tok || tok[0] <= ' ') { printf("error in parseskeleton, expecting 'a' value in line:%s\n", line); return 0; } a = atof( tok ); tok = COM_ParseExt(&string); if (!tok || tok[0] <= ' ') { printf("error in parseskeleton, expecting 'b' value in line:%s\n", line); return 0; } b = atof( tok ); tok = COM_ParseExt(&string); if (!tok || tok[0] <= ' ') { printf("error in parseskeleton, expecting 'c' value in line:%s\n", line); return 0; } c = atof( tok ); // if (num < 0 || num >= numbones) // { // printf("error: invalid bone number: %i\n", num); // return 0; // } // if (!bones[num].defined) // { // printf("error: bone %i not defined\n", num); // return 0; // } // root bones need to be offset if (bones[num].parent < 0) { // Vic: added '* modelscale' (thanks to Riot) x = (x - modelorigin[0]) * modelscale; y = (y - modelorigin[1]) * modelscale; z = (z - modelorigin[2]) * modelscale; } // LordHavoc: compute matrix frames[frame].bones[num] = computebonematrix (x, y, z, a, b, c); } //JAL:HL2SMD[end] } for (frame = 0; frame < numframes; frame++) { if (!frames[frame].defined) { if (frame < 1) { printf("error: no first frame\n"); return 0; } if (!frames[frame - 1].defined) { printf("error: no previous frame to duplicate\n"); return 0; } sprintf(temp, "%s_%i", scene_name, frame - baseframe); if (strlen(temp) > 31) { printf("error: frame name \"%s\" is longer than 31 characters\n", temp); return 0; } printf("frame %s missing, duplicating previous frame %s with new name %s\n", temp, frames[frame - 1].name, temp); frames[frame].defined = 1; cleancopyname(frames[frame].name, temp, MAX_NAME); frames[frame].numbones = numbones + numattachments + 1; frames[frame].bones = malloc(frames[frame].numbones * sizeof(bonepose_t)); memcpy(frames[frame].bones, frames[frame - 1].bones, frames[frame].numbones * sizeof(bonepose_t)); frames[frame].bones[frames[frame].numbones - 1].q[3] = 1; printf("duplicate frame named %s\n", frames[frame].name); } if (frame >= baseframe && headerfile) fprintf(headerfile, "#define MODEL_%s_%s_%i %i\n", model_name_uppercase, scene_name_uppercase, frame - baseframe, frame); } if (headerfile) { fprintf(headerfile, "#define MODEL_%s_%s_START %i\n", model_name_uppercase, scene_name_uppercase, baseframe); fprintf(headerfile, "#define MODEL_%s_%s_END %i\n", model_name_uppercase, scene_name_uppercase, numframes); fprintf(headerfile, "#define MODEL_%s_%s_LENGTH %i\n", model_name_uppercase, scene_name_uppercase, numframes - baseframe); fprintf(headerfile, "\n"); } return 1; } int freeframes(void) { int i; for (i = 0; i < numframes; i++) { if (frames[i].defined && frames[i].bones) free(frames[i].bones); } numframes = 0; return 1; } int initframes(void) { memset(frames, 0, sizeof(frames)); return 1; } //JAL:HL2SMD[start] int parsetriangles(void) { unsigned char line[1024], cleanline[MAX_NAME]; int current = 0, i; double org[3], normal[3]; double d; int vbonenum; double vtexcoord[2]; int numinfluences; int temp_numbone[MAX_INFLUENCES]; float temp_influence[MAX_INFLUENCES]; double temp_origin[MAX_INFLUENCES][3]; double temp_normal[MAX_INFLUENCES][3]; int j; char *tok, *string; numtriangles = 0; numshaders = 0; for (i = 0; i < numbones; i++) { if (!bones[i].defined) continue; if (bones[i].parent >= 0) bonematrix[i] = concattransform(bonematrix[bones[i].parent], frames[0].bones[i]); else bonematrix[i] = frames[0].bones[i]; } while (getline(line)) { if (!strcmp(line, "end")) break; if (current == 0) { // Vic: make sure shader name is valid (thanks to defcon-X) cleancopyname(cleanline, line, MAX_NAME); for (i = 0; i < numshaders; i++) if (!strcmp (shaders[i], cleanline)) break; triangles[numtriangles].shadernum = i; if (i == numshaders) { if( i == MAX_SHADERS ) { printf("MAX_SHADERS reached\n"); return 0; } strcpy(shaders[i], cleanline); numshaders++; } current++; } else { //parse this line read by tokens string = line; org[0] = 0;org[1] = 0;org[2] = 0; normal[0] = 0;normal[1] = 0;normal[2] = 0; vtexcoord[0] = 0;vtexcoord[1] = 0; //get bonenum token tok = COM_ParseExt(&string); if (!tok || tok[0] <= ' ') { printf("error in parsetriangles, expecting 'bonenum', script line:%s\n", line); return 0; } vbonenum = atoi( tok ); //get org[0] token tok = COM_ParseExt(&string); if (!tok || tok[0] <= ' ') { printf("error in parsetriangles, expecting 'org[0]', script line:%s\n", line); return 0; } org[0] = atof( tok ); //get org[1] token tok = COM_ParseExt(&string); if (!tok || tok[0] <= ' ') { printf("error in parsetriangles, expecting 'org[1]', script line:%s\n", line); return 0; } org[1] = atof( tok ); //get org[2] token tok = COM_ParseExt(&string); if (!tok || tok[0] <= ' ') { printf("error in parsetriangles, expecting 'org[2]', script line:%s\n", line); return 0; } org[2] = atof( tok ); //get normal[0] token tok = COM_ParseExt(&string); if (!tok || tok[0] <= ' ') { printf("error in parsetriangles, expecting 'normal[0]', script line:%s\n", line); return 0; } normal[0] = atof( tok ); //get normal[1] token tok = COM_ParseExt(&string); if (!tok || tok[0] <= ' ') { printf("error in parsetriangles, expecting 'normal[1]', script line:%s\n", line); return 0; } normal[1] = atof( tok ); //get normal[2] token tok = COM_ParseExt(&string); if (!tok || tok[0] <= ' ') { printf("error in parsetriangles, expecting 'normal[2]', script line:%s\n", line); return 0; } normal[2] = atof( tok ); //get vtexcoord[0] token tok = COM_ParseExt(&string); if (!tok || tok[0] <= ' ') { printf("error in parsetriangles, expecting 'vtexcoord[0]', script line:%s\n", line); return 0; } vtexcoord[0] = atof( tok ); //get vtexcoord[1] token tok = COM_ParseExt(&string); if (!tok || tok[0] <= ' ') { printf("error in parsetriangles, expecting 'vtexcoord[1]', script line:%s\n", line); return 0; } vtexcoord[1] = atof( tok ); // are there more words (HalfLife2) or not (HalfLife1)? tok = COM_ParseExt(&string); if (!tok || tok[0] <= ' ') { // one influence (HalfLife1) numinfluences = 1; temp_numbone[0] = vbonenum; temp_influence[0] = 1.0f; } else { // multiple influences found (HalfLife2) int c; numinfluences = atoi( tok ); if( !numinfluences ) { printf("error in parsetriangles, expecting 'numinfluences', script line:%s\n", line); return 0; } //read by pairs, bone number and influence for( c = 0; c < numinfluences; c++ ) { //get bone number tok = COM_ParseExt(&string);//printf("tok: \"%s\"\n", tok); if( !tok || tok[0] <= ' ' ) { printf("invalid vertex influence \"%s\"\n", line); return 0; } temp_numbone[c] = atoi(tok); if(temp_numbone[c] < 0 || temp_numbone[c] >= numbones ) { printf("invalid vertex influence (invalid bone number) \"%s\"\n", line); return 0; } //get influence weight tok = COM_ParseExt(&string);//printf("tok: \"%s\"\n", tok); if( !tok || tok[0] <= ' ' ) { printf("invalid vertex influence \"%s\"\n", line); return 0; } temp_influence[c] = atof(tok); if( temp_influence[c] < 0.0f ) { printf("invalid vertex influence weight, ignored \"%s\"\n", line); return 0; } else if( temp_influence[c] > 1.0f ) temp_influence[c] = 1.0f; } } //validate linked bones if( numinfluences < 1 ) { printf("vertex with no influence found in triangle data\n"); return 0; } for( i=0; i= MAX_BONES ) { printf("invalid bone number %i in triangle data\n", temp_numbone[i]); return 0; } if (!bones[temp_numbone[i]].defined) { printf("bone %i in triangle data is not defined\n", temp_numbone[i]); return 0; } } // apply model scaling and offset org[0] = (org[0] - modelorigin[0]) * modelscale; org[1] = (org[1] - modelorigin[1]) * modelscale; org[2] = (org[2] - modelorigin[2]) * modelscale; // untransform the origin and normal for( j=0; j= numverts) { numverts++; vertices[i].shadernum = triangles[numtriangles].shadernum; vertices[i].texcoord[0] = vtexcoord[0]; vertices[i].texcoord[1] = vtexcoord[1]; vertices[i].numinfluences = numinfluences; for( j=0; j < vertices[i].numinfluences; j++ ) { vertices[i].influenceorigin[j][0] = temp_origin[j][0]; vertices[i].influenceorigin[j][1] = temp_origin[j][1]; vertices[i].influenceorigin[j][2] = temp_origin[j][2]; vertices[i].influencenormal[j][0] = temp_normal[j][0]; vertices[i].influencenormal[j][1] = temp_normal[j][1]; vertices[i].influencenormal[j][2] = temp_normal[j][2]; vertices[i].influencebone[j] = temp_numbone[j]; vertices[i].influenceweight[j] = temp_influence[j]; } } current++; if (current >= 4) { current = 0; numtriangles++; } } } return 1; } //JAL:HL2SMD[end] int parsemodelfile(void) { int i; char line[1024], command[256]; tokenpos = modelfile; while (getline(line)) { sscanf(line, "%s %i", command, &i); if (!strcmp(command, "version")) { if (i != 1) { printf("file is version %d, only version 1 is supported\n", i); return 0; } } else if (!strcmp(command, "nodes")) { if (!parsenodes()) return 0; } else if (!strcmp(command, "skeleton")) { if (!parseskeleton()) return 0; } else if (!strcmp(command, "triangles")) { if (!parsetriangles()) return 0; } else { printf ("unknown command \"%s\"\n", line); return 0; } } return 1; } int addattachments(void) { int i, j; for (i = 0; i < numattachments; i++) { bones[numbones].defined = 1; bones[numbones].parent = -1; bones[numbones].flags = SKMBONEFLAG_ATTACH; for (j = 0; j < numbones; j++) if (!strcmp (bones[j].name, attachments[i].parentname)) bones[numbones].parent = j; if (bones[numbones].parent < 0) printf ("warning: unable to find bone \"%s\" for attachment \"%s\", using root instead\n", attachments[i].parentname, attachments[i].name); cleancopyname(bones[numbones].name, attachments[i].name, MAX_NAME); // we have to duplicate the attachment in every frame for (j = 0; j < numframes; j++) frames[j].bones[numbones] = attachments[i].matrix; numbones++; } numattachments = 0; return 1; } int cleanupbones(void) { int i, j; int oldnumbones; int remap[MAX_BONES]; // figure out which bones are used for (i = 0; i < numbones; i++) { if (!bones[i].defined) continue; bones[i].users = 0; if (bones[i].flags & SKMBONEFLAG_ATTACH) bones[i].users++; } //JAL:HL2SMD[start] for (i = 0; i < numverts; i++) { for( j = 0; j < vertices[i].numinfluences; j++ ) bones[vertices[i].influencebone[j]].users++; } //JAL:HL2SMD[end] for (i = 0; i < numbones; i++) if (bones[i].defined && bones[i].users && bones[i].parent >= 0) bones[bones[i].parent].users++; // now calculate the remapping table for whichever ones should remain oldnumbones = numbones; numbones = 0; for (i = 0; i < oldnumbones; i++) { if (bones[i].defined && bones[i].users) remap[i] = numbones++; else { remap[i] = -1; } } // shuffle bone data around to eliminate gaps for (i = 0; i < oldnumbones; i++) if (bones[i].parent >= 0) bones[i].parent = remap[bones[i].parent]; for (i = 0; i < oldnumbones; i++) if (remap[i] >= 0 && remap[i] != i) bones[remap[i]] = bones[i]; for (i = 0; i < numframes; i++) { if (!frames[i].defined) continue; for (j = 0; j < oldnumbones; j++) { if (remap[j] >= 0 && remap[j] != j) frames[i].bones[remap[j]] = frames[i].bones[j]; } } //JAL:HL2SMD[start] // remap vertex references for (i = 0; i < numverts; i++) { for( j=0; j ' ' && *token != '(' && *token != ')' && *token != '{' && *token != '}' && *token != '[' && *token != ']' && *token != '\"') *out++ = *token++; *out++ = 0; return tokenbuffer; } } typedef struct sccommand_s { char *name; int (*code)(void); } sccommand; int isdouble(char *c) { if (!c) // Vic return 0; while (*c) { switch (*c) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case '.': case 'e': case 'E': case '-': case '+': break; default: return 0; } c++; } return 1; } int isfilename(char *c) { if (!c) // Vic return 0; while (*c) { if (*c < ' ') return 0; c++; } return 1; } int sc_attachment(void) { int i; char *c; double origin[3], angles[3]; if (numattachments >= MAX_ATTACHMENTS) { printf ("ran out of attachment slots\n"); return 0; } c = gettoken(); if (!isfilename(c)) return 0; cleancopyname(attachments[numattachments].name, c, MAX_NAME); c = gettoken(); if (!isfilename(c)) return 0; cleancopyname(attachments[numattachments].parentname, c, MAX_NAME); for (i = 0; i < 6; i++) { c = gettoken(); if (!isdouble(c)) return 0; if (i < 3) origin[i] = atof(c); else angles[i - 3] = atof(c) * (M_PI / 180.0); } attachments[numattachments].matrix = computebonematrix(origin[0], origin[1], origin[2], angles[0], angles[1], angles[2]); numattachments++; return 1; } int sc_outputdir(void) { char *c = gettoken(); if (!isfilename(c)) return 0; strcpy(outputdir_name, c); chopextension(outputdir_name); if (strlen(outputdir_name) && outputdir_name[strlen(outputdir_name) - 1] != '/') strcat(outputdir_name, "/"); return 1; } int sc_model(void) { char *c = gettoken(); if (!isfilename(c)) return 0; strcpy(model_name, c); chopextension(model_name); stringtouppercase(model_name, model_name_uppercase); sprintf(header_name, "%s%s.h", outputdir_name, model_name); sprintf(output_name, "%s%s.skm", outputdir_name, model_name); return 1; } int sc_export(void) { char *c = gettoken(); char temp[MAX_PATH]; if (!isfilename(c)) return 0; strcpy(temp, c); chopextension(temp); sprintf(pose_name, "%s%s.skp", outputdir_name, temp); return 1; } int sc_origin(void) { int i; char *c; for( i = 0; i < 3; i++ ) { c = gettoken(); if (!c) return 0; if (!isdouble(c)) return 0; modelorigin[i] = atof(c); } return 1; } int sc_rotate_pitch(void) { char *c = gettoken(); if (!c) return 0; if (!isdouble(c)) return 0; modelrotate_pitch = atof(c); return 1; } int sc_rotate_yaw(void) { char *c = gettoken(); if (!c) return 0; if (!isdouble(c)) return 0; modelrotate_yaw = atof(c); return 1; } int sc_rotate_roll(void) { char *c = gettoken(); if (!c) return 0; if (!isdouble(c)) return 0; modelrotate_roll = atof(c); return 1; } int sc_scale(void) { char *c = gettoken(); if (!c) return 0; if (!isdouble(c)) return 0; modelscale = atof (c); return 1; } int sc_cleanbones(void) { cleanbones = 1; return 1; } int sc_scene(void) { char *c = gettoken(); if (!c) return 0; if (!isfilename(c)) return 0; modelfile = readfile(c, NULL); if (!modelfile) return 0; cleancopyname(scene_name, c, MAX_NAME); chopextension(scene_name); stringtouppercase(scene_name, scene_name_uppercase); printf("parsing scene %s\n", scene_name); if (!headerfile) { headerfile = fopen(header_name, "wb"); if (headerfile) { fprintf(headerfile, "/*\n"); fprintf(headerfile, "Generated header file for %s\n", model_name); fprintf(headerfile, "This file contains frame number definitions for use in code referencing the model, to make code more readable and maintainable.\n"); fprintf(headerfile, "*/\n"); fprintf(headerfile, "\n"); fprintf(headerfile, "#ifndef MODEL_%s_H\n", model_name_uppercase); fprintf(headerfile, "#define MODEL_%s_H\n", model_name_uppercase); fprintf(headerfile, "\n"); } } if (!parsemodelfile()) return 0; free(modelfile); return 1; } int sc_comment(void) { while (gettoken()[0] != '\n'); return 1; } int sc_nothing(void) { return 1; } sccommand sc_commands[] = { {"attachment", sc_attachment}, {"outputdir", sc_outputdir}, {"model", sc_model}, {"origin", sc_origin}, {"rotatepitch", sc_rotate_pitch}, {"rotateyaw", sc_rotate_yaw}, {"rotateroll", sc_rotate_roll}, {"scale", sc_scale}, {"scene", sc_scene}, {"export", sc_export}, {"cleanbones", sc_cleanbones}, {"\n", sc_nothing}, {"", NULL} }; int processcommand(char *command) { int r; sccommand *c; c = sc_commands; // Vic: allow this style of comments // #commment if (command[0] == '#') { sc_comment(); return 1; } while (c->name[0]) { if (!strcmp(c->name, command)) { printf("executing command %s\n", command); r = c->code(); if (!r) printf("error processing script\n"); return r; } c++; } printf("command %s not recognized\n", command); return 0; } int convertmodel(void); void processscript(void) { char *c; inittokens(scriptbytes); numframes = 0; numbones = 0; numshaders = 0; numtriangles = 0; cleanbones = 0; initframes(); while ((c = gettoken())) if (c[0] > ' ') if (!processcommand(c)) return; if (headerfile) { fprintf (headerfile, "#endif /*MODEL_%s_H*/\n", model_name_uppercase); fclose (headerfile); } if (!addattachments()) { freeframes(); return; } if( cleanbones ) { if (!cleanupbones()) { freeframes(); return; } } if (!cleanupshadernames()) { freeframes(); return; } fixrootbones(); if (!convertmodel()) { freeframes(); return; } freeframes(); } int main(int argc, char **argv) { if (argc != 2) { printf ("usage: %s scriptname.txt\n", argv[0]); return 0; } scriptbytes = readfile (argv[1], &scriptsize); if (!scriptbytes) { printf ("unable to read script file\n"); return 0; } scriptend = scriptbytes + scriptsize; processscript(); #if (_MSC_VER /*&& _DEBUG*/) printf ("press ENTER\n"); getchar (); #endif return 0; } unsigned char *output; unsigned char outputbuffer[MAX_FILESIZE]; void putstring(char *in, int length) { while (*in && length) { *output++ = *in++; length--; } // pad with nulls while (length--) *output++ = 0; } void putnulls(int num) { while (num--) *output++ = 0; } void putlong(int num) { *output++ = ((num >> 0) & 0xFF); *output++ = ((num >> 8) & 0xFF); *output++ = ((num >> 16) & 0xFF); *output++ = ((num >> 24) & 0xFF); } void putfloat(double num) { union { float f; int i; } n; n.f = num; // this matches for both positive and negative 0, thus setting it to positive 0 if (n.f == 0) n.f = 0; putlong(n.i); } void putinit(void) { output = outputbuffer; } int putgetposition(void) { return (int)output - (int)outputbuffer; } void putsetposition(int n) { output = (unsigned char *)(n + (int)outputbuffer); } typedef struct lump_s { int start, length; } lump_t; char m_triused[MAX_TRIS]; int m_numtriangles; triangle m_triangles[MAX_TRIS]; int m_striptris[MAX_TRIS]; // returns number of triangles in tristrip, similar to // what Quake does int strip_length(int starttri, int startvert) { int m1, m2; int j; triangle *last, *check; int k, stripcount; m_triused[starttri] = 2; last = &triangles[starttri]; m_striptris[0] = starttri; stripcount = 1; m1 = last->v[(startvert+2)%3]; m2 = last->v[(startvert+1)%3]; // look for a matching triangle nexttri: for (j = starttri + 1, check = &triangles[starttri+1]; j < m_numtriangles; j++, check++) { for (k = 0; k < 3; k++) { if (check->v[k] != m1) continue; if (check->v[(k+1)%3] != m2) continue; // if we can't use this triangle, this tristrip is done if (m_triused[j]) goto done; // the new edge if (stripcount & 1) m2 = check->v[ (k+2)%3 ]; else m1 = check->v[ (k+2)%3 ]; m_striptris[stripcount++] = j; m_triused[j] = 2; goto nexttri; } } done: // clear the temp used flags for (j = starttri + 1; j < m_numtriangles; j++) if (m_triused[j] == 2) m_triused[j] = 0; return stripcount; } void build_tris(void) { int i, j; int startv; int len, bestlen; int besttris[MAX_TRIS]; memset (m_triused, 0, sizeof(m_triused)); for (i = 0; i < m_numtriangles; i++) { if (m_triused[i]) continue; bestlen = 0; for (startv = 0; startv < 3; startv++) { len = strip_length (i, startv); if (len > bestlen) { bestlen = len; for (j = 0; j < bestlen; j++) besttris[j] = m_striptris[j]; } } for (j = 0; j < bestlen; j++) m_triused[besttris[j]] = 1; for (j = 0; j < bestlen; j++) { putlong (m_triangles[besttris[j]].v[0]); putlong (m_triangles[besttris[j]].v[1]); putlong (m_triangles[besttris[j]].v[2]); } } } int convertmodel(void) { int i, j, k, nverts, ntris; int pos_filesize, pos_lumps, pos_frames, pos_bones, pos_meshes, pos_verts, pos_texcoords, pos_index, pos_references; int filesize, restoreposition; int numreferences, references[MAX_BONES]; char meshname[MAX_NAME]; memset(meshname, 0, sizeof(meshname)); putinit(); // ID string putstring(SKMFORMAT_HEADER, strlen(SKMFORMAT_HEADER)); // model type putlong(SKMFORMAT_VERSION); // filesize pos_filesize = putgetposition(); putlong(0); // numbers of things putlong(numbones); putlong(numshaders); // offsets to things pos_lumps = putgetposition(); putlong(0); // store the meshes pos_meshes = putgetposition(); // skip over the mesh structs, they will be filled in later putsetposition(pos_meshes + numshaders * (MAX_NAME*2 + 28)); // store the data referenced by meshes for (i = 0; i < numshaders; i++) { pos_verts = putgetposition(); nverts = 0; m_numtriangles = 0; memset (references, 0, sizeof(references)); for (j = 0; j < numverts; j++) { if (vertices[j].shadernum == i) { vertremap[j] = nverts++; //JAL:HL2SMD[start] putlong(vertices[j].numinfluences); // how many bones for this vertex (always 1 for smd) for( k=0; k> 10); writefile(output_name, outputbuffer, filesize); printf("wrote file %s\n", output_name); //jal: write the .skin file. { FILE *skinfile = NULL; char skinfile_name[MAX_PATH]; sprintf(skinfile_name, "%s%s.skin", outputdir_name, model_name); if (!skinfile) { skinfile = fopen(skinfile_name, "wb"); fprintf(skinfile, "//Skin file generated by skmodel\n"); fprintf(skinfile, "//this converter generates the mesh names by their mesh number\n"); for (i = 0; i < numshaders; i++) fprintf(skinfile, "%i,%s\n", i, shaders[i]); } fclose(skinfile); printf("wrote file %s\n", skinfile_name); } // store the data referenced by frames if (pose_name[0]) { int pos_framebones; putinit(); // ID string putstring(SKMFORMAT_HEADER, strlen(SKMFORMAT_HEADER)); // model type putlong(SKMFORMAT_VERSION); // filesize pos_filesize = putgetposition(); putlong(0); putlong(numbones); putlong(numframes); // offsets to things pos_lumps = putgetposition(); putlong(0); putlong(0); // store the bones pos_bones = putgetposition(); for (i = 0; i < numbones; i++) { putstring(bones[i].name, MAX_NAME); putlong(bones[i].parent); putlong(bones[i].flags); } // store the frames pos_frames = putgetposition(); // skip over the frame structs, they will be filled in later putsetposition(pos_frames + numframes * (MAX_NAME + 4)); for (i = 0; i < numframes; i++) { pos_framebones = putgetposition(); for (j = 0; j < numbones; j++) { for (k = 0; k < 4; k++) putfloat(frames[i].bones[j].q[k]); for (k = 0; k < 3; k++) putfloat(frames[i].bones[j].origin[k]); } // now we actually write the frame header restoreposition = putgetposition(); putsetposition(pos_frames + i * (MAX_NAME + 4)); putstring(frames[i].name, MAX_NAME); putlong(pos_framebones); putsetposition(restoreposition); } filesize = putgetposition(); putsetposition(pos_lumps); putlong(pos_bones); putlong(pos_frames); putsetposition(pos_filesize); putlong(filesize); putsetposition(filesize); printf("\n"); printf("file size: %5ik\n", (filesize + 1023) >> 10); writefile(pose_name, outputbuffer, filesize); printf("wrote file %s\n", pose_name); } return 1; }