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 <chennes@gmail.com>
This commit is contained in:
Shai Seger
2024-08-21 23:18:52 +03:00
committed by GitHub
parent 80a09b3853
commit a750034490
42 changed files with 2744 additions and 1067 deletions

View File

@@ -135,6 +135,9 @@
<file>gl_simulator/SingleStep.png</file>
<file>gl_simulator/Slider.png</file>
<file>gl_simulator/Thumb.png</file>
<file>gl_simulator/Path.png</file>
<file>gl_simulator/View.png</file>
<file>gl_simulator/AmbientOclusion.png</file>
<file>preferences/Advanced.ui</file>
<file>preferences/PathDressupHoldingTags.ui</file>
<file>preferences/PathJob.ui</file>

Binary file not shown.

After

Width:  |  Height:  |  Size: 537 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -14,25 +14,6 @@
<string>Path Simulator</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QProgressBar" name="progressBar">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>5</height>
</size>
</property>
<property name="textVisible">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<property name="topMargin">

View File

@@ -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()

View File

@@ -20,10 +20,16 @@
* *
***************************************************************************/
#include "PreCompiled.h"
#include "PreCompiled.h" // NOLINT
#ifndef _PreComp_
#include <string>
#include <vector>
#endif
#include "CAMSim.h"
#include "DlgCAMSimulator.h"
#include <stdio.h>
#include <Mod/Part/App/BRepMesh.h>
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<float> toolProfilePoints,
void CAMSim::addTool(const std::vector<float> &toolProfilePoints,
int toolNumber,
float diameter,
float resolution)
@@ -65,6 +55,15 @@ void CAMSim::addTool(const std::vector<float> 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();

View File

@@ -27,10 +27,9 @@
#include <TopoDS_Shape.hxx>
#include <Mod/CAM/App/Command.h>
#include <Mod/Part/App/TopoShape.h>
#include <Mod/CAM/PathGlobal.h>
#include <Mod/Mesh/App/Mesh.h>
#include <Mod/CAM/App/Command.h>
#include <Mod/Part/App/TopoShape.h>
#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<float> toolProfilePoints,
void addTool(const std::vector<float> &toolProfilePoints,
int toolNumber,
float diameter,
float resolution);
void SetBaseShape(const Part::TopoShape& baseShape, float resolution);
void AddCommand(Command* cmd);
public:

View File

@@ -35,19 +35,28 @@
<UserDocu>
ResetSimulation():
Clear the simulation and all gcode commands
Clear the simulation and all gcode commands
</UserDocu>
</Documentation>
</Methode>
<Methode Name="AddTool" Keyword='true'>
<Documentation>
<UserDocu>
SetToolShape(shape, toolnumber, diameter, resolution):
AddTool(shape, toolnumber, diameter, resolution):
Set the shape of the tool to be used for simulation
</UserDocu>
</Documentation>
</Methode>
<Methode Name="SetBaseShape" Keyword='true'>
<Documentation>
<UserDocu>
SetBaseShape(shape, resolution):
Set the shape of the base object of the job
</UserDocu>
</Documentation>
</Methode>
<Methode Name="AddCommand">
<Documentation>
<UserDocu>

View File

@@ -73,7 +73,7 @@ PyObject* CAMSimPy::BeginSimulation(PyObject* args, PyObject* kwds)
return nullptr;
}
CAMSim* sim = getCAMSimPtr();
Part::TopoShape* stock = static_cast<Part::TopoShapePy*>(pObjStock)->getTopoShapePtr();
const Part::TopoShape& stock = *static_cast<Part::TopoShapePy*>(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<const char*, 3> 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<Part::TopoShapePy*>(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<Path::CommandPy*>(pObjCmd)->getCommandPtr();
sim->AddCommand(cmd);
Py_INCREF(Py_None);
return Py_None;
}

View File

@@ -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

View File

@@ -24,6 +24,7 @@
#include "DlgCAMSimulator.h"
#include "MillSimulation.h"
#include <Mod/Part/App/BRepMesh.h>
#include <QDateTime>
#include <QSurfaceFormat>
#include <QPoint>
@@ -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<float> 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<Vertex>& verts,
std::vector<GLushort>& indices)
{
std::vector<int> normalCount;
int nVerts = 0;
for (auto& shape : tshape.getSubTopoShapes(TopAbs_FACE)) {
std::vector<Base::Vector3d> points;
std::vector<Data::ComplexGeoData::Facet> facets;
shape.getFaces(points, facets, resolution);
std::vector<Base::Vector3d> normals(points.size());
std::vector<int> 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<Vertex> verts;
std::vector<GLushort> indices;
GetMeshData(shape, resolution, verts, indices);
mMillSimulator->SetArbitraryStock(verts, indices);
}
void DlgCAMSimulator::SetBaseShape(const Part::TopoShape& tshape, float resolution)
{
std::vector<Vertex> verts;
std::vector<GLushort> indices;
GetMeshData(tshape, resolution, verts, indices);
mMillSimulator->SetBaseObject(verts, indices);
}
DlgCAMSimulator* DlgCAMSimulator::mInstance = nullptr;
//************************************************************************************************************

View File

@@ -23,6 +23,7 @@
#ifndef PATHSIMULATOR_CAMSimulatorGui_H
#define PATHSIMULATOR_CAMSimulatorGui_H
#include <Mod/Part/App/TopoShape.h>
#include <QWindow>
#include <QOpenGLExtraFunctions>
#include <QPainter>
@@ -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<float> 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<MillSim::Vertex>& verts,
std::vector<GLushort>& 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;
};

View File

@@ -35,31 +35,28 @@ EndMill::EndMill(int toolid, float diameter)
EndMill::EndMill(const std::vector<float>& toolProfile, int toolid, float diameter)
: EndMill(toolid, diameter)
{
profilePoints = nullptr;
mHandleAllocation = false;
profilePoints.clear();
int srcBuffSize = static_cast<int>(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,

View File

@@ -35,7 +35,7 @@ namespace MillSim
class EndMill
{
public:
float* profilePoints = nullptr;
std::vector<float>profilePoints;
float radius;
int nPoints = 0;
int toolId = -1;
@@ -53,9 +53,6 @@ public:
protected:
void MirrorPointBuffer();
private:
bool mHandleAllocation = false;
};
} // namespace MillSim

View File

@@ -27,8 +27,6 @@
#endif
#include "GCodeParser.h"
#include <ctype.h>
#include <stdio.h>
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);

View File

@@ -45,8 +45,8 @@ public:
public:
std::vector<MillMotion> 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);

View File

@@ -25,6 +25,8 @@
namespace MillSim
{
int gWindowSizeW = 800;
int gWindowSizeH = 600;
int gDebug = -1;

View File

@@ -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__

View File

@@ -24,23 +24,24 @@
#include "OpenGlWrapper.h"
#include "MillSimulation.h"
#include <cstddef>
#include "GlUtils.h"
#include <stdlib.h>
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<std::string> 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);
}
}

View File

@@ -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;

View File

@@ -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

View File

@@ -0,0 +1,34 @@
#ifndef __millpathline_h__
#define __millpathline_h__
#include <vector>
namespace MillSim
{
struct MillPathPosition
{
float X, Y, Z;
int SegmentId;
};
class MillPathLine
{
public:
MillPathLine();
void GenerateModel();
void Clear();
void Render();
public:
std::vector<MillPathPosition> MillPathPointsBuffer;
protected:
unsigned int mVbo;
unsigned int mVao;
int mNumVerts;
};
} // namespace Millsim
#endif // !__millpathline_h__

View File

@@ -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<MillPathPosition>& 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;

View File

@@ -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<MillPathPosition>& 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:

File diff suppressed because it is too large Load Diff

View File

@@ -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 <sstream>
#include <vector>
#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<Vertex>& verts, std::vector<GLushort>& indices);
void SetBaseObject(std::vector<Vertex>& verts, std::vector<GLushort>& 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<EndMill*> mToolTable;
Shader shader3D, shaderInv3D, shaderFlat;
GCodeParser mCodeParser;
GuiDisplay guiDisplay;
SimDisplay simDisplay;
MillPathLine millPathLine;
std::vector<MillPathSegment*> 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

View File

@@ -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__

View File

@@ -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

View File

@@ -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__

View File

@@ -0,0 +1,556 @@
/***************************************************************************
* Copyright (c) 2024 Shai Seger <shaise at gmail> *
* *
* 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 <random>
#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<GLfloat> 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<Point3D> 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

View File

@@ -0,0 +1,138 @@
/***************************************************************************
* Copyright (c) 2024 Shai Seger <shaise at gmail> *
* *
* 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 <vector>
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<Point3D> mSsaoKernel;
unsigned int mSsaoFbo;
unsigned int mSsaoBlurFbo;
unsigned int mFboSsaoTexture;
unsigned int mFboSsaoBlurTexture;
unsigned int mFboSsaoNoiseTexture;
};
} // namespace MillSim
#endif // !__simdisplay_h__

View File

@@ -24,49 +24,39 @@
#include "Shader.h"
#include "GlUtils.h"
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include <cstddef>
#include <vector>
using namespace MillSim;
static float* sinTable = nullptr;
static float* cosTable = nullptr;
static int lastNumSlices = 0;
int Shape::lastNumSlices = 0;
std::vector<float> Shape::sinTable;
std::vector<float> 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<Vertex> vbuffer(numVerts);
std::vector<GLushort> 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<Vertex> vbuffer(numVerts);
std::vector<GLushort> 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<Vertex> vbuffer(numVerts);
std::vector<GLushort> 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<Vertex>& vbuffer, std::vector<GLushort>& 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();
}

View File

@@ -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<Vertex>& vbuffer, std::vector<GLushort>& 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<float> sinTable;
static std::vector<float> 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

View File

@@ -0,0 +1,79 @@
/***************************************************************************
* Copyright (c) 2024 Shai Seger <shaise at gmail> *
* *
* 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 <algorithm>
#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<Vertex>& verts, std::vector<GLushort>& 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;
}

View File

@@ -0,0 +1,53 @@
/***************************************************************************
* Copyright (c) 2024 Shai Seger <shaise at gmail> *
* *
* 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 <vector>
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<Vertex> & verts, std::vector<GLushort>& indices);
vec3 center = {};
vec3 size = {};
vec3 position = {};
bool isValid = false;
protected:
mat4x4 mModelMat;
};
} // namespace MillSim
#endif

View File

@@ -22,34 +22,15 @@
#include "StockObject.h"
#include "Shader.h"
#include <stdlib.h>
#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);

View File

@@ -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
#endif

View File

@@ -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
} // namespace MillSim

View File

@@ -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;
};

View File

@@ -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<std::string> 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);

View File

@@ -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 <string.h>
#include <math.h>
#include <string.h>
#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