From 778107939c455ddd3a13ffc3065b3c133780a1c6 Mon Sep 17 00:00:00 2001 From: Shai Seger Date: Wed, 21 Aug 2024 23:18:52 +0300 Subject: [PATCH] Cam simulator feature update (#15597) * remove redundant code * Improve lighting, add ambient occlusion * Add cleanup code. Dialog is now deleted when cloesd. * change back to ambient occlusion * Fix G8x drill sequence bug. issue #14369 * fix bad simulation artifacts under Linux and QT. Issue #14369 * fix merge issue * fix border artifact on buttons * support showing path lines. revise the gui. * add option for arbitrary solids. wip * use vectors instead of mallocs * Handle arbitrary stock shapes + show base shape. * Complete the base shape display feature. eliminate co-planar artifacts. * support window scaling. upstream issue #14334 * Apply lint fixes * some missing lints. * Attend pylint issues * Apply code fixes based on @kadet1090 review * fix some clang-tidy warnings. * CAM: Linter cleanup round 1 --------- Co-authored-by: Chris Hennes --- src/Mod/CAM/Gui/Resources/Path.qrc | 3 + .../gl_simulator/AmbientOclusion.png | Bin 0 -> 537 bytes .../CAM/Gui/Resources/gl_simulator/Path.png | Bin 0 -> 1469 bytes .../CAM/Gui/Resources/gl_simulator/View.png | Bin 0 -> 1171 bytes .../Gui/Resources/panels/TaskCAMSimulator.ui | 19 - src/Mod/CAM/Path/Main/Gui/SimulatorGL.py | 112 +- src/Mod/CAM/PathSimulator/AppGL/CAMSim.cpp | 41 +- src/Mod/CAM/PathSimulator/AppGL/CAMSim.h | 19 +- src/Mod/CAM/PathSimulator/AppGL/CAMSimPy.xml | 13 +- .../CAM/PathSimulator/AppGL/CAMSimPyImp.cpp | 26 +- .../CAM/PathSimulator/AppGL/CMakeLists.txt | 6 + .../PathSimulator/AppGL/DlgCAMSimulator.cpp | 119 +- .../CAM/PathSimulator/AppGL/DlgCAMSimulator.h | 20 +- src/Mod/CAM/PathSimulator/AppGL/EndMill.cpp | 30 +- src/Mod/CAM/PathSimulator/AppGL/EndMill.h | 5 +- .../CAM/PathSimulator/AppGL/GCodeParser.cpp | 5 +- src/Mod/CAM/PathSimulator/AppGL/GCodeParser.h | 4 +- src/Mod/CAM/PathSimulator/AppGL/GlUtils.cpp | 2 + src/Mod/CAM/PathSimulator/AppGL/GlUtils.h | 18 +- .../CAM/PathSimulator/AppGL/GuiDisplay.cpp | 175 ++- src/Mod/CAM/PathSimulator/AppGL/GuiDisplay.h | 72 +- .../CAM/PathSimulator/AppGL/MillPathLine.cpp | 60 + .../CAM/PathSimulator/AppGL/MillPathLine.h | 34 + .../PathSimulator/AppGL/MillPathSegment.cpp | 31 +- .../CAM/PathSimulator/AppGL/MillPathSegment.h | 5 +- .../PathSimulator/AppGL/MillSimulation.cpp | 1069 +++++++++-------- .../CAM/PathSimulator/AppGL/MillSimulation.h | 66 +- .../CAM/PathSimulator/AppGL/OpenGlWrapper.h | 23 +- src/Mod/CAM/PathSimulator/AppGL/Shader.cpp | 591 +++++++-- src/Mod/CAM/PathSimulator/AppGL/Shader.h | 40 +- .../CAM/PathSimulator/AppGL/SimDisplay.cpp | 556 +++++++++ src/Mod/CAM/PathSimulator/AppGL/SimDisplay.h | 138 +++ src/Mod/CAM/PathSimulator/AppGL/SimShapes.cpp | 289 ++--- src/Mod/CAM/PathSimulator/AppGL/SimShapes.h | 21 +- .../CAM/PathSimulator/AppGL/SolidObject.cpp | 79 ++ src/Mod/CAM/PathSimulator/AppGL/SolidObject.h | 53 + .../CAM/PathSimulator/AppGL/StockObject.cpp | 26 +- src/Mod/CAM/PathSimulator/AppGL/StockObject.h | 15 +- src/Mod/CAM/PathSimulator/AppGL/Texture.cpp | 10 +- src/Mod/CAM/PathSimulator/AppGL/Texture.h | 3 +- .../CAM/PathSimulator/AppGL/TextureLoader.cpp | 5 +- src/Mod/CAM/PathSimulator/AppGL/linmath.h | 8 +- 42 files changed, 2744 insertions(+), 1067 deletions(-) create mode 100644 src/Mod/CAM/Gui/Resources/gl_simulator/AmbientOclusion.png create mode 100644 src/Mod/CAM/Gui/Resources/gl_simulator/Path.png create mode 100644 src/Mod/CAM/Gui/Resources/gl_simulator/View.png create mode 100644 src/Mod/CAM/PathSimulator/AppGL/MillPathLine.cpp create mode 100644 src/Mod/CAM/PathSimulator/AppGL/MillPathLine.h create mode 100644 src/Mod/CAM/PathSimulator/AppGL/SimDisplay.cpp create mode 100644 src/Mod/CAM/PathSimulator/AppGL/SimDisplay.h create mode 100644 src/Mod/CAM/PathSimulator/AppGL/SolidObject.cpp create mode 100644 src/Mod/CAM/PathSimulator/AppGL/SolidObject.h diff --git a/src/Mod/CAM/Gui/Resources/Path.qrc b/src/Mod/CAM/Gui/Resources/Path.qrc index 18c1ed1234..07cf8cedf0 100644 --- a/src/Mod/CAM/Gui/Resources/Path.qrc +++ b/src/Mod/CAM/Gui/Resources/Path.qrc @@ -135,6 +135,9 @@ gl_simulator/SingleStep.png gl_simulator/Slider.png gl_simulator/Thumb.png + gl_simulator/Path.png + gl_simulator/View.png + gl_simulator/AmbientOclusion.png preferences/Advanced.ui preferences/PathDressupHoldingTags.ui preferences/PathJob.ui diff --git a/src/Mod/CAM/Gui/Resources/gl_simulator/AmbientOclusion.png b/src/Mod/CAM/Gui/Resources/gl_simulator/AmbientOclusion.png new file mode 100644 index 0000000000000000000000000000000000000000..8a64fa6a1e89e2cfc7e1f23e52bc4a68154b12ff GIT binary patch literal 537 zcmV+!0_OdRP)0{rv6e1KQI(tr*vS#GoYu_ zDG6VI77$AYKvl`I%r-T{gNUf(q~QM|-v}M2bc>7q_WQeL2Gjt?n2u1ErE69L^G@ZD z;*qdPUij%3wfOr2~fOnu! bd*y!vv^*%+ycCec00000NkvXXu0mjf&3o-{ literal 0 HcmV?d00001 diff --git a/src/Mod/CAM/Gui/Resources/gl_simulator/Path.png b/src/Mod/CAM/Gui/Resources/gl_simulator/Path.png new file mode 100644 index 0000000000000000000000000000000000000000..20f8eb67786b61372081618fa4d42414862c8dd0 GIT binary patch literal 1469 zcmV;u1w#6XP) z@H`L#w0G}bSD@C`R$ab)+0H~y?eqCGXU-fG^j%;9;0LsM^JaB+c546r{bs!h;0U1Y z+qc^weSLi@DJdCHr!8By3`pnv`SZ>C4PY_w#K@x2s3a*62sj&zMnH{?jaIg4( z0bjf+R9#(dWj#GT%FD|$+-=|u!*1NTQGI=VmJNkMh6@3F4QT1or8x3^b|7cVy4J)juq2DE-E~r;c!?51qFt? z50n6Z16sFkog$IQfI7Q`@vfQ%R08LLFM+pBBbAnx+KrJ&M1_TgW^*U78F&n+u&^+p zETK^D!~YubTV~g;U9LtdE-p43yMQ<2QmtOSTH$cmvgPIF%FoYNS6A25h4GdS0NS=~ zn+*_+MzwY8R%g230v-Wcv0{ZwR#sMKOH)@@H@FaZ#o1-1r6p;{jvZ!00N4gR0JMDh za)m-6%N;myKpv0B3eTQBJ7^7GGKJr;VS}s7cJAD1;2`i$yiv4d$r9H^aOlt>Ba524 zCKLiWrWR}0u5}fD&z?O7>;-lK9e@@sTI7;dR8%-+rNAvf_4V}$g+Bl)C@65%!t3=K zC=Be5pEu>_=PMWtTCTFP(kXi%Xa+QU_H6s3g2A9OOR+Qf@ZrNmg^vL50e1oA<>jfT zr^j+tRaHt#N;2#R#O_MXnl(!g9z3wz(W6HVa|KgiGg?|&%rEW(J_P;(v~b}<1p)!f zRaaL#W&42N0nMB_Qym>0maD6)Q)+5zG|rU_5Z2b#njlw!KLIURut0vl-!g{}AJ(v8 z!wlyIegiaP#thxNch7S5_4OJxYLwy10ngKf0+gGZE1%EjO3(1&!|e+fxCm(a^y#{D z=Z@trT)3doqeq)3`wB=>>_iMMoHuWtOLpYQk^Zt@0p;Z6==SZ~mTPEe(3mk}3|D1j z358d#Tv1X|lIG5xtM2Y@`}bqVj%mb*5hlndz&Su!Sy{Su>z3s%UAm;PW5*iq8>cLx zP?Fl(+FVEE`0?XT*{8rSfTm5Grna^=%QZGODlIL|a5cax;3H!9?wCP^&T-GBYz> z88~Y=k347#fuj; zY0@MUvld8^<5S;5?2W$)$d>z{4kLb7{?BP)$0HVzC(EaG0s7Y5MyI z=AGGozyF>T2of>6U18DXlR#7m#Den#F3h8i;%Y+?N&t zN~t{HGEnMpIB08omm^1>LctX6B1&h+&6w6wGk3WWhe zz+)m3PKf|;FK`_Yuh%OR6XT{8o;&w}IGxUfHT)q1B2AgnrUP?>%7&ezdM}B<*rvND` zDwMgofNAhxFd%k&w&Cdc1X~;MJ|Gnp6{fbcvp*!F_EJ&;zgZ@U z9Q2tqZWizhAd{2kTi4m?GwhcDhlpTVYlTL@QdjplWo3I!&WbLYrWw(A2i8>vPG-$Pi!^ zIBeF0HuSERef!EJ9G*9IbpHHDqLea{m;=6KZSS+QvrYGZc=#)^+wF!;tLZUZOCkrj z0Z2(niA+sRm?m-Ql3$973X^!Cw)RmOAOCKHuHS!A91e$}_pyQ6W=!Y?dVoDSIXSep zp5*A!r#3KQJRWCu_8QS>go1)X3JVL8)>l?mXm9V}#EFxvuC4-n3Op?$D@lqe?gIN- z5(3D912y9JUzA8h+wvaJv_AnZ911|y@ z|M)$S0+9-!4tNCEjo!4NzlcJ>B+xG+6KOex^b^=Fv23lfy^;Z-lzI+$0q_87Q;UBu zf=x{lHsf;!;8pGn2iyW`0Ia|( l=Path Simulator - - - - - 0 - 0 - - - - - 16777215 - 5 - - - - false - - - diff --git a/src/Mod/CAM/Path/Main/Gui/SimulatorGL.py b/src/Mod/CAM/Path/Main/Gui/SimulatorGL.py index e711858e9c..5b8cf7dbb3 100644 --- a/src/Mod/CAM/Path/Main/Gui/SimulatorGL.py +++ b/src/Mod/CAM/Path/Main/Gui/SimulatorGL.py @@ -19,20 +19,22 @@ # * USA * # * * # *************************************************************************** +""" +Command and task window handler for the OpenGL based CAM simulator +""" + -import FreeCAD -import Path -import Path.Base.Util as PathUtil -import Path.Dressup.Utils as PathDressup -import PathScripts.PathUtils as PathUtils -import Path.Main.Job as PathJob -import PathGui -import CAMSimulator import math import os +import FreeCAD +import Path.Base.Util as PathUtil +import Path.Dressup.Utils as PathDressup +import Path.Main.Job as PathJob +from PathScripts import PathUtils +import CAMSimulator + +from FreeCAD import Vector -from FreeCAD import Vector, Base -from PySide.QtGui import QDialogButtonBox # lazily loaded modules from lazy_loader.lazy_loader import LazyLoader @@ -43,37 +45,52 @@ Part = LazyLoader("Part", globals(), "Part") if FreeCAD.GuiUp: import FreeCADGui from PySide import QtGui, QtCore + from PySide.QtGui import QDialogButtonBox _filePath = os.path.dirname(os.path.abspath(__file__)) def IsSame(x, y): + """ Check if two floats are the same within an epsilon + """ return abs(x - y) < 0.0001 def RadiusAt(edge, p): + """ Find the tool radius within a point on its circumference + """ x = edge.valueAt(p).x y = edge.valueAt(p).y return math.sqrt(x * x + y * y) class CAMSimTaskUi: + """ Handles the simulator task panel + """ def __init__(self, parent): # this will create a Qt widget from our ui file self.form = FreeCADGui.PySideUic.loadUi(":/panels/TaskCAMSimulator.ui") self.parent = parent - def getStandardButtons(self, *args): + def getStandardButtons(self, *_args): + """ Task panel needs only Close button + """ return QDialogButtonBox.Close def reject(self): + """ User Pressed the Close button + """ self.parent.cancel() FreeCADGui.Control.closeDialog() def TSError(msg): + """ Display error message + """ QtGui.QMessageBox.information(None, "Path Simulation", msg) class CAMSimulation: + """ Handles and prepares CAM jobs for simulation + """ def __init__(self): self.debug = False self.stdrot = FreeCAD.Rotation(Vector(0, 0, 1), 0) @@ -83,12 +100,26 @@ class CAMSimulation: self.quality = 10 self.resetSimulation = False self.jobs = [] + self.initdone = False + self.taskForm = None + self.disableAnim = False + self.firstDrill = True + self.millSim = None + self.job = None + self.activeOps = [] + self.ioperation = 0 + self.stock = None + self.busy = False + self.operations = [] + self.baseShape = None def Connect(self, but, sig): + """ Connect task panel buttons """ QtCore.QObject.connect(but, QtCore.SIGNAL("clicked()"), sig) - ## Convert tool shape to tool profile needed by GL simulator def FindClosestEdge(self, edges, px, pz): + """ Convert tool shape to tool profile needed by GL simulator + """ for edge in edges: p1 = edge.FirstParameter p2 = edge.LastParameter @@ -109,6 +140,8 @@ class CAMSimulation: return None, 0.0, 0.0 def FindTopMostEdge(self, edges): + """ Examine tool solid edges and find the top most one + """ maxz = -99999999.0 topedge = None top_p1 = 0.0 @@ -130,16 +163,17 @@ class CAMSimulation: maxz = z return topedge, top_p1, top_p2 - #the algo is based on locating the side edge that OCC creates on any revolved object def GetToolProfile(self, tool, resolution): + """ Get the edge profile of a tool solid. Basically locating the + side edge that OCC creates on any revolved object + """ shape = tool.Shape sideEdgeList = [] - for i in range(len(shape.Edges)): - edge = shape.Edges[i] + for _i, edge in enumerate(shape.Edges): if not edge.isClosed(): - v1 = edge.firstVertex() - v2 = edge.lastVertex() - tp = "arc" if type(edge.Curve) is Part.Circle else "line" + # v1 = edge.firstVertex() + # v2 = edge.lastVertex() + # tp = "arc" if type(edge.Curve) is Part.Circle else "line" sideEdgeList.append(edge) # sort edges as a single 3d line on the x-z plane @@ -151,12 +185,11 @@ class CAMSimulation: # one by one find all connecting edges while edge is not None: sideEdgeList.remove(edge) - if type(edge.Curve) is Part.Circle: + if isinstance(edge.Curve, Part.Circle): # if edge is curved, approximate it with lines based on resolution nsegments = int(edge.Length / resolution) + 1 step = (p2 - p1) / nsegments location = p1 + step - print (edge.Length, nsegments, step) while nsegments > 0: endrad = RadiusAt(edge, location) endz = edge.valueAt(location).z @@ -169,7 +202,7 @@ class CAMSimulation: endz = edge.valueAt(p2).z profile.append(endrad) profile.append(endz) - edge, p1, p2 = self.FindClosestEdge(sideEdgeList, endrad, endz) + edge, p1, p2 = self.FindClosestEdge(sideEdgeList, endrad, endz) if edge is None: break startrad = RadiusAt(edge, p1) @@ -181,6 +214,8 @@ class CAMSimulation: return profile def Activate(self): + """ Invoke the simulator task panel + """ self.initdone = False self.taskForm = CAMSimTaskUi(self) form = self.taskForm.form @@ -200,7 +235,8 @@ class CAMSimulation: # self.SetupSimulation() def _populateJobSelection(self, form): - # Make Job selection combobox + """ Make Job selection combobox + """ setJobIdx = 0 jobName = "" jIdx = 0 @@ -236,6 +272,8 @@ class CAMSimulation: form.comboJobs.setCurrentIndex(0) def SetupSimulation(self): + """ Prepare all selected job operations for simulation + """ form = self.taskForm.form self.activeOps = [] self.numCommands = 0 @@ -250,6 +288,8 @@ class CAMSimulation: self.busy = False def onJobChange(self): + """ When a new job is selected from the drop-down, update job operation list + """ form = self.taskForm.form j = self.jobs[form.comboJobs.currentIndex()] self.job = j @@ -262,18 +302,26 @@ class CAMSimulation: listItem.setCheckState(QtCore.Qt.CheckState.Checked) self.operations.append(op) form.listOperations.addItem(listItem) + if len(j.Model.OutList) > 0: + self.baseShape = j.Model.OutList[0].Shape + else: + self.baseShape = None def onAccuracyBarChange(self): + """ Update simulation quality + """ form = self.taskForm.form self.quality = form.sliderAccuracy.value() qualText = QtCore.QT_TRANSLATE_NOOP("CAM_Simulator", "High") - if (self.quality < 4): + if self.quality < 4: qualText = QtCore.QT_TRANSLATE_NOOP("CAM_Simulator", "Low") - elif (self.quality < 9): + elif self.quality < 9: qualText = QtCore.QT_TRANSLATE_NOOP("CAM_Simulator", "Medium") form.labelAccuracy.setText(qualText) def onOperationItemChange(self, _item): + """ Check if at least one operation is selected to enable the Play button + """ playvalid = False form = self.taskForm.form for i in range(form.listOperations.count()): @@ -283,6 +331,8 @@ class CAMSimulation: form.toolButtonPlay.setEnabled(playvalid) def SimPlay(self): + """ Activate the simulation + """ self.SetupSimulation() self.millSim.ResetSimulation() for op in self.activeOps: @@ -294,14 +344,20 @@ class CAMSimulation: for cmd in opCommands: self.millSim.AddCommand(cmd) self.millSim.BeginSimulation(self.stock, self.quality) + if self.baseShape is not None: + self.millSim.SetBaseShape(self.baseShape, 1) def cancel(self): - #self.EndSimulation() - pass + """ Cancel the simulation + """ class CommandCAMSimulate: + """ FreeCAD invoke simulation task panel command + """ def GetResources(self): + """ Command info + """ return { "Pixmap": "CAM_SimulatorGL", "MenuText": QtCore.QT_TRANSLATE_NOOP("CAM_Simulator", "New CAM Simulator"), @@ -312,6 +368,8 @@ class CommandCAMSimulate: } def IsActive(self): + """ Command is active if at least one CAM job exists + """ if FreeCAD.ActiveDocument is not None: for o in FreeCAD.ActiveDocument.Objects: if o.Name[:3] == "Job": @@ -319,6 +377,8 @@ class CommandCAMSimulate: return False def Activated(self): + """ Activate the simulation + """ CamSimulation = CAMSimulation() CamSimulation.Activate() diff --git a/src/Mod/CAM/PathSimulator/AppGL/CAMSim.cpp b/src/Mod/CAM/PathSimulator/AppGL/CAMSim.cpp index 24f1ff2f83..e5665c735d 100644 --- a/src/Mod/CAM/PathSimulator/AppGL/CAMSim.cpp +++ b/src/Mod/CAM/PathSimulator/AppGL/CAMSim.cpp @@ -20,10 +20,16 @@ * * ***************************************************************************/ -#include "PreCompiled.h" +#include "PreCompiled.h" // NOLINT +#ifndef _PreComp_ +#include +#include +#endif + #include "CAMSim.h" #include "DlgCAMSimulator.h" -#include +#include + using namespace Base; @@ -31,25 +37,9 @@ using namespace CAMSimulator; TYPESYSTEM_SOURCE(CAMSimulator::CAMSim, Base::BaseClass); -#define MAX_GCODE_LINE_LEN 120 - -CAMSim::CAMSim() -{} - -CAMSim::~CAMSim() -{} - -void CAMSim::BeginSimulation(Part::TopoShape* stock, float quality) +void CAMSim::BeginSimulation(const Part::TopoShape& stock, float quality) { - Base::BoundBox3d bbox = stock->getBoundBox(); - SimStock stk = {(float)bbox.MinX, - (float)bbox.MinY, - (float)bbox.MinZ, - (float)bbox.LengthX(), - (float)bbox.LengthY(), - (float)bbox.LengthZ(), - quality}; - DlgCAMSimulator::GetInstance()->startSimulation(&stk, quality); + DlgCAMSimulator::GetInstance()->startSimulation(stock, quality); } void CAMSimulator::CAMSim::resetSimulation() @@ -57,7 +47,7 @@ void CAMSimulator::CAMSim::resetSimulation() DlgCAMSimulator::GetInstance()->resetSimulation(); } -void CAMSim::addTool(const std::vector toolProfilePoints, +void CAMSim::addTool(const std::vector &toolProfilePoints, int toolNumber, float diameter, float resolution) @@ -65,6 +55,15 @@ void CAMSim::addTool(const std::vector toolProfilePoints, DlgCAMSimulator::GetInstance()->addTool(toolProfilePoints, toolNumber, diameter, resolution); } +void CAMSimulator::CAMSim::SetBaseShape(const Part::TopoShape& baseShape, float resolution) +{ + if (baseShape.isNull()) { + return; + } + + DlgCAMSimulator::GetInstance()->SetBaseShape(baseShape, resolution); +} + void CAMSim::AddCommand(Command* cmd) { std::string gline = cmd->toGCode(); diff --git a/src/Mod/CAM/PathSimulator/AppGL/CAMSim.h b/src/Mod/CAM/PathSimulator/AppGL/CAMSim.h index 9fd1d67ab3..2fb85b655d 100644 --- a/src/Mod/CAM/PathSimulator/AppGL/CAMSim.h +++ b/src/Mod/CAM/PathSimulator/AppGL/CAMSim.h @@ -27,10 +27,9 @@ #include #include -#include #include #include -#include +#include #include "DlgCAMSimulator.h" @@ -46,25 +45,25 @@ class CAMSimulatorExport CAMSim: public Base::BaseClass // TYPESYSTEM_HEADER(); public: - static Base::Type getClassTypeId(void); - virtual Base::Type getTypeId(void) const; - static void init(void); - static void* create(void); + static Base::Type getClassTypeId(); + Base::Type getTypeId() const override; + static void init(); + static void* create(); private: static Base::Type classTypeId; public: - CAMSim(); - ~CAMSim(); + CAMSim() = default; - void BeginSimulation(Part::TopoShape* stock, float resolution); + void BeginSimulation(const Part::TopoShape& stock, float resolution); void resetSimulation(); - void addTool(const std::vector toolProfilePoints, + void addTool(const std::vector &toolProfilePoints, int toolNumber, float diameter, float resolution); + void SetBaseShape(const Part::TopoShape& baseShape, float resolution); void AddCommand(Command* cmd); public: diff --git a/src/Mod/CAM/PathSimulator/AppGL/CAMSimPy.xml b/src/Mod/CAM/PathSimulator/AppGL/CAMSimPy.xml index 85f3e3f382..fe3117b3a7 100644 --- a/src/Mod/CAM/PathSimulator/AppGL/CAMSimPy.xml +++ b/src/Mod/CAM/PathSimulator/AppGL/CAMSimPy.xml @@ -35,19 +35,28 @@ ResetSimulation(): - Clear the simulation and all gcode commands + Clear the simulation and all gcode commands - SetToolShape(shape, toolnumber, diameter, resolution): + AddTool(shape, toolnumber, diameter, resolution): Set the shape of the tool to be used for simulation + + + + SetBaseShape(shape, resolution): + + Set the shape of the base object of the job + + + diff --git a/src/Mod/CAM/PathSimulator/AppGL/CAMSimPyImp.cpp b/src/Mod/CAM/PathSimulator/AppGL/CAMSimPyImp.cpp index 5ce3560537..5932b7aaed 100644 --- a/src/Mod/CAM/PathSimulator/AppGL/CAMSimPyImp.cpp +++ b/src/Mod/CAM/PathSimulator/AppGL/CAMSimPyImp.cpp @@ -73,7 +73,7 @@ PyObject* CAMSimPy::BeginSimulation(PyObject* args, PyObject* kwds) return nullptr; } CAMSim* sim = getCAMSimPtr(); - Part::TopoShape* stock = static_cast(pObjStock)->getTopoShapePtr(); + const Part::TopoShape& stock = *static_cast(pObjStock)->getTopoShapePtr(); sim->BeginSimulation(stock, resolution); Py_IncRef(Py_None); return Py_None; @@ -102,6 +102,28 @@ PyObject* CAMSimPy::AddTool(PyObject* args, PyObject* kwds) CAMSim* sim = getCAMSimPtr(); sim->addTool(toolProfile, toolNumber, diameter, resolution); + Py_INCREF(Py_None); + return Py_None; +} + +PyObject* CAMSimPy::SetBaseShape(PyObject* args, PyObject* kwds) +{ + static const std::array kwlist {"shape", "resolution", nullptr}; + PyObject* pObjBaseShape; + float resolution; + if (!Base::Wrapped_ParseTupleAndKeywords(args, kwds,"O!f", + kwlist, &(Part::TopoShapePy::Type), &pObjBaseShape, &resolution)) { + return nullptr; + } + if (!PyArg_ParseTuple(args, "O!f", &(Part::TopoShapePy::Type), &pObjBaseShape, &resolution)) { + return nullptr; + } + CAMSim* sim = getCAMSimPtr(); + const Part::TopoShape& baseShape = + static_cast(pObjBaseShape)->getTopoShapePtr()->getShape(); + sim->SetBaseShape(baseShape, resolution); + + Py_IncRef(Py_None); return Py_None; } @@ -114,6 +136,8 @@ PyObject* CAMSimPy::AddCommand(PyObject* args) CAMSim* sim = getCAMSimPtr(); Path::Command* cmd = static_cast(pObjCmd)->getCommandPtr(); sim->AddCommand(cmd); + + Py_INCREF(Py_None); return Py_None; } diff --git a/src/Mod/CAM/PathSimulator/AppGL/CMakeLists.txt b/src/Mod/CAM/PathSimulator/AppGL/CMakeLists.txt index a397dcd666..d2d5e3d99a 100644 --- a/src/Mod/CAM/PathSimulator/AppGL/CMakeLists.txt +++ b/src/Mod/CAM/PathSimulator/AppGL/CMakeLists.txt @@ -53,6 +53,8 @@ SET(CAMSimulator_SRCS_Core GuiDisplay.h linmath.h MillMotion.h + MillPathLine.cpp + MillPathLine.h MillPathSegment.cpp MillPathSegment.h MillSimulation.cpp @@ -60,10 +62,14 @@ SET(CAMSimulator_SRCS_Core OpenGlWrapper.h Shader.cpp Shader.h + SimDisplay.cpp + SimDisplay.h SimShapes.cpp SimShapes.h StockObject.cpp StockObject.h + SolidObject.cpp + SolidObject.h Texture.cpp Texture.h TextureLoader.cpp diff --git a/src/Mod/CAM/PathSimulator/AppGL/DlgCAMSimulator.cpp b/src/Mod/CAM/PathSimulator/AppGL/DlgCAMSimulator.cpp index c1474d3b4e..55e81b9521 100644 --- a/src/Mod/CAM/PathSimulator/AppGL/DlgCAMSimulator.cpp +++ b/src/Mod/CAM/PathSimulator/AppGL/DlgCAMSimulator.cpp @@ -24,6 +24,7 @@ #include "DlgCAMSimulator.h" #include "MillSimulation.h" +#include #include #include #include @@ -64,8 +65,9 @@ bool DlgCAMSimulator::event(QEvent* event) renderNow(); return true; default: - return QWindow::event(event); + break; } + return QWindow::event(event); } void DlgCAMSimulator::exposeEvent(QExposeEvent* event) @@ -99,7 +101,6 @@ void DlgCAMSimulator::wheelEvent(QWheelEvent* ev) void DlgCAMSimulator::resetSimulation() { - mMillSimulator->Clear(); } void DlgCAMSimulator::addGcodeCommand(const char* cmd) @@ -122,35 +123,107 @@ void DlgCAMSimulator::addTool(const std::vector toolProfilePoints, void DlgCAMSimulator::hideEvent(QHideEvent* ev) { - Q_UNUSED(ev) + mMillSimulator->Clear(); + doGlCleanup(); mAnimating = false; + QWindow::hideEvent(ev); + close(); + mInstance = nullptr; } -void DlgCAMSimulator::startSimulation(const SimStock* stock, float quality) +void DlgCAMSimulator::resizeEvent(QResizeEvent* event) +{ + if (!mContext) { + return; + } + QSize newSize = event->size(); + int newWidth = newSize.width(); + int newHeight = newSize.height(); + if (mMillSimulator != nullptr) { + mMillSimulator->UpdateWindowScale(newWidth, newHeight); + } + const qreal retinaScale = devicePixelRatio(); + glViewport(0, 0, newWidth * retinaScale, newHeight * retinaScale); +} + +void DlgCAMSimulator::GetMeshData(const Part::TopoShape& tshape, + float resolution, + std::vector& verts, + std::vector& indices) +{ + std::vector normalCount; + int nVerts = 0; + for (auto& shape : tshape.getSubTopoShapes(TopAbs_FACE)) { + std::vector points; + std::vector facets; + shape.getFaces(points, facets, resolution); + + std::vector normals(points.size()); + std::vector normalCount(points.size()); + + // copy triangle indices and calculate normals + for (auto face : facets) { + indices.push_back(face.I1 + nVerts); + indices.push_back(face.I2 + nVerts); + indices.push_back(face.I3 + nVerts); + + // calculate normal + Base::Vector3d vAB = points[face.I2] - points[face.I1]; + Base::Vector3d vAC = points[face.I3] - points[face.I1]; + Base::Vector3d vNorm = vAB.Cross(vAC).Normalize(); + + normals[face.I1] += vNorm; + normals[face.I2] += vNorm; + normals[face.I3] += vNorm; + + normalCount[face.I1]++; + normalCount[face.I2]++; + normalCount[face.I3]++; + } + + // copy points and set normals + for (unsigned int i = 0; i < points.size(); i++) { + Base::Vector3d& point = points[i]; + Base::Vector3d& normal = normals[i]; + int count = normalCount[i]; + normal /= count; + verts.push_back(Vertex(point.x, point.y, point.z, normal.x, normal.y, normal.z)); + } + + nVerts = verts.size(); + } +} + +void DlgCAMSimulator::startSimulation(const Part::TopoShape& stock, float quality) { - mStock = *stock; mQuality = quality; mNeedsInitialize = true; show(); + checkInitialization(); + SetStockShape(stock, 1); setAnimating(true); } void DlgCAMSimulator::initialize() { - mMillSimulator - ->SetBoxStock(mStock.mPx, mStock.mPy, mStock.mPz, mStock.mLx, mStock.mLy, mStock.mLz); mMillSimulator->InitSimulation(mQuality); const qreal retinaScale = devicePixelRatio(); glViewport(0, 0, width() * retinaScale, height() * retinaScale); + glEnable(GL_MULTISAMPLE); } void DlgCAMSimulator::checkInitialization() { if (!mContext) { + mLastContext = QOpenGLContext::currentContext(); mContext = new QOpenGLContext(this); mContext->setFormat(requestedFormat()); mContext->create(); + QSurfaceFormat format; + format.setSamples(16); + format.setSwapInterval(2); + mContext->setFormat(format); gOpenGlContext = mContext; mNeedsInitialize = true; } @@ -164,6 +237,17 @@ void DlgCAMSimulator::checkInitialization() } } +void DlgCAMSimulator::doGlCleanup() +{ + if (mLastContext != nullptr) { + mLastContext->makeCurrent(this); + } + if (mContext != nullptr) { + mContext->deleteLater(); + mContext = nullptr; + } +} + void DlgCAMSimulator::renderNow() { static unsigned int lastTime = 0; @@ -213,13 +297,30 @@ DlgCAMSimulator* DlgCAMSimulator::GetInstance() format.setStencilBufferSize(8); mInstance = new DlgCAMSimulator(); mInstance->setFormat(format); - mInstance->resize(800, 600); + mInstance->resize(MillSim::gWindowSizeW, MillSim::gWindowSizeH); mInstance->setModality(Qt::ApplicationModal); - mInstance->show(); + mInstance->setMinimumWidth(700); + mInstance->setMinimumHeight(400); } return mInstance; } +void DlgCAMSimulator::SetStockShape(const Part::TopoShape& shape, float resolution) +{ + std::vector verts; + std::vector indices; + GetMeshData(shape, resolution, verts, indices); + mMillSimulator->SetArbitraryStock(verts, indices); +} + +void DlgCAMSimulator::SetBaseShape(const Part::TopoShape& tshape, float resolution) +{ + std::vector verts; + std::vector indices; + GetMeshData(tshape, resolution, verts, indices); + mMillSimulator->SetBaseObject(verts, indices); +} + DlgCAMSimulator* DlgCAMSimulator::mInstance = nullptr; //************************************************************************************************************ diff --git a/src/Mod/CAM/PathSimulator/AppGL/DlgCAMSimulator.h b/src/Mod/CAM/PathSimulator/AppGL/DlgCAMSimulator.h index a60b434860..50dda37f0c 100644 --- a/src/Mod/CAM/PathSimulator/AppGL/DlgCAMSimulator.h +++ b/src/Mod/CAM/PathSimulator/AppGL/DlgCAMSimulator.h @@ -23,6 +23,7 @@ #ifndef PATHSIMULATOR_CAMSimulatorGui_H #define PATHSIMULATOR_CAMSimulatorGui_H +#include #include #include #include @@ -33,7 +34,9 @@ namespace MillSim { -class MillSimulation; // use short declaration as using 'include' causes a header loop +// use short declaration as using 'include' causes a header loop +class MillSimulation; +struct Vertex; } namespace CAMSimulator @@ -63,11 +66,13 @@ public: void setAnimating(bool animating); static DlgCAMSimulator* GetInstance(); + void SetStockShape(const Part::TopoShape& tshape, float resolution); + void SetBaseShape(const Part::TopoShape& tshape, float resolution); -public: // slots: + public: // slots: void renderLater(); void renderNow(); - void startSimulation(const SimStock* stock, float quality); + void startSimulation(const Part::TopoShape& stock, float quality); void resetSimulation(); void addGcodeCommand(const char* cmd); void addTool(const std::vector toolProfilePoints, @@ -77,23 +82,28 @@ public: // slots: protected: bool event(QEvent* event) override; - void checkInitialization(); + void doGlCleanup(); void exposeEvent(QExposeEvent* event) override; void mouseMoveEvent(QMouseEvent* ev) override; void mousePressEvent(QMouseEvent* ev) override; void mouseReleaseEvent(QMouseEvent* ev) override; void wheelEvent(QWheelEvent* ev) override; void hideEvent(QHideEvent* ev) override; + void resizeEvent(QResizeEvent* event) override; + void GetMeshData(const Part::TopoShape& tshape, + float resolution, + std::vector& verts, + std::vector& indices); private: bool mAnimating = false; bool mNeedsInitialize = false; QOpenGLContext* mContext = nullptr; + QOpenGLContext* mLastContext = nullptr; MillSim::MillSimulation* mMillSimulator = nullptr; static DlgCAMSimulator* mInstance; - SimStock mStock = {0, 0, 0, 1, 1, 1, 1}; float mQuality = 10; }; diff --git a/src/Mod/CAM/PathSimulator/AppGL/EndMill.cpp b/src/Mod/CAM/PathSimulator/AppGL/EndMill.cpp index 039d5d27f9..a6f078d98c 100644 --- a/src/Mod/CAM/PathSimulator/AppGL/EndMill.cpp +++ b/src/Mod/CAM/PathSimulator/AppGL/EndMill.cpp @@ -35,31 +35,28 @@ EndMill::EndMill(int toolid, float diameter) EndMill::EndMill(const std::vector& toolProfile, int toolid, float diameter) : EndMill(toolid, diameter) { - profilePoints = nullptr; - mHandleAllocation = false; + profilePoints.clear(); - int srcBuffSize = static_cast(toolProfile.size()); + int srcBuffSize = toolProfile.size(); nPoints = srcBuffSize / 2; if (nPoints < 2) { return; } // make sure last point is at 0,0 else, add it - bool missingCenterPoint = fabs(toolProfile[nPoints * 2 - 2]) > 0.0001F; + bool missingCenterPoint = fabs(toolProfile[nPoints * 2 - 2]) > 0.0001f; if (missingCenterPoint) { nPoints++; } int buffSize = PROFILE_BUFFER_SIZE(nPoints); - profilePoints = new float[buffSize]; - if (profilePoints == nullptr) { - return; - } + profilePoints.resize(buffSize); // copy profile points - mHandleAllocation = true; - for (int i = 0; i < srcBuffSize; i++) { - profilePoints[i] = toolProfile[i] + 0.01F; // add some width to reduce simulation artifacts + for (int i = 0; i < srcBuffSize; i += 2) { + // add some width to reduce simulation artifacts + profilePoints[i] = toolProfile[i] + diameter * 0.01f; + profilePoints[i + 1] = toolProfile[i + 1] - diameter * 0.01f; } if (missingCenterPoint) { profilePoints[srcBuffSize] = 0.0F; @@ -74,9 +71,6 @@ EndMill::~EndMill() toolShape.FreeResources(); halfToolShape.FreeResources(); pathShape.FreeResources(); - if (mHandleAllocation) { - delete[] profilePoints; - } } void EndMill::GenerateDisplayLists(float quality) @@ -91,21 +85,21 @@ void EndMill::GenerateDisplayLists(float quality) } // full tool - toolShape.RotateProfile(profilePoints, nPoints, 0, nslices, false); + toolShape.RotateProfile(profilePoints.data(), nPoints, 0, 0, nslices, false); // half tool - halfToolShape.RotateProfile(profilePoints, nPoints, 0, nslices / 2, true); + halfToolShape.RotateProfile(profilePoints.data(), nPoints, 0, 0, nslices / 2, true); // unit path int nFullPoints = PROFILE_BUFFER_POINTS(nPoints); - pathShape.ExtrudeProfileLinear(profilePoints, nFullPoints, 0, 1, 0, 0, true, false); + pathShape.ExtrudeProfileLinear(profilePoints.data(), nFullPoints, 0, 1, 0, 0, true, false); } unsigned int EndMill::GenerateArcSegmentDL(float radius, float angleRad, float zShift, Shape* retShape) { int nFullPoints = PROFILE_BUFFER_POINTS(nPoints); - retShape->ExtrudeProfileRadial(profilePoints, + retShape->ExtrudeProfileRadial(profilePoints.data(), nFullPoints, radius, angleRad, diff --git a/src/Mod/CAM/PathSimulator/AppGL/EndMill.h b/src/Mod/CAM/PathSimulator/AppGL/EndMill.h index f60a2d81e4..66db2a2eb9 100644 --- a/src/Mod/CAM/PathSimulator/AppGL/EndMill.h +++ b/src/Mod/CAM/PathSimulator/AppGL/EndMill.h @@ -35,7 +35,7 @@ namespace MillSim class EndMill { public: - float* profilePoints = nullptr; + std::vectorprofilePoints; float radius; int nPoints = 0; int toolId = -1; @@ -53,9 +53,6 @@ public: protected: void MirrorPointBuffer(); - -private: - bool mHandleAllocation = false; }; } // namespace MillSim diff --git a/src/Mod/CAM/PathSimulator/AppGL/GCodeParser.cpp b/src/Mod/CAM/PathSimulator/AppGL/GCodeParser.cpp index 4702f32838..29c177f42d 100644 --- a/src/Mod/CAM/PathSimulator/AppGL/GCodeParser.cpp +++ b/src/Mod/CAM/PathSimulator/AppGL/GCodeParser.cpp @@ -27,8 +27,6 @@ #endif #include "GCodeParser.h" -#include -#include using namespace MillSim; @@ -43,7 +41,7 @@ GCodeParser::~GCodeParser() bool GCodeParser::Parse(const char* filename) { Operations.clear(); - lastState = {eNop, -1, 0.0F, 0.0F, 0.0F, 0.0F, 0.0F, 0.0F, 0.0F}; + lastState = {eNop, -1, 0, 0, 0, 0, 0, 0, 0}; lastTool = -1; FILE* fl; @@ -222,6 +220,7 @@ bool GCodeParser::AddLine(const char* ptr) Operations.push_back(lastState); lastState.z = rPlane; Operations.push_back(lastState); + lastState.cmd = eDril; } else { Operations.push_back(lastState); diff --git a/src/Mod/CAM/PathSimulator/AppGL/GCodeParser.h b/src/Mod/CAM/PathSimulator/AppGL/GCodeParser.h index 14ab3b04c3..7632de8a25 100644 --- a/src/Mod/CAM/PathSimulator/AppGL/GCodeParser.h +++ b/src/Mod/CAM/PathSimulator/AppGL/GCodeParser.h @@ -45,8 +45,8 @@ public: public: std::vector Operations; - MillMotion lastState = {eNop, 0, 0.0F, 0.0F, 0.0F, 0.0F, 0.0F, 0.0F, 0.0F}; - MillMotion lastLastState = {eNop, 0, 0.0F, 0.0F, 0.0F, 0.0F, 0.0F, 0.0F, 0.0F}; + MillMotion lastState = {eNop, 0, 0, 0, 0, 0, 0, 0, 0}; + MillMotion lastLastState = {eNop, 0, 0, 0, 0, 0, 0, 0, 0}; protected: const char* GetNextToken(const char* ptr, GCToken* token); diff --git a/src/Mod/CAM/PathSimulator/AppGL/GlUtils.cpp b/src/Mod/CAM/PathSimulator/AppGL/GlUtils.cpp index 50f91983f4..a428198964 100644 --- a/src/Mod/CAM/PathSimulator/AppGL/GlUtils.cpp +++ b/src/Mod/CAM/PathSimulator/AppGL/GlUtils.cpp @@ -25,6 +25,8 @@ namespace MillSim { +int gWindowSizeW = 800; +int gWindowSizeH = 600; int gDebug = -1; diff --git a/src/Mod/CAM/PathSimulator/AppGL/GlUtils.h b/src/Mod/CAM/PathSimulator/AppGL/GlUtils.h index 255664b760..558501d1a5 100644 --- a/src/Mod/CAM/PathSimulator/AppGL/GlUtils.h +++ b/src/Mod/CAM/PathSimulator/AppGL/GlUtils.h @@ -28,6 +28,7 @@ #define PI 3.14159265f #define PI2 (PI * 2) + constexpr auto EPSILON = 0.00001f; #define EQ_FLOAT(x, y) (fabs((x) - (y)) < EPSILON) @@ -38,16 +39,31 @@ constexpr auto EPSILON = 0.00001f; { \ GLClearError(); \ x; \ - if (GLLogError()) \ + if (GLLogError()) \ __debugbreak(); \ } #define RadToDeg(x) (x * 180.0f / PI) +#define GLDELETE(type, x) \ + { \ + if (x != 0) \ + glDelete##type(1, &x); \ + x = 0; \ + } + +#define GLDELETE_FRAMEBUFFER(x) GLDELETE(Framebuffers, x) +#define GLDELETE_TEXTURE(x) GLDELETE(Textures, x) +#define GLDELETE_VERTEXARRAY(x) GLDELETE(VertexArrays, x) +#define GLDELETE_RENDERBUFFER(x) GLDELETE(Renderbuffers, x) +#define GLDELETE_BUFFER(x) GLDELETE(Buffers, x) + namespace MillSim { void GLClearError(); bool GLLogError(); extern mat4x4 identityMat; extern int gDebug; +extern int gWindowSizeW; +extern int gWindowSizeH; } // namespace MillSim #endif // !__glutils_h__ diff --git a/src/Mod/CAM/PathSimulator/AppGL/GuiDisplay.cpp b/src/Mod/CAM/PathSimulator/AppGL/GuiDisplay.cpp index 6e2cb43ef7..b3005f41ff 100644 --- a/src/Mod/CAM/PathSimulator/AppGL/GuiDisplay.cpp +++ b/src/Mod/CAM/PathSimulator/AppGL/GuiDisplay.cpp @@ -24,23 +24,24 @@ #include "OpenGlWrapper.h" #include "MillSimulation.h" #include -#include "GlUtils.h" -#include using namespace MillSim; GuiItem guiItems[] = { - {0, 0, 360, 554, 0, false, false, {}}, - {0, 0, 448, 540, 1, false, false, {}}, - {0, 0, 170, 540, 'P', true, false, {}}, - {0, 0, 170, 540, 'S', false, false, {}}, - {0, 0, 210, 540, 'T', false, false, {}}, - {0, 0, 250, 540, 'F', false, false, {}}, - {0, 0, 290, 540, ' ', false, false, {}}, - {0, 0, 620, 540, 0, false, false, {}}, - {0, 0, 660, 540, 0, false, false, {}}, - {0, 0, 645, 540, 0, false, false, {}}, - {0, 0, 640, 540, 0, true, false, {}}, + {eGuiItemSlider, 0, 0, 240, -36, 0}, + {eGuiItemThumb, 0, 0, 328, -50, 1}, + {eGuiItemPause, 0, 0, 40, -50, 'P', true}, + {eGuiItemPlay, 0, 0, 40, -50, 'S', false}, + {eGuiItemSingleStep, 0, 0, 80, -50, 'T'}, + {eGuiItemFaster, 0, 0, 120, -50, 'F'}, + {eGuiItemRotate, 0, 0, -140, -50, ' ', false, GUIITEM_CHECKABLE}, + {eGuiItemCharXImg, 0, 0, 160, -50, 0, false, 0}, // 620 + {eGuiItemChar0Img, 0, 0, 200, -50, 0, false, 0}, + {eGuiItemChar1Img, 0, 0, 185, -50, 0, false, 0}, + {eGuiItemChar4Img, 0, 0, 180, -50, 0, true, 0}, + {eGuiItemPath, 0, 0, -100, -50, 'L', false, GUIITEM_CHECKABLE}, + {eGuiItemAmbientOclusion, 0, 0, -60, -50, 'A', false, GUIITEM_CHECKABLE}, + {eGuiItemView, 0, 0, -180, -50, 'V', false}, }; #define NUM_GUI_ITEMS (sizeof(guiItems) / sizeof(GuiItem)) @@ -56,7 +57,19 @@ std::vector guiFileNames = {"Slider.png", "X.png", "0.png", "1.png", - "4.png"}; + "4.png", + "Path.png", + "AmbientOclusion.png", + "View.png"}; + +void GuiDisplay::UpdateProjection() +{ + mat4x4 projmat; + // mat4x4 viewmat; + mat4x4_ortho(projmat, 0, gWindowSizeW, gWindowSizeH, 0, -1, 1); + mShader.Activate(); + mShader.UpdateProjectionMat(projmat); +} bool GuiDisplay::GenerateGlItem(GuiItem* guiItem) { @@ -95,8 +108,17 @@ bool GuiDisplay::GenerateGlItem(GuiItem* guiItem) return true; } -bool GuiDisplay::InutGui() +void GuiDisplay::DestroyGlItem(GuiItem* guiItem) { + GLDELETE_BUFFER((guiItem->vbo)); + GLDELETE_VERTEXARRAY((guiItem->vao)); +} + +bool GuiDisplay::InitGui() +{ + if (guiInitiated) { + return true; + } // index buffer glGenBuffers(1, &mIbo); GLshort indices[6] = {0, 2, 3, 0, 3, 1}; @@ -108,26 +130,35 @@ bool GuiDisplay::InutGui() return false; } mTexture.LoadImage(buffer, TEX_SIZE, TEX_SIZE); - for (unsigned long i = 0; i < NUM_GUI_ITEMS; i++) { + for (unsigned int i = 0; i < NUM_GUI_ITEMS; i++) { guiItems[i].texItem = *tLoader.GetTextureItem(i); GenerateGlItem(&(guiItems[i])); } - mThumbStartX = guiItems[eGuiItemSlider].sx - guiItems[eGuiItemThumb].texItem.w / 2; + mThumbStartX = guiItems[eGuiItemSlider].posx() - guiItems[eGuiItemThumb].texItem.w / 2; mThumbMaxMotion = (float)guiItems[eGuiItemSlider].texItem.w; - UpdateSimSpeed(1); - - // shader - mat4x4 projmat; - // mat4x4 viewmat; - mat4x4_ortho(projmat, 0, 800, 600, 0, -1, 1); + // init shader mShader.CompileShader((char*)VertShader2DTex, (char*)FragShader2dTex); mShader.UpdateTextureSlot(0); - mShader.UpdateProjectionMat(projmat); + + UpdateSimSpeed(1); + UpdateProjection(); + guiInitiated = true; return true; } +void GuiDisplay::ResetGui() +{ + mShader.Destroy(); + for (unsigned int i = 0; i < NUM_GUI_ITEMS; i++) { + DestroyGlItem(&(guiItems[i])); + } + mTexture.DestroyTexture(); + GLDELETE_BUFFER(mIbo); + guiInitiated = false; +} + void GuiDisplay::RenderItem(int itemId) { GuiItem* item = &(guiItems[itemId]); @@ -135,9 +166,9 @@ void GuiDisplay::RenderItem(int itemId) return; } mat4x4 model; - mat4x4_translate(model, (float)item->sx, (float)item->sy, 0); + mat4x4_translate(model, (float)item->posx(), (float)item->posy(), 0); mShader.UpdateModelMat(model, nullptr); - if (itemId == mPressedItem) { + if (item == mPressedItem) { mShader.UpdateObjColor(mPressedColor); } else if (item->mouseOver) { @@ -146,6 +177,9 @@ void GuiDisplay::RenderItem(int itemId) else if (itemId > 1 && item->actionKey == 0) { mShader.UpdateObjColor(mTextColor); } + else if (item->flags & GUIITEM_CHECKED) { + mShader.UpdateObjColor(mToggleColor); + } else { mShader.UpdateObjColor(mStdColor); } @@ -157,13 +191,32 @@ void GuiDisplay::RenderItem(int itemId) void GuiDisplay::MouseCursorPos(int x, int y) { - for (unsigned long i = 0; i < NUM_GUI_ITEMS; i++) { + mMouseOverItem = nullptr; + for (unsigned int i = 0; i < NUM_GUI_ITEMS; i++) { GuiItem* g = &(guiItems[i]); if (g->actionKey == 0) { continue; } - g->mouseOver = - (x > g->sx && y > g->sy && x < (g->sx + g->texItem.w) && y < (g->sy + g->texItem.h)); + bool mouseCursorContained = + x > g->posx() && x < (g->posx() + g->texItem.w) && + y > g->posy() && y < (g->posy() + g->texItem.h); + + g->mouseOver = !g->hidden && mouseCursorContained; + + if (g->mouseOver) { + mMouseOverItem = g; + } + } +} + +void MillSim::GuiDisplay::HandleActionItem(GuiItem* guiItem) +{ + if (guiItem->actionKey >= ' ') { + if (guiItem->flags & GUIITEM_CHECKABLE) { + guiItem->flags ^= GUIITEM_CHECKED; + } + bool isChecked = (guiItem->flags & GUIITEM_CHECKED) != 0; + mMillSim->HandleGuiAction(guiItem->name, isChecked); } } @@ -171,45 +224,38 @@ void GuiDisplay::MousePressed(int button, bool isPressed, bool isSimRunning) { if (button == MS_MOUSE_LEFT) { if (isPressed) { - mPressedItem = eGuiItemMax; - for (unsigned long i = 1; i < NUM_GUI_ITEMS; i++) { - GuiItem* g = &(guiItems[i]); - if (g->mouseOver && !g->hidden) { - mPressedItem = (eGuiItems)i; - break; - } - } - if (mPressedItem != eGuiItemMax) { - GuiItem* g = &(guiItems[mPressedItem]); - if (g->actionKey >= 32) { - mMillSim->HandleKeyPress(g->actionKey); - } + if (mMouseOverItem != nullptr) { + mPressedItem = mMouseOverItem; + HandleActionItem(mPressedItem); } } else // button released { UpdatePlayState(isSimRunning); - mPressedItem = eGuiItemMax; + if (mPressedItem != nullptr) { + MouseCursorPos(mPressedItem->posx() + 1, mPressedItem->posy() + 1); + mPressedItem = nullptr; + } } } } -void GuiDisplay::MouseDrag(int buttons, int dx, int dy) +void GuiDisplay::MouseDrag(int /* buttons */, int dx, int /* dy */) { - (void)buttons; - (void)dy; - if (mPressedItem == eGuiItemThumb) { - GuiItem* g = &(guiItems[eGuiItemThumb]); - int newx = g->sx + dx; + if (mPressedItem == nullptr) { + return; + } + if (mPressedItem->name == eGuiItemThumb) { + int newx = mPressedItem->posx() + dx; if (newx < mThumbStartX) { newx = mThumbStartX; } if (newx > ((int)mThumbMaxMotion + mThumbStartX)) { newx = (int)mThumbMaxMotion + mThumbStartX; } - if (newx != g->sx) { + if (newx != mPressedItem->posx()) { mMillSim->SetSimulationStage((float)(newx - mThumbStartX) / mThumbMaxMotion); - g->sx = newx; + mPressedItem->setPosx(newx); } } } @@ -227,19 +273,40 @@ void MillSim::GuiDisplay::UpdateSimSpeed(int speed) guiItems[eGuiItemChar4Img].hidden = speed != 40; } +void MillSim::GuiDisplay::HandleKeyPress(int key) +{ + for (unsigned int i = 0; i < NUM_GUI_ITEMS; i++) { + GuiItem* g = &(guiItems[i]); + if (g->actionKey == key) { + HandleActionItem(g); + } + } +} + +bool MillSim::GuiDisplay::IsChecked(eGuiItems item) +{ + return (guiItems[item].flags & GUIITEM_CHECKED) != 0; +} + +void MillSim::GuiDisplay::UpdateWindowScale() +{ + UpdateProjection(); +} + void GuiDisplay::Render(float progress) { - if (mPressedItem != eGuiItemThumb) { - guiItems[eGuiItemThumb].sx = (int)(mThumbMaxMotion * progress) + mThumbStartX; + if (mPressedItem == nullptr || mPressedItem->name != eGuiItemThumb) { + guiItems[eGuiItemThumb].setPosx((int)(mThumbMaxMotion * progress) + mThumbStartX); } glDisable(GL_CULL_FACE); glDisable(GL_DEPTH_TEST); + mTexture.Activate(); mShader.Activate(); mShader.UpdateTextureSlot(0); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - for (unsigned long i = 0; i < NUM_GUI_ITEMS; i++) { + for (int i = 0; i < (int)NUM_GUI_ITEMS; i++) { RenderItem(i); } } diff --git a/src/Mod/CAM/PathSimulator/AppGL/GuiDisplay.h b/src/Mod/CAM/PathSimulator/AppGL/GuiDisplay.h index 0d9574e4d2..d98c2f1c64 100644 --- a/src/Mod/CAM/PathSimulator/AppGL/GuiDisplay.h +++ b/src/Mod/CAM/PathSimulator/AppGL/GuiDisplay.h @@ -26,27 +26,12 @@ #include "Texture.h" #include "Shader.h" #include "TextureLoader.h" +#include "GlUtils.h" namespace MillSim { class MillSimulation; -struct GuiItem -{ - unsigned int vbo, vao; - int sx, sy; // screen location - int actionKey; // action key when item pressed - bool hidden; // is item hidden - bool mouseOver; - TextureItem texItem; -}; - -struct Vertex2D -{ - float x, y; - float tx, ty; -}; - enum eGuiItems { eGuiItemSlider, @@ -60,17 +45,58 @@ enum eGuiItems eGuiItemChar0Img, eGuiItemChar1Img, eGuiItemChar4Img, + eGuiItemPath, + eGuiItemAmbientOclusion, + eGuiItemView, eGuiItemMax }; +struct GuiItem +{ + eGuiItems name; + unsigned int vbo, vao; + int sx, sy; // screen location + int actionKey; // action key when item pressed + bool hidden {}; // is item hidden + unsigned int flags {}; + bool mouseOver {}; + TextureItem texItem {}; + + int posx() { + return sx >= 0 ? sx : gWindowSizeW + sx; + } + int posy() { + return sy >= 0 ? sy : gWindowSizeH + sy; + } + void setPosx(int x) + { + sx = sx >= 0 ? x : x - gWindowSizeW; + } + void setPosy(int y) + { + sy = sy >= 0 ? y : y - gWindowSizeH; + } +}; + +#define GUIITEM_CHECKABLE 0x01 +#define GUIITEM_CHECKED 0x02 + + +struct Vertex2D +{ + float x, y; + float tx, ty; +}; class GuiDisplay { public: // GuiDisplay() {}; - bool InutGui(); + bool InitGui(); + void ResetGui(); void Render(float progress); void MouseCursorPos(int x, int y); + void HandleActionItem(GuiItem* guiItem); void MousePressed(int button, bool isPressed, bool isRunning); void MouseDrag(int buttons, int dx, int dy); void SetMillSimulator(MillSimulation* millSim) @@ -79,19 +105,29 @@ public: } void UpdatePlayState(bool isRunning); void UpdateSimSpeed(int speed); + void HandleKeyPress(int key); + bool IsChecked(eGuiItems item); + void UpdateWindowScale(); + +public: + bool guiInitiated = false; private: + void UpdateProjection(); bool GenerateGlItem(GuiItem* guiItem); + void DestroyGlItem(GuiItem* guiItem); void RenderItem(int itemId); vec3 mStdColor = {0.8f, 0.8f, 0.4f}; + vec3 mToggleColor = {0.9f, 0.6f, 0.2f}; vec3 mHighlightColor = {1.0f, 1.0f, 0.9f}; vec3 mPressedColor = {1.0f, 0.5f, 0.0f}; vec3 mTextColor = {1.0f, 0.5f, 0.0f}; Shader mShader; Texture mTexture; - eGuiItems mPressedItem = eGuiItemMax; + GuiItem* mPressedItem = nullptr; + GuiItem* mMouseOverItem = nullptr; MillSimulation* mMillSim = nullptr; unsigned int mIbo = 0; int mThumbStartX = 0; diff --git a/src/Mod/CAM/PathSimulator/AppGL/MillPathLine.cpp b/src/Mod/CAM/PathSimulator/AppGL/MillPathLine.cpp new file mode 100644 index 0000000000..c35976de9a --- /dev/null +++ b/src/Mod/CAM/PathSimulator/AppGL/MillPathLine.cpp @@ -0,0 +1,60 @@ +#include "MillPathLine.h" +#include "OpenGlWrapper.h" +#include "GlUtils.h" +#include "Shader.h" + +namespace MillSim +{ + + +MillPathLine::MillPathLine() +{ + mVao = mVbo = 0; +} + +void MillPathLine::GenerateModel() +{ + mNumVerts = MillPathPointsBuffer.size(); + void* vbuffer = MillPathPointsBuffer.data(); + + // vertex array + glGenVertexArrays(1, &mVao); + glBindVertexArray(mVao); + + // vertex buffer + glGenBuffers(1, &mVbo); + glBindBuffer(GL_ARRAY_BUFFER, mVbo); + glBufferData(GL_ARRAY_BUFFER, mNumVerts * sizeof(MillPathPosition), vbuffer, GL_STATIC_DRAW); + + // vertex attribs + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(MillPathPosition), + (void*)offsetof(MillPathPosition, X)); + glEnableVertexAttribArray(1); + glVertexAttribIPointer(1, 1, GL_INT, sizeof(MillPathPosition), + (void*)offsetof(MillPathPosition, SegmentId)); + + // unbind and free + glBindVertexArray(0); + MillPathPointsBuffer.clear(); + +} + +void MillPathLine::Clear() +{ + MillPathPointsBuffer.clear(); + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindVertexArray(0); + GLDELETE_BUFFER(mVbo); + GLDELETE_VERTEXARRAY(mVao); +} + +void MillPathLine::Render() +{ + glBindVertexArray(mVao); + glDrawArrays(GL_LINE_STRIP, 0, mNumVerts); +} + +} // namespace Millsim + + diff --git a/src/Mod/CAM/PathSimulator/AppGL/MillPathLine.h b/src/Mod/CAM/PathSimulator/AppGL/MillPathLine.h new file mode 100644 index 0000000000..c62d486a36 --- /dev/null +++ b/src/Mod/CAM/PathSimulator/AppGL/MillPathLine.h @@ -0,0 +1,34 @@ +#ifndef __millpathline_h__ +#define __millpathline_h__ +#include + +namespace MillSim +{ + +struct MillPathPosition +{ + float X, Y, Z; + int SegmentId; +}; + +class MillPathLine +{ +public: + MillPathLine(); + void GenerateModel(); + void Clear(); + void Render(); + +public: + std::vector MillPathPointsBuffer; + +protected: + unsigned int mVbo; + unsigned int mVao; + int mNumVerts; +}; + +} // namespace Millsim + +#endif // !__millpathline_h__ + diff --git a/src/Mod/CAM/PathSimulator/AppGL/MillPathSegment.cpp b/src/Mod/CAM/PathSimulator/AppGL/MillPathSegment.cpp index fbd126b851..310363b959 100644 --- a/src/Mod/CAM/PathSimulator/AppGL/MillPathSegment.cpp +++ b/src/Mod/CAM/PathSimulator/AppGL/MillPathSegment.cpp @@ -56,11 +56,8 @@ float MillPathSegment::mResolution = 1; float MillPathSegment::mSmallRadStep = (PI / 8); MillPathSegment::MillPathSegment(EndMill* _endmill, MillMotion* from, MillMotion* to) - : mShearMat {{1.0F, 0.0F, 0.0F, 0.0F}, - {0.0F, 1.0F, 0.0F, 0.0F}, - {0.0F, 0.0F, 1.0F, 0.0F}, - {0.0F, 0.0F, 0.0F, 1.0F}} { + mat4x4_identity(mShearMat); MotionPosToVec(mStartPos, from); MotionPosToVec(mDiff, to); vec3_sub(mDiff, mDiff, mStartPos); @@ -142,6 +139,32 @@ MillPathSegment::~MillPathSegment() } +void MillPathSegment::AppendPathPoints(std::vector& pointsBuffer) +{ + MillPathPosition mpPos; + if (mMotionType == MTCurved) { + float ang = mStartAngRad; + float z = mStartPos[PZ]; + float zStep = mDiff[PZ] / numSimSteps; + for (int i = 1; i < numSimSteps; i++) { + ang -= mStepAngRad; + z += zStep; + mpPos.X = mCenter[PX] - sinf(ang) * mRadius; + mpPos.Y = mCenter[PY] + cosf(ang) * mRadius; + mpPos.Z = z; + mpPos.SegmentId = segmentIndex; + pointsBuffer.push_back(mpPos); + } + } + else { + mpPos.X = mStartPos[PX] + mDiff[PX]; + mpPos.Y = mStartPos[PY] + mDiff[PY]; + mpPos.Z = mStartPos[PZ] + mDiff[PZ]; + mpPos.SegmentId = segmentIndex; + pointsBuffer.push_back(mpPos); + } +} + void MillPathSegment::render(int step) { mStepNumber = step; diff --git a/src/Mod/CAM/PathSimulator/AppGL/MillPathSegment.h b/src/Mod/CAM/PathSimulator/AppGL/MillPathSegment.h index 43369cb5f7..09c670e4b1 100644 --- a/src/Mod/CAM/PathSimulator/AppGL/MillPathSegment.h +++ b/src/Mod/CAM/PathSimulator/AppGL/MillPathSegment.h @@ -27,6 +27,7 @@ #include "MillMotion.h" #include "EndMill.h" #include "linmath.h" +#include "MillPathLine.h" namespace MillSim { @@ -38,7 +39,6 @@ enum MotionType MTCurved }; - bool IsVerticalMotion(MillMotion* m1, MillMotion* m2); @@ -55,7 +55,7 @@ public: virtual ~MillPathSegment(); - /// Calls the display list. + virtual void AppendPathPoints(std::vector& pointsBuffer); virtual void render(int substep); virtual void GetHeadPosition(vec3 headPos); static float SetQuality(float quality, float maxStockDimension); // 1 minimum, 10 maximum @@ -65,6 +65,7 @@ public: bool isMultyPart; int numSimSteps; int indexInArray; + int segmentIndex; protected: diff --git a/src/Mod/CAM/PathSimulator/AppGL/MillSimulation.cpp b/src/Mod/CAM/PathSimulator/AppGL/MillSimulation.cpp index e0f7556949..09f9bc5ca5 100644 --- a/src/Mod/CAM/PathSimulator/AppGL/MillSimulation.cpp +++ b/src/Mod/CAM/PathSimulator/AppGL/MillSimulation.cpp @@ -25,580 +25,609 @@ #include #include -namespace MillSim { +namespace MillSim +{ - MillSimulation::MillSimulation() - { - mCurMotion = { eNop, -1, 0.0F, 0.0F, 0.0F, 0.0F, 0.0F, 0.0F, 0.0F }; - guiDisplay.SetMillSimulator(this); +MillSimulation::MillSimulation() +{ + mCurMotion = {eNop, -1, 0, 0, 0, 0, 0, 0, 0}; + guiDisplay.SetMillSimulator(this); +} + +MillSimulation::~MillSimulation() +{ + Clear(); +} + +void MillSimulation::ClearMillPathSegments() +{ + for (unsigned int i = 0; i < MillPathSegments.size(); i++) { + delete MillPathSegments[i]; + } + MillPathSegments.clear(); +} + +void MillSimulation::Clear() +{ + mCodeParser.Operations.clear(); + for (unsigned int i = 0; i < mToolTable.size(); i++) { + delete mToolTable[i]; + } + ClearMillPathSegments(); + mStockObject.~StockObject(); + mToolTable.clear(); + guiDisplay.ResetGui(); + simDisplay.CleanGL(); + mCurStep = 0; + mPathStep = -1; + mNTotalSteps = 0; +} + + +void MillSimulation::SimNext() +{ + static int simDecim = 0; + + simDecim++; + if (simDecim < 1) { + return; } - void MillSimulation::ClearMillPathSegments() { - //for (std::vector::const_iterator i = MillPathSegments.begin(); i != MillPathSegments.end(); ++i) { - // MillSim::MillPathSegment* p = *i; - // delete p; - //} - for (std::size_t i = 0; i < MillPathSegments.size(); i++) { - delete MillPathSegments[i]; - } - MillPathSegments.clear(); + simDecim = 0; + + if (mCurStep < mNTotalSteps) { + mCurStep += mSimSpeed; + CalcSegmentPositions(); + simDisplay.updateDisplay = true; } +} - void MillSimulation::Clear() - { - mCodeParser.Operations.clear(); - for (std::size_t i = 0; i < mToolTable.size(); i++) { - delete mToolTable[i]; - } - mToolTable.clear(); - mCurStep = 0; - mPathStep = -1; - mNTotalSteps = 0; - } +void MillSimulation::InitSimulation(float quality) +{ + ClearMillPathSegments(); + millPathLine.Clear(); + simDisplay.applySSAO = guiDisplay.IsChecked(eGuiItemAmbientOclusion); - - - void MillSimulation::SimNext() - { - static int simDecim = 0; - - simDecim++; - if (simDecim < 1) - return; - - simDecim = 0; - - if (mCurStep < mNTotalSteps) - { - mCurStep += mSimSpeed; - CalcSegmentPositions(); + mDestMotion = mZeroPos; + // gDestPos = curMillOperation->startPos; + mCurStep = 0; + mPathStep = -1; + mNTotalSteps = 0; + mSimPlaying = false; + mSimSpeed = 1; + MillPathSegment::SetQuality(quality, simDisplay.maxFar); + int nOperations = (int)mCodeParser.Operations.size(); + int segId = 0; + for (int i = 0; i < nOperations; i++) { + mCurMotion = mDestMotion; + mDestMotion = mCodeParser.Operations[i]; + EndMill* tool = GetTool(mDestMotion.tool); + if (tool != nullptr) { + MillSim::MillPathSegment* segment = + new MillSim::MillPathSegment(tool, &mCurMotion, &mDestMotion); + segment->indexInArray = i; + segment->segmentIndex = segId++; + mNTotalSteps += segment->numSimSteps; + MillPathSegments.push_back(segment); + segment->AppendPathPoints(millPathLine.MillPathPointsBuffer); } } + mNPathSteps = (int)MillPathSegments.size(); + millPathLine.GenerateModel(); + InitDisplay(quality); +} - void MillSimulation::InitSimulation(float quality) - { - ClearMillPathSegments(); - - mDestMotion = mZeroPos; - //gDestPos = curMillOperation->startPos; - mCurStep = 0; - mPathStep = -1; - mNTotalSteps = 0; - MillPathSegment::SetQuality(quality, mMaxFar); - int nOperations = (int)mCodeParser.Operations.size();; - for (int i = 0; i < nOperations; i++) - { - mCurMotion = mDestMotion; - mDestMotion = mCodeParser.Operations[i]; - EndMill* tool = GetTool(mDestMotion.tool); - if (tool != nullptr) - { - MillSim::MillPathSegment* segment = new MillSim::MillPathSegment(tool, &mCurMotion, &mDestMotion); - segment->indexInArray = i; - mNTotalSteps += segment->numSimSteps; - MillPathSegments.push_back(segment); - } - } - mNPathSteps = (int)MillPathSegments.size(); - InitDisplay(quality); - } - - EndMill* MillSimulation::GetTool(int toolId) - { - for (std::size_t i = 0; i < mToolTable.size(); i++) - { - if (mToolTable[i]->toolId == toolId) - { - return mToolTable[i]; - } - } - return nullptr; - } - - void MillSimulation::RemoveTool(int toolId) - { - EndMill* tool; - if ((tool = GetTool(toolId)) != nullptr) { - auto it = std::find(mToolTable.begin(), mToolTable.end(), tool); - if (it != mToolTable.end()) { - mToolTable.erase(it); - } - delete tool; +EndMill* MillSimulation::GetTool(int toolId) +{ + for (unsigned int i = 0; i < mToolTable.size(); i++) { + if (mToolTable[i]->toolId == toolId) { + return mToolTable[i]; } } + return nullptr; +} - void MillSimulation::AddTool(EndMill* tool) - { - // if we have another tool with same id, remove it - RemoveTool(tool->toolId); - mToolTable.push_back(tool); - } - - void - MillSimulation::AddTool(const std::vector& toolProfile, int toolid, float diameter) - { - // if we have another tool with same id, remove it - RemoveTool(toolid); - EndMill* tool = new EndMill(toolProfile, toolid, diameter); - mToolTable.push_back(tool); - } - - void MillSimulation::GlsimStart() - { - glEnable(GL_CULL_FACE); - glEnable(GL_DEPTH_TEST); - glEnable(GL_STENCIL_TEST); - glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); - } - - void MillSimulation::GlsimToolStep1(void) - { - glCullFace(GL_BACK); - glDepthFunc(GL_LESS); - glStencilFunc(GL_ALWAYS, 1, 0xFF); - glStencilOp(GL_ZERO, GL_ZERO, GL_REPLACE); - glDepthMask(GL_FALSE); - } - - - void MillSimulation::GlsimToolStep2(void) - { - glStencilFunc(GL_EQUAL, 1, 0xFF); - glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); - glDepthFunc(GL_GREATER); - glCullFace(GL_FRONT); - glDepthMask(GL_TRUE); - } - - void MillSimulation::GlsimClipBack(void) - { - glStencilFunc(GL_ALWAYS, 1, 0xFF); - glStencilOp(GL_REPLACE, GL_REPLACE, GL_ZERO); - glDepthFunc(GL_LESS); - glCullFace(GL_FRONT); - glDepthMask(GL_FALSE); - } - - - void MillSimulation::GlsimRenderStock(void) - { - glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); - glEnable(GL_STENCIL_TEST); - glStencilFunc(GL_EQUAL, 1, 0xFF); - glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); - glDepthFunc(GL_EQUAL); - glCullFace(GL_BACK); - } - - void MillSimulation::GlsimRenderTools(void) - { - glCullFace(GL_FRONT); - } - - void MillSimulation::GlsimEnd(void) - { - glCullFace(GL_BACK); - glStencilFunc(GL_ALWAYS, 1, 0xFF); - glDisable(GL_STENCIL_TEST); - glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); - glDepthMask(GL_TRUE); - glDepthFunc(GL_LESS); - } - - void MillSimulation::renderSegmentForward(int iSeg) - { - MillSim::MillPathSegment* p = MillPathSegments.at(iSeg); - int step = iSeg == mPathStep ? mSubStep : p->numSimSteps; - int start = p->isMultyPart ? 1 : step; - for (int i = start; i <= step; i++) - { - GlsimToolStep1(); - p->render(i); - GlsimToolStep2(); - p->render(i); +void MillSimulation::RemoveTool(int toolId) +{ + EndMill* tool; + if ((tool = GetTool(toolId)) != nullptr) { + auto it = std::find(mToolTable.begin(), mToolTable.end(), tool); + if (it != mToolTable.end()) { + mToolTable.erase(it); } + delete tool; } +} - void MillSimulation::renderSegmentReversed(int iSeg) - { - MillSim::MillPathSegment* p = MillPathSegments.at(iSeg); - int step = iSeg == mPathStep ? mSubStep : p->numSimSteps; - int end = p->isMultyPart ? 1 : step; - for (int i = step; i >= end; i--) - { - GlsimToolStep1(); - p->render(i); - GlsimToolStep2(); - p->render(i); - } - } - void MillSimulation::CalcSegmentPositions() - { - mSubStep = mCurStep; - for (mPathStep = 0; mPathStep < mNPathSteps; mPathStep++) - { - MillSim::MillPathSegment* p = MillPathSegments[mPathStep]; - if (mSubStep < p->numSimSteps) - break; - mSubStep -= p->numSimSteps; - } - if (mPathStep >= mNPathSteps) - { - mPathStep = mNPathSteps - 1; - mSubStep = MillPathSegments[mPathStep]->numSimSteps; - } - else - mSubStep++; - } +void MillSimulation::AddTool(EndMill* tool) +{ + // if we have another tool with same id, remove it + RemoveTool(tool->toolId); + mToolTable.push_back(tool); +} - void MillSimulation::Render() - { - mat4x4 matLookAt, model; - mat4x4_identity(model); - mat4x4_look_at(matLookAt, eye, target, upvec); +void MillSimulation::AddTool(const std::vector& toolProfile, int toolid, float diameter) +{ + // if we have another tool with same id, remove it + RemoveTool(toolid); + EndMill* tool = new EndMill(toolProfile, toolid, diameter); + mToolTable.push_back(tool); +} - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); +void MillSimulation::GlsimStart() +{ + glDisable(GL_BLEND); + glEnable(GL_STENCIL_TEST); + glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); +} - mat4x4_translate_in_place(matLookAt, mEyeX * mEyeXZFactor, 0, mEyeZ * mEyeXZFactor); - mat4x4_rotate_X(matLookAt, matLookAt, mEyeInclination); - mat4x4_rotate_Z(matLookAt, matLookAt, mEyeRoration); - mat4x4_translate_in_place(matLookAt, -mStockObject.center[0], -mStockObject.center[1], -mStockObject.center[2]); +void MillSimulation::GlsimToolStep1(void) +{ + glCullFace(GL_BACK); + glDepthFunc(GL_LESS); + glStencilFunc(GL_ALWAYS, 1, 0xFF); + glStencilOp(GL_ZERO, GL_ZERO, GL_REPLACE); + glDepthMask(GL_FALSE); +} - shaderFlat.Activate(); - shaderFlat.UpdateViewMat(matLookAt); - GlsimStart(); - mStockObject.render(); +void MillSimulation::GlsimToolStep2(void) +{ + glStencilFunc(GL_EQUAL, 1, 0xFF); + glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); + glDepthFunc(GL_GREATER); + glCullFace(GL_FRONT); + glDepthMask(GL_TRUE); +} +void MillSimulation::GlsimClipBack(void) +{ + glStencilFunc(GL_ALWAYS, 1, 0xFF); + glStencilOp(GL_REPLACE, GL_REPLACE, GL_ZERO); + glDepthFunc(GL_LESS); + glCullFace(GL_FRONT); + glDepthMask(GL_FALSE); +} + + +void MillSimulation::GlsimRenderStock(void) +{ + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + glEnable(GL_STENCIL_TEST); + glStencilFunc(GL_EQUAL, 1, 0xFF); + glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); + glDepthFunc(GL_EQUAL); + glCullFace(GL_BACK); +} + +void MillSimulation::GlsimRenderTools(void) +{ + glCullFace(GL_FRONT); +} + +void MillSimulation::GlsimEnd(void) +{ + glCullFace(GL_BACK); + glStencilFunc(GL_ALWAYS, 1, 0xFF); + glDisable(GL_STENCIL_TEST); + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + glDepthMask(GL_TRUE); + glDepthFunc(GL_LESS); +} + +void MillSimulation::renderSegmentForward(int iSeg) +{ + MillSim::MillPathSegment* p = MillPathSegments.at(iSeg); + int step = iSeg == mPathStep ? mSubStep : p->numSimSteps; + int start = p->isMultyPart ? 1 : step; + for (int i = start; i <= step; i++) { + GlsimToolStep1(); + p->render(i); GlsimToolStep2(); - - for (int i = 0; i <= mPathStep; i++) - renderSegmentForward(i); - - for (int i = mPathStep; i >= 0; i--) - renderSegmentForward(i); - - for (int i = 0; i < mPathStep; i++) - renderSegmentReversed(i); - - for (int i = mPathStep; i >= 0; i--) - renderSegmentReversed(i); - - GlsimClipBack(); - mStockObject.render(); - - // start coloring - shader3D.Activate(); - shader3D.UpdateViewMat(matLookAt); - shader3D.UpdateObjColor(stockColor); - GlsimRenderStock(); - mStockObject.render(); - GlsimRenderTools(); - - // render cuts (back faces of tools) - - shaderInv3D.Activate(); - shaderInv3D.UpdateViewMat(matLookAt); - shaderInv3D.UpdateObjColor(cutColor); - for (int i = 0; i <= mPathStep; i++) - { - MillSim::MillPathSegment* p = MillPathSegments.at(i); - int step = (i == mPathStep) ? mSubStep : p->numSimSteps; - int start = p->isMultyPart ? 1 : step; - for (int j = start; j <= step; j++) - MillPathSegments.at(i)->render(j); - } - - GlsimEnd(); - - glEnable(GL_CULL_FACE); - - if (mPathStep >= 0) - { - vec3 toolPos; - MotionPosToVec(toolPos, &mDestMotion); - MillSim::MillPathSegment* p = MillPathSegments.at(mPathStep); - p->GetHeadPosition(toolPos); - mat4x4 tmat; - mat4x4_translate(tmat, toolPos[0], toolPos[1], toolPos[2]); - //mat4x4_translate(tmat, toolPos.x, toolPos.y, toolPos.z); - shader3D.Activate(); - shader3D.UpdateObjColor(toolColor); - p->endmill->toolShape.Render(tmat, identityMat); - } - - shaderFlat.Activate(); - shaderFlat.UpdateObjColor(lightColor); - mlightObject.render(); - - if (mDebug > 0) - { - mat4x4 test; - mat4x4_dup(test, model); - mat4x4_translate_in_place(test, 20, 20, 3); - mat4x4_rotate_Z(test, test, 30.f * 3.14f / 180.f); - int dpos = mNPathSteps - mDebug2; - MillSim::MillPathSegment* p = MillPathSegments.at(dpos); - if (mDebug > p->numSimSteps) - mDebug = 1; - p->render(mDebug); - } - float progress = (float)mCurStep / mNTotalSteps; - guiDisplay.Render(progress); + p->render(i); } +} - void MillSimulation::ProcessSim(unsigned int time_ms) { - - static int ancient = 0; - static unsigned int last = 0; - static unsigned int msec = 0xFFFFFFFF; - static int fps = 0; - static int renderTime = 0; - - last = msec == 0xFFFFFFFF ? time_ms : msec; - msec = time_ms; - if (mIsRotate) { - mEyeRoration += (msec - last) / 4600.0f; - while (mEyeRoration >= PI2) - mEyeRoration -= PI2; - } - - if (last / 1000 != msec / 1000) { - float calcFps = 1000.0f * fps / (msec - ancient); - mFpsStream.str(""); - mFpsStream << "fps: " << calcFps << " rendertime:" << renderTime << " zpos:" << mDestMotion.z << std::ends; - ancient = msec; - fps = 0; - } - - if (mSimPlaying || mSingleStep) - { - SimNext(); - mSingleStep = false; - } - - Render(); - - ++fps; +void MillSimulation::renderSegmentReversed(int iSeg) +{ + MillSim::MillPathSegment* p = MillPathSegments.at(iSeg); + int step = iSeg == mPathStep ? mSubStep : p->numSimSteps; + int end = p->isMultyPart ? 1 : step; + for (int i = step; i >= end; i--) { + GlsimToolStep1(); + p->render(i); + GlsimToolStep2(); + p->render(i); } +} - void MillSimulation::HandleKeyPress(int key) - { - switch (key) { - case ' ': - mIsRotate = !mIsRotate; +void MillSimulation::CalcSegmentPositions() +{ + mSubStep = mCurStep; + for (mPathStep = 0; mPathStep < mNPathSteps; mPathStep++) { + MillSim::MillPathSegment* p = MillPathSegments[mPathStep]; + if (mSubStep < p->numSimSteps) { break; + } + mSubStep -= p->numSimSteps; + } + if (mPathStep >= mNPathSteps) { + mPathStep = mNPathSteps - 1; + mSubStep = MillPathSegments[mPathStep]->numSimSteps; + } + else { + mSubStep++; + } +} - case 'S': +void MillSimulation::RenderSimulation() +{ + if ((mViewItems & VIEWITEM_SIMULATION) == 0) { + return; + } + + simDisplay.StartDepthPass(); + + GlsimStart(); + mStockObject.render(); + + GlsimToolStep2(); + + for (int i = 0; i <= mPathStep; i++) { + renderSegmentForward(i); + } + + for (int i = mPathStep; i >= 0; i--) { + renderSegmentForward(i); + } + + for (int i = 0; i < mPathStep; i++) { + renderSegmentReversed(i); + } + + for (int i = mPathStep; i >= 0; i--) { + renderSegmentReversed(i); + } + + GlsimClipBack(); + mStockObject.render(); + + // start coloring + simDisplay.StartGeometryPass(stockColor, false); + GlsimRenderStock(); + mStockObject.render(); + + // render cuts (back faces of tools) + simDisplay.StartGeometryPass(cutColor, true); + GlsimRenderTools(); + for (int i = 0; i <= mPathStep; i++) { + MillSim::MillPathSegment* p = MillPathSegments.at(i); + int step = (i == mPathStep) ? mSubStep : p->numSimSteps; + int start = p->isMultyPart ? 1 : step; + for (int j = start; j <= step; j++) { + MillPathSegments.at(i)->render(j); + } + } + + GlsimEnd(); +} + +void MillSimulation::RenderTool() +{ + if (mPathStep < 0) { + return; + } + + vec3 toolPos; + MotionPosToVec(toolPos, &mDestMotion); + MillSim::MillPathSegment* p = MillPathSegments.at(mPathStep); + p->GetHeadPosition(toolPos); + mat4x4 tmat; + mat4x4_translate(tmat, toolPos[0], toolPos[1], toolPos[2]); + // mat4x4_translate(tmat, toolPos.x, toolPos.y, toolPos.z); + simDisplay.StartGeometryPass(toolColor, false); + p->endmill->toolShape.Render(tmat, identityMat); +} + +void MillSimulation::RenderPath() +{ + if (!guiDisplay.IsChecked(eGuiItemPath)) { + return; + } + simDisplay.SetupLinePathPass(mPathStep, false); + millPathLine.Render(); + simDisplay.SetupLinePathPass(mPathStep, true); + millPathLine.Render(); + glDepthMask(GL_TRUE); +} + +void MillSimulation::RenderBaseShape() +{ + if ((mViewItems & VIEWITEM_BASE_SHAPE) == 0) { + return; + } + simDisplay.StartDepthPass(); + glPolygonOffset(0, -2); + glEnable(GL_POLYGON_OFFSET_FILL); + simDisplay.StartGeometryPass(baseShapeColor, false); + mBaseShape.render(); + glDisable(GL_POLYGON_OFFSET_FILL); +} + +void MillSimulation::Render() +{ + // set background + glClearColor(bgndColor[0], bgndColor[1], bgndColor[2], 1.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); + simDisplay.PrepareDisplay(mStockObject.center); + + // render the simulation offscreen in an FBO + if (simDisplay.updateDisplay) { + simDisplay.PrepareFrameBuffer(); + RenderSimulation(); + RenderTool(); + RenderBaseShape(); + RenderPath(); + simDisplay.updateDisplay = false; + } + + simDisplay.RenderResult(); + + /* if (mDebug > 0) { + mat4x4 test; + mat4x4_identity(test); + mat4x4_translate_in_place(test, 20, 20, 3); + mat4x4_rotate_Z(test, test, 30.f * 3.14f / 180.f); + int dpos = mNPathSteps - mDebug2; + MillSim::MillPathSegment* p = MillPathSegments.at(dpos); + if (mDebug > p->numSimSteps) { + mDebug = 1; + } + p->render(mDebug); + }*/ + + float progress = (float)mCurStep / mNTotalSteps; + glBindFramebuffer(GL_FRAMEBUFFER, 0); + guiDisplay.Render(progress); +} + +void MillSimulation::ProcessSim(unsigned int time_ms) +{ + + static int ancient = 0; + static unsigned int last = 0; + static unsigned int msec = 0xFFFFFFFF; + static int fps = 0; + static int renderTime = 0; + + last = msec == 0xFFFFFFFF ? time_ms : msec; + msec = time_ms; + if (guiDisplay.IsChecked(eGuiItemRotate)) { + simDisplay.RotateEye((msec - last) / 4600.0f); + } + + if (last / 1000 != msec / 1000) { + float calcFps = 1000.0f * fps / (msec - ancient); + mFpsStream.str(""); + mFpsStream << "fps: " << calcFps << " rendertime:" << renderTime + << " zpos:" << mDestMotion.z << std::ends; + ancient = msec; + fps = 0; + } + + if (mSimPlaying || mSingleStep) { + SimNext(); + mSingleStep = false; + } + + Render(); + + ++fps; +} + +void MillSimulation::HandleKeyPress(int key) +{ + if (key >= '1' && key <= '9') { + mSimSpeed = key - '0'; + } + else if (key == 'D') { + mDebug++; + } + else if (key == 'K') { + mDebug2++; + gDebug = mNPathSteps - mDebug2; + } + else { + guiDisplay.HandleKeyPress(key); + } +} + +void MillSimulation::HandleGuiAction(eGuiItems actionItem, bool checked) +{ + switch (actionItem) { + case eGuiItemPlay: mSimPlaying = true; break; - case 'P': + case eGuiItemPause: mSimPlaying = false; break; - case 'T': + case eGuiItemSingleStep: mSimPlaying = false; mSingleStep = true; break; - case'D': - mDebug++; - break; - - case'K': - mDebug2++; - gDebug = mNPathSteps - mDebug2; - break; - - case 'F': - if (mSimSpeed == 1) mSimSpeed = 10; - else if (mSimSpeed == 10) mSimSpeed = 40; - else mSimSpeed = 1; + case eGuiItemFaster: + if (mSimSpeed == 1) { + mSimSpeed = 10; + } + else if (mSimSpeed == 10) { + mSimSpeed = 40; + } + else { + mSimSpeed = 1; + } guiDisplay.UpdateSimSpeed(mSimSpeed); break; - default: - if (key >= '1' && key <= '9') - mSimSpeed = key - '0'; + case eGuiItemPath: + simDisplay.updateDisplay = true; break; - } - guiDisplay.UpdatePlayState(mSimPlaying); - } - void MillSimulation::UpdateEyeFactor(float factor) - { - mEyeDistFactor = factor; - mEyeXZFactor = factor * mMaxFar * 0.005f; - eye[1] = -factor * mMaxFar; - } + case eGuiItemAmbientOclusion: + simDisplay.applySSAO = checked; + simDisplay.updateDisplay = true; + break; - void MillSimulation::TiltEye(float tiltStep) - { - mEyeInclination += tiltStep; - if (mEyeInclination > PI / 2) - mEyeInclination = PI / 2; - else if (mEyeInclination < -PI / 2) - mEyeInclination = -PI / 2; - } - - void MillSimulation::RotateEye(float rotStep) - { - mEyeRoration += rotStep; - if (mEyeRoration > PI2) - mEyeRoration -= PI2; - else if (mEyeRoration < 0) - mEyeRoration += PI2; - } - - void MillSimulation::MoveEye(float x, float z) - { - mEyeX += x; - if (mEyeX > 100) mEyeX = 100; - else if (mEyeX < -100) mEyeX = -100; - mEyeZ += z; - if (mEyeZ > 100) mEyeZ = 100; - else if (mEyeZ < -100) mEyeZ = -100; - } - - void MillSimulation::UpdateProjection() - { - // Setup projection - mat4x4 projmat; - mat4x4_perspective(projmat, 0.7f, 4.0f / 3.0f, 1.0f, mMaxFar); - //mat4x4_perspective(projmat, 0.7f, 4.0f / 3.0f, 1, 100); - shader3D.Activate(); - shader3D.UpdateProjectionMat(projmat); - shaderInv3D.Activate(); - shaderInv3D.UpdateProjectionMat(projmat); - shaderFlat.Activate(); - shaderFlat.UpdateProjectionMat(projmat); - } - - void MillSimulation::InitDisplay(float quality) - { - // gray background - glClearColor(0.6f, 0.8f, 1.0f, 1.0f); - mEyeRoration = 0.0; - - // use shaders - // standard diffuse shader - shader3D.CompileShader((char*)VertShader3DNorm, (char*)FragShaderNorm); - shader3D.UpdateEnvColor(lightPos, lightColor, ambientCol); - - // invarted normal diffuse shader for inner mesh - shaderInv3D.CompileShader((char*)VertShader3DInvNorm, (char*)FragShaderNorm); - shaderInv3D.UpdateEnvColor(lightPos, lightColor, ambientCol); - - // null shader to calculate meshes only (simulation stage) - shaderFlat.CompileShader((char*)VertShader3DNorm, (char*)FragShaderFlat); - UpdateProjection(); - - // setup light object and generate tools - mlightObject.GenerateBoxStock(-0.5f, -0.5f, -0.5f, 1, 1, 1); - for (std::size_t i = 0; i < mToolTable.size(); i++) { - mToolTable[i]->GenerateDisplayLists(quality); - } - - // init gui elements - guiDisplay.InutGui(); - - } - - void MillSimulation::SetBoxStock(float x, float y, float z, float l, float w, float h) - { - mStockObject.GenerateBoxStock(x, y, z, l, w, h); - mMaxStockDim = fmaxf(w, l); - mMaxFar = mMaxStockDim * 4; - UpdateProjection(); - vec3_set(eye, 0, 0, 0); - UpdateEyeFactor(0.4f); - vec3_set(lightPos, x, y, h + mMaxStockDim / 3); - mlightObject.SetPosition(lightPos); - } - - void MillSimulation::MouseDrag(int buttons, int dx, int dy) - { - if (buttons == (MS_MOUSE_MID | MS_MOUSE_LEFT)) - { - TiltEye((float)dy / 100.0f); - RotateEye((float)dx / 100.0f); - } - else if (buttons == MS_MOUSE_MID) - { - MoveEye(dx, -dy); - } - guiDisplay.MouseDrag(buttons, dx, dy); - } - - void MillSimulation::MouseMove(int px, int py) - { - if (mMouseButtonState > 0) - { - int dx = px - mLastMouseX; - int dy = py - mLastMouseY; - if (dx != 0 || dy != 0) - { - MouseDrag(mMouseButtonState, dx, dy); - mLastMouseX = px; - mLastMouseY = py; + case eGuiItemView: + mViewItems++; + if (mViewItems >= VIEWITEM_MAX) { + mViewItems = VIEWITEM_SIMULATION; } - } - else - MouseHover(px, py); + simDisplay.updateDisplay = true; + break; + + default: + break; + + } + guiDisplay.UpdatePlayState(mSimPlaying); +} + + +void MillSimulation::InitDisplay(float quality) +{ + // generate tools + for (unsigned int i = 0; i < mToolTable.size(); i++) { + mToolTable[i]->GenerateDisplayLists(quality); } - void MillSimulation::MouseScroll(float dy) - { - float f = mEyeDistFactor; - f += 0.05f * dy; - if (f > 0.6f) f = 0.6f; - else if (f < 0.05f) f = 0.05f; - UpdateEyeFactor(f); + // init 3d display + simDisplay.InitGL(); + + // init gui elements + guiDisplay.InitGui(); +} + +void MillSimulation::SetBoxStock(float x, float y, float z, float l, float w, float h) +{ + mStockObject.GenerateBoxStock(x, y, z, l, w, h); + simDisplay.ScaleViewToStock(&mStockObject); +} + +void MillSimulation::SetArbitraryStock(std::vector& verts, std::vector& indices) +{ + mStockObject.GenerateSolid(verts, indices); + simDisplay.ScaleViewToStock(&mStockObject); +} + +void MillSimulation::SetBaseObject(std::vector& verts, std::vector& indices) +{ + mBaseShape.GenerateSolid(verts, indices); +} + +void MillSimulation::MouseDrag(int buttons, int dx, int dy) +{ + if (buttons == (MS_MOUSE_MID | MS_MOUSE_LEFT)) { + simDisplay.TiltEye((float)dy / 100.0f); + simDisplay.RotateEye((float)dx / 100.0f); } - - - void MillSimulation::MouseHover(int px, int py) - { - guiDisplay.MouseCursorPos(px, py); + else if (buttons == MS_MOUSE_MID) { + simDisplay.MoveEye(dx, -dy); } + guiDisplay.MouseDrag(buttons, dx, dy); +} - void MillSimulation::MousePress(int button, bool isPressed, int px, int py) - { - if (isPressed) - mMouseButtonState |= button; - else - mMouseButtonState &= ~button; - - if (mMouseButtonState > 0) - { +void MillSimulation::MouseMove(int px, int py) +{ + if (mMouseButtonState > 0) { + int dx = px - mLastMouseX; + int dy = py - mLastMouseY; + if (dx != 0 || dy != 0) { + MouseDrag(mMouseButtonState, dx, dy); mLastMouseX = px; mLastMouseY = py; } - guiDisplay.MousePressed(button, isPressed, mSimPlaying); } - - - bool MillSimulation::LoadGCodeFile(const char* fileName) - { - if (mCodeParser.Parse(fileName)) - { - std::cout << "GCode file loaded successfully" << std::endl; - return true; - } - return false; + else { + MouseHover(px, py); } - - bool MillSimulation::AddGcodeLine(const char* line) - { - return mCodeParser.AddLine(line); - } - - void MillSimulation::SetSimulationStage(float stage) - { - mCurStep = (int)((float)mNTotalSteps * stage); - CalcSegmentPositions(); - } - } + +void MillSimulation::MouseScroll(float dy) +{ + float f = simDisplay.GetEyeFactor(); + f += 0.05f * dy; + if (f > 0.6f) { + f = 0.6f; + } + else if (f < 0.05f) { + f = 0.05f; + } + simDisplay.UpdateEyeFactor(f); +} + + +void MillSimulation::MouseHover(int px, int py) +{ + guiDisplay.MouseCursorPos(px, py); +} + +void MillSimulation::MousePress(int button, bool isPressed, int px, int py) +{ + if (isPressed) { + mMouseButtonState |= button; + } + else { + mMouseButtonState &= ~button; + } + + if (mMouseButtonState > 0) { + mLastMouseX = px; + mLastMouseY = py; + } + guiDisplay.MousePressed(button, isPressed, mSimPlaying); +} + +void MillSimulation::UpdateWindowScale(int width, int height) +{ + if (width == gWindowSizeW && height == gWindowSizeH) { + return; + } + gWindowSizeW = width; + gWindowSizeH = height; + simDisplay.UpdateWindowScale(); + guiDisplay.UpdateWindowScale(); + simDisplay.updateDisplay = true; +} + + +bool MillSimulation::LoadGCodeFile(const char* fileName) +{ + if (mCodeParser.Parse(fileName)) { + std::cout << "GCode file loaded successfully" << std::endl; + return true; + } + return false; +} + +bool MillSimulation::AddGcodeLine(const char* line) +{ + return mCodeParser.AddLine(line); +} + +void MillSimulation::SetSimulationStage(float stage) +{ + int newStep = (int)((float)mNTotalSteps * stage); + if (newStep == mCurStep) { + return; + } + mCurStep = newStep; + simDisplay.updateDisplay = true; + mSingleStep = true; + CalcSegmentPositions(); +} + +} // namespace MillSim diff --git a/src/Mod/CAM/PathSimulator/AppGL/MillSimulation.h b/src/Mod/CAM/PathSimulator/AppGL/MillSimulation.h index 765ea8add9..42c9df4f62 100644 --- a/src/Mod/CAM/PathSimulator/AppGL/MillSimulation.h +++ b/src/Mod/CAM/PathSimulator/AppGL/MillSimulation.h @@ -30,10 +30,17 @@ #include "GlUtils.h" #include "StockObject.h" #include "MillPathSegment.h" +#include "SimDisplay.h" #include "GuiDisplay.h" +#include "MillPathLine.h" +#include "SolidObject.h" #include #include +#define VIEWITEM_SIMULATION 1 +#define VIEWITEM_BASE_SHAPE 2 +#define VIEWITEM_MAX 4 + namespace MillSim { @@ -41,6 +48,7 @@ class MillSimulation { public: MillSimulation(); + ~MillSimulation(); void ClearMillPathSegments(); void Clear(); void SimNext(); @@ -51,23 +59,27 @@ public: { return GetTool(toolid) != nullptr; } + void RenderSimulation(); + void RenderTool(); + void RenderPath(); + void RenderBaseShape(); void Render(); void ProcessSim(unsigned int time_ms); void HandleKeyPress(int key); - void UpdateEyeFactor(float factor); - void TiltEye(float tiltStep); - void RotateEye(float rotStep); - void MoveEye(float x, float y); - void UpdateProjection(); + void HandleGuiAction(eGuiItems actionItem, bool checked); bool LoadGCodeFile(const char* fileName); bool AddGcodeLine(const char* line); void SetSimulationStage(float stage); void SetBoxStock(float x, float y, float z, float l, float w, float h); + void SetArbitraryStock(std::vector& verts, std::vector& indices); + void SetBaseObject(std::vector& verts, std::vector& indices); void MouseDrag(int buttons, int dx, int dy); void MouseMove(int px, int py); void MouseScroll(float dy); void MouseHover(int px, int py); void MousePress(int button, bool isPressed, int px, int py); + void UpdateWindowScale(int width, int height); + protected: @@ -88,44 +100,25 @@ protected: protected: std::vector mToolTable; - Shader shader3D, shaderInv3D, shaderFlat; GCodeParser mCodeParser; GuiDisplay guiDisplay; + SimDisplay simDisplay; + MillPathLine millPathLine; std::vector MillPathSegments; std::ostringstream mFpsStream; - MillMotion mZeroPos = {eNop, -1, 0.0F, 0.0F, 100.0F, 0.0F, 0.0F, 0.0F, 0.0F}; - MillMotion mCurMotion = {eNop, -1, 0.0F, 0.0F, 0.0F, 0.0F, 0.0F, 0.0F, 0.0F}; - MillMotion mDestMotion = {eNop, -1, 0.0F, 0.0F, 0.0F, 0.0F, 0.0F, 0.0F, 0.0F}; + MillMotion mZeroPos = {eNop, -1, 0, 0, 100, 0, 0, 0, 0}; + MillMotion mCurMotion = {eNop, -1, 0, 0, 0, 0, 0, 0, 0}; + MillMotion mDestMotion = {eNop, -1, 0, 0, 0, 0, 0, 0, 0}; StockObject mStockObject; - StockObject mlightObject; - - vec3 lightColor = {0.8f, 0.9f, 1.0f}; - vec3 lightPos = {20.0f, 20.0f, 10.0f}; - vec3 ambientCol = {0.3f, 0.3f, 0.5f}; - - vec3 eye = {0, 100, 40}; - vec3 target = {0, 0, -10}; - vec3 upvec = {0, 0, 1}; - - vec3 stockColor = {0.7f, 0.7f, 0.7f}; - vec3 cutColor = {0.4f, 0.7f, 0.4f}; - vec3 toolColor = {0.4f, 0.4f, 0.7f}; - - float mEyeDistance = 30; - float mEyeRoration = 0; - float mEyeInclination = PI / 6; // 30 degree - float mEyeStep = PI / 36; // 5 degree - - float mMaxStockDim = 100; - float mMaxFar = 100; - float mEyeDistFactor = 0.4f; - float mEyeXZFactor = 0.01f; - float mEyeXZScale = 0; - float mEyeX = 0.0f; - float mEyeZ = 0.0f; + SolidObject mBaseShape; + vec3 bgndColor = {0.1f, 0.2f, 0.3f}; + vec3 stockColor = {0.7f, 0.75f, 0.9f}; + vec3 cutColor = {0.85f, 0.95f, 0.85f}; + vec3 toolColor = {0.5f, 0.4f, 0.3f}; + vec3 baseShapeColor = {0.7f, 0.6f, 0.5f}; int mCurStep = 0; int mNTotalSteps = 0; @@ -136,14 +129,15 @@ protected: int mDebug1 = 0; int mDebug2 = 12; int mSimSpeed = 1; + int mViewItems = VIEWITEM_SIMULATION; int mLastMouseX = 0, mLastMouseY = 0; int mMouseButtonState = 0; bool mIsInStock = false; - bool mIsRotate = true; bool mSimPlaying = false; bool mSingleStep = false; + }; } // namespace MillSim #endif diff --git a/src/Mod/CAM/PathSimulator/AppGL/OpenGlWrapper.h b/src/Mod/CAM/PathSimulator/AppGL/OpenGlWrapper.h index 92ad6483de..415036ec24 100644 --- a/src/Mod/CAM/PathSimulator/AppGL/OpenGlWrapper.h +++ b/src/Mod/CAM/PathSimulator/AppGL/OpenGlWrapper.h @@ -22,9 +22,7 @@ #ifndef __openglwrapper_h__ #define __openglwrapper_h__ -#ifdef CAM_SIM_USE_GLEW -#include "GL/glew.h" -#else + #include "DlgCAMSimulator.h" extern QOpenGLContext* gOpenGlContext; #define gSimWindow CAMSimulator::DlgCAMSimulator::GetInstance() @@ -40,6 +38,8 @@ extern QOpenGLContext* gOpenGlContext; #define glVertexAttribPointer gSimWindow->glVertexAttribPointer #define glShaderSource gSimWindow->glShaderSource #define glCompileShader gSimWindow->glCompileShader +#define glDeleteShader gSimWindow->glDeleteShader +#define glDeleteProgram gSimWindow->glDeleteProgram #define glAttachShader gSimWindow->glAttachShader #define glLinkProgram gSimWindow->glLinkProgram #define glGetProgramiv gSimWindow->glGetProgramiv @@ -69,6 +69,21 @@ extern QOpenGLContext* gOpenGlContext; #define glTexParameteri gSimWindow->glTexParameteri #define glTexImage2D gSimWindow->glTexImage2D #define glDeleteTextures gSimWindow->glDeleteTextures -#endif // HAVE_OPENGL_EXT +#define glPolygonOffset gSimWindow->glPolygonOffset +#define glBindFramebuffer gSimWindow->glBindFramebuffer +#define glUniform1f gSimWindow->glUniform1f +#define glGenFramebuffers gSimWindow->glGenFramebuffers +#define glFramebufferTexture2D gSimWindow->glFramebufferTexture2D +#define glDrawBuffers gSimWindow->glDrawBuffers +#define glGenRenderbuffers gSimWindow->glGenRenderbuffers +#define glBindRenderbuffer gSimWindow->glBindRenderbuffer +#define glRenderbufferStorage gSimWindow->glRenderbufferStorage +#define glFramebufferRenderbuffer gSimWindow->glFramebufferRenderbuffer +#define glCheckFramebufferStatus gSimWindow->glCheckFramebufferStatus +#define glDeleteFramebuffers gSimWindow->glDeleteFramebuffers +#define glDeleteRenderbuffers gSimWindow->glDeleteRenderbuffers +#define glVertexAttribIPointer gSimWindow->glVertexAttribIPointer +#define glUniform4fv gSimWindow->glUniform4fv +#define glLineWidth gSimWindow->glLineWidth #endif // !__openglwrapper_h__ diff --git a/src/Mod/CAM/PathSimulator/AppGL/Shader.cpp b/src/Mod/CAM/PathSimulator/AppGL/Shader.cpp index ddaa5acc07..c03d6449f0 100644 --- a/src/Mod/CAM/PathSimulator/AppGL/Shader.cpp +++ b/src/Mod/CAM/PathSimulator/AppGL/Shader.cpp @@ -29,6 +29,11 @@ namespace MillSim Shader* CurrentShader = nullptr; +Shader::~Shader() +{ + Destroy(); +} + void Shader::UpdateModelMat(mat4x4 tmat, mat4x4 nmat) { if (mModelPos >= 0) { @@ -49,13 +54,11 @@ void Shader::UpdateProjectionMat(mat4x4 mat) void Shader::UpdateViewMat(mat4x4 mat) { if (mViewPos >= 0) { - if (mViewPos >= 0) { - glUniformMatrix4fv(mViewPos, 1, GL_FALSE, (GLfloat*)mat); - } + glUniformMatrix4fv(mViewPos, 1, GL_FALSE, (GLfloat*)mat); } } -void Shader::UpdateEnvColor(vec3 lightPos, vec3 lightColor, vec3 ambient) +void Shader::UpdateEnvColor(vec3 lightPos, vec3 lightColor, vec3 ambient, float linearity) { if (mLightPosPos >= 0) { glUniform3fv(mLightPosPos, 1, lightPos); @@ -63,8 +66,11 @@ void Shader::UpdateEnvColor(vec3 lightPos, vec3 lightColor, vec3 ambient) if (mLightColorPos >= 0) { glUniform3fv(mLightColorPos, 1, lightColor); } - if (mAmbientPos >= 0) { - glUniform3fv(mAmbientPos, 1, ambient); + if (mLightAmbientPos >= 0) { + glUniform3fv(mLightAmbientPos, 1, ambient); + } + if (mLightLinearPos >= 0) { + glUniform1f(mLightLinearPos, linearity); } } @@ -75,6 +81,20 @@ void Shader::UpdateObjColor(vec3 objColor) } } +void Shader::UpdateObjColorAlpha(vec4 objColor) +{ + if (mObjectColorAlphaPos >= 0) { + glUniform4fv(mObjectColorAlphaPos, 1, objColor); + } +} + +void Shader::UpdateNormalState(bool isInverted) +{ + if (mInvertedNormalsPos >= 0) { + glUniform1i(mInvertedNormalsPos, isInverted); + } +} + void Shader::UpdateTextureSlot(int slot) { if (mTexSlotPos >= 0) { @@ -82,12 +102,61 @@ void Shader::UpdateTextureSlot(int slot) } } -bool CheckCompileResult(int shader) +void Shader::UpdateAlbedoTexSlot(int albedoSlot) { + if (mAlbedoPos >= 0) { + glUniform1i(mAlbedoPos, albedoSlot); + } +} + +void Shader::UpdatePositionTexSlot(int positionSlot) +{ + if (mPositionPos >= 0) { + glUniform1i(mPositionPos, positionSlot); + } +} + +void Shader::UpdateNormalTexSlot(int normalSlot) +{ + if (mNormalPos >= 0) { + glUniform1i(mNormalPos, normalSlot); + } +} + +void Shader::UpdateNoiseTexSlot(int noiseSlot) +{ + if (mNoisePos >= 0) { + glUniform1i(mAlbedoPos, noiseSlot); + } +} + +void Shader::UpdateSsaoTexSlot(int ssaoSlot) +{ + if (mSsaoPos >= 0) { + glUniform1i(mSsaoPos, ssaoSlot); + } +} + +void Shader::UpdateKernelVals(int nVals, float *vals) +{ + glUniform3fv(mSamplesPos, nVals, vals); +} + +void Shader::UpdateCurSegment(int curSeg) +{ + if (mCurSegmentPos >= 0) { + glUniform1i(mCurSegmentPos, curSeg); + } +} + + #ifdef QT_OPENGL_LIB - (void)shader; +bool CheckCompileResult(int /* shader */) +{ return false; #else +bool CheckCompileResult(int shader) +{ char log[1024]; int res = 0; GLsizei len; @@ -105,7 +174,7 @@ bool CheckCompileResult(int shader) #endif } -unsigned int Shader::CompileShader(char* _vertShader, char* _fragShader) +unsigned int Shader::CompileShader(const char* _vertShader, const char* _fragShader) { vertShader = _vertShader; fragShader = _fragShader; @@ -114,6 +183,7 @@ unsigned int Shader::CompileShader(char* _vertShader, char* _fragShader) glShaderSource(vertex_shader, 1, &vertShader, NULL); glCompileShader(vertex_shader); if (CheckCompileResult(vertex_shader)) { + glDeleteShader(vertex_shader); return 0xdeadbeef; } @@ -121,6 +191,8 @@ unsigned int Shader::CompileShader(char* _vertShader, char* _fragShader) glShaderSource(fragment_shader, 1, &fragShader, NULL); glCompileShader(fragment_shader); if (CheckCompileResult(fragment_shader)) { + glDeleteShader(fragment_shader); + glDeleteShader(vertex_shader); return 0xdeadbeef; } @@ -131,6 +203,7 @@ unsigned int Shader::CompileShader(char* _vertShader, char* _fragShader) glGetProgramiv(shaderId, GL_LINK_STATUS, &res); if (res == 0) { + Destroy(); return 0xdeadbeef; } @@ -141,9 +214,21 @@ unsigned int Shader::CompileShader(char* _vertShader, char* _fragShader) mViewPos = glGetUniformLocation(shaderId, "view"); mLightPosPos = glGetUniformLocation(shaderId, "lightPos"); mLightColorPos = glGetUniformLocation(shaderId, "lightColor"); - mAmbientPos = glGetUniformLocation(shaderId, "ambient"); + mLightLinearPos = glGetUniformLocation(shaderId, "lightLinear"); + mLightAmbientPos = glGetUniformLocation(shaderId, "lightAmbient"); mObjectColorPos = glGetUniformLocation(shaderId, "objectColor"); + mObjectColorAlphaPos = glGetUniformLocation(shaderId, "objectColorAlpha"); mTexSlotPos = glGetUniformLocation(shaderId, "texSlot"); + mInvertedNormalsPos = glGetUniformLocation(shaderId, "invertedNormals"); + mSsaoSamplesPos = glGetUniformLocation(shaderId, "ssaoSamples"); + mAlbedoPos = glGetUniformLocation(shaderId, "texAlbedo"); + mPositionPos = glGetUniformLocation(shaderId, "texPosition"); + mNormalPos = glGetUniformLocation(shaderId, "texNormal"); + mSsaoPos = glGetUniformLocation(shaderId, "texSsao"); + mNoisePos = glGetUniformLocation(shaderId, "texNoise"); + mSamplesPos = glGetUniformLocation(shaderId, "ssaoSamples"); + mCurSegmentPos = glGetUniformLocation(shaderId, "curSegment"); + Activate(); return shaderId; } @@ -156,118 +241,418 @@ void Shader::Activate() CurrentShader = this; } - -const char* VertShader3DNorm = - "#version 330 core \n" - - "layout(location = 0) in vec3 aPosition; \n" - "layout(location = 1) in vec3 aNormal; \n" - - "out vec3 Normal; \n" - "out vec3 FragPos; \n" - - "uniform mat4 model; \n" - "uniform mat4 view; \n" - "uniform mat4 projection; \n" - "uniform mat4 normalRot; \n" - - "void main(void) \n" - "{ \n" - " gl_Position = projection * view * model * vec4(aPosition, 1.0); \n" - " FragPos = vec3(model * vec4(aPosition, 1.0)); \n" - " Normal = vec3(normalRot * vec4(aNormal, 1.0)); \n" - "} \n"; - -const char* VertShader3DInvNorm = - "#version 330 core \n" - - "layout(location = 0) in vec3 aPosition; \n" - "layout(location = 1) in vec3 aNormal; \n" - - "out vec3 Normal; \n" - "out vec3 FragPos; \n" - - "uniform mat4 model; \n" - "uniform mat4 view; \n" - "uniform mat4 projection; \n" - "uniform mat4 normalRot; \n" - - "void main(void) \n" - "{ \n" - " gl_Position = projection * view * model * vec4(aPosition, 1.0); \n" - " FragPos = vec3(model * vec4(aPosition, 1.0)); \n" - " Normal = -vec3(normalRot * vec4(aNormal, 1.0)); \n" - "} \n"; +void Shader::Destroy() +{ + if (shaderId == 0) { + return; + } + glDeleteProgram(shaderId); + shaderId = 0; +} -const char* VertShader2DTex = - "#version 330 core \n" // -----> add long remark for a uniform auto formatting +const char* VertShader3DNorm = R"( + #version 330 core - "layout(location = 0) in vec2 aPosition; \n" - "layout(location = 1) in vec2 aTexCoord; \n" + layout(location = 0) in vec3 aPosition; + layout(location = 1) in vec3 aNormal; - "out vec2 texCoord; \n" + out vec3 Normal; + out vec3 FragPos; - "uniform mat4 projection; \n" - "uniform mat4 model; \n" + uniform mat4 model; + uniform mat4 view; + uniform mat4 projection; + uniform mat4 normalRot; - "void main(void) \n" - "{ \n" - " gl_Position = projection * model * vec4(aPosition, 0.0, 1.0); \n" - " texCoord = aTexCoord; \n" - "} \n"; + void main(void) + { + vec4 viewPos = view * model * vec4(aPosition, 1.0); + FragPos = vec3(model * vec4(aPosition, 1.0)); + Normal = vec3(normalRot * vec4(aNormal, 1.0)); + gl_Position = projection * viewPos; + } +)"; -const char* FragShader2dTex = - "#version 330\n" // -----> add long remark for a uniform auto formatting +const char* VertShader3DInvNorm = R"( + #version 330 core - "out vec4 FragColor; \n" - "in vec2 texCoord; \n" + layout(location = 0) in vec3 aPosition; + layout(location = 1) in vec3 aNormal; - "uniform vec3 objectColor; \n" - "uniform sampler2D texSlot; \n" + out vec3 Normal; + out vec3 FragPos; - "void main() \n" - "{ \n" - " vec4 texColor = texture(texSlot, texCoord); \n" - " FragColor = vec4(objectColor, 1.0) * texColor; \n" - "} \n"; + uniform mat4 model; + uniform mat4 view; + uniform mat4 projection; + uniform mat4 normalRot; + + void main(void) + { + gl_Position = projection * view * model * vec4(aPosition, 1.0); + FragPos = vec3(model * vec4(aPosition, 1.0)); + Normal = -vec3(normalRot * vec4(aNormal, 1.0)); + } +)"; -const char* FragShaderNorm = - "#version 330\n" // -----> add long remark for a uniform auto formatting +const char* VertShader2DTex = R"( + #version 330 core - "out vec4 FragColor; \n" + layout(location = 0) in vec2 aPosition; + layout(location = 1) in vec2 aTexCoord; - "in vec3 Normal; \n" - "in vec3 FragPos; \n" + out vec2 texCoord; - "uniform vec3 lightPos; \n" - "uniform vec3 lightColor; \n" - "uniform vec3 objectColor; \n" - "uniform vec3 ambient; \n" + uniform mat4 projection; + uniform mat4 model; - "void main() \n" - "{ \n" - " vec3 norm = normalize(Normal); \n" - " vec3 lightDir = normalize(lightPos - FragPos); \n" - " float diff = max(dot(norm, lightDir), 0.0); \n" - " vec3 diffuse = diff * lightColor; \n" - " vec3 result = (ambient + diffuse) * objectColor; \n" - " FragColor = vec4(result, 1.0); \n" - "} \n"; + void main(void) + { + gl_Position = projection * model * vec4(aPosition, 0.0, 1.0); + texCoord = aTexCoord; + } +)"; -const char* FragShaderFlat = - "#version 330\n" // -----> add long remark for a uniform auto formatting +const char* FragShader2dTex = R"( + #version 330 - "out vec4 FragColor; \n" + out vec4 FragColor; + in vec2 texCoord; - "in vec3 Normal; \n" - "in vec3 FragPos; \n" - "uniform vec3 objectColor; \n" + uniform vec3 objectColor; + uniform sampler2D texSlot; - "void main() \n" - "{ \n" - " FragColor = vec4(objectColor, 1.0); \n" - "} \n"; + void main() + { + vec4 texColor = texture(texSlot, texCoord); + FragColor = vec4(objectColor, 1.0) * texColor; + } +)"; + + +const char* FragShaderNorm = R"( + #version 330 + + out vec4 FragColor; + + in vec3 Normal; + in vec3 FragPos; + + uniform vec3 lightPos; + uniform vec3 lightColor; + uniform vec3 objectColor; + uniform vec3 lightAmbient; + + void main() + { + vec3 norm = normalize(Normal); + vec3 lightDir = normalize(lightPos - FragPos); + float diff = max(dot(norm, lightDir), 0.0); + vec3 diffuse = diff * lightColor; + vec3 result = (lightAmbient + diffuse) * objectColor; + FragColor = vec4(result, 1.0); + } +)"; + +const char* FragShaderFlat = R"( + #version 330 + + out vec4 FragColor; + + in vec3 Normal; + in vec3 FragPos; + uniform vec3 objectColor; + + void main() + { + FragColor = vec4(objectColor, 1.0); + } +)"; + + + const char* VertShader2DFbo = R"( + #version 330 core + + layout(location = 0) in vec2 aPosition; + layout(location = 1) in vec2 aTexCoord; + + out vec2 texCoord; + + void main(void) + { + gl_Position = vec4(aPosition.x, aPosition.y, 0.0, 1.0); + texCoord = aTexCoord; + } +)"; + + const char* FragShader2dFbo = R"( + #version 330 + + out vec4 FragColor; + in vec2 texCoord; + + uniform sampler2D texSlot; + + void main() + { + vec4 tc = texture(texSlot, texCoord); + FragColor = tc; + } +)"; + +const char* VertShaderGeom = R"( + #version 330 core + layout (location = 0) in vec3 aPos; + layout (location = 1) in vec3 aNormal; + + out vec3 FragPos; + out vec3 Normal; + + uniform bool invertedNormals; + + uniform mat4 model; + uniform mat4 view; + uniform mat4 projection; + + void main() + { + vec4 viewPos = view * model * vec4(aPos, 1.0); + FragPos = viewPos.xyz; + + mat3 normalMatrix = transpose(inverse(mat3(view * model))); + Normal = normalMatrix * (invertedNormals ? -aNormal : aNormal); + + gl_Position = projection * viewPos; + } +)"; + +const char* FragShaderGeom = R"( + #version 330 core + layout (location = 0) out vec4 texAlbedo; + layout (location = 1) out vec3 texPosition; + layout (location = 2) out vec3 texNormal; + + in vec3 FragPos; + in vec3 Normal; + + uniform vec3 objectColor; + + void main() + { + // store the fragment position vector in the first gbuffer texture + texPosition = FragPos; + // also store the per-fragment normals into the gbuffer + texNormal = normalize(Normal); + // and the diffuse per-fragment color + texAlbedo = vec4(objectColor, 1.0f); + } +)"; + +const char* FragShaderSSAO = R"( + #version 330 core + out vec4 FragColor; + + in vec2 texCoord; + + uniform sampler2D texNoise; + uniform sampler2D texPosition; + uniform sampler2D texNormal; + + uniform vec3 ssaoSamples[64]; + + // parameters (you'd probably want to use them as uniforms to more easily tweak the effect) + int kernelSize = 64; + float radius = 2.5f; + float bias = 0.025; + + // tile noise texture over screen based on screen dimensions divided by noise size + const vec2 noiseScale = vec2(800.0/4.0, 600.0/4.0); + + uniform mat4 projection; + + void main() + { + // get input for SSAO algorithm + vec3 fragPos = texture(texPosition, texCoord).xyz; + vec3 normal = normalize(texture(texNormal, texCoord).rgb); + vec3 randomVec = normalize(texture(texNoise, texCoord * noiseScale).xyz); + // create TBN change-of-basis matrix: from tangent-space to view-space + vec3 tangent = normalize(randomVec - normal * dot(randomVec, normal)); + vec3 bitangent = cross(normal, tangent); + mat3 TBN = mat3(tangent, bitangent, normal); + // iterate over the sample kernel and calculate occlusion factor + float occlusion = 0.0; + for(int i = 0; i < kernelSize; ++i) + { + // get sample position + vec3 samplePos = TBN * ssaoSamples[i]; // from tangent to view-space + samplePos = fragPos + samplePos * radius; + + // project sample position (to sample texture) (to get position on screen/texture) + vec4 offset = vec4(samplePos, 1.0); + offset = projection * offset; // from view to clip-space + offset.xyz /= offset.w; // perspective divide + offset.xyz = offset.xyz * 0.5 + 0.5; // transform to range 0.0 - 1.0 + + // get sample depth + float sampleDepth = texture(texPosition, offset.xy).z; // get depth value of kernel + // sample + + // range check & accumulate + float rangeCheck = smoothstep(0.0, 1.0, radius * 0.1f / abs(sampleDepth - fragPos.z)); + occlusion += (sampleDepth >= samplePos.z + bias ? 1.0 : 0.0) * rangeCheck; + } + occlusion = 1.0 - (occlusion / kernelSize); + FragColor = vec4(pow(occlusion, 2), 0, 0, 1); + } +)"; + +const char* FragShaderSSAOLighting = R"( + #version 330 core + out vec4 FragColor; + + in vec2 texCoord; + + uniform sampler2D texSsao; + uniform sampler2D texAlbedo; + uniform sampler2D texPosition; + uniform sampler2D texNormal; + + uniform vec3 lightPos; + uniform vec3 lightColor; + uniform vec3 lightAmbient; + + uniform float lightLinear; + + void main() + { + // retrieve data from gbuffer + vec4 DiffuseA = texture(texAlbedo, texCoord); + vec3 Diffuse = DiffuseA.rgb; + vec3 FragPos = texture(texPosition, texCoord).rgb; + vec3 Normal = texture(texNormal, texCoord).rgb; + float AmbientOcclusion = texture(texSsao, texCoord).r; + + // then calculate lighting as usual + vec3 lighting = lightAmbient * Diffuse * AmbientOcclusion; + vec3 viewDir = normalize(-FragPos); // viewpos is (0.0.0) + // diffuse + vec3 lightDir = normalize(lightPos - FragPos); + vec3 diffuse = max(dot(Normal, lightDir), 0.0) * Diffuse * lightColor; + // specular + vec3 halfwayDir = normalize(lightDir + viewDir); + float spec = pow(max(dot(Normal, halfwayDir), 0.0), 16.0); + vec3 specular = lightColor * spec; + // attenuation + float distance = length(lightPos - FragPos); + float attenuation = 1.0 / (1.0 + lightLinear * distance); + lighting += (diffuse + specular * 0.3) * attenuation; + + FragColor = vec4(lighting, DiffuseA.a); + } +)"; + +const char* FragShaderStdLighting = R"( + #version 330 core + out vec4 FragColor; + + in vec2 texCoord; + + uniform sampler2D texAlbedo; + uniform sampler2D texPosition; + uniform sampler2D texNormal; + + uniform vec3 lightPos; + uniform vec3 lightColor; + uniform float lightLinear; + uniform vec3 lightAmbient; + + void main() + { + // retrieve data from gbuffer + vec4 DiffuseA = texture(texAlbedo, texCoord); + vec3 Diffuse = DiffuseA.rgb; + vec3 FragPos = texture(texPosition, texCoord).rgb; + vec3 Normal = texture(texNormal, texCoord).rgb; + + // then calculate lighting as usual + vec3 lighting = lightAmbient * Diffuse; + vec3 viewDir = normalize(-FragPos); // viewpos is (0.0.0) + // diffuse + vec3 lightDir = normalize(lightPos - FragPos); + vec3 diffuse = max(dot(Normal, lightDir), 0.0) * Diffuse * lightColor; + // specular + vec3 halfwayDir = normalize(lightDir + viewDir); + float spec = pow(max(dot(Normal, halfwayDir), 0.0), 16.0); + vec3 specular = lightColor * spec; + // attenuation + float distance = length(lightPos - FragPos); + float attenuation = 1.0 / (1.0 + lightLinear * distance); + lighting += (diffuse + specular * 0.3) * attenuation; + + FragColor = vec4(lighting, DiffuseA.a); + } +)"; + +const char* FragShaderSSAOBlur = R"( + #version 330 core + out vec4 FragColor; + + in vec2 texCoord; + + uniform sampler2D texSsao; + + void main() + { + vec2 texelSize = 1.0 / vec2(textureSize(texSsao, 0)); + float result = 0.0; + for (int x = -2; x <= 1; ++x) + { + for (int y = -2; y <= 1; ++y) + { + vec2 offset = vec2(float(x), float(y)) * texelSize; + result += texture(texSsao, texCoord + offset).r; + } + } + FragColor = vec4(result / (4.0 * 4.0), 0, 0, 1); + } +)"; + + +const char* VertShader3DLine = R"( + #version 330 core + + layout(location = 0) in vec3 aPosition; + layout(location = 1) in int aIndex; + flat out int Index; + + uniform mat4 view; + uniform mat4 projection; + + void main(void) + { + gl_Position = projection * view * vec4(aPosition, 1.0); + Index = aIndex; + } +)"; + +const char* FragShader3DLine = R"( + #version 330 + + out vec4 FragColor; + + flat in int Index; + uniform vec4 objectColorAlpha; + uniform vec3 objectColor; + uniform int curSegment; + + void main() + { + if (Index > curSegment) FragColor = objectColorAlpha; + else FragColor = vec4(objectColor, objectColorAlpha.a); + } +)"; } // namespace MillSim diff --git a/src/Mod/CAM/PathSimulator/AppGL/Shader.h b/src/Mod/CAM/PathSimulator/AppGL/Shader.h index 3b11ec3a5c..c808ee9dc1 100644 --- a/src/Mod/CAM/PathSimulator/AppGL/Shader.h +++ b/src/Mod/CAM/PathSimulator/AppGL/Shader.h @@ -33,17 +33,28 @@ class Shader public: Shader() {} + ~Shader(); public: unsigned int shaderId = 0; void UpdateModelMat(mat4x4 transformMat, mat4x4 normalMat); void UpdateProjectionMat(mat4x4 mat); void UpdateViewMat(mat4x4 mat); - void UpdateEnvColor(vec3 lightPos, vec3 lightColor, vec3 ambient); + void UpdateEnvColor(vec3 lightPos, vec3 lightColor, vec3 ambient, float linearity); void UpdateObjColor(vec3 objColor); + void UpdateObjColorAlpha(vec4 objColor); + void UpdateNormalState(bool isInverted); void UpdateTextureSlot(int slot); - unsigned int CompileShader(char* vertShader, char* fragShader); + void UpdateAlbedoTexSlot(int albedoSlot); + void UpdatePositionTexSlot(int positionSlot); + void UpdateNormalTexSlot(int normalSlot); + void UpdateNoiseTexSlot(int noiseSlot); + void UpdateSsaoTexSlot(int ssaoSlot); + void UpdateKernelVals(int nVals, float *vals); + void UpdateCurSegment(int curSeg); + unsigned int CompileShader(const char* vertShader, const char* fragShader); void Activate(); + void Destroy(); bool IsValid() { return shaderId > 0; @@ -57,9 +68,20 @@ protected: int mViewPos = -1; int mLightPosPos = -1; int mLightColorPos = -1; - int mAmbientPos = -1; + int mLightLinearPos = -1; + int mLightAmbientPos = -1; int mObjectColorPos = -1; + int mObjectColorAlphaPos = -1; int mTexSlotPos = -1; + int mInvertedNormalsPos = -1; + int mSsaoSamplesPos = -1; + int mAlbedoPos = -1; + int mPositionPos = -1; + int mNormalPos = -1; + int mSsaoPos = -1; + int mNoisePos = -1; + int mSamplesPos = -1; + int mCurSegmentPos = -1; const char* vertShader = nullptr; const char* fragShader = nullptr; @@ -73,5 +95,17 @@ extern const char* VertShader3DNorm; extern const char* VertShader3DInvNorm; extern const char* VertShader2DTex; extern const char* FragShader2dTex; +extern const char* VertShader2DFbo; +extern const char* FragShader2dFbo; +extern const char* VertShaderGeom; +extern const char* FragShaderGeom; +extern const char* FragShaderSSAO; +extern const char* FragShaderSSAOLighting; +extern const char* FragShaderStdLighting; +extern const char* FragShaderSSAOBlur; +extern const char* VertShader3DLine; +extern const char* FragShader3DLine; + + } // namespace MillSim #endif // !__shader_h__ diff --git a/src/Mod/CAM/PathSimulator/AppGL/SimDisplay.cpp b/src/Mod/CAM/PathSimulator/AppGL/SimDisplay.cpp new file mode 100644 index 0000000000..6940650032 --- /dev/null +++ b/src/Mod/CAM/PathSimulator/AppGL/SimDisplay.cpp @@ -0,0 +1,556 @@ +/*************************************************************************** + * Copyright (c) 2024 Shai Seger * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + +#include "SimDisplay.h" +#include "linmath.h" +#include "OpenGlWrapper.h" +#include + +#define GL_UBYTE GL_UNSIGNED_BYTE + + +namespace MillSim +{ + +void SimDisplay::InitShaders() +{ + // use shaders + // standard diffuse shader + shader3D.CompileShader(VertShader3DNorm, FragShaderNorm); + shader3D.UpdateEnvColor(lightPos, lightColor, ambientCol, 0.0f); + + // invarted normal diffuse shader for inner mesh + shaderInv3D.CompileShader(VertShader3DInvNorm, FragShaderNorm); + shaderInv3D.UpdateEnvColor(lightPos, lightColor, ambientCol, 0.0f); + + // null shader to calculate meshes only (simulation stage) + shaderFlat.CompileShader(VertShader3DNorm, FragShaderFlat); + + // texture shader to render Simulator FBO + shaderSimFbo.CompileShader(VertShader2DFbo, FragShader2dFbo); + shaderSimFbo.UpdateTextureSlot(0); + + // geometric shader - generate texture with all geometric info for further processing + shaderGeom.CompileShader(VertShaderGeom, FragShaderGeom); + shaderGeomCloser.CompileShader(VertShaderGeom, FragShaderGeom); + + // lighting shader - apply standard lighting based on geometric buffers + shaderLighting.CompileShader(VertShader2DFbo, FragShaderStdLighting); + shaderLighting.UpdateAlbedoTexSlot(0); + shaderLighting.UpdatePositionTexSlot(1); + shaderLighting.UpdateNormalTexSlot(2); + shaderLighting.UpdateEnvColor(lightPos, lightColor, ambientCol, 0.01f); + + // SSAO shader - generate SSAO info and embed in texture buffer + shaderSSAO.CompileShader(VertShader2DFbo, FragShaderSSAO); + shaderSSAO.UpdateNoiseTexSlot(0); + shaderSSAO.UpdatePositionTexSlot(1); + shaderSSAO.UpdateNormalTexSlot(2); + + // SSAO blur shader - smooth generated SSAO texture + shaderSSAOBlur.CompileShader(VertShader2DFbo, FragShaderSSAOBlur); + shaderSSAOBlur.UpdateSsaoTexSlot(0); + + // SSAO lighting shader - apply lightig modified by SSAO calculations + shaderSSAOLighting.CompileShader(VertShader2DFbo, FragShaderSSAOLighting); + shaderSSAOLighting.UpdateAlbedoTexSlot(0); + shaderSSAOLighting.UpdatePositionTexSlot(1); + shaderSSAOLighting.UpdateNormalTexSlot(2); + shaderSSAOLighting.UpdateSsaoTexSlot(3); + shaderSSAOLighting.UpdateEnvColor(lightPos, lightColor, ambientCol, 0.01f); + + // Mill Path Line Shader + shaderLinePath.CompileShader(VertShader3DLine, FragShader3DLine); + +} + +void SimDisplay::CreateFboQuad() +{ + float quadVertices[] = {// a quad that fills the entire screen in Normalized Device Coordinates. + // positions // texCoords + -1.0f, 1.0f, 0.0f, 1.0f, -1.0f, -1.0f, 0.0f, 0.0f, + 1.0f, -1.0f, 1.0f, 0.0f, -1.0f, 1.0f, 0.0f, 1.0f, + 1.0f, -1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f}; + + glGenVertexArrays(1, &mFboQuadVAO); + glGenBuffers(1, &mFboQuadVBO); + glBindVertexArray(mFboQuadVAO); + glBindBuffer(GL_ARRAY_BUFFER, mFboQuadVBO); + glBufferData(GL_ARRAY_BUFFER, sizeof(quadVertices), &quadVertices[0], GL_STATIC_DRAW); + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)0); + glEnableVertexAttribArray(1); + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)(2 * sizeof(float))); +} + +void SimDisplay::CreateDisplayFbos() +{ + // setup frame buffer for simulation + glGenFramebuffers(1, &mFbo); + glBindFramebuffer(GL_FRAMEBUFFER, mFbo); + + // a color texture for the frame buffer + glGenTextures(1, &mFboColTexture); + glBindTexture(GL_TEXTURE_2D, mFboColTexture); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, gWindowSizeW, gWindowSizeH, 0, GL_RGBA, GL_UBYTE, NULL); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mFboColTexture, 0); + + // a position texture for the frame buffer + glGenTextures(1, &mFboPosTexture); + glBindTexture(GL_TEXTURE_2D, mFboPosTexture); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, gWindowSizeW, gWindowSizeH, 0, GL_RGBA, GL_FLOAT, NULL); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, mFboPosTexture, 0); + + // a normal texture for the frame buffer + glGenTextures(1, &mFboNormTexture); + glBindTexture(GL_TEXTURE_2D, mFboNormTexture); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, gWindowSizeW, gWindowSizeH, 0, GL_RGBA, GL_FLOAT, NULL); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT2, GL_TEXTURE_2D, mFboNormTexture, 0); + + + unsigned int attachments[3] = {GL_COLOR_ATTACHMENT0, + GL_COLOR_ATTACHMENT1, + GL_COLOR_ATTACHMENT2}; + glDrawBuffers(3, attachments); + + glGenRenderbuffers(1, &mRboDepthStencil); + glBindRenderbuffer(GL_RENDERBUFFER, mRboDepthStencil); + glRenderbufferStorage( + GL_RENDERBUFFER, + GL_DEPTH24_STENCIL8, + gWindowSizeW, + gWindowSizeH); // use a single renderbuffer object for both a depth AND stencil buffer. + glFramebufferRenderbuffer(GL_FRAMEBUFFER, + GL_DEPTH_STENCIL_ATTACHMENT, + GL_RENDERBUFFER, + mRboDepthStencil); // now actually attach it + + if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { + return; + } + + glBindFramebuffer(GL_FRAMEBUFFER, 0); +} + + +void SimDisplay::CreateSsaoFbos() +{ + + mSsaoValid = true; + + // setup framebuffer for SSAO processing + glGenFramebuffers(1, &mSsaoFbo); + glBindFramebuffer(GL_FRAMEBUFFER, mSsaoFbo); + // SSAO color buffer + glGenTextures(1, &mFboSsaoTexture); + glBindTexture(GL_TEXTURE_2D, mFboSsaoTexture); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, gWindowSizeW, gWindowSizeH, 0, GL_RED, GL_FLOAT, NULL); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mFboSsaoTexture, 0); + if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { + mSsaoValid = false; + return; + } + + // setup framebuffer for SSAO blur processing + glGenFramebuffers(1, &mSsaoBlurFbo); + glBindFramebuffer(GL_FRAMEBUFFER, mSsaoBlurFbo); + glGenTextures(1, &mFboSsaoBlurTexture); + glBindTexture(GL_TEXTURE_2D, mFboSsaoBlurTexture); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, gWindowSizeW, gWindowSizeH, 0, GL_RED, GL_FLOAT, NULL); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glFramebufferTexture2D(GL_FRAMEBUFFER, + GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, + mFboSsaoBlurTexture, + 0); + if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { + mSsaoValid = false; + return; + } + + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + // generate sample kernel + std::uniform_real_distribution randomFloats(0.0, 1.0); + std::default_random_engine generator; + for (unsigned int i = 0; i < 64; ++i) { + vec3 sample; + vec3_set(sample, + randomFloats(generator) * 2.0f - 1.0f, + randomFloats(generator) * 2.0f - 1.0f, + randomFloats(generator));// * 2.0f - 1.0f); + vec3_norm(sample, sample); + vec3_scale(sample, sample, randomFloats(generator)); + float scale = float(i) / 64.0f; + + // scale samples s.t. they're more aligned to center of kernel + scale = Lerp(0.1f, 1.0f, scale * scale); + vec3_scale(sample, sample, scale); + mSsaoKernel.push_back(*(Point3D*)sample); + } + shaderSSAO.Activate(); + shaderSSAO.UpdateKernelVals(mSsaoKernel.size(), &mSsaoKernel[0].x); + + // generate noise texture + std::vector ssaoNoise; + for (unsigned int i = 0; i < 16; i++) { + vec3 noise; + vec3_set(noise, + randomFloats(generator) * 2.0f - 1.0f, + randomFloats(generator) * 2.0f - 1.0f, + 0.0f); // rotate around z-axis (in tangent space) + ssaoNoise.push_back(*(Point3D*)noise); + } + glGenTextures(1, &mFboSsaoNoiseTexture); + glBindTexture(GL_TEXTURE_2D, mFboSsaoNoiseTexture); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, 4, 4, 0, GL_RGB, GL_FLOAT, &ssaoNoise[0]); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); +} + + +SimDisplay::~SimDisplay() +{ + CleanGL(); +} + +void SimDisplay::InitGL() +{ + if (displayInitiated) { + return; + } + + // setup light object + mlightObject.GenerateBoxStock(-0.5f, -0.5f, -0.5f, 1, 1, 1); + + InitShaders(); + CreateDisplayFbos(); + CreateSsaoFbos(); + CreateFboQuad(); + + UpdateProjection(); + displayInitiated = true; +} + +void SimDisplay::CleanFbos() +{ + // cleanup frame buffers + GLDELETE_FRAMEBUFFER(mFbo); + GLDELETE_FRAMEBUFFER(mSsaoFbo); + GLDELETE_FRAMEBUFFER(mSsaoBlurFbo); + + // cleanup fbo textures + GLDELETE_TEXTURE(mFboColTexture); + GLDELETE_TEXTURE(mFboPosTexture); + GLDELETE_TEXTURE(mFboNormTexture); + GLDELETE_TEXTURE(mFboSsaoTexture); + GLDELETE_TEXTURE(mFboSsaoBlurTexture); + GLDELETE_TEXTURE(mFboSsaoNoiseTexture); + GLDELETE_RENDERBUFFER(mRboDepthStencil); +} + +void SimDisplay::CleanGL() +{ + CleanFbos(); + + // cleanup geometry + GLDELETE_VERTEXARRAY(mFboQuadVAO); + GLDELETE_BUFFER(mFboQuadVBO); + + // cleanup shaders + shader3D.Destroy(); + shaderInv3D.Destroy(); + shaderFlat.Destroy(); + shaderSimFbo.Destroy(); + shaderGeom.Destroy(); + shaderSSAO.Destroy(); + shaderLighting.Destroy(); + shaderSSAOLighting.Destroy(); + shaderSSAOBlur.Destroy(); + + displayInitiated = false; +} + +void SimDisplay::PrepareDisplay(vec3 objCenter) +{ + mat4x4_look_at(mMatLookAt, eye, target, upvec); + mat4x4_translate_in_place(mMatLookAt, mEyeX * mEyeXZFactor, 0, mEyeZ * mEyeXZFactor); + mat4x4_rotate_X(mMatLookAt, mMatLookAt, mEyeInclination); + mat4x4_rotate_Z(mMatLookAt, mMatLookAt, mEyeRoration); + mat4x4_translate_in_place(mMatLookAt, -objCenter[0], -objCenter[1], -objCenter[2]); +} + +void SimDisplay::PrepareFrameBuffer() +{ + glBindFramebuffer(GL_FRAMEBUFFER, mFbo); + glClearColor(0, 0, 0, 0); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); + glEnable(GL_CULL_FACE); + glEnable(GL_DEPTH_TEST); + glDepthFunc(GL_LESS); +} + +void SimDisplay::StartDepthPass() +{ + glEnable(GL_DEPTH_TEST); + glDepthFunc(GL_LESS); + glDepthMask(GL_TRUE); + shaderFlat.Activate(); + shaderFlat.UpdateViewMat(mMatLookAt); +} + +void SimDisplay::StartGeometryPass(vec3 objColor, bool invertNormals) +{ + glBindFramebuffer(GL_FRAMEBUFFER, mFbo); + shaderGeom.Activate(); + shaderGeom.UpdateNormalState(invertNormals); + shaderGeom.UpdateViewMat(mMatLookAt); + shaderGeom.UpdateObjColor(objColor); + glEnable(GL_CULL_FACE); + glDisable(GL_BLEND); +} + +// A 'closer' geometry pass is similar to std geometry pass, but render the objects +// slightly closer to the camera. This mitigates overlapping faces artifacts. +void SimDisplay::StartCloserGeometryPass(vec3 objColor) +{ + glBindFramebuffer(GL_FRAMEBUFFER, mFbo); + shaderGeomCloser.Activate(); + shaderGeomCloser.UpdateNormalState(false); + shaderGeomCloser.UpdateViewMat(mMatLookAt); + shaderGeomCloser.UpdateObjColor(objColor); + glEnable(GL_CULL_FACE); + glDisable(GL_BLEND); +} + +void SimDisplay::RenderLightObject() +{ + shaderFlat.Activate(); + shaderFlat.UpdateObjColor(lightColor); + mlightObject.render(); +} + +void SimDisplay::ScaleViewToStock(StockObject* obj) +{ + mMaxStockDim = fmaxf(obj->size[0], obj->size[1]); + maxFar = mMaxStockDim * 4; + UpdateProjection(); + vec3_set(eye, 0, 0, 0); + UpdateEyeFactor(0.4f); + vec3_set(lightPos, obj->position[0], obj->position[1], obj->position[2] + mMaxStockDim / 3); + mlightObject.SetPosition(lightPos); +} + +void SimDisplay::RenderResult() +{ + if (mSsaoValid && applySSAO) { + RenderResultSSAO(); + } + else { + RenderResultStandard(); + } +} + +void SimDisplay::RenderResultStandard() +{ + // set default frame buffer + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + // display the sim result within the FBO + shaderLighting.Activate(); + // shaderSimFbo.Activate(); + glBindVertexArray(mFboQuadVAO); + glDisable(GL_DEPTH_TEST); + glDisable(GL_CULL_FACE); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, mFboColTexture); + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, mFboPosTexture); + glActiveTexture(GL_TEXTURE2); + glBindTexture(GL_TEXTURE_2D, mFboNormTexture); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glDrawArrays(GL_TRIANGLES, 0, 6); +} + +void SimDisplay::RenderResultSSAO() +{ + glDisable(GL_DEPTH_TEST); + glDisable(GL_CULL_FACE); + // generate SSAO texture + glBindFramebuffer(GL_FRAMEBUFFER, mSsaoFbo); + shaderSSAO.Activate(); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, mFboSsaoNoiseTexture); + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, mFboPosTexture); + glActiveTexture(GL_TEXTURE2); + glBindTexture(GL_TEXTURE_2D, mFboNormTexture); + glBindVertexArray(mFboQuadVAO); + glDrawArrays(GL_TRIANGLES, 0, 6); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + // blur SSAO texture to remove noise + glBindFramebuffer(GL_FRAMEBUFFER, mSsaoBlurFbo); + glClear(GL_COLOR_BUFFER_BIT); + shaderSSAOBlur.Activate(); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, mFboSsaoTexture); + glBindVertexArray(mFboQuadVAO); + glDrawArrays(GL_TRIANGLES, 0, 6); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + // lighting pass: deferred Blinn-Phong lighting with added screen-space ambient occlusion + shaderSSAOLighting.Activate(); + shaderSSAOLighting.UpdateAlbedoTexSlot(0); + shaderSSAOLighting.UpdatePositionTexSlot(1); + shaderSSAOLighting.UpdateNormalTexSlot(2); + shaderSSAOLighting.UpdateSsaoTexSlot(3); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, mFboColTexture); + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, mFboPosTexture); + glActiveTexture(GL_TEXTURE2); + glBindTexture(GL_TEXTURE_2D, mFboNormTexture); + glActiveTexture(GL_TEXTURE3); // add extra SSAO texture to lighting pass + glBindTexture(GL_TEXTURE_2D, mFboSsaoBlurTexture); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glBindVertexArray(mFboQuadVAO); + glDrawArrays(GL_TRIANGLES, 0, 6); +} + +void SimDisplay::SetupLinePathPass(int curSegment, bool isHidden) +{ + glEnable(GL_DEPTH_TEST); + glDepthMask(GL_FALSE); + glDepthFunc(isHidden ? GL_GREATER : GL_LESS); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glLineWidth(2); + shaderLinePath.Activate(); + pathLineColor[3] = isHidden ? 0.1f : 1.0f; + shaderLinePath.UpdateObjColorAlpha(pathLineColor); + shaderLinePath.UpdateCurSegment(curSegment); + shaderLinePath.UpdateViewMat(mMatLookAt); +} + +void SimDisplay::TiltEye(float tiltStep) +{ + mEyeInclination += tiltStep; + if (mEyeInclination > PI / 2) { + mEyeInclination = PI / 2; + } + else if (mEyeInclination < -PI / 2) { + mEyeInclination = -PI / 2; + } +} + +void SimDisplay::RotateEye(float rotStep) +{ + mEyeRoration += rotStep; + if (mEyeRoration > PI2) { + mEyeRoration -= PI2; + } + else if (mEyeRoration < 0) { + mEyeRoration += PI2; + } + updateDisplay = true; +} + +void SimDisplay::MoveEye(float x, float z) +{ + mEyeX += x; + if (mEyeX > 100) { + mEyeX = 100; + } + else if (mEyeX < -100) { + mEyeX = -100; + } + mEyeZ += z; + if (mEyeZ > 100) { + mEyeZ = 100; + } + else if (mEyeZ < -100) { + mEyeZ = -100; + } + updateDisplay = true; +} + +void SimDisplay::UpdateEyeFactor(float factor) +{ + if (mEyeDistFactor == factor) { + return; + } + updateDisplay = true; + mEyeDistFactor = factor; + mEyeXZFactor = factor * maxFar * 0.005f; + eye[1] = -factor * maxFar; +} + +void SimDisplay::UpdateWindowScale() +{ + glBindFramebuffer(GL_FRAMEBUFFER, mFbo); + CleanFbos(); + CreateDisplayFbos(); + CreateSsaoFbos(); + UpdateProjection(); +} + +void SimDisplay::UpdateProjection() +{ + // Setup projection + mat4x4 projmat; + mat4x4_perspective(projmat, 0.7f, (float)gWindowSizeW / gWindowSizeH, 1.0f, maxFar); + shader3D.Activate(); + shader3D.UpdateProjectionMat(projmat); + shaderInv3D.Activate(); + shaderInv3D.UpdateProjectionMat(projmat); + shaderFlat.Activate(); + shaderFlat.UpdateProjectionMat(projmat); + shaderGeom.Activate(); + shaderGeom.UpdateProjectionMat(projmat); + shaderSSAO.Activate(); + shaderSSAO.UpdateProjectionMat(projmat); + shaderLinePath.Activate(); + shaderLinePath.UpdateProjectionMat(projmat); + shaderLinePath.UpdateObjColor(pathLineColorPassed); + + projmat[2][2] *= 0.99999; + shaderGeomCloser.Activate(); + shaderGeomCloser.UpdateProjectionMat(projmat); +} + + +} // namespace MillSim diff --git a/src/Mod/CAM/PathSimulator/AppGL/SimDisplay.h b/src/Mod/CAM/PathSimulator/AppGL/SimDisplay.h new file mode 100644 index 0000000000..ecf1e7c22c --- /dev/null +++ b/src/Mod/CAM/PathSimulator/AppGL/SimDisplay.h @@ -0,0 +1,138 @@ +/*************************************************************************** + * Copyright (c) 2024 Shai Seger * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + +#ifndef __simdisplay_h__ +#define __simdisplay_h__ + +#include "GlUtils.h" +#include "Shader.h" +#include "StockObject.h" +#include "MillPathLine.h" +#include + + +namespace MillSim +{ + +struct Point3D +{ + float x, y, z; +}; + +class SimDisplay +{ +public: + ~SimDisplay(); + void InitGL(); + void CleanGL(); + void CleanFbos(); + void PrepareDisplay(vec3 objCenter); + void PrepareFrameBuffer(); + void StartDepthPass(); + void StartGeometryPass(vec3 objColor, bool invertNormals); + void StartCloserGeometryPass(vec3 objColor); + void RenderLightObject(); + void ScaleViewToStock(StockObject* obj); + void RenderResult(); + void RenderResultStandard(); + void RenderResultSSAO(); + void SetupLinePathPass(int curSegment, bool isHidden); + void TiltEye(float tiltStep); + void RotateEye(float rotStep); + void MoveEye(float x, float z); + void UpdateEyeFactor(float factor); + void UpdateWindowScale(); + + void UpdateProjection(); + float GetEyeFactor() + { + return mEyeDistFactor; + } + +public: + bool applySSAO = false; + bool updateDisplay = false; + float maxFar = 100; + bool displayInitiated = false; + +protected: + void InitShaders(); + void CreateDisplayFbos(); + void CreateSsaoFbos(); + void CreateFboQuad(); + float Lerp(float a, float b, float f) + { + return a + f * (b - a); + } + +protected: + // shaders + Shader shader3D, shaderInv3D, shaderFlat, shaderSimFbo; + Shader shaderGeom, shaderSSAO, shaderLighting, shaderSSAOLighting, shaderSSAOBlur; + Shader shaderGeomCloser; + Shader shaderLinePath; + vec3 lightColor = {0.8f, 0.9f, 1.0f}; + vec3 lightPos = {20.0f, 20.0f, 10.0f}; + vec3 ambientCol = {0.6f, 0.6f, 0.7f}; + vec4 pathLineColor = {0.0f, 0.9f, 0.0f, 1.0}; + vec3 pathLineColorPassed = {0.9f, 0.3f, 0.3f}; + + vec3 eye = {0, 100, 40}; + vec3 target = {0, 0, -10}; + vec3 upvec = {0, 0, 1}; + + mat4x4 mMatLookAt; + StockObject mlightObject; + + + float mEyeDistance = 30; + float mEyeRoration = 0; + float mEyeInclination = PI / 6; // 30 degree + float mEyeStep = PI / 36; // 5 degree + + float mMaxStockDim = 100; + float mEyeDistFactor = 0.0f; + float mEyeXZFactor = 0.01f; + float mEyeXZScale = 0; + float mEyeX = 0.0f; + float mEyeZ = 0.0f; + + // base frame buffer + unsigned int mFbo; + unsigned int mFboColTexture; + unsigned int mFboPosTexture; + unsigned int mFboNormTexture; + unsigned int mRboDepthStencil; + unsigned int mFboQuadVAO, mFboQuadVBO; + + // ssao frame buffers + bool mSsaoValid = false; + std::vector mSsaoKernel; + unsigned int mSsaoFbo; + unsigned int mSsaoBlurFbo; + unsigned int mFboSsaoTexture; + unsigned int mFboSsaoBlurTexture; + unsigned int mFboSsaoNoiseTexture; +}; + +} // namespace MillSim +#endif // !__simdisplay_h__ diff --git a/src/Mod/CAM/PathSimulator/AppGL/SimShapes.cpp b/src/Mod/CAM/PathSimulator/AppGL/SimShapes.cpp index de60e2e207..f151aa10f1 100644 --- a/src/Mod/CAM/PathSimulator/AppGL/SimShapes.cpp +++ b/src/Mod/CAM/PathSimulator/AppGL/SimShapes.cpp @@ -24,49 +24,39 @@ #include "Shader.h" #include "GlUtils.h" #include -#include -#include #include +#include using namespace MillSim; -static float* sinTable = nullptr; -static float* cosTable = nullptr; -static int lastNumSlices = 0; +int Shape::lastNumSlices = 0; +std::vector Shape::sinTable; +std::vector Shape::cosTable; -static bool GenerateSinTable(int nSlices) +void Shape::GenerateSinTable(int nSlices) { - if (nSlices == lastNumSlices) - return true; - if (sinTable != nullptr) - free(sinTable); - if (cosTable != nullptr) - free(cosTable); - sinTable = cosTable = nullptr; + if (nSlices == lastNumSlices) { + return; + } float slice = (float)(2 * PI / nSlices); int nvals = nSlices + 1; - sinTable = (float*)malloc(nvals * sizeof(float)); - if (sinTable == nullptr) - return false; - cosTable = (float*)malloc(nvals * sizeof(float)); - if (cosTable == nullptr) - { - free(sinTable); - sinTable = nullptr; - return false; - } - for (int i = 0; i < nvals; i++) - { + sinTable.resize(nvals); + cosTable.resize(nvals); + for (int i = 0; i < nvals; i++) { sinTable[i] = sinf(slice * i); cosTable[i] = cosf(slice * i); } lastNumSlices = nvals; - return true; } -void MillSim::Shape::RotateProfile(float* profPoints, int nPoints, float distance, int nSlices, bool isHalfTurn) +void Shape::RotateProfile(float* profPoints, + int nPoints, + float distance, + float /* deltaHeight */, + int nSlices, + bool isHalfTurn) { int vidx = 0; int iidx = 0; @@ -76,27 +66,15 @@ void MillSim::Shape::RotateProfile(float* profPoints, int nPoints, float distanc numVerts = nPoints * 2 * (nSlices + 1); numIndices = (nPoints - 1) * nSlices * 6; - float* vbuffer = (float*)malloc(numVerts * sizeof(Vertex)); - if (vbuffer == nullptr) - return; - GLushort* ibuffer = (GLushort*)malloc(numIndices * sizeof(GLushort)); - if (ibuffer == nullptr) - { - free(vbuffer); - return; - } + std::vector vbuffer(numVerts); + std::vector ibuffer(numIndices); int nsinvals = nSlices; - if (isHalfTurn) + if (isHalfTurn) { nsinvals *= 2; - if (GenerateSinTable(nsinvals) == false) - { - free(vbuffer); - free(ibuffer); - return; } + GenerateSinTable(nsinvals); - for (int i = 0; i < nPoints; i++) - { + for (int i = 0; i < nPoints; i++) { int i2 = i * 2; float prevy = i > 0 ? profPoints[i2 - 2] : 0; @@ -110,8 +88,7 @@ void MillSim::Shape::RotateProfile(float* profPoints, int nPoints, float distanc float nz = diffy / len; vstart = i * 2 * (nSlices + 1); - for (int j = 0; j <= nSlices; j++) - { + for (int j = 0; j <= nSlices; j++) { // generate vertices float sx = sinTable[j]; float sy = cosTable[j]; @@ -125,69 +102,77 @@ void MillSim::Shape::RotateProfile(float* profPoints, int nPoints, float distanc float nx = ny * sx; ny *= sy; - SET_TRIPLE(vbuffer, vidx, x1, y1, prevz); - SET_TRIPLE(vbuffer, vidx, nx, ny, nz); - SET_TRIPLE(vbuffer, vidx, x2, y2, z2); - SET_TRIPLE(vbuffer, vidx, nx, ny, nz); + vbuffer[vidx++] = {x1, y1, prevz, nx, ny, nz }; + vbuffer[vidx++] = {x2, y2, z2, nx, ny, nz}; - if (j != nSlices) - { + if (j != nSlices) { // generate indices { 0, 3, 1, 0, 2, 3 } int pos = vstart + 2 * j; - if (i < (nPoints - 1)) + if (i < (nPoints - 1)) { SET_TRIPLE(ibuffer, iidx, pos, pos + 3, pos + 1); - if (i > 0) + } + if (i > 0) { SET_TRIPLE(ibuffer, iidx, pos, pos + 2, pos + 3); + } } } } - GenerateModel(vbuffer, ibuffer, numVerts, numIndices); - - free(vbuffer); - free(ibuffer); + SetModelData(vbuffer, ibuffer); } -void MillSim::Shape::CalculateExtrudeBufferSizes(int nProfilePoints, bool capStart, bool capEnd, - int* numVerts, int* numIndices, int* vc1idx, int* vc2idx, int* ic1idx, int* ic2idx) +void Shape::CalculateExtrudeBufferSizes(int nProfilePoints, + bool capStart, + bool capEnd, + int* numVerts, + int* numIndices, + int* vc1idx, + int* vc2idx, + int* ic1idx, + int* ic2idx) { - *numVerts = nProfilePoints * 4; // one face per profile point times 4 vertex per face - *numIndices = nProfilePoints * 2 * 3; // 2 triangles per face times 3 indices per triangle - if (capStart) - { - *vc1idx = *numVerts * 6; + *numVerts = nProfilePoints * 4; // one face per profile point times 4 vertex per face + *numIndices = nProfilePoints * 2 * 3; // 2 triangles per face times 3 indices per triangle + if (capStart) { + *vc1idx = *numVerts; *numVerts += nProfilePoints; *ic1idx = *numIndices; *numIndices += (nProfilePoints - 2) * 3; } - if (capEnd) - { - *vc2idx = *numVerts * 6; + if (capEnd) { + *vc2idx = *numVerts; *numVerts += nProfilePoints; *ic2idx = *numIndices; *numIndices += (nProfilePoints - 2) * 3; } } -void MillSim::Shape::ExtrudeProfileRadial(float* profPoints, int nPoints, float radius, float angleRad, float deltaHeight, bool capStart, bool capEnd) +void Shape::ExtrudeProfileRadial(float* profPoints, + int nPoints, + float radius, + float angleRad, + float deltaHeight, + bool capStart, + bool capEnd) { int vidx = 0, vc1idx, vc2idx; int iidx = 0, ic1idx, ic2idx; int numVerts, numIndices; - CalculateExtrudeBufferSizes(nPoints, capStart, capEnd, &numVerts, &numIndices, &vc1idx, &vc2idx, &ic1idx, &ic2idx); - int vc1start = vc1idx / 6; - int vc2start = vc2idx / 6; + CalculateExtrudeBufferSizes(nPoints, + capStart, + capEnd, + &numVerts, + &numIndices, + &vc1idx, + &vc2idx, + &ic1idx, + &ic2idx); + int vc1start = vc1idx; + int vc2start = vc2idx; - float* vbuffer = (float*)malloc(numVerts * sizeof(Vertex)); - if (!vbuffer) - return; - GLushort* ibuffer = (GLushort*)malloc(numIndices * sizeof(GLushort)); - if (!ibuffer) - { - free(vbuffer); - return; - } + std::vector vbuffer(numVerts); + std::vector ibuffer(numIndices); bool is_clockwise = angleRad > 0; angleRad = (float)fabs(angleRad); @@ -197,8 +182,7 @@ void MillSim::Shape::ExtrudeProfileRadial(float* profPoints, int nPoints, float float cosAng = cosf(angleRad); float sinAng = sinf(angleRad); - for (int i = 0; i < nPoints; i++) - { + for (int i = 0; i < nPoints; i++) { int p1 = i * 2; float y1 = profPoints[p1] + radius; float z1 = profPoints[p1 + 1]; @@ -216,16 +200,14 @@ void MillSim::Shape::ExtrudeProfileRadial(float* profPoints, int nPoints, float ny *= cosAng; // start verts - SET_TRIPLE(vbuffer, vidx, 0, y1, z1); - SET_TRIPLE(vbuffer, vidx, nx, ny, nz); - SET_TRIPLE(vbuffer, vidx, 0, y2, z2); - SET_TRIPLE(vbuffer, vidx, nx, ny, nz); + vbuffer[vidx++] = {0, y1, z1, nx, ny, nz}; + vbuffer[vidx++] = {0, y2, z2, nx, ny, nz}; if (capStart) { - SET_TRIPLE(vbuffer, vc1idx, 0, y1, z1); - SET_TRIPLE(vbuffer, vc1idx, -1 * dir, 0, 0); - if (i > 1) + vbuffer[vc1idx++] = {0, y1, z1, -1 * dir, 0, 0}; + if (i > 1) { SET_TRIPLE(ibuffer, ic1idx, vc1start, vc1start + i + offs1, vc1start + i + offs2); + } } float x1 = y1 * sinAng * dir; @@ -234,61 +216,60 @@ void MillSim::Shape::ExtrudeProfileRadial(float* profPoints, int nPoints, float y2 *= cosAng; z1 += deltaHeight; z2 += deltaHeight; - SET_TRIPLE(vbuffer, vidx, x1, y1, z1); - SET_TRIPLE(vbuffer, vidx, nx, ny, nz); - SET_TRIPLE(vbuffer, vidx, x2, y2, z2); - SET_TRIPLE(vbuffer, vidx, nx, ny, nz); + vbuffer[vidx++] = {x1, y1, z1, nx, ny, nz}; + vbuffer[vidx++] = {x2, y2, z2, nx, ny, nz}; // face have 2 triangles { 0, 2, 3, 0, 3, 1 }; GLushort vistart = i * 4; - if (is_clockwise) - { + if (is_clockwise) { SET_TRIPLE(ibuffer, iidx, vistart, vistart + 2, vistart + 3); SET_TRIPLE(ibuffer, iidx, vistart, vistart + 3, vistart + 1); } - else - { + else { SET_TRIPLE(ibuffer, iidx, vistart, vistart + 3, vistart + 2); SET_TRIPLE(ibuffer, iidx, vistart, vistart + 1, vistart + 3); } - if (capEnd) - { - SET_TRIPLE(vbuffer, vc2idx, x1, y1, z1); - SET_TRIPLE(vbuffer, vc2idx, cosAng * dir, -sinAng, 0); - if (i > 1) + if (capEnd) { + vbuffer[vc2idx++] = {x1, y1, z1, cosAng * dir, -sinAng, 0}; + if (i > 1) { SET_TRIPLE(ibuffer, ic2idx, vc2start, vc2start + i + offs2, vc2start + i + offs1); + } } } - GenerateModel(vbuffer, ibuffer, numVerts, numIndices); - - free(vbuffer); - free(ibuffer); + SetModelData(vbuffer, ibuffer); } -void MillSim::Shape::ExtrudeProfileLinear(float* profPoints, int nPoints, float fromX, float toX, float fromZ, float toZ, bool capStart, bool capEnd) +void Shape::ExtrudeProfileLinear(float* profPoints, + int nPoints, + float fromX, + float toX, + float fromZ, + float toZ, + bool capStart, + bool capEnd) { int vidx = 0, vc1idx, vc2idx; int iidx = 0, ic1idx, ic2idx; int numVerts, numIndices; - CalculateExtrudeBufferSizes(nPoints, capStart, capEnd, &numVerts, &numIndices, &vc1idx, &vc2idx, &ic1idx, &ic2idx); - int vc1start = vc1idx / 6; - int vc2start = vc2idx / 6; + CalculateExtrudeBufferSizes(nPoints, + capStart, + capEnd, + &numVerts, + &numIndices, + &vc1idx, + &vc2idx, + &ic1idx, + &ic2idx); + int vc1start = vc1idx; + int vc2start = vc2idx; - float* vbuffer = (float*)malloc(numVerts * sizeof(Vertex)); - if (!vbuffer) - return; - GLushort* ibuffer = (GLushort*)malloc(numIndices * sizeof(GLushort)); - if (!ibuffer) - { - free(vbuffer); - return; - } + std::vector vbuffer(numVerts); + std::vector ibuffer(numIndices); - for (int i = 0; i < nPoints; i++) - { + for (int i = 0; i < nPoints; i++) { // hollow pipe verts int p1 = i * 2; float y1 = profPoints[p1]; @@ -304,45 +285,36 @@ void MillSim::Shape::ExtrudeProfileLinear(float* profPoints, int nPoints, float float ny = -zdiff / len; float nz = ydiff / len; - SET_TRIPLE(vbuffer, vidx, fromX, y1, z1 + fromZ); - SET_TRIPLE(vbuffer, vidx, 0, ny, nz); - SET_TRIPLE(vbuffer, vidx, fromX, y2, z2 + fromZ); - SET_TRIPLE(vbuffer, vidx, 0, ny, nz); - SET_TRIPLE(vbuffer, vidx, toX, y1, z1 + toZ); - SET_TRIPLE(vbuffer, vidx, 0, ny, nz); - SET_TRIPLE(vbuffer, vidx, toX, y2, z2 + toZ); - SET_TRIPLE(vbuffer, vidx, 0, ny, nz); + vbuffer[vidx++] = {fromX, y1, z1 + fromZ, 0, ny, nz}; + vbuffer[vidx++] = {fromX, y2, z2 + fromZ, 0, ny, nz}; + vbuffer[vidx++] = {toX, y1, z1 + toZ, 0, ny, nz}; + vbuffer[vidx++] = {toX, y2, z2 + toZ, 0, ny, nz}; // face have 2 triangles { 0, 2, 3, 0, 3, 1 }; GLushort vistart = i * 4; SET_TRIPLE(ibuffer, iidx, vistart, vistart + 2, vistart + 3); SET_TRIPLE(ibuffer, iidx, vistart, vistart + 3, vistart + 1); - if (capStart) - { - SET_TRIPLE(vbuffer, vc1idx, fromX, profPoints[p1], profPoints[p1 + 1] + fromZ); - SET_TRIPLE(vbuffer, vc1idx, -1, 0, 0); - if (i > 1) + if (capStart) { + vbuffer[vc1idx++] = {fromX, profPoints[p1], profPoints[p1 + 1] + fromZ, -1, 0, 0}; + if (i > 1) { SET_TRIPLE(ibuffer, ic1idx, vc1start, vc1start + i - 1, vc1start + i); + } } - if (capEnd) - { - SET_TRIPLE(vbuffer, vc2idx, toX, profPoints[p1], profPoints[p1 + 1] + toZ); - SET_TRIPLE(vbuffer, vc2idx, 1, 0, 0); - if (i > 1) + if (capEnd) { + vbuffer[vc2idx++] = {toX, profPoints[p1], profPoints[p1 + 1] + toZ, 1, 0, 0}; + if (i > 1) { SET_TRIPLE(ibuffer, ic2idx, vc2start, vc2start + i, vc2start + i - 1); + } } } - GenerateModel(vbuffer, ibuffer, numVerts, numIndices); - - free(vbuffer); - free(ibuffer); + SetModelData(vbuffer, ibuffer); } -void MillSim::Shape::GenerateModel(float* vbuffer, GLushort* ibuffer, int numVerts, int nIndices) +void Shape::GenerateModel(float* vbuffer, GLushort* ibuffer, int numVerts, int nIndices) { - //GLuint vbo, ibo, vao; + // GLuint vbo, ibo, vao; // vertex buffer glGenBuffers(1, &vbo); @@ -367,32 +339,33 @@ void MillSim::Shape::GenerateModel(float* vbuffer, GLushort* ibuffer, int numVer numIndices = nIndices; } -void MillSim::Shape::Render() +void MillSim::Shape::SetModelData(std::vector& vbuffer, std::vector& ibuffer) +{ + GenerateModel((float*)vbuffer.data(), ibuffer.data(), (int)vbuffer.size(), (int)ibuffer.size()); +} + +void Shape::Render() { glBindVertexArray(vao); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo); glDrawElements(GL_TRIANGLES, numIndices, GL_UNSIGNED_SHORT, nullptr); } -void MillSim::Shape::Render(mat4x4 modelMat, mat4x4 normallMat) // normals are rotated only +void Shape::Render(mat4x4 modelMat, mat4x4 normallMat) // normals are rotated only { CurrentShader->UpdateModelMat(modelMat, normallMat); Render(); } -void MillSim::Shape::FreeResources() +void Shape::FreeResources() { - if (vao > 0) - { - glBindVertexArray(vao); - if (vbo > 0) glDeleteBuffers(1, &vbo); - if (ibo > 0) glDeleteBuffers(1, &ibo); - glDeleteVertexArrays(1, &vao); - vbo = ibo = vao = 0; - } + glBindVertexArray(0); + GLDELETE_BUFFER(vbo); + GLDELETE_BUFFER(ibo); + GLDELETE_VERTEXARRAY(vao); } -MillSim::Shape::~Shape() +Shape::~Shape() { FreeResources(); } diff --git a/src/Mod/CAM/PathSimulator/AppGL/SimShapes.h b/src/Mod/CAM/PathSimulator/AppGL/SimShapes.h index 0b2821e4d8..e065250972 100644 --- a/src/Mod/CAM/PathSimulator/AppGL/SimShapes.h +++ b/src/Mod/CAM/PathSimulator/AppGL/SimShapes.h @@ -37,7 +37,6 @@ var[idx++] = z; \ } - #define SET_TRIPLE_OFFS(var, idx, offs, x, y, z) \ { \ var[idx++] = x + offs; \ @@ -51,6 +50,18 @@ typedef unsigned int uint; struct Vertex { + Vertex() + : Vertex(0, 0, 0) + {} + Vertex(float _x, float _y, float _z, float _nx = 0, float _ny = 0, float _nz = 0) + { + x = _x; + y = _y; + z = _z; + nx = _nx; + ny = _ny; + nz = _nz; + } float x, y, z; float nx, ny, nz; }; @@ -72,9 +83,11 @@ public: void Render(); void Render(mat4x4 modelMat, mat4x4 normallMat); void FreeResources(); + void SetModelData(std::vector& vbuffer, std::vector& ibuffer); void RotateProfile(float* profPoints, int nPoints, float distance, + float deltaHeight, int nSlices, bool isHalfTurn); void ExtrudeProfileRadial(float* profPoints, @@ -93,6 +106,11 @@ public: bool capStart, bool capEnd); + static void GenerateSinTable(int nSlices); + static std::vector sinTable; + static std::vector cosTable; + static int lastNumSlices; + protected: void GenerateModel(float* vbuffer, GLushort* ibuffer, int numVerts, int numIndices); void CalculateExtrudeBufferSizes(int nProfilePoints, @@ -104,6 +122,7 @@ protected: int* vc2idx, int* ic1idx, int* ic2idx); + }; } // namespace MillSim diff --git a/src/Mod/CAM/PathSimulator/AppGL/SolidObject.cpp b/src/Mod/CAM/PathSimulator/AppGL/SolidObject.cpp new file mode 100644 index 0000000000..263d15e167 --- /dev/null +++ b/src/Mod/CAM/PathSimulator/AppGL/SolidObject.cpp @@ -0,0 +1,79 @@ +/*************************************************************************** + * Copyright (c) 2024 Shai Seger * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + +#include "SolidObject.h" +#include "Shader.h" +#include + +#define NUM_PROFILE_POINTS 4 +using namespace MillSim; + +SolidObject::SolidObject() +{ + mat4x4_identity(mModelMat); + vec3_set(center, 0, 0, 0); +} + +SolidObject::~SolidObject() +{ + isValid = false; + shape.FreeResources(); +} + +void MillSim::SolidObject::SetPosition(vec3 position) +{ + mat4x4_translate(mModelMat, position[0], position[1], position[2]); +} + +void SolidObject::render() +{ + if (!isValid) { + return; + } + // UpdateObjColor(color); + shape.Render(mModelMat, mModelMat); // model is not rotated hence both are identity matrix +} + +void SolidObject::GenerateSolid(std::vector& verts, std::vector& indices) +{ + shape.SetModelData(verts, indices); + + // calculate object's bounding box: + float x = 999999.0f, y = 999999.0f, z = 999999.0f; + float l = -999999.0f, w = -999999.0f, h = -999999.0f; + for (auto& vert : verts) + { + x = std::fminf(x, vert.x); + y = std::fminf(y, vert.y); + z = std::fminf(z, vert.z); + l = std::fmaxf(l, vert.x); + w = std::fmaxf(w, vert.y); + h = std::fmaxf(h, vert.z); + } + l -= x; + w -= y; + h -= z; + vec3_set(position, x, y, z); + vec3_set(center, x + l / 2, y + w / 2, z + h / 2); + vec3_set(size, l, w, h); + isValid = true; +} diff --git a/src/Mod/CAM/PathSimulator/AppGL/SolidObject.h b/src/Mod/CAM/PathSimulator/AppGL/SolidObject.h new file mode 100644 index 0000000000..674fc77c69 --- /dev/null +++ b/src/Mod/CAM/PathSimulator/AppGL/SolidObject.h @@ -0,0 +1,53 @@ +/*************************************************************************** + * Copyright (c) 2024 Shai Seger * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + +#ifndef __solid_object_h__ +#define __solid_object_h__ +#include "SimShapes.h" +#include "linmath.h" +#include + +namespace MillSim +{ + +class SolidObject +{ +public: + SolidObject(); + virtual ~SolidObject(); + void SetPosition(vec3 position); + + /// Calls the display list. + virtual void render(); + Shape shape; + void GenerateSolid(std::vector & verts, std::vector& indices); + vec3 center = {}; + vec3 size = {}; + vec3 position = {}; + bool isValid = false; + +protected: + mat4x4 mModelMat; +}; +} // namespace MillSim + +#endif diff --git a/src/Mod/CAM/PathSimulator/AppGL/StockObject.cpp b/src/Mod/CAM/PathSimulator/AppGL/StockObject.cpp index 56db4f709a..1586448f69 100644 --- a/src/Mod/CAM/PathSimulator/AppGL/StockObject.cpp +++ b/src/Mod/CAM/PathSimulator/AppGL/StockObject.cpp @@ -22,34 +22,15 @@ #include "StockObject.h" #include "Shader.h" -#include #define NUM_PROFILE_POINTS 4 +using namespace MillSim; -MillSim::StockObject::StockObject() +StockObject::StockObject() { - mat4x4_identity(mModelMat); - vec3_set(center, 0, 0, 0); } -MillSim::StockObject::~StockObject() -{ - shape.FreeResources(); -} - -void MillSim::StockObject::render() -{ - // glCallList(mDisplayListId); - // UpdateObjColor(color); - shape.Render(mModelMat, mModelMat); // model is not rotated hence both are identity matrix -} - -void MillSim::StockObject::SetPosition(vec3 position) -{ - mat4x4_translate(mModelMat, position[0], position[1], position[2]); -} - -void MillSim::StockObject::GenerateBoxStock(float x, float y, float z, float l, float w, float h) +void StockObject::GenerateBoxStock(float x, float y, float z, float l, float w, float h) { int idx = 0; SET_DUAL(mProfile, idx, y + w, z + h); @@ -57,6 +38,7 @@ void MillSim::StockObject::GenerateBoxStock(float x, float y, float z, float l, SET_DUAL(mProfile, idx, y, z); SET_DUAL(mProfile, idx, y, z + h); + vec3_set(position, x, y, z); vec3_set(center, x + l / 2, y + w / 2, z + h / 2); vec3_set(size, l, w, h); diff --git a/src/Mod/CAM/PathSimulator/AppGL/StockObject.h b/src/Mod/CAM/PathSimulator/AppGL/StockObject.h index 0bca595669..d6926c8237 100644 --- a/src/Mod/CAM/PathSimulator/AppGL/StockObject.h +++ b/src/Mod/CAM/PathSimulator/AppGL/StockObject.h @@ -22,31 +22,22 @@ #ifndef __stock_object_h__ #define __stock_object_h__ -#include "SimShapes.h" +#include "SolidObject.h" #include "linmath.h" namespace MillSim { -class StockObject +class StockObject : public SolidObject { public: StockObject(); - virtual ~StockObject(); - - /// Calls the display list. - virtual void render(); - Shape shape; - void SetPosition(vec3 position); void GenerateBoxStock(float x, float y, float z, float l, float w, float h); - vec3 center = {}; - vec3 size = {}; private: float mProfile[8] = {}; - mat4x4 mModelMat; }; } // namespace MillSim -#endif \ No newline at end of file +#endif diff --git a/src/Mod/CAM/PathSimulator/AppGL/Texture.cpp b/src/Mod/CAM/PathSimulator/AppGL/Texture.cpp index 2a5572a4b2..6a46f8950c 100644 --- a/src/Mod/CAM/PathSimulator/AppGL/Texture.cpp +++ b/src/Mod/CAM/PathSimulator/AppGL/Texture.cpp @@ -27,11 +27,17 @@ namespace MillSim { Texture::~Texture() { - glDeleteTextures(1, &mTextureId); + DestroyTexture(); +} + +void Texture::DestroyTexture() +{ + GLDELETE_TEXTURE(mTextureId); } bool Texture::LoadImage(unsigned int* image, int _width, int _height) { + DestroyTexture(); width = _width; height = _height; glGenTextures(1, &mTextureId); @@ -58,4 +64,4 @@ bool Texture::unbind() return true; } -} // namespace MillSim \ No newline at end of file +} // namespace MillSim diff --git a/src/Mod/CAM/PathSimulator/AppGL/Texture.h b/src/Mod/CAM/PathSimulator/AppGL/Texture.h index c08c4c9f68..0d7def416b 100644 --- a/src/Mod/CAM/PathSimulator/AppGL/Texture.h +++ b/src/Mod/CAM/PathSimulator/AppGL/Texture.h @@ -33,6 +33,7 @@ public: Texture() {} ~Texture(); + void DestroyTexture(); bool LoadImage(unsigned int* image, int x, int y); bool Activate(); bool unbind(); @@ -51,7 +52,7 @@ public: protected: - unsigned int mTextureId = -1; + unsigned int mTextureId = 0; }; diff --git a/src/Mod/CAM/PathSimulator/AppGL/TextureLoader.cpp b/src/Mod/CAM/PathSimulator/AppGL/TextureLoader.cpp index c8ece95397..095db63843 100644 --- a/src/Mod/CAM/PathSimulator/AppGL/TextureLoader.cpp +++ b/src/Mod/CAM/PathSimulator/AppGL/TextureLoader.cpp @@ -36,6 +36,9 @@ TextureItem texItems[] = { {70, 50, 0, 0}, {27, 50, 0, 0}, {44, 50, 0, 0}, + {90, 50, 0, 0}, + {128, 50, 0, 0}, + {168, 50, 0, 0}, }; @@ -51,7 +54,7 @@ TextureLoader::TextureLoader(std::string imgFolder, std::vector fil if (mRawData == nullptr) { return; } - memset(mRawData, 0x80, buffsize); + memset(mRawData, 0x00, buffsize); for (std::size_t i = 0; i < fileNames.size(); i++) { QImage pixmap((imgFolder + fileNames[i]).c_str()); AddImage(&(texItems[i]), pixmap, mRawData, textureSize); diff --git a/src/Mod/CAM/PathSimulator/AppGL/linmath.h b/src/Mod/CAM/PathSimulator/AppGL/linmath.h index 2346ff2a47..7ebdc2cfba 100644 --- a/src/Mod/CAM/PathSimulator/AppGL/linmath.h +++ b/src/Mod/CAM/PathSimulator/AppGL/linmath.h @@ -1,12 +1,11 @@ -/*************************************************************************************************/ -/* Taken from https://github.com/datenwolf/linmath.h under 'Do whatever you want' license */ -/*************************************************************************************************/ +//************************************************************************************************* +//* Taken from https://github.com/datenwolf/linmath.h under 'Do whatever you want' license * +//************************************************************************************************* #ifndef LINMATH_H #define LINMATH_H #include #include -#include #ifdef LINMATH_NO_INLINE #define LINMATH_H_FUNC static @@ -630,5 +629,4 @@ LINMATH_H_FUNC void mat4x4_arcball(mat4x4 R, mat4x4 const M, vec2 const _a, vec2 float const angle = acosf(vec3_mul_inner(a_, b_)) * s; mat4x4_rotate(R, M, c_[0], c_[1], c_[2], angle); } - #endif