// 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 }
// 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); }
// 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; } } } } } }