ORF
From ODF::Wiki
OutrageRoomFile
Rooms are small units of a level in Descent 3.
Here is a short but hopefully useful document on how the Outrage Room Format (ORF) works. This document is meant to help gamers who want to write their own exporters and use their favorite 3d modelling program and D3edit together.
An ORF file describes one room. A room is a collection of faces defined by any number of 3d vectors. To help conserve memory, faces can share vectors.
The ORF file is broken up into chunks which describe a certain aspect of the room like header, vectors, and textures. A chunk is a CHUNK_ID (int, 4 bytes), a size (int, 4bytes), and the corresponding data.
Here, in C, is a code that saves a room. For our purposes, the room structure would be your 3d modelers data coverted in a way that could be used by the following routine.
#define ROOM_VERTEX_CHUNK 1 #define ROOM_FACES_CHUNK 2 #define ROOM_END_CHUNK 3 #define ROOM_TEXTURE_CHUNK 4 #define ROOM_HEADER_CHUNK 5 #define ROOMFILE_VERSION 4 #define LIGHT_MULTIPLE_DEFAULT 4 #define TEXTURE_DEFAULT 0 // saves a room in our ORF (Outrage room file) format void SaveRoom (char *filename) { CFILE *outfile; int headsize,savepos,vertsize,facesize,texsize; int highest_index=0; short Room_to_texture[MAX_TEXTURES]; int t,found_it=0; // Open the file for writing outfile=(CFILE *)cfopen (filename,"wb"); if (!outfile) { return; // Couldn't open file for writing! } // write out header info cf_WriteInt (outfile,ROOM_NEW_HEADER_CHUNK); headsize=cftell(outfile); // Save this file position so we know where to write the size of the chunk cf_WriteInt (outfile,-1); cf_WriteInt (outfile,ROOMFILE_VERSION); cf_WriteInt (outfile,Room.num_verts); cf_WriteInt (outfile,Room.num_faces); // Now go back and fill in the size of the chunk savepos=cftell (outfile); cfseek (outfile,headsize,SEEK_SET); cf_WriteInt (outfile,(savepos-headsize)-4); cfseek (outfile,savepos,SEEK_SET); // write out vertex info cf_WriteInt (outfile,ROOM_VERTEX_CHUNK); vertsize=cftell(outfile); cf_WriteInt (outfile,-1); for (int i=0;i<Room.num_verts;i++) { cf_WriteFloat (outfile,Room.verts[i].x); cf_WriteFloat (outfile,Room.verts[i].y); cf_WriteFloat (outfile,Room.verts[i].z); } savepos=cftell (outfile); cfseek (outfile,vertsize,SEEK_SET); cf_WriteInt (outfile,(savepos-vertsize)-4); cfseek (outfile,savepos,SEEK_SET); // write out face info cf_WriteInt (outfile,ROOM_FACES_CHUNK); facesize=cftell(outfile); cf_WriteInt (outfile,-1); for (i=0;i<Room.num_faces;i++) { // For our purposes, the light multiple is always the same cf_WriteByte (outfile,LIGHT_MULTIPLE_DEFAULT); cf_WriteInt (outfile,Room.faces[i].num_verts); // Write out the normal for this face cf_WriteFloat (outfile,Room.faces[i].normal.x); cf_WriteFloat (outfile,Room.faces[i].normal.y); cf_WriteFloat (outfile,Room.faces[i].normal.z); cf_WriteShort (outfile,TEXTURE_DEFAULT); // Bash to texture 0 // Write out vertex specific info for (t=0;t<Room.faces[i].num_verts;t++) { // This field is an index into the global Room.verts field. This allows faces to // share vectors without duplicating their x,y,and z info. cf_WriteShort (outfile,Room.faces[i].face_verts[t]); // Write out UV info cf_WriteFloat (outfile,Room.faces[i].face_uvls[t].u); cf_WriteFloat (outfile,Room.faces[i].face_uvls[t].v); cf_WriteFloat (outfile,0); // These are zero to maintain compat with older room readers cf_WriteFloat (outfile,0); cf_WriteFloat (outfile,0); cf_WriteFloat (outfile,0); cf_WriteFloat (outfile,1.0); // Alpha for vert } } savepos=cftell (outfile); cfseek (outfile,facesize,SEEK_SET); cf_WriteInt (outfile,(savepos-facesize)-4); cfseek (outfile,savepos,SEEK_SET); // Write the end chunk cf_WriteInt (outfile,ROOM_END_CHUNK); cf_WriteInt (outfile,4); // Close the file cfclose (outfile); // We're done! return; }

