Add SceneInterface

+ Renames binaries qtk_main->qtk_app and example->example_app
This commit is contained in:
Shaun Reed 2023-03-11 10:58:49 -05:00
parent 0dcb6d337b
commit 0659df94bd
24 changed files with 139 additions and 100 deletions

View File

@ -74,7 +74,7 @@ We can install this library on a system path or a custom path and then set `CMAK
Below is an example of installing on a system path.
```bash
cmake -S qtk/ -B qtk/build/ -DCMAKE_PREFIX_PATH=$HOME/Qt/6.5.0/gcc_64 -DQTK_INSTALL_GUI=OFF -DQTK_INSTALL_PLUGINS=OFF
cmake -S qtk/ -B qtk/build/ -DCMAKE_PREFIX_PATH=$HOME/Qt/6.5.0/gcc_64 -DQTK_INSTALL_GUI=OFF -DQTK_INSTALL_PLUGINS=OFF -DQTK_DEBUG=OFF
cmake --build qtk/build/ -j $(nproc --ignore=2)
sudo cmake --install . --prefix=/usr/local
-- Install configuration: "Release"

View File

@ -2,7 +2,7 @@
include("${CMAKE_CURRENT_LIST_DIR}/QtkTargets.cmake")
set_and_check(QTK_EXECUTABLE "${PACKAGE_PREFIX_DIR}/bin/qtk_main")
set_and_check(QTK_EXECUTABLE "${PACKAGE_PREFIX_DIR}/bin/qtk_app")
set_and_check(QTK_INCLUDE_DIR "${PACKAGE_PREFIX_DIR}/include")
set_and_check(QTK_LIBRARIES "${PACKAGE_PREFIX_DIR}/lib")

View File

@ -65,6 +65,6 @@ set(
examplewidget.cpp examplewidget.h
)
add_executable(example ${EXAMPLE_SOURCES})
target_link_libraries(example PUBLIC Qt6::Widgets Qt6::OpenGLWidgets Qt6::Core)
target_link_libraries(example PUBLIC Qtk::qtk_library)
add_executable(example_app ${EXAMPLE_SOURCES})
target_link_libraries(example_app PUBLIC Qt6::Widgets Qt6::OpenGLWidgets Qt6::Core)
target_link_libraries(example_app PUBLIC Qtk::qtk_library)

View File

@ -1,7 +1,10 @@
This is an example application that is using the Qtk API to create custom Qt
OpenGL widgets. This is very similar to `QtkWidget` in the Qtk desktop
application, but could be modified for different uses if needed.
application source code, but could be modified for different uses if needed.
There are no camera controls supported in this example. The camera is fixed.
If these controls are desired, they can be implemented by the client.
You can import your own models within `examplescene.cpp`, inside the
`ExampleScene::init()` function. Rotations and translations

View File

@ -10,7 +10,7 @@
using namespace Qtk;
ExampleScene::ExampleScene() {
ExampleScene::ExampleScene(Qtk::Scene * scene) : Qtk::SceneInterface(scene) {
setSceneName("Example Scene");
getCamera().getTransform().setTranslation(-8.0f, 0.0f, 10.0f);
getCamera().getTransform().setRotation(-5.0f, 0.0f, 1.0f, 0.0f);

View File

@ -11,9 +11,9 @@
#include <qtk/scene.h>
class ExampleScene : public Qtk::Scene {
class ExampleScene : public Qtk::SceneInterface {
public:
ExampleScene();
ExampleScene(Qtk::Scene * scene);
~ExampleScene();

View File

@ -11,7 +11,12 @@
#include "examplewidget.h"
ExampleWidget::ExampleWidget(QWidget * parent) :
QOpenGLWidget(parent), mScene(new ExampleScene) {
QOpenGLWidget(parent), mScene(new ExampleScene(new Qtk::SceneEmpty)) {
// NOTE: The decorator pattern is used to save / load scenes in Qtk currently.
// The initializer above sets mScene to the concrete decorator ExampleScene.
// Qtk::SceneEmpty provides an empty scene as the concrete component.
// ExampleScene is defined in client source, deriving Qtk::SceneInterface.
QSurfaceFormat format;
format.setRenderableType(QSurfaceFormat::OpenGL);
format.setProfile(QSurfaceFormat::CoreProfile);

View File

@ -32,7 +32,7 @@ class ExampleWidget : public QOpenGLWidget, protected QOpenGLFunctions {
void update();
private:
ExampleScene * mScene;
Qtk::Scene * mScene;
};
#endif // QTKCLIENT_EXAMPLEWIDGET_H

View File

@ -32,7 +32,7 @@ if (QTK_INSTALL_GUI OR QTK_INSTALL_PLUGINS)
if(QTK_INSTALL_GUI)
install(
TARGETS qtk_main qtk_library
TARGETS qtk_app qtk_library
COMPONENT qtk
BUNDLE DESTINATION .
LIBRARY DESTINATION lib
@ -41,7 +41,7 @@ if (QTK_INSTALL_GUI OR QTK_INSTALL_PLUGINS)
)
qt_generate_deploy_app_script(
TARGET qtk_main
TARGET qtk_app
OUTPUT_SCRIPT QTK_DEPLOY_SCRIPT
NO_UNSUPPORTED_PLATFORM_ERROR
)
@ -58,7 +58,7 @@ if (QTK_INSTALL_GUI OR QTK_INSTALL_PLUGINS)
)
file(TO_NATIVE_PATH "${QT6_INSTALL_PREFIX}/bin" QT6_INSTALL_PREFIX)
set(VSUSER_FILE "${CMAKE_CURRENT_BINARY_DIR}/qtk_main.vcxproj.user")
set(VSUSER_FILE "${CMAKE_CURRENT_BINARY_DIR}/qtk_app.vcxproj.user")
file(WRITE ${VSUSER_FILE} "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n")
file(APPEND ${VSUSER_FILE} "<Project xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n")
file(APPEND ${VSUSER_FILE} " <PropertyGroup>\n")
@ -137,11 +137,11 @@ set(CPACK_NSIS_ENABLE_UNINSTALL_BEFORE_INSTALL ON)
# https://nsis.sourceforge.io/Reference/CreateShortCut
set(
CPACK_NSIS_CREATE_ICONS_EXTRA
"CreateShortCut '$SMPROGRAMS\\$STARTMENU_FOLDER\\Qtk.lnk' '$INSTDIR\\bin\\qtk_main.exe'"
"CreateShortCut '$SMPROGRAMS\\\\$STARTMENU_FOLDER\\\\Qtk.lnk' '$INSTDIR\\\\bin\\\\qtk_app.exe'"
)
set(
CPACK_NSIS_DELETE_ICONS_EXTRA
"Delete '$SMPROGRAMS\\$START_MENU\\Qtk.lnk'"
"Delete '$SMPROGRAMS\\\\$START_MENU\\\\Qtk.lnk'"
)
# TODO: Icons for NSIS installer.
#set(CPACK_NSIS_MUI_ICON "${CMAKE_SOURCE_DIR}/resources/icon.png")
@ -154,9 +154,8 @@ set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON)
#set(CPACK_PACKAGING_INSTALL_PREFIX /usr/local/)
# OSX
# TODO: Fix OSX appbundle error.
set(CPACK_BUNDLE_NAME ${PROJECT_NAME})
set(CPACK_BUNDLE_PLIST $<TARGET_BUNDLE_CONTENT_DIR:qtk_main>/Info.plist)
set(CPACK_BUNDLE_PLIST $<TARGET_BUNDLE_CONTENT_DIR:qtk_app>/Info.plist)
set(CPACK_BUNDLE_ICON ${QTK_OSX_ICONS})
# Platform defaults for source bundles.

View File

@ -50,7 +50,7 @@ target_link_libraries(qtk_collection PUBLIC qtk_plugin_library)
set(
QTK_APP_SOURCES
examplescene.cpp examplescene.h
qtkscene.cpp qtkscene.h
main.cpp
)
@ -61,11 +61,11 @@ configure_file(
@ONLY
)
qt_add_executable(qtk_main ${QTK_APP_SOURCES})
target_link_libraries(qtk_main PRIVATE qtk_plugin_library)
qt_add_executable(qtk_app ${QTK_APP_SOURCES})
target_link_libraries(qtk_app PRIVATE qtk_plugin_library)
set_target_properties(
qtk_main PROPERTIES
qtk_app PROPERTIES
WIN32_EXECUTABLE TRUE
MACOSX_BUNDLE TRUE
MACOSX_BUNDLE_BUNDLE_NAME Qtk

View File

@ -9,6 +9,7 @@
#include <QApplication>
#include "qtkmainwindow.h"
#include "qtkscene.h"
int main(int argc, char * argv[]) {
Q_INIT_RESOURCE(resources);
@ -16,7 +17,12 @@ int main(int argc, char * argv[]) {
QApplication a(argc, argv);
auto window = MainWindow::getMainWindow();
window->show();
// Qtk currently uses the decorator pattern to save / load scenes.
// This is a temporary solution and will be improved in the future.
auto emptyScene = new Qtk::SceneEmpty;
window->getQtkWidget()->setScene(new QtkScene(emptyScene));
window->show();
return QApplication::exec();
}

View File

@ -7,7 +7,7 @@
##############################################################################*/
#include "qtkmainwindow.h"
#include "examplescene.h"
#include "qtkscene.h"
#include "ui_qtkmainwindow.h"
MainWindow * MainWindow::mainWindow_ = Q_NULLPTR;
@ -27,7 +27,7 @@ MainWindow::MainWindow(QWidget * parent) : QMainWindow(parent) {
// Initialize static container for all active QtkWidgets
auto qtkWidgets = findChildren<Qtk::QtkWidget *>();
for(auto & qtkWidget : qtkWidgets) {
qtkWidget->setScene(new ExampleScene);
qtkWidget->setScene(new Qtk::SceneEmpty);
views_.emplace(qtkWidget->getScene()->getSceneName(), qtkWidget);
ui_->menuView->addAction(qtkWidget->getActionToggleConsole());
connect(
@ -60,6 +60,13 @@ MainWindow * MainWindow::getMainWindow() {
return mainWindow_;
}
Qtk::QtkWidget * MainWindow::getQtkWidget(int64_t index) {
if(views_.size() <= index) {
return Q_NULLPTR;
}
return views_.begin(index)->second;
}
Qtk::QtkWidget * MainWindow::getQtkWidget(const QString & name) {
if(!views_.count(name)) {
return Q_NULLPTR;

View File

@ -53,6 +53,8 @@ class MainWindow : public QMainWindow {
*/
static MainWindow * getMainWindow();
Qtk::QtkWidget * getQtkWidget(int64_t index = 0);
/**
* Accessor for retrieving a QtkWidget by it's objectName.
* This function will not construct a new QtkWidget if none is found.

View File

@ -6,7 +6,7 @@
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
##############################################################################*/
#include "examplescene.h"
#include "qtkscene.h"
#include "resources.h"
using namespace Qtk;
@ -15,13 +15,14 @@ using namespace Qtk;
* Constructors, Destructors
******************************************************************************/
ExampleScene::ExampleScene() {
setSceneName("Example Scene");
QtkScene::QtkScene(Qtk::Scene * scene) :
Qtk::SceneInterface(scene) {
setSceneName("Qtk Scene");
getCamera().getTransform().setTranslation(0.0f, 0.0f, 20.0f);
getCamera().getTransform().setRotation(-5.0f, 0.0f, 1.0f, 0.0f);
}
ExampleScene::~ExampleScene() {
QtkScene::~QtkScene() {
delete mTestPhong;
delete mTestSpecular;
delete mTestDiffuse;
@ -32,7 +33,7 @@ ExampleScene::~ExampleScene() {
* Public Member Functions
******************************************************************************/
void ExampleScene::init() {
void QtkScene::init() {
// Add a skybox to the scene using default cube map images and settings.
setSkybox(new Qtk::Skybox("Skybox"));
@ -97,7 +98,7 @@ void ExampleScene::init() {
model->getTransform().scale(0.15f);
model =
addObject(new Qtk::Model("scythe", ":/models/models/scythe/scythe.obj"));
addObject(new Qtk::Model("My scythe", ":/models/models/scythe/scythe.obj"));
model->getTransform().setTranslation(-6.0f, 0.0f, -10.0f);
model->getTransform().rotate(-90.0f, 1.0f, 0.0f, 0.0f);
model->getTransform().rotate(90.0f, 0.0f, 1.0f, 0.0f);
@ -387,19 +388,11 @@ void ExampleScene::init() {
mesh->reallocateTexCoords(mesh->getTexCoords());
}
void ExampleScene::draw() {
void QtkScene::draw() {
// WARNING: We must call the base class draw() function first.
// + This will handle rendering core scene components like the Skybox.
Scene::draw();
for(const auto & model : getModels()) {
model->draw();
}
for(const auto & mesh : getMeshes()) {
mesh->draw();
}
mTestPhong->bindShaders();
mTestPhong->setUniform(
"uModelInverseTransposed",
@ -409,14 +402,14 @@ void ExampleScene::draw() {
MeshRenderer::getInstance("phongLight")->getTransform().getTranslation());
mTestPhong->setUniform(
"uCameraPosition",
ExampleScene::getCamera().getTransform().getTranslation());
QtkScene::getCamera().getTransform().getTranslation());
mTestPhong->releaseShaders();
mTestPhong->draw();
mTestAmbient->bindShaders();
mTestAmbient->setUniform(
"uCameraPosition",
ExampleScene::getCamera().getTransform().getTranslation());
QtkScene::getCamera().getTransform().getTranslation());
mTestAmbient->releaseShaders();
mTestAmbient->draw();
@ -430,7 +423,7 @@ void ExampleScene::draw() {
.getTranslation());
mTestDiffuse->setUniform(
"uCameraPosition",
ExampleScene::getCamera().getTransform().getTranslation());
QtkScene::getCamera().getTransform().getTranslation());
mTestDiffuse->releaseShaders();
mTestDiffuse->draw();
@ -444,12 +437,12 @@ void ExampleScene::draw() {
.getTranslation());
mTestSpecular->setUniform(
"uCameraPosition",
ExampleScene::getCamera().getTransform().getTranslation());
QtkScene::getCamera().getTransform().getTranslation());
mTestSpecular->releaseShaders();
mTestSpecular->draw();
}
void ExampleScene::update() {
void QtkScene::update() {
auto mySpartan = Model::getInstance("My spartan");
mySpartan->getTransform().rotate(0.75f, 0.0f, 1.0f, 0.0f);
@ -463,12 +456,12 @@ void ExampleScene::update() {
alien->setUniform("uLight.position", position);
alien->setUniform(
"uCameraPosition",
ExampleScene::getCamera().getTransform().getTranslation());
QtkScene::getCamera().getTransform().getTranslation());
auto posMatrix = alien->getTransform().toMatrix();
alien->setUniform("uMVP.normalMatrix", posMatrix.normalMatrix());
alien->setUniform("uMVP.model", posMatrix);
alien->setUniform("uMVP.view", ExampleScene::getCamera().toMatrix());
alien->setUniform("uMVP.projection", ExampleScene::getProjectionMatrix());
alien->setUniform("uMVP.view", QtkScene::getCamera().toMatrix());
alien->setUniform("uMVP.projection", QtkScene::getProjectionMatrix());
alien->getTransform().rotate(0.75f, 0.0f, 1.0f, 0.0f);
position = MeshRenderer::getInstance("spartanTestLight")
@ -478,12 +471,12 @@ void ExampleScene::update() {
spartan->setUniform("uLight.position", position);
spartan->setUniform(
"uCameraPosition",
ExampleScene::getCamera().getTransform().getTranslation());
QtkScene::getCamera().getTransform().getTranslation());
posMatrix = spartan->getTransform().toMatrix();
spartan->setUniform("uMVP.normalMatrix", posMatrix.normalMatrix());
spartan->setUniform("uMVP.model", posMatrix);
spartan->setUniform("uMVP.view", ExampleScene::getCamera().toMatrix());
spartan->setUniform("uMVP.projection", ExampleScene::getProjectionMatrix());
spartan->setUniform("uMVP.view", QtkScene::getCamera().toMatrix());
spartan->setUniform("uMVP.projection", QtkScene::getProjectionMatrix());
spartan->getTransform().rotate(0.75f, 0.0f, 1.0f, 0.0f);
auto phong = MeshRenderer::getInstance("testPhong");
@ -494,12 +487,12 @@ void ExampleScene::update() {
phong->setUniform("uLight.position", position);
phong->setUniform(
"uCameraPosition",
ExampleScene::getCamera().getTransform().getTranslation());
QtkScene::getCamera().getTransform().getTranslation());
posMatrix = phong->getTransform().toMatrix();
phong->setUniform("uMVP.normalMatrix", posMatrix.normalMatrix());
phong->setUniform("uMVP.model", posMatrix);
phong->setUniform("uMVP.view", ExampleScene::getCamera().toMatrix());
phong->setUniform("uMVP.projection", ExampleScene::getProjectionMatrix());
phong->setUniform("uMVP.view", QtkScene::getCamera().toMatrix());
phong->setUniform("uMVP.projection", QtkScene::getProjectionMatrix());
phong->releaseShaders();
// Rotate lighting example cubes

View File

@ -29,15 +29,15 @@
*
* To create your own Scene from scratch see Qtk::Scene.
*/
class ExampleScene : public Qtk::Scene {
class QtkScene : public Qtk::SceneInterface {
public:
/***************************************************************************
* Contructors / Destructors
**************************************************************************/
ExampleScene();
QtkScene(Qtk::Scene * scene);
~ExampleScene();
~QtkScene();
/***************************************************************************
* Inherited Public Overrides

View File

@ -100,6 +100,13 @@ namespace Qtk {
*/
inline Qtk::Scene * getScene() { return mScene; }
/**
* @return Pointer to the QOpenGLDebugLogger attached to this widget.
*/
inline QOpenGLDebugLogger * getOpenGLDebugLogger() {
return mDebugLogger;
}
/*************************************************************************
* Setters
************************************************************************/

View File

@ -37,7 +37,7 @@ void Qtk::TreeView::updateView(const Qtk::Scene * scene) {
mSceneName = scene->getSceneName();
auto objects = scene->getObjects();
for(const auto & object : objects) {
auto item = new QTreeWidgetItem(QStringList(QString(object->getName())));
auto item = new QTreeWidgetItem(QStringList(QString(object->getName().c_str())));
ui->treeWidget->insertTopLevelItem(0, item);
}
}
@ -48,6 +48,10 @@ void Qtk::TreeView::itemFocus(QTreeWidgetItem * item, int column) {
MainWindow::getMainWindow()->getQtkWidget(mSceneName)->getScene();
auto & transform = scene->getCamera().getTransform();
auto object = scene->getObject(name);
if (object == Q_NULLPTR) {
qDebug() << "Attempt to get non-existing object with name '" << name
<< "'\n";
}
Transform3D * objectTransform;
if(object->getType() == Object::QTK_MESH) {
objectTransform = &dynamic_cast<MeshRenderer *>(object)->getTransform();

View File

@ -38,7 +38,7 @@ MeshRenderer::MeshRenderer(const char * name, const ShapeBase & shape) :
}
MeshRenderer::~MeshRenderer() {
sInstances.remove(mName);
sInstances.remove(mName.c_str());
}
/*******************************************************************************

View File

@ -96,7 +96,7 @@ void Model::loadModel(const std::string & path) {
sortModelMeshes();
// Object finished loading, insert it into ModelManager
mManager.insert(getName(), this);
mManager.insert(getName().c_str(), this);
}
void Model::processNode(aiNode * node, const aiScene * scene) {
@ -200,7 +200,7 @@ ModelMesh Model::processMesh(aiMesh * mesh, const aiScene * scene) {
textures.insert(textures.end(), normalMaps.begin(), normalMaps.end());
}
return {vertices, indices, textures, mVertexShader, mFragmentShader};
return {vertices, indices, textures, mVertexShader.c_str(), mFragmentShader.c_str()};
}
ModelMesh::Textures Model::loadMaterialTextures(

View File

@ -58,7 +58,7 @@ namespace Qtk {
loadModel(mModelPath);
}
inline ~Model() override { mManager.remove(getName()); }
inline ~Model() override { mManager.remove(getName().c_str()); }
/*************************************************************************
* Public Methods
@ -197,7 +197,7 @@ namespace Qtk {
/** The directory this model and it's textures are stored. */
std::string mDirectory {};
/** File names for shaders and 3D model on disk. */
const char *mVertexShader, *mFragmentShader, *mModelPath;
std::string mVertexShader, mFragmentShader, mModelPath;
};
} // namespace Qtk

View File

@ -73,7 +73,7 @@ void ModelMesh::initMesh(const char * vert, const char * frag) {
initializeOpenGLFunctions();
// Create VAO, VBO, EBO
mVAO->create();
bool status = mVAO->create();
mVBO->create();
mEBO->create();

View File

@ -92,7 +92,7 @@ namespace Qtk {
return mShape.mVertices;
}
[[nodiscard]] inline const char * getName() const { return mName; }
[[nodiscard]] inline std::string getName() const { return mName; }
[[nodiscard]] inline const Type & getType() const { return mType; }
@ -160,7 +160,7 @@ namespace Qtk {
Transform3D mTransform;
Shape mShape;
Texture mTexture;
const char * mName;
std::string mName;
bool mBound;
Type mType = QTK_OBJECT;
};

View File

@ -37,6 +37,23 @@ Scene::~Scene() {
* Public Methods
******************************************************************************/
void Scene::draw() {
if(!mInit) {
initializeOpenGLFunctions();
init();
mInit = true;
}
if(mSkybox != Q_NULLPTR) {
mSkybox->draw();
}
for(auto & model : mModels) {
model->draw();
}
for(const auto & mesh : mMeshes) {
mesh->draw();
}
}
std::vector<Object *> Scene::getObjects() const {
// All scene objects must inherit from Qtk::Object.
std::vector<Object *> objects(mMeshes.begin(), mMeshes.end());
@ -51,7 +68,7 @@ std::vector<Object *> Scene::getObjects() const {
Object * Scene::getObject(const QString & name) {
for(auto object : getObjects()) {
if(object->getName() == name) {
if(object->getName() == name.toStdString()) {
return object;
}
}
@ -74,24 +91,3 @@ template <> Model * Scene::addObject(Model * object) {
sceneUpdated(mSceneName);
return object;
}
/*******************************************************************************
* Private Methods
******************************************************************************/
void Scene::privateDraw() {
if(!mInit) {
initializeOpenGLFunctions();
init();
mInit = true;
}
if(mSkybox != Q_NULLPTR) {
mSkybox->draw();
}
for(auto & model : mModels) {
model->draw();
}
for(const auto & mesh : mMeshes) {
mesh->draw();
}
}

View File

@ -10,6 +10,7 @@
#define QTK_SCENE_H
#include <QMatrix4x4>
#include <utility>
#include "camera3d.h"
@ -65,7 +66,7 @@ namespace Qtk {
*
* This function is only called when the widget is redrawn.
*/
virtual void draw() { privateDraw(); };
virtual void draw();
/**
* Function called to update the QOpenGLWidget. Does not trigger a redraw.
@ -165,6 +166,7 @@ namespace Qtk {
*/
inline void setSceneName(QString name) { mSceneName = std::move(name); }
std::vector<Model *> mModels {};
signals:
/**
* Signal thrown when the scene is modified by adding or removing objects.
@ -175,16 +177,6 @@ namespace Qtk {
void sceneUpdated(QString sceneName);
private:
/*************************************************************************
* Private Methods
************************************************************************/
/**
* Handles drawing members encapsulated by this base class.
* Child classes do not need to draw these objects manually.
*/
void privateDraw();
/*************************************************************************
* Private Members
************************************************************************/
@ -199,8 +191,33 @@ namespace Qtk {
/* MeshRenderers used simple geometry. */
std::vector<MeshRenderer *> mMeshes {};
/* Models used for storing 3D models in the scene. */
std::vector<Model *> mModels {};
};
class SceneEmpty : public Scene {
public:
void init() override {
setSceneName("Empty Scene");
}
void draw() override { Scene::draw(); }
void update() override { Scene::update(); }
};
class SceneInterface : public Scene {
public:
explicit SceneInterface(Scene * scene) : mScene(scene) {}
void init() override { mScene->init(); }
void draw() override { mScene->draw(); }
void update() override { mScene->update(); }
protected:
Scene * mScene;
};
} // namespace Qtk
#endif // QTK_SCENE_H