diff --git a/assignment-client/src/audio/AudioMixerSlave.cpp b/assignment-client/src/audio/AudioMixerSlave.cpp index b897efe197..42e8c5bdef 100644 --- a/assignment-client/src/audio/AudioMixerSlave.cpp +++ b/assignment-client/src/audio/AudioMixerSlave.cpp @@ -32,6 +32,7 @@ #include "AudioMixerClientData.h" #include "AvatarAudioStream.h" #include "InjectedAudioStream.h" +#include "AudioHelpers.h" #include "AudioMixerSlave.h" @@ -406,63 +407,6 @@ void AudioMixerSlave::addStreamToMix(AudioMixerClientData& listenerNodeData, con AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL); } -const int IEEE754_MANT_BITS = 23; -const int IEEE754_EXPN_BIAS = 127; - -// -// for x > 0.0f, returns log2(x) -// for x <= 0.0f, returns large negative value -// -// abs |error| < 8e-3, smooth (exact for x=2^N) for NPOLY=3 -// abs |error| < 2e-4, smooth (exact for x=2^N) for NPOLY=5 -// rel |error| < 0.4 from precision loss very close to 1.0f -// -static inline float fastlog2(float x) { - - union { float f; int32_t i; } mant, bits = { x }; - - // split into mantissa and exponent - mant.i = (bits.i & ((1 << IEEE754_MANT_BITS) - 1)) | (IEEE754_EXPN_BIAS << IEEE754_MANT_BITS); - int32_t expn = (bits.i >> IEEE754_MANT_BITS) - IEEE754_EXPN_BIAS; - - mant.f -= 1.0f; - - // polynomial for log2(1+x) over x=[0,1] - //x = (-0.346555386f * mant.f + 1.346555386f) * mant.f; - x = (((-0.0821307180f * mant.f + 0.321188984f) * mant.f - 0.677784014f) * mant.f + 1.43872575f) * mant.f; - - return x + expn; -} - -// -// for -126 <= x < 128, returns exp2(x) -// -// rel |error| < 3e-3, smooth (exact for x=N) for NPOLY=3 -// rel |error| < 9e-6, smooth (exact for x=N) for NPOLY=5 -// -static inline float fastexp2(float x) { - - union { float f; int32_t i; } xi; - - // bias such that x > 0 - x += IEEE754_EXPN_BIAS; - //x = MAX(x, 1.0f); - //x = MIN(x, 254.9999f); - - // split into integer and fraction - xi.i = (int32_t)x; - x -= xi.i; - - // construct exp2(xi) as a float - xi.i <<= IEEE754_MANT_BITS; - - // polynomial for exp2(x) over x=[0,1] - //x = (0.339766028f * x + 0.660233972f) * x + 1.0f; - x = (((0.0135557472f * x + 0.0520323690f) * x + 0.241379763f) * x + 0.693032121f) * x + 1.0f; - - return x * xi.f; -} - float AudioMixerSlave::gainForSource(const AvatarAudioStream& listeningNodeStream, const PositionalAudioStream& streamToAdd, const glm::vec3& relativePosition, bool isEcho) { float gain = 1.0f; @@ -514,7 +458,7 @@ float AudioMixerSlave::gainForSource(const AvatarAudioStream& listeningNodeStrea g = (g > 1.0f) ? 1.0f : g; // calculate the distance coefficient using the distance to this node - float distanceCoefficient = fastexp2(fastlog2(g) * fastlog2(distanceBetween/ATTENUATION_BEGINS_AT_DISTANCE)); + float distanceCoefficient = fastExp2f(fastLog2f(g) * fastLog2f(distanceBetween/ATTENUATION_BEGINS_AT_DISTANCE)); // multiply the current attenuation coefficient by the distance coefficient gain *= distanceCoefficient; diff --git a/interface/resources/html/img/controls-help-oculus.png b/interface/resources/html/img/controls-help-oculus.png index 85e25c2a19..0bd0a656de 100644 Binary files a/interface/resources/html/img/controls-help-oculus.png and b/interface/resources/html/img/controls-help-oculus.png differ diff --git a/interface/resources/meshes/controller/touch/Oculus-Labels-L.fbx b/interface/resources/meshes/controller/touch/Oculus-Labels-L.fbx index 4f1f60be0b..50f415b7ca 100644 Binary files a/interface/resources/meshes/controller/touch/Oculus-Labels-L.fbx and b/interface/resources/meshes/controller/touch/Oculus-Labels-L.fbx differ diff --git a/interface/resources/meshes/controller/touch/Oculus-Labels-R.fbx b/interface/resources/meshes/controller/touch/Oculus-Labels-R.fbx index 49a3d6c832..9e9d92541c 100644 Binary files a/interface/resources/meshes/controller/touch/Oculus-Labels-R.fbx and b/interface/resources/meshes/controller/touch/Oculus-Labels-R.fbx differ diff --git a/interface/resources/qml/hifi/Pal.qml b/interface/resources/qml/hifi/Pal.qml index 833cf4efe2..8a583bfe4c 100644 --- a/interface/resources/qml/hifi/Pal.qml +++ b/interface/resources/qml/hifi/Pal.qml @@ -104,6 +104,7 @@ Item { border.width: 2 // "ADMIN" text RalewaySemiBold { + id: adminTabText text: "ADMIN" // Text size size: hifi.fontSizes.tableHeading + 2 @@ -325,7 +326,7 @@ Item { visible: iAmAdmin color: hifi.colors.lightGrayText } - // This Rectangle refers to the [?] popup button + // This Rectangle refers to the [?] popup button next to "NAMES" Rectangle { color: hifi.colors.tableBackgroundLight width: 20 @@ -353,7 +354,36 @@ Item { onExited: helpText.color = hifi.colors.darkGray } } - // Explanitory popup upon clicking "[?]" + // This Rectangle refers to the [?] popup button next to "ADMIN" + Rectangle { + visible: iAmAdmin + color: adminTab.color + width: 20 + height: 28 + anchors.right: adminTab.right + anchors.rightMargin: 31 + anchors.top: adminTab.top + anchors.topMargin: 2 + RalewayRegular { + id: adminHelpText + text: "[?]" + size: hifi.fontSizes.tableHeading + 2 + font.capitalization: Font.AllUppercase + color: hifi.colors.redHighlight + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + anchors.fill: parent + } + MouseArea { + anchors.fill: parent + acceptedButtons: Qt.LeftButton + hoverEnabled: true + onClicked: adminPopup.visible = true + onEntered: adminHelpText.color = "#94132e" + onExited: adminHelpText.color = hifi.colors.redHighlight + } + } + // Explanitory popup upon clicking "[?]" next to "NAMES" Item { visible: false id: namesPopup @@ -392,6 +422,44 @@ Item { } } } + // Explanitory popup upon clicking "[?]" next to "ADMIN" + Item { + visible: false + id: adminPopup + anchors.fill: pal + Rectangle { + anchors.fill: parent + color: "black" + opacity: 0.5 + radius: hifi.dimensions.borderRadius + } + Rectangle { + width: Math.max(parent.width * 0.75, 400) + height: adminPopupText.contentHeight*1.5 + anchors.centerIn: parent + radius: hifi.dimensions.borderRadius + color: "white" + FiraSansSemiBold { + id: adminPopupText + text: 'Silencing a user mutes their microphone. Silenced users can unmute themselves by clicking the "UNMUTE" button on their HUD.\n\n' + + "Banning a user will remove them from this domain and prevent them from returning. You can un-ban users from your domain's settings page." + size: hifi.fontSizes.textFieldInput + color: hifi.colors.darkGray + horizontalAlignment: Text.AlignHCenter + anchors.fill: parent + anchors.leftMargin: 15 + anchors.rightMargin: 15 + wrapMode: Text.WordWrap + } + } + MouseArea { + anchors.fill: parent + acceptedButtons: Qt.LeftButton + onClicked: { + adminPopup.visible = false + } + } + } function findSessionIndex(sessionId, optionalData) { // no findIndex in .qml var data = optionalData || userModelData, length = data.length; diff --git a/interface/resources/qml/styles-uit/HifiConstants.qml b/interface/resources/qml/styles-uit/HifiConstants.qml index da1b2868a7..cb99dc6005 100644 --- a/interface/resources/qml/styles-uit/HifiConstants.qml +++ b/interface/resources/qml/styles-uit/HifiConstants.qml @@ -95,7 +95,7 @@ Item { readonly property color tableRowDarkEven: "#1c1c1c" // Equivalent to "#a6181818" over #404040 background readonly property color tableBackgroundLight: tableRowLightEven readonly property color tableBackgroundDark: tableRowDarkEven - readonly property color tableScrollHandleLight: tableRowLightOdd + readonly property color tableScrollHandleLight: "#8F8F8F" readonly property color tableScrollHandleDark: "#707070" readonly property color tableScrollBackgroundLight: tableRowLightEven readonly property color tableScrollBackgroundDark: "#323232" diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index b58bedb363..832419a75c 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1137,6 +1137,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo _settingsTimer.moveToThread(&_settingsThread); _settingsTimer.setSingleShot(false); _settingsTimer.setInterval(SAVE_SETTINGS_INTERVAL); // 10s, Qt::CoarseTimer acceptable + _settingsThread.setPriority(QThread::LowestPriority); _settingsThread.start(); if (Menu::getInstance()->isOptionChecked(MenuOption::FirstPerson)) { diff --git a/libraries/audio/src/AudioInjector.cpp b/libraries/audio/src/AudioInjector.cpp index 23e37d911e..48b44bd3d3 100644 --- a/libraries/audio/src/AudioInjector.cpp +++ b/libraries/audio/src/AudioInjector.cpp @@ -25,6 +25,7 @@ #include "AudioLogging.h" #include "SoundCache.h" #include "AudioSRC.h" +#include "AudioHelpers.h" int audioInjectorPtrMetaTypeId = qRegisterMetaType(); @@ -187,7 +188,7 @@ bool AudioInjector::injectLocally() { return success; } -const uchar MAX_INJECTOR_VOLUME = 0xFF; +const uchar MAX_INJECTOR_VOLUME = packFloatGainToByte(1.0f); static const int64_t NEXT_FRAME_DELTA_ERROR_OR_FINISHED = -1; static const int64_t NEXT_FRAME_DELTA_IMMEDIATELY = 0; @@ -333,7 +334,7 @@ int64_t AudioInjector::injectNextFrame() { _currentPacket->writePrimitive(_options.position); _currentPacket->writePrimitive(_options.orientation); - quint8 volume = MAX_INJECTOR_VOLUME * _options.volume; + quint8 volume = packFloatGainToByte(_options.volume); _currentPacket->seek(volumeOptionOffset); _currentPacket->writePrimitive(volume); diff --git a/libraries/audio/src/AudioInjector.h b/libraries/audio/src/AudioInjector.h index c5e741365a..2abc445034 100644 --- a/libraries/audio/src/AudioInjector.h +++ b/libraries/audio/src/AudioInjector.h @@ -63,7 +63,7 @@ public: AudioFOA& getLocalFOA() { return _localFOA; } bool isLocalOnly() const { return _options.localOnly; } - float getVolume() const { return glm::clamp(_options.volume, 0.0f, 1.0f); } + float getVolume() const { return _options.volume; } glm::vec3 getPosition() const { return _options.position; } glm::quat getOrientation() const { return _options.orientation; } bool isStereo() const { return _options.stereo; } diff --git a/libraries/audio/src/InjectedAudioStream.cpp b/libraries/audio/src/InjectedAudioStream.cpp index 519a3e2459..a06dba5389 100644 --- a/libraries/audio/src/InjectedAudioStream.cpp +++ b/libraries/audio/src/InjectedAudioStream.cpp @@ -18,6 +18,7 @@ #include #include "InjectedAudioStream.h" +#include "AudioHelpers.h" InjectedAudioStream::InjectedAudioStream(const QUuid& streamIdentifier, bool isStereo, int numStaticJitterFrames) : PositionalAudioStream(PositionalAudioStream::Injector, isStereo, numStaticJitterFrames), @@ -25,8 +26,6 @@ InjectedAudioStream::InjectedAudioStream(const QUuid& streamIdentifier, bool isS _radius(0.0f), _attenuationRatio(0) {} -const uchar MAX_INJECTOR_VOLUME = 255; - int InjectedAudioStream::parseStreamProperties(PacketType type, const QByteArray& packetAfterSeqNum, int& numAudioSamples) { @@ -62,7 +61,7 @@ int InjectedAudioStream::parseStreamProperties(PacketType type, quint8 attenuationByte = 0; packetStream >> attenuationByte; - _attenuationRatio = attenuationByte / (float)MAX_INJECTOR_VOLUME; + _attenuationRatio = unpackFloatGainFromByte(attenuationByte); packetStream >> _ignorePenumbra; diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp index c4846f6af8..a6f7d3caf4 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp @@ -165,6 +165,7 @@ public: if (newPlugin) { bool hasVsync = true; + QThread::setPriority(newPlugin->getPresentPriority()); bool wantVsync = newPlugin->wantVsync(); _context->makeCurrent(); #if defined(Q_OS_WIN) diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h index a6de3f7baa..ee0bcf91a9 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h @@ -14,6 +14,7 @@ #include #include +#include #include #include @@ -80,6 +81,7 @@ protected: void updateCompositeFramebuffer(); + virtual QThread::Priority getPresentPriority() { return QThread::HighPriority; } virtual void compositeLayers(); virtual void compositeScene(); virtual void compositeOverlay(); diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index 07e1ece0cb..46d6915a12 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -78,7 +78,7 @@ PacketVersion versionForPacketType(PacketType packetType) { case PacketType::MicrophoneAudioNoEcho: case PacketType::MicrophoneAudioWithEcho: case PacketType::AudioStreamStats: - return static_cast(AudioVersion::SpaceBubbleChanges); + return static_cast(AudioVersion::HighDynamicRangeVolume); default: return 17; diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 1867d2193c..3561624f63 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -245,6 +245,7 @@ enum class AudioVersion : PacketVersion { TerminatingStreamStats, SpaceBubbleChanges, HasPersonalMute, + HighDynamicRangeVolume, }; #endif // hifi_PacketHeaders_h diff --git a/libraries/script-engine/src/UsersScriptingInterface.h b/libraries/script-engine/src/UsersScriptingInterface.h index 4182d5244c..341285a742 100644 --- a/libraries/script-engine/src/UsersScriptingInterface.h +++ b/libraries/script-engine/src/UsersScriptingInterface.h @@ -37,7 +37,7 @@ public slots: * @param {nodeID} nodeID The node or session ID of the user you want to ignore. * @param {bool} enable True for ignored; false for un-ignored. */ - void ignore(const QUuid& nodeID, bool ignoreEnabled); + void ignore(const QUuid& nodeID, bool ignoreEnabled = true); /**jsdoc * Gets a bool containing whether you have ignored the given Avatar UUID. @@ -52,7 +52,7 @@ public slots: * @param {nodeID} nodeID The node or session ID of the user you want to mute. * @param {bool} enable True for enabled; false for disabled. */ - void personalMute(const QUuid& nodeID, bool muteEnabled); + void personalMute(const QUuid& nodeID, bool muteEnabled = true); /**jsdoc * Requests a bool containing whether you have personally muted the given Avatar UUID. diff --git a/libraries/shared/src/AudioHelpers.h b/libraries/shared/src/AudioHelpers.h new file mode 100644 index 0000000000..b43764ef5d --- /dev/null +++ b/libraries/shared/src/AudioHelpers.h @@ -0,0 +1,94 @@ +// +// AudioHelpers.h +// libraries/shared/src +// +// Created by Ken Cooke on 1/4/17. +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_AudioHelpers_h +#define hifi_AudioHelpers_h + +#include + +const int IEEE754_MANT_BITS = 23; +const int IEEE754_EXPN_BIAS = 127; + +// +// for x > 0.0f, returns log2(x) +// for x <= 0.0f, returns large negative value +// +// abs |error| < 2e-4, smooth (exact for x=2^N) +// rel |error| < 0.4 from precision loss very close to 1.0f +// +static inline float fastLog2f(float x) { + + union { float f; int32_t i; } mant, bits = { x }; + + // split into mantissa and exponent + mant.i = (bits.i & ((1 << IEEE754_MANT_BITS) - 1)) | (IEEE754_EXPN_BIAS << IEEE754_MANT_BITS); + int32_t expn = (bits.i >> IEEE754_MANT_BITS) - IEEE754_EXPN_BIAS; + + mant.f -= 1.0f; + + // polynomial for log2(1+x) over x=[0,1] + x = (((-0.0821307180f * mant.f + 0.321188984f) * mant.f - 0.677784014f) * mant.f + 1.43872575f) * mant.f; + + return x + expn; +} + +// +// for -127 <= x < 128, returns exp2(x) +// otherwise, returns undefined +// +// rel |error| < 9e-6, smooth (exact for x=N) +// +static inline float fastExp2f(float x) { + + union { float f; int32_t i; } xi; + + // bias such that x > 0 + x += IEEE754_EXPN_BIAS; + + // split into integer and fraction + xi.i = (int32_t)x; + x -= xi.i; + + // construct exp2(xi) as a float + xi.i <<= IEEE754_MANT_BITS; + + // polynomial for exp2(x) over x=[0,1] + x = (((0.0135557472f * x + 0.0520323690f) * x + 0.241379763f) * x + 0.693032121f) * x + 1.0f; + + return x * xi.f; +} + +// +// Quantize a non-negative gain value to the nearest 0.5dB, and pack to a byte. +// +// Values above +30dB are clamped to +30dB +// Values below -97dB are clamped to -inf +// Value of 1.0 (+0dB) is reconstructed exactly +// +const float GAIN_CONVERSION_RATIO = 2.0f * 6.02059991f; // scale log2 to 0.5dB +const float GAIN_CONVERSION_OFFSET = 255 - 60.0f; // translate +30dB to max + +static inline uint8_t packFloatGainToByte(float gain) { + + float f = fastLog2f(gain) * GAIN_CONVERSION_RATIO + GAIN_CONVERSION_OFFSET; + int32_t i = (int32_t)(f + 0.5f); // quantize + + uint8_t byte = (i < 0) ? 0 : ((i > 255) ? 255 : i); // clamp + return byte; +} + +static inline float unpackFloatGainFromByte(uint8_t byte) { + + float gain = (byte == 0) ? 0.0f : fastExp2f((byte - GAIN_CONVERSION_OFFSET) * (1.0f/GAIN_CONVERSION_RATIO)); + return gain; +} + +#endif // hifi_AudioHelpers_h diff --git a/libraries/shared/src/SettingHandle.cpp b/libraries/shared/src/SettingHandle.cpp index 626d7e7ef5..a04a7c3f61 100644 --- a/libraries/shared/src/SettingHandle.cpp +++ b/libraries/shared/src/SettingHandle.cpp @@ -74,7 +74,9 @@ void Settings::endGroup() { } void Settings::setValue(const QString& name, const QVariant& value) { - _manager->setValue(name, value); + if (_manager->value(name) != value) { + _manager->setValue(name, value); + } } QVariant Settings::value(const QString& name, const QVariant& defaultValue) const { diff --git a/plugins/oculus/src/OculusDisplayPlugin.h b/plugins/oculus/src/OculusDisplayPlugin.h index e44596d6e9..ccab31785b 100644 --- a/plugins/oculus/src/OculusDisplayPlugin.h +++ b/plugins/oculus/src/OculusDisplayPlugin.h @@ -24,6 +24,8 @@ public: virtual QJsonObject getHardwareStats() const; protected: + QThread::Priority getPresentPriority() override { return QThread::TimeCriticalPriority; } + bool internalActivate() override; void hmdPresent() override; bool isHmdMounted() const override; diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index 513e70d18c..2ed09232e6 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -195,6 +195,8 @@ var STATE_OVERLAY_TOUCHING = 8; var holdEnabled = true; var nearGrabEnabled = true; var farGrabEnabled = true; +var myAvatarScalingEnabled = true; +var objectScalingEnabled = true; // "collidesWith" is specified by comma-separated list of group names // the possible group names are: static, dynamic, kinematic, myAvatar, otherAvatar @@ -2312,6 +2314,10 @@ function MyController(hand) { }; this.maybeScale = function(props) { + if (!objectScalingEnabled) { + return; + } + if (!this.shouldScale) { // If both secondary triggers squeezed, and the non-holding hand is empty, start scaling if (this.secondarySqueezed() && this.getOtherHandController().secondarySqueezed() && this.getOtherHandController().state === STATE_OFF) { @@ -2331,6 +2337,10 @@ function MyController(hand) { } this.maybeScaleMyAvatar = function() { + if (!myAvatarScalingEnabled) { + return; + } + if (!this.shouldScale) { // If both secondary triggers squeezed, start scaling if (this.secondarySqueezed() && this.getOtherHandController().secondarySqueezed()) { @@ -3016,6 +3026,14 @@ var handleHandMessages = function(channel, message, sender) { print("farGrabEnabled: ", data.farGrabEnabled); farGrabEnabled = data.farGrabEnabled; } + if (data.myAvatarScalingEnabled !== undefined) { + print("myAvatarScalingEnabled: ", data.myAvatarScalingEnabled); + myAvatarScalingEnabled = data.myAvatarScalingEnabled; + } + if (data.objectScalingEnabled !== undefined) { + print("objectScalingEnabled: ", data.objectScalingEnabled); + objectScalingEnabled = data.objectScalingEnabled; + } } else if (channel === 'Hifi-Hand-Grab') { try { data = JSON.parse(message); diff --git a/scripts/system/controllers/touchControllerConfiguration.js b/scripts/system/controllers/touchControllerConfiguration.js index 6241a776c4..a4622fd5e6 100644 --- a/scripts/system/controllers/touchControllerConfiguration.js +++ b/scripts/system/controllers/touchControllerConfiguration.js @@ -74,6 +74,9 @@ TOUCH_CONTROLLER_CONFIGURATION_LEFT = { teleport: { defaultTextureURL: BASE_URL + "Oculus-Labels-L.fbx/Oculus-Labels-L.fbm/Teleport.png" }, + both_triggers: { + defaultTextureURL: BASE_URL + "Oculus-Labels-L.fbx/Oculus-Labels-L.fbm/Grip-Trigger.png" + }, } }, @@ -212,7 +215,7 @@ TOUCH_CONTROLLER_CONFIGURATION_RIGHT = { modelURL: BASE_URL + "Oculus-Labels-R.fbx", naturalPosition: { x: 0.009739525616168976, y: -0.0017818436026573181, z: 0.016794726252555847 }, - textureName: "Texture", + textureName: "blank", defaultTextureLayer: "blank", textureLayers: { blank: { @@ -230,6 +233,9 @@ TOUCH_CONTROLLER_CONFIGURATION_RIGHT = { teleport: { defaultTextureURL: BASE_URL + "Oculus-Labels-R.fbx/Oculus-Labels-R.fbm/Teleport.png" }, + both_triggers: { + defaultTextureURL: BASE_URL + "Oculus-Labels-R.fbx/Oculus-Labels-R.fbm/Grip-Trigger.png" + }, } }, diff --git a/scripts/tutorials/entity_scripts/springAway.js b/scripts/tutorials/entity_scripts/springAway.js new file mode 100644 index 0000000000..1652db216f --- /dev/null +++ b/scripts/tutorials/entity_scripts/springAway.js @@ -0,0 +1,130 @@ +// springAway.js +// +// If you attach this entity script to an object, the object will spring away from +// your avatar's hands. Useful for making a beachball or basketball, because you +// can bounce it on your hands. +// +// You can change the force applied by the script by setting userData to contain +// a value 'strength', which will otherwise default to DEFAULT_STRENGTH +// +// Note that the use of dimensions.x as the size of the entity means that it +// needs to be spherical for this to look correct. +// +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +(function(){ + + var UPDATE_INTERVAL_MSECS = 1000/45; // Update the spring/object at 45Hz + var SETTINGS_INTERVAL_MSECS = 1000; // Periodically check user data for updates + var TOO_FAR = 5.0; // If the object + var DEFAULT_STRENGTH = 3.0; // How strong/stiff is the spring? + + + var entity; + var props; + var checkTimer = false; + var settingsTimer = false; + var strength = DEFAULT_STRENGTH; + var _this; + + var WANT_DEBUG = false; + function debugPrint(string) { + if (WANT_DEBUG) { + print(string); + } + } + + function howFarAway(position) { + return Vec3.distance(MyAvatar.position, position); + } + + function springForce(position, center, radius) { + // + // Return a vector corresponding to a normalized spring force ranging from 1 at + // exact center to zero at distance radius from center. + // + var distance = Vec3.distance(position, center); + return Vec3.multiply(1.0 - distance / radius, Vec3.normalize(Vec3.subtract(center, position))); + } + + function fingerPosition(which) { + // + // Get the worldspace position of either the tip of the index finger (jointName "RightHandIndex3", etc), or + // fall back to the controller position if that doesn't exist. + // + var joint = MyAvatar.getJointPosition(which === "RIGHT" ? "RightHandIndex3" : "LeftHandIndex3"); + if (Vec3.length(joint) > 0) { + return joint; + } else { + return Vec3.sum(MyAvatar.position, + Vec3.multiplyQbyV(MyAvatar.orientation, + Controller.getPoseValue(which === "RIGHT" ? Controller.Standard.RightHand : Controller.Standard.LeftHand).translation)); + } + } + + this.preload = function(entityID) { + // Load the sound and range from the entity userData fields, and note the position of the entity. + debugPrint("springAway preload"); + entity = entityID; + _this = this; + checkTimer = Script.setInterval(this.maybePush, UPDATE_INTERVAL_MSECS); + settingsTimer = Script.setInterval(this.checkSettings, SETTINGS_INTERVAL_MSECS); + }; + + this.maybePush = function() { + props = Entities.getEntityProperties(entity, [ "position", "dimensions", "velocity" ]); + + // First, check if the entity is far enough away to not need to do anything with it + + if (howFarAway(props.position) - props.dimensions.x / 2 > TOO_FAR) { + return; + } + + var rightFingerPosition = fingerPosition("RIGHT"); + var leftFingerPosition = fingerPosition("LEFT"); + + var addVelocity = { x: 0, y: 0, z: 0 }; + + + if (Vec3.distance(leftFingerPosition, props.position) < props.dimensions.x / 2) { + addVelocity = Vec3.sum(addVelocity, Vec3.multiply(springForce(leftFingerPosition, props.position, props.dimensions.x), strength)); + } + if (Vec3.distance(rightFingerPosition, props.position) < props.dimensions.x / 2) { + addVelocity = Vec3.sum(addVelocity, Vec3.multiply(springForce(rightFingerPosition, props.position, props.dimensions.x), strength)); + } + + if (Vec3.length(addVelocity) > 0) { + Entities.editEntity(entity, { + velocity: Vec3.sum(props.velocity, addVelocity) + }); + } + } + + this.checkSettings = function() { + var dataProps = Entities.getEntityProperties(entity, [ "userData" ]); + if (dataProps.userData) { + var data = JSON.parse(dataProps.userData); + if (data.strength) { + if (!(strength === data.strength)) { + debugPrint("Read new spring strength: " + data.strength); + } + strength = data.strength; + } + } + } + + this.unload = function(entityID) { + debugPrint("springAway unload"); + if (checkTimer) { + Script.clearInterval(checkTimer); + } + if (settingsTimer) { + Script.clearInterval(settingsTimer); + } + }; + +}) diff --git a/server-console/src/main.js b/server-console/src/main.js index c99e39faf8..b387aa5dd0 100644 --- a/server-console/src/main.js +++ b/server-console/src/main.js @@ -42,7 +42,7 @@ const appIcon = path.join(__dirname, '../resources/console.png'); const DELETE_LOG_FILES_OLDER_THAN_X_SECONDS = 60 * 60 * 24 * 7; // 7 Days const LOG_FILE_REGEX = /(domain-server|ac-monitor|ac)-.*-std(out|err).txt/; -const HOME_CONTENT_URL = "http://cachefly.highfidelity.com/home-tutorial-release-5572.tar.gz"; +const HOME_CONTENT_URL = "http://cdn.highfidelity.com/content-sets/home-tutorial-28.tar.gz"; function getBuildInfo() { var buildInfoPath = null; diff --git a/tutorial/tutorial.js b/tutorial/tutorial.js index b75bf74579..26fa51e896 100644 --- a/tutorial/tutorial.js +++ b/tutorial/tutorial.js @@ -58,6 +58,9 @@ function info() { } } +const CONTROLLER_TOUCH = 'touch'; +const CONTROLLER_VIVE = 'vive'; + var NEAR_BOX_SPAWN_NAME = "tutorial/nearGrab/box_spawn"; var FAR_BOX_SPAWN_NAME = "tutorial/farGrab/box_spawn"; var GUN_SPAWN_NAME = "tutorial/gun_spawn"; @@ -323,7 +326,7 @@ function hideEntitiesWithTag(tag) { //}); } -/** +/** * Return the entity properties for an entity with a given name if it is in our * cached list of entities. Otherwise, return undefined. */ @@ -371,6 +374,8 @@ function disableEverything() { nearGrabEnabled: true, holdEnabled: false, farGrabEnabled: false, + myAvatarScalingEnabled: false, + objectScalingEnabled: false, })); setControllerPartLayer('touchpad', 'blank'); setControllerPartLayer('trigger', 'blank'); @@ -399,6 +404,8 @@ function reenableEverything() { nearGrabEnabled: true, holdEnabled: true, farGrabEnabled: true, + myAvatarScalingEnabled: true, + objectScalingEnabled: true, })); setControllerPartLayer('touchpad', 'blank'); setControllerPartLayer('trigger', 'blank'); @@ -516,12 +523,14 @@ stepOrient.prototype = { // STEP: Near Grab // // // /////////////////////////////////////////////////////////////////////////////// -var stepNearGrab = function() { +var stepNearGrab = function(tutorialManager) { this.name = 'nearGrab'; - this.tag = "nearGrab"; + this.tags = ["bothGrab", "nearGrab", "nearGrab-" + tutorialManager.controllerName]; this.tempTag = "nearGrab-temporary"; this.birdIDs = []; + this.controllerName = tutorialManager.controllerName; + Messages.subscribe("Entity-Exploded"); Messages.messageReceived.connect(this.onMessage.bind(this)); } @@ -530,12 +539,17 @@ stepNearGrab.prototype = { this.finished = false; this.onFinish = onFinish; - setControllerPartLayer('tips', 'trigger'); - setControllerPartLayer('trigger', 'highlight'); + if (this.controllerName === CONTROLLER_TOUCH) { + setControllerPartLayer('tips', 'both_triggers'); + setControllerPartLayer('trigger', 'highlight'); + setControllerPartLayer('grip', 'highlight'); + } else { + setControllerPartLayer('tips', 'trigger'); + setControllerPartLayer('trigger', 'highlight'); + } - // Spawn content set - showEntitiesWithTag(this.tag, { visible: true }); - showEntitiesWithTag('bothGrab', { visible: true }); + // Show content set + showEntitiesWithTags(this.tags); var boxSpawnPosition = getEntityWithName(NEAR_BOX_SPAWN_NAME).position; function createBlock(fireworkNumber) { @@ -573,8 +587,8 @@ stepNearGrab.prototype = { this.finished = true; setControllerPartLayer('tips', 'blank'); setControllerPartLayer('trigger', 'normal'); - hideEntitiesWithTag(this.tag, { visible: false}); - hideEntitiesWithTag('bothGrab', { visible: false}); + setControllerPartLayer('grip', 'normal'); + hideEntitiesWithTags(this.tags); deleteEntitiesWithTag(this.tempTag); if (this.positionWatcher) { this.positionWatcher.destroy(); @@ -696,6 +710,7 @@ PositionWatcher.prototype = { /////////////////////////////////////////////////////////////////////////////// var stepEquip = function(tutorialManager) { const controllerName = tutorialManager.controllerName; + this.controllerName = controllerName; this.name = 'equip'; @@ -714,8 +729,13 @@ var stepEquip = function(tutorialManager) { } stepEquip.prototype = { start: function(onFinish) { - setControllerPartLayer('tips', 'trigger'); - setControllerPartLayer('trigger', 'highlight'); + if (this.controllerName === CONTROLLER_TOUCH) { + setControllerPartLayer('tips', 'grip'); + setControllerPartLayer('grip', 'highlight'); + } else { + setControllerPartLayer('tips', 'trigger'); + setControllerPartLayer('trigger', 'highlight'); + } Messages.sendLocalMessage('Hifi-Grab-Disable', JSON.stringify({ holdEnabled: true, diff --git a/tutorial/tutorialEntityIDs.js b/tutorial/tutorialEntityIDs.js index 0200c2c247..f53ac1a56c 100644 --- a/tutorial/tutorialEntityIDs.js +++ b/tutorial/tutorialEntityIDs.js @@ -52,6 +52,16 @@ TUTORIAL_TAG_TO_ENTITY_IDS_MAP = { "tag": "farGrab" } }, + "nearGrab-vive": { + "{88221a22-b710-4d35-852b-5257b0aa77dc}": { + "tag": "nearGrab-vive" + } + }, + "nearGrab-touch": { + "{7c0f2fde-6c5c-459b-bf82-421979cebf2e}": { + "tag": "nearGrab-touch" + } + }, "nearGrab": { "{55c861ef-60ca-4722-a6c5-9c6967966ec5}": { "tag": "nearGrab" @@ -59,9 +69,6 @@ TUTORIAL_TAG_TO_ENTITY_IDS_MAP = { "{644d655b-ae66-43b1-9bab-a44b9a8ad632}": { "tag": "nearGrab" }, - "{88221a22-b710-4d35-852b-5257b0aa77dc}": { - "tag": "nearGrab" - }, "{8bf0baa1-88d0-448a-a782-100d4413bd82}": { "tag": "nearGrab" }, @@ -70,13 +77,20 @@ TUTORIAL_TAG_TO_ENTITY_IDS_MAP = { "tag": "nearGrab" } }, + "equip-part1-touch": { + "{470f0634-8be7-4b52-a8bd-5183d489fcb6}": { + "tag": "equip-part1-touch" + } + }, + "equip-part1-vive": { + "{97ced5e7-fc81-40f9-a9e8-f85b4b30f24c}": { + "tag": "equip-part1-vive" + } + }, "equip-part1": { "{d73822ca-0a34-4cf4-a530-3258ac459a14}": { "tag": "equip-part1" }, - "{97ced5e7-fc81-40f9-a9e8-f85b4b30f24c}": { - "tag": "equip-part1" - }, "{8572d991-5777-45df-97bf-7243d7b12f81}": { "tag": "equip-part1" }, @@ -102,16 +116,17 @@ TUTORIAL_TAG_TO_ENTITY_IDS_MAP = { "equip-part2-vive": { "{b5d17eda-90ab-40cf-b973-efcecb2e992e}": { "tag": "equip-part2-vive" + }, + "{6307cd16-dd1d-4988-a339-578178436b45}": { + "tag": "equip-part2-vive" } }, "equip-part2-touch": { "{69195139-e020-4739-bb2c-50faebc6860a}": { "tag": "equip-part2-touch" - } - }, - "equip-part2": { - "{6307cd16-dd1d-4988-a339-578178436b45}": { - "tag": "equip-part2" + }, + "{9b0a99ae-221b-4e59-ba3c-d8e64a083774}": { + "tag": "equip-part2-touch" } }, "bothGrab": {