It Slices! It Dices!

I am a fruit ninja addict. Any time I am given the opportunity to play the game, I will spend more than an hour hogging whoever’s phone or tablet. Yes, I do not even own my own copy.

Yesterday, in a somehow miraculous surge of productivity, I decided I would try to replicate the slicing mechanics in one of my existing 2D games. And, well, it’s time to share the results with the internet, of course!

Whatcha know about texture slicin’, hater?

First of all, I need to clarify. This is 2D, texture based cutting, done using the existing 2D architecture from XNA (Spritebatch, Texture2D, and whatnot). This means no vertex buffers, no shaders and thus no vertex displacement. Cutting by adding vertices and splitting models based on these points is a lot more efficient than the method I am about to show, as it gives all the hard work to the graphics card. However, if you are limited to the 2D XNA framework, you might want to consider the following approach.

Now that this is out the way, let’s do this *crow noise*!

What do we want to do?

Let’s get the problem down first. We would like to be able to slice an object in two. This means that we should feed in a single texture into our method, and obtain two at the end.

public static void SliceTextureLine(Texture2D input,
            out Texture2D output1, out Texture2D output2)
        {
            //Save orphans and puppies from burning buildings.
            //Also, slice a texture in two.
        }

What about the line?

As we are talking about slicing, we should assume that what we want is a straight line going through a texture. The question is, how should we represent line coordinates? What is the lowest level of representing a straight line? We could use two points, but why not use its basic mathematical equation?

Line equation: f(x) : y = ax + b, where a is the gradient and b the intercept.

This means we will need a gradient and an intercept to represent the line. Using two points would eventually boil down to finding the coefficient, which means it’s not lower level enough to be in the scope of the method!

public static void SliceTextureLine(Texture2D input,
            float gradient, float intercept,
            out Texture2D output1, out Texture2D output2)
        {
            //Save orphans and puppies from burning buildings.
            //Also, slice a texture in two.
        }

A note about method scope:

We need to keep in mind that staying within the method scope is essential. Therefore, we should input the line coordinates according to texture space, and not according to world space, as converting from world space to texture space should not be the method’s business.

We need to consider two types of coordinates:

  • Use standard texture coordinates (ie (10, 20) in a 100×100 texture).
  • Use relative texture coordinates (ie (0.1, 0.2) in a 100×100 -or any- texture).

It all depends on how you would like to implement it within the program. Relative texture coordinates are better for encapsulation, but require more calculations on your part. I will use standard coordinates for this example.

Texture data as one-dimensional Color arrays

To slice the input texture into two output textures, we need to fiddle with what’s inside it, which is an array of colours.

We need to obtain colour data from the texture in the form of a one-dimensional Color array, and create two one-dimensional Color arrays to store the texture data for the outputs. Let’s first store the data for the input texture:

Color[] incolors = new Color[input.Width * input.Height];
input.GetData<Color>(incolors);

The principle is to cycle through the input texture data, and set each pixel to its respective slice. For this, we will need a nice nested for loop.

for (int x = 0; x < input.Width; ++x)
                for (int y = 0; y < input.Height; ++y)
                {
                    //Declare world peace.
                    //Also, store incolor into either outcolor1 or outcolor2.
                }

Now, let’s declare the output textures and arrays.

output1 = new Texture2D(device, input.Width, input.Height, false, SurfaceFormat.Color);
            output2 = new Texture2D(device, input.Width, input.Height, false, SurfaceFormat.Color);
            Color[] outcolors1 = new Color[input.Width * input.Height];
            Color[] outcolors2 = new Color[input.Width * input.Height];

Why are we using the same texture size?

In the code above, you might notice that both the output textures have the same size.

Why is this? Isn’t there a better way of doing this? Well, yes there is. The reason why we set the size of the output textures to the size of the input is simply because we do not know the size of the outputs before they are themselves determined.

The most efficient method would be to calculate the two intersection points between the line and the texture rectangle, and using them to determine the size of each output array. However,  this requires quite a huge chunk of code, and a separate method for segment intersections. For the sake of simplicity, the output textures will be kept the same size as the input texture.

Replacing colours

As both output textures are the same size, each pixel will need to be added to both of them, one being transparent, and the other being the original. The original pixel will figure in the correct part of the cut, while the transparent one will replace the original in the other part of the cut. It is pretty simple if put in an image!

This means we will need to set up an empty Color:

Color empty = new Color(0, 0, 0, 0);

Finally, the slicing

How do we separate each side? Thanks to the equation of our line, we can use it to compare if a pixel is higher or lower than the slice.
y > Gradient * x + Intercept would then mean that the pixel [x,y] is above the slice line!

We just need to add this to our for loop:

index = x + y * input.Width;
                    if (y > x * gradient + intercept)
                    {
                        outcolors1[index] = incolors[index];
                        outcolors2[index] = empty;
                    }
                    else
                    {
                        outcolors1[index] = empty;
                        outcolors2[index] = incolors[index];
                    }

And there we go!

The final code should look something like this:

public static bool SliceTexture(GraphicsDevice device, Texture2D input,
            float gradient, float intercept,
            out Texture2D output1, out Texture2D output2)
        {
            Color[] incolors = new Color[input.Width * input.Height];
            input.GetData<Color>(incolors);

            output1 = new Texture2D(device, input.Width, input.Height, false, SurfaceFormat.Color);
            output2 = new Texture2D(device, input.Width, input.Height, false, SurfaceFormat.Color);
            Color[] outcolors1 = new Color[input.Width * input.Height];
            Color[] outcolors2 = new Color[input.Width * input.Height];

            int index = 0;
            Color empty = new Color(0, 0, 0, 0);

            for (int x = 0; x < input.Width; ++x)
                for (int y = 0; y < input.Height; ++y)
                {
                    index = x + y * input.Width;
                    if (y > x * gradient + intercept)
                    {
                        outcolors1[index] = incolors[index];
                        outcolors2[index] = empty;
                    }
                    else
                    {
                        outcolors1[index] = empty;
                        outcolors2[index] = incolors[index];
                    }
                }

            output1.SetData<Color>(outcolors1);
            output2.SetData<Color>(outcolors2);
            return true;
        }

What is there left to do?

Remember that the input to this method is relative to the texture itself, so you will need to take into account all the transformations of your sprite. Here is a small example program I made to test the algorithm, where you get to slice famous internet brawler Kimbo Slice. You can download it here.

It works!