private static void CreateMesh(Device device, ref MD3LoadResult result, List <WorldVertex> verts, List <int> indices) { //create mesh Mesh mesh = new Mesh(device, indices.Count / 3, verts.Count, MeshFlags.Use32Bit | MeshFlags.IndexBufferManaged | MeshFlags.VertexBufferManaged, vertexElements); using (DataStream stream = mesh.LockVertexBuffer(LockFlags.None)) { stream.WriteRange(verts.ToArray()); } mesh.UnlockVertexBuffer(); using (DataStream stream = mesh.LockIndexBuffer(LockFlags.None)) { stream.WriteRange(indices.ToArray()); } mesh.UnlockIndexBuffer(); mesh.OptimizeInPlace(MeshOptimizeFlags.AttributeSort); //store in result result.Meshes.Add(mesh); }
private static void LoadModel(ModelData mde, List <DataReader> containers, Device device) { mde.Model = new GZModel(); BoundingBoxSizes bbs = new BoundingBoxSizes(); MD3LoadResult result = new MD3LoadResult(); //load models and textures for (int i = 0; i < mde.ModelNames.Count; i++) { // Use model skins? // INFO: Skin MODELDEF property overrides both embedded surface names and ones set using SurfaceSkin MODELDEF property Dictionary <int, string> skins = null; if (string.IsNullOrEmpty(mde.SkinNames[i])) { skins = (mde.SurfaceSkinNames[i].Count > 0 ? mde.SurfaceSkinNames[i] : new Dictionary <int, string>()); } // Load mesh MemoryStream ms = LoadFile(containers, mde.ModelNames[i], true); if (ms == null) { General.ErrorLogger.Add(ErrorType.Error, "Error while loading \"" + mde.ModelNames[i] + "\": unable to find file."); continue; } string ext = Path.GetExtension(mde.ModelNames[i]); switch (ext) { case ".md3": if (!string.IsNullOrEmpty(mde.FrameNames[i])) { General.ErrorLogger.Add(ErrorType.Error, "Error while loading \"" + mde.ModelNames[i] + "\": frame names are not supported for MD3 models!"); continue; } result = ReadMD3Model(ref bbs, skins, ms, device, mde.FrameIndices[i]); break; case ".md2": result = ReadMD2Model(ref bbs, ms, device, mde.FrameIndices[i], mde.FrameNames[i]); break; default: result.Errors = "model format is not supported"; break; } ms.Close(); //got errors? if (!String.IsNullOrEmpty(result.Errors)) { General.ErrorLogger.Add(ErrorType.Error, "Error while loading \"" + mde.ModelNames[i] + "\": " + result.Errors); } else { //add loaded data to ModeldefEntry mde.Model.Meshes.AddRange(result.Meshes); //prepare UnknownTexture3D... just in case :) if (General.Map.Data.UnknownTexture3D.Texture == null || General.Map.Data.UnknownTexture3D.Texture.Disposed) { General.Map.Data.UnknownTexture3D.CreateTexture(); } //load texture List <string> errors = new List <string>(); // Texture not defined in MODELDEF? if (skins != null) { //try to use model's own skins for (int m = 0; m < result.Meshes.Count; m++) { if (string.IsNullOrEmpty(result.Skins[m])) { mde.Model.Textures.Add(General.Map.Data.UnknownTexture3D.Texture); errors.Add("texture not found in MODELDEF or model skin."); continue; } string path = result.Skins[m].Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar); ext = Path.GetExtension(path); if (Array.IndexOf(ModelData.SUPPORTED_TEXTURE_EXTENSIONS, ext) == -1) { mde.Model.Textures.Add(General.Map.Data.UnknownTexture3D.Texture); errors.Add("image format \"" + ext + "\" is not supported!"); continue; } //relative path? if (path.IndexOf(Path.DirectorySeparatorChar) == -1) { path = Path.Combine(Path.GetDirectoryName(mde.ModelNames[i]), path); } Texture t = LoadTexture(containers, path, device); if (t == null) { mde.Model.Textures.Add(General.Map.Data.UnknownTexture3D.Texture); errors.Add("unable to load skin \"" + result.Skins[m] + "\""); continue; } mde.Model.Textures.Add(t); } } //Try to use texture loaded from MODELDEFS else { Texture t = LoadTexture(containers, mde.SkinNames[i], device); if (t == null) { mde.Model.Textures.Add(General.Map.Data.UnknownTexture3D.Texture); errors.Add("unable to load texture \"" + mde.SkinNames[i] + "\""); } else { mde.Model.Textures.Add(t); } } //report errors if (errors.Count > 0) { foreach (string e in errors) { General.ErrorLogger.Add(ErrorType.Error, "Error while loading \"" + mde.ModelNames[i] + "\": " + e); } } } } //clear unneeded data mde.SkinNames = null; mde.ModelNames = null; if (mde.Model.Meshes == null || mde.Model.Meshes.Count == 0) { mde.Model = null; return; } //scale bbs bbs.MaxX = (int)(bbs.MaxX * mde.Scale.X); bbs.MinX = (int)(bbs.MinX * mde.Scale.X); bbs.MaxY = (int)(bbs.MaxY * mde.Scale.Y); bbs.MinY = (int)(bbs.MinY * mde.Scale.Y); //calculate model radius mde.Model.Radius = Math.Max(Math.Max(Math.Abs(bbs.MinY), Math.Abs(bbs.MaxY)), Math.Max(Math.Abs(bbs.MinX), Math.Abs(bbs.MaxX))); }
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); }
internal static MD3LoadResult ReadMD3Model(ref BoundingBoxSizes bbs, Dictionary <int, string> skins, Stream s, Device device, int frame) { long start = s.Position; MD3LoadResult result = new MD3LoadResult(); using (var br = new BinaryReader(s, Encoding.ASCII)) { string magic = ReadString(br, 4); if (magic != "IDP3") { result.Errors = "unknown header: expected \"IDP3\", but got \"" + magic + "\""; return(result); } int modelVersion = br.ReadInt32(); if (modelVersion != 15) //MD3 version. Must be equal to 15 { result.Errors = "expected MD3 version 15, but got " + modelVersion; return(result); } s.Position += 76; int numSurfaces = br.ReadInt32(); s.Position += 12; int ofsSurfaces = br.ReadInt32(); s.Position = ofsSurfaces + start; List <int> polyIndecesList = new List <int>(); List <WorldVertex> vertList = new List <WorldVertex>(); Dictionary <string, List <List <int> > > polyIndecesListsPerTexture = new Dictionary <string, List <List <int> > >(StringComparer.Ordinal); Dictionary <string, List <WorldVertex> > vertListsPerTexture = new Dictionary <string, List <WorldVertex> >(StringComparer.Ordinal); Dictionary <string, List <int> > vertexOffsets = new Dictionary <string, List <int> >(StringComparer.Ordinal); bool useskins = false; for (int c = 0; c < numSurfaces; c++) { string skin = ""; string error = ReadSurface(ref bbs, ref skin, br, polyIndecesList, vertList, frame); if (!string.IsNullOrEmpty(error)) { result.Errors = error; return(result); } // Pick a skin to use if (skins == null) { // skins is null when Skin MODELDEF property is set skin = string.Empty; } else if (skins.ContainsKey(c)) { // Overrtide surface skin with SurfaceSkin MODELDEF property skin = skins[c]; } if (!string.IsNullOrEmpty(skin)) { useskins = true; if (polyIndecesListsPerTexture.ContainsKey(skin)) { polyIndecesListsPerTexture[skin].Add(polyIndecesList); vertListsPerTexture[skin].AddRange(vertList.ToArray()); vertexOffsets[skin].Add(vertList.Count); } else { polyIndecesListsPerTexture.Add(skin, new List <List <int> > { polyIndecesList }); vertListsPerTexture.Add(skin, vertList); vertexOffsets.Add(skin, new List <int> { vertList.Count }); } //reset lists polyIndecesList = new List <int>(); vertList = new List <WorldVertex>(); } } if (!useskins) { //create mesh CreateMesh(device, ref result, vertList, polyIndecesList); result.Skins.Add(""); } else { //create a mesh for each surface texture foreach (KeyValuePair <string, List <List <int> > > group in polyIndecesListsPerTexture) { polyIndecesList = new List <int>(); int offset = 0; //collect indices, fix vertex offsets for (int i = 0; i < group.Value.Count; i++) { if (i > 0) { //TODO: Damn I need to rewrite all of this stuff from scratch... offset += vertexOffsets[group.Key][i - 1]; for (int c = 0; c < group.Value[i].Count; c++) { group.Value[i][c] += offset; } } polyIndecesList.AddRange(group.Value[i].ToArray()); } CreateMesh(device, ref result, vertListsPerTexture[group.Key], polyIndecesList); result.Skins.Add(group.Key.ToLowerInvariant()); } } } return(result); }