huhb3d-viewer / src /render /render_manager.cpp
Hgodwarrior's picture
Deploy: Huhb3D Generator with Docker
1dd0e3b
#ifdef _WIN32
#define NOMINMAX
#define WIN32_LEAN_AND_MEAN
#endif
#include <cstdint>
#include <iostream>
#include <cmath>
#include <vector>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <fstream>
#include <glad/glad.h>
#define GLFW_INCLUDE_NONE
#include <GLFW/glfw3.h>
#ifdef _WIN32
#include <Windows.h>
#include <commdlg.h>
#define GLFW_EXPOSE_NATIVE_WIN32
#include <GLFW/glfw3native.h>
#endif
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include "imgui/imgui.h"
#include "imgui/imgui_impl_glfw.h"
#include "imgui/imgui_impl_opengl3.h"
#include <chrono>
#include <thread>
#include <iomanip>
#include <sstream>
#include <filesystem>
#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<skill::AutoRotateSkill>(*this));
registry.registerSkill("reset_camera", std::make_unique<skill::ResetCameraSkill>(*this));
registry.registerSkill("zoom_in", std::make_unique<skill::ZoomInSkill>(*this));
registry.registerSkill("rotate", std::make_unique<skill::RotateSkill>(*this));
registry.registerSkill("optimize_view", std::make_unique<skill::OptimizeViewSkill>(*this));
registry.registerSkill("measure_model", std::make_unique<skill::MeasureModelSkill>(*this));
registry.registerSkill("analyze_geometry", std::make_unique<skill::AnalyzeGeometrySkill>(*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<int>& 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<hhb::core::Triangle*> result_parts;
HighlightType htype = HighlightType::None;
std::string desc;
if (cmd.action == "check_thickness") {
result_parts = geometryAPI.getThinParts(static_cast<float>(cmd.value));
htype = HighlightType::ThinParts;
desc = "薄弱部位 (厚度<" + std::to_string(static_cast<int>(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<float>(cmd.value));
htype = HighlightType::CurvedSurfaces;
desc = "曲面/曲线区域 (曲率>" + std::to_string(static_cast<float>(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<float>(cmd.value));
htype = HighlightType::SharpEdges;
desc = "锐角/棱边区域 (角度>" + std::to_string(static_cast<int>(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<float>(cmd.value));
htype = HighlightType::FlatSurfaces;
desc = "平面区域 (平坦度<" + std::to_string(static_cast<float>(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<hhb::core::Triangle*, int> ptrToIndex;
int idx = 0;
trianglePool->for_each([&](hhb::core::Triangle* tri) {
ptrToIndex[tri] = idx;
idx++;
});
std::vector<int> 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<std::mutex> lock(highlightMutex);
newHighlightIndices = std::move(indices);
currentHighlightType = htype;
lastAnalysisDesc = desc;
}
highlightCalculated = true;
std::cout << "Analysis completed: " << desc << std::endl;
});
}
}
if (highlightCalculated) {
{
std::lock_guard<std::mutex> 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<float>(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<float>(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<float>(currentFrame - lastFrame).count();
lastFrame = currentFrame;
}
void RenderManager::setTriangles(hhb::core::ObjectPool<hhb::core::Triangle>& 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<int64_t, VertexInfo> 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<int64_t>()(k.v0) ^ (std::hash<int64_t>()(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<EdgeKey, std::vector<int>, EdgeKeyHash> edgeToTriangles;
std::vector<int64_t> 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<bool> visited(triangleCount, false);
std::vector<std::vector<int>> clusters;
for (size_t start = 0; start < triangleCount; ++start) {
if (visited[start]) continue;
int curvId = faceCurvatureIds[start];
std::vector<int> cluster;
std::vector<int> 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<int> 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<float> 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<hhb::core::Triangle>& 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<double, std::micro> 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<float>(xpos)) / static_cast<float>(width) - 1.0f;
float ndc_y = 1.0f - (2.0f * static_cast<float>(ypos)) / static_cast<float>(height);
// 计算射线方向(在相机空间中)
float aspect = static_cast<float>(width) / static_cast<float>(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<double, std::micro> intersect_duration = end_time - start_time;
pickTime = static_cast<float>(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<int>(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<int>& indices, HighlightType type, const std::string& desc) {
// 具身智能工具执行结果回调:将分析结果映射到 OpenGL 高亮缓冲区
std::lock_guard<std::mutex> 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<unsigned char> 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<const char*>(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<unsigned char>& pixels) {
std::vector<unsigned char> 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<unsigned char> 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<unsigned char> 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<float> depthPixels(config.imageWidth * config.imageHeight);
glReadPixels(0, 0, config.imageWidth, config.imageHeight,
GL_DEPTH_COMPONENT, GL_FLOAT, depthPixels.data());
std::vector<unsigned char> 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<const char*>(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<float>(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<hhb::core::Triangle>();
// 解析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<double, std::milli> load_duration = load_end - load_start;
loadTime = static_cast<float>(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