// Created by Lenn on 2025/12/16. // #ifndef TACTILEIPC3D_GLWIDGET_H #define TACTILEIPC3D_GLWIDGET_H #include #include #include #include #include #include #include #include #include #include #include #include #include struct Ray { QVector3D origin; QVector3D dir; }; class GLWidget : public QOpenGLWidget, protected QOpenGLFunctions_3_3_Core { Q_OBJECT Q_PROPERTY(float yaw READ yaw WRITE setYaw NOTIFY yawChanged) Q_PROPERTY(int row READ row WRITE setRow) Q_PROPERTY(int col READ col WRITE setCol) // Q_PROPERTY(bool showGrid READ showGrid WRITE setShowGrid NOTIFY showGridChanged) public: enum RenderMode { Realistic = 0, DataViz = 1, }; Q_ENUM(RenderMode) enum LabelMode { LabelsOff = 0, LabelsHover = 1, LabelsAlways = 2, }; Q_ENUM(LabelMode) explicit GLWidget(QWidget* parent = nullptr); ~GLWidget(); // panel 的物理尺寸(单位随你:米/毫米都行,只要全程一致) void setPanelSize(float w, float h, float d); void setPanelThickness(float h); // rows/cols: 传感点阵行列;pitch: 点与点的间距;dotRaius: 单个点的“显示半径” void setSpec(int rows, int cols, float pitch, float dotRaius); // 总点数 = 行 * 列 int dotCount() const { return m_cols * m_rows; } // 提交一帧传感值(数量必须等于 dotCount),在 paintGL 里用 instanced 的方式绘制 void submitValues(const QVector& values); float yaw() const { return m_camYawDeg; } bool showGrid() const { return m_showGrid; } int row() const { return m_rows; } int col() const { return m_cols; } public slots: // 值域范围,用于 shader 里把 value 映射到颜色(绿->红) void setRange(int minV, int maxV); void setYaw(float yawDeg); void setRenderModeString(const QString& mode); void setLabelModeString(const QString& mode); void setLightMode(bool on); void setShowBg(bool on); void setShowGrid(bool on); void setCol(int col); void setRow(int row); void setColorLow(const QColor& color); void setColorMid(const QColor& color); void setColorHigh(const QColor& color); signals: void yawChanged(); void dotClicked(int index, int row, int col, float value); protected: void initializeGL() override; void resizeGL(int w, int h) override; void paintGL() override; void mousePressEvent(QMouseEvent *event) override; void mouseMoveEvent(QMouseEvent *event) override; void mouseReleaseEvent(QMouseEvent *event) override; void wheelEvent(QWheelEvent *event) override; private: void initGeometry_(); void initPanelGeometry_(); void initDotGeometry_(); void initBackgroundGeometry_(); void initPrograms_(); void initDotTexture_(); void initRoomGeometry_(); void updateInstanceBufferIfNeeded_(); void updateMatrices_(); void updateRoom_(); bool projectToScreen_(const QVector3D& world, QPointF* out) const; int pickDotIndex_(const QPoint& pos, QVector3D* worldOut) const; private: // 传感值范围(用于颜色映射) int m_min = 0; int m_max = 1000; // 点阵规格 int m_rows = 3; int m_cols = 4; // panel: 一个长方体/板子(当前只画顶面矩形) float m_panelW = 0.25f; float m_panelH = 0.35f; float m_panelD = 0.05f; // 点阵布局参数 float m_pitch = 0.1f; float m_dotRadius = 0.03f; // 传感数据(GUI/定时器线程提交,GL 线程绘制) QMutex m_dataMutex; QVector m_latestValues; bool m_valuesDirty = false; std::atomic m_hasData{false}; // shader program(编译/链接后的可执行 GPU 程序) QOpenGLShaderProgram* m_bgProg = nullptr; QOpenGLShaderProgram* m_roomProg = nullptr; QOpenGLShaderProgram* m_panelProg = nullptr; QOpenGLShaderProgram* m_dotsProg = nullptr; unsigned int m_dotTex = 0; // panel 的 VAO/VBO/IBO(VAO: 顶点格式/绑定状态;VBO: 顶点数据;IBO: 索引数据) unsigned int m_panelVao = 0; unsigned int m_panelVbo = 0; unsigned int m_panelIbo = 0; int m_panelIndexCount = 0; bool m_panelGeometryDirty = false; // dots 的 VAO/VBO + instance VBO(instance VBO 每个点一条:offsetXZ + value) unsigned int m_dotsVao = 0; unsigned int m_dotsVbo = 0; unsigned int m_instanceVbo = 0; unsigned int room_vao = 0; unsigned int room_vbo = 0; unsigned int room_ibo = 0; int room_index_count = 0; bool m_showGrid = true; int m_instanceCount = 0; bool m_dotsGeometryDirty = false; unsigned int m_bgVao = 0; unsigned int m_bgVbo = 0; bool m_lightMode = true; bool m_showBg = true; QVector3D m_colorLow{0.10f, 0.75f, 1.00f}; QVector3D m_colorMid{0.10f, 0.95f, 0.35f}; QVector3D m_colorHigh{1.00f, 0.22f, 0.10f}; // MVP = Projection * View * Model。 // 这里 panel/dots 顶点基本已经是“世界坐标”,所以我们用 proj*view 即可(model 先省略)。 QMatrix4x4 m_mvp; QVector3D m_cameraPos{0.0f, 0.0f, 1.0f}; RenderMode m_renderMode = DataViz; LabelMode m_labelMode = LabelsOff; int m_hoveredIndex = -1; // 早期/预留的矩阵数组(现在主要使用 QMatrix4x4 的 m_mvp,这些暂时保留做对照/扩展) float m_proj[16]{}; float m_view[16]{}; float m_modelPanel[16]{}; float m_zoom_ = 45.0; float m_camYawDeg = -90.0f; float m_camPitchDeg = 0.0f; std::atomic m_rightDown{false}; QPoint m_lastPos; }; #endif //TACTILEIPC3D_GLWIDGET_H