public static void OptToObj(OptFile opt, string objPath, bool scale) { if (opt == null) { throw new ArgumentNullException("opt"); } string objDirectory = Path.GetDirectoryName(objPath); string objName = Path.GetFileNameWithoutExtension(objPath); var objMaterials = new ObjMaterialDictionary(); foreach (var texture in opt.Textures.Values) { var material = new ObjMaterial { Name = texture.Name, DiffuseMapFileName = string.Format(CultureInfo.InvariantCulture, "{0}.png", texture.Name) }; texture.Save(Path.Combine(objDirectory, material.DiffuseMapFileName)); if (texture.HasAlpha) { material.AlphaMapFileName = string.Format(CultureInfo.InvariantCulture, "{0}_alpha.png", texture.Name); texture.SaveAlphaMap(Path.Combine(objDirectory, material.AlphaMapFileName)); } objMaterials.Add(texture.Name, material); } objMaterials.Save(Path.ChangeExtension(objPath, "mtl")); var distances = opt.Meshes .SelectMany(t => t.Lods) .Select(t => t.Distance) .Distinct() .OrderByDescending(t => t) .ToArray(); for (int distance = 0; distance < distances.Length; distance++) { var obj = new ObjFile(); int objectsIndex = 0; int verticesIndex = 0; int verticesTexIndex = 0; int verticesNormalIndex = 0; foreach (var mesh in opt.Meshes) { var lod = mesh.Lods.FirstOrDefault(t => t.Distance <= distances[distance]); if (lod == null) { continue; } var objMesh = new ObjMesh(string.Format(CultureInfo.InvariantCulture, "{0}.{1:D3}", mesh.Descriptor.MeshType, objectsIndex)); obj.Meshes.Add(objMesh); objectsIndex++; if (scale) { foreach (var v in mesh.Vertices) { obj.Vertices.Add(new ObjVector3(v.X * OptFile.ScaleFactor, v.Z * OptFile.ScaleFactor, v.Y * OptFile.ScaleFactor)); } } else { foreach (var v in mesh.Vertices) { obj.Vertices.Add(new ObjVector3(v.X, v.Z, v.Y)); } } foreach (var v in mesh.TextureCoordinates) { obj.VertexTexCoords.Add(new ObjVector2(v.U, -v.V)); } foreach (var v in mesh.VertexNormals) { obj.VertexNormals.Add(new ObjVector3(v.X, v.Z, v.Y)); } foreach (var faceGroup in lod.FaceGroups) { var objFaceGroup = new ObjFaceGroup(); if (faceGroup.Textures.Count > 0) { objFaceGroup.MaterialName = faceGroup.Textures[0]; } objMesh.FaceGroups.Add(objFaceGroup); foreach (var face in faceGroup.Faces) { if (face.VerticesIndex.D < 0) { objFaceGroup.Faces.Add(new ObjFace() { VerticesIndex = new ObjIndex( verticesIndex + face.VerticesIndex.A, verticesIndex + face.VerticesIndex.B, verticesIndex + face.VerticesIndex.C ), VertexTexCoordsIndex = new ObjIndex( verticesTexIndex + face.TextureCoordinatesIndex.A, verticesTexIndex + face.TextureCoordinatesIndex.B, verticesTexIndex + face.TextureCoordinatesIndex.C), VertexNormalsIndex = new ObjIndex( verticesNormalIndex + face.VertexNormalsIndex.A, verticesNormalIndex + face.VertexNormalsIndex.B, verticesNormalIndex + face.VertexNormalsIndex.C) }); } else { objFaceGroup.Faces.Add(new ObjFace() { VerticesIndex = new ObjIndex( verticesIndex + face.VerticesIndex.A, verticesIndex + face.VerticesIndex.B, verticesIndex + face.VerticesIndex.C, verticesIndex + face.VerticesIndex.D ), VertexTexCoordsIndex = new ObjIndex( verticesTexIndex + face.TextureCoordinatesIndex.A, verticesTexIndex + face.TextureCoordinatesIndex.B, verticesTexIndex + face.TextureCoordinatesIndex.C, verticesTexIndex + face.TextureCoordinatesIndex.D), VertexNormalsIndex = new ObjIndex( verticesNormalIndex + face.VertexNormalsIndex.A, verticesNormalIndex + face.VertexNormalsIndex.B, verticesNormalIndex + face.VertexNormalsIndex.C, verticesNormalIndex + face.VertexNormalsIndex.D) }); } } } verticesIndex += mesh.Vertices.Count; verticesTexIndex += mesh.TextureCoordinates.Count; verticesNormalIndex += mesh.VertexNormals.Count; } obj.Save(Path.Combine(objDirectory, string.Format(CultureInfo.InvariantCulture, "{0}_{1}.obj", objName, distance)), objName); } }
public static ObjFile FromFile(string fileName) { ObjFile obj = new ObjFile(); obj.FileName = fileName; using (StreamReader file = new StreamReader(fileName)) { string line; string materialName = null; ObjMesh mesh = new ObjMesh("unnamed"); obj.Meshes.Add(mesh); ObjFaceGroup faceGroup = new ObjFaceGroup(); mesh.FaceGroups.Add(faceGroup); while ((line = file.ReadLine()) != null) { if (string.IsNullOrWhiteSpace(line)) { continue; } line = line.Trim(); if (line.StartsWith("#", StringComparison.OrdinalIgnoreCase)) { continue; } string[] values = line.Split(new[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries); switch (values[0].ToUpperInvariant()) { case "MTLLIB": if (values.Length != 2) { throw new InvalidDataException("missing mtllib name"); } if (obj.Materials.Count > 0) { throw new InvalidDataException("multiple mtllib"); } obj.Materials = ObjMaterialDictionary.FromFile(Path.Combine(Path.GetDirectoryName(fileName), values[1])); break; case "V": if (values.Length < 4) { throw new InvalidDataException("v must be x y z"); } obj.Vertices.Add(new ObjVector3( float.Parse(values[1], CultureInfo.InvariantCulture), float.Parse(values[2], CultureInfo.InvariantCulture), float.Parse(values[3], CultureInfo.InvariantCulture))); break; case "VT": if (values.Length < 3) { throw new InvalidDataException("vt must be u v"); } obj.VertexTexCoords.Add(new ObjVector2( float.Parse(values[1], CultureInfo.InvariantCulture), float.Parse(values[2], CultureInfo.InvariantCulture))); break; case "VN": if (values.Length < 4) { throw new InvalidDataException("vn must be x y z"); } obj.VertexNormals.Add(new ObjVector3( float.Parse(values[1], CultureInfo.InvariantCulture), float.Parse(values[2], CultureInfo.InvariantCulture), float.Parse(values[3], CultureInfo.InvariantCulture))); break; case "O": case "G": if (values.Length < 2) { throw new InvalidDataException("missing object name"); } if (faceGroup.Faces.Count == 0) { mesh.Name = values[1]; } else { mesh = new ObjMesh(values[1]); obj.Meshes.Add(mesh); faceGroup = new ObjFaceGroup(); faceGroup.MaterialName = materialName; mesh.FaceGroups.Add(faceGroup); } break; case "USEMTL": if (values.Length != 2) { throw new InvalidDataException("missing material name"); } materialName = values[1]; if (faceGroup.Faces.Count == 0) { faceGroup.MaterialName = materialName; } else { faceGroup = new ObjFaceGroup(); faceGroup.MaterialName = materialName; mesh.FaceGroups.Add(faceGroup); } break; case "F": if (values.Length == 4) { var v1 = ParseFaceVertex(values[1]); var v2 = ParseFaceVertex(values[2]); var v3 = ParseFaceVertex(values[3]); var face = new ObjFace(); face.VerticesIndex = new ObjIndex(v1.Item1, v2.Item1, v3.Item1); face.VertexTexCoordsIndex = new ObjIndex(v1.Item2, v2.Item2, v3.Item2); face.VertexNormalsIndex = new ObjIndex(v1.Item3, v2.Item3, v3.Item3); faceGroup.Faces.Add(face); } else if (values.Length == 5) { var v1 = ParseFaceVertex(values[1]); var v2 = ParseFaceVertex(values[2]); var v3 = ParseFaceVertex(values[3]); var v4 = ParseFaceVertex(values[4]); var face = new ObjFace(); face.VerticesIndex = new ObjIndex(v1.Item1, v2.Item1, v3.Item1, v4.Item1); face.VertexTexCoordsIndex = new ObjIndex(v1.Item2, v2.Item2, v3.Item2, v4.Item2); face.VertexNormalsIndex = new ObjIndex(v1.Item3, v2.Item3, v3.Item3, v4.Item3); faceGroup.Faces.Add(face); } else { throw new InvalidDataException("invalid face"); } break; } } } return obj; }