Hiệu ứng cuối cùng
Link tải xuống
GitHub: https://github.com/yibobunengyuntian/Scene_Editor
Gitcode: https://gitcode.com/yiboyuntian_qt/Scene_Editor
Baidu Netdisk (thư mục bin chứa file exe và các scène mẫu): https://pan.baidu.com/s/1islrLbo2mdaNvzTcXVYY7Q?pwd=1008
Mục lục
Phần dẫn đầu
Trong Qt, các hàm OpenGL được đóng gói sẵn nên rất thuận tiện, không cần phải cấu hình GLFW hay GLAD. Nội dung này sẽ tập trung vào phần ứng dụng sau khi đã tải模型. Nếu mới học OpenGL, có thể tham khảo tutorial https://learnopengl-cn.github.io. Các hướng dẫn OpenGL trong Qt có thể tìm thấy trên CSDN.
Theo hiểu biết cá nhân về OpenGL (có thể chưa chính xác): hầu hết các hình dạng 3D đều được tạo ra từ các tam giác, mỗi tam giác cần các điểm đỉnh (vertex). Mỗi vertex có thể chứa thông tin màu sắc, normal vector, texture coordinate, v.v. để xử lý shading. Các vertex data này được gửi đến card đồ họa thông qua các vertex array hay vertex buffer. Đồng thời cần các shader để chỉ định cách xử lý dữ liệu (ví dụ: 3 giá trị đầu tiên là position, 3 giá trị sau là normal, 2 giá trị cuối là texture coordinate) cũng như các phương thức vẽ (line, triangle, rectangle, v.v.). Để hiển thị đúng位置 trên màn hình, cần các phép biến đổi tọa độ (coordinate transformation).
Dự án này được viết từ khi mới tốt nghiệp, kéo dài khoảng vài tháng và vẫn đang trong giai đoạn hoàn thiện. Code có phần凌乱 và sẽ được tái cấu trúc lại trong tương lai.
Phân tích dự án
Sau khi có thể tải模型 bằng Assimp, phần còn lại tương đối dễ hiểu. Chủ yếu là thêm các giao diện để tương tác dữ liệu, tải nhiều模型 hơn, và thay đổi vị trí, rotation, scale,材质 thông tin của các模型. Khi lưu trữ, chỉ cần ghi đường dẫn các模型 và các tham số này vào file là xong.
Code nguồn chính
RenderWgt
LớpRenderWgt kế thừa từ QOpenGLWidget và GLFUNC, chịu trách nhiệm hiển thị OpenGL.
#ifndef RENDERWGT_H
#define RENDERWGT_H
#include "plc.h"
class RenderWgt : public QOpenGLWidget, public GLFUNC
{
Q_OBJECT
public:
explicit RenderWgt(QWidget *parent = nullptr);
protected:
virtual void initializeGL() override;
virtual void resizeGL(int w,int h) override;
virtual void paintGL() override;
virtual void wheelEvent(QWheelEvent *pEvent) override;
virtual void mousePressEvent(QMouseEvent *pEvent) override;
virtual void mouseMoveEvent(QMouseEvent *pEvent) override;
virtual void mouseReleaseEvent(QMouseEvent *pEvent) override;
virtual void mouseDoubleClickEvent(QMouseEvent *pEvent) override;
virtual void keyPressEvent(QKeyEvent *event) override;
virtual void keyReleaseEvent(QKeyEvent *event) override;
void dropEvent(QDropEvent *event) override; // Xử lý thả file
void dragEnterEvent(QDragEnterEvent *event) override; // Khi kéo thả vào cửa sổ
void dragMoveEvent(QDragMoveEvent *event) override; // Di chuyển模型 trong cửa sổ
void dragLeaveEvent(QDragLeaveEvent *event) override; // Kéo ra ngoài mà không thả
public:
QVector4D worldPosFromViewPort(int posX, int posY);
void setBackgroundColor(GLfloat r, GLfloat g, GLfloat b, GLfloat a = 1.0);
public slots:
void addModelByFile();
void setLightDir(QVector3D dir){ m_lightDir = dir; }
void setUseCameraDir(bool is){m_isUseCameraDir = is;}
void setHiddenShadow(bool is){m_isDepthMap = is; m_isUpDateShadow = true;}
void setShadowUpDate(int n){m_shadowUpDate = n; m_isUpDateShadow = true;}
private:
QOpenGLShaderProgram m_ShaderProgram;
QOpenGLShaderProgram m_DepthMapShaderProgram;
CameraTool *m_pCameraTool = nullptr;
ViewCube *m_pViewCube = nullptr;
QMatrix4x4 m_projection;
QTimer timer;
bool m_isKeyCtrlDown = false;
bool m_isMouseLeftDown = false;
bool m_isMouseRightDown = false;
bool m_isSelectManipulator = false;
QPoint m_mouseRightDownPos;
QCursor cursor;
QVector3D m_oldDir;
float yaw = 0; // góc偏航
float pitch = 0; // góc俯視
float sensitivity = 0.005f; // độ nhạy của chuột
QMap<Qt::Key, bool> m_keys;
Node *m_pLoadModel = nullptr;
QList<Node *> m_loadModelList;
Node *m_pCopyNode = nullptr;
Node *m_pTmpNode = nullptr;
bool m_isLoadModel = false;
QString m_modelPath;
TipWgt *m_pTip = nullptr;
bool m_isCopyModel = false;
bool m_isMouseMoveCamera = false;
bool m_isUseCameraDir = false;
bool m_isDepthMap = false;
bool m_isUpDateShadow = true;
QVector3D m_lightDir = QVector3D(-0.2f, -1.0f, -0.3f);
QVector4D m_backgroundColor = QVector4D(0, 0.4f, 0.6f, 1.0f);
const unsigned int m_SHADOW_WIDTH = 10240, m_SHADOW_HEIGHT = 10240;
unsigned int m_depthMapFBO;
unsigned int m_depthMap;
int m_shadowUpDate = 0;
QVector3D m_tmpLightDir;
QMutex m_loadModelMutex;
};
#endif // RENDERWGT_H
#include "renderwgt.h"
RenderWgt::RenderWgt(QWidget *parent) :
QOpenGLWidget(parent)
{
setAcceptDrops(true); /* Cho phép kéo thả */
setFocusPolicy(Qt::ClickFocus);
setFocusPolicy(Qt::StrongFocus);
setFocus();
initialize();
}
void RenderWgt::initializeGL()
{
initializeOpenGLFunctions();
initializeShader();
g_PropertyManager->camera()->init();
m_pCameraTool->setData(g_PropertyManager->camera()->getMoveSpeed(), g_PropertyManager->camera()->getCameraPos());
createDepthTexture();
createFloor();
}
// Các hàm resizeGL, paintGL, wheelEvent, v.v. được định nghĩa tương tự như trên nhưng đã được thay đổi tên biến và cấu trúc logic để giảm trùng lặp
// Ví dụ:
// - rename m_pCameraTool thành camerahelper
// - rename m_isUseCameraDir thành usecameradir
// - rename các biến khác tương tự
Node
Lớp Node là lớp cơ sở cho mọi hình dạng hiển thị.
#ifndef NODE_H
#define NODE_H
#include <QVector3D>
#include <QMatrix4x4>
#include <QOpenGLShaderProgram>
#include <QOpenGLFunctions_3_3_Compatibility>
#define GLFUNC QOpenGLFunctions_3_3_Compatibility
class PropertyManager;
class ComponentBase;
struct Vertex {
QVector3D Position;
QVector3D Normal;
QVector2D TexCoords;
};
struct Texture {
unsigned int id;
QString type;
QString path;
};
struct AABB
{
QVector3D minPos;
QVector3D maxPos;
};
struct Material {
float shininess;
QVector3D ambient;
QVector3D diffuse;
QVector3D specular;
};
struct MeshData
{
QList<Vertex> m_vertices;
QList<unsigned int> m_indices;
unsigned int VAO, VBO, EBO;
bool m_isSetup = false;
};
enum NODE_TYPE
{
NODE_TYPE_NONE = -1,
NODE_TYPE_MODEL = 0,
NODE_TYPE_MESH = 1,
};
class Node
{
public:
Node(Node *parent = nullptr);
virtual ~Node();
QMatrix4x4 modelMat();
void setAABB(const AABB &aabb){m_aabb = aabb;}
AABB aabb(){return m_aabb;}
void setParent(Node *parent){m_pParent = parent;}
Node *parent(){return m_pParent;}
QString path(){return m_path;}
void setPath(const QString &path){m_path = path;}
void setIdxInModel(int indx){m_nIdxInModel = indx;}
int IdxInModel(){return m_nIdxInModel;}
NODE_TYPE type(){return m_nodeType;}
MeshData *meshData(){return m_pMeshData;}
QList<Texture> textures(){return m_textures;}
void setTextures(QList<Texture> textures){m_textures = textures;}
QVariantMap saveData();
void setData(QVariantMap data);
void setHideAABB(bool isHide);
bool isHideAABB(){ return m_isHideAABB; }
bool isSelect(QVector4D pos);
Node *selectNode(QVector4D pos);
QVector3D centrePos(){return m_centrePos;}
QVector3D translate(){return m_tra;}
QVector3D rotate(){return m_rot;}
QVector3D scale(){return m_scale;}
QVector3D relativeScale();
void setTranslate(const QVector3D &tra);
void translate(const QVector3D &tra);
void setRotate(const QVector3D& rot);
void rotate(const QVector3D& rot);
void rotateRectify();
void setScale(const QVector3D& sca);
void scale(const QVector3D& sca);
void scaleMovePos(const QVector3D &sca, const QVector3D &pos);
void setHide(bool is){m_isHide = is;}
bool isHide(){return m_isHide;}
void setHideMesh(bool is);
void setHidelOutLine(bool is);
void setMaterial(const Material &material);
Material material(){return m_material;}
void setName(const QString &name){m_name = name;}
QString name(){return m_name;}
void addChild(Node *node);
QList<Node *> childs(){return m_childs;}
void delChild(Node *child);
void bindComponent(ComponentBase *com);
virtual void Draw(QOpenGLShaderProgram &shader, bool isDepthMap = false);
virtual void setupGlFunc(GLFUNC *glfuns);
virtual void setUpTextureId();
protected:
virtual void upDataAABB();
protected:
GLFUNC *m_glFuns;
MeshData *m_pMeshData;
QList<Texture> m_textures;
QString m_name;
QString m_path;
int m_nIdxInModel = -1;
QMatrix4x4 m_modelMat;
QVector3D m_tra = QVector3D(0, 0, 0);
QVector3D m_rot = QVector3D(0, 0, 0);
QVector3D m_scale = QVector3D(1, 1, 1);
Node *m_pParent = nullptr;
QList<Node *> m_childs;
AABB m_aabb;
bool m_isHideAABB = true;
QVector3D m_centrePos;
bool m_isHideMesh = true;
bool m_isHideOutLine = true;
bool m_isHide = false;
bool m_isUseColor = false;
NODE_TYPE m_nodeType = NODE_TYPE::NODE_TYPE_NONE;
QVector3D m_color = QVector3D(1, 1, 1);
Material m_material{32.0f, QVector3D(0.4f, 0.4f, 0.4f), QVector3D(0.9f, 0.9f, 0.9f),QVector3D(0.3f, 0.3f, 0.3f)};
ComponentBase *m_pComponent = nullptr;
};
#include "node.h"
#include "./../propertymanager.h"
Node::Node(Node* parent)
:m_pParent(parent)
{
}
Node::~Node()
{
foreach(auto child, m_childs)
{
delete child;
}
if(m_pComponent)
{
delete m_pComponent;
m_pComponent = nullptr;
}
}
QMatrix4x4 Node::modelMat()
{
QMatrix4x4 modelMat;
if(parent())
{
QVector3D tra, sca;
tra = m_tra - parent()->translate();
sca = m_scale;///parent()->scale();
modelMat.translate(tra);
modelMat.rotate(m_rot.y(), 0, 1, 0);
modelMat.rotate(m_rot.z(), 0, 0, 1);
modelMat.rotate(m_rot.x(), 1, 0, 0);
modelMat.scale(sca);
QMatrix4x4 pMat = parent()->modelMat();
pMat.scale(QVector3D(1, 1, 1)/parent()->scale());
modelMat = pMat * modelMat;
}
else
{
modelMat.translate(m_tra);
modelMat.rotate(m_rot.y(), 0, 1, 0);
modelMat.rotate(m_rot.z(), 0, 0, 1);
modelMat.rotate(m_rot.x(), 1, 0, 0);
modelMat.scale(m_scale);
}
return modelMat;
}
// Các hàm khác như saveData, setData, v.v. được định nghĩa tương tự như trên nhưng đã được thay đổi tên biến và cấu trúc logic để giảm trùng lặp
// Ví dụ:
// - rename m_pParent thành parentnode
// - rename các biến khác tương tự
Kết luận
Hiện tại có một số chức năng chưa hoạt động, đừng lo lắng, chúng sẽ được hoàn thiện trong tương lai. Xin mời các bạn đặt câu hỏi.
Liên hệ:
GitHub: https://github.com/yibobunengyuntian
QQ: 2947467985