summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPaul Lemire <paul.lemire@kdab.com>2019-08-07 07:47:31 +0200
committerPaul Lemire <paul.lemire@kdab.com>2019-08-07 07:47:31 +0200
commit8af94d9a048f7d26a8137c3c51dcf47605fcf4b6 (patch)
treee5ef1654a217bf82f0bb3549d1c8f1762f1a9f25
parent44470868450c440ca874ba585e2b71dfc8e89f1d (diff)
parent7be8029d4c2cd0ce24f0277d6ee87041161edab4 (diff)
Merge remote-tracking branch 'qt-gerrit/dev' into wip/refactor
-rw-r--r--src/animation/backend/animationutils.cpp37
-rw-r--r--src/animation/backend/fcurve.cpp11
-rw-r--r--src/animation/backend/fcurve_p.h2
-rw-r--r--src/core/qchangearbiter.cpp21
-rw-r--r--src/core/qchangearbiter_p.h2
-rw-r--r--src/core/services/qeventfilterservice.cpp29
-rw-r--r--src/doc/qt3d.qdocconf1
-rw-r--r--src/doc/src/qmlextramaterials.qdoc20
-rw-r--r--src/extras/defaults/qdiffusemapmaterial.cpp3
-rw-r--r--src/extras/defaults/qdiffusespecularmapmaterial.cpp4
-rw-r--r--src/extras/defaults/qdiffusespecularmaterial.cpp8
-rw-r--r--src/extras/defaults/qmetalroughmaterial.cpp8
-rw-r--r--src/extras/defaults/qnormaldiffusemapmaterial.cpp4
-rw-r--r--src/extras/defaults/qnormaldiffusespecularmapmaterial.cpp4
-rw-r--r--src/extras/defaults/qskyboxentity.cpp45
-rw-r--r--src/input/frontend/qmouseevent.h4
-rw-r--r--src/logic/qframeaction.cpp4
-rw-r--r--src/plugins/sceneparsers/assimp/assimphelpers.cpp57
-rw-r--r--src/plugins/sceneparsers/assimp/assimphelpers.h6
-rw-r--r--src/plugins/sceneparsers/assimp/assimpimporter.h2
-rw-r--r--src/plugins/sceneparsers/gltf/gltfimporter.cpp11
-rw-r--r--src/plugins/sceneparsers/gltf/gltfparser.cpp1560
-rw-r--r--src/quick3d/imports/scene3d/scene3drenderer.cpp1
-rw-r--r--src/render/backend/commandexecuter.cpp2
-rw-r--r--src/render/backend/managers.cpp6
-rw-r--r--src/render/backend/managers_p.h1
-rw-r--r--src/render/backend/uniform.cpp6
-rw-r--r--src/render/framegraph/qclearbuffers.cpp2
-rw-r--r--src/render/frontend/qcamera.cpp103
-rw-r--r--src/render/geometry/joint.cpp1
-rw-r--r--src/render/lights/qdirectionallight.cpp6
-rw-r--r--src/render/lights/qenvironmentlight.cpp6
-rw-r--r--src/render/lights/qpointlight.cpp27
-rw-r--r--src/render/lights/qspotlight.cpp24
-rw-r--r--src/render/materialsystem/qshaderimage_p.h11
-rw-r--r--src/render/materialsystem/shaderdata.cpp2
-rw-r--r--src/render/raycasting/qray3d.cpp8
-rw-r--r--src/render/raycasting/qraycastingservice.cpp1
-rw-r--r--src/render/renderers/opengl/jobs/renderviewjobutils.cpp14
-rw-r--r--src/render/renderers/opengl/jobs/renderviewjobutils_p.h6
-rw-r--r--src/render/renderers/opengl/renderer/renderer.cpp2
-rw-r--r--src/render/renderers/opengl/renderer/renderview.cpp22
-rw-r--r--src/render/renderers/opengl/renderstates/renderstateset.cpp15
-rw-r--r--src/render/renderers/opengl/renderstates/renderstateset_p.h3
-rw-r--r--src/render/renderstates/genericstate_p.h2
-rw-r--r--src/render/renderstates/renderstatenode_p.h2
-rw-r--r--src/render/texture/qtextureimage.cpp4
-rw-r--r--tests/auto/render/gltexture/tst_gltexture.cpp38
-rw-r--r--tests/auto/render/qray3d/tst_qray3d.cpp36
-rw-r--r--tests/auto/render/qrenderstate/tst_qrenderstate.cpp8
-rw-r--r--tests/manual/manual.pro1
-rw-r--r--tests/manual/qtbug-76766/FrameGraph.qml140
-rw-r--r--tests/manual/qtbug-76766/Material1.qml93
-rw-r--r--tests/manual/qtbug-76766/Material2.qml105
-rw-r--r--tests/manual/qtbug-76766/PostProcess.qml117
-rw-r--r--tests/manual/qtbug-76766/SceneRoot.qml176
-rw-r--r--tests/manual/qtbug-76766/expected_output.pngbin0 -> 22639 bytes
-rw-r--r--tests/manual/qtbug-76766/main.cpp74
-rw-r--r--tests/manual/qtbug-76766/main.qml76
-rw-r--r--tests/manual/qtbug-76766/qml.qrc11
-rw-r--r--tests/manual/qtbug-76766/qtbug-76766.pro10
-rw-r--r--tests/manual/qtbug-76766/shaders.qrc6
-rw-r--r--tests/manual/qtbug-76766/shaders/shader.frag11
-rw-r--r--tests/manual/qtbug-76766/shaders/shader.vert36
64 files changed, 1342 insertions, 1716 deletions
diff --git a/src/animation/backend/animationutils.cpp b/src/animation/backend/animationutils.cpp
index 29de69df6..92e614236 100644
--- a/src/animation/backend/animationutils.cpp
+++ b/src/animation/backend/animationutils.cpp
@@ -55,6 +55,10 @@
QT_BEGIN_NAMESPACE
+namespace {
+const auto slerpThreshold = 0.01f;
+}
+
namespace Qt3DAnimation {
namespace Animation {
@@ -270,17 +274,38 @@ ClipResults evaluateClipAtLocalTime(AnimationClip *clip, float localTime)
const int lowerKeyframeBound = channel.channelComponents[0].fcurve.lowerKeyframeBound(localTime);
const auto lowerQuat = quaternionFromChannel(lowerKeyframeBound);
const auto higherQuat = quaternionFromChannel(lowerKeyframeBound + 1);
- const float omega = std::acos(qBound(-1.0f, QQuaternion::dotProduct(lowerQuat, higherQuat), 1.0f));
-
- if (qFuzzyIsNull(omega)) {
- // If the two keyframe quaternions are equal, just return the first one as the interpolated value.
+ auto cosHalfTheta = QQuaternion::dotProduct(lowerQuat, higherQuat);
+ // If the two keyframe quaternions are equal, just return the first one as the interpolated value.
+ if (std::abs(cosHalfTheta) >= 1.0f) {
channelResults[i++] = lowerQuat.scalar();
channelResults[i++] = lowerQuat.x();
channelResults[i++] = lowerQuat.y();
channelResults[i++] = lowerQuat.z();
} else {
- for (const auto &channelComponent : qAsConst(channel.channelComponents))
- channelResults[i++] = channelComponent.fcurve.evaluateAtTimeAsSlerp(localTime, lowerKeyframeBound, omega);
+ const auto sinHalfTheta = std::sqrt(1.0f - std::pow(cosHalfTheta,2.0f));
+ if (std::abs(sinHalfTheta) < ::slerpThreshold) {
+ auto initial_i = i;
+ for (const auto &channelComponent : qAsConst(channel.channelComponents))
+ channelResults[i++] = channelComponent.fcurve.evaluateAtTime(localTime, lowerKeyframeBound);
+
+ // Normalize the resulting quaternion
+ QQuaternion quat{channelResults[initial_i], channelResults[initial_i+1], channelResults[initial_i+2], channelResults[initial_i+3]};
+ quat.normalize();
+ channelResults[initial_i+0] = quat.scalar();
+ channelResults[initial_i+1] = quat.x();
+ channelResults[initial_i+2] = quat.y();
+ channelResults[initial_i+3] = quat.z();
+ } else {
+ const auto reverseQ1 = cosHalfTheta < 0 ? -1.0f : 1.0f;
+ cosHalfTheta *= reverseQ1;
+ const auto halfTheta = std::acos(cosHalfTheta);
+ for (const auto &channelComponent : qAsConst(channel.channelComponents))
+ channelResults[i++] = channelComponent.fcurve.evaluateAtTimeAsSlerp(localTime,
+ lowerKeyframeBound,
+ halfTheta,
+ sinHalfTheta,
+ reverseQ1);
+ }
}
}
}
diff --git a/src/animation/backend/fcurve.cpp b/src/animation/backend/fcurve.cpp
index 490866d54..18f1f427e 100644
--- a/src/animation/backend/fcurve.cpp
+++ b/src/animation/backend/fcurve.cpp
@@ -97,7 +97,7 @@ float FCurve::evaluateAtTime(float localTime, int lowerBound) const
return m_keyframes.first().value;
}
-float FCurve::evaluateAtTimeAsSlerp(float localTime, int lowerBound, float omega) const
+float FCurve::evaluateAtTimeAsSlerp(float localTime, int lowerBound, float halfTheta, float sinHalfTheta, float reverseQ1) const
{
// TODO: Implement extrapolation beyond first/last keyframes
if (localTime < m_localTimes.first())
@@ -119,10 +119,11 @@ float FCurve::evaluateAtTimeAsSlerp(float localTime, int lowerBound, float omega
return keyframe0.value;
case QKeyFrame::LinearInterpolation:
if (localTime >= t0 && localTime <= t1 && t1 > t0) {
- const float t = (localTime - t0) / (t1 - t0);
- const float div = 1.0f / std::sin(omega);
- return std::sin((1 - t) * omega) * div * keyframe0.value +
- std::sin(t * omega) * div * keyframe1.value;
+ const auto t = (localTime - t0) / (t1 - t0);
+
+ const auto A = std::sin((1.0f-t) * halfTheta) / sinHalfTheta;
+ const auto B = std::sin(t * halfTheta) / sinHalfTheta;
+ return A * keyframe0.value + reverseQ1 * B * keyframe1.value;
}
break;
case QKeyFrame::BezierInterpolation:
diff --git a/src/animation/backend/fcurve_p.h b/src/animation/backend/fcurve_p.h
index 337eb615d..935db5922 100644
--- a/src/animation/backend/fcurve_p.h
+++ b/src/animation/backend/fcurve_p.h
@@ -84,7 +84,7 @@ public:
float evaluateAtTime(float localTime) const;
float evaluateAtTime(float localTime, int lowerBound) const;
- float evaluateAtTimeAsSlerp(float localTime, int lowerBound, float omega) const;
+ float evaluateAtTimeAsSlerp(float localTime, int lowerBound, float halfTheta, float sinHalfTheta, float reverseQ1) const;
int lowerKeyframeBound(float localTime) const;
void read(const QJsonObject &json);
diff --git a/src/core/qchangearbiter.cpp b/src/core/qchangearbiter.cpp
index e0ee389c4..8cfc4b066 100644
--- a/src/core/qchangearbiter.cpp
+++ b/src/core/qchangearbiter.cpp
@@ -51,6 +51,8 @@
#include <Qt3DCore/private/qscene_p.h>
#include <Qt3DCore/private/qsceneobserverinterface_p.h>
+#include <mutex>
+
QT_BEGIN_NAMESPACE
namespace Qt3DCore {
@@ -76,7 +78,6 @@ namespace Qt3DCore {
*/
QChangeArbiter::QChangeArbiter(QObject *parent)
: QObject(parent)
- , m_mutex(QMutex::Recursive)
, m_jobManager(nullptr)
, m_postman(nullptr)
, m_scene(nullptr)
@@ -151,31 +152,31 @@ QThreadStorage<QChangeArbiter::QChangeQueue *> *QChangeArbiter::tlsChangeQueue()
void QChangeArbiter::appendChangeQueue(QChangeArbiter::QChangeQueue *queue)
{
- QMutexLocker locker(&m_mutex);
+ const std::lock_guard<QRecursiveMutex> locker(m_mutex);;
m_changeQueues.append(queue);
}
void QChangeArbiter::removeChangeQueue(QChangeArbiter::QChangeQueue *queue)
{
- QMutexLocker locker(&m_mutex);
+ const std::lock_guard<QRecursiveMutex> locker(m_mutex);;
m_changeQueues.removeOne(queue);
}
void QChangeArbiter::appendLockingChangeQueue(QChangeArbiter::QChangeQueue *queue)
{
- QMutexLocker locker(&m_mutex);
+ const std::lock_guard<QRecursiveMutex> locker(m_mutex);;
m_lockingChangeQueues.append(queue);
}
void QChangeArbiter::removeLockingChangeQueue(QChangeArbiter::QChangeQueue *queue)
{
- QMutexLocker locker(&m_mutex);
+ const std::lock_guard<QRecursiveMutex> locker(m_mutex);;
m_lockingChangeQueues.removeOne(queue);
}
void QChangeArbiter::syncChanges()
{
- QMutexLocker locker(&m_mutex);
+ const std::lock_guard<QRecursiveMutex> locker(m_mutex);;
for (QChangeArbiter::QChangeQueue *changeQueue : qAsConst(m_changeQueues))
distributeQueueChanges(changeQueue);
@@ -202,7 +203,7 @@ void QChangeArbiter::registerObserver(QObserverInterface *observer,
QNodeId nodeId,
ChangeFlags changeFlags)
{
- QMutexLocker locker(&m_mutex);
+ const std::lock_guard<QRecursiveMutex> locker(m_mutex);;
QObserverList &observerList = m_nodeObservations[nodeId];
observerList.append(QObserverPair(changeFlags, observer));
}
@@ -216,7 +217,7 @@ void QChangeArbiter::registerSceneObserver(QSceneObserverInterface *observer)
void QChangeArbiter::unregisterObserver(QObserverInterface *observer, QNodeId nodeId)
{
- QMutexLocker locker(&m_mutex);
+ const std::lock_guard<QRecursiveMutex> locker(m_mutex);;
const auto it = m_nodeObservations.find(nodeId);
if (it != m_nodeObservations.end()) {
QObserverList &observers = it.value();
@@ -251,13 +252,13 @@ void QChangeArbiter::sceneChangeEvent(const QSceneChangePtr &e)
void QChangeArbiter::sceneChangeEventWithLock(const QSceneChangePtr &e)
{
- QMutexLocker locker(&m_mutex);
+ const std::lock_guard<QRecursiveMutex> locker(m_mutex);;
sceneChangeEvent(e);
}
void QChangeArbiter::sceneChangeEventWithLock(const QSceneChangeList &e)
{
- QMutexLocker locker(&m_mutex);
+ const std::lock_guard<QRecursiveMutex> locker(m_mutex);;
QChangeQueue *localChangeQueue = m_tlsChangeQueue.localData();
qCDebug(ChangeArbiter) << Q_FUNC_INFO << "Handles " << e.size() << " changes at once";
localChangeQueue->insert(localChangeQueue->end(), e.begin(), e.end());
diff --git a/src/core/qchangearbiter_p.h b/src/core/qchangearbiter_p.h
index ac52273ea..1f453ff56 100644
--- a/src/core/qchangearbiter_p.h
+++ b/src/core/qchangearbiter_p.h
@@ -137,7 +137,7 @@ protected:
void removeLockingChangeQueue(QChangeQueue *queue);
private:
- mutable QMutex m_mutex;
+ mutable QRecursiveMutex m_mutex;
QAbstractAspectJobManager *m_jobManager;
// The lists of observers indexed by observable (QNodeId).
diff --git a/src/core/services/qeventfilterservice.cpp b/src/core/services/qeventfilterservice.cpp
index c4ec79a13..036bcd7b4 100644
--- a/src/core/services/qeventfilterservice.cpp
+++ b/src/core/services/qeventfilterservice.cpp
@@ -39,12 +39,13 @@
#include "qeventfilterservice_p.h"
-#include <QtCore/QMap>
#include <QtCore/QObject>
-#include <QtCore/QVector>
#include <Qt3DCore/private/qabstractserviceprovider_p.h>
+#include <algorithm>
+#include <vector>
+
QT_BEGIN_NAMESPACE
namespace {
@@ -54,10 +55,10 @@ namespace {
int priority;
};
- bool operator <(const FilterPriorityPair &a, const FilterPriorityPair &b)
+ const auto byPriority = [](const FilterPriorityPair &a, const FilterPriorityPair &b) noexcept
{
return a.priority < b.priority;
- }
+ };
}
Q_DECLARE_TYPEINFO(FilterPriorityPair, Q_PRIMITIVE_TYPE);
@@ -89,32 +90,26 @@ public:
void registerEventFilter(QObject *eventFilter, int priority)
{
- for (int i = 0, m = m_eventFilters.size(); i < m; ++i)
- if (m_eventFilters.at(i).priority == priority)
- return;
-
FilterPriorityPair fpPair;
fpPair.filter = eventFilter;
fpPair.priority = priority;
- m_eventFilters.push_back(fpPair);
- std::sort(m_eventFilters.begin(), m_eventFilters.end());
+ const auto it = std::lower_bound(m_eventFilters.begin(), m_eventFilters.end(), fpPair, byPriority);
+ if (it == m_eventFilters.end() || it->priority != priority)
+ m_eventFilters.insert(it, std::move(fpPair));
}
void unregisterEventFilter(QObject *eventFilter)
{
- QVector<FilterPriorityPair>::iterator it = m_eventFilters.begin();
- const QVector<FilterPriorityPair>::iterator end = m_eventFilters.end();
- while (it != end) {
+ for (auto it = m_eventFilters.begin(), end = m_eventFilters.end(); it != end; ++it) {
if (it->filter == eventFilter) {
m_eventFilters.erase(it);
return;
}
- ++it;
}
}
QScopedPointer<InternalEventListener> m_eventDispatcher;
- QVector<FilterPriorityPair> m_eventFilters;
+ std::vector<FilterPriorityPair> m_eventFilters;
};
/* !\internal
@@ -179,8 +174,8 @@ InternalEventListener::InternalEventListener(QEventFilterServicePrivate *filterS
bool InternalEventListener::eventFilter(QObject *obj, QEvent *e)
{
- for (int i = m_filterService->m_eventFilters.size() - 1; i >= 0; --i) {
- const FilterPriorityPair &fPPair = m_filterService->m_eventFilters.at(i);
+ for (auto i = m_filterService->m_eventFilters.size(); i > 0; --i) {
+ const FilterPriorityPair &fPPair = m_filterService->m_eventFilters[i - 1];
if (fPPair.filter->eventFilter(obj, e))
return true;
}
diff --git a/src/doc/qt3d.qdocconf b/src/doc/qt3d.qdocconf
index 25dafd2eb..ded8fea7a 100644
--- a/src/doc/qt3d.qdocconf
+++ b/src/doc/qt3d.qdocconf
@@ -1,4 +1,5 @@
include($QT_INSTALL_DOCS/global/qt-module-defaults.qdocconf)
+include($QT_INSTALL_DOCS/config/exampleurl-qt3d.qdocconf)
project = Qt3D
description = Qt 3D Reference Documentation
diff --git a/src/doc/src/qmlextramaterials.qdoc b/src/doc/src/qmlextramaterials.qdoc
index c54cc948b..e0a2e9edf 100644
--- a/src/doc/src/qmlextramaterials.qdoc
+++ b/src/doc/src/qmlextramaterials.qdoc
@@ -69,6 +69,10 @@
Holds the current texture scale. It is applied as a multiplier to texture
coordinates at render time. Defaults to 1.0.
+
+ When used in conjunction with WrapMode.Repeat, textureScale provides a simple
+ way to tile a texture across a surface. For example, a texture scale of \c 4.0
+ would result in 16 (4x4) tiles.
*/
/*!
\qmlproperty TextureImage DiffuseMapMaterial::diffuse
@@ -152,6 +156,10 @@
Holds the current texture scale. It is applied as a multiplier to texture
coordinates at render time. Defaults to 1.0.
+
+ When used in conjunction with WrapMode.Repeat, textureScale provides a simple
+ way to tile a texture across a surface. For example, a texture scale of \c 4.0
+ would result in 16 (4x4) tiles.
*/
/*!
@@ -292,6 +300,10 @@
Holds the current texture scale. It is applied as a multiplier to texture
coordinates at render time. Defaults to 1.0.
+
+ When used in conjunction with WrapMode.Repeat, textureScale provides a simple
+ way to tile a texture across a surface. For example, a texture scale of \c 4.0
+ would result in 16 (4x4) tiles.
*/
/*!
@@ -366,6 +378,10 @@
Holds the current texture scale. It is applied as a multiplier to texture
coordinates at render time. Defaults to 1.0.
+
+ When used in conjunction with WrapMode.Repeat, textureScale provides a simple
+ way to tile a texture across a surface. For example, a texture scale of \c 4.0
+ would result in 16 (4x4) tiles.
*/
/*!
@@ -449,6 +465,10 @@
Holds the current texture scale. It is applied as a multiplier to texture
coordinates at render time. Defaults to 1.0.
+
+ When used in conjunction with WrapMode.Repeat, textureScale provides a simple
+ way to tile a texture across a surface. For example, a texture scale of \c 4.0
+ would result in 16 (4x4) tiles.
*/
/*!
diff --git a/src/extras/defaults/qdiffusemapmaterial.cpp b/src/extras/defaults/qdiffusemapmaterial.cpp
index d5e729b8e..83eec26a8 100644
--- a/src/extras/defaults/qdiffusemapmaterial.cpp
+++ b/src/extras/defaults/qdiffusemapmaterial.cpp
@@ -296,6 +296,9 @@ QAbstractTexture *QDiffuseMapMaterial::diffuse() const
Holds the current texture scale. It is applied as a multiplier to texture
coordinates at render time. Defaults to 1.0.
+ When used in conjunction with QTextureWrapMode::Repeat, textureScale provides a simple
+ way to tile a texture across a surface. For example, a texture scale of \c 4.0
+ would result in 16 (4x4) tiles.
*/
float QDiffuseMapMaterial::textureScale() const
{
diff --git a/src/extras/defaults/qdiffusespecularmapmaterial.cpp b/src/extras/defaults/qdiffusespecularmapmaterial.cpp
index e1d23622c..9a1092fbc 100644
--- a/src/extras/defaults/qdiffusespecularmapmaterial.cpp
+++ b/src/extras/defaults/qdiffusespecularmapmaterial.cpp
@@ -314,6 +314,10 @@ float QDiffuseSpecularMapMaterial::shininess() const
Holds the current texture scale. It is applied as a multiplier to texture
coordinates at render time. Defaults to 1.0.
+
+ When used in conjunction with QTextureWrapMode::Repeat, textureScale provides a simple
+ way to tile a texture across a surface. For example, a texture scale of \c 4.0
+ would result in 16 (4x4) tiles.
*/
float QDiffuseSpecularMapMaterial::textureScale() const
{
diff --git a/src/extras/defaults/qdiffusespecularmaterial.cpp b/src/extras/defaults/qdiffusespecularmaterial.cpp
index be187b46a..8938ce19a 100644
--- a/src/extras/defaults/qdiffusespecularmaterial.cpp
+++ b/src/extras/defaults/qdiffusespecularmaterial.cpp
@@ -385,12 +385,20 @@ QVariant QDiffuseSpecularMaterial::normal() const
Holds the current texture scale. It is applied as a multiplier to texture
coordinates at render time. Defaults to 1.0.
+
+ When used in conjunction with QTextureWrapMode::Repeat, textureScale provides a simple
+ way to tile a texture across a surface. For example, a texture scale of \c 4.0
+ would result in 16 (4x4) tiles.
*/
/*!
\qmlproperty real DiffuseSpecularMaterial::textureScale
Holds the current texture scale. It is applied as a multiplier to texture
coordinates at render time. Defaults to 1.0.
+
+ When used in conjunction with WrapMode.Repeat, textureScale provides a simple
+ way to tile a texture across a surface. For example, a texture scale of \c 4.0
+ would result in 16 (4x4) tiles.
*/
float QDiffuseSpecularMaterial::textureScale() const
{
diff --git a/src/extras/defaults/qmetalroughmaterial.cpp b/src/extras/defaults/qmetalroughmaterial.cpp
index d404f03de..572bfecfd 100644
--- a/src/extras/defaults/qmetalroughmaterial.cpp
+++ b/src/extras/defaults/qmetalroughmaterial.cpp
@@ -306,12 +306,20 @@ QVariant QMetalRoughMaterial::normal() const
Holds the current texture scale. It is applied as a multiplier to texture
coordinates at render time. Defaults to 1.0.
+
+ When used in conjunction with QTextureWrapMode::Repeat, textureScale provides a simple
+ way to tile a texture across a surface. For example, a texture scale of \c 4.0
+ would result in 16 (4x4) tiles.
*/
/*!
\qmlproperty real Qt3D.Extras::MetalRoughMaterial::textureScale
Holds the current texture scale. It is applied as a multiplier to texture
coordinates at render time. Defaults to 1.0.
+
+ When used in conjunction with WrapMode.Repeat, textureScale provides a simple
+ way to tile a texture across a surface. For example, a texture scale of \c 4.0
+ would result in 16 (4x4) tiles.
*/
float QMetalRoughMaterial::textureScale() const
{
diff --git a/src/extras/defaults/qnormaldiffusemapmaterial.cpp b/src/extras/defaults/qnormaldiffusemapmaterial.cpp
index 7c8260084..9d41ddb32 100644
--- a/src/extras/defaults/qnormaldiffusemapmaterial.cpp
+++ b/src/extras/defaults/qnormaldiffusemapmaterial.cpp
@@ -338,6 +338,10 @@ float QNormalDiffuseMapMaterial::shininess() const
Holds the current texture scale. It is applied as a multiplier to texture
coordinates at render time. Defaults to 1.0.
+
+ When used in conjunction with QTextureWrapMode::Repeat, textureScale provides a simple
+ way to tile a texture across a surface. For example, a texture scale of \c 4.0
+ would result in 16 (4x4) tiles.
*/
float QNormalDiffuseMapMaterial::textureScale() const
{
diff --git a/src/extras/defaults/qnormaldiffusespecularmapmaterial.cpp b/src/extras/defaults/qnormaldiffusespecularmapmaterial.cpp
index 5fb2b7e9e..a76d9856b 100644
--- a/src/extras/defaults/qnormaldiffusespecularmapmaterial.cpp
+++ b/src/extras/defaults/qnormaldiffusespecularmapmaterial.cpp
@@ -355,6 +355,10 @@ float QNormalDiffuseSpecularMapMaterial::shininess() const
Holds the current texture scale. It is applied as a multiplier to texture
coordinates at render time. Defaults to 1.0.
+
+ When used in conjunction with QTextureWrapMode::Repeat, textureScale provides a simple
+ way to tile a texture across a surface. For example, a texture scale of \c 4.0
+ would result in 16 (4x4) tiles.
*/
float QNormalDiffuseSpecularMapMaterial::textureScale() const
{
diff --git a/src/extras/defaults/qskyboxentity.cpp b/src/extras/defaults/qskyboxentity.cpp
index 7de552b38..e82a30950 100644
--- a/src/extras/defaults/qskyboxentity.cpp
+++ b/src/extras/defaults/qskyboxentity.cpp
@@ -238,6 +238,30 @@ void QSkyboxEntityPrivate::reloadTexture()
*/
/*!
+ * \qmltype SkyboxEntity
+ * \instantiates Qt3DExtras::QSkyboxEntity
+ \inqmlmodule Qt3D.Extras
+ *
+ * \brief SkyboxEntity is a convenience Entity subclass that can be used to
+ * insert a skybox in a 3D scene.
+ *
+ * By specifying a base name and an extension, SkyboxEntity will take care of
+ * building a TextureCubeMap to be rendered at runtime. The images in the
+ * source directory should match the pattern: \b base name + *
+ * "_posx|_posy|_posz|_negx|_negy|_negz" + extension
+ *
+ * By default the extension defaults to .png.
+ *
+ * Be sure to disable frustum culling in the FrameGraph through which the
+ * skybox rendering happens.
+ *
+ * \note Please note that you shouldn't try to render a skybox with an
+ * orthographic projection.
+ *
+ * \since 5.5
+ */
+
+/*!
* Constructs a new Qt3DExtras::QSkyboxEntity object with \a parent as parent.
*/
QSkyboxEntity::QSkyboxEntity(QNode *parent)
@@ -269,6 +293,11 @@ void QSkyboxEntity::setBaseName(const QString &baseName)
Contains the base name of the Skybox.
*/
/*!
+ \qmlproperty string QSkyboxEntity::baseName
+
+ Contains the base name of the Skybox.
+*/
+/*!
* Returns the base name of the Skybox.
*/
QString QSkyboxEntity::baseName() const
@@ -298,6 +327,15 @@ void QSkyboxEntity::setExtension(const QString &extension)
The default value is: .png
*/
+
+/*!
+ \qmlproperty string QSkyboxEntity::extension
+
+ Contains the extension of the filename for the skybox image, including the
+ leading '.'.
+
+ The default value is: .png
+*/
/*!
* Returns the extension
*/
@@ -336,5 +374,12 @@ bool QSkyboxEntity::isGammaCorrectEnabled() const
\property QSkyboxEntity::gammaCorrect
A boolean indicating whether gamma correction is enabled.
+ \since 5.9
+*/
+/*!
+ \qmlproperty bool QSkyboxEntity::gammaCorrect
+
+ A boolean indicating whether gamma correction is enabled.
+ \since 5.9
*/
QT_END_NAMESPACE
diff --git a/src/input/frontend/qmouseevent.h b/src/input/frontend/qmouseevent.h
index a7aa8fe2b..1402d8210 100644
--- a/src/input/frontend/qmouseevent.h
+++ b/src/input/frontend/qmouseevent.h
@@ -142,8 +142,8 @@ public:
explicit QWheelEvent(const QT_PREPEND_NAMESPACE(QWheelEvent) &e);
~QWheelEvent();
- inline int x() const { return m_event.x(); }
- inline int y() const { return m_event.y(); }
+ inline int x() const { return int(m_event.position().x()); }
+ inline int y() const { return int(m_event.position().y()); }
inline QPoint angleDelta() const { return m_event.angleDelta(); }
int buttons() const;
Modifiers modifiers() const;
diff --git a/src/logic/qframeaction.cpp b/src/logic/qframeaction.cpp
index 77cdae943..a3ae748be 100644
--- a/src/logic/qframeaction.cpp
+++ b/src/logic/qframeaction.cpp
@@ -127,12 +127,12 @@ void QFrameAction::onTriggered(float dt)
/*!
\qmlsignal Qt3D.Logic::FrameAction::triggered(real dt)
- This signal is emitted each frame.
+ This signal is emitted each frame with \a dt being the time (in seconds) since the last triggering.
*/
/*!
\fn Qt3DLogic::QFrameAction::triggered(float dt)
- This signal is emitted each frame with \a dt being the time since the last triggering.
+ This signal is emitted each frame with \a dt being the time (in seconds) since the last triggering.
*/
} // namespace Qt3DLogic
diff --git a/src/plugins/sceneparsers/assimp/assimphelpers.cpp b/src/plugins/sceneparsers/assimp/assimphelpers.cpp
index ce6619541..77209a7a9 100644
--- a/src/plugins/sceneparsers/assimp/assimphelpers.cpp
+++ b/src/plugins/sceneparsers/assimp/assimphelpers.cpp
@@ -157,29 +157,29 @@ void AssimpIOStream::Flush()
*
*/
-/*!
- * Builds a new instance of AssimpIOSystem.
- */
-AssimpIOSystem::AssimpIOSystem() :
- Assimp::IOSystem()
-{
- m_openModeMaps[QByteArrayLiteral("r")] = QIODevice::ReadOnly;
- m_openModeMaps[QByteArrayLiteral("r+")] = QIODevice::ReadWrite;
- m_openModeMaps[QByteArrayLiteral("w")] = QIODevice::WriteOnly | QIODevice::Truncate;
- m_openModeMaps[QByteArrayLiteral("w+")] = QIODevice::ReadWrite | QIODevice::Truncate;
- m_openModeMaps[QByteArrayLiteral("a")] = QIODevice::WriteOnly | QIODevice::Append;
- m_openModeMaps[QByteArrayLiteral("a+")] = QIODevice::ReadWrite | QIODevice::Append;
- m_openModeMaps[QByteArrayLiteral("wb")] = QIODevice::WriteOnly;
- m_openModeMaps[QByteArrayLiteral("wt")] = QIODevice::WriteOnly | QIODevice::Text;
- m_openModeMaps[QByteArrayLiteral("rb")] = QIODevice::ReadOnly;
- m_openModeMaps[QByteArrayLiteral("rt")] = QIODevice::ReadOnly | QIODevice::Text;
-}
-
-/*!
- * Clears an AssimpIOSystem instance before deletion.
- */
-AssimpIOSystem::~AssimpIOSystem()
+static QIODevice::OpenMode openModeFromText(const char *name) noexcept
{
+ static const struct OpenModeMapping {
+ char name[2];
+ ushort mode;
+ } openModeMapping[] = {
+ { { 'r', 0 }, QIODevice::ReadOnly },
+ { { 'r', '+' }, QIODevice::ReadWrite },
+ { { 'w', 0 }, QIODevice::WriteOnly | QIODevice::Truncate },
+ { { 'w', '+' }, QIODevice::ReadWrite | QIODevice::Truncate },
+ { { 'a', 0 }, QIODevice::WriteOnly | QIODevice::Append },
+ { { 'a', '+' }, QIODevice::ReadWrite | QIODevice::Append },
+ { { 'w', 'b' }, QIODevice::WriteOnly },
+ { { 'w', 't' }, QIODevice::WriteOnly | QIODevice::Text },
+ { { 'r', 'b' }, QIODevice::ReadOnly },
+ { { 'r', 't' }, QIODevice::ReadOnly | QIODevice::Text },
+ };
+
+ for (auto e : openModeMapping) {
+ if (qstrncmp(e.name, name, sizeof(OpenModeMapping::name)) == 0)
+ return static_cast<QIODevice::OpenMode>(e.mode);
+ }
+ return QIODevice::NotOpen;
}
/*!
@@ -205,14 +205,13 @@ char AssimpIOSystem::getOsSeparator() const
Assimp::IOStream *AssimpIOSystem::Open(const char *pFile, const char *pMode)
{
const QString fileName(QString::fromUtf8(pFile));
- const QByteArray cleanedMode(QByteArray(pMode).trimmed());
-
- const QIODevice::OpenMode openMode = m_openModeMaps.value(cleanedMode, QIODevice::NotOpen);
-
- QScopedPointer<QFile> file(new QFile(fileName));
- if (file->open(openMode))
- return new AssimpIOStream(file.take());
+ const QLatin1String cleanedMode = QLatin1String{pMode}.trimmed();
+ if (const QIODevice::OpenMode openMode = openModeFromText(cleanedMode.data())) {
+ QScopedPointer<QFile> file(new QFile(fileName));
+ if (file->open(openMode))
+ return new AssimpIOStream(file.take());
+ }
return nullptr;
}
diff --git a/src/plugins/sceneparsers/assimp/assimphelpers.h b/src/plugins/sceneparsers/assimp/assimphelpers.h
index 0db22ce7b..fd102213c 100644
--- a/src/plugins/sceneparsers/assimp/assimphelpers.h
+++ b/src/plugins/sceneparsers/assimp/assimphelpers.h
@@ -55,7 +55,6 @@
#include <assimp/IOStream.hpp>
#include <assimp/IOSystem.hpp>
#include <QtCore/QIODevice>
-#include <QtCore/QMap>
QT_BEGIN_NAMESPACE
@@ -84,15 +83,10 @@ private:
class AssimpIOSystem : public Assimp::IOSystem
{
public :
- AssimpIOSystem();
- ~AssimpIOSystem();
bool Exists(const char *pFile) const override;
char getOsSeparator() const override;
Assimp::IOStream *Open(const char *pFile, const char *pMode) override;
void Close(Assimp::IOStream *pFile) override;
-
-private:
- QMap<QByteArray, QIODevice::OpenMode> m_openModeMaps;
};
} // namespace AssimpHelper
diff --git a/src/plugins/sceneparsers/assimp/assimpimporter.h b/src/plugins/sceneparsers/assimp/assimpimporter.h
index 9a020b3e3..9d6bd73de 100644
--- a/src/plugins/sceneparsers/assimp/assimpimporter.h
+++ b/src/plugins/sceneparsers/assimp/assimpimporter.h
@@ -57,7 +57,6 @@
#include <assimp/postprocess.h>
#include <assimp/DefaultLogger.hpp>
-#include <QtCore/QMap>
#include <QtCore/QDir>
#include <QtCore/QLoggingCategory>
#include <QtCore/QVector>
@@ -139,7 +138,6 @@ private:
Assimp::Importer *m_importer;
mutable const aiScene *m_aiScene;
- QMap<uint, QAbstractTexture *> m_embeddedTextures;
QHash<aiTextureType, QString> m_textureToParameterName;
QVector<Qt3DAnimation::QKeyframeAnimation *> m_animations;
QVector<Qt3DAnimation::QMorphingAnimation *> m_morphAnimations;
diff --git a/src/plugins/sceneparsers/gltf/gltfimporter.cpp b/src/plugins/sceneparsers/gltf/gltfimporter.cpp
index 7cda33113..a10a83cca 100644
--- a/src/plugins/sceneparsers/gltf/gltfimporter.cpp
+++ b/src/plugins/sceneparsers/gltf/gltfimporter.cpp
@@ -1514,16 +1514,6 @@ void GLTFImporter::processJSONBufferView(const QString &id, const QJsonObject& j
} else {
target = targetValue.toInt();
}
- Qt3DRender::QBuffer::BufferType ty(Qt3DRender::QBuffer::VertexBuffer);
-
- switch (target) {
- case GL_ARRAY_BUFFER: ty = Qt3DRender::QBuffer::VertexBuffer; break;
- case GL_ELEMENT_ARRAY_BUFFER: ty = Qt3DRender::QBuffer::IndexBuffer; break;
- default:
- qCWarning(GLTFImporterLog, "buffer %ls unsupported target: %d",
- qUtf16PrintableImpl(id), target);
- return;
- }
quint64 offset = 0;
const auto byteOffset = json.value(KEY_BYTE_OFFSET);
@@ -1541,7 +1531,6 @@ void GLTFImporter::processJSONBufferView(const QString &id, const QJsonObject& j
}
Qt3DRender::QBuffer *b = new Qt3DRender::QBuffer();
- b->setType(ty);
b->setData(bytes);
m_buffers[id] = b;
}
diff --git a/src/plugins/sceneparsers/gltf/gltfparser.cpp b/src/plugins/sceneparsers/gltf/gltfparser.cpp
deleted file mode 100644
index 881e7f1b2..000000000
--- a/src/plugins/sceneparsers/gltf/gltfparser.cpp
+++ /dev/null
@@ -1,1560 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB).
-** Copyright (C) 2015 The Qt Company Ltd and/or its subsidiary(-ies).
-** Contact: http://www.qt-project.org/legal
-**
-** This file is part of the Qt3D module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL3$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see http://www.qt.io/terms-conditions. For further
-** information use the contact form at http://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPLv3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or later as published by the Free
-** Software Foundation and appearing in the file LICENSE.GPL included in
-** the packaging of this file. Please review the following information to
-** ensure the GNU General Public License version 2.0 requirements will be
-** met: http://www.gnu.org/licenses/gpl-2.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "gltfparser.h"
-
-#include <QtCore/QDir>
-#include <QtCore/QFileInfo>
-#include <QtCore/QJsonArray>
-#include <QtCore/QJsonObject>
-
-#include <QtGui/QVector2D>
-
-#include <Qt3DCore/QCameraLens>
-#include <Qt3DCore/QEntity>
-#include <Qt3DCore/QTransform>
-
-#include <Qt3DRender/private/qurlhelper_p.h>
-
-#include <Qt3DRender/QAlphaCoverage>
-#include <Qt3DRender/QBlendEquation>
-#include <Qt3DRender/QBlendStateSeparate>
-#include <Qt3DRender/QColorMask>
-#include <Qt3DRender/QCullFace>
-#include <Qt3DRender/QDepthMask>
-#include <Qt3DRender/QDepthTest>
-#include <Qt3DRender/QEffect>
-#include <Qt3DRender/QFrontFace>
-#include <Qt3DRender/QGeometry>
-#include <Qt3DRender/QGeometryRenderer>
-#include <Qt3DRender/QMaterial>
-#include <Qt3DRender/QGraphicsApiFilter>
-#include <Qt3DRender/QParameter>
-#include <Qt3DRender/QParameterMapping>
-#include <Qt3DRender/QPolygonOffset>
-#include <Qt3DRender/QRenderState>
-#include <Qt3DRender/QScissorTest>
-#include <Qt3DRender/QShaderProgram>
-#include <Qt3DRender/QTechnique>
-#include <Qt3DRender/QTexture>
-
-#include <Qt3DRender/QPhongMaterial>
-#include <Qt3DRender/QDiffuseMapMaterial>
-#include <Qt3DRender/QDiffuseSpecularMapMaterial>
-#include <Qt3DRender/QNormalDiffuseMapMaterial>
-#include <Qt3DRender/QNormalDiffuseSpecularMapMaterial>
-
-QT_BEGIN_NAMESPACE
-
-using namespace Qt3DCore;
-
-namespace Qt3DRender {
-
-Q_LOGGING_CATEGORY(GLTFParserLog, "Qt3D.GLTFParser")
-
-namespace {
-
-const QString KEY_CAMERA = QStringLiteral("camera");
-const QString KEY_CAMERAS = QStringLiteral("cameras");
-const QString KEY_SCENES = QStringLiteral("scenes");
-const QString KEY_NODES = QStringLiteral("nodes");
-const QString KEY_MESHES = QStringLiteral("meshes");
-const QString KEY_CHILDREN = QStringLiteral("children");
-const QString KEY_MATRIX = QStringLiteral("matrix");
-const QString KEY_ROTATION = QStringLiteral("rotation");
-const QString KEY_SCALE = QStringLiteral("scale");
-const QString KEY_TRANSLATION = QStringLiteral("translation");
-const QString KEY_TYPE = QStringLiteral("type");
-const QString KEY_PERSPECTIVE =QStringLiteral("perspective");
-const QString KEY_NAME = QStringLiteral("name");
-const QString KEY_COUNT = QStringLiteral("count");
-const QString KEY_YFOV = QStringLiteral("yfov");
-const QString KEY_ZNEAR = QStringLiteral("znear");
-const QString KEY_ZFAR = QStringLiteral("zfar");
-const QString KEY_MATERIALS = QStringLiteral("materials");
-const QString KEY_EXTENSIONS = QStringLiteral("extensions");
-const QString KEY_COMMON_MAT = QStringLiteral("KHR_materials_common");
-const QString KEY_TECHNIQUE = QStringLiteral("technique");
-const QString KEY_VALUES = QStringLiteral("values");
-const QString KEY_BUFFERS = QStringLiteral("buffers");
-const QString KEY_SHADERS = QStringLiteral("shaders");
-const QString KEY_PROGRAMS = QStringLiteral("programs");
-const QString KEY_PROGRAM = QStringLiteral("program");
-const QString KEY_TECHNIQUES = QStringLiteral("techniques");
-const QString KEY_ACCESSORS = QStringLiteral("accessors");
-const QString KEY_IMAGES = QStringLiteral("images");
-const QString KEY_TEXTURES = QStringLiteral("textures");
-const QString KEY_SCENE = QStringLiteral("scene");
-const QString KEY_BUFFER = QStringLiteral("buffer");
-const QString KEY_TARGET = QStringLiteral("target");
-const QString KEY_BYTE_OFFSET = QStringLiteral("byteOffset");
-const QString KEY_BYTE_LENGTH = QStringLiteral("byteLength");
-const QString KEY_BYTE_STRIDE = QStringLiteral("byteStride");
-const QString KEY_PRIMITIVES = QStringLiteral("primitives");
-const QString KEY_MODE = QStringLiteral("mode");
-const QString KEY_MATERIAL = QStringLiteral("material");
-const QString KEY_ATTRIBUTES = QStringLiteral("attributes");
-const QString KEY_INDICES = QStringLiteral("indices");
-const QString KEY_URI = QStringLiteral("uri");
-const QString KEY_FORMAT = QStringLiteral("format");
-const QString KEY_PASSES = QStringLiteral("passes");
-const QString KEY_SOURCE = QStringLiteral("source");
-const QString KEY_SAMPLER = QStringLiteral("sampler");
-const QString KEY_SAMPLERS = QStringLiteral("samplers");
-const QString KEY_SEMANTIC = QStringLiteral("semantic");
-const QString KEY_STATES = QStringLiteral("states");
-const QString KEY_UNIFORMS = QStringLiteral("uniforms");
-const QString KEY_PARAMETERS = QStringLiteral("parameters");
-const QString KEY_WRAP_S = QStringLiteral("wrapS");
-const QString KEY_MIN_FILTER = QStringLiteral("minFilter");
-const QString KEY_MAG_FILTER = QStringLiteral("magFilter");
-
-const QString KEY_INSTANCE_TECHNIQUE = QStringLiteral("instanceTechnique");
-const QString KEY_INSTANCE_PROGRAM = QStringLiteral("instanceProgram");
-const QString KEY_BUFFER_VIEWS = QStringLiteral("bufferViews");
-const QString KEY_BUFFER_VIEW = QStringLiteral("bufferView");
-const QString KEY_VERTEX_SHADER = QStringLiteral("vertexShader");
-const QString KEY_FRAGMENT_SHADER = QStringLiteral("fragmentShader");
-const QString KEY_INTERNAL_FORMAT = QStringLiteral("internalFormat");
-const QString KEY_COMPONENT_TYPE = QStringLiteral("componentType");
-const QString KEY_ASPECT_RATIO = QStringLiteral("aspect_ratio");
-const QString KEY_VALUE = QStringLiteral("value");
-const QString KEY_ENABLE = QStringLiteral("enable");
-const QString KEY_FUNCTIONS = QStringLiteral("functions");
-const QString KEY_TECHNIQUE_CORE = QStringLiteral("techniqueCore");
-const QString KEY_TECHNIQUE_GL2 = QStringLiteral("techniqueGL2");
-
-} // of anonymous namespace
-
-GLTFParser::GLTFParser() : QAbstractSceneParser(),
- m_parseDone(false)
-{
-}
-
-GLTFParser::~GLTFParser()
-{
-
-}
-
-void GLTFParser::setBasePath(const QString& path)
-{
- m_basePath = path;
-}
-
-bool GLTFParser::setJSON(const QJsonDocument &json )
-{
- if ( !json.isObject() ) {
- return false;
- }
-
- m_json = json;
- m_parseDone = false;
-
- cleanup();
-
- return true;
-}
-
-/*
- * Sets the \a path used by the parser to load the scene file.
- * If the file is valid, parsing is automatically triggered.
- */
-void GLTFParser::setSource(const QUrl &source)
-{
- const QString path = QUrlHelper::urlToLocalFileOrQrc(source);
- QFile f(path);
- if (Q_UNLIKELY(!f.open(QIODevice::ReadOnly))) {
- qCWarning(GLTFParserLog) << "cannot open " << path << ": " << f.errorString();
- return;
- }
-
- QByteArray jsonData = f.readAll();
- QJsonDocument sceneDocument = QJsonDocument::fromBinaryData(jsonData);
- if (sceneDocument.isNull())
- sceneDocument = QJsonDocument::fromJson(jsonData);
-
- if (!setJSON(sceneDocument)) {
- qCWarning(GLTFParserLog) << "not a JSON document";
- return;
- }
-
- setBasePath(QFileInfo(path).dir().absolutePath());
-}
-
-/*
- * Returns true if the extension of \a path is supported by the
- * GLTF parser.
- */
-bool GLTFParser::isExtensionSupported(const QUrl &source) const
-{
- const QString path = QUrlHelper::urlToLocalFileOrQrc(source);
- return GLTFParser::isGLTFPath(path);
-}
-
-Qt3DCore::QEntity* GLTFParser::node(const QString &id)
-{
- QJsonObject nodes = m_json.object().value(KEY_NODES).toObject();
- if (!nodes.contains(id)) {
- qCWarning(GLTFParserLog) << "unknown node" << id << "in GLTF file" << m_basePath;
- return NULL;
- }
-
- QJsonObject jsonObj = nodes.value(id).toObject();
- QEntity* result = nullptr;
-
- // Qt3D has a limitation that a QEntity can only have 1 mesh and 1 material component
- // So if the node has only 1 mesh, we only create 1 QEntity
- // Otherwise if there are n meshes, there is 1 QEntity, with n children for each mesh/material combo
- if (jsonObj.contains(KEY_MESHES)) {
- QVector<QEntity *> entities;
-
- Q_FOREACH (QJsonValue mesh, jsonObj.value(KEY_MESHES).toArray()) {
- if (!m_meshDict.contains(mesh.toString())) {
- qCWarning(GLTFParserLog) << "node" << id << "references unknown mesh" << mesh.toString();
- continue;
- }
-
- Q_FOREACH (QGeometryRenderer *geometryRenderer, m_meshDict.values(mesh.toString())) {
- QEntity *entity = new QEntity;
- entity->addComponent(geometryRenderer);
- QMaterial *mat = material(m_meshMaterialDict[geometryRenderer]);
- if (mat)
- entity->addComponent(mat);
- entities.append(entity);
- }
-
- }
-
- if (entities.count() == 1) {
- result = entities.first();
- } else {
- result = new QEntity;
- Q_FOREACH (QEntity *entity, entities) {
- entity->setParent(result);
- }
- }
- }
-
- //If the entity contains no meshes, results will still be null here
- if (result == nullptr)
- result = new QEntity;
-
- if ( jsonObj.contains(KEY_CHILDREN) ) {
- Q_FOREACH (QJsonValue c, jsonObj.value(KEY_CHILDREN).toArray()) {
- QEntity* child = node(c.toString());
- if (!child)
- continue;
- child->setParent(result);
- }
- }
-
- renameFromJson(jsonObj, result);
-
-
- // Node Transforms
- Qt3DCore::QTransform *trans = nullptr;
- if ( jsonObj.contains(KEY_MATRIX) ) {
- QMatrix4x4 m(Qt::Uninitialized);
-
- QJsonArray matrixValues = jsonObj.value(KEY_MATRIX).toArray();
- for (int i=0; i<16; ++i) {
- double v = matrixValues.at( i ).toDouble();
- m(i % 4, i >> 2) = v;
- }
-
- // ADD MATRIX TRANSFORM COMPONENT TO ENTITY
- if (trans == nullptr)
- trans = new Qt3DCore::QTransform;
- trans->setMatrix(m);
- }
-
- // Rotation quaternion
- if (jsonObj.contains(KEY_ROTATION)) {
- if (trans == nullptr)
- trans = new Qt3DCore::QTransform;
-
- QJsonArray quaternionValues = jsonObj.value(KEY_ROTATION).toArray();
- QQuaternion quaternion(quaternionValues[0].toDouble(),
- quaternionValues[1].toDouble(),
- quaternionValues[2].toDouble(),
- quaternionValues[3].toDouble());
- trans->setRotation(quaternion);
- }
-
- // Translation
- if (jsonObj.contains(KEY_TRANSLATION)) {
- if (trans == nullptr)
- trans = new Qt3DCore::QTransform;
-
- QJsonArray translationValues = jsonObj.value(KEY_TRANSLATION).toArray();
- trans->setTranslation(QVector3D(translationValues[0].toDouble(),
- translationValues[1].toDouble(),
- translationValues[2].toDouble()));
- }
-
- // Scale
- if (jsonObj.contains(KEY_SCALE)) {
- if (trans == nullptr)
- trans = new Qt3DCore::QTransform;
-
- QJsonArray scaleValues = jsonObj.value(KEY_SCALE).toArray();
- trans->setScale3D(QVector3D(scaleValues[0].toDouble(),
- scaleValues[1].toDouble(),
- scaleValues[2].toDouble()));
- }
-
- // Add the Transform component
- if (trans != nullptr)
- result->addComponent(trans);
-
- if ( jsonObj.contains(KEY_CAMERA) ) {
- QCameraLens* cam = camera( jsonObj.value(KEY_CAMERA).toString() );
- if (!cam) {
- qCWarning(GLTFParserLog) << "failed to build camera:" << jsonObj.value(KEY_CAMERA)
- << "on node" << id;
- } else {
- result->addComponent(cam);
- }
- } // of have camera attribute
-
- return result;
-}
-
-Qt3DCore::QEntity* GLTFParser::scene(const QString &id)
-{
- parse();
-
- QJsonObject scenes = m_json.object().value(KEY_SCENES).toObject();
- if (!scenes.contains(id)) {
- if (!id.isNull())
- qCWarning(GLTFParserLog) << "GLTF: no such scene" << id << "in file" << m_basePath;
- return defaultScene();
- }
-
- QJsonObject sceneObj = scenes.value(id).toObject();
- QEntity* sceneEntity = new QEntity;
- Q_FOREACH (QJsonValue nnv, sceneObj.value(KEY_NODES).toArray()) {
- QString nodeName = nnv.toString();
- QEntity* child = node(nodeName);
- if (!child)
- continue;
- child->setParent(sceneEntity);
- }
-
- return sceneEntity;
-}
-
-GLTFParser::BufferData::BufferData()
- : length(0)
- , data(nullptr)
-{
-}
-
-GLTFParser::BufferData::BufferData(QJsonObject json)
-{
- path = json.value(KEY_URI).toString();
- length = json.value(KEY_BYTE_LENGTH).toInt();
- data = nullptr;
-}
-
-GLTFParser::ParameterData::ParameterData() :
- type(0)
-{
-
-}
-
-GLTFParser::ParameterData::ParameterData(QJsonObject json)
-{
- type = json.value(KEY_TYPE).toInt();
- semantic = json.value(KEY_SEMANTIC).toString();
-}
-
-GLTFParser::AccessorData::AccessorData()
- : type(QAttribute::Float)
- , dataSize(0)
- , count(0)
- , offset(0)
- , stride(0)
-{
-
-}
-
-GLTFParser::AccessorData::AccessorData(const QJsonObject &json)
-{
- bufferViewName = json.value(KEY_BUFFER_VIEW).toString();
- offset = 0;
- stride = 0;
- int componentType = json.value(KEY_COMPONENT_TYPE).toInt();
- type = accessorTypeFromJSON(componentType);
- count = json.value(KEY_COUNT).toInt();
- dataSize = accessorDataSizeFromJson(json.value(KEY_TYPE).toString());
-
- if ( json.contains(KEY_BYTE_OFFSET))
- offset = json.value(KEY_BYTE_OFFSET).toInt();
- if ( json.contains(KEY_BYTE_STRIDE))
- stride = json.value(KEY_BYTE_STRIDE).toInt();
-}
-
-bool GLTFParser::isGLTFPath(const QString& path)
-{
- QFileInfo finfo(path);
- if (!finfo.exists())
- return false;
-
- // might need to detect other things in the future, but would
- // prefer to avoid doing a full parse.
- QString suffix = finfo.suffix().toLower();
- return (suffix == QStringLiteral("json") || suffix == QStringLiteral("gltf") || suffix == QStringLiteral("qgltf"));
-}
-
-void GLTFParser::renameFromJson(const QJsonObject &json, QObject * const object)
-{
- if ( json.contains(KEY_NAME) )
- object->setObjectName( json.value(KEY_NAME).toString() );
-}
-
-QString GLTFParser::standardUniformNamefromSemantic(const QString &semantic)
-{
- //Standard Uniforms
- //if (semantic == QStringLiteral("LOCAL"));
- if (semantic == QStringLiteral("MODEL"))
- return QStringLiteral("modelMatrix");
- if (semantic == QStringLiteral("VIEW"))
- return QStringLiteral("viewMatrix");
- if (semantic == QStringLiteral("PROJECTION"))
- return QStringLiteral("projectionMatrix");
- if (semantic == QStringLiteral("MODELVIEW"))
- return QStringLiteral("modelView");
- if (semantic == QStringLiteral("MODELVIEWPROJECTION"))
- return QStringLiteral("modelViewProjection");
- if (semantic == QStringLiteral("MODELINVERSE"))
- return QStringLiteral("inverseModelMatrix");
- if (semantic == QStringLiteral("VIEWINVERSE"))
- return QStringLiteral("inverViewMatrix");
- if (semantic == QStringLiteral("PROJECTIONINVERSE"))
- return QStringLiteral("inverseProjectionMatrix");
- if (semantic == QStringLiteral("MODELVIEWPROJECTIONINVERSE"))
- return QStringLiteral("inverseModelViewProjection");
- if (semantic == QStringLiteral("MODELINVERSETRANSPOSE"))
- return QStringLiteral("modelNormalMatrix");
- if (semantic == QStringLiteral("MODELVIEWINVERSETRANSPOSE"))
- return QStringLiteral("modelViewNormal");
- if (semantic == QStringLiteral("VIEWPORT"))
- return QStringLiteral("viewportMatrix");
-
- return QString();
-}
-
-QString GLTFParser::standardAttributeNameFromSemantic(const QString &semantic)
-{
- //Standard Attributes
- if (semantic.startsWith(QStringLiteral("POSITION")))
- return QAttribute::defaultPositionAttributeName();
- if (semantic.startsWith(QStringLiteral("NORMAL")))
- return QAttribute::defaultNormalAttributeName();
- if (semantic.startsWith(QStringLiteral("TEXCOORD")))
- return QAttribute::defaultTextureCoordinateAttributeName();
- if (semantic.startsWith(QStringLiteral("COLOR")))
- return QAttribute::defaultColorAttributeName();
- if (semantic.startsWith(QStringLiteral("TANGENT")))
- return QAttribute::defaultTangentAttributeName();
-
-// if (semantic.startsWith(QStringLiteral("JOINT")));
-// if (semantic.startsWith(QStringLiteral("JOINTMATRIX")));
-// if (semantic.startsWith(QStringLiteral("WEIGHT")));
-
- return QString();
-}
-
-QParameter *GLTFParser::parameterFromTechnique(QTechnique *technique, const QString &parameterName)
-{
- Q_FOREACH (QParameter *parameter, technique->parameters()) {
- if (parameter->name() == parameterName) {
- return parameter;
- }
- }
-
- return nullptr;
-}
-
-Qt3DCore::QEntity* GLTFParser::defaultScene()
-{
- if (m_defaultScene.isEmpty()) {
- qCWarning(GLTFParserLog) << Q_FUNC_INFO << "no default scene";
- return NULL;
- }
-
- return scene(m_defaultScene);
-}
-
-QMaterial *GLTFParser::materialWithCustomShader(const QString &id, const QJsonObject &jsonObj)
-{
- //Default ES2 Technique
- QString techniqueName = jsonObj.value(KEY_TECHNIQUE).toString();
- if (!m_techniques.contains(techniqueName)) {
- qCWarning(GLTFParserLog) << "unknown technique" << techniqueName
- << "for material" << id << "in GLTF file" << m_basePath;
- return NULL;
- }
- QTechnique *technique = m_techniques.value(techniqueName);
- technique->graphicsApiFilter()->setApi(QGraphicsApiFilter::OpenGLES);
- technique->graphicsApiFilter()->setMajorVersion(2);
- technique->graphicsApiFilter()->setMinorVersion(0);
- technique->graphicsApiFilter()->setProfile(QGraphicsApiFilter::NoProfile);
-
-
- //Optional Core technique
- QTechnique *coreTechnique = nullptr;
- QTechnique *gl2Technique = nullptr;
- QString coreTechniqueName = jsonObj.value(KEY_TECHNIQUE_CORE).toString();
- if (!coreTechniqueName.isNull()) {
- if (!m_techniques.contains(coreTechniqueName)) {
- qCWarning(GLTFParserLog) << "unknown technique" << coreTechniqueName
- << "for material" << id << "in GLTF file" << m_basePath;
- } else {
- coreTechnique = m_techniques.value(coreTechniqueName);
- coreTechnique->graphicsApiFilter()->setApi(QGraphicsApiFilter::OpenGL);
- coreTechnique->graphicsApiFilter()->setMajorVersion(3);
- coreTechnique->graphicsApiFilter()->setMinorVersion(1);
- coreTechnique->graphicsApiFilter()->setProfile(QGraphicsApiFilter::CoreProfile);
- }
- }
- //Optional GL2 technique
- QString gl2TechniqueName = jsonObj.value(KEY_TECHNIQUE_GL2).toString();
- if (!gl2TechniqueName.isNull()) {
- if (!m_techniques.contains(gl2TechniqueName)) {
- qCWarning(GLTFParserLog) << "unknown technique" << gl2TechniqueName
- << "for material" << id << "in GLTF file" << m_basePath;
- } else {
- gl2Technique = m_techniques.value(gl2TechniqueName);
- gl2Technique->graphicsApiFilter()->setApi(QGraphicsApiFilter::OpenGL);
- gl2Technique->graphicsApiFilter()->setMajorVersion(2);
- gl2Technique->graphicsApiFilter()->setMinorVersion(0);
- gl2Technique->graphicsApiFilter()->setProfile(QGraphicsApiFilter::NoProfile);
- }
- }
-
-
- // glTF doesn't deal in effects, but we need a trivial one to wrap
- // up our techniques
- // However we need to create a unique effect for each material instead
- // of caching because QMaterial does not keep up with effects
- // its not the parent of.
- QEffect* effect = new QEffect;
- effect->setObjectName(techniqueName);
- effect->addTechnique(technique);
- if (coreTechnique != nullptr)
- effect->addTechnique(coreTechnique);
- if (gl2Technique != nullptr)
- effect->addTechnique(gl2Technique);
-
- QMaterial* mat = new QMaterial;
- mat->setEffect(effect);
-
- renameFromJson(jsonObj, mat);
-
- QJsonObject values = jsonObj.value(KEY_VALUES).toObject();
- Q_FOREACH (QString vName, values.keys()) {
- QParameter *param = parameterFromTechnique(technique, vName);
-
- if (param == nullptr && coreTechnique != nullptr) {
- param = parameterFromTechnique(coreTechnique, vName);
- }
-
- if (param == nullptr && gl2Technique != nullptr) {
- param = parameterFromTechnique(gl2Technique, vName);
- }
-
- if (param == nullptr) {
- qCWarning(GLTFParserLog) << "unknown parameter:" << vName << "in technique" << techniqueName
- << "processing material" << id;
- continue;
- }
-
- ParameterData paramData = m_parameterDataDict.value(param);
- QVariant var = parameterValueFromJSON(paramData.type, values.value(vName));
-
- mat->addParameter(new QParameter(param->name(), var));
- } // of material technique-instance values iteration
-
- return mat;
-}
-
-static inline QVariant vec4ToRgb(const QVariant &vec4Var)
-{
- const QVector4D v = vec4Var.value<QVector4D>();
- return QVariant(QColor::fromRgbF(v.x(), v.y(), v.z()));
-}
-
-QMaterial *GLTFParser::commonMaterial(const QJsonObject &jsonObj)
-{
- QVariantHash params;
- bool hasDiffuseMap = false;
- bool hasSpecularMap = false;
- bool hasNormalMap = false;
-
- QJsonObject values = jsonObj.value(KEY_VALUES).toObject();
- Q_FOREACH (const QString &vName, values.keys()) {
- const QJsonValue val = values.value(vName);
- QVariant var;
- QString propertyName = vName;
- if (vName == QStringLiteral("ambient") && val.isArray()) {
- var = vec4ToRgb(parameterValueFromJSON(GL_FLOAT_VEC4, val));
- } else if (vName == QStringLiteral("diffuse")) {
- if (val.isString()) {
- var = parameterValueFromJSON(GL_SAMPLER_2D, val);
- hasDiffuseMap = true;
- } else if (val.isArray()) {
- var = vec4ToRgb(parameterValueFromJSON(GL_FLOAT_VEC4, val));
- }
- } else if (vName == QStringLiteral("specular")) {
- if (val.isString()) {
- var = parameterValueFromJSON(GL_SAMPLER_2D, val);
- hasSpecularMap = true;
- } else if (val.isArray()) {
- var = vec4ToRgb(parameterValueFromJSON(GL_FLOAT_VEC4, val));
- }
- } else if (vName == QStringLiteral("shininess") && val.isDouble()) {
- var = parameterValueFromJSON(GL_FLOAT, val);
- } else if (vName == QStringLiteral("normalmap") && val.isString()) {
- var = parameterValueFromJSON(GL_SAMPLER_2D, val);
- propertyName = QStringLiteral("normal");
- hasNormalMap = true;
- } else if (vName == QStringLiteral("transparency")) {
- qCWarning(GLTFParserLog) << "Semi-transparent common materials are not currently supported, ignoring alpha";
- }
- if (var.isValid())
- params[propertyName] = var;
- }
-
- QMaterial *mat = nullptr;
- if (hasNormalMap) {
- if (hasSpecularMap) {
- mat = new QNormalDiffuseSpecularMapMaterial;
- } else {
- if (hasDiffuseMap)
- mat = new QNormalDiffuseMapMaterial;
- else
- qCWarning(GLTFParserLog) << "Common material with normal and specular maps needs a diffuse map as well";
- }
- } else {
- if (hasSpecularMap) {
- if (hasDiffuseMap)
- mat = new QDiffuseSpecularMapMaterial;
- else
- qCWarning(GLTFParserLog) << "Common material with specular map needs a diffuse map as well";
- } else if (hasDiffuseMap) {
- mat = new QDiffuseMapMaterial;
- } else {
- mat = new QPhongMaterial;
- }
- }
-
- if (mat) {
- for (QVariantHash::const_iterator it = params.constBegin(), itEnd = params.constEnd(); it != itEnd; ++it)
- mat->setProperty(it.key().toUtf8(), it.value());
- } else {
- qCWarning(GLTFParserLog) << "Could not find a suitable built-in material for KHR_materials_common";
- }
-
- return mat;
-}
-
-QMaterial* GLTFParser::material(const QString &id)
-{
- if (m_materialCache.contains(id))
- return m_materialCache.value(id);
-
- QJsonObject mats = m_json.object().value(KEY_MATERIALS).toObject();
- if (!mats.contains(id)) {
- qCWarning(GLTFParserLog) << "unknown material" << id << "in GLTF file" << m_basePath;
- return NULL;
- }
-
- QJsonObject jsonObj = mats.value(id).toObject();
-
- QMaterial *mat = nullptr;
-
- // Prefer common materials over custom shaders.
- if (jsonObj.contains(KEY_EXTENSIONS)) {
- QJsonObject extensions = jsonObj.value(KEY_EXTENSIONS).toObject();
- if (extensions.contains(KEY_COMMON_MAT))
- mat = commonMaterial(extensions.value(KEY_COMMON_MAT).toObject());
- }
-
- if (!mat)
- mat = materialWithCustomShader(id, jsonObj);
-
- m_materialCache[id] = mat;
- return mat;
-}
-
-QCameraLens* GLTFParser::camera(const QString &id) const
-{
- QJsonObject cams = m_json.object().value(KEY_CAMERAS).toObject();
- if (!cams.contains(id)) {
- qCWarning(GLTFParserLog) << "unknown camera" << id << "in GLTF file" << m_basePath;
- return nullptr;
- }
-
- QJsonObject jsonObj = cams.value(id).toObject();
- QString camTy = jsonObj.value(KEY_TYPE).toString();
-
- if (camTy == QStringLiteral("perspective")) {
- if (!jsonObj.contains(KEY_PERSPECTIVE)) {
- qCWarning(GLTFParserLog) << "camera:" << id << "missing 'perspective' object";
- return nullptr;
- }
-
- QJsonObject pObj = jsonObj.value(KEY_PERSPECTIVE).toObject();
- double aspectRatio = pObj.value(KEY_ASPECT_RATIO).toDouble();
- double yfov = pObj.value(KEY_YFOV).toDouble();
- double frustumNear = pObj.value(KEY_ZNEAR).toDouble();
- double frustumFar = pObj.value(KEY_ZFAR).toDouble();
-
- QCameraLens* result = new QCameraLens;
- result->setPerspectiveProjection(yfov, aspectRatio, frustumNear, frustumFar);
- return result;
- } else if (camTy == QStringLiteral("orthographic")) {
- qCWarning(GLTFParserLog) << Q_FUNC_INFO << "implement me";
-
- return nullptr;
- } else {
- qCWarning(GLTFParserLog) << "camera:" << id << "has unsupported type:" << camTy;
- return nullptr;
- }
-}
-
-
-void GLTFParser::parse()
-{
- if (m_parseDone)
- return;
-
- QJsonObject buffers = m_json.object().value(KEY_BUFFERS).toObject();
- Q_FOREACH (QString nm, buffers.keys()) {
- processJSONBuffer( nm, buffers.value(nm).toObject() );
- }
-
- QJsonObject views = m_json.object().value(KEY_BUFFER_VIEWS).toObject();
- loadBufferData();
- Q_FOREACH (QString nm, views.keys()) {
- processJSONBufferView( nm, views.value(nm).toObject() );
- }
- unloadBufferData();
-
- QJsonObject shaders = m_json.object().value(KEY_SHADERS).toObject();
- Q_FOREACH (QString nm, shaders.keys()) {
- processJSONShader( nm, shaders.value(nm).toObject() );
- }
-
- QJsonObject programs = m_json.object().value(KEY_PROGRAMS).toObject();
- Q_FOREACH (QString nm, programs.keys()) {
- processJSONProgram( nm, programs.value(nm).toObject() );
- }
-
- QJsonObject techniques = m_json.object().value(KEY_TECHNIQUES).toObject();
- Q_FOREACH (QString nm, techniques.keys()) {
- processJSONTechnique( nm, techniques.value(nm).toObject() );
- }
-
- QJsonObject attrs = m_json.object().value(KEY_ACCESSORS).toObject();
- Q_FOREACH (QString nm, attrs.keys()) {
- processJSONAccessor( nm, attrs.value(nm).toObject() );
- }
-
- QJsonObject meshes = m_json.object().value(KEY_MESHES).toObject();
- Q_FOREACH (QString nm, meshes.keys()) {
- processJSONMesh( nm, meshes.value(nm).toObject() );
- }
-
- QJsonObject images = m_json.object().value(KEY_IMAGES).toObject();
- Q_FOREACH (QString nm, images.keys()) {
- processJSONImage( nm, images.value(nm).toObject() );
- }
-
- QJsonObject textures = m_json.object().value(KEY_TEXTURES).toObject();
- Q_FOREACH (QString nm, textures.keys()) {
- processJSONTexture(nm, textures.value(nm).toObject() );
- }
-
- m_defaultScene = m_json.object().value(KEY_SCENE).toString();
- m_parseDone = true;
-}
-
-void GLTFParser::cleanup()
-{
- m_meshDict.clear();
- m_meshMaterialDict.clear();
- m_accessorDict.clear();
- //Check for Materials with no parent
- Q_FOREACH (QMaterial *material, m_materialCache.values()) {
- if (material->parent() == nullptr)
- delete material;
- }
- m_materialCache.clear();
- m_bufferDatas.clear();
- m_buffers.clear();
- m_shaderPaths.clear();
- //Check for ShaderPrograms with no parent
- Q_FOREACH (QShaderProgram *program, m_programs.values()) {
- if (program->parent() == nullptr)
- delete program;
- }
- m_programs.clear();
- //Check for Techniques with no parent
- Q_FOREACH (QTechnique *technique, m_techniques.values()) {
- if (technique->parent() == nullptr)
- delete technique;
- }
- m_techniques.clear();
- //Check for Textures with no parent
- Q_FOREACH (QAbstractTextureProvider *texture, m_textures.values()) {
- if (texture->parent() == nullptr)
- delete texture;
- }
- m_textures.clear();
- m_imagePaths.clear();
- m_defaultScene.clear();
- m_parameterDataDict.clear();
-}
-
-void GLTFParser::processJSONBuffer(const QString &id, const QJsonObject& json)
-{
- // simply cache buffers for lookup by buffer-views
- m_bufferDatas[id] = BufferData(json);
-}
-
-void GLTFParser::processJSONBufferView(const QString &id, const QJsonObject& json)
-{
- QString bufName = json.value(KEY_BUFFER).toString();
- if (!m_bufferDatas.contains(bufName)) {
- qCWarning(GLTFParserLog) << "unknown buffer:" << bufName << "processing view:" << id;
- return;
- }
-
- int target = json.value(KEY_TARGET).toInt();
- QBuffer::BufferType ty(QBuffer::VertexBuffer);
-
- switch (target) {
- case GL_ARRAY_BUFFER: ty = QBuffer::VertexBuffer; break;
- case GL_ELEMENT_ARRAY_BUFFER: ty = QBuffer::IndexBuffer; break;
- default:
- qCWarning(GLTFParserLog) << Q_FUNC_INFO << "buffer" << id << "unsupported target:" << target;
- return;
- }
-
- quint64 offset = 0;
- if (json.contains(KEY_BYTE_OFFSET)) {
- offset = json.value(KEY_BYTE_OFFSET).toInt();
- qCDebug(GLTFParserLog) << "bv:" << id << "has offset:" << offset;
- }
-
- quint64 len = json.value(KEY_BYTE_LENGTH).toInt();
-
- QByteArray bytes(m_bufferDatas[bufName].data->mid(offset, len));
- if (bytes.count() != (int) len) {
- qCWarning(GLTFParserLog) << "failed to read sufficient bytes from:" << m_bufferDatas[bufName].path
- << "for view" << id;
- }
-
- QBuffer *b(new QBuffer(ty));
- b->setData(bytes);
- m_buffers[id] = b;
-}
-
-void GLTFParser::processJSONShader(const QString &id, const QJsonObject &jsonObject)
-{
- // shaders are trivial for the moment, defer the real work
- // to the program section
- QString path = jsonObject.value(KEY_URI).toString();
-
- QFileInfo info(m_basePath, path);
- if (!info.exists()) {
- qCWarning(GLTFParserLog) << "can't find shader" << id << "from path" << path;
- return;
- }
-
- m_shaderPaths[id] = info.absoluteFilePath();
-}
-
-void GLTFParser::processJSONProgram(const QString &id, const QJsonObject &jsonObject)
-{
- QShaderProgram* prog = new QShaderProgram;
- prog->setObjectName(id);
-
- QString fragName = jsonObject.value(KEY_FRAGMENT_SHADER).toString(),
- vertName = jsonObject.value(KEY_VERTEX_SHADER).toString();
- if (!m_shaderPaths.contains(fragName) || !m_shaderPaths.contains(vertName)) {
- qCWarning(GLTFParserLog) << Q_FUNC_INFO << "program:" << id << "missing shader:"
- << fragName << vertName;
- return;
- }
-
- prog->setFragmentShaderCode(QShaderProgram::loadSource(QUrl::fromLocalFile(m_shaderPaths[fragName])));
- prog->setVertexShaderCode(QShaderProgram::loadSource(QUrl::fromLocalFile(m_shaderPaths[vertName])));
- m_programs[id] = prog;
-}
-
-void GLTFParser::processJSONTechnique(const QString &id, const QJsonObject &jsonObject )
-{
- QTechnique *t = new QTechnique;
- t->setObjectName(id);
-
- // Parameters
- QHash<QString, QParameter*> paramDict;
- QJsonObject params = jsonObject.value(KEY_PARAMETERS).toObject();
- Q_FOREACH (QString pname, params.keys()) {
- QJsonObject po = params.value(pname).toObject();
-
- //QString semantic = po.value(KEY_SEMANTIC).toString();
- QParameter *p = new QParameter(t);
- p->setName(pname);
- m_parameterDataDict.insert(p, ParameterData(po));
-
- //If the parameter has default value, set it
- QJsonValue value = po.value(KEY_VALUE);
- if (!value.isUndefined()) {
- int dataType = po.value(KEY_TYPE).toInt();
- p->setValue(parameterValueFromJSON(dataType, value));
- }
-
- t->addParameter(p);
-
- paramDict[pname] = p;
- } // of parameters iteration
-
- // Program
- QString programName = jsonObject.value(KEY_PROGRAM).toString();
- if (!m_programs.contains(programName)) {
- qCWarning(GLTFParserLog) << Q_FUNC_INFO << "technique" << id
- << ": missing program" << programName;
- }
-
- QRenderPass* pass = new QRenderPass;
- pass->setShaderProgram(m_programs[programName]);
-
- // Attributes
- QJsonObject attrs = jsonObject.value(KEY_ATTRIBUTES).toObject();
- Q_FOREACH ( QString shaderAttributeName, attrs.keys() ) {
- QString pname = attrs.value(shaderAttributeName).toString();
- QParameter *parameter = paramDict.value(pname, nullptr);
- QString attributeName = pname;
- if (parameter == nullptr) {
- qCWarning(GLTFParserLog) << Q_FUNC_INFO << "attribute " << pname
- << "defined in instanceProgram but not as parameter";
- continue;
- }
- //Check if the parameter has a standard attribute semantic
- QString standardAttributeName = standardAttributeNameFromSemantic(m_parameterDataDict[parameter].semantic);
- if (!standardAttributeName.isNull()) {
- attributeName = standardAttributeName;
- t->removeParameter(parameter);
- m_parameterDataDict.remove(parameter);
- delete parameter;
- }
-
- pass->addBinding(new QParameterMapping(attributeName, shaderAttributeName, QParameterMapping::Attribute));
- } // of program-instance attributes
-
- // Uniforms
- QJsonObject uniforms = jsonObject.value(KEY_UNIFORMS).toObject();
- Q_FOREACH (QString shaderUniformName, uniforms.keys()) {
- QString pname = uniforms.value(shaderUniformName).toString();
- QParameter *parameter = paramDict.value(pname, nullptr);
- if (parameter == nullptr) {
- qCWarning(GLTFParserLog) << Q_FUNC_INFO << "uniform " << pname
- << "defined in instanceProgram but not as parameter";
- continue;
- }
- //Check if the parameter has a standard uniform semantic
- QString standardUniformName = standardUniformNamefromSemantic(m_parameterDataDict[parameter].semantic);
- if (standardUniformName.isNull()) {
- pass->addBinding(new QParameterMapping(pname, shaderUniformName, QParameterMapping::Uniform));
- } else {
- pass->addBinding(new QParameterMapping(standardUniformName, shaderUniformName, QParameterMapping::StandardUniform));
- t->removeParameter(parameter);
- m_parameterDataDict.remove(parameter);
- delete parameter;
- }
- } // of program-instance uniforms
-
-
- // States
- QJsonObject states = jsonObject.value(KEY_STATES).toObject();
-
- //Process states to enable
- QJsonArray enableStatesArray = states.value(KEY_ENABLE).toArray();
- QVector<int> enableStates;
- Q_FOREACH (QJsonValue enableValue, enableStatesArray) {
- enableStates.append(enableValue.toInt());
- }
-
- //Process the list of state functions
- QJsonObject functions = states.value(KEY_FUNCTIONS).toObject();
- Q_FOREACH (QString functionName, functions.keys()) {
- int enableStateType = 0;
- QRenderState *renderState = buildState(functionName, functions.value(functionName), enableStateType);
- if (renderState != nullptr) {
- //Remove the need to set a default state values for enableStateType
- enableStates.removeOne(enableStateType);
- pass->addRenderState(renderState);
- }
- }
-
- //Create render states with default values for any remaining enable states
- Q_FOREACH (int enableState, enableStates) {
- QRenderState *renderState = buildStateEnable(enableState);
- if (renderState != nullptr)
- pass->addRenderState(renderState);
- }
-
-
- t->addPass(pass);
-
- m_techniques[id] = t;
-}
-
-void GLTFParser::processJSONAccessor( const QString &id, const QJsonObject& json )
-{
- m_accessorDict[id] = AccessorData(json);
-}
-
-void GLTFParser::processJSONMesh(const QString &id, const QJsonObject &json)
-{
- QJsonArray primitivesArray = json.value(KEY_PRIMITIVES).toArray();
- Q_FOREACH (QJsonValue primitiveValue, primitivesArray) {
- QJsonObject primitiveObject = primitiveValue.toObject();
- int type = primitiveObject.value(KEY_MODE).toInt();
- QString material = primitiveObject.value(KEY_MATERIAL).toString();
-
- if ( material.isEmpty()) {
- qCWarning(GLTFParserLog) << "malformed primitive on " << id << ", missing material value"
- << material;
- continue;
- }
-
- QGeometryRenderer *geometryRenderer = new QGeometryRenderer;
- QGeometry *meshGeometry = new QGeometry(geometryRenderer);
-
- //Set Primitive Type
- geometryRenderer->setPrimitiveType(static_cast<QGeometryRenderer::PrimitiveType>(type));
-
- //Save Material for mesh
- m_meshMaterialDict[geometryRenderer] = material;
-
- QJsonObject attrs = primitiveObject.value(KEY_ATTRIBUTES).toObject();
- Q_FOREACH (QString attrName, attrs.keys()) {
- QString k = attrs.value(attrName).toString();
- if (!m_accessorDict.contains(k)) {
- qCWarning(GLTFParserLog) << "unknown attribute accessor:" << k << "on mesh" << id;
- continue;
- }
-
- QString attributeName = standardAttributeNameFromSemantic(attrName);
- if (attributeName.isEmpty())
- attributeName = attrName;
-
- //Get buffer handle for accessor
- QBuffer *buffer = m_buffers.value(m_accessorDict[k].bufferViewName, nullptr);
- if (buffer == nullptr) {
- qCWarning(GLTFParserLog) << "unknown buffer-view:" << m_accessorDict[k].bufferViewName << "processing accessor:" << id;
- continue;
- }
-
- QAttribute *attribute = new QAttribute(buffer,
- attributeName,
- m_accessorDict[k].type,
- m_accessorDict[k].dataSize,
- m_accessorDict[k].count,
- m_accessorDict[k].offset,
- m_accessorDict[k].stride);
- attribute->setAttributeType(QAttribute::VertexAttribute);
- meshGeometry->addAttribute(attribute);
- }
-
- if ( primitiveObject.contains(KEY_INDICES)) {
- QString k = primitiveObject.value(KEY_INDICES).toString();
- if (!m_accessorDict.contains(k)) {
- qCWarning(GLTFParserLog) << "unknown index accessor:" << k << "on mesh" << id;
- } else {
- //Get buffer handle for accessor
- QBuffer *buffer = m_buffers.value(m_accessorDict[k].bufferViewName, nullptr);
- if (buffer == nullptr) {
- qCWarning(GLTFParserLog) << "unknown buffer-view:" << m_accessorDict[k].bufferViewName << "processing accessor:" << id;
- continue;
- }
-
- QAttribute *attribute = new QAttribute(buffer,
- m_accessorDict[k].type,
- m_accessorDict[k].dataSize,
- m_accessorDict[k].count,
- m_accessorDict[k].offset,
- m_accessorDict[k].stride);
- attribute->setAttributeType(QAttribute::IndexAttribute);
- meshGeometry->addAttribute(attribute);
- }
- } // of has indices
-
- geometryRenderer->setGeometry(meshGeometry);
-
- m_meshDict.insert( id, geometryRenderer);
- } // of primitives iteration
-}
-
-void GLTFParser::processJSONImage(const QString &id, const QJsonObject &jsonObject)
-{
- QString path = jsonObject.value(KEY_URI).toString();
- QFileInfo info(m_basePath, path);
- if (!info.exists()) {
- qCWarning(GLTFParserLog) << "can't find image" << id << "from path" << path;
- return;
- }
-
- m_imagePaths[id] = info.absoluteFilePath();
-}
-
-void GLTFParser::processJSONTexture(const QString &id, const QJsonObject &jsonObject)
-{
- int target = jsonObject.value(KEY_TARGET).toInt(GL_TEXTURE_2D);
- //TODO: support other targets that GL_TEXTURE_2D (though the spec doesn't support anything else)
- if (target != GL_TEXTURE_2D) {
- qCWarning(GLTFParserLog) << "unsupported texture target: " << target;
- return;
- }
-
- QTexture2D* tex = new QTexture2D;
-
- // TODO: Choose suitable internal format - may vary on OpenGL context type
- //int pixelFormat = jsonObj.value(KEY_FORMAT).toInt(GL_RGBA);
- int internalFormat = jsonObject.value(KEY_INTERNAL_FORMAT).toInt(GL_RGBA);
-
- tex->setFormat(static_cast<QAbstractTextureProvider::TextureFormat>(internalFormat));
-
- QString samplerId = jsonObject.value(KEY_SAMPLER).toString();
- QString source = jsonObject.value(KEY_SOURCE).toString();
- if (!m_imagePaths.contains(source)) {
- qCWarning(GLTFParserLog) << "texture" << id << "references missing image" << source;
- return;
- }
-
- QTextureImage *texImage = new QTextureImage(tex);
- texImage->setSource(QUrl::fromLocalFile(m_imagePaths[source]));
- tex->addTextureImage(texImage);
-
- QJsonObject samplersDict(m_json.object().value(KEY_SAMPLERS).toObject());
- if (!samplersDict.contains(samplerId)) {
- qCWarning(GLTFParserLog) << "texture" << id << "references unknown sampler" << samplerId;
- return;
- }
-
- QJsonObject sampler = samplersDict.value(samplerId).toObject();
-
- tex->setWrapMode(QTextureWrapMode(static_cast<QTextureWrapMode::WrapMode>(sampler.value(KEY_WRAP_S).toInt())));
- tex->setMinificationFilter(static_cast<QAbstractTextureProvider::Filter>(sampler.value(KEY_MIN_FILTER).toInt()));
- if (tex->minificationFilter() == QAbstractTextureProvider::NearestMipMapLinear ||
- tex->minificationFilter() == QAbstractTextureProvider::LinearMipMapNearest ||
- tex->minificationFilter() == QAbstractTextureProvider::NearestMipMapNearest ||
- tex->minificationFilter() == QAbstractTextureProvider::LinearMipMapLinear) {
-
- tex->setGenerateMipMaps(true);
- }
- tex->setMagnificationFilter(static_cast<QAbstractTextureProvider::Filter>(sampler.value(KEY_MAG_FILTER).toInt()));
-
- m_textures[id] = tex;
-}
-
-void GLTFParser::loadBufferData()
-{
- Q_FOREACH (QString bufferName, m_bufferDatas.keys()) {
- if (m_bufferDatas[bufferName].data == nullptr) {
- QFile* bufferFile = resolveLocalData(m_bufferDatas[bufferName].path);
- QByteArray *data = new QByteArray(bufferFile->readAll());
- m_bufferDatas[bufferName].data = data;
- delete bufferFile;
- }
- }
-}
-
-void GLTFParser::unloadBufferData()
-{
- Q_FOREACH (QString bufferName, m_bufferDatas.keys()) {
- QByteArray *data = m_bufferDatas[bufferName].data;
- delete data;
- }
-}
-
-QFile *GLTFParser::resolveLocalData(QString path) const
-{
- QDir d(m_basePath);
- Q_ASSERT(d.exists());
-
- QString absPath = d.absoluteFilePath(path);
- QFile* f = new QFile(absPath);
- f->open(QIODevice::ReadOnly);
- return f;
-}
-
-QVariant GLTFParser::parameterValueFromJSON(int type, const QJsonValue &value) const
-{
- if (value.isBool()) {
- if (type == GL_BOOL)
- return QVariant(static_cast<GLboolean>(value.toBool()));
- } else if (value.isString()) {
- if (type == GL_SAMPLER_2D) {
- //Textures are special because we need to do a lookup to return the
- //QAbstractTextureProvider
- QString textureId = value.toString();
- if (!m_textures.contains(textureId)) {
- qCWarning(GLTFParserLog) << "unknown texture" << textureId;
- return QVariant();
- } else {
- return QVariant::fromValue(m_textures.value(textureId));
- }
- }
- } else if (value.isDouble()) {
- switch (type) {
- case GL_BYTE:
- return QVariant(static_cast<GLbyte>(value.toInt()));
- case GL_UNSIGNED_BYTE:
- return QVariant(static_cast<GLubyte>(value.toInt()));
- case GL_SHORT:
- return QVariant(static_cast<GLshort>(value.toInt()));
- case GL_UNSIGNED_SHORT:
- return QVariant(static_cast<GLushort>(value.toInt()));
- case GL_INT:
- return QVariant(static_cast<GLint>(value.toInt()));
- case GL_UNSIGNED_INT:
- return QVariant(static_cast<GLuint>(value.toInt()));
- case GL_FLOAT:
- return QVariant(static_cast<GLfloat>(value.toDouble()));
- }
- } else if (value.isArray()) {
-
- QJsonArray valueArray = value.toArray();
-
- QVector2D vector2D;
- QVector3D vector3D;
- QVector4D vector4D;
- QVector<float> dataMat2(4, 0.0f);
- QVector<float> dataMat3(9, 0.0f);
-
- switch (type) {
- case GL_BYTE:
- return QVariant(static_cast<GLbyte>(valueArray.first().toInt()));
- case GL_UNSIGNED_BYTE:
- return QVariant(static_cast<GLubyte>(valueArray.first().toInt()));
- case GL_SHORT:
- return QVariant(static_cast<GLshort>(valueArray.first().toInt()));
- case GL_UNSIGNED_SHORT:
- return QVariant(static_cast<GLushort>(valueArray.first().toInt()));
- case GL_INT:
- return QVariant(static_cast<GLint>(valueArray.first().toInt()));
- case GL_UNSIGNED_INT:
- return QVariant(static_cast<GLuint>(valueArray.first().toInt()));
- case GL_FLOAT:
- return QVariant(static_cast<GLfloat>(valueArray.first().toDouble()));
- case GL_FLOAT_VEC2:
- vector2D.setX(static_cast<GLfloat>(valueArray.at(0).toDouble()));
- vector2D.setY(static_cast<GLfloat>(valueArray.at(1).toDouble()));
- return QVariant(vector2D);
- case GL_FLOAT_VEC3:
- vector3D.setX(static_cast<GLfloat>(valueArray.at(0).toDouble()));
- vector3D.setY(static_cast<GLfloat>(valueArray.at(1).toDouble()));
- vector3D.setZ(static_cast<GLfloat>(valueArray.at(2).toDouble()));
- return QVariant(vector3D);
- case GL_FLOAT_VEC4:
- vector4D.setX(static_cast<GLfloat>(valueArray.at(0).toDouble()));
- vector4D.setY(static_cast<GLfloat>(valueArray.at(1).toDouble()));
- vector4D.setZ(static_cast<GLfloat>(valueArray.at(2).toDouble()));
- vector4D.setW(static_cast<GLfloat>(valueArray.at(3).toDouble()));
- return QVariant(vector4D);
- case GL_INT_VEC2:
- vector2D.setX(static_cast<GLint>(valueArray.at(0).toInt()));
- vector2D.setY(static_cast<GLint>(valueArray.at(1).toInt()));
- return QVariant(vector2D);
- case GL_INT_VEC3:
- vector3D.setX(static_cast<GLint>(valueArray.at(0).toInt()));
- vector3D.setY(static_cast<GLint>(valueArray.at(1).toInt()));
- vector3D.setZ(static_cast<GLint>(valueArray.at(2).toInt()));
- return QVariant(vector3D);
- case GL_INT_VEC4:
- vector4D.setX(static_cast<GLint>(valueArray.at(0).toInt()));
- vector4D.setY(static_cast<GLint>(valueArray.at(1).toInt()));
- vector4D.setZ(static_cast<GLint>(valueArray.at(2).toInt()));
- vector4D.setW(static_cast<GLint>(valueArray.at(3).toInt()));
- return QVariant(vector4D);
- case GL_BOOL:
- return QVariant(static_cast<GLboolean>(valueArray.first().toBool()));
- case GL_BOOL_VEC2:
- vector2D.setX(static_cast<GLboolean>(valueArray.at(0).toBool()));
- vector2D.setY(static_cast<GLboolean>(valueArray.at(1).toBool()));
- return QVariant(vector2D);
- case GL_BOOL_VEC3:
- vector3D.setX(static_cast<GLboolean>(valueArray.at(0).toBool()));
- vector3D.setY(static_cast<GLboolean>(valueArray.at(1).toBool()));
- vector3D.setZ(static_cast<GLboolean>(valueArray.at(2).toBool()));
- return QVariant(vector3D);
- case GL_BOOL_VEC4:
- vector4D.setX(static_cast<GLboolean>(valueArray.at(0).toBool()));
- vector4D.setY(static_cast<GLboolean>(valueArray.at(1).toBool()));
- vector4D.setZ(static_cast<GLboolean>(valueArray.at(2).toBool()));
- vector4D.setW(static_cast<GLboolean>(valueArray.at(3).toBool()));
- return QVariant(vector4D);
- case GL_FLOAT_MAT2:
- //Matrix2x2 is in Row Major ordering (so we need to convert)
- dataMat2[0] = static_cast<GLfloat>(valueArray.at(0).toDouble());
- dataMat2[1] = static_cast<GLfloat>(valueArray.at(2).toDouble());
- dataMat2[2] = static_cast<GLfloat>(valueArray.at(1).toDouble());
- dataMat2[3] = static_cast<GLfloat>(valueArray.at(3).toDouble());
- return QVariant::fromValue(QMatrix2x2(dataMat2.constData()));
- case GL_FLOAT_MAT3:
- //Matrix3x3 is in Row Major ordering (so we need to convert)
- dataMat3[0] = static_cast<GLfloat>(valueArray.at(0).toDouble());
- dataMat3[1] = static_cast<GLfloat>(valueArray.at(3).toDouble());
- dataMat3[2] = static_cast<GLfloat>(valueArray.at(6).toDouble());
- dataMat3[3] = static_cast<GLfloat>(valueArray.at(1).toDouble());
- dataMat3[4] = static_cast<GLfloat>(valueArray.at(4).toDouble());
- dataMat3[5] = static_cast<GLfloat>(valueArray.at(7).toDouble());
- dataMat3[6] = static_cast<GLfloat>(valueArray.at(2).toDouble());
- dataMat3[7] = static_cast<GLfloat>(valueArray.at(5).toDouble());
- dataMat3[8] = static_cast<GLfloat>(valueArray.at(8).toDouble());
- return QVariant::fromValue(QMatrix3x3(dataMat3.constData()));
- case GL_FLOAT_MAT4:
- //Matrix4x4 is Column Major ordering
- return QVariant(QMatrix4x4(static_cast<GLfloat>(valueArray.at(0).toDouble()),
- static_cast<GLfloat>(valueArray.at(1).toDouble()),
- static_cast<GLfloat>(valueArray.at(2).toDouble()),
- static_cast<GLfloat>(valueArray.at(3).toDouble()),
- static_cast<GLfloat>(valueArray.at(4).toDouble()),
- static_cast<GLfloat>(valueArray.at(5).toDouble()),
- static_cast<GLfloat>(valueArray.at(6).toDouble()),
- static_cast<GLfloat>(valueArray.at(7).toDouble()),
- static_cast<GLfloat>(valueArray.at(8).toDouble()),
- static_cast<GLfloat>(valueArray.at(9).toDouble()),
- static_cast<GLfloat>(valueArray.at(10).toDouble()),
- static_cast<GLfloat>(valueArray.at(11).toDouble()),
- static_cast<GLfloat>(valueArray.at(12).toDouble()),
- static_cast<GLfloat>(valueArray.at(13).toDouble()),
- static_cast<GLfloat>(valueArray.at(14).toDouble()),
- static_cast<GLfloat>(valueArray.at(15).toDouble())));
- case GL_SAMPLER_2D:
- return QVariant(valueArray.at(0).toString());
- }
- }
- return QVariant();
-}
-
-QAttribute::DataType GLTFParser::accessorTypeFromJSON(int componentType)
-{
- if (componentType == GL_BYTE) {
- return QAttribute::Byte;
- } else if (componentType == GL_UNSIGNED_BYTE) {
- return QAttribute::UnsignedByte;
- } else if (componentType == GL_SHORT) {
- return QAttribute::Short;
- } else if (componentType == GL_UNSIGNED_SHORT) {
- return QAttribute::UnsignedShort;
- } else if (componentType == GL_UNSIGNED_INT) {
- return QAttribute::UnsignedInt;
- } else if (componentType == GL_FLOAT) {
- return QAttribute::Float;
- }
-
- //There shouldn't be an invalid case here
- qCWarning(GLTFParserLog) << "unsupported accessor type" << componentType;
- return QAttribute::Float;
-}
-
-uint GLTFParser::accessorDataSizeFromJson(const QString &type)
-{
- QString typeName = type.toUpper();
- if (typeName == "SCALAR")
- return 1;
- if (typeName == "VEC2")
- return 2;
- if (typeName == "VEC3")
- return 3;
- if (typeName == "VEC4")
- return 4;
- if (typeName == "MAT2")
- return 4;
- if (typeName == "MAT3")
- return 9;
- if (typeName == "MAT4")
- return 16;
-
- return 0;
-}
-
-QRenderState *GLTFParser::buildStateEnable(int state)
-{
- int type = 0;
- //By calling buildState with QJsonValue(), a Render State with
- //default values is created.
-
- if (state == GL_BLEND) {
- //It doesn't make sense to handle this state alone
- return nullptr;
- }
-
- if (state == GL_CULL_FACE) {
- return buildState(QStringLiteral("cullFace"), QJsonValue(), type);
- }
-
- if (state == GL_DEPTH_TEST) {
- return buildState(QStringLiteral("depthFunc"), QJsonValue(), type);
- }
-
- if (state == GL_POLYGON_OFFSET_FILL) {
- return buildState(QStringLiteral("polygonOffset"), QJsonValue(), type);
- }
-
- if (state == GL_SAMPLE_ALPHA_TO_COVERAGE) {
- return new QAlphaCoverage();
- }
-
- if (state == GL_SCISSOR_TEST) {
- return buildState(QStringLiteral("scissor"), QJsonValue(), type);
- }
-
- qCWarning(GLTFParserLog) << Q_FUNC_INFO << "unsupported render state:" << state;
-
- return nullptr;
-}
-
-QRenderState* GLTFParser::buildState(const QString& functionName, const QJsonValue &value, int &type)
-{
- type = -1;
- QJsonArray values = value.toArray();
-
- if (functionName == QStringLiteral("blendColor")) {
- type = GL_BLEND;
- //TODO: support render state blendColor
- qCWarning(GLTFParserLog) << Q_FUNC_INFO << "unsupported render state:" << functionName;
- return nullptr;
- }
-
- if (functionName == QStringLiteral("blendEquationSeparate")) {
- type = GL_BLEND;
- //TODO: support settings blendEquation alpha
- QBlendEquation *blendEquation = new QBlendEquation;
- blendEquation->setMode((QBlendEquation::BlendMode)values.at(0).toInt(GL_FUNC_ADD));
- return blendEquation;
- }
-
- if (functionName == QStringLiteral("blendFuncSeparate")) {
- type = GL_BLEND;
- QBlendStateSeparate *blendState = new QBlendStateSeparate;
- blendState->setSrcRGB((QBlendState::Blending)values.at(0).toInt(GL_ONE));
- blendState->setSrcAlpha((QBlendState::Blending)values.at(1).toInt(GL_ONE));
- blendState->setDstRGB((QBlendState::Blending)values.at(2).toInt(GL_ZERO));
- blendState->setDstAlpha((QBlendState::Blending)values.at(3).toInt(GL_ZERO));
- return blendState;
- }
-
- if (functionName == QStringLiteral("colorMask")) {
- QColorMask *colorMask = new QColorMask;
- colorMask->setRed(values.at(0).toBool(true));
- colorMask->setGreen(values.at(1).toBool(true));
- colorMask->setBlue(values.at(2).toBool(true));
- colorMask->setAlpha(values.at(3).toBool(true));
- return colorMask;
- }
-
- if (functionName == QStringLiteral("cullFace")) {
- type = GL_CULL_FACE;
- QCullFace *cullFace = new QCullFace;
- cullFace->setMode((QCullFace::CullingMode)values.at(0).toInt(GL_BACK));
- return cullFace;
- }
-
- if (functionName == QStringLiteral("depthFunc")) {
- type = GL_DEPTH_TEST;
- QDepthTest *depthTest = new QDepthTest;
- depthTest->setFunc((QDepthTest::DepthFunc)values.at(0).toInt(GL_LESS));
- return depthTest;
- }
-
- if (functionName == QStringLiteral("depthMask")) {
- QDepthMask *depthMask = new QDepthMask;
- depthMask->setMask(values.at(0).toBool(true));
- }
-
- if (functionName == QStringLiteral("depthRange")) {
- //TODO: support render state depthRange
- qCWarning(GLTFParserLog) << Q_FUNC_INFO << "unsupported render state:" << functionName;
- return nullptr;
- }
-
- if (functionName == QStringLiteral("frontFace")) {
- QFrontFace *frontFace = new QFrontFace;
- frontFace->setDirection((QFrontFace::FaceDir)values.at(0).toInt(GL_CCW));
- return frontFace;
- }
-
- if (functionName == QStringLiteral("lineWidth")) {
- //TODO: support render state lineWidth
- qCWarning(GLTFParserLog) << Q_FUNC_INFO << "unsupported render state:" << functionName;
- return nullptr;
- }
-
- if (functionName == QStringLiteral("polygonOffset")) {
- type = GL_POLYGON_OFFSET_FILL;
- QPolygonOffset *polygonOffset = new QPolygonOffset;
- polygonOffset->setFactor((float)values.at(0).toDouble(0.0f));
- polygonOffset->setUnits((float)values.at(1).toDouble(0.0f));
- return polygonOffset;
- }
-
- if (functionName == QStringLiteral("scissor")) {
- type = GL_SCISSOR_TEST;
- QScissorTest *scissorTest = new QScissorTest;
- scissorTest->setLeft(values.at(0).toDouble(0.0f));
- scissorTest->setBottom(values.at(1).toDouble(0.0f));
- scissorTest->setWidth(values.at(2).toDouble(0.0f));
- scissorTest->setHeight(values.at(3).toDouble(0.0f));
- return scissorTest;
- }
-
- qCWarning(GLTFParserLog) << Q_FUNC_INFO << "unsupported render state:" << functionName;
- return nullptr;
-}
-
-} // namespace Qt3DRender
-
-QT_END_NAMESPACE
diff --git a/src/quick3d/imports/scene3d/scene3drenderer.cpp b/src/quick3d/imports/scene3d/scene3drenderer.cpp
index ca637f830..bda8e7343 100644
--- a/src/quick3d/imports/scene3d/scene3drenderer.cpp
+++ b/src/quick3d/imports/scene3d/scene3drenderer.cpp
@@ -313,6 +313,7 @@ void Scene3DRenderer::synchronize()
m_lastMultisample = m_multisample;
m_node->markDirty(QSGNode::DirtyMaterial);
+ m_item->update();
}
}
diff --git a/src/render/backend/commandexecuter.cpp b/src/render/backend/commandexecuter.cpp
index 54c3548ba..8bccb1437 100644
--- a/src/render/backend/commandexecuter.cpp
+++ b/src/render/backend/commandexecuter.cpp
@@ -265,7 +265,7 @@ QJsonObject parameterPackToJson(const Render::ShaderParameterPack &pack)
for (const auto & texture : textures) {
QJsonObject textureObj;
textureObj.insert(QLatin1String("name"), Render::StringToInt::lookupString(texture.glslNameId));
- textureObj.insert(QLatin1String("id"), qint64(texture.texId.id()));
+ textureObj.insert(QLatin1String("id"), qint64(texture.nodeId.id()));
texturesArray.push_back(textureObj);
}
obj.insert(QLatin1String("textures"), texturesArray);
diff --git a/src/render/backend/managers.cpp b/src/render/backend/managers.cpp
index 26fd68600..aa7dbb741 100644
--- a/src/render/backend/managers.cpp
+++ b/src/render/backend/managers.cpp
@@ -110,6 +110,12 @@ void JointManager::addDirtyJoint(Qt3DCore::QNodeId jointId)
m_dirtyJoints.push_back(jointHandle);
}
+void JointManager::removeDirtyJoint(Qt3DCore::QNodeId jointId)
+{
+ const HJoint jointHandle = lookupHandle(jointId);
+ m_dirtyJoints.removeAll(jointHandle);
+}
+
QVector<HJoint> JointManager::dirtyJoints()
{
return std::move(m_dirtyJoints);
diff --git a/src/render/backend/managers_p.h b/src/render/backend/managers_p.h
index 89b02b86f..759c16f64 100644
--- a/src/render/backend/managers_p.h
+++ b/src/render/backend/managers_p.h
@@ -443,6 +443,7 @@ class JointManager : public Qt3DCore::QResourceManager<
{
public:
void addDirtyJoint(Qt3DCore::QNodeId jointId);
+ void removeDirtyJoint(Qt3DCore::QNodeId jointId);
QVector<HJoint> dirtyJoints();
private:
diff --git a/src/render/backend/uniform.cpp b/src/render/backend/uniform.cpp
index 41ee24967..17c26e6c7 100644
--- a/src/render/backend/uniform.cpp
+++ b/src/render/backend/uniform.cpp
@@ -38,6 +38,7 @@
****************************************************************************/
#include "uniform_p.h"
+#include "qabstracttexture.h"
QT_BEGIN_NAMESPACE
@@ -261,6 +262,11 @@ UniformValue UniformValue::fromVariant(const QVariant &variant)
memcpy(v.data<float>(), mat33.constData(), 9 * sizeof(float));
break;
}
+ if (variant.userType() == qMetaTypeId<Qt3DRender::QAbstractTexture *>()) {
+ // silently ignore null texture pointers as they are common while textures are loading
+ if (variant.value<Qt3DRender::QAbstractTexture *>() == nullptr)
+ break;
+ }
qWarning() << "Unknown uniform type or value:" << variant << "Please check your QParameters";
}
}
diff --git a/src/render/framegraph/qclearbuffers.cpp b/src/render/framegraph/qclearbuffers.cpp
index 67773a6b7..4cc4c98b6 100644
--- a/src/render/framegraph/qclearbuffers.cpp
+++ b/src/render/framegraph/qclearbuffers.cpp
@@ -220,7 +220,7 @@ void QClearBuffers::setClearStencilValue(int clearStencilValue)
ColorBuffer flag is set, all color buffers will be cleared.
*/
/*!
- \qmlproperty RenderTargetOutput Qt3D.Render::ClearBuffers::colorbuffer
+ \qmlproperty RenderTargetOutput Qt3D.Render::ClearBuffers::colorBuffer
Specifies a specific color buffer to clear. If set to NULL (default), and
ColorBuffer flag is set, all color buffers will be cleared.
*/
diff --git a/src/render/frontend/qcamera.cpp b/src/render/frontend/qcamera.cpp
index 274b555f6..29703878d 100644
--- a/src/render/frontend/qcamera.cpp
+++ b/src/render/frontend/qcamera.cpp
@@ -254,11 +254,14 @@ void QCameraPrivate::updateViewMatrixAndTransform(bool doEmit)
/*!
* \qmlproperty enumeration Qt3D.Render::Camera::projectionType
*
- * Holds the type of the camera projection.
+ * Holds the type of the camera projection. The default value is
+ * CameraLens.PerspectiveProjection.
*
* \list
- * \li CameraLens.OrthographicProjection
- * \li CameraLens.PerspectiveProjection
+ * \li CameraLens.OrthographicProjection - Parallel lines appear parallel. Objects appear
+ * the same size regardless of distance.
+ * \li CameraLens.PerspectiveProjection - Parallel lines appear to meet in the distance.
+ * Objects appear to shrink the farther they are from the camera.
* \li CameraLens.FrustumProjection
* \li CameraLens.CustomProjection
* \endlist
@@ -267,17 +270,28 @@ void QCameraPrivate::updateViewMatrixAndTransform(bool doEmit)
/*!
* \qmlproperty real Qt3D.Render::Camera::nearPlane
- * Holds the current camera near plane of the camera.
+ * Holds the current camera near plane of the camera. Objects that
+ * are closer to the camera than the nearPlane will not be rendered.
*/
/*!
* \qmlproperty real Qt3D.Render::Camera::farPlane
- * Holds the current camera far plane of the camera.
+ * Holds the current camera far plane of the camera. Objects that
+ * are farther from the camera than the farPlane will not be rendered.
*/
/*!
* \qmlproperty real Qt3D.Render::Camera::fieldOfView
- * Holds the current field of view of the camera in degrees.
+ * Holds the current vertical field of view of the camera in degrees.
+ *
+ * Along with \l aspectRatio, this property determines how much of
+ * the scene is visible to the camera. In that respect you might
+ * think of it as analogous to choosing a wide angle (wide horizontal
+ * field of view) or telephoto (narrow horizontal field of view) lens,
+ * depending on how much of a scene you want to capture.
+ *
+ * fieldOfView is only relevant when \l projectionType is
+ * CameraLens.PerspectiveProjection.
*/
/*!
@@ -288,21 +302,33 @@ void QCameraPrivate::updateViewMatrixAndTransform(bool doEmit)
/*!
*\qmlproperty real Qt3D.Render::Camera::left
* Holds the current left of the camera.
+ *
+ * This property is only relevant when \l projectionType is
+ * CameraLens.OrthographicProjection.
*/
/*!
* \qmlproperty real Qt3D.Render::Camera::right
* Holds the current right of the camera.
+ *
+ * This property is only relevant when \l projectionType is
+ * CameraLens.OrthographicProjection.
*/
/*!
* \qmlproperty real Qt3D.Render::Camera::bottom
* Holds the current bottom of the camera.
+ *
+ * This property is only relevant when \l projectionType is
+ * CameraLens.OrthographicProjection.
*/
/*!
* \qmlproperty real Qt3D.Render::Camera::top
* Holds the current top of the camera.
+ *
+ * This property is only relevant when \l projectionType is
+ * CameraLens.OrthographicProjection.
*/
/*!
@@ -321,19 +347,29 @@ void QCameraPrivate::updateViewMatrixAndTransform(bool doEmit)
* \qmlproperty vector3d Qt3D.Render::Camera::upVector
* Holds the current up vector of the camera in coordinates relative to
* the parent entity.
+ *
+ * The up vector indicates which direction the top of the camera is
+ * facing. Think of taking a picture: after positioning yourself
+ * and pointing the camera at your target, you might rotate the camera
+ * left or right, giving you a portrait or landscape (or angled!)
+ * shot. upVector allows you to control this type of movement.
*/
/*!
* \qmlproperty vector3d Qt3D.Render::Camera::viewCenter
* Holds the current view center of the camera in coordinates relative to
* the parent entity.
- * \readonly
+ *
+ * Intuitively, the viewCenter is the location the camera is pointing at.
*/
/*!
* \qmlproperty vector3d Qt3D.Render::Camera::viewVector
* Holds the camera's view vector in coordinates relative to
* the parent entity.
+ *
+ * This vector decribes the displacement from the camera (\l position)
+ * to its target (\l viewCenter).
* \readonly
*/
@@ -348,30 +384,44 @@ void QCameraPrivate::updateViewMatrixAndTransform(bool doEmit)
/*!
* \property QCamera::projectionType
*
- * Holds the type of the camera projection.
+ * Holds the type of the camera projection. The default value is
+ * QCameraLens::PerspectiveProjection.
*
* \list
- * \li CameraLens.OrthographicProjection
- * \li CameraLens.PerspectiveProjection
- * \li CameraLens.FrustumProjection
- * \li CameraLens.CustomProjection
+ * \li QCameraLens::OrthographicProjection - Parallel lines appear parallel. Objects appear
+ * the same size regardless of distance.
+ * \li QCameraLens::PerspectiveProjection - Parallel lines appear to meet in the distance.
+ * Objects appear to shrink the farther they are from the camera.
+ * \li QCameraLens::FrustumProjection
+ * \li QCameraLens::CustomProjection
* \endlist
* \sa Qt3DRender::QCameraLens::ProjectionType
*/
/*!
* \property QCamera::nearPlane
- * Holds the current camera near plane.
+ * Holds the current camera near plane. Objects that are closer to the
+ * camera than the nearPlane will not be rendered.
*/
/*!
* \property QCamera::farPlane
- * Holds the current camera far plane.
+ * Holds the current camera far plane. Objects that are farther from the
+ * camera than the farPlane will not be rendered.
*/
/*!
* \property QCamera::fieldOfView
- * Holds the current field of view in degrees.
+ * Holds the current vertical field of view in degrees.
+ *
+ * Along with \l aspectRatio, this property determines how much of
+ * the scene is visible to the camera. In that respect you might
+ * think of it as analogous to choosing a wide angle (wide horizontal
+ * field of view) or telephoto (narrow horizontal field of view) lens
+ * depending on how much of a scene you want to capture.
+ *
+ * fieldOfView is only relevant when \l projectionType is
+ * QCameraLens::PerspectiveProjection.
*/
/*!
@@ -382,21 +432,33 @@ void QCameraPrivate::updateViewMatrixAndTransform(bool doEmit)
/*!
*\property QCamera::left
* Holds the current left of the camera.
+ *
+ * This property is only relevant when \l projectionType is
+ * QCameraLens::OrthographicProjection.
*/
/*!
* \property QCamera::right
* Holds the current right of the camera.
+ *
+ * This property is only relevant when \l projectionType is
+ * QCameraLens::OrthographicProjection.
*/
/*!
* \property QCamera::bottom
* Holds the current bottom of the camera.
+ *
+ * This property is only relevant when \l projectionType is
+ * QCameraLens::OrthographicProjection.
*/
/*!
* \property QCamera::top
* Holds the current top of the camera.
+ *
+ * This property is only relevant when \l projectionType is
+ * QCameraLens::OrthographicProjection.
*/
/*!
@@ -419,18 +481,29 @@ void QCameraPrivate::updateViewMatrixAndTransform(bool doEmit)
* \property QCamera::upVector
* Holds the camera's up vector in coordinates relative to
* the parent entity.
+ *
+ * The up vector indicates which direction the top of the camera is
+ * facing. Think of taking a picture: after positioning yourself
+ * and pointing the camera at your target, you might rotate the camera
+ * left or right, giving you a portrait or landscape (or angled!)
+ * shot. upVector allows you to control this type of movement.
*/
/*!
* \property QCamera::viewCenter
* Holds the camera's view center in coordinates relative to
* the parent entity.
+ *
+ * Intuitively, the viewCenter is the location the camera is pointing at.
*/
/*!
* \property QCamera::viewVector
* Holds the camera's view vector in coordinates relative to
* the parent entity.
+ *
+ * This vector decribes the displacement from the camera (\l position)
+ * to its target (\l viewCenter).
*/
/*!
diff --git a/src/render/geometry/joint.cpp b/src/render/geometry/joint.cpp
index 9c53b8ef8..c770564f9 100644
--- a/src/render/geometry/joint.cpp
+++ b/src/render/geometry/joint.cpp
@@ -153,6 +153,7 @@ Qt3DCore::QBackendNode *JointFunctor::get(Qt3DCore::QNodeId id) const
void JointFunctor::destroy(Qt3DCore::QNodeId id) const
{
+ m_jointManager->removeDirtyJoint(id);
m_jointManager->releaseResource(id);
}
diff --git a/src/render/lights/qdirectionallight.cpp b/src/render/lights/qdirectionallight.cpp
index 14bdb2c38..9b6e580de 100644
--- a/src/render/lights/qdirectionallight.cpp
+++ b/src/render/lights/qdirectionallight.cpp
@@ -76,6 +76,9 @@ QDirectionalLightPrivate::QDirectionalLightPrivate()
\since 5.7
\brief Encapsulate a Directional Light object in a Qt 3D scene.
+ A directional light is a light source that behaves similar to sunlight.
+ The light from a directional light hits all objects from the same direction
+ and with the same intensity, regardless of where they are in the scene.
*/
/*!
@@ -86,6 +89,9 @@ QDirectionalLightPrivate::QDirectionalLightPrivate()
\since 5.7
\brief Encapsulate a Directional Light object in a Qt 3D scene.
+ A directional light is a light source that behaves similar to sunlight.
+ The light from a directional light hits all objects from the same direction
+ and with the same intensity, regardless of where they are in the scene.
*/
/*!
diff --git a/src/render/lights/qenvironmentlight.cpp b/src/render/lights/qenvironmentlight.cpp
index 866905fb6..b3dac56ff 100644
--- a/src/render/lights/qenvironmentlight.cpp
+++ b/src/render/lights/qenvironmentlight.cpp
@@ -53,6 +53,9 @@ namespace Qt3DRender
* \instantiates Qt3DRender::QEnvironmentLight
* \brief Encapsulate an environment light object in a Qt 3D scene.
* \since 5.9
+ *
+ * EnvironmentLight uses cubemaps to implement image-based lighting (IBL), a technique
+ * often used in conjunction with physically-based rendering (PBR).
*/
QEnvironmentLightPrivate::QEnvironmentLightPrivate()
@@ -97,6 +100,9 @@ Qt3DCore::QNodeCreatedChangeBasePtr QEnvironmentLight::createNodeCreationChange(
\inmodule Qt3DRender
\brief Encapsulate an environment light object in a Qt 3D scene.
\since 5.9
+
+ EnvironmentLight uses cubemaps to implement image-based lighting (IBL), a technique
+ often used in conjunction with physically-based rendering (PBR).
*/
QEnvironmentLight::QEnvironmentLight(Qt3DCore::QNode *parent)
diff --git a/src/render/lights/qpointlight.cpp b/src/render/lights/qpointlight.cpp
index 28dd265b8..2b042c91d 100644
--- a/src/render/lights/qpointlight.cpp
+++ b/src/render/lights/qpointlight.cpp
@@ -75,6 +75,19 @@ QPointLightPrivate::QPointLightPrivate()
\since 5.5
\brief Encapsulate a Point Light object in a Qt 3D scene.
+ A point light is a light source that emits light in all directions, from a single point.
+ Conceptually, this is similar to light given off by a standard light bulb.
+
+ A point light uses three attenuation factors to describe how the intensity of the light
+ decreases over distance. These factors are designed to be used together in calcuating total
+ attenuation. For the materials in Qt3D Extras the following formula is used, where distance
+ is the distance from the light to the surface being rendered:
+
+ \code
+ totalAttenuation = 1.0 / (constantAttenuation + (linearAttenuation * distance) + (quadraticAttenuation * distance * distance));
+ \endcode
+
+ Custom materials may choose to interpret these factors differently.
*/
/*!
@@ -84,6 +97,20 @@ QPointLightPrivate::QPointLightPrivate()
\inqmlmodule Qt3D.Render
\since 5.5
\brief Encapsulate a Point Light object in a Qt 3D scene.
+
+ A point light is a light source that emits light in all directions, from a single point.
+ Conceptually, this is similar to light given off by a standard light bulb.
+
+ A point light uses three attenuation factors to describe how the intensity of the light
+ decreases over distance. These factors are designed to be used together in calcuating total
+ attenuation. For the materials in Qt3D Extras the following formula is used, where distance
+ is the distance from the light to the surface being rendered:
+
+ \code
+ totalAttenuation = 1.0 / (constantAttenuation + (linearAttenuation * distance) + (quadraticAttenuation * distance * distance));
+ \endcode
+
+ Custom materials may choose to interpret these factors differently.
*/
/*!
diff --git a/src/render/lights/qspotlight.cpp b/src/render/lights/qspotlight.cpp
index 81c18387b..c4deaf817 100644
--- a/src/render/lights/qspotlight.cpp
+++ b/src/render/lights/qspotlight.cpp
@@ -83,6 +83,18 @@ QSpotLightPrivate::QSpotLightPrivate()
\since 5.5
\brief Encapsulate a Spot Light object in a Qt 3D scene.
+ A spotlight is a light source that emits a cone of light in a particular direction.
+
+ A spotlight uses three attenuation factors to describe how the intensity of the light
+ decreases over distance. These factors are designed to be used together in calcuating total
+ attenuation. For the materials in Qt3D Extras the following formula is used, where distance
+ is the distance from the light to the surface being rendered:
+
+ \code
+ totalAttenuation = 1.0 / (constantAttenuation + (linearAttenuation * distance) + (quadraticAttenuation * distance * distance));
+ \endcode
+
+ Custom materials may choose to interpret these factors differently.
*/
/*!
@@ -93,6 +105,18 @@ QSpotLightPrivate::QSpotLightPrivate()
\since 5.5
\brief Encapsulate a Spot Light object in a Qt 3D scene.
+ A spotlight is a light source that emits a cone of light in a particular direction.
+
+ A spotlight uses three attenuation factors to describe how the intensity of the light
+ decreases over distance. These factors are designed to be used together in calcuating total
+ attenuation. For the materials in Qt3D Extras the following formula is used, where distance
+ is the distance from the light to the surface being rendered:
+
+ \code
+ totalAttenuation = 1.0 / (constantAttenuation + (linearAttenuation * distance) + (quadraticAttenuation * distance * distance));
+ \endcode
+
+ Custom materials may choose to interpret these factors differently.
*/
/*!
diff --git a/src/render/materialsystem/qshaderimage_p.h b/src/render/materialsystem/qshaderimage_p.h
index 93eb8635d..33cffb84a 100644
--- a/src/render/materialsystem/qshaderimage_p.h
+++ b/src/render/materialsystem/qshaderimage_p.h
@@ -40,6 +40,17 @@
#ifndef QT3DRENDER_QSHADERIMAGE_P_H
#define QT3DRENDER_QSHADERIMAGE_P_H
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
#include <Qt3DCore/private/qnode_p.h>
#include <Qt3DRender/qshaderimage.h>
diff --git a/src/render/materialsystem/shaderdata.cpp b/src/render/materialsystem/shaderdata.cpp
index de423c3c2..130333898 100644
--- a/src/render/materialsystem/shaderdata.cpp
+++ b/src/render/materialsystem/shaderdata.cpp
@@ -245,7 +245,7 @@ void ShaderData::sceneChangeEvent(const Qt3DCore::QSceneChangePtr &e)
// Note we aren't notified about nested QShaderData in this call
// only scalar / vec properties
m_originalProperties.insert(propertyName, propertyValue);
- BackendNode::markDirty(AbstractRenderer::AllDirty);
+ BackendNode::markDirty(AbstractRenderer::ParameterDirty);
}
BackendNode::sceneChangeEvent(e);
diff --git a/src/render/raycasting/qray3d.cpp b/src/render/raycasting/qray3d.cpp
index d8ad25b50..18dd9a40a 100644
--- a/src/render/raycasting/qray3d.cpp
+++ b/src/render/raycasting/qray3d.cpp
@@ -101,7 +101,7 @@ QRay3D::QRay3D()
*/
QRay3D::QRay3D(const Vector3D &origin, const Vector3D &direction, float distance)
: m_origin(origin)
- , m_direction(direction)
+ , m_direction(direction.normalized())
, m_distance(distance)
{}
@@ -157,7 +157,7 @@ void QRay3D::setDirection(const Vector3D &value)
if (value.isNull())
return;
- m_direction = value;
+ m_direction = value.normalized();
}
float QRay3D::distance() const
@@ -178,14 +178,14 @@ Vector3D QRay3D::point(float t) const
QRay3D &QRay3D::transform(const Matrix4x4 &matrix)
{
m_origin = matrix * m_origin;
- m_direction = matrix.mapVector(m_direction);
+ m_direction = matrix.mapVector(m_direction).normalized();
return *this;
}
QRay3D QRay3D::transformed(const Matrix4x4 &matrix) const
{
- return QRay3D(matrix * m_origin, matrix.mapVector(m_direction));
+ return QRay3D(matrix * m_origin, matrix.mapVector(m_direction).normalized());
}
bool QRay3D::operator==(const QRay3D &other) const
diff --git a/src/render/raycasting/qraycastingservice.cpp b/src/render/raycasting/qraycastingservice.cpp
index a0f21aa41..bdb1557f7 100644
--- a/src/render/raycasting/qraycastingservice.cpp
+++ b/src/render/raycasting/qraycastingservice.cpp
@@ -43,7 +43,6 @@
#include <Qt3DRender/private/sphere_p.h>
#include <Qt3DRender/private/qboundingvolumeprovider_p.h>
-#include <QMap>
#include <QtConcurrent>
#include "math.h"
diff --git a/src/render/renderers/opengl/jobs/renderviewjobutils.cpp b/src/render/renderers/opengl/jobs/renderviewjobutils.cpp
index e55497d06..d2babe58a 100644
--- a/src/render/renderers/opengl/jobs/renderviewjobutils.cpp
+++ b/src/render/renderers/opengl/jobs/renderviewjobutils.cpp
@@ -194,8 +194,10 @@ void setRenderViewConfigFromFrameGraphLeafNode(RenderView *rv, const FrameGraphN
rv->setStateSet(stateSet);
}
+ // Add states from new stateSet we might be missing
+ // but don' t override existing states (lower StateSetNode always has priority)
if (rStateSet->hasRenderStates())
- addToRenderStateSet(stateSet, rStateSet->renderStates(), manager->renderStateManager());
+ addUniqueStatesToRenderStateSet(stateSet, rStateSet->renderStates(), manager->renderStateManager());
break;
}
@@ -433,14 +435,16 @@ void parametersFromMaterialEffectTechnique(ParameterInfoList *infoList,
parametersFromParametersProvider(infoList, manager, technique);
}
-void addToRenderStateSet(RenderStateSet *stateSet,
- const QVector<Qt3DCore::QNodeId> stateIds,
- RenderStateManager *manager)
+// Only add states with types we don't already have
+void addUniqueStatesToRenderStateSet(RenderStateSet *stateSet,
+ const QVector<Qt3DCore::QNodeId> stateIds,
+ RenderStateManager *manager)
{
for (const Qt3DCore::QNodeId &stateId : stateIds) {
RenderStateNode *node = manager->lookupResource(stateId);
- if (node->isEnabled())
+ if (node->isEnabled() && !stateSet->hasStateOfType(node->type())) {
stateSet->addState(node->impl());
+ }
}
}
diff --git a/src/render/renderers/opengl/jobs/renderviewjobutils_p.h b/src/render/renderers/opengl/jobs/renderviewjobutils_p.h
index 90c4a53cc..bd2e12534 100644
--- a/src/render/renderers/opengl/jobs/renderviewjobutils_p.h
+++ b/src/render/renderers/opengl/jobs/renderviewjobutils_p.h
@@ -150,9 +150,9 @@ void parametersFromParametersProvider(ParameterInfoList *infoList,
Q_AUTOTEST_EXPORT ParameterInfoList::const_iterator findParamInfo(ParameterInfoList *infoList,
const int nameId);
-Q_AUTOTEST_EXPORT void addToRenderStateSet(RenderStateSet *stateSet,
- const QVector<Qt3DCore::QNodeId> stateIds,
- RenderStateManager *manager);
+Q_AUTOTEST_EXPORT void addUniqueStatesToRenderStateSet(RenderStateSet *stateSet,
+ const QVector<Qt3DCore::QNodeId> stateIds,
+ RenderStateManager *manager);
typedef QHash<int, QVariant> UniformBlockValueBuilderHash;
diff --git a/src/render/renderers/opengl/renderer/renderer.cpp b/src/render/renderers/opengl/renderer/renderer.cpp
index 68d31be71..b60eea33d 100644
--- a/src/render/renderers/opengl/renderer/renderer.cpp
+++ b/src/render/renderers/opengl/renderer/renderer.cpp
@@ -1648,7 +1648,7 @@ Renderer::ViewSubmissionResultData Renderer::submitRenderViews(const QVector<Ren
// Reset state and call doneCurrent if the surface
// is valid and was actually activated
- if (surface && m_submissionContext->hasValidGLHelper()) {
+ if (lastUsedSurface && m_submissionContext->hasValidGLHelper()) {
// Reset state to the default state if the last stateset is not the
// defaultRenderStateSet
if (m_submissionContext->currentStateSet() != m_defaultRenderStateSet)
diff --git a/src/render/renderers/opengl/renderer/renderview.cpp b/src/render/renderers/opengl/renderer/renderview.cpp
index ebb1ee7ab..5845fe147 100644
--- a/src/render/renderers/opengl/renderer/renderview.cpp
+++ b/src/render/renderers/opengl/renderer/renderview.cpp
@@ -656,10 +656,7 @@ QVector<RenderCommand *> RenderView::buildDrawRenderCommands(const QVector<Entit
RenderPass *pass = passData.pass;
if (pass->hasRenderStates()) {
command->m_stateSet = new RenderStateSet();
- addToRenderStateSet(command->m_stateSet, pass->renderStates(), m_manager->renderStateManager());
-
- // Merge per pass stateset with global stateset
- // so that the local stateset only overrides
+ addUniqueStatesToRenderStateSet(command->m_stateSet, pass->renderStates(), m_manager->renderStateManager());
if (m_stateSet != nullptr)
command->m_stateSet->merge(m_stateSet);
command->m_changeCost = m_renderer->defaultRenderState()->changeCost(command->m_stateSet);
@@ -779,15 +776,26 @@ QVector<RenderCommand *> RenderView::buildComputeRenderCommands(const QVector<En
// 1 RenderCommand per RenderPass pass on an Entity with a Mesh
for (const RenderPassParameterData &passData : renderPassData) {
// Add the RenderPass Parameters
- ParameterInfoList globalParameters = passData.parameterInfo;
+ RenderCommand *command = new RenderCommand();
RenderPass *pass = passData.pass;
- parametersFromParametersProvider(&globalParameters, m_manager->parameterManager(), pass);
- RenderCommand *command = new RenderCommand();
+ if (pass->hasRenderStates()) {
+ command->m_stateSet = new RenderStateSet();
+ addUniqueStatesToRenderStateSet(command->m_stateSet, pass->renderStates(), m_manager->renderStateManager());
+
+ // Merge per pass stateset with global stateset
+ // so that the local stateset only overrides
+ if (m_stateSet != nullptr)
+ command->m_stateSet->merge(m_stateSet);
+ command->m_changeCost = m_renderer->defaultRenderState()->changeCost(command->m_stateSet);
+ }
+
command->m_type = RenderCommand::Compute;
command->m_workGroups[0] = std::max(m_workGroups[0], computeJob->x());
command->m_workGroups[1] = std::max(m_workGroups[1], computeJob->y());
command->m_workGroups[2] = std::max(m_workGroups[2], computeJob->z());
+
+ ParameterInfoList globalParameters = passData.parameterInfo;
setShaderAndUniforms(command,
pass,
globalParameters,
diff --git a/src/render/renderers/opengl/renderstates/renderstateset.cpp b/src/render/renderers/opengl/renderstates/renderstateset.cpp
index f7fc279a1..b14695c77 100644
--- a/src/render/renderers/opengl/renderstates/renderstateset.cpp
+++ b/src/render/renderers/opengl/renderstates/renderstateset.cpp
@@ -103,9 +103,24 @@ StateMaskSet RenderStateSet::stateMask() const
return m_stateMask;
}
+// This modifies our state to add states from others
+// if we don't already contain a state with that type set
void RenderStateSet::merge(RenderStateSet *other)
{
m_stateMask |= other->stateMask();
+ const QVector<StateVariant> otherStates = other->states();
+
+ // We only add states which are new (different type)
+ for (const StateVariant &otherState : otherStates) {
+ const bool hasFoundStateOfSameType = hasStateOfType(otherState.type);
+ if (!hasFoundStateOfSameType)
+ m_states.push_back(otherState);
+ }
+}
+
+bool RenderStateSet::hasStateOfType(StateMask type) const
+{
+ return (type & stateMask());
}
bool RenderStateSet::contains(const StateVariant &ds) const
diff --git a/src/render/renderers/opengl/renderstates/renderstateset_p.h b/src/render/renderers/opengl/renderstates/renderstateset_p.h
index 09b58b859..29be4d2f1 100644
--- a/src/render/renderers/opengl/renderstates/renderstateset_p.h
+++ b/src/render/renderers/opengl/renderstates/renderstateset_p.h
@@ -93,6 +93,9 @@ public:
QVector<StateVariant> states() const { return m_states; }
+ bool hasStateOfType(StateMask type) const;
+
+
/**
* @brief contains - check if this set contains a matching piece of state
* @param ds
diff --git a/src/render/renderstates/genericstate_p.h b/src/render/renderstates/genericstate_p.h
index 69c3dee15..b07487d65 100644
--- a/src/render/renderstates/genericstate_p.h
+++ b/src/render/renderstates/genericstate_p.h
@@ -96,7 +96,7 @@ public:
bool equalTo(const RenderStateImpl &renderState) const override
{
const GenericState *other = static_cast<const GenericState*>(&renderState);
- return (other != NULL && other->m_values == m_values);
+ return (other != nullptr && other->m_values == m_values);
}
StateMask mask() const override
diff --git a/src/render/renderstates/renderstatenode_p.h b/src/render/renderstates/renderstatenode_p.h
index 886bb0c95..277b8a7c8 100644
--- a/src/render/renderstates/renderstatenode_p.h
+++ b/src/render/renderstates/renderstatenode_p.h
@@ -65,7 +65,7 @@ public:
void sceneChangeEvent(const Qt3DCore::QSceneChangePtr &e) override;
- StateMaskSet mask() const { return m_impl.type; }
+ StateMask type() const { return m_impl.type; }
StateVariant impl() const { return m_impl; }
protected:
diff --git a/src/render/texture/qtextureimage.cpp b/src/render/texture/qtextureimage.cpp
index 6bcdaba7d..b4e9ed46f 100644
--- a/src/render/texture/qtextureimage.cpp
+++ b/src/render/texture/qtextureimage.cpp
@@ -289,10 +289,12 @@ QImageTextureDataFunctor::QImageTextureDataFunctor(const QUrl &url, bool mirrore
QTextureImageDataPtr QImageTextureDataFunctor::operator ()()
{
+ if (!m_url.isValid())
+ return QTextureImageDataPtr();
+
// We assume that a texture image is going to contain a single image data
// For compressed dds or ktx textures a warning should be issued if
// there are layers or 3D textures
-
if (!Qt3DCore::QDownloadHelperService::isLocal(m_url))
qWarning() << "QTextureImage only supports local url";
diff --git a/tests/auto/render/gltexture/tst_gltexture.cpp b/tests/auto/render/gltexture/tst_gltexture.cpp
index 8a8701f8e..e971078d0 100644
--- a/tests/auto/render/gltexture/tst_gltexture.cpp
+++ b/tests/auto/render/gltexture/tst_gltexture.cpp
@@ -864,6 +864,44 @@ private Q_SLOTS:
// Cleanup
texture.destroy();
}
+
+ void checkPropertiesAfterLoadTextureDataFromImages()
+ {
+ // GIVEN
+ GLTexture texture;
+ TextureProperties props;
+ props.target = QAbstractTexture::TargetCubeMap;
+ props.format = QAbstractTexture::Automatic;
+ props.width = 1;
+ props.height = 1;
+ texture.setProperties(props);
+ QVector<GLTexture::Image> images;
+ // test a image texture data generator whose url is invalid
+ QImageTextureDataFunctorPtr gen = QImageTextureDataFunctorPtr::create(QUrl(), true);
+ images.push_back({gen, 0, 0, QAbstractTexture::CubeMapPositiveX});
+ texture.setImages(images);
+
+ // WHEN
+ texture.createOrUpdateGLTexture();
+
+ // THEN
+ QCOMPARE(texture.properties().format, QAbstractTexture::Automatic);
+
+ // WHEN
+ // test a image texture data generator whose url is valid
+ gen = QImageTextureDataFunctorPtr::create(QUrl("qrc:/image.jpg"), true);
+ images.clear();
+ images.push_back({gen, 0, 0, QAbstractTexture::CubeMapPositiveX});
+ texture.setImages(images);
+ texture.createOrUpdateGLTexture();
+
+ // THEN
+ QVERIFY(texture.properties().format != QAbstractTexture::Automatic);
+ QVERIFY(texture.properties().format != QAbstractTexture::NoFormat);
+
+ // Cleanup
+ texture.destroy();
+ }
};
QTEST_MAIN(tst_GLTexture);
diff --git a/tests/auto/render/qray3d/tst_qray3d.cpp b/tests/auto/render/qray3d/tst_qray3d.cpp
index dfc2a7396..ef0702a0a 100644
--- a/tests/auto/render/qray3d/tst_qray3d.cpp
+++ b/tests/auto/render/qray3d/tst_qray3d.cpp
@@ -108,23 +108,23 @@ void tst_QRay3D::create_data()
// non-normalized direction vectors
QTest::newRow("line on x-axis from origin - B")
<< Vector3D()
- << Vector3D(2.0f, 0.0f, 0.0f);
+ << Vector3D(2.0f, 0.0f, 0.0f).normalized();
QTest::newRow("line parallel -z-axis from 3,3,3 - B")
<< Vector3D(3.0f, 3.0f, 3.0f)
- << Vector3D(0.0f, 0.0f, -0.7f);
+ << Vector3D(0.0f, 0.0f, -0.7f).normalized();
QTest::newRow("vertical line (parallel to y-axis) - B")
<< Vector3D(0.5f, 0.0f, 0.5f)
- << Vector3D(0.0f, 5.3f, 0.0f);
+ << Vector3D(0.0f, 5.3f, 0.0f).normalized();
QTest::newRow("equidistant from all 3 axes - B")
<< Vector3D(0.5f, 0.0f, 0.5f)
- << Vector3D(1.0f, 1.0f, 1.0f);
+ << Vector3D(1.0f, 1.0f, 1.0f).normalized();
QTest::newRow("negative direction")
<< Vector3D(-3.0f, -3.0f, -3.0f)
- << Vector3D(-1.2f, -1.8f, -2.4f);
+ << Vector3D(-1.2f, -1.8f, -2.4f).normalized();
}
void tst_QRay3D::create()
@@ -203,32 +203,32 @@ void tst_QRay3D::point_data()
QTest::newRow("line on x-axis from origin")
<< Vector3D()
<< Vector3D(2.0f, 0.0f, 0.0f)
- << Vector3D(1.2f, 0.0f, 0.0f)
- << Vector3D(-14.4f, 0.0f, 0.0f);
+ << Vector3D(0.6f, 0.0f, 0.0f)
+ << Vector3D(-7.2f, 0.0f, 0.0f);
QTest::newRow("line parallel -z-axis from 3,3,3")
<< Vector3D(3.0f, 3.0f, 3.0f)
<< Vector3D(0.0f, 0.0f, -0.7f)
- << Vector3D(3.0f, 3.0f, 2.58f)
- << Vector3D(3.0f, 3.0f, 8.04f);
+ << Vector3D(3.0f, 3.0f, 2.4f)
+ << Vector3D(3.0f, 3.0f, 10.2f);
QTest::newRow("vertical line (parallel to y-axis)")
<< Vector3D(0.5f, 0.0f, 0.5f)
<< Vector3D(0.0f, 5.3f, 0.0f)
- << Vector3D(0.5f, 3.18f, 0.5f)
- << Vector3D(0.5f, -38.16f, 0.5f);
+ << Vector3D(0.5f, 0.6f, 0.5f)
+ << Vector3D(0.5f, -7.2f, 0.5f);
QTest::newRow("equidistant from all 3 axes")
<< Vector3D(0.5f, 0.0f, 0.5f)
<< Vector3D(1.0f, 1.0f, 1.0f)
- << Vector3D(1.1f, 0.6f, 1.1f)
- << Vector3D(-6.7f, -7.2f, -6.7f);
+ << Vector3D(0.84641f, 0.34641f, 0.84641f)
+ << Vector3D(-3.65692f, -4.15692f, -3.65692f);
QTest::newRow("negative direction")
<< Vector3D(-3.0f, -3.0f, -3.0f)
<< Vector3D(-1.2f, -1.8f, -2.4f)
- << Vector3D(-3.72f, -4.08f, -4.44f)
- << Vector3D(5.64f, 9.96f, 14.28f);
+ << Vector3D(-3.22283f, -3.33425f, -3.44567f)
+ << Vector3D(-0.325987f, 1.01102f, 2.34803f);
}
void tst_QRay3D::point()
@@ -475,7 +475,7 @@ void tst_QRay3D::transform()
QVERIFY(fuzzyCompare(ray1.direction(), ray3.direction()));
QVERIFY(fuzzyCompare(ray1.origin(), m * point));
- QVERIFY(fuzzyCompare(ray1.direction(), m.mapVector(direction)));
+ QVERIFY(fuzzyCompare(ray1.direction(), m.mapVector(direction).normalized()));
}
class tst_QRay3DProperties : public QObject
@@ -503,7 +503,7 @@ void tst_QRay3D::properties()
Qt3DRender::RayCasting::QRay3D r = qvariant_cast<Qt3DRender::RayCasting::QRay3D>(obj.property("ray"));
QCOMPARE(r.origin(), Vector3D(1, 2, 3));
- QCOMPARE(r.direction(), Vector3D(4, 5, 6));
+ QCOMPARE(r.direction(), Vector3D(4, 5, 6).normalized());
obj.setProperty("ray",
QVariant::fromValue
@@ -511,7 +511,7 @@ void tst_QRay3D::properties()
r = qvariant_cast<Qt3DRender::RayCasting::QRay3D>(obj.property("ray"));
QCOMPARE(r.origin(), Vector3D(-1, -2, -3));
- QCOMPARE(r.direction(), Vector3D(-4, -5, -6));
+ QCOMPARE(r.direction(), Vector3D(-4, -5, -6).normalized());
}
void tst_QRay3D::metaTypes()
diff --git a/tests/auto/render/qrenderstate/tst_qrenderstate.cpp b/tests/auto/render/qrenderstate/tst_qrenderstate.cpp
index 4dc850b25..596aa9553 100644
--- a/tests/auto/render/qrenderstate/tst_qrenderstate.cpp
+++ b/tests/auto/render/qrenderstate/tst_qrenderstate.cpp
@@ -187,8 +187,8 @@ private Q_SLOTS:
// THEN
RenderStateNode *backend1 = createBackendNode(frontend1);
RenderStateNode *backend2 = createBackendNode(frontend2);
- QVERIFY(backend1->mask() == mask);
- QVERIFY(backend2->mask() == mask);
+ QVERIFY(backend1->type() == mask);
+ QVERIFY(backend2->type() == mask);
QVERIFY(backend1->impl() != backend2->impl());
// WHEN
@@ -272,8 +272,8 @@ private Q_SLOTS:
// THEN
RenderStateNode *backend1 = createBackendNode(frontend1);
RenderStateNode *backend2 = createBackendNode(frontend2);
- QVERIFY(backend1->mask() == mask);
- QVERIFY(backend2->mask() == mask);
+ QVERIFY(backend1->type() == mask);
+ QVERIFY(backend2->type() == mask);
QVERIFY(backend1->impl() != backend2->impl());
// WHEN
diff --git a/tests/manual/manual.pro b/tests/manual/manual.pro
index 88e339561..07f7f9132 100644
--- a/tests/manual/manual.pro
+++ b/tests/manual/manual.pro
@@ -64,6 +64,7 @@ SUBDIRS += \
raster-cpp \
raster-qml \
qtbug-72236 \
+ qtbug-76766 \
shader-image-qml
qtHaveModule(multimedia): {
diff --git a/tests/manual/qtbug-76766/FrameGraph.qml b/tests/manual/qtbug-76766/FrameGraph.qml
new file mode 100644
index 000000000..0b096bc19
--- /dev/null
+++ b/tests/manual/qtbug-76766/FrameGraph.qml
@@ -0,0 +1,140 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import Qt3D.Core 2.9
+import Qt3D.Render 2.9
+
+RenderSurfaceSelector {
+ id: surfaceSelector
+
+ readonly property Layer layer: Layer {
+ recursive: true
+ }
+
+ property alias camera: cameraSelector.camera
+ property alias clearColor: clearBuffers.clearColor
+ property alias fbo : renderToFboSelector.target
+
+ Viewport {
+ normalizedRect: Qt.rect(0.0, 0.0, 1.0, 1.0)
+
+ // 1st: render scene to texture
+ RenderTargetSelector {
+ id : renderToFboSelector
+
+ RenderStateSet {
+ renderStates: [
+ MultiSampleAntiAliasing {},
+ DepthTest {
+ depthFunction: DepthTest.Less
+ },
+ CullFace{
+ mode: CullFace.Back
+ }
+ ]
+
+ ClearBuffers {
+ id: clearBuffers
+ buffers: ClearBuffers.ColorDepthBuffer
+ NoDraw {}
+ }
+
+ CameraSelector {
+ id: cameraSelector
+
+ LayerFilter {
+ layers: [layer]
+ filterMode: LayerFilter.DiscardAllMatchingLayers
+ TechniqueFilter {
+ RenderPassFilter {
+ matchAny: FilterKey { name: "pass"; value: 0 }
+ }
+ SortPolicy {
+ sortTypes: [SortPolicy.BackToFront]
+ RenderPassFilter {
+ matchAny: FilterKey { name: "pass"; value: 1 }
+ }
+ }
+ }
+ }
+
+ RenderStateSet {
+ renderStates: [
+ DepthTest {
+ depthFunction: DepthTest.Always
+ }
+ ]
+ LayerFilter {
+ layers: [layer]
+ filterMode: LayerFilter.AcceptAnyMatchingLayers
+ TechniqueFilter {
+ RenderPassFilter {
+ matchAny: FilterKey { name: "pass"; value: 0 }
+ }
+ SortPolicy {
+ sortTypes: [SortPolicy.BackToFront]
+ RenderPassFilter {
+ matchAny: FilterKey { name: "pass"; value: 1 }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ RenderStateSet {
+ renderStates: [
+ DepthTest {depthFunction: DepthTest.Always},
+ BlendEquation {},
+ BlendEquationArguments {
+ sourceRgb: BlendEquationArguments.One
+ sourceAlpha: BlendEquationArguments.One
+ destinationRgb: BlendEquationArguments.Zero
+ destinationAlpha: BlendEquationArguments.Zero
+ }
+ ]
+
+ RenderPassFilter {
+ matchAny : FilterKey { name : "pass"; value : "final" }
+ }
+ }
+ }
+}
diff --git a/tests/manual/qtbug-76766/Material1.qml b/tests/manual/qtbug-76766/Material1.qml
new file mode 100644
index 000000000..069d99c74
--- /dev/null
+++ b/tests/manual/qtbug-76766/Material1.qml
@@ -0,0 +1,93 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import Qt3D.Core 2.0
+import Qt3D.Render 2.0
+
+Material {
+ id: root
+
+ property color color: Qt.rgba(0.15, 0.35, 0.50, 1.0)
+
+ parameters: [
+ Parameter { name: "color"; value: Qt.vector3d(root.color.r, root.color.g, root.color.b) }
+ ]
+
+
+ effect: Effect {
+ property string vertex: "qrc:/shaders/shader.vert"
+ property string fragment: "qrc:/shaders/shader.frag"
+
+ FilterKey {
+ id: forward
+ name: "renderingStyle"
+ value: "forward"
+ }
+
+ ShaderProgram {
+ id: gl3Shader
+ vertexShaderCode: loadSource(parent.vertex)
+ fragmentShaderCode: loadSource(parent.fragment)
+ }
+
+ techniques: [
+ // OpenGL 3.1
+ Technique {
+ filterKeys: [ forward ]
+ graphicsApiFilter {
+ api: GraphicsApiFilter.OpenGL
+ profile: GraphicsApiFilter.CoreProfile
+ majorVersion: 3
+ minorVersion: 3
+ }
+ renderPasses: RenderPass {
+ filterKeys: [
+ FilterKey {
+ name: "pass"
+ value: 0
+ }
+ ]
+ shaderProgram: gl3Shader
+ }
+ }
+ ]
+ }
+}
+
+
diff --git a/tests/manual/qtbug-76766/Material2.qml b/tests/manual/qtbug-76766/Material2.qml
new file mode 100644
index 000000000..0af0ecb6c
--- /dev/null
+++ b/tests/manual/qtbug-76766/Material2.qml
@@ -0,0 +1,105 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import Qt3D.Core 2.0
+import Qt3D.Render 2.0
+
+Material {
+ id: root
+
+ property color color: Qt.rgba(0.15, 0.35, 0.50, 1.0)
+
+ parameters: [
+ Parameter { name: "color"; value: Qt.vector3d(root.color.r, root.color.g, root.color.b) }
+ ]
+
+
+ effect: Effect {
+ property string vertex: "qrc:/shaders/shader.vert"
+ property string fragment: "qrc:/shaders/shader.frag"
+
+ FilterKey {
+ id: forward
+ name: "renderingStyle"
+ value: "forward"
+ }
+
+ ShaderProgram {
+ id: gl3Shader
+ vertexShaderCode: loadSource(parent.vertex)
+ fragmentShaderCode: loadSource(parent.fragment)
+ }
+
+ techniques: [
+ // OpenGL 3.1
+ Technique {
+ filterKeys: [ forward ]
+ graphicsApiFilter {
+ api: GraphicsApiFilter.OpenGL
+ profile: GraphicsApiFilter.CoreProfile
+ majorVersion: 3
+ minorVersion: 3
+ }
+ renderPasses: [
+ RenderPass {
+ filterKeys: [
+ FilterKey {
+ name: "pass"
+ value: 1
+ }
+ ]
+ shaderProgram: gl3Shader
+ renderStates: [
+ NoDepthMask {},
+ BlendEquation {},
+ BlendEquationArguments {
+ sourceRgb: BlendEquationArguments.One
+ destinationRgb: BlendEquationArguments.One
+ sourceAlpha: BlendEquationArguments.Zero
+ destinationAlpha: BlendEquationArguments.One
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+}
+
+
diff --git a/tests/manual/qtbug-76766/PostProcess.qml b/tests/manual/qtbug-76766/PostProcess.qml
new file mode 100644
index 000000000..cd4c9c0c6
--- /dev/null
+++ b/tests/manual/qtbug-76766/PostProcess.qml
@@ -0,0 +1,117 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import Qt3D.Core 2.9
+import Qt3D.Render 2.9
+import Qt3D.Extras 2.0
+
+Entity {
+ id: root
+
+ property Texture colorTexture
+ readonly property int samples : colorTexture ? colorTexture.samples : 1
+
+ Effect {
+ id: materialEffect
+ techniques : [
+ // OpenGL 3.2
+ Technique {
+ graphicsApiFilter {
+ api : GraphicsApiFilter.OpenGL
+ profile : GraphicsApiFilter.CoreProfile
+ minorVersion : 3
+ majorVersion : 2
+ }
+
+ renderPasses : RenderPass {
+ filterKeys : FilterKey { name : "pass"; value : "final" }
+
+ shaderProgram : ShaderProgram {
+ vertexShaderCode:
+ "#version 150 core
+
+ in vec3 vertexPosition;
+ out vec2 texCoords;
+
+ void main() {
+ texCoords = vec2(0.5) + vec2(0.5) * vertexPosition.xz;
+ gl_Position = vec4(vertexPosition.x, vertexPosition.z, 0.0, 1.0);
+ }"
+ fragmentShaderCode:
+ "#version 150 core
+
+ uniform sampler2DMS source;
+
+ in vec2 texCoords;
+ out vec4 fragColor;
+
+ void main() {
+ vec4 c = vec4(0.0);
+ c += texelFetch(source, ivec2(gl_FragCoord), 0);
+ fragColor = vec4(c.rgb / max(c.a, 0.01), c.a);
+ }"
+ }
+ }
+ }
+ ]
+ }
+
+ Material {
+ id: materialWithoutTexture
+
+ parameters: [
+ Parameter { name: "source"; value: colorTexture },
+ Parameter { name: "samples"; value: root.samples }
+ ]
+
+ effect: materialEffect
+ }
+
+ PlaneMesh {
+ id: planeMesh
+ width: 2.0
+ height: 2.0
+ meshResolution: Qt.size(2, 2)
+ }
+
+ components : [
+ planeMesh,
+ materialWithoutTexture
+ ]
+}
diff --git a/tests/manual/qtbug-76766/SceneRoot.qml b/tests/manual/qtbug-76766/SceneRoot.qml
new file mode 100644
index 000000000..6e34633b1
--- /dev/null
+++ b/tests/manual/qtbug-76766/SceneRoot.qml
@@ -0,0 +1,176 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import Qt3D.Core 2.0
+import Qt3D.Render 2.0
+import Qt3D.Extras 2.0
+import Qt3D.Input 2.0
+import QtQuick 2.0 as QQ2
+import QtQuick 2.12
+
+
+Entity {
+ id: sceneRoot
+
+ Camera {
+ id: camera
+ projectionType: CameraLens.OrthographicProjection
+ left: -2
+ right: 2
+ top: 2
+ bottom: -2
+ nearPlane: 0.1
+ farPlane: 1000.0
+ position: Qt.vector3d(0.0, 0.0, 1.0)
+ upVector: Qt.vector3d(0.0, 1.0, 0.0)
+ viewCenter: Qt.vector3d(0.0, 0.0, 0.0)
+ }
+
+ RenderTarget {
+ id: renderTargetFBO
+ attachments : [
+ RenderTargetOutput {
+ attachmentPoint : RenderTargetOutput.Color0
+ texture : Texture2DMultisample {
+ id : colorAttachment
+ width : scene3d.width
+ height : scene3d.height
+ format : Texture.RGBA8_UNorm
+ generateMipMaps : false
+ samples: 1
+ }
+ },
+ RenderTargetOutput {
+ attachmentPoint : RenderTargetOutput.Depth
+ texture : Texture2DMultisample {
+ width : scene3d.width
+ height : scene3d.height
+ format : Texture.D32F
+ generateMipMaps : false
+ samples: 1
+ }
+ }
+ ]
+ }
+
+ components: [
+ RenderSettings {
+ activeFrameGraph: FrameGraph {
+ id: framegraph
+ camera: camera
+ fbo: renderTargetFBO
+ }
+ renderPolicy: RenderSettings.Always
+ }
+ ]
+
+ Entity {
+ id: firstPassEntities
+
+ Entity {
+ components: [
+ Transform {
+ translation: Qt.vector3d(0,0,-2)
+ },
+ Material1 {
+ color: "red"
+ },
+ SphereMesh {
+ }
+ ]
+ }
+
+
+ Entity {
+ components: [
+ Transform {
+ translation: Qt.vector3d(1,0,-1)
+ },
+ Material2 {
+ color: "green"
+ },
+ SphereMesh {
+ }
+ ]
+ }
+ }
+
+ Entity {
+ id: secondPassEntities
+ components: [framegraph.layer]
+
+ Entity {
+ components: [
+ Transform {
+ translation: Qt.vector3d(0,0,-4)
+ scale: 0.5
+ },
+ Material1 {
+ ColorAnimation on color {
+ from: "black"
+ to: "purple"
+ duration: 2000
+ loops: Animation.Infinite
+ }
+ },
+ SphereMesh {
+ }
+ ]
+ }
+
+
+ Entity {
+ components: [
+ Transform {
+ translation: Qt.vector3d(1,0,-3)
+ scale: 0.5
+ },
+ Material2 {
+ color: "orange"
+ },
+ SphereMesh {
+ }
+ ]
+ }
+ }
+
+ PostProcess {
+ colorTexture: colorAttachment
+ }
+}
diff --git a/tests/manual/qtbug-76766/expected_output.png b/tests/manual/qtbug-76766/expected_output.png
new file mode 100644
index 000000000..8f642e843
--- /dev/null
+++ b/tests/manual/qtbug-76766/expected_output.png
Binary files differ
diff --git a/tests/manual/qtbug-76766/main.cpp b/tests/manual/qtbug-76766/main.cpp
new file mode 100644
index 000000000..f9d49846a
--- /dev/null
+++ b/tests/manual/qtbug-76766/main.cpp
@@ -0,0 +1,74 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QGuiApplication>
+#include <QQuickView>
+#include <QOpenGLContext>
+
+void setSurfaceFormat()
+{
+ QSurfaceFormat format;
+#ifdef QT_OPENGL_ES_2
+ format.setRenderableType(QSurfaceFormat::OpenGLES);
+#else
+ if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGL) {
+ format.setVersion(4, 3);
+ format.setProfile(QSurfaceFormat::CoreProfile);
+ }
+#endif
+ format.setDepthBufferSize(24);
+ format.setSamples(4);
+ format.setStencilBufferSize(8);
+ QSurfaceFormat::setDefaultFormat(format);
+}
+
+int main(int argc, char **argv)
+{
+ QGuiApplication app(argc, argv);
+ setSurfaceFormat();
+
+ QQuickView view;
+
+ view.resize(1920, 1080);
+ view.setResizeMode(QQuickView::SizeRootObjectToView);
+ view.setSource(QUrl("qrc:/main.qml"));
+ view.show();
+
+ return app.exec();
+}
diff --git a/tests/manual/qtbug-76766/main.qml b/tests/manual/qtbug-76766/main.qml
new file mode 100644
index 000000000..9b8f4b691
--- /dev/null
+++ b/tests/manual/qtbug-76766/main.qml
@@ -0,0 +1,76 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.12
+import QtQuick.Scene3D 2.12
+import Qt3D.Render 2.12
+
+Item {
+ anchors.fill: parent
+
+ Scene3D {
+ id: scene3d
+ anchors.fill: parent
+ focus: true
+ cameraAspectRatioMode: Scene3D.AutomaticAspectRatio
+
+ SceneRoot {
+ id: root
+ }
+ }
+
+ Image {
+ width: 400
+ fillMode: Image.PreserveAspectFit
+ source: "qrc:/expected_output.png"
+ Text {
+ anchors.centerIn: parent
+ color: "white"
+ text: "This is the expected output"
+ }
+ Rectangle {
+ anchors.fill: parent
+ color: "transparent"
+ border {
+ color: "white"
+ width: 2
+ }
+ }
+ }
+}
diff --git a/tests/manual/qtbug-76766/qml.qrc b/tests/manual/qtbug-76766/qml.qrc
new file mode 100644
index 000000000..b1f8c2c21
--- /dev/null
+++ b/tests/manual/qtbug-76766/qml.qrc
@@ -0,0 +1,11 @@
+<RCC>
+ <qresource prefix="/">
+ <file>main.qml</file>
+ <file>SceneRoot.qml</file>
+ <file>FrameGraph.qml</file>
+ <file>PostProcess.qml</file>
+ <file>Material1.qml</file>
+ <file>Material2.qml</file>
+ <file>expected_output.png</file>
+ </qresource>
+</RCC>
diff --git a/tests/manual/qtbug-76766/qtbug-76766.pro b/tests/manual/qtbug-76766/qtbug-76766.pro
new file mode 100644
index 000000000..f332d7d57
--- /dev/null
+++ b/tests/manual/qtbug-76766/qtbug-76766.pro
@@ -0,0 +1,10 @@
+TEMPLATE = app
+
+QT += qml quick
+CONFIG += c++11
+
+SOURCES += main.cpp
+
+RESOURCES += qml.qrc \
+ shaders.qrc
+
diff --git a/tests/manual/qtbug-76766/shaders.qrc b/tests/manual/qtbug-76766/shaders.qrc
new file mode 100644
index 000000000..665814e55
--- /dev/null
+++ b/tests/manual/qtbug-76766/shaders.qrc
@@ -0,0 +1,6 @@
+<RCC>
+ <qresource prefix="/">
+ <file>shaders/shader.frag</file>
+ <file>shaders/shader.vert</file>
+ </qresource>
+</RCC>
diff --git a/tests/manual/qtbug-76766/shaders/shader.frag b/tests/manual/qtbug-76766/shaders/shader.frag
new file mode 100644
index 000000000..92d304640
--- /dev/null
+++ b/tests/manual/qtbug-76766/shaders/shader.frag
@@ -0,0 +1,11 @@
+#version 150 core
+
+uniform vec3 color;
+
+out vec4 fragColor;
+
+void main()
+{
+ fragColor = vec4(color,1.0);
+}
+
diff --git a/tests/manual/qtbug-76766/shaders/shader.vert b/tests/manual/qtbug-76766/shaders/shader.vert
new file mode 100644
index 000000000..fa836855d
--- /dev/null
+++ b/tests/manual/qtbug-76766/shaders/shader.vert
@@ -0,0 +1,36 @@
+#version 150 core
+
+in vec3 vertexPosition;
+in vec3 vertexNormal;
+in vec2 vertexTexCoord;
+in vec4 vertexTangent;
+
+out vec3 worldPosition;
+out vec3 worldNormal;
+out vec4 worldTangent;
+out vec2 texCoord;
+
+uniform mat4 modelMatrix;
+uniform mat3 modelNormalMatrix;
+uniform mat4 mvp;
+
+void main()
+{
+ // Scale texture coordinates for for fragment shader
+ texCoord = vertexTexCoord;
+
+ // Transform position, normal, and tangent to world coords
+ worldPosition = vec3(modelMatrix * vec4(vertexPosition, 1.0));
+ worldNormal = normalize(modelNormalMatrix * vertexNormal);
+ worldTangent.xyz = normalize(vec3(modelMatrix * vec4(vertexTangent.xyz, 0.0)));
+ worldTangent.w = vertexTangent.w;
+
+ // Calculate animated vertex positions
+
+ float sinPos = (vertexPosition.z)+(vertexPosition.x);
+ float sinPos2 = (vertexPosition.y/2)+(vertexPosition.z);
+ vec3 vertMod = vec3(vertexPosition.x,vertexPosition.y,vertexPosition.z);
+
+ // Calculate vertex position in clip coordinates
+ gl_Position = mvp * vec4(vertexPosition, 1.0);
+}