diff --git a/CMakeLists.txt b/CMakeLists.txt
index 8798bfd8..8d165b49 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -2,19 +2,17 @@ cmake_minimum_required(VERSION 3.11)
project(dismech-rods)
set(CMAKE_CXX_STANDARD 17)
-# set(CMAKE_BUILD_TYPE Release)
-# set(CMAKE_BUILD_TYPE Debug)
set(CMAKE_CXX_FLAGS "-O3 -DNDEBUG -Wno-deprecated-declarations")
include(CheckCXXCompilerFlag)
CHECK_CXX_COMPILER_FLAG("-march=native" COMPILER_SUPPORTS_MARCH_NATIVE)
-if(COMPILER_SUPPORTS_MARCH_NATIVE)
+if (COMPILER_SUPPORTS_MARCH_NATIVE)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=native")
-endif()
+endif ()
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "../")
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE)
-
+set(MAGNUM_WITH_SDL2APPLICATION ON)
set(THREADS_PREFER_PTHREAD_FLAG ON)
# Per https://eigen.tuxfamily.org/dox/TopicUsingIntelMKL.html eigen should be used
@@ -34,6 +32,24 @@ find_package(Eigen3 3.4 REQUIRED)
find_package(OpenMP)
find_package(FCL REQUIRED)
+# Run with -DWITH_MAGNUM=ON to build with Magnum library.
+option(WITH_MAGNUM OFF)
+if (WITH_MAGNUM)
+ add_definitions(-DWITH_MAGNUM)
+ find_package(Corrade REQUIRED Main PluginManager Utility)
+ find_package(Magnum REQUIRED
+ GL
+ MeshTools
+ Primitives
+ Shaders
+ Trade
+ Sdl2Application
+ SceneGraph)
+ find_package(MagnumPlugins REQUIRED PngImageConverter)
+else ()
+ message(STATUS "Building with Magnum disabled")
+endif ()
+
if (${HAVE_SYMENGINE_LLVM})
message(STATUS "Testing SymEngine LLVM & SBML support - found")
else ()
@@ -49,7 +65,6 @@ include_directories(${SYMENGINE_INCLUDE_DIRS}
link_directories(${MKL_LIBRARY_DIR})
-
# Run with cmake --fresh -DCREATE_DOCS=ON .. only when documentation needs to be updated.
# If docs change and you want to preview the updates in index.html, you'll need to rm -rf * and
@@ -61,9 +76,9 @@ link_directories(${MKL_LIBRARY_DIR})
# Probably a good idea to split the documentation cmake process into its own directory later.
option(CREATE_DOCS OFF)
-if(CREATE_DOCS)
+if (CREATE_DOCS)
add_subdirectory("docs")
-else()
+else ()
add_executable(disMech
src/main.cpp
src/world.cpp
@@ -116,7 +131,13 @@ else()
src/controllers/activeEntanglementController.cpp
src/utils/utils.cpp
- )
+ src/simulation_environments/magnumDERSimulationEnvironment.cpp
+ )
+ if (WITH_MAGNUM)
+ target_sources(disMech PRIVATE
+ src/simulation_environments/magnumDERSimulationEnvironment.cpp
+ )
+ endif ()
target_include_directories(disMech PRIVATE
src
@@ -139,8 +160,23 @@ else()
${SYMENGINE_LIBRARIES}
fcl
)
+ if (WITH_MAGNUM)
+ target_link_libraries(disMech PRIVATE
+ Corrade::Main
+ Corrade::PluginManager
+ Corrade::Utility
+ Magnum::Application
+ Magnum::GL
+ Magnum::Magnum
+ Magnum::MeshTools
+ Magnum::Primitives
+ Magnum::SceneGraph
+ Magnum::Shaders
+ )
+ target_link_libraries(disMech PRIVATE MagnumPlugins::PngImageConverter)
+ endif ()
if (OpenMP_CXX_FOUND)
target_link_libraries(disMech PUBLIC OpenMP::OpenMP_CXX)
endif ()
-endif()
+endif ()
diff --git a/README.md b/README.md
index f083f073..338976f7 100755
--- a/README.md
+++ b/README.md
@@ -43,7 +43,6 @@ If you'd like DisMech to support a new feature, feel free create an issue and we
#### High priority
- [ ] Improve robustness of friction.
-- [ ] Add a more sophisticated renderer.
- [ ] Add contact logic for joints.
- [ ] Add URDF functionality for instantiating robot.
- [ ] Add shell functionality.
@@ -57,6 +56,7 @@ If you'd like DisMech to support a new feature, feel free create an issue and we
- [ ] Add more controller types.
### COMPLETED
+- [x] Add a more sophisticated renderer. PR [#12]()
- [x] Add per-limb friction coefficient logic. PR [#5](https://github.com/StructuresComp/dismech-rods/pull/5)
- [x] Add active entanglement example code.
- [x] Add limb self-contact option.
@@ -138,13 +138,43 @@ For other operating systems you should be able to modify the commands below appr
```
- [OpenGL / GLUT](https://www.opengl.org/)
- - OpenGL / GLUT is used for rendering the knot through a simple graphic.
+ - OpenGL / GLUT is used for barebones rendering through simple line graphics.
- Simply install through apt package manager:
- **Ubuntu**: `sudo apt-get install libglu1-mesa-dev freeglut3-dev mesa-common-dev`
- **macOS**: `sudo port install freeglut pkgconfig` (Note: `pkgconfig` is necessary to avoid finding system GLUT instead of `freeglut`.)
- Lapack (*included in MKL*)
+### Optional Dependencies
+
+The default renderer used in DisMech is simply an isometric view of lines depicting rod centerlines using a barebones implementation based on OpenGL.
+For users wishing to have more advanced rendering with camera pose manipulation via the mouse, they can install and build with the Magnum Engine.
+Below is an example rendering.
+
+
+
+- [Magnum Engine](https://magnum.graphics/)
+ - Before downloading magnum, users must first build and install Magnum's utility library [Corrade](https://magnum.graphics/corrade/).
+ Install instructions for various platforms can be found [here](https://doc.magnum.graphics/corrade/building-corrade.html).
+ - After installing Corrade, install instructions for Magnum can similarly be found [here](https://doc.magnum.graphics/magnum/building.html).
+ - Finally, Magnum allows users to save each rendered frame as a png file which can be used to create videos such as `ffmpeg`.
+ For this feature, we must download and build [magnum-plugins](https://doc.magnum.graphics/magnum/building-plugins.html#building-plugins-manual) as follows.
+ ```bash
+ sudo apt-get install libpng-dev # a dependency of magnum-plugins' PngImageConverter
+ git clone https://github.com/mosra/magnum-plugins
+ cd magnum-plugins && mkdir build && cd build
+ cmake -DWITH_PNGIMAGECONVERTER=ON ..
+ make -j$(nproc)
+ sudo make install
+ ```
+ - Once Corrade and Magnum are properly installed, users can build DisMech with the following additional cmake build flag:
+ ```bash
+ mkdir build && cd build
+ cmake -DWITH_MAGNUM=ON ..
+ make -j$(nproc)
+ ```
+ - For those wishing to customize the rendering settings, users can take a look and adjust the source code in `magnumDERSimulationEnvironment.cpp` accordingly.
+
***
### Running Examples
@@ -158,7 +188,7 @@ For example, using the cantilever beam example:
cp examples/cantilever_case/cantileverExample.cpp robotDescription.cpp
mkdir build && cd build
cmake ..
-make -j4
+make -j$(nproc)
cd ..
```
Afterwards, simply run the simulation using the `dismech.sh` script.
@@ -179,8 +209,12 @@ In addition, many numerical parameters can be set through the `simParams` struct
struct simParams {
double sim_time = 10; // Total time for simulation [s]
double dt = 1e-3; // Time step size [s]
- bool render = true; // Live OpenGL rendering
- bool show_mat_frames = false; // Render material frames
+ renderer_type renderer = OPENGL; // * Renderer type
+ int render_every = 1; // Rendering period
+ // Currently only for Magnum. TODO for OpenGL support.
+ string render_record_path; // Rendering frames recording path (only for Magnum).
+ // Does not record if render_record_path is an empty string.
+ bool show_mat_frames = false; // Render material frames (only for OpenGL)
double render_scale = 1.0; // Rendering scale
double dtol = 1e-2; // *^ Dynamics tolerance [m/s]
double ftol = 1e-4; // *^ Force tolerance
@@ -196,6 +230,11 @@ struct simParams {
Detailed parameter explanations:
+- `renderer_type` - Determines the renderer. Currently, available options are
+ - `HEADLESS`: No rendering. Fastest and best for rapid data generation.
+ - `OPENGL`: Fixed isometric view rendering. Renders centerlines of rods as simple lines. Relatively low computational overhead. Recommended for general use and debugging.
+ - `MAGNUM`: Camera pose manipulation via mouse. Renders rod and environment with 3D meshes and shading. Higher computational overhead. Recommended only for creating videos.
+
- `numerical_integration_scheme` - Determines the numerical integration scheme. Currently, available options are
- `FORWARD_EULER`: https://en.wikipedia.org/wiki/Euler_method
- `VERLET_POSITION`: https://en.wikipedia.org/wiki/Verlet_integration
diff --git a/media/magnum.gif b/media/magnum.gif
new file mode 100644
index 00000000..a27c0c70
Binary files /dev/null and b/media/magnum.gif differ
diff --git a/src/main.cpp b/src/main.cpp
index 48455b61..b90136e6 100755
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -3,6 +3,9 @@
#include "logging/worldLogger.h"
#include "simulation_environments/headlessDERSimulationEnvironment.h"
#include "simulation_environments/openglDERSimulationEnvironment.h"
+#ifdef WITH_MAGNUM
+#include "simulation_environments/magnumDERSimulationEnvironment.h"
+#endif
#include "robotDescription.h"
#include "global_const.h"
@@ -31,11 +34,20 @@ int main(int argc,char *argv[])
verbosity = sim_params.debug_verbosity;
unique_ptr env;
- if (sim_params.render) {
- env = make_unique(my_world, sim_params, logger, argc, argv);
- }
- else {
- env = make_unique(my_world, sim_params, logger);
+ switch(sim_params.renderer) {
+ case HEADLESS:
+ env = make_unique(my_world, sim_params, logger);
+ break;
+ case OPENGL:
+ env = make_unique(my_world, sim_params, logger, argc, argv);
+ break;
+#ifdef WITH_MAGNUM
+ case MAGNUM:
+ env = make_unique(my_world, sim_params, logger, argc, argv);
+ break;
+#endif
+ default:
+ throw std::runtime_error("Unknown renderer type provided.");
}
env->runSimulation();
diff --git a/src/robotDescription.h b/src/robotDescription.h
index 420236c6..4552ed4f 100755
--- a/src/robotDescription.h
+++ b/src/robotDescription.h
@@ -30,13 +30,20 @@ typedef enum {FORWARD_EULER,
IMPLICIT_MIDPOINT}
numerical_integration_scheme;
+typedef enum {HEADLESS,
+ OPENGL,
+ MAGNUM}
+ renderer_type;
+
struct simParams {
double sim_time = 10; // Total time for simulation [s]
double dt = 1e-3; // Time step size [s]
- bool render = true; // Live OpenGL rendering
- bool show_mat_frames = false; // Render material frames
+ renderer_type renderer = OPENGL; // Renderer type
+ bool show_mat_frames = false; // Render material frames (only for OpenGL)
double render_scale = 1.0; // Rendering scale
+ int render_every = 1; // Rendering period (only for Magnum)
+ string render_record_path; // Rendering frames recording path (only for Magnum)
double dtol = 1e-2; // Dynamics tolerance [m/s]*
double ftol = 1e-4; // Force tolerance*
int max_iter = 500; // Maximum iterations for a time step
diff --git a/src/rod_mechanics/external_forces/floorContactForce.h b/src/rod_mechanics/external_forces/floorContactForce.h
index d715b745..4c8fd5fc 100644
--- a/src/rod_mechanics/external_forces/floorContactForce.h
+++ b/src/rod_mechanics/external_forces/floorContactForce.h
@@ -17,6 +17,7 @@ class floorContactForce : public baseForce
void computeForceAndJacobian(double dt) override;
double min_dist;
+ double floor_z;
int num_contacts;
void change_slip_tol(double scale);
@@ -40,6 +41,5 @@ class floorContactForce : public baseForce
double orig_slip_tol;
double K1;
double K2;
- double floor_z;
};
#endif
\ No newline at end of file
diff --git a/src/simulation_environments/magnumDERSimulationEnvironment.cpp b/src/simulation_environments/magnumDERSimulationEnvironment.cpp
new file mode 100644
index 00000000..4a06883a
--- /dev/null
+++ b/src/simulation_environments/magnumDERSimulationEnvironment.cpp
@@ -0,0 +1,370 @@
+#include "magnumDERSimulationEnvironment.h"
+#include "eigenIncludes.h"
+
+
+/*
+ * The majority of this code's mouse interaction logic has been adapted from
+ * https://doc.magnum.graphics/magnum/mouseinteraction_2MouseInteractionExample_8cpp-example.html
+ */
+
+
+namespace Magnum {
+
+
+magnumDERSimulationEnvironment::magnumDERSimulationEnvironment(const shared_ptr& m_world, const simParams& sim_params,
+ const shared_ptr& logger, int argc, char **argv)
+ : Platform::Application({argc, argv}, NoCreate),
+ derSimulationEnvironment(m_world, sim_params, logger) {
+ const Vector2 dpi_scaling = this->dpiScaling({});
+ Configuration conf;
+ conf.setTitle("DisMech")
+// .setSize(conf.size(), dpiScaling);
+ .setSize({1280, 720}, dpi_scaling);
+ GLConfiguration gl_conf;
+ gl_conf.setSampleCount(dpi_scaling.max() < 2.0f ? 8 : 2);
+ if (!tryCreate(conf, gl_conf))
+ create(conf, gl_conf.setSampleCount(0));
+
+ // Setup background color to off-white.
+ GL::Renderer::setClearColor(0xFAF9F6_rgbf);
+
+ GL::Renderer::enable(GL::Renderer::Feature::DepthTest);
+ render_scale = (float)sim_params.render_scale;
+ render_every = sim_params.render_every;
+ render_record_path = sim_params.render_record_path;
+
+ // Render a floor if necessary. If not, turn on face culling for improved speed.
+ if (w_p->floorExists()) {
+ auto floor_z = (float)(w_p->getFloorZ() * render_scale);
+ floor_mesh = MeshTools::compile(Primitives::planeSolid());
+ floor = std::make_unique(&scene);
+ floor->
+ scale(Vector3{8.0f})
+ .rotateX(90.0_degf)
+ .translate(Vector3(0.0f, floor_z, 0.0f));
+
+ misc_drawables.push_back(std::make_unique(*floor,
+ flat_shader,
+ floor_mesh,
+ drawables,
+ 0xE0E0E0_rgbf));
+
+ grid_mesh = MeshTools::compile(Primitives::grid3DWireframe({15, 15}));
+ grid= std::make_unique(&scene);
+ grid->
+ scale(Vector3{8.0f})
+ .rotateX(90.0_degf)
+ .translate(Vector3(0.0f, floor_z, 0.0f));
+
+ misc_drawables.push_back(std::make_unique(*grid,
+ flat_shader,
+ grid_mesh,
+ drawables,
+ 0x000000_rgbf));
+ }
+ else {
+ GL::Renderer::enable(GL::Renderer::Feature::FaceCulling);
+ }
+
+ vertex_color_shader = Shaders::VertexColorGL3D{};
+ flat_shader = Shaders::FlatGL3D{};
+ phong_shader = Shaders::PhongGL{};
+
+ coordinate_frame = std::make_unique(&scene);
+ coordinate_frame->scale(Vector3{2.0f});
+ misc_drawables.push_back(std::make_unique(*coordinate_frame,
+ vertex_color_shader,
+ drawables));
+
+ camera_object = std::make_unique(&scene);
+ camera_object->
+ translate(Vector3::zAxis(5.0f))
+ .rotateX(-15.0_degf)
+ .rotateY(30.0_degf);
+ camera = std::make_unique(*camera_object);
+ camera->setProjectionMatrix(Matrix4::perspectiveProjection(45.0_degf,
+ Vector2{windowSize()}.aspectRatio(),
+ 0.01f, 100.0f));
+
+ last_depth = ((camera->projectionMatrix() * camera->cameraMatrix()).transformPoint({}).z() + 1.0f) * 0.5f;
+}
+
+Float magnumDERSimulationEnvironment::depthAt(const Vector2i &windowPosition) {
+ const Vector2i position = windowPosition * Vector2{framebufferSize()} / Vector2{windowSize()};
+ const Vector2i fbPosition{position.x(), GL::defaultFramebuffer.viewport().sizeY() - position.y() - 1};
+
+ GL::defaultFramebuffer.mapForRead(GL::DefaultFramebuffer::ReadAttachment::Front);
+ Image2D data = GL::defaultFramebuffer.read(
+ Range2Di::fromSize(fbPosition, Vector2i{1}).padded(Vector2i{2}),
+ {GL::PixelFormat::DepthComponent, GL::PixelType::Float});
+
+ return Math::min(data.pixels().asContiguous());
+}
+
+Vector3 magnumDERSimulationEnvironment::unproject(const Vector2i &windowPosition, Float depth) const {
+ const Vector2i viewSize = windowSize();
+ const Vector2i viewPosition{windowPosition.x(), viewSize.y() - windowPosition.y() - 1};
+ const Vector3 in{2 * Vector2{viewPosition} / Vector2{viewSize} - Vector2{1.0f}, depth * 2.0f - 1.0f};
+
+ return camera->projectionMatrix().inverted().transformPoint(in);
+}
+
+void magnumDERSimulationEnvironment::keyPressEvent(KeyEvent &event) {
+ if (event.key() == KeyEvent::Key::Zero || event.key() == KeyEvent::Key::NumZero) {
+ camera_object->
+ resetTransformation()
+ .translate(Vector3::zAxis(5.0f))
+ .rotateX(-15.0_degf)
+ .rotateY(30.0_degf);
+ redraw();
+ return;
+ } else if (event.key() == KeyEvent::Key::One || event.key() == KeyEvent::Key::NumOne ||
+ event.key() == KeyEvent::Key::Three || event.key() == KeyEvent::Key::NumThree ||
+ event.key() == KeyEvent::Key::Seven || event.key() == KeyEvent::Key::NumSeven) {
+ const Vector3 viewTranslation = camera_object->transformation().rotationScaling().inverted() *
+ camera_object->transformation().translation();
+
+ const Float multiplier = event.modifiers() & KeyEvent::Modifier::Ctrl ? -1.0f : 1.0f;
+
+ Matrix4 transformation;
+ if (event.key() == KeyEvent::Key::Seven || event.key() == KeyEvent::Key::NumSeven)
+ transformation = Matrix4::rotationX(-90.0_degf * multiplier);
+ else if (event.key() == KeyEvent::Key::One || event.key() == KeyEvent::Key::NumOne)
+ transformation = Matrix4::rotationY(90.0_degf - 90.0_degf * multiplier);
+ else if (event.key() == KeyEvent::Key::Three || event.key() == KeyEvent::Key::NumThree)
+ transformation = Matrix4::rotationY(90.0_degf * multiplier);
+ else
+ CORRADE_INTERNAL_ASSERT_UNREACHABLE();
+
+ camera_object->setTransformation(transformation * Matrix4::translation(viewTranslation));
+ redraw();
+ }
+}
+
+void magnumDERSimulationEnvironment::mousePressEvent(MouseEvent &event) {
+ if (event.button() != MouseEvent::Button::Left && event.button() != MouseEvent::Button::Middle)
+ return;
+
+ const Float current_depth = depthAt(event.position());
+ const Float depth = current_depth == 1.0f ? last_depth : current_depth;
+ translation_point = unproject(event.position(), depth);
+ if (current_depth != 1.0f || rotation_point.isZero()) {
+ rotation_point = translation_point;
+ last_depth = depth;
+ }
+
+ redraw();
+}
+
+void magnumDERSimulationEnvironment::mouseMoveEvent(MouseMoveEvent &event) {
+ if (last_position == Vector2i{-1}) last_position = event.position();
+ const Vector2i delta = event.position() - last_position;
+ last_position = event.position();
+
+ if (!event.buttons()) return;
+
+ if (event.modifiers() & MouseMoveEvent::Modifier::Shift) {
+ const Vector3 p = unproject(event.position(), last_depth);
+ camera_object->translateLocal(translation_point - p);
+ translation_point = p;
+ } else {
+ camera_object->transformLocal(
+ Matrix4::translation(rotation_point) *
+ Matrix4::rotationX(-0.01_radf * delta.y()) *
+ Matrix4::rotationY(-0.01_radf * delta.x()) *
+ Matrix4::translation(-rotation_point));
+ }
+
+ redraw();
+}
+
+void magnumDERSimulationEnvironment::mouseScrollEvent(MouseScrollEvent &event) {
+ const Float current_depth = depthAt(event.position());
+ const Float depth = current_depth == 1.0f ? last_depth : current_depth;
+ const Vector3 p = unproject(event.position(), depth);
+ if (current_depth != 1.0f || rotation_point.isZero()) {
+ rotation_point = p;
+ last_depth = depth;
+ }
+
+ const Float direction = event.offset().y();
+ if (!direction) return;
+
+ camera_object->translateLocal(rotation_point * direction * 0.1f);
+
+ event.setAccepted();
+ redraw();
+}
+
+void magnumDERSimulationEnvironment::drawEvent() {
+ GL::defaultFramebuffer.clear(GL::FramebufferClear::Color | GL::FramebufferClear::Depth);
+
+ camera->draw(drawables);
+
+ swapBuffers();
+
+ redraw();
+}
+
+
+void magnumDERSimulationEnvironment::renderEdge(Eigen::Vector3d top_vertex,
+ Eigen::Vector3d bot_vertex,
+ float radius, Magnum::Color3 color) {
+ top_vertex *= render_scale;
+ bot_vertex *= render_scale;
+ radius *= render_scale;
+
+ Eigen::Vector3d center_line_eigen = top_vertex - bot_vertex;
+ Eigen::Vector3d center_pos_eigen = 0.5 * (top_vertex + bot_vertex);
+
+ // Magnum uses a right-handed coordinate system where y points up
+ Magnum::Vector3 center_line(center_line_eigen.x(), center_line_eigen.z(), -center_line_eigen.y());
+ Magnum::Vector3 center_pos(center_pos_eigen.x(), center_pos_eigen.z(), -center_pos_eigen.y());
+
+ float length = center_line.length();
+ Magnum::Vector3 axis = center_line.normalized() ;
+
+ // https://doc.magnum.graphics/magnum/namespaceMagnum_1_1Primitives.html#add456eac5c394549b21ae2ae88f697b0
+ float half_length = 0.5f * length / radius;
+
+ capsule_mesh = MeshTools::compile(Primitives::capsule3DSolid(5, 5, 16, half_length));
+ edges.push_back(std::make_unique(&scene));
+ capsule_drawables.push_back(std::make_unique(*edges.back(),
+ phong_shader,
+ capsule_mesh,
+ drawables,
+ color));
+
+ Magnum::Matrix4 translation = Matrix4::translation(center_pos);
+ Magnum::Matrix4 rotation = Matrix4::rotation(Math::angle(Vector3::yAxis(), axis),
+ Math::cross(Vector3::yAxis(), axis).normalized());
+
+ Magnum::Matrix4 tf = translation * rotation * Matrix4::scaling(Vector3{radius});
+
+ edges.back()->setTransformation(tf);
+}
+
+
+void magnumDERSimulationEnvironment::runSimulation() {
+ int curr_iter = -1;
+
+ PluginManager::Manager manager;
+ Containers::Pointer converter;
+ bool record_frames = !render_record_path.empty();
+ if (record_frames) {
+ // Initialize the PNG image converter
+ converter = manager.loadAndInstantiate("PngImageConverter");
+ if (!converter) {
+ Error{} << "Cannot load the PngImageConverter plugin.";
+ }
+ }
+
+ while (w_p->simulationRunning()) {
+ curr_iter++;
+ try {
+ w_p->updateTimeStep();
+ }
+ catch (std::runtime_error &excep) {
+ if (verbosity >= 1) {
+ std::cout << "Caught a runtime_error when trying to world->updateTimeStep: " << excep.what() << std::endl;
+ std::cout << "Attempting clean shutdown..." << std::endl;
+ }
+ cleanShutdown(logger_p, is_logging);
+ }
+
+ // Output info to cmdline
+ cmdlineOutputHelper();
+
+ if (curr_iter % render_every != 0)
+ continue;
+
+ capsule_drawables.clear();
+ edges.clear();
+
+ for (const auto &limb: w_p->soft_robots->limbs) {
+ float radius = limb->rod_radius;
+ for (int i = 0; i < limb->ne; i++) {
+ if (limb->isEdgeJoint[i] != 0) {
+ continue;
+ }
+ renderEdge(limb->getVertex(i+1),
+ limb->getVertex(i),
+ radius,
+ Color3::fromHsv({210.0_degf, 0.75f, 1.0f}));
+ }
+ }
+
+ int n, l;
+ for (const auto &joint : w_p->soft_robots->joints) {
+ for (int i = 0; i < joint->ne; i++) {
+ n = joint->connected_nodes[i].first;
+ l = joint->connected_nodes[i].second;
+
+ renderEdge(w_p->soft_robots->limbs[l]->getVertex(n),
+ joint->x,
+ w_p->soft_robots->limbs[l]->rod_radius,
+ Color3::fromHsv({240.0_degf, 0.75f, 1.0f}));
+ }
+ }
+ // Perform Magnum simulation loop
+ mainLoopIteration();
+
+ if (record_frames) {
+ // Capture the frame
+ Image2D image = GL::defaultFramebuffer.read(GL::defaultFramebuffer.viewport(), {PixelFormat::RGBA8Unorm});
+
+ std::ostringstream filename;
+ filename << render_record_path << "frame_" << std::setw(5) << std::setfill('0') << curr_iter << ".png";
+
+ // Convert and save the image as a PNG file
+ if (!converter->convertToFile(image, filename.str())) {
+ Error{} << "Failed to save the image";
+ }
+ }
+ }
+}
+
+
+FlatDrawable::FlatDrawable(Object3D &object, Shaders::FlatGL3D &shader, GL::Mesh &mesh,
+ SceneGraph::DrawableGroup3D &drawables, Color3 color) :
+ SceneGraph::Drawable3D{object, &drawables},
+ shader(shader), mesh(mesh), color(color) {}
+
+void FlatDrawable::draw(const Matrix4 &transformation, SceneGraph::Camera3D &camera) {
+ shader
+ .setColor(color)
+ .setTransformationProjectionMatrix(camera.projectionMatrix() * transformation)
+ .draw(mesh);
+}
+
+CapsuleDrawable::CapsuleDrawable(Magnum::Object3D &object, Shaders::PhongGL &shader, GL::Mesh &mesh,
+ SceneGraph::DrawableGroup3D &drawables, Color3 color) :
+ SceneGraph::Drawable3D{object, &drawables},
+ shader(shader), mesh(mesh),
+ color(color) {}
+
+void CapsuleDrawable::draw(const Matrix4 &transformation, SceneGraph::Camera3D &camera) {
+ shader.setLightPositions({{1.4f, 1.0f, 0.75f, 0.0f}})
+ .setDiffuseColor(color)
+ .setAmbientColor(Color3::fromHsv({color.hue(), 0.75f, 0.3f}))
+ .setTransformationMatrix(transformation)
+ .setNormalMatrix(transformation.normalMatrix())
+ .setProjectionMatrix(camera.projectionMatrix())
+ .draw(mesh);
+}
+
+CoordinateFrame::CoordinateFrame(Object3D &object, Shaders::VertexColorGL3D &shader,
+ SceneGraph::DrawableGroup3D &drawables) :
+ SceneGraph::Drawable3D{object, &drawables},
+ shader(shader) {
+ mesh = MeshTools::compile(Primitives::axis3D());
+}
+
+void CoordinateFrame::draw(const Matrix4 &transformation, SceneGraph::Camera3D &camera) {
+ shader
+ .setTransformationProjectionMatrix(camera.projectionMatrix() * transformation *
+ Matrix4::rotationY(-90.0_degf) * Matrix4::rotationX(-90.0_degf))
+ .draw(mesh);
+}
+
+} // namespace Magnum
diff --git a/src/simulation_environments/magnumDERSimulationEnvironment.h b/src/simulation_environments/magnumDERSimulationEnvironment.h
new file mode 100644
index 00000000..d9a665eb
--- /dev/null
+++ b/src/simulation_environments/magnumDERSimulationEnvironment.h
@@ -0,0 +1,137 @@
+// magnumDERSimulationEnvironment.h
+#ifndef MAGNUMRENDERER_H
+#define MAGNUMRENDERER_H
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "derSimulationEnvironment.h"
+
+namespace Magnum {
+
+using namespace Math::Literals;
+using Object3D = SceneGraph::Object;
+using Scene3D = SceneGraph::Scene;
+
+
+class CapsuleDrawable : public SceneGraph::Drawable3D {
+public:
+ explicit CapsuleDrawable(Object3D &object, Shaders::PhongGL &shader, GL::Mesh &mesh,
+ SceneGraph::DrawableGroup3D &drawables, Color3 color);
+
+ void draw(const Matrix4 &transformation, SceneGraph::Camera3D &camera) override;
+
+private:
+ Shaders::PhongGL &shader;
+ GL::Mesh &mesh;
+ Color3 color;
+};
+
+
+class FlatDrawable : public SceneGraph::Drawable3D {
+public:
+ explicit FlatDrawable(Object3D &object, Shaders::FlatGL3D &shader, GL::Mesh &mesh,
+ SceneGraph::DrawableGroup3D &drawables, Color3 color);
+
+ void draw(const Matrix4 &transformation, SceneGraph::Camera3D &camera) override;
+
+private:
+ Shaders::FlatGL3D &shader;
+ GL::Mesh &mesh;
+ Color3 color;
+};
+
+
+class CoordinateFrame : public SceneGraph::Drawable3D {
+public:
+ explicit CoordinateFrame(Object3D &object, Shaders::VertexColorGL3D &shader,
+ SceneGraph::DrawableGroup3D &drawables);
+
+ void draw(const Matrix4 &transformation, SceneGraph::Camera3D &camera) override;
+
+private:
+ Shaders::VertexColorGL3D &shader;
+ GL::Mesh mesh;
+};
+
+
+class magnumDERSimulationEnvironment : public Platform::Application, public derSimulationEnvironment {
+public:
+ explicit magnumDERSimulationEnvironment(const shared_ptr& m_world, const simParams& sim_params,
+ const shared_ptr& logger, int argc, char **argv);
+
+ void runSimulation() override;
+
+private:
+ Float depthAt(const Vector2i &windowPosition);
+ Vector3 unproject(const Vector2i &windowPosition, Float depth) const;
+
+ float render_scale;
+ int render_every;
+ string render_record_path;
+
+ void keyPressEvent(KeyEvent &event) override;
+ void mousePressEvent(MouseEvent &event) override;
+ void mouseMoveEvent(MouseMoveEvent &event) override;
+ void mouseScrollEvent(MouseScrollEvent &event) override;
+ void drawEvent() override;
+
+ void renderEdge(Eigen::Vector3d top_vertex, Eigen::Vector3d bot_vertex, float radius, Color3 color);
+
+ Shaders::VertexColorGL3D vertex_color_shader{NoCreate};
+ Shaders::FlatGL3D flat_shader{NoCreate};
+ Shaders::PhongGL phong_shader{NoCreate};
+
+ GL::Mesh capsule_mesh{NoCreate};
+ GL::Mesh grid_mesh{NoCreate};
+ GL::Mesh floor_mesh{NoCreate};
+
+ Scene3D scene;
+ SceneGraph::DrawableGroup3D drawables;
+ std::unique_ptr camera_object;
+ std::unique_ptr camera;
+
+ std::unique_ptr floor;
+ std::unique_ptr grid;
+ std::unique_ptr coordinate_frame;
+
+ std::vector> edges;
+ std::vector> capsule_drawables;
+ std::vector> misc_drawables;
+
+ Float last_depth;
+ Vector2i last_position{-1};
+ Vector3 rotation_point, translation_point;
+};
+
+} // namespace Magnum
+
+#endif // MAGNUMRENDERER_H
diff --git a/src/simulation_environments/openglDERSimulationEnvironment.h b/src/simulation_environments/openglDERSimulationEnvironment.h
index 646a3a78..8c695da8 100644
--- a/src/simulation_environments/openglDERSimulationEnvironment.h
+++ b/src/simulation_environments/openglDERSimulationEnvironment.h
@@ -30,7 +30,7 @@ class openglDERSimulationEnvironment : public derSimulationEnvironment
/**
* Start the simulation!
*/
- void runSimulation();
+ void runSimulation() override;
static bool show_mat_frames;
static double render_scale;
diff --git a/src/world.cpp b/src/world.cpp
index 2b95e4d6..f9bf8a20 100755
--- a/src/world.cpp
+++ b/src/world.cpp
@@ -138,3 +138,16 @@ double world::getCurrentTime() const
{
return curr_time;
}
+
+
+bool world::floorExists()
+{
+ return forces->ff != nullptr;
+}
+
+double world::getFloorZ()
+{
+ if (forces->ff)
+ return forces->ff->floor_z;
+ throw std::runtime_error("Floor does not exist.");
+}
diff --git a/src/world.h b/src/world.h
index 0db01bda..fa6e64ea 100755
--- a/src/world.h
+++ b/src/world.h
@@ -36,6 +36,8 @@ class world
bool simulationRunning() const;
int getTimeStep() const;
void printSimData();
+ bool floorExists();
+ double getFloorZ();
shared_ptr soft_robots;