/// <summary> /// Rename every material from the obj and mtl to make them unique after the merge /// </summary> /// <param name="objData"></param> /// <param name="mtlData"></param> /// <param name="indexMaterial"></param> private static void SetUniqueMaterialsNames(ObjData objData, MtlData mtlData, IntWrapper indexMaterial) { // Dictionary that associate every material to a list of groups Dictionary <string, List <int> > dictMaterialGroups = ObjUtils.GetDictMaterialGroups(objData); if (mtlData == null) // Need to rename the Materials in obj only { foreach (KeyValuePair <string, List <int> > keyValue in dictMaterialGroups) { SetUniqueMaterialsName(objData, keyValue.Value, indexMaterial); indexMaterial.Value++; } } else // Need to rename the Materials in both obj and mtl { // Dictionary that associate every material of the mtl file to their index Dictionary <string, int> dictMaterialIndex = MtlUtils.GetDictMaterialIndex(mtlData); foreach (KeyValuePair <string, List <int> > keyValue in dictMaterialGroups) { SetUniqueMaterialsName(objData, keyValue.Value, indexMaterial); if (dictMaterialIndex.TryGetValue(keyValue.Key, out int index)) // We get the index of the material { MaterialMtl materialMtl = mtlData.MaterialsList[index]; if (materialMtl.NewMtl != null) { materialMtl.NewMtl = GenericUtils.MergeIndexStr(indexMaterial.Value, materialMtl.NewMtl); } } indexMaterial.Value++; } } }
/// <summary> /// Delete every unused material the obj file /// </summary> /// <param name="objData">Data parsed from the obj file</param> /// <param name="mtlData">Data parsed from the mtl file</param> public static bool DeleteUnusedMaterials(ObjData objData, MtlData mtlData) { if (mtlData == null) { return(false); } // Dictionary that associate every material to a list of groups Dictionary <string, List <int> > dictMaterialGroups = ObjUtils.GetDictMaterialGroups(objData); // Dictionary that associate every material of the mtl file to their index Dictionary <string, int> dictMaterialIndex = MtlUtils.GetDictMaterialIndex(mtlData); List <MaterialMtl> newMaterialsList = new List <MaterialMtl>(); foreach (KeyValuePair <string, List <int> > keyValue in dictMaterialGroups) { if (dictMaterialIndex.TryGetValue(keyValue.Key, out int index)) // We get the index of the material { MaterialMtl materialMtl = mtlData.MaterialsList[index]; newMaterialsList.Add(materialMtl); } } // Every material that wasnt used in a group was not added to the list mtlData.MaterialsList = newMaterialsList; return(true); }
/// <summary> /// Iterate through the mtl to update every texture associated to each material in the obj /// </summary> /// <param name="objData">Data parsed from the obj file</param> /// <param name="mtlData">Data parsed from the mtl file</param> public static void UpdateUsedTexture(ObjData objData, MtlData mtlData) { if (mtlData != null) { Dictionary <string, string> dictMaterialTexture = MtlUtils.GetDictMaterialTexture(mtlData); foreach (ObjectObj objectObj in objData.ObjectsList) { if (objectObj.MaterialName != null) { objectObj.TextureName = MtlUtils.GetTextureFromMaterial(dictMaterialTexture, objectObj.MaterialName); } } } }
private static int GetIndexEnd(List <Tuple <string, string> > listKeysValues, int length, int indexStart) { int i = indexStart + 1; // We start at the line after the newmtl while (i < length) { Tuple <string, string> keyValue = listKeysValues[i]; if (keyValue == null) // The only null KeyValue is the last one { return(i); } if (MtlUtils.IsNewMtl(keyValue.Item1)) { return(i); } i++; } return(-1); }
/// <summary> /// Parse the given mtl file /// </summary> /// <param name="file">File to parse</param> /// <returns></returns> public static MtlData ParseMtl(string file) { string[] lines = GenericUtils.RemoveBlankSpaces(FileUtilsShared.ReadFile(file)); // Read the file and remove blankspaces if (lines != null) { // Store all the other data to parse List <Tuple <string, string> > listKeysValues = GetListKeysValues(lines); MtlData mtlData = new MtlData(file); lines = null; // To let the garbage collector free the file from memory int i = 0; int length = listKeysValues.Count; while (i < length) { Tuple <string, string> keyValue = listKeysValues[i]; if (keyValue != null) // Never null unless error or final line { if (MtlUtils.IsNewMtl(keyValue.Item1)) { int materialStart = i; int materialEnd = GetIndexEnd(listKeysValues, length, materialStart); if (materialEnd != -1) { mtlData.MaterialsList.Add(MaterialData(listKeysValues, materialStart, materialEnd)); i = materialEnd; // Set to next material continue; } } } i++; } return(mtlData); } return(null); }
/// <summary> /// Create the obj and mtl files with the given data /// </summary> /// <param name="objData">Data parsed from the obj file</param> /// <param name="mtlData">Data parsed from the mtl file</param> /// <param name="objFilename">Output path</param> /// <param name="makeMtl">Create a mtl file</param> /// <param name="useExistingMtl">Use the mtlData to create the mtl file</param> /// <returns></returns> public static bool WriteObj(ObjData objData, MtlData mtlData, string objFilename, bool makeMtl, bool useExistingMtl) { // objFilename has the .obj extension string directory = System.IO.Path.GetDirectoryName(objFilename); string noExtension = System.IO.Path.GetFileNameWithoutExtension(objFilename); string mtlRelative = string.Format("{0}.mtl", noExtension); string mtlFilename = System.IO.Path.Combine(directory, mtlRelative); List <string> mtlLines = new List <string>(); // To store the lines to append to the mtl file try { using (StreamWriter obj = new StreamWriter(objFilename)) { // mtllib, o, v, vt, g, usemtl, s, f obj.WriteLine(GenericUtils.GetCreditsFile()); if (makeMtl) { obj.WriteLine(ObjUtils.GetNewMtlLib(mtlRelative)); } LocalIndexesObj indexesObj = new LocalIndexesObj(); foreach (ObjectObj objectObj in objData.ObjectsList) { if (objectObj.ObjectName != null) { obj.WriteLine(ObjUtils.GetNewObject(objectObj.ObjectName)); // o } foreach (Point p in objectObj.VerticesList) { obj.WriteLine(ObjUtils.GetNewCoord(p.ToString())); // v } foreach (UVCoordinates uv in objectObj.UVsList) { obj.WriteLine(ObjUtils.GetNewTexCoord(uv.ToString())); // vt } foreach (Vector v in objectObj.NormalsList) { obj.WriteLine(ObjUtils.GetNewVertNormal(v.ToString())); // vn } if (objectObj.GroupName != null) { obj.WriteLine(ObjUtils.GetNewGroup(objectObj.GroupName)); // g } if (objectObj.MaterialName != null) { obj.WriteLine(ObjUtils.GetNewUseMtl(objectObj.MaterialName)); // usemtl // Store lines in the mtl array to append to the file later mtlLines.Add(MtlUtils.GetMtlData(objectObj.MaterialName, objectObj.TextureName)); } if (objectObj.Smooth == -1) { obj.WriteLine(ObjUtils.GetNewSmoothGroup()); // s } else { obj.WriteLine(ObjUtils.GetNewSmoothGroup(objectObj.Smooth)); // s } foreach (VertIndexesObj vertIndexes in objectObj.VertIndexesList) { obj.WriteLine(ObjUtils.GetNewF(GetDataF(vertIndexes, indexesObj))); // f } indexesObj.vIndex += objectObj.VerticesList.Count; indexesObj.vtIndex += objectObj.UVsList.Count; indexesObj.vnIndex += objectObj.NormalsList.Count; } } } catch { return(false); } if (makeMtl) // If we dont delete materials { if (useExistingMtl && mtlData != null) // Use the parsed mtl { return(MtlExporter.WriteMtl(mtlData, mtlFilename)); } else // Create a mtl from the data obtained in the obj { return(MtlExporter.WriteMtl(mtlLines, mtlFilename)); } } return(true); }
/// <summary> /// Create the obj and mtl files with the given data /// </summary> /// <param name="wrlData">Data parsed from the wrl file</param> /// <param name="objFilename">Output file</param> /// <returns></returns> public static bool WriteObj(WrlData wrlData, string objFilename) { // objFilename has the .obj extension string directory = System.IO.Path.GetDirectoryName(objFilename); string noExtension = System.IO.Path.GetFileNameWithoutExtension(objFilename); string mtlRelative = $"{noExtension}.mtl"; string mtlFilename = System.IO.Path.Combine(directory, mtlRelative); List <string> mtlLines = new List <string>(); // To store the lines to append to the mtl file try { using (StreamWriter obj = new StreamWriter(objFilename)) { // mtllib, o, v, vt, g, usemtl, s, f obj.WriteLine(GenericUtils.GetCreditsFile()); obj.WriteLine(ObjUtils.GetNewMtlLib(mtlRelative)); int i_coordIndex = 0; int i_texCoordIndex = 0; int index = 0; foreach (Transform transform in wrlData.TransformsList) { foreach (Shape shape in transform.ShapesList) { string groupName = ""; string urlTexture = null; string diffuseColor = null; if (shape.Appearance != null) { if (shape.Appearance.Material != null) { if (shape.Appearance.Material.DiffuseColor != null) { diffuseColor = shape.Appearance.Material.DiffuseColor.ToString(); } } if (shape.Appearance.Texture != null) { urlTexture = shape.Appearance.Texture.Url; if (urlTexture != null) { groupName = ObjUtils.GetGroupName(index, urlTexture); } else { groupName = ObjUtils.GetGroupName(index); } } else { groupName = ObjUtils.GetGroupName(index); } } mtlLines.Add(MtlUtils.GetMtlData(groupName, urlTexture, diffuseColor)); obj.WriteLine(ObjUtils.GetNewObject(groupName)); // o int sizeCoord = 0; int sizeTexCoord = 0; if (shape.Geometry != null) { if (shape.Geometry.Coord != null) { sizeCoord = shape.Geometry.Coord.PointsList.Count; foreach (Point point in shape.Geometry.Coord.PointsList) { obj.WriteLine(ObjUtils.GetNewCoord(point.ToString())); // v } } if (shape.Geometry.TexCoord != null) { sizeTexCoord = shape.Geometry.TexCoord.UVList.Count; foreach (UVCoordinates uv in shape.Geometry.TexCoord.UVList) { obj.WriteLine(ObjUtils.GetNewTexCoord(uv.ToString())); // vt } } } obj.WriteLine(ObjUtils.GetNewGroup(groupName)); // g obj.WriteLine(ObjUtils.GetNewUseMtl(groupName)); // usemtl obj.WriteLine(ObjUtils.GetNewSmoothGroup()); // s if (shape.Geometry != null) { CoordIndexesWrl coordIndexes = shape.Geometry.CoordIndexes; CoordIndexesWrl texCoordIndexes = shape.Geometry.TexCoordIndexes; if (coordIndexes != null) { List <CoordIndexWrl> coordIndexesList = coordIndexes.IndexesList; if (texCoordIndexes != null) { List <CoordIndexWrl> texCoordIndexesList = texCoordIndexes.IndexesList; for (int i = 0; i < coordIndexesList.Count; i++) { string strIndexesF = GetIndexesF(coordIndexesList[i], texCoordIndexesList[i], i_coordIndex, i_texCoordIndex); obj.WriteLine(ObjUtils.GetNewF(strIndexesF)); // f } } else { foreach (CoordIndexWrl coordIndex in coordIndexesList) { string strIndexesF = GetIndexesF(coordIndex, i_coordIndex); obj.WriteLine(ObjUtils.GetNewF(strIndexesF)); // f } } } } i_coordIndex += sizeCoord; i_texCoordIndex += sizeTexCoord; index++; } } } } catch { return(false); } return(MtlExporter.WriteMtl(mtlLines, mtlFilename)); // Create the mtl file }
/// <summary> /// Create the mtl file with the given data /// </summary> /// <param name="mtlData">Data parsed from the mtl file</param> /// <param name="mtlFilename">Output path</param> /// <returns></returns> public static bool WriteMtl(MtlData mtlData, string mtlFilename) { try { using (StreamWriter mtl = new StreamWriter(mtlFilename)) { mtl.WriteLine(GenericUtils.GetCreditsFile()); // newmtl, Ns, Ka, Kd, Ks, Ni, d, illum, map_Kd, map_Ka, map_Ks, map_d foreach (MaterialMtl material in mtlData.MaterialsList) { mtl.WriteLine(MtlUtils.GetNewNewMtl(material.NewMtl)); mtl.WriteLine(MtlUtils.GetNewNs(material.Ns)); if (material.Ka != null) { mtl.WriteLine(MtlUtils.GetNewKa(material.Ka.ToString())); } else { mtl.WriteLine(MtlUtils.GetNewKa()); } if (material.Kd != null) { mtl.WriteLine(MtlUtils.GetNewKd(material.Kd.ToString())); } else { mtl.WriteLine(MtlUtils.GetNewKd()); } if (material.Ks != null) { mtl.WriteLine(MtlUtils.GetNewKs(material.Ks.ToString())); } else { mtl.WriteLine(MtlUtils.GetNewKs()); } if (material.Ke != null) { mtl.WriteLine(MtlUtils.GetNewKe(material.Ke.ToString())); } else { mtl.WriteLine(MtlUtils.GetNewKe()); } mtl.WriteLine(MtlUtils.GetNewNi(material.Ni)); mtl.WriteLine(MtlUtils.GetNewD(material.D)); mtl.WriteLine(MtlUtils.GetNewIllum(material.Illum)); if (material.MapKd != null) { mtl.WriteLine(MtlUtils.GetNewMapKd(material.MapKd)); } if (material.MapKa != null) { mtl.WriteLine(MtlUtils.GetNewMapKa(material.MapKa)); } if (material.MapKs != null) { mtl.WriteLine(MtlUtils.GetNewMapKs(material.MapKs)); } if (material.MapD != null) { mtl.WriteLine(MtlUtils.GetNewMapD(material.MapD)); } mtl.WriteLine(); // Empty line } } } catch { return(false); } return(true); }
/// <summary> /// Delete all groups that use textures from a given list (compare the textures) /// </summary> /// <param name="objData">Data parsed from the obj file</param> /// <param name="mtlData">Data parsed from the mtl file</param> /// <param name="listFileName">List of textures that should be matched</param> public static bool DeleteMatchingGroups(ObjData objData, MtlData mtlData, List <string> listFileName) { if (mtlData == null) { return(false); } List <BitmapStoreData> imgList = BitmapStoreData.GetListBitmapStoreData(listFileName); var tupleTextureMaterials = MtlUtils.GetTupleDictTextureMaterials(mtlData); // Dictionary that associate every texture to a list of materials Dictionary <string, List <int> > dictTextureMaterials = tupleTextureMaterials.Item1; // List of materials without any texture List <int> untexturedMaterials = tupleTextureMaterials.Item2; // Dictionary that associate every material to a list of groups Dictionary <string, List <int> > dictMaterialGroups = ObjUtils.GetDictMaterialGroups(objData); // Dictionary that associate every texture to a list of groups Dictionary <string, List <int> > dictTextureGroups = ObjUtils.GetDictTextureGroups(objData, mtlData, dictTextureMaterials, dictMaterialGroups); List <ObjectObj> newObjectsList = new List <ObjectObj>(); List <MaterialMtl> newMaterialsList = new List <MaterialMtl>(); string srcDir = System.IO.Path.GetDirectoryName(mtlData.FilePath); foreach (KeyValuePair <string, List <int> > keyValue in dictTextureGroups) { List <int> groups = keyValue.Value; if (groups != null) { if (groups.Count >= 1) // Should always be the case { string texturePath = keyValue.Key; if (!System.IO.Path.IsPathRooted(texturePath)) // Not an absolute path { texturePath = System.IO.Path.Combine(srcDir, texturePath); } System.Drawing.Bitmap img = ImageUtilsShared.CreateBitmap(texturePath); if (img != null) { BitmapStoreData bmpData = new BitmapStoreData(img); if (bmpData.BData != null) { if (!ImageUtilsShared.SamePictures(imgList, bmpData)) // Not the same image { // We can keep all these groups and materials foreach (int groupId in groups) { ObjectObj objectObj = objData.ObjectsList[groupId]; newObjectsList.Add(objectObj); // Insert the object in the list } if (dictTextureMaterials.TryGetValue(keyValue.Key, out List <int> materials)) { if (materials != null) { foreach (int materialId in materials) { MaterialMtl materialMtl = mtlData.MaterialsList[materialId]; newMaterialsList.Add(materialMtl); // Insert the material in the list } } } } bmpData.UnlockBits(); } } } } } foreach (int materialId in untexturedMaterials) // The untextured materials { MaterialMtl materialMtl = mtlData.MaterialsList[materialId]; newMaterialsList.Add(materialMtl); // Insert material in the list if (dictMaterialGroups.TryGetValue(materialMtl.NewMtl, out List <int> groups)) { if (groups != null) { foreach (int groupId in groups) { ObjectObj objectObj = objData.ObjectsList[groupId]; newObjectsList.Add(objectObj); // Insert the object in the list } } } } BitmapStoreData.BitmapDataUnlock(imgList); objData.ObjectsList = newObjectsList; mtlData.MaterialsList = newMaterialsList; return(true); }
/// <summary> /// Copy every texture used by the obj file /// </summary> /// <param name="mtlData">Data parsed from the mtl file</param> /// <param name="outputFolder">Where the textures will be copied</param> /// <param name="srcDir">Where the textures are located</param> /// <returns>True if successful</returns> public static bool CopyUsedTextures(MtlData mtlData, string outputFolder, string srcDir) { if (mtlData == null) { // No Mtl to read texture data return(false); } bool relative = false; if (srcDir == null) { // If a textureDirectory was not specified, the textures are found relative to the mtl file srcDir = System.IO.Path.GetDirectoryName(mtlData.FilePath); relative = true; } if (!System.IO.Directory.Exists(srcDir)) { // The source directory doesn't exist return(false); } if (!System.IO.Directory.Exists(outputFolder)) { if (!FileUtilsShared.TryCreateDirectory(outputFolder)) { // Impossible to create the destination directory return(false); } } HashSet <string> texturesList = MtlUtils.GetListTextures(mtlData); foreach (string texture in texturesList) { string srcFile; string destFile = System.IO.Path.Combine(outputFolder, System.IO.Path.GetFileName(texture)); if (relative) // The path to the texture is inside the material { if (System.IO.Path.IsPathRooted(texture)) // Absolute path to the picture { srcFile = texture; } else // Relative to the mtl { srcFile = System.IO.Path.Combine(srcDir, texture); } } else // We find the texture in a given folder { srcFile = System.IO.Path.Combine(srcDir, System.IO.Path.GetFileName(texture)); } if (!destFile.Equals(srcFile)) // Destination file is not the Source file { if (System.IO.File.Exists(srcFile)) { if (System.IO.File.Exists(destFile)) // The destination file already exists, we need to delete it { if (!FileUtilsShared.TryDeleteFile(destFile)) { continue; // Impossible to delete the file, we go to the next texture } } FileUtilsShared.TryCopyFile(srcFile, destFile); } } } return(true); }
/// <summary> /// Merge together groups that share a texture /// </summary> /// <param name="objData">Data parsed from the obj file</param> /// <param name="mtlData">Data parsed from the mtl file</param> public static bool MergeGroups(ObjData objData, MtlData mtlData) { if (mtlData == null) { return(false); } var tupleTextureMaterials = MtlUtils.GetTupleDictTextureMaterials(mtlData); // Dictionary that associate every texture to a list of materials Dictionary <string, List <int> > dictTextureMaterials = tupleTextureMaterials.Item1; // List of materials without any texture List <int> untexturedMaterials = tupleTextureMaterials.Item2; // Dictionary that associate every material to a list of groups Dictionary <string, List <int> > dictMaterialGroups = ObjUtils.GetDictMaterialGroups(objData); // Dictionary that associate every texture to a list of groups Dictionary <string, List <int> > dictTextureGroups = ObjUtils.GetDictTextureGroups(objData, mtlData, dictTextureMaterials, dictMaterialGroups); List <ObjectObj> newObjectsList = new List <ObjectObj>(); List <MaterialMtl> newMaterialsList = new List <MaterialMtl>(); foreach (KeyValuePair <string, List <int> > keyValue in dictTextureGroups) // Textured groups { // Merge the groups ObjectObj objectObj = MergeObjects(objData, keyValue.Key, keyValue.Value); newObjectsList.Add(objectObj); if (dictTextureMaterials.TryGetValue(keyValue.Key, out List <int> materials)) { if (materials != null) { // Merge the materials MaterialMtl materialMtl = MergeMaterials(mtlData, keyValue.Key, materials); newMaterialsList.Add(materialMtl); } } } foreach (int materialId in untexturedMaterials) // The untextured materials { MaterialMtl materialMtl = mtlData.MaterialsList[materialId]; newMaterialsList.Add(materialMtl); // Insert material in the list if (dictMaterialGroups.TryGetValue(materialMtl.NewMtl, out List <int> groups)) { if (groups != null) { foreach (int groupId in groups) { ObjectObj objectObj = objData.ObjectsList[groupId]; newObjectsList.Add(objectObj); // Insert the object in the list } } } } objData.ObjectsList = newObjectsList; mtlData.MaterialsList = newMaterialsList; return(true); }