// Distinguish completely transparent pixels from significant pixel by
    // "binarizing" the texture via a bit array.
    // false if a pixel is not significant (= transparent), true otherwise
    public static BinarizedImage BinarizeTexture( Texture2D a_rTextureToFilter, float a_fAlphaCutOff )
    {
        if(a_rTextureToFilter == null)
        {
            return null;
        }

        float fAlphaCutOff32 = a_fAlphaCutOff * 255;

        // Retrieve texture pixels (in 32bits format)
        // Array is flattened / pixels laid left to right, bottom to top
        Color32[ ] oTexturePixels = a_rTextureToFilter.GetPixels32( );
        int iPixelCount = oTexturePixels.Length;

        // Create (padded) sprite shape pixels array (bit array)
        BinarizedImage oBinarizedTexture = new BinarizedImage( a_rTextureToFilter.width, a_rTextureToFilter.height, false );

        // Parse all pixels
        for( int iPixelIndex = 0; iPixelIndex < iPixelCount; ++iPixelIndex )
        {
            Color32 f4Pixel = oTexturePixels[ iPixelIndex ];

            oBinarizedTexture.SetUnpaddedPixel( iPixelIndex, ( f4Pixel.a >= fAlphaCutOff32 ) );
        }

        // Fill 1px holes
        oBinarizedTexture.FillInsulatedHoles( );
        return oBinarizedTexture;
    }
    // Distinguish completely transparent pixels from significant pixel by
    // "binarizing" the texture via a bit array.
    // false if a pixel is not significant (= transparent), true otherwise
    public static BinarizedImage BinarizeTexture(Texture2D a_rTextureToFilter, float a_fAlphaCutOff)
    {
        if (a_rTextureToFilter == null)
        {
            return(null);
        }

        // float to byte
        byte iAlphaCutOff32 = (byte)(a_fAlphaCutOff * 255.0f);

        // Retrieve texture pixels (in 32bits format, faster)
        // Array is flattened / pixels laid left to right, bottom to top
        Color32[] oTexturePixels = a_rTextureToFilter.GetPixels32( );
        int       iPixelCount    = oTexturePixels.Length;

        // Create (padded) sprite shape pixels array (bit array)
        BinarizedImage oBinarizedTexture = new BinarizedImage(a_rTextureToFilter.width, a_rTextureToFilter.height, false);

        // Parse all pixels
        for (int iPixelIndex = 0; iPixelIndex < iPixelCount; ++iPixelIndex)
        {
            Color32 f4Pixel = oTexturePixels[iPixelIndex];
            oBinarizedTexture.SetUnpaddedPixel(iPixelIndex, (f4Pixel.a >= iAlphaCutOff32), f4Pixel.a / 255.0f);
        }

        // Fill 1px holes
        //oBinarizedTexture.FillInsulatedHoles( );
        return(oBinarizedTexture);
    }
    // Double the width and height of a binarized image
    public static BinarizedImage ResizeImage(BinarizedImage a_rBinarizedImage)
    {
        int iImageHeight = a_rBinarizedImage.Height;
        int iImageWidth  = a_rBinarizedImage.Width;

        int iResizedImageHeight = 2 * iImageHeight;
        int iResizedImageWidth  = 2 * iImageWidth;

        BinarizedImage oResizedBinarizedImage = new BinarizedImage(iResizedImageWidth, iResizedImageHeight, false);

        // Upsampling
        // Copy original pixels to resized sprite pixels array
        for (int iResizedY = 0; iResizedY < iResizedImageHeight; ++iResizedY)
        {
            for (int iResizedX = 0; iResizedX < iResizedImageWidth; ++iResizedX)
            {
                // Euclidian div
                int iOriginalX     = iResizedX / 2;
                int iOriginalY     = iResizedY / 2;
                int iOriginalIndex = a_rBinarizedImage.Coords2PixelIndex(iOriginalX, iOriginalY);
                int iResizedIndex  = oResizedBinarizedImage.Coords2PixelIndex(iResizedX, iResizedY);

                // Pixel copy
                oResizedBinarizedImage[iResizedIndex] = a_rBinarizedImage[iOriginalIndex];
            }
        }

        return(oResizedBinarizedImage);
    }
    // Double the width and height of a binarized image
    public static BinarizedImage ResizeImage( BinarizedImage a_rBinarizedImage )
    {
        int iImageHeight = a_rBinarizedImage.Height;
        int iImageWidth  = a_rBinarizedImage.Width;

        int iResizedImageHeight = 2 * iImageHeight;
        int iResizedImageWidth  = 2 * iImageWidth;

        BinarizedImage oResizedBinarizedImage = new BinarizedImage( iResizedImageWidth, iResizedImageHeight, false );

        // Upsampling
        // Copy original pixels to resized sprite pixels array
        for( int iResizedY = 0; iResizedY < iResizedImageHeight; ++iResizedY )
        {
            for( int iResizedX = 0; iResizedX < iResizedImageWidth; ++iResizedX )
            {
                // Euclidian div
                int iOriginalX = iResizedX / 2;
                int iOriginalY = iResizedY / 2;
                int iOriginalIndex = a_rBinarizedImage.Coords2PixelIndex( iOriginalX, iOriginalY );
                int iResizedIndex = oResizedBinarizedImage.Coords2PixelIndex( iResizedX, iResizedY );

                // Pixel copy
                oResizedBinarizedImage[ iResizedIndex ] = a_rBinarizedImage[ iOriginalIndex ];
            }
        }

        return oResizedBinarizedImage;
    }
    public static BinarizedImage DownScaleImage(BinarizedImage a_rBinarizedImage, int a_iHorizontalSubDivs, int a_iVerticalSubDivs)
    {
        int iImageWidth  = a_rBinarizedImage.Width;
        int iImageHeight = a_rBinarizedImage.Height;

        int iGridSubDivPerRow    = (a_iVerticalSubDivs + 1);
        int iGridSubDivPerColumn = (a_iHorizontalSubDivs + 1);
        int iGridSubDivCount     = iGridSubDivPerRow * iGridSubDivPerColumn;

        // Grid div dimensions
        float fGridSubDivPixelWidth  = ((float)iImageWidth) / (float)iGridSubDivPerRow;
        float fGridSubDivPixelHeight = ((float)iImageHeight) / (float)iGridSubDivPerColumn;

        BinarizedImage oResizedBinarizedImage = new BinarizedImage(iGridSubDivPerRow, iGridSubDivPerColumn, false);

        // Iterate grid divs
        for (int iGridSubDivIndex = 0; iGridSubDivIndex < iGridSubDivCount; ++iGridSubDivIndex)
        {
            // ( X, Y ) grid div pos from grid div index (in grid div space)
            int iGridSubDivX = iGridSubDivIndex % iGridSubDivPerRow;
            int iGridSubDivY = iGridSubDivIndex / iGridSubDivPerRow;

            // Compute the pixel bounds for this subdivision
            int iStartX = Mathf.FloorToInt(iGridSubDivX * fGridSubDivPixelWidth);
            int iStartY = Mathf.FloorToInt(iGridSubDivY * fGridSubDivPixelHeight);

            int iEndX = Mathf.CeilToInt((iGridSubDivX + 1) * fGridSubDivPixelWidth);
            int iEndY = Mathf.CeilToInt((iGridSubDivY + 1) * fGridSubDivPixelHeight);

            int iWidth  = iEndX - iStartX;
            int iHeight = iEndY - iStartY;

            // Set grid sub div as empty while no significant pixel found
            bool  bEmptyGridSubDiv     = true;
            float fGridSubDivAlphaMean = 0.0f;
            for (int iY = 0; iY < iHeight; ++iY)
            {
                for (int iX = 0; iX < iWidth; ++iX)
                {
                    int iPixelIndex = a_rBinarizedImage.Coords2PixelIndex(iX + iStartX, iY + iStartY);

                    // At least one pixel is significant => need to create the whole grid div
                    if (a_rBinarizedImage[iPixelIndex])
                    {
                        bEmptyGridSubDiv = false;
                    }
                    fGridSubDivAlphaMean += a_rBinarizedImage.GetAlphaValue(iPixelIndex);
                }
            }
            fGridSubDivAlphaMean /= iHeight * iWidth;

            int iResizedIndex = oResizedBinarizedImage.Coords2PixelIndex(iGridSubDivX, iGridSubDivY);
            oResizedBinarizedImage.SetInternalPixel(iResizedIndex, !bEmptyGridSubDiv, fGridSubDivAlphaMean);
        }

        return(oResizedBinarizedImage);
    }
	public static BinarizedImage DownScaleImage(BinarizedImage a_rBinarizedImage, int a_iHorizontalSubDivs, int a_iVerticalSubDivs)
	{
		int iImageWidth  = a_rBinarizedImage.Width;
		int iImageHeight = a_rBinarizedImage.Height;

		int iGridSubDivPerRow    = ( a_iVerticalSubDivs + 1 );
		int iGridSubDivPerColumn = ( a_iHorizontalSubDivs + 1 );
		int iGridSubDivCount     = iGridSubDivPerRow * iGridSubDivPerColumn;
		
		// Grid div dimensions
		float fGridSubDivPixelWidth  = ( (float) iImageWidth  ) / (float) iGridSubDivPerRow;
		float fGridSubDivPixelHeight = ( (float) iImageHeight ) / (float) iGridSubDivPerColumn;
		
		BinarizedImage oResizedBinarizedImage = new BinarizedImage( iGridSubDivPerRow, iGridSubDivPerColumn, false );
		// Iterate grid divs
		for( int iGridSubDivIndex = 0; iGridSubDivIndex < iGridSubDivCount; ++iGridSubDivIndex )
		{
			// ( X, Y ) grid div pos from grid div index (in grid div space)
			int iGridSubDivX = iGridSubDivIndex % iGridSubDivPerRow;
			int iGridSubDivY = iGridSubDivIndex / iGridSubDivPerRow;
			
			// Compute the pixel bounds for this subdivision
			int iStartX = Mathf.FloorToInt(iGridSubDivX * fGridSubDivPixelWidth);
			int iStartY = Mathf.FloorToInt(iGridSubDivY * fGridSubDivPixelHeight);
			
			int iEndX = Mathf.CeilToInt((iGridSubDivX + 1) * fGridSubDivPixelWidth);
			int iEndY = Mathf.CeilToInt((iGridSubDivY + 1) * fGridSubDivPixelHeight);
			
			int iWidth = iEndX - iStartX;
			int iHeight = iEndY - iStartY;
			
			// Set grid sub div as empty while no significant pixel found
			bool bEmptyGridSubDiv = true;
			float fGridSubDivAlphaMean = 0.0f;
			for( int iY = 0; iY < iHeight; ++iY )
			{
				for( int iX = 0; iX < iWidth; ++iX )
				{
					int iPixelIndex = a_rBinarizedImage.Coords2PixelIndex( iX + iStartX, iY + iStartY );

					// At least one pixel is significant => need to create the whole grid div
					if( a_rBinarizedImage[ iPixelIndex ] )
					{
						bEmptyGridSubDiv = false;
					}
					fGridSubDivAlphaMean += a_rBinarizedImage.GetAlphaValue(iPixelIndex);
				}
			}
			fGridSubDivAlphaMean /= iHeight * iWidth;
			
			int iResizedIndex = oResizedBinarizedImage.Coords2PixelIndex( iGridSubDivX, iGridSubDivY );
			oResizedBinarizedImage.SetInternalPixel(iResizedIndex, !bEmptyGridSubDiv, fGridSubDivAlphaMean);
		}
		
		return oResizedBinarizedImage;
	}
    public static Contour TraceContour(int a_iStartingPixelIndex, NeighborDirection a_eStartingDirection, int a_iContourLabel, BinarizedImage a_rBinarizedImage, int[] a_rLabelMap)
    {
        int iPixelIndexTrace;
        NeighborDirection eDirectionNext = a_eStartingDirection;

        FindNextPoint(a_iStartingPixelIndex, a_eStartingDirection, a_rBinarizedImage, a_rLabelMap, out iPixelIndexTrace, out eDirectionNext);
        Contour oContour = new Contour(a_iContourLabel);

        oContour.AddFirst(a_rBinarizedImage.PixelIndex2Coords(iPixelIndexTrace));
        int  iPreviousPixelIndex = a_iStartingPixelIndex;
        int  iCurrentPixelIndex  = iPixelIndexTrace;
        bool bDone = (a_iStartingPixelIndex == iPixelIndexTrace);

        // Choose a bias factor
        // The bias factor points to exterior if tracing an outer contour
        // The bias factor points to interior if tracing an inner contour
        float fOrthoBiasFactor;

        if (a_eStartingDirection == NeighborDirection.DirectionUpRight)                 // inner contour
        {
            fOrthoBiasFactor = -0.2f;
        }
        else         // outer contour
        {
            fOrthoBiasFactor = 0.2f;
        }

        while (bDone == false)
        {
            a_rLabelMap[iCurrentPixelIndex] = a_iContourLabel;
            NeighborDirection eDirectionSearch = (NeighborDirection)(((int)eDirectionNext + 6) % 8);
            int iNextPixelIndex;
            FindNextPoint(iCurrentPixelIndex, eDirectionSearch, a_rBinarizedImage, a_rLabelMap, out iNextPixelIndex, out eDirectionNext);

            iPreviousPixelIndex = iCurrentPixelIndex;
            iCurrentPixelIndex  = iNextPixelIndex;
            bDone = (iPreviousPixelIndex == a_iStartingPixelIndex && iCurrentPixelIndex == iPixelIndexTrace);

            if (bDone == false)
            {
                // Apply some bias to inner and outer contours to avoid them overlap
                // Use the orthogonal vector to direction
                NeighborDirection eOrthoBiasDirection = (NeighborDirection)(((int)eDirectionNext + 6) % 8);                       // == direction - 2 % 8 but easier to compute (always positive)
                Vector2           f2Bias = fOrthoBiasFactor * BinarizedImage.GetDirectionVector(eOrthoBiasDirection);

                // Add bias to pixel pos
                Vector2 f2BiasedPos = f2Bias + a_rBinarizedImage.PixelIndex2Coords(iNextPixelIndex);

                // Add biased pos to contour
                oContour.AddFirst(f2BiasedPos);
            }
        }

        return(oContour);
    }
	public static Contour TraceContour( int a_iStartingPixelIndex, NeighborDirection a_eStartingDirection, int a_iContourLabel, BinarizedImage a_rBinarizedImage, int[ ] a_rLabelMap )
	{
		int iPixelIndexTrace;
		NeighborDirection eDirectionNext = a_eStartingDirection;
		FindNextPoint( a_iStartingPixelIndex, a_eStartingDirection, a_rBinarizedImage, a_rLabelMap, out iPixelIndexTrace, out eDirectionNext );
		Contour oContour = new Contour( a_iContourLabel );
		oContour.AddFirst( a_rBinarizedImage.PixelIndex2Coords( iPixelIndexTrace ) );
		int iPreviousPixelIndex = a_iStartingPixelIndex;
		int iCurrentPixelIndex = iPixelIndexTrace;
		bool bDone = ( a_iStartingPixelIndex == iPixelIndexTrace );

		// Choose a bias factor
		// The bias factor points to exterior if tracing an outer contour
		// The bias factor points to interior if tracing an inner contour
		float fOrthoBiasFactor;
		if( a_eStartingDirection == NeighborDirection.DirectionUpRight )	// inner contour
		{
			fOrthoBiasFactor = -0.2f;
		}
		else // outer contour
		{
			fOrthoBiasFactor = 0.2f;
		}
		
		while( bDone == false )
		{
			a_rLabelMap[ iCurrentPixelIndex ] = a_iContourLabel;
			NeighborDirection eDirectionSearch = (NeighborDirection) ( ( (int) eDirectionNext + 6 ) % 8 );
			int iNextPixelIndex;
			FindNextPoint( iCurrentPixelIndex, eDirectionSearch, a_rBinarizedImage, a_rLabelMap, out iNextPixelIndex, out eDirectionNext );

			iPreviousPixelIndex = iCurrentPixelIndex;
			iCurrentPixelIndex = iNextPixelIndex;
			bDone = ( iPreviousPixelIndex == a_iStartingPixelIndex && iCurrentPixelIndex == iPixelIndexTrace );

			if( bDone == false )
			{
				// Apply some bias to inner and outer contours to avoid them overlap
				// Use the orthogonal vector to direction
				NeighborDirection eOrthoBiasDirection = (NeighborDirection) ( ( (int) eDirectionNext + 6 ) % 8 ); // == direction - 2 % 8 but easier to compute (always positive)
				Vector2 f2Bias = fOrthoBiasFactor * BinarizedImage.GetDirectionVector( eOrthoBiasDirection );

				// Add bias to pixel pos
				Vector2 f2BiasedPos = f2Bias + a_rBinarizedImage.PixelIndex2Coords( iNextPixelIndex );

				// Add biased pos to contour
				oContour.AddFirst( f2BiasedPos );
			}
		}

		return oContour;	
	}
    public static void FindNextPoint( int a_iStartingPixelIndex, NeighborDirection a_eStartingDirection, BinarizedImage a_rBinarizedImage, int[ ] a_rLabelMap, out int a_iNextPixelIndex, out NeighborDirection a_eNextDirection )
    {
        a_eNextDirection = a_eStartingDirection;

        // Search in all directions except initial direction (=> 7)
        for( int iSearchIteration = 0; iSearchIteration < 7; ++iSearchIteration )
        {
            a_iNextPixelIndex = a_rBinarizedImage.GetNeighborPixelIndex( a_iStartingPixelIndex, a_eNextDirection );

            if( a_rBinarizedImage[ a_iNextPixelIndex ] == false )	// Background pixel, mark it as visited
            {
                a_rLabelMap[ a_iNextPixelIndex ] = -1;	// mark as visited
                a_eNextDirection = (NeighborDirection) ( ( (int) a_eNextDirection + 1 ) % 8 );	// Next direction...
            }
            else // Non background pixel
            {
                return;
            }
        }
        a_iNextPixelIndex = a_iStartingPixelIndex;	// return starting index
    }
Exemple #10
0
    // Polygonizes a flat parametrized grid mesh from a binarized image
    public static Mesh PolygonizeGrid(BinarizedImage a_rBinarizedImage, Vector2 a_f2Scale, Vector3 a_f3PivotPoint, int a_iHorizontalSubDivs, int a_iVerticalSubDivs, bool a_bExtrude = false, float a_fExtrusionDepth = 0.0f)
    {
        BinarizedImage oDownScaledBinarizedImage = Uni2DEditorShapeExtractionUtils.DownScaleImage(a_rBinarizedImage, a_iHorizontalSubDivs, a_iVerticalSubDivs);

        Mesh oGridPlaneMesh = new Mesh( );

        // Texture dimensions
        int iTextureWidth  = a_rBinarizedImage.Width;
        int iTextureHeight = a_rBinarizedImage.Height;

        int iGridSubDivPerRow    = (a_iVerticalSubDivs + 1);
        int iGridSubDivPerColumn = (a_iHorizontalSubDivs + 1);
        int iGridSubDivCount     = iGridSubDivPerRow * iGridSubDivPerColumn;

        int iGridVertexCount = (a_iHorizontalSubDivs + 2) * (a_iVerticalSubDivs + 2);

        // Grid div dimensions
        float fGridSubDivPixelWidth  = ((float)iTextureWidth) / (float)iGridSubDivPerRow;
        float fGridSubDivPixelHeight = ((float)iTextureHeight) / (float)iGridSubDivPerColumn;

        // Vertex pos -> index dictionary: stores the index associated to a given vertex
        int iCurrentVertexIndex = 0;
        Dictionary <Vector3, int> oVertexPosIndexDict = new Dictionary <Vector3, int>(iGridVertexCount);

        // Mesh data
        List <int> oGridPlaneTriangleList = new List <int>(6 * iGridVertexCount);               // quad = 2 tris, 1 tri = 3 vertices => 2 * 3
        //List<Vector2> oGridPlaneUVList   = new List<Vector2>( iGridVertexCount );

        List <int> oOutlineSides = new List <int>();

        for (int iGridSubDivIndex = 0; iGridSubDivIndex < iGridSubDivCount; ++iGridSubDivIndex)
        {
            int iGridSubDivX = iGridSubDivIndex % iGridSubDivPerRow;
            int iGridSubDivY = iGridSubDivIndex / iGridSubDivPerRow;
            int iIndex       = oDownScaledBinarizedImage.Coords2PixelIndex(iGridSubDivX, iGridSubDivY);
            if (oDownScaledBinarizedImage[iIndex])
            {
                // ( X, Y ) grid div pos from grid div index (in grid div space)

                /*
                 * Grid div
                 *
                 *	       ...       ...
                 *	        |         |
                 *	... --- TL ------- TR --- ...
                 *	        |   T2  / |
                 *	        |     /   |
                 *	        |   /     |
                 *	        | /   T1  |
                 *	... --- BL ------- BR --- ...
                 *          |         |
                 *         ...       ...
                 */
                Vector3 f3BottomLeftVertex  = new Vector3(iGridSubDivX * fGridSubDivPixelWidth, iGridSubDivY * fGridSubDivPixelHeight, 0.0f);                                   // BL
                Vector3 f3BottomRightVertex = new Vector3((iGridSubDivX + 1) * fGridSubDivPixelWidth, iGridSubDivY * fGridSubDivPixelHeight, 0.0f);                             // BR
                Vector3 f3TopRightVertex    = new Vector3((iGridSubDivX + 1) * fGridSubDivPixelWidth, (iGridSubDivY + 1) * fGridSubDivPixelHeight, 0.0f);                       // TR
                Vector3 f3TopLeftVertex     = new Vector3(iGridSubDivX * fGridSubDivPixelWidth, (iGridSubDivY + 1) * fGridSubDivPixelHeight, 0.0f);                             // TL

                int iBottomLeftVertexIndex;
                int iBottomRightVertexIndex;
                int iTopRightVertexIndex;
                int iTopLeftVertexIndex;

                // For each grid div vertex, query its index if already created (the vertex might be shared with other grid divs).
                // If not, create it.
                if (oVertexPosIndexDict.TryGetValue(f3BottomLeftVertex, out iBottomLeftVertexIndex) == false)
                {
                    iBottomLeftVertexIndex = iCurrentVertexIndex++;
                    oVertexPosIndexDict.Add(f3BottomLeftVertex, iBottomLeftVertexIndex);
                    //oGridPlaneUVList.Add( new Vector2( f3BottomLeftVertex.x / (float) iTextureWidth, f3BottomLeftVertex.y / (float) iTextureHeight ) );
                }

                if (oVertexPosIndexDict.TryGetValue(f3BottomRightVertex, out iBottomRightVertexIndex) == false)
                {
                    iBottomRightVertexIndex = iCurrentVertexIndex++;
                    oVertexPosIndexDict.Add(f3BottomRightVertex, iBottomRightVertexIndex);
                    //oGridPlaneUVList.Add( new Vector2( f3BottomRightVertex.x / (float) iTextureWidth, f3BottomRightVertex.y / (float) iTextureHeight ) );
                }

                if (oVertexPosIndexDict.TryGetValue(f3TopRightVertex, out iTopRightVertexIndex) == false)
                {
                    iTopRightVertexIndex = iCurrentVertexIndex++;
                    oVertexPosIndexDict.Add(f3TopRightVertex, iTopRightVertexIndex);
                    //oGridPlaneUVList.Add( new Vector2( f3TopRightVertex.x / (float) iTextureWidth, f3TopRightVertex.y / (float) iTextureHeight ) );
                }

                if (oVertexPosIndexDict.TryGetValue(f3TopLeftVertex, out iTopLeftVertexIndex) == false)
                {
                    iTopLeftVertexIndex = iCurrentVertexIndex++;
                    oVertexPosIndexDict.Add(f3TopLeftVertex, iTopLeftVertexIndex);
                    //oGridPlaneUVList.Add( new Vector2( f3TopLeftVertex.x / (float) iTextureWidth, f3TopLeftVertex.y / (float) iTextureHeight ) );
                }

                // Create grid div triangles
                // First triangle (T1)
                oGridPlaneTriangleList.Add(iBottomRightVertexIndex);                    // BR
                oGridPlaneTriangleList.Add(iBottomLeftVertexIndex);                     // BL
                oGridPlaneTriangleList.Add(iTopRightVertexIndex);                       // TR

                // Second triangle (T2)
                oGridPlaneTriangleList.Add(iBottomLeftVertexIndex);                     // BL
                oGridPlaneTriangleList.Add(iTopLeftVertexIndex);                        // TL
                oGridPlaneTriangleList.Add(iTopRightVertexIndex);                       // TR

                // If we extrude check the sides to see if there are parts of the outline
                if (a_bExtrude)
                {
                    // Up
                    if (IsBinarizedImageCellEmpty(iGridSubDivX, iGridSubDivY + 1, iGridSubDivPerRow, iGridSubDivPerColumn, oDownScaledBinarizedImage))
                    {
                        oOutlineSides.Add(iTopLeftVertexIndex);
                        oOutlineSides.Add(iTopRightVertexIndex);
                    }

                    // Right
                    if (IsBinarizedImageCellEmpty(iGridSubDivX + 1, iGridSubDivY, iGridSubDivPerRow, iGridSubDivPerColumn, oDownScaledBinarizedImage))
                    {
                        oOutlineSides.Add(iTopRightVertexIndex);
                        oOutlineSides.Add(iBottomRightVertexIndex);
                    }

                    // Bottom
                    if (IsBinarizedImageCellEmpty(iGridSubDivX, iGridSubDivY - 1, iGridSubDivPerRow, iGridSubDivPerColumn, oDownScaledBinarizedImage))
                    {
                        oOutlineSides.Add(iBottomRightVertexIndex);
                        oOutlineSides.Add(iBottomLeftVertexIndex);
                    }

                    // Left
                    if (IsBinarizedImageCellEmpty(iGridSubDivX - 1, iGridSubDivY, iGridSubDivPerRow, iGridSubDivPerColumn, oDownScaledBinarizedImage))
                    {
                        oOutlineSides.Add(iBottomLeftVertexIndex);
                        oOutlineSides.Add(iTopLeftVertexIndex);
                    }
                }
            }
        }

        // Apply pivot + compute UVs
        int     i2DVertexCount  = oVertexPosIndexDict.Count;
        int     iVertexCount    = i2DVertexCount;
        Vector3 f3ExtrudeOffset = Vector3.zero;

        if (a_bExtrude)
        {
            iVertexCount    = 2 * iVertexCount;
            f3ExtrudeOffset = Vector3.forward * a_fExtrusionDepth * 0.5f;
        }
        Vector2[] oGridPlaneUVs      = new Vector2[iVertexCount];
        Vector3[] oGridPlaneVertices = new Vector3[iVertexCount];
        Vector2   f2Dimensions       = new Vector2(1.0f / (float)iTextureWidth, 1.0f / (float)iTextureHeight);

        foreach (KeyValuePair <Vector3, int> rVertexPosIndexPair in oVertexPosIndexDict)
        {
            Vector3 f3VertexPos = rVertexPosIndexPair.Key;
            int     iIndex      = rVertexPosIndexPair.Value;

            Vector2 f2UV     = new Vector2(f3VertexPos.x * f2Dimensions.x, f3VertexPos.y * f2Dimensions.y);
            Vector3 f3Vertex = (f3VertexPos - a_f3PivotPoint);
            f3Vertex.x *= a_f2Scale.x;
            f3Vertex.y *= a_f2Scale.y;

            if (a_bExtrude)
            {
                oGridPlaneUVs[iIndex]      = f2UV;
                oGridPlaneVertices[iIndex] = f3Vertex - f3ExtrudeOffset;

                oGridPlaneUVs[iIndex + i2DVertexCount]      = f2UV;
                oGridPlaneVertices[iIndex + i2DVertexCount] = f3Vertex + f3ExtrudeOffset;
            }
            else
            {
                oGridPlaneUVs[iIndex]      = f2UV;
                oGridPlaneVertices[iIndex] = f3Vertex;
            }
        }


        // Surface Triangles
        int i2DTriangleIndicesCount      = oGridPlaneTriangleList.Count;
        int iSurfaceTriangleIndicesCount = oGridPlaneTriangleList.Count;
        int iTriangleIndicesCount        = oGridPlaneTriangleList.Count;

        if (a_bExtrude)
        {
            iSurfaceTriangleIndicesCount *= 2;
            iTriangleIndicesCount         = 2 * iTriangleIndicesCount + oOutlineSides.Count * 3;
        }
        int[] oGridPlaneTriangleIndices = new int[iTriangleIndicesCount];
        for (int i = 0; i < i2DTriangleIndicesCount; ++i)
        {
            int iIndex = oGridPlaneTriangleList[i];
            oGridPlaneTriangleIndices[i] = iIndex;
            if (a_bExtrude)
            {
                oGridPlaneTriangleIndices[iSurfaceTriangleIndicesCount - 1 - i] = iIndex + i2DVertexCount;
            }
        }

        // Side Triangles
        if (a_bExtrude)
        {
            int iStartSideTriangle = iSurfaceTriangleIndicesCount;
            int iOutlineSideCount  = oOutlineSides.Count / 2;
            for (int i = 0; i < iOutlineSideCount; ++i)
            {
                int iSideFrontBegin = oOutlineSides[i * 2];
                int iSideFrontEnd   = oOutlineSides[i * 2 + 1];
                int iSideBackEnd    = iSideFrontEnd + i2DVertexCount;
                int iSideBackBegin  = iSideFrontBegin + i2DVertexCount;

                int iSideQuadStartIndex = iStartSideTriangle + i * 6;

                oGridPlaneTriangleIndices[iSideQuadStartIndex + 0] = iSideFrontBegin;
                oGridPlaneTriangleIndices[iSideQuadStartIndex + 1] = iSideBackBegin;
                oGridPlaneTriangleIndices[iSideQuadStartIndex + 2] = iSideBackEnd;

                oGridPlaneTriangleIndices[iSideQuadStartIndex + 3] = iSideFrontBegin;
                oGridPlaneTriangleIndices[iSideQuadStartIndex + 4] = iSideBackEnd;
                oGridPlaneTriangleIndices[iSideQuadStartIndex + 5] = iSideFrontEnd;
            }
        }

        oGridPlaneMesh.vertices  = oGridPlaneVertices;
        oGridPlaneMesh.uv        = oGridPlaneUVs;
        oGridPlaneMesh.triangles = oGridPlaneTriangleIndices;           // LINQ

        return(oGridPlaneMesh);
    }
Exemple #11
0
    private static bool IsBinarizedImageCellEmpty(int a_iCellX, int a_iCellY, int a_iGridWidth, int a_iGridHeight, BinarizedImage a_oBinaryGrid)
    {
        // If the cell is exterior to the grid consider it empty
        if (a_iCellX < 0 || a_iCellX >= a_iGridWidth ||
            a_iCellY < 0 || a_iCellY >= a_iGridHeight)
        {
            return(true);
        }

        return(a_oBinaryGrid[a_iCellY * a_iGridWidth + a_iCellX] == false);
    }
Exemple #12
0
    // Polygonizes a flat parametrized grid mesh from a binarized image
    public static Mesh PolygonizeGrid(BinarizedImage a_rBinarizedImage, float a_fScale, Vector3 a_f3PivotPoint, int a_iHorizontalSubDivs, int a_iVerticalSubDivs)
    {
        Mesh oGridPlaneMesh = new Mesh( );

        // Texture dimensions
        int iTextureWidth  = a_rBinarizedImage.Width;
        int iTextureHeight = a_rBinarizedImage.Height;

        int iGridSubDivPerRow    = (a_iVerticalSubDivs + 1);
        int iGridSubDivPerColumn = (a_iHorizontalSubDivs + 1);
        int iGridSubDivCount     = iGridSubDivPerRow * iGridSubDivPerColumn;

        int iGridVertexCount = (a_iHorizontalSubDivs + 2) * (a_iVerticalSubDivs + 2);

        // Grid div dimensions
        float fGridSubDivPixelWidth  = ((float)iTextureWidth) / (float)iGridSubDivPerRow;
        float fGridSubDivPixelHeight = ((float)iTextureHeight) / (float)iGridSubDivPerColumn;

        // Also store these values as ints
        int iGridSubDivPixelWidth  = Mathf.FloorToInt(fGridSubDivPixelWidth);
        int iGridSubDivPixelHeight = Mathf.FloorToInt(fGridSubDivPixelHeight);

        // Vertex pos -> index dictionary: stores the index associated to a given vertex
        int iCurrentVertexIndex = 0;
        Dictionary <Vector3, int> oVertexPosIndexDict = new Dictionary <Vector3, int>(iGridVertexCount);

        // Mesh data
        List <int> oGridPlaneTriangleList = new List <int>(6 * iGridVertexCount);               // quad = 2 tris, 1 tri = 3 vertices => 2 * 3

        //List<Vector2> oGridPlaneUVList   = new List<Vector2>( iGridVertexCount );

        // Iterate grid divs
        for (int iGridSubDivIndex = 0; iGridSubDivIndex < iGridSubDivCount; ++iGridSubDivIndex)
        {
            // ( X, Y ) grid div pos from grid div index (in grid div space)
            int iGridSubDivX = iGridSubDivIndex % iGridSubDivPerRow;
            int iGridSubDivY = iGridSubDivIndex / iGridSubDivPerRow;

            // Set grid sub div as empty while no significant pixel found
            bool bEmptyGridSubDiv = true;
            for (int iY = 0, iYLimit = iGridSubDivPixelHeight; bEmptyGridSubDiv && iY < iYLimit; ++iY)
            {
                for (int iX = 0, iXLimit = iGridSubDivPixelWidth; bEmptyGridSubDiv && iX < iXLimit; ++iX)
                {
                    int iPixelIndex = a_rBinarizedImage.Coords2PixelIndex(iX + Mathf.FloorToInt(iGridSubDivX * fGridSubDivPixelWidth), iY + Mathf.FloorToInt(iGridSubDivY * fGridSubDivPixelHeight));
                    //int iPixelIndex = Mathf.FloorToInt( iGridSubDivY * fGridSubDivPixelHeight ) * iTextureWidth + Mathf.FloorToInt( iGridSubDivX * fGridSubDivPixelWidth ) + iY * iTextureWidth + iX;
                    //bEmptyGridSubDiv = a_rBinarizedImage[ iPixelIndex ];

                    // At least one pixel is significant => need to create the whole grid div
                    if (a_rBinarizedImage[iPixelIndex])
                    {
                        /*
                         * Grid div
                         *
                         *	       ...       ...
                         *	        |         |
                         *	... --- TL ------- TR --- ...
                         *	        |   T2  / |
                         *	        |     /   |
                         *	        |   /     |
                         *	        | /   T1  |
                         *	... --- BL ------- BR --- ...
                         *          |         |
                         *         ...       ...
                         */
                        Vector3 f3BottomLeftVertex  = new Vector3(iGridSubDivX * fGridSubDivPixelWidth, iGridSubDivY * fGridSubDivPixelHeight, 0.0f);                                           // BL
                        Vector3 f3BottomRightVertex = new Vector3((iGridSubDivX + 1) * fGridSubDivPixelWidth, iGridSubDivY * fGridSubDivPixelHeight, 0.0f);                                     // BR
                        Vector3 f3TopRightVertex    = new Vector3((iGridSubDivX + 1) * fGridSubDivPixelWidth, (iGridSubDivY + 1) * fGridSubDivPixelHeight, 0.0f);                               // TR
                        Vector3 f3TopLeftVertex     = new Vector3(iGridSubDivX * fGridSubDivPixelWidth, (iGridSubDivY + 1) * fGridSubDivPixelHeight, 0.0f);                                     // TL

                        int iBottomLeftVertexIndex;
                        int iBottomRightVertexIndex;
                        int iTopRightVertexIndex;
                        int iTopLeftVertexIndex;

                        // For each grid div vertex, query its index if already created (the vertex might be shared with other grid divs).
                        // If not, create it.
                        if (oVertexPosIndexDict.TryGetValue(f3BottomLeftVertex, out iBottomLeftVertexIndex) == false)
                        {
                            iBottomLeftVertexIndex = iCurrentVertexIndex++;
                            oVertexPosIndexDict.Add(f3BottomLeftVertex, iBottomLeftVertexIndex);
                            //oGridPlaneUVList.Add( new Vector2( f3BottomLeftVertex.x / (float) iTextureWidth, f3BottomLeftVertex.y / (float) iTextureHeight ) );
                        }

                        if (oVertexPosIndexDict.TryGetValue(f3BottomRightVertex, out iBottomRightVertexIndex) == false)
                        {
                            iBottomRightVertexIndex = iCurrentVertexIndex++;
                            oVertexPosIndexDict.Add(f3BottomRightVertex, iBottomRightVertexIndex);
                            //oGridPlaneUVList.Add( new Vector2( f3BottomRightVertex.x / (float) iTextureWidth, f3BottomRightVertex.y / (float) iTextureHeight ) );
                        }

                        if (oVertexPosIndexDict.TryGetValue(f3TopRightVertex, out iTopRightVertexIndex) == false)
                        {
                            iTopRightVertexIndex = iCurrentVertexIndex++;
                            oVertexPosIndexDict.Add(f3TopRightVertex, iTopRightVertexIndex);
                            //oGridPlaneUVList.Add( new Vector2( f3TopRightVertex.x / (float) iTextureWidth, f3TopRightVertex.y / (float) iTextureHeight ) );
                        }

                        if (oVertexPosIndexDict.TryGetValue(f3TopLeftVertex, out iTopLeftVertexIndex) == false)
                        {
                            iTopLeftVertexIndex = iCurrentVertexIndex++;
                            oVertexPosIndexDict.Add(f3TopLeftVertex, iTopLeftVertexIndex);
                            //oGridPlaneUVList.Add( new Vector2( f3TopLeftVertex.x / (float) iTextureWidth, f3TopLeftVertex.y / (float) iTextureHeight ) );
                        }

                        // Create grid div triangles
                        // First triangle (T1)
                        oGridPlaneTriangleList.Add(iBottomRightVertexIndex);                            // BR
                        oGridPlaneTriangleList.Add(iBottomLeftVertexIndex);                             // BL
                        oGridPlaneTriangleList.Add(iTopRightVertexIndex);                               // TR

                        // Second triangle (T2)
                        oGridPlaneTriangleList.Add(iBottomLeftVertexIndex);                             // BL
                        oGridPlaneTriangleList.Add(iTopLeftVertexIndex);                                // TL
                        oGridPlaneTriangleList.Add(iTopRightVertexIndex);                               // TR

                        // Set grid sub div as not empty
                        bEmptyGridSubDiv = false;
                    }
                }
            }
        }

        // Apply pivot + compute UVs
        int iVertexCount = oVertexPosIndexDict.Count;

        Vector2[] oGridPlaneUVs      = new Vector2[iVertexCount];
        Vector3[] oGridPlaneVertices = new Vector3[iVertexCount];
        Vector2   f2Dimensions       = new Vector2(1.0f / (float)iTextureWidth, 1.0f / (float)iTextureHeight);

        foreach (KeyValuePair <Vector3, int> rVertexPosIndexPair in oVertexPosIndexDict)
        {
            Vector3 f3VertexPos = rVertexPosIndexPair.Key;
            int     iIndex      = rVertexPosIndexPair.Value;

            oGridPlaneUVs[iIndex]      = new Vector2(f3VertexPos.x * f2Dimensions.x, f3VertexPos.y * f2Dimensions.y);
            oGridPlaneVertices[iIndex] = (f3VertexPos - a_f3PivotPoint) * a_fScale;
        }

        oGridPlaneMesh.vertices  = oGridPlaneVertices;
        oGridPlaneMesh.uv        = oGridPlaneUVs;
        oGridPlaneMesh.triangles = oGridPlaneTriangleList.ToArray( );           // LINQ

        return(oGridPlaneMesh);
    }
	// Polygonizes a flat parametrized grid mesh from a binarized image
	public static Mesh PolygonizeGrid( BinarizedImage a_rBinarizedImage, float a_fScale, Vector3 a_f3PivotPoint, int a_iHorizontalSubDivs, int a_iVerticalSubDivs )
	{
		Mesh oGridPlaneMesh = new Mesh( );

		// Texture dimensions
		int iTextureWidth  = a_rBinarizedImage.Width;
		int iTextureHeight = a_rBinarizedImage.Height;

		int iGridSubDivPerRow    = ( a_iVerticalSubDivs + 1 );
		int iGridSubDivPerColumn = ( a_iHorizontalSubDivs + 1 );
		int iGridSubDivCount     = iGridSubDivPerRow * iGridSubDivPerColumn;

		int iGridVertexCount = ( a_iHorizontalSubDivs + 2 ) * ( a_iVerticalSubDivs + 2 );
		
		// Grid div dimensions
		float fGridSubDivPixelWidth  = ( (float) iTextureWidth  ) / (float) iGridSubDivPerRow;
		float fGridSubDivPixelHeight = ( (float) iTextureHeight ) / (float) iGridSubDivPerColumn;

		// Vertex pos -> index dictionary: stores the index associated to a given vertex
		int iCurrentVertexIndex = 0;
		Dictionary<Vector3,int> oVertexPosIndexDict = new Dictionary<Vector3, int>( iGridVertexCount );

		// Mesh data
		List<int> oGridPlaneTriangleList = new List<int>( 6 * iGridVertexCount );	// quad = 2 tris, 1 tri = 3 vertices => 2 * 3
		//List<Vector2> oGridPlaneUVList   = new List<Vector2>( iGridVertexCount );

		// Iterate grid divs
		for( int iGridSubDivIndex = 0; iGridSubDivIndex < iGridSubDivCount; ++iGridSubDivIndex )
		{
			// ( X, Y ) grid div pos from grid div index (in grid div space)
			int iGridSubDivX = iGridSubDivIndex % iGridSubDivPerRow;
			int iGridSubDivY = iGridSubDivIndex / iGridSubDivPerRow;
			
			// Compute the pixel bounds for this subdivision
			int iStartX = Mathf.FloorToInt(iGridSubDivX * fGridSubDivPixelWidth);
			int iStartY = Mathf.FloorToInt(iGridSubDivY * fGridSubDivPixelHeight);
			
			int iEndX = Mathf.CeilToInt((iGridSubDivX + 1) * fGridSubDivPixelWidth);
			int iEndY = Mathf.CeilToInt((iGridSubDivY + 1) * fGridSubDivPixelHeight);
			
			int iWidth = iEndX - iStartX;
			int iHeight = iEndY - iStartY;
			
			// Set grid sub div as empty while no significant pixel found
			bool bEmptyGridSubDiv = true;
			for( int iY = 0; bEmptyGridSubDiv && iY < iHeight; ++iY )
			{
				for( int iX = 0; bEmptyGridSubDiv && iX < iWidth; ++iX )
				{
					int iPixelIndex = a_rBinarizedImage.Coords2PixelIndex( iX + iStartX, iY + iStartY );
					//int iPixelIndex = Mathf.FloorToInt( iGridSubDivY * fGridSubDivPixelHeight ) * iTextureWidth + Mathf.FloorToInt( iGridSubDivX * fGridSubDivPixelWidth ) + iY * iTextureWidth + iX;
					//bEmptyGridSubDiv = a_rBinarizedImage[ iPixelIndex ];

					// At least one pixel is significant => need to create the whole grid div
					if( a_rBinarizedImage[ iPixelIndex ] )
					{
						/*
						 * Grid div
						 * 
						 *	       ...       ...
						 *	        |         |
						 *	... --- TL ------- TR --- ...
						 *	        |   T2  / |
						 *	        |     /   |
						 *	        |   /     |
						 *	        | /   T1  |
						 *	... --- BL ------- BR --- ...
						 * 	        |         |
						 * 	       ...       ...
						 */
						Vector3 f3BottomLeftVertex  = new Vector3(         iGridSubDivX * fGridSubDivPixelWidth,         iGridSubDivY * fGridSubDivPixelHeight, 0.0f );	// BL
						Vector3 f3BottomRightVertex = new Vector3( ( iGridSubDivX + 1 ) * fGridSubDivPixelWidth,         iGridSubDivY * fGridSubDivPixelHeight, 0.0f );	// BR
						Vector3 f3TopRightVertex    = new Vector3( ( iGridSubDivX + 1 ) * fGridSubDivPixelWidth, ( iGridSubDivY + 1 ) * fGridSubDivPixelHeight, 0.0f );	// TR
						Vector3 f3TopLeftVertex     = new Vector3(         iGridSubDivX * fGridSubDivPixelWidth, ( iGridSubDivY + 1 ) * fGridSubDivPixelHeight, 0.0f );	// TL
						
						int iBottomLeftVertexIndex;
						int iBottomRightVertexIndex;
						int iTopRightVertexIndex;
						int iTopLeftVertexIndex;
						
						// For each grid div vertex, query its index if already created (the vertex might be shared with other grid divs).
						// If not, create it.
						if( oVertexPosIndexDict.TryGetValue( f3BottomLeftVertex, out iBottomLeftVertexIndex ) == false )
						{
							iBottomLeftVertexIndex = iCurrentVertexIndex++;
							oVertexPosIndexDict.Add( f3BottomLeftVertex, iBottomLeftVertexIndex );
							//oGridPlaneUVList.Add( new Vector2( f3BottomLeftVertex.x / (float) iTextureWidth, f3BottomLeftVertex.y / (float) iTextureHeight ) );
						}
						
						if( oVertexPosIndexDict.TryGetValue( f3BottomRightVertex, out iBottomRightVertexIndex ) == false )
						{
							iBottomRightVertexIndex = iCurrentVertexIndex++;
							oVertexPosIndexDict.Add( f3BottomRightVertex, iBottomRightVertexIndex );
							//oGridPlaneUVList.Add( new Vector2( f3BottomRightVertex.x / (float) iTextureWidth, f3BottomRightVertex.y / (float) iTextureHeight ) );
						}
						
						if( oVertexPosIndexDict.TryGetValue( f3TopRightVertex, out iTopRightVertexIndex ) == false )
						{
							iTopRightVertexIndex = iCurrentVertexIndex++;
							oVertexPosIndexDict.Add( f3TopRightVertex, iTopRightVertexIndex );
							//oGridPlaneUVList.Add( new Vector2( f3TopRightVertex.x / (float) iTextureWidth, f3TopRightVertex.y / (float) iTextureHeight ) );
						}
						
						if( oVertexPosIndexDict.TryGetValue( f3TopLeftVertex, out iTopLeftVertexIndex ) == false )
						{
							iTopLeftVertexIndex = iCurrentVertexIndex++;
							oVertexPosIndexDict.Add( f3TopLeftVertex, iTopLeftVertexIndex );
							//oGridPlaneUVList.Add( new Vector2( f3TopLeftVertex.x / (float) iTextureWidth, f3TopLeftVertex.y / (float) iTextureHeight ) );
						}
						
						// Create grid div triangles
						// First triangle (T1)
						oGridPlaneTriangleList.Add( iBottomRightVertexIndex );	// BR
						oGridPlaneTriangleList.Add( iBottomLeftVertexIndex );	// BL
						oGridPlaneTriangleList.Add( iTopRightVertexIndex );		// TR
						
						// Second triangle (T2)
						oGridPlaneTriangleList.Add( iBottomLeftVertexIndex );	// BL
						oGridPlaneTriangleList.Add( iTopLeftVertexIndex );		// TL
						oGridPlaneTriangleList.Add( iTopRightVertexIndex );		// TR

						// Set grid sub div as not empty
						bEmptyGridSubDiv = false;
					}
				}
			}
		}

		// Apply pivot + compute UVs
		int iVertexCount = oVertexPosIndexDict.Count;
		Vector2[ ] oGridPlaneUVs      = new Vector2[ iVertexCount ];
		Vector3[ ] oGridPlaneVertices = new Vector3[ iVertexCount ];
		Vector2 f2Dimensions          = new Vector2( 1.0f / (float) iTextureWidth, 1.0f / (float) iTextureHeight );

		foreach( KeyValuePair<Vector3,int> rVertexPosIndexPair in oVertexPosIndexDict )
		{
			Vector3 f3VertexPos = rVertexPosIndexPair.Key;
			int iIndex = rVertexPosIndexPair.Value;

			oGridPlaneUVs[ iIndex ]      = new Vector2( f3VertexPos.x * f2Dimensions.x, f3VertexPos.y * f2Dimensions.y );
			oGridPlaneVertices[ iIndex ] = ( f3VertexPos - a_f3PivotPoint ) * a_fScale;
		}
		
		oGridPlaneMesh.vertices  = oGridPlaneVertices;
		oGridPlaneMesh.uv        = oGridPlaneUVs;
		oGridPlaneMesh.triangles = oGridPlaneTriangleList.ToArray( );	// LINQ

		return oGridPlaneMesh;
	}
	// Polygonizes a flat parametrized grid mesh from a binarized image
	public static Mesh PolygonizeGrid(BinarizedImage a_rBinarizedImage, Vector2 a_f2Scale, Vector3 a_f3PivotPoint, int a_iHorizontalSubDivs, int a_iVerticalSubDivs, bool a_bExtrude = false, float a_fExtrusionDepth = 0.0f)
	{
		BinarizedImage oDownScaledBinarizedImage = Uni2DEditorShapeExtractionUtils.DownScaleImage(a_rBinarizedImage, a_iHorizontalSubDivs, a_iVerticalSubDivs);
		
		Mesh oGridPlaneMesh = new Mesh( );
		
		// Texture dimensions
		int iTextureWidth  = a_rBinarizedImage.Width;
		int iTextureHeight = a_rBinarizedImage.Height;

		int iGridSubDivPerRow    = ( a_iVerticalSubDivs + 1 );
		int iGridSubDivPerColumn = ( a_iHorizontalSubDivs + 1 );
		int iGridSubDivCount     = iGridSubDivPerRow * iGridSubDivPerColumn;

		int iGridVertexCount = ( a_iHorizontalSubDivs + 2 ) * ( a_iVerticalSubDivs + 2 );
		
		// Grid div dimensions
		float fGridSubDivPixelWidth  = ( (float) iTextureWidth  ) / (float) iGridSubDivPerRow;
		float fGridSubDivPixelHeight = ( (float) iTextureHeight ) / (float) iGridSubDivPerColumn;

		// Vertex pos -> index dictionary: stores the index associated to a given vertex
		int iCurrentVertexIndex = 0;
		Dictionary<Vector3,int> oVertexPosIndexDict = new Dictionary<Vector3, int>( iGridVertexCount );

		// Mesh data
		List<int> oGridPlaneTriangleList = new List<int>( 6 * iGridVertexCount );	// quad = 2 tris, 1 tri = 3 vertices => 2 * 3
		//List<Vector2> oGridPlaneUVList   = new List<Vector2>( iGridVertexCount );
		
		List<int> oOutlineSides = new List<int>();
		for( int iGridSubDivIndex = 0; iGridSubDivIndex < iGridSubDivCount; ++iGridSubDivIndex )
		{		
			int iGridSubDivX = iGridSubDivIndex % iGridSubDivPerRow;
			int iGridSubDivY = iGridSubDivIndex / iGridSubDivPerRow;
			int iIndex = oDownScaledBinarizedImage.Coords2PixelIndex(iGridSubDivX, iGridSubDivY);
			if( oDownScaledBinarizedImage[ iIndex ] )
			{
				// ( X, Y ) grid div pos from grid div index (in grid div space)
				
				/*
				 * Grid div
				 * 
				 *	       ...       ...
				 *	        |         |
				 *	... --- TL ------- TR --- ...
				 *	        |   T2  / |
				 *	        |     /   |
				 *	        |   /     |
				 *	        | /   T1  |
				 *	... --- BL ------- BR --- ...
				 * 	        |         |
				 * 	       ...       ...
				 */
				Vector3 f3BottomLeftVertex  = new Vector3(         iGridSubDivX * fGridSubDivPixelWidth,         iGridSubDivY * fGridSubDivPixelHeight, 0.0f );	// BL
				Vector3 f3BottomRightVertex = new Vector3( ( iGridSubDivX + 1 ) * fGridSubDivPixelWidth,         iGridSubDivY * fGridSubDivPixelHeight, 0.0f );	// BR
				Vector3 f3TopRightVertex    = new Vector3( ( iGridSubDivX + 1 ) * fGridSubDivPixelWidth, ( iGridSubDivY + 1 ) * fGridSubDivPixelHeight, 0.0f );	// TR
				Vector3 f3TopLeftVertex     = new Vector3(         iGridSubDivX * fGridSubDivPixelWidth, ( iGridSubDivY + 1 ) * fGridSubDivPixelHeight, 0.0f );	// TL
				
				int iBottomLeftVertexIndex;
				int iBottomRightVertexIndex;
				int iTopRightVertexIndex;
				int iTopLeftVertexIndex;
				
				// For each grid div vertex, query its index if already created (the vertex might be shared with other grid divs).
				// If not, create it.
				if( oVertexPosIndexDict.TryGetValue( f3BottomLeftVertex, out iBottomLeftVertexIndex ) == false )
				{
					iBottomLeftVertexIndex = iCurrentVertexIndex++;
					oVertexPosIndexDict.Add( f3BottomLeftVertex, iBottomLeftVertexIndex );
					//oGridPlaneUVList.Add( new Vector2( f3BottomLeftVertex.x / (float) iTextureWidth, f3BottomLeftVertex.y / (float) iTextureHeight ) );
				}
				
				if( oVertexPosIndexDict.TryGetValue( f3BottomRightVertex, out iBottomRightVertexIndex ) == false )
				{
					iBottomRightVertexIndex = iCurrentVertexIndex++;
					oVertexPosIndexDict.Add( f3BottomRightVertex, iBottomRightVertexIndex );
					//oGridPlaneUVList.Add( new Vector2( f3BottomRightVertex.x / (float) iTextureWidth, f3BottomRightVertex.y / (float) iTextureHeight ) );
				}
				
				if( oVertexPosIndexDict.TryGetValue( f3TopRightVertex, out iTopRightVertexIndex ) == false )
				{
					iTopRightVertexIndex = iCurrentVertexIndex++;
					oVertexPosIndexDict.Add( f3TopRightVertex, iTopRightVertexIndex );
					//oGridPlaneUVList.Add( new Vector2( f3TopRightVertex.x / (float) iTextureWidth, f3TopRightVertex.y / (float) iTextureHeight ) );
				}
				
				if( oVertexPosIndexDict.TryGetValue( f3TopLeftVertex, out iTopLeftVertexIndex ) == false )
				{
					iTopLeftVertexIndex = iCurrentVertexIndex++;
					oVertexPosIndexDict.Add( f3TopLeftVertex, iTopLeftVertexIndex );
					//oGridPlaneUVList.Add( new Vector2( f3TopLeftVertex.x / (float) iTextureWidth, f3TopLeftVertex.y / (float) iTextureHeight ) );
				}
				
				// Create grid div triangles
				// First triangle (T1)
				oGridPlaneTriangleList.Add( iBottomRightVertexIndex );	// BR
				oGridPlaneTriangleList.Add( iBottomLeftVertexIndex );	// BL
				oGridPlaneTriangleList.Add( iTopRightVertexIndex );		// TR
				
				// Second triangle (T2)
				oGridPlaneTriangleList.Add( iBottomLeftVertexIndex );	// BL
				oGridPlaneTriangleList.Add( iTopLeftVertexIndex );		// TL
				oGridPlaneTriangleList.Add( iTopRightVertexIndex );		// TR
				
				// If we extrude check the sides to see if there are parts of the outline 
				if(a_bExtrude)
				{
					
					// Up
					if(IsBinarizedImageCellEmpty(iGridSubDivX, iGridSubDivY + 1, iGridSubDivPerRow, iGridSubDivPerColumn, oDownScaledBinarizedImage))
					{
						oOutlineSides.Add(iTopLeftVertexIndex);
						oOutlineSides.Add(iTopRightVertexIndex);
					}
					
					// Right
					if(IsBinarizedImageCellEmpty(iGridSubDivX + 1, iGridSubDivY, iGridSubDivPerRow, iGridSubDivPerColumn, oDownScaledBinarizedImage))
					{
						oOutlineSides.Add(iTopRightVertexIndex);
						oOutlineSides.Add(iBottomRightVertexIndex);
					}
					
					// Bottom
					if(IsBinarizedImageCellEmpty(iGridSubDivX, iGridSubDivY - 1, iGridSubDivPerRow, iGridSubDivPerColumn, oDownScaledBinarizedImage))
					{
						oOutlineSides.Add(iBottomRightVertexIndex);
						oOutlineSides.Add(iBottomLeftVertexIndex);
					}
					
					// Left
					if(IsBinarizedImageCellEmpty(iGridSubDivX - 1, iGridSubDivY, iGridSubDivPerRow, iGridSubDivPerColumn, oDownScaledBinarizedImage))
					{
						oOutlineSides.Add(iBottomLeftVertexIndex);
						oOutlineSides.Add(iTopLeftVertexIndex);
					}
				}
			}
		}

		// Apply pivot + compute UVs
		int i2DVertexCount = oVertexPosIndexDict.Count;
		int iVertexCount = i2DVertexCount;
		Vector3 f3ExtrudeOffset = Vector3.zero;
		if(a_bExtrude)
		{
			iVertexCount = 2 * iVertexCount;
			f3ExtrudeOffset = Vector3.forward * a_fExtrusionDepth * 0.5f;
		}
		Vector2[ ] oGridPlaneUVs      = new Vector2[ iVertexCount ];
		Vector3[ ] oGridPlaneVertices = new Vector3[ iVertexCount ];
		Vector2 f2Dimensions          = new Vector2( 1.0f / (float) iTextureWidth, 1.0f / (float) iTextureHeight );

		foreach( KeyValuePair<Vector3,int> rVertexPosIndexPair in oVertexPosIndexDict )
		{
			Vector3 f3VertexPos = rVertexPosIndexPair.Key;
			int iIndex = rVertexPosIndexPair.Value;
			
			Vector2 f2UV = new Vector2( f3VertexPos.x * f2Dimensions.x, f3VertexPos.y * f2Dimensions.y );
			Vector3 f3Vertex = ( f3VertexPos - a_f3PivotPoint );
			f3Vertex.x *= a_f2Scale.x;
			f3Vertex.y *= a_f2Scale.y;
			
			if(a_bExtrude)
			{
				oGridPlaneUVs[iIndex] = f2UV;
				oGridPlaneVertices[iIndex] = f3Vertex - f3ExtrudeOffset;
				
				oGridPlaneUVs[iIndex + i2DVertexCount] = f2UV;
				oGridPlaneVertices[iIndex + i2DVertexCount] = f3Vertex + f3ExtrudeOffset;
			}
			else
			{
				oGridPlaneUVs[iIndex] = f2UV;
				oGridPlaneVertices[iIndex] = f3Vertex;
			}
		}
		
		
		// Surface Triangles
		int i2DTriangleIndicesCount = oGridPlaneTriangleList.Count;
		int iSurfaceTriangleIndicesCount = oGridPlaneTriangleList.Count;
		int iTriangleIndicesCount = oGridPlaneTriangleList.Count;
		if(a_bExtrude)
		{
			iSurfaceTriangleIndicesCount *= 2;
			iTriangleIndicesCount = 2 * iTriangleIndicesCount + oOutlineSides.Count * 3;
		}
		int[] oGridPlaneTriangleIndices = new int[iTriangleIndicesCount];
		for(int i = 0; i < i2DTriangleIndicesCount; ++i)
		{
			int iIndex = oGridPlaneTriangleList[i];
			oGridPlaneTriangleIndices[i] = iIndex;
			if(a_bExtrude)
			{
				oGridPlaneTriangleIndices[iSurfaceTriangleIndicesCount - 1 - i] = iIndex + i2DVertexCount;
			}
		}
		
		// Side Triangles
		if(a_bExtrude)
		{
			int iStartSideTriangle = iSurfaceTriangleIndicesCount;
			int iOutlineSideCount = oOutlineSides.Count/2;
			for(int i = 0; i < iOutlineSideCount; ++i)
			{
				int iSideFrontBegin = oOutlineSides[i * 2];
				int iSideFrontEnd = oOutlineSides[i * 2 + 1];
				int iSideBackEnd = iSideFrontEnd + i2DVertexCount;
				int iSideBackBegin = iSideFrontBegin + i2DVertexCount;
				
				int iSideQuadStartIndex = iStartSideTriangle + i * 6;
				
				oGridPlaneTriangleIndices[iSideQuadStartIndex + 0] = iSideFrontBegin;
				oGridPlaneTriangleIndices[iSideQuadStartIndex + 1] = iSideBackBegin;
				oGridPlaneTriangleIndices[iSideQuadStartIndex + 2] = iSideBackEnd;
				
				oGridPlaneTriangleIndices[iSideQuadStartIndex + 3] = iSideFrontBegin;
				oGridPlaneTriangleIndices[iSideQuadStartIndex + 4] = iSideBackEnd;
				oGridPlaneTriangleIndices[iSideQuadStartIndex + 5] = iSideFrontEnd;
			}
		}
		
		oGridPlaneMesh.vertices  = oGridPlaneVertices;
		oGridPlaneMesh.uv        = oGridPlaneUVs;
		oGridPlaneMesh.triangles = oGridPlaneTriangleIndices;	// LINQ

		return oGridPlaneMesh;
	}
	private static bool IsBinarizedImageCellEmpty(int a_iCellX, int a_iCellY, int a_iGridWidth, int a_iGridHeight, BinarizedImage a_oBinaryGrid)
	{
		// If the cell is exterior to the grid consider it empty
		if(		a_iCellX < 0 || a_iCellX >= a_iGridWidth
			|| 	a_iCellY < 0 || a_iCellY >= a_iGridHeight)
		{
			return true;
		}
		
		return a_oBinaryGrid[a_iCellY * a_iGridWidth + a_iCellX] == false;
	}
    public static void CombinedContourLabeling( BinarizedImage a_rBinarizedImage, bool a_bPolygonizeHoles, out List<Contour> a_rOuterContours, out List<Contour> a_rInnerContours )
    {
        a_rOuterContours = new List<Contour>( );
        a_rInnerContours = new List<Contour>( );

        int[ ] oLabelMap = new int[ a_rBinarizedImage.PixelCount ];	// init as 0
        int iRegionCounter = 0;

        for( int iY = 0; iY < a_rBinarizedImage.Height; ++iY )
        {
            int iLabel = 0;

            for( int iX = 0; iX < a_rBinarizedImage.Width; ++iX )
            {
                int iPixelIndex = a_rBinarizedImage.Coords2PixelIndex( iX, iY );

                if( a_rBinarizedImage[ iPixelIndex ] == true )	// Foreground
                {
                    if( iLabel != 0 )	// Continue inside region
                    {
                        oLabelMap[ iPixelIndex ] = iLabel;
                    }
                    else
                    {
                        iLabel = oLabelMap[ iPixelIndex ];
                        if( iLabel == 0 )	// New outer contour
                        {
                            ++iRegionCounter;
                            iLabel = iRegionCounter;
                            Contour rOuterContour = TraceContour( iPixelIndex, NeighborDirection.DirectionRight, iLabel, a_rBinarizedImage, oLabelMap );
                            a_rOuterContours.Add( rOuterContour );
                            oLabelMap[ iPixelIndex ] = iLabel;
                        }
                    }
                }
                else // Background
                {
                    if( iLabel != 0 )
                    {
                        if( a_bPolygonizeHoles == true )
                        {
                            if( oLabelMap[ iPixelIndex ] == 0 )	// New inner contour
                            {
                                iPixelIndex = a_rBinarizedImage.Coords2PixelIndex( iX - 1, iY );
                                Contour rInnerContour = TraceContour( iPixelIndex, NeighborDirection.DirectionUpRight, iLabel, a_rBinarizedImage, oLabelMap );
                                a_rInnerContours.Add( rInnerContour );
                            }
                            iLabel = 0;
                        }
                        else if( oLabelMap[ iPixelIndex ] != -1 )
                        {
                            oLabelMap[ iPixelIndex ] = iLabel;
                        }
                        else
                        {
                            iLabel = 0;
                        }
                    }
                }
            }
        }
    }
    public static void FindNextPoint(int a_iStartingPixelIndex, NeighborDirection a_eStartingDirection, BinarizedImage a_rBinarizedImage, int[] a_rLabelMap, out int a_iNextPixelIndex, out NeighborDirection a_eNextDirection)
    {
        a_eNextDirection = a_eStartingDirection;

        // Search in all directions except initial direction (=> 7)
        for (int iSearchIteration = 0; iSearchIteration < 7; ++iSearchIteration)
        {
            a_iNextPixelIndex = a_rBinarizedImage.GetNeighborPixelIndex(a_iStartingPixelIndex, a_eNextDirection);

            if (a_rBinarizedImage[a_iNextPixelIndex] == false)                           // Background pixel, mark it as visited
            {
                a_rLabelMap[a_iNextPixelIndex] = -1;                                     // mark as visited
                a_eNextDirection = (NeighborDirection)(((int)a_eNextDirection + 1) % 8); // Next direction...
            }
            else                                                                         // Non background pixel
            {
                return;
            }
        }
        a_iNextPixelIndex = a_iStartingPixelIndex;              // return starting index
    }
    public static void CombinedContourLabeling(BinarizedImage a_rBinarizedImage, bool a_bPolygonizeHoles, out List <Contour> a_rOuterContours, out List <Contour> a_rInnerContours)
    {
        a_rOuterContours = new List <Contour>( );
        a_rInnerContours = new List <Contour>( );

        int[] oLabelMap      = new int[a_rBinarizedImage.PixelCount];           // init as 0
        int   iRegionCounter = 0;

        for (int iY = 0; iY < a_rBinarizedImage.Height; ++iY)
        {
            int iLabel = 0;

            for (int iX = 0; iX < a_rBinarizedImage.Width; ++iX)
            {
                int iPixelIndex = a_rBinarizedImage.Coords2PixelIndex(iX, iY);

                if (a_rBinarizedImage[iPixelIndex] == true)     // Foreground
                {
                    if (iLabel != 0)                            // Continue inside region
                    {
                        oLabelMap[iPixelIndex] = iLabel;
                    }
                    else
                    {
                        iLabel = oLabelMap[iPixelIndex];
                        if (iLabel == 0)                                // New outer contour
                        {
                            ++iRegionCounter;
                            iLabel = iRegionCounter;
                            Contour rOuterContour = TraceContour(iPixelIndex, NeighborDirection.DirectionRight, iLabel, a_rBinarizedImage, oLabelMap);
                            a_rOuterContours.Add(rOuterContour);
                            oLabelMap[iPixelIndex] = iLabel;
                        }
                    }
                }
                else                 // Background
                {
                    if (iLabel != 0)
                    {
                        if (a_bPolygonizeHoles == true)
                        {
                            if (oLabelMap[iPixelIndex] == 0)                                    // New inner contour
                            {
                                iPixelIndex = a_rBinarizedImage.Coords2PixelIndex(iX - 1, iY);
                                Contour rInnerContour = TraceContour(iPixelIndex, NeighborDirection.DirectionUpRight, iLabel, a_rBinarizedImage, oLabelMap);
                                a_rInnerContours.Add(rInnerContour);
                            }
                            iLabel = 0;
                        }
                        else if (oLabelMap[iPixelIndex] != -1)
                        {
                            oLabelMap[iPixelIndex] = iLabel;
                        }
                        else
                        {
                            iLabel = 0;
                        }
                    }
                }
            }
        }
    }