ray picking

razdazzle

Newcomer
Joined
Feb 5, 2005
Messages
7
hi guys,

I'm trying to check for intersections with meshes in my scene(to find out which tile the user clicked on). I am developing a fake isometric(ortho view) tile based game in c# and MDX. I found an example of how to do this in vb.net(intersecting teapot mesh). I played around with it in vb, added extra meshes and changed the view and projection matrices etc. just to mimic how I would be using it in my game. I can't however seem to get the ray picking to work now that I have converted the code. any ideas on where I could be going wrong?

here is the vb ver

Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
'=======Parameters
presentParams = New PresentParameters
presentParams.Windowed = True
presentParams.SwapEffect = SwapEffect.Discard
'=======Device
dev = New Direct3D.Device(0, DeviceType.Hardware, pic3d, CreateFlags.HardwareVertexProcessing, presentParams)
'=======Mesh
mesh = mesh.Teapot(dev)
mesh2 = mesh.Teapot(dev)

'=======Camera
dev.Transform.View = Matrix.LookAtLH(New Vector3(0.0F, 2.0F, 2.0F), New Vector3(0, 0, 0), New Vector3(0, 1, 0))
'dev.Transform.View = Matrix.LookAtLH(New Vector3(0.0F, 30.0F, 30.0F), New Vector3(1, 1, 0), New Vector3(0, 1, 0))
'dev.Transform.View = Matrix.LookAtLH(Me.Width / 35, Me.Height / 35, -1000.0F, 10000.0F)

dev.Transform.Projection = Matrix.OrthoLH(CSng(Math.PI / 4), 1.3333, 1, 100)
'=======Loop
Me.Show()
MainLoop()
End Sub

Private Sub MainLoop()
Do
Render()
Application.DoEvents()
Loop
End Sub

Private Sub Render()
dev.Clear(ClearFlags.Target, System.Drawing.Color.CornflowerBlue, 1.0F, 0)
dev.BeginScene()
'==========
vec1 = New Vector3(1.7, 0, 0)
vec2 = New Vector3(-1.7, 0, 0)

dev.Transform.World = Matrix.Translation(vec1)
mesh.DrawSubset(0)


dev.Transform.World = Matrix.Translation(vec2)
mesh2.DrawSubset(0)
'==========
dev.EndScene()
dev.Present()
End Sub



Private Sub Form1_Mousedown(ByVal sender As System.Object, ByVal e As _
System.Windows.Forms.MouseEventArgs) Handles pic3d.MouseDown
If e.Button = MouseButtons.Left Then
Intersect(e.X, e.Y, vec1, mesh)
Intersect(e.X, e.Y, vec2, mesh2)

End If
End Sub


Public Sub Intersect(ByVal x As Single, ByVal y As Single, ByVal t As Vector3, ByVal m As Direct3D.Mesh)
Dim Near As Vector3
Dim Far As Vector3
Dim Closest As IntersectInformation
Dim locMesh As Mesh
Dim tran As Vector3


Near = New Vector3(x, y, 0)
Far = New Vector3(x, y, 1)
tran = New Vector3(0, 0, 0)

locMesh = m


Near.Unproject(dev.Viewport, dev.Transform.Projection, dev.Transform.View, Matrix.Translation(t))
Far.Unproject(dev.Viewport, dev.Transform.Projection, dev.Transform.View, Matrix.Translation(t))
Far.Subtract(Near)
'=======================================================
' Here is where you check for an intersection
If locMesh.Intersect(Near, Far, Closest) Then
dev.RenderState.FillMode = FillMode.WireFrame

Else
'dev.RenderState.FillMode = FillMode.Solid
End If




End Sub






here is some important snippets from the c# ver

private void SetupCamera()
{


device.Transform.Projection = Matrix.OrthoLH(this.Width / 35, this.Height / 35,-1000.0f, 10000.0f);
//device.Transform.View = Matrix.LookAtLH(new Vector3(0f,0f, 18.0f), new Vector3(28f,28f,0), new Vector3(1,1,0));
device.Transform.View = Matrix.LookAtLH(new Vector3(0f,0f, 18.0f), new Vector3(28f,28f,0), new Vector3(1,1,0));

//device.RenderState.Ambient = Color.DarkBlue;
device.Lights[0].Type = LightType.Directional;
device.Lights[0].Diffuse = Color.White;
device.Lights[0].Direction = new Vector3(0, -1, -2);
device.Lights[0].Enabled = true;


}

protected override void OnMouseDown(MouseEventArgs e)
{

if(e.Button == MouseButtons.Left)
{

intersection(grassMesh);


}

}

private void intersection(StaticMesh staticMesh)
{
IntersectInformation closest = new IntersectInformation();

Vector3 near = new Vector3(xMousePos,yMousePos,0);
Vector3 far = new Vector3(xMousePos,yMousePos,1);


Vector3 vec = (Vector3) tilesCoOrd[0];


near.Unproject(device.Viewport,device.Transform.Projection,device.Transform.View,Matrix.Translation(vec));
far.Unproject(device.Viewport,device.Transform.Projection,device.Transform.View,Matrix.Translation(vec));
far.Subtract(near);


Mesh mesh = staticMesh.getMesh();


if( mesh.Intersect(near,far,out closest))
{

//handle intersection


}


}
 
I recently implemented Ray Picking myself, I'm not an expert but here is what I did. I tranformed back through the Project and View matrix's and created a ray. Then I used this ray with a bounding sphere in the world to detect my own collisions, instead of using the mesh.intersect() method. Using that method requires you to tranform the ray back through the mesh's world tranform. Which I never got to work properly.

I'm not familiar with how the DirectX unproject functions work but I suspect you are getting a translation component in your far vector. Which mesh.intersect() expects a Ray Orgin and Ray Direction (no translation in direction).

Anyway, here is how I transformed the mouse click into world co-ordinates

Code:
        private void HardwareToWorld(int x, int y, out Vector3 RayOrig, out Vector3 RayDir)
        {
            Matrix invView = Matrix.Invert(device.Transform.View);
            Vector3 v = new Vector3();
            //Reverse the Projection transformation
            v.X = (float)(((2.0 * x) / container.Width) - 1) / device.Transform.Projection.M11;
            v.Y = (float)-(((2.0 * y) / container.Height) - 1) / device.Transform.Projection.M22;
            v.Z = -1;  //We are looking down the Z
            //Multiplying by the inverse Matrix in this way prevents the translation component
            //of the Matrix from taking affect.
            RayDir.X = v.X * invView.M11 + v.Y * invView.M21 + v.Z * invView.M31;
            RayDir.Y = v.X * invView.M12 + v.Y * invView.M22 + v.Z * invView.M32;
            RayDir.Z = v.X * invView.M13 + v.Y * invView.M23 + v.Z * invView.M33;
            RayDir.Normalize();
            RayOrig.X = invView.M41;
            RayOrig.Y = invView.M42;
            RayOrig.Z = invView.M43;

            RayOrig += RayDir * camera.Near; //Move it to the near plane
        }

Hope it helps!
 
thanks a lot for the reply. I have tried implementing your method, I haven't been able to get it working yet,but I think that may be because I'm scrolling my tile map by changing the tile meshes position in world co-ords. I'll have a go at it again when I set up scrolling based on the view matrix.
 
Back
Top