blob: 2acce915d3c73f7dc985f2d83af19948a2d54ef9 [file] [log] [blame]
// Copyright 2017 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef GARNET_LIB_UI_GFX_ENGINE_HIT_TESTER_H_
#define GARNET_LIB_UI_GFX_ENGINE_HIT_TESTER_H_
#include <vector>
#include "garnet/lib/ui/gfx/engine/hit.h"
namespace scenic_impl {
namespace gfx {
class Node;
class Session;
// Performs a hit test on the contents of a node.
class HitTester {
public:
HitTester() = default;
virtual ~HitTester() = default;
// Performs a hit test along the specified ray. Returns a list of hits
// sorted by increasing distance, then by increasing tree depth.
//
// The specific hit collection behavior depends on should_participate()
// behavior, implemented by each subclass.
std::vector<Hit> HitTest(Node* node, const escher::ray4& ray);
protected:
// Describes a possible hit within an enclosing tag node.
struct TagInfo {
static constexpr float kNoHit = std::numeric_limits<float>::infinity();
// The distance to the intersection as defined by |Hit.distance|.
float distance = kNoHit;
bool is_hit() const { return distance < kNoHit; }
void ReportIntersection(float d) {
if (d < distance)
distance = d;
}
};
// Describes a ray and its accumulated transform.
struct RayInfo {
// The ray to test in the object's coordinate system.
escher::ray4 ray;
// The accumulated inverse transformation matrix which maps the coordinate
// system of the node at which the hit test was initiated into the
// coordinate system of the object.
escher::mat4 inverse_transform;
};
// Used to determine hit collection behavior.
// In a session-based hit test, nodes must have a compatible tag and session.
// In a global hit test, all nodes participate, regardless of tag or session.
virtual bool should_participate(Node* node) = 0;
// Accumulates hit test results from the node, as seen by its parent.
// Applies the node's transform to the ray stack.
// |ray_info_| must be in the parent's local coordinate system.
void AccumulateHitsOuter(Node* node);
// Accumulates hit test results from the node, as seen by the node itself.
// Applies the node's tag to the tag stack.
// |ray_info_| must be in the node's local coordinate system.
void AccumulateHitsLocal(Node* node);
// Accumulates hit test results from the node's content and children.
// |ray_info_| must be in the node's local coordinate system.
void AccumulateHitsInner(Node* node);
// Returns true if the ray passes through the node's parts.
// |ray| must be in the node's local coordinate system.
static bool IsRayWithinPartsInner(Node* node, const escher::ray4& ray);
// Returns true if the ray passes through the node's clipped content.
// |ray| must be in the parent's local coordinate system.
//
// TODO(MZ-207): The way this works only makes geometric sense if the ray
// is parallel to the camera projection at the point being sampled.
static bool IsRayWithinClippedContentOuter(Node* node,
const escher::ray4& ray);
// Returns true if the ray passes through the node's clipped content.
// |ray| must be in the node's local coordinate system.
static bool IsRayWithinClippedContentInner(Node* node,
const escher::ray4& ray);
// The vector which accumulates hits.
std::vector<Hit> hits_;
// The current tag information.
// Null if there is no enclosing tagged node.
// TODO(SCN-909): Refactor out.
TagInfo* tag_info_ = nullptr;
// The current ray information.
// Null if there is no hit test currently in progress.
// TODO(SCN-909): Refactor out.
RayInfo* ray_info_ = nullptr;
};
class SessionHitTester : public HitTester {
public:
explicit SessionHitTester(Session* session);
virtual ~SessionHitTester() = default;
private:
// Node seen in hit testing only if non-zero tag and compatible session.
bool should_participate(Node* node) override;
Session* session_;
};
class GlobalHitTester : public HitTester {
public:
GlobalHitTester() = default;
virtual ~GlobalHitTester() = default;
private:
// A node always participates in the hit test, regardless of tag and session.
bool should_participate(Node* node) override { return true; }
};
} // namespace gfx
} // namespace scenic_impl
#endif // GARNET_LIB_UI_GFX_ENGINE_HIT_TESTER_H_