Initial commit
|
@ -1,3 +1,9 @@
|
||||||
|
# CLion
|
||||||
|
**/.idea/**
|
||||||
|
|
||||||
|
# CMake build files
|
||||||
|
**/cmake-build-debug/**
|
||||||
|
|
||||||
# C++ objects and libs
|
# C++ objects and libs
|
||||||
*.slo
|
*.slo
|
||||||
*.lo
|
*.lo
|
||||||
|
|
|
@ -0,0 +1,152 @@
|
||||||
|
################################################################################
|
||||||
|
## Author: Shaun Reed | Contact: shaunrd0@gmail.com | URL: www.shaunreed.com ##
|
||||||
|
## ##
|
||||||
|
## Project for working with OpenGL and Qt5 widgets ##
|
||||||
|
################################################################################
|
||||||
|
cmake_minimum_required(VERSION 3.5)
|
||||||
|
|
||||||
|
project(
|
||||||
|
#[[NAME]] Qtk
|
||||||
|
VERSION 1.0
|
||||||
|
DESCRIPTION "An example project using QT and OpenGL"
|
||||||
|
LANGUAGES CXX
|
||||||
|
)
|
||||||
|
|
||||||
|
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||||
|
set(CMAKE_AUTOUIC ON)
|
||||||
|
set(CMAKE_AUTOMOC ON)
|
||||||
|
set(CMAKE_AUTORCC ON)
|
||||||
|
set(CMAKE_CXX_STANDARD 11)
|
||||||
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
|
||||||
|
# Find all Qt package components required for this project
|
||||||
|
find_package(Qt5 COMPONENTS Core LinguistTools Gui Widgets REQUIRED)
|
||||||
|
|
||||||
|
# Add our Qt resources.qrc file to our application
|
||||||
|
set(SOURCES app/main.cpp)
|
||||||
|
qt5_add_resources(SOURCES resources.qrc)
|
||||||
|
|
||||||
|
# Set translation files
|
||||||
|
set(TS_FILES qtk_en_US.ts)
|
||||||
|
|
||||||
|
add_executable(
|
||||||
|
qtk # Executable name
|
||||||
|
${SOURCES} # Executable source code
|
||||||
|
${TS_FILES} # Link translation files
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# External Libraries
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
# Find and link GLUT package; Otherwise show an error
|
||||||
|
find_package(GLUT REQUIRED)
|
||||||
|
if (!GLUT_FOUND)
|
||||||
|
message(
|
||||||
|
"Error: CMake was unable to find the GLUT package\n"
|
||||||
|
"Please install GLUT (freeglut3-dev) and try again\n"
|
||||||
|
"sudo apt install freeglut3-dev\n"
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Find and link OpenGL package; Otherwise show an error
|
||||||
|
set(OpenGL_GL_PREFERENCE LEGACY)
|
||||||
|
find_package(OpenGL REQUIRED)
|
||||||
|
if (!OPENGL_FOUND)
|
||||||
|
message(
|
||||||
|
"Error: CMake was unable to find the OpenGL package\n"
|
||||||
|
"Please install OpenGL and try again\n"
|
||||||
|
"sudo apt install mesa-utils\n"
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# https://github.com/assimp/assimp/commit/6ac8279977c3a54118551e549d77329497116f66
|
||||||
|
find_package(assimp REQUIRED)
|
||||||
|
if (!assimp_FOUND)
|
||||||
|
message(
|
||||||
|
"Error: CMake was unable to find the Assimp package\n"
|
||||||
|
"Please install Assimp and try again\n"
|
||||||
|
"sudo apt install libassimp-dev\n"
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# Custom Libraries
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
# Mainwidget
|
||||||
|
add_library(main-widget lib/mainwidget.cpp)
|
||||||
|
target_include_directories(main-widget PUBLIC lib/)
|
||||||
|
#target_link_libraries(main-widget PUBLIC Qt5::Widgets)
|
||||||
|
# + This lib and all linked targets will also link to OpenGL
|
||||||
|
target_include_directories(main-widget PUBLIC ${OPENGL_INCLUDE_DIR})
|
||||||
|
target_link_libraries(main-widget PUBLIC ${OPENGL_LIBRARIES})
|
||||||
|
|
||||||
|
# Model
|
||||||
|
add_library(model lib/model.cpp)
|
||||||
|
target_include_directories(model PRIVATE ${ASSIMP_INCLUDE_DIR})
|
||||||
|
target_link_libraries(model PRIVATE ${ASSIMP_LIBRARIES})
|
||||||
|
target_link_libraries(model PUBLIC Qt5::Widgets)
|
||||||
|
target_link_libraries(model PRIVATE main-widget)
|
||||||
|
|
||||||
|
# Input
|
||||||
|
add_library(input lib/input.cpp)
|
||||||
|
target_include_directories(input PUBLIC lib/)
|
||||||
|
target_link_libraries(input PUBLIC Qt5::Widgets)
|
||||||
|
|
||||||
|
# Mesh
|
||||||
|
add_library(mesh lib/mesh.cpp)
|
||||||
|
target_include_directories(mesh PUBLIC lib/)
|
||||||
|
target_link_libraries(mesh PUBLIC Qt5::Widgets)
|
||||||
|
|
||||||
|
# Transform3D
|
||||||
|
add_library(transform3d lib/transform3D.cpp)
|
||||||
|
target_include_directories(transform3d PUBLIC lib/)
|
||||||
|
target_link_libraries(transform3d PUBLIC Qt5::Widgets)
|
||||||
|
|
||||||
|
# Camera3D
|
||||||
|
add_library(camera3d lib/camera3d.cpp)
|
||||||
|
target_include_directories(camera3d PUBLIC lib/)
|
||||||
|
target_link_libraries(camera3d PUBLIC Qt5::Widgets)
|
||||||
|
|
||||||
|
# Texture
|
||||||
|
add_library(texture lib/texture.cpp)
|
||||||
|
target_include_directories(texture PUBLIC lib/)
|
||||||
|
target_link_libraries(texture PUBLIC Qt5::Widgets)
|
||||||
|
|
||||||
|
# Object
|
||||||
|
add_library(object lib/object.cpp)
|
||||||
|
target_include_directories(object PUBLIC lib/)
|
||||||
|
target_link_libraries(object PUBLIC Qt5::Widgets)
|
||||||
|
|
||||||
|
# MeshRenderer
|
||||||
|
add_library(meshrenderer lib/meshrenderer.cpp)
|
||||||
|
target_include_directories(meshrenderer PUBLIC lib/)
|
||||||
|
target_link_libraries(meshrenderer PUBLIC Qt5::Widgets)
|
||||||
|
|
||||||
|
# Skybox
|
||||||
|
add_library(skybox lib/skybox.cpp)
|
||||||
|
target_link_libraries(skybox PUBLIC Qt5::Widgets)
|
||||||
|
target_link_libraries(skybox PRIVATE mesh)
|
||||||
|
target_link_libraries(skybox PRIVATE camera3d)
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# Final Application
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
target_link_libraries(main-widget PUBLIC model)
|
||||||
|
target_link_libraries(main-widget PUBLIC input)
|
||||||
|
target_link_libraries(main-widget PUBLIC transform3d)
|
||||||
|
target_link_libraries(main-widget PUBLIC object)
|
||||||
|
target_link_libraries(main-widget PUBLIC meshrenderer)
|
||||||
|
target_link_libraries(main-widget PUBLIC texture)
|
||||||
|
target_link_libraries(main-widget PUBLIC skybox)
|
||||||
|
target_link_libraries(main-widget PUBLIC mesh)
|
||||||
|
|
||||||
|
# Link qtk executable to main main-widget library
|
||||||
|
target_link_libraries(qtk PUBLIC main-widget)
|
||||||
|
|
||||||
|
# Set up QT Linguist translation
|
||||||
|
qt5_create_translation(QM_FILES ${CMAKE_SOURCE_DIR} ${TS_FILES})
|
60
README.md
|
@ -1,2 +1,58 @@
|
||||||
# qtk
|
# Qtk
|
||||||
Practice using OpenGL in Qt5 widget application
|
|
||||||
|
Practice project for learning about using OpenGL in Qt5 widget applications.
|
||||||
|
Model loader using [Assimp](https://assimp.org/).
|
||||||
|
|
||||||
|
You can fly around and see the examples I made, or import your own models within
|
||||||
|
`mainwdget.cpp`, inside the `MainWidget::initObjects()` function. I've commented
|
||||||
|
throughout the code there to explain which model or example I'm modifying.
|
||||||
|
Rotations and translations happen in `MainWidget::update()`, to get textures
|
||||||
|
loading on models look into
|
||||||
|
[material files](http://www.paulbourke.net/dataformats/mtl/) and see some
|
||||||
|
examples in the `resources/models/` directory.
|
||||||
|
|
||||||
|
Can be built with cmake manually or using
|
||||||
|
[Qt Creator](https://github.com/qt-creator/qt-creator).
|
||||||
|
|
||||||
|
To build and run `qtk` on Ubuntu -
|
||||||
|
```bash
|
||||||
|
# Qt Creator
|
||||||
|
sudo apt update -y && sudo apt install qttools5-dev freeglut3-dev libassimp-dev
|
||||||
|
git clone https://gitlab.com/shaunrd0/qtk
|
||||||
|
mkdir build && cd build
|
||||||
|
cmake .. && cmake --build
|
||||||
|
./qtk
|
||||||
|
```
|
||||||
|
|
||||||
|
You can fly around the scene if you hold the right mouse button and use WASD.
|
||||||
|
If you see a small triangle floating by a model it represents the light source
|
||||||
|
that is being used for the shader rendering the model. These appear on models
|
||||||
|
using phong, specular, and diffuse lighting techniques.
|
||||||
|
|
||||||
|
![](resources/screenshot.png)
|
||||||
|
|
||||||
|
Spartan with no normals -
|
||||||
|
|
||||||
|
![](resources/spartan-specular.png)
|
||||||
|
|
||||||
|
Spartan with normals -
|
||||||
|
|
||||||
|
![](resources/spartan-normals.png)
|
||||||
|
|
||||||
|
## Model Artists
|
||||||
|
|
||||||
|
|
||||||
|
"Alien Hominid" (https://skfb.ly/onStx) by Nwilly_art is licensed under Creative Commons Attribution (http://creativecommons.org/licenses/by/4.0/).
|
||||||
|
|
||||||
|
"Scythe World Of Warcraft" (https://skfb.ly/6UooG) by Warcraft-3D-Models is licensed under Creative Commons Attribution (http://creativecommons.org/licenses/by/4.0/).
|
||||||
|
|
||||||
|
"Spartan Armour MKV - Halo Reach" () by McCarthy3D is licensed under Creative Commons Attribution (http://creativecommons.org/licenses/by/4.0/).
|
||||||
|
|
||||||
|
"Survival Guitar Backpack (Low Poly)" (https://skfb.ly/6RnCB) by Berk Gedik is licensed under Creative Commons Attribution (http://creativecommons.org/licenses/by/4.0/).
|
||||||
|
Model by Berk Gedik, from: https://sketchfab.com/3d-models/survival-guitar-backpack-low-poly-799f8c4511f84fab8c3f12887f7e6b36
|
||||||
|
Modified material assignment (Joey de Vries) for easier load in OpenGL model loading chapter, and renamed albedo to diffuse and metallic to specular to match non-PBR lighting setup.
|
||||||
|
|
||||||
|
"Terror-bird (NHMW-Geo 2012/0007/0001)" (https://skfb.ly/onAWy) by Natural History Museum Vienna is licensed under Creative Commons Attribution-NonCommercial (http://creativecommons.org/licenses/by-nc/4.0/).
|
||||||
|
|
||||||
|
"Golden Lion Sitting OBJ Low Poly FREE" (https://skfb.ly/onZAH) by LordSamueliSolo is licensed under Creative Commons Attribution (http://creativecommons.org/licenses/by/4.0/).
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
/*##############################################################################
|
||||||
|
## Author: Shaun Reed ##
|
||||||
|
## Legal: All Content (c) 2021 Shaun Reed, all rights reserved ##
|
||||||
|
## About: Main program for practice using Qt5 widgets and OpenGL ##
|
||||||
|
## ##
|
||||||
|
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
|
||||||
|
##############################################################################*/
|
||||||
|
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QLabel>
|
||||||
|
|
||||||
|
#include <mainwidget.h>
|
||||||
|
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
QApplication a(argc, argv);
|
||||||
|
|
||||||
|
// Set OpenGL Version information
|
||||||
|
// Note: This format must be set before show() is called.
|
||||||
|
QSurfaceFormat format;
|
||||||
|
format.setRenderableType(QSurfaceFormat::OpenGL);
|
||||||
|
format.setProfile(QSurfaceFormat::CoreProfile);
|
||||||
|
format.setVersion(4,5);
|
||||||
|
// Set the number of samples used for glEnable(GL_MULTISAMPLING)
|
||||||
|
format.setSamples(4);
|
||||||
|
// Set the size of teh depth bufer for glEnable(GL_DEPTH_TEST)
|
||||||
|
format.setDepthBufferSize(16);
|
||||||
|
#ifdef QTK_DEBUG
|
||||||
|
format.setOption(QSurfaceFormat::DebugContext);
|
||||||
|
#endif // QTK_DEBUG
|
||||||
|
|
||||||
|
// Set the widget up using a custom format
|
||||||
|
MainWidget widget(format);
|
||||||
|
|
||||||
|
return a.exec();
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,58 @@
|
||||||
|
/*##############################################################################
|
||||||
|
## Author: Shaun Reed ##
|
||||||
|
## Legal: All Content (c) 2021 Shaun Reed, all rights reserved ##
|
||||||
|
## About: Fly camera class from tutorials followed at trentreed.net ##
|
||||||
|
## ##
|
||||||
|
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
|
||||||
|
##############################################################################*/
|
||||||
|
|
||||||
|
#include <camera3d.h>
|
||||||
|
|
||||||
|
|
||||||
|
const QVector3D Camera3D::LocalForward(0.0f, 0.0f, -1.0f);
|
||||||
|
const QVector3D Camera3D::LocalUp(0.0f, 1.0f, 0.0f);
|
||||||
|
const QVector3D Camera3D::LocalRight(1.0f, 0.0f, 0.0f);
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
* Accessors
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
// Produces worldToView matrix
|
||||||
|
const QMatrix4x4 & Camera3D::toMatrix()
|
||||||
|
{
|
||||||
|
mWorld.setToIdentity();
|
||||||
|
mWorld.rotate(mTransform.rotation().conjugate());
|
||||||
|
mWorld.translate(-mTransform.translation());
|
||||||
|
return mWorld;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
* Qt Streams
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
QDataStream & operator<<(QDataStream & out, Camera3D & transform)
|
||||||
|
{
|
||||||
|
out << transform.transform();
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
QDataStream & operator>>(QDataStream & in, Camera3D & transform)
|
||||||
|
{
|
||||||
|
in >> transform.transform();
|
||||||
|
return in;
|
||||||
|
}
|
||||||
|
|
||||||
|
QDebug operator<<(QDebug dbg, const Camera3D & transform)
|
||||||
|
{
|
||||||
|
dbg << "Camera3D\n{\n";
|
||||||
|
dbg << "Position: <" << transform.translation().x() << ", "
|
||||||
|
<< transform.translation().y() << ", "
|
||||||
|
<< transform.translation().z() << ">\n";
|
||||||
|
dbg << "Rotation: <" << transform.rotation().x() << ", "
|
||||||
|
<< transform.rotation().y() << ", "
|
||||||
|
<< transform.rotation().z() << " | "
|
||||||
|
<< transform.rotation().scalar() << ">\n}";
|
||||||
|
return dbg;
|
||||||
|
}
|
|
@ -0,0 +1,62 @@
|
||||||
|
/*##############################################################################
|
||||||
|
## Author: Shaun Reed ##
|
||||||
|
## Legal: All Content (c) 2021 Shaun Reed, all rights reserved ##
|
||||||
|
## About: Fly camera class from tutorials followed at trentreed.net ##
|
||||||
|
## ##
|
||||||
|
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
|
||||||
|
##############################################################################*/
|
||||||
|
|
||||||
|
#ifndef QTK_CAMERA3D_H
|
||||||
|
#define QTK_CAMERA3D_H
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
#include <transform3D.h>
|
||||||
|
|
||||||
|
|
||||||
|
class Camera3D {
|
||||||
|
public:
|
||||||
|
// Constants
|
||||||
|
static const QVector3D LocalForward;
|
||||||
|
static const QVector3D LocalUp;
|
||||||
|
static const QVector3D LocalRight;
|
||||||
|
|
||||||
|
// Accessors
|
||||||
|
inline Transform3D & transform() { return mTransform;}
|
||||||
|
inline const QVector3D & translation() const
|
||||||
|
{ return mTransform.translation();}
|
||||||
|
inline const QQuaternion & rotation() const
|
||||||
|
{ return mTransform.rotation();}
|
||||||
|
const QMatrix4x4 & toMatrix();
|
||||||
|
|
||||||
|
// Queries
|
||||||
|
inline QVector3D forward() const
|
||||||
|
{ return mTransform.rotation().rotatedVector(LocalForward);}
|
||||||
|
inline QVector3D right() const
|
||||||
|
{ return mTransform.rotation().rotatedVector(LocalRight);}
|
||||||
|
inline QVector3D up() const
|
||||||
|
{ return mTransform.rotation().rotatedVector(LocalUp);}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Transform3D mTransform;
|
||||||
|
QMatrix4x4 mWorld;
|
||||||
|
|
||||||
|
#ifndef QT_NO_DATASTREAM
|
||||||
|
friend QDataStream & operator<<(QDataStream & out, Camera3D & transform);
|
||||||
|
friend QDataStream & operator>>(QDataStream & in, Camera3D & transform);
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
Q_DECLARE_TYPEINFO(Camera3D, Q_MOVABLE_TYPE);
|
||||||
|
|
||||||
|
// Qt Streams
|
||||||
|
#ifndef QT_NO_DATASTREAM
|
||||||
|
QDataStream & operator<<(QDataStream & out, const Camera3D & transform);
|
||||||
|
QDataStream & operator>>(QDataStream & in, Camera3D & transform);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef QT_NO_DEBUG_STREAM
|
||||||
|
QDebug operator<<(QDebug dbg, const Camera3D & transform);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // QTK_CAMERA3D_H
|
|
@ -0,0 +1,185 @@
|
||||||
|
/*##############################################################################
|
||||||
|
## Author: Shaun Reed ##
|
||||||
|
## Legal: All Content (c) 2021 Shaun Reed, all rights reserved ##
|
||||||
|
## About: Input class from tutorials followed at trentreed.net ##
|
||||||
|
## ##
|
||||||
|
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
|
||||||
|
##############################################################################*/
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <QCursor>
|
||||||
|
|
||||||
|
#include <input.h>
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
* Static Helper Structs
|
||||||
|
******************************************************************************/
|
||||||
|
template <typename T>
|
||||||
|
struct InputInstance : std::pair<T, Input::InputState>
|
||||||
|
{
|
||||||
|
typedef std::pair<T, Input::InputState> base_class;
|
||||||
|
|
||||||
|
inline InputInstance(T value)
|
||||||
|
: base_class(value, Input::InputInvalid) {}
|
||||||
|
|
||||||
|
inline InputInstance(T value, Input::InputState state)
|
||||||
|
: base_class(value, state) {}
|
||||||
|
|
||||||
|
inline bool operator==(const InputInstance & rhs) const
|
||||||
|
{ return this->first == rhs.first;}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Key, button instance typedefs
|
||||||
|
typedef InputInstance<Qt::Key> KeyInstance;
|
||||||
|
typedef InputInstance<Qt::MouseButton> ButtonInstance;
|
||||||
|
|
||||||
|
// Key, button instance container typedefs
|
||||||
|
typedef std::vector<KeyInstance> KeyContainer;
|
||||||
|
typedef std::vector<ButtonInstance> ButtonContainer;
|
||||||
|
|
||||||
|
// Static containers for key, button instances
|
||||||
|
static KeyContainer sg_keyInstances;
|
||||||
|
static ButtonContainer sg_buttonInstances;
|
||||||
|
// Static containers for mouse data
|
||||||
|
static QPoint sg_mouseCurrPosition;
|
||||||
|
static QPoint sg_mousePrevPosition;
|
||||||
|
static QPoint sg_mouseDelta;
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
* Static Inline Helper Functions
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
static inline KeyContainer::iterator FindKey(Qt::Key value)
|
||||||
|
{
|
||||||
|
return std::find(sg_keyInstances.begin(), sg_keyInstances.end(), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline ButtonContainer::iterator FindButton(Qt::MouseButton value)
|
||||||
|
{
|
||||||
|
return std::find(sg_buttonInstances.begin(), sg_buttonInstances.end(), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename TPair>
|
||||||
|
static inline void UpdateStates(TPair & instance)
|
||||||
|
{
|
||||||
|
switch (instance.second)
|
||||||
|
{
|
||||||
|
case Input::InputRegistered:
|
||||||
|
instance.second = Input::InputTriggered;
|
||||||
|
break;
|
||||||
|
case Input::InputTriggered:
|
||||||
|
instance.second = Input::InputPressed;
|
||||||
|
break;
|
||||||
|
case Input::InputUnregistered:
|
||||||
|
instance.second = Input::InputReleased;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename TPair>
|
||||||
|
static inline bool CheckReleased(const TPair & instance)
|
||||||
|
{
|
||||||
|
return instance.second == Input::InputReleased;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Container>
|
||||||
|
static inline void Update(Container & container)
|
||||||
|
{
|
||||||
|
typedef typename Container::iterator Iter;
|
||||||
|
typedef typename Container::value_type TPair;
|
||||||
|
|
||||||
|
// Remove old data
|
||||||
|
Iter remove =
|
||||||
|
std::remove_if(container.begin(), container.end(), &CheckReleased<TPair>);
|
||||||
|
container.erase(remove, container.end());
|
||||||
|
|
||||||
|
// Update existing data
|
||||||
|
std::for_each(container.begin(), container.end(), &UpdateStates<TPair>);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
* Input Implementation
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
Input::InputState Input::keyState(Qt::Key k)
|
||||||
|
{
|
||||||
|
KeyContainer::iterator it = FindKey(k);
|
||||||
|
return (it != sg_keyInstances.end()) ? it->second : InputInvalid;
|
||||||
|
}
|
||||||
|
|
||||||
|
Input::InputState Input::buttonState(Qt::MouseButton k)
|
||||||
|
{
|
||||||
|
ButtonContainer::iterator it = FindButton(k);
|
||||||
|
return (it != sg_buttonInstances.end()) ? it->second : InputInvalid;
|
||||||
|
}
|
||||||
|
|
||||||
|
QPoint Input::mousePosition()
|
||||||
|
{
|
||||||
|
return QCursor::pos();
|
||||||
|
}
|
||||||
|
|
||||||
|
QPoint Input::mouseDelta()
|
||||||
|
{
|
||||||
|
return sg_mouseDelta;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Input::update()
|
||||||
|
{
|
||||||
|
// Update Mouse Delta
|
||||||
|
sg_mousePrevPosition = sg_mouseCurrPosition;
|
||||||
|
sg_mouseCurrPosition = QCursor::pos();
|
||||||
|
sg_mouseDelta = sg_mouseCurrPosition - sg_mousePrevPosition;
|
||||||
|
|
||||||
|
// Update KeyState values
|
||||||
|
Update(sg_buttonInstances);
|
||||||
|
Update(sg_keyInstances);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Input::registerKeyPress(int k)
|
||||||
|
{
|
||||||
|
KeyContainer::iterator it = FindKey((Qt::Key)k);
|
||||||
|
if (it == sg_keyInstances.end())
|
||||||
|
{
|
||||||
|
sg_keyInstances.push_back(KeyInstance((Qt::Key)k, InputRegistered));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Input::registerKeyRelease(int k)
|
||||||
|
{
|
||||||
|
KeyContainer::iterator it = FindKey((Qt::Key)k);
|
||||||
|
if (it != sg_keyInstances.end())
|
||||||
|
{
|
||||||
|
it->second = InputUnregistered;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Input::registerMousePress(Qt::MouseButton btn)
|
||||||
|
{
|
||||||
|
ButtonContainer::iterator it = FindButton(btn);
|
||||||
|
if (it == sg_buttonInstances.end())
|
||||||
|
{
|
||||||
|
sg_buttonInstances.push_back(ButtonInstance(btn, InputRegistered));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Input::registerMouseRelease(Qt::MouseButton btn)
|
||||||
|
{
|
||||||
|
ButtonContainer::iterator it = FindButton(btn);
|
||||||
|
if (it != sg_buttonInstances.end())
|
||||||
|
{
|
||||||
|
it->second = InputUnregistered;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Input::reset()
|
||||||
|
{
|
||||||
|
sg_keyInstances.clear();
|
||||||
|
sg_buttonInstances.clear();
|
||||||
|
}
|
|
@ -0,0 +1,63 @@
|
||||||
|
/*##############################################################################
|
||||||
|
## Author: Shaun Reed ##
|
||||||
|
## Legal: All Content (c) 2021 Shaun Reed, all rights reserved ##
|
||||||
|
## About: Input class from tutorials followed at trentreed.net ##
|
||||||
|
## ##
|
||||||
|
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
|
||||||
|
##############################################################################*/
|
||||||
|
|
||||||
|
#ifndef QTOPENGL_INPUT_H
|
||||||
|
#define QTOPENGL_INPUT_H
|
||||||
|
|
||||||
|
#include <QPoint>
|
||||||
|
#include <Qt>
|
||||||
|
|
||||||
|
|
||||||
|
class Input {
|
||||||
|
friend class MainWidget;
|
||||||
|
public:
|
||||||
|
|
||||||
|
// Possible key states
|
||||||
|
enum InputState
|
||||||
|
{
|
||||||
|
InputInvalid,
|
||||||
|
InputRegistered,
|
||||||
|
InputUnregistered,
|
||||||
|
InputTriggered,
|
||||||
|
InputPressed,
|
||||||
|
InputReleased
|
||||||
|
};
|
||||||
|
|
||||||
|
// State checking
|
||||||
|
inline static bool keyTriggered(Qt::Key key)
|
||||||
|
{ return keyState(key) == InputTriggered;}
|
||||||
|
inline static bool keyPressed(Qt::Key key)
|
||||||
|
{ return keyState(key) == InputPressed;}
|
||||||
|
inline static bool keyReleased(Qt::Key key)
|
||||||
|
{ return keyState(key) == InputReleased;}
|
||||||
|
inline static bool buttonTriggered(Qt::MouseButton button)
|
||||||
|
{ return buttonState(button) == InputTriggered;}
|
||||||
|
inline static bool buttonPressed(Qt::MouseButton button)
|
||||||
|
{ return buttonState(button) == InputPressed;}
|
||||||
|
inline static bool buttonReleased(Qt::MouseButton button)
|
||||||
|
{ return buttonState(button) == InputReleased;}
|
||||||
|
|
||||||
|
// Implementation
|
||||||
|
static InputState keyState(Qt::Key key);
|
||||||
|
static InputState buttonState(Qt::MouseButton button);
|
||||||
|
|
||||||
|
static QPoint mousePosition();
|
||||||
|
static QPoint mouseDelta();
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
// State updating
|
||||||
|
static void update();
|
||||||
|
static void registerKeyPress(int key);
|
||||||
|
static void registerKeyRelease(int key);
|
||||||
|
static void registerMousePress(Qt::MouseButton button);
|
||||||
|
static void registerMouseRelease(Qt::MouseButton button);
|
||||||
|
static void reset();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // QTOPENGL_INPUT_H
|
|
@ -0,0 +1,84 @@
|
||||||
|
/*##############################################################################
|
||||||
|
## Author: Shaun Reed ##
|
||||||
|
## Legal: All Content (c) 2021 Shaun Reed, all rights reserved ##
|
||||||
|
## About: Main window for Qt5 OpenGL widget application ##
|
||||||
|
## ##
|
||||||
|
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
|
||||||
|
##############################################################################*/
|
||||||
|
#ifndef QTK_MAINWIDGET_H
|
||||||
|
#define QTK_MAINWIDGET_H
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#include <QMatrix4x4>
|
||||||
|
#include <QOpenGLDebugLogger>
|
||||||
|
#include <QOpenGLFunctions>
|
||||||
|
#include <QOpenGLWidget>
|
||||||
|
|
||||||
|
#include <camera3d.h>
|
||||||
|
|
||||||
|
#define QTK_DEBUG
|
||||||
|
|
||||||
|
class MeshRenderer;
|
||||||
|
class Model;
|
||||||
|
class Object;
|
||||||
|
class Skybox;
|
||||||
|
class Texture;
|
||||||
|
|
||||||
|
class MainWidget : public QOpenGLWidget,
|
||||||
|
protected QOpenGLFunctions {
|
||||||
|
Q_OBJECT;
|
||||||
|
|
||||||
|
public:
|
||||||
|
// Constructors
|
||||||
|
MainWidget();
|
||||||
|
explicit MainWidget(const QSurfaceFormat &format);
|
||||||
|
~MainWidget() override;
|
||||||
|
|
||||||
|
// Static Members
|
||||||
|
static inline QMatrix4x4 &Projection() { return mProjection;}
|
||||||
|
static inline Camera3D &Camera() { return mCamera;}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void teardownGL();
|
||||||
|
void initObjects();
|
||||||
|
|
||||||
|
public:
|
||||||
|
// Inherited virtual Members
|
||||||
|
void paintGL() override;
|
||||||
|
void initializeGL() override;
|
||||||
|
void resizeGL(int width, int height) override;
|
||||||
|
|
||||||
|
protected slots:
|
||||||
|
void update();
|
||||||
|
void messageLogged(const QOpenGLDebugMessage &msg);
|
||||||
|
|
||||||
|
// Protected Helpers
|
||||||
|
protected:
|
||||||
|
void keyPressEvent(QKeyEvent *event);
|
||||||
|
void keyReleaseEvent(QKeyEvent *event);
|
||||||
|
void mousePressEvent(QMouseEvent *event);
|
||||||
|
void mouseReleaseEvent(QMouseEvent *event);
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Private helpers
|
||||||
|
void printContextInformation();
|
||||||
|
void updateCameraInput();
|
||||||
|
|
||||||
|
// Private Members
|
||||||
|
static Camera3D mCamera;
|
||||||
|
static QMatrix4x4 mProjection;
|
||||||
|
|
||||||
|
Skybox * mSkybox;
|
||||||
|
Object * mObject;
|
||||||
|
MeshRenderer * mTestPhong;
|
||||||
|
MeshRenderer * mTestSpecular;
|
||||||
|
MeshRenderer * mTestDiffuse;
|
||||||
|
MeshRenderer * mTestAmbient;
|
||||||
|
std::vector<MeshRenderer *> mMeshes;
|
||||||
|
std::vector<Model *> mModels;
|
||||||
|
|
||||||
|
QOpenGLDebugLogger * mDebugLogger;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // QTK_MAINWIDGET_H
|
|
@ -0,0 +1,336 @@
|
||||||
|
/*##############################################################################
|
||||||
|
## Author: Shaun Reed ##
|
||||||
|
## Legal: All Content (c) 2021 Shaun Reed, all rights reserved ##
|
||||||
|
## About: Collection of static mesh data for quick initialization ##
|
||||||
|
## ##
|
||||||
|
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
|
||||||
|
##############################################################################*/
|
||||||
|
|
||||||
|
#include <mesh.h>
|
||||||
|
|
||||||
|
|
||||||
|
Cube::Cube(DrawMode mode)
|
||||||
|
{
|
||||||
|
mDrawMode = mode;
|
||||||
|
switch(mode) {
|
||||||
|
// Cube data for use with glDrawArrays
|
||||||
|
case QTK_DRAW_ARRAYS:
|
||||||
|
mIndices = { /* No indices needed for glDrawArrays */ };
|
||||||
|
|
||||||
|
mNormals =
|
||||||
|
{FACE_FRONT, FACE_BACK, FACE_TOP, FACE_BOTTOM, FACE_LEFT, FACE_RIGHT};
|
||||||
|
|
||||||
|
mVertices = {
|
||||||
|
// Face 1 (Front)
|
||||||
|
VERTEX_FTR, VERTEX_FTL, VERTEX_FBL,
|
||||||
|
VERTEX_FBL, VERTEX_FBR, VERTEX_FTR,
|
||||||
|
// Face 2 (Back)
|
||||||
|
VERTEX_BBR, VERTEX_BTL, VERTEX_BTR,
|
||||||
|
VERTEX_BTL, VERTEX_BBR, VERTEX_BBL,
|
||||||
|
// Face 3 (Top)
|
||||||
|
VERTEX_FTR, VERTEX_BTR, VERTEX_BTL,
|
||||||
|
VERTEX_BTL, VERTEX_FTL, VERTEX_FTR,
|
||||||
|
// Face 4 (Bottom)
|
||||||
|
VERTEX_FBR, VERTEX_FBL, VERTEX_BBL,
|
||||||
|
VERTEX_BBL, VERTEX_BBR, VERTEX_FBR,
|
||||||
|
// Face 5 (Left)
|
||||||
|
VERTEX_FBL, VERTEX_FTL, VERTEX_BTL,
|
||||||
|
VERTEX_FBL, VERTEX_BTL, VERTEX_BBL,
|
||||||
|
// Face 6 (Right)
|
||||||
|
VERTEX_FTR, VERTEX_FBR, VERTEX_BBR,
|
||||||
|
VERTEX_BBR, VERTEX_BTR, VERTEX_FTR
|
||||||
|
};
|
||||||
|
|
||||||
|
mColors = {
|
||||||
|
// Face 1 (Front)
|
||||||
|
RED, GREEN, BLUE,
|
||||||
|
BLUE, WHITE, RED,
|
||||||
|
// Face 2 (Back)
|
||||||
|
YELLOW, CYAN, MAGENTA,
|
||||||
|
CYAN, YELLOW, BLACK,
|
||||||
|
// Face 3 (Top)
|
||||||
|
RED, MAGENTA, CYAN,
|
||||||
|
CYAN, GREEN, RED,
|
||||||
|
// Face 4 (Bottom)
|
||||||
|
WHITE, BLUE, BLACK,
|
||||||
|
BLACK, YELLOW, WHITE,
|
||||||
|
// Face 5 (Left)
|
||||||
|
BLUE, GREEN, CYAN,
|
||||||
|
BLUE, CYAN, BLACK,
|
||||||
|
// Face 6 (Right)
|
||||||
|
RED, WHITE, YELLOW,
|
||||||
|
YELLOW, MAGENTA, RED
|
||||||
|
};
|
||||||
|
|
||||||
|
mTexCoords = {
|
||||||
|
// Face 1 (Front)
|
||||||
|
UV_TOP, UV_ORIGIN, UV_RIGHT,
|
||||||
|
UV_RIGHT, UV_CORNER, UV_TOP,
|
||||||
|
// Face 2 (Back)
|
||||||
|
UV_TOP, UV_RIGHT, UV_CORNER,
|
||||||
|
UV_RIGHT, UV_TOP, UV_ORIGIN,
|
||||||
|
// Face 3 (Top)
|
||||||
|
UV_CORNER, UV_TOP, UV_ORIGIN,
|
||||||
|
UV_ORIGIN, UV_RIGHT, UV_CORNER,
|
||||||
|
// Face 4 (Bottom)
|
||||||
|
UV_TOP, UV_ORIGIN, UV_RIGHT,
|
||||||
|
UV_RIGHT, UV_CORNER, UV_TOP,
|
||||||
|
// Face 5 (Left)
|
||||||
|
UV_TOP, UV_CORNER, UV_RIGHT,
|
||||||
|
UV_TOP, UV_RIGHT, UV_ORIGIN,
|
||||||
|
// Face 6 (Right)
|
||||||
|
UV_TOP, UV_CORNER, UV_RIGHT,
|
||||||
|
UV_RIGHT, UV_ORIGIN, UV_TOP
|
||||||
|
};
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
|
||||||
|
// Cube data for use with glDrawElements
|
||||||
|
case QTK_DRAW_ELEMENTS:
|
||||||
|
mNormals =
|
||||||
|
{/* For normals and glDrawElements, see QTK_DRAW_ELEMENTS_NORMALS */};
|
||||||
|
mTexCoords =
|
||||||
|
{ /* For UVs and glDrawElements, see QTK_DRAW_ELEMENTS_NORMALS */ };
|
||||||
|
|
||||||
|
mColors = {RED, GREEN, BLUE, WHITE, YELLOW, CYAN, MAGENTA, BLACK};
|
||||||
|
|
||||||
|
mVertices = {
|
||||||
|
// 0 1 2 3
|
||||||
|
VERTEX_FTR, VERTEX_FTL, VERTEX_FBL, VERTEX_FBR,
|
||||||
|
// 4 5 6 7
|
||||||
|
VERTEX_BTR, VERTEX_BTL, VERTEX_BBL, VERTEX_BBR
|
||||||
|
};
|
||||||
|
mIndices = {
|
||||||
|
// Face 1 (Front)
|
||||||
|
0, 1, 2, 2, 3, 0,
|
||||||
|
// Face 2 (Back)
|
||||||
|
7, 5, 4, 5, 7, 6,
|
||||||
|
// Face 3 (Top)
|
||||||
|
0, 4, 5, 5, 1, 0,
|
||||||
|
// Face 4 (Bottom)
|
||||||
|
3, 2, 6, 6, 7, 3,
|
||||||
|
// Face 5 (Left)
|
||||||
|
2, 1, 5, 2, 5, 6,
|
||||||
|
// Face 6 (Right)
|
||||||
|
0, 3, 7, 7, 4, 0
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Cube shape data for using normals and UVs with glDrawElements
|
||||||
|
case QTK_DRAW_ELEMENTS_NORMALS:
|
||||||
|
mColors = {RED, GREEN, BLUE, WHITE, YELLOW, CYAN, MAGENTA, BLACK};
|
||||||
|
|
||||||
|
mVertices = {
|
||||||
|
// Face 1 (Front)
|
||||||
|
// 0 1 2 3
|
||||||
|
VERTEX_FTL, VERTEX_FBL, VERTEX_FBR, VERTEX_FTR,
|
||||||
|
// Face 2 (Back)
|
||||||
|
// 4 5 6 7
|
||||||
|
VERTEX_BTL, VERTEX_BBL, VERTEX_BBR, VERTEX_BTR,
|
||||||
|
// Face 3 (Top)
|
||||||
|
// 8 9 10 11
|
||||||
|
VERTEX_FTL, VERTEX_BTL, VERTEX_BTR, VERTEX_FTR,
|
||||||
|
// Face 4 (Bottom)
|
||||||
|
// 12 13 14 15
|
||||||
|
VERTEX_FBL, VERTEX_BBL, VERTEX_BBR, VERTEX_FBR,
|
||||||
|
// Face 5 (Left)
|
||||||
|
// 16 17 18 19
|
||||||
|
VERTEX_FBL, VERTEX_BBL, VERTEX_BTL, VERTEX_FTL,
|
||||||
|
// Face 6 (Right)
|
||||||
|
// 20 21 22 23
|
||||||
|
VERTEX_FBR, VERTEX_BBR, VERTEX_BTR, VERTEX_FTR
|
||||||
|
};
|
||||||
|
|
||||||
|
mIndices = {
|
||||||
|
// Face 1 (Front)
|
||||||
|
0, 1, 2, 2, 3, 0,
|
||||||
|
// Face 2 (Back)
|
||||||
|
4, 5, 6, 6, 7, 4,
|
||||||
|
// Face 3 (Top)
|
||||||
|
8, 9, 10, 10, 11, 8,
|
||||||
|
// Face 4 (Bottom)
|
||||||
|
12, 13, 14, 14, 15, 12,
|
||||||
|
|
||||||
|
// Face 5 (Left)
|
||||||
|
16, 17, 18, 18, 19, 16,
|
||||||
|
// Face 6 (Right)
|
||||||
|
20, 21, 22, 22, 23, 20
|
||||||
|
};
|
||||||
|
|
||||||
|
mNormals = {
|
||||||
|
VECTOR_FORWARD, VECTOR_FORWARD, VECTOR_FORWARD, VECTOR_FORWARD,
|
||||||
|
VECTOR_BACK, VECTOR_BACK, VECTOR_BACK, VECTOR_BACK,
|
||||||
|
VECTOR_UP, VECTOR_UP, VECTOR_UP, VECTOR_UP,
|
||||||
|
VECTOR_DOWN, VECTOR_DOWN, VECTOR_DOWN, VECTOR_DOWN,
|
||||||
|
VECTOR_LEFT, VECTOR_LEFT, VECTOR_LEFT, VECTOR_LEFT,
|
||||||
|
VECTOR_RIGHT, VECTOR_RIGHT, VECTOR_RIGHT, VECTOR_RIGHT,
|
||||||
|
};
|
||||||
|
|
||||||
|
mTexCoords = {
|
||||||
|
// Face 1 (Front)
|
||||||
|
UV_TOP, UV_RIGHT, UV_CORNER,
|
||||||
|
UV_RIGHT, UV_TOP, UV_ORIGIN,
|
||||||
|
// Face 2 (Back)
|
||||||
|
UV_TOP, UV_RIGHT, UV_CORNER,
|
||||||
|
UV_RIGHT, UV_TOP, UV_ORIGIN,
|
||||||
|
// Face 3 (Top)
|
||||||
|
UV_TOP, UV_RIGHT, UV_CORNER,
|
||||||
|
UV_RIGHT, UV_TOP, UV_ORIGIN,
|
||||||
|
// Face 4 (Bottom)
|
||||||
|
UV_TOP, UV_RIGHT, UV_CORNER,
|
||||||
|
UV_RIGHT, UV_TOP, UV_ORIGIN,
|
||||||
|
// Face 5 (Left)
|
||||||
|
UV_TOP, UV_RIGHT, UV_CORNER,
|
||||||
|
UV_RIGHT, UV_TOP, UV_ORIGIN,
|
||||||
|
// Face 6 (Right)
|
||||||
|
UV_TOP, UV_RIGHT, UV_CORNER,
|
||||||
|
UV_RIGHT, UV_TOP, UV_ORIGIN,
|
||||||
|
};
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Triangle::Triangle(DrawMode mode)
|
||||||
|
{
|
||||||
|
mDrawMode = mode;
|
||||||
|
const QVector3D triangleTop = QVector3D(0.0f, 0.5f, 0.0f);
|
||||||
|
switch(mode) {
|
||||||
|
case QTK_DRAW_ARRAYS:
|
||||||
|
mIndices = { /* No indices needed for glDrawArrays */ };
|
||||||
|
|
||||||
|
mColors = { RED, GREEN, BLUE, WHITE, YELLOW, CYAN, MAGENTA, BLACK };
|
||||||
|
|
||||||
|
mVertices = {
|
||||||
|
// Bottom face (Base of the pyramid)
|
||||||
|
VERTEX_BBL, VERTEX_BBR, VERTEX_FBR,
|
||||||
|
VERTEX_FBR, VERTEX_FBL, VERTEX_BBL,
|
||||||
|
|
||||||
|
// Front face
|
||||||
|
VERTEX_FBL, VERTEX_FBR, triangleTop,
|
||||||
|
// Back face
|
||||||
|
VERTEX_BBR, VERTEX_BBL, triangleTop,
|
||||||
|
// Left face
|
||||||
|
VERTEX_BBL, VERTEX_FBL, triangleTop,
|
||||||
|
// Right face
|
||||||
|
VERTEX_FBR, VERTEX_BBR, triangleTop,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Find normals for each triangle of the mesh
|
||||||
|
for (int i = 0; i < mVertices.size(); i += 3) {
|
||||||
|
QVector3D vertexNormal =
|
||||||
|
QVector3D::normal(mVertices[i], mVertices[i+1], mVertices[i+2]);
|
||||||
|
// Three points share this normal
|
||||||
|
for (int j = 0; j < 3; j++) {
|
||||||
|
mNormals.push_back(vertexNormal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mTexCoords = {
|
||||||
|
// Bottom face (Base of the pyramid)
|
||||||
|
UV_ORIGIN, UV_RIGHT, UV_CORNER,
|
||||||
|
UV_CORNER, UV_TOP, UV_ORIGIN,
|
||||||
|
|
||||||
|
// Front face
|
||||||
|
UV_ORIGIN, UV_RIGHT, UV_CORNER,
|
||||||
|
// Back face
|
||||||
|
UV_ORIGIN, UV_RIGHT, UV_CORNER,
|
||||||
|
// Left face
|
||||||
|
UV_ORIGIN, UV_RIGHT, UV_CORNER,
|
||||||
|
// Right face
|
||||||
|
UV_ORIGIN, UV_RIGHT, UV_CORNER,
|
||||||
|
};
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Triangle shape data for using glDrawElements
|
||||||
|
case QTK_DRAW_ELEMENTS:
|
||||||
|
mColors = { RED, GREEN, BLUE, WHITE, YELLOW, CYAN, MAGENTA, BLACK };
|
||||||
|
|
||||||
|
mVertices = {VERTEX_FBL, VERTEX_FBR, VERTEX_BBL, VERTEX_BBR, triangleTop};
|
||||||
|
|
||||||
|
mIndices = {
|
||||||
|
// Bottom face (Base of the pyramid)
|
||||||
|
2, 3, 1, // Use customVertexes[2], then 3, 1...
|
||||||
|
1, 0, 2, // Use customVertexes[1], then 0, 2
|
||||||
|
|
||||||
|
0, 1, 4, // Front face
|
||||||
|
3, 2, 4, // Back face
|
||||||
|
2, 0, 4, // Left face
|
||||||
|
1, 3, 4, // Right face
|
||||||
|
};
|
||||||
|
|
||||||
|
mNormals =
|
||||||
|
{/* Use QTK_DRAW_ELEMENTS_NORMALS for normals with glDrawElements */};
|
||||||
|
|
||||||
|
mTexCoords = { /* No UVs for triangle with glDrawElements */ };
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Triangle shape data for using normals and UVs with glDrawElements
|
||||||
|
case QTK_DRAW_ELEMENTS_NORMALS:
|
||||||
|
mColors = { RED, GREEN, BLUE, WHITE, YELLOW, CYAN, MAGENTA, BLACK };
|
||||||
|
|
||||||
|
mVertices = {
|
||||||
|
// Bottom face
|
||||||
|
// 0 1 2
|
||||||
|
VERTEX_FBL, VERTEX_FBR, VERTEX_BBL,
|
||||||
|
// 3 4 5
|
||||||
|
VERTEX_BBR, VERTEX_FBR, VERTEX_BBL,
|
||||||
|
|
||||||
|
// Front face
|
||||||
|
// 6 7 8
|
||||||
|
VERTEX_FBL, VERTEX_FBR, triangleTop,
|
||||||
|
// Back face
|
||||||
|
// 9 10 11
|
||||||
|
VERTEX_BBR, VERTEX_BBL, triangleTop,
|
||||||
|
// Left face
|
||||||
|
// 12 13 14
|
||||||
|
VERTEX_BBL, VERTEX_FBL, triangleTop,
|
||||||
|
// Right face
|
||||||
|
// 15 16 17
|
||||||
|
VERTEX_FBR, VERTEX_BBR, triangleTop,
|
||||||
|
};
|
||||||
|
|
||||||
|
mIndices = {
|
||||||
|
// Bottom face (Base of the pyramid)
|
||||||
|
0, 1, 2, // Use customVertexes[2], then 3, 1...
|
||||||
|
3, 4, 5, // Use customVertexes[1], then 0, 2
|
||||||
|
|
||||||
|
6, 7, 8, // Front face
|
||||||
|
9, 10, 11, // Back face
|
||||||
|
12, 13, 14, // Left face
|
||||||
|
15, 16, 17, // Right face
|
||||||
|
};
|
||||||
|
|
||||||
|
// Find normals for each triangle of the mesh
|
||||||
|
for (int i = 0; i < mVertices.size(); i += 3) {
|
||||||
|
QVector3D vertexNormal =
|
||||||
|
QVector3D::normal(mVertices[mIndices[i]],
|
||||||
|
mVertices[mIndices[i+1]],
|
||||||
|
mVertices[mIndices[i+2]]);
|
||||||
|
// Three points share this normal
|
||||||
|
for (int j = 0; j < 3; j++) {
|
||||||
|
mNormals.push_back(vertexNormal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mTexCoords = {
|
||||||
|
// Bottom face
|
||||||
|
UV_ORIGIN, UV_RIGHT, UV_TOP,
|
||||||
|
UV_CORNER, UV_RIGHT, UV_TOP,
|
||||||
|
|
||||||
|
// Front face
|
||||||
|
UV_ORIGIN, UV_RIGHT, UV_CORNER,
|
||||||
|
// Back face
|
||||||
|
UV_ORIGIN, UV_RIGHT, UV_CORNER,
|
||||||
|
// Left face
|
||||||
|
UV_ORIGIN, UV_RIGHT, UV_CORNER,
|
||||||
|
// Right face
|
||||||
|
UV_ORIGIN, UV_RIGHT, UV_CORNER,
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,131 @@
|
||||||
|
/*##############################################################################
|
||||||
|
## Author: Shaun Reed ##
|
||||||
|
## Legal: All Content (c) 2021 Shaun Reed, all rights reserved ##
|
||||||
|
## About: Collection of static mesh data for quick initialization ##
|
||||||
|
## ##
|
||||||
|
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
|
||||||
|
##############################################################################*/
|
||||||
|
#ifndef QTK_MESH_H
|
||||||
|
#define QTK_MESH_H
|
||||||
|
|
||||||
|
#include <QOpenGLWidget>
|
||||||
|
#include <QVector2D>
|
||||||
|
#include <QVector3D>
|
||||||
|
|
||||||
|
#include <mainwidget.h>
|
||||||
|
#include <transform3D.h>
|
||||||
|
|
||||||
|
|
||||||
|
// Define vertices for drawing a cube using two faces (8 vertex points)
|
||||||
|
// Front Vertices
|
||||||
|
#define VERTEX_FTR QVector3D( 0.5f, 0.5f, 0.5f) // 1
|
||||||
|
#define VERTEX_FTL QVector3D(-0.5f, 0.5f, 0.5f) // 2
|
||||||
|
#define VERTEX_FBL QVector3D(-0.5f, -0.5f, 0.5f) // 3
|
||||||
|
#define VERTEX_FBR QVector3D( 0.5f, -0.5f, 0.5f) // 4
|
||||||
|
|
||||||
|
// Back Vertices
|
||||||
|
#define VERTEX_BTR QVector3D( 0.5f, 0.5f, -0.5f) // 5
|
||||||
|
#define VERTEX_BTL QVector3D(-0.5f, 0.5f, -0.5f) // 6
|
||||||
|
#define VERTEX_BBL QVector3D(-0.5f, -0.5f, -0.5f) // 7
|
||||||
|
#define VERTEX_BBR QVector3D( 0.5f, -0.5f, -0.5f) // 8
|
||||||
|
|
||||||
|
// Direction vectors
|
||||||
|
#define VECTOR_UP QVector3D(0.0f, 1.0f, 0.0f)
|
||||||
|
#define VECTOR_DOWN QVector3D(0.0f, -1.0f, 0.0f)
|
||||||
|
#define VECTOR_LEFT QVector3D(-1.0f, 0.0f, 0.0f)
|
||||||
|
#define VECTOR_RIGHT QVector3D(1.0f, 0.0f, 0.0f)
|
||||||
|
#define VECTOR_FORWARD QVector3D(0.0f, 0.0f, 1.0f)
|
||||||
|
#define VECTOR_BACK QVector3D(0.0f, 0.0f, -1.0f)
|
||||||
|
|
||||||
|
// Identity and zero vectors
|
||||||
|
#define VECTOR_ONE QVector3D(1.0f, 1.0f, 1.0f)
|
||||||
|
#define VECTOR_ZERO QVector3D(0.0f, 0.0f, 0.0f)
|
||||||
|
|
||||||
|
// A series of direction vectors to represent cube face normal
|
||||||
|
#define FACE_TOP VECTOR_UP, VECTOR_UP, VECTOR_UP, \
|
||||||
|
VECTOR_UP, VECTOR_UP, VECTOR_UP
|
||||||
|
#define FACE_BOTTOM VECTOR_DOWN, VECTOR_DOWN, VECTOR_DOWN, \
|
||||||
|
VECTOR_DOWN, VECTOR_DOWN, VECTOR_DOWN
|
||||||
|
#define FACE_LEFT VECTOR_LEFT, VECTOR_LEFT, VECTOR_LEFT, \
|
||||||
|
VECTOR_LEFT, VECTOR_LEFT, VECTOR_LEFT
|
||||||
|
#define FACE_RIGHT VECTOR_RIGHT, VECTOR_RIGHT, VECTOR_RIGHT, \
|
||||||
|
VECTOR_RIGHT, VECTOR_RIGHT, VECTOR_RIGHT
|
||||||
|
#define FACE_FRONT VECTOR_FORWARD, VECTOR_FORWARD, VECTOR_FORWARD, \
|
||||||
|
VECTOR_FORWARD, VECTOR_FORWARD, VECTOR_FORWARD
|
||||||
|
#define FACE_BACK VECTOR_BACK, VECTOR_BACK, VECTOR_BACK, \
|
||||||
|
VECTOR_BACK, VECTOR_BACK, VECTOR_BACK
|
||||||
|
|
||||||
|
// Colors using QVector3Ds as RGB values
|
||||||
|
#define WHITE VECTOR_ONE
|
||||||
|
#define BLACK VECTOR_ZERO
|
||||||
|
#define RED QVector3D(1.0f, 0.0f, 0.0f)
|
||||||
|
#define GREEN QVector3D(0.0f, 1.0f, 0.0f)
|
||||||
|
#define BLUE QVector3D(0.0f, 0.0f, 1.0f)
|
||||||
|
#define YELLOW QVector3D(1.0f, 1.0f, 0.0f)
|
||||||
|
#define CYAN QVector3D(0.0f, 1.0f, 1.0f)
|
||||||
|
#define MAGENTA QVector3D(1.0f, 0.0f, 1.0f)
|
||||||
|
|
||||||
|
#define UV_ORIGIN QVector2D(0.0f, 0.0f)
|
||||||
|
#define UV_TOP QVector2D(1.0f, 0.0f)
|
||||||
|
#define UV_RIGHT QVector2D(0.0f, 1.0f)
|
||||||
|
#define UV_CORNER QVector2D(1.0f, 1.0f)
|
||||||
|
|
||||||
|
|
||||||
|
typedef std::vector<QVector3D> Vertices;
|
||||||
|
typedef std::vector<QVector3D> Colors;
|
||||||
|
typedef std::vector<GLuint> Indices;
|
||||||
|
typedef std::vector<QVector2D> TexCoords;
|
||||||
|
typedef std::vector<QVector3D> Normals;
|
||||||
|
|
||||||
|
enum DrawMode { QTK_DRAW_ARRAYS, QTK_DRAW_ELEMENTS, QTK_DRAW_ELEMENTS_NORMALS };
|
||||||
|
|
||||||
|
struct ShapeBase {
|
||||||
|
ShapeBase(DrawMode mode=QTK_DRAW_ARRAYS, Vertices v={},Indices i={}, Colors c={},
|
||||||
|
TexCoords t={}, Normals n={})
|
||||||
|
: mVertices(v), mColors(c), mIndices(i), mTexCoords(t), mNormals(n)
|
||||||
|
{}
|
||||||
|
|
||||||
|
inline const Vertices & vertices() const { return mVertices;}
|
||||||
|
inline const Indices & indices() const { return mIndices;}
|
||||||
|
inline const Colors & colors() const { return mColors;}
|
||||||
|
inline const TexCoords & texCoords() const { return mTexCoords;}
|
||||||
|
inline const Normals & normals() const { return mNormals;}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
DrawMode mDrawMode;
|
||||||
|
|
||||||
|
Vertices mVertices;
|
||||||
|
Colors mColors;
|
||||||
|
Indices mIndices;
|
||||||
|
TexCoords mTexCoords;
|
||||||
|
Normals mNormals;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Shape : public ShapeBase {
|
||||||
|
friend MeshRenderer;
|
||||||
|
friend Object;
|
||||||
|
Shape () {}
|
||||||
|
Shape(const ShapeBase & rhs) : ShapeBase(rhs) {}
|
||||||
|
|
||||||
|
virtual inline void setVertices(const Vertices & value) {mVertices = value;}
|
||||||
|
virtual inline void setIndices(const Indices & value) {mIndices = value;}
|
||||||
|
virtual inline void setColors(const Colors & value) {mColors = value;}
|
||||||
|
virtual inline void setTexCoords(const TexCoords & value) {mTexCoords = value;}
|
||||||
|
virtual inline void setNormals(const Normals & value) {mNormals = value;}
|
||||||
|
virtual inline void setShape(const Shape & value) { *this = value;}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Primitives inherit from ShapeBase, does not allow setting of shape values
|
||||||
|
class Mesh {
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Cube : public ShapeBase {
|
||||||
|
Cube(DrawMode mode=QTK_DRAW_ARRAYS);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Triangle : public ShapeBase {
|
||||||
|
Triangle(DrawMode mode=QTK_DRAW_ARRAYS);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // QTK_MESH_H
|
|
@ -0,0 +1,171 @@
|
||||||
|
/*##############################################################################
|
||||||
|
## Author: Shaun Reed ##
|
||||||
|
## Legal: All Content (c) 2021 Shaun Reed, all rights reserved ##
|
||||||
|
## About: MeshRenderer class for quick object creation and drawing ##
|
||||||
|
## ##
|
||||||
|
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
|
||||||
|
##############################################################################*/
|
||||||
|
|
||||||
|
#include <QImageReader>
|
||||||
|
|
||||||
|
#include <texture.h>
|
||||||
|
|
||||||
|
#include <meshrenderer.h>
|
||||||
|
|
||||||
|
|
||||||
|
// Static QHash that holds all MeshRenderer instances using their mName as keys
|
||||||
|
MeshRenderer::MeshManager MeshRenderer::sInstances;
|
||||||
|
|
||||||
|
MeshRenderer::MeshRenderer(const char * name, const ShapeBase & shape)
|
||||||
|
: Object(name, shape), mVertexShader(":/multi-color.vert"),
|
||||||
|
mFragmentShader(":/multi-color.frag"), mDrawType(GL_TRIANGLES),
|
||||||
|
mHasTexture(false)
|
||||||
|
{
|
||||||
|
mShape = Shape(shape);
|
||||||
|
init();
|
||||||
|
sInstances.insert(name, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
MeshRenderer::~MeshRenderer()
|
||||||
|
{
|
||||||
|
if (mHasTexture) {
|
||||||
|
mTexture->destroy();
|
||||||
|
}
|
||||||
|
sInstances.remove(mName);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Static member function to retrieve instances of MeshRenderers
|
||||||
|
MeshRenderer * MeshRenderer::getInstance(const QString & name)
|
||||||
|
{ return sInstances[name];}
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
* Public Member Functions
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
void MeshRenderer::init()
|
||||||
|
{
|
||||||
|
if (mVAO.isCreated()) mVAO.destroy();
|
||||||
|
if (mProgram.isLinked()) mProgram.removeAllShaders();
|
||||||
|
if (mVBO.isCreated()) mVBO.destroy();
|
||||||
|
|
||||||
|
mVAO.create();
|
||||||
|
mVAO.bind();
|
||||||
|
|
||||||
|
mProgram.create();
|
||||||
|
mProgram.addShaderFromSourceFile(QOpenGLShader::Vertex,
|
||||||
|
mVertexShader.c_str());
|
||||||
|
mProgram.addShaderFromSourceFile(QOpenGLShader::Fragment,
|
||||||
|
mFragmentShader.c_str());
|
||||||
|
mProgram.link();
|
||||||
|
mProgram.bind();
|
||||||
|
|
||||||
|
mVBO.create();
|
||||||
|
mVBO.setUsagePattern(QOpenGLBuffer::StaticDraw);
|
||||||
|
mVBO.bind();
|
||||||
|
|
||||||
|
// Combine position and color data into one vector, allowing us to use one VBO
|
||||||
|
Vertices combined;
|
||||||
|
combined.reserve(vertices().size() + colors().size());
|
||||||
|
combined.insert(combined.end(), vertices().begin(), vertices().end());
|
||||||
|
combined.insert(combined.end(), colors().begin(), colors().end());
|
||||||
|
|
||||||
|
mVBO.allocate(combined.data(),
|
||||||
|
combined.size() * sizeof(combined[0]));
|
||||||
|
|
||||||
|
// Enable position attribute
|
||||||
|
mProgram.enableAttributeArray(0);
|
||||||
|
mProgram.setAttributeBuffer(0, GL_FLOAT, 0,
|
||||||
|
3, sizeof(QVector3D));
|
||||||
|
// Enable color attribute, setting offset to total size of vertices()
|
||||||
|
mProgram.enableAttributeArray(1);
|
||||||
|
mProgram.setAttributeBuffer(1, GL_FLOAT,
|
||||||
|
vertices().size() * sizeof(vertices()[0]),
|
||||||
|
3, sizeof(QVector3D));
|
||||||
|
|
||||||
|
mVBO.release();
|
||||||
|
|
||||||
|
mProgram.release();
|
||||||
|
mVAO.release();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MeshRenderer::draw()
|
||||||
|
{
|
||||||
|
mProgram.bind();
|
||||||
|
mVAO.bind();
|
||||||
|
|
||||||
|
if(mHasTexture) {
|
||||||
|
mTexture->bind();
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Automate uniforms some other way
|
||||||
|
setUniformMVP();
|
||||||
|
|
||||||
|
if (mShape.mDrawMode == QTK_DRAW_ARRAYS) {
|
||||||
|
glDrawArrays(mDrawType, 0, vertices().size());
|
||||||
|
}
|
||||||
|
else if (mShape.mDrawMode == QTK_DRAW_ELEMENTS
|
||||||
|
|| mShape.mDrawMode == QTK_DRAW_ELEMENTS_NORMALS) {
|
||||||
|
glDrawElements(mDrawType, mShape.mIndices.size(),
|
||||||
|
GL_UNSIGNED_INT, mShape.mIndices.data());
|
||||||
|
}
|
||||||
|
|
||||||
|
if(mHasTexture) {
|
||||||
|
mTexture->release();
|
||||||
|
}
|
||||||
|
|
||||||
|
mVAO.release();
|
||||||
|
mProgram.release();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MeshRenderer::setShaders(const std::string & vert, const std::string & frag)
|
||||||
|
{
|
||||||
|
mVertexShader = vert;
|
||||||
|
mFragmentShader = frag;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MeshRenderer::setUniformMVP(const char * model, const char * view,
|
||||||
|
const char * projection)
|
||||||
|
{
|
||||||
|
mProgram.setUniformValue(projection, MainWidget::Projection());
|
||||||
|
mProgram.setUniformValue(view, MainWidget::Camera().toMatrix());
|
||||||
|
mProgram.setUniformValue(model, mTransform.toMatrix());
|
||||||
|
}
|
||||||
|
|
||||||
|
void MeshRenderer::setColor(const QVector3D & color)
|
||||||
|
{
|
||||||
|
if (mShape.mColors.empty()) {
|
||||||
|
for (const auto & vertex : mShape.vertices()) {
|
||||||
|
mShape.mColors.push_back(color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
for (int i = 0; i < mShape.colors().size(); i++) {
|
||||||
|
mShape.mColors[i] = color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MeshRenderer::setTexture(const char * path)
|
||||||
|
{
|
||||||
|
mTexture = new QOpenGLTexture(*Texture::initImage(path));
|
||||||
|
mHasTexture = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MeshRenderer::setTexture(QOpenGLTexture * texture)
|
||||||
|
{
|
||||||
|
mTexture = texture;
|
||||||
|
mHasTexture = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
* Inherited Virtual Member Functions
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
void MeshRenderer::setShape(const Shape & value)
|
||||||
|
{
|
||||||
|
Object::setShape(value);
|
||||||
|
init();
|
||||||
|
}
|
|
@ -0,0 +1,76 @@
|
||||||
|
/*##############################################################################
|
||||||
|
## Author: Shaun Reed ##
|
||||||
|
## Legal: All Content (c) 2021 Shaun Reed, all rights reserved ##
|
||||||
|
## About: MeshRenderer class for quick object creation and drawing ##
|
||||||
|
## ##
|
||||||
|
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
|
||||||
|
##############################################################################*/
|
||||||
|
#ifndef QTK_MESHRENDERER_H
|
||||||
|
#define QTK_MESHRENDERER_H
|
||||||
|
|
||||||
|
#include <mesh.h>
|
||||||
|
#include <object.h>
|
||||||
|
|
||||||
|
|
||||||
|
class MeshRenderer : public Object {
|
||||||
|
public:
|
||||||
|
// Delegate constructors
|
||||||
|
MeshRenderer(const char * name, Vertices vertices, Indices indices,
|
||||||
|
DrawMode mode=QTK_DRAW_ARRAYS)
|
||||||
|
: MeshRenderer(name, ShapeBase(mode, vertices, indices))
|
||||||
|
{}
|
||||||
|
MeshRenderer(const char * name)
|
||||||
|
: MeshRenderer(name, Cube(QTK_DRAW_ELEMENTS))
|
||||||
|
{}
|
||||||
|
// Constructor
|
||||||
|
MeshRenderer(const char * name, const ShapeBase &shape);
|
||||||
|
~MeshRenderer();
|
||||||
|
|
||||||
|
// Retrieve a mesh by name stored within a static QHash
|
||||||
|
static MeshRenderer * getInstance(const QString & name);
|
||||||
|
|
||||||
|
void init();
|
||||||
|
void draw();
|
||||||
|
|
||||||
|
// Draw types like GL_TRIANGLES, GL_POINTS, GL_LINES, etc
|
||||||
|
void setDrawType(int drawType) { mDrawType = drawType;}
|
||||||
|
|
||||||
|
// Shader settings
|
||||||
|
inline void setShaderVertex(const std::string & vert) { mVertexShader = vert;}
|
||||||
|
inline void setShaderFragment(const std::string & frag) { mFragmentShader = frag;}
|
||||||
|
void setShaders(const std::string & vert, const std::string & frag);
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
inline void setUniform(int location, T value)
|
||||||
|
{ mProgram.setUniformValue(location, value);}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
inline void setUniform(const char * location, T value)
|
||||||
|
{ mProgram.setUniformValue(location, value);}
|
||||||
|
|
||||||
|
// Set MVP matrix using this Object's transform
|
||||||
|
// + View and projection provided by MainWidget static members
|
||||||
|
void setUniformMVP(const char * model="uModel", const char * view="uView",
|
||||||
|
const char * projection="uProjection");
|
||||||
|
|
||||||
|
// Sets the texture to the image at the given path
|
||||||
|
// + Sets mHasTexture to enable texture binding in draw()
|
||||||
|
void setTexture(const char * path);
|
||||||
|
void setTexture(QOpenGLTexture * texture);
|
||||||
|
|
||||||
|
// These functions modify data stored in a VBO
|
||||||
|
// + After calling them, the VBO will need to be reallocated
|
||||||
|
void setShape(const Shape & value) override;
|
||||||
|
void setColor(const QVector3D & color);
|
||||||
|
|
||||||
|
// Static QHash of all mesh objects within the scene
|
||||||
|
typedef QHash<QString, MeshRenderer *> MeshManager;
|
||||||
|
private:
|
||||||
|
static MeshManager sInstances;
|
||||||
|
|
||||||
|
int mDrawType;
|
||||||
|
bool mHasTexture;
|
||||||
|
std::string mVertexShader, mFragmentShader;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // QTK_MESHRENDERER_H
|
|
@ -0,0 +1,401 @@
|
||||||
|
/*##############################################################################
|
||||||
|
## Author: Shaun Reed ##
|
||||||
|
## Legal: All Content (c) 2021 Shaun Reed, all rights reserved ##
|
||||||
|
## About: Model classes for importing with Assimp ##
|
||||||
|
## From following tutorials on learnopengl.com ##
|
||||||
|
## ##
|
||||||
|
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
|
||||||
|
##############################################################################*/
|
||||||
|
|
||||||
|
#include <QFileInfo>
|
||||||
|
|
||||||
|
#include <mainwidget.h>
|
||||||
|
#include <texture.h>
|
||||||
|
|
||||||
|
#include <model.h>
|
||||||
|
|
||||||
|
|
||||||
|
Model::ModelManager Model::mManager;
|
||||||
|
|
||||||
|
// Static function to access ModelManager for getting Models by name
|
||||||
|
Model * Model::getInstance(const char * name)
|
||||||
|
{
|
||||||
|
return mManager[name];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
* ModelMesh Private Member Functions
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
void ModelMesh::initMesh(const char * vert, const char * frag)
|
||||||
|
{
|
||||||
|
// Create VAO, VBO, EBO
|
||||||
|
mVAO->create();
|
||||||
|
mVBO->create();
|
||||||
|
mEBO->create();
|
||||||
|
|
||||||
|
mVAO->bind();
|
||||||
|
|
||||||
|
// Allocate VBO
|
||||||
|
mVBO->setUsagePattern(QOpenGLBuffer::StaticDraw);
|
||||||
|
mVBO->bind();
|
||||||
|
|
||||||
|
mVBO->allocate(mVertices.data(),
|
||||||
|
mVertices.size() * sizeof(mVertices[0]));
|
||||||
|
|
||||||
|
// Allocate EBO
|
||||||
|
mEBO->setUsagePattern(QOpenGLBuffer::StaticDraw);
|
||||||
|
mEBO->bind();
|
||||||
|
mEBO->allocate(mIndices.data(),
|
||||||
|
mIndices.size() * sizeof(mIndices[0]));
|
||||||
|
mEBO->release();
|
||||||
|
|
||||||
|
// Load and link shaders
|
||||||
|
mProgram->addShaderFromSourceFile(QOpenGLShader::Vertex, vert);
|
||||||
|
mProgram->addShaderFromSourceFile(QOpenGLShader::Fragment, frag);
|
||||||
|
mProgram->link();
|
||||||
|
mProgram->bind();
|
||||||
|
|
||||||
|
// Positions
|
||||||
|
mProgram->enableAttributeArray(0);
|
||||||
|
mProgram->setAttributeBuffer(0, GL_FLOAT,
|
||||||
|
offsetof(ModelVertex, mPosition), 3,
|
||||||
|
sizeof(ModelVertex));
|
||||||
|
|
||||||
|
// Normals
|
||||||
|
mProgram->enableAttributeArray(1);
|
||||||
|
mProgram->setAttributeBuffer(1, GL_FLOAT,
|
||||||
|
offsetof(ModelVertex, mNormal), 3,
|
||||||
|
sizeof(ModelVertex));
|
||||||
|
|
||||||
|
// Texture Coordinates
|
||||||
|
mProgram->enableAttributeArray(2);
|
||||||
|
mProgram->setAttributeBuffer(2, GL_FLOAT,
|
||||||
|
offsetof(ModelVertex, mTextureCoord), 2,
|
||||||
|
sizeof(ModelVertex));
|
||||||
|
|
||||||
|
// Vertex tangents
|
||||||
|
mProgram->enableAttributeArray(3);
|
||||||
|
mProgram->setAttributeBuffer(3, GL_FLOAT,
|
||||||
|
offsetof(ModelVertex, mTangent), 3,
|
||||||
|
sizeof(ModelVertex));
|
||||||
|
|
||||||
|
// Vertex bitangents
|
||||||
|
mProgram->enableAttributeArray(4);
|
||||||
|
mProgram->setAttributeBuffer(4, GL_FLOAT,
|
||||||
|
offsetof(ModelVertex, mBitangent), 3,
|
||||||
|
sizeof(ModelVertex));
|
||||||
|
|
||||||
|
mProgram->release();
|
||||||
|
mVBO->release();
|
||||||
|
mVAO->release();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
* ModelMesh Public Member Functions
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
void ModelMesh::draw(QOpenGLShaderProgram & shader)
|
||||||
|
{
|
||||||
|
mVAO->bind();
|
||||||
|
// Bind shader
|
||||||
|
shader.bind();
|
||||||
|
|
||||||
|
// Set Model View Projection values
|
||||||
|
shader.setUniformValue("uModel", mTransform.toMatrix());
|
||||||
|
shader.setUniformValue("uView", MainWidget::Camera().toMatrix());
|
||||||
|
shader.setUniformValue("uProjection", MainWidget::Projection());
|
||||||
|
|
||||||
|
GLuint diffuseCount = 1;
|
||||||
|
GLuint specularCount = 1;
|
||||||
|
GLuint normalCount = 1;
|
||||||
|
for (GLuint i = 0; i < mTextures.size(); i++) {
|
||||||
|
// Activate the current texture index by adding offset to GL_TEXTURE0
|
||||||
|
glActiveTexture(GL_TEXTURE0 + i);
|
||||||
|
mTextures[i].mTexture->bind();
|
||||||
|
|
||||||
|
// Get a name for the texture using a known convention -
|
||||||
|
// Diffuse: material.texture_diffuse1, material.texture_diffuse2, ...
|
||||||
|
// Specular: material.texture_specular1, material.texture_specular2, ...
|
||||||
|
std::string number;
|
||||||
|
std::string name = mTextures[i].mType;
|
||||||
|
if (name == "texture_diffuse") number = std::to_string(diffuseCount++);
|
||||||
|
if (name == "texture_specular") number = std::to_string(specularCount++);
|
||||||
|
if (name == "texture_normal") number = std::to_string(normalCount++);
|
||||||
|
|
||||||
|
// Set the uniform to track this texture ID using our naming convention
|
||||||
|
shader.setUniformValue((name + number).c_str(), i);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw the mesh
|
||||||
|
glDrawElements(GL_TRIANGLES, mIndices.size(),
|
||||||
|
GL_UNSIGNED_INT, mIndices.data());
|
||||||
|
|
||||||
|
// Release shader, textures
|
||||||
|
for (const auto & texture : mTextures) {
|
||||||
|
texture.mTexture->release();
|
||||||
|
}
|
||||||
|
shader.release();
|
||||||
|
mVAO->release();
|
||||||
|
glActiveTexture(GL_TEXTURE0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
* Model Public Member Functions
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
void Model::draw()
|
||||||
|
{
|
||||||
|
for (GLuint i = 0; i < mMeshes.size(); i++) {
|
||||||
|
mMeshes[i].mTransform = mTransform;
|
||||||
|
mMeshes[i].draw();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Model::draw(QOpenGLShaderProgram & shader)
|
||||||
|
{
|
||||||
|
for (GLuint i = 0; i < mMeshes.size(); i++) {
|
||||||
|
mMeshes[i].mTransform = mTransform;
|
||||||
|
mMeshes[i].draw(shader);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Model::flipTexture(const std::string & fileName, bool flipX, bool flipY)
|
||||||
|
{
|
||||||
|
bool modified = false;
|
||||||
|
std::string fullPath = mDirectory + '/' + fileName;
|
||||||
|
for (auto & texture : mTexturesLoaded) {
|
||||||
|
if (texture.mPath == fileName) {
|
||||||
|
texture.mTexture->destroy();
|
||||||
|
texture.mTexture->create();
|
||||||
|
texture.mTexture->setData(
|
||||||
|
*Texture::initImage(fullPath.c_str(), flipX, flipY));
|
||||||
|
modified = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!modified) {
|
||||||
|
qDebug() << "Attempt to flip texture that doesn't exist: "
|
||||||
|
<< fullPath.c_str() << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
* Model Private Member Functions
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
void Model::loadModel(const std::string & path)
|
||||||
|
{
|
||||||
|
Assimp::Importer import;
|
||||||
|
|
||||||
|
// JIC a relative path was used, get the absolute file path
|
||||||
|
QFileInfo info(path.c_str());
|
||||||
|
info.makeAbsolute();
|
||||||
|
std::string temp = info.absoluteFilePath().toStdString();
|
||||||
|
|
||||||
|
// Import the model, converting non-triangular geometry to triangles
|
||||||
|
// + And flipping texture UVs, etc..
|
||||||
|
// Assimp options: http://assimp.sourceforge.net/lib_html/postprocess_8h.html
|
||||||
|
const aiScene * scene =
|
||||||
|
import.ReadFile(temp, aiProcess_Triangulate
|
||||||
|
| aiProcess_FlipUVs
|
||||||
|
| aiProcess_GenSmoothNormals
|
||||||
|
| aiProcess_CalcTangentSpace
|
||||||
|
| aiProcess_OptimizeMeshes
|
||||||
|
| aiProcess_SplitLargeMeshes
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
// If there were errors, print and return
|
||||||
|
if (!scene || scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode) {
|
||||||
|
qDebug() << "Error::ASSIMP::" << import.GetErrorString() << "\n";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// If there were no errors, find the directory that contains this model
|
||||||
|
mDirectory = path.substr(0, path.find_last_of('/'));
|
||||||
|
|
||||||
|
// Pass the pointers to the root node and the scene to recursive function
|
||||||
|
// + Base case breaks when no nodes left to process on model
|
||||||
|
processNode(scene->mRootNode, scene);
|
||||||
|
|
||||||
|
// Sort models by their distance from the camera
|
||||||
|
// Optimizes drawing so that overlapping objects are not overwritten
|
||||||
|
// + Since the topmost object will be drawn first
|
||||||
|
sortModels();
|
||||||
|
|
||||||
|
// Object finished loading, insert it into ModelManager
|
||||||
|
mManager.insert(mName, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Model::processNode(aiNode * node, const aiScene * scene)
|
||||||
|
{
|
||||||
|
// Process each mesh that is available for this node
|
||||||
|
for (GLuint i = 0; i < node->mNumMeshes; i++) {
|
||||||
|
aiMesh * mesh = scene->mMeshes[node->mMeshes[i]];
|
||||||
|
mMeshes.push_back(processMesh(mesh, scene));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process each child node for this mesh using recursion
|
||||||
|
for (GLuint i = 0; i < node->mNumChildren; i++) {
|
||||||
|
processNode(node->mChildren[i], scene);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ModelMesh Model::processMesh(aiMesh * mesh, const aiScene * scene)
|
||||||
|
{
|
||||||
|
ModelMesh::Vertices vertices;
|
||||||
|
ModelMesh::Indices indices;
|
||||||
|
ModelMesh::Textures textures;
|
||||||
|
|
||||||
|
// For each vertex in the aiMesh
|
||||||
|
for (GLuint i = 0; i < mesh->mNumVertices; i++) {
|
||||||
|
// Create a local vertex object for positions, normals, and texture coords
|
||||||
|
ModelVertex vertex;
|
||||||
|
|
||||||
|
// Reuse this vector to initialize positions and normals
|
||||||
|
QVector3D vector3D;
|
||||||
|
|
||||||
|
// Initialize vertex position
|
||||||
|
vector3D.setX(mesh->mVertices[i].x);
|
||||||
|
vector3D.setY(mesh->mVertices[i].y);
|
||||||
|
vector3D.setZ(mesh->mVertices[i].z);
|
||||||
|
// Set the position of our local vertex to the local vector object
|
||||||
|
vertex.mPosition = vector3D;
|
||||||
|
|
||||||
|
if (mesh->HasNormals()) {
|
||||||
|
// Initialize vertex normal
|
||||||
|
vector3D.setX(mesh->mNormals[i].x);
|
||||||
|
vector3D.setY(mesh->mNormals[i].y);
|
||||||
|
vector3D.setZ(mesh->mNormals[i].z);
|
||||||
|
// Set the normals of our local vertex to the local vector object
|
||||||
|
vertex.mNormal = vector3D;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize texture coordinates, if any are available
|
||||||
|
if (mesh->mTextureCoords[0]) {
|
||||||
|
QVector2D vector2D;
|
||||||
|
// Texture coordinates
|
||||||
|
vector2D.setX(mesh->mTextureCoords[0][i].x);
|
||||||
|
vector2D.setY(mesh->mTextureCoords[0][i].y);
|
||||||
|
vertex.mTextureCoord = vector2D;
|
||||||
|
|
||||||
|
// Tangents
|
||||||
|
vector3D.setX(mesh->mTangents[i].x);
|
||||||
|
vector3D.setY(mesh->mTangents[i].y);
|
||||||
|
vector3D.setZ(mesh->mTangents[i].z);
|
||||||
|
vertex.mTangent = vector3D;
|
||||||
|
|
||||||
|
// Bitangents
|
||||||
|
vector3D.setX(mesh->mBitangents[i].x);
|
||||||
|
vector3D.setY(mesh->mBitangents[i].y);
|
||||||
|
vector3D.setZ(mesh->mBitangents[i].z);
|
||||||
|
vertex.mBitangent = vector3D;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
vertex.mTextureCoord = {0.0f, 0.0f};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the initialized vertex to our container of vertices
|
||||||
|
vertices.push_back(vertex);
|
||||||
|
}
|
||||||
|
|
||||||
|
// For each face on the mesh, process its indices
|
||||||
|
for (GLuint i = 0; i < mesh->mNumFaces; i++) {
|
||||||
|
aiFace face = mesh->mFaces[i];
|
||||||
|
for (GLuint j = 0; j < face.mNumIndices; j++) {
|
||||||
|
// Add the index to out container of indices
|
||||||
|
indices.push_back(face.mIndices[j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process material
|
||||||
|
if (mesh->mMaterialIndex >= 0) {
|
||||||
|
// Get the material attached to the model using Assimp
|
||||||
|
aiMaterial * material = scene->mMaterials[mesh->mMaterialIndex];
|
||||||
|
|
||||||
|
// Get all diffuse textures from the material
|
||||||
|
ModelMesh::Textures diffuseMaps =
|
||||||
|
loadMaterialTextures(material, aiTextureType_DIFFUSE,
|
||||||
|
"texture_diffuse");
|
||||||
|
// Insert all diffuse textures found into our textures container
|
||||||
|
textures.insert(textures.end(), diffuseMaps.begin(), diffuseMaps.end());
|
||||||
|
|
||||||
|
// Get all specular textures from the material
|
||||||
|
ModelMesh::Textures specularMaps =
|
||||||
|
loadMaterialTextures(material, aiTextureType_SPECULAR,
|
||||||
|
"texture_specular");
|
||||||
|
// Insert all specular textures found into our textures container
|
||||||
|
textures.insert(textures.end(), specularMaps.begin(), specularMaps.end());
|
||||||
|
|
||||||
|
// Get all normal textures from the material
|
||||||
|
ModelMesh::Textures normalMaps =
|
||||||
|
loadMaterialTextures(material, aiTextureType_HEIGHT,
|
||||||
|
"texture_normal");
|
||||||
|
// Insert all normal maps found into our textures container
|
||||||
|
textures.insert(textures.end(), normalMaps.begin(), normalMaps.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
return ModelMesh(vertices, indices, textures,
|
||||||
|
mVertexShader, mFragmentShader);
|
||||||
|
}
|
||||||
|
|
||||||
|
ModelMesh::Textures Model::loadMaterialTextures(
|
||||||
|
aiMaterial * mat, aiTextureType type, const std::string & typeName)
|
||||||
|
{
|
||||||
|
ModelMesh::Textures textures;
|
||||||
|
|
||||||
|
for (GLuint i = 0; i < mat->GetTextureCount(type); i++) {
|
||||||
|
|
||||||
|
// Call GetTexture to get the name of the texture file to load
|
||||||
|
aiString fileName;
|
||||||
|
mat->GetTexture(type, i, &fileName);
|
||||||
|
|
||||||
|
// Check if we have already loaded this texture
|
||||||
|
bool skip = false;
|
||||||
|
for (GLuint j = 0; j < mTexturesLoaded.size(); j++) {
|
||||||
|
// If the path to the texture already exists in m_texturesLoaded, skip it
|
||||||
|
if (std::strcmp(mTexturesLoaded[j].mPath.data(), fileName.C_Str()) == 0) {
|
||||||
|
textures.push_back(mTexturesLoaded[j]);
|
||||||
|
// If we have loaded the texture, do not load it again
|
||||||
|
skip = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the texture has not yet been loaded
|
||||||
|
if (!skip) {
|
||||||
|
ModelTexture texture;
|
||||||
|
texture.mTexture = Texture::initTexture2D(
|
||||||
|
std::string(mDirectory + '/' + fileName.C_Str()).c_str(),
|
||||||
|
false, false);
|
||||||
|
texture.mID = texture.mTexture->textureId();
|
||||||
|
texture.mType = typeName;
|
||||||
|
texture.mPath = fileName.C_Str();
|
||||||
|
// Add the texture to the textures container
|
||||||
|
textures.push_back(texture);
|
||||||
|
// Add the texture to the loaded textures to avoid loading it twice
|
||||||
|
mTexturesLoaded.push_back(texture);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the resulting textures
|
||||||
|
return textures;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Model::sortModels()
|
||||||
|
{
|
||||||
|
auto cameraPos = MainWidget::Camera().transform();
|
||||||
|
auto cameraDistance = [&cameraPos](const ModelMesh &a, const ModelMesh &b)
|
||||||
|
{
|
||||||
|
// Sort by the first vertex position, since all transforms will be the same
|
||||||
|
return (cameraPos.translation().distanceToPoint(a.mVertices[0].mPosition))
|
||||||
|
< (cameraPos.translation().distanceToPoint(b.mVertices[0].mPosition));
|
||||||
|
};
|
||||||
|
std::sort(mMeshes.begin(), mMeshes.end(), cameraDistance);
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,139 @@
|
||||||
|
/*##############################################################################
|
||||||
|
## Author: Shaun Reed ##
|
||||||
|
## Legal: All Content (c) 2021 Shaun Reed, all rights reserved ##
|
||||||
|
## About: Model classes for importing with Assimp ##
|
||||||
|
## From following tutorials on learnopengl.com ##
|
||||||
|
## ##
|
||||||
|
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
|
||||||
|
##############################################################################*/
|
||||||
|
#ifndef QTK_MODEL_H
|
||||||
|
#define QTK_MODEL_H
|
||||||
|
|
||||||
|
// QT
|
||||||
|
#include <QObject>
|
||||||
|
#include <QOpenGLBuffer>
|
||||||
|
#include <QOpenGLFunctions>
|
||||||
|
#include <QOpenGLShaderProgram>
|
||||||
|
#include <QOpenGLTexture>
|
||||||
|
#include <QOpenGLVertexArrayObject>
|
||||||
|
|
||||||
|
// Assimp
|
||||||
|
#include <assimp/Importer.hpp>
|
||||||
|
#include <assimp/postprocess.h>
|
||||||
|
#include <assimp/scene.h>
|
||||||
|
|
||||||
|
// QTK
|
||||||
|
#include <transform3D.h>
|
||||||
|
|
||||||
|
|
||||||
|
struct ModelVertex {
|
||||||
|
QVector3D mPosition;
|
||||||
|
QVector3D mNormal;
|
||||||
|
QVector3D mTangent;
|
||||||
|
QVector3D mBitangent;
|
||||||
|
QVector2D mTextureCoord;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ModelTexture {
|
||||||
|
GLuint mID;
|
||||||
|
QOpenGLTexture * mTexture;
|
||||||
|
std::string mType;
|
||||||
|
std::string mPath;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Model;
|
||||||
|
|
||||||
|
class ModelMesh {
|
||||||
|
public:
|
||||||
|
friend Model;
|
||||||
|
typedef std::vector<ModelVertex> Vertices;
|
||||||
|
typedef std::vector<GLuint> Indices;
|
||||||
|
typedef std::vector<ModelTexture> Textures;
|
||||||
|
|
||||||
|
// Constructors, Destructors
|
||||||
|
ModelMesh(Vertices vertices, Indices indices, Textures textures,
|
||||||
|
const char * vertexShader=":/model-basic.vert",
|
||||||
|
const char * fragmentShader=":/model-basic.frag")
|
||||||
|
: mProgram(new QOpenGLShaderProgram),
|
||||||
|
mVAO(new QOpenGLVertexArrayObject),
|
||||||
|
mVBO(new QOpenGLBuffer(QOpenGLBuffer::VertexBuffer)),
|
||||||
|
mEBO(new QOpenGLBuffer(QOpenGLBuffer::IndexBuffer)),
|
||||||
|
mVertices(std::move(vertices)),
|
||||||
|
mIndices(std::move(indices)),
|
||||||
|
mTextures(std::move(textures))
|
||||||
|
{ initMesh(vertexShader, fragmentShader);}
|
||||||
|
~ModelMesh() {}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void initMesh(const char * vert, const char * frag);
|
||||||
|
|
||||||
|
// ModelMesh Private Members
|
||||||
|
QOpenGLBuffer * mVBO, * mEBO;
|
||||||
|
QOpenGLVertexArrayObject * mVAO;
|
||||||
|
QOpenGLShaderProgram * mProgram;
|
||||||
|
|
||||||
|
public:
|
||||||
|
inline void draw() { draw(*mProgram);}
|
||||||
|
void draw(QOpenGLShaderProgram & shader);
|
||||||
|
|
||||||
|
// ModelMesh Public Members
|
||||||
|
Vertices mVertices;
|
||||||
|
Indices mIndices;
|
||||||
|
Textures mTextures;
|
||||||
|
Transform3D mTransform;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class Model : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
inline Model(const char * name, const char * path,
|
||||||
|
const char * vertexShader=":/model-basic.vert",
|
||||||
|
const char * fragmentShader=":/model-basic.frag")
|
||||||
|
: mName(name), mVertexShader(vertexShader),
|
||||||
|
mFragmentShader(fragmentShader)
|
||||||
|
{ loadModel(path);}
|
||||||
|
inline ~Model() { mManager.remove(mName);}
|
||||||
|
|
||||||
|
void draw();
|
||||||
|
void draw(QOpenGLShaderProgram & shader);
|
||||||
|
|
||||||
|
void flipTexture(const std::string & fileName,
|
||||||
|
bool flipX=false, bool flipY=true);
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void setUniform(const char * location, T value)
|
||||||
|
{
|
||||||
|
for (auto & mesh : mMeshes) {
|
||||||
|
mesh.mProgram->bind();
|
||||||
|
|
||||||
|
mesh.mProgram->setUniformValue(location, value);
|
||||||
|
|
||||||
|
mesh.mProgram->release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Transform3D mTransform;
|
||||||
|
|
||||||
|
static Model * getInstance(const char * name);
|
||||||
|
|
||||||
|
typedef QHash<QString, Model *> ModelManager;
|
||||||
|
private:
|
||||||
|
static ModelManager mManager;
|
||||||
|
void loadModel(const std::string & path);
|
||||||
|
void processNode(aiNode * node, const aiScene * scene);
|
||||||
|
ModelMesh processMesh(aiMesh * mesh, const aiScene * scene);
|
||||||
|
ModelMesh::Textures loadMaterialTextures(aiMaterial * mat, aiTextureType type,
|
||||||
|
const std::string & typeName);
|
||||||
|
void sortModels();
|
||||||
|
|
||||||
|
// Model Private Members
|
||||||
|
|
||||||
|
ModelMesh::Textures mTexturesLoaded;
|
||||||
|
std::vector<ModelMesh> mMeshes;
|
||||||
|
std::string mDirectory;
|
||||||
|
const char * mVertexShader, * mFragmentShader, * mName;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // QTK_MODEL_H
|
|
@ -0,0 +1,9 @@
|
||||||
|
/*##############################################################################
|
||||||
|
## Author: Shaun Reed ##
|
||||||
|
## Legal: All Content (c) 2021 Shaun Reed, all rights reserved ##
|
||||||
|
## About: Object class for storing object data ##
|
||||||
|
## ##
|
||||||
|
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
|
||||||
|
##############################################################################*/
|
||||||
|
|
||||||
|
#include <object.h>
|
|
@ -0,0 +1,62 @@
|
||||||
|
/*##############################################################################
|
||||||
|
## Author: Shaun Reed ##
|
||||||
|
## Legal: All Content (c) 2021 Shaun Reed, all rights reserved ##
|
||||||
|
## About: Object class for storing object data ##
|
||||||
|
## ##
|
||||||
|
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
|
||||||
|
##############################################################################*/
|
||||||
|
#ifndef QTK_OBJECT_H
|
||||||
|
#define QTK_OBJECT_H
|
||||||
|
|
||||||
|
#include <QOpenGLBuffer>
|
||||||
|
#include <QOpenGLShaderProgram>
|
||||||
|
#include <QOpenGLTexture>
|
||||||
|
#include <QOpenGLVertexArrayObject>
|
||||||
|
|
||||||
|
#include <mainwidget.h>
|
||||||
|
#include <mesh.h>
|
||||||
|
|
||||||
|
|
||||||
|
class Object : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
friend MeshRenderer;
|
||||||
|
// Initialize an object with no shape data assigned
|
||||||
|
Object(const char * name)
|
||||||
|
: mName(name), mVBO(QOpenGLBuffer::VertexBuffer)
|
||||||
|
{ }
|
||||||
|
// Initialize an object with shape data assigned
|
||||||
|
Object(const char * name, const ShapeBase & shape)
|
||||||
|
: mName(name), mVBO(QOpenGLBuffer::VertexBuffer), mShape(shape)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
~Object() {}
|
||||||
|
|
||||||
|
inline const Vertices & vertices() { return mShape.mVertices;}
|
||||||
|
inline const Indices & indices() { return mShape.mIndices;}
|
||||||
|
inline const Colors & colors() { return mShape.mColors;}
|
||||||
|
inline const TexCoords & texCoords() { return mShape.mTexCoords;}
|
||||||
|
inline const Normals & normals() { return mShape.mNormals;}
|
||||||
|
inline QOpenGLTexture & texture() const { return *mTexture;}
|
||||||
|
|
||||||
|
virtual inline void setVertices(const Vertices & value) { mShape.mVertices = value;}
|
||||||
|
virtual inline void setIndices(const Indices & value) { mShape.mIndices = value;}
|
||||||
|
virtual inline void setColors(const Colors & value) { mShape.mColors = value;}
|
||||||
|
virtual inline void setTexCoords(const TexCoords & value) { mShape.mTexCoords = value;}
|
||||||
|
virtual inline void setNormals(const Normals & value) { mShape.mNormals = value;}
|
||||||
|
virtual inline void setShape(const Shape & value) { mShape = value;}
|
||||||
|
|
||||||
|
QOpenGLBuffer mVBO, mNBO;
|
||||||
|
QOpenGLVertexArrayObject mVAO;
|
||||||
|
QOpenGLShaderProgram mProgram;
|
||||||
|
|
||||||
|
Transform3D mTransform;
|
||||||
|
Shape mShape;
|
||||||
|
|
||||||
|
const char * mName;
|
||||||
|
private:
|
||||||
|
QOpenGLTexture * mTexture;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // QTK_OBJECT_H
|
|
@ -0,0 +1,100 @@
|
||||||
|
/*##############################################################################
|
||||||
|
## Author: Shaun Reed ##
|
||||||
|
## Legal: All Content (c) 2021 Shaun Reed, all rights reserved ##
|
||||||
|
## About: Skybox class using QtOpenGL ##
|
||||||
|
## ##
|
||||||
|
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
|
||||||
|
##############################################################################*/
|
||||||
|
|
||||||
|
#include <texture.h>
|
||||||
|
|
||||||
|
#include <skybox.h>
|
||||||
|
|
||||||
|
|
||||||
|
Skybox::Skybox(std::string right, std::string top, std::string front,
|
||||||
|
std::string left, std::string bottom, std::string back,
|
||||||
|
const std::string & name)
|
||||||
|
: mCubeMap(Texture::initCubeMap(
|
||||||
|
QImage(right.c_str()).mirrored(), QImage(top.c_str()),
|
||||||
|
QImage(front.c_str()), QImage(left.c_str()),
|
||||||
|
QImage(bottom.c_str()), QImage(back.c_str()))),
|
||||||
|
mVBO(QOpenGLBuffer::VertexBuffer),
|
||||||
|
mVertices(Cube(QTK_DRAW_ELEMENTS).vertices()),
|
||||||
|
mIndices(Cube(QTK_DRAW_ELEMENTS).indices())
|
||||||
|
{ init();}
|
||||||
|
|
||||||
|
Skybox::Skybox(std::string name)
|
||||||
|
: Skybox(":/right.png", ":/top.png", ":/front.png",
|
||||||
|
":/left.png", ":/bottom.png", ":/back.png",
|
||||||
|
name)
|
||||||
|
{}
|
||||||
|
|
||||||
|
Skybox::Skybox(QOpenGLTexture * cubeMap, const std::string & name)
|
||||||
|
: mCubeMap(cubeMap) { init();}
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
* Public Member Functions
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
void Skybox::draw()
|
||||||
|
{
|
||||||
|
glDepthFunc(GL_LEQUAL);
|
||||||
|
glDepthMask(GL_FALSE);
|
||||||
|
|
||||||
|
mVAO.bind();
|
||||||
|
mProgram.bind();
|
||||||
|
mCubeMap->bind();
|
||||||
|
|
||||||
|
mProgram.setUniformValue("uProjectionMatrix", MainWidget::Projection());
|
||||||
|
mProgram.setUniformValue("uViewMatrix", MainWidget::Camera().toMatrix());
|
||||||
|
mProgram.setUniformValue("uTexture", 0);
|
||||||
|
glDrawElements(GL_TRIANGLES, mIndices.size(),
|
||||||
|
GL_UNSIGNED_INT, mIndices.data());
|
||||||
|
|
||||||
|
mCubeMap->release();
|
||||||
|
mProgram.release();
|
||||||
|
mVAO.release();
|
||||||
|
|
||||||
|
glDepthFunc(GL_LESS);
|
||||||
|
glDepthMask(GL_TRUE);
|
||||||
|
glActiveTexture(GL_TEXTURE0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
* Private Member Functions
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
void Skybox::init()
|
||||||
|
{
|
||||||
|
// Set up shader program
|
||||||
|
mProgram.create();
|
||||||
|
mProgram.addShaderFromSourceFile(QOpenGLShader::Vertex, ":/skybox.vert");
|
||||||
|
mProgram.addShaderFromSourceFile(QOpenGLShader::Fragment, ":/skybox.frag");
|
||||||
|
mProgram.link();
|
||||||
|
mProgram.bind();
|
||||||
|
|
||||||
|
// Setup VAO
|
||||||
|
mVAO.create();
|
||||||
|
mVAO.bind();
|
||||||
|
|
||||||
|
// Setup VBO for vertex position data
|
||||||
|
mVBO.create();
|
||||||
|
mVBO.setUsagePattern(QOpenGLBuffer::StaticDraw);
|
||||||
|
mVBO.bind();
|
||||||
|
// Allocate vertex positions into VBO
|
||||||
|
mVBO.allocate(mVertices.data(),
|
||||||
|
mVertices.size() * sizeof(mVertices[0]));
|
||||||
|
|
||||||
|
// Enable attribute array for vertex positions
|
||||||
|
mProgram.enableAttributeArray(0);
|
||||||
|
mProgram.setAttributeBuffer(0, GL_FLOAT, 0,
|
||||||
|
3, sizeof(QVector3D));
|
||||||
|
// Set shader texture unit to 0
|
||||||
|
mProgram.setUniformValue("uTexture", 0);
|
||||||
|
|
||||||
|
mVAO.release();
|
||||||
|
mVBO.release();
|
||||||
|
mProgram.release();
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
/*##############################################################################
|
||||||
|
## Author: Shaun Reed ##
|
||||||
|
## Legal: All Content (c) 2021 Shaun Reed, all rights reserved ##
|
||||||
|
## About: Skybox class using QtOpenGL ##
|
||||||
|
## ##
|
||||||
|
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
|
||||||
|
##############################################################################*/
|
||||||
|
#ifndef QTK_SKYBOX_H
|
||||||
|
#define QTK_SKYBOX_H
|
||||||
|
|
||||||
|
#include <QImage>
|
||||||
|
#include <QOpenGLBuffer>
|
||||||
|
#include <QOpenGLShaderProgram>
|
||||||
|
#include <QOpenGLTexture>
|
||||||
|
#include <QOpenGLVertexArrayObject>
|
||||||
|
|
||||||
|
#include <camera3d.h>
|
||||||
|
#include <mainwidget.h>
|
||||||
|
#include <mesh.h>
|
||||||
|
|
||||||
|
|
||||||
|
class Skybox {
|
||||||
|
public:
|
||||||
|
// Delegate this constructor to use default skybox images
|
||||||
|
// + This allows creating a skybox with no arguments ( auto s = new Skybox; )
|
||||||
|
explicit Skybox(std::string name="Skybox");
|
||||||
|
explicit Skybox(QOpenGLTexture * cubeMap, const std::string & name="Skybox");
|
||||||
|
// Constructor, Destructor
|
||||||
|
Skybox(std::string right, std::string top, std::string front,
|
||||||
|
std::string left, std::string bottom, std::string back,
|
||||||
|
const std::string & name="Skybox");
|
||||||
|
~Skybox() {}
|
||||||
|
|
||||||
|
void draw();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void init();
|
||||||
|
|
||||||
|
Vertices mVertices;
|
||||||
|
Indices mIndices;
|
||||||
|
|
||||||
|
QOpenGLShaderProgram mProgram;
|
||||||
|
QOpenGLVertexArrayObject mVAO;
|
||||||
|
QOpenGLBuffer mVBO;
|
||||||
|
QOpenGLTexture * mCubeMap;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // QTK_SKYBOX_H
|
|
@ -0,0 +1,103 @@
|
||||||
|
/*##############################################################################
|
||||||
|
## Author: Shaun Reed ##
|
||||||
|
## Legal: All Content (c) 2021 Shaun Reed, all rights reserved ##
|
||||||
|
## About: Texture class to help with texture and image initializations ##
|
||||||
|
## ##
|
||||||
|
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
|
||||||
|
##############################################################################*/
|
||||||
|
|
||||||
|
#include <QImageReader>
|
||||||
|
|
||||||
|
#include <texture.h>
|
||||||
|
|
||||||
|
|
||||||
|
QImage * Texture::initImage(const char * image, bool flipX, bool flipY)
|
||||||
|
{
|
||||||
|
auto loadedImage = new QImage(QImage(image).mirrored(flipX, flipY));
|
||||||
|
if (loadedImage->isNull()) {
|
||||||
|
qDebug() << "Error loading image: " << image << "\n";
|
||||||
|
qDebug() << QImageReader::supportedImageFormats();
|
||||||
|
return Q_NULLPTR;
|
||||||
|
}
|
||||||
|
|
||||||
|
return loadedImage;
|
||||||
|
}
|
||||||
|
|
||||||
|
QOpenGLTexture * Texture::initTexture2D(const char * texture,
|
||||||
|
bool flipX, bool flipY)
|
||||||
|
{
|
||||||
|
QImage * image = initImage(texture, flipX, flipY);
|
||||||
|
auto newTexture = new QOpenGLTexture(QOpenGLTexture::Target2D);
|
||||||
|
newTexture->setData(*image);
|
||||||
|
newTexture->setWrapMode(QOpenGLTexture::Repeat);
|
||||||
|
newTexture->setMinMagFilters(QOpenGLTexture::LinearMipMapLinear,
|
||||||
|
QOpenGLTexture::Linear);
|
||||||
|
delete image;
|
||||||
|
return newTexture;
|
||||||
|
}
|
||||||
|
|
||||||
|
QOpenGLTexture * Texture::initCubeMap(const char * tile)
|
||||||
|
{
|
||||||
|
return initCubeMap(QImage(tile), QImage(tile),
|
||||||
|
QImage(tile), QImage(tile),
|
||||||
|
QImage(tile), QImage(tile));
|
||||||
|
}
|
||||||
|
|
||||||
|
QOpenGLTexture * Texture::initCubeMap(
|
||||||
|
const char * right, const char * top,
|
||||||
|
const char * front, const char * left,
|
||||||
|
const char * bottom, const char * back)
|
||||||
|
{
|
||||||
|
return initCubeMap(QImage(right), QImage(top),
|
||||||
|
QImage(front), QImage(left),
|
||||||
|
QImage(bottom), QImage(back));
|
||||||
|
}
|
||||||
|
|
||||||
|
QOpenGLTexture * Texture::initCubeMap(
|
||||||
|
QImage right, QImage top,
|
||||||
|
QImage front, QImage left,
|
||||||
|
QImage bottom, QImage back)
|
||||||
|
{
|
||||||
|
auto texture = new QOpenGLTexture(QOpenGLTexture::TargetCubeMap);
|
||||||
|
std::vector<QImage> faceTextures = {
|
||||||
|
right, top, front,
|
||||||
|
left, bottom, back
|
||||||
|
};
|
||||||
|
// Initialize skybox cubemap texture
|
||||||
|
texture->create();
|
||||||
|
texture->bind();
|
||||||
|
// For each cube map face
|
||||||
|
std::vector<QOpenGLTexture::CubeMapFace> faces = {
|
||||||
|
QOpenGLTexture::CubeMapPositiveX, QOpenGLTexture::CubeMapPositiveY,
|
||||||
|
QOpenGLTexture::CubeMapPositiveZ, QOpenGLTexture::CubeMapNegativeX,
|
||||||
|
QOpenGLTexture::CubeMapNegativeY, QOpenGLTexture::CubeMapNegativeZ
|
||||||
|
};
|
||||||
|
int i = 0;
|
||||||
|
for (const auto & face : faces) {
|
||||||
|
QImage faceImage(faceTextures[i]);
|
||||||
|
if (faceImage.isNull()) {
|
||||||
|
qDebug() << "Error loading cube map image\n";
|
||||||
|
}
|
||||||
|
faceImage = faceImage.convertToFormat(QImage::Format_RGBA8888);
|
||||||
|
|
||||||
|
// On the first iteration, set format and allocate texture storage
|
||||||
|
if (face == QOpenGLTexture::CubeMapPositiveX) {
|
||||||
|
// This also needs to happen on the first iteration, anyways
|
||||||
|
texture->setSize(faceImage.width(), faceImage.height(), faceImage.depth());
|
||||||
|
texture->setFormat(QOpenGLTexture::RGBA8_UNorm);
|
||||||
|
texture->allocateStorage();
|
||||||
|
}
|
||||||
|
|
||||||
|
texture->setData(0, 0, face,
|
||||||
|
QOpenGLTexture::RGBA, QOpenGLTexture::UInt8,
|
||||||
|
faceImage.constBits());
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
texture->setWrapMode(QOpenGLTexture::ClampToEdge);
|
||||||
|
texture->generateMipMaps();
|
||||||
|
texture->setMinificationFilter(QOpenGLTexture::LinearMipMapLinear);
|
||||||
|
texture->setMagnificationFilter(QOpenGLTexture::Linear);
|
||||||
|
texture->release();
|
||||||
|
return texture;
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
/*##############################################################################
|
||||||
|
## Author: Shaun Reed ##
|
||||||
|
## Legal: All Content (c) 2021 Shaun Reed, all rights reserved ##
|
||||||
|
## About: Texture class to help with texture and image initializations ##
|
||||||
|
## ##
|
||||||
|
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
|
||||||
|
##############################################################################*/
|
||||||
|
|
||||||
|
#ifndef QTOPENGL_TEXTURE_H
|
||||||
|
#define QTOPENGL_TEXTURE_H
|
||||||
|
|
||||||
|
#include <QOpenGLTexture>
|
||||||
|
|
||||||
|
#include <mainwidget.h>
|
||||||
|
|
||||||
|
|
||||||
|
class Texture {
|
||||||
|
public:
|
||||||
|
~Texture() {}
|
||||||
|
|
||||||
|
// QImage
|
||||||
|
static QImage * initImage(const char * image,
|
||||||
|
bool flipX=false, bool flipY=false);
|
||||||
|
|
||||||
|
// 2D Texture
|
||||||
|
static QOpenGLTexture * initTexture2D(const char * texture,
|
||||||
|
bool flipX=false, bool flipY=false);
|
||||||
|
|
||||||
|
// Cube maps
|
||||||
|
static QOpenGLTexture * initCubeMap(QImage right, QImage top,
|
||||||
|
QImage front, QImage left,
|
||||||
|
QImage bottom, QImage back);
|
||||||
|
// Overloads for cube map initialization
|
||||||
|
static QOpenGLTexture * initCubeMap(const char * tile);
|
||||||
|
static QOpenGLTexture * initCubeMap(const char * right, const char * top,
|
||||||
|
const char * front, const char * left,
|
||||||
|
const char * bottom, const char * back);
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Private ctor to prevent creating instances of this class
|
||||||
|
Texture() {}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // QTOPENGL_TEXTURE_H
|
|
@ -0,0 +1,144 @@
|
||||||
|
/*##############################################################################
|
||||||
|
## Author: Shaun Reed ##
|
||||||
|
## Legal: All Content (c) 2021 Shaun Reed, all rights reserved ##
|
||||||
|
## About: Transform3D class to represent object position in 3D space ##
|
||||||
|
## From following tutorials at trentreed.net ##
|
||||||
|
## ##
|
||||||
|
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
|
||||||
|
##############################################################################*/
|
||||||
|
|
||||||
|
#include <transform3D.h>
|
||||||
|
|
||||||
|
|
||||||
|
const QVector3D Transform3D::LocalForward(0.0f, 0.0f, 1.0f);
|
||||||
|
const QVector3D Transform3D::LocalUp(0.0f, 1.0f, 0.0f);
|
||||||
|
const QVector3D Transform3D::LocalRight(1.0f, 0.0f, 0.0f);
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
* Transformations
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
void Transform3D::translate(const QVector3D & dt)
|
||||||
|
{
|
||||||
|
m_dirty = true;
|
||||||
|
mTranslation += dt;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Transform3D::scale(const QVector3D & ds)
|
||||||
|
{
|
||||||
|
m_dirty = true;
|
||||||
|
mScale *= ds;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Transform3D::rotate(const QQuaternion & dr)
|
||||||
|
{
|
||||||
|
m_dirty = true;
|
||||||
|
mRotation = dr * mRotation;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Transform3D::grow(const QVector3D & ds)
|
||||||
|
{
|
||||||
|
m_dirty = true;
|
||||||
|
mScale += ds;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
* Setters
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
void Transform3D::setTranslation(const QVector3D & t)
|
||||||
|
{
|
||||||
|
m_dirty = true;
|
||||||
|
mTranslation = t;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Transform3D::setScale(const QVector3D & s)
|
||||||
|
{
|
||||||
|
m_dirty = true;
|
||||||
|
mScale = s;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Transform3D::setRotation(const QQuaternion & r)
|
||||||
|
{
|
||||||
|
m_dirty = true;
|
||||||
|
mRotation = r;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
* Accessors
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
// Produces modelToWorld matrix using current set of transformations
|
||||||
|
// Transformation * rotation * scale = modelToWorld
|
||||||
|
const QMatrix4x4 & Transform3D::toMatrix()
|
||||||
|
{
|
||||||
|
if (m_dirty)
|
||||||
|
{
|
||||||
|
m_dirty = false;
|
||||||
|
mWorld.setToIdentity();
|
||||||
|
mWorld.translate(mTranslation);
|
||||||
|
mWorld.rotate(mRotation);
|
||||||
|
mWorld.scale(mScale);
|
||||||
|
}
|
||||||
|
return mWorld;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
* Queries
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
QVector3D Transform3D::forward() const
|
||||||
|
{
|
||||||
|
return mRotation.rotatedVector(LocalForward);
|
||||||
|
}
|
||||||
|
|
||||||
|
QVector3D Transform3D::up() const
|
||||||
|
{
|
||||||
|
return mRotation.rotatedVector(LocalUp);
|
||||||
|
}
|
||||||
|
|
||||||
|
QVector3D Transform3D::right() const
|
||||||
|
{
|
||||||
|
return mRotation.rotatedVector(LocalRight);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
* QT Streams
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
QDebug operator<<(QDebug dbg, const Transform3D & transform)
|
||||||
|
{
|
||||||
|
dbg << "Transform3D\n{\n";
|
||||||
|
dbg << "Position: <" << transform.translation().x() << ", "
|
||||||
|
<< transform.translation().y() << ", "
|
||||||
|
<< transform.translation().z() << ">\n";
|
||||||
|
dbg << "Scale: <" << transform.scale().x() << ", "
|
||||||
|
<< transform.scale().y() << ", "
|
||||||
|
<< transform.scale().z() << ">\n";
|
||||||
|
dbg << "Rotation: <" << transform.rotation().x() << ", "
|
||||||
|
<< transform.rotation().y() << ", "
|
||||||
|
<< transform.rotation().z() << " | " <<
|
||||||
|
transform.rotation().scalar() << ">\n}";
|
||||||
|
return dbg;
|
||||||
|
}
|
||||||
|
|
||||||
|
QDataStream & operator<<(QDataStream & out, const Transform3D & transform)
|
||||||
|
{
|
||||||
|
out << transform.mTranslation;
|
||||||
|
out << transform.mScale;
|
||||||
|
out << transform.mRotation;
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
QDataStream & operator>>(QDataStream & in, Transform3D & transform)
|
||||||
|
{
|
||||||
|
in >> transform.mTranslation;
|
||||||
|
in >> transform.mScale;
|
||||||
|
in >> transform.mRotation;
|
||||||
|
transform.m_dirty = true;
|
||||||
|
return in;
|
||||||
|
}
|
|
@ -0,0 +1,117 @@
|
||||||
|
/*##############################################################################
|
||||||
|
## Author: Shaun Reed ##
|
||||||
|
## Legal: All Content (c) 2021 Shaun Reed, all rights reserved ##
|
||||||
|
## About: Transform3D class to represent object position in 3D space ##
|
||||||
|
## From following tutorials at trentreed.net ##
|
||||||
|
## ##
|
||||||
|
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
|
||||||
|
##############################################################################*/
|
||||||
|
|
||||||
|
#ifndef QTK_TRANSFORM3D_H
|
||||||
|
#define QTK_TRANSFORM3D_H
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QMatrix4x4>
|
||||||
|
#include <QQuaternion>
|
||||||
|
#include <QVector3D>
|
||||||
|
|
||||||
|
|
||||||
|
class Transform3D
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// Constructors
|
||||||
|
inline Transform3D() : m_dirty(true),
|
||||||
|
mScale(1.0f, 1.0f, 1.0f),
|
||||||
|
mTranslation(0.0f, 0.0f, 0.0f) { }
|
||||||
|
|
||||||
|
//
|
||||||
|
// Transformations
|
||||||
|
|
||||||
|
void translate(const QVector3D & dt);
|
||||||
|
inline void translate(float dx, float dy, float dz)
|
||||||
|
{ translate(QVector3D(dx, dy, dz));}
|
||||||
|
|
||||||
|
// Scale object with multiplication
|
||||||
|
void scale(const QVector3D & ds);
|
||||||
|
inline void scale(float dx, float dy, float dz)
|
||||||
|
{ scale(QVector3D(dx, dy, dz));}
|
||||||
|
inline void scale(float factor)
|
||||||
|
{ scale(QVector3D(factor, factor, factor));}
|
||||||
|
|
||||||
|
// Multiplying by a rotation
|
||||||
|
void rotate(const QQuaternion & dr);
|
||||||
|
inline void rotate(float angle, const QVector3D & axis)
|
||||||
|
{ rotate(QQuaternion::fromAxisAndAngle(axis, angle));}
|
||||||
|
inline void rotate(float angle, float ax, float ay, float az)
|
||||||
|
{ rotate(QQuaternion::fromAxisAndAngle(ax, ay, az, angle));}
|
||||||
|
|
||||||
|
// Scale object by addition
|
||||||
|
void grow(const QVector3D & ds);
|
||||||
|
inline void grow(float dx, float dy, float dz)
|
||||||
|
{ grow(QVector3D(dx, dy, dz));}
|
||||||
|
inline void grow(float factor)
|
||||||
|
{ grow(QVector3D(factor, factor, factor));}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Setters
|
||||||
|
|
||||||
|
// Set object position
|
||||||
|
void setTranslation(const QVector3D & t);
|
||||||
|
inline void setTranslation(float x, float y, float z)
|
||||||
|
{ setTranslation(QVector3D(x, y, z));}
|
||||||
|
|
||||||
|
// Set object scale
|
||||||
|
void setScale(const QVector3D & s);
|
||||||
|
inline void setScale(float x, float y, float z)
|
||||||
|
{ setScale(QVector3D(x, y, z));}
|
||||||
|
inline void setScale(float k)
|
||||||
|
{ setScale(QVector3D(k, k, k));}
|
||||||
|
|
||||||
|
// Set object rotation
|
||||||
|
void setRotation(const QQuaternion & r);
|
||||||
|
inline void setRotation(float angle, const QVector3D & axis)
|
||||||
|
{ setRotation(QQuaternion::fromAxisAndAngle(axis, angle));}
|
||||||
|
inline void setRotation(float angle, float ax, float ay, float az)
|
||||||
|
{ setRotation(QQuaternion::fromAxisAndAngle(ax, ay, az, angle));}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Accessors
|
||||||
|
|
||||||
|
inline const QVector3D & translation() const { return mTranslation;}
|
||||||
|
inline const QVector3D & scale() const { return mScale; }
|
||||||
|
inline const QQuaternion & rotation() const { return mRotation; }
|
||||||
|
const QMatrix4x4 & toMatrix();
|
||||||
|
|
||||||
|
QVector3D forward() const;
|
||||||
|
QVector3D up() const;
|
||||||
|
QVector3D right() const;
|
||||||
|
|
||||||
|
static const QVector3D LocalForward, LocalUp, LocalRight;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QVector3D mTranslation;
|
||||||
|
QQuaternion mRotation;
|
||||||
|
QVector3D mScale;
|
||||||
|
QMatrix4x4 mWorld;
|
||||||
|
|
||||||
|
bool m_dirty;
|
||||||
|
|
||||||
|
#ifndef QT_NO_DATASTREAM
|
||||||
|
friend QDataStream &operator<<(QDataStream & out, const Transform3D & transform);
|
||||||
|
friend QDataStream &operator>>(QDataStream & in, Transform3D & transform);
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
Q_DECLARE_TYPEINFO(Transform3D, Q_MOVABLE_TYPE);
|
||||||
|
|
||||||
|
// Qt Streams
|
||||||
|
#ifndef QT_NO_DEBUG_STREAM
|
||||||
|
QDebug operator<<(QDebug dbg, const Transform3D & transform);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef QT_NO_DATASTREAM
|
||||||
|
QDataStream &operator<<(QDataStream & out, const Transform3D & transform);
|
||||||
|
QDataStream &operator>>(QDataStream & in, Transform3D & transform);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // QTK_TRANSFORM3D_H
|
|
@ -0,0 +1,4 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!DOCTYPE TS>
|
||||||
|
<TS version="2.1" language="en_US">
|
||||||
|
</TS>
|
|
@ -0,0 +1,81 @@
|
||||||
|
<RCC>
|
||||||
|
<qresource prefix="/">
|
||||||
|
<!--3DModel test shader-->
|
||||||
|
<file alias="model.frag">resources/shaders/fragment/model.frag</file>
|
||||||
|
<file alias="model.vert">resources/shaders/vertex/model.vert</file>
|
||||||
|
<!--Phong test shader-->
|
||||||
|
<file alias="phong.frag">resources/shaders/fragment/phong.frag</file>
|
||||||
|
<file alias="phong.vert">resources/shaders/vertex/phong.vert</file>
|
||||||
|
|
||||||
|
|
||||||
|
<!--Simple Solid Shader-->
|
||||||
|
<file alias="solid.frag">resources/shaders/fragment/solid.frag</file>
|
||||||
|
<file alias="solid.vert">resources/shaders/vertex/solid.vert</file>
|
||||||
|
|
||||||
|
<!--Solid Color Shader-->
|
||||||
|
<file alias="solid-perspective.frag">resources/shaders/fragment/solid-perspective.frag</file>
|
||||||
|
<file alias="solid-perspective.vert">resources/shaders/vertex/solid-perspective.vert</file>
|
||||||
|
|
||||||
|
<!--Multi-color Shader-->
|
||||||
|
<file alias="multi-color.frag">resources/shaders/fragment/multi-color.frag</file>
|
||||||
|
<file alias="multi-color.vert">resources/shaders/vertex/multi-color.vert</file>
|
||||||
|
|
||||||
|
<!--RGB Normals Shader-->
|
||||||
|
<file alias="rgb-normals.frag">resources/shaders/fragment/rgb-normals.frag</file>
|
||||||
|
<file alias="rgb-normals.vert">resources/shaders/vertex/rgb-normals.vert</file>
|
||||||
|
|
||||||
|
<!--CubeMap Texture Shader-->
|
||||||
|
<file alias="texture-cubemap.frag">resources/shaders/fragment/texture-cubemap.frag</file>
|
||||||
|
<file alias="texture-cubemap.vert">resources/shaders/vertex/texture-cubemap.vert</file>
|
||||||
|
|
||||||
|
<!--2D Texture Shader-->
|
||||||
|
<file alias="texture2d.frag">resources/shaders/fragment/texture2d.frag</file>
|
||||||
|
<file alias="texture2d.vert">resources/shaders/vertex/texture2d.vert</file>
|
||||||
|
|
||||||
|
|
||||||
|
<!--Ambient Shader-->
|
||||||
|
<file alias="solid-ambient.frag">resources/shaders/fragment/solid-ambient.frag</file>
|
||||||
|
<file alias="solid-ambient.vert">resources/shaders/vertex/solid-ambient.vert</file>
|
||||||
|
<!--Diffuse Shader-->
|
||||||
|
<file alias="solid-diffuse.frag">resources/shaders/fragment/solid-diffuse.frag</file>
|
||||||
|
<file alias="solid-diffuse.vert">resources/shaders/vertex/solid-diffuse.vert</file>
|
||||||
|
<!--Specular Shader-->
|
||||||
|
<file alias="solid-specular.frag">resources/shaders/fragment/solid-specular.frag</file>
|
||||||
|
<file alias="solid-specular.vert">resources/shaders/vertex/solid-specular.vert</file>
|
||||||
|
<!--Basic Phong Shader-->
|
||||||
|
<file alias="solid-phong.frag">resources/shaders/fragment/solid-phong.frag</file>
|
||||||
|
<file alias="solid-phong.vert">resources/shaders/vertex/solid-phong.vert</file>
|
||||||
|
|
||||||
|
|
||||||
|
<!--3DModel Basic Shader-->
|
||||||
|
<file alias="model-basic.frag">resources/shaders/fragment/model-basic.frag</file>
|
||||||
|
<file alias="model-basic.vert">resources/shaders/vertex/model-basic.vert</file>
|
||||||
|
|
||||||
|
<!--3DModel shader with specular mapping-->
|
||||||
|
<file alias="model-specular.frag">resources/shaders/fragment/model-specular.frag</file>
|
||||||
|
<file alias="model-specular.vert">resources/shaders/vertex/model-specular.vert</file>
|
||||||
|
|
||||||
|
<!--3DModel shader with normal mapping-->
|
||||||
|
<file alias="model-normals.frag">resources/shaders/fragment/model-normals.frag</file>
|
||||||
|
<file alias="model-normals.vert">resources/shaders/vertex/model-normals.vert</file>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<!-- Skybox Shaders-->
|
||||||
|
<file alias="skybox.frag">resources/skybox/skybox.frag</file>
|
||||||
|
<file alias="skybox.vert">resources/skybox/skybox.vert</file>
|
||||||
|
|
||||||
|
|
||||||
|
<!--Texture Images-->
|
||||||
|
<file alias="crate.png">resources/images/crate.png</file>
|
||||||
|
<file alias="stone.png">resources/images/stone.png</file>
|
||||||
|
<file alias="wood.png">resources/images/wood.png</file>
|
||||||
|
<!-- Skybox Images-->
|
||||||
|
<file alias="back.png">resources/skybox/back.png</file>
|
||||||
|
<file alias="bottom.png">resources/skybox/bottom.png</file>
|
||||||
|
<file alias="front.png">resources/skybox/front.png</file>
|
||||||
|
<file alias="left.png">resources/skybox/left.png</file>
|
||||||
|
<file alias="right.png">resources/skybox/right.png</file>
|
||||||
|
<file alias="top.png">resources/skybox/top.png</file>
|
||||||
|
</qresource>
|
||||||
|
</RCC>
|
After Width: | Height: | Size: 91 KiB |
After Width: | Height: | Size: 1.5 MiB |
After Width: | Height: | Size: 76 KiB |
|
@ -0,0 +1,55 @@
|
||||||
|
# Blender MTL File: 'None'
|
||||||
|
# Material Count: 4
|
||||||
|
|
||||||
|
newmtl AlienHominid
|
||||||
|
Ns 225.000000
|
||||||
|
Ka 1.000000 1.000000 1.000000
|
||||||
|
Kd 0.800000 0.533344 0.000000
|
||||||
|
Ks 0.500000 0.500000 0.500000
|
||||||
|
Ke 0.000000 0.000000 0.000000
|
||||||
|
Ni 1.450000
|
||||||
|
d 1.000000
|
||||||
|
illum 2
|
||||||
|
map_Bump normal.png
|
||||||
|
map_Kd diffuse.png
|
||||||
|
map_Ns roughness.png
|
||||||
|
refl specular.png
|
||||||
|
|
||||||
|
newmtl Blaster
|
||||||
|
Ns 225.000000
|
||||||
|
Ka 1.000000 1.000000 1.000000
|
||||||
|
Kd 0.800000 0.800000 0.800000
|
||||||
|
Ks 0.500000 0.500000 0.500000
|
||||||
|
Ke 0.000000 0.000000 0.000000
|
||||||
|
Ni 1.450000
|
||||||
|
d 1.000000
|
||||||
|
illum 2
|
||||||
|
map_Bump blaster_normal.png
|
||||||
|
map_Kd blaster_diffuse.png
|
||||||
|
map_Ke blaster_emissive.png
|
||||||
|
map_Ns blaster_roughness.png
|
||||||
|
refl blaster_specular.png
|
||||||
|
|
||||||
|
newmtl Screenshot_2021-04-19_194508
|
||||||
|
Ns 225.000000
|
||||||
|
Ka 1.000000 1.000000 1.000000
|
||||||
|
Kd 0.800000 0.800000 0.800000
|
||||||
|
Ks 0.500000 0.500000 0.500000
|
||||||
|
Ke 0.000000 0.000000 0.000000
|
||||||
|
Ni 1.450000
|
||||||
|
d 1.000000
|
||||||
|
illum 2
|
||||||
|
map_Kd smoke.png
|
||||||
|
map_d smoke.png
|
||||||
|
|
||||||
|
newmtl Untitled-1
|
||||||
|
Ns 77.904748
|
||||||
|
Ka 1.000000 1.000000 1.000000
|
||||||
|
Kd 0.800000 0.800000 0.800000
|
||||||
|
Ks 0.500000 0.500000 0.500000
|
||||||
|
Ke 0.000000 0.000000 0.000000
|
||||||
|
Ni 1.450000
|
||||||
|
d 1.000000
|
||||||
|
illum 2
|
||||||
|
map_Kd Untitled-1.png
|
||||||
|
map_d Untitled-1.png
|
After Width: | Height: | Size: 14 MiB |
After Width: | Height: | Size: 455 KiB |
After Width: | Height: | Size: 9.4 MiB |
After Width: | Height: | Size: 5.2 MiB |
After Width: | Height: | Size: 2.8 MiB |
After Width: | Height: | Size: 8.8 MiB |
After Width: | Height: | Size: 10 MiB |
After Width: | Height: | Size: 4.0 MiB |
After Width: | Height: | Size: 26 KiB |
After Width: | Height: | Size: 1.8 MiB |
|
@ -0,0 +1,16 @@
|
||||||
|
# Blender MTL File: 'None'
|
||||||
|
# Material Count: 1
|
||||||
|
|
||||||
|
newmtl Scene_-_Root
|
||||||
|
Ns 225.000000
|
||||||
|
Ka 1.000000 1.000000 1.000000
|
||||||
|
Kd 0.800000 0.800000 0.800000
|
||||||
|
Ks 0.500000 0.500000 0.500000
|
||||||
|
Ke 0.0 0.0 0.0
|
||||||
|
Ni 1.450000
|
||||||
|
d 1.000000
|
||||||
|
illum 2
|
||||||
|
map_Kd diffuse.jpg
|
||||||
|
map_Bump normal.png
|
||||||
|
map_Ks specular.jpg
|
||||||
|
|
After Width: | Height: | Size: 5.8 MiB |
After Width: | Height: | Size: 14 MiB |
After Width: | Height: | Size: 4.2 MiB |
|
@ -0,0 +1,3 @@
|
||||||
|
Model by Berk Gedik, from: https://sketchfab.com/3d-models/survival-guitar-backpack-low-poly-799f8c4511f84fab8c3f12887f7e6b36
|
||||||
|
|
||||||
|
Modified material assignment (Joey de Vries) for easier load in OpenGL model loading chapter, and renamed albedo to diffuse and metallic to specular to match non-PBR lighting setup.
|
After Width: | Height: | Size: 6.4 MiB |
|
@ -0,0 +1,7 @@
|
||||||
|
newmtl material_0
|
||||||
|
map_bump normal.jpg
|
||||||
|
norm normal.jpg
|
||||||
|
map_Kd diffuse.jpg
|
||||||
|
occlusion occlusion.jpg
|
||||||
|
Pm 0
|
||||||
|
Pr 0.793152
|
After Width: | Height: | Size: 11 MiB |
After Width: | Height: | Size: 3.3 MiB |
After Width: | Height: | Size: 1.7 MiB |
|
@ -0,0 +1,12 @@
|
||||||
|
# Blender v2.82 (sub 7) OBJ File: ''
|
||||||
|
# www.blender.org
|
||||||
|
mtllib floor.mtl
|
||||||
|
o floor
|
||||||
|
v -1.000000 -0.000000 1.000000
|
||||||
|
v 1.000000 -0.000000 1.000000
|
||||||
|
v 1.000000 0.000000 -1.000000
|
||||||
|
v -1.000000 0.000000 -1.000000
|
||||||
|
vn 0.0000 1.0000 0.0000
|
||||||
|
usemtl Default_OBJ
|
||||||
|
s off
|
||||||
|
f 1//1 2//1 3//1 4//1
|
After Width: | Height: | Size: 1.1 MiB |
After Width: | Height: | Size: 254 KiB |
|
@ -0,0 +1,16 @@
|
||||||
|
# Blender MTL File: 'None'
|
||||||
|
# Material Count: 1
|
||||||
|
|
||||||
|
newmtl Lion
|
||||||
|
Ns 500.000001
|
||||||
|
Ka 1.000000 1.000000 1.000000
|
||||||
|
Kd 0.800000 0.800000 0.800000
|
||||||
|
Ks 0.800000 0.800000 0.800000
|
||||||
|
Ke 0.000000 0.000000 0.000000
|
||||||
|
Ni 1.450000
|
||||||
|
d 1.000000
|
||||||
|
illum 2
|
||||||
|
map_Bump normal.jpg
|
||||||
|
map_Kd diffuse.jpg
|
||||||
|
map_Ns roughness.jpg
|
||||||
|
refl specular.jpg
|
After Width: | Height: | Size: 484 KiB |
After Width: | Height: | Size: 562 KiB |
After Width: | Height: | Size: 134 KiB |
After Width: | Height: | Size: 133 KiB |
|
@ -0,0 +1,8 @@
|
||||||
|
# WaveFront *.mtl file (generated by CINEMA 4D)
|
||||||
|
|
||||||
|
newmtl item/objectcomponents/weapon/staff_2h_artifactdeadwind_d_03.m2_Geoset_000
|
||||||
|
Ka 0.25 0.25 0.25
|
||||||
|
Kd 1 1 1
|
||||||
|
map_Kd diffuse.png
|
||||||
|
illum 7
|
||||||
|
|
After Width: | Height: | Size: 1.4 MiB |
After Width: | Height: | Size: 4.0 MiB |
After Width: | Height: | Size: 807 KiB |
After Width: | Height: | Size: 5.9 MiB |
After Width: | Height: | Size: 1.5 MiB |
After Width: | Height: | Size: 614 KiB |
After Width: | Height: | Size: 1.7 MiB |
After Width: | Height: | Size: 4.8 MiB |
After Width: | Height: | Size: 1.3 MiB |
After Width: | Height: | Size: 4.5 MiB |
After Width: | Height: | Size: 2.2 MiB |
After Width: | Height: | Size: 885 KiB |
After Width: | Height: | Size: 96 KiB |
After Width: | Height: | Size: 1.1 MiB |
After Width: | Height: | Size: 426 KiB |
After Width: | Height: | Size: 983 KiB |
After Width: | Height: | Size: 484 KiB |
After Width: | Height: | Size: 279 KiB |
After Width: | Height: | Size: 930 KiB |
After Width: | Height: | Size: 3.9 MiB |
After Width: | Height: | Size: 1.3 MiB |
After Width: | Height: | Size: 4.4 MiB |
After Width: | Height: | Size: 1.7 MiB |
After Width: | Height: | Size: 862 KiB |
After Width: | Height: | Size: 1.4 MiB |
After Width: | Height: | Size: 4.0 MiB |
After Width: | Height: | Size: 1008 KiB |
After Width: | Height: | Size: 4.8 MiB |
After Width: | Height: | Size: 1.8 MiB |
After Width: | Height: | Size: 638 KiB |
After Width: | Height: | Size: 844 KiB |
After Width: | Height: | Size: 3.4 MiB |
After Width: | Height: | Size: 1.1 MiB |
After Width: | Height: | Size: 3.8 MiB |
After Width: | Height: | Size: 1.7 MiB |
After Width: | Height: | Size: 761 KiB |