#include #include #include #include "../rw.h" #include "dff.h" void ParseSection(clump *clp, rwheader *sec, int par, FILE *dff); void ReadSection(clump *clp, rwheader *rwh, FILE *dff); void ReadStruct(clump *clp, rwheader *rwh, int par, FILE *dff); void ReadFrame(clump *clp, rwheader *rwh, FILE *dff); /* Checks if two vertices are the same and returns 1 if so, * otherwise 0. */ int IsSameVert(float *vert0, float *vert1) { if (vert0[X] == vert1[X] && vert0[Y] == vert1[Y] && vert0[Z] == vert1[Z]) { return 1; } else return 0; } /* prints a 4x4 matrix, mainly for debugging purposes. */ void printmat44(float mat[][4]) { printf("%f %f %f %f\n%f %f %f %f\n%f %f %f %f\n%f %f %f %f\n\n", mat[0][0], mat[0][1], mat[0][2], mat[0][3], mat[1][0], mat[1][1], mat[1][2], mat[1][3], mat[2][0], mat[2][1], mat[2][2], mat[2][3], mat[3][0], mat[3][1], mat[3][2], mat[3][3]); } /* Copies a 4x4 matrix. */ void CopyMat44(float from[][4], float to[][4]) { to[0][0] = from[0][0]; to[0][1] = from[0][1]; to[0][2] = from[0][2]; to[0][3] = from[0][3]; to[1][0] = from[1][0]; to[1][1] = from[1][1]; to[1][2] = from[1][2]; to[1][3] = from[1][3]; to[2][0] = from[2][0]; to[2][1] = from[2][1]; to[2][2] = from[2][2]; to[2][3] = from[2][3]; to[3][0] = from[3][0]; to[3][1] = from[3][1]; to[3][2] = from[3][2]; to[3][3] = from[3][3]; } /* Copies a vector. */ void CopyVect(float *from, float *to) { to[0] = from[0]; to[1] = from[1]; to[2] = from[2]; to[3] = from[3]; } /* Muliplies a 4x4 matrix with a vector. */ void MultMat44Vect(float dest[], float mat[][4], float vect[]) { float temp[4]; int i; for (i = 0; i < 4; i++) { temp[i] = mat[i][0] * vect[0] + mat[i][1] * vect[1] + mat[i][2] * vect[2] + mat[i][3] * vect[3]; } CopyVect(temp, dest); } /* Multiplies to 4x4 matrices and saves result in "dest". */ void MultMat44(float dest[][4], float one[][4], float two[][4]) { int i; float temp[4][4]; for (i = 0; i < 4; i++) { temp[0][i] = one[0][0] * two[0][i] + one[0][1] * two[1][i] + one[0][2] * two[2][i] + one[0][3] * two[3][i]; temp[1][i] = one[1][0] * two[0][i] + one[1][1] * two[1][i] + one[1][2] * two[2][i] + one[1][3] * two[3][i]; temp[2][i] = one[2][0] * two[0][i] * one[2][1] * two[1][i] + one[2][2] * two[2][i] + one[2][3] * two[3][i]; temp[3][i] = one[3][0] * two[0][i] * one[3][1] * two[1][i] + one[3][2] * two[2][i] + one[3][3] * two[3][i]; } CopyMat44(temp, dest); } /* Adds all position matrices of parent to get absolute position of frame */ /* Note: rotation not yet supported. */ void GetTotalTrans(frame *frm, int i) { if (frm[i].parent == -1) { MultMat44(frm[i].absmat, frm[i].rotmatrix, frm[i].posmatrix); return; } GetTotalTrans(frm, frm[i].parent); /* Multiply the parent's absolute matrix with the own to get own * absolute matrix */ MultMat44(frm[i].absmat, frm[frm[i].parent].absmat, frm[i].posmatrix); } void ReadSection(clump *clp, rwheader *rwh, FILE *dff) { rwheader child; int secend; secend = rwh->size + ftell(dff); while (secend > ftell(dff)) { child = ReadHeader(dff); ParseSection(clp, &child, rwh->type, dff); } } /* Reads a struct section. */ void ReadStruct(clump *clp, rwheader *rwh, int par, FILE *dff) { int i, j; float temp; atomic *atm; geometry *geo; material *mat; texture *tex; switch(par) { case CLUMP: fread(&clp->objcount, 4, 1, dff); clp->atm = (atomic *) malloc(clp->objcount*sizeof(atomic)); /* Unknown, not used in GTA: III */ if (rwh->version != 0x310) fseek(dff, 8, SEEK_CUR); break; case FRAMELIST: /* Read Rotation Martrix, position and parentframe */ fread(&clp->frmcount, 4, 1, dff); clp->frm = (frame *) malloc(clp->frmcount*sizeof(frame)); for (i = 0; i < clp->frmcount; i++) { int j, k; /* initialize matrices to 0 */ for (j = 0; j < 4; j++) for (k = 0; k < 4; k++) { clp->frm[i].rotmatrix[j][k] = 0; clp->frm[i].posmatrix[j][k] = 0; } clp->frm[i].rotmatrix[3][3] = 1; clp->frm[i].posmatrix[0][0] = 1; clp->frm[i].posmatrix[1][1] = 1; clp->frm[i].posmatrix[2][2] = 1; clp->frm[i].posmatrix[3][3] = 1; /* read rotational matrix */ for (j = 0; j < 3; j++) for (k = 0; k < 3; k++) { fread(&temp, 4, 1, dff); clp->frm[i].rotmatrix[j][k] = temp; } /* read position */ fread(&temp, 4, 1, dff); clp->frm[i].posmatrix[0][3] = temp; fread(&temp, 4, 1, dff); clp->frm[i].posmatrix[1][3] = temp; fread(&temp, 4, 1, dff); clp->frm[i].posmatrix[2][3] = temp; /* read parent frame */ fread(&clp->frm[i].parent, 4, 1, dff); fread(&clp->frm[i].unknown, 4, 1, dff); } break; case GEOMETRYLIST: fread(&clp->geocount, 4, 1, dff); clp->geo = (geometry *) malloc(clp->geocount*sizeof(geometry)); break; case GEOMETRY: geo = &clp->geo[clp->gc]; /* Initialize material counter */ clp->geo[clp->gc].mc = 0; /* If the section is bigger than 52 it must be a PC dff */ if (rwh->size > 52) platform = PC; else platform = PS2; fread(&geo->flags, 2, 1, dff); fread(&geo->uvsets, 1, 1, dff); geo->newuvsets = geo->uvsets; if (geo->uvsets > 2) { printf("Cannot handle more than 2 UV sets!\n"); exit(3); } /* Set number of UV sets according to flags */ if (geo->uvsets == 0 && (geo->flags & 0x80 || geo->flags & 0x04)) geo->newuvsets = 1; fread(&geo->unknown, 1, 1, dff); fread(&geo->facec, 4, 1, dff); fread(&geo->vertexc, 4, 1, dff); fread(&geo->framec, 4, 1, dff); if (rwh->version == 0x0C02FFFF || rwh->version == 0x310) { fread(&geo->ambient, 4, 1, dff); fread(&geo->diffuse, 4, 1, dff); fread(&geo->specular, 4, 1, dff); } if (platform == PC) { geo->vertices = (float **) malloc(geo->vertexc*4); geo->uv[0] = (float **) malloc(geo->vertexc*4); geo->uv[1] = (float **) malloc(geo->vertexc*4); geo->vc = (unsigned char **) malloc(geo->vertexc*4); geo->normals = (float **) malloc(geo->vertexc*4); /* If prelit flag is set, read vertex colors */ for (i = 0; i < geo->vertexc; i++) { geo->vc[i] = (unsigned char *) malloc(1*4); memset(geo->vc[i], 0, 4); if ((geo->flags & 0x08) != 0) { fread(geo->vc[i], 1, 4, dff); } } /* Read UV Coordinates */ for (j = 0; j < geo->newuvsets; j++) for (i = 0; i < geo->vertexc; i++) { geo->uv[j][i] = (float *) malloc(2*4); memset(geo->uv[j][i], 0, 2*4); fread(geo->uv[j][i], 4, 2, dff); } /* Read faces */ geo->faces = (unsigned short **) malloc(geo->facec*4); for (i = 0; i < geo->facec; i++) { geo->faces[i] = (unsigned short *) malloc(2*4); fread(geo->faces[i], 2, 4, dff); } } fread(&geo->bs, 4, 4, dff); fread(&geo->bspos, 4, 1, dff); fread(&geo->bsnor, 4, 1, dff); if (platform == PC) { /* Read vertices */ for (i = 0; i < geo->vertexc; i++) { geo->vertices[i] = (float *) malloc(3*4); fread(geo->vertices[i], 4, 3, dff); } /* If normals flag is set, read normals */ for (i = 0; i < geo->vertexc; i++) { geo->normals[i] = (float *) malloc(3*4); memset(geo->normals[i], 0, 3*4); if ((geo->flags & 0x10) != 0) { fread(geo->normals[i], 4, 3, dff); } } } break; case MATERIALLIST: geo = &clp->geo[clp->gc]; fread(&geo->matcount, 4, 1, dff); geo->mat = (material *) malloc(geo->matcount*sizeof(material)); fseek(dff, 4*geo->matcount, SEEK_CUR); /* unknown */ break; case MATERIAL: geo = &clp->geo[clp->gc]; mat = &geo->mat[geo->mc]; /* Initialize texture counter */ clp->geo[clp->gc].mat[clp->geo[clp->gc].mc].tc = 0; fseek(dff, 4, SEEK_CUR); /* unknown */ fread(&mat->color, 1, 4, dff); fseek(dff, 4, SEEK_CUR); /* unknown */ fread(&mat->texcount, 1, 4, dff); mat->tex = 0; mat->tex = (texture *) malloc(mat->texcount*sizeof(texture)); fseek(dff, 12, SEEK_CUR); /* unknown */ break; case TEXTURE: geo = &clp->geo[clp->gc]; mat = &geo->mat[geo->mc]; tex = &mat->tex[mat->tc]; fread(&tex->filterflags, 2, 1, dff); fread(&tex->unknown, 2, 1, dff); /* Texture name */ *rwh = ReadHeader(dff); /* String */ tex->tex = (char *) malloc(rwh->size+1); fread(tex->tex, rwh->size, 1, dff); tex->tex[rwh->size] = '\0'; /* Alpha texture name */ *rwh = ReadHeader(dff); /* String */ tex->alpha = (char *) malloc(rwh->size+1); fread(tex->alpha, rwh->size, 1, dff); tex->alpha[rwh->size] ='\0'; break; case ATOMIC: atm = &clp->atm[clp->oc]; fread(&atm->frameindex, 4, 1, dff); fread(&atm->geometryindex, 4, 1, dff); fread(&atm->unknown0, 4, 1, dff); fread(&atm->unknown1, 4, 1, dff); break; default: break; } } void ReadFrame(clump *clp, rwheader *rwh, FILE *dff) { clp->frm[clp->fc].name = (char *) malloc(rwh->size+1); fread(clp->frm[clp->fc].name, rwh->size, 1, dff); clp->frm[clp->fc].name[rwh->size] = '\0'; clp->frm[clp->fc].display = 1; #ifdef ASK fprintf(stderr, "Shall object %s be displayed?\n", clp->frm[clp->fc].name); scanf("%d", &clp->frm[clp->fc].display); getchar(); #endif } /* Main function for reading dffs. * Multi clump support is not yet implemented. */ void ReadDff(clump *clp, FILE *dff) { rwheader rwh; /* Initialize object, frame, and geometry counter */ clp->oc = 0; clp->fc = 0; clp->gc = 0; rwh = ReadHeader(dff); while (rwh.type != 0) { ParseSection(clp, &rwh, 0, dff); rwh = ReadHeader(dff); } } /* Decides which function to call depending on the section type. */ void ParseSection(clump *clp, rwheader *sec, int par, FILE *dff) { static int clumpc = 0; switch (sec->type) { case CLUMP: if (clumpc < 1) { ReadSection(clp, sec, dff); clumpc++; } else { fprintf(stderr, "Multi clump dffs are not supported\n"); exit(2); } break; case STRUCT: ReadStruct(clp, sec, par, dff); break; case EXTENSION: ReadSection(clp, sec, dff); break; case FRAMELIST: ReadSection(clp, sec, dff); break; case FRAME: ReadFrame(clp, sec, dff); clp->fc++; break; case GEOMETRYLIST: ReadSection(clp, sec, dff); break; case GEOMETRY: ReadSection(clp, sec, dff); clp->gc++; break; case MATERIALLIST: ReadSection(clp, sec, dff); break; case MATERIAL: ReadSection(clp, sec, dff); clp->geo[clp->gc].mc++; break; case TEXTURE: ReadSection(clp, sec, dff); clp->geo[clp->gc].mat[clp->geo[clp->gc].mc].tc++; break; case ATOMIC: ReadSection(clp, sec, dff); clp->oc++; break; case BINMESH: if (platform == PS2) ReadBinMeshPS2(&clp->geo[clp->gc], dff); else if (platform == PC) ReadBinMeshPC(&clp->geo[clp->gc], dff); else { printf("Don't know platform\n"); exit(2); } break; case NATIVEDATA: /* The San Andreas Native Data section is actually * a special case of the VC section. * This test does not always the right thing, * San Andreas weapons for example have the Vice City format */ if (sec->version == 0x1803FFFF) ReadNativeDataSAPS2(&clp->geo[clp->gc], dff); else ReadNativeDataVCPS2(&clp->geo[clp->gc], dff); break; case SKIN: case MORPH: case PARTICLES: case SKYMIPMAP: case MATERIALEFFECTS: case RIGHTTORENDER: case HANIM: case REFLECTIONMAT: case SPECULARMAT: case MESHEXTENSION: case COLLISIONMODEL: case NIGHTVERTEXCOLOR: case ADCPLG: /* Skip section */ fseek(dff, sec->size, SEEK_CUR); break; default: fprintf(stderr, "Error: Unknown section: %X at %lX\n", sec->type, ftell(dff)); exit(2); break; } } void WriteObj(clump *clp, FILE *obj) { int i, j, k, faceinc; int g, f; float *vert; float newvert[4]; /* faceinc is used to increment face indices */ faceinc = 0; for (i = 0; i < clp->objcount; i++) { g = clp->atm[i].geometryindex; f = clp->atm[i].frameindex; if (clp->frm[f].display == 0) continue; GetTotalTrans(clp->frm, f); fprintf(obj, "g %s\n", clp->frm[f].name); for (j = 0; j < clp->geo[g].splitcount; j++) { split cursplit; cursplit = clp->geo[g].splt[j]; /* fprintf(obj, "# tex: %s\n", clp->geo[g].mat[cursplit.matindex].tex[0].tex); fprintf(obj, "# alpha: %s\n", clp->geo[g].mat[cursplit.matindex].tex[0].alpha); */ /* print vertices */ for (k = 0; k < cursplit.vertexcount; k++) { vert = cursplit.vertices[k]; newvert[X] = vert[X]; newvert[Y] = vert[Y]; newvert[Z] = vert[Z]; newvert[3] = 1; /* Get absolute position */ MultMat44Vect(newvert, clp->frm[f].absmat, newvert); fprintf(obj, "v %f %f %f\n", newvert[X], newvert[Y], newvert[Z]); } /* print UV coordinates */ for (k = 0; k < cursplit.vertexcount; k++) { fprintf(obj, "vt %f %f\n", cursplit.uv[0][k][U], 1 - cursplit.uv[0][k][V]); } /* print normals */ // if (platform != PS2) for (k = 0; k < cursplit.vertexcount; k++) { fprintf(obj, "vn %f %f %f\n", cursplit.normals[k][X], cursplit.normals[k][Y], cursplit.normals[k][Z]); } /* print faces */ /* Vertex strip */ if (clp->geo[g].facetype == 1) { for (k = 2; k < cursplit.vertexcount; k++) { /* if two vertices of the same face are the same, begin a new face */ if (IsSameVert(cursplit.vertices[k], cursplit.vertices[k-1]) || IsSameVert(cursplit.vertices[k], cursplit.vertices[k-2]) || IsSameVert(cursplit.vertices[k-1], cursplit.vertices[k-2])) continue; fprintf(obj, "f %d/%d/%d %d/%d/%d %d/%d/%d\n", k-1+faceinc, k-1+faceinc, k-1+faceinc, k + faceinc, k + faceinc, k + faceinc, k+1+faceinc, k+1+faceinc, k+1+faceinc ); } /* Vertex list */ } else { for (k = 0; k < cursplit.vertexcount; k += 3) { fprintf(obj, "f %d/%d/%d %d/%d/%d %d/%d/%d\n", k+1+faceinc, k+1+faceinc, k+1+faceinc, k+2+faceinc, k+2+faceinc, k+2+faceinc, k+3+faceinc, k+3+faceinc,k+3+faceinc); } } faceinc += clp->geo[g].splt[j].vertexcount; } } }