Commit ca7443d7 authored by Yuwei Xiao's avatar Yuwei Xiao
Browse files

init

parents
#include "Gui.h"
#include <igl/Hit.h>
#include <igl/project.h>
#include <igl/writeOFF.h>
#include <iomanip>
#include <sstream>
void Gui::setSimulation(Simulation *sim) {
p_simulator = new Simulator(sim);
p_simulator->reset();
p_simulator->setSimulationSpeed(m_simSpeed);
}
void Gui::start() {
// message: http://patorjk.com/software/taag/#p=display&v=0&f=Roman&t=PBS%2019
std::string usage(
R"(
ooooooooo. oooooooooo. .oooooo..o
`888 `Y88. `888' `Y8b d8P' `Y8
888 .d88' 888 888 Y88bo.
888ooo88P' 888oooo888' `"Y8888o.
888 888 `88b `"Y88b
888 888 .88P oo .d8P
o888o o888bood8P' 8""88888P'
252-0546-00L Physically-Based Simulation in Computer Graphics @ ETH Zurich
Course Exercise Framework
Shortcuts:
[drag] Rotate scene | [space] Start/pause simulation
I,i Toggle invert normals | A,a Single step
L,l Toggle wireframe | R,r Reset simulation
T,t Toggle filled faces | C,c Clear screen
; Toggle vertex labels | : Toggle face labels
- Toggle fast forward | Z Snap to canonical view
. Turn up lighting | , Turn down lighting
O,o Toggle orthographic/perspective projection)");
std::cout << usage << std::endl;
// setting up viewer
m_viewer.data().show_lines = false;
m_viewer.data().point_size = 2.0f;
m_viewer.core().is_animating = true;
m_viewer.core().camera_zoom = 0.1;
m_viewer.core().object_scale = 1.0;
// setting up menu
igl::opengl::glfw::imgui::ImGuiMenu menu;
m_viewer.plugins.push_back(&menu);
menu.callback_draw_viewer_window = [&]() { drawMenuWindow(menu); };
showAxes(m_showAxes);
p_simulator->setNumRecords(m_numRecords);
p_simulator->setMaxSteps(m_maxSteps);
// callbacks
m_viewer.callback_key_pressed = [&](igl::opengl::glfw::Viewer &viewer,
unsigned int key, int modifiers) {
return keyCallback(viewer, key, modifiers);
};
m_viewer.callback_pre_draw = [&](igl::opengl::glfw::Viewer &viewer) {
return drawCallback(viewer);
};
m_viewer.callback_mouse_scroll = [&](igl::opengl::glfw::Viewer &viewer,
float delta_y) {
return scrollCallback(viewer, delta_y);
};
m_viewer.callback_mouse_down = [&](igl::opengl::glfw::Viewer &viewer,
int button, int modifier) {
return mouseCallback(viewer, menu, button, modifier);
};
// start viewer
m_viewer.launch();
}
void Gui::resetSimulation() {
p_simulator->reset();
m_timerAverage = 0.0;
}
#pragma region ArrowInterface
int Gui::addArrow(const Eigen::Vector3d &start, const Eigen::Vector3d &end,
const Eigen::Vector3d &color) {
m_arrows.push_back(Arrow(start, end, color));
m_arrows.back().id = m_numArrows++;
return m_arrows.back().id;
}
void Gui::removeArrow(size_t index) {
bool found = false;
for (size_t i = 0; i < m_arrows.size(); i++) {
if (m_arrows[i].id == index) {
found = true;
m_arrows.erase(m_arrows.begin() + i);
}
}
assert(found && "unable to find index");
}
void Gui::drawArrow(const Arrow &arrow) {
m_viewer.data_list[0].add_edges(arrow.start, arrow.end, arrow.color);
m_viewer.data_list[0].add_edges(arrow.end, arrow.head[0], arrow.color);
m_viewer.data_list[0].add_edges(arrow.end, arrow.head[1], arrow.color);
m_viewer.data_list[0].add_edges(arrow.end, arrow.head[2], arrow.color);
m_viewer.data_list[0].add_edges(arrow.end, arrow.head[3], arrow.color);
m_viewer.data_list[0].add_edges(arrow.head[0], arrow.head[2], arrow.color);
m_viewer.data_list[0].add_edges(arrow.head[1], arrow.head[2], arrow.color);
m_viewer.data_list[0].add_edges(arrow.head[1], arrow.head[3], arrow.color);
m_viewer.data_list[0].add_edges(arrow.head[3], arrow.head[0], arrow.color);
m_viewer.data_list[0].add_edges(arrow.head[3], arrow.head[2], arrow.color);
m_viewer.data_list[0].add_edges(arrow.head[1], arrow.head[0], arrow.color);
}
#pragma endregion ArrowInterface
void Gui::drawReferencePlane() {
m_viewer.data_list[0].add_edges(m_referencePlane.start, m_referencePlane.end, m_referencePlane.color);
}
void Gui::showVertexArrow() {
if (m_clickedArrow >= 0) {
removeArrow(m_clickedArrow);
m_clickedArrow = -1;
}
if (m_clickedVertex >= 0) {
Eigen::Vector3d pos;
Eigen::Vector3d norm;
if (callback_clicked_vertex) {
callback_clicked_vertex(m_clickedVertex, m_clickedObject, pos,
norm);
}
else {
pos = m_viewer.data_list[m_clickedObject].V.row(m_clickedVertex);
norm = m_viewer.data_list[m_clickedObject].V_normals.row(
m_clickedVertex);
}
m_clickedArrow =
addArrow(pos, pos + norm, Eigen::RowVector3d(1.0, 0, 0));
}
}
bool Gui::drawCallback(igl::opengl::glfw::Viewer &viewer) {
if (m_request_clear) {
for (auto &d : viewer.data_list) {
d.clear();
}
m_request_clear = false;
m_clickedVertex = -1;
if (m_clickedArrow >= 0) {
removeArrow(m_clickedArrow);
m_clickedArrow = -1;
}
}
viewer.data_list[0].clear();
if (m_arrows.size() > 0 || m_showReferencePlane) {
for (size_t i = 0; i < m_arrows.size(); i++) {
drawArrow(m_arrows[i]);
}
if (m_showReferencePlane) drawReferencePlane();
}
p_simulator->render(viewer);
return false;
}
bool Gui::scrollCallback(igl::opengl::glfw::Viewer &viewer, float delta_y) {
double factor = 1.5;
if (delta_y > 0)
viewer.core().camera_zoom *= factor;
else
viewer.core().camera_zoom /= factor;
return true;
}
void Gui::toggleSimulation() {
if (p_simulator->isPaused()) {
if (!p_simulator->hasStarted()) {
updateSimulationParameters();
}
p_simulator->run();
}
else {
p_simulator->pause();
}
}
void Gui::singleStep() {
if (!p_simulator->hasStarted()) {
updateSimulationParameters();
}
p_simulator->run(true);
}
void Gui::clearScreen() {
m_request_clear = true;
clearSimulation();
}
bool Gui::keyCallback(igl::opengl::glfw::Viewer &viewer, unsigned int key,
int modifiers) {
switch (key) {
case 'I':
case 'i':
for (auto &d : viewer.data_list) {
d.dirty |= igl::opengl::MeshGL::DIRTY_NORMAL;
d.invert_normals = !d.invert_normals;
}
return true;
case 'L':
case 'l':
for (auto &d : viewer.data_list) {
d.show_lines = !d.show_lines;
}
return true;
case 'T':
case 't':
for (auto &d : viewer.data_list) {
d.show_faces = !d.show_faces;
}
return true;
case ';':
for (auto &d : viewer.data_list) {
d.show_vertid = !d.show_vertid;
}
return true;
case ':':
for (auto &d : viewer.data_list) {
d.show_faceid = !d.show_faceid;
}
return true;
case ' ':
toggleSimulation();
return true;
case 'r':
case 'R':
resetSimulation();
return true;
case 'a':
case 'A':
singleStep();
case 'c':
case 'C':
clearScreen();
return true;
case '-':
if (!m_fastForward) {
p_simulator->setSimulationSpeed(240);
}
else {
p_simulator->setSimulationSpeed(m_simSpeed);
}
m_fastForward = !m_fastForward;
return true;
case '.':
viewer.core().lighting_factor += 0.1;
break;
case ',':
viewer.core().lighting_factor -= 0.1;
break;
default:
return childKeyCallback(viewer, key, modifiers);
}
viewer.core().lighting_factor =
std::min(std::max(viewer.core().lighting_factor, 0.f), 1.f);
return false;
}
bool Gui::mouseCallback(igl::opengl::glfw::Viewer &viewer,
igl::opengl::glfw::imgui::ImGuiMenu &menu, int button,
int modifier) {
// get vertices, project them onto screen and find closest vertex to mouse
float minDist = std::numeric_limits<float>::infinity();
int vertex = -1;
int object = -1;
for (size_t i = 0; i < viewer.data_list.size(); i++) {
Eigen::MatrixXf Vf = viewer.data_list[i].V.cast<float>();
Eigen::MatrixXf projections;
igl::project(Vf, viewer.core().view, viewer.core().proj,
viewer.core().viewport, projections);
Eigen::VectorXf x = projections.col(0).array() - viewer.current_mouse_x;
Eigen::VectorXf y = -projections.col(1).array() +
viewer.core().viewport(3) - viewer.current_mouse_y;
Eigen::VectorXf distances =
(x.array().square() + y.array().square()).matrix().cwiseSqrt();
int vi = -1;
float dist = distances.minCoeff(&vi);
if (dist < minDist) {
minDist = dist;
vertex = vi;
object = i;
}
}
if (minDist < 20) {
// only select vertex if user clicked "close"
m_clickedVertex = vertex;
m_clickedObject = object;
showVertexArrow();
}
return false;
}
void Gui::drawMenuWindow(igl::opengl::glfw::imgui::ImGuiMenu &menu) {
glfwSetWindowTitle(m_viewer.window, "PBS Exercises");
float menu_width = 220.f * menu.menu_scaling();
// Controls
ImGui::SetNextWindowPos(ImVec2(0.0f, 0.0f), ImGuiSetCond_FirstUseEver);
ImGui::SetNextWindowSize(ImVec2(0.0f, 0.0f), ImGuiSetCond_FirstUseEver);
ImGui::SetNextWindowSizeConstraints(ImVec2(menu_width, -1.0f),
ImVec2(menu_width, -1.0f));
bool _viewer_menu_visible = true;
ImGui::Begin(
"Viewer", &_viewer_menu_visible,
ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_AlwaysAutoResize);
ImGui::PushItemWidth(ImGui::GetWindowWidth() * 0.5f);
drawMenu(m_viewer, menu);
ImGui::PopItemWidth();
ImGui::End();
// Clicking
if (m_clickedVertex >= 0) {
ImGui::SetNextWindowPos(ImVec2(0, 0), ImGuiSetCond_FirstUseEver);
ImGui::SetNextWindowSize(ImGui::GetIO().DisplaySize,
ImGuiSetCond_FirstUseEver);
bool visible = true;
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(0, 0, 0, 0));
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0);
ImGui::Begin(
"ViewerLabels", &visible,
ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize |
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoScrollbar |
ImGuiWindowFlags_NoScrollWithMouse |
ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoSavedSettings |
ImGuiWindowFlags_NoInputs);
Eigen::Vector3d pos =
m_viewer.data_list[m_clickedObject].V.row(m_clickedVertex);
Eigen::Vector3d norm =
m_viewer.data_list[m_clickedObject].V_normals.row(m_clickedVertex);
std::string text = "(" + std::to_string(pos(0)) + ", " +
std::to_string(pos(1)) + ", " +
std::to_string(pos(2)) + ")";
ImDrawList *drawList = ImGui::GetWindowDrawList();
Eigen::Vector3f c0 = igl::project(
Eigen::Vector3f((pos + 0.1 * norm).cast<float>()),
m_viewer.core().view, m_viewer.core().proj, m_viewer.core().viewport);
drawList->AddText(ImGui::GetFont(), ImGui::GetFontSize() * 1.2,
ImVec2(c0(0), m_viewer.core().viewport[3] - c0(1)),
ImGui::GetColorU32(ImVec4(0, 0, 10, 255)), &text[0],
&text[0] + text.size());
ImGui::End();
ImGui::PopStyleColor();
ImGui::PopStyleVar();
showVertexArrow();
}
// Stats
if (m_showStats) {
int width, height;
glfwGetWindowSize(m_viewer.window, &width, &height);
ImGui::SetNextWindowPos(ImVec2(width - menu_width, 0.0f),
ImGuiSetCond_FirstUseEver);
ImGui::SetNextWindowSize(ImVec2(0.0f, 0.0f), ImGuiSetCond_FirstUseEver);
ImGui::SetNextWindowSizeConstraints(ImVec2(menu_width, -1.0f),
ImVec2(menu_width, -1.0f));
ImGui::Begin("Stats", &_viewer_menu_visible,
ImGuiWindowFlags_NoSavedSettings |
ImGuiWindowFlags_AlwaysAutoResize);
ImGui::PushItemWidth(ImGui::GetWindowWidth() * 0.5f);
if (!p_simulator->isPaused()) {
if (m_timerAverage > 0.0) {
double alpha = 0.95;
m_timerAverage = m_timerAverage * alpha +
(1.0 - alpha) * p_simulator->getDuration();
}
else {
m_timerAverage = p_simulator->getDuration();
}
}
ImGui::Text("Iteration: %ld", p_simulator->getSimulationStep());
ImGui::Text("Average time per iteration: %.2fms", m_timerAverage);
ImGui::Text("Current time: %.5f", p_simulator->getSimulationTime());
drawSimulationStats();
ImGui::PopItemWidth();
ImGui::End();
}
}
inline std::string getFilename(int total_numObj, int obj, int total_steps,
int step) {
std::stringstream ss;
ss << "_object" << std::setw(std::log10(total_numObj)) << std::setfill('0')
<< obj << "_" << std::setw(std::log10(total_steps)) << step << ".obj";
return ss.str();
}
void Gui::exportRecording() {
std::string path = igl::file_dialog_save();
size_t finddot = path.find_last_of(".");
path = path.substr(0, finddot);
std::cout << "Exporting Recording to " << path << "_objectxxx_xxx.obj"
<< std::endl;
auto rec = p_simulator->getRecords();
int steps = rec[0].size();
for (size_t i = 0; i < rec.size(); i++) {
int j = 0;
while (rec[i].size() > 0) {
std::string filename =
path + getFilename(rec.size(), i, steps, j++);
auto p = rec[i].front();
rec[i].pop();
bool succ = igl::writeOBJ(filename, p.first, p.second);
if (!succ) {
std::cerr << "Failed to write recording" << std::endl;
}
}
}
}
bool Gui::drawMenu(igl::opengl::glfw::Viewer &viewer,
igl::opengl::glfw::imgui::ImGuiMenu &menu) {
if (ImGui::CollapsingHeader("Simulation Control",
ImGuiTreeNodeFlags_DefaultOpen)) {
if (ImGui::Button(
p_simulator->isPaused() ? "Run Simulation" : "Pause Simulation",
ImVec2(-1, 0))) {
toggleSimulation();
}
if (ImGui::Button("Single Step", ImVec2(-1, 0))) {
singleStep();
}
if (ImGui::Button("Reset Simulation", ImVec2(-1, 0))) {
resetSimulation();
}
if (ImGui::Button("Clear Screen", ImVec2(-1, 0))) {
clearScreen();
}
if (ImGui::SliderInt("Steps/Second", &m_simSpeed, 1, 240)) {
p_simulator->setSimulationSpeed(m_simSpeed);
}
if (ImGui::InputInt("Max Steps", &m_maxSteps, -1, -1)) {
p_simulator->setMaxSteps(m_maxSteps);
}
}
if (ImGui::CollapsingHeader("Overlays", ImGuiTreeNodeFlags_DefaultOpen)) {
if (ImGui::CheckboxFlags("Wireframe", &(viewer.data().show_lines), 1)) {
for (size_t i = 0; i < viewer.data_list.size(); i++) {
viewer.data_list[i].show_lines = viewer.data().show_lines;
}
}
if (ImGui::CheckboxFlags("Fill", &(viewer.data().show_faces), 1)) {
for (size_t i = 0; i < viewer.data_list.size(); i++) {
viewer.data_list[i].show_faces = viewer.data().show_faces;
}
}
if (ImGui::Checkbox("Show vertex labels",
&(viewer.data().show_vertid))) {
for (size_t i = 0; i < viewer.data_list.size(); i++) {
viewer.data_list[i].show_vertid = viewer.data().show_vertid;
}
}
if (ImGui::Checkbox("Show faces labels",
&(viewer.data().show_faceid))) {
for (size_t i = 0; i < viewer.data_list.size(); i++) {
viewer.data_list[i].show_faceid = viewer.data().show_faceid;
}
}
ImGui::Checkbox("Show stats", &m_showStats);
if (ImGui::Checkbox("Show axes", &m_showAxes)) {
showAxes(m_showAxes);
}
if (ImGui::Checkbox("Show reference plane", &m_showReferencePlane)) {
;
}
}
if (ImGui::CollapsingHeader("Simulation Parameters",
ImGuiTreeNodeFlags_DefaultOpen)) {
drawSimulationParameterMenu();
}
if (ImGui::CollapsingHeader("Recording")) {
bool hasRecords = p_simulator->getRecords().size() > 0 &&
p_simulator->getRecords()[0].size() > 0;
if (!hasRecords) {
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0));
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0, 0, 0, 0));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0, 0, 0, 0));
}
if (ImGui::Button("Export Recording", ImVec2(-1, 0))) {
if (hasRecords) {
exportRecording();
}
}
if (!hasRecords) {
ImGui::PopStyleColor();
ImGui::PopStyleColor();
ImGui::PopStyleColor();
}
if (ImGui::InputInt("Steps in Recording to keep", &m_numRecords, 0,
0)) {
p_simulator->setNumRecords(m_numRecords);
}
bool isRecording = p_simulator->isRecording();
ImGui::PushStyleColor(ImGuiCol_Button,
isRecording ? ImVec4(0.98f, 0.26f, 0.26f, 0.40f)
: ImVec4(0.26f, 0.98f, 0.40f, 0.40f));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered,
isRecording ? ImVec4(0.98f, 0.26f, 0.26f, 1.0f)
: ImVec4(0.26f, 0.98f, 0.40f, 1.0f));
ImGui::PushStyleColor(ImGuiCol_ButtonActive,
isRecording ? ImVec4(0.98f, 0.26f, 0.00f, 0.9f)
: ImVec4(0.00f, 0.98f, 0.40f, 0.9f));
if (ImGui::Button(isRecording ? "Stop Recording" : "Start Recording",
ImVec2(-1, 0))) {
p_simulator->setRecording(!isRecording);
}
ImGui::PopStyleColor();
ImGui::PopStyleColor();
ImGui::PopStyleColor();
}
return false;
}
void Gui::showAxes(bool show_axes) {
if (show_axes && m_axesID < 0) {
Eigen::RowVector3d origin = Eigen::Vector3d::Zero();
m_axesID = addArrow(origin, Eigen::Vector3d(1, 0, 0),
Eigen::Vector3d(1, 0, 0));
addArrow(origin, Eigen::Vector3d(0, 1, 0), Eigen::Vector3d(0, 1, 0));
addArrow(origin, Eigen::Vector3d(0, 0, 1), Eigen::Vector3d(0, 0, 1));
}
if (!show_axes && m_axesID >= 0) {
removeArrow(m_axesID);
removeArrow(m_axesID + 1);
removeArrow(m_axesID + 2);
m_axesID = -1;
}
}
\ No newline at end of file
#ifndef GUI_H
#define GUI_H
#include <igl/opengl/glfw/Viewer.h>
#include <igl/opengl/glfw/imgui/ImGuiMenu.h>
#include "Arrow.h"
#include "ReferencePlane.h"
#include "Simulator.h"
/*
* Base class to open a window with a simple GUI for simulation. Inherit from
* this class to perform a simulation and customize the menu.
*/
class Gui {
public:
Gui() {}
~Gui() {}
/*
* Set simulation to be performed in the simulator.
*/
void setSimulation(Simulation *sim);
/*
* Initialize all the necessary data structures and callbacks and open the
* window.
*/
void start();
/*
* Call the setters for your simulation in this method to update the
* parameters entered in the GUI. This method is called before the
* simulation is started for the first time.
*/
virtual void updateSimulationParameters() = 0;
/*
* Clear some custom datastructures/visualizations when the user requests
* it.
*/
virtual void clearSimulation() {}
/*
* Callback to enable custom shortcuts.
*/
virtual bool childKeyCallback(igl::opengl::glfw::Viewer &viewer,
unsigned int key, int modifiers) {
return false;
}
/*
* Setup your own (additional) ImGUI components in this method.
*/
virtual void drawSimulationParameterMenu() {}
/*
* Setup your own (additional) ImGUI debugging output.
*/
virtual void drawSimulationStats() {}
#pragma region ArrowInterface
/*
* Create and add an arrow to be displayed in the GUI. Returns the index of
* the drawn arrow (keep it if you want to delete this arrow later).
*/
int addArrow(const Eigen::Vector3d &start, const Eigen::Vector3d &end,
const Eigen::Vector3d &color=Eigen::RowVector3d(1, 0, 0));
/*
* Delete arrow at given index.
*/
void removeArrow(size_t index);
#pragma endregion ArrowInterface
/*
* Show the standard basis axis (x, y, z)
*/
void showAxes(bool show_axes);
/*
* Callback to show a different vector for a clicked vertex than the normal
*/
std::function<void(int clickedVertexIndex, int clickedObjectIndex,
Eigen::Vector3d &pos, Eigen::Vector3d &dir)>
callback_clicked_vertex = nullptr;
void turnOffLight() { m_viewer.core().lighting_factor = 0; }
protected:
void drawArrow(const Arrow &arrow);
void drawReferencePlane();
void showVertexArrow();
void toggleSimulation();
void singleStep();
void resetSimulation();
void exportRecording();
void clearScreen();
bool keyCallback(igl::opengl::glfw::Viewer &viewer, unsigned int key,
int modifiers);
bool keyReleasedCallback(igl::opengl::glfw::Viewer &viewer,
unsigned int key, int modifiers);
void drawMenuWindow(igl::opengl::glfw::imgui::ImGuiMenu &menu);
bool drawMenu(igl::opengl::glfw::Viewer &viewer,
igl::opengl::glfw::imgui::ImGuiMenu &menu);
bool drawCallback(igl::opengl::glfw::Viewer &viewer);
bool scrollCallback(igl::opengl::glfw::Viewer &viewer, float delta_y);
bool mouseCallback(igl::opengl::glfw::Viewer &viewer,
igl::opengl::glfw::imgui::ImGuiMenu &menu, int button,
int modifier);
igl::opengl::glfw::Viewer m_viewer;
Simulator *p_simulator = NULL;
bool m_request_clear = false;
int m_simSpeed = 60;
bool m_fastForward = false;
int m_clickedVertex = -1; // index of clicked vertex
int m_clickedObject = -1; // id of clicked object
int m_clickedArrow = -1; // index of arrow of clicked vertex
std::vector<Arrow> m_arrows; // data structure to store all the arrows
// to be rendered
unsigned long m_numArrows = 0; // increasing counter for arrows
int m_axesID = -1; // (lowest) id of the 3 base axes
bool m_showAxes = true;
ReferencePlane m_referencePlane;
bool m_showReferencePlane = true;
bool m_showStats = true;
double m_timerAverage = 0; // running average of execution time of
// one iteration of the simulation
int m_maxSteps = -1;
int m_numRecords = 100; // number of records to keep
};
#endif
\ No newline at end of file
#ifndef MACGRID2_H
#define MACGRID2_H
#include <igl/colormap.h>
#include <Eigen/Core>
#include "Array2T.h"
class MACGrid2 {
public:
MACGrid2(int res_x, int res_y, double dx) {
m_res_x = res_x;
m_res_y = res_y;
m_dx = dx;
m_x = Array2d(res_x + 1, res_y);
m_y = Array2d(res_x, res_y + 1);
buildGrid();
}
Array2d& x() { return m_x; }
Array2d& y() { return m_y; }
const Eigen::MatrixXd& s() { return m_start; }
const Eigen::MatrixXd& e() { return m_end; }
const Eigen::MatrixXd& vs() { return m_vs; }
const Eigen::MatrixXd& ve() { return m_ve; }
const Eigen::MatrixXd& vc() { return m_vc; }
void buildGrid() {
int num_edges = 2 * m_res_x * m_res_y + m_res_x + m_res_y;
m_start = Eigen::MatrixXd(num_edges, 3);
m_end = Eigen::MatrixXd(num_edges, 3);
int i = 0;
for (int y = 0; y <= m_res_y; ++y)
for (int x = 0; x <= m_res_x; ++x) {
if (x < m_res_x) {
m_start.row(i) = Eigen::RowVector3d(x, y, 0) * m_dx;
m_end.row(i++) = Eigen::RowVector3d(x + 1, y, 0) * m_dx;
}
if (y < m_res_y) {
m_start.row(i) = Eigen::RowVector3d(x, y, 0) * m_dx;
m_end.row(i++) = Eigen::RowVector3d(x, y + 1, 0) * m_dx;
}
}
}
void updateEdges(double scale = 1) {
int num_edges = (m_res_x + 1) * m_res_y + m_res_x * (m_res_y + 1) + m_res_x * m_res_y;
if (m_vs.rows() == 0) {
m_vs = Eigen::MatrixXd(num_edges, 3);
m_ve = Eigen::MatrixXd(num_edges, 3);
m_vc = Eigen::MatrixXd(num_edges, 3);
}
int i = 0;
for (int y = 0; y <= m_res_y; ++y)
for (int x = 0; x <= m_res_x; ++x) {
if (y < m_res_y) {
m_vc.row(i) = Eigen::RowVector3d(1, 0, 0);
m_vs.row(i) = Eigen::RowVector3d(x, y + 0.5, 0) * m_dx;
m_ve.row(i++) = Eigen::RowVector3d(x + m_x(x, y) * scale, y + 0.5, 0) * m_dx;
}
if (x < m_res_x) {
m_vc.row(i) = Eigen::RowVector3d(0, 0, 1);
m_vs.row(i) = Eigen::RowVector3d(x + 0.5, y, 0) * m_dx;
m_ve.row(i++) = Eigen::RowVector3d(x + 0.5, y + m_y(x, y) * scale, 0) * m_dx;
}
if (y < m_res_y && x < m_res_x) {
double vx = (m_x(x, y) + m_x(x + 1, y)) * 0.5;
double vy = (m_y(x, y) + m_y(x, y + 1)) * 0.5;
m_vc.row(i) = Eigen::RowVector3d(0, 1, 0);
m_vs.row(i) = Eigen::RowVector3d(x + 0.5, y + 0.5, 0) * m_dx;
m_ve.row(i++) = Eigen::RowVector3d(x + 0.5 + vx * scale, y + 0.5 + vy * scale, 0) * m_dx;
}
}
}
void reset() {
m_x.zero();
m_y.zero();
}
protected:
int m_res_x, m_res_y;
double m_dx;
Array2d m_x;
Array2d m_y;
Eigen::MatrixXd m_start;
Eigen::MatrixXd m_end;
Eigen::MatrixXd m_vs;
Eigen::MatrixXd m_ve;
Eigen::MatrixXd m_vc;
};
#endif // MACGRID2_H
\ No newline at end of file
#include <Eigen/Core>
#include <vector>
class ReferencePlane {
public:
ReferencePlane() : size(10), color(Eigen::RowVector3d(0.25, 0.25, 0.25)) {
//// for set_edges
//int num_edges = 2 * (2 * size)*(2 * size) + (2 * size) * 2;
//int num_points = (2 * size + 1)*(2 * size + 1);
//points = Eigen::MatrixXd(num_points, 3);
//edges = Eigen::MatrixXi(num_edges, 2);
//int i = 0;
//for (int z = -size; z <= size; ++z) {
// for (int x = -size; x <= size; ++x) {
// points.row(i++) = Eigen::RowVector3d(x, -5, z);
// }
//}
//i = 0;
//for (int z = 0; z <= 2*size; ++z) {
// for (int x = 0; x <= 2*size; ++x) {
// int p1 = z * (2 * size + 1) + x;
// int p2 = p1 + 1;
// int p3 = p1 + (2 * size + 1);
// if (x < 2*size)
// edges.row(i++) = Eigen::RowVector2i(p1, p2);
// if (z < 2 * size)
// edges.row(i++) = Eigen::RowVector2i(p1, p3);
// }
//}
int num_edges = 2 * (2 * size)*(2 * size) + (2 * size) * 2;
start = Eigen::MatrixXd(num_edges, 3);
end = Eigen::MatrixXd(num_edges, 3);
int e = 0;
for (int z = -size; z <= size; ++z)
for (int x = -size; x <= size; ++x) {
if (x < size) {
start.row(e) = Eigen::RowVector3d(x, 0, z);
end.row(e++) = Eigen::RowVector3d(x + 1, 0, z);
}
if (z < size) {
start.row(e) = Eigen::RowVector3d(x, 0, z);
end.row(e++) = Eigen::RowVector3d(x, 0, z + 1);
}
}
}
//Eigen::MatrixXd points;
//Eigen::MatrixXi edges;
int size;
Eigen::MatrixXd start;
Eigen::MatrixXd end;
Eigen::RowVector3d color;
};
\ No newline at end of file
#include "RigidObject.h"
RigidObject::RigidObject(const std::string& mesh_file, const ObjType t) {
findAndLoadMesh(mesh_file);
setType(t);
setMass(1.0);
// inertia of cube
setInertia(getMass() * 2.0 / 6.0 * Eigen::Matrix3d::Identity());
reset();
}
void RigidObject::resetMembers() {
setLinearMomentum(Eigen::Vector3d::Zero());
setAngularMomentum(Eigen::Vector3d::Zero());
resetForce();
resetTorque();
}
void RigidObject::applyForceToCOM(const Eigen::Vector3d& f) {
if (m_type != ObjType::DYNAMIC) return;
m_force += f;
}
void RigidObject::applyForce(const Eigen::Vector3d& f,
const Eigen::Vector3d& p) {
if (m_type != ObjType::DYNAMIC) return;
m_force += f;
m_torque += (p - m_position).cross(f);
}
void RigidObject::applyTorque(const Eigen::Vector3d& t) {
if (m_type != ObjType::DYNAMIC) return;
m_torque += t;
}
void RigidObject::printDebug(const std::string& message) const {
std::cout << std::endl;
if (message.size() > 0) {
std::cout << message << std::endl;
}
std::cout << "position: " << m_position.transpose() << std::endl;
std::cout << "rotation: " << std::endl << m_rot << std::endl;
std::cout << "linear velocity: " << m_v.transpose() << std::endl;
std::cout << "angular velocity: " << m_w.transpose() << std::endl;
std::cout << "force: " << m_force.transpose() << std::endl;
std::cout << "torque: " << m_torque.transpose() << std::endl;
std::cout << "mass (inv): " << m_mass << " (" << m_massInv << ")"
<< std::endl;
}
#pragma region GettersAndSetters
void RigidObject::setType(ObjType t) {
m_type = t;
if (m_type == ObjType::STATIC) {
m_mass = std::numeric_limits<double>::infinity();
m_massInv = 0.0;
m_inertia.setZero();
m_inertiaInv.setZero();
m_force.setZero();
m_torque.setZero();
}
}
void RigidObject::setMass(double m) {
if (m_type != ObjType::DYNAMIC) return;
m_mass = m;
m_massInv = 1.0 / m_mass;
}
void RigidObject::setInertia(const Eigen::Matrix3d& I) {
if (m_type != ObjType::DYNAMIC) return;
m_inertia = I;
m_inertiaInv = m_inertia.inverse();
}
void RigidObject::setLinearMomentum(const Eigen::Vector3d& p) {
if (m_type != ObjType::DYNAMIC) return;
m_v = m_massInv * p;
}
void RigidObject::setAngularMomentum(const Eigen::Vector3d& l) {
if (m_type != ObjType::DYNAMIC) return;
m_w = getInertiaInvWorld() * l;
}
void RigidObject::setLinearVelocity(const Eigen::Vector3d& v) {
if (m_type != ObjType::DYNAMIC) return;
m_v = v;
}
void RigidObject::setAngularVelocity(const Eigen::Vector3d& w) {
if (m_type != ObjType::DYNAMIC) return;
m_w = w;
}
void RigidObject::setForce(const Eigen::Vector3d& f) {
if (m_type != ObjType::DYNAMIC) return;
m_force = f;
}
void RigidObject::setTorque(const Eigen::Vector3d& t) {
if (m_type != ObjType::DYNAMIC) return;
m_torque = t;
}
void RigidObject::resetForce() { m_force.setZero(); };
void RigidObject::resetTorque() { m_torque.setZero(); };
double RigidObject::getMass() const { return m_mass; }
double RigidObject::getMassInv() const { return m_massInv; }
Eigen::Matrix3d RigidObject::getInertia() const { return m_inertia; }
Eigen::Matrix3d RigidObject::getInertiaInv() const { return m_inertiaInv; }
Eigen::Matrix3d RigidObject::getInertiaInvWorld() const {
return m_quat * m_inertiaInv * m_quat.inverse();
}
Eigen::Matrix3d RigidObject::getInertiaWorld() const {
return m_quat * m_inertia * m_quat.inverse();
}
Eigen::Vector3d RigidObject::getLinearMomentum() const { return m_v * m_mass; }
Eigen::Vector3d RigidObject::getAngularMomentum() const {
return getInertiaWorld() * m_w;
}
Eigen::Vector3d RigidObject::getLinearVelocity() const { return m_v; }
Eigen::Vector3d RigidObject::getVelocity(const Eigen::Vector3d& point) const {
return getLinearVelocity() +
getAngularVelocity().cross(point - getPosition());
}
Eigen::Vector3d RigidObject::getAngularVelocity() const { return m_w; }
Eigen::Vector3d RigidObject::getForce() const { return m_force; }
Eigen::Vector3d RigidObject::getTorque() const { return m_torque; }
#pragma endregion GettersAndSetters
\ No newline at end of file
#ifndef RIGIDOBJECT_H
#define RIGIDOBJECT_H
#include "BaseObject.h"
/*
* Base class representing a simple rigid object.
*/
class RigidObject : public BaseObject {
public:
RigidObject() {}
RigidObject(const std::string& mesh_path,
const ObjType t = ObjType::DYNAMIC);
virtual ~RigidObject() {}
void applyForceToCOM(const Eigen::Vector3d& f);
/*
* Apply force f at point p.
*/
void applyForce(const Eigen::Vector3d& f, const Eigen::Vector3d& p);
void applyTorque(const Eigen::Vector3d& t);
void printDebug(const std::string& message = "") const;
#pragma region GettersAndSetters
virtual void setType(ObjType t) override;
void setMass(double m);
void setInertia(const Eigen::Matrix3d& I);
void setLinearMomentum(const Eigen::Vector3d& p);
void setAngularMomentum(const Eigen::Vector3d& l);
void setLinearVelocity(const Eigen::Vector3d& v);
void setAngularVelocity(const Eigen::Vector3d& w);
void setForce(const Eigen::Vector3d& f);
void setTorque(const Eigen::Vector3d& t);
void resetForce();
void resetTorque();
double getMass() const;
double getMassInv() const;
Eigen::Matrix3d getInertia() const;
Eigen::Matrix3d getInertiaInv() const;
Eigen::Matrix3d getInertiaInvWorld() const;
Eigen::Matrix3d getInertiaWorld() const;
Eigen::Vector3d getLinearMomentum() const;
Eigen::Vector3d getAngularMomentum() const;
Eigen::Vector3d getLinearVelocity() const;
Eigen::Vector3d getVelocity(const Eigen::Vector3d& point) const;
Eigen::Vector3d getAngularVelocity() const;
Eigen::Vector3d getForce() const;
Eigen::Vector3d getTorque() const;
#pragma endregion GettersAndSetters
protected:
virtual void resetMembers() override;
private:
double m_mass; // Body mass
double m_massInv; // Inverted mass
Eigen::Matrix3d m_inertia; // Intertial Tensor (initially set to cube)
Eigen::Matrix3d m_inertiaInv; // Inverse
Eigen::Vector3d m_v; // Linear velocity
Eigen::Vector3d m_w; // Angular velocity
Eigen::Vector3d m_force; // Force on body
Eigen::Vector3d m_torque; // Torque on body
};
#endif
\ No newline at end of file
#ifndef SIMULATION_H
#define SIMULATION_H
#include <igl/opengl/glfw/Viewer.h>
#include "RigidObject.h"
/*
* Base class for different kinds of simulation. Provides methods for
* controlling the simulation and rendering it in a Gui.
*/
class Simulation {
public:
Simulation() {}
virtual ~Simulation() {}
/*
* Initialize the necessary class variables at the beginning of the
* simulation.
*/
virtual void init() {}
/*
* Reset class variables to reset the simulation.
*/
void reset() {
m_time = 0.0;
m_step = 0;
resetMembers();
}
/*
* Update the rendering data structures. This method will be called in
* alternation with advance(). This method blocks rendering in the
* viewer, so do *not* do extensive computation here (leave it to
* advance()).
*/
virtual void updateRenderGeometry() = 0;
/*
* Performs one simulation step of length m_dt. This method *must* be
* thread-safe with respect to renderRenderGeometry() (easiest is to not
* touch any rendering data structures at all). You have to update the time
* variables at the end of each step if they are necessary for your
* simulation.
*/
virtual bool advance() = 0;
/*
* Perform any actual rendering here. This method *must* be thread-safe with
* respect to advance(). This method runs in the same thread as the
* viewer and blocks user IO, so there really should not be any extensive
* computation here or the UI will lag/become unresponsive (the whole reason
* the simulation itself is in its own thread.)
*/
virtual void renderRenderGeometry(igl::opengl::glfw::Viewer &viewer) = 0;
void setTimestep(double t) { m_dt = t; }
double getTime() const { return m_time; }
unsigned long getStep() const { return m_step; }
std::vector<RigidObject> &getObjects() { return m_objects; }
protected:
/*
* Reset class variables specific to a certain simulation. Is called by
* Simulation::reset().
*/
virtual void resetMembers() = 0;
std::vector<RigidObject>
m_objects; // vector of all objects in the simulation
double m_dt = 0.0; // length of timestep
double m_time = 0.0; // current time
unsigned long m_step = 0; // number of performed steps in simulation
};
#endif
\ No newline at end of file
#ifndef SIMULATOR_H
#define SIMULATOR_H
#include "Simulation.h"
#include <igl/opengl/glfw/Viewer.h>
#include <igl/opengl/glfw/imgui/ImGuiMenu.h>
#include <mutex>
#include <queue>
#include <thread>
using namespace std::chrono;
/*
* Class for running a simulation (see Simulation.h) on a separate thread.
* Performs stepping through simulation (including pausing/resuming/killing) and
* updating the rendering in the viewer.
*/
class Simulator {
public:
Simulator(Simulation *sim)
: p_simulator_thread(NULL),
p_simulation(sim),
m_please_pause(false),
m_please_die(false),
m_running(false),
m_started(false),
m_single_iteration(false),
m_recording(false) {}
virtual ~Simulator() {
killSimulatorThread();
delete p_simulation;
p_simulation = NULL;
}
/*
* Runs the simulation, if it has been paused (or never started).
*/
void run(bool single = false) {
m_status_mutex.lock();
if (!m_started) {
p_simulation->reset();
m_started = true;
if (single) {
std::cout << "Single step" << std::endl;
}
else {
std::cout << "Start simulation" << std::endl;
}
}
else if (m_please_pause) {
if (single) {
std::cout << "Single step" << std::endl;
}
else {
std::cout << "Resume simulation" << std::endl;
}
}
if (m_please_pause) {
m_single_iteration = single;
}
if (!single) {
m_please_pause = false;
}
m_status_mutex.unlock();
}
/*
* Resets the p_simulation (and leaves it in a paused state; call run() to
* start it).
*/
void reset() {
killSimulatorThread();
if (m_started) {
std::cout << "Reset simulation" << std::endl;
}
m_please_die = m_running = m_started = false;
m_please_pause = true;
clearRecords();
setRecording(m_recording);
p_simulation->reset();
p_simulation->updateRenderGeometry();
p_simulator_thread = new std::thread(&Simulator::runSimThread, this);
}
/*
* Pause a m_running p_simulation. The p_simulation will pause at the end of
* its current "step"; this method will not interrupt simulateOneStep
* mid-processing.
*/
void pause() {
m_status_mutex.lock();
m_please_pause = true;
m_status_mutex.unlock();
std::cout << "Pause simulation" << std::endl;
}
bool isPaused() {
bool ret = false;
m_status_mutex.lock();
if (m_running && m_please_pause) ret = true;
m_status_mutex.unlock();
return ret;
}
bool hasStarted() const { return m_started; }
void render(igl::opengl::glfw::Viewer &viewer) {
m_render_mutex.lock();
p_simulation->renderRenderGeometry(viewer);
m_render_mutex.unlock();
}
double getDuration() const {
return duration_cast<microseconds>(m_duration).count() * 0.001;
}
double getSimulationTime() const { return p_simulation->getTime(); }
unsigned long getSimulationStep() const { return p_simulation->getStep(); }
void setSimulationSpeed(int speed) {
m_maxTimePerStep = std::round(1000 / speed);
}
// set maximum number of timesteps after which the simulation will stop, -1
// for infinite simulation
void setMaxSteps(int n = -1) { m_maxSteps = n; }
void clearRecords() {
for (size_t i = 0; i < m_record.size(); i++) {
m_record[i] =
std::queue<std::pair<Eigen::MatrixXd, Eigen::MatrixXi>>();
}
}
void setRecording(bool r) {
if (r && m_record.size() == 0) {
m_record.resize(p_simulation->getObjects().size());
}
else {
clearRecords();
}
m_recording = r;
}
bool isRecording() const { return m_recording; }
void setNumRecords(int n) { m_numRecords = n; }
std::vector<std::queue<std::pair<Eigen::MatrixXd, Eigen::MatrixXi>>>
&getRecords() {
return m_record;
}
protected:
void storeRecord() {
auto os = p_simulation->getObjects();
Eigen::MatrixXd V;
Eigen::MatrixXi F;
for (size_t i = 0; i < os.size(); i++) {
os[i].getMesh(V, F);
m_record[i].push(std::make_pair(V, F));
while (m_record[i].size() > (size_t)m_numRecords) {
m_record[i].pop();
}
}
}
void runSimThread() {
m_status_mutex.lock();
m_running = true;
m_status_mutex.unlock();
bool done = false;
while (!done) {
m_status_mutex.lock();
bool pausenow = m_please_pause;
bool single = m_single_iteration;
m_status_mutex.unlock();
if (pausenow && !single) {
// don't use to much CPU time
std::this_thread::sleep_for(milliseconds(10));
}
else {
if (m_maxSteps >= 0 && m_maxSteps <= p_simulation->getStep()) {
pause();
continue;
}
// time execution of one loop (advance + rendering)
high_resolution_clock::time_point start =
high_resolution_clock::now();
done = p_simulation->advance();
if (m_recording) {
storeRecord();
}
m_render_mutex.lock();
p_simulation->updateRenderGeometry();
m_render_mutex.unlock();
high_resolution_clock::time_point end =
high_resolution_clock::now();
m_duration = end - start;
// sleep such that simulation runs at the set iterations per
// second
milliseconds sleepTime =
milliseconds(m_maxTimePerStep) -
duration_cast<milliseconds>(m_duration);
std::this_thread::sleep_for(sleepTime);
}
m_status_mutex.lock();
if (single) m_single_iteration = false;
if (m_please_die) done = true;
m_status_mutex.unlock();
}
m_status_mutex.lock();
m_running = false;
m_status_mutex.unlock();
}
void killSimulatorThread() {
if (p_simulator_thread) {
m_status_mutex.lock();
m_please_die = true;
m_status_mutex.unlock();
p_simulator_thread->join();
delete p_simulator_thread;
p_simulator_thread = NULL;
}
}
std::thread *p_simulator_thread;
Simulation *p_simulation;
duration<double> m_duration;
bool m_please_pause;
bool m_please_die;
bool m_running;
bool m_started;
bool m_single_iteration;
int m_maxTimePerStep;
int m_maxSteps = -1; // max number of steps to perform, -1 for infinite
std::mutex m_render_mutex;
std::mutex m_status_mutex;
bool m_recording;
int m_numRecords;
std::vector<std::queue<std::pair<Eigen::MatrixXd, Eigen::MatrixXi>>>
m_record; // one queue of (vertices, faces)-pairs for every object
};
#endif
\ No newline at end of file
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment