QOpenGL三维物体选中

#ifndef CUBE_H
#define CUBE_H

#include "Shape.h"

class Cube : public Shape
{
public:
    explicit Cube(const QString &id);
    virtual ~Cube() = default;

    virtual std::shared_ptr<Mesh> getMesh() override;
    virtual std::shared_ptr<Material> getMaterial() override;
    virtual QMatrix4x4 &getTransformation() override;
    virtual QString ID() const override;

private:
    std::shared_ptr<Mesh> m_mesh;
    std::shared_ptr<Material> m_material;
    QMatrix4x4 m_transformation;
    QString m_id;
};

#endif // CUBE_H
#include "Cube.h"

#include <QRandomGenerator>
#include <QQuaternion>

Cube::Cube(const QString &id) :
    m_id(id)
{
    m_material = std::make_shared<Material>();

    // clang-format off
    QVector<VerticeInfo> vertices = {
        // Vertex data for face 0
        {QVector3D(-1.0f, -1.0f,  1.0f), QVector4D(1.0f, 1.0f, 1.0f, 1.0f)}, // v0
        {QVector3D( 1.0f, -1.0f,  1.0f), QVector4D(1.0f, 1.0f, 1.0f, 1.0f)}, // v1
        {QVector3D(-1.0f,  1.0f,  1.0f), m_material->Color[0]}, // v2
        {QVector3D( 1.0f,  1.0f,  1.0f), m_material->Color[0]}, // v3

        // Vertex data for face 1
        {QVector3D( 1.0f, -1.0f,  1.0f), QVector4D(1.0f, 1.0f, 1.0f, 1.0f)}, // v4
        {QVector3D( 1.0f, -1.0f, -1.0f), QVector4D(1.0f, 1.0f, 1.0f, 1.0f)}, // v5
        {QVector3D( 1.0f,  1.0f,  1.0f), m_material->Color[1]}, // v6
        {QVector3D( 1.0f,  1.0f, -1.0f), m_material->Color[1]}, // v7

        // Vertex data for face 2
        {QVector3D( 1.0f, -1.0f, -1.0f), QVector4D(1.0f, 1.0f, 1.0f, 1.0f)}, // v8
        {QVector3D(-1.0f, -1.0f, -1.0f), QVector4D(1.0f, 1.0f, 1.0f, 1.0f)}, // v9
        {QVector3D( 1.0f,  1.0f, -1.0f), m_material->Color[2]}, // v10
        {QVector3D(-1.0f,  1.0f, -1.0f), m_material->Color[2]}, // v11

        // Vertex data for face 3
        {QVector3D(-1.0f, -1.0f, -1.0f), QVector4D(1.0f, 1.0f, 1.0f, 1.0f)}, // v12
        {QVector3D(-1.0f, -1.0f,  1.0f), QVector4D(1.0f, 1.0f, 1.0f, 1.0f)}, // v13
        {QVector3D(-1.0f,  1.0f, -1.0f), m_material->Color[3]}, // v14
        {QVector3D(-1.0f,  1.0f,  1.0f), m_material->Color[3]}, // v15

        // Vertex data for face 4
        {QVector3D(-1.0f, -1.0f, -1.0f), QVector4D(0.5f, 0.5f, 0.5f, 1.0f)}, // v16
        {QVector3D( 1.0f, -1.0f, -1.0f), QVector4D(0.5f, 0.5f, 0.5f, 1.0f)}, // v17
        {QVector3D(-1.0f, -1.0f,  1.0f), QVector4D(0.5f, 0.5f, 0.5f, 1.0f)}, // v18
        {QVector3D( 1.0f, -1.0f,  1.0f), QVector4D(0.5f, 0.5f, 0.5f, 1.0f)}, // v19

        // Vertex data for face 5
        {QVector3D(-1.0f,  1.0f,  1.0f), m_material->Color[5]}, // v20
        {QVector3D( 1.0f,  1.0f,  1.0f), m_material->Color[5]}, // v21
        {QVector3D(-1.0f,  1.0f, -1.0f), m_material->Color[5]}, // v22
        {QVector3D( 1.0f,  1.0f, -1.0f), m_material->Color[5]}  // v23
    };

    QVector<GLushort> indices = {
         0,  1,  2,  3,  3,     // Face 0 - triangle strip ( v0,  v1,  v2,  v3)
         4,  4,  5,  6,  7,  7, // Face 1 - triangle strip ( v4,  v5,  v6,  v7)
         8,  8,  9, 10, 11, 11, // Face 2 - triangle strip ( v8,  v9, v10, v11)
        12, 12, 13, 14, 15, 15, // Face 3 - triangle strip (v12, v13, v14, v15)
        16, 16, 17, 18, 19, 19, // Face 4 - triangle strip (v16, v17, v18, v19)
        20, 20, 21, 22, 23      // Face 5 - triangle strip (v20, v21, v22, v23)
    };
    // clang-format on
    m_mesh = std::make_shared<Mesh>(vertices, indices);

    std::uniform_real_distribution<float> rand(-10.0, 10.0);

    QVector3D pos(rand(*QRandomGenerator::global()), rand(*QRandomGenerator::global()), rand(*QRandomGenerator::global()));
    m_transformation.setToIdentity();
    m_transformation.translate(pos);
}

std::shared_ptr<Mesh> Cube::getMesh()
{
    return m_mesh;
}

std::shared_ptr<Material> Cube::getMaterial()
{
    return m_material;
}

QMatrix4x4 &Cube::getTransformation()
{
    return m_transformation;
}

QString Cube::ID() const
{
    return m_id;
}
#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QString>

QT_BEGIN_NAMESPACE
namespace Ui
{
class MainWindow;
}
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private slots:
    void on_pushButton_rotate_toggled(bool checked);

    void on_pushButton_pan_toggled(bool checked);

    void on_pushButton_zoom_toggled(bool checked);

    void UpdateStatusLabel(const QString &msg);

private:
    Ui::MainWindow *ui;
};
#endif    // MAINWINDOW_H
#include "MainWindow.h"
#include "ui_MainWindow.h"

#include <QSurfaceFormat>

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    QSurfaceFormat glFormat;
    glFormat.setRenderableType(QSurfaceFormat::OpenGL);
    glFormat.setDepthBufferSize(24);
    glFormat.setOption(QSurfaceFormat::DebugContext);

    ui->scene->setFormat(glFormat);
    ui->scene->setFocus();
    connect(ui->pushButton_cube, &QPushButton::clicked, ui->scene, &SceneManager::onCreateCube);
    connect(ui->pushButton_pan, &QPushButton::toggled, ui->scene, &SceneManager::onPanToggled);
    connect(ui->pushButton_rotate, &QPushButton::toggled, ui->scene, &SceneManager::onRotateToggled);
    connect(ui->pushButton_zoom, &QPushButton::toggled, ui->scene, &SceneManager::onZoomToggled);
    connect(ui->scene, &SceneManager::UpdateStatusLabel, this, &MainWindow::UpdateStatusLabel);
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::UpdateStatusLabel(const QString &msg)
{
    ui->label_message->setText(msg);
}

void MainWindow::on_pushButton_rotate_toggled(bool checked)
{
    if (checked) {
        ui->pushButton_pan->setChecked(false);
        ui->pushButton_zoom->setChecked(false);
    }
}

void MainWindow::on_pushButton_pan_toggled(bool checked)
{
    if (checked) {
        ui->pushButton_rotate->setChecked(false);
        ui->pushButton_zoom->setChecked(false);
    }
}

void MainWindow::on_pushButton_zoom_toggled(bool checked)
{
    if (checked) {
        ui->pushButton_pan->setChecked(false);
        ui->pushButton_rotate->setChecked(false);
    }
}
#ifndef MATERIAL_H
#define MATERIAL_H

#include <QVector4D>

#define MATERIAL_COLOR_COUNT 6

struct Material {
    Material();
    QVector4D Color[MATERIAL_COLOR_COUNT];
};

#endif    // MATERIAL_H
#include "Material.h"

#include <QRandomGenerator>

Material::Material()
{
    std::uniform_real_distribution<float> randColor(0.0, 1.0);
    for (int i = 0; i < MATERIAL_COLOR_COUNT; i++) {
        Color[i] = QVector4D(randColor(*QRandomGenerator::global()), randColor(*QRandomGenerator::global()),
                             randColor(*QRandomGenerator::global()), 1.0f);
    }
}
#ifndef MESH_H
#define MESH_H

#include <QVector3D>
#include <QVector4D>
#include <QOpenGLFunctions>

struct VerticeInfo {
    QVector3D pos;
    QVector4D color;
};

class Mesh
{
public:
    explicit Mesh(const QVector<VerticeInfo> &vertices, const QVector<GLushort> &indices);

    const QVector<VerticeInfo> &getVertices();
    const QVector<GLushort> &getIndices();

private:
    QVector<VerticeInfo> m_vertices;
    QVector<GLushort> m_indices;
};

#endif    // MESH_H
#include "Mesh.h"

Mesh::Mesh(const QVector<VerticeInfo> &vertices, const QVector<GLushort> &indices) :
    m_vertices(vertices),
    m_indices(indices)
{
}

const QVector<VerticeInfo> &Mesh::getVertices()
{
    return m_vertices;
}

const QVector<GLushort> &Mesh::getIndices()
{
    return m_indices;
}
#ifndef SCENEMANAGER_H
#define SCENEMANAGER_H

#include <QOpenGLWidget>
#include <QOpenGLFunctions>
#include <QOpenGLShaderProgram>
#include <QOpenGLBuffer>
#include <QOpenGLDebugLogger>
#include <QKeyEvent>
#include <QString>

#include <unordered_map>

class Shape;

enum class CameraState { NONE, ROTATE, PAN, ZOOM };

struct Camera {
    QVector3D Position;
    QVector3D LookAt;
    QVector3D Up;
    QVector3D Right;
    float FOV;
    CameraState State;
};

namespace Ui
{
class SceneManager;
}

class SceneManager : public QOpenGLWidget, protected QOpenGLFunctions
{
    Q_OBJECT

public:
    explicit SceneManager(QWidget *parent = nullptr);
    ~SceneManager();

signals:
    void UpdateStatusLabel(const QString &msg);

public slots:
    void onCreateCube();
    void onPanToggled(bool checked);
    void onRotateToggled(bool checked);
    void onZoomToggled(bool checked);
protected slots:
    void keyPressEvent(QKeyEvent *e) override;
    void mousePressEvent(QMouseEvent *e) override;
    void wheelEvent(QWheelEvent *event) override;
    void PrintLoggedMessage(const QOpenGLDebugMessage &debugMessage);

protected:
    void InitalizeShaders();
    void InitalizeBuffers();
    void initializeGL() override;
    void paintGL() override;

private:
    Ui::SceneManager *ui;
    QOpenGLDebugLogger m_logger;
    QOpenGLShaderProgram m_program;
    std::unordered_map<QString, Shape *> m_shapes;
    QMatrix4x4 m_projection;
    QMatrix4x4 m_view;
    QOpenGLBuffer m_arrayBuf;
    QOpenGLBuffer m_indexBuf;
    Camera m_camera;
    Shape *m_selected_shape;

    const float m_near_z = 2.0f;
    const float m_far_z = 200.0f;
    const float m_default_fov = 30.0f;
    const float m_rotation_speed_scalar = 2.0f;

    void renderAll();
    Shape *pickShape(int x, int y);
    Shape *createShape(const QString &type, QString &id);
    void PanViewport(int key);
    void ZoomViewport(int key);
    void RotateViewport(int key);

    void CastRayFromScreenToWorld(int mouseX, int mouseY, QVector3D &out_origin, QVector3D &out_direction);
    bool RayIntersectionTest(Shape *shape, const QVector3D &ray_origin, const QVector3D &ray_direction);
};

#endif    // SCENEMANAGER_H
#include "SceneManager.h"
#include "ui_SceneManager.h"

#include <QOpenGLContext>
#include <QOpenGLFramebufferObject>
#include <QRandomGenerator>
#include <QUuid>
#include <QVector3D>
#include <QVector4D>
#include <QDebug>
#include "Cube.h"

SceneManager::SceneManager(QWidget *parent) :
    QOpenGLWidget(parent),
    ui(new Ui::SceneManager),
    m_arrayBuf(QOpenGLBuffer::VertexBuffer),
    m_indexBuf(QOpenGLBuffer::IndexBuffer),
    m_selected_shape(nullptr)
{
    ui->setupUi(this);
    connect(&m_logger, &QOpenGLDebugLogger::messageLogged, this, &SceneManager::PrintLoggedMessage);
    m_camera.Position = QVector3D(-30.0f, 30.0f, 40.0f);
    m_camera.LookAt = QVector3D(0.0f, 0.0f, 0.0f);
    QVector3D dir = (m_camera.Position - m_camera.LookAt).normalized();
    m_camera.Right = QVector3D::crossProduct(QVector3D(0.0f, 1.0f, 0.0f), dir).normalized();
    m_camera.Up = QVector3D::crossProduct(dir, m_camera.Right).normalized();
    m_camera.FOV = m_default_fov;
    m_camera.State = CameraState::NONE;
}

SceneManager::~SceneManager()
{
    makeCurrent();
    m_logger.stopLogging();
    m_arrayBuf.destroy();
    m_indexBuf.destroy();
    m_program.release();
    doneCurrent();
    for (const auto &shape : m_shapes) {
        if (shape.second) {
            delete shape.second;
        }
    }
    delete ui;
}

void SceneManager::renderAll()
{
    // Clear color and depth buffer
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    for (const auto &shape : m_shapes) {
        Shape *cube = shape.second;
        auto vertices = cube->getMesh()->getVertices();
        auto indices = cube->getMesh()->getIndices();

        // Update vertex and index buffers
        m_arrayBuf.write(0, vertices.data(), (int)vertices.size() * sizeof(VerticeInfo));
        m_indexBuf.write(0, indices.data(), (int)indices.size() * sizeof(GLushort));

        // Model View Projection
        static const QVector3D up(0.0f, 1.0f, 0.0f);
        QVector3D dir = (m_camera.Position - m_camera.LookAt).normalized();
        m_camera.Right = QVector3D::crossProduct(up, dir).normalized();
        m_camera.Up = QVector3D::crossProduct(dir, m_camera.Right).normalized();
        m_view.setToIdentity();
        m_view.lookAt(m_camera.Position, m_camera.LookAt, m_camera.Up);

        // Let GPU do the calculation of the final mvp
        m_program.setUniformValue("u_proj", m_projection);
        m_program.setUniformValue("u_view", m_view);
        m_program.setUniformValue("u_trans", cube->getTransformation());

        int offset = 0;
        // Vertex positions
        int vertexLocation = m_program.attributeLocation("a_position");
        Q_ASSERT(vertexLocation != -1);
        m_program.enableAttributeArray(vertexLocation);
        m_program.setAttributeBuffer(vertexLocation, GL_FLOAT, 0, 3, sizeof(VerticeInfo));

        offset += (int)sizeof(QVector3D);

        // Colors for the surfaces
        int colorLocation = m_program.attributeLocation("a_color");
        Q_ASSERT(colorLocation != -1);
        m_program.enableAttributeArray(colorLocation);
        m_program.setAttributeBuffer(colorLocation, GL_FLOAT, offset, 4, sizeof(VerticeInfo));

        // Draw the shape
        glDrawElements(GL_TRIANGLE_STRIP, indices.size(), GL_UNSIGNED_SHORT, nullptr);
    }

    if (m_selected_shape) {
        // Draw x-y-z axes of the selected shape from its center
        m_program.setUniformValue("u_trans", m_selected_shape->getTransformation());

        static const QVector<VerticeInfo> vertices = { { QVector3D(0.0f, 0.0f, 0.0f), QVector4D(1.0f, 0.0f, 0.0f, 1.0f) },
                                                       { QVector3D(6.0f, 0.0f, 0.0f), QVector4D(1.0f, 0.0f, 0.0f, 1.0f) },
                                                       { QVector3D(0.0f, 0.0f, 0.0f), QVector4D(0.0f, 1.0f, 0.0f, 1.0f) },
                                                       { QVector3D(0.0f, 6.0f, 0.0f), QVector4D(0.0f, 1.0f, 0.0f, 1.0f) },
                                                       { QVector3D(0.0f, 0.0f, 0.0f), QVector4D(0.0f, 0.0f, 1.0f, 1.0f) },
                                                       { QVector3D(0.0f, 0.0f, 6.0f), QVector4D(0.0f, 0.0f, 1.0f, 1.0f) } };

        m_arrayBuf.write(0, vertices.data(), (int)vertices.size() * sizeof(VerticeInfo));

        glDrawArrays(GL_LINES, 0, vertices.size());
    }
}

Shape *SceneManager::pickShape(int mouse_x, int mouse_y)
{
    QVector3D ray_origin;
    QVector3D ray_direction;
    CastRayFromScreenToWorld(mouse_x, this->height() - mouse_y, ray_origin, ray_direction);

    for (const auto &shape : m_shapes) {
        if (RayIntersectionTest(shape.second, ray_origin, ray_direction)) {
            return shape.second;
        }
    }

    return nullptr;
}

Shape *SceneManager::createShape(const QString &type, QString &id)
{
    if (type == "Cube") {
        id = QUuid::createUuid().toString(QUuid::WithoutBraces);
        Shape *newShape = new Cube(id);
        return newShape;
    }
    return nullptr;
}

void SceneManager::onCreateCube()
{
    QString id;
    Shape *newShape = createShape("Cube", id);
    if (newShape) {
        m_shapes[id] = newShape;
        qDebug() << " new cube id = " << id;
    }
    update();
}

void SceneManager::onPanToggled(bool checked)
{
    m_camera.State = checked ? CameraState::PAN : CameraState::NONE;
}

void SceneManager::onRotateToggled(bool checked)
{
    m_camera.State = checked ? CameraState::ROTATE : CameraState::NONE;
}

void SceneManager::onZoomToggled(bool checked)
{
    m_camera.State = checked ? CameraState::ZOOM : CameraState::NONE;
}

void SceneManager::keyPressEvent(QKeyEvent *event)
{
    auto key = event->key();
    switch (m_camera.State) {
    case CameraState::PAN:
        PanViewport(key);
        break;
    case CameraState::ZOOM:
        ZoomViewport(key);
        break;
    case CameraState::ROTATE:
        RotateViewport(key);
        break;
    default:
        break;
    }
}

void SceneManager::mousePressEvent(QMouseEvent *e)
{
    Shape *cube = pickShape(e->pos().x(), e->pos().y());
    if (!cube) {
        m_selected_shape = nullptr;
        emit UpdateStatusLabel("Nothing is selected.");
    } else {
        m_selected_shape = cube;
        emit UpdateStatusLabel("Cube is selected.");
    }
    update();
}

void SceneManager::wheelEvent(QWheelEvent *event)
{
    if (event->angleDelta().y() > 0) {
        // Zoom in
        ZoomViewport(Qt::Key_Up);
    } else if (event->angleDelta().y() < 0) {
        // Zoom out
        ZoomViewport(Qt::Key_Down);
    }
}

void SceneManager::InitalizeShaders()
{
    // Compile vertex shader
    if (!m_program.addShaderFromSourceFile(QOpenGLShader::Vertex, ":/vertex.glsl")) {
        qDebug() << "SceneManager::InitalizeShaders: Failed to compile vertex shader!";
        close();
    }

    // Compile fragment shader
    if (!m_program.addShaderFromSourceFile(QOpenGLShader::Fragment, ":/fragment.glsl")) {
        qDebug() << "SceneManager::InitalizeShaders: Failed to compile fragment shader!";
        close();
    }

    // Link shader pipeline
    if (!m_program.link()) {
        qDebug() << "SceneManager::InitalizeShaders: Failed to link shaders!";
        close();
    }

    // Bind shader pipeline for use
    if (!m_program.bind()) {
        qDebug() << "SceneManager::InitalizeShaders: Failed to bind shader program!";
        close();
    }
}

void SceneManager::InitalizeBuffers()
{
    if (!m_arrayBuf.create())
        close();
    if (!m_arrayBuf.bind())
        close();
    if (!m_indexBuf.create())
        close();
    if (!m_indexBuf.bind())
        close();

    m_arrayBuf.allocate(nullptr, 24 * sizeof(VerticeInfo));
    m_indexBuf.allocate(nullptr, 34 * sizeof(GLushort));
}

void SceneManager::initializeGL()
{
    qDebug() << Q_FUNC_INFO << ": initializing GL...";
    initializeOpenGLFunctions();
    qDebug() << "OpenGL Version: " << QOpenGLContext::currentContext()->format().majorVersion()
             << QOpenGLContext::currentContext()->format().minorVersion();

    glEnable(GL_DEBUG_OUTPUT);
    m_logger.initialize();
    m_logger.startLogging();

    InitalizeShaders();
    InitalizeBuffers();

    // Enable depth buffer
    glEnable(GL_DEPTH_TEST);
    // Enable back face culling
    glEnable(GL_CULL_FACE);

    glClearColor(0.8f, 0.8f, 0.8f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    m_projection.setToIdentity();
    m_projection.perspective(m_camera.FOV, (float)this->width() / (float)this->height(), m_near_z, m_far_z);
}

void SceneManager::paintGL()
{
    renderAll();
}

void SceneManager::PanViewport(int key)
{
    switch (key) {
    case Qt::Key_Up:
        m_camera.Position += m_camera.Up;
        m_camera.LookAt += m_camera.Up;
        break;
    case Qt::Key_Down:
        m_camera.Position -= m_camera.Up;
        m_camera.LookAt -= m_camera.Up;
        break;
    case Qt::Key_Right:
        m_camera.Position += m_camera.Right;
        m_camera.LookAt += m_camera.Right;
        break;
    case Qt::Key_Left:
        m_camera.Position -= m_camera.Right;
        m_camera.LookAt -= m_camera.Right;
        break;
    default:
        return;
    }
    update();
}

void SceneManager::ZoomViewport(int key)
{
    switch (key) {
    case Qt::Key_Up:
        m_camera.FOV -= 2.0f;
        if (m_camera.FOV < 2.0f) {
            m_camera.FOV = 2.0f;
        }
        break;
    case Qt::Key_Down:
        m_camera.FOV += 2.0f;
        if (m_camera.FOV > 178.0f) {
            m_camera.FOV = 178.0f;
        }
        break;
    default:
        return;
    }
    // Reset perspective projection
    m_projection.setToIdentity();
    m_projection.perspective(m_camera.FOV, (float)this->width() / (float)this->height(), m_near_z, m_far_z);
    update();
}

void SceneManager::RotateViewport(int key)
{
    switch (key) {
    // Make sure up and down rotations wont flip the scene
    case Qt::Key_Up:
        if (((m_camera.Position.z() > 0.0f) && ((m_camera.Position.z() + m_camera.Up.z() * m_rotation_speed_scalar) > 0.0f)) ||
            ((m_camera.Position.z() < 0.0f) && ((m_camera.Position.z() + m_camera.Up.z() * m_rotation_speed_scalar) < 0.0f))) {
            m_camera.Position += m_camera.Up * m_rotation_speed_scalar;
        }
        break;
    case Qt::Key_Down:
        if (((m_camera.Position.z() > 0.0f) && ((m_camera.Position.z() - m_camera.Up.z() * m_rotation_speed_scalar) > 0.0f)) ||
            ((m_camera.Position.z() < 0.0f) && ((m_camera.Position.z() - m_camera.Up.z() * m_rotation_speed_scalar) < 0.0f))) {
            m_camera.Position -= m_camera.Up * m_rotation_speed_scalar;
        }
        break;
    case Qt::Key_Right:
        m_camera.Position += m_camera.Right * m_rotation_speed_scalar;
        break;
    case Qt::Key_Left:
        m_camera.Position -= m_camera.Right * m_rotation_speed_scalar;
        break;
    default:
        return;
    }
    update();
}

void SceneManager::CastRayFromScreenToWorld(int mouseX, int mouseY, QVector3D &out_origin, QVector3D &out_direction)
{
    // The ray Start and End positions, in Normalized Device Coordinates
    QVector4D lRayStart_NDC(((float)mouseX / (float)this->width() - 0.5f) * 2.0f, ((float)mouseY / (float)this->height() - 0.5f) * 2.0f,
                            -1.0,    // The near plane maps to Z=-1 in Normalized Device Coordinates
                            1.0f);
    QVector4D lRayEnd_NDC(((float)mouseX / (float)this->width() - 0.5f) * 2.0f, ((float)mouseY / (float)this->height() - 0.5f) * 2.0f, 0.0,
                          1.0f);

    // The Projection matrix goes from Camera Space to NDC.
    // So inverse(ProjectionMatrix) goes from NDC to Camera Space.
    QMatrix4x4 InverseProjectionMatrix = m_projection.inverted();

    // The View Matrix goes from World Space to Camera Space.
    // So inverse(ViewMatrix) goes from Camera Space to World Space.
    QMatrix4x4 InverseViewMatrix = m_view.inverted();

    QVector4D lRayStart_camera = InverseProjectionMatrix * lRayStart_NDC;
    lRayStart_camera /= lRayStart_camera.w();
    QVector4D lRayStart_world = InverseViewMatrix * lRayStart_camera;
    lRayStart_world /= lRayStart_world.w();
    QVector4D lRayEnd_camera = InverseProjectionMatrix * lRayEnd_NDC;
    lRayEnd_camera /= lRayEnd_camera.w();
    QVector4D lRayEnd_world = InverseViewMatrix * lRayEnd_camera;
    lRayEnd_world /= lRayEnd_world.w();

    QVector3D lRayDir_world(lRayEnd_world - lRayStart_world);
    lRayDir_world = lRayDir_world.normalized();

    out_origin = QVector3D(lRayStart_world);
    out_direction = lRayDir_world.normalized();
}

bool SceneManager::RayIntersectionTest(Shape *shape, const QVector3D &ray_origin, const QVector3D &ray_direction)
{
    // Intersection method from Real-Time Rendering and Essential Mathematics for Games
    float tMin = 0.0f;
    float tMax = 100000.0f;

    QVector3D OBBposition_worldspace(shape->getTransformation().column(3).x(), shape->getTransformation().column(3).y(),
                                     shape->getTransformation().column(3).z());

    QVector3D delta = OBBposition_worldspace - ray_origin;
    static const QVector3D aabb_min(-1.0f, -1.0f, -1.0f);
    static const QVector3D aabb_max(1.0f, 1.0f, 1.0f);

    // Test intersection with the 2 planes perpendicular to the OBB's X axis
    {
        QVector3D xaxis(shape->getTransformation().column(0).x(), shape->getTransformation().column(0).y(),
                        shape->getTransformation().column(0).z());
        float e = QVector3D::dotProduct(xaxis, delta);
        float f = QVector3D::dotProduct(ray_direction, xaxis);

        if (fabs(f) > 0.001f) {    // Standard case

            float t1 = (e + aabb_min.x()) / f;    // Intersection with the "left" plane
            float t2 = (e + aabb_max.x()) / f;    // Intersection with the "right" plane
            // t1 and t2 now contain distances betwen ray origin and ray-plane intersections

            // We want t1 to represent the nearest intersection,
            // so if it's not the case, invert t1 and t2
            if (t1 > t2) {
                float w = t1;
                t1 = t2;
                t2 = w;    // swap t1 and t2
            }

            // tMax is the nearest "far" intersection (amongst the X,Y and Z planes pairs)
            if (t2 < tMax)
                tMax = t2;
            // tMin is the farthest "near" intersection (amongst the X,Y and Z planes pairs)
            if (t1 > tMin)
                tMin = t1;

            // If "far" is closer than "near", then there is NO intersection.
            if (tMax < tMin)
                return false;

        } else {    // Rare case : the ray is almost parallel to the planes, so they don't have any "intersection"
            if (-e + aabb_min.x() > 0.0f || -e + aabb_max.x() < 0.0f)
                return false;
        }
    }

    // Test intersection with the 2 planes perpendicular to the OBB's Y axis
    {
        QVector3D yaxis(shape->getTransformation().column(1).x(), shape->getTransformation().column(1).y(),
                        shape->getTransformation().column(1).z());
        float e = QVector3D::dotProduct(yaxis, delta);
        float f = QVector3D::dotProduct(ray_direction, yaxis);

        if (fabs(f) > 0.001f) {

            float t1 = (e + aabb_min.y()) / f;
            float t2 = (e + aabb_max.y()) / f;

            if (t1 > t2) {
                float w = t1;
                t1 = t2;
                t2 = w;
            }

            if (t2 < tMax)
                tMax = t2;
            if (t1 > tMin)
                tMin = t1;
            if (tMin > tMax)
                return false;

        } else {
            if (-e + aabb_min.y() > 0.0f || -e + aabb_max.y() < 0.0f)
                return false;
        }
    }

    // Test intersection with the 2 planes perpendicular to the OBB's Z axis
    {
        QVector3D zaxis(shape->getTransformation().column(2).x(), shape->getTransformation().column(2).y(),
                        shape->getTransformation().column(2).z());
        float e = QVector3D::dotProduct(zaxis, delta);
        float f = QVector3D::dotProduct(ray_direction, zaxis);

        if (fabs(f) > 0.001f) {

            float t1 = (e + aabb_min.z()) / f;
            float t2 = (e + aabb_max.z()) / f;

            if (t1 > t2) {
                float w = t1;
                t1 = t2;
                t2 = w;
            }

            if (t2 < tMax)
                tMax = t2;
            if (t1 > tMin)
                tMin = t1;
            if (tMin > tMax)
                return false;

        } else {
            if (-e + aabb_min.z() > 0.0f || -e + aabb_max.z() < 0.0f)
                return false;
        }
    }

    return true;
}

void SceneManager::PrintLoggedMessage(const QOpenGLDebugMessage &debugMessage)
{
    qDebug() << debugMessage.message();
}
#ifndef SHAPE_H
#define SHAPE_H

#include "Mesh.h"
#include "Material.h"
#include <QMatrix4x4>
#include <QString>

#include <memory>

class Shape
{
public:
    virtual ~Shape() = default;
    virtual std::shared_ptr<Mesh> getMesh() = 0;
    virtual std::shared_ptr<Material> getMaterial() = 0;
    virtual QMatrix4x4 &getTransformation() = 0;
    virtual QString ID() const = 0;
};

#endif    // SHAPE_H
#version 330

in highp vec4 color;

out highp vec4 fragColor;

void main(void)
{
   fragColor = color;
}
#version 330

in highp vec3 a_position;
in highp vec4 a_color;

uniform highp mat4 u_proj;
uniform highp mat4 u_view;
uniform highp mat4 u_trans;

out highp vec4 color;

void main(void)
{
   gl_Position = u_proj * u_view * u_trans * vec4(a_position, 1.0);
   color = a_color;
}

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值