Compare commits
33 Commits
main
...
0a71bdc1c0
| Author | SHA1 | Date | |
|---|---|---|---|
| 0a71bdc1c0 | |||
| ac87635ff7 | |||
| 4a1050e02f | |||
| 8d60355ccf | |||
| 0551b4f91f | |||
| ecae09f82d | |||
| e68d384340 | |||
| e2c7f5ba75 | |||
| f1d9a07940 | |||
| f5a38892b1 | |||
| d230662924 | |||
| 97bf086a87 | |||
| e97ccd8195 | |||
| 728710ece2 | |||
| 40119856de | |||
| de9ef4c948 | |||
| b252922b0a | |||
| 12883e8c20 | |||
| bdd178d0f2 | |||
| 9bd09b2bb0 | |||
| 0d2a73a35f | |||
| ea25ba312a | |||
| 78639cf1c2 | |||
| 941f2d228c | |||
| 16baf6cdaf | |||
| c86a7744b3 | |||
| 7fac6bafb4 | |||
| 1bed9545c9 | |||
| 32641acd8d | |||
| 68bfff7bcd | |||
| 92e5937cc7 | |||
| 1e1c328a5a | |||
| dcbeb26738 |
24
.github/workflows/build.yml
vendored
24
.github/workflows/build.yml
vendored
@@ -43,16 +43,13 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
version: ${{ env.QT_VERSION }}
|
version: ${{ env.QT_VERSION }}
|
||||||
|
|
||||||
- name: Install pkgconfiglite
|
# Windows
|
||||||
|
|
||||||
|
- name: Chocolatey Action
|
||||||
if: matrix.os == 'windows-latest'
|
if: matrix.os == 'windows-latest'
|
||||||
uses: crazy-max/ghaction-chocolatey@v2
|
uses: crazy-max/ghaction-chocolatey@v2
|
||||||
with:
|
with:
|
||||||
args: install pkgconfiglite --checksum e87b5ea3c9142256af60f2d5b917aa63b571e6a0 --checksum-type sha1
|
args: install pkgconfiglite --checksum e87b5ea3c9142256af60f2d5b917aa63b571e6a0 --checksum-type sha1
|
||||||
- name: Install nsis
|
|
||||||
if: matrix.os == 'windows-latest'
|
|
||||||
uses: crazy-max/ghaction-chocolatey@v2
|
|
||||||
with:
|
|
||||||
args: install nsis
|
|
||||||
|
|
||||||
- name: Install Debian packaging dependencies
|
- name: Install Debian packaging dependencies
|
||||||
if: matrix.os == 'ubuntu-latest'
|
if: matrix.os == 'ubuntu-latest'
|
||||||
@@ -194,11 +191,6 @@ jobs:
|
|||||||
uses: crazy-max/ghaction-chocolatey@v2
|
uses: crazy-max/ghaction-chocolatey@v2
|
||||||
with:
|
with:
|
||||||
args: install pkgconfiglite --checksum e87b5ea3c9142256af60f2d5b917aa63b571e6a0 --checksum-type sha1
|
args: install pkgconfiglite --checksum e87b5ea3c9142256af60f2d5b917aa63b571e6a0 --checksum-type sha1
|
||||||
- name: Install nsis
|
|
||||||
if: matrix.os == 'windows-latest'
|
|
||||||
uses: crazy-max/ghaction-chocolatey@v2
|
|
||||||
with:
|
|
||||||
args: install nsis
|
|
||||||
|
|
||||||
- name: Configure Qtk Library
|
- name: Configure Qtk Library
|
||||||
shell: bash
|
shell: bash
|
||||||
@@ -206,13 +198,13 @@ jobs:
|
|||||||
|
|
||||||
- name: Build Qtk Library
|
- name: Build Qtk Library
|
||||||
shell: bash
|
shell: bash
|
||||||
run: cmake --build build/ --config Release --target qtk -- ${{ matrix.flags }}
|
run: cmake --build build/ --config Release --target qtk_library -- ${{ matrix.flags }}
|
||||||
|
|
||||||
# Packaging
|
# Packaging
|
||||||
|
|
||||||
- name: Install Qtk Library
|
- name: Install Qtk Library
|
||||||
shell: bash
|
shell: bash
|
||||||
run: cmake --install build/ --config Release --prefix=$(pwd)/install --component qtk
|
run: cmake --install build/ --config Release --prefix=$(pwd)/install --component qtk_library
|
||||||
|
|
||||||
- name: Package Qtk Library
|
- name: Package Qtk Library
|
||||||
shell: bash
|
shell: bash
|
||||||
@@ -371,12 +363,13 @@ jobs:
|
|||||||
|
|
||||||
release:
|
release:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: startsWith(github.ref, 'refs/tags/')
|
if: startsWith(github.event.workflow_run.head_branch, 'v')
|
||||||
needs: [Qtk, Qtk-Library, Qtk-Plugins]
|
needs: [Qtk, Qtk-Library, Qtk-Plugins]
|
||||||
steps:
|
steps:
|
||||||
- name: Download Installer Artifact
|
- name: Download Installer Artifact
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v4
|
||||||
with:
|
with:
|
||||||
|
name: Qtk Packages
|
||||||
path: |
|
path: |
|
||||||
build/packages/*
|
build/packages/*
|
||||||
install/*
|
install/*
|
||||||
@@ -385,7 +378,8 @@ jobs:
|
|||||||
id: create_release
|
id: create_release
|
||||||
uses: softprops/action-gh-release@v1
|
uses: softprops/action-gh-release@v1
|
||||||
with:
|
with:
|
||||||
name: Qtk ${{ github.ref_name }}
|
tag_name: ${{ github.event.workflow_run.head_branch }}
|
||||||
|
name: Qtk ${{ github.event.workflow_run.head_branch }}
|
||||||
draft: true
|
draft: true
|
||||||
prerelease: false
|
prerelease: false
|
||||||
generate_release_notes: true
|
generate_release_notes: true
|
||||||
|
|||||||
14
.github/workflows/linting.yml
vendored
14
.github/workflows/linting.yml
vendored
@@ -39,7 +39,7 @@ jobs:
|
|||||||
# Check the entire repo for source files to tidy
|
# Check the entire repo for source files to tidy
|
||||||
files-changed-only: false
|
files-changed-only: false
|
||||||
# Ignore qtk build and external assimp directories
|
# Ignore qtk build and external assimp directories
|
||||||
ignore: '.github|build|extern'
|
ignore: '.github|build|extern/assimp/assimp'
|
||||||
# Point to compile_commands.json produced by build
|
# Point to compile_commands.json produced by build
|
||||||
database: 'build'
|
database: 'build'
|
||||||
# Use thread comments as feedback
|
# Use thread comments as feedback
|
||||||
@@ -61,12 +61,8 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- uses: cpp-linter/cpp-linter-action@v2
|
- name: clang-format Check
|
||||||
|
uses: jidicula/clang-format-action@v4.9.0
|
||||||
with:
|
with:
|
||||||
version: '18'
|
clang-format-version: '18'
|
||||||
style: 'file'
|
check-path: ${{ matrix.path }}
|
||||||
tidy-checks: ''
|
|
||||||
files-changed-only: false
|
|
||||||
ignore: '.github|build|extern'
|
|
||||||
extensions: 'cpp,h'
|
|
||||||
files: ${{ matrix.path }}
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
## Author: Shaun Reed | Contact: shaunrd0@gmail.com | URL: www.shaunreed.com ##
|
## Author: Shaun Reed | Contact: shaunrd0@gmail.com | URL: www.shaunreed.com ##
|
||||||
## All Content (c) 2025 Shaun Reed, all rights reserved ##
|
## All Content (c) 2025 Shaun Reed, all rights reserved ##
|
||||||
################################################################################
|
################################################################################
|
||||||
cmake_minimum_required(VERSION 3.28)
|
cmake_minimum_required(VERSION 3.23)
|
||||||
|
|
||||||
################################################################################
|
################################################################################
|
||||||
# Constants
|
# Constants
|
||||||
@@ -31,7 +31,7 @@ add_compile_options(-fPIC)
|
|||||||
################################################################################
|
################################################################################
|
||||||
project(
|
project(
|
||||||
#[[NAME]] Qtk
|
#[[NAME]] Qtk
|
||||||
VERSION 0.3
|
VERSION 0.2
|
||||||
DESCRIPTION "Qt OpenGL library and desktop application."
|
DESCRIPTION "Qt OpenGL library and desktop application."
|
||||||
LANGUAGES CXX C
|
LANGUAGES CXX C
|
||||||
)
|
)
|
||||||
@@ -48,20 +48,9 @@ include(GNUInstallDirs)
|
|||||||
################################################################################
|
################################################################################
|
||||||
option(QTK_DEBUG "Enable debugger" OFF)
|
option(QTK_DEBUG "Enable debugger" OFF)
|
||||||
option(QTK_SUBMODULES "Update external project (assimp) submodule" OFF)
|
option(QTK_SUBMODULES "Update external project (assimp) submodule" OFF)
|
||||||
|
option(QTK_PLUGINS "Install Qtk plugins to Qt Creator path." OFF)
|
||||||
option(QTK_EXAMPLE "Build the Qtk example desktop application" ON)
|
option(QTK_EXAMPLE "Build the Qtk example desktop application" ON)
|
||||||
option(QTK_CCACHE "Enable ccache" ON)
|
option(QTK_CCACHE "Enable ccache" ON)
|
||||||
# Install Qtk for use within Qt Creator projects only, instead of system-wide.
|
|
||||||
option(QTK_PREFIX_QTCREATOR "Install Qtk to Qt Creator. Untested." OFF)
|
|
||||||
# Option for bringing your own assimp installation; Otherwise not needed
|
|
||||||
# + If assimp is available system-wide we can just set QTK_SUBMODULES OFF
|
|
||||||
option(
|
|
||||||
QTK_ASSIMP_NEW_INTERFACE
|
|
||||||
"Use the assimp::assimp interface (WIN / OSX)"
|
|
||||||
OFF
|
|
||||||
)
|
|
||||||
|
|
||||||
# Qtk Component Options
|
|
||||||
option(QTK_PLUGINS "Install Qtk plugins to Qt Designer path." OFF)
|
|
||||||
# Options for qtk_gui
|
# Options for qtk_gui
|
||||||
option(QTK_GUI "Build the Qtk desktop application" ON)
|
option(QTK_GUI "Build the Qtk desktop application" ON)
|
||||||
option(QTK_GUI_SCENE
|
option(QTK_GUI_SCENE
|
||||||
@@ -72,6 +61,17 @@ if (QTK_CCACHE)
|
|||||||
set(CMAKE_CXX_COMPILER_LAUNCHER ccache)
|
set(CMAKE_CXX_COMPILER_LAUNCHER ccache)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
# Install Qtk for use within Qt Creator projects only, instead of system-wide.
|
||||||
|
option(QTK_PREFIX_QTCREATOR "Install Qtk to Qt Creator. Untested." OFF)
|
||||||
|
|
||||||
|
# Option for bringing your own assimp installation; Otherwise not needed
|
||||||
|
# + If assimp is available system-wide we can just set QTK_SUBMODULES OFF
|
||||||
|
option(
|
||||||
|
QTK_ASSIMP_NEW_INTERFACE
|
||||||
|
"Use the assimp::assimp interface (WIN / OSX)"
|
||||||
|
OFF
|
||||||
|
)
|
||||||
|
|
||||||
if(QTK_DEBUG OR CMAKE_BUILD_TYPE MATCHES "^[Dd][Ee][Bb][Uu][Gg]$")
|
if(QTK_DEBUG OR CMAKE_BUILD_TYPE MATCHES "^[Dd][Ee][Bb][Uu][Gg]$")
|
||||||
set(QTK_DEBUG ON)
|
set(QTK_DEBUG ON)
|
||||||
set(CMAKE_BUILD_TYPE Debug)
|
set(CMAKE_BUILD_TYPE Debug)
|
||||||
@@ -84,7 +84,7 @@ endif()
|
|||||||
set(QT_INSTALL_DIR "$ENV{HOME}/Qt/6.5.0/gcc_64/lib/cmake" CACHE PATH "Path to Qt6 install.")
|
set(QT_INSTALL_DIR "$ENV{HOME}/Qt/6.5.0/gcc_64/lib/cmake" CACHE PATH "Path to Qt6 install.")
|
||||||
if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
|
if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
|
||||||
set(CMAKE_INSTALL_PREFIX "${CMAKE_BINARY_DIR}/install")
|
set(CMAKE_INSTALL_PREFIX "${CMAKE_BINARY_DIR}/install")
|
||||||
endif()
|
endif ()
|
||||||
|
|
||||||
set(QTK_RESOURCES "${CMAKE_SOURCE_DIR}/resources")
|
set(QTK_RESOURCES "${CMAKE_SOURCE_DIR}/resources")
|
||||||
set(QTK_OSX_ICONS ${CMAKE_SOURCE_DIR}/resources/icons/osx/kilroy.icns)
|
set(QTK_OSX_ICONS ${CMAKE_SOURCE_DIR}/resources/icons/osx/kilroy.icns)
|
||||||
@@ -157,15 +157,8 @@ list(APPEND VAR_NAMES QT6_INSTALL_PLUGINS)
|
|||||||
|
|
||||||
# Find Assimp.
|
# Find Assimp.
|
||||||
if(QTK_SUBMODULES)
|
if(QTK_SUBMODULES)
|
||||||
if(APPLE)
|
# Required to statically link.
|
||||||
# Avoid zlib redefining fdopen, causing build failures in apple clang.
|
add_compile_options(-fPIC)
|
||||||
# https://github.com/assimp/assimp/issues/6118
|
|
||||||
add_compile_definitions(-Dfdopen=fdopen)
|
|
||||||
endif()
|
|
||||||
if(NOT WIN32)
|
|
||||||
# Required to statically link.
|
|
||||||
add_compile_options(-fPIC)
|
|
||||||
endif()
|
|
||||||
set(BUILD_SHARED_LIBS OFF CACHE STRING "Build static assimp libs" FORCE)
|
set(BUILD_SHARED_LIBS OFF CACHE STRING "Build static assimp libs" FORCE)
|
||||||
set(ASSIMP_BUILD_ZLIB ON CACHE STRING "Build Zlib with assimp." FORCE)
|
set(ASSIMP_BUILD_ZLIB ON CACHE STRING "Build Zlib with assimp." FORCE)
|
||||||
set(
|
set(
|
||||||
@@ -182,14 +175,6 @@ if(QTK_SUBMODULES)
|
|||||||
"${CMAKE_CURRENT_SOURCE_DIR}/extern/assimp/assimp/"
|
"${CMAKE_CURRENT_SOURCE_DIR}/extern/assimp/assimp/"
|
||||||
EXCLUDE_FROM_ALL
|
EXCLUDE_FROM_ALL
|
||||||
)
|
)
|
||||||
install(
|
|
||||||
TARGETS assimp zlibstatic
|
|
||||||
EXPORT qtk_export
|
|
||||||
COMPONENT qtk
|
|
||||||
ARCHIVE DESTINATION lib
|
|
||||||
LIBRARY DESTINATION lib
|
|
||||||
RUNTIME DESTINATION bin
|
|
||||||
)
|
|
||||||
else()
|
else()
|
||||||
find_package(assimp REQUIRED)
|
find_package(assimp REQUIRED)
|
||||||
endif()
|
endif()
|
||||||
@@ -204,8 +189,8 @@ endif()
|
|||||||
add_subdirectory(src)
|
add_subdirectory(src)
|
||||||
|
|
||||||
if(QTK_EXAMPLE)
|
if(QTK_EXAMPLE)
|
||||||
# Create a namespaced alias for linking with libqtk in the example.
|
# Create a namespaced alias for linking with qtk_library in the example.
|
||||||
add_library(${PROJECT_NAME}::qtk ALIAS qtk)
|
add_library(${PROJECT_NAME}::qtk_library ALIAS qtk_library)
|
||||||
add_subdirectory(example-app EXCLUDE_FROM_ALL)
|
add_subdirectory(example-app EXCLUDE_FROM_ALL)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
@@ -220,25 +205,3 @@ foreach(VAR_NAME IN LISTS VAR_NAMES VAR_PATHS)
|
|||||||
message(STATUS "[Qtk] ${VAR_NAME}=${${VAR_NAME}}")
|
message(STATUS "[Qtk] ${VAR_NAME}=${${VAR_NAME}}")
|
||||||
endif()
|
endif()
|
||||||
endforeach()
|
endforeach()
|
||||||
message(STATUS "[Qtk] Installation prefix: ${CMAKE_INSTALL_PREFIX}")
|
|
||||||
message(STATUS "[Qtk] Found Qt6: ${Qt6Core_VERSION}")
|
|
||||||
|
|
||||||
# qt_generate_deploy_app_script is supported on Linux in QtCore >= 6.5.0.
|
|
||||||
# qt_generate_deploy_app_script supports Windows and OSX in QtCore >= 6.3.
|
|
||||||
# https://doc.qt.io/qt-6.5/qt-generate-deploy-app-script.html
|
|
||||||
# https://doc.qt.io/archives/qt-6.4/qt-generate-deploy-app-script.html
|
|
||||||
# The application can still build and run, we just can't install.
|
|
||||||
if(QTK_GUI OR QTK_EXAMPLE)
|
|
||||||
if(UNIX AND NOT APPLE)
|
|
||||||
# Ubuntu 24.04 provides QtCore 6.4.2 in qt-base-dev.
|
|
||||||
# This version of qt_generate_deploy_app_script does not support Linux.
|
|
||||||
if (Qt6_VERSION VERSION_LESS "6.5.0")
|
|
||||||
message(WARNING "[Qtk] Installation is only supported on Qt >=6.5.\n")
|
|
||||||
endif()
|
|
||||||
elseif(APPLE OR WIN32)
|
|
||||||
# Predates qt_generate_deploy_app_script.
|
|
||||||
if (Qt6_VERSION VERSION_LESS "6.3.0")
|
|
||||||
message(WARNING "[Qtk] Installation is only supported on Qt >=6.5.\n")
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|||||||
196
README.md
196
README.md
@@ -1,18 +1,13 @@
|
|||||||
# Qtk
|
# Qtk
|
||||||
|
|
||||||
[](https://github.com/shaunrd0/qtk/actions/workflows/build.yml)
|
[](https://github.com/shaunrd0/qtk/actions/workflows/all-builds.yml)
|
||||||
[](https://github.com/shaunrd0/qtk/actions/workflows/linting.yml)
|
[](https://github.com/shaunrd0/qtk/actions/workflows/linting.yml)
|
||||||
|
|
||||||
|
Qtk is a Qt OpenGL graphics library that wraps some QOpenGL functionality in convenience classes
|
||||||
|
that allow rendering geometry in 2D and 3D using custom GLSL shader programs.
|
||||||
|
|
||||||
The Qtk desktop application provides a model loader using [Assimp](https://assimp.org/) within a Qt widget application.
|
The Qtk desktop application provides a model loader using [Assimp](https://assimp.org/) within a Qt widget application.
|
||||||
You can fly around the scene using WASD while holding down the left or right mouse button.
|
You can fly around the scene using WASD while holding down the left or right mouse button.
|
||||||
[QtkWidget](./src/designer-plugins/qtkwidget.h) is the primary QOpenGLWidget used to render the scene and handle input.
|
|
||||||
|
|
||||||
The underlying shared library [libqtk](./src/qtk) wraps QOpenGL objects in convenience classes that leverage
|
|
||||||
lower-level OpenGL APIs to handle the rendering process manually. Many of these classes offer
|
|
||||||
ways to expand the low-level OpenGL logic within a Qt application without having to set up much scaffolding.
|
|
||||||
|
|
||||||
The Qtk GUI is built using custom [Qt Designer plugins](https://doc.qt.io/qt-6/designer-creating-custom-widgets.html). These can be installed to Qt Designer for
|
|
||||||
use in other Qt applications, or built exclusively for Qtk. See [Build Options](#build-options) for more details.
|
|
||||||
|
|
||||||
Object names can be double-clicked in the tree view panel for quick camera navigation.
|
Object names can be double-clicked in the tree view panel for quick camera navigation.
|
||||||
Properties of the object, like shader code and translation / scale, can be viewed and modified in the side panel.
|
Properties of the object, like shader code and translation / scale, can be viewed and modified in the side panel.
|
||||||
@@ -29,13 +24,7 @@ detached from the main window in this way.
|
|||||||
The small triangles floating near 3D models represent the light source being used for the shader.
|
The small triangles floating near 3D models represent the light source being used for the shader.
|
||||||
These appear on models using phong, specular, and diffuse lighting techniques.
|
These appear on models using phong, specular, and diffuse lighting techniques.
|
||||||
|
|
||||||
The example scene contains basic examples like texture mapping to make a crate from simple cube geometry.
|
The default scene contains basic examples like texture mapping to make a crate from basic cube geometry
|
||||||
This scene is used in the following screenshots, and can be built locally by enabling
|
|
||||||
the `QTK_GUI_SCENE` [Build Option](#build-options) described below. Because this scene
|
|
||||||
uses large 3D model resources, this option is disabled by default.
|
|
||||||
|
|
||||||
The default scene with this option disabled is empty, but comes with a default skybox.
|
|
||||||
Models can be added to the scene by clicking and dragging an `.obj` into the scene view.
|
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
@@ -49,7 +38,7 @@ And more advanced techniques like Phong lighting (ambient + diffuse + specular)
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
| Normal Mapping Disabled | Normal Mapping Enabled |
|
| Normal Mapping Disabled | Normal Mapping Enabled |
|
||||||
|------------------------------------------------------|--------------------------------------------------------|
|
|------------------------------------------------------|--------------------------------------------------------|
|
||||||
| <img src="resources/screenshots/spartan-phong.png"/> | <img src="resources/screenshots/spartan-normals.png"/> |
|
| <img src="resources/screenshots/spartan-phong.png"/> | <img src="resources/screenshots/spartan-normals.png"/> |
|
||||||
|
|
||||||
@@ -60,99 +49,43 @@ Key features that are planned:
|
|||||||
- [x] Runtime loading of `.obj` or similar 3D models.
|
- [x] Runtime loading of `.obj` or similar 3D models.
|
||||||
- [x] Drag-and-drop interaction for adding objects to the scene.
|
- [x] Drag-and-drop interaction for adding objects to the scene.
|
||||||
- [x] Shader / object properties panel to modify related settings.
|
- [x] Shader / object properties panel to modify related settings.
|
||||||
- [x] Reduce size of application resources and git references.
|
|
||||||
- [ ] Runtime reloading of modified GLSL shaders attached to objects within scenes.
|
- [ ] Runtime reloading of modified GLSL shaders attached to objects within scenes.
|
||||||
- [ ] Multiple views of a scene at one time.
|
- [ ] Multiple views of a scene at one time.
|
||||||
- [ ] Camera control modes such as panning, orbiting, or following objects.
|
- [ ] Camera control modes such as panning, orbiting, or following objects.
|
||||||
- [ ] Save / load scene data. The current model requires writing C++ code.
|
- [ ] Save / load scene data. The current model requires writing C++ code.
|
||||||
- [ ] Basic text editor for quickly modifying shaders attached to objects.
|
- [ ] Basic text editor for quickly modifying shaders attached to objects.
|
||||||
|
- [ ] Reduce size of application resources and git references.
|
||||||
|
|
||||||
For examples of using libqtk, see the [example-app](./example-app)
|
For examples of using the Qtk API, see the `example-app` project in the root of
|
||||||
project in the root of this repository.
|
this repository.
|
||||||
|
|
||||||
To get textures loading on models look
|
To get textures loading on models look
|
||||||
into [material files](http://www.paulbourke.net/dataformats/mtl/)
|
into [material files](http://www.paulbourke.net/dataformats/mtl/)
|
||||||
and see some examples at [qtk-resources/resources/models](https://git.shaunreed.com/shaunrd0/qtk-resources/src/branch/master/models).
|
and see some examples in the `resources/models/` directory.
|
||||||
|
|
||||||
### Source Builds
|
### Source Builds
|
||||||
|
|
||||||
Qtk was developed and tested using CLion
|
Qtk was developed and tested using CLion
|
||||||
and [Qt Creator](https://github.com/qt-creator/qt-creator).
|
and [Qt Creator](https://github.com/qt-creator/qt-creator).
|
||||||
Simply open the root `CMakeLists.txt` with either of these editors and default
|
Simply open the root `CMakeLists.txt` with either of these editors and
|
||||||
configurations will be loaded. To simplify providing Qt to the build, Qt Creator
|
configurations will be loaded.
|
||||||
is the recommended option.
|
|
||||||
|
|
||||||
If you have manually installed [Qt6 Open Source Binaries](https://www.qt.io/download-qt-installer)
|
This project has been ported to **Qt 6.6.0**, which is not yet available in
|
||||||
for your system, be sure to correctly set your `CMAKE_PREFIX_PATH` in the next steps.
|
Ubuntu apt repositories.
|
||||||
On Ubuntu 24.04 the default installation directory to use for this path using Qt 6.5.0 is `$HOME/Qt/6.5.0/gcc_64/lib/cmake`.
|
To run this project, you will *need* to
|
||||||
|
install [Qt6 Open Source Binaries](https://www.qt.io/download-qt-installer) for
|
||||||
The Ubuntu apt repositories contain all the packages we need to build all targets.
|
your system, **version 6.6.0** or later.
|
||||||
To build Qtk desktop application with the scene in the screenshots below run the following commands.
|
Be sure to take note of the Qt6 installation directory, as we will need it to
|
||||||
|
correctly set our `CMAKE_PREFIX_PATH` in the next steps.
|
||||||
```bash
|
|
||||||
sudo apt update && sudo apt install cmake build-essential git ccache libxkbcommon-dev libassimp-dev qt6-base-dev qt6-tools-dev zlib1g-dev
|
|
||||||
cmake -DQTK_GUI_SCENE=ON -B build
|
|
||||||
cmake --build build
|
|
||||||
./build/bin/qtk_gui
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Build Options
|
|
||||||
|
|
||||||
Qtk is composed of three separate components.
|
|
||||||
|
|
||||||
* The shared library [libqtk](./src/qtk) provides classes that leverage QOpenGL functionality
|
|
||||||
while still using lower-level OpenGL APIs to customize the rendering process.
|
|
||||||
Many of these classes can be further expanded, such as [Qtk::Scene](./src/qtk/scene.h).
|
|
||||||
This taget, `qtk` in cmake, is always selected to build and install as
|
|
||||||
it is required by all other components in this project.
|
|
||||||
* The [Qtk desktop application](./src/app) is built using libqtk within a Qt application.
|
|
||||||
This target, `qtk_gui` in cmake, is optional and can be controlled using the `QTK_GUI` option below.
|
|
||||||
* The GUI for the Qtk desktop application is constructed using a [custom set of Qt Designer widget plugins](./src/designer-plugins) that are also built using libqtk.
|
|
||||||
If `QTK_GUI` is disabled this target (`qtk_plugins`) is optional and can be controlled using the `QTK_PLUGINS` options below.
|
|
||||||
|
|
||||||
|
|
||||||
| Name | Description | Default |
|
|
||||||
|--------------------------|--------------------------------------------------------------|:--------|
|
|
||||||
| QTK_DEBUG | Enable debug mode. | OFF |
|
|
||||||
| QTK_SUBMODULES | Use git submodules to fetch assimp dependency. | OFF |
|
|
||||||
| QTK_EXAMPLE | Build the libqtk example desktop application. | ON |
|
|
||||||
| QTK_CCACHE | Enable CCACHE. | ON |
|
|
||||||
| QTK_ASSIMP_NEW_INTERFACE | Use the assimp::assimp interface. Recommended for WIN / OSX. | OFF |
|
|
||||||
| QTK_PLUGINS* | Install Qtk plugins to Qt Designer. | OFF |
|
|
||||||
| QTK_GUI | Build and install Qtk desktop application. | ON |
|
|
||||||
| QTK_GUI_SCENE | Fetch external 3D model resources for example scene. | OFF |
|
|
||||||
|
|
||||||
*The Qtk plugins are always built if `QTK_GUI` is enabled. Disabling this option
|
|
||||||
with QTK_GUI set will not mark the plugins for installation if we do
|
|
||||||
`cmake --install build/` without selecting a component. If both `QTK_GUI` and
|
|
||||||
`QTK_PLUGINS` are unset, neither will be built.
|
|
||||||
|
|
||||||
If you are building on **Windows / Mac**, consider setting
|
If you are building on **Windows / Mac**, consider setting
|
||||||
the `-DQTK_ASSIMP_NEW_INTERFACE` cmake build option.
|
the `-DQTK_ASSIMP_NEW_INTERFACE` cmake build option.
|
||||||
|
|
||||||
By default, the build will not initialize Assimp as a git submodule and build
|
If the build is configured with all options enabled, we can subsequently install
|
||||||
from source.
|
individual components as needed with cmake.
|
||||||
We can turn this on by setting the `-DQTK_SUBMODULES=ON` flag when running
|
|
||||||
CMake.
|
|
||||||
Building using this option will fetch and build Assimp for us, but builds will
|
|
||||||
take longer as a result.
|
|
||||||
Using `-DQTK_SUBMODULES=ON` supports providing assimp on cross-platform builds (
|
|
||||||
Windows / Mac / Linux) and may be easier to configure.
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cmake -B build-all -DQTK_GUI=ON -DQTK_PLUGINS=ON -DQTK_EXAMPLE=ON -DQTK_SUBMODULES=ON -DCMAKE_PREFIX_PATH=$HOME/Qt/6.6.0/gcc_64
|
sudo apt update -y && sudo apt install libassimp-dev cmake build-essential git ccache libgl1-mesa-dev libglvnd-dev zlib1g-dev -y
|
||||||
```
|
|
||||||
|
|
||||||
#### Qtk Components
|
|
||||||
|
|
||||||
As described in [Build Options](#build-options), Qtk is composed of three separate components.
|
|
||||||
Each component can be individually selected for building or installation.
|
|
||||||
|
|
||||||
For this example we will configure the build with all options enabled.
|
|
||||||
In the separate sections below we can install individual components with cmake.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo apt update -y && sudo apt install cmake build-essential git ccache libxkbcommon-dev libassimp-dev qt6-base-dev qt6-tools-dev -y
|
|
||||||
git clone https://github.com/shaunrd0/qtk
|
git clone https://github.com/shaunrd0/qtk
|
||||||
cd qtk
|
cd qtk
|
||||||
# Configure the build with all components enabled
|
# Configure the build with all components enabled
|
||||||
@@ -161,33 +94,54 @@ cmake -B build-all -DQTK_GUI=ON -DQTK_PLUGINS=ON -DQTK_EXAMPLE=ON -DCMAKE_PREFIX
|
|||||||
cmake --build build-all/
|
cmake --build build-all/
|
||||||
````
|
````
|
||||||
|
|
||||||
Now that we have all the components fully built, the following sections will
|
By default, the build will not initialize Assimp as a git submodule and build
|
||||||
install each component individually.
|
from source.
|
||||||
If you want to uninstall previously installed components, run the following command.
|
We can turn this on by setting the `-DQTK_SUBMODULES=ON` flag when running
|
||||||
|
CMake.
|
||||||
|
Building using this option will fetch and build Assimp for us, but builds will
|
||||||
|
take longer as a result.
|
||||||
|
Using `-DQTK_SUBMODULES=ON` supports providing assimp on cross-platform builds (
|
||||||
|
Windows / Mac / Linux) and may be easier
|
||||||
|
to configure.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo xargs rm -v < install_manifest.txt
|
cmake -B build-all -DQTK_GUI=ON -DQTK_PLUGINS=ON -DQTK_EXAMPLE=ON -DQTK_SUBMODULES=ON -DCMAKE_PREFIX_PATH=$HOME/Qt/6.6.0/gcc_64
|
||||||
```
|
```
|
||||||
|
|
||||||
##### Qtk Library
|
#### Qtk GUI
|
||||||
|
|
||||||
Shared libqtk library for working with lower-level OpenGL to customize the rendering process.
|
```bash
|
||||||
|
cmake --build build-all/ --target qtk_gui -- -j $(nproc)
|
||||||
|
# Install Qtk desktop application (output removed)
|
||||||
|
# Installation prefix path must be absolute, since Qtk uses Qt deploy tools.
|
||||||
|
cmake --install build-all/ --component qtk_gui --prefix=$(pwd)/install
|
||||||
|
./install/bin/qtk_gui
|
||||||
|
```
|
||||||
|
|
||||||
|
If any errors are encountered loading plugins, we can debug plugin loading by
|
||||||
|
setting the following environment variable -
|
||||||
|
|
||||||
|
```bash
|
||||||
|
QT_DEBUG_PLUGINS=1 ./install/bin/qtk_gui
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Qtk Library
|
||||||
|
|
||||||
|
Qtk provides a simple library for working with QOpenGL.
|
||||||
We can install this library on a system path or a custom path and then
|
We can install this library on a system path or a custom path and then
|
||||||
set `CMAKE_PREFIX_PATH` to point to this location when building an application
|
set `CMAKE_PREFIX_PATH` to point to this location when building an application
|
||||||
using libqtk.
|
using libqtk.
|
||||||
|
|
||||||
Here we will install to the `/usr/local/` path.
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Install libqtk only
|
# Install libqtk only
|
||||||
cmake --build build-all/ --target qtk -- -j $(nproc)
|
cmake --build build-all/ --target qtk_library -- -j $(nproc)
|
||||||
cmake --install build-all/ --component qtk --prefix=/usr/local
|
cmake --install build-all/ --component qtk_library --prefix=/usr/local
|
||||||
-- Install configuration: "Release"
|
-- Install configuration: "Release"
|
||||||
-- Installing: /usr/local/lib/cmake/Qtk/QtkConfig.cmake
|
-- Installing: /usr/local/lib/cmake/Qtk/QtkConfig.cmake
|
||||||
-- Installing: /usr/local/lib/cmake/Qtk/QtkConfigVersion.cmake
|
-- Installing: /usr/local/lib/cmake/Qtk/QtkConfigVersion.cmake
|
||||||
-- Installing: /usr/local/lib/cmake/Qtk/QtkTargets.cmake
|
-- Installing: /usr/local/lib/cmake/Qtk/QtkTargets.cmake
|
||||||
-- Installing: /usr/local/lib/cmake/Qtk/QtkTargets-release.cmake
|
-- Installing: /usr/local/lib/cmake/Qtk/QtkTargets-release.cmake
|
||||||
-- Installing: /usr/local/lib/static/libqtk.a
|
-- Installing: /usr/local/lib/static/libqtk_library.a
|
||||||
-- Installing: /usr/local/include/qtk/camera3d.h
|
-- Installing: /usr/local/include/qtk/camera3d.h
|
||||||
-- Installing: /usr/local/include/qtk/input.h
|
-- Installing: /usr/local/include/qtk/input.h
|
||||||
-- Installing: /usr/local/include/qtk/meshrenderer.h
|
-- Installing: /usr/local/include/qtk/meshrenderer.h
|
||||||
@@ -204,26 +158,7 @@ cmake --install build-all/ --component qtk --prefix=/usr/local
|
|||||||
-- Installing: /usr/local/include/qtk/transform3D.h
|
-- Installing: /usr/local/include/qtk/transform3D.h
|
||||||
```
|
```
|
||||||
|
|
||||||
##### Qtk GUI
|
#### Qtk Plugin Collection
|
||||||
|
|
||||||
The Qtk desktop application can be built and installed with the following commands.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cmake --build build-all/ --target qtk_gui -- -j $(nproc)
|
|
||||||
# Install Qtk desktop application (output removed)
|
|
||||||
# Installation prefix path must be absolute, since Qtk uses Qt deploy tools.
|
|
||||||
cmake --install build-all/ --component qtk_gui --prefix=$(pwd)/install
|
|
||||||
./install/bin/qtk_gui
|
|
||||||
```
|
|
||||||
|
|
||||||
If any errors are encountered loading plugins, we can debug plugin loading by
|
|
||||||
setting the following environment variable
|
|
||||||
|
|
||||||
```bash
|
|
||||||
QT_DEBUG_PLUGINS=1 ./install/bin/qtk_gui
|
|
||||||
```
|
|
||||||
|
|
||||||
##### Qtk Plugin Collection
|
|
||||||
|
|
||||||
This project defines a collection of widget plugins for use with Qt Designer.
|
This project defines a collection of widget plugins for use with Qt Designer.
|
||||||
These plugins were used to build the interface for the Qtk desktop application.
|
These plugins were used to build the interface for the Qtk desktop application.
|
||||||
@@ -244,12 +179,19 @@ cmake --build build-all/ --target qtk_plugins -- -j $(nproc)
|
|||||||
# The path here should be initialized during build configuration, so no need for --prefix
|
# The path here should be initialized during build configuration, so no need for --prefix
|
||||||
cmake --install build-all/ --component qtk_plugins
|
cmake --install build-all/ --component qtk_plugins
|
||||||
-- Install configuration: "Release"
|
-- Install configuration: "Release"
|
||||||
-- Up-to-date: /home/shaun/Qt/6.6.0/gcc_64/../../Tools/QtCreator/lib/Qt/lib/libqtk.a
|
-- Up-to-date: /home/shaun/Qt/6.6.0/gcc_64/../../Tools/QtCreator/lib/Qt/lib/libqtk_library.a
|
||||||
-- Up-to-date: /home/shaun/Qt/6.6.0/gcc_64/../../Tools/QtCreator/lib/Qt/lib/libqtk_plugin_library.a
|
-- Up-to-date: /home/shaun/Qt/6.6.0/gcc_64/../../Tools/QtCreator/lib/Qt/lib/libqtk_plugin_library.a
|
||||||
-- Up-to-date: /home/shaun/Qt/6.6.0/gcc_64/../../Tools/QtCreator/lib/Qt/plugins/designer/libqtk_collection.so
|
-- Up-to-date: /home/shaun/Qt/6.6.0/gcc_64/../../Tools/QtCreator/lib/Qt/plugins/designer/libqtk_collection.so
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Example libqtk Application
|
To uninstall after a previous installation, we can run the following command
|
||||||
|
from the root of the repository.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
xargs rm < build/install_manifest.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Qtk Example
|
||||||
|
|
||||||
There is a simple example of using libqtk in the [example-app/](example-app)
|
There is a simple example of using libqtk in the [example-app/](example-app)
|
||||||
directory. The example can be built standalone using `find_package` or as a
|
directory. The example can be built standalone using `find_package` or as a
|
||||||
@@ -271,7 +213,7 @@ This project is using `clang-format` version `>=15.0.5`.
|
|||||||
On Ubuntu 24.04, clang-format 18 is available to install in apt repositories.
|
On Ubuntu 24.04, clang-format 18 is available to install in apt repositories.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo apt install clang-format clang-tidy
|
sudo apt install clang-format
|
||||||
```
|
```
|
||||||
|
|
||||||
If `clang-format --version` is any earlier than `15.0.0`, running `clang-format` will fail because this project uses configuration options made available since `15.0.0`.
|
If `clang-format --version` is any earlier than `15.0.0`, running `clang-format` will fail because this project uses configuration options made available since `15.0.0`.
|
||||||
@@ -296,18 +238,14 @@ If you'd still like to run these tools manually, see the instructions below.
|
|||||||
cd qtk
|
cd qtk
|
||||||
# Build
|
# Build
|
||||||
cmake -B build && cmake --build build -- -j $(nproc)
|
cmake -B build && cmake --build build -- -j $(nproc)
|
||||||
```
|
clang-tidy -p build/ --fix --config-file=.clang-tidy src/**/*.cpp src/**/*.h example-app/*.cpp example-app/*.h
|
||||||
|
|
||||||
```bash
|
|
||||||
export SOURCES=src/**/*.cpp src/**/*.h example-app/*.cpp example-app/*.h
|
|
||||||
run-clang-tidy -p build/ -j $(nproc --ignore=1) -fix -config-file=.clang-tidy $SOURCES
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Last we need to run `clang-format`, this can be done with the command directly.
|
Last we need to run `clang-format`, this can be done with the command directly.
|
||||||
This will reformat all the code in the repository.
|
This will reformat all the code in the repository.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
clang-format -i --style=file:.clang-format $SOURCES
|
clang-format -i --style=file:.clang-format src/**/*.cpp src/**/*.h example-app/*.cpp example-app/*.h
|
||||||
```
|
```
|
||||||
|
|
||||||
`clang-format` can be run with git integration (or CLion if you prefer).
|
`clang-format` can be run with git integration (or CLion if you prefer).
|
||||||
|
|||||||
@@ -56,8 +56,8 @@ endif()
|
|||||||
|
|
||||||
# Allow add_subdirectory on this project to use target ALIAS if available.
|
# Allow add_subdirectory on this project to use target ALIAS if available.
|
||||||
# If this example project is opened standalone we will use find_package.
|
# If this example project is opened standalone we will use find_package.
|
||||||
if(NOT TARGET Qtk::qtk)
|
if(NOT TARGET Qtk::qtk_library)
|
||||||
find_package(Qtk 0.3 REQUIRED)
|
find_package(Qtk 0.2 REQUIRED)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
find_package(Qt6 COMPONENTS Core Widgets OpenGLWidgets REQUIRED)
|
find_package(Qt6 COMPONENTS Core Widgets OpenGLWidgets REQUIRED)
|
||||||
@@ -77,27 +77,20 @@ configure_file(
|
|||||||
|
|
||||||
qt_add_executable(qtk_example ${EXAMPLE_SOURCES})
|
qt_add_executable(qtk_example ${EXAMPLE_SOURCES})
|
||||||
target_link_libraries(qtk_example PUBLIC Qt6::Widgets Qt6::OpenGLWidgets Qt6::Core)
|
target_link_libraries(qtk_example PUBLIC Qt6::Widgets Qt6::OpenGLWidgets Qt6::Core)
|
||||||
target_link_libraries(qtk_example PUBLIC Qtk::qtk)
|
target_link_libraries(qtk_example PUBLIC Qtk::qtk_library)
|
||||||
target_include_directories(qtk_example PRIVATE "${CMAKE_CURRENT_BINARY_DIR}")
|
target_include_directories(qtk_example PRIVATE "${CMAKE_CURRENT_BINARY_DIR}")
|
||||||
|
|
||||||
# qt_generate_deploy_app_script supports Windows and OSX in QtCore >= 6.3.
|
install(
|
||||||
# qt_generate_deploy_app_script is supported on Linux in QtCore >= 6.5.0.
|
TARGETS qtk_example
|
||||||
if((Qt6_VERSION VERSION_GREATER_EQUAL "6.3.0" AND (WIN32 OR APPLE))
|
COMPONENT qtk_example
|
||||||
OR Qt6_VERSION VERSION_GREATER_EQUAL "6.5.0")
|
BUNDLE DESTINATION .
|
||||||
install(
|
LIBRARY DESTINATION lib
|
||||||
TARGETS qtk_example
|
ARCHIVE DESTINATION lib/static
|
||||||
COMPONENT qtk_example
|
RUNTIME DESTINATION bin
|
||||||
BUNDLE DESTINATION .
|
)
|
||||||
LIBRARY DESTINATION lib
|
qt_generate_deploy_app_script(
|
||||||
ARCHIVE DESTINATION lib/static
|
TARGET qtk_example
|
||||||
RUNTIME DESTINATION bin
|
OUTPUT_SCRIPT QTK_EXAMPLE_DEPLOY_SCRIPT
|
||||||
)
|
NO_UNSUPPORTED_PLATFORM_ERROR
|
||||||
qt_generate_deploy_app_script(
|
)
|
||||||
TARGET qtk_example
|
install(SCRIPT ${QTK_EXAMPLE_DEPLOY_SCRIPT} COMPONENT qtk_example)
|
||||||
OUTPUT_SCRIPT QTK_EXAMPLE_DEPLOY_SCRIPT
|
|
||||||
NO_UNSUPPORTED_PLATFORM_ERROR
|
|
||||||
)
|
|
||||||
install(SCRIPT ${QTK_EXAMPLE_DEPLOY_SCRIPT} COMPONENT qtk_example)
|
|
||||||
elseif(NOT Qtk_IS_TOP_LEVEL)
|
|
||||||
message(WARNING "[Qtk] Installation is only supported on Qt >=6.5.\n")
|
|
||||||
endif()
|
|
||||||
|
|||||||
@@ -22,7 +22,18 @@ ExampleScene::~ExampleScene() = default;
|
|||||||
|
|
||||||
void ExampleScene::init()
|
void ExampleScene::init()
|
||||||
{
|
{
|
||||||
setSkybox(new Qtk::Skybox);
|
setSkybox(new Qtk::Skybox(":/textures/skybox/right.png",
|
||||||
|
":/textures/skybox/top.png",
|
||||||
|
":/textures/skybox/front.png",
|
||||||
|
":/textures/skybox/left.png",
|
||||||
|
":/textures/skybox/bottom.png",
|
||||||
|
":/textures/skybox/back.png",
|
||||||
|
"Skybox"));
|
||||||
|
|
||||||
|
std::string spartanPath = QTK_EXAMPLE_SOURCE_DIR;
|
||||||
|
spartanPath += "/../resources/models/spartan/spartan.obj";
|
||||||
|
auto spartan = addObject(new Model("spartan", spartanPath.c_str()));
|
||||||
|
spartan->getTransform().setTranslation(-4.0f, 0.0f, 0.0f);
|
||||||
|
|
||||||
auto mesh = addObject(
|
auto mesh = addObject(
|
||||||
new Qtk::MeshRenderer("rightTriangle", Triangle(QTK_DRAW_ELEMENTS)));
|
new Qtk::MeshRenderer("rightTriangle", Triangle(QTK_DRAW_ELEMENTS)));
|
||||||
|
|||||||
2
extern/assimp/assimp
vendored
2
extern/assimp/assimp
vendored
Submodule extern/assimp/assimp updated: e0b52347c6...5d5496f1ad
BIN
resources/images/plaster.png
Normal file
BIN
resources/images/plaster.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.8 MiB |
@@ -1,21 +0,0 @@
|
|||||||
<RCC>
|
|
||||||
<qresource prefix="/textures">
|
|
||||||
<file alias="crate.png">images/crate.png</file>
|
|
||||||
<file alias="stone.png">images/stone.png</file>
|
|
||||||
<file alias="wood.png">images/wood.png</file>
|
|
||||||
<file>skybox/back.png</file>
|
|
||||||
<file>skybox/bottom.png</file>
|
|
||||||
<file>skybox/front.png</file>
|
|
||||||
<file>skybox/left.png</file>
|
|
||||||
<file>skybox/right.png</file>
|
|
||||||
<file>skybox/top.png</file>
|
|
||||||
</qresource>
|
|
||||||
<qresource prefix="/icons">
|
|
||||||
<file>fontawesome-free-6.2.1-desktop/svgs/solid/cube.svg</file>
|
|
||||||
<file>fontawesome-free-6.2.1-desktop/svgs/regular/trash-can.svg</file>
|
|
||||||
<file>fontawesome-free-6.2.1-desktop/svgs/regular/folder-open.svg</file>
|
|
||||||
<file>fontawesome-free-6.2.1-desktop/svgs/regular/floppy-disk.svg</file>
|
|
||||||
<file>fontawesome-free-6.2.1-desktop/svgs/brands/git-alt.svg</file>
|
|
||||||
<file alias="icon.png">icons/icon.png</file>
|
|
||||||
</qresource>
|
|
||||||
</RCC>
|
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
<RCC>
|
<RCC>
|
||||||
<qresource prefix="/textures">
|
<qresource prefix="/textures">
|
||||||
|
<file alias="plaster.png">images/plaster.png</file>
|
||||||
<file alias="crate.png">images/crate.png</file>
|
<file alias="crate.png">images/crate.png</file>
|
||||||
<file alias="stone.png">images/stone.png</file>
|
<file alias="stone.png">images/stone.png</file>
|
||||||
<file alias="wood.png">images/wood.png</file>
|
<file alias="wood.png">images/wood.png</file>
|
||||||
@@ -27,6 +28,8 @@
|
|||||||
<file alias="solid.vert">shaders/vertex/solid.vert</file>
|
<file alias="solid.vert">shaders/vertex/solid.vert</file>
|
||||||
<file alias="solid-perspective.frag">shaders/fragment/solid-perspective.frag</file>
|
<file alias="solid-perspective.frag">shaders/fragment/solid-perspective.frag</file>
|
||||||
<file alias="solid-perspective.vert">shaders/vertex/solid-perspective.vert</file>
|
<file alias="solid-perspective.vert">shaders/vertex/solid-perspective.vert</file>
|
||||||
|
<file alias="multi-color.frag">shaders/fragment/multi-color.frag</file>
|
||||||
|
<file alias="multi-color.vert">shaders/vertex/multi-color.vert</file>
|
||||||
<file alias="rgb-normals.frag">shaders/fragment/rgb-normals.frag</file>
|
<file alias="rgb-normals.frag">shaders/fragment/rgb-normals.frag</file>
|
||||||
<file alias="rgb-normals.vert">shaders/vertex/rgb-normals.vert</file>
|
<file alias="rgb-normals.vert">shaders/vertex/rgb-normals.vert</file>
|
||||||
<file alias="texture-cubemap.frag">shaders/fragment/texture-cubemap.frag</file>
|
<file alias="texture-cubemap.frag">shaders/fragment/texture-cubemap.frag</file>
|
||||||
@@ -41,9 +44,13 @@
|
|||||||
<file alias="solid-specular.vert">shaders/vertex/solid-specular.vert</file>
|
<file alias="solid-specular.vert">shaders/vertex/solid-specular.vert</file>
|
||||||
<file alias="solid-phong.frag">shaders/fragment/solid-phong.frag</file>
|
<file alias="solid-phong.frag">shaders/fragment/solid-phong.frag</file>
|
||||||
<file alias="solid-phong.vert">shaders/vertex/solid-phong.vert</file>
|
<file alias="solid-phong.vert">shaders/vertex/solid-phong.vert</file>
|
||||||
|
<file alias="model-basic.frag">shaders/fragment/model-basic.frag</file>
|
||||||
|
<file alias="model-basic.vert">shaders/vertex/model-basic.vert</file>
|
||||||
<file alias="model-phong.frag">shaders/fragment/model-phong.frag</file>
|
<file alias="model-phong.frag">shaders/fragment/model-phong.frag</file>
|
||||||
<file alias="model-phong.vert">shaders/vertex/model-phong.vert</file>
|
<file alias="model-phong.vert">shaders/vertex/model-phong.vert</file>
|
||||||
<file alias="model-normals.frag">shaders/fragment/model-normals.frag</file>
|
<file alias="model-normals.frag">shaders/fragment/model-normals.frag</file>
|
||||||
<file alias="model-normals.vert">shaders/vertex/model-normals.vert</file>
|
<file alias="model-normals.vert">shaders/vertex/model-normals.vert</file>
|
||||||
|
<file alias="skybox.frag">skybox/skybox.frag</file>
|
||||||
|
<file alias="skybox.vert">skybox/skybox.vert</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
|
|||||||
11
resources/shaders/fragment/model-basic.frag
Normal file
11
resources/shaders/fragment/model-basic.frag
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
#version 330 core
|
||||||
|
out vec4 fColor;
|
||||||
|
|
||||||
|
in vec2 vTextureCoord;
|
||||||
|
|
||||||
|
uniform sampler2D texture_diffuse1;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
fColor = texture(texture_diffuse1, vTextureCoord);
|
||||||
|
}
|
||||||
9
resources/shaders/fragment/multi-color.frag
Normal file
9
resources/shaders/fragment/multi-color.frag
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
#version 330
|
||||||
|
in vec4 vColor;
|
||||||
|
|
||||||
|
out vec4 fColor;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
fColor = vColor;
|
||||||
|
}
|
||||||
16
resources/shaders/vertex/model-basic.vert
Normal file
16
resources/shaders/vertex/model-basic.vert
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
#version 330 core
|
||||||
|
layout (location = 0) in vec3 aPosition;
|
||||||
|
layout (location = 1) in vec3 aNormal;
|
||||||
|
layout (location = 2) in vec2 aTextureCoord;
|
||||||
|
|
||||||
|
out vec2 vTextureCoord;
|
||||||
|
|
||||||
|
uniform mat4 uModel;
|
||||||
|
uniform mat4 uView;
|
||||||
|
uniform mat4 uProjection;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
vTextureCoord = aTextureCoord;
|
||||||
|
gl_Position = uProjection * uView * uModel * vec4(aPosition, 1.0);
|
||||||
|
}
|
||||||
16
resources/shaders/vertex/multi-color.vert
Normal file
16
resources/shaders/vertex/multi-color.vert
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
#version 330
|
||||||
|
layout(location = 0) in vec3 aPosition;
|
||||||
|
layout(location = 1) in vec3 aColor;
|
||||||
|
|
||||||
|
out vec4 vColor;
|
||||||
|
|
||||||
|
uniform mat4 uModel; // Model
|
||||||
|
uniform mat4 uView; // View
|
||||||
|
uniform mat4 uProjection; // Projection
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
gl_Position = uProjection * uView * uModel * vec4(aPosition, 1.0);
|
||||||
|
|
||||||
|
vColor = vec4(aColor, 1.0f);
|
||||||
|
}
|
||||||
9
resources/skybox/skybox.frag
Normal file
9
resources/skybox/skybox.frag
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
#version 330
|
||||||
|
uniform samplerCube uTexture;
|
||||||
|
|
||||||
|
varying vec3 vTexCoord;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
gl_FragColor = texture(uTexture, vTexCoord);
|
||||||
|
}
|
||||||
15
resources/skybox/skybox.vert
Normal file
15
resources/skybox/skybox.vert
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
#version 330
|
||||||
|
layout(location = 0) in vec3 aPosition;
|
||||||
|
|
||||||
|
out vec3 vTexCoord;
|
||||||
|
|
||||||
|
uniform mat4 uProjectionMatrix;
|
||||||
|
uniform mat4 uViewMatrix;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
// Strip translation column from camera's 4x4 matrix
|
||||||
|
mat4 view = mat4(mat3(uViewMatrix));
|
||||||
|
gl_Position = uProjectionMatrix * view * vec4(aPosition, 1.0);
|
||||||
|
vTexCoord = aPosition;
|
||||||
|
}
|
||||||
@@ -13,22 +13,22 @@ install(
|
|||||||
FILES
|
FILES
|
||||||
"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake"
|
"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake"
|
||||||
"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake"
|
"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake"
|
||||||
COMPONENT qtk
|
COMPONENT qtk_library
|
||||||
DESTINATION lib/cmake/${PROJECT_NAME}
|
DESTINATION lib/cmake/${PROJECT_NAME}
|
||||||
)
|
)
|
||||||
install(
|
install(
|
||||||
EXPORT qtk_export
|
EXPORT qtk_export
|
||||||
FILE ${PROJECT_NAME}Targets.cmake
|
FILE ${PROJECT_NAME}Targets.cmake
|
||||||
NAMESPACE ${PROJECT_NAME}::
|
NAMESPACE ${PROJECT_NAME}::
|
||||||
COMPONENT qtk
|
COMPONENT qtk_library
|
||||||
DESTINATION lib/cmake/${PROJECT_NAME}
|
DESTINATION lib/cmake/${PROJECT_NAME}
|
||||||
)
|
)
|
||||||
# System install for libqtk
|
# System install for qtk_library
|
||||||
install(
|
install(
|
||||||
TARGETS qtk
|
TARGETS qtk_library
|
||||||
# Associate libqtk target with qtk-export
|
# Associate qtk_library target with qtk-export
|
||||||
EXPORT qtk_export
|
EXPORT qtk_export
|
||||||
COMPONENT qtk
|
COMPONENT qtk_library
|
||||||
FILE_SET HEADERS DESTINATION include
|
FILE_SET HEADERS DESTINATION include
|
||||||
INCLUDES DESTINATION include
|
INCLUDES DESTINATION include
|
||||||
LIBRARY DESTINATION lib
|
LIBRARY DESTINATION lib
|
||||||
|
|||||||
@@ -17,8 +17,6 @@ if (QTK_GUI_SCENE)
|
|||||||
qtkscene.cpp qtkscene.h
|
qtkscene.cpp qtkscene.h
|
||||||
main.cpp
|
main.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
qt6_add_big_resources(QTK_GUI_SOURCES "${QTK_RESOURCES}/resources.qrc")
|
|
||||||
else()
|
else()
|
||||||
# The scene will use a default skybox with no models or examples.
|
# The scene will use a default skybox with no models or examples.
|
||||||
# Models can be added by click-and-dragging an .obj into the scene.
|
# Models can be added by click-and-dragging an .obj into the scene.
|
||||||
@@ -26,18 +24,13 @@ else()
|
|||||||
qtkmainwindow.cpp qtkmainwindow.h qtkmainwindow.ui
|
qtkmainwindow.cpp qtkmainwindow.h qtkmainwindow.ui
|
||||||
main.cpp
|
main.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
qt6_add_big_resources(
|
|
||||||
QTK_GUI_SOURCES
|
|
||||||
"${QTK_RESOURCES}/minimal_resources.qrc"
|
|
||||||
)
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
qt_add_executable(qtk_gui ${QTK_GUI_SOURCES})
|
qt_add_executable(qtk_gui ${QTK_GUI_SOURCES})
|
||||||
target_link_libraries(qtk_gui PRIVATE qtk_plugin_library)
|
target_link_libraries(qtk_gui PRIVATE qtk_plugin_library)
|
||||||
|
|
||||||
if (QTK_GUI_SCENE)
|
if (QTK_GUI_SCENE)
|
||||||
target_compile_definitions(qtk_gui PRIVATE -DQTK_GUI_SCENE)
|
target_compile_definitions(qtk_gui PUBLIC QTK_GUI_SCENE)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (WIN32)
|
if (WIN32)
|
||||||
@@ -56,25 +49,21 @@ elseif(APPLE)
|
|||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# qt_generate_deploy_app_script supports Windows and OSX in QtCore >= 6.3.
|
install(
|
||||||
# qt_generate_deploy_app_script is supported on Linux in QtCore >= 6.5.0.
|
TARGETS qtk_gui
|
||||||
if((Qt6_VERSION VERSION_GREATER_EQUAL "6.3.0" AND (WIN32 OR APPLE))
|
COMPONENT qtk_gui
|
||||||
OR Qt6_VERSION VERSION_GREATER_EQUAL "6.5.0")
|
BUNDLE DESTINATION .
|
||||||
install(
|
LIBRARY DESTINATION lib
|
||||||
TARGETS qtk_gui
|
ARCHIVE DESTINATION lib
|
||||||
COMPONENT qtk_gui
|
RUNTIME DESTINATION bin
|
||||||
BUNDLE DESTINATION .
|
)
|
||||||
LIBRARY DESTINATION lib
|
|
||||||
ARCHIVE DESTINATION lib
|
qt_generate_deploy_app_script(
|
||||||
RUNTIME DESTINATION bin
|
TARGET qtk_gui
|
||||||
)
|
OUTPUT_SCRIPT QTK_DEPLOY_SCRIPT
|
||||||
qt_generate_deploy_app_script(
|
NO_UNSUPPORTED_PLATFORM_ERROR
|
||||||
TARGET qtk_gui
|
)
|
||||||
OUTPUT_SCRIPT QTK_DEPLOY_SCRIPT
|
install(SCRIPT ${QTK_DEPLOY_SCRIPT} COMPONENT qtk_gui)
|
||||||
NO_UNSUPPORTED_PLATFORM_ERROR
|
|
||||||
)
|
|
||||||
install(SCRIPT ${QTK_DEPLOY_SCRIPT} COMPONENT qtk_gui)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
if(MSVC AND TARGET Qt6::qmake)
|
if(MSVC AND TARGET Qt6::qmake)
|
||||||
|
|||||||
@@ -9,10 +9,12 @@
|
|||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
|
|
||||||
#include "qtkmainwindow.h"
|
#include "qtkmainwindow.h"
|
||||||
|
#include "qtkscene.h"
|
||||||
|
|
||||||
int main(int argc, char * argv[])
|
int main(int argc, char * argv[])
|
||||||
{
|
{
|
||||||
initResources();
|
Q_INIT_RESOURCE(resources);
|
||||||
|
|
||||||
QApplication a(argc, argv);
|
QApplication a(argc, argv);
|
||||||
|
|
||||||
auto window = MainWindow::getMainWindow();
|
auto window = MainWindow::getMainWindow();
|
||||||
@@ -22,7 +24,11 @@ int main(int argc, char * argv[])
|
|||||||
// NOTE: We set the scene here and not in QtkMainWindow to detach the scene
|
// NOTE: We set the scene here and not in QtkMainWindow to detach the scene
|
||||||
// from the QtkWidget plugin (qtk_plugin_library build target).
|
// from the QtkWidget plugin (qtk_plugin_library build target).
|
||||||
// Once we can save / load scenes, this call, and QtkScene, can be removed.
|
// Once we can save / load scenes, this call, and QtkScene, can be removed.
|
||||||
window->setScene(new AppScene);
|
#ifdef QTK_GUI_SCENE
|
||||||
|
window->setScene(new QtkScene);
|
||||||
|
#else
|
||||||
|
window->setScene(new EmptyScene);
|
||||||
|
#endif
|
||||||
|
|
||||||
window->show();
|
window->show();
|
||||||
|
|
||||||
|
|||||||
@@ -48,16 +48,6 @@ MainWindow::MainWindow(QWidget * parent) : QMainWindow(parent)
|
|||||||
&Qtk::ToolBox::updateFocus);
|
&Qtk::ToolBox::updateFocus);
|
||||||
}
|
}
|
||||||
|
|
||||||
connect(ui_->actionDelete_Object,
|
|
||||||
&QAction::triggered,
|
|
||||||
this,
|
|
||||||
&MainWindow::deleteObject);
|
|
||||||
|
|
||||||
connect(ui_->actionLoad_Model,
|
|
||||||
&QAction::triggered,
|
|
||||||
this,
|
|
||||||
&MainWindow::loadObject);
|
|
||||||
|
|
||||||
// TODO: Fix / use MainWindow in Qt Designer to add these dock widgets.
|
// TODO: Fix / use MainWindow in Qt Designer to add these dock widgets.
|
||||||
// For now we will add them manually, but we should be able to do this in the
|
// For now we will add them manually, but we should be able to do this in the
|
||||||
// designer. At the moment if you edit the UI in designer the dock widget
|
// designer. At the moment if you edit the UI in designer the dock widget
|
||||||
@@ -72,7 +62,7 @@ MainWindow::MainWindow(QWidget * parent) : QMainWindow(parent)
|
|||||||
ui_->menuView->addAction(ui_->qtk__TreeView->toggleViewAction());
|
ui_->menuView->addAction(ui_->qtk__TreeView->toggleViewAction());
|
||||||
|
|
||||||
// Set the window icon used for Qtk.
|
// Set the window icon used for Qtk.
|
||||||
setWindowIcon(getIcon());
|
setWindowIcon(Qtk::getIcon());
|
||||||
}
|
}
|
||||||
|
|
||||||
MainWindow::~MainWindow()
|
MainWindow::~MainWindow()
|
||||||
@@ -114,26 +104,6 @@ void MainWindow::refreshScene(const QString & sceneName)
|
|||||||
ui_->qtk__TreeView->updateView(getQtkWidget()->getScene());
|
ui_->qtk__TreeView->updateView(getQtkWidget()->getScene());
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::deleteObject()
|
|
||||||
{
|
|
||||||
if (auto object = ui_->qtk__ToolBox->getObjectFocus(); object != Q_NULLPTR) {
|
|
||||||
auto scene = getQtkWidget()->getScene();
|
|
||||||
switch (object->getType()) {
|
|
||||||
case Qtk::Object::Type::QTK_MESH:
|
|
||||||
scene->removeObject(dynamic_cast<Qtk::MeshRenderer *>(object));
|
|
||||||
ui_->qtk__ToolBox->clearFocus();
|
|
||||||
break;
|
|
||||||
case Qtk::Object::Type::QTK_MODEL:
|
|
||||||
scene->removeObject(dynamic_cast<Qtk::Model *>(object));
|
|
||||||
ui_->qtk__ToolBox->clearFocus();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
qDebug() << "Failed to delete model with invalid type";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void MainWindow::setScene(Qtk::Scene * scene)
|
void MainWindow::setScene(Qtk::Scene * scene)
|
||||||
{
|
{
|
||||||
connect(scene,
|
connect(scene,
|
||||||
|
|||||||
@@ -11,8 +11,8 @@
|
|||||||
|
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|
||||||
#include <QFileDialog>
|
|
||||||
#include <QMainWindow>
|
#include <QMainWindow>
|
||||||
|
#include <QPlainTextEdit>
|
||||||
|
|
||||||
#include "designer-plugins/debugconsole.h"
|
#include "designer-plugins/debugconsole.h"
|
||||||
|
|
||||||
@@ -62,27 +62,6 @@ class EmptyScene : public Qtk::Scene
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
|
||||||
* Conditionally include the QtkScene header if the example is enabled.
|
|
||||||
* Set AppScene type to use in main() for creating the scene.
|
|
||||||
* Define helper function to initialize Qt resources for the application.
|
|
||||||
* These resources are different based on if the example is enabled.
|
|
||||||
*/
|
|
||||||
#ifdef QTK_GUI_SCENE
|
|
||||||
#include "qtkscene.h"
|
|
||||||
using AppScene = QtkScene;
|
|
||||||
inline void initResources()
|
|
||||||
{
|
|
||||||
Q_INIT_RESOURCE(resources);
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
using AppScene = EmptyScene;
|
|
||||||
inline void initResources()
|
|
||||||
{
|
|
||||||
Q_INIT_RESOURCE(minimal_resources);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* MainWindow class to provide an example of using a QtkWidget within a Qt
|
* MainWindow class to provide an example of using a QtkWidget within a Qt
|
||||||
* window application.
|
* window application.
|
||||||
@@ -132,11 +111,6 @@ class MainWindow : public QMainWindow
|
|||||||
*/
|
*/
|
||||||
void setScene(Qtk::Scene * scene);
|
void setScene(Qtk::Scene * scene);
|
||||||
|
|
||||||
/**
|
|
||||||
* @return Default icon to use for Qtk desktop application.
|
|
||||||
*/
|
|
||||||
static QIcon getIcon() { return QIcon(":/icons/icon.png"); }
|
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
/**
|
/**
|
||||||
* Trigger a refresh for widgets related to a scene that has been updated.
|
* Trigger a refresh for widgets related to a scene that has been updated.
|
||||||
@@ -144,23 +118,6 @@ class MainWindow : public QMainWindow
|
|||||||
*/
|
*/
|
||||||
void refreshScene(const QString & sceneName);
|
void refreshScene(const QString & sceneName);
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Opens a QFileDialog for selecting an object file to load into the scene.
|
|
||||||
*/
|
|
||||||
void loadObject()
|
|
||||||
{
|
|
||||||
const QUrl file = QFileDialog::getOpenFileName(
|
|
||||||
this, tr("Load Model"), QDir::homePath(), tr("Object Files (*.obj)"));
|
|
||||||
getQtkWidget()->getScene()->loadModel(file.fileName().replace(".obj", ""),
|
|
||||||
file.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Deletes the currently selected object from the scene.
|
|
||||||
*/
|
|
||||||
void deleteObject();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/***************************************************************************
|
/***************************************************************************
|
||||||
* Private Members
|
* Private Members
|
||||||
|
|||||||
@@ -222,9 +222,6 @@
|
|||||||
</property>
|
</property>
|
||||||
</action>
|
</action>
|
||||||
<action name="actionLoad_Model">
|
<action name="actionLoad_Model">
|
||||||
<property name="enabled">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
<property name="icon">
|
<property name="icon">
|
||||||
<iconset>
|
<iconset>
|
||||||
<normaloff>:/icons/fontawesome-free-6.2.1-desktop/svgs/solid/cube.svg</normaloff>:/icons/fontawesome-free-6.2.1-desktop/svgs/solid/cube.svg</iconset>
|
<normaloff>:/icons/fontawesome-free-6.2.1-desktop/svgs/solid/cube.svg</normaloff>:/icons/fontawesome-free-6.2.1-desktop/svgs/solid/cube.svg</iconset>
|
||||||
@@ -237,9 +234,6 @@
|
|||||||
</property>
|
</property>
|
||||||
</action>
|
</action>
|
||||||
<action name="actionDelete_Object">
|
<action name="actionDelete_Object">
|
||||||
<property name="enabled">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
<property name="icon">
|
<property name="icon">
|
||||||
<iconset>
|
<iconset>
|
||||||
<normaloff>:/icons/fontawesome-free-6.2.1-desktop/svgs/regular/trash-can.svg</normaloff>:/icons/fontawesome-free-6.2.1-desktop/svgs/regular/trash-can.svg</iconset>
|
<normaloff>:/icons/fontawesome-free-6.2.1-desktop/svgs/regular/trash-can.svg</normaloff>:/icons/fontawesome-free-6.2.1-desktop/svgs/regular/trash-can.svg</iconset>
|
||||||
|
|||||||
@@ -42,8 +42,8 @@ void QtkScene::init()
|
|||||||
// Clone qtk-resources if it doesn't already exist.
|
// Clone qtk-resources if it doesn't already exist.
|
||||||
QDir repoDir("resources/");
|
QDir repoDir("resources/");
|
||||||
if (!repoDir.exists()) {
|
if (!repoDir.exists()) {
|
||||||
qDebug() << "Cloning qtk-resources repository to " << repoDir.absolutePath()
|
qDebug() << "Cloning qtk-resources repository to '"
|
||||||
<< "...";
|
<< repoDir.absolutePath() << "'...";
|
||||||
|
|
||||||
// Run git clone
|
// Run git clone
|
||||||
QProcess gitProcess;
|
QProcess gitProcess;
|
||||||
@@ -426,7 +426,6 @@ void QtkScene::draw()
|
|||||||
// WARNING: We must call the base class draw() function first.
|
// WARNING: We must call the base class draw() function first.
|
||||||
// + This will handle rendering core scene components like the Skybox.
|
// + This will handle rendering core scene components like the Skybox.
|
||||||
Scene::draw();
|
Scene::draw();
|
||||||
const QVector3D cameraPosition = getCamera().getTransform().getTranslation();
|
|
||||||
|
|
||||||
mTestPhong->bindShaders();
|
mTestPhong->bindShaders();
|
||||||
mTestPhong->setUniform("uModelInverseTransposed",
|
mTestPhong->setUniform("uModelInverseTransposed",
|
||||||
@@ -434,12 +433,14 @@ void QtkScene::draw()
|
|||||||
mTestPhong->setUniform(
|
mTestPhong->setUniform(
|
||||||
"uLightPosition",
|
"uLightPosition",
|
||||||
MeshRenderer::getInstance("phongLight")->getTransform().getTranslation());
|
MeshRenderer::getInstance("phongLight")->getTransform().getTranslation());
|
||||||
mTestPhong->setUniform("uCameraPosition", cameraPosition);
|
mTestPhong->setUniform("uCameraPosition",
|
||||||
|
QtkScene::getCamera().getTransform().getTranslation());
|
||||||
mTestPhong->releaseShaders();
|
mTestPhong->releaseShaders();
|
||||||
mTestPhong->draw();
|
mTestPhong->draw();
|
||||||
|
|
||||||
mTestAmbient->bindShaders();
|
mTestAmbient->bindShaders();
|
||||||
mTestAmbient->setUniform("uCameraPosition", cameraPosition);
|
mTestAmbient->setUniform(
|
||||||
|
"uCameraPosition", QtkScene::getCamera().getTransform().getTranslation());
|
||||||
mTestAmbient->releaseShaders();
|
mTestAmbient->releaseShaders();
|
||||||
mTestAmbient->draw();
|
mTestAmbient->draw();
|
||||||
|
|
||||||
@@ -451,7 +452,8 @@ void QtkScene::draw()
|
|||||||
MeshRenderer::getInstance("diffuseLight")
|
MeshRenderer::getInstance("diffuseLight")
|
||||||
->getTransform()
|
->getTransform()
|
||||||
.getTranslation());
|
.getTranslation());
|
||||||
mTestDiffuse->setUniform("uCameraPosition", cameraPosition);
|
mTestDiffuse->setUniform(
|
||||||
|
"uCameraPosition", QtkScene::getCamera().getTransform().getTranslation());
|
||||||
mTestDiffuse->releaseShaders();
|
mTestDiffuse->releaseShaders();
|
||||||
mTestDiffuse->draw();
|
mTestDiffuse->draw();
|
||||||
|
|
||||||
@@ -463,70 +465,67 @@ void QtkScene::draw()
|
|||||||
MeshRenderer::getInstance("specularLight")
|
MeshRenderer::getInstance("specularLight")
|
||||||
->getTransform()
|
->getTransform()
|
||||||
.getTranslation());
|
.getTranslation());
|
||||||
mTestSpecular->setUniform("uCameraPosition", cameraPosition);
|
mTestSpecular->setUniform(
|
||||||
|
"uCameraPosition", QtkScene::getCamera().getTransform().getTranslation());
|
||||||
mTestSpecular->releaseShaders();
|
mTestSpecular->releaseShaders();
|
||||||
mTestSpecular->draw();
|
mTestSpecular->draw();
|
||||||
}
|
}
|
||||||
|
|
||||||
void QtkScene::update()
|
void QtkScene::update()
|
||||||
{
|
{
|
||||||
auto getModel = Model::getInstance;
|
auto mySpartan = Model::getInstance("My spartan");
|
||||||
const QVector3D cameraPosition = getCamera().getTransform().getTranslation();
|
mySpartan->getTransform().rotate(0.75f, 0.0f, 1.0f, 0.0f);
|
||||||
|
|
||||||
// Models may have failed to load, so we should check before accessing.
|
auto myCube = MeshRenderer::getInstance("My cube");
|
||||||
if (auto mySpartan = getModel("My spartan"); mySpartan) {
|
myCube->getTransform().rotate(-0.75f, 0.0f, 1.0f, 0.0f);
|
||||||
mySpartan->getTransform().rotate(0.75f, 0.0f, 1.0f, 0.0f);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (auto myCube = getModel("My cube"); myCube) {
|
auto position = MeshRenderer::getInstance("alienTestLight")
|
||||||
myCube->getTransform().rotate(-0.75f, 0.0f, 1.0f, 0.0f);
|
->getTransform()
|
||||||
}
|
.getTranslation();
|
||||||
|
auto alien = Model::getInstance("alienTest");
|
||||||
|
alien->setUniform("uLight.position", position);
|
||||||
|
alien->setUniform("uCameraPosition",
|
||||||
|
QtkScene::getCamera().getTransform().getTranslation());
|
||||||
|
auto posMatrix = alien->getTransform().toMatrix();
|
||||||
|
alien->setUniform("uMVP.normalMatrix", posMatrix.normalMatrix());
|
||||||
|
alien->setUniform("uMVP.model", posMatrix);
|
||||||
|
alien->setUniform("uMVP.view", QtkScene::getCamera().toMatrix());
|
||||||
|
alien->setUniform("uMVP.projection", QtkScene::getProjectionMatrix());
|
||||||
|
alien->getTransform().rotate(0.75f, 0.0f, 1.0f, 0.0f);
|
||||||
|
|
||||||
QMatrix4x4 posMatrix;
|
position = MeshRenderer::getInstance("spartanTestLight")
|
||||||
if (auto alien = getModel("alienTest"); alien) {
|
->getTransform()
|
||||||
alien->setLightPosition("alienTestLight");
|
.getTranslation();
|
||||||
|
auto spartan = Model::getInstance("spartanTest");
|
||||||
|
spartan->setUniform("uLight.position", position);
|
||||||
|
spartan->setUniform("uCameraPosition",
|
||||||
|
QtkScene::getCamera().getTransform().getTranslation());
|
||||||
|
posMatrix = spartan->getTransform().toMatrix();
|
||||||
|
spartan->setUniform("uMVP.normalMatrix", posMatrix.normalMatrix());
|
||||||
|
spartan->setUniform("uMVP.model", posMatrix);
|
||||||
|
spartan->setUniform("uMVP.view", QtkScene::getCamera().toMatrix());
|
||||||
|
spartan->setUniform("uMVP.projection", QtkScene::getProjectionMatrix());
|
||||||
|
spartan->getTransform().rotate(0.75f, 0.0f, 1.0f, 0.0f);
|
||||||
|
|
||||||
alien->setUniform("uCameraPosition", cameraPosition);
|
auto phong = MeshRenderer::getInstance("testPhong");
|
||||||
posMatrix = alien->getTransform().toMatrix();
|
phong->getTransform().rotate(0.75f, 1.0f, 0.5f, 0.0f);
|
||||||
alien->setUniform("uMVP.normalMatrix", posMatrix.normalMatrix());
|
phong->bindShaders();
|
||||||
alien->setUniform("uMVP.model", posMatrix);
|
position =
|
||||||
alien->setUniform("uMVP.view", QtkScene::getCamera().toMatrix());
|
MeshRenderer::getInstance("testLight")->getTransform().getTranslation();
|
||||||
alien->setUniform("uMVP.projection", QtkScene::getProjectionMatrix());
|
phong->setUniform("uLight.position", position);
|
||||||
alien->getTransform().rotate(0.75f, 0.0f, 1.0f, 0.0f);
|
phong->setUniform("uCameraPosition",
|
||||||
}
|
QtkScene::getCamera().getTransform().getTranslation());
|
||||||
|
posMatrix = phong->getTransform().toMatrix();
|
||||||
if (auto spartan = getModel("spartanTest"); spartan) {
|
phong->setUniform("uMVP.normalMatrix", posMatrix.normalMatrix());
|
||||||
spartan->setLightPosition("spartanTestLight");
|
phong->setUniform("uMVP.model", posMatrix);
|
||||||
|
phong->setUniform("uMVP.view", QtkScene::getCamera().toMatrix());
|
||||||
spartan->setUniform("uCameraPosition", cameraPosition);
|
phong->setUniform("uMVP.projection", QtkScene::getProjectionMatrix());
|
||||||
posMatrix = spartan->getTransform().toMatrix();
|
phong->releaseShaders();
|
||||||
spartan->setUniform("uMVP.normalMatrix", posMatrix.normalMatrix());
|
|
||||||
spartan->setUniform("uMVP.model", posMatrix);
|
|
||||||
spartan->setUniform("uMVP.view", QtkScene::getCamera().toMatrix());
|
|
||||||
spartan->setUniform("uMVP.projection", QtkScene::getProjectionMatrix());
|
|
||||||
spartan->getTransform().rotate(0.75f, 0.0f, 1.0f, 0.0f);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (auto phong = getModel("testPhong"); phong) {
|
|
||||||
phong->setLightPosition("testLight");
|
|
||||||
|
|
||||||
phong->getTransform().rotate(0.75f, 1.0f, 0.5f, 0.0f);
|
|
||||||
phong->bindShaders();
|
|
||||||
phong->setUniform("uCameraPosition", cameraPosition);
|
|
||||||
posMatrix = phong->getTransform().toMatrix();
|
|
||||||
phong->setUniform("uMVP.normalMatrix", posMatrix.normalMatrix());
|
|
||||||
phong->setUniform("uMVP.model", posMatrix);
|
|
||||||
phong->setUniform("uMVP.view", QtkScene::getCamera().toMatrix());
|
|
||||||
phong->setUniform("uMVP.projection", QtkScene::getProjectionMatrix());
|
|
||||||
phong->releaseShaders();
|
|
||||||
}
|
|
||||||
|
|
||||||
// MeshRenderers are lower level opengl objects baked into the source code.
|
|
||||||
auto getMesh = MeshRenderer::getInstance;
|
|
||||||
|
|
||||||
// Rotate lighting example cubes
|
// Rotate lighting example cubes
|
||||||
mTestPhong->getTransform().rotate(0.75f, 0.5f, 0.3f, 0.2f);
|
mTestPhong->getTransform().rotate(0.75f, 0.5f, 0.3f, 0.2f);
|
||||||
getMesh("noLight")->getTransform().rotate(0.75f, 0.5f, 0.3f, 0.2f);
|
MeshRenderer::getInstance("noLight")->getTransform().rotate(
|
||||||
|
0.75f, 0.5f, 0.3f, 0.2f);
|
||||||
mTestAmbient->getTransform().rotate(0.75f, 0.5f, 0.3f, 0.2f);
|
mTestAmbient->getTransform().rotate(0.75f, 0.5f, 0.3f, 0.2f);
|
||||||
mTestDiffuse->getTransform().rotate(0.75f, 0.5f, 0.3f, 0.2f);
|
mTestDiffuse->getTransform().rotate(0.75f, 0.5f, 0.3f, 0.2f);
|
||||||
mTestSpecular->getTransform().rotate(0.75f, 0.5f, 0.3f, 0.2f);
|
mTestSpecular->getTransform().rotate(0.75f, 0.5f, 0.3f, 0.2f);
|
||||||
@@ -534,27 +533,46 @@ void QtkScene::update()
|
|||||||
// Examples of various translations and rotations
|
// Examples of various translations and rotations
|
||||||
|
|
||||||
// Rotate in multiple directions simultaneously
|
// Rotate in multiple directions simultaneously
|
||||||
getMesh("rgbNormalsCube")->getTransform().rotate(0.75f, 0.2f, 0.4f, 0.6f);
|
MeshRenderer::getInstance("rgbNormalsCube")
|
||||||
|
->getTransform()
|
||||||
|
.rotate(0.75f, 0.2f, 0.4f, 0.6f);
|
||||||
|
|
||||||
// Pitch forward and roll sideways
|
// Pitch forward and roll sideways
|
||||||
getMesh("leftTriangle")->getTransform().rotate(0.75f, 1.0f, 0.0f, 0.0f);
|
MeshRenderer::getInstance("leftTriangle")
|
||||||
getMesh("rightTriangle")->getTransform().rotate(0.75f, 0.0f, 0.0f, 1.0f);
|
->getTransform()
|
||||||
|
.rotate(0.75f, 1.0f, 0.0f, 0.0f);
|
||||||
|
MeshRenderer::getInstance("rightTriangle")
|
||||||
|
->getTransform()
|
||||||
|
.rotate(0.75f, 0.0f, 0.0f, 1.0f);
|
||||||
|
|
||||||
// Move between two positions over time
|
// Move between two positions over time
|
||||||
static float translateX = 0.025f;
|
static float translateX = 0.025f;
|
||||||
float limit = -9.0f; // Origin position.x - 2.0f
|
float limit = -9.0f; // Origin position.x - 2.0f
|
||||||
float posX = getMesh("topTriangle")->getTransform().getTranslation().x();
|
float posX = MeshRenderer::getInstance("topTriangle")
|
||||||
|
->getTransform()
|
||||||
|
.getTranslation()
|
||||||
|
.x();
|
||||||
if (posX < limit || posX > limit + 4.0f) {
|
if (posX < limit || posX > limit + 4.0f) {
|
||||||
translateX = -translateX;
|
translateX = -translateX;
|
||||||
}
|
}
|
||||||
getMesh("topTriangle")->getTransform().translate(translateX, 0.0f, 0.0f);
|
MeshRenderer::getInstance("topTriangle")
|
||||||
getMesh("bottomTriangle")->getTransform().translate(-translateX, 0.0f, 0.0f);
|
->getTransform()
|
||||||
|
.translate(translateX, 0.0f, 0.0f);
|
||||||
|
MeshRenderer::getInstance("bottomTriangle")
|
||||||
|
->getTransform()
|
||||||
|
.translate(-translateX, 0.0f, 0.0f);
|
||||||
// And lets rotate the triangles in two directions at once
|
// And lets rotate the triangles in two directions at once
|
||||||
getMesh("topTriangle")->getTransform().rotate(0.75f, 0.2f, 0.0f, 0.4f);
|
MeshRenderer::getInstance("topTriangle")
|
||||||
getMesh("bottomTriangle")->getTransform().rotate(0.75f, 0.0f, 0.2f, 0.4f);
|
->getTransform()
|
||||||
|
.rotate(0.75f, 0.2f, 0.0f, 0.4f);
|
||||||
|
MeshRenderer::getInstance("bottomTriangle")
|
||||||
|
->getTransform()
|
||||||
|
.rotate(0.75f, 0.0f, 0.2f, 0.4f);
|
||||||
// And make the bottom triangle green, instead of RGB
|
// And make the bottom triangle green, instead of RGB
|
||||||
|
|
||||||
// Rotate center cube in several directions simultaneously
|
// Rotate center cube in several directions simultaneously
|
||||||
// + Not subject to gimbal lock since we are using quaternions :)
|
// + Not subject to gimbal lock since we are using quaternions :)
|
||||||
getMesh("centerCube")->getTransform().rotate(0.75f, 0.2f, 0.4f, 0.6f);
|
MeshRenderer::getInstance("centerCube")
|
||||||
|
->getTransform()
|
||||||
|
.rotate(0.75f, 0.2f, 0.4f, 0.6f);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ target_sources(
|
|||||||
"${QTK_PLUGIN_LIBRARY_SOURCES}"
|
"${QTK_PLUGIN_LIBRARY_SOURCES}"
|
||||||
"${QTK_PLUGIN_LIBRARY_HEADERS}"
|
"${QTK_PLUGIN_LIBRARY_HEADERS}"
|
||||||
)
|
)
|
||||||
target_link_libraries(qtk_plugin_library PUBLIC Qt6::UiPlugin qtk)
|
target_link_libraries(qtk_plugin_library PUBLIC Qt6::UiPlugin qtk_library)
|
||||||
|
|
||||||
################################################################################
|
################################################################################
|
||||||
# Qtk Widget Plugins
|
# Qtk Widget Plugins
|
||||||
@@ -47,7 +47,7 @@ target_link_libraries(qtk_plugins PUBLIC qtk_plugin_library)
|
|||||||
# Otherwise, we just use them for building the Qtk desktop application.
|
# Otherwise, we just use them for building the Qtk desktop application.
|
||||||
if(QTK_PLUGINS)
|
if(QTK_PLUGINS)
|
||||||
install(
|
install(
|
||||||
TARGETS qtk_plugins qtk qtk_plugin_library
|
TARGETS qtk_plugins qtk_library qtk_plugin_library
|
||||||
COMPONENT qtk_plugins
|
COMPONENT qtk_plugins
|
||||||
LIBRARY DESTINATION "${QTK_PLUGIN_INSTALL_DIR}"
|
LIBRARY DESTINATION "${QTK_PLUGIN_INSTALL_DIR}"
|
||||||
ARCHIVE DESTINATION "${QTK_PLUGIN_INSTALL_DIR}"
|
ARCHIVE DESTINATION "${QTK_PLUGIN_INSTALL_DIR}"
|
||||||
|
|||||||
@@ -98,18 +98,6 @@ void QtkWidget::initializeGL()
|
|||||||
this,
|
this,
|
||||||
SLOT(messageLogged(QOpenGLDebugMessage)));
|
SLOT(messageLogged(QOpenGLDebugMessage)));
|
||||||
mDebugLogger->startLogging();
|
mDebugLogger->startLogging();
|
||||||
mConsole->sendLog(
|
|
||||||
"Object files such as .obj can be dragged into the scene to load new "
|
|
||||||
"models.",
|
|
||||||
DebugContext::Warn);
|
|
||||||
mConsole->sendLog("Click and hold LMB or RMB to move the camera with WASD.",
|
|
||||||
DebugContext::Warn);
|
|
||||||
mConsole->sendLog(
|
|
||||||
"Click an object name in the side panel to view or modify properties.",
|
|
||||||
DebugContext::Warn);
|
|
||||||
mConsole->sendLog(
|
|
||||||
"Double click an object name to move the camera to it's position.",
|
|
||||||
DebugContext::Warn);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
printContextInformation();
|
printContextInformation();
|
||||||
|
|||||||
@@ -264,7 +264,6 @@ namespace Qtk
|
|||||||
<< "[QtkWidgetManager " << this
|
<< "[QtkWidgetManager " << this
|
||||||
<< " ] Failed to add a QtkWidget with the previously used name '"
|
<< " ] Failed to add a QtkWidget with the previously used name '"
|
||||||
<< name << "'";
|
<< name << "'";
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
qDebug() << this << " Adding new QtkWidget named '" << name << "'";
|
qDebug() << this << " Adding new QtkWidget named '" << name << "'";
|
||||||
mQtkWidgets[name] = widget;
|
mQtkWidgets[name] = widget;
|
||||||
|
|||||||
@@ -16,121 +16,20 @@
|
|||||||
|
|
||||||
using namespace Qtk;
|
using namespace Qtk;
|
||||||
|
|
||||||
ToolBox::ToolBox(QWidget * parent) :
|
ToolBox::ToolBox(QWidget * parent) : QDockWidget(parent), ui(new Ui::ToolBox)
|
||||||
QDockWidget(parent), objectDetails_(this), transformPanel_(this),
|
|
||||||
scalePanel_(this), vertex_(this, "Vertex Shader:"),
|
|
||||||
fragment_(this, "Fragment Shader:"), properiesForm_(new QFormLayout),
|
|
||||||
shaderForm_(new QFormLayout), ui(new Ui::ToolBox), objectFocus_(Q_NULLPTR)
|
|
||||||
{
|
{
|
||||||
ui->setupUi(this);
|
ui->setupUi(this);
|
||||||
setMinimumWidth(350);
|
setMinimumWidth(350);
|
||||||
|
|
||||||
// Object Properties.
|
|
||||||
ui->page_properties->setLayout(properiesForm_);
|
|
||||||
properiesForm_->addRow(objectDetails_.name.label, objectDetails_.name.value);
|
|
||||||
properiesForm_->addRow(objectDetails_.objectType.label,
|
|
||||||
objectDetails_.objectType.value);
|
|
||||||
properiesForm_->addRow(reinterpret_cast<QWidget *>(&transformPanel_));
|
|
||||||
properiesForm_->addRow(reinterpret_cast<QWidget *>(&scalePanel_));
|
|
||||||
ui->toolBox->setCurrentWidget(ui->page_properties);
|
|
||||||
|
|
||||||
// Shader views.
|
|
||||||
ui->page_shaders->setLayout(shaderForm_);
|
|
||||||
shaderForm_->addRow(reinterpret_cast<QWidget *>(&vertex_));
|
|
||||||
shaderForm_->addRow(reinterpret_cast<QWidget *>(&fragment_));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ToolBox::updateFocus(const QString & name)
|
void ToolBox::updateFocus(const QString & name)
|
||||||
{
|
{
|
||||||
auto object =
|
auto object =
|
||||||
QtkWidget::mWidgetManager.get_widget()->getScene()->getObject(name);
|
Qtk::QtkWidget::mWidgetManager.get_widget()->getScene()->getObject(name);
|
||||||
// If we can't find the object show a warning.
|
if (object != Q_NULLPTR) {
|
||||||
if (object == Q_NULLPTR) {
|
removePages();
|
||||||
qDebug() << "Failed to find selected object: " << name
|
createPageProperties(object);
|
||||||
<< "; Clearing object panels.";
|
createPageShader(object);
|
||||||
}
|
|
||||||
|
|
||||||
// We should still pass the nullptr here if we failed to find the object
|
|
||||||
// above.
|
|
||||||
objectFocus_ = object;
|
|
||||||
refreshProperties(object);
|
|
||||||
refreshShaders(object);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ToolBox::clearFocus()
|
|
||||||
{
|
|
||||||
objectFocus_ = Q_NULLPTR;
|
|
||||||
refreshProperties(objectFocus_);
|
|
||||||
refreshShaders(objectFocus_);
|
|
||||||
}
|
|
||||||
|
|
||||||
ToolBox::SpinBox3D::SpinBox3D(QWidget * parent, const char * l) :
|
|
||||||
QWidget(parent), layout(new QHBoxLayout(this)), label(new QLabel(tr(l)))
|
|
||||||
{
|
|
||||||
// The layout owns the widget and will clean it up on destruction.
|
|
||||||
layout->addWidget(label);
|
|
||||||
for (const auto & f : fields) {
|
|
||||||
layout->addWidget(f->spinBox);
|
|
||||||
f->spinBox->setMinimum(std::numeric_limits<double>::lowest());
|
|
||||||
f->spinBox->setSingleStep(0.1);
|
|
||||||
f->spinBox->setFixedWidth(75);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ToolBox::SpinBox::disconnect() const
|
|
||||||
{
|
|
||||||
Object::disconnect(connection);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ToolBox::TransformPanel::setObject(const Qtk::Object * object)
|
|
||||||
{
|
|
||||||
// Zero the panel contents if there is no object selected.
|
|
||||||
if (object == Q_NULLPTR) {
|
|
||||||
spinBox3D.clear();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Reconnect translation panel controls to the new object.
|
|
||||||
const std::vector binds = {&Object::setTranslationX,
|
|
||||||
&Object::setTranslationY,
|
|
||||||
&Object::setTranslationZ};
|
|
||||||
for (size_t i = 0; i < spinBox3D.fields.size(); i++) {
|
|
||||||
auto * f = spinBox3D.fields[i];
|
|
||||||
// Disconnect before changing spin box value.
|
|
||||||
f->disconnect();
|
|
||||||
|
|
||||||
// Set the values in the spin box to the object's current X,Y,Z
|
|
||||||
f->spinBox->setValue(object->getTransform().getTranslation()[i]);
|
|
||||||
|
|
||||||
// Reconnect to bind spin box value to the new object's position.
|
|
||||||
f->connection =
|
|
||||||
connect(f->spinBox, &QDoubleSpinBox::valueChanged, object, binds[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ToolBox::ScalePanel::setObject(const Qtk::Object * object)
|
|
||||||
{
|
|
||||||
// Zero the panel contents if there is no object selected.
|
|
||||||
if (object == Q_NULLPTR) {
|
|
||||||
spinBox3D.clear();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reconnect scale panel controls to the new object.
|
|
||||||
const std::vector binds = {
|
|
||||||
&Object::setScaleX, &Object::setScaleY, &Object::setScaleZ};
|
|
||||||
for (size_t i = 0; i < spinBox3D.fields.size(); i++) {
|
|
||||||
auto * f = spinBox3D.fields[i];
|
|
||||||
// Disconnect before changing spin box value.
|
|
||||||
f->disconnect();
|
|
||||||
|
|
||||||
// Set the values in the spin box to the object's current X,Y,Z
|
|
||||||
f->spinBox->setValue(object->getTransform().getScale()[i]);
|
|
||||||
|
|
||||||
// Reconnect to bind spin box value to the new object's scale.
|
|
||||||
f->connection =
|
|
||||||
connect(f->spinBox, &QDoubleSpinBox::valueChanged, object, binds[i]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -139,32 +38,120 @@ ToolBox::~ToolBox()
|
|||||||
delete ui;
|
delete ui;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ToolBox::refreshProperties(const Object * object)
|
void ToolBox::removePages()
|
||||||
{
|
{
|
||||||
// Refresh to show the new object's details.
|
// Remove all existing pages.
|
||||||
objectDetails_.setObject(object);
|
for (size_t i = 0; i < ui->toolBox->count(); i++) {
|
||||||
// Reconnect transform panel controls to the new object.
|
delete ui->toolBox->widget(i);
|
||||||
transformPanel_.setObject(object);
|
ui->toolBox->removeItem(i);
|
||||||
scalePanel_.setObject(object);
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ToolBox::refreshShaders(const Object * object)
|
void ToolBox::createPageProperties(const Object * object)
|
||||||
{
|
{
|
||||||
// Zero the panel contents if there is no object selected.
|
auto transform = object->getTransform();
|
||||||
if (object == Q_NULLPTR) {
|
auto type = object->getType();
|
||||||
vertex_.clear();
|
auto * widget = new QWidget;
|
||||||
fragment_.clear();
|
ui->toolBox->addItem(widget, "Properties");
|
||||||
return;
|
ui->toolBox->setCurrentWidget(widget);
|
||||||
|
|
||||||
|
auto * layout = new QFormLayout;
|
||||||
|
layout->addRow(new QLabel(tr("Name:")),
|
||||||
|
new QLabel(object->getName().c_str()));
|
||||||
|
|
||||||
|
layout->addRow(new QLabel(tr("Type:")),
|
||||||
|
new QLabel(type == Object::Type::QTK_MESH ? "Mesh" : "Model"));
|
||||||
|
|
||||||
|
auto rowLayout = new QHBoxLayout;
|
||||||
|
rowLayout->addWidget(new QLabel(tr("Translation:")));
|
||||||
|
int minWidth = 75;
|
||||||
|
for (size_t i = 0; i < 3; i++) {
|
||||||
|
auto spinBox = new QDoubleSpinBox;
|
||||||
|
spinBox->setMinimum(std::numeric_limits<double>::lowest());
|
||||||
|
spinBox->setSingleStep(0.1);
|
||||||
|
spinBox->setValue(transform.getTranslation()[i]);
|
||||||
|
spinBox->setFixedWidth(minWidth);
|
||||||
|
rowLayout->addWidget(spinBox);
|
||||||
|
|
||||||
|
if (i == 0) {
|
||||||
|
connect(spinBox,
|
||||||
|
&QDoubleSpinBox::valueChanged,
|
||||||
|
object,
|
||||||
|
&Object::setTranslationX);
|
||||||
|
} else if (i == 1) {
|
||||||
|
connect(spinBox,
|
||||||
|
&QDoubleSpinBox::valueChanged,
|
||||||
|
object,
|
||||||
|
&Object::setTranslationY);
|
||||||
|
} else if (i == 2) {
|
||||||
|
connect(spinBox,
|
||||||
|
&QDoubleSpinBox::valueChanged,
|
||||||
|
object,
|
||||||
|
&Object::setTranslationZ);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
layout->addRow(rowLayout);
|
||||||
|
|
||||||
|
rowLayout = new QHBoxLayout;
|
||||||
|
rowLayout->addWidget(new QLabel(tr("Scale:")));
|
||||||
|
for (size_t i = 0; i < 3; i++) {
|
||||||
|
auto spinBox = new QDoubleSpinBox;
|
||||||
|
spinBox->setMinimum(std::numeric_limits<double>::lowest());
|
||||||
|
spinBox->setSingleStep(0.1);
|
||||||
|
spinBox->setValue(transform.getScale()[i]);
|
||||||
|
spinBox->setFixedWidth(minWidth);
|
||||||
|
rowLayout->addWidget(spinBox);
|
||||||
|
|
||||||
|
if (i == 0) {
|
||||||
|
connect(
|
||||||
|
spinBox, &QDoubleSpinBox::valueChanged, object, &Object::setScaleX);
|
||||||
|
} else if (i == 1) {
|
||||||
|
connect(
|
||||||
|
spinBox, &QDoubleSpinBox::valueChanged, object, &Object::setScaleY);
|
||||||
|
} else if (i == 2) {
|
||||||
|
connect(
|
||||||
|
spinBox, &QDoubleSpinBox::valueChanged, object, &Object::setScaleZ);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
layout->addRow(rowLayout);
|
||||||
|
widget->setLayout(layout);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ToolBox::createPageShader(const Object * object)
|
||||||
|
{
|
||||||
|
// Shaders page.
|
||||||
|
auto widget = new QWidget;
|
||||||
|
ui->toolBox->addItem(widget, "Shaders");
|
||||||
|
auto mainLayout = new QFormLayout;
|
||||||
|
auto rowLayout = new QHBoxLayout;
|
||||||
|
rowLayout->addWidget(new QLabel("Vertex Shader:"));
|
||||||
|
rowLayout->addWidget(new QLabel(object->getVertexShader().c_str()));
|
||||||
|
mainLayout->addRow(rowLayout);
|
||||||
|
|
||||||
|
auto shaderView = new QTextEdit;
|
||||||
|
shaderView->setReadOnly(true);
|
||||||
|
auto vertexFile = QFile(object->getVertexShader().c_str());
|
||||||
|
if (vertexFile.exists()) {
|
||||||
|
vertexFile.open(QIODeviceBase::ReadOnly);
|
||||||
|
shaderView->setText(vertexFile.readAll());
|
||||||
|
vertexFile.close();
|
||||||
|
mainLayout->addRow(shaderView);
|
||||||
}
|
}
|
||||||
|
|
||||||
vertex_.path.setValue(object->getVertexShader().c_str());
|
rowLayout = new QHBoxLayout;
|
||||||
vertex_.editor->setText(object->getVertexShaderSourceCode().c_str());
|
rowLayout->addWidget(new QLabel("Fragment Shader:"));
|
||||||
fragment_.path.setValue(object->getFragmentShader().c_str());
|
rowLayout->addWidget(new QLabel(object->getFragmentShader().c_str()));
|
||||||
fragment_.editor->setText(object->getFragmentShaderSourceCode().c_str());
|
mainLayout->addRow(rowLayout);
|
||||||
}
|
|
||||||
|
|
||||||
void ToolBox::refresh(const Object * object)
|
shaderView = new QTextEdit;
|
||||||
{
|
shaderView->setReadOnly(true);
|
||||||
refreshProperties(object);
|
auto fragmentfile = QFile(object->getFragmentShader().c_str());
|
||||||
refreshShaders(object);
|
if (fragmentfile.exists()) {
|
||||||
|
fragmentfile.open(QIODeviceBase::ReadOnly);
|
||||||
|
shaderView->setText(fragmentfile.readAll());
|
||||||
|
fragmentfile.close();
|
||||||
|
mainLayout->addRow(shaderView);
|
||||||
|
}
|
||||||
|
|
||||||
|
widget->setLayout(mainLayout);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,13 +13,10 @@
|
|||||||
#include <QDesignerExportWidget>
|
#include <QDesignerExportWidget>
|
||||||
#include <QDockWidget>
|
#include <QDockWidget>
|
||||||
#include <QDoubleSpinBox>
|
#include <QDoubleSpinBox>
|
||||||
#include <QFormLayout>
|
#include <QGroupBox>
|
||||||
#include <QHBoxLayout>
|
|
||||||
#include <QLabel>
|
|
||||||
#include <QTextEdit>
|
|
||||||
|
|
||||||
#include "qtk/object.h"
|
|
||||||
|
|
||||||
|
#include "qtk/scene.h"
|
||||||
|
|
||||||
namespace Ui
|
namespace Ui
|
||||||
{
|
{
|
||||||
@@ -28,7 +25,7 @@ namespace Ui
|
|||||||
|
|
||||||
namespace Qtk
|
namespace Qtk
|
||||||
{
|
{
|
||||||
class ToolBox final : public QDockWidget
|
class ToolBox : public QDockWidget
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
@@ -41,184 +38,20 @@ namespace Qtk
|
|||||||
|
|
||||||
~ToolBox();
|
~ToolBox();
|
||||||
|
|
||||||
void refreshProperties(const Object * object);
|
void removePages();
|
||||||
|
|
||||||
void refreshShaders(const Object * object);
|
void createPageProperties(const Object * object);
|
||||||
|
|
||||||
void refresh(const Object * object);
|
void createPageShader(const Object * object);
|
||||||
|
|
||||||
void updateFocus(const QString & name);
|
void updateFocus(const QString & name);
|
||||||
|
|
||||||
[[nodiscard]] Object * getObjectFocus() const { return objectFocus_; }
|
|
||||||
|
|
||||||
void clearFocus();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/*************************************************************************
|
/*************************************************************************
|
||||||
* Private Members
|
* Private Members
|
||||||
************************************************************************/
|
************************************************************************/
|
||||||
|
|
||||||
/// Displays details on the object.
|
|
||||||
struct ObjectDetails {
|
|
||||||
/// Single item containing a label and value.
|
|
||||||
struct Item {
|
|
||||||
explicit Item(QWidget * parent,
|
|
||||||
const char * l = "Item:",
|
|
||||||
const char * v = "") :
|
|
||||||
label(new QLabel(tr(l), parent)),
|
|
||||||
value(new QLabel(tr(v), parent))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void setValue(const char * v) const { value->setText(tr(v)); }
|
|
||||||
|
|
||||||
void setItem(const char * l, const char * v) const
|
|
||||||
{
|
|
||||||
label->setText(tr(l));
|
|
||||||
value->setText(tr(v));
|
|
||||||
}
|
|
||||||
|
|
||||||
QLabel * label;
|
|
||||||
QLabel * value;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// We pass the parent widget so that Qt handles releasing memory.
|
|
||||||
explicit ObjectDetails(QWidget * parent) :
|
|
||||||
name(parent, "Name:"), objectType(parent, "Object Type:")
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Refresh to display the new object's details
|
|
||||||
void setObject(const Qtk::Object * object) const
|
|
||||||
{
|
|
||||||
// Zero contents if there is no object selected.
|
|
||||||
if (object == Q_NULLPTR) {
|
|
||||||
name.setValue("");
|
|
||||||
objectType.setValue("No object selected");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
name.setItem("Name:", object->getName().toStdString().c_str());
|
|
||||||
objectType.setItem(
|
|
||||||
"Type:",
|
|
||||||
object->getType() == Object::QTK_MESH ? "Mesh" : "Model");
|
|
||||||
}
|
|
||||||
|
|
||||||
Item name, objectType;
|
|
||||||
};
|
|
||||||
ObjectDetails objectDetails_;
|
|
||||||
|
|
||||||
/// Structure to associate a QSpinBox with a connection.
|
|
||||||
struct SpinBox {
|
|
||||||
/**
|
|
||||||
* Default constructor passes no parent to the QSpinBox.
|
|
||||||
* It must be added to a layout for Qt to clean up the resources.
|
|
||||||
*/
|
|
||||||
SpinBox() : spinBox(new QDoubleSpinBox) {}
|
|
||||||
|
|
||||||
/// Disconnect the associated connection.
|
|
||||||
void disconnect() const;
|
|
||||||
|
|
||||||
QDoubleSpinBox * spinBox;
|
|
||||||
QMetaObject::Connection connection;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Spinbox with 3 fields and a single label.
|
|
||||||
class SpinBox3D final : QWidget
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
/// We pass a parent to ensure Qt will clean up resources.
|
|
||||||
/// Assigning a QWidget to a QLayout also ensures Qt will clean up.
|
|
||||||
explicit SpinBox3D(QWidget * parent, const char * l = "SpinBox3D:");
|
|
||||||
|
|
||||||
/// Zero the SpinBox3D display.
|
|
||||||
void clear()
|
|
||||||
{
|
|
||||||
for (auto & field : fields) {
|
|
||||||
field->disconnect();
|
|
||||||
field->spinBox->setValue(0.0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The main layout for the SpinBox3D widget.
|
|
||||||
QHBoxLayout * layout;
|
|
||||||
|
|
||||||
/// Label for the SpinBox3D.
|
|
||||||
QLabel * label;
|
|
||||||
|
|
||||||
/// SpinBox and a connection for each field.
|
|
||||||
SpinBox x, y, z;
|
|
||||||
/// Array for iterating over fields.
|
|
||||||
std::array<SpinBox *, 3> fields {&x, &y, &z};
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Initialize the transform panel and configure QObject connections.
|
|
||||||
struct TransformPanel {
|
|
||||||
explicit TransformPanel(QWidget * parent) :
|
|
||||||
spinBox3D(parent, "Transform:")
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Reconnect QObject connections and spin box values in UI.
|
|
||||||
void setObject(const Qtk::Object * object);
|
|
||||||
|
|
||||||
SpinBox3D spinBox3D;
|
|
||||||
};
|
|
||||||
TransformPanel transformPanel_;
|
|
||||||
|
|
||||||
/// Initialize the scale panel and configure QObject connections.
|
|
||||||
struct ScalePanel {
|
|
||||||
explicit ScalePanel(QWidget * parent) : spinBox3D(parent, "Scale:") {}
|
|
||||||
|
|
||||||
/// Reconnect QObject connections and spin box values in UI.
|
|
||||||
void setObject(const Qtk::Object * object);
|
|
||||||
|
|
||||||
SpinBox3D spinBox3D;
|
|
||||||
};
|
|
||||||
ScalePanel scalePanel_;
|
|
||||||
|
|
||||||
/// Displays shader name, path, and read-only text view.
|
|
||||||
class ShaderView final : QWidget
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
explicit ShaderView(QWidget * parent,
|
|
||||||
const char * l = "ShaderView:") :
|
|
||||||
layout(new QVBoxLayout(this)), path(parent, l),
|
|
||||||
editor(new QTextEdit(parent))
|
|
||||||
{
|
|
||||||
// Create a child horizontal layout for shader name and file path.
|
|
||||||
auto * pathLayout = new QHBoxLayout;
|
|
||||||
pathLayout->addWidget(path.label);
|
|
||||||
pathLayout->addWidget(path.value);
|
|
||||||
layout->addLayout(pathLayout);
|
|
||||||
|
|
||||||
// Add the read-only text editor widget to the main layout.
|
|
||||||
editor->setReadOnly(true);
|
|
||||||
layout->addWidget(editor);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Zero the ShaderView display.
|
|
||||||
void clear() const
|
|
||||||
{
|
|
||||||
path.setValue("");
|
|
||||||
editor->setText("");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The main layout for the ShaderView widget.
|
|
||||||
QVBoxLayout * layout;
|
|
||||||
|
|
||||||
/// Shader name and path on disk.
|
|
||||||
ObjectDetails::Item path;
|
|
||||||
|
|
||||||
/// Read-only (for now) display of the shader source code.
|
|
||||||
QTextEdit * editor;
|
|
||||||
};
|
|
||||||
ShaderView vertex_, fragment_;
|
|
||||||
|
|
||||||
QFormLayout * properiesForm_;
|
|
||||||
QFormLayout * shaderForm_;
|
|
||||||
|
|
||||||
Object * objectFocus_ {};
|
|
||||||
|
|
||||||
Ui::ToolBox * ui;
|
Ui::ToolBox * ui;
|
||||||
};
|
};
|
||||||
} // namespace Qtk
|
} // namespace Qtk
|
||||||
|
|||||||
@@ -19,8 +19,6 @@ Qtk::TreeView::TreeView(QWidget * parent) :
|
|||||||
QDockWidget(parent), ui(new Ui::TreeView)
|
QDockWidget(parent), ui(new Ui::TreeView)
|
||||||
{
|
{
|
||||||
ui->setupUi(this);
|
ui->setupUi(this);
|
||||||
connect(
|
|
||||||
ui->treeWidget, &QTreeWidget::itemClicked, this, &TreeView::itemSelect);
|
|
||||||
connect(ui->treeWidget,
|
connect(ui->treeWidget,
|
||||||
&QTreeWidget::itemDoubleClicked,
|
&QTreeWidget::itemDoubleClicked,
|
||||||
this,
|
this,
|
||||||
@@ -43,7 +41,7 @@ void Qtk::TreeView::updateView(const Qtk::Scene * scene)
|
|||||||
mSceneName = scene->getSceneName();
|
mSceneName = scene->getSceneName();
|
||||||
auto objects = scene->getObjects();
|
auto objects = scene->getObjects();
|
||||||
for (const auto & object : objects) {
|
for (const auto & object : objects) {
|
||||||
QStringList list(QStringList(QString(object->getName())));
|
QStringList list(QStringList(QString(object->getName().c_str())));
|
||||||
ui->treeWidget->insertTopLevelItem(0, new QTreeWidgetItem(list));
|
ui->treeWidget->insertTopLevelItem(0, new QTreeWidgetItem(list));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -74,10 +72,3 @@ void Qtk::TreeView::itemFocus(QTreeWidgetItem * item, int column)
|
|||||||
// Emit signal from qtk widget for new object focus. Triggers GUI updates.
|
// Emit signal from qtk widget for new object focus. Triggers GUI updates.
|
||||||
emit QtkWidget::mWidgetManager.get_widget()->objectFocusChanged(name);
|
emit QtkWidget::mWidgetManager.get_widget()->objectFocusChanged(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Qtk::TreeView::itemSelect(QTreeWidgetItem * item, int column)
|
|
||||||
{
|
|
||||||
// Emit signal from qtk widget for new object focus. Triggers GUI updates.
|
|
||||||
const QString & name = item->text(column);
|
|
||||||
emit QtkWidget::mWidgetManager.get_widget()->objectFocusChanged(name);
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -58,16 +58,6 @@ namespace Qtk
|
|||||||
*/
|
*/
|
||||||
void itemFocus(QTreeWidgetItem * item, int column);
|
void itemFocus(QTreeWidgetItem * item, int column);
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the object to show details for.
|
|
||||||
* Triggered by QTreeWidget::itemClicked signal.
|
|
||||||
*
|
|
||||||
* @param item The item that was clicked
|
|
||||||
* @param column The column of the item that was double clicked.
|
|
||||||
* This param is currently not used but required for this signal.
|
|
||||||
*/
|
|
||||||
void itemSelect(QTreeWidgetItem * item, int column);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/*************************************************************************
|
/*************************************************************************
|
||||||
* Private Members
|
* Private Members
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ QString WidgetPlugin::whatsThis() const
|
|||||||
|
|
||||||
QIcon WidgetPlugin::icon() const
|
QIcon WidgetPlugin::icon() const
|
||||||
{
|
{
|
||||||
return QIcon(":/icons/icon.png");
|
return Qtk::getIcon();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WidgetPlugin::isContainer() const
|
bool WidgetPlugin::isContainer() const
|
||||||
|
|||||||
@@ -24,7 +24,6 @@ set(
|
|||||||
skybox.h
|
skybox.h
|
||||||
texture.h
|
texture.h
|
||||||
transform3D.h
|
transform3D.h
|
||||||
shaders.h
|
|
||||||
)
|
)
|
||||||
|
|
||||||
set(
|
set(
|
||||||
@@ -44,10 +43,11 @@ set(
|
|||||||
transform3D.cpp
|
transform3D.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
qt_add_library(qtk STATIC EXCLUDE_FROM_ALL)
|
qt6_add_big_resources(QTK_LIBRARY_SOURCES "${QTK_RESOURCES}/resources.qrc")
|
||||||
target_sources(qtk PRIVATE ${QTK_LIBRARY_SOURCES})
|
qt_add_library(qtk_library STATIC EXCLUDE_FROM_ALL)
|
||||||
|
target_sources(qtk_library PRIVATE ${QTK_LIBRARY_SOURCES})
|
||||||
target_sources(
|
target_sources(
|
||||||
qtk PUBLIC
|
qtk_library PUBLIC
|
||||||
FILE_SET HEADERS
|
FILE_SET HEADERS
|
||||||
BASE_DIRS $<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/src>
|
BASE_DIRS $<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/src>
|
||||||
BASE_DIRS $<INSTALL_INTERFACE:${CMAKE_INSTALL_PREFIX}/include>
|
BASE_DIRS $<INSTALL_INTERFACE:${CMAKE_INSTALL_PREFIX}/include>
|
||||||
@@ -55,25 +55,25 @@ target_sources(
|
|||||||
)
|
)
|
||||||
|
|
||||||
if(QTK_DEBUG)
|
if(QTK_DEBUG)
|
||||||
target_compile_definitions(qtk PUBLIC -DQTK_DEBUG)
|
target_compile_definitions(qtk_library PUBLIC QTK_DEBUG)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
set_target_properties(
|
set_target_properties(
|
||||||
qtk PROPERTIES
|
qtk_library PROPERTIES
|
||||||
VERSION ${PROJECT_VERSION}
|
VERSION ${PROJECT_VERSION}
|
||||||
)
|
)
|
||||||
|
|
||||||
target_link_libraries(
|
target_link_libraries(
|
||||||
qtk PUBLIC
|
qtk_library PUBLIC
|
||||||
Qt6::Core Qt6::OpenGLWidgets Qt6::Widgets
|
Qt6::Core Qt6::OpenGLWidgets Qt6::Widgets
|
||||||
)
|
)
|
||||||
|
|
||||||
if(QTK_SUBMODULES OR NOT QTK_ASSIMP_NEW_INTERFACE)
|
if(QTK_SUBMODULES OR NOT QTK_ASSIMP_NEW_INTERFACE)
|
||||||
target_link_libraries(qtk PUBLIC assimp)
|
target_link_libraries(qtk_library PUBLIC assimp)
|
||||||
elseif(QTK_ASSIMP_NEW_INTERFACE)
|
elseif(QTK_ASSIMP_NEW_INTERFACE)
|
||||||
target_link_libraries(qtk PUBLIC assimp::assimp)
|
target_link_libraries(qtk_library PUBLIC assimp::assimp)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
target_link_libraries(qtk PUBLIC OpenGL::GL)
|
target_link_libraries(qtk_library PUBLIC OpenGL::GL)
|
||||||
endif()
|
endif()
|
||||||
|
|||||||
@@ -10,7 +10,6 @@
|
|||||||
|
|
||||||
#include "meshrenderer.h"
|
#include "meshrenderer.h"
|
||||||
#include "scene.h"
|
#include "scene.h"
|
||||||
#include "shaders.h"
|
|
||||||
#include "texture.h"
|
#include "texture.h"
|
||||||
|
|
||||||
using namespace Qtk;
|
using namespace Qtk;
|
||||||
@@ -36,7 +35,8 @@ MeshRenderer::MeshRenderer(const char * name) :
|
|||||||
}
|
}
|
||||||
|
|
||||||
MeshRenderer::MeshRenderer(const char * name, const ShapeBase & shape) :
|
MeshRenderer::MeshRenderer(const char * name, const ShapeBase & shape) :
|
||||||
Object(name, shape, QTK_MESH), mDrawType(GL_TRIANGLES)
|
Object(name, shape, QTK_MESH), mVertexShader(":/shaders/multi-color.vert"),
|
||||||
|
mFragmentShader(":/shaders/multi-color.frag"), mDrawType(GL_TRIANGLES)
|
||||||
{
|
{
|
||||||
mShape = Shape(shape);
|
mShape = Shape(shape);
|
||||||
init();
|
init();
|
||||||
@@ -45,7 +45,7 @@ MeshRenderer::MeshRenderer(const char * name, const ShapeBase & shape) :
|
|||||||
|
|
||||||
MeshRenderer::~MeshRenderer()
|
MeshRenderer::~MeshRenderer()
|
||||||
{
|
{
|
||||||
sInstances.remove(mName);
|
sInstances.remove(mName.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
@@ -68,22 +68,10 @@ void MeshRenderer::init()
|
|||||||
mVAO.bind();
|
mVAO.bind();
|
||||||
|
|
||||||
mProgram.create();
|
mProgram.create();
|
||||||
// If no shader is provided, use a default one.
|
mProgram.addShaderFromSourceFile(QOpenGLShader::Vertex,
|
||||||
if (mVertexShader.empty()) {
|
mVertexShader.c_str());
|
||||||
mProgram.addShaderFromSourceCode(QOpenGLShader::Vertex,
|
mProgram.addShaderFromSourceFile(QOpenGLShader::Fragment,
|
||||||
QTK_SHADER_VERTEX_MESH);
|
mFragmentShader.c_str());
|
||||||
} else {
|
|
||||||
mProgram.addShaderFromSourceFile(QOpenGLShader::Vertex,
|
|
||||||
mVertexShader.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mFragmentShader.empty()) {
|
|
||||||
mProgram.addShaderFromSourceCode(QOpenGLShader::Fragment,
|
|
||||||
QTK_SHADER_FRAGMENT_MESH);
|
|
||||||
} else {
|
|
||||||
mProgram.addShaderFromSourceFile(QOpenGLShader::Fragment,
|
|
||||||
mFragmentShader.c_str());
|
|
||||||
}
|
|
||||||
mProgram.link();
|
mProgram.link();
|
||||||
mProgram.bind();
|
mProgram.bind();
|
||||||
|
|
||||||
@@ -121,7 +109,9 @@ void MeshRenderer::draw()
|
|||||||
bindShaders();
|
bindShaders();
|
||||||
mVAO.bind();
|
mVAO.bind();
|
||||||
|
|
||||||
mTexture.bind();
|
if (mTexture.hasTexture()) {
|
||||||
|
mTexture.getOpenGLTexture().bind();
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Automate uniforms some other way
|
// TODO: Automate uniforms some other way
|
||||||
setUniformMVP();
|
setUniformMVP();
|
||||||
@@ -136,7 +126,9 @@ void MeshRenderer::draw()
|
|||||||
mShape.mIndices.data());
|
mShape.mIndices.data());
|
||||||
}
|
}
|
||||||
|
|
||||||
mTexture.bind();
|
if (mTexture.hasTexture()) {
|
||||||
|
mTexture.getOpenGLTexture().release();
|
||||||
|
}
|
||||||
|
|
||||||
mVAO.release();
|
mVAO.release();
|
||||||
releaseShaders();
|
releaseShaders();
|
||||||
|
|||||||
@@ -56,19 +56,8 @@ void Model::flipTexture(const std::string & fileName, bool flipX, bool flipY)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Model::setLightPosition(const QString & lightName, const char * uniform)
|
|
||||||
{
|
|
||||||
if (auto light = MeshRenderer::getInstance(lightName); light) {
|
|
||||||
QVector3D position = light->getTransform().getTranslation();
|
|
||||||
setUniform(uniform, position);
|
|
||||||
} else {
|
|
||||||
qDebug() << "[QtkScene] Failed to set " << mName
|
|
||||||
<< "light position: " << lightName;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Static function to access ModelManager for getting Models by name
|
// Static function to access ModelManager for getting Models by name
|
||||||
Model * Model::getInstance(const char * name)
|
Model * Qtk::Model::getInstance(const char * name)
|
||||||
{
|
{
|
||||||
return mManager[name];
|
return mManager[name];
|
||||||
}
|
}
|
||||||
@@ -113,7 +102,7 @@ void Model::loadModel(const std::string & path)
|
|||||||
sortModelMeshes();
|
sortModelMeshes();
|
||||||
|
|
||||||
// Object finished loading, insert it into ModelManager
|
// 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)
|
void Model::processNode(aiNode * node, const aiScene * scene)
|
||||||
@@ -272,11 +261,13 @@ ModelMesh::Textures Model::loadMaterialTextures(aiMaterial * mat,
|
|||||||
|
|
||||||
void Model::sortModelMeshes()
|
void Model::sortModelMeshes()
|
||||||
{
|
{
|
||||||
auto cameraPos = Scene::getCamera().getTransform().getTranslation();
|
auto cameraPos = Scene::getCamera().getTransform();
|
||||||
auto cameraDistance = [&cameraPos](const ModelMesh & a, const ModelMesh & b) {
|
auto cameraDistance = [&cameraPos](const ModelMesh & a, const ModelMesh & b) {
|
||||||
// Sort by the first vertex position in the model
|
// Sort by the first vertex position in the model
|
||||||
return cameraPos.distanceToPoint(a.mVertices[0].mPosition)
|
return (cameraPos.getTranslation().distanceToPoint(
|
||||||
< cameraPos.distanceToPoint(b.mVertices[0].mPosition);
|
a.mVertices[0].mPosition))
|
||||||
|
< (cameraPos.getTranslation().distanceToPoint(
|
||||||
|
b.mVertices[0].mPosition));
|
||||||
};
|
};
|
||||||
std::sort(mMeshes.begin(), mMeshes.end(), cameraDistance);
|
std::sort(mMeshes.begin(), mMeshes.end(), cameraDistance);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,15 +55,15 @@ namespace Qtk
|
|||||||
*/
|
*/
|
||||||
inline Model(const char * name,
|
inline Model(const char * name,
|
||||||
const char * path,
|
const char * path,
|
||||||
const char * vertexShader = "",
|
const char * vertexShader = ":/shaders/model-basic.vert",
|
||||||
const char * fragmentShader = "") :
|
const char * fragmentShader = ":/shaders/model-basic.frag") :
|
||||||
Object(name, QTK_MODEL), mModelPath(path),
|
Object(name, QTK_MODEL), mModelPath(path),
|
||||||
mVertexShader(vertexShader), mFragmentShader(fragmentShader)
|
mVertexShader(vertexShader), mFragmentShader(fragmentShader)
|
||||||
{
|
{
|
||||||
loadModel(mModelPath);
|
loadModel(mModelPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline ~Model() override { mManager.remove(getName()); }
|
inline ~Model() override { mManager.remove(getName().c_str()); }
|
||||||
|
|
||||||
/*************************************************************************
|
/*************************************************************************
|
||||||
* Public Methods
|
* Public Methods
|
||||||
@@ -113,14 +113,6 @@ namespace Qtk
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the position of a light used in GLSL unfiroms.
|
|
||||||
*
|
|
||||||
* @param lightName Object name of the light
|
|
||||||
*/
|
|
||||||
void setLightPosition(const QString & lightName,
|
|
||||||
const char * uniform = "uLight.position");
|
|
||||||
|
|
||||||
/*************************************************************************
|
/*************************************************************************
|
||||||
* Accessors
|
* Accessors
|
||||||
************************************************************************/
|
************************************************************************/
|
||||||
|
|||||||
@@ -8,7 +8,6 @@
|
|||||||
|
|
||||||
#include "modelmesh.h"
|
#include "modelmesh.h"
|
||||||
#include "scene.h"
|
#include "scene.h"
|
||||||
#include "shaders.h"
|
|
||||||
|
|
||||||
using namespace Qtk;
|
using namespace Qtk;
|
||||||
|
|
||||||
@@ -74,12 +73,12 @@ void ModelMesh::draw(QOpenGLShaderProgram & shader)
|
|||||||
* Private Member Functions
|
* Private Member Functions
|
||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
|
|
||||||
void ModelMesh::initMesh(const std::string & vert, const std::string & frag)
|
void ModelMesh::initMesh(const char * vert, const char * frag)
|
||||||
{
|
{
|
||||||
initializeOpenGLFunctions();
|
initializeOpenGLFunctions();
|
||||||
|
|
||||||
// Create VAO, VBO, EBO
|
// Create VAO, VBO, EBO
|
||||||
mVAO->create();
|
bool status = mVAO->create();
|
||||||
mVBO->create();
|
mVBO->create();
|
||||||
mEBO->create();
|
mEBO->create();
|
||||||
|
|
||||||
@@ -98,26 +97,10 @@ void ModelMesh::initMesh(const std::string & vert, const std::string & frag)
|
|||||||
mEBO->release();
|
mEBO->release();
|
||||||
|
|
||||||
// Load and link shaders
|
// Load and link shaders
|
||||||
if (!vert.empty()) {
|
mProgram->addShaderFromSourceFile(QOpenGLShader::Vertex, vert);
|
||||||
mProgram->addShaderFromSourceFile(QOpenGLShader::Vertex, vert.c_str());
|
mProgram->addShaderFromSourceFile(QOpenGLShader::Fragment, frag);
|
||||||
} else {
|
mProgram->link();
|
||||||
mProgram->addShaderFromSourceCode(QOpenGLShader::Vertex,
|
mProgram->bind();
|
||||||
QTK_SHADER_VERTEX_MODEL);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!frag.empty()) {
|
|
||||||
mProgram->addShaderFromSourceFile(QOpenGLShader::Fragment, frag.c_str());
|
|
||||||
} else {
|
|
||||||
mProgram->addShaderFromSourceCode(QOpenGLShader::Fragment,
|
|
||||||
QTK_SHADER_FRAGMENT_MODEL);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!mProgram->link()) {
|
|
||||||
qDebug() << "Failed to link shader: " << mProgram->log();
|
|
||||||
}
|
|
||||||
if (!mProgram->bind()) {
|
|
||||||
qDebug() << "Failed to bind shader: " << mProgram->log();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Positions
|
// Positions
|
||||||
mProgram->enableAttributeArray(0);
|
mProgram->enableAttributeArray(0);
|
||||||
|
|||||||
@@ -97,8 +97,8 @@ namespace Qtk
|
|||||||
ModelMesh(Vertices vertices,
|
ModelMesh(Vertices vertices,
|
||||||
Indices indices,
|
Indices indices,
|
||||||
Textures textures,
|
Textures textures,
|
||||||
const char * vertexShader = "",
|
const char * vertexShader = ":/model-basic.vert",
|
||||||
const char * fragmentShader = "") :
|
const char * fragmentShader = ":/model-basic.frag") :
|
||||||
mProgram(new QOpenGLShaderProgram),
|
mProgram(new QOpenGLShaderProgram),
|
||||||
mVAO(new QOpenGLVertexArrayObject),
|
mVAO(new QOpenGLVertexArrayObject),
|
||||||
mVBO(new QOpenGLBuffer(QOpenGLBuffer::VertexBuffer)),
|
mVBO(new QOpenGLBuffer(QOpenGLBuffer::VertexBuffer)),
|
||||||
@@ -146,7 +146,7 @@ namespace Qtk
|
|||||||
* @param vert Path to vertex shader to use for this model.
|
* @param vert Path to vertex shader to use for this model.
|
||||||
* @param frag Path to fragment shader to use for this model.
|
* @param frag Path to fragment shader to use for this model.
|
||||||
*/
|
*/
|
||||||
void initMesh(const std::string & vert, const std::string & frag);
|
void initMesh(const char * vert, const char * frag);
|
||||||
|
|
||||||
/*************************************************************************
|
/*************************************************************************
|
||||||
* Private Members
|
* Private Members
|
||||||
|
|||||||
@@ -9,15 +9,3 @@
|
|||||||
#include "object.h"
|
#include "object.h"
|
||||||
|
|
||||||
using namespace Qtk;
|
using namespace Qtk;
|
||||||
|
|
||||||
std::string Object::getShaderSourceCode(
|
|
||||||
QOpenGLShader::ShaderType shader_type) const
|
|
||||||
{
|
|
||||||
for (const auto & shader : mProgram.shaders()) {
|
|
||||||
if (shader->shaderType() == shader_type) {
|
|
||||||
return shader->sourceCode().toStdString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
qDebug() << "Failed to find shader of type " << shader_type;
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ namespace Qtk
|
|||||||
mName(name), mVBO(QOpenGLBuffer::VertexBuffer), mBound(false),
|
mName(name), mVBO(QOpenGLBuffer::VertexBuffer), mBound(false),
|
||||||
mType(type)
|
mType(type)
|
||||||
{
|
{
|
||||||
|
initResources();
|
||||||
setObjectName(name);
|
setObjectName(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,6 +60,7 @@ namespace Qtk
|
|||||||
mName(name), mVBO(QOpenGLBuffer::VertexBuffer), mShape(shape),
|
mName(name), mVBO(QOpenGLBuffer::VertexBuffer), mShape(shape),
|
||||||
mBound(false), mType(type)
|
mBound(false), mType(type)
|
||||||
{
|
{
|
||||||
|
initResources();
|
||||||
setObjectName(name);
|
setObjectName(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,7 +102,7 @@ namespace Qtk
|
|||||||
return mShape.mVertices;
|
return mShape.mVertices;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] inline QString getName() const { return mName; }
|
[[nodiscard]] inline std::string getName() const { return mName; }
|
||||||
|
|
||||||
[[nodiscard]] inline const Type & getType() const { return mType; }
|
[[nodiscard]] inline const Type & getType() const { return mType; }
|
||||||
|
|
||||||
@@ -124,26 +126,11 @@ namespace Qtk
|
|||||||
return "Base Object has no fragment shader.";
|
return "Base Object has no fragment shader.";
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] virtual std::string getShaderSourceCode(
|
|
||||||
QOpenGLShader::ShaderType shader_type) const;
|
|
||||||
|
|
||||||
|
|
||||||
[[nodiscard]] virtual inline std::string getVertexShaderSourceCode() const
|
|
||||||
{
|
|
||||||
return getShaderSourceCode(QOpenGLShader::Vertex);
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] virtual inline std::string getFragmentShaderSourceCode()
|
|
||||||
const
|
|
||||||
{
|
|
||||||
return getShaderSourceCode(QOpenGLShader::Fragment);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*************************************************************************
|
/*************************************************************************
|
||||||
* Setters
|
* Setters
|
||||||
************************************************************************/
|
************************************************************************/
|
||||||
|
|
||||||
virtual inline void setName(const QString & name) { mName = name; }
|
virtual inline void setName(const std::string & name) { mName = name; }
|
||||||
|
|
||||||
virtual inline void setColors(const Colors & value)
|
virtual inline void setColors(const Colors & value)
|
||||||
{
|
{
|
||||||
@@ -244,23 +231,6 @@ namespace Qtk
|
|||||||
mProgram.release();
|
mProgram.release();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*************************************************************************
|
|
||||||
* Public Static Methods
|
|
||||||
************************************************************************/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Helper to disconnect a QObject connection, only if it's valid.
|
|
||||||
* If the connection is valid and we fail to disconnect log a message.
|
|
||||||
*
|
|
||||||
* @param con QObject connection handle to disconnect.
|
|
||||||
*/
|
|
||||||
static void disconnect(const QMetaObject::Connection & con)
|
|
||||||
{
|
|
||||||
if (con && !QObject::disconnect(con)) {
|
|
||||||
qDebug() << "[Qtk] Failed to disconnect valid connection: " << con;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/*************************************************************************
|
/*************************************************************************
|
||||||
* Private Members
|
* Private Members
|
||||||
@@ -272,7 +242,7 @@ namespace Qtk
|
|||||||
Transform3D mTransform;
|
Transform3D mTransform;
|
||||||
Shape mShape;
|
Shape mShape;
|
||||||
Texture mTexture;
|
Texture mTexture;
|
||||||
QString mName;
|
std::string mName;
|
||||||
bool mBound;
|
bool mBound;
|
||||||
Type mType = QTK_OBJECT;
|
Type mType = QTK_OBJECT;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -22,6 +22,16 @@
|
|||||||
#define QTKAPI
|
#define QTKAPI
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize Qt resources required by the Qtk library.
|
||||||
|
* This cannot be defined within any namespace, but can be called by ctors.
|
||||||
|
* See object.h for example.
|
||||||
|
*/
|
||||||
|
inline void initResources()
|
||||||
|
{
|
||||||
|
Q_INIT_RESOURCE(resources);
|
||||||
|
}
|
||||||
|
|
||||||
namespace Qtk
|
namespace Qtk
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
@@ -43,6 +53,14 @@ namespace Qtk
|
|||||||
}
|
}
|
||||||
return widget;
|
return widget;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Default icon to use for Qtk desktop application.
|
||||||
|
*/
|
||||||
|
static QIcon getIcon()
|
||||||
|
{
|
||||||
|
return QIcon(":/icons/icon.png");
|
||||||
|
}
|
||||||
} // namespace Qtk
|
} // namespace Qtk
|
||||||
|
|
||||||
#endif // QTK_QTKAPI_H
|
#endif // QTK_QTKAPI_H
|
||||||
|
|||||||
@@ -55,34 +55,6 @@ template <> Model * Scene::addObject(Model * object)
|
|||||||
return object;
|
return object;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <> void Scene::removeObject(MeshRenderer * object)
|
|
||||||
{
|
|
||||||
auto it = std::find(mMeshes.begin(), mMeshes.end(), object);
|
|
||||||
if (it == mMeshes.end()) {
|
|
||||||
qDebug() << "[Scene::removeObject]: Failed to remove object: "
|
|
||||||
<< object->getName() << " (" << object << ")";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
--mObjectCount[object->getName()];
|
|
||||||
mMeshes.erase(it);
|
|
||||||
emit sceneUpdated(mSceneName);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <> void Scene::removeObject(Model * object)
|
|
||||||
{
|
|
||||||
auto it = std::find(mModels.begin(), mModels.end(), object);
|
|
||||||
if (it == mModels.end()) {
|
|
||||||
qDebug() << "[Scene::removeObject]: Failed to remove object: "
|
|
||||||
<< object->getName() << " (" << object << ")";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
--mObjectCount[object->getName()];
|
|
||||||
mModels.erase(it);
|
|
||||||
emit sceneUpdated(mSceneName);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Scene::draw()
|
void Scene::draw()
|
||||||
{
|
{
|
||||||
if (!mInit) {
|
if (!mInit) {
|
||||||
@@ -131,7 +103,7 @@ std::vector<Object *> Scene::getObjects() const
|
|||||||
Object * Scene::getObject(const QString & name) const
|
Object * Scene::getObject(const QString & name) const
|
||||||
{
|
{
|
||||||
for (const auto & object : getObjects()) {
|
for (const auto & object : getObjects()) {
|
||||||
if (object->getName() == name) {
|
if (object->getName() == name.toStdString()) {
|
||||||
return object;
|
return object;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -149,6 +121,6 @@ void Scene::initSceneObjectName(Object * object)
|
|||||||
// If the object name exists make it unique.
|
// If the object name exists make it unique.
|
||||||
auto count = ++mObjectCount[object->getName()];
|
auto count = ++mObjectCount[object->getName()];
|
||||||
if (count > 1) {
|
if (count > 1) {
|
||||||
object->setName(object->getName() + " (" + QString::number(count) + ")");
|
object->setName(object->getName() + " (" + std::to_string(count) + ")");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -85,16 +85,16 @@ namespace Qtk
|
|||||||
|
|
||||||
void loadModel(const QUrl & url)
|
void loadModel(const QUrl & url)
|
||||||
{
|
{
|
||||||
auto fileName = url.fileName().replace(".obj", "");
|
auto fileName = url.fileName().replace(".obj", "").toStdString();
|
||||||
auto filePath = url.toLocalFile();
|
auto filePath = url.toLocalFile().toStdString();
|
||||||
loadModel(fileName, filePath);
|
loadModel(fileName, filePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
void loadModel(const QString & name, const QString & path)
|
void loadModel(const std::string & name, const std::string & path)
|
||||||
{
|
{
|
||||||
// Add the dropped model to the load queue.
|
// Add the dropped model to the load queue.
|
||||||
// This is consumed during rendering of the scene if not empty.
|
// This is consumed during rendering of the scene if not empty.
|
||||||
mModelLoadQueue.emplace(name.toStdString(), path.toStdString());
|
mModelLoadQueue.emplace(name, path);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*************************************************************************
|
/*************************************************************************
|
||||||
@@ -120,7 +120,9 @@ namespace Qtk
|
|||||||
*/
|
*/
|
||||||
[[nodiscard]] uint64_t getObjectCount(const QString & name)
|
[[nodiscard]] uint64_t getObjectCount(const QString & name)
|
||||||
{
|
{
|
||||||
return mObjectCount.count(name) ? mObjectCount[name] : 0;
|
return mObjectCount.count(name.toStdString())
|
||||||
|
? mObjectCount[name.toStdString()]
|
||||||
|
: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -182,9 +184,8 @@ namespace Qtk
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds objects to the scene.
|
* Adds objects to the scene.
|
||||||
* This template provides explicit specializations for the valid types:
|
* This template provides explicit specializations for valid types.
|
||||||
* MeshRenderer, Model
|
* Adding any object other than these types will cause errors.
|
||||||
* Any other object type will cause errors.
|
|
||||||
* TODO: Refactor to use Object base class container for scene objects.
|
* TODO: Refactor to use Object base class container for scene objects.
|
||||||
*
|
*
|
||||||
* If creating a new object type for a scene, it must inherit Qtk::Object
|
* If creating a new object type for a scene, it must inherit Qtk::Object
|
||||||
@@ -195,17 +196,6 @@ namespace Qtk
|
|||||||
*/
|
*/
|
||||||
template <typename T> T * addObject(T * object);
|
template <typename T> T * addObject(T * object);
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes an object from the scene.
|
|
||||||
* This template provides explicit specializations for the valid types:
|
|
||||||
* MeshRenderer, Model
|
|
||||||
* Any other object type will cause errors.
|
|
||||||
* TODO: Refactor to use Object base class container for scene objects.
|
|
||||||
*
|
|
||||||
* @param object Pointer to the object to remove from the scene.
|
|
||||||
*/
|
|
||||||
template <typename T> void removeObject(T * object);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param name The name to use for this scene.
|
* @param name The name to use for this scene.
|
||||||
*/
|
*/
|
||||||
@@ -258,7 +248,7 @@ namespace Qtk
|
|||||||
/* MeshRenderers used simple geometry. */
|
/* MeshRenderers used simple geometry. */
|
||||||
std::vector<MeshRenderer *> mMeshes {};
|
std::vector<MeshRenderer *> mMeshes {};
|
||||||
/* Track count of objects with same initial name. */
|
/* Track count of objects with same initial name. */
|
||||||
std::unordered_map<QString, uint64_t> mObjectCount;
|
std::unordered_map<std::string, uint64_t> mObjectCount;
|
||||||
};
|
};
|
||||||
} // namespace Qtk
|
} // namespace Qtk
|
||||||
|
|
||||||
|
|||||||
@@ -1,121 +0,0 @@
|
|||||||
/*##############################################################################
|
|
||||||
## Author: Shaun Reed ##
|
|
||||||
## Legal: All Content (c) 2025 Shaun Reed, all rights reserved ##
|
|
||||||
## About: Default GLSL shaders to use for objects if no shader if provided. ##
|
|
||||||
## ##
|
|
||||||
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
|
|
||||||
##############################################################################*/
|
|
||||||
#ifndef QTK_SHADERS_H
|
|
||||||
#define QTK_SHADERS_H
|
|
||||||
|
|
||||||
//
|
|
||||||
// Model
|
|
||||||
|
|
||||||
#define QTK_SHADER_VERTEX_MODEL \
|
|
||||||
R"(
|
|
||||||
#version 330 core
|
|
||||||
layout (location = 0) in vec3 aPosition;
|
|
||||||
layout (location = 1) in vec3 aNormal;
|
|
||||||
layout (location = 2) in vec2 aTextureCoord;
|
|
||||||
|
|
||||||
out vec2 vTextureCoord;
|
|
||||||
|
|
||||||
uniform mat4 uModel;
|
|
||||||
uniform mat4 uView;
|
|
||||||
uniform mat4 uProjection;
|
|
||||||
|
|
||||||
void main()
|
|
||||||
{
|
|
||||||
vTextureCoord = aTextureCoord;
|
|
||||||
gl_Position = uProjection * uView * uModel * vec4(aPosition, 1.0);
|
|
||||||
}
|
|
||||||
)"
|
|
||||||
|
|
||||||
#define QTK_SHADER_FRAGMENT_MODEL \
|
|
||||||
R"(
|
|
||||||
#version 330 core
|
|
||||||
out vec4 fColor;
|
|
||||||
|
|
||||||
in vec2 vTextureCoord;
|
|
||||||
|
|
||||||
uniform sampler2D texture_diffuse1;
|
|
||||||
|
|
||||||
void main()
|
|
||||||
{
|
|
||||||
fColor = texture(texture_diffuse1, vTextureCoord);
|
|
||||||
}
|
|
||||||
)"
|
|
||||||
|
|
||||||
//
|
|
||||||
// MeshRenderer
|
|
||||||
|
|
||||||
#define QTK_SHADER_VERTEX_MESH \
|
|
||||||
R"(
|
|
||||||
#version 330
|
|
||||||
layout(location = 0) in vec3 aPosition;
|
|
||||||
layout(location = 1) in vec3 aColor;
|
|
||||||
|
|
||||||
out vec4 vColor;
|
|
||||||
|
|
||||||
uniform mat4 uModel; // Model
|
|
||||||
uniform mat4 uView; // View
|
|
||||||
uniform mat4 uProjection; // Projection
|
|
||||||
|
|
||||||
void main()
|
|
||||||
{
|
|
||||||
gl_Position = uProjection * uView * uModel * vec4(aPosition, 1.0);
|
|
||||||
|
|
||||||
vColor = vec4(aColor, 1.0f);
|
|
||||||
}
|
|
||||||
)"
|
|
||||||
|
|
||||||
#define QTK_SHADER_FRAGMENT_MESH \
|
|
||||||
R"(
|
|
||||||
#version 330
|
|
||||||
in vec4 vColor;
|
|
||||||
|
|
||||||
out vec4 fColor;
|
|
||||||
|
|
||||||
void main()
|
|
||||||
{
|
|
||||||
fColor = vColor;
|
|
||||||
}
|
|
||||||
)"
|
|
||||||
|
|
||||||
//
|
|
||||||
// Skybox
|
|
||||||
|
|
||||||
#define QTK_SHADER_VERTEX_SKYBOX \
|
|
||||||
R"(
|
|
||||||
#version 330
|
|
||||||
layout(location = 0) in vec3 aPosition;
|
|
||||||
|
|
||||||
out vec3 vTexCoord;
|
|
||||||
|
|
||||||
uniform mat4 uProjectionMatrix;
|
|
||||||
uniform mat4 uViewMatrix;
|
|
||||||
|
|
||||||
void main()
|
|
||||||
{
|
|
||||||
// Strip translation column from camera's 4x4 matrix
|
|
||||||
mat4 view = mat4(mat3(uViewMatrix));
|
|
||||||
gl_Position = uProjectionMatrix * view * vec4(aPosition, 1.0);
|
|
||||||
vTexCoord = aPosition;
|
|
||||||
}
|
|
||||||
)"
|
|
||||||
|
|
||||||
#define QTK_SHADER_FRAGMENT_SKYBOX \
|
|
||||||
R"(
|
|
||||||
#version 330
|
|
||||||
uniform samplerCube uTexture;
|
|
||||||
|
|
||||||
in vec3 vTexCoord;
|
|
||||||
out vec4 FragColor;
|
|
||||||
|
|
||||||
void main()
|
|
||||||
{
|
|
||||||
FragColor = texture(uTexture, vTexCoord);
|
|
||||||
}
|
|
||||||
)"
|
|
||||||
|
|
||||||
#endif // QTK_SHADERS_H
|
|
||||||
@@ -8,7 +8,6 @@
|
|||||||
|
|
||||||
#include "skybox.h"
|
#include "skybox.h"
|
||||||
#include "scene.h"
|
#include "scene.h"
|
||||||
#include "shaders.h"
|
|
||||||
#include "texture.h"
|
#include "texture.h"
|
||||||
|
|
||||||
using namespace Qtk;
|
using namespace Qtk;
|
||||||
@@ -17,28 +16,9 @@ using namespace Qtk;
|
|||||||
* Constructors / Destructors
|
* Constructors / Destructors
|
||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
|
|
||||||
Skybox::Skybox(const std::string & name) :
|
Skybox::Skybox(QOpenGLTexture * cubeMap, const std::string & name)
|
||||||
mVBO(QOpenGLBuffer::VertexBuffer),
|
|
||||||
mVertices(Cube(QTK_DRAW_ELEMENTS).getVertices()),
|
|
||||||
mIndices(Cube(QTK_DRAW_ELEMENTS).getIndexData())
|
|
||||||
{
|
{
|
||||||
QImage image({1024, 1024}, QImage::Format_RGBA8888);
|
mTexture.setTexture(cubeMap);
|
||||||
image.fill(Qt::darkGray);
|
|
||||||
mTexture.setCubeMap(image, image, image, image, image, image);
|
|
||||||
init();
|
|
||||||
}
|
|
||||||
|
|
||||||
Skybox::Skybox(QOpenGLTexture * cubeMap, const std::string & name) :
|
|
||||||
mVBO(QOpenGLBuffer::VertexBuffer),
|
|
||||||
mVertices(Cube(QTK_DRAW_ELEMENTS).getVertices()),
|
|
||||||
mIndices(Cube(QTK_DRAW_ELEMENTS).getIndexData())
|
|
||||||
{
|
|
||||||
if (cubeMap == Q_NULLPTR) {
|
|
||||||
qDebug()
|
|
||||||
<< "[Qtk] Failed to set cubemap for skybox with null QOpenGLTexture.";
|
|
||||||
} else {
|
|
||||||
mTexture.setTexture(cubeMap);
|
|
||||||
}
|
|
||||||
init();
|
init();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,13 +33,13 @@ Skybox::Skybox(const std::string & right,
|
|||||||
mVertices(Cube(QTK_DRAW_ELEMENTS).getVertices()),
|
mVertices(Cube(QTK_DRAW_ELEMENTS).getVertices()),
|
||||||
mIndices(Cube(QTK_DRAW_ELEMENTS).getIndexData())
|
mIndices(Cube(QTK_DRAW_ELEMENTS).getIndexData())
|
||||||
{
|
{
|
||||||
|
init();
|
||||||
mTexture.setCubeMap(QImage(right.c_str()).mirrored(),
|
mTexture.setCubeMap(QImage(right.c_str()).mirrored(),
|
||||||
QImage(top.c_str()),
|
QImage(top.c_str()),
|
||||||
QImage(front.c_str()),
|
QImage(front.c_str()),
|
||||||
QImage(left.c_str()),
|
QImage(left.c_str()),
|
||||||
QImage(bottom.c_str()),
|
QImage(bottom.c_str()),
|
||||||
QImage(back.c_str()));
|
QImage(back.c_str()));
|
||||||
init();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
@@ -73,7 +53,7 @@ void Skybox::draw()
|
|||||||
|
|
||||||
mVAO.bind();
|
mVAO.bind();
|
||||||
mProgram.bind();
|
mProgram.bind();
|
||||||
mTexture.bind();
|
mTexture.getOpenGLTexture().bind();
|
||||||
|
|
||||||
mProgram.setUniformValue("uProjectionMatrix", Scene::getProjectionMatrix());
|
mProgram.setUniformValue("uProjectionMatrix", Scene::getProjectionMatrix());
|
||||||
mProgram.setUniformValue("uViewMatrix", Scene::getCamera().toMatrix());
|
mProgram.setUniformValue("uViewMatrix", Scene::getCamera().toMatrix());
|
||||||
@@ -81,7 +61,7 @@ void Skybox::draw()
|
|||||||
glDrawElements(
|
glDrawElements(
|
||||||
GL_TRIANGLES, mIndices.size(), GL_UNSIGNED_INT, mIndices.data());
|
GL_TRIANGLES, mIndices.size(), GL_UNSIGNED_INT, mIndices.data());
|
||||||
|
|
||||||
mTexture.bind();
|
mTexture.getOpenGLTexture().bind();
|
||||||
mProgram.release();
|
mProgram.release();
|
||||||
mVAO.release();
|
mVAO.release();
|
||||||
|
|
||||||
@@ -100,10 +80,10 @@ void Skybox::init()
|
|||||||
|
|
||||||
// Set up shader program
|
// Set up shader program
|
||||||
mProgram.create();
|
mProgram.create();
|
||||||
mProgram.addShaderFromSourceCode(QOpenGLShader::Fragment,
|
mProgram.addShaderFromSourceFile(QOpenGLShader::Vertex,
|
||||||
QTK_SHADER_FRAGMENT_SKYBOX);
|
":/shaders/skybox.vert");
|
||||||
mProgram.addShaderFromSourceCode(QOpenGLShader::Vertex,
|
mProgram.addShaderFromSourceFile(QOpenGLShader::Fragment,
|
||||||
QTK_SHADER_VERTEX_SKYBOX);
|
":/shaders/skybox.frag");
|
||||||
mProgram.link();
|
mProgram.link();
|
||||||
mProgram.bind();
|
mProgram.bind();
|
||||||
|
|
||||||
|
|||||||
@@ -34,12 +34,7 @@ namespace Qtk
|
|||||||
* Constructors / Destructors
|
* Constructors / Destructors
|
||||||
************************************************************************/
|
************************************************************************/
|
||||||
|
|
||||||
/**
|
// Delegate this constructor to use default skybox images
|
||||||
* Construct a skybox with a default texture.
|
|
||||||
*
|
|
||||||
* @param name The objectName to use for the Skybox.
|
|
||||||
*/
|
|
||||||
explicit Skybox(const std::string & name = "Skybox");
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct a skybox with an existing QOpenGLTexture.
|
* Construct a skybox with an existing QOpenGLTexture.
|
||||||
|
|||||||
@@ -8,7 +8,6 @@
|
|||||||
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QImageReader>
|
#include <QImageReader>
|
||||||
#include <QPainter>
|
|
||||||
|
|
||||||
#include "texture.h"
|
#include "texture.h"
|
||||||
|
|
||||||
@@ -66,22 +65,6 @@ QOpenGLTexture * OpenGLTextureFactory::initCubeMap(const char * right,
|
|||||||
QImage(back));
|
QImage(back));
|
||||||
}
|
}
|
||||||
|
|
||||||
QImage OpenGLTextureFactory::defaultTexture()
|
|
||||||
{
|
|
||||||
QImage image({256, 256}, QImage::Format_RGBA8888);
|
|
||||||
image.fill(Qt::lightGray);
|
|
||||||
|
|
||||||
// Draw a red '?' to the center of the image.
|
|
||||||
QPainter painter(&image);
|
|
||||||
painter.setRenderHint(QPainter::Antialiasing);
|
|
||||||
painter.setPen(Qt::red);
|
|
||||||
painter.setFont({"Helvetica", 100, QFont::Bold});
|
|
||||||
constexpr QRect rect(0, 0, 256, 256);
|
|
||||||
painter.drawText(rect, Qt::AlignCenter, "?");
|
|
||||||
|
|
||||||
return image;
|
|
||||||
}
|
|
||||||
|
|
||||||
QOpenGLTexture * OpenGLTextureFactory::initCubeMap(const QImage & right,
|
QOpenGLTexture * OpenGLTextureFactory::initCubeMap(const QImage & right,
|
||||||
const QImage & top,
|
const QImage & top,
|
||||||
const QImage & front,
|
const QImage & front,
|
||||||
@@ -104,9 +87,9 @@ QOpenGLTexture * OpenGLTextureFactory::initCubeMap(const QImage & right,
|
|||||||
QOpenGLTexture::CubeMapNegativeZ};
|
QOpenGLTexture::CubeMapNegativeZ};
|
||||||
int i = 0;
|
int i = 0;
|
||||||
for (const auto & face : faces) {
|
for (const auto & face : faces) {
|
||||||
QImage & faceImage = faceTextures[i];
|
QImage faceImage(faceTextures[i]);
|
||||||
if (faceImage.isNull()) {
|
if (faceImage.isNull()) {
|
||||||
qDebug() << "[libqtk] Error loading cube map image\n";
|
qDebug() << "Error loading cube map image\n";
|
||||||
faceImage = defaultTexture();
|
faceImage = defaultTexture();
|
||||||
}
|
}
|
||||||
faceImage = faceImage.convertToFormat(QImage::Format_RGBA8888);
|
faceImage = faceImage.convertToFormat(QImage::Format_RGBA8888);
|
||||||
|
|||||||
@@ -145,7 +145,13 @@ namespace Qtk
|
|||||||
const char * back);
|
const char * back);
|
||||||
|
|
||||||
/// The texture used in place of a missing texture.
|
/// The texture used in place of a missing texture.
|
||||||
static QImage defaultTexture();
|
static QImage defaultTexture()
|
||||||
|
{
|
||||||
|
// Use plaster for default texture if image fails to load.
|
||||||
|
// This prevents segfaults when loading a texture that doesn't exist.
|
||||||
|
// TODO: Replace with a '?' texture to indicate missing texture.
|
||||||
|
return QImage(":/textures/plaster.png");
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Private ctor to prevent creating instances of this class
|
// Private ctor to prevent creating instances of this class
|
||||||
@@ -217,20 +223,6 @@ namespace Qtk
|
|||||||
return mOpenGLTexture != Q_NULLPTR;
|
return mOpenGLTexture != Q_NULLPTR;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Bind the OpenGL texture if it exists, avoiding segmentation faults.
|
|
||||||
*/
|
|
||||||
bool bind() const
|
|
||||||
{
|
|
||||||
if (hasTexture()) {
|
|
||||||
// TODO: It would be nice to warn here but some objects may not have
|
|
||||||
// a texture. Factor Texture out of those objects so we don't bind.
|
|
||||||
mOpenGLTexture->bind();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*************************************************************************
|
/*************************************************************************
|
||||||
* Accessors
|
* Accessors
|
||||||
************************************************************************/
|
************************************************************************/
|
||||||
@@ -314,12 +306,12 @@ namespace Qtk
|
|||||||
/**
|
/**
|
||||||
* Sets this Texture to be a cube map with provided sides.
|
* Sets this Texture to be a cube map with provided sides.
|
||||||
*
|
*
|
||||||
* @param right QImage texture to use for right cube map side.
|
* @param right Path to texture to use for right cube map side.
|
||||||
* @param top QImage texture to use for top cube map side.
|
* @param top Path to texture to use for top cube map side.
|
||||||
* @param front QImage texture to use for front cube map side.
|
* @param front Path to texture to use for front cube map side.
|
||||||
* @param left QImage texture to use for left cube map side.
|
* @param left Path to texture to use for left cube map side.
|
||||||
* @param bottom QImage texture to use for bottom cube map side.
|
* @param bottom Path to texture to use for bottom cube map side.
|
||||||
* @param back QImage texture to use for back cube map side.
|
* @param back Path to texture to use for back cube map side.
|
||||||
*/
|
*/
|
||||||
virtual inline void setCubeMap(const QImage & right,
|
virtual inline void setCubeMap(const QImage & right,
|
||||||
const QImage & top,
|
const QImage & top,
|
||||||
|
|||||||
@@ -11,6 +11,5 @@ cmake -B build && cmake --build build -- -j $(nproc --ignore=1)
|
|||||||
|
|
||||||
# Run clang-tidy and clang-format
|
# Run clang-tidy and clang-format
|
||||||
SOURCES="src/**/*.cpp src/**/*.h example-app/*.cpp example-app/*.h"
|
SOURCES="src/**/*.cpp src/**/*.h example-app/*.cpp example-app/*.h"
|
||||||
run-clang-tidy \
|
clang-tidy -p build/ --fix --config-file=.clang-tidy $SOURCES
|
||||||
-p build/ -j $(nproc --ignore=1) -fix -config-file=.clang-tidy $SOURCES
|
|
||||||
clang-format -i --style=file:.clang-format $SOURCES
|
clang-format -i --style=file:.clang-format $SOURCES
|
||||||
|
|||||||
Reference in New Issue
Block a user