summaryrefslogtreecommitdiffstats
path: root/src/render/jobs/pickboundingvolumeutils.cpp
diff options
context:
space:
mode:
authorPaul Lemire <paul.lemire@kdab.com>2016-08-03 09:09:19 +0200
committerPaul Lemire <paul.lemire@kdab.com>2016-10-13 12:06:03 +0000
commit970019a6704bbb9b965cffc589ab76a787e06542 (patch)
tree30687c07bc6601b9cf397d2c67067ad28f6ccace /src/render/jobs/pickboundingvolumeutils.cpp
parent140fe02a9e8fa6eb42edaa99474baaa20f7a18b3 (diff)
PickBoundingVolumeJob: small refactoring
- Split into two files (pickboundingvolumeutils.cpp and pickboundingvolumejob.cpp) to make unit testing easier. - Remove tight coupling to Renderer - Separate whole logic in smaller functions Change-Id: Ibb463e9bac161b666956d3503301fd1e2e1e8b75 Reviewed-by: Paul Lemire <paul.lemire@kdab.com>
Diffstat (limited to 'src/render/jobs/pickboundingvolumeutils.cpp')
-rw-r--r--src/render/jobs/pickboundingvolumeutils.cpp267
1 files changed, 267 insertions, 0 deletions
diff --git a/src/render/jobs/pickboundingvolumeutils.cpp b/src/render/jobs/pickboundingvolumeutils.cpp
new file mode 100644
index 000000000..c9f14490a
--- /dev/null
+++ b/src/render/jobs/pickboundingvolumeutils.cpp
@@ -0,0 +1,267 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 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 "pickboundingvolumeutils_p.h"
+#include <Qt3DRender/private/framegraphnode_p.h>
+#include <Qt3DRender/private/cameralens_p.h>
+#include <Qt3DRender/private/cameraselectornode_p.h>
+#include <Qt3DRender/private/viewportnode_p.h>
+#include <Qt3DRender/private/rendersurfaceselector_p.h>
+#include <Qt3DRender/private/triangleboundingvolume_p.h>
+#include <Qt3DRender/private/nodemanagers_p.h>
+#include <Qt3DRender/private/sphere_p.h>
+#include <Qt3DRender/private/entity_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+
+namespace Render {
+
+namespace PickingUtils {
+
+void ViewportCameraAreaGatherer::visit(FrameGraphNode *node)
+{
+ const auto children = node->children();
+ for (Render::FrameGraphNode *n : children)
+ visit(n);
+ if (node->childrenIds().empty())
+ m_leaves.push_back(node);
+}
+
+ViewportCameraAreaTriplet ViewportCameraAreaGatherer::gatherUpViewportCameraAreas(Render::FrameGraphNode *node) const
+{
+ ViewportCameraAreaTriplet vca;
+ vca.viewport = QRectF(0.0f, 0.0f, 1.0f, 1.0f);
+
+ while (node) {
+ if (node->isEnabled()) {
+ switch (node->nodeType()) {
+ case FrameGraphNode::CameraSelector:
+ vca.cameraId = static_cast<const CameraSelector *>(node)->cameraUuid();
+ break;
+ case FrameGraphNode::Viewport:
+ vca.viewport = computeViewport(vca.viewport, static_cast<const ViewportNode *>(node));
+ break;
+ case FrameGraphNode::Surface:
+ vca.area = static_cast<const RenderSurfaceSelector *>(node)->renderTargetSize();
+ break;
+ default:
+ break;
+ }
+ }
+ node = node->parent();
+ }
+ return vca;
+}
+
+QVector<ViewportCameraAreaTriplet> ViewportCameraAreaGatherer::gather(FrameGraphNode *root)
+{
+ // Retrieve all leaves
+ visit(root);
+ QVector<ViewportCameraAreaTriplet> vcaTriplets;
+ vcaTriplets.reserve(m_leaves.count());
+
+ // Find all viewport/camera pairs by traversing from leaf to root
+ for (Render::FrameGraphNode *leaf : qAsConst(m_leaves)) {
+ ViewportCameraAreaTriplet vcaTriplet = gatherUpViewportCameraAreas(leaf);
+ if (!vcaTriplet.cameraId.isNull() && isUnique(vcaTriplets, vcaTriplet))
+ vcaTriplets.push_back(vcaTriplet);
+ }
+ return vcaTriplets;
+}
+
+bool PickingUtils::ViewportCameraAreaGatherer::isUnique(const QVector<ViewportCameraAreaTriplet> &vcaTriplets, const ViewportCameraAreaTriplet &vca) const
+{
+ for (const ViewportCameraAreaTriplet &triplet : vcaTriplets) {
+ if (vca.cameraId == triplet.cameraId && vca.viewport == triplet.viewport && vca.area == triplet.area)
+ return false;
+ }
+ return true;
+}
+
+QVector<Entity *> gatherEntities(Entity *entity, QVector<Entity *> entities)
+{
+ if (entity != nullptr) {
+ entities.push_back(entity);
+ // Traverse children
+ const auto children = entity->children();
+ for (Entity *child : children)
+ entities = gatherEntities(child, std::move(entities));
+ }
+ return entities;
+}
+
+EntityGatherer::EntityGatherer(Entity *root)
+ : m_root(root)
+ , m_needsRefresh(true)
+{
+}
+
+QVector<Entity *> EntityGatherer::entities() const
+{
+ if (m_needsRefresh) {
+ m_entities.clear();
+ m_entities = gatherEntities(m_root, std::move(m_entities));
+ m_needsRefresh = false;
+ }
+ return m_entities;
+}
+
+void CollisionVisitor::visit(uint andx, const QVector3D &a, uint bndx, const QVector3D &b, uint cndx, const QVector3D &c)
+{
+ TriangleBoundingVolume volume(m_root->peerId(), a, b, c);
+ volume = volume.transform(*m_root->worldTransform());
+
+ QCollisionQueryResult::Hit queryResult = rayCasting.query(m_ray, &volume);
+ if (queryResult.m_distance > 0.) {
+ queryResult.m_triangleIndex = m_triangleIndex;
+ queryResult.m_vertexIndex[0] = andx;
+ queryResult.m_vertexIndex[1] = bndx;
+ queryResult.m_vertexIndex[2] = cndx;
+ hits.push_back(queryResult);
+ }
+
+ m_triangleIndex++;
+}
+
+AbstractCollisionGathererFunctor::AbstractCollisionGathererFunctor()
+ : m_manager(nullptr)
+{ }
+
+AbstractCollisionGathererFunctor::~AbstractCollisionGathererFunctor()
+{ }
+
+AbstractCollisionGathererFunctor::result_type AbstractCollisionGathererFunctor::operator ()(const Entity *entity) const
+{
+ HObjectPicker objectPickerHandle = entity->componentHandle<ObjectPicker, 16>();
+
+ // If the Entity which actually received the hit doesn't have
+ // an object picker component, we need to check the parent if it has one ...
+ while (objectPickerHandle.isNull() && entity != nullptr) {
+ entity = entity->parent();
+ if (entity != nullptr)
+ objectPickerHandle = entity->componentHandle<ObjectPicker, 16>();
+ }
+
+ ObjectPicker *objectPicker = m_manager->objectPickerManager()->data(objectPickerHandle);
+ if (objectPicker == nullptr)
+ return result_type(); // don't bother picking entities that don't have an object picker
+
+ Qt3DRender::QRayCastingService rayCasting;
+
+ return pick(&rayCasting, entity);
+}
+
+AbstractCollisionGathererFunctor::result_type EntityCollisionGathererFunctor::pick(QAbstractCollisionQueryService *rayCasting, const Entity *entity) const
+{
+ result_type result;
+
+ const QCollisionQueryResult::Hit queryResult = rayCasting->query(m_ray, entity->worldBoundingVolume());
+ if (queryResult.m_distance >= 0.f)
+ result.push_back(queryResult);
+
+ return result;
+}
+
+AbstractCollisionGathererFunctor::result_type TriangleCollisionGathererFunctor::pick(QAbstractCollisionQueryService *rayCasting, const Entity *entity) const
+{
+ result_type result;
+
+ GeometryRenderer *gRenderer = entity->renderComponent<GeometryRenderer>();
+ if (!gRenderer)
+ return result;
+
+ if (rayHitsEntity(rayCasting, entity)) {
+ CollisionVisitor visitor(m_manager, entity, m_ray);
+ visitor.apply(gRenderer, entity->peerId());
+ result = visitor.hits;
+
+ struct
+ {
+ bool operator()(const result_type::value_type &a, const result_type::value_type &b)
+ {
+ return a.m_distance < b.m_distance;
+ }
+ } compareHitsDistance;
+ std::sort(result.begin(), result.end(), compareHitsDistance);
+ }
+
+ return result;
+}
+
+bool TriangleCollisionGathererFunctor::rayHitsEntity(QAbstractCollisionQueryService *rayCasting, const Entity *entity) const
+{
+ const QCollisionQueryResult::Hit queryResult = rayCasting->query(m_ray, entity->worldBoundingVolume());
+ return queryResult.m_distance >= 0.f;
+}
+
+CollisionVisitor::HitList reduceToFirstHit(CollisionVisitor::HitList &result, const CollisionVisitor::HitList &intermediate)
+{
+ if (!intermediate.empty()) {
+ if (result.empty())
+ result.push_back(intermediate.front());
+ float closest = result.front().m_distance;
+ for (const auto &v : intermediate) {
+ if (v.m_distance < closest) {
+ result.push_front(v);
+ closest = v.m_distance;
+ }
+ }
+
+ while (result.size() > 1)
+ result.pop_back();
+ }
+ return result;
+}
+
+CollisionVisitor::HitList reduceToAllHits(CollisionVisitor::HitList &results, const CollisionVisitor::HitList &intermediate)
+{
+ if (!intermediate.empty())
+ results << intermediate;
+ return results;
+}
+
+} // PickingUtils
+
+} // Render
+
+} // Qt3DRender
+
+QT_END_NAMESPACE