public static void GenerateKulerIslandMap(List <String> files = null, int resolution = 1024, int padding = 0, string colorize = "Random", string wireframe = "None", float wirethickness = 2F) { /* Process Walkthrough * 01: - collect all vertex data from each OBJ file * 02: - sort tris into islands for colorizing * 03: - create bitmaps */ if (files == null || files.Count() == 0) { return; } // list containg all the file assets collected List <ModelAsset> Assets = new List <ModelAsset>(); /* 01 */ // loop through each file and collect the vertex data foreach (string filepath in files) { // get data from file if (filepath.ToLower().EndsWith(".obj")) { ModelAsset newAsset = ReadFileOBJ(filepath: filepath); newAsset.SourceFile = filepath; Assets.Add(newAsset); Console.WriteLine("Completed Reading OBJ File - {0}", filepath); } } /* 02 */ // loop through each assets collected data and sort into UV lands // Matt's Algorithm FindIslands(Assets); /* 03 */ // eventually condense into foreach loop above - john // Next Generate the actual bitmaps foreach (ModelAsset asset in Assets) { //Create bitmap with padding and crop afterwards to avoid pixels being placed out of place Bitmap bitmap = new Bitmap(Convert.ToInt32(resolution), Convert.ToInt32(resolution), System.Drawing.Imaging.PixelFormat.Format32bppArgb); Graphics g = Graphics.FromImage(bitmap); // make optional to use Alias or not Aliased // Set the SmoothingMode property to smooth the line. g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias; // flip image on the y. UV space in 3d apps [0,0] starts in the bottom/left, images start start top/left g.ScaleTransform(1.0F, -1.0F); // Translate the drawing area accordingly g.TranslateTransform(0.0F, -(float)resolution); // clear canvas background to be black g.Clear(Color.Black); // color each island based on user settings for (int i = 0; i < asset.Islands.Count; i++) { var island = asset.Islands[i]; // Create solid brush color based on user settings Color col = GetRandomColor(colorize); SolidBrush brush = new SolidBrush(col); // draw the original fill color foreach (Tri tri in island.TriList) { PointF A = ConvertUVSpaceToPixels(tri.posA, resolution, padding); PointF B = ConvertUVSpaceToPixels(tri.posB, resolution, padding); PointF C = ConvertUVSpaceToPixels(tri.posC, resolution, padding); PointF[] Triangle = { A, B, C }; g.FillPolygon(brush, Triangle); if (padding != 0) { Pen penPadding = new Pen(brush); penPadding.LineJoin = System.Drawing.Drawing2D.LineJoin.Round; penPadding.Width = padding; g.DrawPolygon(penPadding, Triangle); penPadding.Dispose(); } } // if we don't draw wire after the fill color it will appear broken if (wireframe != "None") { foreach (Tri tri in island.TriList) { PointF A = ConvertUVSpaceToPixels(tri.posA, resolution, padding); PointF B = ConvertUVSpaceToPixels(tri.posB, resolution, padding); PointF C = ConvertUVSpaceToPixels(tri.posC, resolution, padding); PointF[] Triangle = { A, B, C }; Pen penWire = wireframe == "White" ? new Pen(Brushes.White) : new Pen(Brushes.Black); penWire.LineJoin = System.Drawing.Drawing2D.LineJoin.Round; penWire.Width = wirethickness; g.DrawPolygon(penWire, Triangle); penWire.Dispose(); } } } // save bitmap var bmpFilepath = Path.ChangeExtension(asset.SourceFile, "_UVMap_KI.png"); string filepath = bmpFilepath; bitmap.Save(filepath, ImageFormat.Png); Console.WriteLine("Completed Image Creation - {0}", filepath); } }
// returns asset with tri list contain vert positions and corresponding indices public static ModelAsset ReadFileOBJ(string filepath = "") { // complete asset ModelAsset newAsset = new ModelAsset(); // list used to store position data from OBJ file List<PointF> positionList = new List<PointF>(); // list used to store position indices from OBJ file List<List<int>> indicesList = new List<List<int>>(); try { // Read each line of the file into a string array. Each element of the array is one line of the file. string[] lines = System.IO.File.ReadAllLines(filepath); // OBJ format: only collect valid UV Verts from lines which start with vt foreach (string line in lines) { // TVerts - positions if (line.StartsWith("vt")) { /* Example Lines 3dsMax: vt 0.0854 0.0854 0.0000 Maya: vt 0.0854 0.0854 0.0000 */ // remove leading 'vt' text from each line of the file char[] invalidChars = { 'v', 't', ' ' }; string pointString = line.TrimStart(invalidChars); // remove any leading or trailing spaces pointString = pointString.Trim(); // split each pt into it's respected parts X,Y,Z string[] parts = pointString.Split(' '); if (parts.Length >= 2) { float X = Convert.ToSingle(parts[0]); float Y = Convert.ToSingle(parts[1]); PointF pt = new PointF(X, Y); positionList.Add(pt); } } // TVert Indices if (line.StartsWith("f ")) { /* Example Lines 3dsMax: f 1/1 2/2 3/3 4/4 Maya: f 1/1/1 2/2/2 3/3/3 4/4/4 */ // remove leading 'f' text from each line of the file char[] invalidChars = { 'f', ' ' }; string pointString = line.TrimStart(invalidChars); // remove any leading or trailing spaces pointString = pointString.Trim(); // split each pt into it's respected parts A,B,C or A,B,C,D string[] parts = pointString.Split(' '); if (parts.Length >= 2) { // list containing indices collect from line of file List<int> indices = new List<int>(); foreach (string p in parts) { string[] subParts = p.Split('/'); // the second value in the 1/1/1 is the vertex id int id = Convert.ToInt32(subParts[1]); indices.Add(id); } // generate Tri object for each tri in faces // Important: offset the indices by '-1' since arrays start at 0 but indices in the OBJ files start at 1 // Matt@boomerlabs:: you should really do a -1 here and not bother later switch (indices.Count) { case 3: // single tri face Tri triA = new Tri(); triA.indexA = indices[0]-1; triA.indexB = indices[1]-1; triA.indexC = indices[2]-1; newAsset.TriList.Add(triA); break; case 4: // quad face made of two tris Tri triB = new Tri(); triB.indexA = indices[0]-1; triB.indexB = indices[1]-1; triB.indexC = indices[2]-1; newAsset.TriList.Add(triB); Tri triC = new Tri(); triC.indexA = indices[2]-1; triC.indexB = indices[3]-1; triC.indexC = indices[0]-1; newAsset.TriList.Add(triC); break; } } } } } catch (UnauthorizedAccessException) { } // if UV coordinate data exists go through and assign positions to the Tri's if (positionList.Count >= 3) { foreach (var t in newAsset.TriList) { t.posA = positionList[t.indexA]; t.posB = positionList[t.indexB]; t.posC = positionList[t.indexC]; } } // Store the number of verts newAsset.numberOfVerts = positionList.Count(); return newAsset; }
// returns asset with tri list contain vert positions and corresponding indices public static ModelAsset ReadFileOBJ(string filepath = "") { // complete asset ModelAsset newAsset = new ModelAsset(); // list used to store position data from OBJ file List <PointF> positionList = new List <PointF>(); // list used to store position indices from OBJ file List <List <int> > indicesList = new List <List <int> >(); try { // Read each line of the file into a string array. Each element of the array is one line of the file. string[] lines = System.IO.File.ReadAllLines(filepath); // OBJ format: only collect valid UV Verts from lines which start with vt foreach (string line in lines) { // TVerts - positions if (line.StartsWith("vt")) { /* Example Lines * 3dsMax: vt 0.0854 0.0854 0.0000 * Maya: vt 0.0854 0.0854 0.0000 */ // remove leading 'vt' text from each line of the file char[] invalidChars = { 'v', 't', ' ' }; string pointString = line.TrimStart(invalidChars); // remove any leading or trailing spaces pointString = pointString.Trim(); // split each pt into it's respected parts X,Y,Z string[] parts = pointString.Split(' '); if (parts.Length >= 2) { float X = Convert.ToSingle(parts[0]); float Y = Convert.ToSingle(parts[1]); PointF pt = new PointF(X, Y); positionList.Add(pt); } } // TVert Indices if (line.StartsWith("f ")) { /* Example Lines * 3dsMax: f 1/1 2/2 3/3 4/4 * Maya: f 1/1/1 2/2/2 3/3/3 4/4/4 */ // remove leading 'f' text from each line of the file char[] invalidChars = { 'f', ' ' }; string pointString = line.TrimStart(invalidChars); // remove any leading or trailing spaces pointString = pointString.Trim(); // split each pt into it's respected parts A,B,C or A,B,C,D string[] parts = pointString.Split(' '); if (parts.Length >= 2) { // list containing indices collect from line of file List <int> indices = new List <int>(); foreach (string p in parts) { string[] subParts = p.Split('/'); // the second value in the 1/1/1 is the vertex id int id = Convert.ToInt32(subParts[1]); indices.Add(id); } // generate Tri object for each tri in faces // Important: offset the indices by '-1' since arrays start at 0 but indices in the OBJ files start at 1 // Matt@boomerlabs:: you should really do a -1 here and not bother later switch (indices.Count) { case 3: // single tri face Tri triA = new Tri(); triA.indexA = indices[0] - 1; triA.indexB = indices[1] - 1; triA.indexC = indices[2] - 1; newAsset.TriList.Add(triA); break; case 4: // quad face made of two tris Tri triB = new Tri(); triB.indexA = indices[0] - 1; triB.indexB = indices[1] - 1; triB.indexC = indices[2] - 1; newAsset.TriList.Add(triB); Tri triC = new Tri(); triC.indexA = indices[2] - 1; triC.indexB = indices[3] - 1; triC.indexC = indices[0] - 1; newAsset.TriList.Add(triC); break; } } } } } catch (UnauthorizedAccessException) { } // if UV coordinate data exists go through and assign positions to the Tri's if (positionList.Count >= 3) { foreach (var t in newAsset.TriList) { t.posA = positionList[t.indexA]; t.posB = positionList[t.indexB]; t.posC = positionList[t.indexC]; } } // Store the number of verts newAsset.numberOfVerts = positionList.Count(); return(newAsset); }