Browse Source

add single_color_model; colormap_model

cflin
cflin 2 years ago
parent
commit
76faa16a95
  1. 4
      .gitignore
  2. 11
      CMakeLists.txt
  3. 129725
      assets/armadillo.obj
  4. 2
      comparisons/Bullet/README.md
  5. 3
      engine/CMakeLists.txt
  6. 26
      engine/engine.json.in
  7. 1
      src/CMakeLists.txt
  8. 14
      src/main.cpp
  9. 7
      src/path_config.h.in
  10. 8
      src/viewer/UIMenu.cpp
  11. 4
      src/viewer/UISimState.cpp
  12. 12
      src/viewer/UISimState.hpp
  13. 10
      src/viewer/UIStaticSimState.cpp
  14. 169
      src/viewer/UIStaticSimState.h
  15. 16
      static_sim/CMakeLists.txt
  16. 46
      static_sim/SimTargetOption.h
  17. 8
      static_sim/StaticSim.cpp
  18. 107
      static_sim/StaticSim.h

4
.gitignore

@ -1,4 +1,6 @@
cmake-*/ cmake-*/
.idea/ .idea/
stl/ output/
*.und/
path_config.h
engine.json engine.json

11
CMakeLists.txt

@ -24,6 +24,10 @@ if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/RigidIPCOptions.cmake)
include(${CMAKE_CURRENT_SOURCE_DIR}/RigidIPCOptions.cmake) include(${CMAKE_CURRENT_SOURCE_DIR}/RigidIPCOptions.cmake)
endif () endif ()
# Edit start
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
# Edit end
################################################################################ ################################################################################
project(RigidIPC project(RigidIPC
@ -285,6 +289,9 @@ if(RIGID_IPC_WITH_OPENGL)
src/viewer/igl_viewer_ext.cpp src/viewer/igl_viewer_ext.cpp
src/viewer/UISimState.cpp src/viewer/UISimState.cpp
src/viewer/UIMenu.cpp src/viewer/UIMenu.cpp
# Edit start
src/viewer/UIStaticSimState.cpp
# Edit end
) )
endif () endif ()
@ -343,3 +350,7 @@ endif()
if (RIGID_IPC_WITH_PYTHON) if (RIGID_IPC_WITH_PYTHON)
add_subdirectory(python) add_subdirectory(python)
endif () endif ()
add_subdirectory(src)
add_subdirectory(engine)
add_subdirectory(static_sim)

129725
assets/armadillo.obj

File diff suppressed because it is too large

2
comparisons/Bullet/README.md

@ -2,7 +2,7 @@
Bullet provides two ways of modeling contacts between complex geometries. Similar to our method, the first way uses the triangle mesh geometry directly (`btGImpactMeshShape`). The second way uses convex shapes (or compound convex shapes). Bullet additionally, provides a direct way of generating approximate convex decompositions of concave shapes through Volumetric Hierarchical Approximate Decomposition (V-HACD). Bullet provides two ways of modeling contacts between complex geometries. Similar to our method, the first way uses the triangle mesh geometry directly (`btGImpactMeshShape`). The second way uses convex shapes (or compound convex shapes). Bullet additionally, provides a direct way of generating approximate convex decompositions of concave shapes through Volumetric Hierarchical Approximate Decomposition (V-HACD).
## Triangle Mesh Collisions (`btGImpactMeshShape`) ## Triangle Model Collisions (`btGImpactMeshShape`)
We modified the ImportMJCFDemo example to automatically take an MJCF scene file as input, run the scene for 10 simulated seconds, and then terminate. We use `GImpact` for any input triangle meshes, and enforce only one substep for each time step (time step size fixed at `1e-2`, `1e-3`, or `1e-4` s for our experiments). To do this we implemented mesh volume computation and enabled automatic mass computation from density input (`m=ρV`), and we also enabled mesh scaling, `dt`, and `mu` setting from MJCF file which are not supported in the original demo. We modified the ImportMJCFDemo example to automatically take an MJCF scene file as input, run the scene for 10 simulated seconds, and then terminate. We use `GImpact` for any input triangle meshes, and enforce only one substep for each time step (time step size fixed at `1e-2`, `1e-3`, or `1e-4` s for our experiments). To do this we implemented mesh volume computation and enabled automatic mass computation from density input (`m=ρV`), and we also enabled mesh scaling, `dt`, and `mu` setting from MJCF file which are not supported in the original demo.

3
engine/CMakeLists.txt

@ -0,0 +1,3 @@
# replace the absolute path to the local directory with a relative one
set(LOCAL_DIR_PATH ${CMAKE_CURRENT_SOURCE_DIR} CACHE PATH "PATH TO LOCAL DIR")
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/engine.json.in ${CMAKE_CURRENT_SOURCE_DIR}/engine.json @ONLY)

26
engine/engine.json → engine/engine.json.in

@ -6,7 +6,7 @@
"coefficient_restitution": -1, "coefficient_restitution": -1,
"gravity": [0, 9.8, 0], "gravity": [0, 9.8, 0],
"rigid_bodies": [{ "rigid_bodies": [{
"mesh": "/home/oo/project/vscode/rigid_ipc/rigid-ipc/engine/stl/crank.stl", "mesh": "@LOCAL_DIR_PATH@/stl/crank.stl",
"density": 1000, "density": 1000,
"angular_velocity": [0, 0, 450], "angular_velocity": [0, 0, 450],
"torque": [0, 0, 300], "torque": [0, 0, 300],
@ -14,59 +14,59 @@
"is_dof_fixed": [true, true, true, true, true, false], "is_dof_fixed": [true, true, true, true, true, false],
"scale": 0.001 "scale": 0.001
}, { }, {
"mesh": "/home/oo/project/vscode/rigid_ipc/rigid-ipc/engine/stl/piston1_head.stl", "mesh": "@LOCAL_DIR_PATH@/stl/piston1_head.stl",
"is_dof_fixed": [true, false, true, true, true, true], "is_dof_fixed": [true, false, true, true, true, true],
"density": 1000, "density": 1000,
"scale": 0.001 "scale": 0.001
}, { }, {
"mesh": "/home/oo/project/vscode/rigid_ipc/rigid-ipc/engine/stl/piston1_pin.stl", "mesh": "@LOCAL_DIR_PATH@/stl/piston1_pin.stl",
"is_dof_fixed": [true, false, true, true, true, true], "is_dof_fixed": [true, false, true, true, true, true],
"density": 1000, "density": 1000,
"scale": 0.001 "scale": 0.001
}, { }, {
"mesh": "/home/oo/project/vscode/rigid_ipc/rigid-ipc/engine/stl/piston1_mid.stl", "mesh": "@LOCAL_DIR_PATH@/stl/piston1_mid.stl",
"density": 1000, "density": 1000,
"scale": 0.001 "scale": 0.001
}, { }, {
"mesh": "/home/oo/project/vscode/rigid_ipc/rigid-ipc/engine/stl/piston2_head.stl", "mesh": "@LOCAL_DIR_PATH@/stl/piston2_head.stl",
"is_dof_fixed": [true, false, true, true, true, true], "is_dof_fixed": [true, false, true, true, true, true],
"density": 1000, "density": 1000,
"scale": 0.001 "scale": 0.001
}, { }, {
"mesh": "/home/oo/project/vscode/rigid_ipc/rigid-ipc/engine/stl/piston2_pin.stl", "mesh": "@LOCAL_DIR_PATH@/stl/piston2_pin.stl",
"is_dof_fixed": [true, false, true, true, true, true], "is_dof_fixed": [true, false, true, true, true, true],
"density": 1000, "density": 1000,
"scale": 0.001 "scale": 0.001
}, { }, {
"mesh": "/home/oo/project/vscode/rigid_ipc/rigid-ipc/engine/stl/piston2_mid.stl", "mesh": "@LOCAL_DIR_PATH@/stl/piston2_mid.stl",
"density": 1000, "density": 1000,
"scale": 0.001 "scale": 0.001
}, { }, {
"mesh": "/home/oo/project/vscode/rigid_ipc/rigid-ipc/engine/stl/piston3_head.stl", "mesh": "@LOCAL_DIR_PATH@/stl/piston3_head.stl",
"is_dof_fixed": [true, false, true, true, true, true], "is_dof_fixed": [true, false, true, true, true, true],
"density": 1000, "density": 1000,
"scale": 0.001 "scale": 0.001
}, { }, {
"mesh": "/home/oo/project/vscode/rigid_ipc/rigid-ipc/engine/stl/piston3_pin.stl", "mesh": "@LOCAL_DIR_PATH@/stl/piston3_pin.stl",
"is_dof_fixed": [true, false, true, true, true, true], "is_dof_fixed": [true, false, true, true, true, true],
"density": 1000, "density": 1000,
"scale": 0.001 "scale": 0.001
}, { }, {
"mesh": "/home/oo/project/vscode/rigid_ipc/rigid-ipc/engine/stl/piston3_mid.stl", "mesh": "@LOCAL_DIR_PATH@/stl/piston3_mid.stl",
"density": 1000, "density": 1000,
"scale": 0.001 "scale": 0.001
}, { }, {
"mesh": "/home/oo/project/vscode/rigid_ipc/rigid-ipc/engine/stl/piston4_head.stl", "mesh": "@LOCAL_DIR_PATH@/stl/piston4_head.stl",
"is_dof_fixed": [true, false, true, true, true, true], "is_dof_fixed": [true, false, true, true, true, true],
"density": 1000, "density": 1000,
"scale": 0.001 "scale": 0.001
}, { }, {
"mesh": "/home/oo/project/vscode/rigid_ipc/rigid-ipc/engine/stl/piston4_pin.stl", "mesh": "@LOCAL_DIR_PATH@/stl/piston4_pin.stl",
"is_dof_fixed": [true, false, true, true, true, true], "is_dof_fixed": [true, false, true, true, true, true],
"density": 1000, "density": 1000,
"scale": 0.001 "scale": 0.001
}, { }, {
"mesh": "/home/oo/project/vscode/rigid_ipc/rigid-ipc/engine/stl/piston4_mid.stl", "mesh": "@LOCAL_DIR_PATH@/stl/piston4_mid.stl",
"density": 1000, "density": 1000,
"scale": 0.001 "scale": 0.001
}] }]

1
src/CMakeLists.txt

@ -0,0 +1 @@
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/path_config.h.in ${CMAKE_CURRENT_SOURCE_DIR}/path_config.h @ONLY)

14
src/main.cpp

@ -11,10 +11,11 @@
#include <SimState.hpp> #include <SimState.hpp>
#ifdef RIGID_IPC_WITH_OPENGL #ifdef RIGID_IPC_WITH_OPENGL
#include <viewer/UISimState.hpp> #include <viewer/UISimState.hpp>
#include "viewer/UIStaticSimState.h"
#endif #endif
#include <logger.hpp> #include <logger.hpp>
#include <profiler.hpp> #include <profiler.hpp>
#include "path_config.h"
int main(int argc, char* argv[]) int main(int argc, char* argv[])
{ {
using namespace ipc::rigid; using namespace ipc::rigid;
@ -88,7 +89,16 @@ int main(int argc, char* argv[])
if (with_viewer) { if (with_viewer) {
#ifdef RIGID_IPC_WITH_OPENGL #ifdef RIGID_IPC_WITH_OPENGL
UISimState ui; // UISimState ui;
// ui.launch(scene_path);
// Static Simulation
ssim::SimTargetOption option;
option.set_option(option.U_NORM);
std::string obj_path=CMAKE_SOURCE_DIR "/assets/armadillo.obj";
std::shared_ptr<ssim::StaticSim> sp_StaticSim = std::make_shared<ssim::StaticSim>(option,obj_path);
UIStaticSimState ui(sp_StaticSim);
ui.launch(scene_path); ui.launch(scene_path);
#else #else
exit(app.exit(CLI::Error( exit(app.exit(CLI::Error(

7
src/path_config.h.in

@ -0,0 +1,7 @@
#ifndef PATH_CONFIG_H_IN
#define PATH_CONFIG_H_IN
#cmakedefine CMAKE_SOURCE_DIR "@CMAKE_SOURCE_DIR@"
#endif

8
src/viewer/UIMenu.cpp

@ -236,10 +236,10 @@ void UISimState::draw_settings()
ImGui::EndTooltip(); ImGui::EndTooltip();
} }
if (ImGui::Button("应力##IO", ImVec2(-1, 0))) { if (ImGui::Button("应力##IO", ImVec2(-1, 0))) {
std::string fname = igl::file_dialog_open(); // std::string fname = igl::file_dialog_open();
if (fname != "") { // if (fname != "") {
load(fname); // load(fname);
} // }
} }
} }

4
src/viewer/UISimState.cpp

@ -5,7 +5,7 @@
#include <physics/rigid_body_problem.hpp> #include <physics/rigid_body_problem.hpp>
#include <utils/eigen_ext.hpp> #include <utils/eigen_ext.hpp>
#include "path_config.h"
namespace ipc::rigid { namespace ipc::rigid {
UISimState::UISimState() UISimState::UISimState()
@ -55,7 +55,7 @@ void UISimState::init(igl::opengl::glfw::Viewer* _viewer)
ImGuiIO& io = ImGui::GetIO(); ImGuiIO& io = ImGui::GetIO();
io.Fonts->Clear(); io.Fonts->Clear();
io.Fonts->AddFontFromFileTTF( io.Fonts->AddFontFromFileTTF(
"/home/oo/project/vscode/rigid_ipc/rigid-ipc/src/viewer/Arial Unicode MS.TTF", CMAKE_SOURCE_DIR "/src/viewer/Arial Unicode MS.TTF",
14.0f, nullptr, io.Fonts->GetGlyphRangesChineseFull()); 14.0f, nullptr, io.Fonts->GetGlyphRangesChineseFull());
viewer->data().clear(); viewer->data().clear();

12
src/viewer/UISimState.hpp

@ -28,7 +28,7 @@ class UISimState : public igl::opengl::glfw::imgui::ImGuiMenu {
public: public:
UISimState(); UISimState();
~UISimState() override {} virtual ~UISimState() override {}
enum PlayerState { Playing = 0, Paused, TotalPlayerStatus }; enum PlayerState { Playing = 0, Paused, TotalPlayerStatus };
@ -40,11 +40,11 @@ public:
std::shared_ptr<igl::opengl::ViewerDataExt> std::shared_ptr<igl::opengl::ViewerDataExt>
get_data(const std::string& data) const; get_data(const std::string& data) const;
void launch(const std::string& inital_scene); virtual void launch(const std::string& inital_scene);
void load_scene(); virtual void load_scene();
void redraw_scene(); virtual void redraw_scene();
bool pre_draw_loop(); virtual bool pre_draw_loop();
bool post_draw_loop(); virtual bool post_draw_loop();
bool custom_key_pressed(unsigned int unicode_key, int modifiers); bool custom_key_pressed(unsigned int unicode_key, int modifiers);

10
src/viewer/UIStaticSimState.cpp

@ -0,0 +1,10 @@
//
// Created by cflin on 4/7/23.
//
#include "UIStaticSimState.h"
namespace ipc {
namespace rigid {
} // ipc
} // rigid

169
src/viewer/UIStaticSimState.h

@ -0,0 +1,169 @@
//
// Created by cflin on 4/7/23.
//
#ifndef RIGIDIPC_UISTATICSIMSTATE_H
#define RIGIDIPC_UISTATICSIMSTATE_H
#include <memory> // shared_ptr
#include <spdlog/spdlog.h>
#include <igl/Timer.h>
#include <igl/opengl/glfw/Viewer.h>
#include <igl/opengl/glfw/imgui/ImGuiMenu.h>
#include <igl/png/render_to_png.h>
#include <igl/png/writePNG.h>
#include <igl/jet.h>
#include <viewer/igl_viewer_ext.hpp>
#include "UISimState.hpp"
#include "path_config.h"
#include "../../static_sim/StaticSim.h"
namespace ipc {
namespace rigid {
struct GUICtrl {
bool mesh_visible = true;
bool single_color = false;
};
class UIStaticSimState : public UISimState {
typedef igl::opengl::glfw::imgui::ImGuiMenu Super;
public:
UIStaticSimState(std::shared_ptr<ssim::StaticSim> sp_StaticSim) : UISimState(),sp_StaticSim_(sp_StaticSim),display_target(ssim::SimTargetOption::U_NORM) {
// set gui_ctrl_;
gui_ctrl_.single_color = true;
gui_ctrl_.mesh_visible = true;
}
virtual ~UIStaticSimState() override {}
virtual void init(igl::opengl::glfw::Viewer *_viewer) override {
Super::init(_viewer);
ImGuiIO &io = ImGui::GetIO();
io.Fonts->Clear();
io.Fonts->AddFontFromFileTTF(
CMAKE_SOURCE_DIR "/src/viewer/Arial Unicode MS.TTF",
14.0f, nullptr, io.Fonts->GetGlyphRangesChineseFull());
viewer->data().clear();
load_scene();
}
// virtual void draw_menu() override;
void load_scene() {
ssim::Model model = sp_StaticSim_->get_mesh();
const Eigen::MatrixXd &V = model.V;
const Eigen::MatrixXi &F = model.F;
Eigen::MatrixXd Target = sp_StaticSim_->EvaluateTarget(display_target);
// plot
viewer->data().set_mesh(V, F);
// colormap
Eigen::MatrixXd C;
igl::jet(Target, true, C);
viewer->data().set_colors(C);
m_has_scene = true;
int dim = V.cols();
m_viewer.core().trackball_angle.setIdentity();
m_viewer.core().set_rotation_type(
dim == 2 ? igl::opengl::ViewerCore::ROTATION_TYPE_NO_ROTATION
: igl::opengl::ViewerCore::
ROTATION_TYPE_TWO_AXIS_VALUATOR_FIXED_UP);
m_viewer.core().orthographic = dim == 2;
m_viewer.core().lighting_factor = 0.0; // dim == 2 ? 0.0 : 1.0;
// mesh_data->data().set_face_based(true);
assert(dim == 3.0);
m_viewer.core().align_camera_center(
V, F);
// Default colors
m_viewer.core().background_color << 0.9f, 0.9f, 0.9f, 0.4f;
// background_color << 0.3f, 0.3f, 0.5f, 1.0f;
// Camera parameters
m_viewer.core().camera_zoom = 1.0f;
m_viewer.core().camera_translation << 0, 0, 0;
}
void launch(const std::string &inital_scene) override {
m_viewer.plugins.push_back(this);
m_viewer.core().set_rotation_type(
igl::opengl::ViewerCore::ROTATION_TYPE_NO_ROTATION);
m_viewer.core().orthographic = true;
m_viewer.core().is_animating = true;
m_viewer.core().lighting_factor = 0.0;
m_viewer.core().animation_max_fps = 120.0;
// this->inital_scene = inital_scene;
m_viewer.callback_pre_draw = [&](igl::opengl::glfw::Viewer &) {
return pre_draw_loop();
};
m_viewer.callback_post_draw = [&](igl::opengl::glfw::Viewer &) {
return post_draw_loop();
};
m_viewer.callback_key_pressed = [&](igl::opengl::glfw::Viewer &,
unsigned int unicode_key,
int modifiers) {
return custom_key_pressed(unicode_key, modifiers);
};
m_viewer.launch(
/*resizable=*/true, /*fullscreen=*/false,
/*name=*/"静力学仿真");
}
// void load_scene();
void redraw_scene() {
ssim::Model model = sp_StaticSim_->get_mesh();
const Eigen::MatrixXd &V = model.V;
const Eigen::MatrixXi &F = model.F;
Eigen::MatrixXd Target = sp_StaticSim_->EvaluateTarget(display_target);
// plot
viewer->data().set_mesh(V, F);
if (!gui_ctrl_.mesh_visible) {
viewer->data().show_overlay = false;
viewer->data().show_lines = false;
}
if (!gui_ctrl_.single_color) {
// colormap
Eigen::MatrixXd C;
igl::jet(Target, true, C);
viewer->data().set_colors(C);
} else {
// single color
viewer->data().set_colors(Eigen::RowVector3d(1, 0, 0));
}
}
bool pre_draw_loop() {
redraw_scene();
return false;
}
bool post_draw_loop() {
return false;
}
private:
std::shared_ptr<ssim::StaticSim> sp_StaticSim_;
ssim::SimTargetOption::Target display_target;
GUICtrl gui_ctrl_;
};
} // ipc
} // rigid
#endif //RIGIDIPC_UISTATICSIMSTATE_H

16
static_sim/CMakeLists.txt

@ -0,0 +1,16 @@
# Static Simulation Project
add_library(StaticSim STATIC StaticSim.cpp)
# eigen
target_link_libraries(StaticSim PUBLIC Eigen3::Eigen)
# igl
include(libigl)
target_link_libraries(StaticSim PUBLIC
igl::core
igl::predicates
)
target_link_libraries(StaticSim PUBLIC spdlog::spdlog)
# IPC link static simulation
target_link_libraries(rigid_ipc_sim PUBLIC StaticSim)

46
static_sim/SimTargetOption.h

@ -0,0 +1,46 @@
//
// Created by cflin on 4/7/23.
//
#ifndef RIGIDIPC_SIMTARGETOPTION_H
#define RIGIDIPC_SIMTARGETOPTION_H
namespace ssim {
class SimTargetOption {
public:
static const int NUM_TARGETS = 10;
enum Target{
U_NORM = 0,S_NORM, S_X, S_Y, S_Z
};
SimTargetOption(int init_state=0) : state(init_state) {}
void set_option(int option) {
state |= (1 << option);
}
void unset_option(int option) {
state &= ~(1 << option);
}
bool is_option_set(int option) const {
return state & (1 << option);
}
void clear() {
state = 0;
}
void set() {
state = INT_MAX;
}
private:
int state;
};
} // ssim
#endif //RIGIDIPC_SIMTARGETOPTION_H

8
static_sim/StaticSim.cpp

@ -0,0 +1,8 @@
//
// Created by cflin on 4/7/23.
//
#include "StaticSim.h"
namespace ssim {
} // ssim

107
static_sim/StaticSim.h

@ -0,0 +1,107 @@
//
// Created by cflin on 4/7/23.
//
#ifndef RIGIDIPC_STATICSIM_H
#define RIGIDIPC_STATICSIM_H
#include <igl/readOBJ.h>
#include <Eigen/Dense>
#include <spdlog/spdlog.h>
#include "SimTargetOption.h"
namespace ssim {
struct Model {
Eigen::MatrixX3d V;
Eigen::MatrixX3i F;
Model() = default;
Model(const Eigen::MatrixX3d &V, const Eigen::MatrixX3i &F) : V(V), F(F) {}
int NumVertex() const{
return V.rows();
};
};
class StaticSim {
using MeshModel = Model;
using StlModel = Model;
public:
StaticSim(const SimTargetOption &option,const std::string & obj_path){
igl::readOBJ(obj_path,mesh_.V,mesh_.F);
// set option
option_=option;
map_target_to_evaluated_.resize(option_.NUM_TARGETS);
for (int i = 0; i < option_.NUM_TARGETS; ++i) {
if(option_.is_option_set(i))
MapAppendTarget(i);
}
}
StaticSim(const StlModel &stl, const SimTargetOption &option) : stl_(stl), option_(option) {
map_target_to_evaluated_.resize(option_.NUM_TARGETS);
// TODO: boundary condition && solve
for (int i = 0; i < option_.NUM_TARGETS; ++i) {
MapAppendTarget(i);
}
// ...
}
Eigen::MatrixXd EvaluateTarget(SimTargetOption::Target target) {
if (!option_.is_option_set(target)) {
// new target, update option_ and map_
MapAppendTarget(target);
option_.set_option(target);
}
return map_target_to_evaluated_[target];
}
void set_stl(const std::string &stl_path) {
// TODO read stl file
}
Model get_mesh() const {
return mesh_;
}
Model get_stl() const {
return stl_;
}
private:
MeshModel mesh_;
StlModel stl_;
SimTargetOption option_;
std::vector<Eigen::MatrixXd> map_target_to_evaluated_;
Eigen::MatrixXd EvaluateUNorm() const {
// TODO
return (Eigen::VectorXd::Random(mesh_.NumVertex()).array() + 1.0) * 50;
}
Eigen::MatrixXd EvaluateSNorm() const {
// TODO
return (Eigen::VectorXd::Random(mesh_.NumVertex()).array() + 1.0) * 50;
}
void MapAppendTarget(int target) {
switch (target) {
case SimTargetOption::U_NORM:
map_target_to_evaluated_[target] = EvaluateUNorm();
break;
case SimTargetOption::S_NORM:
map_target_to_evaluated_[target] = EvaluateSNorm();
break;
default:
spdlog::warn("Wrong target {:d} !(TODO)", target);
}
}
};
} // ssim
#endif //RIGIDIPC_STATICSIM_H
Loading…
Cancel
Save