mirror of
https://github.com/overte-org/overte.git
synced 2025-04-29 22:42:42 +02:00
Merge branch 'master' of github.com:highfidelity/hifi into fix-imported-children-querybox
This commit is contained in:
commit
10f1143278
12 changed files with 507 additions and 52 deletions
|
@ -452,6 +452,13 @@ void EntityTree::deleteEntity(const EntityItemID& entityID, bool force, bool ign
|
||||||
|
|
||||||
// NOTE: callers must lock the tree before using this method
|
// NOTE: callers must lock the tree before using this method
|
||||||
DeleteEntityOperator theOperator(getThisPointer(), entityID);
|
DeleteEntityOperator theOperator(getThisPointer(), entityID);
|
||||||
|
|
||||||
|
existingEntity->forEachDescendant([&](SpatiallyNestablePointer descendant) {
|
||||||
|
auto descendantID = descendant->getID();
|
||||||
|
theOperator.addEntityIDToDeleteList(descendantID);
|
||||||
|
emit deletingEntity(descendantID);
|
||||||
|
});
|
||||||
|
|
||||||
recurseTreeWithOperator(&theOperator);
|
recurseTreeWithOperator(&theOperator);
|
||||||
processRemovedEntities(theOperator);
|
processRemovedEntities(theOperator);
|
||||||
_isDirty = true;
|
_isDirty = true;
|
||||||
|
|
|
@ -376,7 +376,7 @@ bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const g
|
||||||
glm::vec3 meshFrameOrigin = glm::vec3(worldToMeshMatrix * glm::vec4(origin, 1.0f));
|
glm::vec3 meshFrameOrigin = glm::vec3(worldToMeshMatrix * glm::vec4(origin, 1.0f));
|
||||||
glm::vec3 meshFrameDirection = glm::vec3(worldToMeshMatrix * glm::vec4(direction, 0.0f));
|
glm::vec3 meshFrameDirection = glm::vec3(worldToMeshMatrix * glm::vec4(direction, 0.0f));
|
||||||
|
|
||||||
for (const auto& triangleSet : _modelSpaceMeshTriangleSets) {
|
for (auto& triangleSet : _modelSpaceMeshTriangleSets) {
|
||||||
float triangleSetDistance = 0.0f;
|
float triangleSetDistance = 0.0f;
|
||||||
BoxFace triangleSetFace;
|
BoxFace triangleSetFace;
|
||||||
glm::vec3 triangleSetNormal;
|
glm::vec3 triangleSetNormal;
|
||||||
|
@ -1052,7 +1052,6 @@ void Model::updateRig(float deltaTime, glm::mat4 parentTransform) {
|
||||||
|
|
||||||
void Model::computeMeshPartLocalBounds() {
|
void Model::computeMeshPartLocalBounds() {
|
||||||
for (auto& part : _modelMeshRenderItems) {
|
for (auto& part : _modelMeshRenderItems) {
|
||||||
assert(part->_meshIndex < _modelMeshRenderItems.size());
|
|
||||||
const Model::MeshState& state = _meshStates.at(part->_meshIndex);
|
const Model::MeshState& state = _meshStates.at(part->_meshIndex);
|
||||||
part->computeAdjustedLocalBound(state.clusterMatrices);
|
part->computeAdjustedLocalBound(state.clusterMatrices);
|
||||||
}
|
}
|
||||||
|
|
|
@ -114,6 +114,10 @@ static bool isWithin(float value, float corner, float size) {
|
||||||
return value >= corner && value <= corner + size;
|
return value >= corner && value <= corner + size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool AABox::contains(const Triangle& triangle) const {
|
||||||
|
return contains(triangle.v0) && contains(triangle.v1) && contains(triangle.v2);
|
||||||
|
}
|
||||||
|
|
||||||
bool AABox::contains(const glm::vec3& point) const {
|
bool AABox::contains(const glm::vec3& point) const {
|
||||||
return isWithin(point.x, _corner.x, _scale.x) &&
|
return isWithin(point.x, _corner.x, _scale.x) &&
|
||||||
isWithin(point.y, _corner.y, _scale.y) &&
|
isWithin(point.y, _corner.y, _scale.y) &&
|
||||||
|
@ -622,3 +626,40 @@ void AABox::transform(const glm::mat4& matrix) {
|
||||||
_corner = newCenter - newDir;
|
_corner = newCenter - newDir;
|
||||||
_scale = newDir * 2.0f;
|
_scale = newDir * 2.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AABox AABox::getOctreeChild(OctreeChild child) const {
|
||||||
|
AABox result(*this); // self
|
||||||
|
switch (child) {
|
||||||
|
case topLeftNear:
|
||||||
|
result._corner.y += _scale.y / 2.0f;
|
||||||
|
break;
|
||||||
|
case topLeftFar:
|
||||||
|
result._corner.y += _scale.y / 2.0f;
|
||||||
|
result._corner.z += _scale.z / 2.0f;
|
||||||
|
break;
|
||||||
|
case topRightNear:
|
||||||
|
result._corner.y += _scale.y / 2.0f;
|
||||||
|
result._corner.x += _scale.x / 2.0f;
|
||||||
|
break;
|
||||||
|
case topRightFar:
|
||||||
|
result._corner.y += _scale.y / 2.0f;
|
||||||
|
result._corner.x += _scale.x / 2.0f;
|
||||||
|
result._corner.z += _scale.z / 2.0f;
|
||||||
|
break;
|
||||||
|
case bottomLeftNear:
|
||||||
|
// _corner = same as parent
|
||||||
|
break;
|
||||||
|
case bottomLeftFar:
|
||||||
|
result._corner.z += _scale.z / 2.0f;
|
||||||
|
break;
|
||||||
|
case bottomRightNear:
|
||||||
|
result._corner.x += _scale.x / 2.0f;
|
||||||
|
break;
|
||||||
|
case bottomRightFar:
|
||||||
|
result._corner.x += _scale.x / 2.0f;
|
||||||
|
result._corner.z += _scale.z / 2.0f;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
result._scale /= 2.0f; // everything is half the scale
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
|
||||||
#include "BoxBase.h"
|
#include "BoxBase.h"
|
||||||
|
#include "GeometryUtil.h"
|
||||||
#include "StreamUtils.h"
|
#include "StreamUtils.h"
|
||||||
|
|
||||||
class AACube;
|
class AACube;
|
||||||
|
@ -58,6 +59,7 @@ public:
|
||||||
const glm::vec3& getMinimumPoint() const { return _corner; }
|
const glm::vec3& getMinimumPoint() const { return _corner; }
|
||||||
glm::vec3 getMaximumPoint() const { return calcTopFarLeft(); }
|
glm::vec3 getMaximumPoint() const { return calcTopFarLeft(); }
|
||||||
|
|
||||||
|
bool contains(const Triangle& triangle) const;
|
||||||
bool contains(const glm::vec3& point) const;
|
bool contains(const glm::vec3& point) const;
|
||||||
bool contains(const AABox& otherBox) const;
|
bool contains(const AABox& otherBox) const;
|
||||||
bool touches(const AABox& otherBox) const;
|
bool touches(const AABox& otherBox) const;
|
||||||
|
@ -112,6 +114,19 @@ public:
|
||||||
|
|
||||||
void clear() { _corner = INFINITY_VECTOR; _scale = glm::vec3(0.0f); }
|
void clear() { _corner = INFINITY_VECTOR; _scale = glm::vec3(0.0f); }
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
topLeftNear,
|
||||||
|
topLeftFar,
|
||||||
|
topRightNear,
|
||||||
|
topRightFar,
|
||||||
|
bottomLeftNear,
|
||||||
|
bottomLeftFar,
|
||||||
|
bottomRightNear,
|
||||||
|
bottomRightFar
|
||||||
|
} OctreeChild;
|
||||||
|
|
||||||
|
AABox getOctreeChild(OctreeChild child) const; // returns the AABox of the would be octree child of this AABox
|
||||||
|
|
||||||
private:
|
private:
|
||||||
glm::vec3 getClosestPointOnFace(const glm::vec3& point, BoxFace face) const;
|
glm::vec3 getClosestPointOnFace(const glm::vec3& point, BoxFace face) const;
|
||||||
glm::vec3 getClosestPointOnFace(const glm::vec4& origin, const glm::vec4& direction, BoxFace face) const;
|
glm::vec3 getClosestPointOnFace(const glm::vec4& origin, const glm::vec4& direction, BoxFace face) const;
|
||||||
|
|
|
@ -12,9 +12,11 @@
|
||||||
#include "GLMHelpers.h"
|
#include "GLMHelpers.h"
|
||||||
#include "TriangleSet.h"
|
#include "TriangleSet.h"
|
||||||
|
|
||||||
void TriangleSet::insert(const Triangle& t) {
|
|
||||||
_triangles.push_back(t);
|
|
||||||
|
|
||||||
|
void TriangleSet::insert(const Triangle& t) {
|
||||||
|
_isBalanced = false;
|
||||||
|
|
||||||
|
_triangles.push_back(t);
|
||||||
_bounds += t.v0;
|
_bounds += t.v0;
|
||||||
_bounds += t.v1;
|
_bounds += t.v1;
|
||||||
_bounds += t.v2;
|
_bounds += t.v2;
|
||||||
|
@ -23,39 +25,31 @@ void TriangleSet::insert(const Triangle& t) {
|
||||||
void TriangleSet::clear() {
|
void TriangleSet::clear() {
|
||||||
_triangles.clear();
|
_triangles.clear();
|
||||||
_bounds.clear();
|
_bounds.clear();
|
||||||
|
_isBalanced = false;
|
||||||
|
|
||||||
|
_triangleOctree.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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.
|
|
||||||
bool TriangleSet::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
bool TriangleSet::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||||
float& distance, BoxFace& face, glm::vec3& surfaceNormal, bool precision) const {
|
float& distance, BoxFace& face, glm::vec3& surfaceNormal, bool precision) {
|
||||||
|
|
||||||
bool intersectedSomething = false;
|
// reset our distance to be the max possible, lower level tests will store best distance here
|
||||||
float boxDistance = std::numeric_limits<float>::max();
|
distance = std::numeric_limits<float>::max();
|
||||||
float bestDistance = std::numeric_limits<float>::max();
|
|
||||||
|
|
||||||
if (_bounds.findRayIntersection(origin, direction, boxDistance, face, surfaceNormal)) {
|
if (!_isBalanced) {
|
||||||
if (precision) {
|
balanceOctree();
|
||||||
for (const auto& triangle : _triangles) {
|
|
||||||
float thisTriangleDistance;
|
|
||||||
if (findRayTriangleIntersection(origin, direction, triangle, thisTriangleDistance)) {
|
|
||||||
if (thisTriangleDistance < bestDistance) {
|
|
||||||
bestDistance = thisTriangleDistance;
|
|
||||||
intersectedSomething = true;
|
|
||||||
surfaceNormal = triangle.getNormal();
|
|
||||||
distance = bestDistance;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
intersectedSomething = true;
|
|
||||||
distance = boxDistance;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return intersectedSomething;
|
int trianglesTouched = 0;
|
||||||
}
|
auto result = _triangleOctree.findRayIntersection(origin, direction, distance, face, surfaceNormal, precision, trianglesTouched);
|
||||||
|
|
||||||
|
#if WANT_DEBUGGING
|
||||||
|
if (precision) {
|
||||||
|
qDebug() << "trianglesTouched :" << trianglesTouched << "out of:" << _triangleOctree._population << "_triangles.size:" << _triangles.size();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
bool TriangleSet::convexHullContains(const glm::vec3& point) const {
|
bool TriangleSet::convexHullContains(const glm::vec3& point) const {
|
||||||
if (!_bounds.contains(point)) {
|
if (!_bounds.contains(point)) {
|
||||||
|
@ -74,3 +68,198 @@ bool TriangleSet::convexHullContains(const glm::vec3& point) const {
|
||||||
return insideMesh;
|
return insideMesh;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TriangleSet::debugDump() {
|
||||||
|
qDebug() << __FUNCTION__;
|
||||||
|
qDebug() << "bounds:" << getBounds();
|
||||||
|
qDebug() << "triangles:" << size() << "at top level....";
|
||||||
|
qDebug() << "----- _triangleOctree -----";
|
||||||
|
_triangleOctree.debugDump();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TriangleSet::balanceOctree() {
|
||||||
|
_triangleOctree.reset(_bounds, 0);
|
||||||
|
|
||||||
|
// insert all the triangles
|
||||||
|
for (size_t i = 0; i < _triangles.size(); i++) {
|
||||||
|
_triangleOctree.insert(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
_isBalanced = true;
|
||||||
|
|
||||||
|
#if WANT_DEBUGGING
|
||||||
|
debugDump();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
bool TriangleSet::TriangleOctreeCell::findRayIntersectionInternal(const glm::vec3& origin, const glm::vec3& direction,
|
||||||
|
float& distance, BoxFace& face, glm::vec3& surfaceNormal, bool precision, int& trianglesTouched) {
|
||||||
|
|
||||||
|
bool intersectedSomething = false;
|
||||||
|
float boxDistance = distance;
|
||||||
|
float bestDistance = distance;
|
||||||
|
|
||||||
|
if (_bounds.findRayIntersection(origin, direction, boxDistance, face, surfaceNormal)) {
|
||||||
|
|
||||||
|
// if our bounding box intersects at a distance greater than the current known
|
||||||
|
// best distance, than we can safely not check any of our triangles
|
||||||
|
if (boxDistance > bestDistance) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (precision) {
|
||||||
|
for (const auto& triangleIndex : _triangleIndices) {
|
||||||
|
const auto& triangle = _allTriangles[triangleIndex];
|
||||||
|
float thisTriangleDistance;
|
||||||
|
trianglesTouched++;
|
||||||
|
if (findRayTriangleIntersection(origin, direction, triangle, thisTriangleDistance)) {
|
||||||
|
if (thisTriangleDistance < bestDistance) {
|
||||||
|
bestDistance = thisTriangleDistance;
|
||||||
|
intersectedSomething = true;
|
||||||
|
surfaceNormal = triangle.getNormal();
|
||||||
|
distance = bestDistance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
intersectedSomething = true;
|
||||||
|
distance = boxDistance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return intersectedSomething;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const int MAX_DEPTH = 4; // for now
|
||||||
|
static const int MAX_CHILDREN = 8;
|
||||||
|
|
||||||
|
TriangleSet::TriangleOctreeCell::TriangleOctreeCell(std::vector<Triangle>& allTriangles, const AABox& bounds, int depth) :
|
||||||
|
_allTriangles(allTriangles)
|
||||||
|
{
|
||||||
|
reset(bounds, depth);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TriangleSet::TriangleOctreeCell::clear() {
|
||||||
|
_population = 0;
|
||||||
|
_triangleIndices.clear();
|
||||||
|
_bounds.clear();
|
||||||
|
_children.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TriangleSet::TriangleOctreeCell::reset(const AABox& bounds, int depth) {
|
||||||
|
clear();
|
||||||
|
_bounds = bounds;
|
||||||
|
_depth = depth;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TriangleSet::TriangleOctreeCell::debugDump() {
|
||||||
|
qDebug() << __FUNCTION__;
|
||||||
|
qDebug() << "bounds:" << getBounds();
|
||||||
|
qDebug() << "depth:" << _depth;
|
||||||
|
qDebug() << "population:" << _population << "this level or below"
|
||||||
|
<< " ---- triangleIndices:" << _triangleIndices.size() << "in this cell";
|
||||||
|
|
||||||
|
qDebug() << "child cells:" << _children.size();
|
||||||
|
if (_depth < MAX_DEPTH) {
|
||||||
|
int childNum = 0;
|
||||||
|
for (auto& child : _children) {
|
||||||
|
qDebug() << "child:" << childNum;
|
||||||
|
child.second.debugDump();
|
||||||
|
childNum++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TriangleSet::TriangleOctreeCell::insert(size_t triangleIndex) {
|
||||||
|
const Triangle& triangle = _allTriangles[triangleIndex];
|
||||||
|
_population++;
|
||||||
|
// if we're not yet at the max depth, then check which child the triangle fits in
|
||||||
|
if (_depth < MAX_DEPTH) {
|
||||||
|
|
||||||
|
for (int child = 0; child < MAX_CHILDREN; child++) {
|
||||||
|
AABox childBounds = getBounds().getOctreeChild((AABox::OctreeChild)child);
|
||||||
|
|
||||||
|
|
||||||
|
// if the child AABox would contain the triangle...
|
||||||
|
if (childBounds.contains(triangle)) {
|
||||||
|
// if the child cell doesn't yet exist, create it...
|
||||||
|
if (_children.find((AABox::OctreeChild)child) == _children.end()) {
|
||||||
|
_children.insert(
|
||||||
|
std::pair<AABox::OctreeChild, TriangleOctreeCell>
|
||||||
|
((AABox::OctreeChild)child, TriangleOctreeCell(_allTriangles, childBounds, _depth + 1)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// insert the triangleIndex in the child cell
|
||||||
|
_children.at((AABox::OctreeChild)child).insert(triangleIndex);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// either we're at max depth, or the triangle doesn't fit in one of our
|
||||||
|
// children and so we want to just record it here
|
||||||
|
_triangleIndices.push_back(triangleIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TriangleSet::TriangleOctreeCell::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||||
|
float& distance, BoxFace& face, glm::vec3& surfaceNormal, bool precision, int& trianglesTouched) {
|
||||||
|
|
||||||
|
if (_population < 1) {
|
||||||
|
return false; // no triangles below here, so we can't intersect
|
||||||
|
}
|
||||||
|
|
||||||
|
float bestLocalDistance = distance;
|
||||||
|
BoxFace bestLocalFace;
|
||||||
|
glm::vec3 bestLocalNormal;
|
||||||
|
bool intersects = false;
|
||||||
|
|
||||||
|
// if the ray intersects our bounding box, then continue
|
||||||
|
if (getBounds().findRayIntersection(origin, direction, bestLocalDistance, bestLocalFace, bestLocalNormal)) {
|
||||||
|
|
||||||
|
// if the intersection with our bounding box, is greater than the current best distance (the distance passed in)
|
||||||
|
// then we know that none of our triangles can represent a better intersection and we can return
|
||||||
|
|
||||||
|
if (bestLocalDistance > distance) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bestLocalDistance = distance;
|
||||||
|
|
||||||
|
float childDistance = distance;
|
||||||
|
BoxFace childFace;
|
||||||
|
glm::vec3 childNormal;
|
||||||
|
|
||||||
|
// if we're not yet at the max depth, then check which child the triangle fits in
|
||||||
|
if (_depth < MAX_DEPTH) {
|
||||||
|
for (auto& child : _children) {
|
||||||
|
// check each child, if there's an intersection, it will return some distance that we need
|
||||||
|
// to compare against the other results, because there might be multiple intersections and
|
||||||
|
// we will always choose the best (shortest) intersection
|
||||||
|
if (child.second.findRayIntersection(origin, direction, childDistance, childFace, childNormal, precision, trianglesTouched)) {
|
||||||
|
if (childDistance < bestLocalDistance) {
|
||||||
|
bestLocalDistance = childDistance;
|
||||||
|
bestLocalFace = childFace;
|
||||||
|
bestLocalNormal = childNormal;
|
||||||
|
intersects = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// also check our local triangle set
|
||||||
|
if (findRayIntersectionInternal(origin, direction, childDistance, childFace, childNormal, precision, trianglesTouched)) {
|
||||||
|
if (childDistance < bestLocalDistance) {
|
||||||
|
bestLocalDistance = childDistance;
|
||||||
|
bestLocalFace = childFace;
|
||||||
|
bestLocalNormal = childNormal;
|
||||||
|
intersects = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (intersects) {
|
||||||
|
distance = bestLocalDistance;
|
||||||
|
face = bestLocalFace;
|
||||||
|
surfaceNormal = bestLocalNormal;
|
||||||
|
}
|
||||||
|
return intersects;
|
||||||
|
}
|
||||||
|
|
|
@ -15,19 +15,64 @@
|
||||||
#include "GeometryUtil.h"
|
#include "GeometryUtil.h"
|
||||||
|
|
||||||
class TriangleSet {
|
class TriangleSet {
|
||||||
public:
|
|
||||||
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(); }
|
|
||||||
|
|
||||||
const Triangle& getTriangle(size_t t) const { return _triangles[t]; }
|
class TriangleOctreeCell {
|
||||||
|
public:
|
||||||
|
TriangleOctreeCell(std::vector<Triangle>& allTriangles) :
|
||||||
|
_allTriangles(allTriangles)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
void insert(size_t triangleIndex);
|
||||||
|
void reset(const AABox& bounds, int depth = 0);
|
||||||
|
void clear();
|
||||||
|
|
||||||
|
bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||||
|
float& distance, BoxFace& face, glm::vec3& surfaceNormal, bool precision, int& trianglesTouched);
|
||||||
|
|
||||||
|
const AABox& getBounds() const { return _bounds; }
|
||||||
|
|
||||||
|
void debugDump();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
TriangleOctreeCell(std::vector<Triangle>& allTriangles, const AABox& bounds, int depth);
|
||||||
|
|
||||||
|
// checks our internal list of triangles
|
||||||
|
bool findRayIntersectionInternal(const glm::vec3& origin, const glm::vec3& direction,
|
||||||
|
float& distance, BoxFace& face, glm::vec3& surfaceNormal, bool precision, int& trianglesTouched);
|
||||||
|
|
||||||
|
std::vector<Triangle>& _allTriangles;
|
||||||
|
std::map<AABox::OctreeChild, TriangleOctreeCell> _children;
|
||||||
|
int _depth{ 0 };
|
||||||
|
int _population{ 0 };
|
||||||
|
AABox _bounds;
|
||||||
|
std::vector<size_t> _triangleIndices;
|
||||||
|
|
||||||
|
friend class TriangleSet;
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
TriangleSet() :
|
||||||
|
_triangleOctree(_triangles)
|
||||||
|
{}
|
||||||
|
|
||||||
|
void debugDump();
|
||||||
|
|
||||||
void insert(const Triangle& t);
|
void insert(const Triangle& t);
|
||||||
|
|
||||||
|
bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||||
|
float& distance, BoxFace& face, glm::vec3& surfaceNormal, bool precision);
|
||||||
|
|
||||||
|
void balanceOctree();
|
||||||
|
|
||||||
|
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(); }
|
||||||
void clear();
|
void clear();
|
||||||
|
|
||||||
// Determine if the given ray (origin/direction) in model space intersects with any triangles in the set. If an
|
// Determine if 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.
|
// intersection occurs, the distance and surface normal will be provided.
|
||||||
bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
// note: this might side-effect internal structures
|
||||||
float& distance, BoxFace& face, glm::vec3& surfaceNormal, bool precision) const;
|
bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||||
|
float& distance, BoxFace& face, glm::vec3& surfaceNormal, bool precision, int& trianglesTouched);
|
||||||
|
|
||||||
// Determine if a point is "inside" all the triangles of a convex hull. It is the responsibility of the caller to
|
// Determine if a point is "inside" all the triangles of a convex hull. It is the responsibility of the caller to
|
||||||
// determine that the triangle set is indeed a convex hull. If the triangles added to this set are not in fact a
|
// determine that the triangle set is indeed a convex hull. If the triangles added to this set are not in fact a
|
||||||
|
@ -35,7 +80,10 @@ public:
|
||||||
bool convexHullContains(const glm::vec3& point) const;
|
bool convexHullContains(const glm::vec3& point) const;
|
||||||
const AABox& getBounds() const { return _bounds; }
|
const AABox& getBounds() const { return _bounds; }
|
||||||
|
|
||||||
private:
|
protected:
|
||||||
|
|
||||||
|
bool _isBalanced{ false };
|
||||||
|
TriangleOctreeCell _triangleOctree;
|
||||||
std::vector<Triangle> _triangles;
|
std::vector<Triangle> _triangles;
|
||||||
AABox _bounds;
|
AABox _bounds;
|
||||||
};
|
};
|
||||||
|
|
|
@ -40,7 +40,7 @@ void releaseOpenVrSystem();
|
||||||
|
|
||||||
|
|
||||||
static const char* CONTROLLER_MODEL_STRING = "vr_controller_05_wireless_b";
|
static const char* CONTROLLER_MODEL_STRING = "vr_controller_05_wireless_b";
|
||||||
const quint64 CALIBRATION_TIMELAPSE = 2 * USECS_PER_SECOND;
|
const quint64 CALIBRATION_TIMELAPSE = 1 * USECS_PER_SECOND;
|
||||||
|
|
||||||
static const char* MENU_PARENT = "Avatar";
|
static const char* MENU_PARENT = "Avatar";
|
||||||
static const char* MENU_NAME = "Vive Controllers";
|
static const char* MENU_NAME = "Vive Controllers";
|
||||||
|
|
131
scripts/developer/tests/performance/rayPickPerformance.js
Normal file
131
scripts/developer/tests/performance/rayPickPerformance.js
Normal file
|
@ -0,0 +1,131 @@
|
||||||
|
//
|
||||||
|
// rayPickingPerformance.js
|
||||||
|
// examples
|
||||||
|
//
|
||||||
|
// Created by Brad Hefta-Gaub on 5/13/2017
|
||||||
|
// Copyright 2017 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var MIN_RANGE = -3;
|
||||||
|
var MAX_RANGE = 3;
|
||||||
|
var RANGE_DELTA = 0.5;
|
||||||
|
var OUTER_LOOPS = 10;
|
||||||
|
|
||||||
|
// NOTE: These expected results depend completely on the model, and the range settings above
|
||||||
|
var EXPECTED_TESTS = 1385 * OUTER_LOOPS;
|
||||||
|
var EXPECTED_INTERSECTIONS = 1286 * OUTER_LOOPS;
|
||||||
|
|
||||||
|
|
||||||
|
var center = Vec3.sum(MyAvatar.position, Vec3.multiply(3, Quat.getFront(Camera.getOrientation())));
|
||||||
|
var model_url = "http://hifi-content.s3.amazonaws.com/caitlyn/production/Scansite/buddhaReduced.fbx";
|
||||||
|
|
||||||
|
var rayPickOverlays = Array();
|
||||||
|
|
||||||
|
var modelEntity = Entities.addEntity({
|
||||||
|
type: "Model",
|
||||||
|
modelURL: model_url,
|
||||||
|
dimensions: {
|
||||||
|
x: 0.671,
|
||||||
|
y: 1.21,
|
||||||
|
z: 0.938
|
||||||
|
},
|
||||||
|
position: center
|
||||||
|
});
|
||||||
|
|
||||||
|
function rayCastTest() {
|
||||||
|
var tests = 0;
|
||||||
|
var intersections = 0;
|
||||||
|
|
||||||
|
var testStart = Date.now();
|
||||||
|
for (var t = 0; t < OUTER_LOOPS; t++) {
|
||||||
|
print("beginning loop:" + t);
|
||||||
|
for (var x = MIN_RANGE; x < MAX_RANGE; x += RANGE_DELTA) {
|
||||||
|
for (var y = MIN_RANGE; y < MAX_RANGE; y += RANGE_DELTA) {
|
||||||
|
for (var z = MIN_RANGE; z < MAX_RANGE; z += RANGE_DELTA) {
|
||||||
|
if ((x <= -2 || x >= 2) ||
|
||||||
|
(y <= -2 || y >= 2) ||
|
||||||
|
(z <= -2 || z >= 2)) {
|
||||||
|
|
||||||
|
tests++;
|
||||||
|
|
||||||
|
var origin = { x: center.x + x,
|
||||||
|
y: center.y + y,
|
||||||
|
z: center.z + z };
|
||||||
|
var direction = Vec3.subtract(center, origin);
|
||||||
|
|
||||||
|
var pickRay = {
|
||||||
|
origin: origin,
|
||||||
|
direction: direction
|
||||||
|
};
|
||||||
|
|
||||||
|
var pickResults = Entities.findRayIntersection(pickRay, true);
|
||||||
|
|
||||||
|
var color;
|
||||||
|
var visible;
|
||||||
|
|
||||||
|
if (pickResults.intersects && pickResults.entityID == modelEntity) {
|
||||||
|
intersections++;
|
||||||
|
color = {
|
||||||
|
red: 0,
|
||||||
|
green: 255,
|
||||||
|
blue: 0
|
||||||
|
};
|
||||||
|
visible = false;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
/*
|
||||||
|
print("NO INTERSECTION?");
|
||||||
|
Vec3.print("origin:", origin);
|
||||||
|
Vec3.print("direction:", direction);
|
||||||
|
*/
|
||||||
|
|
||||||
|
color = {
|
||||||
|
red: 255,
|
||||||
|
green: 0,
|
||||||
|
blue: 0
|
||||||
|
};
|
||||||
|
visible = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
var overlayID = Overlays.addOverlay("line3d", {
|
||||||
|
color: color,
|
||||||
|
alpha: 1,
|
||||||
|
visible: visible,
|
||||||
|
lineWidth: 2,
|
||||||
|
start: origin,
|
||||||
|
end: Vec3.sum(origin,Vec3.multiply(5,direction))
|
||||||
|
});
|
||||||
|
|
||||||
|
rayPickOverlays.push(overlayID);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
print("ending loop:" + t);
|
||||||
|
}
|
||||||
|
var testEnd = Date.now();
|
||||||
|
var testElapsed = testEnd - testStart;
|
||||||
|
|
||||||
|
|
||||||
|
print("EXPECTED tests:" + EXPECTED_TESTS + " intersections:" + EXPECTED_INTERSECTIONS);
|
||||||
|
print("ACTUAL tests:" + tests + " intersections:" + intersections);
|
||||||
|
print("ELAPSED TIME:" + testElapsed + " ms");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function cleanup() {
|
||||||
|
Entities.deleteEntity(modelEntity);
|
||||||
|
rayPickOverlays.forEach(function(item){
|
||||||
|
Overlays.deleteOverlay(item);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Script.scriptEnding.connect(cleanup);
|
||||||
|
|
||||||
|
rayCastTest(); // run ray cast test immediately
|
|
@ -359,7 +359,7 @@ function showUploadingMessage(selectedID, destination) {
|
||||||
shareBarHelp.classList.add("uploading");
|
shareBarHelp.classList.add("uploading");
|
||||||
shareBarHelp.setAttribute("data-destination", destination);
|
shareBarHelp.setAttribute("data-destination", destination);
|
||||||
}
|
}
|
||||||
function hideUploadingMessageAndShare(selectedID, storyID) {
|
function hideUploadingMessageAndMaybeShare(selectedID, storyID) {
|
||||||
if (selectedID.id) {
|
if (selectedID.id) {
|
||||||
selectedID = selectedID.id; // sometimes (?), `containerID` is passed as an HTML object to these functions; we just want the ID
|
selectedID = selectedID.id; // sometimes (?), `containerID` is passed as an HTML object to these functions; we just want the ID
|
||||||
}
|
}
|
||||||
|
@ -382,21 +382,28 @@ function hideUploadingMessageAndShare(selectedID, storyID) {
|
||||||
var facebookButton = document.getElementById(selectedID + "facebookButton");
|
var facebookButton = document.getElementById(selectedID + "facebookButton");
|
||||||
window.open(facebookButton.getAttribute("href"), "_blank");
|
window.open(facebookButton.getAttribute("href"), "_blank");
|
||||||
shareBarHelp.innerHTML = facebookShareText;
|
shareBarHelp.innerHTML = facebookShareText;
|
||||||
|
// This emitWebEvent() call isn't necessary in the "hifi" and "blast" cases
|
||||||
|
// because the "removeFromStoryIDsToMaybeDelete()" call happens
|
||||||
|
// in snapshot.js when sharing with that method.
|
||||||
|
EventBridge.emitWebEvent(JSON.stringify({
|
||||||
|
type: "snapshot",
|
||||||
|
action: "removeFromStoryIDsToMaybeDelete",
|
||||||
|
story_id: storyID
|
||||||
|
}));
|
||||||
break;
|
break;
|
||||||
case 'twitter':
|
case 'twitter':
|
||||||
var twitterButton = document.getElementById(selectedID + "twitterButton");
|
var twitterButton = document.getElementById(selectedID + "twitterButton");
|
||||||
window.open(twitterButton.getAttribute("href"), "_blank");
|
window.open(twitterButton.getAttribute("href"), "_blank");
|
||||||
shareBarHelp.innerHTML = twitterShareText;
|
shareBarHelp.innerHTML = twitterShareText;
|
||||||
|
EventBridge.emitWebEvent(JSON.stringify({
|
||||||
|
type: "snapshot",
|
||||||
|
action: "removeFromStoryIDsToMaybeDelete",
|
||||||
|
story_id: storyID
|
||||||
|
}));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
shareBarHelp.setAttribute("data-destination", "");
|
shareBarHelp.setAttribute("data-destination", "");
|
||||||
|
|
||||||
EventBridge.emitWebEvent(JSON.stringify({
|
|
||||||
type: "snapshot",
|
|
||||||
action: "removeFromStoryIDsToMaybeDelete",
|
|
||||||
story_id: storyID
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function updateShareInfo(containerID, storyID) {
|
function updateShareInfo(containerID, storyID) {
|
||||||
|
@ -417,7 +424,7 @@ function updateShareInfo(containerID, storyID) {
|
||||||
twitterButton.setAttribute("target", "_blank");
|
twitterButton.setAttribute("target", "_blank");
|
||||||
twitterButton.setAttribute("href", 'https://twitter.com/intent/tweet?text=I%20just%20took%20a%20snapshot!&url=' + shareURL + '&via=highfidelityinc&hashtags=VR,HiFi');
|
twitterButton.setAttribute("href", 'https://twitter.com/intent/tweet?text=I%20just%20took%20a%20snapshot!&url=' + shareURL + '&via=highfidelityinc&hashtags=VR,HiFi');
|
||||||
|
|
||||||
hideUploadingMessageAndShare(containerID, storyID);
|
hideUploadingMessageAndMaybeShare(containerID, storyID);
|
||||||
}
|
}
|
||||||
function blastToConnections(selectedID, isGif) {
|
function blastToConnections(selectedID, isGif) {
|
||||||
if (selectedID.id) {
|
if (selectedID.id) {
|
||||||
|
@ -552,6 +559,12 @@ function shareButtonClicked(destination, selectedID) {
|
||||||
|
|
||||||
if (!storyID) {
|
if (!storyID) {
|
||||||
showUploadingMessage(selectedID, destination);
|
showUploadingMessage(selectedID, destination);
|
||||||
|
} else {
|
||||||
|
EventBridge.emitWebEvent(JSON.stringify({
|
||||||
|
type: "snapshot",
|
||||||
|
action: "removeFromStoryIDsToMaybeDelete",
|
||||||
|
story_id: storyID
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1020,7 +1020,7 @@ function loaded() {
|
||||||
|
|
||||||
elTextText.value = properties.text;
|
elTextText.value = properties.text;
|
||||||
elTextLineHeight.value = properties.lineHeight.toFixed(4);
|
elTextLineHeight.value = properties.lineHeight.toFixed(4);
|
||||||
elTextFaceCamera = properties.faceCamera;
|
elTextFaceCamera.checked = properties.faceCamera;
|
||||||
elTextTextColor.style.backgroundColor = "rgb(" + properties.textColor.red + "," + properties.textColor.green + "," + properties.textColor.blue + ")";
|
elTextTextColor.style.backgroundColor = "rgb(" + properties.textColor.red + "," + properties.textColor.green + "," + properties.textColor.blue + ")";
|
||||||
elTextTextColorRed.value = properties.textColor.red;
|
elTextTextColorRed.value = properties.textColor.red;
|
||||||
elTextTextColorGreen.value = properties.textColor.green;
|
elTextTextColorGreen.value = properties.textColor.green;
|
||||||
|
|
|
@ -37,6 +37,12 @@
|
||||||
Window.alert(message);
|
Window.alert(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function logDetails() {
|
||||||
|
return {
|
||||||
|
current_domain: location.placename
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
RecordingIndicator = (function () {
|
RecordingIndicator = (function () {
|
||||||
// Displays "recording" overlay.
|
// Displays "recording" overlay.
|
||||||
|
|
||||||
|
@ -181,7 +187,7 @@
|
||||||
|
|
||||||
recordingState = IDLE;
|
recordingState = IDLE;
|
||||||
log("Finish recording");
|
log("Finish recording");
|
||||||
UserActivityLogger.logAction("record_finish_recording");
|
UserActivityLogger.logAction("record_finish_recording", logDetails());
|
||||||
playSound(finishRecordingSound);
|
playSound(finishRecordingSound);
|
||||||
Recording.stopRecording();
|
Recording.stopRecording();
|
||||||
RecordingIndicator.hide();
|
RecordingIndicator.hide();
|
||||||
|
@ -519,7 +525,7 @@
|
||||||
value: Player.numberOfPlayers()
|
value: Player.numberOfPlayers()
|
||||||
}));
|
}));
|
||||||
updateRecordingStatus(!Recorder.isIdle());
|
updateRecordingStatus(!Recorder.isIdle());
|
||||||
UserActivityLogger.logAction("record_open_dialog");
|
UserActivityLogger.logAction("record_open_dialog", logDetails());
|
||||||
break;
|
break;
|
||||||
case STOP_PLAYING_RECORDING_ACTION:
|
case STOP_PLAYING_RECORDING_ACTION:
|
||||||
// Stop the specified player.
|
// Stop the specified player.
|
||||||
|
@ -530,7 +536,7 @@
|
||||||
recording = Window.browseAssets("Select Recording to Play", "recordings", "*.hfr");
|
recording = Window.browseAssets("Select Recording to Play", "recordings", "*.hfr");
|
||||||
if (recording) {
|
if (recording) {
|
||||||
log("Load recording " + recording);
|
log("Load recording " + recording);
|
||||||
UserActivityLogger.logAction("record_load_recording");
|
UserActivityLogger.logAction("record_load_recording", logDetails());
|
||||||
Player.playRecording("atp:" + recording, MyAvatar.position, MyAvatar.orientation);
|
Player.playRecording("atp:" + recording, MyAvatar.position, MyAvatar.orientation);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -660,7 +666,7 @@
|
||||||
isConnected = Window.location.isConnected;
|
isConnected = Window.location.isConnected;
|
||||||
Script.update.connect(onUpdate);
|
Script.update.connect(onUpdate);
|
||||||
|
|
||||||
UserActivityLogger.logAction("record_run_script");
|
UserActivityLogger.logAction("record_run_script", logDetails());
|
||||||
}
|
}
|
||||||
|
|
||||||
function tearDown() {
|
function tearDown() {
|
||||||
|
|
|
@ -51,6 +51,11 @@ function openLoginWindow() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function removeFromStoryIDsToMaybeDelete(story_id) {
|
||||||
|
storyIDsToMaybeDelete.splice(storyIDsToMaybeDelete.indexOf(story_id), 1);
|
||||||
|
print('storyIDsToMaybeDelete[] now:', JSON.stringify(storyIDsToMaybeDelete));
|
||||||
|
}
|
||||||
|
|
||||||
function onMessage(message) {
|
function onMessage(message) {
|
||||||
// Receives message from the html dialog via the qwebchannel EventBridge. This is complicated by the following:
|
// Receives message from the html dialog via the qwebchannel EventBridge. This is complicated by the following:
|
||||||
// 1. Although we can send POJOs, we cannot receive a toplevel object. (Arrays of POJOs are fine, though.)
|
// 1. Although we can send POJOs, we cannot receive a toplevel object. (Arrays of POJOs are fine, though.)
|
||||||
|
@ -191,6 +196,7 @@ function onMessage(message) {
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
print("SUCCESS uploading announcement story! Story ID:", response.user_story.id);
|
print("SUCCESS uploading announcement story! Story ID:", response.user_story.id);
|
||||||
|
removeFromStoryIDsToMaybeDelete(message.story_id); // Don't delete original "for_url" story
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -230,13 +236,13 @@ function onMessage(message) {
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
print("SUCCESS changing audience" + (message.isAnnouncement ? " and posting announcement!" : "!"));
|
print("SUCCESS changing audience" + (message.isAnnouncement ? " and posting announcement!" : "!"));
|
||||||
|
removeFromStoryIDsToMaybeDelete(message.story_id);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'removeFromStoryIDsToMaybeDelete':
|
case 'removeFromStoryIDsToMaybeDelete':
|
||||||
storyIDsToMaybeDelete.splice(storyIDsToMaybeDelete.indexOf(message.story_id), 1);
|
removeFromStoryIDsToMaybeDelete(message.story_id);
|
||||||
print('storyIDsToMaybeDelete[] now:', JSON.stringify(storyIDsToMaybeDelete));
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
print('Unknown message action received by snapshot.js!');
|
print('Unknown message action received by snapshot.js!');
|
||||||
|
|
Loading…
Reference in a new issue