Unreal File FormatWritten by PantherD of Team Panther.
Thanks to Tim Sweeney for the UnrealEd mesh code Edited by Paul Bourke
IntroductionUnreal models come in a combination of two files. Data files which you will see as _d.3d and Aniv files which you'll see as _a.3d. Both are binary files. The data file contains a data header with number of polygons, number of vertices, etc. A lot of the header data is actually throw away (you'll see later in my document a typedef for the dataheader structure). You'll also find each polygon with it's three vertex indices. The index is the order of the vertex within the aniv file. All polygons are triangles. The Aniv file contains the a list of the vertices per each frame. The first part of the aniv file describes the number of frames, then the framesize (Number of vertices * bytesize of a vertex). Then all the vertices for every frame. Data FileData File HeaderThe data file header should be written to the file first. The only thing that you need to worry about is the number of polygons and the number of vertices. The rest of the information is not currently read by UnrealEd. typedef struct datahead_struct { unsigned short NumPolygons; unsigned short NumVertices; unsigned short BogusRot; unsigned short BogusFrame; unsigned long BogusNormX; unsigned long BogusNormY; unsigned long BogusNormZ; unsigned long FixScale; unsigned long Unused[3]; unsigned char Unknown[12]; } dataheader; Data File PolygonsThe polygon structure includes references to the three vertices, the type of polgyon, the color (which I honestly don't know what that is good for), the texture UV coords and a texture number. The flags are currently unused by UnrealEd. The texture number will correspond with whatever is in the .uc class file for the object. The only texture information stored for a polgyon is the UV coordinates. The actual texture image file reference is done with the .uc class file. The mVertex[3] is an array of the 3 vertices. Each vertex (ex. MVertex[0] = 2) stores the index of the vertex coordinate information stored in the aniv file. The values will be [0 to (n - 1)], n being the number of vertices in the mesh. My example points the first polgyon of the triangle to whatever the 3rd vertices in the aniv file coordinate are. The UV texture coordinates are stored as a value within a 256 x 256 grid. Standard UV coordinates are [0 to 1] with values between. You can correspond 256 to 1 for Unreal. Typical Unreal texture map images are [128x128] or [256x256] pixels. It's a two dimensional array the first dimension being 0 or 1 indicating whether it's a U or V coord: Thus:
[0][0] - U coordinate of the first vertex James Mesh Types0 = Normal one-sided1 = Normal two-sided 2 = Translucent two-sided 3 = Masked two-sided 4 = Modulation blended two-sided 8 = Placeholder triangle for weapon positioning (invisible) typedef struct unreal_tri_struct { unsigned short mVertex[3]; // Vertex indices char mType; // James' Mesh Type char mColor; // Color for flat and Gourand Shaded unsigned char mTex[3][2]; // Texture UV coordinates char mTextureNum; // Source texture offset char mFlags; // Unreal Mesh Flags (unused) } unreal_tri; This is my datafile output function that I used for my UnrealSaver plugin (note this also output a UV text file I am messing with as well): void CreateDataFile(MeshData *mesh) { FILE *data_fp; FILE *uv_fp; dataheader dh; unreal_tri *unrealpoly; int i; memset(&dh, '\0', sizeof(dataheader)); dh.NumPolygons = mesh->NumPolygons; dh.NumVertices = mesh->NumVertices; data_fp = fopen(mesh->config->datafile, "wb"); uv_fp = fopen(mesh->config->uvfile, "w"); unrealpoly = malloc(sizeof(unreal_tri)); if (data_fp) { //write the Header fwrite(&dh, sizeof(dataheader), 1, data_fp); //write the polygon data mesh->PolyList->curr_poly = mesh->PolyList->start_poly; while (mesh->PolyList->curr_poly != NULL) { // copy polygon data to the unrealpolygon for storage for (i = 0; i < 3; i++) { unrealpoly->mVertex[i] = mesh->PolyList->curr_poly->mVertex[i]; unrealpoly->mTex[i][0] = mesh->PolyList->curr_poly->mTex[i][0]; unrealpoly->mTex[i][1] = mesh->PolyList->curr_poly->mTex[i][1]; //write the UV data out to uvd file - 8/29/98 fprintf(uv_fp, "%d %d ", unrealpoly->mTex[i][0], unrealpoly->mTex[i][1]); } //write newline for next polygon fprintf(uv_fp, "\n"); unrealpoly->mType = mesh->PolyList->curr_poly->mType; unrealpoly->mColor = mesh->PolyList->curr_poly->mColor; unrealpoly->mTextureNum = mesh->PolyList->curr_poly->mTextureNum; unrealpoly->mFlags = mesh->PolyList->curr_poly->mFlags; fwrite(unrealpoly, sizeof(unreal_tri), 1, data_fp); mesh->PolyList->curr_poly = mesh->PolyList->curr_poly->next_poly; } fclose(data_fp); fclose(uv_fp); } Aniv FileThe aniv file is actually pretty simple. Each vertices is stored in an unsigned long (examine my code below for the formula). The first step is to write the number of frames, then the framesize (number of vertices * sizeof(unsigned long) ). int CreateAnimFile(MeshData *mesh) { FILE *anim_fp; unsigned long unreal_vertex; int frames_written; short FrameSize; frame *curr_frame; vertices *curr_vertices; frames_written = 0; anim_fp = fopen(mesh->config->anivfile, "wb"); if (anim_fp) { //write number of frames fwrite(&mesh->frame_header->number_of_frames, sizeof(short), 1, anim_fp); //write framesize FrameSize = mesh->NumVertices * sizeof(unsigned long); fwrite(&FrameSize, sizeof(FrameSize), 1, anim_fp); //write the vertices (converted to unreal unsigned long storage) curr_frame = mesh->frame_header->frame_list; while (curr_frame != NULL ) { curr_vertices = curr_frame->VertexList->start_vertex; while (curr_vertices != NULL) { unreal_vertex = ( (int)(curr_vertices->x * 8.0) & 0x7ff ) | ( ( (int)(curr_vertices->y * 8.0) & 0x7ff) << 11 ) | ( ( (int)(curr_vertices->z * 4.0) & 0x3ff) << 22 ); fwrite(&unreal_vertex, sizeof(unsigned long), 1, anim_fp); curr_vertices = curr_vertices->next_vertex; } frames_written++; curr_frame = curr_frame->next_frame; } fclose(anim_fp); } return frames_written; } UC Class Filevoid CreateUCFile(MeshData *mesh) { FILE *fp; int texture_loop; fp = fopen(mesh->config->ucfile, "w"); if (fp) { fprintf(fp, "#exec MESH IMPORT MESH=%s ANIVFILE=MODELS\\%s_a.3d DATAFILE=MODELS\\%s_d.3d X=0 Y=0 Z=0\n", mesh->config->classname, mesh->config->classname, mesh->config->classname); fprintf(fp, "#exec MESH ORIGIN MESH=%s X=0 Y=0 Z=0\n", mesh->config->classname); //Print out the anmimation sequences, using the all, an intial still and one last sample fprintf(fp, "#exec MESH SEQUENCE MESH=%s SEQ=All STARTFRAME=0 NUMFRAMES=%d\n", mesh->config->classname, mesh->NumFrames); fprintf(fp, "#exec MESH SEQUENCE MESH=%s SEQ=Still STARTFRAME=0 NUMFRAMES=1\n", mesh->config->classname); fprintf(fp, "#exec MESH SEQUENCE MESH=%s SEQ=YouNameIt STARTFRAME=0 NUMFRAMES=%d\n", mesh->config->classname, mesh->NumFrames); //Print out textures for (texture_loop = 0; texture_loop <= mesh->NumTextures; texture_loop++) fprintf(fp, "#exec TEXTURE IMPORT NAME=%s FILE=MODELS\\%s.PCX GROUP=\"Skins\"\n", mesh->texturename[texture_loop], mesh->texturename[texture_loop]); fprintf(fp, "#exec MESHMAP SCALE MESHMAP=%s X=0.5 Y=0.5 Z=1.0\n", mesh->config->classname); for (texture_loop = 0; texture_loop <= mesh->NumTextures; texture_loop++) fprintf(fp, "#exec MESHMAP SETTEXTURE MESHMAP=%s NUM=%d TEXTURE=%s\n", mesh->config->classname, texture_loop, mesh->texturename[texture_loop]); fclose(fp); } } |