#ifdef _WIN32 #define NOMINMAX #define WIN32_LEAN_AND_MEAN #endif #include #include #include #include #include #include #include #include #include #define GLFW_INCLUDE_NONE #include #ifdef _WIN32 #include #include #define GLFW_EXPOSE_NATIVE_WIN32 #include #endif #include #include #include #include "imgui/imgui.h" #include "imgui/imgui_impl_glfw.h" #include "imgui/imgui_impl_opengl3.h" #include #include #include #include #include #define STB_IMAGE_WRITE_IMPLEMENTATION #include "stb_image_write.h" #include "render_manager.h" #include "../skill/ISkill.h" #include "../skill/SkillRegistry.h" #include "../skill/AutoRotateSkill.h" #include "../skill/ResetCameraSkill.h" #include "../skill/ZoomInSkill.h" #include "../skill/RotateSkill.h" #include "../skill/OptimizeViewSkill.h" #include "../skill/MeasureModelSkill.h" #include "../skill/AnalyzeGeometrySkill.h" namespace hhb { namespace render { // 顶点着色器源代码 const char* vertexShaderSource = "\n" "#version 330\n" "layout (location = 0) in vec3 aPos;\n" "layout (location = 1) in vec3 aNormal;\n" "\n" "uniform mat4 model;\n" "uniform mat4 view;\n" "uniform mat4 projection;\n" "\n" "out vec3 Normal;\n" "out vec3 FragPos;\n" "\n" "void main()\n" "{\n" " FragPos = vec3(model * vec4(aPos, 1.0));\n" " Normal = mat3(transpose(inverse(model))) * aNormal;\n" " gl_Position = projection * view * model * vec4(aPos, 1.0);\n" "}\n"; // 片元着色器源代码 const char* fragmentShaderSource = "\n" "#version 330\n" "out vec4 FragColor;\n" "\n" "in vec3 Normal;\n" "in vec3 FragPos;\n" "\n" "uniform vec3 lightPos;\n" "uniform vec3 viewPos;\n" "uniform vec3 objectColor;\n" "uniform vec3 lightColor;\n" "uniform float metallic;\n" "uniform float roughness;\n" "uniform bool isSelected;\n" "uniform vec3 highlightColor;\n" "\n" "// 法线分布函数 (Trowbridge-Reitz GGX)\n" "float DistributionGGX(vec3 N, vec3 H, float roughness)\n" "{\n" " float a = roughness * roughness;\n" " float a2 = a * a;\n" " float NdotH = max(dot(N, H), 0.0);\n" " float NdotH2 = NdotH * NdotH;\n" " \n" " float nom = a2;\n" " float denom = (NdotH2 * (a2 - 1.0) + 1.0);\n" " denom = 3.1415926535 * denom * denom;\n" " \n" " return nom / denom;\n" "}\n" "\n" "// 几何函数 (Schlick-GGX)\n" "float GeometrySchlickGGX(float NdotV, float roughness)\n" "{\n" " float r = (roughness + 1.0);\n" " float k = (r * r) / 8.0;\n" " \n" " float nom = NdotV;\n" " float denom = NdotV * (1.0 - k) + k;\n" " \n" " return nom / denom;\n" "}\n" "\n" "// 几何函数 (Smith)\n" "float GeometrySmith(vec3 N, vec3 V, vec3 L, float roughness)\n" "{\n" " float NdotV = max(dot(N, V), 0.0);\n" " float NdotL = max(dot(N, L), 0.0);\n" " float ggx2 = GeometrySchlickGGX(NdotV, roughness);\n" " float ggx1 = GeometrySchlickGGX(NdotL, roughness);\n" " \n" " return ggx1 * ggx2;\n" "}\n" "\n" "// 菲涅尔方程 (Fresnel-Schlick)\n" "vec3 FresnelSchlick(float cosTheta, vec3 F0)\n" "{\n" " return F0 + (1.0 - F0) * pow(clamp(1.0 - cosTheta, 0.0, 1.0), 5.0);\n" "}\n" "\n" "void main()\n" "{\n" " if (isSelected) {\n" " FragColor = vec4(highlightColor, 1.0);\n" " return;\n" " }\n" " \n" " vec3 N = normalize(Normal);\n" " // 对于没有法线的模型或者法线错误的情况,如果面向背对,翻转法线\n" " if (!gl_FrontFacing) N = -N;\n" " \n" " vec3 V = normalize(viewPos - FragPos);\n" " \n" " vec3 F0 = vec3(0.04); \n" " F0 = mix(F0, objectColor, metallic);\n" " \n" " // 反射率方程\n" " vec3 Lo = vec3(0.0);\n" " \n" " // 计算每个光源的辐射度 (这里只有一个点光源)\n" " vec3 L = normalize(lightPos - FragPos);\n" " vec3 H = normalize(V + L);\n" " \n" " // 衰减\n" " float distance = length(lightPos - FragPos);\n" " float attenuation = 1.0 / (distance * distance * 0.01 + 1.0); // 调整了衰减系数,防止模型全黑\n" " vec3 radiance = lightColor * attenuation * 500.0; // 增加了光源强度\n" " \n" " // Cook-Torrance BRDF\n" " float NDF = DistributionGGX(N, H, roughness);\n" " float G = GeometrySmith(N, V, L, roughness);\n" " vec3 F = FresnelSchlick(max(dot(H, V), 0.0), F0);\n" " \n" " vec3 numerator = NDF * G * F;\n" " float denominator = 4.0 * max(dot(N, V), 0.0) * max(dot(N, L), 0.0) + 0.0001;\n" " vec3 specular = numerator / denominator;\n" " \n" " vec3 kS = F;\n" " vec3 kD = vec3(1.0) - kS;\n" " kD *= 1.0 - metallic;\n" " \n" " float NdotL = max(dot(N, L), 0.0);\n" " \n" " Lo += (kD * objectColor / 3.1415926535 + specular) * radiance * NdotL;\n" " \n" " // 环境光 (基础的)\n" " vec3 ambient = vec3(0.2) * objectColor * (1.0 - metallic);\n" " \n" " vec3 color = ambient + Lo;\n" " \n" " // HDR色调映射\n" " color = color / (color + vec3(1.0));\n" " // Gamma校正\n" " color = pow(color, vec3(1.0/2.2)); \n" " \n" " FragColor = vec4(color, 1.0);\n" "}\n"; const char* labelVertexShaderSource = "\n" "#version 330\n" "layout (location = 0) in vec3 aPos;\n" "layout (location = 1) in vec3 aLabelColor;\n" "\n" "uniform mat4 model;\n" "uniform mat4 view;\n" "uniform mat4 projection;\n" "\n" "out vec3 LabelColor;\n" "\n" "void main()\n" "{\n" " LabelColor = aLabelColor;\n" " gl_Position = projection * view * model * vec4(aPos, 1.0);\n" "}\n"; const char* labelFragmentShaderSource = "\n" "#version 330\n" "out vec4 FragColor;\n" "\n" "in vec3 LabelColor;\n" "\n" "void main()\n" "{\n" " FragColor = vec4(LabelColor, 1.0);\n" "}\n"; RenderManager::RenderManager(int width, int height, const std::string& title) : width(width), height(height), title(title), window(nullptr), VAO(0), VBO(0), shaderProgram(0), labelVAO(0), labelVBO(0), labelShaderProgram(0), labelModeActive(false), faceCategoriesComputed(false), triangleCount(0), trianglePool(nullptr), zoom(1.0f), frameCount(0), lastFpsUpdate(0.0f), fps(0.0f), metallic(0.5f), roughness(0.5f), loadTime(0.0f), memoryUsage(0), selectedTriangle(nullptr), selectedTriangleIndex(-1), pickTime(0.0f), showBVH(false), showHighlight(false), currentHighlightType(HighlightType::None), highlightCalculated(false), highlightBlinkTimer_(0.0f), highlightBlinkState_(true) { commandDispatcher.start(8080); cameraPosition[0] = 0.0f; cameraPosition[1] = 0.0f; cameraPosition[2] = 5.0f; // 初始化相机旋转 cameraRotation[0] = 0.0f; cameraRotation[1] = 0.0f; // 初始化时间 lastFrame = std::chrono::steady_clock::now(); deltaTime = 0.0f; // 自动旋转初始化 autoRotate = false; autoRotateSpeed = 0.5f; automationMode = false; // 相机动画初始化 cameraAnimating = false; targetCameraPos[0] = targetCameraPos[1] = targetCameraPos[2] = 0.0f; targetCameraRot[0] = targetCameraRot[1] = 0.0f; cameraPosStart[0] = cameraPosStart[1] = cameraPosStart[2] = 0.0f; cameraRotStart[0] = cameraRotStart[1] = 0.0f; targetZoom = 1.0f; zoomStart = 1.0f; cameraAnimDuration = 0.5f; // 初始化文件路径缓冲区 memset(filePathBuffer, 0, sizeof(filePathBuffer)); } RenderManager::~RenderManager() { // 等待计算线程完成 if (calculationThread.joinable()) { calculationThread.join(); } // 清理对象池 if (trianglePool) { delete trianglePool; trianglePool = nullptr; } // 清理ImGui shutdownImGui(); // 清理OpenGL资源 glDeleteVertexArrays(1, &VAO); glDeleteBuffers(1, &VBO); glDeleteProgram(shaderProgram); glDeleteVertexArrays(1, &labelVAO); glDeleteBuffers(1, &labelVBO); glDeleteProgram(labelShaderProgram); // 清理GLFW if (window) { glfwDestroyWindow(window); } glfwTerminate(); } void RenderManager::initSkills() { // 获取技能注册表实例 auto& registry = skill::SkillRegistry::getInstance(); // 注册技能 registry.registerSkill("auto_rotate", std::make_unique(*this)); registry.registerSkill("reset_camera", std::make_unique(*this)); registry.registerSkill("zoom_in", std::make_unique(*this)); registry.registerSkill("rotate", std::make_unique(*this)); registry.registerSkill("optimize_view", std::make_unique(*this)); registry.registerSkill("measure_model", std::make_unique(*this)); registry.registerSkill("analyze_geometry", std::make_unique(*this)); std::cout << "Skills initialized successfully" << std::endl; } bool RenderManager::initialize() { // 初始化GLFW if (!glfwInit()) { std::cerr << "Failed to initialize GLFW" << std::endl; return false; } // 设置GLFW窗口属性 glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // 创建窗口 window = glfwCreateWindow(width, height, title.c_str(), nullptr, nullptr); if (!window) { std::cerr << "Failed to create GLFW window" << std::endl; glfwTerminate(); return false; } // 设置当前上下文 glfwMakeContextCurrent(window); // 初始化 GLAD if (!gladLoadGL((GLADloadfunc)glfwGetProcAddress)) { std::cout << "Failed to initialize GLAD" << std::endl; return false; } // 设置视口 glViewport(0, 0, width, height); // 启用深度测试 glEnable(GL_DEPTH_TEST); // 初始化着色器 if (!initShaders()) { return false; } // 初始化标签模式着色器 if (!initLabelShaders()) { std::cerr << "Warning: Label shader initialization failed" << std::endl; } // 初始化缓冲区 if (!initBuffers()) { return false; } // 初始化ImGui initImGui(); initSkills(); // 初始化具身智能 Agent:将几何分析能力注册为 LLM 可调用的工具 // 设置结果回调,当工具执行完成后自动更新 OpenGL 高亮 embodiedAgent_.setResultCallback([this](const std::vector& indices, HighlightType type, const std::string& desc) { onToolResult(indices, type, desc); }); // 默认配置(用户可通过 UI 修改) embodiedAgent_.initialize( "https://api.openai.com", "", "gpt-4o-mini", &geometryAPI ); return true; } void RenderManager::render() { if (!automationMode) { ImGui_ImplOpenGL3_NewFrame(); ImGui_ImplGlfw_NewFrame(); ImGui::NewFrame(); updateImGui(); } // 处理来自网络的命令 while (commandDispatcher.hasCommand()) { hhb::core::Command cmd = commandDispatcher.getCommand(); std::cout << "Processing command: " << cmd.action << " with value: " << cmd.value << std::endl; if (trianglePool) { if (calculationThread.joinable()) { calculationThread.join(); } calculationThread = std::thread([this, cmd]() { std::cout << "Starting analysis in background thread..." << std::endl; std::vector result_parts; HighlightType htype = HighlightType::None; std::string desc; if (cmd.action == "check_thickness") { result_parts = geometryAPI.getThinParts(static_cast(cmd.value)); htype = HighlightType::ThinParts; desc = "薄弱部位 (厚度<" + std::to_string(static_cast(cmd.value)) + "mm)"; std::cout << "Found " << result_parts.size() << " thin parts" << std::endl; } else if (cmd.action == "find_curved_surfaces") { result_parts = geometryAPI.getCurvedSurfaces(static_cast(cmd.value)); htype = HighlightType::CurvedSurfaces; desc = "曲面/曲线区域 (曲率>" + std::to_string(static_cast(cmd.value)) + ")"; std::cout << "Found " << result_parts.size() << " curved surface triangles" << std::endl; } else if (cmd.action == "find_sharp_edges") { result_parts = geometryAPI.getSharpEdges(static_cast(cmd.value)); htype = HighlightType::SharpEdges; desc = "锐角/棱边区域 (角度>" + std::to_string(static_cast(cmd.value)) + "°)"; std::cout << "Found " << result_parts.size() << " sharp edge triangles" << std::endl; } else if (cmd.action == "find_flat_surfaces") { result_parts = geometryAPI.getFlatSurfaces(static_cast(cmd.value)); htype = HighlightType::FlatSurfaces; desc = "平面区域 (平坦度<" + std::to_string(static_cast(cmd.value)) + ")"; std::cout << "Found " << result_parts.size() << " flat surface triangles" << std::endl; } else { std::cout << "Unknown command action: " << cmd.action << std::endl; return; } std::unordered_map ptrToIndex; int idx = 0; trianglePool->for_each([&](hhb::core::Triangle* tri) { ptrToIndex[tri] = idx; idx++; }); std::vector indices; for (const auto& tri : result_parts) { auto it = ptrToIndex.find(tri); if (it != ptrToIndex.end()) { indices.push_back(it->second); } } std::cout << "Matched " << indices.size() << " / " << result_parts.size() << " triangles to VBO indices" << std::endl; { std::lock_guard lock(highlightMutex); newHighlightIndices = std::move(indices); currentHighlightType = htype; lastAnalysisDesc = desc; } highlightCalculated = true; std::cout << "Analysis completed: " << desc << std::endl; }); } } if (highlightCalculated) { { std::lock_guard lock(highlightMutex); highlightIndices = std::move(newHighlightIndices); } showHighlight = true; highlightCalculated = false; std::cout << "Updated highlight indices: " << highlightIndices.size() << " parts (" << lastAnalysisDesc << ")" << std::endl; } // 检查具身智能 Agent 的异步结果 if (embodiedAgent_.isProcessing()) { embodiedAgent_.checkPendingResult(); } // 开启深度测试与背景清理 glEnable(GL_DEPTH_TEST); glClearColor(0.1f, 0.1f, 0.1f, 1.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // 使用着色器程序 glUseProgram(shaderProgram); // 计算 deltaTime auto currentFrame = std::chrono::steady_clock::now(); deltaTime = std::chrono::duration(currentFrame - lastFrame).count(); lastFrame = currentFrame; // 自动旋转逻辑 if (autoRotate) { cameraRotation[1] += autoRotateSpeed * deltaTime; if (cameraRotation[1] > 360.0f) { cameraRotation[1] -= 360.0f; } } // 相机平滑动画更新 if (cameraAnimating) { auto now = std::chrono::steady_clock::now(); float elapsed = std::chrono::duration(now - cameraAnimStart).count(); float t = std::min(elapsed / cameraAnimDuration, 1.0f); // 使用 smoothstep 进行平滑插值 t = t * t * (3.0f - 2.0f * t); // 插值相机位置 cameraPosition[0] = cameraPosStart[0] + (targetCameraPos[0] - cameraPosStart[0]) * t; cameraPosition[1] = cameraPosStart[1] + (targetCameraPos[1] - cameraPosStart[1]) * t; cameraPosition[2] = cameraPosStart[2] + (targetCameraPos[2] - cameraPosStart[2]) * t; // 插值相机旋转 cameraRotation[0] = cameraRotStart[0] + (targetCameraRot[0] - cameraRotStart[0]) * t; cameraRotation[1] = cameraRotStart[1] + (targetCameraRot[1] - cameraRotStart[1]) * t; // 插值缩放 zoom = zoomStart + (targetZoom - zoomStart) * t; // 检查动画是否完成 if (t >= 1.0f) { cameraAnimating = false; cameraPosition[0] = targetCameraPos[0]; cameraPosition[1] = targetCameraPos[1]; cameraPosition[2] = targetCameraPos[2]; cameraRotation[0] = targetCameraRot[0]; cameraRotation[1] = targetCameraRot[1]; zoom = targetZoom; } } // 更新 FPS updateFPS(); // 设置模型矩阵 - 简单的单位矩阵 float model[16] = { 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f }; GLint modelLoc = glGetUniformLocation(shaderProgram, "model"); glUniformMatrix4fv(modelLoc, 1, GL_FALSE, model); // 设置视图矩阵 (CAD Orbit Camera) float distance = 5.0f / zoom; // 我们围绕 target (cameraPosition) 进行旋转 float cx = cosf(cameraRotation[0]); float sx = sinf(cameraRotation[0]); float cy = cosf(cameraRotation[1]); float sy = sinf(cameraRotation[1]); // 相机在世界坐标系下的实际位置 // x = target_x + dist * cos(pitch) * sin(yaw) // y = target_y + dist * sin(pitch) // z = target_z + dist * cos(pitch) * cos(yaw) float camPosX = cameraPosition[0] + distance * cx * sy; float camPosY = cameraPosition[1] + distance * sx; float camPosZ = cameraPosition[2] + distance * cx * cy; // View矩阵(列主序) float view[16] = { cy, -sx*sy, -cx*sy, 0.0f, 0.0f, cx, -sx, 0.0f, sy, sx*cy, cx*cy, 0.0f, 0.0f, 0.0f, -distance, 1.0f }; // 还要把 target 平移加进去,由于我们在原点旋转后再平移,其实 View 矩阵是: // View = LookAt(camPos, target, up) // 根据标准推导,旋转矩阵不变,平移部分为 -R * camPos // R 是前 3x3 转置,上面已经是 R 了 view[12] = -(view[0]*camPosX + view[4]*camPosY + view[8]*camPosZ); view[13] = -(view[1]*camPosX + view[5]*camPosY + view[9]*camPosZ); view[14] = -(view[2]*camPosX + view[6]*camPosY + view[10]*camPosZ); GLint viewLoc = glGetUniformLocation(shaderProgram, "view"); glUniformMatrix4fv(viewLoc, 1, GL_FALSE, view); // 设置投影矩阵 float aspect = (float)width / (float)height; float fov = 45.0f; float nearPlane = 0.1f; float farPlane = 1000.0f; float f = 1.0f / tanf(fov * 0.5f * 3.1415926535f / 180.0f); float projection[16] = { f / aspect, 0.0f, 0.0f, 0.0f, 0.0f, f, 0.0f, 0.0f, 0.0f, 0.0f, (farPlane + nearPlane) / (nearPlane - farPlane), -1.0f, 0.0f, 0.0f, (2.0f * farPlane * nearPlane) / (nearPlane - farPlane), 0.0f }; GLint projectionLoc = glGetUniformLocation(shaderProgram, "projection"); glUniformMatrix4fv(projectionLoc, 1, GL_FALSE, projection); // 设置光源位置 float lightPos[3] = { camPosX, camPosY + 10.0f, camPosZ }; GLint lightPosLoc = glGetUniformLocation(shaderProgram, "lightPos"); glUniform3fv(lightPosLoc, 1, lightPos); // 设置观察位置 float camPosArr[3] = { camPosX, camPosY, camPosZ }; GLint viewPosLoc = glGetUniformLocation(shaderProgram, "viewPos"); glUniform3fv(viewPosLoc, 1, camPosArr); // 设置物体颜色 float objectColor[3] = { 0.8f, 0.8f, 0.8f }; // 调亮一点的默认颜色 GLint objectColorLoc = glGetUniformLocation(shaderProgram, "objectColor"); glUniform3fv(objectColorLoc, 1, objectColor); // 设置光源颜色 float lightColor[3] = { 1.0f, 1.0f, 1.0f }; GLint lightColorLoc = glGetUniformLocation(shaderProgram, "lightColor"); glUniform3fv(lightColorLoc, 1, lightColor); // 设置高亮参数 GLint isSelectedLoc = glGetUniformLocation(shaderProgram, "isSelected"); GLint highlightColorLoc = glGetUniformLocation(shaderProgram, "highlightColor"); float highlightColor[3] = {1.0f, 0.5f, 0.0f}; // 橙色高亮 // 绑定VAO glBindVertexArray(VAO); // 绘制普通三角形 if (isSelectedLoc != -1) { glUniform1i(isSelectedLoc, GL_FALSE); } // 设置PBR参数,保证每一帧实时更新到Shader GLint metallicLoc = glGetUniformLocation(shaderProgram, "metallic"); if (metallicLoc != -1) { glUniform1f(metallicLoc, metallic); } GLint roughnessLoc = glGetUniformLocation(shaderProgram, "roughness"); if (roughnessLoc != -1) { glUniform1f(roughnessLoc, roughness); } if (triangleCount > 0) { glDrawArrays(GL_TRIANGLES, 0, triangleCount * 3); } // 如果有选中的三角形,高亮显示 if (selectedTriangleIndex >= 0 && isSelectedLoc != -1 && highlightColorLoc != -1) { glUniform1i(isSelectedLoc, GL_TRUE); glUniform3fv(highlightColorLoc, 1, highlightColor); // 只绘制选中的三角形 glDrawArrays(GL_TRIANGLES, selectedTriangleIndex * 3, 3); } highlightParts(); // 解绑VAO glBindVertexArray(0); // 渲染BVH可视化(如果开启) if (showBVH) { renderBVH(); } if (!automationMode) { ImGui::Render(); renderImGui(); } // 更新FPS updateFPS(); } void RenderManager::processInput() { // 检查窗口是否需要关闭 if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) { glfwSetWindowShouldClose(window, true); } ImGuiIO& io = ImGui::GetIO(); // 处理鼠标输入 static bool firstMouse = true; static float lastX = width / 2.0f; static float lastY = height / 2.0f; static bool leftButtonPressed = false; double xpos, ypos; glfwGetCursorPos(window, &xpos, &ypos); if (firstMouse) { lastX = xpos; lastY = ypos; firstMouse = false; } float xoffset = xpos - lastX; float yoffset = lastY - ypos; lastX = xpos; lastY = ypos; // 只有当ImGui不需要鼠标时才处理3D场景交互 if (!io.WantCaptureMouse) { // 处理鼠标左键旋转(仅在拖动时,不是点击时) if (glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_LEFT) == GLFW_PRESS) { // 检测是否是新的点击(用于拾取) if (!leftButtonPressed) { // 鼠标左键按下,处理点击事件(拾取) handleMouseClick(xpos, ypos); leftButtonPressed = true; } // 旋转相机 float sensitivity = 0.005f; xoffset *= sensitivity; yoffset *= sensitivity; cameraRotation[1] += xoffset; cameraRotation[0] += yoffset; // 限制视角范围 if (cameraRotation[0] > 1.57079632679f) { cameraRotation[0] = 1.57079632679f; } if (cameraRotation[0] < -1.57079632679f) { cameraRotation[0] = -1.57079632679f; } } else { leftButtonPressed = false; } // 处理鼠标中键平移 if (glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_MIDDLE) == GLFW_PRESS) { float panSensitivity = 0.002f * zoom; // 计算相机右向量和上向量以进行平移 float cos_rot_x = cosf(cameraRotation[0]); float sin_rot_x = sinf(cameraRotation[0]); float cos_rot_y = cosf(cameraRotation[1]); float sin_rot_y = sinf(cameraRotation[1]); // 相机坐标系下的右向量 (1,0,0) 转换到世界坐标系 float rightX = cos_rot_y; float rightY = 0.0f; float rightZ = -sin_rot_y; // 相机坐标系下的上向量 (0,1,0) 转换到世界坐标系 float upX = sin_rot_y * sin_rot_x; float upY = cos_rot_x; float upZ = cos_rot_y * sin_rot_x; // 移动相机 cameraPosition[0] -= rightX * xoffset * panSensitivity; cameraPosition[1] -= rightY * xoffset * panSensitivity; cameraPosition[2] -= rightZ * xoffset * panSensitivity; cameraPosition[0] += upX * yoffset * panSensitivity; cameraPosition[1] += upY * yoffset * panSensitivity; cameraPosition[2] += upZ * yoffset * panSensitivity; } } else { // ImGui正在使用鼠标,重置按钮状态 leftButtonPressed = false; } // 设置窗口用户指针(用于滚轮回调) glfwSetWindowUserPointer(window, this); } bool RenderManager::shouldClose() { return glfwWindowShouldClose(window); } void RenderManager::swapBuffers() { glfwSwapBuffers(window); glfwPollEvents(); // 计算deltaTime auto currentFrame = std::chrono::steady_clock::now(); deltaTime = std::chrono::duration(currentFrame - lastFrame).count(); lastFrame = currentFrame; } void RenderManager::setTriangles(hhb::core::ObjectPool& pool) { // 保存三角形池的指针 trianglePool = &pool; // 更新顶点数据 updateVertexData(pool); // 更新缓冲区 glBindBuffer(GL_ARRAY_BUFFER, VBO); glBufferData(GL_ARRAY_BUFFER, vertexData.size() * sizeof(float), vertexData.data(), GL_STATIC_DRAW); // 设置顶点属性指针 // glVertexAttribPointer参数定义: // 第一个参数:属性索引,对应着色器中的layout(location = 0) // 第二个参数:属性大小,这里是3个顶点数(x, y, z) // 第三个参数:数据类型,这里是GL_FLOAT // 第四个参数:是否标准化,这里是GL_FALSE // 第五个参数:步长,每个顶点的大小(位置3个顶点数 + 法线3个顶点数 = 6个顶点数) // 第六个参数:偏移量,从缓冲区开始的偏移量 glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0); glEnableVertexAttribArray(0); // 设置法线向量属性指针 glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3 * sizeof(float))); glEnableVertexAttribArray(1); glBindBuffer(GL_ARRAY_BUFFER, 0); // 构建BVH树 buildBVH(); } GLuint RenderManager::loadShader(GLenum type, const char* source) { GLuint shader = glCreateShader(type); glShaderSource(shader, 1, (const GLchar**)&source, nullptr); glCompileShader(shader); // 检查编译错误 int success; char infoLog[512]; glGetShaderiv(shader, GL_COMPILE_STATUS, &success); if (!success) { glGetShaderInfoLog(shader, 512, nullptr, (GLchar*)infoLog); std::cerr << "Shader compilation error: " << infoLog << std::endl; return 0; } return shader; } GLuint RenderManager::createShaderProgram(const char* vertexSource, const char* fragmentSource) { GLuint vertexShader = loadShader(GL_VERTEX_SHADER, vertexSource); GLuint fragmentShader = loadShader(GL_FRAGMENT_SHADER, fragmentSource); if (!vertexShader || !fragmentShader) { return 0; } GLuint program = glCreateProgram(); glAttachShader(program, vertexShader); glAttachShader(program, fragmentShader); glLinkProgram(program); // 检查链接错误 int success; char infoLog[512]; glGetProgramiv(program, GL_LINK_STATUS, &success); if (!success) { glGetProgramInfoLog(program, 512, nullptr, (GLchar*)infoLog); std::cerr << "Program linking error: " << infoLog << std::endl; return 0; } // 删除着色器,因为它们已经链接到程序中 glDeleteShader(vertexShader); glDeleteShader(fragmentShader); return program; } bool RenderManager::initShaders() { shaderProgram = createShaderProgram(vertexShaderSource, fragmentShaderSource); if (!shaderProgram) { return false; } return true; } bool RenderManager::initLabelShaders() { labelShaderProgram = createShaderProgram(labelVertexShaderSource, labelFragmentShaderSource); if (!labelShaderProgram) { std::cerr << "Failed to create label shader program" << std::endl; return false; } glGenVertexArrays(1, &labelVAO); glGenBuffers(1, &labelVBO); glBindVertexArray(labelVAO); glBindBuffer(GL_ARRAY_BUFFER, labelVBO); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0); glEnableVertexAttribArray(0); glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3 * sizeof(float))); glEnableVertexAttribArray(1); glBindBuffer(GL_ARRAY_BUFFER, 0); glBindVertexArray(0); return true; } void RenderManager::categoryToColor(int categoryId, float* outR, float* outG, float* outB) { switch (categoryId) { case 0: *outR = 0.50f; *outG = 0.50f; *outB = 0.50f; break; case 1: *outR = 0.00f; *outG = 0.00f; *outB = 1.00f; break; case 2: *outR = 0.00f; *outG = 1.00f; *outB = 0.00f; break; case 3: *outR = 1.00f; *outG = 0.00f; *outB = 0.00f; break; case 4: *outR = 1.00f; *outG = 1.00f; *outB = 0.00f; break; case 5: *outR = 1.00f; *outG = 0.00f; *outB = 1.00f; break; case 6: *outR = 0.00f; *outG = 1.00f; *outB = 1.00f; break; case 7: *outR = 1.00f; *outG = 0.50f; *outB = 0.00f; break; case 8: *outR = 0.50f; *outG = 0.00f; *outB = 1.00f; break; case 9: *outR = 0.00f; *outG = 0.50f; *outB = 1.00f; break; case 10: *outR = 0.80f; *outG = 0.80f; *outB = 0.00f; break; case 11: *outR = 0.00f; *outG = 0.80f; *outB = 0.40f; break; default: *outR = 1.00f; *outG = 1.00f; *outB = 1.00f; break; } } void RenderManager::computeFaceCategories() { if (!trianglePool || triangleCount == 0) { faceCategoryIds.clear(); faceCategoriesComputed = false; return; } faceCategoryIds.resize(triangleCount, 0); trianglePool->for_each([&](hhb::core::Triangle* tri) { static size_t idx = 0; if (idx >= triangleCount) { idx = 0; } float nx = tri->normal[0]; float ny = tri->normal[1]; float nz = tri->normal[2]; float nLen = std::sqrt(nx * nx + ny * ny + nz * nz); if (nLen > 0.0001f) { nx /= nLen; ny /= nLen; nz /= nLen; } float absNx = std::abs(nx); float absNy = std::abs(ny); float absNz = std::abs(nz); float maxComp = std::max({absNx, absNy, absNz}); float e1[3] = {tri->vertex2[0] - tri->vertex1[0], tri->vertex2[1] - tri->vertex1[1], tri->vertex2[2] - tri->vertex1[2]}; float e2[3] = {tri->vertex3[0] - tri->vertex1[0], tri->vertex3[1] - tri->vertex1[1], tri->vertex3[2] - tri->vertex1[2]}; float cross[3] = { e1[1] * e2[2] - e1[2] * e2[1], e1[2] * e2[0] - e1[0] * e2[2], e1[0] * e2[1] - e1[1] * e2[0] }; float area = 0.5f * std::sqrt(cross[0]*cross[0] + cross[1]*cross[1] + cross[2]*cross[2]); int category = 0; if (area < 0.00001f) { category = 7; } else if (maxComp > 0.98f) { if (absNy > 0.98f) category = 1; else if (absNx > 0.98f) category = 2; else category = 3; } else if (maxComp > 0.85f) { if (absNy >= absNx && absNy >= absNz) category = 4; else if (absNx >= absNz) category = 5; else category = 6; } else { category = 0; } faceCategoryIds[idx] = category; idx++; }); faceCategoriesComputed = true; printf("[LabelMode] Face categories computed: %zu triangles classified\n", faceCategoryIds.size()); fflush(stdout); } void RenderManager::computeCurvature() { if (!trianglePool || triangleCount == 0) { faceGaussianCurvature.clear(); faceMeanCurvature.clear(); faceCurvatureIds.clear(); curvatureComputed = false; return; } faceGaussianCurvature.resize(triangleCount, 0.0f); faceMeanCurvature.resize(triangleCount, 0.0f); faceCurvatureIds.resize(triangleCount, 0); struct VertexInfo { float normal[3] = {0, 0, 0}; float areaSum = 0.0f; int valence = 0; float pos[3] = {0, 0, 0}; }; std::unordered_map vertexMap; auto vertexKey = [](float x, float y, float z) -> int64_t { int ix = (int)(x * 10000.0f); int iy = (int)(y * 10000.0f); int iz = (int)(z * 10000.0f); return ((int64_t)(ix & 0xFFFFF) << 40) | ((int64_t)(iy & 0xFFFFF) << 20) | (int64_t)(iz & 0xFFFFF); }; trianglePool->for_each([&](hhb::core::Triangle* tri) { float e1[3] = {tri->vertex2[0] - tri->vertex1[0], tri->vertex2[1] - tri->vertex1[1], tri->vertex2[2] - tri->vertex1[2]}; float e2[3] = {tri->vertex3[0] - tri->vertex1[0], tri->vertex3[1] - tri->vertex1[1], tri->vertex3[2] - tri->vertex1[2]}; float cross[3] = { e1[1] * e2[2] - e1[2] * e2[1], e1[2] * e2[0] - e1[0] * e2[2], e1[0] * e2[1] - e1[1] * e2[0] }; float area = 0.5f * std::sqrt(cross[0]*cross[0] + cross[1]*cross[1] + cross[2]*cross[2]); float nx = tri->normal[0], ny = tri->normal[1], nz = tri->normal[2]; float nLen = std::sqrt(nx*nx + ny*ny + nz*nz); if (nLen > 0.0001f) { nx /= nLen; ny /= nLen; nz /= nLen; } int64_t keys[3] = { vertexKey(tri->vertex1[0], tri->vertex1[1], tri->vertex1[2]), vertexKey(tri->vertex2[0], tri->vertex2[1], tri->vertex2[2]), vertexKey(tri->vertex3[0], tri->vertex3[1], tri->vertex3[2]) }; float (*verts)[3] = {&tri->vertex1[0], &tri->vertex2[0], &tri->vertex3[0]}; for (int v = 0; v < 3; ++v) { auto& info = vertexMap[keys[v]]; info.normal[0] += nx * area; info.normal[1] += ny * area; info.normal[2] += nz * area; info.areaSum += area; info.valence++; info.pos[0] = verts[v][0]; info.pos[1] = verts[v][1]; info.pos[2] = verts[v][2]; } }); for (auto& [key, info] : vertexMap) { if (info.areaSum > 0.00001f) { info.normal[0] /= info.areaSum; info.normal[1] /= info.areaSum; info.normal[2] /= info.areaSum; float nLen = std::sqrt(info.normal[0]*info.normal[0] + info.normal[1]*info.normal[1] + info.normal[2]*info.normal[2]); if (nLen > 0.0001f) { info.normal[0] /= nLen; info.normal[1] /= nLen; info.normal[2] /= nLen; } } } size_t idx = 0; trianglePool->for_each([&](hhb::core::Triangle* tri) { if (idx >= triangleCount) return; int64_t keys[3] = { vertexKey(tri->vertex1[0], tri->vertex1[1], tri->vertex1[2]), vertexKey(tri->vertex2[0], tri->vertex2[1], tri->vertex2[2]), vertexKey(tri->vertex3[0], tri->vertex3[1], tri->vertex3[2]) }; float e1[3] = {tri->vertex2[0] - tri->vertex1[0], tri->vertex2[1] - tri->vertex1[1], tri->vertex2[2] - tri->vertex1[2]}; float e2[3] = {tri->vertex3[0] - tri->vertex1[0], tri->vertex3[1] - tri->vertex1[1], tri->vertex3[2] - tri->vertex1[2]}; float cross[3] = { e1[1] * e2[2] - e1[2] * e2[1], e1[2] * e2[0] - e1[0] * e2[2], e1[0] * e2[1] - e1[1] * e2[0] }; float area = 0.5f * std::sqrt(cross[0]*cross[0] + cross[1]*cross[1] + cross[2]*cross[2]); float meanCurv = 0.0f; if (area > 0.00001f) { for (int v = 0; v < 3; ++v) { auto it = vertexMap.find(keys[v]); if (it != vertexMap.end()) { float dx = it->second.pos[0] - tri->vertex1[0]; float dy = it->second.pos[1] - tri->vertex1[1]; float dz = it->second.pos[2] - tri->vertex1[2]; float dist = std::sqrt(dx*dx + dy*dy + dz*dz); float dotProd = it->second.normal[0] * tri->normal[0] + it->second.normal[1] * tri->normal[1] + it->second.normal[2] * tri->normal[2]; float angle = std::acos(std::max(-1.0f, std::min(1.0f, dotProd))); meanCurv += angle / dist; } } meanCurv /= (3.0f * area); } float gaussianCurv = 0.0f; if (area > 0.00001f) { float angleSum = 0.0f; for (int v = 0; v < 3; ++v) { auto it = vertexMap.find(keys[v]); if (it != vertexMap.end() && it->second.valence > 0) { angleSum += 2.0f * 3.14159265f / it->second.valence; } } gaussianCurv = (angleSum - 3.14159265f) / area; } faceMeanCurvature[idx] = meanCurv; faceGaussianCurvature[idx] = gaussianCurv; float absMean = std::abs(meanCurv); float absGauss = std::abs(gaussianCurv); if (absMean < 0.5f && absGauss < 0.5f) { faceCurvatureIds[idx] = 0; } else if (absGauss > 2.0f && gaussianCurv > 0) { faceCurvatureIds[idx] = 1; } else if (absGauss > 2.0f && gaussianCurv < 0) { faceCurvatureIds[idx] = 2; } else if (absMean > 1.0f && meanCurv > 0) { faceCurvatureIds[idx] = 3; } else if (absMean > 1.0f && meanCurv < 0) { faceCurvatureIds[idx] = 4; } else { faceCurvatureIds[idx] = 5; } idx++; }); curvatureComputed = true; printf("[Curvature] Computed for %zu triangles\n", faceMeanCurvature.size()); fflush(stdout); } void RenderManager::computeGeometricFeatures() { if (!curvatureComputed) { computeCurvature(); } if (!trianglePool || triangleCount == 0) { geometricFeaturesComputed = false; return; } faceCategoryIds.resize(triangleCount, 0); struct EdgeKey { int64_t v0, v1; bool operator==(const EdgeKey& o) const { return v0 == o.v0 && v1 == o.v1; } }; struct EdgeKeyHash { size_t operator()(const EdgeKey& k) const { return std::hash()(k.v0) ^ (std::hash()(k.v1) << 1); } }; auto vertexKey = [](float x, float y, float z) -> int64_t { int ix = (int)(x * 10000.0f); int iy = (int)(y * 10000.0f); int iz = (int)(z * 10000.0f); return ((int64_t)(ix & 0xFFFFF) << 40) | ((int64_t)(iy & 0xFFFFF) << 20) | (int64_t)(iz & 0xFFFFF); }; auto makeEdge = [](int64_t a, int64_t b) -> EdgeKey { return a < b ? EdgeKey{a, b} : EdgeKey{b, a}; }; std::unordered_map, EdgeKeyHash> edgeToTriangles; std::vector triVertexKeys(triangleCount * 3); size_t idx = 0; trianglePool->for_each([&](hhb::core::Triangle* tri) { if (idx >= triangleCount) return; int64_t k1 = vertexKey(tri->vertex1[0], tri->vertex1[1], tri->vertex1[2]); int64_t k2 = vertexKey(tri->vertex2[0], tri->vertex2[1], tri->vertex2[2]); int64_t k3 = vertexKey(tri->vertex3[0], tri->vertex3[1], tri->vertex3[2]); triVertexKeys[idx * 3 + 0] = k1; triVertexKeys[idx * 3 + 1] = k2; triVertexKeys[idx * 3 + 2] = k3; edgeToTriangles[makeEdge(k1, k2)].push_back((int)idx); edgeToTriangles[makeEdge(k2, k3)].push_back((int)idx); edgeToTriangles[makeEdge(k1, k3)].push_back((int)idx); idx++; }); std::vector visited(triangleCount, false); std::vector> clusters; for (size_t start = 0; start < triangleCount; ++start) { if (visited[start]) continue; int curvId = faceCurvatureIds[start]; std::vector cluster; std::vector stack; stack.push_back((int)start); while (!stack.empty()) { int triIdx = stack.back(); stack.pop_back(); if (triIdx < 0 || triIdx >= (int)triangleCount || visited[triIdx]) continue; if (faceCurvatureIds[triIdx] != curvId) continue; visited[triIdx] = true; cluster.push_back(triIdx); int64_t k1 = triVertexKeys[triIdx * 3 + 0]; int64_t k2 = triVertexKeys[triIdx * 3 + 1]; int64_t k3 = triVertexKeys[triIdx * 3 + 2]; auto addNeighbors = [&](int64_t a, int64_t b) { EdgeKey ek = makeEdge(a, b); auto it = edgeToTriangles.find(ek); if (it != edgeToTriangles.end()) { for (int nIdx : it->second) { if (!visited[nIdx] && faceCurvatureIds[nIdx] == curvId) { stack.push_back(nIdx); } } } }; addNeighbors(k1, k2); addNeighbors(k2, k3); addNeighbors(k1, k3); } if (!cluster.empty()) { clusters.push_back(std::move(cluster)); } } printf("[GeoFeature] Found %zu curvature clusters\n", clusters.size()); int boundaryEdgeCount = 0; for (const auto& [ek, tris] : edgeToTriangles) { if (tris.size() == 1) boundaryEdgeCount++; } for (auto& cluster : clusters) { float totalArea = 0.0f; float centerX = 0, centerY = 0, centerZ = 0; float avgMeanCurv = 0; float avgGaussCurv = 0; int boundaryEdges = 0; std::unordered_set clusterSet(cluster.begin(), cluster.end()); for (int triIdx : cluster) { float e1[3], e2[3]; trianglePool->for_each([&](hhb::core::Triangle* tri) { static size_t counter = 0; if (counter == (size_t)triIdx) { e1[0] = tri->vertex2[0] - tri->vertex1[0]; e1[1] = tri->vertex2[1] - tri->vertex1[1]; e1[2] = tri->vertex2[2] - tri->vertex1[2]; e2[0] = tri->vertex3[0] - tri->vertex1[0]; e2[1] = tri->vertex3[1] - tri->vertex1[1]; e2[2] = tri->vertex3[2] - tri->vertex1[2]; centerX += (tri->vertex1[0] + tri->vertex2[0] + tri->vertex3[0]) / 3.0f; centerY += (tri->vertex1[1] + tri->vertex2[1] + tri->vertex3[1]) / 3.0f; centerZ += (tri->vertex1[2] + tri->vertex2[2] + tri->vertex3[2]) / 3.0f; } counter++; if (counter >= triangleCount) counter = 0; }); float cross[3] = { e1[1]*e2[2] - e1[2]*e2[1], e1[2]*e2[0] - e1[0]*e2[2], e1[0]*e2[1] - e1[1]*e2[0] }; float area = 0.5f * std::sqrt(cross[0]*cross[0] + cross[1]*cross[1] + cross[2]*cross[2]); totalArea += area; avgMeanCurv += faceMeanCurvature[triIdx]; avgGaussCurv += faceGaussianCurvature[triIdx]; int64_t k1 = triVertexKeys[triIdx * 3 + 0]; int64_t k2 = triVertexKeys[triIdx * 3 + 1]; int64_t k3 = triVertexKeys[triIdx * 3 + 2]; auto checkBoundary = [&](int64_t a, int64_t b) { EdgeKey ek = makeEdge(a, b); auto it = edgeToTriangles.find(ek); if (it != edgeToTriangles.end() && it->second.size() == 1) { boundaryEdges++; } else if (it != edgeToTriangles.end()) { bool hasExternal = false; for (int nIdx : it->second) { if (clusterSet.find(nIdx) == clusterSet.end()) { hasExternal = true; break; } } if (hasExternal) boundaryEdges++; } }; checkBoundary(k1, k2); checkBoundary(k2, k3); checkBoundary(k1, k3); } if (cluster.empty()) continue; avgMeanCurv /= cluster.size(); avgGaussCurv /= cluster.size(); centerX /= cluster.size(); centerY /= cluster.size(); centerZ /= cluster.size(); int featureCategory = 0; if (cluster.size() < 5) { featureCategory = 0; } else if (avgGaussCurv > 1.5f && avgMeanCurv > 0.5f && totalArea < 0.5f) { featureCategory = 8; } else if (avgGaussCurv > 1.5f && avgMeanCurv < -0.5f && totalArea < 0.3f) { featureCategory = 9; } else if (avgMeanCurv < -1.0f && boundaryEdges >= 3) { featureCategory = 10; } else if (avgMeanCurv > 1.0f && totalArea > 0.1f && totalArea < 2.0f) { featureCategory = 11; } else if (std::abs(avgMeanCurv) < 0.5f && std::abs(avgGaussCurv) < 0.5f) { featureCategory = 1; } else { featureCategory = 0; } for (int triIdx : cluster) { faceCategoryIds[triIdx] = featureCategory; } } faceCategoriesComputed = true; geometricFeaturesComputed = true; printf("[GeoFeature] Geometric feature classification complete\n"); fflush(stdout); } void RenderManager::updateLabelVertexData() { if (!faceCategoriesComputed || faceCategoryIds.empty()) { computeFaceCategories(); } std::vector labelData; labelData.reserve(triangleCount * 3 * 6); size_t triIdx = 0; if (trianglePool) { trianglePool->for_each([&](hhb::core::Triangle* tri) { int catId = (triIdx < faceCategoryIds.size()) ? faceCategoryIds[triIdx] : 0; float r, g, b; categoryToColor(catId, &r, &g, &b); labelData.push_back(tri->vertex1[0]); labelData.push_back(tri->vertex1[1]); labelData.push_back(tri->vertex1[2]); labelData.push_back(r); labelData.push_back(g); labelData.push_back(b); labelData.push_back(tri->vertex2[0]); labelData.push_back(tri->vertex2[1]); labelData.push_back(tri->vertex2[2]); labelData.push_back(r); labelData.push_back(g); labelData.push_back(b); labelData.push_back(tri->vertex3[0]); labelData.push_back(tri->vertex3[1]); labelData.push_back(tri->vertex3[2]); labelData.push_back(r); labelData.push_back(g); labelData.push_back(b); triIdx++; }); } glBindBuffer(GL_ARRAY_BUFFER, labelVBO); glBufferData(GL_ARRAY_BUFFER, labelData.size() * sizeof(float), labelData.data(), GL_STATIC_DRAW); glBindBuffer(GL_ARRAY_BUFFER, 0); } void RenderManager::renderLabelMode() { if (!labelShaderProgram || triangleCount == 0) return; glUseProgram(labelShaderProgram); float modelMat[16] = { 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f }; GLint modelLoc = glGetUniformLocation(labelShaderProgram, "model"); glUniformMatrix4fv(modelLoc, 1, GL_FALSE, modelMat); float distance = 5.0f / zoom; float cx = cosf(cameraRotation[0]); float sx = sinf(cameraRotation[0]); float cy = cosf(cameraRotation[1]); float sy = sinf(cameraRotation[1]); float camPosX = cameraPosition[0] + distance * cx * sy; float camPosY = cameraPosition[1] + distance * sx; float camPosZ = cameraPosition[2] + distance * cx * cy; float view[16] = { cy, -sx*sy, -cx*sy, 0.0f, 0.0f, cx, -sx, 0.0f, sy, sx*cy, cx*cy, 0.0f, 0.0f, 0.0f, -distance, 1.0f }; view[12] = -(view[0]*camPosX + view[4]*camPosY + view[8]*camPosZ); view[13] = -(view[1]*camPosX + view[5]*camPosY + view[9]*camPosZ); view[14] = -(view[2]*camPosX + view[6]*camPosY + view[10]*camPosZ); GLint viewLoc = glGetUniformLocation(labelShaderProgram, "view"); glUniformMatrix4fv(viewLoc, 1, GL_FALSE, view); float aspect = (float)width / (float)height; float fov = 45.0f; float nearPlane = 0.1f; float farPlane = 1000.0f; float f = 1.0f / tanf(fov * 0.5f * 3.1415926535f / 180.0f); float projection[16] = { f / aspect, 0.0f, 0.0f, 0.0f, 0.0f, f, 0.0f, 0.0f, 0.0f, 0.0f, (farPlane + nearPlane) / (nearPlane - farPlane), -1.0f, 0.0f, 0.0f, (2.0f * farPlane * nearPlane) / (nearPlane - farPlane), 0.0f }; GLint projectionLoc = glGetUniformLocation(labelShaderProgram, "projection"); glUniformMatrix4fv(projectionLoc, 1, GL_FALSE, projection); glBindVertexArray(labelVAO); glDrawArrays(GL_TRIANGLES, 0, triangleCount * 3); glBindVertexArray(0); glUseProgram(0); } void RenderManager::setLabelMode(bool active) { labelModeActive = active; if (active && !faceCategoriesComputed) { computeFaceCategories(); updateLabelVertexData(); } } bool RenderManager::initBuffers() { // 创建VAO和VBO glGenVertexArrays(1, &VAO); glGenBuffers(1, &VBO); // 绑定VAO glBindVertexArray(VAO); // 绑定VBO glBindBuffer(GL_ARRAY_BUFFER, VBO); // 解绑VAO和VBO glBindBuffer(GL_ARRAY_BUFFER, 0); glBindVertexArray(0); return true; } void RenderManager::updateVertexData(hhb::core::ObjectPool& pool) { // 清空顶点数据 vertexData.clear(); // 遍历ObjectPool中的所有三角形 triangleCount = 0; // 使用ObjectPool的for_each方法遍历所有三角形 pool.for_each([&](hhb::core::Triangle* tri) { // 添加顶点1 vertexData.push_back(tri->vertex1[0]); vertexData.push_back(tri->vertex1[1]); vertexData.push_back(tri->vertex1[2]); // 添加法线 vertexData.push_back(tri->normal[0]); vertexData.push_back(tri->normal[1]); vertexData.push_back(tri->normal[2]); // 添加顶点2 vertexData.push_back(tri->vertex2[0]); vertexData.push_back(tri->vertex2[1]); vertexData.push_back(tri->vertex2[2]); // 添加法线 vertexData.push_back(tri->normal[0]); vertexData.push_back(tri->normal[1]); vertexData.push_back(tri->normal[2]); // 添加顶点3 vertexData.push_back(tri->vertex3[0]); vertexData.push_back(tri->vertex3[1]); vertexData.push_back(tri->vertex3[2]); // 添加法线 vertexData.push_back(tri->normal[0]); vertexData.push_back(tri->normal[1]); vertexData.push_back(tri->normal[2]); triangleCount++; }); std::cout << "Extracted " << triangleCount << " triangles from ObjectPool" << std::endl; } void RenderManager::updateFPS() { frameCount++; lastFpsUpdate += deltaTime; if (lastFpsUpdate >= 1.0f) { fps = frameCount / lastFpsUpdate; frameCount = 0; lastFpsUpdate = 0.0f; // 更新窗口标题 std::string newTitle = title + " - FPS: " + std::to_string((int)fps); glfwSetWindowTitle(window, newTitle.c_str()); } } void RenderManager::buildBVH() { std::cout << "Building BVH tree..." << std::endl; // 清空三角形指针数组 trianglePtrs.clear(); // 收集所有三角形指针 trianglePool->for_each([&](hhb::core::Triangle* tri) { trianglePtrs.push_back(tri); }); std::cout << "Collected " << trianglePtrs.size() << " triangles for BVH build" << std::endl; // 构建BVH树 auto start_time = std::chrono::high_resolution_clock::now(); bvh.build(trianglePtrs); auto end_time = std::chrono::high_resolution_clock::now(); std::chrono::duration build_duration = end_time - start_time; std::cout << "BVH build completed in " << build_duration.count() << " microseconds" << std::endl; std::cout << "BVH node count: " << bvh.node_count() << std::endl; } void RenderManager::centerModel() { printf("centerModel() called\n"); fflush(stdout); if (triangleCount == 0) { printf("No triangles loaded, using default camera position\n"); fflush(stdout); cameraPosition[0] = 0.0f; cameraPosition[1] = 0.0f; cameraPosition[2] = 0.0f; cameraRotation[0] = 0.0f; cameraRotation[1] = 0.0f; zoom = 1.0f; return; } // 获取BVH根节点包围盒 hhb::core::Bounds rootBounds = bvh.get_root_bounds(); // 计算包围盒尺寸 float sizeX = rootBounds.max[0] - rootBounds.min[0]; float sizeY = rootBounds.max[1] - rootBounds.min[1]; float sizeZ = rootBounds.max[2] - rootBounds.min[2]; float maxDim = (std::max)((std::max)(sizeX, sizeY), sizeZ); // 计算包围盒中心 float centerX = (rootBounds.max[0] + rootBounds.min[0]) / 2.0f; float centerY = (rootBounds.max[1] + rootBounds.min[1]) / 2.0f; float centerZ = (rootBounds.max[2] + rootBounds.min[2]) / 2.0f; printf("Model bounding box: (%.2f, %.2f, %.2f) to (%.2f, %.2f, %.2f)\n", rootBounds.min[0], rootBounds.min[1], rootBounds.min[2], rootBounds.max[0], rootBounds.max[1], rootBounds.max[2]); printf("Model center: (%.2f, %.2f, %.2f), max dimension: %.2f\n", centerX, centerY, centerZ, maxDim); fflush(stdout); // 设置相机目标点为模型中心 cameraPosition[0] = centerX; cameraPosition[1] = centerY; cameraPosition[2] = centerZ; // 重置相机旋转 cameraRotation[0] = 0.0f; cameraRotation[1] = 0.0f; // 计算合适的缩放级别 // 基础距离是5.0,我们需要让模型在视口中占据合适的比例 float targetDistance = maxDim > 0 ? maxDim * 1.5f : 5.0f; zoom = 5.0f / targetDistance; printf("Camera set to center (%.2f, %.2f, %.2f), zoom: %.4f\n", cameraPosition[0], cameraPosition[1], cameraPosition[2], zoom); fflush(stdout); } void RenderManager::screenToRay(double xpos, double ypos, float* rayOrigin, float* rayDirection) { // 将屏幕坐标转换为标准化设备坐标 (NDC) float ndc_x = (2.0f * static_cast(xpos)) / static_cast(width) - 1.0f; float ndc_y = 1.0f - (2.0f * static_cast(ypos)) / static_cast(height); // 计算射线方向(在相机空间中) float aspect = static_cast(width) / static_cast(height); float fov = 45.0f; float tan_half_fov = tan(fov * 0.5f * 3.1415926535f / 180.0f); rayDirection[0] = ndc_x * aspect * tan_half_fov; rayDirection[1] = ndc_y * tan_half_fov; rayDirection[2] = -1.0f; // 相机看向负Z轴 // 应用相机旋转 float cos_rot_x = cos(cameraRotation[0]); float sin_rot_x = sin(cameraRotation[0]); float cos_rot_y = cos(cameraRotation[1]); float sin_rot_y = sin(cameraRotation[1]); // 绕X轴旋转 float temp_y = rayDirection[1]; float temp_z = rayDirection[2]; rayDirection[1] = temp_y * cos_rot_x - temp_z * sin_rot_x; rayDirection[2] = temp_y * sin_rot_x + temp_z * cos_rot_x; // 绕Y轴旋转 float temp_x = rayDirection[0]; temp_z = rayDirection[2]; rayDirection[0] = temp_x * cos_rot_y + temp_z * sin_rot_y; rayDirection[2] = -temp_x * sin_rot_y + temp_z * cos_rot_y; // 归一化射线方向 float length = sqrt(rayDirection[0] * rayDirection[0] + rayDirection[1] * rayDirection[1] + rayDirection[2] * rayDirection[2]); rayDirection[0] /= length; rayDirection[1] /= length; rayDirection[2] /= length; // 射线原点就是相机位置 rayOrigin[0] = cameraPosition[0]; rayOrigin[1] = cameraPosition[1]; rayOrigin[2] = cameraPosition[2]; } void RenderManager::handleMouseClick(double xpos, double ypos) { // 关键:检查 ImGui 是否想捕获鼠标事件 ImGuiIO& io = ImGui::GetIO(); if (io.WantCaptureMouse) { // std::cout << "ImGui captured mouse click, ignoring ray picking." << std::endl; return; } // std::cout << "Mouse clicked at: " << xpos << ", " << ypos << std::endl; // 计算射线 float rayOrigin[3]; float rayDirection[3]; screenToRay(xpos, ypos, rayOrigin, rayDirection); std::cout << "Ray origin: (" << rayOrigin[0] << ", " << rayOrigin[1] << ", " << rayOrigin[2] << ")" << std::endl; std::cout << "Ray direction: (" << rayDirection[0] << ", " << rayDirection[1] << ", " << rayDirection[2] << ")" << std::endl; // 射线与BVH树相交 auto start_time = std::chrono::high_resolution_clock::now(); float t_hit; hhb::core::Triangle* hit_triangle = nullptr; bool hit = bvh.intersect(rayOrigin, rayDirection, t_hit, hit_triangle); auto end_time = std::chrono::high_resolution_clock::now(); std::chrono::duration intersect_duration = end_time - start_time; pickTime = static_cast(intersect_duration.count()); std::cout << "Ray intersection completed in " << pickTime << " microseconds" << std::endl; if (hit) { selectedTriangle = hit_triangle; // 查找三角形索引 selectedTriangleIndex = -1; if (trianglePool) { for (size_t i = 0; i < triangleCount; ++i) { if (&(*trianglePool)[i] == hit_triangle) { selectedTriangleIndex = static_cast(i); break; } } } std::cout << "Hit triangle found! Index: " << selectedTriangleIndex << std::endl; std::cout << "Intersection distance: " << t_hit << std::endl; std::cout << "Triangle vertices:" << std::endl; std::cout << " Vertex 1: (" << hit_triangle->vertex1[0] << ", " << hit_triangle->vertex1[1] << ", " << hit_triangle->vertex1[2] << ")" << std::endl; std::cout << " Vertex 2: (" << hit_triangle->vertex2[0] << ", " << hit_triangle->vertex2[1] << ", " << hit_triangle->vertex2[2] << ")" << std::endl; std::cout << " Vertex 3: (" << hit_triangle->vertex3[0] << ", " << hit_triangle->vertex3[1] << ", " << hit_triangle->vertex3[2] << ")" << std::endl; std::cout << " Normal: (" << hit_triangle->normal[0] << ", " << hit_triangle->normal[1] << ", " << hit_triangle->normal[2] << ")" << std::endl; } else { selectedTriangle = nullptr; selectedTriangleIndex = -1; std::cout << "No triangle hit." << std::endl; } } void RenderManager::initImGui() { // 创建ImGui上下文 ImGui::CreateContext(); // 设置ImGui样式 ImGui::StyleColorsDark(); // 配置ImGui以支持Unicode ImGuiIO& io = ImGui::GetIO(); // 禁用键盘导航,避免键位映射问题 // io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // 启用键盘控制 // 尝试加载系统字体以支持中文 // 首先添加默认字体 io.Fonts->AddFontDefault(); // 直接加载微软雅黑字体并指定中文字符范围 ImFontConfig config; config.MergeMode = false; config.PixelSnapH = true; if (ImFont* font = io.Fonts->AddFontFromFileTTF("C:\\Windows\\Fonts\\msyh.ttc", 18.0f, &config, io.Fonts->GetGlyphRangesChineseFull())) { std::cout << "Loaded Chinese font: C:\\Windows\\Fonts\\msyh.ttc" << std::endl; } else { // 尝试其他字体路径 if (ImFont* font = io.Fonts->AddFontFromFileTTF("C:\\Windows\\Fonts\\msyh.ttf", 18.0f, &config, io.Fonts->GetGlyphRangesChineseFull())) { std::cout << "Loaded Chinese font: C:\\Windows\\Fonts\\msyh.ttf" << std::endl; } else { std::cout << "Warning: Failed to load Chinese font" << std::endl; } } // 确保ImGui能够处理Unicode字符 io.Fonts->Build(); // 初始化ImGui与GLFW的绑定 // 第二个参数 false 表示不自动安装回调,我们手动处理事件 ImGui_ImplGlfw_InitForOpenGL(window, false); // 设置GLFW回调,确保ImGui能接收键盘和鼠标事件 glfwSetMouseButtonCallback(window, [](GLFWwindow* w, int button, int action, int mods) { ImGui_ImplGlfw_MouseButtonCallback(w, button, action, mods); }); glfwSetScrollCallback(window, [](GLFWwindow* w, double xoffset, double yoffset) { ImGui_ImplGlfw_ScrollCallback(w, xoffset, yoffset); }); glfwSetKeyCallback(window, [](GLFWwindow* w, int key, int scancode, int action, int mods) { ImGui_ImplGlfw_KeyCallback(w, key, scancode, action, mods); }); glfwSetCharCallback(window, [](GLFWwindow* w, unsigned int c) { ImGui_ImplGlfw_CharCallback(w, c); }); // 初始化ImGui与OpenGL的绑定 ImGui_ImplOpenGL3_Init("#version 330"); std::cout << "ImGui initialized successfully" << std::endl; } void RenderManager::updateImGui() { // 渲染主菜单栏 if (ImGui::BeginMainMenuBar()) { if (ImGui::BeginMenu("File")) { if (ImGui::MenuItem("Open STL...")) { printf("UI: Open STL Clicked from Menu!\n"); fflush(stdout); openFileDialog(); } ImGui::Separator(); if (ImGui::MenuItem("Exit")) { glfwSetWindowShouldClose(window, true); } ImGui::EndMenu(); } if (ImGui::BeginMenu("View")) { ImGui::MenuItem("Show BVH Bounds", NULL, &showBVH); if (ImGui::MenuItem("Reset Camera")) { centerModel(); } ImGui::EndMenu(); } ImGui::EndMainMenuBar(); } // 创建主控制面板 - 左侧常驻面板 ImGui::SetNextWindowPos(ImVec2(10, 30), ImGuiCond_Always); ImGui::SetNextWindowSize(ImVec2(280, 0), ImGuiCond_Always); ImGui::Begin("Huhb CAD Control Panel", nullptr, ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoSavedSettings); // 标题 ImGui::TextColored(ImVec4(0.0f, 0.8f, 1.0f, 1.0f), "Huhb CAD Industrial Viewer"); ImGui::Separator(); // 文件操作区域 - 主要的 Open STL 按钮 ImGui::Text("File Operations"); ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.2f, 0.6f, 0.8f, 1.0f)); ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.3f, 0.7f, 0.9f, 1.0f)); if (ImGui::Button("Open STL File", ImVec2(200, 35))) { printf("UI: Open STL Button Clicked!\n"); fflush(stdout); openFileDialog(); } ImGui::PopStyleColor(2); ImGui::Separator(); // 性能监控区域 ImGui::TextColored(ImVec4(1.0f, 0.8f, 0.0f, 1.0f), "Model & Performance"); ImGui::Text("FPS: %.1f", fps); ImGui::Text("Vertices: %zu", triangleCount * 3); ImGui::Text("Triangles: %zu", triangleCount); // 显示包围盒大小 hhb::core::Bounds rootBounds = bvh.get_root_bounds(); float sizeX = rootBounds.max[0] - rootBounds.min[0]; float sizeY = rootBounds.max[1] - rootBounds.min[1]; float sizeZ = rootBounds.max[2] - rootBounds.min[2]; if (sizeX < 0) sizeX = sizeY = sizeZ = 0.0f; ImGui::Text("Bounding Box:"); ImGui::Text(" Size: %.2f x %.2f x %.2f", sizeX, sizeY, sizeZ); ImGui::Text("BVH Depth: %d", bvh.depth()); ImGui::Text("Load Time: %.2f ms", loadTime); ImGui::Text("Memory Usage: %.2f MB", memoryUsage / (1024.0f * 1024.0f)); ImGui::Separator(); // PBR参数调节区域 ImGui::TextColored(ImVec4(0.0f, 1.0f, 0.5f, 1.0f), "PBR Material"); ImGui::SliderFloat("Metallic", &metallic, 0.0f, 1.0f); ImGui::SliderFloat("Roughness", &roughness, 0.0f, 1.0f); ImGui::Separator(); // BVH可视化开关 ImGui::TextColored(ImVec4(1.0f, 0.5f, 0.0f, 1.0f), "Debug Options"); ImGui::Checkbox("Show BVH Bounds", &showBVH); if (ImGui::Button("Reset Camera to Model", ImVec2(200, 25))) { printf("UI: Reset Camera Clicked!\n"); fflush(stdout); centerModel(); } ImGui::Separator(); // 拾取信息区域 ImGui::TextColored(ImVec4(0.8f, 0.5f, 1.0f, 1.0f), "Pick Information"); ImGui::Text("Pick Time: %.2f us", pickTime); if (selectedTriangleIndex >= 0) { ImGui::Text("Selected Triangle Index: %d", selectedTriangleIndex); ImGui::Text("Normal: (%.3f, %.3f, %.3f)", selectedTriangle->normal[0], selectedTriangle->normal[1], selectedTriangle->normal[2]); ImGui::Text("Vertex 1: (%.3f, %.3f, %.3f)", selectedTriangle->vertex1[0], selectedTriangle->vertex1[1], selectedTriangle->vertex1[2]); ImGui::Text("Vertex 2: (%.3f, %.3f, %.3f)", selectedTriangle->vertex2[0], selectedTriangle->vertex2[1], selectedTriangle->vertex2[2]); ImGui::Text("Vertex 3: (%.3f, %.3f, %.3f)", selectedTriangle->vertex3[0], selectedTriangle->vertex3[1], selectedTriangle->vertex3[2]); } ImGui::Separator(); // 具身智能 AI 助手交互区域 ImGui::TextColored(ImVec4(0.5f, 1.0f, 0.5f, 1.0f), "Embodied AI Assistant"); ImGui::Separator(); // 显示 Agent 状态 auto agentState = embodiedAgent_.getState(); if (agentState == hhb::core::EmbodiedAIState::Processing || agentState == hhb::core::EmbodiedAIState::ToolExecuting) { ImGui::TextColored(ImVec4(1.0f, 1.0f, 0.0f, 1.0f), "State: %s", embodiedAgent_.getStateString().c_str()); } else if (agentState == hhb::core::EmbodiedAIState::Error) { ImGui::TextColored(ImVec4(1.0f, 0.3f, 0.3f, 1.0f), "State: %s", embodiedAgent_.getStateString().c_str()); } else { ImGui::TextColored(ImVec4(0.5f, 1.0f, 0.5f, 1.0f), "State: Ready"); } ImGui::InputText("##AIInput", userInputBuffer, sizeof(userInputBuffer)); ImGui::SameLine(); if (ImGui::Button("Send", ImVec2(80, 20))) { processUserInput(); } ImGui::Checkbox("Show Highlight", &showHighlight); if (showHighlight && !highlightIndices.empty()) { const char* typeStr = "Unknown"; switch (currentHighlightType) { case HighlightType::ThinParts: typeStr = "Weak Structure"; break; case HighlightType::CurvedSurfaces: typeStr = "Curved Surfaces"; break; case HighlightType::SharpEdges: typeStr = "Sharp Edges"; break; case HighlightType::FlatSurfaces: typeStr = "Flat Surfaces"; break; default: break; } ImGui::TextColored(ImVec4(1.0f, 0.3f, 0.3f, 1.0f), "Highlight: %s (%zu parts)", typeStr, highlightIndices.size()); } // 显示最后的 AI 回复 std::string lastResp = embodiedAgent_.getLastResponse(); if (!lastResp.empty()) { ImGui::TextWrapped("AI: %s", lastResp.c_str()); } // 显示错误信息 std::string lastErr = embodiedAgent_.getLastError(); if (!lastErr.empty() && lastErr.find("[Error]") != std::string::npos) { ImGui::TextColored(ImVec4(1.0f, 0.2f, 0.2f, 1.0f), "Error: %s", lastErr.c_str()); } if (selectedTriangleIndex < 0) { ImGui::TextDisabled("No triangle selected"); } ImGui::End(); // 具身智能 AI 聊天面板(右侧悬浮窗口) ImGui::SetNextWindowPos(ImVec2(width - 340, 30), ImGuiCond_FirstUseEver); ImGui::SetNextWindowSize(ImVec2(330, height - 40), ImGuiCond_FirstUseEver); ImGui::Begin("Embodied AI Chat", nullptr, ImGuiWindowFlags_NoSavedSettings); ImGui::TextColored(ImVec4(0.2f, 1.0f, 0.8f, 1.0f), "Embodied AI CAD Assistant"); ImGui::SameLine(); if (embodiedAgent_.isProcessing()) { ImGui::TextColored(ImVec4(1.0f, 1.0f, 0.0f, 1.0f), "[Processing...]"); } ImGui::Separator(); // 显示已注册的工具列表 auto toolNames = hhb::core::LLMClient::getInstance().getRegisteredToolNames(); if (!toolNames.empty()) { if (ImGui::CollapsingHeader("Available Tools")) { for (const auto& name : toolNames) { ImGui::BulletText("%s", name.c_str()); } } } // 聊天历史区域 auto chatHistory = embodiedAgent_.getChatHistory(); ImGui::BeginChild("ChatHistory", ImVec2(0, -60), true); for (const auto& msg : chatHistory) { if (msg.isUser) { ImGui::TextColored(ImVec4(0.0f, 0.8f, 1.0f, 1.0f), "[%s] You: %s", msg.timestamp.c_str(), msg.content.c_str()); } else { ImGui::TextColored(ImVec4(0.5f, 1.0f, 0.5f, 1.0f), "[%s] AI: %s", msg.timestamp.c_str(), msg.content.c_str()); } ImGui::Separator(); } // 自动滚动到底部 if (ImGui::GetScrollY() >= ImGui::GetScrollMaxY()) ImGui::SetScrollHereY(1.0f); ImGui::EndChild(); // 输入区域 static char chatInputBuffer[512]; ImGuiInputTextFlags flags = embodiedAgent_.isProcessing() ? ImGuiInputTextFlags_ReadOnly : 0; bool inputEnter = ImGui::InputText("##ChatInput", chatInputBuffer, sizeof(chatInputBuffer), flags | ImGuiInputTextFlags_EnterReturnsTrue); ImGui::SameLine(); if (ImGui::Button("Send##ChatSend", ImVec2(80, 20)) || inputEnter) { std::string chatInput(chatInputBuffer); if (!chatInput.empty() && !embodiedAgent_.isProcessing()) { strncpy(userInputBuffer, chatInputBuffer, sizeof(userInputBuffer) - 1); processUserInput(); memset(chatInputBuffer, 0, sizeof(chatInputBuffer)); } } ImGui::End(); } void RenderManager::renderImGui() { // 渲染ImGui ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); } void RenderManager::processUserInput() { std::string userInput(userInputBuffer); if (userInput.empty()) { return; } SetConsoleOutputCP(CP_UTF8); std::cout << "[RenderManager] User input: " << userInput << std::endl; // 优先使用具身智能 Agent 闭环处理 // 闭环流程:用户自然语言 -> LLM 解析为工具调用 -> C++ 执行几何分析 -> OpenGL 高亮显示 if (!trianglePool || trianglePool->size() == 0) { std::cout << "[RenderManager] No model loaded" << std::endl; return; } // 异步调用具身智能 Agent embodiedAgent_.processInputAsync(userInput); } void RenderManager::onToolResult(const std::vector& indices, HighlightType type, const std::string& desc) { // 具身智能工具执行结果回调:将分析结果映射到 OpenGL 高亮缓冲区 std::lock_guard lock(highlightMutex); newHighlightIndices = indices; currentHighlightType = type; lastAnalysisDesc = desc; highlightCalculated = true; std::cout << "[RenderManager] Tool result received: " << indices.size() << " indices, desc=" << desc << std::endl; } void RenderManager::highlightParts() { if (!showHighlight || highlightIndices.empty()) { return; } // 闪烁效果:使用正弦波调制透明度/亮度,周期约 0.5 秒 highlightBlinkTimer_ += deltaTime; float blink = 0.5f + 0.5f * sinf(highlightBlinkTimer_ * 6.2831853f); // 闪烁时在暗色和亮色之间切换,实现"呼吸"效果 float blinkAlpha = 0.6f + 0.4f * blink; GLint isSelectedLoc = glGetUniformLocation(shaderProgram, "isSelected"); GLint highlightColorLoc = glGetUniformLocation(shaderProgram, "highlightColor"); // 基础颜色:薄弱部位使用高亮红 (R:1.0, G:0.2, B:0.2) float color[3] = {1.0f, 0.2f, 0.2f}; switch (currentHighlightType) { case HighlightType::ThinParts: color[0] = 1.0f * blinkAlpha; color[1] = 0.2f * blinkAlpha; color[2] = 0.2f * blinkAlpha; break; case HighlightType::CurvedSurfaces: color[0] = 0.0f; color[1] = 1.0f * blinkAlpha; color[2] = 0.5f * blinkAlpha; break; case HighlightType::SharpEdges: color[0] = 1.0f * blinkAlpha; color[1] = 0.5f * blinkAlpha; color[2] = 0.0f; break; case HighlightType::FlatSurfaces: color[0] = 0.0f; color[1] = 0.5f * blinkAlpha; color[2] = 1.0f * blinkAlpha; break; default: break; } if (isSelectedLoc != -1 && highlightColorLoc != -1) { glUniform1i(isSelectedLoc, GL_TRUE); glUniform3fv(highlightColorLoc, 1, color); // 关闭深度写入,使高亮始终可见 glDepthMask(GL_FALSE); for (int index : highlightIndices) { glDrawArrays(GL_TRIANGLES, index * 3, 3); } glDepthMask(GL_TRUE); } } void RenderManager::shutdownImGui() { // 清理ImGui资源 ImGui_ImplOpenGL3_Shutdown(); ImGui_ImplGlfw_Shutdown(); ImGui::DestroyContext(); } // 保存截图 bool RenderManager::saveScreenshot(const std::string& filename) { printf("saveScreenshot() called with: %s\n", filename.c_str()); fflush(stdout); // 创建目录(如果不存在) size_t lastSlash = filename.find_last_of("\\/"); if (lastSlash != std::string::npos) { std::string dir = filename.substr(0, lastSlash); #ifdef _WIN32 CreateDirectoryA(dir.c_str(), NULL); #else mkdir(dir.c_str(), 0755); #endif } // 读取像素数据 std::vector pixels(width * height * 3); glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, pixels.data()); // 翻转图像(因为OpenGL坐标系统与图像坐标系统不同) for (int i = 0; i < height / 2; ++i) { for (int j = 0; j < width * 3; ++j) { std::swap(pixels[i * width * 3 + j], pixels[(height - i - 1) * width * 3 + j]); } } // 保存为PPM格式(简单的图像格式,无需外部库) std::ofstream file(filename); if (!file) { printf("Failed to open file for writing: %s\n", filename.c_str()); fflush(stdout); return false; } file << "P6\n" << width << " " << height << "\n255\n"; file.write(reinterpret_cast(pixels.data()), pixels.size()); file.close(); printf("Screenshot saved to: %s\n", filename.c_str()); fflush(stdout); return true; } // 捕获模型的三个视角 void RenderManager::captureModelViews(const std::string& modelName) { printf("captureModelViews() called for model: %s\n", modelName.c_str()); fflush(stdout); // 保存原始相机状态 float originalPos[3] = {cameraPosition[0], cameraPosition[1], cameraPosition[2]}; float originalRot[2] = {cameraRotation[0], cameraRotation[1]}; float originalZoom = zoom; // 确保模型已加载 if (triangleCount == 0) { printf("No model loaded, cannot capture views\n"); fflush(stdout); return; } // 重置相机到模型中心 centerModel(); // 视角1:正面 cameraRotation[0] = 0.0f; // 俯仰角 cameraRotation[1] = 0.0f; // 偏航角 zoom = 1.0f; // 渲染一帧 render(); swapBuffers(); // 保存截图 std::string view1 = "screenshots/" + modelName + "_view1.ppm"; saveScreenshot(view1); // 视角2:侧面 cameraRotation[1] = 3.14159f / 2.0f; // 90度偏航 // 渲染一帧 render(); swapBuffers(); // 保存截图 std::string view2 = "screenshots/" + modelName + "_view2.ppm"; saveScreenshot(view2); // 视角3:顶部 cameraRotation[0] = -3.14159f / 2.0f; // -90度俯仰 cameraRotation[1] = 0.0f; // 渲染一帧 render(); swapBuffers(); // 保存截图 std::string view3 = "screenshots/" + modelName + "_view3.ppm"; saveScreenshot(view3); // 恢复原始相机状态 cameraPosition[0] = originalPos[0]; cameraPosition[1] = originalPos[1]; cameraPosition[2] = originalPos[2]; cameraRotation[0] = originalRot[0]; cameraRotation[1] = originalRot[1]; zoom = originalZoom; // 调用Python脚本进行模型描述和向量存储 std::string apiKey = "YOUR_OPENAI_API_KEY"; // 请替换为实际的API密钥 std::string pythonScript = "model_describer.py"; std::string command = "python " + pythonScript + " " + apiKey + " " + modelName + " " + view1 + " " + view2 + " " + view3; printf("Executing Python script: %s\n", command.c_str()); fflush(stdout); // 执行Python脚本 int result = system(command.c_str()); if (result == 0) { printf("Python script executed successfully\n"); } else { printf("Python script execution failed with code: %d\n", result); } fflush(stdout); printf("Captured 3 views for model: %s\n", modelName.c_str()); fflush(stdout); } void RenderManager::sphericalFibonacciSample(int index, int total, float radius, float* outX, float* outY, float* outZ) { const float goldenRatio = 1.6180339887498948482f; const float phi = 2.0f * 3.14159265358979323846f * index / goldenRatio; float cosTheta = 1.0f - 2.0f * (index + 0.5f) / total; float sinTheta = std::sqrt(1.0f - cosTheta * cosTheta); *outX = radius * sinTheta * std::cos(phi); *outY = radius * cosTheta; *outZ = radius * sinTheta * std::sin(phi); } bool RenderManager::saveFrameAsPNG(const std::string& filename, int w, int h, const std::vector& pixels) { std::vector flipped(pixels.size()); for (int row = 0; row < h; ++row) { const unsigned char* srcRow = pixels.data() + (h - 1 - row) * w * 3; unsigned char* dstRow = flipped.data() + row * w * 3; std::memcpy(dstRow, srcRow, w * 3); } int result = stbi_write_png(filename.c_str(), w, h, 3, flipped.data(), w * 3); if (!result) { printf("[CaptureSynthetic] Failed to save PNG: %s\n", filename.c_str()); fflush(stdout); return false; } return true; } void RenderManager::computeViewMatrixFromPosition(float camX, float camY, float camZ, float targetX, float targetY, float targetZ, float* outView16) { using namespace glm; vec3 camPos(camX, camY, camZ); vec3 target(targetX, targetY, targetZ); vec3 worldUp(0.0f, 1.0f, 0.0f); vec3 forward = normalize(target - camPos); vec3 right = normalize(cross(forward, worldUp)); if (length(right) < 0.0001f) { vec3 altUp(0.0f, 0.0f, 1.0f); right = normalize(cross(forward, altUp)); } vec3 up = cross(right, forward); mat4 viewMat = mat4(1.0f); viewMat[0][0] = right.x; viewMat[1][0] = right.y; viewMat[2][0] = right.z; viewMat[3][0] = -dot(right, camPos); viewMat[0][1] = up.x; viewMat[1][1] = up.y; viewMat[2][1] = up.z; viewMat[3][1] = -dot(up, camPos); viewMat[0][2] = -forward.x; viewMat[1][2] = -forward.y; viewMat[2][2] = -forward.z; viewMat[3][2] = dot(forward, camPos); viewMat[0][3] = 0.0f; viewMat[1][3] = 0.0f; viewMat[2][3] = 0.0f; viewMat[3][3] = 1.0f; std::memcpy(outView16, value_ptr(viewMat), 16 * sizeof(float)); } RenderManager::CaptureResult RenderManager::captureSyntheticData(const CaptureConfig& config) { CaptureResult result; result.totalFrames = config.sampleCount; result.successFrames = 0; result.failedFrames = 0; result.outputDirectory = config.outputDir; printf("\n========== CaptureSyntheticData START ==========\n"); printf(" Sample count : %d\n", config.sampleCount); printf(" Output dir : %s\n", config.outputDir.c_str()); printf(" Camera radius: %.2f\n", config.cameraRadius); printf(" Image size : %dx%d\n", config.imageWidth, config.imageHeight); printf(" Save mask : %s\n", config.saveMask ? "YES" : "NO"); printf(" Save depth : %s\n", config.saveDepth ? "YES" : "NO"); fflush(stdout); if (triangleCount == 0) { printf("[CaptureSynthetic] ERROR: No model loaded!\n"); fflush(stdout); return result; } if (config.saveMask) { computeGeometricFeatures(); updateLabelVertexData(); } namespace fs = std::filesystem; fs::create_directories(config.outputDir); fs::create_directories(config.outputDir + "/rgb"); if (config.saveMask) { fs::create_directories(config.outputDir + "/mask"); } if (config.saveDepth) { fs::create_directories(config.outputDir + "/depth"); } float origPos[3] = {cameraPosition[0], cameraPosition[1], cameraPosition[2]}; float origRot[2] = {cameraRotation[0], cameraRotation[1]}; float origZoom = zoom; SpatialInfo info = getSpatialInfo(); float targetX = info.center[0]; float targetY = info.center[1]; float targetZ = info.center[2]; float origWidth = (float)width; float origHeight = (float)height; glfwSetWindowSize(window, config.imageWidth, config.imageHeight); glViewport(0, 0, config.imageWidth, config.imageHeight); width = config.imageWidth; height = config.imageHeight; auto timeStart = std::chrono::steady_clock::now(); std::ostringstream cameraPosesJson; cameraPosesJson << "{\n"; float fov = 45.0f; float nearPlane = 0.1f; float farPlane = 1000.0f; float aspect = (float)config.imageWidth / (float)config.imageHeight; float f = 1.0f / tanf(fov * 0.5f * 3.1415926535f / 180.0f); float projectionArr[16] = { f / aspect, 0.0f, 0.0f, 0.0f, 0.0f, f, 0.0f, 0.0f, 0.0f, 0.0f, (farPlane + nearPlane) / (nearPlane - farPlane), -1.0f, 0.0f, 0.0f, (2.0f * farPlane * nearPlane) / (nearPlane - farPlane), 0.0f }; for (int i = 0; i < config.sampleCount; ++i) { float camX, camY, camZ; sphericalFibonacciSample(i, config.sampleCount, config.cameraRadius, &camX, &camY, &camZ); camX += targetX; camY += targetY; camZ += targetZ; cameraPosition[0] = targetX; cameraPosition[1] = targetY; cameraPosition[2] = targetZ; float dx = camX - targetX; float dy = camY - targetY; float dz = camZ - targetZ; float dist = std::sqrt(dx * dx + dy * dy + dz * dz); cameraRotation[0] = std::asin(dy / dist); cameraRotation[1] = std::atan2(dx, dz); zoom = 5.0f / dist; float viewMatrix[16]; computeViewMatrixFromPosition(camX, camY, camZ, targetX, targetY, targetZ, viewMatrix); if (i > 0) cameraPosesJson << ",\n"; cameraPosesJson << " \"" << (i + 1) << "\": {\n"; cameraPosesJson << " \"position\": [" << camX << ", " << camY << ", " << camZ << "],\n"; cameraPosesJson << " \"target\": [" << targetX << ", " << targetY << ", " << targetZ << "],\n"; cameraPosesJson << " \"rotation\": [" << cameraRotation[0] << ", " << cameraRotation[1] << "],\n"; cameraPosesJson << " \"view_matrix\": ["; for (int vi = 0; vi < 16; ++vi) { cameraPosesJson << viewMatrix[vi]; if (vi < 15) cameraPosesJson << ", "; } cameraPosesJson << "],\n"; cameraPosesJson << " \"projection_matrix\": ["; for (int pi = 0; pi < 16; ++pi) { cameraPosesJson << projectionArr[pi]; if (pi < 15) cameraPosesJson << ", "; } cameraPosesJson << "],\n"; cameraPosesJson << " \"fov_degrees\": " << fov << ",\n"; cameraPosesJson << " \"near_plane\": " << nearPlane << ",\n"; cameraPosesJson << " \"far_plane\": " << farPlane << ",\n"; cameraPosesJson << " \"image_width\": " << config.imageWidth << ",\n"; cameraPosesJson << " \"image_height\": " << config.imageHeight << "\n"; cameraPosesJson << " }"; // === Pass 1: RGB rendering (PBR shader) === render(); swapBuffers(); std::vector rgbPixels(config.imageWidth * config.imageHeight * 3); glReadPixels(0, 0, config.imageWidth, config.imageHeight, GL_RGB, GL_UNSIGNED_BYTE, rgbPixels.data()); std::ostringstream rgbOss; rgbOss << config.outputDir << "/rgb/frame_" << std::setfill('0') << std::setw(4) << (i + 1) << ".png"; if (saveFrameAsPNG(rgbOss.str(), config.imageWidth, config.imageHeight, rgbPixels)) { result.successFrames++; } else { result.failedFrames++; } // === Pass 2: Mask rendering (label shader) === if (config.saveMask && labelShaderProgram) { glClearColor(0.0f, 0.0f, 0.0f, 1.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glEnable(GL_DEPTH_TEST); renderLabelMode(); swapBuffers(); std::vector maskPixels(config.imageWidth * config.imageHeight * 3); glReadPixels(0, 0, config.imageWidth, config.imageHeight, GL_RGB, GL_UNSIGNED_BYTE, maskPixels.data()); std::ostringstream maskOss; maskOss << config.outputDir << "/mask/mask_" << std::setfill('0') << std::setw(4) << (i + 1) << ".png"; saveFrameAsPNG(maskOss.str(), config.imageWidth, config.imageHeight, maskPixels); glClearColor(0.1f, 0.1f, 0.1f, 1.0f); } // === Pass 3: Depth map rendering === if (config.saveDepth) { std::vector depthPixels(config.imageWidth * config.imageHeight); glReadPixels(0, 0, config.imageWidth, config.imageHeight, GL_DEPTH_COMPONENT, GL_FLOAT, depthPixels.data()); std::vector depthPng(config.imageWidth * config.imageHeight * 3); for (int px = 0; px < config.imageWidth * config.imageHeight; ++px) { float d = depthPixels[px]; if (d >= 1.0f) d = 1.0f; if (d <= 0.0f) d = 0.0f; unsigned char depthByte = (unsigned char)(d * 255.0f); depthPng[px * 3 + 0] = depthByte; depthPng[px * 3 + 1] = depthByte; depthPng[px * 3 + 2] = depthByte; } std::ostringstream depthOss; depthOss << config.outputDir << "/depth/depth_" << std::setfill('0') << std::setw(4) << (i + 1) << ".png"; saveFrameAsPNG(depthOss.str(), config.imageWidth, config.imageHeight, depthPng); std::ostringstream npyOss; npyOss << config.outputDir << "/depth/depth_" << std::setfill('0') << std::setw(4) << (i + 1) << ".raw"; std::ofstream depthFile(npyOss.str(), std::ios::binary); if (depthFile.is_open()) { depthFile.write(reinterpret_cast(depthPixels.data()), depthPixels.size() * sizeof(float)); depthFile.close(); } } glfwPollEvents(); if ((i + 1) % 50 == 0 || i == 0) { printf("[CaptureSynthetic] Progress: %d/%d (%.1f%%)\n", i + 1, config.sampleCount, 100.0f * (i + 1) / config.sampleCount); fflush(stdout); } } cameraPosesJson << "\n}\n"; { std::string posesPath = config.outputDir + "/camera_poses.json"; std::ofstream posesFile(posesPath); if (posesFile.is_open()) { posesFile << cameraPosesJson.str(); posesFile.close(); printf("[CaptureSynthetic] Camera poses saved to: %s\n", posesPath.c_str()); } } auto timeEnd = std::chrono::steady_clock::now(); result.elapsedSeconds = std::chrono::duration(timeEnd - timeStart).count(); cameraPosition[0] = origPos[0]; cameraPosition[1] = origPos[1]; cameraPosition[2] = origPos[2]; cameraRotation[0] = origRot[0]; cameraRotation[1] = origRot[1]; zoom = origZoom; glfwSetWindowSize(window, (int)origWidth, (int)origHeight); glViewport(0, 0, (int)origWidth, (int)origHeight); width = (int)origWidth; height = (int)origHeight; if (config.saveMask) { std::string legendPath = config.outputDir + "/label_legend.txt"; std::ofstream legendFile(legendPath); if (legendFile.is_open()) { legendFile << "# Semantic Label Color Legend\n"; legendFile << "# Category -> (R, G, B) in 0-255 range\n\n"; const char* categoryNames[] = { "FreeSurface", "HorizontalPlane", "LateralPlane_X", "LateralPlane_Z", "NearHorizontal", "NearLateral_X", "NearLateral_Z", "Degenerate", "ConvexFeature_Bolt", "ConcaveFeature_Hole", "Flange", "Boss" }; for (int c = 0; c < 12; ++c) { float r, g, b; categoryToColor(c, &r, &g, &b); legendFile << c << " " << categoryNames[c] << " " << (int)(r * 255) << " " << (int)(g * 255) << " " << (int)(b * 255) << "\n"; } legendFile.close(); printf("[CaptureSynthetic] Label legend saved to: %s\n", legendPath.c_str()); } } printf("\n========== CaptureSyntheticData COMPLETE ==========\n"); printf(" Total : %d\n", result.totalFrames); printf(" Success : %d\n", result.successFrames); printf(" Failed : %d\n", result.failedFrames); printf(" Time : %.2f seconds\n", result.elapsedSeconds); printf(" Output : %s\n", result.outputDirectory.c_str()); fflush(stdout); return result; } void RenderManager::openFileDialog() { printf("openFileDialog() called - opening Windows native file dialog...\n"); fflush(stdout); #ifdef _WIN32 OPENFILENAMEA ofn; ZeroMemory(&ofn, sizeof(ofn)); ofn.lStructSize = sizeof(ofn); ofn.hwndOwner = glfwGetWin32Window(window); char localPathBuffer[512] = {0}; ofn.lpstrFile = localPathBuffer; ofn.nMaxFile = sizeof(localPathBuffer); ofn.lpstrFilter = "STL Files (*.stl)\0*.stl\0All Files (*.*)\0*.*\0"; ofn.nFilterIndex = 1; ofn.lpstrFileTitle = nullptr; ofn.nMaxFileTitle = 0; ofn.lpstrInitialDir = nullptr; ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR; printf("Calling GetOpenFileNameA...\n"); fflush(stdout); if (GetOpenFileNameA(&ofn)) { printf("File selected: %s\n", localPathBuffer); fflush(stdout); loadFile(localPathBuffer); } else { DWORD err = CommDlgExtendedError(); if (err != 0) { printf("GetOpenFileNameA failed with error code: %lu\n", err); fflush(stdout); } else { printf("User cancelled file selection\n"); fflush(stdout); } } #else printf("File dialog not implemented for this platform\n"); fflush(stdout); #endif } void RenderManager::loadFile(const std::string& filename) { printf("loadFile() called with: %s\n", filename.c_str()); fflush(stdout); // 记录加载开始时间 auto load_start = std::chrono::high_resolution_clock::now(); // 创建新的对象池 if (trianglePool) { delete trianglePool; } trianglePool = new hhb::core::ObjectPool(); // 解析STL文件 hhb::core::StlParser parser; hhb::core::ParserResult result = parser.parse(filename, *trianglePool); // 记录加载结束时间 auto load_end = std::chrono::high_resolution_clock::now(); std::chrono::duration load_duration = load_end - load_start; loadTime = static_cast(load_duration.count()); if (result.success) { printf("File loaded successfully in %.2f ms\n", loadTime); printf("Triangles parsed: %zu\n", result.count); fflush(stdout); // 更新顶点数据 updateVertexData(*trianglePool); printf("Vertex data updated, vertexData.size() = %zu\n", vertexData.size()); fflush(stdout); // 绑定VAO和VBO glBindVertexArray(VAO); glBindBuffer(GL_ARRAY_BUFFER, VBO); glBufferData(GL_ARRAY_BUFFER, vertexData.size() * sizeof(float), vertexData.data(), GL_STATIC_DRAW); // 设置顶点属性指针 // 位置属性 (location = 0) glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0); glEnableVertexAttribArray(0); // 法线属性 (location = 1) glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3 * sizeof(float))); glEnableVertexAttribArray(1); glBindBuffer(GL_ARRAY_BUFFER, 0); glBindVertexArray(0); // 构建BVH树 buildBVH(); // 加载完模型后,自动适配视角 centerModel(); // 更新内存使用统计 memoryUsage = triangleCount * sizeof(hhb::core::Triangle); // 重置选中状态 selectedTriangle = nullptr; selectedTriangleIndex = -1; pickTime = 0.0f; geometryAPI.loadFromPool(*trianglePool); std::cout << "Model loaded into GeometryAPI from shared pool: " << geometryAPI.getTriangleCount() << " triangles" << std::endl; // 加载模型到 GeometryExpert geometryExpert.loadModelFromPool(*trianglePool); std::cout << "Model loaded into GeometryExpert from shared pool" << std::endl; // 捕获模型的三个视角 std::string modelName = filename.substr(filename.find_last_of("\\/") + 1); modelName = modelName.substr(0, modelName.find_last_of(".")); captureModelViews(modelName); printf("Model loading complete. Triangle count: %zu\n", triangleCount); fflush(stdout); } else { printf("Failed to load file: %s\n", result.error.c_str()); fflush(stdout); } } void RenderManager::renderBVH() { if (!showBVH || bvh.node_count() == 0) { return; } // 使用线框模式渲染BVH包围盒 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); // 设置线框颜色(绿色) float bvhColor[3] = {0.0f, 1.0f, 0.0f}; GLint objectColorLoc = glGetUniformLocation(shaderProgram, "objectColor"); glUniform3fv(objectColorLoc, 1, bvhColor); // 关闭光照影响,直接渲染纯色 GLint metallicLoc = glGetUniformLocation(shaderProgram, "metallic"); GLint roughnessLoc = glGetUniformLocation(shaderProgram, "roughness"); if (metallicLoc != -1) glUniform1f(metallicLoc, 0.0f); if (roughnessLoc != -1) glUniform1f(roughnessLoc, 1.0f); // 仅渲染根节点包围盒作为示例(完整遍历需要访问BVH内部节点) hhb::core::Bounds rootBounds = bvh.get_root_bounds(); renderAABB(rootBounds, bvhColor); // 恢复填充模式 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); } void RenderManager::renderAABB(const hhb::core::Bounds& bounds, const float* color) { // 构建AABB的8个顶点 float vertices[] = { bounds.min[0], bounds.min[1], bounds.min[2], bounds.max[0], bounds.min[1], bounds.min[2], bounds.max[0], bounds.max[1], bounds.min[2], bounds.min[0], bounds.max[1], bounds.min[2], bounds.min[0], bounds.min[1], bounds.max[2], bounds.max[0], bounds.min[1], bounds.max[2], bounds.max[0], bounds.max[1], bounds.max[2], bounds.min[0], bounds.max[1], bounds.max[2] }; // 12条线段的索引 (每条线2个顶点) unsigned int indices[] = { 0,1, 1,2, 2,3, 3,0, // 底面 4,5, 5,6, 6,7, 7,4, // 顶面 0,4, 1,5, 2,6, 3,7 // 侧面 }; GLuint vao, vbo, ebo; glGenVertexArrays(1, &vao); glGenBuffers(1, &vbo); glGenBuffers(1, &ebo); glBindVertexArray(vao); glBindBuffer(GL_ARRAY_BUFFER, vbo); glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo); glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0); glEnableVertexAttribArray(0); // 禁用法线属性,因为这里只有顶点 glDisableVertexAttribArray(1); // 绘制线框 glDrawElements(GL_LINES, 24, GL_UNSIGNED_INT, 0); // 清理 glDeleteVertexArrays(1, &vao); glDeleteBuffers(1, &vbo); glDeleteBuffers(1, &ebo); } void RenderManager::setTargetCameraPosition(const float pos[3], float duration) { if (duration <= 0.0f) { duration = 0.001f; } if (cameraAnimating) { cameraPosStart[0] = cameraPosition[0]; cameraPosStart[1] = cameraPosition[1]; cameraPosStart[2] = cameraPosition[2]; } else { memcpy(cameraPosStart, cameraPosition, sizeof(cameraPosStart)); } targetCameraPos[0] = pos[0]; targetCameraPos[1] = pos[1]; targetCameraPos[2] = pos[2]; cameraAnimStart = std::chrono::steady_clock::now(); cameraAnimDuration = duration; cameraAnimating = true; } void RenderManager::setTargetCameraRotation(const float rot[2], float duration) { if (duration <= 0.0f) { duration = 0.001f; } if (cameraAnimating) { cameraRotStart[0] = cameraRotation[0]; cameraRotStart[1] = cameraRotation[1]; } else { memcpy(cameraRotStart, cameraRotation, sizeof(cameraRotStart)); } targetCameraRot[0] = rot[0]; targetCameraRot[1] = rot[1]; cameraAnimStart = std::chrono::steady_clock::now(); cameraAnimDuration = duration; cameraAnimating = true; } void RenderManager::setTargetZoom(float target, float duration) { if (duration <= 0.0f) { duration = 0.001f; } if (cameraAnimating) { zoomStart = zoom; } else { zoomStart = zoom; } targetZoom = target; cameraAnimStart = std::chrono::steady_clock::now(); cameraAnimDuration = duration; cameraAnimating = true; } bool RenderManager::isCameraAnimating() const { return cameraAnimating; } void RenderManager::stopCameraAnimation() { cameraAnimating = false; } RenderManager::SpatialInfo RenderManager::getSpatialInfo() { SpatialInfo info; // 计算模型的几何中心和包围盒 if (trianglePtrs.empty()) { // 模型为空,返回默认值 memset(info.center, 0, sizeof(info.center)); memset(info.bounds, 0, sizeof(info.bounds)); } else { // 计算包围盒 float minX = FLT_MAX, minY = FLT_MAX, minZ = FLT_MAX; float maxX = -FLT_MAX, maxY = -FLT_MAX, maxZ = -FLT_MAX; for (const auto& triangle : trianglePtrs) { // 处理第一个顶点 minX = std::min(minX, triangle->vertex1[0]); minY = std::min(minY, triangle->vertex1[1]); minZ = std::min(minZ, triangle->vertex1[2]); maxX = std::max(maxX, triangle->vertex1[0]); maxY = std::max(maxY, triangle->vertex1[1]); maxZ = std::max(maxZ, triangle->vertex1[2]); // 处理第二个顶点 minX = std::min(minX, triangle->vertex2[0]); minY = std::min(minY, triangle->vertex2[1]); minZ = std::min(minZ, triangle->vertex2[2]); maxX = std::max(maxX, triangle->vertex2[0]); maxY = std::max(maxY, triangle->vertex2[1]); maxZ = std::max(maxZ, triangle->vertex2[2]); // 处理第三个顶点 minX = std::min(minX, triangle->vertex3[0]); minY = std::min(minY, triangle->vertex3[1]); minZ = std::min(minZ, triangle->vertex3[2]); maxX = std::max(maxX, triangle->vertex3[0]); maxY = std::max(maxY, triangle->vertex3[1]); maxZ = std::max(maxZ, triangle->vertex3[2]); } // 计算几何中心 info.center[0] = (minX + maxX) / 2.0f; info.center[1] = (minY + maxY) / 2.0f; info.center[2] = (minZ + maxZ) / 2.0f; // 保存包围盒信息 info.bounds[0] = minX; info.bounds[1] = minY; info.bounds[2] = minZ; info.bounds[3] = maxX; info.bounds[4] = maxY; info.bounds[5] = maxZ; } // 保存摄像头信息 memcpy(info.cameraPos, cameraPosition, sizeof(info.cameraPos)); memcpy(info.cameraRot, cameraRotation, sizeof(info.cameraRot)); info.currentZoom = zoom; return info; } } // namespace render } // namespace hhb