private void Parse(TextReader sr) { string buf; // Read lines from the file until EOF while ((buf = sr.ReadLine()) != null) { buf = buf.Trim(); if (string.IsNullOrEmpty(buf)) continue; if (buf[0] == '#') continue; string[] split = buf.Split(s_stringDelimiters, StringSplitOptions.RemoveEmptyEntries); switch (buf[0]) { case 'v': // vertex switch (buf[1]) { case ' ': case '\t': if (split.Length != 4) // v + 3 floats throw new ApplicationException("Parse: Vertex split.Length is invalid"); m_positions.Add(float.Parse(split[1])); m_positions.Add(float.Parse(split[2])); m_positions.Add(float.Parse(split[3])); break; case 'n': if (split.Length != 4) // vn + 3 floats throw new ApplicationException("Parse: Normal split.Length is invalid"); m_normals.Add(float.Parse(split[1])); m_normals.Add(float.Parse(split[2])); m_normals.Add(float.Parse(split[3])); break; case 't': if (split.Length != 3) // vt + 2 floats throw new ApplicationException("Parse: TexCoord split.Length is invalid"); m_texcoords.Add(float.Parse(split[1])); m_texcoords.Add(float.Parse(split[2])); break; default: break; } break; case 'f': // face if (split.Length < 4) // f v/vt/vn v/vt/vn v/vt/vn ... throw new ApplicationException("Parse: Face split.Length is invalid"); m_currentFaceSet.Sizes.Add(split.Length - 1); for (int vi = 1; vi < split.Length; vi++) { string[] ptn = split[vi].Split('/'); if (ptn.Length == 3) // v/vt/vn or v//vn { m_currentFaceSet.Indices.Add(int.Parse(ptn[2]) - 1); m_currentFaceSet.HasNormals = true; if (!string.IsNullOrEmpty(ptn[1])) { m_currentFaceSet.Indices.Add(int.Parse(ptn[1]) - 1); m_currentFaceSet.HasTexCoords = true; } } else if (ptn.Length == 2) // v/vt/ { m_currentFaceSet.Indices.Add(int.Parse(ptn[1]) - 1); m_currentFaceSet.HasTexCoords = true; } m_currentFaceSet.Indices.Add(int.Parse(ptn[0]) - 1); // v or v// or v/vt/vn } break; case 'm': // material if (split.Length == 2) { var uri = new Uri(m_resolvedUri, split[1]); string fullPath = Uri.UnescapeDataString(uri.AbsolutePath); m_mtl = new MtlFile { Name = Path.GetFileName(fullPath) }; m_mtl.Read(fullPath); } break; case 'g': // group case 'o': // object if (split.Length >= 2) // g + groupName + ... { string curMatName = m_currentFaceSet.MaterialName; // Find group, otherwise create new group if (!m_groups.TryGetValue(split[1], out m_currentGroup)) { m_currentGroup = new Group { Name = split[1] }; m_groups.Add(m_currentGroup.Name, m_currentGroup); m_currentFaceSet = new FaceSet(curMatName); m_currentGroup.FaceSets.Add(curMatName, m_currentFaceSet); } // Group exists else { // Find faceset with current material, otherwise create new faceset with current material if (!m_currentGroup.FaceSets.TryGetValue(curMatName, out m_currentFaceSet)) { m_currentFaceSet = new FaceSet(curMatName); m_currentGroup.FaceSets.Add(curMatName, m_currentFaceSet); } } } break; case 'u': if (split.Length != 2) // usemtl + mat name throw new ApplicationException("Parse: Usemtl split.Length is invalid"); string matName = split[1]; // Find material by name if (m_mtl.Materials.ContainsKey(matName)) { // Find faceset with current material, otherwise create new faceset with current material if (!m_currentGroup.FaceSets.TryGetValue(matName, out m_currentFaceSet)) { m_currentFaceSet = new FaceSet(matName); m_currentGroup.FaceSets.Add(matName, m_currentFaceSet); } } else Outputs.WriteLine(OutputMessageType.Warning, "Material not found: {0}", matName); break; default: break; } } if (m_groups.Count == 0) m_groups.Add(m_currentGroup.Name, m_currentGroup); if (m_currentGroup.FaceSets.Count == 0) m_currentGroup.FaceSets[m_currentFaceSet.MaterialName] = m_currentFaceSet; // Remove empty FaceSets and mark empty Groups var delGrpList = new List<string>(); foreach (Group grp in m_groups.Values) { var delList = new List<string>(); foreach (FaceSet fs in grp.FaceSets.Values) { if (fs.Indices.Count == 0) delList.Add(fs.MaterialName); } foreach (string fsName in delList) grp.FaceSets.Remove(fsName); if (grp.FaceSets.Count == 0) delGrpList.Add(grp.Name); } // Remove empty groups foreach (string grpn in delGrpList) m_groups.Remove(grpn); }
private void Parse(TextReader sr) { string buf; // Read lines from the file until EOF while ((buf = sr.ReadLine()) != null) { buf = buf.Trim(); if (string.IsNullOrEmpty(buf)) { continue; } if (buf[0] == '#') { continue; } string[] split = buf.Split(s_stringDelimiters, StringSplitOptions.RemoveEmptyEntries); switch (buf[0]) { case 'v': // vertex switch (buf[1]) { case ' ': case '\t': if (split.Length != 4) // v + 3 floats { throw new ApplicationException("Parse: Vertex split.Length is invalid"); } m_positions.Add(float.Parse(split[1])); m_positions.Add(float.Parse(split[2])); m_positions.Add(float.Parse(split[3])); break; case 'n': if (split.Length != 4) // vn + 3 floats { throw new ApplicationException("Parse: Normal split.Length is invalid"); } m_normals.Add(float.Parse(split[1])); m_normals.Add(float.Parse(split[2])); m_normals.Add(float.Parse(split[3])); break; case 't': if (split.Length != 3) // vt + 2 floats { throw new ApplicationException("Parse: TexCoord split.Length is invalid"); } m_texcoords.Add(float.Parse(split[1])); m_texcoords.Add(float.Parse(split[2])); break; default: break; } break; case 'f': // face if (split.Length < 4) // f v/vt/vn v/vt/vn v/vt/vn ... { throw new ApplicationException("Parse: Face split.Length is invalid"); } m_currentFaceSet.Sizes.Add(split.Length - 1); for (int vi = 1; vi < split.Length; vi++) { string[] ptn = split[vi].Split('/'); if (ptn.Length == 3) // v/vt/vn or v//vn { m_currentFaceSet.Indices.Add(int.Parse(ptn[2]) - 1); m_currentFaceSet.HasNormals = true; if (!string.IsNullOrEmpty(ptn[1])) { m_currentFaceSet.Indices.Add(int.Parse(ptn[1]) - 1); m_currentFaceSet.HasTexCoords = true; } } else if (ptn.Length == 2) // v/vt/ { m_currentFaceSet.Indices.Add(int.Parse(ptn[1]) - 1); m_currentFaceSet.HasTexCoords = true; } m_currentFaceSet.Indices.Add(int.Parse(ptn[0]) - 1); // v or v// or v/vt/vn } break; case 'm': // material if (split.Length == 2) { var uri = new Uri(m_resolvedUri, split[1]); string fullPath = Uri.UnescapeDataString(uri.AbsolutePath); m_mtl = new MtlFile { Name = Path.GetFileName(fullPath) }; m_mtl.Read(fullPath); } break; case 'g': // group case 'o': // object if (split.Length >= 2) // g + groupName + ... { string curMatName = m_currentFaceSet.MaterialName; // Find group, otherwise create new group if (!m_groups.TryGetValue(split[1], out m_currentGroup)) { m_currentGroup = new Group { Name = split[1] }; m_groups.Add(m_currentGroup.Name, m_currentGroup); m_currentFaceSet = new FaceSet(curMatName); m_currentGroup.FaceSets.Add(curMatName, m_currentFaceSet); } // Group exists else { // Find faceset with current material, otherwise create new faceset with current material if (!m_currentGroup.FaceSets.TryGetValue(curMatName, out m_currentFaceSet)) { m_currentFaceSet = new FaceSet(curMatName); m_currentGroup.FaceSets.Add(curMatName, m_currentFaceSet); } } } break; case 'u': if (split.Length != 2) // usemtl + mat name { throw new ApplicationException("Parse: Usemtl split.Length is invalid"); } string matName = split[1]; // Find material by name if (m_mtl.Materials.ContainsKey(matName)) { // Find faceset with current material, otherwise create new faceset with current material if (!m_currentGroup.FaceSets.TryGetValue(matName, out m_currentFaceSet)) { m_currentFaceSet = new FaceSet(matName); m_currentGroup.FaceSets.Add(matName, m_currentFaceSet); } } else { Outputs.WriteLine(OutputMessageType.Warning, "Material not found: {0}", matName); } break; default: break; } } if (m_groups.Count == 0) { m_groups.Add(m_currentGroup.Name, m_currentGroup); } if (m_currentGroup.FaceSets.Count == 0) { m_currentGroup.FaceSets[m_currentFaceSet.MaterialName] = m_currentFaceSet; } // Remove empty FaceSets and mark empty Groups var delGrpList = new List <string>(); foreach (Group grp in m_groups.Values) { var delList = new List <string>(); foreach (FaceSet fs in grp.FaceSets.Values) { if (fs.Indices.Count == 0) { delList.Add(fs.MaterialName); } } foreach (string fsName in delList) { grp.FaceSets.Remove(fsName); } if (grp.FaceSets.Count == 0) { delGrpList.Add(grp.Name); } } // Remove empty groups foreach (string grpn in delGrpList) { m_groups.Remove(grpn); } }