diff --git a/assignment-client/src/octree/OctreeQueryNode.cpp b/assignment-client/src/octree/OctreeQueryNode.cpp index 78a049edd6..55eddf9e13 100644 --- a/assignment-client/src/octree/OctreeQueryNode.cpp +++ b/assignment-client/src/octree/OctreeQueryNode.cpp @@ -153,7 +153,7 @@ bool OctreeQueryNode::updateCurrentViewFrustum() { newestViewFrustum.setPosition(getCameraPosition()); newestViewFrustum.setOrientation(getCameraOrientation()); - newestViewFrustum.setKeyholeRadius(getKeyholeRadius()); + newestViewFrustum.setCenterRadius(getCameraCenterRadius()); // Also make sure it's got the correct lens details from the camera float originalFOV = getCameraFov(); diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 5bb666bf33..f286085092 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3387,7 +3387,7 @@ void Application::queryOctree(NodeType_t serverType, PacketType packetType, Node _octreeQuery.setCameraNearClip(_viewFrustum.getNearClip()); _octreeQuery.setCameraFarClip(_viewFrustum.getFarClip()); _octreeQuery.setCameraEyeOffsetPosition(glm::vec3()); - _octreeQuery.setKeyholeRadius(_viewFrustum.getKeyholeRadius()); + _octreeQuery.setCameraCenterRadius(_viewFrustum.getCenterRadius()); auto lodManager = DependencyManager::get(); _octreeQuery.setOctreeSizeScale(lodManager->getOctreeSizeScale()); _octreeQuery.setBoundaryLevelAdjust(lodManager->getBoundaryLevelAdjust()); @@ -3423,9 +3423,7 @@ void Application::queryOctree(NodeType_t serverType, PacketType packetType, Node rootDetails.y * TREE_SCALE, rootDetails.z * TREE_SCALE) - glm::vec3(HALF_TREE_SCALE), rootDetails.s * TREE_SCALE); - ViewFrustum::location serverFrustumLocation = _viewFrustum.computeCubeViewLocation(serverBounds); - - if (serverFrustumLocation != ViewFrustum::OUTSIDE) { + if (_viewFrustum.cubeIntersectsKeyhole(serverBounds)) { inViewServers++; } } @@ -3491,12 +3489,7 @@ void Application::queryOctree(NodeType_t serverType, PacketType packetType, Node rootDetails.s * TREE_SCALE); - ViewFrustum::location serverFrustumLocation = _viewFrustum.computeCubeViewLocation(serverBounds); - if (serverFrustumLocation != ViewFrustum::OUTSIDE) { - inView = true; - } else { - inView = false; - } + inView = _viewFrustum.cubeIntersectsKeyhole(serverBounds); } else { if (wantExtraDebugging) { qCDebug(interfaceapp) << "Jurisdiction without RootCode for node " << *node << ". That's unusual!"; diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index ad04e8da3a..83351d5188 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -188,15 +188,14 @@ void Avatar::simulate(float deltaTime) { // simple frustum check float boundingRadius = getBoundingRadius(); - bool inViewFrustum = qApp->getViewFrustum()->computeSphereViewLocation(getPosition(), boundingRadius) != - ViewFrustum::OUTSIDE; + bool inView = qApp->getViewFrustum()->sphereIntersectsFrustum(getPosition(), boundingRadius); { PerformanceTimer perfTimer("hand"); getHand()->simulate(deltaTime, false); } - if (_shouldAnimate && !_shouldSkipRender && inViewFrustum) { + if (_shouldAnimate && !_shouldSkipRender && inView) { { PerformanceTimer perfTimer("skeleton"); _skeletonModel.getRig()->copyJointsFromJointData(_jointData); @@ -401,7 +400,7 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) { frustum = qApp->getDisplayViewFrustum(); } - if (frustum->computeSphereViewLocation(getPosition(), boundingRadius) == ViewFrustum::OUTSIDE) { + if (frustum->sphereIntersectsFrustum(getPosition(), boundingRadius)) { endRender(); return; } @@ -517,7 +516,7 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) { auto& frustum = *renderArgs->_viewFrustum; auto textPosition = getDisplayNamePosition(); - if (frustum.computePointFrustumLocation(textPosition) == ViewFrustum::INSIDE) { + if (frustum.pointIntersectsFrustum(textPosition)) { renderDisplayName(batch, frustum, textPosition); } } @@ -670,10 +669,10 @@ glm::vec3 Avatar::getDisplayNamePosition() const { return namePosition; } -Transform Avatar::calculateDisplayNameTransform(const ViewFrustum& frustum, const glm::vec3& textPosition) const { - Q_ASSERT_X(frustum.computePointFrustumLocation(textPosition) == ViewFrustum::INSIDE, +Transform Avatar::calculateDisplayNameTransform(const ViewFrustum& view, const glm::vec3& textPosition) const { + Q_ASSERT_X(view.pointIntersectsFrustum(textPosition), "Avatar::calculateDisplayNameTransform", "Text not in viewfrustum."); - glm::vec3 toFrustum = frustum.getPosition() - textPosition; + glm::vec3 toFrustum = view.getPosition() - textPosition; // Compute orientation // If x and z are 0, atan(x, z) adais undefined, so default to 0 degrees @@ -695,7 +694,7 @@ Transform Avatar::calculateDisplayNameTransform(const ViewFrustum& frustum, cons return result; } -void Avatar::renderDisplayName(gpu::Batch& batch, const ViewFrustum& frustum, const glm::vec3& textPosition) const { +void Avatar::renderDisplayName(gpu::Batch& batch, const ViewFrustum& view, const glm::vec3& textPosition) const { PROFILE_RANGE_BATCH(batch, __FUNCTION__); bool shouldShowReceiveStats = DependencyManager::get()->shouldShowReceiveStats() && !isMyAvatar(); @@ -703,7 +702,7 @@ void Avatar::renderDisplayName(gpu::Batch& batch, const ViewFrustum& frustum, co // If we have nothing to draw, or it's totally transparent, or it's too close or behind the camera, return static const float CLIP_DISTANCE = 0.2f; if ((_displayName.isEmpty() && !shouldShowReceiveStats) || _displayNameAlpha == 0.0f - || (glm::dot(frustum.getDirection(), getDisplayNamePosition() - frustum.getPosition()) <= CLIP_DISTANCE)) { + || (glm::dot(view.getDirection(), getDisplayNamePosition() - view.getPosition()) <= CLIP_DISTANCE)) { return; } auto renderer = textRenderer(DISPLAYNAME); @@ -744,7 +743,7 @@ void Avatar::renderDisplayName(gpu::Batch& batch, const ViewFrustum& frustum, co (_displayNameAlpha / DISPLAYNAME_ALPHA) * DISPLAYNAME_BACKGROUND_ALPHA); // Compute display name transform - auto textTransform = calculateDisplayNameTransform(frustum, textPosition); + auto textTransform = calculateDisplayNameTransform(view, textPosition); // Test on extent above insures abs(height) > 0.0f textTransform.postScale(1.0f / height); batch.setModelTransform(textTransform); diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index 766a4734eb..cd9fc86e1e 100644 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -231,8 +231,8 @@ protected: float getPelvisFloatingHeight() const; glm::vec3 getDisplayNamePosition() const; - Transform calculateDisplayNameTransform(const ViewFrustum& frustum, const glm::vec3& textPosition) const; - void renderDisplayName(gpu::Batch& batch, const ViewFrustum& frustum, const glm::vec3& textPosition) const; + Transform calculateDisplayNameTransform(const ViewFrustum& view, const glm::vec3& textPosition) const; + void renderDisplayName(gpu::Batch& batch, const ViewFrustum& view, const glm::vec3& textPosition) const; virtual void renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, float glowLevel = 0.0f); virtual bool shouldRenderHead(const RenderArgs* renderArgs) const; virtual void fixupModelsInScene(); diff --git a/libraries/entities/src/EntityTreeElement.cpp b/libraries/entities/src/EntityTreeElement.cpp index 8ff93bf808..15ff531b06 100644 --- a/libraries/entities/src/EntityTreeElement.cpp +++ b/libraries/entities/src/EntityTreeElement.cpp @@ -304,15 +304,13 @@ OctreeElement::AppendState EntityTreeElement::appendElementData(OctreePacketData // frustum culling on rendering. bool success; AACube entityCube = entity->getQueryAACube(success); - if (!success || params.viewFrustum->computeCubeViewLocation(entityCube) == ViewFrustum::OUTSIDE) { + if (!success || !params.viewFrustum->cubeIntersectsKeyhole(entityCube)) { includeThisEntity = false; // out of view, don't include it - } - - // Now check the size of the entity, it's possible that a "too small to see" entity is included in a - // larger octree cell because of its position (for example if it crosses the boundary of a cell it - // pops to the next higher cell. So we want to check to see that the entity is large enough to be seen - // before we consider including it. - if (includeThisEntity) { + } else { + // Check the size of the entity, it's possible that a "too small to see" entity is included in a + // larger octree cell because of its position (for example if it crosses the boundary of a cell it + // pops to the next higher cell. So we want to check to see that the entity is large enough to be seen + // before we consider including it. success = true; // we can't cull a parent-entity by its dimensions because the child may be larger. we need to // avoid sending details about a child but not the parent. the parent's queryAACube should have diff --git a/libraries/octree/src/Octree.cpp b/libraries/octree/src/Octree.cpp index d78f21d964..8959bd4aa7 100644 --- a/libraries/octree/src/Octree.cpp +++ b/libraries/octree/src/Octree.cpp @@ -937,7 +937,7 @@ int Octree::encodeTreeBitstream(OctreeElementPointer element, params.stats->traversed(element); } - ViewFrustum::location parentLocationThisView = ViewFrustum::INTERSECT; // assume parent is in view, but not fully + ViewFrustum::intersection parentLocationThisView = ViewFrustum::INTERSECT; // assume parent is in view, but not fully int childBytesWritten = encodeTreeBitstreamRecursion(element, packetData, bag, params, currentEncodeLevel, parentLocationThisView); @@ -974,7 +974,7 @@ int Octree::encodeTreeBitstream(OctreeElementPointer element, int Octree::encodeTreeBitstreamRecursion(OctreeElementPointer element, OctreePacketData* packetData, OctreeElementBag& bag, EncodeBitstreamParams& params, int& currentEncodeLevel, - const ViewFrustum::location& parentLocationThisView) const { + const ViewFrustum::intersection& parentLocationThisView) const { const bool wantDebug = false; @@ -1013,7 +1013,7 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElementPointer element, } } - ViewFrustum::location nodeLocationThisView = ViewFrustum::INSIDE; // assume we're inside + ViewFrustum::intersection nodeLocationThisView = ViewFrustum::INSIDE; // assume we're inside // caller can pass NULL as viewFrustum if they want everything if (params.viewFrustum) { @@ -1034,7 +1034,7 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElementPointer element, // if we are INSIDE, INTERSECT, or OUTSIDE if (parentLocationThisView != ViewFrustum::INSIDE) { assert(parentLocationThisView != ViewFrustum::OUTSIDE); // we shouldn't be here if our parent was OUTSIDE! - nodeLocationThisView = element->computeViewLocation(*params.viewFrustum); + nodeLocationThisView = element->computeViewIntersection(*params.viewFrustum); } // If we're at a element that is out of view, then we can return, because no nodes below us will be in view! @@ -1053,7 +1053,7 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElementPointer element, bool wasInView = false; if (params.deltaViewFrustum && params.lastViewFrustum) { - ViewFrustum::location location = element->computeViewLocation(*params.lastViewFrustum); + ViewFrustum::intersection location = element->computeViewIntersection(*params.lastViewFrustum); // If we're a leaf, then either intersect or inside is considered "formerly in view" if (element->isLeaf()) { @@ -1237,7 +1237,7 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElementPointer element, bool childWasInView = false; if (childElement && params.deltaViewFrustum && params.lastViewFrustum) { - ViewFrustum::location location = childElement->computeViewLocation(*params.lastViewFrustum); + ViewFrustum::intersection location = childElement->computeViewIntersection(*params.lastViewFrustum); // If we're a leaf, then either intersect or inside is considered "formerly in view" if (childElement->isLeaf()) { diff --git a/libraries/octree/src/Octree.h b/libraries/octree/src/Octree.h index 0939ae37f6..81a721c54e 100644 --- a/libraries/octree/src/Octree.h +++ b/libraries/octree/src/Octree.h @@ -367,7 +367,7 @@ protected: int encodeTreeBitstreamRecursion(OctreeElementPointer element, OctreePacketData* packetData, OctreeElementBag& bag, EncodeBitstreamParams& params, int& currentEncodeLevel, - const ViewFrustum::location& parentLocationThisView) const; + const ViewFrustum::intersection& parentLocationThisView) const; static bool countOctreeElementsOperation(OctreeElementPointer element, void* extraData); diff --git a/libraries/octree/src/OctreeElement.cpp b/libraries/octree/src/OctreeElement.cpp index 342a0abf01..db0948b8b6 100644 --- a/libraries/octree/src/OctreeElement.cpp +++ b/libraries/octree/src/OctreeElement.cpp @@ -458,8 +458,8 @@ float OctreeElement::getEnclosingRadius() const { return getScale() * sqrtf(3.0f) / 2.0f; } -ViewFrustum::location OctreeElement::computeViewLocation(const ViewFrustum& viewFrustum) const { - return viewFrustum.computeCubeViewLocation(_cube); +ViewFrustum::intersection OctreeElement::computeViewIntersection(const ViewFrustum& viewFrustum) const { + return viewFrustum.calculateCubeKeyholeIntersection(_cube); } // There are two types of nodes for which we want to "render" diff --git a/libraries/octree/src/OctreeElement.h b/libraries/octree/src/OctreeElement.h index 3f58175337..398f1827b7 100644 --- a/libraries/octree/src/OctreeElement.h +++ b/libraries/octree/src/OctreeElement.h @@ -134,8 +134,8 @@ public: int getLevel() const { return numberOfThreeBitSectionsInCode(getOctalCode()) + 1; } float getEnclosingRadius() const; - bool isInView(const ViewFrustum& viewFrustum) const { return computeViewLocation(viewFrustum) != ViewFrustum::OUTSIDE; } - ViewFrustum::location computeViewLocation(const ViewFrustum& viewFrustum) const; + bool isInView(const ViewFrustum& viewFrustum) const { return computeViewIntersection(viewFrustum) != ViewFrustum::OUTSIDE; } + ViewFrustum::intersection computeViewIntersection(const ViewFrustum& viewFrustum) const; float distanceToCamera(const ViewFrustum& viewFrustum) const; float furthestDistanceToCamera(const ViewFrustum& viewFrustum) const; diff --git a/libraries/octree/src/OctreeHeadlessViewer.cpp b/libraries/octree/src/OctreeHeadlessViewer.cpp index 42bbfb025e..33c12b1fe5 100644 --- a/libraries/octree/src/OctreeHeadlessViewer.cpp +++ b/libraries/octree/src/OctreeHeadlessViewer.cpp @@ -52,7 +52,7 @@ void OctreeHeadlessViewer::queryOctree() { _octreeQuery.setCameraNearClip(_viewFrustum.getNearClip()); _octreeQuery.setCameraFarClip(_viewFrustum.getFarClip()); _octreeQuery.setCameraEyeOffsetPosition(glm::vec3()); - _octreeQuery.setKeyholeRadius(_viewFrustum.getKeyholeRadius()); + _octreeQuery.setCameraCenterRadius(_viewFrustum.getCenterRadius()); _octreeQuery.setOctreeSizeScale(_voxelSizeScale); _octreeQuery.setBoundaryLevelAdjust(_boundaryLevelAdjust); @@ -91,9 +91,7 @@ void OctreeHeadlessViewer::queryOctree() { if (foundRootDetails) { AACube serverBounds(glm::vec3(rootDetails.x, rootDetails.y, rootDetails.z), rootDetails.s); - ViewFrustum::location serverFrustumLocation = _viewFrustum.computeCubeViewLocation(serverBounds); - - if (serverFrustumLocation != ViewFrustum::OUTSIDE) { + if ((bool)(_viewFrustum.calculateCubeKeyholeIntersection(serverBounds))) { inViewServers++; } } @@ -164,13 +162,7 @@ void OctreeHeadlessViewer::queryOctree() { if (foundRootDetails) { AACube serverBounds(glm::vec3(rootDetails.x, rootDetails.y, rootDetails.z), rootDetails.s); - - ViewFrustum::location serverFrustumLocation = _viewFrustum.computeCubeViewLocation(serverBounds); - if (serverFrustumLocation != ViewFrustum::OUTSIDE) { - inView = true; - } else { - inView = false; - } + inView = (bool)(_viewFrustum.calculateCubeKeyholeIntersection(serverBounds)); } if (inView) { diff --git a/libraries/octree/src/OctreeHeadlessViewer.h b/libraries/octree/src/OctreeHeadlessViewer.h index bdfebd82f6..4efdb8546e 100644 --- a/libraries/octree/src/OctreeHeadlessViewer.h +++ b/libraries/octree/src/OctreeHeadlessViewer.h @@ -46,7 +46,7 @@ public slots: // setters for camera attributes void setPosition(const glm::vec3& position) { _viewFrustum.setPosition(position); } void setOrientation(const glm::quat& orientation) { _viewFrustum.setOrientation(orientation); } - void setKeyholeRadius(float keyholdRadius) { _viewFrustum.setKeyholeRadius(keyholdRadius); } + void setCenterRadius(float radius) { _viewFrustum.setCenterRadius(radius); } // setters for LOD and PPS void setVoxelSizeScale(float sizeScale) { _voxelSizeScale = sizeScale; } diff --git a/libraries/octree/src/OctreeQuery.cpp b/libraries/octree/src/OctreeQuery.cpp index 0e7f565c00..a69d88c693 100644 --- a/libraries/octree/src/OctreeQuery.cpp +++ b/libraries/octree/src/OctreeQuery.cpp @@ -64,8 +64,8 @@ int OctreeQuery::getBroadcastData(unsigned char* destinationBuffer) { memcpy(destinationBuffer, &_boundaryLevelAdjust, sizeof(_boundaryLevelAdjust)); destinationBuffer += sizeof(_boundaryLevelAdjust); - memcpy(destinationBuffer, &_keyholeRadius, sizeof(_keyholeRadius)); - destinationBuffer += sizeof(_keyholeRadius); + memcpy(destinationBuffer, &_cameraCenterRadius, sizeof(_cameraCenterRadius)); + destinationBuffer += sizeof(_cameraCenterRadius); return destinationBuffer - bufferStart; } @@ -109,9 +109,9 @@ int OctreeQuery::parseData(ReceivedMessage& message) { auto bytesRead = sourceBuffer - startPosition; auto bytesLeft = message.getSize() - bytesRead; - if (bytesLeft >= (int)sizeof(_keyholeRadius)) { - memcpy(&_keyholeRadius, sourceBuffer, sizeof(_keyholeRadius)); - sourceBuffer += sizeof(_keyholeRadius); + if (bytesLeft >= (int)sizeof(_cameraCenterRadius)) { + memcpy(&_cameraCenterRadius, sourceBuffer, sizeof(_cameraCenterRadius)); + sourceBuffer += sizeof(_cameraCenterRadius); } return sourceBuffer - startPosition; } diff --git a/libraries/octree/src/OctreeQuery.h b/libraries/octree/src/OctreeQuery.h index 7e157c4252..e446e1abc7 100644 --- a/libraries/octree/src/OctreeQuery.h +++ b/libraries/octree/src/OctreeQuery.h @@ -58,7 +58,7 @@ public: float getCameraNearClip() const { return _cameraNearClip; } float getCameraFarClip() const { return _cameraFarClip; } const glm::vec3& getCameraEyeOffsetPosition() const { return _cameraEyeOffsetPosition; } - float getKeyholeRadius() const { return _keyholeRadius; } + float getCameraCenterRadius() const { return _cameraCenterRadius; } glm::vec3 calculateCameraDirection() const; @@ -70,7 +70,7 @@ public: void setCameraNearClip(float nearClip) { _cameraNearClip = nearClip; } void setCameraFarClip(float farClip) { _cameraFarClip = farClip; } void setCameraEyeOffsetPosition(const glm::vec3& eyeOffsetPosition) { _cameraEyeOffsetPosition = eyeOffsetPosition; } - void setKeyholeRadius(float keyholeRadius) { _keyholeRadius = keyholeRadius; } + void setCameraCenterRadius(float radius) { _cameraCenterRadius = radius; } // related to Octree Sending strategies int getMaxQueryPacketsPerSecond() const { return _maxQueryPPS; } @@ -90,7 +90,7 @@ protected: float _cameraAspectRatio = 1.0f; float _cameraNearClip = 0.0f; float _cameraFarClip = 0.0f; - float _keyholeRadius { 0.0f }; + float _cameraCenterRadius { 0.0f }; glm::vec3 _cameraEyeOffsetPosition = glm::vec3(0.0f); // octree server sending items diff --git a/libraries/octree/src/ViewFrustum.cpp b/libraries/octree/src/ViewFrustum.cpp index 4d729179e7..228e4c7d35 100644 --- a/libraries/octree/src/ViewFrustum.cpp +++ b/libraries/octree/src/ViewFrustum.cpp @@ -115,10 +115,6 @@ void ViewFrustum::calculate() { // Our ModelViewProjection : multiplication of our 3 matrices (note: model is identity, so we can drop it) _ourModelViewProjectionMatrix = _projection * view; // Remember, matrix multiplication is the other way around - - // Set up our keyhole bounding box... - glm::vec3 corner = _position - _keyholeRadius; - _keyholeBoundingCube = AACube(corner,(_keyholeRadius * 2.0f)); } //enum { TOP_PLANE = 0, BOTTOM_PLANE, LEFT_PLANE, RIGHT_PLANE, NEAR_PLANE, FAR_PLANE }; @@ -134,219 +130,129 @@ const char* ViewFrustum::debugPlaneName (int plane) const { return "Unknown"; } -ViewFrustum::location ViewFrustum::pointInKeyhole(const glm::vec3& point) const { - - ViewFrustum::location result = INTERSECT; - - float distance = glm::distance(point, _position); - if (distance > _keyholeRadius) { - result = OUTSIDE; - } else if (distance < _keyholeRadius) { - result = INSIDE; - } - - return result; -} - -// To determine if two spheres intersect, simply calculate the distance between the centers of the two spheres. -// If the distance is greater than the sum of the two sphere radii, they don’t intersect. Otherwise they intersect. -// If the distance plus the radius of sphere A is less than the radius of sphere B then, sphere A is inside of sphere B -ViewFrustum::location ViewFrustum::sphereInKeyhole(const glm::vec3& center, float radius) const { - ViewFrustum::location result = INTERSECT; - - float distance = glm::distance(center, _position); - if (distance > (radius + _keyholeRadius)) { - result = OUTSIDE; - } else if ((distance + radius) < _keyholeRadius) { - result = INSIDE; - } - - return result; -} - - -// A box is inside a sphere if all of its corners are inside the sphere -// A box intersects a sphere if any of its edges (as rays) interesect the sphere -// A box is outside a sphere if none of its edges (as rays) interesect the sphere -ViewFrustum::location ViewFrustum::cubeInKeyhole(const AACube& cube) const { - - // First check to see if the cube is in the bounding cube for the sphere, if it's not, then we can short circuit - // this and not check with sphere penetration which is more expensive - if (!_keyholeBoundingCube.contains(cube)) { - return OUTSIDE; - } - - glm::vec3 penetration; - bool intersects = cube.findSpherePenetration(_position, _keyholeRadius, penetration); - - ViewFrustum::location result = OUTSIDE; - - // if the cube intersects the sphere, then it may also be inside... calculate further - if (intersects) { - result = INTERSECT; - - // test all the corners, if they are all inside the sphere, the entire cube is in the sphere - bool allPointsInside = true; // assume the best - for (int v = BOTTOM_LEFT_NEAR; v < TOP_LEFT_FAR; v++) { - glm::vec3 vertex = cube.getVertex((BoxVertex)v); - if (!pointInKeyhole(vertex)) { - allPointsInside = false; - break; +ViewFrustum::intersection ViewFrustum::calculateCubeFrustumIntersection(const AACube& cube) const { + // only check against frustum + ViewFrustum::intersection result = INSIDE; + for(int i=0; i < 6; i++) { + const glm::vec3& normal = _planes[i].getNormal(); + // check distance to farthest cube point + if ( _planes[i].distance(cube.getFarthestVertex(normal)) < 0.0f) { + return OUTSIDE; + } else { + // check distance to nearest cube point + if (_planes[i].distance(cube.getNearestVertex(normal)) < 0.0f) { + // cube straddles the plane + result = INTERSECT; } } - - if (allPointsInside) { - result = INSIDE; - } } - return result; } -// A box is inside a sphere if all of its corners are inside the sphere -// A box intersects a sphere if any of its edges (as rays) interesect the sphere -// A box is outside a sphere if none of its edges (as rays) interesect the sphere -ViewFrustum::location ViewFrustum::boxInKeyhole(const AABox& box) const { +const float HALF_SQRT_THREE = 0.8660254f; - // First check to see if the box is in the bounding box for the sphere, if it's not, then we can short circuit - // this and not check with sphere penetration which is more expensive - if (!_keyholeBoundingCube.contains(box)) { - return OUTSIDE; +ViewFrustum::intersection ViewFrustum::calculateCubeKeyholeIntersection(const AACube& cube) const { + // check against centeral sphere + ViewFrustum::intersection sphereResult = INTERSECT; + glm::vec3 cubeOffset = cube.calcCenter() - _position; + float distance = glm::length(cubeOffset); + if (distance > EPSILON) { + glm::vec3 vertex = cube.getFarthestVertex(cubeOffset) - _position; + if (glm::dot(vertex, cubeOffset) < _centerSphereRadius * distance) { + // the most outward cube vertex is inside central sphere + return INSIDE; + } + if (!cube.touchesSphere(_position, _centerSphereRadius)) { + sphereResult = OUTSIDE; + } + } else if (_centerSphereRadius > HALF_SQRT_THREE * cube.getScale()) { + // the cube is in center of sphere and its bounding radius is inside + return INSIDE; } - glm::vec3 penetration; - bool intersects = box.findSpherePenetration(_position, _keyholeRadius, penetration); + // check against frustum + ViewFrustum::intersection frustumResult = calculateCubeFrustumIntersection(cube); - ViewFrustum::location result = OUTSIDE; - - // if the box intersects the sphere, then it may also be inside... calculate further - if (intersects) { - result = INTERSECT; - - // test all the corners, if they are all inside the sphere, the entire box is in the sphere - bool allPointsInside = true; // assume the best - for (int v = BOTTOM_LEFT_NEAR; v < TOP_LEFT_FAR; v++) { - glm::vec3 vertex = box.getVertex((BoxVertex)v); - if (!pointInKeyhole(vertex)) { - allPointsInside = false; - break; - } - } - - if (allPointsInside) { - result = INSIDE; - } - } - - return result; + return (frustumResult == OUTSIDE) ? sphereResult : frustumResult; } -ViewFrustum::location ViewFrustum::computePointFrustumLocation(const glm::vec3& point) const { - // only checks against frustum, not sphere +bool ViewFrustum::pointIntersectsFrustum(const glm::vec3& point) const { + // only check against frustum for(int i = 0; i < 6; ++i) { float distance = _planes[i].distance(point); if (distance < 0.0f) { - return OUTSIDE; + return false; } } - return INSIDE; + return true; } -ViewFrustum::location ViewFrustum::computeSphereViewLocation(const glm::vec3& center, float radius) const { - ViewFrustum::location regularResult = INSIDE; - ViewFrustum::location keyholeResult = OUTSIDE; - - // If we have a keyholeRadius, check that first, since it's cheaper - if (_keyholeRadius >= 0.0f) { - keyholeResult = sphereInKeyhole(center, radius); - } - if (keyholeResult == INSIDE) { - return keyholeResult; - } - - float distance; +bool ViewFrustum::sphereIntersectsFrustum(const glm::vec3& center, float radius) const { + // only check against frustum for(int i=0; i < 6; i++) { - distance = _planes[i].distance(center); + float distance = _planes[i].distance(center); if (distance < -radius) { // This is outside the regular frustum, so just return the value from checking the keyhole - return keyholeResult; - } else if (distance < radius) { - regularResult = INTERSECT; + return false; } } - - return regularResult; + return true; } - -ViewFrustum::location ViewFrustum::computeCubeViewLocation(const AACube& cube) const { - - ViewFrustum::location regularResult = INSIDE; - ViewFrustum::location keyholeResult = OUTSIDE; - - // If we have a keyholeRadius, check that first, since it's cheaper - if (_keyholeRadius >= 0.0f) { - keyholeResult = cubeInKeyhole(cube); - } - if (keyholeResult == INSIDE) { - return keyholeResult; - } - - // TODO: These calculations are expensive, taking up 80% of our time in this function. - // This appears to be expensive because we have to test the distance to each plane. - // One suggested optimization is to first check against the approximated cone. We might - // also be able to test against the cone to the bounding sphere of the box. +bool ViewFrustum::boxIntersectsFrustum(const AABox& box) const { + // only check against frustum for(int i=0; i < 6; i++) { const glm::vec3& normal = _planes[i].getNormal(); - const glm::vec3& boxVertexP = cube.getVertexP(normal); - float planeToBoxVertexPDistance = _planes[i].distance(boxVertexP); - - const glm::vec3& boxVertexN = cube.getVertexN(normal); - float planeToBoxVertexNDistance = _planes[i].distance(boxVertexN); - - if (planeToBoxVertexPDistance < 0) { - // This is outside the regular frustum, so just return the value from checking the keyhole - return keyholeResult; - } else if (planeToBoxVertexNDistance < 0) { - regularResult = INTERSECT; + // check distance to farthest box point + if ( _planes[i].distance(box.getFarthestVertex(normal)) < 0.0f) { + return false; } } - return regularResult; + return true; } -ViewFrustum::location ViewFrustum::computeBoxViewLocation(const AABox& box) const { - - ViewFrustum::location regularResult = INSIDE; - ViewFrustum::location keyholeResult = OUTSIDE; - - // If we have a keyholeRadius, check that first, since it's cheaper - if (_keyholeRadius >= 0.0f) { - keyholeResult = boxInKeyhole(box); +bool ViewFrustum::sphereIntersectsKeyhole(const glm::vec3& center, float radius) const { + // check positive touch against central sphere + if (glm::length(center - _position) <= (radius + _centerSphereRadius)) { + return true; } - if (keyholeResult == INSIDE) { - return keyholeResult; - } - - // TODO: These calculations are expensive, taking up 80% of our time in this function. - // This appears to be expensive because we have to test the distance to each plane. - // One suggested optimization is to first check against the approximated cone. We might - // also be able to test against the cone to the bounding sphere of the box. + // check negative touches against frustum planes for(int i=0; i < 6; i++) { - const glm::vec3& normal = _planes[i].getNormal(); - const glm::vec3& boxVertexP = box.getVertexP(normal); - float planeToBoxVertexPDistance = _planes[i].distance(boxVertexP); - - const glm::vec3& boxVertexN = box.getVertexN(normal); - float planeToBoxVertexNDistance = _planes[i].distance(boxVertexN); - - if (planeToBoxVertexPDistance < 0) { - // This is outside the regular frustum, so just return the value from checking the keyhole - return keyholeResult; - } else if (planeToBoxVertexNDistance < 0) { - regularResult = INTERSECT; + if ( _planes[i].distance(center) < -radius) { + return false; } } - return regularResult; + return true; +} + +bool ViewFrustum::cubeIntersectsKeyhole(const AACube& cube) const { + // check positive touch against central sphere + if (cube.touchesSphere(_position, _centerSphereRadius)) { + return true; + } + // check negative touches against frustum planes + for(int i=0; i < 6; i++) { + const glm::vec3& normal = _planes[i].getNormal(); + if ( _planes[i].distance(cube.getFarthestVertex(normal)) < 0.0f) { + return false; + } + } + return true; +} + +bool ViewFrustum::boxIntersectsKeyhole(const AABox& box) const { + // check positive touch against central sphere + if (box.touchesSphere(_position, _centerSphereRadius)) { + return true; + } + // check negative touches against frustum planes + for(int i=0; i < 6; i++) { + const glm::vec3& normal = _planes[i].getNormal(); + if ( _planes[i].distance(box.getFarthestVertex(normal)) < 0.0f) { + return false; + } + } + return true; } bool testMatches(glm::quat lhs, glm::quat rhs, float epsilon = EPSILON) { @@ -528,7 +434,7 @@ void ViewFrustum::printDebugDetails() const { qCDebug(octree, "_right=%f,%f,%f", (double)_right.x, (double)_right.y, (double)_right.z ); qCDebug(octree, "_fieldOfView=%f", (double)_fieldOfView); qCDebug(octree, "_aspectRatio=%f", (double)_aspectRatio); - qCDebug(octree, "_keyHoleRadius=%f", (double)_keyholeRadius); + qCDebug(octree, "_centerSphereRadius=%f", (double)_centerSphereRadius); qCDebug(octree, "_nearClip=%f", (double)_nearClip); qCDebug(octree, "_farClip=%f", (double)_farClip); qCDebug(octree, "_focalLength=%f", (double)_focalLength); diff --git a/libraries/octree/src/ViewFrustum.h b/libraries/octree/src/ViewFrustum.h index 20609926fb..412d0f82f2 100644 --- a/libraries/octree/src/ViewFrustum.h +++ b/libraries/octree/src/ViewFrustum.h @@ -27,12 +27,15 @@ #include "OctreeConstants.h" #include "OctreeProjectedPolygon.h" -const float DEFAULT_KEYHOLE_RADIUS = 3.0f; +const float DEFAULT_CENTER_SPHERE_RADIUS = 3.0f; const float DEFAULT_FIELD_OF_VIEW_DEGREES = 45.0f; const float DEFAULT_ASPECT_RATIO = 16.0f/9.0f; const float DEFAULT_NEAR_CLIP = 0.08f; const float DEFAULT_FAR_CLIP = (float)HALF_TREE_SCALE; +// the "ViewFrustum" has a "keyhole" shape: a regular frustum for stuff that is "visible" with +// a central sphere for stuff that is nearby (for physics simulation). + class ViewFrustum { public: // setters for camera attributes @@ -83,19 +86,26 @@ public: const glm::vec3& getNearBottomLeft() const { return _cornersWorld[BOTTOM_LEFT_NEAR]; } const glm::vec3& getNearBottomRight() const { return _cornersWorld[BOTTOM_RIGHT_NEAR]; } - // get/set for keyhole attribute - void setKeyholeRadius(float keyholdRadius) { _keyholeRadius = keyholdRadius; } - float getKeyholeRadius() const { return _keyholeRadius; } + // get/set for central spherek attribute + void setCenterRadius(float radius) { _centerSphereRadius = radius; } + float getCenterRadius() const { return _centerSphereRadius; } void calculate(); - typedef enum {OUTSIDE, INTERSECT, INSIDE} location; + typedef enum { OUTSIDE = 0, INTERSECT, INSIDE } intersection; - ViewFrustum::location computePointFrustumLocation(const glm::vec3& point) const; + /// @return INSIDE, INTERSECT, or OUTSIDE depending on how cube intersects the keyhole shape + ViewFrustum::intersection calculateCubeFrustumIntersection(const AACube& cube) const; + ViewFrustum::intersection calculateCubeKeyholeIntersection(const AACube& cube) const; - ViewFrustum::location computeSphereViewLocation(const glm::vec3& center, float radius) const; - ViewFrustum::location computeCubeViewLocation(const AACube& cube) const; - ViewFrustum::location computeBoxViewLocation(const AABox& box) const; + bool pointIntersectsFrustum(const glm::vec3& point) const; + bool sphereIntersectsFrustum(const glm::vec3& center, float radius) const; + bool cubeIntersectsFrustum(const AACube& box) const; + bool boxIntersectsFrustum(const AABox& box) const; + + bool sphereIntersectsKeyhole(const glm::vec3& center, float radius) const; + bool cubeIntersectsKeyhole(const AACube& cube) const; + bool boxIntersectsKeyhole(const AABox& box) const; // some frustum comparisons bool matches(const ViewFrustum& compareTo, bool debug = false) const; @@ -132,12 +142,6 @@ public: const ::Plane* getPlanes() const { return _planes; } private: - // Used for keyhole calculations - ViewFrustum::location pointInKeyhole(const glm::vec3& point) const; - ViewFrustum::location sphereInKeyhole(const glm::vec3& center, float radius) const; - ViewFrustum::location cubeInKeyhole(const AACube& cube) const; - ViewFrustum::location boxInKeyhole(const AABox& box) const; - // camera location/orientation attributes glm::vec3 _position; // the position in world-frame glm::quat _orientation; @@ -151,9 +155,7 @@ private: glm::vec3 _up = IDENTITY_UP; glm::vec3 _right = IDENTITY_RIGHT; - // keyhole attributes - float _keyholeRadius = DEFAULT_KEYHOLE_RADIUS; - AACube _keyholeBoundingCube; + float _centerSphereRadius = DEFAULT_CENTER_SPHERE_RADIUS; // Calculated values glm::mat4 _inverseProjection; @@ -166,7 +168,7 @@ private: float _fieldOfView = DEFAULT_FIELD_OF_VIEW_DEGREES; glm::vec4 _corners[8]; glm::vec3 _cornersWorld[8]; - ::Plane _planes[6]; // How will this be used? + ::Plane _planes[6]; // plane normals point inside frustum const char* debugPlaneName (int plane) const; diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index 4ae8ba91c7..e5a313e3b1 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -474,15 +474,12 @@ void ModelMeshPartPayload::render(RenderArgs* args) const { #ifdef DEBUG_BOUNDING_PARTS { AABox partBounds = getPartBounds(_meshIndex, partIndex); - bool inView = args->_viewFrustum->computeBoxViewLocation(partBounds) != ViewFrustum::OUTSIDE; - glm::vec4 cubeColor; + glm::vec4 cubeColor(1.0f, 1.0f, 0.0f, 1.0f); if (isSkinned) { cubeColor = glm::vec4(0.0f, 1.0f, 1.0f, 1.0f); - } else if (inView) { + } else if (args->_viewFrustum->boxIntersectsFrustum(partBounds)) { cubeColor = glm::vec4(1.0f, 0.0f, 1.0f, 1.0f); - } else { - cubeColor = glm::vec4(1.0f, 1.0f, 0.0f, 1.0f); } Transform transform; diff --git a/libraries/render/src/render/CullTask.cpp b/libraries/render/src/render/CullTask.cpp index 66ba6f12f5..7824514649 100644 --- a/libraries/render/src/render/CullTask.cpp +++ b/libraries/render/src/render/CullTask.cpp @@ -39,12 +39,12 @@ void render::cullItems(const RenderContextPointer& renderContext, const CullFunc // TODO: some entity types (like lights) might want to be rendered even // when they are outside of the view frustum... - bool outOfView; + bool inView; { - PerformanceTimer perfTimer("computeBoxViewLocation"); - outOfView = frustum->computeBoxViewLocation(item.bound) == ViewFrustum::OUTSIDE; + PerformanceTimer perfTimer("boxIntersectsFrustum"); + inView = frustum->boxIntersectsFrustum(item.bound); } - if (!outOfView) { + if (inView) { bool bigEnoughToRender; { PerformanceTimer perfTimer("shouldRender"); @@ -237,8 +237,8 @@ void CullSpatialSelection::run(const SceneContextPointer& sceneContext, const Re */ } - bool viewTest(const AABox& bound) { - if (_args->_viewFrustum->computeBoxViewLocation(bound) == ViewFrustum::OUTSIDE) { + bool frustumTest(const AABox& bound) { + if (!_args->_viewFrustum->boxIntersectsFrustum(bound)) { _renderDetails._outOfView++; return false; } @@ -302,7 +302,7 @@ void CullSpatialSelection::run(const SceneContextPointer& sceneContext, const Re auto& item = scene->getItem(id); if (_filter.test(item.getKey())) { ItemBound itemBound(id, item.getBound()); - if (test.viewTest(itemBound.bound)) { + if (test.frustumTest(itemBound.bound)) { outItems.emplace_back(itemBound); } } @@ -316,7 +316,7 @@ void CullSpatialSelection::run(const SceneContextPointer& sceneContext, const Re auto& item = scene->getItem(id); if (_filter.test(item.getKey())) { ItemBound itemBound(id, item.getBound()); - if (test.viewTest(itemBound.bound)) { + if (test.frustumTest(itemBound.bound)) { if (test.solidAngleTest(itemBound.bound)) { outItems.emplace_back(itemBound); } diff --git a/libraries/shared/src/AABox.cpp b/libraries/shared/src/AABox.cpp index d16bdafcec..0b453e6f36 100644 --- a/libraries/shared/src/AABox.cpp +++ b/libraries/shared/src/AABox.cpp @@ -75,32 +75,32 @@ void AABox::setBox(const glm::vec3& corner, const glm::vec3& scale) { _scale = scale; } -glm::vec3 AABox::getVertexP(const glm::vec3& normal) const { +glm::vec3 AABox::getFarthestVertex(const glm::vec3& normal) const { glm::vec3 result = _corner; - if (normal.x > 0) { + if (normal.x > 0.0f) { result.x += _scale.x; } - if (normal.y > 0) { + if (normal.y > 0.0f) { result.y += _scale.y; } - if (normal.z > 0) { + if (normal.z > 0.0f) { result.z += _scale.z; } return result; } -glm::vec3 AABox::getVertexN(const glm::vec3& normal) const { +glm::vec3 AABox::getNearestVertex(const glm::vec3& normal) const { glm::vec3 result = _corner; - if (normal.x < 0) { + if (normal.x < 0.0f) { result.x += _scale.x; } - if (normal.y < 0) { + if (normal.y < 0.0f) { result.y += _scale.y; } - if (normal.z < 0) { + if (normal.z < 0.0f) { result.z += _scale.z; } @@ -217,7 +217,7 @@ bool AABox::expandedIntersectsSegment(const glm::vec3& start, const glm::vec3& e isWithin(start.x + axisDistance*direction.x, expandedCorner.x, expandedSize.x)); } -bool AABox::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, +bool AABox::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face, glm::vec3& surfaceNormal) const { // handle the trivial case where the box contains the origin if (contains(origin)) { @@ -281,6 +281,12 @@ bool AABox::findRayIntersection(const glm::vec3& origin, const glm::vec3& direct return false; } +bool AABox::touchesSphere(const glm::vec3& center, float radius) const { + // Avro's algorithm from this paper: http://www.mrtc.mdh.se/projects/3Dgraphics/paperF.pdf + glm::vec3 e = glm::max(_corner - center, Vectors::ZERO) + glm::max(center - _corner - _scale, Vectors::ZERO); + return glm::length2(e) <= radius * radius; +} + bool AABox::findSpherePenetration(const glm::vec3& center, float radius, glm::vec3& penetration) const { glm::vec4 center4 = glm::vec4(center, 1.0f); @@ -537,4 +543,4 @@ void AABox::transform(const Transform& transform) { scale(transform.getScale()); rotate(transform.getRotation()); translate(transform.getTranslation()); -} \ No newline at end of file +} diff --git a/libraries/shared/src/AABox.h b/libraries/shared/src/AABox.h index 24791445b3..ec06c60121 100644 --- a/libraries/shared/src/AABox.h +++ b/libraries/shared/src/AABox.h @@ -35,12 +35,12 @@ public: AABox(const glm::vec3& corner, const glm::vec3& dimensions); AABox(); ~AABox() {}; - + void setBox(const glm::vec3& corner, const glm::vec3& scale); void setBox(const glm::vec3& corner, float scale); - glm::vec3 getVertexP(const glm::vec3& normal) const; - glm::vec3 getVertexN(const glm::vec3& normal) const; + glm::vec3 getFarthestVertex(const glm::vec3& normal) const; // return vertex most parallel to normal + glm::vec3 getNearestVertex(const glm::vec3& normal) const; // return vertex most anti-parallel to normal const glm::vec3& getCorner() const { return _corner; } const glm::vec3& getScale() const { return _scale; } @@ -68,11 +68,12 @@ public: bool expandedContains(const glm::vec3& point, float expansion) const; bool expandedIntersectsSegment(const glm::vec3& start, const glm::vec3& end, float expansion) const; - bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, + bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face, glm::vec3& surfaceNormal) const; + bool touchesSphere(const glm::vec3& center, float radius) const; // fast but may generate false positives bool findSpherePenetration(const glm::vec3& center, float radius, glm::vec3& penetration) const; bool findCapsulePenetration(const glm::vec3& start, const glm::vec3& end, float radius, glm::vec3& penetration) const; - + bool isNull() const { return _scale == glm::vec3(0.0f, 0.0f, 0.0f); } AABox clamp(const glm::vec3& min, const glm::vec3& max) const; @@ -113,7 +114,7 @@ inline bool operator==(const AABox& a, const AABox& b) { } inline QDebug operator<<(QDebug debug, const AABox& box) { - debug << "AABox[ (" + debug << "AABox[ (" << box.getCorner().x << "," << box.getCorner().y << "," << box.getCorner().z << " ) to (" << box.calcTopFarLeft().x << "," << box.calcTopFarLeft().y << "," << box.calcTopFarLeft().z << ") size: (" << box.getDimensions().x << "," << box.getDimensions().y << "," << box.getDimensions().z << ")" diff --git a/libraries/shared/src/AACube.cpp b/libraries/shared/src/AACube.cpp index bc97a6ff69..f8122ea4dc 100644 --- a/libraries/shared/src/AACube.cpp +++ b/libraries/shared/src/AACube.cpp @@ -79,32 +79,32 @@ void AACube::setBox(const glm::vec3& corner, float scale) { _scale = scale; } -glm::vec3 AACube::getVertexP(const glm::vec3& normal) const { +glm::vec3 AACube::getFarthestVertex(const glm::vec3& normal) const { glm::vec3 result = _corner; - if (normal.x > 0) { + if (normal.x > 0.0f) { result.x += _scale; } - if (normal.y > 0) { + if (normal.y > 0.0f) { result.y += _scale; } - if (normal.z > 0) { + if (normal.z > 0.0f) { result.z += _scale; } return result; } -glm::vec3 AACube::getVertexN(const glm::vec3& normal) const { +glm::vec3 AACube::getNearestVertex(const glm::vec3& normal) const { glm::vec3 result = _corner; - if (normal.x < 0) { + if (normal.x < 0.0f) { result.x += _scale; } - if (normal.y < 0) { + if (normal.y < 0.0f) { result.y += _scale; } - if (normal.z < 0) { + if (normal.z < 0.0f) { result.z += _scale; } @@ -284,6 +284,12 @@ bool AACube::findRayIntersection(const glm::vec3& origin, const glm::vec3& direc return false; } +bool AACube::touchesSphere(const glm::vec3& center, float radius) const { + // Avro's algorithm from this paper: http://www.mrtc.mdh.se/projects/3Dgraphics/paperF.pdf + glm::vec3 e = glm::max(_corner - center, Vectors::ZERO) + glm::max(center - _corner - glm::vec3(_scale), Vectors::ZERO); + return glm::length2(e) <= radius * radius; +} + bool AACube::findSpherePenetration(const glm::vec3& center, float radius, glm::vec3& penetration) const { glm::vec4 center4 = glm::vec4(center, 1.0f); diff --git a/libraries/shared/src/AACube.h b/libraries/shared/src/AACube.h index dab5dafdaa..87a38cb304 100644 --- a/libraries/shared/src/AACube.h +++ b/libraries/shared/src/AACube.h @@ -34,8 +34,8 @@ public: ~AACube() {}; void setBox(const glm::vec3& corner, float scale); - glm::vec3 getVertexP(const glm::vec3& normal) const; - glm::vec3 getVertexN(const glm::vec3& normal) const; + glm::vec3 getFarthestVertex(const glm::vec3& normal) const; // return vertex most parallel to normal + glm::vec3 getNearestVertex(const glm::vec3& normal) const; // return vertex most anti-parallel to normal void scale(float scale); const glm::vec3& getCorner() const { return _corner; } float getScale() const { return _scale; } @@ -56,8 +56,9 @@ public: bool touches(const AABox& otherBox) const; bool expandedContains(const glm::vec3& point, float expansion) const; bool expandedIntersectsSegment(const glm::vec3& start, const glm::vec3& end, float expansion) const; - bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, + bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face, glm::vec3& surfaceNormal) const; + bool touchesSphere(const glm::vec3& center, float radius) const; bool findSpherePenetration(const glm::vec3& center, float radius, glm::vec3& penetration) const; bool findCapsulePenetration(const glm::vec3& start, const glm::vec3& end, float radius, glm::vec3& penetration) const; @@ -88,7 +89,7 @@ inline bool operator!=(const AACube& a, const AACube& b) { } inline QDebug operator<<(QDebug debug, const AACube& cube) { - debug << "AACube[ (" + debug << "AACube[ (" << cube.getCorner().x << "," << cube.getCorner().y << "," << cube.getCorner().z << " ) to (" << cube.calcTopFarLeft().x << "," << cube.calcTopFarLeft().y << "," << cube.calcTopFarLeft().z << ") size: (" << cube.getDimensions().x << "," << cube.getDimensions().y << "," << cube.getDimensions().z << ")" diff --git a/tests/octree/src/OctreeTests.cpp b/tests/octree/src/OctreeTests.cpp index d1f6fba891..ef08855d06 100644 --- a/tests/octree/src/OctreeTests.cpp +++ b/tests/octree/src/OctreeTests.cpp @@ -670,9 +670,8 @@ void OctreeTests::byteCountCodingTests() { } void OctreeTests::modelItemTests() { - bool verbose = true; - #if 0 // TODO - repair/replace these + bool verbose = true; //verbose = true; EntityTreeElementExtraEncodeData modelTreeElementExtraEncodeData; diff --git a/tests/octree/src/ViewFrustumTests.cpp b/tests/octree/src/ViewFrustumTests.cpp new file mode 100644 index 0000000000..2e9df54d83 --- /dev/null +++ b/tests/octree/src/ViewFrustumTests.cpp @@ -0,0 +1,1347 @@ +// +// ViewFrustumTests.cpp +// tests/octree/src +// +// Created by Andrew Meadows on 2016.02.19 +// Copyright 2016 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 +// + +#include "ViewFrustumTests.h" + +#include + +#include +#include +#include + +//#include +#include <../GLMTestUtils.h> +#include <../QTestExtensions.h> + + +const float ACCEPTABLE_FLOAT_ERROR = 1.0e-6f; +const float ACCEPTABLE_DOT_ERROR = 1.0e-5f; +const float ACCEPTABLE_CLIP_ERROR = 3e-4f; + +const glm::vec3 localRight(1.0f, 0.0f, 0.0f); +const glm::vec3 localUp(0.0f, 1.0f, 0.0f); +const glm::vec3 localForward(0.0f, 0.0f, -1.0f); + + +QTEST_MAIN(ViewFrustumTests) + +void ViewFrustumTests::testInit() { + float aspect = 1.0f; + float fovX = PI / 2.0f; + float nearClip = 1.0f; + float farClip = 100.0f; + float holeRadius = 10.0f; + + glm::vec3 center = glm::vec3(12.3f, 4.56f, 89.7f); + + float angle = PI / 7.0f; + glm::vec3 axis = glm::normalize(glm::vec3(1.0f, 2.0f, 3.0f)); + glm::quat rotation = glm::angleAxis(angle, axis); + + ViewFrustum view; + view.setProjection(glm::perspective(fovX, aspect, nearClip, farClip)); + view.setPosition(center); + view.setOrientation(rotation); + view.setCenterRadius(holeRadius); + view.calculate(); + + // check frustum dimensions + QCOMPARE_WITH_ABS_ERROR(fovX, glm::radians(view.getFieldOfView()), ACCEPTABLE_FLOAT_ERROR); + QCOMPARE_WITH_ABS_ERROR(aspect, view.getAspectRatio(), ACCEPTABLE_FLOAT_ERROR); + QCOMPARE_WITH_ABS_ERROR(farClip, view.getFarClip(), ACCEPTABLE_CLIP_ERROR); + QCOMPARE_WITH_ABS_ERROR(nearClip, view.getNearClip(), ACCEPTABLE_CLIP_ERROR); + + // check transform + QCOMPARE_WITH_ABS_ERROR(view.getPosition(), center, ACCEPTABLE_FLOAT_ERROR); + float rotationDot = glm::abs(glm::dot(rotation, view.getOrientation())); + QCOMPARE_WITH_ABS_ERROR(rotationDot, 1.0f, ACCEPTABLE_DOT_ERROR); + + // check view directions + glm::vec3 expectedForward = rotation * localForward; + float forwardDot = glm::dot(expectedForward, view.getDirection()); + QCOMPARE_WITH_ABS_ERROR(forwardDot, 1.0f, ACCEPTABLE_DOT_ERROR); + + glm::vec3 expectedRight = rotation * localRight; + float rightDot = glm::dot(expectedRight, view.getRight()); + QCOMPARE_WITH_ABS_ERROR(rightDot, 1.0f, ACCEPTABLE_DOT_ERROR); + + glm::vec3 expectedUp = rotation * localUp; + float upDot = glm::dot(expectedUp, view.getUp()); + QCOMPARE_WITH_ABS_ERROR(upDot, 1.0f, ACCEPTABLE_DOT_ERROR); +} + +void ViewFrustumTests::testCubeKeyholeIntersection() { + float aspect = 1.0f; + float fovX = PI / 2.0f; + float fovY = 2.0f * asinf(sinf(0.5f * fovX) / aspect); + float nearClip = 1.0f; + float farClip = 100.0f; + float holeRadius = 10.0f; + + glm::vec3 center = glm::vec3(12.3f, 4.56f, 89.7f); + + float angle = PI / 7.0f; + glm::vec3 axis = Vectors::UNIT_Y; + glm::quat rotation = glm::angleAxis(angle, axis); + + ViewFrustum view; + view.setProjection(glm::perspective(fovX, aspect, nearClip, farClip)); + view.setPosition(center); + view.setOrientation(rotation); + view.setCenterRadius(holeRadius); + view.calculate(); + + float delta = 0.1f; + float deltaAngle = 0.01f; + glm::quat elevation, swing; + glm::vec3 cubeCenter, localOffset; + + float cubeScale = 2.68f; // must be much smaller than cubeDistance for small angle approx below + glm::vec3 halfScaleOffset = 0.5f * glm::vec3(cubeScale); + float cubeDistance = farClip; + float cubeBoundingRadius = 0.5f * sqrtf(3.0f) * cubeScale; + float cubeAngle = cubeBoundingRadius / cubeDistance; // sine of small angles approximation + AACube cube(center, cubeScale); + + // farPlane + localOffset = (cubeDistance - cubeBoundingRadius - delta) * localForward; + cubeCenter = center + rotation * localOffset; + cube.setBox(cubeCenter - halfScaleOffset, cubeScale); + QCOMPARE(view.calculateCubeKeyholeIntersection(cube), ViewFrustum::INSIDE); + + localOffset = cubeDistance * localForward; + cubeCenter = center + rotation * localOffset; + cube.setBox(cubeCenter - halfScaleOffset, cubeScale); + QCOMPARE(view.calculateCubeKeyholeIntersection(cube), ViewFrustum::INTERSECT); + + localOffset = (cubeDistance + cubeBoundingRadius + delta) * localForward; + cubeCenter = center + rotation * localOffset; + cube.setBox(cubeCenter - halfScaleOffset, cubeScale); + QCOMPARE(view.calculateCubeKeyholeIntersection(cube), ViewFrustum::OUTSIDE); + + // nearPlane + localOffset = (nearClip + 2.0f * cubeBoundingRadius + delta) * localForward; + cubeCenter = center + rotation * localOffset; + cube.setBox(cubeCenter - halfScaleOffset, cubeScale); + QCOMPARE(view.calculateCubeKeyholeIntersection(cube), ViewFrustum::INSIDE); + + localOffset = (nearClip + delta) * localForward; + cubeCenter = center + rotation * localOffset; + cube.setBox(cubeCenter - halfScaleOffset, cubeScale); + QCOMPARE(view.calculateCubeKeyholeIntersection(cube), ViewFrustum::INSIDE); + + // topPlane + angle = 0.5f * fovY; + elevation = glm::angleAxis(angle - cubeAngle - deltaAngle, localRight); + localOffset = elevation * (cubeDistance * localForward); + cubeCenter = center + rotation * localOffset; + cube.setBox(cubeCenter - halfScaleOffset, cubeScale); + QCOMPARE(view.calculateCubeKeyholeIntersection(cube), ViewFrustum::INSIDE); + + elevation = glm::angleAxis(angle, localRight); + localOffset = elevation * (cubeDistance * localForward); + cubeCenter = center + rotation * localOffset; + cube.setBox(cubeCenter - halfScaleOffset, cubeScale); + QCOMPARE(view.calculateCubeKeyholeIntersection(cube), ViewFrustum::INTERSECT); + + elevation = glm::angleAxis(angle + cubeAngle + deltaAngle, localRight); + localOffset = elevation * (cubeDistance * localForward); + cubeCenter = center + rotation * localOffset; + cube.setBox(cubeCenter - halfScaleOffset, cubeScale); + QCOMPARE(view.calculateCubeKeyholeIntersection(cube), ViewFrustum::OUTSIDE); + + // bottom plane + angle = -0.5f * fovY; + elevation = glm::angleAxis(angle + cubeAngle + deltaAngle, localRight); + localOffset = elevation * (cubeDistance * localForward); + cubeCenter = center + rotation * localOffset; + cube.setBox(cubeCenter - halfScaleOffset, cubeScale); + QCOMPARE(view.calculateCubeKeyholeIntersection(cube), ViewFrustum::INSIDE); + + elevation = glm::angleAxis(angle, localRight); + localOffset = elevation * (cubeDistance * localForward); + cubeCenter = center + rotation * localOffset; + cube.setBox(cubeCenter - halfScaleOffset, cubeScale); + QCOMPARE(view.calculateCubeKeyholeIntersection(cube), ViewFrustum::INTERSECT); + + elevation = glm::angleAxis(angle - cubeAngle - deltaAngle, localRight); + localOffset = elevation * (cubeDistance * localForward); + cubeCenter = center + rotation * localOffset; + cube.setBox(cubeCenter - halfScaleOffset, cubeScale); + QCOMPARE(view.calculateCubeKeyholeIntersection(cube), ViewFrustum::OUTSIDE); + + // right plane + angle = 0.5f * fovX; + swing = glm::angleAxis(angle - cubeAngle - deltaAngle, localUp); + localOffset = swing * (cubeDistance * localForward); + cubeCenter = center + rotation * localOffset; + cube.setBox(cubeCenter - halfScaleOffset, cubeScale); + QCOMPARE(view.calculateCubeKeyholeIntersection(cube), ViewFrustum::INSIDE); + + swing = glm::angleAxis(angle, localUp); + localOffset = swing * (cubeDistance * localForward); + cubeCenter = center + rotation * localOffset; + cube.setBox(cubeCenter - halfScaleOffset, cubeScale); + QCOMPARE(view.calculateCubeKeyholeIntersection(cube), ViewFrustum::INTERSECT); + + swing = glm::angleAxis(angle + cubeAngle + deltaAngle, localUp); + localOffset = swing * (cubeDistance * localForward); + cubeCenter = center + rotation * localOffset; + cube.setBox(cubeCenter - halfScaleOffset, cubeScale); + QCOMPARE(view.calculateCubeKeyholeIntersection(cube), ViewFrustum::OUTSIDE); + + // left plane + angle = -0.5f * fovX; + swing = glm::angleAxis(angle + cubeAngle + deltaAngle, localUp); + localOffset = swing * (cubeDistance * localForward); + cubeCenter = center + rotation * localOffset; + cube.setBox(cubeCenter - halfScaleOffset, cubeScale); + QCOMPARE(view.calculateCubeKeyholeIntersection(cube), ViewFrustum::INSIDE); + + swing = glm::angleAxis(angle, localUp); + localOffset = swing * (cubeDistance * localForward); + cubeCenter = center + rotation * localOffset; + cube.setBox(cubeCenter - halfScaleOffset, cubeScale); + QCOMPARE(view.calculateCubeKeyholeIntersection(cube), ViewFrustum::INTERSECT); + + swing = glm::angleAxis(angle - cubeAngle - deltaAngle, localUp); + localOffset = swing * (cubeDistance * localForward); + cubeCenter = center + rotation * localOffset; + cube.setBox(cubeCenter - halfScaleOffset, cubeScale); + QCOMPARE(view.calculateCubeKeyholeIntersection(cube), ViewFrustum::OUTSIDE); + + // central sphere right + localOffset = (holeRadius - cubeBoundingRadius - delta) * localRight; + cubeCenter = center + rotation * localOffset; + cube.setBox(cubeCenter - halfScaleOffset, cubeScale); + QCOMPARE(view.calculateCubeKeyholeIntersection(cube), ViewFrustum::INSIDE); + + localOffset = holeRadius * localRight; + cubeCenter = center + rotation * localOffset; + cube.setBox(cubeCenter - halfScaleOffset, cubeScale); + QCOMPARE(view.calculateCubeKeyholeIntersection(cube), ViewFrustum::INTERSECT); + + localOffset = (holeRadius + cubeBoundingRadius + delta) * localRight; + cubeCenter = center + rotation * localOffset; + cube.setBox(cubeCenter - halfScaleOffset, cubeScale); + QCOMPARE(view.calculateCubeKeyholeIntersection(cube), ViewFrustum::OUTSIDE); + + // central sphere up + localOffset = (holeRadius - cubeBoundingRadius - delta) * localUp; + cubeCenter = center + rotation * localOffset; + cube.setBox(cubeCenter - halfScaleOffset, cubeScale); + QCOMPARE(view.calculateCubeKeyholeIntersection(cube), ViewFrustum::INSIDE); + + localOffset = holeRadius * localUp; + cubeCenter = center + rotation * localOffset; + cube.setBox(cubeCenter - halfScaleOffset, cubeScale); + QCOMPARE(view.calculateCubeKeyholeIntersection(cube), ViewFrustum::INTERSECT); + + localOffset = (holeRadius + cubeBoundingRadius + delta) * localUp; + cubeCenter = center + rotation * localOffset; + cube.setBox(cubeCenter - halfScaleOffset, cubeScale); + QCOMPARE(view.calculateCubeKeyholeIntersection(cube), ViewFrustum::OUTSIDE); + + // central sphere back + localOffset = (-holeRadius + cubeBoundingRadius + delta) * localForward; + cubeCenter = center + rotation * localOffset; + cube.setBox(cubeCenter - halfScaleOffset, cubeScale); + QCOMPARE(view.calculateCubeKeyholeIntersection(cube), ViewFrustum::INSIDE); + + localOffset = - holeRadius * localForward; + cubeCenter = center + rotation * localOffset; + cube.setBox(cubeCenter - halfScaleOffset, cubeScale); + QCOMPARE(view.calculateCubeKeyholeIntersection(cube), ViewFrustum::INTERSECT); + + localOffset = (-holeRadius - cubeBoundingRadius - delta) * localForward; + cubeCenter = center + rotation * localOffset; + cube.setBox(cubeCenter - halfScaleOffset, cubeScale); + QCOMPARE(view.calculateCubeKeyholeIntersection(cube), ViewFrustum::OUTSIDE); + + // central sphere center + float bigCubeScale = 2.0f * holeRadius / sqrtf(3.0f) - delta; + cube.setBox(center - glm::vec3(0.5f * bigCubeScale), bigCubeScale); + QCOMPARE(view.calculateCubeKeyholeIntersection(cube), ViewFrustum::INSIDE); // smaller than sphere + + bigCubeScale = 2.0f * holeRadius / sqrtf(3.0f) + delta; + cube.setBox(center - glm::vec3(0.5f * bigCubeScale), bigCubeScale); + QCOMPARE(view.calculateCubeKeyholeIntersection(cube), ViewFrustum::INTERSECT); // larger than sphere +} + +void ViewFrustumTests::testCubeFrustumIntersection() { + float aspect = 1.0f; + float fovX = PI / 2.0f; + float fovY = 2.0f * asinf(sinf(0.5f * fovX) / aspect); + float nearClip = 1.0f; + float farClip = 100.0f; + float holeRadius = 10.0f; + + glm::vec3 center = glm::vec3(12.3f, 4.56f, 89.7f); + + float angle = PI / 7.0f; + glm::vec3 axis = Vectors::UNIT_Y; + glm::quat rotation = glm::angleAxis(angle, axis); + + ViewFrustum view; + view.setProjection(glm::perspective(fovX, aspect, nearClip, farClip)); + view.setPosition(center); + view.setOrientation(rotation); + view.setCenterRadius(holeRadius); + view.calculate(); + + float delta = 0.1f; + float deltaAngle = 0.01f; + glm::quat elevation, swing; + glm::vec3 cubeCenter, localOffset; + + float cubeScale = 2.68f; // must be much smaller than cubeDistance for small angle approx below + glm::vec3 halfScaleOffset = 0.5f * glm::vec3(cubeScale); + float cubeDistance = farClip; + float cubeBoundingRadius = 0.5f * sqrtf(3.0f) * cubeScale; + float cubeAngle = cubeBoundingRadius / cubeDistance; // sine of small angles approximation + AACube cube(center, cubeScale); + + // farPlane + localOffset = (cubeDistance - cubeBoundingRadius - delta) * localForward; + cubeCenter = center + rotation * localOffset; + cube.setBox(cubeCenter - halfScaleOffset, cubeScale); + QCOMPARE(view.calculateCubeFrustumIntersection(cube), ViewFrustum::INSIDE); + + localOffset = cubeDistance * localForward; + cubeCenter = center + rotation * localOffset; + cube.setBox(cubeCenter - halfScaleOffset, cubeScale); + QCOMPARE(view.calculateCubeFrustumIntersection(cube), ViewFrustum::INTERSECT); + + localOffset = (cubeDistance + cubeBoundingRadius + delta) * localForward; + cubeCenter = center + rotation * localOffset; + cube.setBox(cubeCenter - halfScaleOffset, cubeScale); + QCOMPARE(view.calculateCubeFrustumIntersection(cube), ViewFrustum::OUTSIDE); + + // nearPlane + localOffset = (nearClip + 2.0f * cubeBoundingRadius + delta) * localForward; + cubeCenter = center + rotation * localOffset; + cube.setBox(cubeCenter - halfScaleOffset, cubeScale); + QCOMPARE(view.calculateCubeFrustumIntersection(cube), ViewFrustum::INSIDE); + + localOffset = (nearClip + delta) * localForward; + cubeCenter = center + rotation * localOffset; + cube.setBox(cubeCenter - halfScaleOffset, cubeScale); + QCOMPARE(view.calculateCubeFrustumIntersection(cube), ViewFrustum::INTERSECT); + + localOffset = (nearClip - cubeBoundingRadius - delta) * localForward; + cubeCenter = center + rotation * localOffset; + cube.setBox(cubeCenter - halfScaleOffset, cubeScale); + QCOMPARE(view.calculateCubeFrustumIntersection(cube), ViewFrustum::OUTSIDE); + + // topPlane + angle = 0.5f * fovY; + elevation = glm::angleAxis(angle - cubeAngle - deltaAngle, localRight); + localOffset = elevation * (cubeDistance * localForward); + cubeCenter = center + rotation * localOffset; + cube.setBox(cubeCenter - halfScaleOffset, cubeScale); + QCOMPARE(view.calculateCubeFrustumIntersection(cube), ViewFrustum::INSIDE); + + elevation = glm::angleAxis(angle, localRight); + localOffset = elevation * (cubeDistance * localForward); + cubeCenter = center + rotation * localOffset; + cube.setBox(cubeCenter - halfScaleOffset, cubeScale); + QCOMPARE(view.calculateCubeFrustumIntersection(cube), ViewFrustum::INTERSECT); + + elevation = glm::angleAxis(angle + cubeAngle + deltaAngle, localRight); + localOffset = elevation * (cubeDistance * localForward); + cubeCenter = center + rotation * localOffset; + cube.setBox(cubeCenter - halfScaleOffset, cubeScale); + QCOMPARE(view.calculateCubeFrustumIntersection(cube), ViewFrustum::OUTSIDE); + + // bottom plane + angle = -0.5f * fovY; + elevation = glm::angleAxis(angle + cubeAngle + deltaAngle, localRight); + localOffset = elevation * (cubeDistance * localForward); + cubeCenter = center + rotation * localOffset; + cube.setBox(cubeCenter - halfScaleOffset, cubeScale); + QCOMPARE(view.calculateCubeFrustumIntersection(cube), ViewFrustum::INSIDE); + + elevation = glm::angleAxis(angle, localRight); + localOffset = elevation * (cubeDistance * localForward); + cubeCenter = center + rotation * localOffset; + cube.setBox(cubeCenter - halfScaleOffset, cubeScale); + QCOMPARE(view.calculateCubeFrustumIntersection(cube), ViewFrustum::INTERSECT); + + elevation = glm::angleAxis(angle - cubeAngle - deltaAngle, localRight); + localOffset = elevation * (cubeDistance * localForward); + cubeCenter = center + rotation * localOffset; + cube.setBox(cubeCenter - halfScaleOffset, cubeScale); + QCOMPARE(view.calculateCubeFrustumIntersection(cube), ViewFrustum::OUTSIDE); + + // right plane + angle = 0.5f * fovX; + swing = glm::angleAxis(angle - cubeAngle - deltaAngle, localUp); + localOffset = swing * (cubeDistance * localForward); + cubeCenter = center + rotation * localOffset; + cube.setBox(cubeCenter - halfScaleOffset, cubeScale); + QCOMPARE(view.calculateCubeFrustumIntersection(cube), ViewFrustum::INSIDE); + + swing = glm::angleAxis(angle, localUp); + localOffset = swing * (cubeDistance * localForward); + cubeCenter = center + rotation * localOffset; + cube.setBox(cubeCenter - halfScaleOffset, cubeScale); + QCOMPARE(view.calculateCubeFrustumIntersection(cube), ViewFrustum::INTERSECT); + + swing = glm::angleAxis(angle + cubeAngle + deltaAngle, localUp); + localOffset = swing * (cubeDistance * localForward); + cubeCenter = center + rotation * localOffset; + cube.setBox(cubeCenter - halfScaleOffset, cubeScale); + QCOMPARE(view.calculateCubeFrustumIntersection(cube), ViewFrustum::OUTSIDE); + + // left plane + angle = -0.5f * fovX; + swing = glm::angleAxis(angle + cubeAngle + deltaAngle, localUp); + localOffset = swing * (cubeDistance * localForward); + cubeCenter = center + rotation * localOffset; + cube.setBox(cubeCenter - halfScaleOffset, cubeScale); + QCOMPARE(view.calculateCubeFrustumIntersection(cube), ViewFrustum::INSIDE); + + swing = glm::angleAxis(angle, localUp); + localOffset = swing * (cubeDistance * localForward); + cubeCenter = center + rotation * localOffset; + cube.setBox(cubeCenter - halfScaleOffset, cubeScale); + QCOMPARE(view.calculateCubeFrustumIntersection(cube), ViewFrustum::INTERSECT); + + swing = glm::angleAxis(angle - cubeAngle - deltaAngle, localUp); + localOffset = swing * (cubeDistance * localForward); + cubeCenter = center + rotation * localOffset; + cube.setBox(cubeCenter - halfScaleOffset, cubeScale); + QCOMPARE(view.calculateCubeFrustumIntersection(cube), ViewFrustum::OUTSIDE); +} + +void ViewFrustumTests::testPointIntersectsFrustum() { + float aspect = 1.0f; + float fovX = PI / 2.0f; + float fovY = 2.0f * asinf(sinf(0.5f * fovX) / aspect); + float nearClip = 1.0f; + float farClip = 100.0f; + float holeRadius = 10.0f; + + glm::vec3 center = glm::vec3(12.3f, 4.56f, 89.7f); + + float angle = PI / 7.0f; + glm::vec3 axis = Vectors::UNIT_Y; + glm::quat rotation = glm::angleAxis(angle, axis); + + ViewFrustum view; + view.setProjection(glm::perspective(fovX, aspect, nearClip, farClip)); + view.setPosition(center); + view.setOrientation(rotation); + view.setCenterRadius(holeRadius); + view.calculate(); + + float delta = 0.1f; + float deltaAngle = 0.01f; + glm::quat elevation, swing; + glm::vec3 point, localOffset; + float pointDistance = farClip; + + // farPlane + localOffset = (pointDistance - delta) * localForward; + point = center + rotation * localOffset; + QCOMPARE(view.pointIntersectsFrustum(point), true); // inside + + localOffset = (pointDistance + delta) * localForward; + point = center + rotation * localOffset; + QCOMPARE(view.pointIntersectsFrustum(point), false); // outside + + // nearPlane + localOffset = (nearClip + delta) * localForward; + point = center + rotation * localOffset; + QCOMPARE(view.pointIntersectsFrustum(point), true); // inside + + localOffset = (nearClip - delta) * localForward; + point = center + rotation * localOffset; + QCOMPARE(view.pointIntersectsFrustum(point), false); // outside + + // topPlane + angle = 0.5f * fovY; + elevation = glm::angleAxis(angle - deltaAngle, localRight); + localOffset = elevation * (pointDistance * localForward); + point = center + rotation * localOffset; + QCOMPARE(view.pointIntersectsFrustum(point), true); // inside + + elevation = glm::angleAxis(angle + deltaAngle, localRight); + localOffset = elevation * (pointDistance * localForward); + point = center + rotation * localOffset; + QCOMPARE(view.pointIntersectsFrustum(point), false); // outside + + // bottom plane + angle = -0.5f * fovY; + elevation = glm::angleAxis(angle + deltaAngle, localRight); + localOffset = elevation * (pointDistance * localForward); + point = center + rotation * localOffset; + QCOMPARE(view.pointIntersectsFrustum(point), true); // inside + + elevation = glm::angleAxis(angle - deltaAngle, localRight); + localOffset = elevation * (pointDistance * localForward); + point = center + rotation * localOffset; + QCOMPARE(view.pointIntersectsFrustum(point), false); // outside + + // right plane + angle = 0.5f * fovX; + swing = glm::angleAxis(angle - deltaAngle, localUp); + localOffset = swing * (pointDistance * localForward); + point = center + rotation * localOffset; + QCOMPARE(view.pointIntersectsFrustum(point), true); // inside + + swing = glm::angleAxis(angle + deltaAngle, localUp); + localOffset = swing * (pointDistance * localForward); + point = center + rotation * localOffset; + QCOMPARE(view.pointIntersectsFrustum(point), false); // outside + + // left plane + angle = -0.5f * fovX; + swing = glm::angleAxis(angle + deltaAngle, localUp); + localOffset = swing * (pointDistance * localForward); + point = center + rotation * localOffset; + QCOMPARE(view.pointIntersectsFrustum(point), true); // inside + + swing = glm::angleAxis(angle - deltaAngle, localUp); + localOffset = swing * (pointDistance * localForward); + point = center + rotation * localOffset; + QCOMPARE(view.pointIntersectsFrustum(point), false); // outside +} + +void ViewFrustumTests::testSphereIntersectsFrustum() { + float aspect = 1.0f; + float fovX = PI / 2.0f; + float fovY = 2.0f * asinf(sinf(0.5f * fovX) / aspect); + float nearClip = 1.0f; + float farClip = 100.0f; + float holeRadius = 10.0f; + + glm::vec3 center = glm::vec3(12.3f, 4.56f, 89.7f); + + float angle = PI / 7.0f; + glm::vec3 axis = Vectors::UNIT_Y; + glm::quat rotation = glm::angleAxis(angle, axis); + + ViewFrustum view; + view.setProjection(glm::perspective(fovX, aspect, nearClip, farClip)); + view.setPosition(center); + view.setOrientation(rotation); + view.setCenterRadius(holeRadius); + view.calculate(); + + float delta = 0.1f; + float deltaAngle = 0.01f; + glm::quat elevation, swing; + glm::vec3 sphereCenter, localOffset; + + float sphereRadius = 2.68f; // must be much smaller than sphereDistance for small angle approx below + float sphereDistance = farClip; + float sphereAngle = sphereRadius / sphereDistance; // sine of small angles approximation + + // farPlane + localOffset = (sphereDistance - sphereRadius - delta) * localForward; + sphereCenter = center + rotation * localOffset; + QCOMPARE(view.sphereIntersectsFrustum(sphereCenter, sphereRadius), true); // inside + + localOffset = (sphereDistance + sphereRadius - delta) * localForward; + sphereCenter = center + rotation * localOffset; + QCOMPARE(view.sphereIntersectsFrustum(sphereCenter, sphereRadius), true); // straddle + + localOffset = (sphereDistance + sphereRadius + delta) * localForward; + sphereCenter = center + rotation * localOffset; + QCOMPARE(view.sphereIntersectsFrustum(sphereCenter, sphereRadius), false); // outside + + // nearPlane + localOffset = (nearClip + 2.0f * sphereRadius + delta) * localForward; + sphereCenter = center + rotation * localOffset; + QCOMPARE(view.sphereIntersectsFrustum(sphereCenter, sphereRadius), true); // inside + + localOffset = (nearClip - sphereRadius + delta) * localForward; + sphereCenter = center + rotation * localOffset; + QCOMPARE(view.sphereIntersectsFrustum(sphereCenter, sphereRadius), true); // straddle + + localOffset = (nearClip - sphereRadius - delta) * localForward; + sphereCenter = center + rotation * localOffset; + QCOMPARE(view.sphereIntersectsFrustum(sphereCenter, sphereRadius), false); // outside + + // topPlane + angle = 0.5f * fovY - sphereAngle; + elevation = glm::angleAxis(angle - deltaAngle, localRight); + localOffset = elevation * (sphereDistance * localForward); + sphereCenter = center + rotation * localOffset; + QCOMPARE(view.sphereIntersectsFrustum(sphereCenter, sphereRadius), true); // inside + + angle = 0.5f * fovY + sphereAngle; + elevation = glm::angleAxis(angle - deltaAngle, localRight); + localOffset = elevation * (sphereDistance * localForward); + sphereCenter = center + rotation * localOffset; + QCOMPARE(view.sphereIntersectsFrustum(sphereCenter, sphereRadius), true); // straddle + + elevation = glm::angleAxis(angle + deltaAngle, localRight); + localOffset = elevation * (sphereDistance * localForward); + sphereCenter = center + rotation * localOffset; + QCOMPARE(view.sphereIntersectsFrustum(sphereCenter, sphereRadius), false); // outside + + // bottom plane + angle = -0.5f * fovY + sphereAngle; + elevation = glm::angleAxis(angle + deltaAngle, localRight); + localOffset = elevation * (sphereDistance * localForward); + sphereCenter = center + rotation * localOffset; + QCOMPARE(view.sphereIntersectsFrustum(sphereCenter, sphereRadius), true); // inside + + angle = -0.5f * fovY - sphereAngle; + elevation = glm::angleAxis(angle + deltaAngle, localRight); + localOffset = elevation * (sphereDistance * localForward); + sphereCenter = center + rotation * localOffset; + QCOMPARE(view.sphereIntersectsFrustum(sphereCenter, sphereRadius), true); // straddle + + elevation = glm::angleAxis(angle - deltaAngle, localRight); + localOffset = elevation * (sphereDistance * localForward); + sphereCenter = center + rotation * localOffset; + QCOMPARE(view.sphereIntersectsFrustum(sphereCenter, sphereRadius), false); // outside + + // right plane + angle = 0.5f * fovX - sphereAngle; + swing = glm::angleAxis(angle - deltaAngle, localUp); + localOffset = swing * (sphereDistance * localForward); + sphereCenter = center + rotation * localOffset; + QCOMPARE(view.sphereIntersectsFrustum(sphereCenter, sphereRadius), true); // inside + + angle = 0.5f * fovX + sphereAngle; + swing = glm::angleAxis(angle - deltaAngle, localUp); + localOffset = swing * (sphereDistance * localForward); + sphereCenter = center + rotation * localOffset; + QCOMPARE(view.sphereIntersectsFrustum(sphereCenter, sphereRadius), true); // straddle + + swing = glm::angleAxis(angle + deltaAngle, localUp); + localOffset = swing * (sphereDistance * localForward); + sphereCenter = center + rotation * localOffset; + QCOMPARE(view.sphereIntersectsFrustum(sphereCenter, sphereRadius), false); // outside + + // left plane + angle = -0.5f * fovX + sphereAngle; + swing = glm::angleAxis(angle + deltaAngle, localUp); + localOffset = swing * (sphereDistance * localForward); + sphereCenter = center + rotation * localOffset; + QCOMPARE(view.sphereIntersectsFrustum(sphereCenter, sphereRadius), true); // inside + + angle = -0.5f * fovX - sphereAngle; + swing = glm::angleAxis(angle + deltaAngle, localUp); + localOffset = swing * (sphereDistance * localForward); + sphereCenter = center + rotation * localOffset; + QCOMPARE(view.sphereIntersectsFrustum(sphereCenter, sphereRadius), true); // straddle + + swing = glm::angleAxis(angle - sphereAngle - deltaAngle, localUp); + localOffset = swing * (sphereDistance * localForward); + sphereCenter = center + rotation * localOffset; + QCOMPARE(view.sphereIntersectsFrustum(sphereCenter, sphereRadius), false); // outside +} + +void ViewFrustumTests::testBoxIntersectsFrustum() { + float aspect = 1.0f; + float fovX = PI / 2.0f; + float fovY = 2.0f * asinf(sinf(0.5f * fovX) / aspect); + float nearClip = 1.0f; + float farClip = 100.0f; + float holeRadius = 10.0f; + + glm::vec3 center = glm::vec3(12.3f, 4.56f, 89.7f); + + float angle = PI / 7.0f; + glm::vec3 axis = Vectors::UNIT_Y; + glm::quat rotation = glm::angleAxis(angle, axis); + + ViewFrustum view; + view.setProjection(glm::perspective(fovX, aspect, nearClip, farClip)); + view.setPosition(center); + view.setOrientation(rotation); + view.setCenterRadius(holeRadius); + view.calculate(); + + float delta = 0.1f; + float deltaAngle = 0.01f; + glm::quat elevation, swing; + glm::vec3 boxCenter, localOffset; + + glm::vec3 boxScale = glm::vec3(2.68f, 1.78f, 0.431f); + float boxDistance = farClip; + float boxBoundingRadius = 0.5f * glm::length(boxScale); + float boxAngle = boxBoundingRadius / boxDistance; // sine of small angles approximation + AABox box(center, boxScale); + + // farPlane + localOffset = (boxDistance - boxBoundingRadius - delta) * localForward; + boxCenter = center + rotation * localOffset; + box.setBox(boxCenter - 0.5f * boxScale, boxScale); + QCOMPARE(view.boxIntersectsFrustum(box), true); // inside + + localOffset = boxDistance * localForward; + boxCenter = center + rotation * localOffset; + box.setBox(boxCenter - 0.5f * boxScale, boxScale); + QCOMPARE(view.boxIntersectsFrustum(box), true); // straddle + + localOffset = (boxDistance + boxBoundingRadius + delta) * localForward; + boxCenter = center + rotation * localOffset; + box.setBox(boxCenter - 0.5f * boxScale, boxScale); + QCOMPARE(view.boxIntersectsFrustum(box), false); // outside + + // nearPlane + localOffset = (nearClip + 2.0f * boxBoundingRadius + delta) * localForward; + boxCenter = center + rotation * localOffset; + box.setBox(boxCenter - 0.5f * boxScale, boxScale); + QCOMPARE(view.boxIntersectsFrustum(box), true); // inside + + localOffset = nearClip * localForward; + boxCenter = center + rotation * localOffset; + box.setBox(boxCenter - 0.5f * boxScale, boxScale); + QCOMPARE(view.boxIntersectsFrustum(box), true); // straddle + + localOffset = (nearClip - boxBoundingRadius - delta) * localForward; + boxCenter = center + rotation * localOffset; + box.setBox(boxCenter - 0.5f * boxScale, boxScale); + QCOMPARE(view.boxIntersectsFrustum(box), false); // outside + + // topPlane + angle = 0.5f * fovY; + elevation = glm::angleAxis(angle - boxAngle - deltaAngle, localRight); + localOffset = elevation * (boxDistance * localForward); + boxCenter = center + rotation * localOffset; + box.setBox(boxCenter - 0.5f * boxScale, boxScale); + QCOMPARE(view.boxIntersectsFrustum(box), true); // inside + + elevation = glm::angleAxis(angle, localRight); + localOffset = elevation * (boxDistance * localForward); + boxCenter = center + rotation * localOffset; + box.setBox(boxCenter - 0.5f * boxScale, boxScale); + QCOMPARE(view.boxIntersectsFrustum(box), true); // straddle + + elevation = glm::angleAxis(angle + boxAngle + deltaAngle, localRight); + localOffset = elevation * (boxDistance * localForward); + boxCenter = center + rotation * localOffset; + box.setBox(boxCenter - 0.5f * boxScale, boxScale); + QCOMPARE(view.boxIntersectsFrustum(box), false); // outside + + // bottom plane + angle = -0.5f * fovY; + elevation = glm::angleAxis(angle + boxAngle + deltaAngle, localRight); + localOffset = elevation * (boxDistance * localForward); + boxCenter = center + rotation * localOffset; + box.setBox(boxCenter - 0.5f * boxScale, boxScale); + QCOMPARE(view.boxIntersectsFrustum(box), true); // inside + + elevation = glm::angleAxis(angle, localRight); + localOffset = elevation * (boxDistance * localForward); + boxCenter = center + rotation * localOffset; + box.setBox(boxCenter - 0.5f * boxScale, boxScale); + QCOMPARE(view.boxIntersectsFrustum(box), true); // straddle + + elevation = glm::angleAxis(angle - boxAngle - deltaAngle, localRight); + localOffset = elevation * (boxDistance * localForward); + boxCenter = center + rotation * localOffset; + box.setBox(boxCenter - 0.5f * boxScale, boxScale); + QCOMPARE(view.boxIntersectsFrustum(box), false); // outside + + // right plane + angle = 0.5f * fovX; + swing = glm::angleAxis(angle - boxAngle - deltaAngle, localUp); + localOffset = swing * (boxDistance * localForward); + boxCenter = center + rotation * localOffset; + box.setBox(boxCenter - 0.5f * boxScale, boxScale); + QCOMPARE(view.boxIntersectsFrustum(box), true); // inside + + swing = glm::angleAxis(angle, localUp); + localOffset = swing * (boxDistance * localForward); + boxCenter = center + rotation * localOffset; + box.setBox(boxCenter - 0.5f * boxScale, boxScale); + QCOMPARE(view.boxIntersectsFrustum(box), true); // straddle + + swing = glm::angleAxis(angle + boxAngle + deltaAngle, localUp); + localOffset = swing * (boxDistance * localForward); + boxCenter = center + rotation * localOffset; + box.setBox(boxCenter - 0.5f * boxScale, boxScale); + QCOMPARE(view.boxIntersectsFrustum(box), false); // outside + + // left plane + angle = -0.5f * fovX; + swing = glm::angleAxis(angle + boxAngle + deltaAngle, localUp); + localOffset = swing * (boxDistance * localForward); + boxCenter = center + rotation * localOffset; + box.setBox(boxCenter - 0.5f * boxScale, boxScale); + QCOMPARE(view.boxIntersectsFrustum(box), true); // inside + + swing = glm::angleAxis(angle, localUp); + localOffset = swing * (boxDistance * localForward); + boxCenter = center + rotation * localOffset; + box.setBox(boxCenter - 0.5f * boxScale, boxScale); + QCOMPARE(view.boxIntersectsFrustum(box), true); // straddle + + swing = glm::angleAxis(angle - boxAngle - deltaAngle, localUp); + localOffset = swing * (boxDistance * localForward); + boxCenter = center + rotation * localOffset; + box.setBox(boxCenter - 0.5f * boxScale, boxScale); + QCOMPARE(view.boxIntersectsFrustum(box), false); // outside +} + +void ViewFrustumTests::testSphereIntersectsKeyhole() { + float aspect = 1.0f; + float fovX = PI / 2.0f; + float fovY = 2.0f * asinf(sinf(0.5f * fovX) / aspect); + float nearClip = 1.0f; + float farClip = 100.0f; + float holeRadius = 10.0f; + + glm::vec3 center = glm::vec3(12.3f, 4.56f, 89.7f); + + float angle = PI / 7.0f; + glm::vec3 axis = Vectors::UNIT_Y; + glm::quat rotation = glm::angleAxis(angle, axis); + + ViewFrustum view; + view.setProjection(glm::perspective(fovX, aspect, nearClip, farClip)); + view.setPosition(center); + view.setOrientation(rotation); + view.setCenterRadius(holeRadius); + view.calculate(); + + float delta = 0.1f; + float deltaAngle = 0.01f; + glm::quat elevation, swing; + glm::vec3 sphereCenter, localOffset; + + float sphereRadius = 2.68f; // must be much smaller than sphereDistance for small angle approx below + float sphereDistance = farClip; + float sphereAngle = sphereRadius / sphereDistance; // sine of small angles approximation + + // farPlane + localOffset = (sphereDistance - sphereRadius - delta) * localForward; + sphereCenter = center + rotation * localOffset; + QCOMPARE(view.sphereIntersectsKeyhole(sphereCenter, sphereRadius), true); // inside + + localOffset = sphereDistance * localForward; + sphereCenter = center + rotation * localOffset; + QCOMPARE(view.sphereIntersectsKeyhole(sphereCenter, sphereRadius), true); // straddle + + localOffset = (sphereDistance + sphereRadius + delta) * localForward; + sphereCenter = center + rotation * localOffset; + QCOMPARE(view.sphereIntersectsKeyhole(sphereCenter, sphereRadius), false); // outside + + // nearPlane + localOffset = (nearClip + 2.0f * sphereRadius + delta) * localForward; + sphereCenter = center + rotation * localOffset; + QCOMPARE(view.sphereIntersectsKeyhole(sphereCenter, sphereRadius), true); // inside + + localOffset = (nearClip - sphereRadius + delta) * localForward; + sphereCenter = center + rotation * localOffset; + QCOMPARE(view.sphereIntersectsKeyhole(sphereCenter, sphereRadius), true); // straddle + + localOffset = (nearClip - sphereRadius - delta) * localForward; + sphereCenter = center + rotation * localOffset; + QCOMPARE(view.sphereIntersectsKeyhole(sphereCenter, sphereRadius), true); // touches central sphere + + // topPlane + angle = 0.5f * fovY - sphereAngle; + elevation = glm::angleAxis(angle - deltaAngle, localRight); + localOffset = elevation * (sphereDistance * localForward); + sphereCenter = center + rotation * localOffset; + QCOMPARE(view.sphereIntersectsKeyhole(sphereCenter, sphereRadius), true); // inside + + angle = 0.5f * fovY + sphereAngle; + elevation = glm::angleAxis(angle - deltaAngle, localRight); + localOffset = elevation * (sphereDistance * localForward); + sphereCenter = center + rotation * localOffset; + QCOMPARE(view.sphereIntersectsKeyhole(sphereCenter, sphereRadius), true); // straddle + + elevation = glm::angleAxis(angle + deltaAngle, localRight); + localOffset = elevation * (sphereDistance * localForward); + sphereCenter = center + rotation * localOffset; + QCOMPARE(view.sphereIntersectsKeyhole(sphereCenter, sphereRadius), false); // outside + + // bottom plane + angle = -0.5f * fovY + sphereAngle; + elevation = glm::angleAxis(angle + deltaAngle, localRight); + localOffset = elevation * (sphereDistance * localForward); + sphereCenter = center + rotation * localOffset; + QCOMPARE(view.sphereIntersectsKeyhole(sphereCenter, sphereRadius), true); // inside + + angle = -0.5f * fovY - sphereAngle; + elevation = glm::angleAxis(angle + deltaAngle, localRight); + localOffset = elevation * (sphereDistance * localForward); + sphereCenter = center + rotation * localOffset; + QCOMPARE(view.sphereIntersectsKeyhole(sphereCenter, sphereRadius), true); // straddle + + elevation = glm::angleAxis(angle - deltaAngle, localRight); + localOffset = elevation * (sphereDistance * localForward); + sphereCenter = center + rotation * localOffset; + QCOMPARE(view.sphereIntersectsKeyhole(sphereCenter, sphereRadius), false); // outside + + // right plane + angle = 0.5f * fovX - sphereAngle; + swing = glm::angleAxis(angle - deltaAngle, localUp); + localOffset = swing * (sphereDistance * localForward); + sphereCenter = center + rotation * localOffset; + QCOMPARE(view.sphereIntersectsKeyhole(sphereCenter, sphereRadius), true); // inside + + angle = 0.5f * fovX + sphereAngle; + swing = glm::angleAxis(angle - deltaAngle, localUp); + localOffset = swing * (sphereDistance * localForward); + sphereCenter = center + rotation * localOffset; + QCOMPARE(view.sphereIntersectsKeyhole(sphereCenter, sphereRadius), true); // straddle + + swing = glm::angleAxis(angle + deltaAngle, localUp); + localOffset = swing * (sphereDistance * localForward); + sphereCenter = center + rotation * localOffset; + QCOMPARE(view.sphereIntersectsKeyhole(sphereCenter, sphereRadius), false); // outside + + // left plane + angle = -0.5f * fovX + sphereAngle; + swing = glm::angleAxis(angle + deltaAngle, localUp); + localOffset = swing * (sphereDistance * localForward); + sphereCenter = center + rotation * localOffset; + QCOMPARE(view.sphereIntersectsKeyhole(sphereCenter, sphereRadius), true); // inside + + angle = -0.5f * fovX - sphereAngle; + swing = glm::angleAxis(angle + deltaAngle, localUp); + localOffset = swing * (sphereDistance * localForward); + sphereCenter = center + rotation * localOffset; + QCOMPARE(view.sphereIntersectsKeyhole(sphereCenter, sphereRadius), true); // straddle + + swing = glm::angleAxis(angle - sphereAngle - deltaAngle, localUp); + localOffset = swing * (sphereDistance * localForward); + sphereCenter = center + rotation * localOffset; + QCOMPARE(view.sphereIntersectsKeyhole(sphereCenter, sphereRadius), false); // outside + + // central sphere right + localOffset = (holeRadius - sphereRadius - delta) * localRight; + sphereCenter = center + rotation * localOffset; + QCOMPARE(view.sphereIntersectsKeyhole(sphereCenter, sphereRadius), true); // inside right + + localOffset = holeRadius * localRight; + sphereCenter = center + rotation * localOffset; + QCOMPARE(view.sphereIntersectsKeyhole(sphereCenter, sphereRadius), true); // straddle right + + localOffset = (holeRadius + sphereRadius + delta) * localRight; + sphereCenter = center + rotation * localOffset; + QCOMPARE(view.sphereIntersectsKeyhole(sphereCenter, sphereRadius), false); // outside right + + // central sphere up + localOffset = (holeRadius - sphereRadius - delta) * localUp; + sphereCenter = center + rotation * localOffset; + QCOMPARE(view.sphereIntersectsKeyhole(sphereCenter, sphereRadius), true); // inside up + + localOffset = holeRadius * localUp; + sphereCenter = center + rotation * localOffset; + QCOMPARE(view.sphereIntersectsKeyhole(sphereCenter, sphereRadius), true); // straddle up + + localOffset = (holeRadius + sphereRadius + delta) * localUp; + sphereCenter = center + rotation * localOffset; + QCOMPARE(view.sphereIntersectsKeyhole(sphereCenter, sphereRadius), false); // outside up + + // central sphere back + localOffset = (-holeRadius + sphereRadius + delta) * localForward; + sphereCenter = center + rotation * localOffset; + QCOMPARE(view.sphereIntersectsKeyhole(sphereCenter, sphereRadius), true); // inside back + + localOffset = - holeRadius * localForward; + sphereCenter = center + rotation * localOffset; + QCOMPARE(view.sphereIntersectsKeyhole(sphereCenter, sphereRadius), true); // straddle back + + localOffset = (-holeRadius - sphereRadius - delta) * localForward; + sphereCenter = center + rotation * localOffset; + QCOMPARE(view.sphereIntersectsKeyhole(sphereCenter, sphereRadius), false); // outside back +} + +void ViewFrustumTests::testCubeIntersectsKeyhole() { + float aspect = 1.0f; + float fovX = PI / 2.0f; + float fovY = 2.0f * asinf(sinf(0.5f * fovX) / aspect); + float nearClip = 1.0f; + float farClip = 100.0f; + float holeRadius = 10.0f; + + glm::vec3 center = glm::vec3(12.3f, 4.56f, 89.7f); + + float angle = PI / 7.0f; + glm::vec3 axis = Vectors::UNIT_Y; + glm::quat rotation = glm::angleAxis(angle, axis); + + ViewFrustum view; + view.setProjection(glm::perspective(fovX, aspect, nearClip, farClip)); + view.setPosition(center); + view.setOrientation(rotation); + view.setCenterRadius(holeRadius); + view.calculate(); + + float delta = 0.1f; + float deltaAngle = 0.01f; + glm::quat elevation, swing; + glm::vec3 cubeCenter, localOffset; + + float cubeScale = 2.68f; // must be much smaller than cubeDistance for small angle approx below + glm::vec3 halfScaleOffset = 0.5f * glm::vec3(cubeScale); + float cubeDistance = farClip; + float cubeBoundingRadius = 0.5f * sqrtf(3.0f) * cubeScale; + float cubeAngle = cubeBoundingRadius / cubeDistance; // sine of small angles approximation + AACube cube(center, cubeScale); + + // farPlane + localOffset = (cubeDistance - cubeBoundingRadius - delta) * localForward; + cubeCenter = center + rotation * localOffset; + cube.setBox(cubeCenter - halfScaleOffset, cubeScale); + QCOMPARE(view.cubeIntersectsKeyhole(cube), true); + + localOffset = cubeDistance * localForward; + cubeCenter = center + rotation * localOffset; + cube.setBox(cubeCenter - halfScaleOffset, cubeScale); + QCOMPARE(view.cubeIntersectsKeyhole(cube), true); + + localOffset = (cubeDistance + cubeBoundingRadius + delta) * localForward; + cubeCenter = center + rotation * localOffset; + cube.setBox(cubeCenter - halfScaleOffset, cubeScale); + QCOMPARE(view.cubeIntersectsKeyhole(cube), false); + + // nearPlane + localOffset = (nearClip + 2.0f * cubeBoundingRadius + delta) * localForward; + cubeCenter = center + rotation * localOffset; + cube.setBox(cubeCenter - halfScaleOffset, cubeScale); + QCOMPARE(view.cubeIntersectsKeyhole(cube), true); // inside + + localOffset = (nearClip + delta) * localForward; + cubeCenter = center + rotation * localOffset; + cube.setBox(cubeCenter - halfScaleOffset, cubeScale); + QCOMPARE(view.cubeIntersectsKeyhole(cube), true); // straddle + + localOffset = (nearClip - cubeBoundingRadius - delta) * localForward; + cubeCenter = center + rotation * localOffset; + cube.setBox(cubeCenter - halfScaleOffset, cubeScale); + QCOMPARE(view.cubeIntersectsKeyhole(cube), true); // touches centeral sphere + + // topPlane + angle = 0.5f * fovY; + elevation = glm::angleAxis(angle - cubeAngle - deltaAngle, localRight); + localOffset = elevation * (cubeDistance * localForward); + cubeCenter = center + rotation * localOffset; + cube.setBox(cubeCenter - halfScaleOffset, cubeScale); + QCOMPARE(view.cubeIntersectsKeyhole(cube), true); // inside + + elevation = glm::angleAxis(angle, localRight); + localOffset = elevation * (cubeDistance * localForward); + cubeCenter = center + rotation * localOffset; + cube.setBox(cubeCenter - halfScaleOffset, cubeScale); + QCOMPARE(view.cubeIntersectsKeyhole(cube), true); // straddle + + elevation = glm::angleAxis(angle + cubeAngle + deltaAngle, localRight); + localOffset = elevation * (cubeDistance * localForward); + cubeCenter = center + rotation * localOffset; + cube.setBox(cubeCenter - halfScaleOffset, cubeScale); + QCOMPARE(view.cubeIntersectsKeyhole(cube), false); // outside + + // bottom plane + angle = -0.5f * fovY; + elevation = glm::angleAxis(angle + cubeAngle + deltaAngle, localRight); + localOffset = elevation * (cubeDistance * localForward); + cubeCenter = center + rotation * localOffset; + cube.setBox(cubeCenter - halfScaleOffset, cubeScale); + QCOMPARE(view.cubeIntersectsKeyhole(cube), true); // inside + + elevation = glm::angleAxis(angle, localRight); + localOffset = elevation * (cubeDistance * localForward); + cubeCenter = center + rotation * localOffset; + cube.setBox(cubeCenter - halfScaleOffset, cubeScale); + QCOMPARE(view.cubeIntersectsKeyhole(cube), true); // straddle + + elevation = glm::angleAxis(angle - cubeAngle - deltaAngle, localRight); + localOffset = elevation * (cubeDistance * localForward); + cubeCenter = center + rotation * localOffset; + cube.setBox(cubeCenter - halfScaleOffset, cubeScale); + QCOMPARE(view.cubeIntersectsKeyhole(cube), false); // outside + + // right plane + angle = 0.5f * fovX; + swing = glm::angleAxis(angle - cubeAngle - deltaAngle, localUp); + localOffset = swing * (cubeDistance * localForward); + cubeCenter = center + rotation * localOffset; + cube.setBox(cubeCenter - halfScaleOffset, cubeScale); + QCOMPARE(view.cubeIntersectsKeyhole(cube), true); // inside + + swing = glm::angleAxis(angle, localUp); + localOffset = swing * (cubeDistance * localForward); + cubeCenter = center + rotation * localOffset; + cube.setBox(cubeCenter - halfScaleOffset, cubeScale); + QCOMPARE(view.cubeIntersectsKeyhole(cube), true); // straddle + + swing = glm::angleAxis(angle + cubeAngle + deltaAngle, localUp); + localOffset = swing * (cubeDistance * localForward); + cubeCenter = center + rotation * localOffset; + cube.setBox(cubeCenter - halfScaleOffset, cubeScale); + QCOMPARE(view.cubeIntersectsKeyhole(cube), false); // outside + + // left plane + angle = -0.5f * fovX; + swing = glm::angleAxis(angle + cubeAngle + deltaAngle, localUp); + localOffset = swing * (cubeDistance * localForward); + cubeCenter = center + rotation * localOffset; + cube.setBox(cubeCenter - halfScaleOffset, cubeScale); + QCOMPARE(view.cubeIntersectsKeyhole(cube), true); // inside + + swing = glm::angleAxis(angle, localUp); + localOffset = swing * (cubeDistance * localForward); + cubeCenter = center + rotation * localOffset; + cube.setBox(cubeCenter - halfScaleOffset, cubeScale); + QCOMPARE(view.cubeIntersectsKeyhole(cube), true); // straddle + + swing = glm::angleAxis(angle - cubeAngle - deltaAngle, localUp); + localOffset = swing * (cubeDistance * localForward); + cubeCenter = center + rotation * localOffset; + cube.setBox(cubeCenter - halfScaleOffset, cubeScale); + QCOMPARE(view.cubeIntersectsKeyhole(cube), false); // outside + + // central sphere right + localOffset = (holeRadius - cubeBoundingRadius - delta) * localRight; + cubeCenter = center + rotation * localOffset; + cube.setBox(cubeCenter - halfScaleOffset, cubeScale); + QCOMPARE(view.cubeIntersectsKeyhole(cube), true); // inside right + + localOffset = holeRadius * localRight; + cubeCenter = center + rotation * localOffset; + cube.setBox(cubeCenter - halfScaleOffset, cubeScale); + QCOMPARE(view.cubeIntersectsKeyhole(cube), true); // straddle right + + localOffset = (holeRadius + cubeBoundingRadius + delta) * localRight; + cubeCenter = center + rotation * localOffset; + cube.setBox(cubeCenter - halfScaleOffset, cubeScale); + QCOMPARE(view.cubeIntersectsKeyhole(cube), false); // outside right + + // central sphere up + localOffset = (holeRadius - cubeBoundingRadius - delta) * localUp; + cubeCenter = center + rotation * localOffset; + cube.setBox(cubeCenter - halfScaleOffset, cubeScale); + QCOMPARE(view.cubeIntersectsKeyhole(cube), true); // inside up + + localOffset = holeRadius * localUp; + cubeCenter = center + rotation * localOffset; + cube.setBox(cubeCenter - halfScaleOffset, cubeScale); + QCOMPARE(view.cubeIntersectsKeyhole(cube), true); // straddle up + + localOffset = (holeRadius + cubeBoundingRadius + delta) * localUp; + cubeCenter = center + rotation * localOffset; + cube.setBox(cubeCenter - halfScaleOffset, cubeScale); + QCOMPARE(view.cubeIntersectsKeyhole(cube), false); // outside up + + // central sphere back + localOffset = (-holeRadius + cubeBoundingRadius + delta) * localForward; + cubeCenter = center + rotation * localOffset; + cube.setBox(cubeCenter - halfScaleOffset, cubeScale); + QCOMPARE(view.cubeIntersectsKeyhole(cube), true); // inside back + + localOffset = - holeRadius * localForward; + cubeCenter = center + rotation * localOffset; + cube.setBox(cubeCenter - halfScaleOffset, cubeScale); + QCOMPARE(view.cubeIntersectsKeyhole(cube), true); // straddle back + + localOffset = (-holeRadius - cubeBoundingRadius - delta) * localForward; + cubeCenter = center + rotation * localOffset; + cube.setBox(cubeCenter - halfScaleOffset, cubeScale); + QCOMPARE(view.cubeIntersectsKeyhole(cube), false); // outside back +} + +void ViewFrustumTests::testBoxIntersectsKeyhole() { + float aspect = 1.0f; + float fovX = PI / 2.0f; + float fovY = 2.0f * asinf(sinf(0.5f * fovX) / aspect); + float nearClip = 1.0f; + float farClip = 100.0f; + float holeRadius = 10.0f; + + glm::vec3 center = glm::vec3(12.3f, 4.56f, 89.7f); + + float angle = PI / 7.0f; + glm::vec3 axis = Vectors::UNIT_Y; + glm::quat rotation = glm::angleAxis(angle, axis); + + ViewFrustum view; + view.setProjection(glm::perspective(fovX, aspect, nearClip, farClip)); + view.setPosition(center); + view.setOrientation(rotation); + view.setCenterRadius(holeRadius); + view.calculate(); + + float delta = 0.1f; + float deltaAngle = 0.01f; + glm::quat elevation, swing; + glm::vec3 boxCenter, localOffset; + + glm::vec3 boxScale = glm::vec3(2.68f, 1.78f, 0.431f); // sides must be much smaller than boxDistance + glm::vec3 halfScaleOffset = 0.5f * boxScale; + float boxDistance = farClip; + float boxBoundingRadius = 0.5f * glm::length(boxScale); + float boxAngle = boxBoundingRadius / boxDistance; // sine of small angles approximation + AABox box(center, boxScale); + + // farPlane + localOffset = (boxDistance - boxBoundingRadius - delta) * localForward; + boxCenter = center + rotation * localOffset; + box.setBox(boxCenter - halfScaleOffset, boxScale); + QCOMPARE(view.boxIntersectsKeyhole(box), true); + + localOffset = boxDistance * localForward; + boxCenter = center + rotation * localOffset; + box.setBox(boxCenter - halfScaleOffset, boxScale); + QCOMPARE(view.boxIntersectsKeyhole(box), true); + + localOffset = (boxDistance + boxBoundingRadius + delta) * localForward; + boxCenter = center + rotation * localOffset; + box.setBox(boxCenter - halfScaleOffset, boxScale); + QCOMPARE(view.boxIntersectsKeyhole(box), false); + + // nearPlane + localOffset = (nearClip + 2.0f * boxBoundingRadius + delta) * localForward; + boxCenter = center + rotation * localOffset; + box.setBox(boxCenter - halfScaleOffset, boxScale); + QCOMPARE(view.boxIntersectsKeyhole(box), true); // inside + + localOffset = (nearClip + delta) * localForward; + boxCenter = center + rotation * localOffset; + box.setBox(boxCenter - halfScaleOffset, boxScale); + QCOMPARE(view.boxIntersectsKeyhole(box), true); // straddle + + localOffset = (nearClip - boxBoundingRadius - delta) * localForward; + boxCenter = center + rotation * localOffset; + box.setBox(boxCenter - halfScaleOffset, boxScale); + QCOMPARE(view.boxIntersectsKeyhole(box), true); // touches centeral sphere + + // topPlane + angle = 0.5f * fovY; + elevation = glm::angleAxis(angle - boxAngle - deltaAngle, localRight); + localOffset = elevation * (boxDistance * localForward); + boxCenter = center + rotation * localOffset; + box.setBox(boxCenter - halfScaleOffset, boxScale); + QCOMPARE(view.boxIntersectsKeyhole(box), true); // inside + + elevation = glm::angleAxis(angle, localRight); + localOffset = elevation * (boxDistance * localForward); + boxCenter = center + rotation * localOffset; + box.setBox(boxCenter - halfScaleOffset, boxScale); + QCOMPARE(view.boxIntersectsKeyhole(box), true); // straddle + + elevation = glm::angleAxis(angle + boxAngle + deltaAngle, localRight); + localOffset = elevation * (boxDistance * localForward); + boxCenter = center + rotation * localOffset; + box.setBox(boxCenter - halfScaleOffset, boxScale); + QCOMPARE(view.boxIntersectsKeyhole(box), false); // outside + + // bottom plane + angle = -0.5f * fovY; + elevation = glm::angleAxis(angle + boxAngle + deltaAngle, localRight); + localOffset = elevation * (boxDistance * localForward); + boxCenter = center + rotation * localOffset; + box.setBox(boxCenter - halfScaleOffset, boxScale); + QCOMPARE(view.boxIntersectsKeyhole(box), true); // inside + + elevation = glm::angleAxis(angle, localRight); + localOffset = elevation * (boxDistance * localForward); + boxCenter = center + rotation * localOffset; + box.setBox(boxCenter - halfScaleOffset, boxScale); + QCOMPARE(view.boxIntersectsKeyhole(box), true); // straddle + + elevation = glm::angleAxis(angle - boxAngle - deltaAngle, localRight); + localOffset = elevation * (boxDistance * localForward); + boxCenter = center + rotation * localOffset; + box.setBox(boxCenter - halfScaleOffset, boxScale); + QCOMPARE(view.boxIntersectsKeyhole(box), false); // outside + + // right plane + angle = 0.5f * fovX; + swing = glm::angleAxis(angle - boxAngle - deltaAngle, localUp); + localOffset = swing * (boxDistance * localForward); + boxCenter = center + rotation * localOffset; + box.setBox(boxCenter - halfScaleOffset, boxScale); + QCOMPARE(view.boxIntersectsKeyhole(box), true); // inside + + swing = glm::angleAxis(angle, localUp); + localOffset = swing * (boxDistance * localForward); + boxCenter = center + rotation * localOffset; + box.setBox(boxCenter - halfScaleOffset, boxScale); + QCOMPARE(view.boxIntersectsKeyhole(box), true); // straddle + + swing = glm::angleAxis(angle + boxAngle + deltaAngle, localUp); + localOffset = swing * (boxDistance * localForward); + boxCenter = center + rotation * localOffset; + box.setBox(boxCenter - halfScaleOffset, boxScale); + QCOMPARE(view.boxIntersectsKeyhole(box), false); // outside + + // left plane + angle = -0.5f * fovX; + swing = glm::angleAxis(angle + boxAngle + deltaAngle, localUp); + localOffset = swing * (boxDistance * localForward); + boxCenter = center + rotation * localOffset; + box.setBox(boxCenter - halfScaleOffset, boxScale); + QCOMPARE(view.boxIntersectsKeyhole(box), true); // inside + + swing = glm::angleAxis(angle, localUp); + localOffset = swing * (boxDistance * localForward); + boxCenter = center + rotation * localOffset; + box.setBox(boxCenter - halfScaleOffset, boxScale); + QCOMPARE(view.boxIntersectsKeyhole(box), true); // straddle + + swing = glm::angleAxis(angle - boxAngle - deltaAngle, localUp); + localOffset = swing * (boxDistance * localForward); + boxCenter = center + rotation * localOffset; + box.setBox(boxCenter - halfScaleOffset, boxScale); + QCOMPARE(view.boxIntersectsKeyhole(box), false); // outside + + // central sphere right + localOffset = (holeRadius - boxBoundingRadius - delta) * localRight; + boxCenter = center + rotation * localOffset; + box.setBox(boxCenter - halfScaleOffset, boxScale); + QCOMPARE(view.boxIntersectsKeyhole(box), true); // inside right + + localOffset = holeRadius * localRight; + boxCenter = center + rotation * localOffset; + box.setBox(boxCenter - halfScaleOffset, boxScale); + QCOMPARE(view.boxIntersectsKeyhole(box), true); // straddle right + + localOffset = (holeRadius + boxBoundingRadius + delta) * localRight; + boxCenter = center + rotation * localOffset; + box.setBox(boxCenter - halfScaleOffset, boxScale); + QCOMPARE(view.boxIntersectsKeyhole(box), false); // outside right + + // central sphere up + localOffset = (holeRadius - boxBoundingRadius - delta) * localUp; + boxCenter = center + rotation * localOffset; + box.setBox(boxCenter - halfScaleOffset, boxScale); + QCOMPARE(view.boxIntersectsKeyhole(box), true); // inside up + + localOffset = holeRadius * localUp; + boxCenter = center + rotation * localOffset; + box.setBox(boxCenter - halfScaleOffset, boxScale); + QCOMPARE(view.boxIntersectsKeyhole(box), true); // straddle up + + localOffset = (holeRadius + boxBoundingRadius + delta) * localUp; + boxCenter = center + rotation * localOffset; + box.setBox(boxCenter - halfScaleOffset, boxScale); + QCOMPARE(view.boxIntersectsKeyhole(box), false); // outside up + + // central sphere back + localOffset = (-holeRadius + boxBoundingRadius + delta) * localForward; + boxCenter = center + rotation * localOffset; + box.setBox(boxCenter - halfScaleOffset, boxScale); + QCOMPARE(view.boxIntersectsKeyhole(box), true); // inside back + + localOffset = - holeRadius * localForward; + boxCenter = center + rotation * localOffset; + box.setBox(boxCenter - halfScaleOffset, boxScale); + QCOMPARE(view.boxIntersectsKeyhole(box), true); // straddle back + + localOffset = (-holeRadius - boxBoundingRadius - delta) * localForward; + boxCenter = center + rotation * localOffset; + box.setBox(boxCenter - halfScaleOffset, boxScale); + QCOMPARE(view.boxIntersectsKeyhole(box), false); // outside back +} diff --git a/tests/octree/src/ViewFrustumTests.h b/tests/octree/src/ViewFrustumTests.h new file mode 100644 index 0000000000..a64a72e669 --- /dev/null +++ b/tests/octree/src/ViewFrustumTests.h @@ -0,0 +1,32 @@ +// +// ViewFrustumTests.h +// tests/octree/src +// +// Created by Andrew Meadows on 2016.02.19 +// Copyright 2016 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 +// + +#ifndef hifi_ViewFruxtumTests_h +#define hifi_ViewFruxtumTests_h + +#include + +class ViewFrustumTests : public QObject { + Q_OBJECT + +private slots: + void testInit(); + void testCubeFrustumIntersection(); + void testCubeKeyholeIntersection(); + void testPointIntersectsFrustum(); + void testSphereIntersectsFrustum(); + void testBoxIntersectsFrustum(); + void testSphereIntersectsKeyhole(); + void testCubeIntersectsKeyhole(); + void testBoxIntersectsKeyhole(); +}; + +#endif // hifi_ViewFruxtumTests_h diff --git a/tests/shared/src/AABoxTests.cpp b/tests/shared/src/AABoxTests.cpp new file mode 100644 index 0000000000..fd709a488c --- /dev/null +++ b/tests/shared/src/AABoxTests.cpp @@ -0,0 +1,153 @@ +// +// AABoxTests.cpp +// tests/shared/src +// +// Created by Andrew Meadows on 2016.02.19 +// Copyright 2016 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 +// + +#include + +#include "AABoxTests.h" + +#include +#include +#include + +#include <../GLMTestUtils.h> +#include <../QTestExtensions.h> + + +QTEST_MAIN(AABoxTests) + +void AABoxTests::testCtorsAndSetters() { + const glm::vec3 corner(1.23f, 4.56f, 7.89f); + const glm::vec3 scale(2.34f, 7.53f, 9.14f); + + // test ctor + AABox box(corner, scale); + QCOMPARE_WITH_ABS_ERROR(box.getCorner(), corner, EPSILON); + QCOMPARE_WITH_ABS_ERROR(box.getScale(), scale, EPSILON); + + // test copy ctor + AABox copyBox(box); + QCOMPARE_WITH_ABS_ERROR(copyBox.getCorner(), corner, EPSILON); + QCOMPARE_WITH_ABS_ERROR(copyBox.getScale(), scale, EPSILON); + + // test setBox() + const glm::vec3 newCorner(9.87f, 6.54f, 3.21f); + const glm::vec3 newScale = glm::vec3(4.32f, 8.95f, 10.31f); + box.setBox(newCorner, newScale); + QCOMPARE_WITH_ABS_ERROR(box.getCorner(), newCorner, EPSILON); + QCOMPARE_WITH_ABS_ERROR(box.getScale(), newScale, EPSILON); + + // test misc + QCOMPARE_WITH_ABS_ERROR(newCorner, box.getMinimumPoint(), EPSILON); + + glm::vec3 expectedMaxCorner = newCorner + glm::vec3(newScale); + QCOMPARE_WITH_ABS_ERROR(expectedMaxCorner, box.getMaximumPoint(), EPSILON); + + glm::vec3 expectedCenter = newCorner + glm::vec3(0.5f * newScale); + QCOMPARE_WITH_ABS_ERROR(expectedCenter, box.calcCenter(), EPSILON); +} + +void AABoxTests::testContainsPoint() { + const glm::vec3 corner(4.56f, 7.89f, -1.35f); + const glm::vec3 scale(2.34f, 7.53f, 9.14f); + AABox box(corner, scale); + + float delta = 0.00001f; + glm::vec3 center = box.calcCenter(); + QCOMPARE(box.contains(center), true); + + for (int i = 0; i < 3; ++i) { + glm::vec3 halfScale = Vectors::ZERO; + halfScale[i] = 0.5f * scale[i]; + glm::vec3 deltaOffset = Vectors::ZERO; + deltaOffset[i] = delta; + + QCOMPARE(box.contains(center + halfScale + deltaOffset), false); // outside +face + QCOMPARE(box.contains(center + halfScale - deltaOffset), true); // inside +face + QCOMPARE(box.contains(center - halfScale + deltaOffset), true); // inside -face + QCOMPARE(box.contains(center - halfScale - deltaOffset), false); // outside -face + } +} + +void AABoxTests::testTouchesSphere() { + glm::vec3 corner(-4.56f, 7.89f, -1.35f); + float scale = 1.23f; + AABox box(corner, scale); + + float delta = 0.00001f; + glm::vec3 cubeCenter = box.calcCenter(); + float sphereRadius = 0.468f; + + for (int i = 0; i < 3; ++i) { + int j = (i + 1) % 3; + int k = (j + 1) % 3; + + { // faces + glm::vec3 scaleOffset = Vectors::ZERO; + scaleOffset[i] = 0.5f * scale + sphereRadius; + + glm::vec3 deltaOffset = Vectors::ZERO; + deltaOffset[i] = delta; + + // outside +face + glm::vec3 sphereCenter = cubeCenter + scaleOffset + deltaOffset; + QCOMPARE(box.touchesSphere(sphereCenter, sphereRadius), false); + + // inside +face + sphereCenter = cubeCenter + scaleOffset - deltaOffset; + QCOMPARE(box.touchesSphere(sphereCenter, sphereRadius), true); + + // inside -face + sphereCenter = cubeCenter - scaleOffset + deltaOffset; + QCOMPARE(box.touchesSphere(sphereCenter, sphereRadius), true); + + // outside -face + sphereCenter = cubeCenter - scaleOffset - deltaOffset; + QCOMPARE(box.touchesSphere(sphereCenter, sphereRadius), false); + } + + { // edges + glm::vec3 edgeOffset = Vectors::ZERO; + edgeOffset[i] = 0.5f * scale; + edgeOffset[j] = 0.5f * scale; + glm::vec3 edgeDirection = glm::normalize(edgeOffset); + glm::vec3 sphereCenter; + + // inside ij + sphereCenter = cubeCenter + edgeOffset + (sphereRadius - delta) * edgeDirection; + QCOMPARE(box.touchesSphere(sphereCenter, sphereRadius), true); + sphereCenter = cubeCenter - edgeOffset - (sphereRadius - delta) * edgeDirection; + QCOMPARE(box.touchesSphere(sphereCenter, sphereRadius), true); + + // outside ij + sphereCenter = cubeCenter + edgeOffset + (sphereRadius + delta) * edgeDirection; + QCOMPARE(box.touchesSphere(sphereCenter, sphereRadius), false); + sphereCenter = cubeCenter - edgeOffset - (sphereRadius + delta) * edgeDirection; + QCOMPARE(box.touchesSphere(sphereCenter, sphereRadius), false); + + edgeOffset[j] = 0.0f; + edgeOffset[k] = 0.5f * scale; + edgeDirection = glm::normalize(edgeOffset); + + // inside ik + sphereCenter = cubeCenter + edgeOffset + (sphereRadius - delta) * edgeDirection; + QCOMPARE(box.touchesSphere(sphereCenter, sphereRadius), true); + sphereCenter = cubeCenter - edgeOffset - (sphereRadius - delta) * edgeDirection; + QCOMPARE(box.touchesSphere(sphereCenter, sphereRadius), true); + + // outside ik + sphereCenter = cubeCenter + edgeOffset + (sphereRadius + delta) * edgeDirection; + QCOMPARE(box.touchesSphere(sphereCenter, sphereRadius), false); + sphereCenter = cubeCenter - edgeOffset - (sphereRadius + delta) * edgeDirection; + QCOMPARE(box.touchesSphere(sphereCenter, sphereRadius), false); + } + } +} + diff --git a/tests/shared/src/AABoxTests.h b/tests/shared/src/AABoxTests.h new file mode 100644 index 0000000000..fe7afede57 --- /dev/null +++ b/tests/shared/src/AABoxTests.h @@ -0,0 +1,28 @@ +// +// AABoxTests.h +// tests/shared/src +// +// Created by Andrew Meadows on 2016.02.19 +// Copyright 2016 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 +// + +#ifndef hifi_AABoxTests_h +#define hifi_AABoxTests_h + +#include +#include + +#include + +class AABoxTests : public QObject { + Q_OBJECT +private slots: + void testCtorsAndSetters(); + void testContainsPoint(); + void testTouchesSphere(); +}; + +#endif // hifi_AABoxTests_h diff --git a/tests/shared/src/AACubeTests.cpp b/tests/shared/src/AACubeTests.cpp new file mode 100644 index 0000000000..177daf89f1 --- /dev/null +++ b/tests/shared/src/AACubeTests.cpp @@ -0,0 +1,154 @@ +// +// AACubeTests.cpp +// tests/shared/src +// +// Created by Andrew Meadows on 2016.02.19 +// Copyright 2016 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 +// + +#include + +#include "AACubeTests.h" + +#include +#include +#include + +#include <../GLMTestUtils.h> +#include <../QTestExtensions.h> + + +QTEST_MAIN(AACubeTests) + +void AACubeTests::ctorsAndSetters() { + const glm::vec3 corner(1.23f, 4.56f, 7.89f); + const float scale = 2.34f; + + // test ctor + AACube cube(corner, scale); + QCOMPARE_WITH_ABS_ERROR(cube.getCorner(), corner, EPSILON); + QCOMPARE_WITH_ABS_ERROR(cube.getScale(), scale, EPSILON); + + // test copy ctor + AACube copyCube(cube); + QCOMPARE_WITH_ABS_ERROR(copyCube.getCorner(), corner, EPSILON); + QCOMPARE_WITH_ABS_ERROR(copyCube.getScale(), scale, EPSILON); + + // test setBox() + const glm::vec3 newCorner(9.87f, 6.54f, 3.21f); + const float newScale = 4.32f; + cube.setBox(newCorner, newScale); + QCOMPARE_WITH_ABS_ERROR(cube.getCorner(), newCorner, EPSILON); + QCOMPARE_WITH_ABS_ERROR(cube.getScale(), newScale, EPSILON); + + // test misc + QCOMPARE_WITH_ABS_ERROR(cube.getMinimumPoint(), newCorner, EPSILON); + + glm::vec3 expectedMaxCorner = newCorner + glm::vec3(newScale); + QCOMPARE_WITH_ABS_ERROR(cube.getMaximumPoint(), expectedMaxCorner, EPSILON); + + glm::vec3 expectedCenter = newCorner + glm::vec3(0.5f * newScale); + QCOMPARE_WITH_ABS_ERROR(cube.calcCenter(), expectedCenter, EPSILON); +} + +void AACubeTests::containsPoint() { + const glm::vec3 corner(4.56f, 7.89f, -1.35f); + const float scale = 1.23f; + AACube cube(corner, scale); + + const float delta = scale / 1000.0f; + const glm::vec3 center = cube.calcCenter(); + QCOMPARE(cube.contains(center), true); + + for (int i = 0; i < 3; ++i) { + glm::vec3 scaleOffset = Vectors::ZERO; + scaleOffset[i] = 0.5f * scale; + + glm::vec3 deltaOffset = Vectors::ZERO; + deltaOffset[i] = delta; + + QCOMPARE(cube.contains(center + scaleOffset + deltaOffset), false); // outside +face + QCOMPARE(cube.contains(center + scaleOffset - deltaOffset), true); // inside +face + QCOMPARE(cube.contains(center - scaleOffset + deltaOffset), true); // inside -face + QCOMPARE(cube.contains(center - scaleOffset - deltaOffset), false); // outside -face + } +} + +void AACubeTests::touchesSphere() { + const glm::vec3 corner(-4.56f, 7.89f, -1.35f); + const float scale = 1.23f; + AACube cube(corner, scale); + + const float delta = scale / 1000.0f; + const glm::vec3 cubeCenter = cube.calcCenter(); + const float sphereRadius = 0.468f; + + for (int i = 0; i < 3; ++i) { + int j = (i + 1) % 3; + int k = (j + 1) % 3; + + { // faces + glm::vec3 scaleOffset = Vectors::ZERO; + scaleOffset[i] = 0.5f * scale + sphereRadius; + + glm::vec3 deltaOffset = Vectors::ZERO; + deltaOffset[i] = delta; + + // outside +face + glm::vec3 sphereCenter = cubeCenter + scaleOffset + deltaOffset; + QCOMPARE(cube.touchesSphere(sphereCenter, sphereRadius), false); + + // inside +face + sphereCenter = cubeCenter + scaleOffset - deltaOffset; + QCOMPARE(cube.touchesSphere(sphereCenter, sphereRadius), true); + + // inside -face + sphereCenter = cubeCenter - scaleOffset + deltaOffset; + QCOMPARE(cube.touchesSphere(sphereCenter, sphereRadius), true); + + // outside -face + sphereCenter = cubeCenter - scaleOffset - deltaOffset; + QCOMPARE(cube.touchesSphere(sphereCenter, sphereRadius), false); + } + + { // edges + glm::vec3 edgeOffset = Vectors::ZERO; + edgeOffset[i] = 0.5f * scale; + edgeOffset[j] = 0.5f * scale; + glm::vec3 edgeDirection = glm::normalize(edgeOffset); + glm::vec3 sphereCenter; + + // inside ij + sphereCenter = cubeCenter + edgeOffset + (sphereRadius - delta) * edgeDirection; + QCOMPARE(cube.touchesSphere(sphereCenter, sphereRadius), true); + sphereCenter = cubeCenter - edgeOffset - (sphereRadius - delta) * edgeDirection; + QCOMPARE(cube.touchesSphere(sphereCenter, sphereRadius), true); + + // outside ij + sphereCenter = cubeCenter + edgeOffset + (sphereRadius + delta) * edgeDirection; + QCOMPARE(cube.touchesSphere(sphereCenter, sphereRadius), false); + sphereCenter = cubeCenter - edgeOffset - (sphereRadius + delta) * edgeDirection; + QCOMPARE(cube.touchesSphere(sphereCenter, sphereRadius), false); + + edgeOffset[j] = 0.0f; + edgeOffset[k] = 0.5f * scale; + edgeDirection = glm::normalize(edgeOffset); + + // inside ik + sphereCenter = cubeCenter + edgeOffset + (sphereRadius - delta) * edgeDirection; + QCOMPARE(cube.touchesSphere(sphereCenter, sphereRadius), true); + sphereCenter = cubeCenter - edgeOffset - (sphereRadius - delta) * edgeDirection; + QCOMPARE(cube.touchesSphere(sphereCenter, sphereRadius), true); + + // outside ik + sphereCenter = cubeCenter + edgeOffset + (sphereRadius + delta) * edgeDirection; + QCOMPARE(cube.touchesSphere(sphereCenter, sphereRadius), false); + sphereCenter = cubeCenter - edgeOffset - (sphereRadius + delta) * edgeDirection; + QCOMPARE(cube.touchesSphere(sphereCenter, sphereRadius), false); + } + } +} + diff --git a/tests/shared/src/AACubeTests.h b/tests/shared/src/AACubeTests.h new file mode 100644 index 0000000000..a2b2e08cc5 --- /dev/null +++ b/tests/shared/src/AACubeTests.h @@ -0,0 +1,28 @@ +// +// AACubeTests.h +// tests/shared/src +// +// Created by Andrew Meadows on 2016.02.19 +// Copyright 2016 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 +// + +#ifndef hifi_AACubeTests_h +#define hifi_AACubeTests_h + +#include +#include + +#include + +class AACubeTests : public QObject { + Q_OBJECT +private slots: + void ctorsAndSetters(); + void containsPoint(); + void touchesSphere(); +}; + +#endif // hifi_AACubeTests_h diff --git a/tests/shared/src/AngularConstraintTests.cpp b/tests/shared/src/AngularConstraintTests.cpp deleted file mode 100644 index a711bac709..0000000000 --- a/tests/shared/src/AngularConstraintTests.cpp +++ /dev/null @@ -1,316 +0,0 @@ -// -// AngularConstraintTests.cpp -// tests/physics/src -// -// Created by Andrew Meadows on 2014.05.30 -// Copyright 2014 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 -// - -#include "AngularConstraintTests.h" - -#include - -#include -#include -#include - -#include "../QTestExtensions.h" - - -QTEST_MAIN(AngularConstraintTests) - -void AngularConstraintTests::testHingeConstraint() { - float minAngle = -PI; - float maxAngle = 0.0f; - glm::vec3 yAxis(0.0f, 1.0f, 0.0f); - glm::vec3 minAngles(0.0f, -PI, 0.0f); - glm::vec3 maxAngles(0.0f, 0.0f, 0.0f); - - AngularConstraint* c = AngularConstraint::newAngularConstraint(minAngles, maxAngles); - QVERIFY2(c != nullptr, "newAngularConstraint should make a constraint"); - { // test in middle of constraint - float angle = 0.5f * (minAngle + maxAngle); - glm::quat rotation = glm::angleAxis(angle, yAxis); - - glm::quat newRotation = rotation; - bool constrained = c->clamp(newRotation); - - QVERIFY2(constrained == false, "HingeConstraint should not clamp()"); - QVERIFY2(rotation == newRotation, "HingeConstraint should not change rotation"); - } - { // test just inside min edge of constraint - float angle = minAngle + 10.0f * EPSILON; - glm::quat rotation = glm::angleAxis(angle, yAxis); - - glm::quat newRotation = rotation; - bool constrained = c->clamp(newRotation); - - QVERIFY2(!constrained, "HingeConstraint should not clamp()"); - QVERIFY2(newRotation == rotation, "HingeConstraint should not change rotation"); - } - { // test just inside max edge of constraint - float angle = maxAngle - 10.0f * EPSILON; - glm::quat rotation = glm::angleAxis(angle, yAxis); - - glm::quat newRotation = rotation; - bool constrained = c->clamp(newRotation); - - QVERIFY2(!constrained, "HingeConstraint should not clamp()"); - QVERIFY2(newRotation == rotation, "HingeConstraint should not change rotation"); - } - { // test just outside min edge of constraint - float angle = minAngle - 0.001f; - glm::quat rotation = glm::angleAxis(angle, yAxis); - - glm::quat newRotation = rotation; - bool constrained = c->clamp(newRotation); - glm::quat expectedRotation = glm::angleAxis(minAngle, yAxis); - - QVERIFY2(constrained, "HingeConstraint should clamp()"); - QVERIFY2(newRotation != rotation, "HingeConstraint should change rotation"); - QCOMPARE_WITH_ABS_ERROR(newRotation, expectedRotation, EPSILON); - } - { // test just outside max edge of constraint - float angle = maxAngle + 0.001f; - glm::quat rotation = glm::angleAxis(angle, yAxis); - - glm::quat newRotation = rotation; - bool constrained = c->clamp(newRotation); - - QVERIFY2(constrained, "HingeConstraint should clamp()"); - QVERIFY2(newRotation != rotation, "HingeConstraint should change rotation"); - QCOMPARE_WITH_ABS_ERROR(newRotation, rotation, EPSILON); - } - { // test far outside min edge of constraint (wraps around to max) - float angle = minAngle - 0.75f * (TWO_PI - (maxAngle - minAngle)); - glm::quat rotation = glm::angleAxis(angle, yAxis); - - glm::quat newRotation = rotation; - bool constrained = c->clamp(newRotation); - - glm::quat expectedRotation = glm::angleAxis(maxAngle, yAxis); - QVERIFY2(constrained, "HingeConstraint should clamp()"); - QVERIFY2(newRotation != rotation, "HingeConstraint should change rotation"); - QCOMPARE_WITH_ABS_ERROR(newRotation, expectedRotation, EPSILON); - } - { // test far outside max edge of constraint (wraps around to min) - float angle = maxAngle + 0.75f * (TWO_PI - (maxAngle - minAngle)); - glm::quat rotation = glm::angleAxis(angle, yAxis); - - glm::quat newRotation = rotation; - bool constrained = c->clamp(newRotation); - glm::quat expectedRotation = glm::angleAxis(minAngle, yAxis); - - QVERIFY2(constrained, "HingeConstraint should clamp()"); - QVERIFY2(newRotation != rotation, "HingeConstraint should change rotation"); - QCOMPARE_WITH_ABS_ERROR(newRotation, expectedRotation, EPSILON); - } - - float ACCEPTABLE_ERROR = 1.0e-4f; - { // test nearby but off-axis rotation - float offAngle = 0.1f; - glm::quat offRotation(offAngle, glm::vec3(1.0f, 0.0f, 0.0f)); - float angle = 0.5f * (maxAngle + minAngle); - glm::quat rotation = offRotation * glm::angleAxis(angle, yAxis); - - glm::quat newRotation = rotation; - bool constrained = c->clamp(newRotation); - glm::quat expectedRotation = glm::angleAxis(angle, yAxis); - - QVERIFY2(constrained, "HingeConstraint should clamp()"); - QVERIFY2(newRotation != rotation, "HingeConstraint should change rotation"); - QCOMPARE_WITH_ABS_ERROR(newRotation, expectedRotation, ACCEPTABLE_ERROR); - } - { // test way off rotation > maxAngle - float offAngle = 0.5f; - glm::quat offRotation = glm::angleAxis(offAngle, glm::vec3(1.0f, 0.0f, 0.0f)); - float angle = maxAngle + 0.2f * (TWO_PI - (maxAngle - minAngle)); - glm::quat rotation = glm::angleAxis(angle, yAxis); - rotation = offRotation * glm::angleAxis(angle, yAxis); - - glm::quat newRotation = rotation; - bool constrained = c->clamp(newRotation); - glm::quat expectedRotation = glm::angleAxis(maxAngle, yAxis); - - QVERIFY2(constrained, "HingeConstraint should clamp()"); - QVERIFY2(newRotation != rotation, "HingeConstraint should change rotation"); - QCOMPARE_WITH_ABS_ERROR(newRotation, expectedRotation, EPSILON); - } - { // test way off rotation < minAngle - float offAngle = 0.5f; - glm::quat offRotation = glm::angleAxis(offAngle, glm::vec3(1.0f, 0.0f, 0.0f)); - float angle = minAngle - 0.2f * (TWO_PI - (maxAngle - minAngle)); - glm::quat rotation = glm::angleAxis(angle, yAxis); - rotation = offRotation * glm::angleAxis(angle, yAxis); - - glm::quat newRotation = rotation; - bool constrained = c->clamp(newRotation); - glm::quat expectedRotation = glm::angleAxis(minAngle, yAxis); - - QVERIFY2(constrained, "HingeConstraint should clamp()"); - QVERIFY2(newRotation != rotation, "HingeConstraint should change rotation"); - QCOMPARE_WITH_ABS_ERROR(newRotation, expectedRotation, EPSILON); - } - { // test way off rotation > maxAngle with wrap over to minAngle - float offAngle = -0.5f; - glm::quat offRotation = glm::angleAxis(offAngle, glm::vec3(1.0f, 0.0f, 0.0f)); - float angle = maxAngle + 0.6f * (TWO_PI - (maxAngle - minAngle)); - glm::quat rotation = glm::angleAxis(angle, yAxis); - rotation = offRotation * glm::angleAxis(angle, yAxis); - - glm::quat newRotation = rotation; - bool constrained = c->clamp(newRotation); - glm::quat expectedRotation = glm::angleAxis(minAngle, yAxis); - - QVERIFY2(constrained, "HingeConstraint should clamp()"); - QVERIFY2(newRotation != rotation, "HingeConstraint should change rotation"); - QCOMPARE_WITH_ABS_ERROR(newRotation, expectedRotation, EPSILON); - } - { // test way off rotation < minAngle with wrap over to maxAngle - float offAngle = -0.6f; - glm::quat offRotation = glm::angleAxis(offAngle, glm::vec3(1.0f, 0.0f, 0.0f)); - float angle = minAngle - 0.7f * (TWO_PI - (maxAngle - minAngle)); - glm::quat rotation = glm::angleAxis(angle, yAxis); - rotation = offRotation * glm::angleAxis(angle, yAxis); - - glm::quat newRotation = rotation; - bool constrained = c->clamp(newRotation); - glm::quat expectedRotation = glm::angleAxis(maxAngle, yAxis); - - QVERIFY2(constrained, "HingeConstraint should clamp()"); - QVERIFY2(newRotation != rotation, "HingeConstraint should change rotation"); - QCOMPARE_WITH_ABS_ERROR(newRotation, expectedRotation, EPSILON); - } - delete c; -} - -void AngularConstraintTests::testConeRollerConstraint() { - float minAngleX = -PI / 5.0f; - float minAngleY = -PI / 5.0f; - float minAngleZ = -PI / 8.0f; - - float maxAngleX = PI / 4.0f; - float maxAngleY = PI / 3.0f; - float maxAngleZ = PI / 4.0f; - - glm::vec3 minAngles(minAngleX, minAngleY, minAngleZ); - glm::vec3 maxAngles(maxAngleX, maxAngleY, maxAngleZ); - AngularConstraint* c = AngularConstraint::newAngularConstraint(minAngles, maxAngles); - - float expectedConeAngle = 0.25f * (maxAngleX - minAngleX + maxAngleY - minAngleY); - glm::vec3 middleAngles = 0.5f * (maxAngles + minAngles); - glm::quat yaw = glm::angleAxis(middleAngles[1], glm::vec3(0.0f, 1.0f, 0.0f)); - glm::quat pitch = glm::angleAxis(middleAngles[0], glm::vec3(1.0f, 0.0f, 0.0f)); - glm::vec3 expectedConeAxis = pitch * yaw * glm::vec3(0.0f, 0.0f, 1.0f); - - glm::vec3 xAxis(1.0f, 0.0f, 0.0f); - glm::vec3 perpAxis = glm::normalize(xAxis - glm::dot(xAxis, expectedConeAxis) * expectedConeAxis); - - QVERIFY2(c != nullptr, "newAngularConstraint() should make a constraint"); - { // test in middle of constraint - glm::vec3 angles(PI/20.0f, 0.0f, PI/10.0f); - glm::quat rotation(angles); - - glm::quat newRotation = rotation; - bool constrained = c->clamp(newRotation); - QVERIFY2(!constrained, "ConeRollerConstraint should not clamp()"); - QVERIFY2(newRotation == rotation, "ConeRollerConstraint should not change rotation"); - } - float deltaAngle = 0.001f; - { // test just inside edge of cone - glm::quat rotation = glm::angleAxis(expectedConeAngle - deltaAngle, perpAxis); - - glm::quat newRotation = rotation; - bool constrained = c->clamp(newRotation); - - QVERIFY2(!constrained, "ConeRollerConstraint should not clamp()"); - QVERIFY2(newRotation == rotation, "ConeRollerConstraint should not change rotation"); - } - { // test just outside edge of cone - glm::quat rotation = glm::angleAxis(expectedConeAngle + deltaAngle, perpAxis); - - glm::quat newRotation = rotation; - bool constrained = c->clamp(newRotation); - - QVERIFY2(constrained, "ConeRollerConstraint should clamp()"); - QVERIFY2(newRotation != rotation, "ConeRollerConstraint should change rotation"); - } - { // test just inside min edge of roll - glm::quat rotation = glm::angleAxis(minAngleZ + deltaAngle, expectedConeAxis); - - glm::quat newRotation = rotation; - bool constrained = c->clamp(newRotation); - - QVERIFY2(!constrained, "ConeRollerConstraint should not clamp()"); - QVERIFY2(newRotation == rotation, "ConeRollerConstraint should not change rotation"); - } - { // test just inside max edge of roll - glm::quat rotation = glm::angleAxis(maxAngleZ - deltaAngle, expectedConeAxis); - - glm::quat newRotation = rotation; - bool constrained = c->clamp(newRotation); - - QVERIFY2(!constrained, "ConeRollerConstraint should not clamp()"); - QVERIFY2(newRotation == rotation, "ConeRollerConstraint should not change rotation"); - } - { // test just outside min edge of roll - glm::quat rotation = glm::angleAxis(minAngleZ - deltaAngle, expectedConeAxis); - - glm::quat newRotation = rotation; - bool constrained = c->clamp(newRotation); - glm::quat expectedRotation = glm::angleAxis(minAngleZ, expectedConeAxis); - - QVERIFY2(constrained, "ConeRollerConstraint should clamp()"); - QVERIFY2(newRotation != rotation, "ConeRollerConstraint should change rotation"); - QCOMPARE_WITH_ABS_ERROR(newRotation, expectedRotation, EPSILON); - } - { // test just outside max edge of roll - glm::quat rotation = glm::angleAxis(maxAngleZ + deltaAngle, expectedConeAxis); - - glm::quat newRotation = rotation; - bool constrained = c->clamp(newRotation); - glm::quat expectedRotation = glm::angleAxis(maxAngleZ, expectedConeAxis); - - QVERIFY2(constrained, "ConeRollerConstraint should clamp()"); - QVERIFY2(newRotation != rotation, "ConeRollerConstraint should change rotation"); - QCOMPARE_WITH_ABS_ERROR(newRotation, expectedRotation, EPSILON); - } - deltaAngle = 0.25f * expectedConeAngle; - { // test far outside cone and min roll - glm::quat roll = glm::angleAxis(minAngleZ - deltaAngle, expectedConeAxis); - glm::quat pitchYaw = glm::angleAxis(expectedConeAngle + deltaAngle, perpAxis); - glm::quat rotation = pitchYaw * roll; - - glm::quat newRotation = rotation; - bool constrained = c->clamp(newRotation); - - glm::quat expectedRoll = glm::angleAxis(minAngleZ, expectedConeAxis); - glm::quat expectedPitchYaw = glm::angleAxis(expectedConeAngle, perpAxis); - glm::quat expectedRotation = expectedPitchYaw * expectedRoll; - - QVERIFY2(constrained, "ConeRollerConstraint should clamp()"); - QVERIFY2(newRotation != rotation, "ConeRollerConstraint should change rotation"); - QCOMPARE_WITH_ABS_ERROR(newRotation, expectedRotation, EPSILON); - } - { // test far outside cone and max roll - glm::quat roll = glm::angleAxis(maxAngleZ + deltaAngle, expectedConeAxis); - glm::quat pitchYaw = glm::angleAxis(- expectedConeAngle - deltaAngle, perpAxis); - glm::quat rotation = pitchYaw * roll; - - glm::quat newRotation = rotation; - bool constrained = c->clamp(newRotation); - - glm::quat expectedRoll = glm::angleAxis(maxAngleZ, expectedConeAxis); - glm::quat expectedPitchYaw = glm::angleAxis(- expectedConeAngle, perpAxis); - glm::quat expectedRotation = expectedPitchYaw * expectedRoll; - - QVERIFY2(constrained, "ConeRollerConstraint should clamp()"); - QVERIFY2(newRotation != rotation, "ConeRollerConstraint should change rotation"); - QCOMPARE_WITH_ABS_ERROR(newRotation, expectedRotation, EPSILON); - } - delete c; -} - diff --git a/tests/shared/src/AngularConstraintTests.h b/tests/shared/src/AngularConstraintTests.h deleted file mode 100644 index 705639b571..0000000000 --- a/tests/shared/src/AngularConstraintTests.h +++ /dev/null @@ -1,27 +0,0 @@ -// -// AngularConstraintTests.h -// tests/physics/src -// -// Created by Andrew Meadows on 2014.05.30 -// Copyright 2014 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 -// - -#ifndef hifi_AngularConstraintTests_h -#define hifi_AngularConstraintTests_h - -#include -#include - -class AngularConstraintTests : public QObject { - Q_OBJECT -private slots: - void testHingeConstraint(); - void testConeRollerConstraint(); -}; - -float getErrorDifference(const glm::quat& a, const glm::quat& b); - -#endif // hifi_AngularConstraintTests_h