Array

Windows Phone Collision Detection #Part 1

Must Read

Admin
Admin
Just post!

Detecting the intersection of line segments

Basically, line segments’ intersection is a mathematical concept. To detect the intersection of two line segments, find their intersection points. For 2D games, it is very helpful when an explosion animation appears at a position where the two lines intersect; for example, two laser shoots collide. The line segments’ intersection can also help you to make a decision in pool games as a guideline, especially for beginners. In this recipe, you will learn how to detect the line segments’ intersection.

Getting ready

To define the two line segments, we need the following two formulae:

intersection of line segmentsIf you put in 0 for U, you’ll get the start point, if you put in 1, you’ll get the end point.
With the two equations, if the intersection happens between the two line segments:

The equation could be rewritten as follows:

In order to get the Ua and Ub values, we need two equations. The previous equation could also be written using x and y factors of the points:

You can use the two equations to solve for Ua and Ub:

The denominator of both of the equations is the same. Solve it first. If it is zero, the lines are parallel. If both numerators are also zero, then the two line segments are coincident.

Since these equations treat the lines as infinitely long lines instead of line segments, there is a guarantee of having an intersection point if the lines aren’t parallel. To determine if it happens with the segments we’ve specified, we need to see if U is between zero and one. Verify that both of the following are true:

If we’ve gotten this far, then our line segments intersect, and we just need to find the point at which they do and then we’re done:

The following pseudo code describes the line segments’ intersection algorithm coming from the previous description:

[code]
ua = (p4.x – p3.x)*(p1.y – p3.y) – (p4.y – p3.y)*(p1.x – p3.x)
ub = (p2.x – p1.x)*(p1.y – p3.y) – (p2.y – p1.y)*(p1.x – p3.x)
denominator = (p4.y p2.y)*(p2.x–p1.x) – (p4.x-p2.x)(p2.y-p1.y)
if( | denominator | < epsilon)
{
// Now, two line segments are parallel
If(| ua | <= epsilon && | ub | <= epsilon)
{
// Now, two line segments are coincident
}
}
else
{
ua /= denominator;
ub /= denominator
if( |ua| < 1 && |ub| < 1)
{
// Intersected
intersectionPoint.x = p1.x + ua * (p2.x – p1.x)
intersectionPoint.y = p1.y + ua * (p2.y – p1.y)
}
}
[/code]

Translate the pseudo code to an XNA version, as follows:

[code]
// Line segments’ intersection detection
private void DetectLineSegmentsIntersection(
ref bool intersected, ref bool coincidence,
ref Vector2 intersectedPoint,
ref Vector2 point1, ref Vector2 point2,
ref Vector2 point3, ref Vector2 point4)
{
// Compute the ua, ub factor of two line segments
float ua = (point4.X – point3.X) * (point1.Y – point3.Y) –
(point4.Y – point3.Y) * (point1.X – point3.X);
float ub = (point2.X – point1.X) * (point1.Y – point3.Y) –
(point2.Y – point1.Y) * (point1.X – point3.X);
// Calculate the denominator
float denominator = (point4.Y – point3.Y) * (point2.X –
point1.X) – (point4.X – point3.X)*(point2.Y – point1.Y);
// If the denominator is very close to zero, it means
// the two line segments are parallel
if (Math.Abs(denominator) <= float.Epsilon)
{
// If the ua and ub are very close to zero, it means
// the two line segments are coincident
if (Math.Abs(ua) <= float.Epsilon && Math.Abs(ub) <=
float.Epsilon)
{
intersected = coincidence = true;
intersectedPoint = (point1 + point2) / 2;
}
}
else
{
// If the denominator is greater than zero, it means
// the two line segments have different directions.
ua /= denominator;
ub /= denominator;
// Check the ua and ub are both between 0 and 1 to
// take the line segments’ intersection detection
if (ua >= 0 && ua <= 1 && ub >= 0 && ub <= 1)
{
intersected = true;
// Compute the position of the intersection point
intersectedPoint.X = point1.X +
ua * (point2.X – point1.X);
intersectedPoint.Y = point1.Y +
ua * (point2.Y – point1.Y);
}
else
{
intersected = false;
}
}
}
[/code]

The method receives the four points of the two line segments and two flags for indicating the intersected and coincident states. The first two lines are to compute the numerators of ua and ub, then the following line is on calculating the denominator. After that, we begin to check the value of the denominator to see whether the two line segments intersected. If the denominator is almost zero, it means the two line segments are parallel. Meanwhile, if the numerators of Ua and Ub are both almost zero, the two line segments are coincident. Otherwise, if the denominator is greater than zero, it means the two lines where the two line segments lie intersect. In order to make sure the two line segments intersect, we need to check that the absolute values of ua and ub are both less than or equal to 1. If true, the intersection between them happens; now we can use ua or ub to calculate the intersectionPoint.

How to do it…

The following steps will show you how to master the practical method of using the line segments’ intersection:

  1. Create a Windows Phone Game project named LineSegmentIntersection, change Game1.cs to LineSegmentsIntersectionGame.cs. Then, add Line.cs to the project.
  2. In the next step, we need to define the Line class in Line.cs. The class will draw the line between two points on the Windows Phone 7 screen.
    Declare the indispensable variables of the class, and then add the following code to the class field:
    [code]
    // Line Texture
    private Texture2D lineTexture;
    // Origin point of line texture for translation, scale and
    // rotation
    private Vector2 origin;
    // Scale factor
    private Vector2 scale;
    // Rotation factor
    private float rotation;
    // Axis X
    Vector2 AxisX = new Vector2(1, 0);
    // Dictance vector
    Vector2 distanceVector;
    // Line direction
    Vector2 Direction = Vector2.Zero;
    // The angle between the line and axis X
    float theta = 0;
    // Line thickness
    private int Thickness = 2;
    // Line color
    private Color color;
    [/code]
  3. Create the constructor of the Line class:
    [code]
    public void Load(GraphicsDevice graphicsDevice)
    {
    // Initialize the line texture and its origin point
    lineTexture = CreateLineUnitTexture(graphicsDevice,
    Thickness, color);
    origin = new Vector2(0, Thickness / 2f);
    }
    [/code]
  4. Define the CalculateRotation() method called in the previous step in the Line class. This method calculates the angle between the line and the X-axis:
    [code]
    private void CalculateRotation(Vector2 distanceVector)
    {
    // Normalize the distance vector for line direction
    Vector2.Normalize(ref distanceVector, out Direction);
    // Compute the angle between axis X and line
    Vector2.Dot(ref AxisX, ref Direction, out theta);
    theta = (float)Math.Acos(theta);
    // If the Y factor of distanceVector is less than 0
    // this means the start point is lower than the end point,
    // the rotation direction should be in the opposite
    // direction.
    if (distanceVector.Y < 0)
    {
    theta = -theta;
    }
    // return the angle value for rotation
    rotation = theta;
    }
    [/code]
  5. Implement the CalculateScale() method in the Line class. The method will calculate a scale represented as a Vector2 object. The X factor stores the number of textures while the Y factor stores the scale degree.
    [code]
    private void CalculateScale(Vector2 distanceVector)
    {
    // The Vector2 object scale determines how many textures
    // will be drawn based on the input rotation and start
    // point, X for the number, Y for the scale factor
    float desiredLength = distanceVector.Length();
    scale.X = desiredLength / lineTexture.Width;
    scale.Y = 1f;
    }
    [/code]
  6. Define the CreateLineUnitTexture() method, in the Line class, which creates the line unit texture according to the input line thickness.
    [code]
    // Create a unit texture of line, the texture will be used to
    // generate a line with desired number
    public static Texture2D CreateLineUnitTexture(
    GraphicsDevice graphicsDevice,
    int lineThickness, Color color)
    {
    // Initialize the line unit texture according to the line
    // thickness
    Texture2D texture2D = new Texture2D(graphicsDevice,
    lineThickness, lineThickness, false,
    SurfaceFormat.Color);
    // Set the color of every pixel of the line texture
    int count = lineThickness * lineThickness;
    Color[] colorArray = new Color[count];
    for (int i = 0; i < count; i++)
    {
    colorArray[i] = color;
    }
    texture2D.SetData<Color>(colorArray);
    return texture2D;
    }
    [/code]
  7. Define the Draw() method in the Line class that draws the line segment on Windows Phone 7.
    [code]
    // Draw the line
    public void Draw(SpriteBatch spriteBatch, Vector2 startPoint,
    Vector2 endPoint)
    {
    // Compute the distance vector between the line start
    // point and end point
    Vector2.Subtract(ref endPoint, ref startPoint,
    out distanceVector);
    // Calculate the rotation angle
    CalculateRotation(distanceVector);
    // Calculate the scale factor
    CalculateScale(distanceVector);
    // Draw the line texture on screen
    spriteBatch.Draw(lineTexture, startPoint, null, color,
    rotation, origin, scale, SpriteEffects.None, 0);
    }
    [/code]
  8. From this step, we will begin to interact with the tap gesture and draw the line segments and the intersection point if intersection takes place on the Windows Phone 7 screen. First, add the following lines to the LineSegmentsIntersectionGame class field:
    [code]
    // Line Object
    Line line;
    // Circle Texture
    Texture2D circleTexture;
    // Points of two lines for intersection testing
    Vector2 point1, point2, point3, point4, intersectionPoint;
    // The flag for intersection
    bool Intersection;
    // The flag for coincidence
    bool Coincidence;
    [/code]
  9. Initialize the four points of the two line segments. Insert the following lines to the Initialize() method:
    [code]
    // Initialize the points of two lines
    point1 = Vector2.Zero;
    point2 = newVector2(600, 300);
    point3 = newVector2(0, 200);
    point4 = newVector2(800, 200);
    [/code]
  10. Initialize the line and circleTexture objects. Add the following code to the LoadContent() method:
    [code]
    // Initialize the two line objects with white color
    line = new Line(Color.White);
    line.Load(GraphicsDevice);
    // Initialize the texture of circle
    circleTexture = CreateCircleTexture(GraphicsDevice, 5, Color.
    White);
    [/code]
  11. This step is the key to detecting the line segment intersection. Add the following code to the Update() method:
    [code]
    // Do the line segments’ intersection testing
    DetectLineSegmentsIntersection(ref Intersection,
    ref Coincidence, ref intersectionPoint,
    ref point1, ref point2, ref point3, ref point4);
    // Check the tapped position to see whether it is inside the
    //TopHitRegion
    // or BottomHitRegion
    TouchCollection touches = TouchPanel.GetState();
    if (touches.Count > 0 && touches[0].State ==
    TouchLocationState.Pressed)
    {
    Point point = new Point((int)touches[0].Position.X,
    (int)touches[0].Position.Y);
    if (GraphicsDevice.Viewport.Bounds.Contains(point))
    {
    point1.X = point.X;
    point1.Y = point.Y;
    }
    }
    [/code]
  12. Insert the DetectLineSegmentsIntersection() method into the LineSegmentsIntersectionGame class. This method is the same as the definition we gave at the beginning of the method.
  13. Define the CreateCircleTexture() method. This method creates the circle textures for showing the end points of the line segments:
    [code]
    // Create the circle texture
    public static Texture2D CreateCircleTexture(GraphicsDevice
    graphicsDevice, int radius, Color color)
    {
    int x = 0;
    int y = 0;
    // Compute the diameter of circle
    int diameter = radius * 2;
    // Calculate the center of circle
    Vector2 center = new Vector2(radius, radius);
    // Initialize the circle texture
    Texture2D circle = new Texture2D(graphicsDevice, diameter,
    diameter, false, SurfaceFormat.Color);
    // Initialize the color array of circle texture
    Color[] colors = new Color[diameter * diameter];
    // Set the color of the circle texture
    for (int i = 0; i < colors.Length; i++)
    {
    // For raw
    if (i % diameter == 0)
    {
    y += 1;
    }
    // For column
    x = i % diameter;
    // Calculate the distance from current position to
    // circle center
    Vector2 diff = new Vector2(x, y) – center;
    float distance = diff.Length();
    // Check the position whether inside the circle
    if (distance > radius)
    {
    // If not, set the pixel color to transparent
    colors[i] = Color.Transparent;
    }
    else
    {
    // If yes, set the pixel color to desired color
    colors[i] = color;
    }
    }
    // Assign the processed circle color array to circle
    // texture
    circle.SetData<Color>(colors);
    return circle;
    }
    [/code]
  14. Draw the line segments on screen with end points and intersection point on the Windows Phone 7 screen. Add the following code to the Draw() method:
    [code]
    spriteBatch.Begin();
    // Draw the two lines segments
    line.Draw(spriteBatch, point1, point2);
    line.Draw(spriteBatch, point3, point4);
    // Draw the circles to indicate the endpoints of the two lines
    // with red color
    Vector2 circleOrigin = new Vector2(
    circleTexture.Width / 2, circleTexture.Height / 2);
    spriteBatch.Draw(circleTexture, point1, null, Color.Red, 0,
    circleOrigin, 1, SpriteEffects.None, 0);
    spriteBatch.Draw(circleTexture, point2, null, Color.Red, 0,
    circleOrigin, 1, SpriteEffects.None, 0);
    spriteBatch.Draw(circleTexture, point3, null, Color.Red, 0,
    circleOrigin, 1, SpriteEffects.None, 0);
    spriteBatch.Draw(circleTexture, point4, null, Color.Red, 0,
    circleOrigin, 1, SpriteEffects.None, 0);
    // If the intersection takes place, draw the intersection
    // point
    if (Intersection)
    {
    // If the two lines are coincident, draw the intersection
    // point in green else in yellow
    spriteBatch.Draw(circleTexture, intersectionPoint, null,
    Coincidence ? Color.Green : Color.Yellow, 0,
    circleOrigin, 1, SpriteEffects.None, 0);
    }
    spriteBatch.End();
    [/code]
  15. Now, build and run the application. When you tap on the screen, it should run as shown in the following screenshots:
    intersection of line segments

How it works…

We need to draw the line from a texture since there is no drawing method in XNA 4.0.

In step 2, the lineTexture holds the texture for the drawing line; the origin is the center point for rotation, translation, and scale. The scale, a Vector2 object, the X-factor tells SpriteBatch.Draw() method how many texture units will be drawn. The Y-factor stands for the scaling degree; rotation specifies the rotating degrees; AxisX is the vector represents the X-axis; distanceVector holds the vector between two designated points; Direction indicates the line segment direction; the theta variable shows the angle between the X-axis and the line segment; the Thickness means how thick the line segment should be; color defines the color of the line segment.

In step 4, the first line normalizes the distanceVector, which stores the vector between two points to Direction, a unit vector variable. Then, we use Vector2.Dot() and the Math.Acos() method to calculate the angle between Direction and distanceVector. When we get theta, if the distanceVector.Y is less than 0, this means the start point is lower than the end point, but the theta, which should be a negative angle, is still greater than 0 because in the XNA coordinate system, all the location coordinates are greater than 0, so the dot product is always greater than 0. Thus, we should negate the theta value to meet the actual angle value. Finally, return the theta value to the rotation variable.

In step 5, first distanceVector.Length() returns the length between the two end points of the given line segment. Then, we calculate the number of line unit textures based on the texture width and assign the value to scale.X. After that, we save the scale degree to scale.Y.

In step 6, this method first initializes the line unit texture, of which the size depends on the line thickness. Then, we set the input color to every pixel of the texture. Finally, we return the generated line unit texture.

In step 8, the line will be used to draw the line segments; circle is responsible for drawing the end points and the intersection points of the lines; the Intersection is the flag indicating whether the line segments’ intersection happens; the Coincidence shows whether the line segments are coincident or not.

In step 10, the line has white color; the radius of circle textures is 5.

In step 11, the first line of the method is to detect the line segments’ intersection. The DetectLineSegmentsIntersection() method uses the point1, point2, point3, and point4 to compute the intersection equation. If there is an intersection, the Intersection variable will be true and the intersectionPoint will return the intersected point. We have discussed a more detailed explanation of this method at the beginning of the recipe. The second part is to control the position of the first point to make an interactive manipulation on one of the line segments. If the tapped position is valid, the position of the first point will be changed to the current tapped position on screen.

In step 13, the method first computes diameter for the width and height of the circle textures. The center specifies the center point of the circle. After that, we initialize the circle texture and the texture color array of which the length is diameter*diameter and then we will iterate each pixel in the array. If the position is outside the region of the circle, the pixel color will be set to transparent, or the color what you want.

Implementing per pixel collision detection in a 2D game

In a 2D game, a general method for detecting collision is by using bounding box. This is the solution for a lot of situations where precision is not the most important factor. However, if your game cares whether two irregular objects collide with each other or overlap, the bounding box will not be comfortable with the. At this moment, per pixel collision will help you. In this recipe, you will learn how to use this technique in your game.

How to do it…

  1. Create a Windows Phone Game project named PixelCollision2D, change Game1.cs to PixelCollision2DGame.cs. Then, add the PixelBall.png and PixelScene.png file to the content project.
  2. Add the indispensable data members to the field of PixelCollision2DGame.
    [code]
    // SpriteFont object
    SpriteFont font;
    // The images we will draw
    Texture2D texScene;
    Texture2D texBall;
    // The color data for the images; used for per pixel collision
    Color[] textureDataScene;
    Color[] textureDataBall;
    // Ball position and bound rectangle
    Vector2 positionBall;
    Rectangle boundBall;
    // Scene position and bound rectangle
    Vector2 positionScene;
    Rectangle boundScene;
    // Collision flag
    bool Collided;
    // Ball selected flag
    bool Selected;
    [/code]
  3. Initialize the positions of ball and scene and enable the FreeDrag gesture. Insert the following code in to the Initialize() method:
    [code]
    // Initialize the position of ball
    positionBall = new Vector2(600, 10);
    // Initialize the position of scene
    positionScene = new Vector2(400, 240);
    TouchPanel.EnabledGestures = GestureType.FreeDrag;
    [/code]
  4. Load the textures of ball and scene. Then, extract the color data of these textures and create their bounding box based on the initial position.
    [code]
    // Load Font
    font = Content.Load<SpriteFont>(“gameFont”);
    // Load textures
    texScene = Content.Load<Texture2D>(“PixelScene”);
    texBall = Content.Load<Texture2D>(“PixelBall”);
    // Extract scene texture color array
    textureDataScene =
    new Color[texScene.Width * texScene.Height];
    texScene.GetData(textureDataScene);
    // Extract ball texture color array
    textureDataBall =
    new Color[texBall.Width * texBall.Height];
    texBall.GetData(textureDataBall);
    // Create the ball bound
    boundBall = new Rectangle((int)positionBall.X,
    (int)positionBall.Y,
    texBall.Width, texBall.Height);
    boundScene = new Rectangle((int)positionScene.X,
    (int)positionScene.Y, texScene.Width, texScene.Height);
    [/code]
  5. Define the IntersectPixels() method. This method determines if there is overlap of the non-transparent pixels between two textures.
    [code]
    static bool IntersectPixels(
    Rectangle rectangleA, Color[] dataA,
    Rectangle rectangleB, 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;
    }
    [/code]
  6. Call the IntersectPixels() method within the Update()method for examining the per pixel collision. Add the following code to the Update() method:
    [code]
    // Move the ball
    TouchCollection touches = TouchPanel.GetState();
    if (touches.Count > 0 && touches[0].State ==
    TouchLocationState.Pressed)
    {
    Point point = new Point((int)touches[0].Position.X,
    (int)touches[0].Position.Y);
    if (boundBall.Contains(point))
    {
    Selected = true;
    }
    else
    {
    Selected = false;
    }
    }
    // Check whether the gesture is enabled
    while (TouchPanel.IsGestureAvailable)
    {
    // Read the taking place gesture
    GestureSample gestures = TouchPanel.ReadGesture();
    switch (gestures.GestureType)
    {
    // If the on-going gesture is FreeDrag
    case GestureType.FreeDrag:
    if (Selected)
    {
    // If the ball is selected, update the
    // position of ball texture and the ball bound
    positionBall += gestures.Delta;
    boundBall.X += (int)gestures.Delta.X;
    boundBall.Y += (int)gestures.Delta.Y;
    }
    break;
    }
    }
    // Check collision with Scene
    if (IntersectPixels(boundBall, textureDataBall,
    boundScene, textureDataScene))
    {
    Collided = true;
    }
    else
    {
    Collided = false;
    }
    }
    [/code]
  7. Draw the ball, scene, and collision state on the Windows Phone 7 screen.
    [code]
    spriteBatch.Begin();
    // Draw the scene
    spriteBatch.Draw(texScene, boundScene, Color.White);
    // Draw the ball
    spriteBatch.Draw(texBall, positionBall, Color.White);
    spriteBatch.DrawString(font, “Collided: ” +
    Collided.ToString(), new Vector2(0, 0), Color.White);
    spriteBatch.End();
    [/code]
  8. Now, build and run the application. It will run as shown in the following screenshots:
    Implementing per pixel collision

How it works…

In step 2, the font is used to draw the collision state; texScene loads the scene image; texBall holds the ball texture; textureDataScene and textureDataBall store their texture color array data; positionBall and positionScene specify the position of ball and scene textures; boundBall and boundScene define the bound around the ball and scene texture; Collided is the flag that shows the collision state; Selected indicates whether the ball is tapped.

In step 5, the IntersectPixels() method is the key method that detects the per pixel collision. The first four variables top, bottom, left, and right individually represent the top, bottom, left, and right side of the intersection rectangle of the two bound boxes around the two textures in the example. Then, in the for loop, we check whether the color alpha value of every pixel of both the textures within the intersection rectangle is completely transparent. If yes, the collision occurs; the method will return true, otherwise, it will return false.

In step 6, the first part is to check whether the ball is selected. If yes, then Selected will be true. The second part is about reading the on-going gesture; if the gesture type is FreeDrag, we will update the position of the ball and its bounding box. The third part calls the IntersectPixels() method to detect the pixel-by-pixel collision.

Implementing BoundingBox collision detection in a 3D game

Regardless of whether you are programming 2D or 3D games, collision detection based on bounding box is straightforward, i simple and easy to understand. You can imagine that every object is individually covered by a box. The boxes are moving along with the corresponding boxes; when the boxes collide, the objects collide too. The boxes are called the BoundingBox. To compose the BoundingBox, you only need to go through all the points or vertices, and then find the min and max ones. After that, the BoundingBox collision detection will depend on the min and max information of every BoundingBox to examine whether their min and max values are inside its own range to make the collision decision. Even in a more accurate collision detection system, bounding box collision detection will be taken first, before using the more precise, but costly method. In this recipe, you will learn how to apply the technique to a simple game.

How to do it…

The following steps will help you build your own BoundingBox information content processor and use the BoundingBox in your game:

  1. Create a Windows Phone Game project named BoundingBoxCollision and change Game1.cs to BoundingBoxCollisionGame.cs. Then, create a Content Pipeline Extension Library named MeshVerticesProcessor and replace the ContentProcessor1.cs with MeshVerticesProcessor.cs. We create the content pipeline processor for processing and extracting the BoundingBox information from the model objects before running the game. This will accelerate the game loading speed because your application won’t need to do this work again and again. After that, add the model file BigBox.FBX to the content project.
  2. Next, we need to define the MeshVerticesProcessor class in MeshVerticesProcessor.cs of the MeshVerticesProcessor project. Extend the MeshVerticesProcessor class from ModelProcessor, because we need the model vertices information based on the original model.
    [code]
    [ContentProcessor]
    publicclass MeshVerticesProcessor : ModelProcessor
    [/code]
  3. Add the Dictionary object in the class field.
    [code]
    Dictionary<string, List<Vector3>> tagData =
    new Dictionary<string, List<Vector3>>();
    [/code]
  4. Define the Process() method in the MeshVerticesProcessor class:
    [code]
    // The main method in charge of processing the content.
    public override ModelContent Process(NodeContent input,
    ContentProcessorContext context)
    {
    FindVertices(input);
    ModelContent model = base.Process(input, context);
    model.Tag = tagData;
    return model;
    }
    [/code]
  5. Define the FindVertices() method in the MeshVerticesProcessor class:
    [code]
    // Extracting a list of all the vertex positions in
    // a model.
    void FindVertices(NodeContent node)
    {
    // Transform the current NodeContent to MeshContent
    MeshContent mesh = node as MeshContent;
    if (mesh != null)
    {
    string meshName = mesh.Name;
    List<Vector3> meshVertices = new List<Vector3>();
    // Look up the absolute transform of the mesh.
    Matrix absoluteTransform = mesh.AbsoluteTransform;
    // Loop over all the pieces of geometry in the mesh.
    foreach (GeometryContent geometry in mesh.Geometry)
    {
    // Loop over all the indices in this piece of
    // geometry. Every group of three indices
    // represents one triangle.
    foreach (int index in geometry.Indices)
    {
    // Look up the position of this vertex.
    Vector3 vertex =
    geometry.Vertices.Positions[index];
    // Transform from local into world space.
    vertex = Vector3.Transform(vertex,
    absoluteTransform);
    // Store this vertex.
    meshVertices.Add(vertex);
    }
    }
    tagData.Add(meshName, meshVertices);
    }
    // Recursively scan over the children of this node.
    foreach (NodeContent child in node.Children)
    {
    FindVertices(child);
    }
    }
    [/code]
  6. Build the MeshVerticesProcessor project. Add a reference to MeshVerticesProcessor.dll in the content project and change the Content Processor of BigBox.FBX to MeshVerticesProcessor, as shown in the following screenshot:
    Content Processor of BigBox
  7. From this step, we will begin to draw the two boxes on screen and detect the bounding box collision between them in the BoundingBoxCollisionGame class in BoundingBoxCollisionGame.cs of the BoundingBoxCollision project. First, add the following lines to the class field:
    [code]
    // The sprite font for drawing collision state
    SpriteFont font;
    // Model box A and B
    Model modelBoxA;
    Model modelBoxB;
    // The world transformation of box A and B
    Matrix worldBoxA;
    Matrix worldBoxB;
    // BoundingBox of model A and B
    BoundingBox boundingBoxA;
    BoundingBox boundingBoxB;
    // The bounding box stores the transformed boundingBox
    BoundingBox boundingBox;
    // Camera
    Vector3 cameraPosition;
    Matrix view;
    Matrix projection;
    // Hit regions
    Rectangle LeftHitRegion;
    Rectangle RightHitRegion;
    // Collided state
    bool Collided;
    [/code]
  8. Initialize the world matrix of box A and B, the camera, and the left and right hit regions. Paste the following code into the Initialize() method in the BoundingBoxCollisionGame class:
    [code]
    // Translate the model box A and B
    worldBoxA = Matrix.CreateTranslation(new Vector3(-10, 0, 0));
    worldBoxB = Matrix.CreateTranslation(new Vector3(10, 0, 0));
    // Initialize the camera
    cameraPosition = new Vector3(0, 10, 40);
    view = Matrix.CreateLookAt(cameraPosition, Vector3.Zero,
    Vector3.Up);
    projection = Matrix.CreatePerspectiveFieldOfView(
    MathHelper.PiOver4, GraphicsDevice.Viewport.AspectRatio,
    0.1f, 1000.0f);
    // Initialize the left and right hit regions
    Viewport viewport = GraphicsDevice.Viewport;
    LeftHitRegion = new Rectangle(0, 0, viewport.Width / 2,
    viewport.Height);
    RightHitRegion = new Rectangle(viewport.Width / 2, 0,
    viewport.Width / 2, viewport.Height);
    [/code]
  9. Load the box model and sprite font. Then extract the box model vertices. With the extracted vertices, create the bounding box for box A and B. Insert the following code into the LoadContent() method in the BoundingBoxCollisionGame class:
    [code]
    // Create a new SpriteBatch, which can be used to draw
    // textures.
    spriteBatch = new SpriteBatch(GraphicsDevice);
    // Load the sprite font
    font = Content.Load<SpriteFont>(“gameFont”);
    // Load the box model
    modelBoxA = Content.Load<Model>(“BigBox”);
    modelBoxB = Content.Load<Model>(“BigBox”);
    // Get the vertices of box A and B
    List<Vector3> boxVerticesA =
    ((Dictionary<string, List<Vector3>>)modelBoxA.Tag)
    [“Box001”];
    List<Vector3> boxVerticesB =
    ((Dictionary<string, List<Vector3>>)modelBoxA.Tag)
    [“Box001”];
    // Create the bounding box for box A and B
    boundingBoxA = BoundingBox.CreateFromPoints(boxVerticesA);
    boundingBoxB = BoundingBox.CreateFromPoints(boxVerticesB);
    // Translate the bounding box of box B to designated position
    boundingBoxB.Min = Vector3.Transform(boundingBoxB.Min,
    worldBoxB);
    boundingBoxB.Max = Vector3.Transform(boundingBoxB.Max,
    worldBoxB);
    [/code]
  10. Move the box A and the corresponding bounding box and detect the bounding box collision between box A and box B. Add the following code to the Update() method in the BoundingBoxCollisionGame class:
    [code]
    // Interact with tapping
    TouchCollection touches = TouchPanel.GetState();
    if (touches.Count > 0 && touches[0].State ==
    TouchLocationState.Pressed)
    {
    Point point = new Point(
    (int)touches[0].Position.X,(int)touches[0].Position.Y);
    // If the tapped position is inside the left hit region,
    // move the box A left
    if (LeftHitRegion.Contains(point))
    {
    worldBoxA.Translation -= new Vector3(1, 0, 0);
    }
    // If the tapped position is inside the right hit region,
    //move the box A right
    if (RightHitRegion.Contains(point))
    {
    worldBoxA.Translation += new Vector3(1, 0, 0);
    }
    }
    // Create a bounding box for the transformed bounding box A
    boundingBox = new BoundingBox(
    Vector3.Transform(boundingBoxA.Min, worldBoxA),
    Vector3.Transform(boundingBoxA.Max, worldBoxA));
    // Take the collision detection between the transformed
    // bounding box A and bounding box B
    if (boundingBox.Intersects(boundingBoxB))
    {
    Collided = true;
    }
    else
    {
    Collided = false;
    }
    [/code]
  11. Define the DrawModel() method.
    [code]
    // Draw model
    public void DrawModel(Model model, Matrix world, Matrix view,
    Matrix projection)
    {
    Matrix[] transforms = new Matrix[model.Bones.Count];
    model.CopyAbsoluteBoneTransformsTo(transforms);
    foreach (ModelMesh mesh in model.Meshes)
    {
    foreach (BasicEffect effect in mesh.Effects)
    {
    effect.PreferPerPixelLighting = true;
    effect.EnableDefaultLighting();
    effect.DiffuseColor = Color.White.ToVector3();
    effect.World = transforms[mesh.ParentBone.Index] *
    world;
    effect.View = view;
    effect.Projection = projection;
    }
    mesh.Draw();
    }
    }
    [/code]
  12. Draw the boxes on the Windows Phone 7 screen. Add the code to the Draw() method.
    [code]
    GraphicsDevice.DepthStencilState = DepthStencilState.Default;
    // Draw the box model A and B
    DrawModel(modelBoxA, worldBoxA, view, projection);
    DrawModel(modelBoxB, worldBoxB, view, projection);
    // Draw the collision state
    spriteBatch.Begin();
    spriteBatch.DrawString(font, “Collided: ” + Collided.
    ToString(), new Vector2(0, 0), Color.White);
    spriteBatch.End();
    [/code]
  13. Now, build and run the application. The application should run as shown in the following screenshots:
    BoundingBox collision detection

How it works…

In step 2, the [ContentProcessor] attribute is required. It makes the MeshVerticesProcessor class in to a content processor, which will show up in the content project when you change the model processor.

In step 3, the tagData receives the mesh name as the key and the corresponding mesh vertices as the value.

In step 4, the input—a NodeContent object—represents the root NodeContent of the input model. The key called method is the FindVertices() method, which iterates the meshes in the input model and stores the mesh vertices in the tagData with the mesh name.

In step 5, the first line transforms the current NodeContent to MeshContent so that we can get the mesh vertices. If the current NodeContent is MeshContent, declare meshName variable for holding the current mesh name, meshVertices saves the mesh vertices and stores the world absolute transformation matrix to the absoluteTransform matrix using MeshContent.AbsoluteTransform. The following foreach loop iterates every vertex of the model geometries and transforms it from object coordinate to world coordinate; it then stores the current vertex to the meshVertices. When all the vertices of the current mesh are processed, we add the meshVertices to the tagData dictionary with the meshName as the key. The last part is to recursively process the vertices of the child NodeContent objects of the temporary MeshContent.

In step 7, the font is responsible for drawing the collision state on screen; modelBoxA and modelBoxB hold the two box models; worldBoxA and worldBoxB represent the world transformation of the two boxes; boundingBoxA and boundingBoxB store the bound boxes individually around the two boxes; boundingBox will save the transformed bounding box A for collision detection; the cameraPosition, view, and projection will be used to initialize the camera; LeftHitRegion and RightHitRegion define the left and right hit regions on the Windows Phone 7 screen.

In step 9, in this method, we read the vertices of box A and B from the Model.Tag property. Then, we use BoundingBox.CreateFromPoints() to create the bounding box from the extracted vertices of the box model. Notice, so far, the generated bounding boxes are in the same place; we need to translate them to the place where the corresponding box model locates. Since we will use box A as the moving object, the position will be updated in real time. Now, we just translate the bounding box for box B.

In step 10, in the first part, we check whether the tapped position is in the left or right hit region and move box A. After that, we create a new boundingbox for representing the transformed bounding box A. Then, we take the bounding box collision detection between the boundingBoxA and boundingBoxB using the BoundingBox.Intersects() method. If a collision happens, the method will return true, otherwise it will return false.

Implementing BoundingSphere collision detection in a 3D game

Unlike the bounding box, bounding sphere based collision detection is faster. The technique just needs to compute the length between two points or vertices whether less, equal, or greater than the sum of radii. In modern games, bounding sphere based collision detection is preferred rather than the bounding box. In this recipe, you will learn how to use the technique in an XNA application.

How to do it…

Follow the steps below to master the technique of using BoundingSphere in your game:

  1. Create a Windows Phone Game project named BoundingSphereCollision and change Game1.cs to BoundingSphereCollisionGame.cs. Then, create a Content Pipeline Extension Library named MeshVerticesProcessor and replace the ContentProcessor1.cs with MeshVerticesProcessor.cs. After that, add the model file BallLowPoly.FBX to the content project.
  2. Define the MeshVerticesProcessor class in MeshVerticesProcessor.cs of the MeshVerticesProcessor project. The class is the same as the one mentioned in the last recipe Implementing BoundingBox collision detection in a 3D game. For a full explanation, please refer back to it.
  3. Build the MeshVerticesProcessor project. Add a reference to MeshVerticesProcessor.dll in the content project and change the Content Processor of BallLowPoly.FBX to MeshVerticesProcessor, as shown in the following screenshot:
    Content Processor of BallLowPoly.FBX
  4. From this step, we will begin to draw the two balls on screen and detect the bounding sphere collision between them in the BoundingSphereCollisionGame class in BoundingSphereCollisionGame.cs of the BoundingSphereCollision project. First, add the following lines to the class field:
    [code]
    // The sprite font for drawing collision state
    SpriteFont font;
    // Model ball A and B
    Model modelBallA;
    Model modelBallB;
    // The world transformation of ball A and B
    Matrix worldBallA;
    Matrix worldBallB;
    // BoundingSphere of model A and B
    BoundingSphere boundingSphereA;
    BoundingSphere boundingSphereB;
    // Camera
    Vector3 cameraPosition;
    Matrix view;
    Matrix projection;
    // Hit regions
    Rectangle LeftHitRegion;
    Rectangle RightHitRegion;
    // Collided state
    bool Collided;
    [/code]
  5. Load the ball model and sprite font. Then, extract the vertices of the ball model. With the extracted vertices, create the bounding spheres for ball A and B. Insert the following code into the LoadContent()method:
    [code]
    // Translate the model ball A and B
    worldBallA = Matrix.CreateTranslation(new Vector3(-10, 0, 0));
    worldBallB = Matrix.CreateTranslation(new Vector3(10, 0, 0));
    // Initialize the camera
    cameraPosition = new Vector3(0, 10, 40);
    view = Matrix.CreateLookAt(cameraPosition, Vector3.Zero,
    Vector3.Up);
    projection = Matrix.CreatePerspectiveFieldOfView(
    MathHelper.PiOver4, GraphicsDevice.Viewport.AspectRatio,
    0.1f, 1000.0f);
    // Initialize the left and right hit regions
    Viewport viewport = GraphicsDevice.Viewport;
    LeftHitRegion = new Rectangle(0, 0, viewport.Width / 2,
    viewport.Height);
    RightHitRegion = new Rectangle(viewport.Width / 2, 0,
    viewport.Width / 2, viewport.Height);
    [/code]
  6. Move the ball A and the corresponding bounding sphere. Then, detect the bounding sphere collision between ball A and B. Add the following code to the Update() method:
    [code]
    // Check the tapped position
    TouchCollection touches = TouchPanel.GetState();
    if (touches.Count > 0 && touches[0].State ==
    TouchLocationState.Pressed)
    {
    Point point = new Point((int)touches[0].Position.X,
    (int)touches[0].Position.Y);
    // If the tapped position is inside the left hit region,
    // move ball A to left
    if (LeftHitRegion.Contains(point))
    {
    worldBallA.Translation -= new Vector3(1, 0, 0);
    }
    // If the tapped position is inside the left right region,
    // move the ball A right
    if (RightHitRegion.Contains(point))
    {
    worldBallA.Translation += new Vector3(1, 0, 0);
    }
    }
    // Update the position of bounding sphere A
    boundingSphereA.Center = worldBallA.Translation;
    // Detect collision between bounding sphere A and B
    if (boundingSphereA.Intersects(boundingSphereB))
    {
    Collided = true;
    }
    else
    {
    Collided = false;
    }
    [/code]
  7. Define the DrawModel() method.
    [code]
    // Draw model
    public void DrawModel(Model model, Matrix world, Matrix view,
    Matrix projection)
    {
    Matrix[] transforms = new Matrix[model.Bones.Count];
    model.CopyAbsoluteBoneTransformsTo(transforms);
    foreach (ModelMesh mesh in model.Meshes)
    {
    foreach (BasicEffect effect in mesh.Effects)
    {
    effect.PreferPerPixelLighting = true;
    effect.EnableDefaultLighting();
    effect.DiffuseColor = Color.White.ToVector3();
    effect.World = transforms[mesh.ParentBone.Index] *
    world;
    effect.View = view;
    effect.Projection = projection;
    }
    mesh.Draw();
    }
    }
    [/code]
  8. Draw the spheres on screen. Add the following code to the Draw() method.
    [code]
    GraphicsDevice.DepthStencilState = DepthStencilState.Default;
    // Draw the ball model A and B
    DrawModel(modelBallA, worldBallA, view, projection);
    DrawModel(modelBallB, worldBallB, view, projection);
    // Draw the collision state
    spriteBatch.Begin();
    spriteBatch.DrawString(font, “Collided:” + Collided.ToString(),
    new Vector2(0, 0), Color.White);
    spriteBatch.End();
    [/code]
  9. Now, build and run the application. The application should run as shown in the following screenshots:
    BoundingSphere collision detection in a 3D game

How it works…

In step 4, the font is responsible for drawing the collision state on screen; modelBallA and modelBallB hold the two box models; worldBallA and worldBallB represent the world transformation of the two boxes; boundingSphereA and boundingSphereB store the bound boxes individually around the two boxes; the cameraPosition, view, and projection will be used to initialize the camera; LeftHitRegion and RightHitRegion define the left and right hit regions on the Windows Phone 7 screen.

In step 6, the first part is to check whether the tapped position is in the left or the right hit region and to move ball A. After that, we update the center position of bounding sphere A with the newest position of ball A. Then, we take the bounding sphere collision detection between the boundingSphereA and boundingSphereB using the BoundingSphere. Intersects() method. If the collision happens, the method will return true, otherwise it will return false.

Implementing ray-triangle collision detection

Ray-triangle collision gives very accurate collision detection in games. Depending on the return value of the distance from the ray start position to the triangle, it is easy for you to decide whether a collision occurs. As you might know, all the models in 3D games are made of triangles, whether static or dynamic. The ray is like a bullet fired from a gun; here, you can consider the gun as another object, with a straight thin rope behind. Once the bullet hits the triangle—an object, the collision happens. A lot of methods on ray-triangle are available; in this recipe, you will learn how to implement the method which has the best time and space complexity to make your game run faster with less memory usage.

Getting ready…

The ray-triangle collision detection method provides more accurate data than other methods using BoundingBox or BoundingSphere. Before the best ray-triangle collision detection method was invented by Moller and Trumbore, most of the existing methods first compute the intersection point between the ray and the triangle’s plane. After that, the intersection point will be projected to the axis-aligned plane to determine whether it is inside the 2D projected triangle. These kinds of methods need the plain equation of triangle based on the computed normal every frame, for a triangle mesh; to do this will cost considerable memory space and CPU resources. However, the method from Moller and Trumbore requires only two cross product computations and also gives us an intersection point.

As a detailed explanation, a point v in a triangle is represented by Barycentric coordinates and not Cartesian coordinates. Since the Barycentric coordinate is the most suitable coordinate system to describe a point position in a triangle, the point could be represented by the following formula:

The u and v coordinates—two of the Barycentric coordinates—are also used in texture mapping, normal interpolation like the Phong lighting algorithm, and color interpolation.

For a ray, a point on the ray is given by:

The intersection point between the ray and the triangle means the point is both on the ray and the triangle. To get the point, we have the formula:

We rearrange the previous equation to an expression in matrix notation:

The previous equation means the distance t from the ray origin to the intersection point and the Barycentric coordinate (u,v) can be found in the equation solution. If

is a matrix M, our job is to find the M-1. The equation will be:

Now, let

With the Cramer’s rule, we find the following solution:

From linear algebra, the determinant is computed using the Triple Product:

The solution can be rewritten as follows:

The following pseudo code describing the algorithm comes from the solution of the previous equation:

[code]
E1 = P1 – P0;
E2 = P2 – P0;
P = D E2;
determinate = P E1;
if(determinate > -epsilon && determinate < epsilon)
return null;
inverse = 1 / determinate;
S = O – P0
u = P S * inverse;
if( u < 0) return null;
Q = S E1;
v = Q D * inverse
if(v < 0) return null;
t = Q E2 * inverse;
if( t < 0) return null;
return (t, u, v)
[/code]

The XNA code to implement the pseudo code should be:

[code]
public void RayIntersectsTriangle(ref Ray ray,
ref Vector3 vertex1,
ref Vector3 vertex2,
ref Vector3 vertex3, out float? result)
{
// Compute vectors along two edges of the triangle.
Vector3 edge1, edge2;
Vector3.Subtract(ref vertex2, ref vertex1, out edge1);
Vector3.Subtract(ref vertex3, ref vertex1, out edge2);
// Compute the determinant.
Vector3 directionCrossEdge2;
Vector3.Cross(ref ray.Direction, ref edge2,
out directionCrossEdge2);
float determinant;
Vector3.Dot(ref edge1, ref directionCrossEdge2,
out determinant);
// If the ray is parallel to the triangle plane, there is
// no collision.
if (determinant > -float.Epsilon &&
determinant < float.Epsilon)
{
result = null;
return;
}
float inverseDeterminant = 1.0f / determinant;
// Calculate the U parameter of the intersection point.
Vector3 distanceVector;
Vector3.Subtract(ref ray.Position, ref vertex1,
out distanceVector);
float triangleU;
Vector3.Dot(ref distanceVector, ref directionCrossEdge2,
out triangleU);
triangleU *= inverseDeterminant;
// Make sure it is inside the triangle.
if (triangleU < 0 || triangleU > 1)
{
result = null;
return;
}
// Calculate the V parameter of the intersection point.
Vector3 distanceCrossEdge1;
Vector3.Cross(ref distanceVector, ref edge1,
out distanceCrossEdge1);
float triangleV;
Vector3.Dot(ref ray.Direction, ref distanceCrossEdge1,
out triangleV);
triangleV *= inverseDeterminant;
// Make sure it is inside the triangle.
if (triangleV < 0 || triangleU + triangleV > 1)
{
result = null;
return;
}
// Compute the distance along the ray to the triangle.
float rayDistance;
Vector3.Dot(ref edge2, ref distanceCrossEdge1,
out rayDistance);
rayDistance *= inverseDeterminant;
// Is the triangle behind the ray origin?
if (rayDistance < 0)
{
result = null;
return;
}
result = rayDistance;
}
[/code]

As the parameters of the RayIntersectsTriangle() method, the vertex1, vertex2, and vertex3 are the three points of a triangle, ray is the object of the XNA built-in type ray, which specifies the origin point and the ray direction; the result will return the distance between the ray start point and the intersection point. In the body of the method, the first three lines compute the two triangle edges, then they use the ray.Direction and edge2 to compute the cross product directionCrossEdge1 that represents P, which equals P=D E2. Next, we use directionCrossEdge2 that takes the dot multiplication with edge1 to compute the determinate with the equation determinate=P E1. The following if statement is to validate the determinate. If the value tends to 0, the determinate will be rejected. Then, we use inverseDeterminant to represent the following fraction:

Now you have got the denominator of the fraction of Cramer’s rule. With the value, the u, v, and t could be solved as the solution equation. Following the pseudo code, the next step is to calculate the P with equation S=O-P0. Here, ray.Position is o, vertex1 is P0, distanceVector is S. Based on the S value, you could get the u value from the equation u=P S*inverse, the code calls the Vector3.Dot() method between directionCrossEdge2 and distanceVector for the intermediate TriangleU, then the returned value multiplies with the inverseDeterminant for the final TriangleU. The v value triangleV comes from the equation v=Q D*inverse in which Q=S E1. Similarly, you could gain the t value rayDistance from the equation t=Q E2*inverse.

How to do it…

Now, let’s look into an example for a direct experience:

  1. Create a Windows Phone Game project named RayTriangleCollisionGame, change Game1.cs to RayTriangleCollisionGame.cs. Then, add the gameFont.spritefont file to the content project.
  2. Declare the necessary variables of the RayTriangleCollisionGame class. Add the following lines to the class field:
    [code]
    // SpriteFont draw the instructions
    SpriteFont font;
    // Triangle vertex array
    VertexPositionColor[] verticesTriangle;
    VertexBuffer vertexBufferTriangle;
    // Line vertex array
    VertexPositionColor[] verticesLine;
    VertexBuffer vertexBufferLine;
    Matrix worldRay = Matrix.CreateTranslation(-10, 0, 0);
    // Camera view matrix
    Matrix view;
    // Camera projection matrix
    Matrix projection;
    // Ray object
    Ray ray;
    // Distance
    float? distance;
    // Left region on screen
    Rectangle LeftRectangle;
    // Right region on screen
    Rectangle RightRectangle;
    // Render state
    RasterizerState Solid = new RasterizerState()
    {
    FillMode = FillMode.Solid,
    CullMode = CullMode.None
    };
    [/code]
  3. Initialize the camera and the hit regions. Insert the code into the Initialize()method:
    [code]
    view = Matrix.CreateLookAt(new Vector3(20, 5, 20),
    Vector3.Zero, Vector3.Up);
    projection = Matrix.CreatePerspectiveFieldOfView(
    MathHelper.PiOver4,
    GraphicsDevice.Viewport.AspectRatio, 0.1f, 1000.0f);
    LeftRectangle = new Rectangle(0, 0,
    GraphicsDevice.Viewport.Bounds.Width / 2,
    GraphicsDevice.Viewport.Bounds.Height);
    RightRectangle = new Rectangle(
    GraphicsDevice.Viewport.Bounds.Width / 2, 0,
    GraphicsDevice.Viewport.Bounds.Width / 2,
    GraphicsDevice.Viewport.Bounds.Height);
    [/code]
  4. Initialize the vertices and vertex buffer of the triangle and the line. Then, instance the ray object. Add the following code to the LoadContent() method:
    [code]
    // Load the font
    font = Content.Load<SpriteFont>(“gameFont”);
    // Create a triangle
    verticesTriangle = new VertexPositionColor[3];
    verticesTriangle[0] = new VertexPositionColor(
    new Vector3(0, 0, 0), Color.Green);
    verticesTriangle[1] = new VertexPositionColor(
    new Vector3(10, 0, 0), Color.Green);
    verticesTriangle[2] = new VertexPositionColor(
    new Vector3(5, 5, 0), Color.Green);
    // Allocate the vertex buffer for triangle vertices
    vertexBufferTriangle = new VertexBuffer(
    GraphicsDevice, VertexPositionColor.VertexDeclaration, 3,
    BufferUsage.WriteOnly);
    // Set the triangle vertices to the vertex buffer of triangle
    vertexBufferTriangle.SetData(verticesTriangle);
    // Create the line
    verticesLine = new VertexPositionColor[2];
    verticesLine[0] = new VertexPositionColor(
    new Vector3(5, 2.5f, 10), Color.Red);
    verticesLine[1] = new VertexPositionColor(
    new Vector3(5, 2.5f, -10), Color.Red);
    // Allocate the vertex buffer for line points
    vertexBufferLine = new VertexBuffer(GraphicsDevice,
    VertexPositionColor.VertexDeclaration, 2,
    BufferUsage.WriteOnly);
    // Set the line points to the vertex buffer of line
    vertexBufferLine.SetData(verticesLine);
    // Compute the ray direction
    Vector3 rayDirection = verticesLine[1].Position –
    verticesLine[0].Position;
    rayDirection.Normalize();
    // Initialize the ray with position and direction
    ray = new Ray(verticesLine[0].Position, rayDirection);
    // Transform the ray
    ray.Position = Vector3.Transform(ray.Position, worldRay);
    [/code]
  5. Perform the ray-triangle collision detection. Paste the following code into the Update() method:
    [code]
    TouchCollection touches = TouchPanel.GetState();
    if (touches.Count > 0 && touches[0].State ==
    TouchLocationState.Pressed)
    {
    Point point = new Point((int)touches[0].Position.X,
    (int)touches[0].Position.Y);
    if (LeftRectangle.Contains(point))
    {
    worldRay *= Matrix.CreateTranslation(
    new Vector3(-1, 0, 0));
    ray.Position.X -= 1;
    }
    if (RightRectangle.Contains(point))
    {
    worldRay *= Matrix.CreateTranslation(
    new Vector3(1, 0, 0));
    ray.Position.X += 1;
    }
    }
    RayIntersectsTriangle(
    ref ray,
    ref verticesTriangle[0].Position,
    ref verticesTriangle[1].Position,
    ref verticesTriangle[2].Position,
    out distance);
    if (distance != null)
    {
    verticesTriangle[0].Color = Color.Yellow;
    verticesTriangle[1].Color = Color.Yellow;
    verticesTriangle[2].Color = Color.Yellow;
    }
    else
    {
    verticesTriangle[0].Color = Color.Green;
    verticesTriangle[1].Color = Color.Green;
    verticesTriangle[2].Color = Color.Green;
    }
    vertexBufferTriangle.SetData(verticesTriangle);
    [/code]
  6. Define the DrawColoredPrimitives() method in the RayTriangleCollisionGame class, for drawing the line and triangle on the Windows Phone 7 screen.
    [code]
    public void DrawColoredPrimitives(VertexBuffer buffer,
    PrimitiveType primitiveType, int primitiveCount,
    Matrix world)
    {
    BasicEffect effect = new BasicEffect(GraphicsDevice);
    effect.VertexColorEnabled = true;
    effect.World = world;
    effect.View = view;
    effect.Projection = projection;
    effect.CurrentTechnique.Passes[0].Apply();
    GraphicsDevice.SetVertexBuffers(buffer);
    GraphicsDevice.DrawPrimitives(primitiveType, 0,
    primitiveCount);
    }
    [/code]
  7. Draw the ray and triangle on the Windows Phone 7 screen. Insert the following code into the Draw()method:
    [code]
    GraphicsDevice.RasterizerState = Solid;
    // Draw the triangle
    DrawColoredPrimitives(vertexBufferTriangle,
    PrimitiveType.TriangleList, 1, Matrix.Identity);
    // Draw the line which visualizes the ray
    DrawColoredPrimitives(vertexBufferLine, PrimitiveType.LineList,
    1, worldRay);
    spriteBatch.Begin();
    spriteBatch.DrawString(font,
    “Tap the Left or Right Part of nScreen to Move the ray”,
    new Vector2(0, 0), Color.White);
    spriteBatch.End();
    [/code]
  8. Now, build and run the application. It runs as shown in the following screenshots:
    ray-triangle

How it works…

In step 2, the font object will draw the instruction about how to play with the example on screen; verticesTriangle is the vertex array of the testing triangle; vertexBufferTriangle is the vertex buffer that stores the triangle vertices; verticesLine holds the two points of a line, which visually represents the testing ray; the matrix worldRay stands for the ray world position. The following two matrices view and projection will be used to define the camera; the ray object will be the real tested ray; the distance indicates the actual distance from ray origin position to the ray-triangle intersection point; the LeftRectangle and RightRectangle are the hit regions for moving the ray to the left or to the right. The Solid variable specifies the render state of the graphics device.

In step 3, the LeftRectangle occupies the left half of the screen; the RightRectangle takes up the right half of the screen.

In step 4, the first part is to initialize three of the triangle vertices with the position and color. The original color is green, and when ray collides with the triangle, the color will be yellow. Then we set the triangle vertices to the triangle vertex buffer. The second part is about initiating the line that visualizes the ray and putting the data into the vertex buffer for line data. The final part defines the ray object with position and direction.

In step 5, before the RayIntersectsTriangle() method, the code is to check the tapped position to see whether it is in the LeftRectangle or the RightRectangle. When a valid tapping takes place, the ray will move along the X-axis by one unit, then we call the RayIntersectsTriangle() method to judge whether there is a collision between the ray and the triangle. If the returned distance is not null, this means that the collision happened, and we change the color of the triangle vertices to Color.Yellow. Otherwise, the color will be restored to Color.Green. The RayIntersectsTriangle() method has been discussed at the beginning of this recipe and inserts the definition of the RayIntersectsTriangle() method to the RayTriangleCollisionGame class.

In step 6, in the DrawColoredPrimitives() method, the effect receives the view and project matrices for the camera, the world matrix for the world position and transformation. The effect.VertexColorEnabled is set to true to make vertices have color. Then, we use the first pass of the current technique of BasicEffect and GraphicsDevice. The DrawPrimitives() method draws the primitives from the beginning of the vertex array in the vertex buffer.

In step 7, the DrawColoredPrimitives() method is used to draw the triangle that receives a parameter PrimitiveType.TriangleList, where 1 means the count of triangles. When drawing the line the PrimitiveType is LineList.

- Advertisement -

Latest News

Elevate Your Bentley Experience: The Bespoke Elegance of Bentayga EWB by Mulliner

Bentley Motors redefines the essence of bespoke luxury with the introduction of the Bentayga EWB's groundbreaking two-tone customization option—a...
- Advertisement -

More Articles Like This

- Advertisement -