diff --git a/assets/opencvpart.obj b/assets/opencvpart.obj index 03231fe..388524f 100644 --- a/assets/opencvpart.obj +++ b/assets/opencvpart.obj @@ -502,6 +502,7 @@ vt 0.000000 0.000000 vt 0.000000 0.000000 vt 0.000000 0.000000 s 0 +#group 0 f 47/47/1 46/46/1 100/100/1 101/101/1 f 21/21/1 20/20/1 74/74/1 75/75/1 f 48/48/1 47/47/1 101/101/1 102/102/1 @@ -555,6 +556,7 @@ f 45/45/1 44/44/1 98/98/1 99/99/1 f 19/19/1 18/18/1 72/72/1 73/73/1 f 46/46/1 45/45/1 99/99/1 100/100/1 f 20/20/1 19/19/1 73/73/1 74/74/1 +#group 1 f 155/155/2 209/209/2 208/208/2 154/154/2 f 129/129/2 183/183/2 182/182/2 128/128/2 f 156/156/2 210/210/2 209/209/2 155/155/2 @@ -608,6 +610,7 @@ f 153/153/2 207/207/2 206/206/2 152/152/2 f 127/127/2 181/181/2 180/180/2 126/126/2 f 154/154/2 208/208/2 207/207/2 153/153/2 f 128/128/2 182/182/2 181/181/2 127/127/2 +#group 2 f 2/2/3 3/3/3 111/111/3 110/110/3 f 3/3/4 4/4/4 112/112/4 111/111/4 f 4/4/5 5/5/5 113/113/5 112/112/5 @@ -661,6 +664,7 @@ f 51/51/52 52/52/52 160/160/52 159/159/52 f 52/52/53 53/53/53 161/161/53 160/160/53 f 53/53/54 54/54/54 162/162/54 161/161/54 f 54/54/55 1/1/55 109/109/55 162/162/55 +#group 3 f 57/57/35 56/56/35 164/164/35 165/165/35 f 58/58/36 57/57/36 165/165/36 166/166/36 f 59/59/37 58/58/37 166/166/37 167/167/37 @@ -714,5 +718,7 @@ f 106/106/20 105/105/20 213/213/20 214/214/20 f 107/107/21 106/106/21 214/214/21 215/215/21 f 108/108/22 107/107/22 215/215/22 216/216/22 f 55/55/23 108/108/23 216/216/23 163/163/23 +#group 4 f 1/1/67 55/55/67 163/163/67 109/109/67 +#group 5 f 56/56/68 2/2/68 110/110/68 164/164/68 diff --git a/include/OBJ.hpp b/include/OBJ.hpp index d2f6fac..cfd8fc7 100644 --- a/include/OBJ.hpp +++ b/include/OBJ.hpp @@ -2,11 +2,14 @@ #include #include #include +#include struct OBJ { std::vector vertices; std::vector faces; + std::unordered_map> smoothing_groups; - void load_obj(std::string path); + void load_obj(std::string path, bool grouped = false); void draw_obj(); + void draw_obj_smooth(); }; diff --git a/src/Game.cpp b/src/Game.cpp index d475d83..41e244c 100644 --- a/src/Game.cpp +++ b/src/Game.cpp @@ -4,14 +4,16 @@ #include #include "OBJ.hpp" +#include + struct Game::Private { // P-IMPL pattern glm::mat4x4 viewMat; glm::mat4x4 projMat; - OBJ monkey; + std::vector ocvLogo; }; -Game::Game() : m_private(std::make_unique()) {} +Game::Game() : m_private(std::make_unique()), m_window(nullptr) {} Game::~Game() = default; @@ -31,11 +33,49 @@ void Game::set_window(GLFWwindow *window) { #endif void Game::initialize() { - /* m_private->monkey.load_obj(OPENGLTUTOR_HOME "assets/opencvpart.obj"); */ - m_private->monkey.load_obj(OPENGLTUTOR_HOME "assets/monkey.obj"); - /* m_private->monkey.load_obj(OPENGLTUTOR_HOME "assets/cube.obj"); */ + OBJ ocvpart; + ocvpart.load_obj(OPENGLTUTOR_HOME "assets/opencvpart.obj", true); + + for (size_t i = 0; i < 3; ++i) { + m_private->ocvLogo.push_back(ocvpart); + } + constexpr float short_side = 0.65f; + glm::vec3 tri_sides = {short_side, 2 * short_side, short_side * sqrtf(3)}; + + auto C0 = glm::vec3(0.0f, tri_sides.y, 0.0f); + auto C1 = glm::vec3(-tri_sides.z, -tri_sides.x, 0.0f); + auto C2 = glm::vec3(tri_sides.z, -tri_sides.x, 0.0f); + + std::vector cvLogoModel; + + auto model = glm::mat4x4(1.0f); + model = glm::translate(model, C0); + model = glm::rotate(model, glm::radians(90.0f), glm::vec3(1.0f, 0.0f, 0.0f)); + model = glm::rotate(model, glm::radians(150.0f), glm::vec3(0.0f, 1.0f, 0.0f)); + cvLogoModel.push_back(model); + + model = glm::mat4x4(1.0f); + model = glm::translate(model, C1); + model = glm::rotate(model, glm::radians(90.0f), glm::vec3(1.0f, 0.0f, 0.0f)); + model = glm::rotate(model, glm::radians(-90.0f), glm::vec3(0.0f, 1.0f, 0.0f)); + cvLogoModel.push_back(model); + + model = glm::mat4x4(1.0f); + model = glm::translate(model, C2); + model = glm::rotate(model, glm::radians(90.0f), glm::vec3(1.0f, 0.0f, 0.0f)); + model = glm::rotate(model, glm::radians(-30.0f), glm::vec3(0.0f, 1.0f, 0.0f)); + cvLogoModel.push_back(model); + + for (size_t i = 0; i < m_private->ocvLogo.size(); ++i) { + auto& ocvPart = m_private->ocvLogo.at(i); + auto& ocvPartModel = cvLogoModel.at(i); + for (auto& vert : ocvPart.vertices) { + vert = ocvPartModel * glm::vec4(vert, 1.0f); + } + } + CHECK_GL(glEnable(GL_DEPTH_TEST)); - CHECK_GL(glDisable(GL_MULTISAMPLE)); + CHECK_GL(glEnable(GL_MULTISAMPLE)); CHECK_GL(glEnable(GL_BLEND)); CHECK_GL(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); CHECK_GL(glEnable(GL_LIGHTING)); @@ -44,6 +84,7 @@ void Game::initialize() { CHECK_GL(glEnable(GL_CULL_FACE)); CHECK_GL(glCullFace(GL_BACK)); CHECK_GL(glFrontFace(GL_CCW)); + CHECK_GL(glShadeModel(GL_SMOOTH)); } void Game::render() { @@ -52,24 +93,37 @@ void Game::render() { CHECK_GL(glViewport(0, 0, width, height)); CHECK_GL(glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT)); + glClearColor(0.1f, 0.1f, 0.1f, 1.0f); auto projection = m_inputCtl.get_projection_matrix(); CHECK_GL(glMatrixMode(GL_PROJECTION)); CHECK_GL(glLoadMatrixf(glm::value_ptr(projection))); - + auto view = m_inputCtl.get_view_matrix(); - + glm::mat4x4 model(1.0f); + + static float motionAngle = 0.0f; + static constexpr glm::vec3 motionAxis = { 0.0f, 1.0f, 0.0f }; + static constexpr float motionDelta = 1.2f; - static float angle = 0.0f; - model = glm::rotate(model, glm::radians(angle), glm::vec3(0.0f, 1.0f, 0.0f)); - model = glm::translate(model, glm::vec3(0.0f, 0.12f * glm::sin(glm::radians(angle) * 2.718f), 0.0f)); - angle += 0.5f; - CHECK_GL(glMatrixMode(GL_MODELVIEW)); + + model = glm::rotate(glm::mat4(1.0f), glm::radians(motionAngle), motionAxis); + CHECK_GL(glLoadMatrixf(glm::value_ptr(view * model))); + glColor3f(1.0f, 0.0f, 0.0f); + m_private->ocvLogo.at(0).draw_obj_smooth(); + + model = glm::rotate(glm::mat4(1.0f), glm::radians(motionAngle), motionAxis); + CHECK_GL(glLoadMatrixf(glm::value_ptr(view * model))); + glColor3f(0.0f, 1.0f, 0.0f); + m_private->ocvLogo.at(1).draw_obj_smooth(); + + model = glm::rotate(glm::mat4(1.0f), glm::radians(motionAngle), motionAxis); CHECK_GL(glLoadMatrixf(glm::value_ptr(view * model))); + glColor3f(0.0f, 0.0f, 1.0f); + m_private->ocvLogo.at(2).draw_obj_smooth(); - glColor3f(0.9f, 0.6f, 0.1f); - m_private->monkey.draw_obj(); + motionAngle += motionDelta; } diff --git a/src/InputCtl.cpp b/src/InputCtl.cpp index 68444ae..07e0efd 100644 --- a/src/InputCtl.cpp +++ b/src/InputCtl.cpp @@ -26,7 +26,7 @@ static auto glfw_input_callback(FpFn fpFn) { struct CameraState { /* glm::mat4x4 transformation; */ - glm::vec3 eye = {0, 0, 5}; + glm::vec3 eye = {0, 0, 10}; glm::vec3 lookat = {0, 0, 0}; glm::vec3 up_vector = {0, 1, 0}; glm::vec3 keep_up_axis = {0, 1, 0}; diff --git a/src/OBJ.cpp b/src/OBJ.cpp index c9ddab5..e7faea6 100644 --- a/src/OBJ.cpp +++ b/src/OBJ.cpp @@ -6,7 +6,7 @@ #include #include -void OBJ::load_obj(std::string path) { +void OBJ::load_obj(std::string path, bool grouped) { std::ifstream file(path); if (!file.is_open()) { std::cerr << "Failed to open file: " << path << '\n'; @@ -14,6 +14,8 @@ void OBJ::load_obj(std::string path) { } std::string line; + unsigned int current_group = 0; + while (std::getline(file, line)) { if (line.substr(0, 2) == "v ") { std::istringstream s(line.substr(2)); @@ -33,8 +35,15 @@ void OBJ::load_obj(std::string path) { for (size_t i = 2; i < indices.size(); i++) { glm::uvec3 face = glm::uvec3(indices[0], indices[i - 1], indices[i]); faces.push_back(face); + + smoothing_groups[current_group].push_back(faces.size() - 1); } - } + } else if (line.substr(0, 7) == "#group ") { + std::istringstream s(line.substr(7)); + unsigned int group; + s >> group; + if (grouped) current_group = group; + } } file.close(); @@ -52,13 +61,26 @@ static glm::vec3 compute_normal(glm::vec3 a, glm::vec3 b, glm::vec3 c) { return glm::normalize(glm::cross(ab, ac)); } -void OBJ::draw_obj() { - glBegin(GL_TRIANGLES); +static glm::vec3 compute_contrib_normal(glm::vec3 v0, glm::vec3 v1, glm::vec3 v2, uint32_t vert) { + std::vector face = {v0, v1, v2}; + auto const& a = face.at(vert); + auto const& b = face.at((vert + 1) % 3); + auto const& c = face.at((vert + 2) % 3); + + glm::vec3 ab = b - a; + glm::vec3 ac = c - a; + glm::vec3 norm = glm::cross(ab, ac); + float contrib_w = asinf(static_cast(norm.length()) / (ab.length() * ac.length())); - for (auto face : faces) { - auto const &a = vertices.at(face[0]); - auto const &b = vertices.at(face[1]); - auto const &c = vertices.at(face[2]); + return glm::normalize(norm) * contrib_w; +} + +static void object_draw_flat_pass(OBJ const& obj) { + glBegin(GL_TRIANGLES); + for (auto face : obj.faces) { + auto const& a = obj.vertices.at(face[0]); + auto const& b = obj.vertices.at(face[1]); + auto const& c = obj.vertices.at(face[2]); glm::vec3 norm = compute_normal(a, b, c); glNormal3fv(glm::value_ptr(norm)); @@ -67,6 +89,55 @@ void OBJ::draw_obj() { glVertex3fv(glm::value_ptr(b)); glVertex3fv(glm::value_ptr(c)); } + CHECK_GL(glEnd()); +} + +static void normal_compute_pass(OBJ const& obj, std::vector const& face_indices, std::vector& vertex_normal_buffer) { + for (auto const& index : face_indices) { + auto const& face = obj.faces.at(index); + auto const& a = obj.vertices.at(face[0]); + auto const& b = obj.vertices.at(face[1]); + auto const& c = obj.vertices.at(face[2]); + + vertex_normal_buffer[face[0]] += compute_contrib_normal(a, b, c, 0); + vertex_normal_buffer[face[1]] += compute_contrib_normal(a, b, c, 1); + vertex_normal_buffer[face[2]] += compute_contrib_normal(a, b, c, 2); + } + for (auto& vn : vertex_normal_buffer) { + vn = glm::normalize(vn); + } +} +static void object_draw_smooth_pass(OBJ const& obj, std::vector const& face_indices, std::vector const& vertex_normal_buffer) { + glBegin(GL_TRIANGLES); + for (auto const& index : face_indices) { + auto const& face = obj.faces.at(index); + auto const& a = obj.vertices.at(face[0]); + auto const& b = obj.vertices.at(face[1]); + auto const& c = obj.vertices.at(face[2]); + + auto const& n0 = vertex_normal_buffer.at(face[0]); + auto const& n1 = vertex_normal_buffer.at(face[1]); + auto const& n2 = vertex_normal_buffer.at(face[2]); + + glNormal3fv(glm::value_ptr(n0)); + glVertex3fv(glm::value_ptr(a)); + glNormal3fv(glm::value_ptr(n1)); + glVertex3fv(glm::value_ptr(b)); + glNormal3fv(glm::value_ptr(n2)); + glVertex3fv(glm::value_ptr(c)); + } CHECK_GL(glEnd()); } + +void OBJ::draw_obj() { + object_draw_flat_pass(*this); +} + +void OBJ::draw_obj_smooth() { + for (auto const& [i, group] : smoothing_groups) { + std::vector vert_norm_buffer(vertices.size(), glm::vec3(0.0f)); + normal_compute_pass(*this, group, vert_norm_buffer); + object_draw_smooth_pass(*this, group, vert_norm_buffer); + } +} diff --git a/src/main.cpp b/src/main.cpp index 7f82a7c..849a32a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -40,7 +40,7 @@ int main() { } // Create main window - constexpr char title[] = "Example"; + constexpr char title[] = "Homework2: OpenCV 3D Logo"; GLFWwindow *window = glfwCreateWindow(1024, 768, title, NULL, NULL); // Test if window creation succeed