/// <summary>
        /// Adds pixels adjacent to the one specified to the list of pixels needing to be checked if they meet the criteria. Used by flood fill.
        /// </summary>
        /// <param name="co">Convex Object the parent pixel belongs to.</param>
        /// <param name="x">X coordinate of pixel.</param>
        /// <param name="y">Y coordinate of pixel.</param>
        private static void CheckAdjacentPixels(ConvexObject co, int x, int y)
        {
            Vector2Int xy = new Vector2Int(x, y);

            co.AddNewIndividualPixel(xy);
            co.AddPixelAlreadyChecked(xy);
            Vector2Int[] adjacentPixelCoords = new Vector2Int[4]
            {
                new Vector2Int(x + 1, y),
                new Vector2Int(x - 1, y),
                new Vector2Int(x, y + 1),
                new Vector2Int(x, y - 1)
            };
            for (int i = 0; i < adjacentPixelCoords.Length; i++)
            {
                if (IsPixelWithinBounds(adjacentPixelCoords[i]) && co.PixelsAlreadyChecked.Contains(adjacentPixelCoords[i]) == false && co.PixelsToCheck.Contains(adjacentPixelCoords[i]) == false)
                {
                    if (IsPixelColorWithinColorDistance(adjacentPixelCoords[i], ColorType.Individual))
                    {
                        co.AddPixelToCheck(adjacentPixelCoords[i]);
                    }
                    else if (IsPixelColorWithinColorDistance(adjacentPixelCoords[i], ColorType.Separator))
                    {
                        co.AddNewEdgePixel(adjacentPixelCoords[i]);
                        co.AddPixelAlreadyChecked(adjacentPixelCoords[i]);
                    }
                }
            }
        }
 /// <summary>
 /// Runs the check adjacent pixels method on each pixel that needs to be checked. Works recursively to find all adjacent pixels for a convex object.
 /// </summary>
 /// <param name="co">Convex Object the parent pixel belongs to.</param>
 private static void FloodFill(ConvexObject co, int x, int y)
 {
     CheckAdjacentPixels(co, x, y);
     while (co.PixelsToCheck.Count > 0)
     {
         List <Vector2Int> currentPixelsToCheck = co.PixelsToCheck.ToList();
         co.ClearPixelsToCheck();
         foreach (Vector2Int pixel in currentPixelsToCheck)
         {
             CheckAdjacentPixels(co, pixel.X, pixel.Y);
         }
     }
     co.ClearPixelsAlreadyChecked();
 }
        /// <summary>
        /// Determines which tile should be parent to this object based on its center, then checks if a parent tile exists at that location, if it does then it adds
        /// this object to that tile, if not then it creates a new tile at that position and adds this object to the tile.
        /// </summary>
        /// <param name="co">The Convex Object to add to a Tile.</param>
        private static void AddToTile(ConvexObject co)
        {
            Vector2Int tilePos = co.Center.ToTilePosition();

            if (nmg.Tiles.ContainsKey(tilePos))
            {
                nmg.Tiles[tilePos].AddObject(co);
            }
            else
            {
                nmg.Tiles.Add(tilePos, new Tile(tilePos));
                nmg.Tiles[tilePos].AddObject(co);
            }
        }
        /// <summary>
        /// Loops through all pixels in CO and gets a UV coordinate for each, then calls SampleDefaultUV to get a final color to set this pixel on the bitmap.
        /// </summary>
        /// <param name="co">Convex Object to create a normal map for.</param>
        private static void CreateNormalMapForConvexObject(ConvexObject co)
        {
            List <UVPixel> uvPixelsEdge = new List <UVPixel>();
            List <UVPixel> uvPixelsInd  = new List <UVPixel>();

            if (co.EdgePixels.Count > 0)
            {
                foreach (Vector2Int pixel in co.EdgePixels)
                {
                    Vector2Int difference = pixel - co.Center;
                    Vector2    uv         = new Vector2(difference).ToUVEdge();
                    uvPixelsEdge.Add(new UVPixel(pixel, uv));
                }
                foreach (Vector2Int pixel in co.IndividualPixels)
                {
                    Vector2 interpolatedUV = new Vector2(0, 0);
                    float   sumOfWeights   = 0f;
                    foreach (UVPixel uvPixel in uvPixelsEdge)
                    {
                        float weight = 1f / Vector2Int.Distance(pixel, uvPixel.PixelCoords);
                        interpolatedUV += uvPixel.UVCoords * weight;
                        sumOfWeights   += weight;
                    }
                    interpolatedUV /= sumOfWeights;
                    uvPixelsInd.Add(new UVPixel(pixel, interpolatedUV));
                }
            }
            else
            {
                foreach (Vector2Int pixel in co.IndividualPixels)
                {
                    Vector2Int difference = pixel - co.Center;
                    Vector2    uv         = new Vector2(difference).ToUVInd(co.Width, co.Height);
                    uvPixelsInd.Add(new UVPixel(pixel, uv));
                }
            }
            List <UVPixel> allUVPixels = uvPixelsEdge.Concat(uvPixelsInd).ToList();

            foreach (UVPixel uvPixel in allUVPixels)
            {
                nmg.NormalMapImageBitmap.SetPixel(uvPixel.PixelCoords.X, uvPixel.PixelCoords.Y, GetNormalMapColor(uvPixel.UVCoords));
            }
        }
 /// <summary>
 /// Creates a normal map from a loaded bitmap image.
 /// </summary>
 /// <param name="progressLabelText">Reference to progress label.</param>
 /// <param name="progressBarValue">Reference to progress bar.</param>
 /// <param name="progressLabelDetailText">Reference to progress detail label.</param>
 public static void CreateNormalMap(IProgress <string> progressLabelText, IProgress <int> progressBarValue, IProgress <string> progressLabelDetailText)
 {
     CurrentProgress      = 0;
     MaximumPixelsToCheck = (nmg.ImageWidth * nmg.ImageHeight) + 1;
     progressLabelText.Report("In Progress...");
     try
     {
         CurrentProgress = 1;
         for (int x = 0; x < nmg.ImageWidth; x++)
         {
             for (int y = 0; y < nmg.ImageHeight; y++)
             {
                 nmg.cToken.ThrowIfCancellationRequested();
                 Color currentPixelColor = nmg.OriginalImageBitmap.GetPixel(x, y);
                 if (IsColorWithinColorDistance(currentPixelColor, ColorType.Background))
                 {
                     nmg.NormalMapImageBitmap.SetPixel(x, y, nmg.DefaultNormalMapBGColor);
                 }
                 else if (IsColorWithinColorDistance(currentPixelColor, ColorType.Separator))
                 {
                     nmg.NormalMapImageBitmap.SetPixel(x, y, nmg.DefaultNormalMapBGColor);
                 }
                 else if (IsColorWithinColorDistance(currentPixelColor, ColorType.Individual))
                 {
                     if (HasPixelAlreadyBeenAdded(x, y) == false)
                     {
                         ConvexObject co = new ConvexObject();
                         FloodFill(co, x, y);
                         co.CalculateBounds(nmg.ImageWidth, nmg.ImageHeight);
                         AddToTile(co);
                     }
                 }
                 CurrentProgress++;
             }
             progressBarValue.Report(CurrentProgress);
             float percent = (float)CurrentProgress / (float)MaximumPixelsToCheck * 100f;
             progressLabelDetailText.Report($@"{percent.ToString("00.00")}%  --- {CurrentProgress.ToString("0,0")} / {MaximumPixelsToCheck.ToString("0,0")}");
         }
         foreach (KeyValuePair <Vector2Int, Tile> tile in nmg.Tiles)
         {
             foreach (ConvexObject co in tile.Value.ConvexObjects)
             {
                 CreateNormalMapForConvexObject(co);
             }
         }
         progressLabelText.Report("Finished");
         nmg.CreatingNormalMap = false;
     }
     catch (OperationCanceledException)
     {
         MessageBox.Show($@"Operation cancelled!{Environment.NewLine}");
         nmg.CreatingNormalMap = false;
         progressLabelText.Report("Stopped");
     }
     catch (Exception e)
     {
         MessageBox.Show($@"Error creating Normal Map!{Environment.NewLine}{e.ToString()}");
         Console.WriteLine(e.ToString());
         nmg.CreatingNormalMap = false;
         progressLabelText.Report("Error!");
     }
 }
Example #6
0
 /// <summary>
 /// Adds a Convex Object to this Tile's Convex Object list.
 /// </summary>
 /// <param name="co">The Convex Object to add.</param>
 public void AddObject(ConvexObject co)
 {
     this.ConvexObjects.Add(co);
 }