Merge "Merge remote-tracking branch 'origin/5.13' into 5.14"

This commit is contained in:
Paul Lemire 2019-10-15 09:40:09 +02:00
commit 0ff2215bb0
27 changed files with 385 additions and 59 deletions

View File

@ -125,6 +125,11 @@ QAbstractClipAnimator::QAbstractClipAnimator(QAbstractClipAnimatorPrivate &dd, Q
QAbstractClipAnimator::~QAbstractClipAnimator()
{
}
/*!
\qmlproperty bool Qt3DAnimation::AbstractClipAnimator::running
This property holds a boolean indicating whether the animation is currently running.
*/
/*!
\property Qt3DAnimation::QAbstractClipAnimator::running
@ -141,6 +146,13 @@ bool QAbstractClipAnimator::isRunning() const
return d->m_running;
}
/*!
\qmlproperty ChannelMapper Qt3DAnimation::AbstractClipAnimator::channelMapper
This property holds the ChannelMapper that controls how the channels in
the animation clip map onto the properties of the target objects.
*/
/*!
\property Qt3DAnimation::QAbstractClipAnimator::channelMapper
@ -198,6 +210,12 @@ int QAbstractClipAnimator::loopCount() const
Q_D(const QAbstractClipAnimator);
return d->m_loops;
}
/*!
\qmlproperty Clock Qt3DAnimation::AbstractClipAnimator::clock
The clock controls the speed with which an animation is played.
*/
/*!
\property Qt3DAnimation::QAbstractClipAnimator::clock
@ -208,7 +226,11 @@ QClock *QAbstractClipAnimator::clock() const
Q_D(const QAbstractClipAnimator);
return d->m_clock;
}
/*!
\qmlproperty real Qt3DAnimation::AbstractClipAnimator::normalizedTime
This property holds the clips normalized time.
*/
/*!
\property Qt3DAnimation::QAbstractClipAnimator::normalizedTime

View File

@ -56,6 +56,7 @@ QBlendedClipAnimatorPrivate::QBlendedClipAnimatorPrivate()
\qmltype BlendedClipAnimator
\instantiates Qt3DAnimation::QBlendedClipAnimator
\inqmlmodule Qt3D.Animation
\inherits AbstractClipAnimator
\since 5.9
\brief BlendedClipAnimator is a component providing animation playback capabilities of a tree

View File

@ -66,6 +66,7 @@ bool QClipAnimatorPrivate::canPlay() const
\qmltype ClipAnimator
\instantiates Qt3DAnimation::QClipAnimator
\inqmlmodule Qt3D.Animation
\inherits AbstractClipAnimator
\since 5.9
\brief ClipAnimator is a component providing simple animation playback capabilities.

View File

@ -2,7 +2,7 @@ TEMPLATE = subdirs
# QNX is not supported, and Linux GCC 4.9 on ARM chokes on the assimp
# sources (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66964).
QT_FOR_CONFIG += 3dcore-private
qtConfig(assimp):if(qtConfig(system-assimp)|android-clang|gcc:greaterThan(QT_GCC_MAJOR_VERSION, 4)): {
!ios:!tvos:!qcc:qtConfig(assimp):if(qtConfig(system-assimp)|android-clang|clang|win32-msvc)|if(gcc:greaterThan(QT_GCC_MAJOR_VERSION, 4)) {
SUBDIRS += assimp
}
SUBDIRS += gltf

View File

@ -529,6 +529,7 @@ void Scene3DItem::setWindowSurface(QObject *rootObject)
m_dummySurface = new QOffscreenSurface;
m_dummySurface->setParent(qGuiApp); // parent to something suitably long-living
m_dummySurface->setFormat(rw->format());
m_dummySurface->setScreen(rw->screen());
m_dummySurface->create();
surfaceSelector->setSurface(m_dummySurface);
} else {
@ -616,6 +617,9 @@ QSGNode *Scene3DItem::updatePaintNode(QSGNode *node, QQuickItem::UpdatePaintNode
// If the render aspect wasn't created yet, do so now
if (m_renderAspect == nullptr) {
m_renderAspect = new QRenderAspect(QRenderAspect::Synchronous);
auto *rw = QQuickRenderControl::renderWindowFor(window());
static_cast<Qt3DRender::QRenderAspectPrivate *>(Qt3DRender::QRenderAspectPrivate::get(m_renderAspect))->m_screen =
(rw ? rw->screen() : window()->screen());
m_aspectEngine->registerAspect(m_renderAspect);
// Before Synchronizing is in the SG Thread, we want beforeSync to be triggered

View File

@ -66,6 +66,7 @@
#include <Qt3DQuickExtras/private/qt3dquickwindowlogging_p.h>
#include <Qt3DRender/private/qrendersurfaceselector_p.h>
#include <Qt3DRender/private/qrenderaspect_p.h>
QT_BEGIN_NAMESPACE
@ -132,6 +133,8 @@ Qt3DQuickWindow::Qt3DQuickWindow(QWindow *parent)
QSurfaceFormat::setDefaultFormat(format);
d->m_renderAspect = new Qt3DRender::QRenderAspect;
if (parent && parent->screen())
static_cast<Qt3DRender::QRenderAspectPrivate*>(Qt3DRender::QRenderAspectPrivate::get(d->m_renderAspect))->m_screen = parent->screen();
d->m_inputAspect = new Qt3DInput::QInputAspect;
d->m_logicAspect = new Qt3DLogic::QLogicAspect;
d->m_engine = new Qt3DCore::Quick::QQmlAspectEngine;

View File

@ -68,6 +68,8 @@ namespace Quick {
with the item; if an entity has a QObjectPicker component, the pick events from that picker
are sent to the QScene2D and converted to mouse events and finally sent to the item.
\note Only mouse events are supported. The item does not support keyboard input.
\since 5.9
*/
@ -89,6 +91,8 @@ namespace Quick {
with the item; if an entity has an ObjectPicker component, the pick events from that picker
are sent to the Scene2D and converted to mouse events and finally sent to the item.
\note Only mouse events are supported. The item does not support keyboard input.
Usage:
\qml
Entity {

View File

@ -64,6 +64,7 @@ QT_BEGIN_NAMESPACE
class QSurface;
class QSize;
class QScreen;
namespace Qt3DCore {
class QAbstractFrameAdvanceService;
@ -175,6 +176,8 @@ public:
// For QtQuick rendering
virtual void setOpenGLContext(QOpenGLContext *ctx) = 0;
virtual void setScreen(QScreen *) {}
virtual QScreen *screen() const { return nullptr; }
virtual void setOffscreenSurfaceHelper(OffscreenSurfaceHelper *helper) = 0;
virtual QSurfaceFormat format() = 0;

View File

@ -72,6 +72,7 @@ void OffscreenSurfaceHelper::createOffscreenSurface()
m_offscreenSurface = new QOffscreenSurface;
m_offscreenSurface->setParent(this);
m_offscreenSurface->setFormat(m_renderer->format());
m_offscreenSurface->setScreen(m_renderer->screen());
m_offscreenSurface->create();
}

View File

@ -107,10 +107,6 @@ bool PlatformSurfaceFilter::eventFilter(QObject *obj, QEvent *e)
// If we remove it, the call to isSurfaceValid will
// implicitely return false
PlatformSurfaceFilter::m_surfacesValidity.remove(m_surface);
if (m_obj) {
m_obj->removeEventFilter(this);
m_obj = nullptr;
}
break;
}

View File

@ -66,7 +66,7 @@ namespace Qt3DRender {
/*!
\qmltype ComputeCommand
\since 5.7
\inmodule Qt3DRender
\inqmlmodule Qt3D.Render
\inherits Component3D
\instantiates Qt3DRender::QComputeCommand
\brief Component to issue work for the compute shader on GPU.

View File

@ -562,6 +562,7 @@ void QRenderAspect::onRegistered()
// TO DO: Load proper Renderer class based on Qt configuration preferences
d->m_renderer = new Render::Renderer(d->m_renderType);
d->m_renderer->setScreen(d->m_screen);
d->m_renderer->setNodeManagers(d->m_nodeManagers);
// Create a helper for deferring creation of an offscreen surface used during cleanup

View File

@ -60,6 +60,7 @@
QT_BEGIN_NAMESPACE
class QSurface;
class QScreen;
namespace Qt3DRender {
@ -106,6 +107,7 @@ public:
QVector<Render::QRenderPlugin *> m_renderPlugins;
QRenderAspect::RenderType m_renderType;
Render::OffscreenSurfaceHelper *m_offscreenHelper;
QScreen *m_screen = nullptr;
static QMutex m_pluginLock;
static QVector<QString> m_pluginConfig;

View File

@ -44,6 +44,7 @@
#include <QPair>
#include <math.h>
#include <algorithm>
QT_BEGIN_NAMESPACE
@ -55,6 +56,9 @@ namespace {
// returns true and intersection point q; false otherwise
bool intersectRaySphere(const Qt3DRender::RayCasting::QRay3D &ray, const Qt3DRender::Render::Sphere &s, Vector3D *q = nullptr)
{
if (s.isNull())
return false;
const Vector3D p = ray.origin();
const Vector3D d = ray.direction();
const Vector3D m = p - s.center();
@ -139,11 +143,31 @@ inline void sphereFromExtremePoints(Qt3DRender::Render::Sphere &s, const QVector
inline void constructRitterSphere(Qt3DRender::Render::Sphere &s, const QVector<Vector3D> &points)
{
// Calculate the sphere encompassing two axially extreme points
sphereFromExtremePoints(s, points);
//def bounding_sphere(points):
// dist = lambda a,b: ((a[0] - b[0])**2 + (a[1] - b[1])**2 + (a[2] - b[2])**2)**0.5
// x = points[0]
// y = max(points,key= lambda p: dist(p,x) )
// z = max(points,key= lambda p: dist(p,y) )
// bounding_sphere = (((y[0]+z[0])/2,(y[1]+z[1])/2,(y[2]+z[2])/2), dist(y,z)/2)
//
// exterior_points = [p for p in points if dist(p,bounding_sphere[0]) > bounding_sphere[1] ]
// while ( len(exterior_points) > 0 ):
// pt = exterior_points.pop()
// if (dist(pt, bounding_sphere[0]) > bounding_sphere[1]):
// bounding_sphere = (bounding_sphere[0],dist(pt,bounding_sphere[0]))
//
// return bounding_sphere
// Now make sure the sphere bounds all points by growing if needed
s.expandToContain(points);
const Vector3D x = points[0];
const Vector3D y = *std::max_element(points.begin(), points.end(), [&x](const Vector3D& lhs, const Vector3D& rhs){ return (lhs - x).lengthSquared() < (rhs - x).lengthSquared(); });
const Vector3D z = *std::max_element(points.begin(), points.end(), [&y](const Vector3D& lhs, const Vector3D& rhs){ return (lhs - y).lengthSquared() < (rhs - y).lengthSquared(); });
const Vector3D center = (y + z) * 0.5f;
const Vector3D maxDistPt = *std::max_element(points.begin(), points.end(), [&center](const Vector3D& lhs, const Vector3D& rhs){ return (lhs - center).lengthSquared() < (rhs - center).lengthSquared(); });
const float radius = (maxDistPt - center).length();
s.setCenter(center);
s.setRadius(radius);
}
} // anonymous namespace
@ -169,6 +193,12 @@ void Sphere::initializeFromPoints(const QVector<Vector3D> &points)
void Sphere::expandToContain(const Vector3D &p)
{
if (isNull()) {
m_center = p;
m_radius = 0.0f;
return;
}
const Vector3D d = p - m_center;
const float dist2 = d.lengthSquared();
@ -184,6 +214,13 @@ void Sphere::expandToContain(const Vector3D &p)
void Sphere::expandToContain(const Sphere &sphere)
{
if (isNull()) {
*this = sphere;
return;
} else if (sphere.isNull()) {
return;
}
const Vector3D d(sphere.m_center - m_center);
const float dist2 = d.lengthSquared();
@ -206,6 +243,9 @@ void Sphere::expandToContain(const Sphere &sphere)
Sphere Sphere::transformed(const Matrix4x4 &mat) const
{
if (isNull())
return *this;
// Transform extremities in x, y, and z directions to find extremities
// of the resulting ellipsoid
Vector3D x = mat.map(m_center + Vector3D(m_radius, 0.0f, 0.0f));

View File

@ -69,7 +69,7 @@ class Q_3DRENDERSHARED_PRIVATE_EXPORT Sphere : public RayCasting::BoundingSphere
public:
inline Sphere(Qt3DCore::QNodeId i = Qt3DCore::QNodeId())
: m_center()
, m_radius(0.0f)
, m_radius(-1.0f)
, m_id(i)
{}
@ -82,7 +82,7 @@ public:
void setCenter(const Vector3D &c);
Vector3D center() const override;
inline bool isNull() { return m_center == Vector3D() && m_radius == 0.0f; }
bool isNull() const { return m_center == Vector3D() && m_radius == -1.0f; }
void setRadius(float r);
float radius() const override;
@ -131,7 +131,9 @@ inline Vector3D Sphere::center() const
inline void Sphere::setRadius(float r)
{
m_radius = r;
Q_ASSERT(r >= 0.0f);
if (r >= 0.0f)
m_radius = r;
}
inline float Sphere::radius() const
@ -142,11 +144,14 @@ inline float Sphere::radius() const
inline void Sphere::clear()
{
m_center = Vector3D();
m_radius = 0.0f;
m_radius = -1.0f;
}
inline bool intersects(const Sphere &a, const Sphere &b)
{
if (a.isNull() || b.isNull())
return false;
// Calculate squared distance between sphere centers
const Vector3D d = a.center() - b.center();
const float distSq = Vector3D::dotProduct(d, d);

View File

@ -90,30 +90,42 @@ public:
m_min = QVector3D(findExtremePoints.xMin, findExtremePoints.yMin, findExtremePoints.zMin);
m_max = QVector3D(findExtremePoints.xMax, findExtremePoints.yMax, findExtremePoints.zMax);
// Calculate squared distance for the pairs of points
const float xDist2 = (findExtremePoints.xMaxPt - findExtremePoints.xMinPt).lengthSquared();
const float yDist2 = (findExtremePoints.yMaxPt - findExtremePoints.yMinPt).lengthSquared();
const float zDist2 = (findExtremePoints.zMaxPt - findExtremePoints.zMinPt).lengthSquared();
// Select most distant pair
Vector3D p = findExtremePoints.xMinPt;
Vector3D q = findExtremePoints.xMaxPt;
if (yDist2 > xDist2 && yDist2 > zDist2) {
p = findExtremePoints.yMinPt;
q = findExtremePoints.yMaxPt;
FindMaxDistantPoint maxDistantPointY(m_manager);
maxDistantPointY.setReferencePoint = true;
if (!maxDistantPointY.apply(positionAttribute, indexAttribute, drawVertexCount,
primitiveRestartEnabled, primitiveRestartIndex)) {
return false;
}
if (zDist2 > xDist2 && zDist2 > yDist2) {
p = findExtremePoints.zMinPt;
q = findExtremePoints.zMaxPt;
if (maxDistantPointY.hasNoPoints)
return false;
//const Vector3D x = maxDistantPointY.referencePt;
const Vector3D y = maxDistantPointY.maxDistPt;
FindMaxDistantPoint maxDistantPointZ(m_manager);
maxDistantPointZ.setReferencePoint = false;
maxDistantPointZ.referencePt = y;
if (!maxDistantPointZ.apply(positionAttribute, indexAttribute, drawVertexCount,
primitiveRestartEnabled, primitiveRestartIndex)) {
return false;
}
const Vector3D z = maxDistantPointZ.maxDistPt;
const Vector3D center = (y + z) * 0.5f;
FindMaxDistantPoint maxDistantPointCenter(m_manager);
maxDistantPointCenter.setReferencePoint = false;
maxDistantPointCenter.referencePt = center;
if (!maxDistantPointCenter.apply(positionAttribute, indexAttribute, drawVertexCount,
primitiveRestartEnabled, primitiveRestartIndex)) {
return false;
}
const Vector3D c = 0.5f * (p + q);
m_volume.setCenter(c);
m_volume.setRadius((q - c).length());
const float radius = (center - maxDistantPointCenter.maxDistPt).length();
ExpandSphere expandSphere(m_manager, m_volume);
if (!expandSphere.apply(positionAttribute, indexAttribute, drawVertexCount,
primitiveRestartEnabled, primitiveRestartIndex))
m_volume = Qt3DRender::Render::Sphere(center, radius);
if (m_volume.isNull())
return false;
return true;
@ -172,18 +184,34 @@ private:
}
};
class ExpandSphere : public Buffer3fVisitor
class FindMaxDistantPoint : public Buffer3fVisitor
{
public:
ExpandSphere(NodeManagers *manager, Sphere& volume)
: Buffer3fVisitor(manager), m_volume(volume)
FindMaxDistantPoint(NodeManagers *manager)
: Buffer3fVisitor(manager)
{ }
Sphere& m_volume;
float maxLengthSquared = 0.0f;
Vector3D maxDistPt;
Vector3D referencePt;
bool setReferencePoint = false;
bool hasNoPoints = true;
void visit(uint ndx, float x, float y, float z) override
{
Q_UNUSED(ndx);
m_volume.expandToContain(Vector3D(x, y, z));
const Vector3D p = Vector3D(x, y, z);
if (hasNoPoints && setReferencePoint) {
maxLengthSquared = 0.0f;
referencePt = p;
}
const float lengthSquared = (p - referencePt).lengthSquared();
if ( lengthSquared >= maxLengthSquared ) {
maxDistPt = p;
maxLengthSquared = lengthSquared;
}
hasNoPoints = false;
}
};
};

View File

@ -202,12 +202,12 @@ void PickBoundingVolumeJob::setRoot(Entity *root)
void PickBoundingVolumeJob::setMouseEvents(const QList<QPair<QObject*, QMouseEvent>> &pendingEvents)
{
m_pendingMouseEvents = pendingEvents;
m_pendingMouseEvents.append(pendingEvents);
}
void PickBoundingVolumeJob::setKeyEvents(const QList<QKeyEvent> &pendingEvents)
{
m_pendingKeyEvents = pendingEvents;
m_pendingKeyEvents.append(pendingEvents);
}
void PickBoundingVolumeJob::markPickersDirty()

View File

@ -65,6 +65,8 @@ QEffectPrivate::QEffectPrivate()
An QEffect instance should be shared among several QMaterial instances when possible.
\note QEffect node can not be disabled.
\code
QEffect *effect = new QEffect();
@ -111,6 +113,8 @@ QEffectPrivate::QEffectPrivate()
A Parameter defined on an Effect is overridden by a QParameter (of the same
name) defined in a Material, TechniqueFilter, RenderPassFilter.
\note Effect node can not be disabled.
\code
Effect {
id: effect

View File

@ -60,6 +60,8 @@ QFilterKeyPrivate::QFilterKeyPrivate()
Filter keys are used by QTechnique and QRenderPass to specify at which stage of rendering the
technique or the render pass is used.
\note QFilterKey node can not be disabled.
*/
/*!
@ -73,6 +75,8 @@ QFilterKeyPrivate::QFilterKeyPrivate()
A FilterKey is a storage type for filter key and value pair.
Filter keys are used by Technique and RenderPass to specify at which stage of rendering the
technique or the render pass is used.
\note FilterKey node can not be disabled.
*/
QFilterKey::QFilterKey(QNode *parent)

View File

@ -76,6 +76,8 @@
\note when the targeted uniform is an array, the name should be the name
of the uniform with [0] appended to it.
\note Parameter node can not be disabled.
\code
Parameter {
name: "diffuseValues[0]"
@ -138,6 +140,8 @@
\note when the targeted uniform is an array, the name should be the name
of the uniform with [0] appended to it.
\note QParameter node can not be disabled.
\code
QParameter *param = new QParameter();
QVariantList values = QVariantList() << 0.0f << 1.0f << 2.0f << 3.0f << 4.0f << 883.0f << 1340.0f << 1584.0f;

View File

@ -87,6 +87,8 @@ QTechniquePrivate::~QTechniquePrivate()
QSurfaceFormat::setDefaultFormat(). Setting the QSurfaceFormat on the view
will likely have no effect on Qt3D related rendering.
\note Technique node can not be disabled.
\qml
Technique {
id: gl3Technique
@ -152,6 +154,8 @@ QTechniquePrivate::~QTechniquePrivate()
QSurfaceFormat::setDefaultFormat(). Setting the QSurfaceFormat on the view
will likely have no effect on Qt3D related rendering.
\note QTechnique node can not be disabled.
\code
QTechnique *gl3Technique = new QTechnique();

View File

@ -82,6 +82,9 @@ namespace Qt3DRender {
\note Instances of this component shouldn't be shared, not respecting that
condition will most likely result in undefined behavior.
\note The camera far plane value affects picking and produces incorrect results due to
floating-point precision if it is greater than ~100 000.
\since 5.6
*/
@ -115,6 +118,9 @@ namespace Qt3DRender {
\note Instances of this component shouldn't be shared, not respecting that
condition will most likely result in undefined behavior.
\note The camera far plane value affects picking and produces incorrect results due to
floating-point precision if it is greater than ~100 000.
*/
/*!

View File

@ -347,6 +347,16 @@ void Renderer::setOpenGLContext(QOpenGLContext *context)
m_glContext = context;
}
void Renderer::setScreen(QScreen *scr)
{
m_screen = scr;
}
QScreen *Renderer::screen() const
{
return m_screen;
}
// Called in RenderThread context by the run method of RenderThread
// RenderThread has locked the mutex already and unlocks it when this
// method termintates
@ -364,6 +374,8 @@ void Renderer::initialize()
// we need to create it
if (!m_glContext) {
ctx = new QOpenGLContext;
if (m_screen)
ctx->setScreen(m_screen);
ctx->setShareContext(qt_gl_global_share_context());
// TO DO: Shouldn't we use the highest context available and trust
@ -393,6 +405,8 @@ void Renderer::initialize()
if (!ctx->shareContext()) {
m_shareContext = new QOpenGLContext;
if (ctx->screen())
m_shareContext->setScreen(ctx->screen());
m_shareContext->setFormat(ctx->format());
m_shareContext->setShareContext(ctx);
m_shareContext->create();

View File

@ -106,6 +106,7 @@ QT_BEGIN_NAMESPACE
class QSurface;
class QMouseEvent;
class QScreen;
namespace Qt3DCore {
class QEntity;
@ -293,6 +294,8 @@ public:
ViewSubmissionResultData submitRenderViews(const QVector<Render::RenderView *> &renderViews);
RendererCache *cache() { return &m_cache; }
void setScreen(QScreen *scr) override;
QScreen *screen() const override;
#ifdef QT3D_RENDER_UNIT_TESTS
public:
@ -426,6 +429,7 @@ private:
bool m_shouldSwapBuffers;
QVector<FrameGraphNode *> m_frameGraphLeaves;
QScreen *m_screen = nullptr;
};
} // namespace Render

View File

@ -59,6 +59,7 @@ namespace Qt3DRender {
/*!
\qmltype RenderState
\inqmlmodule Qt3D.Render
\brief An abstract base type for all render states.
\since 5.7
\inherits Node

View File

@ -204,13 +204,150 @@ class tst_BoundingSphere : public Qt3DCore::QBackendNodeTester
private:
private Q_SLOTS:
void checkIsNull() {
auto defaultSphere = Qt3DRender::Render::Sphere();
QVERIFY(defaultSphere.isNull());
}
void remainsNotNullAfterTransform() {
QMatrix4x4 mat;
mat.translate(-5,-5,-5);
auto mMat = Matrix4x4(mat);
auto pointSphere = Qt3DRender::Render::Sphere(Vector3D(5.f,5.f,5.f),0.f);
pointSphere.transform(mMat);
QVERIFY(!pointSphere.isNull());
QVERIFY(pointSphere.center() == Vector3D(0.,0.,0));
QVERIFY(pointSphere.radius() == 0.f);
}
void remainsNullAfterTransform() {
QMatrix4x4 mat;
mat.translate(-5,-5,-5);
auto mMat = Matrix4x4(mat);
auto defaultSphere = Qt3DRender::Render::Sphere();
defaultSphere.transform(mMat);
QVERIFY(defaultSphere.isNull());
}
void expandToContainSphere() {
auto firstValidSphere = Qt3DRender::Render::Sphere(Vector3D(-10.f,-10.f,-10.f),1.f);
auto secondValidSphere = Qt3DRender::Render::Sphere(Vector3D(10.f,10.f,10.f),1.f);
firstValidSphere.expandToContain(secondValidSphere);
QVERIFY(firstValidSphere.center()==Vector3D(0.f,0.f,0.f));
float dist = static_cast<float>((2 + sqrt(3.*(20)*(20)))/2.);
QVERIFY(qFuzzyCompare(firstValidSphere.radius(), dist));
}
void expandToContainSphereOneInvalid() {
auto firstValidSphere = Qt3DRender::Render::Sphere(Vector3D(-10.f,-10.f,-10.f),1.f);
auto defaultSphere = Qt3DRender::Render::Sphere();
auto copiedSphere = firstValidSphere;
firstValidSphere.expandToContain(defaultSphere);
QVERIFY(firstValidSphere.center() == copiedSphere.center());
QVERIFY(firstValidSphere.radius() == copiedSphere.radius());
QVERIFY(!firstValidSphere.isNull());
}
void expandToContainOtherSphereInvalid() {
auto firstValidSphere = Qt3DRender::Render::Sphere(Vector3D(-10.f,-10.f,-10.f),1.f);
auto defaultSphere = Qt3DRender::Render::Sphere();
defaultSphere.expandToContain(firstValidSphere);
QVERIFY(defaultSphere.center() == firstValidSphere.center());
QVERIFY(defaultSphere.radius() == firstValidSphere.radius());
QVERIFY(!defaultSphere.isNull());
}
void expandNullSphereWithNullSphere() {
auto defaultSphere = Qt3DRender::Render::Sphere();
auto otherDefaultSphere = Qt3DRender::Render::Sphere();
defaultSphere.expandToContain(otherDefaultSphere);
QVERIFY(defaultSphere.isNull());
}
void expandToContainPoint() {
auto firstValidSphere = Qt3DRender::Render::Sphere(Vector3D(-10.f,-10.f,-10.f),1.f);
firstValidSphere.expandToContain(Vector3D(0,0,0));
QVERIFY(!firstValidSphere.isNull());
float expectedRadius = static_cast<float>((1 + qSqrt(3.*(10)*(10)))/2.);
QVERIFY(qFuzzyCompare(firstValidSphere.radius(), expectedRadius));
}
void nullSphereExpandToContainPoint() {
auto defaultSphere = Qt3DRender::Render::Sphere();
defaultSphere.expandToContain(Vector3D(5,5,5));
QVERIFY(!defaultSphere.isNull());
QVERIFY(defaultSphere.center() == Vector3D(5,5,5));
QVERIFY(qFuzzyIsNull(defaultSphere.radius()));
}
void nullSphereExpandToOrigin() {
auto defaultSphere = Qt3DRender::Render::Sphere();
defaultSphere.expandToContain(Vector3D(0,0,0));
QVERIFY(!defaultSphere.isNull());
QVERIFY(defaultSphere.center() == Vector3D(0,0,0));
QVERIFY(qFuzzyIsNull(defaultSphere.radius()));
}
void ritterSphereCubePoints() {
QVector<Vector3D> cubePts={
Vector3D(-0.5, -0.5, 0.5),
Vector3D( 0.5, -0.5, -0.5),
Vector3D(-0.5, 0.5, -0.5),
Vector3D( 0.5, 0.5, -0.5),
Vector3D(-0.5, -0.5, -0.5),
Vector3D( 0.5, -0.5, 0.5),
Vector3D(-0.5, 0.5, 0.5),
Vector3D( 0.5, 0.5, 0.5)
};
auto ritterSphere=Qt3DRender::Render::Sphere::fromPoints(cubePts);
QVERIFY(!ritterSphere.isNull());
QVERIFY(qFuzzyIsNull(ritterSphere.center().x()));
QVERIFY(qFuzzyIsNull(ritterSphere.center().y()));
QVERIFY(qFuzzyIsNull(ritterSphere.center().z()));
QVERIFY(qFuzzyCompare(ritterSphere.radius(), static_cast<float>(qSqrt(3)/2)));
}
void ritterSphereRandomPoints() {
QVector<Vector3D> randomPts={
Vector3D(-81, 55, 46),
Vector3D(-91, -73, -42),
Vector3D(-50, -76, -77),
Vector3D(-40, 63, 58),
Vector3D(-28, -2, -57),
Vector3D(84, 17, 33),
Vector3D(53, 11, -49),
Vector3D(-7, -24, -86),
Vector3D(-89, 6, 76),
Vector3D(46, -18, -27)
};
auto ritterSphere = Qt3DRender::Render::Sphere::fromPoints(randomPts);
QVERIFY(!ritterSphere.isNull());
QVERIFY(qFuzzyCompare(ritterSphere.center().x(), 17.f));
QVERIFY(qFuzzyCompare(ritterSphere.center().y(), -29.5f));
QVERIFY(qFuzzyCompare(ritterSphere.center().z(), -22.0f));
QVERIFY(qFuzzyCompare(ritterSphere.radius(), 148.66152831179963f));
}
void ritterSphereOnePoint() {
QVector<Vector3D> singlePt={
Vector3D(-0.5, -0.5, -0.5),
};
auto ritterSphere = Qt3DRender::Render::Sphere::fromPoints(singlePt);
QVERIFY(!ritterSphere.isNull());
QVERIFY(qFuzzyCompare(ritterSphere.center().x(), -0.5f));
QVERIFY(qFuzzyCompare(ritterSphere.center().y(), -0.5f));
QVERIFY(qFuzzyCompare(ritterSphere.center().z(), -0.5f));
QVERIFY(qFuzzyIsNull(ritterSphere.radius()));
}
void checkExtraGeometries_data()
{
QTest::addColumn<QString>("qmlFile");
QTest::addColumn<QVector3D>("sphereCenter");
QTest::addColumn<float>("sphereRadius");
QTest::newRow("SphereMesh") << "qrc:/sphere.qml" << QVector3D(0.f, 0.f, 0.f) << 1.f;
QTest::newRow("CubeMesh") << "qrc:/cube.qml" << QVector3D(0.0928356f, -0.212021f, -0.0467958f) << 1.07583f; // weird!
QTest::newRow("CubeMesh") << "qrc:/cube.qml" << QVector3D(0.f, 0.f, 0.f) << static_cast<float>(qSqrt(3.)/2.); // not weird at all
}
void checkExtraGeometries()
@ -235,9 +372,10 @@ private Q_SLOTS:
const auto boundingSphere = test->sceneRoot()->worldBoundingVolumeWithChildren();
qDebug() << qmlFile << boundingSphere->radius() << boundingSphere->center();
QCOMPARE(boundingSphere->radius(), sphereRadius);
QVERIFY(qAbs(boundingSphere->center().x() - sphereCenter.x()) < 0.000001f); // qFuzzyCompare hates 0s
QVERIFY(qAbs(boundingSphere->center().y() - sphereCenter.y()) < 0.000001f);
QVERIFY(qAbs(boundingSphere->center().z() - sphereCenter.z()) < 0.000001f);
QVERIFY(qFuzzyIsNull(boundingSphere->center().x() - sphereCenter.x()));
QVERIFY(qFuzzyIsNull(boundingSphere->center().y() - sphereCenter.y()));
QVERIFY(qFuzzyIsNull(boundingSphere->center().z() - sphereCenter.z()));
}
void checkCustomGeometry_data()
@ -247,10 +385,10 @@ private Q_SLOTS:
QTest::addColumn<QVector3D>("expectedCenter");
QTest::addColumn<float>("expectedRadius");
QTest::addColumn<bool>("withPrimitiveRestart");
QTest::newRow("all") << 0 << 0 << QVector3D(-0.488892f, 0.0192147f, -75.4804f) << 25.5442f << false;
QTest::newRow("all") << 0 << 0 << QVector3D(0.0f, 0.0f, -75.0f) << 25.03997f << false;
QTest::newRow("first only") << 3 << 0 << QVector3D(0, 1, -100) << 1.0f << false;
QTest::newRow("second only") << 3 << int(3 * sizeof(ushort)) << QVector3D(0, -1, -50) << 1.0f << false;
QTest::newRow("all with primitive restart") << 0 << 0 << QVector3D(-0.488892f, 0.0192147f, -75.4804f) << 25.5442f << true;
QTest::newRow("all with primitive restart") << 0 << 0 << QVector3D(0.0f, 0.0f, -75.0f) << 25.03997f << true;
QTest::newRow("first only with primitive restart") << 4 << 0 << QVector3D(0, 1, -100) << 1.0f << true;
QTest::newRow("second only with primitive restart") << 4 << int(3 * sizeof(ushort)) << QVector3D(0, -1, -50) << 1.0f << true;
}
@ -376,18 +514,17 @@ private Q_SLOTS:
float radius = entityBackend->localBoundingVolume()->radius();
qDebug() << radius << center;
// truncate and compare integers only
QCOMPARE(int(radius), int(expectedRadius));
QCOMPARE(int(center.x()), int(expectedCenter.x()));
QCOMPARE(int(center.y()), int(expectedCenter.y()));
QCOMPARE(int(center.z()), int(expectedCenter.z()));
QCOMPARE(radius, expectedRadius);
QCOMPARE(center.x(), expectedCenter.x());
QCOMPARE(center.y(), expectedCenter.y());
QCOMPARE(center.z(), expectedCenter.z());
}
void checkCustomPackedGeometry()
{
int drawVertexCount = 6;
QVector3D expectedCenter(-0.488892f, 0.0192147f, -75.4804f);
float expectedRadius = 25.5442f;
QVector3D expectedCenter(0.0f, 0.0f, -75.0f);
float expectedRadius = 25.03997f;
// two triangles with different Z
QByteArray vdata;
@ -467,11 +604,10 @@ private Q_SLOTS:
float radius = entityBackend->localBoundingVolume()->radius();
qDebug() << radius << center;
// truncate and compare integers only
QVERIFY(int(radius) == int(expectedRadius));
QVERIFY(int(center.x()) == int(expectedCenter.x()));
QVERIFY(int(center.y()) == int(expectedCenter.y()));
QVERIFY(int(center.z()) == int(expectedCenter.z()));
QCOMPARE(radius, expectedRadius);
QCOMPARE(center.x(), expectedCenter.x());
QCOMPARE(center.y(), expectedCenter.y());
QCOMPARE(center.z(), expectedCenter.z());
}
};

View File

@ -29,6 +29,10 @@
#include <QtTest/QTest>
#include <Qt3DCore/qentity.h>
#include <Qt3DCore/qtransform.h>
#include <Qt3DRender/qgeometry.h>
#include <Qt3DRender/qgeometryrenderer.h>
#include <Qt3DRender/qattribute.h>
#include <Qt3DRender/qbuffer.h>
#include <Qt3DRender/private/nodemanagers_p.h>
#include <Qt3DRender/private/managers_p.h>
#include <Qt3DRender/private/entity_p.h>
@ -43,6 +47,40 @@ namespace {
Qt3DCore::QEntity *buildEntityAtDistance(float distance, Qt3DCore::QEntity *parent)
{
Qt3DCore::QEntity *entity = new Qt3DCore::QEntity(parent);
// create geometry with a valid bounding volume - a single point is sufficient
auto geometry = new Qt3DRender::QGeometry;
auto vertexBuffer = new Qt3DRender::QBuffer(Qt3DRender::QBuffer::VertexBuffer, geometry);
auto positionAttribute = new Qt3DRender::QAttribute;
positionAttribute->setName(Qt3DRender::QAttribute::defaultPositionAttributeName());
positionAttribute->setAttributeType(Qt3DRender::QAttribute::VertexAttribute);
positionAttribute->setVertexBaseType(Qt3DRender::QAttribute::Float);
positionAttribute->setVertexSize(3);
positionAttribute->setByteStride(3 * sizeof(float));
positionAttribute->setBuffer(vertexBuffer);
QByteArray vertexBufferData;
vertexBufferData.resize(static_cast<int>(3 * sizeof(float)));
auto vertexArray = reinterpret_cast<float*>(vertexBufferData.data());
int i = 0;
vertexArray[i++] = 0.0f;
vertexArray[i++] = 0.0f;
vertexArray[i++] = 0.0f;
vertexBuffer->setData(vertexBufferData);
positionAttribute->setCount(1);
geometry->addAttribute(positionAttribute);
auto geometryRenderer = new Qt3DRender::QGeometryRenderer;
geometryRenderer->setPrimitiveType(Qt3DRender::QGeometryRenderer::Points);
geometryRenderer->setGeometry(geometry);
entity->addComponent(geometryRenderer);
Qt3DCore::QTransform *transform = new Qt3DCore::QTransform(parent);
const QVector3D t = QVector3D(1.0f, 0.0f, 0.0f) * distance;