Пример #1
0
        public MaskData BitwiseAnd(MaskData other)
        {
            Rectangle bounds = Rectangle.Intersect(this.Bounds, other.Bounds);

            if (bounds.Width == 0 || bounds.Height == 0)
            {
                return(new MaskData());
            }

            int localStartX = bounds.X;
            int localStartY = bounds.Y;
            int localEndX   = bounds.X + bounds.Width;
            int localEndY   = bounds.Y + bounds.Height;

            MaskData result = new MaskData(bounds);

            for (int y = localStartY; y < localEndY; y++)
            {
                for (int x = localStartX; x < localEndX; x++)
                {
                    result[x, y] = this[x, y] & other[x, y];
                }
            }

            return(result.LazyCopyAutoCrop());
        }
Пример #2
0
        /// <summary>Set an area to a flat height, relative to the given height of the input mask</summary>
        public void SetFlatRelative(MaskData maskData, byte height, int offset)
        {
            var baseMask       = maskData.Translated(0, -(int)height); // Convert a "top" mask
            int absoluteHeight = Math.Max(byte.MinValue, Math.Min(byte.MaxValue, (int)height + offset));

            SetFromFlatBaseMask(baseMask, (byte)absoluteHeight);
        }
Пример #3
0
 private static void CheckPoint(MaskData data, Stack <Point> points, int x, int y, bool setTo)
 {
     if (data[x, y] != setTo)
     {
         points.Push(new Point(x, y));
         data[x, y] = setTo;
     }
 }
Пример #4
0
        public static void DrawPixel(ref MaskData data, int x, int y, bool setTo)
        {
            if (setTo)
            {
                data = data.LazyCopyExpandToContain(new Rectangle(x, y, 1, 1));
            }

            if (data.Bounds.Contains(x, y))
            {
                data[x, y] = setTo;
            }
        }
Пример #5
0
        // NOTE: Rendering from world heightmap is slow!!
        // NOTE: Much copy-pasted code from normal heightmap rendering
        public void DrawWorldPhysicsXZRegion(WorldPhysics worldPhysics, MaskData xzMask, SortedList <int, Color> heightColorGradient)
        {
            // Constrain rendering to the display bounds
            int startX = Math.Max(worldPhysics.StartX, displayBounds.Left);
            int endX   = Math.Min(worldPhysics.EndX, displayBounds.Right);

            if (endX <= startX)
            {
                return;                                                        // Off screen
            }
            for (int z = worldPhysics.EndZ - 1; z >= worldPhysics.StartZ; z--) // From back to front of heightmap
            {
                for (int x = startX; x < endX; x++)
                {
                    if (!xzMask.GetOrDefault(x, z)) // TODO: Fix up bounds so we never leave this area (and use regular [,] access) - PERF
                    {
                        continue;
                    }

                    int height = worldPhysics.GetGroundHeightAt(x, z, WorldPhysics.MaximumHeight, WorldPhysics.MaximumHeight, null);
                    if (height == WorldPhysics.MaximumHeight)
                    {
                        continue;
                    }

                    int nextHeight = 0; // Height of the next Z-value (row)
                    if (xzMask.GetOrDefault(x, z - 1))
                    {
                        nextHeight = worldPhysics.GetGroundHeightAt(x, z - 1, WorldPhysics.MaximumHeight, WorldPhysics.MaximumHeight, null);
                    }

                    if (nextHeight != WorldPhysics.MaximumHeight && nextHeight > height)
                    {
                        continue; // Next row will cover this one entirely
                    }
                    // Draw top surface
                    const int zTestOffset = 1; // <- The top surface should be "under" any other pixels
                    DrawPixel(new Position(x, height, z), heightColorGradient.GetColorFromGradient(height), zTestOffset);

                    if (nextHeight != WorldPhysics.MaximumHeight && nextHeight == height)
                    {
                        continue; // Next row covers this one's "solid" section
                    }
                    // Draw solidness
                    for (int h = height - 1; h >= 0; h--)
                    {
                        Color c = Color.Lerp(heightColorGradient.GetColorFromGradient(h), Color.Black, 0.6f);
                        DrawPixel(new Position(x, h, z), c);
                    }
                }
            }
        }
Пример #6
0
        public MaskData MakeFlippedX()
        {
            MaskData flipped = new MaskData(Bounds.FlipXIndexable());

            for (int y = StartY; y < EndY; y++)
            {
                for (int x = StartX; x < EndX; x++)
                {
                    flipped[-x, y] = this[x, y];
                }
            }
            return(flipped);
        }
Пример #7
0
        public static Data2D <Color> CreateColorData(this MaskData mask, Color color)
        {
            var data = new Data2D <Color>(mask.Bounds);

            for (var y = data.StartY; y < data.EndY; y++)
            {
                for (var x = data.StartX; x < data.EndX; x++)
                {
                    data[x, y] = mask[x, y] ? color : Color.Transparent;
                }
            }

            return(data);
        }
Пример #8
0
        public static void DrawLine(ref MaskData data, int x1, int y1, int x2, int y2, bool setTo)
        {
            data = data.LazyCopyExpandToContain(Rectangle.Union(new Rectangle(x1, y1, 1, 1), new Rectangle(x2, y2, 1, 1)));

            int x = x1;
            int y = y1;

            int dx    = x2 - x1;
            int dy    = y2 - y1;
            int x_inc = (dx < 0) ? -1 : 1;
            int l     = System.Math.Abs(dx);
            int y_inc = (dy < 0) ? -1 : 1;
            int m     = System.Math.Abs(dy);
            int dx2   = l << 1;
            int dy2   = m << 1;

            if ((l >= m))
            {
                int err_1 = dy2 - l;
                for (int i = 0; i < l; i++)
                {
                    data[x, y] = setTo;
                    if (err_1 > 0)
                    {
                        y     += y_inc;
                        err_1 -= dx2;
                    }
                    err_1 += dy2;
                    x     += x_inc;
                }
            }
            else
            {
                int err_1 = dx2 - m;
                for (int i = 0; i < m; i++)
                {
                    data[x, y] = setTo;
                    if (err_1 > 0)
                    {
                        x     += x_inc;
                        err_1 -= dy2;
                    }
                    err_1 += dx2;
                    y     += y_inc;
                }
            }

            data[x, y] = setTo;
        }
Пример #9
0
        public void SetBitwiseOrFrom(MaskData other)
        {
            int localStartX = System.Math.Max(this.StartX, other.StartX);
            int localStartY = System.Math.Max(this.StartY, other.StartY);
            int localEndX   = System.Math.Min(this.EndX, other.EndX);
            int localEndY   = System.Math.Min(this.EndY, other.EndY);

            for (int y = localStartY; y < localEndY; y++)
            {
                for (int x = localStartX; x < localEndX; x++)
                {
                    this[x, y] |= other[x, y];
                }
            }
        }
Пример #10
0
        /// <summary>Set an area to a flat height given a mask of the base of the object</summary>
        public void SetFromFlatBaseMask(MaskData maskData, byte height)
        {
            // Ensure that there's enough room in the heightmap to contain mask...
            heightmapData = heightmapData.LazyCopyExpandToContain(maskData.Bounds, DefaultHeight);

            for (int y = maskData.StartY; y < maskData.EndY; y++)
            {
                for (int x = maskData.StartX; x < maskData.EndX; x++)
                {
                    if (maskData[x, y])
                    {
                        heightmapData[x, y] = height;
                    }
                }
            }
        }
Пример #11
0
        public void SetBitwiseAndFromMustBeContained(MaskData other)
        {
            Debug.Assert(other.Bounds.Contains(this.Bounds));

            int localStartX = StartX;
            int localStartY = StartY;
            int localEndX   = EndX;
            int localEndY   = EndY;

            for (int y = localStartY; y < localEndY; y++)
            {
                for (int x = localStartX; x < localEndX; x++)
                {
                    this[x, y] &= other[x, y];
                }
            }
        }
Пример #12
0
        /// <summary>Set from a 1px deep alpha mask (such as for a railing)</summary>
        public void SetFromRailingMask(MaskData maskData)
        {
            heightmapData = heightmapData.LazyCopyExpandToContain(new Rectangle(maskData.OffsetX, 0, maskData.Width, 1), DefaultHeight);

            for (int x = maskData.StartX; x < maskData.EndX; x++)          // For each column in the image
            {
                for (int y = maskData.EndY - 1; y >= maskData.StartY; y--) // Search top-to-bottom
                {
                    if (maskData[x, y])
                    {
                        heightmapData[x, 0] = (byte)y;
                        goto nextColumn;
                    }
                }
nextColumn:
                ;
            }
        }
Пример #13
0
        /// <summary>Convert color data to a trimmed 1-bit mask</summary>
        public static MaskData CreateMask(this Data2D <Color> data, Color color, bool inverse = false)
        {
            Rectangle trimBounds = data.FindTrimBounds(color, inverse);

            // TODO: Avoid this copy...
            Data2D <Color> trimData = data.CopyWithNewBounds(trimBounds);

            MaskData mask = new MaskData(trimData.Bounds);

            for (int y = trimData.StartY; y < trimData.EndY; y++)
            {
                for (int x = trimData.StartX; x < trimData.EndX; x++)
                {
                    mask[x, y] = ((trimData[x, y] == color) != inverse);
                }
            }

            return(mask);
        }
Пример #14
0
        /// <summary>
        /// Copy the data from one region to another (without moving it).
        /// Any existing data outside the new boundary is lost.
        /// </summary>
        public MaskData CopyWithNewBounds(Rectangle newBounds)
        {
            MaskData copy = new MaskData(newBounds);

            int startX = System.Math.Max(copy.StartX, this.StartX);
            int startY = System.Math.Max(copy.StartY, this.StartY);
            int endX   = System.Math.Min(copy.EndX, this.EndX);
            int endY   = System.Math.Min(copy.EndY, this.EndY);

            for (int y = startY; y < endY; y++)
            {
                for (int x = startX; x < endX; x++)
                {
                    copy[x, y] = this[x, y];
                }
            }

            return(copy);
        }
Пример #15
0
        public int GetHeightByWalkingObliqueForward(MaskData maskData, int frontEdgeDepth, Oblique oblique, int x, int y)
        {
            // Try to walk forward in the mask to find the front edge, from which we have a specified depth, and can calculate the height
            while (true)
            {
                // NOTE: Don't need to do special handling of "upright" sections, because our caller works top-down through the image
                //       (So these sections will be overwritten as appropriate)

                int nextX = x - (int)oblique; // Walk forward (down the mask) in the oblique direction
                int nextY = y - 1;

                if (!maskData.GetOrDefault(nextX, nextY)) // Reached the front of the mask data
                {
                    return(y - frontEdgeDepth);
                }

                x = nextX;
                y = nextY;
            }
        }
Пример #16
0
        /// <param name="slope">Number of pixels to travel backwards before traveling in the oblique direction</param>
        public void SetFromFrontEdge(MaskData maskData, int frontEdgeDepth, int depth, Oblique obliqueDirection, int slope, int offset)
        {
            Debug.Assert(depth > 0);
            Debug.Assert(slope > 0);

            // How far do we travel on the X axis as we go backwards?
            int pixelsTraveledSideways = ((depth + slope - 1) / slope - 1) * (int)obliqueDirection;
            int outputStartX           = Math.Min(maskData.StartX, maskData.StartX + pixelsTraveledSideways);
            int outputEndX             = Math.Max(maskData.EndX, maskData.EndX + pixelsTraveledSideways);

            // Ensure that there's enough room in the heightmap to contain the maximum extents of the processed mask...
            Rectangle outputPotentialBounds = new Rectangle(outputStartX, frontEdgeDepth, outputEndX - outputStartX, depth);

            heightmapData = heightmapData.LazyCopyExpandToContain(outputPotentialBounds, DefaultHeight);


            // Read the mask upwards to find the "lip" of the mask surface
            for (int x = maskData.StartX; x < maskData.EndX; x++)     // For each column in the image
            {
                for (int y = maskData.StartY; y < maskData.EndY; y++) // Search from bottom-to-top
                {
                    if (maskData[x, y])
                    {
                        // Found the lip at a given Y height, copy it backwards at the given pitch
                        for (int d = 0; d < depth; d++)
                        {
                            int zz = frontEdgeDepth + d;
                            int xx = x + (d / slope) * (int)obliqueDirection;

                            heightmapData[xx, zz] = (byte)(y - frontEdgeDepth + offset);
                        }

                        goto nextColumn;
                    }
                }

nextColumn:
                ;
            }
        }
Пример #17
0
        public static void DrawFloodFill(MaskData data, int x, int y, bool setTo)
        {
            // http://csharphelper.com/blog/2014/09/write-a-graphical-floodfill-method-in-c/

            if (!data.Bounds.Contains(x, y))
            {
                return;
            }
            if (data[x, y] == setTo)
            {
                return;
            }

            var points = new Stack <Point>();

            points.Push(new Point(x, y));
            data[x, y] = setTo;

            while (points.Count > 0)
            {
                var pt = points.Pop();
                if (pt.X > data.StartX)
                {
                    CheckPoint(data, points, pt.X - 1, pt.Y, setTo);
                }
                if (pt.Y > data.StartY)
                {
                    CheckPoint(data, points, pt.X, pt.Y - 1, setTo);
                }
                if (pt.X < data.EndX - 1)
                {
                    CheckPoint(data, points, pt.X + 1, pt.Y, setTo);
                }
                if (pt.Y < data.EndY - 1)
                {
                    CheckPoint(data, points, pt.X, pt.Y + 1, setTo);
                }
            }
        }
Пример #18
0
        /// <summary>Set heights from a top-surface mask where the front edge is at a particular depth</summary>
        /// <param name="maskData">The 1-bit mask representing the top surface</param>
        /// <param name="frontEdgeDepth">The depth of the front edge of pixels in the mask</param>
        /// <param name="perspective">Oblique direction that the mask projects backwards towards</param>
        public void SetFromObliqueTopMask(MaskData maskData, int frontEdgeDepth, Oblique oblique)
        {
            // Ensure that there's enough room in the heightmap to contain the maximum extents of the mask
            Rectangle outputPotentialBounds = maskData.Bounds;

            outputPotentialBounds.Y = frontEdgeDepth; // The usable Z range = [frontEdgeDepth, frontEdgeDepth + Height)
            heightmapData           = heightmapData.LazyCopyExpandToContain(outputPotentialBounds, DefaultHeight);

            // Note: Y axis seeks from the top downwards (from back to front)
            for (int y = maskData.EndY - 1; y >= maskData.StartY; y--)
            {
                for (int x = maskData.StartX; x < maskData.EndX; x++)
                {
                    if (maskData[x, y])
                    {
                        int height = GetHeightByWalkingObliqueForward(maskData, frontEdgeDepth, oblique, x, y);
                        int z      = y - height;

                        heightmapData[x, z] = (byte)height; // (If height overflows... at least it will be obvious)
                    }
                }
            }
        }
Пример #19
0
        // For high-performance mask comparisons:

        public MaskData CopyAndExpandForBitShift()
        {
            if (Width % 32 == 1)
            {
                // We can be a direct copy, because bit-shifting 31 times will not shift any data off the end of the image
                uint[] copyData = (uint[])packedData.Clone();
                return(new MaskData(copyData, OffsetX, OffsetY, Width + 31, Height));
            }
            else
            {
                // In this case we need to expand because we will eventually shift into a new 32-bit column:
                int dataWidth    = DataWidth;
                int newDataWidth = dataWidth + 1;
                Debug.Assert(newDataWidth == MaskData.WidthToDataWidth(Width + 31));
                uint[] copyData = new uint[newDataWidth * Height];

                for (int y = 0; y < Height; y++)
                {
                    Array.Copy(packedData, dataWidth * y, copyData, newDataWidth * y, dataWidth);
                }

                return(new MaskData(copyData, OffsetX, OffsetY, Width + 31, Height));
            }
        }
Пример #20
0
        public void SetFromObliqueSide(MaskData maskData, Oblique obliqueDirection, int offset)
        {
            // Straight and Right use the same input direction (because Straight input does not make sense, but Straight output is ok)
            int inputReadDirection = 1;
            int x = maskData.StartX;

            if (obliqueDirection == Oblique.Left)
            {
                inputReadDirection = -1;
                x = maskData.EndX - 1;
            }

            int y;

            while (x >= maskData.StartX && x < maskData.EndX)
            {
                for (y = maskData.StartY; y < maskData.EndY; y++) // read bottom-to-top
                {
                    if (maskData[x, y])
                    {
                        goto foundStartPosition;
                    }
                }
                x += inputReadDirection;
            }

            // No data found!
            return;

foundStartPosition:

            // Ensure that there's enough room in the heightmap to contain the maximum extents of the processed mask...
            {
                int left, right;
                if (inputReadDirection == 1)
                {
                    left  = x;
                    right = maskData.EndX - 1;
                }
                else // reading right-to-left
                {
                    left  = maskData.StartX;
                    right = x;
                }
                // Account for offset:
                left  += Math.Min(offset, 0);
                right += Math.Max(offset, 0);
                int front = y;
                int back  = front + (right - left); // can move back one pixel for each column of input

                Rectangle outputPotentialBounds = new Rectangle(left, front, (right - left) + 1, (back - front) + 1);
                heightmapData = heightmapData.LazyCopyExpandToContain(outputPotentialBounds, DefaultHeight);
            }


            // Convert mask to heightmap:
            int writeX = x;
            int writeZ = y;
            int baseY  = y;

            while (x >= maskData.StartX && x < maskData.EndX) // For each column to end of image
            {
                y = baseY;                                    // Count pixels from base upwards
                while (y < maskData.EndY && maskData[x, y])
                {
                    y++;
                }

                int height = y - baseY;
                if (height > 0)
                {
                    int i = 0;
                    do
                    {
                        heightmapData[writeX + i * Math.Sign(offset), writeZ] = (byte)Math.Min(byte.MaxValue, height);
                        i++;
                    } while(i < Math.Abs(offset));
                }

                // Move input:
                x += inputReadDirection;
                baseY++;

                // Move output:
                writeX += (int)obliqueDirection;
                writeZ++;
            }
        }
Пример #21
0
 /// <summary>Set an area to a flat height given a mask of the top of the object.</summary>
 public void SetFromFlatTopMask(MaskData maskData, byte height)
 {
     // Just convert it to a "base" mask and use that...
     SetFromFlatBaseMask(maskData.Translated(0, -(int)height), height);
 }
Пример #22
0
 public TransformedMaskData(MaskData maskData, bool flipX)
 {
     this.maskData = maskData;
     this.flipX    = flipX;
 }