using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
namespace SpaceTransformationExample
{
class WorldObject
{
public Texture2D Texture;
public Vector2 TextureOrigin;
public Vector2 PositionInWorldSpace;
public Color color;
public Vector2 velocity;
public Color[] data;
public float RotationInWorldSpace;
static Vector2 rotate(Vector2 v, float theta)
{
return new Vector2((float)(v.X*Math.Cos(theta) - v.Y*Math.Sin(theta)),(float) (v.X*Math.Sin(theta) + v.Y*Math.Cos(theta)));
}
public static float min4(float a, float b, float c, float d)
{
return Math.Min(Math.Min(a,b), Math.Min(c,d));
}
public static float max4(float a, float b, float c, float d)
{
return Math.Max(Math.Max(a,b), Math.Max(c,d));
}
///
/// Given a rectangle defining an AABB (in world space), and a vector from the
/// upper-left corner of the AABB to the rotation point, rotate this AABB around
/// that point, and return the smallest possible AABB that encloses this AABB.
///
/// The AABB to rotate
/// Vector from the upper-left corner of the AABB to the point of rotation
/// Amount to rotate (in radians)
/// New AABB that contains the rotated AABB
public static Rectangle rotateAABB(Rectangle source, Vector2 rotationPoint, float angle)
{
// First, extract the 4 extreme points in the AABB
Vector2 TopLeft = - rotationPoint;
Vector2 TopRight = new Vector2(source.Width-rotationPoint.X, -rotationPoint.Y);
Vector2 BottomLeft = new Vector2(-rotationPoint.X, source.Height -rotationPoint.Y);
Vector2 BottomRight = new Vector2(source.Width - rotationPoint.X, source.Height-rotationPoint.Y);
// Rotate these 4 points
TopLeft = WorldObject.rotate(TopLeft, angle);
TopRight = WorldObject.rotate(TopRight, angle);
BottomLeft = WorldObject.rotate(BottomLeft, angle);
BottomRight = WorldObject.rotate(BottomRight, angle);
// Find the smallest rectangle that contains these 4 points
int left = (int) WorldObject.min4(TopLeft.X, TopRight.X, BottomLeft.X, BottomRight.X);
int right = (int) WorldObject.max4(TopLeft.X, TopRight.X, BottomLeft.X, BottomRight.X);
int top = (int) WorldObject.min4(TopLeft.Y, TopRight.Y, BottomLeft.Y, BottomRight.Y);
int bottom = (int) WorldObject.max4(TopLeft.Y, TopRight.Y, BottomLeft.Y, BottomRight.Y);
return new Rectangle(left, top, right-left, bottom-top);
}
///
/// Given an x,y position in Texture space, return true if that location
/// is a valid location in the texture and contains a non-transparent pixel
///
/// X position in texture space
/// Y position in texture space
/// True if there is a non-trasparent pixel at this location
public bool nonTransparentPixel(int x, int y)
{
if (x < 0 || x >= Texture.Width || y < 0 || y >= Texture.Height)
return false;
return data[x + y * Texture.Width].A > 0;
}
///
/// Given an position in Texture space, return true if that location
/// is a valid location in the texture and contains a non-transparent pixel
///
/// Point in texture space to check
/// True if there is a non-transparent pixel at this location
public bool nonTransparentPixel(Vector2 pointInTexureSpace)
{
return nonTransparentPixel((int) pointInTexureSpace.X, (int) pointInTexureSpace.Y);
}
///
/// Returns the smallest possible Axis Aligned Bounding Box (in world space) that
/// completely contains the sprite
///
///
public Rectangle AABB()
{
Rectangle myAABB = new Rectangle((int)(PositionInWorldSpace.X - TextureOrigin.X), (int)(PositionInWorldSpace.Y - TextureOrigin.Y), Texture.Width, Texture.Height);
if (RotationInWorldSpace == 0)
{
return myAABB;
}
else
{
return WorldObject.rotateAABB(myAABB, TextureOrigin, RotationInWorldSpace);
}
}
///
/// Converrt a point from the object space of this object to the texture space
/// of this object
///
/// Point to convert from object space to texture space
/// The point in texture space
public Vector2 objectSpaceToTextureSpace(Vector2 pointInObjectSpace)
{
return pointInObjectSpace + TextureOrigin;
}
///
/// Converrt a point from the texture space of this object to the object space
/// of this object
///
///
/// The point in object space
public Vector2 TextureSpaceToObjectSpace(Vector2 pointInObjectSpace)
{
return pointInObjectSpace - TextureOrigin;
}
///
/// Converrt a point from world space to the object space
/// of this object
///
/// Point to convert from world space
/// Point in object space
public Vector2 WorldSpaceToObjectSpace(Vector2 pointInWorldSpace)
{
return WorldObject.rotate(pointInWorldSpace - PositionInWorldSpace, -RotationInWorldSpace);
}
///
/// Converrt a point from the object space of this object to
/// world space
///
/// Point to convert from object space to world space
/// Point in world space
public Vector2 ObjectSpaceToWorldSpace(Vector2 pointInObjectSpace)
{
return PositionInWorldSpace + WorldObject.rotate(pointInObjectSpace, RotationInWorldSpace);
}
///
/// Convert a point from the texture space of this object to world space
///
/// The point to convert to world space
/// Point in world space
public Vector2 TextureSpaceToWorldSpace(Vector2 pointInTextureSpace)
{
return ObjectSpaceToWorldSpace(TextureSpaceToObjectSpace(pointInTextureSpace));
}
///
/// Convert a point from world space to the texture space of this object
///
/// The point to conver from world space to texture space
/// Point in texture space
public Vector2 WorldSpaceToTextureSpace(Vector2 pointInWorldSpace)
{
return objectSpaceToTextureSpace(WorldSpaceToObjectSpace(pointInWorldSpace));
}
public bool Collides(WorldObject other)
{
Rectangle myAABB = AABB();
Rectangle otherAABB = other.AABB();
if (myAABB.Intersects(otherAABB))
{
// No rotation case
if (RotationInWorldSpace == 0 && other.RotationInWorldSpace == 0)
{
// Find the top, left, bottom, and right of the rectangle defined by the
// interesction of the two AABBs
int left = Math.Max(myAABB.Left, otherAABB.Left);
int top = Math.Max(myAABB.Top, otherAABB.Top);
int bottom = Math.Min(myAABB.Bottom, otherAABB.Bottom);
int right = Math.Min(myAABB.Right, otherAABB.Right);
// Go through every point in this intersection rectangle, and determine if the
// corresponding points in the other two textures are both non-transparent
for (int i = left; i < right; i++)
{
for (int j = top; j < bottom; j++)
{
int index1 = i - myAABB.Left + (j - myAABB.Top) * Texture.Width;
int index2 = i - otherAABB.Left + (j - otherAABB.Top) * other.Texture.Width;
if (data[index1].A > 0 && other.data[index2].A > 0)
{
return true;
}
}
}
return false;
}
// Rotation case
else
{
// First, find the 4 corners of the other object in my texture space
Vector2 upperLeft = WorldSpaceToTextureSpace(other.TextureSpaceToWorldSpace(new Vector2(0, 0)));
Vector2 upperRight = WorldSpaceToTextureSpace(other.TextureSpaceToWorldSpace(new Vector2(other.Texture.Width, 0)));
Vector2 lowerRight = WorldSpaceToTextureSpace(other.TextureSpaceToWorldSpace(new Vector2(other.Texture.Width, other.Texture.Height)));
Vector2 lowerLeft = WorldSpaceToTextureSpace(other.TextureSpaceToWorldSpace(new Vector2(0, other.Texture.Height)));
// Next, find the AABB that contains these points, in my texture space
int top = (int)min4(upperLeft.Y, upperRight.Y, lowerLeft.Y, lowerRight.Y);
int bottom = (int)max4(upperLeft.Y, upperRight.Y, lowerLeft.Y, lowerRight.Y);
int left = (int)min4(upperLeft.X, upperRight.X, lowerLeft.X, lowerRight.X);
int right = (int)max4(upperLeft.X, upperRight.X, lowerLeft.X, lowerRight.X);
// Next, find the intersection of this AABB and my texture in my texture space
int top2 = Math.Max(top, 0);
int bottom2 = Math.Min(bottom, Texture.Height);
int left2 = Math.Max(0, left);
int right2 = Math.Min(right, Texture.Width);
// Go though each point in this intersection AABB (which has been defined in *my* texture space),
// and find the corresponding point in the other texture. Check if both points are non-transparent.
for (int i = left2; i < right2; i++)
{
for (int j = top2; j < bottom2; j++)
{
Vector2 pointInMyTextureSpace = new Vector2(i, j);
Vector2 pointInOtherTextureSpace = other.WorldSpaceToTextureSpace(TextureSpaceToWorldSpace(pointInMyTextureSpace));
if (nonTransparentPixel(pointInMyTextureSpace) &&
other.nonTransparentPixel(pointInOtherTextureSpace))
{
return true;
}
}
}
return false;
}
}
return false;
}
public WorldObject(Texture2D texture)
{
color = Color.White;
Texture = texture;
TextureOrigin = new Vector2(texture.Width, texture.Height) / 2;
data = new Color[texture.Width * texture.Height];
Texture.GetData(data);
RotationInWorldSpace = 0.0f;
}
public WorldObject(Texture2D texture, Vector2 initialPosition) : this(texture)
{
PositionInWorldSpace = initialPosition;
}
public void Update(GameTime gameTime)
{
PositionInWorldSpace += velocity * (float) gameTime.ElapsedGameTime.TotalSeconds;
if (PositionInWorldSpace.X > Game1.SCREEN_WIDTH)
{
PositionInWorldSpace.X = Game1.SCREEN_WIDTH;
velocity.X = -velocity.X;
}
if (PositionInWorldSpace.X < 0)
{
PositionInWorldSpace.X = 0;
velocity.X = -velocity.X;
}
if (PositionInWorldSpace.Y > Game1.SCREEN_HEIGHT)
{
PositionInWorldSpace.Y = Game1.SCREEN_HEIGHT;
velocity.Y = -velocity.Y;
}
if (PositionInWorldSpace.Y < 0)
{
PositionInWorldSpace.Y = 0;
velocity.Y = -velocity.Y;
}
RotationInWorldSpace = RotationInWorldSpace + (velocity.X - 15) * (float)gameTime.ElapsedGameTime.TotalSeconds / 10.0f;
if (RotationInWorldSpace > 2 * Math.PI)
{
RotationInWorldSpace -= 2 * (float)Math.PI;
}
}
}
}