From f1356a59fc0c22e555b936ae4c6841ad154a5ed7 Mon Sep 17 00:00:00 2001 From: Yuwei Xiao <xyw1105@126.com> Date: Mon, 6 Apr 2020 16:45:42 +0800 Subject: [PATCH] ex4 --- 4_collision/CMakeLists.txt | 43 ++++ 4_collision/CollisionDetection.cpp | 191 ++++++++++++++++++ 4_collision/CollisionDetection.h | 176 +++++++++++++++++ 4_collision/CollisionSim.h | 302 +++++++++++++++++++++++++++++ 4_collision/main.cpp | 94 +++++++++ 5 files changed, 806 insertions(+) create mode 100644 4_collision/CMakeLists.txt create mode 100644 4_collision/CollisionDetection.cpp create mode 100644 4_collision/CollisionDetection.h create mode 100644 4_collision/CollisionSim.h create mode 100644 4_collision/main.cpp diff --git a/4_collision/CMakeLists.txt b/4_collision/CMakeLists.txt new file mode 100644 index 0000000..f2a3b20 --- /dev/null +++ b/4_collision/CMakeLists.txt @@ -0,0 +1,43 @@ + +cmake_minimum_required(VERSION 3.1) +project(4_collision) + +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/../cmake) +set(CMAKE_CXX_FLAGS "-Wall") + +# libigl +option(LIBIGL_USE_STATIC_LIBRARY "Use libigl as static library" OFF) +option(LIBIGL_WITH_ANTTWEAKBAR "Use AntTweakBar" OFF) +option(LIBIGL_WITH_CGAL "Use CGAL" OFF) +option(LIBIGL_WITH_COMISO "Use CoMiso" OFF) +option(LIBIGL_WITH_CORK "Use Cork" OFF) +option(LIBIGL_WITH_EMBREE "Use Embree" OFF) +option(LIBIGL_WITH_LIM "Use LIM" OFF) +option(LIBIGL_WITH_MATLAB "Use Matlab" OFF) +option(LIBIGL_WITH_MOSEK "Use MOSEK" OFF) +option(LIBIGL_WITH_OPENGL "Use OpenGL" ON) +option(LIBIGL_WITH_OPENGL_GLFW "Use GLFW" ON) +option(LIBIGL_WITH_OPENGL_GLFW_IMGUI "Use ImGui" ON) +option(LIBIGL_WITH_PNG "Use PNG" OFF) +option(LIBIGL_WITH_PYTHON "Use Python" OFF) +option(LIBIGL_WITH_TETGEN "Use Tetgen" OFF) +option(LIBIGL_WITH_TRIANGLE "Use Triangle" OFF) +option(LIBIGL_WITH_VIEWER "Use OpenGL viewer" ON) +option(LIBIGL_WITH_XML "Use XML" OFF) + +if (NOT LIBIGL_FOUND) + find_package(LIBIGL REQUIRED QUIET) +endif() + +# Add default project files +file(GLOB LIBFILES ${PROJECT_SOURCE_DIR}/../include/*.*) +source_group("Library Files" FILES ${LIBFILES}) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../include) + +# Add your project files +file(GLOB SRCFILES *.cpp) +file(GLOB HFILES *.h) + +add_definitions(-DIGL_VIEWER_VIEWER_QUIET) +add_executable(${PROJECT_NAME} ${SRCFILES} ${HFILES} ${LIBFILES} ) +target_link_libraries(${PROJECT_NAME} igl::core igl::opengl_glfw igl::opengl_glfw_imgui) \ No newline at end of file diff --git a/4_collision/CollisionDetection.cpp b/4_collision/CollisionDetection.cpp new file mode 100644 index 0000000..91cc8eb --- /dev/null +++ b/4_collision/CollisionDetection.cpp @@ -0,0 +1,191 @@ +#include "CollisionDetection.h" + +void CollisionDetection::computeBroadPhase(int broadPhaseMethod) { + // compute possible collisions + m_overlappingBodys.clear(); + + switch (broadPhaseMethod) { + case 0: { // none + for (size_t i = 0; i < m_objects.size(); i++) { + for (size_t j = i + 1; j < m_objects.size(); j++) { + m_overlappingBodys.push_back(std::make_pair(i, j)); + } + } + break; + } + + case 1: { // AABB + // compute bounding boxes + std::vector<AABB> aabbs(m_objects.size()); + for (size_t i = 0; i < aabbs.size(); i++) { + aabbs[i].computeAABB(m_objects[i]); + } + for (size_t i = 0; i < m_objects.size(); i++) { + for (size_t j = i + 1; j < m_objects.size(); j++) { + // add pair of objects to possible collision if their + // bounding boxes overlap + if (aabbs[i].testCollision(aabbs[j])) { + m_overlappingBodys.push_back(std::make_pair(i, j)); + } + } + } + break; + } + + case 2: { + // TODO: implement other broad phase algorithm + break; + } + } +} + +void CollisionDetection::computeNarrowPhase(int narrowPhaseMethod) { + switch (narrowPhaseMethod) { + case 0: { + // exhaustive + // iterate through all pairs of possible collisions + for (auto overlap : m_overlappingBodys) { + std::vector<Contact> temp_contacts[2]; + // compute intersection of a with b first and intersectino + // of b with a and store results in temp_contacts + for (int switcher = 0; switcher < 2; switcher++) { + RigidObject* a = + &m_objects[(!switcher) ? overlap.first + : overlap.second]; + RigidObject* b = + &m_objects[(!switcher) ? overlap.second + : overlap.first]; + + Eigen::MatrixXd Va, Vb; + Eigen::MatrixXi Fa, Fb; + a->getMesh(Va, Fa); + b->getMesh(Vb, Fb); + + // iterate through all faces of first object + for (int face = 0; face < Fa.rows(); face++) { + // iterate through all edges of given face + for (size_t j = 0; j < 3; j++) { + int start = Fa(face, j); + int end = Fa(face, (j + 1) % 3); + + // check if there is a collision + ContactType ct = isColliding( + Va.row(start), Va.row(end), Vb, Fb); + + // find collision and check for duplicates + switch (ct) { + case ContactType::VERTEXFACE: { + auto ret = m_penetratingVertices.insert( + Fa(face, j)); + // if not already in set + if (ret.second) { + Contact temp_col = + findVertexFaceCollision( + Va.row(Fa(face, j)), Vb, + Fb); + temp_col.a = a; + temp_col.b = b; + temp_col.type = + ContactType::VERTEXFACE; + temp_contacts[switcher].push_back( + temp_col); + } + break; + } + case ContactType::EDGEEDGE: { + int orderedStart = std::min(start, end); + int orderedEnd = std::max(start, end); + auto ret = m_penetratingEdges.insert( + std::make_pair(orderedStart, + orderedEnd)); + // if not already in set + if (ret.second) { + Contact temp_col = + findEdgeEdgeCollision( + Va.row(orderedStart), + Va.row(orderedEnd), Vb, Fb); + temp_col.a = a; + temp_col.b = b; + temp_col.type = + ContactType::EDGEEDGE; + temp_contacts[switcher].push_back( + temp_col); + } + break; + } + case ContactType::NONE: { + break; + } + } + } + } + } + + // look for vertexFace + bool found = false; + for (int i = 0; i < 2; i++) { + for (auto cont : temp_contacts[i]) { + if (cont.type == ContactType::VERTEXFACE) { + m_contacts.push_back(cont); + found = true; + break; + } + } + if (found) { + break; + } + } + if (found) { + continue; + } + + // take single edgeedge if possible + if (temp_contacts[0].size() > 0 && + temp_contacts[0].size() < temp_contacts[1].size()) { + for (int i = 0; i < temp_contacts[0].size(); i++) { + m_contacts.push_back(temp_contacts[0][i]); + } + } else if (temp_contacts[1].size() > 0 && + temp_contacts[0].size() > + temp_contacts[1].size()) { + for (int i = 0; i < temp_contacts[1].size(); i++) { + m_contacts.push_back(temp_contacts[1][i]); + } + } else if (temp_contacts[0].size() > 0) { + for (int i = 0; i < temp_contacts[0].size(); i++) { + m_contacts.push_back(temp_contacts[0][i]); + } + } else if (temp_contacts[1].size() > 0) { + for (int i = 0; i < temp_contacts[1].size(); i++) { + m_contacts.push_back(temp_contacts[1][i]); + } + } + } + break; + } + + case 1: { + // TODO: implement other narrow phase algorithm + break; + } + } +} + +void CollisionDetection::applyImpulse(double eps) { + // compute impulse for all contacts + for (auto contact : m_contacts) { + Eigen::Vector3d vrel_vec = contact.a->getVelocity(contact.p) - + contact.b->getVelocity(contact.p); + double vrel = contact.n.dot(vrel_vec); + if (vrel > 0) { + // bodies are moving apart + continue; + } + + // TODO: compute impulse and update the following momentums + //contact.a->setLinearMomentum(); + //contact.b->setLinearMomentum(); + //contact.a->setAngularMomentum(); + //contact.b->setAngularMomentum(); + } +} \ No newline at end of file diff --git a/4_collision/CollisionDetection.h b/4_collision/CollisionDetection.h new file mode 100644 index 0000000..2a667cd --- /dev/null +++ b/4_collision/CollisionDetection.h @@ -0,0 +1,176 @@ +#ifndef COLLISIONDETECTION_H +#define COLLISIONDETECTION_H + +#include <igl/ray_mesh_intersect.h> +#include <set> +#include <utility> +#include <vector> +#include "AABB.h" +#include "RigidObject.h" + +enum class ContactType { VERTEXFACE, EDGEEDGE, NONE }; + +struct Contact { + RigidObject* a; // body containing vertex + RigidObject* b; // body containing face + Eigen::Vector3d n; // world-space vertex location + Eigen::Vector3d p; // outwards pointing normal of face + Eigen::Vector3d ea; // edge direction for A + Eigen::Vector3d eb; // edge direction for B + + ContactType type; // type of contact +}; + +class CollisionDetection { + public: + CollisionDetection(std::vector<RigidObject>& world) : m_objects(world) {} + + // pass objects in scene to collision detection + void setObjects(std::vector<RigidObject>& world) { m_objects = world; } + + void computeBroadPhase(int broadPhaseMethod); + + // test if ray from start towards end intersects object with vertices V and + // faces F + ContactType isColliding(const Eigen::Vector3d& start, + const Eigen::Vector3d& end, + const Eigen::MatrixXd& V, + const Eigen::MatrixXi& F) { + std::vector<igl::Hit> hits; + igl::ray_mesh_intersect(start, end - start, V, F, hits); + + // count number of intersections with object that happen between start + // and end (so along the edge) + int cntr = 0; + for (auto hit : hits) { + if (hit.t > 0 && hit.t <= 1) { + cntr++; + } + } + + // if hits is odd then the ray enters through one face and + // does not leave again through another, hence the starting point is + // inside the object, if the number of intersections between start and + // end of edge are even, then the edge entering through one and leaving + // through another face, hence the edge is intersection the object + ContactType ret = ContactType::NONE; + if (hits.size() % 2 == 1) { + ret = ContactType::VERTEXFACE; + } + if (cntr % 2 == 0 && cntr > 0) { + ret = ContactType::EDGEEDGE; + } + return ret; + } + + // compute normal for all faces and use it to find closesest face to + // given vertex + Contact findVertexFaceCollision(const Eigen::Vector3d& vertex, + const Eigen::MatrixXd& V, + const Eigen::MatrixXi& F) { + double minDist = std::numeric_limits<double>::infinity(); + Eigen::Vector3d minNormal; + for (int i = 0; i < F.rows(); i++) { + Eigen::Vector3d a = V.row(F(i, 0)); + Eigen::Vector3d b = V.row(F(i, 1)); + Eigen::Vector3d c = V.row(F(i, 2)); + + Eigen::Vector3d n = -(b - a).cross(c - a).normalized(); + + Eigen::Vector3d v = vertex - a; + + double distance = v.dot(n); + // if vertex inside + if (distance >= 0) { + if (distance < minDist) { + minDist = distance; + minNormal = -n; + } + } + } + Contact ret; + ret.n = minNormal; + ret.p = vertex + minDist * minNormal; + return ret; + } + + // loop over edges of faces and compute closest to given edge + Contact findEdgeEdgeCollision(const Eigen::Vector3d& start, + const Eigen::Vector3d& end, + const Eigen::MatrixXd& V, + const Eigen::MatrixXi& F) { + double minDist = std::numeric_limits<double>::infinity(); + Contact ret; + for (int i = 0; i < F.rows(); i++) { + Eigen::Vector3d a = V.row(F(i, 0)); + Eigen::Vector3d b = V.row(F(i, 1)); + Eigen::Vector3d c = V.row(F(i, 2)); + + Eigen::Vector3d n_face = -(b - a).cross(c - a).normalized(); + + for (int j = 0; j < 3; j++) { + Eigen::Vector3d s = V.row(F(i, j)); + Eigen::Vector3d e = V.row(F(i, (j + 1) % 3)); + + Eigen::Vector3d ea = end - start; + Eigen::Vector3d eb = e - s; + Eigen::Vector3d n = + (ea).cross(eb); // direction of shortest distance + double distance = n.dot(start - s) / n.norm(); + + Eigen::Vector3d plane_normal = n.cross(eb).normalized(); + double t = + (s - start).dot(plane_normal) / (ea.dot(plane_normal)); + if (n_face.dot(n) < 0 && distance < 0 && -distance < minDist && + t >= 0 && t <= 1) { + ret.ea = ea; + ret.eb = eb; + ret.n = n; + ret.p = start + t * ea; + minDist = -distance; + } + } + } + return ret; + } + + void computeNarrowPhase(int narrowPhaseMethod); + + void applyImpulse(double eps = 1.0); + + void clearDataStructures() { + m_penetratingEdges.clear(); + m_penetratingVertices.clear(); + m_overlappingBodys.clear(); + m_contacts.clear(); + } + + void computeCollisionDetection(int broadPhaseMethod = 0, + int narrowPhaseMethod = 0, + double eps = 1.0) { + clearDataStructures(); + + computeBroadPhase(broadPhaseMethod); + + computeNarrowPhase(narrowPhaseMethod); + + applyImpulse(eps); + } + + std::vector<Contact> getContacts() { return m_contacts; } + + std::vector<RigidObject>& m_objects; // all objects in scene + // result of broadphase, pairs of objects with possible collisions + std::vector<std::pair<size_t, size_t>> m_overlappingBodys; + + // set of vertex indices that penetrate a face, used to avoid duplicates + std::set<int> m_penetratingVertices; + // set of pairs of vertex indices that represent a penetrating edge, used to + // avoid duplicates + std::set<std::pair<int, int>> m_penetratingEdges; + + // computed contact points + std::vector<Contact> m_contacts; +}; + +#endif diff --git a/4_collision/CollisionSim.h b/4_collision/CollisionSim.h new file mode 100644 index 0000000..38ffc18 --- /dev/null +++ b/4_collision/CollisionSim.h @@ -0,0 +1,302 @@ +#include "CollisionDetection.h" +#include "Simulation.h" + +#include <deque> + +using namespace std; + +/* + */ +class CollisionSim : public Simulation { + public: + CollisionSim() : Simulation(), m_collisionDetection(m_objects) { init(); } + + const int NUM_CUBES = 5; // complexity of scene + + virtual void init() override { + std::string path = "cube.off"; + for (int i = 0; i < NUM_CUBES; i++) { + m_objects.push_back(RigidObject(path)); + } + path = "cube_shallow.off"; + for (int i = 0; i < 5; ++i) { + m_objects.push_back(RigidObject(path)); + } + + m_collisionDetection.setObjects(m_objects); + + m_dt = 1e-3 * 3; + m_gravity << 0, -9.81, 0; + m_mass = 1.0; + m_showContacts = false; + m_broadPhaseMethod = 0; + m_narrowPhaseMethod = 0; + m_eps = 1.0; + + reset(); + } + + virtual void resetMembers() override { + for (auto &o : m_objects) { + o.reset(); + } + + double x1 = 5; + double y1 = 4; + m_objects[0].setPosition(Eigen::Vector3d(0, y1, 0)); + m_objects[0].setRotation( + Eigen::Quaterniond(0, -0.3444844, -0.3444844, -0.8733046)); + + Eigen::RowVector3d c0(204.0 / 255.0, 0, 0); + Eigen::RowVector3d c1(0, 128.0 / 255.0, 204.0 / 255.0); + for (int i = 1; i < NUM_CUBES; ++i) { + m_objects[i].setPosition(Eigen::Vector3d(x1, y1*(i+1), 0)); + double a = double(i+1) / NUM_CUBES; + m_objects[i].setColors(c0*a + c1*(1 - a)); + } + + for (size_t i = 0; i < NUM_CUBES; i++) { + m_objects[i].setMass(m_mass); + } + + for (int i = 0; i < 5; ++i) { + m_objects[i + NUM_CUBES].setScale(10); + m_objects[i + NUM_CUBES].setType(ObjType::STATIC); + m_objects[i + NUM_CUBES].setColors(Eigen::RowVector3d(0.5, 0.5, 0.5)); + m_objects[i + NUM_CUBES].setMass(std::numeric_limits<double>::max()); + } + + m_objects[0 + NUM_CUBES].setPosition( + Eigen::Vector3d(0, -0.1, 0)); + + m_objects[1 + NUM_CUBES].setPosition( + Eigen::Vector3d(-10.1, 10, 0)); + m_objects[1 + NUM_CUBES].setRotation( + Eigen::Quaterniond(0.7071, 0, 0, 0.7071)); + + m_objects[2 + NUM_CUBES].setPosition( + Eigen::Vector3d(10.1, 10, 0)); + m_objects[2 + NUM_CUBES].setRotation( + Eigen::Quaterniond(0.7071, 0, 0, 0.7071)); + + m_objects[3 + NUM_CUBES].setPosition( + Eigen::Vector3d(0, 10, -10.1)); + m_objects[3 + NUM_CUBES].setRotation( + Eigen::Quaterniond(0.5, 0.5, 0.5, 0.5)); + + m_objects[4 + NUM_CUBES].setPosition( + Eigen::Vector3d(0, 10, 10.1)); + m_objects[4 + NUM_CUBES].setRotation( + Eigen::Quaterniond(0.5, 0.5, 0.5, 0.5)); + + + updateVars(); + } + + virtual void updateRenderGeometry() override { + for (size_t i = 0; i < m_objects.size(); i++) { + RigidObject &o = m_objects[i]; + if (o.getID() < 0) { + m_renderVs.emplace_back(); + m_renderFs.emplace_back(); + } + + m_objects[i].getMesh(m_renderVs[i], m_renderFs[i]); + } + } + + virtual bool advance() override { + // compute the collision detection + m_collisionDetection.computeCollisionDetection(m_broadPhaseMethod, m_narrowPhaseMethod, m_eps); + + // apply forces (only gravity in this case) + for (auto &o : m_objects) { + o.applyForceToCOM(m_gravity); + } + + for (auto &o : m_objects) { + // integrate velocities + o.setLinearMomentum(o.getLinearMomentum() + m_dt * o.getForce()); + o.setAngularMomentum(o.getAngularMomentum() + m_dt * o.getTorque()); + o.resetForce(); + o.resetTorque(); + + // angular velocity (matrix) + Eigen::Vector3d w = o.getAngularVelocity(); + Eigen::Quaterniond wq; + wq.w() = 0; + wq.vec() = w; + + // integrate position and rotation + o.setPosition(o.getPosition() + m_dt * o.getLinearVelocity()); + Eigen::Quaterniond q = o.getRotation(); + Eigen::Quaterniond dq = wq * q; + Eigen::Quaterniond new_q; + new_q.w() = q.w() + 0.5 * m_dt * dq.w(); + new_q.vec() = q.vec() + 0.5 * m_dt * dq.vec(); + o.setRotation(new_q.normalized()); + } + // advance time + m_time += m_dt; + m_step++; + + return false; + } + + virtual void renderRenderGeometry( + igl::opengl::glfw::Viewer &viewer) override { + for (size_t i = 0; i < m_objects.size(); i++) { + RigidObject &o = m_objects[i]; + if (o.getID() < 0) { + int new_id = 0; + if (i > 0) { + new_id = viewer.append_mesh(); + o.setID(new_id); + } else { + o.setID(new_id); + } + + size_t meshIndex = viewer.mesh_index(o.getID()); + if (i >= NUM_CUBES) { + viewer.data_list[meshIndex].show_lines = true; + viewer.data_list[meshIndex].show_faces = false; + } + else { + viewer.data_list[meshIndex].show_lines = false; + } + viewer.data_list[meshIndex].set_face_based(true); + viewer.data_list[meshIndex].point_size = 2.0f; + viewer.data_list[meshIndex].clear(); + } + size_t meshIndex = viewer.mesh_index(o.getID()); + + viewer.data_list[meshIndex].set_mesh(m_renderVs[i], m_renderFs[i]); + viewer.data_list[meshIndex].compute_normals(); + + Eigen::MatrixXd color; + o.getColors(color); + viewer.data_list[meshIndex].set_colors(color); + } + + if (m_showContacts) { + // number of timesteps to keep showing collision + int delay = 10; + + // clear old points + viewer.data_list[1].points = Eigen::MatrixXd(0, 6); + viewer.data_list[1].point_size = 10.0f; + + // remove expired points + while (m_contactMemory.size() > 0 && + m_contactMemory.front().second + delay < m_step) { + m_contactMemory.pop_front(); + } + + // get new points and add them to memory + auto contacts = m_collisionDetection.getContacts(); + for (auto &contact : contacts) { + m_contactMemory.push_back(std::make_pair(contact, m_step)); + } + + // show points + for (auto &contact_int_p : m_contactMemory) { + viewer.data_list[1].add_points( + contact_int_p.first.p.transpose(), + (contact_int_p.first.type == ContactType::EDGEEDGE) + ? Eigen::RowVector3d(0, 1, 0) + : Eigen::RowVector3d(0, 0, 1)); + } + } + } + +#pragma region SettersAndGetters + /* + * Compute magnitude and direction of momentum and apply it to o + */ + void updateVars() { + Eigen::Vector3d momentum; + momentum << std::sin(m_angle), std::cos(m_angle), 0; + momentum *= m_force; + m_objects[0].setLinearMomentum(momentum); + } + + void setAngle(double a) { + m_angle = a; + updateVars(); + } + + void setForce(double f) { + m_force = f; + updateVars(); + } + + void setMass(double m) { m_mass = m; } + + void showContacts(bool s) { + if (!s) { + m_contactMemory.clear(); + } + m_showContacts = s; + } + + void setBroadPhaseMethod(int m) { m_broadPhaseMethod = m; } + void setNarrowPhaseMethod(int m) { m_narrowPhaseMethod = m; } + + void setEps(double eps) { m_eps = eps; } + + Eigen::Vector3d getKineticEnergy() { + Eigen::Vector3d res; + res.setZero(); + for (auto o : m_objects) { + if (o.getType() == ObjType::STATIC) continue; + Eigen::Vector3d rotE = 0.5 * o.getInertia().diagonal().cwiseProduct( + o.getAngularVelocity()); + Eigen::Vector3d kinE = + 0.5 * o.getMass() * o.getLinearVelocity().array().square(); + res += rotE + kinE; + } + return res; + } + + Eigen::Vector3d getLinearMomentum() { + Eigen::Vector3d res; + res.setZero(); + for (auto o : m_objects) { + if (o.getType() == ObjType::STATIC) continue; + res += o.getLinearMomentum(); + } + return res; + } + + Eigen::Vector3d getAngularMomentum() { + Eigen::Vector3d res; + res.setZero(); + for (auto o : m_objects) { + if (o.getType() == ObjType::STATIC) continue; + res += o.getAngularMomentum(); + } + return res; + } + +#pragma endregion SettersAndGetters + + private: + [[maybe_unused]] int m_method; // id of integrator to be used (0: analytical, 1: explicit + // euler, 2: semi-implicit euler) + double m_angle; + double m_force; + double m_mass; + + Eigen::Vector3d m_gravity; + + CollisionDetection m_collisionDetection; + int m_broadPhaseMethod; + int m_narrowPhaseMethod; + double m_eps; + + std::vector<Eigen::MatrixXd> m_renderVs; // vertex positions for rendering + std::vector<Eigen::MatrixXi> m_renderFs; // face indices for rendering + + bool m_showContacts; + std::deque<std::pair<Contact, int>> m_contactMemory; +}; \ No newline at end of file diff --git a/4_collision/main.cpp b/4_collision/main.cpp new file mode 100644 index 0000000..e120f39 --- /dev/null +++ b/4_collision/main.cpp @@ -0,0 +1,94 @@ +#include <igl/writeOFF.h> +#include "CollisionSim.h" +#include "Gui.h" + +class CollisionGui : public Gui { + public: + float m_angle = 1.047f; + float m_force = 10.0f; + float m_dt = 1e-1 * 3; + float m_mass = 1.0; + bool m_showContacts = false; + bool m_useAABB = false; + float m_eps = 0.6; + + int m_maxHistory = 200; + std::vector<float> m_energy_history; + + int m_selectedBroadPhase = 0; + const std::vector<char const *> m_broadphases = {"None", "AABB", "Own"}; + int m_selectedNarrowPhase = 0; + const std::vector<char const *> m_narrowphases = {"Exhaustive", "Own"}; + + CollisionSim *p_CollisionSim = NULL; + + CollisionGui() { + p_CollisionSim = new CollisionSim(); + setSimulation(p_CollisionSim); + + // show vertex velocity instead of normal + callback_clicked_vertex = [&](int clickedVertexIndex, + int clickedObjectIndex, + Eigen::Vector3d &pos, + Eigen::Vector3d &dir) { + RigidObject &o = p_CollisionSim->getObjects()[clickedObjectIndex]; + pos = o.getVertexPosition(clickedVertexIndex); + dir = o.getVelocity(pos); + }; + start(); + } + + virtual void updateSimulationParameters() override { + p_CollisionSim->setForce(m_force); + p_CollisionSim->setAngle(m_angle); + p_CollisionSim->setTimestep(m_dt); + p_CollisionSim->setMass(m_mass); + p_CollisionSim->setEps(m_eps); + } + + virtual void clearSimulation() override { + p_CollisionSim->showContacts(false); + p_CollisionSim->showContacts(m_showContacts); + } + + virtual void drawSimulationParameterMenu() override { + ImGui::SliderAngle("Angle", &m_angle, -180.0f, 180.0f); + ImGui::InputFloat("Force", &m_force, 0, 0); + ImGui::InputFloat("Mass", &m_mass, 0, 0); + ImGui::InputFloat("dt", &m_dt, 0, 0); + if (ImGui::Checkbox("Show contacts", &m_showContacts)) { + p_CollisionSim->showContacts(m_showContacts); + } + if (ImGui::Combo("Broadphase", &m_selectedBroadPhase, + m_broadphases.data(), m_broadphases.size())) { + p_CollisionSim->setBroadPhaseMethod(m_selectedBroadPhase); + } + if (ImGui::Combo("Narrowphase", &m_selectedNarrowPhase, + m_narrowphases.data(), m_narrowphases.size())) { + p_CollisionSim->setNarrowPhaseMethod(m_selectedNarrowPhase); + } + ImGui::InputFloat("eps", &m_eps, 0, 0); + } + + virtual void drawSimulationStats() override { + Eigen::Vector3d E = p_CollisionSim->getKineticEnergy(); + m_energy_history.push_back(E.cast<float>().cwiseAbs().sum()); + if (m_energy_history.size() > m_maxHistory) + m_energy_history.erase(m_energy_history.begin(), + m_energy_history.begin() + 1); + ImGui::Text("E: %.3f, %.3f, %.3f", E(0), E(1), E(2)); + ImGui::PlotLines("Total Energy", &m_energy_history[0], + m_energy_history.size(), 0, NULL, 0, 1000, + ImVec2(0, 200)); + Eigen::Vector3d p = p_CollisionSim->getLinearMomentum(); + ImGui::Text("M: %.3f, %.3f, %.3f", p(0), p(1), p(2)); + Eigen::Vector3d l = p_CollisionSim->getAngularMomentum(); + ImGui::Text("L: %.3f, %.3f, %.3f", l(0), l(1), l(2)); + } +}; + +int main(int argc, char *argv[]) { + new CollisionGui(); + + return 0; +} \ No newline at end of file -- GitLab