/// <summary> /// Read the material data from the specified stream. /// </summary> /// <param name="reader">The reader.</param> /// <param name="path">The path. This can be null - it is only used for recording diagnostic information.</param> /// <param name="loadTextureImages">if set to <c>true</c> [load texture images].</param> /// <returns> /// The results of the file load. /// </returns> private static FileLoadResult <List <Material> > Read(TextReader reader, string path, bool loadTextureImages) { // The model we are loading is a list of materials. During loading, we'll keep // track of messages that may be useful to consumers. var materials = new List <Material>(); var messages = new List <Message>(); // As we load, we're enriching the data of a Material object. Material currentMaterial = null; // Go through each line, keeping track of the line number. var lineNumberCounter = 0; string line; while ((line = reader.ReadLine()) != null) { ++lineNumberCounter; // Strip any comments from the line and skip empty lines. line = LineData.StripComments(line); if (string.IsNullOrWhiteSpace(line)) { continue; } // Try and read the line type and data. string lineType, lineData; if (LineData.TryReadLineType(line, out lineType, out lineData) == false) { continue; } if (lineType.IsLineType(LineTypeNewMaterial)) { // Add a new material to the list, store it as the current one and set the name. currentMaterial = new Material { Name = lineData }; materials.Add(currentMaterial); } else if (currentMaterial != null) { if (lineType.IsLineType(LineTypeMaterialAmbient)) { currentMaterial.Ambient = ReadColour(lineData); } else if (lineType.IsLineType(LineTypeMaterialDiffuse)) { currentMaterial.Diffuse = ReadColour(lineData); } else if (lineType.IsLineType(LineTypeMaterialSpecular)) { currentMaterial.Specular = ReadColour(lineData); } else if (lineType.IsLineType(LineTypeMaterialShininess)) { currentMaterial.Shininess = float.Parse(lineData); } else if (lineType.IsLineType(LineTypeOpticalDensity)) { currentMaterial.OpticalDensity = float.Parse(lineData); } else if (lineType.IsLineType(LineTypeBumpStrength)) { currentMaterial.BumpStrength = float.Parse(lineData); } else if (lineType.IsLineType(LineTypeTextureMapAmbient)) { currentMaterial.TextureMapAmbient = ReadTextureMap(path, lineNumberCounter, messages, lineData, loadTextureImages); } else if (lineType.IsLineType(LineTypeTextureMapDiffuse)) { currentMaterial.TextureMapDiffuse = ReadTextureMap(path, lineNumberCounter, messages, lineData, loadTextureImages); } else if (lineType.IsLineType(LineTypeTextureMapSpecular)) { currentMaterial.TextureMapSpecular = ReadTextureMap(path, lineNumberCounter, messages, lineData, loadTextureImages); } else if (lineType.IsLineType(LineTypeTextureMapSpecularHighlight)) { currentMaterial.TextureMapSpecularHighlight = ReadTextureMap(path, lineNumberCounter, messages, lineData, loadTextureImages); } else if (lineType.IsLineType(LineTypeTextureMapAlpha)) { currentMaterial.TextureMapAlpha = ReadTextureMap(path, lineNumberCounter, messages, lineData, loadTextureImages); } else if (lineType.IsLineType(LineTypeTextureMapBump)) { currentMaterial.TextureMapBump = ReadTextureMap(path, lineNumberCounter, messages, lineData, loadTextureImages); } else if (lineType.IsLineType(LineTypeDissolve) || lineType.IsLineType(LineTypeTransparent)) { // Read the transparency. currentMaterial.Transparency = float.Parse(lineData); } else if (lineType.IsLineType(LineTypeIlluminationModel)) { currentMaterial.IlluminationModel = int.Parse(lineData); } else if (lineType.IsLineType("Tf")) { currentMaterial.TransmissionFilter = ReadColour(lineData); } else { // Anything we encounter here we don't understand. messages.Add(new Message(MessageType.Warning, path, lineNumberCounter, string.Format("Skipped unknown line type '{0}'.", lineType))); } } else { // Anything we encounter here we don't understand. messages.Add(new Message(MessageType.Warning, path, lineNumberCounter, string.Format("Skipped unknown or out of context line type '{0}'.", lineType))); } } // Return the model and messages as a file load result. return(new FileLoadResult <List <Material> >(materials, messages)); }
/// <summary> /// Internally used to reads the scene. /// </summary> /// <param name="reader">The reader.</param> /// <param name="path">The path.</param> /// <param name="loadTextureImages">if set to <c>true</c> [load texture images].</param> /// <returns> /// The file load result. /// </returns> private static FileLoadResult <Scene> ReadScene(StreamReader reader, string path, bool loadTextureImages) { // Keep track of messages and the raw data we will use to build a scene. var messages = new List <Message>(); var uvs = new List <Vector2>(); var normals = new List <Vector3>(); var vertices = new List <Vector3>(); var interimFaces = new List <InterimFace>(); var materials = new List <Material>(); var groups = new List <Group>(); string objectName = null; // State changing data is loaded as we go through the file - once loaded, state changing // data applies to all subsequent elements until it is explicitly changed by introducing // new state changing data. Group currentGroup = null; string currentMaterialName = null; // Read line by line. string line; int lineNumberCounter = 0; while ((line = reader.ReadLine()) != null) { ++lineNumberCounter; // Strip any comments from the line and skip empty lines. line = LineData.StripComments(line); if (string.IsNullOrWhiteSpace(line)) { continue; } // Try and read the line type and data. string lineType, lineData; if (LineData.TryReadLineType(line, out lineType, out lineData) == false) { continue; } // Read texture coordinates. if (lineType.IsLineType(LineTypeTextureCoordinate)) { try { // Split the line data into texture coordinates. var dataStrings = lineData.Split(dataSeparators, StringSplitOptions.RemoveEmptyEntries); // Add the UV. uvs.Add(new Vector2 { X = float.Parse(dataStrings[0]), Y = float.Parse(dataStrings[1]) }); } catch (Exception exception) { messages.Add(new Message(MessageType.Error, path, lineNumberCounter, "There was an error reading the texture coordinate data.", exception)); } } else if (lineType.IsLineType(LineTypeNormalCoordinate)) { try { // Split the line data into normal coordinates. var dataStrings = lineData.Split(dataSeparators, StringSplitOptions.RemoveEmptyEntries); normals.Add(new Vector3 { X = float.Parse(dataStrings[0]), Y = float.Parse(dataStrings[1]), Z = float.Parse(dataStrings[2]) }); } catch (Exception exception) { messages.Add(new Message(MessageType.Error, path, lineNumberCounter, "There was an error reading the normal data.", exception)); } } else if (lineType.IsLineType(LineTypeVertex)) { try { // Split the line data into vertex coordinates. var dataStrings = lineData.Split(dataSeparators, StringSplitOptions.RemoveEmptyEntries); vertices.Add(new Vector3 { X = float.Parse(dataStrings[0]), Y = float.Parse(dataStrings[1]), Z = float.Parse(dataStrings[2]) }); } catch (Exception exception) { messages.Add(new Message(MessageType.Error, path, lineNumberCounter, "There was an error reading the vertex data.", exception)); } } else if (lineType.IsLineType(LineTypeFace)) { try { var indices = new List <Index>(); // Split the line data into index strings. var indexStrings = lineData.Split(dataSeparators, StringSplitOptions.RemoveEmptyEntries); foreach (var indexString in indexStrings) { // Split the parts. var parts = indexString.Split(new[] { '/' }, StringSplitOptions.None); var vertex = MapIndex(vertices.Count, int.Parse(parts[0])); var uv = (parts.Length > 1 && parts[1].Length > 0) ? (int?)MapIndex(uvs.Count, int.Parse(parts[1])) : null; var normal = (parts.Length > 2 && parts[2].Length > 0) ? (int?)MapIndex(normals.Count, int.Parse(parts[2])) : null; indices.Add(new Index { Vertex = vertex, Uv = uv, Normal = normal }); } interimFaces.Add(new InterimFace { materialName = currentMaterialName, indices = indices, @group = currentGroup }); } catch (Exception exception) { messages.Add(new Message(MessageType.Error, path, lineNumberCounter, "There was an error reading the index data.", exception)); } } else if (lineType.IsLineType(LineTypeMaterialLibrary)) { // The material file path is the line data. var materialPath = lineData; // If the path is relative, make it absolute based on the current directory (if we've been passed a path). if (Path.IsPathRooted(lineData) == false && path != null) { materialPath = Path.Combine(Path.GetDirectoryName(path), materialPath); } // Read the material file. try { var fileLoadResult = FileFormatMtl.Load(GD, materialPath, loadTextureImages); materials.AddRange(fileLoadResult.Model); messages.AddRange(fileLoadResult.Messages); } catch (Exception exception) { messages.Add(new Message(MessageType.Error, path, lineNumberCounter, string.Format("Failed to load material file '{0}'.", materialPath), exception)); } } else if (lineType.IsLineType(LineTypeUseMaterial)) { // The material name is simply the line data. currentMaterialName = lineData; } else if (lineType.IsLineType(LineTypeGroup)) { // Create a new group. var groupNames = lineData.Split(dataSeparators, StringSplitOptions.RemoveEmptyEntries); currentGroup = new Group(groupNames); groups.Add(currentGroup); } else if (lineType.IsLineType(LineTypeSmoothingGroup)) { // If we have no current group, we cannot set a smoothing group. if (currentGroup == null) { messages.Add(new Message(MessageType.Warning, path, lineNumberCounter, string.Format("Cannot set smoothing group '{0}' as the current context has no group.", lineData))); } else { // The smoothing group is an int, if we can get it. int smoothingGroup; if (int.TryParse(lineData, out smoothingGroup)) { currentGroup.SetSmoothingGroup(smoothingGroup); } currentGroup.SetSmoothingGroup(null); } } else if (lineType.IsLineType(LineTypeObjectName)) { // Set the object name, warning if it's already set. if (objectName != null) { messages.Add(new Message(MessageType.Warning, path, lineNumberCounter, string.Format("An object name statement to set the name to '{0}' will overwrite the current object name '{1}'.", lineData, objectName))); } objectName = lineData; } else { messages.Add(new Message(MessageType.Warning, path, lineNumberCounter, string.Format("Skipped unknown line type '{0}'.", lineType))); } } // Currently we don't have faces, just indexes and material names. But now that we've loaded // the entire file, we can map the material names to the actual materials. var ungroupedFaces = new List <Face>(); foreach (var interimFace in interimFaces) { // If we have a material named but not in the set of materials, warn. var material = materials.FirstOrDefault(m => m.Name == interimFace.materialName); if (material == null) { messages.Add(new Message(MessageType.Warning, path, lineNumberCounter, string.Format("Material '{0}' is referenced for a face, but not included in any material files.", interimFace.materialName))); } // If the face is grouped, add it to the group. Otherwise add it to the ungrouped faces. var face = new Face(material, interimFace.indices); if (interimFace.group != null) { interimFace.group.AddFace(face); } else { ungroupedFaces.Add(face); } } return(new FileLoadResult <Scene>(new Scene(vertices, uvs, normals, ungroupedFaces, groups, materials, objectName), messages)); }