Balder Sphere HitTest

Mar 17, 2010 at 6:15 PM

Hi~
I was trying to locate the exact hit position in 3D when a mouse hit on my sphere happend and I was confused by the following 2 problems:

1. The handler function did not execute when a mouse hit event triggered.

<execution:Game x:Name="myGame" Width="640" Height="480">
                <execution:Game.Camera>
                    <view:Camera Position="100,50,-100" Target="0,0,0"/>
                </execution:Game.Camera>

                <lighting:OmniLight Position="-100,100,0" Ambient="Brown" Diffuse="Green" Specular="Blue"/>

                <local:Globe x:Name="Globe" Radius="20" Slices="24" Stacks="24" MouseLeftButtonDown="Globe_MouseLeftButtonDown"/>

                <!--<geometries:Sphere Radius="20" Segments="8"/>-->
            </execution:Game>

public MainPage()
        {
            InitializeComponent();

            myGame.MouseLeftButtonDown += new MouseButtonEventHandler(Globe_MouseLeftButtonDown);
        }

        void Globe_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            int x = Convert.ToInt32(e.GetPosition(myGame).X);
            int y = Convert.ToInt32(e.GetPosition(myGame).Y);

            Vector v = myGame.Scene.GetPointAtScreenCoordinate(myGame.Viewport, x, y);
            txt.Text = v.ToString();
        }

 

2. I added a "GetPointAtScreenCoordinate" method according to the original "GetNodeAtScreenCoordinate" in Scene.cs

Will it work?Do I need to do something more?

 

public Vector GetPointAtScreenCoordinate(Viewport viewport, int x, int y)
        {
            var nearSource = new Vector((float)x, (float)y, 0f);
            var farSource = new Vector((float)x, (float)y, 1f);
            var view = viewport.View;
            var world = Matrix.CreateTranslation(0, 0, 0);
            var nearPoint = viewport.Unproject(nearSource, view.ProjectionMatrix, view.ViewMatrix, world);
            var farPoint = viewport.Unproject(farSource, view.ProjectionMatrix, view.ViewMatrix, world);

            var direction = farPoint - nearPoint;
            direction.Normalize();

            var pickRay = new Ray(nearPoint, direction);

            var closestObjectDistance = float.MaxValue;
            RenderableNode closestObject = null;

            lock (_renderableNodes)
            {
                foreach (var node in _renderableNodes)
                {
                    if (null != (object)node.BoundingSphere)
                    {
                        // Todo : Hierarchical pick
                        var transformedSphere = node.BoundingSphere.Transform(node.World);
                        var distance = pickRay.Intersects(transformedSphere);
                        if (distance.HasValue)
                        {
                            if (distance < closestObjectDistance)
                            {
                                closestObject = node as RenderableNode;
                                closestObjectDistance = distance.Value;
                            }
                        }
                    }
                }
            }

            return closestObjectDistance * pickRay.Direction + nearPoint;
        }

Thanks a lot!

Coordinator
Mar 17, 2010 at 7:49 PM

For your Globe object, I am assuming that you are generating the mesh yourself. 

Are you initializing the BoundingSphere property?

After you have created your mesh (Vertices + Faces), you can then call the InitializeBoundingSphere() from the base class and you'll get a bounding sphere for the object. I am guessing that is why the event is not firing. 

 

Mar 18, 2010 at 3:51 AM

Thank you~The first problem has been solved and now I focus on the next one.

Whether I click on my globe or off my globe, Globe object is always picked. My textblock always shows "****.Globe".

How does the "GetNodeAtScreenCoordinate" method work?

Thanks again~


Here's my code:

 

<StackPanel Orientation="Vertical">
            <execution:Game x:Name="myGame" Width="640" Height="480">
                <execution:Game.Camera>
                    <view:Camera Position="100,50,-100" Target="0,0,0"/>
                </execution:Game.Camera>

                <lighting:OmniLight Position="-100,100,0" Ambient="Brown" Diffuse="Green" Specular="Blue"/>

                <local:Globe x:Name="Globe" Radius="20" Slices="24" Stacks="24" MouseLeftButtonDown="Globe_MouseLeftButtonDown"/>

                <!--<geometries:Sphere Radius="20" Segments="8"/>-->
            </execution:Game>

            <TextBlock Name="txt" Height="50" Width="300" Margin="0,0,0,0" VerticalAlignment="Top" HorizontalAlignment="Left" />
        </StackPanel>

 

void Globe_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            int x = Convert.ToInt32(e.GetPosition(myGame).X);
            int y = Convert.ToInt32(e.GetPosition(myGame).Y);

            RenderableNode node = myGame.Scene.GetNodeAtScreenCoordinate(myGame.Viewport, x, y);
            txt.Text = node.ToString();
        }

 

public RenderableNode GetNodeAtScreenCoordinate(Viewport viewport, int x, int y)
        {
            var nearSource = new Vector((float)x, (float)y, 0f);
            var farSource = new Vector((float)x, (float)y, 1f);
            var view = viewport.View;
            var world = Matrix.CreateTranslation(0, 0, 0);
            var nearPoint = viewport.Unproject(nearSource, view.ProjectionMatrix, view.ViewMatrix, world);
            var farPoint = viewport.Unproject(farSource, view.ProjectionMatrix, view.ViewMatrix, world);

            var direction = farPoint - nearPoint;
            direction.Normalize();

            var pickRay = new Ray(nearPoint, direction);

            var closestObjectDistance = float.MaxValue;
            RenderableNode closestObject = null;
            
            lock (_renderableNodes)
            {
                foreach (var node in _renderableNodes)
                {
                    if (null != (object)node.BoundingSphere)
                    {
                        // Todo : Hierarchical pick
                        var transformedSphere = node.BoundingSphere.Transform(node.World);
                        var distance = pickRay.Intersects(transformedSphere);
                        if (distance.HasValue)
                        {
                            if (distance < closestObjectDistance)
                            {
                                closestObject = node as RenderableNode;
                                closestObjectDistance = distance.Value;
                            }
                        }
                    }
                }
            }

            return closestObject;
        }

Coordinator
Mar 18, 2010 at 9:24 AM

Hi,

I discovered yesterday that the complete intersection code is buggy. This is my focus area the next couple of days. Hopefully have it nailed soon. 

 

Mar 21, 2010 at 11:26 PM

SampleBrowser @ http://www.ingebrigtsen.info/Silverlight/Balder/20100208/TestPage.html .. Events .. Mouse

is buggy too, especially LButton state.

 

Coordinator
Mar 22, 2010 at 7:36 AM

The button states has been fixed, but still having trouble with the intersection bits. I am closing in though, so hopefully soon it will be fixed. 

Coordinator
Mar 27, 2010 at 2:56 PM

You'll be happy to know that the mouse events has been fixed in the latest source code. It has even been created more accurately than originally planned, its in fact on a pixel level now, rather than bounding spheres