diff --git a/CMakeLists.txt b/CMakeLists.txt index 9e95974383..a8271abd8e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -36,7 +36,12 @@ if (WIN32) if (MSVC10) set(WINDOW_SDK_PATH "C:\\Program Files\\Microsoft SDKs\\Windows\\v7.1 ") elseif (MSVC12) - set(WINDOW_SDK_PATH "C:\\Program Files (x86)\\Windows Kits\\8.1\\Lib\\winv6.3\\um\\x86 ") + if ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8") + set(WINDOW_SDK_FOLDER "x64") + else() + set(WINDOW_SDK_FOLDER "x86") + endif() + set(WINDOW_SDK_PATH "C:\\Program Files (x86)\\Windows Kits\\8.1\\Lib\\winv6.3\\um\\${WINDOW_SDK_FOLDER}") endif () message (WINDOW_SDK_PATH= ${WINDOW_SDK_PATH}) set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} ${WINDOW_SDK_PATH}) @@ -221,3 +226,36 @@ if (HIFI_MEMORY_DEBUGGING) MESSAGE("-- Memory debugging is enabled") endif (UNIX) endif () + +include_application_version() + +if (DEFINED DEPLOY_PACKAGE AND DEPLOY_PACKAGE) + message(STATUS "+++++ Package for deployment will be generated on this build +++++") + + file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/full-stack-deployment) + + set(ICONPATH_INTERFACE "$INSTDIR/${PATH_INSTALL_DATA}/interface.ico") + set(ICONPATH_STACK_MANAGER "$INSTDIR/${PATH_INSTALL_DATA}/stack-manager.ico") + string(REPLACE "/" "\\\\" ICONPATH_INTERFACE ${ICONPATH_INTERFACE}) + string(REPLACE "/" "\\\\" ICONPATH_STACK_MANAGER ${ICONPATH_STACK_MANAGER}) + + set(CPACK_PACKAGE_NAME "High Fidelity") + set(CPACK_PACKAGE_VENDOR "High Fidelity, Inc") + set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "High Fidelity Interface and Stack") + set(CPACK_PACKAGE_VERSION "${BUILD_SEQ}") + set(CPACK_PACKAGE_VERSION_MAJOR "${BUILD_SEQ}") + set(CPACK_PACKAGE_VERSION_MINOR "0") + set(CPACK_PACKAGE_VERSION_PATCH "0") + set(CPACK_PACKAGE_INSTALL_DIRECTORY "High Fidelity-${BUILD_SEQ}") + set(CPACK_NSIS_EXECUTABLES_DIRECTORY ".") + set(CPACK_PACKAGE_EXECUTABLES + stack-manager "Stack Manager" + interface "Interface" + ) + + if (WIN32) + install(DIRECTORY ${CMAKE_BINARY_DIR}/full-stack-deployment/ DESTINATION "./") + endif (WIN32) + + include(CPack) +endif () diff --git a/assignment-client/CMakeLists.txt b/assignment-client/CMakeLists.txt index 58f200b0fe..3064e97d70 100644 --- a/assignment-client/CMakeLists.txt +++ b/assignment-client/CMakeLists.txt @@ -11,3 +11,4 @@ link_hifi_libraries( include_application_version() package_libraries_for_deployment() +consolidate_stack_components() \ No newline at end of file diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index d744acc42b..46fa8c8181 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -102,8 +102,10 @@ void Agent::handleOctreePacket(QSharedPointer message, SharedNo packetType = message->getType(); } // fall through to piggyback message - if (packetType == PacketType::EntityData || packetType == PacketType::EntityErase) { + if (packetType == PacketType::EntityData) { _entityViewer.processDatagram(*message, senderNode); + } else if (packetType == PacketType::EntityErase) { + _entityViewer.processEraseMessage(*message, senderNode); } } diff --git a/assignment-client/src/octree/OctreeQueryNode.cpp b/assignment-client/src/octree/OctreeQueryNode.cpp index ba47a15ef3..ffe2e24ca0 100644 --- a/assignment-client/src/octree/OctreeQueryNode.cpp +++ b/assignment-client/src/octree/OctreeQueryNode.cpp @@ -225,6 +225,8 @@ bool OctreeQueryNode::updateCurrentViewFrustum() { newestViewFrustum.setPosition(getCameraPosition()); newestViewFrustum.setOrientation(getCameraOrientation()); + newestViewFrustum.setKeyholeRadius(getKeyholeRadius()); + // Also make sure it's got the correct lens details from the camera float originalFOV = getCameraFov(); float wideFOV = originalFOV + VIEW_FRUSTUM_FOV_OVERSEND; diff --git a/assignment-client/src/octree/OctreeSendThread.cpp b/assignment-client/src/octree/OctreeSendThread.cpp index 9b664572c1..b4eb75ede9 100644 --- a/assignment-client/src/octree/OctreeSendThread.cpp +++ b/assignment-client/src/octree/OctreeSendThread.cpp @@ -416,27 +416,6 @@ int OctreeSendThread::packetDistributor(OctreeQueryNode* nodeData, bool viewFrus return; } - /* TODO: Looking for a way to prevent locking and encoding a tree that is not - // going to result in any packets being sent... - // - // If our node is root, and the root hasn't changed, and our view hasn't changed, - // and we've already seen at least one duplicate packet, then we probably don't need - // to lock the tree and encode, because the result should be that no bytes will be - // encoded, and this will be a duplicate packet from the last one we sent... - OctreeElementPointer root = _myServer->getOctree()->getRoot(); - bool skipEncode = false; - if ( - (subTree == root) - && (nodeData->getLastRootTimestamp() == root->getLastChanged()) - && !viewFrustumChanged - && (nodeData->getDuplicatePacketCount() > 0) - ) { - qDebug() << "is root, root not changed, view not changed, already seen a duplicate!" - << "Can we skip it?"; - skipEncode = true; - } - */ - float octreeSizeScale = nodeData->getOctreeSizeScale(); int boundaryLevelAdjustClient = nodeData->getBoundaryLevelAdjust(); @@ -471,20 +450,9 @@ int OctreeSendThread::packetDistributor(OctreeQueryNode* nodeData, bool viewFrus // the packet and send it completedScene = nodeData->elementBag.isEmpty(); - // if we're trying to fill a full size packet, then we use this logic to determine if we have a DIDNT_FIT case. - if (_packetData.getTargetSize() == MAX_OCTREE_PACKET_DATA_SIZE) { - if (_packetData.hasContent() && bytesWritten == 0 && - params.stopReason == EncodeBitstreamParams::DIDNT_FIT) { - lastNodeDidntFit = true; - } - } else { - // in compressed mode and we are trying to pack more... and we don't care if the _packetData has - // content or not... because in this case even if we were unable to pack any data, we want to drop - // below to our sendNow logic, but we do want to track that we attempted to pack extra + if (params.stopReason == EncodeBitstreamParams::DIDNT_FIT) { + lastNodeDidntFit = true; extraPackingAttempts++; - if (bytesWritten == 0 && params.stopReason == EncodeBitstreamParams::DIDNT_FIT) { - lastNodeDidntFit = true; - } } nodeData->stats.encodeStopped(); @@ -517,7 +485,6 @@ int OctreeSendThread::packetDistributor(OctreeQueryNode* nodeData, bool viewFrus } nodeData->writeToPacket(_packetData.getFinalizedData(), _packetData.getFinalizedSize()); - extraPackingAttempts = 0; quint64 compressAndWriteEnd = usecTimestampNow(); compressAndWriteElapsedUsec = (float)(compressAndWriteEnd - compressAndWriteStart); } @@ -526,8 +493,8 @@ int OctreeSendThread::packetDistributor(OctreeQueryNode* nodeData, bool viewFrus // the packet doesn't have enough space to bother attempting to pack more... bool sendNow = true; - if (nodeData->getAvailable() >= MINIMUM_ATTEMPT_MORE_PACKING && - extraPackingAttempts <= REASONABLE_NUMBER_OF_PACKING_ATTEMPTS) { + if (!completedScene && (nodeData->getAvailable() >= MINIMUM_ATTEMPT_MORE_PACKING && + extraPackingAttempts <= REASONABLE_NUMBER_OF_PACKING_ATTEMPTS)) { sendNow = false; // try to pack more } @@ -539,6 +506,7 @@ int OctreeSendThread::packetDistributor(OctreeQueryNode* nodeData, bool viewFrus packetSendingElapsedUsec = (float)(packetSendingEnd - packetSendingStart); targetSize = nodeData->getAvailable() - sizeof(OCTREE_PACKET_INTERNAL_SECTION_SIZE); + extraPackingAttempts = 0; } else { // If we're in compressed mode, then we want to see if we have room for more in this wire packet. // but we've finalized the _packetData, so we want to start a new section, we will do that by diff --git a/cmake/externals/LibOVR/CMakeLists.txt b/cmake/externals/LibOVR/CMakeLists.txt index 8d13882d48..a98745b404 100644 --- a/cmake/externals/LibOVR/CMakeLists.txt +++ b/cmake/externals/LibOVR/CMakeLists.txt @@ -15,17 +15,16 @@ string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER) # 0.6 public # URL http://static.oculus.com/sdk-downloads/0.6.0.1/Public/1435190862/ovr_sdk_win_0.6.0.1.zip # URL_MD5 4b3ef825f9a1d6d3035c9f6820687da9 -# 0.7 alpha -# URL https://s3.amazonaws.com/static.oculus.com/sdk-downloads/0.7.0.0/Public/Alpha/ovr_sdk_win_0.7.0.0_RC1.zip -# URL_MD5 a562bb9d117087b2cf9d86653ea70fd8 - +# 0.8 public +# URL http://static.oculus.com/sdk-downloads/0.8.0.0/Public/1445451746/ovr_sdk_win_0.8.0.0.zip +# URL_MD5 54944b03b95149d6010f84eb701b9647 if (WIN32) ExternalProject_Add( ${EXTERNAL_NAME} - URL http://static.oculus.com/sdk-downloads/0.6.0.1/Public/1435190862/ovr_sdk_win_0.6.0.1.zip - URL_MD5 4b3ef825f9a1d6d3035c9f6820687da9 + URL http://static.oculus.com/sdk-downloads/0.8.0.0/Public/1445451746/ovr_sdk_win_0.8.0.0.zip + URL_MD5 54944b03b95149d6010f84eb701b9647 CONFIGURE_COMMAND "" BUILD_COMMAND "" INSTALL_COMMAND "" diff --git a/cmake/externals/openvr/CMakeLists.txt b/cmake/externals/openvr/CMakeLists.txt index f9d0ef5a71..9ef95b17da 100644 --- a/cmake/externals/openvr/CMakeLists.txt +++ b/cmake/externals/openvr/CMakeLists.txt @@ -7,9 +7,8 @@ string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER) ExternalProject_Add( ${EXTERNAL_NAME} - #URL https://github.com/ValveSoftware/openvr/archive/0.9.1.zip - URL http://hifi-public.s3.amazonaws.com/dependencies/openvr-0.9.1.zip - URL_MD5 f986f5a6815e9454c53c5bf58ce02fdc + URL https://github.com/ValveSoftware/openvr/archive/v0.9.12.zip + URL_MD5 c08dced68ce4e341e1467e6814ae419d CONFIGURE_COMMAND "" BUILD_COMMAND "" INSTALL_COMMAND "" diff --git a/cmake/externals/sdl2/CMakeLists.txt b/cmake/externals/sdl2/CMakeLists.txt index ee1d57d2e9..4222cd41f0 100644 --- a/cmake/externals/sdl2/CMakeLists.txt +++ b/cmake/externals/sdl2/CMakeLists.txt @@ -63,7 +63,11 @@ set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals") if (APPLE) + +# NOOP + elseif (WIN32) + ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR) set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIR ${SOURCE_DIR}/include CACHE PATH "Location of SDL2 include directory") @@ -75,8 +79,12 @@ elseif (WIN32) set(${EXTERNAL_NAME_UPPER}_DLL_PATH ${SOURCE_DIR}/lib/x86 CACHE PATH "Location of SDL2 DLL") endif() + add_paths_to_fixup_libs(${${EXTERNAL_NAME_UPPER}_DLL_PATH}) + else () + ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR) set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIR ${INSTALL_DIR}/include/SDL2 CACHE PATH "Location of SDL2 include directory") set(${EXTERNAL_NAME_UPPER}_LIBRARY_TEMP ${INSTALL_DIR}/lib/libSDL2.so CACHE FILEPATH "Path to SDL2 library") + endif () diff --git a/cmake/externals/sixense/CMakeLists.txt b/cmake/externals/sixense/CMakeLists.txt index c41694344e..16f2850449 100644 --- a/cmake/externals/sixense/CMakeLists.txt +++ b/cmake/externals/sixense/CMakeLists.txt @@ -8,14 +8,14 @@ string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER) #set(SIXENSE_URL_MD5 "10cc8dc470d2ac1244a88cf04bc549cc") #set(SIXENSE_NEW_LAYOUT 0) -#set(SIXENSE_URL "http://public.s3.amazonaws.com/dependencies/SixenseSDK_071615.zip") -#set(SIXENSE_URL_MD5 "752a3901f334124e9cffc2ba4136ef7d") -#set(SIXENSE_NEW_LAYOUT 1) - -set(SIXENSE_URL "http://hifi-public.s3.amazonaws.com/dependencies/SixenseSDK_102215.zip") -set(SIXENSE_URL_MD5 "93c3a6795cce777a0f472b09532935f1") +set(SIXENSE_URL "http://hifi-public.s3.amazonaws.com/dependencies/SixenseSDK_071615.zip") +set(SIXENSE_URL_MD5 "752a3901f334124e9cffc2ba4136ef7d") set(SIXENSE_NEW_LAYOUT 1) +#set(SIXENSE_URL "http://hifi-public.s3.amazonaws.com/dependencies/SixenseSDK_102215.zip") +#set(SIXENSE_URL_MD5 "93c3a6795cce777a0f472b09532935f1") +#set(SIXENSE_NEW_LAYOUT 1) + ExternalProject_Add( ${EXTERNAL_NAME} URL ${SIXENSE_URL} diff --git a/cmake/macros/ConsolidateStackComponents.cmake b/cmake/macros/ConsolidateStackComponents.cmake new file mode 100644 index 0000000000..4bcc777751 --- /dev/null +++ b/cmake/macros/ConsolidateStackComponents.cmake @@ -0,0 +1,27 @@ +macro(CONSOLIDATE_STACK_COMPONENTS) + + if (DEFINED DEPLOY_PACKAGE AND DEPLOY_PACKAGE AND WIN32) + + # Copy all the output for this target into the common deployment location + add_custom_command( + TARGET ${TARGET_NAME} POST_BUILD + COMMAND "${CMAKE_COMMAND}" -E copy_directory $ ${CMAKE_BINARY_DIR}/full-stack-deployment + ) + + # Copy icon files for interface and stack manager + if (TARGET_NAME STREQUAL "interface" OR TARGET_NAME STREQUAL "stack-manager") + if (TARGET_NAME STREQUAL "interface") + set (ICON_FILE_PATH "${PROJECT_SOURCE_DIR}/icon/interface.ico") + set (ICON_DESTINATION_NAME "interface.ico") + elseif (TARGET_NAME STREQUAL "stack-manager") + set (ICON_FILE_PATH "${PROJECT_SOURCE_DIR}/assets/icon.ico") + set (ICON_DESTINATION_NAME "stack-manager.ico") + endif () + add_custom_command( + TARGET ${TARGET_NAME} POST_BUILD + COMMAND "${CMAKE_COMMAND}" -E copy ${ICON_FILE_PATH} ${CMAKE_BINARY_DIR}/full-stack-deployment/${ICON_DESTINATION_NAME} + ) + endif () + endif () + +endmacro() \ No newline at end of file diff --git a/cmake/macros/IncludeApplicationVersion.cmake b/cmake/macros/IncludeApplicationVersion.cmake index 96137294a7..a91aad6acc 100644 --- a/cmake/macros/IncludeApplicationVersion.cmake +++ b/cmake/macros/IncludeApplicationVersion.cmake @@ -10,10 +10,15 @@ # macro(INCLUDE_APPLICATION_VERSION) + # + # We are relying on Jenkins defined environment variables to determine the origin of this build + # and will only package if this is a PR or Release build if (DEFINED ENV{JOB_ID}) + set (DEPLOY_PACKAGE 1) set (BUILD_SEQ $ENV{JOB_ID}) elseif (DEFINED ENV{ghprbPullId}) - set (BUILD_SEQ "PR: $ENV{ghprbPullId} - Commit: $ENV{ghprbActualCommit}") + set (DEPLOY_PACKAGE 1) + set (BUILD_SEQ "PR-$ENV{ghprbPullId}") else () set(BUILD_SEQ "dev") endif () diff --git a/cmake/macros/CopyDllsBesideWindowsExecutable.cmake b/cmake/macros/PackageLibrariesForDeployment.cmake similarity index 98% rename from cmake/macros/CopyDllsBesideWindowsExecutable.cmake rename to cmake/macros/PackageLibrariesForDeployment.cmake index 9330515a62..bb0b268dd4 100644 --- a/cmake/macros/CopyDllsBesideWindowsExecutable.cmake +++ b/cmake/macros/PackageLibrariesForDeployment.cmake @@ -1,5 +1,5 @@ # -# CopyDllsBesideWindowsExecutable.cmake +# PackageLibrariesForDeployment.cmake # cmake/macros # # Copyright 2015 High Fidelity, Inc. diff --git a/cmake/modules/FindOpenSSL.cmake b/cmake/modules/FindOpenSSL.cmake index 2142322687..9d9557ad9e 100644 --- a/cmake/modules/FindOpenSSL.cmake +++ b/cmake/modules/FindOpenSSL.cmake @@ -107,7 +107,7 @@ if (WIN32 AND NOT CYGWIN) select_library_configurations(SSL_EAY) set(OPENSSL_LIBRARIES ${SSL_EAY_LIBRARY} ${LIB_EAY_LIBRARY}) - + find_path(OPENSSL_DLL_PATH NAMES ssleay32.dll PATH_SUFFIXES "bin" ${_OPENSSL_ROOT_HINTS_AND_PATHS}) elseif (MINGW) @@ -252,6 +252,15 @@ endif () if (WIN32) add_paths_to_fixup_libs(${OPENSSL_DLL_PATH}) + # + # For some reason fixup misses the following DLL and only copies libeay32. There's gotta be a better way to handle this + # but for now resorting to the following interm solution + if (DEFINED DEPLOY_PACKAGE AND DEPLOY_PACKAGE) + add_custom_command( + TARGET ${TARGET_NAME} POST_BUILD + COMMAND "${CMAKE_COMMAND}" -E copy ${OPENSSL_DLL_PATH}/ssleay32.dll ${CMAKE_BINARY_DIR}/full-stack-deployment/ + ) + endif () endif () mark_as_advanced(OPENSSL_INCLUDE_DIR OPENSSL_LIBRARIES OPENSSL_SEARCH_DIRS) diff --git a/cmake/templates/FixupBundlePostBuild.cmake.in b/cmake/templates/FixupBundlePostBuild.cmake.in index ee686a6967..6e3ad9723b 100644 --- a/cmake/templates/FixupBundlePostBuild.cmake.in +++ b/cmake/templates/FixupBundlePostBuild.cmake.in @@ -53,4 +53,4 @@ else() endif() file(GLOB RUNTIME_PLUGINS "${BUNDLE_PLUGIN_DIR}/*.${PLUGIN_EXTENSION}") -fixup_bundle("${BUNDLE_EXECUTABLE}" "${RUNTIME_PLUGINS}" "@FIXUP_LIBS@") \ No newline at end of file +fixup_bundle("${BUNDLE_EXECUTABLE}" "${RUNTIME_PLUGINS}" "@FIXUP_LIBS@") diff --git a/domain-server/CMakeLists.txt b/domain-server/CMakeLists.txt index 1f9280a899..2200916765 100644 --- a/domain-server/CMakeLists.txt +++ b/domain-server/CMakeLists.txt @@ -38,3 +38,4 @@ endif (UNIX) include_application_version() package_libraries_for_deployment() +consolidate_stack_components() \ No newline at end of file diff --git a/examples/away.js b/examples/away.js index 8cc3790200..0a86815d68 100644 --- a/examples/away.js +++ b/examples/away.js @@ -14,7 +14,6 @@ // Goes into "paused" when the '.' key (and automatically when started in HMD), and normal when pressing any key. // See MAIN CONTROL, below, for what "paused" actually does. -var IK_WINDOW_AFTER_GOING_ACTIVE = 3000; // milliseconds var OVERLAY_DATA = { text: "Paused:\npress any key to continue", font: {size: 75}, @@ -31,7 +30,6 @@ function playAwayAnimation() { return {isAway: true, isNotAway: false, isNotMoving: false, ikOverlayAlpha: 0.0}; } if (stopper) { - Script.clearTimeout(stopper); stopper = false; MyAvatar.removeAnimationStateHandler(activeAnimationHandlerId); // do it now, before making new assignment } @@ -47,15 +45,14 @@ function stopAwayAnimation() { // It cannot be as soon as we want to stop the away animation, because then things will look goofy as we come out of that animation. // (Imagine an away animation that sits or kneels, and then stands back up when coming out of it. If head is at the HMD, then it won't // want to track the standing up animation.) - // Our standard anim graph flips 'awayOutroOnDone' for one frame, but it's a trigger (not an animVar) and other folks might use different graphs. - // So... Just give us a fixed amount of time to be done with animation, before we turn ik back on. + // The anim graph will trigger awayOutroOnDone when awayOutro is finished. var backToNormal = false; - stopper = Script.setTimeout(function () { - backToNormal = true; - stopper = false; - }, IK_WINDOW_AFTER_GOING_ACTIVE); + stopper = true; function animateActive(state) { - if (state.ikOverlayAlpha) { + if (state.awayOutroOnDone) { + backToNormal = true; + stopper = false; + } else if (state.ikOverlayAlpha) { // Once the right state gets reflected back to us, we don't need the hander any more. // But we are locked against handler changes during the execution of a handler, so remove asynchronously. Script.setTimeout(function () { MyAvatar.removeAnimationStateHandler(activeAnimationHandlerId); }, 0); @@ -63,7 +60,7 @@ function stopAwayAnimation() { // It might be cool to "come back to life" by fading the ik overlay back in over a short time. But let's see how this goes. return {isAway: false, isNotAway: true, ikOverlayAlpha: backToNormal ? 1.0 : 0.0}; // IWBNI we had a way of deleting an anim var. } - activeAnimationHandlerId = MyAvatar.addAnimationStateHandler(animateActive, ['isAway', 'isNotAway', 'isNotMoving', 'ikOverlayAlpha']); + activeAnimationHandlerId = MyAvatar.addAnimationStateHandler(animateActive, ['ikOverlayAlpha', 'awayOutroOnDone']); } // OVERLAY diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index 7976e5286f..937af02973 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -202,31 +202,41 @@ function entityIsGrabbedByOther(entityID) { } function getSpatialOffsetPosition(hand, spatialKey) { + var position = Vec3.ZERO; + if (hand !== RIGHT_HAND && spatialKey.leftRelativePosition) { - return spatialKey.leftRelativePosition; + position = spatialKey.leftRelativePosition; } if (hand === RIGHT_HAND && spatialKey.rightRelativePosition) { - return spatialKey.rightRelativePosition; + position = spatialKey.rightRelativePosition; } if (spatialKey.relativePosition) { - return spatialKey.relativePosition; + position = spatialKey.relativePosition; } - return Vec3.ZERO; + return position; } +var yFlip = Quat.angleAxis(180, Vec3.UNIT_Y); function getSpatialOffsetRotation(hand, spatialKey) { + var rotation = Quat.IDENTITY; + if (hand !== RIGHT_HAND && spatialKey.leftRelativeRotation) { - return spatialKey.leftRelativeRotation; + rotation = spatialKey.leftRelativeRotation; } if (hand === RIGHT_HAND && spatialKey.rightRelativeRotation) { - return spatialKey.rightRelativeRotation; + rotation = spatialKey.rightRelativeRotation; } if (spatialKey.relativeRotation) { - return spatialKey.relativeRotation; + rotation = spatialKey.relativeRotation; } - return Quat.IDENTITY; + // Flip left hand + if (hand !== RIGHT_HAND) { + rotation = Quat.multiply(yFlip, rotation); + } + + return rotation; } function MyController(hand) { @@ -254,6 +264,7 @@ function MyController(hand) { this.overlayLine = null; + this.ignoreIK = false; this.offsetPosition = Vec3.ZERO; this.offsetRotation = Quat.IDENTITY; @@ -853,9 +864,12 @@ function MyController(hand) { if (this.state != STATE_NEAR_GRABBING && grabbableData.spatialKey) { // if an object is "equipped" and has a spatialKey, use it. + this.ignoreIK = grabbableData.spatialKey.ignoreIK ? grabbableData.spatialKey.ignoreIK : false; this.offsetPosition = getSpatialOffsetPosition(this.hand, grabbableData.spatialKey); this.offsetRotation = getSpatialOffsetRotation(this.hand, grabbableData.spatialKey); } else { + this.ignoreIK = false; + var objectRotation = grabbedProperties.rotation; this.offsetRotation = Quat.multiply(Quat.inverse(handRotation), objectRotation); @@ -872,7 +886,8 @@ function MyController(hand) { relativeRotation: this.offsetRotation, ttl: ACTION_TTL, kinematic: NEAR_GRABBING_KINEMATIC, - kinematicSetVelocity: true + kinematicSetVelocity: true, + ignoreIK: this.ignoreIK }); if (this.actionID === NULL_ACTION_ID) { this.actionID = null; @@ -956,7 +971,8 @@ function MyController(hand) { relativeRotation: this.offsetRotation, ttl: ACTION_TTL, kinematic: NEAR_GRABBING_KINEMATIC, - kinematicSetVelocity: true + kinematicSetVelocity: true, + ignoreIK: this.ignoreIK }); this.actionTimeout = now + (ACTION_TTL * MSEC_PER_SEC); } @@ -981,6 +997,7 @@ function MyController(hand) { // use a spring to pull the object to where it will be when equipped var relativeRotation = getSpatialOffsetRotation(this.hand, grabbableData.spatialKey); var relativePosition = getSpatialOffsetPosition(this.hand, grabbableData.spatialKey); + var ignoreIK = grabbableData.spatialKey.ignoreIK ? grabbableData.spatialKey.ignoreIK : false; var handRotation = this.getHandRotation(); var handPosition = this.getHandPosition(); var targetRotation = Quat.multiply(handRotation, relativeRotation); @@ -995,7 +1012,8 @@ function MyController(hand) { linearTimeScale: EQUIP_SPRING_TIMEFRAME, targetRotation: targetRotation, angularTimeScale: EQUIP_SPRING_TIMEFRAME, - ttl: ACTION_TTL + ttl: ACTION_TTL, + ignoreIK: ignoreIK }); if (this.equipSpringID === NULL_ACTION_ID) { this.equipSpringID = null; @@ -1008,7 +1026,8 @@ function MyController(hand) { linearTimeScale: EQUIP_SPRING_TIMEFRAME, targetRotation: targetRotation, angularTimeScale: EQUIP_SPRING_TIMEFRAME, - ttl: ACTION_TTL + ttl: ACTION_TTL, + ignoreIK: ignoreIK }); } diff --git a/examples/drylake/createAvatarDetector.js b/examples/drylake/createAvatarDetector.js new file mode 100644 index 0000000000..51ca55248e --- /dev/null +++ b/examples/drylake/createAvatarDetector.js @@ -0,0 +1,55 @@ +// +// createAvatarDetector.js +// +// Created by James B. Pollack @imgntn on 12/7/2015 +// Copyright 2015 High Fidelity, Inc. +// +// Run this script if you want the rats to run away from you. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + + +var avatarDetector = null; + +function createAvatarDetector() { + + var detectorProperties = { + name: 'Hifi-Avatar-Detector', + type: 'Box', + position: MyAvatar.position, + dimensions: { + x: 1, + y: 2, + z: 1 + }, + collisionsWillMove: false, + ignoreForCollisions: true, + visible: false, + color: { + red: 255, + green: 0, + blue: 0 + } + } + + avatarDetector = Entities.addEntity(detectorProperties); +}; + +var updateAvatarDetector = function() { + // print('updating detector position' + JSON.stringify(MyAvatar.position)) + Entities.editEntity(avatarDetector, { + position: MyAvatar.position + }); + +}; + +var cleanup = function() { + Script.update.disconnect(updateAvatarDetector); + Entities.deleteEntity(avatarDetector); +} + +createAvatarDetector(); +Script.scriptEnding.connect(cleanup); +Script.update.connect(updateAvatarDetector); \ No newline at end of file diff --git a/examples/drylake/ratCreator.js b/examples/drylake/ratCreator.js new file mode 100644 index 0000000000..237b8ec32f --- /dev/null +++ b/examples/drylake/ratCreator.js @@ -0,0 +1,471 @@ +// +// ratCreator.js +// +// Created by James B. Pollack @imgntn on 12/7/2015 +// Copyright 2015 High Fidelity, Inc. +// +// This script spawns some rats that have simple steering behaviors applied to them. +// Run it in the 'drylake' environment, or adjust all object locations to match your scene. +// +// Steering bevhaviors from ratSteer.js: +// The rats will move from a spawning point toward their nest. +// They will avoid avoider blocks moving across the alley +// They will avoid avatars running createAvatarDetector.js +// +// 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('ratSteer.js'); +var steer = loadSteer(); +Script.include('../libraries/tween.js'); +var TWEEN = loadTween(); + +var USE_CONSTANT_SPAWNER = true; + +var RAT_SPAWNER_LOCATION = { + x: 1000.5, + y: 98, + z: 1040 +}; + +var RAT_NEST_LOCATION = { + x: 1003.5, + y: 99, + z: 964.2 +}; + +var RAT_DIMENSIONS = { + x: 0.22, + y: 0.32, + z: 1.14 +}; + +var RAT_MODEL_URL = 'http://hifi-content.s3.amazonaws.com/james/rat/models/rat_model.fbx'; +var RAT_IDLE_ANIMATION_URL = 'http://hifi-content.s3.amazonaws.com/james/rat/animations/idle.fbx'; +var RAT_WALKING_ANIMATION_URL = 'http://hifi-content.s3.amazonaws.com/james/rat/animations/walk.fbx'; +var RAT_RUNNING_ANIMATION_URL = 'http://hifi-content.s3.amazonaws.com/james/rat/animations/run.fbx'; +var RAT_DEATH_ANIMATION_URL = 'http://hifi-content.s3.amazonaws.com/james/rat/animations/death.fbx'; + +var RAT_IN_NEST_DISTANCE = 4; + +//how many milliseconds between rats +var RAT_SPAWN_RATE = 2500; + +var RAT_SOUND_URL = 'http://hifi-public.s3.amazonaws.com/sounds/Rats_Squeaks_Active.wav'; +var ratRunningSound = SoundCache.getSound(RAT_SOUND_URL); + +function playRatRunningAnimation(rat) { + var animationSettings = JSON.stringify({ + running: true + }); + Entities.editEntity(rat, { + animationURL: RAT_RUNNING_ANIMATION_URL, + animation: { + url: RAT_RUNNING_ANIMATION_URL, + running: true, + fps: 30 + }, + }); +} + +function playRatDeathAnimation(rat) { + var animationSettings = JSON.stringify({ + running: true + }); + + Entities.editEntity(rat, { + animationURL: RAT_DEATH_ANIMATION_URL, + animationSettings: animationSettings + }); +} + +var modelRatProperties = { + name: 'rat', + type: 'Model', + modelURL: RAT_MODEL_URL, + dimensions: RAT_DIMENSIONS, + position: RAT_SPAWNER_LOCATION, + shapeType: 'Box', + damping: 0.8, + angularDamping: 0.99, + friction: 0.75, + collisionsWillMove: true, + ignoreForCollisions: false, + gravity: { + x: 0, + y: -9.8, + z: 0 + }, + lifetime: 30, + userData: JSON.stringify({ + grabbableKey: { + grabbable: false + } + }) + +}; + +var targetProperties = { + name: 'Hifi-Rat-Nest', + type: 'Box', + color: { + red: 0, + green: 255, + blue: 0 + }, + dimensions: { + x: 1, + y: 1, + z: 1 + }, + visible: false, + position: RAT_NEST_LOCATION +}; + +var target = Entities.addEntity(targetProperties); + +function addRat() { + var rat = Entities.addEntity(modelRatProperties); + return rat; +} + +//every sixth rat will play a sound +var RAT_SOUND_RATE = 6; + +//spawn rate will be multiplied by this to clear any sounds hanging around +var RAT_SOUND_CLEAR_RATE = 3; + +var rats = []; +var metaRats = []; +var ratCount = 0; + +var AVOIDER_Y_HEIGHT = 99; +var FIRST_AVOIDER_START_POSITION = { + x: 1004, + y: AVOIDER_Y_HEIGHT, + z: 1019 +}; +var FIRST_AVOIDER_FINISH_POSITION = { + x: 997, + y: AVOIDER_Y_HEIGHT, + z: 1019 +}; +var SECOND_AVOIDER_START_POSITION = { + x: 998, + y: AVOIDER_Y_HEIGHT, + z: 998 +}; +var SECOND_AVOIDER_FINISH_POSITION = { + x: 1005, + y: AVOIDER_Y_HEIGHT, + z: 999 +}; +var THIRD_AVOIDER_START_POSITION = { + x: 1001.5, + y: 100, + z: 978 +}; +var THIRD_AVOIDER_FINISH_POSITION = { + x: 1005, + y: 100, + z: 974 +}; + +cleanupLeftoverAvoidersBeforeStart(); + +var avoiders = []; +addAvoiderBlock(FIRST_AVOIDER_START_POSITION); +addAvoiderBlock(SECOND_AVOIDER_START_POSITION); +addAvoiderBlock(THIRD_AVOIDER_START_POSITION); + +function addAvoiderBlock(position) { + var avoiderProperties = { + name: 'Hifi-Rat-Avoider', + type: 'Box', + color: { + red: 255, + green: 0, + blue: 255 + }, + dimensions: { + x: 1, + y: 1, + z: 1 + }, + position: position, + collisionsWillMove: false, + ignoreForCollisions: true, + visible: false + }; + + var avoider = Entities.addEntity(avoiderProperties); + avoiders.push(avoider); +} + + +tweenAvoider(avoiders[0], FIRST_AVOIDER_START_POSITION, FIRST_AVOIDER_FINISH_POSITION); +tweenAvoider(avoiders[1], SECOND_AVOIDER_START_POSITION, SECOND_AVOIDER_FINISH_POSITION); +tweenAvoider(avoiders[2], THIRD_AVOIDER_START_POSITION, THIRD_AVOIDER_FINISH_POSITION); + +function tweenAvoider(entityID, startPosition, endPosition) { + var ANIMATION_DURATION = 4200; + + var begin = { + x: startPosition.x, + y: startPosition.y, + z: startPosition.z + }; + + var end = endPosition; + + var original = startPosition; + + var tweenHead = new TWEEN.Tween(begin).to(end, ANIMATION_DURATION); + + function updateTo() { + Entities.editEntity(entityID, { + position: { + x: begin.x, + y: begin.y, + z: begin.z + } + }); + } + + function updateBack() { + Entities.editEntity(entityID, { + position: { + x: begin.x, + y: begin.y, + z: begin.z + } + }) + } + + var tweenBack = new TWEEN.Tween(begin).to(original, ANIMATION_DURATION).onUpdate(updateBack); + + tweenHead.onUpdate(function() { + updateTo() + }); + + tweenHead.chain(tweenBack); + tweenBack.chain(tweenHead); + tweenHead.start(); + +} + +function updateTweens() { + TWEEN.update(); +} + +function createRatSoundInjector() { + var audioOptions = { + volume: 0.05, + loop: false + }; + + var injector = Audio.playSound(ratRunningSound, audioOptions); + + return injector; +} + +function moveRats() { + rats.forEach(function(rat) { + + //remove the rat if its near the nest + checkDistanceFromNest(rat); + + //see if there are avatars to run from + var avatarFlightVectors = steer.fleeAllAvatars(rat); + var averageAvatarFlight; + var i; + for (i = 0; i < avatarFlightVectors.length; i++) { + if (i === 0) { + averageAvatarFlight = avatarFlightVectors[0]; + } else { + averageAvatarFlight = Vec3.sum(avatarFlightVectors[i - 1], avatarFlightVectors[i]); + } + } + averageAvatarFlight = Vec3.multiply(averageAvatarFlight, 1 / avatarFlightVectors.length); + + //see if there are avoiders to flee + var avoidBlockVectors = steer.fleeAvoiderBlocks(rat); + var averageAvoiderFlight; + var j; + for (j = 0; j < avoidBlockVectors.length; j++) { + if (j === 0) { + averageAvoiderFlight = avoidBlockVectors[0]; + } else { + averageAvoiderFlight = Vec3.sum(avoidBlockVectors[j - 1], avoidBlockVectors[j]); + } + }; + averageAvoiderFlight = Vec3.multiply(averageAvoiderFlight, 1 / avoidBlockVectors.length); + + //add all of the vectors and divide them by total to get average vector + //start by trying to go toward the nest + var seek = steer.arrive(rat, target); + var averageVector = seek; + var divisorCount = 1; + + //if there are avatars to run away from + if (avatarFlightVectors.length > 0) { + divisorCount++; + averageVector = Vec3.sum(averageVector, averageAvatarFlight); + } + + //or if there are avoider blocks to run away from + if (avoidBlockVectors.length > 0) { + divisorCount++; + averageVector = Vec3.sum(averageVector, averageAvoiderFlight); + } + + averageVector = Vec3.multiply(averageVector, 1 / divisorCount); + + var thisRatProps = Entities.getEntityProperties(rat, ["position", "rotation"]); + var ratPosition = thisRatProps.position; + var ratToNest = Vec3.subtract(RAT_NEST_LOCATION, ratPosition); + var ratRotation = Quat.rotationBetween(Vec3.UNIT_Z, ratToNest); + var eulerAngle = Quat.safeEulerAngles(ratRotation); + eulerAngle.x = 0; + eulerAngle.z = 0; + var constrainedRotation = Quat.fromVec3Degrees(eulerAngle); + + Entities.editEntity(rat, { + velocity: averageVector, + rotation: constrainedRotation, + }); + + //have to make a 'meta' rat object to keep track of rats for updating sound injector locations. parenting sounds would make this easy. + var metaRat = getMetaRatByRat(rat); + if (metaRat !== undefined) { + if (metaRat.injector !== undefined) { + if (metaRat.injector.isPlaying === true) { + metaRat.injector.options = { + loop: true, + position: ratPosition + }; + } + } + + } + }) +} + +Script.update.connect(moveRats) +Script.update.connect(updateTweens); + +function checkDistanceFromNest(rat) { + var ratProps = Entities.getEntityProperties(rat, "position"); + var distance = Vec3.distance(ratProps.position, RAT_NEST_LOCATION); + if (distance < RAT_IN_NEST_DISTANCE) { + //at nest + removeRatFromScene(rat); + } +} + +function removeRatFromScene(rat) { + + var index = rats.indexOf(rat); + if (index > -1) { + rats.splice(index, 1); + Entities.deleteEntity(rat); + + } + + var metaRatIndex = findWithAttr(metaRats, 'rat', rat); + if (metaRatIndex > -1) { + metaRats[index].injector.stop(); + metaRats.splice(index, 1); + } + +} + +function popRatFromStack(entityID) { + + var index = rats.indexOf(entityID); + if (index > -1) { + rats.splice(index, 1); + } + + var metaRatIndex = findWithAttr(metaRats, 'rat', entityID); + if (metaRatIndex > -1) { + metaRats[index].injector.stop(); + metaRats.splice(index, 1); + } + +} + +function findWithAttr(array, attr, value) { + for (var i = 0; i < array.length; i += 1) { + if (array[i][attr] === value) { + return i; + } + } +} + +function getMetaRatByRat(rat) { + var result = metaRats.filter(function(metaRat) { + return rat === metaRat.rat; + }); + return result[0]; +} + +Entities.deletingEntity.connect(popRatFromStack); + + +function cleanupLeftoverAvoidersBeforeStart() { + //sometimes if we crash or something there could be extra avoider blocks around. clear them out. + var nearbyEntities = Entities.findEntities(RAT_SPAWNER_LOCATION, 100); + var entityIndex; + for (entityIndex = 0; entityIndex < nearbyEntities.length; entityIndex++) { + var entityID = nearbyEntities[entityIndex]; + var entityProps = Entities.getEntityProperties(entityID); + if (entityProps.name === 'Hifi-Rat-Avoider') { + Entities.deleteEntity(entityID); + } + } +} + +function cleanup() { + while (rats.length > 0) { + Entities.deleteEntity(rats.pop()); + } + + while (avoiders.length > 0) { + Entities.deleteEntity(avoiders.pop()); + } + + Entities.deleteEntity(target); + Script.update.disconnect(moveRats); + Script.update.disconnect(updateTweens); + Entities.deletingEntity.disconnect(popRatFromStack); + Script.clearInterval(ratSpawnerInterval); +} + +Script.scriptEnding.connect(cleanup); + +var ratSpawnerInterval; + +if (USE_CONSTANT_SPAWNER === true) { + ratSpawnerInterval = Script.setInterval(function() { + var rat = addRat(); + playRatRunningAnimation(rat); + rats.push(rat); + ratCount++; + if (ratCount % RAT_SOUND_RATE === 0) { + var metaRat = { + rat: rat, + injector: createRatSoundInjector() + } + metaRats.push(metaRat); + + Script.setTimeout(function() { + //if we have too many injectors hanging around there are problems + metaRat.injector.stop(); + delete metaRat.injector; + }, RAT_SPAWN_RATE * RAT_SOUND_CLEAR_RATE; + } + + }, RAT_SPAWN_RATE); + } \ No newline at end of file diff --git a/examples/drylake/ratSteer.js b/examples/drylake/ratSteer.js new file mode 100644 index 0000000000..e9a2647ff5 --- /dev/null +++ b/examples/drylake/ratSteer.js @@ -0,0 +1,183 @@ +// +// ratSteer.js +// +// Created by James B. Pollack @imgntn on 12/7/2015 +// Copyright 2015 High Fidelity, Inc. +// +// This is an example of steering behaviors that can be applied entities. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +function flee(thisEntity, target) { + var targetPosition = Entities.getEntityProperties(target, "position").position; + var properties = Entities.getEntityProperties(thisEntity, ["position", "velocity"]); + var location = properties.position; + var velocity = properties.velocity; + var MAX_SPEED = 1; + var MAX_FORCE = 1; + var FLEE_RANGE = 2; + var desired = Vec3.subtract(location, targetPosition); + var d = Vec3.length(desired); + desired = Vec3.normalize(desired); + desired = Vec3.multiply(MAX_SPEED, desired); + if (d < FLEE_RANGE) { + var steer = Vec3.subtract(desired, velocity); + + var steerVector = new V3(desired.x, 0, desired.z); + steer = steerVector.limit(MAX_FORCE); + + return steer; + } else { + //target too far away to flee + return + } +} + +function fleeAllAvatars(thisEntity) { + //print('FLEE AVATARS'); + var properties = Entities.getEntityProperties(thisEntity, ["position", "velocity"]); + var location = properties.position; + var velocity = properties.velocity; + + var nearbyEntities = Entities.findEntities(location, 3); + var flightVectors = []; + for (var entityIndex = 0; entityIndex < nearbyEntities.length; entityIndex++) { + var entityID = nearbyEntities[entityIndex]; + var entityProps = Entities.getEntityProperties(entityID); + if (entityProps.name === 'Hifi-Avatar-Detector') { + //found an avatar to flee + + var MAX_SPEED = 8; + var MAX_FORCE = 8; + var FLEE_AVATAR_RANGE = 3; + var desired = Vec3.subtract(location, entityProps.position); + var d = Vec3.length(desired); + desired = Vec3.normalize(desired); + desired = Vec3.multiply(MAX_SPEED, desired); + if (d < FLEE_AVATAR_RANGE) { + var steer = Vec3.subtract(desired, velocity); + var steerVector = new V3(desired.x, 0, desired.z); + steer = steerVector.limit(MAX_FORCE); + flightVectors.push(steer); + } else { + // target too far away from this avatar to flee + } + } + + } + + return flightVectors; +} + +function fleeAvoiderBlocks(thisEntity) { + // print('FLEE AVOIDER BLOCKS'); + var properties = Entities.getEntityProperties(thisEntity, ["position", "velocity"]); + var location = properties.position; + var velocity = properties.velocity; + + var nearbyEntities = Entities.findEntities(location, 2); + var flightVectors = []; + for (var entityIndex = 0; entityIndex < nearbyEntities.length; entityIndex++) { + var entityID = nearbyEntities[entityIndex]; + var entityProps = Entities.getEntityProperties(entityID); + if (entityProps.name === 'Hifi-Rat-Avoider') { + //found an avoiderblock to flee + + var MAX_SPEED = 11; + var MAX_FORCE = 6; + var FLEE_AVOIDER_RANGE = 5; + + var desired = Vec3.subtract(location, entityProps.position); + var d = Vec3.length(desired); + desired = Vec3.normalize(desired); + desired = Vec3.multiply(MAX_SPEED, desired); + + if (d < FLEE_AVOIDER_RANGE) { + var steer = Vec3.subtract(desired, velocity); + var steerVector = new V3(desired.x, 0, desired.z); + steer = steerVector.limit(MAX_FORCE); + flightVectors.push(steer); + } else { + //target too far away from this avoider to flee + } + + } + + } + + return flightVectors; +} + +function arrive(thisEntity, target) { + + var targetPosition = Entities.getEntityProperties(target, "position").position; + var properties = Entities.getEntityProperties(thisEntity, ["position", "velocity"]); + var location = properties.position; + var velocity = properties.velocity; + var MAX_SPEED = 10; + var MAX_FORCE = 6; + var ARRIVAL_DISTANCE = 2; + + var desired = Vec3.subtract(targetPosition, location); + var d = Vec3.length(desired); + desired = Vec3.normalize(desired); + + if (d < ARRIVAL_DISTANCE) { + var m = scale(d, 0, ARRIVAL_DISTANCE, 0, MAX_SPEED); + } else { + desired = Vec3.multiply(MAX_SPEED, desired); + + } + + var steer = Vec3.subtract(desired, velocity); + var steerVector = new V3(desired.x, 0, desired.z); + steer = steerVector.limit(MAX_FORCE); + + return steer; +} + + +function V3(x, y, z) { + this.x = x; + this.y = y; + this.z = z; + return +} + +V3.prototype.length = function() { + return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z); +}; + +V3.prototype.limit = function(s) { + var len = this.length(); + + if (len > s && len > 0) { + this.scale(s / len); + } + + return this; +}; + +V3.prototype.scale = function(f) { + this.x *= f; + this.y *= f; + this.z *= f; + return this; +}; + +var v3 = new V3(); + +var scale = function(value, min1, max1, min2, max2) { + return min2 + (max2 - min2) * ((value - min1) / (max1 - min1)); +} + +loadSteer = function() { + return { + flee: flee, + fleeAllAvatars: fleeAllAvatars, + fleeAvoiderBlocks: fleeAvoiderBlocks, + arrive: arrive + }; +} \ No newline at end of file diff --git a/examples/html/entityProperties.html b/examples/html/entityProperties.html index 186b2ee828..ff72e95313 100644 --- a/examples/html/entityProperties.html +++ b/examples/html/entityProperties.html @@ -298,7 +298,6 @@ var elModelAnimationLastFrame = document.getElementById("property-model-animation-last-frame"); var elModelAnimationLoop = document.getElementById("property-model-animation-loop"); var elModelAnimationHold = document.getElementById("property-model-animation-hold"); - var elModelAnimationStartAutomatically = document.getElementById("property-model-animation-start-automatically"); var elModelTextures = document.getElementById("property-model-textures"); var elModelOriginalTextures = document.getElementById("property-model-original-textures"); @@ -532,7 +531,6 @@ elModelAnimationLastFrame.value = properties.animation.lastFrame; elModelAnimationLoop.checked = properties.animation.loop; elModelAnimationHold.checked = properties.animation.hold; - elModelAnimationStartAutomatically.checked = properties.animation.startAutomatically; elModelTextures.value = properties.textures; elModelOriginalTextures.value = properties.originalTextures; } else if (properties.type == "Web") { @@ -785,7 +783,6 @@ elModelAnimationLastFrame.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('animation', 'lastFrame')); elModelAnimationLoop.addEventListener('change', createEmitGroupCheckedPropertyUpdateFunction('animation', 'loop')); elModelAnimationHold.addEventListener('change', createEmitGroupCheckedPropertyUpdateFunction('animation', 'hold')); - elModelAnimationStartAutomatically.addEventListener('change', createEmitGroupCheckedPropertyUpdateFunction('animation', 'startAutomatically')); elModelTextures.addEventListener('change', createEmitTextPropertyUpdateFunction('textures')); @@ -1348,12 +1345,6 @@ -
- Animation Start Automatically - - - -
Textures
diff --git a/examples/particle_explorer/main.js b/examples/particle_explorer/main.js index 32ec12b640..a2a112d8e5 100644 --- a/examples/particle_explorer/main.js +++ b/examples/particle_explorer/main.js @@ -74,7 +74,9 @@ var keysToIgnore = [ 'shapeType', 'isEmitting', 'sittingPoints', - 'originalTextures' + 'originalTextures', + 'parentJointIndex', + 'parentID' ]; var individualKeys = []; diff --git a/examples/particle_explorer/particleExplorer.js b/examples/particle_explorer/particleExplorer.js index 16c75e18c6..0cc51c1a39 100644 --- a/examples/particle_explorer/particleExplorer.js +++ b/examples/particle_explorer/particleExplorer.js @@ -128,7 +128,6 @@ function setUp() { blue: 255 }, lifespan: 5.0, - visible: false, locked: false, isEmitting: true, lifetime: 3600 // 1 hour; just in case diff --git a/examples/toybox/bow/bow.js b/examples/toybox/bow/bow.js index 1ea3e39b02..44fe33bb31 100644 --- a/examples/toybox/bow/bow.js +++ b/examples/toybox/bow/bow.js @@ -66,21 +66,6 @@ max2: 15 } - var BOW_SPATIAL_KEY = { - leftRelativePosition: { - x: 0.05, - y: 0.06, - z: -0.05 - }, - rightRelativePosition: { - x: -0.05, - y: 0.06, - z: -0.05 - }, - relativeRotation: Quat.fromPitchYawRollDegrees(0, 90, -90) - } - - var USE_DEBOUNCE = false; var TRIGGER_CONTROLS = [ @@ -168,11 +153,9 @@ var handToDisable = this.initialHand === 'right' ? 'left' : 'right'; Messages.sendMessage('Hifi-Hand-Disabler', handToDisable); - setEntityCustomData('grabbableKey', this.entityID, { - grabbable: false, - invertSolidWhileHeld: true, - spatialKey: BOW_SPATIAL_KEY - }); + var data = getEntityCustomData('grabbableKey', this.entityID, {}); + data.grabbable = false; + setEntityCustomData('grabbableKey', this.entityID, data); }, continueNearGrab: function() { @@ -226,11 +209,10 @@ this.isGrabbed = false; this.stringDrawn = false; this.deleteStrings(); - setEntityCustomData('grabbableKey', this.entityID, { - grabbable: true, - invertSolidWhileHeld: true, - spatialKey: BOW_SPATIAL_KEY - }); + + var data = getEntityCustomData('grabbableKey', this.entityID, {}); + data.grabbable = true; + setEntityCustomData('grabbableKey', this.entityID, data); Entities.deleteEntity(this.preNotchString); Entities.deleteEntity(this.arrow); this.aiming = false; diff --git a/examples/toybox/bow/createBow.js b/examples/toybox/bow/createBow.js index 9a9ed98c20..4f0cddfc0d 100644 --- a/examples/toybox/bow/createBow.js +++ b/examples/toybox/bow/createBow.js @@ -49,14 +49,14 @@ var bow = Entities.addEntity({ invertSolidWhileHeld: true, spatialKey: { leftRelativePosition: { - x: 0.05, - y: 0.06, - z: -0.05 + x: -0.02, + y: 0.08, + z: 0.09 }, - rightRelativePosition: { - x: -0.05, - y: 0.06, - z: -0.05 + relativePosition: { + x: 0.02, + y: 0.08, + z: 0.09 }, relativeRotation: Quat.fromPitchYawRollDegrees(0, 90, -90) } diff --git a/examples/toybox/bubblewand/createWand.js b/examples/toybox/bubblewand/createWand.js index 6b4f9717ec..efd86b046f 100644 --- a/examples/toybox/bubblewand/createWand.js +++ b/examples/toybox/bubblewand/createWand.js @@ -53,8 +53,13 @@ var wand = Entities.addEntity({ y: 0.1, z: 0 }, - relativeRotation: Quat.fromPitchYawRollDegrees(0, 0, 90) + relativeRotation: Quat.fromPitchYawRollDegrees(0, 0, -90) } } }) -}); \ No newline at end of file +}); + +function scriptEnding() { + Entities.deleteEntity(wand); +} +Script.scriptEnding.connect(scriptEnding); diff --git a/examples/toybox/flashlight/flashlight.js b/examples/toybox/flashlight/flashlight.js index 8911c0ecd8..251ca71b7a 100644 --- a/examples/toybox/flashlight/flashlight.js +++ b/examples/toybox/flashlight/flashlight.js @@ -25,7 +25,7 @@ //we are creating lights that we don't want to get stranded so lets make sure that we can get rid of them var startTime = Date.now(); //if you're going to be using this in a dungeon or something and holding it for a long time, increase this lifetime value. - var LIFETIME = 25; + var LIFETIME = 100; var MSEC_PER_SEC = 1000.0; // this is the "constructor" for the entity as a JS object we don't do much here, but we do want to remember @@ -85,9 +85,14 @@ this.hand = 'LEFT'; }, - startNearGrab: function() { + startNearGrab: function(entityID) { if (!this.hasSpotlight) { + var modelProperties = Entities.getEntityProperties(this.entityID, ['position', 'rotation']); + var lightTransform = evalLightWorldTransform(modelProperties.position, modelProperties.rotation); + var glowLightTransform = glowLightWorldTransform(modelProperties.position, modelProperties.rotation); + + //this light casts the beam this.spotlight = Entities.addEntity({ type: "Light", @@ -97,6 +102,7 @@ y: 2, z: 20 }, + parentID: this.entityID, color: { red: 255, green: 255, @@ -105,7 +111,9 @@ intensity: 2, exponent: 0.3, cutoff: 20, - lifetime: LIFETIME + lifetime: LIFETIME, + position: lightTransform.p, + rotation: lightTransform.q, }); //this light creates the effect of a bulb at the end of the flashlight @@ -116,6 +124,7 @@ y: 0.25, z: 0.25 }, + parentID: this.entityID, isSpotlight: false, color: { red: 255, @@ -123,8 +132,11 @@ blue: 255 }, exponent: 0, + lifetime: LIFETIME, cutoff: 90, // in degrees - lifetime: LIFETIME + position: glowLightTransform.p, + rotation: glowLightTransform.q, + }); this.hasSpotlight = true; @@ -142,7 +154,6 @@ //only set the active hand once -- if we always read the current hand, our 'holding' hand will get overwritten this.setWhichHand(); } else { - this.updateLightPositions(); this.changeLightWithTriggerPressure(this.whichHand); } }, @@ -159,29 +170,7 @@ this.lightOn = false; } }, - - updateLightPositions: function() { - var modelProperties = Entities.getEntityProperties(this.entityID, ['position', 'rotation']); - - //move the two lights along the vectors we set above - var lightTransform = evalLightWorldTransform(modelProperties.position, modelProperties.rotation); - var glowLightTransform = glowLightWorldTransform(modelProperties.position, modelProperties.rotation); - - //move them with the entity model - Entities.editEntity(this.spotlight, { - position: lightTransform.p, - rotation: lightTransform.q, - lifetime: (Date.now() - startTime) / MSEC_PER_SEC + LIFETIME - }); - - Entities.editEntity(this.glowLight, { - position: glowLightTransform.p, - rotation: glowLightTransform.q, - lifetime: (Date.now() - startTime) / MSEC_PER_SEC + LIFETIME - }); - - }, - + changeLightWithTriggerPressure: function(flashLightHand) { if (flashLightHand === 'LEFT') { diff --git a/examples/toybox/pistol/createPistol.js b/examples/toybox/pistol/createPistol.js index d804150cbe..693c53b9b4 100644 --- a/examples/toybox/pistol/createPistol.js +++ b/examples/toybox/pistol/createPistol.js @@ -28,10 +28,10 @@ var pistol = Entities.addEntity({ spatialKey: { relativePosition: { x: 0, - y: 0, - z: 0 + y: 0.05, + z: -0.08 }, - relativeRotation: Quat.fromPitchYawRollDegrees(45, 90, 0) + relativeRotation: Quat.fromPitchYawRollDegrees(90, 90, 0) }, invertSolidWhileHeld: true } diff --git a/examples/utilities/tools/reverbTest.js b/examples/utilities/tools/reverbTest.js index 32c28a993f..a7a6bad9d7 100644 --- a/examples/utilities/tools/reverbTest.js +++ b/examples/utilities/tools/reverbTest.js @@ -12,31 +12,57 @@ Script.include("cookies.js"); var audioOptions = new AudioEffectOptions({ - maxRoomSize: 50, + bandwidth: 10000, + preDelay: 20, + lateDelay: 0, + reverbTime: 2, + earlyDiffusion: 100, + lateDiffusion: 100, roomSize: 50, - reverbTime: 4, - damping: 0.50, - inputBandwidth: 0.8, - earlyLevel: 0, - tailLevel: 0, - dryLevel: -6, - wetLevel: -6 + density: 100, + bassMult: 1.5, + bassFreq: 250, + highGain: -6, + highFreq: 3000, + modRate: 2.3, + modDepth: 50, + earlyGain: 0, + lateGain: 0, + earlyMixLeft: 20, + earlyMixRight: 20, + lateMixLeft: 90, + lateMixRight: 90, + wetDryMix: 50, }); AudioDevice.setReverbOptions(audioOptions); AudioDevice.setReverb(true); print("Reverb is ON."); -var panel = new Panel(10, 200); +var panel = new Panel(10, 160); var parameters = [ - { name: "roomSize", min: 0, max: 100, units: " feet" }, - { name: "reverbTime", min: 0, max: 10, units: " sec" }, - { name: "damping", min: 0, max: 1, units: " " }, - { name: "inputBandwidth", min: 0, max: 1, units: " " }, - { name: "earlyLevel", min: -48, max: 0, units: " dB" }, - { name: "tailLevel", min: -48, max: 0, units: " dB" }, - { name: "wetLevel", min: -48, max: 0, units: " dB" }, + { name: "bandwidth", min: 1000, max: 12000, units: " Hz" }, + { name: "preDelay", min: 0, max: 333, units: " ms" }, + { name: "lateDelay", min: 0, max: 166, units: " ms" }, + { name: "reverbTime", min: 0.1, max: 10, units: " seconds" }, + { name: "earlyDiffusion", min: 0, max: 100, units: " percent" }, + { name: "lateDiffusion", min: 0, max: 100, units: " percent" }, + { name: "roomSize", min: 0, max: 100, units: " percent" }, + { name: "density", min: 0, max: 100, units: " percent" }, + { name: "bassMult", min: 0.1, max: 4, units: " ratio" }, + { name: "bassFreq", min: 10, max: 500, units: " Hz" }, + { name: "highGain", min: -24, max: 0, units: " dB" }, + { name: "highFreq", min: 1000, max: 12000, units: " Hz" }, + { name: "modRate", min: 0.1, max: 10, units: " Hz" }, + { name: "modDepth", min: 0, max: 100, units: " percent" }, + { name: "earlyGain", min: -96, max: 24, units: " dB" }, + { name: "lateGain", min: -96, max: 24, units: " dB" }, + { name: "earlyMixLeft", min: 0, max: 100, units: " percent" }, + { name: "earlyMixRight", min: 0, max: 100, units: " percent" }, + { name: "lateMixLeft", min: 0, max: 100, units: " percent" }, + { name: "lateMixRight", min: 0, max: 100, units: " percent" }, + { name: "wetDryMix", min: 0, max: 100, units: " percent" }, ] function setter(name) { @@ -48,7 +74,7 @@ function getter(name) { } function displayer(units) { - return function(value) { return (value).toFixed(1) + units; }; + return function(value) { return (value).toFixed(1) + units; } } // create a slider for each parameter diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index 573fc1ce1e..87cf1a384c 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -100,6 +100,15 @@ else() add_executable(${TARGET_NAME} ${INTERFACE_SRCS} ${QM}) endif() +# These are external plugins, but we need to do the 'add dependency' here so that their +# binary directories get added to the fixup path +add_dependency_external_projects(sixense) +add_dependency_external_projects(sdl2) +if (WIN32) + add_dependency_external_projects(OpenVR) +endif() + + # disable /OPT:REF and /OPT:ICF for the Debug builds # This will prevent the following linker warnings # LINK : warning LNK4075: ignoring '/INCREMENTAL' due to '/OPT:ICF' specification @@ -194,7 +203,6 @@ 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) else (WIN32) # Nothing else required on linux apparently @@ -202,3 +210,4 @@ else (APPLE) endif (APPLE) package_libraries_for_deployment() +consolidate_stack_components() diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index c0cfbda15b..2b1644e3ca 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -68,7 +68,6 @@ #include #include #include -#include // this should probably be removed #include #include #include @@ -195,6 +194,8 @@ static const QString INFO_EDIT_ENTITIES_PATH = "html/edit-commands.html"; static const unsigned int THROTTLED_SIM_FRAMERATE = 15; static const int THROTTLED_SIM_FRAME_PERIOD_MS = MSECS_PER_SECOND / THROTTLED_SIM_FRAMERATE; +static const float PHYSICS_READY_RANGE = 3.0f; // how far from avatar to check for entities that aren't ready for simulation + #ifndef __APPLE__ static const QString DESKTOP_LOCATION = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation); #else @@ -350,6 +351,7 @@ bool setupEssentials(int& argc, char** argv) { DependencyManager::set(); DependencyManager::set(); DependencyManager::set(); + DependencyManager::set(true, qApp, qApp); return true; } @@ -369,7 +371,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : _frameCount(0), _fps(60.0f), _physicsEngine(new PhysicsEngine(Vectors::ZERO)), - _entities(true, this, this), _entityClipboardRenderer(false, this, this), _entityClipboard(new EntityTree()), _lastQueriedTime(usecTimestampNow()), @@ -862,7 +863,7 @@ void Application::cleanupBeforeQuit() { } _keyboardFocusHighlight = nullptr; - _entities.clear(); // this will allow entity scripts to properly shutdown + getEntities()->clear(); // this will allow entity scripts to properly shutdown auto nodeList = DependencyManager::get(); @@ -873,7 +874,7 @@ void Application::cleanupBeforeQuit() { // tell the packet receiver we're shutting down, so it can drop packets nodeList->getPacketReceiver().setShouldDropPackets(true); - _entities.shutdown(); // tell the entities system we're shutting down, so it will stop running scripts + getEntities()->shutdown(); // tell the entities system we're shutting down, so it will stop running scripts ScriptEngine::stopAllScripts(this); // stop all currently running global scripts // first stop all timers directly or by invokeMethod @@ -919,7 +920,7 @@ void Application::emptyLocalCache() { } Application::~Application() { - EntityTreePointer tree = _entities.getTree(); + EntityTreePointer tree = getEntities()->getTree(); tree->setSimulation(NULL); _octreeProcessor.terminate(); @@ -1122,29 +1123,6 @@ void Application::paintGL() { _inPaint = true; Finally clearFlagLambda([this] { _inPaint = false; }); - // Some LOD-like controls need to know a smoothly varying "potential" frame rate that doesn't - // include time waiting for vsync, and which can report a number above target if we've got the headroom. - // For example, if we're shooting for 75fps and paintWait is 3.3333ms (= 75% * 13.33ms), our deducedNonVSyncFps - // would be 100fps. In principle, a paintWait of zero would have deducedNonVSyncFps=75. - // Here we make a guess for deducedNonVSyncFps = 1 / deducedNonVSyncPeriod. - // - // Time between previous paintGL call and this one, which can vary not only with vSync misses, but also with QT timing. - // We're using this as a proxy for the time between vsync and displayEnd, below. (Not exact, but tends to be the same over time.) - // This is not the same as update(deltaTime), because the latter attempts to throttle to 60hz and also clamps to 1/4 second. - const float actualPeriod = diff / (float)USECS_PER_SECOND; // same as 1/instantaneousFps but easier for compiler to optimize - // Note that _lastPaintWait (stored at end of last call) is for the same paint cycle. - float deducedNonVSyncPeriod = actualPeriod - _lastPaintWait + _marginForDeducedFramePeriod; // plus a some non-zero time for machinery we can't measure - // We don't know how much time to allow for that, but if we went over the target period, we know it's at least the portion - // of paintWait up to the next vSync. This gives us enough of a penalty so that when actualPeriod crosses two cycles, - // the key part (and not an exagerated part) of _lastPaintWait is accounted for. - const float targetPeriod = getTargetFramePeriod(); - if (_lastPaintWait > EPSILON && actualPeriod > targetPeriod) { - // Don't use C++ remainder(). It's authors are mathematically insane. - deducedNonVSyncPeriod += fmod(actualPeriod, _lastPaintWait); - } - _lastDeducedNonVSyncFps = 1.0f / deducedNonVSyncPeriod; - _lastInstantaneousFps = instantaneousFps; - auto displayPlugin = getActiveDisplayPlugin(); // FIXME not needed anymore? _offscreenContext->makeCurrent(); @@ -1401,7 +1379,6 @@ void Application::paintGL() { Q_ASSERT(!_lockedFramebufferMap.contains(finalTexture)); _lockedFramebufferMap[finalTexture] = scratchFramebuffer; - uint64_t displayStart = usecTimestampNow(); Q_ASSERT(isCurrentContext(_offscreenContext->getContext())); { PROFILE_RANGE(__FUNCTION__ "/pluginSubmitScene"); @@ -1410,9 +1387,6 @@ void Application::paintGL() { } Q_ASSERT(isCurrentContext(_offscreenContext->getContext())); - uint64_t displayEnd = usecTimestampNow(); - const float displayPeriodUsec = (float)(displayEnd - displayStart); // usecs - _lastPaintWait = displayPeriodUsec / (float)USECS_PER_SECOND; } { @@ -1423,6 +1397,14 @@ void Application::paintGL() { batch.resetStages(); }); } + + // Some LOD-like controls need to know a smoothly varying "potential" frame rate that doesn't + // include time waiting for sync, and which can report a number above target if we've got the headroom. + // In my tests, the following is mostly less than 0.5ms, and never more than 3ms. I don't think its worth measuring during runtime. + const float paintWaitAndQTTimerAllowance = 0.001f; // seconds + // Store both values now for use by next cycle. + _lastInstantaneousFps = instantaneousFps; + _lastUnsynchronizedFps = 1.0f / (((usecTimestampNow() - now) / (float)USECS_PER_SECOND) + paintWaitAndQTTimerAllowance); } void Application::runTests() { @@ -1993,7 +1975,7 @@ void Application::mouseMoveEvent(QMouseEvent* event, unsigned int deviceID) { event->buttons(), event->modifiers()); - _entities.mouseMoveEvent(&mappedEvent, deviceID); + getEntities()->mouseMoveEvent(&mappedEvent, deviceID); _controllerScriptingInterface->emitMouseMoveEvent(&mappedEvent, deviceID); // send events to any registered scripts // if one of our scripts have asked to capture this event, then stop processing it @@ -2019,7 +2001,7 @@ void Application::mousePressEvent(QMouseEvent* event, unsigned int deviceID) { event->buttons(), event->modifiers()); if (!_aboutToQuit) { - _entities.mousePressEvent(&mappedEvent, deviceID); + getEntities()->mousePressEvent(&mappedEvent, deviceID); } _controllerScriptingInterface->emitMousePressEvent(&mappedEvent); // send events to any registered scripts @@ -2064,7 +2046,7 @@ void Application::mouseReleaseEvent(QMouseEvent* event, unsigned int deviceID) { event->buttons(), event->modifiers()); if (!_aboutToQuit) { - _entities.mouseReleaseEvent(&mappedEvent, deviceID); + getEntities()->mouseReleaseEvent(&mappedEvent, deviceID); } _controllerScriptingInterface->emitMouseReleaseEvent(&mappedEvent); // send events to any registered scripts @@ -2387,7 +2369,7 @@ void Application::calibrateEyeTracker5Points() { bool Application::exportEntities(const QString& filename, const QVector& entityIDs) { QVector entities; - auto entityTree = _entities.getTree(); + auto entityTree = getEntities()->getTree(); auto exportTree = std::make_shared(); exportTree->createRootElement(); @@ -2431,7 +2413,7 @@ bool Application::exportEntities(const QString& filename, const QVector entities; - _entities.getTree()->findEntities(AACube(glm::vec3(x, y, z), scale), entities); + getEntities()->getTree()->findEntities(AACube(glm::vec3(x, y, z), scale), entities); if (entities.size() > 0) { glm::vec3 root(x, y, z); @@ -2499,7 +2481,7 @@ bool Application::importEntities(const QString& urlOrFilename) { } QVector Application::pasteEntities(float x, float y, float z) { - return _entityClipboard->sendEntities(&_entityEditSender, _entities.getTree(), x, y, z); + return _entityClipboard->sendEntities(&_entityEditSender, getEntities()->getTree(), x, y, z); } void Application::initDisplay() { @@ -2538,13 +2520,13 @@ void Application::init() { // fire off an immediate domain-server check in now that settings are loaded DependencyManager::get()->sendDomainServerCheckIn(); - _entities.init(); - _entities.setViewFrustum(getViewFrustum()); + getEntities()->init(); + getEntities()->setViewFrustum(getViewFrustum()); ObjectMotionState::setShapeManager(&_shapeManager); _physicsEngine->init(); - EntityTreePointer tree = _entities.getTree(); + EntityTreePointer tree = getEntities()->getTree(); _entitySimulation.init(tree, _physicsEngine, &_entityEditSender); tree->setSimulation(&_entitySimulation); @@ -2552,11 +2534,11 @@ void Application::init() { // connect the _entityCollisionSystem to our EntityTreeRenderer since that's what handles running entity scripts connect(&_entitySimulation, &EntitySimulation::entityCollisionWithEntity, - &_entities, &EntityTreeRenderer::entityCollisionWithEntity); + getEntities(), &EntityTreeRenderer::entityCollisionWithEntity); // connect the _entities (EntityTreeRenderer) to our script engine's EntityScriptingInterface for firing // of events related clicking, hovering over, and entering entities - _entities.connectSignalsToSlots(entityScriptingInterface.data()); + getEntities()->connectSignalsToSlots(entityScriptingInterface.data()); _entityClipboardRenderer.init(); _entityClipboardRenderer.setViewFrustum(getViewFrustum()); @@ -2781,6 +2763,8 @@ void Application::reloadResourceCaches() { DependencyManager::get()->refreshAll(); DependencyManager::get()->reset(); // Force redownload of .fst models + + getMyAvatar()->resetFullAvatarURL(); } void Application::rotationModeChanged() { @@ -2905,19 +2889,19 @@ void Application::update(float deltaTime) { _avatarUpdate->synchronousProcess(); - { + if (_physicsEnabled) { PerformanceTimer perfTimer("physics"); static VectorOfMotionStates motionStates; _entitySimulation.getObjectsToDelete(motionStates); _physicsEngine->deleteObjects(motionStates); - _entities.getTree()->withWriteLock([&] { + getEntities()->getTree()->withWriteLock([&] { _entitySimulation.getObjectsToAdd(motionStates); _physicsEngine->addObjects(motionStates); }); - _entities.getTree()->withWriteLock([&] { + getEntities()->getTree()->withWriteLock([&] { _entitySimulation.getObjectsToChange(motionStates); VectorOfMotionStates stillNeedChange = _physicsEngine->changeObjects(motionStates); _entitySimulation.setObjectsToChange(stillNeedChange); @@ -2935,12 +2919,12 @@ void Application::update(float deltaTime) { myAvatar->prepareForPhysicsSimulation(); - _entities.getTree()->withWriteLock([&] { + getEntities()->getTree()->withWriteLock([&] { _physicsEngine->stepSimulation(); }); if (_physicsEngine->hasOutgoingChanges()) { - _entities.getTree()->withWriteLock([&] { + getEntities()->getTree()->withWriteLock([&] { _entitySimulation.handleOutgoingChanges(_physicsEngine->getOutgoingChanges(), _physicsEngine->getSessionID()); avatarManager->handleOutgoingChanges(_physicsEngine->getOutgoingChanges()); }); @@ -2955,12 +2939,13 @@ void Application::update(float deltaTime) { // Collision events (and their scripts) must not be handled when we're locked, above. (That would risk // deadlock.) _entitySimulation.handleCollisionEvents(collisionEvents); - // NOTE: the _entities.update() call below will wait for lock + // NOTE: the getEntities()->update() call below will wait for lock // and will simulate entity motion (the EntityTree has been given an EntitySimulation). - _entities.update(); // update the models... + getEntities()->update(); // update the models... } myAvatar->harvestResultsFromPhysicsSimulation(); + myAvatar->simulateAttachments(deltaTime); } } @@ -3093,6 +3078,7 @@ void Application::queryOctree(NodeType_t serverType, PacketType packetType, Node _octreeQuery.setCameraNearClip(_viewFrustum.getNearClip()); _octreeQuery.setCameraFarClip(_viewFrustum.getFarClip()); _octreeQuery.setCameraEyeOffsetPosition(glm::vec3()); + _octreeQuery.setKeyholeRadius(_viewFrustum.getKeyholeRadius()); auto lodManager = DependencyManager::get(); _octreeQuery.setOctreeSizeScale(lodManager->getOctreeSizeScale()); _octreeQuery.setBoundaryLevelAdjust(lodManager->getBoundaryLevelAdjust()); @@ -3253,6 +3239,7 @@ void Application::queryOctree(NodeType_t serverType, PacketType packetType, Node bool Application::isHMDMode() const { return getActiveDisplayPlugin()->isHmd(); } +float Application::getTargetFrameRate() { return getActiveDisplayPlugin()->getTargetFrameRate(); } QRect Application::getDesirableApplicationGeometry() { QRect applicationGeometry = getWindow()->geometry(); @@ -3444,10 +3431,10 @@ namespace render { // Background rendering decision auto skyStage = DependencyManager::get()->getSkyStage(); - auto skybox = model::SkyboxPointer(); if (skyStage->getBackgroundMode() == model::SunSkyStage::NO_BACKGROUND) { + // this line intentionally left blank } else if (skyStage->getBackgroundMode() == model::SunSkyStage::SKY_DOME) { - if (/*!selfAvatarOnly &&*/ Menu::getInstance()->isOptionChecked(MenuOption::Stars)) { + if (Menu::getInstance()->isOptionChecked(MenuOption::Stars)) { PerformanceTimer perfTimer("stars"); PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "Application::payloadRender() ... stars..."); @@ -3513,10 +3500,9 @@ namespace render { } } else if (skyStage->getBackgroundMode() == model::SunSkyStage::SKY_BOX) { PerformanceTimer perfTimer("skybox"); - - skybox = skyStage->getSkybox(); + auto skybox = skyStage->getSkybox(); if (skybox) { - skybox->render(batch, *(qApp->getDisplayViewFrustum())); + skybox->render(batch, *(args->_viewFrustum)); } } } @@ -3779,13 +3765,19 @@ void Application::clearDomainOctreeDetails() { }); // reset the model renderer - _entities.clear(); + getEntities()->clear(); + + auto skyStage = DependencyManager::get()->getSkyStage(); + skyStage->setBackgroundMode(model::SunSkyStage::SKY_DOME); + } void Application::domainChanged(const QString& domainHostname) { updateWindowTitle(); clearDomainOctreeDetails(); _domainConnectionRefusals.clear(); + // disable physics until we have enough information about our new location to not cause craziness. + _physicsEnabled = false; } void Application::handleDomainConnectionDeniedPacket(QSharedPointer message) { @@ -3893,6 +3885,34 @@ void Application::trackIncomingOctreePacket(ReceivedMessage& message, SharedNode } } +bool Application::nearbyEntitiesAreReadyForPhysics() { + // this is used to avoid the following scenario: + // A table has some items sitting on top of it. The items are at rest, meaning they aren't active in bullet. + // Someone logs in close to the table. They receive information about the items on the table before they + // receive information about the table. The items are very close to the avatar's capsule, so they become + // activated in bullet. This causes them to fall to the floor, because the table's shape isn't yet in bullet. + EntityTreePointer entityTree = getEntities()->getTree(); + if (!entityTree) { + return false; + } + + QVector entities; + entityTree->withReadLock([&] { + AABox box(getMyAvatar()->getPosition() - glm::vec3(PHYSICS_READY_RANGE), glm::vec3(2 * PHYSICS_READY_RANGE)); + entityTree->findEntities(box, entities); + }); + + foreach (EntityItemPointer entity, entities) { + if (!entity->isReadyToComputeShape()) { + static QString repeatedMessage = + LogHandler::getInstance().addRepeatedMessageRegex("Physics disabled until entity loads: .*"); + qCDebug(interfaceapp) << "Physics disabled until entity loads: " << entity->getID() << entity->getName(); + return false; + } + } + return true; +} + int Application::processOctreeStats(ReceivedMessage& message, SharedNodePointer sendingNode) { // But, also identify the sender, and keep track of the contained jurisdiction root for this server @@ -3938,7 +3958,22 @@ int Application::processOctreeStats(ReceivedMessage& message, SharedNodePointer }); }); - + if (!_physicsEnabled) { + if (nearbyEntitiesAreReadyForPhysics()) { + // These stats packets are sent in between full sends of a scene. + // We keep physics disabled until we've recieved a full scene and everything near the avatar in that + // scene is ready to compute its collision shape. + _physicsEnabled = true; + getMyAvatar()->updateMotionBehaviorFromMenu(); + } else { + auto characterController = getMyAvatar()->getCharacterController(); + if (characterController) { + // if we have a character controller, disable it here so the avatar doesn't get stuck due to + // a non-loading collision hull. + characterController->setEnabled(false); + } + } + } return statsMessageLength; } @@ -3987,20 +4022,13 @@ void Application::saveScripts() { settings.endArray(); } -QScriptValue joystickToScriptValue(QScriptEngine *engine, Joystick* const &in) { - return engine->newQObject(in); -} - -void joystickFromScriptValue(const QScriptValue &object, Joystick* &out) { - out = qobject_cast(object.toQObject()); -} void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scriptEngine) { // setup the packet senders and jurisdiction listeners of the script engine's scripting interfaces so // we can use the same ones from the application. auto entityScriptingInterface = DependencyManager::get(); entityScriptingInterface->setPacketSender(&_entityEditSender); - entityScriptingInterface->setEntityTree(_entities.getTree()); + entityScriptingInterface->setEntityTree(getEntities()->getTree()); // AvatarManager has some custom types AvatarManager::registerMetaTypes(scriptEngine); @@ -4021,7 +4049,7 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri scriptEngine->registerGlobalObject("Clipboard", clipboardScriptable); connect(scriptEngine, SIGNAL(finished(const QString&)), clipboardScriptable, SLOT(deleteLater())); - connect(scriptEngine, SIGNAL(finished(const QString&)), this, SLOT(scriptFinished(const QString&))); + connect(scriptEngine, &ScriptEngine::finished, this, &Application::scriptFinished, Qt::DirectConnection); connect(scriptEngine, SIGNAL(loadScript(const QString&, bool)), this, SLOT(loadScript(const QString&, bool))); connect(scriptEngine, SIGNAL(reloadScript(const QString&, bool)), this, SLOT(reloadScript(const QString&, bool))); @@ -4058,8 +4086,6 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri scriptEngine->registerGlobalObject("AvatarManager", DependencyManager::get().data()); - qScriptRegisterMetaType(scriptEngine, joystickToScriptValue, joystickFromScriptValue); - scriptEngine->registerGlobalObject("UndoStack", &_undoStackScriptingInterface); scriptEngine->registerGlobalObject("LODManager", DependencyManager::get().data()); @@ -4267,10 +4293,13 @@ ScriptEngine* Application::loadScript(const QString& scriptFilename, bool isUser QUrl scriptUrl(scriptFilename); const QString& scriptURLString = scriptUrl.toString(); - if (_scriptEnginesHash.contains(scriptURLString) && loadScriptFromEditor - && !_scriptEnginesHash[scriptURLString]->isFinished()) { + { + QReadLocker lock(&_scriptEnginesHashLock); + if (_scriptEnginesHash.contains(scriptURLString) && loadScriptFromEditor + && !_scriptEnginesHash[scriptURLString]->isFinished()) { - return _scriptEnginesHash[scriptURLString]; + return _scriptEnginesHash[scriptURLString]; + } } ScriptEngine* scriptEngine = new ScriptEngine(NO_SCRIPT, "", &_controllerScriptingInterface); @@ -4310,7 +4339,11 @@ void Application::reloadScript(const QString& scriptName, bool isUserLoaded) { void Application::handleScriptEngineLoaded(const QString& scriptFilename) { ScriptEngine* scriptEngine = qobject_cast(sender()); - _scriptEnginesHash.insertMulti(scriptFilename, scriptEngine); + { + QWriteLocker lock(&_scriptEnginesHashLock); + _scriptEnginesHash.insertMulti(scriptFilename, scriptEngine); + } + _runningScriptsWidget->setRunningScripts(getRunningScripts()); UserActivityLogger::getInstance().loadedScript(scriptFilename); @@ -4325,55 +4358,88 @@ void Application::handleScriptLoadError(const QString& scriptFilename) { QMessageBox::warning(getWindow(), "Error Loading Script", scriptFilename + " failed to load."); } -void Application::scriptFinished(const QString& scriptName) { - const QString& scriptURLString = QUrl(scriptName).toString(); - QHash::iterator it = _scriptEnginesHash.find(scriptURLString); - if (it != _scriptEnginesHash.end()) { - _scriptEnginesHash.erase(it); - _runningScriptsWidget->scriptStopped(scriptName); - _runningScriptsWidget->setRunningScripts(getRunningScripts()); +QStringList Application::getRunningScripts() { + QReadLocker lock(&_scriptEnginesHashLock); + return _scriptEnginesHash.keys(); +} + +ScriptEngine* Application::getScriptEngine(const QString& scriptHash) { + QReadLocker lock(&_scriptEnginesHashLock); + return _scriptEnginesHash.value(scriptHash, nullptr); +} + +void Application::scriptFinished(const QString& scriptName, ScriptEngine* engine) { + bool removed = false; + { + QWriteLocker lock(&_scriptEnginesHashLock); + const QString& scriptURLString = QUrl(scriptName).toString(); + for (auto it = _scriptEnginesHash.find(scriptURLString); it != _scriptEnginesHash.end(); ++it) { + if (it.value() == engine) { + _scriptEnginesHash.erase(it); + removed = true; + break; + } + } + } + if (removed) { + postLambdaEvent([this, scriptName]() { + _runningScriptsWidget->scriptStopped(scriptName); + _runningScriptsWidget->setRunningScripts(getRunningScripts()); + }); } } void Application::stopAllScripts(bool restart) { - if (restart) { - // Delete all running scripts from cache so that they are re-downloaded when they are restarted - auto scriptCache = DependencyManager::get(); - for (QHash::const_iterator it = _scriptEnginesHash.constBegin(); + { + QReadLocker lock(&_scriptEnginesHashLock); + + if (restart) { + // Delete all running scripts from cache so that they are re-downloaded when they are restarted + auto scriptCache = DependencyManager::get(); + for (QHash::const_iterator it = _scriptEnginesHash.constBegin(); it != _scriptEnginesHash.constEnd(); it++) { - if (!it.value()->isFinished()) { - scriptCache->deleteScript(it.key()); + if (!it.value()->isFinished()) { + scriptCache->deleteScript(it.key()); + } } } - } - // Stop and possibly restart all currently running scripts - for (QHash::const_iterator it = _scriptEnginesHash.constBegin(); - it != _scriptEnginesHash.constEnd(); it++) { - if (it.value()->isFinished()) { - continue; + // Stop and possibly restart all currently running scripts + for (QHash::const_iterator it = _scriptEnginesHash.constBegin(); + it != _scriptEnginesHash.constEnd(); it++) { + if (it.value()->isFinished()) { + continue; + } + if (restart && it.value()->isUserLoaded()) { + connect(it.value(), &ScriptEngine::finished, this, [this](QString scriptName, ScriptEngine* engine) { + reloadScript(scriptName); + }); + } + QMetaObject::invokeMethod(it.value(), "stop"); + //it.value()->stop(); + qCDebug(interfaceapp) << "stopping script..." << it.key(); } - if (restart && it.value()->isUserLoaded()) { - connect(it.value(), SIGNAL(finished(const QString&)), SLOT(reloadScript(const QString&))); - } - it.value()->stop(); - qCDebug(interfaceapp) << "stopping script..." << it.key(); } getMyAvatar()->clearScriptableSettings(); } bool Application::stopScript(const QString& scriptHash, bool restart) { bool stoppedScript = false; - if (_scriptEnginesHash.contains(scriptHash)) { - ScriptEngine* scriptEngine = _scriptEnginesHash[scriptHash]; - if (restart) { - auto scriptCache = DependencyManager::get(); - scriptCache->deleteScript(QUrl(scriptHash)); - connect(scriptEngine, SIGNAL(finished(const QString&)), SLOT(reloadScript(const QString&))); + { + QReadLocker lock(&_scriptEnginesHashLock); + if (_scriptEnginesHash.contains(scriptHash)) { + ScriptEngine* scriptEngine = _scriptEnginesHash[scriptHash]; + if (restart) { + auto scriptCache = DependencyManager::get(); + scriptCache->deleteScript(QUrl(scriptHash)); + connect(scriptEngine, &ScriptEngine::finished, this, [this](QString scriptName, ScriptEngine* engine) { + reloadScript(scriptName); + }); + } + scriptEngine->stop(); + stoppedScript = true; + qCDebug(interfaceapp) << "stopping script..." << scriptHash; } - scriptEngine->stop(); - stoppedScript = true; - qCDebug(interfaceapp) << "stopping script..." << scriptHash; } if (_scriptEnginesHash.empty()) { getMyAvatar()->clearScriptableSettings(); @@ -4392,6 +4458,7 @@ void Application::reloadOneScript(const QString& scriptName) { } void Application::loadDefaultScripts() { + QReadLocker lock(&_scriptEnginesHashLock); if (!_scriptEnginesHash.contains(DEFAULT_SCRIPTS_JS_URL)) { loadScript(DEFAULT_SCRIPTS_JS_URL); } diff --git a/interface/src/Application.h b/interface/src/Application.h index ce33f051ef..e0f86d97bd 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -136,7 +136,7 @@ public: const ViewFrustum* getDisplayViewFrustum() const; ViewFrustum* getShadowViewFrustum() { return &_shadowViewFrustum; } const OctreePacketProcessor& getOctreePacketProcessor() const { return _octreeProcessor; } - EntityTreeRenderer* getEntities() { return &_entities; } + EntityTreeRenderer* getEntities() { return DependencyManager::get().data(); } QUndoStack* getUndoStack() { return &_undoStack; } MainWindow* getWindow() { return _window; } EntityTreePointer getEntityClipboard() { return _entityClipboard; } @@ -160,14 +160,9 @@ public: uint32_t getFrameCount() { return _frameCount; } float getFps() const { return _fps; } - float const HMD_TARGET_FRAME_RATE = 75.0f; - float const DESKTOP_TARGET_FRAME_RATE = 60.0f; - float getTargetFrameRate() { return isHMDMode() ? HMD_TARGET_FRAME_RATE : DESKTOP_TARGET_FRAME_RATE; } - float getTargetFramePeriod() { return isHMDMode() ? 1.0f / HMD_TARGET_FRAME_RATE : 1.0f / DESKTOP_TARGET_FRAME_RATE; } // same as 1/getTargetFrameRate, but w/compile-time division + float getTargetFrameRate(); // frames/second float getLastInstanteousFps() const { return _lastInstantaneousFps; } - float getLastPaintWait() const { return _lastPaintWait; }; - float getLastDeducedNonVSyncFps() const { return _lastDeducedNonVSyncFps; } - void setMarginForDeducedFramePeriod(float newValue) { _marginForDeducedFramePeriod = newValue; } + float getLastUnsynchronizedFps() const { return _lastUnsynchronizedFps; } float getFieldOfView() { return _fieldOfView.get(); } void setFieldOfView(float fov); @@ -202,8 +197,8 @@ public: NodeToJurisdictionMap& getEntityServerJurisdictions() { return _entityServerJurisdictions; } - QStringList getRunningScripts() { return _scriptEnginesHash.keys(); } - ScriptEngine* getScriptEngine(const QString& scriptHash) { return _scriptEnginesHash.value(scriptHash, NULL); } + QStringList getRunningScripts(); + ScriptEngine* getScriptEngine(const QString& scriptHash); float getRenderResolutionScale() const; @@ -336,7 +331,7 @@ private slots: void loadSettings(); void saveSettings(); - void scriptFinished(const QString& scriptName); + void scriptFinished(const QString& scriptName, ScriptEngine* engine); void saveScripts(); void reloadScript(const QString& scriptName, bool isUserLoaded = true); @@ -395,6 +390,7 @@ private: bool importSVOFromURL(const QString& urlString); + bool nearbyEntitiesAreReadyForPhysics(); int processOctreeStats(ReceivedMessage& message, SharedNodePointer sendingNode); void trackIncomingOctreePacket(ReceivedMessage& message, SharedNodePointer sendingNode, bool wasStatsPacket); @@ -442,15 +438,12 @@ private: QElapsedTimer _timerStart; QElapsedTimer _lastTimeUpdated; float _lastInstantaneousFps { 0.0f }; - float _lastPaintWait { 0.0f }; - float _lastDeducedNonVSyncFps { 0.0f }; - float _marginForDeducedFramePeriod{ 0.002f }; // 2ms, adjustable + float _lastUnsynchronizedFps { 0.0f }; ShapeManager _shapeManager; PhysicalEntitySimulation _entitySimulation; PhysicsEnginePointer _physicsEngine; - EntityTreeRenderer _entities; EntityTreeRenderer _entityClipboardRenderer; EntityTreePointer _entityClipboard; @@ -505,6 +498,7 @@ private: TouchEvent _lastTouchEvent; + QReadWriteLock _scriptEnginesHashLock; RunningScriptsWidget* _runningScriptsWidget; QHash _scriptEnginesHash; bool _runningScriptsWidgetWasVisible; @@ -564,6 +558,7 @@ private: bool _isForeground = true; // starts out assumed to be in foreground bool _inPaint = false; bool _isGLInitialized { false }; + bool _physicsEnabled { false }; }; #endif // hifi_Application_h diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index e2b92cc06f..218d679898 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -200,7 +200,6 @@ void Avatar::simulate(float deltaTime) { PerformanceTimer perfTimer("skeleton"); _skeletonModel.getRig()->copyJointsFromJointData(_jointData); _skeletonModel.simulate(deltaTime, _hasNewJointRotations || _hasNewJointTranslations); - simulateAttachments(deltaTime); locationChanged(); // joints changed, so if there are any children, update them. _hasNewJointRotations = false; _hasNewJointTranslations = false; diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index 99a4fc52a9..09685c318e 100644 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -69,6 +69,7 @@ public: void init(); void simulate(float deltaTime); + void simulateAttachments(float deltaTime); virtual void render(RenderArgs* renderArgs, const glm::vec3& cameraPosition); @@ -87,7 +88,6 @@ public: bool isInitialized() const { return _initialized; } SkeletonModel& getSkeletonModel() { return _skeletonModel; } const SkeletonModel& getSkeletonModel() const { return _skeletonModel; } - const QVector& getAttachmentModels() const { return _attachmentModels; } glm::vec3 getChestPosition() const; float getAvatarScale() const { return getScale().y; } const Head* getHead() const { return static_cast(_headData); } @@ -217,8 +217,6 @@ protected: virtual bool shouldRenderHead(const RenderArgs* renderArgs) const; virtual void fixupModelsInScene(); - void simulateAttachments(float deltaTime); - virtual void updateJointMappings(); render::ItemID _renderItemID; diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 4e3d9b92fe..7c1a52f1b3 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -111,7 +111,6 @@ void AvatarManager::init() { _renderDistanceController.setKP(0.0008f); // Usually about 0.6 of largest that doesn't oscillate when other parameters 0. _renderDistanceController.setKI(0.0006f); // Big enough to bring us to target with the above KP. _renderDistanceController.setKD(0.000001f); // A touch of kd increases the speed by which we get there. - } void AvatarManager::updateMyAvatar(float deltaTime) { @@ -146,13 +145,19 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { PerformanceTimer perfTimer("otherAvatars"); - _renderDistanceController.setMeasuredValueSetpoint(qApp->getTargetFrameRate()); // No problem updating in flight. - // The PID controller raises the controlled value when the measured value goes up. - // The measured value is frame rate. When the controlled value (1 / render cutoff distance) - // goes up, the render cutoff distance gets closer, the number of rendered avatars is less, and frame rate - // goes up. - const float deduced = qApp->getLastDeducedNonVSyncFps(); - const float distance = 1.0f / _renderDistanceController.update(deduced, deltaTime); + float distance; + if (!qApp->isThrottleRendering()) { + _renderDistanceController.setMeasuredValueSetpoint(qApp->getTargetFrameRate()); // No problem updating in flight. + // The PID controller raises the controlled value when the measured value goes up. + // The measured value is frame rate. When the controlled value (1 / render cutoff distance) + // goes up, the render cutoff distance gets closer, the number of rendered avatars is less, and frame rate + // goes up. + const float deduced = qApp->getLastUnsynchronizedFps(); + distance = 1.0f / _renderDistanceController.update(deduced, deltaTime); + } else { + // Here we choose to just use the maximum render cutoff distance if throttled. + distance = 1.0f / _renderDistanceController.getControlledValueLowLimit(); + } _renderDistanceAverage.updateAverage(distance); _renderDistance = _renderDistanceAverage.getAverage(); int renderableCount = 0; diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 6637331b64..b3f6b4c238 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -323,11 +323,6 @@ void MyAvatar::simulate(float deltaTime) { return; } - { - PerformanceTimer perfTimer("attachments"); - simulateAttachments(deltaTime); - } - { PerformanceTimer perfTimer("joints"); // copy out the skeleton joints from the model @@ -991,6 +986,14 @@ void MyAvatar::setSkeletonModelURL(const QUrl& skeletonModelURL) { _headBoneSet.clear(); } + +void MyAvatar::resetFullAvatarURL() { + auto lastAvatarURL = getFullAvatarURLFromPreferences(); + auto lastAvatarName = getFullAvatarModelName(); + useFullAvatarURL(QUrl()); + useFullAvatarURL(lastAvatarURL, lastAvatarName); +} + void MyAvatar::useFullAvatarURL(const QUrl& fullAvatarURL, const QString& modelName) { if (QThread::currentThread() != thread()) { @@ -1058,7 +1061,7 @@ void MyAvatar::rebuildSkeletonBody() { void MyAvatar::prepareForPhysicsSimulation() { relayDriveKeysToCharacterController(); _characterController.setTargetVelocity(getTargetVelocity()); - _characterController.setAvatarPositionAndOrientation(getPosition(), getOrientation()); + _characterController.setPositionAndOrientation(getPosition(), getOrientation()); if (qApp->isHMDMode()) { updateHMDFollowVelocity(); } else if (_followSpeed > 0.0f) { @@ -1071,7 +1074,7 @@ void MyAvatar::prepareForPhysicsSimulation() { void MyAvatar::harvestResultsFromPhysicsSimulation() { glm::vec3 position = getPosition(); glm::quat orientation = getOrientation(); - _characterController.getAvatarPositionAndOrientation(position, orientation); + _characterController.getPositionAndOrientation(position, orientation); nextAttitude(position, orientation); if (_followSpeed > 0.0f) { adjustSensorTransform(); diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 309e600978..514261bf16 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -196,6 +196,8 @@ public: Q_INVOKABLE void useFullAvatarURL(const QUrl& fullAvatarURL, const QString& modelName = QString()); Q_INVOKABLE const QUrl& getFullAvatarURLFromPreferences() const { return _fullAvatarURLFromPreferences; } Q_INVOKABLE const QString& getFullAvatarModelName() const { return _fullAvatarModelName; } + void resetFullAvatarURL(); + virtual void setAttachmentData(const QVector& attachmentData) override; diff --git a/interface/src/avatar/MyCharacterController.cpp b/interface/src/avatar/MyCharacterController.cpp index e8f686da6f..23d601e58e 100644 --- a/interface/src/avatar/MyCharacterController.cpp +++ b/interface/src/avatar/MyCharacterController.cpp @@ -11,255 +11,25 @@ #include "MyCharacterController.h" -#include -#include -#include -#include - -#include -#include -#include +#include #include "MyAvatar.h" -const btVector3 LOCAL_UP_AXIS(0.0f, 1.0f, 0.0f); -const float DEFAULT_GRAVITY = -5.0f; -const float JUMP_SPEED = 3.5f; - -const float MAX_FALL_HEIGHT = 20.0f; - // TODO: improve walking up steps // TODO: make avatars able to walk up and down steps/slopes // TODO: make avatars stand on steep slope // TODO: make avatars not snag on low ceilings -// helper class for simple ray-traces from character -class ClosestNotMe : public btCollisionWorld::ClosestRayResultCallback { -public: - ClosestNotMe(btRigidBody* me) : btCollisionWorld::ClosestRayResultCallback(btVector3(0.0f, 0.0f, 0.0f), btVector3(0.0f, 0.0f, 0.0f)) { - _me = me; - } - virtual btScalar addSingleResult(btCollisionWorld::LocalRayResult& rayResult,bool normalInWorldSpace) { - if (rayResult.m_collisionObject == _me) { - return 1.0f; - } - return ClosestRayResultCallback::addSingleResult(rayResult, normalInWorldSpace); - } -protected: - btRigidBody* _me; -}; - MyCharacterController::MyCharacterController(MyAvatar* avatar) { - _halfHeight = 1.0f; assert(avatar); _avatar = avatar; - - _enabled = false; - - _floorDistance = MAX_FALL_HEIGHT; - - _walkVelocity.setValue(0.0f, 0.0f, 0.0f); - _followVelocity.setValue(0.0f, 0.0f, 0.0f); - _jumpSpeed = JUMP_SPEED; - _isOnGround = false; - _isJumping = false; - _isFalling = false; - _isHovering = true; - _isPushingUp = false; - _jumpToHoverStart = 0; - _followTime = 0.0f; - - _pendingFlags = PENDING_FLAG_UPDATE_SHAPE; updateShapeIfNecessary(); } MyCharacterController::~MyCharacterController() { } -void MyCharacterController::preStep(btCollisionWorld* collisionWorld) { - // trace a ray straight down to see if we're standing on the ground - const btTransform& xform = _rigidBody->getWorldTransform(); - - // rayStart is at center of bottom sphere - btVector3 rayStart = xform.getOrigin() - _halfHeight * _currentUp; - - // rayEnd is some short distance outside bottom sphere - const btScalar FLOOR_PROXIMITY_THRESHOLD = 0.3f * _radius; - btScalar rayLength = _radius + FLOOR_PROXIMITY_THRESHOLD; - btVector3 rayEnd = rayStart - rayLength * _currentUp; - - // scan down for nearby floor - ClosestNotMe rayCallback(_rigidBody); - rayCallback.m_closestHitFraction = 1.0f; - collisionWorld->rayTest(rayStart, rayEnd, rayCallback); - if (rayCallback.hasHit()) { - _floorDistance = rayLength * rayCallback.m_closestHitFraction - _radius; - } -} - -void MyCharacterController::playerStep(btCollisionWorld* dynaWorld, btScalar dt) { - btVector3 actualVelocity = _rigidBody->getLinearVelocity(); - btScalar actualSpeed = actualVelocity.length(); - - btVector3 desiredVelocity = _walkVelocity; - btScalar desiredSpeed = desiredVelocity.length(); - - const btScalar MIN_UP_PUSH = 0.1f; - if (desiredVelocity.dot(_currentUp) < MIN_UP_PUSH) { - _isPushingUp = false; - } - - const btScalar MIN_SPEED = 0.001f; - if (_isHovering) { - if (desiredSpeed < MIN_SPEED) { - if (actualSpeed < MIN_SPEED) { - _rigidBody->setLinearVelocity(btVector3(0.0f, 0.0f, 0.0f)); - } else { - const btScalar HOVER_BRAKING_TIMESCALE = 0.1f; - btScalar tau = glm::max(dt / HOVER_BRAKING_TIMESCALE, 1.0f); - _rigidBody->setLinearVelocity((1.0f - tau) * actualVelocity); - } - } else { - const btScalar HOVER_ACCELERATION_TIMESCALE = 0.1f; - btScalar tau = dt / HOVER_ACCELERATION_TIMESCALE; - _rigidBody->setLinearVelocity(actualVelocity - tau * (actualVelocity - desiredVelocity)); - } - } else { - if (onGround()) { - // walking on ground - if (desiredSpeed < MIN_SPEED) { - if (actualSpeed < MIN_SPEED) { - _rigidBody->setLinearVelocity(btVector3(0.0f, 0.0f, 0.0f)); - } else { - const btScalar HOVER_BRAKING_TIMESCALE = 0.1f; - btScalar tau = dt / HOVER_BRAKING_TIMESCALE; - _rigidBody->setLinearVelocity((1.0f - tau) * actualVelocity); - } - } else { - // TODO: modify desiredVelocity using floor normal - const btScalar WALK_ACCELERATION_TIMESCALE = 0.1f; - btScalar tau = dt / WALK_ACCELERATION_TIMESCALE; - btVector3 velocityCorrection = tau * (desiredVelocity - actualVelocity); - // subtract vertical component - velocityCorrection -= velocityCorrection.dot(_currentUp) * _currentUp; - _rigidBody->setLinearVelocity(actualVelocity + velocityCorrection); - } - } else { - // transitioning to flying - btVector3 velocityCorrection = desiredVelocity - actualVelocity; - const btScalar FLY_ACCELERATION_TIMESCALE = 0.2f; - btScalar tau = dt / FLY_ACCELERATION_TIMESCALE; - if (!_isPushingUp) { - // actually falling --> compute a different velocity attenuation factor - const btScalar FALL_ACCELERATION_TIMESCALE = 2.0f; - tau = dt / FALL_ACCELERATION_TIMESCALE; - // zero vertical component - velocityCorrection -= velocityCorrection.dot(_currentUp) * _currentUp; - } - _rigidBody->setLinearVelocity(actualVelocity + tau * velocityCorrection); - } - } - - // Rather than add _followVelocity to the velocity of the RigidBody, we explicitly teleport - // the RigidBody forward according to the formula: distance = rate * time - if (_followVelocity.length2() > 0.0f) { - btTransform bodyTransform = _rigidBody->getWorldTransform(); - bodyTransform.setOrigin(bodyTransform.getOrigin() + dt * _followVelocity); - _rigidBody->setWorldTransform(bodyTransform); - } - _followTime += dt; -} - -void MyCharacterController::jump() { - // check for case where user is holding down "jump" key... - // we'll eventually tansition to "hover" - if (!_isJumping) { - if (!_isHovering) { - _jumpToHoverStart = usecTimestampNow(); - _pendingFlags |= PENDING_FLAG_JUMP; - } - } else { - quint64 now = usecTimestampNow(); - const quint64 JUMP_TO_HOVER_PERIOD = 75 * (USECS_PER_SECOND / 100); - if (now - _jumpToHoverStart > JUMP_TO_HOVER_PERIOD) { - _isPushingUp = true; - setHovering(true); - } - } -} - -bool MyCharacterController::onGround() const { - const btScalar FLOOR_PROXIMITY_THRESHOLD = 0.3f * _radius; - return _floorDistance < FLOOR_PROXIMITY_THRESHOLD; -} - -void MyCharacterController::setHovering(bool hover) { - if (hover != _isHovering) { - _isHovering = hover; - _isJumping = false; - - if (_rigidBody) { - if (hover) { - _rigidBody->setGravity(btVector3(0.0f, 0.0f, 0.0f)); - } else { - _rigidBody->setGravity(DEFAULT_GRAVITY * _currentUp); - } - } - } -} - -void MyCharacterController::setLocalBoundingBox(const glm::vec3& corner, const glm::vec3& scale) { - _boxScale = scale; - - float x = _boxScale.x; - float z = _boxScale.z; - float radius = 0.5f * sqrtf(0.5f * (x * x + z * z)); - float halfHeight = 0.5f * _boxScale.y - radius; - float MIN_HALF_HEIGHT = 0.1f; - if (halfHeight < MIN_HALF_HEIGHT) { - halfHeight = MIN_HALF_HEIGHT; - } - - // compare dimensions - float radiusDelta = glm::abs(radius - _radius); - float heightDelta = glm::abs(halfHeight - _halfHeight); - if (radiusDelta < FLT_EPSILON && heightDelta < FLT_EPSILON) { - // shape hasn't changed --> nothing to do - } else { - if (_dynamicsWorld) { - // must REMOVE from world prior to shape update - _pendingFlags |= PENDING_FLAG_REMOVE_FROM_SIMULATION; - } - _pendingFlags |= PENDING_FLAG_UPDATE_SHAPE; - // only need to ADD back when we happen to be enabled - if (_enabled) { - _pendingFlags |= PENDING_FLAG_ADD_TO_SIMULATION; - } - } - - // it's ok to change offset immediately -- there are no thread safety issues here - _shapeLocalOffset = corner + 0.5f * _boxScale; -} - -void MyCharacterController::setEnabled(bool enabled) { - if (enabled != _enabled) { - if (enabled) { - // Don't bother clearing REMOVE bit since it might be paired with an UPDATE_SHAPE bit. - // Setting the ADD bit here works for all cases so we don't even bother checking other bits. - _pendingFlags |= PENDING_FLAG_ADD_TO_SIMULATION; - } else { - if (_dynamicsWorld) { - _pendingFlags |= PENDING_FLAG_REMOVE_FROM_SIMULATION; - } - _pendingFlags &= ~ PENDING_FLAG_ADD_TO_SIMULATION; - _isOnGround = false; - } - setHovering(true); - _enabled = enabled; - } -} - void MyCharacterController::updateShapeIfNecessary() { if (_pendingFlags & PENDING_FLAG_UPDATE_SHAPE) { _pendingFlags &= ~ PENDING_FLAG_UPDATE_SHAPE; @@ -300,7 +70,7 @@ void MyCharacterController::updateShapeIfNecessary() { if (_isHovering) { _rigidBody->setGravity(btVector3(0.0f, 0.0f, 0.0f)); } else { - _rigidBody->setGravity(DEFAULT_GRAVITY * _currentUp); + _rigidBody->setGravity(DEFAULT_CHARACTER_GRAVITY * _currentUp); } //_rigidBody->setCollisionFlags(btCollisionObject::CF_CHARACTER_OBJECT); } else { @@ -309,98 +79,3 @@ void MyCharacterController::updateShapeIfNecessary() { } } -void MyCharacterController::updateUpAxis(const glm::quat& rotation) { - btVector3 oldUp = _currentUp; - _currentUp = quatRotate(glmToBullet(rotation), LOCAL_UP_AXIS); - if (!_isHovering) { - const btScalar MIN_UP_ERROR = 0.01f; - if (oldUp.distance(_currentUp) > MIN_UP_ERROR) { - _rigidBody->setGravity(DEFAULT_GRAVITY * _currentUp); - } - } -} - -void MyCharacterController::setAvatarPositionAndOrientation( - const glm::vec3& position, - const glm::quat& orientation) { - // TODO: update gravity if up has changed - updateUpAxis(orientation); - - btQuaternion bodyOrientation = glmToBullet(orientation); - btVector3 bodyPosition = glmToBullet(position + orientation * _shapeLocalOffset); - _avatarBodyTransform = btTransform(bodyOrientation, bodyPosition); -} - -void MyCharacterController::getAvatarPositionAndOrientation(glm::vec3& position, glm::quat& rotation) const { - if (_enabled && _rigidBody) { - const btTransform& avatarTransform = _rigidBody->getWorldTransform(); - rotation = bulletToGLM(avatarTransform.getRotation()); - position = bulletToGLM(avatarTransform.getOrigin()) - rotation * _shapeLocalOffset; - } -} - -void MyCharacterController::setTargetVelocity(const glm::vec3& velocity) { - //_walkVelocity = glmToBullet(_avatarData->getTargetVelocity()); - _walkVelocity = glmToBullet(velocity); -} - -void MyCharacterController::setFollowVelocity(const glm::vec3& velocity) { - _followVelocity = glmToBullet(velocity); -} - -glm::vec3 MyCharacterController::getLinearVelocity() const { - glm::vec3 velocity(0.0f); - if (_rigidBody) { - velocity = bulletToGLM(_rigidBody->getLinearVelocity()); - } - return velocity; -} - -void MyCharacterController::preSimulation() { - if (_enabled && _dynamicsWorld) { - // slam body to where it is supposed to be - _rigidBody->setWorldTransform(_avatarBodyTransform); - - // scan for distant floor - // rayStart is at center of bottom sphere - btVector3 rayStart = _avatarBodyTransform.getOrigin() - _halfHeight * _currentUp; - - // rayEnd is straight down MAX_FALL_HEIGHT - btScalar rayLength = _radius + MAX_FALL_HEIGHT; - btVector3 rayEnd = rayStart - rayLength * _currentUp; - - ClosestNotMe rayCallback(_rigidBody); - rayCallback.m_closestHitFraction = 1.0f; - _dynamicsWorld->rayTest(rayStart, rayEnd, rayCallback); - if (rayCallback.hasHit()) { - _floorDistance = rayLength * rayCallback.m_closestHitFraction - _radius; - const btScalar MIN_HOVER_HEIGHT = 3.0f; - if (_isHovering && _floorDistance < MIN_HOVER_HEIGHT && !_isPushingUp) { - setHovering(false); - } - // TODO: use collision events rather than ray-trace test to disable jumping - const btScalar JUMP_PROXIMITY_THRESHOLD = 0.1f * _radius; - if (_floorDistance < JUMP_PROXIMITY_THRESHOLD) { - _isJumping = false; - } - } else { - _floorDistance = FLT_MAX; - setHovering(true); - } - - if (_pendingFlags & PENDING_FLAG_JUMP) { - _pendingFlags &= ~ PENDING_FLAG_JUMP; - if (onGround()) { - _isJumping = true; - btVector3 velocity = _rigidBody->getLinearVelocity(); - velocity += _jumpSpeed * _currentUp; - _rigidBody->setLinearVelocity(velocity); - } - } - } - _followTime = 0.0f; -} - -void MyCharacterController::postSimulation() { - // postSimulation() exists for symmetry and just in case we need to do something here later -} diff --git a/interface/src/avatar/MyCharacterController.h b/interface/src/avatar/MyCharacterController.h index 82aa958309..39f0f99917 100644 --- a/interface/src/avatar/MyCharacterController.h +++ b/interface/src/avatar/MyCharacterController.h @@ -13,12 +13,8 @@ #ifndef hifi_MyCharacterController_h #define hifi_MyCharacterController_h -#include -#include - -#include #include -#include +//#include class btCollisionShape; class MyAvatar; @@ -28,79 +24,10 @@ public: MyCharacterController(MyAvatar* avatar); ~MyCharacterController (); - // TODO: implement these when needed - virtual void setVelocityForTimeInterval(const btVector3 &velocity, btScalar timeInterval) override { assert(false); } - virtual void reset(btCollisionWorld* collisionWorld) override { } - virtual void warp(const btVector3& origin) override { } - virtual void debugDraw(btIDebugDraw* debugDrawer) override { } - virtual void setUpInterpolate(bool value) override { } - - // overrides from btCharacterControllerInterface - virtual void updateAction(btCollisionWorld* collisionWorld, btScalar deltaTime) override { - preStep(collisionWorld); - playerStep(collisionWorld, deltaTime); - } - virtual void preStep(btCollisionWorld* collisionWorld) override; - virtual void playerStep(btCollisionWorld* collisionWorld, btScalar dt) override; - virtual bool canJump() const override { assert(false); return false; } // never call this - virtual void jump() override; // call this every frame the jump button is pressed - virtual bool onGround() const override; - - // overrides from CharacterController - virtual void preSimulation() override; - virtual void postSimulation() override; - - bool isHovering() const { return _isHovering; } - void setHovering(bool enabled); - - void setEnabled(bool enabled); - bool isEnabled() const { return _enabled && _dynamicsWorld; } - - void setLocalBoundingBox(const glm::vec3& corner, const glm::vec3& scale); - virtual void updateShapeIfNecessary() override; - void setAvatarPositionAndOrientation( const glm::vec3& position, const glm::quat& orientation); - void getAvatarPositionAndOrientation(glm::vec3& position, glm::quat& rotation) const; - - void setTargetVelocity(const glm::vec3& velocity); - void setFollowVelocity(const glm::vec3& velocity); - float getFollowTime() const { return _followTime; } - - glm::vec3 getLinearVelocity() const; - protected: - void updateUpAxis(const glm::quat& rotation); - -protected: - btVector3 _currentUp; - btVector3 _walkVelocity; - btVector3 _followVelocity; - btTransform _avatarBodyTransform; - - glm::vec3 _shapeLocalOffset; - glm::vec3 _boxScale; // used to compute capsule shape - - quint64 _jumpToHoverStart; - MyAvatar* _avatar { nullptr }; - - btScalar _halfHeight; - btScalar _radius; - - btScalar _floorDistance; - - btScalar _gravity; - - btScalar _jumpSpeed; - btScalar _followTime; - - bool _enabled; - bool _isOnGround; - bool _isJumping; - bool _isFalling; - bool _isHovering; - bool _isPushingUp; }; #endif // hifi_MyCharacterController_h diff --git a/interface/src/scripting/ControllerScriptingInterface.cpp b/interface/src/scripting/ControllerScriptingInterface.cpp index 11053154b6..3bcf8bb311 100644 --- a/interface/src/scripting/ControllerScriptingInterface.cpp +++ b/interface/src/scripting/ControllerScriptingInterface.cpp @@ -20,9 +20,6 @@ #include "Application.h" #include "devices/MotionTracker.h" -// TODO: this needs to be removed, as well as any related controller-specific information -#include - void ControllerScriptingInterface::handleMetaEvent(HFMetaEvent* event) { if (event->type() == HFActionEvent::startType()) { emit actionStartEvent(static_cast(*event)); diff --git a/interface/src/ui/ApplicationCompositor.cpp b/interface/src/ui/ApplicationCompositor.cpp index 4cee190a47..f4dbdd3bc0 100644 --- a/interface/src/ui/ApplicationCompositor.cpp +++ b/interface/src/ui/ApplicationCompositor.cpp @@ -26,7 +26,6 @@ #include "Tooltip.h" #include "Application.h" -#include // TODO: any references to sixense should be removed here #include diff --git a/interface/src/ui/PreferencesDialog.cpp b/interface/src/ui/PreferencesDialog.cpp index a25f868172..9a995263cd 100644 --- a/interface/src/ui/PreferencesDialog.cpp +++ b/interface/src/ui/PreferencesDialog.cpp @@ -16,7 +16,6 @@ #include #include #include -#include // TODO: This should be replaced with InputDevice/InputPlugin, or something similar #include #include "Application.h" diff --git a/libraries/animation/src/AnimVariant.cpp b/libraries/animation/src/AnimVariant.cpp index 118d2e8ce9..d57d1b3c34 100644 --- a/libraries/animation/src/AnimVariant.cpp +++ b/libraries/animation/src/AnimVariant.cpp @@ -50,9 +50,11 @@ QScriptValue AnimVariantMap::animVariantMapToScriptValue(QScriptEngine* engine, if (useNames) { // copy only the requested names for (const QString& name : names) { auto search = _map.find(name); - if (search != _map.end()) { // scripts are allowed to request names that do not exist + if (search != _map.end()) { setOne(name, search->second); - } + } else if (_triggers.count(name) == 1) { + target.setProperty(name, true); + } // scripts are allowed to request names that do not exist } } else { // copy all of them diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index a80ef5d2eb..464ff28980 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -717,7 +717,8 @@ void Rig::updateAnimationStateHandlers() { // called on avatar update thread (wh // This works (I tried it), but the result would be that we would still have same runtime type checks as the invokeMethod above // (occuring within the ScriptEngine::callAnimationStateHandler invokeMethod trampoline), _plus_ another runtime check for the dynamic_cast. - // gather results in (likely from an earlier update): + // Gather results in (likely from an earlier update). + // Note: the behavior is undefined if a handler (re-)sets a trigger. Scripts should not be doing that. _animVars.copyVariantsFrom(value.results); // If multiple handlers write the same anim var, the last registgered wins. (_map preserves order). } } diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index c998c57e47..a8a68de60d 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -541,26 +541,40 @@ bool AudioClient::switchOutputToAudioDevice(const QString& outputDeviceName) { void AudioClient::configureReverb() { ReverbParameters p; - _listenerReverb.getParameters(&p); - - // for now, reuse the gverb parameters p.sampleRate = _outputFormat.sampleRate(); - p.roomSize = _reverbOptions->getRoomSize(); + + p.bandwidth = _reverbOptions->getBandwidth(); + p.preDelay = _reverbOptions->getPreDelay(); + p.lateDelay = _reverbOptions->getLateDelay(); p.reverbTime = _reverbOptions->getReverbTime(); - p.highGain = -24.0f * (1.0f - _reverbOptions->getDamping()); - p.bandwidth = 10000.0f * _reverbOptions->getInputBandwidth(); - p.earlyGain = _reverbOptions->getEarlyLevel(); - p.lateGain = _reverbOptions->getTailLevel(); - p.wetDryMix = 100.0f * powf(10.0f, _reverbOptions->getWetLevel() * (1/20.0f)); + p.earlyDiffusion = _reverbOptions->getEarlyDiffusion(); + p.lateDiffusion = _reverbOptions->getLateDiffusion(); + p.roomSize = _reverbOptions->getRoomSize(); + p.density = _reverbOptions->getDensity(); + p.bassMult = _reverbOptions->getBassMult(); + p.bassFreq = _reverbOptions->getBassFreq(); + p.highGain = _reverbOptions->getHighGain(); + p.highFreq = _reverbOptions->getHighFreq(); + p.modRate = _reverbOptions->getModRate(); + p.modDepth = _reverbOptions->getModDepth(); + p.earlyGain = _reverbOptions->getEarlyGain(); + p.lateGain = _reverbOptions->getLateGain(); + p.earlyMixLeft = _reverbOptions->getEarlyMixLeft(); + p.earlyMixRight = _reverbOptions->getEarlyMixRight(); + p.lateMixLeft = _reverbOptions->getLateMixLeft(); + p.lateMixRight = _reverbOptions->getLateMixRight(); + p.wetDryMix = _reverbOptions->getWetDryMix(); + _listenerReverb.setParameters(&p); - // used for adding self-reverb to loopback audio + // used only for adding self-reverb to loopback audio p.wetDryMix = 100.0f; p.preDelay = 0.0f; p.earlyGain = -96.0f; // disable ER - p.lateGain -= 12.0f; // quieter than listener reverb + p.lateGain -= 12.0f; // quieter than listener reverb p.lateMixLeft = 0.0f; p.lateMixRight = 0.0f; + _sourceReverb.setParameters(&p); } @@ -572,10 +586,10 @@ void AudioClient::updateReverbOptions() { _zoneReverbOptions.setReverbTime(_receivedAudioStream.getRevebTime()); reverbChanged = true; } - if (_zoneReverbOptions.getWetLevel() != _receivedAudioStream.getWetLevel()) { - _zoneReverbOptions.setWetLevel(_receivedAudioStream.getWetLevel()); - reverbChanged = true; - } + //if (_zoneReverbOptions.getWetLevel() != _receivedAudioStream.getWetLevel()) { + // _zoneReverbOptions.setWetLevel(_receivedAudioStream.getWetLevel()); + // reverbChanged = true; + //} if (_reverbOptions != &_zoneReverbOptions) { _reverbOptions = &_zoneReverbOptions; @@ -602,17 +616,27 @@ void AudioClient::setReverb(bool reverb) { void AudioClient::setReverbOptions(const AudioEffectOptions* options) { // Save the new options - _scriptReverbOptions.setMaxRoomSize(options->getMaxRoomSize()); - _scriptReverbOptions.setRoomSize(options->getRoomSize()); + _scriptReverbOptions.setBandwidth(options->getBandwidth()); + _scriptReverbOptions.setPreDelay(options->getPreDelay()); + _scriptReverbOptions.setLateDelay(options->getLateDelay()); _scriptReverbOptions.setReverbTime(options->getReverbTime()); - _scriptReverbOptions.setDamping(options->getDamping()); - _scriptReverbOptions.setSpread(options->getSpread()); - _scriptReverbOptions.setInputBandwidth(options->getInputBandwidth()); - _scriptReverbOptions.setEarlyLevel(options->getEarlyLevel()); - _scriptReverbOptions.setTailLevel(options->getTailLevel()); - - _scriptReverbOptions.setDryLevel(options->getDryLevel()); - _scriptReverbOptions.setWetLevel(options->getWetLevel()); + _scriptReverbOptions.setEarlyDiffusion(options->getEarlyDiffusion()); + _scriptReverbOptions.setLateDiffusion(options->getLateDiffusion()); + _scriptReverbOptions.setRoomSize(options->getRoomSize()); + _scriptReverbOptions.setDensity(options->getDensity()); + _scriptReverbOptions.setBassMult(options->getBassMult()); + _scriptReverbOptions.setBassFreq(options->getBassFreq()); + _scriptReverbOptions.setHighGain(options->getHighGain()); + _scriptReverbOptions.setHighFreq(options->getHighFreq()); + _scriptReverbOptions.setModRate(options->getModRate()); + _scriptReverbOptions.setModDepth(options->getModDepth()); + _scriptReverbOptions.setEarlyGain(options->getEarlyGain()); + _scriptReverbOptions.setLateGain(options->getLateGain()); + _scriptReverbOptions.setEarlyMixLeft(options->getEarlyMixLeft()); + _scriptReverbOptions.setEarlyMixRight(options->getEarlyMixRight()); + _scriptReverbOptions.setLateMixLeft(options->getLateMixLeft()); + _scriptReverbOptions.setLateMixRight(options->getLateMixRight()); + _scriptReverbOptions.setWetDryMix(options->getWetDryMix()); if (_reverbOptions == &_scriptReverbOptions) { // Apply them to the reverb instances diff --git a/libraries/audio/src/AudioEffectOptions.cpp b/libraries/audio/src/AudioEffectOptions.cpp index a61213d9c4..758875e896 100644 --- a/libraries/audio/src/AudioEffectOptions.cpp +++ b/libraries/audio/src/AudioEffectOptions.cpp @@ -10,58 +10,76 @@ #include "AudioEffectOptions.h" -static const QString MAX_ROOM_SIZE_HANDLE = "maxRoomSize"; -static const QString ROOM_SIZE_HANDLE = "roomSize"; +static const QString BANDWIDTH_HANDLE = "bandwidth"; +static const QString PRE_DELAY_HANDLE = "preDelay"; +static const QString LATE_DELAY_HANDLE = "lateDelay"; static const QString REVERB_TIME_HANDLE = "reverbTime"; -static const QString DAMPIMG_HANDLE = "damping"; -static const QString SPREAD_HANDLE = "spread"; -static const QString INPUT_BANDWIDTH_HANDLE = "inputBandwidth"; -static const QString EARLY_LEVEL_HANDLE = "earlyLevel"; -static const QString TAIL_LEVEL_HANDLE = "tailLevel"; -static const QString DRY_LEVEL_HANDLE = "dryLevel"; -static const QString WET_LEVEL_HANDLE = "wetLevel"; +static const QString EARLY_DIFFUSION_HANDLE = "earlyDiffusion"; +static const QString LATE_DIFFUSION_HANDLE = "lateDiffusion"; +static const QString ROOM_SIZE_HANDLE = "roomSize"; +static const QString DENSITY_HANDLE = "density"; +static const QString BASS_MULT_HANDLE = "bassMult"; +static const QString BASS_FREQ_HANDLE = "bassFreq"; +static const QString HIGH_GAIN_HANDLE = "highGain"; +static const QString HIGH_FREQ_HANDLE = "highFreq"; +static const QString MOD_RATE_HANDLE = "modRate"; +static const QString MOD_DEPTH_HANDLE = "modDepth"; +static const QString EARLY_GAIN_HANDLE = "earlyGain"; +static const QString LATE_GAIN_HANDLE = "lateGain"; +static const QString EARLY_MIX_LEFT_HANDLE = "earlyMixLeft"; +static const QString EARLY_MIX_RIGHT_HANDLE = "earlyMixRight"; +static const QString LATE_MIX_LEFT_HANDLE = "lateMixLeft"; +static const QString LATE_MIX_RIGHT_HANDLE = "lateMixRight"; +static const QString WET_DRY_MIX_HANDLE = "wetDryMix"; -AudioEffectOptions::AudioEffectOptions(QScriptValue arguments) : - _maxRoomSize(50.0f), - _roomSize(50.0f), - _reverbTime(4.0f), - _damping(0.5f), - _spread(15.0f), - _inputBandwidth(0.75f), - _earlyLevel(-12.0f), - _tailLevel(-18.0f), - _dryLevel(0.0f), - _wetLevel(0.0f) { - if (arguments.property(MAX_ROOM_SIZE_HANDLE).isNumber()) { - _maxRoomSize = arguments.property(MAX_ROOM_SIZE_HANDLE).toNumber(); - } - if (arguments.property(ROOM_SIZE_HANDLE).isNumber()) { - _roomSize = arguments.property(ROOM_SIZE_HANDLE).toNumber(); - } - if (arguments.property(REVERB_TIME_HANDLE).isNumber()) { - _reverbTime = arguments.property(REVERB_TIME_HANDLE).toNumber(); - } - if (arguments.property(DAMPIMG_HANDLE).isNumber()) { - _damping = arguments.property(DAMPIMG_HANDLE).toNumber(); - } - if (arguments.property(SPREAD_HANDLE).isNumber()) { - _spread = arguments.property(SPREAD_HANDLE).toNumber(); - } - if (arguments.property(INPUT_BANDWIDTH_HANDLE).isNumber()) { - _inputBandwidth = arguments.property(INPUT_BANDWIDTH_HANDLE).toNumber(); - } - if (arguments.property(EARLY_LEVEL_HANDLE).isNumber()) { - _earlyLevel = arguments.property(EARLY_LEVEL_HANDLE).toNumber(); - } - if (arguments.property(TAIL_LEVEL_HANDLE).isNumber()) { - _tailLevel = arguments.property(TAIL_LEVEL_HANDLE).toNumber(); - } - if (arguments.property(DRY_LEVEL_HANDLE).isNumber()) { - _dryLevel = arguments.property(DRY_LEVEL_HANDLE).toNumber(); - } - if (arguments.property(WET_LEVEL_HANDLE).isNumber()) { - _wetLevel = arguments.property(WET_LEVEL_HANDLE).toNumber(); - } +static const float BANDWIDTH_DEFAULT = 10000.0f; +static const float PRE_DELAY_DEFAULT = 20.0f; +static const float LATE_DELAY_DEFAULT = 0.0f; +static const float REVERB_TIME_DEFAULT = 2.0f; +static const float EARLY_DIFFUSION_DEFAULT = 100.0f; +static const float LATE_DIFFUSION_DEFAULT = 100.0f; +static const float ROOM_SIZE_DEFAULT = 50.0f; +static const float DENSITY_DEFAULT = 100.0f; +static const float BASS_MULT_DEFAULT = 1.5f; +static const float BASS_FREQ_DEFAULT = 250.0f; +static const float HIGH_GAIN_DEFAULT = -6.0f; +static const float HIGH_FREQ_DEFAULT = 3000.0f; +static const float MOD_RATE_DEFAULT = 2.3f; +static const float MOD_DEPTH_DEFAULT = 50.0f; +static const float EARLY_GAIN_DEFAULT = 0.0f; +static const float LATE_GAIN_DEFAULT = 0.0f; +static const float EARLY_MIX_LEFT_DEFAULT = 20.0f; +static const float EARLY_MIX_RIGHT_DEFAULT = 20.0f; +static const float LATE_MIX_LEFT_DEFAULT = 90.0f; +static const float LATE_MIX_RIGHT_DEFAULT = 90.0f; +static const float WET_DRY_MIX_DEFAULT = 50.0f; + +static void setOption(QScriptValue arguments, const QString name, float defaultValue, float& variable) { + variable = arguments.property(name).isNumber() ? arguments.property(name).toNumber() : defaultValue; +} + +AudioEffectOptions::AudioEffectOptions(QScriptValue arguments) { + setOption(arguments, BANDWIDTH_HANDLE, BANDWIDTH_DEFAULT, _bandwidth); + setOption(arguments, PRE_DELAY_HANDLE, PRE_DELAY_DEFAULT, _preDelay); + setOption(arguments, LATE_DELAY_HANDLE, LATE_DELAY_DEFAULT, _lateDelay); + setOption(arguments, REVERB_TIME_HANDLE, REVERB_TIME_DEFAULT, _reverbTime); + setOption(arguments, EARLY_DIFFUSION_HANDLE, EARLY_DIFFUSION_DEFAULT, _earlyDiffusion); + setOption(arguments, LATE_DIFFUSION_HANDLE, LATE_DIFFUSION_DEFAULT, _lateDiffusion); + setOption(arguments, ROOM_SIZE_HANDLE, ROOM_SIZE_DEFAULT, _roomSize); + setOption(arguments, DENSITY_HANDLE, DENSITY_DEFAULT, _density); + setOption(arguments, BASS_MULT_HANDLE, BASS_MULT_DEFAULT, _bassMult); + setOption(arguments, BASS_FREQ_HANDLE, BASS_FREQ_DEFAULT, _bassFreq); + setOption(arguments, HIGH_GAIN_HANDLE, HIGH_GAIN_DEFAULT, _highGain); + setOption(arguments, HIGH_FREQ_HANDLE, HIGH_FREQ_DEFAULT, _highFreq); + setOption(arguments, MOD_RATE_HANDLE, MOD_RATE_DEFAULT, _modRate); + setOption(arguments, MOD_DEPTH_HANDLE, MOD_DEPTH_DEFAULT, _modDepth); + setOption(arguments, EARLY_GAIN_HANDLE, EARLY_GAIN_DEFAULT, _earlyGain); + setOption(arguments, LATE_GAIN_HANDLE, LATE_GAIN_DEFAULT, _lateGain); + setOption(arguments, EARLY_MIX_LEFT_HANDLE, EARLY_MIX_LEFT_DEFAULT, _earlyMixLeft); + setOption(arguments, EARLY_MIX_RIGHT_HANDLE, EARLY_MIX_RIGHT_DEFAULT, _earlyMixRight); + setOption(arguments, LATE_MIX_LEFT_HANDLE, LATE_MIX_LEFT_DEFAULT, _lateMixLeft); + setOption(arguments, LATE_MIX_RIGHT_HANDLE, LATE_MIX_RIGHT_DEFAULT, _lateMixRight); + setOption(arguments, WET_DRY_MIX_HANDLE, WET_DRY_MIX_DEFAULT, _wetDryMix); } AudioEffectOptions::AudioEffectOptions(const AudioEffectOptions &other) : QObject() { @@ -69,17 +87,28 @@ AudioEffectOptions::AudioEffectOptions(const AudioEffectOptions &other) : QObjec } AudioEffectOptions& AudioEffectOptions::operator=(const AudioEffectOptions &other) { - _maxRoomSize = other._maxRoomSize; - _roomSize = other._roomSize; + _bandwidth = other._bandwidth; + _preDelay = other._preDelay; + _lateDelay = other._lateDelay; _reverbTime = other._reverbTime; - _damping = other._damping; - _spread = other._spread; - _inputBandwidth = other._inputBandwidth; - _earlyLevel = other._earlyLevel; - _tailLevel = other._tailLevel; - _dryLevel = other._dryLevel; - _wetLevel = other._wetLevel; - + _earlyDiffusion = other._earlyDiffusion; + _lateDiffusion = other._lateDiffusion; + _roomSize = other._roomSize; + _density = other._density; + _bassMult = other._bassMult; + _bassFreq = other._bassFreq; + _highGain = other._highGain; + _highFreq = other._highFreq; + _modRate = other._modRate; + _modDepth = other._modDepth; + _earlyGain = other._earlyGain; + _lateGain = other._lateGain; + _earlyMixLeft = other._earlyMixLeft; + _earlyMixRight = other._earlyMixRight; + _lateMixLeft = other._lateMixLeft; + _lateMixRight = other._lateMixRight; + _wetDryMix = other._wetDryMix; + return *this; } diff --git a/libraries/audio/src/AudioEffectOptions.h b/libraries/audio/src/AudioEffectOptions.h index be5e1cca5e..9a65301473 100644 --- a/libraries/audio/src/AudioEffectOptions.h +++ b/libraries/audio/src/AudioEffectOptions.h @@ -15,32 +15,30 @@ #include #include -#include "AudioReverb.h" - class AudioEffectOptions : public QObject { Q_OBJECT - // Meters Square - Q_PROPERTY(float maxRoomSize READ getMaxRoomSize WRITE setMaxRoomSize) - Q_PROPERTY(float roomSize READ getRoomSize WRITE setRoomSize) - - // Seconds + Q_PROPERTY(float bandwidth READ getBandwidth WRITE setBandwidth) + Q_PROPERTY(float preDelay READ getPreDelay WRITE setPreDelay) + Q_PROPERTY(float lateDelay READ getLateDelay WRITE setLateDelay) Q_PROPERTY(float reverbTime READ getReverbTime WRITE setReverbTime) - - // Ratio between 0 and 1 - Q_PROPERTY(float damping READ getDamping WRITE setDamping) - - // (?) Does not appear to be set externally very often - Q_PROPERTY(float spread READ getSpread WRITE setSpread) - - // Ratio between 0 and 1 - Q_PROPERTY(float inputBandwidth READ getInputBandwidth WRITE setInputBandwidth) - - // in dB - Q_PROPERTY(float earlyLevel READ getEarlyLevel WRITE setEarlyLevel) - Q_PROPERTY(float tailLevel READ getTailLevel WRITE setTailLevel) - Q_PROPERTY(float dryLevel READ getDryLevel WRITE setDryLevel) - Q_PROPERTY(float wetLevel READ getWetLevel WRITE setWetLevel) + Q_PROPERTY(float earlyDiffusion READ getEarlyDiffusion WRITE setEarlyDiffusion) + Q_PROPERTY(float lateDiffusion READ getLateDiffusion WRITE setLateDiffusion) + Q_PROPERTY(float roomSize READ getRoomSize WRITE setRoomSize) + Q_PROPERTY(float density READ getDensity WRITE setDensity) + Q_PROPERTY(float bassMult READ getBassMult WRITE setBassMult) + Q_PROPERTY(float bassFreq READ getBassFreq WRITE setBassFreq) + Q_PROPERTY(float highGain READ getHighGain WRITE setHighGain) + Q_PROPERTY(float highFreq READ getHighFreq WRITE setHighFreq) + Q_PROPERTY(float modRate READ getModRate WRITE setModRate) + Q_PROPERTY(float modDepth READ getModDepth WRITE setModDepth) + Q_PROPERTY(float earlyGain READ getEarlyGain WRITE setEarlyGain) + Q_PROPERTY(float lateGain READ getLateGain WRITE setLateGain) + Q_PROPERTY(float earlyMixLeft READ getEarlyMixLeft WRITE setEarlyMixLeft) + Q_PROPERTY(float earlyMixRight READ getEarlyMixRight WRITE setEarlyMixRight) + Q_PROPERTY(float lateMixLeft READ getLateMixLeft WRITE setLateMixLeft) + Q_PROPERTY(float lateMixRight READ getLateMixRight WRITE setLateMixRight) + Q_PROPERTY(float wetDryMix READ getWetDryMix WRITE setWetDryMix) public: AudioEffectOptions(QScriptValue arguments = QScriptValue()); @@ -49,60 +47,100 @@ public: static QScriptValue constructor(QScriptContext* context, QScriptEngine* engine); - float getRoomSize() const { return _roomSize; } - void setRoomSize(float roomSize ) { _roomSize = roomSize; } + float getBandwidth() const { return _bandwidth; } + void setBandwidth(float bandwidth) { _bandwidth = bandwidth; } - float getMaxRoomSize() const { return _maxRoomSize; } - void setMaxRoomSize(float maxRoomSize ) { _maxRoomSize = maxRoomSize; } + float getPreDelay() const { return _preDelay; } + void setPreDelay(float preDelay) { _preDelay = preDelay; } + + float getLateDelay() const { return _lateDelay; } + void setLateDelay(float lateDelay) { _lateDelay = lateDelay; } float getReverbTime() const { return _reverbTime; } - void setReverbTime(float reverbTime ) { _reverbTime = reverbTime; } + void setReverbTime(float reverbTime) { _reverbTime = reverbTime; } - float getDamping() const { return _damping; } - void setDamping(float damping ) { _damping = damping; } + float getEarlyDiffusion() const { return _earlyDiffusion; } + void setEarlyDiffusion(float earlyDiffusion) { _earlyDiffusion = earlyDiffusion; } - float getSpread() const { return _spread; } - void setSpread(float spread ) { _spread = spread; } + float getLateDiffusion() const { return _lateDiffusion; } + void setLateDiffusion(float lateDiffusion) { _lateDiffusion = lateDiffusion; } - float getInputBandwidth() const { return _inputBandwidth; } - void setInputBandwidth(float inputBandwidth ) { _inputBandwidth = inputBandwidth; } + float getRoomSize() const { return _roomSize; } + void setRoomSize(float roomSize) { _roomSize = roomSize; } - float getEarlyLevel() const { return _earlyLevel; } - void setEarlyLevel(float earlyLevel ) { _earlyLevel = earlyLevel; } + float getDensity() const { return _density; } + void setDensity(float density) { _density = density; } - float getTailLevel() const { return _tailLevel; } - void setTailLevel(float tailLevel ) { _tailLevel = tailLevel; } + float getBassMult() const { return _bassMult; } + void setBassMult(float bassMult) { _bassMult = bassMult; } - float getDryLevel() const { return _dryLevel; } - void setDryLevel(float dryLevel) { _dryLevel = dryLevel; } + float getBassFreq() const { return _bassFreq; } + void setBassFreq(float bassFreq) { _bassFreq = bassFreq; } - float getWetLevel() const { return _wetLevel; } - void setWetLevel(float wetLevel) { _wetLevel = wetLevel; } + float getHighGain() const { return _highGain; } + void setHighGain(float highGain) { _highGain = highGain; } + + float getHighFreq() const { return _highFreq; } + void setHighFreq(float highFreq) { _highFreq = highFreq; } + + float getModRate() const { return _modRate; } + void setModRate(float modRate) { _modRate = modRate; } + + float getModDepth() const { return _modDepth; } + void setModDepth(float modDepth) { _modDepth = modDepth; } + + float getEarlyGain() const { return _earlyGain; } + void setEarlyGain(float earlyGain) { _earlyGain = earlyGain; } + + float getLateGain() const { return _lateGain; } + void setLateGain(float lateGain) { _lateGain = lateGain; } + + float getEarlyMixLeft() const { return _earlyMixLeft; } + void setEarlyMixLeft(float earlyMixLeft) { _earlyMixLeft = earlyMixLeft; } + + float getEarlyMixRight() const { return _earlyMixRight; } + void setEarlyMixRight(float earlyMixRight) { _earlyMixRight = earlyMixRight; } + + float getLateMixLeft() const { return _lateMixLeft; } + void setLateMixLeft(float lateMixLeft) { _lateMixLeft = lateMixLeft; } + + float getLateMixRight() const { return _lateMixRight; } + void setLateMixRight(float lateMixRight) { _lateMixRight = lateMixRight; } + + float getWetDryMix() const { return _wetDryMix; } + void setWetDryMix(float wetDryMix) { _wetDryMix = wetDryMix; } private: - // http://wiki.audacityteam.org/wiki/GVerb#Instant_Reverberb_settings + float _bandwidth; // [20, 24000] Hz - // Meters Square - float _maxRoomSize; - float _roomSize; + float _preDelay; // [0, 333] ms + float _lateDelay; // [0, 166] ms - // Seconds - float _reverbTime; + float _reverbTime; // [0.1, 100] seconds - // Ratio between 0 and 1 - float _damping; + float _earlyDiffusion; // [0, 100] percent + float _lateDiffusion; // [0, 100] percent - // ? (Does not appear to be set externally very often) - float _spread; + float _roomSize; // [0, 100] percent + float _density; // [0, 100] percent - // Ratio between 0 and 1 - float _inputBandwidth; + float _bassMult; // [0.1, 10] ratio + float _bassFreq; // [10, 500] Hz + float _highGain; // [-24, 0] dB + float _highFreq; // [1000, 12000] Hz - // dB - float _earlyLevel; - float _tailLevel; - float _dryLevel; - float _wetLevel; + float _modRate; // [0.1, 10] Hz + float _modDepth; // [0, 100] percent + + float _earlyGain; // [-96, +24] dB + float _lateGain; // [-96, +24] dB + + float _earlyMixLeft; // [0, 100] percent + float _earlyMixRight; // [0, 100] percent + float _lateMixLeft; // [0, 100] percent + float _lateMixRight; // [0, 100] percent + + float _wetDryMix; // [0, 100] percent }; #endif // hifi_AudioEffectOptions_h diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 3f2cd92dbc..d241df7e0e 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -47,7 +47,7 @@ const QString AvatarData::FRAME_NAME = "com.highfidelity.recording.AvatarData"; static std::once_flag frameTypeRegistration; AvatarData::AvatarData() : - SpatiallyNestable(NestableTypes::Avatar, QUuid()), + SpatiallyNestable(NestableType::Avatar, QUuid()), _handPosition(0.0f), _targetScale(1.0f), _handState(0), diff --git a/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.cpp index 6c450e0735..71a885b31c 100644 --- a/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.cpp @@ -83,19 +83,18 @@ void Basic2DWindowOpenGLDisplayPlugin::internalPresent() { } WindowOpenGLDisplayPlugin::internalPresent(); } - +const uint32_t THROTTLED_FRAMERATE = 15; int Basic2DWindowOpenGLDisplayPlugin::getDesiredInterval() const { - static const int THROTTLED_PAINT_TIMER_DELAY_MS = MSECS_PER_SECOND / 15; static const int ULIMIITED_PAINT_TIMER_DELAY_MS = 1; int result = ULIMIITED_PAINT_TIMER_DELAY_MS; - if (_isThrottled) { - result = THROTTLED_PAINT_TIMER_DELAY_MS; - } if (0 != _framerateTarget) { result = MSECS_PER_SECOND / _framerateTarget; + } else if (_isThrottled) { + // This test wouldn't be necessary if we could depend on updateFramerate setting _framerateTarget. + // Alas, that gets complicated: isThrottled() is const and other stuff depends on it. + result = MSECS_PER_SECOND / THROTTLED_FRAMERATE; } - qDebug() << "New interval " << result; return result; } @@ -112,7 +111,6 @@ bool Basic2DWindowOpenGLDisplayPlugin::isThrottled() const { return shouldThrottle; } - void Basic2DWindowOpenGLDisplayPlugin::updateFramerate() { QAction* checkedFramerate{ nullptr }; foreach(auto action, _framerateActions) { @@ -134,11 +132,12 @@ void Basic2DWindowOpenGLDisplayPlugin::updateFramerate() { } else if (FRAMERATE_30 == actionText) { _framerateTarget = 30; } - } + } else if (_isThrottled) { + _framerateTarget = THROTTLED_FRAMERATE; + } int newInterval = getDesiredInterval(); - qDebug() << newInterval; - _timer.start(getDesiredInterval()); + _timer.start(newInterval); } // FIXME target the screen the window is currently on diff --git a/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.h index 36a1a73b94..e3633b5fe8 100644 --- a/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.h @@ -9,6 +9,8 @@ #include "WindowOpenGLDisplayPlugin.h" +const float TARGET_FRAMERATE_Basic2DWindowOpenGL = 60.0f; + class QScreen; class QAction; @@ -18,6 +20,8 @@ class Basic2DWindowOpenGLDisplayPlugin : public WindowOpenGLDisplayPlugin { public: virtual const QString & getName() const override; + virtual float getTargetFrameRate() override { return _framerateTarget ? (float) _framerateTarget : TARGET_FRAMERATE_Basic2DWindowOpenGL; } + virtual void activate() override; virtual void submitSceneTexture(uint32_t frameIndex, uint32_t sceneTexture, const glm::uvec2& sceneSize) override; diff --git a/libraries/display-plugins/src/display-plugins/DisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/DisplayPlugin.cpp index 6c34612e8c..08368597d0 100644 --- a/libraries/display-plugins/src/display-plugins/DisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/DisplayPlugin.cpp @@ -14,8 +14,6 @@ #include "stereo/InterleavedStereoDisplayPlugin.h" #include "Basic2DWindowOpenGLDisplayPlugin.h" -#include "openvr/OpenVrDisplayPlugin.h" - const QString& DisplayPlugin::MENU_PATH() { static const QString value = "Display"; return value; @@ -25,22 +23,14 @@ const QString& DisplayPlugin::MENU_PATH() { DisplayPluginList getDisplayPlugins() { DisplayPlugin* PLUGIN_POOL[] = { new Basic2DWindowOpenGLDisplayPlugin(), - new NullDisplayPlugin(), #ifdef DEBUG + new NullDisplayPlugin(), #endif - // Stereo modes - // SBS left/right new SideBySideStereoDisplayPlugin(), // Interleaved left/right new InterleavedStereoDisplayPlugin(), - - // HMDs -//#ifdef Q_OS_WIN -// // SteamVR SDK -// new OpenVrDisplayPlugin(), -//#endif nullptr }; diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp index 9a0db0ad97..880e4a16b3 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp @@ -15,6 +15,7 @@ #include #include +#include #include #include @@ -54,6 +55,7 @@ public: } virtual void run() override { + OpenGLDisplayPlugin* currentPlugin{ nullptr }; Q_ASSERT(_context); while (!_shutdown) { if (_pendingMainThreadOperation) { @@ -81,12 +83,13 @@ public: // Check if we have a new plugin to activate if (_newPlugin != nullptr) { // Deactivate the old plugin - if (_activePlugin != nullptr) { - _activePlugin->uncustomizeContext(); + if (currentPlugin != nullptr) { + currentPlugin->uncustomizeContext(); + currentPlugin->enableDeactivate(); } _newPlugin->customizeContext(); - _activePlugin = _newPlugin; + currentPlugin = _newPlugin; _newPlugin = nullptr; } _context->doneCurrent(); @@ -94,20 +97,25 @@ public: } // If there's no active plugin, just sleep - if (_activePlugin == nullptr) { + if (currentPlugin == nullptr) { QThread::usleep(100); continue; } // take the latest texture and present it _context->makeCurrent(); - _activePlugin->present(); - _context->doneCurrent(); + if (QOpenGLContext::currentContext() == _context->contextHandle()) { + currentPlugin->present(); + _context->doneCurrent(); + } else { + qWarning() << "Makecurrent failed"; + } } _context->makeCurrent(); - if (_activePlugin) { - _activePlugin->uncustomizeContext(); + if (currentPlugin) { + currentPlugin->uncustomizeContext(); + currentPlugin->enableDeactivate(); } _context->doneCurrent(); _context->moveToThread(qApp->thread()); @@ -147,7 +155,6 @@ private: bool _finishedMainThreadOperation { false }; QThread* _mainThread { nullptr }; OpenGLDisplayPlugin* _newPlugin { nullptr }; - OpenGLDisplayPlugin* _activePlugin { nullptr }; QGLContext* _context { nullptr }; }; @@ -208,11 +215,16 @@ void OpenGLDisplayPlugin::stop() { } void OpenGLDisplayPlugin::deactivate() { + { + Lock lock(_mutex); + _deactivateWait.wait(lock, [&]{ return _uncustomized; }); + } _timer.stop(); DisplayPlugin::deactivate(); } void OpenGLDisplayPlugin::customizeContext() { + _uncustomized = false; auto presentThread = DependencyManager::get(); Q_ASSERT(thread() == presentThread->thread()); @@ -233,6 +245,7 @@ void OpenGLDisplayPlugin::uncustomizeContext() { _plane.reset(); } + // Pressing Alt (and Meta) key alone activates the menubar because its style inherits the // SHMenuBarAltKeyNavigation from QWindowsStyle. This makes it impossible for a scripts to // receive keyPress events for the Alt (and Meta) key in a reliable manner. @@ -380,3 +393,9 @@ QImage OpenGLDisplayPlugin::getScreenshot() const { }); return result; } + +void OpenGLDisplayPlugin::enableDeactivate() { + Lock lock(_mutex); + _uncustomized = true; + _deactivateWait.notify_one(); +} \ No newline at end of file diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h index ef78374994..72211056d9 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h @@ -9,6 +9,8 @@ #include "DisplayPlugin.h" +#include + #include #include @@ -18,8 +20,9 @@ class OpenGLDisplayPlugin : public DisplayPlugin { protected: - using Mutex = std::recursive_mutex; + using Mutex = std::mutex; using Lock = std::unique_lock; + using Condition = std::condition_variable; public: OpenGLDisplayPlugin(); virtual void activate() override; @@ -71,7 +74,7 @@ protected: ProgramPtr _program; ShapeWrapperPtr _plane; - Mutex _mutex; + mutable Mutex _mutex; SimpleMovingAverage _usecsPerFrame { 10 }; QMap _sceneTextureToFrameIndexMap; @@ -82,6 +85,12 @@ protected: GLTextureEscrow _sceneTextureEscrow; bool _vsyncSupported { false }; + +private: + void enableDeactivate(); + Condition _deactivateWait; + bool _uncustomized{ false }; + }; diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.h b/libraries/entities-renderer/src/EntityTreeRenderer.h index 9980b9d8b6..076fe26d6f 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.h +++ b/libraries/entities-renderer/src/EntityTreeRenderer.h @@ -30,7 +30,7 @@ class ZoneEntityItem; // Generic client side Octree renderer class. -class EntityTreeRenderer : public OctreeRenderer, public EntityItemFBXService { +class EntityTreeRenderer : public OctreeRenderer, public EntityItemFBXService, public Dependency { Q_OBJECT public: EntityTreeRenderer(bool wantScripts, AbstractViewStateInterface* viewState, diff --git a/libraries/entities-renderer/src/RenderableBoxEntityItem.cpp b/libraries/entities-renderer/src/RenderableBoxEntityItem.cpp index 588f17d959..54fe491c46 100644 --- a/libraries/entities-renderer/src/RenderableBoxEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableBoxEntityItem.cpp @@ -41,6 +41,7 @@ void RenderableBoxEntityItem::render(RenderArgs* args) { Q_ASSERT(getType() == EntityTypes::Box); Q_ASSERT(args->_batch); + if (!_procedural) { _procedural.reset(new Procedural(this->getUserData())); _procedural->_vertexSource = simple_vert; @@ -64,4 +65,6 @@ void RenderableBoxEntityItem::render(RenderArgs* args) { } else { DependencyManager::get()->renderSolidCubeInstance(batch, getTransformToCenter(), cubeColor); } -}; + static const auto triCount = DependencyManager::get()->getCubeTriangleCount(); + args->_details._trianglesRendered += triCount; +} diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index 5b4704cabb..b5203ea460 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -12,12 +12,14 @@ #include #include +#include #include #include #include #include #include +#include #include "EntityTreeRenderer.h" #include "EntitiesRendererLogging.h" @@ -44,6 +46,32 @@ RenderableModelEntityItem::~RenderableModelEntityItem() { } } +void RenderableModelEntityItem::setModelURL(const QString& url) { + auto& currentURL = getParsedModelURL(); + ModelEntityItem::setModelURL(url); + + if (currentURL != getParsedModelURL() || !_model) { + EntityTreePointer tree = getTree(); + if (tree) { + QMetaObject::invokeMethod(tree.get(), "callLoader", Qt::QueuedConnection, Q_ARG(EntityItemID, getID())); + } + } +} + +void RenderableModelEntityItem::loader() { + _needsModelReload = true; + EntityTreeRenderer* renderer = DependencyManager::get().data(); + assert(renderer); + if (!_model || _needsModelReload) { + PerformanceTimer perfTimer("getModel"); + getModel(renderer); + } + if (_model) { + _model->setURL(getParsedModelURL()); + _model->setCollisionModelURL(QUrl(getCompoundShapeURL())); + } +} + void RenderableModelEntityItem::setDimensions(const glm::vec3& value) { _dimensionsInitialized = true; ModelEntityItem::setDimensions(value); @@ -223,7 +251,7 @@ void RenderableModelEntityItem::render(RenderArgs* args) { // check if the URL has changed auto& currentURL = getParsedModelURL(); if (currentURL != _model->getURL()) { - qDebug().noquote() << "Updating model URL: " << currentURL.toDisplayString(); + qCDebug(entitiesrenderer).noquote() << "Updating model URL: " << currentURL.toDisplayString(); _model->setURL(currentURL); } @@ -318,7 +346,7 @@ void RenderableModelEntityItem::render(RenderArgs* args) { Model* RenderableModelEntityItem::getModel(EntityTreeRenderer* renderer) { Model* result = NULL; - + if (!renderer) { return result; } @@ -340,7 +368,8 @@ Model* RenderableModelEntityItem::getModel(EntityTreeRenderer* renderer) { // if we have a previously allocated model, but its URL doesn't match // then we need to let our renderer update our model for us. - if (_model && QUrl(getModelURL()) != _model->getURL()) { + if (_model && (QUrl(getModelURL()) != _model->getURL() || + QUrl(getCompoundShapeURL()) != _model->getCollisionURL())) { result = _model = _myRenderer->updateModel(_model, getModelURL(), getCompoundShapeURL()); _needsInitialSimulation = true; } else if (!_model) { // if we don't yet have a model, then we want our renderer to allocate one @@ -403,25 +432,29 @@ bool RenderableModelEntityItem::findDetailedRayIntersection(const glm::vec3& ori } void RenderableModelEntityItem::setCompoundShapeURL(const QString& url) { + auto currentCompoundShapeURL = getCompoundShapeURL(); ModelEntityItem::setCompoundShapeURL(url); - if (_model) { - _model->setCollisionModelURL(QUrl(url)); + + if (getCompoundShapeURL() != currentCompoundShapeURL || !_model) { + EntityTreePointer tree = getTree(); + if (tree) { + QMetaObject::invokeMethod(tree.get(), "callLoader", Qt::QueuedConnection, Q_ARG(EntityItemID, getID())); + } } } bool RenderableModelEntityItem::isReadyToComputeShape() { ShapeType type = getShapeType(); + if (type == SHAPE_TYPE_COMPOUND) { - if (!_model) { + EntityTreePointer tree = getTree(); + if (tree) { + QMetaObject::invokeMethod(tree.get(), "callLoader", Qt::QueuedConnection, Q_ARG(EntityItemID, getID())); + } return false; // hmm... } - if (_needsInitialSimulation) { - // the _model's offset will be wrong until _needsInitialSimulation is false - return false; - } - assert(!_model->getCollisionURL().isEmpty()); if (_model->getURL().isEmpty()) { @@ -435,6 +468,14 @@ bool RenderableModelEntityItem::isReadyToComputeShape() { if ((collisionNetworkGeometry && collisionNetworkGeometry->isLoaded()) && (renderNetworkGeometry && renderNetworkGeometry->isLoaded())) { // we have both URLs AND both geometries AND they are both fully loaded. + + if (_needsInitialSimulation) { + // the _model's offset will be wrong until _needsInitialSimulation is false + PerformanceTimer perfTimer("_model->simulate"); + _model->simulate(0.0f); + _needsInitialSimulation = false; + } + return true; } diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.h b/libraries/entities-renderer/src/RenderableModelEntityItem.h index 5c05dc5b44..0aaa7a0977 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.h +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.h @@ -29,7 +29,8 @@ public: virtual ~RenderableModelEntityItem(); virtual void setDimensions(const glm::vec3& value) override; - + virtual void setModelURL(const QString& url); + virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const override; virtual bool setProperties(const EntityItemProperties& properties) override; virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, @@ -71,6 +72,8 @@ public: virtual glm::quat getAbsoluteJointRotationInObjectFrame(int index) const override; virtual glm::vec3 getAbsoluteJointTranslationInObjectFrame(int index) const override; + virtual void loader() override; + private: void remapTextures(); diff --git a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp index 43c6373147..520069abdf 100644 --- a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp @@ -23,46 +23,73 @@ #include "untextured_particle_frag.h" #include "textured_particle_vert.h" #include "textured_particle_frag.h" -#include "textured_particle_alpha_discard_frag.h" -class ParticlePayload { + +class ParticlePayloadData { public: - typedef render::Payload Payload; - typedef Payload::DataPointer Pointer; - typedef RenderableParticleEffectEntityItem::Vertex Vertex; + static const size_t VERTEX_PER_PARTICLE = 4; - ParticlePayload(EntityItemPointer entity) : - _entity(entity), - _vertexFormat(std::make_shared()), - _vertexBuffer(std::make_shared()), - _indexBuffer(std::make_shared()) { - - _vertexFormat->setAttribute(gpu::Stream::POSITION, 0, gpu::Element::VEC3F_XYZ, 0); - _vertexFormat->setAttribute(gpu::Stream::TEXCOORD, 0, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV), offsetof(Vertex, uv)); - _vertexFormat->setAttribute(gpu::Stream::COLOR, 0, gpu::Element::COLOR_RGBA_32, offsetof(Vertex, rgba)); + template + struct InterpolationData { + T start; + T middle; + T finish; + T spread; + }; + struct ParticleUniforms { + InterpolationData radius; + InterpolationData color; // rgba + float lifespan; + }; + + struct ParticlePrimitive { + ParticlePrimitive(glm::vec3 xyzIn, glm::vec2 uvIn) : xyz(xyzIn), uv(uvIn) {} + glm::vec3 xyz; // Position + glm::vec2 uv; // Lifetime + seed + }; + + using Payload = render::Payload; + using Pointer = Payload::DataPointer; + using PipelinePointer = gpu::PipelinePointer; + using FormatPointer = gpu::Stream::FormatPointer; + using BufferPointer = gpu::BufferPointer; + using TexturePointer = gpu::TexturePointer; + using Format = gpu::Stream::Format; + using Buffer = gpu::Buffer; + using BufferView = gpu::BufferView; + using ParticlePrimitives = std::vector; + + ParticlePayloadData() { + ParticleUniforms uniforms; + _uniformBuffer = std::make_shared(sizeof(ParticleUniforms), (const gpu::Byte*) &uniforms); + + _vertexFormat->setAttribute(gpu::Stream::POSITION, 0, gpu::Element::VEC3F_XYZ, + offsetof(ParticlePrimitive, xyz), gpu::Stream::PER_INSTANCE); + _vertexFormat->setAttribute(gpu::Stream::COLOR, 0, gpu::Element::VEC2F_UV, + offsetof(ParticlePrimitive, uv), gpu::Stream::PER_INSTANCE); } - void setPipeline(gpu::PipelinePointer pipeline) { _pipeline = pipeline; } - const gpu::PipelinePointer& getPipeline() const { return _pipeline; } + void setPipeline(PipelinePointer pipeline) { _pipeline = pipeline; } + const PipelinePointer& getPipeline() const { return _pipeline; } const Transform& getModelTransform() const { return _modelTransform; } void setModelTransform(const Transform& modelTransform) { _modelTransform = modelTransform; } const AABox& getBound() const { return _bound; } - void setBound(AABox& bound) { _bound = bound; } + void setBound(const AABox& bound) { _bound = bound; } - gpu::BufferPointer getVertexBuffer() { return _vertexBuffer; } - const gpu::BufferPointer& getVertexBuffer() const { return _vertexBuffer; } + BufferPointer getParticleBuffer() { return _particleBuffer; } + const BufferPointer& getParticleBuffer() const { return _particleBuffer; } + + const ParticleUniforms& getParticleUniforms() const { return _uniformBuffer.get(); } + ParticleUniforms& editParticleUniforms() { return _uniformBuffer.edit(); } - gpu::BufferPointer getIndexBuffer() { return _indexBuffer; } - const gpu::BufferPointer& getIndexBuffer() const { return _indexBuffer; } - - void setTexture(gpu::TexturePointer texture) { _texture = texture; } - const gpu::TexturePointer& getTexture() const { return _texture; } + void setTexture(TexturePointer texture) { _texture = texture; } + const TexturePointer& getTexture() const { return _texture; } bool getVisibleFlag() const { return _visibleFlag; } void setVisibleFlag(bool visibleFlag) { _visibleFlag = visibleFlag; } - + void render(RenderArgs* args) const { assert(_pipeline); @@ -74,29 +101,28 @@ public: } batch.setModelTransform(_modelTransform); + batch.setUniformBuffer(0, _uniformBuffer); batch.setInputFormat(_vertexFormat); - batch.setInputBuffer(0, _vertexBuffer, 0, sizeof(Vertex)); - batch.setIndexBuffer(gpu::UINT16, _indexBuffer, 0); + batch.setInputBuffer(0, _particleBuffer, 0, sizeof(ParticlePrimitive)); - auto numIndices = _indexBuffer->getSize() / sizeof(uint16_t); - batch.drawIndexed(gpu::TRIANGLES, numIndices); + auto numParticles = _particleBuffer->getSize() / sizeof(ParticlePrimitive); + batch.drawInstanced(numParticles, gpu::TRIANGLE_STRIP, VERTEX_PER_PARTICLE); } protected: - EntityItemPointer _entity; Transform _modelTransform; AABox _bound; - gpu::PipelinePointer _pipeline; - gpu::Stream::FormatPointer _vertexFormat; - gpu::BufferPointer _vertexBuffer; - gpu::BufferPointer _indexBuffer; - gpu::TexturePointer _texture; + PipelinePointer _pipeline; + FormatPointer _vertexFormat { std::make_shared() }; + BufferPointer _particleBuffer { std::make_shared() }; + BufferView _uniformBuffer; + TexturePointer _texture; bool _visibleFlag = true; }; namespace render { template <> - const ItemKey payloadGetKey(const ParticlePayload::Pointer& payload) { + const ItemKey payloadGetKey(const ParticlePayloadData::Pointer& payload) { if (payload->getVisibleFlag()) { return ItemKey::Builder::transparentShape(); } else { @@ -105,13 +131,15 @@ namespace render { } template <> - const Item::Bound payloadGetBound(const ParticlePayload::Pointer& payload) { + const Item::Bound payloadGetBound(const ParticlePayloadData::Pointer& payload) { return payload->getBound(); } template <> - void payloadRender(const ParticlePayload::Pointer& payload, RenderArgs* args) { - payload->render(args); + void payloadRender(const ParticlePayloadData::Pointer& payload, RenderArgs* args) { + if (payload->getVisibleFlag()) { + payload->render(args); + } } } @@ -119,7 +147,7 @@ namespace render { EntityItemPointer RenderableParticleEffectEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { - EntityItemPointer entity{ new RenderableParticleEffectEntityItem(entityID) }; + auto entity = std::make_shared(entityID); entity->setProperties(properties); return entity; } @@ -127,7 +155,7 @@ EntityItemPointer RenderableParticleEffectEntityItem::factory(const EntityItemID RenderableParticleEffectEntityItem::RenderableParticleEffectEntityItem(const EntityItemID& entityItemID) : ParticleEffectEntityItem(entityItemID) { // lazy creation of particle system pipeline - if (!_untexturedPipeline && !_texturedPipeline) { + if (!_untexturedPipeline || !_texturedPipeline) { createPipelines(); } } @@ -135,18 +163,15 @@ RenderableParticleEffectEntityItem::RenderableParticleEffectEntityItem(const Ent bool RenderableParticleEffectEntityItem::addToScene(EntityItemPointer self, render::ScenePointer scene, render::PendingChanges& pendingChanges) { - - auto particlePayload = - std::shared_ptr(new ParticlePayload(getThisPointer())); - particlePayload->setPipeline(_untexturedPipeline); - _renderItemId = scene->allocateID(); - auto renderData = ParticlePayload::Pointer(particlePayload); - auto renderPayload = render::PayloadPointer(new ParticlePayload::Payload(renderData)); + _scene = scene; + _renderItemId = _scene->allocateID(); + auto particlePayloadData = std::make_shared(); + particlePayloadData->setPipeline(_untexturedPipeline); + auto renderPayload = std::make_shared(particlePayloadData); render::Item::Status::Getters statusGetters; makeEntityItemStatusGetters(getThisPointer(), statusGetters); renderPayload->addStatusGetters(statusGetters); pendingChanges.resetItem(_renderItemId, renderPayload); - _scene = scene; return true; } @@ -174,141 +199,71 @@ void RenderableParticleEffectEntityItem::update(const quint64& now) { updateRenderItem(); } -uint32_t toRGBA(uint8_t r, uint8_t g, uint8_t b, uint8_t a) { - return ((uint32_t)r | (uint32_t)g << 8 | (uint32_t)b << 16 | (uint32_t)a << 24); -} - -class ParticleDetails { -public: - ParticleDetails(glm::vec3 position, float radius, uint32_t rgba) : position(position), radius(radius), rgba(rgba) { } - - glm::vec3 position; - float radius; - uint32_t rgba; -}; - -static glm::vec3 zSortAxis; -static bool zSort(const ParticleDetails& rhs, const ParticleDetails& lhs) { - return glm::dot(rhs.position, ::zSortAxis) > glm::dot(lhs.position, ::zSortAxis); -} - void RenderableParticleEffectEntityItem::updateRenderItem() { if (!_scene) { return; } - - // make a copy of each particle's details - std::vector particleDetails; - particleDetails.reserve(getLivingParticleCount()); - for (quint32 i = _particleHeadIndex; i != _particleTailIndex; i = (i + 1) % _maxParticles) { - auto xcolor = _particleColors[i]; - auto alpha = (uint8_t)(glm::clamp(_particleAlphas[i] * getLocalRenderAlpha(), 0.0f, 1.0f) * 255.0f); - auto rgba = toRGBA(xcolor.red, xcolor.green, xcolor.blue, alpha); - particleDetails.push_back(ParticleDetails(_particlePositions[i], _particleRadiuses[i], rgba)); + if (!getVisible()) { + render::PendingChanges pendingChanges; + pendingChanges.updateItem(_renderItemId, [](ParticlePayloadData& payload) { + payload.setVisibleFlag(false); + }); + + _scene->enqueuePendingChanges(pendingChanges); + return; } - - // sort particles back to front - // NOTE: this is view frustum might be one frame out of date. - auto frustum = AbstractViewStateInterface::instance()->getCurrentViewFrustum(); + using ParticleUniforms = ParticlePayloadData::ParticleUniforms; + using ParticlePrimitive = ParticlePayloadData::ParticlePrimitive; + using ParticlePrimitives = ParticlePayloadData::ParticlePrimitives; - // No need to sort if we're doing additive blending - if (_additiveBlending != true) { - ::zSortAxis = frustum->getDirection(); - qSort(particleDetails.begin(), particleDetails.end(), zSort); - } - - - - // allocate vertices - _vertices.clear(); - - // build vertices from particle positions and radiuses - glm::vec3 dir = frustum->getDirection(); - for (auto&& particle : particleDetails) { - glm::vec3 right = glm::normalize(glm::cross(glm::vec3(0.0f, 1.0f, 0.0f), dir)); - glm::vec3 up = glm::normalize(glm::cross(right, dir)); - - glm::vec3 upOffset = up * particle.radius; - glm::vec3 rightOffset = right * particle.radius; - // generate corners of quad aligned to face the camera. - _vertices.emplace_back(particle.position + rightOffset + upOffset, glm::vec2(1.0f, 1.0f), particle.rgba); - _vertices.emplace_back(particle.position - rightOffset + upOffset, glm::vec2(0.0f, 1.0f), particle.rgba); - _vertices.emplace_back(particle.position - rightOffset - upOffset, glm::vec2(0.0f, 0.0f), particle.rgba); - _vertices.emplace_back(particle.position + rightOffset - upOffset, glm::vec2(1.0f, 0.0f), particle.rgba); + // Fill in Uniforms structure + ParticleUniforms particleUniforms; + particleUniforms.radius.start = getRadiusStart(); + particleUniforms.radius.middle = getParticleRadius(); + particleUniforms.radius.finish = getRadiusFinish(); + particleUniforms.radius.spread = getRadiusSpread(); + particleUniforms.color.start = toGlm(getColorStart(), getAlphaStart()); + particleUniforms.color.middle = toGlm(getXColor(), getAlpha()); + particleUniforms.color.finish = toGlm(getColorFinish(), getAlphaFinish()); + particleUniforms.color.spread = toGlm(getColorSpread(), getAlphaSpread()); + particleUniforms.lifespan = getLifespan(); + + // Build particle primitives + auto particlePrimitives = std::make_shared(); + particlePrimitives->reserve(_particles.size()); // Reserve space + for (auto& particle : _particles) { + particlePrimitives->emplace_back(particle.position, glm::vec2(particle.lifetime, particle.seed)); } + + auto bounds = getAABox(); + auto position = getPosition(); + auto rotation = getRotation(); + Transform transform; + transform.setTranslation(position); + transform.setRotation(rotation); render::PendingChanges pendingChanges; - pendingChanges.updateItem(_renderItemId, [this](ParticlePayload& payload) { - // update vertex buffer - auto vertexBuffer = payload.getVertexBuffer(); - size_t numBytes = sizeof(Vertex) * _vertices.size(); - + pendingChanges.updateItem(_renderItemId, [=](ParticlePayloadData& payload) { + payload.setVisibleFlag(true); + + // Update particle uniforms + memcpy(&payload.editParticleUniforms(), &particleUniforms, sizeof(ParticleUniforms)); + + // Update particle buffer + auto particleBuffer = payload.getParticleBuffer(); + size_t numBytes = sizeof(ParticlePrimitive) * particlePrimitives->size(); + particleBuffer->resize(numBytes); if (numBytes == 0) { - vertexBuffer->resize(0); - auto indexBuffer = payload.getIndexBuffer(); - indexBuffer->resize(0); return; } + memcpy(particleBuffer->editData(), particlePrimitives->data(), numBytes); - vertexBuffer->resize(numBytes); - gpu::Byte* data = vertexBuffer->editData(); - memcpy(data, &(_vertices[0]), numBytes); + // Update transform and bounds + payload.setModelTransform(transform); + payload.setBound(bounds); - // FIXME, don't update index buffer if num particles has not changed. - // update index buffer - auto indexBuffer = payload.getIndexBuffer(); - const size_t NUM_VERTS_PER_PARTICLE = 4; - const size_t NUM_INDICES_PER_PARTICLE = 6; - auto numQuads = (_vertices.size() / NUM_VERTS_PER_PARTICLE); - numBytes = sizeof(uint16_t) * numQuads * NUM_INDICES_PER_PARTICLE; - indexBuffer->resize(numBytes); - data = indexBuffer->editData(); - auto indexPtr = reinterpret_cast(data); - for (size_t i = 0; i < numQuads; ++i) { - indexPtr[i * NUM_INDICES_PER_PARTICLE + 0] = i * NUM_VERTS_PER_PARTICLE + 0; - indexPtr[i * NUM_INDICES_PER_PARTICLE + 1] = i * NUM_VERTS_PER_PARTICLE + 1; - indexPtr[i * NUM_INDICES_PER_PARTICLE + 2] = i * NUM_VERTS_PER_PARTICLE + 3; - indexPtr[i * NUM_INDICES_PER_PARTICLE + 3] = i * NUM_VERTS_PER_PARTICLE + 1; - indexPtr[i * NUM_INDICES_PER_PARTICLE + 4] = i * NUM_VERTS_PER_PARTICLE + 2; - indexPtr[i * NUM_INDICES_PER_PARTICLE + 5] = i * NUM_VERTS_PER_PARTICLE + 3; - } - - // update transform - glm::quat rot = getRotation(); - glm::vec3 pos = getPosition(); - Transform t; - t.setRotation(rot); - payload.setModelTransform(t); - - // transform _particleMinBound and _particleMaxBound corners into world coords - glm::vec3 d = _particleMaxBound - _particleMinBound; - const size_t NUM_BOX_CORNERS = 8; - glm::vec3 corners[NUM_BOX_CORNERS] = { - pos + rot * (_particleMinBound + glm::vec3(0.0f, 0.0f, 0.0f)), - pos + rot * (_particleMinBound + glm::vec3(d.x, 0.0f, 0.0f)), - pos + rot * (_particleMinBound + glm::vec3(0.0f, d.y, 0.0f)), - pos + rot * (_particleMinBound + glm::vec3(d.x, d.y, 0.0f)), - pos + rot * (_particleMinBound + glm::vec3(0.0f, 0.0f, d.z)), - pos + rot * (_particleMinBound + glm::vec3(d.x, 0.0f, d.z)), - pos + rot * (_particleMinBound + glm::vec3(0.0f, d.y, d.z)), - pos + rot * (_particleMinBound + glm::vec3(d.x, d.y, d.z)) - }; - glm::vec3 min(FLT_MAX, FLT_MAX, FLT_MAX); - glm::vec3 max = -min; - for (size_t i = 0; i < NUM_BOX_CORNERS; i++) { - min.x = std::min(min.x, corners[i].x); - min.y = std::min(min.y, corners[i].y); - min.z = std::min(min.z, corners[i].z); - max.x = std::max(max.x, corners[i].x); - max.y = std::max(max.y, corners[i].y); - max.z = std::max(max.z, corners[i].z); - } - AABox bound(min, max - min); - payload.setBound(bound); - - bool textured = _texture && _texture->isLoaded(); - if (textured) { + if (_texture && _texture->isLoaded()) { payload.setTexture(_texture->getGPUTexture()); payload.setPipeline(_texturedPipeline); } else { @@ -321,46 +276,29 @@ void RenderableParticleEffectEntityItem::updateRenderItem() { } void RenderableParticleEffectEntityItem::createPipelines() { - bool writeToDepthBuffer = false; - gpu::State::BlendArg destinationColorBlendArg; - if (_additiveBlending) { - destinationColorBlendArg = gpu::State::ONE; - } - else { - destinationColorBlendArg = gpu::State::INV_SRC_ALPHA; - writeToDepthBuffer = true; - } if (!_untexturedPipeline) { auto state = std::make_shared(); state->setCullMode(gpu::State::CULL_BACK); - state->setDepthTest(true, writeToDepthBuffer, gpu::LESS_EQUAL); - state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, - destinationColorBlendArg, gpu::State::FACTOR_ALPHA, - gpu::State::BLEND_OP_ADD, gpu::State::ONE); + state->setDepthTest(true, false, gpu::LESS_EQUAL); + state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE, + gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE); + auto vertShader = gpu::Shader::createVertex(std::string(untextured_particle_vert)); auto fragShader = gpu::Shader::createPixel(std::string(untextured_particle_frag)); + auto program = gpu::Shader::createProgram(vertShader, fragShader); _untexturedPipeline = gpu::Pipeline::create(program, state); } if (!_texturedPipeline) { auto state = std::make_shared(); state->setCullMode(gpu::State::CULL_BACK); - + state->setDepthTest(true, false, gpu::LESS_EQUAL); + state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE, + gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE); - bool writeToDepthBuffer = !_additiveBlending; - state->setDepthTest(true, writeToDepthBuffer, gpu::LESS_EQUAL); - state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, - destinationColorBlendArg, gpu::State::FACTOR_ALPHA, - gpu::State::BLEND_OP_ADD, gpu::State::ONE); auto vertShader = gpu::Shader::createVertex(std::string(textured_particle_vert)); - gpu::ShaderPointer fragShader; - if (_additiveBlending) { - fragShader = gpu::Shader::createPixel(std::string(textured_particle_frag)); - } - else { - //If we are sorting and have no additive blending, we want to discard pixels with low alpha to avoid inter-particle entity artifacts - fragShader = gpu::Shader::createPixel(std::string(textured_particle_alpha_discard_frag)); - } + auto fragShader = gpu::Shader::createPixel(std::string(textured_particle_frag)); + auto program = gpu::Shader::createProgram(vertShader, fragShader); _texturedPipeline = gpu::Pipeline::create(program, state); } diff --git a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.h b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.h index b024ddb991..0c1cc50a98 100644 --- a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.h +++ b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.h @@ -16,7 +16,7 @@ #include "RenderableEntityItem.h" class RenderableParticleEffectEntityItem : public ParticleEffectEntityItem { -friend class ParticlePayload; + friend class ParticlePayloadData; public: static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties); RenderableParticleEffectEntityItem(const EntityItemID& entityItemID); @@ -29,23 +29,14 @@ public: virtual void removeFromScene(EntityItemPointer self, render::ScenePointer scene, render::PendingChanges& pendingChanges) override; protected: - render::ItemID _renderItemId; - - struct Vertex { - Vertex(glm::vec3 xyzIn, glm::vec2 uvIn, uint32_t rgbaIn) : xyz(xyzIn), uv(uvIn), rgba(rgbaIn) {} - glm::vec3 xyz; - glm::vec2 uv; - uint32_t rgba; - }; - void createPipelines(); - - std::vector _vertices; + + render::ScenePointer _scene; + render::ItemID _renderItemId; + + NetworkTexturePointer _texture; gpu::PipelinePointer _untexturedPipeline; gpu::PipelinePointer _texturedPipeline; - - render::ScenePointer _scene; - NetworkTexturePointer _texture; }; diff --git a/libraries/entities-renderer/src/RenderableSphereEntityItem.cpp b/libraries/entities-renderer/src/RenderableSphereEntityItem.cpp index 2bdcfc19ad..8768e41a07 100644 --- a/libraries/entities-renderer/src/RenderableSphereEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableSphereEntityItem.cpp @@ -71,4 +71,6 @@ void RenderableSphereEntityItem::render(RenderArgs* args) { batch.setModelTransform(Transform()); DependencyManager::get()->renderSolidSphereInstance(batch, modelTransform, sphereColor); } -}; + static const auto triCount = DependencyManager::get()->getSphereTriangleCount(); + args->_details._trianglesRendered += triCount; +} diff --git a/libraries/entities-renderer/src/textured_particle.slf b/libraries/entities-renderer/src/textured_particle.slf index 58a3103323..e139c7cc01 100644 --- a/libraries/entities-renderer/src/textured_particle.slf +++ b/libraries/entities-renderer/src/textured_particle.slf @@ -11,12 +11,11 @@ uniform sampler2D colorMap; -in vec4 _color; -in vec2 _texCoord0; +in vec4 varColor; +in vec2 varTexcoord; out vec4 outFragColor; void main(void) { - vec4 color = texture(colorMap, _texCoord0); - outFragColor = color * _color; + outFragColor = texture(colorMap, varTexcoord.xy) * varColor; } diff --git a/libraries/entities-renderer/src/textured_particle.slv b/libraries/entities-renderer/src/textured_particle.slv index c741af559d..1e9275ec72 100644 --- a/libraries/entities-renderer/src/textured_particle.slv +++ b/libraries/entities-renderer/src/textured_particle.slv @@ -10,21 +10,84 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -<@include gpu/Inputs.slh@> - <@include gpu/Transform.slh@> <$declareStandardTransform()$> -out vec4 _color; -out vec2 _texCoord0; +struct Radii { + float start; + float middle; + float finish; + float spread; +}; +struct Colors { + vec4 start; + vec4 middle; + vec4 finish; + vec4 spread; +}; + +struct ParticleUniforms { + Radii radius; + Colors color; + float lifespan; +}; + +uniform particleBuffer { + ParticleUniforms particle; +}; + +in vec3 inPosition; +in vec2 inColor; // This is actual Lifetime + Seed + +out vec4 varColor; +out vec2 varTexcoord; + +const int NUM_VERTICES_PER_PARTICLE = 4; +const vec4 UNIT_QUAD[NUM_VERTICES_PER_PARTICLE] = vec4[NUM_VERTICES_PER_PARTICLE]( + vec4(-1.0, -1.0, 0.0, 0.0), + vec4(1.0, -1.0, 0.0, 0.0), + vec4(-1.0, 1.0, 0.0, 0.0), + vec4(1.0, 1.0, 0.0, 0.0) +); + +float bezierInterpolate(float y1, float y2, float y3, float u) { + // https://en.wikipedia.org/wiki/Bezier_curve + return (1.0 - u) * (1.0 - u) * y1 + 2.0 * (1.0 - u) * u * y2 + u * u * y3; +} + +vec4 interpolate3Vec4(vec4 y1, vec4 y2, vec4 y3, float u) { + return vec4(bezierInterpolate(y1.x, y2.x, y3.x, u), + bezierInterpolate(y1.y, y2.y, y3.y, u), + bezierInterpolate(y1.z, y2.z, y3.z, u), + bezierInterpolate(y1.w, y2.w, y3.w, u)); +} + void main(void) { - // pass along the color & uvs to fragment shader - _color = inColor; - _texCoord0 = inTexCoord0.xy; - TransformCamera cam = getTransformCamera(); TransformObject obj = getTransformObject(); - <$transformModelToClipPos(cam, obj, inPosition, gl_Position)$> + + // Which icon are we dealing with ? + int particleID = gl_VertexID / NUM_VERTICES_PER_PARTICLE; + // Which quad vertex pos? + int twoTriID = gl_VertexID - particleID * NUM_VERTICES_PER_PARTICLE; + + // Particle properties + float age = inColor.x / particle.lifespan; + float seed = inColor.y; + + // Pass the texcoord and the z texcoord is representing the texture icon + varTexcoord = vec2((UNIT_QUAD[twoTriID].xy + 1.0) * 0.5); + varColor = interpolate3Vec4(particle.color.start, particle.color.middle, particle.color.finish, age); + + // anchor point in eye space + float radius = bezierInterpolate(particle.radius.start, particle.radius.middle, particle.radius.finish , age); + vec4 quadPos = radius * UNIT_QUAD[twoTriID]; + + vec4 anchorPoint; + <$transformModelToEyePos(cam, obj, inPosition, anchorPoint)$> + + vec4 eyePos = anchorPoint + quadPos; + <$transformEyeToClipPos(cam, eyePos, gl_Position)$> } diff --git a/libraries/entities-renderer/src/textured_particle_alpha_discard.slf b/libraries/entities-renderer/src/textured_particle_alpha_discard.slf deleted file mode 100644 index 389744449a..0000000000 --- a/libraries/entities-renderer/src/textured_particle_alpha_discard.slf +++ /dev/null @@ -1,25 +0,0 @@ -<@include gpu/Config.slh@> -<$VERSION_HEADER$> -// Generated on <$_SCRIBE_DATE$> -// fragment shader -// -// 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 -// - -uniform sampler2D colorMap; - -in vec4 _color; -in vec2 _texCoord0; - -out vec4 outFragColor; - -void main(void) { - vec4 color = texture(colorMap, _texCoord0); - if (color.a < 0.1) { - discard; - } - outFragColor = color * _color; -} diff --git a/libraries/entities/src/AnimationPropertyGroup.cpp b/libraries/entities/src/AnimationPropertyGroup.cpp index f9d8c07443..ef89128e30 100644 --- a/libraries/entities/src/AnimationPropertyGroup.cpp +++ b/libraries/entities/src/AnimationPropertyGroup.cpp @@ -29,7 +29,6 @@ void AnimationPropertyGroup::copyToScriptValue(const EntityPropertyFlags& desire COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_ANIMATION_FIRST_FRAME, Animation, animation, FirstFrame, firstFrame, _animationLoop->getFirstFrame); COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_ANIMATION_LAST_FRAME, Animation, animation, LastFrame, lastFrame, _animationLoop->getLastFrame); COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_ANIMATION_HOLD, Animation, animation, Hold, hold, _animationLoop->getHold); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_ANIMATION_START_AUTOMATICALLY, Animation, animation, StartAutomatically, startAutomatically, _animationLoop->getStartAutomatically); } else { COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_ANIMATION_FPS, Animation, animation, FPS, fps); COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_ANIMATION_FRAME_INDEX, Animation, animation, CurrentFrame, currentFrame); @@ -38,7 +37,6 @@ void AnimationPropertyGroup::copyToScriptValue(const EntityPropertyFlags& desire COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_ANIMATION_FIRST_FRAME, Animation, animation, FirstFrame, firstFrame); COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_ANIMATION_LAST_FRAME, Animation, animation, LastFrame, lastFrame); COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_ANIMATION_HOLD, Animation, animation, Hold, hold); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_ANIMATION_START_AUTOMATICALLY, Animation, animation, StartAutomatically, startAutomatically); } } @@ -58,7 +56,6 @@ void AnimationPropertyGroup::copyFromScriptValue(const QScriptValue& object, boo COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(animation, firstFrame, float, _animationLoop->setFirstFrame); COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(animation, lastFrame, float, _animationLoop->setLastFrame); COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(animation, hold, bool, _animationLoop->setHold); - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(animation, startAutomatically, bool, _animationLoop->setStartAutomatically); // legacy property support COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(animationFPS, float, _animationLoop->setFPS, _animationLoop->getFPS); @@ -73,7 +70,6 @@ void AnimationPropertyGroup::copyFromScriptValue(const QScriptValue& object, boo COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(animation, firstFrame, float, setFirstFrame); COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(animation, lastFrame, float, setLastFrame); COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(animation, hold, bool, setHold); - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(animation, startAutomatically, bool, setStartAutomatically); // legacy property support COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(animationFPS, float, setFPS, getFPS); @@ -95,7 +91,6 @@ void AnimationPropertyGroup::setFromOldAnimationSettings(const QString& value) { float lastFrame = _animationLoop ? _animationLoop->getLastFrame() : getLastFrame(); bool loop = _animationLoop ? _animationLoop->getLoop() : getLoop(); bool hold = _animationLoop ? _animationLoop->getHold() : getHold(); - bool startAutomatically = _animationLoop ? _animationLoop->getStartAutomatically() : getStartAutomatically(); QJsonDocument settingsAsJson = QJsonDocument::fromJson(value.toUtf8()); QJsonObject settingsAsJsonObject = settingsAsJson.object(); @@ -130,10 +125,6 @@ void AnimationPropertyGroup::setFromOldAnimationSettings(const QString& value) { running = settingsMap["hold"].toBool(); } - if (settingsMap.contains("startAutomatically")) { - running = settingsMap["startAutomatically"].toBool(); - } - if (_animationLoop) { _animationLoop->setFPS(fps); _animationLoop->setCurrentFrame(currentFrame); @@ -142,7 +133,6 @@ void AnimationPropertyGroup::setFromOldAnimationSettings(const QString& value) { _animationLoop->setLastFrame(lastFrame); _animationLoop->setLoop(loop); _animationLoop->setHold(hold); - _animationLoop->setStartAutomatically(startAutomatically); } else { setFPS(fps); setCurrentFrame(currentFrame); @@ -151,7 +141,6 @@ void AnimationPropertyGroup::setFromOldAnimationSettings(const QString& value) { setLastFrame(lastFrame); setLoop(loop); setHold(hold); - setStartAutomatically(startAutomatically); } } @@ -194,7 +183,6 @@ bool AnimationPropertyGroup::appendToEditPacket(OctreePacketData* packetData, APPEND_ENTITY_PROPERTY(PROP_ANIMATION_FIRST_FRAME, _animationLoop->getFirstFrame()); APPEND_ENTITY_PROPERTY(PROP_ANIMATION_LAST_FRAME, _animationLoop->getLastFrame()); APPEND_ENTITY_PROPERTY(PROP_ANIMATION_HOLD, _animationLoop->getHold()); - APPEND_ENTITY_PROPERTY(PROP_ANIMATION_START_AUTOMATICALLY, _animationLoop->getStartAutomatically()); } else { APPEND_ENTITY_PROPERTY(PROP_ANIMATION_FPS, getFPS()); APPEND_ENTITY_PROPERTY(PROP_ANIMATION_FRAME_INDEX, getCurrentFrame()); @@ -203,7 +191,6 @@ bool AnimationPropertyGroup::appendToEditPacket(OctreePacketData* packetData, APPEND_ENTITY_PROPERTY(PROP_ANIMATION_FIRST_FRAME, getFirstFrame()); APPEND_ENTITY_PROPERTY(PROP_ANIMATION_LAST_FRAME, getLastFrame()); APPEND_ENTITY_PROPERTY(PROP_ANIMATION_HOLD, getHold()); - APPEND_ENTITY_PROPERTY(PROP_ANIMATION_START_AUTOMATICALLY, getStartAutomatically()); } return true; @@ -226,7 +213,6 @@ bool AnimationPropertyGroup::decodeFromEditPacket(EntityPropertyFlags& propertyF READ_ENTITY_PROPERTY(PROP_ANIMATION_FIRST_FRAME, float, _animationLoop->setFirstFrame); READ_ENTITY_PROPERTY(PROP_ANIMATION_LAST_FRAME, float, _animationLoop->setLastFrame); READ_ENTITY_PROPERTY(PROP_ANIMATION_HOLD, bool, _animationLoop->setHold); - READ_ENTITY_PROPERTY(PROP_ANIMATION_START_AUTOMATICALLY, bool, _animationLoop->setStartAutomatically); } else { READ_ENTITY_PROPERTY(PROP_ANIMATION_FPS, float, setFPS); READ_ENTITY_PROPERTY(PROP_ANIMATION_FRAME_INDEX, float, setCurrentFrame); @@ -235,7 +221,6 @@ bool AnimationPropertyGroup::decodeFromEditPacket(EntityPropertyFlags& propertyF READ_ENTITY_PROPERTY(PROP_ANIMATION_FIRST_FRAME, float, setFirstFrame); READ_ENTITY_PROPERTY(PROP_ANIMATION_LAST_FRAME, float, setLastFrame); READ_ENTITY_PROPERTY(PROP_ANIMATION_HOLD, bool, setHold); - READ_ENTITY_PROPERTY(PROP_ANIMATION_START_AUTOMATICALLY, bool, setStartAutomatically); } DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_ANIMATION_URL, URL); @@ -246,7 +231,6 @@ bool AnimationPropertyGroup::decodeFromEditPacket(EntityPropertyFlags& propertyF DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_ANIMATION_FIRST_FRAME, FirstFrame); DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_ANIMATION_LAST_FRAME, LastFrame); DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_ANIMATION_HOLD, Hold); - DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_ANIMATION_START_AUTOMATICALLY, StartAutomatically); processedBytes += bytesRead; @@ -273,7 +257,6 @@ EntityPropertyFlags AnimationPropertyGroup::getChangedProperties() const { CHECK_PROPERTY_CHANGE(PROP_ANIMATION_FIRST_FRAME, firstFrame); CHECK_PROPERTY_CHANGE(PROP_ANIMATION_LAST_FRAME, lastFrame); CHECK_PROPERTY_CHANGE(PROP_ANIMATION_HOLD, hold); - CHECK_PROPERTY_CHANGE(PROP_ANIMATION_START_AUTOMATICALLY, startAutomatically); return changedProperties; } @@ -288,7 +271,6 @@ void AnimationPropertyGroup::getProperties(EntityItemProperties& properties) con COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Animation, FirstFrame, _animationLoop->getFirstFrame); COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Animation, LastFrame, _animationLoop->getLastFrame); COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Animation, Hold, _animationLoop->getHold); - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Animation, StartAutomatically, _animationLoop->getStartAutomatically); } else { COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Animation, FPS, getFPS); COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Animation, CurrentFrame, getCurrentFrame); @@ -297,7 +279,6 @@ void AnimationPropertyGroup::getProperties(EntityItemProperties& properties) con COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Animation, FirstFrame, getFirstFrame); COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Animation, LastFrame, getLastFrame); COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Animation, Hold, getHold); - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Animation, StartAutomatically, getStartAutomatically); } } @@ -313,7 +294,6 @@ bool AnimationPropertyGroup::setProperties(const EntityItemProperties& propertie SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Animation, FirstFrame, firstFrame, _animationLoop->setFirstFrame); SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Animation, LastFrame, lastFrame, _animationLoop->setLastFrame); SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Animation, Hold, hold, _animationLoop->setHold); - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Animation, StartAutomatically, startAutomatically, _animationLoop->setStartAutomatically); } else { SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Animation, FPS, fps, setFPS); SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Animation, CurrentFrame, currentFrame, setCurrentFrame); @@ -322,7 +302,6 @@ bool AnimationPropertyGroup::setProperties(const EntityItemProperties& propertie SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Animation, FirstFrame, firstFrame, setFirstFrame); SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Animation, LastFrame, lastFrame, setLastFrame); SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Animation, Hold, hold, setHold); - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Animation, StartAutomatically, startAutomatically, setStartAutomatically); } return somethingChanged; @@ -339,7 +318,6 @@ EntityPropertyFlags AnimationPropertyGroup::getEntityProperties(EncodeBitstreamP requestedProperties += PROP_ANIMATION_FIRST_FRAME; requestedProperties += PROP_ANIMATION_LAST_FRAME; requestedProperties += PROP_ANIMATION_HOLD; - requestedProperties += PROP_ANIMATION_START_AUTOMATICALLY; return requestedProperties; } @@ -363,7 +341,6 @@ void AnimationPropertyGroup::appendSubclassData(OctreePacketData* packetData, En APPEND_ENTITY_PROPERTY(PROP_ANIMATION_FIRST_FRAME, _animationLoop->getFirstFrame()); APPEND_ENTITY_PROPERTY(PROP_ANIMATION_LAST_FRAME, _animationLoop->getLastFrame()); APPEND_ENTITY_PROPERTY(PROP_ANIMATION_HOLD, _animationLoop->getHold()); - APPEND_ENTITY_PROPERTY(PROP_ANIMATION_START_AUTOMATICALLY, _animationLoop->getStartAutomatically()); } else { APPEND_ENTITY_PROPERTY(PROP_ANIMATION_FPS, getFPS()); APPEND_ENTITY_PROPERTY(PROP_ANIMATION_FRAME_INDEX, getCurrentFrame()); @@ -372,7 +349,6 @@ void AnimationPropertyGroup::appendSubclassData(OctreePacketData* packetData, En APPEND_ENTITY_PROPERTY(PROP_ANIMATION_FIRST_FRAME, getFirstFrame()); APPEND_ENTITY_PROPERTY(PROP_ANIMATION_LAST_FRAME, getLastFrame()); APPEND_ENTITY_PROPERTY(PROP_ANIMATION_HOLD, getHold()); - APPEND_ENTITY_PROPERTY(PROP_ANIMATION_START_AUTOMATICALLY, getStartAutomatically()); } } @@ -395,7 +371,6 @@ int AnimationPropertyGroup::readEntitySubclassDataFromBuffer(const unsigned char READ_ENTITY_PROPERTY(PROP_ANIMATION_FIRST_FRAME, float, _animationLoop->setFirstFrame); READ_ENTITY_PROPERTY(PROP_ANIMATION_LAST_FRAME, float, _animationLoop->setLastFrame); READ_ENTITY_PROPERTY(PROP_ANIMATION_HOLD, bool, _animationLoop->setHold); - READ_ENTITY_PROPERTY(PROP_ANIMATION_START_AUTOMATICALLY, bool, _animationLoop->setStartAutomatically); } else { READ_ENTITY_PROPERTY(PROP_ANIMATION_FPS, float, setFPS); READ_ENTITY_PROPERTY(PROP_ANIMATION_FRAME_INDEX, float, setCurrentFrame); @@ -404,7 +379,6 @@ int AnimationPropertyGroup::readEntitySubclassDataFromBuffer(const unsigned char READ_ENTITY_PROPERTY(PROP_ANIMATION_FIRST_FRAME, float, setFirstFrame); READ_ENTITY_PROPERTY(PROP_ANIMATION_LAST_FRAME, float, setLastFrame); READ_ENTITY_PROPERTY(PROP_ANIMATION_HOLD, bool, setHold); - READ_ENTITY_PROPERTY(PROP_ANIMATION_START_AUTOMATICALLY, bool, setStartAutomatically); } return bytesRead; diff --git a/libraries/entities/src/AnimationPropertyGroup.h b/libraries/entities/src/AnimationPropertyGroup.h index 8c40b0c036..8e85d8bc99 100644 --- a/libraries/entities/src/AnimationPropertyGroup.h +++ b/libraries/entities/src/AnimationPropertyGroup.h @@ -80,7 +80,6 @@ public: DEFINE_PROPERTY(PROP_ANIMATION_FIRST_FRAME, FirstFrame, firstFrame, float, 0.0f); // was animationSettings.firstFrame DEFINE_PROPERTY(PROP_ANIMATION_LAST_FRAME, LastFrame, lastFrame, float, AnimationLoop::MAXIMUM_POSSIBLE_FRAME); // was animationSettings.lastFrame DEFINE_PROPERTY(PROP_ANIMATION_HOLD, Hold, hold, bool, false); // was animationSettings.hold - DEFINE_PROPERTY(PROP_ANIMATION_START_AUTOMATICALLY, StartAutomatically, startAutomatically, bool, false); // was animationSettings.startAutomatically protected: void setFromOldAnimationSettings(const QString& value); diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 9b31c2aa59..f74cdedb9d 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -36,7 +36,7 @@ int EntityItem::_maxActionsDataSize = 800; quint64 EntityItem::_rememberDeletedActionTime = 20 * USECS_PER_SECOND; EntityItem::EntityItem(const EntityItemID& entityItemID) : - SpatiallyNestable(NestableTypes::Entity, entityItemID), + SpatiallyNestable(NestableType::Entity, entityItemID), _type(EntityTypes::Unknown), _lastSimulated(0), _lastUpdated(0), @@ -628,71 +628,50 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef bool weOwnSimulation = _simulationOwner.matchesValidID(myNodeID); - if (args.bitstreamVersion >= VERSION_ENTITIES_HAVE_SIMULATION_OWNER_AND_ACTIONS_OVER_WIRE) { - // pack SimulationOwner and terse update properties near each other + // pack SimulationOwner and terse update properties near each other - // NOTE: the server is authoritative for changes to simOwnerID so we always unpack ownership data - // even when we would otherwise ignore the rest of the packet. + // NOTE: the server is authoritative for changes to simOwnerID so we always unpack ownership data + // even when we would otherwise ignore the rest of the packet. - if (propertyFlags.getHasProperty(PROP_SIMULATION_OWNER)) { + if (propertyFlags.getHasProperty(PROP_SIMULATION_OWNER)) { - QByteArray simOwnerData; - int bytes = OctreePacketData::unpackDataFromBytes(dataAt, simOwnerData); - SimulationOwner newSimOwner; - newSimOwner.fromByteArray(simOwnerData); - dataAt += bytes; - bytesRead += bytes; + QByteArray simOwnerData; + int bytes = OctreePacketData::unpackDataFromBytes(dataAt, simOwnerData); + SimulationOwner newSimOwner; + newSimOwner.fromByteArray(simOwnerData); + dataAt += bytes; + bytesRead += bytes; - if (wantTerseEditLogging() && _simulationOwner != newSimOwner) { - qCDebug(entities) << "sim ownership for" << getDebugName() << "is now" << newSimOwner; - } - if (_simulationOwner.set(newSimOwner)) { - _dirtyFlags |= Simulation::DIRTY_SIMULATOR_ID; - } + if (wantTerseEditLogging() && _simulationOwner != newSimOwner) { + qCDebug(entities) << "sim ownership for" << getDebugName() << "is now" << newSimOwner; } - { // When we own the simulation we don't accept updates to the entity's transform/velocities - // but since we're using macros below we have to temporarily modify overwriteLocalData. - bool oldOverwrite = overwriteLocalData; - overwriteLocalData = overwriteLocalData && !weOwnSimulation; - READ_ENTITY_PROPERTY(PROP_POSITION, glm::vec3, updatePosition); - READ_ENTITY_PROPERTY(PROP_ROTATION, glm::quat, updateRotation); - READ_ENTITY_PROPERTY(PROP_VELOCITY, glm::vec3, updateVelocity); - READ_ENTITY_PROPERTY(PROP_ANGULAR_VELOCITY, glm::vec3, updateAngularVelocity); - READ_ENTITY_PROPERTY(PROP_ACCELERATION, glm::vec3, setAcceleration); - overwriteLocalData = oldOverwrite; + if (_simulationOwner.set(newSimOwner)) { + _dirtyFlags |= Simulation::DIRTY_SIMULATOR_ID; } - - READ_ENTITY_PROPERTY(PROP_DIMENSIONS, glm::vec3, updateDimensions); - READ_ENTITY_PROPERTY(PROP_DENSITY, float, updateDensity); - READ_ENTITY_PROPERTY(PROP_GRAVITY, glm::vec3, updateGravity); - - READ_ENTITY_PROPERTY(PROP_DAMPING, float, updateDamping); - READ_ENTITY_PROPERTY(PROP_RESTITUTION, float, updateRestitution); - READ_ENTITY_PROPERTY(PROP_FRICTION, float, updateFriction); - READ_ENTITY_PROPERTY(PROP_LIFETIME, float, updateLifetime); - READ_ENTITY_PROPERTY(PROP_SCRIPT, QString, setScript); - READ_ENTITY_PROPERTY(PROP_SCRIPT_TIMESTAMP, quint64, setScriptTimestamp); - READ_ENTITY_PROPERTY(PROP_REGISTRATION_POINT, glm::vec3, setRegistrationPoint); - } else { - // legacy order of packing here - // TODO: purge this logic in a few months from now (2015.07) - READ_ENTITY_PROPERTY(PROP_POSITION, glm::vec3, updatePosition); - READ_ENTITY_PROPERTY(PROP_DIMENSIONS, glm::vec3, updateDimensions); - READ_ENTITY_PROPERTY(PROP_ROTATION, glm::quat, updateRotation); - READ_ENTITY_PROPERTY(PROP_DENSITY, float, updateDensity); - READ_ENTITY_PROPERTY(PROP_VELOCITY, glm::vec3, updateVelocity); - READ_ENTITY_PROPERTY(PROP_GRAVITY, glm::vec3, updateGravity); - READ_ENTITY_PROPERTY(PROP_ACCELERATION, glm::vec3, setAcceleration); - - READ_ENTITY_PROPERTY(PROP_DAMPING, float, updateDamping); - READ_ENTITY_PROPERTY(PROP_RESTITUTION, float, updateRestitution); - READ_ENTITY_PROPERTY(PROP_FRICTION, float, updateFriction); - READ_ENTITY_PROPERTY(PROP_LIFETIME, float, updateLifetime); - READ_ENTITY_PROPERTY(PROP_SCRIPT, QString, setScript); - READ_ENTITY_PROPERTY(PROP_SCRIPT_TIMESTAMP, quint64, setScriptTimestamp); - READ_ENTITY_PROPERTY(PROP_REGISTRATION_POINT, glm::vec3, setRegistrationPoint); - READ_ENTITY_PROPERTY(PROP_ANGULAR_VELOCITY, glm::vec3, updateAngularVelocity); } + { // When we own the simulation we don't accept updates to the entity's transform/velocities + // but since we're using macros below we have to temporarily modify overwriteLocalData. + bool oldOverwrite = overwriteLocalData; + overwriteLocalData = overwriteLocalData && !weOwnSimulation; + READ_ENTITY_PROPERTY(PROP_POSITION, glm::vec3, updatePosition); + READ_ENTITY_PROPERTY(PROP_ROTATION, glm::quat, updateRotation); + READ_ENTITY_PROPERTY(PROP_VELOCITY, glm::vec3, updateVelocity); + READ_ENTITY_PROPERTY(PROP_ANGULAR_VELOCITY, glm::vec3, updateAngularVelocity); + READ_ENTITY_PROPERTY(PROP_ACCELERATION, glm::vec3, setAcceleration); + overwriteLocalData = oldOverwrite; + } + + READ_ENTITY_PROPERTY(PROP_DIMENSIONS, glm::vec3, updateDimensions); + READ_ENTITY_PROPERTY(PROP_DENSITY, float, updateDensity); + READ_ENTITY_PROPERTY(PROP_GRAVITY, glm::vec3, updateGravity); + + READ_ENTITY_PROPERTY(PROP_DAMPING, float, updateDamping); + READ_ENTITY_PROPERTY(PROP_RESTITUTION, float, updateRestitution); + READ_ENTITY_PROPERTY(PROP_FRICTION, float, updateFriction); + READ_ENTITY_PROPERTY(PROP_LIFETIME, float, updateLifetime); + READ_ENTITY_PROPERTY(PROP_SCRIPT, QString, setScript); + READ_ENTITY_PROPERTY(PROP_SCRIPT_TIMESTAMP, quint64, setScriptTimestamp); + READ_ENTITY_PROPERTY(PROP_REGISTRATION_POINT, glm::vec3, setRegistrationPoint); READ_ENTITY_PROPERTY(PROP_ANGULAR_DAMPING, float, updateAngularDamping); READ_ENTITY_PROPERTY(PROP_VISIBLE, bool, setVisible); @@ -701,17 +680,6 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef READ_ENTITY_PROPERTY(PROP_LOCKED, bool, setLocked); READ_ENTITY_PROPERTY(PROP_USER_DATA, QString, setUserData); - if (args.bitstreamVersion < VERSION_ENTITIES_HAVE_SIMULATION_OWNER_AND_ACTIONS_OVER_WIRE) { - // this code for when there is only simulatorID and no simulation priority - - // we always accept the server's notion of simulatorID, so we fake overwriteLocalData as true - // before we try to READ_ENTITY_PROPERTY it - bool temp = overwriteLocalData; - overwriteLocalData = true; - READ_ENTITY_PROPERTY(PROP_SIMULATION_OWNER, QUuid, updateSimulatorID); - overwriteLocalData = temp; - } - if (args.bitstreamVersion >= VERSION_ENTITIES_HAS_MARKETPLACE_ID) { READ_ENTITY_PROPERTY(PROP_MARKETPLACE_ID, QString, setMarketplaceID); } @@ -1104,7 +1072,7 @@ bool EntityItem::setProperties(const EntityItemProperties& properties) { bool somethingChanged = false; // these affect TerseUpdate properties - SET_ENTITY_PROPERTY_FROM_PROPERTIES(simulationOwner, setSimulationOwner); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(simulationOwner, updateSimulationOwner); SET_ENTITY_PROPERTY_FROM_PROPERTIES(position, updatePosition); SET_ENTITY_PROPERTY_FROM_PROPERTIES(rotation, updateRotation); SET_ENTITY_PROPERTY_FROM_PROPERTIES(velocity, updateVelocity); @@ -1324,6 +1292,13 @@ void EntityItem::updatePosition(const glm::vec3& value) { } if (getLocalPosition() != value) { setLocalPosition(value); + _dirtyFlags |= Simulation::DIRTY_POSITION; + forEachDescendant([&](SpatiallyNestablePointer object) { + if (object->getNestableType() == NestableType::Entity) { + EntityItemPointer entity = std::static_pointer_cast(object); + entity->_dirtyFlags |= Simulation::DIRTY_POSITION; + } + }); } } @@ -1342,7 +1317,7 @@ void EntityItem::updateRotation(const glm::quat& rotation) { setLocalOrientation(rotation); _dirtyFlags |= Simulation::DIRTY_ROTATION; forEachDescendant([&](SpatiallyNestablePointer object) { - if (object->getNestableType() == NestableTypes::Entity) { + if (object->getNestableType() == NestableType::Entity) { EntityItemPointer entity = std::static_pointer_cast(object); entity->_dirtyFlags |= Simulation::DIRTY_ROTATION; entity->_dirtyFlags |= Simulation::DIRTY_POSITION; @@ -1496,12 +1471,12 @@ void EntityItem::setSimulationOwner(const SimulationOwner& owner) { _simulationOwner.set(owner); } -void EntityItem::updateSimulatorID(const QUuid& value) { - if (wantTerseEditLogging() && _simulationOwner.getID() != value) { - qCDebug(entities) << "sim ownership for" << getDebugName() << "is now" << value; +void EntityItem::updateSimulationOwner(const SimulationOwner& owner) { + if (wantTerseEditLogging() && _simulationOwner != owner) { + qCDebug(entities) << "sim ownership for" << getDebugName() << "is now" << owner; } - if (_simulationOwner.setID(value)) { + if (_simulationOwner.set(owner)) { _dirtyFlags |= Simulation::DIRTY_SIMULATOR_ID; } } diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index ed4dd236d7..a1fc89c5e0 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -288,7 +288,7 @@ public: quint8 getSimulationPriority() const { return _simulationOwner.getPriority(); } QUuid getSimulatorID() const { return _simulationOwner.getID(); } - void updateSimulatorID(const QUuid& value); + void updateSimulationOwner(const SimulationOwner& owner); void clearSimulationOwnership(); const QString& getMarketplaceID() const { return _marketplaceID; } @@ -382,6 +382,8 @@ public: virtual glm::quat getAbsoluteJointRotationInObjectFrame(int index) const override { return glm::quat(); } virtual glm::vec3 getAbsoluteJointTranslationInObjectFrame(int index) const override { return glm::vec3(0.0f); } + virtual void loader() {} // called indirectly when urls for geometry are updated + protected: const QByteArray getActionDataInternal() const; diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index d040f785da..2e785519da 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -747,7 +747,6 @@ void EntityItemProperties::entityPropertyFlagsFromScriptValue(const QScriptValue ADD_GROUP_PROPERTY_TO_MAP(PROP_ANIMATION_FIRST_FRAME, Animation, animation, FirstFrame, firstFrame); ADD_GROUP_PROPERTY_TO_MAP(PROP_ANIMATION_LAST_FRAME, Animation, animation, LastFrame, lastFrame); ADD_GROUP_PROPERTY_TO_MAP(PROP_ANIMATION_HOLD, Animation, animation, Hold, hold); - ADD_GROUP_PROPERTY_TO_MAP(PROP_ANIMATION_START_AUTOMATICALLY, Animation, animation, StartAutomatically, startAutomatically); ADD_GROUP_PROPERTY_TO_MAP(PROP_ATMOSPHERE_CENTER, Atmosphere, atmosphere, Center, center); ADD_GROUP_PROPERTY_TO_MAP(PROP_ATMOSPHERE_INNER_RADIUS, Atmosphere, atmosphere, InnerRadius, innerRadius); diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index abad1e5cd1..fb2acf3fe7 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -219,20 +219,27 @@ bool EntityTree::updateEntityWithElement(EntityItemPointer entity, const EntityI // if the entity has children, run UpdateEntityOperator on them. If the children have children, recurse QQueue toProcess; foreach (SpatiallyNestablePointer child, entity->getChildren()) { - if (child && child->getNestableType() == NestableTypes::Entity) { + if (child && child->getNestableType() == NestableType::Entity) { toProcess.enqueue(child); } } while (!toProcess.empty()) { EntityItemPointer childEntity = std::static_pointer_cast(toProcess.dequeue()); + if (!childEntity) { + continue; + } BoundingBoxRelatedProperties newChildBBRelProperties(childEntity); + EntityTreeElementPointer containingElement = childEntity->getElement(); + if (!containingElement) { + continue; + } UpdateEntityOperator theChildOperator(getThisPointer(), - childEntity->getElement(), + containingElement, childEntity, newChildBBRelProperties); recurseTreeWithOperator(&theChildOperator); foreach (SpatiallyNestablePointer childChild, childEntity->getChildren()) { - if (childChild && childChild->getNestableType() == NestableTypes::Entity) { + if (childChild && childChild->getNestableType() == NestableType::Entity) { toProcess.enqueue(childChild); } } @@ -1266,3 +1273,12 @@ void EntityTree::trackIncomingEntityLastEdited(quint64 lastEditedTime, int bytes } } } + + +void EntityTree::callLoader(EntityItemID entityID) { + // this is used to bounce from the networking thread to the main thread + EntityItemPointer entity = findEntityByEntityItemID(entityID); + if (entity) { + entity->loader(); + } +} diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index 064c106b91..e893c69259 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -235,6 +235,9 @@ public: return _deletedEntityItemIDs.contains(id); } +public slots: + void callLoader(EntityItemID entityID); + signals: void deletingEntity(const EntityItemID& entityID); void addingEntity(const EntityItemID& entityID); diff --git a/libraries/entities/src/ModelEntityItem.cpp b/libraries/entities/src/ModelEntityItem.cpp index b0b0e7c76a..d96814ff04 100644 --- a/libraries/entities/src/ModelEntityItem.cpp +++ b/libraries/entities/src/ModelEntityItem.cpp @@ -384,11 +384,6 @@ void ModelEntityItem::setAnimationSettings(const QString& value) { setAnimationHold(hold); } - if (settingsMap.contains("startAutomatically")) { - bool startAutomatically = settingsMap["startAutomatically"].toBool(); - setAnimationStartAutomatically(startAutomatically); - } - _dirtyFlags |= Simulation::DIRTY_UPDATEABLE; } diff --git a/libraries/entities/src/ModelEntityItem.h b/libraries/entities/src/ModelEntityItem.h index cbd54f7168..ac70c73654 100644 --- a/libraries/entities/src/ModelEntityItem.h +++ b/libraries/entities/src/ModelEntityItem.h @@ -74,11 +74,10 @@ public: _color[GREEN_INDEX] = value.green; _color[BLUE_INDEX] = value.blue; } - - // model related properties - void setModelURL(const QString& url) { _modelURL = url; _parsedModelURL = QUrl(url); } - virtual void setCompoundShapeURL(const QString& url); + // model related properties + virtual void setModelURL(const QString& url) { _modelURL = url; _parsedModelURL = QUrl(url); } + virtual void setCompoundShapeURL(const QString& url); // Animation related items... const AnimationPropertyGroup& getAnimationProperties() const { return _animationProperties; } @@ -97,9 +96,6 @@ public: void setAnimationHold(bool hold) { _animationLoop.setHold(hold); } bool getAnimationHold() const { return _animationLoop.getHold(); } - void setAnimationStartAutomatically(bool startAutomatically) { _animationLoop.setStartAutomatically(startAutomatically); } - bool getAnimationStartAutomatically() const { return _animationLoop.getStartAutomatically(); } - void setAnimationFirstFrame(float firstFrame) { _animationLoop.setFirstFrame(firstFrame); } float getAnimationFirstFrame() const { return _animationLoop.getFirstFrame(); } diff --git a/libraries/entities/src/ParticleEffectEntityItem.cpp b/libraries/entities/src/ParticleEffectEntityItem.cpp index 33e7d1ca90..5600c85650 100644 --- a/libraries/entities/src/ParticleEffectEntityItem.cpp +++ b/libraries/entities/src/ParticleEffectEntityItem.cpp @@ -40,9 +40,6 @@ #include "EntityScriptingInterface.h" #include "ParticleEffectEntityItem.h" -const glm::vec3 X_AXIS = glm::vec3(1.0f, 0.0f, 0.0f); -const glm::vec3 Z_AXIS = glm::vec3(0.0f, 0.0f, 1.0f); - const float SCRIPT_MAXIMUM_PI = 3.1416f; // Round up so that reasonable property values work const xColor ParticleEffectEntityItem::DEFAULT_COLOR = { 255, 255, 255 }; @@ -66,8 +63,8 @@ const float ParticleEffectEntityItem::DEFAULT_EMIT_SPEED = 5.0f; const float ParticleEffectEntityItem::MINIMUM_EMIT_SPEED = 0.0f; const float ParticleEffectEntityItem::MAXIMUM_EMIT_SPEED = 1000.0f; // Approx mach 3 const float ParticleEffectEntityItem::DEFAULT_SPEED_SPREAD = 1.0f; -const glm::quat ParticleEffectEntityItem::DEFAULT_EMIT_ORIENTATION = glm::angleAxis(-PI_OVER_TWO, X_AXIS); // Vertical -const glm::vec3 ParticleEffectEntityItem::DEFAULT_EMIT_DIMENSIONS = glm::vec3(0.0f, 0.0f, 0.0f); // Emit from point +const glm::quat ParticleEffectEntityItem::DEFAULT_EMIT_ORIENTATION = glm::angleAxis(-PI_OVER_TWO, Vectors::UNIT_X); // Vertical +const glm::vec3 ParticleEffectEntityItem::DEFAULT_EMIT_DIMENSIONS = Vectors::ZERO; // Emit from point const float ParticleEffectEntityItem::MINIMUM_EMIT_DIMENSION = 0.0f; const float ParticleEffectEntityItem::MAXIMUM_EMIT_DIMENSION = (float)TREE_SCALE; const float ParticleEffectEntityItem::DEFAULT_EMIT_RADIUS_START = 1.0f; // Emit from surface (when emitDimensions > 0) @@ -106,36 +103,12 @@ EntityItemPointer ParticleEffectEntityItem::factory(const EntityItemID& entityID // our non-pure virtual subclass for now... ParticleEffectEntityItem::ParticleEffectEntityItem(const EntityItemID& entityItemID) : EntityItem(entityItemID), - _lastSimulated(usecTimestampNow()), - _particleLifetimes(DEFAULT_MAX_PARTICLES, 0.0f), - _particlePositions(DEFAULT_MAX_PARTICLES, glm::vec3(0.0f, 0.0f, 0.0f)), - _particleVelocities(DEFAULT_MAX_PARTICLES, glm::vec3(0.0f, 0.0f, 0.0f)), - _particleAccelerations(DEFAULT_MAX_PARTICLES, glm::vec3(0.0f, 0.0f, 0.0f)), - _particleRadiuses(DEFAULT_MAX_PARTICLES, DEFAULT_PARTICLE_RADIUS), - _radiusStarts(DEFAULT_MAX_PARTICLES, DEFAULT_PARTICLE_RADIUS), - _radiusMiddles(DEFAULT_MAX_PARTICLES, DEFAULT_PARTICLE_RADIUS), - _radiusFinishes(DEFAULT_MAX_PARTICLES, DEFAULT_PARTICLE_RADIUS), - _particleColors(DEFAULT_MAX_PARTICLES, DEFAULT_COLOR), - _colorStarts(DEFAULT_MAX_PARTICLES, DEFAULT_COLOR), - _colorMiddles(DEFAULT_MAX_PARTICLES, DEFAULT_COLOR), - _colorFinishes(DEFAULT_MAX_PARTICLES, DEFAULT_COLOR), - _particleAlphas(DEFAULT_MAX_PARTICLES, DEFAULT_ALPHA), - _alphaStarts(DEFAULT_MAX_PARTICLES, DEFAULT_ALPHA), - _alphaMiddles(DEFAULT_MAX_PARTICLES, DEFAULT_ALPHA), - _alphaFinishes(DEFAULT_MAX_PARTICLES, DEFAULT_ALPHA), - _particleMaxBound(glm::vec3(1.0f, 1.0f, 1.0f)), - _particleMinBound(glm::vec3(-1.0f, -1.0f, -1.0f)) , - _additiveBlending(DEFAULT_ADDITIVE_BLENDING) + _lastSimulated(usecTimestampNow()) { - _type = EntityTypes::ParticleEffect; setColor(DEFAULT_COLOR); } -ParticleEffectEntityItem::~ParticleEffectEntityItem() { -} - - void ParticleEffectEntityItem::setAlpha(float alpha) { if (MINIMUM_ALPHA <= alpha && alpha <= MAXIMUM_ALPHA) { _alpha = alpha; @@ -310,8 +283,8 @@ void ParticleEffectEntityItem::setRadiusSpread(float radiusSpread) { void ParticleEffectEntityItem::computeAndUpdateDimensions() { const float time = _lifespan * 1.1f; // add 10% extra time to account for incremental timer accumulation error - glm::vec3 velocity = _emitSpeed * (_emitOrientation * Z_AXIS); - glm::vec3 velocitySpread = _speedSpread * (_emitOrientation * Z_AXIS); + glm::vec3 velocity = _emitSpeed * (_emitOrientation * Vectors::UNIT_Z); + glm::vec3 velocitySpread = _speedSpread * (_emitOrientation * Vectors::UNIT_Z); glm::vec3 maxVelocity = glm::abs(velocity) + velocitySpread; glm::vec3 maxAccleration = glm::abs(_acceleration) + _accelerationSpread; @@ -578,7 +551,7 @@ void ParticleEffectEntityItem::appendSubclassData(OctreePacketData* packetData, bool ParticleEffectEntityItem::isEmittingParticles() const { // keep emitting if there are particles still alive. - return (getIsEmitting() || getLivingParticleCount() > 0); + return (getIsEmitting() || !_particles.empty()); } bool ParticleEffectEntityItem::needsToCallUpdate() const { @@ -613,62 +586,25 @@ void ParticleEffectEntityItem::updateShapeType(ShapeType type) { } } -void ParticleEffectEntityItem::updateRadius(quint32 index, float age) { - _particleRadiuses[index] = Interpolate::interpolate3Points(_radiusStarts[index], _radiusMiddles[index], - _radiusFinishes[index], age); -} - -void ParticleEffectEntityItem::updateColor(quint32 index, float age) { - _particleColors[index].red = (int)Interpolate::interpolate3Points(_colorStarts[index].red, _colorMiddles[index].red, - _colorFinishes[index].red, age); - _particleColors[index].green = (int)Interpolate::interpolate3Points(_colorStarts[index].green, _colorMiddles[index].green, - _colorFinishes[index].green, age); - _particleColors[index].blue = (int)Interpolate::interpolate3Points(_colorStarts[index].blue, _colorMiddles[index].blue, - _colorFinishes[index].blue, age); -} - -void ParticleEffectEntityItem::updateAlpha(quint32 index, float age) { - _particleAlphas[index] = Interpolate::interpolate3Points(_alphaStarts[index], _alphaMiddles[index], - _alphaFinishes[index], age); -} - -void ParticleEffectEntityItem::extendBounds(const glm::vec3& point) { - _particleMinBound.x = glm::min(_particleMinBound.x, point.x); - _particleMinBound.y = glm::min(_particleMinBound.y, point.y); - _particleMinBound.z = glm::min(_particleMinBound.z, point.z); - _particleMaxBound.x = glm::max(_particleMaxBound.x, point.x); - _particleMaxBound.y = glm::max(_particleMaxBound.y, point.y); - _particleMaxBound.z = glm::max(_particleMaxBound.z, point.z); -} - -void ParticleEffectEntityItem::integrateParticle(quint32 index, float deltaTime) { - glm::vec3 accel = _particleAccelerations[index]; - glm::vec3 atSquared = (0.5f * deltaTime * deltaTime) * accel; - glm::vec3 at = accel * deltaTime; - _particlePositions[index] += _particleVelocities[index] * deltaTime + atSquared; - _particleVelocities[index] += at; +void ParticleEffectEntityItem::integrateParticle(Particle& particle, float deltaTime) { + glm::vec3 atSquared = (0.5f * deltaTime * deltaTime) * particle.acceleration; + glm::vec3 at = particle.acceleration * deltaTime; + particle.position += particle.velocity * deltaTime + atSquared; + particle.velocity += at; } void ParticleEffectEntityItem::stepSimulation(float deltaTime) { - - _particleMinBound = glm::vec3(-1.0f, -1.0f, -1.0f); - _particleMaxBound = glm::vec3(1.0f, 1.0f, 1.0f); - // update particles between head and tail - for (quint32 i = _particleHeadIndex; i != _particleTailIndex; i = (i + 1) % _maxParticles) { - _particleLifetimes[i] += deltaTime; + for (Particle& particle : _particles) { + particle.lifetime += deltaTime; // if particle has died. - if (_particleLifetimes[i] >= _lifespan || _lifespan < EPSILON) { + if (particle.lifetime >= _lifespan) { // move head forward - _particleHeadIndex = (_particleHeadIndex + 1) % _maxParticles; + _particles.pop_front(); } else { - float age = _particleLifetimes[i] / _lifespan; // 0.0 .. 1.0 - updateRadius(i, age); - updateColor(i, age); - updateAlpha(i, age); - integrateParticle(i, deltaTime); - extendBounds(_particlePositions[i]); + // Otherwise update it + integrateParticle(particle, deltaTime); } } @@ -677,192 +613,104 @@ void ParticleEffectEntityItem::stepSimulation(float deltaTime) { float timeLeftInFrame = deltaTime; while (_timeUntilNextEmit < timeLeftInFrame) { - - timeLeftInFrame -= _timeUntilNextEmit; - _timeUntilNextEmit = 1.0f / _emitRate; - - // emit a new particle at tail index. - quint32 i = _particleTailIndex; - _particleLifetimes[i] = 0.0f; - - // Radius - if (_radiusSpread == 0.0f) { - _radiusStarts[i] = getRadiusStart(); - _radiusMiddles[i] =_particleRadius; - _radiusFinishes[i] = getRadiusFinish(); - } else { - float spreadMultiplier; - if (_particleRadius > 0.0f) { - spreadMultiplier = 1.0f + randFloatInRange(-1.0f, 1.0f) * _radiusSpread / _particleRadius; - } else { - spreadMultiplier = 1.0f; - } - _radiusStarts[i] = - glm::clamp(spreadMultiplier * getRadiusStart(), MINIMUM_PARTICLE_RADIUS, MAXIMUM_PARTICLE_RADIUS); - _radiusMiddles[i] = - glm::clamp(spreadMultiplier * _particleRadius, MINIMUM_PARTICLE_RADIUS, MAXIMUM_PARTICLE_RADIUS); - _radiusFinishes[i] = - glm::clamp(spreadMultiplier * getRadiusFinish(), MINIMUM_PARTICLE_RADIUS, MAXIMUM_PARTICLE_RADIUS); - } - updateRadius(i, 0.0f); - - // Position, velocity, and acceleration - if (_polarStart == 0.0f && _polarFinish == 0.0f && _emitDimensions.z == 0.0f) { - // Emit along z-axis from position - _particlePositions[i] = getPosition(); - _particleVelocities[i] = - (_emitSpeed + randFloatInRange(-1.0f, 1.0f) * _speedSpread) * (_emitOrientation * Z_AXIS); - _particleAccelerations[i] = _emitAcceleration + randFloatInRange(-1.0f, 1.0f) * _accelerationSpread; - - } else { - // Emit around point or from ellipsoid - // - Distribute directions evenly around point - // - Distribute points relatively evenly over ellipsoid surface - // - Distribute points relatively evenly within ellipsoid volume - - float elevationMinZ = sin(PI_OVER_TWO - _polarFinish); - float elevationMaxZ = sin(PI_OVER_TWO - _polarStart); - float elevation = asin(elevationMinZ + (elevationMaxZ - elevationMinZ) * randFloat()); - - float azimuth; - if (_azimuthFinish >= _azimuthStart) { - azimuth = _azimuthStart + (_azimuthFinish - _azimuthStart) * randFloat(); - } else { - azimuth = _azimuthStart + (TWO_PI + _azimuthFinish - _azimuthStart) * randFloat(); - } - - glm::vec3 emitDirection; - - if (_emitDimensions == glm::vec3()) { - // Point - emitDirection = glm::quat(glm::vec3(PI_OVER_TWO - elevation, 0.0f, azimuth)) * Z_AXIS; - - _particlePositions[i] = getPosition(); - } else { - // Ellipsoid - float radiusScale = 1.0f; - if (_emitRadiusStart < 1.0f) { - float emitRadiusStart = glm::max(_emitRadiusStart, EPSILON); // Avoid math complications at center - float randRadius = - emitRadiusStart + randFloatInRange(0.0f, MAXIMUM_EMIT_RADIUS_START - emitRadiusStart); - radiusScale = 1.0f - std::pow(1.0f - randRadius, 3.0f); - } - - glm::vec3 radiuses = radiusScale * 0.5f * _emitDimensions; - float x = radiuses.x * glm::cos(elevation) * glm::cos(azimuth); - float y = radiuses.y * glm::cos(elevation) * glm::sin(azimuth); - float z = radiuses.z * glm::sin(elevation); - glm::vec3 emitPosition = glm::vec3(x, y, z); - emitDirection = glm::normalize(glm::vec3( - radiuses.x > 0.0f ? x / (radiuses.x * radiuses.x) : 0.0f, - radiuses.y > 0.0f ? y / (radiuses.y * radiuses.y) : 0.0f, - radiuses.z > 0.0f ? z / (radiuses.z * radiuses.z) : 0.0f - )); - - _particlePositions[i] = getPosition() + _emitOrientation * emitPosition; - } - - _particleVelocities[i] = - (_emitSpeed + randFloatInRange(-1.0f, 1.0f) * _speedSpread) * (_emitOrientation * emitDirection); - _particleAccelerations[i] = _emitAcceleration + randFloatInRange(-1.0f, 1.0f) * _accelerationSpread; - } - integrateParticle(i, timeLeftInFrame); - extendBounds(_particlePositions[i]); - - // Color - if (_colorSpread == xColor{ 0, 0, 0 }) { - _colorStarts[i] = getColorStart(); - _colorMiddles[i] = getXColor(); - _colorFinishes[i] = getColorFinish(); - } else { - xColor startColor = getColorStart(); - xColor middleColor = getXColor(); - xColor finishColor = getColorFinish(); - - float spread = randFloatInRange(-1.0f, 1.0f); - float spreadMultiplierRed = - middleColor.red > 0 ? 1.0f + spread * (float)_colorSpread.red / (float)middleColor.red : 1.0f; - float spreadMultiplierGreen = - middleColor.green > 0 ? 1.0f + spread * (float)_colorSpread.green / (float)middleColor.green : 1.0f; - float spreadMultiplierBlue = - middleColor.blue > 0 ? 1.0f + spread * (float)_colorSpread.blue / (float)middleColor.blue : 1.0f; - - _colorStarts[i].red = (int)glm::clamp(spreadMultiplierRed * (float)startColor.red, 0.0f, 255.0f); - _colorStarts[i].green = (int)glm::clamp(spreadMultiplierGreen * (float)startColor.green, 0.0f, 255.0f); - _colorStarts[i].blue = (int)glm::clamp(spreadMultiplierBlue * (float)startColor.blue, 0.0f, 255.0f); - - _colorMiddles[i].red = (int)glm::clamp(spreadMultiplierRed * (float)middleColor.red, 0.0f, 255.0f); - _colorMiddles[i].green = (int)glm::clamp(spreadMultiplierGreen * (float)middleColor.green, 0.0f, 255.0f); - _colorMiddles[i].blue = (int)glm::clamp(spreadMultiplierBlue * (float)middleColor.blue, 0.0f, 255.0f); - - _colorFinishes[i].red = (int)glm::clamp(spreadMultiplierRed * (float)finishColor.red, 0.0f, 255.0f); - _colorFinishes[i].green = (int)glm::clamp(spreadMultiplierGreen * (float)finishColor.green, 0.0f, 255.0f); - _colorFinishes[i].blue = (int)glm::clamp(spreadMultiplierBlue * (float)finishColor.blue, 0.0f, 255.0f); - } - updateColor(i, 0.0f); - - // Alpha - if (_alphaSpread == 0.0f) { - _alphaStarts[i] = getAlphaStart(); - _alphaMiddles[i] = _alpha; - _alphaFinishes[i] = getAlphaFinish(); - } else { - float spreadMultiplier = 1.0f + randFloatInRange(-1.0f, 1.0f) * _alphaSpread / _alpha; - _alphaStarts[i] = spreadMultiplier * getAlphaStart(); - _alphaMiddles[i] = spreadMultiplier * _alpha; - _alphaFinishes[i] = spreadMultiplier * getAlphaFinish(); - } - updateAlpha(i, 0.0f); - - _particleTailIndex = (_particleTailIndex + 1) % _maxParticles; - // overflow! move head forward by one. // because the case of head == tail indicates an empty array, not a full one. // This can drop an existing older particle, but this is by design, newer particles are a higher priority. - if (_particleTailIndex == _particleHeadIndex) { - _particleHeadIndex = (_particleHeadIndex + 1) % _maxParticles; + if (_particles.size() >= _maxParticles) { + _particles.pop_front(); } + + // emit a new particle at tail index. + _particles.push_back(createParticle()); + auto particle = _particles.back(); + particle.lifetime += timeLeftInFrame; + + // Initialize it + integrateParticle(particle, deltaTime); + + // Advance in frame + timeLeftInFrame -= _timeUntilNextEmit; + _timeUntilNextEmit = 1.0f / _emitRate; } _timeUntilNextEmit -= timeLeftInFrame; } } +ParticleEffectEntityItem::Particle ParticleEffectEntityItem::createParticle() { + Particle particle; + + particle.seed = randFloatInRange(0.0f, 1.0f); + + // Position, velocity, and acceleration + if (_polarStart == 0.0f && _polarFinish == 0.0f && _emitDimensions.z == 0.0f) { + // Emit along z-axis from position + particle.velocity = (_emitSpeed + randFloatInRange(-1.0f, 1.0f) * _speedSpread) * (_emitOrientation * Vectors::UNIT_Z); + particle.acceleration = _emitAcceleration + randFloatInRange(-1.0f, 1.0f) * _accelerationSpread; + + } else { + // Emit around point or from ellipsoid + // - Distribute directions evenly around point + // - Distribute points relatively evenly over ellipsoid surface + // - Distribute points relatively evenly within ellipsoid volume + + float elevationMinZ = sin(PI_OVER_TWO - _polarFinish); + float elevationMaxZ = sin(PI_OVER_TWO - _polarStart); + float elevation = asin(elevationMinZ + (elevationMaxZ - elevationMinZ) * randFloat()); + + float azimuth; + if (_azimuthFinish >= _azimuthStart) { + azimuth = _azimuthStart + (_azimuthFinish - _azimuthStart) * randFloat(); + } else { + azimuth = _azimuthStart + (TWO_PI + _azimuthFinish - _azimuthStart) * randFloat(); + } + + glm::vec3 emitDirection; + + if (_emitDimensions == Vectors::ZERO) { + // Point + emitDirection = glm::quat(glm::vec3(PI_OVER_TWO - elevation, 0.0f, azimuth)) * Vectors::UNIT_Z; + } else { + // Ellipsoid + float radiusScale = 1.0f; + if (_emitRadiusStart < 1.0f) { + float emitRadiusStart = glm::max(_emitRadiusStart, EPSILON); // Avoid math complications at center + float randRadius = + emitRadiusStart + randFloatInRange(0.0f, MAXIMUM_EMIT_RADIUS_START - emitRadiusStart); + radiusScale = 1.0f - std::pow(1.0f - randRadius, 3.0f); + } + + glm::vec3 radii = radiusScale * 0.5f * _emitDimensions; + float x = radii.x * glm::cos(elevation) * glm::cos(azimuth); + float y = radii.y * glm::cos(elevation) * glm::sin(azimuth); + float z = radii.z * glm::sin(elevation); + glm::vec3 emitPosition = glm::vec3(x, y, z); + emitDirection = glm::normalize(glm::vec3( + radii.x > 0.0f ? x / (radii.x * radii.x) : 0.0f, + radii.y > 0.0f ? y / (radii.y * radii.y) : 0.0f, + radii.z > 0.0f ? z / (radii.z * radii.z) : 0.0f + )); + + particle.position = _emitOrientation * emitPosition; + } + + particle.velocity = (_emitSpeed + randFloatInRange(-1.0f, 1.0f) * _speedSpread) * (_emitOrientation * emitDirection); + particle.acceleration = _emitAcceleration + randFloatInRange(-1.0f, 1.0f) * _accelerationSpread; + } + + return particle; +} + void ParticleEffectEntityItem::setMaxParticles(quint32 maxParticles) { if (_maxParticles != maxParticles && MINIMUM_MAX_PARTICLES <= maxParticles && maxParticles <= MAXIMUM_MAX_PARTICLES) { _maxParticles = maxParticles; - // TODO: try to do something smart here and preserve the state of existing particles. - - // resize vectors - _particleLifetimes.resize(_maxParticles); - _particlePositions.resize(_maxParticles); - _particleVelocities.resize(_maxParticles); - _particleRadiuses.resize(_maxParticles); - _radiusStarts.resize(_maxParticles); - _radiusMiddles.resize(_maxParticles); - _radiusFinishes.resize(_maxParticles); - _particleColors.resize(_maxParticles); - _colorStarts.resize(_maxParticles); - _colorMiddles.resize(_maxParticles); - _colorFinishes.resize(_maxParticles); - _particleAlphas.resize(_maxParticles); - _alphaStarts.resize(_maxParticles); - _alphaMiddles.resize(_maxParticles); - _alphaFinishes.resize(_maxParticles); + // Pop all the overflowing oldest particles + while (_particles.size() > _maxParticles) { + _particles.pop_front(); + } // effectively clear all particles and start emitting new ones from scratch. - _particleHeadIndex = 0; - _particleTailIndex = 0; _timeUntilNextEmit = 0.0f; } } - -// because particles are in a ring buffer, this isn't trivial -quint32 ParticleEffectEntityItem::getLivingParticleCount() const { - if (_particleTailIndex >= _particleHeadIndex) { - return _particleTailIndex - _particleHeadIndex; - } else { - return (_maxParticles - _particleHeadIndex) + _particleTailIndex; - } -} diff --git a/libraries/entities/src/ParticleEffectEntityItem.h b/libraries/entities/src/ParticleEffectEntityItem.h index d6dd257f7e..c35a45baeb 100644 --- a/libraries/entities/src/ParticleEffectEntityItem.h +++ b/libraries/entities/src/ParticleEffectEntityItem.h @@ -11,19 +11,17 @@ #ifndef hifi_ParticleEffectEntityItem_h #define hifi_ParticleEffectEntityItem_h -#include +#include #include "EntityItem.h" class ParticleEffectEntityItem : public EntityItem { public: + ALLOW_INSTANTIATION // This class can be instantiated static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties); ParticleEffectEntityItem(const EntityItemID& entityItemID); - virtual ~ParticleEffectEntityItem(); - - ALLOW_INSTANTIATION // This class can be instantiated // methods for getting/setting all properties of this entity virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const; @@ -218,16 +216,27 @@ public: virtual bool supportsDetailedRayIntersection() const { return false; } protected: + struct Particle; + using Particles = std::deque; bool isAnimatingSomething() const; + + Particle createParticle(); void stepSimulation(float deltaTime); - void updateRadius(quint32 index, float age); - void updateColor(quint32 index, float age); - void updateAlpha(quint32 index, float age); - void extendBounds(const glm::vec3& point); - void integrateParticle(quint32 index, float deltaTime); - quint32 getLivingParticleCount() const; - // the properties of this entity + void integrateParticle(Particle& particle, float deltaTime); + + struct Particle { + float seed { 0.0f }; + float lifetime { 0.0f }; + glm::vec3 position { Vectors::ZERO }; + glm::vec3 velocity { Vectors::ZERO }; + glm::vec3 acceleration { Vectors::ZERO }; + }; + + // Particles container + Particles _particles; + + // Particles properties rgbColor _color; xColor _colorStart = DEFAULT_COLOR; xColor _colorFinish = DEFAULT_COLOR; @@ -236,63 +245,42 @@ protected: float _alphaStart = DEFAULT_ALPHA_START; float _alphaFinish = DEFAULT_ALPHA_FINISH; float _alphaSpread = DEFAULT_ALPHA_SPREAD; - quint32 _maxParticles = DEFAULT_MAX_PARTICLES; - float _lifespan = DEFAULT_LIFESPAN; - float _emitRate = DEFAULT_EMIT_RATE; - float _emitSpeed = DEFAULT_EMIT_SPEED; - float _speedSpread = DEFAULT_SPEED_SPREAD; - glm::quat _emitOrientation = DEFAULT_EMIT_ORIENTATION; - glm::vec3 _emitDimensions = DEFAULT_EMIT_DIMENSIONS; - float _emitRadiusStart = DEFAULT_EMIT_RADIUS_START; - float _polarStart = DEFAULT_POLAR_START; - float _polarFinish = DEFAULT_POLAR_FINISH; - float _azimuthStart = DEFAULT_AZIMUTH_START; - float _azimuthFinish = DEFAULT_AZIMUTH_FINISH; - glm::vec3 _emitAcceleration = DEFAULT_EMIT_ACCELERATION; - glm::vec3 _accelerationSpread = DEFAULT_ACCELERATION_SPREAD; float _particleRadius = DEFAULT_PARTICLE_RADIUS; float _radiusStart = DEFAULT_RADIUS_START; float _radiusFinish = DEFAULT_RADIUS_FINISH; float _radiusSpread = DEFAULT_RADIUS_SPREAD; + float _lifespan = DEFAULT_LIFESPAN; + + // Emiter properties + quint32 _maxParticles = DEFAULT_MAX_PARTICLES; + + float _emitRate = DEFAULT_EMIT_RATE; + float _emitSpeed = DEFAULT_EMIT_SPEED; + float _speedSpread = DEFAULT_SPEED_SPREAD; + + glm::quat _emitOrientation = DEFAULT_EMIT_ORIENTATION; + glm::vec3 _emitDimensions = DEFAULT_EMIT_DIMENSIONS; + float _emitRadiusStart = DEFAULT_EMIT_RADIUS_START; + glm::vec3 _emitAcceleration = DEFAULT_EMIT_ACCELERATION; + glm::vec3 _accelerationSpread = DEFAULT_ACCELERATION_SPREAD; + + float _polarStart = DEFAULT_POLAR_START; + float _polarFinish = DEFAULT_POLAR_FINISH; + float _azimuthStart = DEFAULT_AZIMUTH_START; + float _azimuthFinish = DEFAULT_AZIMUTH_FINISH; + + quint64 _lastSimulated { 0 }; + bool _isEmitting { true }; - quint64 _lastSimulated; - bool _isEmitting = true; + QString _textures { DEFAULT_TEXTURES }; + bool _texturesChangedFlag { false }; + ShapeType _shapeType { SHAPE_TYPE_NONE }; + + float _timeUntilNextEmit { 0.0f }; - QString _textures = DEFAULT_TEXTURES; - bool _texturesChangedFlag = false; - ShapeType _shapeType = SHAPE_TYPE_NONE; - - // all the internals of running the particle sim - QVector _particleLifetimes; - QVector _particlePositions; - QVector _particleVelocities; - QVector _particleAccelerations; - QVector _particleRadiuses; - QVector _radiusStarts; - QVector _radiusMiddles; - QVector _radiusFinishes; - QVector _particleColors; - QVector _colorStarts; - QVector _colorMiddles; - QVector _colorFinishes; - QVector _particleAlphas; - QVector _alphaStarts; - QVector _alphaMiddles; - QVector _alphaFinishes; - - float _timeUntilNextEmit = 0.0f; - - // particle arrays are a ring buffer, use these indices - // to keep track of the living particles. - quint32 _particleHeadIndex = 0; - quint32 _particleTailIndex = 0; - - // bounding volume - glm::vec3 _particleMaxBound; - glm::vec3 _particleMinBound; - - bool _additiveBlending; + + bool _additiveBlending { DEFAULT_ADDITIVE_BLENDING }; }; #endif // hifi_ParticleEffectEntityItem_h diff --git a/libraries/entities/src/ZoneEntityItem.h b/libraries/entities/src/ZoneEntityItem.h index bf323248c0..19206f8acc 100644 --- a/libraries/entities/src/ZoneEntityItem.h +++ b/libraries/entities/src/ZoneEntityItem.h @@ -57,7 +57,7 @@ public: static bool getDrawZoneBoundaries() { return _drawZoneBoundaries; } static void setDrawZoneBoundaries(bool value) { _drawZoneBoundaries = value; } - virtual bool isReadyToComputeShape() { return false; } + virtual bool isReadyToComputeShape() { return true; } void updateShapeType(ShapeType type) { _shapeType = type; } virtual ShapeType getShapeType() const; diff --git a/libraries/gl/src/gl/GLEscrow.h b/libraries/gl/src/gl/GLEscrow.h index d860f1239c..9da3ce0b0b 100644 --- a/libraries/gl/src/gl/GLEscrow.h +++ b/libraries/gl/src/gl/GLEscrow.h @@ -49,35 +49,32 @@ template < > class GLEscrow { public: + static const uint64_t MAX_UNSIGNALED_TIME = USECS_PER_SECOND / 2; struct Item { - T _value; + const T _value; GLsync _sync; - uint64_t _created; + const uint64_t _created; Item(T value, GLsync sync) : _value(value), _sync(sync), _created(usecTimestampNow()) { } - uint64_t age() { + uint64_t age() const { return usecTimestampNow() - _created; } - bool signaled() { + bool signaled() const { auto result = glClientWaitSync(_sync, 0, 0); if (GL_TIMEOUT_EXPIRED != result && GL_WAIT_FAILED != result) { return true; } - if (age() > (USECS_PER_SECOND / 2)) { - qWarning() << "Long unsignaled sync"; - } return false; } }; - using Mutex = std::recursive_mutex; - using Lock = std::unique_lock; + using Mutex = std::mutex; using Recycler = std::function; // deque gives us random access, double ended push & pop and size, all in constant time using Deque = std::deque; @@ -87,9 +84,32 @@ public: _recycler = recycler; } - size_t depth() { + template + void withLock(F f) { + using Lock = std::unique_lock; Lock lock(_mutex); - return _submits.size(); + f(); + } + + template + bool tryLock(F f) { + using Lock = std::unique_lock; + bool result = false; + Lock lock(_mutex, std::try_to_lock_t()); + if (lock.owns_lock()) { + f(); + result = true; + } + return result; + } + + + size_t depth() { + size_t result{ 0 }; + withLock([&]{ + result = _submits.size(); + }); + return result; } // Submit a new resource from the producer context @@ -104,11 +124,9 @@ public: glFlush(); } - { - Lock lock(_mutex); + withLock([&]{ _submits.push_back(Item(t, writeSync)); - } - + }); return cleanTrash(); } @@ -120,13 +138,13 @@ public: // On the one hand using try_lock() reduces the chance of blocking the consumer thread, // but if the produce thread is going fast enough, it could effectively // starve the consumer out of ever actually getting resources. - if (_mutex.try_lock()) { + tryLock([&]{ + // May be called on any thread, but must be inside a locked section if (signaled(_submits, 0)) { result = _submits.at(0)._value; _submits.pop_front(); } - _mutex.unlock(); - } + }); return result; } @@ -154,37 +172,45 @@ public: glFlush(); } - Lock lock(_mutex); - _releases.push_back(Item(t, readSync)); + withLock([&]{ + _releases.push_back(Item(t, readSync)); + }); } private: size_t cleanTrash() { size_t wastedWork{ 0 }; List trash; - { + tryLock([&]{ + while (!_submits.empty()) { + const auto& item = _submits.front(); + if (!item._sync || item.age() < MAX_UNSIGNALED_TIME) { + break; + } + qWarning() << "Long unsignaled sync " << item._sync << " unsignaled for " << item.age(); + _trash.push_front(item); + _submits.pop_front(); + } + // We only ever need one ready item available in the list, so if the // second item is signaled (implying the first is as well, remove the first // item. Iterate until the SECOND item in the list is not in the ready state // The signaled function takes care of checking against the deque size while (signaled(_submits, 1)) { - pop(_submits); + _trash.push_front(_submits.front()); + _submits.pop_front(); ++wastedWork; } // Stuff in the release queue can be cleared out as soon as it's signaled while (signaled(_releases, 0)) { - pop(_releases); + _trash.push_front(_releases.front()); + _releases.pop_front(); } - { - // FIXME I don't think this lock should be necessary, only the submitting thread - // touches the trash - Lock lock(_mutex); - trash.swap(_trash); - } - } - + trash.swap(_trash); + }); + // FIXME maybe doing a timing on the deleters and warn if it's taking excessive time? // although we are out of the lock, so it shouldn't be blocking anything std::for_each(trash.begin(), trash.end(), [&](typename List::const_reference item) { @@ -198,14 +224,6 @@ private: return wastedWork; } - // May be called on any thread, but must be inside a locked section - void pop(Deque& deque) { - Lock lock(_mutex); - auto& item = deque.front(); - _trash.push_front(item); - deque.pop_front(); - } - // May be called on any thread, but must be inside a locked section bool signaled(Deque& deque, size_t i) { if (i >= deque.size()) { diff --git a/libraries/gl/src/gl/OffscreenQmlSurface.cpp b/libraries/gl/src/gl/OffscreenQmlSurface.cpp index 482eb1b36f..d30294ae7c 100644 --- a/libraries/gl/src/gl/OffscreenQmlSurface.cpp +++ b/libraries/gl/src/gl/OffscreenQmlSurface.cpp @@ -8,16 +8,18 @@ #include "OffscreenQmlSurface.h" #include "OglplusHelpers.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include #include @@ -25,8 +27,6 @@ #include "GLEscrow.h" #include "OffscreenGLCanvas.h" -// FIXME move to threaded rendering with Qt 5.5 -//#define QML_THREADED // Time between receiving a request to render the offscreen UI actually triggering // the render. Could possibly be increased depending on the framerate we expect to @@ -56,13 +56,11 @@ private: Q_DECLARE_LOGGING_CATEGORY(offscreenFocus) Q_LOGGING_CATEGORY(offscreenFocus, "hifi.offscreen.focus") -#ifdef QML_THREADED static const QEvent::Type INIT = QEvent::Type(QEvent::User + 1); static const QEvent::Type RENDER = QEvent::Type(QEvent::User + 2); static const QEvent::Type RESIZE = QEvent::Type(QEvent::User + 3); static const QEvent::Type STOP = QEvent::Type(QEvent::User + 4); static const QEvent::Type UPDATE = QEvent::Type(QEvent::User + 5); -#endif class OffscreenQmlRenderer : public OffscreenGLCanvas { friend class OffscreenQmlSurface; @@ -70,22 +68,30 @@ public: OffscreenQmlRenderer(OffscreenQmlSurface* surface, QOpenGLContext* shareContext) : _surface(surface) { OffscreenGLCanvas::create(shareContext); -#ifdef QML_THREADED + + _renderControl = new QMyQuickRenderControl(); + + // Create a QQuickWindow that is associated with out render control. Note that this + // window never gets created or shown, meaning that it will never get an underlying + // native (platform) window. + QQuickWindow::setDefaultAlphaBuffer(true); + // Weirdness... QQuickWindow NEEDS to be created on the rendering thread, or it will refuse to render + // because it retains an internal 'context' object that retains the thread it was created on, + // regardless of whether you later move it to another thread. + _quickWindow = new QQuickWindow(_renderControl); + _quickWindow->setColor(QColor(255, 255, 255, 0)); + _quickWindow->setFlags(_quickWindow->flags() | static_cast(Qt::WA_TranslucentBackground)); + // Qt 5.5 - _renderControl->prepareThread(_renderThread); + _renderControl->prepareThread(&_thread); _context->moveToThread(&_thread); moveToThread(&_thread); _thread.setObjectName("QML Thread"); _thread.start(); post(INIT); -#else - init(); -#endif } -#ifdef QML_THREADED - bool event(QEvent *e) - { + bool event(QEvent *e) { switch (int(e->type())) { case INIT: { @@ -120,7 +126,6 @@ public: QCoreApplication::postEvent(this, new QEvent(type)); } -#endif private: @@ -143,27 +148,9 @@ private: void init() { - _renderControl = new QMyQuickRenderControl(); connect(_renderControl, &QQuickRenderControl::renderRequested, _surface, &OffscreenQmlSurface::requestRender); connect(_renderControl, &QQuickRenderControl::sceneChanged, _surface, &OffscreenQmlSurface::requestUpdate); - // Create a QQuickWindow that is associated with out render control. Note that this - // window never gets created or shown, meaning that it will never get an underlying - // native (platform) window. - QQuickWindow::setDefaultAlphaBuffer(true); - // Weirdness... QQuickWindow NEEDS to be created on the rendering thread, or it will refuse to render - // because it retains an internal 'context' object that retains the thread it was created on, - // regardless of whether you later move it to another thread. - _quickWindow = new QQuickWindow(_renderControl); - _quickWindow->setColor(QColor(255, 255, 255, 0)); - _quickWindow->setFlags(_quickWindow->flags() | static_cast(Qt::WA_TranslucentBackground)); - -#ifdef QML_THREADED - // However, because we want to use synchronous events with the quickwindow, we need to move it back to the main - // thread after it's created. - _quickWindow->moveToThread(qApp->thread()); -#endif - if (!makeCurrent()) { qWarning("Failed to make context current on render thread"); return; @@ -189,17 +176,15 @@ private: doneCurrent(); -#ifdef QML_THREADED _context->moveToThread(QCoreApplication::instance()->thread()); _cond.wakeOne(); -#endif } - void resize(const QSize& newSize) { + void resize() { // Update our members if (_quickWindow) { - _quickWindow->setGeometry(QRect(QPoint(), newSize)); - _quickWindow->contentItem()->setSize(newSize); + _quickWindow->setGeometry(QRect(QPoint(), _newSize)); + _quickWindow->contentItem()->setSize(_newSize); } // Qt bug in 5.4 forces this check of pixel ratio, @@ -209,7 +194,7 @@ private: pixelRatio = _renderControl->_renderWindow->devicePixelRatio(); } - uvec2 newOffscreenSize = toGlm(newSize * pixelRatio); + uvec2 newOffscreenSize = toGlm(_newSize * pixelRatio); _textures.setSize(newOffscreenSize); if (newOffscreenSize == _size) { return; @@ -222,7 +207,7 @@ private: return; } - qDebug() << "Offscreen UI resizing to " << newSize.width() << "x" << newSize.height() << " with pixel ratio " << pixelRatio; + qDebug() << "Offscreen UI resizing to " << _newSize.width() << "x" << _newSize.height() << " with pixel ratio " << pixelRatio; setupFbo(); doneCurrent(); } @@ -237,54 +222,44 @@ private: return; } - //Q_ASSERT(toGlm(_quickWindow->geometry().size()) == _size); - //Q_ASSERT(toGlm(_quickWindow->geometry().size()) == _textures._size); - _renderControl->sync(); -#ifdef QML_THREADED _cond.wakeOne(); lock->unlock(); -#endif - using namespace oglplus; _quickWindow->setRenderTarget(GetName(*_fbo), QSize(_size.x, _size.y)); - TexturePtr texture = _textures.getNextTexture(); - _fbo->Bind(Framebuffer::Target::Draw); - _fbo->AttachTexture(Framebuffer::Target::Draw, FramebufferAttachment::Color, *texture, 0); - _fbo->Complete(Framebuffer::Target::Draw); - //Context::Clear().ColorBuffer(); { - _renderControl->render(); - // FIXME The web browsers seem to be leaving GL in an error state. - // Need a debug context with sync logging to figure out why. - // for now just clear the errors - glGetError(); + PROFILE_RANGE("qml_render") + TexturePtr texture = _textures.getNextTexture(); + _fbo->Bind(Framebuffer::Target::Draw); + _fbo->AttachTexture(Framebuffer::Target::Draw, FramebufferAttachment::Color, *texture, 0); + _fbo->Complete(Framebuffer::Target::Draw); + { + _renderControl->render(); + // FIXME The web browsers seem to be leaving GL in an error state. + // Need a debug context with sync logging to figure out why. + // for now just clear the errors + glGetError(); + } + // FIXME probably unecessary + DefaultFramebuffer().Bind(Framebuffer::Target::Draw); + _quickWindow->resetOpenGLState(); + _escrow.submit(GetName(*texture)); } - // FIXME probably unecessary - DefaultFramebuffer().Bind(Framebuffer::Target::Draw); - _quickWindow->resetOpenGLState(); - _escrow.submit(GetName(*texture)); _lastRenderTime = usecTimestampNow(); } void aboutToQuit() { -#ifdef QML_THREADED QMutexLocker lock(&_quitMutex); _quit = true; -#endif } void stop() { -#ifdef QML_THREADED - QMutexLocker lock(&_quitMutex); + QMutexLocker lock(&_mutex); post(STOP); _cond.wait(&_mutex); -#else - cleanup(); -#endif } bool allowNewFrame(uint8_t fps) { @@ -297,13 +272,12 @@ private: QQuickWindow* _quickWindow{ nullptr }; QMyQuickRenderControl* _renderControl{ nullptr }; -#ifdef QML_THREADED QThread _thread; QMutex _mutex; QWaitCondition _cond; QMutex _quitMutex; -#endif + QSize _newSize; bool _quit; FramebufferPtr _fbo; RenderbufferPtr _depthStencil; @@ -346,9 +320,7 @@ void OffscreenQmlSurface::create(QOpenGLContext* shareContext) { } void OffscreenQmlSurface::resize(const QSize& newSize) { -#ifdef QML_THREADED - QMutexLocker _locker(&(_renderer->_mutex)); -#endif + if (!_renderer || !_renderer->_quickWindow) { QSize currentSize = _renderer->_quickWindow->geometry().size(); if (newSize == currentSize) { @@ -362,11 +334,12 @@ void OffscreenQmlSurface::resize(const QSize& newSize) { _rootItem->setSize(newSize); } -#ifdef QML_THREADED + { + QMutexLocker _locker(&(_renderer->_mutex)); + _renderer->_newSize = newSize; + } + _renderer->post(RESIZE); -#else - _renderer->resize(newSize); -#endif } QQuickItem* OffscreenQmlSurface::getRootItem() { @@ -466,11 +439,7 @@ void OffscreenQmlSurface::updateQuick() { } if (_render) { -#ifdef QML_THREADED _renderer->post(RENDER); -#else - _renderer->render(nullptr); -#endif _render = false; } diff --git a/libraries/gpu/src/gpu/Batch.cpp b/libraries/gpu/src/gpu/Batch.cpp index 80b3a4f158..14871aafd1 100644 --- a/libraries/gpu/src/gpu/Batch.cpp +++ b/libraries/gpu/src/gpu/Batch.cpp @@ -16,13 +16,6 @@ #if defined(NSIGHT_FOUND) #include "nvToolsExt.h" -ProfileRange::ProfileRange(const char *name) { - nvtxRangePush(name); -} -ProfileRange::~ProfileRange() { - nvtxRangePop(); -} - ProfileRangeBatch::ProfileRangeBatch(gpu::Batch& batch, const char *name) : _batch(batch) { _batch.pushProfileRange(name); } diff --git a/libraries/gpu/src/gpu/Batch.h b/libraries/gpu/src/gpu/Batch.h index da1f13151e..2afcc7caa9 100644 --- a/libraries/gpu/src/gpu/Batch.h +++ b/libraries/gpu/src/gpu/Batch.h @@ -15,6 +15,8 @@ #include #include +#include + #include "Framebuffer.h" #include "Pipeline.h" #include "Query.h" @@ -22,18 +24,6 @@ #include "Texture.h" #include "Transform.h" - -#if defined(NSIGHT_FOUND) - class ProfileRange { - public: - ProfileRange(const char *name); - ~ProfileRange(); - }; -#define PROFILE_RANGE(name) ProfileRange profileRangeThis(name); -#else -#define PROFILE_RANGE(name) -#endif - class QDebug; namespace gpu { diff --git a/libraries/gpu/src/gpu/GLBackendShader.cpp b/libraries/gpu/src/gpu/GLBackendShader.cpp index 5a0ab93ec5..19fa94524e 100755 --- a/libraries/gpu/src/gpu/GLBackendShader.cpp +++ b/libraries/gpu/src/gpu/GLBackendShader.cpp @@ -154,7 +154,7 @@ GLBackend::GLShader* compileShader(const Shader& shader) { qCWarning(gpulogging) << "GLShader::compileShader - failed to compile the gl shader object:"; qCWarning(gpulogging) << temp; - + /* filestream.open("debugshader.glsl.info.txt"); if (filestream.is_open()) { diff --git a/libraries/gpu/src/gpu/Stream.cpp b/libraries/gpu/src/gpu/Stream.cpp index 183f2137fb..ff765f3250 100644 --- a/libraries/gpu/src/gpu/Stream.cpp +++ b/libraries/gpu/src/gpu/Stream.cpp @@ -77,16 +77,6 @@ bool Stream::Format::setAttribute(Slot slot, Slot channel, Frequency frequency) return true; } - -BufferStream::BufferStream() : - _buffers(), - _offsets(), - _strides() -{} - -BufferStream::~BufferStream() { -} - void BufferStream::addBuffer(const BufferPointer& buffer, Offset offset, Offset stride) { _buffers.push_back(buffer); _offsets.push_back(offset); diff --git a/libraries/gpu/src/gpu/Stream.h b/libraries/gpu/src/gpu/Stream.h index 4fff3b651d..8c0833dd42 100644 --- a/libraries/gpu/src/gpu/Stream.h +++ b/libraries/gpu/src/gpu/Stream.h @@ -50,7 +50,7 @@ public: // Frequency describer enum Frequency { PER_VERTEX = 0, - PER_INSTANCE, + PER_INSTANCE = 1, }; // The attribute description @@ -106,6 +106,7 @@ public: bool setAttribute(Slot slot, Frequency frequency = PER_VERTEX); bool setAttribute(Slot slot, Slot channel, Frequency frequency = PER_VERTEX); + bool hasAttribute(Slot slot) const { return (_attributes.find(slot) != _attributes.end()); } protected: AttributeMap _attributes; @@ -124,10 +125,7 @@ typedef std::vector< Offset > Offsets; // A Buffer Stream can be assigned to the Batch to set several stream channels in one call class BufferStream { public: - typedef Offsets Strides; - - BufferStream(); - ~BufferStream(); + using Strides = Offsets; void clear() { _buffers.clear(); _offsets.clear(); _strides.clear(); } void addBuffer(const BufferPointer& buffer, Offset offset, Offset stride); diff --git a/libraries/gpu/src/gpu/Transform.slh b/libraries/gpu/src/gpu/Transform.slh index e67fbf7f66..26ea9784be 100644 --- a/libraries/gpu/src/gpu/Transform.slh +++ b/libraries/gpu/src/gpu/Transform.slh @@ -86,7 +86,6 @@ TransformCamera getTransformCamera() { } <@endfunc@> - <@func transformModelToWorldPos(objectTransform, modelPos, worldPos)@> { // transformModelToWorldPos <$worldPos$> = (<$objectTransform$>._model * <$modelPos$>); @@ -136,7 +135,22 @@ TransformCamera getTransformCamera() { <@func transformClipToEyeDir(cameraTransform, clipPos, eyeDir)@> { // transformClipToEyeDir - <$eyeDir$> = vec3(<$cameraTransform$>._projectionInverse * vec4(<$clipPos$>.xyz, 1.0)); + <$eyeDir$> = vec3(<$cameraTransform$>._projectionInverse * vec4(<$clipPos$>.xyz, 1.0)); // Must be 1.0 here + } +<@endfunc@> + +<@func $transformModelToEyePos(cameraTransform, objectTransform, modelPos, eyePos)@> + + { // transformModelToEyePos + vec4 _worldpos = (<$objectTransform$>._model * vec4(<$modelPos$>.xyz, 1.0)); + <$eyePos$> = (<$cameraTransform$>._view * _worldpos); + } +<@endfunc@> + +<@func transformEyeToClipPos(cameraTransform, eyePos, clipPos)@> + { // transformEyeToClipPos + <$clipPos$> = <$cameraTransform$>._projection * vec4(<$eyePos$>.xyz, 1.0); } <@endfunc@> diff --git a/libraries/input-plugins/CMakeLists.txt b/libraries/input-plugins/CMakeLists.txt index 0e21d4a40c..b81554511d 100644 --- a/libraries/input-plugins/CMakeLists.txt +++ b/libraries/input-plugins/CMakeLists.txt @@ -1,15 +1,5 @@ set(TARGET_NAME input-plugins) setup_hifi_library() -link_hifi_libraries(shared plugins controllers script-engine render-utils) +link_hifi_libraries(shared plugins controllers) GroupSources("src/input-plugins") - -if (WIN32) - add_dependency_external_projects(OpenVR) - find_package(OpenVR REQUIRED) - target_include_directories(${TARGET_NAME} PRIVATE ${OPENVR_INCLUDE_DIRS}) - target_link_libraries(${TARGET_NAME} ${OPENVR_LIBRARIES}) -endif() - -target_sdl2() -target_sixense() diff --git a/libraries/input-plugins/src/input-plugins/InputPlugin.cpp b/libraries/input-plugins/src/input-plugins/InputPlugin.cpp index 6db35572b5..4d59adb602 100644 --- a/libraries/input-plugins/src/input-plugins/InputPlugin.cpp +++ b/libraries/input-plugins/src/input-plugins/InputPlugin.cpp @@ -13,17 +13,11 @@ #include #include "KeyboardMouseDevice.h" -#include "SDL2Manager.h" -#include "SixenseManager.h" -#include "ViveControllerManager.h" // TODO migrate to a DLL model where plugins are discovered and loaded at runtime by the PluginManager class InputPluginList getInputPlugins() { InputPlugin* PLUGIN_POOL[] = { new KeyboardMouseDevice(), - new SDL2Manager(), - new SixenseManager(), - new ViveControllerManager(), nullptr }; diff --git a/libraries/model/src/model/Geometry.cpp b/libraries/model/src/model/Geometry.cpp index 63adeec0f1..df86ba2a8f 100755 --- a/libraries/model/src/model/Geometry.cpp +++ b/libraries/model/src/model/Geometry.cpp @@ -92,7 +92,7 @@ void Mesh::setPartBuffer(const BufferView& buffer) { _partBuffer = buffer; } -const Box Mesh::evalPartBound(int partNum) const { +Box Mesh::evalPartBound(int partNum) const { Box box; if (partNum < _partBuffer.getNum()) { const Part& part = _partBuffer.get(partNum); @@ -111,7 +111,7 @@ const Box Mesh::evalPartBound(int partNum) const { return box; } -const Box Mesh::evalPartBounds(int partStart, int partEnd, Boxes& bounds) const { +Box Mesh::evalPartBounds(int partStart, int partEnd, Boxes& bounds) const { Box totalBound; auto part = _partBuffer.cbegin() + partStart; auto partItEnd = _partBuffer.cbegin() + partEnd; diff --git a/libraries/model/src/model/Geometry.h b/libraries/model/src/model/Geometry.h index 8b5f448a64..a35d55d938 100755 --- a/libraries/model/src/model/Geometry.h +++ b/libraries/model/src/model/Geometry.h @@ -107,10 +107,10 @@ public: uint getNumParts() const { return _partBuffer.getNumElements(); } // evaluate the bounding box of A part - const Box evalPartBound(int partNum) const; + Box evalPartBound(int partNum) const; // evaluate the bounding boxes of the parts in the range [start, end[ and fill the bounds parameter // the returned box is the bounding box of ALL the evaluated part bounds. - const Box evalPartBounds(int partStart, int partEnd, Boxes& bounds) const; + Box evalPartBounds(int partStart, int partEnd, Boxes& bounds) const; static gpu::Primitive topologyToPrimitive(Topology topo) { return static_cast(topo); } diff --git a/libraries/model/src/model/Light.cpp b/libraries/model/src/model/Light.cpp index 823be727a4..917e3bed7e 100755 --- a/libraries/model/src/model/Light.cpp +++ b/libraries/model/src/model/Light.cpp @@ -18,7 +18,7 @@ Light::Light() : _transform() { // only if created from nothing shall we create the Buffer to store the properties Schema schema; - _schemaBuffer = gpu::BufferView(std::make_shared(sizeof(Schema), (const gpu::Byte*) &schema)); + _schemaBuffer = std::make_shared(sizeof(Schema), (const gpu::Byte*) &schema); } Light::Light(const Light& light) : diff --git a/libraries/model/src/model/Light.h b/libraries/model/src/model/Light.h index efc02e3de4..de7a846a25 100755 --- a/libraries/model/src/model/Light.h +++ b/libraries/model/src/model/Light.h @@ -112,8 +112,6 @@ public: Vec4 _shadow{0.0f}; Vec4 _control{0.0f, 0.0f, 0.0f, 0.0f}; - - Schema() {} }; const UniformBufferView& getSchemaBuffer() const { return _schemaBuffer; } diff --git a/libraries/model/src/model/Skybox.cpp b/libraries/model/src/model/Skybox.cpp index 3e445933fa..8c37359638 100755 --- a/libraries/model/src/model/Skybox.cpp +++ b/libraries/model/src/model/Skybox.cpp @@ -71,22 +71,12 @@ void Skybox::render(gpu::Batch& batch, const ViewFrustum& frustum) const { void Skybox::render(gpu::Batch& batch, const ViewFrustum& viewFrustum, const Skybox& skybox) { // Create the static shared elements used to render the skybox - static gpu::BufferPointer theBuffer; - static gpu::Stream::FormatPointer theFormat; static gpu::BufferPointer theConstants; static gpu::PipelinePointer thePipeline; const int SKYBOX_SKYMAP_SLOT = 0; const int SKYBOX_CONSTANTS_SLOT = 0; static std::once_flag once; std::call_once(once, [&] { - { - const float CLIP = 1.0f; - const glm::vec2 vertices[4] = { { -CLIP, -CLIP }, { CLIP, -CLIP }, { -CLIP, CLIP }, { CLIP, CLIP } }; - theBuffer = std::make_shared(sizeof(vertices), (const gpu::Byte*) vertices); - theFormat = std::make_shared(); - theFormat->setAttribute(gpu::Stream::POSITION, gpu::Stream::POSITION, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::XYZ)); - } - { auto skyVS = gpu::Shader::createVertex(std::string(Skybox_vert)); auto skyFS = gpu::Shader::createPixel(std::string(Skybox_frag)); @@ -115,8 +105,6 @@ void Skybox::render(gpu::Batch& batch, const ViewFrustum& viewFrustum, const Sky batch.setProjectionTransform(projMat); batch.setViewTransform(viewTransform); batch.setModelTransform(Transform()); // only for Mac - batch.setInputBuffer(gpu::Stream::POSITION, theBuffer, 0, 8); - batch.setInputFormat(theFormat); gpu::TexturePointer skymap; if (skybox.getCubemap() && skybox.getCubemap()->isDefined()) { diff --git a/libraries/model/src/model/Skybox.slv b/libraries/model/src/model/Skybox.slv index d1b9a20a66..810afb1033 100755 --- a/libraries/model/src/model/Skybox.slv +++ b/libraries/model/src/model/Skybox.slv @@ -11,21 +11,26 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // - -<@include gpu/Inputs.slh@> - <@include gpu/Transform.slh@> <$declareStandardTransform()$> out vec3 _normal; -void main(void) { +void main(void) { + const float depth = 0.0; + const vec4 UNIT_QUAD[4] = vec4[4]( + vec4(-1.0, -1.0, depth, 1.0), + vec4(1.0, -1.0, depth, 1.0), + vec4(-1.0, 1.0, depth, 1.0), + vec4(1.0, 1.0, depth, 1.0) + ); + vec4 inPosition = UNIT_QUAD[gl_VertexID]; + // standard transform TransformCamera cam = getTransformCamera(); vec3 clipDir = vec3(inPosition.xy, 0.0); vec3 eyeDir; - <$transformClipToEyeDir(cam, clipDir, eyeDir)$> <$transformEyeToWorldDir(cam, eyeDir, _normal)$> diff --git a/libraries/networking/src/PacketReceiver.cpp b/libraries/networking/src/PacketReceiver.cpp index 7e91a42a64..530efc5fb3 100644 --- a/libraries/networking/src/PacketReceiver.cpp +++ b/libraries/networking/src/PacketReceiver.cpp @@ -296,8 +296,6 @@ void PacketReceiver::handleVerifiedMessage(QSharedPointer recei if (matchingNode) { emit dataReceived(matchingNode->getType(), receivedMessage->getSize()); matchingNode->recordBytesReceived(receivedMessage->getSize()); - Node* n = matchingNode.data(); - auto addr = n->getActiveSocket(); QMetaMethod metaMethod = listener.method; diff --git a/libraries/networking/src/ReceivedMessage.cpp b/libraries/networking/src/ReceivedMessage.cpp index f383257520..651b856923 100644 --- a/libraries/networking/src/ReceivedMessage.cpp +++ b/libraries/networking/src/ReceivedMessage.cpp @@ -22,8 +22,8 @@ static const int HEAD_DATA_SIZE = 512; ReceivedMessage::ReceivedMessage(const NLPacketList& packetList) : _data(packetList.getMessage()), _headData(_data.mid(0, HEAD_DATA_SIZE)), - _sourceID(packetList.getSourceID()), _numPackets(packetList.getNumPackets()), + _sourceID(packetList.getSourceID()), _packetType(packetList.getType()), _packetVersion(packetList.getVersion()), _senderSockAddr(packetList.getSenderSockAddr()), @@ -34,8 +34,8 @@ ReceivedMessage::ReceivedMessage(const NLPacketList& packetList) ReceivedMessage::ReceivedMessage(NLPacket& packet) : _data(packet.readAll()), _headData(_data.mid(0, HEAD_DATA_SIZE)), - _sourceID(packet.getSourceID()), _numPackets(1), + _sourceID(packet.getSourceID()), _packetType(packet.getType()), _packetVersion(packet.getVersion()), _senderSockAddr(packet.getSenderSockAddr()), diff --git a/libraries/networking/src/ResourceCache.cpp b/libraries/networking/src/ResourceCache.cpp index 84af87f217..e5771401b9 100644 --- a/libraries/networking/src/ResourceCache.cpp +++ b/libraries/networking/src/ResourceCache.cpp @@ -319,10 +319,10 @@ void Resource::attemptRequest() { void Resource::finishedLoading(bool success) { if (success) { - qDebug().noquote() << "Finished loading:" << _url.toDisplayString(); + qCDebug(networking).noquote() << "Finished loading:" << _url.toDisplayString(); _loaded = true; } else { - qDebug().noquote() << "Failed to load:" << _url.toDisplayString(); + qCDebug(networking).noquote() << "Failed to load:" << _url.toDisplayString(); _failedToLoad = true; } _loadPriorities.clear(); @@ -339,13 +339,13 @@ void Resource::makeRequest() { _request = ResourceManager::createResourceRequest(this, _activeUrl); if (!_request) { - qDebug().noquote() << "Failed to get request for" << _url.toDisplayString(); + qCDebug(networking).noquote() << "Failed to get request for" << _url.toDisplayString(); ResourceCache::requestCompleted(this); finishedLoading(false); return; } - qDebug().noquote() << "Starting request for:" << _url.toDisplayString(); + qCDebug(networking).noquote() << "Starting request for:" << _url.toDisplayString(); connect(_request, &ResourceRequest::progress, this, &Resource::handleDownloadProgress); connect(_request, &ResourceRequest::finished, this, &Resource::handleReplyFinished); @@ -369,7 +369,7 @@ void Resource::handleReplyFinished() { if (result == ResourceRequest::Success) { _data = _request->getData(); auto extraInfo = _url == _activeUrl ? "" : QString(", %1").arg(_activeUrl.toDisplayString()); - qDebug().noquote() << QString("Request finished for %1%2").arg(_url.toDisplayString(), extraInfo); + qCDebug(networking).noquote() << QString("Request finished for %1%2").arg(_url.toDisplayString(), extraInfo); finishedLoading(true); emit loaded(_data); @@ -377,7 +377,7 @@ void Resource::handleReplyFinished() { } else { switch (result) { case ResourceRequest::Result::Timeout: { - qDebug() << "Timed out loading" << _url << "received" << _bytesReceived << "total" << _bytesTotal; + qCDebug(networking) << "Timed out loading" << _url << "received" << _bytesReceived << "total" << _bytesTotal; // Fall through to other cases } case ResourceRequest::Result::ServerUnavailable: { @@ -386,7 +386,7 @@ void Resource::handleReplyFinished() { const int BASE_DELAY_MS = 1000; if (_attempts++ < MAX_ATTEMPTS) { auto waitTime = BASE_DELAY_MS * (int)pow(2.0, _attempts); - qDebug().nospace() << "Retrying to load the asset in " << waitTime + qCDebug(networking).nospace() << "Retrying to load the asset in " << waitTime << "ms, attempt " << _attempts << " of " << MAX_ATTEMPTS; QTimer::singleShot(waitTime, this, &Resource::attemptRequest); break; @@ -394,7 +394,7 @@ void Resource::handleReplyFinished() { // fall through to final failure } default: { - qDebug() << "Error loading " << _url; + qCDebug(networking) << "Error loading " << _url; auto error = (result == ResourceRequest::Timeout) ? QNetworkReply::TimeoutError : QNetworkReply::UnknownNetworkError; emit failed(error); diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index 93fbe7acdc..4daf7f83b6 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -41,7 +41,7 @@ PacketVersion versionForPacketType(PacketType packetType) { case PacketType::EntityAdd: case PacketType::EntityEdit: case PacketType::EntityData: - return VERSION_ENTITIES_HAVE_PARENTS; + return VERSION_ENTITIES_REMOVED_START_AUTOMATICALLY_FROM_ANIMATION_PROPERTY_GROUP; case PacketType::AvatarData: case PacketType::BulkAvatarData: return 17; diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index cd7b9a113c..5daa185af4 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -161,5 +161,6 @@ const PacketVersion VERSION_ENTITIES_KEYLIGHT_PROPERTIES_GROUP_BIS = 48; const PacketVersion VERSION_ENTITIES_PARTICLES_ADDITIVE_BLENDING = 49; const PacketVersion VERSION_ENTITIES_POLYLINE_TEXTURE = 50; const PacketVersion VERSION_ENTITIES_HAVE_PARENTS = 51; +const PacketVersion VERSION_ENTITIES_REMOVED_START_AUTOMATICALLY_FROM_ANIMATION_PROPERTY_GROUP = 52; #endif // hifi_PacketHeaders_h diff --git a/libraries/octree/src/OctreeHeadlessViewer.cpp b/libraries/octree/src/OctreeHeadlessViewer.cpp index 32da638f96..3f761a0748 100644 --- a/libraries/octree/src/OctreeHeadlessViewer.cpp +++ b/libraries/octree/src/OctreeHeadlessViewer.cpp @@ -56,6 +56,7 @@ void OctreeHeadlessViewer::queryOctree() { _octreeQuery.setCameraNearClip(_viewFrustum.getNearClip()); _octreeQuery.setCameraFarClip(_viewFrustum.getFarClip()); _octreeQuery.setCameraEyeOffsetPosition(glm::vec3()); + _octreeQuery.setKeyholeRadius(_viewFrustum.getKeyholeRadius()); _octreeQuery.setOctreeSizeScale(getVoxelSizeScale()); _octreeQuery.setBoundaryLevelAdjust(getBoundaryLevelAdjust()); diff --git a/libraries/octree/src/OctreeQuery.cpp b/libraries/octree/src/OctreeQuery.cpp index 07bca14844..5a30e178f8 100644 --- a/libraries/octree/src/OctreeQuery.cpp +++ b/libraries/octree/src/OctreeQuery.cpp @@ -63,6 +63,9 @@ int OctreeQuery::getBroadcastData(unsigned char* destinationBuffer) { // desired boundaryLevelAdjust memcpy(destinationBuffer, &_boundaryLevelAdjust, sizeof(_boundaryLevelAdjust)); destinationBuffer += sizeof(_boundaryLevelAdjust); + + memcpy(destinationBuffer, &_keyholeRadius, sizeof(_keyholeRadius)); + destinationBuffer += sizeof(_keyholeRadius); return destinationBuffer - bufferStart; } @@ -104,6 +107,12 @@ int OctreeQuery::parseData(ReceivedMessage& message) { memcpy(&_boundaryLevelAdjust, sourceBuffer, sizeof(_boundaryLevelAdjust)); sourceBuffer += sizeof(_boundaryLevelAdjust); + auto bytesRead = sourceBuffer - startPosition; + auto bytesLeft = message.getSize() - bytesRead; + if (bytesLeft >= sizeof(_keyholeRadius)) { + memcpy(&_keyholeRadius, sourceBuffer, sizeof(_keyholeRadius)); + sourceBuffer += sizeof(_keyholeRadius); + } return sourceBuffer - startPosition; } diff --git a/libraries/octree/src/OctreeQuery.h b/libraries/octree/src/OctreeQuery.h index fec1ac0c2a..7e157c4252 100644 --- a/libraries/octree/src/OctreeQuery.h +++ b/libraries/octree/src/OctreeQuery.h @@ -58,6 +58,7 @@ public: float getCameraNearClip() const { return _cameraNearClip; } float getCameraFarClip() const { return _cameraFarClip; } const glm::vec3& getCameraEyeOffsetPosition() const { return _cameraEyeOffsetPosition; } + float getKeyholeRadius() const { return _keyholeRadius; } glm::vec3 calculateCameraDirection() const; @@ -69,6 +70,7 @@ public: void setCameraNearClip(float nearClip) { _cameraNearClip = nearClip; } void setCameraFarClip(float farClip) { _cameraFarClip = farClip; } void setCameraEyeOffsetPosition(const glm::vec3& eyeOffsetPosition) { _cameraEyeOffsetPosition = eyeOffsetPosition; } + void setKeyholeRadius(float keyholeRadius) { _keyholeRadius = keyholeRadius; } // related to Octree Sending strategies int getMaxQueryPacketsPerSecond() const { return _maxQueryPPS; } @@ -88,6 +90,7 @@ protected: float _cameraAspectRatio = 1.0f; float _cameraNearClip = 0.0f; float _cameraFarClip = 0.0f; + float _keyholeRadius { 0.0f }; glm::vec3 _cameraEyeOffsetPosition = glm::vec3(0.0f); // octree server sending items diff --git a/libraries/physics/src/CharacterController.cpp b/libraries/physics/src/CharacterController.cpp index 8da9541387..44d4269e0e 100644 --- a/libraries/physics/src/CharacterController.cpp +++ b/libraries/physics/src/CharacterController.cpp @@ -11,8 +11,55 @@ #include "CharacterController.h" +#include + +#include "BulletUtil.h" #include "PhysicsCollisionGroups.h" +const btVector3 LOCAL_UP_AXIS(0.0f, 1.0f, 0.0f); +const float JUMP_SPEED = 3.5f; +const float MAX_FALL_HEIGHT = 20.0f; + + +// helper class for simple ray-traces from character +class ClosestNotMe : public btCollisionWorld::ClosestRayResultCallback { +public: + ClosestNotMe(btRigidBody* me) : btCollisionWorld::ClosestRayResultCallback(btVector3(0.0f, 0.0f, 0.0f), btVector3(0.0f, 0.0f, 0.0f)) { + _me = me; + } + virtual btScalar addSingleResult(btCollisionWorld::LocalRayResult& rayResult,bool normalInWorldSpace) { + if (rayResult.m_collisionObject == _me) { + return 1.0f; + } + return ClosestRayResultCallback::addSingleResult(rayResult, normalInWorldSpace); + } +protected: + btRigidBody* _me; +}; + + +CharacterController::CharacterController() { + _halfHeight = 1.0f; + + _enabled = false; + + _floorDistance = MAX_FALL_HEIGHT; + + _walkVelocity.setValue(0.0f, 0.0f, 0.0f); + _followVelocity.setValue(0.0f, 0.0f, 0.0f); + _jumpSpeed = JUMP_SPEED; + _isOnGround = false; + _isJumping = false; + _isFalling = false; + _isHovering = true; + _isPushingUp = false; + _jumpToHoverStart = 0; + _followTime = 0.0f; + + _pendingFlags = PENDING_FLAG_UPDATE_SHAPE; + +} + bool CharacterController::needsRemoval() const { return ((_pendingFlags & PENDING_FLAG_REMOVE_FROM_SIMULATION) == PENDING_FLAG_REMOVE_FROM_SIMULATION); } @@ -23,7 +70,7 @@ bool CharacterController::needsAddition() const { void CharacterController::setDynamicsWorld(btDynamicsWorld* world) { if (_dynamicsWorld != world) { - if (_dynamicsWorld) { + if (_dynamicsWorld) { if (_rigidBody) { _dynamicsWorld->removeRigidBody(_rigidBody); _dynamicsWorld->removeAction(this); @@ -33,9 +80,13 @@ void CharacterController::setDynamicsWorld(btDynamicsWorld* world) { if (world && _rigidBody) { _dynamicsWorld = world; _pendingFlags &= ~PENDING_FLAG_JUMP; + // Before adding the RigidBody to the world we must save its oldGravity to the side + // because adding an object to the world will overwrite it with the default gravity. + btVector3 oldGravity = _rigidBody->getGravity(); _dynamicsWorld->addRigidBody(_rigidBody, COLLISION_GROUP_MY_AVATAR, COLLISION_MASK_MY_AVATAR); _dynamicsWorld->addAction(this); - //reset(_dynamicsWorld); + // restore gravity settings + _rigidBody->setGravity(oldGravity); } } if (_dynamicsWorld) { @@ -48,5 +99,283 @@ void CharacterController::setDynamicsWorld(btDynamicsWorld* world) { } else { _pendingFlags &= ~PENDING_FLAG_REMOVE_FROM_SIMULATION; } -} +} +void CharacterController::preStep(btCollisionWorld* collisionWorld) { + // trace a ray straight down to see if we're standing on the ground + const btTransform& xform = _rigidBody->getWorldTransform(); + + // rayStart is at center of bottom sphere + btVector3 rayStart = xform.getOrigin() - _halfHeight * _currentUp; + + // rayEnd is some short distance outside bottom sphere + const btScalar FLOOR_PROXIMITY_THRESHOLD = 0.3f * _radius; + btScalar rayLength = _radius + FLOOR_PROXIMITY_THRESHOLD; + btVector3 rayEnd = rayStart - rayLength * _currentUp; + + // scan down for nearby floor + ClosestNotMe rayCallback(_rigidBody); + rayCallback.m_closestHitFraction = 1.0f; + collisionWorld->rayTest(rayStart, rayEnd, rayCallback); + if (rayCallback.hasHit()) { + _floorDistance = rayLength * rayCallback.m_closestHitFraction - _radius; + } +} + +void CharacterController::playerStep(btCollisionWorld* dynaWorld, btScalar dt) { + btVector3 actualVelocity = _rigidBody->getLinearVelocity(); + btScalar actualSpeed = actualVelocity.length(); + + btVector3 desiredVelocity = _walkVelocity; + btScalar desiredSpeed = desiredVelocity.length(); + + const btScalar MIN_UP_PUSH = 0.1f; + if (desiredVelocity.dot(_currentUp) < MIN_UP_PUSH) { + _isPushingUp = false; + } + + const btScalar MIN_SPEED = 0.001f; + if (_isHovering) { + if (desiredSpeed < MIN_SPEED) { + if (actualSpeed < MIN_SPEED) { + _rigidBody->setLinearVelocity(btVector3(0.0f, 0.0f, 0.0f)); + } else { + const btScalar HOVER_BRAKING_TIMESCALE = 0.1f; + btScalar tau = glm::max(dt / HOVER_BRAKING_TIMESCALE, 1.0f); + _rigidBody->setLinearVelocity((1.0f - tau) * actualVelocity); + } + } else { + const btScalar HOVER_ACCELERATION_TIMESCALE = 0.1f; + btScalar tau = dt / HOVER_ACCELERATION_TIMESCALE; + _rigidBody->setLinearVelocity(actualVelocity - tau * (actualVelocity - desiredVelocity)); + } + } else { + if (onGround()) { + // walking on ground + if (desiredSpeed < MIN_SPEED) { + if (actualSpeed < MIN_SPEED) { + _rigidBody->setLinearVelocity(btVector3(0.0f, 0.0f, 0.0f)); + } else { + const btScalar HOVER_BRAKING_TIMESCALE = 0.1f; + btScalar tau = dt / HOVER_BRAKING_TIMESCALE; + _rigidBody->setLinearVelocity((1.0f - tau) * actualVelocity); + } + } else { + // TODO: modify desiredVelocity using floor normal + const btScalar WALK_ACCELERATION_TIMESCALE = 0.1f; + btScalar tau = dt / WALK_ACCELERATION_TIMESCALE; + btVector3 velocityCorrection = tau * (desiredVelocity - actualVelocity); + // subtract vertical component + velocityCorrection -= velocityCorrection.dot(_currentUp) * _currentUp; + _rigidBody->setLinearVelocity(actualVelocity + velocityCorrection); + } + } else { + // transitioning to flying + btVector3 velocityCorrection = desiredVelocity - actualVelocity; + const btScalar FLY_ACCELERATION_TIMESCALE = 0.2f; + btScalar tau = dt / FLY_ACCELERATION_TIMESCALE; + if (!_isPushingUp) { + // actually falling --> compute a different velocity attenuation factor + const btScalar FALL_ACCELERATION_TIMESCALE = 2.0f; + tau = dt / FALL_ACCELERATION_TIMESCALE; + // zero vertical component + velocityCorrection -= velocityCorrection.dot(_currentUp) * _currentUp; + } + _rigidBody->setLinearVelocity(actualVelocity + tau * velocityCorrection); + } + } + + // Rather than add _followVelocity to the velocity of the RigidBody, we explicitly teleport + // the RigidBody forward according to the formula: distance = rate * time + if (_followVelocity.length2() > 0.0f) { + btTransform bodyTransform = _rigidBody->getWorldTransform(); + bodyTransform.setOrigin(bodyTransform.getOrigin() + dt * _followVelocity); + _rigidBody->setWorldTransform(bodyTransform); + } + _followTime += dt; +} + +void CharacterController::jump() { + // check for case where user is holding down "jump" key... + // we'll eventually tansition to "hover" + if (!_isJumping) { + if (!_isHovering) { + _jumpToHoverStart = usecTimestampNow(); + _pendingFlags |= PENDING_FLAG_JUMP; + } + } else { + quint64 now = usecTimestampNow(); + const quint64 JUMP_TO_HOVER_PERIOD = 75 * (USECS_PER_SECOND / 100); + if (now - _jumpToHoverStart > JUMP_TO_HOVER_PERIOD) { + _isPushingUp = true; + setHovering(true); + } + } +} + +bool CharacterController::onGround() const { + const btScalar FLOOR_PROXIMITY_THRESHOLD = 0.3f * _radius; + return _floorDistance < FLOOR_PROXIMITY_THRESHOLD; +} + +void CharacterController::setHovering(bool hover) { + if (hover != _isHovering) { + _isHovering = hover; + _isJumping = false; + + if (_rigidBody) { + if (hover) { + _rigidBody->setGravity(btVector3(0.0f, 0.0f, 0.0f)); + } else { + _rigidBody->setGravity(DEFAULT_CHARACTER_GRAVITY * _currentUp); + } + } + } +} + +void CharacterController::setLocalBoundingBox(const glm::vec3& corner, const glm::vec3& scale) { + _boxScale = scale; + + float x = _boxScale.x; + float z = _boxScale.z; + float radius = 0.5f * sqrtf(0.5f * (x * x + z * z)); + float halfHeight = 0.5f * _boxScale.y - radius; + float MIN_HALF_HEIGHT = 0.1f; + if (halfHeight < MIN_HALF_HEIGHT) { + halfHeight = MIN_HALF_HEIGHT; + } + + // compare dimensions + float radiusDelta = glm::abs(radius - _radius); + float heightDelta = glm::abs(halfHeight - _halfHeight); + if (radiusDelta < FLT_EPSILON && heightDelta < FLT_EPSILON) { + // shape hasn't changed --> nothing to do + } else { + if (_dynamicsWorld) { + // must REMOVE from world prior to shape update + _pendingFlags |= PENDING_FLAG_REMOVE_FROM_SIMULATION; + } + _pendingFlags |= PENDING_FLAG_UPDATE_SHAPE; + // only need to ADD back when we happen to be enabled + if (_enabled) { + _pendingFlags |= PENDING_FLAG_ADD_TO_SIMULATION; + } + } + + // it's ok to change offset immediately -- there are no thread safety issues here + _shapeLocalOffset = corner + 0.5f * _boxScale; +} + +void CharacterController::setEnabled(bool enabled) { + if (enabled != _enabled) { + if (enabled) { + // Don't bother clearing REMOVE bit since it might be paired with an UPDATE_SHAPE bit. + // Setting the ADD bit here works for all cases so we don't even bother checking other bits. + _pendingFlags |= PENDING_FLAG_ADD_TO_SIMULATION; + } else { + if (_dynamicsWorld) { + _pendingFlags |= PENDING_FLAG_REMOVE_FROM_SIMULATION; + } + _pendingFlags &= ~ PENDING_FLAG_ADD_TO_SIMULATION; + _isOnGround = false; + } + setHovering(true); + _enabled = enabled; + } +} + +void CharacterController::updateUpAxis(const glm::quat& rotation) { + btVector3 oldUp = _currentUp; + _currentUp = quatRotate(glmToBullet(rotation), LOCAL_UP_AXIS); + if (!_isHovering) { + const btScalar MIN_UP_ERROR = 0.01f; + if (oldUp.distance(_currentUp) > MIN_UP_ERROR) { + _rigidBody->setGravity(DEFAULT_CHARACTER_GRAVITY * _currentUp); + } + } +} + +void CharacterController::setPositionAndOrientation( + const glm::vec3& position, + const glm::quat& orientation) { + // TODO: update gravity if up has changed + updateUpAxis(orientation); + + btQuaternion bodyOrientation = glmToBullet(orientation); + btVector3 bodyPosition = glmToBullet(position + orientation * _shapeLocalOffset); + _characterBodyTransform = btTransform(bodyOrientation, bodyPosition); +} + +void CharacterController::getPositionAndOrientation(glm::vec3& position, glm::quat& rotation) const { + if (_enabled && _rigidBody) { + const btTransform& avatarTransform = _rigidBody->getWorldTransform(); + rotation = bulletToGLM(avatarTransform.getRotation()); + position = bulletToGLM(avatarTransform.getOrigin()) - rotation * _shapeLocalOffset; + } +} + +void CharacterController::setTargetVelocity(const glm::vec3& velocity) { + //_walkVelocity = glmToBullet(_avatarData->getTargetVelocity()); + _walkVelocity = glmToBullet(velocity); +} + +void CharacterController::setFollowVelocity(const glm::vec3& velocity) { + _followVelocity = glmToBullet(velocity); +} + +glm::vec3 CharacterController::getLinearVelocity() const { + glm::vec3 velocity(0.0f); + if (_rigidBody) { + velocity = bulletToGLM(_rigidBody->getLinearVelocity()); + } + return velocity; +} + +void CharacterController::preSimulation() { + if (_enabled && _dynamicsWorld) { + // slam body to where it is supposed to be + _rigidBody->setWorldTransform(_characterBodyTransform); + + // scan for distant floor + // rayStart is at center of bottom sphere + btVector3 rayStart = _characterBodyTransform.getOrigin() - _halfHeight * _currentUp; + + // rayEnd is straight down MAX_FALL_HEIGHT + btScalar rayLength = _radius + MAX_FALL_HEIGHT; + btVector3 rayEnd = rayStart - rayLength * _currentUp; + + ClosestNotMe rayCallback(_rigidBody); + rayCallback.m_closestHitFraction = 1.0f; + _dynamicsWorld->rayTest(rayStart, rayEnd, rayCallback); + if (rayCallback.hasHit()) { + _floorDistance = rayLength * rayCallback.m_closestHitFraction - _radius; + const btScalar MIN_HOVER_HEIGHT = 3.0f; + if (_isHovering && _floorDistance < MIN_HOVER_HEIGHT && !_isPushingUp) { + setHovering(false); + } + // TODO: use collision events rather than ray-trace test to disable jumping + const btScalar JUMP_PROXIMITY_THRESHOLD = 0.1f * _radius; + if (_floorDistance < JUMP_PROXIMITY_THRESHOLD) { + _isJumping = false; + } + } else { + _floorDistance = FLT_MAX; + setHovering(true); + } + + if (_pendingFlags & PENDING_FLAG_JUMP) { + _pendingFlags &= ~ PENDING_FLAG_JUMP; + if (onGround()) { + _isJumping = true; + btVector3 velocity = _rigidBody->getLinearVelocity(); + velocity += _jumpSpeed * _currentUp; + _rigidBody->setLinearVelocity(velocity); + } + } + } + _followTime = 0.0f; +} + +void CharacterController::postSimulation() { + // postSimulation() exists for symmetry and just in case we need to do something here later +} diff --git a/libraries/physics/src/CharacterController.h b/libraries/physics/src/CharacterController.h index e9e6f1328e..6edbabdab8 100644 --- a/libraries/physics/src/CharacterController.h +++ b/libraries/physics/src/CharacterController.h @@ -17,11 +17,14 @@ #include #include +#include + const uint32_t PENDING_FLAG_ADD_TO_SIMULATION = 1U << 0; const uint32_t PENDING_FLAG_REMOVE_FROM_SIMULATION = 1U << 1; const uint32_t PENDING_FLAG_UPDATE_SHAPE = 1U << 2; const uint32_t PENDING_FLAG_JUMP = 1U << 3; +const float DEFAULT_CHARACTER_GRAVITY = -5.0f; class btRigidBody; class btCollisionWorld; @@ -29,31 +32,89 @@ class btDynamicsWorld; class CharacterController : public btCharacterControllerInterface { public: + CharacterController(); + virtual ~CharacterController() {} + bool needsRemoval() const; bool needsAddition() const; void setDynamicsWorld(btDynamicsWorld* world); btCollisionObject* getCollisionObject() { return _rigidBody; } virtual void updateShapeIfNecessary() = 0; - virtual void preSimulation() = 0; - virtual void postSimulation() = 0; + // overrides from btCharacterControllerInterface virtual void setWalkDirection(const btVector3 &walkDirection) { assert(false); } + virtual void setVelocityForTimeInterval(const btVector3 &velocity, btScalar timeInterval) override { assert(false); } + virtual void reset(btCollisionWorld* collisionWorld) override { } + virtual void warp(const btVector3& origin) override { } + virtual void debugDraw(btIDebugDraw* debugDrawer) override { } + virtual void setUpInterpolate(bool value) override { } + virtual void updateAction(btCollisionWorld* collisionWorld, btScalar deltaTime) override { + preStep(collisionWorld); + playerStep(collisionWorld, deltaTime); + } + virtual void preStep(btCollisionWorld *collisionWorld) override; + virtual void playerStep(btCollisionWorld *collisionWorld, btScalar dt) override; + virtual bool canJump() const override { assert(false); return false; } // never call this + virtual void jump() override; + virtual bool onGround() const; + + void preSimulation(); + void postSimulation(); + + void setPositionAndOrientation( const glm::vec3& position, const glm::quat& orientation); + void getPositionAndOrientation(glm::vec3& position, glm::quat& rotation) const; + + void setTargetVelocity(const glm::vec3& velocity); + void setFollowVelocity(const glm::vec3& velocity); + float getFollowTime() const { return _followTime; } + + glm::vec3 getLinearVelocity() const; + + bool isHovering() const { return _isHovering; } + void setHovering(bool enabled); + + void setLocalBoundingBox(const glm::vec3& corner, const glm::vec3& scale); + + void setEnabled(bool enabled); + bool isEnabled() const { return _enabled && _dynamicsWorld; } - /* these from btCharacterControllerInterface remain to be overridden - virtual void setVelocityForTimeInterval(const btVector3 &velocity, btScalar timeInterval) = 0; - virtual void reset() = 0; - virtual void warp(const btVector3 &origin) = 0; - virtual void preStep(btCollisionWorld *collisionWorld) = 0; - virtual void playerStep(btCollisionWorld *collisionWorld, btScalar dt) = 0; - virtual bool canJump() const = 0; - virtual void jump() = 0; - virtual bool onGround() const = 0; - */ protected: + void updateUpAxis(const glm::quat& rotation); + +protected: + btVector3 _currentUp; + btVector3 _walkVelocity; + btVector3 _followVelocity; + btTransform _characterBodyTransform; + + glm::vec3 _shapeLocalOffset; + + glm::vec3 _boxScale; // used to compute capsule shape + + quint64 _jumpToHoverStart; + + btScalar _halfHeight; + btScalar _radius; + + btScalar _floorDistance; + + btScalar _gravity; + + btScalar _jumpSpeed; + btScalar _followTime; + + bool _enabled; + bool _isOnGround; + bool _isJumping; + bool _isFalling; + bool _isHovering; + bool _isPushingUp; + btDynamicsWorld* _dynamicsWorld { nullptr }; btRigidBody* _rigidBody { nullptr }; uint32_t _pendingFlags { 0 }; + }; #endif // hifi_CharacterControllerInterface_h diff --git a/libraries/plugins/src/plugins/DisplayPlugin.h b/libraries/plugins/src/plugins/DisplayPlugin.h index 83afbc9402..7f3b38e4a2 100644 --- a/libraries/plugins/src/plugins/DisplayPlugin.h +++ b/libraries/plugins/src/plugins/DisplayPlugin.h @@ -58,6 +58,7 @@ public: /// By default, all HMDs are stereo virtual bool isStereo() const { return isHmd(); } virtual bool isThrottled() const { return false; } + virtual float getTargetFrameRate() { return 0.0f; } // Rendering support diff --git a/libraries/procedural/src/procedural/ProceduralSkybox.cpp b/libraries/procedural/src/procedural/ProceduralSkybox.cpp index bd61de7338..68645045b4 100644 --- a/libraries/procedural/src/procedural/ProceduralSkybox.cpp +++ b/libraries/procedural/src/procedural/ProceduralSkybox.cpp @@ -51,14 +51,6 @@ void ProceduralSkybox::render(gpu::Batch& batch, const ViewFrustum& viewFrustum, static gpu::Stream::FormatPointer theFormat; if (skybox._procedural && skybox._procedural->_enabled && skybox._procedural->ready()) { - if (!theBuffer) { - const float CLIP = 1.0f; - const glm::vec2 vertices[4] = { { -CLIP, -CLIP }, { CLIP, -CLIP }, { -CLIP, CLIP }, { CLIP, CLIP } }; - theBuffer = std::make_shared(sizeof(vertices), (const gpu::Byte*) vertices); - theFormat = std::make_shared(); - theFormat->setAttribute(gpu::Stream::POSITION, gpu::Stream::POSITION, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::XYZ)); - } - glm::mat4 projMat; viewFrustum.evalProjectionMatrix(projMat); @@ -67,8 +59,6 @@ void ProceduralSkybox::render(gpu::Batch& batch, const ViewFrustum& viewFrustum, batch.setProjectionTransform(projMat); batch.setViewTransform(viewTransform); batch.setModelTransform(Transform()); // only for Mac - batch.setInputBuffer(gpu::Stream::POSITION, theBuffer, 0, 8); - batch.setInputFormat(theFormat); if (skybox.getCubemap() && skybox.getCubemap()->isDefined()) { batch.setResourceTexture(0, skybox.getCubemap()); diff --git a/libraries/render-utils/src/GeometryCache.cpp b/libraries/render-utils/src/GeometryCache.cpp index 75b3b91c10..b9fdcd95a4 100644 --- a/libraries/render-utils/src/GeometryCache.cpp +++ b/libraries/render-utils/src/GeometryCache.cpp @@ -214,6 +214,19 @@ VertexVector tesselate(const VertexVector& startingTriangles, int count) { return triangles; } +size_t GeometryCache::getShapeTriangleCount(Shape shape) { + return _shapes[shape]._indexCount / VERTICES_PER_TRIANGLE; +} + +size_t GeometryCache::getSphereTriangleCount() { + return getShapeTriangleCount(Sphere); +} + +size_t GeometryCache::getCubeTriangleCount() { + return getShapeTriangleCount(Cube); +} + + // FIXME solids need per-face vertices, but smooth shaded // components do not. Find a way to support using draw elements // or draw arrays as appropriate @@ -1727,3 +1740,4 @@ void GeometryCache::useSimpleDrawPipeline(gpu::Batch& batch, bool noBlend) { batch.setPipeline(_standardDrawPipeline); } } + diff --git a/libraries/render-utils/src/GeometryCache.h b/libraries/render-utils/src/GeometryCache.h index aa1593db78..c408115011 100644 --- a/libraries/render-utils/src/GeometryCache.h +++ b/libraries/render-utils/src/GeometryCache.h @@ -151,16 +151,19 @@ public: void renderWireShapeInstances(gpu::Batch& batch, Shape shape, size_t count, gpu::BufferPointer& transformBuffer, gpu::BufferPointer& colorBuffer); void renderShape(gpu::Batch& batch, Shape shape); void renderWireShape(gpu::Batch& batch, Shape shape); + size_t getShapeTriangleCount(Shape shape); void renderCubeInstances(gpu::Batch& batch, size_t count, gpu::BufferPointer transformBuffer, gpu::BufferPointer colorBuffer); void renderWireCubeInstances(gpu::Batch& batch, size_t count, gpu::BufferPointer transformBuffer, gpu::BufferPointer colorBuffer); void renderCube(gpu::Batch& batch); void renderWireCube(gpu::Batch& batch); + size_t getCubeTriangleCount(); void renderSphereInstances(gpu::Batch& batch, size_t count, gpu::BufferPointer transformBuffer, gpu::BufferPointer colorBuffer); void renderWireSphereInstances(gpu::Batch& batch, size_t count, gpu::BufferPointer transformBuffer, gpu::BufferPointer colorBuffer); void renderSphere(gpu::Batch& batch); void renderWireSphere(gpu::Batch& batch); + size_t getSphereTriangleCount(); void renderGrid(gpu::Batch& batch, int xDivisions, int yDivisions, const glm::vec4& color); void renderGrid(gpu::Batch& batch, int x, int y, int width, int height, int rows, int cols, const glm::vec4& color, int id = UNKNOWN_ID); diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index bb37bb4932..2d8d7b598b 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -25,7 +25,7 @@ namespace render { // Return opaque for lack of a better idea return ItemKey::Builder::opaqueShape(); } - + template <> const Item::Bound payloadGetBound(const MeshPartPayload::Pointer& payload) { if (payload) { return payload->getBound(); @@ -39,55 +39,40 @@ namespace render { using namespace render; -MeshPartPayload::MeshPartPayload(Model* model, int meshIndex, int partIndex, int shapeIndex, - glm::vec3 position, glm::quat orientation) : - model(model), - meshIndex(meshIndex), - partIndex(partIndex), - _shapeID(shapeIndex), - _modelPosition(position), - _modelOrientation(orientation) { - initCache(); +MeshPartPayload::MeshPartPayload(model::MeshPointer mesh, int partIndex, model::MaterialPointer material, const Transform& transform, const Transform& offsetTransform) { + + updateMeshPart(mesh, partIndex); + updateMaterial(material); + updateTransform(transform, offsetTransform); } -void MeshPartPayload::initCache() { - const std::vector>& networkMeshes = model->_geometry->getMeshes(); - const NetworkMesh& networkMesh = *(networkMeshes.at(meshIndex).get()); - _drawMesh = networkMesh._mesh; - - const FBXGeometry& geometry = model->_geometry->getFBXGeometry(); - const FBXMesh& mesh = geometry.meshes.at(meshIndex); - _hasColorAttrib = !mesh.colors.isEmpty(); - _isBlendShaped = !mesh.blendshapes.isEmpty(); - _isSkinned = !mesh.clusterIndices.isEmpty(); - - - _drawPart = _drawMesh->getPartBuffer().get(partIndex); - - auto networkMaterial = model->_geometry->getShapeMaterial(_shapeID); - if (networkMaterial) { - _drawMaterial = networkMaterial->_material; - }; +void MeshPartPayload::updateMeshPart(model::MeshPointer drawMesh, int partIndex) { + _drawMesh = drawMesh; + if (_drawMesh) { + auto vertexFormat = _drawMesh->getVertexFormat(); + _hasColorAttrib = vertexFormat->hasAttribute(gpu::Stream::COLOR); + _drawPart = _drawMesh->getPartBuffer().get(partIndex); + _localBound = _drawMesh->evalPartBound(partIndex); + } } -void MeshPartPayload::updateModelLocation(glm::vec3 position, glm::quat orientation) { - _modelPosition = position; - _modelOrientation = orientation; +void MeshPartPayload::updateTransform(const Transform& transform, const Transform& offsetTransform) { + _transform = transform; + _offsetTransform = offsetTransform; + Transform::mult(_drawTransform, _transform, _offsetTransform); + _worldBound = _localBound; + _worldBound.transform(_drawTransform); +} + +void MeshPartPayload::updateMaterial(model::MaterialPointer drawMaterial) { + _drawMaterial = drawMaterial; } render::ItemKey MeshPartPayload::getKey() const { ItemKey::Builder builder; builder.withTypeShape(); - if (!model->isVisible()) { - builder.withInvisible(); - } - - if (_isBlendShaped || _isSkinned) { - builder.withDeformed(); - } - if (_drawMaterial) { auto matKey = _drawMaterial->getKey(); if (matKey.isTransparent() || matKey.isTransparentMap()) { @@ -99,9 +84,7 @@ render::ItemKey MeshPartPayload::getKey() const { } render::Item::Bound MeshPartPayload::getBound() const { - // NOTE: we can't cache this bounds because we need to handle the case of a moving - // entity or mesh part. - return model->getPartBounds(meshIndex, partIndex, _modelPosition, _modelOrientation); + return _worldBound; } void MeshPartPayload::drawCall(gpu::Batch& batch) const { @@ -109,22 +92,12 @@ void MeshPartPayload::drawCall(gpu::Batch& batch) const { } void MeshPartPayload::bindMesh(gpu::Batch& batch) const { - if (!_isBlendShaped) { - batch.setIndexBuffer(gpu::UINT32, (_drawMesh->getIndexBuffer()._buffer), 0); - - batch.setInputFormat((_drawMesh->getVertexFormat())); - - batch.setInputStream(0, _drawMesh->getVertexStream()); - } else { - batch.setIndexBuffer(gpu::UINT32, (_drawMesh->getIndexBuffer()._buffer), 0); + batch.setIndexBuffer(gpu::UINT32, (_drawMesh->getIndexBuffer()._buffer), 0); - batch.setInputFormat((_drawMesh->getVertexFormat())); + batch.setInputFormat((_drawMesh->getVertexFormat())); + + batch.setInputStream(0, _drawMesh->getVertexStream()); - batch.setInputBuffer(0, model->_blendedVertexBuffers[meshIndex], 0, sizeof(glm::vec3)); - batch.setInputBuffer(1, model->_blendedVertexBuffers[meshIndex], _drawMesh->getNumVertices() * sizeof(glm::vec3), sizeof(glm::vec3)); - batch.setInputStream(2, _drawMesh->getVertexStream().makeRangedStream(2)); - } - // TODO: Get rid of that extra call if (!_hasColorAttrib) { batch._glColor4f(1.0f, 1.0f, 1.0f, 1.0f); @@ -215,31 +188,208 @@ void MeshPartPayload::bindMaterial(gpu::Batch& batch, const ModelRender::Locatio } void MeshPartPayload::bindTransform(gpu::Batch& batch, const ModelRender::Locations* locations) const { - // Still relying on the raw data from the model - const Model::MeshState& state = model->_meshStates.at(meshIndex); - - Transform transform; - if (state.clusterBuffer) { - if (model->_cauterizeBones) { - batch.setUniformBuffer(ModelRender::SKINNING_GPU_SLOT, state.cauterizedClusterBuffer); - } else { - batch.setUniformBuffer(ModelRender::SKINNING_GPU_SLOT, state.clusterBuffer); - } - } else { - if (model->_cauterizeBones) { - transform = Transform(state.cauterizedClusterMatrices[0]); - } else { - transform = Transform(state.clusterMatrices[0]); - } - } - transform.preTranslate(_modelPosition); - batch.setModelTransform(transform); + batch.setModelTransform(_drawTransform); } void MeshPartPayload::render(RenderArgs* args) const { PerformanceTimer perfTimer("MeshPartPayload::render"); - if (!model->_readyWhenAdded || !model->_isVisible) { + + + gpu::Batch& batch = *(args->_batch); + auto mode = args->_renderMode; + + auto alphaThreshold = args->_alphaThreshold; //translucent ? TRANSPARENT_ALPHA_THRESHOLD : OPAQUE_ALPHA_THRESHOLD; // FIX ME + + model::MaterialKey drawMaterialKey; + if (_drawMaterial) { + drawMaterialKey = _drawMaterial->getKey(); + } + bool translucentMesh = drawMaterialKey.isTransparent() || drawMaterialKey.isTransparentMap(); + + bool hasTangents = drawMaterialKey.isNormalMap(); + bool hasSpecular = drawMaterialKey.isGlossMap(); + bool hasLightmap = drawMaterialKey.isLightmapMap(); + bool isSkinned = false; + bool wireframe = false; + if (wireframe) { + translucentMesh = hasTangents = hasSpecular = hasLightmap = isSkinned = false; + } + + ModelRender::Locations* locations = nullptr; + ModelRender::pickPrograms(batch, mode, translucentMesh, alphaThreshold, hasLightmap, hasTangents, hasSpecular, isSkinned, wireframe, + args, locations); + + + // Bind the model transform and the skinCLusterMatrices if needed + bindTransform(batch, locations); + + //Bind the index buffer and vertex buffer and Blend shapes if needed + bindMesh(batch); + + // apply material properties + bindMaterial(batch, locations); + + + // TODO: We should be able to do that just in the renderTransparentJob + if (translucentMesh && locations->lightBufferUnit >= 0) { + PerformanceTimer perfTimer("DLE->setupTransparent()"); + + DependencyManager::get()->setupTransparent(args, locations->lightBufferUnit); + } + if (args) { + args->_details._materialSwitches++; + } + + // Draw! + { + PerformanceTimer perfTimer("batch.drawIndexed()"); + drawCall(batch); + } + + if (args) { + const int INDICES_PER_TRIANGLE = 3; + args->_details._trianglesRendered += _drawPart._numIndices / INDICES_PER_TRIANGLE; + } +} + + + +namespace render { + template <> const ItemKey payloadGetKey(const ModelMeshPartPayload::Pointer& payload) { + if (payload) { + return payload->getKey(); + } + // Return opaque for lack of a better idea + return ItemKey::Builder::opaqueShape(); + } + + template <> const Item::Bound payloadGetBound(const ModelMeshPartPayload::Pointer& payload) { + if (payload) { + return payload->getBound(); + } + return render::Item::Bound(); + } + template <> void payloadRender(const ModelMeshPartPayload::Pointer& payload, RenderArgs* args) { + return payload->render(args); + } +} + +using namespace render; + +ModelMeshPartPayload::ModelMeshPartPayload(Model* model, int _meshIndex, int partIndex, int shapeIndex, const Transform& transform, const Transform& offsetTransform) : + _model(model), + _meshIndex(_meshIndex), + _shapeID(shapeIndex) { + auto& modelMesh = _model->_geometry->getMeshes().at(_meshIndex)->_mesh; + updateMeshPart(modelMesh, partIndex); + + updateTransform(transform, offsetTransform); + initCache(); +} + +void ModelMeshPartPayload::initCache() { + if (_drawMesh) { + auto vertexFormat = _drawMesh->getVertexFormat(); + _hasColorAttrib = vertexFormat->hasAttribute(gpu::Stream::COLOR); + _isSkinned = vertexFormat->hasAttribute(gpu::Stream::SKIN_CLUSTER_WEIGHT) && vertexFormat->hasAttribute(gpu::Stream::SKIN_CLUSTER_INDEX); + + + const FBXGeometry& geometry = _model->_geometry->getFBXGeometry(); + const FBXMesh& mesh = geometry.meshes.at(_meshIndex); + _isBlendShaped = !mesh.blendshapes.isEmpty(); + } + + auto networkMaterial = _model->_geometry->getShapeMaterial(_shapeID); + if (networkMaterial) { + _drawMaterial = networkMaterial->_material; + }; + +} + + +void ModelMeshPartPayload::notifyLocationChanged() { + _model->_needsUpdateClusterMatrices = true; +} + +render::ItemKey ModelMeshPartPayload::getKey() const { + ItemKey::Builder builder; + builder.withTypeShape(); + + if (!_model->isVisible()) { + builder.withInvisible(); + } + + if (_isBlendShaped || _isSkinned) { + builder.withDeformed(); + } + + if (_drawMaterial) { + auto matKey = _drawMaterial->getKey(); + if (matKey.isTransparent() || matKey.isTransparentMap()) { + builder.withTransparent(); + } + } + + return builder.build(); +} + +render::Item::Bound ModelMeshPartPayload::getBound() const { + // NOTE: we can't cache this bounds because we need to handle the case of a moving + // entity or mesh part. + return _model->getPartBounds(_meshIndex, _partIndex, _transform.getTranslation(), _transform.getRotation()); +} + +void ModelMeshPartPayload::bindMesh(gpu::Batch& batch) const { + if (!_isBlendShaped) { + batch.setIndexBuffer(gpu::UINT32, (_drawMesh->getIndexBuffer()._buffer), 0); + + batch.setInputFormat((_drawMesh->getVertexFormat())); + + batch.setInputStream(0, _drawMesh->getVertexStream()); + } else { + batch.setIndexBuffer(gpu::UINT32, (_drawMesh->getIndexBuffer()._buffer), 0); + + batch.setInputFormat((_drawMesh->getVertexFormat())); + + batch.setInputBuffer(0, _model->_blendedVertexBuffers[_meshIndex], 0, sizeof(glm::vec3)); + batch.setInputBuffer(1, _model->_blendedVertexBuffers[_meshIndex], _drawMesh->getNumVertices() * sizeof(glm::vec3), sizeof(glm::vec3)); + batch.setInputStream(2, _drawMesh->getVertexStream().makeRangedStream(2)); + } + + // TODO: Get rid of that extra call + if (!_hasColorAttrib) { + batch._glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + } +} + +void ModelMeshPartPayload::bindTransform(gpu::Batch& batch, const ModelRender::Locations* locations) const { + // Still relying on the raw data from the model + const Model::MeshState& state = _model->_meshStates.at(_meshIndex); + + Transform transform; + if (state.clusterBuffer) { + if (_model->_cauterizeBones) { + batch.setUniformBuffer(ModelRender::SKINNING_GPU_SLOT, state.cauterizedClusterBuffer); + } else { + batch.setUniformBuffer(ModelRender::SKINNING_GPU_SLOT, state.clusterBuffer); + } + } else { + if (_model->_cauterizeBones) { + transform = Transform(state.cauterizedClusterMatrices[0]); + } else { + transform = Transform(state.clusterMatrices[0]); + } + } + // transform.preTranslate(_modelPosition); + transform.preTranslate(_transform.getTranslation()); + batch.setModelTransform(transform); +} + + +void ModelMeshPartPayload::render(RenderArgs* args) const { + PerformanceTimer perfTimer("ModelMeshPartPayload::render"); + if (!_model->_readyWhenAdded || !_model->_isVisible) { return; // bail asap } @@ -248,25 +398,25 @@ void MeshPartPayload::render(RenderArgs* args) const { auto alphaThreshold = args->_alphaThreshold; //translucent ? TRANSPARENT_ALPHA_THRESHOLD : OPAQUE_ALPHA_THRESHOLD; // FIX ME - const FBXGeometry& geometry = model->_geometry->getFBXGeometry(); - const std::vector>& networkMeshes = model->_geometry->getMeshes(); + const FBXGeometry& geometry = _model->_geometry->getFBXGeometry(); + const std::vector>& networkMeshes = _model->_geometry->getMeshes(); // guard against partially loaded meshes - if (meshIndex >= (int)networkMeshes.size() || meshIndex >= (int)geometry.meshes.size() || meshIndex >= (int)model->_meshStates.size() ) { + if (_meshIndex >= (int)networkMeshes.size() || _meshIndex >= (int)geometry.meshes.size() || _meshIndex >= (int)_model->_meshStates.size() ) { return; } // Back to model to update the cluster matrices right now - model->updateClusterMatrices(_modelPosition, _modelOrientation); + _model->updateClusterMatrices(_transform.getTranslation(), _transform.getRotation()); - const FBXMesh& mesh = geometry.meshes.at(meshIndex); + const FBXMesh& mesh = geometry.meshes.at(_meshIndex); // if our index is ever out of range for either meshes or networkMeshes, then skip it, and set our _meshGroupsKnown // to false to rebuild out mesh groups. - if (meshIndex < 0 || meshIndex >= (int)networkMeshes.size() || meshIndex > geometry.meshes.size()) { - model->_meshGroupsKnown = false; // regenerate these lists next time around. - model->_readyWhenAdded = false; // in case any of our users are using scenes - model->invalidCalculatedMeshBoxes(); // if we have to reload, we need to assume our mesh boxes are all invalid + if (_meshIndex < 0 || _meshIndex >= (int)networkMeshes.size() || _meshIndex > geometry.meshes.size()) { + _model->_meshGroupsKnown = false; // regenerate these lists next time around. + _model->_readyWhenAdded = false; // in case any of our users are using scenes + _model->invalidCalculatedMeshBoxes(); // if we have to reload, we need to assume our mesh boxes are all invalid return; // FIXME! } @@ -276,13 +426,7 @@ void MeshPartPayload::render(RenderArgs* args) const { // sanity check return; // FIXME! } - - - // guard against partially loaded meshes - if (partIndex >= mesh.parts.size()) { - return; - } - + model::MaterialKey drawMaterialKey; if (_drawMaterial) { drawMaterialKey = _drawMaterial->getKey(); @@ -293,12 +437,12 @@ void MeshPartPayload::render(RenderArgs* args) const { bool hasSpecular = drawMaterialKey.isGlossMap(); bool hasLightmap = drawMaterialKey.isLightmapMap(); bool isSkinned = _isSkinned; - bool wireframe = model->isWireframe(); + bool wireframe = _model->isWireframe(); // render the part bounding box #ifdef DEBUG_BOUNDING_PARTS { - AABox partBounds = getPartBounds(meshIndex, partIndex); + AABox partBounds = getPartBounds(_meshIndex, partIndex); bool inView = args->_viewFrustum->boxInFrustum(partBounds) != ViewFrustum::OUTSIDE; glm::vec4 cubeColor; diff --git a/libraries/render-utils/src/MeshPartPayload.h b/libraries/render-utils/src/MeshPartPayload.h index b29d9510d1..e03d1c8a68 100644 --- a/libraries/render-utils/src/MeshPartPayload.h +++ b/libraries/render-utils/src/MeshPartPayload.h @@ -1,5 +1,5 @@ // -// MeshPartPayload.h +// ModelMeshPartPayload.h // interface/src/renderer // // Created by Sam Gateau on 10/3/15. @@ -24,41 +24,44 @@ class Model; class MeshPartPayload { public: - MeshPartPayload(Model* model, int meshIndex, int partIndex, int shapeIndex, glm::vec3 position, glm::quat orientation); - + MeshPartPayload() {} + MeshPartPayload(model::MeshPointer mesh, int partIndex, model::MaterialPointer material, const Transform& transform, const Transform& offsetTransform); + typedef render::Payload Payload; typedef Payload::DataPointer Pointer; - Model* model; - int meshIndex; - int partIndex; - int _shapeID; - glm::vec3 _modelPosition; - glm::quat _modelOrientation; + virtual void updateMeshPart(model::MeshPointer drawMesh, int partIndex); - void updateModelLocation(glm::vec3 position, glm::quat orientation); + virtual void notifyLocationChanged() {} + virtual void updateTransform(const Transform& transform, const Transform& offsetTransform); + + virtual void updateMaterial(model::MaterialPointer drawMaterial); // Render Item interface - render::ItemKey getKey() const; - render::Item::Bound getBound() const; - void render(RenderArgs* args) const; - - // MeshPartPayload functions to perform render + virtual render::ItemKey getKey() const; + virtual render::Item::Bound getBound() const; + virtual void render(RenderArgs* args) const; + + // ModelMeshPartPayload functions to perform render void drawCall(gpu::Batch& batch) const; - void bindMesh(gpu::Batch& batch) const; - void bindMaterial(gpu::Batch& batch, const ModelRender::Locations* locations) const; - void bindTransform(gpu::Batch& batch, const ModelRender::Locations* locations) const; - - - void initCache(); + virtual void bindMesh(gpu::Batch& batch) const; + virtual void bindMaterial(gpu::Batch& batch, const ModelRender::Locations* locations) const; + virtual void bindTransform(gpu::Batch& batch, const ModelRender::Locations* locations) const; // Payload resource cached values model::MeshPointer _drawMesh; + int _partIndex = 0; model::Mesh::Part _drawPart; + model::MaterialPointer _drawMaterial; + + model::Box _localBound; + Transform _drawTransform; + Transform _transform; + Transform _offsetTransform; + mutable model::Box _worldBound; + bool _hasColorAttrib = false; - bool _isSkinned = false; - bool _isBlendShaped = false; }; namespace render { @@ -67,4 +70,32 @@ namespace render { template <> void payloadRender(const MeshPartPayload::Pointer& payload, RenderArgs* args); } + +class ModelMeshPartPayload : public MeshPartPayload { +public: + ModelMeshPartPayload(Model* model, int meshIndex, int partIndex, int shapeIndex, const Transform& transform, const Transform& offsetTransform); + + typedef render::Payload Payload; + typedef Payload::DataPointer Pointer; + + void notifyLocationChanged() override; + + // Render Item interface + render::ItemKey getKey() const override; + render::Item::Bound getBound() const override; + void render(RenderArgs* args) const override; + + // ModelMeshPartPayload functions to perform render + void bindMesh(gpu::Batch& batch) const override; + void bindTransform(gpu::Batch& batch, const ModelRender::Locations* locations) const override; + + + void initCache(); + Model* _model; + int _meshIndex; + int _shapeID; + bool _isSkinned = false; + bool _isBlendShaped = false; +}; + #endif // hifi_MeshPartPayload_h diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index b367d299e5..99261a02b4 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -90,7 +90,7 @@ void Model::setScale(const glm::vec3& scale) { _scaledToFit = false; } -const float METERS_PER_MILLIMETER = 0.01f; +const float METERS_PER_MILLIMETER = 0.01f; void Model::setScaleInternal(const glm::vec3& scale) { if (glm::distance(_scale, scale) > METERS_PER_MILLIMETER) { @@ -110,11 +110,19 @@ void Model::setOffset(const glm::vec3& offset) { void Model::enqueueLocationChange() { render::ScenePointer scene = AbstractViewStateInterface::instance()->getMain3DScene(); + Transform transform; + transform.setTranslation(_translation); + transform.setRotation(_rotation); + + Transform offset; + offset.setScale(_scale); + offset.postTranslate(_offset); + render::PendingChanges pendingChanges; foreach (auto itemID, _renderItems.keys()) { - pendingChanges.updateItem(itemID, [=](MeshPartPayload& data) { - data.updateModelLocation(_translation, _rotation); - data.model->_needsUpdateClusterMatrices = true; + pendingChanges.updateItem(itemID, [transform, offset](MeshPartPayload& data) { + data.updateTransform(transform, offset); + data.notifyLocationChanged(); }); } @@ -495,11 +503,10 @@ bool Model::addToScene(std::shared_ptr scene, render::PendingChan foreach (auto renderItem, _renderItemsSet) { auto item = scene->allocateID(); - auto renderData = MeshPartPayload::Pointer(renderItem); - auto renderPayload = std::make_shared(renderData); + auto renderPayload = std::make_shared(renderItem); pendingChanges.resetItem(item, renderPayload); - pendingChanges.updateItem(item, [&](MeshPartPayload& data) { - data.model->_needsUpdateClusterMatrices = true; + pendingChanges.updateItem(item, [](MeshPartPayload& data) { + data.notifyLocationChanged(); }); _renderItems.insert(item, renderPayload); somethingAdded = true; @@ -523,12 +530,11 @@ bool Model::addToScene(std::shared_ptr scene, foreach (auto renderItem, _renderItemsSet) { auto item = scene->allocateID(); - auto renderData = MeshPartPayload::Pointer(renderItem); - auto renderPayload = std::make_shared(renderData); + auto renderPayload = std::make_shared(renderItem); renderPayload->addStatusGetters(statusGetters); pendingChanges.resetItem(item, renderPayload); - pendingChanges.updateItem(item, [&](MeshPartPayload& data) { - data.model->_needsUpdateClusterMatrices = true; + pendingChanges.updateItem(item, [](MeshPartPayload& data) { + data.notifyLocationChanged(); }); _renderItems.insert(item, renderPayload); somethingAdded = true; @@ -1127,8 +1133,14 @@ AABox Model::getPartBounds(int meshIndex, int partIndex, glm::vec3 modelPosition void Model::segregateMeshGroups() { QSharedPointer networkGeometry; - if (_showCollisionHull && _collisionGeometry && _collisionGeometry->isLoaded()) { - networkGeometry = _collisionGeometry; + bool showingCollisionHull = false; + if (_showCollisionHull && _collisionGeometry) { + if (_collisionGeometry->isLoaded()) { + networkGeometry = _collisionGeometry; + showingCollisionHull = true; + } else { + return; + } } else { networkGeometry = _geometry; } @@ -1136,8 +1148,10 @@ void Model::segregateMeshGroups() { const std::vector>& networkMeshes = networkGeometry->getMeshes(); // all of our mesh vectors must match in size - if ((int)networkMeshes.size() != geometry.meshes.size() || - geometry.meshes.size() != _meshStates.size()) { + auto geoMeshesSize = geometry.meshes.size(); + if ((int)networkMeshes.size() != geoMeshesSize || + // geometry.meshes.size() != _meshStates.size()) { + geoMeshesSize > _meshStates.size()) { qDebug() << "WARNING!!!! Mesh Sizes don't match! We will not segregate mesh groups yet."; return; } @@ -1147,15 +1161,30 @@ void Model::segregateMeshGroups() { _renderItemsSet.clear(); + Transform transform; + transform.setTranslation(_translation); + transform.setRotation(_rotation); + + Transform offset; + offset.setScale(_scale); + offset.postTranslate(_offset); + // Run through all of the meshes, and place them into their segregated, but unsorted buckets int shapeID = 0; for (int i = 0; i < (int)networkMeshes.size(); i++) { const FBXMesh& mesh = geometry.meshes.at(i); + const NetworkMesh& networkMesh = *(networkMeshes.at(i).get()); // Create the render payloads int totalParts = mesh.parts.size(); for (int partIndex = 0; partIndex < totalParts; partIndex++) { - _renderItemsSet << std::make_shared(this, i, partIndex, shapeID, _translation, _rotation); + if (showingCollisionHull) { + _renderItemsSet << std::make_shared(networkMesh._mesh, partIndex, ModelRender::getCollisionHullMaterial(), transform, offset); + + } else { + _renderItemsSet << std::make_shared(this, i, partIndex, shapeID, transform, offset); + } + shapeID++; } } @@ -1168,15 +1197,22 @@ bool Model::initWhenReady(render::ScenePointer scene) { render::PendingChanges pendingChanges; + Transform transform; + transform.setTranslation(_translation); + transform.setRotation(_rotation); + + Transform offset; + offset.setScale(_scale); + offset.postTranslate(_offset); + foreach (auto renderItem, _renderItemsSet) { auto item = scene->allocateID(); - auto renderData = MeshPartPayload::Pointer(renderItem); - auto renderPayload = std::make_shared(renderData); + auto renderPayload = std::make_shared(renderItem); _renderItems.insert(item, renderPayload); pendingChanges.resetItem(item, renderPayload); - pendingChanges.updateItem(item, [&](MeshPartPayload& data) { - data.updateModelLocation(_translation, _rotation); - data.model->_needsUpdateClusterMatrices = true; + pendingChanges.updateItem(item, [transform,offset](MeshPartPayload& data) { + data.updateTransform(transform, offset); + data.notifyLocationChanged(); }); } scene->enqueuePendingChanges(pendingChanges); diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index d77cf830bd..08abb74316 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -366,7 +366,7 @@ private: bool _needsUpdateClusterMatrices = true; bool _showCollisionHull = false; - friend class MeshPartPayload; + friend class ModelMeshPartPayload; protected: RigPointer _rig; }; diff --git a/libraries/render-utils/src/ModelRender.cpp b/libraries/render-utils/src/ModelRender.cpp index 73f3d715b6..10c9d738d2 100644 --- a/libraries/render-utils/src/ModelRender.cpp +++ b/libraries/render-utils/src/ModelRender.cpp @@ -280,3 +280,16 @@ void ModelRender::pickPrograms(gpu::Batch& batch, RenderArgs::RenderMode mode, b DependencyManager::get()->getNormalFittingTexture()); } } + +model::MaterialPointer ModelRender::_collisionHullMaterial; + +model::MaterialPointer ModelRender::getCollisionHullMaterial() { + if (!_collisionHullMaterial) { + _collisionHullMaterial = std::make_shared(); + _collisionHullMaterial->setDiffuse(glm::vec3(1.0f, 0.5f, 0.0f)); + _collisionHullMaterial->setMetallic(0.02f); + _collisionHullMaterial->setGloss(1.0f); + } + return _collisionHullMaterial; +} + diff --git a/libraries/render-utils/src/ModelRender.h b/libraries/render-utils/src/ModelRender.h index 1528dcfc87..39fe05378d 100644 --- a/libraries/render-utils/src/ModelRender.h +++ b/libraries/render-utils/src/ModelRender.h @@ -149,6 +149,11 @@ public: static const RenderPipelineLib& getRenderPipelineLib(); + // Collision hull Material + static model::MaterialPointer _collisionHullMaterial; + + static model::MaterialPointer getCollisionHullMaterial(); + }; #endif // hifi_ModelRender_h \ No newline at end of file diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 3306659c80..ded3db11e9 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -82,7 +82,7 @@ void avatarDataFromScriptValue(const QScriptValue &object, AvatarData* &out) { } Q_DECLARE_METATYPE(controller::InputController*) -static int inputControllerPointerId = qRegisterMetaType(); +//static int inputControllerPointerId = qRegisterMetaType(); QScriptValue inputControllerToScriptValue(QScriptEngine *engine, controller::InputController* const &in) { return engine->newQObject(in); @@ -183,7 +183,7 @@ void ScriptEngine::runInThread() { QSet ScriptEngine::_allKnownScriptEngines; QMutex ScriptEngine::_allScriptsMutex; -bool ScriptEngine::_stoppingAllScripts = false; +std::atomic ScriptEngine::_stoppingAllScripts { false }; void ScriptEngine::stopAllScripts(QObject* application) { _allScriptsMutex.lock(); @@ -752,7 +752,7 @@ void ScriptEngine::run() { } if (_wantSignals) { - emit finished(_fileNameString); + emit finished(_fileNameString, this); } _isRunning = false; @@ -775,6 +775,10 @@ void ScriptEngine::stopAllTimers() { void ScriptEngine::stop() { if (!_isFinished) { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "stop"); + return; + } _isFinished = true; if (_wantSignals) { emit runningStateChanged(); diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index 90b99d46fd..67a206d673 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -153,7 +153,7 @@ signals: void errorLoadingScript(const QString& scriptFilename); void update(float deltaTime); void scriptEnding(); - void finished(const QString& fileNameString); + void finished(const QString& fileNameString, ScriptEngine* engine); void cleanupMenuItem(const QString& menuItemString); void printedMessage(const QString& message); void errorMessage(const QString& message); @@ -166,8 +166,8 @@ signals: protected: QString _scriptContents; QString _parentURL; - bool _isFinished { false }; - bool _isRunning { false }; + std::atomic _isFinished { false }; + std::atomic _isRunning { false }; int _evaluatesPending { 0 }; bool _isInitialized { false }; QHash _timerFunctionMap; @@ -193,7 +193,7 @@ protected: Quat _quatLibrary; Vec3 _vec3Library; ScriptUUID _uuidLibrary; - bool _isUserLoaded { false }; + std::atomic _isUserLoaded { false }; bool _isReloading { false }; ArrayBufferClass* _arrayBufferClass; @@ -206,7 +206,7 @@ protected: static QSet _allKnownScriptEngines; static QMutex _allScriptsMutex; - static bool _stoppingAllScripts; + static std::atomic _stoppingAllScripts; }; #endif // hifi_ScriptEngine_h \ No newline at end of file diff --git a/libraries/shared/CMakeLists.txt b/libraries/shared/CMakeLists.txt index 2691c51128..489819747f 100644 --- a/libraries/shared/CMakeLists.txt +++ b/libraries/shared/CMakeLists.txt @@ -4,3 +4,4 @@ set(TARGET_NAME shared) setup_hifi_library(Gui Network Script Widgets) target_zlib() +target_nsight() diff --git a/libraries/shared/src/AABox.cpp b/libraries/shared/src/AABox.cpp index b514e171eb..d16bdafcec 100644 --- a/libraries/shared/src/AABox.cpp +++ b/libraries/shared/src/AABox.cpp @@ -12,6 +12,7 @@ #include "AABox.h" #include "AACube.h" +#include "Transform.h" #include "Extents.h" #include "GeometryUtil.h" #include "NumericalConstants.h" @@ -42,11 +43,6 @@ glm::vec3 AABox::calcCenter() const { return center; } -void AABox::scale(float scale) { - _corner = _corner * scale; - _scale = _scale * scale; -} - glm::vec3 AABox::getVertex(BoxVertex vertex) const { switch (vertex) { case BOTTOM_LEFT_NEAR: @@ -486,3 +482,59 @@ AABox& AABox::operator += (const AABox& box) { } return (*this); } + +void AABox::scale(const glm::vec3& scale) { + _corner *= scale; + _scale *= scale; +} + + +void AABox::rotate(const glm::quat& rotation) { + auto minimum = _corner; + auto maximum = _corner + _scale; + + glm::vec3 bottomLeftNear(minimum.x, minimum.y, minimum.z); + glm::vec3 bottomRightNear(maximum.x, minimum.y, minimum.z); + glm::vec3 bottomLeftFar(minimum.x, minimum.y, maximum.z); + glm::vec3 bottomRightFar(maximum.x, minimum.y, maximum.z); + glm::vec3 topLeftNear(minimum.x, maximum.y, minimum.z); + glm::vec3 topRightNear(maximum.x, maximum.y, minimum.z); + glm::vec3 topLeftFar(minimum.x, maximum.y, maximum.z); + glm::vec3 topRightFar(maximum.x, maximum.y, maximum.z); + + glm::vec3 bottomLeftNearRotated = rotation * bottomLeftNear; + glm::vec3 bottomRightNearRotated = rotation * bottomRightNear; + glm::vec3 bottomLeftFarRotated = rotation * bottomLeftFar; + glm::vec3 bottomRightFarRotated = rotation * bottomRightFar; + glm::vec3 topLeftNearRotated = rotation * topLeftNear; + glm::vec3 topRightNearRotated = rotation * topRightNear; + glm::vec3 topLeftFarRotated = rotation * topLeftFar; + glm::vec3 topRightFarRotated = rotation * topRightFar; + + minimum = glm::min(bottomLeftNearRotated, + glm::min(bottomRightNearRotated, + glm::min(bottomLeftFarRotated, + glm::min(bottomRightFarRotated, + glm::min(topLeftNearRotated, + glm::min(topRightNearRotated, + glm::min(topLeftFarRotated, + topRightFarRotated))))))); + + maximum = glm::max(bottomLeftNearRotated, + glm::max(bottomRightNearRotated, + glm::max(bottomLeftFarRotated, + glm::max(bottomRightFarRotated, + glm::max(topLeftNearRotated, + glm::max(topRightNearRotated, + glm::max(topLeftFarRotated, + topRightFarRotated))))))); + + _corner = minimum; + _scale = maximum - minimum; +} + +void AABox::transform(const Transform& transform) { + scale(transform.getScale()); + rotate(transform.getRotation()); + translate(transform.getTranslation()); +} \ No newline at end of file diff --git a/libraries/shared/src/AABox.h b/libraries/shared/src/AABox.h index 1f5923dbd8..24791445b3 100644 --- a/libraries/shared/src/AABox.h +++ b/libraries/shared/src/AABox.h @@ -24,6 +24,7 @@ class AACube; class Extents; +class Transform; class AABox { @@ -34,13 +35,13 @@ public: AABox(const glm::vec3& corner, const glm::vec3& dimensions); AABox(); ~AABox() {}; - + void setBox(const glm::vec3& corner, const glm::vec3& scale); void setBox(const glm::vec3& corner, float scale); glm::vec3 getVertexP(const glm::vec3& normal) const; glm::vec3 getVertexN(const glm::vec3& normal) const; - void scale(float scale); + const glm::vec3& getCorner() const { return _corner; } const glm::vec3& getScale() const { return _scale; } const glm::vec3& getDimensions() const { return _scale; } @@ -80,6 +81,20 @@ public: AABox& operator += (const glm::vec3& point); AABox& operator += (const AABox& box); + // Translate the AABox just moving the corner + void translate(const glm::vec3& translation) { _corner += translation; } + + // Rotate the AABox around its frame origin + // meaning rotating the corners of the AABox around the point {0,0,0} and reevaluating the min max + void rotate(const glm::quat& rotation); + + /// Scale the AABox + void scale(float scale); + void scale(const glm::vec3& scale); + + // Transform the extents with transform + void transform(const Transform& transform); + bool isInvalid() const { return _corner == glm::vec3(std::numeric_limits::infinity()); } private: diff --git a/libraries/shared/src/DebugDraw.cpp b/libraries/shared/src/DebugDraw.cpp index 3060e151c8..557889cab0 100644 --- a/libraries/shared/src/DebugDraw.cpp +++ b/libraries/shared/src/DebugDraw.cpp @@ -8,10 +8,11 @@ // #include "DebugDraw.h" +#include "SharedUtil.h" DebugDraw& DebugDraw::getInstance() { - static DebugDraw instance; - return instance; + static DebugDraw* instance = globalInstance("com.highfidelity.DebugDraw"); + return *instance; } DebugDraw::DebugDraw() { diff --git a/libraries/shared/src/GLMHelpers.cpp b/libraries/shared/src/GLMHelpers.cpp index 257ef2f00e..101412bbf7 100644 --- a/libraries/shared/src/GLMHelpers.cpp +++ b/libraries/shared/src/GLMHelpers.cpp @@ -360,6 +360,10 @@ QSize fromGlm(const glm::ivec2 & v) { return QSize(v.x, v.y); } +vec4 toGlm(const xColor& color, float alpha) { + return vec4((float)color.red / 255.0f, (float)color.green / 255.0f, (float)color.blue / 255.0f, alpha); +} + QRectF glmToRect(const glm::vec2 & pos, const glm::vec2 & size) { QRectF result(pos.x, pos.y, size.x, size.y); return result; diff --git a/libraries/shared/src/GLMHelpers.h b/libraries/shared/src/GLMHelpers.h index 7daf1b7e43..25ded54e93 100644 --- a/libraries/shared/src/GLMHelpers.h +++ b/libraries/shared/src/GLMHelpers.h @@ -156,6 +156,7 @@ vec2 toGlm(const QPointF& pt); vec3 toGlm(const xColor& color); vec4 toGlm(const QColor& color); ivec4 toGlm(const QRect& rect); +vec4 toGlm(const xColor& color, float alpha); QSize fromGlm(const glm::ivec2 & v); QMatrix4x4 fromGlm(const glm::mat4 & m); diff --git a/libraries/shared/src/PIDController.cpp b/libraries/shared/src/PIDController.cpp index a853553b89..78aa31e4c7 100644 --- a/libraries/shared/src/PIDController.cpp +++ b/libraries/shared/src/PIDController.cpp @@ -65,7 +65,7 @@ void PIDController::updateHistory(float measuredValue, float dt, float error, fl } } void PIDController::reportHistory() { - qCDebug(shared) << _label << "measured dt FIXME || error accumulated changed || p i d controlled"; + qCDebug(shared) << _label << "measured dt || error accumulated changed || p i d controlled"; for (int i = 0; i < _history.size(); i++) { Row& row = _history[i]; qCDebug(shared) << row.measured << row.dt << diff --git a/libraries/shared/src/SpatiallyNestable.cpp b/libraries/shared/src/SpatiallyNestable.cpp index bde788f6ff..98d127cc1e 100644 --- a/libraries/shared/src/SpatiallyNestable.cpp +++ b/libraries/shared/src/SpatiallyNestable.cpp @@ -15,7 +15,7 @@ #include "SpatiallyNestable.h" -SpatiallyNestable::SpatiallyNestable(NestableTypes::NestableType nestableType, QUuid id) : +SpatiallyNestable::SpatiallyNestable(NestableType nestableType, QUuid id) : _nestableType(nestableType), _id(id), _transform() { diff --git a/libraries/shared/src/SpatiallyNestable.h b/libraries/shared/src/SpatiallyNestable.h index e656957912..7a43e2a563 100644 --- a/libraries/shared/src/SpatiallyNestable.h +++ b/libraries/shared/src/SpatiallyNestable.h @@ -25,17 +25,14 @@ using SpatiallyNestableWeakConstPointer = std::weak_ptr using SpatiallyNestablePointer = std::shared_ptr; using SpatiallyNestableConstPointer = std::shared_ptr; -class NestableTypes { -public: - using NestableType = enum NestableType_t { - Entity, - Avatar - }; +enum class NestableType { + Entity, + Avatar }; class SpatiallyNestable : public std::enable_shared_from_this { public: - SpatiallyNestable(NestableTypes::NestableType nestableType, QUuid id); + SpatiallyNestable(NestableType nestableType, QUuid id); virtual ~SpatiallyNestable() { } virtual const QUuid& getID() const { return _id; } @@ -88,17 +85,17 @@ public: virtual void setLocalScale(const glm::vec3& scale); QList getChildren() const; - NestableTypes::NestableType getNestableType() const { return _nestableType; } + NestableType getNestableType() const { return _nestableType; } // this object's frame virtual const Transform getAbsoluteJointTransformInObjectFrame(int jointIndex) const; virtual glm::quat getAbsoluteJointRotationInObjectFrame(int index) const { assert(false); return glm::quat(); } virtual glm::vec3 getAbsoluteJointTranslationInObjectFrame(int index) const { assert(false); return glm::vec3(); } - + SpatiallyNestablePointer getThisPointer() const; protected: - NestableTypes::NestableType _nestableType; // EntityItem or an AvatarData + const NestableType _nestableType; // EntityItem or an AvatarData QUuid _id; QUuid _parentID; // what is this thing's transform relative to? quint16 _parentJointIndex { 0 }; // which joint of the parent is this relative to? diff --git a/libraries/shared/src/shared/NsightHelpers.cpp b/libraries/shared/src/shared/NsightHelpers.cpp new file mode 100644 index 0000000000..e48e228588 --- /dev/null +++ b/libraries/shared/src/shared/NsightHelpers.cpp @@ -0,0 +1,22 @@ +// +// Created by Bradley Austin Davis on 2015/12/10 +// 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 "NsightHelpers.h" + +#if defined(NSIGHT_FOUND) +#include "nvToolsExt.h" + +ProfileRange::ProfileRange(const char *name) { + nvtxRangePush(name); +} + +ProfileRange::~ProfileRange() { + nvtxRangePop(); +} + +#endif diff --git a/libraries/shared/src/shared/NsightHelpers.h b/libraries/shared/src/shared/NsightHelpers.h new file mode 100644 index 0000000000..3acdf14411 --- /dev/null +++ b/libraries/shared/src/shared/NsightHelpers.h @@ -0,0 +1,24 @@ +// +// Created by Bradley Austin Davis on 2015/12/10 +// 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 +// +#pragma once +#ifndef hifi_gl_NsightHelpers_h +#define hifi_gl_NsightHelpers_h + +#if defined(NSIGHT_FOUND) + class ProfileRange { + public: + ProfileRange(const char *name); + ~ProfileRange(); + }; +#define PROFILE_RANGE(name) ProfileRange profileRangeThis(name); +#else +#define PROFILE_RANGE(name) +#endif + + +#endif \ No newline at end of file diff --git a/plugins/hifiSdl2/CMakeLists.txt b/plugins/hifiSdl2/CMakeLists.txt new file mode 100644 index 0000000000..2f106aedce --- /dev/null +++ b/plugins/hifiSdl2/CMakeLists.txt @@ -0,0 +1,12 @@ +# +# Created by Bradley Austin Davis on 2015/11/18 +# 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 +# + +set(TARGET_NAME hifiSdl2) +setup_hifi_plugin(Script Qml Widgets) +link_hifi_libraries(shared controllers plugins input-plugins script-engine) +target_sdl2() diff --git a/libraries/input-plugins/src/input-plugins/Joystick.cpp b/plugins/hifiSdl2/src/Joystick.cpp similarity index 100% rename from libraries/input-plugins/src/input-plugins/Joystick.cpp rename to plugins/hifiSdl2/src/Joystick.cpp diff --git a/libraries/input-plugins/src/input-plugins/Joystick.h b/plugins/hifiSdl2/src/Joystick.h similarity index 100% rename from libraries/input-plugins/src/input-plugins/Joystick.h rename to plugins/hifiSdl2/src/Joystick.h diff --git a/libraries/input-plugins/src/input-plugins/SDL2Manager.cpp b/plugins/hifiSdl2/src/SDL2Manager.cpp similarity index 99% rename from libraries/input-plugins/src/input-plugins/SDL2Manager.cpp rename to plugins/hifiSdl2/src/SDL2Manager.cpp index c6f5491a5a..7ca49bbd75 100644 --- a/libraries/input-plugins/src/input-plugins/SDL2Manager.cpp +++ b/plugins/hifiSdl2/src/SDL2Manager.cpp @@ -11,8 +11,6 @@ #include -#include -#include #include #include @@ -163,6 +161,7 @@ void SDL2Manager::pluginUpdate(float deltaTime, bool jointsCaptured) { joystick->handleButtonEvent(event.cbutton); } +#if 0 if (event.cbutton.button == SDL_CONTROLLER_BUTTON_BACK) { // this will either start or stop a global back event QEvent::Type backType = (event.type == SDL_CONTROLLERBUTTONDOWN) @@ -172,6 +171,7 @@ void SDL2Manager::pluginUpdate(float deltaTime, bool jointsCaptured) { qApp->sendEvent(qApp, &backEvent); } +#endif } else if (event.type == SDL_CONTROLLERDEVICEADDED) { SDL_GameController* controller = SDL_GameControllerOpen(event.cdevice.which); diff --git a/libraries/input-plugins/src/input-plugins/SDL2Manager.h b/plugins/hifiSdl2/src/SDL2Manager.h similarity index 98% rename from libraries/input-plugins/src/input-plugins/SDL2Manager.h rename to plugins/hifiSdl2/src/SDL2Manager.h index d6b6de24b3..61e2d37fd5 100644 --- a/libraries/input-plugins/src/input-plugins/SDL2Manager.h +++ b/plugins/hifiSdl2/src/SDL2Manager.h @@ -17,8 +17,7 @@ #endif #include - -#include "InputPlugin.h" +#include #include "Joystick.h" class SDL2Manager : public InputPlugin { diff --git a/plugins/hifiSdl2/src/SDL2Provider.cpp b/plugins/hifiSdl2/src/SDL2Provider.cpp new file mode 100644 index 0000000000..1c387a9886 --- /dev/null +++ b/plugins/hifiSdl2/src/SDL2Provider.cpp @@ -0,0 +1,44 @@ +// +// Created by Bradley Austin Davis on 2015/10/25 +// 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 +#include +#include + +#include +#include + +#include "SDL2Manager.h" + +class SDL2Provider : public QObject, public InputProvider { + Q_OBJECT + Q_PLUGIN_METADATA(IID InputProvider_iid FILE "plugin.json") + Q_INTERFACES(InputProvider) + +public: + SDL2Provider(QObject* parent = nullptr) : QObject(parent) {} + virtual ~SDL2Provider() {} + + virtual InputPluginList getInputPlugins() override { + static std::once_flag once; + std::call_once(once, [&] { + InputPluginPointer plugin(new SDL2Manager()); + if (plugin->isSupported()) { + _inputPlugins.push_back(plugin); + } + }); + return _inputPlugins; + } + +private: + InputPluginList _inputPlugins; +}; + +#include "SDL2Provider.moc" diff --git a/plugins/hifiSdl2/src/plugin.json b/plugins/hifiSdl2/src/plugin.json new file mode 100644 index 0000000000..0967ef424b --- /dev/null +++ b/plugins/hifiSdl2/src/plugin.json @@ -0,0 +1 @@ +{} diff --git a/plugins/hifiSixense/CMakeLists.txt b/plugins/hifiSixense/CMakeLists.txt new file mode 100644 index 0000000000..c790f6f14d --- /dev/null +++ b/plugins/hifiSixense/CMakeLists.txt @@ -0,0 +1,12 @@ +# +# Created by Bradley Austin Davis on 2015/11/18 +# 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 +# + +set(TARGET_NAME hifiSixense) +setup_hifi_plugin(Script Qml Widgets) +link_hifi_libraries(shared controllers plugins input-plugins) +target_sixense() diff --git a/libraries/input-plugins/src/input-plugins/SixenseManager.cpp b/plugins/hifiSixense/src/SixenseManager.cpp similarity index 85% rename from libraries/input-plugins/src/input-plugins/SixenseManager.cpp rename to plugins/hifiSixense/src/SixenseManager.cpp index 5dd0248224..93329d768e 100644 --- a/libraries/input-plugins/src/input-plugins/SixenseManager.cpp +++ b/plugins/hifiSixense/src/SixenseManager.cpp @@ -21,14 +21,17 @@ #include #include +#include #include #include #include #include #include -#include -#include "InputPluginsLogging.h" +#include + +Q_DECLARE_LOGGING_CATEGORY(inputplugins) +Q_LOGGING_CATEGORY(inputplugins, "hifi.inputplugins") static const unsigned int BUTTON_0 = 1U << 0; // the skinny button between 1 and 2 static const unsigned int BUTTON_1 = 1U << 5; @@ -48,6 +51,8 @@ const QString MENU_PARENT = "Avatar"; const QString MENU_NAME = "Sixense"; const QString MENU_PATH = MENU_PARENT + ">" + MENU_NAME; const QString TOGGLE_SMOOTH = "Smooth Sixense Movement"; +const QString SHOW_DEBUG_RAW = "Debug Draw Raw Data"; +const QString SHOW_DEBUG_CALIBRATED = "Debug Draw Calibrated Data"; bool SixenseManager::isSupported() const { #ifdef HAVE_SIXENSE @@ -72,6 +77,14 @@ void SixenseManager::activate() { [this] (bool clicked) { setSixenseFilter(clicked); }, true, true); + _container->addMenuItem(PluginType::INPUT_PLUGIN, MENU_PATH, SHOW_DEBUG_RAW, + [this] (bool clicked) { _inputDevice->setDebugDrawRaw(clicked); }, + true, false); + + _container->addMenuItem(PluginType::INPUT_PLUGIN, MENU_PATH, SHOW_DEBUG_CALIBRATED, + [this] (bool clicked) { _inputDevice->setDebugDrawCalibrated(clicked); }, + true, false); + auto userInputMapper = DependencyManager::get(); userInputMapper->registerDevice(_inputDevice); @@ -149,6 +162,9 @@ void SixenseManager::InputDevice::update(float deltaTime, bool jointsCaptured) { // we only support two controllers SixenseControllerData controllers[2]; + // store the raw controller data for debug rendering + controller::Pose rawPoses[2]; + int numActiveControllers = 0; for (int i = 0; i < maxControllers && numActiveControllers < 2; i++) { if (!sixenseIsControllerEnabled(i)) { @@ -175,7 +191,7 @@ void SixenseManager::InputDevice::update(float deltaTime, bool jointsCaptured) { // Rotation of Palm glm::quat rotation(data->rot_quat[3], data->rot_quat[0], data->rot_quat[1], data->rot_quat[2]); handlePoseEvent(deltaTime, position, rotation, left); - + rawPoses[i] = controller::Pose(position, rotation, glm::vec3(0), glm::quat()); } else { _poseStateMap.clear(); _collectedSamples.clear(); @@ -197,9 +213,54 @@ void SixenseManager::InputDevice::update(float deltaTime, bool jointsCaptured) { _axisStateMap[axisState.first] = 0.0f; } } + + if (_debugDrawCalibrated) { + auto poseIter = _poseStateMap.find(controller::StandardPoseChannel::LEFT_HAND); + if (poseIter != _poseStateMap.end() && poseIter->second.isValid()) { + DebugDraw::getInstance().addMyAvatarMarker("SIXENSE_CALIBRATED_LEFT", poseIter->second.rotation, poseIter->second.translation, glm::vec4(1)); + } else { + DebugDraw::getInstance().removeMyAvatarMarker("SIXENSE_CALIBRATED_LEFT"); + } + poseIter = _poseStateMap.find(controller::StandardPoseChannel::RIGHT_HAND); + if (poseIter != _poseStateMap.end() && poseIter->second.isValid()) { + DebugDraw::getInstance().addMyAvatarMarker("SIXENSE_CALIBRATED_RIGHT", poseIter->second.rotation, poseIter->second.translation, glm::vec4(1)); + } else { + DebugDraw::getInstance().removeMyAvatarMarker("SIXENSE_CALIBRATED_RIGHT"); + } + } + + if (_debugDrawRaw) { + if (rawPoses[0].isValid()) { + DebugDraw::getInstance().addMyAvatarMarker("SIXENSE_RAW_LEFT", rawPoses[0].rotation, rawPoses[0].translation, glm::vec4(1)); + } else { + DebugDraw::getInstance().removeMyAvatarMarker("SIXENSE_RAW_LEFT"); + } + if (rawPoses[1].isValid()) { + DebugDraw::getInstance().addMyAvatarMarker("SIXENSE_RAW_RIGHT", rawPoses[1].rotation, rawPoses[1].translation, glm::vec4(1)); + } else { + DebugDraw::getInstance().removeMyAvatarMarker("SIXENSE_RAW_RIGHT"); + } + } + #endif // HAVE_SIXENSE } +void SixenseManager::InputDevice::setDebugDrawRaw(bool flag) { + _debugDrawRaw = flag; + if (!flag) { + DebugDraw::getInstance().removeMyAvatarMarker("SIXENSE_RAW_LEFT"); + DebugDraw::getInstance().removeMyAvatarMarker("SIXENSE_RAW_RIGHT"); + } +} + +void SixenseManager::InputDevice::setDebugDrawCalibrated(bool flag) { + _debugDrawCalibrated = flag; + if (!flag) { + DebugDraw::getInstance().removeMyAvatarMarker("SIXENSE_CALIBRATED_LEFT"); + DebugDraw::getInstance().removeMyAvatarMarker("SIXENSE_CALIBRATED_RIGHT"); + } +} + #ifdef HAVE_SIXENSE // the calibration sequence is: diff --git a/libraries/input-plugins/src/input-plugins/SixenseManager.h b/plugins/hifiSixense/src/SixenseManager.h similarity index 94% rename from libraries/input-plugins/src/input-plugins/SixenseManager.h rename to plugins/hifiSixense/src/SixenseManager.h index 348a7a4590..5cd4dc2451 100644 --- a/libraries/input-plugins/src/input-plugins/SixenseManager.h +++ b/plugins/hifiSixense/src/SixenseManager.h @@ -17,7 +17,7 @@ #include #include -#include "InputPlugin.h" +#include struct _sixenseControllerData; using SixenseControllerData = _sixenseControllerData; @@ -60,6 +60,8 @@ private: class InputDevice : public controller::InputDevice { public: InputDevice() : controller::InputDevice("Hydra") {} + void setDebugDrawRaw(bool flag); + void setDebugDrawCalibrated(bool flag); private: // Device functions virtual controller::Input::NamedVector getAvailableInputs() const override; @@ -82,6 +84,8 @@ private: float _lastDistance; bool _requestReset { false }; + bool _debugDrawRaw { false }; + bool _debugDrawCalibrated { false }; // these are measured values used to compute the calibration results quint64 _lockExpiry; glm::vec3 _averageLeft; diff --git a/plugins/hifiSixense/src/SixenseProvider.cpp b/plugins/hifiSixense/src/SixenseProvider.cpp new file mode 100644 index 0000000000..2958e47848 --- /dev/null +++ b/plugins/hifiSixense/src/SixenseProvider.cpp @@ -0,0 +1,45 @@ +// +// Created by Bradley Austin Davis on 2015/10/25 +// 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 +#include +#include + +#include +#include + +#include "SixenseManager.h" + +class SixenseProvider : public QObject, public InputProvider +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID InputProvider_iid FILE "plugin.json") + Q_INTERFACES(InputProvider) + +public: + SixenseProvider(QObject* parent = nullptr) : QObject(parent) {} + virtual ~SixenseProvider() {} + + virtual InputPluginList getInputPlugins() override { + static std::once_flag once; + std::call_once(once, [&] { + InputPluginPointer plugin(new SixenseManager()); + if (plugin->isSupported()) { + _inputPlugins.push_back(plugin); + } + }); + return _inputPlugins; + } + +private: + InputPluginList _inputPlugins; +}; + +#include "SixenseProvider.moc" diff --git a/libraries/input-plugins/src/input-plugins/SixenseSupportOSX.cpp b/plugins/hifiSixense/src/SixenseSupportOSX.cpp similarity index 99% rename from libraries/input-plugins/src/input-plugins/SixenseSupportOSX.cpp rename to plugins/hifiSixense/src/SixenseSupportOSX.cpp index f6cec5d67f..85e0f3fe48 100644 --- a/libraries/input-plugins/src/input-plugins/SixenseSupportOSX.cpp +++ b/plugins/hifiSixense/src/SixenseSupportOSX.cpp @@ -17,8 +17,7 @@ #include #include - -#include "InputPluginsLogging.h" +#include #ifndef SIXENSE_LIB_FILENAME #define SIXENSE_LIB_FILENAME QCoreApplication::applicationDirPath() + "/../Frameworks/libsixense_x64" diff --git a/plugins/hifiSixense/src/plugin.json b/plugins/hifiSixense/src/plugin.json new file mode 100644 index 0000000000..0967ef424b --- /dev/null +++ b/plugins/hifiSixense/src/plugin.json @@ -0,0 +1 @@ +{} diff --git a/plugins/oculus/src/OculusBaseDisplayPlugin.cpp b/plugins/oculus/src/OculusBaseDisplayPlugin.cpp index 7c057c7152..d6811c3529 100644 --- a/plugins/oculus/src/OculusBaseDisplayPlugin.cpp +++ b/plugins/oculus/src/OculusBaseDisplayPlugin.cpp @@ -20,9 +20,7 @@ glm::mat4 OculusBaseDisplayPlugin::getProjection(Eye eye, const glm::mat4& baseP } void OculusBaseDisplayPlugin::resetSensors() { -#if (OVR_MAJOR_VERSION >= 6) - ovr_RecenterPose(_hmd); -#endif + ovr_RecenterPose(_session); } glm::mat4 OculusBaseDisplayPlugin::getEyeToHeadTransform(Eye eye) const { @@ -30,27 +28,39 @@ glm::mat4 OculusBaseDisplayPlugin::getEyeToHeadTransform(Eye eye) const { } glm::mat4 OculusBaseDisplayPlugin::getHeadPose(uint32_t frameIndex) const { -#if (OVR_MAJOR_VERSION >= 6) - auto frameTiming = ovr_GetFrameTiming(_hmd, frameIndex); - auto trackingState = ovr_GetTrackingState(_hmd, frameTiming.DisplayMidpointSeconds); + static uint32_t lastFrameSeen = 0; + auto displayTime = ovr_GetPredictedDisplayTime(_session, frameIndex); + auto trackingState = ovr_GetTrackingState(_session, displayTime, frameIndex > lastFrameSeen); + if (frameIndex > lastFrameSeen) { + lastFrameSeen = frameIndex; + } return toGlm(trackingState.HeadPose.ThePose); -#endif } bool OculusBaseDisplayPlugin::isSupported() const { -#if (OVR_MAJOR_VERSION >= 6) if (!OVR_SUCCESS(ovr_Initialize(nullptr))) { return false; } - bool result = false; - if (ovrHmd_Detect() > 0) { - result = true; + + ovrSession session { nullptr }; + ovrGraphicsLuid luid; + auto result = ovr_Create(&session, &luid); + if (!OVR_SUCCESS(result)) { + ovrErrorInfo error; + ovr_GetLastErrorInfo(&error); + ovr_Shutdown(); + return false; } + + auto hmdDesc = ovr_GetHmdDesc(session); + if (hmdDesc.Type == ovrHmd_None) { + ovr_Destroy(session); + ovr_Shutdown(); + return false; + } + ovr_Shutdown(); - return result; -#else - return false; -#endif + return true; } // DLL based display plugins MUST initialize GLEW inside the DLL code. @@ -69,28 +79,22 @@ void OculusBaseDisplayPlugin::deinit() { void OculusBaseDisplayPlugin::activate() { WindowOpenGLDisplayPlugin::activate(); -#if (OVR_MAJOR_VERSION >= 6) if (!OVR_SUCCESS(ovr_Initialize(nullptr))) { qFatal("Could not init OVR"); } -#if (OVR_MAJOR_VERSION == 6) - if (!OVR_SUCCESS(ovr_Create(0, &_hmd))) { -#elif (OVR_MAJOR_VERSION == 7) - if (!OVR_SUCCESS(ovr_Create(&_hmd, &_luid))) { -#endif - Q_ASSERT(false); + if (!OVR_SUCCESS(ovr_Create(&_session, &_luid))) { qFatal("Failed to acquire HMD"); } - _hmdDesc = ovr_GetHmdDesc(_hmd); + _hmdDesc = ovr_GetHmdDesc(_session); - _ipd = ovr_GetFloat(_hmd, OVR_KEY_IPD, _ipd); + _ipd = ovr_GetFloat(_session, OVR_KEY_IPD, _ipd); glm::uvec2 eyeSizes[2]; ovr_for_each_eye([&](ovrEyeType eye) { _eyeFovs[eye] = _hmdDesc.DefaultEyeFov[eye]; - ovrEyeRenderDesc& erd = _eyeRenderDescs[eye] = ovr_GetRenderDesc(_hmd, eye, _eyeFovs[eye]); + ovrEyeRenderDesc& erd = _eyeRenderDescs[eye] = ovr_GetRenderDesc(_session, eye, _eyeFovs[eye]); ovrMatrix4f ovrPerspectiveProjection = ovrMatrix4f_Projection(erd.Fov, DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP, ovrProjection_RightHanded); _eyeProjections[eye] = toGlm(ovrPerspectiveProjection); @@ -100,7 +104,7 @@ void OculusBaseDisplayPlugin::activate() { _compositeEyeProjections[eye] = toGlm(ovrPerspectiveProjection); _eyeOffsets[eye] = erd.HmdToEyeViewOffset; - eyeSizes[eye] = toGlm(ovr_GetFovTextureSize(_hmd, eye, erd.Fov, 1.0f)); + eyeSizes[eye] = toGlm(ovr_GetFovTextureSize(_session, eye, erd.Fov, 1.0f)); }); ovrFovPort combined = _eyeFovs[Left]; combined.LeftTan = std::max(_eyeFovs[Left].LeftTan, _eyeFovs[Right].LeftTan); @@ -115,34 +119,33 @@ void OculusBaseDisplayPlugin::activate() { eyeSizes[0].x + eyeSizes[1].x, std::max(eyeSizes[0].y, eyeSizes[1].y)); - if (!OVR_SUCCESS(ovr_ConfigureTracking(_hmd, + if (!OVR_SUCCESS(ovr_ConfigureTracking(_session, ovrTrackingCap_Orientation | ovrTrackingCap_Position | ovrTrackingCap_MagYawCorrection, 0))) { qFatal("Could not attach to sensor device"); } - // Parent class relies on our _hmd intialization, so it must come after that. + // Parent class relies on our _session intialization, so it must come after that. memset(&_sceneLayer, 0, sizeof(ovrLayerEyeFov)); _sceneLayer.Header.Type = ovrLayerType_EyeFov; _sceneLayer.Header.Flags = ovrLayerFlag_TextureOriginAtBottomLeft; ovr_for_each_eye([&](ovrEyeType eye) { ovrFovPort & fov = _sceneLayer.Fov[eye] = _eyeRenderDescs[eye].Fov; - ovrSizei & size = _sceneLayer.Viewport[eye].Size = ovr_GetFovTextureSize(_hmd, eye, fov, 1.0f); + ovrSizei & size = _sceneLayer.Viewport[eye].Size = ovr_GetFovTextureSize(_session, eye, fov, 1.0f); _sceneLayer.Viewport[eye].Pos = { eye == ovrEye_Left ? 0 : size.w, 0 }; }); - if (!OVR_SUCCESS(ovr_ConfigureTracking(_hmd, + if (!OVR_SUCCESS(ovr_ConfigureTracking(_session, ovrTrackingCap_Orientation | ovrTrackingCap_Position | ovrTrackingCap_MagYawCorrection, 0))) { qFatal("Could not attach to sensor device"); } -#endif } void OculusBaseDisplayPlugin::deactivate() { WindowOpenGLDisplayPlugin::deactivate(); #if (OVR_MAJOR_VERSION >= 6) - ovr_Destroy(_hmd); - _hmd = nullptr; + ovr_Destroy(_session); + _session = nullptr; ovr_Shutdown(); #endif } @@ -151,7 +154,7 @@ void OculusBaseDisplayPlugin::deactivate() { float OculusBaseDisplayPlugin::getIPD() const { float result = OVR_DEFAULT_IPD; #if (OVR_MAJOR_VERSION >= 6) - result = ovr_GetFloat(_hmd, OVR_KEY_IPD, result); + result = ovr_GetFloat(_session, OVR_KEY_IPD, result); #endif return result; } diff --git a/plugins/oculus/src/OculusBaseDisplayPlugin.h b/plugins/oculus/src/OculusBaseDisplayPlugin.h index 711be6aa5e..1a26c6958b 100644 --- a/plugins/oculus/src/OculusBaseDisplayPlugin.h +++ b/plugins/oculus/src/OculusBaseDisplayPlugin.h @@ -43,17 +43,13 @@ protected: mat4 _compositeEyeProjections[2]; uvec2 _desiredFramebufferSize; -#if (OVR_MAJOR_VERSION >= 6) - ovrHmd _hmd; + ovrSession _session; + ovrGraphicsLuid _luid; float _ipd{ OVR_DEFAULT_IPD }; ovrEyeRenderDesc _eyeRenderDescs[2]; ovrFovPort _eyeFovs[2]; ovrHmdDesc _hmdDesc; ovrLayerEyeFov _sceneLayer; -#endif -#if (OVR_MAJOR_VERSION == 7) - ovrGraphicsLuid _luid; -#endif }; #if (OVR_MAJOR_VERSION == 6) diff --git a/plugins/oculus/src/OculusDisplayPlugin.cpp b/plugins/oculus/src/OculusDisplayPlugin.cpp index 8846b8a6a6..c8fb0ba080 100644 --- a/plugins/oculus/src/OculusDisplayPlugin.cpp +++ b/plugins/oculus/src/OculusDisplayPlugin.cpp @@ -23,12 +23,16 @@ // ovr_CreateMirrorTextureGL, etc template struct RiftFramebufferWrapper : public FramebufferWrapper { - ovrHmd hmd; - RiftFramebufferWrapper(const ovrHmd & hmd) : hmd(hmd) { + ovrSession session; + RiftFramebufferWrapper(const ovrSession& session) : session(session) { color = 0; depth = 0; }; + ~RiftFramebufferWrapper() { + destroyColor(); + } + void Resize(const uvec2 & size) { glBindFramebuffer(GL_DRAW_FRAMEBUFFER, oglplus::GetName(fbo)); glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); @@ -39,6 +43,9 @@ struct RiftFramebufferWrapper : public FramebufferWrapper { } protected: + virtual void destroyColor() { + } + virtual void initDepth() override final { } }; @@ -53,12 +60,6 @@ struct SwapFramebufferWrapper : public RiftFramebufferWrapperCurrentIndex; @@ -66,13 +67,17 @@ struct SwapFramebufferWrapper : public RiftFramebufferWrapper { MirrorFramebufferWrapper(const ovrHmd & hmd) : RiftFramebufferWrapper(hmd) { } - virtual ~MirrorFramebufferWrapper() { +private: + virtual void destroyColor() override { if (color) { - ovr_DestroyMirrorTexture(hmd, (ovrTexture*)color); + ovr_DestroyMirrorTexture(session, (ovrTexture*)color); color = nullptr; } } -private: void initColor() override { - if (color) { - ovr_DestroyMirrorTexture(hmd, (ovrTexture*)color); - color = nullptr; - } - ovrResult result = ovr_CreateMirrorTextureGL(hmd, GL_RGBA, size.x, size.y, (ovrTexture**)&color); + destroyColor(); + ovrResult result = ovr_CreateMirrorTextureGL(session, GL_SRGB8_ALPHA8, size.x, size.y, (ovrTexture**)&color); Q_ASSERT(OVR_SUCCESS(result)); } @@ -154,8 +156,7 @@ void OculusDisplayPlugin::activate() { void OculusDisplayPlugin::customizeContext() { OculusBaseDisplayPlugin::customizeContext(); -#if (OVR_MAJOR_VERSION >= 6) - _sceneFbo = SwapFboPtr(new SwapFramebufferWrapper(_hmd)); + _sceneFbo = SwapFboPtr(new SwapFramebufferWrapper(_session)); _sceneFbo->Init(getRecommendedRenderSize()); // We're rendering both eyes to the same texture, so only one of the @@ -163,7 +164,7 @@ void OculusDisplayPlugin::customizeContext() { _sceneLayer.ColorTexture[0] = _sceneFbo->color; // not needed since the structure was zeroed on init, but explicit _sceneLayer.ColorTexture[1] = nullptr; -#endif + enableVsync(false); // Only enable mirroring if we know vsync is disabled _enablePreview = !isVsyncEnabled(); @@ -177,7 +178,6 @@ void OculusDisplayPlugin::uncustomizeContext() { } void OculusDisplayPlugin::internalPresent() { -#if (OVR_MAJOR_VERSION >= 6) if (!_currentSceneTexture) { return; } @@ -206,8 +206,10 @@ void OculusDisplayPlugin::internalPresent() { auto size = _sceneFbo->size; Context::Viewport(size.x, size.y); glBindTexture(GL_TEXTURE_2D, _currentSceneTexture); + //glEnable(GL_FRAMEBUFFER_SRGB); GLenum err = glGetError(); drawUnitQuad(); + //glDisable(GL_FRAMEBUFFER_SRGB); }); uint32_t frameIndex { 0 }; @@ -230,13 +232,12 @@ void OculusDisplayPlugin::internalPresent() { viewScaleDesc.HmdToEyeViewOffset[1] = _eyeOffsets[1]; ovrLayerHeader* layers = &_sceneLayer.Header; - ovrResult result = ovr_SubmitFrame(_hmd, 0, &viewScaleDesc, &layers, 1); + ovrResult result = ovr_SubmitFrame(_session, frameIndex, &viewScaleDesc, &layers, 1); if (!OVR_SUCCESS(result)) { qDebug() << result; } } _sceneFbo->Increment(); -#endif /* The swapbuffer call here is only required if we want to mirror the content to the screen. diff --git a/plugins/oculus/src/OculusDisplayPlugin.h b/plugins/oculus/src/OculusDisplayPlugin.h index 5509715b9f..03c9ba7511 100644 --- a/plugins/oculus/src/OculusDisplayPlugin.h +++ b/plugins/oculus/src/OculusDisplayPlugin.h @@ -12,12 +12,16 @@ struct SwapFramebufferWrapper; using SwapFboPtr = QSharedPointer; +const float TARGET_RATE_Oculus = 75.0f; + class OculusDisplayPlugin : public OculusBaseDisplayPlugin { public: virtual void activate() override; virtual const QString & getName() const override; virtual void setEyeRenderPose(uint32_t frameIndex, Eye eye, const glm::mat4& pose) override final; + virtual float getTargetFrameRate() override { return TARGET_RATE_Oculus; } + protected: virtual void internalPresent() override; virtual void customizeContext() override; diff --git a/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.h b/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.h index ccf1ffdb57..d3e68585df 100644 --- a/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.h +++ b/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.h @@ -13,6 +13,8 @@ #include +const float TARGET_RATE_OculusLegacy = 75.0f; + class OculusLegacyDisplayPlugin : public WindowOpenGLDisplayPlugin { public: OculusLegacyDisplayPlugin(); @@ -25,6 +27,8 @@ public: virtual bool eventFilter(QObject* receiver, QEvent* event) override; virtual int getHmdScreen() const override; + virtual float getTargetFrameRate() override { return TARGET_RATE_OculusLegacy; } + // Stereo specific methods virtual bool isHmd() const override { return true; } virtual glm::mat4 getProjection(Eye eye, const glm::mat4& baseProjection) const override; diff --git a/plugins/openvr/CMakeLists.txt b/plugins/openvr/CMakeLists.txt new file mode 100644 index 0000000000..c3c4771276 --- /dev/null +++ b/plugins/openvr/CMakeLists.txt @@ -0,0 +1,27 @@ +# +# Created by Bradley Austin Davis on 2015/11/18 +# 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 +# + +# OpenVR is disabled until a) it works with threaded present and +# b) it doesn't interfere with Oculus SDK 0.8 +if (FALSE) +#if (WIN32) + # we're using static GLEW, so define GLEW_STATIC + add_definitions(-DGLEW_STATIC) + set(TARGET_NAME openvr) + setup_hifi_plugin(OpenGL Script Qml Widgets) + link_hifi_libraries(shared gl networking controllers + plugins display-plugins input-plugins script-engine + render-utils model gpu render model-networking fbx) + + include_hifi_library_headers(octree) + + add_dependency_external_projects(OpenVR) + find_package(OpenVR REQUIRED) + target_include_directories(${TARGET_NAME} PRIVATE ${OPENVR_INCLUDE_DIRS}) + target_link_libraries(${TARGET_NAME} ${OPENVR_LIBRARIES}) +endif() diff --git a/libraries/display-plugins/src/display-plugins/openvr/OpenVrDisplayPlugin.cpp b/plugins/openvr/src/OpenVrDisplayPlugin.cpp similarity index 65% rename from libraries/display-plugins/src/display-plugins/openvr/OpenVrDisplayPlugin.cpp rename to plugins/openvr/src/OpenVrDisplayPlugin.cpp index 68a711a847..58f7536856 100644 --- a/libraries/display-plugins/src/display-plugins/openvr/OpenVrDisplayPlugin.cpp +++ b/plugins/openvr/src/OpenVrDisplayPlugin.cpp @@ -7,24 +7,23 @@ // #include "OpenVrDisplayPlugin.h" -#if defined(Q_OS_WIN) - #include #include +#include #include -#include #include #include +#include +#include + #include #include #include #include "OpenVrHelpers.h" -#include "GLMHelpers.h" -#include Q_DECLARE_LOGGING_CATEGORY(displayplugins) Q_LOGGING_CATEGORY(displayplugins, "hifi.displayplugins") @@ -41,12 +40,11 @@ vr::TrackedDevicePose_t _trackedDevicePose[vr::k_unMaxTrackedDeviceCount]; mat4 _trackedDevicePoseMat4[vr::k_unMaxTrackedDeviceCount]; static mat4 _sensorResetMat; static uvec2 _windowSize; -static ivec2 _windowPosition; static uvec2 _renderTargetSize; struct PerEyeData { - uvec2 _viewportOrigin; - uvec2 _viewportSize; + //uvec2 _viewportOrigin; + //uvec2 _viewportSize; mat4 _projectionMatrix; mat4 _eyeOffset; mat4 _pose; @@ -89,36 +87,17 @@ void OpenVrDisplayPlugin::activate() { } Q_ASSERT(_hmd); - _hmd->GetWindowBounds(&_windowPosition.x, &_windowPosition.y, &_windowSize.x, &_windowSize.y); _hmd->GetRecommendedRenderTargetSize(&_renderTargetSize.x, &_renderTargetSize.y); // Recommended render target size is per-eye, so double the X size for // left + right eyes _renderTargetSize.x *= 2; openvr_for_each_eye([&](vr::Hmd_Eye eye) { PerEyeData& eyeData = _eyesData[eye]; - _hmd->GetEyeOutputViewport(eye, - &eyeData._viewportOrigin.x, &eyeData._viewportOrigin.y, - &eyeData._viewportSize.x, &eyeData._viewportSize.y); eyeData._projectionMatrix = toGlm(_hmd->GetProjectionMatrix(eye, DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP, vr::API_OpenGL)); eyeData._eyeOffset = toGlm(_hmd->GetEyeToHeadTransform(eye)); }); - - - vr::HmdError eError = vr::HmdError_None; - _compositor = (vr::IVRCompositor*)vr::VR_GetGenericInterface(vr::IVRCompositor_Version, &eError); - Q_ASSERT(eError == vr::HmdError_None); + _compositor = vr::VRCompositor(); Q_ASSERT(_compositor); - - _compositor->SetGraphicsDevice(vr::Compositor_DeviceType_OpenGL, NULL); - - uint32_t unSize = _compositor->GetLastError(NULL, 0); - if (unSize > 1) { - char* buffer = new char[unSize]; - _compositor->GetLastError(buffer, unSize); - printf("Compositor - %s\n", buffer); - delete[] buffer; - } - Q_ASSERT(unSize <= 1); WindowOpenGLDisplayPlugin::activate(); } @@ -132,6 +111,16 @@ void OpenVrDisplayPlugin::deactivate() { WindowOpenGLDisplayPlugin::deactivate(); } +void OpenVrDisplayPlugin::customizeContext() { + static std::once_flag once; + std::call_once(once, []{ + glewExperimental = true; + GLenum err = glewInit(); + glGetError(); + }); + WindowOpenGLDisplayPlugin::customizeContext(); +} + uvec2 OpenVrDisplayPlugin::getRecommendedRenderSize() const { return _renderTargetSize; } @@ -153,33 +142,41 @@ glm::mat4 OpenVrDisplayPlugin::getEyeToHeadTransform(Eye eye) const { } glm::mat4 OpenVrDisplayPlugin::getHeadPose(uint32_t frameIndex) const { - return _trackedDevicePoseMat4[0]; + glm::mat4 result; + { + Lock lock(_mutex); + result = _trackedDevicePoseMat4[0]; + + } + return result; } -void OpenVrDisplayPlugin::customizeContext() { - WindowOpenGLDisplayPlugin::customizeContext(); + +void OpenVrDisplayPlugin::submitSceneTexture(uint32_t frameIndex, uint32_t sceneTexture, const glm::uvec2& sceneSize) { + WindowOpenGLDisplayPlugin::submitSceneTexture(frameIndex, sceneTexture, sceneSize); } -//void OpenVrDisplayPlugin::display(uint32_t frameIndex, uint32_t finalTexture, const glm::uvec2& sceneSize) { -// // Flip y-axis since GL UV coords are backwards. -// static vr::Compositor_TextureBounds leftBounds{ 0, 1, 0.5f, 0 }; -// static vr::Compositor_TextureBounds rightBounds{ 0.5f, 1, 1, 0 }; -// _compositor->Submit(vr::Eye_Left, (void*)finalTexture, &leftBounds); -// _compositor->Submit(vr::Eye_Right, (void*)finalTexture, &rightBounds); -// glFinish(); -//} - -//void OpenVrDisplayPlugin::finishFrame() { -//// swapBuffers(); -// doneCurrent(); -// _compositor->WaitGetPoses(_trackedDevicePose, vr::k_unMaxTrackedDeviceCount); -// for (int i = 0; i < vr::k_unMaxTrackedDeviceCount; i++) { -// _trackedDevicePoseMat4[i] = _sensorResetMat * toGlm(_trackedDevicePose[i].mDeviceToAbsoluteTracking); -// } -// openvr_for_each_eye([&](vr::Hmd_Eye eye) { -// _eyesData[eye]._pose = _trackedDevicePoseMat4[0]; -// }); -//}; - -#endif +void OpenVrDisplayPlugin::internalPresent() { + // Flip y-axis since GL UV coords are backwards. + static vr::VRTextureBounds_t leftBounds{ 0, 0, 0.5f, 1 }; + static vr::VRTextureBounds_t rightBounds{ 0.5f, 0, 1, 1 }; + vr::Texture_t texture{ (void*)_currentSceneTexture, vr::API_OpenGL, vr::ColorSpace_Auto }; + { + Lock lock(_mutex); + _compositor->Submit(vr::Eye_Left, &texture, &leftBounds); + _compositor->Submit(vr::Eye_Right, &texture, &rightBounds); + } + glFinish(); + { + Lock lock(_mutex); + _compositor->WaitGetPoses(_trackedDevicePose, vr::k_unMaxTrackedDeviceCount, nullptr, 0); + for (int i = 0; i < vr::k_unMaxTrackedDeviceCount; i++) { + _trackedDevicePoseMat4[i] = _sensorResetMat * toGlm(_trackedDevicePose[i].mDeviceToAbsoluteTracking); + } + openvr_for_each_eye([&](vr::Hmd_Eye eye) { + _eyesData[eye]._pose = _trackedDevicePoseMat4[0]; + }); + } + //WindowOpenGLDisplayPlugin::internalPresent(); +} diff --git a/libraries/display-plugins/src/display-plugins/openvr/OpenVrDisplayPlugin.h b/plugins/openvr/src/OpenVrDisplayPlugin.h similarity index 74% rename from libraries/display-plugins/src/display-plugins/openvr/OpenVrDisplayPlugin.h rename to plugins/openvr/src/OpenVrDisplayPlugin.h index c8887276b7..8186e59936 100644 --- a/libraries/display-plugins/src/display-plugins/openvr/OpenVrDisplayPlugin.h +++ b/plugins/openvr/src/OpenVrDisplayPlugin.h @@ -9,10 +9,11 @@ #include -#if defined(Q_OS_WIN) #include -#include "../WindowOpenGLDisplayPlugin.h" +#include + +const float TARGET_RATE_OpenVr = 90.0f; // FIXME: get from sdk tracked device property? This number is vive-only. class OpenVrDisplayPlugin : public WindowOpenGLDisplayPlugin { public: @@ -20,9 +21,13 @@ public: virtual const QString & getName() const override; virtual bool isHmd() const override { return true; } + virtual float getTargetFrameRate() override { return TARGET_RATE_OpenVr; } + virtual void activate() override; virtual void deactivate() override; + virtual void customizeContext() override; + virtual glm::uvec2 getRecommendedRenderSize() const override; virtual glm::uvec2 getRecommendedUiSize() const override { return uvec2(1920, 1080); } @@ -32,15 +37,13 @@ public: virtual glm::mat4 getEyeToHeadTransform(Eye eye) const override; virtual glm::mat4 getHeadPose(uint32_t frameIndex) const override; + virtual void submitSceneTexture(uint32_t frameIndex, uint32_t sceneTexture, const glm::uvec2& sceneSize) override; protected: -// virtual void display(uint32_t frameIndex, uint32_t finalTexture, const glm::uvec2& sceneSize) override; - virtual void customizeContext() override; + virtual void internalPresent() override; private: vr::IVRSystem* _hmd { nullptr }; static const QString NAME; }; -#endif - diff --git a/libraries/display-plugins/src/display-plugins/openvr/OpenVrHelpers.cpp b/plugins/openvr/src/OpenVrHelpers.cpp similarity index 80% rename from libraries/display-plugins/src/display-plugins/openvr/OpenVrHelpers.cpp rename to plugins/openvr/src/OpenVrHelpers.cpp index f8e810beaf..7020fcb40d 100644 --- a/libraries/display-plugins/src/display-plugins/openvr/OpenVrHelpers.cpp +++ b/plugins/openvr/src/OpenVrHelpers.cpp @@ -7,14 +7,15 @@ // #include "OpenVrHelpers.h" -#if defined(Q_OS_WIN) - -#include - #include #include -#include "../Logging.h" +#include +#include +#include + + +Q_DECLARE_LOGGING_CATEGORY(displayplugins) using Mutex = std::mutex; using Lock = std::unique_lock; @@ -30,13 +31,13 @@ vr::IVRSystem* acquireOpenVrSystem() { if (hmdPresent) { Lock lock(mutex); if (!activeHmd) { - qCDebug(displayPlugins) << "openvr: No vr::IVRSystem instance active, building"; - vr::HmdError eError = vr::HmdError_None; + qCDebug(displayplugins) << "openvr: No vr::IVRSystem instance active, building"; + vr::EVRInitError eError = vr::VRInitError_None; activeHmd = vr::VR_Init(&eError); - qCDebug(displayPlugins) << "openvr display: HMD is " << activeHmd << " error is " << eError; + qCDebug(displayplugins) << "openvr display: HMD is " << activeHmd << " error is " << eError; } if (activeHmd) { - qCDebug(displayPlugins) << "openvr: incrementing refcount"; + qCDebug(displayplugins) << "openvr: incrementing refcount"; ++refCount; } } @@ -46,10 +47,10 @@ vr::IVRSystem* acquireOpenVrSystem() { void releaseOpenVrSystem() { if (activeHmd) { Lock lock(mutex); - qDebug() << "openvr: decrementing refcount"; + qCDebug(displayplugins) << "openvr: decrementing refcount"; --refCount; if (0 == refCount) { - qDebug() << "openvr: zero refcount, deallocate VR system"; + qCDebug(displayplugins) << "openvr: zero refcount, deallocate VR system"; // Avoid spamming the VR system with activate/deactivate calls at system startup by // putting in a delay before we destory the shutdown the VR subsystem @@ -71,5 +72,3 @@ void releaseOpenVrSystem() { } } } - -#endif diff --git a/libraries/display-plugins/src/display-plugins/openvr/OpenVrHelpers.h b/plugins/openvr/src/OpenVrHelpers.h similarity index 89% rename from libraries/display-plugins/src/display-plugins/openvr/OpenVrHelpers.h rename to plugins/openvr/src/OpenVrHelpers.h index 3e445d90ba..513e1702d2 100644 --- a/libraries/display-plugins/src/display-plugins/openvr/OpenVrHelpers.h +++ b/plugins/openvr/src/OpenVrHelpers.h @@ -7,9 +7,6 @@ // #pragma once -#include - -#if defined(Q_OS_WIN) #include #include #include @@ -18,5 +15,3 @@ vr::IVRSystem* acquireOpenVrSystem(); void releaseOpenVrSystem(); -#endif - diff --git a/plugins/openvr/src/OpenVrProvider.cpp b/plugins/openvr/src/OpenVrProvider.cpp new file mode 100644 index 0000000000..66227a9543 --- /dev/null +++ b/plugins/openvr/src/OpenVrProvider.cpp @@ -0,0 +1,60 @@ +// +// Created by Bradley Austin Davis on 2015/10/25 +// 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 +#include +#include + +#include + +#include "OpenVrDisplayPlugin.h" +#include "ViveControllerManager.h" + +class OpenVrProvider : public QObject, public DisplayProvider, InputProvider +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID DisplayProvider_iid FILE "plugin.json") + Q_INTERFACES(DisplayProvider) + Q_PLUGIN_METADATA(IID InputProvider_iid FILE "plugin.json") + Q_INTERFACES(InputProvider) + +public: + OpenVrProvider(QObject* parent = nullptr) : QObject(parent) {} + virtual ~OpenVrProvider() {} + + virtual DisplayPluginList getDisplayPlugins() override { + static std::once_flag once; + std::call_once(once, [&] { + DisplayPluginPointer plugin(new OpenVrDisplayPlugin()); + if (plugin->isSupported()) { + _displayPlugins.push_back(plugin); + } + }); + return _displayPlugins; + } + + virtual InputPluginList getInputPlugins() override { + static std::once_flag once; + std::call_once(once, [&] { + InputPluginPointer plugin(new ViveControllerManager()); + if (plugin->isSupported()) { + _inputPlugins.push_back(plugin); + } + }); + return _inputPlugins; + } + + +private: + DisplayPluginList _displayPlugins; + InputPluginList _inputPlugins; +}; + +#include "OpenVrProvider.moc" diff --git a/libraries/input-plugins/src/input-plugins/ViveControllerManager.cpp b/plugins/openvr/src/ViveControllerManager.cpp similarity index 97% rename from libraries/input-plugins/src/input-plugins/ViveControllerManager.cpp rename to plugins/openvr/src/ViveControllerManager.cpp index 5315152cea..39056cd46f 100644 --- a/libraries/input-plugins/src/input-plugins/ViveControllerManager.cpp +++ b/plugins/openvr/src/ViveControllerManager.cpp @@ -17,7 +17,6 @@ #include #include #include -#include #include #include #include @@ -26,10 +25,10 @@ #include -#ifdef Q_OS_WIN +#include "OpenVrHelpers.h" + extern vr::TrackedDevicePose_t _trackedDevicePose[vr::k_unMaxTrackedDeviceCount]; extern mat4 _trackedDevicePoseMat4[vr::k_unMaxTrackedDeviceCount]; -#endif vr::IVRSystem* acquireOpenVrSystem(); void releaseOpenVrSystem(); @@ -39,7 +38,7 @@ static const float CONTROLLER_LENGTH_OFFSET = 0.0762f; // three inches static const glm::vec3 CONTROLLER_OFFSET = glm::vec3(CONTROLLER_LENGTH_OFFSET / 2.0f, CONTROLLER_LENGTH_OFFSET / 2.0f, CONTROLLER_LENGTH_OFFSET * 2.0f); -static const QString CONTROLLER_MODEL_STRING = "vr_controller_05_wireless_b"; +static const char* CONTROLLER_MODEL_STRING = "vr_controller_05_wireless_b"; static const QString MENU_PARENT = "Avatar"; static const QString MENU_NAME = "Vive Controllers"; @@ -49,19 +48,14 @@ static const QString RENDER_CONTROLLERS = "Render Hand Controllers"; const QString ViveControllerManager::NAME = "OpenVR"; bool ViveControllerManager::isSupported() const { -#ifdef Q_OS_WIN auto hmd = acquireOpenVrSystem(); bool success = hmd != nullptr; releaseOpenVrSystem(); return success; -#else - return false; -#endif } void ViveControllerManager::activate() { InputPlugin::activate(); -#ifdef Q_OS_WIN _container->addMenu(MENU_PATH); _container->addMenuItem(PluginType::INPUT_PLUGIN, MENU_PATH, RENDER_CONTROLLERS, [this] (bool clicked) { this->setRenderControllers(clicked); }, @@ -72,8 +66,11 @@ void ViveControllerManager::activate() { } Q_ASSERT(_hmd); + auto renderModels = vr::VRRenderModels(); + vr::RenderModel_t model; - if (!_hmd->LoadRenderModel(CONTROLLER_MODEL_STRING.toStdString().c_str(), &model)) { + /* + if (!_hmd->LoadRenderModel(CONTROLLER_MODEL_STRING, &model)) { qDebug() << QString("Unable to load render model %1\n").arg(CONTROLLER_MODEL_STRING); } else { model::Mesh* mesh = new model::Mesh(); @@ -118,7 +115,7 @@ void ViveControllerManager::activate() { _modelLoaded = true; _renderControllers = true; } -#endif + */ // unregister with UserInputMapper auto userInputMapper = DependencyManager::get(); @@ -129,7 +126,6 @@ void ViveControllerManager::activate() { void ViveControllerManager::deactivate() { InputPlugin::deactivate(); -#ifdef Q_OS_WIN _container->removeMenuItem(MENU_NAME, RENDER_CONTROLLERS); _container->removeMenu(MENU_PATH); @@ -139,7 +135,6 @@ void ViveControllerManager::deactivate() { } _inputDevice->_poseStateMap.clear(); -#endif // unregister with UserInputMapper auto userInputMapper = DependencyManager::get(); @@ -225,7 +220,6 @@ void ViveControllerManager::pluginUpdate(float deltaTime, bool jointsCaptured) { } void ViveControllerManager::InputDevice::update(float deltaTime, bool jointsCaptured) { -#ifdef Q_OS_WIN _poseStateMap.clear(); _buttonPressedMap.clear(); @@ -276,7 +270,6 @@ void ViveControllerManager::InputDevice::update(float deltaTime, bool jointsCapt } _trackedControllers = numTrackedControllers; -#endif } void ViveControllerManager::InputDevice::focusOutEvent() { @@ -286,7 +279,6 @@ void ViveControllerManager::InputDevice::focusOutEvent() { // These functions do translation from the Steam IDs to the standard controller IDs void ViveControllerManager::InputDevice::handleAxisEvent(uint32_t axis, float x, float y, bool left) { -#ifdef Q_OS_WIN //FIX ME? It enters here every frame: probably we want to enter only if an event occurs axis += vr::k_EButton_Axis0; using namespace controller; @@ -296,12 +288,10 @@ void ViveControllerManager::InputDevice::handleAxisEvent(uint32_t axis, float x, } else if (axis == vr::k_EButton_SteamVR_Trigger) { _axisStateMap[left ? LT : RT] = x; } -#endif } // These functions do translation from the Steam IDs to the standard controller IDs void ViveControllerManager::InputDevice::handleButtonEvent(uint32_t button, bool pressed, bool left) { -#ifdef Q_OS_WIN if (!pressed) { return; } @@ -319,7 +309,6 @@ void ViveControllerManager::InputDevice::handleButtonEvent(uint32_t button, bool //FIX ME: not able to ovrewrite the behaviour of this button _buttonPressedMap.insert(left ? controller::LEFT_SECONDARY_THUMB : controller::RIGHT_SECONDARY_THUMB); } -#endif } void ViveControllerManager::InputDevice::handlePoseEvent(const mat4& mat, bool left) { diff --git a/libraries/input-plugins/src/input-plugins/ViveControllerManager.h b/plugins/openvr/src/ViveControllerManager.h similarity index 98% rename from libraries/input-plugins/src/input-plugins/ViveControllerManager.h rename to plugins/openvr/src/ViveControllerManager.h index 02bdecb10a..077aec7fc3 100644 --- a/libraries/input-plugins/src/input-plugins/ViveControllerManager.h +++ b/plugins/openvr/src/ViveControllerManager.h @@ -20,7 +20,7 @@ #include #include #include -#include "InputPlugin.h" +#include #include #include diff --git a/plugins/openvr/src/plugin.json b/plugins/openvr/src/plugin.json new file mode 100644 index 0000000000..0967ef424b --- /dev/null +++ b/plugins/openvr/src/plugin.json @@ -0,0 +1 @@ +{} diff --git a/stack-manager/CMakeLists.txt b/stack-manager/CMakeLists.txt index e70fefc6e0..520beb6893 100644 --- a/stack-manager/CMakeLists.txt +++ b/stack-manager/CMakeLists.txt @@ -1,16 +1,20 @@ set(TARGET_NAME "stack-manager") set(BUILD_BUNDLE YES) setup_hifi_project(Widgets Gui Svg Core Network WebKitWidgets) +add_dependencies(${TARGET_NAME} assignment-client domain-server) +include_application_version() if (WIN32) target_zlib() endif () target_quazip() -set_target_properties( - ${TARGET_NAME} PROPERTIES - EXCLUDE_FROM_ALL TRUE -) +if (UNIX) + set_target_properties( + ${TARGET_NAME} PROPERTIES + EXCLUDE_FROM_ALL TRUE + ) +endif (UNIX) if (DEFINED ENV{JOB_ID}) set(PR_BUILD "false") @@ -45,4 +49,5 @@ if (APPLE) set(SM_SRCS ${SM_SRCS} "${CMAKE_CURRENT_SOURCE_DIR}/assets/icon.icns") endif () -package_libraries_for_deployment() \ No newline at end of file +package_libraries_for_deployment() +consolidate_stack_components() \ No newline at end of file diff --git a/stack-manager/src/AppDelegate.cpp b/stack-manager/src/AppDelegate.cpp index ea9310a2d8..3e65406bc5 100644 --- a/stack-manager/src/AppDelegate.cpp +++ b/stack-manager/src/AppDelegate.cpp @@ -81,10 +81,6 @@ void myMessageHandler(QtMsgType type, const QMessageLogContext &context, const Q AppDelegate::AppDelegate(int argc, char* argv[]) : QApplication(argc, argv), - _qtReady(false), - _dsReady(false), - _dsResourcesReady(false), - _acReady(false), _domainServerProcess(NULL), _acMonitorProcess(NULL), _domainServerName("localhost") @@ -115,14 +111,15 @@ AppDelegate::AppDelegate(int argc, char* argv[]) : _window = new MainWindow(); - createExecutablePath(); - downloadLatestExecutablesAndRequirements(); - _checkVersionTimer.setInterval(0); connect(&_checkVersionTimer, SIGNAL(timeout()), this, SLOT(checkVersion())); _checkVersionTimer.start(); connect(this, &QApplication::aboutToQuit, this, &AppDelegate::stopStack); + + _window->setRequirementsLastChecked(QDateTime::currentDateTime().toString()); + _window->show(); + toggleStack(true); } AppDelegate::~AppDelegate() { @@ -427,227 +424,6 @@ void AppDelegate::handleContentSetDownloadFinished() { emit domainAddressChanged(); } -void AppDelegate::onFileSuccessfullyInstalled(const QUrl& url) { - if (url == GlobalData::getInstance().getRequirementsURL()) { - _qtReady = true; - } else if (url == GlobalData::getInstance().getAssignmentClientURL()) { - _acReady = true; - } else if (url == GlobalData::getInstance().getDomainServerURL()) { - _dsReady = true; - } else if (url == GlobalData::getInstance().getDomainServerResourcesURL()) { - _dsResourcesReady = true; - } - - if (_qtReady && _acReady && _dsReady && _dsResourcesReady) { - _window->setRequirementsLastChecked(QDateTime::currentDateTime().toString()); - _window->show(); - toggleStack(true); - } -} - -void AppDelegate::createExecutablePath() { - QDir launchDir(GlobalData::getInstance().getClientsLaunchPath()); - QDir resourcesDir(GlobalData::getInstance().getClientsResourcesPath()); - QDir logsDir(GlobalData::getInstance().getLogsPath()); - if (!launchDir.exists()) { - if (QDir().mkpath(launchDir.absolutePath())) { - qDebug() << "Successfully created directory: " - << launchDir.absolutePath(); - } else { - qCritical() << "Failed to create directory: " - << launchDir.absolutePath(); - } - } - if (!resourcesDir.exists()) { - if (QDir().mkpath(resourcesDir.absolutePath())) { - qDebug() << "Successfully created directory: " - << resourcesDir.absolutePath(); - } else { - qCritical() << "Failed to create directory: " - << resourcesDir.absolutePath(); - } - } - if (!logsDir.exists()) { - if (QDir().mkpath(logsDir.absolutePath())) { - qDebug() << "Successfully created directory: " - << logsDir.absolutePath(); - } else { - qCritical() << "Failed to create directory: " - << logsDir.absolutePath(); - } - } -} - -void AppDelegate::downloadLatestExecutablesAndRequirements() { - // Check if Qt is already installed - if (GlobalData::getInstance().getPlatform() == "mac") { - if (QDir(GlobalData::getInstance().getClientsLaunchPath() + "QtCore.framework").exists()) { - _qtReady = true; - } - } else if (GlobalData::getInstance().getPlatform() == "win") { - if (QFileInfo(GlobalData::getInstance().getClientsLaunchPath() + "Qt5Core.dll").exists()) { - _qtReady = true; - } - } else { // linux - if (QFileInfo(GlobalData::getInstance().getClientsLaunchPath() + "libQt5Core.so.5").exists()) { - _qtReady = true; - } - } - - - QFile reqZipFile(GlobalData::getInstance().getRequirementsZipPath()); - QByteArray reqZipData; - if (reqZipFile.open(QIODevice::ReadOnly)) { - reqZipData = reqZipFile.readAll(); - reqZipFile.close(); - } - QFile resZipFile(GlobalData::getInstance().getDomainServerResourcesZipPath()); - QByteArray resZipData; - if (resZipFile.open(QIODevice::ReadOnly)) { - resZipData = resZipFile.readAll(); - resZipFile.close(); - } - - QDir resourcesDir(GlobalData::getInstance().getClientsResourcesPath()); - if (!(resourcesDir.entryInfoList(QDir::AllEntries).size() < 3)) { - _dsResourcesReady = true; - } - - // if the user has set hifiBuildDirectory, don't attempt to download the domain-server or assignement-client - if (GlobalData::getInstance().isGetHifiBuildDirectorySet()) { - _dsReady = true; - _acReady = true; - } else { - QByteArray dsData; - QFile dsFile(GlobalData::getInstance().getDomainServerExecutablePath()); - if (dsFile.open(QIODevice::ReadOnly)) { - dsData = dsFile.readAll(); - dsFile.close(); - } - QByteArray acData; - QFile acFile(GlobalData::getInstance().getAssignmentClientExecutablePath()); - if (acFile.open(QIODevice::ReadOnly)) { - acData = acFile.readAll(); - acFile.close(); - } - - QNetworkRequest acReq(QUrl(GlobalData::getInstance().getAssignmentClientMD5URL())); - QNetworkReply* acReply = _manager->get(acReq); - QEventLoop acLoop; - connect(acReply, SIGNAL(finished()), &acLoop, SLOT(quit())); - acLoop.exec(); - QByteArray acMd5Data = acReply->readAll().trimmed(); - if (GlobalData::getInstance().getPlatform() == "win") { - // fix for reading the MD5 hash from Windows-generated - // binary data of the MD5 hash - QTextStream stream(acMd5Data); - stream >> acMd5Data; - } - - // fix for Mac and Linux network accessibility - if (acMd5Data.size() == 0) { - // network is not accessible - qDebug() << "Could not connect to the internet."; - _window->show(); - return; - } - - qDebug() << "AC MD5: " << acMd5Data; - if (acMd5Data.toLower() == QCryptographicHash::hash(acData, QCryptographicHash::Md5).toHex()) { - _acReady = true; - } - - - QNetworkRequest dsReq(QUrl(GlobalData::getInstance().getDomainServerMD5URL())); - QNetworkReply* dsReply = _manager->get(dsReq); - QEventLoop dsLoop; - connect(dsReply, SIGNAL(finished()), &dsLoop, SLOT(quit())); - dsLoop.exec(); - QByteArray dsMd5Data = dsReply->readAll().trimmed(); - if (GlobalData::getInstance().getPlatform() == "win") { - // fix for reading the MD5 hash from Windows generated - // binary data of the MD5 hash - QTextStream stream(dsMd5Data); - stream >> dsMd5Data; - } - qDebug() << "DS MD5: " << dsMd5Data; - if (dsMd5Data.toLower() == QCryptographicHash::hash(dsData, QCryptographicHash::Md5).toHex()) { - _dsReady = true; - } - } - - if (_qtReady) { - // check MD5 of requirements.zip only if Qt is found - QNetworkRequest reqZipReq(QUrl(GlobalData::getInstance().getRequirementsMD5URL())); - QNetworkReply* reqZipReply = _manager->get(reqZipReq); - QEventLoop reqZipLoop; - connect(reqZipReply, SIGNAL(finished()), &reqZipLoop, SLOT(quit())); - reqZipLoop.exec(); - QByteArray reqZipMd5Data = reqZipReply->readAll().trimmed(); - if (GlobalData::getInstance().getPlatform() == "win") { - // fix for reading the MD5 hash from Windows generated - // binary data of the MD5 hash - QTextStream stream(reqZipMd5Data); - stream >> reqZipMd5Data; - } - qDebug() << "Requirements ZIP MD5: " << reqZipMd5Data; - if (reqZipMd5Data.toLower() != QCryptographicHash::hash(reqZipData, QCryptographicHash::Md5).toHex()) { - _qtReady = false; - } - } - - if (_dsResourcesReady) { - // check MD5 of resources.zip only if Domain Server - // resources are installed - QNetworkRequest resZipReq(QUrl(GlobalData::getInstance().getDomainServerResourcesMD5URL())); - QNetworkReply* resZipReply = _manager->get(resZipReq); - QEventLoop resZipLoop; - connect(resZipReply, SIGNAL(finished()), &resZipLoop, SLOT(quit())); - resZipLoop.exec(); - QByteArray resZipMd5Data = resZipReply->readAll().trimmed(); - if (GlobalData::getInstance().getPlatform() == "win") { - // fix for reading the MD5 hash from Windows generated - // binary data of the MD5 hash - QTextStream stream(resZipMd5Data); - stream >> resZipMd5Data; - } - qDebug() << "Domain Server Resources ZIP MD5: " << resZipMd5Data; - if (resZipMd5Data.toLower() != QCryptographicHash::hash(resZipData, QCryptographicHash::Md5).toHex()) { - _dsResourcesReady = false; - } - } - - DownloadManager* downloadManager = 0; - if (!_qtReady || !_acReady || !_dsReady || !_dsResourcesReady) { - // initialise DownloadManager - downloadManager = new DownloadManager(_manager); - downloadManager->setWindowModality(Qt::ApplicationModal); - connect(downloadManager, SIGNAL(fileSuccessfullyInstalled(QUrl)), - SLOT(onFileSuccessfullyInstalled(QUrl))); - downloadManager->show(); - } else { - _window->setRequirementsLastChecked(QDateTime::currentDateTime().toString()); - _window->show(); - toggleStack(true); - } - - if (!_qtReady) { - downloadManager->downloadFile(GlobalData::getInstance().getRequirementsURL()); - } - - if (!_acReady) { - downloadManager->downloadFile(GlobalData::getInstance().getAssignmentClientURL()); - } - - if (!_dsReady) { - downloadManager->downloadFile(GlobalData::getInstance().getDomainServerURL()); - } - - if (!_dsResourcesReady) { - downloadManager->downloadFile(GlobalData::getInstance().getDomainServerResourcesURL()); - } -} - void AppDelegate::checkVersion() { QNetworkRequest latestVersionRequest((QUrl(CHECK_BUILDS_URL))); latestVersionRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT); diff --git a/stack-manager/src/AppDelegate.h b/stack-manager/src/AppDelegate.h index 1d4728b7ce..ebcb8ce904 100644 --- a/stack-manager/src/AppDelegate.h +++ b/stack-manager/src/AppDelegate.h @@ -53,7 +53,6 @@ signals: void indexPathChangeResponse(bool wasSuccessful); void stackStateChanged(bool isOn); private slots: - void onFileSuccessfullyInstalled(const QUrl& url); void requestDomainServerID(); void handleDomainIDReply(); void handleDomainGetReply(); @@ -64,16 +63,10 @@ private slots: private: void parseCommandLine(); - void createExecutablePath(); - void downloadLatestExecutablesAndRequirements(); void changeDomainServerIndexPath(const QString& newPath); QNetworkAccessManager* _manager; - bool _qtReady; - bool _dsReady; - bool _dsResourcesReady; - bool _acReady; BackgroundProcess* _domainServerProcess; BackgroundProcess* _acMonitorProcess; QHash _scriptProcesses; diff --git a/stack-manager/src/GlobalData.cpp b/stack-manager/src/GlobalData.cpp index ecc5ed520d..bc2f3e3482 100644 --- a/stack-manager/src/GlobalData.cpp +++ b/stack-manager/src/GlobalData.cpp @@ -32,11 +32,8 @@ GlobalData::GlobalData() { _resourcePath = "resources/"; _assignmentClientExecutable = "assignment-client"; _domainServerExecutable = "domain-server"; - QString applicationSupportDirectory = QStandardPaths::writableLocation(QStandardPaths::DataLocation); - if (PR_BUILD) { - applicationSupportDirectory += "/pr-binaries"; - } - + QString applicationSupportDirectory = QCoreApplication::applicationDirPath(); + _clientsLaunchPath = QDir::toNativeSeparators(applicationSupportDirectory + "/"); _clientsResourcePath = QDir::toNativeSeparators(applicationSupportDirectory + "/" + _resourcePath); @@ -49,18 +46,6 @@ GlobalData::GlobalData() { _domainServerExecutablePath.append(".exe"); } - _requirementsURL = urlBase + "/binaries/" + _platform + "/requirements/requirements.zip"; - _requirementsZipPath = _clientsLaunchPath + "requirements.zip"; - _requirementsMD5URL = urlBase + "/binaries/" + _platform + "/requirements/requirements.md5"; - _assignmentClientURL = urlBase + "/binaries/" + _platform + "/assignment-client" + (_platform == "win" ? "/assignment-client.exe" : "/assignment-client"); - _domainServerResourcesURL = urlBase + "/binaries/" + _platform + "/domain-server/resources.zip"; - _domainServerResourcesZipPath = _clientsLaunchPath + "resources.zip"; - _domainServerResourcesMD5URL = urlBase + "/binaries/" + _platform + "/domain-server/resources.md5"; - _domainServerURL = urlBase + "/binaries/" + _platform + "/domain-server" + (_platform == "win" ? "/domain-server.exe" : "/domain-server"); - - _assignmentClientMD5URL = urlBase + "/binaries/" + _platform + "/assignment-client/assignment-client.md5"; - _domainServerMD5URL = urlBase + "/binaries/" + _platform + "/domain-server/domain-server.md5"; - _defaultDomain = "localhost"; _logsPath = QDir::toNativeSeparators(_clientsLaunchPath + "logs/"); _availableAssignmentTypes.insert("audio-mixer", 0); diff --git a/stack-manager/src/GlobalData.h b/stack-manager/src/GlobalData.h index 58c9a93526..a8687f31d2 100644 --- a/stack-manager/src/GlobalData.h +++ b/stack-manager/src/GlobalData.h @@ -9,6 +9,7 @@ #ifndef hifi_GlobalData_h #define hifi_GlobalData_h +#include #include #include