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;