#include "lopenglprogram.h" #include "camera.h" #include #include #include #include #include #include #include #include #include #include #include #include #include class GLWidget { public: struct ViewPort { int width; int height; }; GLWidget(ViewPort port) : viewport_(port) , window_(nullptr, [](GLFWwindow* w){if(w) glfwDestroyWindow(w);}) , deinit_(false) { lastX_ = port.width / 2; lastY_ = port.height / 2; initGeometry(); } ~GLWidget() { if (!deinit_) { // OpenGL objects should be deleted while a valid context still exists. if (window_) { glfwMakeContextCurrent(window_.get()); } if (panelIbo_) { glDeleteBuffers(1, &panelIbo_); } if (panelVbo_) { glDeleteBuffers(1, &panelVbo_); } if (panelVao_) { glDeleteVertexArrays(1, &panelVao_); } if (bgVao_) { glDeleteVertexArrays(1, &bgVao_); } if (bgVbo_) { glDeleteBuffers(1, &bgVbo_); } if (heatmapVao_) { glDeleteVertexArrays(1, &heatmapVao_); } if (heatmapVbo_) { glDeleteBuffers(1, &heatmapVbo_); } if (heatmapIbo_) { glDeleteBuffers(1, &heatmapIbo_); } if (skirtVao_) { glDeleteVertexArrays(1, &skirtVao_); } if (skirtVbo_) { glDeleteBuffers(1, &skirtVbo_); } if (skirtIbo_) { glDeleteBuffers(1, &skirtIbo_); } deinit_ = true; } } void setViewPort(int width, int height) { viewport_ = {width, height}; } bool initGeometry() { if (!glfwInit()) { std::cout << "Failed to initialize GLFW\n"; return false; } glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); #if __APPLE__ glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); #endif window_.reset(glfwCreateWindow(viewport_.width, viewport_.height, "3dviewer", nullptr, nullptr)); if (!window_) { std::cout << "Failed to create GLFW window\n"; glfwTerminate(); return false; } glfwMakeContextCurrent(window_.get()); if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) { std::cout << "Failed to initialize GLAD\n"; return false; } glViewport(0, 0, viewport_.width, viewport_.height); glEnable(GL_DEPTH_TEST); glfwSetInputMode(window_.get(), GLFW_CURSOR, GLFW_CURSOR_DISABLED); glfwSetWindowUserPointer(window_.get(), this); glfwSetFramebufferSizeCallback(window_.get(), &GLWidget::framebufferSizeCallback); glfwSetCursorPosCallback(window_.get(), &GLWidget::mouseCallback); glfwSetScrollCallback(window_.get(), &GLWidget::scrollCallback); return true; } void setBgShaderPath(std::string vp, std::string fp) { bgVertShaderPath_ = vp; bgFragShaderPath_ = fp; } void setPanelShaderPath(std::string vp, std::string fp) { panelVertShaderPath_ = vp; panelFragShaderPath_ = fp; } private: void initBgGeometry() { if (bgVbo_) { glDeleteBuffers(1, &bgVbo_); bgVbo_ = 0; } if (bgVao_) { glDeleteVertexArrays(1, &bgVao_); bgVao_ = 0; } const float verts[] = { -1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0, -1.0, 1.0, 1.0, -1.0, 1.0, }; glGenVertexArrays(1, &bgVao_); glBindVertexArray(bgVao_); glGenBuffers(1, &bgVbo_); glBindBuffer(GL_ARRAY_BUFFER, bgVbo_); glBufferData(GL_ARRAY_BUFFER, sizeof(verts), verts, GL_STATIC_DRAW); glEnableVertexAttribArray(0); glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), (void*)0); glBindVertexArray(0); } void initPanelGeometry_() { if (panelVbo_) { glDeleteBuffers(1, &panelVbo_); panelVbo_ = 0; } if (panelVao_) { glDeleteVertexArrays(1, &panelVao_); panelVao_ = 0; } if (panelIbo_) { glDeleteBuffers(1, &panelIbo_); panelIbo_ = 0; } const float y = panelHeight_ * 0.5f; const float x = panelWidth_ * 0.5f; const float z = panelDepth_ * 0.5f; struct V { float x, y, z; float nx, ny, nz; }; const V verts[24] = { // 顶面 {-x, y, -z, 0, 1, 0}, {x, y, -z, 0, 1, 0}, {x, y, z, 0, 1, 0 }, {-x, y, z, 0, 1, 0}, // 前面 {-x, y, z, 0, 0, 1}, {x, y, z, 0, 0, 1}, {x, -y, z, 0, 0, 1}, {-x, -y, z, 0, 0, 1}, // 底面 {-x, -y, -z, 0, -1, 0}, {x, -y, -z, 0, -1, 0}, {x, -y, z, 0, -1, 0}, {-x, -y, z, 0, -1, 0}, // 后面 {-x, y, -z, 0, 0, -1}, {x, y, -z, 0, 0, -1}, {x, -y, -z, 0, 0, -1}, {-x, -y, -z, 0, 0, -1}, // 左面 {-x, y, z, -1, 0, 0}, {-x, y, -z, -1, 0, 0}, {-x, -y, -z, -1, 0, 0}, {-x, -y, z, -1, 0, 0}, // 右面 {x, y, -z, 1, 0, 0}, {x, y, z, 1, 0, 0}, {x, -y, z, 1, 0, 0}, {x, -y, -z, 1, 0, 0}, }; unsigned int idx[36] = { 0, 1, 2, 0, 2, 3, 4, 5, 6, 4, 6, 7, 8, 9, 10, 8, 10, 11, 12, 13, 14, 12, 14, 15, 16, 17, 18, 16, 18, 19, 20, 21, 22, 20, 22, 23, }; glGenVertexArrays(1, &panelVao_); glBindVertexArray(panelVao_); glGenBuffers(1, &panelVbo_); glBindBuffer(GL_ARRAY_BUFFER, panelVbo_); glBufferData(GL_ARRAY_BUFFER, sizeof(verts), verts, GL_STATIC_DRAW); glGenBuffers(1, &panelIbo_); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, panelIbo_); glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(idx), idx, GL_STATIC_DRAW); glEnableVertexAttribArray(0); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(V), (void*)0); glEnableVertexAttribArray(1); glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(V), (void*)(3 * sizeof(float))); glBindVertexArray(0); } void initHeatmapGeometry_() { if (heatmapIbo_) { glDeleteBuffers(1, &heatmapIbo_); heatmapIbo_ = 0; } if (heatmapVbo_) { glDeleteBuffers(1, &heatmapVbo_); heatmapVbo_ = 0; } if (heatmapVao_) { glDeleteVertexArrays(1, &heatmapVao_); heatmapVao_ = 0; } const float aspect = (panelWidth_ > 1e-6F) ? (panelHeight_ / panelWidth_) : 1.0F; const int max_res = 512; const int cols = std::max(2, std::min(dotcols_ * dotupscale_, max_res)); int rows = std::max(2, int(cols * aspect + 0.5F)); rows = std::max(2, std::min(rows, max_res)); heatmapcols_ = cols; heatmaprows_ = rows; const int vertex_count = cols * rows; std::vector verts; for (int i = 0; i < rows; ++i) { const float v = (rows > 1) ? (float)(i) / (float)(rows - 1) : 0.0F; // normailze to 0-1 const float y = (v - 0.5F) * panelHeight_; // -0.5 - 0.5 for (int j = 0; j < cols; ++j) { const float u = (cols > 1) ? (float)(j) / (float)(cols - 1) : 0.0F; const float x = (u - 0.5F) * panelWidth_; verts.push_back(x); verts.push_back(y); verts.push_back(0.0F); verts.push_back(u); verts.push_back(v); } } std::vector idx; idx.reserve((cols - 1) * (rows - 1) * 6); for (int r = 0; r < rows - 1; ++r) { for (int c = 0; c < cols - 1; ++c) { auto i0 = r * cols + c; auto i1 = r * cols + c + 1; auto i2 = (r + 1) * cols + c + 1; auto i3 = (r + 1) * cols + c; idx.push_back(i0); idx.push_back(i1); idx.push_back(i2); idx.push_back(i0); idx.push_back(i2); idx.push_back(i3); } } heatmapIndexCount = int(idx.size()); glGenVertexArrays(1, &heatmapVao_); glBindVertexArray(heatmapVao_); glGenBuffers(1, &heatmapVbo_); glBindBuffer(GL_ARRAY_BUFFER, heatmapVbo_); glBufferData(GL_ARRAY_BUFFER, verts.size() * sizeof(float), verts.data(), GL_STATIC_DRAW); glGenBuffers(1, &heatmapIbo_); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, heatmapIbo_); glBufferData(GL_ELEMENT_ARRAY_BUFFER, idx.size() * sizeof(unsigned int), idx.data(), GL_STATIC_DRAW); glEnableVertexAttribArray(0); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(0)); glEnableVertexAttribArray(1); glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3 * sizeof(float))); glBindVertexArray(0); } void initSkirtGeometry_() { if (skirtIbo_) { glDeleteBuffers(1, &skirtIbo_); skirtIbo_ = 0; } if (skirtVao_) { glDeleteVertexArrays(1, &skirtVao_); skirtVao_ = 0; } if (skirtVbo_) { glDeleteBuffers(1, &skirtVbo_); skirtVbo_ = 0; } skirtUVs.clear(); skirtNormals.clear(); const int cols = std::max(2, heatmapcols_); const int rows = std::max(2, heatmaprows_); auto push_border = [&](int c, int r, const glm::vec3& n) { const float u = (cols > 1) ? float(c) / float(cols - 1) : 0.0F; const float v = (rows > 1) ? float(r) / float(rows - 1) : 0.0F; skirtUVs.push_back(glm::vec2(u, v)); skirtNormals.push_back(n); }; for (int c = 0; c < cols; ++c) { push_border(c, 0, glm::vec3(0.0F, -1.0F, 0.0F)); } for (int r = 1; r < rows; ++r) { push_border(cols - 1, r, glm::vec3(1.0F, 0.0F, 0.0F)); } for (int c = cols -1; c >= 0; --c) { push_border(c, rows - 1, glm::vec3(0.0F, 1.0F, 0.0F)); } for (int r = rows - 2; r >= 1; --r) { push_border(0, r, glm::vec3(-1.0F, 0.0F, 0.0F)); } const int border_count = skirtUVs.size(); // TODO: fill // std::vector verts; // verts.assign(border_count * 2 * 8, 0.0F); skirtVertices.assign(border_count * 8 * 2, 0.0F); std::vector idx; idx.reserve(border_count * 6); for (int i = 0; i < border_count; ++i) { unsigned int next = (i + 1) % border_count; const unsigned int top0 = unsigned(i * 2); const unsigned int bot0 = unsigned(i * 2 + 1); const unsigned int top1 = unsigned(next * 2); const unsigned int bot1 = unsigned(next * 2 + 1); idx.push_back(top0); idx.push_back(top1); idx.push_back(bot1); idx.push_back(top0); idx.push_back(bot0); idx.push_back(bot1); } skirtIndexCount_ = static_cast(idx.size()); glGenVertexArrays(1, &skirtVao_); glBindVertexArray(skirtVao_); glGenBuffers(1, &skirtVbo_); glBindBuffer(GL_ARRAY_BUFFER, skirtVbo_); glBufferData(GL_ARRAY_BUFFER, skirtVertices.size() * sizeof(float), skirtVertices.data(), GL_STATIC_DRAW); glGenBuffers(1, &skirtIbo_); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, skirtIbo_); } bool initBgProgram() { auto vshader = std::make_unique(LOpenGLShader::ShaderType::Vertex); auto fshader = std::make_unique(LOpenGLShader::ShaderType::Fragment); if (!vshader->compileShaderFromFile(bgVertShaderPath_)) { std::cout << "Vertex shader compile failed: " << bgVertShaderPath_ << "\n" << vshader->Log() << std::endl; return false; } if (!fshader->compileShaderFromFile(bgFragShaderPath_)) { std::cout << "Fragment shader compile failed: " << bgFragShaderPath_ << "\n" << fshader->Log() << std::endl; return false; } bgProg_ = std::make_unique(); if (!bgProg_->addShader(std::move(vshader))) { std::cout << "Failed to attach vertex shader\n"; return false; } if (!bgProg_->addShader(std::move(fshader))) { std::cout << "Failed to attach fragment shader\n"; return false; } bool ret = bgProg_->Link(); if (!ret) { std::cout << "Failed to link background program\n"; return false; } return true; } bool initPanelProgram() { auto vshader = std::make_unique(LOpenGLShader::ShaderType::Vertex); auto fshader = std::make_unique(LOpenGLShader::ShaderType::Fragment); if (!vshader->compileShaderFromFile(panelVertShaderPath_)) { std::cout << "Vertex shader compile failed: " << panelVertShaderPath_ << "\n" << vshader->Log() << std::endl; return false; } if (!fshader->compileShaderFromFile(panelFragShaderPath_)) { std::cout << "Fragment shader compile failed: " << panelFragShaderPath_ << "\n" << fshader->Log() << std::endl; return false; } panelProg_ = std::make_unique(); if (!panelProg_->addShader(std::move(vshader))) { std::cout << "Failed to attach vertex shader\n"; return false; } if (!panelProg_->addShader(std::move(fshader))) { std::cout << "Failed to attach fragment shader\n"; return false; } bool ret = panelProg_->Link(); if (!ret) { std::cout << "Failed to link panel program\n"; return false; } return true; } bool initHeatmapProgram() { auto vshader = std::make_unique(LOpenGLShader::ShaderType::Vertex); auto fshader = std::make_unique(LOpenGLShader::ShaderType::Fragment); if (!vshader->compileShaderFromFile(heatmapVertShaderPath_)) { std::cout << "Vertex shader compile failed: " << heatmapVertShaderPath_ << "\n" << vshader->Log() << std::endl; return false; } if (!fshader->compileShaderFromFile(heatmapFragShaderPath_)) { std::cout << "Fragment shader compile failed: " << heatmapFragShaderPath_ << "\n" << fshader->Log() << std::endl; return false; } heatmapProg_ = std::make_unique(); if (!heatmapProg_->addShader(std::move(vshader))) { std::cout << "Failed to attach vertex shader\n"; return false; } if (!heatmapProg_->addShader(std::move(fshader))) { std::cout << "Failed to attach vertex shader\n"; return false; } bool ret = panelProg_->Link(); if (!ret) { std::cout << "Failed to link heatmap program\n"; return false; } return true; } void drawBg() { if (!bgProg_ || !bgVao_ || !bgVbo_) { std::cout << "check !bgProg_ || !bgVao_ || !bgVbo_ failed\n"; return; } bgProg_->Use(); bgProg_->setUniformValue("uViewport", glm::vec2(viewport_.width, viewport_.height)); bgProg_->setUniformValue("uMajorStep", 120.0f); bgProg_->setUniformValue("uMinorStep", 24.0f); bgProg_->setUniformValue("uLightMode", lightMode_); glDisable(GL_DEPTH_TEST); glBindVertexArray(bgVao_); glDrawArrays(GL_TRIANGLES, 0, 6); glBindVertexArray(0); glEnable(GL_DEPTH_TEST); } void drawPanel() { if (!panelProg_ || !panelVao_ || !panelVbo_ || !panelIbo_) { std:: cout << "check !panelProg_ || !panelVao || !panelVbo || !panelIbo\n"; return; } auto [model, view, projection] = getMVP(); panelProg_->Use(); panelProg_->setUniformValue("model", model); panelProg_->setUniformValue("view", view); panelProg_->setUniformValue("projection", projection); panelProg_->setUniformValue("uLightMode", lightMode_); glBindVertexArray(panelVao_); glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_INT, nullptr); glBindVertexArray(0); } std::tuple getMVP() { auto model = glm::mat4(1.0f); auto view = camera_.getViewMatrix(); auto projection = glm::perspective(glm::radians(camera_.Zoom()), (float)viewport_.width / (float)viewport_.height, 0.1f, 100.0f); return {model, view, projection}; } public: void eventLoop() { if (!window_) { return; } initBgGeometry(); if (!initBgProgram()) { return; } initPanelGeometry_(); if (!initPanelProgram()) { return; } while (!glfwWindowShouldClose(window_.get())) { const float now = static_cast(glfwGetTime()); const float deltaTime = now - lastFrame_; lastFrame_ = now; processInput(window_.get(), deltaTime); glClearColor(0.08f, 0.08f, 0.10f, 1.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); drawBg(); drawPanel(); glfwSwapBuffers(window_.get()); glfwPollEvents(); } } private: static void framebufferSizeCallback(GLFWwindow* window, int width, int height) { auto* self = static_cast(glfwGetWindowUserPointer(window)); if (!self) { return; } self->onFramebufferSize(width, height); } void onFramebufferSize(int width, int height) { viewport_ = {width, height}; glViewport(0, 0, width, height); } void processInput(GLFWwindow* window, float deltaTime) { if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) { glfwSetWindowShouldClose(window, true); } if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS) { camera_.keyboardCallback(Camera::FORWARD, deltaTime); } if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS) { camera_.keyboardCallback(Camera::BACKWARD, deltaTime); } if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS) { camera_.keyboardCallback(Camera::LEFT, deltaTime); } if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS) { camera_.keyboardCallback(Camera::RIGHT, deltaTime); } lightMode_ = glfwGetKey(window, GLFW_KEY_L) == GLFW_PRESS; } static void mouseCallback(GLFWwindow* window, double x, double y) { auto* self = static_cast(glfwGetWindowUserPointer(window)); if (!self) { return; } self->onMouseMove(x, y); } void onMouseMove(double x, double y) { float xpos = static_cast(x); float ypos = static_cast(y); if (camera_.firstMouse()) { lastX_ = x; lastY_ = y; camera_.triggleFirstMouse(); } float xoffset = xpos - lastX_; float yoffset = lastY_ - ypos; lastX_ = xpos; lastY_ = ypos; camera_.mouseMoveCallback(xoffset, yoffset); } static void scrollCallback(GLFWwindow* window, double xoffset, double yoffset) { auto* self = static_cast(glfwGetWindowUserPointer(window)); if (!self) { return; } self->onScrollRoll(xoffset, yoffset); } void onScrollRoll(double xoffset, double yoffset) { camera_.mouseScrollCallback(yoffset); } private: std::unique_ptr bgProg_; std::unique_ptr> window_; std::string bgFragShaderPath_; std::string bgVertShaderPath_; unsigned int bgVao_ = 0; unsigned int bgVbo_ = 0; std::unique_ptr panelProg_; std::string panelVertShaderPath_; std::string panelFragShaderPath_; unsigned int panelVao_ = 0; unsigned int panelVbo_ = 0; unsigned int panelIbo_ = 0; float panelWidth_ = 0.25; float panelHeight_ = 0.35; float panelDepth_ = 0.05; std::unique_ptr heatmapProg_; std::string heatmapVertShaderPath_; std::string heatmapFragShaderPath_; unsigned int heatmapVao_ = 0; unsigned int heatmapVbo_ = 0; unsigned int heatmapIbo_ = 0; int heatmapIndexCount = 0; std::unique_ptr skirtProg_; std::string skirtVertShaderPath_; std::string skirtFragShaderPath_; unsigned int skirtVao_ = 0; unsigned int skirtVbo_ = 0; unsigned int skirtIbo_ = 0; std::vector skirtUVs; std::vector skirtNormals; int skirtIndexCount_ = 0; std::vector skirtVertices; ViewPort viewport_{800, 600}; Camera camera_{glm::vec3(0.0F, 0.0F, 2.0F)}; bool firstMouse_ = true; float lastX_; float lastY_; float lastFrame_ = 0.0f; bool lightMode_ = false; int dotcols_ = 8; int dotrows_ = 12; int dotupscale_ = 100; int heatmapcols_; int heatmaprows_; bool deinit_; }; int main() { { GLWidget glw({800, 600}); glw.setBgShaderPath("../shader/bg.vert", "../shader/bg.frag"); glw.setPanelShaderPath("../shader/panel.vert", "../shader/panel.frag"); glw.eventLoop(); } glfwTerminate(); return 0; }