diff --git a/BUILD.md b/BUILD.md index 159d9101fa..be30d35f21 100644 --- a/BUILD.md +++ b/BUILD.md @@ -98,6 +98,9 @@ Currently building on Windows has been tested using the following compilers: Whichever version of Visual Studio you use, first install [Microsoft Windows SDK for Windows 7 and .NET Framework 4](http://www.microsoft.com/en-us/download/details.aspx?id=8279). +######Windows 8.1 +You may have already downloaded the Windows 8 SDK (e.g. if you have previously installed Visual Studio 2013). If so, change CMAKE_PREFIX_PATH in %HIFI_DIR%\CMakeLists.txt to point to the Windows 8 SDK binaries. The default path is `C:\Program Files (x86)\Windows Kits\8.1\Lib\winv6.3\um\x86` + #####Visual Studio C++ 2010 Express Visual Studio C++ 2010 Express can be downloaded [here](http://www.visualstudio.com/en-us/downloads#d-2010-express). @@ -116,6 +119,8 @@ Visual Studio 2013 doesn't have a shortcut to start a Visual Studio Command Prom "%VS120COMNTOOLS%\vsvars32.bat" +If you experience issues building interface on Visual Studio 2013, try generating the build files with Visual Studio 2010 instead. To do so, download Visual Studio 2010 and run `cmake .. -G "Visual Studio 10"` (Assuming running from %HIFI_DIR%\build). + ####Qt You can use the online installer or the offline installer. If you use the offline installer, be sure to select the "OpenGL" version. diff --git a/CMakeLists.txt b/CMakeLists.txt index b8566dd050..b7c55740a0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,6 +9,10 @@ add_definitions(-DGLM_FORCE_RADIANS) if (WIN32) add_definitions(-DNOMINMAX -D_CRT_SECURE_NO_WARNINGS) + # set path for Microsoft SDKs + # if get build error about missing 'glu32' this path is likely wrong + # Uncomment the line with 8.1 if running Windows 8.1 + #set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} "C:\\Program Files (x86)\\Windows Kits\\8.1\\Lib\\winv6.3\\um\\x86") set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} "C:\\Program Files\\Microsoft SDKs\\Windows\\v7.1 ") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP") elseif (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX) diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index 0449e0d682..144df2202b 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -29,6 +29,8 @@ #include // TODO: consider moving to scriptengine.h #include // TODO: consider moving to scriptengine.h +#include "avatars/ScriptableAvatar.h" + #include "Agent.h" Agent::Agent(const QByteArray& packet) : @@ -228,8 +230,9 @@ void Agent::run() { qDebug() << "Downloaded script:" << scriptContents; // setup an Avatar for the script to use - AvatarData scriptedAvatar; - + ScriptableAvatar scriptedAvatar(&_scriptEngine); + scriptedAvatar.setForceFaceshiftConnected(true); + // call model URL setters with empty URLs so our avatar, if user, will have the default models scriptedAvatar.setFaceModelURL(QUrl()); scriptedAvatar.setSkeletonModelURL(QUrl()); diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index 37d4a7993d..ad4787b407 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -237,14 +237,6 @@ void AudioMixer::addBufferToMixForListeningNodeWithBuffer(PositionalAudioRingBuf _clientSamples[delayedChannelIndex + SINGLE_STEREO_OFFSET] += delayBufferSample[1]; } - // The following code is pretty gross and redundant, but AFAIK it's the best way to avoid - // too many conditionals in handling the delay samples at the beginning of _clientSamples. - // Basically we try to take the samples in batches of four, and then handle the remainder - // conditionally to get rid of the rest. - - const int DOUBLE_STEREO_OFFSET = 4; - const int TRIPLE_STEREO_OFFSET = 6; - if (numSamplesDelay > 0) { // if there was a sample delay for this buffer, we need to pull samples prior to the nextOutput // to stick at the beginning diff --git a/assignment-client/src/audio/AudioMixerClientData.cpp b/assignment-client/src/audio/AudioMixerClientData.cpp index b42a690d97..a4983e6a95 100644 --- a/assignment-client/src/audio/AudioMixerClientData.cpp +++ b/assignment-client/src/audio/AudioMixerClientData.cpp @@ -90,12 +90,12 @@ int AudioMixerClientData::parseData(const QByteArray& packet) { switch (packetArrivalInfo._status) { case SequenceNumberStats::Early: { int packetsLost = packetArrivalInfo._seqDiffFromExpected; - avatarRingBuffer->parseData(packet, packetsLost); + avatarRingBuffer->parseDataAndHandleDroppedPackets(packet, packetsLost); break; } case SequenceNumberStats::OnTime: { // ask the AvatarAudioRingBuffer instance to parse the data - avatarRingBuffer->parseData(packet); + avatarRingBuffer->parseDataAndHandleDroppedPackets(packet, 0); break; } default: { @@ -134,12 +134,12 @@ int AudioMixerClientData::parseData(const QByteArray& packet) { switch (packetArrivalInfo._status) { case SequenceNumberStats::Early: { int packetsLost = packetArrivalInfo._seqDiffFromExpected; - matchingInjectedRingBuffer->parseData(packet, packetsLost); + matchingInjectedRingBuffer->parseDataAndHandleDroppedPackets(packet, packetsLost); break; } case SequenceNumberStats::OnTime: { // ask the AvatarAudioRingBuffer instance to parse the data - matchingInjectedRingBuffer->parseData(packet); + matchingInjectedRingBuffer->parseDataAndHandleDroppedPackets(packet, 0); break; } default: { diff --git a/assignment-client/src/audio/AvatarAudioRingBuffer.cpp b/assignment-client/src/audio/AvatarAudioRingBuffer.cpp index f6edde7ac4..382e8de68b 100644 --- a/assignment-client/src/audio/AvatarAudioRingBuffer.cpp +++ b/assignment-client/src/audio/AvatarAudioRingBuffer.cpp @@ -18,7 +18,7 @@ AvatarAudioRingBuffer::AvatarAudioRingBuffer(bool isStereo, bool dynamicJitterBu } -int AvatarAudioRingBuffer::parseData(const QByteArray& packet, int packetsSkipped) { +int AvatarAudioRingBuffer::parseDataAndHandleDroppedPackets(const QByteArray& packet, int packetsSkipped) { frameReceivedUpdateTimingStats(); _shouldLoopbackForNode = (packetTypeForPacket(packet) == PacketTypeMicrophoneAudioWithEcho); diff --git a/assignment-client/src/audio/AvatarAudioRingBuffer.h b/assignment-client/src/audio/AvatarAudioRingBuffer.h index df9dc3787a..96233220cd 100644 --- a/assignment-client/src/audio/AvatarAudioRingBuffer.h +++ b/assignment-client/src/audio/AvatarAudioRingBuffer.h @@ -20,7 +20,7 @@ class AvatarAudioRingBuffer : public PositionalAudioRingBuffer { public: AvatarAudioRingBuffer(bool isStereo = false, bool dynamicJitterBuffer = false); - int parseData(const QByteArray& packet, int packetsSkipped = 0); + int parseDataAndHandleDroppedPackets(const QByteArray& packet, int packetsSkipped); private: // disallow copying of AvatarAudioRingBuffer objects AvatarAudioRingBuffer(const AvatarAudioRingBuffer&); diff --git a/assignment-client/src/avatars/ScriptableAvatar.cpp b/assignment-client/src/avatars/ScriptableAvatar.cpp new file mode 100644 index 0000000000..150e364ed7 --- /dev/null +++ b/assignment-client/src/avatars/ScriptableAvatar.cpp @@ -0,0 +1,88 @@ +// +// ScriptableAvatar.cpp +// +// +// Created by Clement on 7/22/14. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include + +#include "ScriptableAvatar.h" + +ScriptableAvatar::ScriptableAvatar(ScriptEngine* scriptEngine) : _scriptEngine(scriptEngine), _animation(NULL) { + connect(_scriptEngine, SIGNAL(update(float)), this, SLOT(update(float))); +} + +// hold and priority unused but kept so that client side JS can run. +void ScriptableAvatar::startAnimation(const QString& url, float fps, float priority, + bool loop, bool hold, float firstFrame, float lastFrame, const QStringList& maskedJoints) { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "startAnimation", Q_ARG(const QString&, url), Q_ARG(float, fps), + Q_ARG(float, priority), Q_ARG(bool, loop), Q_ARG(bool, hold), Q_ARG(float, firstFrame), + Q_ARG(float, lastFrame), Q_ARG(const QStringList&, maskedJoints)); + return; + } + _animation = _scriptEngine->getAnimationCache()->getAnimation(url); + _animationDetails = AnimationDetails("", QUrl(url), fps, 0, loop, hold, false, firstFrame, lastFrame, true, firstFrame); + _maskedJoints = maskedJoints; +} + +void ScriptableAvatar::stopAnimation() { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "stopAnimation"); + return; + } + _animation.clear(); +} + +AnimationDetails ScriptableAvatar::getAnimationDetails() { + if (QThread::currentThread() != thread()) { + AnimationDetails result; + QMetaObject::invokeMethod(this, "getAnimationDetails", Qt::BlockingQueuedConnection, + Q_RETURN_ARG(AnimationDetails, result)); + return result; + } + return _animationDetails; +} + +void ScriptableAvatar::update(float deltatime) { + // Run animation + if (_animation != NULL && _animation->isValid() && _animation->getFrames().size() > 0) { + QStringList modelJoints = getJointNames(); + QStringList animationJoints = _animation->getJointNames(); + + if (_jointData.size() != modelJoints.size()) { + _jointData.resize(modelJoints.size()); + } + + float frameIndex = _animationDetails.frameIndex + deltatime * _animationDetails.fps; + if (_animationDetails.loop || frameIndex < _animationDetails.lastFrame) { + while (frameIndex >= _animationDetails.lastFrame) { + frameIndex -= (_animationDetails.lastFrame - _animationDetails.firstFrame); + } + _animationDetails.frameIndex = frameIndex; + + const int frameCount = _animation->getFrames().size(); + const FBXAnimationFrame& floorFrame = _animation->getFrames().at((int)glm::floor(frameIndex) % frameCount); + const FBXAnimationFrame& ceilFrame = _animation->getFrames().at((int)glm::ceil(frameIndex) % frameCount); + const float frameFraction = glm::fract(frameIndex); + + for (int i = 0; i < modelJoints.size(); i++) { + int mapping = animationJoints.indexOf(modelJoints[i]); + if (mapping != -1 && !_maskedJoints.contains(modelJoints[i])) { + JointData& data = _jointData[i]; + data.valid = true; + data.rotation = safeMix(floorFrame.rotations.at(i), ceilFrame.rotations.at(i), frameFraction); + } else { + _jointData[i].valid = false; + } + } + } else { + _animation.clear(); + } + } +} diff --git a/assignment-client/src/avatars/ScriptableAvatar.h b/assignment-client/src/avatars/ScriptableAvatar.h new file mode 100644 index 0000000000..5a99c8c8da --- /dev/null +++ b/assignment-client/src/avatars/ScriptableAvatar.h @@ -0,0 +1,40 @@ +// +// ScriptableAvatar.h +// +// +// Created by Clement on 7/22/14. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_ScriptableAvatar_h +#define hifi_ScriptableAvatar_h + +#include +#include +#include + +class ScriptableAvatar : public AvatarData { + Q_OBJECT +public: + ScriptableAvatar(ScriptEngine* scriptEngine); + + /// Allows scripts to run animations. + Q_INVOKABLE void startAnimation(const QString& url, float fps = 30.0f, float priority = 1.0f, bool loop = false, + bool hold = false, float firstFrame = 0.0f, float lastFrame = FLT_MAX, const QStringList& maskedJoints = QStringList()); + Q_INVOKABLE void stopAnimation(); + Q_INVOKABLE AnimationDetails getAnimationDetails(); + +private slots: + void update(float deltatime); + +private: + ScriptEngine* _scriptEngine; + AnimationPointer _animation; + AnimationDetails _animationDetails; + QStringList _maskedJoints; +}; + +#endif // hifi_ScriptableAvatar_h \ No newline at end of file diff --git a/cmake/macros/HifiLibrarySearchHints.cmake b/cmake/macros/HifiLibrarySearchHints.cmake new file mode 100644 index 0000000000..f56b3134d8 --- /dev/null +++ b/cmake/macros/HifiLibrarySearchHints.cmake @@ -0,0 +1,27 @@ +# +# HifiLibrarySearchHints.cmake +# +# Created by Stephen Birarda on July 24th, 2014 +# Copyright 2014 High Fidelity, Inc. +# +# Distributed under the Apache License, Version 2.0. +# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +# + +macro(HIFI_LIBRARY_SEARCH_HINTS LIBRARY_FOLDER) + string(TOUPPER ${LIBRARY_FOLDER} LIBRARY_PREFIX) + set(${LIBRARY_PREFIX}_SEARCH_DIRS "") + + if (${LIBRARY_PREFIX}_ROOT_DIR) + set(${LIBRARY_PREFIX}_SEARCH_DIRS "${${LIBRARY_PREFIX}_ROOT_DIR}") + endif () + + if (DEFINED ENV{${LIBRARY_PREFIX}_ROOT_DIR}) + set(${LIBRARY_PREFIX}_SEARCH_DIRS "${${LIBRARY_PREFIX}_SEARCH_DIRS}" "$ENV{${LIBRARY_PREFIX}_ROOT_DIR}") + endif () + + if (DEFINED ENV{HIFI_LIB_DIR}) + set(${LIBRARY_PREFIX}_SEARCH_DIRS "${${LIBRARY_PREFIX}_SEARCH_DIRS}" "$ENV{HIFI_LIB_DIR}/${LIBRARY_FOLDER}") + endif () + +endmacro(HIFI_LIBRARY_SEARCH_HINTS _library_folder) \ No newline at end of file diff --git a/cmake/modules/FindFaceshift.cmake b/cmake/modules/FindFaceshift.cmake index e11c21cd3f..2641475fa3 100644 --- a/cmake/modules/FindFaceshift.cmake +++ b/cmake/modules/FindFaceshift.cmake @@ -18,7 +18,8 @@ # See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html # -set(FACESHIFT_SEARCH_DIRS "${FACESHIFT_ROOT_DIR}" "$ENV{FACESHIFT_ROOT_DIR}" "$ENV{HIFI_LIB_DIR}/faceshift") +include("${MACRO_DIR}/HifiLibrarySearchHints.cmake") +hifi_library_search_hints("faceshift") find_path(FACESHIFT_INCLUDE_DIRS fsbinarystream.h PATH_SUFFIXES include HINTS ${FACESHIFT_SEARCH_DIRS}) @@ -41,4 +42,4 @@ set(FACESHIFT_LIBRARIES ${FACESHIFT_LIBRARY}) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(FACESHIFT DEFAULT_MSG FACESHIFT_INCLUDE_DIRS FACESHIFT_LIBRARIES) -mark_as_advanced(FACESHIFT_INCLUDE_DIRS FACESHIFT_LIBRARIES) \ No newline at end of file +mark_as_advanced(FACESHIFT_INCLUDE_DIRS FACESHIFT_LIBRARIES FACESHIFT_SEARCH_DIRS) \ No newline at end of file diff --git a/cmake/modules/FindGLEW.cmake b/cmake/modules/FindGLEW.cmake index 59927dc468..99d6dfc003 100644 --- a/cmake/modules/FindGLEW.cmake +++ b/cmake/modules/FindGLEW.cmake @@ -19,12 +19,13 @@ # if (WIN32) - set(WIN_GLEW_SEARCH_DIRS "${GLEW_ROOT_DIR}" "$ENV{GLEW_ROOT_DIR}" "$ENV{HIFI_LIB_DIR}/glew") + include("${MACRO_DIR}/HifiLibrarySearchHints.cmake") + hifi_library_search_hints("glew") - find_path(GLEW_INCLUDE_DIRS GL/glew.h PATH_SUFFIXES include HINTS ${WIN_GLEW_SEARCH_DIRS}) + find_path(GLEW_INCLUDE_DIRS GL/glew.h PATH_SUFFIXES include HINTS ${GLEW_SEARCH_DIRS}) - find_library(GLEW_LIBRARY_RELEASE glew32s PATH_SUFFIXES "lib/Release/Win32" "lib" HINTS ${WIN_GLEW_SEARCH_DIRS}) - find_library(GLEW_LIBRARY_DEBUG glew32s PATH_SUFFIXES "lib/Debug/Win32" "lib" HINTS ${WIN_GLEW_SEARCH_DIRS}) + find_library(GLEW_LIBRARY_RELEASE glew32s PATH_SUFFIXES "lib/Release/Win32" "lib" HINTS ${GLEW_SEARCH_DIRS}) + find_library(GLEW_LIBRARY_DEBUG glew32s PATH_SUFFIXES "lib/Debug/Win32" "lib" HINTS ${GLEW_SEARCH_DIRS}) include(SelectLibraryConfigurations) select_library_configurations(GLEW) @@ -35,4 +36,4 @@ set(GLEW_LIBRARIES "${GLEW_LIBRARY}") include(FindPackageHandleStandardArgs) find_package_handle_standard_args(GLEW DEFAULT_MSG GLEW_INCLUDE_DIRS GLEW_LIBRARIES) -mark_as_advanced(GLEW_INCLUDE_DIRS GLEW_LIBRARIES) \ No newline at end of file +mark_as_advanced(GLEW_INCLUDE_DIRS GLEW_LIBRARIES GLEW_SEARCH_DIRS) \ No newline at end of file diff --git a/cmake/modules/FindGLM.cmake b/cmake/modules/FindGLM.cmake index 04bc252796..852ebbc15e 100644 --- a/cmake/modules/FindGLM.cmake +++ b/cmake/modules/FindGLM.cmake @@ -14,13 +14,14 @@ # # setup hints for GLM search -set(GLM_HEADER_SEARCH_HINTS "${GLM_ROOT_DIR}" "$ENV{GLM_ROOT_DIR}" "$ENV{HIFI_LIB_DIR}/glm") +include("${MACRO_DIR}/HifiLibrarySearchHints.cmake") +hifi_library_search_hints("glm") # locate header -find_path(GLM_INCLUDE_DIR "glm/glm.hpp" HINTS ${GLM_HEADER_SEARCH_HINTS}) +find_path(GLM_INCLUDE_DIR "glm/glm.hpp" HINTS ${GLM_SEARCH_DIRS}) set(GLM_INCLUDE_DIRS "${GLM_INCLUDE_DIR}") include(FindPackageHandleStandardArgs) find_package_handle_standard_args(GLM DEFAULT_MSG GLM_INCLUDE_DIRS) -mark_as_advanced(GLM_INCLUDE_DIRS) \ No newline at end of file +mark_as_advanced(GLM_INCLUDE_DIRS GLM_SEARCH_DIRS) \ No newline at end of file diff --git a/cmake/modules/FindGLUT.cmake b/cmake/modules/FindGLUT.cmake index 881e649ecd..e7bf752aca 100644 --- a/cmake/modules/FindGLUT.cmake +++ b/cmake/modules/FindGLUT.cmake @@ -18,16 +18,17 @@ # See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html # -set(GLUT_HINT_DIRS "${GLUT_ROOT_DIR}" "$ENV{GLUT_ROOT_DIR}" "$ENV{HIFI_LIB_DIR}/freeglut") +include("${MACRO_DIR}/HifiLibrarySearchHints.cmake") +hifi_library_search_hints("freeglut") if (WIN32) - set(GLUT_HINT_DIRS "${GLUT_HINT_DIRS}" "${OPENGL_INCLUDE_DIR}") + set(GLUT_HINT_DIRS "${FREEGLUT_SEARCH_DIRS} ${OPENGL_INCLUDE_DIR}") - find_path(GLUT_INCLUDE_DIRS GL/glut.h PATH_SUFFIXES include HINTS ${GLUT_HINT_DIRS}) - find_library(GLUT_LIBRARY freeglut PATH_SUFFIXES lib HINTS ${GLUT_HINT_DIRS}) + find_path(GLUT_INCLUDE_DIRS GL/glut.h PATH_SUFFIXES include HINTS ${FREEGLUT_SEARCH_DIRS}) + find_library(GLUT_LIBRARY freeglut PATH_SUFFIXES lib HINTS ${FREEGLUT_SEARCH_DIRS}) else () - find_path(GLUT_INCLUDE_DIRS GL/glut.h PATH_SUFFIXES include HINTS ${GLUT_HINT_DIRS}) - find_library(GLUT_LIBRARY glut PATH_SUFFIXES lib HINTS ${GLUT_HINT_DIRS}) + find_path(GLUT_INCLUDE_DIRS GL/glut.h PATH_SUFFIXES include HINTS ${FREEGLUT_SEARCH_DIRS}) + find_library(GLUT_LIBRARY glut PATH_SUFFIXES lib HINTS ${FREEGLUT_SEARCH_DIRS}) endif () include(FindPackageHandleStandardArgs) @@ -35,12 +36,12 @@ include(FindPackageHandleStandardArgs) set(GLUT_LIBRARIES "${GLUT_LIBRARY}" "${XMU_LIBRARY}" "${XI_LIBRARY}") if (UNIX) - find_library(XI_LIBRARY Xi PATH_SUFFIXES lib HINTS ${GLUT_HINT_DIRS}) - find_library(XMU_LIBRARY Xmu PATH_SUFFIXES lib HINTS ${GLUT_HINT_DIRS}) + find_library(XI_LIBRARY Xi PATH_SUFFIXES lib HINTS ${FREEGLUT_SEARCH_DIRS}) + find_library(XMU_LIBRARY Xmu PATH_SUFFIXES lib HINTS ${FREEGLUT_SEARCH_DIRS}) find_package_handle_standard_args(GLUT DEFAULT_MSG GLUT_INCLUDE_DIRS GLUT_LIBRARIES XI_LIBRARY XMU_LIBRARY) else () find_package_handle_standard_args(GLUT DEFAULT_MSG GLUT_INCLUDE_DIRS GLUT_LIBRARIES) endif () -mark_as_advanced(GLUT_INCLUDE_DIRS GLUT_LIBRARIES GLUT_LIBRARY XI_LIBRARY XMU_LIBRARY) \ No newline at end of file +mark_as_advanced(GLUT_INCLUDE_DIRS GLUT_LIBRARIES GLUT_LIBRARY XI_LIBRARY XMU_LIBRARY FREEGLUT_SEARCH_DIRS) \ No newline at end of file diff --git a/cmake/modules/FindLeapMotion.cmake b/cmake/modules/FindLeapMotion.cmake index 91ac080c30..a64fc22e48 100644 --- a/cmake/modules/FindLeapMotion.cmake +++ b/cmake/modules/FindLeapMotion.cmake @@ -12,24 +12,24 @@ # Copyright (c) 2014 High Fidelity # -set(LEAPMOTION_SEARCH_DIRS "${LEAPMOTION_ROOT_DIR}" "$ENV{HIFI_LIB_DIR}/leapmotion") +include("${MACRO_DIR}/HifiLibrarySearchHints.cmake") +hifi_library_search_hints("leapmotion") find_path(LEAPMOTION_INCLUDE_DIRS Leap.h PATH_SUFFIXES include HINTS ${LEAPMOTION_SEARCH_DIRS}) if (WIN32) - find_library(LEAPMOTION_LIBRARY_DEBUG "lib/x86/Leapd.lib" HINTS ${LEAPMOTION_SEARCH_DIRS}) - find_library(LEAPMOTION_LIBRARY_RELEASE "lib/x86/Leap.lib" HINTS ${LEAPMOTION_SEARCH_DIRS}) -endif (WIN32) -if (APPLE) - find_library(LEAPMOTION_LIBRARY_RELEASE "lib/libLeap.dylib" HINTS ${LEAPMOTION_SEARCH_DIRS}) -endif (APPLE) + find_library(LEAPMOTION_LIBRARY_DEBUG Leapd PATH_SUFFIXES lib/x86 HINTS ${LEAPMOTION_SEARCH_DIRS}) + find_library(LEAPMOTION_LIBRARY_RELEASE Leap PATH_SUFFIXES lib/x86 HINTS ${LEAPMOTION_SEARCH_DIRS}) +elseif (APPLE) + find_library(LEAPMOTION_LIBRARY_RELEASE Leap PATH_SUFFIXES lib HINTS ${LEAPMOTION_SEARCH_DIRS}) +endif () include(SelectLibraryConfigurations) select_library_configurations(LEAPMOTION) -set(LEAPMOTION_LIBRARIES "${LEAPMOTION_LIBARIES}") +set(LEAPMOTION_LIBRARIES "${LEAPMOTION_LIBRARY}") include(FindPackageHandleStandardArgs) find_package_handle_standard_args(LEAPMOTION DEFAULT_MSG LEAPMOTION_INCLUDE_DIRS LEAPMOTION_LIBRARIES) -mark_as_advanced(LEAPMOTION_INCLUDE_DIRS LEAPMOTION_LIBRARIES) +mark_as_advanced(LEAPMOTION_INCLUDE_DIRS LEAPMOTION_LIBRARIES LEAPMOTION_SEARCH_DIRS) diff --git a/cmake/modules/FindLibOVR.cmake b/cmake/modules/FindLibOVR.cmake index 738a30519c..786a24a06b 100644 --- a/cmake/modules/FindLibOVR.cmake +++ b/cmake/modules/FindLibOVR.cmake @@ -18,10 +18,11 @@ # See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html # -set(LIBOVR_SEARCH_DIRS "${LIBOVR_ROOT_DIR}" "$ENV{LIBOVR_ROOT_DIR}" "$ENV{HIFI_LIB_DIR}/oculus") +include("${MACRO_DIR}/HifiLibrarySearchHints.cmake") +hifi_library_search_hints("oculus") -find_path(LIBOVR_INCLUDE_DIRS OVR.h PATH_SUFFIXES Include HINTS ${LIBOVR_SEARCH_DIRS}) -find_path(LIBOVR_UTIL_INCLUDE_DIR Util_Render_Stereo.h PATH_SUFFIXES Src/Util HINTS ${LIBOVR_SEARCH_DIRS}) +find_path(LIBOVR_INCLUDE_DIRS OVR.h PATH_SUFFIXES Include HINTS ${OCULUS_SEARCH_DIRS}) +find_path(LIBOVR_UTIL_INCLUDE_DIR Util_Render_Stereo.h PATH_SUFFIXES Src/Util HINTS ${OCULUS_SEARCH_DIRS}) # add the util include dir to the general include dirs set(LIBOVR_INCLUDE_DIRS "${LIBOVR_INCLUDE_DIRS}" "${LIBOVR_UTIL_INCLUDE_DIR}") @@ -29,8 +30,8 @@ set(LIBOVR_INCLUDE_DIRS "${LIBOVR_INCLUDE_DIRS}" "${LIBOVR_UTIL_INCLUDE_DIR}") include(SelectLibraryConfigurations) if (APPLE) - find_library(LIBOVR_LIBRARY_DEBUG "Lib/MacOS/Debug/libovr.a" HINTS ${LIBOVR_SEARCH_DIRS}) - find_library(LIBOVR_LIBRARY_RELEASE "Lib/MacOS/Release/libovr.a" HINTS ${LIBOVR_SEARCH_DIRS}) + find_library(LIBOVR_LIBRARY_DEBUG "Lib/MacOS/Debug/libovr.a" HINTS ${OCULUS_SEARCH_DIRS}) + find_library(LIBOVR_LIBRARY_RELEASE "Lib/MacOS/Release/libovr.a" HINTS ${OCULUS_SEARCH_DIRS}) elseif (UNIX) find_library(UDEV_LIBRARY_RELEASE udev /usr/lib/x86_64-linux-gnu/) find_library(XINERAMA_LIBRARY_RELEASE Xinerama /usr/lib/x86_64-linux-gnu/) @@ -41,15 +42,15 @@ elseif (UNIX) set(LINUX_ARCH_DIR "x86_64") endif() - find_library(LIBOVR_LIBRARY_DEBUG "Lib/Linux/Debug/${LINUX_ARCH_DIR}/libovr.a" HINTS ${LIBOVR_SEARCH_DIRS}) - find_library(LIBOVR_LIBRARY_RELEASE "Lib/Linux/Release/${LINUX_ARCH_DIR}/libovr.a" HINTS ${LIBOVR_SEARCH_DIRS}) + find_library(LIBOVR_LIBRARY_DEBUG "Lib/Linux/Debug/${LINUX_ARCH_DIR}/libovr.a" HINTS ${OCULUS_SEARCH_DIRS}) + find_library(LIBOVR_LIBRARY_RELEASE "Lib/Linux/Release/${LINUX_ARCH_DIR}/libovr.a" HINTS ${OCULUS_SEARCH_DIRS}) select_library_configurations(UDEV) select_library_configurations(XINERAMA) elseif (WIN32) - find_library(LIBOVR_LIBRARY_DEBUG "Lib/Win32/libovrd.lib" HINTS ${LIBOVR_SEARCH_DIRS}) - find_library(LIBOVR_LIBRARY_RELEASE "Lib/Win32/libovr.lib" HINTS ${LIBOVR_SEARCH_DIRS}) + find_library(LIBOVR_LIBRARY_DEBUG "Lib/Win32/libovrd.lib" HINTS ${OCULUS_SEARCH_DIRS}) + find_library(LIBOVR_LIBRARY_RELEASE "Lib/Win32/libovr.lib" HINTS ${OCULUS_SEARCH_DIRS}) endif () select_library_configurations(LIBOVR) @@ -63,4 +64,4 @@ elseif () find_package_handle_standard_args(LIBOVR DEFAULT_MSG LIBOVR_INCLUDE_DIRS LIBOVR_UTIL_INCLUDE_DIR LIBOVR_LIBRARIES UDEV_LIBRARY XINERAMA_LIBRARY) endif () -mark_as_advanced(LIBOVR_INCLUDE_DIRS LIBOVR_LIBRARIES) +mark_as_advanced(LIBOVR_INCLUDE_DIRS LIBOVR_LIBRARIES OCULUS_SEARCH_DIRS) diff --git a/cmake/modules/FindOpenSSL.cmake b/cmake/modules/FindOpenSSL.cmake index 403bb4e246..16db0a16f0 100644 --- a/cmake/modules/FindOpenSSL.cmake +++ b/cmake/modules/FindOpenSSL.cmake @@ -46,7 +46,10 @@ if (WIN32) unset(_programfiles) set(_OPENSSL_ROOT_HINTS_AND_PATHS HINTS ${_OPENSSL_ROOT_HINTS} PATHS ${_OPENSSL_ROOT_PATHS}) else () - set(_OPENSSL_ROOT_HINTS_AND_PATHS ${OPENSSL_ROOT_DIR} $ENV{OPENSSL_ROOT_DIR} $ENV{HIFI_LIB_DIR}/openssl) + include("${MACRO_DIR}/HifiLibrarySearchHints.cmake") + hifi_library_search_hints("openssl") + + set(_OPENSSL_ROOT_HINTS_AND_PATHS ${OPENSSL_SEARCH_DIRS}) endif () find_path(OPENSSL_INCLUDE_DIR NAMES openssl/ssl.h HINTS ${_OPENSSL_ROOT_HINTS_AND_PATHS} ${_OPENSSL_INCLUDEDIR} PATH_SUFFIXES include) @@ -228,4 +231,4 @@ else () ) endif () -mark_as_advanced(OPENSSL_INCLUDE_DIR OPENSSL_LIBRARIES) +mark_as_advanced(OPENSSL_INCLUDE_DIR OPENSSL_LIBRARIES OPENSSL_SEARCH_DIRS) diff --git a/cmake/modules/FindQxmpp.cmake b/cmake/modules/FindQxmpp.cmake index 71c99369aa..e5b6e6506a 100644 --- a/cmake/modules/FindQxmpp.cmake +++ b/cmake/modules/FindQxmpp.cmake @@ -18,7 +18,8 @@ # See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html # -set(QXMPP_SEARCH_DIRS "${QXMPP_ROOT_DIR}" "$ENV{HIFI_LIB_DIR}/qxmpp") +include("${MACRO_DIR}/HifiLibrarySearchHints.cmake") +hifi_library_search_hints("qxmpp") find_path(QXMPP_INCLUDE_DIRS QXmppClient.h PATH_SUFFIXES include/qxmpp HINTS ${QXMPP_SEARCH_DIRS}) @@ -33,4 +34,4 @@ set(QXMPP_LIBRARIES "${QXMPP_LIBRARY}") include(FindPackageHandleStandardArgs) find_package_handle_standard_args(QXMPP DEFAULT_MSG QXMPP_INCLUDE_DIRS QXMPP_LIBRARIES) -mark_as_advanced(QXMPP_INCLUDE_DIRS QXMPP_LIBRARIES) \ No newline at end of file +mark_as_advanced(QXMPP_INCLUDE_DIRS QXMPP_LIBRARIES QXMPP_SEARCH_DIRS) \ No newline at end of file diff --git a/cmake/modules/FindRtMidi.cmake b/cmake/modules/FindRtMidi.cmake index d03477710c..93f1cc69cc 100644 --- a/cmake/modules/FindRtMidi.cmake +++ b/cmake/modules/FindRtMidi.cmake @@ -18,7 +18,8 @@ # See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html # -set(RTMIDI_SEARCH_DIRS "${RTMIDI_ROOT_DIR}" "$ENV{HIFI_LIB_DIR}/rtmidi") +include("${MACRO_DIR}/HifiLibrarySearchHints.cmake") +hifi_library_search_hints("rtmidi") find_path(RTMIDI_INCLUDE_DIRS RtMidi.h PATH_SUFFIXES include HINTS ${RTMIDI_SEARCH_DIRS}) find_library(RTMIDI_LIBRARIES NAMES rtmidi PATH_SUFFIXES lib HINTS ${RTMIDI_SEARCH_DIRS}) @@ -26,4 +27,4 @@ find_library(RTMIDI_LIBRARIES NAMES rtmidi PATH_SUFFIXES lib HINTS ${RTMIDI_SEAR include(FindPackageHandleStandardArgs) find_package_handle_standard_args(RTMIDI DEFAULT_MSG RTMIDI_INCLUDE_DIRS RTMIDI_LIBRARIES) -mark_as_advanced(RTMIDI_INCLUDE_DIRS RTMIDI_LIBRARIES) \ No newline at end of file +mark_as_advanced(RTMIDI_INCLUDE_DIRS RTMIDI_LIBRARIES RTMIDI_SEARCH_DIRS) \ No newline at end of file diff --git a/cmake/modules/FindSixense.cmake b/cmake/modules/FindSixense.cmake index 94d211fdac..a24131698b 100644 --- a/cmake/modules/FindSixense.cmake +++ b/cmake/modules/FindSixense.cmake @@ -18,7 +18,8 @@ # See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html # -set(SIXENSE_SEARCH_DIRS "${SIXENSE_ROOT_DIR}" "$ENV{HIFI_LIB_DIR}/sixense") +include("${MACRO_DIR}/HifiLibrarySearchHints.cmake") +hifi_library_search_hints("sixense") find_path(SIXENSE_INCLUDE_DIRS sixense.h PATH_SUFFIXES include HINTS ${SIXENSE_SEARCH_DIRS}) @@ -41,4 +42,4 @@ set(SIXENSE_LIBRARIES "${SIXENSE_LIBRARY}") include(FindPackageHandleStandardArgs) find_package_handle_standard_args(SIXENSE DEFAULT_MSG SIXENSE_INCLUDE_DIRS SIXENSE_LIBRARIES) -mark_as_advanced(SIXENSE_LIBRARIES SIXENSE_INCLUDE_DIRS) +mark_as_advanced(SIXENSE_LIBRARIES SIXENSE_INCLUDE_DIRS SIXENSE_SEARCH_DIRS) diff --git a/cmake/modules/FindVisage.cmake b/cmake/modules/FindVisage.cmake index 7e1d6e60b6..b461926604 100644 --- a/cmake/modules/FindVisage.cmake +++ b/cmake/modules/FindVisage.cmake @@ -18,7 +18,8 @@ # See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html # -set(VISAGE_SEARCH_DIRS "${VISAGE_ROOT_DIR}" "$ENV{VISAGE_ROOT_DIR}" "$ENV{HIFI_LIB_DIR}/visage") +include("${MACRO_DIR}/HifiLibrarySearchHints.cmake") +hifi_library_search_hints("visage") find_path(VISAGE_BASE_INCLUDE_DIR VisageTracker2.h PATH_SUFFIXES include HINTS ${VISAGE_SEARCH_DIRS}) @@ -53,5 +54,5 @@ set(VISAGE_LIBRARIES "${VISAGE_CORE_LIBRARY}" "${VISAGE_VISION_LIBRARY}" "${VISA mark_as_advanced( VISAGE_INCLUDE_DIRS VISAGE_LIBRARIES VISAGE_BASE_INCLUDE_DIR VISAGE_XML_INCLUDE_DIR VISAGE_OPENCV_INCLUDE_DIR VISAGE_OPENCV2_INCLUDE_DIR - VISAGE_CORE_LIBRARY VISAGE_VISION_LIBRARY VISAGE_OPENCV_LIBRARY + VISAGE_CORE_LIBRARY VISAGE_VISION_LIBRARY VISAGE_OPENCV_LIBRARY VISAGE_SEARCH_DIRS ) diff --git a/examples/bot_randomExpression.js b/examples/bot_randomExpression.js new file mode 100644 index 0000000000..2d2653e6d5 --- /dev/null +++ b/examples/bot_randomExpression.js @@ -0,0 +1,144 @@ +// +// bot_randomExpression.js +// examples +// +// Created by Ben Arnold on 7/23/14. +// Copyright 2014 High Fidelity, Inc. +// +// This is an example script that demonstrates an NPC avatar with +// random facial expressions. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +function getRandomFloat(min, max) { + return Math.random() * (max - min) + min; +} + +function getRandomInt (min, max) { + return Math.floor(Math.random() * (max - min + 1)) + min; +} + +function printVector(string, vector) { + print(string + " " + vector.x + ", " + vector.y + ", " + vector.z); +} + +var timePassed = 0.0; +var updateSpeed = 3.0; + +var X_MIN = 5.0; +var X_MAX = 15.0; +var Z_MIN = 5.0; +var Z_MAX = 15.0; +var Y_PELVIS = 1.0; + +// pick an integer between 1 and 100 for the body model for this bot +botNumber = getRandomInt(1, 100); + +newFaceFilePrefix = "ron"; + +newBodyFilePrefix = "bot" + botNumber; + +// set the face model fst using the bot number +// there is no need to change the body model - we're using the default +Avatar.faceModelURL = "https://s3-us-west-1.amazonaws.com/highfidelity-public/meshes/" + newFaceFilePrefix + ".fst"; +Avatar.skeletonModelURL = "https://s3-us-west-1.amazonaws.com/highfidelity-public/meshes/" + newBodyFilePrefix + ".fst"; +Avatar.billboardURL = "https://s3-us-west-1.amazonaws.com/highfidelity-public/meshes/billboards/bot" + botNumber + ".png"; + +Agent.isAvatar = true; +Agent.isListeningToAudioStream = true; + +// change the avatar's position to the random one +Avatar.position = { x: getRandomFloat(X_MIN, X_MAX), y: Y_PELVIS, z: getRandomFloat(Z_MIN, Z_MAX) };; +printVector("New bot, position = ", Avatar.position); + +var allBlendShapes = []; +var targetBlendCoefficient = []; +var currentBlendCoefficient = []; + +function addBlendShape(s) { + allBlendShapes[allBlendShapes.length] = s; +} + +//It is imperative that the following blendshapes are all present and are in the correct order +addBlendShape("EyeBlink_L"); +addBlendShape("EyeBlink_R"); +addBlendShape("EyeSquint_L"); +addBlendShape("EyeSquint_R"); +addBlendShape("EyeDown_L"); +addBlendShape("EyeDown_R"); +addBlendShape("EyeIn_L"); +addBlendShape("EyeIn_R"); +addBlendShape("EyeOpen_L"); +addBlendShape("EyeOpen_R"); +addBlendShape("EyeOut_L"); +addBlendShape("EyeOut_R"); +addBlendShape("EyeUp_L"); +addBlendShape("EyeUp_R"); +addBlendShape("BrowsD_L"); +addBlendShape("BrowsD_R"); +addBlendShape("BrowsU_C"); +addBlendShape("BrowsU_L"); +addBlendShape("BrowsU_R"); +addBlendShape("JawFwd"); +addBlendShape("JawLeft"); +addBlendShape("JawOpen"); +addBlendShape("JawChew"); +addBlendShape("JawRight"); +addBlendShape("MouthLeft"); +addBlendShape("MouthRight"); +addBlendShape("MouthFrown_L"); +addBlendShape("MouthFrown_R"); +addBlendShape("MouthSmile_L"); +addBlendShape("MouthSmile_R"); +addBlendShape("MouthDimple_L"); +addBlendShape("MouthDimple_R"); +addBlendShape("LipsStretch_L"); +addBlendShape("LipsStretch_R"); +addBlendShape("LipsUpperClose"); +addBlendShape("LipsLowerClose"); +addBlendShape("LipsUpperUp"); +addBlendShape("LipsLowerDown"); +addBlendShape("LipsUpperOpen"); +addBlendShape("LipsLowerOpen"); +addBlendShape("LipsFunnel"); +addBlendShape("LipsPucker"); +addBlendShape("ChinLowerRaise"); +addBlendShape("ChinUpperRaise"); +addBlendShape("Sneer"); +addBlendShape("Puff"); +addBlendShape("CheekSquint_L"); +addBlendShape("CheekSquint_R"); + +for (var i = 0; i < allBlendShapes.length; i++) { + targetBlendCoefficient[i] = 0; + currentBlendCoefficient[i] = 0; +} + +function setRandomExpression() { + for (var i = 0; i < allBlendShapes.length; i++) { + targetBlendCoefficient[i] = Math.random(); + } +} + +var expressionChangeSpeed = 0.1; + +function updateBlendShapes(deltaTime) { + + for (var i = 0; i < allBlendShapes.length; i++) { + currentBlendCoefficient[i] += (targetBlendCoefficient[i] - currentBlendCoefficient[i]) * expressionChangeSpeed; + Avatar.setBlendshape(allBlendShapes[i], currentBlendCoefficient[i]); + } +} + +function update(deltaTime) { + timePassed += deltaTime; + if (timePassed > updateSpeed) { + timePassed = 0; + setRandomExpression(); + } + updateBlendShapes(deltaTime); +} + +Script.update.connect(update); \ No newline at end of file diff --git a/examples/editModels.js b/examples/editModels.js index 458ddf7b4a..d430800748 100644 --- a/examples/editModels.js +++ b/examples/editModels.js @@ -748,18 +748,19 @@ function Tooltip() { this.updateText = function(properties) { var angles = Quat.safeEulerAngles(properties.modelRotation); var text = "Model Properties:\n" - text += "x: " + properties.position.x.toFixed(this.decimals) + "\n" - text += "y: " + properties.position.y.toFixed(this.decimals) + "\n" - text += "z: " + properties.position.z.toFixed(this.decimals) + "\n" - text += "pitch: " + angles.x.toFixed(this.decimals) + "\n" - text += "yaw: " + angles.y.toFixed(this.decimals) + "\n" - text += "roll: " + angles.z.toFixed(this.decimals) + "\n" + text += "X: " + properties.position.x.toFixed(this.decimals) + "\n" + text += "Y: " + properties.position.y.toFixed(this.decimals) + "\n" + text += "Z: " + properties.position.z.toFixed(this.decimals) + "\n" + text += "Pitch: " + angles.x.toFixed(this.decimals) + "\n" + text += "Yaw: " + angles.y.toFixed(this.decimals) + "\n" + text += "Roll: " + angles.z.toFixed(this.decimals) + "\n" text += "Scale: " + 2 * properties.radius.toFixed(this.decimals) + "\n" text += "ID: " + properties.id + "\n" - text += "model url: " + properties.modelURL + "\n" - text += "animation url: " + properties.animationURL + "\n" + text += "Model URL: " + properties.modelURL + "\n" + text += "Animation URL: " + properties.animationURL + "\n" + text += "Animation is playing: " + properties.animationIsPlaying + "\n" if (properties.sittingPoints.length > 0) { - text += properties.sittingPoints.length + " sitting points: " + text += properties.sittingPoints.length + " Sitting points: " for (var i = 0; i < properties.sittingPoints.length; ++i) { text += properties.sittingPoints[i].name + " " } @@ -804,7 +805,7 @@ function mousePressEvent(event) { } } else if (browser == toolBar.clicked(clickedOverlay)) { - var url = Window.s3Browse(); + var url = Window.s3Browse(".*(fbx|FBX)"); if (url == null || url == "") { return; } @@ -1146,6 +1147,7 @@ function handeMenuEvent(menuItem){ var decimals = 3; array.push({ label: "Model URL:", value: selectedModelProperties.modelURL }); array.push({ label: "Animation URL:", value: selectedModelProperties.animationURL }); + array.push({ label: "Animation is playing:", value: selectedModelProperties.animationIsPlaying }); array.push({ label: "X:", value: selectedModelProperties.position.x.toFixed(decimals) }); array.push({ label: "Y:", value: selectedModelProperties.position.y.toFixed(decimals) }); array.push({ label: "Z:", value: selectedModelProperties.position.z.toFixed(decimals) }); @@ -1158,17 +1160,18 @@ function handeMenuEvent(menuItem){ var propertyName = Window.form("Edit Properties", array); modelSelected = false; - selectedModelProperties.modelURL = array[0].value; - selectedModelProperties.animationURL = array[1].value; - selectedModelProperties.position.x = array[2].value; - selectedModelProperties.position.y = array[3].value; - selectedModelProperties.position.z = array[4].value; - angles.x = array[5].value; - angles.y = array[6].value; - angles.z = array[7].value; + var index = 0; + selectedModelProperties.modelURL = array[index++].value; + selectedModelProperties.animationURL = array[index++].value; + selectedModelProperties.animationIsPlaying = array[index++].value; + selectedModelProperties.position.x = array[index++].value; + selectedModelProperties.position.y = array[index++].value; + selectedModelProperties.position.z = array[index++].value; + angles.x = array[index++].value; + angles.y = array[index++].value; + angles.z = array[index++].value; selectedModelProperties.modelRotation = Quat.fromVec3Degrees(angles); - selectedModelProperties.radius = array[8].value / 2; - print(selectedModelProperties.radius); + selectedModelProperties.radius = array[index++].value / 2; Models.editModel(selectedModelID, selectedModelProperties); } @@ -1201,6 +1204,7 @@ Controller.keyPressEvent.connect(function(event) { somethingChanged = true; } }); + Controller.keyReleaseEvent.connect(function(event) { if (event.text == "z" || event.text == "Z") { zIsPressed = false; diff --git a/examples/editVoxels.js b/examples/editVoxels.js index 8d2a516f55..77cec87b15 100644 --- a/examples/editVoxels.js +++ b/examples/editVoxels.js @@ -37,7 +37,7 @@ var WHITE_COLOR = { red: 255, green: 255, blue: 255 }; var MAX_PASTE_VOXEL_SCALE = 256; var MIN_PASTE_VOXEL_SCALE = .256; -var zFightingSizeAdjust = 0.002; // used to adjust preview voxels to prevent z fighting +var zFightingSizeAdjustRatio = 0.004; // used to adjust preview voxels to prevent z fighting var previewLineWidth = 1.5; var inspectJsIsRunning = false; @@ -51,7 +51,6 @@ var lastVoxelScale = 0; var dragStart = { x: 0, y: 0 }; var wheelPixelsMoved = 0; - var mouseX = 0; var mouseY = 0; @@ -168,7 +167,16 @@ var voxelPreview = Overlays.addOverlay("cube", { lineWidth: 4 }); -var linePreviewTop = Overlays.addOverlay("line3d", { +var linePreviewTop = []; +var linePreviewBottom = []; +var linePreviewLeft = []; +var linePreviewRight = []; + +// Currend cursor index +var currentCursor = 0; + +function addLineOverlay() { + return Overlays.addOverlay("line3d", { position: { x: 0, y: 0, z: 0}, end: { x: 0, y: 0, z: 0}, color: { red: 255, green: 255, blue: 255}, @@ -176,34 +184,24 @@ var linePreviewTop = Overlays.addOverlay("line3d", { visible: false, lineWidth: previewLineWidth }); +} + +//Cursor line previews for up to three cursors +linePreviewTop[0] = addLineOverlay(); +linePreviewTop[1] = addLineOverlay(); +linePreviewTop[2] = addLineOverlay(); -var linePreviewBottom = Overlays.addOverlay("line3d", { - position: { x: 0, y: 0, z: 0}, - end: { x: 0, y: 0, z: 0}, - color: { red: 255, green: 255, blue: 255}, - alpha: 1, - visible: false, - lineWidth: previewLineWidth - }); - -var linePreviewLeft = Overlays.addOverlay("line3d", { - position: { x: 0, y: 0, z: 0}, - end: { x: 0, y: 0, z: 0}, - color: { red: 255, green: 255, blue: 255}, - alpha: 1, - visible: false, - lineWidth: previewLineWidth - }); - -var linePreviewRight = Overlays.addOverlay("line3d", { - position: { x: 0, y: 0, z: 0}, - end: { x: 0, y: 0, z: 0}, - color: { red: 255, green: 255, blue: 255}, - alpha: 1, - visible: false, - lineWidth: previewLineWidth - }); - +linePreviewBottom[0] = addLineOverlay(); +linePreviewBottom[1] = addLineOverlay(); +linePreviewBottom[2] = addLineOverlay(); + +linePreviewLeft[0] = addLineOverlay(); +linePreviewLeft[1] = addLineOverlay(); +linePreviewLeft[2] = addLineOverlay(); + +linePreviewRight[0] = addLineOverlay(); +linePreviewRight[1] = addLineOverlay(); +linePreviewRight[2] = addLineOverlay(); // these will be used below var scaleSelectorWidth = 144; @@ -698,79 +696,99 @@ function calculateVoxelFromIntersection(intersection, operation) { if (wantDebug) { print("wantAddAdjust="+wantAddAdjust); } + + var zFightingSizeAdjust = zFightingSizeAdjustRatio * intersection.distance; // now we also want to calculate the "edge square" for the face for this voxel if (intersection.face == "MIN_X_FACE") { - + highlightAt.x = x - zFightingSizeAdjust; + highlightAt.y = y + zFightingSizeAdjust; + highlightAt.z = z + zFightingSizeAdjust; + voxelSize -= 2 * zFightingSizeAdjust; if (wantAddAdjust) { resultVoxel.x -= voxelSize; } - resultVoxel.bottomLeft = {x: highlightAt.x, y: highlightAt.y + zFightingSizeAdjust, z: highlightAt.z + zFightingSizeAdjust }; - resultVoxel.bottomRight = {x: highlightAt.x, y: highlightAt.y + zFightingSizeAdjust, z: highlightAt.z + voxelSize - zFightingSizeAdjust }; - resultVoxel.topLeft = {x: highlightAt.x, y: highlightAt.y + voxelSize - zFightingSizeAdjust, z: highlightAt.z + zFightingSizeAdjust }; - resultVoxel.topRight = {x: highlightAt.x, y: highlightAt.y + voxelSize - zFightingSizeAdjust, z: highlightAt.z + voxelSize - zFightingSizeAdjust }; + resultVoxel.bottomLeft = {x: highlightAt.x, y: highlightAt.y, z: highlightAt.z }; + resultVoxel.bottomRight = {x: highlightAt.x, y: highlightAt.y, z: highlightAt.z + voxelSize }; + resultVoxel.topLeft = {x: highlightAt.x, y: highlightAt.y + voxelSize, z: highlightAt.z }; + resultVoxel.topRight = {x: highlightAt.x, y: highlightAt.y + voxelSize, z: highlightAt.z + voxelSize }; } else if (intersection.face == "MAX_X_FACE") { highlightAt.x = x + voxelSize + zFightingSizeAdjust; + highlightAt.y = y + zFightingSizeAdjust; + highlightAt.z = z + zFightingSizeAdjust; + voxelSize -= 2 * zFightingSizeAdjust; if (wantAddAdjust) { resultVoxel.x += resultVoxel.s; } - resultVoxel.bottomRight = {x: highlightAt.x, y: highlightAt.y + zFightingSizeAdjust, z: highlightAt.z + zFightingSizeAdjust }; - resultVoxel.bottomLeft = {x: highlightAt.x, y: highlightAt.y + zFightingSizeAdjust, z: highlightAt.z + voxelSize - zFightingSizeAdjust }; - resultVoxel.topRight = {x: highlightAt.x, y: highlightAt.y + voxelSize - zFightingSizeAdjust, z: highlightAt.z + zFightingSizeAdjust }; - resultVoxel.topLeft = {x: highlightAt.x, y: highlightAt.y + voxelSize - zFightingSizeAdjust, z: highlightAt.z + voxelSize - zFightingSizeAdjust }; + resultVoxel.bottomRight = {x: highlightAt.x, y: highlightAt.y, z: highlightAt.z }; + resultVoxel.bottomLeft = {x: highlightAt.x, y: highlightAt.y, z: highlightAt.z + voxelSize }; + resultVoxel.topRight = {x: highlightAt.x, y: highlightAt.y + voxelSize, z: highlightAt.z }; + resultVoxel.topLeft = {x: highlightAt.x, y: highlightAt.y + voxelSize, z: highlightAt.z + voxelSize }; } else if (intersection.face == "MIN_Y_FACE") { + highlightAt.x = x + zFightingSizeAdjust; highlightAt.y = y - zFightingSizeAdjust; + highlightAt.z = z + zFightingSizeAdjust; + voxelSize -= 2 * zFightingSizeAdjust; if (wantAddAdjust) { resultVoxel.y -= voxelSize; } - resultVoxel.topRight = {x: highlightAt.x + zFightingSizeAdjust , y: highlightAt.y, z: highlightAt.z + zFightingSizeAdjust }; - resultVoxel.topLeft = {x: highlightAt.x + voxelSize - zFightingSizeAdjust, y: highlightAt.y, z: highlightAt.z + zFightingSizeAdjust }; - resultVoxel.bottomRight = {x: highlightAt.x + zFightingSizeAdjust , y: highlightAt.y, z: highlightAt.z + voxelSize - zFightingSizeAdjust }; - resultVoxel.bottomLeft = {x: highlightAt.x + voxelSize - zFightingSizeAdjust , y: highlightAt.y, z: highlightAt.z + voxelSize - zFightingSizeAdjust }; + resultVoxel.topRight = {x: highlightAt.x , y: highlightAt.y, z: highlightAt.z }; + resultVoxel.topLeft = {x: highlightAt.x + voxelSize, y: highlightAt.y, z: highlightAt.z }; + resultVoxel.bottomRight = {x: highlightAt.x , y: highlightAt.y, z: highlightAt.z + voxelSize }; + resultVoxel.bottomLeft = {x: highlightAt.x + voxelSize , y: highlightAt.y, z: highlightAt.z + voxelSize }; } else if (intersection.face == "MAX_Y_FACE") { + highlightAt.x = x + zFightingSizeAdjust; highlightAt.y = y + voxelSize + zFightingSizeAdjust; + highlightAt.z = z + zFightingSizeAdjust; + voxelSize -= 2 * zFightingSizeAdjust; if (wantAddAdjust) { resultVoxel.y += voxelSize; } - resultVoxel.bottomRight = {x: highlightAt.x + zFightingSizeAdjust, y: highlightAt.y, z: highlightAt.z + zFightingSizeAdjust }; - resultVoxel.bottomLeft = {x: highlightAt.x + voxelSize - zFightingSizeAdjust, y: highlightAt.y, z: highlightAt.z + zFightingSizeAdjust}; - resultVoxel.topRight = {x: highlightAt.x + zFightingSizeAdjust, y: highlightAt.y, z: highlightAt.z + voxelSize - zFightingSizeAdjust}; - resultVoxel.topLeft = {x: highlightAt.x + voxelSize - zFightingSizeAdjust, y: highlightAt.y, z: highlightAt.z + voxelSize - zFightingSizeAdjust}; + resultVoxel.bottomRight = {x: highlightAt.x, y: highlightAt.y, z: highlightAt.z }; + resultVoxel.bottomLeft = {x: highlightAt.x + voxelSize, y: highlightAt.y, z: highlightAt.z}; + resultVoxel.topRight = {x: highlightAt.x, y: highlightAt.y, z: highlightAt.z + voxelSize}; + resultVoxel.topLeft = {x: highlightAt.x + voxelSize, y: highlightAt.y, z: highlightAt.z + voxelSize}; } else if (intersection.face == "MIN_Z_FACE") { + highlightAt.x = x + zFightingSizeAdjust; + highlightAt.y = y + zFightingSizeAdjust; highlightAt.z = z - zFightingSizeAdjust; + voxelSize -= 2 * zFightingSizeAdjust; if (wantAddAdjust) { resultVoxel.z -= voxelSize; } - resultVoxel.bottomRight = {x: highlightAt.x + zFightingSizeAdjust, y: highlightAt.y + zFightingSizeAdjust, z: highlightAt.z }; - resultVoxel.bottomLeft = {x: highlightAt.x + voxelSize - zFightingSizeAdjust, y: highlightAt.y + zFightingSizeAdjust, z: highlightAt.z}; - resultVoxel.topRight = {x: highlightAt.x + zFightingSizeAdjust, y: highlightAt.y + voxelSize - zFightingSizeAdjust, z: highlightAt.z }; - resultVoxel.topLeft = {x: highlightAt.x + voxelSize - zFightingSizeAdjust, y: highlightAt.y + voxelSize - zFightingSizeAdjust, z: highlightAt.z}; + resultVoxel.bottomRight = {x: highlightAt.x, y: highlightAt.y, z: highlightAt.z }; + resultVoxel.bottomLeft = {x: highlightAt.x + voxelSize, y: highlightAt.y, z: highlightAt.z}; + resultVoxel.topRight = {x: highlightAt.x, y: highlightAt.y + voxelSize, z: highlightAt.z }; + resultVoxel.topLeft = {x: highlightAt.x + voxelSize, y: highlightAt.y + voxelSize, z: highlightAt.z}; } else if (intersection.face == "MAX_Z_FACE") { + highlightAt.x = x + zFightingSizeAdjust; + highlightAt.y = y + zFightingSizeAdjust; highlightAt.z = z + voxelSize + zFightingSizeAdjust; + voxelSize -= 2 * zFightingSizeAdjust; if (wantAddAdjust) { resultVoxel.z += voxelSize; } - resultVoxel.bottomLeft = {x: highlightAt.x + zFightingSizeAdjust, y: highlightAt.y + zFightingSizeAdjust, z: highlightAt.z }; - resultVoxel.bottomRight = {x: highlightAt.x + voxelSize - zFightingSizeAdjust, y: highlightAt.y + zFightingSizeAdjust, z: highlightAt.z}; - resultVoxel.topLeft = {x: highlightAt.x + zFightingSizeAdjust, y: highlightAt.y + voxelSize - zFightingSizeAdjust, z: highlightAt.z }; - resultVoxel.topRight = {x: highlightAt.x + voxelSize - zFightingSizeAdjust, y: highlightAt.y + voxelSize - zFightingSizeAdjust, z: highlightAt.z}; + resultVoxel.bottomLeft = {x: highlightAt.x, y: highlightAt.y, z: highlightAt.z }; + resultVoxel.bottomRight = {x: highlightAt.x + voxelSize, y: highlightAt.y, z: highlightAt.z}; + resultVoxel.topLeft = {x: highlightAt.x, y: highlightAt.y + voxelSize, z: highlightAt.z }; + resultVoxel.topRight = {x: highlightAt.x + voxelSize, y: highlightAt.y + voxelSize, z: highlightAt.z}; } @@ -809,21 +827,21 @@ function showPreviewLines() { var pasteVoxel = getNewPasteVoxel(pickRay); // X axis - Overlays.editOverlay(linePreviewBottom, { + Overlays.editOverlay(linePreviewBottom[currentCursor], { position: pasteVoxel.origin, end: {x: pasteVoxel.origin.x + pasteVoxel.voxelSize, y: pasteVoxel.origin.y, z: pasteVoxel.origin.z }, visible: true }); // Y axis - Overlays.editOverlay(linePreviewRight, { + Overlays.editOverlay(linePreviewRight[currentCursor], { position: pasteVoxel.origin, end: {x: pasteVoxel.origin.x, y: pasteVoxel.origin.y + pasteVoxel.voxelSize, z: pasteVoxel.origin.z }, visible: true }); // Z axis - Overlays.editOverlay(linePreviewTop, { + Overlays.editOverlay(linePreviewTop[currentCursor], { position: pasteVoxel.origin, end: {x: pasteVoxel.origin.x, y: pasteVoxel.origin.y, z: pasteVoxel.origin.z - pasteVoxel.voxelSize }, visible: true @@ -837,22 +855,22 @@ function showPreviewLines() { if (intersection.intersects) { resultVoxel = calculateVoxelFromIntersection(intersection,""); Overlays.editOverlay(voxelPreview, { visible: false }); - Overlays.editOverlay(linePreviewTop, { position: resultVoxel.topLeft, end: resultVoxel.topRight, visible: true }); - Overlays.editOverlay(linePreviewBottom, { position: resultVoxel.bottomLeft, end: resultVoxel.bottomRight, visible: true }); - Overlays.editOverlay(linePreviewLeft, { position: resultVoxel.topLeft, end: resultVoxel.bottomLeft, visible: true }); - Overlays.editOverlay(linePreviewRight, { position: resultVoxel.topRight, end: resultVoxel.bottomRight, visible: true }); + Overlays.editOverlay(linePreviewTop[currentCursor], { position: resultVoxel.topLeft, end: resultVoxel.topRight, visible: true }); + Overlays.editOverlay(linePreviewBottom[currentCursor], { position: resultVoxel.bottomLeft, end: resultVoxel.bottomRight, visible: true }); + Overlays.editOverlay(linePreviewLeft[currentCursor], { position: resultVoxel.topLeft, end: resultVoxel.bottomLeft, visible: true }); + Overlays.editOverlay(linePreviewRight[currentCursor], { position: resultVoxel.topRight, end: resultVoxel.bottomRight, visible: true }); colors[0] = {red: intersection.voxel.red, green: intersection.voxel.green , blue: intersection.voxel.blue }; if (copyScale) { scaleSelector.setScale(intersection.voxel.s); } moveTools(); - } else { + } else if (intersection.accurate) { Overlays.editOverlay(voxelPreview, { visible: false }); - Overlays.editOverlay(linePreviewTop, { visible: false }); - Overlays.editOverlay(linePreviewBottom, { visible: false }); - Overlays.editOverlay(linePreviewLeft, { visible: false }); - Overlays.editOverlay(linePreviewRight, { visible: false }); + Overlays.editOverlay(linePreviewTop[currentCursor], { visible: false }); + Overlays.editOverlay(linePreviewBottom[currentCursor], { visible: false }); + Overlays.editOverlay(linePreviewLeft[currentCursor], { visible: false }); + Overlays.editOverlay(linePreviewRight[currentCursor], { visible: false }); } } @@ -862,20 +880,20 @@ function showPreviewGuides() { showPreviewVoxel(); // make sure alternative is hidden - Overlays.editOverlay(linePreviewTop, { visible: false }); - Overlays.editOverlay(linePreviewBottom, { visible: false }); - Overlays.editOverlay(linePreviewLeft, { visible: false }); - Overlays.editOverlay(linePreviewRight, { visible: false }); + Overlays.editOverlay(linePreviewTop[currentCursor], { visible: false }); + Overlays.editOverlay(linePreviewBottom[currentCursor], { visible: false }); + Overlays.editOverlay(linePreviewLeft[currentCursor], { visible: false }); + Overlays.editOverlay(linePreviewRight[currentCursor], { visible: false }); } else { showPreviewLines(); } } else { // make sure all previews are off Overlays.editOverlay(voxelPreview, { visible: false }); - Overlays.editOverlay(linePreviewTop, { visible: false }); - Overlays.editOverlay(linePreviewBottom, { visible: false }); - Overlays.editOverlay(linePreviewLeft, { visible: false }); - Overlays.editOverlay(linePreviewRight, { visible: false }); + Overlays.editOverlay(linePreviewTop[currentCursor], { visible: false }); + Overlays.editOverlay(linePreviewBottom[currentCursor], { visible: false }); + Overlays.editOverlay(linePreviewLeft[currentCursor], { visible: false }); + Overlays.editOverlay(linePreviewRight[currentCursor], { visible: false }); } } @@ -968,6 +986,14 @@ function mousePressEvent(event) { return; } + if (event.deviceID == 1500) { // Left Hydra Controller + currentCursor = 0; + } else if (event.deviceID == 1501) { // Right Hydra Controller + currentCursor = 1; + } else { + currentCursor = 2; + } + var clickedOnSomething = false; var clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y}); @@ -1220,11 +1246,16 @@ function menuItemEvent(menuItem) { } function mouseMoveEvent(event) { - if (!editToolsOn) { - return; + if (!editToolsOn || inspectJsIsRunning) { + return; } - if (inspectJsIsRunning) { - return; + + if (event.deviceID == 1500) { // Left Hydra Controller + currentCursor = 0; + } else if (event.deviceID == 1501) { // Right Hydra Controller + currentCursor = 1; + } else { + currentCursor = 2; } // Move Import Preview @@ -1475,10 +1506,12 @@ Controller.captureKeyEvents({ text: "-" }); function scriptEnding() { Overlays.deleteOverlay(voxelPreview); - Overlays.deleteOverlay(linePreviewTop); - Overlays.deleteOverlay(linePreviewBottom); - Overlays.deleteOverlay(linePreviewLeft); - Overlays.deleteOverlay(linePreviewRight); + for (var i = 0; i < linePreviewTop.length; i++) { + Overlays.deleteOverlay(linePreviewTop[i]); + Overlays.deleteOverlay(linePreviewBottom[i]); + Overlays.deleteOverlay(linePreviewLeft[i]); + Overlays.deleteOverlay(linePreviewRight[i]); + } for (s = 0; s < numColors; s++) { Overlays.deleteOverlay(swatches[s]); } diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index 138ae02b48..c549440334 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -12,15 +12,15 @@ project(${TARGET_NAME}) # setup for find modules set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../cmake/modules/") -set(FACEPLUS_ROOT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/external/faceplus") -set(FACESHIFT_ROOT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/external/faceshift") -set(LIBOVR_ROOT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/external/oculus") -set(PRIOVR_ROOT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/external/priovr") -set(SIXENSE_ROOT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/external/sixense") -set(VISAGE_ROOT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/external/visage") -set(LEAPMOTION_ROOT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/external/leapmotion") -set(RTMIDI_ROOT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/external/rtmidi") +# set a default root dir for each of our optional externals if it was not passed +set(OPTIONAL_EXTERNALS "faceplus" "faceshift" "oculus" "priovr" "sixense" "visage" "leapmotion" "rtmidi" "qxmpp") +foreach(EXTERNAL ${OPTIONAL_EXTERNALS}) + string(TOUPPER ${EXTERNAL} UPPER_EXTERNAL) + if (NOT ${UPPER_EXTERNAL}_ROOT_DIR) + set(${UPPER_EXTERNAL}_ROOT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/external/${EXTERNAL}") + endif () +endforeach() find_package(Qt5LinguistTools REQUIRED) find_package(Qt5LinguistToolsMacros) @@ -134,105 +134,54 @@ find_package(Qxmpp) find_package(RtMidi) find_package(OpenSSL REQUIRED) -# include the Sixense library for Razer Hydra if available -if (SIXENSE_FOUND AND NOT DISABLE_SIXENSE) - add_definitions(-DHAVE_SIXENSE) - include_directories(SYSTEM "${SIXENSE_INCLUDE_DIRS}") - if (APPLE OR UNIX) - SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -isystem ${SIXENSE_INCLUDE_DIRS}") - endif (APPLE OR UNIX) - target_link_libraries(${TARGET_NAME} ${SIXENSE_LIBRARIES}) -endif (SIXENSE_FOUND AND NOT DISABLE_SIXENSE) - -# likewise with Visage library for webcam feature tracking -if (VISAGE_FOUND AND NOT DISABLE_VISAGE) - add_definitions(-DHAVE_VISAGE -DVISAGE_STATIC) - include_directories(SYSTEM "${VISAGE_INCLUDE_DIRS}") - if (APPLE) - add_definitions(-DMAC_OS_X) - SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-comment") - include_directories(SYSTEM "${VISAGE_INCLUDE_DIRS}") - find_library(AVFoundation AVFoundation) - find_library(CoreMedia CoreMedia) - find_library(NEW_STD_LIBRARY libc++.dylib /usr/lib/) - target_link_libraries(${TARGET_NAME} ${AVFoundation} ${CoreMedia} ${NEW_STD_LIBRARY}) - endif (APPLE) - target_link_libraries(${TARGET_NAME} "${VISAGE_LIBRARIES}") -endif (VISAGE_FOUND AND NOT DISABLE_VISAGE) - -# and with Faceplus library, also for webcam feature tracking -if (FACEPLUS_FOUND AND NOT DISABLE_FACEPLUS) - add_definitions(-DHAVE_FACEPLUS) - include_directories(SYSTEM "${FACEPLUS_INCLUDE_DIRS}") - target_link_libraries(${TARGET_NAME} "${FACEPLUS_LIBRARIES}") -endif (FACEPLUS_FOUND AND NOT DISABLE_FACEPLUS) - -# and with LibOVR for Oculus Rift -if (LIBOVR_FOUND AND NOT DISABLE_LIBOVR) - add_definitions(-DHAVE_LIBOVR) - include_directories(SYSTEM ${LIBOVR_INCLUDE_DIRS}) +# perform standard include and linking for found externals +foreach(EXTERNAL ${OPTIONAL_EXTERNALS}) + string(TOUPPER ${EXTERNAL} UPPER_EXTERNAL) - if (APPLE) - foreach(LIBOVR_DIR ${LIBOVR_INCLUDE_DIRS}) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -isystem ${LIBOVR_DIR}") - endforeach() + if (${UPPER_EXTERNAL} MATCHES "OCULUS") + # the oculus directory is named OCULUS and not LIBOVR so hack to fix that here + set(UPPER_EXTERNAL "LIBOVR") endif () - target_link_libraries(${TARGET_NAME} ${LIBOVR_LIBRARIES}) -endif (LIBOVR_FOUND AND NOT DISABLE_LIBOVR) - -# and with PrioVR library -if (PRIOVR_FOUND AND NOT DISABLE_PRIOVR) - add_definitions(-DHAVE_PRIOVR) - include_directories(SYSTEM "${PRIOVR_INCLUDE_DIRS}") - target_link_libraries(${TARGET_NAME} "${PRIOVR_LIBRARIES}") -endif (PRIOVR_FOUND AND NOT DISABLE_PRIOVR) - -# and with LeapMotion library -if (LEAPMOTION_FOUND AND NOT DISABLE_LEAPMOTION) - add_definitions(-DHAVE_LEAPMOTION) - include_directories(SYSTEM "${LEAPMOTION_INCLUDE_DIRS}") - + if (${UPPER_EXTERNAL}_FOUND AND NOT DISABLE_${UPPER_EXTERNAL}) + add_definitions(-DHAVE_${UPPER_EXTERNAL}) + + # include the library directories (ignoring warnings) + include_directories(SYSTEM ${${UPPER_EXTERNAL}_INCLUDE_DIRS}) + + # perform the system include hack for OS X to ignore warnings if (APPLE) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -isystem ${LEAPMOTION_INCLUDE_DIRS}") + foreach(EXTERNAL_INCLUDE_DIR ${${UPPER_EXTERNAL}_INCLUDE_DIRS}) + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -isystem ${EXTERNAL_INCLUDE_DIR}") + endforeach() endif () - target_link_libraries(${TARGET_NAME} ${LEAPMOTION_LIBRARIES}) -endif (LEAPMOTION_FOUND AND NOT DISABLE_LEAPMOTION) + + target_link_libraries(${TARGET_NAME} ${${UPPER_EXTERNAL}_LIBRARIES}) + + endif () +endforeach() -# and with SDL for joysticks -if (SDL_FOUND AND NOT DISABLE_SDL) - add_definitions(-DHAVE_SDL) - include_directories(SYSTEM "${SDL_INCLUDE_DIR}") - target_link_libraries(${TARGET_NAME} "${SDL_LIBRARY}") -endif (SDL_FOUND AND NOT DISABLE_SDL) +# special APPLE modifications for Visage library +if (VISAGE_FOUND AND NOT DISABLE_VISAGE AND APPLE) + add_definitions(-DMAC_OS_X) + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-comment") + find_library(AVFoundation AVFoundation) + find_library(CoreMedia CoreMedia) + find_library(NEW_STD_LIBRARY libc++.dylib /usr/lib/) + target_link_libraries(${TARGET_NAME} ${AVFoundation} ${CoreMedia} ${NEW_STD_LIBRARY}) +endif () -# and with qxmpp for chat -if (QXMPP_FOUND AND NOT DISABLE_QXMPP) - add_definitions(-DHAVE_QXMPP -DQXMPP_STATIC) - include_directories(SYSTEM "${QXMPP_INCLUDE_DIRS}") +# special OS X modifications for RtMidi library +if (RTMIDI_FOUND AND NOT DISABLE_RTMIDI AND APPLE) + find_library(CoreMIDI CoreMIDI) + add_definitions(-D__MACOSX_CORE__) + target_link_libraries(${TARGET_NAME} ${CoreMIDI}) +endif () - target_link_libraries(${TARGET_NAME} ${QXMPP_LIBRARIES}) -endif (QXMPP_FOUND AND NOT DISABLE_QXMPP) - -# and with RtMidi for RtMidi control -if (RTMIDI_FOUND AND NOT DISABLE_RTMIDI) - add_definitions(-DHAVE_RTMIDI) - include_directories(SYSTEM "${RTMIDI_INCLUDE_DIRS}") - target_link_libraries(${TARGET_NAME} ${RTMIDI_LIBRARIES}) - - if (APPLE) - find_library(CoreMIDI CoreMIDI) - add_definitions(-D__MACOSX_CORE__) - target_link_libraries(${TARGET_NAME} ${CoreMIDI}) - endif() -endif() - -# and with Faceshift for depth camera face tracking -if (FACESHIFT_FOUND AND NOT DISABLE_FACESHIFT) - add_definitions(-DHAVE_FACESHIFT) - include_directories(SYSTEM "${FACESHIFT_INCLUDE_DIRS}") - target_link_libraries(${TARGET_NAME} ${FACESHIFT_LIBRARIES}) -endif() +if (QXMPP_FOUND AND NOT DISABLE_QXMPP AND WIN32) + # assume we're linking a static Qt on windows + add_definitions(-DQXMPP_STATIC) +endif () # include headers for interface and InterfaceConfig. include_directories("${PROJECT_SOURCE_DIR}/src" "${PROJECT_BINARY_DIR}/includes") diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index a3d61a4d1e..9b54b84351 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -524,8 +524,8 @@ void Application::initializeGL() { // Before we render anything, let's set up our viewFrustumOffsetCamera with a sufficiently large // field of view and near and far clip to make it interesting. //viewFrustumOffsetCamera.setFieldOfView(90.0); - _viewFrustumOffsetCamera.setNearClip(0.1f); - _viewFrustumOffsetCamera.setFarClip(500.0f * TREE_SCALE); + _viewFrustumOffsetCamera.setNearClip(DEFAULT_NEAR_CLIP); + _viewFrustumOffsetCamera.setFarClip(DEFAULT_FAR_CLIP); initDisplay(); qDebug( "Initialized Display."); @@ -592,7 +592,7 @@ void Application::paintGL() { if (_myCamera.getMode() == CAMERA_MODE_FIRST_PERSON) { _myCamera.setTightness(0.0f); // In first person, camera follows (untweaked) head exactly without delay - _myCamera.setTargetPosition(_myAvatar->getHead()->calculateAverageEyePosition()); + _myCamera.setTargetPosition(_myAvatar->getHead()->getFilteredEyePosition()); _myCamera.setTargetRotation(_myAvatar->getHead()->getCameraOrientation()); } else if (_myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) { @@ -611,10 +611,10 @@ void Application::paintGL() { if (OculusManager::isConnected()) { _myCamera.setDistance(MIRROR_FULLSCREEN_DISTANCE * _scaleMirror); _myCamera.setTargetRotation(_myAvatar->getWorldAlignedOrientation() * glm::quat(glm::vec3(0.0f, PI + _rotateMirror, 0.0f))); - _myCamera.setTargetPosition(_myAvatar->getHead()->calculateAverageEyePosition() + glm::vec3(0, _raiseMirror * _myAvatar->getScale(), 0)); + _myCamera.setTargetPosition(_myAvatar->getHead()->getEyePosition() + glm::vec3(0, _raiseMirror * _myAvatar->getScale(), 0)); } else { _myCamera.setTightness(0.0f); - glm::vec3 eyePosition = _myAvatar->getHead()->calculateAverageEyePosition(); + glm::vec3 eyePosition = _myAvatar->getHead()->getFilteredEyePosition(); float headHeight = eyePosition.y - _myAvatar->getPosition().y; _myCamera.setDistance(MIRROR_FULLSCREEN_DISTANCE * _scaleMirror); _myCamera.setTargetPosition(_myAvatar->getPosition() + glm::vec3(0, headHeight + (_raiseMirror * _myAvatar->getScale()), 0)); @@ -1925,17 +1925,9 @@ void Application::updateMyAvatarLookAtPosition() { } } else { // I am not looking at anyone else, so just look forward - lookAtSpot = _myAvatar->getHead()->calculateAverageEyePosition() + + lookAtSpot = _myAvatar->getHead()->getEyePosition() + (_myAvatar->getHead()->getFinalOrientationInWorldFrame() * glm::vec3(0.f, 0.f, -TREE_SCALE)); } - // TODO: Add saccade to mouse pointer when stable, IF not looking at someone (since we know we are looking at it) - /* - const float FIXED_MIN_EYE_DISTANCE = 0.3f; - float minEyeDistance = FIXED_MIN_EYE_DISTANCE + (_myCamera.getMode() == CAMERA_MODE_FIRST_PERSON ? 0.0f : - glm::distance(_mouseRayOrigin, _myAvatar->getHead()->calculateAverageEyePosition())); - lookAtSpot = _mouseRayOrigin + _mouseRayDirection * qMax(minEyeDistance, distance); - */ - } // // Deflect the eyes a bit to match the detected Gaze from 3D camera if active @@ -1945,7 +1937,7 @@ void Application::updateMyAvatarLookAtPosition() { float eyeYaw = tracker->getEstimatedEyeYaw(); const float GAZE_DEFLECTION_REDUCTION_DURING_EYE_CONTACT = 0.1f; // deflect using Faceshift gaze data - glm::vec3 origin = _myAvatar->getHead()->calculateAverageEyePosition(); + glm::vec3 origin = _myAvatar->getHead()->getEyePosition(); float pitchSign = (_myCamera.getMode() == CAMERA_MODE_MIRROR) ? -1.0f : 1.0f; float deflection = Menu::getInstance()->getFaceshiftEyeDeflection(); if (isLookingAtSomeone) { @@ -2949,7 +2941,7 @@ void Application::renderRearViewMirror(const QRect& region, bool billboard) { _mirrorCamera.setTargetPosition(glm::vec3()); } else { - _mirrorCamera.setTargetPosition(_myAvatar->getHead()->calculateAverageEyePosition()); + _mirrorCamera.setTargetPosition(_myAvatar->getHead()->getEyePosition()); } } _mirrorCamera.setAspectRatio((float)region.width() / region.height()); @@ -2978,7 +2970,7 @@ void Application::renderRearViewMirror(const QRect& region, bool billboard) { _myAvatar->getSkeletonModel().getNeckPosition(neckPosition); // get the eye position relative to the body - glm::vec3 eyePosition = _myAvatar->getHead()->calculateAverageEyePosition(); + glm::vec3 eyePosition = _myAvatar->getHead()->getEyePosition(); float eyeHeight = eyePosition.y - _myAvatar->getPosition().y; // set the translation of the face relative to the neck position @@ -3369,7 +3361,7 @@ void Application::nodeKilled(SharedNodePointer node) { _modelEditSender.nodeKilled(node); if (node->getType() == NodeType::AudioMixer) { - QMetaObject::invokeMethod(&_audio, "resetIncomingMixedAudioSequenceNumberStats"); + QMetaObject::invokeMethod(&_audio, "audioMixerKilled"); } if (node->getType() == NodeType::VoxelServer) { @@ -3655,7 +3647,7 @@ ScriptEngine* Application::loadScript(const QString& scriptName, bool loadScript scriptEngine->getModelsScriptingInterface()->setModelTree(_models.getTree()); // model has some custom types - Model::registerMetaTypes(scriptEngine->getEngine()); + Model::registerMetaTypes(scriptEngine); // hook our avatar object into this script engine scriptEngine->setAvatarData(_myAvatar, "MyAvatar"); // leave it as a MyAvatar class to expose thrust features diff --git a/interface/src/Audio.cpp b/interface/src/Audio.cpp index 24dcdcf6df..5054537287 100644 --- a/interface/src/Audio.cpp +++ b/interface/src/Audio.cpp @@ -43,6 +43,7 @@ #include "Audio.h" #include "Menu.h" #include "Util.h" +#include "AudioRingBuffer.h" static const float AUDIO_CALLBACK_MSECS = (float) NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL / (float)SAMPLE_RATE * 1000.0; @@ -125,14 +126,16 @@ Audio::Audio(int16_t initialJitterBufferSamples, QObject* parent) : _scopeInput(0), _scopeOutputLeft(0), _scopeOutputRight(0), + _statsEnabled(false), _starveCount(0), _consecutiveNotMixedCount(0), _outgoingAvatarAudioSequenceNumber(0), _incomingMixedAudioSequenceNumberStats(INCOMING_SEQ_STATS_HISTORY_LENGTH), _interframeTimeGapStats(TIME_GAPS_STATS_INTERVAL_SAMPLES, TIME_GAP_STATS_WINDOW_INTERVALS), - _inputRingBufferFramesAvailableStats(1, FRAMES_AVAILABLE_STATS_WINDOW_SECONDS), + _audioInputMsecsReadStats(MSECS_PER_SECOND / (float)AUDIO_CALLBACK_MSECS * CALLBACK_ACCELERATOR_RATIO, FRAMES_AVAILABLE_STATS_WINDOW_SECONDS), + _inputRingBufferMsecsAvailableStats(1, FRAMES_AVAILABLE_STATS_WINDOW_SECONDS), _outputRingBufferFramesAvailableStats(1, FRAMES_AVAILABLE_STATS_WINDOW_SECONDS), - _audioOutputBufferFramesAvailableStats(1, FRAMES_AVAILABLE_STATS_WINDOW_SECONDS) + _audioOutputMsecsUnplayedStats(1, FRAMES_AVAILABLE_STATS_WINDOW_SECONDS) { // clear the array of locally injected samples memset(_localProceduralSamples, 0, NETWORK_BUFFER_LENGTH_BYTES_PER_CHANNEL); @@ -148,15 +151,34 @@ void Audio::init(QGLWidget *parent) { void Audio::reset() { _ringBuffer.reset(); - + + // we don't want to reset seq numbers when space-bar reset occurs. + //_outgoingAvatarAudioSequenceNumber = 0; + + resetStats(); +} + +void Audio::resetStats() { _starveCount = 0; _consecutiveNotMixedCount = 0; _audioMixerAvatarStreamAudioStats = AudioStreamStats(); _audioMixerInjectedStreamAudioStatsMap.clear(); - //_outgoingAvatarAudioSequenceNumber = 0; _incomingMixedAudioSequenceNumberStats.reset(); + + _interframeTimeGapStats.reset(); + + _audioInputMsecsReadStats.reset(); + _inputRingBufferMsecsAvailableStats.reset(); + + _outputRingBufferFramesAvailableStats.reset(); + _audioOutputMsecsUnplayedStats.reset(); +} + +void Audio::audioMixerKilled() { + _outgoingAvatarAudioSequenceNumber = 0; + resetStats(); } QAudioDeviceInfo getNamedAudioDeviceForMode(QAudio::Mode mode, const QString& deviceName) { @@ -499,8 +521,11 @@ void Audio::handleAudioInput() { } _inputRingBuffer.writeData(inputByteArray.data(), inputByteArray.size()); + + float audioInputMsecsRead = inputByteArray.size() / (float)(_inputFormat.bytesForDuration(USECS_PER_MSEC)); + _audioInputMsecsReadStats.update(audioInputMsecsRead); - while (_inputRingBuffer.samplesAvailable() > inputSamplesRequired) { + while (_inputRingBuffer.samplesAvailable() >= inputSamplesRequired) { int16_t* inputAudioSamples = new int16_t[inputSamplesRequired]; _inputRingBuffer.readSamples(inputAudioSamples, inputSamplesRequired); @@ -811,11 +836,12 @@ AudioStreamStats Audio::getDownstreamAudioStreamStats() const { void Audio::sendDownstreamAudioStatsPacket() { - _inputRingBufferFramesAvailableStats.update(getInputRingBufferFramesAvailable()); + // since this function is called every second, we'll sample some of our stats here + + _inputRingBufferMsecsAvailableStats.update(getInputRingBufferMsecsAvailable()); - // since this function is called every second, we'll sample the number of audio frames available here. _outputRingBufferFramesAvailableStats.update(_ringBuffer.framesAvailable()); - _audioOutputBufferFramesAvailableStats.update(getOutputRingBufferFramesAvailable()); + _audioOutputMsecsUnplayedStats.update(getAudioOutputMsecsUnplayed()); // push the current seq number stats into history, which moves the history window forward 1s // (since that's how often pushStatsToHistory() is called) @@ -1286,6 +1312,10 @@ void Audio::toggleScopePause() { _scopeEnabledPause = !_scopeEnabledPause; } +void Audio::toggleStats() { + _statsEnabled = !_statsEnabled; +} + void Audio::selectAudioScopeFiveFrames() { if (Menu::getInstance()->isOptionChecked(MenuOption::AudioScopeFiveFrames)) { reallocateScope(5); @@ -1365,6 +1395,174 @@ void Audio::addBufferToScope( } } +void Audio::renderStats(const float* color, int width, int height) { + if (!_statsEnabled) { + return; + } + + const int LINES_WHEN_CENTERED = 30; + const int CENTERED_BACKGROUND_HEIGHT = STATS_HEIGHT_PER_LINE * LINES_WHEN_CENTERED; + + int lines = _audioMixerInjectedStreamAudioStatsMap.size() * 7 + 23; + int statsHeight = STATS_HEIGHT_PER_LINE * lines; + + + static const float backgroundColor[4] = { 0.2f, 0.2f, 0.2f, 0.6f }; + + int x = std::max((width - (int)STATS_WIDTH) / 2, 0); + int y = std::max((height - CENTERED_BACKGROUND_HEIGHT) / 2, 0); + int w = STATS_WIDTH; + int h = statsHeight; + renderBackground(backgroundColor, x, y, w, h); + + + int horizontalOffset = x + 5; + int verticalOffset = y; + + float scale = 0.10f; + float rotation = 0.0f; + int font = 2; + + + char latencyStatString[512]; + + const float BUFFER_SEND_INTERVAL_MSECS = BUFFER_SEND_INTERVAL_USECS / (float)USECS_PER_MSEC; + + float audioInputBufferLatency = 0.0f, inputRingBufferLatency = 0.0f, networkRoundtripLatency = 0.0f, mixerRingBufferLatency = 0.0f, outputRingBufferLatency = 0.0f, audioOutputBufferLatency = 0.0f; + + SharedNodePointer audioMixerNodePointer = NodeList::getInstance()->soloNodeOfType(NodeType::AudioMixer); + if (!audioMixerNodePointer.isNull()) { + audioInputBufferLatency = _audioInputMsecsReadStats.getWindowAverage(); + inputRingBufferLatency = getInputRingBufferAverageMsecsAvailable(); + networkRoundtripLatency = audioMixerNodePointer->getPingMs(); + mixerRingBufferLatency = _audioMixerAvatarStreamAudioStats._ringBufferFramesAvailableAverage * BUFFER_SEND_INTERVAL_MSECS; + outputRingBufferLatency = _outputRingBufferFramesAvailableStats.getWindowAverage() * BUFFER_SEND_INTERVAL_MSECS; + audioOutputBufferLatency = _audioOutputMsecsUnplayedStats.getWindowAverage(); + } + float totalLatency = audioInputBufferLatency + inputRingBufferLatency + networkRoundtripLatency + mixerRingBufferLatency + outputRingBufferLatency + audioOutputBufferLatency; + + sprintf(latencyStatString, " Audio input buffer: %7.2fms - avg msecs of samples read to the input ring buffer in last 10s", audioInputBufferLatency); + verticalOffset += STATS_HEIGHT_PER_LINE; + drawText(horizontalOffset, verticalOffset, scale, rotation, font, latencyStatString, color); + + sprintf(latencyStatString, " Input ring buffer: %7.2fms - avg msecs of samples in input ring buffer in last 10s", inputRingBufferLatency); + verticalOffset += STATS_HEIGHT_PER_LINE; + drawText(horizontalOffset, verticalOffset, scale, rotation, font, latencyStatString, color); + + sprintf(latencyStatString, " Network to mixer: %7.2fms - half of last ping value calculated by the node list", networkRoundtripLatency / 2.0f); + verticalOffset += STATS_HEIGHT_PER_LINE; + drawText(horizontalOffset, verticalOffset, scale, rotation, font, latencyStatString, color); + + sprintf(latencyStatString, " AudioMixer ring buffer: %7.2fms - avg msecs of samples in audio mixer's ring buffer in last 10s", mixerRingBufferLatency); + verticalOffset += STATS_HEIGHT_PER_LINE; + drawText(horizontalOffset, verticalOffset, scale, rotation, font, latencyStatString, color); + + sprintf(latencyStatString, " Network to client: %7.2fms - half of last ping value calculated by the node list", networkRoundtripLatency / 2.0f); + verticalOffset += STATS_HEIGHT_PER_LINE; + drawText(horizontalOffset, verticalOffset, scale, rotation, font, latencyStatString, color); + + sprintf(latencyStatString, " Output ring buffer: %7.2fms - avg msecs of samples in output ring buffer in last 10s", outputRingBufferLatency); + verticalOffset += STATS_HEIGHT_PER_LINE; + drawText(horizontalOffset, verticalOffset, scale, rotation, font, latencyStatString, color); + + sprintf(latencyStatString, " Audio output buffer: %7.2fms - avg msecs of samples in audio output buffer in last 10s", audioOutputBufferLatency); + verticalOffset += STATS_HEIGHT_PER_LINE; + drawText(horizontalOffset, verticalOffset, scale, rotation, font, latencyStatString, color); + + sprintf(latencyStatString, " TOTAL: %7.2fms\n", totalLatency); + verticalOffset += STATS_HEIGHT_PER_LINE; + drawText(horizontalOffset, verticalOffset, scale, rotation, font, latencyStatString, color); + + + verticalOffset += STATS_HEIGHT_PER_LINE; // blank line + + + char downstreamLabelString[] = "Downstream mixed audio stats:"; + verticalOffset += STATS_HEIGHT_PER_LINE; + drawText(horizontalOffset, verticalOffset, scale, rotation, font, downstreamLabelString, color); + + renderAudioStreamStats(getDownstreamAudioStreamStats(), horizontalOffset, verticalOffset, scale, rotation, font, color, true); + + + verticalOffset += STATS_HEIGHT_PER_LINE; // blank line + + char upstreamMicLabelString[] = "Upstream mic audio stats:"; + verticalOffset += STATS_HEIGHT_PER_LINE; + drawText(horizontalOffset, verticalOffset, scale, rotation, font, upstreamMicLabelString, color); + + renderAudioStreamStats(_audioMixerAvatarStreamAudioStats, horizontalOffset, verticalOffset, scale, rotation, font, color); + + + foreach(const AudioStreamStats& injectedStreamAudioStats, _audioMixerInjectedStreamAudioStatsMap) { + + verticalOffset += STATS_HEIGHT_PER_LINE; // blank line + + char upstreamInjectedLabelString[512]; + sprintf(upstreamInjectedLabelString, "Upstream injected audio stats: stream ID: %s", + injectedStreamAudioStats._streamIdentifier.toString().toLatin1().data()); + verticalOffset += STATS_HEIGHT_PER_LINE; + drawText(horizontalOffset, verticalOffset, scale, rotation, font, upstreamInjectedLabelString, color); + + renderAudioStreamStats(injectedStreamAudioStats, horizontalOffset, verticalOffset, scale, rotation, font, color); + } +} + +void Audio::renderAudioStreamStats(const AudioStreamStats& streamStats, int horizontalOffset, int& verticalOffset, + float scale, float rotation, int font, const float* color, bool isDownstreamStats) { + + char stringBuffer[512]; + + sprintf(stringBuffer, " Packet loss | overall: %5.2f%% (%d lost), last_30s: %5.2f%% (%d lost)", + streamStats._packetStreamStats.getLostRate() * 100.0f, + streamStats._packetStreamStats._numLost, + streamStats._packetStreamWindowStats.getLostRate() * 100.0f, + streamStats._packetStreamWindowStats._numLost); + verticalOffset += STATS_HEIGHT_PER_LINE; + drawText(horizontalOffset, verticalOffset, scale, rotation, font, stringBuffer, color); + + if (isDownstreamStats) { + + const float BUFFER_SEND_INTERVAL_MSECS = BUFFER_SEND_INTERVAL_USECS / (float)USECS_PER_MSEC; + sprintf(stringBuffer, " Ringbuffer frames | desired: %u, avg_available(10s): %u+%d, available: %u+%d", + streamStats._ringBufferDesiredJitterBufferFrames, + streamStats._ringBufferFramesAvailableAverage, + (int)(getAudioOutputAverageMsecsUnplayed() / BUFFER_SEND_INTERVAL_MSECS), + streamStats._ringBufferFramesAvailable, + (int)(getAudioOutputMsecsUnplayed() / BUFFER_SEND_INTERVAL_MSECS)); + } else { + sprintf(stringBuffer, " Ringbuffer frames | desired: %u, avg_available(10s): %u, available: %u", + streamStats._ringBufferDesiredJitterBufferFrames, + streamStats._ringBufferFramesAvailableAverage, + streamStats._ringBufferFramesAvailable); + } + + verticalOffset += STATS_HEIGHT_PER_LINE; + drawText(horizontalOffset, verticalOffset, scale, rotation, font, stringBuffer, color); + + sprintf(stringBuffer, " Ringbuffer stats | starves: %u, prev_starve_lasted: %u, frames_dropped: %u, overflows: %u", + streamStats._ringBufferStarveCount, + streamStats._ringBufferConsecutiveNotMixedCount, + streamStats._ringBufferSilentFramesDropped, + streamStats._ringBufferOverflowCount); + verticalOffset += STATS_HEIGHT_PER_LINE; + drawText(horizontalOffset, verticalOffset, scale, rotation, font, stringBuffer, color); + + sprintf(stringBuffer, " Inter-packet timegaps (overall) | min: %9s, max: %9s, avg: %9s", + formatUsecTime(streamStats._timeGapMin).toLatin1().data(), + formatUsecTime(streamStats._timeGapMax).toLatin1().data(), + formatUsecTime(streamStats._timeGapAverage).toLatin1().data()); + verticalOffset += STATS_HEIGHT_PER_LINE; + drawText(horizontalOffset, verticalOffset, scale, rotation, font, stringBuffer, color); + + sprintf(stringBuffer, " Inter-packet timegaps (last 30s) | min: %9s, max: %9s, avg: %9s", + formatUsecTime(streamStats._timeGapWindowMin).toLatin1().data(), + formatUsecTime(streamStats._timeGapWindowMax).toLatin1().data(), + formatUsecTime(streamStats._timeGapWindowAverage).toLatin1().data()); + verticalOffset += STATS_HEIGHT_PER_LINE; + drawText(horizontalOffset, verticalOffset, scale, rotation, font, stringBuffer, color); +} + + void Audio::renderScope(int width, int height) { if (!_scopeEnabled) @@ -1622,15 +1820,14 @@ int Audio::calculateNumberOfFrameSamples(int numBytes) const { return frameSamples; } -int Audio::getOutputRingBufferFramesAvailable() const { - float networkOutputToOutputRatio = (_desiredOutputFormat.sampleRate() / (float)_outputFormat.sampleRate()) - * (_desiredOutputFormat.channelCount() / (float)_outputFormat.channelCount()); - - return (_audioOutput->bufferSize() - _audioOutput->bytesFree()) * networkOutputToOutputRatio - / (sizeof(int16_t) * _ringBuffer.getNumFrameSamples()); +float Audio::getAudioOutputMsecsUnplayed() const { + int bytesAudioOutputUnplayed = _audioOutput->bufferSize() - _audioOutput->bytesFree(); + float msecsAudioOutputUnplayed = bytesAudioOutputUnplayed / (float)_outputFormat.bytesForDuration(USECS_PER_MSEC); + return msecsAudioOutputUnplayed; } -int Audio::getInputRingBufferFramesAvailable() const { - float inputToNetworkInputRatio = calculateDeviceToNetworkInputRatio(_numInputCallbackBytes); - return _inputRingBuffer.samplesAvailable() / inputToNetworkInputRatio / _inputRingBuffer.getNumFrameSamples(); +float Audio::getInputRingBufferMsecsAvailable() const { + int bytesInInputRingBuffer = _inputRingBuffer.samplesAvailable() * sizeof(int16_t); + float msecsInInputRingBuffer = bytesInInputRingBuffer / (float)(_inputFormat.bytesForDuration(USECS_PER_MSEC)); + return msecsInInputRingBuffer; } diff --git a/interface/src/Audio.h b/interface/src/Audio.h index ed50815d78..67a951b8d9 100644 --- a/interface/src/Audio.h +++ b/interface/src/Audio.h @@ -71,6 +71,7 @@ public: void renderToolBox(int x, int y, bool boxed); void renderScope(int width, int height); + void renderStats(const float* color, int width, int height); int getNetworkSampleRate() { return SAMPLE_RATE; } int getNetworkBufferLengthSamplesPerChannel() { return NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL; } @@ -78,12 +79,12 @@ public: bool getProcessSpatialAudio() const { return _processSpatialAudio; } const SequenceNumberStats& getIncomingMixedAudioSequenceNumberStats() const { return _incomingMixedAudioSequenceNumberStats; } + + float getInputRingBufferMsecsAvailable() const; + float getInputRingBufferAverageMsecsAvailable() const { return (float)_inputRingBufferMsecsAvailableStats.getWindowAverage(); } - int getInputRingBufferFramesAvailable() const; - int getInputRingBufferAverageFramesAvailable() const { return (int)_inputRingBufferFramesAvailableStats.getWindowAverage(); } - - int getOutputRingBufferFramesAvailable() const; - int getOutputRingBufferAverageFramesAvailable() const { return (int)_audioOutputBufferFramesAvailableStats.getWindowAverage(); } + float getAudioOutputMsecsUnplayed() const; + float getAudioOutputAverageMsecsUnplayed() const { return (float)_audioOutputMsecsUnplayedStats.getWindowAverage(); } public slots: void start(); @@ -93,12 +94,14 @@ public slots: void addSpatialAudioToBuffer(unsigned int sampleTime, const QByteArray& spatialAudio, unsigned int numSamples); void handleAudioInput(); void reset(); - void resetIncomingMixedAudioSequenceNumberStats() { _incomingMixedAudioSequenceNumberStats.reset(); } + void resetStats(); + void audioMixerKilled(); void toggleMute(); void toggleAudioNoiseReduction(); void toggleToneInjection(); void toggleScope(); void toggleScopePause(); + void toggleStats(); void toggleAudioSpatialProcessing(); void toggleStereoInput(); void selectAudioScopeFiveFrames(); @@ -245,6 +248,10 @@ private: void renderGrid(const float* color, int x, int y, int width, int height, int rows, int cols); void renderLineStrip(const float* color, int x, int y, int n, int offset, const QByteArray* byteArray); + // audio stats methods for rendering + void renderAudioStreamStats(const AudioStreamStats& streamStats, int horizontalOffset, int& verticalOffset, + float scale, float rotation, int font, const float* color, bool isDownstreamStats = false); + // Audio scope data static const unsigned int NETWORK_SAMPLES_PER_FRAME = NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL; static const unsigned int DEFAULT_FRAMES_PER_SCOPE = 5; @@ -261,6 +268,13 @@ private: QByteArray* _scopeInput; QByteArray* _scopeOutputLeft; QByteArray* _scopeOutputRight; +#ifdef _WIN32 + static const unsigned int STATS_WIDTH = 1500; +#else + static const unsigned int STATS_WIDTH = 650; +#endif + static const unsigned int STATS_HEIGHT_PER_LINE = 20; + bool _statsEnabled; int _starveCount; int _consecutiveNotMixedCount; @@ -273,10 +287,11 @@ private: MovingMinMaxAvg _interframeTimeGapStats; - MovingMinMaxAvg _inputRingBufferFramesAvailableStats; + MovingMinMaxAvg _audioInputMsecsReadStats; + MovingMinMaxAvg _inputRingBufferMsecsAvailableStats; MovingMinMaxAvg _outputRingBufferFramesAvailableStats; - MovingMinMaxAvg _audioOutputBufferFramesAvailableStats; + MovingMinMaxAvg _audioOutputMsecsUnplayedStats; }; diff --git a/interface/src/Camera.cpp b/interface/src/Camera.cpp index 4490b60fc9..4a924b4ec3 100644 --- a/interface/src/Camera.cpp +++ b/interface/src/Camera.cpp @@ -47,8 +47,8 @@ Camera::Camera() : _targetPosition(0.0f, 0.0f, 0.0f), _fieldOfView(DEFAULT_FIELD_OF_VIEW_DEGREES), _aspectRatio(16.0f/9.0f), - _nearClip(0.08f), // default - _farClip(50.0f * TREE_SCALE), // default + _nearClip(DEFAULT_NEAR_CLIP), // default + _farClip(DEFAULT_FAR_CLIP), // default _upShift(0.0f), _distance(0.0f), _tightness(10.0f), // default diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 16c3419561..05b1e7ac69 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -242,8 +242,8 @@ Menu::Menu() : const QXmppClient& xmppClient = XmppClient::getInstance().getXMPPClient(); toggleChat(); - connect(&xmppClient, SIGNAL(connected()), this, SLOT(toggleChat())); - connect(&xmppClient, SIGNAL(disconnected()), this, SLOT(toggleChat())); + connect(&xmppClient, &QXmppClient::connected, this, &Menu::toggleChat); + connect(&xmppClient, &QXmppClient::disconnected, this, &Menu::toggleChat); QDir::setCurrent(Application::resourcesPath()); // init chat window to listen chat @@ -593,6 +593,12 @@ Menu::Menu() : Qt::CTRL | Qt::SHIFT | Qt::Key_U, false); + addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::AudioStats, + 0, + false, + appInstance->getAudio(), + SLOT(toggleStats())); + addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::DisableQAudioOutputOverflowCheck, 0, true); addActionToQMenuAndActionHash(developerMenu, MenuOption::PasteToVoxel, diff --git a/interface/src/Menu.h b/interface/src/Menu.h index e4221b0913..81dd26dc01 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -315,6 +315,7 @@ namespace MenuOption { const QString AudioScopeFrames = "Display Frames"; const QString AudioScopePause = "Pause Audio Scope"; const QString AudioScopeTwentyFrames = "Twenty"; + const QString AudioStats = "Audio Stats"; const QString AudioSpatialProcessingAlternateDistanceAttenuate = "Alternate distance attenuation"; const QString AudioSpatialProcessing = "Audio Spatial Processing"; const QString AudioSpatialProcessingDontDistanceAttenuate = "Don't calculate distance attenuation"; diff --git a/interface/src/XmppClient.cpp b/interface/src/XmppClient.cpp index ef9db55620..7f3c7cf291 100644 --- a/interface/src/XmppClient.cpp +++ b/interface/src/XmppClient.cpp @@ -9,7 +9,6 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#ifdef HAVE_QXMPP #include @@ -18,6 +17,7 @@ const QString DEFAULT_XMPP_SERVER = "chat.highfidelity.io"; const QString DEFAULT_CHAT_ROOM = "public@public-chat.highfidelity.io"; +#ifdef HAVE_QXMPP XmppClient::XmppClient() : _xmppClient(), _xmppMUCManager() @@ -26,6 +26,11 @@ XmppClient::XmppClient() : connect(&accountManager, SIGNAL(profileChanged()), this, SLOT(connectToServer())); connect(&accountManager, SIGNAL(logoutComplete()), this, SLOT(disconnectFromServer())); } +#else +XmppClient::XmppClient() { + +} +#endif XmppClient& XmppClient::getInstance() { static XmppClient sharedInstance; @@ -33,18 +38,23 @@ XmppClient& XmppClient::getInstance() { } void XmppClient::xmppConnected() { +#ifdef HAVE_QXMPP _publicChatRoom = _xmppMUCManager.addRoom(DEFAULT_CHAT_ROOM); _publicChatRoom->setNickName(AccountManager::getInstance().getAccountInfo().getUsername()); _publicChatRoom->join(); emit joinedPublicChatRoom(); +#endif } +#ifdef HAVE_QXMPP void XmppClient::xmppError(QXmppClient::Error error) { qDebug() << "Error connnecting to XMPP for user " << AccountManager::getInstance().getAccountInfo().getUsername() << ": " << error; } +#endif void XmppClient::connectToServer() { +#ifdef HAVE_QXMPP disconnectFromServer(); if (_xmppClient.addExtension(&_xmppMUCManager)) { @@ -55,12 +65,15 @@ void XmppClient::connectToServer() { QString user = accountManager.getAccountInfo().getUsername(); const QString& password = accountManager.getAccountInfo().getXMPPPassword(); _xmppClient.connectToServer(user + "@" + DEFAULT_XMPP_SERVER, password); +#endif } void XmppClient::disconnectFromServer() { +#ifdef HAVE_QXMPP if (_xmppClient.isConnected()) { _xmppClient.disconnectFromServer(); } +#endif } XmppClient::XmppClient(const XmppClient& other) { @@ -70,5 +83,3 @@ XmppClient::XmppClient(const XmppClient& other) { void XmppClient::operator =(XmppClient const& other) { Q_UNUSED(other); } - -#endif diff --git a/interface/src/XmppClient.h b/interface/src/XmppClient.h index cbb06cf992..91c10c4055 100644 --- a/interface/src/XmppClient.h +++ b/interface/src/XmppClient.h @@ -9,31 +9,35 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#ifdef HAVE_QXMPP - #ifndef hifi_XmppClient_h #define hifi_XmppClient_h #include + +#ifdef HAVE_QXMPP #include #include +#endif /// Generalized threaded processor for handling received inbound packets. class XmppClient : public QObject { Q_OBJECT - public: static XmppClient& getInstance(); +#ifdef HAVE_QXMPP QXmppClient& getXMPPClient() { return _xmppClient; } const QXmppMucRoom* getPublicChatRoom() const { return _publicChatRoom; } - +#endif + signals: void joinedPublicChatRoom(); private slots: void xmppConnected(); +#ifdef HAVE_QXMPP void xmppError(QXmppClient::Error error); +#endif void connectToServer(); void disconnectFromServer(); @@ -43,11 +47,11 @@ private: XmppClient(XmppClient const& other); // not implemented void operator=(XmppClient const& other); // not implemented +#ifdef HAVE_QXMPP QXmppClient _xmppClient; QXmppMucManager _xmppMUCManager; QXmppMucRoom* _publicChatRoom; +#endif }; -#endif // __interface__XmppClient__ - #endif // hifi_XmppClient_h diff --git a/interface/src/avatar/Head.cpp b/interface/src/avatar/Head.cpp index ee242d179a..d3d1e74fc8 100644 --- a/interface/src/avatar/Head.cpp +++ b/interface/src/avatar/Head.cpp @@ -159,6 +159,10 @@ void Head::simulate(float deltaTime, bool isMine, bool billboard) { } } _eyePosition = calculateAverageEyePosition(); + + float velocityFilter = glm::clamp(1.0f - glm::length(_filteredEyePosition - _eyePosition), 0.0f, 1.0f); + _filteredEyePosition = velocityFilter * _filteredEyePosition + (1.0f - velocityFilter) * _eyePosition; + } void Head::relaxLean(float deltaTime) { diff --git a/interface/src/avatar/Head.h b/interface/src/avatar/Head.h index 36df51fa6f..6d1e82b97f 100644 --- a/interface/src/avatar/Head.h +++ b/interface/src/avatar/Head.h @@ -88,8 +88,7 @@ public: const bool getReturnToCenter() const { return _returnHeadToCenter; } // Do you want head to try to return to center (depends on interface detected) float getAverageLoudness() const { return _averageLoudness; } - glm::vec3 calculateAverageEyePosition() const { return _leftEyePosition + (_rightEyePosition - _leftEyePosition ) * ONE_HALF; } - + glm::vec3 getFilteredEyePosition() const { return _filteredEyePosition; } /// \return the point about which scaling occurs. glm::vec3 getScalePivot() const; @@ -110,6 +109,8 @@ public: void addLeanDeltas(float sideways, float forward); private: + glm::vec3 calculateAverageEyePosition() const { return _leftEyePosition + (_rightEyePosition - _leftEyePosition ) * ONE_HALF; } + // disallow copies of the Head, copy of owning Avatar is disallowed too Head(const Head&); Head& operator= (const Head&); @@ -120,6 +121,8 @@ private: glm::vec3 _leftEyePosition; glm::vec3 _rightEyePosition; glm::vec3 _eyePosition; + glm::vec3 _filteredEyePosition; // velocity filtered world space eye position + float _scale; float _lastLoudness; float _audioAttack; diff --git a/interface/src/avatar/MuscleConstraint.cpp b/interface/src/avatar/MuscleConstraint.cpp new file mode 100644 index 0000000000..f4ba7975d0 --- /dev/null +++ b/interface/src/avatar/MuscleConstraint.cpp @@ -0,0 +1,34 @@ +// +// MuscleConstraint.cpp +// interface/src/avatar +// +// Created by Andrew Meadows 2014.07.24 +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include +#include + +#include "MuscleConstraint.h" + +const float DEFAULT_MUSCLE_STRENGTH = 0.5f * MAX_MUSCLE_STRENGTH; + +MuscleConstraint::MuscleConstraint(VerletPoint* parent, VerletPoint* child) + : _rootPoint(parent), _childPoint(child), + _parentIndex(-1), _childndex(-1), _strength(DEFAULT_MUSCLE_STRENGTH) { + _childOffset = child->_position - parent->_position; +} + +float MuscleConstraint::enforce() { + _childPoint->_position = (1.0f - _strength) * _childPoint->_position + _strength * (_rootPoint->_position + _childOffset); + return 0.0f; +} + +void MuscleConstraint::setIndices(int parent, int child) { + _parentIndex = parent; + _childndex = child; +} + diff --git a/interface/src/avatar/MuscleConstraint.h b/interface/src/avatar/MuscleConstraint.h new file mode 100644 index 0000000000..882b351b80 --- /dev/null +++ b/interface/src/avatar/MuscleConstraint.h @@ -0,0 +1,43 @@ +// +// MuscleConstraint.h +// interface/src/avatar +// +// Created by Andrew Meadows 2014.07.24 +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_MuscleConstraint_h +#define hifi_MuscleConstraint_h + +#include + +// MuscleConstraint is a simple constraint that pushes the child toward an offset relative to the parent. +// It does NOT push the parent. + +const float MAX_MUSCLE_STRENGTH = 0.5f; + +class MuscleConstraint : public Constraint { +public: + MuscleConstraint(VerletPoint* parent, VerletPoint* child); + MuscleConstraint(const MuscleConstraint& other); + float enforce(); + + void setIndices(int parent, int child); + int getParentIndex() const { return _parentIndex; } + int getChildIndex() const { return _childndex; } + void setChildOffset(const glm::vec3& offset) { _childOffset = offset; } + void setStrength(float strength) { _strength = glm::clamp(strength, 0.0f, MAX_MUSCLE_STRENGTH); } + float getStrength() const { return _strength; } +private: + VerletPoint* _rootPoint; + VerletPoint* _childPoint; + int _parentIndex; + int _childndex; + glm::vec3 _childOffset; + float _strength; // a value in range [0,MAX_MUSCLE_STRENGTH] +}; + +#endif // hifi_MuscleConstraint_h diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index b98e229394..4d2d679956 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -162,9 +162,16 @@ void MyAvatar::simulate(float deltaTime) { PerformanceTimer perfTimer("joints"); // copy out the skeleton joints from the model _jointData.resize(_skeletonModel.getJointStateCount()); - for (int i = 0; i < _jointData.size(); i++) { - JointData& data = _jointData[i]; - data.valid = _skeletonModel.getJointState(i, data.rotation); + if (Menu::getInstance()->isOptionChecked(MenuOption::CollideAsRagdoll)) { + for (int i = 0; i < _jointData.size(); i++) { + JointData& data = _jointData[i]; + data.valid = _skeletonModel.getVisibleJointState(i, data.rotation); + } + } else { + for (int i = 0; i < _jointData.size(); i++) { + JointData& data = _jointData[i]; + data.valid = _skeletonModel.getJointState(i, data.rotation); + } } } @@ -905,7 +912,7 @@ const float RENDER_HEAD_CUTOFF_DISTANCE = 0.50f; bool MyAvatar::shouldRenderHead(const glm::vec3& cameraPosition, RenderMode renderMode) const { const Head* head = getHead(); return (renderMode != NORMAL_RENDER_MODE) || - (glm::length(cameraPosition - head->calculateAverageEyePosition()) > RENDER_HEAD_CUTOFF_DISTANCE * _scale); + (glm::length(cameraPosition - head->getEyePosition()) > RENDER_HEAD_CUTOFF_DISTANCE * _scale); } float MyAvatar::computeDistanceToFloor(const glm::vec3& startPoint) { @@ -1871,8 +1878,6 @@ void MyAvatar::renderLaserPointers() { //Gets the tip position for the laser pointer glm::vec3 MyAvatar::getLaserPointerTipPosition(const PalmData* palm) { const ApplicationOverlay& applicationOverlay = Application::getInstance()->getApplicationOverlay(); - const float PALM_TIP_ROD_LENGTH_MULT = 40.0f; - glm::vec3 direction = glm::normalize(palm->getTipPosition() - palm->getPosition()); glm::vec3 position = palm->getPosition(); diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 59f39834e3..3caaad1391 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -14,11 +14,14 @@ #include #include +#include +#include #include "Application.h" #include "Avatar.h" #include "Hand.h" #include "Menu.h" +#include "MuscleConstraint.h" #include "SkeletonModel.h" SkeletonModel::SkeletonModel(Avatar* owningAvatar, QObject* parent) : @@ -505,6 +508,7 @@ void SkeletonModel::renderRagdoll() { // virtual void SkeletonModel::initRagdollPoints() { clearRagdollConstraintsAndPoints(); + _muscleConstraints.clear(); // one point for each joint int numJoints = _jointStates.size(); @@ -512,8 +516,7 @@ void SkeletonModel::initRagdollPoints() { for (int i = 0; i < numJoints; ++i) { const JointState& state = _jointStates.at(i); glm::vec3 position = state.getPosition(); - _ragdollPoints[i]._position = position; - _ragdollPoints[i]._lastPosition = position; + _ragdollPoints[i].initPosition(position); } } @@ -523,33 +526,74 @@ void SkeletonModel::buildRagdollConstraints() { const int numPoints = _ragdollPoints.size(); assert(numPoints == _jointStates.size()); + float minBone = FLT_MAX; + float maxBone = -FLT_MAX; QMultiMap families; for (int i = 0; i < numPoints; ++i) { const JointState& state = _jointStates.at(i); - const FBXJoint& joint = state.getFBXJoint(); - int parentIndex = joint.parentIndex; + int parentIndex = state.getParentIndex(); if (parentIndex == -1) { FixedConstraint* anchor = new FixedConstraint(&(_ragdollPoints[i]), glm::vec3(0.0f)); _ragdollConstraints.push_back(anchor); } else { DistanceConstraint* bone = new DistanceConstraint(&(_ragdollPoints[i]), &(_ragdollPoints[parentIndex])); + bone->setDistance(state.getDistanceToParent()); _ragdollConstraints.push_back(bone); families.insert(parentIndex, i); } + float boneLength = glm::length(state.getPositionInParentFrame()); + if (boneLength > maxBone) { + maxBone = boneLength; + } else if (boneLength < minBone) { + minBone = boneLength; + } } // Joints that have multiple children effectively have rigid constraints between the children - // in the parent frame, so we add constraints between children in the same family. + // in the parent frame, so we add DistanceConstraints between children in the same family. QMultiMap::iterator itr = families.begin(); while (itr != families.end()) { QList children = families.values(itr.key()); - if (children.size() > 1) { - for (int i = 1; i < children.size(); ++i) { + int numChildren = children.size(); + if (numChildren > 1) { + for (int i = 1; i < numChildren; ++i) { DistanceConstraint* bone = new DistanceConstraint(&(_ragdollPoints[children[i-1]]), &(_ragdollPoints[children[i]])); _ragdollConstraints.push_back(bone); } + if (numChildren > 2) { + DistanceConstraint* bone = new DistanceConstraint(&(_ragdollPoints[children[numChildren-1]]), &(_ragdollPoints[children[0]])); + _ragdollConstraints.push_back(bone); + } } ++itr; } + + float MAX_STRENGTH = 0.3f; + float MIN_STRENGTH = 0.005f; + // each joint gets a MuscleConstraint to its parent + for (int i = 1; i < numPoints; ++i) { + const JointState& state = _jointStates.at(i); + int p = state.getParentIndex(); + if (p == -1) { + continue; + } + MuscleConstraint* constraint = new MuscleConstraint(&(_ragdollPoints[p]), &(_ragdollPoints[i])); + _ragdollConstraints.push_back(constraint); + _muscleConstraints.push_back(constraint); + + // Short joints are more susceptible to wiggle so we modulate the strength based on the joint's length: + // long = weak and short = strong. + constraint->setIndices(p, i); + float boneLength = glm::length(state.getPositionInParentFrame()); + + float strength = MIN_STRENGTH + (MAX_STRENGTH - MIN_STRENGTH) * (maxBone - boneLength) / (maxBone - minBone); + if (!families.contains(i)) { + // Although muscles only pull on the children not parents, nevertheless those joints that have + // parents AND children are more stable than joints at the end such as fingers. For such joints we + // bestow maximum strength which helps reduce wiggle. + strength = MAX_MUSCLE_STRENGTH; + } + constraint->setStrength(strength); + } } @@ -597,11 +641,8 @@ void SkeletonModel::updateVisibleJointStates() { // virtual void SkeletonModel::stepRagdollForward(float deltaTime) { - // NOTE: increasing this timescale reduces vibrations in the ragdoll solution and reduces tunneling - // but makes the shapes slower to follow the body (introduces lag). - const float RAGDOLL_FOLLOWS_JOINTS_TIMESCALE = 0.05f; - float fraction = glm::clamp(deltaTime / RAGDOLL_FOLLOWS_JOINTS_TIMESCALE, 0.0f, 1.0f); - moveShapesTowardJoints(fraction); + Ragdoll::stepRagdollForward(deltaTime); + updateMuscles(); } float DENSITY_OF_WATER = 1000.0f; // kg/m^3 @@ -669,24 +710,46 @@ void SkeletonModel::buildShapes() { if (_ragdollPoints.size() == numStates) { int numJoints = _jointStates.size(); for (int i = 0; i < numJoints; ++i) { - _ragdollPoints[i]._lastPosition = _ragdollPoints.at(i)._position; - _ragdollPoints[i]._position = _jointStates.at(i).getPosition(); + _ragdollPoints[i].initPosition(_jointStates.at(i).getPosition()); } } enforceRagdollConstraints(); } -void SkeletonModel::moveShapesTowardJoints(float fraction) { +void SkeletonModel::moveShapesTowardJoints(float deltaTime) { + // KEEP: although we don't currently use this method we may eventually need it to help + // unravel a skelton that has become tangled in its constraints. So let's keep this + // around for a while just in case. const int numStates = _jointStates.size(); assert(_jointStates.size() == _ragdollPoints.size()); - assert(fraction >= 0.0f && fraction <= 1.0f); - if (_ragdollPoints.size() == numStates) { - float oneMinusFraction = 1.0f - fraction; - int numJoints = _jointStates.size(); - for (int i = 0; i < numJoints; ++i) { - _ragdollPoints[i]._lastPosition = _ragdollPoints[i]._position; - _ragdollPoints[i]._position = oneMinusFraction * _ragdollPoints[i]._position + fraction * _jointStates.at(i).getPosition(); + if (_ragdollPoints.size() != numStates) { + return; + } + + // fraction = 0 means keep old position, = 1 means slave 100% to target position + const float RAGDOLL_FOLLOWS_JOINTS_TIMESCALE = 0.05f; + float fraction = glm::clamp(deltaTime / RAGDOLL_FOLLOWS_JOINTS_TIMESCALE, 0.0f, 1.0f); + + float oneMinusFraction = 1.0f - fraction; + for (int i = 0; i < numStates; ++i) { + _ragdollPoints[i].initPosition(oneMinusFraction * _ragdollPoints[i]._position + + fraction * _jointStates.at(i).getPosition()); + } +} + +void SkeletonModel::updateMuscles() { + int numConstraints = _muscleConstraints.size(); + for (int i = 0; i < numConstraints; ++i) { + MuscleConstraint* constraint = _muscleConstraints[i]; + int j = constraint->getParentIndex(); + if (j == -1) { + continue; } + int k = constraint->getChildIndex(); + if (k == -1) { + continue; + } + constraint->setChildOffset(_jointStates.at(k).getPosition() - _jointStates.at(j).getPosition()); } } @@ -706,8 +769,7 @@ void SkeletonModel::computeBoundingShape(const FBXGeometry& geometry) { int parentIndex = joint.parentIndex; if (parentIndex == -1) { transforms[i] = _jointStates[i].getTransform(); - _ragdollPoints[i]._position = extractTranslation(transforms[i]); - _ragdollPoints[i]._lastPosition = _ragdollPoints[i]._position; + _ragdollPoints[i].initPosition(extractTranslation(transforms[i])); continue; } @@ -715,8 +777,7 @@ void SkeletonModel::computeBoundingShape(const FBXGeometry& geometry) { transforms[i] = transforms[parentIndex] * glm::translate(joint.translation) * joint.preTransform * glm::mat4_cast(modifiedRotation) * joint.postTransform; // setting the ragdollPoints here slams the VerletShapes into their default positions - _ragdollPoints[i]._position = extractTranslation(transforms[i]); - _ragdollPoints[i]._lastPosition = _ragdollPoints[i]._position; + _ragdollPoints[i].initPosition(extractTranslation(transforms[i])); } // compute bounding box that encloses all shapes diff --git a/interface/src/avatar/SkeletonModel.h b/interface/src/avatar/SkeletonModel.h index 7a88d890ac..5cb89f5e44 100644 --- a/interface/src/avatar/SkeletonModel.h +++ b/interface/src/avatar/SkeletonModel.h @@ -18,6 +18,7 @@ #include class Avatar; +class MuscleConstraint; /// A skeleton loaded from a model. class SkeletonModel : public Model, public Ragdoll { @@ -100,6 +101,7 @@ public: virtual void stepRagdollForward(float deltaTime); void moveShapesTowardJoints(float fraction); + void updateMuscles(); void computeBoundingShape(const FBXGeometry& geometry); void renderBoundingCollisionShapes(float alpha); @@ -144,6 +146,7 @@ private: CapsuleShape _boundingShape; glm::vec3 _boundingShapeLocalOffset; + QVector _muscleConstraints; }; #endif // hifi_SkeletonModel_h diff --git a/interface/src/models/ModelTreeRenderer.h b/interface/src/models/ModelTreeRenderer.h index b6df71565d..d69b85efe9 100644 --- a/interface/src/models/ModelTreeRenderer.h +++ b/interface/src/models/ModelTreeRenderer.h @@ -27,6 +27,7 @@ // Generic client side Octree renderer class. class ModelTreeRenderer : public OctreeRenderer, public ModelItemFBXService { + Q_OBJECT public: ModelTreeRenderer(); virtual ~ModelTreeRenderer(); @@ -56,7 +57,7 @@ public: protected: void clearModelsCache(); - Model* getModel(const ModelItem& modelItem); + Q_INVOKABLE Model* getModel(const ModelItem& modelItem); QMap _knownModelsItemModels; QMap _unknownModelsItemModels; }; diff --git a/interface/src/renderer/JointState.cpp b/interface/src/renderer/JointState.cpp index 865036df8e..2a4372401e 100644 --- a/interface/src/renderer/JointState.cpp +++ b/interface/src/renderer/JointState.cpp @@ -20,6 +20,7 @@ JointState::JointState() : _animationPriority(0.0f), _positionInParentFrame(0.0f), + _distanceToParent(0.0f), _fbxJoint(NULL), _constraint(NULL) { } @@ -29,6 +30,7 @@ JointState::JointState(const JointState& other) : _constraint(NULL) { _rotation = other._rotation; _rotationInConstrainedFrame = other._rotationInConstrainedFrame; _positionInParentFrame = other._positionInParentFrame; + _distanceToParent = other._distanceToParent; _animationPriority = other._animationPriority; _fbxJoint = other._fbxJoint; // DO NOT copy _constraint @@ -72,6 +74,7 @@ void JointState::copyState(const JointState& state) { _rotation = extractRotation(_transform); _rotationInConstrainedFrame = state._rotationInConstrainedFrame; _positionInParentFrame = state._positionInParentFrame; + _distanceToParent = state._distanceToParent; _visibleTransform = state._visibleTransform; _visibleRotation = extractRotation(_visibleTransform); @@ -82,6 +85,7 @@ void JointState::copyState(const JointState& state) { void JointState::initTransform(const glm::mat4& parentTransform) { computeTransform(parentTransform); _positionInParentFrame = glm::inverse(extractRotation(parentTransform)) * (extractTranslation(_transform) - extractTranslation(parentTransform)); + _distanceToParent = glm::length(_positionInParentFrame); } void JointState::computeTransform(const glm::mat4& parentTransform) { @@ -214,6 +218,14 @@ void JointState::setVisibleRotationInConstrainedFrame(const glm::quat& targetRot _visibleRotation = parentRotation * _fbxJoint->preRotation * _visibleRotationInConstrainedFrame * _fbxJoint->postRotation; } +const bool JointState::rotationIsDefault(const glm::quat& rotation, float tolerance) const { + glm::quat defaultRotation = _fbxJoint->rotation; + return glm::abs(rotation.x - defaultRotation.x) < tolerance && + glm::abs(rotation.y - defaultRotation.y) < tolerance && + glm::abs(rotation.z - defaultRotation.z) < tolerance && + glm::abs(rotation.w - defaultRotation.w) < tolerance; +} + const glm::vec3& JointState::getDefaultTranslationInConstrainedFrame() const { assert(_fbxJoint != NULL); return _fbxJoint->translation; diff --git a/interface/src/renderer/JointState.h b/interface/src/renderer/JointState.h index 86ee5d01b7..94811fe13c 100644 --- a/interface/src/renderer/JointState.h +++ b/interface/src/renderer/JointState.h @@ -51,6 +51,7 @@ public: glm::quat getRotationInParentFrame() const; glm::quat getVisibleRotationInParentFrame() const; const glm::vec3& getPositionInParentFrame() const { return _positionInParentFrame; } + float getDistanceToParent() const { return _distanceToParent; } int getParentIndex() const { return _fbxJoint->parentIndex; } @@ -81,6 +82,9 @@ public: void setRotationInConstrainedFrame(const glm::quat& targetRotation); void setVisibleRotationInConstrainedFrame(const glm::quat& targetRotation); const glm::quat& getRotationInConstrainedFrame() const { return _rotationInConstrainedFrame; } + const glm::quat& getVisibleRotationInConstrainedFrame() const { return _visibleRotationInConstrainedFrame; } + + const bool rotationIsDefault(const glm::quat& rotation, float tolerance = EPSILON) const; const glm::vec3& getDefaultTranslationInConstrainedFrame() const; @@ -104,6 +108,7 @@ private: glm::quat _rotation; // joint- to model-frame glm::quat _rotationInConstrainedFrame; // rotation in frame where angular constraints would be applied glm::vec3 _positionInParentFrame; // only changes when the Model is scaled + float _distanceToParent; glm::mat4 _visibleTransform; glm::quat _visibleRotation; diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index 8fac5d3d03..8c19c11ed3 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -676,12 +676,18 @@ bool Model::getJointState(int index, glm::quat& rotation) const { if (index == -1 || index >= _jointStates.size()) { return false; } - rotation = _jointStates.at(index).getRotationInConstrainedFrame(); - const glm::quat& defaultRotation = _geometry->getFBXGeometry().joints.at(index).rotation; - return glm::abs(rotation.x - defaultRotation.x) >= EPSILON || - glm::abs(rotation.y - defaultRotation.y) >= EPSILON || - glm::abs(rotation.z - defaultRotation.z) >= EPSILON || - glm::abs(rotation.w - defaultRotation.w) >= EPSILON; + const JointState& state = _jointStates.at(index); + rotation = state.getRotationInConstrainedFrame(); + return !state.rotationIsDefault(rotation); +} + +bool Model::getVisibleJointState(int index, glm::quat& rotation) const { + if (index == -1 || index >= _jointStates.size()) { + return false; + } + const JointState& state = _jointStates.at(index); + rotation = state.getVisibleRotationInConstrainedFrame(); + return !state.rotationIsDefault(rotation); } void Model::setJointState(int index, bool valid, const glm::quat& rotation, float priority) { diff --git a/interface/src/renderer/Model.h b/interface/src/renderer/Model.h index a4eae8fd9a..cbed941791 100644 --- a/interface/src/renderer/Model.h +++ b/interface/src/renderer/Model.h @@ -113,6 +113,10 @@ public: /// Fetches the joint state at the specified index. /// \return whether or not the joint state is "valid" (that is, non-default) bool getJointState(int index, glm::quat& rotation) const; + + /// Fetches the visible joint state at the specified index. + /// \return whether or not the joint state is "valid" (that is, non-default) + bool getVisibleJointState(int index, glm::quat& rotation) const; /// Sets the joint state at the specified index. void setJointState(int index, bool valid, const glm::quat& rotation = glm::quat(), float priority = 1.0f); diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index 3badde4042..c49873b5f5 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -178,7 +178,7 @@ void ApplicationOverlay::computeOculusPickRay(float x, float y, glm::vec3& direc float dist = sqrt(x * x + y * y); float z = -sqrt(1.0f - dist * dist); - glm::vec3 relativePosition = myAvatar->getHead()->calculateAverageEyePosition() + + glm::vec3 relativePosition = myAvatar->getHead()->getEyePosition() + glm::normalize(myAvatar->getOrientation() * glm::vec3(x, y, z)); //Rotate the UI pick ray by the avatar orientation @@ -274,7 +274,7 @@ QPoint ApplicationOverlay::getPalmClickLocation(const PalmData *palm) const { MyAvatar* myAvatar = application->getAvatar(); glm::vec3 tip = myAvatar->getLaserPointerTipPosition(palm); - glm::vec3 eyePos = myAvatar->getHead()->calculateAverageEyePosition(); + glm::vec3 eyePos = myAvatar->getHead()->getEyePosition(); glm::quat orientation = glm::inverse(myAvatar->getOrientation()); glm::vec3 dir = orientation * glm::normalize(application->getCamera()->getPosition() - tip); //direction of ray goes towards camera glm::vec3 tipPos = orientation * (tip - eyePos); @@ -331,7 +331,7 @@ bool ApplicationOverlay::calculateRayUICollisionPoint(const glm::vec3& position, glm::quat orientation = myAvatar->getOrientation(); - glm::vec3 relativePosition = orientation * (position - myAvatar->getHead()->calculateAverageEyePosition()); + glm::vec3 relativePosition = orientation * (position - myAvatar->getHead()->getEyePosition()); glm::vec3 relativeDirection = orientation * direction; float t; @@ -375,7 +375,7 @@ void ApplicationOverlay::displayOverlayTextureOculus(Camera& whichCamera) { glPushMatrix(); const glm::quat& orientation = myAvatar->getOrientation(); - const glm::vec3& position = myAvatar->getHead()->calculateAverageEyePosition(); + const glm::vec3& position = myAvatar->getHead()->getEyePosition(); glm::mat4 rotation = glm::toMat4(orientation); @@ -1022,6 +1022,8 @@ void ApplicationOverlay::renderAudioMeter() { audio->renderScope(glWidget->width(), glWidget->height()); + audio->renderStats(WHITE_TEXT, glWidget->width(), glWidget->height()); + glBegin(GL_QUADS); if (isClipping) { glColor3f(1, 0, 0); @@ -1210,7 +1212,7 @@ void ApplicationOverlay::renderTexturedHemisphere() { Application* application = Application::getInstance(); MyAvatar* myAvatar = application->getAvatar(); const glm::quat& orientation = myAvatar->getOrientation(); - const glm::vec3& position = myAvatar->getHead()->calculateAverageEyePosition(); + const glm::vec3& position = myAvatar->getHead()->getEyePosition(); glm::mat4 rotation = glm::toMat4(orientation); diff --git a/interface/src/ui/ModelsBrowser.cpp b/interface/src/ui/ModelsBrowser.cpp index 41dea69581..5e107677f0 100644 --- a/interface/src/ui/ModelsBrowser.cpp +++ b/interface/src/ui/ModelsBrowser.cpp @@ -182,7 +182,7 @@ ModelHandler::ModelHandler(ModelType modelsType, QWidget* parent) : QObject(parent), _initiateExit(false), _type(modelsType), - _nameFilter(".*(fst|fbx|FST|FBX)") + _nameFilter(".*fst") { // set headers data QStringList headerData; @@ -253,7 +253,6 @@ void ModelHandler::downloadFinished() { parseHeaders(reply); } reply->deleteLater(); - sender()->deleteLater(); } void ModelHandler::queryNewFiles(QString marker) { diff --git a/interface/src/ui/RunningScriptsWidget.cpp b/interface/src/ui/RunningScriptsWidget.cpp index 8a7ebcbfd4..5bcb1a85de 100644 --- a/interface/src/ui/RunningScriptsWidget.cpp +++ b/interface/src/ui/RunningScriptsWidget.cpp @@ -23,7 +23,6 @@ #include "Menu.h" #include "ScriptsModel.h" - RunningScriptsWidget::RunningScriptsWidget(QWidget* parent) : FramelessDialog(parent, 0, POSITION_LEFT), ui(new Ui::RunningScriptsWidget), @@ -104,13 +103,21 @@ void RunningScriptsWidget::setRunningScripts(const QStringList& list) { delete widget->widget(); delete widget; } + QHash hash; const int CLOSE_ICON_HEIGHT = 12; for (int i = 0; i < list.size(); i++) { + if (!hash.contains(list.at(i))) { + hash.insert(list.at(i), 1); + } QWidget* row = new QWidget(ui->scrollAreaWidgetContents); row->setLayout(new QHBoxLayout(row)); QUrl url = QUrl(list.at(i)); QLabel* name = new QLabel(url.fileName(), row); + if (hash.find(list.at(i)).value() != 1) { + name->setText(name->text() + "(" + QString::number(hash.find(list.at(i)).value()) + ")"); + } + ++hash[list.at(i)]; QPushButton* closeButton = new QPushButton(row); closeButton->setFlat(true); closeButton->setIcon( diff --git a/interface/src/ui/Stats.cpp b/interface/src/ui/Stats.cpp index f82bc7ba17..77598e0c5e 100644 --- a/interface/src/ui/Stats.cpp +++ b/interface/src/ui/Stats.cpp @@ -278,9 +278,8 @@ void Stats::display( Audio* audio = Application::getInstance()->getAudio(); - const QHash& audioMixerInjectedStreamAudioStatsMap = audio->getAudioMixerInjectedStreamAudioStatsMap(); - lines = _expanded ? 13 + (audioMixerInjectedStreamAudioStatsMap.size() + 2) * 3 : 3; + lines = _expanded ? 4 : 3; drawBackground(backgroundColor, horizontalOffset, 0, _pingStatsWidth, lines * STATS_PELS_PER_LINE + 10); horizontalOffset += 5; @@ -313,128 +312,6 @@ void Stats::display( verticalOffset += STATS_PELS_PER_LINE; drawText(horizontalOffset, verticalOffset, scale, rotation, font, voxelMaxPing, color); - - char inputAudioLabelString[] = "Input: avail_avg_10s/avail"; - - verticalOffset += STATS_PELS_PER_LINE; - drawText(horizontalOffset, verticalOffset, scale, rotation, font, inputAudioLabelString, color); - - char inputAudioStatsString[512]; - sprintf(inputAudioStatsString, " %d/%d", audio->getInputRingBufferAverageFramesAvailable(), - audio->getInputRingBufferFramesAvailable()); - - verticalOffset += STATS_PELS_PER_LINE; - drawText(horizontalOffset, verticalOffset, scale, rotation, font, inputAudioStatsString, color); - - char audioMixerStatsLabelString[] = "AudioMixer stats:"; - char streamStatsFormatLabelString[] = "lost%/lost_30s%"; - char streamStatsFormatLabelString2[] = "desired/avail_avg_10s/avail"; - char streamStatsFormatLabelString3[] = "gaps: min/max/avg, starv/ovfl"; - char streamStatsFormatLabelString4[] = "gaps_30s: (same), notmix/sdrop"; - - verticalOffset += STATS_PELS_PER_LINE; - drawText(horizontalOffset, verticalOffset, scale, rotation, font, audioMixerStatsLabelString, color); - verticalOffset += STATS_PELS_PER_LINE; - drawText(horizontalOffset, verticalOffset, scale, rotation, font, streamStatsFormatLabelString, color); - verticalOffset += STATS_PELS_PER_LINE; - drawText(horizontalOffset, verticalOffset, scale, rotation, font, streamStatsFormatLabelString2, color); - verticalOffset += STATS_PELS_PER_LINE; - drawText(horizontalOffset, verticalOffset, scale, rotation, font, streamStatsFormatLabelString3, color); - verticalOffset += STATS_PELS_PER_LINE; - drawText(horizontalOffset, verticalOffset, scale, rotation, font, streamStatsFormatLabelString4, color); - - char downstreamLabelString[] = " Downstream:"; - verticalOffset += STATS_PELS_PER_LINE; - drawText(horizontalOffset, verticalOffset, scale, rotation, font, downstreamLabelString, color); - - char downstreamAudioStatsString[512]; - - AudioStreamStats downstreamAudioStreamStats = audio->getDownstreamAudioStreamStats(); - - sprintf(downstreamAudioStatsString, " mix: %.2f%%/%.2f%%, %u/%u+%d/%u+%d", downstreamAudioStreamStats._packetStreamStats.getLostRate()*100.0f, - downstreamAudioStreamStats._packetStreamWindowStats.getLostRate() * 100.0f, - downstreamAudioStreamStats._ringBufferDesiredJitterBufferFrames, downstreamAudioStreamStats._ringBufferFramesAvailableAverage, - audio->getOutputRingBufferAverageFramesAvailable(), - downstreamAudioStreamStats._ringBufferFramesAvailable, audio->getOutputRingBufferFramesAvailable()); - - verticalOffset += STATS_PELS_PER_LINE; - drawText(horizontalOffset, verticalOffset, scale, rotation, font, downstreamAudioStatsString, color); - - sprintf(downstreamAudioStatsString, " %s/%s/%s, %u/%u", formatUsecTime(downstreamAudioStreamStats._timeGapMin).toLatin1().data(), - formatUsecTime(downstreamAudioStreamStats._timeGapMax).toLatin1().data(), - formatUsecTime(downstreamAudioStreamStats._timeGapAverage).toLatin1().data(), - downstreamAudioStreamStats._ringBufferStarveCount, downstreamAudioStreamStats._ringBufferOverflowCount); - - verticalOffset += STATS_PELS_PER_LINE; - drawText(horizontalOffset, verticalOffset, scale, rotation, font, downstreamAudioStatsString, color); - - sprintf(downstreamAudioStatsString, " %s/%s/%s, %u/?", formatUsecTime(downstreamAudioStreamStats._timeGapWindowMin).toLatin1().data(), - formatUsecTime(downstreamAudioStreamStats._timeGapWindowMax).toLatin1().data(), - formatUsecTime(downstreamAudioStreamStats._timeGapWindowAverage).toLatin1().data(), - downstreamAudioStreamStats._ringBufferConsecutiveNotMixedCount); - - verticalOffset += STATS_PELS_PER_LINE; - drawText(horizontalOffset, verticalOffset, scale, rotation, font, downstreamAudioStatsString, color); - - - char upstreamLabelString[] = " Upstream:"; - verticalOffset += STATS_PELS_PER_LINE; - drawText(horizontalOffset, verticalOffset, scale, rotation, font, upstreamLabelString, color); - - char upstreamAudioStatsString[512]; - - const AudioStreamStats& audioMixerAvatarAudioStreamStats = audio->getAudioMixerAvatarStreamAudioStats(); - - sprintf(upstreamAudioStatsString, " mic: %.2f%%/%.2f%%, %u/%u/%u", audioMixerAvatarAudioStreamStats._packetStreamStats.getLostRate()*100.0f, - audioMixerAvatarAudioStreamStats._packetStreamWindowStats.getLostRate() * 100.0f, - audioMixerAvatarAudioStreamStats._ringBufferDesiredJitterBufferFrames, audioMixerAvatarAudioStreamStats._ringBufferFramesAvailableAverage, - audioMixerAvatarAudioStreamStats._ringBufferFramesAvailable); - - verticalOffset += STATS_PELS_PER_LINE; - drawText(horizontalOffset, verticalOffset, scale, rotation, font, upstreamAudioStatsString, color); - - sprintf(upstreamAudioStatsString, " %s/%s/%s, %u/%u", formatUsecTime(audioMixerAvatarAudioStreamStats._timeGapMin).toLatin1().data(), - formatUsecTime(audioMixerAvatarAudioStreamStats._timeGapMax).toLatin1().data(), - formatUsecTime(audioMixerAvatarAudioStreamStats._timeGapAverage).toLatin1().data(), - audioMixerAvatarAudioStreamStats._ringBufferStarveCount, audioMixerAvatarAudioStreamStats._ringBufferOverflowCount); - - verticalOffset += STATS_PELS_PER_LINE; - drawText(horizontalOffset, verticalOffset, scale, rotation, font, upstreamAudioStatsString, color); - - sprintf(upstreamAudioStatsString, " %s/%s/%s, %u/%u", formatUsecTime(audioMixerAvatarAudioStreamStats._timeGapWindowMin).toLatin1().data(), - formatUsecTime(audioMixerAvatarAudioStreamStats._timeGapWindowMax).toLatin1().data(), - formatUsecTime(audioMixerAvatarAudioStreamStats._timeGapWindowAverage).toLatin1().data(), - audioMixerAvatarAudioStreamStats._ringBufferConsecutiveNotMixedCount, audioMixerAvatarAudioStreamStats._ringBufferSilentFramesDropped); - - verticalOffset += STATS_PELS_PER_LINE; - drawText(horizontalOffset, verticalOffset, scale, rotation, font, upstreamAudioStatsString, color); - - foreach(const AudioStreamStats& injectedStreamAudioStats, audioMixerInjectedStreamAudioStatsMap) { - - sprintf(upstreamAudioStatsString, " inj: %.2f%%/%.2f%%, %u/%u/%u", injectedStreamAudioStats._packetStreamStats.getLostRate()*100.0f, - injectedStreamAudioStats._packetStreamWindowStats.getLostRate() * 100.0f, - injectedStreamAudioStats._ringBufferDesiredJitterBufferFrames, injectedStreamAudioStats._ringBufferFramesAvailableAverage, - injectedStreamAudioStats._ringBufferFramesAvailable); - - verticalOffset += STATS_PELS_PER_LINE; - drawText(horizontalOffset, verticalOffset, scale, rotation, font, upstreamAudioStatsString, color); - - sprintf(upstreamAudioStatsString, " %s/%s/%s, %u/%u", formatUsecTime(injectedStreamAudioStats._timeGapMin).toLatin1().data(), - formatUsecTime(injectedStreamAudioStats._timeGapMax).toLatin1().data(), - formatUsecTime(injectedStreamAudioStats._timeGapAverage).toLatin1().data(), - injectedStreamAudioStats._ringBufferStarveCount, injectedStreamAudioStats._ringBufferOverflowCount); - - verticalOffset += STATS_PELS_PER_LINE; - drawText(horizontalOffset, verticalOffset, scale, rotation, font, upstreamAudioStatsString, color); - - sprintf(upstreamAudioStatsString, " %s/%s/%s, %u/%u", formatUsecTime(injectedStreamAudioStats._timeGapWindowMin).toLatin1().data(), - formatUsecTime(injectedStreamAudioStats._timeGapWindowMax).toLatin1().data(), - formatUsecTime(injectedStreamAudioStats._timeGapWindowAverage).toLatin1().data(), - injectedStreamAudioStats._ringBufferConsecutiveNotMixedCount, injectedStreamAudioStats._ringBufferSilentFramesDropped); - - verticalOffset += STATS_PELS_PER_LINE; - drawText(horizontalOffset, verticalOffset, scale, rotation, font, upstreamAudioStatsString, color); - } } verticalOffset = 0; diff --git a/libraries/audio/src/InjectedAudioRingBuffer.cpp b/libraries/audio/src/InjectedAudioRingBuffer.cpp index da2d8336de..4723bca906 100644 --- a/libraries/audio/src/InjectedAudioRingBuffer.cpp +++ b/libraries/audio/src/InjectedAudioRingBuffer.cpp @@ -30,7 +30,7 @@ InjectedAudioRingBuffer::InjectedAudioRingBuffer(const QUuid& streamIdentifier, const uchar MAX_INJECTOR_VOLUME = 255; -int InjectedAudioRingBuffer::parseData(const QByteArray& packet, int packetsSkipped) { +int InjectedAudioRingBuffer::parseDataAndHandleDroppedPackets(const QByteArray& packet, int packetsSkipped) { frameReceivedUpdateTimingStats(); // setup a data stream to read from this packet diff --git a/libraries/audio/src/InjectedAudioRingBuffer.h b/libraries/audio/src/InjectedAudioRingBuffer.h index f226d99b12..4a1f8b5292 100644 --- a/libraries/audio/src/InjectedAudioRingBuffer.h +++ b/libraries/audio/src/InjectedAudioRingBuffer.h @@ -20,7 +20,7 @@ class InjectedAudioRingBuffer : public PositionalAudioRingBuffer { public: InjectedAudioRingBuffer(const QUuid& streamIdentifier = QUuid(), bool dynamicJitterBuffer = false); - int parseData(const QByteArray& packet, int packetsSkipped = 0); + int parseDataAndHandleDroppedPackets(const QByteArray& packet, int packetsSkipped); const QUuid& getStreamIdentifier() const { return _streamIdentifier; } float getRadius() const { return _radius; } diff --git a/libraries/audio/src/PositionalAudioRingBuffer.h b/libraries/audio/src/PositionalAudioRingBuffer.h index f0d6aff80b..0b14a12858 100644 --- a/libraries/audio/src/PositionalAudioRingBuffer.h +++ b/libraries/audio/src/PositionalAudioRingBuffer.h @@ -45,7 +45,7 @@ public: PositionalAudioRingBuffer(PositionalAudioRingBuffer::Type type, bool isStereo = false, bool dynamicJitterBuffers = false); - virtual int parseData(const QByteArray& packet, int packetsSkipped = 0) = 0; + virtual int parseDataAndHandleDroppedPackets(const QByteArray& packet, int packetsSkipped) = 0; int parsePositionalData(const QByteArray& positionalByteArray); int parseListenModeData(const QByteArray& listenModeByteArray); diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 6cfa4ba488..c3ea2f8b50 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -43,6 +43,7 @@ AvatarData::AvatarData() : _handState(0), _keyState(NO_KEY_DOWN), _isChatCirclingEnabled(false), + _forceFaceshiftConnected(false), _hasNewJointRotations(true), _headData(NULL), _handData(NULL), @@ -80,6 +81,9 @@ QByteArray AvatarData::toByteArray() { // lazily allocate memory for HeadData in case we're not an Avatar instance if (!_headData) { _headData = new HeadData(this); + if (_forceFaceshiftConnected) { + _headData->_isFaceshiftConnected = true; + } } QByteArray avatarDataByteArray; diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 433a52035b..008aecc817 100755 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -206,6 +206,10 @@ public: Q_INVOKABLE virtual QStringList getJointNames() const { return _jointNames; } + Q_INVOKABLE void setBlendshape(QString name, float val) { _headData->setBlendshape(name, val); } + + void setForceFaceshiftConnected(bool connected) { _forceFaceshiftConnected = connected; } + // key state void setKeyState(KeyState s) { _keyState = s; } KeyState keyState() const { return _keyState; } @@ -300,7 +304,7 @@ protected: std::string _chatMessage; bool _isChatCirclingEnabled; - + bool _forceFaceshiftConnected; bool _hasNewJointRotations; // set in AvatarData, cleared in Avatar HeadData* _headData; diff --git a/libraries/avatars/src/HeadData.cpp b/libraries/avatars/src/HeadData.cpp index c691a2c28a..b29277ddeb 100644 --- a/libraries/avatars/src/HeadData.cpp +++ b/libraries/avatars/src/HeadData.cpp @@ -16,6 +16,8 @@ #include "AvatarData.h" #include "HeadData.h" +#include "../fbx/src/FBXReader.h" + HeadData::HeadData(AvatarData* owningAvatar) : _baseYaw(0.0f), _basePitch(0.0f), @@ -52,6 +54,26 @@ void HeadData::setOrientation(const glm::quat& orientation) { _baseRoll = eulers.z; } +void HeadData::setBlendshape(QString name, float val) { + static bool hasInitializedLookupMap = false; + static QMap blendshapeLookupMap; + //Lazily construct a lookup map from the blendshapes + if (!hasInitializedLookupMap) { + for (int i = 0; i < NUM_FACESHIFT_BLENDSHAPES; i++) { + blendshapeLookupMap[FACESHIFT_BLENDSHAPES[i]] = i; + } + } + + //Check to see if the named blendshape exists, and then set its value if it does + QMap::iterator it = blendshapeLookupMap.find(name); + if (it != blendshapeLookupMap.end()) { + if (_blendshapeCoefficients.size() <= it.value()) { + _blendshapeCoefficients.resize(it.value() + 1); + } + _blendshapeCoefficients[it.value()] = val; + } +} + void HeadData::addYaw(float yaw) { setBaseYaw(_baseYaw + yaw); } diff --git a/libraries/avatars/src/HeadData.h b/libraries/avatars/src/HeadData.h index 9e2920ae85..782386c649 100644 --- a/libraries/avatars/src/HeadData.h +++ b/libraries/avatars/src/HeadData.h @@ -54,6 +54,7 @@ public: float getAudioAverageLoudness() const { return _audioAverageLoudness; } void setAudioAverageLoudness(float audioAverageLoudness) { _audioAverageLoudness = audioAverageLoudness; } + void setBlendshape(QString name, float val); const QVector& getBlendshapeCoefficients() const { return _blendshapeCoefficients; } float getPupilDilation() const { return _pupilDilation; } diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index f366b6a5c3..cf726800a1 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -577,6 +577,8 @@ const char* FACESHIFT_BLENDSHAPES[] = { "" }; +const int NUM_FACESHIFT_BLENDSHAPES = sizeof(FACESHIFT_BLENDSHAPES) / sizeof(char*); + const char* HUMANIK_JOINTS[] = { "RightHand", "RightForeArm", diff --git a/libraries/fbx/src/FBXReader.h b/libraries/fbx/src/FBXReader.h index c336252574..b5340978c1 100644 --- a/libraries/fbx/src/FBXReader.h +++ b/libraries/fbx/src/FBXReader.h @@ -29,6 +29,8 @@ typedef QList FBXNodeList; /// The names of the blendshapes expected by Faceshift, terminated with an empty string. extern const char* FACESHIFT_BLENDSHAPES[]; +/// The size of FACESHIFT_BLENDSHAPES +extern const int NUM_FACESHIFT_BLENDSHAPES; /// The names of the joints in the Maya HumanIK rig, terminated with an empty string. extern const char* HUMANIK_JOINTS[]; diff --git a/libraries/models/src/ModelsScriptingInterface.cpp b/libraries/models/src/ModelsScriptingInterface.cpp index 634039f949..d2c3ed1ba7 100644 --- a/libraries/models/src/ModelsScriptingInterface.cpp +++ b/libraries/models/src/ModelsScriptingInterface.cpp @@ -70,7 +70,7 @@ ModelItemProperties ModelsScriptingInterface::getModelProperties(ModelItemID mod if (_modelTree) { _modelTree->lockForRead(); ModelItem* model = const_cast(_modelTree->findModelByID(identity.id, true)); - if (model) { + if (model && _modelTree->getGeometryForModel(*model)) { model->setSittingPoints(_modelTree->getGeometryForModel(*model)->sittingPoints); results.copyFromModelItem(*model); } else { diff --git a/libraries/octree/src/OctreeHeadlessViewer.cpp b/libraries/octree/src/OctreeHeadlessViewer.cpp index 2372d85b0e..2f21c7a899 100644 --- a/libraries/octree/src/OctreeHeadlessViewer.cpp +++ b/libraries/octree/src/OctreeHeadlessViewer.cpp @@ -22,7 +22,7 @@ OctreeHeadlessViewer::OctreeHeadlessViewer() : _viewFrustum.setFieldOfView(DEFAULT_FIELD_OF_VIEW_DEGREES); _viewFrustum.setAspectRatio(DEFAULT_ASPECT_RATIO); _viewFrustum.setNearClip(DEFAULT_NEAR_CLIP); - _viewFrustum.setFarClip(TREE_SCALE); + _viewFrustum.setFarClip(DEFAULT_FAR_CLIP); } OctreeHeadlessViewer::~OctreeHeadlessViewer() { diff --git a/libraries/octree/src/ViewFrustum.cpp b/libraries/octree/src/ViewFrustum.cpp index c06cb2ea88..9552129e29 100644 --- a/libraries/octree/src/ViewFrustum.cpp +++ b/libraries/octree/src/ViewFrustum.cpp @@ -36,8 +36,8 @@ ViewFrustum::ViewFrustum() : _height(1.0f), _fieldOfView(0.0), _aspectRatio(1.0f), - _nearClip(0.1f), - _farClip(500.0f), + _nearClip(DEFAULT_NEAR_CLIP), + _farClip(DEFAULT_FAR_CLIP), _focalLength(0.25f), _keyholeRadius(DEFAULT_KEYHOLE_RADIUS), _farTopLeft(0,0,0), diff --git a/libraries/octree/src/ViewFrustum.h b/libraries/octree/src/ViewFrustum.h index bed8d9c18c..a23ec2af92 100644 --- a/libraries/octree/src/ViewFrustum.h +++ b/libraries/octree/src/ViewFrustum.h @@ -28,7 +28,7 @@ const float DEFAULT_FIELD_OF_VIEW_DEGREES = 90.0f; const float DEFAULT_REAL_WORLD_FIELD_OF_VIEW_DEGREES = 30.f; const float DEFAULT_ASPECT_RATIO = 16.f/9.f; const float DEFAULT_NEAR_CLIP = 0.08f; -const float DEFAULT_FAR_CLIP = 50.0f * TREE_SCALE; +const float DEFAULT_FAR_CLIP = TREE_SCALE; class ViewFrustum { public: diff --git a/libraries/script-engine/src/ArrayBufferClass.cpp b/libraries/script-engine/src/ArrayBufferClass.cpp index ab33b5ffe7..b84188f707 100644 --- a/libraries/script-engine/src/ArrayBufferClass.cpp +++ b/libraries/script-engine/src/ArrayBufferClass.cpp @@ -23,8 +23,8 @@ static const QString CLASS_NAME = "ArrayBuffer"; Q_DECLARE_METATYPE(QByteArray*) ArrayBufferClass::ArrayBufferClass(ScriptEngine* scriptEngine) : -QObject(scriptEngine->getEngine()), -QScriptClass(scriptEngine->getEngine()), +QObject(scriptEngine), +QScriptClass(scriptEngine), _scriptEngine(scriptEngine) { qScriptRegisterMetaType(engine(), toScriptValue, fromScriptValue); QScriptValue global = engine()->globalObject(); diff --git a/libraries/script-engine/src/ArrayBufferViewClass.cpp b/libraries/script-engine/src/ArrayBufferViewClass.cpp index aad2e6add7..cf776ed834 100644 --- a/libraries/script-engine/src/ArrayBufferViewClass.cpp +++ b/libraries/script-engine/src/ArrayBufferViewClass.cpp @@ -14,8 +14,8 @@ Q_DECLARE_METATYPE(QByteArray*) ArrayBufferViewClass::ArrayBufferViewClass(ScriptEngine* scriptEngine) : -QObject(scriptEngine->getEngine()), -QScriptClass(scriptEngine->getEngine()), +QObject(scriptEngine), +QScriptClass(scriptEngine), _scriptEngine(scriptEngine) { // Save string handles for quick lookup _bufferName = engine()->toStringHandle(BUFFER_PROPERTY_NAME.toLatin1()); diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index fab21ea928..df66fa44d5 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -87,7 +87,6 @@ ScriptEngine::ScriptEngine(const QString& scriptContents, const QString& fileNam _isFinished(false), _isRunning(false), _isInitialized(false), - _engine(), _isAvatar(false), _avatarIdentityTimer(NULL), _avatarBillboardTimer(NULL), @@ -113,7 +112,6 @@ ScriptEngine::ScriptEngine(const QUrl& scriptURL, _isFinished(false), _isRunning(false), _isInitialized(false), - _engine(), _isAvatar(false), _avatarIdentityTimer(NULL), _avatarBillboardTimer(NULL), @@ -194,7 +192,7 @@ void ScriptEngine::setAvatarData(AvatarData* avatarData, const QString& objectNa _avatarData = avatarData; // remove the old Avatar property, if it exists - _engine.globalObject().setProperty(objectName, QScriptValue()); + globalObject().setProperty(objectName, QScriptValue()); // give the script engine the new Avatar script property registerGlobalObject(objectName, _avatarData); @@ -202,7 +200,7 @@ void ScriptEngine::setAvatarData(AvatarData* avatarData, const QString& objectNa void ScriptEngine::setAvatarHashMap(AvatarHashMap* avatarHashMap, const QString& objectName) { // remove the old Avatar property, if it exists - _engine.globalObject().setProperty(objectName, QScriptValue()); + globalObject().setProperty(objectName, QScriptValue()); // give the script engine the new avatar hash map registerGlobalObject(objectName, avatarHashMap); @@ -231,48 +229,48 @@ void ScriptEngine::init() { _particlesScriptingInterface.init(); // register various meta-types - registerMetaTypes(&_engine); - registerMIDIMetaTypes(&_engine); - registerVoxelMetaTypes(&_engine); - registerEventTypes(&_engine); - registerMenuItemProperties(&_engine); - registerAnimationTypes(&_engine); - registerAvatarTypes(&_engine); - Bitstream::registerTypes(&_engine); + registerMetaTypes(this); + registerMIDIMetaTypes(this); + registerVoxelMetaTypes(this); + registerEventTypes(this); + registerMenuItemProperties(this); + registerAnimationTypes(this); + registerAvatarTypes(this); + Bitstream::registerTypes(this); - qScriptRegisterMetaType(&_engine, ParticlePropertiesToScriptValue, ParticlePropertiesFromScriptValue); - qScriptRegisterMetaType(&_engine, ParticleIDtoScriptValue, ParticleIDfromScriptValue); - qScriptRegisterSequenceMetaType >(&_engine); + qScriptRegisterMetaType(this, ParticlePropertiesToScriptValue, ParticlePropertiesFromScriptValue); + qScriptRegisterMetaType(this, ParticleIDtoScriptValue, ParticleIDfromScriptValue); + qScriptRegisterSequenceMetaType >(this); - qScriptRegisterMetaType(&_engine, ModelItemPropertiesToScriptValue, ModelItemPropertiesFromScriptValue); - qScriptRegisterMetaType(&_engine, ModelItemIDtoScriptValue, ModelItemIDfromScriptValue); - qScriptRegisterMetaType(&_engine, RayToModelIntersectionResultToScriptValue, RayToModelIntersectionResultFromScriptValue); - qScriptRegisterSequenceMetaType >(&_engine); + qScriptRegisterMetaType(this, ModelItemPropertiesToScriptValue, ModelItemPropertiesFromScriptValue); + qScriptRegisterMetaType(this, ModelItemIDtoScriptValue, ModelItemIDfromScriptValue); + qScriptRegisterMetaType(this, RayToModelIntersectionResultToScriptValue, RayToModelIntersectionResultFromScriptValue); + qScriptRegisterSequenceMetaType >(this); - qScriptRegisterSequenceMetaType >(&_engine); - qScriptRegisterSequenceMetaType >(&_engine); - qScriptRegisterSequenceMetaType >(&_engine); + qScriptRegisterSequenceMetaType >(this); + qScriptRegisterSequenceMetaType >(this); + qScriptRegisterSequenceMetaType >(this); - QScriptValue xmlHttpRequestConstructorValue = _engine.newFunction(XMLHttpRequestClass::constructor); - _engine.globalObject().setProperty("XMLHttpRequest", xmlHttpRequestConstructorValue); + QScriptValue xmlHttpRequestConstructorValue = newFunction(XMLHttpRequestClass::constructor); + globalObject().setProperty("XMLHttpRequest", xmlHttpRequestConstructorValue); - QScriptValue printConstructorValue = _engine.newFunction(debugPrint); - _engine.globalObject().setProperty("print", printConstructorValue); + QScriptValue printConstructorValue = newFunction(debugPrint); + globalObject().setProperty("print", printConstructorValue); - QScriptValue soundConstructorValue = _engine.newFunction(soundConstructor); - QScriptValue soundMetaObject = _engine.newQMetaObject(&Sound::staticMetaObject, soundConstructorValue); - _engine.globalObject().setProperty("Sound", soundMetaObject); + QScriptValue soundConstructorValue = newFunction(soundConstructor); + QScriptValue soundMetaObject = newQMetaObject(&Sound::staticMetaObject, soundConstructorValue); + globalObject().setProperty("Sound", soundMetaObject); - QScriptValue injectionOptionValue = _engine.scriptValueFromQMetaObject(); - _engine.globalObject().setProperty("AudioInjectionOptions", injectionOptionValue); + QScriptValue injectionOptionValue = scriptValueFromQMetaObject(); + globalObject().setProperty("AudioInjectionOptions", injectionOptionValue); - QScriptValue localVoxelsValue = _engine.scriptValueFromQMetaObject(); - _engine.globalObject().setProperty("LocalVoxels", localVoxelsValue); + QScriptValue localVoxelsValue = scriptValueFromQMetaObject(); + globalObject().setProperty("LocalVoxels", localVoxelsValue); - qScriptRegisterMetaType(&_engine, injectorToScriptValue, injectorFromScriptValue); - qScriptRegisterMetaType( &_engine, injectorToScriptValueInputController, injectorFromScriptValueInputController); + qScriptRegisterMetaType(this, injectorToScriptValue, injectorFromScriptValue); + qScriptRegisterMetaType( this, injectorToScriptValueInputController, injectorFromScriptValueInputController); - qScriptRegisterMetaType(&_engine, animationDetailsToScriptValue, animationDetailsFromScriptValue); + qScriptRegisterMetaType(this, animationDetailsToScriptValue, animationDetailsFromScriptValue); registerGlobalObject("Script", this); registerGlobalObject("Audio", &_audioScriptingInterface); @@ -287,15 +285,14 @@ void ScriptEngine::init() { registerGlobalObject("Voxels", &_voxelsScriptingInterface); // constants - QScriptValue globalObject = _engine.globalObject(); - globalObject.setProperty("TREE_SCALE", _engine.newVariant(QVariant(TREE_SCALE))); - globalObject.setProperty("COLLISION_GROUP_ENVIRONMENT", _engine.newVariant(QVariant(COLLISION_GROUP_ENVIRONMENT))); - globalObject.setProperty("COLLISION_GROUP_AVATARS", _engine.newVariant(QVariant(COLLISION_GROUP_AVATARS))); - globalObject.setProperty("COLLISION_GROUP_VOXELS", _engine.newVariant(QVariant(COLLISION_GROUP_VOXELS))); - globalObject.setProperty("COLLISION_GROUP_PARTICLES", _engine.newVariant(QVariant(COLLISION_GROUP_PARTICLES))); + globalObject().setProperty("TREE_SCALE", newVariant(QVariant(TREE_SCALE))); + globalObject().setProperty("COLLISION_GROUP_ENVIRONMENT", newVariant(QVariant(COLLISION_GROUP_ENVIRONMENT))); + globalObject().setProperty("COLLISION_GROUP_AVATARS", newVariant(QVariant(COLLISION_GROUP_AVATARS))); + globalObject().setProperty("COLLISION_GROUP_VOXELS", newVariant(QVariant(COLLISION_GROUP_VOXELS))); + globalObject().setProperty("COLLISION_GROUP_PARTICLES", newVariant(QVariant(COLLISION_GROUP_PARTICLES))); - globalObject.setProperty("AVATAR_MOTION_OBEY_LOCAL_GRAVITY", _engine.newVariant(QVariant(AVATAR_MOTION_OBEY_LOCAL_GRAVITY))); - globalObject.setProperty("AVATAR_MOTION_OBEY_ENVIRONMENTAL_GRAVITY", _engine.newVariant(QVariant(AVATAR_MOTION_OBEY_ENVIRONMENTAL_GRAVITY))); + globalObject().setProperty("AVATAR_MOTION_OBEY_LOCAL_GRAVITY", newVariant(QVariant(AVATAR_MOTION_OBEY_LOCAL_GRAVITY))); + globalObject().setProperty("AVATAR_MOTION_OBEY_ENVIRONMENTAL_GRAVITY", newVariant(QVariant(AVATAR_MOTION_OBEY_ENVIRONMENTAL_GRAVITY))); // let the VoxelPacketSender know how frequently we plan to call it _voxelsScriptingInterface.getVoxelPacketSender()->setProcessCallIntervalHint(SCRIPT_DATA_CALLBACK_USECS); @@ -304,8 +301,8 @@ void ScriptEngine::init() { QScriptValue ScriptEngine::registerGlobalObject(const QString& name, QObject* object) { if (object) { - QScriptValue value = _engine.newQObject(object); - _engine.globalObject().setProperty(name, value); + QScriptValue value = newQObject(object); + globalObject().setProperty(name, value); return value; } return QScriptValue::NullValue; @@ -313,15 +310,15 @@ QScriptValue ScriptEngine::registerGlobalObject(const QString& name, QObject* ob void ScriptEngine::registerGetterSetter(const QString& name, QScriptEngine::FunctionSignature getter, QScriptEngine::FunctionSignature setter, QScriptValue object) { - QScriptValue setterFunction = _engine.newFunction(setter, 1); - QScriptValue getterFunction = _engine.newFunction(getter); + QScriptValue setterFunction = newFunction(setter, 1); + QScriptValue getterFunction = newFunction(getter); if (!object.isNull()) { object.setProperty(name, setterFunction, QScriptValue::PropertySetter); object.setProperty(name, getterFunction, QScriptValue::PropertyGetter); } else { - _engine.globalObject().setProperty(name, setterFunction, QScriptValue::PropertySetter); - _engine.globalObject().setProperty(name, getterFunction, QScriptValue::PropertyGetter); + globalObject().setProperty(name, setterFunction, QScriptValue::PropertySetter); + globalObject().setProperty(name, getterFunction, QScriptValue::PropertyGetter); } } @@ -330,25 +327,24 @@ void ScriptEngine::evaluate() { init(); } - QScriptValue result = _engine.evaluate(_scriptContents); + QScriptValue result = evaluate(_scriptContents); - if (_engine.hasUncaughtException()) { - int line = _engine.uncaughtExceptionLineNumber(); + if (hasUncaughtException()) { + int line = uncaughtExceptionLineNumber(); qDebug() << "Uncaught exception at (" << _fileNameString << ") line" << line << ":" << result.toString(); emit errorMessage("Uncaught exception at (" + _fileNameString + ") line" + QString::number(line) + ":" + result.toString()); - _engine.clearExceptions(); + clearExceptions(); } } QScriptValue ScriptEngine::evaluate(const QString& program, const QString& fileName, int lineNumber) { - QScriptValue result = _engine.evaluate(program, fileName, lineNumber); - bool hasUncaughtException = _engine.hasUncaughtException(); - if (hasUncaughtException) { - int line = _engine.uncaughtExceptionLineNumber(); + QScriptValue result = QScriptEngine::evaluate(program, fileName, lineNumber); + if (hasUncaughtException()) { + int line = uncaughtExceptionLineNumber(); qDebug() << "Uncaught exception at (" << _fileNameString << ") line" << line << ": " << result.toString(); } - emit evaluationFinished(result, hasUncaughtException); - _engine.clearExceptions(); + emit evaluationFinished(result, hasUncaughtException()); + clearExceptions(); return result; } @@ -372,12 +368,12 @@ void ScriptEngine::run() { _isFinished = false; emit runningStateChanged(); - QScriptValue result = _engine.evaluate(_scriptContents); - if (_engine.hasUncaughtException()) { - int line = _engine.uncaughtExceptionLineNumber(); + QScriptValue result = evaluate(_scriptContents); + if (hasUncaughtException()) { + int line = uncaughtExceptionLineNumber(); qDebug() << "Uncaught exception at (" << _fileNameString << ") line" << line << ":" << result.toString(); emit errorMessage("Uncaught exception at (" + _fileNameString + ") line" + QString::number(line) + ":" + result.toString()); - _engine.clearExceptions(); + clearExceptions(); } QElapsedTimer startTime; @@ -532,11 +528,11 @@ void ScriptEngine::run() { qint64 now = usecTimestampNow(); float deltaTime = (float) (now - lastUpdate) / (float) USECS_PER_SECOND; - if (_engine.hasUncaughtException()) { - int line = _engine.uncaughtExceptionLineNumber(); - qDebug() << "Uncaught exception at (" << _fileNameString << ") line" << line << ":" << _engine.uncaughtException().toString(); - emit errorMessage("Uncaught exception at (" + _fileNameString + ") line" + QString::number(line) + ":" + _engine.uncaughtException().toString()); - _engine.clearExceptions(); + if (hasUncaughtException()) { + int line = uncaughtExceptionLineNumber(); + qDebug() << "Uncaught exception at (" << _fileNameString << ") line" << line << ":" << uncaughtException().toString(); + emit errorMessage("Uncaught exception at (" + _fileNameString + ") line" + QString::number(line) + ":" + uncaughtException().toString()); + clearExceptions(); } emit update(deltaTime); @@ -694,12 +690,12 @@ void ScriptEngine::include(const QString& includeFile) { } } - QScriptValue result = _engine.evaluate(includeContents); - if (_engine.hasUncaughtException()) { - int line = _engine.uncaughtExceptionLineNumber(); + QScriptValue result = evaluate(includeContents); + if (hasUncaughtException()) { + int line = uncaughtExceptionLineNumber(); qDebug() << "Uncaught exception at (" << includeFile << ") line" << line << ":" << result.toString(); emit errorMessage("Uncaught exception at (" + includeFile + ") line" + QString::number(line) + ":" + result.toString()); - _engine.clearExceptions(); + clearExceptions(); } } diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index 0eda74914f..fe39f286be 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -38,7 +38,7 @@ const QString NO_SCRIPT(""); const unsigned int SCRIPT_DATA_CALLBACK_USECS = floor(((1.0 / 60.0f) * 1000 * 1000) + 0.5); -class ScriptEngine : public QObject { +class ScriptEngine : public QScriptEngine { Q_OBJECT public: ScriptEngine(const QUrl& scriptURL, @@ -57,8 +57,8 @@ public: /// Access the ModelsScriptingInterface in order to initialize it with a custom packet sender and jurisdiction listener static ModelsScriptingInterface* getModelsScriptingInterface() { return &_modelsScriptingInterface; } - QScriptEngine* getEngine() { return &_engine; } ArrayBufferClass* getArrayBufferClass() { return _arrayBufferClass; } + AnimationCache* getAnimationCache() { return &_animationCache; } /// sets the script contents, will return false if failed, will fail if script is already running bool setScriptContents(const QString& scriptContents, const QString& fileNameString = QString("")); @@ -121,7 +121,6 @@ protected: bool _isFinished; bool _isRunning; bool _isInitialized; - QScriptEngine _engine; bool _isAvatar; QTimer* _avatarIdentityTimer; QTimer* _avatarBillboardTimer; diff --git a/libraries/shared/src/Constraint.h b/libraries/shared/src/Constraint.h new file mode 100644 index 0000000000..422675b85d --- /dev/null +++ b/libraries/shared/src/Constraint.h @@ -0,0 +1,28 @@ +// +// Constraint.h +// libraries/shared/src +// +// Created by Andrew Meadows 2014.07.24 +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_Constraint_h +#define hifi_Constraint_h + +class Constraint { +public: + Constraint() {} + virtual ~Constraint() {} + + /// Enforce contraint by moving relevant points. + /// \return max distance of point movement + virtual float enforce() = 0; + +protected: + int _type; +}; + +#endif // hifi_Constraint_h diff --git a/libraries/shared/src/DistanceConstraint.cpp b/libraries/shared/src/DistanceConstraint.cpp new file mode 100644 index 0000000000..50fa09d307 --- /dev/null +++ b/libraries/shared/src/DistanceConstraint.cpp @@ -0,0 +1,43 @@ +// +// DistanceConstraint.cpp +// libraries/shared/src +// +// Created by Andrew Meadows 2014.07.24 +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "DistanceConstraint.h" +#include "SharedUtil.h" // for EPSILON +#include "VerletPoint.h" + +DistanceConstraint::DistanceConstraint(VerletPoint* startPoint, VerletPoint* endPoint) : _distance(-1.0f) { + _points[0] = startPoint; + _points[1] = endPoint; + _distance = glm::distance(_points[0]->_position, _points[1]->_position); +} + +DistanceConstraint::DistanceConstraint(const DistanceConstraint& other) { + _distance = other._distance; + _points[0] = other._points[0]; + _points[1] = other._points[1]; +} + +void DistanceConstraint::setDistance(float distance) { + _distance = fabsf(distance); +} + +float DistanceConstraint::enforce() { + // TODO: use a fast distance approximation + float newDistance = glm::distance(_points[0]->_position, _points[1]->_position); + glm::vec3 direction(0.0f, 1.0f, 0.0f); + if (newDistance > EPSILON) { + direction = (_points[0]->_position - _points[1]->_position) / newDistance; + } + glm::vec3 center = 0.5f * (_points[0]->_position + _points[1]->_position); + _points[0]->_position = center + (0.5f * _distance) * direction; + _points[1]->_position = center - (0.5f * _distance) * direction; + return glm::abs(newDistance - _distance); +} diff --git a/libraries/shared/src/DistanceConstraint.h b/libraries/shared/src/DistanceConstraint.h new file mode 100644 index 0000000000..c588807178 --- /dev/null +++ b/libraries/shared/src/DistanceConstraint.h @@ -0,0 +1,31 @@ +// +// DistanceConstraint.h +// libraries/shared/src +// +// Created by Andrew Meadows 2014.07.24 +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_DistanceConstraint_h +#define hifi_DistanceConstraint_h + +#include "Constraint.h" + +class VerletPoint; + +class DistanceConstraint : public Constraint { +public: + DistanceConstraint(VerletPoint* startPoint, VerletPoint* endPoint); + DistanceConstraint(const DistanceConstraint& other); + float enforce(); + void setDistance(float distance); + float getDistance() const { return _distance; } +private: + float _distance; + VerletPoint* _points[2]; +}; + +#endif // hifi_DistanceConstraint_h diff --git a/libraries/shared/src/FixedConstraint.cpp b/libraries/shared/src/FixedConstraint.cpp new file mode 100644 index 0000000000..099c6d7bac --- /dev/null +++ b/libraries/shared/src/FixedConstraint.cpp @@ -0,0 +1,35 @@ +// +// FixedConstraint.cpp +// libraries/shared/src +// +// Created by Andrew Meadows 2014.07.24 +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "FixedConstraint.h" +#include "Shape.h" // for MAX_SHAPE_MASS +#include "VerletPoint.h" + +FixedConstraint::FixedConstraint(VerletPoint* point, const glm::vec3& anchor) : _point(point), _anchor(anchor) { +} + +float FixedConstraint::enforce() { + assert(_point != NULL); + // TODO: use fast approximate sqrt here + float distance = glm::distance(_anchor, _point->_position); + _point->_position = _anchor; + return distance; +} + +void FixedConstraint::setPoint(VerletPoint* point) { + assert(point); + _point = point; + _point->_mass = MAX_SHAPE_MASS; +} + +void FixedConstraint::setAnchor(const glm::vec3& anchor) { + _anchor = anchor; +} diff --git a/libraries/shared/src/FixedConstraint.h b/libraries/shared/src/FixedConstraint.h new file mode 100644 index 0000000000..050232a027 --- /dev/null +++ b/libraries/shared/src/FixedConstraint.h @@ -0,0 +1,32 @@ +// +// FixedConstraint.h +// libraries/shared/src +// +// Created by Andrew Meadows 2014.07.24 +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_FixedConstraint_h +#define hifi_FixedConstraint_h + +#include + +#include "Constraint.h" + +class VerletPoint; + +class FixedConstraint : public Constraint { +public: + FixedConstraint(VerletPoint* point, const glm::vec3& anchor); + float enforce(); + void setPoint(VerletPoint* point); + void setAnchor(const glm::vec3& anchor); +private: + VerletPoint* _point; + glm::vec3 _anchor; +}; + +#endif // hifi_FixedConstraint_h diff --git a/libraries/shared/src/Ragdoll.cpp b/libraries/shared/src/Ragdoll.cpp index 1d24e74864..6282db4dfb 100644 --- a/libraries/shared/src/Ragdoll.cpp +++ b/libraries/shared/src/Ragdoll.cpp @@ -11,86 +11,9 @@ #include "Ragdoll.h" -#include "CapsuleShape.h" -#include "CollisionInfo.h" -#include "SharedUtil.h" -#include "SphereShape.h" - -// ---------------------------------------------------------------------------- -// VerletPoint -// ---------------------------------------------------------------------------- -void VerletPoint::accumulateDelta(const glm::vec3& delta) { - _accumulatedDelta += delta; - ++_numDeltas; -} - -void VerletPoint::applyAccumulatedDelta() { - if (_numDeltas > 0) { - _position += _accumulatedDelta / (float)_numDeltas; - _accumulatedDelta = glm::vec3(0.0f); - _numDeltas = 0; - } -} - -// ---------------------------------------------------------------------------- -// FixedConstraint -// ---------------------------------------------------------------------------- -FixedConstraint::FixedConstraint(VerletPoint* point, const glm::vec3& anchor) : _point(point), _anchor(anchor) { -} - -float FixedConstraint::enforce() { - assert(_point != NULL); - // TODO: use fast approximate sqrt here - float distance = glm::distance(_anchor, _point->_position); - _point->_position = _anchor; - return distance; -} - -void FixedConstraint::setPoint(VerletPoint* point) { - assert(point); - _point = point; - _point->_mass = MAX_SHAPE_MASS; -} - -void FixedConstraint::setAnchor(const glm::vec3& anchor) { - _anchor = anchor; -} - -// ---------------------------------------------------------------------------- -// DistanceConstraint -// ---------------------------------------------------------------------------- -DistanceConstraint::DistanceConstraint(VerletPoint* startPoint, VerletPoint* endPoint) : _distance(-1.0f) { - _points[0] = startPoint; - _points[1] = endPoint; - _distance = glm::distance(_points[0]->_position, _points[1]->_position); -} - -DistanceConstraint::DistanceConstraint(const DistanceConstraint& other) { - _distance = other._distance; - _points[0] = other._points[0]; - _points[1] = other._points[1]; -} - -void DistanceConstraint::setDistance(float distance) { - _distance = fabsf(distance); -} - -float DistanceConstraint::enforce() { - // TODO: use a fast distance approximation - float newDistance = glm::distance(_points[0]->_position, _points[1]->_position); - glm::vec3 direction(0.0f, 1.0f, 0.0f); - if (newDistance > EPSILON) { - direction = (_points[0]->_position - _points[1]->_position) / newDistance; - } - glm::vec3 center = 0.5f * (_points[0]->_position + _points[1]->_position); - _points[0]->_position = center + (0.5f * _distance) * direction; - _points[1]->_position = center - (0.5f * _distance) * direction; - return glm::abs(newDistance - _distance); -} - -// ---------------------------------------------------------------------------- -// Ragdoll -// ---------------------------------------------------------------------------- +#include "Constraint.h" +#include "DistanceConstraint.h" +#include "FixedConstraint.h" Ragdoll::Ragdoll() { } @@ -98,6 +21,13 @@ Ragdoll::Ragdoll() { Ragdoll::~Ragdoll() { clearRagdollConstraintsAndPoints(); } + +void Ragdoll::stepRagdollForward(float deltaTime) { + int numPoints = _ragdollPoints.size(); + for (int i = 0; i < numPoints; ++i) { + _ragdollPoints[i].integrateForward(); + } +} void Ragdoll::clearRagdollConstraintsAndPoints() { int numConstraints = _ragdollConstraints.size(); @@ -118,4 +48,3 @@ float Ragdoll::enforceRagdollConstraints() { } return maxDistance; } - diff --git a/libraries/shared/src/Ragdoll.h b/libraries/shared/src/Ragdoll.h index 59c1291725..91a7e7330e 100644 --- a/libraries/shared/src/Ragdoll.h +++ b/libraries/shared/src/Ragdoll.h @@ -14,71 +14,11 @@ #include #include +#include "VerletPoint.h" #include -class Shape; - -// TODO: Andrew to move VerletPoint class to its own file -class VerletPoint { -public: - VerletPoint() : _position(0.0f), _lastPosition(0.0f), _mass(1.0f), _accumulatedDelta(0.0f), _numDeltas(0) {} - - void accumulateDelta(const glm::vec3& delta); - void applyAccumulatedDelta(); - - glm::vec3 getAccumulatedDelta() const { - glm::vec3 foo(0.0f); - if (_numDeltas > 0) { - foo = _accumulatedDelta / (float)_numDeltas; - } - return foo; - } - - glm::vec3 _position; - glm::vec3 _lastPosition; - float _mass; - -private: - glm::vec3 _accumulatedDelta; - int _numDeltas; -}; - -class Constraint { -public: - Constraint() {} - virtual ~Constraint() {} - - /// Enforce contraint by moving relevant points. - /// \return max distance of point movement - virtual float enforce() = 0; - -protected: - int _type; -}; - -class FixedConstraint : public Constraint { -public: - FixedConstraint(VerletPoint* point, const glm::vec3& anchor); - float enforce(); - void setPoint(VerletPoint* point); - void setAnchor(const glm::vec3& anchor); -private: - VerletPoint* _point; - glm::vec3 _anchor; -}; - -class DistanceConstraint : public Constraint { -public: - DistanceConstraint(VerletPoint* startPoint, VerletPoint* endPoint); - DistanceConstraint(const DistanceConstraint& other); - float enforce(); - void setDistance(float distance); - float getDistance() const { return _distance; } -private: - float _distance; - VerletPoint* _points[2]; -}; +class Constraint; class Ragdoll { public: @@ -86,7 +26,7 @@ public: Ragdoll(); virtual ~Ragdoll(); - virtual void stepRagdollForward(float deltaTime) = 0; + virtual void stepRagdollForward(float deltaTime); /// \return max distance of point movement float enforceRagdollConstraints(); diff --git a/libraries/shared/src/SharedUtil.cpp b/libraries/shared/src/SharedUtil.cpp index b5be502ed5..e795ea746c 100644 --- a/libraries/shared/src/SharedUtil.cpp +++ b/libraries/shared/src/SharedUtil.cpp @@ -733,6 +733,13 @@ glm::quat rotationBetween(const glm::vec3& v1, const glm::vec3& v2) { } } else { axis = glm::normalize(glm::cross(v1, v2)); + // It is possible for axis to be nan even when angle is not less than EPSILON. + // For example when angle is small but not tiny but v1 and v2 and have very short lengths. + if (glm::isnan(glm::dot(axis, axis))) { + // set angle and axis to values that will generate an identity rotation + angle = 0.0f; + axis = glm::vec3(1.0f, 0.0f, 0.0f); + } } return glm::angleAxis(angle, axis); } diff --git a/libraries/shared/src/VerletPoint.cpp b/libraries/shared/src/VerletPoint.cpp new file mode 100644 index 0000000000..641ac39341 --- /dev/null +++ b/libraries/shared/src/VerletPoint.cpp @@ -0,0 +1,31 @@ +// +// VerletPoint.cpp +// libraries/shared/src +// +// Created by Andrew Meadows 2014.07.24 +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "VerletPoint.h" + +void VerletPoint::integrateForward() { + glm::vec3 oldPosition = _position; + _position += 0.6f * (_position - _lastPosition); + _lastPosition = oldPosition; +} + +void VerletPoint::accumulateDelta(const glm::vec3& delta) { + _accumulatedDelta += delta; + ++_numDeltas; +} + +void VerletPoint::applyAccumulatedDelta() { + if (_numDeltas > 0) { + _position += _accumulatedDelta / (float)_numDeltas; + _accumulatedDelta = glm::vec3(0.0f); + _numDeltas = 0; + } +} diff --git a/libraries/shared/src/VerletPoint.h b/libraries/shared/src/VerletPoint.h new file mode 100644 index 0000000000..076a624776 --- /dev/null +++ b/libraries/shared/src/VerletPoint.h @@ -0,0 +1,39 @@ +// +// VerletPoint.h +// libraries/shared/src +// +// Created by Andrew Meadows 2014.07.24 +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_VerletPoint_h +#define hifi_VerletPoint_h + +#include + +class VerletPoint { +public: + VerletPoint() : _position(0.0f), _lastPosition(0.0f), _mass(1.0f), _accumulatedDelta(0.0f), _numDeltas(0) {} + + void initPosition(const glm::vec3& position) { _position = position; _lastPosition = position; } + void integrateForward(); + void accumulateDelta(const glm::vec3& delta); + void applyAccumulatedDelta(); + + glm::vec3 getAccumulatedDelta() const { + return (_numDeltas > 0) ? _accumulatedDelta / (float)_numDeltas : glm::vec3(0.0f); + } + + glm::vec3 _position; + glm::vec3 _lastPosition; + float _mass; + +private: + glm::vec3 _accumulatedDelta; + int _numDeltas; +}; + +#endif // hifi_VerletPoint_h