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

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