Search code examples
c#wpfhittestsharpdxhelix-3d-toolkit

How to select topmost element in a helix-toolkit sharpDX viewport?


I'm working with the sharpDX branch of the helix-toolkit library for a project at my university (HelixToolKit Library)

Currently I'm searching for a way to correctly select elements in a viewport.

I found a helpful example in the source code of the helixtoolkit library:example code on GitHub

public class MyLineGeometryModel3D : LineGeometryModel3D
{
    private Color? initialColor = null;

    public override bool HitTest(Ray rayWS, ref List<HitTestResult> hits)
    {
        if (initialColor == null)
        {
            initialColor = this.Color;
        }

        var result = base.HitTest(rayWS, ref hits);
        var pressedMouseButtons = Viewport3DX.GetPressedMouseButtons();

        if (pressedMouseButtons == 0 || pressedMouseButtons.HasFlag(MouseButtons.Left))
        {
            this.Color = result ? Color.Red : this.initialColor.Value;
        }
        return result;
    }

}

I managed to get this running in my application. However instead of selecting only the topmost element, all elements intersected by the ray are selected. Some kind of handler function is probably needed to highlight the element with the shortest distance?

I was checking some of the standard WPF solutions for that and they often use an eventhandler. ( e.g. 3D Hit Testing in WPF )

private void m_viewport3d_MouseDown(object sender, MouseButtonEventArgs e)
{
    Point mousePos = e.GetPosition(m_viewport3d);
    PointHitTestParameters hitParams = new PointHitTestParameters(mousePos);
    HitTestResult result = VisualTreeHelper.HitTest(m_viewport3d, mousePos);
    RayMeshGeometry3DHitTestResult rayMeshResult = result as RayMeshGeometry3DHitTestResult;
    if (rayMeshResult != null)
    {
        MeshGeometry3D mesh = new MeshGeometry3D();
        mesh.Positions.Add(rayMeshResult.MeshHit.Positions[rayMeshResult.VertexIndex1]);
        mesh.Positions.Add(rayMeshResult.MeshHit.Positions[rayMeshResult.VertexIndex2]);
        mesh.Positions.Add(rayMeshResult.MeshHit.Positions[rayMeshResult.VertexIndex3]);
        mesh.TriangleIndices.Add(0);
        mesh.TriangleIndices.Add(1);
        mesh.TriangleIndices.Add(2);
        GeometryModel3D marker = new GeometryModel3D(mesh, new DiffuseMaterial(Brushes.Blue));
        //...add marker to the scene...
    }
}

Is using an eventhandler a sensible solution ? And if yes how to get a ray element for calling the HitTest function in the eventhandler?


Solution

  • Use the Viewport method FindNearest:

    private void OnMouseDown(object sender, MouseButtonEventArgs e)
    {     
        Viewport3DX vp = e.Source as Viewport3DX;
    
        Point3D p;
        Vector3D v;
        Model3D m;
        if (vp.FindNearest(e.GetPosition(vp), out p, out v, out m))
        {
             //Do something with the found object
        }
     }