Big bang
This commit is contained in:
57
src/CMakeLists.txt
Normal file
57
src/CMakeLists.txt
Normal file
@@ -0,0 +1,57 @@
|
||||
find_package(Corrade REQUIRED Main)
|
||||
find_package(Magnum REQUIRED
|
||||
GL
|
||||
MeshTools
|
||||
Primitives
|
||||
SceneGraph
|
||||
Shaders)
|
||||
find_package(Qt5 REQUIRED COMPONENTS
|
||||
Core
|
||||
Quick)
|
||||
|
||||
if(MAGNUM_TARGET_GLES AND NOT MAGNUM_TARGET_DESKTOP_GLES)
|
||||
find_package(Magnum REQUIRED EglContext)
|
||||
elseif(CORRADE_TARGET_WINDOWS)
|
||||
find_package(Magnum REQUIRED WglContext)
|
||||
elseif(CORRADE_TARGET_APPLE)
|
||||
find_package(Magnum REQUIRED CglContext)
|
||||
elseif(CORRADE_TARGET_UNIX)
|
||||
find_package(Magnum REQUIRED GlxContext)
|
||||
else()
|
||||
message(FATAL_ERROR "Magnum context creation is not supported on this platform")
|
||||
endif()
|
||||
|
||||
set_directory_properties(PROPERTIES CORRADE_USE_PEDANTIC_FLAGS ON)
|
||||
|
||||
if(ANDROID)
|
||||
add_library(MyApplication SHARED
|
||||
arc_ball.cpp
|
||||
magnum_render.cpp
|
||||
magnum_item.cpp
|
||||
qml/main.qrc
|
||||
main.cpp)
|
||||
else()
|
||||
add_executable(MyApplication
|
||||
arc_ball.cpp
|
||||
magnum_render.cpp
|
||||
magnum_item.cpp
|
||||
qml/main.qrc
|
||||
main.cpp)
|
||||
endif()
|
||||
|
||||
# set_property(SOURCE usr/include/Magnum/GL/Attribute.h PROPERTY SKIP_AUTOMOC ON)
|
||||
|
||||
target_compile_definitions(MyApplication
|
||||
PRIVATE $<$<OR:$<CONFIG:Debug>,$<CONFIG:RelWithDebInfo>>:QT_QML_DEBUG>)
|
||||
target_link_libraries(MyApplication PRIVATE
|
||||
Corrade::Main
|
||||
Magnum::GL
|
||||
Magnum::GLContext
|
||||
Magnum::Magnum
|
||||
Magnum::MeshTools
|
||||
Magnum::Primitives
|
||||
Magnum::SceneGraph
|
||||
Magnum::Shaders
|
||||
Qt5::Core
|
||||
Qt5::Quick)
|
||||
|
134
src/arc_ball.cpp
Normal file
134
src/arc_ball.cpp
Normal file
@@ -0,0 +1,134 @@
|
||||
#include <Magnum/Math/Matrix3.h>
|
||||
|
||||
#include "arc_ball.h"
|
||||
|
||||
/* Project a point in NDC onto the arcball sphere */
|
||||
Quaternion ndcToArcBall(const Vector2 &p) {
|
||||
const Float dist = Math::dot(p, p);
|
||||
|
||||
/* Point is on sphere */
|
||||
if (dist <= 1.0f)
|
||||
return {{p.x(), p.y(), Math::sqrt(1.0f - dist)}, 0.0f};
|
||||
|
||||
/* Point is outside sphere */
|
||||
else {
|
||||
const Vector2 proj = p.normalized();
|
||||
return {{proj.x(), proj.y(), 0.0f}, 0.0f};
|
||||
}
|
||||
}
|
||||
|
||||
ArcBall::ArcBall(const Vector3 &eye, const Vector3 &viewCenter,
|
||||
const Vector3 &upDir, Deg fov, const Vector2i &windowSize)
|
||||
: m_fov{fov}, m_windowSize{windowSize} {
|
||||
setViewParameters(eye, viewCenter, upDir);
|
||||
}
|
||||
|
||||
void ArcBall::setViewParameters(const Vector3 &eye, const Vector3 &viewCenter,
|
||||
const Vector3 &upDir) {
|
||||
const Vector3 dir = viewCenter - eye;
|
||||
Vector3 zAxis = dir.normalized();
|
||||
Vector3 xAxis = (Math::cross(zAxis, upDir.normalized())).normalized();
|
||||
Vector3 yAxis = (Math::cross(xAxis, zAxis)).normalized();
|
||||
xAxis = (Math::cross(zAxis, yAxis)).normalized();
|
||||
|
||||
m_targetPosition = -viewCenter;
|
||||
m_targetZooming = -dir.length();
|
||||
m_targetQRotation =
|
||||
Quaternion::fromMatrix(Matrix3x3{xAxis, yAxis, -zAxis}.transposed())
|
||||
.normalized();
|
||||
|
||||
m_positionT0 = m_currentPosition = m_targetPosition;
|
||||
m_zoomingT0 = m_currentZooming = m_targetZooming;
|
||||
m_qRotationT0 = m_currentQRotation = m_targetQRotation;
|
||||
|
||||
updateInternalTransformations();
|
||||
}
|
||||
|
||||
void ArcBall::reset() {
|
||||
m_targetPosition = m_positionT0;
|
||||
m_targetZooming = m_zoomingT0;
|
||||
m_targetQRotation = m_qRotationT0;
|
||||
}
|
||||
|
||||
void ArcBall::setLagging(const Float lagging) {
|
||||
CORRADE_INTERNAL_ASSERT(lagging >= 0.0f && lagging < 1.0f);
|
||||
m_lagging = lagging;
|
||||
}
|
||||
|
||||
void ArcBall::initTransformation(const Vector2i &mousePos) {
|
||||
m_prevMousePosNDC = screenCoordToNDC(mousePos);
|
||||
}
|
||||
|
||||
void ArcBall::rotate(const Vector2i &mousePos) {
|
||||
const Vector2 mousePosNDC = screenCoordToNDC(mousePos);
|
||||
const Quaternion currentQRotation = ndcToArcBall(mousePosNDC);
|
||||
const Quaternion prevQRotation = ndcToArcBall(m_prevMousePosNDC);
|
||||
m_prevMousePosNDC = mousePosNDC;
|
||||
m_targetQRotation =
|
||||
(currentQRotation * prevQRotation * m_targetQRotation).normalized();
|
||||
}
|
||||
|
||||
void ArcBall::translate(const Vector2i &mousePos) {
|
||||
const Vector2 mousePosNDC = screenCoordToNDC(mousePos);
|
||||
const Vector2 translationNDC = mousePosNDC - m_prevMousePosNDC;
|
||||
m_prevMousePosNDC = mousePosNDC;
|
||||
translateDelta(translationNDC);
|
||||
}
|
||||
|
||||
void ArcBall::translateDelta(const Vector2 &translationNDC) {
|
||||
/* Half size of the screen viewport at the view center and perpendicular
|
||||
with the viewDir */
|
||||
const Float hh = Math::abs(m_targetZooming) * Math::tan(m_fov * 0.5f);
|
||||
const Float hw = hh * Vector2{m_windowSize}.aspectRatio();
|
||||
|
||||
m_targetPosition += m_inverseView.transformVector(
|
||||
{translationNDC.x() * hw, translationNDC.y() * hh, 0.0f});
|
||||
}
|
||||
|
||||
void ArcBall::zoom(const Float delta) { m_targetZooming += delta; }
|
||||
|
||||
bool ArcBall::updateTransformation() {
|
||||
const Vector3 diffViewCenter = m_targetPosition - m_currentPosition;
|
||||
const Quaternion diffRotation = m_targetQRotation - m_currentQRotation;
|
||||
const Float diffZooming = m_targetZooming - m_currentZooming;
|
||||
|
||||
const Float dViewCenter = Math::dot(diffViewCenter, diffViewCenter);
|
||||
const Float dRotation = Math::dot(diffRotation, diffRotation);
|
||||
const Float dZooming = diffZooming * diffZooming;
|
||||
|
||||
/* Nothing change */
|
||||
if (dViewCenter < 1.0e-10f && dRotation < 1.0e-10f && dZooming < 1.0e-10f) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Nearly done: just jump directly to the target */
|
||||
if (dViewCenter < 1.0e-6f && dRotation < 1.0e-6f && dZooming < 1.0e-6f) {
|
||||
m_currentPosition = m_targetPosition;
|
||||
m_currentQRotation = m_targetQRotation;
|
||||
m_currentZooming = m_targetZooming;
|
||||
|
||||
/* Interpolate between the current transformation and the target
|
||||
transformation */
|
||||
} else {
|
||||
const Float t = 1 - m_lagging;
|
||||
m_currentPosition = Math::lerp(m_currentPosition, m_targetPosition, t);
|
||||
m_currentZooming = Math::lerp(m_currentZooming, m_targetZooming, t);
|
||||
m_currentQRotation =
|
||||
Math::slerpShortestPath(m_currentQRotation, m_targetQRotation, t);
|
||||
}
|
||||
|
||||
updateInternalTransformations();
|
||||
return true;
|
||||
}
|
||||
|
||||
void ArcBall::updateInternalTransformations() {
|
||||
m_view = DualQuaternion::translation(Vector3::zAxis(m_currentZooming)) *
|
||||
DualQuaternion{m_currentQRotation} *
|
||||
DualQuaternion::translation(m_currentPosition);
|
||||
m_inverseView = m_view.inverted();
|
||||
}
|
||||
|
||||
Vector2 ArcBall::screenCoordToNDC(const Vector2i &mousePos) const {
|
||||
return {mousePos.x() * 2.0f / m_windowSize.x() - 1.0f,
|
||||
1.0f - 2.0f * mousePos.y() / m_windowSize.y()};
|
||||
}
|
100
src/arc_ball.h
Normal file
100
src/arc_ball.h
Normal file
@@ -0,0 +1,100 @@
|
||||
#ifndef ARCBALL_H
|
||||
#define ARCBALL_H
|
||||
|
||||
#include <Magnum/Magnum.h>
|
||||
#include <Magnum/Math/DualQuaternion.h>
|
||||
#include <Magnum/Math/Functions.h>
|
||||
#include <Magnum/Math/Vector2.h>
|
||||
#include <Magnum/Math/Vector3.h>
|
||||
|
||||
using namespace Magnum;
|
||||
|
||||
Quaternion ndcToArcBall(const Vector2 &p);
|
||||
|
||||
/* Implementation of Ken Shoemake's arcball camera with smooth navigation
|
||||
feature: https://www.talisman.org/~erlkonig/misc/shoemake92-arcball.pdf */
|
||||
class ArcBall {
|
||||
public:
|
||||
ArcBall(const Vector3 &cameraPosition, const Vector3 &viewCenter,
|
||||
const Vector3 &upDir, Deg fov, const Vector2i &windowSize);
|
||||
|
||||
/* Set the camera view parameters: eye position, view center, up
|
||||
direction */
|
||||
void setViewParameters(const Vector3 &eye, const Vector3 &viewCenter,
|
||||
const Vector3 &upDir);
|
||||
|
||||
/* Reset the camera to its initial position, view center, and up dir */
|
||||
void reset();
|
||||
|
||||
/* Update screen size after the window has been resized */
|
||||
void reshape(const Vector2i &windowSize) { m_windowSize = windowSize; }
|
||||
|
||||
/* Update any unfinished transformation due to lagging, return true if
|
||||
the camera matrices have changed */
|
||||
bool updateTransformation();
|
||||
|
||||
/* Get/set the amount of lagging such that the camera will (slowly)
|
||||
smoothly navigate. Lagging must be in [0, 1) */
|
||||
Float lagging() const { return m_lagging; }
|
||||
void setLagging(Float lagging);
|
||||
|
||||
/* Initialize the first (screen) mouse position for camera
|
||||
transformation. This should be called in mouse pressed event. */
|
||||
void initTransformation(const Vector2i &mousePos);
|
||||
|
||||
/* Rotate the camera from the previous (screen) mouse position to the
|
||||
current (screen) position */
|
||||
void rotate(const Vector2i &mousePos);
|
||||
|
||||
/* Translate the camera from the previous (screen) mouse position to
|
||||
the current (screen) mouse position */
|
||||
void translate(const Vector2i &mousePos);
|
||||
|
||||
/* Translate the camera by the delta amount of (NDC) mouse position.
|
||||
Note that NDC position must be in [-1, -1] to [1, 1]. */
|
||||
void translateDelta(const Vector2 &translationNDC);
|
||||
|
||||
/* Zoom the camera (positive delta = zoom in, negative = zoom out) */
|
||||
void zoom(Float delta);
|
||||
|
||||
/* Get the camera's view transformation as a qual quaternion */
|
||||
const DualQuaternion &view() const { return m_view; }
|
||||
|
||||
/* Get the camera's view transformation as a matrix */
|
||||
Matrix4 viewMatrix() const { return m_view.toMatrix(); }
|
||||
|
||||
/* Get the camera's inverse view matrix (which also produces
|
||||
transformation of the camera) */
|
||||
Matrix4 inverseViewMatrix() const { return m_inverseView.toMatrix(); }
|
||||
|
||||
/* Get the camera's transformation as a dual quaternion */
|
||||
const DualQuaternion &transformation() const { return m_inverseView; }
|
||||
|
||||
/* Get the camera's transformation matrix */
|
||||
Matrix4 transformationMatrix() const { return m_inverseView.toMatrix(); }
|
||||
|
||||
/* Return the distance from the camera position to the center view */
|
||||
Float viewDistance() const { return Math::abs(m_targetZooming); }
|
||||
|
||||
protected:
|
||||
/* Update the camera transformations */
|
||||
void updateInternalTransformations();
|
||||
|
||||
/* Transform from screen coordinate to NDC - normalized device
|
||||
coordinate. The top-left of the screen corresponds to [-1, 1] NDC,
|
||||
and the bottom right is [1, -1] NDC. */
|
||||
Vector2 screenCoordToNDC(const Vector2i &mousePos) const;
|
||||
|
||||
Deg m_fov;
|
||||
Vector2i m_windowSize;
|
||||
|
||||
Vector2 m_prevMousePosNDC;
|
||||
Float m_lagging{};
|
||||
|
||||
Vector3 m_targetPosition, m_currentPosition, m_positionT0;
|
||||
Quaternion m_targetQRotation, m_currentQRotation, m_qRotationT0;
|
||||
Float m_targetZooming, m_currentZooming, m_zoomingT0;
|
||||
DualQuaternion m_view, m_inverseView;
|
||||
};
|
||||
|
||||
#endif
|
65
src/arc_ball_camera.h
Normal file
65
src/arc_ball_camera.h
Normal file
@@ -0,0 +1,65 @@
|
||||
#ifndef ARCBALLCAMERA_H
|
||||
#define ARCBALLCAMERA_H
|
||||
|
||||
#include <Magnum/SceneGraph/AbstractTranslationRotation3D.h>
|
||||
#include <Magnum/SceneGraph/Camera.h>
|
||||
#include <Magnum/SceneGraph/Object.h>
|
||||
#include <Magnum/SceneGraph/Scene.h>
|
||||
#include "arc_ball.h"
|
||||
|
||||
using namespace Magnum;
|
||||
|
||||
/* Arcball camera implementation integrated into the SceneGraph */
|
||||
class ArcBallCamera : public ArcBall {
|
||||
public:
|
||||
template <class Transformation>
|
||||
ArcBallCamera(SceneGraph::Scene<Transformation> &scene,
|
||||
const Vector3 &cameraPosition, const Vector3 &viewCenter,
|
||||
const Vector3 &upDir, Deg fov, const Vector2i &windowSize,
|
||||
const Vector2i &viewportSize)
|
||||
: ArcBall{cameraPosition, viewCenter, upDir, fov, windowSize} {
|
||||
/* Create a camera object of a concrete type */
|
||||
auto *cameraObject = new SceneGraph::Object<Transformation>{&scene};
|
||||
(*(m_camera = new SceneGraph::Camera3D{*cameraObject}))
|
||||
.setAspectRatioPolicy(SceneGraph::AspectRatioPolicy::Extend)
|
||||
.setProjectionMatrix(Matrix4::perspectiveProjection(
|
||||
fov, Vector2{windowSize}.aspectRatio(), 0.01f, 100.0f))
|
||||
.setViewport(viewportSize);
|
||||
|
||||
/* Save the abstract transformation interface and initialize the
|
||||
camera position through that */
|
||||
(*(m_cameraObject = cameraObject))
|
||||
.rotate(transformation().rotation())
|
||||
.translate(transformation().translation());
|
||||
}
|
||||
|
||||
/* Update screen and viewport size after the window has been resized */
|
||||
void reshape(const Vector2i &windowSize, const Vector2i &viewportSize) {
|
||||
m_windowSize = windowSize;
|
||||
m_camera->setViewport(viewportSize);
|
||||
}
|
||||
|
||||
/* Update the SceneGraph camera if arcball has been changed */
|
||||
bool update() {
|
||||
/* call the internal update */
|
||||
if (!updateTransformation())
|
||||
return false;
|
||||
|
||||
(*m_cameraObject)
|
||||
.resetTransformation()
|
||||
.rotate(transformation().rotation())
|
||||
.translate(transformation().translation());
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Draw objects using the internal scenegraph camera */
|
||||
void draw(SceneGraph::DrawableGroup3D &drawables) {
|
||||
m_camera->draw(drawables);
|
||||
}
|
||||
|
||||
private:
|
||||
SceneGraph::AbstractTranslationRotation3D *m_cameraObject{};
|
||||
SceneGraph::Camera3D *m_camera{};
|
||||
};
|
||||
|
||||
#endif
|
11
src/glclampf.h
Normal file
11
src/glclampf.h
Normal file
@@ -0,0 +1,11 @@
|
||||
#ifndef GLCLAMPF_H
|
||||
#define GLCLAMPF_H
|
||||
|
||||
#if 1
|
||||
typedef GLfloat GLclampf;
|
||||
#undef __glew_h__ /* shh, Qt, shh */
|
||||
#undef __GLEW_H__
|
||||
#include <QOpenGLFunctions>
|
||||
#endif
|
||||
|
||||
#endif // GLCLAMPF_H
|
126
src/magnum_item.cpp
Normal file
126
src/magnum_item.cpp
Normal file
@@ -0,0 +1,126 @@
|
||||
#include <Magnum/GL/Framebuffer.h>
|
||||
#include <Magnum/Platform/GLContext.h>
|
||||
#include <QtCore/QRunnable>
|
||||
#include <QtQuick/QQuickWindow>
|
||||
#include <QOpenGLFramebufferObject>
|
||||
#include "magnum_item.h"
|
||||
#include "magnum_render.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace Magnum;
|
||||
using namespace Math::Literals;
|
||||
|
||||
class MagnumFBORenderer : public QQuickFramebufferObject::Renderer {
|
||||
public:
|
||||
MagnumFBORenderer(QQuickWindow *window, MagnumRenderer *renderer) :
|
||||
m_window(window),
|
||||
m_renderer(renderer) {}
|
||||
|
||||
void render() override {
|
||||
m_renderer->render();
|
||||
m_window->resetOpenGLState();
|
||||
update();
|
||||
}
|
||||
|
||||
void synchronize(QQuickFramebufferObject *item) override {
|
||||
auto mitem = static_cast<MagnumItem*>(item);
|
||||
m_renderer->t(mitem->t());
|
||||
m_renderer->hue(mitem->hue());
|
||||
m_renderer->count(mitem->count());
|
||||
}
|
||||
|
||||
QOpenGLFramebufferObject *createFramebufferObject(const QSize &size) override {
|
||||
QOpenGLFramebufferObjectFormat format;
|
||||
format.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil);
|
||||
format.setSamples(4);
|
||||
m_fbo = new QOpenGLFramebufferObject(size, format);
|
||||
// ---
|
||||
if (m_ctx == nullptr)
|
||||
m_ctx = new Platform::GLContext();
|
||||
Platform::GLContext::makeCurrent(m_ctx);
|
||||
m_renderer->reset(
|
||||
m_ctx,
|
||||
GL::Framebuffer::wrap(
|
||||
m_fbo->handle(), {{}, {size.width(),
|
||||
size.height()}}),
|
||||
Vector2i{m_window->width(), m_window->height()},
|
||||
Vector2i{size.width(), size.height()});
|
||||
return m_fbo;
|
||||
}
|
||||
|
||||
private:
|
||||
QQuickWindow *m_window;
|
||||
QOpenGLFramebufferObject *m_fbo;
|
||||
Platform::GLContext *m_ctx{};
|
||||
MagnumRenderer *m_renderer;
|
||||
};
|
||||
|
||||
MagnumItem::MagnumItem() : m_renderer(new MagnumRenderer()) {
|
||||
setMirrorVertically(true);
|
||||
setAcceptHoverEvents(true);
|
||||
setAcceptedMouseButtons(Qt::AllButtons);
|
||||
setFlag(ItemAcceptsInputMethod, true);
|
||||
}
|
||||
|
||||
QQuickFramebufferObject::Renderer *MagnumItem::createRenderer() const {
|
||||
return new MagnumFBORenderer(window(), m_renderer);
|
||||
}
|
||||
|
||||
void MagnumItem::setT(qreal t) {
|
||||
if (t == m_t) return;
|
||||
m_t = t;
|
||||
emit tChanged();
|
||||
update();
|
||||
}
|
||||
|
||||
void MagnumItem::setHue(qreal hue) {
|
||||
if (hue == m_hue) return;
|
||||
m_hue = hue;
|
||||
emit hueChanged();
|
||||
update();
|
||||
}
|
||||
|
||||
void MagnumItem::setCount(qint8 count) {
|
||||
if (count == m_count) return;
|
||||
m_count = count;
|
||||
emit countChanged();
|
||||
update();
|
||||
}
|
||||
|
||||
void MagnumItem::keyPressEvent(QKeyEvent *evt) {
|
||||
switch (evt->key()) {
|
||||
case Qt::Key_L:
|
||||
// if (m_camera->lagging() > 0.0f) {
|
||||
// m_camera->setLagging(0.0f);
|
||||
// } else {
|
||||
// m_camera->setLagging(0.85f);
|
||||
// }
|
||||
break;
|
||||
}
|
||||
evt->accept();
|
||||
}
|
||||
|
||||
void MagnumItem::mousePressEvent(QMouseEvent *evt) {
|
||||
}
|
||||
|
||||
void MagnumItem::mouseReleaseEvent(QMouseEvent *evt) {
|
||||
}
|
||||
|
||||
void MagnumItem::mouseMoveEvent(QMouseEvent *evt) {}
|
||||
|
||||
void MagnumItem::wheelEvent(QWheelEvent *evt) {}
|
||||
|
||||
class CleanupJob : public QRunnable {
|
||||
public:
|
||||
CleanupJob(MagnumRenderer *renderer) : m_renderer(renderer) {}
|
||||
void run() override { delete m_renderer; }
|
||||
private:
|
||||
MagnumRenderer *m_renderer;
|
||||
};
|
||||
|
||||
void MagnumItem::releaseResources() {
|
||||
window()->scheduleRenderJob(
|
||||
new CleanupJob(m_renderer),
|
||||
QQuickWindow::BeforeSynchronizingStage);
|
||||
m_renderer = nullptr;
|
||||
}
|
55
src/magnum_item.h
Normal file
55
src/magnum_item.h
Normal file
@@ -0,0 +1,55 @@
|
||||
#ifndef MAGNUM_ITEM_H
|
||||
#define MAGNUM_ITEM_H
|
||||
|
||||
#include <Magnum/GL/Framebuffer.h>
|
||||
#include <Magnum/Platform/GLContext.h>
|
||||
#include "glclampf.h"
|
||||
#include <QOpenGLFunctions>
|
||||
#include <QtQuick/QQuickFramebufferObject>
|
||||
|
||||
// Forward declaring the renderer to avoid CMake's AUTOMOC
|
||||
// to process Magnum's headers (where it fails)
|
||||
|
||||
class MagnumRenderer;
|
||||
|
||||
class MagnumItem : public QQuickFramebufferObject {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(qreal t READ t WRITE setT NOTIFY tChanged)
|
||||
Q_PROPERTY(qreal hue READ hue WRITE setHue NOTIFY hueChanged)
|
||||
Q_PROPERTY(qreal count READ count WRITE setCount NOTIFY countChanged)
|
||||
public:
|
||||
MagnumItem();
|
||||
Renderer *createRenderer() const override;
|
||||
|
||||
inline qreal t() const { return m_t; }
|
||||
void setT(qreal t);
|
||||
|
||||
inline qreal hue() const { return m_hue; }
|
||||
void setHue(qreal hue);
|
||||
|
||||
inline qint8 count() const { return m_count; }
|
||||
void setCount(qint8 count);
|
||||
|
||||
signals:
|
||||
void tChanged();
|
||||
void hueChanged();
|
||||
void countChanged();
|
||||
|
||||
protected:
|
||||
void keyPressEvent(QKeyEvent *evt) override;
|
||||
void mousePressEvent(QMouseEvent *evt) override;
|
||||
void mouseReleaseEvent(QMouseEvent *evt) override;
|
||||
void mouseMoveEvent(QMouseEvent *evt) override;
|
||||
void wheelEvent(QWheelEvent *evt) override;
|
||||
|
||||
private:
|
||||
void releaseResources() override;
|
||||
|
||||
qreal m_t;
|
||||
qreal m_hue;
|
||||
qint8 m_count;
|
||||
|
||||
MagnumRenderer *m_renderer;
|
||||
};
|
||||
|
||||
#endif // MAGNUM_ITEM_H
|
149
src/magnum_render.cpp
Normal file
149
src/magnum_render.cpp
Normal file
@@ -0,0 +1,149 @@
|
||||
#include <Corrade/Utility/DebugStl.h>
|
||||
#include <Magnum/GL/Framebuffer.h>
|
||||
#include <Magnum/GL/Renderer.h>
|
||||
#include <Magnum/Math/Color.h>
|
||||
#include <Magnum/MeshTools/Compile.h>
|
||||
#include <Magnum/Platform/GLContext.h>
|
||||
#include <Magnum/Primitives/Cube.h>
|
||||
#include <Magnum/Primitives/Grid.h>
|
||||
#include <Magnum/Trade/MeshData.h>
|
||||
#include "magnum_render.h"
|
||||
|
||||
using namespace Magnum;
|
||||
using namespace Math::Literals;
|
||||
|
||||
static const auto HUE_BG = 210.0_degf;
|
||||
static const auto COLOR_BG = Color4::fromHsv({HUE_BG, .3f, .9f}, 1.f);
|
||||
static const auto COLOR_GRID = Color4::fromHsv({HUE_BG, .3f, .7f}, 1.f);
|
||||
|
||||
MagnumRenderer::MagnumRenderer() {}
|
||||
|
||||
MagnumRenderer::~MagnumRenderer() {}
|
||||
|
||||
void MagnumRenderer::t(const float t) {
|
||||
if (m_subjectObject != nullptr) {
|
||||
m_subjectObject->resetTransformation();
|
||||
m_subjectObject->translate(Vector3{0.f, 1.f, 0.f});
|
||||
m_subjectObject->rotateY(Rad(t*Math::Constants<float>::pi()));
|
||||
}
|
||||
}
|
||||
|
||||
void MagnumRenderer::hue(float hue) {
|
||||
if (m_subjectShader.id()) {
|
||||
auto const color = Color3::fromHsv({Rad(hue), 1.f, 1.f});
|
||||
m_subjectShader
|
||||
.setDiffuseColor(color)
|
||||
.setAmbientColor(Color3::fromHsv({color.hue(), 1.0f, 0.3f}));
|
||||
}
|
||||
}
|
||||
|
||||
void MagnumRenderer::count(int count) {
|
||||
m_count = count;
|
||||
}
|
||||
|
||||
void MagnumRenderer::lazyInitialize() {
|
||||
if (m_init) return;
|
||||
|
||||
// The Cube
|
||||
{
|
||||
m_subject = MeshTools::compile(Primitives::cubeSolid());
|
||||
m_subjectObject = new Object3D{&m_scene};
|
||||
(*m_subjectObject).translate(Vector3{0.f, 1.f, 0.f});
|
||||
|
||||
m_subjectShader = Shaders::Phong{};
|
||||
m_subjectShader
|
||||
.setShininess(20.f)
|
||||
.setLightColor(Color3{1.f})
|
||||
.setLightPosition(Vector3{-100.f, 100.f, 50.f});
|
||||
|
||||
new SubjectDrawable{*m_subjectObject, m_subjectShader, m_subject, m_drawables};
|
||||
}
|
||||
|
||||
// The Grid
|
||||
{
|
||||
m_grid = MeshTools::compile(Primitives::grid3DWireframe({15, 15}));
|
||||
auto obj = new Object3D{&m_scene};
|
||||
(*obj).rotateX(90.0_degf).scale(Vector3{8.0f});
|
||||
|
||||
m_gridShader = Shaders::Flat3D{};
|
||||
m_gridShader.setColor(COLOR_GRID);
|
||||
|
||||
new GridDrawable{*obj, m_gridShader, m_grid, m_drawables};
|
||||
}
|
||||
|
||||
// The Camera
|
||||
{
|
||||
const Vector3 eye{-10.0f, 8.f, -6.f};
|
||||
const Vector3 center{0.f, 1.f, 0.f};
|
||||
const Vector3 up = Vector3::yAxis();
|
||||
m_camera.emplace(m_scene, eye, center, up, 45.0_degf,
|
||||
Vector2i{100, 100}, Vector2i{100, 100});
|
||||
}
|
||||
|
||||
m_init = true;
|
||||
}
|
||||
|
||||
void MagnumRenderer::reset(Platform::GLContext *ctx,
|
||||
GL::Framebuffer fbo,
|
||||
Vector2i windowSize,
|
||||
Vector2i viewportSize) {
|
||||
lazyInitialize();
|
||||
m_camera->reshape(windowSize, viewportSize);
|
||||
m_FBO = std::move(fbo);
|
||||
m_ctx = ctx;
|
||||
}
|
||||
|
||||
void MagnumRenderer::prepGLState() {
|
||||
GL::Renderer::setClearColor(COLOR_BG);
|
||||
GL::Renderer::enable(GL::Renderer::Feature::DepthTest);
|
||||
GL::Renderer::enable(GL::Renderer::Feature::FaceCulling);
|
||||
GL::Renderer::enable(GL::Renderer::Feature::Multisampling);
|
||||
|
||||
GL::Renderer::setDepthFunction(GL::Renderer::DepthFunction::LessOrEqual);
|
||||
GL::Renderer::setBlendEquation(GL::Renderer::BlendEquation::Add,
|
||||
GL::Renderer::BlendEquation::Add);
|
||||
GL::Renderer::setBlendFunction(
|
||||
GL::Renderer::BlendFunction::One,
|
||||
GL::Renderer::BlendFunction::OneMinusSourceAlpha);
|
||||
GL::Renderer::enable(GL::Renderer::Feature::Blending);
|
||||
}
|
||||
|
||||
void MagnumRenderer::render() {
|
||||
m_ctx->resetState(GL::Context::State::ExitExternal);
|
||||
prepGLState();
|
||||
// ---
|
||||
|
||||
m_FBO.bind();
|
||||
m_FBO.clear(GL::FramebufferClear::Color |
|
||||
GL::FramebufferClear::Depth);
|
||||
|
||||
m_camera->update();
|
||||
m_camera->draw(m_drawables);
|
||||
|
||||
/** Use instead this to limit rendering to camera changes
|
||||
* bool cameraChanged = m_camera->update();
|
||||
* m_camera->draw(m_drawables);
|
||||
* if (cameraChanged) {
|
||||
* redraw();
|
||||
* } */
|
||||
|
||||
// ---
|
||||
m_ctx->resetState(GL::Context::State::EnterExternal);
|
||||
}
|
||||
|
||||
void GridDrawable::draw(const Matrix4 &transformation,
|
||||
SceneGraph::Camera3D &camera) {
|
||||
m_shader
|
||||
.setTransformationProjectionMatrix(
|
||||
camera.projectionMatrix()*transformation)
|
||||
.draw(m_mesh);
|
||||
}
|
||||
|
||||
void SubjectDrawable::draw(const Matrix4 &transformation,
|
||||
SceneGraph::Camera3D &camera) {
|
||||
m_shader
|
||||
.setTransformationMatrix(transformation)
|
||||
.setNormalMatrix(transformation.normalMatrix())
|
||||
.setProjectionMatrix(camera.projectionMatrix())
|
||||
.draw(m_mesh);
|
||||
}
|
91
src/magnum_render.h
Normal file
91
src/magnum_render.h
Normal file
@@ -0,0 +1,91 @@
|
||||
#ifndef MAGNUM_RENDER_H
|
||||
#define MAGNUM_RENDER_H
|
||||
|
||||
#include <Corrade/Containers/Optional.h>
|
||||
#include <Magnum/GL/Framebuffer.h>
|
||||
#include <Magnum/GL/Mesh.h>
|
||||
#include <Magnum/Math/Vector2.h>
|
||||
#include <Magnum/Platform/GLContext.h>
|
||||
#include <Magnum/SceneGraph/Drawable.h>
|
||||
#include <Magnum/SceneGraph/MatrixTransformation3D.h>
|
||||
#include <Magnum/SceneGraph/Object.h>
|
||||
#include <Magnum/SceneGraph/Scene.h>
|
||||
#include <Magnum/Shaders/Flat.h>
|
||||
#include <Magnum/Shaders/Phong.h>
|
||||
#include "arc_ball_camera.h"
|
||||
|
||||
using namespace Magnum;
|
||||
|
||||
using Object3D = SceneGraph::Object<SceneGraph::MatrixTransformation3D>;
|
||||
using Scene3D = SceneGraph::Scene<SceneGraph::MatrixTransformation3D>;
|
||||
|
||||
class MagnumRenderer {
|
||||
public:
|
||||
MagnumRenderer();
|
||||
~MagnumRenderer();
|
||||
|
||||
void reset(Platform::GLContext *ctx,
|
||||
GL::Framebuffer fbo,
|
||||
Vector2i windowSize,
|
||||
Vector2i viewportSize);
|
||||
|
||||
void render();
|
||||
|
||||
void t(float t);
|
||||
void hue(float hue);
|
||||
void count(int count);
|
||||
|
||||
private:
|
||||
void lazyInitialize();
|
||||
void prepGLState();
|
||||
|
||||
bool m_init{false};
|
||||
|
||||
float m_count{1};
|
||||
|
||||
Platform::GLContext *m_ctx;
|
||||
GL::Framebuffer m_FBO{NoCreate};
|
||||
|
||||
Scene3D m_scene;
|
||||
SceneGraph::DrawableGroup3D m_drawables;
|
||||
Containers::Optional<ArcBallCamera> m_camera;
|
||||
|
||||
GL::Mesh m_subject{NoCreate};
|
||||
Shaders::Phong m_subjectShader{NoCreate};
|
||||
Object3D* m_subjectObject{nullptr};
|
||||
|
||||
GL::Mesh m_grid{NoCreate};
|
||||
Shaders::Flat3D m_gridShader{NoCreate};
|
||||
};
|
||||
|
||||
class GridDrawable : public SceneGraph::Drawable3D {
|
||||
public:
|
||||
explicit GridDrawable(Object3D &object, Shaders::Flat3D &shader,
|
||||
GL::Mesh &mesh, SceneGraph::DrawableGroup3D &drawables)
|
||||
: SceneGraph::Drawable3D{object, &drawables},
|
||||
m_shader(shader),
|
||||
m_mesh(mesh) {}
|
||||
|
||||
void draw(const Matrix4 &transformation, SceneGraph::Camera3D &camera);
|
||||
|
||||
private:
|
||||
Shaders::Flat3D &m_shader;
|
||||
GL::Mesh &m_mesh;
|
||||
};
|
||||
|
||||
class SubjectDrawable : public SceneGraph::Drawable3D {
|
||||
public:
|
||||
explicit SubjectDrawable(Object3D &object, Shaders::Phong &shader,
|
||||
GL::Mesh &mesh, SceneGraph::DrawableGroup3D &drawables)
|
||||
: SceneGraph::Drawable3D{object, &drawables},
|
||||
m_shader(shader),
|
||||
m_mesh(mesh) {}
|
||||
|
||||
void draw(const Matrix4 &transformation, SceneGraph::Camera3D &camera);
|
||||
|
||||
private:
|
||||
Shaders::Phong &m_shader;
|
||||
GL::Mesh &m_mesh;
|
||||
};
|
||||
|
||||
#endif // MAGNUM_RENDER_H
|
36
src/main.cpp
Normal file
36
src/main.cpp
Normal file
@@ -0,0 +1,36 @@
|
||||
#include <Corrade/Containers/Optional.h>
|
||||
#include <Magnum/GL/Framebuffer.h>
|
||||
#include <Magnum/Platform/GLContext.h>
|
||||
|
||||
#include "magnum_item.h"
|
||||
#include "glclampf.h"
|
||||
|
||||
#include <QGuiApplication>
|
||||
#include <QQmlApplicationEngine>
|
||||
#include <QLoggingCategory>
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
// Enable AA
|
||||
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
|
||||
QGuiApplication::setAttribute(Qt::AA_UseOpenGLES);
|
||||
|
||||
// Initialize Qt application
|
||||
QGuiApplication app(argc, argv);
|
||||
|
||||
// Enable logging
|
||||
QLoggingCategory::setFilterRules("qt.scenegraph.general=true");
|
||||
|
||||
// Manually register custom types
|
||||
qmlRegisterType<MagnumItem>("Magnum", 1, 0, "Magnum");
|
||||
|
||||
QQmlApplicationEngine engine;
|
||||
const QUrl url(QStringLiteral("qrc:/main.qml"));
|
||||
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
|
||||
&app, [url](QObject *obj, const QUrl &objUrl) {
|
||||
if (!obj && url == objUrl)
|
||||
QCoreApplication::exit(-1);
|
||||
}, Qt::QueuedConnection);
|
||||
engine.load(url);
|
||||
|
||||
return app.exec();
|
||||
}
|
103
src/qml/main.qml
Normal file
103
src/qml/main.qml
Normal file
@@ -0,0 +1,103 @@
|
||||
import QtQuick 2.12
|
||||
import QtQuick.Controls 2.5
|
||||
import QtQuick.Layouts 1.3
|
||||
import Magnum 1.0
|
||||
|
||||
ApplicationWindow {
|
||||
visible: true
|
||||
width: 640
|
||||
height: 480
|
||||
title: qsTr("My Application")
|
||||
|
||||
RowLayout {
|
||||
id: root
|
||||
anchors.fill: parent
|
||||
spacing: 0
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillHeight: true
|
||||
Layout.preferredWidth: parent.width * 0.25
|
||||
Layout.alignment: Qt.AlignLeft | Qt.AlignTop
|
||||
Layout.margins: 20
|
||||
spacing: 20
|
||||
|
||||
Label {
|
||||
text: "Controls"
|
||||
font.pointSize: 14
|
||||
}
|
||||
GroupBox {
|
||||
Layout.preferredWidth: parent.width
|
||||
|
||||
RowLayout {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
Label {
|
||||
Layout.preferredWidth: parent.width/4
|
||||
text: "Hue"
|
||||
}
|
||||
Label {
|
||||
text: qsTr("%1°").arg(Math.round((180/Math.PI)*hue.value))
|
||||
color: palette.highlight
|
||||
}
|
||||
Slider {
|
||||
id: hue
|
||||
anchors.right: parent.right
|
||||
Layout.preferredWidth: parent.width/2
|
||||
|
||||
from: 0
|
||||
to: Math.PI*2
|
||||
}
|
||||
}
|
||||
}
|
||||
GroupBox {
|
||||
Layout.preferredWidth: parent.width
|
||||
|
||||
RowLayout {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
Label {
|
||||
Layout.preferredWidth: parent.width/4
|
||||
text: "Count"
|
||||
}
|
||||
Label {
|
||||
text: count.value
|
||||
color: palette.highlight
|
||||
}
|
||||
Slider {
|
||||
id: count
|
||||
anchors.right: parent.right
|
||||
Layout.preferredWidth: parent.width/2
|
||||
|
||||
from: 1
|
||||
to: 5
|
||||
stepSize: 1
|
||||
snapMode: Slider.SnapAlways
|
||||
}
|
||||
}
|
||||
}
|
||||
Item {
|
||||
Layout.preferredWidth: parent.width
|
||||
|
||||
CheckBox {
|
||||
text: "Lagging"
|
||||
}
|
||||
}
|
||||
}
|
||||
Magnum {
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
|
||||
hue: hue.value
|
||||
count: count.value
|
||||
|
||||
SequentialAnimation on t {
|
||||
NumberAnimation { to: 1; duration: 2500; easing.type: Easing.InQuad }
|
||||
NumberAnimation { to: 0; duration: 2500; easing.type: Easing.OutQuad }
|
||||
loops: Animation.Infinite
|
||||
running: true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
6
src/qml/main.qrc
Normal file
6
src/qml/main.qrc
Normal file
@@ -0,0 +1,6 @@
|
||||
<RCC>
|
||||
<qresource prefix="/">
|
||||
<file>main.qml</file>
|
||||
<file>qtquickcontrols2.conf</file>
|
||||
</qresource>
|
||||
</RCC>
|
9
src/qml/qtquickcontrols2.conf
Normal file
9
src/qml/qtquickcontrols2.conf
Normal file
@@ -0,0 +1,9 @@
|
||||
; This file can be edited to change the style of the application
|
||||
; Read "Qt Quick Controls 2 Configuration File" for details:
|
||||
; http://doc.qt.io/qt-5/qtquickcontrols2-configuration.html
|
||||
|
||||
[Controls]
|
||||
Style=Fusion
|
||||
|
||||
[Fusion\Palette]
|
||||
Highlight=#ff0000
|
Reference in New Issue
Block a user