unsafe IOReadResult BuildMeshes_Simple(ReadOptions options, IMeshBuilder builder) { if (vPositions.Length == 0) { return(new IOReadResult(IOCode.GarbageDataError, "No vertices in file")); } if (vTriangles.Length == 0) { return(new IOReadResult(IOCode.GarbageDataError, "No triangles in file")); } // [TODO] support non-per-vertex normals/colors bool bHaveNormals = (vNormals.Length == vPositions.Length); bool bHaveColors = (vColors.Length == vPositions.Length); bool bHaveUVs = (vUVs.Length / 2 == vPositions.Length / 3); int nVertices = vPositions.Length / 3; int[] mapV = new int[nVertices]; int meshID = builder.AppendNewMesh(bHaveNormals, bHaveColors, bHaveUVs, false); for (int k = 0; k < nVertices; ++k) { vtx_key vk = new vtx_key() { vi = k, ci = k, ni = k, ui = k }; mapV[k] = append_vertex(builder, vk, bHaveNormals, bHaveColors, bHaveUVs); } // [TODO] this doesn't handle missing vertices... for (int k = 0; k < vTriangles.Length; ++k) { append_triangle(builder, k, mapV); } if (UsedMaterials.Count == 1) // [RMS] should not be in here otherwise { int material_id = UsedMaterials.Keys.First(); string sMatName = UsedMaterials[material_id]; OBJMaterial useMat = Materials[sMatName]; int matID = builder.BuildMaterial(useMat); builder.AssignMaterial(matID, meshID); } return(new IOReadResult(IOCode.Ok, "")); }
// write .mtl file IOWriteResult write_materials(List <GenericMaterial> vMaterials, WriteOptions options) { Stream stream = OpenStreamF(options.MaterialFilePath); if (stream == null) { return(new IOWriteResult(IOCode.FileAccessError, "Could not open file " + options.MaterialFilePath + " for writing")); } try { StreamWriter w = new StreamWriter(stream); foreach (GenericMaterial gmat in vMaterials) { if (gmat is OBJMaterial == false) { continue; } OBJMaterial mat = gmat as OBJMaterial; w.WriteLine("newmtl {0}", mat.name); if (mat.Ka != GenericMaterial.Invalid) { w.WriteLine("Ka {0} {1} {2}", mat.Ka.x, mat.Ka.y, mat.Ka.z); } if (mat.Kd != GenericMaterial.Invalid) { w.WriteLine("Kd {0} {1} {2}", mat.Kd.x, mat.Kd.y, mat.Kd.z); } if (mat.Ks != GenericMaterial.Invalid) { w.WriteLine("Ks {0} {1} {2}", mat.Ks.x, mat.Ks.y, mat.Ks.z); } if (mat.Ke != GenericMaterial.Invalid) { w.WriteLine("Ke {0} {1} {2}", mat.Ke.x, mat.Ke.y, mat.Ke.z); } if (mat.Tf != GenericMaterial.Invalid) { w.WriteLine("Tf {0} {1} {2}", mat.Tf.x, mat.Tf.y, mat.Tf.z); } if (mat.d != Single.MinValue) { w.WriteLine("d {0}", mat.d); } if (mat.Ns != Single.MinValue) { w.WriteLine("Ns {0}", mat.Ns); } if (mat.Ni != Single.MinValue) { w.WriteLine("Ni {0}", mat.Ni); } if (mat.sharpness != Single.MinValue) { w.WriteLine("sharpness {0}", mat.sharpness); } if (mat.illum != -1) { w.WriteLine("illum {0}", mat.illum); } if (mat.map_Ka != null && mat.map_Ka != "") { w.WriteLine("map_Ka {0}", mat.map_Ka); } if (mat.map_Kd != null && mat.map_Kd != "") { w.WriteLine("map_Kd {0}", mat.map_Kd); } if (mat.map_Ks != null && mat.map_Ks != "") { w.WriteLine("map_Ks {0}", mat.map_Ks); } if (mat.map_Ke != null && mat.map_Ke != "") { w.WriteLine("map_Ke {0}", mat.map_Ke); } if (mat.map_d != null && mat.map_d != "") { w.WriteLine("map_d {0}", mat.map_d); } if (mat.map_Ns != null && mat.map_Ns != "") { w.WriteLine("map_Ns {0}", mat.map_Ns); } if (mat.bump != null && mat.bump != "") { w.WriteLine("bump {0}", mat.bump); } if (mat.disp != null && mat.disp != "") { w.WriteLine("disp {0}", mat.disp); } if (mat.decal != null && mat.decal != "") { w.WriteLine("decal {0}", mat.decal); } if (mat.refl != null && mat.refl != "") { w.WriteLine("refl {0}", mat.refl); } } } finally { CloseStreamF(stream); } return(IOWriteResult.Ok); }
public IOReadResult ReadMaterials(string sPath) { if (nWarningLevel >= 1) { emit_warning("[OBJReader] ReadMaterials " + sPath); } StreamReader reader; try { reader = new StreamReader(sPath); if (reader.EndOfStream) { return(new IOReadResult(IOCode.FileAccessError, "")); } } catch { return(new IOReadResult(IOCode.FileAccessError, "")); } OBJMaterial curMaterial = null; while (reader.Peek() >= 0) { string line = reader.ReadLine(); string[] tokens = line.Split((char[])null, StringSplitOptions.RemoveEmptyEntries); if (tokens.Length == 0) { continue; } if (tokens[0][0] == '#') { continue; } else if (tokens[0] == "newmtl") { curMaterial = new OBJMaterial(); curMaterial.name = tokens[1]; curMaterial.id = Materials.Count; if (Materials.ContainsKey(curMaterial.name)) { emit_warning("Material file " + sPath + " / material " + curMaterial.name + " : already exists in Material set. Replacing."); } if (nWarningLevel >= 1) { emit_warning("[OBJReader] parsing material " + curMaterial.name); } Materials[curMaterial.name] = curMaterial; } else if (tokens[0] == "Ka") { if (curMaterial != null) { curMaterial.Ka = parse_mtl_color(tokens); } } else if (tokens[0] == "Kd") { if (curMaterial != null) { curMaterial.Kd = parse_mtl_color(tokens); } } else if (tokens[0] == "Ks") { if (curMaterial != null) { curMaterial.Ks = parse_mtl_color(tokens); } } else if (tokens[0] == "Ke") { if (curMaterial != null) { curMaterial.Ke = parse_mtl_color(tokens); } } else if (tokens[0] == "Tf") { if (curMaterial != null) { curMaterial.Tf = parse_mtl_color(tokens); } } else if (tokens[0] == "illum") { if (curMaterial != null) { curMaterial.illum = int.Parse(tokens[1]); } } else if (tokens[0] == "d") { if (curMaterial != null) { curMaterial.d = Single.Parse(tokens[1]); } } else if (tokens[0] == "Tr") { // alternate to d/alpha, [Tr]ansparency is 1-d if (curMaterial != null) { curMaterial.d = 1.0f - Single.Parse(tokens[1]); } } else if (tokens[0] == "Ns") { if (curMaterial != null) { curMaterial.Ns = Single.Parse(tokens[1]); } } else if (tokens[0] == "sharpness") { if (curMaterial != null) { curMaterial.sharpness = Single.Parse(tokens[1]); } } else if (tokens[0] == "Ni") { if (curMaterial != null) { curMaterial.Ni = Single.Parse(tokens[1]); } } else if (tokens[0] == "map_Ka") { if (curMaterial != null) { curMaterial.map_Ka = parse_mtl_path(line, tokens); } } else if (tokens[0] == "map_Kd") { if (curMaterial != null) { curMaterial.map_Kd = parse_mtl_path(line, tokens); } } else if (tokens[0] == "map_Ks") { if (curMaterial != null) { curMaterial.map_Ks = parse_mtl_path(line, tokens); } } else if (tokens[0] == "map_Ke") { if (curMaterial != null) { curMaterial.map_Ke = parse_mtl_path(line, tokens); } } else if (tokens[0] == "map_d") { if (curMaterial != null) { curMaterial.map_d = parse_mtl_path(line, tokens); } } else if (tokens[0] == "map_Ns") { if (curMaterial != null) { curMaterial.map_Ns = parse_mtl_path(line, tokens); } } else if (tokens[0] == "bump" || tokens[0] == "map_bump") { if (curMaterial != null) { curMaterial.bump = parse_mtl_path(line, tokens); } } else if (tokens[0] == "disp") { if (curMaterial != null) { curMaterial.disp = parse_mtl_path(line, tokens); } } else if (tokens[0] == "decal") { if (curMaterial != null) { curMaterial.decal = parse_mtl_path(line, tokens); } } else if (tokens[0] == "refl") { if (curMaterial != null) { curMaterial.refl = parse_mtl_path(line, tokens); } } else { emit_warning("unknown material command " + tokens[0]); } } if (nWarningLevel >= 1) { emit_warning("[OBJReader] ReadMaterials completed"); } return(new IOReadResult(IOCode.Ok, "ok")); }
private void append_face(string[] tokens, OBJMaterial activeMaterial, int nActiveGroup) { int nMode = 0; if (tokens[1].IndexOf("//") != -1) { nMode = 1; } else if (tokens[1].IndexOf('/') != -1) { nMode = 2; } var t = new Triangle(); t.clear(); for (int ti = 0; ti < tokens.Length - 1; ++ti) { int j = (ti < 3) ? ti : 2; if (ti >= 3) { t.move_vertex(2, 1); } // parse next vertex if (nMode == 0) { // "f v1 v2 v3" t.set_vertex(j, parse_v(tokens[ti + 1])); } else if (nMode == 1) { // "f v1//vn1 v2//vn2 v3//vn3" string[] parts = tokens[ti + 1].Split(this.splitDoubleSlash, StringSplitOptions.RemoveEmptyEntries); t.set_vertex(j, parse_v(parts[0]), parse_n(parts[1])); } else if (nMode == 2) { string[] parts = tokens[ti + 1].Split(this.splitSlash, StringSplitOptions.RemoveEmptyEntries); if (parts.Length == 2) { // "f v1/vt1 v2/vt2 v3/vt3" t.set_vertex(j, parse_v(parts[0]), -1, parse_u(parts[1])); } else if (parts.Length == 3) { // "f v1/vt1/vn1 v2/vt2/vn2 v3/vt3/vn3" t.set_vertex(j, parse_v(parts[0]), parse_n(parts[2]), parse_u(parts[1])); } else { emit_warning("parse_triangle unexpected face component " + tokens[j]); } } // do append if (ti >= 2) { if (activeMaterial != null) { t.nMaterialID = activeMaterial.id; UsedMaterials[activeMaterial.id] = activeMaterial.name; } t.nGroupID = nActiveGroup; vTriangles.Add(t); if (t.is_complex()) { HasComplexVertices = true; } } } }
public IOReadResult ParseInput(TextReader reader, ReadOptions options) { vPositions = new DVector <double>(); vNormals = new DVector <float>(); vUVs = new DVector <float>(); vColors = new DVector <float>(); vTriangles = new DVector <Triangle>(); bool bVerticesHaveColors = false; int nMaxUVLength = 0; OBJMaterial activeMaterial = null; var GroupNames = new Dictionary <string, int>(); int nGroupCounter = 0; int nActiveGroup = Triangle.InvalidGroupID; int nLines = 0; while (reader.Peek() >= 0) { string line = reader.ReadLine(); nLines++; string[] tokens = line.Split((char[])null, StringSplitOptions.RemoveEmptyEntries); if (tokens.Length == 0) { continue; } // [RMS] this will hang VS on large models... //if (nWarningLevel >= 2) // emit_warning("Parsing line " + line); try { if (tokens[0][0] == 'v') { if (tokens[0].Length == 1) { if (tokens.Length == 7) { vPositions.Add(Double.Parse(tokens[1])); vPositions.Add(Double.Parse(tokens[2])); vPositions.Add(Double.Parse(tokens[3])); vColors.Add(float.Parse(tokens[4])); vColors.Add(float.Parse(tokens[5])); vColors.Add(float.Parse(tokens[6])); bVerticesHaveColors = true; } else if (tokens.Length >= 4) { vPositions.Add(Double.Parse(tokens[1])); vPositions.Add(Double.Parse(tokens[2])); vPositions.Add(Double.Parse(tokens[3])); } if (tokens.Length != 4 && tokens.Length != 7) { emit_warning("[OBJReader] vertex has unknown format: " + line); } } else if (tokens[0][1] == 'n') { if (tokens.Length >= 4) { vNormals.Add(float.Parse(tokens[1])); vNormals.Add(float.Parse(tokens[2])); vNormals.Add(float.Parse(tokens[3])); } if (tokens.Length != 4) { emit_warning("[OBJReader] normal has more than 3 coordinates: " + line); } } else if (tokens[0][1] == 't') { if (tokens.Length >= 3) { vUVs.Add(float.Parse(tokens[1])); vUVs.Add(float.Parse(tokens[2])); nMaxUVLength = Math.Max(nMaxUVLength, tokens.Length); } if (tokens.Length != 3) { emit_warning("[OBJReader] UV has unknown format: " + line); } } } else if (tokens[0][0] == 'f') { if (tokens.Length < 4) { emit_warning("[OBJReader] degenerate face specified : " + line); } else if (tokens.Length == 4) { var tri = new Triangle(); parse_triangle(tokens, ref tri); tri.nGroupID = nActiveGroup; if (activeMaterial != null) { tri.nMaterialID = activeMaterial.id; UsedMaterials[activeMaterial.id] = activeMaterial.name; } vTriangles.Add(tri); if (tri.is_complex()) { HasComplexVertices = true; } } else { append_face(tokens, activeMaterial, nActiveGroup); } } else if (tokens[0][0] == 'g') { string sGroupName = (tokens.Length == 2) ? tokens[1] : line.Substring(line.IndexOf(tokens[1])); if (GroupNames.ContainsKey(sGroupName)) { nActiveGroup = GroupNames[sGroupName]; } else { nActiveGroup = nGroupCounter; GroupNames[sGroupName] = nGroupCounter++; } } else if (tokens[0][0] == 'o') { // TODO multi-object support } else if (tokens[0] == "mtllib" && options.ReadMaterials) { if (MTLFileSearchPaths.Count == 0) { emit_warning("Materials requested but Material Search Paths not initialized!"); } string sMTLPathString = (tokens.Length == 2) ? tokens[1] : line.Substring(line.IndexOf(tokens[1])); string sFile = FindMTLFile(sMTLPathString); if (sFile != null) { IOReadResult result = ReadMaterials(sFile); if (result.code != IOCode.Ok) { emit_warning("error parsing " + sFile + " : " + result.message); } } else { emit_warning("material file " + sMTLPathString + " could not be found in material search paths"); } } else if (tokens[0] == "usemtl" && options.ReadMaterials) { activeMaterial = find_material(tokens[1]); } } catch (Exception e) { emit_warning("error parsing line " + nLines.ToString() + ": " + line + ", exception " + e.Message); } } m_bOBJHasPerVertexColors = bVerticesHaveColors; m_bOBJHasTriangleGroups = (nActiveGroup != Triangle.InvalidGroupID); m_nSetInvalidGroupsTo = nGroupCounter++; m_nUVComponents = nMaxUVLength; return(new IOReadResult(IOCode.Ok, "")); }
IOReadResult BuildMeshes_ByMaterial(ReadOptions options, IMeshBuilder builder) { if (vPositions.Length == 0) { return(new IOReadResult(IOCode.GarbageDataError, "No vertices in file")); } if (vTriangles.Length == 0) { return(new IOReadResult(IOCode.GarbageDataError, "No triangles in file")); } bool bHaveNormals = (vNormals.Length > 0); bool bHaveColors = (vColors.Length > 0); bool bHaveUVs = (vUVs.Length > 0); var usedMaterialIDs = new List <int>(UsedMaterials.Keys); usedMaterialIDs.Add(Triangle.InvalidMaterialID); foreach (int material_id in usedMaterialIDs) { int matID = Triangle.InvalidMaterialID; if (material_id != Triangle.InvalidMaterialID) { string sMatName = UsedMaterials[material_id]; OBJMaterial useMat = Materials[sMatName]; matID = builder.BuildMaterial(useMat); } bool bMatHaveUVs = (material_id == Triangle.InvalidMaterialID) ? false : bHaveUVs; // don't append mesh until we actually see triangles int meshID = -1; var mapV = new Dictionary <Index3i, int>(); for (int k = 0; k < vTriangles.Length; ++k) { Triangle t = vTriangles[k]; if (t.nMaterialID == material_id) { if (meshID == -1) { meshID = builder.AppendNewMesh(bHaveNormals, bHaveColors, bMatHaveUVs, false); } var t2 = new Triangle(); for (int j = 0; j < 3; ++j) { var vk = new Index3i( t.vIndices[j] - 1, t.vNormals[j] - 1, t.vUVs[j] - 1); int use_vtx = -1; if (mapV.ContainsKey(vk) == false) { use_vtx = append_vertex(builder, vk, bHaveNormals, bHaveColors, bMatHaveUVs); mapV[vk] = use_vtx; } else { use_vtx = mapV[vk]; } t2.vIndices[j] = use_vtx; } append_triangle(builder, t2); } } if (matID != Triangle.InvalidMaterialID) { builder.AssignMaterial(matID, meshID); } } return(new IOReadResult(IOCode.Ok, "")); }