From e527f49d6563e09673e923d8b64cac6b60089b8a Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 27 Jul 2015 15:30:10 -0700 Subject: [PATCH 01/65] removing interface dependency on ShapeCollider --- interface/src/avatar/MyAvatar.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index cdd7bf2b66..42c1d15db7 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -29,7 +29,6 @@ #include #include #include -#include #include #include #include @@ -105,7 +104,6 @@ MyAvatar::MyAvatar(RigPointer rig) : _rig(rig), _prevShouldDrawHead(true) { - ShapeCollider::initDispatchTable(); for (int i = 0; i < MAX_DRIVE_KEYS; i++) { _driveKeys[i] = 0.0f; } From c7f367bfd7ac3923a05f66f0ae1942572c1c3d15 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 27 Jul 2015 15:30:37 -0700 Subject: [PATCH 02/65] removing unnecessary #include --- libraries/fbx/src/FBXReader.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index 507e7938de..4ba47d4b46 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -27,7 +27,6 @@ #include #include #include -#include #include #include From ef5e7e9ccc2fa113a7c57597dfdb7742f32ca773 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 28 Jul 2015 15:45:48 -0700 Subject: [PATCH 03/65] remove PlaneShape dependency from GeometryUtils also add unit tests for indRayRectangleIntersection() --- libraries/shared/src/GeometryUtil.cpp | 58 +++++---- tests/shared/src/GeometryUtilTests.cpp | 160 +++++++++++++++++++++++++ tests/shared/src/GeometryUtilTests.h | 28 +++++ 3 files changed, 224 insertions(+), 22 deletions(-) create mode 100644 tests/shared/src/GeometryUtilTests.cpp create mode 100644 tests/shared/src/GeometryUtilTests.h diff --git a/libraries/shared/src/GeometryUtil.cpp b/libraries/shared/src/GeometryUtil.cpp index afb5890d05..b88166995a 100644 --- a/libraries/shared/src/GeometryUtil.cpp +++ b/libraries/shared/src/GeometryUtil.cpp @@ -11,10 +11,10 @@ #include #include +#include #include "GeometryUtil.h" #include "NumericalConstants.h" -#include "PlaneShape.h" #include "RayIntersectionInfo.h" glm::vec3 computeVectorFromPointToSegment(const glm::vec3& point, const glm::vec3& start, const glm::vec3& end) { @@ -496,31 +496,45 @@ void PolygonClip::copyCleanArray(int& lengthA, glm::vec2* vertexArrayA, int& len bool findRayRectangleIntersection(const glm::vec3& origin, const glm::vec3& direction, const glm::quat& rotation, const glm::vec3& position, const glm::vec2& dimensions, float& distance) { - RayIntersectionInfo rayInfo; - rayInfo._rayStart = origin; - rayInfo._rayDirection = direction; - rayInfo._rayLength = std::numeric_limits::max(); - - PlaneShape plane; - const glm::vec3 UNROTATED_NORMAL(0.0f, 0.0f, -1.0f); glm::vec3 normal = rotation * UNROTATED_NORMAL; - plane.setNormal(normal); - plane.setPoint(position); // the position is definitely a point on our plane - bool intersects = plane.findRayIntersection(rayInfo); + bool maybeIntersects = false; + float denominator = glm::dot(normal, direction); + glm::vec3 offset = origin - position; + float normDotOffset = glm::dot(offset, normal); + float d = 0.0f; + if (fabsf(denominator) < EPSILON) { + // line is perpendicular to plane + if (fabsf(normDotOffset) < EPSILON) { + // ray starts on the plane + maybeIntersects = true; - if (intersects) { - distance = rayInfo._hitDistance; - - glm::vec3 hitPosition = origin + (distance * direction); - glm::vec3 localHitPosition = glm::inverse(rotation) * (hitPosition - position); - - glm::vec2 halfDimensions = 0.5f * dimensions; - - intersects = -halfDimensions.x <= localHitPosition.x && localHitPosition.x <= halfDimensions.x - && -halfDimensions.y <= localHitPosition.y && localHitPosition.y <= halfDimensions.y; + // compute distance to closest approach + d = - glm::dot(offset, direction); // distance to closest approach of center of rectangle + if (d < 0.0f) { + // ray points away from center of rectangle, so ray's start is the closest approach + d = 0.0f; + } + } + } else { + d = - normDotOffset / denominator; + if (d > 0.0f) { + // ray points toward plane + maybeIntersects = true; + } } - return intersects; + if (maybeIntersects) { + glm::vec3 hitPosition = origin + (d * direction); + glm::vec3 localHitPosition = glm::inverse(rotation) * (hitPosition - position); + glm::vec2 halfDimensions = 0.5f * dimensions; + if (fabsf(localHitPosition.x) < halfDimensions.x && fabsf(localHitPosition.y) < halfDimensions.y) { + // only update distance on intersection + distance = d; + return true; + } + } + + return false; } diff --git a/tests/shared/src/GeometryUtilTests.cpp b/tests/shared/src/GeometryUtilTests.cpp new file mode 100644 index 0000000000..798951e304 --- /dev/null +++ b/tests/shared/src/GeometryUtilTests.cpp @@ -0,0 +1,160 @@ +// +// GeometryUtilTests.cpp +// tests/physics/src +// +// Created by Andrew Meadows on 2015.07.27 +// Copyright 2015 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 "GeometryUtilTests.h" + +#include +#include +#include + +#include <../QTestExtensions.h> + + +QTEST_MAIN(GeometryUtilTests) + +float getErrorDifference(const float& a, const float& b) { + return fabsf(a - b); +} + +float getErrorDifference(const glm::vec3& a, const glm::vec3& b) { + return glm::distance(a, b); +} + +void GeometryUtilTests::testLocalRayRectangleIntersection() { + glm::vec3 xAxis(1.0f, 0.0f, 0.0f); + glm::vec3 yAxis(0.0f, 1.0f, 0.0f); + glm::vec3 zAxis(0.0f, 0.0f, 1.0f); + + // create a rectangle in the local frame on the XY plane with normal along -zAxis + // (this is the assumed local orientation of the rectangle in findRayRectangleIntersection()) + glm::vec2 rectDimensions(3.0f, 5.0f); + glm::vec3 rectCenter(0.0f, 0.0f, 0.0f); + glm::quat rectRotation = glm::quat(); // identity + + // create points for generating rays that hit the plane and don't + glm::vec3 rayStart(1.0f, 2.0f, 3.0f); + float delta = 0.1f; + + { // verify hit + glm::vec3 rayEnd = rectCenter + rectRotation * ((0.5f * rectDimensions.x - delta) * xAxis); + glm::vec3 rayHitDirection = glm::normalize(rayEnd - rayStart); + float expectedDistance = glm::length(rayEnd - rayStart); + + float distance = FLT_MAX; + bool hit = findRayRectangleIntersection(rayStart, rayHitDirection, rectRotation, rectCenter, rectDimensions, distance); + QCOMPARE(hit, true); + QCOMPARE_WITH_ABS_ERROR(distance, expectedDistance, EPSILON); + } + + { // verify miss + glm::vec3 rayEnd = rectCenter + rectRotation * ((0.5f * rectDimensions.y + delta) * yAxis); + glm::vec3 rayMissDirection = glm::normalize(rayEnd - rayStart); + float distance = FLT_MAX; + bool hit = findRayRectangleIntersection(rayStart, rayMissDirection, rectRotation, rectCenter, rectDimensions, distance); + QCOMPARE(hit, false); + QCOMPARE(distance, FLT_MAX); // distance should be unchanged + } + + { // hit with co-planer line + float yFraction = 0.25f; + rayStart = rectCenter + rectRotation * (rectDimensions.x * xAxis + yFraction * rectDimensions.y * yAxis); + + glm::vec3 rayEnd = rectCenter - rectRotation * (rectDimensions.x * xAxis + yFraction * rectDimensions.y * yAxis); + glm::vec3 rayHitDirection = glm::normalize(rayEnd - rayStart); + float expectedDistance = rectDimensions.x; + + float distance = FLT_MAX; + bool hit = findRayRectangleIntersection(rayStart, rayHitDirection, rectRotation, rectCenter, rectDimensions, distance); + QCOMPARE(hit, true); + QCOMPARE_WITH_ABS_ERROR(distance, expectedDistance, EPSILON); + } + + { // miss with co-planer line + float yFraction = 0.75f; + rayStart = rectCenter + rectRotation * (rectDimensions.x * xAxis + (yFraction * rectDimensions.y) * yAxis); + + glm::vec3 rayEnd = rectCenter + rectRotation * (- rectDimensions.x * xAxis + (yFraction * rectDimensions.y) * yAxis); + glm::vec3 rayHitDirection = glm::normalize(rayEnd - rayStart); + + float distance = FLT_MAX; + bool hit = findRayRectangleIntersection(rayStart, rayHitDirection, rectRotation, rectCenter, rectDimensions, distance); + QCOMPARE(hit, false); + QCOMPARE(distance, FLT_MAX); // distance should be unchanged + } +} + +void GeometryUtilTests::testWorldRayRectangleIntersection() { + glm::vec3 xAxis(1.0f, 0.0f, 0.0f); + glm::vec3 yAxis(0.0f, 1.0f, 0.0f); + glm::vec3 zAxis(0.0f, 0.0f, 1.0f); + + // create a rectangle in the local frame on the XY plane with normal along -zAxis + // (this is the assumed local orientation of the rectangle in findRayRectangleIntersection()) + // and then rotate and shift everything into the world frame + glm::vec2 rectDimensions(3.0f, 5.0f); + glm::vec3 rectCenter(0.7f, 0.5f, -0.3f); + glm::quat rectRotation = glm::quat(); // identity + + + // create points for generating rays that hit the plane and don't + glm::vec3 rayStart(1.0f, 2.0f, 3.0f); + float delta = 0.1f; + + { // verify hit + glm::vec3 rayEnd = rectCenter + rectRotation * ((0.5f * rectDimensions.x - delta) * xAxis); + glm::vec3 rayHitDirection = glm::normalize(rayEnd - rayStart); + float expectedDistance = glm::length(rayEnd - rayStart); + + float distance = FLT_MAX; + bool hit = findRayRectangleIntersection(rayStart, rayHitDirection, rectRotation, rectCenter, rectDimensions, distance); + QCOMPARE(hit, true); + QCOMPARE_WITH_ABS_ERROR(distance, expectedDistance, EPSILON); + } + + { // verify miss + glm::vec3 rayEnd = rectCenter + rectRotation * ((0.5f * rectDimensions.y + delta) * yAxis); + glm::vec3 rayMissDirection = glm::normalize(rayEnd - rayStart); + float distance = FLT_MAX; + bool hit = findRayRectangleIntersection(rayStart, rayMissDirection, rectRotation, rectCenter, rectDimensions, distance); + QCOMPARE(hit, false); + QCOMPARE(distance, FLT_MAX); // distance should be unchanged + } + + { // hit with co-planer line + float yFraction = 0.25f; + rayStart = rectCenter + rectRotation * (rectDimensions.x * xAxis + (yFraction * rectDimensions.y) * yAxis); + + glm::vec3 rayEnd = rectCenter - rectRotation * (rectDimensions.x * xAxis + (yFraction * rectDimensions.y) * yAxis); + glm::vec3 rayHitDirection = glm::normalize(rayEnd - rayStart); + float expectedDistance = rectDimensions.x; + + float distance = FLT_MAX; + bool hit = findRayRectangleIntersection(rayStart, rayHitDirection, rectRotation, rectCenter, rectDimensions, distance); + QCOMPARE(hit, true); + QCOMPARE_WITH_ABS_ERROR(distance, expectedDistance, EPSILON); + } + + { // miss with co-planer line + float yFraction = 0.75f; + rayStart = rectCenter + rectRotation * (rectDimensions.x * xAxis + (yFraction * rectDimensions.y) * yAxis); + + glm::vec3 rayEnd = rectCenter + rectRotation * (-rectDimensions.x * xAxis + (yFraction * rectDimensions.y) * yAxis); + glm::vec3 rayHitDirection = glm::normalize(rayEnd - rayStart); + + float distance = FLT_MAX; + bool hit = findRayRectangleIntersection(rayStart, rayHitDirection, rectRotation, rectCenter, rectDimensions, distance); + QCOMPARE(hit, false); + QCOMPARE(distance, FLT_MAX); // distance should be unchanged + } +} + diff --git a/tests/shared/src/GeometryUtilTests.h b/tests/shared/src/GeometryUtilTests.h new file mode 100644 index 0000000000..d75fce556e --- /dev/null +++ b/tests/shared/src/GeometryUtilTests.h @@ -0,0 +1,28 @@ +// +// GeometryUtilTests.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 GeometryUtilTests : public QObject { + Q_OBJECT +private slots: + void testLocalRayRectangleIntersection(); + void testWorldRayRectangleIntersection(); +}; + +float getErrorDifference(const float& a, const float& b); +float getErrorDifference(const glm::vec3& a, const glm::vec3& b); + +#endif // hifi_AngularConstraintTests_h From 8b20f9d3a68a31450c95a6dbb90f51aa9fca8837 Mon Sep 17 00:00:00 2001 From: Sam Gateau Date: Tue, 28 Jul 2015 15:48:01 -0700 Subject: [PATCH 04/65] do the minimum include to use glew on linux --- libraries/gpu/src/gpu/GLBackend.cpp | 7 +++++++ libraries/gpu/src/gpu/GPUConfig.h | 7 +++++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/libraries/gpu/src/gpu/GLBackend.cpp b/libraries/gpu/src/gpu/GLBackend.cpp index 6b1d552be9..3942af4f84 100644 --- a/libraries/gpu/src/gpu/GLBackend.cpp +++ b/libraries/gpu/src/gpu/GLBackend.cpp @@ -115,6 +115,13 @@ GLBackend::GLBackend() : #endif #if defined(Q_OS_LINUX) + GLenum err = glewInit(); + if (GLEW_OK != err) { + /* Problem: glewInit failed, something is seriously wrong. */ + qCDebug(gpulogging, "Error: %s\n", glewGetErrorString(err)); + } + qCDebug(gpulogging, "Status: Using GLEW %s\n", glewGetString(GLEW_VERSION)); + // TODO: Write the correct code for Linux... /* if (wglewGetExtension("WGL_EXT_swap_control")) { int swapInterval = wglGetSwapIntervalEXT(); diff --git a/libraries/gpu/src/gpu/GPUConfig.h b/libraries/gpu/src/gpu/GPUConfig.h index 1d092dbc6a..a9657076fa 100644 --- a/libraries/gpu/src/gpu/GPUConfig.h +++ b/libraries/gpu/src/gpu/GPUConfig.h @@ -34,8 +34,11 @@ #elif defined(ANDROID) #else -#include -#include + +#include +#include +//#include +//#include #define GPU_FEATURE_PROFILE GPU_LEGACY #define GPU_TRANSFORM_PROFILE GPU_LEGACY From 27d3d3f450cc904636cfe55e13a4a1d053923c82 Mon Sep 17 00:00:00 2001 From: samcake Date: Tue, 28 Jul 2015 15:51:15 -0700 Subject: [PATCH 05/65] fix w to x --- libraries/gpu/src/gpu/GPUConfig.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/gpu/src/gpu/GPUConfig.h b/libraries/gpu/src/gpu/GPUConfig.h index a9657076fa..2e3149919c 100644 --- a/libraries/gpu/src/gpu/GPUConfig.h +++ b/libraries/gpu/src/gpu/GPUConfig.h @@ -36,7 +36,7 @@ #else #include -#include +#include //#include //#include From 77a12eb50ea7af97a46060e178c2d6479af0bdc0 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 28 Jul 2015 17:53:01 -0700 Subject: [PATCH 06/65] compile on linux with GLEW --- cmake/modules/FindGLEW.cmake | 14 +++++++ interface/CMakeLists.txt | 6 +++ interface/src/Stars.cpp | 10 ++--- interface/src/devices/OculusManager.cpp | 6 +-- interface/src/devices/TV3DManager.cpp | 9 +++-- interface/src/devices/TV3DManager.h | 1 + .../src/RenderablePolyVoxEntityItem.cpp | 2 +- .../src/RenderableWebEntityItem.cpp | 3 +- libraries/gpu/CMakeLists.txt | 7 +++- libraries/gpu/src/gpu/Batch.cpp | 6 +-- libraries/gpu/src/gpu/GLBackend.cpp | 3 +- libraries/gpu/src/gpu/GLBackendQuery.cpp | 1 - libraries/gpu/src/gpu/GLBackendShader.cpp | 1 - libraries/gpu/src/gpu/GLBackendShared.h | 5 ++- libraries/gpu/src/gpu/GPUConfig.h | 2 +- libraries/model/src/model/Material.h | 2 +- .../src/AmbientOcclusionEffect.cpp | 6 +-- .../src/DeferredLightingEffect.cpp | 38 +++++++++--------- libraries/render-utils/src/GeometryCache.cpp | 10 ++--- libraries/render-utils/src/Model.cpp | 8 ++-- .../render-utils/src/RenderDeferredTask.cpp | 4 +- libraries/render-utils/src/TextureCache.cpp | 7 ++-- libraries/render/src/render/DrawStatus.cpp | 7 ++-- libraries/render/src/render/DrawTask.cpp | 6 +-- tests/render-utils/src/main.cpp | 39 ++++++++++--------- 25 files changed, 109 insertions(+), 94 deletions(-) diff --git a/cmake/modules/FindGLEW.cmake b/cmake/modules/FindGLEW.cmake index e86db3fdac..b1789fb614 100644 --- a/cmake/modules/FindGLEW.cmake +++ b/cmake/modules/FindGLEW.cmake @@ -38,5 +38,19 @@ if (WIN32) find_package_handle_standard_args(GLEW DEFAULT_MSG GLEW_INCLUDE_DIRS GLEW_LIBRARIES GLEW_DLL_PATH) add_paths_to_fixup_libs(${GLEW_DLL_PATH}) +elseif (APPLE) +else () + find_path(GLEW_INCLUDE_DIR GL/glew.h) + find_library(GLEW_LIBRARY NAMES GLEW glew32 glew glew32s PATH_SUFFIXES lib64) + + set(GLEW_INCLUDE_DIRS ${GLEW_INCLUDE_DIR}) + set(GLEW_LIBRARIES ${GLEW_LIBRARY}) + + include(FindPackageHandleStandardArgs) + find_package_handle_standard_args(GLEW + REQUIRED_VARS GLEW_INCLUDE_DIR GLEW_LIBRARY) + + mark_as_advanced(GLEW_INCLUDE_DIR GLEW_LIBRARY) + endif () diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index 0c44ac801f..6af3cfa08b 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -245,6 +245,12 @@ else (APPLE) endif () endif() + else (WIN32) + find_package(GLEW REQUIRED) + target_include_directories(${TARGET_NAME} PRIVATE ${GLEW_INCLUDE_DIRS}) + + target_link_libraries(${TARGET_NAME} ${GLEW_LIBRARIES}) + message(STATUS ${GLEW_LIBRARY}) endif() endif (APPLE) diff --git a/interface/src/Stars.cpp b/interface/src/Stars.cpp index ebfddee38c..7b612acb68 100644 --- a/interface/src/Stars.cpp +++ b/interface/src/Stars.cpp @@ -14,8 +14,6 @@ #include #include -#include -#include #include #include #include @@ -207,7 +205,7 @@ void Stars::render(RenderArgs* renderArgs, float alpha) { batch._glUniform1f(_timeSlot, secs); geometryCache->renderUnitCube(batch); - glHint(GL_POINT_SMOOTH_HINT, GL_NICEST); + //glHint(GL_POINT_SMOOTH_HINT, GL_NICEST); static const size_t VERTEX_STRIDE = sizeof(StarVertex); size_t offset = offsetof(StarVertex, position); @@ -217,9 +215,9 @@ void Stars::render(RenderArgs* renderArgs, float alpha) { // Render the stars batch.setPipeline(_starsPipeline); - batch._glEnable(GL_PROGRAM_POINT_SIZE_EXT); - batch._glEnable(GL_VERTEX_PROGRAM_POINT_SIZE); - batch._glEnable(GL_POINT_SMOOTH); + //batch._glEnable(GL_PROGRAM_POINT_SIZE_EXT); + //batch._glEnable(GL_VERTEX_PROGRAM_POINT_SIZE); + //batch._glEnable(GL_POINT_SMOOTH); batch.setInputFormat(streamFormat); batch.setInputBuffer(VERTICES_SLOT, posView); diff --git a/interface/src/devices/OculusManager.cpp b/interface/src/devices/OculusManager.cpp index c9c70b4417..16685df96f 100644 --- a/interface/src/devices/OculusManager.cpp +++ b/interface/src/devices/OculusManager.cpp @@ -11,16 +11,16 @@ // #include "OculusManager.h" -#include +#include #include #include +#include #include +#include #include #include -#include -#include #include #include diff --git a/interface/src/devices/TV3DManager.cpp b/interface/src/devices/TV3DManager.cpp index fefaf060bd..5dee5988c1 100644 --- a/interface/src/devices/TV3DManager.cpp +++ b/interface/src/devices/TV3DManager.cpp @@ -9,13 +9,14 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include "TV3DManager.h" + #include #include -#include "gpu/GLBackend.h" -#include "Application.h" +#include -#include "TV3DManager.h" +#include "Application.h" #include "Menu.h" int TV3DManager::_screenWidth = 1; @@ -63,6 +64,7 @@ void TV3DManager::setFrustum(Camera& whichCamera) { } void TV3DManager::configureCamera(Camera& whichCamera, int screenWidth, int screenHeight) { +#ifdef THIS_CURRENTLY_BROKEN_WAITING_FOR_DISPLAY_PLUGINS if (screenHeight == 0) { screenHeight = 1; // prevent divide by 0 } @@ -72,6 +74,7 @@ void TV3DManager::configureCamera(Camera& whichCamera, int screenWidth, int scre setFrustum(whichCamera); glViewport (0, 0, _screenWidth, _screenHeight); // sets drawing viewport +#endif } void TV3DManager::display(RenderArgs* renderArgs, Camera& whichCamera) { diff --git a/interface/src/devices/TV3DManager.h b/interface/src/devices/TV3DManager.h index 330a4ee0ee..96ee79f7d1 100644 --- a/interface/src/devices/TV3DManager.h +++ b/interface/src/devices/TV3DManager.h @@ -17,6 +17,7 @@ #include class Camera; +class RenderArgs; struct eyeFrustum { double left; diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp index d3ee312311..681702bb07 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp @@ -35,7 +35,7 @@ #include #include "model/Geometry.h" -#include "gpu/GLBackend.h" +#include "gpu/Context.h" #include "EntityTreeRenderer.h" #include "RenderablePolyVoxEntityItem.h" diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp index 7fa615073b..9e48164647 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp @@ -8,7 +8,6 @@ #include "RenderableWebEntityItem.h" -#include #include #include #include @@ -24,7 +23,7 @@ #include #include #include -#include +#include #include "EntityTreeRenderer.h" diff --git a/libraries/gpu/CMakeLists.txt b/libraries/gpu/CMakeLists.txt index 30949b83e1..eda168091e 100644 --- a/libraries/gpu/CMakeLists.txt +++ b/libraries/gpu/CMakeLists.txt @@ -31,13 +31,16 @@ elseif (WIN32) elseif (ANDROID) target_link_libraries(${TARGET_NAME} "-lGLESv3" "-lEGL") else () + find_package(GLEW REQUIRED) + target_include_directories(${TARGET_NAME} PUBLIC ${GLEW_INCLUDE_DIRS}) + find_package(OpenGL REQUIRED) if (${OPENGL_INCLUDE_DIR}) include_directories(SYSTEM "${OPENGL_INCLUDE_DIR}") endif () - target_link_libraries(${TARGET_NAME} "${OPENGL_LIBRARY}") + target_link_libraries(${TARGET_NAME} "${GLEW_LIBRARIES}" "${OPENGL_LIBRARY}") - target_include_directories(${TARGET_NAME} PUBLIC ${OPENGL_INCLUDE_DIR}) + # target_include_directories(${TARGET_NAME} PUBLIC ${OPENGL_INCLUDE_DIR}) endif (APPLE) diff --git a/libraries/gpu/src/gpu/Batch.cpp b/libraries/gpu/src/gpu/Batch.cpp index 4ac33d8f14..b7f3ef444c 100644 --- a/libraries/gpu/src/gpu/Batch.cpp +++ b/libraries/gpu/src/gpu/Batch.cpp @@ -9,11 +9,11 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // #include "Batch.h" -#include "GPUConfig.h" - -#include #include +#include + +#include "GPUConfig.h" #if defined(NSIGHT_FOUND) diff --git a/libraries/gpu/src/gpu/GLBackend.cpp b/libraries/gpu/src/gpu/GLBackend.cpp index 3942af4f84..4bc8abadd0 100644 --- a/libraries/gpu/src/gpu/GLBackend.cpp +++ b/libraries/gpu/src/gpu/GLBackend.cpp @@ -8,9 +8,10 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include "GLBackendShared.h" + #include #include "GPULogging.h" -#include "GLBackendShared.h" #include using namespace gpu; diff --git a/libraries/gpu/src/gpu/GLBackendQuery.cpp b/libraries/gpu/src/gpu/GLBackendQuery.cpp index 39db19dafd..297bdf9c40 100644 --- a/libraries/gpu/src/gpu/GLBackendQuery.cpp +++ b/libraries/gpu/src/gpu/GLBackendQuery.cpp @@ -8,7 +8,6 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include "GPULogging.h" #include "GLBackendShared.h" diff --git a/libraries/gpu/src/gpu/GLBackendShader.cpp b/libraries/gpu/src/gpu/GLBackendShader.cpp index fd5bed2e5e..dccd035cb4 100755 --- a/libraries/gpu/src/gpu/GLBackendShader.cpp +++ b/libraries/gpu/src/gpu/GLBackendShader.cpp @@ -8,7 +8,6 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include "GPULogging.h" #include "GLBackendShared.h" #include "Format.h" diff --git a/libraries/gpu/src/gpu/GLBackendShared.h b/libraries/gpu/src/gpu/GLBackendShared.h index 27f58fcbe3..75bef461f9 100644 --- a/libraries/gpu/src/gpu/GLBackendShared.h +++ b/libraries/gpu/src/gpu/GLBackendShared.h @@ -11,10 +11,11 @@ #ifndef hifi_gpu_GLBackend_Shared_h #define hifi_gpu_GLBackend_Shared_h -#include "GLBackend.h" - #include +#include "GPULogging.h" +#include "GLBackend.h" + #include "Batch.h" static const GLenum _primitiveToGLmode[gpu::NUM_PRIMITIVES] = { diff --git a/libraries/gpu/src/gpu/GPUConfig.h b/libraries/gpu/src/gpu/GPUConfig.h index 2e3149919c..5046221ad3 100644 --- a/libraries/gpu/src/gpu/GPUConfig.h +++ b/libraries/gpu/src/gpu/GPUConfig.h @@ -36,7 +36,7 @@ #else #include -#include +//#include //#include //#include diff --git a/libraries/model/src/model/Material.h b/libraries/model/src/model/Material.h index 392fd918a1..a1a17d29e9 100755 --- a/libraries/model/src/model/Material.h +++ b/libraries/model/src/model/Material.h @@ -13,13 +13,13 @@ #include #include +#include #include #include "gpu/Resource.h" #include "gpu/Texture.h" -#include namespace model { diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.cpp b/libraries/render-utils/src/AmbientOcclusionEffect.cpp index f19fa6e18a..22e5a705e3 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.cpp +++ b/libraries/render-utils/src/AmbientOcclusionEffect.cpp @@ -9,15 +9,12 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -// include this before QOpenGLFramebufferObject, which includes an earlier version of OpenGL -#include - -#include #include #include #include +#include #include "gpu/StandardShaderLib.h" #include "AmbientOcclusionEffect.h" @@ -176,7 +173,6 @@ void AmbientOcclusion::run(const render::SceneContextPointer& sceneContext, cons assert(renderContext->args); assert(renderContext->args->_viewFrustum); RenderArgs* args = renderContext->args; - auto& scene = sceneContext->_scene; gpu::Batch batch; diff --git a/libraries/render-utils/src/DeferredLightingEffect.cpp b/libraries/render-utils/src/DeferredLightingEffect.cpp index c14bbfcb1d..7563609026 100644 --- a/libraries/render-utils/src/DeferredLightingEffect.cpp +++ b/libraries/render-utils/src/DeferredLightingEffect.cpp @@ -12,12 +12,12 @@ #include "DeferredLightingEffect.h" #include -#include +#include +#include + #include #include #include -#include -#include #include "AbstractViewStateInterface.h" #include "GeometryCache.h" @@ -291,7 +291,7 @@ void DeferredLightingEffect::render(RenderArgs* args) { locations = &_directionalAmbientSphereLightCascadedShadowMapLocations; } batch.setPipeline(program); - batch._glUniform3fv(locations->shadowDistances, 1, (const GLfloat*) &_viewState->getShadowDistances()); + batch._glUniform3fv(locations->shadowDistances, 1, (const float*) &_viewState->getShadowDistances()); } else { if (useSkyboxCubemap) { @@ -325,7 +325,7 @@ void DeferredLightingEffect::render(RenderArgs* args) { sh = (*_skybox->getCubemap()->getIrradiance()); } for (int i =0; i ambientSphere + i, 1, (const GLfloat*) (&sh) + i * 4); + batch._glUniform4fv(locations->ambientSphere + i, 1, (const float*) (&sh) + i * 4); } } @@ -340,7 +340,7 @@ void DeferredLightingEffect::render(RenderArgs* args) { if (_atmosphere && (locations->atmosphereBufferUnit >= 0)) { batch.setUniformBuffer(locations->atmosphereBufferUnit, _atmosphere->getDataBuffer()); } - batch._glUniformMatrix4fv(locations->invViewMat, 1, false, reinterpret_cast< const GLfloat* >(&invViewMat)); + batch._glUniformMatrix4fv(locations->invViewMat, 1, false, reinterpret_cast< const float* >(&invViewMat)); } float left, right, bottom, top, nearVal, farVal; @@ -419,9 +419,9 @@ void DeferredLightingEffect::render(RenderArgs* args) { batch._glUniform2f(_pointLightLocations.depthTexCoordOffset, depthTexCoordOffsetS, depthTexCoordOffsetT); batch._glUniform2f(_pointLightLocations.depthTexCoordScale, depthTexCoordScaleS, depthTexCoordScaleT); - batch._glUniformMatrix4fv(_pointLightLocations.invViewMat, 1, false, reinterpret_cast< const GLfloat* >(&invViewMat)); + batch._glUniformMatrix4fv(_pointLightLocations.invViewMat, 1, false, reinterpret_cast< const float* >(&invViewMat)); - batch._glUniformMatrix4fv(_pointLightLocations.texcoordMat, 1, false, reinterpret_cast< const GLfloat* >(&texcoordMat)); + batch._glUniformMatrix4fv(_pointLightLocations.texcoordMat, 1, false, reinterpret_cast< const float* >(&texcoordMat)); for (auto lightID : _pointLights) { auto& light = _allocatedLights[lightID]; @@ -467,9 +467,9 @@ void DeferredLightingEffect::render(RenderArgs* args) { batch._glUniform2f(_spotLightLocations.depthTexCoordOffset, depthTexCoordOffsetS, depthTexCoordOffsetT); batch._glUniform2f(_spotLightLocations.depthTexCoordScale, depthTexCoordScaleS, depthTexCoordScaleT); - batch._glUniformMatrix4fv(_spotLightLocations.invViewMat, 1, false, reinterpret_cast< const GLfloat* >(&invViewMat)); + batch._glUniformMatrix4fv(_spotLightLocations.invViewMat, 1, false, reinterpret_cast< const float* >(&invViewMat)); - batch._glUniformMatrix4fv(_spotLightLocations.texcoordMat, 1, false, reinterpret_cast< const GLfloat* >(&texcoordMat)); + batch._glUniformMatrix4fv(_spotLightLocations.texcoordMat, 1, false, reinterpret_cast< const float* >(&texcoordMat)); for (auto lightID : _spotLights) { auto light = _allocatedLights[lightID]; @@ -489,7 +489,7 @@ void DeferredLightingEffect::render(RenderArgs* args) { if ((eyeHalfPlaneDistance > -nearRadius) && (glm::distance(eyePoint, glm::vec3(light->getPosition())) < expandedRadius + nearRadius)) { coneParam.w = 0.0f; - batch._glUniform4fv(_spotLightLocations.coneParam, 1, reinterpret_cast< const GLfloat* >(&coneParam)); + batch._glUniform4fv(_spotLightLocations.coneParam, 1, reinterpret_cast< const float* >(&coneParam)); Transform model; model.setTranslation(glm::vec3(0.0f, 0.0f, -1.0f)); @@ -509,7 +509,7 @@ void DeferredLightingEffect::render(RenderArgs* args) { batch.setViewTransform(viewMat); } else { coneParam.w = 1.0f; - batch._glUniform4fv(_spotLightLocations.coneParam, 1, reinterpret_cast< const GLfloat* >(&coneParam)); + batch._glUniform4fv(_spotLightLocations.coneParam, 1, reinterpret_cast< const float* >(&coneParam)); Transform model; model.setTranslation(light->getPosition()); @@ -595,9 +595,9 @@ void DeferredLightingEffect::loadLightProgram(const char* vertSource, const char slotBindings.insert(gpu::Shader::Binding(std::string("depthMap"), 3)); slotBindings.insert(gpu::Shader::Binding(std::string("shadowMap"), 4)); slotBindings.insert(gpu::Shader::Binding(std::string("skyboxMap"), 5)); - const GLint LIGHT_GPU_SLOT = 3; + const int LIGHT_GPU_SLOT = 3; slotBindings.insert(gpu::Shader::Binding(std::string("lightBuffer"), LIGHT_GPU_SLOT)); - const GLint ATMOSPHERE_GPU_SLOT = 4; + const int ATMOSPHERE_GPU_SLOT = 4; slotBindings.insert(gpu::Shader::Binding(std::string("atmosphereBufferUnit"), ATMOSPHERE_GPU_SLOT)); gpu::Shader::makeProgram(*program, slotBindings); @@ -677,10 +677,10 @@ model::MeshPointer DeferredLightingEffect::getSpotLightMesh() { int ringFloatOffset = slices * 3; - GLfloat* vertexData = new GLfloat[verticesSize]; - GLfloat* vertexRing0 = vertexData; - GLfloat* vertexRing1 = vertexRing0 + ringFloatOffset; - GLfloat* vertexRing2 = vertexRing1 + ringFloatOffset; + float* vertexData = new float[verticesSize]; + float* vertexRing0 = vertexData; + float* vertexRing1 = vertexRing0 + ringFloatOffset; + float* vertexRing2 = vertexRing1 + ringFloatOffset; for (int i = 0; i < slices; i++) { float theta = TWO_PI * i / slices; @@ -746,7 +746,7 @@ model::MeshPointer DeferredLightingEffect::getSpotLightMesh() { *(index++) = capVertex; } - _spotLightMesh->setIndexBuffer(gpu::BufferView(new gpu::Buffer(sizeof(GLushort) * indices, (gpu::Byte*) indexData), gpu::Element::INDEX_UINT16)); + _spotLightMesh->setIndexBuffer(gpu::BufferView(new gpu::Buffer(sizeof(unsigned short) * indices, (gpu::Byte*) indexData), gpu::Element::INDEX_UINT16)); delete[] indexData; model::Mesh::Part part(0, indices, 0, model::Mesh::TRIANGLES); diff --git a/libraries/render-utils/src/GeometryCache.cpp b/libraries/render-utils/src/GeometryCache.cpp index 8550f8d8b6..b873b35b9c 100644 --- a/libraries/render-utils/src/GeometryCache.cpp +++ b/libraries/render-utils/src/GeometryCache.cpp @@ -9,22 +9,22 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -// include this before QOpenGLBuffer, which includes an earlier version of OpenGL +#include "GeometryCache.h" + #include #include #include #include -#include -#include - #include #include +#include +#include + #include "TextureCache.h" #include "RenderUtilsLogging.h" -#include "GeometryCache.h" #include "standardTransformPNTC_vert.h" #include "standardDrawTexture_frag.h" diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 67eb85edfc..1e0e04c776 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -18,20 +18,18 @@ #include #include -#include -#include #include #include -#include "PhysicsEntity.h" #include #include #include +#include +#include #include "AbstractViewStateInterface.h" #include "AnimationHandle.h" #include "DeferredLightingEffect.h" #include "Model.h" -#include "RenderUtilsLogging.h" #include "model_vert.h" #include "model_shadow_vert.h" @@ -96,7 +94,7 @@ Model::~Model() { } Model::RenderPipelineLib Model::_renderPipelineLib; -const GLint MATERIAL_GPU_SLOT = 3; +const int MATERIAL_GPU_SLOT = 3; void Model::RenderPipelineLib::addRenderPipeline(Model::RenderKey key, gpu::ShaderPointer& vertexShader, diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index cf9b455059..b4863a4764 100755 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -10,12 +10,10 @@ // #include "RenderDeferredTask.h" -#include -#include -#include #include #include #include +#include #include "FramebufferCache.h" #include "DeferredLightingEffect.h" diff --git a/libraries/render-utils/src/TextureCache.cpp b/libraries/render-utils/src/TextureCache.cpp index 1a6ea97b64..d6a9bf5b36 100644 --- a/libraries/render-utils/src/TextureCache.cpp +++ b/libraries/render-utils/src/TextureCache.cpp @@ -16,16 +16,15 @@ #include #include -#include -#include -#include - #include #include #include #include #include +#include + + #include "RenderUtilsLogging.h" diff --git a/libraries/render/src/render/DrawStatus.cpp b/libraries/render/src/render/DrawStatus.cpp index f84d212112..f50f517d7d 100644 --- a/libraries/render/src/render/DrawStatus.cpp +++ b/libraries/render/src/render/DrawStatus.cpp @@ -15,15 +15,14 @@ #include #include +#include +#include #include #include #include #include -#include -#include - #include "drawItemBounds_vert.h" #include "drawItemBounds_frag.h" #include "drawItemStatus_vert.h" @@ -171,4 +170,4 @@ void DrawStatus::run(const SceneContextPointer& sceneContext, const RenderContex args->_context->syncCache(); renderContext->args->_context->syncCache(); args->_context->render((batch)); -} \ No newline at end of file +} diff --git a/libraries/render/src/render/DrawTask.cpp b/libraries/render/src/render/DrawTask.cpp index 0e3eba0b53..d29420fdfd 100755 --- a/libraries/render/src/render/DrawTask.cpp +++ b/libraries/render/src/render/DrawTask.cpp @@ -14,12 +14,12 @@ #include #include -#include -#include -#include #include #include #include +#include +#include +#include using namespace render; diff --git a/tests/render-utils/src/main.cpp b/tests/render-utils/src/main.cpp index 3b7eb18368..ab01d333ac 100644 --- a/tests/render-utils/src/main.cpp +++ b/tests/render-utils/src/main.cpp @@ -8,30 +8,31 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - #include #include + #include -#include +#include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include class RateCounter { std::vector times; From 475d069185ffe68e2979a43856d082adb747b04c Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 28 Jul 2015 18:12:10 -0700 Subject: [PATCH 07/65] fix rendering on linux --- libraries/gpu/src/gpu/Config.slh | 7 +++---- libraries/gpu/src/gpu/GPUConfig.h | 4 ++-- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/libraries/gpu/src/gpu/Config.slh b/libraries/gpu/src/gpu/Config.slh index 76be161822..f24b54e5d5 100644 --- a/libraries/gpu/src/gpu/Config.slh +++ b/libraries/gpu/src/gpu/Config.slh @@ -21,10 +21,9 @@ <@def VERSION_HEADER #version 120 #extension GL_EXT_gpu_shader4 : enable@> <@else@> - <@def GPU_FEATURE_PROFILE GPU_LEGACY@> - <@def GPU_TRANSFORM_PROFILE GPU_LEGACY@> - <@def VERSION_HEADER #version 120 -#extension GL_EXT_gpu_shader4 : enable@> + <@def GPU_FEATURE_PROFILE GPU_CORE@> + <@def GPU_TRANSFORM_PROFILE GPU_CORE@> + <@def VERSION_HEADER #version 430 compatibility@> <@endif@> <@endif@> diff --git a/libraries/gpu/src/gpu/GPUConfig.h b/libraries/gpu/src/gpu/GPUConfig.h index 5046221ad3..d9b6d18894 100644 --- a/libraries/gpu/src/gpu/GPUConfig.h +++ b/libraries/gpu/src/gpu/GPUConfig.h @@ -40,8 +40,8 @@ //#include //#include -#define GPU_FEATURE_PROFILE GPU_LEGACY -#define GPU_TRANSFORM_PROFILE GPU_LEGACY +#define GPU_FEATURE_PROFILE GPU_CORE +#define GPU_TRANSFORM_PROFILE GPU_CORE #endif From 7325e6a7b7fcfa280ab3ed4534f84db804402880 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Wed, 29 Jul 2015 14:57:06 +0200 Subject: [PATCH 08/65] Send ping requests to the nodes (AvatarMixer, AudioMixer, EntityServer) that the Assignment agent connected with to keep the connections alive. --- assignment-client/src/Agent.cpp | 27 ++++++++++++++++++++++++++- assignment-client/src/Agent.h | 2 ++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index da588bc316..77b47f4d7c 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -107,6 +107,7 @@ void Agent::handleAudioPacket(QSharedPointer packet) { } const QString AGENT_LOGGING_NAME = "agent"; +const int PING_INTERVAL = 1000; void Agent::run() { ThreadedAssignment::commonInit(AGENT_LOGGING_NAME, NodeType::Agent); @@ -118,6 +119,10 @@ void Agent::run() { << NodeType::EntityServer ); + _pingTimer = new QTimer(this); + connect(_pingTimer, SIGNAL(timeout()), SLOT(sendPingRequests())); + _pingTimer->start(PING_INTERVAL); + // figure out the URL for the script for this agent assignment QUrl scriptURL; if (_payload.isEmpty()) { @@ -193,7 +198,27 @@ void Agent::run() { void Agent::aboutToFinish() { _scriptEngine.stop(); - + + _pingTimer->stop(); + delete _pingTimer; + // our entity tree is going to go away so tell that to the EntityScriptingInterface DependencyManager::get()->setEntityTree(NULL); } + +void Agent::sendPingRequests() { + auto nodeList = DependencyManager::get(); + + nodeList->eachMatchingNode([](const SharedNodePointer& node)->bool { + switch (node->getType()) { + case NodeType::AvatarMixer: + case NodeType::AudioMixer: + case NodeType::EntityServer: + return true; + default: + return false; + } + }, [nodeList](const SharedNodePointer& node) { + nodeList->sendPacket(nodeList->constructPingPacket(), *node); + }); +} diff --git a/assignment-client/src/Agent.h b/assignment-client/src/Agent.h index 241e14439c..4c207e59aa 100644 --- a/assignment-client/src/Agent.h +++ b/assignment-client/src/Agent.h @@ -58,11 +58,13 @@ private slots: void handleAudioPacket(QSharedPointer packet); void handleOctreePacket(QSharedPointer packet, SharedNodePointer senderNode); void handleJurisdictionPacket(QSharedPointer packet, SharedNodePointer senderNode); + void sendPingRequests(); private: ScriptEngine _scriptEngine; EntityEditPacketSender _entityEditSender; EntityTreeHeadlessViewer _entityViewer; + QTimer* _pingTimer; MixedAudioStream _receivedAudioStream; float _lastReceivedAudioLoudness; From 0a5ada3c09dfe906031d7f2b4b3dc291f85dd409 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 29 Jul 2015 09:02:24 -0700 Subject: [PATCH 09/65] remove legacy shapes --- interface/src/avatar/Avatar.h | 3 - libraries/avatars/src/AvatarData.h | 5 - libraries/entities/src/EntityItem.h | 1 - .../entities/src/EntityScriptingInterface.h | 1 - libraries/entities/src/TextEntityItem.cpp | 47 +- libraries/entities/src/WebEntityItem.cpp | 49 +- libraries/fbx/src/OBJReader.cpp | 1 - libraries/octree/src/Octree.cpp | 8 - libraries/octree/src/OctreeElement.cpp | 1 - libraries/octree/src/OctreeElement.h | 1 - libraries/render-utils/src/Model.cpp | 1 - libraries/render-utils/src/PhysicsEntity.h | 3 - libraries/script-engine/src/ScriptEngine.cpp | 3 - libraries/shared/src/AACubeShape.cpp | 76 - libraries/shared/src/AACubeShape.h | 50 - libraries/shared/src/CapsuleShape.cpp | 218 -- libraries/shared/src/CapsuleShape.h | 62 - libraries/shared/src/CollisionInfo.cpp | 110 - libraries/shared/src/CollisionInfo.h | 101 - libraries/shared/src/GeometryUtil.cpp | 1 - libraries/shared/src/ListShape.cpp | 90 - libraries/shared/src/ListShape.h | 72 - libraries/shared/src/PlaneShape.cpp | 78 - libraries/shared/src/PlaneShape.h | 30 - libraries/shared/src/RayIntersectionInfo.h | 37 - libraries/shared/src/Shape.h | 144 -- libraries/shared/src/ShapeCollider.cpp | 1211 ----------- libraries/shared/src/ShapeCollider.h | 156 -- libraries/shared/src/SphereShape.cpp | 51 - libraries/shared/src/SphereShape.h | 63 - libraries/shared/src/StreamUtils.cpp | 32 - libraries/shared/src/StreamUtils.h | 10 - tests/physics/src/CollisionInfoTests.cpp | 70 - tests/physics/src/CollisionInfoTests.h | 29 - tests/physics/src/ShapeColliderTests.cpp | 1936 ----------------- tests/physics/src/ShapeColliderTests.h | 60 - 36 files changed, 15 insertions(+), 4796 deletions(-) delete mode 100644 libraries/shared/src/AACubeShape.cpp delete mode 100644 libraries/shared/src/AACubeShape.h delete mode 100644 libraries/shared/src/CapsuleShape.cpp delete mode 100644 libraries/shared/src/CapsuleShape.h delete mode 100644 libraries/shared/src/CollisionInfo.cpp delete mode 100644 libraries/shared/src/CollisionInfo.h delete mode 100644 libraries/shared/src/ListShape.cpp delete mode 100644 libraries/shared/src/ListShape.h delete mode 100644 libraries/shared/src/PlaneShape.cpp delete mode 100644 libraries/shared/src/PlaneShape.h delete mode 100644 libraries/shared/src/RayIntersectionInfo.h delete mode 100644 libraries/shared/src/Shape.h delete mode 100644 libraries/shared/src/ShapeCollider.cpp delete mode 100644 libraries/shared/src/ShapeCollider.h delete mode 100644 libraries/shared/src/SphereShape.cpp delete mode 100644 libraries/shared/src/SphereShape.h delete mode 100644 tests/physics/src/CollisionInfoTests.cpp delete mode 100644 tests/physics/src/CollisionInfoTests.h delete mode 100644 tests/physics/src/ShapeColliderTests.cpp delete mode 100644 tests/physics/src/ShapeColliderTests.h diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index dcf37c9a1e..ea4c95aca2 100644 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -168,9 +168,6 @@ public: friend class AvatarManager; -signals: - void collisionWithAvatar(const QUuid& myUUID, const QUuid& theirUUID, const CollisionInfo& collision); - protected: SkeletonModel _skeletonModel; glm::vec3 _skeletonOffset; diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 60c643eff9..919004aa84 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -46,7 +46,6 @@ typedef unsigned long long quint64; #include #include -#include #include #include #include @@ -257,10 +256,6 @@ public: const HeadData* getHeadData() const { return _headData; } const HandData* getHandData() const { return _handData; } - virtual bool findSphereCollisions(const glm::vec3& particleCenter, float particleRadius, CollisionList& collisions) { - return false; - } - bool hasIdentityChangedAfterParsing(NLPacket& packet); QByteArray identityByteArray(); diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index b698a5c511..57f8883cea 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -18,7 +18,6 @@ #include #include // for Animation, AnimationCache, and AnimationPointer classes -#include #include // for EncodeBitstreamParams class #include // for OctreeElement::AppendState #include diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index c232437757..d3710c6ccc 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -16,7 +16,6 @@ #include -#include #include #include #include diff --git a/libraries/entities/src/TextEntityItem.cpp b/libraries/entities/src/TextEntityItem.cpp index a851d025fc..3ab15ba6cb 100644 --- a/libraries/entities/src/TextEntityItem.cpp +++ b/libraries/entities/src/TextEntityItem.cpp @@ -15,7 +15,7 @@ #include #include -#include +#include #include "EntityTree.h" #include "EntityTreeElement.h" @@ -128,46 +128,13 @@ void TextEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBits } - bool TextEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, bool& keepSearching, OctreeElement*& element, float& distance, BoxFace& face, void** intersectedObject, bool precisionPicking) const { - - RayIntersectionInfo rayInfo; - rayInfo._rayStart = origin; - rayInfo._rayDirection = direction; - rayInfo._rayLength = std::numeric_limits::max(); - - PlaneShape plane; - - const glm::vec3 UNROTATED_NORMAL(0.0f, 0.0f, -1.0f); - glm::vec3 normal = getRotation() * UNROTATED_NORMAL; - plane.setNormal(normal); - plane.setPoint(getPosition()); // the position is definitely a point on our plane - - bool intersects = plane.findRayIntersection(rayInfo); - - if (intersects) { - glm::vec3 hitAt = origin + (direction * rayInfo._hitDistance); - // now we know the point the ray hit our plane - - glm::mat4 rotation = glm::mat4_cast(getRotation()); - glm::mat4 translation = glm::translate(getPosition()); - glm::mat4 entityToWorldMatrix = translation * rotation; - glm::mat4 worldToEntityMatrix = glm::inverse(entityToWorldMatrix); - - glm::vec3 dimensions = getDimensions(); - glm::vec3 registrationPoint = getRegistrationPoint(); - glm::vec3 corner = -(dimensions * registrationPoint); - AABox entityFrameBox(corner, dimensions); - - glm::vec3 entityFrameHitAt = glm::vec3(worldToEntityMatrix * glm::vec4(hitAt, 1.0f)); - - intersects = entityFrameBox.contains(entityFrameHitAt); - } - - if (intersects) { - distance = rayInfo._hitDistance; - } - return intersects; + glm::vec3 dimensions = getDimensions(); + glm::vec2 xyDimensions(dimensions.x, dimensions.y); + glm::quat rotation = getRotation(); + glm::vec3 position = getPosition() + rotation * + (dimensions * (getRegistrationPoint() - ENTITY_ITEM_DEFAULT_REGISTRATION_POINT)); + return findRayRectangleIntersection(origin, direction, rotation, position, xyDimensions, distance); } diff --git a/libraries/entities/src/WebEntityItem.cpp b/libraries/entities/src/WebEntityItem.cpp index 2cb233bbee..c3dc757199 100644 --- a/libraries/entities/src/WebEntityItem.cpp +++ b/libraries/entities/src/WebEntityItem.cpp @@ -13,7 +13,7 @@ #include #include -#include +#include #include "EntityTree.h" #include "EntityTreeElement.h" @@ -98,50 +98,17 @@ void WebEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBitst APPEND_ENTITY_PROPERTY(PROP_SOURCE_URL, _sourceUrl); } - bool WebEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, bool& keepSearching, OctreeElement*& element, float& distance, BoxFace& face, void** intersectedObject, bool precisionPicking) const { - - RayIntersectionInfo rayInfo; - rayInfo._rayStart = origin; - rayInfo._rayDirection = direction; - rayInfo._rayLength = std::numeric_limits::max(); - - PlaneShape plane; - - const glm::vec3 UNROTATED_NORMAL(0.0f, 0.0f, -1.0f); - glm::vec3 normal = getRotation() * UNROTATED_NORMAL; - plane.setNormal(normal); - plane.setPoint(getPosition()); // the position is definitely a point on our plane - - bool intersects = plane.findRayIntersection(rayInfo); - - if (intersects) { - glm::vec3 hitAt = origin + (direction * rayInfo._hitDistance); - // now we know the point the ray hit our plane - - glm::mat4 rotation = glm::mat4_cast(getRotation()); - glm::mat4 translation = glm::translate(getPosition()); - glm::mat4 entityToWorldMatrix = translation * rotation; - glm::mat4 worldToEntityMatrix = glm::inverse(entityToWorldMatrix); - - glm::vec3 dimensions = getDimensions(); - glm::vec3 registrationPoint = getRegistrationPoint(); - glm::vec3 corner = -(dimensions * registrationPoint); - AABox entityFrameBox(corner, dimensions); - - glm::vec3 entityFrameHitAt = glm::vec3(worldToEntityMatrix * glm::vec4(hitAt, 1.0f)); - - intersects = entityFrameBox.contains(entityFrameHitAt); - } - - if (intersects) { - distance = rayInfo._hitDistance; - } - return intersects; + glm::vec3 dimensions = getDimensions(); + glm::vec2 xyDimensions(dimensions.x, dimensions.y); + glm::quat rotation = getRotation(); + glm::vec3 position = getPosition() + rotation * + (dimensions * (getRegistrationPoint() - ENTITY_ITEM_DEFAULT_REGISTRATION_POINT)); + return findRayRectangleIntersection(origin, direction, rotation, position, xyDimensions, distance); } - + void WebEntityItem::setSourceUrl(const QString& value) { if (_sourceUrl != value) { _sourceUrl = value; diff --git a/libraries/fbx/src/OBJReader.cpp b/libraries/fbx/src/OBJReader.cpp index 35ba437745..693c1f64d3 100644 --- a/libraries/fbx/src/OBJReader.cpp +++ b/libraries/fbx/src/OBJReader.cpp @@ -23,7 +23,6 @@ #include #include "FBXReader.h" #include "OBJReader.h" -#include "Shape.h" #include "ModelFormatLogging.h" diff --git a/libraries/octree/src/Octree.cpp b/libraries/octree/src/Octree.cpp index 203ff2b072..48f5f99906 100644 --- a/libraries/octree/src/Octree.cpp +++ b/libraries/octree/src/Octree.cpp @@ -38,7 +38,6 @@ #include #include #include -#include #include #include "CoverageMap.h" @@ -791,13 +790,6 @@ public: bool found; }; -class ShapeArgs { -public: - const Shape* shape; - CollisionList& collisions; - bool found; -}; - class ContentArgs { public: AACube cube; diff --git a/libraries/octree/src/OctreeElement.cpp b/libraries/octree/src/OctreeElement.cpp index e11a11fc8e..23034e636a 100644 --- a/libraries/octree/src/OctreeElement.cpp +++ b/libraries/octree/src/OctreeElement.cpp @@ -19,7 +19,6 @@ #include #include #include -#include #include "AACube.h" #include "OctalCode.h" diff --git a/libraries/octree/src/OctreeElement.h b/libraries/octree/src/OctreeElement.h index 830655242f..d2f3f7d3aa 100644 --- a/libraries/octree/src/OctreeElement.h +++ b/libraries/octree/src/OctreeElement.h @@ -24,7 +24,6 @@ #include "ViewFrustum.h" #include "OctreeConstants.h" -class CollisionList; class EncodeBitstreamParams; class Octree; class OctreeElement; diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index c28dbf4247..d352da55ee 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -16,7 +16,6 @@ #include #include -#include #include #include #include diff --git a/libraries/render-utils/src/PhysicsEntity.h b/libraries/render-utils/src/PhysicsEntity.h index 3b527c7827..f36473af26 100644 --- a/libraries/render-utils/src/PhysicsEntity.h +++ b/libraries/render-utils/src/PhysicsEntity.h @@ -18,9 +18,6 @@ #include #include -#include -#include - class PhysicsEntity { public: diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index d5e727657c..cfd6cda56b 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -20,7 +20,6 @@ #include #include #include -#include #include #include #include @@ -366,8 +365,6 @@ void ScriptEngine::init() { // constants globalObject().setProperty("TREE_SCALE", newVariant(QVariant(TREE_SCALE))); - globalObject().setProperty("COLLISION_GROUP_ENVIRONMENT", newVariant(QVariant(COLLISION_GROUP_ENVIRONMENT))); - globalObject().setProperty("COLLISION_GROUP_AVATARS", newVariant(QVariant(COLLISION_GROUP_AVATARS))); } QScriptValue ScriptEngine::registerGlobalObject(const QString& name, QObject* object) { diff --git a/libraries/shared/src/AACubeShape.cpp b/libraries/shared/src/AACubeShape.cpp deleted file mode 100644 index 5c40b3bb64..0000000000 --- a/libraries/shared/src/AACubeShape.cpp +++ /dev/null @@ -1,76 +0,0 @@ -// -// AACubeShape.cpp -// libraries/shared/src -// -// Created by Andrew Meadows on 2014.08.22 -// 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 -#include - -#include "AACubeShape.h" -#include "NumericalConstants.h" // for SQUARE_ROOT_OF_3 - -glm::vec3 faceNormals[3] = { glm::vec3(1.0f, 0.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f), glm::vec3(0.0f, 0.0f, 1.0f) }; - -bool AACubeShape::findRayIntersection(RayIntersectionInfo& intersection) const { - // A = ray point - // B = cube center - glm::vec3 BA = _translation - intersection._rayStart; - - // check for ray intersection with cube's bounding sphere - // a = distance along line to closest approach to B - float a = glm::dot(intersection._rayDirection, BA); - // b2 = squared distance from cube center to point of closest approach - float b2 = glm::length2(a * intersection._rayDirection - BA); - // r = bounding radius of cube - float halfSide = 0.5f * _scale; - const float r = SQUARE_ROOT_OF_3 * halfSide; - if (b2 > r * r) { - // line doesn't hit cube's bounding sphere - return false; - } - - // check for tuncated/short ray - // maxLength = maximum possible distance between rayStart and center of cube - const float maxLength = glm::min(intersection._rayLength, intersection._hitDistance) + r; - if (a * a + b2 > maxLength * maxLength) { - // ray is not long enough to reach cube's bounding sphere - // NOTE: we don't fall in here when ray's length if FLT_MAX because maxLength^2 will be inf or nan - return false; - } - - // the trivial checks have been exhausted, so must trace to each face - bool hit = false; - for (int i = 0; i < 3; ++i) { - for (float sign = -1.0f; sign < 2.0f; sign += 2.0f) { - glm::vec3 faceNormal = sign * faceNormals[i]; - float rayDotPlane = glm::dot(intersection._rayDirection, faceNormal); - if (glm::abs(rayDotPlane) > EPSILON) { - float distanceToFace = (halfSide + glm::dot(BA, faceNormal)) / rayDotPlane; - if (distanceToFace >= 0.0f) { - glm::vec3 point = distanceToFace * intersection._rayDirection - BA; - int j = (i + 1) % 3; - int k = (i + 2) % 3; - glm::vec3 secondNormal = faceNormals[j]; - glm::vec3 thirdNormal = faceNormals[k]; - if (glm::abs(glm::dot(point, secondNormal)) > halfSide || - glm::abs(glm::dot(point, thirdNormal)) > halfSide) { - continue; - } - if (distanceToFace < intersection._hitDistance && distanceToFace < intersection._rayLength) { - intersection._hitDistance = distanceToFace; - intersection._hitNormal = faceNormal; - intersection._hitShape = const_cast(this); - hit = true; - } - } - } - } - } - return hit; -} diff --git a/libraries/shared/src/AACubeShape.h b/libraries/shared/src/AACubeShape.h deleted file mode 100644 index da7ba9d53f..0000000000 --- a/libraries/shared/src/AACubeShape.h +++ /dev/null @@ -1,50 +0,0 @@ -// -// AACubeShape.h -// libraries/shared/src -// -// Created by Andrew Meadows on 2014.08.22 -// 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_AACubeShape_h -#define hifi_AACubeShape_h - -#include -#include "Shape.h" - -class AACubeShape : public Shape { -public: - AACubeShape() : Shape(AACUBE_SHAPE), _scale(1.0f) { } - AACubeShape(float scale) : Shape(AACUBE_SHAPE), _scale(scale) { } - AACubeShape(float scale, const glm::vec3& position) : Shape(AACUBE_SHAPE, position), _scale(scale) { } - - virtual ~AACubeShape() { } - - float getScale() const { return _scale; } - void setScale(float scale) { _scale = scale; } - - bool findRayIntersection(RayIntersectionInfo& intersection) const; - - float getVolume() const { return _scale * _scale * _scale; } - virtual QDebug& dumpToDebug(QDebug& debugConext) const; - -protected: - float _scale; -}; - -inline QDebug& AACubeShape::dumpToDebug(QDebug& debugConext) const { - debugConext << "AACubeShape[ (" - << "type: " << getType() - << "position: " - << getTranslation().x << ", " << getTranslation().y << ", " << getTranslation().z - << "scale: " - << getScale() - << "]"; - - return debugConext; -} - -#endif // hifi_AACubeShape_h diff --git a/libraries/shared/src/CapsuleShape.cpp b/libraries/shared/src/CapsuleShape.cpp deleted file mode 100644 index ec71ebadbb..0000000000 --- a/libraries/shared/src/CapsuleShape.cpp +++ /dev/null @@ -1,218 +0,0 @@ -// -// CapsuleShape.cpp -// libraries/shared/src -// -// Created by Andrew Meadows on 02/20/2014. -// 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 -#include - -#include "CapsuleShape.h" -#include "GeometryUtil.h" -#include "NumericalConstants.h" - -CapsuleShape::CapsuleShape() : Shape(CAPSULE_SHAPE), _radius(0.0f), _halfHeight(0.0f) {} - -CapsuleShape::CapsuleShape(float radius, float halfHeight) : Shape(CAPSULE_SHAPE), - _radius(radius), _halfHeight(halfHeight) { - updateBoundingRadius(); -} - -CapsuleShape::CapsuleShape(float radius, float halfHeight, const glm::vec3& position, const glm::quat& rotation) : - Shape(CAPSULE_SHAPE, position, rotation), _radius(radius), _halfHeight(halfHeight) { - updateBoundingRadius(); -} - -CapsuleShape::CapsuleShape(float radius, const glm::vec3& startPoint, const glm::vec3& endPoint) : - Shape(CAPSULE_SHAPE), _radius(radius), _halfHeight(0.0f) { - setEndPoints(startPoint, endPoint); -} - -/// \param[out] startPoint is the center of start cap -void CapsuleShape::getStartPoint(glm::vec3& startPoint) const { - startPoint = _translation - _rotation * glm::vec3(0.0f, _halfHeight, 0.0f); -} - -/// \param[out] endPoint is the center of the end cap -void CapsuleShape::getEndPoint(glm::vec3& endPoint) const { - endPoint = _translation + _rotation * glm::vec3(0.0f, _halfHeight, 0.0f); -} - -void CapsuleShape::computeNormalizedAxis(glm::vec3& axis) const { - // default axis of a capsule is along the yAxis - axis = _rotation * DEFAULT_CAPSULE_AXIS; -} - -void CapsuleShape::setRadius(float radius) { - _radius = radius; - updateBoundingRadius(); -} - -void CapsuleShape::setHalfHeight(float halfHeight) { - _halfHeight = halfHeight; - updateBoundingRadius(); -} - -void CapsuleShape::setRadiusAndHalfHeight(float radius, float halfHeight) { - _radius = radius; - _halfHeight = halfHeight; - updateBoundingRadius(); -} - -void CapsuleShape::setEndPoints(const glm::vec3& startPoint, const glm::vec3& endPoint) { - glm::vec3 axis = endPoint - startPoint; - _translation = 0.5f * (endPoint + startPoint); - float height = glm::length(axis); - if (height > EPSILON) { - _halfHeight = 0.5f * height; - axis /= height; - _rotation = computeNewRotation(axis); - } - updateBoundingRadius(); -} - -// helper -bool findRayIntersectionWithCap(const glm::vec3& sphereCenter, float sphereRadius, - const glm::vec3& capsuleCenter, RayIntersectionInfo& intersection) { - float r2 = sphereRadius * sphereRadius; - - // compute closest approach (CA) - float a = glm::dot(sphereCenter - intersection._rayStart, intersection._rayDirection); // a = distance from ray-start to CA - float b2 = glm::distance2(sphereCenter, intersection._rayStart + a * intersection._rayDirection); // b2 = squared distance from sphere-center to CA - if (b2 > r2) { - // ray does not hit sphere - return false; - } - float c = sqrtf(r2 - b2); // c = distance from CA to sphere surface along intersection._rayDirection - float d2 = glm::distance2(intersection._rayStart, sphereCenter); // d2 = squared distance from sphere-center to ray-start - float distance = FLT_MAX; - if (a < 0.0f) { - // ray points away from sphere-center - if (d2 > r2) { - // ray starts outside sphere - return false; - } - // ray starts inside sphere - distance = c + a; - } else if (d2 > r2) { - // ray starts outside sphere - distance = a - c; - } else { - // ray starts inside sphere - distance = a + c; - } - if (distance > 0.0f && distance < intersection._rayLength && distance < intersection._hitDistance) { - glm::vec3 sphereCenterToHitPoint = intersection._rayStart + distance * intersection._rayDirection - sphereCenter; - if (glm::dot(sphereCenterToHitPoint, sphereCenter - capsuleCenter) >= 0.0f) { - intersection._hitDistance = distance; - intersection._hitNormal = glm::normalize(sphereCenterToHitPoint); - return true; - } - } - return false; -} - -bool CapsuleShape::findRayIntersectionWithCaps(const glm::vec3& capsuleCenter, RayIntersectionInfo& intersection) const { - glm::vec3 capCenter; - getStartPoint(capCenter); - bool hit = findRayIntersectionWithCap(capCenter, _radius, capsuleCenter, intersection); - getEndPoint(capCenter); - hit = findRayIntersectionWithCap(capCenter, _radius, capsuleCenter, intersection) || hit; - if (hit) { - intersection._hitShape = const_cast(this); - } - return hit; -} - -bool CapsuleShape::findRayIntersection(RayIntersectionInfo& intersection) const { - // ray is U, capsule is V - glm::vec3 axisV; - computeNormalizedAxis(axisV); - glm::vec3 centerV = getTranslation(); - - // first handle parallel case - float uDotV = glm::dot(axisV, intersection._rayDirection); - glm::vec3 UV = intersection._rayStart - centerV; - if (glm::abs(1.0f - glm::abs(uDotV)) < EPSILON) { - // line and cylinder are parallel - float distanceV = glm::dot(UV, intersection._rayDirection); - if (glm::length2(UV - distanceV * intersection._rayDirection) <= _radius * _radius) { - // ray is inside cylinder's radius and might intersect caps - return findRayIntersectionWithCaps(centerV, intersection); - } - return false; - } - - // Given a line with point 'U' and normalized direction 'u' and - // a cylinder with axial point 'V', radius 'r', and normalized direction 'v' - // the intersection of the two is on the line at distance 't' from 'U'. - // - // Determining the values of t reduces to solving a quadratic equation: At^2 + Bt + C = 0 - // - // where: - // - // UV = U-V - // w = u-(u.v)v - // Q = UV-(UV.v)v - // - // A = w^2 - // B = 2(w.Q) - // C = Q^2 - r^2 - - glm::vec3 w = intersection._rayDirection - uDotV * axisV; - glm::vec3 Q = UV - glm::dot(UV, axisV) * axisV; - - // we save a few multiplies by storing 2*A rather than just A - float A2 = 2.0f * glm::dot(w, w); - float B = 2.0f * glm::dot(w, Q); - - // since C is only ever used once (in the determinant) we compute it inline - float determinant = B * B - 2.0f * A2 * (glm::dot(Q, Q) - _radius * _radius); - if (determinant < 0.0f) { - return false; - } - float hitLow = (-B - sqrtf(determinant)) / A2; - float hitHigh = -(hitLow + 2.0f * B / A2); - - if (hitLow > hitHigh) { - // re-arrange so hitLow is always the smaller value - float temp = hitHigh; - hitHigh = hitLow; - hitLow = temp; - } - if (hitLow < 0.0f) { - if (hitHigh < 0.0f) { - // capsule is completely behind rayStart - return false; - } - hitLow = hitHigh; - } - - glm::vec3 p = intersection._rayStart + hitLow * intersection._rayDirection; - float d = glm::dot(p - centerV, axisV); - if (glm::abs(d) <= getHalfHeight()) { - // we definitely hit the cylinder wall - intersection._hitDistance = hitLow; - intersection._hitNormal = glm::normalize(p - centerV - d * axisV); - intersection._hitShape = const_cast(this); - return true; - } - - // ray still might hit the caps - return findRayIntersectionWithCaps(centerV, intersection); -} - -// static -glm::quat CapsuleShape::computeNewRotation(const glm::vec3& newAxis) { - float angle = glm::angle(newAxis, DEFAULT_CAPSULE_AXIS); - if (angle > EPSILON) { - glm::vec3 rotationAxis = glm::normalize(glm::cross(DEFAULT_CAPSULE_AXIS, newAxis)); - return glm::angleAxis(angle, rotationAxis); - } - return glm::quat(); -} diff --git a/libraries/shared/src/CapsuleShape.h b/libraries/shared/src/CapsuleShape.h deleted file mode 100644 index 32c09696d7..0000000000 --- a/libraries/shared/src/CapsuleShape.h +++ /dev/null @@ -1,62 +0,0 @@ -// -// CapsuleShape.h -// libraries/shared/src -// -// Created by Andrew Meadows on 02/20/2014. -// 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_CapsuleShape_h -#define hifi_CapsuleShape_h - -#include "NumericalConstants.h" -#include "Shape.h" - -// default axis of CapsuleShape is Y-axis -const glm::vec3 DEFAULT_CAPSULE_AXIS(0.0f, 1.0f, 0.0f); - - -class CapsuleShape : public Shape { -public: - CapsuleShape(); - CapsuleShape(float radius, float halfHeight); - CapsuleShape(float radius, float halfHeight, const glm::vec3& position, const glm::quat& rotation); - CapsuleShape(float radius, const glm::vec3& startPoint, const glm::vec3& endPoint); - - virtual ~CapsuleShape() {} - - float getRadius() const { return _radius; } - virtual float getHalfHeight() const { return _halfHeight; } - - /// \param[out] startPoint is the center of start cap - virtual void getStartPoint(glm::vec3& startPoint) const; - - /// \param[out] endPoint is the center of the end cap - virtual void getEndPoint(glm::vec3& endPoint) const; - - virtual void computeNormalizedAxis(glm::vec3& axis) const; - - void setRadius(float radius); - virtual void setHalfHeight(float height); - virtual void setRadiusAndHalfHeight(float radius, float height); - - /// Sets the endpoints and updates center, rotation, and halfHeight to agree. - virtual void setEndPoints(const glm::vec3& startPoint, const glm::vec3& endPoint); - - bool findRayIntersection(RayIntersectionInfo& intersection) const; - - virtual float getVolume() const { return (PI * _radius * _radius) * (1.3333333333f * _radius + getHalfHeight()); } - -protected: - bool findRayIntersectionWithCaps(const glm::vec3& capsuleCenter, RayIntersectionInfo& intersection) const; - virtual void updateBoundingRadius() { _boundingRadius = _radius + getHalfHeight(); } - static glm::quat computeNewRotation(const glm::vec3& newAxis); - - float _radius; - float _halfHeight; -}; - -#endif // hifi_CapsuleShape_h diff --git a/libraries/shared/src/CollisionInfo.cpp b/libraries/shared/src/CollisionInfo.cpp deleted file mode 100644 index 7f145efb20..0000000000 --- a/libraries/shared/src/CollisionInfo.cpp +++ /dev/null @@ -1,110 +0,0 @@ -// -// CollisionInfo.cpp -// libraries/shared/src -// -// Created by Andrew Meadows on 02/14/2014. -// 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 "CollisionInfo.h" -#include "NumericalConstants.h" -#include "Shape.h" - -CollisionInfo::CollisionInfo() : - _data(NULL), - _intData(0), - _shapeA(NULL), - _shapeB(NULL), - _damping(0.0f), - _elasticity(1.0f), - _contactPoint(0.0f), - _penetration(0.0f), - _addedVelocity(0.0f) { -} - -quint64 CollisionInfo::getShapePairKey() const { - if (_shapeB == NULL || _shapeA == NULL) { - // zero is an invalid key - return 0; - } - quint32 idA = _shapeA->getID(); - quint32 idB = _shapeB->getID(); - return idA < idB ? ((quint64)idA << 32) + (quint64)idB : ((quint64)idB << 32) + (quint64)idA; -} - -CollisionList::CollisionList(int maxSize) : - _maxSize(maxSize), - _size(0) { - _collisions.resize(_maxSize); -} - -void CollisionInfo::apply() { - assert(_shapeA); - // NOTE: Shape::computeEffectiveMass() has side effects: computes and caches partial Lagrangian coefficients - Shape* shapeA = const_cast(_shapeA); - float massA = shapeA->computeEffectiveMass(_penetration, _contactPoint); - float massB = MAX_SHAPE_MASS; - float totalMass = massA + massB; - if (_shapeB) { - Shape* shapeB = const_cast(_shapeB); - massB = shapeB->computeEffectiveMass(-_penetration, _contactPoint - _penetration); - totalMass = massA + massB; - if (totalMass < EPSILON) { - massA = massB = 1.0f; - totalMass = 2.0f; - } - // remember that _penetration points from A into B - shapeB->accumulateDelta(massA / totalMass, _penetration); - } - // NOTE: Shape::accumulateDelta() uses the coefficients from previous call to Shape::computeEffectiveMass() - // remember that _penetration points from A into B - shapeA->accumulateDelta(massB / totalMass, -_penetration); -} - -CollisionInfo* CollisionList::getNewCollision() { - // return pointer to existing CollisionInfo, or NULL of list is full - return (_size < _maxSize) ? &(_collisions[_size++]) : NULL; -} - -void CollisionList::deleteLastCollision() { - if (_size > 0) { - --_size; - } -} - -CollisionInfo* CollisionList::getCollision(int index) { - return (index > -1 && index < _size) ? &(_collisions[index]) : NULL; -} - -CollisionInfo* CollisionList::getLastCollision() { - return (_size > 0) ? &(_collisions[_size - 1]) : NULL; -} - -void CollisionList::clear() { - // we rely on the external context to properly set or clear the data members of CollisionInfos - /* - for (int i = 0; i < _size; ++i) { - // we only clear the important stuff - CollisionInfo& collision = _collisions[i]; - //collision._data = NULL; - //collision._intData = 0; - //collision._floatDAta = 0.0f; - //collision._vecData = glm::vec3(0.0f); - //collision._shapeA = NULL; - //collision._shapeB = NULL; - //collision._damping; - //collision._elasticity; - //collision._contactPoint; - //collision._penetration; - //collision._addedVelocity; - } - */ - _size = 0; -} - -CollisionInfo* CollisionList::operator[](int index) { - return (index > -1 && index < _size) ? &(_collisions[index]) : NULL; -} diff --git a/libraries/shared/src/CollisionInfo.h b/libraries/shared/src/CollisionInfo.h deleted file mode 100644 index e6daf949b3..0000000000 --- a/libraries/shared/src/CollisionInfo.h +++ /dev/null @@ -1,101 +0,0 @@ -// -// CollisionInfo.h -// libraries/shared/src -// -// Created by Andrew Meadows on 02/14/2014. -// 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_CollisionInfo_h -#define hifi_CollisionInfo_h - -#include -#include - -#include -#include - -class Shape; - -const quint32 COLLISION_GROUP_ENVIRONMENT = 1U << 0; -const quint32 COLLISION_GROUP_AVATARS = 1U << 1; -const quint32 COLLISION_GROUP_VOXELS = 1U << 2; -const quint32 VALID_COLLISION_GROUPS = 0x0f; - -// CollisionInfo contains details about the collision between two things: BodyA and BodyB. -// The assumption is that the context that analyzes the collision knows about BodyA but -// does not necessarily know about BodyB. Hence the data storred in the CollisionInfo -// is expected to be relative to BodyA (for example the penetration points from A into B). - -class CollisionInfo { -public: - CollisionInfo(); - ~CollisionInfo() {} - - // TODO: Andrew to get rid of these data members - void* _data; - int _intData; - float _floatData; - glm::vec3 _vecData; - - /// accumulates position changes for the shapes in this collision to resolve penetration - void apply(); - - Shape* getShapeA() const { return const_cast(_shapeA); } - Shape* getShapeB() const { return const_cast(_shapeB); } - - /// \return unique key for shape pair - quint64 getShapePairKey() const; - - const Shape* _shapeA; // pointer to shapeA in this collision - const Shape* _shapeB; // pointer to shapeB in this collision - - void* _extraData; // pointer to extraData for this collision, opaque to the collision info, useful for external data - - float _damping; // range [0,1] of friction coeficient - float _elasticity; // range [0,1] of energy conservation - glm::vec3 _contactPoint; // world-frame point on BodyA that is deepest into BodyB - glm::vec3 _penetration; // depth that BodyA penetrates into BodyB - glm::vec3 _addedVelocity; // velocity of BodyB -}; - -// CollisionList is intended to be a recycled container. Fill the CollisionInfo's, -// use them, and then clear them for the next frame or context. - -class CollisionList { -public: - CollisionList(int maxSize); - - /// \return pointer to next collision. NULL if list is full. - CollisionInfo* getNewCollision(); - - /// \forget about collision at the end - void deleteLastCollision(); - - /// \return pointer to collision by index. NULL if index out of bounds. - CollisionInfo* getCollision(int index); - - /// \return pointer to last collision on the list. NULL if list is empty - CollisionInfo* getLastCollision(); - - /// \return true if list is full - bool isFull() const { return _size == _maxSize; } - - /// \return number of valid collisions - int size() const { return _size; } - - /// Clear valid collisions. - void clear(); - - CollisionInfo* operator[](int index); - -private: - int _maxSize; // the container cannot get larger than this - int _size; // the current number of valid collisions in the list - QVector _collisions; -}; - -#endif // hifi_CollisionInfo_h diff --git a/libraries/shared/src/GeometryUtil.cpp b/libraries/shared/src/GeometryUtil.cpp index b88166995a..b612fe0696 100644 --- a/libraries/shared/src/GeometryUtil.cpp +++ b/libraries/shared/src/GeometryUtil.cpp @@ -15,7 +15,6 @@ #include "GeometryUtil.h" #include "NumericalConstants.h" -#include "RayIntersectionInfo.h" glm::vec3 computeVectorFromPointToSegment(const glm::vec3& point, const glm::vec3& start, const glm::vec3& end) { // compute the projection of the point vector onto the segment vector diff --git a/libraries/shared/src/ListShape.cpp b/libraries/shared/src/ListShape.cpp deleted file mode 100644 index 255330c713..0000000000 --- a/libraries/shared/src/ListShape.cpp +++ /dev/null @@ -1,90 +0,0 @@ -// -// ListShape.cpp -// libraries/shared/src -// -// Created by Andrew Meadows on 02/20/2014. -// 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 "ListShape.h" - -// ListShapeEntry - -void ListShapeEntry::updateTransform(const glm::vec3& rootPosition, const glm::quat& rootRotation) { - _shape->setTranslation(rootPosition + rootRotation * _localPosition); - _shape->setRotation(_localRotation * rootRotation); -} - -// ListShape - -ListShape::~ListShape() { - clear(); -} - -void ListShape::setTranslation(const glm::vec3& position) { - _subShapeTransformsAreDirty = true; - Shape::setTranslation(position); -} - -void ListShape::setRotation(const glm::quat& rotation) { - _subShapeTransformsAreDirty = true; - Shape::setRotation(rotation); -} - -const Shape* ListShape::getSubShape(int index) const { - if (index < 0 || index > _subShapeEntries.size()) { - return NULL; - } - return _subShapeEntries[index]._shape; -} - -void ListShape::updateSubTransforms() { - if (_subShapeTransformsAreDirty) { - for (int i = 0; i < _subShapeEntries.size(); ++i) { - _subShapeEntries[i].updateTransform(_translation, _rotation); - } - _subShapeTransformsAreDirty = false; - } -} - -void ListShape::addShape(Shape* shape, const glm::vec3& localPosition, const glm::quat& localRotation) { - if (shape) { - ListShapeEntry entry; - entry._shape = shape; - entry._localPosition = localPosition; - entry._localRotation = localRotation; - _subShapeEntries.push_back(entry); - } -} - -void ListShape::setShapes(QVector& shapes) { - clear(); - _subShapeEntries.swap(shapes); - // TODO: audit our new list of entries and delete any that have null pointers - computeBoundingRadius(); -} - -void ListShape::clear() { - // the ListShape owns its subShapes, so they need to be deleted - for (int i = 0; i < _subShapeEntries.size(); ++i) { - delete _subShapeEntries[i]._shape; - } - _subShapeEntries.clear(); - setBoundingRadius(0.0f); -} - -void ListShape::computeBoundingRadius() { - float maxRadius = 0.0f; - for (int i = 0; i < _subShapeEntries.size(); ++i) { - ListShapeEntry& entry = _subShapeEntries[i]; - float radius = glm::length(entry._localPosition) + entry._shape->getBoundingRadius(); - if (radius > maxRadius) { - maxRadius = radius; - } - } - setBoundingRadius(maxRadius); -} - diff --git a/libraries/shared/src/ListShape.h b/libraries/shared/src/ListShape.h deleted file mode 100644 index 6352ef3f07..0000000000 --- a/libraries/shared/src/ListShape.h +++ /dev/null @@ -1,72 +0,0 @@ -// -// ListShape.h -// libraries/shared/src -// -// Created by Andrew Meadows on 02/20/2014. -// Copyright 2014 High Fidelity, Inc. -// -// ListShape: A collection of shapes, each with a local transform. -// -// 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_ListShape_h -#define hifi_ListShape_h - -#include - -#include -#include -#include - -#include "Shape.h" - - -class ListShapeEntry { -public: - void updateTransform(const glm::vec3& position, const glm::quat& rotation); - - Shape* _shape; - glm::vec3 _localPosition; - glm::quat _localRotation; -}; - -class ListShape : public Shape { -public: - - ListShape() : Shape(LIST_SHAPE), _subShapeEntries(), _subShapeTransformsAreDirty(false) {} - - ListShape(const glm::vec3& position, const glm::quat& rotation) : - Shape(LIST_SHAPE, position, rotation), _subShapeEntries(), _subShapeTransformsAreDirty(false) {} - - ~ListShape(); - - void setTranslation(const glm::vec3& position); - void setRotation(const glm::quat& rotation); - - const Shape* getSubShape(int index) const; - - void updateSubTransforms(); - - int size() const { return _subShapeEntries.size(); } - - void addShape(Shape* shape, const glm::vec3& localPosition, const glm::quat& localRotation); - - void setShapes(QVector& shapes); - - // TODO: either implement this or remove ListShape altogether - bool findRayIntersection(RayIntersectionInfo& intersection) const { return false; } - -protected: - void clear(); - void computeBoundingRadius(); - - QVector _subShapeEntries; - bool _subShapeTransformsAreDirty; - -private: - ListShape(const ListShape& otherList); // don't implement this -}; - -#endif // hifi_ListShape_h diff --git a/libraries/shared/src/PlaneShape.cpp b/libraries/shared/src/PlaneShape.cpp deleted file mode 100644 index cad04afa42..0000000000 --- a/libraries/shared/src/PlaneShape.cpp +++ /dev/null @@ -1,78 +0,0 @@ -// -// PlaneShape.cpp -// libraries/shared/src -// -// Created by Andrzej Kapolka on 4/10/2014. -// 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 "GLMHelpers.h" -#include "NumericalConstants.h" -#include "PlaneShape.h" - -const glm::vec3 UNROTATED_NORMAL(0.0f, 1.0f, 0.0f); - -PlaneShape::PlaneShape(const glm::vec4& coefficients) : - Shape(PLANE_SHAPE) { - - glm::vec3 normal = glm::vec3(coefficients); - _translation = -normal * coefficients.w; - - float angle = acosf(glm::dot(normal, UNROTATED_NORMAL)); - if (angle > EPSILON) { - if (angle > PI - EPSILON) { - _rotation = glm::angleAxis(PI, glm::vec3(1.0f, 0.0f, 0.0f)); - } else { - _rotation = glm::angleAxis(angle, glm::normalize(glm::cross(UNROTATED_NORMAL, normal))); - } - } -} - -glm::vec3 PlaneShape::getNormal() const { - return _rotation * UNROTATED_NORMAL; -} - -void PlaneShape::setNormal(const glm::vec3& direction) { - glm::vec3 oldTranslation = _translation; - _rotation = rotationBetween(UNROTATED_NORMAL, direction); - glm::vec3 normal = getNormal(); - _translation = glm::dot(oldTranslation, normal) * normal; -} - -void PlaneShape::setPoint(const glm::vec3& point) { - glm::vec3 normal = getNormal(); - _translation = glm::dot(point, normal) * normal; -} - -glm::vec4 PlaneShape::getCoefficients() const { - glm::vec3 normal = _rotation * UNROTATED_NORMAL; - return glm::vec4(normal.x, normal.y, normal.z, -glm::dot(normal, _translation)); -} - -bool PlaneShape::findRayIntersection(RayIntersectionInfo& intersection) const { - glm::vec3 n = getNormal(); - float denominator = glm::dot(n, intersection._rayDirection); - if (fabsf(denominator) < EPSILON) { - // line is parallel to plane - if (glm::dot(_translation - intersection._rayStart, n) < EPSILON) { - // ray starts on the plane - intersection._hitDistance = 0.0f; - intersection._hitNormal = n; - intersection._hitShape = const_cast(this); - return true; - } - } else { - float d = glm::dot(_translation - intersection._rayStart, n) / denominator; - if (d > 0.0f && d < intersection._rayLength && d < intersection._hitDistance) { - // ray points toward plane - intersection._hitDistance = d; - intersection._hitNormal = n; - intersection._hitShape = const_cast(this); - return true; - } - } - return false; -} diff --git a/libraries/shared/src/PlaneShape.h b/libraries/shared/src/PlaneShape.h deleted file mode 100644 index 8d6de326af..0000000000 --- a/libraries/shared/src/PlaneShape.h +++ /dev/null @@ -1,30 +0,0 @@ -// -// PlaneShape.h -// libraries/shared/src -// -// Created by Andrzej Kapolka on 4/9/2014. -// 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_PlaneShape_h -#define hifi_PlaneShape_h - -#include "Shape.h" - -class PlaneShape : public Shape { -public: - PlaneShape(const glm::vec4& coefficients = glm::vec4(0.0f, 1.0f, 0.0f, 0.0f)); - - glm::vec3 getNormal() const; - glm::vec4 getCoefficients() const; - - void setNormal(const glm::vec3& normal); - void setPoint(const glm::vec3& point); - - bool findRayIntersection(RayIntersectionInfo& intersection) const; -}; - -#endif // hifi_PlaneShape_h diff --git a/libraries/shared/src/RayIntersectionInfo.h b/libraries/shared/src/RayIntersectionInfo.h deleted file mode 100644 index 06ec0aceaa..0000000000 --- a/libraries/shared/src/RayIntersectionInfo.h +++ /dev/null @@ -1,37 +0,0 @@ -// -// RayIntersectionInfo.h -// libraries/physcis/src -// -// Created by Andrew Meadows 2014.09.09 -// 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_RayIntersectionInfo_h -#define hifi_RayIntersectionInfo_h - -#include - -class Shape; - -class RayIntersectionInfo { -public: - RayIntersectionInfo() : _rayStart(0.0f), _rayDirection(1.0f, 0.0f, 0.0f), _rayLength(FLT_MAX), - _hitDistance(FLT_MAX), _hitNormal(1.0f, 0.0f, 0.0f), _hitShape(NULL) { } - - glm::vec3 getIntersectionPoint() const { return _rayStart + _hitDistance * _rayDirection; } - - // input - glm::vec3 _rayStart; - glm::vec3 _rayDirection; - float _rayLength; - - // output - float _hitDistance; - glm::vec3 _hitNormal; - Shape* _hitShape; -}; - -#endif // hifi_RayIntersectionInfo_h diff --git a/libraries/shared/src/Shape.h b/libraries/shared/src/Shape.h deleted file mode 100644 index 048b8d7257..0000000000 --- a/libraries/shared/src/Shape.h +++ /dev/null @@ -1,144 +0,0 @@ -// -// Shape.h -// libraries/shared/src -// -// Created by Andrew Meadows on 2014. -// 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_Shape_h -#define hifi_Shape_h - -#include -#include -#include -#include -#include - -#include "RayIntersectionInfo.h" - -class PhysicsEntity; - -const float MAX_SHAPE_MASS = 1.0e18f; // something less than sqrt(FLT_MAX) - -// DANGER: until ShapeCollider goes away the order of these values matter. Specifically, -// UNKNOWN_SHAPE must be equal to the number of shapes that ShapeCollider actually supports. -const quint8 SPHERE_SHAPE = 0; -const quint8 CAPSULE_SHAPE = 1; -const quint8 PLANE_SHAPE = 2; -const quint8 AACUBE_SHAPE = 3; -const quint8 LIST_SHAPE = 4; -const quint8 UNKNOWN_SHAPE = 5; -const quint8 INVALID_SHAPE = 5; - -// new shapes to be supported by Bullet -const quint8 BOX_SHAPE = 7; -const quint8 CYLINDER_SHAPE = 8; - -class Shape { -public: - - typedef quint8 Type; - - static quint32 getNextID() { static quint32 nextID = 0; return ++nextID; } - - Shape() : _type(UNKNOWN_SHAPE), _owningEntity(NULL), _boundingRadius(0.0f), - _translation(0.0f), _rotation(), _mass(MAX_SHAPE_MASS) { - _id = getNextID(); - } - virtual ~Shape() { } - - Type getType() const { return _type; } - quint32 getID() const { return _id; } - - void setEntity(PhysicsEntity* entity) { _owningEntity = entity; } - PhysicsEntity* getEntity() const { return _owningEntity; } - - float getBoundingRadius() const { return _boundingRadius; } - - virtual const glm::quat& getRotation() const { return _rotation; } - virtual void setRotation(const glm::quat& rotation) { _rotation = rotation; } - - virtual void setTranslation(const glm::vec3& translation) { _translation = translation; } - virtual const glm::vec3& getTranslation() const { return _translation; } - - virtual void setMass(float mass) { _mass = mass; } - virtual float getMass() const { return _mass; } - - virtual bool findRayIntersection(RayIntersectionInfo& intersection) const = 0; - - /// \param penetration of collision - /// \param contactPoint of collision - /// \return the effective mass for the collision - /// For most shapes has side effects: computes and caches the partial Lagrangian coefficients which will be - /// used in the next accumulateDelta() call. - virtual float computeEffectiveMass(const glm::vec3& penetration, const glm::vec3& contactPoint) { return _mass; } - - /// \param relativeMassFactor the final ingredient for partial Lagrangian coefficients from computeEffectiveMass() - /// \param penetration the delta movement - virtual void accumulateDelta(float relativeMassFactor, const glm::vec3& penetration) {} - - virtual void applyAccumulatedDelta() {} - - /// \return volume of shape in cubic meters - virtual float getVolume() const { return 1.0; } - - virtual QDebug& dumpToDebug(QDebug& debugConext) const; - -protected: - // these ctors are protected (used by derived classes only) - Shape(Type type) : _type(type), _owningEntity(NULL), - _boundingRadius(0.0f), _translation(0.0f), - _rotation(), _mass(MAX_SHAPE_MASS) { - _id = getNextID(); - } - - Shape(Type type, const glm::vec3& position) : - _type(type), _owningEntity(NULL), - _boundingRadius(0.0f), _translation(position), - _rotation(), _mass(MAX_SHAPE_MASS) { - _id = getNextID(); - } - - Shape(Type type, const glm::vec3& position, const glm::quat& rotation) : _type(type), _owningEntity(NULL), - _boundingRadius(0.0f), _translation(position), - _rotation(rotation), _mass(MAX_SHAPE_MASS) { - _id = getNextID(); - } - - void setBoundingRadius(float radius) { _boundingRadius = radius; } - - Type _type; - quint32 _id; - PhysicsEntity* _owningEntity; - float _boundingRadius; - glm::vec3 _translation; - glm::quat _rotation; - float _mass; -}; - -inline QDebug& Shape::dumpToDebug(QDebug& debugConext) const { - debugConext << "Shape[ (" - << "type: " << getType() - << "position: " - << getTranslation().x << ", " << getTranslation().y << ", " << getTranslation().z - << "radius: " - << getBoundingRadius() - << "]"; - - return debugConext; -} - -inline QDebug operator<<(QDebug debug, const Shape& shape) { - return shape.dumpToDebug(debug); -} - -inline QDebug operator<<(QDebug debug, const Shape* shape) { - return shape->dumpToDebug(debug); -} - - -#endif // hifi_Shape_h diff --git a/libraries/shared/src/ShapeCollider.cpp b/libraries/shared/src/ShapeCollider.cpp deleted file mode 100644 index fa86d8c0fb..0000000000 --- a/libraries/shared/src/ShapeCollider.cpp +++ /dev/null @@ -1,1211 +0,0 @@ -// -// ShapeCollider.cpp -// libraries/shared/src -// -// Created by Andrew Meadows on 02/20/2014. -// 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 - -#include "AACubeShape.h" -#include "CapsuleShape.h" -#include "GeometryUtil.h" -#include "ListShape.h" -#include "NumericalConstants.h" -#include "PlaneShape.h" -#include "ShapeCollider.h" -#include "SphereShape.h" -#include "StreamUtils.h" - - -// NOTE: -// -// * Large ListShape's are inefficient keep the lists short. -// * Collisions between lists of lists work in theory but are not recommended. - -const quint8 NUM_SHAPE_TYPES = UNKNOWN_SHAPE; -const quint8 NUM_DISPATCH_CELLS = NUM_SHAPE_TYPES * NUM_SHAPE_TYPES; - -Shape::Type getDispatchKey(Shape::Type typeA, Shape::Type typeB) { - return typeA + NUM_SHAPE_TYPES * typeB; -} - -// dummy dispatch for any non-implemented pairings -bool notImplemented(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions) { - return false; -} - -// NOTE: hardcode the number of dispatchTable entries (NUM_SHAPE_TYPES ^2) -bool (*dispatchTable[NUM_DISPATCH_CELLS])(const Shape*, const Shape*, CollisionList&); - -namespace ShapeCollider { - -// NOTE: the dispatch table must be initialized before the ShapeCollider is used. -void initDispatchTable() { - for (Shape::Type i = 0; i < NUM_DISPATCH_CELLS; ++i) { - dispatchTable[i] = ¬Implemented; - } - - dispatchTable[getDispatchKey(SPHERE_SHAPE, SPHERE_SHAPE)] = &sphereVsSphere; - dispatchTable[getDispatchKey(SPHERE_SHAPE, CAPSULE_SHAPE)] = &sphereVsCapsule; - dispatchTable[getDispatchKey(SPHERE_SHAPE, PLANE_SHAPE)] = &sphereVsPlane; - dispatchTable[getDispatchKey(SPHERE_SHAPE, AACUBE_SHAPE)] = &sphereVsAACube; - dispatchTable[getDispatchKey(SPHERE_SHAPE, LIST_SHAPE)] = &shapeVsList; - - dispatchTable[getDispatchKey(CAPSULE_SHAPE, SPHERE_SHAPE)] = &capsuleVsSphere; - dispatchTable[getDispatchKey(CAPSULE_SHAPE, CAPSULE_SHAPE)] = &capsuleVsCapsule; - dispatchTable[getDispatchKey(CAPSULE_SHAPE, PLANE_SHAPE)] = &capsuleVsPlane; - dispatchTable[getDispatchKey(CAPSULE_SHAPE, AACUBE_SHAPE)] = &capsuleVsAACube; - dispatchTable[getDispatchKey(CAPSULE_SHAPE, LIST_SHAPE)] = &shapeVsList; - - dispatchTable[getDispatchKey(PLANE_SHAPE, SPHERE_SHAPE)] = &planeVsSphere; - dispatchTable[getDispatchKey(PLANE_SHAPE, CAPSULE_SHAPE)] = &planeVsCapsule; - dispatchTable[getDispatchKey(PLANE_SHAPE, PLANE_SHAPE)] = &planeVsPlane; - dispatchTable[getDispatchKey(PLANE_SHAPE, AACUBE_SHAPE)] = ¬Implemented; - dispatchTable[getDispatchKey(PLANE_SHAPE, LIST_SHAPE)] = &shapeVsList; - - dispatchTable[getDispatchKey(AACUBE_SHAPE, SPHERE_SHAPE)] = &aaCubeVsSphere; - dispatchTable[getDispatchKey(AACUBE_SHAPE, CAPSULE_SHAPE)] = &aaCubeVsCapsule; - dispatchTable[getDispatchKey(AACUBE_SHAPE, PLANE_SHAPE)] = ¬Implemented; - dispatchTable[getDispatchKey(AACUBE_SHAPE, AACUBE_SHAPE)] = &aaCubeVsAACube; - dispatchTable[getDispatchKey(AACUBE_SHAPE, LIST_SHAPE)] = &shapeVsList; - - dispatchTable[getDispatchKey(LIST_SHAPE, SPHERE_SHAPE)] = &listVsShape; - dispatchTable[getDispatchKey(LIST_SHAPE, CAPSULE_SHAPE)] = &listVsShape; - dispatchTable[getDispatchKey(LIST_SHAPE, PLANE_SHAPE)] = &listVsShape; - dispatchTable[getDispatchKey(LIST_SHAPE, AACUBE_SHAPE)] = &listVsShape; - dispatchTable[getDispatchKey(LIST_SHAPE, LIST_SHAPE)] = &listVsList; - - // all of the UNKNOWN_SHAPE pairings are notImplemented -} - -bool collideShapes(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions) { - return (*dispatchTable[shapeA->getType() + NUM_SHAPE_TYPES * shapeB->getType()])(shapeA, shapeB, collisions); -} - -static CollisionList tempCollisions(32); - -bool collideShapeWithShapes(const Shape* shapeA, const QVector& shapes, int startIndex, CollisionList& collisions) { - bool collided = false; - if (shapeA) { - int numShapes = shapes.size(); - for (int i = startIndex; i < numShapes; ++i) { - const Shape* shapeB = shapes.at(i); - if (!shapeB) { - continue; - } - if (collideShapes(shapeA, shapeB, collisions)) { - collided = true; - if (collisions.isFull()) { - break; - } - } - } - } - return collided; -} - -bool collideShapesWithShapes(const QVector& shapesA, const QVector& shapesB, CollisionList& collisions) { - bool collided = false; - int numShapesA = shapesA.size(); - for (int i = 0; i < numShapesA; ++i) { - Shape* shapeA = shapesA.at(i); - if (!shapeA) { - continue; - } - if (collideShapeWithShapes(shapeA, shapesB, 0, collisions)) { - collided = true; - if (collisions.isFull()) { - break; - } - } - } - return collided; -} - -bool collideShapeWithAACubeLegacy(const Shape* shapeA, const glm::vec3& cubeCenter, float cubeSide, CollisionList& collisions) { - Shape::Type typeA = shapeA->getType(); - if (typeA == SPHERE_SHAPE) { - return sphereVsAACubeLegacy(static_cast(shapeA), cubeCenter, cubeSide, collisions); - } else if (typeA == CAPSULE_SHAPE) { - return capsuleVsAACubeLegacy(static_cast(shapeA), cubeCenter, cubeSide, collisions); - } else if (typeA == LIST_SHAPE) { - const ListShape* listA = static_cast(shapeA); - bool touching = false; - for (int i = 0; i < listA->size() && !collisions.isFull(); ++i) { - const Shape* subShape = listA->getSubShape(i); - int subType = subShape->getType(); - if (subType == SPHERE_SHAPE) { - touching = sphereVsAACubeLegacy(static_cast(subShape), cubeCenter, cubeSide, collisions) || touching; - } else if (subType == CAPSULE_SHAPE) { - touching = capsuleVsAACubeLegacy(static_cast(subShape), cubeCenter, cubeSide, collisions) || touching; - } - } - return touching; - } - return false; -} - -bool sphereVsSphere(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions) { - const SphereShape* sphereA = static_cast(shapeA); - const SphereShape* sphereB = static_cast(shapeB); - glm::vec3 BA = sphereB->getTranslation() - sphereA->getTranslation(); - float distanceSquared = glm::dot(BA, BA); - float totalRadius = sphereA->getRadius() + sphereB->getRadius(); - if (distanceSquared < totalRadius * totalRadius) { - // normalize BA - float distance = sqrtf(distanceSquared); - if (distance < EPSILON) { - // the spheres are on top of each other, so we pick an arbitrary penetration direction - BA = glm::vec3(0.0f, 1.0f, 0.0f); - distance = totalRadius; - } else { - BA /= distance; - } - // penetration points from A into B - CollisionInfo* collision = collisions.getNewCollision(); - if (collision) { - collision->_penetration = BA * (totalRadius - distance); - // contactPoint is on surface of A - collision->_contactPoint = sphereA->getTranslation() + sphereA->getRadius() * BA; - collision->_shapeA = shapeA; - collision->_shapeB = shapeB; - return true; - } - } - return false; -} - -bool sphereVsCapsule(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions) { - const SphereShape* sphereA = static_cast(shapeA); - const CapsuleShape* capsuleB = static_cast(shapeB); - // find sphereA's closest approach to axis of capsuleB - glm::vec3 BA = capsuleB->getTranslation() - sphereA->getTranslation(); - glm::vec3 capsuleAxis; - capsuleB->computeNormalizedAxis(capsuleAxis); - float axialDistance = - glm::dot(BA, capsuleAxis); - float absAxialDistance = fabsf(axialDistance); - float totalRadius = sphereA->getRadius() + capsuleB->getRadius(); - if (absAxialDistance < totalRadius + capsuleB->getHalfHeight()) { - glm::vec3 radialAxis = BA + axialDistance * capsuleAxis; // points from A to axis of B - float radialDistance2 = glm::length2(radialAxis); - float totalRadius2 = totalRadius * totalRadius; - if (radialDistance2 > totalRadius2) { - // sphere is too far from capsule axis - return false; - } - if (absAxialDistance > capsuleB->getHalfHeight()) { - // sphere hits capsule on a cap --> recompute radialAxis to point from spherA to cap center - float sign = (axialDistance > 0.0f) ? 1.0f : -1.0f; - radialAxis = BA + (sign * capsuleB->getHalfHeight()) * capsuleAxis; - radialDistance2 = glm::length2(radialAxis); - if (radialDistance2 > totalRadius2) { - return false; - } - } - if (radialDistance2 > EPSILON * EPSILON) { - CollisionInfo* collision = collisions.getNewCollision(); - if (!collision) { - // collisions list is full - return false; - } - // normalize the radialAxis - float radialDistance = sqrtf(radialDistance2); - radialAxis /= radialDistance; - // penetration points from A into B - collision->_penetration = (totalRadius - radialDistance) * radialAxis; // points from A into B - // contactPoint is on surface of sphereA - collision->_contactPoint = sphereA->getTranslation() + sphereA->getRadius() * radialAxis; - collision->_shapeA = shapeA; - collision->_shapeB = shapeB; - } else { - // A is on B's axis, so the penetration is undefined... - if (absAxialDistance > capsuleB->getHalfHeight()) { - // ...for the cylinder case (for now we pretend the collision doesn't exist) - return false; - } - CollisionInfo* collision = collisions.getNewCollision(); - if (!collision) { - // collisions list is full - return false; - } - // ... but still defined for the cap case - if (axialDistance < 0.0f) { - // we're hitting the start cap, so we negate the capsuleAxis - capsuleAxis *= -1; - } - // penetration points from A into B - float sign = (axialDistance > 0.0f) ? -1.0f : 1.0f; - collision->_penetration = (sign * (totalRadius + capsuleB->getHalfHeight() - absAxialDistance)) * capsuleAxis; - // contactPoint is on surface of sphereA - collision->_contactPoint = sphereA->getTranslation() + (sign * sphereA->getRadius()) * capsuleAxis; - collision->_shapeA = shapeA; - collision->_shapeB = shapeB; - } - return true; - } - return false; -} - -bool sphereVsPlane(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions) { - const SphereShape* sphereA = static_cast(shapeA); - const PlaneShape* planeB = static_cast(shapeB); - glm::vec3 penetration; - if (findSpherePlanePenetration(sphereA->getTranslation(), sphereA->getRadius(), planeB->getCoefficients(), penetration)) { - CollisionInfo* collision = collisions.getNewCollision(); - if (!collision) { - return false; // collision list is full - } - collision->_penetration = penetration; - collision->_contactPoint = sphereA->getTranslation() + sphereA->getRadius() * glm::normalize(penetration); - collision->_shapeA = shapeA; - collision->_shapeB = shapeB; - return true; - } - return false; -} - -bool capsuleVsSphere(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions) { - return sphereVsCapsule(shapeB, shapeA, collisions); -} - -/// \param lineP point on line -/// \param lineDir normalized direction of line -/// \param cylinderP point on cylinder axis -/// \param cylinderDir normalized direction of cylinder axis -/// \param cylinderRadius radius of cylinder -/// \param hitLow[out] distance from point on line to first intersection with cylinder -/// \param hitHigh[out] distance from point on line to second intersection with cylinder -/// \return true if line hits cylinder -bool lineCylinder(const glm::vec3& lineP, const glm::vec3& lineDir, - const glm::vec3& cylinderP, const glm::vec3& cylinderDir, float cylinderRadius, - float& hitLow, float& hitHigh) { - - // first handle parallel case - float uDotV = glm::dot(lineDir, cylinderDir); - if (fabsf(1.0f - fabsf(uDotV)) < EPSILON) { - // line and cylinder are parallel - if (glm::distance2(lineP, cylinderP) <= cylinderRadius * cylinderRadius) { - // line is inside cylinder, which we consider a hit - hitLow = 0.0f; - hitHigh = 0.0f; - return true; - } - return false; - } - - // Given a line with point 'p' and normalized direction 'u' and - // a cylinder with axial point 's', radius 'r', and normalized direction 'v' - // the intersection of the two is on the line at distance 't' from 'p'. - // - // Determining the values of t reduces to solving a quadratic equation: At^2 + Bt + C = 0 - // - // where: - // - // P = p-s - // w = u-(u.v)v - // Q = P-(P.v)v - // - // A = w^2 - // B = 2(w.Q) - // C = Q^2 - r^2 - - glm::vec3 P = lineP - cylinderP; - glm::vec3 w = lineDir - uDotV * cylinderDir; - glm::vec3 Q = P - glm::dot(P, cylinderDir) * cylinderDir; - - // we save a few multiplies by storing 2*A rather than just A - float A2 = 2.0f * glm::dot(w, w); - float B = 2.0f * glm::dot(w, Q); - - // since C is only ever used once (in the determinant) we compute it inline - float determinant = B * B - 2.0f * A2 * (glm::dot(Q, Q) - cylinderRadius * cylinderRadius); - if (determinant < 0.0f) { - return false; - } - hitLow = (-B - sqrtf(determinant)) / A2; - hitHigh = -(hitLow + 2.0f * B / A2); - - if (hitLow > hitHigh) { - // re-arrange so hitLow is always the smaller value - float temp = hitHigh; - hitHigh = hitLow; - hitLow = temp; - } - return true; -} - -bool capsuleVsCapsule(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions) { - const CapsuleShape* capsuleA = static_cast(shapeA); - const CapsuleShape* capsuleB = static_cast(shapeB); - glm::vec3 axisA; - capsuleA->computeNormalizedAxis(axisA); - glm::vec3 axisB; - capsuleB->computeNormalizedAxis(axisB); - glm::vec3 centerA = capsuleA->getTranslation(); - glm::vec3 centerB = capsuleB->getTranslation(); - - // NOTE: The formula for closest approach between two lines is: - // d = [(B - A) . (a - (a.b)b)] / (1 - (a.b)^2) - - float aDotB = glm::dot(axisA, axisB); - float denominator = 1.0f - aDotB * aDotB; - float totalRadius = capsuleA->getRadius() + capsuleB->getRadius(); - if (denominator > EPSILON) { - // perform line-cylinder intesection test between axis of cylinderA and cylinderB with exanded radius - float hitLow = 0.0f; - float hitHigh = 0.0f; - if (!lineCylinder(centerA, axisA, centerB, axisB, totalRadius, hitLow, hitHigh)) { - return false; - } - - float halfHeightA = capsuleA->getHalfHeight(); - if (hitLow > halfHeightA || hitHigh < -halfHeightA) { - // the intersections are off the ends of capsuleA - return false; - } - - // compute nearest approach on axisA of axisB - float distanceA = glm::dot((centerB - centerA), (axisA - (aDotB) * axisB)) / denominator; - // clamp to intersection zone - if (distanceA > hitLow) { - if (distanceA > hitHigh) { - distanceA = hitHigh; - } - } else { - distanceA = hitLow; - } - // clamp to capsule segment - distanceA = glm::clamp(distanceA, -halfHeightA, halfHeightA); - - // find the closest point on capsuleB to sphere on capsuleA - float distanceB = glm::dot(centerA + distanceA * axisA - centerB, axisB); - float halfHeightB = capsuleB->getHalfHeight(); - if (fabsf(distanceB) > halfHeightB) { - // we must clamp distanceB... - distanceB = glm::clamp(distanceB, -halfHeightB, halfHeightB); - // ...and therefore must recompute distanceA - distanceA = glm::clamp(glm::dot(centerB + distanceB * axisB - centerA, axisA), -halfHeightA, halfHeightA); - } - - // collide like two spheres (do most of the math relative to B) - glm::vec3 BA = (centerB + distanceB * axisB) - (centerA + distanceA * axisA); - float distanceSquared = glm::dot(BA, BA); - if (distanceSquared < totalRadius * totalRadius) { - CollisionInfo* collision = collisions.getNewCollision(); - if (!collision) { - // collisions list is full - return false; - } - // normalize BA - float distance = sqrtf(distanceSquared); - if (distance < EPSILON) { - // the contact spheres are on top of each other, so we need to pick a penetration direction... - // try vector between the capsule centers... - BA = centerB - centerA; - distanceSquared = glm::length2(BA); - if (distanceSquared > EPSILON * EPSILON) { - distance = sqrtf(distanceSquared); - BA /= distance; - } else - { - // the capsule centers are on top of each other! - // give up on a valid penetration direction and just use the yAxis - BA = glm::vec3(0.0f, 1.0f, 0.0f); - distance = glm::max(capsuleB->getRadius(), capsuleA->getRadius()); - } - } else { - BA /= distance; - } - // penetration points from A into B - collision->_penetration = BA * (totalRadius - distance); - // contactPoint is on surface of A - collision->_contactPoint = centerA + distanceA * axisA + capsuleA->getRadius() * BA; - collision->_shapeA = shapeA; - collision->_shapeB = shapeB; - return true; - } - } else { - // capsules are approximiately parallel but might still collide - glm::vec3 BA = centerB - centerA; - float axialDistance = glm::dot(BA, axisB); - if (fabsf(axialDistance) > totalRadius + capsuleA->getHalfHeight() + capsuleB->getHalfHeight()) { - return false; - } - BA = BA - axialDistance * axisB; // BA now points from centerA to axisB (perp to axis) - float distanceSquared = glm::length2(BA); - if (distanceSquared < totalRadius * totalRadius) { - CollisionInfo* collision = collisions.getNewCollision(); - if (!collision) { - // collisions list is full - return false; - } - // We have all the info we need to compute the penetration vector... - // normalize BA - float distance = sqrtf(distanceSquared); - if (distance < EPSILON) { - // the spheres are on top of each other, so we pick an arbitrary penetration direction - BA = glm::vec3(0.0f, 1.0f, 0.0f); - } else { - BA /= distance; - } - // penetration points from A into B - collision->_penetration = BA * (totalRadius - distance); - - // However we need some more world-frame info to compute the contactPoint, - // which is on the surface of capsuleA... - // - // Find the overlapping secion of the capsules --> they collide as if there were - // two spheres at the midpoint of this overlapping section. - // So we project all endpoints to axisB, find the interior pair, - // and put A's proxy sphere on axisA at the midpoint of this section. - - // sort the projections as much as possible during calculation - float points[5]; - points[0] = -capsuleB->getHalfHeight(); - points[1] = axialDistance - capsuleA->getHalfHeight(); - points[2] = axialDistance + capsuleA->getHalfHeight(); - points[3] = capsuleB->getHalfHeight(); - - // Since there are only three comparisons to do we unroll the sort algorithm... - // and use a fifth slot as temp during swap. - if (points[1] > points[2]) { - points[4] = points[1]; - points[1] = points[2]; - points[2] = points[4]; - } - if (points[2] > points[3]) { - points[4] = points[2]; - points[2] = points[3]; - points[3] = points[4]; - } - if (points[0] > points[1]) { - points[4] = points[0]; - points[0] = points[1]; - points[1] = points[4]; - } - - // average the internal pair, and then do the math from centerB - collision->_contactPoint = centerB + (0.5f * (points[1] + points[2])) * axisB - + (capsuleA->getRadius() - distance) * BA; - collision->_shapeA = shapeA; - collision->_shapeB = shapeB; - return true; - } - } - return false; -} - -bool capsuleVsPlane(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions) { - const CapsuleShape* capsuleA = static_cast(shapeA); - const PlaneShape* planeB = static_cast(shapeB); - glm::vec3 start, end, penetration; - capsuleA->getStartPoint(start); - capsuleA->getEndPoint(end); - glm::vec4 plane = planeB->getCoefficients(); - if (findCapsulePlanePenetration(start, end, capsuleA->getRadius(), plane, penetration)) { - CollisionInfo* collision = collisions.getNewCollision(); - if (!collision) { - return false; // collision list is full - } - collision->_penetration = penetration; - glm::vec3 deepestEnd = (glm::dot(start, glm::vec3(plane)) < glm::dot(end, glm::vec3(plane))) ? start : end; - collision->_contactPoint = deepestEnd + capsuleA->getRadius() * glm::normalize(penetration); - collision->_shapeA = shapeA; - collision->_shapeB = shapeB; - return true; - } - return false; -} - -bool planeVsSphere(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions) { - return sphereVsPlane(shapeB, shapeA, collisions); -} - -bool planeVsCapsule(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions) { - return capsuleVsPlane(shapeB, shapeA, collisions); -} - -bool planeVsPlane(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions) { - // technically, planes always collide unless they're parallel and not coincident; however, that's - // not going to give us any useful information - return false; -} - -// helper function -bool sphereVsAACubeLegacy(const glm::vec3& sphereCenter, float sphereRadius, const glm::vec3& cubeCenter, - float cubeSide, CollisionList& collisions) { - // sphere is A - // cube is B - // BA = B - A = from center of A to center of B - float halfCubeSide = 0.5f * cubeSide; - glm::vec3 BA = cubeCenter - sphereCenter; - float distance = glm::length(BA); - if (distance > EPSILON) { - float maxBA = glm::max(glm::max(glm::abs(BA.x), glm::abs(BA.y)), glm::abs(BA.z)); - if (maxBA > halfCubeSide + sphereRadius) { - // sphere misses cube entirely - return false; - } - CollisionInfo* collision = collisions.getNewCollision(); - if (!collision) { - return false; - } - if (maxBA > halfCubeSide) { - // sphere hits cube but its center is outside cube - - // compute contact anti-pole on cube (in cube frame) - glm::vec3 cubeContact = glm::abs(BA); - if (cubeContact.x > halfCubeSide) { - cubeContact.x = halfCubeSide; - } - if (cubeContact.y > halfCubeSide) { - cubeContact.y = halfCubeSide; - } - if (cubeContact.z > halfCubeSide) { - cubeContact.z = halfCubeSide; - } - glm::vec3 signs = glm::sign(BA); - cubeContact.x *= signs.x; - cubeContact.y *= signs.y; - cubeContact.z *= signs.z; - - // compute penetration direction - glm::vec3 direction = BA - cubeContact; - float lengthDirection = glm::length(direction); - if (lengthDirection < EPSILON) { - // sphereCenter is touching cube surface, so we can't use the difference between those two - // points to compute the penetration direction. Instead we use the unitary components of - // cubeContact. - direction = cubeContact / halfCubeSide; - glm::modf(BA, direction); - lengthDirection = glm::length(direction); - } else if (lengthDirection > sphereRadius) { - collisions.deleteLastCollision(); - return false; - } - direction /= lengthDirection; - - // compute collision details - collision->_contactPoint = sphereCenter + sphereRadius * direction; - collision->_penetration = sphereRadius * direction - (BA - cubeContact); - } else { - // sphere center is inside cube - // --> push out nearest face - glm::vec3 direction; - BA /= maxBA; - glm::modf(BA, direction); - float lengthDirection = glm::length(direction); - direction /= lengthDirection; - - // compute collision details - collision->_floatData = cubeSide; - collision->_vecData = cubeCenter; - collision->_penetration = (halfCubeSide * lengthDirection + sphereRadius - maxBA * glm::dot(BA, direction)) * direction; - collision->_contactPoint = sphereCenter + sphereRadius * direction; - } - collision->_floatData = cubeSide; - collision->_vecData = cubeCenter; - collision->_shapeA = NULL; - collision->_shapeB = NULL; - return true; - } else if (sphereRadius + halfCubeSide > distance) { - // NOTE: for cocentric approximation we collide sphere and cube as two spheres which means - // this algorithm will probably be wrong when both sphere and cube are very small (both ~EPSILON) - CollisionInfo* collision = collisions.getNewCollision(); - if (collision) { - // the penetration and contactPoint are undefined, so we pick a penetration direction (-yAxis) - collision->_penetration = (sphereRadius + halfCubeSide) * glm::vec3(0.0f, -1.0f, 0.0f); - // contactPoint is on surface of A - collision->_contactPoint = sphereCenter + collision->_penetration; - - collision->_floatData = cubeSide; - collision->_vecData = cubeCenter; - collision->_shapeA = NULL; - collision->_shapeB = NULL; - return true; - } - } - return false; -} - -// helper function -CollisionInfo* sphereVsAACubeHelper(const glm::vec3& sphereCenter, float sphereRadius, const glm::vec3& cubeCenter, - float cubeSide, CollisionList& collisions) { - // sphere is A - // cube is B - // BA = B - A = from center of A to center of B - float halfCubeSide = 0.5f * cubeSide; - glm::vec3 BA = cubeCenter - sphereCenter; - float distance = glm::length(BA); - if (distance > EPSILON) { - float maxBA = glm::max(glm::max(glm::abs(BA.x), glm::abs(BA.y)), glm::abs(BA.z)); - if (maxBA > halfCubeSide + sphereRadius) { - // sphere misses cube entirely - return NULL; - } - CollisionInfo* collision = collisions.getNewCollision(); - if (!collision) { - return NULL; - } - if (maxBA > halfCubeSide) { - // sphere hits cube but its center is outside cube - - // compute contact anti-pole on cube (in cube frame) - glm::vec3 cubeContact = glm::abs(BA); - if (cubeContact.x > halfCubeSide) { - cubeContact.x = halfCubeSide; - } - if (cubeContact.y > halfCubeSide) { - cubeContact.y = halfCubeSide; - } - if (cubeContact.z > halfCubeSide) { - cubeContact.z = halfCubeSide; - } - glm::vec3 signs = glm::sign(BA); - cubeContact.x *= signs.x; - cubeContact.y *= signs.y; - cubeContact.z *= signs.z; - - // compute penetration direction - glm::vec3 direction = BA - cubeContact; - float lengthDirection = glm::length(direction); - if (lengthDirection < EPSILON) { - // sphereCenter is touching cube surface, so we can't use the difference between those two - // points to compute the penetration direction. Instead we use the unitary components of - // cubeContact. - glm::modf(cubeContact / halfCubeSide, direction); - lengthDirection = glm::length(direction); - } else if (lengthDirection > sphereRadius) { - collisions.deleteLastCollision(); - return NULL; - } - direction /= lengthDirection; - - // compute collision details - collision->_contactPoint = sphereCenter + sphereRadius * direction; - collision->_penetration = sphereRadius * direction - (BA - cubeContact); - } else { - // sphere center is inside cube - // --> push out nearest face - glm::vec3 direction; - BA /= maxBA; - glm::modf(BA, direction); - float lengthDirection = glm::length(direction); - direction /= lengthDirection; - - // compute collision details - collision->_floatData = cubeSide; - collision->_vecData = cubeCenter; - collision->_penetration = (halfCubeSide * lengthDirection + sphereRadius - maxBA * glm::dot(BA, direction)) * direction; - collision->_contactPoint = sphereCenter + sphereRadius * direction; - } - collision->_shapeA = NULL; - collision->_shapeB = NULL; - return collision; - } else if (sphereRadius + halfCubeSide > distance) { - // NOTE: for cocentric approximation we collide sphere and cube as two spheres which means - // this algorithm will probably be wrong when both sphere and cube are very small (both ~EPSILON) - CollisionInfo* collision = collisions.getNewCollision(); - if (collision) { - // the penetration and contactPoint are undefined, so we pick a penetration direction (-yAxis) - collision->_penetration = (sphereRadius + halfCubeSide) * glm::vec3(0.0f, -1.0f, 0.0f); - // contactPoint is on surface of A - collision->_contactPoint = sphereCenter + collision->_penetration; - collision->_shapeA = NULL; - collision->_shapeB = NULL; - return collision; - } - } - return NULL; -} - -bool sphereVsAACube(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions) { - // BA = B - A = from center of A to center of B - const SphereShape* sphereA = static_cast(shapeA); - const AACubeShape* cubeB = static_cast(shapeB); - - CollisionInfo* collision = sphereVsAACubeHelper( sphereA->getTranslation(), sphereA->getRadius(), - cubeB->getTranslation(), cubeB->getScale(), collisions); - if (collision) { - collision->_shapeA = shapeA; - collision->_shapeB = shapeB; - return true; - } - return false; -} - -glm::vec3 cubeNormals[3] = { glm::vec3(1.0f, 0.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f), glm::vec3(0.0f, 0.0f, 1.0f) }; - -// the wallIndices is a sequence of pairs that represent the OTHER directions for each cube face, -// hence the first pair is (1, 2) because the OTHER faces for xFace are (yFace, zFace) = (1, 2) -int wallIndices[] = { 1, 2, 0, 2, 0, 1 }; - -bool capsuleVsAACubeFace(const CapsuleShape* capsuleA, const AACubeShape* cubeB, int faceIndex, const glm::vec3& faceNormal, CollisionList& collisions) { - // we only fall in here when the capsuleAxis is nearly parallel to the face of a cube - glm::vec3 capsuleAxis; - capsuleA->computeNormalizedAxis(capsuleAxis); - glm::vec3 cubeCenter = cubeB->getTranslation(); - - // Revisualize the capsule as a line segment between two points. We'd like to collide the - // capsule as two spheres located at the endpoints or where the line segment hits the boundary - // of the face. - - // We raytrace forward into the four planes that neigbhor the face to find the boundary - // points of the line segment. - glm::vec3 capsuleStart; - capsuleA->getStartPoint(capsuleStart); - - // translate into cube-relative frame - capsuleStart -= cubeCenter; - float capsuleLength = 2.0f * capsuleA->getHalfHeight(); - float halfCubeSide = 0.5f * cubeB->getScale(); - float capsuleRadius = capsuleA->getRadius(); - - // preload distances with values that work for when the capsuleAxis runs parallel to neighbor face - float distances[] = {FLT_MAX, -FLT_MAX, FLT_MAX, -FLT_MAX, 0.0f}; - - // Loop over the directions that are NOT parallel to face (there are two of them). - // For each direction we'll raytrace against the positive and negative planes to find where - // the axis passes through. - for (int i = 0; i < 2; ++i) { - int wallIndex = wallIndices[2 * faceIndex + i]; - glm::vec3 wallNormal = cubeNormals[wallIndex]; - // each direction has two walls: positive and negative - float axisDotWall = glm::dot(capsuleAxis, wallNormal); - if (fabsf(axisDotWall) > EPSILON) { - // formula for distance to intersection between line (P,p) and plane (V,n) is: (V-P)*n / p*n - distances[2 * i] = (halfCubeSide - glm::dot(capsuleStart, wallNormal)) / axisDotWall; - distances[2 * i + 1] = -(halfCubeSide + glm::dot(capsuleStart, wallNormal)) / axisDotWall; - } - } - - // sort the distances from large to small - int j = 3; - while (j > 0) { - for (int i = 0; i <= j; ++i) { - if (distances[i] < distances[i+1]) { - // swap (using distances[4] as temp space) - distances[4] = distances[i]; - distances[i] = distances[i+1]; - distances[i+1] = distances[4]; - } - } - --j; - } - - // the capsule overlaps when the max of the mins is less than the min of the maxes - distances[0] = glm::min(capsuleLength, distances[1]); // maxDistance - distances[1] = glm::max(0.0f, distances[2]); // minDistance - bool hit = false; - if (distances[1] < distances[0]) { - // if we collide at all it will be at two points - for (int i = 0; i < 2; ++i) { - glm::vec3 sphereCenter = cubeCenter + capsuleStart + distances[i] * capsuleAxis; - // collide like a sphere at point0 with capsuleRadius - CollisionInfo* collision = sphereVsAACubeHelper(sphereCenter, capsuleRadius, - cubeCenter, 2.0f * halfCubeSide, collisions); - if (collision) { - // we hit! so store back pointers to the shapes - collision->_shapeA = capsuleA; - collision->_shapeB = cubeB; - hit = true; - } - } - return hit; - } else if (distances[1] < capsuleLength + capsuleRadius ) { - // we might collide at the end cap - glm::vec3 sphereCenter = cubeCenter + capsuleStart + capsuleLength * capsuleAxis; - // collide like a sphere at point0 with capsuleRadius - CollisionInfo* collision = sphereVsAACubeHelper(sphereCenter, capsuleRadius, - cubeCenter, 2.0f * halfCubeSide, collisions); - if (collision) { - // we hit! so store back pointers to the shapes - collision->_shapeA = capsuleA; - collision->_shapeB = cubeB; - hit = true; - } - } else if (distances[0] > -capsuleLength) { - // we might collide at the start cap - glm::vec3 sphereCenter = cubeCenter + capsuleStart; - // collide like a sphere at point0 with capsuleRadius - CollisionInfo* collision = sphereVsAACubeHelper(sphereCenter, capsuleRadius, - cubeCenter, 2.0f * halfCubeSide, collisions); - if (collision) { - // we hit! so store back pointers to the shapes - collision->_shapeA = capsuleA; - collision->_shapeB = cubeB; - hit = true; - } - } - return hit; -} - -bool capsuleVsAACube(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions) { - const CapsuleShape* capsuleA = static_cast(shapeA); - const AACubeShape* cubeB = static_cast(shapeB); - - // find nearest approach of capsule's line segment to cube's center - glm::vec3 capsuleAxis; - capsuleA->computeNormalizedAxis(capsuleAxis); - float halfHeight = capsuleA->getHalfHeight(); - glm::vec3 cubeCenter = cubeB->getTranslation(); - glm::vec3 capsuleCenter = capsuleA->getTranslation(); - glm::vec3 BA = cubeCenter - capsuleCenter; - float axialOffset = glm::dot(capsuleAxis, BA); - if (fabsf(axialOffset) > halfHeight) { - axialOffset = (axialOffset < 0.0f) ? -halfHeight : halfHeight; - } - glm::vec3 nearestApproach = capsuleCenter + axialOffset * capsuleAxis; - - // transform nearestApproach into cube-relative frame - nearestApproach -= cubeCenter; - - // determine the face of nearest approach - glm::vec3 signs = glm::sign(nearestApproach); - int faceIndex = 0; - glm::vec3 faceNormal(signs.x, 0.0f, 0.0f); - float maxApproach = fabsf(nearestApproach.x); - if (maxApproach < fabsf(nearestApproach.y)) { - maxApproach = fabsf(nearestApproach.y); - faceIndex = 1; - faceNormal = glm::vec3(0.0f, signs.y, 0.0f); - } - if (maxApproach < fabsf(nearestApproach.z)) { - maxApproach = fabsf(nearestApproach.z); - faceIndex = 2; - faceNormal = glm::vec3(0.0f, 0.0f, signs.z); - } - - if (fabsf(glm::dot(faceNormal, capsuleAxis)) < EPSILON) { - if (glm::dot(nearestApproach, faceNormal) > cubeB->getScale() + capsuleA->getRadius()) { - return false; - } - // we expect this case to be rare but complicated enough that we split it out - // into its own helper function - return capsuleVsAACubeFace(capsuleA, cubeB, faceIndex, faceNormal, collisions); - } - - // Revisualize the capsule as a startPoint and an axis that points toward the cube face. - // We raytrace forward into the four planes that neigbhor the face to find the furthest - // point along the capsule's axis that might hit face. - glm::vec3 capsuleStart; - if (glm::dot(capsuleAxis, faceNormal) < 0.0f) { - capsuleA->getStartPoint(capsuleStart); - } else { - // NOTE: we want dot(capsuleAxis, faceNormal) to be negative which simplifies some - // logic below, so we pretend the end is the start thereby reversing its axis. - capsuleA->getEndPoint(capsuleStart); - capsuleAxis *= -1.0f; - } - // translate into cube-relative frame - capsuleStart -= cubeCenter; - float capsuleLength = 2.0f * halfHeight; - float halfCubeSide = 0.5f * cubeB->getScale(); - float capsuleRadius = capsuleA->getRadius(); - - // Loop over the directions that are NOT parallel to face (there are two of them). - // For each direction we'll raytrace along capsuleAxis to find where the axis passes - // through the furthest face and then we'll clamp to remain on the capsule's line segment - float shortestDistance = capsuleLength; - - for (int i = 0; i < 2; ++i) { - int wallIndex = wallIndices[2 * faceIndex + i]; - // each direction has two walls: positive and negative - for (float wallSign = -1.0f; wallSign < 2.0f; wallSign += 2.0f) { - glm::vec3 wallNormal = wallSign * cubeNormals[wallIndex]; - float axisDotWall = glm::dot(capsuleAxis, wallNormal); - if (axisDotWall > EPSILON) { - // formula for distance to intersection between line (P,p) and plane (V,n) is: (V-P)*n / p*n - float newDistance = (halfCubeSide - glm::dot(capsuleStart, wallNormal)) / axisDotWall; - if (newDistance < 0.0f) { - // The wall is behind the capsule, but there is still a possibility that it collides - // against the edge so we recast against the diagonal plane beteween the two faces. - // NOTE: it is impossible for the startPoint to be in front of the diagonal plane, - // therefore we know we'll get a valid distance. - glm::vec3 thirdNormal = glm::cross(faceNormal, wallNormal); - glm::vec3 diagonalNormal = glm::normalize(glm::cross(glm::cross(capsuleAxis, thirdNormal), thirdNormal)); - newDistance = glm::dot(halfCubeSide * (faceNormal + wallNormal) - capsuleStart, diagonalNormal) / - glm::dot(capsuleAxis, diagonalNormal); - } else if (newDistance < capsuleLength) { - // The wall truncates the capsule axis, but we must check the case where the capsule - // collides with an edge/corner rather than the face. The good news is that this gives us - // an opportunity to check for an early exit case. - float heightOfImpact = glm::dot(capsuleStart + newDistance * capsuleAxis, faceNormal); - if (heightOfImpact > halfCubeSide + SQUARE_ROOT_OF_2 * capsuleRadius) { - // it is impossible for the capsule to hit the face - return false; - } else { - // recast against the diagonal plane between the two faces - glm::vec3 thirdNormal = glm::cross(faceNormal, wallNormal); - glm::vec3 diagonalNormal = glm::normalize(glm::cross(glm::cross(capsuleAxis, thirdNormal), thirdNormal)); - newDistance = glm::dot(halfCubeSide * (faceNormal + wallNormal) - capsuleStart, diagonalNormal) / - glm::dot(capsuleAxis, diagonalNormal); - } - } - if (newDistance < shortestDistance) { - shortestDistance = newDistance; - } - // there can only be one hit per direction - break; - } - } - } - - // chose the point that produces the deepest penetration against face - // and translate back into real frame - glm::vec3 sphereCenter = cubeCenter + capsuleStart + shortestDistance * capsuleAxis; - - // collide like a sphere at point0 with capsuleRadius - CollisionInfo* collision = sphereVsAACubeHelper(sphereCenter, capsuleRadius, - cubeCenter, 2.0f * halfCubeSide, collisions); - if (collision) { - // we hit! so store back pointers to the shapes - collision->_shapeA = shapeA; - collision->_shapeB = shapeB; - return true; - } - return false; -} - -bool aaCubeVsSphere(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions) { - return sphereVsAACube(shapeB, shapeA, collisions); -} - -bool aaCubeVsCapsule(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions) { - return capsuleVsAACube(shapeB, shapeA, collisions); -} - -// helper function -CollisionInfo* aaCubeVsAACubeHelper(const glm::vec3& cubeCenterA, float cubeSideA, const glm::vec3& cubeCenterB, - float cubeSideB, CollisionList& collisions) { - // cube is A - // cube is B - // BA = B - A = from center of A to center of B - float halfCubeSideA = 0.5f * cubeSideA; - float halfCubeSideB = 0.5f * cubeSideB; - glm::vec3 BA = cubeCenterB - cubeCenterA; - - float distance = glm::length(BA); - - if (distance > EPSILON) { - float maxBA = glm::max(glm::max(glm::abs(BA.x), glm::abs(BA.y)), glm::abs(BA.z)); - if (maxBA > halfCubeSideB + halfCubeSideA) { - // cube misses cube entirely - return NULL; - } - CollisionInfo* collision = collisions.getNewCollision(); - if (!collision) { - return NULL; // no more room for collisions - } - if (maxBA > halfCubeSideB) { - // cube hits cube but its center is outside cube - // compute contact anti-pole on cube (in cube frame) - glm::vec3 cubeContact = glm::abs(BA); - if (cubeContact.x > halfCubeSideB) { - cubeContact.x = halfCubeSideB; - } - if (cubeContact.y > halfCubeSideB) { - cubeContact.y = halfCubeSideB; - } - if (cubeContact.z > halfCubeSideB) { - cubeContact.z = halfCubeSideB; - } - glm::vec3 signs = glm::sign(BA); - cubeContact.x *= signs.x; - cubeContact.y *= signs.y; - cubeContact.z *= signs.z; - - // compute penetration direction - glm::vec3 direction = BA - cubeContact; - - float lengthDirection = glm::length(direction); - - if (lengthDirection < EPSILON) { - // cubeCenterA is touching cube B surface, so we can't use the difference between those two - // points to compute the penetration direction. Instead we use the unitary components of - // cubeContact. - glm::modf(cubeContact / halfCubeSideB, direction); - lengthDirection = glm::length(direction); - } else if (lengthDirection > halfCubeSideA) { - collisions.deleteLastCollision(); - return NULL; - } - direction /= lengthDirection; - - // compute collision details - collision->_contactPoint = cubeCenterA + halfCubeSideA * direction; - collision->_penetration = halfCubeSideA * direction - (BA - cubeContact); - } else { - // cube center is inside cube - // --> push out nearest face - glm::vec3 direction; - BA /= maxBA; - glm::modf(BA, direction); - float lengthDirection = glm::length(direction); - direction /= lengthDirection; - - // compute collision details - collision->_floatData = cubeSideB; - collision->_vecData = cubeCenterB; - collision->_penetration = (halfCubeSideB * lengthDirection + halfCubeSideA - maxBA * glm::dot(BA, direction)) * direction; - collision->_contactPoint = cubeCenterA + halfCubeSideA * direction; - } - collision->_shapeA = NULL; - collision->_shapeB = NULL; - return collision; - } else if (halfCubeSideA + halfCubeSideB > distance) { - // NOTE: for cocentric approximation we collide sphere and cube as two spheres which means - // this algorithm will probably be wrong when both sphere and cube are very small (both ~EPSILON) - CollisionInfo* collision = collisions.getNewCollision(); - if (collision) { - // the penetration and contactPoint are undefined, so we pick a penetration direction (-yAxis) - collision->_penetration = (halfCubeSideA + halfCubeSideB) * glm::vec3(0.0f, -1.0f, 0.0f); - // contactPoint is on surface of A - collision->_contactPoint = cubeCenterA + collision->_penetration; - collision->_shapeA = NULL; - collision->_shapeB = NULL; - return collision; - } - } - return NULL; -} - -bool aaCubeVsAACube(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions) { - // BA = B - A = from center of A to center of B - const AACubeShape* cubeA = static_cast(shapeA); - const AACubeShape* cubeB = static_cast(shapeB); - CollisionInfo* collision = aaCubeVsAACubeHelper( cubeA->getTranslation(), cubeA->getScale(), - cubeB->getTranslation(), cubeB->getScale(), collisions); - if (collision) { - collision->_shapeA = shapeA; - collision->_shapeB = shapeB; - return true; - } - return false; -} - -bool shapeVsList(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions) { - bool touching = false; - const ListShape* listB = static_cast(shapeB); - for (int i = 0; i < listB->size() && !collisions.isFull(); ++i) { - const Shape* subShape = listB->getSubShape(i); - touching = collideShapes(shapeA, subShape, collisions) || touching; - } - return touching; -} - -bool listVsShape(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions) { - bool touching = false; - const ListShape* listA = static_cast(shapeA); - for (int i = 0; i < listA->size() && !collisions.isFull(); ++i) { - const Shape* subShape = listA->getSubShape(i); - touching = collideShapes(subShape, shapeB, collisions) || touching; - } - return touching; -} - -bool listVsList(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions) { - bool touching = false; - const ListShape* listA = static_cast(shapeA); - const ListShape* listB = static_cast(shapeB); - for (int i = 0; i < listA->size() && !collisions.isFull(); ++i) { - const Shape* subShape = listA->getSubShape(i); - for (int j = 0; j < listB->size() && !collisions.isFull(); ++j) { - touching = collideShapes(subShape, listB->getSubShape(j), collisions) || touching; - } - } - return touching; -} - -// helper function -/* KEEP THIS CODE -- this is how to collide the cube with stark face normals (no rounding). -* We might want to use this code later for sealing boundaries between adjacent voxels. -bool sphereAACube_StarkAngles(const glm::vec3& sphereCenter, float sphereRadius, const glm::vec3& cubeCenter, - float cubeSide, CollisionList& collisions) { - glm::vec3 BA = cubeCenter - sphereCenter; - float distance = glm::length(BA); - if (distance > EPSILON) { - BA /= distance; // BA is now normalized - // compute the nearest point on sphere - glm::vec3 surfaceA = sphereCenter + sphereRadius * BA; - // compute the nearest point on cube - float maxBA = glm::max(glm::max(fabsf(BA.x), fabsf(BA.y)), fabsf(BA.z)); - glm::vec3 surfaceB = cubeCenter - (0.5f * cubeSide / maxBA) * BA; - // collision happens when "vector to surfaceA from surfaceB" dots with BA to produce a positive value - glm::vec3 surfaceAB = surfaceA - surfaceB; - if (glm::dot(surfaceAB, BA) > 0.0f) { - CollisionInfo* collision = collisions.getNewCollision(); - if (collision) { - // penetration is parallel to box side direction - BA /= maxBA; - glm::vec3 direction; - glm::modf(BA, direction); - direction = glm::normalize(direction); - - // penetration is the projection of surfaceAB on direction - collision->_penetration = glm::dot(surfaceAB, direction) * direction; - // contactPoint is on surface of A - collision->_contactPoint = sphereCenter + sphereRadius * direction; - collision->_shapeA = NULL; - collision->_shapeB = NULL; - return true; - } - } - } else if (sphereRadius + 0.5f * cubeSide > distance) { - // NOTE: for cocentric approximation we collide sphere and cube as two spheres which means - // this algorithm will probably be wrong when both sphere and cube are very small (both ~EPSILON) - CollisionInfo* collision = collisions.getNewCollision(); - if (collision) { - // the penetration and contactPoint are undefined, so we pick a penetration direction (-yAxis) - collision->_penetration = (sphereRadius + 0.5f * cubeSide) * glm::vec3(0.0f, -1.0f, 0.0f); - // contactPoint is on surface of A - collision->_contactPoint = sphereCenter + collision->_penetration; - collision->_shapeA = NULL; - collision->_shapeB = NULL; - return true; - } - } - return false; -} -*/ - -bool sphereVsAACubeLegacy(const SphereShape* sphereA, const glm::vec3& cubeCenter, float cubeSide, CollisionList& collisions) { - return sphereVsAACubeLegacy(sphereA->getTranslation(), sphereA->getRadius(), cubeCenter, cubeSide, collisions); -} - -bool capsuleVsAACubeLegacy(const CapsuleShape* capsuleA, const glm::vec3& cubeCenter, float cubeSide, CollisionList& collisions) { - // find nerest approach of capsule line segment to cube - glm::vec3 capsuleAxis; - capsuleA->computeNormalizedAxis(capsuleAxis); - float offset = glm::dot(cubeCenter - capsuleA->getTranslation(), capsuleAxis); - float halfHeight = capsuleA->getHalfHeight(); - if (offset > halfHeight) { - offset = halfHeight; - } else if (offset < -halfHeight) { - offset = -halfHeight; - } - glm::vec3 nearestApproach = capsuleA->getTranslation() + offset * capsuleAxis; - // collide nearest approach like a sphere at that point - return sphereVsAACubeLegacy(nearestApproach, capsuleA->getRadius(), cubeCenter, cubeSide, collisions); -} - -bool findRayIntersection(const QVector& shapes, RayIntersectionInfo& intersection) { - int numShapes = shapes.size(); - bool hit = false; - for (int i = 0; i < numShapes; ++i) { - Shape* shape = shapes.at(i); - if (shape) { - if (shape->findRayIntersection(intersection)) { - hit = true; - } - } - } - return hit; -} - -} // namespace ShapeCollider diff --git a/libraries/shared/src/ShapeCollider.h b/libraries/shared/src/ShapeCollider.h deleted file mode 100644 index da37adcd49..0000000000 --- a/libraries/shared/src/ShapeCollider.h +++ /dev/null @@ -1,156 +0,0 @@ -// -// ShapeCollider.h -// libraries/shared/src -// -// Created by Andrew Meadows on 02/20/2014. -// 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_ShapeCollider_h -#define hifi_ShapeCollider_h - -#include - -#include "CollisionInfo.h" -#include "RayIntersectionInfo.h" -#include "SharedUtil.h" - - -class Shape; -class SphereShape; -class CapsuleShape; - -namespace ShapeCollider { - - /// MUST CALL this FIRST before using the ShapeCollider - void initDispatchTable(); - - /// \param shapeA pointer to first shape (cannot be NULL) - /// \param shapeB pointer to second shape (cannot be NULL) - /// \param collisions[out] collision details - /// \return true if shapes collide - bool collideShapes(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions); - - bool collideShapeWithShapes(const Shape* shapeA, const QVector& shapes, int startIndex, CollisionList& collisions); - bool collideShapesWithShapes(const QVector& shapesA, const QVector& shapesB, CollisionList& collisions); - - /// \param shapeA a pointer to a shape (cannot be NULL) - /// \param cubeCenter center of cube - /// \param cubeSide lenght of side of cube - /// \param collisions[out] average collision details - /// \return true if shapeA collides with axis aligned cube - bool collideShapeWithAACubeLegacy(const Shape* shapeA, const glm::vec3& cubeCenter, float cubeSide, CollisionList& collisions); - - /// \param sphereA pointer to first shape (cannot be NULL) - /// \param sphereB pointer to second shape (cannot be NULL) - /// \param[out] collisions where to append collision details - /// \return true if shapes collide - bool sphereVsSphere(const Shape* sphereA, const Shape* sphereB, CollisionList& collisions); - - /// \param sphereA pointer to first shape (cannot be NULL) - /// \param capsuleB pointer to second shape (cannot be NULL) - /// \param[out] collisions where to append collision details - /// \return true if shapes collide - bool sphereVsCapsule(const Shape* sphereA, const Shape* capsuleB, CollisionList& collisions); - - /// \param sphereA pointer to first shape (cannot be NULL) - /// \param planeB pointer to second shape (cannot be NULL) - /// \param[out] collisions where to append collision details - /// \return true if shapes collide - bool sphereVsPlane(const Shape* sphereA, const Shape* planeB, CollisionList& collisions); - - /// \param capsuleA pointer to first shape (cannot be NULL) - /// \param sphereB pointer to second shape (cannot be NULL) - /// \param[out] collisions where to append collision details - /// \return true if shapes collide - bool capsuleVsSphere(const Shape* capsuleA, const Shape* sphereB, CollisionList& collisions); - - /// \param capsuleA pointer to first shape (cannot be NULL) - /// \param capsuleB pointer to second shape (cannot be NULL) - /// \param[out] collisions where to append collision details - /// \return true if shapes collide - bool capsuleVsCapsule(const Shape* capsuleA, const Shape* capsuleB, CollisionList& collisions); - - /// \param capsuleA pointer to first shape (cannot be NULL) - /// \param planeB pointer to second shape (cannot be NULL) - /// \param[out] collisions where to append collision details - /// \return true if shapes collide - bool capsuleVsPlane(const Shape* capsuleA, const Shape* planeB, CollisionList& collisions); - - /// \param planeA pointer to first shape (cannot be NULL) - /// \param sphereB pointer to second shape (cannot be NULL) - /// \param[out] collisions where to append collision details - /// \return true if shapes collide - bool planeVsSphere(const Shape* planeA, const Shape* sphereB, CollisionList& collisions); - - /// \param planeA pointer to first shape (cannot be NULL) - /// \param capsuleB pointer to second shape (cannot be NULL) - /// \param[out] collisions where to append collision details - /// \return true if shapes collide - bool planeVsCapsule(const Shape* planeA, const Shape* capsuleB, CollisionList& collisions); - - /// \param planeA pointer to first shape (cannot be NULL) - /// \param planeB pointer to second shape (cannot be NULL) - /// \param[out] collisions where to append collision details - /// \return true if shapes collide - bool planeVsPlane(const Shape* planeA, const Shape* planeB, CollisionList& collisions); - - /// helper function for *VsAACube() methods - /// \param sphereCenter center of sphere - /// \param sphereRadius radius of sphere - /// \param cubeCenter center of AACube - /// \param cubeSide scale of cube - /// \param[out] collisions where to append collision details - /// \return valid pointer to CollisionInfo if sphere and cube overlap or NULL if not - CollisionInfo* sphereVsAACubeHelper(const glm::vec3& sphereCenter, float sphereRadius, - const glm::vec3& cubeCenter, float cubeSide, CollisionList& collisions); - - bool sphereVsAACube(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions); - bool capsuleVsAACube(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions); - bool aaCubeVsSphere(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions); - bool aaCubeVsCapsule(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions); - bool aaCubeVsAACube(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions); - - /// \param shapeA pointer to first shape (cannot be NULL) - /// \param listB pointer to second shape (cannot be NULL) - /// \param[out] collisions where to append collision details - /// \return true if shapes collide - bool shapeVsList(const Shape* shapeA, const Shape* listB, CollisionList& collisions); - - /// \param listA pointer to first shape (cannot be NULL) - /// \param shapeB pointer to second shape (cannot be NULL) - /// \param[out] collisions where to append collision details - /// \return true if shapes collide - bool listVsShape(const Shape* listA, const Shape* shapeB, CollisionList& collisions); - - /// \param listA pointer to first shape (cannot be NULL) - /// \param listB pointer to second shape (cannot be NULL) - /// \param[out] collisions where to append collision details - /// \return true if shapes collide - bool listVsList(const Shape* listA, const Shape* listB, CollisionList& collisions); - - /// \param sphereA pointer to sphere (cannot be NULL) - /// \param cubeCenter center of cube - /// \param cubeSide lenght of side of cube - /// \param[out] collisions where to append collision details - /// \return true if sphereA collides with axis aligned cube - bool sphereVsAACubeLegacy(const SphereShape* sphereA, const glm::vec3& cubeCenter, float cubeSide, CollisionList& collisions); - - /// \param capsuleA pointer to capsule (cannot be NULL) - /// \param cubeCenter center of cube - /// \param cubeSide lenght of side of cube - /// \param[out] collisions where to append collision details - /// \return true if capsuleA collides with axis aligned cube - bool capsuleVsAACubeLegacy(const CapsuleShape* capsuleA, const glm::vec3& cubeCenter, float cubeSide, CollisionList& collisions); - - /// \param shapes list of pointers to shapes (shape pointers may be NULL) - /// \param intersection[out] struct with info about Ray and hit - /// \return true if ray hits any shape in shapes - bool findRayIntersection(const QVector& shapes, RayIntersectionInfo& intersection); - -} // namespace ShapeCollider - -#endif // hifi_ShapeCollider_h diff --git a/libraries/shared/src/SphereShape.cpp b/libraries/shared/src/SphereShape.cpp deleted file mode 100644 index 4c47ae91c0..0000000000 --- a/libraries/shared/src/SphereShape.cpp +++ /dev/null @@ -1,51 +0,0 @@ -// -// SphereShape.cpp -// libraries/shared/src -// -// Created by Andrew Meadows on 2014.06.17 -// 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 - -#include "SphereShape.h" - -bool SphereShape::findRayIntersection(RayIntersectionInfo& intersection) const { - float r2 = _boundingRadius * _boundingRadius; - - // compute closest approach (CA) - float a = glm::dot(_translation - intersection._rayStart, intersection._rayDirection); // a = distance from ray-start to CA - float b2 = glm::distance2(_translation, intersection._rayStart + a * intersection._rayDirection); // b2 = squared distance from sphere-center to CA - if (b2 > r2) { - // ray does not hit sphere - return false; - } - float c = sqrtf(r2 - b2); // c = distance from CA to sphere surface along intersection._rayDirection - float d2 = glm::distance2(intersection._rayStart, _translation); // d2 = squared distance from sphere-center to ray-start - float distance = FLT_MAX; - if (a < 0.0f) { - // ray points away from sphere-center - if (d2 > r2) { - // ray starts outside sphere - return false; - } - // ray starts inside sphere - distance = c + a; - } else if (d2 > r2) { - // ray starts outside sphere - distance = a - c; - } else { - // ray starts inside sphere - distance = a + c; - } - if (distance > 0.0f && distance < intersection._rayLength && distance < intersection._hitDistance) { - intersection._hitDistance = distance; - intersection._hitNormal = glm::normalize(intersection._rayStart + distance * intersection._rayDirection - _translation); - intersection._hitShape = const_cast(this); - return true; - } - return false; -} diff --git a/libraries/shared/src/SphereShape.h b/libraries/shared/src/SphereShape.h deleted file mode 100644 index 964881a715..0000000000 --- a/libraries/shared/src/SphereShape.h +++ /dev/null @@ -1,63 +0,0 @@ -// -// SphereShape.h -// libraries/shared/src -// -// Created by Andrew Meadows on 2014. -// 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_SphereShape_h -#define hifi_SphereShape_h - -#include "NumericalConstants.h" -#include "Shape.h" - -class SphereShape : public Shape { -public: - SphereShape() : Shape(SPHERE_SHAPE) {} - - SphereShape(float radius) : Shape(SPHERE_SHAPE) { - _boundingRadius = radius; - } - - SphereShape(float radius, const glm::vec3& position) : Shape(SPHERE_SHAPE, position) { - _boundingRadius = radius; - } - - virtual ~SphereShape() {} - - float getRadius() const { return _boundingRadius; } - - void setRadius(float radius) { _boundingRadius = radius; } - - bool findRayIntersection(RayIntersectionInfo& intersection) const; - - float getVolume() const { return 1.3333333333f * PI * _boundingRadius * _boundingRadius * _boundingRadius; } -}; - -inline QDebug operator<<(QDebug debug, const SphereShape& shape) { - debug << "SphereShape[ (" - << "position: " - << shape.getTranslation().x << ", " << shape.getTranslation().y << ", " << shape.getTranslation().z - << "radius: " - << shape.getRadius() - << "]"; - - return debug; -} - -inline QDebug operator<<(QDebug debug, const SphereShape* shape) { - debug << "SphereShape[ (" - << "center: " - << shape->getTranslation().x << ", " << shape->getTranslation().y << ", " << shape->getTranslation().z - << "radius: " - << shape->getRadius() - << "]"; - - return debug; -} - -#endif // hifi_SphereShape_h diff --git a/libraries/shared/src/StreamUtils.cpp b/libraries/shared/src/StreamUtils.cpp index 5726728f30..d4378d82b3 100644 --- a/libraries/shared/src/StreamUtils.cpp +++ b/libraries/shared/src/StreamUtils.cpp @@ -65,38 +65,6 @@ QDataStream& operator>>(QDataStream& in, glm::quat& quaternion) { return in >> quaternion.x >> quaternion.y >> quaternion.z >> quaternion.w; } -// less common utils can be enabled with DEBUG -// FIXME, remove the second defined clause once these compile, or remove the -// functions. -#if defined(DEBUG) && defined(FIXED_STREAMS) - -std::ostream& operator<<(std::ostream& s, const CollisionInfo& c) { - s << "{penetration=" << c._penetration - << ", contactPoint=" << c._contactPoint - << ", addedVelocity=" << c._addedVelocity - << "}"; - return s; -} - -std::ostream& operator<<(std::ostream& s, const SphereShape& sphere) { - s << "{type='sphere', center=" << sphere.getPosition() - << ", radius=" << sphere.getRadius() - << "}"; - return s; -} - -std::ostream& operator<<(std::ostream& s, const CapsuleShape& capsule) { - s << "{type='capsule', center=" << capsule.getPosition() - << ", radius=" << capsule.getRadius() - << ", length=" << (2.0f * capsule.getHalfHeight()) - << ", begin=" << capsule.getStartPoint() - << ", end=" << capsule.getEndPoint() - << "}"; - return s; -} - -#endif // DEBUG - #ifndef QT_NO_DEBUG_STREAM #include diff --git a/libraries/shared/src/StreamUtils.h b/libraries/shared/src/StreamUtils.h index fd22f7c068..caf7b565f4 100644 --- a/libraries/shared/src/StreamUtils.h +++ b/libraries/shared/src/StreamUtils.h @@ -37,16 +37,6 @@ QDataStream& operator>>(QDataStream& in, glm::vec3& vector); QDataStream& operator<<(QDataStream& out, const glm::quat& quaternion); QDataStream& operator>>(QDataStream& in, glm::quat& quaternion); -// less common utils can be enabled with DEBUG -#ifdef DEBUG -class CollisionInfo; -class SphereShape; -class CapsuleShape; -std::ostream& operator<<(std::ostream& s, const CollisionInfo& c); -std::ostream& operator<<(std::ostream& s, const SphereShape& shape); -std::ostream& operator<<(std::ostream& s, const CapsuleShape& capsule); -#endif // DEBUG - #ifndef QT_NO_DEBUG_STREAM class QDebug; // Add support for writing these to qDebug(). diff --git a/tests/physics/src/CollisionInfoTests.cpp b/tests/physics/src/CollisionInfoTests.cpp deleted file mode 100644 index 70e2e14bb0..0000000000 --- a/tests/physics/src/CollisionInfoTests.cpp +++ /dev/null @@ -1,70 +0,0 @@ -// -// CollisionInfoTests.cpp -// tests/physics/src -// -// Created by Andrew Meadows on 2/21/2014. -// 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 - -#include -#include - -#include -#include -#include - -#include "CollisionInfoTests.h" - - - -QTEST_MAIN(CollisionInfoTests) -/* -static glm::vec3 xAxis(1.0f, 0.0f, 0.0f); -static glm::vec3 xZxis(0.0f, 1.0f, 0.0f); -static glm::vec3 xYxis(0.0f, 0.0f, 1.0f); - -void CollisionInfoTests::rotateThenTranslate() { - CollisionInfo collision; - collision._penetration = xAxis; - collision._contactPoint = xYxis; - collision._addedVelocity = xAxis + xYxis + xZxis; - - glm::quat rotation = glm::angleAxis(PI_OVER_TWO, zAxis); - float distance = 3.0f; - glm::vec3 translation = distance * xYxis; - - collision.rotateThenTranslate(rotation, translation); - QCOMPARE(collision._penetration, xYxis); - - glm::vec3 expectedContactPoint = -xAxis + translation; - QCOMPARE(collision._contactPoint, expectedContactPoint); - - glm::vec3 expectedAddedVelocity = xYxis - xAxis + xZxis; - QCOMPARE(collision._addedVelocity, expectedAddedVelocity); -} - -void CollisionInfoTests::translateThenRotate() { - CollisionInfo collision; - collision._penetration = xAxis; - collision._contactPoint = xYxis; - collision._addedVelocity = xAxis + xYxis + xZxis; - - glm::quat rotation = glm::angleAxis( -PI_OVER_TWO, zAxis); - float distance = 3.0f; - glm::vec3 translation = distance * xYxis; - - collision.translateThenRotate(translation, rotation); - QCOMPARE(collision._penetration, -xYxis); - - glm::vec3 expectedContactPoint = (1.0f + distance) * xAxis; - QCOMPARE(collision._contactPoint, expectedContactPoint); - - glm::vec3 expectedAddedVelocity = - xYxis + xAxis + xYxis; - QCOMPARE(collision._addedVelocity, expectedAddedVelocity); -}*/ - diff --git a/tests/physics/src/CollisionInfoTests.h b/tests/physics/src/CollisionInfoTests.h deleted file mode 100644 index d26d39be4b..0000000000 --- a/tests/physics/src/CollisionInfoTests.h +++ /dev/null @@ -1,29 +0,0 @@ -// -// CollisionInfoTests.h -// tests/physics/src -// -// Created by Andrew Meadows on 2/21/2014. -// 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_CollisionInfoTests_h -#define hifi_CollisionInfoTests_h - -#include - -// Add additional qtest functionality (the include order is important!) -#include "GlmTestUtils.h" -#include "../QTestExtensions.h" - -class CollisionInfoTests : public QObject { - Q_OBJECT - -private slots: -// void rotateThenTranslate(); -// void translateThenRotate(); -}; - -#endif // hifi_CollisionInfoTests_h diff --git a/tests/physics/src/ShapeColliderTests.cpp b/tests/physics/src/ShapeColliderTests.cpp deleted file mode 100644 index cb42f534cb..0000000000 --- a/tests/physics/src/ShapeColliderTests.cpp +++ /dev/null @@ -1,1936 +0,0 @@ -// -// ShapeColliderTests.cpp -// tests/physics/src -// -// Created by Andrew Meadows on 02/21/2014. -// 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 -#include -#include - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "ShapeColliderTests.h" - - -const glm::vec3 origin(0.0f); -static const glm::vec3 xAxis(1.0f, 0.0f, 0.0f); -static const glm::vec3 yAxis(0.0f, 1.0f, 0.0f); -static const glm::vec3 zAxis(0.0f, 0.0f, 1.0f); - -QTEST_MAIN(ShapeColliderTests) - -void ShapeColliderTests::initTestCase() { - ShapeCollider::initDispatchTable(); -} - -void ShapeColliderTests::sphereMissesSphere() { - // non-overlapping spheres of unequal size - float radiusA = 7.0f; - float radiusB = 3.0f; - float alpha = 1.2f; - float beta = 1.3f; - glm::vec3 offsetDirection = glm::normalize(glm::vec3(1.0f, 2.0f, 3.0f)); - float offsetDistance = alpha * radiusA + beta * radiusB; - - SphereShape sphereA(radiusA, origin); - SphereShape sphereB(radiusB, offsetDistance * offsetDirection); - CollisionList collisions(16); - - // collide A to B and vice versa - QCOMPARE(ShapeCollider::collideShapes(&sphereA, &sphereB, collisions), false); - QCOMPARE(ShapeCollider::collideShapes(&sphereB, &sphereA, collisions), false); - - // Collision list should be empty - QCOMPARE(collisions.size(), 0); -} - -void ShapeColliderTests::sphereTouchesSphere() { - // overlapping spheres of unequal size - float radiusA = 7.0f; - float radiusB = 3.0f; - float alpha = 0.2f; - float beta = 0.3f; - glm::vec3 offsetDirection = glm::normalize(glm::vec3(1.0f, 2.0f, 3.0f)); - float offsetDistance = alpha * radiusA + beta * radiusB; - float expectedPenetrationDistance = (1.0f - alpha) * radiusA + (1.0f - beta) * radiusB; - glm::vec3 expectedPenetration = expectedPenetrationDistance * offsetDirection; - - SphereShape sphereA(radiusA, origin); - SphereShape sphereB(radiusB, offsetDistance * offsetDirection); - CollisionList collisions(16); - int numCollisions = 0; - - // collide A to B... - { - QCOMPARE(ShapeCollider::collideShapes(&sphereA, &sphereB, collisions), true); - ++numCollisions; - - // verify state of collisions - QCOMPARE(collisions.size(), numCollisions); - CollisionInfo* collision = collisions.getCollision(numCollisions - 1); - QVERIFY(collision != nullptr); - - // penetration points from sphereA into sphereB - QCOMPARE(collision->_penetration, expectedPenetration); - - // contactPoint is on surface of sphereA - glm::vec3 AtoB = sphereB.getTranslation() - sphereA.getTranslation(); - glm::vec3 expectedContactPoint = sphereA.getTranslation() + radiusA * glm::normalize(AtoB); - QCOMPARE(collision->_contactPoint, expectedContactPoint); - - QCOMPARE_WITH_ABS_ERROR(collision->_contactPoint, expectedContactPoint, EPSILON); - } - - // collide B to A... - { - QCOMPARE(ShapeCollider::collideShapes(&sphereB, &sphereA, collisions), true); - ++numCollisions; - - // penetration points from sphereA into sphereB - CollisionInfo* collision = collisions.getCollision(numCollisions - 1); - QCOMPARE_WITH_ABS_ERROR(collision->_penetration, -expectedPenetration, EPSILON); - - // contactPoint is on surface of sphereB - glm::vec3 BtoA = sphereA.getTranslation() - sphereB.getTranslation(); - glm::vec3 expectedContactPoint = sphereB.getTranslation() + radiusB * glm::normalize(BtoA); - - QCOMPARE_WITH_ABS_ERROR(collision->_contactPoint, expectedContactPoint, EPSILON); - } -} - -void ShapeColliderTests::sphereMissesCapsule() { - // non-overlapping sphere and capsule - float radiusA = 1.5f; - float radiusB = 2.3f; - float totalRadius = radiusA + radiusB; - float halfHeightB = 1.7f; - float axialOffset = totalRadius + 1.1f * halfHeightB; - float radialOffset = 1.2f * radiusA + 1.3f * radiusB; - - SphereShape sphereA(radiusA); - CapsuleShape capsuleB(radiusB, halfHeightB); - - // give the capsule some arbirary transform - float angle = 37.8f; - glm::vec3 axis = glm::normalize( glm::vec3(-7.0f, 2.8f, 9.3f) ); - glm::quat rotation = glm::angleAxis(angle, axis); - glm::vec3 translation(15.1f, -27.1f, -38.6f); - capsuleB.setRotation(rotation); - capsuleB.setTranslation(translation); - - CollisionList collisions(16); - - // walk sphereA along the local yAxis next to, but not touching, capsuleB - glm::vec3 localStartPosition(radialOffset, axialOffset, 0.0f); - int numberOfSteps = 10; - float delta = 1.3f * (totalRadius + halfHeightB) / (numberOfSteps - 1); - for (int i = 0; i < numberOfSteps; ++i) { - // translate sphereA into world-frame - glm::vec3 localPosition = localStartPosition + ((float)i * delta) * yAxis; - sphereA.setTranslation(rotation * localPosition + translation); - - // sphereA agains capsuleB and vice versa - QCOMPARE(ShapeCollider::collideShapes(&sphereA, &capsuleB, collisions), false); - QCOMPARE(ShapeCollider::collideShapes(&capsuleB, &sphereA, collisions), false); - } - - QCOMPARE(collisions.size(), 0); -} - -void ShapeColliderTests::sphereTouchesCapsule() { - // overlapping sphere and capsule - float radiusA = 2.0f; - float radiusB = 1.0f; - float totalRadius = radiusA + radiusB; - float halfHeightB = 2.0f; - float alpha = 0.5f; - float beta = 0.5f; - float radialOffset = alpha * radiusA + beta * radiusB; - - SphereShape sphereA(radiusA); - CapsuleShape capsuleB(radiusB, halfHeightB); - - CollisionList collisions(16); - int numCollisions = 0; - - { // sphereA collides with capsuleB's cylindrical wall - sphereA.setTranslation(radialOffset * xAxis); - - QCOMPARE(ShapeCollider::collideShapes(&sphereA, &capsuleB, collisions), true); - ++numCollisions; - - // penetration points from sphereA into capsuleB - CollisionInfo* collision = collisions.getCollision(numCollisions - 1); - glm::vec3 expectedPenetration = (radialOffset - totalRadius) * xAxis; - QCOMPARE_WITH_ABS_ERROR(collision->_penetration, expectedPenetration, EPSILON); - - // contactPoint is on surface of sphereA - glm::vec3 expectedContactPoint = sphereA.getTranslation() - radiusA * xAxis; - QCOMPARE_WITH_ABS_ERROR(collision->_contactPoint, expectedContactPoint, EPSILON); - - // capsuleB collides with sphereA - QCOMPARE(ShapeCollider::collideShapes(&capsuleB, &sphereA, collisions), true); - ++numCollisions; - - // penetration points from sphereA into capsuleB - collision = collisions.getCollision(numCollisions - 1); - expectedPenetration = - (radialOffset - totalRadius) * xAxis; - if (collision->_shapeA == &sphereA) { - // the ShapeCollider swapped the order of the shapes - expectedPenetration *= -1.0f; - } - QCOMPARE_WITH_ABS_ERROR(collision->_penetration, expectedPenetration, EPSILON); - - // contactPoint is on surface of capsuleB - glm::vec3 BtoA = sphereA.getTranslation() - capsuleB.getTranslation(); - glm::vec3 closestApproach = capsuleB.getTranslation() + glm::dot(BtoA, yAxis) * yAxis; - expectedContactPoint = closestApproach + radiusB * glm::normalize(BtoA - closestApproach); - if (collision->_shapeA == &sphereA) { - // the ShapeCollider swapped the order of the shapes - closestApproach = sphereA.getTranslation() - glm::dot(BtoA, yAxis) * yAxis; - expectedContactPoint = closestApproach - radiusB * glm::normalize(BtoA - closestApproach); - } - QCOMPARE_WITH_ABS_ERROR(collision->_contactPoint, expectedContactPoint, EPSILON); - } - { // sphereA hits end cap at axis - glm::vec3 axialOffset = (halfHeightB + alpha * radiusA + beta * radiusB) * yAxis; - sphereA.setTranslation(axialOffset); - - QCOMPARE(ShapeCollider::collideShapes(&sphereA, &capsuleB, collisions), true); - ++numCollisions; - - // penetration points from sphereA into capsuleB - CollisionInfo* collision = collisions.getCollision(numCollisions - 1); - glm::vec3 expectedPenetration = - ((1.0f - alpha) * radiusA + (1.0f - beta) * radiusB) * yAxis; - QCOMPARE_WITH_ABS_ERROR(collision->_penetration, expectedPenetration, EPSILON); - - // contactPoint is on surface of sphereA - glm::vec3 expectedContactPoint = sphereA.getTranslation() - radiusA * yAxis; - - QCOMPARE_WITH_ABS_ERROR(collision->_contactPoint, expectedContactPoint, EPSILON); - - // capsuleB collides with sphereA - if (!ShapeCollider::collideShapes(&capsuleB, &sphereA, collisions)) - { - std::cout << __FILE__ << ":" << __LINE__ - << " ERROR: capsule and sphere should touch" << std::endl; - } else { - ++numCollisions; - } - - // penetration points from sphereA into capsuleB - collision = collisions.getCollision(numCollisions - 1); - expectedPenetration = ((1.0f - alpha) * radiusA + (1.0f - beta) * radiusB) * yAxis; - if (collision->_shapeA == &sphereA) { - // the ShapeCollider swapped the order of the shapes - expectedPenetration *= -1.0f; - } - QCOMPARE_WITH_ABS_ERROR(collision->_penetration, expectedPenetration, EPSILON); - - // contactPoint is on surface of capsuleB - glm::vec3 endPoint; - capsuleB.getEndPoint(endPoint); - expectedContactPoint = endPoint + radiusB * yAxis; - if (collision->_shapeA == &sphereA) { - // the ShapeCollider swapped the order of the shapes - expectedContactPoint = axialOffset - radiusA * yAxis; - } - - QCOMPARE_WITH_ABS_ERROR(collision->_contactPoint, expectedContactPoint, EPSILON); - } - { // sphereA hits start cap at axis - glm::vec3 axialOffset = - (halfHeightB + alpha * radiusA + beta * radiusB) * yAxis; - sphereA.setTranslation(axialOffset); - - if (!ShapeCollider::collideShapes(&sphereA, &capsuleB, collisions)) - { - std::cout << __FILE__ << ":" << __LINE__ - << " ERROR: sphere and capsule should touch" << std::endl; - } else { - ++numCollisions; - } - - // penetration points from sphereA into capsuleB - CollisionInfo* collision = collisions.getCollision(numCollisions - 1); - glm::vec3 expectedPenetration = ((1.0f - alpha) * radiusA + (1.0f - beta) * radiusB) * yAxis; - - QCOMPARE_WITH_ABS_ERROR(collision->_penetration, expectedPenetration, EPSILON); - - // contactPoint is on surface of sphereA - glm::vec3 expectedContactPoint = sphereA.getTranslation() + radiusA * yAxis; - QCOMPARE_WITH_ABS_ERROR(collision->_contactPoint, expectedContactPoint, EPSILON); - - // capsuleB collides with sphereA - if (!ShapeCollider::collideShapes(&capsuleB, &sphereA, collisions)) - { - std::cout << __FILE__ << ":" << __LINE__ - << " ERROR: capsule and sphere should touch" << std::endl; - } else { - ++numCollisions; - } - - // penetration points from sphereA into capsuleB - collision = collisions.getCollision(numCollisions - 1); - expectedPenetration = - ((1.0f - alpha) * radiusA + (1.0f - beta) * radiusB) * yAxis; - if (collision->_shapeA == &sphereA) { - // the ShapeCollider swapped the order of the shapes - expectedPenetration *= -1.0f; - } - QCOMPARE_WITH_ABS_ERROR(collision->_penetration, expectedPenetration, EPSILON); - - // contactPoint is on surface of capsuleB - glm::vec3 startPoint; - capsuleB.getStartPoint(startPoint); - expectedContactPoint = startPoint - radiusB * yAxis; - if (collision->_shapeA == &sphereA) { - // the ShapeCollider swapped the order of the shapes - expectedContactPoint = axialOffset + radiusA * yAxis; - } - QCOMPARE_WITH_ABS_ERROR(collision->_contactPoint, expectedContactPoint, EPSILON); - } - if (collisions.size() != numCollisions) { - std::cout << __FILE__ << ":" << __LINE__ - << " ERROR: expected " << numCollisions << " collisions but actual number is " << collisions.size() - << std::endl; - } -} - -void ShapeColliderTests::capsuleMissesCapsule() { - // non-overlapping capsules - float radiusA = 2.0f; - float halfHeightA = 3.0f; - float radiusB = 3.0f; - float halfHeightB = 4.0f; - - float totalRadius = radiusA + radiusB; - float totalHalfLength = totalRadius + halfHeightA + halfHeightB; - - CapsuleShape capsuleA(radiusA, halfHeightA); - CapsuleShape capsuleB(radiusB, halfHeightB); - - CollisionList collisions(16); - - // side by side - capsuleB.setTranslation((1.01f * totalRadius) * xAxis); - QCOMPARE(ShapeCollider::collideShapes(&capsuleA, &capsuleB, collisions), false); - QCOMPARE(ShapeCollider::collideShapes(&capsuleB, &capsuleA, collisions), false); - - // end to end - capsuleB.setTranslation((1.01f * totalHalfLength) * xAxis); - QCOMPARE(ShapeCollider::collideShapes(&capsuleA, &capsuleB, collisions), false); - QCOMPARE(ShapeCollider::collideShapes(&capsuleB, &capsuleA, collisions), false); - - // rotate B and move it to the side - glm::quat rotation = glm::angleAxis(PI_OVER_TWO, zAxis); - capsuleB.setRotation(rotation); - capsuleB.setTranslation((1.01f * (totalRadius + capsuleB.getHalfHeight())) * xAxis); - - QCOMPARE(ShapeCollider::collideShapes(&capsuleA, &capsuleB, collisions), false); - QCOMPARE(ShapeCollider::collideShapes(&capsuleB, &capsuleA, collisions), false); - - QCOMPARE(collisions.size(), 0); -} - -void ShapeColliderTests::capsuleTouchesCapsule() { - // overlapping capsules - float radiusA = 2.0f; - float halfHeightA = 3.0f; - float radiusB = 3.0f; - float halfHeightB = 4.0f; - - float totalRadius = radiusA + radiusB; - float totalHalfLength = totalRadius + halfHeightA + halfHeightB; - - CapsuleShape capsuleA(radiusA, halfHeightA); - CapsuleShape capsuleB(radiusB, halfHeightB); - - CollisionList collisions(16); - int numCollisions = 0; - - { // side by side - capsuleB.setTranslation((0.99f * totalRadius) * xAxis); - QCOMPARE(ShapeCollider::collideShapes(&capsuleA, &capsuleB, collisions), true); - QCOMPARE(ShapeCollider::collideShapes(&capsuleB, &capsuleA, collisions), true); - numCollisions += 2; - } - - { // end to end - capsuleB.setTranslation((0.99f * totalHalfLength) * yAxis); - - QCOMPARE(ShapeCollider::collideShapes(&capsuleA, &capsuleB, collisions), true); - QCOMPARE(ShapeCollider::collideShapes(&capsuleB, &capsuleA, collisions), true); - numCollisions += 2; - } - - { // rotate B and move it to the side - glm::quat rotation = glm::angleAxis(PI_OVER_TWO, zAxis); - capsuleB.setRotation(rotation); - capsuleB.setTranslation((0.99f * (totalRadius + capsuleB.getHalfHeight())) * xAxis); - - QCOMPARE(ShapeCollider::collideShapes(&capsuleA, &capsuleB, collisions), true); - QCOMPARE(ShapeCollider::collideShapes(&capsuleB, &capsuleA, collisions), true); - numCollisions += 2; - } - - { // again, but this time check collision details - float overlap = 0.1f; - glm::quat rotation = glm::angleAxis(PI_OVER_TWO, zAxis); - capsuleB.setRotation(rotation); - glm::vec3 positionB = ((totalRadius + capsuleB.getHalfHeight()) - overlap) * xAxis; - capsuleB.setTranslation(positionB); - - // capsuleA vs capsuleB - QCOMPARE(ShapeCollider::collideShapes(&capsuleA, &capsuleB, collisions), true); - ++numCollisions; - - CollisionInfo* collision = collisions.getCollision(numCollisions - 1); - glm::vec3 expectedPenetration = overlap * xAxis; - - QCOMPARE_WITH_ABS_ERROR(collision->_penetration, expectedPenetration, EPSILON); - - glm::vec3 expectedContactPoint = capsuleA.getTranslation() + radiusA * xAxis; - QCOMPARE_WITH_ABS_ERROR(collision->_contactPoint, expectedContactPoint, EPSILON); - - // capsuleB vs capsuleA - QCOMPARE(ShapeCollider::collideShapes(&capsuleB, &capsuleA, collisions), true); - ++numCollisions; - - collision = collisions.getCollision(numCollisions - 1); - expectedPenetration = - overlap * xAxis; - QCOMPARE_WITH_ABS_ERROR(collision->_penetration, expectedPenetration, EPSILON); - - expectedContactPoint = capsuleB.getTranslation() - (radiusB + halfHeightB) * xAxis; - QCOMPARE_WITH_ABS_ERROR(collision->_contactPoint, expectedContactPoint, EPSILON); - } - - { // collide cylinder wall against cylinder wall - float overlap = 0.137f; - float shift = 0.317f * halfHeightA; - glm::quat rotation = glm::angleAxis(PI_OVER_TWO, zAxis); - capsuleB.setRotation(rotation); - glm::vec3 positionB = (totalRadius - overlap) * zAxis + shift * yAxis; - capsuleB.setTranslation(positionB); - - // capsuleA vs capsuleB - QCOMPARE(ShapeCollider::collideShapes(&capsuleA, &capsuleB, collisions), true); - ++numCollisions; - - CollisionInfo* collision = collisions.getCollision(numCollisions - 1); - glm::vec3 expectedPenetration = overlap * zAxis; - - QCOMPARE_WITH_ABS_ERROR(collision->_penetration, expectedPenetration, EPSILON); - - glm::vec3 expectedContactPoint = capsuleA.getTranslation() + radiusA * zAxis + shift * yAxis; - QCOMPARE_WITH_ABS_ERROR(collision->_contactPoint, expectedContactPoint, EPSILON); - } -} - -void ShapeColliderTests::sphereMissesAACube() { - CollisionList collisions(16); - - float sphereRadius = 1.0f; - glm::vec3 sphereCenter(0.0f); - - glm::vec3 cubeCenter(1.5f, 0.0f, 0.0f); - - float cubeSide = 2.0f; - - glm::vec3 faceNormals[] = {xAxis, yAxis, zAxis}; - int numDirections = 3; - - float offset = 2.0f * EPSILON; - - // faces - for (int i = 0; i < numDirections; ++i) { - for (float sign = -1.0f; sign < 2.0f; sign += 2.0f) { - glm::vec3 faceNormal = sign * faceNormals[i]; - - sphereCenter = cubeCenter + (0.5f * cubeSide + sphereRadius + offset) * faceNormal; - - CollisionInfo* collision = ShapeCollider::sphereVsAACubeHelper(sphereCenter, sphereRadius, - cubeCenter, cubeSide, collisions); - - if (collision) { - QFAIL_WITH_MESSAGE("sphere should NOT collide with cube face.\n\t\t" - << "faceNormal = " << faceNormal); -// std::cout << __FILE__ << ":" << __LINE__ << " ERROR: sphere should NOT collide with cube face." -// << " faceNormal = " << faceNormal << std::endl; - } - } - } - - // edges - int numSteps = 5; - // loop over each face... - for (int i = 0; i < numDirections; ++i) { - for (float faceSign = -1.0f; faceSign < 2.0f; faceSign += 2.0f) { - glm::vec3 faceNormal = faceSign * faceNormals[i]; - - // loop over each neighboring face... - for (int j = (i + 1) % numDirections; j != i; j = (j + 1) % numDirections) { - // Compute the index to the third direction, which points perpendicular to both the face - // and the neighbor face. - int k = (j + 1) % numDirections; - if (k == i) { - k = (i + 1) % numDirections; - } - glm::vec3 thirdNormal = faceNormals[k]; - - for (float neighborSign = -1.0f; neighborSign < 2.0f; neighborSign += 2.0f) { - collisions.clear(); - glm::vec3 neighborNormal = neighborSign * faceNormals[j]; - - // combine the face and neighbor normals to get the edge normal - glm::vec3 edgeNormal = glm::normalize(faceNormal + neighborNormal); - // Step the sphere along the edge in the direction of thirdNormal, starting at one corner and - // moving to the other. Test the penetration (invarient) and contact (changing) at each point. - float delta = cubeSide / (float)(numSteps - 1); - glm::vec3 startPosition = cubeCenter + (0.5f * cubeSide) * (faceNormal + neighborNormal - thirdNormal); - for (int m = 0; m < numSteps; ++m) { - sphereCenter = startPosition + ((float)m * delta) * thirdNormal + (sphereRadius + offset) * edgeNormal; - - CollisionInfo* collision = ShapeCollider::sphereVsAACubeHelper(sphereCenter, sphereRadius, - cubeCenter, cubeSide, collisions); - if (collision) { - QFAIL_WITH_MESSAGE("sphere should NOT collide with cube edge.\n\t\t" - << "edgeNormal = " << edgeNormal); - } - } - - } - } - } - } - - // corners - for (float firstSign = -1.0f; firstSign < 2.0f; firstSign += 2.0f) { - glm::vec3 firstNormal = firstSign * faceNormals[0]; - for (float secondSign = -1.0f; secondSign < 2.0f; secondSign += 2.0f) { - glm::vec3 secondNormal = secondSign * faceNormals[1]; - for (float thirdSign = -1.0f; thirdSign < 2.0f; thirdSign += 2.0f) { - collisions.clear(); - glm::vec3 thirdNormal = thirdSign * faceNormals[2]; - - // the cornerNormal is the normalized sum of the three faces - glm::vec3 cornerNormal = glm::normalize(firstNormal + secondNormal + thirdNormal); - - // compute a direction that is slightly offset from cornerNormal - glm::vec3 perpAxis = glm::normalize(glm::cross(cornerNormal, firstNormal)); - glm::vec3 nearbyAxis = glm::normalize(cornerNormal + 0.3f * perpAxis); - - // swing the sphere on a small cone that starts at the corner and is centered on the cornerNormal - float delta = TWO_PI / (float)(numSteps - 1); - for (int i = 0; i < numSteps; i++) { - float angle = (float)i * delta; - glm::quat rotation = glm::angleAxis(angle, cornerNormal); - glm::vec3 offsetAxis = rotation * nearbyAxis; - sphereCenter = cubeCenter + (SQUARE_ROOT_OF_3 * 0.5f * cubeSide) * cornerNormal + (sphereRadius + offset) * offsetAxis; - - CollisionInfo* collision = ShapeCollider::sphereVsAACubeHelper(sphereCenter, sphereRadius, - cubeCenter, cubeSide, collisions); - - if (collision) { - - QFAIL_WITH_MESSAGE("sphere should NOT collide with cube corner\n\t\t" << - "cornerNormal = " << cornerNormal); - break; - } - } - } - } - } -} - -void ShapeColliderTests::sphereTouchesAACubeFaces() { - CollisionList collisions(16); - - float sphereRadius = 1.13f; - glm::vec3 sphereCenter(0.0f); - - glm::vec3 cubeCenter(1.23f, 4.56f, 7.89f); - float cubeSide = 4.34f; - - glm::vec3 faceNormals[] = {xAxis, yAxis, zAxis}; - int numDirections = 3; - - for (int i = 0; i < numDirections; ++i) { - // loop over both sides of cube positive and negative - for (float sign = -1.0f; sign < 2.0f; sign += 2.0f) { - glm::vec3 faceNormal = sign * faceNormals[i]; - // outside - { - collisions.clear(); - float overlap = 0.25f * sphereRadius; - float parallelOffset = 0.5f * cubeSide + sphereRadius - overlap; - float perpOffset = 0.25f * cubeSide; - glm::vec3 expectedPenetration = - overlap * faceNormal; - - // We rotate the position of the sphereCenter about a circle on the cube face so that - // it hits the same face in multiple spots. The penetration should be invarient for - // all collisions. - float delta = TWO_PI / 4.0f; - for (float angle = 0; angle < TWO_PI + EPSILON; angle += delta) { - glm::quat rotation = glm::angleAxis(angle, faceNormal); - glm::vec3 perpAxis = rotation * faceNormals[(i + 1) % numDirections]; - - sphereCenter = cubeCenter + parallelOffset * faceNormal + perpOffset * perpAxis; - - CollisionInfo* collision = ShapeCollider::sphereVsAACubeHelper(sphereCenter, sphereRadius, - cubeCenter, cubeSide, collisions); - - if (!collision) { - - QFAIL_WITH_MESSAGE("sphere should collide outside cube face\n\t\t" << - "faceNormal = " << faceNormal); - break; - } - - QCOMPARE_WITH_ABS_ERROR(collision->_penetration, expectedPenetration, EPSILON); - - glm::vec3 expectedContact = sphereCenter - sphereRadius * faceNormal; - QCOMPARE_WITH_ABS_ERROR(collision->_contactPoint, expectedContact, EPSILON); - QCOMPARE(collision->getShapeA(), (Shape*)nullptr); - QCOMPARE(collision->getShapeB(), (Shape*)nullptr); - } - } - - // inside - { - collisions.clear(); - float overlap = 1.25f * sphereRadius; - float sphereOffset = 0.5f * cubeSide + sphereRadius - overlap; - sphereCenter = cubeCenter + sphereOffset * faceNormal; - - CollisionInfo* collision = ShapeCollider::sphereVsAACubeHelper(sphereCenter, sphereRadius, - cubeCenter, cubeSide, collisions); - - if (!collision) { - QFAIL_WITH_MESSAGE("sphere should collide inside cube face.\n\t\t" - << "faceNormal = " << faceNormal); - break; - } - - glm::vec3 expectedPenetration = - overlap * faceNormal; - QCOMPARE_WITH_ABS_ERROR(collision->_penetration, expectedPenetration, EPSILON); - - glm::vec3 expectedContact = sphereCenter - sphereRadius * faceNormal; - QCOMPARE_WITH_ABS_ERROR(collision->_contactPoint, expectedContact, EPSILON); - } - } - } -} - -void ShapeColliderTests::sphereTouchesAACubeEdges() { - CollisionList collisions(20); - - float sphereRadius = 1.37f; - glm::vec3 sphereCenter(0.0f); - - glm::vec3 cubeCenter(1.23f, 4.56f, 7.89f); - float cubeSide = 2.98f; - - float overlap = 0.25f * sphereRadius; - int numSteps = 5; - - glm::vec3 faceNormals[] = {xAxis, yAxis, zAxis}; - int numDirections = 3; - - // loop over each face... - for (int i = 0; i < numDirections; ++i) { - for (float faceSign = -1.0f; faceSign < 2.0f; faceSign += 2.0f) { - glm::vec3 faceNormal = faceSign * faceNormals[i]; - - // loop over each neighboring face... - for (int j = (i + 1) % numDirections; j != i; j = (j + 1) % numDirections) { - // Compute the index to the third direction, which points perpendicular to both the face - // and the neighbor face. - int k = (j + 1) % numDirections; - if (k == i) { - k = (i + 1) % numDirections; - } - glm::vec3 thirdNormal = faceNormals[k]; - - for (float neighborSign = -1.0f; neighborSign < 2.0f; neighborSign += 2.0f) { - collisions.clear(); - glm::vec3 neighborNormal = neighborSign * faceNormals[j]; - - // combine the face and neighbor normals to get the edge normal - glm::vec3 edgeNormal = glm::normalize(faceNormal + neighborNormal); - - // Step the sphere along the edge in the direction of thirdNormal, starting at one corner and - // moving to the other. Test the penetration (invarient) and contact (changing) at each point. - glm::vec3 expectedPenetration = - overlap * edgeNormal; - float delta = cubeSide / (float)(numSteps - 1); - glm::vec3 startPosition = cubeCenter + (0.5f * cubeSide) * (faceNormal + neighborNormal - thirdNormal); - for (int m = 0; m < numSteps; ++m) { - sphereCenter = startPosition + ((float)m * delta) * thirdNormal + (sphereRadius - overlap) * edgeNormal; - - CollisionInfo* collision = ShapeCollider::sphereVsAACubeHelper(sphereCenter, sphereRadius, - cubeCenter, cubeSide, collisions); - if (!collision) { - QFAIL_WITH_MESSAGE("sphere should collide with cube edge.\n\t\t" - << "edgeNormal = " << edgeNormal); - break; - } - QCOMPARE_WITH_ABS_ERROR(collision->_penetration, expectedPenetration, EPSILON); - - glm::vec3 expectedContact = sphereCenter - sphereRadius * edgeNormal; - QCOMPARE_WITH_ABS_ERROR(collision->_contactPoint, expectedContact, EPSILON); - } - } - } - } - } -} - -void ShapeColliderTests::sphereTouchesAACubeCorners() { - CollisionList collisions(20); - - float sphereRadius = 1.37f; - glm::vec3 sphereCenter(0.0f); - - glm::vec3 cubeCenter(1.23f, 4.56f, 7.89f); - float cubeSide = 2.98f; - - float overlap = 0.25f * sphereRadius; - int numSteps = 5; - - glm::vec3 faceNormals[] = {xAxis, yAxis, zAxis}; - - for (float firstSign = -1.0f; firstSign < 2.0f; firstSign += 2.0f) { - glm::vec3 firstNormal = firstSign * faceNormals[0]; - for (float secondSign = -1.0f; secondSign < 2.0f; secondSign += 2.0f) { - glm::vec3 secondNormal = secondSign * faceNormals[1]; - for (float thirdSign = -1.0f; thirdSign < 2.0f; thirdSign += 2.0f) { - collisions.clear(); - glm::vec3 thirdNormal = thirdSign * faceNormals[2]; - - // the cornerNormal is the normalized sum of the three faces - glm::vec3 cornerNormal = glm::normalize(firstNormal + secondNormal + thirdNormal); - - // compute a direction that is slightly offset from cornerNormal - glm::vec3 perpAxis = glm::normalize(glm::cross(cornerNormal, firstNormal)); - glm::vec3 nearbyAxis = glm::normalize(cornerNormal + 0.1f * perpAxis); - - // swing the sphere on a small cone that starts at the corner and is centered on the cornerNormal - float delta = TWO_PI / (float)(numSteps - 1); - for (int i = 0; i < numSteps; i++) { - float angle = (float)i * delta; - glm::quat rotation = glm::angleAxis(angle, cornerNormal); - glm::vec3 offsetAxis = rotation * nearbyAxis; - sphereCenter = cubeCenter + (SQUARE_ROOT_OF_3 * 0.5f * cubeSide) * cornerNormal + (sphereRadius - overlap) * offsetAxis; - - CollisionInfo* collision = ShapeCollider::sphereVsAACubeHelper(sphereCenter, sphereRadius, - cubeCenter, cubeSide, collisions); - - if (!collision) { - QFAIL_WITH_MESSAGE("sphere should collide with cube corner.\n\t\t" - << "cornerNormal = " << cornerNormal); - break; - } - - glm::vec3 expectedPenetration = - overlap * offsetAxis; - QCOMPARE_WITH_ABS_ERROR(collision->_penetration, expectedPenetration, EPSILON); - - glm::vec3 expectedContact = sphereCenter - sphereRadius * offsetAxis; - QCOMPARE_WITH_ABS_ERROR(collision->_contactPoint, expectedContact, EPSILON); - } - } - } - } -} - -void ShapeColliderTests::capsuleMissesAACube() { - CollisionList collisions(16); - - float capsuleRadius = 1.0f; - - glm::vec3 cubeCenter(1.23f, 4.56f, 7.89f); - float cubeSide = 2.0f; - AACubeShape cube(cubeSide, cubeCenter); - - glm::vec3 faceNormals[] = {xAxis, yAxis, zAxis}; - int numDirections = 3; - - float offset = 2.0f * EPSILON; - - // capsule caps miss cube faces - for (int i = 0; i < numDirections; ++i) { - for (float sign = -1.0f; sign < 2.0f; sign += 2.0f) { - glm::vec3 faceNormal = sign * faceNormals[i]; - glm::vec3 secondNormal = faceNormals[(i + 1) % numDirections]; - glm::vec3 thirdNormal = faceNormals[(i + 2) % numDirections]; - - // pick a random point somewhere above the face - glm::vec3 startPoint = cubeCenter + (cubeSide + capsuleRadius) * faceNormal + - (cubeSide * (randFloat() - 0.5f)) * secondNormal + - (cubeSide * (randFloat() - 0.5f)) * thirdNormal; - - // pick a second random point slightly more than one radius above the face - glm::vec3 endPoint = cubeCenter + (0.5f * cubeSide + capsuleRadius + offset) * faceNormal + - (cubeSide * (randFloat() - 0.5f)) * secondNormal + - (cubeSide * (randFloat() - 0.5f)) * thirdNormal; - - // randomly swap the points so capsule axis may point toward or away from face - if (randFloat() > 0.5f) { - glm::vec3 temp = startPoint; - startPoint = endPoint; - endPoint = temp; - } - - // create a capsule between the points - CapsuleShape capsule(capsuleRadius, startPoint, endPoint); - - // collide capsule with cube - QCOMPARE(ShapeCollider::capsuleVsAACube(&capsule, &cube, collisions), false); - } - } - - // capsule caps miss cube edges - // loop over each face... - for (int i = 0; i < numDirections; ++i) { - for (float faceSign = -1.0f; faceSign < 2.0f; faceSign += 2.0f) { - glm::vec3 faceNormal = faceSign * faceNormals[i]; - - // loop over each neighboring face... - for (int j = (i + 1) % numDirections; j != i; j = (j + 1) % numDirections) { - // Compute the index to the third direction, which points perpendicular to both the face - // and the neighbor face. - int k = (j + 1) % numDirections; - if (k == i) { - k = (i + 1) % numDirections; - } - glm::vec3 thirdNormal = faceNormals[k]; - - collisions.clear(); - for (float neighborSign = -1.0f; neighborSign < 2.0f; neighborSign += 2.0f) { - glm::vec3 neighborNormal = neighborSign * faceNormals[j]; - - // combine the face and neighbor normals to get the edge normal - glm::vec3 edgeNormal = glm::normalize(faceNormal + neighborNormal); - - // pick a random point somewhere above the edge - glm::vec3 startPoint = cubeCenter + (SQUARE_ROOT_OF_2 * cubeSide + capsuleRadius) * edgeNormal + - (cubeSide * (randFloat() - 0.5f)) * thirdNormal; - - // pick a second random point slightly more than one radius above the edge - glm::vec3 endPoint = cubeCenter + (SQUARE_ROOT_OF_2 * 0.5f * cubeSide + capsuleRadius + offset) * edgeNormal + - (cubeSide * (randFloat() - 0.5f)) * thirdNormal; - - // randomly swap the points so capsule axis may point toward or away from edge - if (randFloat() > 0.5f) { - glm::vec3 temp = startPoint; - startPoint = endPoint; - endPoint = temp; - } - - // create a capsule between the points - CapsuleShape capsule(capsuleRadius, startPoint, endPoint); - - // collide capsule with cube - QCOMPARE(ShapeCollider::capsuleVsAACube(&capsule, &cube, collisions), false); - } - } - } - } - - // capsule caps miss cube corners - for (float firstSign = -1.0f; firstSign < 2.0f; firstSign += 2.0f) { - glm::vec3 firstNormal = firstSign * faceNormals[0]; - for (float secondSign = -1.0f; secondSign < 2.0f; secondSign += 2.0f) { - glm::vec3 secondNormal = secondSign * faceNormals[1]; - for (float thirdSign = -1.0f; thirdSign < 2.0f; thirdSign += 2.0f) { - collisions.clear(); - glm::vec3 thirdNormal = thirdSign * faceNormals[2]; - - // the cornerNormal is the normalized sum of the three faces - glm::vec3 cornerNormal = glm::normalize(firstNormal + secondNormal + thirdNormal); - - // pick a random point somewhere above the corner - glm::vec3 startPoint = cubeCenter + (SQUARE_ROOT_OF_3 * cubeSide + capsuleRadius) * cornerNormal + - (0.25f * cubeSide * (randFloat() - 0.5f)) * firstNormal + - (0.25f * cubeSide * (randFloat() - 0.5f)) * secondNormal + - (0.25f * cubeSide * (randFloat() - 0.5f)) * thirdNormal; - - // pick a second random point slightly more than one radius above the corner - glm::vec3 endPoint = cubeCenter + (SQUARE_ROOT_OF_3 * 0.5f * cubeSide + capsuleRadius + offset) * cornerNormal; - - // randomly swap the points so capsule axis may point toward or away from corner - if (randFloat() > 0.5f) { - glm::vec3 temp = startPoint; - startPoint = endPoint; - endPoint = temp; - } - - // create a capsule between the points - CapsuleShape capsule(capsuleRadius, startPoint, endPoint); - - // collide capsule with cube - QCOMPARE(ShapeCollider::capsuleVsAACube(&capsule, &cube, collisions), false); - } - } - } - - // capsule sides almost hit cube edges - // loop over each face... - float capsuleLength = 2.0f; - for (int i = 0; i < numDirections; ++i) { - for (float faceSign = -1.0f; faceSign < 2.0f; faceSign += 2.0f) { - glm::vec3 faceNormal = faceSign * faceNormals[i]; - - // loop over each neighboring face... - for (int j = (i + 1) % numDirections; j != i; j = (j + 1) % numDirections) { - // Compute the index to the third direction, which points perpendicular to both the face - // and the neighbor face. - int k = (j + 1) % numDirections; - if (k == i) { - k = (i + 1) % numDirections; - } - glm::vec3 thirdNormal = faceNormals[k]; - - collisions.clear(); - for (float neighborSign = -1.0f; neighborSign < 2.0f; neighborSign += 2.0f) { - glm::vec3 neighborNormal = neighborSign * faceNormals[j]; - - // combine the face and neighbor normals to get the edge normal - glm::vec3 edgeNormal = glm::normalize(faceNormal + neighborNormal); - - // pick a random point somewhere along the edge - glm::vec3 edgePoint = cubeCenter + (SQUARE_ROOT_OF_2 * 0.5f * cubeSide) * edgeNormal + - ((cubeSide - 2.0f * offset) * (randFloat() - 0.5f)) * thirdNormal; - - // pick a random normal that is deflected slightly from edgeNormal - glm::vec3 deflectedNormal = glm::normalize(edgeNormal + - (0.1f * (randFloat() - 0.5f)) * faceNormal + - (0.1f * (randFloat() - 0.5f)) * neighborNormal); - - // compute the axis direction, which will be perp to deflectedNormal and thirdNormal - glm::vec3 axisDirection = glm::normalize(glm::cross(deflectedNormal, thirdNormal)); - - // compute a point for the capsule's axis along deflection normal away from edgePoint - glm::vec3 axisPoint = edgePoint + (capsuleRadius + offset) * deflectedNormal; - - // now we can compute the capsule endpoints - glm::vec3 endPoint = axisPoint + (0.5f * capsuleLength * randFloat()) * axisDirection; - glm::vec3 startPoint = axisPoint - (0.5f * capsuleLength * randFloat()) * axisDirection; - - // create a capsule between the points - CapsuleShape capsule(capsuleRadius, startPoint, endPoint); - - // collide capsule with cube - QCOMPARE(ShapeCollider::capsuleVsAACube(&capsule, &cube, collisions), false); - } - } - } - } - - // capsule sides almost hit cube corners - for (float firstSign = -1.0f; firstSign < 2.0f; firstSign += 2.0f) { - glm::vec3 firstNormal = firstSign * faceNormals[0]; - for (float secondSign = -1.0f; secondSign < 2.0f; secondSign += 2.0f) { - glm::vec3 secondNormal = secondSign * faceNormals[1]; - for (float thirdSign = -1.0f; thirdSign < 2.0f; thirdSign += 2.0f) { - collisions.clear(); - glm::vec3 thirdNormal = thirdSign * faceNormals[2]; - - // the cornerNormal is the normalized sum of the three faces - glm::vec3 cornerNormal = glm::normalize(firstNormal + secondNormal + thirdNormal); - - // compute a penetration normal that is somewhat randomized about cornerNormal - glm::vec3 penetrationNormal = - glm::normalize(cornerNormal + - (0.05f * cubeSide * (randFloat() - 0.5f)) * firstNormal + - (0.05f * cubeSide * (randFloat() - 0.5f)) * secondNormal + - (0.05f * cubeSide * (randFloat() - 0.5f)) * thirdNormal); - - // pick a random point somewhere above the corner - glm::vec3 corner = cubeCenter + (0.5f * cubeSide) * (firstNormal + secondNormal + thirdNormal); - glm::vec3 startPoint = corner + (3.0f * cubeSide) * cornerNormal + - (0.25f * cubeSide * (randFloat() - 0.5f)) * firstNormal + - (0.25f * cubeSide * (randFloat() - 0.5f)) * secondNormal + - (0.25f * cubeSide * (randFloat() - 0.5f)) * thirdNormal; - - // pick a second random point slightly less than one radius above the corner - // with some sight perp motion - glm::vec3 endPoint = corner - (capsuleRadius + offset) * penetrationNormal; - - // randomly swap the points so capsule axis may point toward or away from corner - if (randFloat() > 0.5f) { - glm::vec3 temp = startPoint; - startPoint = endPoint; - endPoint = temp; - } - - // create a capsule between the points - CapsuleShape capsule(capsuleRadius, startPoint, endPoint); - - // collide capsule with cube - QCOMPARE(ShapeCollider::capsuleVsAACube(&capsule, &cube, collisions), false); - } - } - } - - // capsule sides almost hit cube faces - // these are the steps along the capsuleAxis where we'll put the capsule endpoints - float steps[] = { -1.0f, 2.0f, 0.25f, 0.75f, -1.0f }; - - for (int i = 0; i < numDirections; ++i) { - for (float sign = -1.0f; sign < 2.0f; sign += 2.0f) { - glm::vec3 faceNormal = sign * faceNormals[i]; - glm::vec3 secondNormal = faceNormals[(i + 1) % numDirections]; - glm::vec3 thirdNormal = faceNormals[(i + 2) % numDirections]; - - // pick two random point on opposite edges of the face - glm::vec3 firstEdgeIntersection = cubeCenter + (0.5f * cubeSide) * (faceNormal + secondNormal) + - (cubeSide * (randFloat() - 0.5f)) * thirdNormal; - glm::vec3 secondEdgeIntersection = cubeCenter + (0.5f * cubeSide) * (faceNormal - secondNormal) + - (cubeSide * (randFloat() - 0.5f)) * thirdNormal; - - // compute the un-normalized axis for the capsule - glm::vec3 capsuleAxis = secondEdgeIntersection - firstEdgeIntersection; - // there are three pairs in steps[] - for (int j = 0; j < 4; j++) { - collisions.clear(); - glm::vec3 startPoint = firstEdgeIntersection + steps[j] * capsuleAxis + (capsuleRadius + offset) * faceNormal; - glm::vec3 endPoint = firstEdgeIntersection + steps[j + 1] * capsuleAxis + (capsuleRadius + offset) * faceNormal; - - // create a capsule between the points - CapsuleShape capsule(capsuleRadius, startPoint, endPoint); - - // collide capsule with cube - QCOMPARE(ShapeCollider::capsuleVsAACube(&capsule, &cube, collisions), false); - } - } - } -} - -void ShapeColliderTests::capsuleTouchesAACube() { - CollisionList collisions(16); - - float capsuleRadius = 1.0f; - - glm::vec3 cubeCenter(1.23f, 4.56f, 7.89f); - float cubeSide = 2.0f; - AACubeShape cube(cubeSide, cubeCenter); - - glm::vec3 faceNormals[] = {xAxis, yAxis, zAxis}; - int numDirections = 3; - - float overlap = 0.25f * capsuleRadius; - float allowableError = 10.0f * EPSILON; - - // capsule caps hit cube faces - for (int i = 0; i < numDirections; ++i) { - for (float sign = -1.0f; sign < 2.0f; sign += 2.0f) { - glm::vec3 faceNormal = sign * faceNormals[i]; - glm::vec3 secondNormal = faceNormals[(i + 1) % numDirections]; - glm::vec3 thirdNormal = faceNormals[(i + 2) % numDirections]; - - // pick a random point somewhere above the face - glm::vec3 startPoint = cubeCenter + (cubeSide + capsuleRadius) * faceNormal + - (cubeSide * (randFloat() - 0.5f)) * secondNormal + - (cubeSide * (randFloat() - 0.5f)) * thirdNormal; - - // pick a second random point slightly less than one radius above the face - // (but reduce width of range by 2*overlap to prevent the penetration from - // registering against other faces) - glm::vec3 endPoint = cubeCenter + (0.5f * cubeSide + capsuleRadius - overlap) * faceNormal + - ((cubeSide - 2.0f * overlap) * (randFloat() - 0.5f)) * secondNormal + - ((cubeSide - 2.0f * overlap) * (randFloat() - 0.5f)) * thirdNormal; - glm::vec3 collidingPoint = endPoint; - - // randomly swap the points so capsule axis may point toward or away from face - if (randFloat() > 0.5f) { - glm::vec3 temp = startPoint; - startPoint = endPoint; - endPoint = temp; - } - - // create a capsule between the points - CapsuleShape capsule(capsuleRadius, startPoint, endPoint); - - // collide capsule with cube - QCOMPARE(ShapeCollider::capsuleVsAACube(&capsule, &cube, collisions), true); - - CollisionInfo* collision = collisions.getLastCollision(); - - // penetration points from capsule into cube - glm::vec3 expectedPenetration = - overlap * faceNormal; - QCOMPARE_WITH_ABS_ERROR(collision->_penetration, expectedPenetration, allowableError); - - // contactPoint is on surface of capsule - glm::vec3 expectedContactPoint = collidingPoint - capsuleRadius * faceNormal; - QCOMPARE_WITH_ABS_ERROR(collision->_contactPoint, expectedContactPoint, allowableError); - } - } - - // capsule caps hit cube edges - // loop over each face... - for (int i = 0; i < numDirections; ++i) { - for (float faceSign = -1.0f; faceSign < 2.0f; faceSign += 2.0f) { - glm::vec3 faceNormal = faceSign * faceNormals[i]; - - // loop over each neighboring face... - for (int j = (i + 1) % numDirections; j != i; j = (j + 1) % numDirections) { - // Compute the index to the third direction, which points perpendicular to both the face - // and the neighbor face. - int k = (j + 1) % numDirections; - if (k == i) { - k = (i + 1) % numDirections; - } - glm::vec3 thirdNormal = faceNormals[k]; - - collisions.clear(); - for (float neighborSign = -1.0f; neighborSign < 2.0f; neighborSign += 2.0f) { - glm::vec3 neighborNormal = neighborSign * faceNormals[j]; - - // combine the face and neighbor normals to get the edge normal - glm::vec3 edgeNormal = glm::normalize(faceNormal + neighborNormal); - - // pick a random point somewhere above the edge - glm::vec3 startPoint = cubeCenter + (SQUARE_ROOT_OF_2 * cubeSide + capsuleRadius) * edgeNormal + - (cubeSide * (randFloat() - 0.5f)) * thirdNormal; - - // pick a second random point slightly less than one radius above the edge - glm::vec3 endPoint = cubeCenter + (SQUARE_ROOT_OF_2 * 0.5f * cubeSide + capsuleRadius - overlap) * edgeNormal + - (cubeSide * (randFloat() - 0.5f)) * thirdNormal; - glm::vec3 collidingPoint = endPoint; - - // randomly swap the points so capsule axis may point toward or away from edge - if (randFloat() > 0.5f) { - glm::vec3 temp = startPoint; - startPoint = endPoint; - endPoint = temp; - } - - // create a capsule between the points - CapsuleShape capsule(capsuleRadius, startPoint, endPoint); - - // collide capsule with cube - QCOMPARE(ShapeCollider::capsuleVsAACube(&capsule, &cube, collisions), true); - - CollisionInfo* collision = collisions.getLastCollision(); - QCOMPARE(collision != nullptr, true); - - // penetration points from capsule into cube - glm::vec3 expectedPenetration = - overlap * edgeNormal; - QCOMPARE_WITH_ABS_ERROR(collision->_penetration, expectedPenetration, allowableError); - - // contactPoint is on surface of capsule - glm::vec3 expectedContactPoint = collidingPoint - capsuleRadius * edgeNormal; - QCOMPARE_WITH_ABS_ERROR(collision->_contactPoint, expectedContactPoint, allowableError); - } - } - } - } - - // capsule caps hit cube corners - for (float firstSign = -1.0f; firstSign < 2.0f; firstSign += 2.0f) { - glm::vec3 firstNormal = firstSign * faceNormals[0]; - for (float secondSign = -1.0f; secondSign < 2.0f; secondSign += 2.0f) { - glm::vec3 secondNormal = secondSign * faceNormals[1]; - for (float thirdSign = -1.0f; thirdSign < 2.0f; thirdSign += 2.0f) { - collisions.clear(); - glm::vec3 thirdNormal = thirdSign * faceNormals[2]; - - // the cornerNormal is the normalized sum of the three faces - glm::vec3 cornerNormal = glm::normalize(firstNormal + secondNormal + thirdNormal); - - // pick a random point somewhere above the corner - glm::vec3 startPoint = cubeCenter + (SQUARE_ROOT_OF_3 * cubeSide + capsuleRadius) * cornerNormal + - (0.25f * cubeSide * (randFloat() - 0.5f)) * firstNormal + - (0.25f * cubeSide * (randFloat() - 0.5f)) * secondNormal + - (0.25f * cubeSide * (randFloat() - 0.5f)) * thirdNormal; - - // pick a second random point slightly less than one radius above the corner - glm::vec3 endPoint = cubeCenter + (SQUARE_ROOT_OF_3 * 0.5f * cubeSide + capsuleRadius - overlap) * cornerNormal; - glm::vec3 collidingPoint = endPoint; - - // randomly swap the points so capsule axis may point toward or away from corner - if (randFloat() > 0.5f) { - glm::vec3 temp = startPoint; - startPoint = endPoint; - endPoint = temp; - } - - // create a capsule between the points - CapsuleShape capsule(capsuleRadius, startPoint, endPoint); - - // collide capsule with cube - QCOMPARE(ShapeCollider::capsuleVsAACube(&capsule, &cube, collisions), true); - - CollisionInfo* collision = collisions.getLastCollision(); - QCOMPARE(collision != nullptr, true); - - // penetration points from capsule into cube - glm::vec3 expectedPenetration = - overlap * cornerNormal; - QCOMPARE_WITH_ABS_ERROR(collision->_penetration, expectedPenetration, allowableError); - - // contactPoint is on surface of capsule - glm::vec3 expectedContactPoint = collidingPoint - capsuleRadius * cornerNormal; - QCOMPARE_WITH_ABS_ERROR(collision->_contactPoint, expectedContactPoint, allowableError); - } - } - } - - // capsule sides hit cube edges - // loop over each face... - float capsuleLength = 2.0f; - for (int i = 0; i < numDirections; ++i) { - for (float faceSign = -1.0f; faceSign < 2.0f; faceSign += 2.0f) { - glm::vec3 faceNormal = faceSign * faceNormals[i]; - - // loop over each neighboring face... - for (int j = (i + 1) % numDirections; j != i; j = (j + 1) % numDirections) { - // Compute the index to the third direction, which points perpendicular to both the face - // and the neighbor face. - int k = (j + 1) % numDirections; - if (k == i) { - k = (i + 1) % numDirections; - } - glm::vec3 thirdNormal = faceNormals[k]; - - collisions.clear(); - for (float neighborSign = -1.0f; neighborSign < 2.0f; neighborSign += 2.0f) { - glm::vec3 neighborNormal = neighborSign * faceNormals[j]; - - // combine the face and neighbor normals to get the edge normal - glm::vec3 edgeNormal = glm::normalize(faceNormal + neighborNormal); - - // pick a random point somewhere along the edge - glm::vec3 edgePoint = cubeCenter + (SQUARE_ROOT_OF_2 * 0.5f * cubeSide) * edgeNormal + - ((cubeSide - 2.0f * overlap) * (randFloat() - 0.5f)) * thirdNormal; - - // pick a random normal that is deflected slightly from edgeNormal - glm::vec3 deflectedNormal = glm::normalize(edgeNormal + - (0.1f * (randFloat() - 0.5f)) * faceNormal + - (0.1f * (randFloat() - 0.5f)) * neighborNormal); - - // compute the axis direction, which will be perp to deflectedNormal and thirdNormal - glm::vec3 axisDirection = glm::normalize(glm::cross(deflectedNormal, thirdNormal)); - - // compute a point for the capsule's axis along deflection normal away from edgePoint - glm::vec3 axisPoint = edgePoint + (capsuleRadius - overlap) * deflectedNormal; - - // now we can compute the capsule endpoints - glm::vec3 endPoint = axisPoint + (0.5f * capsuleLength * randFloat()) * axisDirection; - glm::vec3 startPoint = axisPoint - (0.5f * capsuleLength * randFloat()) * axisDirection; - - // create a capsule between the points - CapsuleShape capsule(capsuleRadius, startPoint, endPoint); - - // collide capsule with cube - QCOMPARE(ShapeCollider::capsuleVsAACube(&capsule, &cube, collisions), true); - - CollisionInfo* collision = collisions.getLastCollision(); - QCOMPARE(collision != nullptr, true); - - // penetration points from capsule into cube - glm::vec3 expectedPenetration = - overlap * deflectedNormal; - QCOMPARE_WITH_ABS_ERROR(collision->_penetration, expectedPenetration, allowableError); - - // contactPoint is on surface of capsule - glm::vec3 expectedContactPoint = axisPoint - capsuleRadius * deflectedNormal; - QCOMPARE_WITH_ABS_ERROR(collision->_contactPoint, expectedContactPoint, allowableError); } - } - } - } - - // capsule sides hit cube corners - for (float firstSign = -1.0f; firstSign < 2.0f; firstSign += 2.0f) { - glm::vec3 firstNormal = firstSign * faceNormals[0]; - for (float secondSign = -1.0f; secondSign < 2.0f; secondSign += 2.0f) { - glm::vec3 secondNormal = secondSign * faceNormals[1]; - for (float thirdSign = -1.0f; thirdSign < 2.0f; thirdSign += 2.0f) { - collisions.clear(); - glm::vec3 thirdNormal = thirdSign * faceNormals[2]; - - // the cornerNormal is the normalized sum of the three faces - glm::vec3 cornerNormal = glm::normalize(firstNormal + secondNormal + thirdNormal); - - // compute a penetration normal that is somewhat randomized about cornerNormal - glm::vec3 penetrationNormal = - glm::normalize(cornerNormal + - (0.05f * cubeSide * (randFloat() - 0.5f)) * firstNormal + - (0.05f * cubeSide * (randFloat() - 0.5f)) * secondNormal + - (0.05f * cubeSide * (randFloat() - 0.5f)) * thirdNormal); - - // pick a random point somewhere above the corner - glm::vec3 corner = cubeCenter + (0.5f * cubeSide) * (firstNormal + secondNormal + thirdNormal); - glm::vec3 startPoint = corner + (3.0f * cubeSide) * cornerNormal + - (0.25f * cubeSide * (randFloat() - 0.5f)) * firstNormal + - (0.25f * cubeSide * (randFloat() - 0.5f)) * secondNormal + - (0.25f * cubeSide * (randFloat() - 0.5f)) * thirdNormal; - - // pick a second random point slightly less than one radius above the corner - // with some sight perp motion - glm::vec3 endPoint = corner - (capsuleRadius - overlap) * penetrationNormal; - glm::vec3 collidingPoint = endPoint; - - // randomly swap the points so capsule axis may point toward or away from corner - if (randFloat() > 0.5f) { - glm::vec3 temp = startPoint; - startPoint = endPoint; - endPoint = temp; - } - - // create a capsule between the points - CapsuleShape capsule(capsuleRadius, startPoint, endPoint); - - // collide capsule with cube - QCOMPARE(ShapeCollider::capsuleVsAACube(&capsule, &cube, collisions), true); - - CollisionInfo* collision = collisions.getLastCollision(); - QCOMPARE(collision != nullptr, true); - - // penetration points from capsule into cube - glm::vec3 expectedPenetration = overlap * penetrationNormal; - QCOMPARE_WITH_ABS_ERROR(collision->_penetration, expectedPenetration, allowableError); - - // contactPoint is on surface of capsule - glm::vec3 expectedContactPoint = collidingPoint + capsuleRadius * penetrationNormal; - QCOMPARE_WITH_ABS_ERROR(collision->_contactPoint, expectedContactPoint, allowableError); - } - } - } - - // capsule sides hit cube faces - // these are the steps along the capsuleAxis where we'll put the capsule endpoints - float steps[] = { -1.0f, 2.0f, 0.25f, 0.75f, -1.0f }; - - for (int i = 0; i < numDirections; ++i) { - for (float sign = -1.0f; sign < 2.0f; sign += 2.0f) { - glm::vec3 faceNormal = sign * faceNormals[i]; - glm::vec3 secondNormal = faceNormals[(i + 1) % numDirections]; - glm::vec3 thirdNormal = faceNormals[(i + 2) % numDirections]; - - // pick two random point on opposite edges of the face - glm::vec3 firstEdgeIntersection = cubeCenter + (0.5f * cubeSide) * (faceNormal + secondNormal) + - (cubeSide * (randFloat() - 0.5f)) * thirdNormal; - glm::vec3 secondEdgeIntersection = cubeCenter + (0.5f * cubeSide) * (faceNormal - secondNormal) + - (cubeSide * (randFloat() - 0.5f)) * thirdNormal; - - // compute the un-normalized axis for the capsule - glm::vec3 capsuleAxis = secondEdgeIntersection - firstEdgeIntersection; - // there are three pairs in steps[] - for (int j = 0; j < 4; j++) { - collisions.clear(); - glm::vec3 startPoint = firstEdgeIntersection + steps[j] * capsuleAxis + (capsuleRadius - overlap) * faceNormal; - glm::vec3 endPoint = firstEdgeIntersection + steps[j + 1] * capsuleAxis + (capsuleRadius - overlap) * faceNormal; - - // create a capsule between the points - CapsuleShape capsule(capsuleRadius, startPoint, endPoint); - - // collide capsule with cube - QCOMPARE(ShapeCollider::capsuleVsAACube(&capsule, &cube, collisions), true); - QCOMPARE(collisions.size(), 2); - - // compute the expected contact points - // NOTE: whether the startPoint or endPoint are expected to collide depends the relative values - // of the steps[] that were used to compute them above. - glm::vec3 expectedContactPoints[2]; - if (j == 0) { - expectedContactPoints[0] = firstEdgeIntersection - overlap * faceNormal; - expectedContactPoints[1] = secondEdgeIntersection - overlap * faceNormal; - } else if (j == 1) { - expectedContactPoints[0] = secondEdgeIntersection - overlap * faceNormal; - expectedContactPoints[1] = endPoint - capsuleRadius * faceNormal; - } else if (j == 2) { - expectedContactPoints[0] = startPoint - capsuleRadius * faceNormal; - expectedContactPoints[1] = endPoint - capsuleRadius * faceNormal; - } else if (j == 3) { - expectedContactPoints[0] = startPoint - capsuleRadius * faceNormal; - expectedContactPoints[1] = firstEdgeIntersection - overlap * faceNormal; - } - - // verify each contact - for (int k = 0; k < 2; ++k) { - CollisionInfo* collision = collisions.getCollision(k); - // penetration points from capsule into cube - glm::vec3 expectedPenetration = - overlap * faceNormal; - QCOMPARE_WITH_ABS_ERROR(collision->_penetration, expectedPenetration, allowableError); - - // the order of the final contact points is undefined, so we - // figure out which expected contact point is the closest to the real one - // and then verify accuracy on that - float length0 = glm::length(collision->_contactPoint - expectedContactPoints[0]); - float length1 = glm::length(collision->_contactPoint - expectedContactPoints[1]); - glm::vec3 expectedContactPoint = (length0 < length1) ? expectedContactPoints[0] : expectedContactPoints[1]; - // contactPoint is on surface of capsule - QCOMPARE_WITH_ABS_ERROR(collision->_contactPoint, expectedContactPoint, allowableError); - } - } - } - } -} - - -void ShapeColliderTests::rayHitsSphere() { - float startDistance = 3.0f; - - float radius = 1.0f; - glm::vec3 center(0.0f); - SphereShape sphere(radius, center); - - // very simple ray along xAxis - { - RayIntersectionInfo intersection; - intersection._rayStart = -startDistance * xAxis; - intersection._rayDirection = xAxis; - - QCOMPARE(sphere.findRayIntersection(intersection), true); - - float expectedDistance = startDistance - radius; - QCOMPARE_WITH_ABS_ERROR(intersection._hitDistance, expectedDistance, startDistance * EPSILON); - QCOMPARE(intersection._hitShape, &sphere); - } - - // ray along a diagonal axis - { - RayIntersectionInfo intersection; - intersection._rayStart = glm::vec3(startDistance, startDistance, 0.0f); - intersection._rayDirection = - glm::normalize(intersection._rayStart); - QCOMPARE(sphere.findRayIntersection(intersection), true); - - float expectedDistance = SQUARE_ROOT_OF_2 * startDistance - radius; - QCOMPARE_WITH_ABS_ERROR(intersection._hitDistance, expectedDistance, startDistance * EPSILON); - } - - // rotated and displaced ray and sphere - { - startDistance = 7.41f; - radius = 3.917f; - - glm::vec3 axis = glm::normalize(glm::vec3(1.0f, 2.0f, 3.0f)); - glm::quat rotation = glm::angleAxis(0.987654321f, axis); - glm::vec3 translation(35.7f, 2.46f, -1.97f); - - glm::vec3 unrotatedRayDirection = -xAxis; - glm::vec3 untransformedRayStart = startDistance * xAxis; - - RayIntersectionInfo intersection; - intersection._rayStart = rotation * (untransformedRayStart + translation); - intersection._rayDirection = rotation * unrotatedRayDirection; - - sphere.setRadius(radius); - sphere.setTranslation(rotation * translation); - - QCOMPARE(sphere.findRayIntersection(intersection), true); - float expectedDistance = startDistance - radius; - QCOMPARE_WITH_ABS_ERROR(intersection._hitDistance, expectedDistance, startDistance * EPSILON); - } -} - -void ShapeColliderTests::rayBarelyHitsSphere() { - float radius = 1.0f; - glm::vec3 center(0.0f); - float delta = 2.0f * EPSILON; - - SphereShape sphere(radius, center); - float startDistance = 3.0f; - - { - RayIntersectionInfo intersection; - intersection._rayStart = glm::vec3(-startDistance, radius - delta, 0.0f); - intersection._rayDirection = xAxis; - - // very simple ray along xAxis - QCOMPARE(sphere.findRayIntersection(intersection), true); - QCOMPARE(intersection._hitShape, &sphere); - } - - { - // translate and rotate the whole system... - glm::vec3 axis = glm::normalize(glm::vec3(1.0f, 2.0f, 3.0f)); - glm::quat rotation = glm::angleAxis(0.987654321f, axis); - glm::vec3 translation(35.7f, 0.46f, -1.97f); - - RayIntersectionInfo intersection; - intersection._rayStart = rotation * (intersection._rayStart + translation); - intersection._rayDirection = rotation * intersection._rayDirection; - - sphere.setTranslation(rotation * translation); - - // ...and test again - QCOMPARE(sphere.findRayIntersection(intersection), true); - } -} - - -void ShapeColliderTests::rayBarelyMissesSphere() { - // same as the barely-hits case, but this time we move the ray away from sphere - float radius = 1.0f; - glm::vec3 center(0.0f); - float delta = 2.0f * EPSILON; - - SphereShape sphere(radius, center); - float startDistance = 3.0f; - - { - RayIntersectionInfo intersection; - intersection._rayStart = glm::vec3(-startDistance, radius + delta, 0.0f); - intersection._rayDirection = xAxis; - - // very simple ray along xAxis - QCOMPARE(sphere.findRayIntersection(intersection), false); - QCOMPARE(intersection._hitDistance, FLT_MAX); - } - - { - // translate and rotate the whole system... - float angle = 0.987654321f; - glm::vec3 axis = glm::normalize(glm::vec3(1.0f, 2.0f, 3.0f)); - glm::quat rotation = glm::angleAxis(angle, axis); - glm::vec3 translation(35.7f, 2.46f, -1.97f); - - RayIntersectionInfo intersection; - intersection._rayStart = rotation * (glm::vec3(-startDistance, radius + delta, 0.0f) + translation); - intersection._rayDirection = rotation * xAxis; - sphere.setTranslation(rotation * translation); - - // ...and test again - QCOMPARE(sphere.findRayIntersection(intersection), false); - QCOMPARE(intersection._hitDistance == FLT_MAX, true); - QCOMPARE(intersection._hitShape == nullptr, true); - } -} - -void ShapeColliderTests::rayHitsCapsule() { - float startDistance = 3.0f; - float radius = 1.0f; - float halfHeight = 2.0f; - glm::vec3 center(0.0f); - CapsuleShape capsule(radius, halfHeight); - - // simple tests along xAxis - { // toward capsule center - RayIntersectionInfo intersection; - intersection._rayStart = glm::vec3(startDistance, 0.0f, 0.0f); - intersection._rayDirection = - xAxis; - QCOMPARE(capsule.findRayIntersection(intersection), true); - float expectedDistance = startDistance - radius; - QCOMPARE_WITH_ABS_ERROR(intersection._hitDistance, expectedDistance, startDistance * EPSILON); - QCOMPARE(intersection._hitShape, &capsule); - } - - { // toward top of cylindrical wall - RayIntersectionInfo intersection; - intersection._rayStart = glm::vec3(startDistance, halfHeight, 0.0f); - intersection._rayDirection = - xAxis; - QCOMPARE(capsule.findRayIntersection(intersection), true); - float expectedDistance = startDistance - radius; - QCOMPARE_WITH_ABS_ERROR(intersection._hitDistance, expectedDistance, startDistance * EPSILON); - } - - float delta = 2.0f * EPSILON; - { // toward top cap - RayIntersectionInfo intersection; - intersection._rayStart = glm::vec3(startDistance, halfHeight + delta, 0.0f); - intersection._rayDirection = - xAxis; - QCOMPARE(capsule.findRayIntersection(intersection), true); - float expectedDistance = startDistance - radius; - QCOMPARE_WITH_ABS_ERROR(intersection._hitDistance, expectedDistance, startDistance * EPSILON); - } - - const float EDGE_CASE_SLOP_FACTOR = 20.0f; - { // toward tip of top cap - RayIntersectionInfo intersection; - intersection._rayStart = glm::vec3(startDistance, halfHeight + radius - delta, 0.0f); - intersection._rayDirection = - xAxis; - QCOMPARE(capsule.findRayIntersection(intersection), true); - float expectedDistance = startDistance - radius * sqrtf(2.0f * delta); // using small angle approximation of cosine -// float relativeError = fabsf(intersection._hitDistance - expectedDistance) / startDistance; - // for edge cases we allow a LOT of error - QCOMPARE_WITH_ABS_ERROR(intersection._hitDistance, expectedDistance, startDistance * EDGE_CASE_SLOP_FACTOR * EPSILON); - } - - { // toward tip of bottom cap - RayIntersectionInfo intersection; - intersection._rayStart = glm::vec3(startDistance, - halfHeight - radius + delta, 0.0f); - intersection._rayDirection = - xAxis; - QCOMPARE(capsule.findRayIntersection(intersection), true); - float expectedDistance = startDistance - radius * sqrtf(2.0f * delta); // using small angle approximation of cosine -// float relativeError = fabsf(intersection._hitDistance - expectedDistance) / startDistance; - // for edge cases we allow a LOT of error - QCOMPARE_WITH_ABS_ERROR(intersection._hitDistance, expectedDistance, startDistance * EPSILON * EDGE_CASE_SLOP_FACTOR); - } - - { // toward edge of capsule cylindrical face - RayIntersectionInfo intersection; - intersection._rayStart = glm::vec3(startDistance, 0.0f, radius - delta); - intersection._rayDirection = - xAxis; - QCOMPARE(capsule.findRayIntersection(intersection), true); - float expectedDistance = startDistance - radius * sqrtf(2.0f * delta); // using small angle approximation of cosine - float relativeError = fabsf(intersection._hitDistance - expectedDistance) / startDistance; - // for edge cases we allow a LOT of error - QCOMPARE_WITH_ABS_ERROR(intersection._hitDistance, expectedDistance, startDistance * EPSILON * EDGE_CASE_SLOP_FACTOR); - } - // TODO: test at steep angles near cylinder/cap junction -} - -void ShapeColliderTests::rayMissesCapsule() { - // same as edge case hit tests, but shifted in the opposite direction - float startDistance = 3.0f; - float radius = 1.0f; - float halfHeight = 2.0f; - glm::vec3 center(0.0f); - CapsuleShape capsule(radius, halfHeight); - - { // simple test along xAxis - // toward capsule center - RayIntersectionInfo intersection; - intersection._rayStart = glm::vec3(startDistance, 0.0f, 0.0f); - intersection._rayDirection = -xAxis; - float delta = 2.0f * EPSILON; - - // over top cap - intersection._rayStart.y = halfHeight + radius + delta; - intersection._hitDistance = FLT_MAX; - QCOMPARE(capsule.findRayIntersection(intersection), false); - QCOMPARE(intersection._hitDistance, FLT_MAX); - - // below bottom cap - intersection._rayStart.y = - halfHeight - radius - delta; - intersection._hitDistance = FLT_MAX; - QCOMPARE(capsule.findRayIntersection(intersection), false); - QCOMPARE(intersection._hitDistance, FLT_MAX); - - // past edge of capsule cylindrical face - intersection._rayStart.y = 0.0f; - intersection._rayStart.z = radius + delta; - intersection._hitDistance = FLT_MAX; - QCOMPARE(capsule.findRayIntersection(intersection), false); - QCOMPARE(intersection._hitDistance, FLT_MAX); - QCOMPARE(intersection._hitShape, (Shape*)nullptr); - } - // TODO: test at steep angles near edge -} - -void ShapeColliderTests::rayHitsPlane() { - // make a simple plane - float planeDistanceFromOrigin = 3.579f; - glm::vec3 planePosition(0.0f, planeDistanceFromOrigin, 0.0f); - PlaneShape plane; - plane.setPoint(planePosition); - plane.setNormal(yAxis); - - // make a simple ray - float startDistance = 1.234f; - { - RayIntersectionInfo intersection; - intersection._rayStart = -startDistance * xAxis; - intersection._rayDirection = glm::normalize(glm::vec3(1.0f, 1.0f, 1.0f)); - - QCOMPARE(plane.findRayIntersection(intersection), true); - float expectedDistance = SQUARE_ROOT_OF_3 * planeDistanceFromOrigin; - - QCOMPARE_WITH_ABS_ERROR(intersection._hitDistance, expectedDistance, planeDistanceFromOrigin * EPSILON); - QCOMPARE(intersection._hitShape, &plane); - } - - { // rotate the whole system and try again - float angle = 37.8f; - glm::vec3 axis = glm::normalize( glm::vec3(-7.0f, 2.8f, 9.3f) ); - glm::quat rotation = glm::angleAxis(angle, axis); - - plane.setNormal(rotation * yAxis); - plane.setPoint(rotation * planePosition); - RayIntersectionInfo intersection; - intersection._rayStart = rotation * (-startDistance * xAxis); - intersection._rayDirection = rotation * glm::normalize(glm::vec3(1.0f, 1.0f, 1.0f)); - - QCOMPARE(plane.findRayIntersection(intersection), true); - - float expectedDistance = SQUARE_ROOT_OF_3 * planeDistanceFromOrigin; - QCOMPARE_WITH_ABS_ERROR(intersection._hitDistance, expectedDistance, planeDistanceFromOrigin * EPSILON); - } -} - -void ShapeColliderTests::rayMissesPlane() { - // make a simple plane - float planeDistanceFromOrigin = 3.579f; - glm::vec3 planePosition(0.0f, planeDistanceFromOrigin, 0.0f); - PlaneShape plane; - plane.setTranslation(planePosition); - - { // parallel rays should miss - float startDistance = 1.234f; - RayIntersectionInfo intersection; - intersection._rayStart = glm::vec3(-startDistance, 0.0f, 0.0f); - intersection._rayDirection = glm::normalize(glm::vec3(-1.0f, 0.0f, -1.0f)); - - QCOMPARE(plane.findRayIntersection(intersection), false); - QCOMPARE(intersection._hitDistance, FLT_MAX); - - // rotate the whole system and try again - float angle = 37.8f; - glm::vec3 axis = glm::normalize( glm::vec3(-7.0f, 2.8f, 9.3f) ); - glm::quat rotation = glm::angleAxis(angle, axis); - - plane.setTranslation(rotation * planePosition); - plane.setRotation(rotation); - - intersection._rayStart = rotation * intersection._rayStart; - intersection._rayDirection = rotation * intersection._rayDirection; - intersection._hitDistance = FLT_MAX; - - QCOMPARE(plane.findRayIntersection(intersection), false); - QCOMPARE(intersection._hitDistance, FLT_MAX); - QCOMPARE(intersection._hitShape, (Shape*)nullptr); - } - - { // make a simple ray that points away from plane - float startDistance = 1.234f; - - RayIntersectionInfo intersection; - intersection._rayStart = glm::vec3(-startDistance, 0.0f, 0.0f); - intersection._rayDirection = glm::normalize(glm::vec3(-1.0f, -1.0f, -1.0f)); - intersection._hitDistance = FLT_MAX; - - QCOMPARE(plane.findRayIntersection(intersection), false); - QCOMPARE(intersection._hitDistance, FLT_MAX); - - // rotate the whole system and try again - float angle = 37.8f; - glm::vec3 axis = glm::normalize( glm::vec3(-7.0f, 2.8f, 9.3f) ); - glm::quat rotation = glm::angleAxis(angle, axis); - - plane.setTranslation(rotation * planePosition); - plane.setRotation(rotation); - - intersection._rayStart = rotation * intersection._rayStart; - intersection._rayDirection = rotation * intersection._rayDirection; - intersection._hitDistance = FLT_MAX; - - QCOMPARE(plane.findRayIntersection(intersection), false); - QCOMPARE(intersection._hitDistance, FLT_MAX); - } -} - -void ShapeColliderTests::rayHitsAACube() { - glm::vec3 cubeCenter(1.23f, 4.56f, 7.89f); - float cubeSide = 2.127f; - AACubeShape cube(cubeSide, cubeCenter); - - float rayOffset = 3.796f; - - glm::vec3 faceNormals[] = {xAxis, yAxis, zAxis}; - int numDirections = 3; - int numRayCasts = 5; - - for (int i = 0; i < numDirections; ++i) { - for (float sign = -1.0f; sign < 2.0f; sign += 2.0f) { - glm::vec3 faceNormal = sign * faceNormals[i]; - glm::vec3 secondNormal = faceNormals[(i + 1) % numDirections]; - glm::vec3 thirdNormal = faceNormals[(i + 2) % numDirections]; - - // pick a random point somewhere above the face - glm::vec3 rayStart = cubeCenter + - (cubeSide + rayOffset) * faceNormal + - (cubeSide * (randFloat() - 0.5f)) * secondNormal + - (cubeSide * (randFloat() - 0.5f)) * thirdNormal; - - // cast multiple rays toward the face - for (int j = 0; j < numRayCasts; ++j) { - // pick a random point on the face - glm::vec3 facePoint = cubeCenter + - 0.5f * cubeSide * faceNormal + - (cubeSide * (randFloat() - 0.5f)) * secondNormal + - (cubeSide * (randFloat() - 0.5f)) * thirdNormal; - - // construct a ray from first point through second point - RayIntersectionInfo intersection; - intersection._rayStart = rayStart; - intersection._rayDirection = glm::normalize(facePoint - rayStart); - intersection._rayLength = 1.0001f * glm::distance(rayStart, facePoint); - - // cast the ray -// bool hit = cube.findRayIntersection(intersection); - - // validate - QCOMPARE(cube.findRayIntersection(intersection), true); - QCOMPARE_WITH_ABS_ERROR(glm::dot(faceNormal, intersection._hitNormal), 1.0f, EPSILON); - QCOMPARE_WITH_ABS_ERROR(facePoint, intersection.getIntersectionPoint(), EPSILON); - QCOMPARE(intersection._hitShape, &cube); - } - } - } -} - -void ShapeColliderTests::rayMissesAACube() { - //glm::vec3 cubeCenter(1.23f, 4.56f, 7.89f); - //float cubeSide = 2.127f; - glm::vec3 cubeCenter(0.0f); - float cubeSide = 2.0f; - AACubeShape cube(cubeSide, cubeCenter); - - float rayOffset = 3.796f; - - glm::vec3 faceNormals[] = {xAxis, yAxis, zAxis}; - int numDirections = 3; - int numRayCasts = 5; - - const float SOME_SMALL_NUMBER = 0.0001f; - - { // ray misses cube for being too short - for (int i = 0; i < numDirections; ++i) { - for (float sign = -1.0f; sign < 2.0f; sign += 2.0f) { - glm::vec3 faceNormal = sign * faceNormals[i]; - glm::vec3 secondNormal = faceNormals[(i + 1) % numDirections]; - glm::vec3 thirdNormal = faceNormals[(i + 2) % numDirections]; - - // pick a random point somewhere above the face - glm::vec3 rayStart = cubeCenter + - (cubeSide + rayOffset) * faceNormal + - (cubeSide * (randFloat() - 0.5f)) * secondNormal + - (cubeSide * (randFloat() - 0.5f)) * thirdNormal; - - // cast multiple rays toward the face - for (int j = 0; j < numRayCasts; ++j) { - // pick a random point on the face - glm::vec3 facePoint = cubeCenter + - 0.5f * cubeSide * faceNormal + - (cubeSide * (randFloat() - 0.5f)) * secondNormal + - (cubeSide * (randFloat() - 0.5f)) * thirdNormal; - - // construct a ray from first point to almost second point - RayIntersectionInfo intersection; - intersection._rayStart = rayStart; - intersection._rayDirection = glm::normalize(facePoint - rayStart); - intersection._rayLength = (1.0f - SOME_SMALL_NUMBER) * glm::distance(rayStart, facePoint); - - // cast the ray - QCOMPARE(cube.findRayIntersection(intersection), false); - } - } - } - } - { // long ray misses cube - for (int i = 0; i < numDirections; ++i) { - for (float sign = -1.0f; sign < 2.0f; sign += 2.0f) { - glm::vec3 faceNormal = sign * faceNormals[i]; - glm::vec3 secondNormal = faceNormals[(i + 1) % numDirections]; - glm::vec3 thirdNormal = faceNormals[(i + 2) % numDirections]; - - // pick a random point somewhere above the face - glm::vec3 rayStart = cubeCenter + - (cubeSide + rayOffset) * faceNormal + - (cubeSide * (randFloat() - 0.5f)) * secondNormal + - (cubeSide * (randFloat() - 0.5f)) * thirdNormal; - - // cast multiple rays that miss the face - for (int j = 0; j < numRayCasts; ++j) { - // pick a random point just outside of face - float inside = (cubeSide * (randFloat() - 0.5f)); - float outside = 0.5f * cubeSide + SOME_SMALL_NUMBER * randFloat(); - if (randFloat() - 0.5f < 0.0f) { - outside *= -1.0f; - } - glm::vec3 sidePoint = cubeCenter + 0.5f * cubeSide * faceNormal; - if (randFloat() - 0.5f < 0.0f) { - sidePoint += outside * secondNormal + inside * thirdNormal; - } else { - sidePoint += inside * secondNormal + outside * thirdNormal; - } - - // construct a ray from first point through second point - RayIntersectionInfo intersection; - intersection._rayStart = rayStart; - intersection._rayDirection = glm::normalize(sidePoint - rayStart); - - // cast the ray - QCOMPARE(cube.findRayIntersection(intersection), false); - } - } - } - } - { // ray parallel to face barely misses cube - for (int i = 0; i < numDirections; ++i) { - for (float sign = -1.0f; sign < 2.0f; sign += 2.0f) { - glm::vec3 faceNormal = sign * faceNormals[i]; - glm::vec3 secondNormal = faceNormals[(i + 1) % numDirections]; - glm::vec3 thirdNormal = faceNormals[(i + 2) % numDirections]; - - // cast multiple rays that miss the face - for (int j = 0; j < numRayCasts; ++j) { - // rayStart is above the face - glm::vec3 rayStart = cubeCenter + (0.5f + SOME_SMALL_NUMBER) * cubeSide * faceNormal; - - // move rayStart to some random edge and choose the ray direction to point across the face - float inside = (cubeSide * (randFloat() - 0.5f)); - float outside = 0.5f * cubeSide + SOME_SMALL_NUMBER * randFloat(); - if (randFloat() - 0.5f < 0.0f) { - outside *= -1.0f; - } - glm::vec3 rayDirection = secondNormal; - if (randFloat() - 0.5f < 0.0f) { - rayStart += outside * secondNormal + inside * thirdNormal; - } else { - rayStart += inside * secondNormal + outside * thirdNormal; - rayDirection = thirdNormal; - } - if (outside > 0.0f) { - rayDirection *= -1.0f; - } - - // construct a ray from first point through second point - RayIntersectionInfo intersection; - intersection._rayStart = rayStart; - intersection._rayDirection = rayDirection; - - // cast the ray - QCOMPARE(cube.findRayIntersection(intersection), false); - } - } - } - } - -} - -void ShapeColliderTests::measureTimeOfCollisionDispatch() { - - // TODO: use QBENCHMARK for this - - /* KEEP for future manual testing - // create two non-colliding spheres - float radiusA = 7.0f; - float radiusB = 3.0f; - float alpha = 1.2f; - float beta = 1.3f; - glm::vec3 offsetDirection = glm::normalize(glm::vec3(1.0f, 2.0f, 3.0f)); - float offsetDistance = alpha * radiusA + beta * radiusB; - - SphereShape sphereA(radiusA, origin); - SphereShape sphereB(radiusB, offsetDistance * offsetDirection); - CollisionList collisions(16); - - //int numTests = 1; - quint64 oldTime; - quint64 newTime; - int numTests = 100000000; - { - quint64 startTime = usecTimestampNow(); - for (int i = 0; i < numTests; ++i) { - ShapeCollider::collideShapes(&sphereA, &sphereB, collisions); - } - quint64 endTime = usecTimestampNow(); - std::cout << numTests << " non-colliding collisions in " << (endTime - startTime) << " usec" << std::endl; - newTime = endTime - startTime; - } - */ -} - diff --git a/tests/physics/src/ShapeColliderTests.h b/tests/physics/src/ShapeColliderTests.h deleted file mode 100644 index 48d9cbd742..0000000000 --- a/tests/physics/src/ShapeColliderTests.h +++ /dev/null @@ -1,60 +0,0 @@ -// -// ShapeColliderTests.h -// tests/physics/src -// -// Created by Andrew Meadows on 02/21/2014. -// 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_ShapeColliderTests_h -#define hifi_ShapeColliderTests_h - -#include -#include - -// Add additional qtest functionality (the include order is important!) -#include "BulletTestUtils.h" -#include "GlmTestUtils.h" -#include "../QTestExtensions.h" - - -class ShapeColliderTests : public QObject { - Q_OBJECT - -private slots: - void initTestCase(); - - void sphereMissesSphere(); - void sphereTouchesSphere(); - - void sphereMissesCapsule(); - void sphereTouchesCapsule(); - - void capsuleMissesCapsule(); - void capsuleTouchesCapsule(); - - void sphereTouchesAACubeFaces(); - void sphereTouchesAACubeEdges(); - void sphereTouchesAACubeCorners(); - void sphereMissesAACube(); - - void capsuleMissesAACube(); - void capsuleTouchesAACube(); - - void rayHitsSphere(); - void rayBarelyHitsSphere(); - void rayBarelyMissesSphere(); - void rayHitsCapsule(); - void rayMissesCapsule(); - void rayHitsPlane(); - void rayMissesPlane(); - void rayHitsAACube(); - void rayMissesAACube(); - - void measureTimeOfCollisionDispatch(); -}; - -#endif // hifi_ShapeColliderTests_h From 76acbde5955c1cd3f33d8e7228e19ae8899b6f34 Mon Sep 17 00:00:00 2001 From: Sam Gateau Date: Wed, 29 Jul 2015 09:54:10 -0700 Subject: [PATCH 10/65] Removing the opengl and glew links from Interface since now coming from gpu lib --- interface/CMakeLists.txt | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index 6af3cfa08b..4f99b6b34f 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -219,21 +219,22 @@ else (APPLE) $/resources ) - find_package(OpenGL REQUIRED) + # find_package(OpenGL REQUIRED) - if (${OPENGL_INCLUDE_DIR}) - include_directories(SYSTEM "${OPENGL_INCLUDE_DIR}") - endif () + # if (${OPENGL_INCLUDE_DIR}) + # include_directories(SYSTEM "${OPENGL_INCLUDE_DIR}") +# endif () - target_link_libraries(${TARGET_NAME} "${OPENGL_LIBRARY}") + # target_link_libraries(${TARGET_NAME} "${OPENGL_LIBRARY}") # link target to external libraries if (WIN32) - add_dependency_external_projects(glew) - find_package(GLEW REQUIRED) - target_include_directories(${TARGET_NAME} PRIVATE ${GLEW_INCLUDE_DIRS}) + # add_dependency_external_projects(glew) + # find_package(GLEW REQUIRED) + # target_include_directories(${TARGET_NAME} PRIVATE ${GLEW_INCLUDE_DIRS}) - target_link_libraries(${TARGET_NAME} ${GLEW_LIBRARIES} wsock32.lib opengl32.lib Winmm.lib) + # target_link_libraries(${TARGET_NAME} ${GLEW_LIBRARIES} wsock32.lib opengl32.lib Winmm.lib) + target_link_libraries(${TARGET_NAME} wsock32.lib Winmm.lib) if (USE_NSIGHT) # try to find the Nsight package and add it to the build if we find it From 17650dde74598fb1cdff9df8979d7f8db096546c Mon Sep 17 00:00:00 2001 From: Sam Gateau Date: Wed, 29 Jul 2015 11:49:22 -0700 Subject: [PATCH 11/65] Solving the coimpiling issue with Render-utils test --- tests/render-utils/src/main.cpp | 27 +++++++++------------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/tests/render-utils/src/main.cpp b/tests/render-utils/src/main.cpp index ab01d333ac..7c02c7e8a7 100644 --- a/tests/render-utils/src/main.cpp +++ b/tests/render-utils/src/main.cpp @@ -19,6 +19,10 @@ #include #include #include + +#include +#include + #include #include #include @@ -29,7 +33,7 @@ #include #include #include -#include + #include @@ -90,6 +94,7 @@ class QTestWindow : public QWindow { QSize _size; //TextRenderer* _textRenderer[4]; RateCounter fps; + gpu::ContextPointer _gpuContext; protected: void renderText(); @@ -119,6 +124,9 @@ public: show(); makeCurrent(); + _gpuContext.reset(new gpu::Context(new gpu::GLBackend())); + + { QOpenGLDebugLogger* logger = new QOpenGLDebugLogger(this); @@ -131,23 +139,6 @@ public: } qDebug() << (const char*)glGetString(GL_VERSION); -#ifdef WIN32 - glewExperimental = true; - GLenum err = glewInit(); - if (GLEW_OK != err) { - /* Problem: glewInit failed, something is seriously wrong. */ - const GLubyte * errStr = glewGetErrorString(err); - qDebug("Error: %s\n", errStr); - } - qDebug("Status: Using GLEW %s\n", glewGetString(GLEW_VERSION)); - - if (wglewGetExtension("WGL_EXT_swap_control")) { - int swapInterval = wglGetSwapIntervalEXT(); - qDebug("V-Sync is %s\n", (swapInterval > 0 ? "ON" : "OFF")); - } - glGetError(); -#endif - //_textRenderer[0] = TextRenderer::getInstance(SANS_FONT_FAMILY, 12, false); //_textRenderer[1] = TextRenderer::getInstance(SERIF_FONT_FAMILY, 12, false, // TextRenderer::SHADOW_EFFECT); From 661f29924fd79f6be76468ff5f6376b8153db557 Mon Sep 17 00:00:00 2001 From: Sam Gateau Date: Wed, 29 Jul 2015 13:55:26 -0700 Subject: [PATCH 12/65] Clean up the cmakelist to normally onlly do th eminimal linking and include for gl --- interface/CMakeLists.txt | 29 ++++------------------------- libraries/gpu/CMakeLists.txt | 2 +- 2 files changed, 5 insertions(+), 26 deletions(-) diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index 4f99b6b34f..b6c8e3f3f4 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -218,40 +218,19 @@ else (APPLE) "${PROJECT_SOURCE_DIR}/resources" $/resources ) - - # find_package(OpenGL REQUIRED) - - # if (${OPENGL_INCLUDE_DIR}) - # include_directories(SYSTEM "${OPENGL_INCLUDE_DIR}") -# endif () - - # target_link_libraries(${TARGET_NAME} "${OPENGL_LIBRARY}") - + # link target to external libraries if (WIN32) - # add_dependency_external_projects(glew) - # find_package(GLEW REQUIRED) - # target_include_directories(${TARGET_NAME} PRIVATE ${GLEW_INCLUDE_DIRS}) - - # target_link_libraries(${TARGET_NAME} ${GLEW_LIBRARIES} wsock32.lib opengl32.lib Winmm.lib) target_link_libraries(${TARGET_NAME} wsock32.lib Winmm.lib) - + if (USE_NSIGHT) - # try to find the Nsight package and add it to the build if we find it - find_package(NSIGHT) + # If required NSIGHT lib comes from gpu lib but still to add a precroc define if (NSIGHT_FOUND) - include_directories(${NSIGHT_INCLUDE_DIRS}) add_definitions(-DNSIGHT_FOUND) - target_link_libraries(${TARGET_NAME} "${NSIGHT_LIBRARIES}") endif () endif() - else (WIN32) - find_package(GLEW REQUIRED) - target_include_directories(${TARGET_NAME} PRIVATE ${GLEW_INCLUDE_DIRS}) - - target_link_libraries(${TARGET_NAME} ${GLEW_LIBRARIES}) - message(STATUS ${GLEW_LIBRARY}) + # Nothing else required on linux apparently endif() endif (APPLE) diff --git a/libraries/gpu/CMakeLists.txt b/libraries/gpu/CMakeLists.txt index eda168091e..1e8faf1bca 100644 --- a/libraries/gpu/CMakeLists.txt +++ b/libraries/gpu/CMakeLists.txt @@ -23,7 +23,7 @@ elseif (WIN32) # try to find the Nsight package and add it to the build if we find it find_package(NSIGHT) if (NSIGHT_FOUND) - include_directories(${NSIGHT_INCLUDE_DIRS}) + target_include_directories(${TARGET_NAME} PUBLIC ${NSIGHT_INCLUDE_DIRS}) add_definitions(-DNSIGHT_FOUND) target_link_libraries(${TARGET_NAME} "${NSIGHT_LIBRARIES}") endif () From 9601e09ba9301005057a608893e82e23f7734427 Mon Sep 17 00:00:00 2001 From: Sam Gateau Date: Wed, 29 Jul 2015 14:42:24 -0700 Subject: [PATCH 13/65] A simpler way to add the NSIGHT_FOUND define to all the projects depending on GPU --- interface/CMakeLists.txt | 8 +------- libraries/gpu/CMakeLists.txt | 8 ++++---- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index d47395a810..f1ef38ade9 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -221,14 +221,8 @@ else (APPLE) # link target to external libraries if (WIN32) + # target_link_libraries(${TARGET_NAME} wsock32.lib Winmm.lib) target_link_libraries(${TARGET_NAME} wsock32.lib Winmm.lib) - - if (USE_NSIGHT) - # If required NSIGHT lib comes from gpu lib but still to add a precroc define - if (NSIGHT_FOUND) - add_definitions(-DNSIGHT_FOUND) - endif () - endif() else (WIN32) # Nothing else required on linux apparently endif() diff --git a/libraries/gpu/CMakeLists.txt b/libraries/gpu/CMakeLists.txt index 1e8faf1bca..89939535b0 100644 --- a/libraries/gpu/CMakeLists.txt +++ b/libraries/gpu/CMakeLists.txt @@ -21,10 +21,11 @@ elseif (WIN32) if (USE_NSIGHT) # try to find the Nsight package and add it to the build if we find it + # note that this will also enable NSIGHT profilers in all the projects linking gpu find_package(NSIGHT) if (NSIGHT_FOUND) target_include_directories(${TARGET_NAME} PUBLIC ${NSIGHT_INCLUDE_DIRS}) - add_definitions(-DNSIGHT_FOUND) + target_compile_definitions(${TARGET_NAME} PUBLIC NSIGHT_FOUND) target_link_libraries(${TARGET_NAME} "${NSIGHT_LIBRARIES}") endif () endif() @@ -39,8 +40,7 @@ else () if (${OPENGL_INCLUDE_DIR}) include_directories(SYSTEM "${OPENGL_INCLUDE_DIR}") endif () - + target_link_libraries(${TARGET_NAME} "${GLEW_LIBRARIES}" "${OPENGL_LIBRARY}") - - # target_include_directories(${TARGET_NAME} PUBLIC ${OPENGL_INCLUDE_DIR}) + endif (APPLE) From 3c934af297617fcceb90b5b70366c673be5dc768 Mon Sep 17 00:00:00 2001 From: Sam Gateau Date: Wed, 29 Jul 2015 14:45:48 -0700 Subject: [PATCH 14/65] clean the gpuCOnfig.h for linux --- libraries/gpu/src/gpu/GPUConfig.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/libraries/gpu/src/gpu/GPUConfig.h b/libraries/gpu/src/gpu/GPUConfig.h index d9b6d18894..5590c4cc8d 100644 --- a/libraries/gpu/src/gpu/GPUConfig.h +++ b/libraries/gpu/src/gpu/GPUConfig.h @@ -36,9 +36,6 @@ #else #include -//#include -//#include -//#include #define GPU_FEATURE_PROFILE GPU_CORE #define GPU_TRANSFORM_PROFILE GPU_CORE From 269db0ff6fc71aeca8179e173ad3e9e042f1da6c Mon Sep 17 00:00:00 2001 From: Sam Gateau Date: Wed, 29 Jul 2015 16:08:16 -0700 Subject: [PATCH 15/65] fixing the stars rendering that was vilently broken durign the hunt for GPUCOnfig.h includes --- interface/src/Stars.cpp | 6 +----- libraries/gpu/src/gpu/GLBackendState.cpp | 6 ++++++ 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/interface/src/Stars.cpp b/interface/src/Stars.cpp index 7b612acb68..68e0001996 100644 --- a/interface/src/Stars.cpp +++ b/interface/src/Stars.cpp @@ -141,6 +141,7 @@ void Stars::render(RenderArgs* renderArgs, float alpha) { auto state = gpu::StatePointer(new gpu::State()); // enable decal blend state->setDepthTest(gpu::State::DepthTest(false)); + state->setAntialiasedLineEnable(true); // line smoothing also smooth points state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA); _gridPipeline.reset(gpu::Pipeline::create(program, state)); } @@ -205,8 +206,6 @@ void Stars::render(RenderArgs* renderArgs, float alpha) { batch._glUniform1f(_timeSlot, secs); geometryCache->renderUnitCube(batch); - //glHint(GL_POINT_SMOOTH_HINT, GL_NICEST); - static const size_t VERTEX_STRIDE = sizeof(StarVertex); size_t offset = offsetof(StarVertex, position); gpu::BufferView posView(vertexBuffer, offset, vertexBuffer->getSize(), VERTEX_STRIDE, positionElement); @@ -215,9 +214,6 @@ void Stars::render(RenderArgs* renderArgs, float alpha) { // Render the stars batch.setPipeline(_starsPipeline); - //batch._glEnable(GL_PROGRAM_POINT_SIZE_EXT); - //batch._glEnable(GL_VERTEX_PROGRAM_POINT_SIZE); - //batch._glEnable(GL_POINT_SMOOTH); batch.setInputFormat(streamFormat); batch.setInputBuffer(VERTICES_SLOT, posView); diff --git a/libraries/gpu/src/gpu/GLBackendState.cpp b/libraries/gpu/src/gpu/GLBackendState.cpp index 18fc9ddd3c..e9dcd3aad3 100644 --- a/libraries/gpu/src/gpu/GLBackendState.cpp +++ b/libraries/gpu/src/gpu/GLBackendState.cpp @@ -482,6 +482,12 @@ void GLBackend::syncPipelineStateCache() { State::Data state; glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS); + + // Point size is always on + glHint(GL_POINT_SMOOTH_HINT, GL_NICEST); + glEnable(GL_PROGRAM_POINT_SIZE_EXT); + glEnable(GL_VERTEX_PROGRAM_POINT_SIZE); + getCurrentGLState(state); State::Signature signature = State::evalSignature(state); From 5baf993c24a70b26de159259a5a6fbb5d6ffbc0f Mon Sep 17 00:00:00 2001 From: Sam Gateau Date: Wed, 29 Jul 2015 16:27:45 -0700 Subject: [PATCH 16/65] fixing the stars again --- interface/src/Stars.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/Stars.cpp b/interface/src/Stars.cpp index 68e0001996..42b1a3f2e2 100644 --- a/interface/src/Stars.cpp +++ b/interface/src/Stars.cpp @@ -141,7 +141,6 @@ void Stars::render(RenderArgs* renderArgs, float alpha) { auto state = gpu::StatePointer(new gpu::State()); // enable decal blend state->setDepthTest(gpu::State::DepthTest(false)); - state->setAntialiasedLineEnable(true); // line smoothing also smooth points state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA); _gridPipeline.reset(gpu::Pipeline::create(program, state)); } @@ -153,6 +152,7 @@ void Stars::render(RenderArgs* renderArgs, float alpha) { auto state = gpu::StatePointer(new gpu::State()); // enable decal blend state->setDepthTest(gpu::State::DepthTest(false)); + state->setAntialiasedLineEnable(true); // line smoothing also smooth points state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA); _starsPipeline.reset(gpu::Pipeline::create(program, state)); @@ -219,6 +219,6 @@ void Stars::render(RenderArgs* renderArgs, float alpha) { batch.setInputBuffer(VERTICES_SLOT, posView); batch.setInputBuffer(COLOR_SLOT, colView); batch.draw(gpu::Primitive::POINTS, STARFIELD_NUM_STARS); - + renderArgs->_context->render(batch); } From 4972cb024f34eb5fe2f4cfa34080be52ae06f3b2 Mon Sep 17 00:00:00 2001 From: Sam Gateau Date: Wed, 29 Jul 2015 16:48:23 -0700 Subject: [PATCH 17/65] Try to make the inlucde sequence simpler in gpu for GLBackend --- libraries/gpu/src/gpu/Batch.cpp | 18 ------------------ libraries/gpu/src/gpu/GLBackend.cpp | 1 - libraries/gpu/src/gpu/GLBackend.h | 2 -- libraries/gpu/src/gpu/GLBackendShared.h | 2 -- 4 files changed, 23 deletions(-) diff --git a/libraries/gpu/src/gpu/Batch.cpp b/libraries/gpu/src/gpu/Batch.cpp index b7f3ef444c..a10f5b3de7 100644 --- a/libraries/gpu/src/gpu/Batch.cpp +++ b/libraries/gpu/src/gpu/Batch.cpp @@ -10,12 +10,6 @@ // #include "Batch.h" -#include -#include - -#include "GPUConfig.h" - - #if defined(NSIGHT_FOUND) #include "nvToolsExt.h" @@ -288,15 +282,3 @@ void Batch::getQuery(const QueryPointer& query) { _params.push_back(_queries.cache(query)); } -void push_back(Batch::Params& params, const vec3& v) { - params.push_back(v.x); - params.push_back(v.y); - params.push_back(v.z); -} - -void push_back(Batch::Params& params, const vec4& v) { - params.push_back(v.x); - params.push_back(v.y); - params.push_back(v.z); - params.push_back(v.a); -} diff --git a/libraries/gpu/src/gpu/GLBackend.cpp b/libraries/gpu/src/gpu/GLBackend.cpp index 4bc8abadd0..51a767882c 100644 --- a/libraries/gpu/src/gpu/GLBackend.cpp +++ b/libraries/gpu/src/gpu/GLBackend.cpp @@ -11,7 +11,6 @@ #include "GLBackendShared.h" #include -#include "GPULogging.h" #include using namespace gpu; diff --git a/libraries/gpu/src/gpu/GLBackend.h b/libraries/gpu/src/gpu/GLBackend.h index 9d8c9ef805..3b1d4cde5e 100644 --- a/libraries/gpu/src/gpu/GLBackend.h +++ b/libraries/gpu/src/gpu/GLBackend.h @@ -18,8 +18,6 @@ #include "GPUConfig.h" #include "Context.h" -#include "Batch.h" - namespace gpu { diff --git a/libraries/gpu/src/gpu/GLBackendShared.h b/libraries/gpu/src/gpu/GLBackendShared.h index 75bef461f9..888fd1164d 100644 --- a/libraries/gpu/src/gpu/GLBackendShared.h +++ b/libraries/gpu/src/gpu/GLBackendShared.h @@ -16,8 +16,6 @@ #include "GPULogging.h" #include "GLBackend.h" -#include "Batch.h" - static const GLenum _primitiveToGLmode[gpu::NUM_PRIMITIVES] = { GL_POINTS, GL_LINES, From a9556660c4ee2995eb5f96039c7489a0bf276d8b Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 29 Jul 2015 20:53:24 -0700 Subject: [PATCH 18/65] fix linux build --- libraries/entities/src/ParticleEffectEntityItem.cpp | 6 +++--- libraries/gpu/src/gpu/Batch.cpp | 2 ++ libraries/gpu/src/gpu/GLBackendShader.cpp | 3 +-- libraries/gpu/src/gpu/GLBackendState.cpp | 10 +++++----- 4 files changed, 11 insertions(+), 10 deletions(-) diff --git a/libraries/entities/src/ParticleEffectEntityItem.cpp b/libraries/entities/src/ParticleEffectEntityItem.cpp index 4dfc9dd436..dc5bbb85ed 100644 --- a/libraries/entities/src/ParticleEffectEntityItem.cpp +++ b/libraries/entities/src/ParticleEffectEntityItem.cpp @@ -155,9 +155,9 @@ void ParticleEffectEntityItem::computeAndUpdateDimensions() { float yMin = std::min(yApex, yEnd); // times 2 because dimensions are diameters not radii. - glm::vec3 dims(2.0f * std::max(fabs(xMin), fabs(xMax)), - 2.0f * std::max(fabs(yMin), fabs(yMax)), - 2.0f * std::max(fabs(zMin), fabs(zMax))); + glm::vec3 dims(2.0f * std::max(fabsf(xMin), fabsf(xMax)), + 2.0f * std::max(fabsf(yMin), fabsf(yMax)), + 2.0f * std::max(fabsf(zMin), fabsf(zMax))); EntityItem::setDimensions(dims); } diff --git a/libraries/gpu/src/gpu/Batch.cpp b/libraries/gpu/src/gpu/Batch.cpp index a10f5b3de7..0347536808 100644 --- a/libraries/gpu/src/gpu/Batch.cpp +++ b/libraries/gpu/src/gpu/Batch.cpp @@ -8,6 +8,8 @@ // 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 "Batch.h" #if defined(NSIGHT_FOUND) diff --git a/libraries/gpu/src/gpu/GLBackendShader.cpp b/libraries/gpu/src/gpu/GLBackendShader.cpp index dccd035cb4..5fa0e277e5 100755 --- a/libraries/gpu/src/gpu/GLBackendShader.cpp +++ b/libraries/gpu/src/gpu/GLBackendShader.cpp @@ -639,14 +639,13 @@ int makeUniformBlockSlots(GLuint glprogram, const Shader::BindingSet& slotBindin GLchar name[NAME_LENGTH]; GLint length = 0; GLint size = 0; - GLenum type = 0; GLint binding = -1; glGetActiveUniformBlockiv(glprogram, i, GL_UNIFORM_BLOCK_NAME_LENGTH, &length); glGetActiveUniformBlockName(glprogram, i, NAME_LENGTH, &length, name); glGetActiveUniformBlockiv(glprogram, i, GL_UNIFORM_BLOCK_BINDING, &binding); glGetActiveUniformBlockiv(glprogram, i, GL_UNIFORM_BLOCK_DATA_SIZE, &size); - + GLuint blockIndex = glGetUniformBlockIndex(glprogram, name); // CHeck if there is a requested binding for this block diff --git a/libraries/gpu/src/gpu/GLBackendState.cpp b/libraries/gpu/src/gpu/GLBackendState.cpp index e9dcd3aad3..22c61b2365 100644 --- a/libraries/gpu/src/gpu/GLBackendState.cpp +++ b/libraries/gpu/src/gpu/GLBackendState.cpp @@ -482,15 +482,15 @@ void GLBackend::syncPipelineStateCache() { State::Data state; glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS); - - // Point size is always on - glHint(GL_POINT_SMOOTH_HINT, GL_NICEST); - glEnable(GL_PROGRAM_POINT_SIZE_EXT); + + // Point size is always on + glHint(GL_POINT_SMOOTH_HINT, GL_NICEST); + glEnable(GL_PROGRAM_POINT_SIZE_EXT); glEnable(GL_VERTEX_PROGRAM_POINT_SIZE); getCurrentGLState(state); State::Signature signature = State::evalSignature(state); - + _pipeline._stateCache = state; _pipeline._stateSignatureCache = signature; } From 85f06639a5a67117be3c7ff644f4ffc771d97123 Mon Sep 17 00:00:00 2001 From: Niels Nesse Date: Thu, 30 Jul 2015 12:50:57 -0700 Subject: [PATCH 19/65] Remove redundant code in getOrCreateChildElementAt() --- libraries/octree/src/OctreeElement.cpp | 45 +------------------------- 1 file changed, 1 insertion(+), 44 deletions(-) diff --git a/libraries/octree/src/OctreeElement.cpp b/libraries/octree/src/OctreeElement.cpp index e11a11fc8e..d166b886b8 100644 --- a/libraries/octree/src/OctreeElement.cpp +++ b/libraries/octree/src/OctreeElement.cpp @@ -640,50 +640,7 @@ OctreeElement* OctreeElement::getOrCreateChildElementAt(float x, float y, float // otherwise, we need to find which of our children we should recurse glm::vec3 ourCenter = _cube.calcCenter(); - int childIndex = CHILD_UNKNOWN; - // left half - if (x > ourCenter.x) { - if (y > ourCenter.y) { - // top left - if (z > ourCenter.z) { - // top left far - childIndex = CHILD_TOP_LEFT_FAR; - } else { - // top left near - childIndex = CHILD_TOP_LEFT_NEAR; - } - } else { - // bottom left - if (z > ourCenter.z) { - // bottom left far - childIndex = CHILD_BOTTOM_LEFT_FAR; - } else { - // bottom left near - childIndex = CHILD_BOTTOM_LEFT_NEAR; - } - } - } else { - // right half - if (y > ourCenter.y) { - // top right - if (z > ourCenter.z) { - // top right far - childIndex = CHILD_TOP_RIGHT_FAR; - } else { - // top right near - childIndex = CHILD_TOP_RIGHT_NEAR; - } - } else { - // bottom right - if (z > ourCenter.z) { - // bottom right far - childIndex = CHILD_BOTTOM_RIGHT_FAR; - } else { - // bottom right near - childIndex = CHILD_BOTTOM_RIGHT_NEAR; - } - } - } + int childIndex = getMyChildContainingPoint(glm::vec3(x, y, z)); // Now, check if we have a child at that location child = getChildAtIndex(childIndex); From 14f4c9c6c04b462f177828717803f52256902887 Mon Sep 17 00:00:00 2001 From: Sam Gateau Date: Thu, 30 Jul 2015 15:07:36 -0700 Subject: [PATCH 20/65] REmove more of the unnecessary GLBacken .h and GPUCOnfig.h include, The gpu::Context is now completely agnostic of the True Backend --- interface/src/Application.cpp | 5 ++-- interface/src/GLCanvas.h | 1 - libraries/gpu/src/gpu/Context.cpp | 17 ++++++----- libraries/gpu/src/gpu/Context.h | 28 +++++++++++++++---- libraries/gpu/src/gpu/GLBackend.cpp | 18 ++++++++---- libraries/gpu/src/gpu/GLBackend.h | 10 +++++-- .../render-utils/src/FramebufferCache.cpp | 1 - libraries/render/src/render/DrawStatus.cpp | 13 ++++----- libraries/render/src/render/DrawTask.cpp | 2 -- tests/render-utils/src/main.cpp | 4 +-- 10 files changed, 63 insertions(+), 36 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 1c91f9282c..047596e40a 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -772,8 +772,9 @@ void Application::initializeGL() { } #endif - // Where the gpuContext is created and where the TRUE Backend is created and assigned - _gpuContext = std::make_shared(new gpu::GLBackend()); + // Where the gpuContext is initialized and where the TRUE Backend is created and assigned + gpu::Context::init(); + _gpuContext = std::make_shared(); initDisplay(); qCDebug(interfaceapp, "Initialized Display."); diff --git a/interface/src/GLCanvas.h b/interface/src/GLCanvas.h index 925061edd0..6c53a17e04 100644 --- a/interface/src/GLCanvas.h +++ b/interface/src/GLCanvas.h @@ -13,7 +13,6 @@ #define hifi_GLCanvas_h #include -#include #include #include diff --git a/libraries/gpu/src/gpu/Context.cpp b/libraries/gpu/src/gpu/Context.cpp index 6730be33bb..239c460c77 100644 --- a/libraries/gpu/src/gpu/Context.cpp +++ b/libraries/gpu/src/gpu/Context.cpp @@ -10,13 +10,16 @@ // #include "Context.h" -// this include should disappear! as soon as the gpu::Context is in place -#include "GLBackend.h" - using namespace gpu; -Context::Context(Backend* backend) : - _backend(backend) { +Context::CreateBackend Context::_createBackendCallback = nullptr; +Context::MakeProgram Context::_makeProgramCallback = nullptr; +std::once_flag Context::_initialized; + +Context::Context() { + if (_createBackendCallback) { + _backend.reset(_createBackendCallback()); + } } Context::Context(const Context& context) { @@ -26,8 +29,8 @@ Context::~Context() { } bool Context::makeProgram(Shader& shader, const Shader::BindingSet& bindings) { - if (shader.isProgram()) { - return GLBackend::makeProgram(shader, bindings); + if (shader.isProgram() && _makeProgramCallback) { + return _makeProgramCallback(shader, bindings); } return false; } diff --git a/libraries/gpu/src/gpu/Context.h b/libraries/gpu/src/gpu/Context.h index ab7a1d1c11..7158bd1a6d 100644 --- a/libraries/gpu/src/gpu/Context.h +++ b/libraries/gpu/src/gpu/Context.h @@ -12,6 +12,7 @@ #define hifi_gpu_Context_h #include +#include #include "Batch.h" @@ -26,13 +27,12 @@ namespace gpu { class Backend { public: - virtual~ Backend() {}; + virtual void render(Batch& batch) = 0; virtual void syncCache() = 0; virtual void downloadFramebuffer(const FramebufferPointer& srcFramebuffer, const Vec4i& region, QImage& destImage) = 0; - class TransformObject { public: Mat4 _model; @@ -118,7 +118,21 @@ protected: class Context { public: - Context(Backend* backend); + typedef Backend* (*CreateBackend)(); + typedef bool (*MakeProgram)(Shader& shader, const Shader::BindingSet& bindings); + + + // This one call must happen before any context is created or used (Shader::MakeProgram) in order to setup the Backend and any singleton data needed + template + static void init() { + std::call_once(_initialized, [] { + _createBackendCallback = T::createBackend; + _makeProgramCallback = T::makeProgram; + T::init(); + }); + } + + Context(); ~Context(); void render(Batch& batch); @@ -132,13 +146,17 @@ public: protected: Context(const Context& context); + std::unique_ptr _backend; + // This function can only be called by "static Shader::makeProgram()" // makeProgramShader(...) make a program shader ready to be used in a Batch. // It compiles the sub shaders, link them and defines the Slots and their bindings. // If the shader passed is not a program, nothing happens. - static bool makeProgram(Shader& shader, const Shader::BindingSet& bindings = Shader::BindingSet()); + static bool makeProgram(Shader& shader, const Shader::BindingSet& bindings); - std::unique_ptr _backend; + static CreateBackend _createBackendCallback; + static MakeProgram _makeProgramCallback; + static std::once_flag _initialized; friend class Shader; }; diff --git a/libraries/gpu/src/gpu/GLBackend.cpp b/libraries/gpu/src/gpu/GLBackend.cpp index 198fe4802a..c74a94e03e 100644 --- a/libraries/gpu/src/gpu/GLBackend.cpp +++ b/libraries/gpu/src/gpu/GLBackend.cpp @@ -63,12 +63,7 @@ GLBackend::CommandCall GLBackend::_commandCalls[Batch::NUM_COMMANDS] = (&::gpu::GLBackend::do_glLineWidth), }; -GLBackend::GLBackend() : - _input(), - _transform(), - _pipeline(), - _output() -{ +void GLBackend::init() { static std::once_flag once; std::call_once(once, [] { qCDebug(gpulogging) << "GL Version: " << QString((const char*) glGetString(GL_VERSION)); @@ -108,7 +103,18 @@ GLBackend::GLBackend() : }*/ #endif }); +} +Backend* GLBackend::createBackend() { + return new GLBackend(); +} + +GLBackend::GLBackend() : + _input(), + _transform(), + _pipeline(), + _output() +{ initInput(); initTransform(); } diff --git a/libraries/gpu/src/gpu/GLBackend.h b/libraries/gpu/src/gpu/GLBackend.h index 3686c5138d..e86424ceb7 100644 --- a/libraries/gpu/src/gpu/GLBackend.h +++ b/libraries/gpu/src/gpu/GLBackend.h @@ -22,10 +22,17 @@ namespace gpu { class GLBackend : public Backend { -public: + + // Context Backend static interface required + friend class Context; + static void init(); + static Backend* createBackend(); + static bool makeProgram(Shader& shader, const Shader::BindingSet& bindings = Shader::BindingSet()); explicit GLBackend(bool syncCache); GLBackend(); +public: + virtual ~GLBackend(); virtual void render(Batch& batch); @@ -47,7 +54,6 @@ public: static void checkGLStackStable(std::function f); - static bool makeProgram(Shader& shader, const Shader::BindingSet& bindings = Shader::BindingSet()); class GLBuffer : public GPUObject { diff --git a/libraries/render-utils/src/FramebufferCache.cpp b/libraries/render-utils/src/FramebufferCache.cpp index 601d99108d..d6ebd001d2 100644 --- a/libraries/render-utils/src/FramebufferCache.cpp +++ b/libraries/render-utils/src/FramebufferCache.cpp @@ -18,7 +18,6 @@ #include #include #include -#include #include "RenderUtilsLogging.h" static QQueue _cachedFramebuffers; diff --git a/libraries/render/src/render/DrawStatus.cpp b/libraries/render/src/render/DrawStatus.cpp index f50f517d7d..cf3616a83a 100644 --- a/libraries/render/src/render/DrawStatus.cpp +++ b/libraries/render/src/render/DrawStatus.cpp @@ -18,10 +18,7 @@ #include #include -#include #include -#include -#include #include "drawItemBounds_vert.h" #include "drawItemBounds_frag.h" @@ -151,17 +148,17 @@ void DrawStatus::run(const SceneContextPointer& sceneContext, const RenderContex const unsigned int VEC3_ADRESS_OFFSET = 3; for (int i = 0; i < nbItems; i++) { - batch._glUniform3fv(_drawItemBoundPosLoc, 1, (const GLfloat*) (itemAABox + i)); - batch._glUniform3fv(_drawItemBoundDimLoc, 1, ((const GLfloat*) (itemAABox + i)) + VEC3_ADRESS_OFFSET); + batch._glUniform3fv(_drawItemBoundPosLoc, 1, (const float*) (itemAABox + i)); + batch._glUniform3fv(_drawItemBoundDimLoc, 1, ((const float*) (itemAABox + i)) + VEC3_ADRESS_OFFSET); batch.draw(gpu::LINES, 24, 0); } batch.setPipeline(getDrawItemStatusPipeline()); for (int i = 0; i < nbItems; i++) { - batch._glUniform3fv(_drawItemStatusPosLoc, 1, (const GLfloat*) (itemAABox + i)); - batch._glUniform3fv(_drawItemStatusDimLoc, 1, ((const GLfloat*) (itemAABox + i)) + VEC3_ADRESS_OFFSET); - batch._glUniform4iv(_drawItemStatusValueLoc, 1, (const GLint*) (itemStatus + i)); + batch._glUniform3fv(_drawItemStatusPosLoc, 1, (const float*) (itemAABox + i)); + batch._glUniform3fv(_drawItemStatusDimLoc, 1, ((const float*) (itemAABox + i)) + VEC3_ADRESS_OFFSET); + batch._glUniform4iv(_drawItemStatusValueLoc, 1, (const int*) (itemStatus + i)); batch.draw(gpu::TRIANGLES, 24, 0); } diff --git a/libraries/render/src/render/DrawTask.cpp b/libraries/render/src/render/DrawTask.cpp index 1a3b68af03..36ff302952 100755 --- a/libraries/render/src/render/DrawTask.cpp +++ b/libraries/render/src/render/DrawTask.cpp @@ -17,9 +17,7 @@ #include #include #include -#include #include -#include using namespace render; diff --git a/tests/render-utils/src/main.cpp b/tests/render-utils/src/main.cpp index 7c02c7e8a7..9abd533650 100644 --- a/tests/render-utils/src/main.cpp +++ b/tests/render-utils/src/main.cpp @@ -94,7 +94,6 @@ class QTestWindow : public QWindow { QSize _size; //TextRenderer* _textRenderer[4]; RateCounter fps; - gpu::ContextPointer _gpuContext; protected: void renderText(); @@ -124,7 +123,8 @@ public: show(); makeCurrent(); - _gpuContext.reset(new gpu::Context(new gpu::GLBackend())); + + gpu::Context::init(); From fbf21cb089a4579b507dee31110266f183429243 Mon Sep 17 00:00:00 2001 From: samcake Date: Thu, 30 Jul 2015 18:27:47 -0700 Subject: [PATCH 21/65] FIxed the problem on Mac, by removing all of the gpuConfig includesgit status q :q wq --- libraries/gpu/src/gpu/GLBackend.h | 8 +++---- libraries/gpu/src/gpu/GLBackendShader.cpp | 22 +++++++++++++++++-- .../src/DeferredLightingEffect.cpp | 5 ----- libraries/render-utils/src/Model.cpp | 6 +---- 4 files changed, 25 insertions(+), 16 deletions(-) diff --git a/libraries/gpu/src/gpu/GLBackend.h b/libraries/gpu/src/gpu/GLBackend.h index e86424ceb7..c0bae76d78 100644 --- a/libraries/gpu/src/gpu/GLBackend.h +++ b/libraries/gpu/src/gpu/GLBackend.h @@ -27,7 +27,7 @@ class GLBackend : public Backend { friend class Context; static void init(); static Backend* createBackend(); - static bool makeProgram(Shader& shader, const Shader::BindingSet& bindings = Shader::BindingSet()); + static bool makeProgram(Shader& shader, const Shader::BindingSet& bindings); explicit GLBackend(bool syncCache); GLBackend(); @@ -95,9 +95,9 @@ public: #if (GPU_TRANSFORM_PROFILE == GPU_CORE) #else - GLuint _transformObject_model = -1; - GLuint _transformCamera_viewInverse = -1; - GLuint _transformCamera_viewport = -1; + GLint _transformObject_model = -1; + GLint _transformCamera_viewInverse = -1; + GLint _transformCamera_viewport = -1; #endif GLShader(); diff --git a/libraries/gpu/src/gpu/GLBackendShader.cpp b/libraries/gpu/src/gpu/GLBackendShader.cpp index 5fa0e277e5..9c0cf7628d 100755 --- a/libraries/gpu/src/gpu/GLBackendShader.cpp +++ b/libraries/gpu/src/gpu/GLBackendShader.cpp @@ -541,7 +541,12 @@ ElementResource getFormatFromGLUniform(GLenum gltype) { }; -int makeUniformSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& uniforms, Shader::SlotSet& textures, Shader::SlotSet& samplers) { +int makeUniformSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, + Shader::SlotSet& uniforms, Shader::SlotSet& textures, Shader::SlotSet& samplers +#if (GPU_FEATURE_PROFILE == GPU_LEGACY) + , Shader::SlotSet& fakeBuffers +#endif +) { GLint uniformsCount = 0; #if (GPU_FEATURE_PROFILE == GPU_LEGACY) @@ -582,6 +587,15 @@ int makeUniformSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, S } if (elementResource._resource == Resource::BUFFER) { +#if (GPU_FEATURE_PROFILE == GPU_LEGACY) + // if in legacy profile, we fake the uniform buffer with an array + // this is where we detect it assuming it's explicitely assinged a binding + auto requestedBinding = slotBindings.find(std::string(sname)); + if (requestedBinding != slotBindings.end()) { + // found one buffer! + fakeBuffers.insert(Shader::Slot(sname, location, elementResource._element, elementResource._resource)); + } +#endif uniforms.insert(Shader::Slot(sname, location, elementResource._element, elementResource._resource)); } else { // For texture/Sampler, the location is the actual binding value @@ -736,8 +750,12 @@ bool GLBackend::makeProgram(Shader& shader, const Shader::BindingSet& slotBindin Shader::SlotSet uniforms; Shader::SlotSet textures; Shader::SlotSet samplers; +#if (GPU_FEATURE_PROFILE == GPU_CORE) makeUniformSlots(object->_program, slotBindings, uniforms, textures, samplers); - +#else + makeUniformSlots(object->_program, slotBindings, uniforms, textures, samplers, buffers); +#endif + Shader::SlotSet inputs; makeInputSlots(object->_program, slotBindings, inputs); diff --git a/libraries/render-utils/src/DeferredLightingEffect.cpp b/libraries/render-utils/src/DeferredLightingEffect.cpp index 7563609026..3fc0a4c46a 100644 --- a/libraries/render-utils/src/DeferredLightingEffect.cpp +++ b/libraries/render-utils/src/DeferredLightingEffect.cpp @@ -614,13 +614,8 @@ void DeferredLightingEffect::loadLightProgram(const char* vertSource, const char locations.texcoordMat = program->getUniforms().findLocation("texcoordMat"); locations.coneParam = program->getUniforms().findLocation("coneParam"); -#if (GPU_FEATURE_PROFILE == GPU_CORE) locations.lightBufferUnit = program->getBuffers().findLocation("lightBuffer"); locations.atmosphereBufferUnit = program->getBuffers().findLocation("atmosphereBufferUnit"); -#else - locations.lightBufferUnit = program->getUniforms().findLocation("lightBuffer"); - locations.atmosphereBufferUnit = program->getUniforms().findLocation("atmosphereBufferUnit"); -#endif auto state = std::make_shared(); if (lightVolume) { diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 1e0e04c776..1e4f3f7190 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -187,13 +187,9 @@ void Model::RenderPipelineLib::initLocations(gpu::ShaderPointer& program, Model: locations.specularTextureUnit = program->getTextures().findLocation("specularMap"); locations.emissiveTextureUnit = program->getTextures().findLocation("emissiveMap"); -#if (GPU_FEATURE_PROFILE == GPU_CORE) locations.materialBufferUnit = program->getBuffers().findLocation("materialBuffer"); locations.lightBufferUnit = program->getBuffers().findLocation("lightBuffer"); -#else - locations.materialBufferUnit = program->getUniforms().findLocation("materialBuffer"); - locations.lightBufferUnit = program->getUniforms().findLocation("lightBuffer"); -#endif + locations.clusterMatrices = program->getUniforms().findLocation("clusterMatrices"); locations.clusterIndices = program->getInputs().findLocation("clusterIndices");; From 0bc94158a8031fa022ae687ea4ea1a8258a82fd7 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 30 Jul 2015 21:09:14 -0700 Subject: [PATCH 22/65] make the height of the tetrahedrons in 'fatten' mode less tall. --- tools/vhacd-util/src/VHACDUtil.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tools/vhacd-util/src/VHACDUtil.cpp b/tools/vhacd-util/src/VHACDUtil.cpp index 2f8175cbb6..02b8e212be 100644 --- a/tools/vhacd-util/src/VHACDUtil.cpp +++ b/tools/vhacd-util/src/VHACDUtil.cpp @@ -125,12 +125,14 @@ void vhacd::VHACDUtil::fattenMeshes(const FBXMesh& mesh, FBXMesh& result, continue; } + // from the middle of the triangle, pull a point down to form a tetrahedron. float dropAmount = 0; dropAmount = glm::max(glm::length(p1 - p0), dropAmount); dropAmount = glm::max(glm::length(p2 - p1), dropAmount); dropAmount = glm::max(glm::length(p0 - p2), dropAmount); + dropAmount *= 0.25f; - glm::vec3 p3 = av - glm::vec3(0, dropAmount, 0); // a point 1 meter below the average of this triangle's points + glm::vec3 p3 = av - glm::vec3(0, dropAmount, 0); int index3 = result.vertices.size(); result.vertices << p3; // add the new point to the result mesh From c8f398024eb21f49959ab2fc92d2e3a4f95a1fbb Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 30 Jul 2015 21:16:07 -0700 Subject: [PATCH 23/65] replace a magic number with a constant --- tools/vhacd-util/src/VHACDUtil.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tools/vhacd-util/src/VHACDUtil.cpp b/tools/vhacd-util/src/VHACDUtil.cpp index 02b8e212be..6743ed6a9f 100644 --- a/tools/vhacd-util/src/VHACDUtil.cpp +++ b/tools/vhacd-util/src/VHACDUtil.cpp @@ -12,6 +12,8 @@ #include #include "VHACDUtil.h" +const float collisionTetrahedronScale = 0.25f; + // FBXReader jumbles the order of the meshes by reading them back out of a hashtable. This will put // them back in the order in which they appeared in the file. @@ -130,7 +132,7 @@ void vhacd::VHACDUtil::fattenMeshes(const FBXMesh& mesh, FBXMesh& result, dropAmount = glm::max(glm::length(p1 - p0), dropAmount); dropAmount = glm::max(glm::length(p2 - p1), dropAmount); dropAmount = glm::max(glm::length(p0 - p2), dropAmount); - dropAmount *= 0.25f; + dropAmount *= collisionTetrahedronScale; glm::vec3 p3 = av - glm::vec3(0, dropAmount, 0); int index3 = result.vertices.size(); From 5bae9843f5ee7fe3220d54bbf343490fa4c6ef5c Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 31 Jul 2015 09:34:50 -0700 Subject: [PATCH 24/65] code review --- tools/vhacd-util/src/VHACDUtil.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/vhacd-util/src/VHACDUtil.cpp b/tools/vhacd-util/src/VHACDUtil.cpp index 6743ed6a9f..1e7f9df7c1 100644 --- a/tools/vhacd-util/src/VHACDUtil.cpp +++ b/tools/vhacd-util/src/VHACDUtil.cpp @@ -12,7 +12,7 @@ #include #include "VHACDUtil.h" -const float collisionTetrahedronScale = 0.25f; +const float COLLISION_TETRAHEDRON_SCALE = 0.25f; // FBXReader jumbles the order of the meshes by reading them back out of a hashtable. This will put @@ -132,9 +132,9 @@ void vhacd::VHACDUtil::fattenMeshes(const FBXMesh& mesh, FBXMesh& result, dropAmount = glm::max(glm::length(p1 - p0), dropAmount); dropAmount = glm::max(glm::length(p2 - p1), dropAmount); dropAmount = glm::max(glm::length(p0 - p2), dropAmount); - dropAmount *= collisionTetrahedronScale; + dropAmount *= COLLISION_TETRAHEDRON_SCALE; - glm::vec3 p3 = av - glm::vec3(0, dropAmount, 0); + glm::vec3 p3 = av - glm::vec3(0.0f, dropAmount, 0.0f); int index3 = result.vertices.size(); result.vertices << p3; // add the new point to the result mesh From 67760fad79897fa3151bd3b265a484f9c8b121d8 Mon Sep 17 00:00:00 2001 From: bwent Date: Fri, 31 Jul 2015 09:27:25 -0700 Subject: [PATCH 25/65] Add gettable naturalPosition property for model entities --- libraries/entities/src/EntityItemProperties.cpp | 9 ++++++++- libraries/entities/src/EntityItemProperties.h | 6 +++++- libraries/entities/src/EntityScriptingInterface.cpp | 1 + 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 61253ba6ba..caae0203ae 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -114,7 +114,8 @@ _glowLevelChanged(false), _localRenderAlphaChanged(false), _defaultSettings(true), -_naturalDimensions(1.0f, 1.0f, 1.0f) +_naturalDimensions(1.0f, 1.0f, 1.0f), +_naturalPosition(0.0f, 0.0f, 0.0f) { } @@ -128,6 +129,11 @@ void EntityItemProperties::setSittingPoints(const QVector& sitting } } +void EntityItemProperties::setNaturalPosition(const glm::vec3& min, const glm::vec3& max) { + glm::vec3 radius = (max - min) / 2.0f; + _naturalPosition = max - radius; +} + bool EntityItemProperties::animationSettingsChanged() const { return _animationSettingsChanged; } @@ -378,6 +384,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool COPY_PROPERTY_TO_QSCRIPTVALUE(dimensions); if (!skipDefaults) { COPY_PROPERTY_TO_QSCRIPTVALUE(naturalDimensions); // gettable, but not settable + COPY_PROPERTY_TO_QSCRIPTVALUE(naturalPosition); } COPY_PROPERTY_TO_QSCRIPTVALUE(rotation); COPY_PROPERTY_TO_QSCRIPTVALUE(velocity); diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index 4532ffd67b..94e0f0fac8 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -192,7 +192,10 @@ public: const glm::vec3& getNaturalDimensions() const { return _naturalDimensions; } void setNaturalDimensions(const glm::vec3& value) { _naturalDimensions = value; } - + + const glm::vec3& getNaturalPosition() const { return _naturalPosition; } + void setNaturalPosition(const glm::vec3& min, const glm::vec3& max); + const QStringList& getTextureNames() const { return _textureNames; } void setTextureNames(const QStringList& value) { _textureNames = value; } @@ -232,6 +235,7 @@ private: QVector _sittingPoints; QStringList _textureNames; glm::vec3 _naturalDimensions; + glm::vec3 _naturalPosition; }; Q_DECLARE_METATYPE(EntityItemProperties); diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 36fcc17a71..c40f7edd47 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -118,6 +118,7 @@ EntityItemProperties EntityScriptingInterface::getEntityProperties(QUuid identit results.setSittingPoints(geometry->sittingPoints); Extents meshExtents = geometry->getUnscaledMeshExtents(); results.setNaturalDimensions(meshExtents.maximum - meshExtents.minimum); + results.setNaturalPosition(meshExtents.minimum, meshExtents.maximum); } } From aab1f708002c5e15d19e7b0371fa05a2faccd6e7 Mon Sep 17 00:00:00 2001 From: bwent Date: Fri, 31 Jul 2015 10:47:05 -0700 Subject: [PATCH 26/65] Renaming vars --- libraries/entities/src/EntityItemProperties.cpp | 6 +++--- libraries/entities/src/EntityItemProperties.h | 2 +- libraries/entities/src/EntityScriptingInterface.cpp | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index caae0203ae..ebe4ac6014 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -129,9 +129,9 @@ void EntityItemProperties::setSittingPoints(const QVector& sitting } } -void EntityItemProperties::setNaturalPosition(const glm::vec3& min, const glm::vec3& max) { - glm::vec3 radius = (max - min) / 2.0f; - _naturalPosition = max - radius; +void EntityItemProperties::calculateNaturalPosition(const glm::vec3& min, const glm::vec3& max) { + glm::vec3 halfDimension = (max - min) / 2.0f; + _naturalPosition = max - halfDimension; } bool EntityItemProperties::animationSettingsChanged() const { diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index 94e0f0fac8..3ce6040d19 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -194,7 +194,7 @@ public: void setNaturalDimensions(const glm::vec3& value) { _naturalDimensions = value; } const glm::vec3& getNaturalPosition() const { return _naturalPosition; } - void setNaturalPosition(const glm::vec3& min, const glm::vec3& max); + void calculateNaturalPosition(const glm::vec3& min, const glm::vec3& max); const QStringList& getTextureNames() const { return _textureNames; } void setTextureNames(const QStringList& value) { _textureNames = value; } diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index c40f7edd47..12f5ffe190 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -118,7 +118,7 @@ EntityItemProperties EntityScriptingInterface::getEntityProperties(QUuid identit results.setSittingPoints(geometry->sittingPoints); Extents meshExtents = geometry->getUnscaledMeshExtents(); results.setNaturalDimensions(meshExtents.maximum - meshExtents.minimum); - results.setNaturalPosition(meshExtents.minimum, meshExtents.maximum); + results.calculateNaturalPosition(meshExtents.minimum, meshExtents.maximum); } } From dff6b0a456a96cd7eb37b9069d811ea133c3f0df Mon Sep 17 00:00:00 2001 From: Zander Otavka Date: Tue, 30 Jun 2015 14:32:37 -0700 Subject: [PATCH 27/65] Fix isFacingAvatar property on BillboardOverlay. --- .../src/ui/overlays/BillboardOverlay.cpp | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/interface/src/ui/overlays/BillboardOverlay.cpp b/interface/src/ui/overlays/BillboardOverlay.cpp index d26ecc5c67..37e197130b 100644 --- a/interface/src/ui/overlays/BillboardOverlay.cpp +++ b/interface/src/ui/overlays/BillboardOverlay.cpp @@ -20,6 +20,9 @@ #include "Application.h" #include "GeometryUtil.h" +#include + + BillboardOverlay::BillboardOverlay() { _isLoaded = false; } @@ -45,9 +48,28 @@ void BillboardOverlay::render(RenderArgs* args) { glm::quat rotation; if (_isFacingAvatar) { + // LOL, quaternions are hard. // rotate about vertical to face the camera +// glm::vec3 dPos = getPosition() - args->_viewFrustum->getPosition(); +// dPos = glm::normalize(dPos); +// rotation = glm::quat(0, dPos.x, dPos.y, dPos.z); rotation = args->_viewFrustum->getOrientation(); rotation *= glm::angleAxis(glm::pi(), IDENTITY_UP); +// float horizontal = glm::sqrt(dPos.x * dPos.x + dPos.y + dPos.y); +// glm::vec3 zAxis = glm::vec3(0, 0, 1); +// rotation = rotationBetween(zAxis, dPos); +// glm::vec3 euler = safeEulerAngles(rotationBetween(zAxis, dPos)); +// rotation = glm::quat(glm::vec3(euler.x, euler.y, 0)); +// float yaw = (dPos.x == 0.0f && dPos.z == 0.0f) ? 0.0f : glm::atan(dPos.x, dPos.z); +// glm::quat yawQuat = glm::quat(glm::vec3(0, yaw, 0)); +// float pitch = (dPos.y == 0.0f && horizontal == 0.0f) ? 0.0f : glm::atan(dPos.y, horizontal); +// glm::quat pitchQuat = glm::quat(glm::vec3(pitch, 0, 0)); +// glm::mat4x4 matrix = glm::lookAt(args->_viewFrustum->getPosition(), getPosition(), +// glm::vec3(0, 1, 0)); +// rotation = glm::quat_cast(matrix); +// rotation = yawQuat * pitchQuat; +// glm::vec3 pitch = glm::vec3(dPos.x, dPos.y, 0); +// rotation = glm::quat(glm::vec3(pitch, yaw, 0)); rotation *= getRotation(); } else { rotation = getRotation(); From 173a79867ce16f78dfe28b8d8affb06b9cde5909 Mon Sep 17 00:00:00 2001 From: Zander Otavka Date: Mon, 13 Jul 2015 16:38:16 -0700 Subject: [PATCH 28/65] Extend Overlays API to allow for 3D UI panels. Currently, only BillboardOverlays can be added to a panel, but more types of overlays will be supported in the future. --- examples/example/ui/floatingUIExample.js | 173 ++++++++++++++++++ .../src/ui/overlays/BillboardOverlay.cpp | 50 ++++- interface/src/ui/overlays/BillboardOverlay.h | 5 +- interface/src/ui/overlays/FloatingUIPanel.cpp | 89 +++++++++ interface/src/ui/overlays/FloatingUIPanel.h | 47 +++++ interface/src/ui/overlays/Overlays.cpp | 139 ++++++++++---- interface/src/ui/overlays/Overlays.h | 24 +++ interface/src/ui/overlays/PanelAttachable.h | 44 +++++ 8 files changed, 530 insertions(+), 41 deletions(-) create mode 100644 examples/example/ui/floatingUIExample.js create mode 100644 interface/src/ui/overlays/FloatingUIPanel.cpp create mode 100644 interface/src/ui/overlays/FloatingUIPanel.h create mode 100644 interface/src/ui/overlays/PanelAttachable.h diff --git a/examples/example/ui/floatingUIExample.js b/examples/example/ui/floatingUIExample.js new file mode 100644 index 0000000000..09deca4ec7 --- /dev/null +++ b/examples/example/ui/floatingUIExample.js @@ -0,0 +1,173 @@ +// +// floatingUI.js +// examples/example/ui +// +// Created by Alexander Otavka +// Copyright 2015 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 +// + +Script.include(["../../libraries/globals.js"]); + +var BG_IMAGE_URL = HIFI_PUBLIC_BUCKET + "images/card-bg.svg"; +var RED_DOT_IMAGE_URL = HIFI_PUBLIC_BUCKET + "images/red-dot.svg"; +var BLUE_SQUARE_IMAGE_URL = HIFI_PUBLIC_BUCKET + "images/blue-square.svg"; + +var BLANK_ROTATION = { x: 0, y: 0, z: 0, w: 0 }; + +function isBlank(rotation) { + return rotation.x == BLANK_ROTATION.x && + rotation.y == BLANK_ROTATION.y && + rotation.z == BLANK_ROTATION.z && + rotation.w == BLANK_ROTATION.w; +} + +var panel = Overlays.addPanel({ + offsetPosition: { x: 0, y: 0, z: 1 }, +}); + +var panelChildren = []; + +var bg = Overlays.addOverlay("billboard", { + url: BG_IMAGE_URL, + dimensions: { + x: 0.5, + y: 0.5, + }, + isFacingAvatar: false, + visible: true, + alpha: 1.0, + ignoreRayIntersection: false, + attachedPanel: panel, +}); +panelChildren.push(bg); + +var redDot = Overlays.addOverlay("billboard", { + url: RED_DOT_IMAGE_URL, + dimensions: { + x: 0.1, + y: 0.1, + }, + isFacingAvatar: false, + visible: true, + alpha: 1.0, + ignoreRayIntersection: false, + attachedPanel: panel, + offsetPosition: { + x: -0.15, + y: -0.15, + z: -0.001 + } +}); +panelChildren.push(redDot); + +var redDot2 = Overlays.addOverlay("billboard", { + url: RED_DOT_IMAGE_URL, + dimensions: { + x: 0.1, + y: 0.1, + }, + isFacingAvatar: false, + visible: true, + alpha: 1.0, + ignoreRayIntersection: false, + attachedPanel: panel, + offsetPosition: { + x: -0.15, + y: 0, + z: -0.001 + } +}); +panelChildren.push(redDot2); + +var blueSquare = Overlays.addOverlay("billboard", { + url: BLUE_SQUARE_IMAGE_URL, + dimensions: { + x: 0.1, + y: 0.1, + }, + isFacingAvatar: false, + visible: true, + alpha: 1.0, + ignoreRayIntersection: false, + attachedPanel: panel, + offsetPosition: { + x: 0.1, + y: 0, + z: -0.001 + } +}); +panelChildren.push(blueSquare); + +var blueSquare2 = Overlays.addOverlay("billboard", { + url: BLUE_SQUARE_IMAGE_URL, + dimensions: { + x: 0.1, + y: 0.1, + }, + isFacingAvatar: false, + visible: true, + alpha: 1.0, + ignoreRayIntersection: false, + attachedPanel: panel, + offsetPosition: { + x: 0.1, + y: 0.11, + z: -0.001 + } +}); +panelChildren.push(blueSquare2); + +var blueSquare3 = Overlays.addOverlay("billboard", { + url: BLUE_SQUARE_IMAGE_URL, + dimensions: { + x: 0.1, + y: 0.1, + }, + isFacingAvatar: false, + visible: true, + alpha: 1.0, + ignoreRayIntersection: false, + attachedPanel: panel, + offsetPosition: { + x: -0.01, + y: 0.11, + z: -0.001 + } +}); +panelChildren.push(blueSquare3); + +Controller.mousePressEvent.connect(function(event) { + if (event.isRightButton) { + var newOffsetRotation = BLANK_ROTATION; + if (isBlank(Overlays.getPanelProperty(panel, "offsetRotation"))) { + newOffsetRotation = Quat.multiply(MyAvatar.orientation, { x: 0, y: 1, z: 0, w: 0 }); + } + Overlays.editPanel(panel, { + offsetRotation: newOffsetRotation + }); + } else if (event.isLeftButton) { + var pickRay = Camera.computePickRay(event.x, event.y) + var rayPickResult = Overlays.findRayIntersection(pickRay); + print(String(rayPickResult.overlayID)); + if (rayPickResult.intersects) { + for (var i in panelChildren) { + if (panelChildren[i] == rayPickResult.overlayID) { + var oldPos = Overlays.getProperty(rayPickResult.overlayID, "offsetPosition"); + var newPos = { + x: Number(oldPos.x), + y: Number(oldPos.y), + z: Number(oldPos.z) + 0.1 + } + Overlays.editOverlay(rayPickResult.overlayID, { offsetPosition: newPos }); + } + } + } + } +}); + +Script.scriptEnding.connect(function() { + Overlays.deletePanel(panel); +}); \ No newline at end of file diff --git a/interface/src/ui/overlays/BillboardOverlay.cpp b/interface/src/ui/overlays/BillboardOverlay.cpp index 37e197130b..9e1a9a44de 100644 --- a/interface/src/ui/overlays/BillboardOverlay.cpp +++ b/interface/src/ui/overlays/BillboardOverlay.cpp @@ -36,6 +36,13 @@ BillboardOverlay::BillboardOverlay(const BillboardOverlay* billboardOverlay) : { } +void BillboardOverlay::update(float deltatime) { + glm::vec3 newPos = getTranslatedPosition(Application::getInstance()->getAvatarPosition()); + if (newPos != glm::vec3()) { + setPosition(newPos); + } +} + void BillboardOverlay::render(RenderArgs* args) { if (!_texture) { _isLoaded = true; @@ -46,15 +53,17 @@ void BillboardOverlay::render(RenderArgs* args) { return; } + glm::vec3 newPos = getTranslatedPosition(Application::getInstance()->getAvatarPosition()); + if (newPos != glm::vec3()) { + setPosition(newPos); + } + glm::quat rotation; if (_isFacingAvatar) { // LOL, quaternions are hard. - // rotate about vertical to face the camera // glm::vec3 dPos = getPosition() - args->_viewFrustum->getPosition(); // dPos = glm::normalize(dPos); // rotation = glm::quat(0, dPos.x, dPos.y, dPos.z); - rotation = args->_viewFrustum->getOrientation(); - rotation *= glm::angleAxis(glm::pi(), IDENTITY_UP); // float horizontal = glm::sqrt(dPos.x * dPos.x + dPos.y + dPos.y); // glm::vec3 zAxis = glm::vec3(0, 0, 1); // rotation = rotationBetween(zAxis, dPos); @@ -70,9 +79,24 @@ void BillboardOverlay::render(RenderArgs* args) { // rotation = yawQuat * pitchQuat; // glm::vec3 pitch = glm::vec3(dPos.x, dPos.y, 0); // rotation = glm::quat(glm::vec3(pitch, yaw, 0)); + // rotate about vertical to be perpendicular to the camera + rotation = args->_viewFrustum->getOrientation(); + rotation *= glm::angleAxis(glm::pi(), IDENTITY_UP); rotation *= getRotation(); } else { rotation = getRotation(); + if (getAttachedPanel()) { + rotation *= getAttachedPanel()->getOffsetRotation() * + getAttachedPanel()->getFacingRotation(); +// if (getAttachedPanel()->getFacingRotation() != glm::quat(0, 0, 0, 0)) { +// rotation *= getAttachedPanel()->getFacingRotation(); +// } else if (getAttachedPanel()->getOffsetRotation() != glm::quat(0, 0, 0, 0)) { +// rotation *= getAttachedPanel()->getOffsetRotation(); +// } else { +// rotation *= Application::getInstance()->getCamera()->getOrientation() * +// glm::quat(0, 0, 1, 0); +// } + } } float imageWidth = _texture->getWidth(); @@ -114,7 +138,7 @@ void BillboardOverlay::render(RenderArgs* args) { Transform transform = _transform; transform.postScale(glm::vec3(getDimensions(), 1.0f)); transform.setRotation(rotation); - + batch->setModelTransform(transform); batch->setResourceTexture(0, _texture->getGPUTexture()); @@ -171,6 +195,21 @@ void BillboardOverlay::setProperties(const QScriptValue &properties) { if (isFacingAvatarValue.isValid()) { _isFacingAvatar = isFacingAvatarValue.toVariant().toBool(); } + + QScriptValue offsetPosition = properties.property("offsetPosition"); + if (offsetPosition.isValid()) { + QScriptValue x = offsetPosition.property("x"); + QScriptValue y = offsetPosition.property("y"); + QScriptValue z = offsetPosition.property("z"); + + if (x.isValid() && y.isValid() && z.isValid()) { + glm::vec3 newPosition; + newPosition.x = x.toVariant().toFloat(); + newPosition.y = y.toVariant().toFloat(); + newPosition.z = z.toVariant().toFloat(); + setOffsetPosition(newPosition); + } + } } QScriptValue BillboardOverlay::getProperty(const QString& property) { @@ -183,6 +222,9 @@ QScriptValue BillboardOverlay::getProperty(const QString& property) { if (property == "isFacingAvatar") { return _isFacingAvatar; } + if (property == "offsetPosition") { + return vec3toScriptValue(_scriptEngine, getOffsetPosition()); + } return Planar3DOverlay::getProperty(property); } diff --git a/interface/src/ui/overlays/BillboardOverlay.h b/interface/src/ui/overlays/BillboardOverlay.h index 15be0419a9..f7bbfd1817 100644 --- a/interface/src/ui/overlays/BillboardOverlay.h +++ b/interface/src/ui/overlays/BillboardOverlay.h @@ -15,8 +15,9 @@ #include #include "Planar3DOverlay.h" +#include "PanelAttachable.h" -class BillboardOverlay : public Planar3DOverlay { +class BillboardOverlay : public Planar3DOverlay, public PanelAttachable { Q_OBJECT public: BillboardOverlay(); @@ -24,6 +25,8 @@ public: virtual void render(RenderArgs* args); + virtual void update(float deltatime); + // setters void setURL(const QString& url); void setIsFacingAvatar(bool isFacingAvatar) { _isFacingAvatar = isFacingAvatar; } diff --git a/interface/src/ui/overlays/FloatingUIPanel.cpp b/interface/src/ui/overlays/FloatingUIPanel.cpp new file mode 100644 index 0000000000..e655a75f07 --- /dev/null +++ b/interface/src/ui/overlays/FloatingUIPanel.cpp @@ -0,0 +1,89 @@ +// +// FloatingUIPanel.cpp +// interface/src/ui/overlays +// +// Created by Zander Otavka on 7/2/15. +// 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 "FloatingUIPanel.h" + +#include +#include + +#include "Application.h" + + +glm::quat FloatingUIPanel::getOffsetRotation() const { + if (getActualOffsetRotation() == glm::quat(0, 0, 0, 0)) { + return Application::getInstance()->getCamera()->getOrientation() * glm::quat(0, 0, 1, 0); + } + return getActualOffsetRotation(); +} + +QScriptValue FloatingUIPanel::getProperty(const QString &property) { + if (property == "offsetPosition") { + return vec3toScriptValue(_scriptEngine, getOffsetPosition()); + } + if (property == "offsetRotation") { + return quatToScriptValue(_scriptEngine, getActualOffsetRotation()); + } + if (property == "facingRotation") { + return quatToScriptValue(_scriptEngine, getFacingRotation()); + } + + return QScriptValue(); +} + +void FloatingUIPanel::setProperties(const QScriptValue &properties) { + QScriptValue offsetPosition = properties.property("offsetPosition"); + if (offsetPosition.isValid()) { + QScriptValue x = offsetPosition.property("x"); + QScriptValue y = offsetPosition.property("y"); + QScriptValue z = offsetPosition.property("z"); + if (x.isValid() && y.isValid() && z.isValid()) { + glm::vec3 newPosition; + newPosition.x = x.toVariant().toFloat(); + newPosition.y = y.toVariant().toFloat(); + newPosition.z = z.toVariant().toFloat(); + setOffsetPosition(newPosition); + } + } + + QScriptValue offsetRotation = properties.property("offsetRotation"); + if (offsetRotation.isValid()) { + QScriptValue x = offsetRotation.property("x"); + QScriptValue y = offsetRotation.property("y"); + QScriptValue z = offsetRotation.property("z"); + QScriptValue w = offsetRotation.property("w"); + + if (x.isValid() && y.isValid() && z.isValid() && w.isValid()) { + glm::quat newRotation; + newRotation.x = x.toVariant().toFloat(); + newRotation.y = y.toVariant().toFloat(); + newRotation.z = z.toVariant().toFloat(); + newRotation.w = w.toVariant().toFloat(); + setOffsetRotation(newRotation); + } + } + + QScriptValue facingRotation = properties.property("facingRotation"); + if (offsetRotation.isValid()) { + QScriptValue x = facingRotation.property("x"); + QScriptValue y = facingRotation.property("y"); + QScriptValue z = facingRotation.property("z"); + QScriptValue w = facingRotation.property("w"); + + if (x.isValid() && y.isValid() && z.isValid() && w.isValid()) { + glm::quat newRotation; + newRotation.x = x.toVariant().toFloat(); + newRotation.y = y.toVariant().toFloat(); + newRotation.z = z.toVariant().toFloat(); + newRotation.w = w.toVariant().toFloat(); + setFacingRotation(newRotation); + } + } +} diff --git a/interface/src/ui/overlays/FloatingUIPanel.h b/interface/src/ui/overlays/FloatingUIPanel.h new file mode 100644 index 0000000000..7f8d42eb5b --- /dev/null +++ b/interface/src/ui/overlays/FloatingUIPanel.h @@ -0,0 +1,47 @@ +// +// FloatingUIPanel.h +// interface/src/ui/overlays +// +// Created by Zander Otavka on 7/2/15. +// 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_FloatingUIPanel_h +#define hifi_FloatingUIPanel_h + +#include +#include +#include + +class FloatingUIPanel : public QObject { + Q_OBJECT +public: + typedef std::shared_ptr Pointer; + + QList children; + + void init(QScriptEngine* scriptEngine) { _scriptEngine = scriptEngine; } + + glm::vec3 getOffsetPosition() const { return _offsetPosition; } + glm::quat getOffsetRotation() const; + glm::quat getActualOffsetRotation() const { return _offsetRotation; } + glm::quat getFacingRotation() const { return _facingRotation; } + + void setOffsetPosition(glm::vec3 position) { _offsetPosition = position; }; + void setOffsetRotation(glm::quat rotation) { _offsetRotation = rotation; }; + void setFacingRotation(glm::quat rotation) { _facingRotation = rotation; }; + + QScriptValue getProperty(const QString& property); + void setProperties(const QScriptValue& properties); + +private: + glm::vec3 _offsetPosition = glm::vec3(0, 0, 0); + glm::quat _offsetRotation = glm::quat(0, 0, 0, 0); + glm::quat _facingRotation = glm::quat(1, 0, 0, 0); + QScriptEngine* _scriptEngine; +}; + +#endif // hifi_FloatingUIPanel_h diff --git a/interface/src/ui/overlays/Overlays.cpp b/interface/src/ui/overlays/Overlays.cpp index ff218db844..fe489847ee 100644 --- a/interface/src/ui/overlays/Overlays.cpp +++ b/interface/src/ui/overlays/Overlays.cpp @@ -48,6 +48,7 @@ Overlays::~Overlays() { } _overlaysHUD.clear(); _overlaysWorld.clear(); + _panels.clear(); } cleanupOverlaysToDelete(); @@ -124,11 +125,36 @@ void Overlays::renderHUD(RenderArgs* renderArgs) { } } +Overlay::Pointer Overlays::getOverlay(unsigned int id) const { + if (_overlaysHUD.contains(id)) { + return _overlaysHUD[id]; + } + if (_overlaysWorld.contains(id)) { + return _overlaysWorld[id]; + } + return nullptr; +} + +void Overlays::setAttachedPanel(Overlay* overlay, unsigned int overlayId, const QScriptValue& property) { + if (PanelAttachable* attachable = dynamic_cast(overlay)) { + if (property.isValid()) { + unsigned int attachedPanelId = property.toVariant().toUInt(); + FloatingUIPanel* panel = nullptr; + if (_panels.contains(attachedPanelId)) { + panel = _panels[attachedPanelId].get(); + panel->children.append(overlayId); + attachable->setAttachedPanel(panel); + } else { + attachable->getAttachedPanel()->children.removeAll(overlayId); + attachable->setAttachedPanel(nullptr); + } + } + } +} + unsigned int Overlays::addOverlay(const QString& type, const QScriptValue& properties) { - unsigned int thisID = 0; Overlay* thisOverlay = NULL; - - bool created = true; + if (type == "image") { thisOverlay = new ImageOverlay(); } else if (type == "text") { @@ -153,16 +179,15 @@ unsigned int Overlays::addOverlay(const QString& type, const QScriptValue& prope thisOverlay = new ModelOverlay(); } else if (type == "billboard") { thisOverlay = new BillboardOverlay(); - } else { - created = false; } - if (created) { + if (thisOverlay) { thisOverlay->setProperties(properties); - thisID = addOverlay(thisOverlay); + unsigned int overlayId = addOverlay(thisOverlay); + setAttachedPanel(thisOverlay, overlayId, properties.property("attachedPanel")); + return overlayId; } - - return thisID; + return 0; } unsigned int Overlays::addOverlay(Overlay* overlay) { @@ -189,17 +214,12 @@ unsigned int Overlays::addOverlay(Overlay* overlay) { } else { _overlaysHUD[thisID] = overlayPointer; } - + return thisID; } unsigned int Overlays::cloneOverlay(unsigned int id) { - Overlay::Pointer thisOverlay = NULL; - if (_overlaysHUD.contains(id)) { - thisOverlay = _overlaysHUD[id]; - } else if (_overlaysWorld.contains(id)) { - thisOverlay = _overlaysWorld[id]; - } + Overlay::Pointer thisOverlay = getOverlay(id); if (thisOverlay) { return addOverlay(thisOverlay->createClone()); @@ -210,14 +230,8 @@ unsigned int Overlays::cloneOverlay(unsigned int id) { bool Overlays::editOverlay(unsigned int id, const QScriptValue& properties) { QWriteLocker lock(&_lock); - Overlay::Pointer thisOverlay; - - if (_overlaysHUD.contains(id)) { - thisOverlay = _overlaysHUD[id]; - } else if (_overlaysWorld.contains(id)) { - thisOverlay = _overlaysWorld[id]; - } + Overlay::Pointer thisOverlay = getOverlay(id); if (thisOverlay) { if (thisOverlay->is3D()) { auto overlay3D = std::static_pointer_cast(thisOverlay); @@ -239,6 +253,8 @@ bool Overlays::editOverlay(unsigned int id, const QScriptValue& properties) { thisOverlay->setProperties(properties); } + setAttachedPanel(thisOverlay.get(), id, properties.property("attachedPanel")); + return true; } return false; @@ -302,15 +318,18 @@ unsigned int Overlays::getOverlayAtPoint(const glm::vec2& point) { OverlayPropertyResult Overlays::getProperty(unsigned int id, const QString& property) { OverlayPropertyResult result; - Overlay::Pointer thisOverlay; + Overlay::Pointer thisOverlay = getOverlay(id); QReadLocker lock(&_lock); - if (_overlaysHUD.contains(id)) { - thisOverlay = _overlaysHUD[id]; - } else if (_overlaysWorld.contains(id)) { - thisOverlay = _overlaysWorld[id]; - } if (thisOverlay) { - result.value = thisOverlay->getProperty(property); + if (property == "attachedPanel") { + if (FloatingUIPanel* panel = dynamic_cast(thisOverlay.get())) { + result.value = _panels.key(FloatingUIPanel::Pointer(panel)); + } else { + result.value = 0; + } + } else { + result.value = thisOverlay->getProperty(property); + } } return result; } @@ -456,12 +475,8 @@ void RayToOverlayIntersectionResultFromScriptValue(const QScriptValue& object, R bool Overlays::isLoaded(unsigned int id) { QReadLocker lock(&_lock); - Overlay::Pointer thisOverlay = NULL; - if (_overlaysHUD.contains(id)) { - thisOverlay = _overlaysHUD[id]; - } else if (_overlaysWorld.contains(id)) { - thisOverlay = _overlaysWorld[id]; - } else { + Overlay::Pointer thisOverlay = getOverlay(id); + if (!thisOverlay) { return false; // not found } return thisOverlay->isLoaded(); @@ -483,3 +498,55 @@ QSizeF Overlays::textSize(unsigned int id, const QString& text) const { } return QSizeF(0.0f, 0.0f); } + +unsigned int Overlays::addPanel(FloatingUIPanel* panel) { + QWriteLocker lock(&_lock); + + FloatingUIPanel::Pointer panelPointer(panel); + unsigned int thisID = _nextOverlayID; + _nextOverlayID++; + _panels[thisID] = panelPointer; + + return thisID; +} + +unsigned int Overlays::addPanel(const QScriptValue& properties) { + FloatingUIPanel* panel = new FloatingUIPanel(); + panel->init(_scriptEngine); + panel->setProperties(properties); + return addPanel(panel); +} + +void Overlays::editPanel(unsigned int panelId, const QScriptValue& properties) { + if (_panels.contains(panelId)) { + _panels[panelId]->setProperties(properties); + } +} + +OverlayPropertyResult Overlays::getPanelProperty(unsigned int panelId, const QString& property) { + OverlayPropertyResult result; + if (_panels.contains(panelId)) { + FloatingUIPanel::Pointer thisPanel = _panels[panelId]; + QReadLocker lock(&_lock); + result.value = thisPanel->getProperty(property); + } + return result; +} + + +void Overlays::deletePanel(unsigned int panelId) { + FloatingUIPanel::Pointer panelToDelete; + + { + QWriteLocker lock(&_lock); + if (_panels.contains(panelId)) { + panelToDelete = _panels.take(panelId); + } else { + return; + } + } + + while (!panelToDelete->children.isEmpty()) { + deleteOverlay(panelToDelete->children.takeLast()); + } +} diff --git a/interface/src/ui/overlays/Overlays.h b/interface/src/ui/overlays/Overlays.h index cd5b0f1d10..2ba2ec8f45 100644 --- a/interface/src/ui/overlays/Overlays.h +++ b/interface/src/ui/overlays/Overlays.h @@ -16,6 +16,9 @@ #include "Overlay.h" +#include "FloatingUIPanel.h" +#include "PanelAttachable.h" + class PickRay; class OverlayPropertyResult { @@ -90,12 +93,33 @@ public slots: /// overlay; in meters if it is a 3D text overlay QSizeF textSize(unsigned int id, const QString& text) const; + + /// adds a panel that has already been created + unsigned int addPanel(FloatingUIPanel* panel); + + /// creates and adds a panel based on a set of properties + unsigned int addPanel(const QScriptValue& properties); + + /// edit the properties of a panel + void editPanel(unsigned int panelId, const QScriptValue& properties); + + /// get a property of a panel + OverlayPropertyResult getPanelProperty(unsigned int panelId, const QString& property); + + /// deletes a panel and all child overlays + void deletePanel(unsigned int panelId); + private: void cleanupOverlaysToDelete(); + Overlay::Pointer getOverlay(unsigned int id) const; + void setAttachedPanel(Overlay* overlay, unsigned int overlayId, const QScriptValue& property); + QMap _overlaysHUD; QMap _overlaysWorld; + QMap _panels; QList _overlaysToDelete; unsigned int _nextOverlayID; + QReadWriteLock _lock; QReadWriteLock _deleteLock; QScriptEngine* _scriptEngine; diff --git a/interface/src/ui/overlays/PanelAttachable.h b/interface/src/ui/overlays/PanelAttachable.h new file mode 100644 index 0000000000..c364d62fa7 --- /dev/null +++ b/interface/src/ui/overlays/PanelAttachable.h @@ -0,0 +1,44 @@ +// +// PanelAttachable.h +// interface/src/ui/overlays +// +// Created by Zander Otavka on 7/1/15. +// 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_PanelAttachable_h +#define hifi_PanelAttachable_h + +#include "FloatingUIPanel.h" + +#include + +class PanelAttachable { +public: + glm::vec3 getOffsetPosition() const { return _offsetPosition; } + void setOffsetPosition(glm::vec3 position) { _offsetPosition = position; } + + FloatingUIPanel* getAttachedPanel() const { return _attachedPanel; } + void setAttachedPanel(FloatingUIPanel* panel) { _attachedPanel = panel; } + + glm::vec3 getTranslatedPosition(glm::vec3 avatarPosition) { + if (getAttachedPanel()) { + glm::vec3 totalOffsetPosition = + getAttachedPanel()->getFacingRotation() * getOffsetPosition() + + getAttachedPanel()->getOffsetPosition(); + + return getAttachedPanel()->getOffsetRotation() * totalOffsetPosition + + avatarPosition; + } + return glm::vec3(); + } + +private: + FloatingUIPanel* _attachedPanel = nullptr; + glm::vec3 _offsetPosition = glm::vec3(0, 0, 0); +}; + +#endif // hifi_PanelAttachable_h From ed7fc07ab1140c2db7f7fdd22e060e4db27cb595 Mon Sep 17 00:00:00 2001 From: Zander Otavka Date: Wed, 15 Jul 2015 14:08:09 -0700 Subject: [PATCH 29/65] Fix cloning bug for BillboardOverlay. --- .../src/ui/overlays/BillboardOverlay.cpp | 1 + interface/src/ui/overlays/PanelAttachable.cpp | 33 +++++++++++++++++++ interface/src/ui/overlays/PanelAttachable.h | 17 +++------- interface/src/ui/overlays/Planar3DOverlay.cpp | 9 ++++- interface/src/ui/overlays/Planar3DOverlay.h | 4 +-- 5 files changed, 49 insertions(+), 15 deletions(-) create mode 100644 interface/src/ui/overlays/PanelAttachable.cpp diff --git a/interface/src/ui/overlays/BillboardOverlay.cpp b/interface/src/ui/overlays/BillboardOverlay.cpp index 9e1a9a44de..18d0307743 100644 --- a/interface/src/ui/overlays/BillboardOverlay.cpp +++ b/interface/src/ui/overlays/BillboardOverlay.cpp @@ -29,6 +29,7 @@ BillboardOverlay::BillboardOverlay() { BillboardOverlay::BillboardOverlay(const BillboardOverlay* billboardOverlay) : Planar3DOverlay(billboardOverlay), + PanelAttachable(billboardOverlay), _url(billboardOverlay->_url), _texture(billboardOverlay->_texture), _fromImage(billboardOverlay->_fromImage), diff --git a/interface/src/ui/overlays/PanelAttachable.cpp b/interface/src/ui/overlays/PanelAttachable.cpp new file mode 100644 index 0000000000..172880db97 --- /dev/null +++ b/interface/src/ui/overlays/PanelAttachable.cpp @@ -0,0 +1,33 @@ +// +// PanelAttachable.cpp +// hifi +// +// Created by Zander Otavka on 7/15/15. +// +// + +#include "PanelAttachable.h" + +PanelAttachable::PanelAttachable() : + _attachedPanel(nullptr), + _offsetPosition(glm::vec3()) +{ +} + +PanelAttachable::PanelAttachable(const PanelAttachable* panelAttachable) : + _attachedPanel(panelAttachable->_attachedPanel), + _offsetPosition(panelAttachable->_offsetPosition) +{ +} + +glm::vec3 PanelAttachable::getTranslatedPosition(glm::vec3 avatarPosition) const { + if (getAttachedPanel()) { + glm::vec3 totalOffsetPosition = + getAttachedPanel()->getFacingRotation() * getOffsetPosition() + + getAttachedPanel()->getOffsetPosition(); + + return getAttachedPanel()->getOffsetRotation() * totalOffsetPosition + + avatarPosition; + } + return glm::vec3(); +} diff --git a/interface/src/ui/overlays/PanelAttachable.h b/interface/src/ui/overlays/PanelAttachable.h index c364d62fa7..29b0673157 100644 --- a/interface/src/ui/overlays/PanelAttachable.h +++ b/interface/src/ui/overlays/PanelAttachable.h @@ -18,26 +18,19 @@ class PanelAttachable { public: + PanelAttachable(); + PanelAttachable(const PanelAttachable* panelAttachable); + glm::vec3 getOffsetPosition() const { return _offsetPosition; } void setOffsetPosition(glm::vec3 position) { _offsetPosition = position; } FloatingUIPanel* getAttachedPanel() const { return _attachedPanel; } void setAttachedPanel(FloatingUIPanel* panel) { _attachedPanel = panel; } - glm::vec3 getTranslatedPosition(glm::vec3 avatarPosition) { - if (getAttachedPanel()) { - glm::vec3 totalOffsetPosition = - getAttachedPanel()->getFacingRotation() * getOffsetPosition() + - getAttachedPanel()->getOffsetPosition(); - - return getAttachedPanel()->getOffsetRotation() * totalOffsetPosition + - avatarPosition; - } - return glm::vec3(); - } + glm::vec3 getTranslatedPosition(glm::vec3 avatarPosition) const; private: - FloatingUIPanel* _attachedPanel = nullptr; + FloatingUIPanel* _attachedPanel; glm::vec3 _offsetPosition = glm::vec3(0, 0, 0); }; diff --git a/interface/src/ui/overlays/Planar3DOverlay.cpp b/interface/src/ui/overlays/Planar3DOverlay.cpp index 0ca092ba23..354991b596 100644 --- a/interface/src/ui/overlays/Planar3DOverlay.cpp +++ b/interface/src/ui/overlays/Planar3DOverlay.cpp @@ -14,8 +14,15 @@ #include #include +Planar3DOverlay::Planar3DOverlay() : + Base3DOverlay(), + _dimensions{1.0f, 1.0f} +{ +} + Planar3DOverlay::Planar3DOverlay(const Planar3DOverlay* planar3DOverlay) : - Base3DOverlay(planar3DOverlay) + Base3DOverlay(planar3DOverlay), + _dimensions(planar3DOverlay->_dimensions) { } diff --git a/interface/src/ui/overlays/Planar3DOverlay.h b/interface/src/ui/overlays/Planar3DOverlay.h index fe8c513efd..08a7121e91 100644 --- a/interface/src/ui/overlays/Planar3DOverlay.h +++ b/interface/src/ui/overlays/Planar3DOverlay.h @@ -17,7 +17,7 @@ class Planar3DOverlay : public Base3DOverlay { Q_OBJECT public: - Planar3DOverlay() {} + Planar3DOverlay(); Planar3DOverlay(const Planar3DOverlay* planar3DOverlay); AABox getBounds() const; @@ -32,7 +32,7 @@ public: virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face); protected: - glm::vec2 _dimensions{1.0f, 1.0f}; + glm::vec2 _dimensions; }; From 377a1a54aeaa8e9343325b7b589b056d6a8d242d Mon Sep 17 00:00:00 2001 From: Zander Otavka Date: Wed, 15 Jul 2015 15:32:14 -0700 Subject: [PATCH 30/65] Add abstraction layer for Overlays.h. `examples/libraries/overlayUtils.js` allows you to manage overlays in an object oriented manner. Instead of: var billboard = Overlays.addOverlay("billboard", { visible: false }); ... Overlays.editOverlay(billboard, { visible: true }); ... Overlays.deleteOverlay(billboard); You can now do: var billboard = new BillboardOverlay({ visible: false }); ... billboard.visible = true; ... billboard.destroy(); --- examples/example/ui/floatingUIExample.js | 98 ++++----- examples/libraries/overlayUtils.js | 257 ++++++++++++++++++++++- interface/src/ui/overlays/Overlays.h | 6 + 3 files changed, 298 insertions(+), 63 deletions(-) diff --git a/examples/example/ui/floatingUIExample.js b/examples/example/ui/floatingUIExample.js index 09deca4ec7..3b555efce3 100644 --- a/examples/example/ui/floatingUIExample.js +++ b/examples/example/ui/floatingUIExample.js @@ -9,7 +9,10 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -Script.include(["../../libraries/globals.js"]); +Script.include([ + "../../libraries/globals.js", + "../../libraries/overlayUtils.js", +]); var BG_IMAGE_URL = HIFI_PUBLIC_BUCKET + "images/card-bg.svg"; var RED_DOT_IMAGE_URL = HIFI_PUBLIC_BUCKET + "images/red-dot.svg"; @@ -24,13 +27,11 @@ function isBlank(rotation) { rotation.w == BLANK_ROTATION.w; } -var panel = Overlays.addPanel({ +var panel = new FloatingUIPanel({ offsetPosition: { x: 0, y: 0, z: 1 }, }); -var panelChildren = []; - -var bg = Overlays.addOverlay("billboard", { +var bg = panel.addChild(new BillboardOverlay({ url: BG_IMAGE_URL, dimensions: { x: 0.5, @@ -40,11 +41,9 @@ var bg = Overlays.addOverlay("billboard", { visible: true, alpha: 1.0, ignoreRayIntersection: false, - attachedPanel: panel, -}); -panelChildren.push(bg); +})); -var redDot = Overlays.addOverlay("billboard", { +var redDot = panel.addChild(new BillboardOverlay({ url: RED_DOT_IMAGE_URL, dimensions: { x: 0.1, @@ -54,16 +53,14 @@ var redDot = Overlays.addOverlay("billboard", { visible: true, alpha: 1.0, ignoreRayIntersection: false, - attachedPanel: panel, offsetPosition: { x: -0.15, y: -0.15, z: -0.001 } -}); -panelChildren.push(redDot); +})); -var redDot2 = Overlays.addOverlay("billboard", { +var redDot2 = panel.addChild(new BillboardOverlay({ url: RED_DOT_IMAGE_URL, dimensions: { x: 0.1, @@ -73,16 +70,14 @@ var redDot2 = Overlays.addOverlay("billboard", { visible: true, alpha: 1.0, ignoreRayIntersection: false, - attachedPanel: panel, offsetPosition: { x: -0.15, y: 0, z: -0.001 } -}); -panelChildren.push(redDot2); +})); -var blueSquare = Overlays.addOverlay("billboard", { +var blueSquare = panel.addChild(new BillboardOverlay({ url: BLUE_SQUARE_IMAGE_URL, dimensions: { x: 0.1, @@ -92,16 +87,14 @@ var blueSquare = Overlays.addOverlay("billboard", { visible: true, alpha: 1.0, ignoreRayIntersection: false, - attachedPanel: panel, offsetPosition: { x: 0.1, y: 0, z: -0.001 } -}); -panelChildren.push(blueSquare); +})); -var blueSquare2 = Overlays.addOverlay("billboard", { +var blueSquare2 = panel.addChild(new BillboardOverlay({ url: BLUE_SQUARE_IMAGE_URL, dimensions: { x: 0.1, @@ -111,63 +104,46 @@ var blueSquare2 = Overlays.addOverlay("billboard", { visible: true, alpha: 1.0, ignoreRayIntersection: false, - attachedPanel: panel, offsetPosition: { x: 0.1, y: 0.11, z: -0.001 } -}); -panelChildren.push(blueSquare2); +})); -var blueSquare3 = Overlays.addOverlay("billboard", { - url: BLUE_SQUARE_IMAGE_URL, - dimensions: { - x: 0.1, - y: 0.1, - }, - isFacingAvatar: false, - visible: true, - alpha: 1.0, - ignoreRayIntersection: false, - attachedPanel: panel, - offsetPosition: { - x: -0.01, - y: 0.11, - z: -0.001 - } -}); -panelChildren.push(blueSquare3); +var blueSquare3 = blueSquare2.clone(); +blueSquare3.offsetPosition = { + x: -0.01, + y: 0.11, + z: -0.001 +}; +blueSquare3.ignoreRayIntersection = false; Controller.mousePressEvent.connect(function(event) { if (event.isRightButton) { - var newOffsetRotation = BLANK_ROTATION; - if (isBlank(Overlays.getPanelProperty(panel, "offsetRotation"))) { + var newOffsetRotation; + print(JSON.stringify(panel.offsetRotation)) + if (isBlank(panel.offsetRotation)) { newOffsetRotation = Quat.multiply(MyAvatar.orientation, { x: 0, y: 1, z: 0, w: 0 }); + } else { + newOffsetRotation = BLANK_ROTATION; } - Overlays.editPanel(panel, { - offsetRotation: newOffsetRotation - }); + panel.offsetRotation = newOffsetRotation; } else if (event.isLeftButton) { var pickRay = Camera.computePickRay(event.x, event.y) - var rayPickResult = Overlays.findRayIntersection(pickRay); - print(String(rayPickResult.overlayID)); - if (rayPickResult.intersects) { - for (var i in panelChildren) { - if (panelChildren[i] == rayPickResult.overlayID) { - var oldPos = Overlays.getProperty(rayPickResult.overlayID, "offsetPosition"); - var newPos = { - x: Number(oldPos.x), - y: Number(oldPos.y), - z: Number(oldPos.z) + 0.1 - } - Overlays.editOverlay(rayPickResult.overlayID, { offsetPosition: newPos }); - } + var overlay = panel.findRayIntersection(pickRay); + if (overlay) { + var oldPos = overlay.offsetPosition; + var newPos = { + x: Number(oldPos.x), + y: Number(oldPos.y), + z: Number(oldPos.z) + 0.1 } + overlay.offsetPosition = newPos; } } }); Script.scriptEnding.connect(function() { - Overlays.deletePanel(panel); + panel.destroy(); }); \ No newline at end of file diff --git a/examples/libraries/overlayUtils.js b/examples/libraries/overlayUtils.js index 636ea40825..3acab0103b 100644 --- a/examples/libraries/overlayUtils.js +++ b/examples/libraries/overlayUtils.js @@ -1,6 +1,39 @@ +// +// overlayUtils.js +// examples/libraries +// +// Modified by Zander Otavka on 7/15/15 +// Copyright 2014 High Fidelity, Inc. +// +// Manage overlays with object oriented goodness, instead of ugly `Overlays.h` methods. +// Instead of: +// +// var billboard = Overlays.addOverlay("billboard", { visible: false }); +// ... +// Overlays.editOverlay(billboard, { visible: true }); +// ... +// Overlays.deleteOverlay(billboard); +// +// You can now do: +// +// var billboard = new BillboardOverlay({ visible: false }); +// ... +// billboard.visible = true; +// ... +// billboard.destroy(); +// +// See more on usage below. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + + /** - * OverlayGroup provides a way to create composite overlays and control their - * position relative to a settable rootPosition and rootRotation. + * DEPRECATION WARNING: Will be deprecated soon in favor of FloatingUIPanel. + * + * OverlayGroup provides a way to create composite overlays and control their + * position relative to a settable rootPosition and rootRotation. */ OverlayGroup = function(opts) { var that = {}; @@ -62,3 +95,223 @@ OverlayGroup = function(opts) { return that; }; + + +/** + * Object oriented abstraction layer for overlays. + * + * Usage: + * // Create an overlay + * var billboard = new BillboardOverlay({ + * visible: true, + * isFacingAvatar: true, + * ignoreRayIntersections: false + * }); + * + * // Get a property + * var isVisible = billboard.visible; + * + * // Set a single property + * billboard.position = { x: 1, y: 3, z: 2 }; + * + * // Set multiple properties at the same time + * billboard.setProperties({ + * url: "http://images.com/overlayImage.jpg", + * dimensions: { x: 2, y: 2 } + * }); + * + * // Clone an overlay + * var clonedBillboard = billboard.clone(); + * + * // Remove an overlay from the world + * billboard.destroy(); + * + * // Remember, there is a poor orphaned JavaScript object left behind. You should remove any + * // references to it so you don't accidentally try to modify an overlay that isn't there. + * billboard = undefined; + */ +(function() { + var ABSTRACT = null; + + function generateOverlayClass(superclass, type, properties) { + var that; + if (type == ABSTRACT) { + that = function(type, params) { + superclass.apply(this, [type, params]); + }; + } else { + that = function(params) { + superclass.apply(this, [type, params]); + }; + } + + that.prototype = new superclass(); + that.prototype.constructor = that; + + properties.forEach(function(prop) { + Object.defineProperty(that.prototype, prop, { + get: function() { + return Overlays.getProperty(this._id, prop); + }, + set: function(newValue) { + var keyValuePair = {}; + keyValuePair[prop] = newValue; + this.setProperties(keyValuePair); + }, + configurable: true + }); + }); + + return that; + } + + + // Supports multiple inheritance of properties. Just `concat` them onto the end of the + // properties list. + var PANEL_ATTACHABLE_FIELDS = ["attachedPanel"]; + + // TODO: finish exposing all overlay classes. + + var Overlay = (function() { + var BaseOverlay = (function() { + var that = function(type, params) { + Object.apply(this, []); + if (type && params) { + this._type = type; + this._id = Overlays.addOverlay(type, params); + } else { + this._type = ""; + this._id = 0; + } + this._attachedPanelPointer = null; + }; + + that.prototype = new Object(); + that.prototype.constructor = that; + + Object.defineProperty(that.prototype, "overlayType", { + get: function() { + return this._type; + } + }); + + that.prototype.setProperties = function(properties) { + Overlays.editOverlay(this._id, properties); + }; + + that.prototype.clone = function() { + var clone = new this.constructor(); + clone._type = this._type; + clone._id = Overlays.cloneOverlay(this._id); + if (this._attachedPanelPointer) { + this._attachedPanelPointer.addChild(clone); + } + return clone; + }; + + that.prototype.destroy = function() { + Overlays.deleteOverlay(this._id); + }; + + return that; + }()); + + return generateOverlayClass(BaseOverlay, ABSTRACT, [ + "alpha", "glowLevel", "pulseMax", "pulseMin", "pulsePeriod", "glowLevelPulse", + "alphaPulse", "colorPulse", "visible", "anchor" + ]); + }()); + + var Base3DOverlay = generateOverlayClass(Overlay, ABSTRACT, [ + "position", "lineWidth", "rotation", "isSolid", "isFilled", "isWire", "isDashedLine", + "ignoreRayIntersection", "drawInFront", "drawOnHUD" + ]); + + var Planar3DOverlay = generateOverlayClass(Base3DOverlay, ABSTRACT, [ + "dimensions" + ]); + + BillboardOverlay = generateOverlayClass(Planar3DOverlay, "billboard", [ + "url", "subImage", "isFacingAvatar", "offsetPosition" + ].concat(PANEL_ATTACHABLE_FIELDS)); +}()); + + +/** + * Object oriented abstraction layer for panels. + */ +FloatingUIPanel = (function() { + var that = function(params) { + this._id = Overlays.addPanel(params); + this._children = []; + }; + + var FIELDS = ["offsetPosition", "offsetRotation", "facingRotation"]; + FIELDS.forEach(function(prop) { + Object.defineProperty(that.prototype, prop, { + get: function() { + return Overlays.getPanelProperty(this._id, prop); + }, + set: function(newValue) { + var keyValuePair = {}; + keyValuePair[prop] = newValue; + this.setProperties(keyValuePair); + }, + configurable: false + }); + }); + + Object.defineProperty(that.prototype, "children", { + get: function() { + return this._children.slice(); + } + }) + + that.prototype.addChild = function(overlay) { + overlay.attachedPanel = this._id; + overlay._attachedPanelPointer = this; + this._children.push(overlay); + return overlay; + }; + + that.prototype.removeChild = function(overlay) { + var i = this._children.indexOf(overlay); + if (i >= 0) { + overlay.attachedPanel = 0; + overlay._attachedPanelPointer = null; + this._children.splice(i, 1); + } + }; + + that.prototype.setVisible = function(visible) { + for (var i in this._children) { + this._children[i].visible = visible; + } + }; + + that.prototype.setProperties = function(properties) { + Overlays.editPanel(this._id, properties); + }; + + that.prototype.destroy = function() { + Overlays.deletePanel(this._id); + var i = _panels.indexOf(this); + if (i >= 0) { + _panels.splice(i, 1); + } + }; + + that.prototype.findRayIntersection = function(pickRay) { + var rayPickResult = Overlays.findRayIntersection(pickRay); + if (rayPickResult.intersects) { + for (var i in this._children) { + if (this._children[i]._id == rayPickResult.overlayID) { + return this._children[i]; + } + } + } + return null; + }; + + return that; +}()); diff --git a/interface/src/ui/overlays/Overlays.h b/interface/src/ui/overlays/Overlays.h index 2ba2ec8f45..80da2e6db1 100644 --- a/interface/src/ui/overlays/Overlays.h +++ b/interface/src/ui/overlays/Overlays.h @@ -2,8 +2,14 @@ // Overlays.h // interface/src/ui/overlays // +// Modified by Zander Otavka on 7/15/15 // Copyright 2014 High Fidelity, Inc. // +// Exposes methods for managing `Overlay`s and `FloatingUIPanel`s to scripts. +// +// YOU SHOULD NOT USE `Overlays` DIRECTLY, unless you like pain and deprecation. Instead, use the +// object oriented abstraction layer found in `examples/libraries/overlayUtils.js`. +// // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // From c4cb6fba744b2eea540918fb755f91ddc0c162bf Mon Sep 17 00:00:00 2001 From: Zander Otavka Date: Fri, 17 Jul 2015 14:29:53 -0700 Subject: [PATCH 31/65] Improve BillboardOverlay::findRayIntersection. --- .../src/ui/overlays/BillboardOverlay.cpp | 94 ++++++++----------- interface/src/ui/overlays/BillboardOverlay.h | 1 + interface/src/ui/overlays/PanelAttachable.cpp | 17 ++++ interface/src/ui/overlays/PanelAttachable.h | 1 + 4 files changed, 60 insertions(+), 53 deletions(-) diff --git a/interface/src/ui/overlays/BillboardOverlay.cpp b/interface/src/ui/overlays/BillboardOverlay.cpp index 18d0307743..be8fab6362 100644 --- a/interface/src/ui/overlays/BillboardOverlay.cpp +++ b/interface/src/ui/overlays/BillboardOverlay.cpp @@ -38,9 +38,11 @@ BillboardOverlay::BillboardOverlay(const BillboardOverlay* billboardOverlay) : } void BillboardOverlay::update(float deltatime) { - glm::vec3 newPos = getTranslatedPosition(Application::getInstance()->getAvatarPosition()); - if (newPos != glm::vec3()) { - setPosition(newPos); + if (getVisible()) { + glm::vec3 newPos = getTranslatedPosition(Application::getInstance()->getAvatarPosition()); + if (newPos != glm::vec3()) { + setPosition(newPos); + } } } @@ -59,46 +61,7 @@ void BillboardOverlay::render(RenderArgs* args) { setPosition(newPos); } - glm::quat rotation; - if (_isFacingAvatar) { - // LOL, quaternions are hard. -// glm::vec3 dPos = getPosition() - args->_viewFrustum->getPosition(); -// dPos = glm::normalize(dPos); -// rotation = glm::quat(0, dPos.x, dPos.y, dPos.z); -// float horizontal = glm::sqrt(dPos.x * dPos.x + dPos.y + dPos.y); -// glm::vec3 zAxis = glm::vec3(0, 0, 1); -// rotation = rotationBetween(zAxis, dPos); -// glm::vec3 euler = safeEulerAngles(rotationBetween(zAxis, dPos)); -// rotation = glm::quat(glm::vec3(euler.x, euler.y, 0)); -// float yaw = (dPos.x == 0.0f && dPos.z == 0.0f) ? 0.0f : glm::atan(dPos.x, dPos.z); -// glm::quat yawQuat = glm::quat(glm::vec3(0, yaw, 0)); -// float pitch = (dPos.y == 0.0f && horizontal == 0.0f) ? 0.0f : glm::atan(dPos.y, horizontal); -// glm::quat pitchQuat = glm::quat(glm::vec3(pitch, 0, 0)); -// glm::mat4x4 matrix = glm::lookAt(args->_viewFrustum->getPosition(), getPosition(), -// glm::vec3(0, 1, 0)); -// rotation = glm::quat_cast(matrix); -// rotation = yawQuat * pitchQuat; -// glm::vec3 pitch = glm::vec3(dPos.x, dPos.y, 0); -// rotation = glm::quat(glm::vec3(pitch, yaw, 0)); - // rotate about vertical to be perpendicular to the camera - rotation = args->_viewFrustum->getOrientation(); - rotation *= glm::angleAxis(glm::pi(), IDENTITY_UP); - rotation *= getRotation(); - } else { - rotation = getRotation(); - if (getAttachedPanel()) { - rotation *= getAttachedPanel()->getOffsetRotation() * - getAttachedPanel()->getFacingRotation(); -// if (getAttachedPanel()->getFacingRotation() != glm::quat(0, 0, 0, 0)) { -// rotation *= getAttachedPanel()->getFacingRotation(); -// } else if (getAttachedPanel()->getOffsetRotation() != glm::quat(0, 0, 0, 0)) { -// rotation *= getAttachedPanel()->getOffsetRotation(); -// } else { -// rotation *= Application::getInstance()->getCamera()->getOrientation() * -// glm::quat(0, 0, 1, 0); -// } - } - } + glm::quat rotation = calculateRotation(args->_viewFrustum->getOrientation()); float imageWidth = _texture->getWidth(); float imageHeight = _texture->getHeight(); @@ -143,8 +106,10 @@ void BillboardOverlay::render(RenderArgs* args) { batch->setModelTransform(transform); batch->setResourceTexture(0, _texture->getGPUTexture()); - DependencyManager::get()->renderQuad(*batch, topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight, - glm::vec4(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha)); + DependencyManager::get()->renderQuad( + *batch, topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight, + glm::vec4(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha) + ); batch->setResourceTexture(0, args->_whiteTexture); // restore default white color after me } @@ -239,17 +204,40 @@ void BillboardOverlay::setBillboardURL(const QString& url) { _isLoaded = false; } +glm::quat BillboardOverlay::calculateRotation(glm::quat cameraOrientation) const { + if (_isFacingAvatar) { + // LOL, quaternions are hard. + // glm::vec3 dPos = getPosition() - args->_viewFrustum->getPosition(); + // dPos = glm::normalize(dPos); + // rotation = glm::quat(0, dPos.x, dPos.y, dPos.z); + // float horizontal = glm::sqrt(dPos.x * dPos.x + dPos.y + dPos.y); + // glm::vec3 zAxis = glm::vec3(0, 0, 1); + // rotation = rotationBetween(zAxis, dPos); + // glm::vec3 euler = safeEulerAngles(rotationBetween(zAxis, dPos)); + // rotation = glm::quat(glm::vec3(euler.x, euler.y, 0)); + // float yaw = (dPos.x == 0.0f && dPos.z == 0.0f) ? 0.0f : glm::atan(dPos.x, dPos.z); + // glm::quat yawQuat = glm::quat(glm::vec3(0, yaw, 0)); + // float pitch = (dPos.y == 0.0f && horizontal == 0.0f) ? 0.0f : glm::atan(dPos.y, horizontal); + // glm::quat pitchQuat = glm::quat(glm::vec3(pitch, 0, 0)); + // glm::mat4x4 matrix = glm::lookAt(args->_viewFrustum->getPosition(), getPosition(), + // glm::vec3(0, 1, 0)); + // rotation = glm::quat_cast(matrix); + // rotation = yawQuat * pitchQuat; + // glm::vec3 pitch = glm::vec3(dPos.x, dPos.y, 0); + // rotation = glm::quat(glm::vec3(pitch, yaw, 0)); + // rotate about vertical to be perpendicular to the camera + glm::quat rotation = cameraOrientation; + rotation *= glm::angleAxis(glm::pi(), IDENTITY_UP); + return rotation * getRotation(); + } + return getTranslatedRotation(getRotation()); +} + bool BillboardOverlay::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face) { - if (_texture && _texture->isLoaded()) { - glm::quat rotation = getRotation(); - if (_isFacingAvatar) { - // rotate about vertical to face the camera - rotation = Application::getInstance()->getCamera()->getRotation(); - rotation *= glm::angleAxis(glm::pi(), glm::vec3(0.0f, 1.0f, 0.0f)); - } - + glm::quat rotation = + calculateRotation(Application::getInstance()->getCamera()->getRotation()); // Produce the dimensions of the billboard based on the image's aspect ratio and the overlay's scale. bool isNull = _fromImage.isNull(); float width = isNull ? _texture->getWidth() : _fromImage.width(); diff --git a/interface/src/ui/overlays/BillboardOverlay.h b/interface/src/ui/overlays/BillboardOverlay.h index f7bbfd1817..73e9db4c1b 100644 --- a/interface/src/ui/overlays/BillboardOverlay.h +++ b/interface/src/ui/overlays/BillboardOverlay.h @@ -41,6 +41,7 @@ public: private: void setBillboardURL(const QString& url); + glm::quat calculateRotation(glm::quat cameraOrientation) const; QString _url; NetworkTexturePointer _texture; diff --git a/interface/src/ui/overlays/PanelAttachable.cpp b/interface/src/ui/overlays/PanelAttachable.cpp index 172880db97..1beea6b74e 100644 --- a/interface/src/ui/overlays/PanelAttachable.cpp +++ b/interface/src/ui/overlays/PanelAttachable.cpp @@ -31,3 +31,20 @@ glm::vec3 PanelAttachable::getTranslatedPosition(glm::vec3 avatarPosition) const } return glm::vec3(); } + +glm::quat PanelAttachable::getTranslatedRotation(glm::quat offsetRotation) const { + glm::quat rotation = offsetRotation; + if (getAttachedPanel()) { + rotation *= getAttachedPanel()->getOffsetRotation() * + getAttachedPanel()->getFacingRotation(); + // if (getAttachedPanel()->getFacingRotation() != glm::quat(0, 0, 0, 0)) { + // rotation *= getAttachedPanel()->getFacingRotation(); + // } else if (getAttachedPanel()->getOffsetRotation() != glm::quat(0, 0, 0, 0)) { + // rotation *= getAttachedPanel()->getOffsetRotation(); + // } else { + // rotation *= Application::getInstance()->getCamera()->getOrientation() * + // glm::quat(0, 0, 1, 0); + // } + } + return rotation; +} diff --git a/interface/src/ui/overlays/PanelAttachable.h b/interface/src/ui/overlays/PanelAttachable.h index 29b0673157..ab8b8a4276 100644 --- a/interface/src/ui/overlays/PanelAttachable.h +++ b/interface/src/ui/overlays/PanelAttachable.h @@ -28,6 +28,7 @@ public: void setAttachedPanel(FloatingUIPanel* panel) { _attachedPanel = panel; } glm::vec3 getTranslatedPosition(glm::vec3 avatarPosition) const; + glm::quat getTranslatedRotation(glm::quat offsetRotation) const; private: FloatingUIPanel* _attachedPanel; From c77a91eb63f0b5ab7fff08b201b2fb15aac1edf7 Mon Sep 17 00:00:00 2001 From: Zander Otavka Date: Fri, 17 Jul 2015 14:31:38 -0700 Subject: [PATCH 32/65] Expose address bar toggling to scripts. --- interface/src/Application.cpp | 1 + interface/src/Application.h | 4 ++++ .../DialogsManagerScriptingInterface.cpp | 18 +++++++++++++++ .../DialogsManagerScriptingInterface.h | 23 +++++++++++++++++++ 4 files changed, 46 insertions(+) create mode 100644 interface/src/scripting/DialogsManagerScriptingInterface.cpp create mode 100644 interface/src/scripting/DialogsManagerScriptingInterface.h diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 047596e40a..995c49b891 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3758,6 +3758,7 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri scriptEngine->registerGlobalObject("AnimationCache", DependencyManager::get().data()); scriptEngine->registerGlobalObject("SoundCache", DependencyManager::get().data()); scriptEngine->registerGlobalObject("Account", AccountScriptingInterface::getInstance()); + scriptEngine->registerGlobalObject("DialogsManager", _dialogsManagerScriptingInterface); scriptEngine->registerGlobalObject("GlobalServices", GlobalServicesScriptingInterface::getInstance()); qScriptRegisterMetaType(scriptEngine, DownloadInfoResultToScriptValue, DownloadInfoResultFromScriptValue); diff --git a/interface/src/Application.h b/interface/src/Application.h index d1886862d2..56c126462a 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -49,6 +49,7 @@ #include "avatar/MyAvatar.h" #include "devices/SixenseManager.h" #include "scripting/ControllerScriptingInterface.h" +#include "scripting/DialogsManagerScriptingInterface.h" #include "scripting/WebWindowClass.h" #include "ui/AudioStatsDialog.h" #include "ui/BandwidthDialog.h" @@ -69,6 +70,7 @@ #include "UndoStackScriptingInterface.h" #include "gpu/Context.h" + #include "render/Engine.h" class QGLWidget; @@ -643,6 +645,8 @@ private: ApplicationOverlay _applicationOverlay; ApplicationCompositor _compositor; int _numFramesSinceLastResize = 0; + + DialogsManagerScriptingInterface* _dialogsManagerScriptingInterface = new DialogsManagerScriptingInterface(); }; #endif // hifi_Application_h diff --git a/interface/src/scripting/DialogsManagerScriptingInterface.cpp b/interface/src/scripting/DialogsManagerScriptingInterface.cpp new file mode 100644 index 0000000000..abbedb456e --- /dev/null +++ b/interface/src/scripting/DialogsManagerScriptingInterface.cpp @@ -0,0 +1,18 @@ +// +// DialogsManagerScriptingInterface.cpp +// interface/src/scripting +// +// Created by Zander Otavka on 7/17/15. +// Copyright 2015 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 "DialogsManagerScriptingInterface.h" + +#include "ui/DialogsManager.h" + +void DialogsManagerScriptingInterface::toggleAddressBar() { + QMetaObject::invokeMethod(DependencyManager::get().data(), "toggleAddressBar", Qt::QueuedConnection); +} diff --git a/interface/src/scripting/DialogsManagerScriptingInterface.h b/interface/src/scripting/DialogsManagerScriptingInterface.h new file mode 100644 index 0000000000..9a844f79de --- /dev/null +++ b/interface/src/scripting/DialogsManagerScriptingInterface.h @@ -0,0 +1,23 @@ +// +// DialogsManagerScriptingInterface.h +// interface/src/scripting +// +// Created by Zander Otavka on 7/17/15. +// Copyright 2015 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_DialogsManagerScriptInterface_h +#define hifi_DialogsManagerScriptInterface_h + +#include + +class DialogsManagerScriptingInterface : public QObject { + Q_OBJECT +public slots: + void toggleAddressBar(); +}; + +#endif From 052415534990607d46aa5ab0af11b9919f8d9d3b Mon Sep 17 00:00:00 2001 From: Zander Otavka Date: Fri, 17 Jul 2015 15:35:11 -0700 Subject: [PATCH 33/65] Expose face tracking to the script engine. --- interface/src/Application.cpp | 2 ++ interface/src/devices/FaceTracker.h | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 995c49b891..acdfd8cfc9 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3763,6 +3763,8 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri scriptEngine->registerGlobalObject("GlobalServices", GlobalServicesScriptingInterface::getInstance()); qScriptRegisterMetaType(scriptEngine, DownloadInfoResultToScriptValue, DownloadInfoResultFromScriptValue); + scriptEngine->registerGlobalObject("FaceTracker", DependencyManager::get().data()); + scriptEngine->registerGlobalObject("AvatarManager", DependencyManager::get().data()); qScriptRegisterMetaType(scriptEngine, joystickToScriptValue, joystickFromScriptValue); diff --git a/interface/src/devices/FaceTracker.h b/interface/src/devices/FaceTracker.h index 193262d121..7126d19ca8 100644 --- a/interface/src/devices/FaceTracker.h +++ b/interface/src/devices/FaceTracker.h @@ -47,7 +47,6 @@ public: bool isMuted() const { return _isMuted; } void setIsMuted(bool isMuted) { _isMuted = isMuted; } - void toggleMute(); static float getEyeDeflection() { return _eyeDeflection.get(); } static void setEyeDeflection(float eyeDeflection); @@ -57,6 +56,8 @@ signals: public slots: virtual void setEnabled(bool enabled) = 0; + void toggleMute(); + bool getMuted() { return _isMuted; } protected: virtual ~FaceTracker() {}; From 1655dea2c3409b2ff4793639de2531213098a3c9 Mon Sep 17 00:00:00 2001 From: Zander Otavka Date: Mon, 20 Jul 2015 11:44:03 -0700 Subject: [PATCH 34/65] Fix BillboardOverlay first person render bug. --- interface/src/ui/overlays/BillboardOverlay.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/interface/src/ui/overlays/BillboardOverlay.cpp b/interface/src/ui/overlays/BillboardOverlay.cpp index be8fab6362..c5e73a73e2 100644 --- a/interface/src/ui/overlays/BillboardOverlay.cpp +++ b/interface/src/ui/overlays/BillboardOverlay.cpp @@ -20,6 +20,8 @@ #include "Application.h" #include "GeometryUtil.h" +#include "DeferredLightingEffect.h" + #include @@ -100,12 +102,13 @@ void BillboardOverlay::render(RenderArgs* args) { if (batch) { Transform transform = _transform; - transform.postScale(glm::vec3(getDimensions(), 1.0f)); transform.setRotation(rotation); + transform.postScale(glm::vec3(getDimensions(), 1.0f)); batch->setModelTransform(transform); batch->setResourceTexture(0, _texture->getGPUTexture()); + DependencyManager::get()->bindSimpleProgram(*batch, true, true, true); DependencyManager::get()->renderQuad( *batch, topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight, glm::vec4(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha) From e5b870121534d8d6236278ce8abe04abcda15d25 Mon Sep 17 00:00:00 2001 From: Zander Otavka Date: Mon, 20 Jul 2015 11:45:15 -0700 Subject: [PATCH 35/65] Add toggle event for address bar. --- .../scripting/DialogsManagerScriptingInterface.cpp | 12 ++++++++++-- .../src/scripting/DialogsManagerScriptingInterface.h | 6 ++++++ interface/src/ui/DialogsManager.cpp | 1 + interface/src/ui/DialogsManager.h | 3 +++ 4 files changed, 20 insertions(+), 2 deletions(-) diff --git a/interface/src/scripting/DialogsManagerScriptingInterface.cpp b/interface/src/scripting/DialogsManagerScriptingInterface.cpp index abbedb456e..80a8b4ac7c 100644 --- a/interface/src/scripting/DialogsManagerScriptingInterface.cpp +++ b/interface/src/scripting/DialogsManagerScriptingInterface.cpp @@ -11,8 +11,16 @@ #include "DialogsManagerScriptingInterface.h" +#include + #include "ui/DialogsManager.h" -void DialogsManagerScriptingInterface::toggleAddressBar() { - QMetaObject::invokeMethod(DependencyManager::get().data(), "toggleAddressBar", Qt::QueuedConnection); +DialogsManagerScriptingInterface::DialogsManagerScriptingInterface() { + connect(DependencyManager::get().data(), &DialogsManager::addressBarToggled, + this, &DialogsManagerScriptingInterface::addressBarToggled); +} + +void DialogsManagerScriptingInterface::toggleAddressBar() { + QMetaObject::invokeMethod(DependencyManager::get().data(), + "toggleAddressBar", Qt::QueuedConnection); } diff --git a/interface/src/scripting/DialogsManagerScriptingInterface.h b/interface/src/scripting/DialogsManagerScriptingInterface.h index 9a844f79de..ef44e20d61 100644 --- a/interface/src/scripting/DialogsManagerScriptingInterface.h +++ b/interface/src/scripting/DialogsManagerScriptingInterface.h @@ -16,8 +16,14 @@ class DialogsManagerScriptingInterface : public QObject { Q_OBJECT +public: + DialogsManagerScriptingInterface(); + public slots: void toggleAddressBar(); + +signals: + void addressBarToggled(); }; #endif diff --git a/interface/src/ui/DialogsManager.cpp b/interface/src/ui/DialogsManager.cpp index 04f532c59a..ac5e6833fb 100644 --- a/interface/src/ui/DialogsManager.cpp +++ b/interface/src/ui/DialogsManager.cpp @@ -36,6 +36,7 @@ void DialogsManager::toggleAddressBar() { AddressBarDialog::toggle(); + emit addressBarToggled(); } void DialogsManager::toggleDiskCacheEditor() { diff --git a/interface/src/ui/DialogsManager.h b/interface/src/ui/DialogsManager.h index b15830e35c..09e0274d86 100644 --- a/interface/src/ui/DialogsManager.h +++ b/interface/src/ui/DialogsManager.h @@ -72,6 +72,9 @@ public slots: // Application Update void showUpdateDialog(); +signals: + void addressBarToggled(); + private slots: void toggleToolWindow(); void hmdToolsClosed(); From 1d37df47754f24ce87ce735a5900b157d9fa3e22 Mon Sep 17 00:00:00 2001 From: Zander Otavka Date: Mon, 20 Jul 2015 17:49:06 -0700 Subject: [PATCH 36/65] Improve and optimize panel transformations. --- .../src/ui/overlays/BillboardOverlay.cpp | 94 +++++++------------ interface/src/ui/overlays/BillboardOverlay.h | 7 +- interface/src/ui/overlays/PanelAttachable.cpp | 33 ++----- interface/src/ui/overlays/PanelAttachable.h | 18 ++-- 4 files changed, 57 insertions(+), 95 deletions(-) diff --git a/interface/src/ui/overlays/BillboardOverlay.cpp b/interface/src/ui/overlays/BillboardOverlay.cpp index c5e73a73e2..d8da58485c 100644 --- a/interface/src/ui/overlays/BillboardOverlay.cpp +++ b/interface/src/ui/overlays/BillboardOverlay.cpp @@ -39,13 +39,21 @@ BillboardOverlay::BillboardOverlay(const BillboardOverlay* billboardOverlay) : { } -void BillboardOverlay::update(float deltatime) { - if (getVisible()) { - glm::vec3 newPos = getTranslatedPosition(Application::getInstance()->getAvatarPosition()); - if (newPos != glm::vec3()) { - setPosition(newPos); +bool BillboardOverlay::setTransforms(Transform *transform) { + PanelAttachable::setTransforms(transform); + if (_isFacingAvatar) { + glm::quat rotation = Application::getInstance()->getCamera()->getOrientation(); + rotation *= glm::angleAxis(glm::pi(), IDENTITY_UP); + setRotation(rotation); + return true; } - } + return false; +// } +// return true; +} + +void BillboardOverlay::update(float deltatime) { + setTransforms(&_transform); } void BillboardOverlay::render(RenderArgs* args) { @@ -58,12 +66,8 @@ void BillboardOverlay::render(RenderArgs* args) { return; } - glm::vec3 newPos = getTranslatedPosition(Application::getInstance()->getAvatarPosition()); - if (newPos != glm::vec3()) { - setPosition(newPos); - } - - glm::quat rotation = calculateRotation(args->_viewFrustum->getOrientation()); + Q_ASSERT(args->_batch); + auto batch = args->_batch; float imageWidth = _texture->getWidth(); float imageHeight = _texture->getHeight(); @@ -98,24 +102,20 @@ void BillboardOverlay::render(RenderArgs* args) { xColor color = getColor(); float alpha = getAlpha(); - auto batch = args->_batch; + setTransforms(&_transform); + Transform transform = _transform; + transform.postScale(glm::vec3(getDimensions(), 1.0f)); - if (batch) { - Transform transform = _transform; - transform.setRotation(rotation); - transform.postScale(glm::vec3(getDimensions(), 1.0f)); - - batch->setModelTransform(transform); - batch->setResourceTexture(0, _texture->getGPUTexture()); - - DependencyManager::get()->bindSimpleProgram(*batch, true, true, true); - DependencyManager::get()->renderQuad( - *batch, topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight, - glm::vec4(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha) - ); + batch->setModelTransform(transform); + batch->setResourceTexture(0, _texture->getGPUTexture()); - batch->setResourceTexture(0, args->_whiteTexture); // restore default white color after me - } + DependencyManager::get()->bindSimpleProgram(*batch, true, true, false); + DependencyManager::get()->renderQuad( + *batch, topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight, + glm::vec4(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha) + ); + + batch->setResourceTexture(0, args->_whiteTexture); // restore default white color after me } void BillboardOverlay::setProperties(const QScriptValue &properties) { @@ -207,40 +207,12 @@ void BillboardOverlay::setBillboardURL(const QString& url) { _isLoaded = false; } -glm::quat BillboardOverlay::calculateRotation(glm::quat cameraOrientation) const { - if (_isFacingAvatar) { - // LOL, quaternions are hard. - // glm::vec3 dPos = getPosition() - args->_viewFrustum->getPosition(); - // dPos = glm::normalize(dPos); - // rotation = glm::quat(0, dPos.x, dPos.y, dPos.z); - // float horizontal = glm::sqrt(dPos.x * dPos.x + dPos.y + dPos.y); - // glm::vec3 zAxis = glm::vec3(0, 0, 1); - // rotation = rotationBetween(zAxis, dPos); - // glm::vec3 euler = safeEulerAngles(rotationBetween(zAxis, dPos)); - // rotation = glm::quat(glm::vec3(euler.x, euler.y, 0)); - // float yaw = (dPos.x == 0.0f && dPos.z == 0.0f) ? 0.0f : glm::atan(dPos.x, dPos.z); - // glm::quat yawQuat = glm::quat(glm::vec3(0, yaw, 0)); - // float pitch = (dPos.y == 0.0f && horizontal == 0.0f) ? 0.0f : glm::atan(dPos.y, horizontal); - // glm::quat pitchQuat = glm::quat(glm::vec3(pitch, 0, 0)); - // glm::mat4x4 matrix = glm::lookAt(args->_viewFrustum->getPosition(), getPosition(), - // glm::vec3(0, 1, 0)); - // rotation = glm::quat_cast(matrix); - // rotation = yawQuat * pitchQuat; - // glm::vec3 pitch = glm::vec3(dPos.x, dPos.y, 0); - // rotation = glm::quat(glm::vec3(pitch, yaw, 0)); - // rotate about vertical to be perpendicular to the camera - glm::quat rotation = cameraOrientation; - rotation *= glm::angleAxis(glm::pi(), IDENTITY_UP); - return rotation * getRotation(); - } - return getTranslatedRotation(getRotation()); -} - bool BillboardOverlay::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, - float& distance, BoxFace& face) { + float& distance, BoxFace& face) { if (_texture && _texture->isLoaded()) { - glm::quat rotation = - calculateRotation(Application::getInstance()->getCamera()->getRotation()); + // Make sure position and rotation is updated. + setTransforms(&_transform); + // Produce the dimensions of the billboard based on the image's aspect ratio and the overlay's scale. bool isNull = _fromImage.isNull(); float width = isNull ? _texture->getWidth() : _fromImage.width(); @@ -248,7 +220,7 @@ bool BillboardOverlay::findRayIntersection(const glm::vec3& origin, const glm::v float maxSize = glm::max(width, height); glm::vec2 dimensions = _dimensions * glm::vec2(width / maxSize, height / maxSize); - return findRayRectangleIntersection(origin, direction, rotation, getPosition(), dimensions, distance); + return findRayRectangleIntersection(origin, direction, getRotation(), getPosition(), dimensions, distance); } return false; diff --git a/interface/src/ui/overlays/BillboardOverlay.h b/interface/src/ui/overlays/BillboardOverlay.h index 73e9db4c1b..3f8e18a0ef 100644 --- a/interface/src/ui/overlays/BillboardOverlay.h +++ b/interface/src/ui/overlays/BillboardOverlay.h @@ -39,10 +39,13 @@ public: virtual BillboardOverlay* createClone() const; +protected: + bool setTransforms(Transform* transform); + private: void setBillboardURL(const QString& url); - glm::quat calculateRotation(glm::quat cameraOrientation) const; - +// glm::quat calculateRotation(glm::quat cameraOrientation) const; + QString _url; NetworkTexturePointer _texture; diff --git a/interface/src/ui/overlays/PanelAttachable.cpp b/interface/src/ui/overlays/PanelAttachable.cpp index 1beea6b74e..7e2ce19099 100644 --- a/interface/src/ui/overlays/PanelAttachable.cpp +++ b/interface/src/ui/overlays/PanelAttachable.cpp @@ -20,31 +20,14 @@ PanelAttachable::PanelAttachable(const PanelAttachable* panelAttachable) : { } -glm::vec3 PanelAttachable::getTranslatedPosition(glm::vec3 avatarPosition) const { +bool PanelAttachable::setTransforms(Transform* transform) { + Q_ASSERT(transform != nullptr); if (getAttachedPanel()) { - glm::vec3 totalOffsetPosition = - getAttachedPanel()->getFacingRotation() * getOffsetPosition() + - getAttachedPanel()->getOffsetPosition(); - - return getAttachedPanel()->getOffsetRotation() * totalOffsetPosition + - avatarPosition; + transform->setTranslation(getAttachedPanel()->getAnchorPosition()); + transform->setRotation(getAttachedPanel()->getOffsetRotation()); + transform->postTranslate(getOffsetPosition() + getAttachedPanel()->getOffsetPosition()); + transform->postRotate(getFacingRotation() * getAttachedPanel()->getFacingRotation()); + return true; } - return glm::vec3(); -} - -glm::quat PanelAttachable::getTranslatedRotation(glm::quat offsetRotation) const { - glm::quat rotation = offsetRotation; - if (getAttachedPanel()) { - rotation *= getAttachedPanel()->getOffsetRotation() * - getAttachedPanel()->getFacingRotation(); - // if (getAttachedPanel()->getFacingRotation() != glm::quat(0, 0, 0, 0)) { - // rotation *= getAttachedPanel()->getFacingRotation(); - // } else if (getAttachedPanel()->getOffsetRotation() != glm::quat(0, 0, 0, 0)) { - // rotation *= getAttachedPanel()->getOffsetRotation(); - // } else { - // rotation *= Application::getInstance()->getCamera()->getOrientation() * - // glm::quat(0, 0, 1, 0); - // } - } - return rotation; + return false; } diff --git a/interface/src/ui/overlays/PanelAttachable.h b/interface/src/ui/overlays/PanelAttachable.h index ab8b8a4276..d44504f5d4 100644 --- a/interface/src/ui/overlays/PanelAttachable.h +++ b/interface/src/ui/overlays/PanelAttachable.h @@ -15,24 +15,28 @@ #include "FloatingUIPanel.h" #include +#include class PanelAttachable { public: PanelAttachable(); PanelAttachable(const PanelAttachable* panelAttachable); - glm::vec3 getOffsetPosition() const { return _offsetPosition; } - void setOffsetPosition(glm::vec3 position) { _offsetPosition = position; } - FloatingUIPanel* getAttachedPanel() const { return _attachedPanel; } - void setAttachedPanel(FloatingUIPanel* panel) { _attachedPanel = panel; } + glm::vec3 getOffsetPosition() const { return _offsetPosition; } + glm::quat getFacingRotation() const { return _facingRotation; } - glm::vec3 getTranslatedPosition(glm::vec3 avatarPosition) const; - glm::quat getTranslatedRotation(glm::quat offsetRotation) const; + void setAttachedPanel(FloatingUIPanel* panel) { _attachedPanel = panel; } + void setOffsetPosition(glm::vec3 position) { _offsetPosition = position; } + void setFacingRotation(glm::quat rotation) { _facingRotation = rotation; } + +protected: + bool setTransforms(Transform* transform); private: FloatingUIPanel* _attachedPanel; - glm::vec3 _offsetPosition = glm::vec3(0, 0, 0); + glm::vec3 _offsetPosition; + glm::quat _facingRotation; }; #endif // hifi_PanelAttachable_h From da2afca391054d71328fa8037a1f99706367fb45 Mon Sep 17 00:00:00 2001 From: Zander Otavka Date: Thu, 23 Jul 2015 08:47:33 -0700 Subject: [PATCH 37/65] Expose overlay type checking to JavaScript. --- .../src/ui/overlays/BillboardOverlay.cpp | 18 +-- interface/src/ui/overlays/BillboardOverlay.h | 6 +- interface/src/ui/overlays/Circle3DOverlay.cpp | 2 + interface/src/ui/overlays/Circle3DOverlay.h | 3 + interface/src/ui/overlays/Cube3DOverlay.cpp | 2 + interface/src/ui/overlays/Cube3DOverlay.h | 3 + interface/src/ui/overlays/FloatingUIPanel.cpp | 153 +++++++++++++++--- interface/src/ui/overlays/FloatingUIPanel.h | 25 ++- interface/src/ui/overlays/Grid3DOverlay.cpp | 3 + interface/src/ui/overlays/Grid3DOverlay.h | 3 + interface/src/ui/overlays/ImageOverlay.cpp | 3 + interface/src/ui/overlays/ImageOverlay.h | 3 + interface/src/ui/overlays/Line3DOverlay.cpp | 3 + interface/src/ui/overlays/Line3DOverlay.h | 3 + .../src/ui/overlays/LocalModelsOverlay.cpp | 3 + .../src/ui/overlays/LocalModelsOverlay.h | 3 + interface/src/ui/overlays/ModelOverlay.cpp | 3 + interface/src/ui/overlays/ModelOverlay.h | 3 + interface/src/ui/overlays/Overlay.cpp | 3 +- interface/src/ui/overlays/Overlay.h | 1 + interface/src/ui/overlays/Overlays.cpp | 95 ++++++----- interface/src/ui/overlays/Overlays.h | 14 +- interface/src/ui/overlays/PanelAttachable.cpp | 12 +- interface/src/ui/overlays/PanelAttachable.h | 10 +- .../src/ui/overlays/Rectangle3DOverlay.cpp | 3 + .../src/ui/overlays/Rectangle3DOverlay.h | 3 + interface/src/ui/overlays/Sphere3DOverlay.cpp | 2 + interface/src/ui/overlays/Sphere3DOverlay.h | 3 + interface/src/ui/overlays/Text3DOverlay.cpp | 2 + interface/src/ui/overlays/Text3DOverlay.h | 3 + interface/src/ui/overlays/TextOverlay.cpp | 2 + interface/src/ui/overlays/TextOverlay.h | 3 + 32 files changed, 294 insertions(+), 104 deletions(-) diff --git a/interface/src/ui/overlays/BillboardOverlay.cpp b/interface/src/ui/overlays/BillboardOverlay.cpp index d8da58485c..3e8333b859 100644 --- a/interface/src/ui/overlays/BillboardOverlay.cpp +++ b/interface/src/ui/overlays/BillboardOverlay.cpp @@ -25,6 +25,8 @@ #include +QString const BillboardOverlay::TYPE = "billboard"; + BillboardOverlay::BillboardOverlay() { _isLoaded = false; } @@ -39,17 +41,13 @@ BillboardOverlay::BillboardOverlay(const BillboardOverlay* billboardOverlay) : { } -bool BillboardOverlay::setTransforms(Transform *transform) { +void BillboardOverlay::setTransforms(Transform *transform) { PanelAttachable::setTransforms(transform); - if (_isFacingAvatar) { - glm::quat rotation = Application::getInstance()->getCamera()->getOrientation(); - rotation *= glm::angleAxis(glm::pi(), IDENTITY_UP); - setRotation(rotation); - return true; - } - return false; -// } -// return true; + if (_isFacingAvatar) { + glm::quat rotation = Application::getInstance()->getCamera()->getOrientation(); + rotation *= glm::angleAxis(glm::pi(), IDENTITY_UP); + setRotation(rotation); + } } void BillboardOverlay::update(float deltatime) { diff --git a/interface/src/ui/overlays/BillboardOverlay.h b/interface/src/ui/overlays/BillboardOverlay.h index 3f8e18a0ef..47be764f2f 100644 --- a/interface/src/ui/overlays/BillboardOverlay.h +++ b/interface/src/ui/overlays/BillboardOverlay.h @@ -20,6 +20,9 @@ class BillboardOverlay : public Planar3DOverlay, public PanelAttachable { Q_OBJECT public: + static QString const TYPE; + virtual QString getType() const { return TYPE; } + BillboardOverlay(); BillboardOverlay(const BillboardOverlay* billboardOverlay); @@ -40,11 +43,10 @@ public: virtual BillboardOverlay* createClone() const; protected: - bool setTransforms(Transform* transform); + void setTransforms(Transform* transform); private: void setBillboardURL(const QString& url); -// glm::quat calculateRotation(glm::quat cameraOrientation) const; QString _url; NetworkTexturePointer _texture; diff --git a/interface/src/ui/overlays/Circle3DOverlay.cpp b/interface/src/ui/overlays/Circle3DOverlay.cpp index 53f1b4ce21..b906f008d6 100644 --- a/interface/src/ui/overlays/Circle3DOverlay.cpp +++ b/interface/src/ui/overlays/Circle3DOverlay.cpp @@ -15,6 +15,8 @@ #include +QString const Circle3DOverlay::TYPE = "circle3d"; + Circle3DOverlay::Circle3DOverlay() : _startAt(0.0f), _endAt(360.0f), diff --git a/interface/src/ui/overlays/Circle3DOverlay.h b/interface/src/ui/overlays/Circle3DOverlay.h index d83703fcd0..5879fe1701 100644 --- a/interface/src/ui/overlays/Circle3DOverlay.h +++ b/interface/src/ui/overlays/Circle3DOverlay.h @@ -18,6 +18,9 @@ class Circle3DOverlay : public Planar3DOverlay { Q_OBJECT public: + static QString const TYPE; + virtual QString getType() const { return TYPE; } + Circle3DOverlay(); Circle3DOverlay(const Circle3DOverlay* circle3DOverlay); diff --git a/interface/src/ui/overlays/Cube3DOverlay.cpp b/interface/src/ui/overlays/Cube3DOverlay.cpp index 200a1a328f..a306c7c86d 100644 --- a/interface/src/ui/overlays/Cube3DOverlay.cpp +++ b/interface/src/ui/overlays/Cube3DOverlay.cpp @@ -19,6 +19,8 @@ #include #include +QString const Cube3DOverlay::TYPE = "cube"; + Cube3DOverlay::Cube3DOverlay(const Cube3DOverlay* cube3DOverlay) : Volume3DOverlay(cube3DOverlay) { diff --git a/interface/src/ui/overlays/Cube3DOverlay.h b/interface/src/ui/overlays/Cube3DOverlay.h index 397ad77a9e..6f9026a091 100644 --- a/interface/src/ui/overlays/Cube3DOverlay.h +++ b/interface/src/ui/overlays/Cube3DOverlay.h @@ -17,6 +17,9 @@ class Cube3DOverlay : public Volume3DOverlay { Q_OBJECT public: + static QString const TYPE; + virtual QString getType() const { return TYPE; } + Cube3DOverlay() {} Cube3DOverlay(const Cube3DOverlay* cube3DOverlay); diff --git a/interface/src/ui/overlays/FloatingUIPanel.cpp b/interface/src/ui/overlays/FloatingUIPanel.cpp index e655a75f07..a862e4fcd4 100644 --- a/interface/src/ui/overlays/FloatingUIPanel.cpp +++ b/interface/src/ui/overlays/FloatingUIPanel.cpp @@ -13,23 +13,58 @@ #include #include +#include +#include "avatar/AvatarManager.h" +#include "avatar/MyAvatar.h" #include "Application.h" +#include "Base3DOverlay.h" -glm::quat FloatingUIPanel::getOffsetRotation() const { - if (getActualOffsetRotation() == glm::quat(0, 0, 0, 0)) { - return Application::getInstance()->getCamera()->getOrientation() * glm::quat(0, 0, 1, 0); - } - return getActualOffsetRotation(); +std::function const FloatingUIPanel::AVATAR_POSITION = []() -> glm::vec3 { + return DependencyManager::get()->getMyAvatar()->getPosition(); +}; + +std::function const FloatingUIPanel::AVATAR_ORIENTATION = []() -> glm::quat { + return DependencyManager::get()->getMyAvatar()->getOrientation() * + glm::angleAxis(glm::pi(), IDENTITY_UP); +}; + +FloatingUIPanel::FloatingUIPanel() : + _anchorPosition(AVATAR_POSITION), + _offsetRotation(AVATAR_ORIENTATION) +{ +} + +glm::vec3 FloatingUIPanel::getPosition() const { + return getOffsetRotation() * getOffsetPosition() + getAnchorPosition(); +} + +glm::quat FloatingUIPanel::getRotation() const { + return getOffsetRotation() * getFacingRotation(); +} + +void FloatingUIPanel::setAnchorPosition(glm::vec3 position) { + setAnchorPosition([position]() -> glm::vec3 { + return position; + }); +} + +void FloatingUIPanel::setOffsetRotation(glm::quat rotation) { + setOffsetRotation([rotation]() -> glm::quat { + return rotation; + }); } QScriptValue FloatingUIPanel::getProperty(const QString &property) { - if (property == "offsetPosition") { - return vec3toScriptValue(_scriptEngine, getOffsetPosition()); + if (property == "anchorPosition") { + return vec3toScriptValue(_scriptEngine, getAnchorPosition()); } if (property == "offsetRotation") { - return quatToScriptValue(_scriptEngine, getActualOffsetRotation()); + return quatToScriptValue(_scriptEngine, getOffsetRotation()); + } + if (property == "offsetPosition") { + return vec3toScriptValue(_scriptEngine, getOffsetPosition()); } if (property == "facingRotation") { return quatToScriptValue(_scriptEngine, getFacingRotation()); @@ -39,6 +74,91 @@ QScriptValue FloatingUIPanel::getProperty(const QString &property) { } void FloatingUIPanel::setProperties(const QScriptValue &properties) { + QScriptValue anchor = properties.property("anchorPosition"); + if (anchor.isValid()) { + QScriptValue type = anchor.property("type"); + QScriptValue value = anchor.property("value"); + + if (type.isValid()) { + QString typeString = type.toVariant().toString(); + if (typeString == "myAvatar") { + setAnchorPosition(AVATAR_POSITION); + } else if (value.isValid()) { + if (typeString == "overlay") { + Overlay::Pointer overlay = Application::getInstance()->getOverlays() + .getOverlay(value.toVariant().toUInt()); + if (overlay->is3D()) { + auto overlay3D = std::static_pointer_cast(overlay); + setAnchorPosition([&overlay3D]() -> glm::vec3 { + return overlay3D->getPosition(); + }); + } + } else if (typeString == "panel") { + FloatingUIPanel::Pointer panel = Application::getInstance()->getOverlays() + .getPanel(value.toVariant().toUInt()); + setAnchorPosition([panel]() -> glm::vec3 { + return panel->getPosition(); + }); + } else if (typeString == "vec3") { + QScriptValue x = value.property("x"); + QScriptValue y = value.property("y"); + QScriptValue z = value.property("z"); + if (x.isValid() && y.isValid() && z.isValid()) { + glm::vec3 newPosition; + newPosition.x = x.toVariant().toFloat(); + newPosition.y = y.toVariant().toFloat(); + newPosition.z = z.toVariant().toFloat(); + setAnchorPosition(newPosition); + } + } + } + } + } + + QScriptValue offsetRotation = properties.property("offsetRotation"); + if (offsetRotation.isValid()) { + QScriptValue type = offsetRotation.property("type"); + QScriptValue value = offsetRotation.property("value"); + + if (type.isValid()) { + QString typeString = type.toVariant().toString(); + if (typeString == "myAvatar") { + setOffsetRotation(AVATAR_ORIENTATION); + } else if (value.isValid()) { + if (typeString == "overlay") { + Overlay::Pointer overlay = Application::getInstance()->getOverlays() + .getOverlay(value.toVariant().toUInt()); + if (overlay->is3D()) { + auto overlay3D = std::static_pointer_cast(overlay); + setOffsetRotation([&overlay3D]() -> glm::quat { + return overlay3D->getRotation(); + }); + } + } else if (typeString == "panel") { + FloatingUIPanel::Pointer panel = Application::getInstance()->getOverlays() + .getPanel(value.toVariant().toUInt()); + setOffsetRotation([panel]() -> glm::quat { + return panel->getRotation(); + }); + } else if (typeString == "quat") { + QScriptValue x = value.property("x"); + QScriptValue y = value.property("y"); + QScriptValue z = value.property("z"); + QScriptValue w = value.property("w"); + + if (x.isValid() && y.isValid() && z.isValid() && w.isValid()) { + glm::quat newRotation; + newRotation.x = x.toVariant().toFloat(); + newRotation.y = y.toVariant().toFloat(); + newRotation.z = z.toVariant().toFloat(); + newRotation.w = w.toVariant().toFloat(); + setOffsetRotation(newRotation); + } + } + } + } + } + QScriptValue offsetPosition = properties.property("offsetPosition"); if (offsetPosition.isValid()) { QScriptValue x = offsetPosition.property("x"); @@ -53,23 +173,6 @@ void FloatingUIPanel::setProperties(const QScriptValue &properties) { } } - QScriptValue offsetRotation = properties.property("offsetRotation"); - if (offsetRotation.isValid()) { - QScriptValue x = offsetRotation.property("x"); - QScriptValue y = offsetRotation.property("y"); - QScriptValue z = offsetRotation.property("z"); - QScriptValue w = offsetRotation.property("w"); - - if (x.isValid() && y.isValid() && z.isValid() && w.isValid()) { - glm::quat newRotation; - newRotation.x = x.toVariant().toFloat(); - newRotation.y = y.toVariant().toFloat(); - newRotation.z = z.toVariant().toFloat(); - newRotation.w = w.toVariant().toFloat(); - setOffsetRotation(newRotation); - } - } - QScriptValue facingRotation = properties.property("facingRotation"); if (offsetRotation.isValid()) { QScriptValue x = facingRotation.property("x"); diff --git a/interface/src/ui/overlays/FloatingUIPanel.h b/interface/src/ui/overlays/FloatingUIPanel.h index 7f8d42eb5b..abc1328928 100644 --- a/interface/src/ui/overlays/FloatingUIPanel.h +++ b/interface/src/ui/overlays/FloatingUIPanel.h @@ -25,22 +25,31 @@ public: void init(QScriptEngine* scriptEngine) { _scriptEngine = scriptEngine; } + glm::vec3 getAnchorPosition() const { return _anchorPosition(); } + glm::quat getOffsetRotation() const { return _offsetRotation(); } glm::vec3 getOffsetPosition() const { return _offsetPosition; } - glm::quat getOffsetRotation() const; - glm::quat getActualOffsetRotation() const { return _offsetRotation; } glm::quat getFacingRotation() const { return _facingRotation; } + glm::vec3 getPosition() const; + glm::quat getRotation() const; - void setOffsetPosition(glm::vec3 position) { _offsetPosition = position; }; - void setOffsetRotation(glm::quat rotation) { _offsetRotation = rotation; }; - void setFacingRotation(glm::quat rotation) { _facingRotation = rotation; }; + void setAnchorPosition(const std::function& func) { _anchorPosition = func; } + void setAnchorPosition(glm::vec3 position); + void setOffsetRotation(const std::function& func) { _offsetRotation = func; } + void setOffsetRotation(glm::quat rotation); + void setOffsetPosition(glm::vec3 position) { _offsetPosition = position; } + void setFacingRotation(glm::quat rotation) { _facingRotation = rotation; } QScriptValue getProperty(const QString& property); void setProperties(const QScriptValue& properties); private: - glm::vec3 _offsetPosition = glm::vec3(0, 0, 0); - glm::quat _offsetRotation = glm::quat(0, 0, 0, 0); - glm::quat _facingRotation = glm::quat(1, 0, 0, 0); + static std::function const AVATAR_POSITION; + static std::function const AVATAR_ORIENTATION; + + std::function _anchorPosition; + std::function _offsetRotation; + glm::vec3 _offsetPosition{0, 0, 0}; + glm::quat _facingRotation{1, 0, 0, 0}; QScriptEngine* _scriptEngine; }; diff --git a/interface/src/ui/overlays/Grid3DOverlay.cpp b/interface/src/ui/overlays/Grid3DOverlay.cpp index 956eae35ff..074ad3d17f 100644 --- a/interface/src/ui/overlays/Grid3DOverlay.cpp +++ b/interface/src/ui/overlays/Grid3DOverlay.cpp @@ -18,6 +18,9 @@ #include #include + +QString const Grid3DOverlay::TYPE = "grid"; + Grid3DOverlay::Grid3DOverlay() : _minorGridWidth(1.0), _majorGridEvery(5) { diff --git a/interface/src/ui/overlays/Grid3DOverlay.h b/interface/src/ui/overlays/Grid3DOverlay.h index 80ec4b84b9..5fb3852905 100644 --- a/interface/src/ui/overlays/Grid3DOverlay.h +++ b/interface/src/ui/overlays/Grid3DOverlay.h @@ -18,6 +18,9 @@ class Grid3DOverlay : public Planar3DOverlay { Q_OBJECT public: + static QString const TYPE; + virtual QString getType() const { return TYPE; } + Grid3DOverlay(); Grid3DOverlay(const Grid3DOverlay* grid3DOverlay); diff --git a/interface/src/ui/overlays/ImageOverlay.cpp b/interface/src/ui/overlays/ImageOverlay.cpp index cad9010531..ed88f50981 100644 --- a/interface/src/ui/overlays/ImageOverlay.cpp +++ b/interface/src/ui/overlays/ImageOverlay.cpp @@ -16,6 +16,9 @@ #include #include + +QString const ImageOverlay::TYPE = "image"; + ImageOverlay::ImageOverlay() : _imageURL(), _renderImage(false), diff --git a/interface/src/ui/overlays/ImageOverlay.h b/interface/src/ui/overlays/ImageOverlay.h index 59d4102933..5698a73732 100644 --- a/interface/src/ui/overlays/ImageOverlay.h +++ b/interface/src/ui/overlays/ImageOverlay.h @@ -24,6 +24,9 @@ class ImageOverlay : public Overlay2D { Q_OBJECT public: + static QString const TYPE; + virtual QString getType() const { return TYPE; } + ImageOverlay(); ImageOverlay(const ImageOverlay* imageOverlay); diff --git a/interface/src/ui/overlays/Line3DOverlay.cpp b/interface/src/ui/overlays/Line3DOverlay.cpp index d026e5f56e..0acd7ecc1e 100644 --- a/interface/src/ui/overlays/Line3DOverlay.cpp +++ b/interface/src/ui/overlays/Line3DOverlay.cpp @@ -13,6 +13,9 @@ #include #include + +QString const Line3DOverlay::TYPE = "line3d"; + Line3DOverlay::Line3DOverlay() : _geometryCacheID(DependencyManager::get()->allocateID()) { diff --git a/interface/src/ui/overlays/Line3DOverlay.h b/interface/src/ui/overlays/Line3DOverlay.h index 4a4d8f4d90..05812709dc 100644 --- a/interface/src/ui/overlays/Line3DOverlay.h +++ b/interface/src/ui/overlays/Line3DOverlay.h @@ -17,6 +17,9 @@ class Line3DOverlay : public Base3DOverlay { Q_OBJECT public: + static QString const TYPE; + virtual QString getType() const { return TYPE; } + Line3DOverlay(); Line3DOverlay(const Line3DOverlay* line3DOverlay); ~Line3DOverlay(); diff --git a/interface/src/ui/overlays/LocalModelsOverlay.cpp b/interface/src/ui/overlays/LocalModelsOverlay.cpp index 3f701cbbe6..38e11562da 100644 --- a/interface/src/ui/overlays/LocalModelsOverlay.cpp +++ b/interface/src/ui/overlays/LocalModelsOverlay.cpp @@ -14,6 +14,9 @@ #include #include + +QString const LocalModelsOverlay::TYPE = "localmodels"; + LocalModelsOverlay::LocalModelsOverlay(EntityTreeRenderer* entityTreeRenderer) : Volume3DOverlay(), _entityTreeRenderer(entityTreeRenderer) { diff --git a/interface/src/ui/overlays/LocalModelsOverlay.h b/interface/src/ui/overlays/LocalModelsOverlay.h index c311b2bc1b..011f3dfb11 100644 --- a/interface/src/ui/overlays/LocalModelsOverlay.h +++ b/interface/src/ui/overlays/LocalModelsOverlay.h @@ -19,6 +19,9 @@ class EntityTreeRenderer; class LocalModelsOverlay : public Volume3DOverlay { Q_OBJECT public: + static QString const TYPE; + virtual QString getType() const { return TYPE; } + LocalModelsOverlay(EntityTreeRenderer* entityTreeRenderer); LocalModelsOverlay(const LocalModelsOverlay* localModelsOverlay); diff --git a/interface/src/ui/overlays/ModelOverlay.cpp b/interface/src/ui/overlays/ModelOverlay.cpp index ed15e57d43..7559d696d5 100644 --- a/interface/src/ui/overlays/ModelOverlay.cpp +++ b/interface/src/ui/overlays/ModelOverlay.cpp @@ -13,6 +13,9 @@ #include "Application.h" + +QString const ModelOverlay::TYPE = "model"; + ModelOverlay::ModelOverlay() : _model(), _modelTextures(QVariantMap()), diff --git a/interface/src/ui/overlays/ModelOverlay.h b/interface/src/ui/overlays/ModelOverlay.h index 1c43f42909..97ecfb642c 100644 --- a/interface/src/ui/overlays/ModelOverlay.h +++ b/interface/src/ui/overlays/ModelOverlay.h @@ -19,6 +19,9 @@ class ModelOverlay : public Volume3DOverlay { Q_OBJECT public: + static QString const TYPE; + virtual QString getType() const { return TYPE; } + ModelOverlay(); ModelOverlay(const ModelOverlay* modelOverlay); diff --git a/interface/src/ui/overlays/Overlay.cpp b/interface/src/ui/overlays/Overlay.cpp index 17e4ae74e1..7824c0c498 100644 --- a/interface/src/ui/overlays/Overlay.cpp +++ b/interface/src/ui/overlays/Overlay.cpp @@ -110,7 +110,8 @@ void Overlay::setProperties(const QScriptValue& properties) { } if (properties.property("visible").isValid()) { - setVisible(properties.property("visible").toVariant().toBool()); + bool visible = properties.property("visible").toVariant().toBool(); + setVisible(visible); } if (properties.property("anchor").isValid()) { diff --git a/interface/src/ui/overlays/Overlay.h b/interface/src/ui/overlays/Overlay.h index 1698d71fc3..9a19c71db6 100644 --- a/interface/src/ui/overlays/Overlay.h +++ b/interface/src/ui/overlays/Overlay.h @@ -44,6 +44,7 @@ public: virtual void removeFromScene(Overlay::Pointer overlay, std::shared_ptr scene, render::PendingChanges& pendingChanges); // getters + virtual QString getType() const = 0; virtual bool is3D() const = 0; bool isLoaded() { return _isLoaded; } bool getVisible() const { return _visible; } diff --git a/interface/src/ui/overlays/Overlays.cpp b/interface/src/ui/overlays/Overlays.cpp index fe489847ee..fb9f64c84e 100644 --- a/interface/src/ui/overlays/Overlays.cpp +++ b/interface/src/ui/overlays/Overlays.cpp @@ -135,13 +135,13 @@ Overlay::Pointer Overlays::getOverlay(unsigned int id) const { return nullptr; } -void Overlays::setAttachedPanel(Overlay* overlay, unsigned int overlayId, const QScriptValue& property) { - if (PanelAttachable* attachable = dynamic_cast(overlay)) { +void Overlays::setAttachedPanel(Overlay::Pointer overlay, unsigned int overlayId, const QScriptValue& property) { + auto attachable = std::dynamic_pointer_cast(overlay); + if (attachable) { if (property.isValid()) { unsigned int attachedPanelId = property.toVariant().toUInt(); - FloatingUIPanel* panel = nullptr; if (_panels.contains(attachedPanelId)) { - panel = _panels[attachedPanelId].get(); + auto panel = _panels[attachedPanelId]; panel->children.append(overlayId); attachable->setAttachedPanel(panel); } else { @@ -153,32 +153,32 @@ void Overlays::setAttachedPanel(Overlay* overlay, unsigned int overlayId, const } unsigned int Overlays::addOverlay(const QString& type, const QScriptValue& properties) { - Overlay* thisOverlay = NULL; + Overlay::Pointer thisOverlay = nullptr; - if (type == "image") { - thisOverlay = new ImageOverlay(); - } else if (type == "text") { - thisOverlay = new TextOverlay(); - } else if (type == "text3d") { - thisOverlay = new Text3DOverlay(); - } else if (type == "cube") { - thisOverlay = new Cube3DOverlay(); - } else if (type == "sphere") { - thisOverlay = new Sphere3DOverlay(); - } else if (type == "circle3d") { - thisOverlay = new Circle3DOverlay(); - } else if (type == "rectangle3d") { - thisOverlay = new Rectangle3DOverlay(); - } else if (type == "line3d") { - thisOverlay = new Line3DOverlay(); - } else if (type == "grid") { - thisOverlay = new Grid3DOverlay(); - } else if (type == "localmodels") { - thisOverlay = new LocalModelsOverlay(Application::getInstance()->getEntityClipboardRenderer()); - } else if (type == "model") { - thisOverlay = new ModelOverlay(); - } else if (type == "billboard") { - thisOverlay = new BillboardOverlay(); + if (type == ImageOverlay::TYPE) { + thisOverlay = std::make_shared(); + } else if (type == TextOverlay::TYPE) { + thisOverlay = std::make_shared(); + } else if (type == Text3DOverlay::TYPE) { + thisOverlay = std::make_shared(); + } else if (type == Cube3DOverlay::TYPE) { + thisOverlay = std::make_shared(); + } else if (type == Sphere3DOverlay::TYPE) { + thisOverlay = std::make_shared(); + } else if (type == Circle3DOverlay::TYPE) { + thisOverlay = std::make_shared(); + } else if (type == Rectangle3DOverlay::TYPE) { + thisOverlay = std::make_shared(); + } else if (type == Line3DOverlay::TYPE) { + thisOverlay = std::make_shared(); + } else if (type == Grid3DOverlay::TYPE) { + thisOverlay = std::make_shared(); + } else if (type == LocalModelsOverlay::TYPE) { + thisOverlay = std::make_shared(Application::getInstance()->getEntityClipboardRenderer()); + } else if (type == ModelOverlay::TYPE) { + thisOverlay = std::make_shared(); + } else if (type == BillboardOverlay::TYPE) { + thisOverlay = std::make_shared(); } if (thisOverlay) { @@ -190,29 +190,28 @@ unsigned int Overlays::addOverlay(const QString& type, const QScriptValue& prope return 0; } -unsigned int Overlays::addOverlay(Overlay* overlay) { - Overlay::Pointer overlayPointer(overlay); +unsigned int Overlays::addOverlay(Overlay::Pointer overlay) { overlay->init(_scriptEngine); QWriteLocker lock(&_lock); unsigned int thisID = _nextOverlayID; _nextOverlayID++; if (overlay->is3D()) { - Base3DOverlay* overlay3D = static_cast(overlay); + auto overlay3D = std::static_pointer_cast(overlay); if (overlay3D->getDrawOnHUD()) { - _overlaysHUD[thisID] = overlayPointer; + _overlaysHUD[thisID] = overlay; } else { - _overlaysWorld[thisID] = overlayPointer; + _overlaysWorld[thisID] = overlay; render::ScenePointer scene = Application::getInstance()->getMain3DScene(); render::PendingChanges pendingChanges; - overlayPointer->addToScene(overlayPointer, scene, pendingChanges); + overlay->addToScene(overlay, scene, pendingChanges); scene->enqueuePendingChanges(pendingChanges); } } else { - _overlaysHUD[thisID] = overlayPointer; + _overlaysHUD[thisID] = overlay; } return thisID; @@ -222,7 +221,7 @@ unsigned int Overlays::cloneOverlay(unsigned int id) { Overlay::Pointer thisOverlay = getOverlay(id); if (thisOverlay) { - return addOverlay(thisOverlay->createClone()); + return addOverlay(Overlay::Pointer(thisOverlay->createClone())); } return 0; // Not found @@ -253,7 +252,7 @@ bool Overlays::editOverlay(unsigned int id, const QScriptValue& properties) { thisOverlay->setProperties(properties); } - setAttachedPanel(thisOverlay.get(), id, properties.property("attachedPanel")); + setAttachedPanel(thisOverlay, id, properties.property("attachedPanel")); return true; } @@ -278,6 +277,14 @@ void Overlays::deleteOverlay(unsigned int id) { _overlaysToDelete.push_back(overlayToDelete); } +QString Overlays::getOverlayType(unsigned int overlayId) const { + Overlay::Pointer overlay = getOverlay(overlayId); + if (overlay) { + return overlay->getType(); + } + return ""; +} + unsigned int Overlays::getOverlayAtPoint(const glm::vec2& point) { glm::vec2 pointCopy = point; if (qApp->isHMDMode()) { @@ -322,8 +329,9 @@ OverlayPropertyResult Overlays::getProperty(unsigned int id, const QString& prop QReadLocker lock(&_lock); if (thisOverlay) { if (property == "attachedPanel") { - if (FloatingUIPanel* panel = dynamic_cast(thisOverlay.get())) { - result.value = _panels.key(FloatingUIPanel::Pointer(panel)); + auto panelAttachable = std::dynamic_pointer_cast(thisOverlay); + if (panelAttachable) { + result.value = _panels.key(panelAttachable->getAttachedPanel()); } else { result.value = 0; } @@ -499,19 +507,18 @@ QSizeF Overlays::textSize(unsigned int id, const QString& text) const { return QSizeF(0.0f, 0.0f); } -unsigned int Overlays::addPanel(FloatingUIPanel* panel) { +unsigned int Overlays::addPanel(FloatingUIPanel::Pointer panel) { QWriteLocker lock(&_lock); - FloatingUIPanel::Pointer panelPointer(panel); unsigned int thisID = _nextOverlayID; _nextOverlayID++; - _panels[thisID] = panelPointer; + _panels[thisID] = panel; return thisID; } unsigned int Overlays::addPanel(const QScriptValue& properties) { - FloatingUIPanel* panel = new FloatingUIPanel(); + FloatingUIPanel::Pointer panel = std::make_shared(); panel->init(_scriptEngine); panel->setProperties(properties); return addPanel(panel); diff --git a/interface/src/ui/overlays/Overlays.h b/interface/src/ui/overlays/Overlays.h index 80da2e6db1..ee4fa5b7d4 100644 --- a/interface/src/ui/overlays/Overlays.h +++ b/interface/src/ui/overlays/Overlays.h @@ -66,12 +66,16 @@ public: void update(float deltatime); void renderHUD(RenderArgs* renderArgs); + Overlay::Pointer getOverlay(unsigned int id) const; + FloatingUIPanel::Pointer getPanel(unsigned int id) const { return _panels[id]; } + public slots: /// adds an overlay with the specific properties unsigned int addOverlay(const QString& type, const QScriptValue& properties); /// adds an overlay that's already been created - unsigned int addOverlay(Overlay* overlay); + unsigned int addOverlay(Overlay* overlay) { return addOverlay(Overlay::Pointer(overlay)); } + unsigned int addOverlay(Overlay::Pointer overlay); /// clones an existing overlay unsigned int cloneOverlay(unsigned int id); @@ -83,6 +87,9 @@ public slots: /// deletes a particle void deleteOverlay(unsigned int id); + /// + QString getOverlayType(unsigned int overlayId) const; + /// returns the top most 2D overlay at the screen point, or 0 if not overlay at that point unsigned int getOverlayAtPoint(const glm::vec2& point); @@ -101,7 +108,7 @@ public slots: /// adds a panel that has already been created - unsigned int addPanel(FloatingUIPanel* panel); + unsigned int addPanel(FloatingUIPanel::Pointer panel); /// creates and adds a panel based on a set of properties unsigned int addPanel(const QScriptValue& properties); @@ -117,8 +124,7 @@ public slots: private: void cleanupOverlaysToDelete(); - Overlay::Pointer getOverlay(unsigned int id) const; - void setAttachedPanel(Overlay* overlay, unsigned int overlayId, const QScriptValue& property); + void setAttachedPanel(Overlay::Pointer overlay, unsigned int overlayId, const QScriptValue& property); QMap _overlaysHUD; QMap _overlaysWorld; diff --git a/interface/src/ui/overlays/PanelAttachable.cpp b/interface/src/ui/overlays/PanelAttachable.cpp index 7e2ce19099..bedf7d9fd5 100644 --- a/interface/src/ui/overlays/PanelAttachable.cpp +++ b/interface/src/ui/overlays/PanelAttachable.cpp @@ -3,31 +3,33 @@ // hifi // // Created by Zander Otavka on 7/15/15. +// Copyright 2015 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 "PanelAttachable.h" PanelAttachable::PanelAttachable() : _attachedPanel(nullptr), - _offsetPosition(glm::vec3()) + _facingRotation(1, 0, 0, 0) { } PanelAttachable::PanelAttachable(const PanelAttachable* panelAttachable) : _attachedPanel(panelAttachable->_attachedPanel), - _offsetPosition(panelAttachable->_offsetPosition) + _offsetPosition(panelAttachable->_offsetPosition), + _facingRotation(panelAttachable->_facingRotation) { } -bool PanelAttachable::setTransforms(Transform* transform) { +void PanelAttachable::setTransforms(Transform* transform) { Q_ASSERT(transform != nullptr); if (getAttachedPanel()) { transform->setTranslation(getAttachedPanel()->getAnchorPosition()); transform->setRotation(getAttachedPanel()->getOffsetRotation()); transform->postTranslate(getOffsetPosition() + getAttachedPanel()->getOffsetPosition()); transform->postRotate(getFacingRotation() * getAttachedPanel()->getFacingRotation()); - return true; } - return false; } diff --git a/interface/src/ui/overlays/PanelAttachable.h b/interface/src/ui/overlays/PanelAttachable.h index d44504f5d4..097fc9a517 100644 --- a/interface/src/ui/overlays/PanelAttachable.h +++ b/interface/src/ui/overlays/PanelAttachable.h @@ -3,7 +3,7 @@ // interface/src/ui/overlays // // Created by Zander Otavka on 7/1/15. -// Copyright 2014 High Fidelity, Inc. +// Copyright 2015 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 @@ -22,19 +22,19 @@ public: PanelAttachable(); PanelAttachable(const PanelAttachable* panelAttachable); - FloatingUIPanel* getAttachedPanel() const { return _attachedPanel; } + FloatingUIPanel::Pointer getAttachedPanel() const { return _attachedPanel; } glm::vec3 getOffsetPosition() const { return _offsetPosition; } glm::quat getFacingRotation() const { return _facingRotation; } - void setAttachedPanel(FloatingUIPanel* panel) { _attachedPanel = panel; } + void setAttachedPanel(FloatingUIPanel::Pointer panel) { _attachedPanel = panel; } void setOffsetPosition(glm::vec3 position) { _offsetPosition = position; } void setFacingRotation(glm::quat rotation) { _facingRotation = rotation; } protected: - bool setTransforms(Transform* transform); + void setTransforms(Transform* transform); private: - FloatingUIPanel* _attachedPanel; + FloatingUIPanel::Pointer _attachedPanel; glm::vec3 _offsetPosition; glm::quat _facingRotation; }; diff --git a/interface/src/ui/overlays/Rectangle3DOverlay.cpp b/interface/src/ui/overlays/Rectangle3DOverlay.cpp index 1d5183b833..64c5e4c819 100644 --- a/interface/src/ui/overlays/Rectangle3DOverlay.cpp +++ b/interface/src/ui/overlays/Rectangle3DOverlay.cpp @@ -13,6 +13,9 @@ #include #include + +QString const Rectangle3DOverlay::TYPE = "rectangle3d"; + Rectangle3DOverlay::Rectangle3DOverlay() : _geometryCacheID(DependencyManager::get()->allocateID()) { diff --git a/interface/src/ui/overlays/Rectangle3DOverlay.h b/interface/src/ui/overlays/Rectangle3DOverlay.h index f59ab21198..e5eba8bce8 100644 --- a/interface/src/ui/overlays/Rectangle3DOverlay.h +++ b/interface/src/ui/overlays/Rectangle3DOverlay.h @@ -17,6 +17,9 @@ class Rectangle3DOverlay : public Planar3DOverlay { Q_OBJECT public: + static QString const TYPE; + virtual QString getType() const { return TYPE; } + Rectangle3DOverlay(); Rectangle3DOverlay(const Rectangle3DOverlay* rectangle3DOverlay); ~Rectangle3DOverlay(); diff --git a/interface/src/ui/overlays/Sphere3DOverlay.cpp b/interface/src/ui/overlays/Sphere3DOverlay.cpp index 307b60b2ce..9712375209 100644 --- a/interface/src/ui/overlays/Sphere3DOverlay.cpp +++ b/interface/src/ui/overlays/Sphere3DOverlay.cpp @@ -15,6 +15,8 @@ #include #include +QString const Sphere3DOverlay::TYPE = "sphere"; + Sphere3DOverlay::Sphere3DOverlay(const Sphere3DOverlay* Sphere3DOverlay) : Volume3DOverlay(Sphere3DOverlay) { diff --git a/interface/src/ui/overlays/Sphere3DOverlay.h b/interface/src/ui/overlays/Sphere3DOverlay.h index b82dc548f1..d371077502 100644 --- a/interface/src/ui/overlays/Sphere3DOverlay.h +++ b/interface/src/ui/overlays/Sphere3DOverlay.h @@ -17,6 +17,9 @@ class Sphere3DOverlay : public Volume3DOverlay { Q_OBJECT public: + static QString const TYPE; + virtual QString getType() const { return TYPE; } + Sphere3DOverlay() {} Sphere3DOverlay(const Sphere3DOverlay* Sphere3DOverlay); diff --git a/interface/src/ui/overlays/Text3DOverlay.cpp b/interface/src/ui/overlays/Text3DOverlay.cpp index 737b2d0bc5..00a7d75090 100644 --- a/interface/src/ui/overlays/Text3DOverlay.cpp +++ b/interface/src/ui/overlays/Text3DOverlay.cpp @@ -23,6 +23,8 @@ const int FIXED_FONT_POINT_SIZE = 40; const int FIXED_FONT_SCALING_RATIO = FIXED_FONT_POINT_SIZE * 80.0f; // this is a ratio determined through experimentation const float LINE_SCALE_RATIO = 1.2f; +QString const Text3DOverlay::TYPE = "text3d"; + Text3DOverlay::Text3DOverlay() : _backgroundColor(DEFAULT_BACKGROUND_COLOR), _backgroundAlpha(DEFAULT_BACKGROUND_ALPHA), diff --git a/interface/src/ui/overlays/Text3DOverlay.h b/interface/src/ui/overlays/Text3DOverlay.h index 666b43d8b1..abd5ef54bd 100644 --- a/interface/src/ui/overlays/Text3DOverlay.h +++ b/interface/src/ui/overlays/Text3DOverlay.h @@ -21,6 +21,9 @@ class Text3DOverlay : public Planar3DOverlay { Q_OBJECT public: + static QString const TYPE; + virtual QString getType() const { return TYPE; } + Text3DOverlay(); Text3DOverlay(const Text3DOverlay* text3DOverlay); ~Text3DOverlay(); diff --git a/interface/src/ui/overlays/TextOverlay.cpp b/interface/src/ui/overlays/TextOverlay.cpp index ea0c4f6026..e9fda2def8 100644 --- a/interface/src/ui/overlays/TextOverlay.cpp +++ b/interface/src/ui/overlays/TextOverlay.cpp @@ -78,6 +78,8 @@ QString toQmlColor(const glm::vec4& v) { arg((int)(v.b * 255), 2, 16, QChar('0')); } +QString const TextOverlay::TYPE = "text"; + TextOverlay::TextOverlay() : _backgroundColor(DEFAULT_BACKGROUND_COLOR), _backgroundAlpha(DEFAULT_BACKGROUND_ALPHA), diff --git a/interface/src/ui/overlays/TextOverlay.h b/interface/src/ui/overlays/TextOverlay.h index b70b95ca3b..a8e6967871 100644 --- a/interface/src/ui/overlays/TextOverlay.h +++ b/interface/src/ui/overlays/TextOverlay.h @@ -29,6 +29,9 @@ class TextOverlay : public Overlay2D { Q_OBJECT public: + static QString const TYPE; + virtual QString getType() const { return TYPE; } + TextOverlay(); TextOverlay(const TextOverlay* textOverlay); ~TextOverlay(); From bc5ef8eb113f32295a7a12e1d8fc1b5096e49658 Mon Sep 17 00:00:00 2001 From: Zander Otavka Date: Fri, 24 Jul 2015 17:50:17 -0700 Subject: [PATCH 38/65] Clean up C++ relating to panels. --- .../src/ui/overlays/BillboardOverlay.cpp | 28 ++----- interface/src/ui/overlays/FloatingUIPanel.cpp | 53 +++++++------ interface/src/ui/overlays/FloatingUIPanel.h | 22 ++++-- interface/src/ui/overlays/Overlays.cpp | 75 ++++++++++--------- interface/src/ui/overlays/Overlays.h | 10 ++- interface/src/ui/overlays/PanelAttachable.cpp | 46 ++++++++++++ interface/src/ui/overlays/PanelAttachable.h | 7 +- 7 files changed, 151 insertions(+), 90 deletions(-) diff --git a/interface/src/ui/overlays/BillboardOverlay.cpp b/interface/src/ui/overlays/BillboardOverlay.cpp index 3e8333b859..8ffa909e12 100644 --- a/interface/src/ui/overlays/BillboardOverlay.cpp +++ b/interface/src/ui/overlays/BillboardOverlay.cpp @@ -13,17 +13,15 @@ #include +#include #include #include #include +#include #include "Application.h" #include "GeometryUtil.h" -#include "DeferredLightingEffect.h" - -#include - QString const BillboardOverlay::TYPE = "billboard"; @@ -107,7 +105,7 @@ void BillboardOverlay::render(RenderArgs* args) { batch->setModelTransform(transform); batch->setResourceTexture(0, _texture->getGPUTexture()); - DependencyManager::get()->bindSimpleProgram(*batch, true, true, false); + DependencyManager::get()->bindSimpleProgram(*batch, true, true, false, true); DependencyManager::get()->renderQuad( *batch, topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight, glm::vec4(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha) @@ -118,6 +116,7 @@ void BillboardOverlay::render(RenderArgs* args) { void BillboardOverlay::setProperties(const QScriptValue &properties) { Planar3DOverlay::setProperties(properties); + PanelAttachable::setProperties(properties); QScriptValue urlValue = properties.property("url"); if (urlValue.isValid()) { @@ -162,21 +161,6 @@ void BillboardOverlay::setProperties(const QScriptValue &properties) { if (isFacingAvatarValue.isValid()) { _isFacingAvatar = isFacingAvatarValue.toVariant().toBool(); } - - QScriptValue offsetPosition = properties.property("offsetPosition"); - if (offsetPosition.isValid()) { - QScriptValue x = offsetPosition.property("x"); - QScriptValue y = offsetPosition.property("y"); - QScriptValue z = offsetPosition.property("z"); - - if (x.isValid() && y.isValid() && z.isValid()) { - glm::vec3 newPosition; - newPosition.x = x.toVariant().toFloat(); - newPosition.y = y.toVariant().toFloat(); - newPosition.z = z.toVariant().toFloat(); - setOffsetPosition(newPosition); - } - } } QScriptValue BillboardOverlay::getProperty(const QString& property) { @@ -193,6 +177,10 @@ QScriptValue BillboardOverlay::getProperty(const QString& property) { return vec3toScriptValue(_scriptEngine, getOffsetPosition()); } + QScriptValue value = PanelAttachable::getProperty(_scriptEngine, property); + if (value.isValid()) { + return value; + } return Planar3DOverlay::getProperty(property); } diff --git a/interface/src/ui/overlays/FloatingUIPanel.cpp b/interface/src/ui/overlays/FloatingUIPanel.cpp index a862e4fcd4..eedb4bffd1 100644 --- a/interface/src/ui/overlays/FloatingUIPanel.cpp +++ b/interface/src/ui/overlays/FloatingUIPanel.cpp @@ -20,7 +20,6 @@ #include "Application.h" #include "Base3DOverlay.h" - std::function const FloatingUIPanel::AVATAR_POSITION = []() -> glm::vec3 { return DependencyManager::get()->getMyAvatar()->getPosition(); }; @@ -30,12 +29,6 @@ std::function const FloatingUIPanel::AVATAR_ORIENTATION = []() -> g glm::angleAxis(glm::pi(), IDENTITY_UP); }; -FloatingUIPanel::FloatingUIPanel() : - _anchorPosition(AVATAR_POSITION), - _offsetRotation(AVATAR_ORIENTATION) -{ -} - glm::vec3 FloatingUIPanel::getPosition() const { return getOffsetRotation() * getOffsetPosition() + getAnchorPosition(); } @@ -44,18 +37,30 @@ glm::quat FloatingUIPanel::getRotation() const { return getOffsetRotation() * getFacingRotation(); } -void FloatingUIPanel::setAnchorPosition(glm::vec3 position) { +void FloatingUIPanel::setAnchorPosition(const glm::vec3& position) { setAnchorPosition([position]() -> glm::vec3 { return position; }); } -void FloatingUIPanel::setOffsetRotation(glm::quat rotation) { +void FloatingUIPanel::setOffsetRotation(const glm::quat& rotation) { setOffsetRotation([rotation]() -> glm::quat { return rotation; }); } +void FloatingUIPanel::addChild(unsigned int childId) { + if (!_children.contains(childId)) { + _children.append(childId); + } +} + +void FloatingUIPanel::removeChild(unsigned int childId) { + if (_children.contains(childId)) { + _children.removeOne(childId); + } +} + QScriptValue FloatingUIPanel::getProperty(const QString &property) { if (property == "anchorPosition") { return vec3toScriptValue(_scriptEngine, getAnchorPosition()); @@ -76,15 +81,15 @@ QScriptValue FloatingUIPanel::getProperty(const QString &property) { void FloatingUIPanel::setProperties(const QScriptValue &properties) { QScriptValue anchor = properties.property("anchorPosition"); if (anchor.isValid()) { - QScriptValue type = anchor.property("type"); + QScriptValue bindType = anchor.property("bind"); QScriptValue value = anchor.property("value"); - if (type.isValid()) { - QString typeString = type.toVariant().toString(); - if (typeString == "myAvatar") { + if (bindType.isValid()) { + QString bindTypeString = bindType.toVariant().toString(); + if (bindTypeString == "myAvatar") { setAnchorPosition(AVATAR_POSITION); } else if (value.isValid()) { - if (typeString == "overlay") { + if (bindTypeString == "overlay") { Overlay::Pointer overlay = Application::getInstance()->getOverlays() .getOverlay(value.toVariant().toUInt()); if (overlay->is3D()) { @@ -93,13 +98,13 @@ void FloatingUIPanel::setProperties(const QScriptValue &properties) { return overlay3D->getPosition(); }); } - } else if (typeString == "panel") { + } else if (bindTypeString == "panel") { FloatingUIPanel::Pointer panel = Application::getInstance()->getOverlays() .getPanel(value.toVariant().toUInt()); setAnchorPosition([panel]() -> glm::vec3 { return panel->getPosition(); }); - } else if (typeString == "vec3") { + } else if (bindTypeString == "vec3") { QScriptValue x = value.property("x"); QScriptValue y = value.property("y"); QScriptValue z = value.property("z"); @@ -117,15 +122,15 @@ void FloatingUIPanel::setProperties(const QScriptValue &properties) { QScriptValue offsetRotation = properties.property("offsetRotation"); if (offsetRotation.isValid()) { - QScriptValue type = offsetRotation.property("type"); + QScriptValue bindType = offsetRotation.property("bind"); QScriptValue value = offsetRotation.property("value"); - if (type.isValid()) { - QString typeString = type.toVariant().toString(); - if (typeString == "myAvatar") { + if (bindType.isValid()) { + QString bindTypeString = bindType.toVariant().toString(); + if (bindTypeString == "myAvatar") { setOffsetRotation(AVATAR_ORIENTATION); } else if (value.isValid()) { - if (typeString == "overlay") { + if (bindTypeString == "overlay") { Overlay::Pointer overlay = Application::getInstance()->getOverlays() .getOverlay(value.toVariant().toUInt()); if (overlay->is3D()) { @@ -134,13 +139,13 @@ void FloatingUIPanel::setProperties(const QScriptValue &properties) { return overlay3D->getRotation(); }); } - } else if (typeString == "panel") { + } else if (bindTypeString == "panel") { FloatingUIPanel::Pointer panel = Application::getInstance()->getOverlays() .getPanel(value.toVariant().toUInt()); setOffsetRotation([panel]() -> glm::quat { return panel->getRotation(); }); - } else if (typeString == "quat") { + } else if (bindTypeString == "quat") { QScriptValue x = value.property("x"); QScriptValue y = value.property("y"); QScriptValue z = value.property("z"); @@ -174,7 +179,7 @@ void FloatingUIPanel::setProperties(const QScriptValue &properties) { } QScriptValue facingRotation = properties.property("facingRotation"); - if (offsetRotation.isValid()) { + if (facingRotation.isValid()) { QScriptValue x = facingRotation.property("x"); QScriptValue y = facingRotation.property("y"); QScriptValue z = facingRotation.property("z"); diff --git a/interface/src/ui/overlays/FloatingUIPanel.h b/interface/src/ui/overlays/FloatingUIPanel.h index abc1328928..f65fa93c26 100644 --- a/interface/src/ui/overlays/FloatingUIPanel.h +++ b/interface/src/ui/overlays/FloatingUIPanel.h @@ -12,6 +12,8 @@ #ifndef hifi_FloatingUIPanel_h #define hifi_FloatingUIPanel_h +#include + #include #include #include @@ -21,8 +23,6 @@ class FloatingUIPanel : public QObject { public: typedef std::shared_ptr Pointer; - QList children; - void init(QScriptEngine* scriptEngine) { _scriptEngine = scriptEngine; } glm::vec3 getAnchorPosition() const { return _anchorPosition(); } @@ -33,11 +33,16 @@ public: glm::quat getRotation() const; void setAnchorPosition(const std::function& func) { _anchorPosition = func; } - void setAnchorPosition(glm::vec3 position); + void setAnchorPosition(const glm::vec3& position); void setOffsetRotation(const std::function& func) { _offsetRotation = func; } - void setOffsetRotation(glm::quat rotation); - void setOffsetPosition(glm::vec3 position) { _offsetPosition = position; } - void setFacingRotation(glm::quat rotation) { _facingRotation = rotation; } + void setOffsetRotation(const glm::quat& rotation); + void setOffsetPosition(const glm::vec3& position) { _offsetPosition = position; } + void setFacingRotation(const glm::quat& rotation) { _facingRotation = rotation; } + + const QList& getChildren() { return _children; } + void addChild(unsigned int childId); + void removeChild(unsigned int childId); + unsigned int popLastChild() { return _children.takeLast(); } QScriptValue getProperty(const QString& property); void setProperties(const QScriptValue& properties); @@ -46,11 +51,12 @@ private: static std::function const AVATAR_POSITION; static std::function const AVATAR_ORIENTATION; - std::function _anchorPosition; - std::function _offsetRotation; + std::function _anchorPosition{AVATAR_POSITION}; + std::function _offsetRotation{AVATAR_ORIENTATION}; glm::vec3 _offsetPosition{0, 0, 0}; glm::quat _facingRotation{1, 0, 0, 0}; QScriptEngine* _scriptEngine; + QList _children; }; #endif // hifi_FloatingUIPanel_h diff --git a/interface/src/ui/overlays/Overlays.cpp b/interface/src/ui/overlays/Overlays.cpp index fb9f64c84e..bce219b4b4 100644 --- a/interface/src/ui/overlays/Overlays.cpp +++ b/interface/src/ui/overlays/Overlays.cpp @@ -135,23 +135,6 @@ Overlay::Pointer Overlays::getOverlay(unsigned int id) const { return nullptr; } -void Overlays::setAttachedPanel(Overlay::Pointer overlay, unsigned int overlayId, const QScriptValue& property) { - auto attachable = std::dynamic_pointer_cast(overlay); - if (attachable) { - if (property.isValid()) { - unsigned int attachedPanelId = property.toVariant().toUInt(); - if (_panels.contains(attachedPanelId)) { - auto panel = _panels[attachedPanelId]; - panel->children.append(overlayId); - attachable->setAttachedPanel(panel); - } else { - attachable->getAttachedPanel()->children.removeAll(overlayId); - attachable->setAttachedPanel(nullptr); - } - } - } -} - unsigned int Overlays::addOverlay(const QString& type, const QScriptValue& properties) { Overlay::Pointer thisOverlay = nullptr; @@ -183,9 +166,7 @@ unsigned int Overlays::addOverlay(const QString& type, const QScriptValue& prope if (thisOverlay) { thisOverlay->setProperties(properties); - unsigned int overlayId = addOverlay(thisOverlay); - setAttachedPanel(thisOverlay, overlayId, properties.property("attachedPanel")); - return overlayId; + return addOverlay(thisOverlay); } return 0; } @@ -252,8 +233,6 @@ bool Overlays::editOverlay(unsigned int id, const QScriptValue& properties) { thisOverlay->setProperties(properties); } - setAttachedPanel(thisOverlay, id, properties.property("attachedPanel")); - return true; } return false; @@ -273,8 +252,16 @@ void Overlays::deleteOverlay(unsigned int id) { } } + auto attachable = std::dynamic_pointer_cast(overlayToDelete); + if (attachable && attachable->getAttachedPanel()) { + attachable->getAttachedPanel()->removeChild(id); + attachable->setAttachedPanel(nullptr); + } + QWriteLocker lock(&_deleteLock); _overlaysToDelete.push_back(overlayToDelete); + + emit overlayDeleted(id); } QString Overlays::getOverlayType(unsigned int overlayId) const { @@ -285,6 +272,33 @@ QString Overlays::getOverlayType(unsigned int overlayId) const { return ""; } +unsigned int Overlays::getAttachedPanel(unsigned int childId) const { + Overlay::Pointer overlay = getOverlay(childId); + auto attachable = std::dynamic_pointer_cast(overlay); + if (attachable) { + return _panels.key(attachable->getAttachedPanel()); + } + return 0; +} + +void Overlays::setAttachedPanel(unsigned int childId, unsigned int panelId) { + Overlay::Pointer overlay = getOverlay(childId); + auto attachable = std::dynamic_pointer_cast(overlay); + if (attachable) { + if (_panels.contains(panelId)) { + auto panel = _panels[panelId]; + panel->addChild(childId); + attachable->setAttachedPanel(panel); + } else { + auto panel = attachable->getAttachedPanel(); + if (panel) { + panel->removeChild(childId); + attachable->setAttachedPanel(nullptr); + } + } + } +} + unsigned int Overlays::getOverlayAtPoint(const glm::vec2& point) { glm::vec2 pointCopy = point; if (qApp->isHMDMode()) { @@ -328,16 +342,7 @@ OverlayPropertyResult Overlays::getProperty(unsigned int id, const QString& prop Overlay::Pointer thisOverlay = getOverlay(id); QReadLocker lock(&_lock); if (thisOverlay) { - if (property == "attachedPanel") { - auto panelAttachable = std::dynamic_pointer_cast(thisOverlay); - if (panelAttachable) { - result.value = _panels.key(panelAttachable->getAttachedPanel()); - } else { - result.value = 0; - } - } else { - result.value = thisOverlay->getProperty(property); - } + result.value = thisOverlay->getProperty(property); } return result; } @@ -553,7 +558,9 @@ void Overlays::deletePanel(unsigned int panelId) { } } - while (!panelToDelete->children.isEmpty()) { - deleteOverlay(panelToDelete->children.takeLast()); + while (!panelToDelete->getChildren().isEmpty()) { + deleteOverlay(panelToDelete->popLastChild()); } + + emit panelDeleted(panelId); } diff --git a/interface/src/ui/overlays/Overlays.h b/interface/src/ui/overlays/Overlays.h index ee4fa5b7d4..ce2c3efeae 100644 --- a/interface/src/ui/overlays/Overlays.h +++ b/interface/src/ui/overlays/Overlays.h @@ -87,9 +87,12 @@ public slots: /// deletes a particle void deleteOverlay(unsigned int id); - /// + /// get the string type of the overlay used in addOverlay QString getOverlayType(unsigned int overlayId) const; + unsigned int getAttachedPanel(unsigned int childId) const; + void setAttachedPanel(unsigned int childId, unsigned int panelId); + /// returns the top most 2D overlay at the screen point, or 0 if not overlay at that point unsigned int getOverlayAtPoint(const glm::vec2& point); @@ -122,9 +125,12 @@ public slots: /// deletes a panel and all child overlays void deletePanel(unsigned int panelId); +signals: + void overlayDeleted(unsigned int id); + void panelDeleted(unsigned int id); + private: void cleanupOverlaysToDelete(); - void setAttachedPanel(Overlay::Pointer overlay, unsigned int overlayId, const QScriptValue& property); QMap _overlaysHUD; QMap _overlaysWorld; diff --git a/interface/src/ui/overlays/PanelAttachable.cpp b/interface/src/ui/overlays/PanelAttachable.cpp index bedf7d9fd5..e114fa143e 100644 --- a/interface/src/ui/overlays/PanelAttachable.cpp +++ b/interface/src/ui/overlays/PanelAttachable.cpp @@ -11,6 +11,8 @@ #include "PanelAttachable.h" +#include + PanelAttachable::PanelAttachable() : _attachedPanel(nullptr), _facingRotation(1, 0, 0, 0) @@ -33,3 +35,47 @@ void PanelAttachable::setTransforms(Transform* transform) { transform->postRotate(getFacingRotation() * getAttachedPanel()->getFacingRotation()); } } + +QScriptValue PanelAttachable::getProperty(QScriptEngine* scriptEngine, const QString &property) { + if (property == "offsetPosition") { + return vec3toScriptValue(scriptEngine, getOffsetPosition()); + } + if (property == "facingRotation") { + return quatToScriptValue(scriptEngine, getFacingRotation()); + } + return QScriptValue(); +} + +void PanelAttachable::setProperties(const QScriptValue &properties) { + QScriptValue offsetPosition = properties.property("offsetPosition"); + if (offsetPosition.isValid()) { + QScriptValue x = offsetPosition.property("x"); + QScriptValue y = offsetPosition.property("y"); + QScriptValue z = offsetPosition.property("z"); + + if (x.isValid() && y.isValid() && z.isValid()) { + glm::vec3 newPosition; + newPosition.x = x.toVariant().toFloat(); + newPosition.y = y.toVariant().toFloat(); + newPosition.z = z.toVariant().toFloat(); + setOffsetPosition(newPosition); + } + } + + QScriptValue facingRotation = properties.property("facingRotation"); + if (facingRotation.isValid()) { + QScriptValue x = facingRotation.property("x"); + QScriptValue y = facingRotation.property("y"); + QScriptValue z = facingRotation.property("z"); + QScriptValue w = facingRotation.property("w"); + + if (x.isValid() && y.isValid() && z.isValid() && w.isValid()) { + glm::quat newRotation; + newRotation.x = x.toVariant().toFloat(); + newRotation.y = y.toVariant().toFloat(); + newRotation.z = z.toVariant().toFloat(); + newRotation.w = w.toVariant().toFloat(); + setFacingRotation(newRotation); + } + } +} diff --git a/interface/src/ui/overlays/PanelAttachable.h b/interface/src/ui/overlays/PanelAttachable.h index 097fc9a517..e1fc490d00 100644 --- a/interface/src/ui/overlays/PanelAttachable.h +++ b/interface/src/ui/overlays/PanelAttachable.h @@ -27,8 +27,11 @@ public: glm::quat getFacingRotation() const { return _facingRotation; } void setAttachedPanel(FloatingUIPanel::Pointer panel) { _attachedPanel = panel; } - void setOffsetPosition(glm::vec3 position) { _offsetPosition = position; } - void setFacingRotation(glm::quat rotation) { _facingRotation = rotation; } + void setOffsetPosition(const glm::vec3& position) { _offsetPosition = position; } + void setFacingRotation(const glm::quat& rotation) { _facingRotation = rotation; } + + QScriptValue getProperty(QScriptEngine* scriptEngine, const QString& property); + void setProperties(const QScriptValue& properties); protected: void setTransforms(Transform* transform); From ba190b4b3453a3908a73ad36b3d51640d4386ead Mon Sep 17 00:00:00 2001 From: Zander Otavka Date: Fri, 24 Jul 2015 18:00:33 -0700 Subject: [PATCH 39/65] Improve JavaScript abstraction layer for overlays. Also move it to it's own file. --- examples/libraries/overlayManager.js | 451 +++++++++++++++++++++++++++ examples/libraries/overlayUtils.js | 259 +-------------- 2 files changed, 459 insertions(+), 251 deletions(-) create mode 100644 examples/libraries/overlayManager.js diff --git a/examples/libraries/overlayManager.js b/examples/libraries/overlayManager.js new file mode 100644 index 0000000000..55575badee --- /dev/null +++ b/examples/libraries/overlayManager.js @@ -0,0 +1,451 @@ +// +// overlayManager.js +// examples/libraries +// +// Created by Zander Otavka on 7/24/15 +// Copyright 2015 High Fidelity, Inc. +// +// Manage overlays with object oriented goodness, instead of ugly `Overlays.h` methods. +// Instead of: +// +// var billboard = Overlays.addOverlay("billboard", { visible: false }); +// ... +// Overlays.editOverlay(billboard, { visible: true }); +// ... +// Overlays.deleteOverlay(billboard); +// +// You can now do: +// +// var billboard = new BillboardOverlay({ visible: false }); +// ... +// billboard.visible = true; +// ... +// billboard.destroy(); +// +// See more on usage below. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + + +(function() { + // Delete `Overlays` from the global scope. + var Overlays = this.Overlays; + delete this.Overlays; + + var overlays = {}; + var panels = {}; + + var overlayTypes; + var Overlay, Overlay2D, Base3DOverlay, Planar3DOverlay, Volume3DOverlay; + + + // + // Create a new JavaScript object for an overlay of given ID. + // + function makeOverlayFromId(id) { + var type = Overlays.getOverlayType(id); + if (!type) { + return null; + } + var overlay = new overlayTypes[type](); + overlay._id = id; + var panelID = Overlays.getAttachedPanel(id) + if (panelID && panelID in panels) { + panels[panelID].addChild(overlay); + } + overlays[id] = overlay; + return overlay; + } + + // + // Get or create an overlay object from the id. + // + // @param knownOverlaysOnly (Optional: Boolean) + // If true, a new object will not be created. + // @param searchList (Optional: Object) + // Map of overlay id's and overlay objects. Can be generated with + // `OverlayManager.makeSearchList`. + // + function findOverlay(id, knownOverlaysOnly, searchList) { + if (id > 0) { + knownOverlaysOnly = Boolean(knownOverlaysOnly) || Boolean(searchList); + searchList = searchList || overlays; + var foundOverlay = searchList[id]; + if (foundOverlay) { + return foundOverlay; + } + if (!knownOverlaysOnly) { + return makeOverlayFromId(id); + } + } + return null; + } + + + // + // Perform global scoped operations on overlays, such as finding by ray intersection. + // + OverlayManager = { + findOnRay: function(pickRay, knownOverlaysOnly, searchList) { + var rayPickResult = Overlays.findRayIntersection(pickRay); + print("raypick " + rayPickResult.overlayID); + if (rayPickResult.intersects) { + return findOverlay(rayPickResult.overlayID, knownOverlaysOnly, searchList); + } + return null; + }, + findAtPoint: function(point, knownOverlaysOnly, searchList) { + var foundID = Overlays.getOverlayAtPoint(point); + print("at point " + foundID); + if (foundID) { + return findOverlay(foundID, knownOverlaysOnly, searchList); + } else { + var pickRay = Camera.computePickRay(point.x, point.y); + return OverlayManager.findOnRay(pickRay, knownOverlaysOnly, searchList); + } + }, + makeSearchList: function(overlayArray) { + var searchList = {}; + overlayArray.forEach(function(overlay){ + searchList[overlay._id] = overlay; + }); + return searchList; + } + }; + + + // + // Object oriented abstraction layer for overlays. + // + // Usage: + // // Create an overlay + // var billboard = new BillboardOverlay({ + // visible: true, + // isFacingAvatar: true, + // ignoreRayIntersections: false + // }); + // + // // Get a property + // var isVisible = billboard.visible; + // + // // Set a single property + // billboard.position = { x: 1, y: 3, z: 2 }; + // + // // Set multiple properties at the same time + // billboard.setProperties({ + // url: "http://images.com/overlayImage.jpg", + // dimensions: { x: 2, y: 2 } + // }); + // + // // Clone an overlay + // var clonedBillboard = billboard.clone(); + // + // // Remove an overlay from the world + // billboard.destroy(); + // + // // Remember, there is a poor orphaned JavaScript object left behind. You should + // // remove any references to it so you don't accidentally try to modify an overlay that + // // isn't there. + // billboard = undefined; + // + (function() { + var ABSTRACT = null; + overlayTypes = {}; + + function generateOverlayClass(superclass, type, properties) { + var that; + if (type == ABSTRACT) { + that = function(type, params) { + superclass.call(this, type, params); + }; + } else { + that = function(params) { + superclass.call(this, type, params); + }; + overlayTypes[type] = that; + } + + that.prototype = new superclass(); + that.prototype.constructor = that; + + properties.forEach(function(prop) { + Object.defineProperty(that.prototype, prop, { + get: function() { + return Overlays.getProperty(this._id, prop); + }, + set: function(newValue) { + var keyValuePair = {}; + keyValuePair[prop] = newValue; + this.setProperties(keyValuePair); + }, + configurable: false + }); + }); + + return that; + } + + // Supports multiple inheritance of properties. Just `concat` them onto the end of the + // properties list. + var PANEL_ATTACHABLE_FIELDS = ["offsetPosition", "facingRotation"]; + + Overlay = (function() { + var that = function(type, params) { + if (type && params) { + this._id = Overlays.addOverlay(type, params); + overlays[this._id] = this; + } else { + this._id = 0; + } + this._attachedPanelPointer = null; + }; + + that.prototype.constructor = that; + + Object.defineProperty(that.prototype, "isLoaded", { + get: function() { + return Overlays.isLoaded(this._id); + } + }); + + Object.defineProperty(that.prototype, "attachedPanel", { + get: function() { + return this._attachedPanelPointer; + } + }); + + that.prototype.getTextSize = function(text) { + return Overlays.textSize(this._id, text); + }; + + that.prototype.setProperties = function(properties) { + Overlays.editOverlay(this._id, properties); + }; + + that.prototype.clone = function() { + return makeOverlayFromId(Overlays.cloneOverlay(this._id)); + }; + + that.prototype.destroy = function() { + Overlays.deleteOverlay(this._id); + }; + + return generateOverlayClass(that, ABSTRACT, [ + "alpha", "glowLevel", "pulseMax", "pulseMin", "pulsePeriod", "glowLevelPulse", + "alphaPulse", "colorPulse", "visible", "anchor" + ]); + })(); + + Overlay2D = generateOverlayClass(Overlay, ABSTRACT, [ + "bounds", "x", "y", "width", "height" + ]); + + Base3DOverlay = generateOverlayClass(Overlay, ABSTRACT, [ + "position", "lineWidth", "rotation", "isSolid", "isFilled", "isWire", "isDashedLine", + "ignoreRayIntersection", "drawInFront", "drawOnHUD" + ]); + + Planar3DOverlay = generateOverlayClass(Base3DOverlay, ABSTRACT, [ + "dimensions" + ]); + + Volume3DOverlay = generateOverlayClass(Base3DOverlay, ABSTRACT, [ + "dimensions" + ]); + + generateOverlayClass(Overlay2D, "image", [ + "subImage", "imageURL" + ]); + + generateOverlayClass(Overlay2D, "text", [ + "font", "text", "backgroundColor", "backgroundAlpha", "leftMargin", "topMargin" + ]); + + generateOverlayClass(Planar3DOverlay, "text3d", [ + "text", "backgroundColor", "backgroundAlpha", "lineHeight", "leftMargin", "topMargin", + "rightMargin", "bottomMargin", "isFacingAvatar" + ]); + + generateOverlayClass(Volume3DOverlay, "cube", [ + "borderSize" + ]); + + generateOverlayClass(Volume3DOverlay, "sphere", [ + ]); + + generateOverlayClass(Planar3DOverlay, "circle3d", [ + "startAt", "endAt", "outerRadius", "innerRadius", "hasTickMarks", + "majorTickMarksAngle", "minorTickMarksAngle", "majorTickMarksLength", + "minorTickMarksLength", "majorTickMarksColor", "minorTickMarksColor" + ]); + + generateOverlayClass(Planar3DOverlay, "rectangle3d", [ + ]); + + generateOverlayClass(Base3DOverlay, "line3d", [ + "start", "end" + ]); + + generateOverlayClass(Planar3DOverlay, "grid", [ + "minorGridWidth", "majorGridEvery" + ]); + + generateOverlayClass(Volume3DOverlay, "localmodels", [ + ]); + + generateOverlayClass(Volume3DOverlay, "model", [ + "url", "dimensions", "textures" + ]); + + generateOverlayClass(Planar3DOverlay, "billboard", [ + "url", "subImage", "isFacingAvatar" + ].concat(PANEL_ATTACHABLE_FIELDS)); + })(); + + ImageOverlay = overlayTypes["image"]; + TextOverlay = overlayTypes["text"]; + Text3DOverlay = overlayTypes["text3d"]; + Cube3DOverlay = overlayTypes["cube"]; + Sphere3DOverlay = overlayTypes["sphere"]; + Circle3DOverlay = overlayTypes["circle3d"]; + Rectangle3DOverlay = overlayTypes["rectangle3d"]; + Line3DOverlay = overlayTypes["line3d"]; + Grid3DOverlay = overlayTypes["grid"]; + LocalModelsOverlay = overlayTypes["localmodels"]; + ModelOverlay = overlayTypes["model"]; + BillboardOverlay = overlayTypes["billboard"]; + + + // + // Object oriented abstraction layer for panels. + // + FloatingUIPanel = (function() { + var that = function(params) { + this._id = Overlays.addPanel(params); + this._children = []; + this._visible = Boolean(params.visible); + panels[this._id] = this; + this._attachedPanelPointer = null; + }; + + that.prototype.constructor = that; + + var FIELDS = ["offsetPosition", "offsetRotation", "facingRotation"]; + FIELDS.forEach(function(prop) { + Object.defineProperty(that.prototype, prop, { + get: function() { + return Overlays.getPanelProperty(this._id, prop); + }, + set: function(newValue) { + var keyValuePair = {}; + keyValuePair[prop] = newValue; + this.setProperties(keyValuePair); + }, + configurable: false + }); + }); + + var PSEUDO_FIELDS = []; + + PSEUDO_FIELDS.push("children"); + Object.defineProperty(that.prototype, "children", { + get: function() { + return this._children.slice(); + } + }); + + PSEUDO_FIELDS.push("visible"); + Object.defineProperty(that.prototype, "visible", { + get: function() { + return this._visible; + }, + set: function(visible) { + this._visible = visible; + this._children.forEach(function(child) { + child.visible = visible; + }); + } + }); + + that.prototype.addChild = function(child) { + if (child instanceof Overlay) { + Overlays.setAttachedPanel(child._id, this._id); + } else if (child instanceof FloatingUIPanel) { + child.setProperties({ + anchorPosition: { + bind: "panel", + value: this._id + }, + offsetRotation: { + bind: "panel", + value: this._id + } + }); + } + child._attachedPanelPointer = this; + child.visible = this.visible; + this._children.push(child); + return child; + }; + + that.prototype.removeChild = function(child) { + var i = this._children.indexOf(child); + if (i >= 0) { + if (child instanceof Overlay) { + Overlays.setAttachedPanel(child._id, 0); + } else if (child instanceof FloatingUIPanel) { + child.setProperties({ + anchorPosition: { + bind: "myAvatar" + }, + offsetRotation: { + bind: "myAvatar" + } + }); + } + child._attachedPanelPointer = null; + this._children.splice(i, 1); + } + }; + + that.prototype.setProperties = function(properties) { + for (var i in PSEUDO_FIELDS) { + if (properties[PSEUDO_FIELDS[i]] !== undefined) { + this[PSEUDO_FIELDS[i]] = properties[PSEUDO_FIELDS[i]]; + } + } + Overlays.editPanel(this._id, properties); + }; + + that.prototype.destroy = function() { + Overlays.deletePanel(this._id); + }; + + return that; + })(); + + + function onOverlayDeleted(id) { + if (id in overlays) { + if (overlays[id]._attachedPanelPointer) { + overlays[id]._attachedPanelPointer.removeChild(overlays[id]); + } + delete overlays[id]; + } + } + + function onPanelDeleted(id) { + if (id in panels) { + panels[id]._children.forEach(function(child) { + print(JSON.stringify(child.destroy)); + child.destroy(); + }); + delete panels[id]; + } + } + + Overlays.overlayDeleted.connect(onOverlayDeleted); + Overlays.panelDeleted.connect(onPanelDeleted); +})(); diff --git a/examples/libraries/overlayUtils.js b/examples/libraries/overlayUtils.js index 3acab0103b..a5622ec435 100644 --- a/examples/libraries/overlayUtils.js +++ b/examples/libraries/overlayUtils.js @@ -2,39 +2,16 @@ // overlayUtils.js // examples/libraries // -// Modified by Zander Otavka on 7/15/15 -// Copyright 2014 High Fidelity, Inc. -// -// Manage overlays with object oriented goodness, instead of ugly `Overlays.h` methods. -// Instead of: -// -// var billboard = Overlays.addOverlay("billboard", { visible: false }); -// ... -// Overlays.editOverlay(billboard, { visible: true }); -// ... -// Overlays.deleteOverlay(billboard); -// -// You can now do: -// -// var billboard = new BillboardOverlay({ visible: false }); -// ... -// billboard.visible = true; -// ... -// billboard.destroy(); -// -// See more on usage below. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// Copyright 2015 High Fidelity, Inc. // -/** - * DEPRECATION WARNING: Will be deprecated soon in favor of FloatingUIPanel. - * - * OverlayGroup provides a way to create composite overlays and control their - * position relative to a settable rootPosition and rootRotation. - */ +// +// DEPRECATION WARNING: Will be deprecated soon in favor of FloatingUIPanel. +// +// OverlayGroup provides a way to create composite overlays and control their +// position relative to a settable rootPosition and rootRotation. +// OverlayGroup = function(opts) { var that = {}; @@ -92,226 +69,6 @@ OverlayGroup = function(opts) { } overlays = {}; } - + return that; }; - - -/** - * Object oriented abstraction layer for overlays. - * - * Usage: - * // Create an overlay - * var billboard = new BillboardOverlay({ - * visible: true, - * isFacingAvatar: true, - * ignoreRayIntersections: false - * }); - * - * // Get a property - * var isVisible = billboard.visible; - * - * // Set a single property - * billboard.position = { x: 1, y: 3, z: 2 }; - * - * // Set multiple properties at the same time - * billboard.setProperties({ - * url: "http://images.com/overlayImage.jpg", - * dimensions: { x: 2, y: 2 } - * }); - * - * // Clone an overlay - * var clonedBillboard = billboard.clone(); - * - * // Remove an overlay from the world - * billboard.destroy(); - * - * // Remember, there is a poor orphaned JavaScript object left behind. You should remove any - * // references to it so you don't accidentally try to modify an overlay that isn't there. - * billboard = undefined; - */ -(function() { - var ABSTRACT = null; - - function generateOverlayClass(superclass, type, properties) { - var that; - if (type == ABSTRACT) { - that = function(type, params) { - superclass.apply(this, [type, params]); - }; - } else { - that = function(params) { - superclass.apply(this, [type, params]); - }; - } - - that.prototype = new superclass(); - that.prototype.constructor = that; - - properties.forEach(function(prop) { - Object.defineProperty(that.prototype, prop, { - get: function() { - return Overlays.getProperty(this._id, prop); - }, - set: function(newValue) { - var keyValuePair = {}; - keyValuePair[prop] = newValue; - this.setProperties(keyValuePair); - }, - configurable: true - }); - }); - - return that; - } - - - // Supports multiple inheritance of properties. Just `concat` them onto the end of the - // properties list. - var PANEL_ATTACHABLE_FIELDS = ["attachedPanel"]; - - // TODO: finish exposing all overlay classes. - - var Overlay = (function() { - var BaseOverlay = (function() { - var that = function(type, params) { - Object.apply(this, []); - if (type && params) { - this._type = type; - this._id = Overlays.addOverlay(type, params); - } else { - this._type = ""; - this._id = 0; - } - this._attachedPanelPointer = null; - }; - - that.prototype = new Object(); - that.prototype.constructor = that; - - Object.defineProperty(that.prototype, "overlayType", { - get: function() { - return this._type; - } - }); - - that.prototype.setProperties = function(properties) { - Overlays.editOverlay(this._id, properties); - }; - - that.prototype.clone = function() { - var clone = new this.constructor(); - clone._type = this._type; - clone._id = Overlays.cloneOverlay(this._id); - if (this._attachedPanelPointer) { - this._attachedPanelPointer.addChild(clone); - } - return clone; - }; - - that.prototype.destroy = function() { - Overlays.deleteOverlay(this._id); - }; - - return that; - }()); - - return generateOverlayClass(BaseOverlay, ABSTRACT, [ - "alpha", "glowLevel", "pulseMax", "pulseMin", "pulsePeriod", "glowLevelPulse", - "alphaPulse", "colorPulse", "visible", "anchor" - ]); - }()); - - var Base3DOverlay = generateOverlayClass(Overlay, ABSTRACT, [ - "position", "lineWidth", "rotation", "isSolid", "isFilled", "isWire", "isDashedLine", - "ignoreRayIntersection", "drawInFront", "drawOnHUD" - ]); - - var Planar3DOverlay = generateOverlayClass(Base3DOverlay, ABSTRACT, [ - "dimensions" - ]); - - BillboardOverlay = generateOverlayClass(Planar3DOverlay, "billboard", [ - "url", "subImage", "isFacingAvatar", "offsetPosition" - ].concat(PANEL_ATTACHABLE_FIELDS)); -}()); - - -/** - * Object oriented abstraction layer for panels. - */ -FloatingUIPanel = (function() { - var that = function(params) { - this._id = Overlays.addPanel(params); - this._children = []; - }; - - var FIELDS = ["offsetPosition", "offsetRotation", "facingRotation"]; - FIELDS.forEach(function(prop) { - Object.defineProperty(that.prototype, prop, { - get: function() { - return Overlays.getPanelProperty(this._id, prop); - }, - set: function(newValue) { - var keyValuePair = {}; - keyValuePair[prop] = newValue; - this.setProperties(keyValuePair); - }, - configurable: false - }); - }); - - Object.defineProperty(that.prototype, "children", { - get: function() { - return this._children.slice(); - } - }) - - that.prototype.addChild = function(overlay) { - overlay.attachedPanel = this._id; - overlay._attachedPanelPointer = this; - this._children.push(overlay); - return overlay; - }; - - that.prototype.removeChild = function(overlay) { - var i = this._children.indexOf(overlay); - if (i >= 0) { - overlay.attachedPanel = 0; - overlay._attachedPanelPointer = null; - this._children.splice(i, 1); - } - }; - - that.prototype.setVisible = function(visible) { - for (var i in this._children) { - this._children[i].visible = visible; - } - }; - - that.prototype.setProperties = function(properties) { - Overlays.editPanel(this._id, properties); - }; - - that.prototype.destroy = function() { - Overlays.deletePanel(this._id); - var i = _panels.indexOf(this); - if (i >= 0) { - _panels.splice(i, 1); - } - }; - - that.prototype.findRayIntersection = function(pickRay) { - var rayPickResult = Overlays.findRayIntersection(pickRay); - if (rayPickResult.intersects) { - for (var i in this._children) { - if (this._children[i]._id == rayPickResult.overlayID) { - return this._children[i]; - } - } - } - return null; - }; - - return that; -}()); From 37a1ad76262a1b9b7e109548e012ee7640e0777b Mon Sep 17 00:00:00 2001 From: Zander Otavka Date: Fri, 24 Jul 2015 18:02:18 -0700 Subject: [PATCH 40/65] Improve floatingUIExample.js. Demonstrates overlayManager.js functionality. --- examples/example/ui/floatingUIExample.js | 247 ++++++++++++----------- 1 file changed, 133 insertions(+), 114 deletions(-) diff --git a/examples/example/ui/floatingUIExample.js b/examples/example/ui/floatingUIExample.js index 3b555efce3..9c4e43be94 100644 --- a/examples/example/ui/floatingUIExample.js +++ b/examples/example/ui/floatingUIExample.js @@ -10,140 +10,159 @@ // Script.include([ - "../../libraries/globals.js", - "../../libraries/overlayUtils.js", + "../../libraries/globals.js", + "../../libraries/overlayManager.js", ]); var BG_IMAGE_URL = HIFI_PUBLIC_BUCKET + "images/card-bg.svg"; var RED_DOT_IMAGE_URL = HIFI_PUBLIC_BUCKET + "images/red-dot.svg"; var BLUE_SQUARE_IMAGE_URL = HIFI_PUBLIC_BUCKET + "images/blue-square.svg"; -var BLANK_ROTATION = { x: 0, y: 0, z: 0, w: 0 }; - -function isBlank(rotation) { - return rotation.x == BLANK_ROTATION.x && - rotation.y == BLANK_ROTATION.y && - rotation.z == BLANK_ROTATION.z && - rotation.w == BLANK_ROTATION.w; -} - -var panel = new FloatingUIPanel({ - offsetPosition: { x: 0, y: 0, z: 1 }, +var mainPanel = new FloatingUIPanel({ + offsetRotation: { + bind: "quat", + value: { w: 1, x: 0, y: 0, z: 0 } + }, + offsetPosition: { x: 0, y: 0.4, z: 1 } }); -var bg = panel.addChild(new BillboardOverlay({ - url: BG_IMAGE_URL, - dimensions: { - x: 0.5, - y: 0.5, - }, - isFacingAvatar: false, - visible: true, - alpha: 1.0, - ignoreRayIntersection: false, +var bluePanel = mainPanel.addChild(new FloatingUIPanel ({ + offsetPosition: { x: 0.1, y: 0.1, z: -0.2 } })); -var redDot = panel.addChild(new BillboardOverlay({ - url: RED_DOT_IMAGE_URL, - dimensions: { - x: 0.1, - y: 0.1, - }, - isFacingAvatar: false, - visible: true, - alpha: 1.0, - ignoreRayIntersection: false, - offsetPosition: { - x: -0.15, - y: -0.15, - z: -0.001 - } +var mainPanelBackground = new BillboardOverlay({ + url: BG_IMAGE_URL, + dimensions: { + x: 0.5, + y: 0.5, + }, + isFacingAvatar: false, + alpha: 1.0, + ignoreRayIntersection: false, + offsetPosition: { + x: 0, + y: 0, + z: 0.001 + } +}); + +var bluePanelBackground = mainPanelBackground.clone(); +bluePanelBackground.dimensions = { + x: 0.3, + y: 0.3 +}; + +mainPanel.addChild(mainPanelBackground); +bluePanel.addChild(bluePanelBackground); + +var redDot = mainPanel.addChild(new BillboardOverlay({ + url: RED_DOT_IMAGE_URL, + dimensions: { + x: 0.1, + y: 0.1, + }, + isFacingAvatar: false, + alpha: 1.0, + ignoreRayIntersection: false, + offsetPosition: { + x: -0.15, + y: -0.15, + z: 0 + } })); -var redDot2 = panel.addChild(new BillboardOverlay({ - url: RED_DOT_IMAGE_URL, - dimensions: { - x: 0.1, - y: 0.1, - }, - isFacingAvatar: false, - visible: true, - alpha: 1.0, - ignoreRayIntersection: false, - offsetPosition: { - x: -0.15, - y: 0, - z: -0.001 - } +var redDot2 = mainPanel.addChild(new BillboardOverlay({ + url: RED_DOT_IMAGE_URL, + dimensions: { + x: 0.1, + y: 0.1, + }, + isFacingAvatar: false, + alpha: 1.0, + ignoreRayIntersection: false, + offsetPosition: { + x: -0.155, + y: 0.005, + z: 0 + } })); -var blueSquare = panel.addChild(new BillboardOverlay({ - url: BLUE_SQUARE_IMAGE_URL, - dimensions: { - x: 0.1, - y: 0.1, - }, - isFacingAvatar: false, - visible: true, - alpha: 1.0, - ignoreRayIntersection: false, - offsetPosition: { - x: 0.1, - y: 0, - z: -0.001 - } +var blueSquare = bluePanel.addChild(new BillboardOverlay({ + url: BLUE_SQUARE_IMAGE_URL, + dimensions: { + x: 0.1, + y: 0.1, + }, + isFacingAvatar: false, + alpha: 1.0, + ignoreRayIntersection: false, + offsetPosition: { + x: 0.055, + y: -0.055, + z: 0 + } })); -var blueSquare2 = panel.addChild(new BillboardOverlay({ - url: BLUE_SQUARE_IMAGE_URL, - dimensions: { - x: 0.1, - y: 0.1, - }, - isFacingAvatar: false, - visible: true, - alpha: 1.0, - ignoreRayIntersection: false, - offsetPosition: { - x: 0.1, - y: 0.11, - z: -0.001 - } +var blueSquare2 = bluePanel.addChild(new BillboardOverlay({ + url: BLUE_SQUARE_IMAGE_URL, + dimensions: { + x: 0.1, + y: 0.1, + }, + isFacingAvatar: false, + alpha: 1.0, + ignoreRayIntersection: false, + offsetPosition: { + x: 0.055, + y: 0.055, + z: 0 + } })); var blueSquare3 = blueSquare2.clone(); blueSquare3.offsetPosition = { - x: -0.01, - y: 0.11, - z: -0.001 + x: -0.055, + y: 0.055, + z: 0 }; -blueSquare3.ignoreRayIntersection = false; -Controller.mousePressEvent.connect(function(event) { - if (event.isRightButton) { - var newOffsetRotation; - print(JSON.stringify(panel.offsetRotation)) - if (isBlank(panel.offsetRotation)) { - newOffsetRotation = Quat.multiply(MyAvatar.orientation, { x: 0, y: 1, z: 0, w: 0 }); - } else { - newOffsetRotation = BLANK_ROTATION; - } - panel.offsetRotation = newOffsetRotation; - } else if (event.isLeftButton) { - var pickRay = Camera.computePickRay(event.x, event.y) - var overlay = panel.findRayIntersection(pickRay); - if (overlay) { - var oldPos = overlay.offsetPosition; - var newPos = { - x: Number(oldPos.x), - y: Number(oldPos.y), - z: Number(oldPos.z) + 0.1 - } - overlay.offsetPosition = newPos; - } - } -}); -Script.scriptEnding.connect(function() { - panel.destroy(); -}); \ No newline at end of file +function onMouseDown(event) { + isLeftClick = event.isLeftButton; + isRightClick = event.isRightButton; +} + +function onMouseMove(event) { + isLeftClick = isRightClick = false; +} + +function onMouseUp(event) { + if (isLeftClick && event.isLeftButton) { + var overlay = OverlayManager.findAtPoint({ x: event.x, y: event.y }); + print(overlay.attachedPanel); + if (overlay.attachedPanel === bluePanel) { + overlay.destroy(); + } else if (overlay) { + var oldPos = overlay.offsetPosition; + var newPos = { + x: Number(oldPos.x), + y: Number(oldPos.y), + z: Number(oldPos.z) + 0.1 + }; + overlay.offsetPosition = newPos; + } + } + if (isRightClick && event.isRightButton) { + mainPanel.visible = !mainPanel.visible; + } + isLeftClick = isRightClick = false; +} + +function onScriptEnd() { + mainPanel.destroy(); +} + +Controller.mousePressEvent.connect(onMouseDown); +Controller.mouseMoveEvent.connect(onMouseMove); +Controller.mouseReleaseEvent.connect(onMouseUp); +Script.scriptEnding.connect(onScriptEnd); \ No newline at end of file From 3983c31219f4de1b93fb861d83e2fe9bebb13fcf Mon Sep 17 00:00:00 2001 From: Zander Otavka Date: Fri, 24 Jul 2015 18:03:21 -0700 Subject: [PATCH 41/65] Add controlPanel.js script. Right click to open a control panel. Control panel allows toggling of face mute, mic mute, and address bar. --- examples/controlPanel.js | 221 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 221 insertions(+) create mode 100644 examples/controlPanel.js diff --git a/examples/controlPanel.js b/examples/controlPanel.js new file mode 100644 index 0000000000..6373dfc921 --- /dev/null +++ b/examples/controlPanel.js @@ -0,0 +1,221 @@ +// +// controlPanel.js +// examples +// +// Created by Zander Otavka on 7/15/15. +// Copyright 2015 High Fidelity, Inc. +// +// Shows a few common controls in a FloatingUIPanel on right click. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +Script.include([ + "libraries/globals.js", + "libraries/overlayManager.js", +]); + +var BG_IMAGE_URL = HIFI_PUBLIC_BUCKET + "images/card-bg.svg"; +var CLOSE_IMAGE_URL = "file:///Users/zander/Desktop/assets/close.svg"; +var MIC_IMAGE_URL = "file:///Users/zander/Desktop/assets/mic-composite.svg"; +var FACE_IMAGE_URL = "file:///Users/zander/Desktop/assets/face-composite.svg"; +var ADDRESS_BAR_IMAGE_URL = "file:///Users/zander/Desktop/assets/address-bar-composite.svg"; + +var panel = new FloatingUIPanel({ + anchorPosition: { + bind: "myAvatar" + }, + offsetPosition: { x: 0, y: 0.4, z: 1 } +}); + +var background = new BillboardOverlay({ + url: BG_IMAGE_URL, + dimensions: { + x: 0.5, + y: 0.5, + }, + isFacingAvatar: false, + visible: true, + alpha: 1.0, + ignoreRayIntersection: false +}); +panel.addChild(background); + +var closeButton = new BillboardOverlay({ + url: CLOSE_IMAGE_URL, + dimensions: { + x: 0.15, + y: 0.15, + }, + isFacingAvatar: false, + visible: true, + alpha: 1.0, + ignoreRayIntersection: false, + offsetPosition: { + x: -0.1, + y: 0.1, + z: -0.001 + } +}); +closeButton.onClick = function(event) { + panel.visible = false; +}; +panel.addChild(closeButton); + +var micMuteButton = new BillboardOverlay({ + url: MIC_IMAGE_URL, + subImage: { + x: 0, + y: 0, + width: 45, + height: 45 + }, + dimensions: { + x: 0.15, + y: 0.15, + }, + isFacingAvatar: false, + visible: true, + alpha: 1.0, + ignoreRayIntersection: false, + offsetPosition: { + x: 0.1, + y: 0.1, + z: -0.001 + } +}); +micMuteButton.onClick = function(event) { + AudioDevice.toggleMute(); +}; +panel.addChild(micMuteButton); + +var faceMuteButton = new BillboardOverlay({ + url: FACE_IMAGE_URL, + subImage: { + x: 0, + y: 0, + width: 45, + height: 45 + }, + dimensions: { + x: 0.15, + y: 0.15, + }, + isFacingAvatar: false, + visible: true, + alpha: 1.0, + ignoreRayIntersection: false, + offsetPosition: { + x: 0.1, + y: -0.1, + z: -0.001 + } +}); +faceMuteButton.onClick = function(event) { + FaceTracker.toggleMute(); +}; +panel.addChild(faceMuteButton); + +var addressBarButton = new BillboardOverlay({ + url: ADDRESS_BAR_IMAGE_URL, + subImage: { + x: 0, + y: 0, + width: 45, + height: 45 + }, + dimensions: { + x: 0.15, + y: 0.15, + }, + isFacingAvatar: false, + visible: true, + alpha: 1.0, + ignoreRayIntersection: false, + offsetPosition: { + x: -0.1, + y: -0.1, + z: -0.001 + } +}); +addressBarButton.onClick = function(event) { + DialogsManager.toggleAddressBar(); +}; +panel.addChild(addressBarButton); + + +function onMicMuteToggled() { + var offset; + if (AudioDevice.getMuted()) { + offset = 45; + } else { + offset = 0; + } + micMuteButton.subImage = { + x: offset, + y: 0, + width: 45, + height: 45 + }; +} +onMicMuteToggled(); + +function onFaceMuteToggled() { + var offset; + if (FaceTracker.getMuted()) { + offset = 45; + } else { + offset = 0; + } + faceMuteButton.subImage = { + x: offset, + y: 0, + width: 45, + height: 45 + }; +} +onFaceMuteToggled(); + +var isLeftClick = false, + isRightClick = false; + +function onMouseDown(event) { + isLeftClick = event.isLeftButton; + isRightClick = event.isRightButton; +} + +function onMouseMove(event) { + isLeftClick = isRightClick = false; +} + +function onMouseUp(event) { + if (isLeftClick && event.isLeftButton) { + var overlay = OverlayManager.findAtPoint({ x: event.x, y: event.y }); + if (overlay && overlay.onClick) { + overlay.onClick(event); + } + } + if (isRightClick && event.isRightButton) { + panel.setProperties({ + visible: true, + offsetRotation: { + bind: "quat", + value: Quat.multiply(MyAvatar.orientation, { x: 0, y: 1, z: 0, w: 0 }) + } + }); + } + isLeftClick = isRightClick = false; +} + +function onScriptEnd(event) { + panel.destroy(); + panel2.destroy(); +} + +Controller.mousePressEvent.connect(onMouseDown); +Controller.mouseMoveEvent.connect(onMouseMove); +Controller.mouseReleaseEvent.connect(onMouseUp); +AudioDevice.muteToggled.connect(onMicMuteToggled); +FaceTracker.muteToggled.connect(onFaceMuteToggled); +Script.scriptEnding.connect(onScriptEnd); From c37c3ec2d2f7ed5f31abf290230a7d8ac72d2746 Mon Sep 17 00:00:00 2001 From: Zander Otavka Date: Fri, 24 Jul 2015 19:30:30 -0700 Subject: [PATCH 42/65] Use images off the amazon bucket, not file://. --- examples/controlPanel.js | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/examples/controlPanel.js b/examples/controlPanel.js index 6373dfc921..837b59ffa0 100644 --- a/examples/controlPanel.js +++ b/examples/controlPanel.js @@ -17,10 +17,10 @@ Script.include([ ]); var BG_IMAGE_URL = HIFI_PUBLIC_BUCKET + "images/card-bg.svg"; -var CLOSE_IMAGE_URL = "file:///Users/zander/Desktop/assets/close.svg"; -var MIC_IMAGE_URL = "file:///Users/zander/Desktop/assets/mic-composite.svg"; -var FACE_IMAGE_URL = "file:///Users/zander/Desktop/assets/face-composite.svg"; -var ADDRESS_BAR_IMAGE_URL = "file:///Users/zander/Desktop/assets/address-bar-composite.svg"; +var CLOSE_IMAGE_URL = HIFI_PUBLIC_BUCKET + "images/close.svg"; +var MIC_IMAGE_URL = HIFI_PUBLIC_BUCKET + "images/mic-toggle.svg"; +var FACE_IMAGE_URL = HIFI_PUBLIC_BUCKET + "images/face-toggle.svg"; +var ADDRESS_BAR_IMAGE_URL = HIFI_PUBLIC_BUCKET + "images/address-bar-toggle.svg"; var panel = new FloatingUIPanel({ anchorPosition: { @@ -36,7 +36,6 @@ var background = new BillboardOverlay({ y: 0.5, }, isFacingAvatar: false, - visible: true, alpha: 1.0, ignoreRayIntersection: false }); @@ -49,7 +48,6 @@ var closeButton = new BillboardOverlay({ y: 0.15, }, isFacingAvatar: false, - visible: true, alpha: 1.0, ignoreRayIntersection: false, offsetPosition: { @@ -76,7 +74,6 @@ var micMuteButton = new BillboardOverlay({ y: 0.15, }, isFacingAvatar: false, - visible: true, alpha: 1.0, ignoreRayIntersection: false, offsetPosition: { @@ -103,7 +100,6 @@ var faceMuteButton = new BillboardOverlay({ y: 0.15, }, isFacingAvatar: false, - visible: true, alpha: 1.0, ignoreRayIntersection: false, offsetPosition: { @@ -130,7 +126,6 @@ var addressBarButton = new BillboardOverlay({ y: 0.15, }, isFacingAvatar: false, - visible: true, alpha: 1.0, ignoreRayIntersection: false, offsetPosition: { @@ -198,7 +193,7 @@ function onMouseUp(event) { } if (isRightClick && event.isRightButton) { panel.setProperties({ - visible: true, + visible: !panel.visible, offsetRotation: { bind: "quat", value: Quat.multiply(MyAvatar.orientation, { x: 0, y: 1, z: 0, w: 0 }) @@ -210,7 +205,6 @@ function onMouseUp(event) { function onScriptEnd(event) { panel.destroy(); - panel2.destroy(); } Controller.mousePressEvent.connect(onMouseDown); From 9bd72e5769c7348b6791a6b5c7273a2c4af0c1dc Mon Sep 17 00:00:00 2001 From: Zander Otavka Date: Fri, 24 Jul 2015 20:07:38 -0700 Subject: [PATCH 43/65] Tweak PanelAttachable::setTransforms. Use reference instead of pointer, and make it virtual. --- interface/src/ui/overlays/BillboardOverlay.cpp | 8 ++++---- interface/src/ui/overlays/BillboardOverlay.h | 2 +- interface/src/ui/overlays/PanelAttachable.cpp | 11 +++++------ interface/src/ui/overlays/PanelAttachable.h | 2 +- 4 files changed, 11 insertions(+), 12 deletions(-) diff --git a/interface/src/ui/overlays/BillboardOverlay.cpp b/interface/src/ui/overlays/BillboardOverlay.cpp index 8ffa909e12..3bd1f56ba3 100644 --- a/interface/src/ui/overlays/BillboardOverlay.cpp +++ b/interface/src/ui/overlays/BillboardOverlay.cpp @@ -39,7 +39,7 @@ BillboardOverlay::BillboardOverlay(const BillboardOverlay* billboardOverlay) : { } -void BillboardOverlay::setTransforms(Transform *transform) { +void BillboardOverlay::setTransforms(Transform& transform) { PanelAttachable::setTransforms(transform); if (_isFacingAvatar) { glm::quat rotation = Application::getInstance()->getCamera()->getOrientation(); @@ -49,7 +49,7 @@ void BillboardOverlay::setTransforms(Transform *transform) { } void BillboardOverlay::update(float deltatime) { - setTransforms(&_transform); + setTransforms(_transform); } void BillboardOverlay::render(RenderArgs* args) { @@ -98,7 +98,7 @@ void BillboardOverlay::render(RenderArgs* args) { xColor color = getColor(); float alpha = getAlpha(); - setTransforms(&_transform); + setTransforms(_transform); Transform transform = _transform; transform.postScale(glm::vec3(getDimensions(), 1.0f)); @@ -197,7 +197,7 @@ bool BillboardOverlay::findRayIntersection(const glm::vec3& origin, const glm::v float& distance, BoxFace& face) { if (_texture && _texture->isLoaded()) { // Make sure position and rotation is updated. - setTransforms(&_transform); + setTransforms(_transform); // Produce the dimensions of the billboard based on the image's aspect ratio and the overlay's scale. bool isNull = _fromImage.isNull(); diff --git a/interface/src/ui/overlays/BillboardOverlay.h b/interface/src/ui/overlays/BillboardOverlay.h index 47be764f2f..a034612e71 100644 --- a/interface/src/ui/overlays/BillboardOverlay.h +++ b/interface/src/ui/overlays/BillboardOverlay.h @@ -43,7 +43,7 @@ public: virtual BillboardOverlay* createClone() const; protected: - void setTransforms(Transform* transform); + virtual void setTransforms(Transform& transform); private: void setBillboardURL(const QString& url); diff --git a/interface/src/ui/overlays/PanelAttachable.cpp b/interface/src/ui/overlays/PanelAttachable.cpp index e114fa143e..ac4730cfe8 100644 --- a/interface/src/ui/overlays/PanelAttachable.cpp +++ b/interface/src/ui/overlays/PanelAttachable.cpp @@ -26,13 +26,12 @@ PanelAttachable::PanelAttachable(const PanelAttachable* panelAttachable) : { } -void PanelAttachable::setTransforms(Transform* transform) { - Q_ASSERT(transform != nullptr); +void PanelAttachable::setTransforms(Transform& transform) { if (getAttachedPanel()) { - transform->setTranslation(getAttachedPanel()->getAnchorPosition()); - transform->setRotation(getAttachedPanel()->getOffsetRotation()); - transform->postTranslate(getOffsetPosition() + getAttachedPanel()->getOffsetPosition()); - transform->postRotate(getFacingRotation() * getAttachedPanel()->getFacingRotation()); + transform.setTranslation(getAttachedPanel()->getAnchorPosition()); + transform.setRotation(getAttachedPanel()->getOffsetRotation()); + transform.postTranslate(getOffsetPosition() + getAttachedPanel()->getOffsetPosition()); + transform.postRotate(getFacingRotation() * getAttachedPanel()->getFacingRotation()); } } diff --git a/interface/src/ui/overlays/PanelAttachable.h b/interface/src/ui/overlays/PanelAttachable.h index e1fc490d00..9776ac5ecb 100644 --- a/interface/src/ui/overlays/PanelAttachable.h +++ b/interface/src/ui/overlays/PanelAttachable.h @@ -34,7 +34,7 @@ public: void setProperties(const QScriptValue& properties); protected: - void setTransforms(Transform* transform); + virtual void setTransforms(Transform& transform); private: FloatingUIPanel::Pointer _attachedPanel; From 7973c2d469f1be8747488cfbe690476887e922a6 Mon Sep 17 00:00:00 2001 From: Zander Otavka Date: Fri, 31 Jul 2015 10:55:51 -0700 Subject: [PATCH 44/65] Fix compile issue on Linux and Windows. Shoutout to Andrew for help finding the missing #include. --- interface/src/ui/overlays/FloatingUIPanel.h | 1 + 1 file changed, 1 insertion(+) diff --git a/interface/src/ui/overlays/FloatingUIPanel.h b/interface/src/ui/overlays/FloatingUIPanel.h index f65fa93c26..f84ac32fac 100644 --- a/interface/src/ui/overlays/FloatingUIPanel.h +++ b/interface/src/ui/overlays/FloatingUIPanel.h @@ -13,6 +13,7 @@ #define hifi_FloatingUIPanel_h #include +#include #include #include From b0afdb21ad3daf1b6ae6b139d39f32ba4c9fe48f Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 31 Jul 2015 14:29:05 -0700 Subject: [PATCH 45/65] correct the unique_ptr char allocations --- assignment-client/src/Agent.cpp | 2 +- ice-server/src/IceServer.cpp | 2 +- libraries/networking/src/NLPacket.cpp | 4 ++-- libraries/networking/src/NLPacket.h | 4 ++-- libraries/networking/src/PacketReceiver.cpp | 2 +- libraries/networking/src/udt/Packet.cpp | 6 +++--- libraries/networking/src/udt/Packet.h | 6 +++--- 7 files changed, 13 insertions(+), 13 deletions(-) diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index da588bc316..43866ce3a6 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -70,7 +70,7 @@ void Agent::handleOctreePacket(QSharedPointer packet, SharedNodePointe // pull out the piggybacked packet and create a new QSharedPointer for it int piggyBackedSizeWithHeader = packet->getPayloadSize() - statsMessageLength; - std::unique_ptr buffer = std::unique_ptr(new char[piggyBackedSizeWithHeader]); + auto buffer = std::unique_ptr(new char[piggyBackedSizeWithHeader]); memcpy(buffer.get(), packet->getPayload() + statsMessageLength, piggyBackedSizeWithHeader); auto newPacket = NLPacket::fromReceivedPacket(std::move(buffer), piggyBackedSizeWithHeader, packet->getSenderSockAddr()); diff --git a/ice-server/src/IceServer.cpp b/ice-server/src/IceServer.cpp index 46c1582e7d..2a4e2b4300 100644 --- a/ice-server/src/IceServer.cpp +++ b/ice-server/src/IceServer.cpp @@ -50,7 +50,7 @@ void IceServer::processDatagrams() { while (_serverSocket.hasPendingDatagrams()) { // setup a buffer to read the packet into int packetSizeWithHeader = _serverSocket.pendingDatagramSize(); - std::unique_ptr buffer = std::unique_ptr(new char[packetSizeWithHeader]); + auto buffer = std::unique_ptr(new char[packetSizeWithHeader]); _serverSocket.readDatagram(buffer.get(), packetSizeWithHeader, sendingSockAddr.getAddressPointer(), sendingSockAddr.getPortPointer()); diff --git a/libraries/networking/src/NLPacket.cpp b/libraries/networking/src/NLPacket.cpp index 7a6503dbc3..7ec10fc4ce 100644 --- a/libraries/networking/src/NLPacket.cpp +++ b/libraries/networking/src/NLPacket.cpp @@ -46,7 +46,7 @@ std::unique_ptr NLPacket::create(PacketType::Value type, qint64 size) return packet; } -std::unique_ptr NLPacket::fromReceivedPacket(std::unique_ptr data, qint64 size, +std::unique_ptr NLPacket::fromReceivedPacket(std::unique_ptr data, qint64 size, const HifiSockAddr& senderSockAddr) { // Fail with null data Q_ASSERT(data); @@ -85,7 +85,7 @@ NLPacket::NLPacket(const NLPacket& other) : Packet(other) { } -NLPacket::NLPacket(std::unique_ptr data, qint64 size, const HifiSockAddr& senderSockAddr) : +NLPacket::NLPacket(std::unique_ptr data, qint64 size, const HifiSockAddr& senderSockAddr) : Packet(std::move(data), size, senderSockAddr) { adjustPayloadStartAndCapacity(); diff --git a/libraries/networking/src/NLPacket.h b/libraries/networking/src/NLPacket.h index 669278ed65..6ebf15ffec 100644 --- a/libraries/networking/src/NLPacket.h +++ b/libraries/networking/src/NLPacket.h @@ -20,7 +20,7 @@ class NLPacket : public Packet { Q_OBJECT public: static std::unique_ptr create(PacketType::Value type, qint64 size = -1); - static std::unique_ptr fromReceivedPacket(std::unique_ptr data, qint64 size, + static std::unique_ptr fromReceivedPacket(std::unique_ptr data, qint64 size, const HifiSockAddr& senderSockAddr); // Provided for convenience, try to limit use static std::unique_ptr createCopy(const NLPacket& other); @@ -45,7 +45,7 @@ protected: NLPacket(PacketType::Value type); NLPacket(PacketType::Value type, qint64 size); - NLPacket(std::unique_ptr data, qint64 size, const HifiSockAddr& senderSockAddr); + NLPacket(std::unique_ptr data, qint64 size, const HifiSockAddr& senderSockAddr); NLPacket(const NLPacket& other); void readSourceID(); diff --git a/libraries/networking/src/PacketReceiver.cpp b/libraries/networking/src/PacketReceiver.cpp index 5fc327673d..b1a77d4c39 100644 --- a/libraries/networking/src/PacketReceiver.cpp +++ b/libraries/networking/src/PacketReceiver.cpp @@ -243,7 +243,7 @@ void PacketReceiver::processDatagrams() { while (nodeList && nodeList->getNodeSocket().hasPendingDatagrams()) { // setup a buffer to read the packet into int packetSizeWithHeader = nodeList->getNodeSocket().pendingDatagramSize(); - std::unique_ptr buffer = std::unique_ptr(new char[packetSizeWithHeader]); + auto buffer = std::unique_ptr(new char[packetSizeWithHeader]); // if we're supposed to drop this packet then break out here if (_shouldDropPackets) { diff --git a/libraries/networking/src/udt/Packet.cpp b/libraries/networking/src/udt/Packet.cpp index 02a44c4a4f..13f8a39e26 100644 --- a/libraries/networking/src/udt/Packet.cpp +++ b/libraries/networking/src/udt/Packet.cpp @@ -31,7 +31,7 @@ std::unique_ptr Packet::create(PacketType::Value type, qint64 size) { return packet; } -std::unique_ptr Packet::fromReceivedPacket(std::unique_ptr data, qint64 size, const HifiSockAddr& senderSockAddr) { +std::unique_ptr Packet::fromReceivedPacket(std::unique_ptr data, qint64 size, const HifiSockAddr& senderSockAddr) { // Fail with invalid size Q_ASSERT(size >= 0); @@ -82,7 +82,7 @@ Packet::Packet(PacketType::Value type, qint64 size) : } } -Packet::Packet(std::unique_ptr data, qint64 size, const HifiSockAddr& senderSockAddr) : +Packet::Packet(std::unique_ptr data, qint64 size, const HifiSockAddr& senderSockAddr) : _packetSize(size), _packet(std::move(data)), _senderSockAddr(senderSockAddr) @@ -110,7 +110,7 @@ Packet& Packet::operator=(const Packet& other) { _type = other._type; _packetSize = other._packetSize; - _packet = std::unique_ptr(new char[_packetSize]); + _packet = std::unique_ptr(new char[_packetSize]); memcpy(_packet.get(), other._packet.get(), _packetSize); _payloadStart = _packet.get() + (other._payloadStart - other._packet.get()); diff --git a/libraries/networking/src/udt/Packet.h b/libraries/networking/src/udt/Packet.h index b4c53b8165..91b5974e09 100644 --- a/libraries/networking/src/udt/Packet.h +++ b/libraries/networking/src/udt/Packet.h @@ -27,7 +27,7 @@ public: static const qint64 PACKET_WRITE_ERROR; static std::unique_ptr create(PacketType::Value type, qint64 size = -1); - static std::unique_ptr fromReceivedPacket(std::unique_ptr data, qint64 size, const HifiSockAddr& senderSockAddr); + static std::unique_ptr fromReceivedPacket(std::unique_ptr data, qint64 size, const HifiSockAddr& senderSockAddr); // Provided for convenience, try to limit use static std::unique_ptr createCopy(const Packet& other); @@ -88,7 +88,7 @@ public: protected: Packet(PacketType::Value type, qint64 size); - Packet(std::unique_ptr data, qint64 size, const HifiSockAddr& senderSockAddr); + Packet(std::unique_ptr data, qint64 size, const HifiSockAddr& senderSockAddr); Packet(const Packet& other); Packet& operator=(const Packet& other); Packet(Packet&& other); @@ -109,7 +109,7 @@ protected: PacketVersion _version; // Packet version qint64 _packetSize = 0; // Total size of the allocated memory - std::unique_ptr _packet; // Allocated memory + std::unique_ptr _packet; // Allocated memory char* _payloadStart = nullptr; // Start of the payload qint64 _payloadCapacity = 0; // Total capacity of the payload From c88e0360b3714890b9f6ba1774514f246b27a672 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Fri, 31 Jul 2015 14:46:43 -0700 Subject: [PATCH 46/65] Change the inspect.js pan direction and rate This makes camera zoom, orbit, and panning more similar to Second Life. --- examples/inspect.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/inspect.js b/examples/inspect.js index 0df90fbac3..555b4105b7 100644 --- a/examples/inspect.js +++ b/examples/inspect.js @@ -23,7 +23,7 @@ var RAD_TO_DEG = 180.0 / PI; var AZIMUTH_RATE = 90.0; var ALTITUDE_RATE = 200.0; var RADIUS_RATE = 1.0 / 100.0; -var PAN_RATE = 50.0; +var PAN_RATE = 250.0; var Y_AXIS = { x: 0, @@ -139,7 +139,7 @@ function handlePanMode(dx, dy) { var right = Quat.getRight(Camera.getOrientation()); var distance = Vec3.length(vector); - var dv = Vec3.sum(Vec3.multiply(up, -distance * dy / PAN_RATE), Vec3.multiply(right, distance * dx / PAN_RATE)); + var dv = Vec3.sum(Vec3.multiply(up, distance * dy / PAN_RATE), Vec3.multiply(right, -distance * dx / PAN_RATE)); center = Vec3.sum(center, dv); position = Vec3.sum(position, dv); From 12ad60a6b5afa77c73b4d899922afa5e637e04f8 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 31 Jul 2015 14:48:54 -0700 Subject: [PATCH 47/65] add a missed char[] in OctreePacketProcessor --- interface/src/octree/OctreePacketProcessor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/octree/OctreePacketProcessor.cpp b/interface/src/octree/OctreePacketProcessor.cpp index 7bb94323b7..1abbb21089 100644 --- a/interface/src/octree/OctreePacketProcessor.cpp +++ b/interface/src/octree/OctreePacketProcessor.cpp @@ -57,7 +57,7 @@ void OctreePacketProcessor::processPacket(QSharedPointer packet, Share if (piggybackBytes) { // construct a new packet from the piggybacked one - std::unique_ptr buffer = std::unique_ptr(new char[piggybackBytes]); + auto buffer = std::unique_ptr(new char[piggybackBytes]); memcpy(buffer.get(), packet->getPayload() + statsMessageLength, piggybackBytes); auto newPacket = NLPacket::fromReceivedPacket(std::move(buffer), piggybackBytes, packet->getSenderSockAddr()); From 0dad1adf7d928fc6cc8ef5ec616b92b3a3cf0b2e Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Fri, 31 Jul 2015 18:05:14 -0700 Subject: [PATCH 48/65] Speculative guard against crashing. --- libraries/render-utils/src/Model.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 1dc08b7a06..72a601cd20 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -243,6 +243,9 @@ QVector Model::createJointStates(const FBXGeometry& geometry) { }; void Model::initJointTransforms() { + if (!_geometry) { + return; + } const FBXGeometry& geometry = _geometry->getFBXGeometry(); glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset; _rig->initJointTransforms(parentTransform); @@ -424,6 +427,9 @@ bool Model::updateGeometry() { deleteGeometry(); _dilatedTextures.clear(); + if (!geometry) { + std::cout << "WARNING: no geometry in Model::updateGeometry\n"; + } setGeometry(geometry); _meshGroupsKnown = false; From 1dd6c1117d434ef1555ca7f5b698b6b557fea153 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Sat, 1 Aug 2015 11:29:28 -0700 Subject: [PATCH 49/65] change GeometryCache::renderQuad() to use TRIANGLES as lower level render primitive --- libraries/gpu/src/gpu/Format.h | 2 + libraries/model/src/model/Geometry.h | 2 +- libraries/render-utils/src/GeometryCache.cpp | 97 ++++++++++++++++---- libraries/render-utils/src/GeometryCache.h | 2 + 4 files changed, 86 insertions(+), 17 deletions(-) diff --git a/libraries/gpu/src/gpu/Format.h b/libraries/gpu/src/gpu/Format.h index 981a560965..01d3f37ef8 100644 --- a/libraries/gpu/src/gpu/Format.h +++ b/libraries/gpu/src/gpu/Format.h @@ -213,6 +213,8 @@ enum Primitive { TRIANGLES, TRIANGLE_STRIP, TRIANGLE_FAN, + + // FIXME - remove these QUADS, QUAD_STRIP, diff --git a/libraries/model/src/model/Geometry.h b/libraries/model/src/model/Geometry.h index 16ebb60b72..5ef414a2d1 100755 --- a/libraries/model/src/model/Geometry.h +++ b/libraries/model/src/model/Geometry.h @@ -71,7 +71,7 @@ public: LINE_STRIP, TRIANGLES, TRIANGLE_STRIP, - QUADS, + QUADS, // NOTE: These must be translated to triangles before rendering QUAD_STRIP, NUM_TOPOLOGIES, diff --git a/libraries/render-utils/src/GeometryCache.cpp b/libraries/render-utils/src/GeometryCache.cpp index def5f38db4..12a6d46be6 100644 --- a/libraries/render-utils/src/GeometryCache.cpp +++ b/libraries/render-utils/src/GeometryCache.cpp @@ -1012,21 +1012,27 @@ void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec2& minCorner, co } const int FLOATS_PER_VERTEX = 2; // vertices - const int vertices = 4; + const int NUMBER_OF_INDICES = 6; // 1 quad = 2 triangles + const int VERTICES = 4; // 1 quad = 4 vertices if (!details.isCreated) { details.isCreated = true; - details.vertices = vertices; + details.vertices = VERTICES; + details.indices = NUMBER_OF_INDICES; details.vertexSize = FLOATS_PER_VERTEX; auto verticesBuffer = std::make_shared(); auto colorBuffer = std::make_shared(); + auto indicesBuffer = std::make_shared(); + auto streamFormat = std::make_shared(); auto stream = std::make_shared(); details.verticesBuffer = verticesBuffer; details.colorBuffer = colorBuffer; + details.indicesBuffer = indicesBuffer; + details.streamFormat = streamFormat; details.stream = stream; @@ -1037,7 +1043,7 @@ void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec2& minCorner, co details.stream->addBuffer(details.colorBuffer, 0, details.streamFormat->getChannels().at(1)._stride); - float vertexBuffer[vertices * FLOATS_PER_VERTEX] = { + float vertexBuffer[VERTICES * FLOATS_PER_VERTEX] = { minCorner.x, minCorner.y, maxCorner.x, minCorner.y, maxCorner.x, maxCorner.y, @@ -1050,14 +1056,24 @@ void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec2& minCorner, co ((int(color.w * 255.0f) & 0xFF) << 24); int colors[NUM_COLOR_SCALARS_PER_QUAD] = { compactColor, compactColor, compactColor, compactColor }; + // Sam's recommended triangle slices + // Triangle tri1 = { v0, v1, v3 }; + // Triangle tri2 = { v1, v2, v3 }; + // NOTE: Random guy on the internet's recommended triangle slices + // Triangle tri1 = { v0, v1, v2 }; + // Triangle tri2 = { v2, v3, v0 }; + + quint16 indices[NUMBER_OF_INDICES] = { 0, 1, 3, 1, 2, 3 }; details.verticesBuffer->append(sizeof(vertexBuffer), (gpu::Byte*) vertexBuffer); details.colorBuffer->append(sizeof(colors), (gpu::Byte*) colors); + details.indicesBuffer->append(sizeof(indices), (gpu::Byte*) indices); } batch.setInputFormat(details.streamFormat); batch.setInputStream(0, *details.stream); - batch.draw(gpu::QUADS, 4, 0); + batch.setIndexBuffer(gpu::UINT16, details.indicesBuffer, 0); + batch.drawIndexed(gpu::TRIANGLES, NUMBER_OF_INDICES, 0); } void GeometryCache::renderUnitCube(gpu::Batch& batch) { @@ -1102,23 +1118,29 @@ void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec2& minCorner, co } const int FLOATS_PER_VERTEX = 2 * 2; // text coords & vertices - const int vertices = 4; + const int VERTICES = 4; // 1 quad = 4 vertices const int NUM_POS_COORDS = 2; const int VERTEX_TEXCOORD_OFFSET = NUM_POS_COORDS * sizeof(float); + const int NUMBER_OF_INDICES = 6; // 1 quad = 2 triangles if (!details.isCreated) { details.isCreated = true; - details.vertices = vertices; + details.vertices = VERTICES; + details.indices = NUMBER_OF_INDICES; details.vertexSize = FLOATS_PER_VERTEX; auto verticesBuffer = std::make_shared(); auto colorBuffer = std::make_shared(); + auto indicesBuffer = std::make_shared(); + auto streamFormat = std::make_shared(); auto stream = std::make_shared(); details.verticesBuffer = verticesBuffer; details.colorBuffer = colorBuffer; + details.indicesBuffer = indicesBuffer; + details.streamFormat = streamFormat; details.stream = stream; @@ -1130,7 +1152,7 @@ void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec2& minCorner, co details.stream->addBuffer(details.colorBuffer, 0, details.streamFormat->getChannels().at(1)._stride); - float vertexBuffer[vertices * FLOATS_PER_VERTEX] = { + float vertexBuffer[VERTICES * FLOATS_PER_VERTEX] = { minCorner.x, minCorner.y, texCoordMinCorner.x, texCoordMinCorner.y, maxCorner.x, minCorner.y, texCoordMaxCorner.x, texCoordMinCorner.y, maxCorner.x, maxCorner.y, texCoordMaxCorner.x, texCoordMaxCorner.y, @@ -1144,14 +1166,24 @@ void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec2& minCorner, co ((int(color.w * 255.0f) & 0xFF) << 24); int colors[NUM_COLOR_SCALARS_PER_QUAD] = { compactColor, compactColor, compactColor, compactColor }; + // Sam's recommended triangle slices + // Triangle tri1 = { v0, v1, v3 }; + // Triangle tri2 = { v1, v2, v3 }; + // NOTE: Random guy on the internet's recommended triangle slices + // Triangle tri1 = { v0, v1, v2 }; + // Triangle tri2 = { v2, v3, v0 }; + + quint16 indices[NUMBER_OF_INDICES] = { 0, 1, 3, 1, 2, 3 }; details.verticesBuffer->append(sizeof(vertexBuffer), (gpu::Byte*) vertexBuffer); details.colorBuffer->append(sizeof(colors), (gpu::Byte*) colors); + details.indicesBuffer->append(sizeof(indices), (gpu::Byte*) indices); } batch.setInputFormat(details.streamFormat); batch.setInputStream(0, *details.stream); - batch.draw(gpu::QUADS, 4, 0); + batch.setIndexBuffer(gpu::UINT16, details.indicesBuffer, 0); + batch.drawIndexed(gpu::TRIANGLES, NUMBER_OF_INDICES, 0); } void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec3& minCorner, const glm::vec3& maxCorner, const glm::vec4& color, int id) { @@ -1177,21 +1209,27 @@ void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec3& minCorner, co } const int FLOATS_PER_VERTEX = 3; // vertices - const int vertices = 4; + const int NUMBER_OF_INDICES = 6; // 1 quad = 2 triangles + const int VERTICES = 4; // 1 quad = 4 vertices if (!details.isCreated) { details.isCreated = true; - details.vertices = vertices; + details.vertices = VERTICES; + details.indices = NUMBER_OF_INDICES; details.vertexSize = FLOATS_PER_VERTEX; auto verticesBuffer = std::make_shared(); auto colorBuffer = std::make_shared(); + auto indicesBuffer = std::make_shared(); + auto streamFormat = std::make_shared(); auto stream = std::make_shared(); details.verticesBuffer = verticesBuffer; details.colorBuffer = colorBuffer; + details.indicesBuffer = indicesBuffer; + details.streamFormat = streamFormat; details.stream = stream; @@ -1202,7 +1240,7 @@ void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec3& minCorner, co details.stream->addBuffer(details.colorBuffer, 0, details.streamFormat->getChannels().at(1)._stride); - float vertexBuffer[vertices * FLOATS_PER_VERTEX] = { + float vertexBuffer[VERTICES * FLOATS_PER_VERTEX] = { minCorner.x, minCorner.y, minCorner.z, maxCorner.x, minCorner.y, minCorner.z, maxCorner.x, maxCorner.y, maxCorner.z, @@ -1215,14 +1253,24 @@ void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec3& minCorner, co ((int(color.w * 255.0f) & 0xFF) << 24); int colors[NUM_COLOR_SCALARS_PER_QUAD] = { compactColor, compactColor, compactColor, compactColor }; + // Sam's recommended triangle slices + // Triangle tri1 = { v0, v1, v3 }; + // Triangle tri2 = { v1, v2, v3 }; + // NOTE: Random guy on the internet's recommended triangle slices + // Triangle tri1 = { v0, v1, v2 }; + // Triangle tri2 = { v2, v3, v0 }; + + quint16 indices[NUMBER_OF_INDICES] = { 0, 1, 3, 1, 2, 3 }; details.verticesBuffer->append(sizeof(vertexBuffer), (gpu::Byte*) vertexBuffer); details.colorBuffer->append(sizeof(colors), (gpu::Byte*) colors); + details.indicesBuffer->append(sizeof(indices), (gpu::Byte*) indices); } batch.setInputFormat(details.streamFormat); batch.setInputStream(0, *details.stream); - batch.draw(gpu::QUADS, 4, 0); + batch.setIndexBuffer(gpu::UINT16, details.indicesBuffer, 0); + batch.drawIndexed(gpu::TRIANGLES, NUMBER_OF_INDICES, 0); } void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec3& topLeft, const glm::vec3& bottomLeft, @@ -1267,23 +1315,29 @@ void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec3& topLeft, cons } const int FLOATS_PER_VERTEX = 3 + 2; // 3d vertices + text coords - const int vertices = 4; + const int NUMBER_OF_INDICES = 6; // 1 quad = 2 triangles + const int VERTICES = 4; // 1 quad = 4 vertices const int NUM_POS_COORDS = 3; const int VERTEX_TEXCOORD_OFFSET = NUM_POS_COORDS * sizeof(float); if (!details.isCreated) { details.isCreated = true; - details.vertices = vertices; + details.vertices = VERTICES; + details.indices = NUMBER_OF_INDICES; details.vertexSize = FLOATS_PER_VERTEX; // NOTE: this isn't used for BatchItemDetails maybe we can get rid of it auto verticesBuffer = std::make_shared(); auto colorBuffer = std::make_shared(); + auto indicesBuffer = std::make_shared(); + auto streamFormat = std::make_shared(); auto stream = std::make_shared(); details.verticesBuffer = verticesBuffer; details.colorBuffer = colorBuffer; + details.indicesBuffer = indicesBuffer; + details.streamFormat = streamFormat; details.stream = stream; @@ -1295,7 +1349,7 @@ void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec3& topLeft, cons details.stream->addBuffer(details.colorBuffer, 0, details.streamFormat->getChannels().at(1)._stride); - float vertexBuffer[vertices * FLOATS_PER_VERTEX] = { + float vertexBuffer[VERTICES * FLOATS_PER_VERTEX] = { topLeft.x, topLeft.y, topLeft.z, texCoordTopLeft.x, texCoordTopLeft.y, bottomLeft.x, bottomLeft.y, bottomLeft.z, texCoordBottomLeft.x, texCoordBottomLeft.y, bottomRight.x, bottomRight.y, bottomRight.z, texCoordBottomRight.x, texCoordBottomRight.y, @@ -1309,13 +1363,24 @@ void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec3& topLeft, cons ((int(color.w * 255.0f) & 0xFF) << 24); int colors[NUM_COLOR_SCALARS_PER_QUAD] = { compactColor, compactColor, compactColor, compactColor }; + // Sam's recommended triangle slices + // Triangle tri1 = { v0, v1, v3 }; + // Triangle tri2 = { v1, v2, v3 }; + // NOTE: Random guy on the internet's recommended triangle slices + // Triangle tri1 = { v0, v1, v2 }; + // Triangle tri2 = { v2, v3, v0 }; + + quint16 indices[NUMBER_OF_INDICES] = { 0, 1, 3, 1, 2, 3 }; + details.verticesBuffer->append(sizeof(vertexBuffer), (gpu::Byte*) vertexBuffer); details.colorBuffer->append(sizeof(colors), (gpu::Byte*) colors); + details.indicesBuffer->append(sizeof(indices), (gpu::Byte*) indices); } batch.setInputFormat(details.streamFormat); batch.setInputStream(0, *details.stream); - batch.draw(gpu::QUADS, 4, 0); + batch.setIndexBuffer(gpu::UINT16, details.indicesBuffer, 0); + batch.drawIndexed(gpu::TRIANGLES, NUMBER_OF_INDICES, 0); } void GeometryCache::renderDashedLine(gpu::Batch& batch, const glm::vec3& start, const glm::vec3& end, const glm::vec4& color, int id) { diff --git a/libraries/render-utils/src/GeometryCache.h b/libraries/render-utils/src/GeometryCache.h index 812d12b846..19e84e9346 100644 --- a/libraries/render-utils/src/GeometryCache.h +++ b/libraries/render-utils/src/GeometryCache.h @@ -235,10 +235,12 @@ private: static int population; gpu::BufferPointer verticesBuffer; gpu::BufferPointer colorBuffer; + gpu::BufferPointer indicesBuffer; gpu::Stream::FormatPointer streamFormat; gpu::BufferStreamPointer stream; int vertices; + int indices; int vertexSize; bool isCreated; From 452225ddaf87ba387f6c06ffc96e863f2dfc95be Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Sat, 1 Aug 2015 11:51:07 -0700 Subject: [PATCH 50/65] removed QUADS from Font::drawString() --- libraries/render-utils/src/text/Font.cpp | 30 ++++++++++++++++++++++-- libraries/render-utils/src/text/Font.h | 2 ++ 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/libraries/render-utils/src/text/Font.cpp b/libraries/render-utils/src/text/Font.cpp index b341f444e6..3cedcdcbc1 100644 --- a/libraries/render-utils/src/text/Font.cpp +++ b/libraries/render-utils/src/text/Font.cpp @@ -18,8 +18,12 @@ struct TextureVertex { TextureVertex(const glm::vec2& pos, const glm::vec2& tex) : pos(pos), tex(tex) {} }; +static const int NUMBER_OF_INDICES_PER_QUAD = 6; // 1 quad = 2 triangles +static const int VERTICES_PER_QUAD = 4; // 1 quad = 4 vertices + struct QuadBuilder { - TextureVertex vertices[4]; + TextureVertex vertices[VERTICES_PER_QUAD]; + QuadBuilder(const glm::vec2& min, const glm::vec2& size, const glm::vec2& texMin, const glm::vec2& texSize) { // min = bottomLeft @@ -249,6 +253,9 @@ void Font::setupGPU() { void Font::rebuildVertices(float x, float y, const QString& str, const glm::vec2& bounds) { _verticesBuffer = std::make_shared(); _numVertices = 0; + _indicesBuffer = std::make_shared(); + _numIndices = 0; + _lastStringRendered = str; _lastBounds = bounds; @@ -284,10 +291,28 @@ void Font::rebuildVertices(float x, float y, const QString& str, const glm::vec2 if (!isNewLine) { for (auto c : token) { auto glyph = _glyphs[c]; + quint16 verticesOffset = _numVertices; QuadBuilder qd(glyph, advance - glm::vec2(0.0f, _ascent)); _verticesBuffer->append(sizeof(QuadBuilder), (const gpu::Byte*)&qd); _numVertices += 4; + + // Sam's recommended triangle slices + // Triangle tri1 = { v0, v1, v3 }; + // Triangle tri2 = { v1, v2, v3 }; + // NOTE: Random guy on the internet's recommended triangle slices + // Triangle tri1 = { v0, v1, v2 }; + // Triangle tri2 = { v2, v3, v0 }; + quint16 indices[NUMBER_OF_INDICES_PER_QUAD]; + indices[0] = verticesOffset + 0; + indices[1] = verticesOffset + 1; + indices[2] = verticesOffset + 3; + indices[3] = verticesOffset + 1; + indices[4] = verticesOffset + 2; + indices[5] = verticesOffset + 3; + _indicesBuffer->append(sizeof(indices), (const gpu::Byte*)indices); + _numIndices += NUMBER_OF_INDICES_PER_QUAD; + // Advance by glyph size advance.x += glyph.d; @@ -318,5 +343,6 @@ void Font::drawString(gpu::Batch& batch, float x, float y, const QString& str, c batch.setInputFormat(_format); batch.setInputBuffer(0, _verticesBuffer, 0, _format->getChannels().at(0)._stride); - batch.draw(gpu::QUADS, _numVertices, 0); + batch.setIndexBuffer(gpu::UINT16, _indicesBuffer, 0); + batch.drawIndexed(gpu::TRIANGLES, _numIndices, 0); } diff --git a/libraries/render-utils/src/text/Font.h b/libraries/render-utils/src/text/Font.h index 55801419f9..e10360d45f 100644 --- a/libraries/render-utils/src/text/Font.h +++ b/libraries/render-utils/src/text/Font.h @@ -64,8 +64,10 @@ private: gpu::TexturePointer _texture; gpu::Stream::FormatPointer _format; gpu::BufferPointer _verticesBuffer; + gpu::BufferPointer _indicesBuffer; gpu::BufferStreamPointer _stream; unsigned int _numVertices = 0; + unsigned int _numIndices = 0; int _fontLoc = -1; int _outlineLoc = -1; From 05a4a6aa9bb99c6b55d38d6127d3f18db711d08b Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Sat, 1 Aug 2015 13:07:56 -0700 Subject: [PATCH 51/65] implement on-the-fly conversion of FBXMeshParts that are quads, into their triangle equivalents --- libraries/fbx/src/FBXReader.cpp | 51 +++++++++++++++++++++++++++- libraries/fbx/src/FBXReader.h | 11 ++++-- libraries/render-utils/src/Model.cpp | 5 ++- 3 files changed, 62 insertions(+), 5 deletions(-) diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index e18b334264..51f91de71a 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -830,6 +830,56 @@ public: std::vector attributes; }; +gpu::BufferPointer FBXMeshPart::getTrianglesForQuads() const { + // if we've been asked for our triangulation of the original quads, but we don't yet have them + // then create them now. + if (!trianglesForQuadsAvailable) { + trianglesForQuadsAvailable = true; + + quadsAsTrianglesIndicesBuffer = std::make_shared(); + + // QVector quadIndices; // original indices from the FBX mesh + QVector quadsAsTrianglesIndices; // triangle versions of quads converted when first needed + const int INDICES_PER_ORIGINAL_QUAD = 4; + const int INDICES_PER_TRIANGULATED_QUAD = 6; + int numberOfQuads = quadIndices.size() / INDICES_PER_ORIGINAL_QUAD; + + quadsAsTrianglesIndices.resize(numberOfQuads * INDICES_PER_TRIANGULATED_QUAD); + + int originalIndex = 0; + int triangulatedIndex = 0; + for (int fromQuad = 0; fromQuad < numberOfQuads; fromQuad++) { + int i0 = quadIndices[originalIndex + 0]; + int i1 = quadIndices[originalIndex + 1]; + int i2 = quadIndices[originalIndex + 2]; + int i3 = quadIndices[originalIndex + 3]; + + // Sam's recommended triangle slices + // Triangle tri1 = { v0, v1, v3 }; + // Triangle tri2 = { v1, v2, v3 }; + // NOTE: Random guy on the internet's recommended triangle slices + // Triangle tri1 = { v0, v1, v2 }; + // Triangle tri2 = { v2, v3, v0 }; + + quadsAsTrianglesIndices[triangulatedIndex + 0] = i0; + quadsAsTrianglesIndices[triangulatedIndex + 1] = i1; + quadsAsTrianglesIndices[triangulatedIndex + 2] = i3; + + quadsAsTrianglesIndices[triangulatedIndex + 3] = i1; + quadsAsTrianglesIndices[triangulatedIndex + 4] = i2; + quadsAsTrianglesIndices[triangulatedIndex + 5] = i3; + + originalIndex += INDICES_PER_ORIGINAL_QUAD; + triangulatedIndex += INDICES_PER_TRIANGULATED_QUAD; + } + + trianglesForQuadsIndicesCount = INDICES_PER_TRIANGULATED_QUAD * numberOfQuads; + quadsAsTrianglesIndicesBuffer->append(quadsAsTrianglesIndices.size() * sizeof(quint32), (gpu::Byte*)quadsAsTrianglesIndices.data()); + + } + return quadsAsTrianglesIndicesBuffer; +} + void appendIndex(MeshData& data, QVector& indices, int index) { if (index >= data.polygonIndices.size()) { return; @@ -1089,7 +1139,6 @@ ExtractedMesh extractMesh(const FBXNode& object, unsigned int& meshIndex) { appendIndex(data, part.quadIndices, beginIndex++); appendIndex(data, part.quadIndices, beginIndex++); appendIndex(data, part.quadIndices, beginIndex++); - } else { for (int nextIndex = beginIndex + 1;; ) { appendIndex(data, part.triangleIndices, beginIndex); diff --git a/libraries/fbx/src/FBXReader.h b/libraries/fbx/src/FBXReader.h index 198e2d3534..a20dd4a07f 100644 --- a/libraries/fbx/src/FBXReader.h +++ b/libraries/fbx/src/FBXReader.h @@ -109,9 +109,10 @@ public: class FBXMeshPart { public: - QVector quadIndices; - QVector triangleIndices; - + QVector quadIndices; // original indices from the FBX mesh + QVector triangleIndices; // original indices from the FBX mesh + mutable gpu::BufferPointer quadsAsTrianglesIndicesBuffer; + glm::vec3 diffuseColor; glm::vec3 specularColor; glm::vec3 emissiveColor; @@ -126,6 +127,10 @@ public: QString materialID; model::MaterialPointer _material; + mutable bool trianglesForQuadsAvailable = false; + mutable int trianglesForQuadsIndicesCount = 0; + + gpu::BufferPointer getTrianglesForQuads() const; }; /// A single mesh (with optional blendshapes) extracted from an FBX document. diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 1e4f3f7190..b77bafc354 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -2096,8 +2096,11 @@ void Model::renderPart(RenderArgs* args, int meshIndex, int partIndex, bool tran } if (part.quadIndices.size() > 0) { - batch.drawIndexed(gpu::QUADS, part.quadIndices.size(), offset); + batch.setIndexBuffer(gpu::UINT32, part.getTrianglesForQuads(), 0); + batch.drawIndexed(gpu::TRIANGLES, part.trianglesForQuadsIndicesCount, 0); + offset += part.quadIndices.size() * sizeof(int); + batch.setIndexBuffer(gpu::UINT32, (networkMesh._indexBuffer), 0); // restore this in case there are triangles too } if (part.triangleIndices.size() > 0) { From b138c16c7f689e249cdca0436d1bbcd4b52fbf14 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Sat, 1 Aug 2015 16:50:36 -0700 Subject: [PATCH 52/65] use Austins QUAD to TRIANGLE_STRIP approach --- libraries/render-utils/src/GeometryCache.cpp | 89 +++----------------- libraries/render-utils/src/GeometryCache.h | 2 - 2 files changed, 14 insertions(+), 77 deletions(-) diff --git a/libraries/render-utils/src/GeometryCache.cpp b/libraries/render-utils/src/GeometryCache.cpp index 12a6d46be6..3d43cc6934 100644 --- a/libraries/render-utils/src/GeometryCache.cpp +++ b/libraries/render-utils/src/GeometryCache.cpp @@ -1012,27 +1012,21 @@ void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec2& minCorner, co } const int FLOATS_PER_VERTEX = 2; // vertices - const int NUMBER_OF_INDICES = 6; // 1 quad = 2 triangles const int VERTICES = 4; // 1 quad = 4 vertices if (!details.isCreated) { details.isCreated = true; details.vertices = VERTICES; - details.indices = NUMBER_OF_INDICES; details.vertexSize = FLOATS_PER_VERTEX; auto verticesBuffer = std::make_shared(); auto colorBuffer = std::make_shared(); - auto indicesBuffer = std::make_shared(); - auto streamFormat = std::make_shared(); auto stream = std::make_shared(); details.verticesBuffer = verticesBuffer; details.colorBuffer = colorBuffer; - details.indicesBuffer = indicesBuffer; - details.streamFormat = streamFormat; details.stream = stream; @@ -1046,8 +1040,9 @@ void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec2& minCorner, co float vertexBuffer[VERTICES * FLOATS_PER_VERTEX] = { minCorner.x, minCorner.y, maxCorner.x, minCorner.y, - maxCorner.x, maxCorner.y, - minCorner.x, maxCorner.y }; + minCorner.x, maxCorner.y, + maxCorner.x, maxCorner.y + }; const int NUM_COLOR_SCALARS_PER_QUAD = 4; int compactColor = ((int(color.x * 255.0f) & 0xFF)) | @@ -1056,24 +1051,13 @@ void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec2& minCorner, co ((int(color.w * 255.0f) & 0xFF) << 24); int colors[NUM_COLOR_SCALARS_PER_QUAD] = { compactColor, compactColor, compactColor, compactColor }; - // Sam's recommended triangle slices - // Triangle tri1 = { v0, v1, v3 }; - // Triangle tri2 = { v1, v2, v3 }; - // NOTE: Random guy on the internet's recommended triangle slices - // Triangle tri1 = { v0, v1, v2 }; - // Triangle tri2 = { v2, v3, v0 }; - - quint16 indices[NUMBER_OF_INDICES] = { 0, 1, 3, 1, 2, 3 }; - details.verticesBuffer->append(sizeof(vertexBuffer), (gpu::Byte*) vertexBuffer); details.colorBuffer->append(sizeof(colors), (gpu::Byte*) colors); - details.indicesBuffer->append(sizeof(indices), (gpu::Byte*) indices); } batch.setInputFormat(details.streamFormat); batch.setInputStream(0, *details.stream); - batch.setIndexBuffer(gpu::UINT16, details.indicesBuffer, 0); - batch.drawIndexed(gpu::TRIANGLES, NUMBER_OF_INDICES, 0); + batch.draw(gpu::TRIANGLE_STRIP, 4, 0); } void GeometryCache::renderUnitCube(gpu::Batch& batch) { @@ -1121,25 +1105,21 @@ void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec2& minCorner, co const int VERTICES = 4; // 1 quad = 4 vertices const int NUM_POS_COORDS = 2; const int VERTEX_TEXCOORD_OFFSET = NUM_POS_COORDS * sizeof(float); - const int NUMBER_OF_INDICES = 6; // 1 quad = 2 triangles if (!details.isCreated) { details.isCreated = true; details.vertices = VERTICES; - details.indices = NUMBER_OF_INDICES; details.vertexSize = FLOATS_PER_VERTEX; auto verticesBuffer = std::make_shared(); auto colorBuffer = std::make_shared(); - auto indicesBuffer = std::make_shared(); auto streamFormat = std::make_shared(); auto stream = std::make_shared(); details.verticesBuffer = verticesBuffer; details.colorBuffer = colorBuffer; - details.indicesBuffer = indicesBuffer; details.streamFormat = streamFormat; details.stream = stream; @@ -1155,8 +1135,9 @@ void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec2& minCorner, co float vertexBuffer[VERTICES * FLOATS_PER_VERTEX] = { minCorner.x, minCorner.y, texCoordMinCorner.x, texCoordMinCorner.y, maxCorner.x, minCorner.y, texCoordMaxCorner.x, texCoordMinCorner.y, - maxCorner.x, maxCorner.y, texCoordMaxCorner.x, texCoordMaxCorner.y, - minCorner.x, maxCorner.y, texCoordMinCorner.x, texCoordMaxCorner.y }; + minCorner.x, maxCorner.y, texCoordMinCorner.x, texCoordMaxCorner.y, + maxCorner.x, maxCorner.y, texCoordMaxCorner.x, texCoordMaxCorner.y + }; const int NUM_COLOR_SCALARS_PER_QUAD = 4; @@ -1166,24 +1147,13 @@ void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec2& minCorner, co ((int(color.w * 255.0f) & 0xFF) << 24); int colors[NUM_COLOR_SCALARS_PER_QUAD] = { compactColor, compactColor, compactColor, compactColor }; - // Sam's recommended triangle slices - // Triangle tri1 = { v0, v1, v3 }; - // Triangle tri2 = { v1, v2, v3 }; - // NOTE: Random guy on the internet's recommended triangle slices - // Triangle tri1 = { v0, v1, v2 }; - // Triangle tri2 = { v2, v3, v0 }; - - quint16 indices[NUMBER_OF_INDICES] = { 0, 1, 3, 1, 2, 3 }; - details.verticesBuffer->append(sizeof(vertexBuffer), (gpu::Byte*) vertexBuffer); details.colorBuffer->append(sizeof(colors), (gpu::Byte*) colors); - details.indicesBuffer->append(sizeof(indices), (gpu::Byte*) indices); } batch.setInputFormat(details.streamFormat); batch.setInputStream(0, *details.stream); - batch.setIndexBuffer(gpu::UINT16, details.indicesBuffer, 0); - batch.drawIndexed(gpu::TRIANGLES, NUMBER_OF_INDICES, 0); + batch.draw(gpu::TRIANGLE_STRIP, 4, 0); } void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec3& minCorner, const glm::vec3& maxCorner, const glm::vec4& color, int id) { @@ -1209,26 +1179,22 @@ void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec3& minCorner, co } const int FLOATS_PER_VERTEX = 3; // vertices - const int NUMBER_OF_INDICES = 6; // 1 quad = 2 triangles const int VERTICES = 4; // 1 quad = 4 vertices if (!details.isCreated) { details.isCreated = true; details.vertices = VERTICES; - details.indices = NUMBER_OF_INDICES; details.vertexSize = FLOATS_PER_VERTEX; auto verticesBuffer = std::make_shared(); auto colorBuffer = std::make_shared(); - auto indicesBuffer = std::make_shared(); auto streamFormat = std::make_shared(); auto stream = std::make_shared(); details.verticesBuffer = verticesBuffer; details.colorBuffer = colorBuffer; - details.indicesBuffer = indicesBuffer; details.streamFormat = streamFormat; details.stream = stream; @@ -1243,8 +1209,9 @@ void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec3& minCorner, co float vertexBuffer[VERTICES * FLOATS_PER_VERTEX] = { minCorner.x, minCorner.y, minCorner.z, maxCorner.x, minCorner.y, minCorner.z, - maxCorner.x, maxCorner.y, maxCorner.z, - minCorner.x, maxCorner.y, maxCorner.z }; + minCorner.x, maxCorner.y, maxCorner.z, + maxCorner.x, maxCorner.y, maxCorner.z + }; const int NUM_COLOR_SCALARS_PER_QUAD = 4; int compactColor = ((int(color.x * 255.0f) & 0xFF)) | @@ -1253,24 +1220,13 @@ void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec3& minCorner, co ((int(color.w * 255.0f) & 0xFF) << 24); int colors[NUM_COLOR_SCALARS_PER_QUAD] = { compactColor, compactColor, compactColor, compactColor }; - // Sam's recommended triangle slices - // Triangle tri1 = { v0, v1, v3 }; - // Triangle tri2 = { v1, v2, v3 }; - // NOTE: Random guy on the internet's recommended triangle slices - // Triangle tri1 = { v0, v1, v2 }; - // Triangle tri2 = { v2, v3, v0 }; - - quint16 indices[NUMBER_OF_INDICES] = { 0, 1, 3, 1, 2, 3 }; - details.verticesBuffer->append(sizeof(vertexBuffer), (gpu::Byte*) vertexBuffer); details.colorBuffer->append(sizeof(colors), (gpu::Byte*) colors); - details.indicesBuffer->append(sizeof(indices), (gpu::Byte*) indices); } batch.setInputFormat(details.streamFormat); batch.setInputStream(0, *details.stream); - batch.setIndexBuffer(gpu::UINT16, details.indicesBuffer, 0); - batch.drawIndexed(gpu::TRIANGLES, NUMBER_OF_INDICES, 0); + batch.draw(gpu::TRIANGLE_STRIP, 4, 0); } void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec3& topLeft, const glm::vec3& bottomLeft, @@ -1315,7 +1271,6 @@ void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec3& topLeft, cons } const int FLOATS_PER_VERTEX = 3 + 2; // 3d vertices + text coords - const int NUMBER_OF_INDICES = 6; // 1 quad = 2 triangles const int VERTICES = 4; // 1 quad = 4 vertices const int NUM_POS_COORDS = 3; const int VERTEX_TEXCOORD_OFFSET = NUM_POS_COORDS * sizeof(float); @@ -1324,20 +1279,15 @@ void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec3& topLeft, cons details.isCreated = true; details.vertices = VERTICES; - details.indices = NUMBER_OF_INDICES; details.vertexSize = FLOATS_PER_VERTEX; // NOTE: this isn't used for BatchItemDetails maybe we can get rid of it auto verticesBuffer = std::make_shared(); auto colorBuffer = std::make_shared(); - auto indicesBuffer = std::make_shared(); - auto streamFormat = std::make_shared(); auto stream = std::make_shared(); details.verticesBuffer = verticesBuffer; details.colorBuffer = colorBuffer; - details.indicesBuffer = indicesBuffer; - details.streamFormat = streamFormat; details.stream = stream; @@ -1350,9 +1300,9 @@ void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec3& topLeft, cons float vertexBuffer[VERTICES * FLOATS_PER_VERTEX] = { - topLeft.x, topLeft.y, topLeft.z, texCoordTopLeft.x, texCoordTopLeft.y, bottomLeft.x, bottomLeft.y, bottomLeft.z, texCoordBottomLeft.x, texCoordBottomLeft.y, bottomRight.x, bottomRight.y, bottomRight.z, texCoordBottomRight.x, texCoordBottomRight.y, + topLeft.x, topLeft.y, topLeft.z, texCoordTopLeft.x, texCoordTopLeft.y, topRight.x, topRight.y, topRight.z, texCoordTopRight.x, texCoordTopRight.y, }; @@ -1363,24 +1313,13 @@ void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec3& topLeft, cons ((int(color.w * 255.0f) & 0xFF) << 24); int colors[NUM_COLOR_SCALARS_PER_QUAD] = { compactColor, compactColor, compactColor, compactColor }; - // Sam's recommended triangle slices - // Triangle tri1 = { v0, v1, v3 }; - // Triangle tri2 = { v1, v2, v3 }; - // NOTE: Random guy on the internet's recommended triangle slices - // Triangle tri1 = { v0, v1, v2 }; - // Triangle tri2 = { v2, v3, v0 }; - - quint16 indices[NUMBER_OF_INDICES] = { 0, 1, 3, 1, 2, 3 }; - details.verticesBuffer->append(sizeof(vertexBuffer), (gpu::Byte*) vertexBuffer); details.colorBuffer->append(sizeof(colors), (gpu::Byte*) colors); - details.indicesBuffer->append(sizeof(indices), (gpu::Byte*) indices); } batch.setInputFormat(details.streamFormat); batch.setInputStream(0, *details.stream); - batch.setIndexBuffer(gpu::UINT16, details.indicesBuffer, 0); - batch.drawIndexed(gpu::TRIANGLES, NUMBER_OF_INDICES, 0); + batch.draw(gpu::TRIANGLE_STRIP, 4, 0); } void GeometryCache::renderDashedLine(gpu::Batch& batch, const glm::vec3& start, const glm::vec3& end, const glm::vec4& color, int id) { diff --git a/libraries/render-utils/src/GeometryCache.h b/libraries/render-utils/src/GeometryCache.h index 19e84e9346..812d12b846 100644 --- a/libraries/render-utils/src/GeometryCache.h +++ b/libraries/render-utils/src/GeometryCache.h @@ -235,12 +235,10 @@ private: static int population; gpu::BufferPointer verticesBuffer; gpu::BufferPointer colorBuffer; - gpu::BufferPointer indicesBuffer; gpu::Stream::FormatPointer streamFormat; gpu::BufferStreamPointer stream; int vertices; - int indices; int vertexSize; bool isCreated; From b02f751830bdc9cbd526af7c57608ba1dadd7348 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Sat, 1 Aug 2015 16:53:35 -0700 Subject: [PATCH 53/65] diff redux --- libraries/render-utils/src/GeometryCache.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/render-utils/src/GeometryCache.cpp b/libraries/render-utils/src/GeometryCache.cpp index 3d43cc6934..ca14e80cd3 100644 --- a/libraries/render-utils/src/GeometryCache.cpp +++ b/libraries/render-utils/src/GeometryCache.cpp @@ -1041,7 +1041,7 @@ void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec2& minCorner, co minCorner.x, minCorner.y, maxCorner.x, minCorner.y, minCorner.x, maxCorner.y, - maxCorner.x, maxCorner.y + maxCorner.x, maxCorner.y, }; const int NUM_COLOR_SCALARS_PER_QUAD = 4; @@ -1136,7 +1136,7 @@ void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec2& minCorner, co minCorner.x, minCorner.y, texCoordMinCorner.x, texCoordMinCorner.y, maxCorner.x, minCorner.y, texCoordMaxCorner.x, texCoordMinCorner.y, minCorner.x, maxCorner.y, texCoordMinCorner.x, texCoordMaxCorner.y, - maxCorner.x, maxCorner.y, texCoordMaxCorner.x, texCoordMaxCorner.y + maxCorner.x, maxCorner.y, texCoordMaxCorner.x, texCoordMaxCorner.y, }; @@ -1210,7 +1210,7 @@ void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec3& minCorner, co minCorner.x, minCorner.y, minCorner.z, maxCorner.x, minCorner.y, minCorner.z, minCorner.x, maxCorner.y, maxCorner.z, - maxCorner.x, maxCorner.y, maxCorner.z + maxCorner.x, maxCorner.y, maxCorner.z, }; const int NUM_COLOR_SCALARS_PER_QUAD = 4; From 771ce6dca311ef6ec5be54e0075881b7da1c9d7e Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Sat, 1 Aug 2015 17:20:48 -0700 Subject: [PATCH 54/65] remove QUAD_STRIP from Circle3DOverlay --- interface/src/ui/overlays/Circle3DOverlay.cpp | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/interface/src/ui/overlays/Circle3DOverlay.cpp b/interface/src/ui/overlays/Circle3DOverlay.cpp index 53f1b4ce21..fafb759439 100644 --- a/interface/src/ui/overlays/Circle3DOverlay.cpp +++ b/interface/src/ui/overlays/Circle3DOverlay.cpp @@ -119,19 +119,21 @@ void Circle3DOverlay::render(RenderArgs* args) { float angle = startAt; float angleInRadians = glm::radians(angle); - glm::vec2 firstInnerPoint(cosf(angleInRadians) * innerRadius, sinf(angleInRadians) * innerRadius); - glm::vec2 firstOuterPoint(cosf(angleInRadians) * outerRadius, sinf(angleInRadians) * outerRadius); - - points << firstInnerPoint << firstOuterPoint; + glm::vec2 mostRecentInnerPoint(cosf(angleInRadians) * innerRadius, sinf(angleInRadians) * innerRadius); + glm::vec2 mostRecentOuterPoint(cosf(angleInRadians) * outerRadius, sinf(angleInRadians) * outerRadius); while (angle < endAt) { angleInRadians = glm::radians(angle); glm::vec2 thisInnerPoint(cosf(angleInRadians) * innerRadius, sinf(angleInRadians) * innerRadius); glm::vec2 thisOuterPoint(cosf(angleInRadians) * outerRadius, sinf(angleInRadians) * outerRadius); - points << thisOuterPoint << thisInnerPoint; + points << mostRecentInnerPoint << mostRecentOuterPoint << thisOuterPoint; // first triangle + points << mostRecentInnerPoint << thisInnerPoint << thisOuterPoint; // second triangle angle += SLICE_ANGLE; + + mostRecentInnerPoint = thisInnerPoint; + mostRecentOuterPoint = thisOuterPoint; } // get the last slice portion.... @@ -139,13 +141,14 @@ void Circle3DOverlay::render(RenderArgs* args) { angleInRadians = glm::radians(angle); glm::vec2 lastInnerPoint(cosf(angleInRadians) * innerRadius, sinf(angleInRadians) * innerRadius); glm::vec2 lastOuterPoint(cosf(angleInRadians) * outerRadius, sinf(angleInRadians) * outerRadius); - - points << lastOuterPoint << lastInnerPoint; + + points << mostRecentInnerPoint << mostRecentOuterPoint << lastOuterPoint; // first triangle + points << mostRecentInnerPoint << lastInnerPoint << lastOuterPoint; // second triangle geometryCache->updateVertices(_quadVerticesID, points, color); } - geometryCache->renderVertices(batch, gpu::QUAD_STRIP, _quadVerticesID); + geometryCache->renderVertices(batch, gpu::TRIANGLES, _quadVerticesID); } else { if (_lineVerticesID == GeometryCache::UNKNOWN_ID) { From 493836e36312647ab5fa25313e9937ed704000f7 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Sat, 1 Aug 2015 17:26:38 -0700 Subject: [PATCH 55/65] remove QUADS from GLBackendShared and Format --- libraries/gpu/src/gpu/Format.h | 5 ----- libraries/gpu/src/gpu/GLBackendShared.h | 2 -- 2 files changed, 7 deletions(-) diff --git a/libraries/gpu/src/gpu/Format.h b/libraries/gpu/src/gpu/Format.h index 01d3f37ef8..b710a44f39 100644 --- a/libraries/gpu/src/gpu/Format.h +++ b/libraries/gpu/src/gpu/Format.h @@ -213,11 +213,6 @@ enum Primitive { TRIANGLES, TRIANGLE_STRIP, TRIANGLE_FAN, - - // FIXME - remove these - QUADS, - QUAD_STRIP, - NUM_PRIMITIVES, }; diff --git a/libraries/gpu/src/gpu/GLBackendShared.h b/libraries/gpu/src/gpu/GLBackendShared.h index 888fd1164d..7ce54665be 100644 --- a/libraries/gpu/src/gpu/GLBackendShared.h +++ b/libraries/gpu/src/gpu/GLBackendShared.h @@ -23,8 +23,6 @@ static const GLenum _primitiveToGLmode[gpu::NUM_PRIMITIVES] = { GL_TRIANGLES, GL_TRIANGLE_STRIP, GL_TRIANGLE_FAN, - GL_QUADS, - GL_QUAD_STRIP, }; static const GLenum _elementTypeToGLType[gpu::NUM_TYPES] = { From 8f616c04fb08154b6b4f4dee5a78131c57e75cd8 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Sun, 2 Aug 2015 21:46:39 -0700 Subject: [PATCH 56/65] Fix https://app.asana.com/0/32622044445063/43589567731045 --- libraries/audio-client/src/AudioClient.cpp | 17 ++++++++--------- libraries/audio-client/src/AudioClient.h | 3 --- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index 908310d504..a3d3a72043 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -1037,9 +1037,14 @@ bool AudioClient::outputLocalInjector(bool isStereo, AudioInjector* injector) { localOutput->moveToThread(injector->getLocalBuffer()->thread()); // have it be stopped when that local buffer is about to close - connect(localOutput, &QAudioOutput::stateChanged, this, &AudioClient::audioStateChanged); - connect(this, &AudioClient::audioFinished, localOutput, &QAudioOutput::stop); - connect(this, &AudioClient::audioFinished, injector, &AudioInjector::stop); + // We don't want to stop this localOutput and injector whenever this AudioClient singleton goes idle, + // only when the localOutput does. But the connection is to localOutput, so that it happens on the right thread. + connect(localOutput, &QAudioOutput::stateChanged, localOutput, [=](QAudio::State state) { + if (state == QAudio::IdleState) { + localOutput->stop(); + injector->stop(); + } + }); connect(injector->getLocalBuffer(), &QIODevice::aboutToClose, localOutput, &QAudioOutput::stop); @@ -1358,9 +1363,3 @@ void AudioClient::saveSettings() { windowSecondsForDesiredReduction.set(_receivedAudioStream.getWindowSecondsForDesiredReduction()); repetitionWithFade.set(_receivedAudioStream.getRepetitionWithFade()); } - -void AudioClient::audioStateChanged(QAudio::State state) { - if (state == QAudio::IdleState) { - emit audioFinished(); - } -} diff --git a/libraries/audio-client/src/AudioClient.h b/libraries/audio-client/src/AudioClient.h index a1d08ec540..3205aeda1d 100644 --- a/libraries/audio-client/src/AudioClient.h +++ b/libraries/audio-client/src/AudioClient.h @@ -209,9 +209,6 @@ protected: deleteLater(); } -private slots: - void audioStateChanged(QAudio::State state); - private: void outputFormatChanged(); From 055c9dc59b5e064bfde03281dd894148cf487a21 Mon Sep 17 00:00:00 2001 From: Zander Otavka Date: Mon, 3 Aug 2015 10:41:35 -0700 Subject: [PATCH 57/65] Fix image URLs in controlPanel.js. --- examples/controlPanel.js | 8 ++++---- examples/libraries/overlayManager.js | 4 ++++ 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/examples/controlPanel.js b/examples/controlPanel.js index 837b59ffa0..ebad7d8d83 100644 --- a/examples/controlPanel.js +++ b/examples/controlPanel.js @@ -17,10 +17,10 @@ Script.include([ ]); var BG_IMAGE_URL = HIFI_PUBLIC_BUCKET + "images/card-bg.svg"; -var CLOSE_IMAGE_URL = HIFI_PUBLIC_BUCKET + "images/close.svg"; -var MIC_IMAGE_URL = HIFI_PUBLIC_BUCKET + "images/mic-toggle.svg"; -var FACE_IMAGE_URL = HIFI_PUBLIC_BUCKET + "images/face-toggle.svg"; -var ADDRESS_BAR_IMAGE_URL = HIFI_PUBLIC_BUCKET + "images/address-bar-toggle.svg"; +var CLOSE_IMAGE_URL = HIFI_PUBLIC_BUCKET + "images/tools/close.svg"; +var MIC_IMAGE_URL = HIFI_PUBLIC_BUCKET + "images/tools/mic-toggle.svg"; +var FACE_IMAGE_URL = HIFI_PUBLIC_BUCKET + "images/tools/face-toggle.svg"; +var ADDRESS_BAR_IMAGE_URL = HIFI_PUBLIC_BUCKET + "images/tools/address-bar-toggle.svg"; var panel = new FloatingUIPanel({ anchorPosition: { diff --git a/examples/libraries/overlayManager.js b/examples/libraries/overlayManager.js index 55575badee..cd05cd2a52 100644 --- a/examples/libraries/overlayManager.js +++ b/examples/libraries/overlayManager.js @@ -24,6 +24,10 @@ // // See more on usage below. // +// Note that including this file will delete Overlays from the global scope. All the +// functionality of Overlays is represented here, just better. If you try to use Overlays in +// tandem, there may be performance problems or nasty surprises. +// // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // From d24006e71593ae6db7d03b921d6b6dca011eb462 Mon Sep 17 00:00:00 2001 From: Zander Otavka Date: Mon, 3 Aug 2015 11:14:55 -0700 Subject: [PATCH 58/65] Improve logic around handling click and drag. --- examples/controlPanel.js | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/examples/controlPanel.js b/examples/controlPanel.js index ebad7d8d83..59d66fdaf9 100644 --- a/examples/controlPanel.js +++ b/examples/controlPanel.js @@ -172,26 +172,25 @@ function onFaceMuteToggled() { } onFaceMuteToggled(); -var isLeftClick = false, - isRightClick = false; +var mouseDown = {}; function onMouseDown(event) { - isLeftClick = event.isLeftButton; - isRightClick = event.isRightButton; -} - -function onMouseMove(event) { - isLeftClick = isRightClick = false; + if (event.isLeftButton) { + mouseDown.overlay = OverlayManager.findAtPoint({ x: event.x, y: event.y }); + } + if (event.isRightButton) { + mouseDown.pos = { x: event.x, y: event.y }; + } } function onMouseUp(event) { - if (isLeftClick && event.isLeftButton) { + if (event.isLeftButton) { var overlay = OverlayManager.findAtPoint({ x: event.x, y: event.y }); - if (overlay && overlay.onClick) { + if (overlay && overlay === mouseDown.overlay && overlay.onClick) { overlay.onClick(event); } } - if (isRightClick && event.isRightButton) { + if (event.isRightButton && Vec3.distance(mouseDown.pos, { x: event.x, y: event.y }) < 5) { panel.setProperties({ visible: !panel.visible, offsetRotation: { @@ -200,7 +199,8 @@ function onMouseUp(event) { } }); } - isLeftClick = isRightClick = false; + + mouseDown = {}; } function onScriptEnd(event) { @@ -208,7 +208,6 @@ function onScriptEnd(event) { } Controller.mousePressEvent.connect(onMouseDown); -Controller.mouseMoveEvent.connect(onMouseMove); Controller.mouseReleaseEvent.connect(onMouseUp); AudioDevice.muteToggled.connect(onMicMuteToggled); FaceTracker.muteToggled.connect(onFaceMuteToggled); From 1b4ba75b5a597313576940810833cb5f2a59d82c Mon Sep 17 00:00:00 2001 From: Zander Otavka Date: Mon, 3 Aug 2015 12:01:15 -0700 Subject: [PATCH 59/65] Fix click and drag logic for floatingUIExample.js. --- examples/example/ui/floatingUIExample.js | 43 ++++++++++++------------ 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/examples/example/ui/floatingUIExample.js b/examples/example/ui/floatingUIExample.js index 9c4e43be94..1e957fe6b3 100644 --- a/examples/example/ui/floatingUIExample.js +++ b/examples/example/ui/floatingUIExample.js @@ -127,35 +127,37 @@ blueSquare3.offsetPosition = { }; -function onMouseDown(event) { - isLeftClick = event.isLeftButton; - isRightClick = event.isRightButton; -} +var mouseDown = {}; -function onMouseMove(event) { - isLeftClick = isRightClick = false; +function onMouseDown(event) { + if (event.isLeftButton) { + mouseDown.overlay = OverlayManager.findAtPoint({ x: event.x, y: event.y }); + } + if (event.isRightButton) { + mouseDown.pos = { x: event.x, y: event.y }; + } } function onMouseUp(event) { - if (isLeftClick && event.isLeftButton) { + if (event.isLeftButton) { var overlay = OverlayManager.findAtPoint({ x: event.x, y: event.y }); - print(overlay.attachedPanel); - if (overlay.attachedPanel === bluePanel) { - overlay.destroy(); - } else if (overlay) { - var oldPos = overlay.offsetPosition; - var newPos = { - x: Number(oldPos.x), - y: Number(oldPos.y), - z: Number(oldPos.z) + 0.1 - }; - overlay.offsetPosition = newPos; + if (overlay === mouseDown.overlay) { + if (overlay.attachedPanel === bluePanel) { + overlay.destroy(); + } else if (overlay) { + var oldPos = overlay.offsetPosition; + var newPos = { + x: Number(oldPos.x), + y: Number(oldPos.y), + z: Number(oldPos.z) + 0.1 + }; + overlay.offsetPosition = newPos; + } } } - if (isRightClick && event.isRightButton) { + if (event.isRightButton && Vec3.distance(mouseDown.pos, { x: event.x, y: event.y }) < 5) { mainPanel.visible = !mainPanel.visible; } - isLeftClick = isRightClick = false; } function onScriptEnd() { @@ -163,6 +165,5 @@ function onScriptEnd() { } Controller.mousePressEvent.connect(onMouseDown); -Controller.mouseMoveEvent.connect(onMouseMove); Controller.mouseReleaseEvent.connect(onMouseUp); Script.scriptEnding.connect(onScriptEnd); \ No newline at end of file From 5e7cb728438b4ce6391c3241251e129249046de0 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Mon, 3 Aug 2015 13:02:12 -0700 Subject: [PATCH 60/65] Reduce the speed of movement when in HMD view When using hmdControls.js. --- examples/hmdControls.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/hmdControls.js b/examples/hmdControls.js index e14ddca3ef..04c1dade0a 100644 --- a/examples/hmdControls.js +++ b/examples/hmdControls.js @@ -9,7 +9,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -var MOVE_DISTANCE = 10.0; +var MOVE_DISTANCE = 2.0; var PITCH_INCREMENT = 0.5; // degrees var pitchChange = 0; // degrees var YAW_INCREMENT = 0.5; // degrees From 4c1d1a65d50aab90ca19ccc5d482c1c28732f9e6 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Mon, 3 Aug 2015 15:41:15 -0700 Subject: [PATCH 61/65] Provide initial fight club animations and use them in rig. Still only active when you do Settings.setValue('enableRig', true) and restart. (Will be more exposed after fadein/fadeout is implemented.) --- libraries/animation/src/Rig.cpp | 100 ++++++++++++++++++++++---------- libraries/animation/src/Rig.h | 4 +- 2 files changed, 70 insertions(+), 34 deletions(-) diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 8406b61d12..45828d44b6 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -90,8 +90,34 @@ void Rig::addAnimationByRole(const QString& role, const QString& url, float fps, } } AnimationHandlePointer handle = createAnimationHandle(); + QString standard = ""; + if (url.isEmpty()) { // Default animations for fight club + const QString& base = "https://hifi-public.s3.amazonaws.com/ozan/"; + if (role == "walk") { + standard = base + "support/FightClubBotTest1/Animations/standard_walk.fbx"; + lastFrame = 60; + } else if (role == "leftTurn") { + standard = base + "support/FightClubBotTest1/Animations/left_turn_noHipRotation.fbx"; + lastFrame = 29; + } else if (role == "rightTurn") { + standard = base + "support/FightClubBotTest1/Animations/right_turn_noHipRotation.fbx"; + lastFrame = 31; + } else if (role == "leftStrafe") { + standard = base + "animations/fightclub_bot_anims/side_step_left_inPlace.fbx"; + lastFrame = 31; + } else if (role == "rightStrafe") { + standard = base + "animations/fightclub_bot_anims/side_step_right_inPlace.fbx"; + lastFrame = 31; + } else if (role == "idle") { + standard = base + "support/FightClubBotTest1/Animations/standard_idle.fbx"; + fps = 25.0f; + } + if (!standard.isEmpty()) { + loop = true; + } + } handle->setRole(role); - handle->setURL(url); + handle->setURL(url.isEmpty() ? standard : url); handle->setFPS(fps); handle->setPriority(priority); handle->setLoop(loop); @@ -135,6 +161,14 @@ void Rig::addRunningAnimation(AnimationHandlePointer animationHandle) { bool Rig::isRunningAnimation(AnimationHandlePointer animationHandle) { return _runningAnimations.contains(animationHandle); } +bool Rig::isRunningRole(const QString& role) { //obviously, there are more efficient ways to do this + for (auto animation : _runningAnimations) { + if (animation->getRole() == role) { + return true; + } + } + return false; +} void Rig::deleteAnimations() { for (auto animation : _animationHandles) { @@ -356,37 +390,41 @@ glm::mat4 Rig::getJointVisibleTransform(int jointIndex) const { } void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPosition, const glm::vec3& worldVelocity, const glm::quat& worldRotation) { - if (_enableRig) { - glm::vec3 front = worldRotation * IDENTITY_FRONT; - float forwardSpeed = glm::dot(worldVelocity, front); - float rotationalSpeed = glm::angle(front, _lastFront) / deltaTime; - bool isWalking = std::abs(forwardSpeed) > 0.01f; - bool isTurning = std::abs(rotationalSpeed) > 0.5f; - - // Crude, until we have blending: - isTurning = isTurning && !isWalking; // Only one of walk/turn, walk wins. - isTurning = false; // FIXME - bool isIdle = !isWalking && !isTurning; - auto singleRole = [](bool walking, bool turning, bool idling) { - return walking ? "walk" : (turning ? "turn" : (idling ? "idle" : "")); - }; - QString toStop = singleRole(_isWalking && !isWalking, _isTurning && !isTurning, _isIdle && !isIdle); - if (!toStop.isEmpty()) { - //qCDebug(animation) << "isTurning" << isTurning << "fronts" << front << _lastFront << glm::angle(front, _lastFront) << rotationalSpeed; - stopAnimationByRole(toStop); - } - QString newRole = singleRole(isWalking && !_isWalking, isTurning && !_isTurning, isIdle && !_isIdle); - if (!newRole.isEmpty()) { - startAnimationByRole(newRole); - qCDebug(animation) << deltaTime << ":" << worldVelocity << "." << front << "=> " << forwardSpeed << newRole; - } - - _lastPosition = worldPosition; - _lastFront = front; - _isWalking = isWalking; - _isTurning = isTurning; - _isIdle = isIdle; + if (!_enableRig) { + return; } + bool isMoving = false; + glm::vec3 front = worldRotation * IDENTITY_FRONT; + float forwardSpeed = glm::dot(worldVelocity, front); + float rightLateralSpeed = glm::dot(worldVelocity, worldRotation * IDENTITY_RIGHT); + float rightTurningSpeed = glm::orientedAngle(front, _lastFront, IDENTITY_UP) / deltaTime; + auto updateRole = [=](const QString& role, bool isOn) mutable { + isMoving = isMoving || isOn; + if (isOn) { + if (!isRunningRole(role)) { + qCDebug(animation) << "Rig STARTING" << role; + if (role == "leftTurn" || role == "rightTurn") { + qCDebug(animation) << front.x << front.y << front.z << "=>" << rightTurningSpeed; + } + startAnimationByRole(role); + } + } else { + if (isRunningRole(role)) { + qCDebug(animation) << "Rig stopping" << role; + stopAnimationByRole(role); + } + } + }; + updateRole("walk", std::abs(forwardSpeed) > 0.01f); + bool isTurning = std::abs(rightTurningSpeed) > 0.5f; + updateRole("rightTurn", isTurning && (rightTurningSpeed > 0)); + updateRole("leftTurn", isTurning && (rightTurningSpeed < 0)); + bool isStrafing = std::abs(rightLateralSpeed) > 0.01f; + updateRole("rightStrafe", isStrafing && (rightLateralSpeed > 0.0f)); + updateRole("leftStrafe", isStrafing && (rightLateralSpeed < 0.0f)); + updateRole("idle", !isMoving); // Must be last, as it makes isMoving bogus. + _lastFront = front; + _lastPosition = worldPosition; } void Rig::updateAnimations(float deltaTime, glm::mat4 parentTransform) { diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index 52db16826a..a79b99f4dc 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -76,6 +76,7 @@ public: bool removeRunningAnimation(AnimationHandlePointer animationHandle); void addRunningAnimation(AnimationHandlePointer animationHandle); bool isRunningAnimation(AnimationHandlePointer animationHandle); + bool isRunningRole(const QString& role); // There can be multiple animations per role, so this is more general than isRunningAnimation. const QList& getRunningAnimations() const { return _runningAnimations; } void deleteAnimations(); const QList& getAnimationHandles() const { return _animationHandles; } @@ -161,9 +162,6 @@ public: QList _runningAnimations; bool _enableRig; - bool _isWalking; - bool _isTurning; - bool _isIdle; glm::vec3 _lastFront; glm::vec3 _lastPosition; }; From cdb697760f30f738c79ace5ab87cea573f8e334c Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Mon, 3 Aug 2015 15:51:07 -0700 Subject: [PATCH 62/65] Remove some debug. --- libraries/animation/src/Rig.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 45828d44b6..4a03ab83ee 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -403,9 +403,6 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos if (isOn) { if (!isRunningRole(role)) { qCDebug(animation) << "Rig STARTING" << role; - if (role == "leftTurn" || role == "rightTurn") { - qCDebug(animation) << front.x << front.y << front.z << "=>" << rightTurningSpeed; - } startAnimationByRole(role); } } else { From 2e5142e9bb5110544fea62f597039cf9021bc0ea Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Mon, 3 Aug 2015 16:09:48 -0700 Subject: [PATCH 63/65] Fix end of idle. --- libraries/animation/src/Rig.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 4a03ab83ee..f8419509d4 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -398,7 +398,7 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos float forwardSpeed = glm::dot(worldVelocity, front); float rightLateralSpeed = glm::dot(worldVelocity, worldRotation * IDENTITY_RIGHT); float rightTurningSpeed = glm::orientedAngle(front, _lastFront, IDENTITY_UP) / deltaTime; - auto updateRole = [=](const QString& role, bool isOn) mutable { + auto updateRole = [&](const QString& role, bool isOn) { isMoving = isMoving || isOn; if (isOn) { if (!isRunningRole(role)) { From 61afc362e770d3287832aa390696b4b9ab5bdf7e Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Mon, 3 Aug 2015 17:00:39 -0700 Subject: [PATCH 64/65] For startAnimation (but not startAnimationByRole), use the specified parameters even if the animation was already playing. This fixes the behavior exercised by the squeezeHands.js script. Fixes https://app.asana.com/0/32622044445063/44025709513292 --- libraries/animation/src/Rig.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 8406b61d12..b91606dc8e 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -58,15 +58,18 @@ void Rig::removeAnimationHandle(const AnimationHandlePointer& handle) { void Rig::startAnimation(const QString& url, float fps, float priority, bool loop, bool hold, float firstFrame, float lastFrame, const QStringList& maskedJoints) { - //qCDebug(animation) << "startAnimation" << url << fps << priority << loop << hold << firstFrame << lastFrame << maskedJoints; + // This is different than startAnimationByRole, in which we use the existing values if the animation already exists. + // Here we reuse the animation handle if possible, but in any case, we set the values to those given (or defaulted). + AnimationHandlePointer handle = nullptr; foreach (const AnimationHandlePointer& candidate, _animationHandles) { if (candidate->getURL() == url) { - candidate->start(); - return; + handle = candidate; } } - AnimationHandlePointer handle = createAnimationHandle(); - handle->setURL(url); + if (!handle) { + handle = createAnimationHandle(); + handle->setURL(url); + } handle->setFPS(fps); handle->setPriority(priority); handle->setLoop(loop); From 4bf6f8ed7798a056eaa788535c261c0e8449f837 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Tue, 4 Aug 2015 14:47:13 +0200 Subject: [PATCH 65/65] ModelOverlay causes a crash in the Model class. This is because ModelOverlay doesn't give a valid rig to the Model. --- interface/src/ui/overlays/ModelOverlay.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/interface/src/ui/overlays/ModelOverlay.cpp b/interface/src/ui/overlays/ModelOverlay.cpp index de8e05323b..49321e7d6d 100644 --- a/interface/src/ui/overlays/ModelOverlay.cpp +++ b/interface/src/ui/overlays/ModelOverlay.cpp @@ -10,6 +10,7 @@ // #include "ModelOverlay.h" +#include "EntityRig.h" #include "Application.h" @@ -17,7 +18,7 @@ QString const ModelOverlay::TYPE = "model"; ModelOverlay::ModelOverlay() - : _model(nullptr), + : _model(std::make_shared()), _modelTextures(QVariantMap()), _updateModel(false) { @@ -27,7 +28,7 @@ ModelOverlay::ModelOverlay() ModelOverlay::ModelOverlay(const ModelOverlay* modelOverlay) : Volume3DOverlay(modelOverlay), - _model(nullptr), + _model(std::make_shared()), _modelTextures(QVariantMap()), _url(modelOverlay->_url), _updateModel(false)