2017-06-21 23:26:20 -07:00
|
|
|
|
/*
|
2019-01-30 08:08:24 -08:00
|
|
|
|
Copyright (c) 2019, Lars Brubaker
|
2017-06-21 23:26:20 -07:00
|
|
|
|
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.
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
2017-07-18 18:15:10 -07:00
|
|
|
|
using System;
|
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
|
using System.IO;
|
|
|
|
|
|
using System.Linq;
|
2019-05-04 08:35:43 -07:00
|
|
|
|
using System.Threading;
|
|
|
|
|
|
using System.Threading.Tasks;
|
2017-06-21 23:26:20 -07:00
|
|
|
|
using MatterHackers.Agg;
|
2018-01-23 13:48:21 -08:00
|
|
|
|
using MatterHackers.Agg.Image;
|
2017-06-21 23:26:20 -07:00
|
|
|
|
using MatterHackers.Agg.UI;
|
|
|
|
|
|
using MatterHackers.DataConverters3D;
|
2018-12-21 08:27:40 -08:00
|
|
|
|
using MatterHackers.MatterControl.DesignTools;
|
2018-06-05 13:38:25 -07:00
|
|
|
|
using MatterHackers.MatterControl.PartPreviewWindow.View3D;
|
2019-01-31 07:33:42 -08:00
|
|
|
|
using MatterHackers.MeshVisualizer;
|
2017-06-21 23:26:20 -07:00
|
|
|
|
using MatterHackers.PolygonMesh;
|
|
|
|
|
|
using MatterHackers.RenderOpenGl;
|
|
|
|
|
|
using MatterHackers.RenderOpenGl.OpenGl;
|
|
|
|
|
|
using MatterHackers.VectorMath;
|
|
|
|
|
|
|
2019-01-31 07:33:42 -08:00
|
|
|
|
namespace MatterHackers.MatterControl.PartPreviewWindow
|
2017-06-21 23:26:20 -07:00
|
|
|
|
{
|
2019-01-31 07:33:42 -08:00
|
|
|
|
public partial class InteractionLayer : GuiWidget
|
2017-06-21 23:26:20 -07:00
|
|
|
|
{
|
2018-07-20 15:40:14 -07:00
|
|
|
|
private static ImageBuffer ViewOnlyTexture;
|
2018-01-23 13:48:21 -08:00
|
|
|
|
|
2018-02-02 18:19:23 -08:00
|
|
|
|
private Color lightWireframe = new Color("#aaa4");
|
|
|
|
|
|
private Color darkWireframe = new Color("#3334");
|
|
|
|
|
|
private Color gCodeMeshColor;
|
|
|
|
|
|
|
2019-05-03 07:57:44 -07:00
|
|
|
|
private readonly InteractiveScene scene;
|
2017-06-21 23:26:20 -07:00
|
|
|
|
|
2019-05-03 07:57:44 -07:00
|
|
|
|
private readonly ISceneContext sceneContext;
|
2017-08-16 17:13:16 -07:00
|
|
|
|
|
2019-05-03 07:57:44 -07:00
|
|
|
|
private readonly ThemeConfig theme;
|
|
|
|
|
|
private readonly FloorDrawable floorDrawable;
|
2019-01-31 22:04:39 -08:00
|
|
|
|
|
|
|
|
|
|
private ModelRenderStyle modelRenderStyle = ModelRenderStyle.Wireframe;
|
|
|
|
|
|
|
2019-05-03 07:57:44 -07:00
|
|
|
|
private readonly List<IDrawable> drawables = new List<IDrawable>();
|
|
|
|
|
|
private readonly List<IDrawableItem> itemDrawables = new List<IDrawableItem>();
|
2019-05-22 09:18:26 -07:00
|
|
|
|
|
2019-05-02 22:18:41 -07:00
|
|
|
|
private bool emulatorHooked;
|
|
|
|
|
|
private long lastEmulatorDrawMs;
|
2019-05-03 07:57:44 -07:00
|
|
|
|
private readonly Mesh emulatorNozzleMesh = PlatonicSolids.CreateCube(1, 1, 10);
|
2019-02-22 23:13:56 -08:00
|
|
|
|
|
2017-06-21 23:26:20 -07:00
|
|
|
|
public bool AllowBedRenderingWhenEmpty { get; set; }
|
|
|
|
|
|
|
2017-10-31 11:43:25 -07:00
|
|
|
|
public Color BuildVolumeColor { get; set; }
|
2017-06-21 23:26:20 -07:00
|
|
|
|
|
|
|
|
|
|
public override void OnLoad(EventArgs args)
|
|
|
|
|
|
{
|
2019-04-25 14:56:21 -07:00
|
|
|
|
#if DEBUG
|
2019-02-02 13:52:26 -08:00
|
|
|
|
drawables.AddRange(new IDrawable[]
|
2019-02-01 12:45:04 -08:00
|
|
|
|
{
|
2019-02-02 13:52:26 -08:00
|
|
|
|
new AxisIndicatorDrawable(),
|
|
|
|
|
|
new SceneTraceDataDrawable(sceneContext),
|
2019-02-22 21:53:34 -08:00
|
|
|
|
new AABBDrawable(sceneContext),
|
2019-05-10 08:30:36 -07:00
|
|
|
|
new LevelingDataDrawable(sceneContext),
|
2019-02-01 12:45:04 -08:00
|
|
|
|
});
|
2019-04-25 14:56:21 -07:00
|
|
|
|
#endif
|
2019-02-02 13:52:26 -08:00
|
|
|
|
itemDrawables.AddRange(new IDrawableItem[]
|
2019-02-01 12:45:04 -08:00
|
|
|
|
{
|
2019-02-02 13:52:26 -08:00
|
|
|
|
new SelectedItemDrawable(sceneContext, this),
|
2019-02-02 13:52:46 -08:00
|
|
|
|
new ItemTraceDataDrawable(sceneContext)
|
2019-02-01 12:45:04 -08:00
|
|
|
|
});
|
2019-01-31 22:37:52 -08:00
|
|
|
|
|
|
|
|
|
|
#if DEBUG
|
2019-05-10 08:30:36 -07:00
|
|
|
|
itemDrawables.AddRange(new IDrawableItem[]
|
|
|
|
|
|
{
|
|
|
|
|
|
new InspectedItemDrawable(sceneContext),
|
|
|
|
|
|
new NormalsDrawable(sceneContext)
|
|
|
|
|
|
});
|
2019-01-31 22:37:52 -08:00
|
|
|
|
#endif
|
2019-01-31 22:04:39 -08:00
|
|
|
|
|
2019-02-02 00:03:09 -08:00
|
|
|
|
base.OnLoad(args);
|
2018-11-16 08:44:56 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
2018-12-30 08:17:01 -08:00
|
|
|
|
public override List<WidgetAndPosition> FindDescendants(IEnumerable<string> namesToSearchFor, List<WidgetAndPosition> foundChildren, RectangleDouble touchingBounds, SearchType seachType, bool allowInvalidItems = true)
|
2017-06-21 23:26:20 -07:00
|
|
|
|
{
|
2019-01-31 07:33:42 -08:00
|
|
|
|
foreach (InteractionVolume child in this.InteractionVolumes)
|
2017-10-26 17:57:17 -07:00
|
|
|
|
{
|
|
|
|
|
|
string object3DName = child.Name;
|
|
|
|
|
|
|
|
|
|
|
|
bool nameFound = false;
|
|
|
|
|
|
|
2018-12-30 08:17:01 -08:00
|
|
|
|
foreach (var nameToSearchFor in namesToSearchFor)
|
2017-10-26 17:57:17 -07:00
|
|
|
|
{
|
2018-12-30 08:17:01 -08:00
|
|
|
|
if (seachType == SearchType.Exact)
|
2017-10-26 17:57:17 -07:00
|
|
|
|
{
|
2018-12-30 08:17:01 -08:00
|
|
|
|
if (object3DName == nameToSearchFor)
|
|
|
|
|
|
{
|
|
|
|
|
|
nameFound = true;
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
2017-10-26 17:57:17 -07:00
|
|
|
|
}
|
2018-12-30 08:17:01 -08:00
|
|
|
|
else
|
2017-10-26 17:57:17 -07:00
|
|
|
|
{
|
2018-12-30 08:17:01 -08:00
|
|
|
|
if (nameToSearchFor == ""
|
|
|
|
|
|
|| object3DName.Contains(nameToSearchFor))
|
|
|
|
|
|
{
|
|
|
|
|
|
nameFound = true;
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
2017-10-26 17:57:17 -07:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2017-10-27 14:19:26 -07:00
|
|
|
|
if (nameFound
|
|
|
|
|
|
&& child.CollisionVolume != null)
|
2017-10-26 17:57:17 -07:00
|
|
|
|
{
|
|
|
|
|
|
AxisAlignedBoundingBox bounds = child.CollisionVolume.GetAxisAlignedBoundingBox();
|
|
|
|
|
|
bounds = bounds.NewTransformed(child.TotalTransform);
|
|
|
|
|
|
|
|
|
|
|
|
RectangleDouble screenBoundsOfObject3D = RectangleDouble.ZeroIntersection;
|
|
|
|
|
|
for (int i = 0; i < 4; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
screenBoundsOfObject3D.ExpandToInclude(this.World.GetScreenPosition(bounds.GetTopCorner(i)));
|
|
|
|
|
|
screenBoundsOfObject3D.ExpandToInclude(this.World.GetScreenPosition(bounds.GetBottomCorner(i)));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (touchingBounds.IsTouching(screenBoundsOfObject3D))
|
|
|
|
|
|
{
|
|
|
|
|
|
Vector3 renderPosition = bounds.Center;
|
|
|
|
|
|
Vector2 objectCenterScreenSpace = this.World.GetScreenPosition(renderPosition);
|
2019-05-02 18:06:28 -07:00
|
|
|
|
var screenPositionOfObject3D = new Point2D((int)objectCenterScreenSpace.X, (int)objectCenterScreenSpace.Y);
|
2017-10-26 17:57:17 -07:00
|
|
|
|
|
2017-10-27 14:19:26 -07:00
|
|
|
|
foundChildren.Add(new WidgetAndPosition(this, screenPositionOfObject3D, object3DName, child));
|
2017-10-26 17:57:17 -07:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2017-08-14 08:57:24 -07:00
|
|
|
|
foreach (var child in scene.Children)
|
2017-06-21 23:26:20 -07:00
|
|
|
|
{
|
|
|
|
|
|
string object3DName = child.Name;
|
|
|
|
|
|
if (object3DName == null && child.MeshPath != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
object3DName = Path.GetFileName(child.MeshPath);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool nameFound = false;
|
|
|
|
|
|
|
2018-12-30 08:17:01 -08:00
|
|
|
|
foreach (var nameToSearchFor in namesToSearchFor)
|
2017-06-21 23:26:20 -07:00
|
|
|
|
{
|
2018-12-30 08:17:01 -08:00
|
|
|
|
if (seachType == SearchType.Exact)
|
2017-06-21 23:26:20 -07:00
|
|
|
|
{
|
2018-12-30 08:17:01 -08:00
|
|
|
|
if (object3DName == nameToSearchFor)
|
|
|
|
|
|
{
|
|
|
|
|
|
nameFound = true;
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
2017-06-21 23:26:20 -07:00
|
|
|
|
}
|
2018-12-30 08:17:01 -08:00
|
|
|
|
else
|
2017-06-21 23:26:20 -07:00
|
|
|
|
{
|
2018-12-30 08:17:01 -08:00
|
|
|
|
if (nameToSearchFor == ""
|
|
|
|
|
|
|| object3DName.Contains(nameToSearchFor))
|
|
|
|
|
|
{
|
|
|
|
|
|
nameFound = true;
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
2017-06-21 23:26:20 -07:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (nameFound)
|
|
|
|
|
|
{
|
|
|
|
|
|
AxisAlignedBoundingBox bounds = child.TraceData().GetAxisAlignedBoundingBox();
|
|
|
|
|
|
|
|
|
|
|
|
RectangleDouble screenBoundsOfObject3D = RectangleDouble.ZeroIntersection;
|
2019-05-02 18:06:28 -07:00
|
|
|
|
for (int i = 0; i < 4; i++)
|
2017-06-21 23:26:20 -07:00
|
|
|
|
{
|
|
|
|
|
|
screenBoundsOfObject3D.ExpandToInclude(this.World.GetScreenPosition(bounds.GetTopCorner(i)));
|
|
|
|
|
|
screenBoundsOfObject3D.ExpandToInclude(this.World.GetScreenPosition(bounds.GetBottomCorner(i)));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (touchingBounds.IsTouching(screenBoundsOfObject3D))
|
|
|
|
|
|
{
|
|
|
|
|
|
Vector3 renderPosition = bounds.Center;
|
|
|
|
|
|
Vector2 objectCenterScreenSpace = this.World.GetScreenPosition(renderPosition);
|
2019-05-02 18:06:28 -07:00
|
|
|
|
var screenPositionOfObject3D = new Point2D((int)objectCenterScreenSpace.X, (int)objectCenterScreenSpace.Y);
|
2017-06-21 23:26:20 -07:00
|
|
|
|
|
2017-10-27 14:19:26 -07:00
|
|
|
|
foundChildren.Add(new WidgetAndPosition(this, screenPositionOfObject3D, object3DName, child));
|
2017-06-21 23:26:20 -07:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-12-30 08:17:01 -08:00
|
|
|
|
return base.FindDescendants(namesToSearchFor, foundChildren, touchingBounds, seachType, allowInvalidItems);
|
2017-06-21 23:26:20 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
2018-12-21 08:27:40 -08:00
|
|
|
|
private void DrawObject(IObject3D object3D, List<Object3DView> transparentMeshes, DrawEventArgs e)
|
2017-06-21 23:26:20 -07:00
|
|
|
|
{
|
2018-12-21 08:27:40 -08:00
|
|
|
|
var selectedItem = scene.SelectedItem;
|
|
|
|
|
|
|
2017-11-28 15:34:49 -08:00
|
|
|
|
foreach (var item in object3D.VisibleMeshes())
|
2017-06-21 23:26:20 -07:00
|
|
|
|
{
|
2019-05-04 08:35:43 -07:00
|
|
|
|
// check for correct protection rendering
|
|
|
|
|
|
ValidateViewOnlyTexturing(item);
|
2018-01-23 13:48:21 -08:00
|
|
|
|
|
2019-02-01 14:47:05 -08:00
|
|
|
|
Color drawColor = this.GetItemColor(item, selectedItem);
|
2017-08-01 17:38:07 -07:00
|
|
|
|
|
2019-02-01 14:47:05 -08:00
|
|
|
|
bool hasTransparentTextures = item.Mesh.FaceTextures.Any(ft => ft.Value.image.HasTransparency);
|
2019-01-11 16:49:34 -08:00
|
|
|
|
|
2018-03-05 10:24:26 -08:00
|
|
|
|
if ((drawColor.alpha == 255
|
2019-01-11 16:49:34 -08:00
|
|
|
|
&& !hasTransparentTextures)
|
2019-01-31 22:29:21 -08:00
|
|
|
|
|| (item == scene.DebugItem))
|
2017-08-25 12:39:52 -07:00
|
|
|
|
{
|
2018-02-02 18:33:35 -08:00
|
|
|
|
// Render as solid
|
2018-11-01 09:36:58 -07:00
|
|
|
|
GLHelper.Render(item.Mesh,
|
|
|
|
|
|
drawColor,
|
|
|
|
|
|
item.WorldMatrix(),
|
|
|
|
|
|
sceneContext.ViewState.RenderType,
|
|
|
|
|
|
item.WorldMatrix() * World.ModelviewMatrix,
|
2019-05-02 18:06:28 -07:00
|
|
|
|
darkWireframe,
|
|
|
|
|
|
() => Invalidate());
|
2017-08-25 12:39:52 -07:00
|
|
|
|
}
|
2018-02-02 18:19:23 -08:00
|
|
|
|
else if (drawColor != Color.Transparent)
|
2017-08-25 12:39:52 -07:00
|
|
|
|
{
|
2018-02-02 18:33:35 -08:00
|
|
|
|
// Queue for transparency
|
2018-05-29 17:46:59 -07:00
|
|
|
|
transparentMeshes.Add(new Object3DView(item, drawColor));
|
2017-08-25 12:39:52 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
2018-07-05 15:15:56 -07:00
|
|
|
|
bool isSelected = selectedItem != null
|
2019-05-22 17:09:01 -07:00
|
|
|
|
&& (item == selectedItem
|
2019-05-22 17:26:28 -07:00
|
|
|
|
|| item.Ancestors().Any(p => p == selectedItem));
|
2018-02-02 18:00:21 -08:00
|
|
|
|
|
2019-01-31 22:37:52 -08:00
|
|
|
|
// Invoke all item Drawables
|
2019-05-02 18:06:28 -07:00
|
|
|
|
foreach (var drawable in itemDrawables.Where(d => d.DrawStage != DrawStage.Last && d.Enabled))
|
2019-01-31 22:37:52 -08:00
|
|
|
|
{
|
2019-02-02 00:03:09 -08:00
|
|
|
|
drawable.Draw(this, item, isSelected, e, Matrix4X4.Identity, this.World);
|
2019-01-31 22:37:52 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
2017-08-25 12:39:52 -07:00
|
|
|
|
// turn lighting back on after rendering selection outlines
|
|
|
|
|
|
GL.Enable(EnableCap.Lighting);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2017-08-03 11:59:27 -07:00
|
|
|
|
|
2019-05-04 08:35:43 -07:00
|
|
|
|
private static void ValidateViewOnlyTexturing(IObject3D item)
|
|
|
|
|
|
{
|
|
|
|
|
|
// if there is no view only texture or the item is locked
|
|
|
|
|
|
if (InteractionLayer.ViewOnlyTexture == null
|
|
|
|
|
|
|| item.Mesh.Faces.Count == 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// if the item is not currently be processed
|
|
|
|
|
|
if (!item.RebuildLocked)
|
|
|
|
|
|
{
|
|
|
|
|
|
item.Mesh.FaceTextures.TryGetValue(0, out FaceTextureData faceTexture);
|
|
|
|
|
|
bool viewOnlyTexture = faceTexture?.image == InteractionLayer.ViewOnlyTexture;
|
|
|
|
|
|
|
|
|
|
|
|
// if not persistable and has view only texture, remove the view only texture if it has it
|
|
|
|
|
|
if (item.WorldPersistable()
|
|
|
|
|
|
&& viewOnlyTexture)
|
|
|
|
|
|
{
|
|
|
|
|
|
// make sure it does not have the view only texture
|
|
|
|
|
|
using (item.RebuildLock())
|
|
|
|
|
|
{
|
|
|
|
|
|
item.Mesh.RemoveTexture(ViewOnlyTexture, 0);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (!item.WorldPersistable()
|
|
|
|
|
|
&& !viewOnlyTexture)
|
|
|
|
|
|
{
|
|
|
|
|
|
// add a view only texture if it does not have one
|
|
|
|
|
|
// make a copy of the mesh and texture it
|
|
|
|
|
|
Task.Run(() =>
|
|
|
|
|
|
{
|
|
|
|
|
|
// make sure it does have the view only texture
|
|
|
|
|
|
var aabb = item.Mesh.GetAxisAlignedBoundingBox();
|
|
|
|
|
|
var matrix = Matrix4X4.CreateScale(.5, .5, 1);
|
|
|
|
|
|
matrix *= Matrix4X4.CreateRotationZ(MathHelper.Tau / 8);
|
|
|
|
|
|
// make sure it has it's own copy of the mesh
|
|
|
|
|
|
using (item.RebuildLock())
|
|
|
|
|
|
{
|
|
|
|
|
|
item.Mesh = item.Mesh.Copy(CancellationToken.None);
|
|
|
|
|
|
item.Mesh.PlaceTexture(ViewOnlyTexture, matrix);
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2019-02-01 14:47:05 -08:00
|
|
|
|
private Color GetItemColor(IObject3D item, IObject3D selectedItem)
|
2017-11-28 15:34:49 -08:00
|
|
|
|
{
|
2018-02-04 00:38:50 -08:00
|
|
|
|
Color drawColor = item.WorldColor();
|
|
|
|
|
|
if (item.WorldOutputType() == PrintOutputTypes.Support)
|
2017-11-28 15:34:49 -08:00
|
|
|
|
{
|
|
|
|
|
|
drawColor = new Color(Color.Yellow, 120);
|
|
|
|
|
|
}
|
2019-04-10 16:50:08 -07:00
|
|
|
|
else if (item.WorldOutputType() == PrintOutputTypes.WipeTower)
|
2019-03-17 10:58:28 -07:00
|
|
|
|
{
|
|
|
|
|
|
drawColor = new Color(Color.Cyan, 120);
|
|
|
|
|
|
}
|
2019-04-10 16:50:08 -07:00
|
|
|
|
else if (sceneContext.ViewState.RenderType == RenderTypes.Materials)
|
|
|
|
|
|
{
|
|
|
|
|
|
// check if we should be rendering materials (this overrides the other colors)
|
|
|
|
|
|
drawColor = MaterialRendering.Color(item.WorldMaterialIndex());
|
|
|
|
|
|
}
|
2017-11-28 15:34:49 -08:00
|
|
|
|
|
2019-04-10 16:50:08 -07:00
|
|
|
|
if (sceneContext.Printer is PrinterConfig printer)
|
2017-11-28 15:34:49 -08:00
|
|
|
|
{
|
2019-04-10 16:50:08 -07:00
|
|
|
|
if (printer.InsideBuildVolume(item))
|
2017-11-28 15:34:49 -08:00
|
|
|
|
{
|
2019-05-01 14:57:31 -07:00
|
|
|
|
if (printer.Settings.Helpers.HotendCount() > 1)
|
2019-04-10 16:50:08 -07:00
|
|
|
|
{
|
|
|
|
|
|
var materialIndex = item.WorldMaterialIndex();
|
|
|
|
|
|
if (materialIndex == -1)
|
|
|
|
|
|
{
|
|
|
|
|
|
materialIndex = 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2019-04-15 15:06:47 -07:00
|
|
|
|
bool isWipeTower = item?.OutputType == PrintOutputTypes.WipeTower;
|
2019-04-13 15:35:54 -07:00
|
|
|
|
|
2019-04-10 16:50:08 -07:00
|
|
|
|
// Determine if the given item is outside the bounds of the given extruder
|
2019-04-20 21:03:05 -07:00
|
|
|
|
if (materialIndex < printer.Settings.ToolBounds.Length
|
2019-04-13 15:35:54 -07:00
|
|
|
|
|| isWipeTower)
|
2019-04-10 16:50:08 -07:00
|
|
|
|
{
|
2019-04-15 15:06:47 -07:00
|
|
|
|
var itemAABB = item.WorldAxisAlignedBoundingBox();
|
2019-04-10 16:50:08 -07:00
|
|
|
|
var itemBounds = new RectangleDouble(new Vector2(itemAABB.MinXYZ), new Vector2(itemAABB.MaxXYZ));
|
|
|
|
|
|
|
2019-04-13 15:35:54 -07:00
|
|
|
|
var activeHotends = new HashSet<int>(new[] { materialIndex });
|
|
|
|
|
|
|
|
|
|
|
|
if (isWipeTower)
|
|
|
|
|
|
{
|
|
|
|
|
|
activeHotends.Add(0);
|
|
|
|
|
|
activeHotends.Add(1);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Validate against active hotends
|
2019-05-02 18:06:28 -07:00
|
|
|
|
foreach (var hotendIndex in activeHotends)
|
2019-04-10 16:50:08 -07:00
|
|
|
|
{
|
2019-04-20 21:03:05 -07:00
|
|
|
|
var hotendBounds = printer.Settings.ToolBounds[hotendIndex];
|
2019-04-13 15:35:54 -07:00
|
|
|
|
if (!hotendBounds.Contains(itemBounds))
|
|
|
|
|
|
{
|
|
|
|
|
|
// Draw in red outside of the bounds for the hotend
|
|
|
|
|
|
drawColor = Color.Red.WithAlpha(90);
|
|
|
|
|
|
}
|
2019-04-10 16:50:08 -07:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2019-04-12 22:17:51 -07:00
|
|
|
|
// Outside of printer build volume
|
2017-11-28 15:34:49 -08:00
|
|
|
|
drawColor = new Color(drawColor, 65);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2019-04-11 15:56:19 -07:00
|
|
|
|
|
2019-05-02 18:06:28 -07:00
|
|
|
|
if (drawColor.alpha != 255
|
2017-12-15 15:22:35 -08:00
|
|
|
|
&& item is Object3D item3D)
|
2017-12-14 12:55:56 -08:00
|
|
|
|
{
|
2017-12-15 15:22:35 -08:00
|
|
|
|
item3D.EnsureTransparentSorting();
|
2017-12-14 12:55:56 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
2019-02-01 14:47:05 -08:00
|
|
|
|
if (selectedItem is ISelectableChildContainer selectableChildContainer)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (item.AncestorsAndSelf().Any(i => selectableChildContainer.SelectedChildren.Contains(i.ID)))
|
|
|
|
|
|
{
|
|
|
|
|
|
drawColor = new Color(drawColor, 200);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!sceneContext.ViewState.ModelView)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (modelRenderStyle == ModelRenderStyle.WireframeAndSolid)
|
|
|
|
|
|
{
|
|
|
|
|
|
drawColor = gCodeMeshColor;
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (modelRenderStyle == ModelRenderStyle.Wireframe)
|
|
|
|
|
|
{
|
|
|
|
|
|
drawColor = new Color(gCodeMeshColor, 1);
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (modelRenderStyle == ModelRenderStyle.None)
|
|
|
|
|
|
{
|
|
|
|
|
|
drawColor = Color.Transparent;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2017-11-28 15:34:49 -08:00
|
|
|
|
return drawColor;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2019-05-02 18:06:28 -07:00
|
|
|
|
public enum EditorType
|
|
|
|
|
|
{
|
|
|
|
|
|
Printer,
|
|
|
|
|
|
Part
|
|
|
|
|
|
}
|
2017-08-17 10:52:47 -07:00
|
|
|
|
|
|
|
|
|
|
public EditorType EditorMode { get; set; } = EditorType.Part;
|
2017-08-16 17:13:16 -07:00
|
|
|
|
|
2018-02-02 18:19:23 -08:00
|
|
|
|
private int BackToFrontXY(Object3DView a, Object3DView b)
|
2017-08-25 12:39:52 -07:00
|
|
|
|
{
|
2018-02-09 12:24:04 -08:00
|
|
|
|
var meshA = a.Object3D.Mesh;
|
|
|
|
|
|
var meshB = b.Object3D.Mesh;
|
|
|
|
|
|
|
|
|
|
|
|
if (meshA == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
return 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (meshB == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
return -1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2019-01-11 16:49:34 -08:00
|
|
|
|
var aCenterWorld = Vector3Ex.Transform(meshA.GetAxisAlignedBoundingBox().Center, a.Object3D.Matrix);
|
2017-10-31 12:51:16 -07:00
|
|
|
|
aCenterWorld.Z = 0; // we only want to look at the distance on xy in world space
|
2019-01-11 16:49:34 -08:00
|
|
|
|
var aCenterInViewSpace = Vector3Ex.Transform(aCenterWorld, World.ModelviewMatrix);
|
2017-08-25 12:39:52 -07:00
|
|
|
|
|
2019-01-11 16:49:34 -08:00
|
|
|
|
var bCenterWorld = Vector3Ex.Transform(meshB.GetAxisAlignedBoundingBox().Center, b.Object3D.Matrix);
|
2017-10-31 12:51:16 -07:00
|
|
|
|
bCenterWorld.Z = 0; // we only want to look at the distance on xy in world space
|
2019-01-11 16:49:34 -08:00
|
|
|
|
var bCenterInViewSpace = Vector3Ex.Transform(bCenterWorld, World.ModelviewMatrix);
|
2017-08-25 12:39:52 -07:00
|
|
|
|
|
|
|
|
|
|
return bCenterInViewSpace.LengthSquared.CompareTo(aCenterInViewSpace.LengthSquared);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2019-02-01 11:59:29 -08:00
|
|
|
|
private void DrawGlContent(DrawEventArgs e)
|
2017-06-21 23:26:20 -07:00
|
|
|
|
{
|
2018-02-02 18:19:23 -08:00
|
|
|
|
var gcodeOptions = sceneContext.RendererOptions;
|
|
|
|
|
|
|
2019-01-19 15:20:48 -08:00
|
|
|
|
if (gcodeOptions.GCodeModelView)
|
2018-02-02 18:19:23 -08:00
|
|
|
|
{
|
2019-01-19 15:20:48 -08:00
|
|
|
|
modelRenderStyle = ModelRenderStyle.WireframeAndSolid;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
modelRenderStyle = ModelRenderStyle.None;
|
2018-02-02 18:19:23 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
2019-02-01 21:51:46 -08:00
|
|
|
|
foreach (var drawable in drawables.Where(d => d.DrawStage == DrawStage.First))
|
|
|
|
|
|
{
|
|
|
|
|
|
if (drawable.Enabled)
|
|
|
|
|
|
{
|
|
|
|
|
|
drawable.Draw(this, e, Matrix4X4.Identity, this.World);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
GLHelper.SetGlContext(this.World, renderSource.TransformToScreenSpace(renderSource.LocalBounds), lighting);
|
|
|
|
|
|
|
|
|
|
|
|
foreach (var drawable in drawables.Where(d => d.DrawStage == DrawStage.OpaqueContent))
|
2019-02-01 12:30:40 -08:00
|
|
|
|
{
|
2019-02-01 12:31:18 -08:00
|
|
|
|
if (drawable.Enabled)
|
|
|
|
|
|
{
|
|
|
|
|
|
drawable.Draw(this, e, Matrix4X4.Identity, this.World);
|
|
|
|
|
|
}
|
2019-02-01 12:30:40 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
2018-02-02 18:19:23 -08:00
|
|
|
|
// Draw solid objects, extract transparent
|
|
|
|
|
|
var transparentMeshes = new List<Object3DView>();
|
2019-02-01 11:58:19 -08:00
|
|
|
|
|
2019-02-13 10:26:02 -08:00
|
|
|
|
var selectedItem = scene.SelectedItem;
|
|
|
|
|
|
bool suppressNormalDraw = false;
|
|
|
|
|
|
if (selectedItem != null)
|
|
|
|
|
|
{
|
2019-02-01 11:58:19 -08:00
|
|
|
|
// Invoke existing IEditorDraw when iterating items
|
2019-02-13 10:26:02 -08:00
|
|
|
|
if (selectedItem is IEditorDraw editorDraw)
|
2019-02-01 11:58:19 -08:00
|
|
|
|
{
|
|
|
|
|
|
// TODO: Putting the drawing code in the IObject3D means almost certain bindings to MatterControl in IObject3D. If instead
|
|
|
|
|
|
// we had a UI layer object that used binding to register scene drawing hooks for specific types, we could avoid the bindings
|
2019-02-13 10:26:02 -08:00
|
|
|
|
editorDraw.DrawEditor(this, transparentMeshes, e, ref suppressNormalDraw);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
foreach (var item in scene.Children)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (item.Visible
|
|
|
|
|
|
&& (item != selectedItem || suppressNormalDraw == false))
|
|
|
|
|
|
{
|
|
|
|
|
|
DrawObject(item, transparentMeshes, e);
|
2019-02-01 11:58:19 -08:00
|
|
|
|
}
|
2017-06-21 23:26:20 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
2019-05-03 07:57:44 -07:00
|
|
|
|
if (sceneContext.Printer?.Connection?.serialPort is PrinterEmulator.Emulator emulator)
|
2019-05-02 18:06:28 -07:00
|
|
|
|
{
|
2019-05-02 22:18:41 -07:00
|
|
|
|
void NozzlePositionChanged(object s, EventArgs e2)
|
|
|
|
|
|
{
|
|
|
|
|
|
// limit max number of updates per second to 10
|
|
|
|
|
|
if (UiThread.CurrentTimerMs > lastEmulatorDrawMs + 100)
|
|
|
|
|
|
{
|
|
|
|
|
|
UiThread.RunOnIdle(Invalidate);
|
|
|
|
|
|
// set it to now
|
|
|
|
|
|
lastEmulatorDrawMs = UiThread.CurrentTimerMs;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2019-05-02 18:06:28 -07:00
|
|
|
|
var matrix = Matrix4X4.CreateTranslation(emulator.CurrentPosition + new Vector3(.5, .5, 5));
|
2019-05-03 07:57:44 -07:00
|
|
|
|
GLHelper.Render(emulatorNozzleMesh,
|
2019-05-02 18:06:28 -07:00
|
|
|
|
MaterialRendering.Color(emulator.ExtruderIndex),
|
|
|
|
|
|
matrix,
|
|
|
|
|
|
RenderTypes.Shaded,
|
|
|
|
|
|
matrix * World.ModelviewMatrix);
|
|
|
|
|
|
|
2019-05-02 22:18:41 -07:00
|
|
|
|
if (!emulatorHooked)
|
2019-05-02 18:06:28 -07:00
|
|
|
|
{
|
2019-05-02 22:18:41 -07:00
|
|
|
|
emulator.DestinationChanged += NozzlePositionChanged;
|
|
|
|
|
|
emulatorHooked = true;
|
2019-05-02 18:06:28 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
2019-05-02 22:18:41 -07:00
|
|
|
|
Closed += (s, e3) => emulator.DestinationChanged -= NozzlePositionChanged;
|
2019-05-02 18:06:28 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
2017-08-25 12:39:52 -07:00
|
|
|
|
transparentMeshes.Sort(BackToFrontXY);
|
|
|
|
|
|
|
2019-01-11 16:49:34 -08:00
|
|
|
|
var bedNormalInViewSpace = Vector3Ex.TransformNormal(Vector3.UnitZ, World.ModelviewMatrix).GetNormal();
|
|
|
|
|
|
var pointOnBedInViewSpace = Vector3Ex.Transform(new Vector3(10, 10, 0), World.ModelviewMatrix);
|
|
|
|
|
|
var lookingDownOnBed = Vector3Ex.Dot(bedNormalInViewSpace, pointOnBedInViewSpace) < 0;
|
2017-08-25 12:39:52 -07:00
|
|
|
|
|
2019-01-31 08:07:19 -08:00
|
|
|
|
floorDrawable.LookingDownOnBed = lookingDownOnBed;
|
|
|
|
|
|
|
2017-08-25 12:39:52 -07:00
|
|
|
|
if (lookingDownOnBed)
|
|
|
|
|
|
{
|
2019-01-31 08:07:19 -08:00
|
|
|
|
floorDrawable.Draw(this, e, Matrix4X4.Identity, this.World);
|
2017-11-28 15:41:41 -08:00
|
|
|
|
}
|
2017-11-28 15:34:49 -08:00
|
|
|
|
|
2018-09-20 15:18:06 -07:00
|
|
|
|
var wireColor = Color.Transparent;
|
2019-05-02 18:06:28 -07:00
|
|
|
|
switch (modelRenderStyle)
|
2018-09-20 15:18:06 -07:00
|
|
|
|
{
|
|
|
|
|
|
case ModelRenderStyle.Wireframe:
|
|
|
|
|
|
wireColor = darkWireframe;
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case ModelRenderStyle.WireframeAndSolid:
|
|
|
|
|
|
wireColor = lightWireframe;
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
2019-01-31 13:45:49 -08:00
|
|
|
|
|
2018-02-02 18:19:23 -08:00
|
|
|
|
// Draw transparent objects
|
|
|
|
|
|
foreach (var item in transparentMeshes)
|
2017-11-28 15:41:41 -08:00
|
|
|
|
{
|
2018-02-02 18:19:23 -08:00
|
|
|
|
var object3D = item.Object3D;
|
2017-11-28 15:41:41 -08:00
|
|
|
|
GLHelper.Render(
|
|
|
|
|
|
object3D.Mesh,
|
2018-02-02 18:19:23 -08:00
|
|
|
|
item.Color,
|
2018-02-04 00:38:50 -08:00
|
|
|
|
object3D.WorldMatrix(),
|
2017-11-28 15:41:41 -08:00
|
|
|
|
RenderTypes.Outlines,
|
2018-02-04 00:38:50 -08:00
|
|
|
|
object3D.WorldMatrix() * World.ModelviewMatrix,
|
2019-05-22 17:36:49 -07:00
|
|
|
|
wireColor,
|
|
|
|
|
|
allowBspRendering: transparentMeshes.Count < 1000);
|
2017-08-25 12:39:52 -07:00
|
|
|
|
}
|
2017-11-28 15:41:41 -08:00
|
|
|
|
|
|
|
|
|
|
if (!lookingDownOnBed)
|
2017-08-25 12:39:52 -07:00
|
|
|
|
{
|
2019-01-31 08:07:19 -08:00
|
|
|
|
floorDrawable.Draw(this, e, Matrix4X4.Identity, this.World);
|
2017-08-25 12:39:52 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
DrawInteractionVolumes(e);
|
2017-10-21 16:09:39 -07:00
|
|
|
|
|
2019-02-01 21:51:46 -08:00
|
|
|
|
foreach (var drawable in drawables.Where(d => d.DrawStage == DrawStage.TransparentContent))
|
|
|
|
|
|
{
|
|
|
|
|
|
if (drawable.Enabled)
|
|
|
|
|
|
{
|
|
|
|
|
|
drawable.Draw(this, e, Matrix4X4.Identity, this.World);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
GLHelper.UnsetGlContext();
|
|
|
|
|
|
|
2019-02-02 13:53:59 -08:00
|
|
|
|
// Invoke DrawStage.Last item drawables
|
|
|
|
|
|
foreach (var item in scene.Children)
|
|
|
|
|
|
{
|
|
|
|
|
|
// HACK: Consider how shared code in DrawObject can be reused to prevent duplicate execution
|
|
|
|
|
|
bool isSelected = selectedItem != null
|
2019-05-22 17:09:01 -07:00
|
|
|
|
&& selectedItem.DescendantsAndSelf().Any((i) => i == item);
|
2019-02-02 13:53:59 -08:00
|
|
|
|
|
|
|
|
|
|
foreach (var itemDrawable in itemDrawables.Where(d => d.DrawStage == DrawStage.Last && d.Enabled))
|
|
|
|
|
|
{
|
|
|
|
|
|
itemDrawable.Draw(this, item, isSelected, e, Matrix4X4.Identity, this.World);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Invoke DrawStage.Last scene drawables
|
2019-02-01 21:51:46 -08:00
|
|
|
|
foreach (var drawable in drawables.Where(d => d.DrawStage == DrawStage.Last))
|
2019-01-31 22:04:39 -08:00
|
|
|
|
{
|
2019-02-01 12:31:18 -08:00
|
|
|
|
if (drawable.Enabled)
|
|
|
|
|
|
{
|
|
|
|
|
|
drawable.Draw(this, e, Matrix4X4.Identity, this.World);
|
|
|
|
|
|
}
|
2019-01-31 22:04:39 -08:00
|
|
|
|
}
|
2017-08-25 12:39:52 -07:00
|
|
|
|
}
|
2017-06-21 23:26:20 -07:00
|
|
|
|
|
2017-08-24 13:58:06 -07:00
|
|
|
|
private void DrawInteractionVolumes(DrawEventArgs e)
|
2017-06-21 23:26:20 -07:00
|
|
|
|
{
|
2019-05-20 10:13:45 -07:00
|
|
|
|
foreach (var item in this.InteractionVolumes.OfType<IGLInteractionElement>())
|
|
|
|
|
|
{
|
|
|
|
|
|
item.Visible = !SuppressUiVolumes;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2019-05-02 18:06:28 -07:00
|
|
|
|
if (SuppressUiVolumes)
|
2017-06-21 23:26:20 -07:00
|
|
|
|
{
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// draw on top of anything that is already drawn
|
2019-05-18 10:02:15 -07:00
|
|
|
|
GL.Disable(EnableCap.DepthTest);
|
|
|
|
|
|
|
2019-05-18 13:17:21 -07:00
|
|
|
|
foreach (var interactionVolume in this.InteractionVolumes.OfType<IGLInteractionElement>())
|
2017-06-21 23:26:20 -07:00
|
|
|
|
{
|
|
|
|
|
|
if (interactionVolume.DrawOnTop)
|
|
|
|
|
|
{
|
2017-08-24 13:58:06 -07:00
|
|
|
|
interactionVolume.DrawGlContent(new DrawGlContentEventArgs(false, e));
|
2017-06-21 23:26:20 -07:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2019-05-18 10:02:15 -07:00
|
|
|
|
// Restore DepthTest
|
|
|
|
|
|
GL.Enable(EnableCap.DepthTest);
|
|
|
|
|
|
|
2017-06-21 23:26:20 -07:00
|
|
|
|
// Draw again setting the depth buffer and ensuring that all the interaction objects are sorted as well as we can
|
2019-05-18 13:17:21 -07:00
|
|
|
|
foreach (var interactionVolume in this.InteractionVolumes.OfType<IGLInteractionElement>())
|
2017-06-21 23:26:20 -07:00
|
|
|
|
{
|
2017-08-24 13:58:06 -07:00
|
|
|
|
interactionVolume.DrawGlContent(new DrawGlContentEventArgs(true, e));
|
2017-06-21 23:26:20 -07:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2018-02-02 18:19:23 -08:00
|
|
|
|
|
|
|
|
|
|
public enum ModelRenderStyle
|
|
|
|
|
|
{
|
|
|
|
|
|
Solid,
|
|
|
|
|
|
Wireframe,
|
|
|
|
|
|
WireframeAndSolid,
|
|
|
|
|
|
None
|
|
|
|
|
|
}
|
2019-02-13 10:26:02 -08:00
|
|
|
|
}
|
2018-02-02 18:19:23 -08:00
|
|
|
|
|
2019-02-13 10:26:02 -08:00
|
|
|
|
public class Object3DView
|
|
|
|
|
|
{
|
|
|
|
|
|
public Color Color { get; set; }
|
2018-02-02 18:19:23 -08:00
|
|
|
|
|
2019-02-13 10:26:02 -08:00
|
|
|
|
public IObject3D Object3D { get; }
|
2018-02-02 18:19:23 -08:00
|
|
|
|
|
2019-02-13 10:26:02 -08:00
|
|
|
|
public Object3DView(IObject3D source, Color color)
|
|
|
|
|
|
{
|
|
|
|
|
|
this.Object3D = source;
|
|
|
|
|
|
this.Color = color;
|
2018-02-02 18:19:23 -08:00
|
|
|
|
|
2019-02-13 10:26:02 -08:00
|
|
|
|
if (source is Object3D object3D
|
|
|
|
|
|
&& color != source.Color
|
|
|
|
|
|
&& color.alpha != 255)
|
|
|
|
|
|
{
|
|
|
|
|
|
object3D.EnsureTransparentSorting();
|
2018-02-02 18:19:23 -08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2017-06-21 23:26:20 -07:00
|
|
|
|
}
|
2017-11-28 15:34:49 -08:00
|
|
|
|
}
|