force coarse picking, sort avatars

This commit is contained in:
SamGondelman 2018-08-22 17:49:02 -07:00
parent 5c0b12abf6
commit c474f38860
11 changed files with 178 additions and 108 deletions

View file

@ -46,6 +46,7 @@
#include "InterfaceLogging.h" #include "InterfaceLogging.h"
#include "LocationBookmarks.h" #include "LocationBookmarks.h"
#include "DeferredLightingEffect.h" #include "DeferredLightingEffect.h"
#include "PickManager.h"
#include "AmbientOcclusionEffect.h" #include "AmbientOcclusionEffect.h"
#include "RenderShadowTask.h" #include "RenderShadowTask.h"
@ -688,6 +689,11 @@ Menu::Menu() {
addCheckableActionToQMenuAndActionHash(physicsOptionsMenu, MenuOption::PhysicsShowBulletConstraints, 0, false, qApp, SLOT(setShowBulletConstraints(bool))); addCheckableActionToQMenuAndActionHash(physicsOptionsMenu, MenuOption::PhysicsShowBulletConstraints, 0, false, qApp, SLOT(setShowBulletConstraints(bool)));
addCheckableActionToQMenuAndActionHash(physicsOptionsMenu, MenuOption::PhysicsShowBulletConstraintLimits, 0, false, qApp, SLOT(setShowBulletConstraintLimits(bool))); addCheckableActionToQMenuAndActionHash(physicsOptionsMenu, MenuOption::PhysicsShowBulletConstraintLimits, 0, false, qApp, SLOT(setShowBulletConstraintLimits(bool)));
// Developer > Picking >>>
MenuWrapper* pickingOptionsMenu = developerMenu->addMenu("Picking");
addCheckableActionToQMenuAndActionHash(pickingOptionsMenu, MenuOption::ForceCoarsePicking, 0, false,
DependencyManager::get<PickManager>().data(), SLOT(setForceCoarsePicking(bool)));
// Developer > Display Crash Options // Developer > Display Crash Options
addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::DisplayCrashOptions, 0, true); addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::DisplayCrashOptions, 0, true);
// Developer > Crash >>> // Developer > Crash >>>

View file

@ -221,6 +221,7 @@ namespace MenuOption {
const QString NotificationSounds = "play_notification_sounds"; const QString NotificationSounds = "play_notification_sounds";
const QString NotificationSoundsSnapshot = "play_notification_sounds_snapshot"; const QString NotificationSoundsSnapshot = "play_notification_sounds_snapshot";
const QString NotificationSoundsTablet = "play_notification_sounds_tablet"; const QString NotificationSoundsTablet = "play_notification_sounds_tablet";
const QString ForceCoarsePicking = "Force Coarse Picking";
} }
#endif // hifi_Menu_h #endif // hifi_Menu_h

View file

@ -553,6 +553,14 @@ RayToAvatarIntersectionResult AvatarManager::findRayIntersectionVector(const Pic
return result; return result;
} }
// It's better to intersect the ray against the avatar's actual mesh, but this is currently difficult to
// do, because the transformed mesh data only exists over in GPU-land. As a compromise, this code
// intersects against the avatars capsule and then against the (T-pose) mesh. The end effect is that picking
// against the avatar is sort-of right, but you likely wont be able to pick against the arms.
// TODO -- find a way to extract transformed avatar mesh data from the rendering engine.
std::vector<SortedAvatar> sortedAvatars;
auto avatarHashCopy = getHashCopy(); auto avatarHashCopy = getHashCopy();
for (auto avatarData : avatarHashCopy) { for (auto avatarData : avatarHashCopy) {
auto avatar = std::static_pointer_cast<Avatar>(avatarData); auto avatar = std::static_pointer_cast<Avatar>(avatarData);
@ -561,47 +569,60 @@ RayToAvatarIntersectionResult AvatarManager::findRayIntersectionVector(const Pic
continue; continue;
} }
float distance; float distance = FLT_MAX;
BoxFace face; #if 0
glm::vec3 surfaceNormal;
SkeletonModelPointer avatarModel = avatar->getSkeletonModel();
// It's better to intersect the ray against the avatar's actual mesh, but this is currently difficult to
// do, because the transformed mesh data only exists over in GPU-land. As a compromise, this code
// intersects against the avatars capsule and then against the (T-pose) mesh. The end effect is that picking
// against the avatar is sort-of right, but you likely wont be able to pick against the arms.
// TODO -- find a way to extract transformed avatar mesh data from the rendering engine.
// if we weren't picking against the capsule, we would want to pick against the avatarBounds... // if we weren't picking against the capsule, we would want to pick against the avatarBounds...
// AABox avatarBounds = avatarModel->getRenderableMeshBound(); SkeletonModelPointer avatarModel = avatar->getSkeletonModel();
// if (!avatarBounds.findRayIntersection(ray.origin, normDirection, distance, face, surfaceNormal)) { AABox avatarBounds = avatarModel->getRenderableMeshBound();
// // ray doesn't intersect avatar's bounding-box if (avatarBounds.contains(ray.origin)) {
// continue; distance = 0.0f;
// } } else {
float boundDistance = FLT_MAX;
BoxFace face;
glm::vec3 surfaceNormal;
if (avatarBounds.findRayIntersection(ray.origin, ray.direction, boundDistance, face, surfaceNormal)) {
distance = boundDistance;
}
}
#else
glm::vec3 start; glm::vec3 start;
glm::vec3 end; glm::vec3 end;
float radius; float radius;
avatar->getCapsule(start, end, radius); avatar->getCapsule(start, end, radius);
bool intersects = findRayCapsuleIntersection(ray.origin, ray.direction, start, end, radius, distance); findRayCapsuleIntersection(ray.origin, ray.direction, start, end, radius, distance);
if (!intersects) { #endif
// ray doesn't intersect avatar's capsule
continue; if (distance < FLT_MAX) {
sortedAvatars.emplace_back(distance, avatar);
}
}
if (sortedAvatars.size() > 1) {
static auto comparator = [](const SortedAvatar& left, const SortedAvatar& right) { return left.first > right.first; };
std::sort(sortedAvatars.begin(), sortedAvatars.end(), comparator);
}
for (auto it = sortedAvatars.begin(); it != sortedAvatars.end(); ++it) {
const SortedAvatar& sortedAvatar = *it;
// We can exit once avatarCapsuleDistance > bestDistance
if (sortedAvatar.first > result.distance) {
break;
} }
float distance = FLT_MAX;
BoxFace face;
glm::vec3 surfaceNormal;
QVariantMap extraInfo; QVariantMap extraInfo;
intersects = avatarModel->findRayIntersectionAgainstSubMeshes(ray.origin, ray.direction, SkeletonModelPointer avatarModel = sortedAvatar.second->getSkeletonModel();
distance, face, surfaceNormal, extraInfo, true); if (avatarModel->findRayIntersectionAgainstSubMeshes(ray.origin, ray.direction, distance, face, surfaceNormal, extraInfo, true)) {
if (distance < result.distance) {
if (intersects && (!result.intersects || distance < result.distance)) { result.intersects = true;
result.intersects = true; result.avatarID = sortedAvatar.second->getID();
result.avatarID = avatar->getID(); result.distance = distance;
result.distance = distance; result.face = face;
result.face = face; result.surfaceNormal = surfaceNormal;
result.surfaceNormal = surfaceNormal; result.extraInfo = extraInfo;
result.extraInfo = extraInfo; }
} }
} }
@ -625,6 +646,14 @@ ParabolaToAvatarIntersectionResult AvatarManager::findParabolaIntersectionVector
return result; return result;
} }
// It's better to intersect the ray against the avatar's actual mesh, but this is currently difficult to
// do, because the transformed mesh data only exists over in GPU-land. As a compromise, this code
// intersects against the avatars capsule and then against the (T-pose) mesh. The end effect is that picking
// against the avatar is sort-of right, but you likely wont be able to pick against the arms.
// TODO -- find a way to extract transformed avatar mesh data from the rendering engine.
std::vector<SortedAvatar> sortedAvatars;
auto avatarHashCopy = getHashCopy(); auto avatarHashCopy = getHashCopy();
for (auto avatarData : avatarHashCopy) { for (auto avatarData : avatarHashCopy) {
auto avatar = std::static_pointer_cast<Avatar>(avatarData); auto avatar = std::static_pointer_cast<Avatar>(avatarData);
@ -633,47 +662,60 @@ ParabolaToAvatarIntersectionResult AvatarManager::findParabolaIntersectionVector
continue; continue;
} }
float parabolicDistance; float distance = FLT_MAX;
BoxFace face; #if 0
glm::vec3 surfaceNormal;
SkeletonModelPointer avatarModel = avatar->getSkeletonModel();
// It's better to intersect the parabola against the avatar's actual mesh, but this is currently difficult to
// do, because the transformed mesh data only exists over in GPU-land. As a compromise, this code
// intersects against the avatars capsule and then against the (T-pose) mesh. The end effect is that picking
// against the avatar is sort-of right, but you likely wont be able to pick against the arms.
// TODO -- find a way to extract transformed avatar mesh data from the rendering engine.
// if we weren't picking against the capsule, we would want to pick against the avatarBounds... // if we weren't picking against the capsule, we would want to pick against the avatarBounds...
// AABox avatarBounds = avatarModel->getRenderableMeshBound(); SkeletonModelPointer avatarModel = avatar->getSkeletonModel();
// if (!avatarBounds.findParabolaIntersection(pick.origin, pick.velocity, pick.acceleration, parabolicDistance, face, surfaceNormal)) { AABox avatarBounds = avatarModel->getRenderableMeshBound();
// // parabola doesn't intersect avatar's bounding-box if (avatarBounds.contains(pick.origin)) {
// continue; distance = 0.0f;
// } } else {
float boundDistance = FLT_MAX;
BoxFace face;
glm::vec3 surfaceNormal;
if (avatarBounds.findParabolaIntersection(pick.origin, pick.velocity, pick.acceleration, boundDistance, face, surfaceNormal)) {
distance = boundDistance;
}
}
#else
glm::vec3 start; glm::vec3 start;
glm::vec3 end; glm::vec3 end;
float radius; float radius;
avatar->getCapsule(start, end, radius); avatar->getCapsule(start, end, radius);
bool intersects = findParabolaCapsuleIntersection(pick.origin, pick.velocity, pick.acceleration, start, end, radius, avatar->getWorldOrientation(), parabolicDistance); findParabolaCapsuleIntersection(pick.origin, pick.velocity, pick.acceleration, start, end, radius, avatar->getWorldOrientation(), distance);
if (!intersects) { #endif
// ray doesn't intersect avatar's capsule
continue; if (distance < FLT_MAX) {
sortedAvatars.emplace_back(distance, avatar);
}
}
if (sortedAvatars.size() > 1) {
static auto comparator = [](const SortedAvatar& left, const SortedAvatar& right) { return left.first > right.first; };
std::sort(sortedAvatars.begin(), sortedAvatars.end(), comparator);
}
for (auto it = sortedAvatars.begin(); it != sortedAvatars.end(); ++it) {
const SortedAvatar& sortedAvatar = *it;
// We can exit once avatarCapsuleDistance > bestDistance
if (sortedAvatar.first > result.parabolicDistance) {
break;
} }
float parabolicDistance = FLT_MAX;
BoxFace face;
glm::vec3 surfaceNormal;
QVariantMap extraInfo; QVariantMap extraInfo;
intersects = avatarModel->findParabolaIntersectionAgainstSubMeshes(pick.origin, pick.velocity, pick.acceleration, SkeletonModelPointer avatarModel = sortedAvatar.second->getSkeletonModel();
parabolicDistance, face, surfaceNormal, extraInfo, true); if (avatarModel->findParabolaIntersectionAgainstSubMeshes(pick.origin, pick.velocity, pick.acceleration, parabolicDistance, face, surfaceNormal, extraInfo, true)) {
if (parabolicDistance < result.parabolicDistance) {
if (intersects && (!result.intersects || parabolicDistance < result.parabolicDistance)) { result.intersects = true;
result.intersects = true; result.avatarID = sortedAvatar.second->getID();
result.avatarID = avatar->getID(); result.parabolicDistance = parabolicDistance;
result.parabolicDistance = parabolicDistance; result.face = face;
result.face = face; result.surfaceNormal = surfaceNormal;
result.surfaceNormal = surfaceNormal; result.extraInfo = extraInfo;
result.extraInfo = extraInfo; }
} }
} }

View file

@ -27,6 +27,8 @@
#include "AvatarMotionState.h" #include "AvatarMotionState.h"
#include "MyAvatar.h" #include "MyAvatar.h"
using SortedAvatar = std::pair<float, std::shared_ptr<Avatar>>;
/**jsdoc /**jsdoc
* The AvatarManager API has properties and methods which manage Avatars within the same domain. * The AvatarManager API has properties and methods which manage Avatars within the same domain.
* *

View file

@ -13,11 +13,13 @@
#include "avatar/AvatarManager.h" #include "avatar/AvatarManager.h"
#include "scripting/HMDScriptingInterface.h" #include "scripting/HMDScriptingInterface.h"
#include "DependencyManager.h" #include "DependencyManager.h"
#include "PickManager.h"
PickResultPointer ParabolaPick::getEntityIntersection(const PickParabola& pick) { PickResultPointer ParabolaPick::getEntityIntersection(const PickParabola& pick) {
if (glm::length2(pick.acceleration) > EPSILON && glm::length2(pick.velocity) > EPSILON) { if (glm::length2(pick.acceleration) > EPSILON && glm::length2(pick.velocity) > EPSILON) {
bool precisionPicking = !(getFilter().doesPickCoarse() || DependencyManager::get<PickManager>()->getForceCoarsePicking());
ParabolaToEntityIntersectionResult entityRes = ParabolaToEntityIntersectionResult entityRes =
DependencyManager::get<EntityScriptingInterface>()->findParabolaIntersectionVector(pick, !getFilter().doesPickCoarse(), DependencyManager::get<EntityScriptingInterface>()->findParabolaIntersectionVector(pick, precisionPicking,
getIncludeItemsAs<EntityItemID>(), getIgnoreItemsAs<EntityItemID>(), !getFilter().doesPickInvisible(), !getFilter().doesPickNonCollidable()); getIncludeItemsAs<EntityItemID>(), getIgnoreItemsAs<EntityItemID>(), !getFilter().doesPickInvisible(), !getFilter().doesPickNonCollidable());
if (entityRes.intersects) { if (entityRes.intersects) {
return std::make_shared<ParabolaPickResult>(IntersectionType::ENTITY, entityRes.entityID, entityRes.distance, entityRes.parabolicDistance, entityRes.intersection, pick, entityRes.surfaceNormal, entityRes.extraInfo); return std::make_shared<ParabolaPickResult>(IntersectionType::ENTITY, entityRes.entityID, entityRes.distance, entityRes.parabolicDistance, entityRes.intersection, pick, entityRes.surfaceNormal, entityRes.extraInfo);
@ -28,8 +30,9 @@ PickResultPointer ParabolaPick::getEntityIntersection(const PickParabola& pick)
PickResultPointer ParabolaPick::getOverlayIntersection(const PickParabola& pick) { PickResultPointer ParabolaPick::getOverlayIntersection(const PickParabola& pick) {
if (glm::length2(pick.acceleration) > EPSILON && glm::length2(pick.velocity) > EPSILON) { if (glm::length2(pick.acceleration) > EPSILON && glm::length2(pick.velocity) > EPSILON) {
bool precisionPicking = !(getFilter().doesPickCoarse() || DependencyManager::get<PickManager>()->getForceCoarsePicking());
ParabolaToOverlayIntersectionResult overlayRes = ParabolaToOverlayIntersectionResult overlayRes =
qApp->getOverlays().findParabolaIntersectionVector(pick, !getFilter().doesPickCoarse(), qApp->getOverlays().findParabolaIntersectionVector(pick, precisionPicking,
getIncludeItemsAs<OverlayID>(), getIgnoreItemsAs<OverlayID>(), !getFilter().doesPickInvisible(), !getFilter().doesPickNonCollidable()); getIncludeItemsAs<OverlayID>(), getIgnoreItemsAs<OverlayID>(), !getFilter().doesPickInvisible(), !getFilter().doesPickNonCollidable());
if (overlayRes.intersects) { if (overlayRes.intersects) {
return std::make_shared<ParabolaPickResult>(IntersectionType::OVERLAY, overlayRes.overlayID, overlayRes.distance, overlayRes.parabolicDistance, overlayRes.intersection, pick, overlayRes.surfaceNormal, overlayRes.extraInfo); return std::make_shared<ParabolaPickResult>(IntersectionType::OVERLAY, overlayRes.overlayID, overlayRes.distance, overlayRes.parabolicDistance, overlayRes.intersection, pick, overlayRes.surfaceNormal, overlayRes.extraInfo);

View file

@ -13,10 +13,12 @@
#include "avatar/AvatarManager.h" #include "avatar/AvatarManager.h"
#include "scripting/HMDScriptingInterface.h" #include "scripting/HMDScriptingInterface.h"
#include "DependencyManager.h" #include "DependencyManager.h"
#include "PickManager.h"
PickResultPointer RayPick::getEntityIntersection(const PickRay& pick) { PickResultPointer RayPick::getEntityIntersection(const PickRay& pick) {
bool precisionPicking = !(getFilter().doesPickCoarse() || DependencyManager::get<PickManager>()->getForceCoarsePicking());
RayToEntityIntersectionResult entityRes = RayToEntityIntersectionResult entityRes =
DependencyManager::get<EntityScriptingInterface>()->findRayIntersectionVector(pick, !getFilter().doesPickCoarse(), DependencyManager::get<EntityScriptingInterface>()->findRayIntersectionVector(pick, precisionPicking,
getIncludeItemsAs<EntityItemID>(), getIgnoreItemsAs<EntityItemID>(), !getFilter().doesPickInvisible(), !getFilter().doesPickNonCollidable()); getIncludeItemsAs<EntityItemID>(), getIgnoreItemsAs<EntityItemID>(), !getFilter().doesPickInvisible(), !getFilter().doesPickNonCollidable());
if (entityRes.intersects) { if (entityRes.intersects) {
return std::make_shared<RayPickResult>(IntersectionType::ENTITY, entityRes.entityID, entityRes.distance, entityRes.intersection, pick, entityRes.surfaceNormal, entityRes.extraInfo); return std::make_shared<RayPickResult>(IntersectionType::ENTITY, entityRes.entityID, entityRes.distance, entityRes.intersection, pick, entityRes.surfaceNormal, entityRes.extraInfo);
@ -26,8 +28,9 @@ PickResultPointer RayPick::getEntityIntersection(const PickRay& pick) {
} }
PickResultPointer RayPick::getOverlayIntersection(const PickRay& pick) { PickResultPointer RayPick::getOverlayIntersection(const PickRay& pick) {
bool precisionPicking = !(getFilter().doesPickCoarse() || DependencyManager::get<PickManager>()->getForceCoarsePicking());
RayToOverlayIntersectionResult overlayRes = RayToOverlayIntersectionResult overlayRes =
qApp->getOverlays().findRayIntersectionVector(pick, !getFilter().doesPickCoarse(), qApp->getOverlays().findRayIntersectionVector(pick, precisionPicking,
getIncludeItemsAs<OverlayID>(), getIgnoreItemsAs<OverlayID>(), !getFilter().doesPickInvisible(), !getFilter().doesPickNonCollidable()); getIncludeItemsAs<OverlayID>(), getIgnoreItemsAs<OverlayID>(), !getFilter().doesPickInvisible(), !getFilter().doesPickNonCollidable());
if (overlayRes.intersects) { if (overlayRes.intersects) {
return std::make_shared<RayPickResult>(IntersectionType::OVERLAY, overlayRes.overlayID, overlayRes.distance, overlayRes.intersection, pick, overlayRes.surfaceNormal, overlayRes.extraInfo); return std::make_shared<RayPickResult>(IntersectionType::OVERLAY, overlayRes.overlayID, overlayRes.distance, overlayRes.intersection, pick, overlayRes.surfaceNormal, overlayRes.extraInfo);

View file

@ -1559,7 +1559,7 @@ class RayToAvatarIntersectionResult {
public: public:
bool intersects { false }; bool intersects { false };
QUuid avatarID; QUuid avatarID;
float distance { 0.0f }; float distance { FLT_MAX };
BoxFace face; BoxFace face;
glm::vec3 intersection; glm::vec3 intersection;
glm::vec3 surfaceNormal; glm::vec3 surfaceNormal;

View file

@ -72,11 +72,15 @@ PickResultPointer PickQuery::getPrevPickResult() const {
void PickQuery::setIgnoreItems(const QVector<QUuid>& ignoreItems) { void PickQuery::setIgnoreItems(const QVector<QUuid>& ignoreItems) {
withWriteLock([&] { withWriteLock([&] {
_ignoreItems = ignoreItems; _ignoreItems = ignoreItems;
// We sort these items here so the PickCacheOptimizer can catch cases where two picks have the same ignoreItems in a different order
std::sort(_ignoreItems.begin(), _ignoreItems.end(), std::less<QUuid>());
}); });
} }
void PickQuery::setIncludeItems(const QVector<QUuid>& includeItems) { void PickQuery::setIncludeItems(const QVector<QUuid>& includeItems) {
withWriteLock([&] { withWriteLock([&] {
_includeItems = includeItems; _includeItems = includeItems;
// We sort these items here so the PickCacheOptimizer can catch cases where two picks have the same includeItems in a different order
std::sort(_includeItems.begin(), _includeItems.end(), std::less<QUuid>());
}); });
} }

View file

@ -16,7 +16,10 @@
#include <NumericalConstants.h> #include <NumericalConstants.h>
class PickManager : public Dependency, protected ReadWriteLockable { #include <QObject>
class PickManager : public QObject, public Dependency, protected ReadWriteLockable {
Q_OBJECT
SINGLETON_DEPENDENCY SINGLETON_DEPENDENCY
public: public:
@ -53,7 +56,13 @@ public:
unsigned int getPerFrameTimeBudget() const { return _perFrameTimeBudget; } unsigned int getPerFrameTimeBudget() const { return _perFrameTimeBudget; }
void setPerFrameTimeBudget(unsigned int numUsecs) { _perFrameTimeBudget = numUsecs; } void setPerFrameTimeBudget(unsigned int numUsecs) { _perFrameTimeBudget = numUsecs; }
bool getForceCoarsePicking() { return _forceCoarsePicking; }
public slots:
void setForceCoarsePicking(bool forceCoarsePicking) { _forceCoarsePicking = forceCoarsePicking; }
protected: protected:
bool _forceCoarsePicking { false };
std::function<bool()> _shouldPickHUDOperator; std::function<bool()> _shouldPickHUDOperator;
std::function<glm::vec2(const glm::vec3&)> _calculatePos2DFromHUDOperator; std::function<glm::vec2(const glm::vec3&)> _calculatePos2DFromHUDOperator;

View file

@ -29,7 +29,7 @@ void TriangleSet::clear() {
_bounds.clear(); _bounds.clear();
_isBalanced = false; _isBalanced = false;
_triangleOctree.clear(); _triangleTree.clear();
} }
bool TriangleSet::convexHullContains(const glm::vec3& point) const { bool TriangleSet::convexHullContains(const glm::vec3& point) const {
@ -53,16 +53,16 @@ void TriangleSet::debugDump() {
qDebug() << __FUNCTION__; qDebug() << __FUNCTION__;
qDebug() << "bounds:" << getBounds(); qDebug() << "bounds:" << getBounds();
qDebug() << "triangles:" << size() << "at top level...."; qDebug() << "triangles:" << size() << "at top level....";
qDebug() << "----- _triangleOctree -----"; qDebug() << "----- _triangleTree -----";
_triangleOctree.debugDump(); _triangleTree.debugDump();
} }
void TriangleSet::balanceOctree() { void TriangleSet::balanceTree() {
_triangleOctree.reset(_bounds); _triangleTree.reset(_bounds);
// insert all the triangles // insert all the triangles
for (size_t i = 0; i < _triangles.size(); i++) { for (size_t i = 0; i < _triangles.size(); i++) {
_triangleOctree.insert(i); _triangleTree.insert(i);
} }
_isBalanced = true; _isBalanced = true;
@ -77,13 +77,13 @@ void TriangleSet::balanceOctree() {
// With a k-d tree: 2 ^ MAX_DEPTH = 4096 leaves // With a k-d tree: 2 ^ MAX_DEPTH = 4096 leaves
static const int MAX_DEPTH = 12; static const int MAX_DEPTH = 12;
TriangleSet::TriangleOctreeCell::TriangleOctreeCell(std::vector<Triangle>& allTriangles, const AABox& bounds, int depth) : TriangleSet::TriangleTreeCell::TriangleTreeCell(std::vector<Triangle>& allTriangles, const AABox& bounds, int depth) :
_allTriangles(allTriangles) _allTriangles(allTriangles)
{ {
reset(bounds, depth); reset(bounds, depth);
} }
void TriangleSet::TriangleOctreeCell::clear() { void TriangleSet::TriangleTreeCell::clear() {
_population = 0; _population = 0;
_triangleIndices.clear(); _triangleIndices.clear();
_bounds.clear(); _bounds.clear();
@ -91,13 +91,13 @@ void TriangleSet::TriangleOctreeCell::clear() {
_children.second.reset(); _children.second.reset();
} }
void TriangleSet::TriangleOctreeCell::reset(const AABox& bounds, int depth) { void TriangleSet::TriangleTreeCell::reset(const AABox& bounds, int depth) {
clear(); clear();
_bounds = bounds; _bounds = bounds;
_depth = depth; _depth = depth;
} }
void TriangleSet::TriangleOctreeCell::debugDump() { void TriangleSet::TriangleTreeCell::debugDump() {
qDebug() << __FUNCTION__; qDebug() << __FUNCTION__;
qDebug() << " bounds:" << getBounds(); qDebug() << " bounds:" << getBounds();
qDebug() << " depth:" << _depth; qDebug() << " depth:" << _depth;
@ -123,7 +123,7 @@ void TriangleSet::TriangleOctreeCell::debugDump() {
} }
} }
std::pair<AABox, AABox> TriangleSet::TriangleOctreeCell::getTriangleOctreeCellChildBounds() { std::pair<AABox, AABox> TriangleSet::TriangleTreeCell::getTriangleTreeCellChildBounds() {
std::pair<AABox, AABox> toReturn; std::pair<AABox, AABox> toReturn;
int axis = 0; int axis = 0;
// find biggest axis // find biggest axis
@ -145,20 +145,20 @@ std::pair<AABox, AABox> TriangleSet::TriangleOctreeCell::getTriangleOctreeCellCh
return toReturn; return toReturn;
} }
void TriangleSet::TriangleOctreeCell::insert(size_t triangleIndex) { void TriangleSet::TriangleTreeCell::insert(size_t triangleIndex) {
_population++; _population++;
// if we're not yet at the max depth, then check which child the triangle fits in // if we're not yet at the max depth, then check which child the triangle fits in
if (_depth < MAX_DEPTH) { if (_depth < MAX_DEPTH) {
const Triangle& triangle = _allTriangles[triangleIndex]; const Triangle& triangle = _allTriangles[triangleIndex];
auto childBounds = getTriangleOctreeCellChildBounds(); auto childBounds = getTriangleTreeCellChildBounds();
auto insertOperator = [&](const AABox& childBound, std::shared_ptr<TriangleOctreeCell>& child) { auto insertOperator = [&](const AABox& childBound, std::shared_ptr<TriangleTreeCell>& child) {
// if the child AABox would contain the triangle... // if the child AABox would contain the triangle...
if (childBound.contains(triangle)) { if (childBound.contains(triangle)) {
// if the child cell doesn't yet exist, create it... // if the child cell doesn't yet exist, create it...
if (!child) { if (!child) {
child = std::make_shared<TriangleOctreeCell>(_allTriangles, childBound, _depth + 1); child = std::make_shared<TriangleTreeCell>(_allTriangles, childBound, _depth + 1);
} }
// insert the triangleIndex in the child cell // insert the triangleIndex in the child cell
@ -179,19 +179,19 @@ void TriangleSet::TriangleOctreeCell::insert(size_t triangleIndex) {
bool TriangleSet::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, bool TriangleSet::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance,
BoxFace& face, Triangle& triangle, bool precision, bool allowBackface) { BoxFace& face, Triangle& triangle, bool precision, bool allowBackface) {
if (!_isBalanced) { if (!_isBalanced) {
balanceOctree(); balanceTree();
} }
float localDistance = distance; float localDistance = distance;
int trianglesTouched = 0; int trianglesTouched = 0;
bool hit = _triangleOctree.findRayIntersection(origin, direction, localDistance, face, triangle, precision, trianglesTouched, allowBackface); bool hit = _triangleTree.findRayIntersection(origin, direction, localDistance, face, triangle, precision, trianglesTouched, allowBackface);
if (hit) { if (hit) {
distance = localDistance; distance = localDistance;
} }
#if WANT_DEBUGGING #if WANT_DEBUGGING
if (precision) { if (precision) {
qDebug() << "trianglesTouched :" << trianglesTouched << "out of:" << _triangleOctree._population << "_triangles.size:" << _triangles.size(); qDebug() << "trianglesTouched :" << trianglesTouched << "out of:" << _triangleTree._population << "_triangles.size:" << _triangles.size();
} }
#endif #endif
return hit; return hit;
@ -199,7 +199,7 @@ bool TriangleSet::findRayIntersection(const glm::vec3& origin, const glm::vec3&
// Determine of the given ray (origin/direction) in model space intersects with any triangles // Determine of the given ray (origin/direction) in model space intersects with any triangles
// in the set. If an intersection occurs, the distance and surface normal will be provided. // in the set. If an intersection occurs, the distance and surface normal will be provided.
bool TriangleSet::TriangleOctreeCell::findRayIntersectionInternal(const glm::vec3& origin, const glm::vec3& direction, bool TriangleSet::TriangleTreeCell::findRayIntersectionInternal(const glm::vec3& origin, const glm::vec3& direction,
float& distance, BoxFace& face, Triangle& triangle, bool precision, float& distance, BoxFace& face, Triangle& triangle, bool precision,
int& trianglesTouched, bool allowBackface) { int& trianglesTouched, bool allowBackface) {
bool intersectedSomething = false; bool intersectedSomething = false;
@ -232,7 +232,7 @@ bool TriangleSet::TriangleOctreeCell::findRayIntersectionInternal(const glm::vec
return intersectedSomething; return intersectedSomething;
} }
bool TriangleSet::TriangleOctreeCell::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, bool TriangleSet::TriangleTreeCell::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance,
BoxFace& face, Triangle& triangle, bool precision, int& trianglesTouched, BoxFace& face, Triangle& triangle, bool precision, int& trianglesTouched,
bool allowBackface) { bool allowBackface) {
if (_population < 1) { if (_population < 1) {
@ -261,7 +261,7 @@ bool TriangleSet::TriangleOctreeCell::findRayIntersection(const glm::vec3& origi
// if we're not yet at the max depth, then check our children // if we're not yet at the max depth, then check our children
if (_depth < MAX_DEPTH) { if (_depth < MAX_DEPTH) {
std::list<SortedTriangleCell> sortedTriangleCells; std::list<SortedTriangleCell> sortedTriangleCells;
auto sortingOperator = [&](std::shared_ptr<TriangleOctreeCell>& child) { auto sortingOperator = [&](std::shared_ptr<TriangleTreeCell>& child) {
if (child) { if (child) {
float priority = FLT_MAX; float priority = FLT_MAX;
if (child->getBounds().contains(origin)) { if (child->getBounds().contains(origin)) {
@ -328,25 +328,25 @@ bool TriangleSet::TriangleOctreeCell::findRayIntersection(const glm::vec3& origi
bool TriangleSet::findParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration, bool TriangleSet::findParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration,
float& parabolicDistance, BoxFace& face, Triangle& triangle, bool precision, bool allowBackface) { float& parabolicDistance, BoxFace& face, Triangle& triangle, bool precision, bool allowBackface) {
if (!_isBalanced) { if (!_isBalanced) {
balanceOctree(); balanceTree();
} }
float localDistance = parabolicDistance; float localDistance = parabolicDistance;
int trianglesTouched = 0; int trianglesTouched = 0;
bool hit = _triangleOctree.findParabolaIntersection(origin, velocity, acceleration, localDistance, face, triangle, precision, trianglesTouched, allowBackface); bool hit = _triangleTree.findParabolaIntersection(origin, velocity, acceleration, localDistance, face, triangle, precision, trianglesTouched, allowBackface);
if (hit) { if (hit) {
parabolicDistance = localDistance; parabolicDistance = localDistance;
} }
#if WANT_DEBUGGING #if WANT_DEBUGGING
if (precision) { if (precision) {
qDebug() << "trianglesTouched :" << trianglesTouched << "out of:" << _triangleOctree._population << "_triangles.size:" << _triangles.size(); qDebug() << "trianglesTouched :" << trianglesTouched << "out of:" << _triangleTree._population << "_triangles.size:" << _triangles.size();
} }
#endif #endif
return hit; return hit;
} }
bool TriangleSet::TriangleOctreeCell::findParabolaIntersectionInternal(const glm::vec3& origin, const glm::vec3& velocity, bool TriangleSet::TriangleTreeCell::findParabolaIntersectionInternal(const glm::vec3& origin, const glm::vec3& velocity,
const glm::vec3& acceleration, float& parabolicDistance, const glm::vec3& acceleration, float& parabolicDistance,
BoxFace& face, Triangle& triangle, bool precision, BoxFace& face, Triangle& triangle, bool precision,
int& trianglesTouched, bool allowBackface) { int& trianglesTouched, bool allowBackface) {
@ -380,7 +380,7 @@ bool TriangleSet::TriangleOctreeCell::findParabolaIntersectionInternal(const glm
return intersectedSomething; return intersectedSomething;
} }
bool TriangleSet::TriangleOctreeCell::findParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, bool TriangleSet::TriangleTreeCell::findParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity,
const glm::vec3& acceleration, float& parabolicDistance, const glm::vec3& acceleration, float& parabolicDistance,
BoxFace& face, Triangle& triangle, bool precision, BoxFace& face, Triangle& triangle, bool precision,
int& trianglesTouched, bool allowBackface) { int& trianglesTouched, bool allowBackface) {
@ -410,7 +410,7 @@ bool TriangleSet::TriangleOctreeCell::findParabolaIntersection(const glm::vec3&
// if we're not yet at the max depth, then check our children // if we're not yet at the max depth, then check our children
if (_depth < MAX_DEPTH) { if (_depth < MAX_DEPTH) {
std::list<SortedTriangleCell> sortedTriangleCells; std::list<SortedTriangleCell> sortedTriangleCells;
auto sortingOperator = [&](std::shared_ptr<TriangleOctreeCell>& child) { auto sortingOperator = [&](std::shared_ptr<TriangleTreeCell>& child) {
if (child) { if (child) {
float priority = FLT_MAX; float priority = FLT_MAX;
if (child->getBounds().contains(origin)) { if (child->getBounds().contains(origin)) {

View file

@ -19,10 +19,10 @@
class TriangleSet { class TriangleSet {
class TriangleOctreeCell { class TriangleTreeCell {
public: public:
TriangleOctreeCell(std::vector<Triangle>& allTriangles) : _allTriangles(allTriangles) {} TriangleTreeCell(std::vector<Triangle>& allTriangles) : _allTriangles(allTriangles) {}
TriangleOctreeCell(std::vector<Triangle>& allTriangles, const AABox& bounds, int depth); TriangleTreeCell(std::vector<Triangle>& allTriangles, const AABox& bounds, int depth);
void insert(size_t triangleIndex); void insert(size_t triangleIndex);
void reset(const AABox& bounds, int depth = 0); void reset(const AABox& bounds, int depth = 0);
@ -48,10 +48,10 @@ class TriangleSet {
float& parabolicDistance, BoxFace& face, Triangle& triangle, bool precision, int& trianglesTouched, float& parabolicDistance, BoxFace& face, Triangle& triangle, bool precision, int& trianglesTouched,
bool allowBackface = false); bool allowBackface = false);
std::pair<AABox, AABox> getTriangleOctreeCellChildBounds(); std::pair<AABox, AABox> getTriangleTreeCellChildBounds();
std::vector<Triangle>& _allTriangles; std::vector<Triangle>& _allTriangles;
std::pair<std::shared_ptr<TriangleOctreeCell>, std::shared_ptr<TriangleOctreeCell>> _children; std::pair<std::shared_ptr<TriangleTreeCell>, std::shared_ptr<TriangleTreeCell>> _children;
int _depth { 0 }; int _depth { 0 };
int _population { 0 }; int _population { 0 };
AABox _bounds; AABox _bounds;
@ -60,10 +60,10 @@ class TriangleSet {
friend class TriangleSet; friend class TriangleSet;
}; };
using SortedTriangleCell = std::pair<float, std::shared_ptr<TriangleOctreeCell>>; using SortedTriangleCell = std::pair<float, std::shared_ptr<TriangleTreeCell>>;
public: public:
TriangleSet() : _triangleOctree(_triangles) {} TriangleSet() : _triangleTree(_triangles) {}
void debugDump(); void debugDump();
@ -74,7 +74,7 @@ public:
bool findParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration, bool findParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration,
float& parabolicDistance, BoxFace& face, Triangle& triangle, bool precision, bool allowBackface = false); float& parabolicDistance, BoxFace& face, Triangle& triangle, bool precision, bool allowBackface = false);
void balanceOctree(); void balanceTree();
void reserve(size_t size) { _triangles.reserve(size); } // reserve space in the datastructure for size number of triangles void reserve(size_t size) { _triangles.reserve(size); } // reserve space in the datastructure for size number of triangles
size_t size() const { return _triangles.size(); } size_t size() const { return _triangles.size(); }
@ -89,6 +89,6 @@ public:
protected: protected:
bool _isBalanced { false }; bool _isBalanced { false };
std::vector<Triangle> _triangles; std::vector<Triangle> _triangles;
TriangleOctreeCell _triangleOctree; TriangleTreeCell _triangleTree;
AABox _bounds; AABox _bounds;
}; };