mattercontrol/MatterControl.OpenGL/GCodeRenderer/GCodeRenderer.cs
2017-10-31 11:43:25 -07:00

411 lines
No EOL
14 KiB
C#

/*
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;
using System.Collections.Generic;
using MatterHackers.Agg;
using MatterHackers.Agg.UI;
using MatterHackers.RenderOpenGl;
using MatterHackers.RenderOpenGl.OpenGl;
namespace MatterHackers.GCodeVisualizer
{
[Flags]
public enum RenderType
{
None = 0,
Extrusions = 1,
Moves = 2,
Retractions = 4,
SpeedColors = 8,
SimulateExtrusion = 16,
HideExtruderOffsets = 32,
TransparentExtrusion = 64,
};
public class GCodeRenderer : IDisposable
{
public static double ExtruderWidth { get; set; } = .4;
private List<List<int>> featureStartIndex = new List<List<int>>();
private List<List<int>> featureEndIndex = new List<List<int>>();
private List<List<RenderFeatureBase>> renderFeatures = new List<List<RenderFeatureBase>>();
public static Color TravelColor = Color.Green;
private GCodeFile gCodeFileToDraw;
public GCodeFile GCodeFileToDraw { get { return gCodeFileToDraw; } }
private ExtrusionColors extrusionColors;
public GCodeRenderer(GCodeFile gCodeFileToDraw)
{
if (gCodeFileToDraw != null)
{
this.gCodeFileToDraw = gCodeFileToDraw;
for (int i = 0; i < gCodeFileToDraw.LayerCount; i++)
{
renderFeatures.Add(new List<RenderFeatureBase>());
}
}
}
public void CreateFeaturesForLayerIfRequired(int layerToCreate)
{
if (extrusionColors == null
&& gCodeFileToDraw != null
&& gCodeFileToDraw.LineCount > 0)
{
extrusionColors = new ExtrusionColors();
HashSet<float> speeds = new HashSet<float>();
PrinterMachineInstruction prevInstruction = gCodeFileToDraw.Instruction(0);
for (int i = 1; i < gCodeFileToDraw.LineCount; i++)
{
PrinterMachineInstruction instruction = gCodeFileToDraw.Instruction(i);
if (instruction.EPosition > prevInstruction.EPosition && (instruction.Line.IndexOf('X') != -1 || instruction.Line.IndexOf('Y') != -1))
{
speeds.Add((float)instruction.FeedRate);
}
prevInstruction = instruction;
}
foreach (float speed in speeds)
{
extrusionColors.GetColorForSpeed(speed);
}
}
if (renderFeatures.Count == 0
|| renderFeatures[layerToCreate].Count > 0)
{
return;
}
List<RenderFeatureBase> renderFeaturesForLayer = renderFeatures[layerToCreate];
int startRenderIndex = gCodeFileToDraw.GetInstructionIndexAtLayer(layerToCreate);
int endRenderIndex = gCodeFileToDraw.LineCount - 1;
if (layerToCreate < gCodeFileToDraw.LayerCount - 1)
{
endRenderIndex = gCodeFileToDraw.GetInstructionIndexAtLayer(layerToCreate + 1);
}
for (int instructionIndex = startRenderIndex; instructionIndex < endRenderIndex; instructionIndex++)
{
PrinterMachineInstruction currentInstruction = gCodeFileToDraw.Instruction(instructionIndex);
PrinterMachineInstruction previousInstruction = currentInstruction;
if (instructionIndex > 0)
{
previousInstruction = gCodeFileToDraw.Instruction(instructionIndex - 1);
}
if (currentInstruction.Position == previousInstruction.Position)
{
if (Math.Abs(currentInstruction.EPosition - previousInstruction.EPosition) > 0)
{
// this is a retraction
renderFeaturesForLayer.Add(new RenderFeatureRetract(currentInstruction.Position, currentInstruction.EPosition - previousInstruction.EPosition, currentInstruction.ExtruderIndex, currentInstruction.FeedRate));
}
if (currentInstruction.Line.StartsWith("G10"))
{
renderFeaturesForLayer.Add(new RenderFeatureRetract(currentInstruction.Position, -1, currentInstruction.ExtruderIndex, currentInstruction.FeedRate));
}
else if (currentInstruction.Line.StartsWith("G11"))
{
renderFeaturesForLayer.Add(new RenderFeatureRetract(currentInstruction.Position, 1, currentInstruction.ExtruderIndex, currentInstruction.FeedRate));
}
}
else
{
if (gCodeFileToDraw.IsExtruding(instructionIndex))
{
double layerThickness = gCodeFileToDraw.GetLayerHeight();
if (layerToCreate == 0)
{
layerThickness = gCodeFileToDraw.GetFirstLayerHeight();
}
Color extrusionColor = extrusionColors.GetColorForSpeed((float)currentInstruction.FeedRate);
renderFeaturesForLayer.Add(new RenderFeatureExtrusion(previousInstruction.Position, currentInstruction.Position, currentInstruction.ExtruderIndex, currentInstruction.FeedRate, currentInstruction.EPosition - previousInstruction.EPosition, gCodeFileToDraw.GetFilamentDiameter(), layerThickness, extrusionColor));
}
else
{
renderFeaturesForLayer.Add(new RenderFeatureTravel(previousInstruction.Position, currentInstruction.Position, currentInstruction.ExtruderIndex, currentInstruction.FeedRate));
}
}
}
}
public int GetNumFeatures(int layerToCountFeaturesOn)
{
CreateFeaturesForLayerIfRequired(layerToCountFeaturesOn);
return renderFeatures[layerToCountFeaturesOn].Count;
}
public void Render(Graphics2D graphics2D, GCodeRenderInfo renderInfo)
{
if (renderFeatures.Count > 0)
{
CreateFeaturesForLayerIfRequired(renderInfo.EndLayerIndex);
int featuresOnLayer = renderFeatures[renderInfo.EndLayerIndex].Count;
int endFeature = (int)(featuresOnLayer * renderInfo.FeatureToEndOnRatio0To1 + .5);
endFeature = Math.Max(0, Math.Min(endFeature, featuresOnLayer));
int startFeature = (int)(featuresOnLayer * renderInfo.FeatureToStartOnRatio0To1 + .5);
startFeature = Math.Max(0, Math.Min(startFeature, featuresOnLayer));
// try to make sure we always draw at least one feature
if (endFeature <= startFeature)
{
endFeature = Math.Min(startFeature + 1, featuresOnLayer);
}
if (startFeature >= endFeature)
{
// This can only happen if the start and end are set to the last feature
// Try to set the start feature to one from the end
startFeature = Math.Max(endFeature - 1, 0);
}
Graphics2DOpenGL graphics2DGl = graphics2D as Graphics2DOpenGL;
if (graphics2DGl != null)
{
graphics2DGl.PreRender();
GL.Begin(BeginMode.Triangles);
for (int i = startFeature; i < endFeature; i++)
{
RenderFeatureBase feature = renderFeatures[renderInfo.EndLayerIndex][i];
if (feature != null)
{
feature.Render(graphics2DGl, renderInfo);
}
}
GL.End();
graphics2DGl.PopOrthoProjection();
}
else
{
for (int i = startFeature; i < endFeature; i++)
{
RenderFeatureBase feature = renderFeatures[renderInfo.EndLayerIndex][i];
if (feature != null)
{
feature.Render(graphics2D, renderInfo);
}
}
}
}
}
private void Create3DDataForLayer(int layerIndex,
VectorPOD<ColorVertexData> colorVertexData,
VectorPOD<int> vertexIndexArray,
GCodeRenderInfo renderInfo)
{
colorVertexData.Clear();
vertexIndexArray.Clear();
featureStartIndex[layerIndex].Clear();
featureEndIndex[layerIndex].Clear();
for (int i = 0; i < renderFeatures[layerIndex].Count; i++)
{
featureStartIndex[layerIndex].Add(vertexIndexArray.Count);
RenderFeatureBase feature = renderFeatures[layerIndex][i];
if (feature != null)
{
feature.CreateRender3DData(colorVertexData, vertexIndexArray, renderInfo);
}
featureEndIndex[layerIndex].Add(vertexIndexArray.Count);
}
}
public void Dispose()
{
Clear3DGCode();
}
public void Clear3DGCode()
{
if (layerVertexBuffer != null)
{
for (int i = 0; i < layerVertexBuffer.Count; i++)
{
if (layerVertexBuffer[i] != null)
{
layerVertexBuffer[i].Dispose();
layerVertexBuffer[i] = null;
}
}
}
}
private List<GCodeVertexBuffer> layerVertexBuffer;
private RenderType lastRenderType = RenderType.None;
private static readonly bool Is32Bit = IntPtr.Size == 4;
public void Render3D(GCodeRenderInfo renderInfo, DrawEventArgs e)
{
if (renderInfo == null)
{
return;
}
if (layerVertexBuffer == null)
{
layerVertexBuffer = new List<GCodeVertexBuffer>();
layerVertexBuffer.Capacity = gCodeFileToDraw.LayerCount;
for (int layerIndex = 0; layerIndex < gCodeFileToDraw.LayerCount; layerIndex++)
{
layerVertexBuffer.Add(null);
featureStartIndex.Add(new List<int>());
featureEndIndex.Add(new List<int>());
}
}
for (int layerIndex = 0; layerIndex < gCodeFileToDraw.LayerCount; layerIndex++)
{
CreateFeaturesForLayerIfRequired(layerIndex);
}
if (lastRenderType != renderInfo.CurrentRenderType)
{
Clear3DGCode();
lastRenderType = renderInfo.CurrentRenderType;
}
if (renderFeatures.Count > 0)
{
if (Is32Bit && !GL.GlHasBufferObjects)
{
int maxFeaturesForThisSystem = 125000;
int totalFeaturesToRender = 0;
bool cleanUnusedLayers = false;
// if on 32 bit system make sure we don't run out of memory rendering too many features
for (int i = renderInfo.EndLayerIndex - 1; i >= renderInfo.StartLayerIndex; i--)
{
if (totalFeaturesToRender + renderFeatures[i].Count < maxFeaturesForThisSystem)
{
totalFeaturesToRender += renderFeatures[i].Count;
}
else // don't render any of the layers below this and in fact remove them from memory if possible
{
renderInfo.StartLayerIndex = i + 1;
cleanUnusedLayers = true;
break;
}
}
if (cleanUnusedLayers)
{
// no remove any layers that are set that we are not going to render
for (int removeIndex = 0; removeIndex < layerVertexBuffer.Count; removeIndex++)
{
if (removeIndex < renderInfo.StartLayerIndex || removeIndex >= renderInfo.EndLayerIndex)
{
if (layerVertexBuffer[removeIndex] != null)
{
layerVertexBuffer[removeIndex].Dispose();
layerVertexBuffer[removeIndex] = null;
}
}
}
}
}
for (int i = renderInfo.EndLayerIndex - 1; i >= renderInfo.StartLayerIndex; i--)
{
// If its the first render or we change what we are trying to render then create vertex data.
if (layerVertexBuffer[i] == null)
{
VectorPOD<ColorVertexData> colorVertexData = new VectorPOD<ColorVertexData>();
VectorPOD<int> vertexIndexArray = new VectorPOD<int>();
Create3DDataForLayer(i, colorVertexData, vertexIndexArray, renderInfo);
layerVertexBuffer[i] = new GCodeVertexBuffer();
layerVertexBuffer[i].SetVertexData(colorVertexData.Array);
layerVertexBuffer[i].SetIndexData(vertexIndexArray.Array);
}
}
GL.Disable(EnableCap.Texture2D);
GL.PushAttrib(AttribMask.EnableBit);
GL.DisableClientState(ArrayCap.TextureCoordArray);
GL.Enable(EnableCap.PolygonSmooth);
if (renderInfo.EndLayerIndex - 1 > renderInfo.StartLayerIndex)
{
for (int i = renderInfo.StartLayerIndex; i < renderInfo.EndLayerIndex - 1; i++)
{
int featuresOnLayer = renderFeatures[i].Count;
if (featuresOnLayer > 1)
{
layerVertexBuffer[i].renderRange(0, featureEndIndex[i][featuresOnLayer - 1]);
}
}
}
// draw the partial layer of end-1 from startRatio to endRatio
{
int layerIndex = renderInfo.EndLayerIndex - 1;
int featuresOnLayer = renderFeatures[layerIndex].Count;
int startFeature = (int)(featuresOnLayer * renderInfo.FeatureToStartOnRatio0To1 + .5);
startFeature = Math.Max(0, Math.Min(startFeature, featuresOnLayer));
int endFeature = (int)(featuresOnLayer * renderInfo.FeatureToEndOnRatio0To1 + .5);
endFeature = Math.Max(0, Math.Min(endFeature, featuresOnLayer));
// try to make sure we always draw at least one feature
if (endFeature <= startFeature)
{
endFeature = Math.Min(startFeature + 1, featuresOnLayer);
}
if (startFeature >= endFeature)
{
// This can only happen if the start and end are set to the last feature
// Try to set the start feature to one from the end
startFeature = Math.Max(endFeature - 1, 0);
}
if (endFeature > startFeature)
{
int ellementCount = featureEndIndex[layerIndex][endFeature - 1] - featureStartIndex[layerIndex][startFeature];
layerVertexBuffer[layerIndex].renderRange(featureStartIndex[layerIndex][startFeature], ellementCount);
}
}
GL.PopAttrib();
}
}
}
}