New Cam simulator based on low level OpenGL functions (faster and more precise) (#13884)

* Initial opengl test window

* added core files

* some fixes for code comparability with other platforms

* more compatibility cleanup

* add missing opengl libraries

* Basic simulation window works!

* try using different define

* fix wrapper for better compatibility

* Gui is now operational

* Finally SIM works on actual freecad models

* support drill commands

* cleanup python script and add tool profile generation

* Now using actual tools specified in the operation.

* support mouse wheel and freecad-style 3d navigation

* Support accuracy gauge

* remove endsimulation reference

* show simulation speed indicator

* apply clang-format

* apply changes suggested by code review

* gui items are loaded via QT resource system instead of hard coded

* use vector instead of pointer to pass tool profile points

* Fix some more formatting errors.
This commit is contained in:
Shai Seger
2024-05-22 18:16:34 +03:00
committed by GitHub
parent d119dc574b
commit ff40ef3586
55 changed files with 6427 additions and 5 deletions

View File

@@ -96,6 +96,7 @@ SET(PathPythonMainGui_SRCS
Path/Main/Gui/PreferencesJob.py
Path/Main/Gui/SanityCmd.py
Path/Main/Gui/Simulator.py
Path/Main/Gui/SimulatorGL.py
)
SET(PathPythonMainSanity_SRCS

View File

@@ -49,6 +49,7 @@
<file>icons/CAM_Shape.svg</file>
<file>icons/CAM_SimpleCopy.svg</file>
<file>icons/CAM_Simulator.svg</file>
<file>icons/CAM_SimulatorGL.svg</file>
<file>icons/CAM_Slot.svg</file>
<file>icons/CAM_Stop.svg</file>
<file>icons/CAM_ThreadMilling.svg</file>
@@ -121,7 +122,19 @@
<file>panels/ToolBitSelector.ui</file>
<file>panels/TaskPathCamoticsSim.ui</file>
<file>panels/TaskPathSimulator.ui</file>
<file>panels/TaskCAMSimulator.ui</file>
<file>panels/ZCorrectEdit.ui</file>
<file>gl_simulator/0.png</file>
<file>gl_simulator/1.png</file>
<file>gl_simulator/4.png</file>
<file>gl_simulator/X.png</file>
<file>gl_simulator/Faster.png</file>
<file>gl_simulator/Pause.png</file>
<file>gl_simulator/Play.png</file>
<file>gl_simulator/Rotate.png</file>
<file>gl_simulator/SingleStep.png</file>
<file>gl_simulator/Slider.png</file>
<file>gl_simulator/Thumb.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: 952 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 456 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 826 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 980 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 359 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 697 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 796 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 263 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 573 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 874 B

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 32 KiB

View File

@@ -0,0 +1,154 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>TaskPathSimulator</class>
<widget class="QDialog" name="TaskPathSimulator">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>288</width>
<height>335</height>
</rect>
</property>
<property name="windowTitle">
<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">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="label_4">
<property name="minimumSize">
<size>
<width>50</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Accuracy:</string>
</property>
</widget>
</item>
<item>
<widget class="QSlider" name="sliderAccuracy">
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>10</number>
</property>
<property name="pageStep">
<number>2</number>
</property>
<property name="value">
<number>10</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="labelAccuracy">
<property name="minimumSize">
<size>
<width>50</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>40</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string notr="true">%</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="label">
<property name="maximumSize">
<size>
<width>24</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>Job:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="comboJobs"/>
</item>
<item>
<widget class="QToolButton" name="toolButtonPlay">
<property name="toolTip">
<string>Activate / resume simulation</string>
</property>
<property name="text">
<string>Play</string>
</property>
<property name="icon">
<iconset resource="../Path.qrc">
<normaloff>:/icons/CAM_BPlay.svg</normaloff>:/icons/CAM_BPlay.svg</iconset>
</property>
<property name="iconSize">
<size>
<width>32</width>
<height>32</height>
</size>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QListWidget" name="listOperations">
<property name="selectionMode">
<enum>QAbstractItemView::NoSelection</enum>
</property>
</widget>
</item>
</layout>
</widget>
<resources>
<include location="../Path.qrc"/>
</resources>
<connections/>
<slots>
<slot>SimStop()</slot>
<slot>SimPlay()</slot>
<slot>SimPause()</slot>
<slot>SimStep()</slot>
<slot>SimFF()</slot>
</slots>
</ui>

View File

@@ -97,6 +97,7 @@ class CAMWorkbench(Workbench):
toolcmdlist = [
"CAM_Inspect",
"CAM_Simulator",
"CAM_SimulatorGL",
"CAM_SelectLoop",
"CAM_OpActiveToggle",
]

View File

@@ -53,6 +53,7 @@ def Startup():
from Path.Main.Gui import Fixture
from Path.Main.Gui import Inspect
from Path.Main.Gui import Simulator
from Path.Main.Gui import SimulatorGL
from Path.Main.Sanity import Sanity

View File

@@ -0,0 +1,309 @@
# -*- coding: utf-8 -*-
# ***************************************************************************
# * Copyright (c) 2017 Shai Seger <shaise at gmail> *
# * *
# * This program is free software; you can redistribute it and/or modify *
# * it under the terms of the GNU Lesser General Public License (LGPL) *
# * as published by the Free Software Foundation; either version 2 of *
# * the License, or (at your option) any later version. *
# * for detail see the LICENCE text file. *
# * *
# * This program 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 program; if not, write to the Free Software *
# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
# * USA *
# * *
# ***************************************************************************
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
from FreeCAD import Vector, Base
from PySide2.QtWidgets import QDialogButtonBox
# lazily loaded modules
from lazy_loader.lazy_loader import LazyLoader
Mesh = LazyLoader("Mesh", globals(), "Mesh")
Part = LazyLoader("Part", globals(), "Part")
if FreeCAD.GuiUp:
import FreeCADGui
from PySide import QtGui, QtCore
_filePath = os.path.dirname(os.path.abspath(__file__))
def IsSame(x, y):
return abs(x - y) < 0.0001
def RadiusAt(edge, p):
x = edge.valueAt(p).x
y = edge.valueAt(p).y
return math.sqrt(x * x + y * y)
class CAMSimTaskUi:
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):
return QDialogButtonBox.Close
def reject(self):
self.parent.cancel()
FreeCADGui.Control.closeDialog()
def TSError(msg):
QtGui.QMessageBox.information(None, "Path Simulation", msg)
class CAMSimulation:
def __init__(self):
self.debug = False
self.stdrot = FreeCAD.Rotation(Vector(0, 0, 1), 0)
self.iprogress = 0
self.numCommands = 0
self.simperiod = 20
self.quality = 10
self.resetSimulation = False
self.jobs = []
def Connect(self, but, sig):
QtCore.QObject.connect(but, QtCore.SIGNAL("clicked()"), sig)
## Convert tool shape to tool profile needed by GL simulator
def FindClosestEdge(self, edges, px, pz):
for edge in edges:
p1 = edge.FirstParameter
p2 = edge.LastParameter
rad = RadiusAt(edge, p1)
z = edge.valueAt(p1).z
if IsSame(px, rad) and IsSame(pz, z):
return edge, p1, p2
rad = RadiusAt(edge, p2)
z = edge.valueAt(p2).z
if IsSame(px, rad) and IsSame(pz, z):
return edge, p2, p1
return None, 0.0, 0.0
def FindTopMostEdge(self, edges):
maxz = 0.0
topedge = None
top_p1 = 0.0
top_p2 = 0.0
for edge in edges:
p1 = edge.FirstParameter
p2 = edge.LastParameter
z = edge.valueAt(p1).z
if z > maxz:
topedge = edge
top_p1 = p1
top_p2 = p2
maxz = z
z = edge.valueAt(p2).z
if z > maxz:
topedge = edge
top_p1 = p2
top_p2 = p1
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):
shape = tool.Shape
sideEdgeList = []
for i in range(len(shape.Edges)):
edge = shape.Edges[i]
if not edge.isClosed():
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
profile = [0.0, 0.0]
# first find the topmost edge
edge, p1, p2 = self.FindTopMostEdge(sideEdgeList)
profile = [RadiusAt(edge, p1), edge.valueAt(p1).z]
endrad = 0.0
# one by one find all connecting edges
while edge is not None:
sideEdgeList.remove(edge)
if type(edge.Curve) is Part.Circle:
# if edge is curved, aproximate 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
profile.append(endrad)
profile.append(endz)
location += step
nsegments -= 1
else:
endrad = RadiusAt(edge, p2)
endz = edge.valueAt(p2).z
profile.append(endrad)
profile.append(endz)
edge, p1, p2 = self.FindClosestEdge(sideEdgeList, endrad, endz)
if edge is None:
break
return profile
def Activate(self):
self.initdone = False
self.taskForm = CAMSimTaskUi(self)
form = self.taskForm.form
self.Connect(form.toolButtonPlay, self.SimPlay)
form.sliderAccuracy.valueChanged.connect(self.onAccuracyBarChange)
self.onAccuracyBarChange()
self._populateJobSelection(form)
form.comboJobs.currentIndexChanged.connect(self.onJobChange)
self.onJobChange()
FreeCADGui.Control.showDialog(self.taskForm)
self.disableAnim = False
self.firstDrill = True
self.millSim = CAMSimulator.PathSim()
self.initdone = True
self.job = self.jobs[self.taskForm.form.comboJobs.currentIndex()]
self.SetupSimulation()
def _populateJobSelection(self, form):
# Make Job selection combobox
setJobIdx = 0
jobName = ""
jIdx = 0
# Get list of Job objects in active document
jobList = FreeCAD.ActiveDocument.findObjects("Path::FeaturePython", "Job.*")
jCnt = len(jobList)
# Check if user has selected a specific job for simulation
guiSelection = FreeCADGui.Selection.getSelectionEx()
if guiSelection: # Identify job selected by user
sel = guiSelection[0]
if hasattr(sel.Object, "Proxy") and isinstance(
sel.Object.Proxy, PathJob.ObjectJob
):
jobName = sel.Object.Name
FreeCADGui.Selection.clearSelection()
# populate the job selection combobox
form.comboJobs.blockSignals(True)
form.comboJobs.clear()
form.comboJobs.blockSignals(False)
for j in jobList:
form.comboJobs.addItem(j.ViewObject.Icon, j.Label)
self.jobs.append(j)
if j.Name == jobName or jCnt == 1:
setJobIdx = jIdx
jIdx += 1
# Pre-select GUI-selected job in the combobox
if jobName or jCnt == 1:
form.comboJobs.setCurrentIndex(setJobIdx)
else:
form.comboJobs.setCurrentIndex(0)
def SetupSimulation(self):
form = self.taskForm.form
self.activeOps = []
self.numCommands = 0
self.ioperation = 0
for i in range(form.listOperations.count()):
if form.listOperations.item(i).checkState() == QtCore.Qt.CheckState.Checked:
self.firstDrill = True
self.activeOps.append(self.operations[i])
self.numCommands += len(self.operations[i].Path.Commands)
self.stock = self.job.Stock.Shape
self.busy = False
def onJobChange(self):
form = self.taskForm.form
j = self.jobs[form.comboJobs.currentIndex()]
self.job = j
form.listOperations.clear()
self.operations = []
for op in j.Operations.OutList:
if PathUtil.opProperty(op, "Active"):
listItem = QtGui.QListWidgetItem(op.ViewObject.Icon, op.Label)
listItem.setFlags(listItem.flags() | QtCore.Qt.ItemIsUserCheckable)
listItem.setCheckState(QtCore.Qt.CheckState.Checked)
self.operations.append(op)
form.listOperations.addItem(listItem)
if self.initdone:
self.SetupSimulation()
def onAccuracyBarChange(self):
form = self.taskForm.form
self.quality = form.sliderAccuracy.value()
qualText = QtCore.QT_TRANSLATE_NOOP("CAM_Simulator", "High")
if (self.quality < 4):
qualText = QtCore.QT_TRANSLATE_NOOP("CAM_Simulator", "Low")
elif (self.quality < 9):
qualText = QtCore.QT_TRANSLATE_NOOP("CAM_Simulator", "Medium")
form.labelAccuracy.setText(qualText)
def SimPlay(self):
self.millSim.ResetSimulation()
for op in self.activeOps:
tool = PathDressup.toolController(op).Tool
toolNumber = PathDressup.toolController(op).ToolNumber
toolProfile = self.GetToolProfile(tool, 0.5)
self.millSim.AddTool(toolProfile, toolNumber, tool.Diameter, 1)
opCommands = PathUtils.getPathWithPlacement(op).Commands
for cmd in opCommands:
self.millSim.AddCommand(cmd)
self.millSim.BeginSimulation(self.stock, self.quality)
def cancel(self):
#self.EndSimulation()
pass
class CommandCAMSimulate:
def GetResources(self):
return {
"Pixmap": "CAM_SimulatorGL",
"MenuText": QtCore.QT_TRANSLATE_NOOP("CAM_Simulator", "New CAM Simulator"),
"Accel": "P, N",
"ToolTip": QtCore.QT_TRANSLATE_NOOP(
"CAM_Simulator", "Simulate G-code on stock"
),
}
def IsActive(self):
if FreeCAD.ActiveDocument is not None:
for o in FreeCAD.ActiveDocument.Objects:
if o.Name[:3] == "Job":
return True
return False
def Activated(self):
CamSimulation = CAMSimulation()
CamSimulation.Activate()
if FreeCAD.GuiUp:
# register the FreeCAD command
FreeCADGui.addCommand("CAM_SimulatorGL", CommandCAMSimulate())
FreeCAD.Console.PrintLog("Loading PathSimulator Gui... done\n")

View File

@@ -47,9 +47,18 @@
// PathSimulator
#ifndef PathSimulatorExport
#ifdef PathSimulator_EXPORTS
# define PathSimulatorExport FREECAD_DECL_EXPORT
#define PathSimulatorExport FREECAD_DECL_EXPORT
#else
# define PathSimulatorExport FREECAD_DECL_IMPORT
#define PathSimulatorExport FREECAD_DECL_IMPORT
#endif
#endif
// CAMSimulator (new GL simulator)
#ifndef CAMSimulatorExport
#ifdef CAMSimulator_EXPORTS
#define CAMSimulatorExport FREECAD_DECL_EXPORT
#else
#define CAMSimulatorExport FREECAD_DECL_IMPORT
#endif
#endif

View File

@@ -0,0 +1,85 @@
/***************************************************************************
* Copyright (c) 2017 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 "PreCompiled.h"
#include <Base/Console.h>
#include <Base/Interpreter.h>
#include "CAMSim.h"
#include "CAMSimPy.h"
namespace CAMSimulator
{
class Module: public Py::ExtensionModule<Module>
{
public:
Module()
: Py::ExtensionModule<Module>("CAMSimulator")
{
initialize("This module is the CAMSimulator module."); // register with Python
}
~Module() override
{}
private:
};
PyObject* initModule()
{
return Base::Interpreter().addModule(new Module);
}
} // namespace CAMSimulator
/* Python entry */
PyMOD_INIT_FUNC(CAMSimulator)
{
// load dependent module
try {
Base::Interpreter().runString("import Part");
Base::Interpreter().runString("import Path");
Base::Interpreter().runString("import Mesh");
}
catch (const Base::Exception& e) {
PyErr_SetString(PyExc_ImportError, e.what());
PyMOD_Return(nullptr);
}
//
PyObject* mod = CAMSimulator::initModule();
Base::Console().Log("Loading CAMSimulator module.... done\n");
// Add Types to module
Base::Interpreter().addType(&CAMSimulator::CAMSimPy::Type, mod, "PathSim");
// NOTE: To finish the initialization of our own type objects we must
// call PyType_Ready, otherwise we run into a segmentation fault, later on.
// This function is responsible for adding inherited slots from a type's base class.
CAMSimulator::CAMSim::init();
PyMOD_Return(mod);
}

View File

@@ -0,0 +1,72 @@
/***************************************************************************
* Copyright (c) 2017 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 "PreCompiled.h"
#include "CAMSim.h"
#include "DlgCAMSimulator.h"
#include <stdio.h>
using namespace Base;
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)
{
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);
}
void CAMSimulator::CAMSim::resetSimulation()
{
DlgCAMSimulator::GetInstance()->resetSimulation();
}
void CAMSim::addTool(const std::vector<float> toolProfilePoints,
int toolNumber,
float diameter,
float resolution)
{
DlgCAMSimulator::GetInstance()->addTool(toolProfilePoints, toolNumber, diameter, resolution);
}
void CAMSim::AddCommand(Command* cmd)
{
std::string gline = cmd->toGCode();
DlgCAMSimulator::GetInstance()->addGcodeCommand(gline.c_str());
}

View File

@@ -0,0 +1,77 @@
/***************************************************************************
* Copyright (c) 2017 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 CAMSimulator_CAMSim_H
#define CAMSimulator_CAMSim_H
#include <memory>
#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 "DlgCAMSimulator.h"
using namespace Path;
namespace CAMSimulator
{
/** The representation of a CNC Toolpath Simulator */
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);
private:
static Base::Type classTypeId;
public:
CAMSim();
~CAMSim();
void BeginSimulation(Part::TopoShape* stock, float resolution);
void resetSimulation();
void addTool(const std::vector<float> toolProfilePoints,
int toolNumber,
float diameter,
float resolution);
void AddCommand(Command* cmd);
public:
std::unique_ptr<SimStock> m_stock;
};
} // namespace CAMSimulator
#endif // CAMSimulator_CAMSim_H

View File

@@ -0,0 +1,61 @@
<?xml version="1.0" encoding="UTF-8"?>
<GenerateModel xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="generateMetaModel_Module.xsd">
<PythonExport
Father="BaseClassPy"
Name="CAMSimPy"
Twin="CAMSim"
TwinPointer="CAMSim"
Include="Mod/CAM/PathSimulator/AppGL/CAMSim.h"
Namespace="CAMSimulator"
FatherInclude="Base/BaseClassPy.h"
FatherNamespace="Base"
Constructor="true"
Delete="true">
<Documentation>
<Author Licence="LGPL" Name="Shai Seger" EMail="shaise_at_g-mail" />
<UserDocu>
FreeCAD python wrapper of CAMSimulator
CAMSimulator.CAMSim():
Create a path simulator object
</UserDocu>
</Documentation>
<Methode Name="BeginSimulation" Keyword='true'>
<Documentation>
<UserDocu>
BeginSimulation(stock, resolution):
Start a simulation process on a box shape stock with given resolution
</UserDocu>
</Documentation>
</Methode>
<Methode Name="ResetSimulation">
<Documentation>
<UserDocu>
ResetSimulation():
Clear the simulation and all gcode commands
</UserDocu>
</Documentation>
</Methode>
<Methode Name="AddTool" Keyword='true'>
<Documentation>
<UserDocu>
SetToolShape(shape, toolnumber, diameter, resolution):
Set the shape of the tool to be used for simulation
</UserDocu>
</Documentation>
</Methode>
<Methode Name="AddCommand">
<Documentation>
<UserDocu>
AddCommand(command):
Add a path command to the simulation.
</UserDocu>
</Documentation>
</Methode>
</PythonExport>
</GenerateModel>

View File

@@ -0,0 +1,128 @@
/**************************************************************************
* Copyright (c) 2017 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 "PreCompiled.h"
#include <Base/PlacementPy.h>
#include <Base/PyWrapParseTupleAndKeywords.h>
#include <Mod/Mesh/App/MeshPy.h>
#include <Mod/CAM/App/CommandPy.h>
#include <Mod/Part/App/TopoShapePy.h>
// inclusion of the generated files (generated out of CAMSimPy.xml)
#include "CAMSimPy.h"
#include "CAMSimPy.cpp"
using namespace CAMSimulator;
// returns a string which represents the object e.g. when printed in python
std::string CAMSimPy::representation() const
{
return std::string("<CAMSim object>");
}
PyObject* CAMSimPy::PyMake(struct _typeobject*, PyObject*, PyObject*) // Python wrapper
{
// create a new instance of CAMSimPy and the Twin object
return new CAMSimPy(new CAMSim);
}
// constructor method
int CAMSimPy::PyInit(PyObject* /*args*/, PyObject* /*kwd*/)
{
return 0;
}
PyObject* CAMSimPy::ResetSimulation(PyObject* args)
{
CAMSim* sim = getCAMSimPtr();
sim->resetSimulation();
Py_IncRef(Py_None);
return Py_None;
}
PyObject* CAMSimPy::BeginSimulation(PyObject* args, PyObject* kwds)
{
static const std::array<const char*, 3> kwlist {"stock", "resolution", nullptr};
PyObject* pObjStock;
float resolution;
if (!Base::Wrapped_ParseTupleAndKeywords(args, kwds,"O!f",
kwlist, &(Part::TopoShapePy::Type), &pObjStock, &resolution)) {
return nullptr;
}
CAMSim* sim = getCAMSimPtr();
Part::TopoShape* stock = static_cast<Part::TopoShapePy*>(pObjStock)->getTopoShapePtr();
sim->BeginSimulation(stock, resolution);
Py_IncRef(Py_None);
return Py_None;
}
PyObject* CAMSimPy::AddTool(PyObject* args, PyObject* kwds)
{
static const std::array<const char*, 5> kwlist {
"shape", "toolnumber", "diameter", "resolution", nullptr};
PyObject* pObjToolShape;
int toolNumber;
float resolution;
float diameter;
if (!Base::Wrapped_ParseTupleAndKeywords(args, kwds, "Oiff", kwlist, &pObjToolShape,
&toolNumber, &diameter, &resolution)) {
return nullptr;
}
// The tool shape is defined by a list of 2d points that represents the tool revolving profile
Py_ssize_t num_floats = PyList_Size(pObjToolShape);
std::vector<float> toolProfile;
for (Py_ssize_t i = 0; i < num_floats; ++i) {
PyObject* item = PyList_GetItem(pObjToolShape, i);
toolProfile.push_back(static_cast<float>(PyFloat_AsDouble(item)));
}
CAMSim* sim = getCAMSimPtr();
sim->addTool(toolProfile, toolNumber, diameter, resolution);
return Py_None;
}
PyObject* CAMSimPy::AddCommand(PyObject* args)
{
PyObject* pObjCmd;
if (!PyArg_ParseTuple(args, "O!", &(Path::CommandPy::Type), &pObjCmd)) {
return nullptr;
}
CAMSim* sim = getCAMSimPtr();
Path::Command* cmd = static_cast<Path::CommandPy*>(pObjCmd)->getCommandPtr();
sim->AddCommand(cmd);
return Py_None;
}
PyObject* CAMSimPy::getCustomAttributes(const char* /*attr*/) const
{
return nullptr;
}
int CAMSimPy::setCustomAttributes(const char* /*attr*/, PyObject* /*obj*/)
{
return 0;
}

View File

@@ -0,0 +1,103 @@
include_directories(
${CMAKE_BINARY_DIR}
${CMAKE_SOURCE_DIR}/src
${CMAKE_BINARY_DIR}/src
${CMAKE_CURRENT_BINARY_DIR}
${Boost_INCLUDE_DIRS}
${COIN3D_INCLUDE_DIRS}
${ZLIB_INCLUDE_DIR}
${OCC_INCLUDE_DIR}
${EIGEN3_INCLUDE_DIR}
${PYTHON_INCLUDE_DIRS}
${XercesC_INCLUDE_DIRS}
)
link_directories(${OCC_LIBRARY_DIR})
set(CAMSimulator_LIBS
Path
Part
Mesh
FreeCADApp
FreeCADGui
${QtOpenGL_LIBRARIES}
${OPENGL_gl_LIBRARY}
)
SET(CAMSimulator_SRCS_Python
CAMSimPy.xml
CAMSimPyImp.cpp
)
SET(CAMSimulator_SRCS_Module
AppCAMSimulator.cpp
CAMSim.cpp
CAMSim.h
CAMSimPyImp.cpp
DlgCAMSimulator.cpp
DlgCAMSimulator.h
PreCompiled.cpp
PreCompiled.h
)
SET(CAMSimulator_SRCS_Core
EndMill.cpp
EndMill.h
GCodeParser.cpp
GCodeParser.h
GlUtils.cpp
GlUtils.h
GuiDisplay.cpp
GuiDisplay.h
linmath.h
MillMotion.h
MillPathSegment.cpp
MillPathSegment.h
MillSimulation.cpp
MillSimulation.h
OpenGlWrapper.h
Shader.cpp
Shader.h
SimShapes.cpp
SimShapes.h
StockObject.cpp
StockObject.h
Texture.cpp
Texture.h
TextureLoader.cpp
TextureLoader.h
)
generate_from_xml(CAMSimPy)
SOURCE_GROUP("Module" FILES ${CAMSimulator_SRCS_Module})
SOURCE_GROUP("Python" FILES ${CAMSimulator_SRCS_Python})
SOURCE_GROUP("Core" FILES ${CAMSimulator_SRCS_Core})
SET(CAMSimulator_SRCS_precomp
${CAMSimulator_SRCS_Module}
${CAMSimulator_SRCS_Python}
)
SET(CAMSimulator_SRCS
${CAMSimulator_SRCS_precomp}
${CAMSimulator_SRCS_Core}
)
if(FREECAD_USE_PCH)
add_definitions(-D_PreComp_)
GET_MSVC_PRECOMPILED_SOURCE("PreCompiled.cpp" PCH_SRCS ${CAMSimulator_SRCS_precomp})
ADD_MSVC_PRECOMPILED_HEADER(CAMSimulator PreCompiled.h PreCompiled.cpp PCH_SRCS)
endif(FREECAD_USE_PCH)
add_library(CAMSimulator SHARED ${CAMSimulator_SRCS})
target_link_libraries(CAMSimulator ${CAMSimulator_LIBS})
SET_BIN_DIR(CAMSimulator CAMSimulator /Mod/CAM)
SET_PYTHON_PREFIX_SUFFIX(CAMSimulator)
INSTALL(TARGETS CAMSimulator DESTINATION ${CMAKE_INSTALL_LIBDIR})

View File

@@ -0,0 +1,240 @@
/***************************************************************************
* 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 "PreCompiled.h"
#include "DlgCAMSimulator.h"
#include "MillSimulation.h"
#include <QtGui/QMatrix4x4>
#include <QtGui/qscreen.h>
#include <QDateTime>
#include <QSurfaceFormat>
#include <QMouseEvent>
#include <QWheelEvent>
#include <QPoint>
using namespace CAMSimulator;
using namespace MillSim;
QOpenGLContext* gOpenGlContext;
using namespace MillSim;
namespace CAMSimulator
{
DlgCAMSimulator::DlgCAMSimulator(QWindow* parent)
: QWindow(parent)
{
setSurfaceType(QWindow::OpenGLSurface);
mMillSimulator = new MillSimulation();
}
void DlgCAMSimulator::render(QPainter* painter)
{
Q_UNUSED(painter);
}
void DlgCAMSimulator::render()
{
mMillSimulator->ProcessSim((unsigned int)(QDateTime::currentMSecsSinceEpoch()));
}
void DlgCAMSimulator::renderLater()
{
requestUpdate();
}
bool DlgCAMSimulator::event(QEvent* event)
{
switch (event->type()) {
case QEvent::UpdateRequest:
renderNow();
return true;
default:
return QWindow::event(event);
}
}
void DlgCAMSimulator::exposeEvent(QExposeEvent* event)
{
Q_UNUSED(event);
if (isExposed()) {
renderNow();
}
}
void DlgCAMSimulator::mouseMoveEvent(QMouseEvent* ev)
{
mMillSimulator->MouseMove(ev->x(), ev->y());
}
void DlgCAMSimulator::mousePressEvent(QMouseEvent* ev)
{
mMillSimulator->MousePress(ev->button(), true, ev->x(), ev->y());
}
void DlgCAMSimulator::mouseReleaseEvent(QMouseEvent* ev)
{
mMillSimulator->MousePress(ev->button(), false, ev->x(), ev->y());
}
void DlgCAMSimulator::wheelEvent(QWheelEvent* ev)
{
mMillSimulator->MouseScroll((float)ev->angleDelta().y() / 120.0f);
}
void DlgCAMSimulator::resetSimulation()
{
mMillSimulator->Clear();
}
void DlgCAMSimulator::addGcodeCommand(const char* cmd)
{
mMillSimulator->AddGcodeLine(cmd);
}
void DlgCAMSimulator::addTool(const std::vector<float> toolProfilePoints,
int toolNumber,
float diameter,
float resolution)
{
std::string toolCmd = "T" + std::to_string(toolNumber);
mMillSimulator->AddGcodeLine(toolCmd.c_str());
if (!mMillSimulator->ToolExists(toolNumber)) {
mMillSimulator->AddTool(toolProfilePoints, toolNumber, diameter);
}
}
void DlgCAMSimulator::hideEvent(QHideEvent* ev)
{
mAnimating = false;
}
void DlgCAMSimulator::startSimulation(const SimStock* stock, float quality)
{
mStock = *stock;
mQuality = quality;
mNeedsInitialize = true;
show();
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);
}
void DlgCAMSimulator::checkInitialization()
{
if (!mContext) {
mContext = new QOpenGLContext(this);
mContext->setFormat(requestedFormat());
mContext->create();
gOpenGlContext = mContext;
mNeedsInitialize = true;
}
mContext->makeCurrent(this);
if (mNeedsInitialize) {
initializeOpenGLFunctions();
initialize();
mNeedsInitialize = false;
}
}
void DlgCAMSimulator::renderNow()
{
static unsigned int lastTime = 0;
static int frameCount = 0;
static int fps = 0;
if (!isExposed()) {
return;
}
checkInitialization();
frameCount++;
unsigned int curtime = QDateTime::currentMSecsSinceEpoch();
unsigned int timediff = curtime - lastTime;
if (timediff > 10000) {
fps = frameCount * 1000 / timediff; // for debug only. not used otherwise.
lastTime = curtime;
frameCount = 0;
}
render();
mContext->swapBuffers(this);
if (mAnimating) {
renderLater();
}
}
void DlgCAMSimulator::setAnimating(bool animating)
{
mAnimating = animating;
if (animating) {
renderLater();
}
}
DlgCAMSimulator* DlgCAMSimulator::GetInstance()
{
if (mInstance == nullptr) {
QSurfaceFormat format;
format.setSamples(16);
format.setSwapInterval(2);
mInstance = new DlgCAMSimulator();
mInstance->setFormat(format);
mInstance->resize(800, 600);
mInstance->setModality(Qt::ApplicationModal);
mInstance->show();
}
return mInstance;
}
DlgCAMSimulator* DlgCAMSimulator::mInstance = nullptr;
//************************************************************************************************************
// stock
//************************************************************************************************************
SimStock::SimStock(float px, float py, float pz, float lx, float ly, float lz, float res)
: mPx(px)
, mPy(py)
, mPz(pz + 0.005 * lz)
, mLx(lx)
, mLy(ly)
, mLz(1.01 * lz)
{}
SimStock::~SimStock()
{}
} // namespace CAMSimulator

View File

@@ -0,0 +1,110 @@
/***************************************************************************
* Copyright (c) 2017 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 PATHSIMULATOR_CAMSimulatorGui_H
#define PATHSIMULATOR_CAMSimulatorGui_H
// #include <memory>
// #include <TopoDS_Shape.hxx>
// #include <Mod/CAM/App/Command.h>
// #include <Mod/Part/App/TopoShape.h>
// #include <Mod/CAM/PathGlobal.h>
#include <QtGui/qwindow.h>
#include <QOpenGLExtraFunctions>
#include <QtGui/qpainter.h>
#include <QtGui/qopenglpaintdevice.h>
namespace MillSim
{
class MillSimulation; // use short declaration as using 'include' causes a header loop
}
namespace CAMSimulator
{
struct SimStock
{
public:
SimStock(float px, float py, float pz, float lx, float ly, float lz, float res);
~SimStock();
public:
float mPx, mPy, mPz; // stock zero position
float mLx, mLy, mLz; // stock dimensions
};
class DlgCAMSimulator: public QWindow, public QOpenGLExtraFunctions
{
Q_OBJECT
public:
explicit DlgCAMSimulator(QWindow* parent = nullptr);
~DlgCAMSimulator()
{}
virtual void render(QPainter* painter);
virtual void render();
virtual void initialize();
void setAnimating(bool animating);
static DlgCAMSimulator* GetInstance();
public: // slots:
void renderLater();
void renderNow();
void startSimulation(const SimStock* stock, float quality);
void resetSimulation();
void addGcodeCommand(const char* cmd);
void addTool(const std::vector<float> toolProfilePoints,
int toolNumber,
float diameter,
float resolution);
protected:
bool event(QEvent* event) override;
void checkInitialization();
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;
private:
bool mAnimating = false;
bool mNeedsInitialize = false;
QOpenGLContext* mContext = nullptr;
QOpenGLPaintDevice* mDevice = nullptr;
MillSim::MillSimulation* mMillSimulator = nullptr;
static DlgCAMSimulator* mInstance;
SimStock mStock = {0, 0, 0, 1, 1, 1, 1};
float mQuality = 10;
};
} // namespace CAMSimulator
#endif // PATHSIMULATOR_PathSim_H

View File

@@ -0,0 +1,124 @@
/***************************************************************************
* 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 "EndMill.h"
#include "OpenGlWrapper.h"
#include "SimShapes.h"
using namespace MillSim;
EndMill::EndMill(int toolid, float diameter)
{
radius = diameter / 2;
toolId = toolid;
}
EndMill::EndMill(const std::vector<float>& toolProfile, int toolid, float diameter)
: EndMill(toolid, diameter)
{
profilePoints = nullptr;
mHandleAllocation = false;
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;
if (missingCenterPoint) {
nPoints++;
}
int buffSize = PROFILE_BUFFER_SIZE(nPoints);
profilePoints = new float[buffSize];
if (profilePoints == nullptr) {
return;
}
// 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
}
if (missingCenterPoint) {
profilePoints[srcBuffSize] = profilePoints[srcBuffSize + 1] = 0.0f;
}
MirrorPointBuffer();
}
EndMill::~EndMill()
{
toolShape.FreeResources();
halfToolShape.FreeResources();
pathShape.FreeResources();
if (mHandleAllocation) {
delete[] profilePoints;
}
}
void EndMill::GenerateDisplayLists(float quality)
{
// calculate number of slices based on quality.
int nslices = 16;
if (quality < 3) {
nslices = 4;
}
else if (quality < 7) {
nslices = 8;
}
// full tool
toolShape.RotateProfile(profilePoints, nPoints, 0, 0, nslices, false);
// half tool
halfToolShape.RotateProfile(profilePoints, nPoints, 0, 0, nslices / 2, true);
// unit path
int nFullPoints = PROFILE_BUFFER_POINTS(nPoints);
pathShape.ExtrudeProfileLinear(profilePoints, 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,
PROFILE_BUFFER_POINTS(nPoints),
radius,
angleRad,
zShift,
true,
true);
return 0;
}
void EndMill::MirrorPointBuffer()
{
int endpoint = PROFILE_BUFFER_POINTS(nPoints) - 1;
for (int i = 0, j = endpoint * 2; i < (nPoints - 1) * 2; i += 2, j -= 2) {
profilePoints[j] = -profilePoints[i];
profilePoints[j + 1] = profilePoints[i + 1];
}
}

View File

@@ -0,0 +1,62 @@
/***************************************************************************
* 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 __end_mill_h__
#define __end_mill_h__
#include "SimShapes.h"
#include <vector>
#define PROFILE_BUFFER_POINTS(npoints) ((npoints) * 2 - 1)
#define PROFILE_BUFFER_SIZE(npoints) (PROFILE_BUFFER_POINTS(npoints) * 2)
#define MILL_HEIGHT 10
namespace MillSim
{
class EndMill
{
public:
float* profilePoints = nullptr;
float radius;
int nPoints = 0;
int toolId = -1;
Shape pathShape;
Shape halfToolShape;
Shape toolShape;
public:
EndMill(int toolid, float diameter);
EndMill(const std::vector<float>& toolProfile, int toolid, float diameter);
virtual ~EndMill();
void GenerateDisplayLists(float quality);
unsigned int GenerateArcSegmentDL(float radius, float angleRad, float zShift, Shape* retShape);
protected:
void MirrorPointBuffer();
private:
bool mHandleAllocation = false;
};
} // namespace MillSim
#endif

View File

@@ -0,0 +1,234 @@
/***************************************************************************
* 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 *
* *
***************************************************************************/
#ifdef _MSC_VER
#ifndef _CRT_SECURE_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS
#endif
#endif
#include "GCodeParser.h"
#include <ctype.h>
#include <stdio.h>
using namespace MillSim;
static char TokTypes[] = "GTXYZIJKR";
GCodeParser::~GCodeParser()
{
// Clear the vector
Operations.clear();
}
bool GCodeParser::Parse(const char* filename)
{
Operations.clear();
lastState = {eNop, -1, 0, 0, 0, 0, 0, 0};
lastTool = -1;
FILE* fl;
if ((fl = fopen(filename, "rt")) == nullptr) {
return false;
}
char line[120];
while (!feof(fl)) {
if (fgets(line, 120, fl) != NULL) {
AddLine(line);
}
}
fclose(fl);
return false;
}
const char* GCodeParser::GetNextToken(const char* ptr, GCToken* token)
{
float tokval;
token->letter = '*';
while (*ptr != 0) {
char letter = toupper(*ptr);
ptr++;
if (letter == ' ') {
continue;
}
if (letter == '(') {
break;
}
if (IsValidToken(letter)) {
ptr = ParseFloat(ptr, &tokval);
token->letter = letter;
token->fval = tokval;
token->ival = (int)(tokval + 0.5);
break;
}
}
return ptr;
}
bool GCodeParser::IsValidToken(char tok)
{
int len = (int)strlen(TokTypes);
for (int i = 0; i < len; i++) {
if (tok == TokTypes[i]) {
return true;
}
}
return false;
}
const char* GCodeParser::ParseFloat(const char* ptr, float* retFloat)
{
float decPos = 10;
float sign = 1;
bool decimalPointFound = false;
float res = 0;
while (*ptr != 0) {
char letter = toupper(*ptr);
ptr++;
if (letter == ' ') {
continue;
}
if (letter == '-') {
sign = -1;
}
else if (letter == '.') {
decimalPointFound = true;
}
else if (letter >= '0' && letter <= '9') {
float digitVal = (float)(letter - '0');
if (decimalPointFound) {
res = res + digitVal / decPos;
decPos *= 10;
}
else {
res = res * 10 + digitVal;
}
}
else {
ptr--;
break;
}
}
*retFloat = res * sign;
return ptr;
}
bool GCodeParser::ParseLine(const char* ptr)
{
GCToken token;
bool validMotion = false;
bool exitLoop = false;
int cmd = 0;
while (*ptr != 0 && !exitLoop) {
ptr = GetNextToken(ptr, &token);
lastLastState = lastState;
switch (token.letter) {
case '*':
exitLoop = true;
break;
case 'G':
cmd = token.ival;
if (cmd == 0 || cmd == 1) {
lastState.cmd = eMoveLiner;
}
else if (cmd == 2) {
lastState.cmd = eRotateCW;
}
else if (cmd == 3) {
lastState.cmd = eRotateCCW;
}
else if (cmd == 73 || cmd == 81 || cmd == 82 || cmd == 83) {
lastState.cmd = eDril;
}
break;
case 'T':
lastState.tool = token.ival;
break;
case 'X':
lastState.x = token.fval;
validMotion = true;
break;
case 'Y':
lastState.y = token.fval;
validMotion = true;
break;
case 'Z':
lastState.z = token.fval;
validMotion = true;
break;
case 'I':
lastState.i = token.fval;
break;
case 'J':
lastState.j = token.fval;
break;
case 'K':
lastState.k = token.fval;
break;
case 'R':
lastState.r = token.fval;
break;
}
}
return validMotion;
}
bool GCodeParser::AddLine(const char* ptr)
{
bool res = ParseLine(ptr);
if (res) {
if (lastState.cmd == eDril) {
// split to several motions
lastState.cmd = eMoveLiner;
float rPlane = lastState.r;
float finalDepth = lastState.z;
lastState.z = rPlane;
Operations.push_back(lastState);
lastState.z = finalDepth;
Operations.push_back(lastState);
lastState.z = rPlane;
Operations.push_back(lastState);
// restore original state
lastState.z = finalDepth;
lastState.cmd = eDril;
}
else {
Operations.push_back(lastState);
}
}
return res;
}

View File

@@ -0,0 +1,59 @@
/***************************************************************************
* 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 __csgcodeparser_h__
#define __csgcodeparser_h__
#include "MillMotion.h"
#include <vector>
namespace MillSim
{
struct GCToken
{
char letter;
float fval;
int ival;
};
class GCodeParser
{
public:
GCodeParser()
{}
virtual ~GCodeParser();
bool Parse(const char* filename);
bool AddLine(const char* ptr);
public:
std::vector<MillMotion> Operations;
MillMotion lastState = {eNop};
MillMotion lastLastState = {eNop};
protected:
const char* GetNextToken(const char* ptr, GCToken* token);
bool IsValidToken(char tok);
const char* ParseFloat(const char* ptr, float* retFloat);
bool ParseLine(const char* ptr);
int lastTool = -1;
};
} // namespace MillSim
#endif

View File

@@ -0,0 +1,56 @@
/***************************************************************************
* 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 "GlUtils.h"
#include <iostream>
namespace MillSim
{
int gDebug = -1;
mat4x4 identityMat = {{1, 0, 0, 0}, {0, 1, 0, 0}, {0, 0, 1, 0}, {0, 0, 0, 1}};
void GLClearError()
{
while (glGetError() != GL_NO_ERROR)
;
}
bool GLLogError()
{
bool isError = false;
while (GLenum err = glGetError()) {
std::cout << "[Opengl Error] (" << err << ")" << std::endl;
isError = true;
}
return isError;
}
typedef struct Vertex
{
vec3 pos;
vec3 col;
} Vertex;
} // namespace MillSim

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 __glutils_h__
#define __glutils_h__
#include "OpenGlWrapper.h"
#include "linmath.h"
#define PI 3.14159265f
#define PI2 (PI * 2)
constexpr auto EPSILON = 0.00001f;
#define EQ_FLOAT(x, y) (fabs((x) - (y)) < EPSILON)
#define MS_MOUSE_LEFT 1
#define MS_MOUSE_RIGHT 2
#define MS_MOUSE_MID 4
#define GL(x) \
{ \
GLClearError(); \
x; \
if (GLLogError()) \
__debugbreak(); \
}
#define RadToDeg(x) (x * 180.0f / PI)
namespace MillSim
{
void GLClearError();
bool GLLogError();
extern mat4x4 identityMat;
extern int gDebug;
} // namespace MillSim
#endif // !__glutils_h__

View File

@@ -0,0 +1,243 @@
/***************************************************************************
* 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 "GuiDisplay.h"
#include "OpenGlWrapper.h"
#include "MillSimulation.h"
#include <cstddef>
#include "GlUtils.h"
#include <stdlib.h>
using namespace MillSim;
GuiItem guiItems[] = {
{0, 0, 360, 554, 0},
{0, 0, 448, 540, 1},
{0, 0, 170, 540, 'P', true},
{0, 0, 170, 540, 'S', false},
{0, 0, 210, 540, 'T'},
{0, 0, 250, 540, 'F'},
{0, 0, 290, 540, ' '},
{0, 0, 620, 540, 0, false, 0},
{0, 0, 660, 540, 0, false, 0},
{0, 0, 645, 540, 0, false, 0},
{0, 0, 640, 540, 0, true, 0},
};
#define NUM_GUI_ITEMS (sizeof(guiItems) / sizeof(GuiItem))
#define TEX_SIZE 256
std::vector<std::string> guiFileNames = {"Slider.png",
"Thumb.png",
"Pause.png",
"Play.png",
"SingleStep.png",
"Faster.png",
"Rotate.png",
"X.png",
"0.png",
"1.png",
"4.png"};
bool GuiDisplay::GenerateGlItem(GuiItem* guiItem)
{
Vertex2D verts[4];
int x = guiItem->texItem.tx;
int y = guiItem->texItem.ty;
int w = guiItem->texItem.w;
int h = guiItem->texItem.h;
verts[0] = {0, (float)h, mTexture.getTexX(x), mTexture.getTexY(y + h)};
verts[1] = {(float)w, (float)h, mTexture.getTexX(x + w), mTexture.getTexY(y + h)};
verts[2] = {0, 0, mTexture.getTexX(x), mTexture.getTexY(y)};
verts[3] = {(float)w, 0, mTexture.getTexX(x + w), mTexture.getTexY(y)};
// vertex buffer
glGenBuffers(1, &(guiItem->vbo));
glBindBuffer(GL_ARRAY_BUFFER, guiItem->vbo);
glBufferData(GL_ARRAY_BUFFER, 4 * sizeof(Vertex2D), verts, GL_STATIC_DRAW);
// glDrawElements(GL_TRIANGLES, numIndices, GL_UNSIGNED_SHORT, nullptr);
// vertex array
glGenVertexArrays(1, &(guiItem->vao));
glBindVertexArray(guiItem->vao);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex2D), (void*)offsetof(Vertex2D, x));
glEnableVertexAttribArray(1);
glVertexAttribPointer(1,
2,
GL_FLOAT,
GL_FALSE,
sizeof(Vertex2D),
(void*)offsetof(Vertex2D, tx));
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIbo);
glBindVertexArray(0);
return true;
}
bool GuiDisplay::InutGui()
{
// index buffer
glGenBuffers(1, &mIbo);
GLshort indices[6] = {0, 2, 3, 0, 3, 1};
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIbo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, 6 * sizeof(GLushort), indices, GL_STATIC_DRAW);
TextureLoader tLoader(":/gl_simulator/", guiFileNames, TEX_SIZE);
unsigned int* buffer = tLoader.GetRawData();
if (buffer == nullptr) {
return false;
}
mTexture.LoadImage(buffer, TEX_SIZE, TEX_SIZE);
for (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;
mThumbMaxMotion = (float)guiItems[eGuiItemSlider].texItem.w;
UpdateSimSpeed(1);
// shader
mat4x4 projmat;
// mat4x4 viewmat;
mat4x4_ortho(projmat, 0, 800, 600, 0, -1, 1);
mShader.CompileShader((char*)VertShader2DTex, (char*)FragShader2dTex);
mShader.UpdateTextureSlot(0);
mShader.UpdateProjectionMat(projmat);
return true;
}
void GuiDisplay::RenderItem(int itemId)
{
GuiItem* item = &(guiItems[itemId]);
if (item->hidden) {
return;
}
mat4x4 model;
mat4x4_translate(model, (float)item->sx, (float)item->sy, 0);
mShader.UpdateModelMat(model, nullptr);
if (itemId == mPressedItem) {
mShader.UpdateObjColor(mPressedColor);
}
else if (item->mouseOver) {
mShader.UpdateObjColor(mHighlightColor);
}
else if (itemId > 1 && item->actionKey == 0) {
mShader.UpdateObjColor(mTextColor);
}
else {
mShader.UpdateObjColor(mStdColor);
}
glBindVertexArray(item->vao);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIbo);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, nullptr);
}
void GuiDisplay::MouseCursorPos(int x, int y)
{
for (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));
}
}
void GuiDisplay::MousePressed(int button, bool isPressed, bool isSimRunning)
{
if (button == MS_MOUSE_LEFT) {
if (isPressed) {
mPressedItem = eGuiItemMax;
for (int 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);
}
}
}
else // button released
{
UpdatePlayState(isSimRunning);
mPressedItem = eGuiItemMax;
}
}
}
void GuiDisplay::MouseDrag(int buttons, int dx, int dy)
{
if (mPressedItem == eGuiItemThumb) {
GuiItem* g = &(guiItems[eGuiItemThumb]);
int newx = g->sx + dx;
if (newx < mThumbStartX) {
newx = mThumbStartX;
}
if (newx > ((int)mThumbMaxMotion + mThumbStartX)) {
newx = (int)mThumbMaxMotion + mThumbStartX;
}
if (newx != g->sx) {
mMillSim->SetSimulationStage((float)(newx - mThumbStartX) / mThumbMaxMotion);
g->sx = newx;
}
}
}
void GuiDisplay::UpdatePlayState(bool isRunning)
{
guiItems[eGuiItemPause].hidden = !isRunning;
guiItems[eGuiItemPlay].hidden = isRunning;
}
void MillSim::GuiDisplay::UpdateSimSpeed(int speed)
{
guiItems[eGuiItemChar0Img].hidden = speed == 1;
guiItems[eGuiItemChar1Img].hidden = speed == 40;
guiItems[eGuiItemChar4Img].hidden = speed != 40;
}
void GuiDisplay::Render(float progress)
{
if (mPressedItem != eGuiItemThumb) {
guiItems[eGuiItemThumb].sx = (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 (int i = 0; i < NUM_GUI_ITEMS; i++) {
RenderItem(i);
}
}

View File

@@ -0,0 +1,103 @@
/***************************************************************************
* 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 __guidisplay_t__
#define __guidisplay_t__
#include "OpenGlWrapper.h"
#include "Texture.h"
#include "Shader.h"
#include "TextureLoader.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,
eGuiItemThumb,
eGuiItemPause,
eGuiItemPlay,
eGuiItemSingleStep,
eGuiItemFaster,
eGuiItemRotate,
eGuiItemCharXImg,
eGuiItemChar0Img,
eGuiItemChar1Img,
eGuiItemChar4Img,
eGuiItemMax
};
class GuiDisplay
{
public:
// GuiDisplay() {};
bool InutGui();
void Render(float progress);
void MouseCursorPos(int x, int y);
void MousePressed(int button, bool isPressed, bool isRunning);
void MouseDrag(int buttons, int dx, int dy);
void SetMillSimulator(MillSimulation* millSim)
{
mMillSim = millSim;
}
void UpdatePlayState(bool isRunning);
void UpdateSimSpeed(int speed);
private:
bool GenerateGlItem(GuiItem* guiItem);
void RenderItem(int itemId);
vec3 mStdColor = {0.8f, 0.8f, 0.4f};
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;
MillSimulation* mMillSim = nullptr;
unsigned int mIbo = 0;
int mThumbStartX = 0;
float mThumbMaxMotion = 0;
};
} // namespace MillSim
#endif // __guidisplay_t__

View File

@@ -0,0 +1,65 @@
/***************************************************************************
* 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 __mill_operation_h__
#define __mill_operation_h__
// #include <math.h>
#include "EndMill.h"
#include "linmath.h"
namespace MillSim
{
enum eEndMillType
{
eEndmillFlat,
eEndmillV,
eEndmillBall,
eEndmillFillet
};
enum eCmdType
{
eNop,
eMoveLiner,
eRotateCW,
eRotateCCW,
eDril,
eChangeTool
};
struct MillMotion
{
eCmdType cmd;
int tool;
float x, y, z;
float i, j, k;
float r;
};
static inline void MotionPosToVec(vec3 vec, const MillMotion* motion)
{
vec[0] = motion->x;
vec[1] = motion->y;
vec[2] = motion->z;
}
} // namespace MillSim
#endif

View File

@@ -0,0 +1,242 @@
/***************************************************************************
* 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 "OpenGlWrapper.h"
#include "MillPathSegment.h"
#include "SimShapes.h"
#include "linmath.h"
#include "GlUtils.h"
#include <iostream>
#define N_MILL_SLICES 8
#define MAX_SEG_DEG (PI / 2.0f) // 90 deg
#define NIN_SEG_DEG (PI / 90.0f) // 2 deg
#define SWEEP_ARC_PAD 1.05f
#define PX 0
#define PY 1
#define PZ 2
namespace MillSim
{
bool IsVerticalMotion(MillMotion* m1, MillMotion* m2)
{
return (m1->z != m2->z && EQ_FLOAT(m1->x, m2->x) && EQ_FLOAT(m1->y, m2->y));
}
bool IsArcMotion(MillMotion* m)
{
if (m->cmd != eRotateCCW && m->cmd != eRotateCW) {
return false;
}
return fabs(m->i > EPSILON) || fabs(m->j) > EPSILON;
}
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}
{
MotionPosToVec(mStartPos, from);
MotionPosToVec(mDiff, to);
vec3_sub(mDiff, mDiff, mStartPos);
mXYDistance = sqrtf(mDiff[PX] * mDiff[PX] + mDiff[PY] * mDiff[PY]);
mZDistance = fabsf(mDiff[PY]);
mXYZDistance = sqrtf(mXYDistance * mXYDistance + mDiff[PZ] * mDiff[PZ]);
mXYAngle = atan2f(mDiff[PY], mDiff[PX]);
endmill = _endmill;
mStartAngRad = mStepAngRad = 0;
if (IsArcMotion(to)) {
mMotionType = MTCurved;
mRadius = sqrtf(to->j * to->j + to->i * to->i);
mSmallRad = mRadius <= endmill->radius;
if (mSmallRad) {
mStepAngRad = mSmallRadStep;
}
else {
mStepAngRad = asinf(mResolution / mRadius);
if (mStepAngRad > MAX_SEG_DEG) {
mStepAngRad = MAX_SEG_DEG;
}
else if (mStepAngRad < NIN_SEG_DEG) {
mStepAngRad = NIN_SEG_DEG;
}
}
MotionPosToVec(mCenter, from);
mCenter[PX] += to->i;
mCenter[PY] += to->j;
mArcDir = to->cmd == eRotateCCW ? -1.f : 1.f;
mStartAngRad = atan2f(mCenter[PX] - from->x, from->y - mCenter[PY]);
float endAng = atan2f(mCenter[PX] - to->x, to->y - mCenter[PY]);
mSweepAng = (mStartAngRad - endAng) * mArcDir;
if (mSweepAng < EPSILON) {
mSweepAng += PI * 2;
}
numSimSteps = (int)(mSweepAng / mStepAngRad) + 1;
mStepAngRad = mArcDir * mSweepAng / numSimSteps;
if (mSmallRad) {
// when the radius is too small, we just use the tool itself to carve the stock
mShape = endmill->toolShape;
}
else {
endmill->GenerateArcSegmentDL(mRadius,
mStepAngRad * SWEEP_ARC_PAD,
mDiff[PZ] / numSimSteps,
&mShape);
numSimSteps++;
}
isMultyPart = true;
}
else {
numSimSteps = (int)(mXYZDistance / mResolution);
if (numSimSteps == 0) {
numSimSteps = 1;
}
isMultyPart = false;
mStepDistance = mXYDistance / numSimSteps;
mStepLength[PX] = mDiff[PX];
mStepLength[PY] = mDiff[PY];
mStepLength[PZ] = mDiff[PZ];
vec3_scale(mStepLength, mStepLength, 1.f / (float)numSimSteps);
if (IsVerticalMotion(from, to)) {
mMotionType = MTVertical;
}
else {
mMotionType = MTHorizontal;
mShearMat[0][2] = mDiff[PZ] / mXYDistance;
}
}
}
MillPathSegment::~MillPathSegment()
{
mShape.FreeResources();
}
void MillPathSegment::render(int step)
{
mStepNumber = step;
mat4x4 mat, mat2, rmat;
mat4x4_identity(mat);
mat4x4_identity(rmat);
if (mMotionType == MTCurved) {
mat4x4_translate_in_place(mat,
mCenter[PX],
mCenter[PY],
mCenter[PZ] + mDiff[PZ] * (step - 1) / numSimSteps);
mat4x4_rotate_Z(mat, mat, mStartAngRad - (step - 1) * mStepAngRad);
mat4x4_rotate_Z(rmat, rmat, mStartAngRad - (step - 1) * mStepAngRad);
if (mSmallRad || step == numSimSteps) {
mat4x4_translate_in_place(mat, 0, mRadius, 0);
endmill->toolShape.Render(mat, rmat);
}
else {
mShape.Render(mat, rmat);
}
}
else {
if (mMotionType == MTVertical) {
if (mStepLength[PZ] > 0) {
mat4x4_translate_in_place_v(mat, mStartPos);
}
else {
mat4x4_translate_in_place(mat,
mStartPos[PX],
mStartPos[PY],
mStartPos[PZ] + mStepNumber * mStepLength[PZ]);
}
endmill->toolShape.Render(mat, rmat);
}
else {
float renderDist = step * mStepDistance;
mat4x4_translate_in_place_v(mat, mStartPos);
mat4x4_rotate_Z(mat, mat, mXYAngle);
mat4x4_rotate_Z(rmat, rmat, mXYAngle);
mat4x4_dup(mat2, mat);
if (mDiff[PZ] != 0.0) {
mat4x4_mul(mat2, mat2, mShearMat);
}
mat4x4_scale_aniso(mat2, mat2, renderDist, 1, 1);
endmill->pathShape.Render(mat2, rmat);
mat4x4_translate_in_place(mat, renderDist, 0, mDiff[PZ]);
endmill->halfToolShape.Render(mat, rmat);
}
}
}
void MillPathSegment::GetHeadPosition(vec3 headPos)
{
if (mMotionType == MTCurved) {
float angRad = mStartAngRad - mStepNumber * mStepAngRad;
vec3_set(mHeadPos, -mRadius * sinf(angRad), mRadius * cosf(angRad), 0);
vec3_add(mHeadPos, mHeadPos, mCenter);
}
else {
vec3_dup(mHeadPos, mStepLength);
vec3_scale(mHeadPos, mHeadPos, (float)mStepNumber);
vec3_add(mHeadPos, mHeadPos, mStartPos);
}
vec3_dup(headPos, mHeadPos);
}
float MillPathSegment::SetQuality(float quality, float maxStockDimension)
{
mResolution = maxStockDimension * 0.05 / quality;
if (mResolution > 4) {
mResolution = 4;
}
if (mResolution < 0.5) {
mResolution = 0.5;
}
mSmallRadStep = PI / 8;
if (quality < 4) {
mSmallRadStep = PI / 2;
}
else if (quality < 8) {
mSmallRadStep = PI / 4;
}
return mResolution;
}
} // namespace MillSim

View File

@@ -0,0 +1,98 @@
/***************************************************************************
* 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 __mill_path_segment_h__
#define __mill_path_segment_h__
#include "MillMotion.h"
#include "EndMill.h"
#include "linmath.h"
namespace MillSim
{
enum MotionType
{
MTVertical = 0,
MTHorizontal,
MTCurved
};
bool IsVerticalMotion(MillMotion* m1, MillMotion* m2);
class MillPathSegment
{
public:
/// <summary>
/// Create a mill path segment primitive
/// </summary>
/// <param name="endmill">Mill object</param>
/// <param name="from">Start point</param>
/// <param name="to">End point</param>
MillPathSegment(EndMill* endmill, MillMotion* from, MillMotion* to);
virtual ~MillPathSegment();
/// Calls the display list.
virtual void render(int substep);
virtual void GetHeadPosition(vec3 headPos);
static float SetQuality(float quality, float maxStockDimension); // 1 minimum, 10 maximum
public:
EndMill* endmill = nullptr;
bool isMultyPart;
int numSimSteps;
int indexInArray;
protected:
mat4x4 mShearMat;
Shape mShape;
float mXYDistance;
float mXYZDistance;
float mZDistance;
float mXYAngle;
float mStartAngRad;
float mStepAngRad;
float mStepDistance = 0;
float mSweepAng;
float mRadius = 0;
float mArcDir = 0;
bool mSmallRad = false;
int mStepNumber = 0;
static float mSmallRadStep;
static float mResolution;
vec3 mDiff;
vec3 mStepLength = {0};
vec3 mCenter = {0};
vec3 mStartPos;
vec3 mHeadPos = {0};
MotionType mMotionType;
};
} // namespace MillSim
#endif

View File

@@ -0,0 +1,601 @@
/***************************************************************************
* 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 "MillSimulation.h"
#include "OpenGlWrapper.h"
#include <vector>
#include <iostream>
namespace MillSim {
MillSimulation::MillSimulation()
{
mCurMotion = { eNop, -1, 0, 0, 0, 0, 0, 0 };
guiDisplay.SetMillSimulator(this);
}
void MillSimulation::ClearMillPathSegments() {
//for (std::vector<MillPathSegment*>::const_iterator i = MillPathSegments.begin(); i != MillPathSegments.end(); ++i) {
// MillSim::MillPathSegment* p = *i;
// delete p;
//}
for (int i = 0; i < MillPathSegments.size(); i++)
delete MillPathSegments[i];
MillPathSegments.clear();
}
void MillSimulation::Clear()
{
mCodeParser.Operations.clear();
for (int i = 0; i < mToolTable.size(); i++)
delete mToolTable[i];
mToolTable.clear();
mCurStep = 0;
mPathStep = -1;
mNTotalSteps = 0;
}
void MillSimulation::SimNext()
{
static int simDecim = 0;
simDecim++;
if (simDecim < 1)
return;
simDecim = 0;
if (mCurStep < mNTotalSteps)
{
mCurStep += mSimSpeed;
CalcSegmentPositions();
}
}
void MillSimulation::InitSimulation(float quality)
{
ClearMillPathSegments();
mDestMotion = mZeroPos;
//gDestPos = curMillOperation->startPos;
mCurStep = 0;
mPathStep = -1;
mNTotalSteps = 0;
MillPathSegment::SetQuality(quality, mMaxFar);
int nOperations = (int)mCodeParser.Operations.size();;
for (int i = 0; i < nOperations; i++)
{
mCurMotion = mDestMotion;
mDestMotion = mCodeParser.Operations[i];
EndMill* tool = GetTool(mDestMotion.tool);
if (tool != nullptr)
{
MillSim::MillPathSegment* segment = new MillSim::MillPathSegment(tool, &mCurMotion, &mDestMotion);
segment->indexInArray = i;
mNTotalSteps += segment->numSimSteps;
MillPathSegments.push_back(segment);
}
}
mNPathSteps = (int)MillPathSegments.size();
InitDisplay(quality);
}
EndMill* MillSimulation::GetTool(int toolId)
{
for (int i = 0; i < mToolTable.size(); i++)
{
if (mToolTable[i]->toolId == toolId)
{
return mToolTable[i];
}
}
return nullptr;
}
void MillSimulation::RemoveTool(int toolId)
{
EndMill* tool;
if ((tool = GetTool(toolId)) != nullptr) {
auto it = std::find(mToolTable.begin(), mToolTable.end(), tool);
if (it != mToolTable.end()) {
mToolTable.erase(it);
}
delete tool;
}
}
void MillSimulation::AddTool(EndMill* tool)
{
// if we have another tool with same id, remove it
RemoveTool(tool->toolId);
mToolTable.push_back(tool);
}
void
MillSimulation::AddTool(const std::vector<float>& toolProfile, int toolid, float diameter)
{
// if we have another tool with same id, remove it
RemoveTool(toolid);
EndMill* tool = new EndMill(toolProfile, toolid, diameter);
mToolTable.push_back(tool);
}
void MillSimulation::GlsimStart()
{
glEnable(GL_CULL_FACE);
glEnable(GL_DEPTH_TEST);
glEnable(GL_STENCIL_TEST);
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
}
void MillSimulation::GlsimToolStep1(void)
{
glCullFace(GL_BACK);
glDepthFunc(GL_LESS);
glStencilFunc(GL_ALWAYS, 1, 0xFF);
glStencilOp(GL_ZERO, GL_ZERO, GL_REPLACE);
glDepthMask(GL_FALSE);
}
void MillSimulation::GlsimToolStep2(void)
{
glStencilFunc(GL_EQUAL, 1, 0xFF);
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
glDepthFunc(GL_GREATER);
glCullFace(GL_FRONT);
glDepthMask(GL_TRUE);
}
void MillSimulation::GlsimClipBack(void)
{
glStencilFunc(GL_ALWAYS, 1, 0xFF);
glStencilOp(GL_REPLACE, GL_REPLACE, GL_ZERO);
glDepthFunc(GL_LESS);
glCullFace(GL_FRONT);
glDepthMask(GL_FALSE);
}
void MillSimulation::GlsimRenderStock(void)
{
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
glEnable(GL_STENCIL_TEST);
glStencilFunc(GL_EQUAL, 1, 0xFF);
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
glDepthFunc(GL_EQUAL);
glCullFace(GL_BACK);
}
void MillSimulation::GlsimRenderTools(void)
{
glCullFace(GL_FRONT);
}
void MillSimulation::GlsimEnd(void)
{
glCullFace(GL_BACK);
glStencilFunc(GL_ALWAYS, 1, 0xFF);
glDisable(GL_STENCIL_TEST);
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
glDepthMask(GL_TRUE);
glDepthFunc(GL_LESS);
}
void MillSimulation::renderSegmentForward(int iSeg)
{
MillSim::MillPathSegment* p = MillPathSegments.at(iSeg);
int step = iSeg == mPathStep ? mSubStep : p->numSimSteps;
int start = p->isMultyPart ? 1 : step;
for (int i = start; i <= step; i++)
{
GlsimToolStep1();
p->render(i);
GlsimToolStep2();
p->render(i);
}
}
void MillSimulation::renderSegmentReversed(int iSeg)
{
MillSim::MillPathSegment* p = MillPathSegments.at(iSeg);
int step = iSeg == mPathStep ? mSubStep : p->numSimSteps;
int end = p->isMultyPart ? 1 : step;
for (int i = step; i >= end; i--)
{
GlsimToolStep1();
p->render(i);
GlsimToolStep2();
p->render(i);
}
}
void MillSimulation::CalcSegmentPositions()
{
mSubStep = mCurStep;
for (mPathStep = 0; mPathStep < mNPathSteps; mPathStep++)
{
MillSim::MillPathSegment* p = MillPathSegments[mPathStep];
if (mSubStep < p->numSimSteps)
break;
mSubStep -= p->numSimSteps;
}
if (mPathStep >= mNPathSteps)
{
mPathStep = mNPathSteps - 1;
mSubStep = MillPathSegments[mPathStep]->numSimSteps;
}
else
mSubStep++;
}
void MillSimulation::Render()
{
mat4x4 matLookAt, model;
mat4x4_identity(model);
mat4x4_look_at(matLookAt, eye, target, upvec);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
mat4x4_translate_in_place(matLookAt, mEyeX * mEyeXZFactor, 0, mEyeZ * mEyeXZFactor);
mat4x4_rotate_X(matLookAt, matLookAt, mEyeInclination);
mat4x4_rotate_Z(matLookAt, matLookAt, mEyeRoration);
mat4x4_translate_in_place(matLookAt, -mStockObject.center[0], -mStockObject.center[1], -mStockObject.center[2]);
shaderFlat.Activate();
shaderFlat.UpdateViewMat(matLookAt);
GlsimStart();
mStockObject.render();
GlsimToolStep2();
for (int i = 0; i <= mPathStep; i++)
renderSegmentForward(i);
for (int i = mPathStep; i >= 0; i--)
renderSegmentForward(i);
for (int i = 0; i < mPathStep; i++)
renderSegmentReversed(i);
for (int i = mPathStep; i >= 0; i--)
renderSegmentReversed(i);
GlsimClipBack();
mStockObject.render();
// start coloring
shader3D.Activate();
shader3D.UpdateViewMat(matLookAt);
shader3D.UpdateObjColor(stockColor);
GlsimRenderStock();
mStockObject.render();
GlsimRenderTools();
// render cuts (back faces of tools)
shaderInv3D.Activate();
shaderInv3D.UpdateViewMat(matLookAt);
shaderInv3D.UpdateObjColor(cutColor);
for (int i = 0; i <= mPathStep; i++)
{
MillSim::MillPathSegment* p = MillPathSegments.at(i);
int step = (i == mPathStep) ? mSubStep : p->numSimSteps;
int start = p->isMultyPart ? 1 : step;
for (int j = start; j <= step; j++)
MillPathSegments.at(i)->render(j);
}
GlsimEnd();
glEnable(GL_CULL_FACE);
if (mPathStep >= 0)
{
vec3 toolPos;
MotionPosToVec(toolPos, &mDestMotion);
MillSim::MillPathSegment* p = MillPathSegments.at(mPathStep);
p->GetHeadPosition(toolPos);
mat4x4 tmat;
mat4x4_translate(tmat, toolPos[0], toolPos[1], toolPos[2]);
//mat4x4_translate(tmat, toolPos.x, toolPos.y, toolPos.z);
shader3D.Activate();
shader3D.UpdateObjColor(toolColor);
p->endmill->toolShape.Render(tmat, identityMat);
}
shaderFlat.Activate();
shaderFlat.UpdateObjColor(lightColor);
mlightObject.render();
if (mDebug > 0)
{
mat4x4 test;
mat4x4_dup(test, model);
mat4x4_translate_in_place(test, 20, 20, 3);
mat4x4_rotate_Z(test, test, 30.f * 3.14f / 180.f);
int dpos = mNPathSteps - mDebug2;
MillSim::MillPathSegment* p = MillPathSegments.at(dpos);
if (mDebug > p->numSimSteps)
mDebug = 1;
p->render(mDebug);
}
float progress = (float)mCurStep / mNTotalSteps;
guiDisplay.Render(progress);
}
void MillSimulation::ProcessSim(unsigned int time_ms) {
static int ancient = 0;
static int last = 0;
static int msec = 0;
static int fps = 0;
static int renderTime = 0;
last = msec;
msec = time_ms;
if (mIsRotate) {
mEyeRoration += (msec - last) / 4600.0f;
while (mEyeRoration >= PI2)
mEyeRoration -= PI2;
}
if (last / 1000 != msec / 1000) {
float calcFps = 1000.0f * fps / (msec - ancient);
mFpsStream.str("");
mFpsStream << "fps: " << calcFps << " rendertime:" << renderTime << " zpos:" << mDestMotion.z << std::ends;
ancient = msec;
fps = 0;
}
if (mSimPlaying || mSingleStep)
{
SimNext();
mSingleStep = false;
}
Render();
++fps;
}
void MillSimulation::HandleKeyPress(int key)
{
switch (key) {
case ' ':
mIsRotate = !mIsRotate;
break;
case 'S':
mSimPlaying = true;
break;
case 'P':
mSimPlaying = false;
break;
case 'T':
mSimPlaying = false;
mSingleStep = true;
break;
case'D':
mDebug++;
break;
case'K':
mDebug2++;
gDebug = mNPathSteps - mDebug2;
break;
case 'F':
if (mSimSpeed == 1) mSimSpeed = 10;
else if (mSimSpeed == 10) mSimSpeed = 40;
else mSimSpeed = 1;
guiDisplay.UpdateSimSpeed(mSimSpeed);
break;
default:
if (key >= '1' && key <= '9')
mSimSpeed = key - '0';
break;
}
guiDisplay.UpdatePlayState(mSimPlaying);
}
void MillSimulation::UpdateEyeFactor(float factor)
{
mEyeDistFactor = factor;
mEyeXZFactor = factor * mMaxFar * 0.005f;
eye[1] = -factor * mMaxFar;
}
void MillSimulation::TiltEye(float tiltStep)
{
mEyeInclination += tiltStep;
if (mEyeInclination > PI / 2)
mEyeInclination = PI / 2;
else if (mEyeInclination < -PI / 2)
mEyeInclination = -PI / 2;
}
void MillSimulation::RotateEye(float rotStep)
{
mEyeRoration += rotStep;
if (mEyeRoration > PI2)
mEyeRoration = PI2;
else if (mEyeRoration < 0)
mEyeRoration = 0;
}
void MillSimulation::MoveEye(float x, float z)
{
mEyeX += x;
if (mEyeX > 100) mEyeX = 100;
else if (mEyeX < -100) mEyeX = -100;
mEyeZ += z;
if (mEyeZ > 100) mEyeZ = 100;
else if (mEyeZ < -100) mEyeZ = -100;
}
void MillSimulation::UpdateProjection()
{
// Setup projection
mat4x4 projmat;
mat4x4_perspective(projmat, 0.7f, 4.0f / 3.0f, 1.0f, mMaxFar);
//mat4x4_perspective(projmat, 0.7f, 4.0f / 3.0f, 1, 100);
shader3D.Activate();
shader3D.UpdateProjectionMat(projmat);
shaderInv3D.Activate();
shaderInv3D.UpdateProjectionMat(projmat);
shaderFlat.Activate();
shaderFlat.UpdateProjectionMat(projmat);
}
void MillSimulation::InitDisplay(float quality)
{
// gray background
glClearColor(0.6f, 0.8f, 1.0f, 1.0f);
// use shaders
// standard diffuse shader
shader3D.CompileShader((char*)VertShader3DNorm, (char*)FragShaderNorm);
shader3D.UpdateEnvColor(lightPos, lightColor, ambientCol);
// invarted normal diffuse shader for inner mesh
shaderInv3D.CompileShader((char*)VertShader3DInvNorm, (char*)FragShaderNorm);
shaderInv3D.UpdateEnvColor(lightPos, lightColor, ambientCol);
// null shader to calculate meshes only (simulation stage)
shaderFlat.CompileShader((char*)VertShader3DNorm, (char*)FragShaderFlat);
UpdateProjection();
// setup light object and generate tools
mlightObject.GenerateBoxStock(-0.5f, -0.5f, -0.5f, 1, 1, 1);
for (int i = 0; i < mToolTable.size(); i++)
mToolTable[i]->GenerateDisplayLists(quality);
// init gui elements
guiDisplay.InutGui();
}
void MillSimulation::SetBoxStock(float x, float y, float z, float l, float w, float h)
{
mStockObject.GenerateBoxStock(x, y, z, l, w, h);
mMaxStockDim = fmaxf(w, l);
mMaxFar = mMaxStockDim * 4;
UpdateProjection();
vec3_set(eye, 0, 0, 0);
UpdateEyeFactor(0.4f);
vec3_set(lightPos, x, y, h + mMaxStockDim / 3);
mlightObject.SetPosition(lightPos);
}
void MillSimulation::MouseDrag(int buttons, int dx, int dy)
{
if (buttons == (MS_MOUSE_MID | MS_MOUSE_LEFT))
{
TiltEye((float)dy / 100.0f);
RotateEye((float)dx / 100.0f);
}
else if (buttons == MS_MOUSE_MID)
{
MoveEye(dx, -dy);
}
guiDisplay.MouseDrag(buttons, dx, dy);
}
void MillSimulation::MouseMove(int px, int py)
{
if (mMouseButtonState > 0)
{
int dx = px - mLastMouseX;
int dy = py - mLastMouseY;
if (dx != 0 || dy != 0)
{
MouseDrag(mMouseButtonState, dx, dy);
mLastMouseX = px;
mLastMouseY = py;
}
}
else
MouseHover(px, py);
}
void MillSimulation::MouseScroll(float dy)
{
float f = mEyeDistFactor;
f += 0.05f * dy;
if (f > 0.6f) f = 0.6f;
else if (f < 0.05f) f = 0.05f;
UpdateEyeFactor(f);
}
void MillSimulation::MouseHover(int px, int py)
{
guiDisplay.MouseCursorPos(px, py);
}
void MillSimulation::MousePress(int button, bool isPressed, int px, int py)
{
if (isPressed)
mMouseButtonState |= button;
else
mMouseButtonState &= ~button;
if (mMouseButtonState > 0)
{
mLastMouseX = px;
mLastMouseY = py;
}
guiDisplay.MousePressed(button, isPressed, mSimPlaying);
}
bool MillSimulation::LoadGCodeFile(const char* fileName)
{
if (mCodeParser.Parse(fileName))
{
std::cout << "GCode file loaded successfuly" << std::endl;
return true;
}
return false;
}
bool MillSimulation::AddGcodeLine(const char* line)
{
return mCodeParser.AddLine(line);
}
void MillSimulation::SetSimulationStage(float stage)
{
mCurStep = (int)((float)mNTotalSteps * stage);
CalcSegmentPositions();
}
}

View File

@@ -0,0 +1,149 @@
/***************************************************************************
* 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 __millsimulation__h__
#define __millsimulation__h__
#include "MillMotion.h"
#include "GCodeParser.h"
#include "Shader.h"
#include "linmath.h"
#include "GlUtils.h"
#include "StockObject.h"
#include "MillPathSegment.h"
#include "GuiDisplay.h"
#include <sstream>
#include <vector>
namespace MillSim
{
class MillSimulation
{
public:
MillSimulation();
void ClearMillPathSegments();
void Clear();
void SimNext();
void InitSimulation(float quality);
void AddTool(EndMill* tool);
void AddTool(const std::vector<float>& toolProfile, int toolid, float diameter);
bool ToolExists(int toolid)
{
return GetTool(toolid) != nullptr;
}
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();
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 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);
protected:
void InitDisplay(float quality);
void GlsimStart();
void GlsimToolStep1(void);
void GlsimToolStep2(void);
void GlsimClipBack(void);
void GlsimRenderStock(void);
void GlsimRenderTools(void);
void GlsimEnd(void);
void renderSegmentForward(int iSeg);
void renderSegmentReversed(int iSeg);
void CalcSegmentPositions();
EndMill* GetTool(int tool);
void RemoveTool(int toolId);
protected:
std::vector<EndMill*> mToolTable;
Shader shader3D, shaderInv3D, shaderFlat;
GCodeParser mCodeParser;
GuiDisplay guiDisplay;
std::vector<MillPathSegment*> MillPathSegments;
std::ostringstream mFpsStream;
MillMotion mZeroPos = {eNop, -1, 0, 0, 100, 0, 0, 0};
MillMotion mCurMotion = {eNop, -1, 0, 0, 0, 0, 0, 0};
MillMotion mDestMotion = {eNop, -1, 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;
int mCurStep = 0;
int mNTotalSteps = 0;
int mPathStep = 0;
int mSubStep = 0;
int mNPathSteps = 0;
int mDebug = 0;
int mDebug1 = 0;
int mDebug2 = 12;
int mSimSpeed = 1;
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

@@ -0,0 +1,71 @@
/***************************************************************************
* 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 __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()
#define glGenBuffers gSimWindow->glGenBuffers
#define glBindBuffer gSimWindow->glBindBuffer
#define glBufferData gSimWindow->glBufferData
#define glGenVertexArrays gSimWindow->glGenVertexArrays
#define glBindVertexArray gSimWindow->glBindVertexArray
#define glEnableVertexAttribArray gSimWindow->glEnableVertexAttribArray
#define glVertexAttribPointer gSimWindow->glVertexAttribPointer
#define glShaderSource gSimWindow->glShaderSource
#define glCompileShader gSimWindow->glCompileShader
#define glAttachShader gSimWindow->glAttachShader
#define glLinkProgram gSimWindow->glLinkProgram
#define glGetProgramiv gSimWindow->glGetProgramiv
#define glGetUniformLocation gSimWindow->glGetUniformLocation
#define glGetError gSimWindow->glGetError
#define glEnable gSimWindow->glEnable
#define glColorMask gSimWindow->glColorMask
#define glCullFace gSimWindow->glCullFace
#define glDepthFunc gSimWindow->glDepthFunc
#define glStencilFunc gSimWindow->glStencilFunc
#define glStencilOp gSimWindow->glStencilOp
#define glDepthMask gSimWindow->glDepthMask
#define glDisable gSimWindow->glDisable
#define glMatrixMode gSimWindow->glMatrixMode
#define glUseProgram gSimWindow->glUseProgram
#define glDrawElements gSimWindow->glDrawElements
#define glDeleteVertexArrays gSimWindow->glDeleteVertexArrays
#define glUniformMatrix4fv gSimWindow->glUniformMatrix4fv
#define glUniform3fv gSimWindow->glUniform3fv
#define glUniform1i gSimWindow->glUniform1i
#define glCreateShader gSimWindow->glCreateShader
#define glCreateProgram gSimWindow->glCreateProgram
#define glDeleteBuffers gSimWindow->glDeleteBuffers
#define glActiveTexture gSimWindow->glActiveTexture
#define glBindTexture gSimWindow->glBindTexture
#define glGenTextures gSimWindow->glGenTextures
#define glTexParameteri gSimWindow->glTexParameteri
#define glTexImage2D gSimWindow->glTexImage2D
#define glDeleteTextures gSimWindow->glDeleteTextures
#endif // HAVE_OPENGL_EXT
#endif // !__openglwrapper_h__

View File

@@ -0,0 +1,23 @@
/***************************************************************************
* Copyright (c) 2017 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 "PreCompiled.h"

View File

@@ -0,0 +1,56 @@
/***************************************************************************
* Copyright (c) 2017 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 APP_PRECOMPILED_H
#define APP_PRECOMPILED_H
#include <FCConfig.h>
#ifdef _PreComp_
// standard
#include <cstdio>
#include <cassert>
#include <iostream>
// STL
#include <algorithm>
#include <iostream>
#include <list>
#include <map>
#include <queue>
#include <set>
#include <sstream>
#include <stack>
#include <string>
#include <vector>
// Boost
#include <boost/regex.hpp>
// Xerces
#include <xercesc/util/XercesDefs.hpp>
#endif //_PreComp_
#endif

View File

@@ -0,0 +1,272 @@
/***************************************************************************
* 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 "GlUtils.h"
#include "Shader.h"
#include <iostream>
namespace MillSim
{
Shader* CurrentShader = nullptr;
void Shader::UpdateModelMat(mat4x4 tmat, mat4x4 nmat)
{
if (mModelPos >= 0) {
glUniformMatrix4fv(mModelPos, 1, GL_FALSE, (GLfloat*)tmat);
}
if (mNormalRotPos >= 0) {
glUniformMatrix4fv(mNormalRotPos, 1, GL_FALSE, (GLfloat*)nmat);
}
}
void Shader::UpdateProjectionMat(mat4x4 mat)
{
if (mProjectionPos >= 0) {
glUniformMatrix4fv(mProjectionPos, 1, GL_FALSE, (GLfloat*)mat);
}
}
void Shader::UpdateViewMat(mat4x4 mat)
{
if (mViewPos >= 0) {
if (mViewPos >= 0) {
glUniformMatrix4fv(mViewPos, 1, GL_FALSE, (GLfloat*)mat);
}
}
}
void Shader::UpdateEnvColor(vec3 lightPos, vec3 lightColor, vec3 ambient)
{
if (mLightPosPos >= 0) {
glUniform3fv(mLightPosPos, 1, lightPos);
}
if (mLightColorPos >= 0) {
glUniform3fv(mLightColorPos, 1, lightColor);
}
if (mAmbientPos >= 0) {
glUniform3fv(mAmbientPos, 1, ambient);
}
}
void Shader::UpdateObjColor(vec3 objColor)
{
if (mObjectColorPos >= 0) {
glUniform3fv(mObjectColorPos, 1, objColor);
}
}
void Shader::UpdateTextureSlot(int slot)
{
if (mTexSlotPos >= 0) {
glUniform1i(mTexSlotPos, slot);
}
}
bool CheckCompileResult(int shader)
{
#ifdef QT_OPENGL_LIB
return false;
#else
char log[1024];
int res = 0;
GLsizei len;
glGetShaderiv(shader, GL_COMPILE_STATUS, &res);
if (res != 0) {
return false;
}
glGetShaderInfoLog(shader, 1020, &len, log);
if (len > 1020) {
len = 1020;
}
log[len] = 0;
std::cout << log << std::endl;
return true;
#endif
}
unsigned int Shader::CompileShader(char* _vertShader, char* _fragShader)
{
vertShader = _vertShader;
fragShader = _fragShader;
const GLuint vertex_shader = glCreateShader(GL_VERTEX_SHADER);
GLint res = 0;
glShaderSource(vertex_shader, 1, &vertShader, NULL);
glCompileShader(vertex_shader);
if (CheckCompileResult(vertex_shader)) {
return 0xdeadbeef;
}
const GLuint fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragment_shader, 1, &fragShader, NULL);
glCompileShader(fragment_shader);
if (CheckCompileResult(fragment_shader)) {
return 0xdeadbeef;
}
shaderId = glCreateProgram();
glAttachShader(shaderId, vertex_shader);
glAttachShader(shaderId, fragment_shader);
glLinkProgram(shaderId);
glGetProgramiv(shaderId, GL_LINK_STATUS, &res);
if (res == 0) {
return 0xdeadbeef;
}
// get all uniform parameters positions
mModelPos = glGetUniformLocation(shaderId, "model");
mNormalRotPos = glGetUniformLocation(shaderId, "normalRot");
mProjectionPos = glGetUniformLocation(shaderId, "projection");
mViewPos = glGetUniformLocation(shaderId, "view");
mLightPosPos = glGetUniformLocation(shaderId, "lightPos");
mLightColorPos = glGetUniformLocation(shaderId, "lightColor");
mAmbientPos = glGetUniformLocation(shaderId, "ambient");
mObjectColorPos = glGetUniformLocation(shaderId, "objectColor");
mTexSlotPos = glGetUniformLocation(shaderId, "texSlot");
Activate();
return shaderId;
}
void Shader::Activate()
{
if (shaderId > 0) {
glUseProgram(shaderId);
}
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";
const char* VertShader2DTex =
"#version 330 core \n" // -----> add long remark for a uniform auto formatting
"layout(location = 0) in vec2 aPosition; \n"
"layout(location = 1) in vec2 aTexCoord; \n"
"out vec2 texCoord; \n"
"uniform mat4 projection; \n"
"uniform mat4 model; \n"
"void main(void) \n"
"{ \n"
" gl_Position = projection * model * vec4(aPosition, 0.0, 1.0); \n"
" texCoord = aTexCoord; \n"
"} \n";
const char* FragShader2dTex =
"#version 330\n" // -----> add long remark for a uniform auto formatting
"out vec4 FragColor; \n"
"in vec2 texCoord; \n"
"uniform vec3 objectColor; \n"
"uniform sampler2D texSlot; \n"
"void main() \n"
"{ \n"
" vec4 texColor = texture(texSlot, texCoord); \n"
" FragColor = vec4(objectColor, 1.0) * texColor; \n"
"} \n";
const char* FragShaderNorm =
"#version 330\n" // -----> add long remark for a uniform auto formatting
"out vec4 FragColor; \n"
"in vec3 Normal; \n"
"in vec3 FragPos; \n"
"uniform vec3 lightPos; \n"
"uniform vec3 lightColor; \n"
"uniform vec3 objectColor; \n"
"uniform vec3 ambient; \n"
"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";
const char* FragShaderFlat =
"#version 330\n" // -----> add long remark for a uniform auto formatting
"out vec4 FragColor; \n"
"in vec3 Normal; \n"
"in vec3 FragPos; \n"
"uniform vec3 objectColor; \n"
"void main() \n"
"{ \n"
" FragColor = vec4(objectColor, 1.0); \n"
"} \n";
} // namespace MillSim

View File

@@ -0,0 +1,77 @@
/***************************************************************************
* 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 __shader_h__
#define __shader_h__
#include "OpenGlWrapper.h"
#include "linmath.h"
namespace MillSim
{
class Shader
{
public:
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 UpdateObjColor(vec3 objColor);
void UpdateTextureSlot(int slot);
unsigned int CompileShader(char* vertShader, char* fragShader);
void Activate();
bool IsValid()
{
return shaderId > 0;
}
protected:
int mModelPos = -1;
int mNormalRotPos = -1;
int mProjectionPos = -1;
int mViewPos = -1;
int mLightPosPos = -1;
int mLightColorPos = -1;
int mAmbientPos = -1;
int mObjectColorPos = -1;
int mTexSlotPos = -1;
const char* vertShader = nullptr;
const char* fragShader = nullptr;
};
extern Shader* CurrentShader;
extern const char* FragShaderNorm;
extern const char* FragShaderFlat;
extern const char* VertShader3DNorm;
extern const char* VertShader3DInvNorm;
extern const char* VertShader2DTex;
extern const char* FragShader2dTex;
} // namespace MillSim
#endif // !__shader_h__

View File

@@ -0,0 +1,403 @@
/***************************************************************************
* 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 "SimShapes.h"
#include "Shader.h"
#include "GlUtils.h"
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include <cstddef>
using namespace MillSim;
static float* sinTable = nullptr;
static float* cosTable = nullptr;
static int lastNumSlices = 0;
static int lastNumSectionIndices = 0;
static GLshort quadIndices[] = { 0, 2, 3, 0, 3, 1 };
static GLshort quadIndicesReversed[] = { 0, 3, 2, 0, 1, 3 };
static GLshort* sectionIndicesQuad = nullptr;
static GLshort* sectionIndicesTri = nullptr;
static bool GenerateSinTable(int nSlices)
{
if (nSlices == lastNumSlices)
return true;
if (sinTable != nullptr)
free(sinTable);
if (cosTable != nullptr)
free(cosTable);
sinTable = cosTable = nullptr;
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[i] = sinf(slice * i);
cosTable[i] = cosf(slice * i);
}
lastNumSlices = nvals;
return true;
}
void MillSim::Shape::RotateProfile(float* profPoints, int nPoints, float distance, float deltaHeight, int nSlices, bool isHalfTurn)
{
int vidx = 0;
int iidx = 0;
int numVerts, numIndices;
int vstart;
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;
}
int nsinvals = nSlices;
if (isHalfTurn)
nsinvals *= 2;
if (GenerateSinTable(nsinvals) == false)
{
free(vbuffer);
free(ibuffer);
return;
}
for (int i = 0; i < nPoints; i++)
{
int i2 = i * 2;
float prevy = i > 0 ? profPoints[i2 - 2] : 0;
float prevz = i > 0 ? profPoints[i2 - 1] : profPoints[i2 + 1];
float prevrad = fabsf(prevy);
float rad = fabsf(profPoints[i2]);
float z2 = profPoints[i2 + 1];
float diffy = profPoints[i2] - prevy;
float diffz = z2 - prevz;
float len = sqrtf(diffy * diffy + diffz * diffz);
float nz = diffy / len;
vstart = i * 2 * (nSlices + 1);
for (int j = 0; j <= nSlices; j++)
{
// generate vertices
float sx = sinTable[j];
float sy = cosTable[j];
float x1 = prevrad * sx + distance;
float y1 = prevrad * sy;
float x2 = rad * sx + distance;
float y2 = rad * sy;
// generate normals
float ny = -diffz / len;
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);
if (j != nSlices)
{
// generate indices { 0, 3, 1, 0, 2, 3 }
int pos = vstart + 2 * j;
if (i < (nPoints - 1))
SET_TRIPLE(ibuffer, iidx, pos, pos + 3, pos + 1);
if (i > 0)
SET_TRIPLE(ibuffer, iidx, pos, pos + 2, pos + 3);
}
}
}
GenerateModel(vbuffer, ibuffer, numVerts, numIndices);
free(vbuffer);
free(ibuffer);
}
void MillSim::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;
*ic1idx = *numIndices;
*numIndices += (nProfilePoints - 2) * 3;
}
if (capEnd)
{
*vc2idx = *numVerts * 6;
*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)
{
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;
float* vbuffer = (float*)malloc(numVerts * sizeof(Vertex));
if (!vbuffer)
return;
GLushort* ibuffer = (GLushort*)malloc(numIndices * sizeof(GLushort));
if (!ibuffer)
{
free(vbuffer);
return;
}
bool is_clockwise = angleRad > 0;
angleRad = (float)fabs(angleRad);
float dir = is_clockwise ? 1.0f : -1.0f;
int offs1 = is_clockwise ? -1 : 0;
int offs2 = is_clockwise ? 0 : -1;
float cosAng = cosf(angleRad);
float sinAng = sinf(angleRad);
for (int i = 0; i < nPoints; i++)
{
int p1 = i * 2;
float y1 = profPoints[p1] + radius;
float z1 = profPoints[p1 + 1];
int p2 = (p1 + 2) % (nPoints * 2);
float y2 = profPoints[p2] + radius;
float z2 = profPoints[p2 + 1];
// normals
float ydiff = y2 - y1;
float zdiff = z2 - z1;
float len = sqrtf(ydiff * ydiff + zdiff * zdiff);
float ny = -zdiff / len;
float nz = ydiff / len;
float nx = -sinAng * ny;
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);
if (capStart) {
SET_TRIPLE(vbuffer, vc1idx, 0, y1, z1);
SET_TRIPLE(vbuffer, vc1idx, -1 * dir, 0, 0);
if (i > 1)
SET_TRIPLE(ibuffer, ic1idx, vc1start, vc1start + i + offs1, vc1start + i + offs2);
}
float x1 = y1 * sinAng * dir;
float x2 = y2 * sinAng * dir;
y1 *= cosAng;
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);
// face have 2 triangles { 0, 2, 3, 0, 3, 1 };
GLushort vistart = i * 4;
if (is_clockwise)
{
SET_TRIPLE(ibuffer, iidx, vistart, vistart + 2, vistart + 3);
SET_TRIPLE(ibuffer, iidx, vistart, vistart + 3, vistart + 1);
}
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)
SET_TRIPLE(ibuffer, ic2idx, vc2start, vc2start + i + offs2, vc2start + i + offs1);
}
}
GenerateModel(vbuffer, ibuffer, numVerts, numIndices);
free(vbuffer);
free(ibuffer);
}
void MillSim::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;
float* vbuffer = (float*)malloc(numVerts * sizeof(Vertex));
if (!vbuffer)
return;
GLushort* ibuffer = (GLushort*)malloc(numIndices * sizeof(GLushort));
if (!ibuffer)
{
free(vbuffer);
return;
}
for (int i = 0; i < nPoints; i++)
{
// hollow pipe verts
int p1 = i * 2;
float y1 = profPoints[p1];
float z1 = profPoints[p1 + 1];
int p2 = (p1 + 2) % (nPoints * 2);
float y2 = profPoints[p2];
float z2 = profPoints[p2 + 1];
// nornal
float ydiff = y2 - y1;
float zdiff = z2 - z1;
float len = sqrtf(ydiff * ydiff + zdiff * zdiff);
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);
// 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)
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)
SET_TRIPLE(ibuffer, ic2idx, vc2start, vc2start + i, vc2start + i - 1);
}
}
GenerateModel(vbuffer, ibuffer, numVerts, numIndices);
free(vbuffer);
free(ibuffer);
}
void MillSim::Shape::GenerateModel(float* vbuffer, GLushort* ibuffer, int numVerts, int nIndices)
{
//GLuint vbo, ibo, vao;
// vertex buffer
glGenBuffers(1, &vbo);
GLClearError();
glBindBuffer(GL_ARRAY_BUFFER, vbo);
GLLogError();
glBufferData(GL_ARRAY_BUFFER, numVerts * sizeof(Vertex), vbuffer, GL_STATIC_DRAW);
// index buffer
glGenBuffers(1, &ibo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, nIndices * sizeof(GLushort), ibuffer, GL_STATIC_DRAW);
// vertex array
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, x));
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, nx));
numIndices = nIndices;
}
void MillSim::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
{
CurrentShader->UpdateModelMat(modelMat, normallMat);
Render();
}
void MillSim::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;
}
}
MillSim::Shape::~Shape()
{
FreeResources();
}

View File

@@ -0,0 +1,114 @@
/***************************************************************************
* 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 __sim_shapes_h__
#define __sim_shapes_h__
#include "OpenGlWrapper.h"
#include "linmath.h"
#define SET_DUAL(var, idx, y, z) \
{ \
var[idx++] = y; \
var[idx++] = z; \
}
#define SET_TRIPLE(var, idx, x, y, z) \
{ \
var[idx++] = x; \
var[idx++] = y; \
var[idx++] = z; \
}
// #define SET_TRIPLE(var, idx, x, y, z) {var[idx] = x; var[idx+1] = y; var[idx+2] = z;}
// #define SET_TRIPLE_OFFS(var, idx, offs, x, y, z) {var[idx] = x + offs; var[idx+1] = y + offs;
// var[idx+2] = z + offs;}
#define SET_TRIPLE_OFFS(var, idx, offs, x, y, z) \
{ \
var[idx++] = x + offs; \
var[idx++] = y + offs; \
var[idx++] = z + offs; \
}
namespace MillSim
{
typedef unsigned int uint;
struct Vertex
{
float x, y, z;
float nx, ny, nz;
};
class Shape
{
public:
Shape()
{}
~Shape();
public:
uint vao = 0;
uint vbo = 0;
uint ibo = 0;
int numIndices = 0;
public:
void Render();
void Render(mat4x4 modelMat, mat4x4 normallMat);
void FreeResources();
void RotateProfile(float* profPoints,
int nPoints,
float distance,
float deltaHeight,
int nSlices,
bool isHalfTurn);
void ExtrudeProfileRadial(float* profPoints,
int nPoints,
float radius,
float angleRad,
float deltaHeight,
bool capStart,
bool capEnd);
void ExtrudeProfileLinear(float* profPoints,
int nPoints,
float fromX,
float toX,
float fromZ,
float toZ,
bool capStart,
bool capEnd);
protected:
void GenerateModel(float* vbuffer, GLushort* ibuffer, int numVerts, int numIndices);
void CalculateExtrudeBufferSizes(int nProfilePoints,
bool capStart,
bool capEnd,
int* numVerts,
int* numIndices,
int* vc1idx,
int* vc2idx,
int* ic1idx,
int* ic2idx);
};
} // namespace MillSim
#endif

View File

@@ -0,0 +1,64 @@
/***************************************************************************
* 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 "StockObject.h"
#include "Shader.h"
#include <stdlib.h>
#define NUM_PROFILE_POINTS 4
MillSim::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)
{
int idx = 0;
SET_DUAL(mProfile, idx, y + w, z + h);
SET_DUAL(mProfile, idx, y + w, z);
SET_DUAL(mProfile, idx, y, z);
SET_DUAL(mProfile, idx, y, z + h);
vec3_set(center, x + l / 2, y + w / 2, z + h / 2);
vec3_set(size, l, w, h);
shape.ExtrudeProfileLinear(mProfile, NUM_PROFILE_POINTS, x, x + l, 0, 0, true, true);
}

View File

@@ -0,0 +1,52 @@
/***************************************************************************
* 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 __stock_object_h__
#define __stock_object_h__
#include "SimShapes.h"
#include "linmath.h"
namespace MillSim
{
class StockObject
{
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

View File

@@ -0,0 +1,61 @@
/***************************************************************************
* 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 "Texture.h"
#include "GlUtils.h"
namespace MillSim
{
Texture::~Texture()
{
glDeleteTextures(1, &mTextureId);
}
bool Texture::LoadImage(unsigned int* image, int _width, int _height)
{
width = _width;
height = _height;
glGenTextures(1, &mTextureId);
glBindTexture(GL_TEXTURE_2D, mTextureId);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, image);
glBindTexture(GL_TEXTURE_2D, 0);
return true;
}
bool Texture::Activate()
{
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, mTextureId);
return true;
}
bool Texture::unbind()
{
glBindTexture(GL_TEXTURE_2D, 0);
return true;
}
} // namespace MillSim

View File

@@ -0,0 +1,60 @@
/***************************************************************************
* 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 __texture_h__
#define __texture_h__
#include "OpenGlWrapper.h"
namespace MillSim
{
class Texture
{
public:
Texture()
{}
~Texture();
bool LoadImage(unsigned int* image, int x, int y);
bool Activate();
bool unbind();
float getTexX(int imgX)
{
return (float)imgX / (float)width;
}
float getTexY(int imgY)
{
return (float)imgY / (float)height;
}
public:
int width = 0;
int height = 0;
protected:
unsigned int mTextureId = -1;
};
} // namespace MillSim
#endif // !__texture_h__

View File

@@ -0,0 +1,98 @@
/***************************************************************************
* 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 "TextureLoader.h"
using namespace MillSim;
TextureItem texItems[] = {
{1, 36, 0, 0},
{1, 1, 0, 0},
{70, 1, 0, 0},
{100, 1, 0, 0},
{134, 1, 0, 0},
{172, 1, 0, 0},
{210, 1, 0, 0},
{2, 50, 0, 0},
{70, 50, 0, 0},
{27, 50, 0, 0},
{44, 50, 0, 0},
};
#define NUM_TEX_ITEMS (sizeof(texItems) / sizeof(TextureItem))
int sssize = -1;
TextureLoader::TextureLoader(std::string imgFolder, std::vector<std::string> fileNames, int textureSize)
: mImageFolder(imgFolder)
{
int buffsize = textureSize * textureSize * sizeof(unsigned int);
mRawData = (unsigned int*)malloc(buffsize);
if (mRawData == nullptr) {
return;
}
memset(mRawData, 0x80, buffsize);
for (int i = 0; i < fileNames.size(); i++) {
QImage pixmap((imgFolder + fileNames[i]).c_str());
AddImage(&(texItems[i]), pixmap, mRawData, textureSize);
}
}
// parse compressed image into a texture buffer
bool TextureLoader::AddImage(TextureItem* texItem,
QImage& pixmap,
unsigned int* buffPos,
int stride)
{
int width = pixmap.width();
int height = pixmap.height();
int buffLen = width * height;
buffPos += stride * texItem->ty + texItem->tx;
for (int i = 0; i < height; i++) {
unsigned int* line = reinterpret_cast<unsigned int *>(pixmap.scanLine(i));
for (int j = 0; j < width; j++) {
buffPos[j] = line[j];
}
buffPos += stride;
}
texItem->w = width;
texItem->h = height;
return true;
}
MillSim::TextureLoader::~TextureLoader()
{
if (mRawData != nullptr) {
free(mRawData);
}
}
unsigned int* MillSim::TextureLoader::GetRawData()
{
return mRawData;
}
TextureItem* MillSim::TextureLoader::GetTextureItem(int i)
{
return texItems + i;
}

View File

@@ -0,0 +1,55 @@
/***************************************************************************
* 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 __texture_loader_h__
#define __texture_loader_h__
#include <string>
#include <vector>
#include <QImage>
namespace MillSim
{
struct TextureItem
{
int tx, ty; // texture location
int w, h; // item size
};
class TextureLoader
{
public:
TextureLoader(std::string imgFolder, std::vector<std::string> fileNames, int textureSize);
~TextureLoader();
unsigned int* GetRawData();
TextureItem* GetTextureItem(int i);
protected:
bool AddImage(TextureItem* guiItem, QImage& pixmap, unsigned int* buffPos, int stride);
protected:
unsigned int* mRawData = nullptr;
std::string mImageFolder;
};
} // namespace MillSim
#endif // !__texture_loader_h__

View File

@@ -0,0 +1,634 @@
/*************************************************************************************************/
/* 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
#else
#define LINMATH_H_FUNC static inline
#endif
#define LINMATH_H_DEFINE_VEC(n) \
typedef float vec##n[n]; \
LINMATH_H_FUNC void vec##n##_add(vec##n r, vec##n const a, vec##n const b) \
{ \
int i; \
for (i = 0; i < n; ++i) \
r[i] = a[i] + b[i]; \
} \
LINMATH_H_FUNC void vec##n##_sub(vec##n r, vec##n const a, vec##n const b) \
{ \
int i; \
for (i = 0; i < n; ++i) \
r[i] = a[i] - b[i]; \
} \
LINMATH_H_FUNC void vec##n##_scale(vec##n r, vec##n const v, float const s) \
{ \
int i; \
for (i = 0; i < n; ++i) \
r[i] = v[i] * s; \
} \
LINMATH_H_FUNC float vec##n##_mul_inner(vec##n const a, vec##n const b) \
{ \
float p = 0.f; \
int i; \
for (i = 0; i < n; ++i) \
p += b[i] * a[i]; \
return p; \
} \
LINMATH_H_FUNC float vec##n##_len(vec##n const v) \
{ \
return sqrtf(vec##n##_mul_inner(v, v)); \
} \
LINMATH_H_FUNC void vec##n##_norm(vec##n r, vec##n const v) \
{ \
float k = 1.f / vec##n##_len(v); \
vec##n##_scale(r, v, k); \
} \
LINMATH_H_FUNC void vec##n##_min(vec##n r, vec##n const a, vec##n const b) \
{ \
int i; \
for (i = 0; i < n; ++i) \
r[i] = a[i] < b[i] ? a[i] : b[i]; \
} \
LINMATH_H_FUNC void vec##n##_max(vec##n r, vec##n const a, vec##n const b) \
{ \
int i; \
for (i = 0; i < n; ++i) \
r[i] = a[i] > b[i] ? a[i] : b[i]; \
} \
LINMATH_H_FUNC void vec##n##_dup(vec##n r, vec##n const src) \
{ \
int i; \
for (i = 0; i < n; ++i) \
r[i] = src[i]; \
}
LINMATH_H_DEFINE_VEC(2)
LINMATH_H_DEFINE_VEC(3)
LINMATH_H_DEFINE_VEC(4)
LINMATH_H_FUNC void vec3_set(vec3 r, float x, float y, float z)
{
r[0] = x;
r[1] = y;
r[2] = z;
}
LINMATH_H_FUNC void vec3_mul_cross(vec3 r, vec3 const a, vec3 const b)
{
r[0] = a[1] * b[2] - a[2] * b[1];
r[1] = a[2] * b[0] - a[0] * b[2];
r[2] = a[0] * b[1] - a[1] * b[0];
}
LINMATH_H_FUNC void vec3_reflect(vec3 r, vec3 const v, vec3 const n)
{
float p = 2.f * vec3_mul_inner(v, n);
int i;
for (i = 0; i < 3; ++i) {
r[i] = v[i] - p * n[i];
}
}
LINMATH_H_FUNC void vec4_mul_cross(vec4 r, vec4 const a, vec4 const b)
{
r[0] = a[1] * b[2] - a[2] * b[1];
r[1] = a[2] * b[0] - a[0] * b[2];
r[2] = a[0] * b[1] - a[1] * b[0];
r[3] = 1.f;
}
LINMATH_H_FUNC void vec4_reflect(vec4 r, vec4 const v, vec4 const n)
{
float p = 2.f * vec4_mul_inner(v, n);
int i;
for (i = 0; i < 4; ++i) {
r[i] = v[i] - p * n[i];
}
}
typedef vec4 mat4x4[4];
LINMATH_H_FUNC void mat4x4_identity(mat4x4 M)
{
int i, j;
for (i = 0; i < 4; ++i) {
for (j = 0; j < 4; ++j) {
M[i][j] = i == j ? 1.f : 0.f;
}
}
}
LINMATH_H_FUNC void mat4x4_dup(mat4x4 M, mat4x4 const N)
{
int i;
for (i = 0; i < 4; ++i) {
vec4_dup(M[i], N[i]);
}
}
LINMATH_H_FUNC void mat4x4_row(vec4 r, mat4x4 const M, int i)
{
int k;
for (k = 0; k < 4; ++k) {
r[k] = M[k][i];
}
}
LINMATH_H_FUNC void mat4x4_col(vec4 r, mat4x4 const M, int i)
{
int k;
for (k = 0; k < 4; ++k) {
r[k] = M[i][k];
}
}
LINMATH_H_FUNC void mat4x4_transpose(mat4x4 M, mat4x4 const N)
{
// Note: if M and N are the same, the user has to
// explicitly make a copy of M and set it to N.
int i, j;
for (j = 0; j < 4; ++j) {
for (i = 0; i < 4; ++i) {
M[i][j] = N[j][i];
}
}
}
LINMATH_H_FUNC void mat4x4_add(mat4x4 M, mat4x4 const a, mat4x4 const b)
{
int i;
for (i = 0; i < 4; ++i) {
vec4_add(M[i], a[i], b[i]);
}
}
LINMATH_H_FUNC void mat4x4_sub(mat4x4 M, mat4x4 const a, mat4x4 const b)
{
int i;
for (i = 0; i < 4; ++i) {
vec4_sub(M[i], a[i], b[i]);
}
}
LINMATH_H_FUNC void mat4x4_scale(mat4x4 M, mat4x4 const a, float k)
{
int i;
for (i = 0; i < 4; ++i) {
vec4_scale(M[i], a[i], k);
}
}
LINMATH_H_FUNC void mat4x4_scale_aniso(mat4x4 M, mat4x4 const a, float x, float y, float z)
{
vec4_scale(M[0], a[0], x);
vec4_scale(M[1], a[1], y);
vec4_scale(M[2], a[2], z);
vec4_dup(M[3], a[3]);
}
LINMATH_H_FUNC void mat4x4_mul(mat4x4 M, mat4x4 const a, mat4x4 const b)
{
mat4x4 temp;
int k, r, c;
for (c = 0; c < 4; ++c) {
for (r = 0; r < 4; ++r) {
temp[c][r] = 0.f;
for (k = 0; k < 4; ++k) {
temp[c][r] += a[k][r] * b[c][k];
}
}
}
mat4x4_dup(M, temp);
}
LINMATH_H_FUNC void mat4x4_mul_vec4(vec4 r, mat4x4 const M, vec4 const v)
{
int i, j;
for (j = 0; j < 4; ++j) {
r[j] = 0.f;
for (i = 0; i < 4; ++i) {
r[j] += M[i][j] * v[i];
}
}
}
LINMATH_H_FUNC void mat4x4_translate(mat4x4 T, float x, float y, float z)
{
mat4x4_identity(T);
T[3][0] = x;
T[3][1] = y;
T[3][2] = z;
}
LINMATH_H_FUNC void mat4x4_translate_in_place(mat4x4 M, float x, float y, float z)
{
vec4 t = {x, y, z, 0};
vec4 r;
int i;
for (i = 0; i < 4; ++i) {
mat4x4_row(r, M, i);
M[3][i] += vec4_mul_inner(r, t);
}
}
LINMATH_H_FUNC void mat4x4_translate_in_place_v(mat4x4 M, vec3 v)
{
vec4 t = {v[0], v[1], v[2], 0};
vec4 r;
int i;
for (i = 0; i < 4; ++i) {
mat4x4_row(r, M, i);
M[3][i] += vec4_mul_inner(r, t);
}
}
LINMATH_H_FUNC void mat4x4_from_vec3_mul_outer(mat4x4 M, vec3 const a, vec3 const b)
{
int i, j;
for (i = 0; i < 4; ++i) {
for (j = 0; j < 4; ++j) {
M[i][j] = i < 3 && j < 3 ? a[i] * b[j] : 0.f;
}
}
}
LINMATH_H_FUNC void mat4x4_rotate(mat4x4 R, mat4x4 const M, float x, float y, float z, float angle)
{
float s = sinf(angle);
float c = cosf(angle);
vec3 u = {x, y, z};
if (vec3_len(u) > 1e-4) {
vec3_norm(u, u);
mat4x4 T;
mat4x4_from_vec3_mul_outer(T, u, u);
mat4x4 S = {{0, u[2], -u[1], 0}, {-u[2], 0, u[0], 0}, {u[1], -u[0], 0, 0}, {0, 0, 0, 0}};
mat4x4_scale(S, S, s);
mat4x4 C;
mat4x4_identity(C);
mat4x4_sub(C, C, T);
mat4x4_scale(C, C, c);
mat4x4_add(T, T, C);
mat4x4_add(T, T, S);
T[3][3] = 1.f;
mat4x4_mul(R, M, T);
}
else {
mat4x4_dup(R, M);
}
}
LINMATH_H_FUNC void mat4x4_rotate_X(mat4x4 Q, mat4x4 const M, float angle)
{
float s = sinf(angle);
float c = cosf(angle);
mat4x4 R = {{1.f, 0.f, 0.f, 0.f}, {0.f, c, s, 0.f}, {0.f, -s, c, 0.f}, {0.f, 0.f, 0.f, 1.f}};
mat4x4_mul(Q, M, R);
}
LINMATH_H_FUNC void mat4x4_rotate_Y(mat4x4 Q, mat4x4 const M, float angle)
{
float s = sinf(angle);
float c = cosf(angle);
mat4x4 R = {{c, 0.f, -s, 0.f}, {0.f, 1.f, 0.f, 0.f}, {s, 0.f, c, 0.f}, {0.f, 0.f, 0.f, 1.f}};
mat4x4_mul(Q, M, R);
}
LINMATH_H_FUNC void mat4x4_rotate_Z(mat4x4 Q, mat4x4 const M, float angle)
{
float s = sinf(angle);
float c = cosf(angle);
mat4x4 R = {{c, s, 0.f, 0.f}, {-s, c, 0.f, 0.f}, {0.f, 0.f, 1.f, 0.f}, {0.f, 0.f, 0.f, 1.f}};
mat4x4_mul(Q, M, R);
}
LINMATH_H_FUNC void mat4x4_invert(mat4x4 T, mat4x4 const M)
{
float s[6];
float c[6];
s[0] = M[0][0] * M[1][1] - M[1][0] * M[0][1];
s[1] = M[0][0] * M[1][2] - M[1][0] * M[0][2];
s[2] = M[0][0] * M[1][3] - M[1][0] * M[0][3];
s[3] = M[0][1] * M[1][2] - M[1][1] * M[0][2];
s[4] = M[0][1] * M[1][3] - M[1][1] * M[0][3];
s[5] = M[0][2] * M[1][3] - M[1][2] * M[0][3];
c[0] = M[2][0] * M[3][1] - M[3][0] * M[2][1];
c[1] = M[2][0] * M[3][2] - M[3][0] * M[2][2];
c[2] = M[2][0] * M[3][3] - M[3][0] * M[2][3];
c[3] = M[2][1] * M[3][2] - M[3][1] * M[2][2];
c[4] = M[2][1] * M[3][3] - M[3][1] * M[2][3];
c[5] = M[2][2] * M[3][3] - M[3][2] * M[2][3];
/* Assumes it is invertible */
float idet =
1.0f / (s[0] * c[5] - s[1] * c[4] + s[2] * c[3] + s[3] * c[2] - s[4] * c[1] + s[5] * c[0]);
T[0][0] = (M[1][1] * c[5] - M[1][2] * c[4] + M[1][3] * c[3]) * idet;
T[0][1] = (-M[0][1] * c[5] + M[0][2] * c[4] - M[0][3] * c[3]) * idet;
T[0][2] = (M[3][1] * s[5] - M[3][2] * s[4] + M[3][3] * s[3]) * idet;
T[0][3] = (-M[2][1] * s[5] + M[2][2] * s[4] - M[2][3] * s[3]) * idet;
T[1][0] = (-M[1][0] * c[5] + M[1][2] * c[2] - M[1][3] * c[1]) * idet;
T[1][1] = (M[0][0] * c[5] - M[0][2] * c[2] + M[0][3] * c[1]) * idet;
T[1][2] = (-M[3][0] * s[5] + M[3][2] * s[2] - M[3][3] * s[1]) * idet;
T[1][3] = (M[2][0] * s[5] - M[2][2] * s[2] + M[2][3] * s[1]) * idet;
T[2][0] = (M[1][0] * c[4] - M[1][1] * c[2] + M[1][3] * c[0]) * idet;
T[2][1] = (-M[0][0] * c[4] + M[0][1] * c[2] - M[0][3] * c[0]) * idet;
T[2][2] = (M[3][0] * s[4] - M[3][1] * s[2] + M[3][3] * s[0]) * idet;
T[2][3] = (-M[2][0] * s[4] + M[2][1] * s[2] - M[2][3] * s[0]) * idet;
T[3][0] = (-M[1][0] * c[3] + M[1][1] * c[1] - M[1][2] * c[0]) * idet;
T[3][1] = (M[0][0] * c[3] - M[0][1] * c[1] + M[0][2] * c[0]) * idet;
T[3][2] = (-M[3][0] * s[3] + M[3][1] * s[1] - M[3][2] * s[0]) * idet;
T[3][3] = (M[2][0] * s[3] - M[2][1] * s[1] + M[2][2] * s[0]) * idet;
}
LINMATH_H_FUNC void mat4x4_orthonormalize(mat4x4 R, mat4x4 const M)
{
mat4x4_dup(R, M);
float s = 1.f;
vec3 h;
vec3_norm(R[2], R[2]);
s = vec3_mul_inner(R[1], R[2]);
vec3_scale(h, R[2], s);
vec3_sub(R[1], R[1], h);
vec3_norm(R[1], R[1]);
s = vec3_mul_inner(R[0], R[2]);
vec3_scale(h, R[2], s);
vec3_sub(R[0], R[0], h);
s = vec3_mul_inner(R[0], R[1]);
vec3_scale(h, R[1], s);
vec3_sub(R[0], R[0], h);
vec3_norm(R[0], R[0]);
}
LINMATH_H_FUNC void mat4x4_frustum(mat4x4 M, float l, float r, float b, float t, float n, float f)
{
M[0][0] = 2.f * n / (r - l);
M[0][1] = M[0][2] = M[0][3] = 0.f;
M[1][1] = 2.f * n / (t - b);
M[1][0] = M[1][2] = M[1][3] = 0.f;
M[2][0] = (r + l) / (r - l);
M[2][1] = (t + b) / (t - b);
M[2][2] = -(f + n) / (f - n);
M[2][3] = -1.f;
M[3][2] = -2.f * (f * n) / (f - n);
M[3][0] = M[3][1] = M[3][3] = 0.f;
}
LINMATH_H_FUNC void mat4x4_ortho(mat4x4 M, float l, float r, float b, float t, float n, float f)
{
M[0][0] = 2.f / (r - l);
M[0][1] = M[0][2] = M[0][3] = 0.f;
M[1][1] = 2.f / (t - b);
M[1][0] = M[1][2] = M[1][3] = 0.f;
M[2][2] = -2.f / (f - n);
M[2][0] = M[2][1] = M[2][3] = 0.f;
M[3][0] = -(r + l) / (r - l);
M[3][1] = -(t + b) / (t - b);
M[3][2] = -(f + n) / (f - n);
M[3][3] = 1.f;
}
LINMATH_H_FUNC void mat4x4_perspective(mat4x4 m, float y_fov, float aspect, float n, float f)
{
/* NOTE: Degrees are an unhandy unit to work with.
* linmath.h uses radians for everything! */
float const a = 1.f / tanf(y_fov / 2.f);
m[0][0] = a / aspect;
m[0][1] = 0.f;
m[0][2] = 0.f;
m[0][3] = 0.f;
m[1][0] = 0.f;
m[1][1] = a;
m[1][2] = 0.f;
m[1][3] = 0.f;
m[2][0] = 0.f;
m[2][1] = 0.f;
m[2][2] = -((f + n) / (f - n));
m[2][3] = -1.f;
m[3][0] = 0.f;
m[3][1] = 0.f;
m[3][2] = -((2.f * f * n) / (f - n));
m[3][3] = 0.f;
}
LINMATH_H_FUNC void mat4x4_look_at(mat4x4 m, vec3 const eye, vec3 const center, vec3 const up)
{
/* Adapted from Android's OpenGL Matrix.java. */
/* See the OpenGL GLUT documentation for gluLookAt for a description */
/* of the algorithm. We implement it in a straightforward way: */
/* TODO: The negation of of can be spared by swapping the order of
* operands in the following cross products in the right way. */
vec3 f;
vec3_sub(f, center, eye);
vec3_norm(f, f);
vec3 s;
vec3_mul_cross(s, f, up);
vec3_norm(s, s);
vec3 t;
vec3_mul_cross(t, s, f);
m[0][0] = s[0];
m[0][1] = t[0];
m[0][2] = -f[0];
m[0][3] = 0.f;
m[1][0] = s[1];
m[1][1] = t[1];
m[1][2] = -f[1];
m[1][3] = 0.f;
m[2][0] = s[2];
m[2][1] = t[2];
m[2][2] = -f[2];
m[2][3] = 0.f;
m[3][0] = 0.f;
m[3][1] = 0.f;
m[3][2] = 0.f;
m[3][3] = 1.f;
mat4x4_translate_in_place(m, -eye[0], -eye[1], -eye[2]);
}
typedef float quat[4];
#define quat_add vec4_add
#define quat_sub vec4_sub
#define quat_norm vec4_norm
#define quat_scale vec4_scale
#define quat_mul_inner vec4_mul_inner
LINMATH_H_FUNC void quat_identity(quat q)
{
q[0] = q[1] = q[2] = 0.f;
q[3] = 1.f;
}
LINMATH_H_FUNC void quat_mul(quat r, quat const p, quat const q)
{
vec3 w, tmp;
vec3_mul_cross(tmp, p, q);
vec3_scale(w, p, q[3]);
vec3_add(tmp, tmp, w);
vec3_scale(w, q, p[3]);
vec3_add(tmp, tmp, w);
vec3_dup(r, tmp);
r[3] = p[3] * q[3] - vec3_mul_inner(p, q);
}
LINMATH_H_FUNC void quat_conj(quat r, quat const q)
{
int i;
for (i = 0; i < 3; ++i) {
r[i] = -q[i];
}
r[3] = q[3];
}
LINMATH_H_FUNC void quat_rotate(quat r, float angle, vec3 const axis)
{
vec3 axis_norm;
vec3_norm(axis_norm, axis);
float s = sinf(angle / 2);
float c = cosf(angle / 2);
vec3_scale(r, axis_norm, s);
r[3] = c;
}
LINMATH_H_FUNC void quat_mul_vec3(vec3 r, quat const q, vec3 const v)
{
/*
* Method by Fabian 'ryg' Giessen (of Farbrausch)
t = 2 * cross(q.xyz, v)
v' = v + q.w * t + cross(q.xyz, t)
*/
vec3 t;
vec3 q_xyz = {q[0], q[1], q[2]};
vec3 u = {q[0], q[1], q[2]};
vec3_mul_cross(t, q_xyz, v);
vec3_scale(t, t, 2);
vec3_mul_cross(u, q_xyz, t);
vec3_scale(t, t, q[3]);
vec3_add(r, v, t);
vec3_add(r, r, u);
}
LINMATH_H_FUNC void mat4x4_from_quat(mat4x4 M, quat const q)
{
float a = q[3];
float b = q[0];
float c = q[1];
float d = q[2];
float a2 = a * a;
float b2 = b * b;
float c2 = c * c;
float d2 = d * d;
M[0][0] = a2 + b2 - c2 - d2;
M[0][1] = 2.f * (b * c + a * d);
M[0][2] = 2.f * (b * d - a * c);
M[0][3] = 0.f;
M[1][0] = 2 * (b * c - a * d);
M[1][1] = a2 - b2 + c2 - d2;
M[1][2] = 2.f * (c * d + a * b);
M[1][3] = 0.f;
M[2][0] = 2.f * (b * d + a * c);
M[2][1] = 2.f * (c * d - a * b);
M[2][2] = a2 - b2 - c2 + d2;
M[2][3] = 0.f;
M[3][0] = M[3][1] = M[3][2] = 0.f;
M[3][3] = 1.f;
}
LINMATH_H_FUNC void mat4x4o_mul_quat(mat4x4 R, mat4x4 const M, quat const q)
{
/* XXX: The way this is written only works for orthogonal matrices. */
/* TODO: Take care of non-orthogonal case. */
quat_mul_vec3(R[0], q, M[0]);
quat_mul_vec3(R[1], q, M[1]);
quat_mul_vec3(R[2], q, M[2]);
R[3][0] = R[3][1] = R[3][2] = 0.f;
R[0][3] = M[0][3];
R[1][3] = M[1][3];
R[2][3] = M[2][3];
R[3][3] = M[3][3]; // typically 1.0, but here we make it general
}
LINMATH_H_FUNC void quat_from_mat4x4(quat q, mat4x4 const M)
{
float r = 0.f;
int i;
int perm[] = {0, 1, 2, 0, 1};
int* p = perm;
for (i = 0; i < 3; i++) {
float m = M[i][i];
if (m < r) {
continue;
}
m = r;
p = &perm[i];
}
r = sqrtf(1.f + M[p[0]][p[0]] - M[p[1]][p[1]] - M[p[2]][p[2]]);
if (r < 1e-6) {
q[0] = 1.f;
q[1] = q[2] = q[3] = 0.f;
return;
}
q[0] = r / 2.f;
q[1] = (M[p[0]][p[1]] - M[p[1]][p[0]]) / (2.f * r);
q[2] = (M[p[2]][p[0]] - M[p[0]][p[2]]) / (2.f * r);
q[3] = (M[p[2]][p[1]] - M[p[1]][p[2]]) / (2.f * r);
}
LINMATH_H_FUNC void mat4x4_arcball(mat4x4 R, mat4x4 const M, vec2 const _a, vec2 const _b, float s)
{
vec2 a;
memcpy(a, _a, sizeof(a));
vec2 b;
memcpy(b, _b, sizeof(b));
float z_a = 0.;
float z_b = 0.;
if (vec2_len(a) < 1.) {
z_a = sqrtf(1.f - vec2_mul_inner(a, a));
}
else {
vec2_norm(a, a);
}
if (vec2_len(b) < 1.f) {
z_b = sqrtf(1.f - vec2_mul_inner(b, b));
}
else {
vec2_norm(b, b);
}
vec3 a_ = {a[0], a[1], z_a};
vec3 b_ = {b[0], b[1], z_b};
vec3 c_;
vec3_mul_cross(c_, a_, b_);
float const angle = acosf(vec3_mul_inner(a_, b_)) * s;
mat4x4_rotate(R, M, c_[0], c_[1], c_[2], angle);
}
#endif

View File

@@ -1,8 +1,9 @@
add_subdirectory(App)
# if(BUILD_GUI)
# add_subdirectory(Gui)
# endif(BUILD_GUI)
add_subdirectory(AppGL)
#if(BUILD_GUI)
# add_subdirectory(Gui)
#endif(BUILD_GUI)
# install(
# FILES