C sharp:MD2 loader in CSharp
From GPWikiThis little tutorial is supposed to give you an idea on how to load MD2 data using C#. This doesn't cover rendering or complex data structures to hold the data (yet!), it's just my method of loading data from MD2 files into List structures (provided by C#). You should know from the start that the code isn't optimal or perfect since I wrote this while learning C#. I also wont explain the C# techniques I used since this is not a C# tutorial nor will I explain the MD2 format, there are plenty tutorials available about this subject (you should probably study one of those tutorials to have a clue about how a MD2 file is organized). So enjoy and happy coding. So here we go, the method I used was to create some structs in which I will read the different data blocks in a MD2 model. struct md2_header { public int ident; // signature. must be equal to "IPD2" public int version; // md2 version. must be equal to 8 public int skinwidth; // width of the texture public int skinheight; // height of the texture public int framesize; // size of one frame in bytes public int num_skins; // number of textures public int num_xyz; // number of vertices public int num_st; // number of texture coordinates public int num_tris; // number of triangles public int num_glcmds; // number of opengl commands public int num_frames; // total number of frames public int ofs_skins; // offset to skin names (64 bytes each) public int ofs_st; // offset to s-t texture coordinates public int ofs_tris; // offset to triangles public int ofs_frames; // offset to frame data public int ofs_glcmds; // offset to opengl commands public int ofs_end; // offset to end of file } struct md2_vertex { unsafe public fixed byte coords[3]; //x,y,z compressed coordinates (see frame) public byte light; //index to vertice's lighting normal } struct md2_triangle { unsafe public fixed short index_xyz[3]; // indexes to triangle's vertices unsafe public fixed short index_st[3]; // indexes to vertice's texture coorinates } struct md2_frame { unsafe public fixed float scale[3]; //scale values for x,y,z (multiply) unsafe public fixed float translate[3]; //translate values for x,y,z (add) unsafe public fixed byte name[16]; //frame name; } struct md2_compressed_texture { public short s; //texture x coordinate (compressed, should be divided by skin width) public short t; //texture y coordinate (compressed, should be divided by skin height) } struct md2_glcommand { public float s; public float t; public int index; } You can put these structures into a separate file in the same namespace as your program/engine. Now for reading I used FileStream and BinaryReader like you can see below: FileStream fs = new FileStream(filename, FileMode.Open, FileAccess.Read); //instantiate the stream object BinaryReader br = new BinaryReader(fs); //instantiate the reader object What I do is read the data binary from the file and convert the block to the respective structure: MD2_HEADER: md2_header header; //MD2 header byte[] buff = br.ReadBytes(Marshal.SizeOf(typeof(md2_header)));//read the header as binary data GCHandle handle = GCHandle.Alloc(buff, GCHandleType.Pinned); //prevent the GC from affecting the buffer header = (md2_header)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(md2_header)); //get the header from the buffer handle.Free(); //let the GC do his thing again Ok, now we have the header so we know where to find all the actual model data blocks: MD2_TRIANGLES md2_triangle trt; //MD2 triangle br.BaseStream.Seek(header.ofs_tris, SeekOrigin.Begin); //seek to the triangles offset in the MD2 file for (int i = 0; i <= header.num_tris - 1; i++) { buff = br.ReadBytes(Marshal.SizeOf(typeof(md2_triangle)));//read a triangle handle = GCHandle.Alloc(buff, GCHandleType.Pinned); //block GC trt = (md2_triangle)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(md2_triangle)); //get struct from binary handle.Free(); //free GC //add your new triangle to the triangle pool in your mesh object or whatever structure you want to use } MD2_FRAMES md2_frame ffrm; //MD2 frame br.BaseStream.Seek(header.ofs_frames, SeekOrigin.Begin); //go to first frame in the MD2 file for (int j = 0; j < header.num_frames-1; j++) //read the frames { buff = br.ReadBytes(Marshal.SizeOf(typeof(md2_frame))); handle = GCHandle.Alloc(buff, GCHandleType.Pinned); ffrm = (md2_frame)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(md2_frame)); handle.Free(); //at this point you have the name of the frame so you can create your frame object in the frame pool or whatever :) //now to read all the vertexes in this frame md2_vertex tvrt; float tx, ty, tz; for (int i = 0; i <= header.num_xyz - 1; i++) //for each frame read all the vertexes { buff = br.ReadBytes(Marshal.SizeOf(typeof(md2_vertex))); handle = GCHandle.Alloc(buff, GCHandleType.Pinned); tvrt = (md2_vertex)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(md2_vertex)); handle.Free(); tx = tvrt.coords[0] * ffrm.scale[0] + ffrm.translate[0]; ty = tvrt.coords[1] * ffrm.scale[1] + ffrm.translate[1]; tz = tvrt.coords[2] * ffrm.scale[2] + ffrm.translate[2]; //add the vertex to the frame object... } } MD2_TEXTURE_COORDINATES md2_compressed_texture ctex; //MD2 compressed texture coordinates br.BaseStream.Seek(header.ofs_st, SeekOrigin.Begin); //go to start of texture coordinates for (int i = 0; i < header.num_st; i++) { buff = br.ReadBytes(Marshal.SizeOf(typeof(md2_compressed_texture))); handle = GCHandle.Alloc(buff, GCHandleType.Pinned); ctex = (md2_compressed_texture)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(md2_compressed_texture)); handle.Free(); //add the new texture to your textures pool, dont forget to decompress the texture coordinates (dividing them by skin width and height) } Now you have just enough to start rendering of your model. What I did was to code a render_frame function that based on the supplied frame number would draw each triangle using the appropriate frame vertexes. You might notice that I skipped the reading of normals and glcommands. They are not really necessary as you can calculate your own normals(like I did, because I load also other file formats that dont contain the precalculated normals) and glcommands are used to render the frame using triangle strips and fans (this will achieve better frame reates by lowering the ammount of data sent to opengl to render), instead you can use your own method to strippify your mesh. DOWNLOAD This is my MD2 viewer, the code is a mess and the methods I used for the data structures and rendering might not be the brightest but I achieved my goal to learn OpenGL in C# adn it will give you an idea how I've complicated my life with OOP and OpenGL. For any comments, suggestions or "your code sucks" messages please do not hesitate to write me an email to balex2ro@yahoo.com. Thanks to all the members here writting about the MD2 format, and thanks to the people who wrote the csgl library. STUFF TO READ Cheers |



