Object3DControls are dynamically added for a given part

This commit is contained in:
LarsBrubaker 2020-09-12 19:44:18 -07:00
parent 77ec3d5909
commit a35347df4a
17 changed files with 143 additions and 197 deletions

View file

@ -43,8 +43,6 @@ namespace MatterHackers.MatterControl
{
public class ExtensionsConfig
{
private List<IObject3DControlProvider> _iaVolumeProviders = new List<IObject3DControlProvider>();
private LibraryConfig libraryConfig;
// private List<IObject3DEditor> _IObject3DEditorProviders = new List<IObject3DEditor>()
@ -75,13 +73,6 @@ namespace MatterHackers.MatterControl
}
}
public IEnumerable<IObject3DControlProvider> IAVolumeProviders => _iaVolumeProviders;
public void Register(IObject3DControlProvider volumeProvider)
{
_iaVolumeProviders.Add(volumeProvider);
}
public void Register(IObject3DEditor object3DEditor)
{
this.MapTypesToEditor(object3DEditor);

View file

@ -48,9 +48,6 @@ namespace MatterHackers.Plugins.EditorTools
applicationController.Library.RegisterCreator(item);
}
applicationController.Extensions.Register(new ScaleCornersPlugin());
applicationController.Extensions.Register(new RotateCornerPlugins());
applicationController.Extensions.Register(new OpenSCADBuilder());
// applicationController.Extensions.Register(new PrimitivesEditor());
}

View file

@ -36,6 +36,7 @@ using MatterHackers.Agg.VertexSource;
using MatterHackers.DataConverters3D;
using MatterHackers.MatterControl;
using MatterHackers.MatterControl.DesignTools;
using MatterHackers.MatterControl.PartPreviewWindow;
using MatterHackers.MeshVisualizer;
using MatterHackers.RayTracer;
using MatterHackers.RenderOpenGl;
@ -44,7 +45,7 @@ using MatterHackers.VectorMath;
namespace MatterHackers.Plugins.EditorTools
{
public class PathControl : IObject3DControl
public class PathControl : IObject3DControl, IObject3DControlsProvider
{
private readonly IObject3DControlContext context;
@ -160,11 +161,6 @@ namespace MatterHackers.Plugins.EditorTools
}
}
public void LostFocus()
{
this.Reset();
}
private void Reset()
{
// Clear and close selection targets
@ -178,7 +174,7 @@ namespace MatterHackers.Plugins.EditorTools
lastItem = null;
}
public void SetPosition(IObject3D selectedItem)
public void SetPosition(IObject3D selectedItem, MeshSelectInfo selectInfo)
{
if (selectedItem != lastItem)
{
@ -290,6 +286,21 @@ namespace MatterHackers.Plugins.EditorTools
throw new NotImplementedException();
}
public IEnumerable<IObject3DControl> CreateObject3DControls(IObject3DControlContext context)
{
return new List<IObject3DControl> { new PathControl(context) };
}
public void Dispose()
{
Reset();
}
public List<IObject3DControl> GetObject3DControls(Object3DControlsLayer object3DControlsLayer)
{
throw new NotImplementedException();
}
private class CurveControlPoint : VertexPointWidget
{
public CurveControlPoint(IObject3DControlContext context, PathControl pathControl, VertexStorage vertexStorage, Vector3 point, ShapePath.FlagsAndCommand flagsandCommand, int index)

View file

@ -455,7 +455,7 @@ namespace MatterHackers.Plugins.EditorTools
base.CancelOperation();
}
public override void SetPosition(IObject3D selectedItem)
public override void SetPosition(IObject3D selectedItem, MeshSelectInfo selectInfo)
{
Vector3 boxCenter = GetControlCenter(selectedItem);
double distBetweenPixelsWorldSpace = Object3DControlContext.World.GetWorldUnitsPerScreenPixelAtPosition(boxCenter);
@ -847,6 +847,11 @@ namespace MatterHackers.Plugins.EditorTools
selectedItem.Matrix = selectedItem.Matrix.ApplyAtPosition(mouseDownInfo.SelectedObjectRotationCenter, rotationMatrix);
}
public override void Dispose()
{
angleTextControl.Close();
}
internal class Mouse3DInfo
{
internal Mouse3DInfo(Vector3 downPosition, Matrix4X4 selectedObjectTransform, Vector3 selectedObjectRotationCenter, Vector3 controlCenter, int rotationAxis)

View file

@ -1,48 +0,0 @@
/*
Copyright (c) 2017, Lars Brubaker, John Lewin
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
The views and conclusions contained in the software and documentation are those
of the authors and should not be interpreted as representing official policies,
either expressed or implied, of the FreeBSD Project.
*/
using System.Collections.Generic;
using MatterHackers.MeshVisualizer;
namespace MatterHackers.Plugins.EditorTools
{
public class RotateCornerPlugins : IObject3DControlProvider
{
public IEnumerable<IObject3DControl> Create(IObject3DControlContext context)
{
// X, Y, Z RotateCornerControls
return new[]
{
new RotateCornerControl(context, 0),
new RotateCornerControl(context, 1),
new RotateCornerControl(context, 2)
};
}
}
}

View file

@ -360,7 +360,7 @@ namespace MatterHackers.Plugins.EditorTools
base.CancelOperation();
}
public override void SetPosition(IObject3D selectedItem)
public override void SetPosition(IObject3D selectedItem, MeshSelectInfo selectInfo)
{
Vector3 cornerPosition = GetCornerPosition(selectedItem, quadrantIndex);
double distBetweenPixelsWorldSpace = Object3DControlContext.World.GetWorldUnitsPerScreenPixelAtPosition(cornerPosition);
@ -536,5 +536,11 @@ namespace MatterHackers.Plugins.EditorTools
}
}
}
public override void Dispose()
{
xValueDisplayInfo.Close();
yValueDisplayInfo.Close();
}
}
}

View file

@ -1,49 +0,0 @@
/*
Copyright (c) 2017, Lars Brubaker, John Lewin
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
The views and conclusions contained in the software and documentation are those
of the authors and should not be interpreted as representing official policies,
either expressed or implied, of the FreeBSD Project.
*/
using System.Collections.Generic;
using MatterHackers.MeshVisualizer;
namespace MatterHackers.Plugins.EditorTools
{
public class ScaleCornersPlugin : IObject3DControlProvider
{
public IEnumerable<IObject3DControl> Create(IObject3DControlContext context)
{
return new List<IObject3DControl>
{
new ScaleTopControl(context),
new ScaleCornerControl(context, 0),
new ScaleCornerControl(context, 1),
new ScaleCornerControl(context, 2),
new ScaleCornerControl(context, 3)
};
}
}
}

View file

@ -319,7 +319,7 @@ namespace MatterHackers.Plugins.EditorTools
base.CancelOperation();
}
public override void SetPosition(IObject3D selectedItem)
public override void SetPosition(IObject3D selectedItem, MeshSelectInfo selectInfo)
{
AxisAlignedBoundingBox selectedBounds = selectedItem.GetAxisAlignedBoundingBox();
@ -374,5 +374,10 @@ namespace MatterHackers.Plugins.EditorTools
}
}
}
public override void Dispose()
{
zValueDisplayInfo.Close();
}
}
}

View file

@ -1,5 +1,5 @@
/*
Copyright (c) 2019, Lars Brubaker, John Lewin
Copyright (c) 2017, Lars Brubaker, John Lewin
All rights reserved.
Redistribution and use in source and binary forms, with or without
@ -28,11 +28,15 @@ either expressed or implied, of the FreeBSD Project.
*/
using System.Collections.Generic;
using MatterHackers.MatterControl.PartPreviewWindow;
using MatterHackers.MeshVisualizer;
namespace MatterHackers.MeshVisualizer
namespace MatterHackers.MatterControl.DesignTools
{
public interface IObject3DControlProvider
public interface IObject3DControlsProvider
{
IEnumerable<IObject3DControl> Create(IObject3DControlContext context);
List<IObject3DControl> GetObject3DControls(Object3DControlsLayer object3DControlsLayer);
}
}

View file

@ -28,6 +28,7 @@ either expressed or implied, of the FreeBSD Project.
*/
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
@ -37,12 +38,14 @@ using MatterHackers.Agg.ImageProcessing;
using MatterHackers.Agg.Platform;
using MatterHackers.DataConverters3D;
using MatterHackers.Localizations;
using MatterHackers.MatterControl.PartPreviewWindow;
using MatterHackers.MeshVisualizer;
using MatterHackers.PolygonMesh;
using Newtonsoft.Json;
namespace MatterHackers.MatterControl.DesignTools
{
public class ImageObject3D : AssetObject3D
public class ImageObject3D : AssetObject3D, IObject3DControlsProvider
{
private const double DefaultSizeMm = 60;
@ -126,6 +129,11 @@ namespace MatterHackers.MatterControl.DesignTools
}
}
public List<IObject3DControl> GetObject3DControls(Object3DControlsLayer object3DControlsLayer)
{
return new List<IObject3DControl> { new MoveInZControl(object3DControlsLayer) };
}
private ImageBuffer LoadImage()
{
// TODO: Consider non-awful alternatives

View file

@ -147,6 +147,11 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
Object3DControlContext.GuiSurface.AfterDraw += Object3DControl_AfterDraw;
}
public override void Dispose()
{
zHeightDisplayInfo.Close();
}
public override void Draw(DrawGlContentEventArgs e)
{
bool shouldDrawMoveControls = true;
@ -280,7 +285,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
base.CancelOperation();
}
public override void SetPosition(IObject3D selectedItem)
public override void SetPosition(IObject3D selectedItem, MeshSelectInfo selectInfo)
{
AxisAlignedBoundingBox selectedBounds = selectedItem.GetAxisAlignedBoundingBox();

View file

@ -51,7 +51,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
shadowColor = theme.ResolveColor(theme.BackgroundColor, Color.Black.WithAlpha(80)).WithAlpha(110);
}
public override void SetPosition(IObject3D selectedItem)
public override void SetPosition(IObject3D selectedItem, MeshSelectInfo selectInfo)
{
AxisAlignedBoundingBox selectedBounds = selectedItem.GetAxisAlignedBoundingBox();
Vector3 boundsCenter = selectedBounds.Center;
@ -84,5 +84,10 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
base.Draw(e);
}
public override void Dispose()
{
// no widgets allocated so nothing to close
}
}
}

View file

@ -41,18 +41,24 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
private double lineLength = 15;
private Vector2[] lines = new Vector2[4];
private MeshSelectInfo meshSelectInfo;
public SnappingIndicators(IObject3DControlContext context, MeshSelectInfo currentSelectInfo)
public SnappingIndicators(IObject3DControlContext context)
: base(context)
{
this.DrawOnTop = true;
this.meshSelectInfo = currentSelectInfo;
Object3DControlContext.GuiSurface.AfterDraw += Object3DControl_AfterDraw;
}
public override void SetPosition(IObject3D selectedItem)
private MeshSelectInfo meshSelectInfo;
public override void Dispose()
{
// no objects need to be removed
}
public override void SetPosition(IObject3D selectedItem, MeshSelectInfo meshSelectInfo)
{
this.meshSelectInfo = meshSelectInfo;
// draw the hight from the bottom to the bed
AxisAlignedBoundingBox selectedBounds = selectedItem.GetAxisAlignedBoundingBox();
@ -71,6 +77,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
lines[2] = world.GetScreenPosition(cornerPoint - new Vector3(0, distToStart * distBetweenPixelsWorldSpace, 0));
lines[3] = world.GetScreenPosition(cornerPoint - new Vector3(0, (distToStart + lineLength) * distBetweenPixelsWorldSpace, 0));
}
break;
case HitQuadrant.LT:
@ -84,6 +91,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
lines[2] = world.GetScreenPosition(cornerPoint + new Vector3(0, distToStart * distBetweenPixelsWorldSpace, 0));
lines[3] = world.GetScreenPosition(cornerPoint + new Vector3(0, (distToStart + lineLength) * distBetweenPixelsWorldSpace, 0));
}
break;
case HitQuadrant.RB:
@ -97,6 +105,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
lines[2] = world.GetScreenPosition(cornerPoint - new Vector3(0, distToStart * distBetweenPixelsWorldSpace, 0));
lines[3] = world.GetScreenPosition(cornerPoint - new Vector3(0, (distToStart + lineLength) * distBetweenPixelsWorldSpace, 0));
}
break;
case HitQuadrant.RT:
@ -110,6 +119,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
lines[2] = world.GetScreenPosition(cornerPoint + new Vector3(0, distToStart * distBetweenPixelsWorldSpace, 0));
lines[3] = world.GetScreenPosition(cornerPoint + new Vector3(0, (distToStart + lineLength) * distBetweenPixelsWorldSpace, 0));
}
break;
}
}
@ -118,7 +128,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
{
if (Object3DControlContext.Scene.SelectedItem != null
&& Object3DControlContext.SnapGridDistance > 0
&& meshSelectInfo.DownOnPart)
&& meshSelectInfo?.DownOnPart == true)
{
if (drawEvent != null)
{

View file

@ -28,18 +28,20 @@ either expressed or implied, of the FreeBSD Project.
*/
using MatterHackers.DataConverters3D;
using MatterHackers.MatterControl.PartPreviewWindow;
using MatterHackers.RayTracer;
using System;
namespace MatterHackers.MeshVisualizer
{
/// <summary>
/// Basic Interaction control - notified of position per OpenGL draw
/// </summary>
public interface IObject3DControl
public interface IObject3DControl : IDisposable
{
string Name { get; }
void SetPosition(IObject3D selectedItem);
void SetPosition(IObject3D selectedItem, MeshSelectInfo selectInfo);
/// <summary>
/// The Control has been requested to cancel (usually by the user).
@ -53,8 +55,6 @@ namespace MatterHackers.MeshVisualizer
void OnMouseUp(Mouse3DEventArgs mouseEvent3D);
void LostFocus();
/// <summary>
/// Gets or sets a value indicating whether the control is currently visible.
/// </summary>

View file

@ -29,6 +29,7 @@ either expressed or implied, of the FreeBSD Project.
using MatterHackers.Agg.UI;
using MatterHackers.DataConverters3D;
using MatterHackers.MatterControl.PartPreviewWindow;
using MatterHackers.RayTracer;
using MatterHackers.RayTracer.Traceable;
using MatterHackers.VectorMath;
@ -56,6 +57,8 @@ namespace MatterHackers.MeshVisualizer
public IntersectInfo MouseMoveInfo { get; set; }
public abstract void Dispose();
public bool MouseIsOver
{
get => _mouseIsOver;
@ -113,10 +116,6 @@ namespace MatterHackers.MeshVisualizer
{
}
public virtual void LostFocus()
{
}
public virtual void OnMouseDown(Mouse3DEventArgs mouseEvent3D)
{
if (mouseEvent3D.MouseEvent2D.Button == MouseButtons.Left)
@ -135,7 +134,7 @@ namespace MatterHackers.MeshVisualizer
MouseDownOnControl = false;
}
public virtual void SetPosition(IObject3D selectedItem)
public virtual void SetPosition(IObject3D selectedItem, MeshSelectInfo selectInfo)
{
}

View file

@ -53,37 +53,25 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
{
private IObject3DControl mouseDownObject3DControl = null;
/// <summary>
/// Gets the mapping for Object3DControls for a given type
/// </summary>
private Dictionary<Type, List<IObject3DControl>> Object3DControlMappings { get; } = new Dictionary<Type, List<IObject3DControl>>();
/// <summary>
/// Object3DControl Overrides for the selected scene item
/// </summary>
private List<IObject3DControl> Object3DControlOverrides = null;
private Type selectedItemType;
public WorldView World => sceneContext.World;
public InteractiveScene Scene => sceneContext.Scene;
public bool DrawOpenGLContent { get; set; } = true;
private List<IObject3DControl> DefaultObject3DControls { get; } = new List<IObject3DControl>();
private List<IObject3DControl> CurrentObject3DControls { get; set; } = new List<IObject3DControl>();
public IEnumerable<IObject3DControl> Object3DControls
{
get
{
if (selectedItemType == null)
if (CurrentObject3DControls == null)
{
return Enumerable.Empty<IObject3DControl>();
}
else
{
return Object3DControlOverrides ?? DefaultObject3DControls;
return CurrentObject3DControls;
}
}
}
@ -119,9 +107,6 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
});
}
Object3DControlMappings.Add(typeof(ImageObject3D), new List<IObject3DControl> { new MoveInZControl(this) });
Object3DControlMappings.Add(typeof(PathObject3D), new List<IObject3DControl> { new PathControl(this) });
// Register listeners
sceneContext.Scene.SelectionChanged += this.Scene_SelectionChanged;
if (sceneContext.Printer != null)
@ -149,16 +134,6 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
drawables.Add(drawable);
}
public void RegisterObject3DControl(IObject3DControl object3DControl)
{
DefaultObject3DControls.Add(object3DControl);
}
public void RegisterObject3DControls(IEnumerable<IObject3DControl> Object3DControls)
{
DefaultObject3DControls.AddRange(Object3DControls);
}
public IEnumerable<IDrawable> Drawables => drawables;
public IEnumerable<IDrawableItem> ItemDrawables => itemDrawables;
@ -189,19 +164,43 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
private void Scene_SelectionChanged(object sender, EventArgs e)
{
foreach (var item in this.Object3DControls)
{
item.LostFocus();
}
DisposeCurrentSelectionObject3DControls();
// On selection change, update state for mappings
selectedItemType = scene.SelectedItem?.GetType();
Object3DControlOverrides = null;
CurrentObject3DControls = null;
if (selectedItemType != null)
if (scene.SelectedItem is IObject3DControlsProvider provider)
{
Object3DControlMappings.TryGetValue(selectedItemType, out Object3DControlOverrides);
CurrentObject3DControls = provider.GetObject3DControls(this);
}
else
{
CurrentObject3DControls = new List<IObject3DControl>(new IObject3DControl[]
{
// add default controls
new RotateCornerControl(this, 0),
new RotateCornerControl(this, 1),
new RotateCornerControl(this, 2),
new MoveInZControl(this),
new ScaleTopControl(this),
new ScaleCornerControl(this, 0),
new ScaleCornerControl(this, 1),
new ScaleCornerControl(this, 2),
new ScaleCornerControl(this, 3),
new SelectionShadow(this),
new SnappingIndicators(this),
});
}
}
private void DisposeCurrentSelectionObject3DControls()
{
foreach (var item in this.Object3DControls)
{
item.Dispose();
}
this.CurrentObject3DControls = null;
}
public static void RenderBounds(DrawEventArgs e, WorldView world, IEnumerable<BvhIterator> allResults)
@ -390,9 +389,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
base.OnClosed(e);
// Clear lists and references
Object3DControlOverrides?.Clear();
Object3DControlMappings.Clear();
DefaultObject3DControls.Clear();
DisposeCurrentSelectionObject3DControls();
drawables.Clear();
itemDrawables.Clear();
SelectedObject3DControl = null;

View file

@ -345,16 +345,6 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
viewOptionsBar.AddChild(gridSnapButton);
this.Object3DControlLayer.RegisterObject3DControl(new MoveInZControl(this.Object3DControlLayer));
this.Object3DControlLayer.RegisterObject3DControl(new SelectionShadow(this.Object3DControlLayer));
this.Object3DControlLayer.RegisterObject3DControl(new SnappingIndicators(this.Object3DControlLayer, this.CurrentSelectInfo));
// Add IAVolumeProviderPlugins
foreach (var ivProvider in ApplicationController.Instance.Extensions.IAVolumeProviders)
{
this.Object3DControlLayer.RegisterObject3DControls(ivProvider.Create(this.Object3DControlLayer));
}
this.Object3DControlLayer.AfterDraw += AfterDraw3DContent;
Scene.SelectFirstChild();
@ -888,7 +878,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
{
foreach (var volume in this.Object3DControlLayer.Object3DControls)
{
volume.SetPosition(selectedItem);
volume.SetPosition(selectedItem, CurrentSelectInfo);
}
}
@ -1782,14 +1772,24 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
DrawStage IDrawable.DrawStage { get; } = DrawStage.OpaqueContent;
}
public enum HitQuadrant { LB, LT, RB, RT }
public enum HitQuadrant
{
LB,
LT,
RB,
RT
}
public class MeshSelectInfo
{
public HitQuadrant HitQuadrant;
public bool DownOnPart;
public PlaneShape HitPlane;
public Vector3 LastMoveDelta;
public Vector3 PlaneDownHitPos;
public HitQuadrant HitQuadrant { get; set; }
public bool DownOnPart { get; set; }
public PlaneShape HitPlane { get; set; }
public Vector3 LastMoveDelta { get; set; }
public Vector3 PlaneDownHitPos { get; set; }
}
}