Hit testing (also EventManage3D) of 3D lines work in WPF 3D rendering because there the Ab3d.PowerToys library converts the 3D line into a standard GeometryModel3D object (this object is then updated on each camera change). When you have many 3D lines, then this is a very slow process.
When the 3D scene is rendered with Ab3d.DXEngine, then the graphics card generates the geometry for the lines. This is much much faster and allows rendering millions of 3D lines. But because no GeometryModel3D is created for such lines, the 3D hit testing does not work any more.
You cannot have both - super fast 3D lines rendering and standard 3D hit testing.
If you are not rendering a lot of 3D lines (you are using DXEngine for some other reason - for example many other 3D objects), then you can disable hardware accelerated 3D lines. This can be done with setting HardwareAccelerate3DLines property on DXScene to false:
Code:
_dxViewportView.DXSceneDeviceCreated += delegate(object sender, EventArgs args)
{
if (_dxViewportView.DXScene != null) // Can be null in WPF 3D rendering (used as fallback by default)
_dxViewportView.DXScene.HardwareAccelerate3DLines = false;
};
Another option is to use Ab3d.Utilities.LineSelectorData from Ab3d.PowerToys library - this is a helper class that is meant to help user to select 3D lines. The problem with 3D lines for the application users is that they are usually thick and the user can have some problems to put the mouse exactly on a line. Therefore this class support selecting the closest 3D line - so you can decide how close the user need to be to select a 3D line. See the sample in Ab3d.PowerToys sample project for more info.
This class works only with 3D lines and polylines - not with CornerWireBoxVisual3D or EllipseLineVisual3D.
To support that you would need to manually create the lines needed for those two objects. Here is the code that does that:
CornerWireBoxVisual3D:
Code:
/// <summary>
/// Creates this Model3D
/// </summary>
protected override void CreateModel()
{
var centerPosition = this.CenterPosition;
var size = this.Size;
var isLineLengthPercent = this.IsLineLengthPercent;
var lineLength = this.LineLength;
// In case one size component is 0, we create only a rectangle (8 positions instead of 24 for the whole box)
if (MathUtils.IsZero(size.X) || MathUtils.IsZero(size.Y) || MathUtils.IsZero(size.Z))
{
// Optimize access
double x = centerPosition.X - size.X / 2;
double y = centerPosition.Y - size.Y / 2;
double z = centerPosition.Z - size.Z / 2;
double maxX = x + size.X;
double maxY = y + size.Y;
double maxZ = z + size.Z;
Point3D p1, p2, p3, p4;
var multiPolylines = new List<Point3DCollection>();
if (MathUtils.IsZero(size.X))
{
p1 = new Point3D(x, y, z);
p2 = new Point3D(x, maxY, z);
p3 = new Point3D(x, maxY, maxZ);
p4 = new Point3D(x, y, maxZ);
}
else if (MathUtils.IsZero(size.Y))
{
p1 = new Point3D(x, y, z);
p2 = new Point3D(maxX, y, z);
p3 = new Point3D(maxX, y, maxZ);
p4 = new Point3D(x, y, maxZ);
}
else //if (MathUtils.IsZero(size.Z))
{
p1 = new Point3D(x, y, z);
p2 = new Point3D(x, maxY, z);
p3 = new Point3D(maxX, maxY, z);
p4 = new Point3D(maxX, y, z);
}
var direction1 = p2 - p1;
var length1 = direction1.Length;
direction1 /= length1;
var direction2 = p3 - p2;
var length2 = direction1.Length;
direction2 /= length2;
if (isLineLengthPercent)
{
direction1 *= length1 * lineLength;
direction2 *= length2 * lineLength;
}
else
{
direction1 *= lineLength;
direction2 *= lineLength;
}
var oneCornerPositions = new Point3DCollection(3);
oneCornerPositions.Add(p1 + direction2);
oneCornerPositions.Add(p1);
oneCornerPositions.Add(p1 + direction1);
multiPolylines.Add(oneCornerPositions);
oneCornerPositions = new Point3DCollection(3);
oneCornerPositions.Add(p2 - direction1);
oneCornerPositions.Add(p2);
oneCornerPositions.Add(p2 + direction2);
multiPolylines.Add(oneCornerPositions);
oneCornerPositions = new Point3DCollection(3);
oneCornerPositions.Add(p3 - direction2);
oneCornerPositions.Add(p3);
oneCornerPositions.Add(p3 - direction1);
multiPolylines.Add(oneCornerPositions);
oneCornerPositions = new Point3DCollection(3);
oneCornerPositions.Add(p4 + direction1);
oneCornerPositions.Add(p4);
oneCornerPositions.Add(p4 - direction2);
multiPolylines.Add(oneCornerPositions);
this.Content = Ab3d.Models.Line3DFactory.CreateMultiPolyLine3D(
multiPolylines,
this.LineThickness,
this.LineColor,
LineCap.Flat, LineCap.Flat,
miterLimit: 6.0,
parentVisual3D: this);
return;
}
// All size components are bigger then 0 => create a box with 24 positions
var positions = Ab3d.Models.Line3DFactory.CreateWireBox3DPositions(this.CenterPosition, size);
// Now divide each line into two lines - one at the start position and one at the end position
var cornerLinePositions = new Point3DCollection(positions.Count * 2);
for (int i = 0; i < positions.Count; i += 2)
{
Point3D p1 = positions[i];
Point3D p4 = positions[i + 1];
var direction = p4 - p1;
var length = direction.Length;
direction = direction / length; // Normalize
Point3D p2, p3;
if (isLineLengthPercent)
{
p2 = p1 + direction * length * lineLength;
p3 = p4 - direction * length * lineLength;
}
else
{
p2 = p1 + direction * lineLength;
p3 = p4 - direction * lineLength;
}
cornerLinePositions.Add(p1);
cornerLinePositions.Add(p2);
cornerLinePositions.Add(p3);
cornerLinePositions.Add(p4);
}
SetPositions(cornerLinePositions, raisePropertyChangedEvent: false);
this.Content = Ab3d.Models.Line3DFactory.CreateMultiLine3D(cornerLinePositions, this.LineThickness, this.LineColor, this);
}
EllipseLineVisual3D:
Code:
/// <summary>
/// Creates this Model3D
/// </summary>
protected override void CreateModel()
{
var segments = this.Segments;
bool createNewPositions = _positions == null;
if (createNewPositions)
{
_positions = new Point3DCollection(segments + 1);
}
else
{
SetPositions(null, raisePropertyChangedEvent: false); // Disconnect _positions, otherwise each change on the collection will trigger OnPropertyChanged event
_positions.Clear();
}
var ellipseNormalVector = Vector3D.CrossProduct(WidthDirection, HeightDirection);
Ab3d.Utilities.CurvesHelper.AddArc3DPoints(CenterPosition, ellipseNormalVector, WidthDirection, Width * 0.5, Height * 0.5, 0, 360, segments, _positions);
this.Content = Ab3d.Models.Line3DFactory.CreatePolyLine3D(_positions, LineThickness, LineColor,
areLinesConnected: true, isClosed: false,
startLineCap: LineCap.Flat, endLineCap: LineCap.Flat,
lineSegmentsCountForArrowLength: 0,
miterLimit: Ab3d.Meshes.BaseConnectedLinesMesh3D.DefaultMiterLimit,
parentVisual3D: this, parentViewport3D: null);
}
You may also think if you really want to have mouse events subscribed to helper 3D lines such as CornerWireBoxVisual3D - I find this quite unusual.