private void AddHitLineArrow(Ray ray, DXRayHitTestResult hitResult, Point3D?referenceHitPosition = null, Point3D?rayEndPosition = null)
        {
            Ab3d.Visuals.LineVisual3D lineVisual3D;
            Point3D endPosition;

            if (hitResult != null)
            {
                endPosition = hitResult.HitPosition.ToWpfPoint3D();
            }
            else
            {
                endPosition = rayEndPosition ?? Camera1.TargetPosition + Camera1.Offset;
            }

            if (hitResult != null)
            {
                lineVisual3D = new Ab3d.Visuals.LineVisual3D()
                {
                    StartPosition = ray.Position.ToWpfPoint3D(),
                    EndPosition   = endPosition,
                    LineColor     = Colors.Green,
                    LineThickness = 1,
                    EndLineCap    = LineCap.ArrowAnchor
                };


                if (referenceHitPosition != null)
                {
                    var wireCrossVisual3D = new Ab3d.Visuals.WireCrossVisual3D()
                    {
                        Position      = referenceHitPosition.Value,
                        LineColor     = Colors.Red,
                        LineThickness = 1,
                        LinesLength   = 5
                    };

                    _hitLinesModelVisual3D.Children.Add(wireCrossVisual3D);
                }
            }
            else
            {
                lineVisual3D = new Ab3d.Visuals.LineVisual3D()
                {
                    StartPosition = ray.Position.ToWpfPoint3D(),
                    EndPosition   = endPosition,
                    LineColor     = Colors.Green,
                    LineThickness = 1
                };
            }

            _hitLinesModelVisual3D.Children.Add(lineVisual3D);
        }
        protected override void CheckMouseOverElement(Point viewboxMousePosition, List <BaseEventSource3D> eventSources, List <Visual3D> excludedVisuals, bool checkOnlyDragSurfaces)
        {
            EnsureRayMeshGeometry3DHitTestResultConstructor();

            if (IsUsingWpfHitTesting)
            {
                base.CheckMouseOverElement(viewboxMousePosition, eventSources, excludedVisuals, checkOnlyDragSurfaces);
                return;
            }

            if (_dxViewportView.DXScene == null) // We need to wait until DXScene is initialized
            {
                return;
            }


            var pickRay = _dxViewportView.DXScene.GetRayFromCamera((int)viewboxMousePosition.X, (int)viewboxMousePosition.Y);


            DXRayHitTestResult dxRayHitTestResult = null;

            lastHitEventSource3D = null;
            lastRayHitResult     = null;


            if (!checkOnlyDragSurfaces)
            {
                if (excludedVisuals != null)
                {
                    // Use filter to exclude Visual3D objects from excludedVisuals
                    _dxViewportView.DXScene.DXHitTestOptions.HitTestFilterCallback = delegate(SceneNode node)
                    {
                        var wpfModelVisual3DNode = node as WpfModelVisual3DNode;

                        if (wpfModelVisual3DNode != null)
                        {
                            for (var i = 0; i < excludedVisuals.Count; i++)
                            {
                                if (ReferenceEquals(wpfModelVisual3DNode.ModelVisual3D, excludedVisuals[i]))
                                {
                                    return(DXHitTestOptions.HitTestFilterResult.ContinueSkipSelfAndChildren);
                                }
                            }
                        }

                        return(DXHitTestOptions.HitTestFilterResult.Continue);
                    };
                }
                else
                {
                    _dxViewportView.DXScene.DXHitTestOptions.HitTestFilterCallback = null;
                }

                dxRayHitTestResult = _dxViewportView.DXScene.GetClosestHitObject(pickRay);


                if (dxRayHitTestResult != null)
                {
                    lastRayHitResult = CreateRayMeshGeometry3DHitTestResult(dxRayHitTestResult);

                    var visualHit = lastRayHitResult.VisualHit;

                    // First check if any TargetVisual3D was hit
                    if (visualHit != null)
                    {
                        for (int i = 0; i < eventSources.Count; i++)
                        {
                            BaseEventSource3D oneEventSource3D = eventSources[i];
                            if (oneEventSource3D.IsMyVisual(visualHit))
                            {
                                // Hit oneEventSource3D
                                lastHitEventSource3D = oneEventSource3D; // Get the hit object
                                break;
                            }
                        }
                    }

                    if (lastHitEventSource3D == null)
                    {
                        var modelHit = lastRayHitResult.ModelHit as GeometryModel3D;

                        if (modelHit != null)
                        {
                            int eventSourcesCount = eventSources.Count;
                            for (int i = 0; i < eventSourcesCount; i++) // use for instead of foreach to prevent creating new object in foreach
                            {
                                BaseEventSource3D oneEventSource3D = eventSources[i];

                                // First find the hit object (_lastHitObject)
                                if (lastHitEventSource3D == null)
                                {
                                    if (oneEventSource3D.IsMyGeometryModel3D(modelHit))
                                    {
                                        lastHitEventSource3D = oneEventSource3D;
                                    }
                                }
                            }
                        }
                    }
                }
            }


            if (lastHitEventSource3D == null && (dxRayHitTestResult != null || checkOnlyDragSurfaces))
            {
                // When there is is an object hit, but it is not one that is subscribed,
                // and there is an eventSources that is defined as a DragSource, we need to check each DragSurface if it is hit and found the closest

                // First collect all SceneNodes that need to be checked (SceneNodes that are created from DragSurfaces)
                if (_sceneNodesToCheck == null)
                {
                    _sceneNodesToCheck = new List <SceneNode>(); // Reuse array so we do not create it on each hit test check
                }
                BaseEventSource3D  closestEventSource3D = null;
                DXRayHitTestResult closestRayHit        = null;
                float closestDistanceToRayOrigin        = float.MaxValue;


                for (var i = 0; i < eventSources.Count; i++)
                {
                    var eventSource = eventSources[i];

                    if (!eventSource.IsDragSurface)
                    {
                        continue;
                    }

                    var multiVisualEventSource3D = eventSource as MultiVisualEventSource3D;
                    if (multiVisualEventSource3D != null)
                    {
                        for (var i1 = 0; i1 < multiVisualEventSource3D.TargetVisuals3D.Count; i1++)
                        {
                            var sceneNode = _dxViewportView.GetSceneNodeForWpfObject(multiVisualEventSource3D.TargetVisuals3D[i1]);
                            if (sceneNode != null)
                            {
                                _sceneNodesToCheck.Add(sceneNode);
                            }
                        }
                    }
                    else
                    {
                        var visualEventSource3D = eventSource as VisualEventSource3D;
                        if (visualEventSource3D != null)
                        {
                            var sceneNode = _dxViewportView.GetSceneNodeForWpfObject(visualEventSource3D.TargetVisual3D);
                            if (sceneNode != null)
                            {
                                _sceneNodesToCheck.Add(sceneNode);
                            }
                        }
                    }


                    // Now check if SceneNodes intersect with the pickRay
                    for (var i1 = 0; i1 < _sceneNodesToCheck.Count; i1++)
                    {
                        var sceneNode = _sceneNodesToCheck[i1];

                        // We cannot test WpfModelVisual3DNode because it does not implement IRayHitTestedNode.
                        // Therefore we need to test its child WpfGeometryModel3DNode instead
                        var wpfModelVisual3DNode = sceneNode as WpfModelVisual3DNode;
                        if (wpfModelVisual3DNode != null && wpfModelVisual3DNode.ChildNodesCount == 1)
                        {
                            sceneNode = wpfModelVisual3DNode.ChildNodes[0];
                        }

                        var oneHitResult = _dxViewportView.DXScene.HitTestSceneNode(pickRay, sceneNode);

                        if (oneHitResult != null && oneHitResult.DistanceToRayOrigin < closestDistanceToRayOrigin)
                        {
                            // When we tested the ModelVisual3D,
                            // we need to revert ModelVisual3D.Transform on HitPosition,
                            // because it is applied again in the MouseDrag3DEventArgs constructor
                            if (wpfModelVisual3DNode != null && wpfModelVisual3DNode.Transform != null)
                            {
                                var matrix = wpfModelVisual3DNode.Transform.Value;
                                matrix.Invert();

                                var transformation = new Transformation(matrix);
                                transformation.Transform(ref oneHitResult.HitPosition, out oneHitResult.HitPosition);
                            }

                            closestEventSource3D = eventSource;
                            closestRayHit        = oneHitResult;

                            closestDistanceToRayOrigin = oneHitResult.DistanceToRayOrigin;
                        }
                    }

                    _sceneNodesToCheck.Clear();
                }

                if (closestEventSource3D != null)
                {
                    lastHitEventSource3D = closestEventSource3D;
                    lastRayHitResult     = CreateRayMeshGeometry3DHitTestResult(closestRayHit);
                }
            }
        }
        private RayMeshGeometry3DHitTestResult CreateRayMeshGeometry3DHitTestResult(DXRayHitTestResult dxRayHitTestResult)
        {
            if (dxRayHitTestResult == null)
            {
                return(null);
            }


            Visual3D        visualHit;
            GeometryModel3D modelHit;
            MeshGeometry3D  meshHit;
            int             vertexIndex1, vertexIndex2, vertexIndex3;

            var wpfGeometryModel3DNode = dxRayHitTestResult.HitSceneNode as WpfGeometryModel3DNode;

            if (wpfGeometryModel3DNode != null)
            {
                modelHit = wpfGeometryModel3DNode.GeometryModel3D as GeometryModel3D;
                meshHit  = wpfGeometryModel3DNode.DXMesh.MeshGeometry;

                int indiceIndex = dxRayHitTestResult.TriangleIndex * 3;

                vertexIndex1 = meshHit.TriangleIndices[indiceIndex];
                vertexIndex2 = meshHit.TriangleIndices[indiceIndex + 1];
                vertexIndex3 = meshHit.TriangleIndices[indiceIndex + 2];
            }
            else
            {
                modelHit = null;
                meshHit  = null;

                vertexIndex1 = vertexIndex2 = vertexIndex3 = 0;
            }

            SceneNode currentNode = dxRayHitTestResult.HitSceneNode;

            while (currentNode != null && !(currentNode is WpfModelVisual3DNode))
            {
                currentNode = currentNode.ParentNode;
            }

            var wpfModelVisual3DNode = currentNode as WpfModelVisual3DNode;

            if (wpfModelVisual3DNode != null)
            {
                visualHit = wpfModelVisual3DNode.ModelVisual3D;
            }
            else
            {
                visualHit = null;
            }

            var rayMeshGeometry3DHitTestResult = CreateRayMeshGeometry3DHitTestResult(
                visualHit,
                modelHit,
                meshHit,
                dxRayHitTestResult.HitPosition.ToWpfPoint3D(),
                (double)dxRayHitTestResult.DistanceToRayOrigin,
                vertexIndex1,
                vertexIndex2,
                vertexIndex3,
                new Point(0, 0));     // barycentricCoordinate is not supported - user will need to calculate that by himself from triangle data if needed

            return(rayMeshGeometry3DHitTestResult);
        }