//mxd. Normals calculation algorithm taken from OpenGl wiki private void CalculateNormals() { if (triangles == 0) { return; } BoundingBoxSizes bbs = new BoundingBoxSizes(vertices[0]); for (int i = 0; i < triangles; i++) { int startindex = i * 3; WorldVertex p1 = vertices[startindex]; WorldVertex p2 = vertices[startindex + 1]; WorldVertex p3 = vertices[startindex + 2]; Vector3f U = new Vector3f(p2.x - p1.x, p2.y - p1.y, p2.z - p1.z); Vector3f V = new Vector3f(p3.x - p1.x, p3.y - p1.y, p3.z - p1.z); p1.nx = p2.nx = p3.nx = -(U.Y * V.Z - U.Z * V.Y); p1.ny = p2.ny = p3.ny = -(U.Z * V.X - U.X * V.Z); p1.nz = p2.nz = p3.nz = -(U.X * V.Y - U.Y * V.X); vertices[startindex] = p1; vertices[startindex + 1] = p2; vertices[startindex + 2] = p3; BoundingBoxTools.UpdateBoundingBoxSizes(ref bbs, p1); BoundingBoxTools.UpdateBoundingBoxSizes(ref bbs, p2); BoundingBoxTools.UpdateBoundingBoxSizes(ref bbs, p3); } boundingBox = BoundingBoxTools.CalculateBoundingPlane(bbs); }
private static MD3LoadResult ReadMD2Model(ref BoundingBoxSizes bbs, Stream s, Device device, int frame, string framename) { long start = s.Position; MD3LoadResult result = new MD3LoadResult(); using (var br = new BinaryReader(s, Encoding.ASCII)) { string magic = ReadString(br, 4); if (magic != "IDP2") //magic number: "IDP2" { result.Errors = "unknown header: expected \"IDP2\", but got \"" + magic + "\""; return(result); } int modelVersion = br.ReadInt32(); if (modelVersion != 8) //MD2 version. Must be equal to 8 { result.Errors = "expected MD3 version 15, but got " + modelVersion; return(result); } int texWidth = br.ReadInt32(); int texHeight = br.ReadInt32(); int framesize = br.ReadInt32(); // Size of one frame in bytes s.Position += 4; //Number of textures int num_verts = br.ReadInt32(); //Number of vertices int num_uv = br.ReadInt32(); //The number of UV coordinates in the model int num_tris = br.ReadInt32(); //Number of triangles s.Position += 4; //Number of OpenGL commands int num_frames = br.ReadInt32(); //Total number of frames // Sanity checks if (frame < 0 || frame >= num_frames) { result.Errors = "frame " + frame + " is outside of model's frame range [0.." + (num_frames - 1) + "]"; return(result); } s.Position += 4; //Offset to skin names (each skin name is an unsigned char[64] and are null terminated) int ofs_uv = br.ReadInt32(); //Offset to s-t texture coordinates int ofs_tris = br.ReadInt32(); //Offset to triangles int ofs_animFrame = br.ReadInt32(); //An offset to the first animation frame List <int> polyIndecesList = new List <int>(); List <int> uvIndecesList = new List <int>(); List <Vector2> uvCoordsList = new List <Vector2>(); List <WorldVertex> vertList = new List <WorldVertex>(); // Polygons s.Position = ofs_tris + start; for (int i = 0; i < num_tris; i++) { polyIndecesList.Add(br.ReadUInt16()); polyIndecesList.Add(br.ReadUInt16()); polyIndecesList.Add(br.ReadUInt16()); uvIndecesList.Add(br.ReadUInt16()); uvIndecesList.Add(br.ReadUInt16()); uvIndecesList.Add(br.ReadUInt16()); } // UV coords s.Position = ofs_uv + start; for (int i = 0; i < num_uv; i++) { uvCoordsList.Add(new Vector2((float)br.ReadInt16() / texWidth, (float)br.ReadInt16() / texHeight)); } // Frames // Find correct frame if (!string.IsNullOrEmpty(framename)) { // Skip frames untill frame name matches bool framefound = false; for (int i = 0; i < num_frames; i++) { s.Position = ofs_animFrame + start + i * framesize; s.Position += 24; // Skip scale and translate string curframename = ReadString(br, 16).ToLowerInvariant(); if (curframename == framename) { // Step back so scale and translate can be read s.Position -= 40; framefound = true; break; } } // No dice? Bail out! if (!framefound) { result.Errors = "unable to find frame \"" + framename + "\"!"; return(result); } } else { // If we have frame number, we can go directly to target frame s.Position = ofs_animFrame + start + frame * framesize; } Vector3 scale = new Vector3(br.ReadSingle(), br.ReadSingle(), br.ReadSingle()); Vector3 translate = new Vector3(br.ReadSingle(), br.ReadSingle(), br.ReadSingle()); s.Position += 16; // Skip frame name // Prepare to fix rotation angle float angleOfsetCos = (float)Math.Cos(-Angle2D.PIHALF); float angleOfsetSin = (float)Math.Sin(-Angle2D.PIHALF); //verts for (int i = 0; i < num_verts; i++) { WorldVertex v = new WorldVertex(); v.x = (br.ReadByte() * scale.X + translate.X); v.y = (br.ReadByte() * scale.Y + translate.Y); v.z = (br.ReadByte() * scale.Z + translate.Z); // Fix rotation angle float rx = angleOfsetCos * v.x - angleOfsetSin * v.y; float ry = angleOfsetSin * v.x + angleOfsetCos * v.y; v.y = ry; v.x = rx; vertList.Add(v); s.Position += 1; //vertex normal } for (int i = 0; i < polyIndecesList.Count; i++) { WorldVertex v = vertList[polyIndecesList[i]]; //bounding box BoundingBoxTools.UpdateBoundingBoxSizes(ref bbs, new WorldVertex(v.y, v.x, v.z)); //uv float tu = uvCoordsList[uvIndecesList[i]].X; float tv = uvCoordsList[uvIndecesList[i]].Y; //uv-coordinates already set? if (v.c == -1 && (v.u != tu || v.v != tv)) { //add a new vertex vertList.Add(new WorldVertex(v.x, v.y, v.z, -1, tu, tv)); polyIndecesList[i] = vertList.Count - 1; } else { v.u = tu; v.v = tv; v.c = -1; //set color to white //return to proper place vertList[polyIndecesList[i]] = v; } } //mesh Mesh mesh = new Mesh(device, polyIndecesList.Count / 3, vertList.Count, MeshFlags.Use32Bit | MeshFlags.IndexBufferManaged | MeshFlags.VertexBufferManaged, vertexElements); using (DataStream stream = mesh.LockVertexBuffer(LockFlags.None)) { stream.WriteRange(vertList.ToArray()); } mesh.UnlockVertexBuffer(); using (DataStream stream = mesh.LockIndexBuffer(LockFlags.None)) { stream.WriteRange(polyIndecesList.ToArray()); } mesh.UnlockIndexBuffer(); mesh.OptimizeInPlace(MeshOptimizeFlags.AttributeSort); //store in result result.Meshes.Add(mesh); result.Skins.Add(""); //no skin support for MD2 } return(result); }
private static string ReadSurface(ref BoundingBoxSizes bbs, ref string skin, BinaryReader br, List <int> polyIndecesList, List <WorldVertex> vertList, int frame) { int vertexOffset = vertList.Count; long start = br.BaseStream.Position; string magic = ReadString(br, 4); if (magic != "IDP3") { return("error while reading surface. Unknown header: expected \"IDP3\", but got \"" + magic + "\""); } string name = ReadString(br, 64); int flags = br.ReadInt32(); int numFrames = br.ReadInt32(); //Number of animation frames. This should match NUM_FRAMES in the MD3 header. int numShaders = br.ReadInt32(); //Number of Shader objects defined in this Surface, with a limit of MD3_MAX_SHADERS. Current value of MD3_MAX_SHADERS is 256. int numVerts = br.ReadInt32(); //Number of Vertex objects defined in this Surface, up to MD3_MAX_VERTS. Current value of MD3_MAX_VERTS is 4096. int numTriangles = br.ReadInt32(); //Number of Triangle objects defined in this Surface, maximum of MD3_MAX_TRIANGLES. Current value of MD3_MAX_TRIANGLES is 8192. int ofsTriangles = br.ReadInt32(); //Relative offset from SURFACE_START where the list of Triangle objects starts. int ofsShaders = br.ReadInt32(); int ofsST = br.ReadInt32(); //Relative offset from SURFACE_START where the list of ST objects (s-t texture coordinates) starts. int ofsNormal = br.ReadInt32(); //Relative offset from SURFACE_START where the list of Vertex objects (X-Y-Z-N vertices) starts. int ofsEnd = br.ReadInt32(); //Relative offset from SURFACE_START to where the Surface object ends. // Sanity check if (frame < 0 || frame >= numFrames) { return("frame " + frame + " is outside of model's frame range [0.." + (numFrames - 1) + "]"); } // Polygons if (start + ofsTriangles != br.BaseStream.Position) { br.BaseStream.Position = start + ofsTriangles; } for (int i = 0; i < numTriangles * 3; i++) { polyIndecesList.Add(vertexOffset + br.ReadInt32()); } // Shaders if (start + ofsShaders != br.BaseStream.Position) { br.BaseStream.Position = start + ofsShaders; } skin = ReadString(br, 64); //we are interested only in the first one // Vertices if (start + ofsST != br.BaseStream.Position) { br.BaseStream.Position = start + ofsST; } for (int i = 0; i < numVerts; i++) { WorldVertex v = new WorldVertex(); v.c = -1; //white v.u = br.ReadSingle(); v.v = br.ReadSingle(); vertList.Add(v); } // Positions and normals long vertoffset = start + ofsNormal + numVerts * 8 * frame; // The length of Vertex struct is 8 bytes if (br.BaseStream.Position != vertoffset) { br.BaseStream.Position = vertoffset; } for (int i = vertexOffset; i < vertexOffset + numVerts; i++) { WorldVertex v = vertList[i]; //read vertex v.y = -(float)br.ReadInt16() / 64; v.x = (float)br.ReadInt16() / 64; v.z = (float)br.ReadInt16() / 64; //bounding box BoundingBoxTools.UpdateBoundingBoxSizes(ref bbs, v); var lat = br.ReadByte() * (2 * Math.PI) / 255.0; var lng = br.ReadByte() * (2 * Math.PI) / 255.0; v.nx = (float)(Math.Sin(lng) * Math.Sin(lat)); v.ny = -(float)(Math.Cos(lng) * Math.Sin(lat)); v.nz = (float)(Math.Cos(lat)); vertList[i] = v; } if (start + ofsEnd != br.BaseStream.Position) { br.BaseStream.Position = start + ofsEnd; } return(""); }