#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;
}