Transform Scale Rotation Matrix For Per Pixel Collision - c#

I am trying to do per pixel collision with scaled texture 2ds.
It currently is not detecting the collision and I am not sure why.
Here is where I create the matrix transformation.
private void BuildMatrix()
{
// Build the transformation matrix
Matrix TransformMatrix =
// _Rect.center.x and .y get the centers as this object is based off of a single square pixel.
Matrix.CreateTranslation(new Vector3(-_Rect.Center.X, -_Rect.Center.Y, 0.0f)) *
// _Width is the scale width and height is scale height.
Matrix.CreateScale(_Width, _Height, 0) *
// This one does not have rotation.
Matrix.CreateRotationZ(0) *
// Rect.X / Y are the top left X / Y coordinates for that rectangle.
Matrix.CreateTranslation(new Vector3(_Rect.X, _Rect.Y, 0.0f));
}
Here is the other build matrix.
private void BuildMatrix()
{
// Build the transformation matrix
Matrix TransformMatrix =
// The location.center is the center of the texture 2d. Where it is placed on the screen.
Matrix.CreateTranslation(new Vector3(-Location.Center, 0.0f)) *
// Size width / height are the size of the texture after it is scaled.
Matrix.CreateScale(Size.Width, Size.Height, 0) *
// No rotation.
Matrix.CreateRotationZ(0) *
// Location.Position is the is the top coordinates for the texture2d before scaling.
Matrix.CreateTranslation(new Vector3(Location.Position, 0.0f));
}
Here is the per pixel method.
public static bool IntersectPixels( Matrix transformA, int widthA, int heightA, Color[] dataA, Matrix transformB, int widthB, int heightB, Color[] dataB)
{
// Calculate a matrix which transforms from A's local space into
// world space and then into B's local space
Matrix transformAToB = transformA * Matrix.Invert(transformB);
// When a point moves in A's local space, it moves in B's local space with a
// fixed direction and distance proportional to the movement in A.
// This algorithm steps through A one pixel at a time along A's X and Y axes
// Calculate the analogous steps in B:
Vector2 stepX = Vector2.TransformNormal(Vector2.UnitX, transformAToB);
Vector2 stepY = Vector2.TransformNormal(Vector2.UnitY, transformAToB);
// Calculate the top left corner of A in B's local space
// This variable will be reused to keep track of the start of each row
Vector2 yPosInB = Vector2.Transform(Vector2.Zero, transformAToB);
// For each row of pixels in A
for (int yA = 0; yA < heightA; yA++)
{
// Start at the beginning of the row
Vector2 posInB = yPosInB;
// For each pixel in this row
for (int xA = 0; xA < widthA; xA++)
{
// Round to the nearest pixel
int xB = (int)Math.Round(posInB.X);
int yB = (int)Math.Round(posInB.Y);
// If the pixel lies within the bounds of B
if (0 <= xB && xB < widthB &&
0 <= yB && yB < heightB)
{
// Get the colors of the overlapping pixels
Color colorA = dataA[xA + yA * widthA];
Color colorB = dataB[xB + yB * widthB];
// If both pixels are not completely transparent,
if (colorA.A != 0 && colorB.A != 0) { return true; } // then an intersection has been found
}
// Move to the next pixel in the row
posInB += stepX;
}
// Move to the next row
yPosInB += stepY;
}
// No intersection found
return false;
}
Here is the call to the per pixel collision.
(Statistics.IntersectPixels(Projectile.TransformMatrix, Projectile.Txt2DImage.Width,
Projectile.Txt2DImage.Height, Projectile.TextureColorArr,
TextBoxContainer.Pillar.TransformMatrix, (int)TextBoxContainer.Pillar.Width,
(int)TextBoxContainer.Pillar.Height, TextBoxContainer.Pillar.TextureColorArr)).ToString();

The scale matrix needs to have a Z value of 1.
Matrix.CreateScale(_Width, _Height, 1)

Related

Get world position of a pixel from texture2d/sprite

I'm setting up an automatic system to be able to attach a sprite and it will gather all its colours and the world position of each sprite. A list/class of all the colours used has been set up but how would get the position of all these sprites?
I have already tried doing this mathematically like getting the complete size of the sprite and then working out the size of each pixel and then working out the position from that. But this seems flawed due to the position of the sprite possibly changing.
Sprite ColouredSpriteTexture = ColoredSprite.GetComponent<SpriteRenderer>().sprite;
Texture2D ColouredTexture = ColouredSpriteTexture.texture;
float XsizeF = ColoredSprite.transform.localScale.x;
int Xsize = (int)XsizeF;
float YsizeF = ColoredSprite.transform.localScale.y;
int Ysize = (int)YsizeF;
List<Color> TempList = new List<Color>();
//Could spawn pixels by getting x and y size and dividing them by 100 50/100 = 0.50f
//if the tile has a color then spawn pixel if not 0.50 += 0.50
//TODO test if this logic will work
float PixelSize = XsizeF / 100;
float currentPos = PixelSize;
for (int x = 0; x < Xsize; x++)
{
for (int y = 0; y < Ysize; y++)
{
int listAmount = TempList.Count;
Color ColoredTex = ColouredTexture.GetPixel(x, y);
float TextureAlpha = ColoredTex.a;
if (!TempList.Contains(ColoredTex) && TextureAlpha != 0)
{
TempList.Add(ColoredTex);
ColorByNumber tempColor = new ColorByNumber();
tempColor.Color = ColoredTex;
tempColor.ColorNumber = listAmount;
ColorOptions.Add(tempColor);
}
if(TextureAlpha == 1)
{
GameObject ColorPixel = Instantiate(PixelPrefab);
ColorPixel.transform.localScale = new Vector3(XsizeF, YsizeF, 0);
ColorPixel.transform.SetParent(this.transform);
ColorPixel.name = "Pixel (" + x.ToString() + "," + y.ToString() + ")";
}
}
}
All I would need is somehow each pixel returning its position so I can store this data and be able to spawn anything on top of this pixel.
I haven't had a chance to test this math yet so there may be some mistakes in it:
Every graphical image in Unity has a PPU, this and the object scale are going to be a huge factor. For argument sake I am going to clearly define these for 1 object.
Image dimensions : 128x128
PPU: 64
Scale: 1,1,1
Object Bounds: would
come from the renderer, which I am unsure if that bounds already
takes in account the scale(Most likely) however in the case you
cannot use that you can calculate the ObjectBoundsWidth or height
just by dividing the width or height of the texture by the PPU.
This should give you bounds of the texture in world space.
We are also going to make an assumption that we are only working on the X and Y axis and ignore the Z axis, if you want to use Z instead of Y then just make the necessary changes to be Z Scale and Z position and Z Bounds.
World position of a pixel located at 2,10. Per the documentation the pixel coordinates start at the lower left this means 0,0 is the bottom left corner, and 2,10 is 2 pixels left and 10 pixels up.
EDIT:
So I plugged all of this into a google sheet and determined the previous algorithm I provided was wrong here is the correct one in a pseudo code format
// This function takes in either the x or y, and the width or height of
// the bounds, then the x or y position of the object attached to.
// It also assumes the pivot is the center of the sprite.
float CalculateWorldPosOfPixelCoordinate(int coord, float boundsSize, float position, float scale)
{
float PixelInWorldSpace = 1.0f / PPU;
float startPos= position - (boundsSize* 0.5f * scale);
return startPos + (PixelInWorldSpace * coord) * scale;
}
This is using objectBounds we determined ourselves that is why we are multiply by scale.
this would give use a world position of: -0.97, -0.84
The algorithm i believe is the same for Y, just replace the coord with the Y position, and the bounds with the height instead of the width.
Like I said this could be wrong as I havent had a chance to test it, this also does not account for rotation either.

Pixel-Perfect Collision With Texture Rotation

I have pixel-perfect collision down, but it only works with the texture rotated at 0 radians. Here is my code for determining the pixel-perfect collision-
public static bool IntersectPixels(Texture2D sprite, Rectangle rectangleA, Color[] dataA, Texture2D sprite2, Rectangle rectangleB, Color[] dataB)
{
sprite.GetData<Color>(dataA);
sprite2.GetData<Color>(dataB);
// Find the bounds of the rectangle intersection
int top = Math.Max(rectangleA.Top, rectangleB.Top);
int bottom = Math.Min(rectangleA.Bottom, rectangleB.Bottom);
int left = Math.Max(rectangleA.Left, rectangleB.Left);
int right = Math.Min(rectangleA.Right, rectangleB.Right);
// Check every point within the intersection bounds
for (int y = top; y < bottom; y++)
{
for (int x = left; x < right; x++)
{
// Get the color of both pixels at this point
Color colorA = dataA[(x - rectangleA.Left) + (y - rectangleA.Top) * rectangleA.Width];
Color colorB = dataB[(x - rectangleB.Left) + (y - rectangleB.Top) * rectangleB.Width];
// If both pixels are not completely transparent,
if (colorA.A != 0 && colorB.A != 0)
{
// then an intersection has been found
return true;
}
}
}
// No intersection found
return false;
}
I am having trouble with my collision when it is rotated. How would I go about checking pixel collision with a rotated sprite? Thanks, any help is appreciated.
Ideally, you can exress all transformations with a matrix. That shouldn't be a problem with the helper methods. And if you use matrices, you can easily extend the program without having to change the collision code. Up to now, you can represent a translation with the position of the rectangle.
Let's assume object A has the transformation transA and object B has transB. Then you would iterate over all pixels of object A. If the pixel is not transparent, check which pixel of object B is at this very position and check if this pixel is transparent.
The tricky part is determining which pixel of object B is at a given position. This can be achieved with some matrix math.
You know the position in the space of object A. Firstly, we want to transform this local position to a global position on the screen. This is exactly what transA does. After that, we want to transform the global location to a local location in the space of object B. This is the inverse of transB. So, we have to transform the local position in A with the following matrix:
var fromAToB = transA * Matrix.Invert(transB);
//now iterate over each pixel of A
for(int x = 0; x < ...; ++x)
for(int y = 0; y < ...; ++y)
{
//if the pixel is not transparent, then
//calculate the position in B
var posInA = new Vector2(x, y);
var posInB = Vector2.Transform(posInA, fromAToB);
//round posInB.X and posInB.Y to integer values
//check if the position is within the range of texture B
//check if the pixel is transparent
}

Kinect Cursor Control with virtual XNA-Rectangle-'Touchpad' - Y-Axis inverted

I'm trying to implement a Real-Time Strategy control scheme for the MS Kinect.
So far, I've got a cursor, which can be moved by moving your left Hand (or right, dependant on your handedness). I've got an Open-NI-based Kinect controller which sets up a skeleton for player-movements and delivers the wrist-, elbow-, shoulder- and body-center-coordinates to my application.
To project these wrist-coordinates to the screen, I've set up a Rectangle, which is situated slightly left/right from the player's center and as long as the wrist moves inside the rectangle, the cursor moves on screen.
My problem is, that the XNA-Rectangle has the upper left corner as point of origin, i.e. the X-axis points right, as it "should", but the Y-axis points down, while the Y-axis of the Kinect - coordinate system points up. This results in the cursor moving upwards on screen, when I move my hand down and vice versa. There's no way for me to change anything with the Kinect-coordinate system, so is it possible to 'flip' the 'coordinate system' of the rectangle, so that it's Y-axis points up,too?
Here's the relevant code:
(from Calibrate()-Method:)
List<Vector3> joints = UDPlistener.getInstance().ParseCalibCoordinates(data);
//0 = Right Wrist 1 = Right Elbow 2 = Right Shoulder
//3 = Left Wrist 4 = Left Elbow 5 = Left Shoulder
//6 = Center
height = 762;
width = 1024;
switch (hand)
{
case 0:
cursorSpace = new Rectangle((int)(joints[6].X * 2) - 200, (int)(joints[6].Y * 2) + height, width, height);
break;
case 3:
cursorSpace = new Rectangle((int)(joints[6].X * 2) - 1200, (int)(joints[6].Y * 2) + height, width, height);
break;
}
public Point Cursor(String data)
{
List<Vector3> joints = UDPlistener.getInstance().ParsePlayCoordinates(data);
//0 = Right Wrist 1 = Left Wrist 2 = Center
double mhx = 0; //main hand x-coordinate
double mhy = 0; // main hand y-coordinate
switch (hand)
{
case 0:
mhx = joints[hand].X;
mhy = joints[hand].Y;
break;
case 3:
mhx = joints[hand-2].X;
mhy = joints[hand-2].Y;
break;
}
int x;
int y;
if (Math.Abs(mhx - mhxOld) < 1.0 || Math.Abs(mhy - mhyOld) < 1.0)
//To remove jittering of the cursor
{
x = (int) mhxOld * 2;
y = (int) mhyOld * 2;
}
else
{
x = (int) mhx * 2;
mhxOld = mhx;
y = (int) mhy * 2;
mhyOld = mhy;
}
Point cursor = new Point(0,0);
if (cursorSpace.Contains(x,y))
{
cursor = new Point(x - cursorSpace.X, y - CursorSpace.Y);
lastCursorPos = cursor;
return cursor;
}
Sorry for the wall of text, I hope, I could make myself clear.
Thanks in advance,
KK
I use an extension method for converting OpenNI coordinates. The following example maps the OpenNI coordinates to XNA coordinates in a 640x480 rectangle in the top left corner, represented as a Vector2 object.
public static Vector2 ToXnaCoordinates(this Point3D point)
{
return new Vector2(
point.X + 320,
(point.Y - 240) * -1);
}
The magic that flips the y coordinate is the * -1 part.
If you want to reach a rectangle of different size than 640x480, you need to scale the coordinates accordingly after conversion. Example:
public static Vector2 ToScaledXnaCoordinates(this Point3D point, int rectSizeX, int rectSizeY)
{
return new Vector2(
(point.X + 320) * rectSizeX / 640,
(point.Y - 240) * -rectSizeY / 480);
}
I know this isn't XNA, but I wanted to put this out there for those wpf users:) If you are using something like Channel 9's approach, just have a bool to determine if inverted or not. Example:
private void ScalePosition(FrameworkElement element, Joint joint, bool inverted)
{
//convert the value to X/Y
Joint scaledJoint = joint.ScaleTo(967, 611);
//convert & scale (.3 = means 1/3 of joint distance)
//Joint scaledJoint = joint.ScaleTo(1280, 720, 1f, 1f);
if (!inverted)
{
Canvas.SetLeft(element, scaledJoint.Position.X);
Canvas.SetTop(element, scaledJoint.Position.Y);
}
if (inverted)
{
Canvas.SetLeft(element, scaledJoint.Position.X);
Canvas.SetBottom(element, scaledJoint.Position.Y);
}
}
Hope this helps WPF users!

xna 2d tile map editor drawing only whats on screen

Okay, I have a 2d Tile Map editor I'm working on in xna c#.
In the Draw method I loop through (with a 'for' loop) my 2 dimensional array of tiles so that
my map updates and draws all the tiles every frame.
My question is, how do you draw only the tiles that are seen on screen.
Also is there a better way to draw the tile map (Rather than updating every frame).
In the platformer demo I played around with the visible tiles were calculated and then only those tiles were drawn. I believe you will have to include them in the draw method to be drawn in each time.
Here is a snippet (this only had left to right scrolling so no vertical range was calculated). This kept track of the camera position to calculate it.
Edit:: Added the second method shows how it updated camera position based on the player position stored in a player object.
private void DrawTiles(SpriteBatch spriteBatch)
{
// Calculate the visible range of tiles.
int left = (int)Math.Floor(cameraPosition / Tile.Width);
int right = left + spriteBatch.GraphicsDevice.Viewport.Width / Tile.Width;
right = Math.Min(right, Width - 1);
// For each tile position
for (int y = 0; y < Height; ++y)
{
for (int x = left; x <= right; ++x)
{
// If there is a visible tile in that position
Texture2D texture = tiles[x, y].Texture;
if (texture != null)
{
// Draw it in screen space.
Vector2 position = new Vector2(x, y) * Tile.Size;
spriteBatch.Draw(texture, position, Color.White);
}
}
}
}`
private void ScrollCamera(Viewport viewport)
{
const float ViewMargin = 0.35f;
// Calculate the edges of the screen.
float marginWidth = viewport.Width * ViewMargin;
float marginLeft = cameraPosition + marginWidth;
float marginRight = cameraPosition + viewport.Width - marginWidth;
// Calculate how far to scroll when the player is near the edges of the screen.
float cameraMovement = 0.0f;
if (Player.Position.X < marginLeft)
cameraMovement = Player.Position.X - marginLeft;
else if (Player.Position.X > marginRight)
cameraMovement = Player.Position.X - marginRight;
// Update the camera position, but prevent scrolling off the ends of the level.
float maxCameraPosition = Tile.Width * Width - viewport.Width;
cameraPosition = MathHelper.Clamp(cameraPosition + cameraMovement, 0.0f, maxCameraPosition);
}
int MapSizeX = 20;
int MapSizeY = 20;
int LeftCornerX = 0; //the position of the Tile in the 2Darray that is going
int LeftCornerY = 0; //to be drawn in the left corner of the screen.
int ScreenSizeX = 10;
int ScreenSizeY = 10;
public Tiles[,] tiles = new Tile[MapSizeX, MapSizeY]; //list of all Tiles
//then you can draw it like this....
int counterX = 0; //represents the position on screen
int counterY = 0;
// y and x inside the for loops represents the position in tiles
for(int y = LeftCornerY; y < MapSizeY < y++)
{
for(int x = LeftCornerX; y < MapSizeX < x++)
{
if(counterX < ScreenSizeX && counterY < ScreenSizeY)
{
tiles[x, y].draw(tiles[counterX , counterY]);
}
counterX ++;
//when you do like this you draw the tiles you want
//at the position you want. In the draw method you just
// drawn the tile you want at the position of the tile you
// send as in parameter to the draw method.
}
counterY++;
counterX = 0;
}
then you just have to increase the LeftCorner variables to draw another part of the map

C# XNA 4.0 Getting scaled rotated sprite position in window

I have some problems with collision. I want to ge coords of a sprite that can be rotated scaled or whatever. It's similiar to Riemers guide, but he's getting a collision of two sprites and I only need those points where alpha is zero.
Better see source:
public Color[,] TextureTo2DArray(Texture2D texture) // to get color array
{
Color[] colors1D = new Color[texture.Width * texture.Height];
texture.GetData(colors1D);
Color[,] colors2D = new Color[texture.Width, texture.Height];
for (int x = 0; x < texture.Width; x++)
for (int y = 0; y < texture.Height; y++)
colors2D[x, y] = colors1D[x + y * texture.Width];
return colors2D;
}
With color is pretty easy, but here is the part where I get points:
public Vector2 TexturePos(Color[,] Color, Matrix matrix)
{
int width1 = Color.GetLength(0);
int height1 = Color.GetLength(1);
for (int x = 0; x < width1; x++)
{
for (int y = 0; y < height1; y++)
{
Vector2 pos1 = new Vector2(x, y);
if (Color[x, y].A > 0)
{
Vector2 screenPos = Vector2.Transform(pos1, matrix);
return screenPos;
}
}
}
return new Vector2(-1, -1);
}
And for matrix I'm using this:
Matrix matrix =
Matrix.CreateTranslation(new Vector3(origin, 0)) *
Matrix.CreateRotationZ(MathHelper.ToRadians(rotation))*
Matrix.CreateScale(scale) *
Matrix.CreateTranslation(new Vector3(pos, 0));
Sprite is rectangular but i get circular movement: I'm rotating it (rotation += 0,5), adding gravity and making it collide with some y value:
Pos.Y += 5;
if (Position.Y >= 200)
BoxPos.Y -= 5;
And I get that it rotates as a circle colliding a line, but not as a rectangle.
Is this normal? Maybe I need some fixes in source?
"That method is supposed to get a position of a pixel (in sprite) that is not transperent but is rotated, scaled (depending on sprite)."
You need to have a look at this:
http://create.msdn.com/en-US/education/catalog/tutorial/collision_2d_perpixel_transformed
This is a great article about 2D collisions in XNA and has an example method that performs 2D collision detection for a Scaled & Rotated set of sprites.

Categories

Resources