Problem with very basic collision detection.

Started by
1 comment, last by EvolvedAnt 20 years, 1 month ago
Hello friends. I been trying to figure out how to add working collision detection to my code for a while now. I been researching many web sites and finaly understood how to do it. But when I try to implement the code, it does not seem to be working correctly. What I have is just a very simple DirectX app written in VB.NET. It only has 1 triangle, and 1 point. I want to detect when the point intersects the plane of my triangle. (I am not worrying about wether the point is IN the triangle or not yet.) So I have a point. With a location of (-1.0, 0.0, 0.0) And a triangle. With points A(1.0F, 0.0F, 1.0F) B(1.0F, 1.0F, 0.0F) C(1.0F, 0.0F, -1.0F) As I move the point with my arrow keys, I perform the following to detect if there was a collision with the point and the plane of the triangle. ' Get 2 edges of the triangle U.X = B.X - A.X U.Y = B.Y - A.Y U.Z = B.Z - A.Z V.X = C.X - A.X V.Y = C.Y - A.Y V.Z = C.Z - A.Z ' Calculate the normal of the triangle N = Vector3.Cross(U, V) ' Normalize the normal so it has a length of 1 L = Sqrt((N.X ^ 2) + (N.Y ^ 2) + (N.Z ^ 2)) N.X = N.X / L N.Y = N.Y / L N.Z = N.Z / L ' Get the determinant of the triangles plane equation d = Vector3.Dot(N, A) ' Calculate distance between the plane and the test point Distance = Vector3.Dot(point, N) - d At this point I simply check if the sign of Distance changes. If it goes from positive to negative that means the point passed through the plane of my triangle. If Distance is 0, then the point is exactly lying on the plane. The problem is that the point is going from positive to negative too early. When I move the point closer to the polygon, the distance value lowers as it should, but it reaches zero before it ever reaches the polygon. (Zero should mean the point is directly on the plane) Even more interesting is that if the point starts off at (0.0, 0.0, 0.0) then the collision on the plane works perfect. As soon as the point hits the plane, the distance value is 0, and becomes negative as it passes through. If anyone has any idea why this is happening I would greatly appreciate the help! Here is the complete source code. Lanauge is Visual Basic.NET, API is Managed DirectX 9.0b (Note: Im using the basic code from microsofts MatricesTutorial and changed it to have only the functionality that I need.)

Imports System
Imports System.Drawing
Imports System.Windows.Forms
Imports Microsoft.DirectX
Imports Microsoft.DirectX.Direct3D
Imports Direct3D = Microsoft.DirectX.Direct3D
Imports System.Math

Namespace MatricesTutorial
    Public Class Matrices
        Inherits Form
        Private device As device = Nothing
        Private vbTri As VertexBuffer = Nothing
        Private vbLine As VertexBuffer = Nothing
        Private presentParams As New PresentParameters()
        Private pause As Boolean = False
        Private sngX As Single = 0.0F
        Private camSngZ As Single = -5.0F
        Private camLookVector As Vector3
        Private camLocationVector As Vector3
        Private info As Microsoft.DirectX.Direct3D.Font
        Private U, V As Vector3     ' 2 vectors representing the edges of my triangle
        Private A, B, C As Vector3  ' 3 points on my triangle
        Private N As Vector3        ' vector representing the triangle Normal (a vector perpendicular to the plane of the triangle)
        Private L As Double         ' Length/Magnitude of the normal vector
        Private d As Double         ' plane determinant
        Private cosAlpha2 As Double
        Private point As Vector3
        Private Distance As Double  ' distance between test point and triangle's plane

        ' Variables for determining view position
        Private position As Vector3
        Private velocity As Vector3
        Private yaw As Single = 0.0F
        Private yawVelocity As Single = 0.0F
        Private pitch As Single = 0.0F
        Private pitchVelocity As Single = 0.0F
        Private viewMatrix As Matrix
        Private orientationMatrix As Matrix

        Public Sub New()
            Me.ClientSize = New System.Drawing.Size(400, 300)
            Me.Text = "Collision"
            camLocationVector = New Vector3(0.0F, 0.0F, -1.0F)
            camLookVector = New Vector3(0.0F, 0.0F, 0.0F)

            position = New Vector3(0.0F, 3.0F, -4.0F)
            velocity = New Vector3(0.0F, 0.0F, 0.0F)
            yaw = 0.03F
            yawVelocity = 0.0F
            pitch = 0.5F
            pitchVelocity = 0.0F
            viewMatrix = Matrix.Translation(0.0F, 0.0F, 10.0F)
            orientationMatrix = Matrix.Translation(0.0F, 0.0F, 0.0F)
            camLookVector.Z += 0.1F
            camLookVector.X += 0.1F
            camLocationVector.Z += camLocationVector.Z
            camLocationVector.X += camLookVector.X
            camLookVector.Z += 0.1F
            camLookVector.X += 0.1F
            camLocationVector.Z += camLocationVector.Z
            camLocationVector.X += camLookVector.X
        End Sub

        Public Function InitializeGraphics() As Boolean
            Try
                presentParams.Windowed = True
                presentParams.SwapEffect = SwapEffect.Discard
                presentParams.BackBufferFormat = Format.X8R8G8B8
                presentParams.AutoDepthStencilFormat = DepthFormat.D16
                presentParams.EnableAutoDepthStencil = True
                device = New Device(0, DeviceType.Hardware, Me, CreateFlags.SoftwareVertexProcessing, presentParams)
                AddHandler device.DeviceReset, AddressOf Me.OnResetDevice
                Me.OnCreateDevice(device, Nothing)
                Me.OnResetDevice(device, Nothing)
                pause = False

                Return True
            Catch e As DirectXException
                Return False
            End Try
        End Function 'InitializeGraphics

        Public Sub OnCreateDevice(ByVal sender As Object, ByVal e As EventArgs)
            Dim dev As Device = CType(sender, Device)
            vbTri = New VertexBuffer(GetType(CustomVertex.PositionColored), 3, dev, 0, CustomVertex.PositionColored.Format, Pool.Default)
            AddHandler vbTri.Created, AddressOf Me.OnCreateTriangle
            Me.OnCreateTriangle(vbTri, Nothing)
            vbLine = New VertexBuffer(GetType(CustomVertex.PositionColored), 2, dev, 0, CustomVertex.PositionColored.Format, Pool.Default)
            AddHandler vbLine.Created, AddressOf Me.OnCreateLine
        End Sub 'OnCreateDevice

        Public Sub OnResetDevice(ByVal sender As Object, ByVal e As EventArgs)
            Dim dev As Device = CType(sender, Device)
            dev.RenderState.CullMode = Cull.None
            dev.RenderState.Lighting = False
            dev.RenderState.ZBufferEnable = True
            info = New Microsoft.DirectX.Direct3D.Font(device, New System.Drawing.Font("Arial", 11, FontStyle.Bold))
        End Sub 'OnResetDevice

        Public Sub OnCreateTriangle(ByVal sender As Object, ByVal e As EventArgs)
            Dim vb As VertexBuffer = CType(sender, VertexBuffer)
            Dim verts As CustomVertex.PositionColored() = CType(vb.Lock(0, 0), CustomVertex.PositionColored())
            verts(0).X = 1.0F
            verts(0).Y = 0.0F
            verts(0).Z = 1.0F
            verts(0).Color = System.Drawing.Color.Green.ToArgb()
            verts(1).X = 1.0F
            verts(1).Y = 1.0F
            verts(1).Z = 0.0F
            verts(1).Color = System.Drawing.Color.Red.ToArgb()
            verts(2).X = 1.0F
            verts(2).Y = 0.0F
            verts(2).Z = -1.0F
            verts(2).Color = System.Drawing.Color.Blue.ToArgb()
            vb.Unlock()
            A.X = 1.0F
            A.Y = 0.0F
            A.Z = 1.0F
            B.X = 1.0F
            B.Y = 1.0F
            B.Z = 0.0F
            C.X = 1.0F
            C.Y = 0.0F
            C.Z = -1.0F
        End Sub 'OnCreateTriangle

        Public Sub OnCreateLine(ByVal sender As Object, ByVal e As EventArgs)
            Dim vb As VertexBuffer = CType(sender, VertexBuffer)
            Dim verts As CustomVertex.PositionColored() = CType(vb.Lock(0, 0), CustomVertex.PositionColored())

            verts(0).X = -1.01F
            verts(0).Y = 0.1F
            verts(0).Z = 0.0F
            verts(0).Color = System.Drawing.Color.White.ToArgb()
            verts(1).X = -1.0F
            verts(1).Y = 0.1F
            verts(1).Z = 0.0F
            verts(1).Color = System.Drawing.Color.White.ToArgb()
            vb.Unlock()
            point.X = -1.0F
            point.Y = 0.1F
            point.Z = 0.0F
        End Sub 'OnCreateLine

        Private Sub Render()
            If device Is Nothing Then
                Return
            End If
            If pause Then
                Return
            End If
            device.Clear(ClearFlags.Target Or ClearFlags.ZBuffer, System.Drawing.Color.Black, 1.0F, 0)
            device.BeginScene()

            SetupMatrices()

            device.Transform.World = Matrix.Translation(0.0F, 0.0F, 0.0F)
            device.SetStreamSource(0, vbTri, 0)
            device.VertexFormat = CustomVertex.PositionColored.Format
            device.DrawPrimitives(PrimitiveType.TriangleList, 0, 1)

            device.Transform.World = Matrix.Translation(point.X, point.Y, point.Z)
            device.SetStreamSource(0, vbLine, 0)
            device.VertexFormat = CustomVertex.PositionColored.Format
            device.DrawPrimitives(PrimitiveType.LineList, 0, 1)
            info.DrawText(Nothing, "U: " & U.X & U.Y & U.Z & " V: " & V.X & V.Y & V.Z, New System.Drawing.Rectangle(5, 5, 400, 15), DrawTextFormat.Left Or DrawTextFormat.Top, Drawing.Color.FromArgb(255, 255, 255, 255))
            info.DrawText(Nothing, "Normal: " & N.X & N.Y & N.Z, New System.Drawing.Rectangle(5, 20, 400, 15), DrawTextFormat.Left Or DrawTextFormat.Top, Drawing.Color.FromArgb(255, 255, 255, 255))
            info.DrawText(Nothing, "Length: " & L, New System.Drawing.Rectangle(5, 35, 400, 15), DrawTextFormat.Left Or DrawTextFormat.Top, Drawing.Color.FromArgb(255, 255, 255, 255))
            info.DrawText(Nothing, "Normalized Normal: " & N.X & N.Y & N.Z, New System.Drawing.Rectangle(5, 50, 400, 15), DrawTextFormat.Left Or DrawTextFormat.Top, Drawing.Color.FromArgb(255, 255, 255, 255))
            info.DrawText(Nothing, "d: " & d, New System.Drawing.Rectangle(5, 65, 400, 15), DrawTextFormat.Left Or DrawTextFormat.Top, Drawing.Color.FromArgb(255, 255, 255, 255))
            info.DrawText(Nothing, "Distance: " & Distance, New System.Drawing.Rectangle(5, 80, 400, 15), DrawTextFormat.Left Or DrawTextFormat.Top, Drawing.Color.FromArgb(255, 255, 255, 255))

            device.EndScene()
            device.Present()
        End Sub 'Render

        Private Sub CheckCollision()
            ' Get 2 edges of the triangle
            U.X = B.X - A.X
            U.Y = B.Y - A.Y
            U.Z = B.Z - A.Z
            V.X = C.X - A.X
            V.Y = C.Y - A.Y
            V.Z = C.Z - A.Z
            ' Calculate the normal of the triangle 
            N = Vector3.Cross(U, V)
            ' Normalize the normal so it has a length of 1
            L = Sqrt((N.X ^ 2) + (N.Y ^ 2) + (N.Z ^ 2))
            N.X = N.X / L
            N.Y = N.Y / L
            N.Z = N.Z / L
            ' Get the derterminant of the triangles plane equation
            d = Vector3.Dot(N, B)
            ' Calculate distance between the plane and the test point
            Distance = Vector3.Dot(point, N) - d
        End Sub

        Private Sub SetupMatrices()

            Dim iTime As Integer = Environment.TickCount Mod 1000
            device.Transform.View = Matrix.LookAtLH(camLocationVector, New Vector3(0.0F, 0.0F, 0.0F), New Vector3(0.0F, 1.0F, 0.0F)) 'camLocationVector, camLookVector,
            device.Transform.Projection = Matrix.PerspectiveFovLH(CSng(Math.PI) / 4, 4 / 3, 1.0F, 100.0F)
        End Sub 'SetupMatrices

        Private Sub window_KeyDown(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles MyBase.KeyDown
            Select Case e.KeyCode
                Case Keys.Right
                    point.X = point.X + 0.02F
                    CheckCollision()
                    If Distance < 0 Then
                        point.X = point.X - 0.02F
                    End If
                Case Keys.Left
                    point.X = point.X - 0.02F
                    CheckCollision()
                Case Keys.Up
                    point.Y = point.Y + 0.02F
                    CheckCollision()
                Case Keys.Down
                    point.Y = point.Y - 0.02F
                    CheckCollision()
            End Select
        End Sub
        Protected Overrides Sub offPaint(ByVal e As System.Windows.Forms.PaintEventArgs)
            CheckCollision()
            Me.Render()
        End Sub 'OnPaint

        Protected Overrides Sub offKeyPress(ByVal e As System.Windows.Forms.KeyPressEventArgs)
            If Asc(e.KeyChar) = CInt(System.Windows.Forms.Keys.Escape) Then
                Me.Close()
            End If
        End Sub 'OnKeyPress

        Protected Overrides Sub offResize(ByVal e As System.EventArgs)
            pause = (Me.WindowState = FormWindowState.Minimized Or Not Me.Visible)
        End Sub 'OnResize

        Shared Sub Main()
            Dim frm As New Matrices
            If Not frm.InitializeGraphics() Then
                MessageBox.Show("Could not initialize Direct3D.")
                Return
            End If
            frm.Show()
            frm.Width = frm.Width + 1

            While frm.Created
                frm.Render()
                Application.DoEvents()
            End While
        End Sub 'Main
    End Class
End Namespace
[edited by - EvolvedAnt on March 18, 2004 3:25:08 PM]
Advertisement
don''t see anything wrong in there. what''s your cross product and dot product functions? what the normal looks like?

Everything is better with Metal.

The DirectX API has a Vector3 class that besides holding an X Y Z value, also contains Static methods called Dot() Cross() and Normalize()

I use these rather than write my own Dot product function since I imagine Microsoft knows how to do it better/faster. So im sure there is nothing wrong with how they are implemented.

From my testing I think the problem involved a difference between testing against model space and world space. I think I may have fixed the problem by creating my point based on the origin in model space, and then testing collisions only on the world coordinates of the point after it went through a world transformation.

I still have some more tests to run but I hope I am right.

This topic is closed to new replies.

Advertisement